diff options
Diffstat (limited to 'src/users.cpp')
-rw-r--r-- | src/users.cpp | 1246 |
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; + } + } +} + |