summaryrefslogtreecommitdiff
path: root/src/users.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/users.cpp')
-rw-r--r--src/users.cpp1246
1 files changed, 1246 insertions, 0 deletions
diff --git a/src/users.cpp b/src/users.cpp
new file mode 100644
index 000000000..946f70f6a
--- /dev/null
+++ b/src/users.cpp
@@ -0,0 +1,1246 @@
+/* Routines to maintain a list of online users.
+ *
+ * (C) 2003-2010 Anope Team
+ * Contact us at team@anope.org
+ *
+ * Please read COPYING and README for further details.
+ *
+ * Based on the original code of Epona by Lara.
+ * Based on the original code of Services by Andy Church.
+ *
+ *
+ */
+
+#include "services.h"
+#include "modules.h"
+#include "language.h"
+
+/* Hash maps used for users. Note UserListByUID will not be used on non-TS6 IRCds, and should never
+ * be assumed to have users
+ */
+user_map UserListByNick;
+user_uid_map UserListByUID;
+
+int32 opcnt = 0;
+uint32 usercnt = 0, maxusercnt = 0;
+time_t maxusertime;
+
+/*************************************************************************/
+/*************************************************************************/
+
+User::User(const std::string &snick, const std::string &suid)
+{
+ if (snick.empty())
+ throw "what the craq, empty nick passed to constructor";
+
+ // XXX: we should also duplicate-check here.
+
+ /* we used to do this by calloc, no more. */
+ host = hostip = vhost = realname = NULL;
+ server = NULL;
+ nc = NULL;
+ invalid_pw_count = timestamp = my_signon = invalid_pw_time = lastmemosend = lastnickreg = lastmail = 0;
+ OnAccess = false;
+
+ this->nick = snick;
+ this->uid = suid;
+
+ UserListByNick[snick.c_str()] = this;
+ if (!suid.empty())
+ UserListByUID[suid] = this;
+
+ this->nc = NULL;
+
+ usercnt++;
+
+ if (usercnt > maxusercnt)
+ {
+ maxusercnt = usercnt;
+ maxusertime = time(NULL);
+ if (Config.LogMaxUsers)
+ Alog() << "user: New maximum user count: "<< maxusercnt;
+ }
+
+ this->isSuperAdmin = 0; /* always set SuperAdmin to 0 for new users */
+}
+
+void User::SetNewNick(const std::string &newnick)
+{
+ /* Sanity check to make sure we don't segfault */
+ if (newnick.empty())
+ {
+ throw "User::SetNewNick() got a bad argument";
+ }
+
+ Alog(LOG_DEBUG) << this->nick << " changed nick to " << newnick;
+
+ UserListByNick.erase(this->nick.c_str());
+
+ this->nick = newnick;
+
+ UserListByNick[this->nick.c_str()] = this;
+
+ OnAccess = false;
+ NickAlias *na = findnick(this->nick);
+ if (na)
+ OnAccess = is_on_access(this, na->nc);
+}
+
+void User::SetDisplayedHost(const std::string &shost)
+{
+ if (shost.empty())
+ throw "empty host? in MY services? it seems it's more likely than I thought.";
+
+ if (this->vhost)
+ delete [] this->vhost;
+ this->vhost = sstrdup(shost.c_str());
+
+ Alog(LOG_DEBUG) << this->nick << " changed vhost to " << shost;
+
+ this->UpdateHost();
+}
+
+/** Get the displayed vhost of a user record.
+ * @return The displayed vhost of the user, where ircd-supported, or the user's real host.
+ */
+const std::string User::GetDisplayedHost() const
+{
+ if (ircd->vhost && this->vhost)
+ return this->vhost;
+ else if (this->HasMode(UMODE_CLOAK) && !this->GetCloakedHost().empty())
+ return this->GetCloakedHost();
+ else
+ return this->host;
+}
+
+/** Update the cloaked host of a user
+ * @param host The cloaked host
+ */
+void User::SetCloakedHost(const std::string &newhost)
+{
+ if (newhost.empty())
+ throw "empty host in User::SetCloakedHost";
+
+ chost = newhost;
+
+ Alog(LOG_DEBUG) << this->nick << " changed cloaked host to " << newhost;
+
+ this->UpdateHost();
+}
+
+/** Get the cloaked host of a user
+ * @return The cloaked host
+ */
+const std::string &User::GetCloakedHost() const
+{
+ return chost;
+}
+
+const std::string &User::GetUID() const
+{
+ return this->uid;
+}
+
+
+void User::SetVIdent(const std::string &sident)
+{
+ this->vident = sident;
+
+ Alog(LOG_DEBUG) << this->nick << " changed vident to " << sident;
+
+ this->UpdateHost();
+}
+
+const std::string &User::GetVIdent() const
+{
+ if (this->HasMode(UMODE_CLOAK))
+ return this->vident;
+ else if (ircd->vident && !this->vident.empty())
+ return this->vident;
+ else
+ return this->ident;
+}
+
+void User::SetIdent(const std::string &sident)
+{
+ this->ident = sident;
+
+ Alog(LOG_DEBUG) << this->nick << " changed real ident to " << sident;
+
+ this->UpdateHost();
+}
+
+const std::string &User::GetIdent() const
+{
+ return this->ident;
+}
+
+const std::string User::GetMask()
+{
+ std::stringstream buf;
+ buf << this->nick << "!" << this->ident << "@" << this->host;
+ return buf.str();
+}
+
+void User::SetRealname(const std::string &srealname)
+{
+ if (srealname.empty())
+ throw "realname empty in SetRealname";
+
+ if (this->realname)
+ delete [] this->realname;
+ this->realname = sstrdup(srealname.c_str());
+ NickAlias *na = findnick(this->nick);
+
+ if (na && (this->IsIdentified(true) || (!na->nc->HasFlag(NI_SECURE) && this->IsRecognized())))
+ {
+ if (na->last_realname)
+ delete [] na->last_realname;
+ na->last_realname = sstrdup(srealname.c_str());
+ }
+
+ Alog(LOG_DEBUG) << this->nick << " changed realname to " << srealname;
+}
+
+User::~User()
+{
+ Alog(LOG_DEBUG_2) << "User::~User() called";
+
+ if (Config.LogUsers)
+ {
+ const char *srealname = normalizeBuffer(this->realname);
+
+ Alog() << "LOGUSERS: " << this->GetMask() << (ircd->vhost ? " => " : " ")
+ << (ircd->vhost ? this->GetDisplayedHost() : "")
+ << " (" << srealname << ") left the network (" << this->server->GetName() << ").";
+
+ delete [] srealname;
+ }
+
+ FOREACH_MOD(I_OnUserLogoff, OnUserLogoff(this));
+
+ usercnt--;
+
+ if (is_oper(this))
+ opcnt--;
+
+ while (!this->chans.empty())
+ {
+ this->chans.front()->chan->DeleteUser(this);
+ }
+
+ UserListByNick.erase(this->nick.c_str());
+ if (!this->uid.empty())
+ UserListByUID.erase(this->uid);
+
+ NickAlias *na = findnick(this->nick);
+ if (na)
+ na->OnCancel(this);
+
+ Alog(LOG_DEBUG_2) << "User::~User(): free user data";
+
+ delete [] this->host;
+
+ if (this->vhost)
+ delete [] this->vhost;
+
+ if (this->realname)
+ delete [] this->realname;
+ if (this->hostip)
+ delete [] this->hostip;
+
+ Alog(LOG_DEBUG_2) << "User::~User() done";
+}
+
+void User::SendMessage(const std::string &source, const char *fmt, ...)
+{
+ va_list args;
+ char buf[BUFSIZE];
+ *buf = '\0';
+
+ if (fmt)
+ {
+ va_start(args, fmt);
+ vsnprintf(buf, BUFSIZE - 1, fmt, args);
+
+ this->SendMessage(source, std::string(buf));
+
+ va_end(args);
+ }
+}
+
+void User::SendMessage(const std::string &source, const std::string &msg)
+{
+ /* Send privmsg instead of notice if:
+ * - UsePrivmsg is enabled
+ * - The user is not registered and NSDefMsg is enabled
+ * - The user is registered and has set /ns set msg on
+ */
+ if (Config.UsePrivmsg &&
+ ((!this->nc && Config.NSDefFlags.HasFlag(NI_MSG)) || (this->nc && this->nc->HasFlag(NI_MSG))))
+ {
+ ircdproto->SendPrivmsg(findbot(source), this->nick.c_str(), "%s", msg.c_str());
+ }
+ else
+ {
+ ircdproto->SendNotice(findbot(source), this->nick.c_str(), "%s", msg.c_str());
+ }
+}
+
+/** Collides a nick.
+ *
+ * First, it marks the nick (if the user is on a registered nick, we don't use it without but it could be)
+ * as COLLIDED, this is checked in NickAlias::OnCancel.
+ *
+ * Then it does one of two things.
+ *
+ * 1. This will force change the users nick to the guest nick. This gets processed by the IRCd and comes
+ * back to call do_nick. do_nick changes the nick of the use to the new one, then calls NickAlias::OnCancel
+ * with the users old nick's nickalias (if there is one).
+ *
+ * 2. Calls kill_user, which will either delete the user immediatly or kill them, wait for the QUIT,
+ * then delete the user then. Users destructor then calls NickAlias::OnCancel
+ *
+ * NickAlias::OnCancel checks for NS_COLLIDED, it then does one of two things.
+ *
+ * 1. If supported, we send a SVSHold for the user. We are done here, the IRCds expires this at the time we give it.
+ *
+ * 2. We create a new client with SendClientIntroduction(). Note that is it important that this is called either after the
+ * user has been removed from our internal list of user or after the users nick has been updated completely internally.
+ * This is beacuse SendClientIntroduction will destroy any users we think are currently on the nickname (which causes a
+ * lot of problems, eg, deleting the user which recalls OnCancel), whether they really are or not. We then create a
+ * release timer for this new client that waits and later on sends a QUIT for the client. Release timers are never used
+ * for SVSHolds. Ever.
+ *
+ *
+ * Note that now for the timers we only store the users name, not the NickAlias* pointer. We never remove timers when
+ * a user changes nick or a nick is deleted, the timers must assume that either of these may have happend.
+ *
+ * Storing NickAlias* pointers caused quite a problem, some of which are:
+ *
+ * Having a valid timer alive that calls User::Collide would either:
+ *
+ * 1. Kill the user, causing users destructor to cancel all timers for the nick (as it should, it has no way of knowing
+ * if we are in a timer or not) which would delete the currently active timer while it was running, causing TimerManager
+ * to explode.
+ *
+ * 2. Force a user off of their nick, this would call NickAlias::Cancel before updating the user internally (to cancel the
+ * current nicks timers, granted we could have easially saved this and called it after) which could possibly try to
+ * introduce an enforcer nick. We would then check to see if the nick is already in use (it is, internally) and send
+ * a kill for that nick. That may in turn delete the user immediatly, calling users destructor, which would attempt to
+ * delete the timer, causing TimerManager to explode.
+ *
+ * Additionally, if we marked the timer as "in use" so that calling the ClearTimer function wouldn't delete them, users
+ * destructor would then call NickAlias::OnCancel, which would (at this point, it was unsetting GUESTED after introducing
+ * the new client) introduce the same new client again, without actually deleting the originial user, causing an infinite
+ * loop.
+ *
+ * This is why we remove NS_GUESTED first in NickAlias::OnCancel before introducing a new client, although this should
+ * not happen anymore. If I must emphasize this again, users need to be GONE from the internal list before calling
+ * NickAlias::OnCancel. NickAlias::OnCancel intentionally reffers to this->nick, not the user passed to it. They *can*
+ * (but not always) be different, depending if the user changed nicks or disconnected.
+ *
+ *
+ * Adam
+ */
+void User::Collide(NickAlias *na)
+{
+ if (na)
+ na->SetFlag(NS_COLLIDED);
+
+ if (ircd->svsnick)
+ {
+ std::string guestnick;
+
+ do
+ {
+ char randbuf[17];
+ snprintf(randbuf, sizeof(randbuf), "%d", getrandom16());
+ guestnick = std::string(Config.NSGuestNickPrefix) + std::string(randbuf);
+ }
+ while (finduser(guestnick.c_str()));
+
+ notice_lang(Config.s_NickServ, this, FORCENICKCHANGE_CHANGING, guestnick.c_str());
+ ircdproto->SendForceNickChange(this, guestnick.c_str(), time(NULL));
+ }
+ else
+ {
+ kill_user(Config.s_NickServ, this->nick, "Services nickname-enforcer kill");
+ }
+}
+
+/** Check if the user should become identified because
+ * their svid matches the one stored in their nickcore
+ * @param svid Services id
+ */
+void User::CheckAuthenticationToken(const char *svid)
+{
+ NickAlias *na;
+
+ if ((na = findnick(this->nick)))
+ {
+ char *c;
+ if (na->nc && na->nc->GetExtArray("authenticationtoken", c))
+ {
+ if (svid && c && !strcmp(svid, c))
+ {
+ /* Users authentication token matches so they should become identified */
+ this->Login(na->nc);
+ return;
+ }
+ }
+ }
+
+ validate_user(this);
+}
+
+/** Auto identify the user to the given accountname.
+ * @param account Display nick of account
+ */
+void User::AutoID(const std::string &account)
+{
+ NickCore *core = findcore(account.c_str());
+
+ if (core)
+ {
+ this->Login(core);
+
+ NickAlias *na = findnick(this->nick);
+ if (na && na->nc == core)
+ {
+ if (na->last_realname)
+ delete [] na->last_realname;
+ na->last_realname = sstrdup(this->realname);
+ na->last_seen = time(NULL);
+ this->SetMode(findbot(Config.s_NickServ), UMODE_REGISTERED);
+ this->UpdateHost();
+ check_memos(this);
+
+ FOREACH_MOD(I_OnNickIdentify, OnNickIdentify(this));
+ }
+ }
+}
+
+
+/** Login the user to a NickCore
+ * @param core The account the user is useing
+ */
+void User::Login(NickCore *core)
+{
+ nc = core;
+ core->Users.push_back(this);
+}
+
+/** Logout the user
+ */
+void User::Logout()
+{
+ if (!this->nc)
+ return;
+
+ std::list<User *>::iterator it = std::find(this->nc->Users.begin(), this->nc->Users.end(), this);
+ if (it != this->nc->Users.end())
+ {
+ this->nc->Users.erase(it);
+ }
+
+ nc = NULL;
+}
+
+/** Get the account the user is logged in using
+ * @reurn The account or NULL
+ */
+NickCore *User::Account() const
+{
+ return nc;
+}
+
+/** Check if the user is identified for their nick
+ * @param CheckNick True to check if the user is identified to the nickname they are on too
+ * @return true or false
+ */
+const bool User::IsIdentified(bool CheckNick) const
+{
+ if (CheckNick && this->nc)
+ {
+ NickAlias *na = findnick(this->nc->display);
+
+ if (na && na->nc == this->nc)
+ {
+ return true;
+ }
+
+ return false;
+ }
+
+ return this->nc ? true : false;
+}
+
+/** Check if the user is recognized for their nick (on the nicks access list)
+ * @return true or false
+ */
+const bool User::IsRecognized() const
+{
+ return OnAccess;
+}
+
+/** Update the last usermask stored for a user, and check to see if they are recognized
+ */
+void User::UpdateHost()
+{
+ if (!this->host)
+ return;
+
+ NickAlias *na = findnick(this->nick);
+
+ OnAccess = false;
+ if (na)
+ OnAccess = is_on_access(this, na->nc);
+
+ if (na && (this->IsIdentified(true) || (!na->nc->HasFlag(NI_SECURE) && this->IsRecognized())))
+ {
+ if (na->last_usermask)
+ delete [] na->last_usermask;
+
+ std::string last_usermask = this->GetIdent() + "@" + this->GetDisplayedHost();
+ na->last_usermask = sstrdup(last_usermask.c_str());
+ }
+}
+
+/** Check if the user has a mode
+ * @param Name Mode name
+ * @return true or false
+ */
+const bool User::HasMode(UserModeName Name) const
+{
+ return modes.HasFlag(Name);
+}
+
+/** Set a mode internally on the user, the IRCd is not informed
+ * @param um The user mode
+ * @param Param The param, if there is one
+ */
+void User::SetModeInternal(UserMode *um, const std::string &Param)
+{
+ if (!um)
+ return;
+
+ modes.SetFlag(um->Name);
+ if (!Param.empty())
+ {
+ Params.insert(std::make_pair(um->Name, Param));
+ }
+
+ FOREACH_MOD(I_OnUserModeSet, OnUserModeSet(this, um->Name));
+}
+
+/** Remove a mode internally on the user, the IRCd is not informed
+ * @param um The user mode
+ */
+void User::RemoveModeInternal(UserMode *um)
+{
+ if (!um)
+ return;
+
+ modes.UnsetFlag(um->Name);
+ std::map<UserModeName, std::string>::iterator it = Params.find(um->Name);
+ if (it != Params.end())
+ {
+ Params.erase(it);
+ }
+
+ FOREACH_MOD(I_OnUserModeUnset, OnUserModeUnset(this, um->Name));
+}
+
+/** Set a mode on the user
+ * @param bi The client setting the mode
+ * @param um The user mode
+ * @param Param Optional param for the mode
+ */
+void User::SetMode(BotInfo *bi, UserMode *um, const std::string &Param)
+{
+ if (!um || HasMode(um->Name))
+ return;
+
+ ModeManager::StackerAdd(bi, this, um, true, Param);
+ SetModeInternal(um, Param);
+}
+
+/** Set a mode on the user
+ * @param bi The client setting the mode
+ * @param Name The mode name
+ * @param param Optional param for the mode
+ */
+void User::SetMode(BotInfo *bi, UserModeName Name, const std::string &Param)
+{
+ SetMode(bi, ModeManager::FindUserModeByName(Name), Param);
+}
+
+/* Set a mode on the user
+ * @param bi The client setting the mode
+ * @param ModeChar The mode char
+ * @param param Optional param for the mode
+ */
+void User::SetMode(BotInfo *bi, char ModeChar, const std::string &Param)
+{
+ SetMode(bi, ModeManager::FindUserModeByChar(ModeChar), Param);
+}
+
+/** Remove a mode on the user
+ * @param bi The client setting the mode
+ * @param um The user mode
+ */
+void User::RemoveMode(BotInfo *bi, UserMode *um)
+{
+ if (!um || !HasMode(um->Name))
+ return;
+
+ ModeManager::StackerAdd(bi, this, um, false);
+ RemoveModeInternal(um);
+}
+
+/** Remove a mode from the user
+ * @param bi The client setting the mode
+ * @param Name The mode name
+ */
+void User::RemoveMode(BotInfo *bi, UserModeName Name)
+{
+ RemoveMode(bi, ModeManager::FindUserModeByName(Name));
+}
+
+/** Remove a mode from the user
+ * @param bi The client setting the mode
+ * @param ModeChar The mode char
+ */
+void User::RemoveMode(BotInfo *bi, char ModeChar)
+{
+ RemoveMode(bi, ModeManager::FindUserModeByChar(ModeChar));
+}
+
+/** Set a string of modes on a user
+ * @param bi The client setting the mode
+ * @param umodes The modes
+ */
+void User::SetModes(BotInfo *bi, const char *umodes, ...)
+{
+ char buf[BUFSIZE] = "";
+ va_list args;
+ std::string modebuf, sbuf;
+ int add = -1;
+ va_start(args, umodes);
+ vsnprintf(buf, BUFSIZE - 1, umodes, args);
+ va_end(args);
+
+ spacesepstream sep(buf);
+ sep.GetToken(modebuf);
+ for (unsigned i = 0; i < modebuf.size(); ++i)
+ {
+ UserMode *um;
+
+ switch (modebuf[i])
+ {
+ case '+':
+ add = 1;
+ continue;
+ case '-':
+ add = 0;
+ continue;
+ default:
+ if (add == -1)
+ continue;
+ um = ModeManager::FindUserModeByChar(modebuf[i]);
+ if (!um)
+ continue;
+ }
+
+ if (add)
+ {
+ if (um->Type == MODE_PARAM && sep.GetToken(sbuf))
+ this->SetMode(bi, um, sbuf);
+ else
+ this->SetMode(bi, um);
+ }
+ else
+ {
+ this->RemoveMode(bi, um);
+ }
+ }
+}
+
+/** Find the channel container for Channel c that the user is on
+ * This is preferred over using FindUser in Channel, as there are usually more users in a channel
+ * than channels a user is in
+ * @param c The channel
+ * @return The channel container, or NULL
+ */
+ChannelContainer *User::FindChannel(Channel *c)
+{
+ for (UChannelList::iterator it = this->chans.begin(); it != this->chans.end(); ++it)
+ if ((*it)->chan == c)
+ return *it;
+ return NULL;
+}
+
+/** Check if the user is protected from kicks and negative mode changes
+ * @return true or false
+ */
+bool User::IsProtected() const
+{
+ if (this->HasMode(UMODE_PROTECTED) || this->HasMode(UMODE_GOD))
+ return true;
+
+ return false;
+}
+
+/*************************************************************************/
+
+void get_user_stats(long *nusers, long *memuse)
+{
+ long count = 0, mem = 0;
+
+ for (user_map::const_iterator it = UserListByNick.begin(); it != UserListByNick.end(); ++it)
+ {
+ User *user = it->second;
+
+ count++;
+ mem += sizeof(*user);
+ if (user->host)
+ mem += strlen(user->host) + 1;
+ if (ircd->vhost)
+ {
+ if (user->vhost)
+ mem += strlen(user->vhost) + 1;
+ }
+ if (user->realname)
+ mem += strlen(user->realname) + 1;
+ mem += user->server->GetName().length() + 1;
+ mem += (sizeof(ChannelContainer) * user->chans.size());
+ }
+ *nusers = count;
+ *memuse = mem;
+}
+
+User *finduser(const char *nick)
+{
+ return finduser(ci::string(nick));
+}
+
+User *finduser(const std::string &nick)
+{
+ return finduser(ci::string(nick.c_str()));
+}
+
+User *finduser(const ci::string &nick)
+{
+ Alog(LOG_DEBUG_3) << "finduser("<< nick << ")";
+
+ if (isdigit(nick[0]) && ircd->ts6)
+ return find_byuid(nick);
+
+ user_map::const_iterator it = UserListByNick.find(nick.c_str());
+
+ if (it != UserListByNick.end())
+ return it->second;
+ return NULL;
+}
+
+User *find_byuid(const char *uid)
+{
+ return find_byuid(std::string(uid));
+}
+
+User *find_byuid(const ci::string &uid)
+{
+ return find_byuid(std::string(uid.c_str()));
+}
+
+User *find_byuid(const std::string &uid)
+{
+ Alog(LOG_DEBUG_3) << "finduser_byuid(" << uid << ")";
+
+ user_uid_map::iterator it = UserListByUID.find(uid);
+
+ if (it != UserListByUID.end())
+ return it->second;
+ return NULL;
+}
+
+/*************************************************************************/
+
+/* Handle a server NICK command. */
+
+User *do_nick(const char *source, const char *nick, const char *username, const char *host,
+ const char *server, const char *realname, time_t ts,
+ uint32 ip, const char *vhost, const char *uid)
+{
+ User *user = NULL;
+
+ if (!*source) {
+ char ipbuf[16];
+ struct in_addr addr;
+
+ if (ircd->nickvhost) {
+ if (vhost) {
+ if (!strcmp(vhost, "*")) {
+ vhost = NULL;
+ Alog(LOG_DEBUG) << "new user with no vhost in NICK command: " << nick;
+ }
+ }
+ }
+
+ /* This is a new user; create a User structure for it. */
+ Alog(LOG_DEBUG) << "new user: " << nick;
+
+ if (ircd->nickip) {
+ addr.s_addr = htonl(ip);
+ ntoa(addr, ipbuf, sizeof(ipbuf));
+ }
+
+ Server *serv = Server::Find(server);
+
+ if (Config.LogUsers)
+ {
+ /**
+ * Ugly swap routine for Flop's bug :) XXX
+ **/
+ if (realname)
+ {
+ char *tmp = const_cast<char *>(strchr(realname, '%'));
+ while (tmp)
+ {
+ *tmp = '-';
+ tmp = const_cast<char *>(strchr(realname, '%'));
+ }
+ }
+ const char *logrealname = normalizeBuffer(realname);
+
+ /**
+ * End of ugly swap
+ **/
+ Alog() << "LOGUSERS: " << nick << " (" << username << "@" << host
+ << (ircd->nickvhost && vhost ? " => " : "")
+ << (ircd->nickvhost && vhost ? vhost : "") << ") (" << logrealname << ") "
+ << (ircd->nickip ? "[" : "") << (ircd->nickip ? ipbuf : "") << (ircd->nickip ? "]" : "")
+ << " connected to the network (" << serv->GetName() << ").";
+ delete [] logrealname;
+ }
+
+ /* Allocate User structure and fill it in. */
+ user = new User(nick, uid ? uid : "");
+ user->SetIdent(username);
+ user->host = sstrdup(host);
+ user->server = serv;
+ user->realname = sstrdup(realname);
+ user->timestamp = ts;
+ user->my_signon = time(NULL);
+ if (vhost)
+ user->SetCloakedHost(vhost);
+ user->SetVIdent(username);
+ /* We now store the user's ip in the user_ struct,
+ * because we will use it in serveral places -- DrStein */
+ if (ircd->nickip) {
+ user->hostip = sstrdup(ipbuf);
+ } else {
+ user->hostip = NULL;
+ }
+
+ EventReturn MOD_RESULT;
+ FOREACH_RESULT(I_OnPreUserConnect, OnPreUserConnect(user));
+ if (MOD_RESULT == EVENT_STOP)
+ return finduser(nick);
+
+ if (Config.LimitSessions && !serv->IsULined())
+ add_session(nick, host, ipbuf);
+
+ XLineManager::CheckAll(user);
+
+ /* User is no longer connected, return */
+ if (!finduser(nick))
+ return NULL;
+
+ FOREACH_MOD(I_OnUserConnect, OnUserConnect(user));
+ }
+ else
+ {
+ /* An old user changing nicks. */
+ if (ircd->ts6)
+ user = find_byuid(source);
+
+ if (!user)
+ user = finduser(source);
+
+ if (!user) {
+ Alog() << "user: NICK from nonexistent nick " << source;
+ return NULL;
+ }
+ user->isSuperAdmin = 0; /* Dont let people nick change and stay SuperAdmins */
+ Alog(LOG_DEBUG) << source << " changes nick to " << nick;
+
+ if (Config.LogUsers)
+ {
+ const char *logrealname = normalizeBuffer(user->realname);
+ Alog() << "LOGUSERS: " << user->nick << " (" << user->GetIdent() << "@" << user->host
+ << (ircd->vhost ? " => " : "") << (ircd->vhost ? user->GetDisplayedHost() : "") << ") ("
+ << logrealname << ") " << "changed nick to " << nick << " (" << user->server->GetName() << ").";
+ if (logrealname)
+ delete [] logrealname;
+ }
+
+ user->timestamp = ts;
+
+ if (stricmp(nick, user->nick.c_str()) == 0)
+ {
+ /* No need to redo things */
+ user->SetNewNick(nick);
+ }
+ else
+ {
+ /* Update this only if nicks aren't the same */
+ user->my_signon = time(NULL);
+
+ NickAlias *old_na = findnick(user->nick);
+ if (old_na && (old_na->nc == user->Account() || user->IsRecognized()))
+ old_na->last_seen = time(NULL);
+
+ std::string oldnick = user->nick;
+ user->SetNewNick(nick);
+ FOREACH_MOD(I_OnUserNickChange, OnUserNickChange(user, oldnick));
+
+ if (old_na)
+ old_na->OnCancel(user);
+
+ NickAlias *na = findnick(user->nick);
+ /* If the new nick isnt registerd or its registerd and not yours */
+ if (!na || na->nc != user->Account())
+ {
+ ircdproto->SendUnregisteredNick(user);
+
+ validate_user(user);
+ }
+ else
+ {
+ na->last_seen = time(NULL);
+ user->UpdateHost();
+ do_on_id(user);
+ ircdproto->SetAutoIdentificationToken(user);
+ Alog() << Config.s_NickServ << ": " << user->GetMask() << " automatically identified for group " << user->Account()->display;
+ }
+
+ if (ircd->sqline)
+ {
+ if (!is_oper(user) && SQLine->Check(user))
+ return NULL;
+ }
+ }
+ }
+
+ return user;
+}
+
+/*************************************************************************/
+
+/* Handle a MODE command for a user.
+ * av[0] = nick to change mode for
+ * av[1] = modes
+ */
+
+void do_umode(const char *source, int ac, const char **av)
+{
+ User *user;
+
+ user = finduser(av[0]);
+ if (!user) {
+ Alog() << "user: MODE "<< av[1] << " for nonexistent nick "<< av[0] << ":" << merge_args(ac, av);
+ return;
+ }
+
+ UserSetInternalModes(user, ac - 1, &av[1]);
+}
+
+/*************************************************************************/
+
+/* Handle a QUIT command.
+ * av[0] = reason
+ */
+
+void do_quit(const char *source, int ac, const char **av)
+{
+ User *user;
+ NickAlias *na;
+
+ user = finduser(source);
+ if (!user) {
+ Alog() << "user: QUIT from nonexistent user " << source << ":" << merge_args(ac, av);
+ return;
+ }
+ Alog(LOG_DEBUG) << source << " quits";
+ if ((na = findnick(user->nick)) && !na->HasFlag(NS_FORBIDDEN)
+ && !na->nc->HasFlag(NI_SUSPENDED) && (user->IsRecognized() || user->IsIdentified(true))) {
+ na->last_seen = time(NULL);
+ if (na->last_quit)
+ delete [] na->last_quit;
+ na->last_quit = *av[0] ? sstrdup(av[0]) : NULL;
+ }
+ if (Config.LimitSessions && !user->server->IsULined())
+ del_session(user->host);
+ FOREACH_MOD(I_OnUserQuit, OnUserQuit(user, *av[0] ? av[0] : ""));
+ delete user;
+}
+
+/*************************************************************************/
+
+/* Handle a KILL command.
+ * av[0] = nick being killed
+ * av[1] = reason
+ */
+
+void do_kill(const std::string &nick, const std::string &msg)
+{
+ User *user;
+ NickAlias *na;
+
+ user = finduser(nick);
+ if (!user)
+ {
+ Alog(LOG_DEBUG) << "KILL of nonexistent nick: " << nick;
+ return;
+ }
+ Alog(LOG_DEBUG) << nick << " killed";
+ if ((na = findnick(user->nick)) && !na->HasFlag(NS_FORBIDDEN) && !na->nc->HasFlag(NI_SUSPENDED) && (user->IsRecognized() || user->IsIdentified(true)))
+ {
+ na->last_seen = time(NULL);
+ if (na->last_quit)
+ delete [] na->last_quit;
+ na->last_quit = !msg.empty() ? sstrdup(msg.c_str()) : NULL;
+ }
+ if (Config.LimitSessions && !user->server->IsULined()) {
+ del_session(user->host);
+ }
+ delete user;
+}
+
+/*************************************************************************/
+/*************************************************************************/
+
+/* Is the given nick an oper? */
+
+int is_oper(User * user)
+{
+ if (user && user->HasMode(UMODE_OPER))
+ return 1;
+
+ return 0;
+}
+
+/*************************************************************************/
+/*************************************************************************/
+
+/* Is the given user ban-excepted? */
+int is_excepted(ChannelInfo * ci, User * user)
+{
+ if (!ci->c || !ModeManager::FindChannelModeByName(CMODE_EXCEPT))
+ return 0;
+
+ if (elist_match_user(ci->c->excepts, user))
+ return 1;
+
+ return 0;
+}
+
+/*************************************************************************/
+
+/* Is the given MASK ban-excepted? */
+int is_excepted_mask(ChannelInfo * ci, const char *mask)
+{
+ if (!ci->c || !ModeManager::FindChannelModeByName(CMODE_EXCEPT))
+ return 0;
+
+ if (elist_match_mask(ci->c->excepts, mask, 0))
+ return 1;
+
+ return 0;
+}
+
+
+/*************************************************************************/
+
+/* Does the user's usermask match the given mask (either nick!user@host or
+ * just user@host)?
+ */
+
+int match_usermask(const char *mask, User * user)
+{
+ char *mask2;
+ char *nick, *username, *host;
+ int result;
+
+ if (!mask || !*mask) {
+ return 0;
+ }
+
+ mask2 = sstrdup(mask);
+
+ if (strchr(mask2, '!')) {
+ nick = strtok(mask2, "!");
+ username = strtok(NULL, "@");
+ } else {
+ nick = NULL;
+ username = strtok(mask2, "@");
+ }
+ host = strtok(NULL, "");
+ if (!username || !host) {
+ delete [] mask2;
+ return 0;
+ }
+
+ if (nick) {
+ result = Anope::Match(user->nick, nick, false)
+ && Anope::Match(user->GetIdent().c_str(), username, false)
+ && (Anope::Match(user->host, host, false)
+ || Anope::Match(user->GetDisplayedHost().c_str(), host, false));
+ } else {
+ result = Anope::Match(user->GetIdent().c_str(), username, false)
+ && (Anope::Match(user->host, host, false)
+ || Anope::Match(user->GetDisplayedHost().c_str(), host, false));
+ }
+
+ delete [] mask2;
+ return result;
+}
+
+/*************************************************************************/
+
+/* Given a user, return a mask that will most likely match any address the
+ * user will have from that location. For IP addresses, wildcards the
+ * appropriate subnet mask (e.g. 35.1.1.1 -> 35.*; 128.2.1.1 -> 128.2.*);
+ * for named addresses, wildcards the leftmost part of the name unless the
+ * name only contains two parts. If the username begins with a ~, delete
+ * it. The returned character string is malloc'd and should be free'd
+ * when done with.
+ */
+
+char *create_mask(User * u)
+{
+ char *mask, *s, *end;
+ std::string mident = u->GetIdent();
+ std::string mhost = u->GetDisplayedHost();
+ int ulen = mident.length();
+
+ /* Get us a buffer the size of the username plus hostname. The result
+ * will never be longer than this (and will often be shorter), thus we
+ * can use strcpy() and sprintf() safely.
+ */
+ end = mask = new char[ulen + mhost.length() + 3];
+ if (mident[0] == '~')
+ end += sprintf(end, "*%s@", mident.c_str());
+ else
+ end += sprintf(end, "%s@", mident.c_str());
+
+ // XXX: someone needs to rewrite this godawful kitten murdering pile of crap.
+ if (strspn(mhost.c_str(), "0123456789.") == mhost.length()
+ && (s = strchr(const_cast<char *>(mhost.c_str()), '.')) // XXX - Potentially unsafe cast
+ && (s = strchr(s + 1, '.'))
+ && (s = strchr(s + 1, '.'))
+ && (!strchr(s + 1, '.')))
+ { /* IP addr */
+ s = sstrdup(mhost.c_str());
+ *strrchr(s, '.') = 0;
+
+ sprintf(end, "%s.*", s);
+ delete [] s;
+ }
+ else
+ {
+ if ((s = strchr(const_cast<char *>(mhost.c_str()), '.')) && strchr(s + 1, '.')) {
+ s = sstrdup(strchr(mhost.c_str(), '.') - 1);
+ *s = '*';
+ strcpy(end, s);
+ delete [] s;
+ } else {
+ strcpy(end, mhost.c_str());
+ }
+ }
+ return mask;
+}
+
+/*************************************************************************/
+
+/** Set modes internally on a user
+ * @param user The user
+ * @param ac Number of args
+ * @param av Args
+ */
+void UserSetInternalModes(User *user, int ac, const char **av)
+{
+ int add = -1, j = 0;
+ const char *modes = av[0];
+ if (!user || !modes)
+ return;
+
+ Alog(LOG_DEBUG) << "Changing user modes for " << user->nick << " to " << merge_args(ac, av);
+
+ for (; *modes; modes++)
+ {
+ UserMode *um;
+
+ switch (*modes)
+ {
+ case '+':
+ add = 1;
+ continue;
+ case '-':
+ add = 0;
+ continue;
+ default:
+ if (add == -1)
+ continue;
+ um = ModeManager::FindUserModeByChar(*modes);
+ if (!um)
+ continue;
+ }
+
+ if (um->Type == MODE_REGULAR)
+ {
+ if (add)
+ user->SetModeInternal(um);
+ else
+ user->RemoveModeInternal(um);
+ }
+ else if (++j < ac)
+ {
+ if (add)
+ user->SetModeInternal(um, av[j]);
+ else
+ user->RemoveModeInternal(um);
+ }
+
+ switch (um->Name)
+ {
+ case UMODE_OPER:
+ if (add)
+ {
+ ++opcnt;
+ if (Config.WallOper)
+ ircdproto->SendGlobops(OperServ, "\2%s\2 is now an IRC operator.", user->nick.c_str());
+ }
+ else
+ --opcnt;
+ break;
+ case UMODE_REGISTERED:
+ if (add && !user->IsIdentified())
+ user->RemoveMode(NickServ, UMODE_REGISTERED);
+ break;
+ case UMODE_CLOAK:
+ case UMODE_VHOST:
+ if (!add && user->vhost)
+ {
+ delete [] user->vhost;
+ user->vhost = NULL;
+ }
+ user->UpdateHost();
+ default:
+ break;
+ }
+ }
+}
+