X-Git-Url: https://git.netwichtig.de/gitweb/?a=blobdiff_plain;f=src%2Fusers.cpp;h=ad358211dd777d4cd980b22bd3cccb445f2839b5;hb=dafc021be4f3ad34ca37953de6a0109a161dd165;hp=83d62ecf7ff3cc26af92307aece24e164e54081b;hpb=145862efc772da990aa2c7fbd5000a81e8476b8b;p=user%2Fhenk%2Fcode%2Finspircd.git diff --git a/src/users.cpp b/src/users.cpp index 83d62ecf7..ad358211d 100644 --- a/src/users.cpp +++ b/src/users.cpp @@ -3,13 +3,13 @@ * +------------------------------------+ * * InspIRCd is copyright (C) 2002-2006 ChatSpike-Dev. - * E-mail: - * - * + * E-mail: + * + * * * Written by Craig Edwards, Craig McLure, and others. * This program is free but copyrighted software; see - * the file COPYING for details. + * the file COPYING for details. * * --------------------------------------------------- */ @@ -20,11 +20,7 @@ #include "connection.h" #include "users.h" #include "inspircd.h" -#include -#ifdef THREADED_DNS -#include -#include -#endif +#include #include "inspstring.h" #include "commands.h" #include "helperfuncs.h" @@ -36,23 +32,14 @@ #include "xline.h" #include "cull_list.h" -extern InspIRCd* ServerInstance; -extern int WHOWAS_STALE; -extern int WHOWAS_MAX; extern std::vector modules; extern std::vector factory; -extern std::vector module_sockets; extern int MODCOUNT; -extern InspSocket* socket_ref[MAX_DESCRIPTORS]; extern time_t TIME; -extern userrec* fd_ref_table[MAX_DESCRIPTORS]; -extern ServerConfig *Config; -extern user_hash clientlist; - -whowas_users whowas; - -extern std::vector local_users; +extern Server* MyServer; +irc::whowas::whowas_users whowas; +static unsigned long already_sent[MAX_DESCRIPTORS] = {0}; std::vector all_opers; typedef std::map opertype_t; @@ -61,7 +48,10 @@ typedef opertype_t operclass_t; opertype_t opertypes; operclass_t operclass; -bool InitTypes(const char* tag) +/* XXX: Used for speeding up WriteCommon operations */ +unsigned long uniq_id = 0; + +bool InitTypes(ServerConfig* conf, const char* tag) { for (opertype_t::iterator n = opertypes.begin(); n != opertypes.end(); n++) { @@ -73,7 +63,7 @@ bool InitTypes(const char* tag) return true; } -bool InitClasses(const char* tag) +bool InitClasses(ServerConfig* conf, const char* tag) { for (operclass_t::iterator n = operclass.begin(); n != operclass.end(); n++) { @@ -85,7 +75,7 @@ bool InitClasses(const char* tag) return true; } -bool DoType(const char* tag, char** entries, void** values, int* types) +bool DoType(ServerConfig* conf, const char* tag, char** entries, void** values, int* types) { char* TypeName = (char*)values[0]; char* Classes = (char*)values[1]; @@ -95,7 +85,7 @@ bool DoType(const char* tag, char** entries, void** values, int* types) return true; } -bool DoClass(const char* tag, char** entries, void** values, int* types) +bool DoClass(ServerConfig* conf, const char* tag, char** entries, void** values, int* types) { char* ClassName = (char*)values[0]; char* CommandList = (char*)values[1]; @@ -105,7 +95,7 @@ bool DoClass(const char* tag, char** entries, void** values, int* types) return true; } -bool DoneClassesAndTypes(const char* tag) +bool DoneClassesAndTypes(ServerConfig* conf, const char* tag) { return true; } @@ -137,6 +127,84 @@ bool userrec::ProcessNoticeMasks(const char *sm) return true; } +void userrec::StartDNSLookup() +{ + log(DEBUG,"Commencing reverse lookup"); + try + { + log(DEBUG,"Passing instance: %08x",this->ServerInstance); + res_reverse = new UserResolver(this->ServerInstance, this, this->GetIPString(), false); + MyServer->AddResolver(res_reverse); + } + catch (ModuleException& e) + { + log(DEBUG,"Error in resolver: %s",e.GetReason()); + } +} + +UserResolver::UserResolver(InspIRCd* Instance, userrec* user, std::string to_resolve, bool forward) : + Resolver(Instance, to_resolve, forward ? DNS_QUERY_FORWARD : DNS_QUERY_REVERSE), bound_user(user) +{ + this->fwd = forward; + this->bound_fd = user->fd; +} + +void UserResolver::OnLookupComplete(const std::string &result) +{ + if ((!this->fwd) && (ServerInstance->fd_ref_table[this->bound_fd] == this->bound_user)) + { + log(DEBUG,"Commencing forward lookup"); + this->bound_user->stored_host = result; + try + { + bound_user->res_forward = new UserResolver(this->ServerInstance, this->bound_user, result, true); + MyServer->AddResolver(bound_user->res_forward); + } + catch (ModuleException& e) + { + log(DEBUG,"Error in resolver: %s",e.GetReason()); + } + } + else if ((this->fwd) && (ServerInstance->fd_ref_table[this->bound_fd] == this->bound_user)) + { + /* Both lookups completed */ + if (this->bound_user->GetIPString() == result) + { + std::string hostname = this->bound_user->stored_host; + if (hostname.length() < 65) + { + /* Hostnames starting with : are not a good thing (tm) */ + if (*(hostname.c_str()) == ':') + hostname = "0" + hostname; + + this->bound_user->WriteServ("NOTICE Auth :*** Found your hostname (%s)", hostname.c_str()); + this->bound_user->dns_done = true; + strlcpy(this->bound_user->dhost, hostname.c_str(),64); + strlcpy(this->bound_user->host, hostname.c_str(),64); + } + else + { + this->bound_user->WriteServ("NOTICE Auth :*** Your hostname is longer than the maximum of 64 characters, using your IP address (%s) instead.", this->bound_user->GetIPString()); + } + } + else + { + this->bound_user->WriteServ("NOTICE Auth :*** Your hostname does not match up with your IP address. Sorry, using your IP address (%s) instead.", this->bound_user->GetIPString()); + } + } +} + +void UserResolver::OnError(ResolverError e, const std::string &errormessage) +{ + if (ServerInstance->fd_ref_table[this->bound_fd] == this->bound_user) + { + /* Error message here */ + this->bound_user->WriteServ("NOTICE Auth :*** Could not resolve your hostname, using your IP address (%s) instead.", this->bound_user->GetIPString()); + this->bound_user->dns_done = true; + } +} + + bool userrec::IsNoticeMaskSet(unsigned char sm) { return (snomasks[sm-65]); @@ -187,17 +255,21 @@ const char* userrec::FormatModes() return data; } -userrec::userrec() +userrec::userrec(InspIRCd* Instance) : ServerInstance(Instance) { + log(DEBUG,"userrec::userrec(): Instance: %08x",ServerInstance); // the PROPER way to do it, AVOID bzero at *ALL* costs *password = *nick = *ident = *host = *dhost = *fullname = *awaymsg = *oper = 0; - server = (char*)FindServerNamePtr(Config->ServerName); + server = (char*)Instance->FindServerNamePtr(Instance->Config->ServerName); reset_due = TIME; lines_in = fd = lastping = signon = idle_lastmsg = nping = registered = 0; - timeout = flood = port = bytes_in = bytes_out = cmds_in = cmds_out = 0; + timeout = flood = bytes_in = bytes_out = cmds_in = cmds_out = 0; haspassed = dns_done = false; recvq = ""; sendq = ""; + WriteError = ""; + res_forward = res_reverse = NULL; + ip = NULL; chans.clear(); invites.clear(); chans.resize(MAXCHANS); @@ -219,12 +291,20 @@ userrec::~userrec() ucrec* x = (ucrec*)*n; delete x; } -#ifdef THREADED_DNS - if ((IS_LOCAL(this)) && (!dns_done) && (registered >= REG_NICKUSER)) + + if (ip) { - pthread_kill(this->dnsthread, SIGTERM); - } + if (this->GetProtocolFamily() == AF_INET) + { + delete (sockaddr_in*)ip; + } +#ifdef SUPPORT_IP6LINKS + else + { + delete (sockaddr_in6*)ip; + } #endif + } } /* XXX - minor point, other *Host functions return a char *, this one creates it. Might be nice to be consistant? */ @@ -417,7 +497,7 @@ bool userrec::AddBuffer(const std::string &a) if (recvq.length() > (unsigned)this->recvqmax) { this->SetWriteError("RecvQ exceeded"); - WriteOpers("*** User %s RecvQ of %d exceeds connect class maximum of %d",this->nick,recvq.length(),this->recvqmax); + ServerInstance->WriteOpers("*** User %s RecvQ of %d exceeds connect class maximum of %d",this->nick,recvq.length(),this->recvqmax); return false; } @@ -474,7 +554,7 @@ void userrec::AddWriteBuf(const std::string &data) * to repeatedly add the text to the sendq! */ this->SetWriteError("SendQ exceeded"); - WriteOpers("*** User %s SendQ of %d exceeds connect class maximum of %d",this->nick,sendq.length() + data.length(),this->sendqmax); + ServerInstance->WriteOpers("*** User %s SendQ of %d exceeds connect class maximum of %d",this->nick,sendq.length() + data.length(),this->sendqmax); return; } @@ -517,6 +597,7 @@ void userrec::FlushWriteBuf() void userrec::SetWriteError(const std::string &error) { + log(DEBUG,"SetWriteError: %s",error.c_str()); // don't try to set the error twice, its already set take the first string. if (!this->WriteError.length()) { @@ -530,28 +611,38 @@ const char* userrec::GetWriteError() return this->WriteError.c_str(); } -void AddOper(userrec* user) +void userrec::Oper(const std::string &opertype) { - log(DEBUG,"Oper added to optimization list"); - all_opers.push_back(user); + this->modes[UM_OPERATOR] = 1; + this->WriteServ("MODE %s :+o", this->nick); + FOREACH_MOD(I_OnOper, OnOper(this, opertype)); + log(DEFAULT,"OPER: %s!%s@%s opered as type: %s", this->nick, this->ident, this->host, opertype.c_str()); + strlcpy(this->oper, opertype.c_str(), NICKMAX - 1); + all_opers.push_back(this); + FOREACH_MOD(I_OnPostOper,OnPostOper(this, opertype)); } -void DeleteOper(userrec* user) +void userrec::UnOper() { - for (std::vector::iterator a = all_opers.begin(); a < all_opers.end(); a++) + if (*this->oper) { - if (*a == user) + *this->oper = 0; + this->modes[UM_OPERATOR] = 0; + for (std::vector::iterator a = all_opers.begin(); a < all_opers.end(); a++) { - log(DEBUG,"Oper removed from optimization list"); - all_opers.erase(a); - return; + if (*a == this) + { + log(DEBUG,"Oper removed from optimization list"); + all_opers.erase(a); + return; + } } } } -void kill_link(userrec *user,const char* r) +void userrec::QuitUser(InspIRCd* Instance, userrec *user,const std::string &quitreason) { - user_hash::iterator iter = clientlist.find(user->nick); + user_hash::iterator iter = Instance->clientlist.find(user->nick); /* * I'm pretty sure returning here is causing a desync when part of the net thinks a user is gone, @@ -563,34 +654,33 @@ void kill_link(userrec *user,const char* r) * if (iter == clientlist.end()) * return; */ + std::string reason = quitreason; - char reason[MAXBUF]; - - strlcpy(reason,r,MAXQUIT-1); - log(DEBUG,"kill_link: %s %d '%s'",user->nick,user->fd,reason); + if (reason.length() > MAXQUIT - 1) + reason.resize(MAXQUIT - 1); if (IS_LOCAL(user)) - Write(user->fd,"ERROR :Closing link (%s@%s) [%s]",user->ident,user->host,reason); + user->Write("ERROR :Closing link (%s@%s) [%s]",user->ident,user->host,reason.c_str()); if (user->registered == REG_ALL) { - purge_empty_chans(user); - FOREACH_MOD(I_OnUserQuit,OnUserQuit(user,reason)); - WriteCommonExcept(user,"QUIT :%s",reason); + user->PurgeEmptyChannels(); + FOREACH_MOD_I(Instance,I_OnUserQuit,OnUserQuit(user,reason)); + user->WriteCommonExcept("QUIT :%s",reason.c_str()); } if (IS_LOCAL(user)) user->FlushWriteBuf(); - FOREACH_MOD(I_OnUserDisconnect,OnUserDisconnect(user)); + FOREACH_MOD_I(Instance,I_OnUserDisconnect,OnUserDisconnect(user)); if (IS_LOCAL(user)) { - if (Config->GetIOHook(user->port)) + if (Instance->Config->GetIOHook(user->GetPort())) { try { - Config->GetIOHook(user->port)->OnRawSocketClose(user->fd); + Instance->Config->GetIOHook(user->GetPort())->OnRawSocketClose(user->fd); } catch (ModuleException& modexcept) { @@ -598,12 +688,12 @@ void kill_link(userrec *user,const char* r) } } - ServerInstance->SE->DelFd(user->fd); + Instance->SE->DelFd(user->fd); user->CloseSocket(); } /* - * this must come before the WriteOpers so that it doesnt try to fill their buffer with anything + * this must come before the ServerInstance->WriteOpers so that it doesnt try to fill their buffer with anything * if they were an oper with +s. * * XXX - @@ -613,100 +703,104 @@ void kill_link(userrec *user,const char* r) if (user->registered == REG_ALL) { if (IS_LOCAL(user)) - WriteOpers("*** Client exiting: %s!%s@%s [%s]",user->nick,user->ident,user->host,reason); - AddWhoWas(user); + Instance->WriteOpers("*** Client exiting: %s!%s@%s [%s]",user->nick,user->ident,user->host,reason.c_str()); + user->AddToWhoWas(); } - if (iter != clientlist.end()) + if (iter != Instance->clientlist.end()) { log(DEBUG,"deleting user hash value %lx",(unsigned long)user); if (IS_LOCAL(user)) { - fd_ref_table[user->fd] = NULL; - if (find(local_users.begin(),local_users.end(),user) != local_users.end()) - { - local_users.erase(find(local_users.begin(),local_users.end(),user)); - log(DEBUG,"Delete local user"); - } + Instance->fd_ref_table[user->fd] = NULL; + if (find(Instance->local_users.begin(),Instance->local_users.end(),user) != Instance->local_users.end()) + Instance->local_users.erase(find(Instance->local_users.begin(),Instance->local_users.end(),user)); } - clientlist.erase(iter); + Instance->clientlist.erase(iter); DELETE(user); } } -WhoWasGroup::WhoWasGroup(userrec* user) : host(NULL), dhost(NULL), ident(NULL), server(NULL), gecos(NULL), signon(user->signon) +namespace irc { - this->host = strdup(user->host); - this->dhost = strdup(user->dhost); - this->ident = strdup(user->ident); - this->server = user->server; - this->gecos = strdup(user->fullname); -} + namespace whowas + { -WhoWasGroup::~WhoWasGroup() -{ - if (host) - free(host); - if (dhost) - free(dhost); - if (ident) - free(ident); - if (gecos) - free(gecos); -} + WhoWasGroup::WhoWasGroup(userrec* user) : host(NULL), dhost(NULL), ident(NULL), server(NULL), gecos(NULL), signon(user->signon) + { + this->host = strdup(user->host); + this->dhost = strdup(user->dhost); + this->ident = strdup(user->ident); + this->server = user->server; + this->gecos = strdup(user->fullname); + } + + WhoWasGroup::~WhoWasGroup() + { + if (host) + free(host); + if (dhost) + free(dhost); + if (ident) + free(ident); + if (gecos) + free(gecos); + } + + /* every hour, run this function which removes all entries over 3 days */ + void MaintainWhoWas(time_t TIME) + { + for (whowas_users::iterator iter = ::whowas.begin(); iter != ::whowas.end(); iter++) + { + whowas_set* n = (whowas_set*)iter->second; + if (n->size()) + { + while ((n->begin() != n->end()) && ((*n->begin())->signon < TIME - 259200)) // 3 days + { + WhoWasGroup *a = *(n->begin()); + DELETE(a); + n->erase(n->begin()); + } + } + } + } + }; +}; /* adds or updates an entry in the whowas list */ -void AddWhoWas(userrec* u) +void userrec::AddToWhoWas() { - whowas_users::iterator iter = whowas.find(u->nick); - + irc::whowas::whowas_users::iterator iter = whowas.find(this->nick); + if (iter == whowas.end()) { - whowas_set* n = new whowas_set; - WhoWasGroup *a = new WhoWasGroup(u); + irc::whowas::whowas_set* n = new irc::whowas::whowas_set; + irc::whowas::WhoWasGroup *a = new irc::whowas::WhoWasGroup(this); n->push_back(a); - whowas[u->nick] = n; + whowas[this->nick] = n; } else { - whowas_set* group = (whowas_set*)iter->second; - + irc::whowas::whowas_set* group = (irc::whowas::whowas_set*)iter->second; + if (group->size() > 10) { - WhoWasGroup *a = (WhoWasGroup*)*(group->begin()); + irc::whowas::WhoWasGroup *a = (irc::whowas::WhoWasGroup*)*(group->begin()); DELETE(a); group->pop_front(); } - - WhoWasGroup *a = new WhoWasGroup(u); - group->push_back(a); - } -} -/* every hour, run this function which removes all entries over 3 days */ -void MaintainWhoWas(time_t TIME) -{ - for (whowas_users::iterator iter = whowas.begin(); iter != whowas.end(); iter++) - { - whowas_set* n = (whowas_set*)iter->second; - if (n->size()) - { - while ((n->begin() != n->end()) && ((*n->begin())->signon < TIME - 259200)) // 3 days - { - WhoWasGroup *a = *(n->begin()); - DELETE(a); - n->erase(n->begin()); - } - } + irc::whowas::WhoWasGroup *a = new irc::whowas::WhoWasGroup(this); + group->push_back(a); } } /* add a client connection to the sockets list */ -void AddClient(int socket, int port, bool iscached, insp_inaddr ip4) +void userrec::AddClient(InspIRCd* Instance, int socket, int port, bool iscached, insp_inaddr ip) { std::string tempnick = ConvToStr(socket) + "-unknown"; - user_hash::iterator iter = clientlist.find(tempnick); - const char *ipaddr = insp_ntoa(ip4); + user_hash::iterator iter = Instance->clientlist.find(tempnick); + const char *ipaddr = insp_ntoa(ip); userrec* _new; int j = 0; @@ -719,35 +813,37 @@ void AddClient(int socket, int port, bool iscached, insp_inaddr ip4) * this was probably the cause of 'server ignores me when i hammer it with reconnects' * issue in earlier alphas/betas */ - if (iter != clientlist.end()) + if (iter != Instance->clientlist.end()) { userrec* goner = iter->second; DELETE(goner); - clientlist.erase(iter); + Instance->clientlist.erase(iter); } log(DEBUG,"AddClient: %d %d %s",socket,port,ipaddr); - _new = new userrec(); - clientlist[tempnick] = _new; + _new = new userrec(Instance); + Instance->clientlist[tempnick] = _new; _new->fd = socket; strlcpy(_new->nick,tempnick.c_str(),NICKMAX-1); - /* Smarter than your average bear^H^H^H^Hset of strlcpys. */ - for (const char* temp = ipaddr; *temp && j < 64; temp++, j++) - _new->dhost[j] = _new->host[j] = *temp; - _new->dhost[j] = _new->host[j] = 0; - - _new->server = FindServerNamePtr(Config->ServerName); + _new->server = Instance->FindServerNamePtr(Instance->Config->ServerName); /* We don't need range checking here, we KNOW 'unknown\0' will fit into the ident field. */ strcpy(_new->ident, "unknown"); _new->registered = REG_NONE; - _new->signon = TIME + Config->dns_timeout; + _new->signon = TIME + Instance->Config->dns_timeout; _new->lastping = 1; - _new->ip4 = ip4; - _new->port = port; + log(DEBUG,"Setting socket addresses"); + _new->SetSockAddr(AF_FAMILY, ipaddr, port); + log(DEBUG,"Socket addresses set."); + + /* Smarter than your average bear^H^H^H^Hset of strlcpys. */ + for (const char* temp = _new->GetIPString(); *temp && j < 64; temp++, j++) + _new->dhost[j] = _new->host[j] = *temp; + _new->dhost[j] = _new->host[j] = 0; + // set the registration timeout for this user unsigned long class_regtimeout = 90; int class_flood = 0; @@ -755,9 +851,9 @@ void AddClient(int socket, int port, bool iscached, insp_inaddr ip4) long class_sqmax = 262144; // 256kb long class_rqmax = 4096; // 4k - for (ClassVector::iterator i = Config->Classes.begin(); i != Config->Classes.end(); i++) + for (ClassVector::iterator i = Instance->Config->Classes.begin(); i != Instance->Config->Classes.end(); i++) { - if ((i->type == CC_ALLOW) && (match(ipaddr,i->host.c_str()))) + if ((i->type == CC_ALLOW) && (match(ipaddr,i->host.c_str(),true))) { class_regtimeout = (unsigned long)i->registration_timeout; class_flood = i->flood; @@ -769,25 +865,25 @@ void AddClient(int socket, int port, bool iscached, insp_inaddr ip4) } } - _new->nping = TIME + _new->pingmax + Config->dns_timeout; + _new->nping = TIME + _new->pingmax + Instance->Config->dns_timeout; _new->timeout = TIME+class_regtimeout; _new->flood = class_flood; _new->threshold = class_threshold; _new->sendqmax = class_sqmax; _new->recvqmax = class_rqmax; - fd_ref_table[socket] = _new; - local_users.push_back(_new); + Instance->fd_ref_table[socket] = _new; + Instance->local_users.push_back(_new); - if (local_users.size() > Config->SoftLimit) + if (Instance->local_users.size() > Instance->Config->SoftLimit) { - kill_link(_new,"No more connections allowed"); + userrec::QuitUser(Instance, _new,"No more connections allowed"); return; } - if (local_users.size() >= MAXCLIENTS) + if (Instance->local_users.size() >= MAXCLIENTS) { - kill_link(_new,"No more connections allowed"); + userrec::QuitUser(Instance, _new,"No more connections allowed"); return; } @@ -803,7 +899,7 @@ void AddClient(int socket, int port, bool iscached, insp_inaddr ip4) */ if ((unsigned)socket >= MAX_DESCRIPTORS) { - kill_link(_new,"Server is full"); + userrec::QuitUser(Instance, _new,"Server is full"); return; } char* e = matches_exception(ipaddr); @@ -814,93 +910,97 @@ void AddClient(int socket, int port, bool iscached, insp_inaddr ip4) { char reason[MAXBUF]; snprintf(reason,MAXBUF,"Z-Lined: %s",r); - kill_link(_new,reason); + userrec::QuitUser(Instance, _new,reason); return; } } if (socket > -1) { - ServerInstance->SE->AddFd(socket,true,X_ESTAB_CLIENT); + if (!Instance->SE->AddFd(socket,true,X_ESTAB_CLIENT)) + { + userrec::QuitUser(Instance, _new, "Internal error handling connection"); + return; + } } - WriteServ(_new->fd,"NOTICE Auth :*** Looking up your hostname..."); + _new->WriteServ("NOTICE Auth :*** Looking up your hostname..."); } -long FindMatchingGlobal(userrec* user) +long userrec::GlobalCloneCount() { + char u1[1024]; + char u2[1024]; long x = 0; - for (user_hash::const_iterator a = clientlist.begin(); a != clientlist.end(); a++) + for (user_hash::const_iterator a = ServerInstance->clientlist.begin(); a != ServerInstance->clientlist.end(); a++) { -#ifdef IPV6 - /* I dont think theres any faster way of matching two ipv6 addresses than memcmp - * Let me know if you think of one. - */ - if (!memcmp(a->second->ip4.s6_addr, user->ip4.s6_addr, sizeof(in6_addr))) - x++; -#else - if (a->second->ip4.s_addr == user->ip4.s_addr) - x++; -#endif + /* We have to match ip's as strings - we don't know what protocol + * a remote user may be using + */ + if (!strcasecmp(a->second->GetIPString(u1), this->GetIPString(u2))) + x++; } return x; } -long FindMatchingLocal(userrec* user) +long userrec::LocalCloneCount() { long x = 0; - for (std::vector::const_iterator a = local_users.begin(); a != local_users.end(); a++) + for (std::vector::const_iterator a = ServerInstance->local_users.begin(); a != ServerInstance->local_users.end(); a++) { userrec* comp = *a; #ifdef IPV6 /* I dont think theres any faster way of matching two ipv6 addresses than memcmp */ - if (!memcmp(comp->ip4.s6_addr, user->ip4.s6_addr, sizeof(in6_addr))) + in6_addr* s1 = &(((sockaddr_in6*)comp->ip)->sin6_addr); + in6_addr* s2 = &(((sockaddr_in6*)this->ip)->sin6_addr); + if (!memcmp(s1->s6_addr, s2->s6_addr, sizeof(in6_addr))) x++; #else - if (comp->ip4.s_addr == user->ip4.s_addr) + in_addr* s1 = &((sockaddr_in*)comp->ip)->sin_addr; + in_addr* s2 = &((sockaddr_in*)this->ip)->sin_addr; + if (s1->s_addr == s2->s_addr) x++; #endif } return x; } -void FullConnectUser(userrec* user, CullList* Goners) +void userrec::FullConnect(CullList* Goners) { ServerInstance->stats->statsConnects++; - user->idle_lastmsg = TIME; - log(DEBUG,"ConnectUser: %s",user->nick); + this->idle_lastmsg = TIME; + + ConnectClass a = this->GetClass(); - ConnectClass a = GetClass(user); - if (a.type == CC_DENY) { - Goners->AddItem(user,"Unauthorised connection"); + Goners->AddItem(this,"Unauthorised connection"); return; } - if ((*(a.pass.c_str())) && (!user->haspassed)) + if ((*(a.pass.c_str())) && (!this->haspassed)) { - Goners->AddItem(user,"Invalid password"); + Goners->AddItem(this,"Invalid password"); return; } - if (FindMatchingLocal(user) > a.maxlocal) + if (this->LocalCloneCount() > a.maxlocal) { - Goners->AddItem(user,"No more connections allowed from your host via this connect class (local)"); - WriteOpers("*** WARNING: maximum LOCAL connections (%ld) exceeded for IP %s",a.maxlocal,insp_ntoa(user->ip4)); + Goners->AddItem(this, "No more connections allowed from your host via this connect class (local)"); + ServerInstance->WriteOpers("*** WARNING: maximum LOCAL connections (%ld) exceeded for IP %s", a.maxlocal, this->GetIPString()); return; } - else if (FindMatchingGlobal(user) > a.maxglobal) + else if (this->GlobalCloneCount() > a.maxglobal) { - Goners->AddItem(user,"No more connections allowed from your host via this connect class (global)"); - WriteOpers("*** WARNING: maximum GLOBAL connections (%ld) exceeded for IP %s",a.maxglobal,insp_ntoa(user->ip4)); + Goners->AddItem(this, "No more connections allowed from your host via this connect class (global)"); + ServerInstance->WriteOpers("*** WARNING: maximum GLOBAL connections (%ld) exceeded for IP %s",a.maxglobal, this->GetIPString()); return; } char match_against[MAXBUF]; - snprintf(match_against,MAXBUF,"%s@%s",user->ident,user->host); + snprintf(match_against,MAXBUF,"%s@%s", this->ident, this->host); char* e = matches_exception(match_against); - + if (!e) { char* r = matches_gline(match_against); @@ -909,7 +1009,7 @@ void FullConnectUser(userrec* user, CullList* Goners) { char reason[MAXBUF]; snprintf(reason,MAXBUF,"G-Lined: %s",r); - Goners->AddItem(user,reason); + Goners->AddItem(this, reason); return; } @@ -919,21 +1019,21 @@ void FullConnectUser(userrec* user, CullList* Goners) { char reason[MAXBUF]; snprintf(reason,MAXBUF,"K-Lined: %s",r); - Goners->AddItem(user,reason); + Goners->AddItem(this, reason); return; } } - WriteServ(user->fd,"NOTICE Auth :Welcome to \002%s\002!",Config->Network); - WriteServ(user->fd,"001 %s :Welcome to the %s IRC Network %s!%s@%s",user->nick,Config->Network,user->nick,user->ident,user->host); - WriteServ(user->fd,"002 %s :Your host is %s, running version %s",user->nick,Config->ServerName,VERSION); - WriteServ(user->fd,"003 %s :This server was created %s %s",user->nick,__TIME__,__DATE__); - WriteServ(user->fd,"004 %s %s %s iowghrasxRVSCWBG lvhopsmntikrcaqbegIOLQRSKVHGCNT vhobeIaqglk",user->nick,Config->ServerName,VERSION); - + this->WriteServ("NOTICE Auth :Welcome to \002%s\002!",ServerInstance->Config->Network); + this->WriteServ("001 %s :Welcome to the %s IRC Network %s!%s@%s",this->nick, ServerInstance->Config->Network, this->nick, this->ident, this->host); + this->WriteServ("002 %s :Your host is %s, running version %s",this->nick,ServerInstance->Config->ServerName,VERSION); + this->WriteServ("003 %s :This server was created %s %s", this->nick, __TIME__, __DATE__); + this->WriteServ("004 %s %s %s %s %s %s", this->nick, ServerInstance->Config->ServerName, VERSION, ServerInstance->ModeGrok->UserModeList().c_str(), ServerInstance->ModeGrok->ChannelModeList().c_str(), ServerInstance->ModeGrok->ParaModeList().c_str()); + // anfl @ #ratbox, efnet reminded me that according to the RFC this cant contain more than 13 tokens per line... // so i'd better split it :) - std::stringstream out(Config->data005); + std::stringstream out(ServerInstance->Config->data005); std::string token = ""; std::string line5 = ""; int token_counter = 0; @@ -946,92 +1046,720 @@ void FullConnectUser(userrec* user, CullList* Goners) if ((token_counter >= 13) || (out.eof() == true)) { - WriteServ(user->fd,"005 %s %s:are supported by this server",user->nick,line5.c_str()); + this->WriteServ("005 %s %s:are supported by this server", this->nick, line5.c_str()); line5 = ""; token_counter = 0; } } - ShowMOTD(user); + ShowMOTD(this); /* * fix 3 by brain, move registered = 7 below these so that spurious modes and host * changes dont go out onto the network and produce 'fake direction'. */ - FOREACH_MOD(I_OnUserConnect,OnUserConnect(user)); - FOREACH_MOD(I_OnGlobalConnect,OnGlobalConnect(user)); - user->registered = REG_ALL; - WriteOpers("*** Client connecting on port %lu: %s!%s@%s [%s]",(unsigned long)user->port,user->nick,user->ident,user->host,insp_ntoa(user->ip4)); + FOREACH_MOD(I_OnUserConnect,OnUserConnect(this)); + FOREACH_MOD(I_OnGlobalConnect,OnGlobalConnect(this)); + this->registered = REG_ALL; + ServerInstance->WriteOpers("*** Client connecting on port %d: %s!%s@%s [%s]", this->GetPort(), this->nick, this->ident, this->host, this->GetIPString()); } -/** ReHashNick() +/** userrec::UpdateNick() * re-allocates a nick in the user_hash after they change nicknames, * returns a pointer to the new user as it may have moved */ -userrec* ReHashNick(const char* Old, const char* New) +userrec* userrec::UpdateNickHash(const char* New) { //user_hash::iterator newnick; - user_hash::iterator oldnick = clientlist.find(Old); - - log(DEBUG,"ReHashNick: %s %s",Old,New); + user_hash::iterator oldnick = ServerInstance->clientlist.find(this->nick); - if (!strcasecmp(Old,New)) - { - log(DEBUG,"old nick is new nick, skipping"); + if (!strcasecmp(this->nick,New)) return oldnick->second; - } - if (oldnick == clientlist.end()) + if (oldnick == ServerInstance->clientlist.end()) return NULL; /* doesnt exist */ - log(DEBUG,"ReHashNick: Found hashed nick %s",Old); - userrec* olduser = oldnick->second; - clientlist[New] = olduser; - clientlist.erase(oldnick); - - log(DEBUG,"ReHashNick: Nick rehashed as %s",New); - - return clientlist[New]; + ServerInstance->clientlist[New] = olduser; + ServerInstance->clientlist.erase(oldnick); + return ServerInstance->clientlist[New]; } -void force_nickchange(userrec* user,const char* newnick) +bool userrec::ForceNickChange(const char* newnick) { char nick[MAXBUF]; int MOD_RESULT = 0; *nick = 0; - FOREACH_RESULT(I_OnUserPreNick,OnUserPreNick(user,newnick)); + FOREACH_RESULT(I_OnUserPreNick,OnUserPreNick(this, newnick)); if (MOD_RESULT) { ServerInstance->stats->statsCollisions++; - kill_link(user,"Nickname collision"); - return; + return false; } if (matches_qline(newnick)) { ServerInstance->stats->statsCollisions++; - kill_link(user,"Nickname collision"); + return false; + } + + if (newnick) + { + strlcpy(this->nick, newnick, NICKMAX - 1); + } + if (this->registered == REG_ALL) + { + const char* pars[1]; + pars[0] = nick; + std::string cmd = "NICK"; + ServerInstance->Parser->CallHandler(cmd, pars, 1, this); + } + return true; +} + +void userrec::SetSockAddr(int protocol_family, const char* ip, int port) +{ + switch (protocol_family) + { +#ifdef SUPPORT_IP6LINKS + case AF_INET6: + { + log(DEBUG,"Set inet6 protocol address"); + sockaddr_in6* sin = new sockaddr_in6; + sin->sin6_family = AF_INET6; + sin->sin6_port = port; + inet_pton(AF_INET6, ip, &sin->sin6_addr); + this->ip = (sockaddr*)sin; + } + break; +#endif + case AF_INET: + { + log(DEBUG,"Set inet4 protocol address"); + sockaddr_in* sin = new sockaddr_in; + sin->sin_family = AF_INET; + sin->sin_port = port; + inet_pton(AF_INET, ip, &sin->sin_addr); + this->ip = (sockaddr*)sin; + } + break; + default: + log(DEBUG,"Ut oh, I dont know protocol %d to be set on '%s'!", protocol_family, this->nick); + break; + } +} + +int userrec::GetPort() +{ + if (this->ip == NULL) + return 0; + + switch (this->GetProtocolFamily()) + { +#ifdef SUPPORT_IP6LINKS + case AF_INET6: + { + sockaddr_in6* sin = (sockaddr_in6*)this->ip; + return sin->sin6_port; + } + break; +#endif + case AF_INET: + { + sockaddr_in* sin = (sockaddr_in*)this->ip; + return sin->sin_port; + } + break; + default: + log(DEBUG,"Ut oh, '%s' has an unknown protocol family!",this->nick); + break; + } + return 0; +} + +int userrec::GetProtocolFamily() +{ + if (this->ip == NULL) + return 0; + + sockaddr_in* sin = (sockaddr_in*)this->ip; + return sin->sin_family; +} + +const char* userrec::GetIPString() +{ + static char buf[1024]; + static char temp[1024]; + + if (this->ip == NULL) + return ""; + + switch (this->GetProtocolFamily()) + { +#ifdef SUPPORT_IP6LINKS + case AF_INET6: + { + sockaddr_in6* sin = (sockaddr_in6*)this->ip; + inet_ntop(sin->sin6_family, &sin->sin6_addr, buf, sizeof(buf)); + /* IP addresses starting with a : on irc are a Bad Thing (tm) */ + if (*buf == ':') + { + strlcpy(&temp[1], buf, sizeof(temp)); + *temp = '0'; + return temp; + } + return buf; + } + break; +#endif + case AF_INET: + { + sockaddr_in* sin = (sockaddr_in*)this->ip; + inet_ntop(sin->sin_family, &sin->sin_addr, buf, sizeof(buf)); + return buf; + } + break; + default: + log(DEBUG,"Ut oh, '%s' has an unknown protocol family!",this->nick); + break; + } + return ""; +} + +const char* userrec::GetIPString(char* buf) +{ + static char temp[1024]; + + if (this->ip == NULL) + { + *buf = 0; + return buf; + } + + switch (this->GetProtocolFamily()) + { +#ifdef SUPPORT_IP6LINKS + case AF_INET6: + { + sockaddr_in6* sin = (sockaddr_in6*)this->ip; + inet_ntop(sin->sin6_family, &sin->sin6_addr, buf, sizeof(buf)); + /* IP addresses starting with a : on irc are a Bad Thing (tm) */ + if (*buf == ':') + { + strlcpy(&temp[1], buf, sizeof(temp)); + *temp = '0'; + strlcpy(buf, temp, sizeof(temp)); + } + return buf; + } + break; +#endif + case AF_INET: + { + sockaddr_in* sin = (sockaddr_in*)this->ip; + inet_ntop(sin->sin_family, &sin->sin_addr, buf, sizeof(buf)); + return buf; + } + break; + + default: + log(DEBUG,"Ut oh, '%s' has an unknown protocol family!",this->nick); + break; + } + return ""; +} + + +void userrec::Write(const std::string &text) +{ + if ((this->fd < 0) || (this->fd > MAX_DESCRIPTORS)) + return; + + std::string crlf = text; + crlf.append("\r\n"); + + if (ServerInstance->Config->GetIOHook(this->GetPort())) + { + try + { + ServerInstance->Config->GetIOHook(this->GetPort())->OnRawSocketWrite(this->fd, crlf.data(), crlf.length()); + } + catch (ModuleException& modexcept) + { + log(DEBUG,"Module exception caught: %s",modexcept.GetReason()); + } + } + else + { + this->AddWriteBuf(crlf); + } + ServerInstance->stats->statsSent += crlf.length(); +} + +/** Write() + */ +void userrec::Write(const char *text, ...) +{ + va_list argsPtr; + char textbuffer[MAXBUF]; + + va_start(argsPtr, text); + vsnprintf(textbuffer, MAXBUF, text, argsPtr); + va_end(argsPtr); + + this->Write(std::string(textbuffer)); +} + +void userrec::WriteServ(const std::string& text) +{ + char textbuffer[MAXBUF]; + + snprintf(textbuffer,MAXBUF,":%s %s",ServerInstance->Config->ServerName,text.c_str()); + this->Write(std::string(textbuffer)); +} + +/** WriteServ() + * Same as Write(), except `text' is prefixed with `:server.name '. + */ +void userrec::WriteServ(const char* text, ...) +{ + va_list argsPtr; + char textbuffer[MAXBUF]; + + va_start(argsPtr, text); + vsnprintf(textbuffer, MAXBUF, text, argsPtr); + va_end(argsPtr); + + this->WriteServ(std::string(textbuffer)); +} + + +void userrec::WriteFrom(userrec *user, const std::string &text) +{ + char tb[MAXBUF]; + + snprintf(tb,MAXBUF,":%s %s",user->GetFullHost(),text.c_str()); + + this->Write(std::string(tb)); +} + + +/* write text from an originating user to originating user */ + +void userrec::WriteFrom(userrec *user, const char* text, ...) +{ + va_list argsPtr; + char textbuffer[MAXBUF]; + + va_start(argsPtr, text); + vsnprintf(textbuffer, MAXBUF, text, argsPtr); + va_end(argsPtr); + + this->WriteFrom(user, std::string(textbuffer)); +} + + +/* write text to an destination user from a source user (e.g. user privmsg) */ + +void userrec::WriteTo(userrec *dest, const char *data, ...) +{ + char textbuffer[MAXBUF]; + va_list argsPtr; + + va_start(argsPtr, data); + vsnprintf(textbuffer, MAXBUF, data, argsPtr); + va_end(argsPtr); + + this->WriteTo(dest, std::string(textbuffer)); +} + +void userrec::WriteTo(userrec *dest, const std::string &data) +{ + dest->WriteFrom(this, data); +} + + +void userrec::WriteCommon(const char* text, ...) +{ + char textbuffer[MAXBUF]; + va_list argsPtr; + + if (this->registered != REG_ALL) + return; + + va_start(argsPtr, text); + vsnprintf(textbuffer, MAXBUF, text, argsPtr); + va_end(argsPtr); + + this->WriteCommon(std::string(textbuffer)); +} + +void userrec::WriteCommon(const std::string &text) +{ + bool sent_to_at_least_one = false; + + if (this->registered != REG_ALL) + return; + + uniq_id++; + + for (std::vector::const_iterator v = this->chans.begin(); v != this->chans.end(); v++) + { + ucrec *n = *v; + if (n->channel) + { + CUList *ulist= n->channel->GetUsers(); + + for (CUList::iterator i = ulist->begin(); i != ulist->end(); i++) + { + if ((IS_LOCAL(i->second)) && (already_sent[i->second->fd] != uniq_id)) + { + already_sent[i->second->fd] = uniq_id; + i->second->WriteFrom(this, std::string(text)); + sent_to_at_least_one = true; + } + } + } + } + + /* + * if the user was not in any channels, no users will receive the text. Make sure the user + * receives their OWN message for WriteCommon + */ + if (!sent_to_at_least_one) + { + this->WriteFrom(this,std::string(text)); + } +} + + +/* write a formatted string to all users who share at least one common + * channel, NOT including the source user e.g. for use in QUIT + */ + +void userrec::WriteCommonExcept(const char* text, ...) +{ + char textbuffer[MAXBUF]; + va_list argsPtr; + + va_start(argsPtr, text); + vsnprintf(textbuffer, MAXBUF, text, argsPtr); + va_end(argsPtr); + + this->WriteCommonExcept(std::string(textbuffer)); +} + +void userrec::WriteCommonExcept(const std::string &text) +{ + bool quit_munge = true; + char oper_quit[MAXBUF]; + char textbuffer[MAXBUF]; + + strlcpy(textbuffer, text.c_str(), MAXBUF); + + if (this->registered != REG_ALL) + return; + + uniq_id++; + + /* TODO: We need some form of WriteCommonExcept that will send two lines, one line to + * opers and the other line to non-opers, then all this hidebans and hidesplits gunk + * can go byebye. + */ + if (ServerInstance->Config->HideSplits) + { + char* check = textbuffer + 6; + + if (!strncasecmp(textbuffer, "QUIT :",6)) + { + std::stringstream split(check); + std::string server_one; + std::string server_two; + + split >> server_one; + split >> server_two; + + if ((ServerInstance->FindServerName(server_one)) && (ServerInstance->FindServerName(server_two))) + { + strlcpy(oper_quit,textbuffer,MAXQUIT); + strlcpy(check,"*.net *.split",MAXQUIT); + quit_munge = true; + } + } + } + + if ((ServerInstance->Config->HideBans) && (!quit_munge)) + { + if ((!strncasecmp(textbuffer, "QUIT :G-Lined:",14)) || (!strncasecmp(textbuffer, "QUIT :K-Lined:",14)) + || (!strncasecmp(textbuffer, "QUIT :Q-Lined:",14)) || (!strncasecmp(textbuffer, "QUIT :Z-Lined:",14))) + { + char* check = textbuffer + 13; + strlcpy(oper_quit,textbuffer,MAXQUIT); + *check = 0; // We don't need to strlcpy, we just chop it from the : + quit_munge = true; + } + } + + for (std::vector::const_iterator v = this->chans.begin(); v != this->chans.end(); v++) + { + ucrec* n = *v; + if (n->channel) + { + CUList *ulist= n->channel->GetUsers(); + + for (CUList::iterator i = ulist->begin(); i != ulist->end(); i++) + { + if (this != i->second) + { + if ((IS_LOCAL(i->second)) && (already_sent[i->second->fd] != uniq_id)) + { + already_sent[i->second->fd] = uniq_id; + if (quit_munge) + i->second->WriteFrom(this, *i->second->oper ? std::string(oper_quit) : std::string(textbuffer)); + else + i->second->WriteFrom(this, std::string(textbuffer)); + } + } + } + } + } + +} + +void userrec::WriteWallOps(const std::string &text) +{ + /* Does nothing if theyre not opered */ + if ((!*this->oper) && (IS_LOCAL(this))) return; + + std::string wallop = "WALLOPS :"; + wallop.append(text); + + for (std::vector::const_iterator i = ServerInstance->local_users.begin(); i != ServerInstance->local_users.end(); i++) + { + userrec* t = *i; + if ((IS_LOCAL(t)) && (t->modes[UM_WALLOPS])) + this->WriteTo(t,wallop); } +} + +void userrec::WriteWallOps(const char* text, ...) +{ + char textbuffer[MAXBUF]; + va_list argsPtr; - if (user) + va_start(argsPtr, text); + vsnprintf(textbuffer, MAXBUF, text, argsPtr); + va_end(argsPtr); + + this->WriteWallOps(std::string(textbuffer)); +} + +/* return 0 or 1 depending if users u and u2 share one or more common channels + * (used by QUIT, NICK etc which arent channel specific notices) + * + * The old algorithm in 1.0 for this was relatively inefficient, iterating over + * the first users channels then the second users channels within the outer loop, + * therefore it was a maximum of x*y iterations (upon returning 0 and checking + * all possible iterations). However this new function instead checks against the + * channel's userlist in the inner loop which is a std::map + * and saves us time as we already know what pointer value we are after. + * Don't quote me on the maths as i am not a mathematician or computer scientist, + * but i believe this algorithm is now x+(log y) maximum iterations instead. + */ +bool userrec::SharesChannelWith(userrec *other) +{ + if ((!other) || (this->registered != REG_ALL) || (other->registered != REG_ALL)) + return false; + + /* Outer loop */ + for (std::vector::const_iterator i = this->chans.begin(); i != this->chans.end(); i++) { - if (newnick) + /* Fetch the channel from the user */ + ucrec* user_channel = *i; + + if (user_channel->channel) { - strlcpy(nick,newnick,MAXBUF-1); + /* Eliminate the inner loop (which used to be ~equal in size to the outer loop) + * by replacing it with a map::find which *should* be more efficient + */ + if (user_channel->channel->HasUser(other)) + return true; } + } + return false; +} + +int userrec::CountChannels() +{ + int z = 0; + for (std::vector::const_iterator i = this->chans.begin(); i != this->chans.end(); i++) + if ((*i)->channel) + z++; + return z; +} + +bool userrec::ChangeName(const char* gecos) +{ + if (IS_LOCAL(this)) + { + int MOD_RESULT = 0; + FOREACH_RESULT(I_OnChangeLocalUserGECOS,OnChangeLocalUserGECOS(this,gecos)); + if (MOD_RESULT) + return false; + FOREACH_MOD(I_OnChangeName,OnChangeName(this,gecos)); + } + strlcpy(this->fullname,gecos,MAXGECOS+1); + return true; +} - if (user->registered == REG_ALL) +bool userrec::ChangeDisplayedHost(const char* host) +{ + if (IS_LOCAL(this)) + { + int MOD_RESULT = 0; + FOREACH_RESULT(I_OnChangeLocalUserHost,OnChangeLocalUserHost(this,host)); + if (MOD_RESULT) + return false; + FOREACH_MOD(I_OnChangeHost,OnChangeHost(this,host)); + } + strlcpy(this->dhost,host,63); + + if (IS_LOCAL(this)) + this->WriteServ("396 %s %s :is now your hidden host",this->nick,this->dhost); + + return true; +} + +void userrec::NoticeAll(char* text, ...) +{ + char textbuffer[MAXBUF]; + char formatbuffer[MAXBUF]; + va_list argsPtr; + + va_start(argsPtr, text); + vsnprintf(textbuffer, MAXBUF, text, argsPtr); + va_end(argsPtr); + + snprintf(formatbuffer,MAXBUF,"NOTICE $* :%s",textbuffer); + + for (std::vector::const_iterator i = ServerInstance->local_users.begin(); i != ServerInstance->local_users.end(); i++) + { + userrec* t = *i; + t->WriteFrom(this, std::string(formatbuffer)); + } +} + + +std::string userrec::ChannelList(userrec* source) +{ + std::string list; + for (std::vector::const_iterator i = this->chans.begin(); i != this->chans.end(); i++) + { + ucrec* rec = *i; + + if(rec->channel && rec->channel->name) { - const char* pars[1]; - pars[0] = nick; - std::string cmd = "NICK"; + /* If the target is the same as the sender, let them see all their channels. + * If the channel is NOT private/secret OR the user shares a common channel + * If the user is an oper, and the option is set. + */ + if ((source == this) || (*source->oper && ServerInstance->Config->OperSpyWhois) || (((!rec->channel->modes[CM_PRIVATE]) && (!rec->channel->modes[CM_SECRET])) || (rec->channel->HasUser(source)))) + { + list.append(rec->channel->GetStatusChar(this)).append(rec->channel->name).append(" "); + } + } + } + return list; +} + +void userrec::SplitChanList(userrec* dest, const std::string &cl) +{ + std::string line; + std::ostringstream prefix; + std::string::size_type start, pos, length; - ServerInstance->Parser->CallHandler(cmd,pars,1,user); + prefix << ":" << ServerInstance->Config->ServerName << " 319 " << this->nick << " " << dest->nick << " :"; + line = prefix.str(); + + for (start = 0; (pos = cl.find(' ', start)) != std::string::npos; start = pos+1) + { + length = (pos == std::string::npos) ? cl.length() : pos; + + if (line.length() + length - start > 510) + { + this->Write(line); + line = prefix.str(); + } + + if(pos == std::string::npos) + { + line += cl.substr(start, length - start); + break; + } + else + { + line += cl.substr(start, length - start + 1); } } + + if (line.length()) + { + this->Write(line); + } +} + + +/* looks up a users password for their connection class (/ tags) + * NOTE: If the or tag specifies an ip, and this user resolves, + * then their ip will be taken as 'priority' anyway, so for example, + * will match joe!bloggs@localhost + */ +ConnectClass& userrec::GetClass() +{ + for (ClassVector::iterator i = ServerInstance->Config->Classes.begin(); i != ServerInstance->Config->Classes.end(); i++) + { + if ((match(this->GetIPString(),i->host.c_str(),true)) || (match(this->host,i->host.c_str()))) + return *i; + } + + return *(ServerInstance->Config->Classes.begin()); +} + +void userrec::PurgeEmptyChannels() +{ + std::vector to_delete; + + // firstly decrement the count on each channel + for (std::vector::iterator f = this->chans.begin(); f != this->chans.end(); f++) + { + ucrec* uc = *f; + if (uc->channel) + { + if (uc->channel->DelUser(this) == 0) + { + /* No users left in here, mark it for deletion */ + to_delete.push_back(uc->channel); + uc->channel = NULL; + } + } + } + + for (std::vector::iterator n = to_delete.begin(); n != to_delete.end(); n++) + { + chanrec* thischan = *n; + chan_hash::iterator i2 = ServerInstance->chanlist.find(thischan->name); + if (i2 != ServerInstance->chanlist.end()) + { + FOREACH_MOD(I_OnChannelDelete,OnChannelDelete(i2->second)); + DELETE(i2->second); + ServerInstance->chanlist.erase(i2); + } + } + + this->UnOper(); } +