]> git.netwichtig.de Git - user/henk/code/inspircd.git/blob - src/users.cpp
Adding hook type checking to event calls to speed them up
[user/henk/code/inspircd.git] / src / users.cpp
1 /*       +------------------------------------+
2  *       | Inspire Internet Relay Chat Daemon |
3  *       +------------------------------------+
4  *
5  *  Inspire is copyright (C) 2002-2006 ChatSpike-Dev.
6  *                       E-mail:
7  *                <brain@chatspike.net>
8  *                <Craig@chatspike.net>
9  *     
10  * Written by Craig Edwards, Craig McLure, and others.
11  * This program is free but copyrighted software; see
12  *            the file COPYING for details.
13  *
14  * ---------------------------------------------------
15  */
16
17 using namespace std;
18
19 #include "inspircd_config.h" 
20 #include "channels.h"
21 #include "connection.h"
22 #include "users.h"
23 #include "inspircd.h"
24 #include <stdio.h>
25 #ifdef THREADED_DNS
26 #include <pthread.h>
27 #include <signal.h>
28 #endif
29 #include "inspstring.h"
30 #include "commands.h"
31 #include "helperfuncs.h"
32 #include "typedefs.h"
33 #include "socketengine.h"
34 #include "hashcomp.h"
35 #include "message.h"
36 #include "wildcard.h"
37 #include "xline.h"
38
39 extern InspIRCd* ServerInstance;
40 extern int WHOWAS_STALE;
41 extern int WHOWAS_MAX;
42 extern std::vector<Module*> modules;
43 extern std::vector<ircd_module*> factory;
44 extern std::vector<InspSocket*> module_sockets;
45 extern int MODCOUNT;
46 extern InspSocket* socket_ref[65535];
47 extern time_t TIME;
48 extern userrec* fd_ref_table[65536];
49 extern ServerConfig *Config;
50 extern user_hash clientlist;
51 extern whowas_hash whowas;
52 extern std::vector<userrec*> local_users;
53
54 std::vector<userrec*> all_opers;
55
56 template<typename T> inline string ConvToStr(const T &in)
57 {
58         stringstream tmp;
59         if (!(tmp << in)) return string();
60         return tmp.str();
61 }
62
63 userrec::userrec()
64 {
65         // the PROPER way to do it, AVOID bzero at *ALL* costs
66         *nick = *ident = *host = *dhost = *fullname = *modes = *awaymsg = *oper = *ip = 0;
67         server = (char*)FindServerNamePtr(Config->ServerName);
68         reset_due = TIME;
69         lines_in = fd = lastping = signon = idle_lastmsg = nping = registered = 0;
70         timeout = flood = port = bytes_in = bytes_out = cmds_in = cmds_out = 0;
71         haspassed = dns_done = false;
72         recvq = "";
73         sendq = "";
74         chans.clear();
75         invites.clear();
76 }
77
78 userrec::~userrec()
79 {
80 }
81
82 void userrec::CloseSocket()
83 {
84         shutdown(this->fd,2);
85         close(this->fd);
86 }
87  
88 char* userrec::GetFullHost()
89 {
90         static char result[MAXBUF];
91         snprintf(result,MAXBUF,"%s!%s@%s",nick,ident,dhost);
92         return result;
93 }
94
95 int userrec::ReadData(void* buffer, size_t size)
96 {
97         if (this->fd > -1)
98         {
99                 return read(this->fd, buffer, size);
100         }
101         else return 0;
102 }
103
104
105 char* userrec::GetFullRealHost()
106 {
107         static char fresult[MAXBUF];
108         snprintf(fresult,MAXBUF,"%s!%s@%s",nick,ident,host);
109         return fresult;
110 }
111
112 bool userrec::IsInvited(irc::string &channel)
113 {
114         for (InvitedList::iterator i = invites.begin(); i != invites.end(); i++)
115         {
116                 irc::string compare = i->channel;
117                 if (compare == channel)
118                 {
119                         return true;
120                 }
121         }
122         return false;
123 }
124
125 InvitedList* userrec::GetInviteList()
126 {
127         return &invites;
128 }
129
130 void userrec::InviteTo(irc::string &channel)
131 {
132         Invited i;
133         i.channel = channel;
134         invites.push_back(i);
135 }
136
137 void userrec::RemoveInvite(irc::string &channel)
138 {
139         log(DEBUG,"Removing invites");
140         if (invites.size())
141         {
142                 for (InvitedList::iterator i = invites.begin(); i != invites.end(); i++)
143                 {
144                         irc::string compare = i->channel;
145                         if (compare == channel)
146                         {
147                                 invites.erase(i);
148                                 return;
149                         }
150                 }
151         }
152 }
153
154 bool userrec::HasPermission(std::string &command)
155 {
156         char TypeName[MAXBUF],Classes[MAXBUF],ClassName[MAXBUF],CommandList[MAXBUF];
157         char* mycmd;
158         char* savept;
159         char* savept2;
160         
161         // users on u-lined servers can completely bypass
162         // all permissions based checks.
163         //
164         // of course, if this is sent to a remote server and this
165         // server is not ulined there, then that other server
166         // silently drops the command.
167         if (is_uline(this->server))
168                 return true;
169         
170         // are they even an oper at all?
171         if (strchr(this->modes,'o'))
172         {
173                 for (int j =0; j < Config->ConfValueEnum("type",&Config->config_f); j++)
174                 {
175                         Config->ConfValue("type","name",j,TypeName,&Config->config_f);
176                         if (!strcmp(TypeName,this->oper))
177                         {
178                                 Config->ConfValue("type","classes",j,Classes,&Config->config_f);
179                                 char* myclass = strtok_r(Classes," ",&savept);
180                                 while (myclass)
181                                 {
182                                         for (int k =0; k < Config->ConfValueEnum("class",&Config->config_f); k++)
183                                         {
184                                                 Config->ConfValue("class","name",k,ClassName,&Config->config_f);
185                                                 if (!strcmp(ClassName,myclass))
186                                                 {
187                                                         Config->ConfValue("class","commands",k,CommandList,&Config->config_f);
188                                                         mycmd = strtok_r(CommandList," ",&savept2);
189                                                         while (mycmd)
190                                                         {
191                                                                 if ((!strcasecmp(mycmd,command.c_str())) || (*mycmd == '*'))
192                                                                 {
193                                                                         return true;
194                                                                 }
195                                                                 mycmd = strtok_r(NULL," ",&savept2);
196                                                         }
197                                                 }
198                                         }
199                                         myclass = strtok_r(NULL," ",&savept);
200                                 }
201                         }
202                 }
203         }
204         return false;
205 }
206
207
208 bool userrec::AddBuffer(std::string a)
209 {
210         std::string b = "";
211         for (unsigned int i = 0; i < a.length(); i++)
212                 if ((a[i] != '\r') && (a[i] != '\0') && (a[i] != 7))
213                         b = b + a[i];
214         std::stringstream stream(recvq);
215         stream << b;
216         recvq = stream.str();
217         unsigned int i = 0;
218         // count the size of the first line in the buffer.
219         while (i < recvq.length())
220         {
221                 if (recvq[i++] == '\n')
222                         break;
223         }
224         if (recvq.length() > (unsigned)this->recvqmax)
225         {
226                 this->SetWriteError("RecvQ exceeded");
227                 WriteOpers("*** User %s RecvQ of %d exceeds connect class maximum of %d",this->nick,recvq.length(),this->recvqmax);
228         }
229         // return false if we've had more than 600 characters WITHOUT
230         // a carriage return (this is BAD, drop the socket)
231         return (i < 600);
232 }
233
234 bool userrec::BufferIsReady()
235 {
236         for (unsigned int i = 0; i < recvq.length(); i++)
237                 if (recvq[i] == '\n')
238                         return true;
239         return false;
240 }
241
242 void userrec::ClearBuffer()
243 {
244         recvq = "";
245 }
246
247 std::string userrec::GetBuffer()
248 {
249         if (recvq == "")
250                 return "";
251         char* line = (char*)recvq.c_str();
252         std::string ret = "";
253         while ((*line != '\n') && (strlen(line)))
254         {
255                 ret = ret + *line;
256                 line++;
257         }
258         if ((*line == '\n') || (*line == '\r'))
259                 line++;
260         recvq = line;
261         return ret;
262 }
263
264 void userrec::AddWriteBuf(std::string data)
265 {
266         if (this->GetWriteError() != "")
267                 return;
268         if (sendq.length() + data.length() > (unsigned)this->sendqmax)
269         {
270                 /* Fix by brain - Set the error text BEFORE calling writeopers, because
271                  * if we dont it'll recursively  call here over and over again trying
272                  * to repeatedly add the text to the sendq!
273                  */
274                 this->SetWriteError("SendQ exceeded");
275                 WriteOpers("*** User %s SendQ of %d exceeds connect class maximum of %d",this->nick,sendq.length() + data.length(),this->sendqmax);
276                 return;
277         }
278         std::stringstream stream;
279         stream << sendq << data;
280         sendq = stream.str();
281 }
282
283 // send AS MUCH OF THE USERS SENDQ as we are able to (might not be all of it)
284 void userrec::FlushWriteBuf()
285 {
286         if (sendq.length())
287         {
288                 char* tb = (char*)this->sendq.c_str();
289                 int n_sent = write(this->fd,tb,this->sendq.length());
290                 if (n_sent == -1)
291                 {
292                         this->SetWriteError(strerror(errno));
293                 }
294                 else
295                 {
296                         // advance the queue
297                         tb += n_sent;
298                         this->sendq = tb;
299                         // update the user's stats counters
300                         this->bytes_out += n_sent;
301                         this->cmds_out++;
302                 }
303         }
304 }
305
306 void userrec::SetWriteError(std::string error)
307 {
308         log(DEBUG,"Setting error string for %s to '%s'",this->nick,error.c_str());
309         // don't try to set the error twice, its already set take the first string.
310         if (this->WriteError == "")
311                 this->WriteError = error;
312 }
313
314 std::string userrec::GetWriteError()
315 {
316         return this->WriteError;
317 }
318
319 void AddOper(userrec* user)
320 {
321         log(DEBUG,"Oper added to optimization list");
322         all_opers.push_back(user);
323 }
324
325 void DeleteOper(userrec* user)
326 {
327         for (std::vector<userrec*>::iterator a = all_opers.begin(); a < all_opers.end(); a++)
328         {
329                 if (*a == user)
330                 {
331                         log(DEBUG,"Oper removed from optimization list");
332                         all_opers.erase(a);
333                         return;
334                 }
335         }
336 }
337
338 void kill_link(userrec *user,const char* r)
339 {
340         user_hash::iterator iter = clientlist.find(user->nick);
341
342         char reason[MAXBUF];
343
344         strncpy(reason,r,MAXBUF);
345
346         if (strlen(reason)>MAXQUIT)
347         {
348                 reason[MAXQUIT-1] = '\0';
349         }
350
351         log(DEBUG,"kill_link: %s '%s'",user->nick,reason);
352         Write(user->fd,"ERROR :Closing link (%s@%s) [%s]",user->ident,user->host,reason);
353         log(DEBUG,"closing fd %lu",(unsigned long)user->fd);
354
355         if (user->registered == 7) {
356                 FOREACH_MOD(I_OnUserQuit,OnUserQuit(user,reason));
357                 WriteCommonExcept(user,"QUIT :%s",reason);
358         }
359
360         user->FlushWriteBuf();
361
362         FOREACH_MOD(I_OnUserDisconnect,OnUserDisconnect(user));
363
364         if (user->fd > -1)
365         {
366                 if (Config->GetIOHook(user->port))
367                 {
368                         Config->GetIOHook(user->port)->OnRawSocketClose(user->fd);
369                 }
370                 ServerInstance->SE->DelFd(user->fd);
371                 user->CloseSocket();
372         }
373
374         // this must come before the WriteOpers so that it doesnt try to fill their buffer with anything
375         // if they were an oper with +s.
376         if (user->registered == 7) {
377                 purge_empty_chans(user);
378                 // fix by brain: only show local quits because we only show local connects (it just makes SENSE)
379                 if (user->fd > -1)
380                         WriteOpers("*** Client exiting: %s!%s@%s [%s]",user->nick,user->ident,user->host,reason);
381                 AddWhoWas(user);
382         }
383
384         if (iter != clientlist.end())
385         {
386                 log(DEBUG,"deleting user hash value %lu",(unsigned long)user);
387                 if (user->fd > -1)
388                 {
389                         fd_ref_table[user->fd] = NULL;
390                         if (find(local_users.begin(),local_users.end(),user) != local_users.end())
391                         {
392                                 local_users.erase(find(local_users.begin(),local_users.end(),user));
393                                 log(DEBUG,"Delete local user");
394                         }
395                 }
396                 clientlist.erase(iter);
397         }
398         delete user;
399 }
400
401 void kill_link_silent(userrec *user,const char* r)
402 {
403         user_hash::iterator iter = clientlist.find(user->nick);
404
405         char reason[MAXBUF];
406
407         strncpy(reason,r,MAXBUF);
408
409         if (strlen(reason)>MAXQUIT)
410         {
411                 reason[MAXQUIT-1] = '\0';
412         }
413
414         log(DEBUG,"kill_link: %s '%s'",user->nick,reason);
415         Write(user->fd,"ERROR :Closing link (%s@%s) [%s]",user->ident,user->host,reason);
416         log(DEBUG,"closing fd %lu",(unsigned long)user->fd);
417
418         user->FlushWriteBuf();
419
420         if (user->registered == 7) {
421                 FOREACH_MOD(I_OnUserQuit,OnUserQuit(user,reason));
422                 WriteCommonExcept(user,"QUIT :%s",reason);
423         }
424
425         FOREACH_MOD(I_OnUserDisconnect,OnUserDisconnect(user));
426
427         if (user->fd > -1)
428         {
429                 if (Config->GetIOHook(user->port))
430                 {
431                         Config->GetIOHook(user->port)->OnRawSocketClose(user->fd);
432                 }
433                 ServerInstance->SE->DelFd(user->fd);
434                 user->CloseSocket();
435         }
436
437         if (user->registered == 7) {
438                 purge_empty_chans(user);
439         }
440
441         if (iter != clientlist.end())
442         {
443                 log(DEBUG,"deleting user hash value %lu",(unsigned long)user);
444                 if (user->fd > -1)
445                 {
446                         fd_ref_table[user->fd] = NULL;
447                         if (find(local_users.begin(),local_users.end(),user) != local_users.end())
448                         {
449                                 log(DEBUG,"Delete local user");
450                                 local_users.erase(find(local_users.begin(),local_users.end(),user));
451                         }
452                 }
453                 clientlist.erase(iter);
454         }
455         delete user;
456 }
457
458
459 /* adds or updates an entry in the whowas list */
460 void AddWhoWas(userrec* u)
461 {
462         whowas_hash::iterator iter = whowas.find(u->nick);
463         WhoWasUser *a = new WhoWasUser();
464         strlcpy(a->nick,u->nick,NICKMAX);
465         strlcpy(a->ident,u->ident,IDENTMAX);
466         strlcpy(a->dhost,u->dhost,160);
467         strlcpy(a->host,u->host,160);
468         strlcpy(a->fullname,u->fullname,MAXGECOS);
469         strlcpy(a->server,u->server,256);
470         a->signon = u->signon;
471
472         /* MAX_WHOWAS:   max number of /WHOWAS items
473          * WHOWAS_STALE: number of hours before a WHOWAS item is marked as stale and
474          *               can be replaced by a newer one
475          */
476
477         if (iter == whowas.end())
478         {
479                 if (whowas.size() >= (unsigned)WHOWAS_MAX)
480                 {
481                         for (whowas_hash::iterator i = whowas.begin(); i != whowas.end(); i++)
482                         {
483                                 // 3600 seconds in an hour ;)
484                                 if ((i->second->signon)<(TIME-(WHOWAS_STALE*3600)))
485                                 {
486                                         // delete the old one
487                                         if (i->second) delete i->second;
488                                         // replace with new one
489                                         i->second = a;
490                                         log(DEBUG,"added WHOWAS entry, purged an old record");
491                                         return;
492                                 }
493                         }
494                         // no space left and user doesnt exist. Don't leave ram in use!
495                         log(DEBUG,"Not able to update whowas (list at WHOWAS_MAX entries and trying to add new?), freeing excess ram");
496                         delete a;
497                 }
498                 else
499                 {
500                         log(DEBUG,"added fresh WHOWAS entry");
501                         whowas[a->nick] = a;
502                 }
503         }
504         else
505         {
506                 log(DEBUG,"updated WHOWAS entry");
507                 if (iter->second) delete iter->second;
508                 iter->second = a;
509         }
510 }
511
512 /* add a client connection to the sockets list */
513 void AddClient(int socket, char* host, int port, bool iscached, char* ip)
514 {
515         string tempnick;
516         char tn2[MAXBUF];
517         user_hash::iterator iter;
518
519         tempnick = ConvToStr(socket) + "-unknown";
520         sprintf(tn2,"%lu-unknown",(unsigned long)socket);
521
522         iter = clientlist.find(tempnick);
523
524         // fix by brain.
525         // as these nicknames are 'RFC impossible', we can be sure nobody is going to be
526         // using one as a registered connection. As theyre per fd, we can also safely assume
527         // that we wont have collisions. Therefore, if the nick exists in the list, its only
528         // used by a dead socket, erase the iterator so that the new client may reclaim it.
529         // this was probably the cause of 'server ignores me when i hammer it with reconnects'
530         // issue in earlier alphas/betas
531         if (iter != clientlist.end())
532         {
533                 userrec* goner = iter->second;
534                 delete goner;
535                 clientlist.erase(iter);
536         }
537
538         /*
539          * It is OK to access the value here this way since we know
540          * it exists, we just created it above.
541          *
542          * At NO other time should you access a value in a map or a
543          * hash_map this way.
544          */
545         clientlist[tempnick] = new userrec();
546
547         log(DEBUG,"AddClient: %lu %s %d %s",(unsigned long)socket,host,port,ip);
548
549         clientlist[tempnick]->fd = socket;
550         strlcpy(clientlist[tempnick]->nick, tn2,NICKMAX);
551         strlcpy(clientlist[tempnick]->host, host,160);
552         strlcpy(clientlist[tempnick]->dhost, host,160);
553         clientlist[tempnick]->server = (char*)FindServerNamePtr(Config->ServerName);
554         strlcpy(clientlist[tempnick]->ident, "unknown",IDENTMAX);
555         clientlist[tempnick]->registered = 0;
556         clientlist[tempnick]->signon = TIME + Config->dns_timeout;
557         clientlist[tempnick]->lastping = 1;
558         clientlist[tempnick]->port = port;
559         strlcpy(clientlist[tempnick]->ip,ip,16);
560
561         // set the registration timeout for this user
562         unsigned long class_regtimeout = 90;
563         int class_flood = 0;
564         long class_threshold = 5;
565         long class_sqmax = 262144;      // 256kb
566         long class_rqmax = 4096;        // 4k
567
568         for (ClassVector::iterator i = Config->Classes.begin(); i != Config->Classes.end(); i++)
569         {
570                 if (match(clientlist[tempnick]->host,i->host.c_str()) && (i->type == CC_ALLOW))
571                 {
572                         class_regtimeout = (unsigned long)i->registration_timeout;
573                         class_flood = i->flood;
574                         clientlist[tempnick]->pingmax = i->pingtime;
575                         class_threshold = i->threshold;
576                         class_sqmax = i->sendqmax;
577                         class_rqmax = i->recvqmax;
578                         break;
579                 }
580         }
581
582         clientlist[tempnick]->nping = TIME+clientlist[tempnick]->pingmax + Config->dns_timeout;
583         clientlist[tempnick]->timeout = TIME+class_regtimeout;
584         clientlist[tempnick]->flood = class_flood;
585         clientlist[tempnick]->threshold = class_threshold;
586         clientlist[tempnick]->sendqmax = class_sqmax;
587         clientlist[tempnick]->recvqmax = class_rqmax;
588
589         ucrec a;
590         a.channel = NULL;
591         a.uc_modes = 0;
592         for (int i = 0; i < MAXCHANS; i++)
593                 clientlist[tempnick]->chans.push_back(a);
594
595         if (clientlist.size() > Config->SoftLimit)
596         {
597                 kill_link(clientlist[tempnick],"No more connections allowed");
598                 return;
599         }
600
601         if (clientlist.size() >= MAXCLIENTS)
602         {
603                 kill_link(clientlist[tempnick],"No more connections allowed");
604                 return;
605         }
606
607         // this is done as a safety check to keep the file descriptors within range of fd_ref_table.
608         // its a pretty big but for the moment valid assumption:
609         // file descriptors are handed out starting at 0, and are recycled as theyre freed.
610         // therefore if there is ever an fd over 65535, 65536 clients must be connected to the
611         // irc server at once (or the irc server otherwise initiating this many connections, files etc)
612         // which for the time being is a physical impossibility (even the largest networks dont have more
613         // than about 10,000 users on ONE server!)
614         if ((unsigned)socket > 65534)
615         {
616                 kill_link(clientlist[tempnick],"Server is full");
617                 return;
618         }
619         char* e = matches_exception(ip);
620         if (!e)
621         {
622                 char* r = matches_zline(ip);
623                 if (r)
624                 {
625                         char reason[MAXBUF];
626                         snprintf(reason,MAXBUF,"Z-Lined: %s",r);
627                         kill_link(clientlist[tempnick],reason);
628                         return;
629                 }
630         }
631         fd_ref_table[socket] = clientlist[tempnick];
632         local_users.push_back(clientlist[tempnick]);
633         ServerInstance->SE->AddFd(socket,true,X_ESTAB_CLIENT);
634 }
635
636 void FullConnectUser(userrec* user)
637 {
638         ServerInstance->stats->statsConnects++;
639         user->idle_lastmsg = TIME;
640         log(DEBUG,"ConnectUser: %s",user->nick);
641
642         if ((strcmp(Passwd(user),"")) && (!user->haspassed))
643         {
644                 kill_link(user,"Invalid password");
645                 return;
646         }
647         if (IsDenied(user))
648         {
649                 kill_link(user,"Unauthorised connection");
650                 return;
651         }
652
653         char match_against[MAXBUF];
654         snprintf(match_against,MAXBUF,"%s@%s",user->ident,user->host);
655         char* e = matches_exception(match_against);
656         if (!e)
657         {
658                 char* r = matches_gline(match_against);
659                 if (r)
660                 {
661                         char reason[MAXBUF];
662                         snprintf(reason,MAXBUF,"G-Lined: %s",r);
663                         kill_link_silent(user,reason);
664                         return;
665                 }
666                 r = matches_kline(user->host);
667                 if (r)
668                 {
669                         char reason[MAXBUF];
670                         snprintf(reason,MAXBUF,"K-Lined: %s",r);
671                         kill_link_silent(user,reason);
672                         return;
673                 }
674         }
675
676
677         WriteServ(user->fd,"NOTICE Auth :Welcome to \002%s\002!",Config->Network);
678         WriteServ(user->fd,"001 %s :Welcome to the %s IRC Network %s!%s@%s",user->nick,Config->Network,user->nick,user->ident,user->host);
679         WriteServ(user->fd,"002 %s :Your host is %s, running version %s",user->nick,Config->ServerName,VERSION);
680         WriteServ(user->fd,"003 %s :This server was created %s %s",user->nick,__TIME__,__DATE__);
681         WriteServ(user->fd,"004 %s %s %s iowghraAsORVSxNCWqBzvdHtGI lvhopsmntikrRcaqOALQbSeKVfHGCuzN",user->nick,Config->ServerName,VERSION);
682         // the neatest way to construct the initial 005 numeric, considering the number of configure constants to go in it...
683         std::stringstream v;
684         v << "WALLCHOPS MODES=13 CHANTYPES=# PREFIX=(ohv)@%+ MAP SAFELIST MAXCHANNELS=" << MAXCHANS;
685         v << " MAXBANS=60 NICKLEN=" << NICKMAX;
686         v << " TOPICLEN=" << MAXTOPIC << " KICKLEN=" << MAXKICK << " MAXTARGETS=20 AWAYLEN=" << MAXAWAY << " CHANMODES=ohvb,k,l,psmnti NETWORK=";
687         v << Config->Network;
688         std::string data005 = v.str();
689         FOREACH_MOD(I_On005Numeric,On005Numeric(data005));
690         // anfl @ #ratbox, efnet reminded me that according to the RFC this cant contain more than 13 tokens per line...
691         // so i'd better split it :)
692         std::stringstream out(data005);
693         std::string token = "";
694         std::string line5 = "";
695         int token_counter = 0;
696         while (!out.eof())
697         {
698                 out >> token;
699                 line5 = line5 + token + " ";
700                 token_counter++;
701                 if ((token_counter >= 13) || (out.eof() == true))
702                 {
703                         WriteServ(user->fd,"005 %s %s:are supported by this server",user->nick,line5.c_str());
704                         line5 = "";
705                         token_counter = 0;
706                 }
707         }
708         ShowMOTD(user);
709
710         // fix 3 by brain, move registered = 7 below these so that spurious modes and host changes dont go out
711         // onto the network and produce 'fake direction'
712         FOREACH_MOD(I_OnUserConnect,OnUserConnect(user));
713         FOREACH_MOD(I_OnGlobalConnect,OnGlobalConnect(user));
714         user->registered = 7;
715         WriteOpers("*** Client connecting on port %lu: %s!%s@%s [%s]",(unsigned long)user->port,user->nick,user->ident,user->host,user->ip);
716 }
717
718
719 /* shows the message of the day, and any other on-logon stuff */
720 void ConnectUser(userrec *user)
721 {
722         // dns is already done, things are fast. no need to wait for dns to complete just pass them straight on
723         if ((user->dns_done) && (user->registered >= 3) && (AllModulesReportReady(user)))
724         {
725                 FullConnectUser(user);
726         }
727 }
728
729 /* re-allocates a nick in the user_hash after they change nicknames,
730  * returns a pointer to the new user as it may have moved */
731
732 userrec* ReHashNick(char* Old, char* New)
733 {
734         //user_hash::iterator newnick;
735         user_hash::iterator oldnick = clientlist.find(Old);
736
737         log(DEBUG,"ReHashNick: %s %s",Old,New);
738
739         if (!strcasecmp(Old,New))
740         {
741                 log(DEBUG,"old nick is new nick, skipping");
742                 return oldnick->second;
743         }
744
745         if (oldnick == clientlist.end()) return NULL; /* doesnt exist */
746
747         log(DEBUG,"ReHashNick: Found hashed nick %s",Old);
748
749         userrec* olduser = oldnick->second;
750         clientlist[New] = olduser;
751         clientlist.erase(oldnick);
752
753         log(DEBUG,"ReHashNick: Nick rehashed as %s",New);
754
755         return clientlist[New];
756 }
757
758 void force_nickchange(userrec* user,const char* newnick)
759 {
760         char nick[MAXBUF];
761         int MOD_RESULT = 0;
762
763         *nick = 0;
764
765         FOREACH_RESULT(I_OnUserPreNick,OnUserPreNick(user,newnick));
766         if (MOD_RESULT) {
767                 ServerInstance->stats->statsCollisions++;
768                 kill_link(user,"Nickname collision");
769                 return;
770         }
771         if (matches_qline(newnick))
772         {
773                 ServerInstance->stats->statsCollisions++;
774                 kill_link(user,"Nickname collision");
775                 return;
776         }
777
778         if (user)
779         {
780                 if (newnick)
781                 {
782                         strncpy(nick,newnick,MAXBUF);
783                 }
784                 if (user->registered == 7)
785                 {
786                         char* pars[1];
787                         pars[0] = nick;
788                         std::string cmd = "NICK";
789                         ServerInstance->Parser->CallHandler(cmd,pars,1,user);
790                 }
791         }
792 }
793