diff options
author | brain <brain@e03df62e-2008-0410-955e-edbf42e46eb7> | 2005-12-15 13:44:17 +0000 |
---|---|---|
committer | brain <brain@e03df62e-2008-0410-955e-edbf42e46eb7> | 2005-12-15 13:44:17 +0000 |
commit | 0c55e6d24b32f2cc4e2b8106f2f4032e3197c211 (patch) | |
tree | f3a1995ae58a646d1002b2b9ffe39911e825544b /src/users.cpp | |
parent | 02bfa585d05101058c6244a6aca1a5e030152f41 (diff) |
Moved a ton of user related functions from inspircd.cpp to users.cpp
git-svn-id: http://svn.inspircd.org/repository/trunk/inspircd@2468 e03df62e-2008-0410-955e-edbf42e46eb7
Diffstat (limited to 'src/users.cpp')
-rw-r--r-- | src/users.cpp | 436 |
1 files changed, 414 insertions, 22 deletions
diff --git a/src/users.cpp b/src/users.cpp index 35279cee6..06c34955e 100644 --- a/src/users.cpp +++ b/src/users.cpp @@ -32,6 +32,7 @@ using namespace std; extern ServerConfig* Config; extern time_t TIME; +std::vector<userrec*> all_opers; userrec::userrec() { @@ -93,12 +94,14 @@ char* userrec::GetFullRealHost() return fresult; } -bool userrec::IsInvited(char* channel) +bool userrec::IsInvited(irc::string &channel) { for (InvitedList::iterator i = invites.begin(); i != invites.end(); i++) { - if (i->channel) { - if (!strcasecmp(i->channel,channel)) + if (i->channel) + { + irc::string compare = i->channel; + if (compare == channel) { return true; } @@ -112,36 +115,34 @@ InvitedList* userrec::GetInviteList() return &invites; } -void userrec::InviteTo(char* channel) +void userrec::InviteTo(irc::string &channel) { Invited i; - strlcpy(i.channel,channel,CHANMAX); + i.channel = channel; invites.push_back(i); } -void userrec::RemoveInvite(char* channel) +void userrec::RemoveInvite(std::string &channel) { log(DEBUG,"Removing invites"); - if (channel) + if (invites.size()) { - if (invites.size()) + for (InvitedList::iterator i = invites.begin(); i != invites.end(); i++) { - for (InvitedList::iterator i = invites.begin(); i != invites.end(); i++) - { - if (i->channel) + if (i->channel) + { + irc::string compare = i->channel; + if (compare == channel) { - if (!strcasecmp(i->channel,channel)) - { - invites.erase(i); - return; - } - } - } - } - } + invites.erase(i); + return; + } + } + } + } } -bool userrec::HasPermission(char* command) +bool userrec::HasPermission(std::string &command) { char TypeName[MAXBUF],Classes[MAXBUF],ClassName[MAXBUF],CommandList[MAXBUF]; char* mycmd; @@ -178,7 +179,7 @@ bool userrec::HasPermission(char* command) mycmd = strtok_r(CommandList," ",&savept2); while (mycmd) { - if ((!strcasecmp(mycmd,command)) || (*mycmd == '*')) + if ((!strcasecmp(mycmd,command.c_str())) || (*mycmd == '*')) { return true; } @@ -301,3 +302,394 @@ std::string userrec::GetWriteError() { return this->WriteError; } + +void AddOper(userrec* user) +{ + log(DEBUG,"Oper added to optimization list"); + all_opers.push_back(user); +} + +void DeleteOper(userrec* user) +{ + for (std::vector<userrec*>::iterator a = all_opers.begin(); a < all_opers.end(); a++) + { + if (*a == user) + { + log(DEBUG,"Oper removed from optimization list"); + all_opers.erase(a); + return; + } + } +} + +void kill_link(userrec *user,const char* r) +{ + user_hash::iterator iter = clientlist.find(user->nick); + + char reason[MAXBUF]; + + strncpy(reason,r,MAXBUF); + + if (strlen(reason)>MAXQUIT) + { + reason[MAXQUIT-1] = '\0'; + } + + log(DEBUG,"kill_link: %s '%s'",user->nick,reason); + Write(user->fd,"ERROR :Closing link (%s@%s) [%s]",user->ident,user->host,reason); + log(DEBUG,"closing fd %lu",(unsigned long)user->fd); + + if (user->registered == 7) { + FOREACH_MOD OnUserQuit(user,reason); + WriteCommonExcept(user,"QUIT :%s",reason); + } + + user->FlushWriteBuf(); + + FOREACH_MOD OnUserDisconnect(user); + + if (user->fd > -1) + { + FOREACH_MOD OnRawSocketClose(user->fd); + SE->DelFd(user->fd); + user->CloseSocket(); + } + + // this must come before the WriteOpers so that it doesnt try to fill their buffer with anything + // if they were an oper with +s. + if (user->registered == 7) { + purge_empty_chans(user); + // fix by brain: only show local quits because we only show local connects (it just makes SENSE) + if (user->fd > -1) + WriteOpers("*** Client exiting: %s!%s@%s [%s]",user->nick,user->ident,user->host,reason); + AddWhoWas(user); + } + + if (iter != clientlist.end()) + { + log(DEBUG,"deleting user hash value %lu",(unsigned long)user); + if (user->fd > -1) + fd_ref_table[user->fd] = NULL; + clientlist.erase(iter); + } + delete user; +} + +void kill_link_silent(userrec *user,const char* r) +{ + user_hash::iterator iter = clientlist.find(user->nick); + + char reason[MAXBUF]; + + strncpy(reason,r,MAXBUF); + + if (strlen(reason)>MAXQUIT) + { + reason[MAXQUIT-1] = '\0'; + } + + log(DEBUG,"kill_link: %s '%s'",user->nick,reason); + Write(user->fd,"ERROR :Closing link (%s@%s) [%s]",user->ident,user->host,reason); + log(DEBUG,"closing fd %lu",(unsigned long)user->fd); + + user->FlushWriteBuf(); + + if (user->registered == 7) { + FOREACH_MOD OnUserQuit(user,reason); + WriteCommonExcept(user,"QUIT :%s",reason); + } + + FOREACH_MOD OnUserDisconnect(user); + + if (user->fd > -1) + { + FOREACH_MOD OnRawSocketClose(user->fd); + SE->DelFd(user->fd); + user->CloseSocket(); + } + + if (user->registered == 7) { + purge_empty_chans(user); + } + + if (iter != clientlist.end()) + { + log(DEBUG,"deleting user hash value %lu",(unsigned long)user); + if (user->fd > -1) + fd_ref_table[user->fd] = NULL; + clientlist.erase(iter); + } + delete user; +} + + +/* adds or updates an entry in the whowas list */ +void AddWhoWas(userrec* u) +{ + whowas_hash::iterator iter = whowas.find(u->nick); + WhoWasUser *a = new WhoWasUser(); + strlcpy(a->nick,u->nick,NICKMAX); + strlcpy(a->ident,u->ident,IDENTMAX); + strlcpy(a->dhost,u->dhost,160); + strlcpy(a->host,u->host,160); + strlcpy(a->fullname,u->fullname,MAXGECOS); + strlcpy(a->server,u->server,256); + a->signon = u->signon; + + /* MAX_WHOWAS: max number of /WHOWAS items + * WHOWAS_STALE: number of hours before a WHOWAS item is marked as stale and + * can be replaced by a newer one + */ + + if (iter == whowas.end()) + { + if (whowas.size() >= (unsigned)WHOWAS_MAX) + { + for (whowas_hash::iterator i = whowas.begin(); i != whowas.end(); i++) + { + // 3600 seconds in an hour ;) + if ((i->second->signon)<(TIME-(WHOWAS_STALE*3600))) + { + // delete the old one + if (i->second) delete i->second; + // replace with new one + i->second = a; + log(DEBUG,"added WHOWAS entry, purged an old record"); + return; + } + } + // no space left and user doesnt exist. Don't leave ram in use! + log(DEBUG,"Not able to update whowas (list at WHOWAS_MAX entries and trying to add new?), freeing excess ram"); + delete a; + } + else + { + log(DEBUG,"added fresh WHOWAS entry"); + whowas[a->nick] = a; + } + } + else + { + log(DEBUG,"updated WHOWAS entry"); + if (iter->second) delete iter->second; + iter->second = a; + } +} + +/* add a client connection to the sockets list */ +void AddClient(int socket, char* host, int port, bool iscached, char* ip) +{ + string tempnick; + char tn2[MAXBUF]; + user_hash::iterator iter; + + tempnick = ConvToStr(socket) + "-unknown"; + sprintf(tn2,"%lu-unknown",(unsigned long)socket); + + iter = clientlist.find(tempnick); + + // fix by brain. + // as these nicknames are 'RFC impossible', we can be sure nobody is going to be + // using one as a registered connection. As theyre per fd, we can also safely assume + // that we wont have collisions. Therefore, if the nick exists in the list, its only + // used by a dead socket, erase the iterator so that the new client may reclaim it. + // this was probably the cause of 'server ignores me when i hammer it with reconnects' + // issue in earlier alphas/betas + if (iter != clientlist.end()) + { + userrec* goner = iter->second; + delete goner; + clientlist.erase(iter); + } + + /* + * It is OK to access the value here this way since we know + * it exists, we just created it above. + * + * At NO other time should you access a value in a map or a + * hash_map this way. + */ + clientlist[tempnick] = new userrec(); + + NonBlocking(socket); + log(DEBUG,"AddClient: %lu %s %d %s",(unsigned long)socket,host,port,ip); + + clientlist[tempnick]->fd = socket; + strlcpy(clientlist[tempnick]->nick, tn2,NICKMAX); + strlcpy(clientlist[tempnick]->host, host,160); + strlcpy(clientlist[tempnick]->dhost, host,160); + clientlist[tempnick]->server = (char*)FindServerNamePtr(Config->ServerName); + strlcpy(clientlist[tempnick]->ident, "unknown",IDENTMAX); + clientlist[tempnick]->registered = 0; + clientlist[tempnick]->signon = TIME + Config->dns_timeout; + clientlist[tempnick]->lastping = 1; + clientlist[tempnick]->port = port; + strlcpy(clientlist[tempnick]->ip,ip,16); + + // set the registration timeout for this user + unsigned long class_regtimeout = 90; + int class_flood = 0; + long class_threshold = 5; + long class_sqmax = 262144; // 256kb + long class_rqmax = 4096; // 4k + + for (ClassVector::iterator i = Config->Classes.begin(); i != Config->Classes.end(); i++) + { + if (match(clientlist[tempnick]->host,i->host) && (i->type == CC_ALLOW)) + { + class_regtimeout = (unsigned long)i->registration_timeout; + class_flood = i->flood; + clientlist[tempnick]->pingmax = i->pingtime; + class_threshold = i->threshold; + class_sqmax = i->sendqmax; + class_rqmax = i->recvqmax; + break; + } + } + + clientlist[tempnick]->nping = TIME+clientlist[tempnick]->pingmax + Config->dns_timeout; + clientlist[tempnick]->timeout = TIME+class_regtimeout; + clientlist[tempnick]->flood = class_flood; + clientlist[tempnick]->threshold = class_threshold; + clientlist[tempnick]->sendqmax = class_sqmax; + clientlist[tempnick]->recvqmax = class_rqmax; + + ucrec a; + a.channel = NULL; + a.uc_modes = 0; + for (int i = 0; i < MAXCHANS; i++) + clientlist[tempnick]->chans.push_back(a); + + if (clientlist.size() > Config->SoftLimit) + { + kill_link(clientlist[tempnick],"No more connections allowed"); + return; + } + + if (clientlist.size() >= MAXCLIENTS) + { + kill_link(clientlist[tempnick],"No more connections allowed"); + return; + } + + // this is done as a safety check to keep the file descriptors within range of fd_ref_table. + // its a pretty big but for the moment valid assumption: + // file descriptors are handed out starting at 0, and are recycled as theyre freed. + // therefore if there is ever an fd over 65535, 65536 clients must be connected to the + // irc server at once (or the irc server otherwise initiating this many connections, files etc) + // which for the time being is a physical impossibility (even the largest networks dont have more + // than about 10,000 users on ONE server!) + if ((unsigned)socket > 65534) + { + kill_link(clientlist[tempnick],"Server is full"); + return; + } + char* e = matches_exception(ip); + if (!e) + { + char* r = matches_zline(ip); + if (r) + { + char reason[MAXBUF]; + snprintf(reason,MAXBUF,"Z-Lined: %s",r); + kill_link(clientlist[tempnick],reason); + return; + } + } + fd_ref_table[socket] = clientlist[tempnick]; + SE->AddFd(socket,true,X_ESTAB_CLIENT); +} + +void FullConnectUser(userrec* user) +{ + stats->statsConnects++; + user->idle_lastmsg = TIME; + log(DEBUG,"ConnectUser: %s",user->nick); + + if ((strcmp(Passwd(user),"")) && (!user->haspassed)) + { + kill_link(user,"Invalid password"); + return; + } + if (IsDenied(user)) + { + kill_link(user,"Unauthorised connection"); + return; + } + + char match_against[MAXBUF]; + snprintf(match_against,MAXBUF,"%s@%s",user->ident,user->host); + char* e = matches_exception(match_against); + if (!e) + { + char* r = matches_gline(match_against); + if (r) + { + char reason[MAXBUF]; + snprintf(reason,MAXBUF,"G-Lined: %s",r); + kill_link_silent(user,reason); + return; + } + r = matches_kline(user->host); + if (r) + { + char reason[MAXBUF]; + snprintf(reason,MAXBUF,"K-Lined: %s",r); + kill_link_silent(user,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 iowghraAsORVSxNCWqBzvdHtGI lvhopsmntikrRcaqOALQbSeKVfHGCuzN",user->nick,Config->ServerName,VERSION); + // the neatest way to construct the initial 005 numeric, considering the number of configure constants to go in it... + std::stringstream v; + v << "WALLCHOPS MODES=13 CHANTYPES=# PREFIX=(ohv)@%+ MAP SAFELIST MAXCHANNELS=" << MAXCHANS; + v << " MAXBANS=60 NICKLEN=" << NICKMAX; + v << " TOPICLEN=" << MAXTOPIC << " KICKLEN=" << MAXKICK << " MAXTARGETS=20 AWAYLEN=" << MAXAWAY << " CHANMODES=ohvb,k,l,psmnti NETWORK="; + v << Config->Network; + std::string data005 = v.str(); + FOREACH_MOD On005Numeric(data005); + // 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(data005); + std::string token = ""; + std::string line5 = ""; + int token_counter = 0; + while (!out.eof()) + { + out >> token; + line5 = line5 + token + " "; + token_counter++; + if ((token_counter >= 13) || (out.eof() == true)) + { + WriteServ(user->fd,"005 %s %s:are supported by this server",user->nick,line5.c_str()); + line5 = ""; + token_counter = 0; + } + } + ShowMOTD(user); + + // 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 OnUserConnect(user); + FOREACH_MOD OnGlobalConnect(user); + user->registered = 7; + WriteOpers("*** Client connecting on port %lu: %s!%s@%s [%s]",(unsigned long)user->port,user->nick,user->ident,user->host,user->ip); +} + + +/* shows the message of the day, and any other on-logon stuff */ +void ConnectUser(userrec *user) +{ + // dns is already done, things are fast. no need to wait for dns to complete just pass them straight on + if ((user->dns_done) && (user->registered >= 3) && (AllModulesReportReady(user))) + { + FullConnectUser(user); + } +} + |