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