]> git.netwichtig.de Git - user/henk/code/inspircd.git/blob - src/inspircd.cpp
added mode.h/mode.cpp and seperated mode functions into it
[user/henk/code/inspircd.git] / src / inspircd.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 /* Now with added unF! ;) */
18
19 using namespace std;
20
21 #include "inspircd.h"
22 #include "inspircd_io.h"
23 #include "inspircd_util.h"
24 #include "inspircd_config.h"
25 #include <unistd.h>
26 #include <fcntl.h>
27 #include <sys/errno.h>
28 #include <sys/ioctl.h>
29 #include <sys/utsname.h>
30 #include <cstdio>
31 #include <time.h>
32 #include <string>
33 #ifdef GCC3
34 #include <ext/hash_map>
35 #else
36 #include <hash_map>
37 #endif
38 #include <map>
39 #include <sstream>
40 #include <vector>
41 #include <errno.h>
42 #include <deque>
43 #include <errno.h>
44 #include <unistd.h>
45 #include <sched.h>
46 #include "connection.h"
47 #include "users.h"
48 #include "servers.h"
49 #include "ctables.h"
50 #include "globals.h"
51 #include "modules.h"
52 #include "dynamic.h"
53 #include "wildcard.h"
54 #include "message.h"
55 #include "mode.h"
56
57 #ifdef GCC3
58 #define nspace __gnu_cxx
59 #else
60 #define nspace std
61 #endif
62
63 int LogLevel = DEFAULT;
64 char ServerName[MAXBUF];
65 char Network[MAXBUF];
66 char ServerDesc[MAXBUF];
67 char AdminName[MAXBUF];
68 char AdminEmail[MAXBUF];
69 char AdminNick[MAXBUF];
70 char diepass[MAXBUF];
71 char restartpass[MAXBUF];
72 char motd[MAXBUF];
73 char rules[MAXBUF];
74 char list[MAXBUF];
75 char PrefixQuit[MAXBUF];
76 char DieValue[MAXBUF];
77 int debugging =  0;
78 int WHOWAS_STALE = 48; // default WHOWAS Entries last 2 days before they go 'stale'
79 int WHOWAS_MAX = 100;  // default 100 people maximum in the WHOWAS list
80 int DieDelay  =  5;
81 time_t startup_time = time(NULL);
82 int NetBufferSize = 10240; // NetBufferSize used as the buffer size for all read() ops
83 time_t nb_start = 0;
84
85 extern vector<Module*> modules;
86 std::vector<std::string> module_names;
87 extern vector<ircd_module*> factory;
88 std::vector<int> fd_reap;
89
90 extern int MODCOUNT;
91
92 bool nofork = false;
93
94 namespace nspace
95 {
96         template<> struct nspace::hash<in_addr>
97         {
98                 size_t operator()(const struct in_addr &a) const
99                 {
100                         size_t q;
101                         memcpy(&q,&a,sizeof(size_t));
102                         return q;
103                 }
104         };
105
106         template<> struct nspace::hash<string>
107         {
108                 size_t operator()(const string &s) const
109                 {
110                         char a[MAXBUF];
111                         static struct hash<const char *> strhash;
112                         strcpy(a,s.c_str());
113                         strlower(a);
114                         return strhash(a);
115                 }
116         };
117 }       
118
119
120 struct StrHashComp
121 {
122
123         bool operator()(const string& s1, const string& s2) const
124         {
125                 char a[MAXBUF],b[MAXBUF];
126                 strcpy(a,s1.c_str());
127                 strcpy(b,s2.c_str());
128                 return (strcasecmp(a,b) == 0);
129         }
130
131 };
132
133 struct InAddr_HashComp
134 {
135
136         bool operator()(const in_addr &s1, const in_addr &s2) const
137         {
138                 size_t q;
139                 size_t p;
140                 
141                 memcpy(&q,&s1,sizeof(size_t));
142                 memcpy(&p,&s2,sizeof(size_t));
143                 
144                 return (q == p);
145         }
146
147 };
148
149
150 typedef nspace::hash_map<std::string, userrec*, nspace::hash<string>, StrHashComp> user_hash;
151 typedef nspace::hash_map<std::string, chanrec*, nspace::hash<string>, StrHashComp> chan_hash;
152 typedef nspace::hash_map<in_addr,string*, nspace::hash<in_addr>, InAddr_HashComp> address_cache;
153 typedef std::deque<command_t> command_table;
154
155 serverrec* me[32];
156
157 FILE *log_file;
158
159 user_hash clientlist;
160 chan_hash chanlist;
161 user_hash whowas;
162 command_table cmdlist;
163 file_cache MOTD;
164 file_cache RULES;
165 address_cache IP;
166
167 ClassVector Classes;
168
169 struct linger linger = { 0 };
170 char bannerBuffer[MAXBUF];
171 int boundPortCount = 0;
172 int portCount = 0, UDPportCount = 0, ports[MAXSOCKS];
173 int defaultRoute = 0;
174
175 connection C;
176
177 long MyKey = C.GenKey();
178
179 /* prototypes */
180
181 int has_channel(userrec *u, chanrec *c);
182 int usercount(chanrec *c);
183 int usercount_i(chanrec *c);
184 void update_stats_l(int fd,int data_out);
185 char* Passwd(userrec *user);
186 bool IsDenied(userrec *user);
187 void AddWhoWas(userrec* u);
188
189 std::vector<long> auth_cookies;
190 std::stringstream config_f(stringstream::in | stringstream::out);
191
192
193
194 long GetRevision()
195 {
196         char Revision[] = "$Revision$";
197         char *s1 = Revision;
198         char *savept;
199         char *v1 = strtok_r(s1," ",&savept);
200         s1 = savept;
201         char *v2 = strtok_r(s1," ",&savept);
202         s1 = savept;
203         return (long)(atof(v2)*10000);
204 }
205
206
207 std::string getservername()
208 {
209         return ServerName;
210 }
211
212 std::string getserverdesc()
213 {
214         return ServerDesc;
215 }
216
217 std::string getnetworkname()
218 {
219         return Network;
220 }
221
222 std::string getadminname()
223 {
224         return AdminName;
225 }
226
227 std::string getadminemail()
228 {
229         return AdminEmail;
230 }
231
232 std::string getadminnick()
233 {
234         return AdminNick;
235 }
236
237 void log(int level,char *text, ...)
238 {
239         char textbuffer[MAXBUF];
240         va_list argsPtr;
241         time_t rawtime;
242         struct tm * timeinfo;
243         if (level < LogLevel)
244                 return;
245
246         time(&rawtime);
247         timeinfo = localtime (&rawtime);
248
249         if (log_file)
250         {
251                 char b[MAXBUF];
252                 va_start (argsPtr, text);
253                 vsnprintf(textbuffer, MAXBUF, text, argsPtr);
254                 va_end(argsPtr);
255                 strcpy(b,asctime(timeinfo));
256                 b[strlen(b)-1] = ':';
257                 fprintf(log_file,"%s %s\n",b,textbuffer);
258                 if (nofork)
259                 {
260                         // nofork enabled? display it on terminal too
261                         printf("%s %s\n",b,textbuffer);
262                 }
263         }
264 }
265
266 void readfile(file_cache &F, const char* fname)
267 {
268   FILE* file;
269   char linebuf[MAXBUF];
270
271   log(DEBUG,"readfile: loading %s",fname);
272   F.clear();
273   file =  fopen(fname,"r");
274   if (file)
275   {
276         while (!feof(file))
277         {
278                 fgets(linebuf,sizeof(linebuf),file);
279                 linebuf[strlen(linebuf)-1]='\0';
280                 if (!strcmp(linebuf,""))
281                 {
282                         strcpy(linebuf,"  ");
283                 }
284                 if (!feof(file))
285                 {
286                         F.push_back(linebuf);
287                 }
288         }
289         fclose(file);
290   }
291   else
292   {
293           log(DEBUG,"readfile: failed to load file: %s",fname);
294   }
295   log(DEBUG,"readfile: loaded %s, %d lines",fname,F.size());
296 }
297
298 void ReadConfig(void)
299 {
300   char dbg[MAXBUF],pauseval[MAXBUF],Value[MAXBUF],timeout[MAXBUF],NB[MAXBUF],flood[MAXBUF];
301   ConnectClass c;
302
303   LoadConf(CONFIG_FILE,&config_f);
304   
305   ConfValue("server","name",0,ServerName,&config_f);
306   ConfValue("server","description",0,ServerDesc,&config_f);
307   ConfValue("server","network",0,Network,&config_f);
308   ConfValue("admin","name",0,AdminName,&config_f);
309   ConfValue("admin","email",0,AdminEmail,&config_f);
310   ConfValue("admin","nick",0,AdminNick,&config_f);
311   ConfValue("files","motd",0,motd,&config_f);
312   ConfValue("files","rules",0,rules,&config_f);
313   ConfValue("power","diepass",0,diepass,&config_f);
314   ConfValue("power","pause",0,pauseval,&config_f);
315   ConfValue("power","restartpass",0,restartpass,&config_f);
316   ConfValue("options","prefixquit",0,PrefixQuit,&config_f);
317   ConfValue("die","value",0,DieValue,&config_f);
318   ConfValue("options","loglevel",0,dbg,&config_f);
319   ConfValue("options","netbuffersize",0,NB,&config_f);
320   NetBufferSize = atoi(NB);
321   if ((!NetBufferSize) || (NetBufferSize > 65535) || (NetBufferSize < 1024))
322   {
323         log(DEFAULT,"No NetBufferSize specified or size out of range, setting to default of 10240.");
324         NetBufferSize = 10240;
325   }
326   if (!strcmp(dbg,"debug"))
327         LogLevel = DEBUG;
328   if (!strcmp(dbg,"verbose"))
329         LogLevel = VERBOSE;
330   if (!strcmp(dbg,"default"))
331         LogLevel = DEFAULT;
332   if (!strcmp(dbg,"sparse"))
333         LogLevel = SPARSE;
334   if (!strcmp(dbg,"none"))
335         LogLevel = NONE;
336   readfile(MOTD,motd);
337   log(DEBUG,"Reading message of the day");
338   readfile(RULES,rules);
339   log(DEBUG,"Reading connect classes");
340   Classes.clear();
341   for (int i = 0; i < ConfValueEnum("connect",&config_f); i++)
342   {
343         strcpy(Value,"");
344         ConfValue("connect","allow",i,Value,&config_f);
345         ConfValue("connect","timeout",i,timeout,&config_f);
346         ConfValue("connect","flood",i,flood,&config_f);
347         if (strcmp(Value,""))
348         {
349                 strcpy(c.host,Value);
350                 c.type = CC_ALLOW;
351                 strcpy(Value,"");
352                 ConfValue("connect","password",i,Value,&config_f);
353                 strcpy(c.pass,Value);
354                 c.registration_timeout = 90; // default is 2 minutes
355                 c.flood = atoi(flood);
356                 if (atoi(timeout)>0)
357                 {
358                         c.registration_timeout = atoi(timeout);
359                 }
360                 Classes.push_back(c);
361                 log(DEBUG,"Read connect class type ALLOW, host=%s password=%s timeout=%d flood=%d",c.host,c.pass,c.registration_timeout,c.flood);
362         }
363         else
364         {
365                 ConfValue("connect","deny",i,Value,&config_f);
366                 strcpy(c.host,Value);
367                 c.type = CC_DENY;
368                 Classes.push_back(c);
369                 log(DEBUG,"Read connect class type DENY, host=%s",c.host);
370         }
371         
372   }
373 }
374
375 /* write formatted text to a socket, in same format as printf */
376
377 void Write(int sock,char *text, ...)
378 {
379   if (!text)
380   {
381         log(DEFAULT,"*** BUG *** Write was given an invalid parameter");
382         return;
383   }
384   char textbuffer[MAXBUF];
385   va_list argsPtr;
386   char tb[MAXBUF];
387
388   va_start (argsPtr, text);
389   vsnprintf(textbuffer, MAXBUF, text, argsPtr);
390   va_end(argsPtr);
391   sprintf(tb,"%s\r\n",textbuffer);
392   chop(tb);
393   if (sock != -1)
394   {
395         write(sock,tb,strlen(tb));
396         update_stats_l(sock,strlen(tb)); /* add one line-out to stats L for this fd */
397   }
398 }
399
400 /* write a server formatted numeric response to a single socket */
401
402 void WriteServ(int sock, char* text, ...)
403 {
404   if (!text)
405   {
406         log(DEFAULT,"*** BUG *** WriteServ was given an invalid parameter");
407         return;
408   }
409   char textbuffer[MAXBUF],tb[MAXBUF];
410   va_list argsPtr;
411   va_start (argsPtr, text);
412
413   vsnprintf(textbuffer, MAXBUF, text, argsPtr);
414   va_end(argsPtr);
415   sprintf(tb,":%s %s\r\n",ServerName,textbuffer);
416   chop(tb);
417   if (sock != -1)
418   {
419         write(sock,tb,strlen(tb));
420         update_stats_l(sock,strlen(tb)); /* add one line-out to stats L for this fd */
421   }
422 }
423
424 /* write text from an originating user to originating user */
425
426 void WriteFrom(int sock, userrec *user,char* text, ...)
427 {
428   if ((!text) || (!user))
429   {
430         log(DEFAULT,"*** BUG *** WriteFrom was given an invalid parameter");
431         return;
432   }
433   char textbuffer[MAXBUF],tb[MAXBUF];
434   va_list argsPtr;
435   va_start (argsPtr, text);
436
437   vsnprintf(textbuffer, MAXBUF, text, argsPtr);
438   va_end(argsPtr);
439   sprintf(tb,":%s!%s@%s %s\r\n",user->nick,user->ident,user->dhost,textbuffer);
440   chop(tb);
441   if (sock != -1)
442   {
443         write(sock,tb,strlen(tb));
444         update_stats_l(sock,strlen(tb)); /* add one line-out to stats L for this fd */
445   }
446 }
447
448 /* write text to an destination user from a source user (e.g. user privmsg) */
449
450 void WriteTo(userrec *source, userrec *dest,char *data, ...)
451 {
452         if ((!dest) || (!data))
453         {
454                 log(DEFAULT,"*** BUG *** WriteTo was given an invalid parameter");
455                 return;
456         }
457         char textbuffer[MAXBUF],tb[MAXBUF];
458         va_list argsPtr;
459         va_start (argsPtr, data);
460         vsnprintf(textbuffer, MAXBUF, data, argsPtr);
461         va_end(argsPtr);
462         chop(tb);
463
464         // if no source given send it from the server.
465         if (!source)
466         {
467                 WriteServ(dest->fd,":%s %s",ServerName,textbuffer);
468         }
469         else
470         {
471                 WriteFrom(dest->fd,source,"%s",textbuffer);
472         }
473 }
474
475 /* write formatted text from a source user to all users on a channel
476  * including the sender (NOT for privmsg, notice etc!) */
477
478 void WriteChannel(chanrec* Ptr, userrec* user, char* text, ...)
479 {
480         if ((!Ptr) || (!user) || (!text))
481         {
482                 log(DEFAULT,"*** BUG *** WriteChannel was given an invalid parameter");
483                 return;
484         }
485         char textbuffer[MAXBUF];
486         va_list argsPtr;
487         va_start (argsPtr, text);
488         vsnprintf(textbuffer, MAXBUF, text, argsPtr);
489         va_end(argsPtr);
490         for (user_hash::const_iterator i = clientlist.begin(); i != clientlist.end(); i++)
491         {
492                 if (has_channel(i->second,Ptr))
493                 {
494                         WriteTo(user,i->second,"%s",textbuffer);
495                 }
496         }
497 }
498
499 /* write formatted text from a source user to all users on a channel
500  * including the sender (NOT for privmsg, notice etc!) doesnt send to
501  * users on remote servers */
502
503 void WriteChannelLocal(chanrec* Ptr, userrec* user, char* text, ...)
504 {
505         if ((!Ptr) || (!text))
506         {
507                 log(DEFAULT,"*** BUG *** WriteChannel was given an invalid parameter");
508                 return;
509         }
510         char textbuffer[MAXBUF];
511         va_list argsPtr;
512         va_start (argsPtr, text);
513         vsnprintf(textbuffer, MAXBUF, text, argsPtr);
514         va_end(argsPtr);
515         for (user_hash::const_iterator i = clientlist.begin(); i != clientlist.end(); i++)
516         {
517                 if (has_channel(i->second,Ptr))
518                 {
519                         if (i->second->fd != -1)
520                         {
521                                 if (!user)
522                                 {
523                                         WriteServ(i->second->fd,"%s",textbuffer);
524                                 }
525                                 else
526                                 {
527                                         WriteTo(user,i->second,"%s",textbuffer);
528                                 }
529                         }       
530                 }
531         }
532 }
533
534
535 void WriteChannelWithServ(char* ServerName, chanrec* Ptr, userrec* user, char* text, ...)
536 {
537         if ((!Ptr) || (!user) || (!text))
538         {
539                 log(DEFAULT,"*** BUG *** WriteChannelWithServ was given an invalid parameter");
540                 return;
541         }
542         char textbuffer[MAXBUF];
543         va_list argsPtr;
544         va_start (argsPtr, text);
545         vsnprintf(textbuffer, MAXBUF, text, argsPtr);
546         va_end(argsPtr);
547         for (user_hash::const_iterator i = clientlist.begin(); i != clientlist.end(); i++)
548         {
549                 if (i->second)
550                 {
551                         if (has_channel(i->second,Ptr))
552                         {
553                                 WriteServ(i->second->fd,"%s",textbuffer);
554                         }
555                 }
556         }
557 }
558
559
560 /* write formatted text from a source user to all users on a channel except
561  * for the sender (for privmsg etc) */
562
563 void ChanExceptSender(chanrec* Ptr, userrec* user, char* text, ...)
564 {
565         if ((!Ptr) || (!user) || (!text))
566         {
567                 log(DEFAULT,"*** BUG *** ChanExceptSender was given an invalid parameter");
568                 return;
569         }
570         char textbuffer[MAXBUF];
571         va_list argsPtr;
572         va_start (argsPtr, text);
573         vsnprintf(textbuffer, MAXBUF, text, argsPtr);
574         va_end(argsPtr);
575
576         for (user_hash::const_iterator i = clientlist.begin(); i != clientlist.end(); i++)
577         {
578                 if (i->second)
579                 {
580                         if (has_channel(i->second,Ptr) && (user != i->second))
581                         {
582                                 WriteTo(user,i->second,"%s",textbuffer);
583                         }
584                 }
585         }
586 }
587
588
589 std::string GetServerDescription(char* servername)
590 {
591         for (int j = 0; j < 32; j++)
592         {
593                 if (me[j] != NULL)
594                 {
595                         for (int k = 0; k < me[j]->connectors.size(); k++)
596                         {
597                                 if (!strcasecmp(me[j]->connectors[k].GetServerName().c_str(),servername))
598                                 {
599                                         return me[j]->connectors[k].GetDescription();
600                                 }
601                         }
602                 }
603                 return ServerDesc; // not a remote server that can be found, it must be me.
604         }
605 }
606
607
608 /* write a formatted string to all users who share at least one common
609  * channel, including the source user e.g. for use in NICK */
610
611 void WriteCommon(userrec *u, char* text, ...)
612 {
613         if (!u)
614         {
615                 log(DEFAULT,"*** BUG *** WriteCommon was given an invalid parameter");
616                 return;
617         }
618
619         if (u->registered != 7) {
620                 log(DEFAULT,"*** BUG *** WriteCommon on an unregistered user");
621                 return;
622         }
623         
624         char textbuffer[MAXBUF];
625         va_list argsPtr;
626         va_start (argsPtr, text);
627         vsnprintf(textbuffer, MAXBUF, text, argsPtr);
628         va_end(argsPtr);
629
630         WriteFrom(u->fd,u,"%s",textbuffer);
631
632         for (user_hash::const_iterator i = clientlist.begin(); i != clientlist.end(); i++)
633         {
634                 if (i->second)
635                 {
636                         if (common_channels(u,i->second) && (i->second != u))
637                         {
638                                 WriteFrom(i->second->fd,u,"%s",textbuffer);
639                         }
640                 }
641         }
642 }
643
644 /* write a formatted string to all users who share at least one common
645  * channel, NOT including the source user e.g. for use in QUIT */
646
647 void WriteCommonExcept(userrec *u, char* text, ...)
648 {
649         if (!u)
650         {
651                 log(DEFAULT,"*** BUG *** WriteCommon was given an invalid parameter");
652                 return;
653         }
654
655         if (u->registered != 7) {
656                 log(DEFAULT,"*** BUG *** WriteCommon on an unregistered user");
657                 return;
658         }
659
660         char textbuffer[MAXBUF];
661         va_list argsPtr;
662         va_start (argsPtr, text);
663         vsnprintf(textbuffer, MAXBUF, text, argsPtr);
664         va_end(argsPtr);
665
666         for (user_hash::const_iterator i = clientlist.begin(); i != clientlist.end(); i++)
667         {
668                 if (i->second)
669                 {
670                         if ((common_channels(u,i->second)) && (u != i->second))
671                         {
672                                 WriteFrom(i->second->fd,u,"%s",textbuffer);
673                         }
674                 }
675         }
676 }
677
678 void WriteOpers(char* text, ...)
679 {
680         if (!text)
681         {
682                 log(DEFAULT,"*** BUG *** WriteOpers was given an invalid parameter");
683                 return;
684         }
685
686         char textbuffer[MAXBUF];
687         va_list argsPtr;
688         va_start (argsPtr, text);
689         vsnprintf(textbuffer, MAXBUF, text, argsPtr);
690         va_end(argsPtr);
691
692         for (user_hash::const_iterator i = clientlist.begin(); i != clientlist.end(); i++)
693         {
694                 if (i->second)
695                 {
696                         if (strchr(i->second->modes,'o'))
697                         {
698                                 if (strchr(i->second->modes,'s'))
699                                 {
700                                         // send server notices to all with +s
701                                         // (TODO: needs SNOMASKs)
702                                         WriteServ(i->second->fd,"NOTICE %s :%s",i->second->nick,textbuffer);
703                                 }
704                         }
705                 }
706         }
707 }
708
709 // returns TRUE of any users on channel C occupy server 'servername'.
710
711 bool ChanAnyOnThisServer(chanrec *c,char* servername)
712 {
713         log(DEBUG,"ChanAnyOnThisServer");
714         for (user_hash::iterator i = clientlist.begin(); i != clientlist.end(); i++)
715         {
716                 if (has_channel(i->second,c))
717                 {
718                         if (!strcasecmp(i->second->server,servername))
719                         {
720                                 return true;
721                         }
722                 }
723         }
724         return false;
725 }
726
727 // returns true if user 'u' shares any common channels with any users on server 'servername'
728
729 bool CommonOnThisServer(userrec* u,const char* servername)
730 {
731         log(DEBUG,"ChanAnyOnThisServer");
732         for (user_hash::iterator i = clientlist.begin(); i != clientlist.end(); i++)
733         {
734                 if ((common_channels(u,i->second)) && (u != i->second))
735                 {
736                         if (!strcasecmp(i->second->server,servername))
737                         {
738                                 log(DEBUG,"%s is common to %s sharing with %s",i->second->nick,servername,u->nick);
739                                 return true;
740                         }
741                 }
742         }
743         return false;
744 }
745
746
747 void NetSendToCommon(userrec* u, char* s)
748 {
749         char buffer[MAXBUF];
750         snprintf(buffer,MAXBUF,"%s",s);
751         
752         log(DEBUG,"NetSendToCommon: '%s' '%s'",u->nick,s);
753
754         for (int j = 0; j < 32; j++)
755         {
756                 if (me[j] != NULL)
757                 {
758                         for (int k = 0; k < me[j]->connectors.size(); k++)
759                         {
760                                 if (CommonOnThisServer(u,me[j]->connectors[k].GetServerName().c_str()))
761                                 {
762                                         me[j]->SendPacket(buffer,me[j]->connectors[k].GetServerName().c_str());
763                                 }
764                         }
765                 }
766         }
767 }
768
769
770 void NetSendToAll(char* s)
771 {
772         char buffer[MAXBUF];
773         snprintf(buffer,MAXBUF,"%s",s);
774         
775         log(DEBUG,"NetSendToAll: '%s'",s);
776
777         for (int j = 0; j < 32; j++)
778         {
779                 if (me[j] != NULL)
780                 {
781                         for (int k = 0; k < me[j]->connectors.size(); k++)
782                         {
783                                 me[j]->SendPacket(buffer,me[j]->connectors[k].GetServerName().c_str());
784                         }
785                 }
786         }
787 }
788
789 void NetSendToAllAlive(char* s)
790 {
791         char buffer[MAXBUF];
792         snprintf(buffer,MAXBUF,"%s",s);
793         
794         log(DEBUG,"NetSendToAllAlive: '%s'",s);
795
796         for (int j = 0; j < 32; j++)
797         {
798                 if (me[j] != NULL)
799                 {
800                         for (int k = 0; k < me[j]->connectors.size(); k++)
801                         {
802                                 if (me[j]->connectors[k].GetState() != STATE_DISCONNECTED)
803                                 {
804                                         me[j]->SendPacket(buffer,me[j]->connectors[k].GetServerName().c_str());
805                                 }
806                                 else
807                                 {
808                                         log(DEBUG,"%s is dead, not sending to it.",me[j]->connectors[k].GetServerName().c_str());
809                                 }
810                         }
811                 }
812         }
813 }
814
815
816 void NetSendToOne(char* target,char* s)
817 {
818         char buffer[MAXBUF];
819         snprintf(buffer,MAXBUF,"%s",s);
820         
821         log(DEBUG,"NetSendToOne: '%s' '%s'",target,s);
822
823         for (int j = 0; j < 32; j++)
824         {
825                 if (me[j] != NULL)
826                 {
827                         for (int k = 0; k < me[j]->connectors.size(); k++)
828                         {
829                                 if (!strcasecmp(me[j]->connectors[k].GetServerName().c_str(),target))
830                                 {
831                                         me[j]->SendPacket(buffer,me[j]->connectors[k].GetServerName().c_str());
832                                 }
833                         }
834                 }
835         }
836 }
837
838 void NetSendToAllExcept(const char* target,char* s)
839 {
840         char buffer[MAXBUF];
841         snprintf(buffer,MAXBUF,"%s",s);
842         
843         log(DEBUG,"NetSendToAllExcept: '%s' '%s'",target,s);
844         
845         for (int j = 0; j < 32; j++)
846         {
847                 if (me[j] != NULL)
848                 {
849                         for (int k = 0; k < me[j]->connectors.size(); k++)
850                         {
851                                 if (strcasecmp(me[j]->connectors[k].GetServerName().c_str(),target))
852                                 {
853                                         me[j]->SendPacket(buffer,me[j]->connectors[k].GetServerName().c_str());
854                                 }
855                         }
856                 }
857         }
858 }
859
860
861 void WriteMode(const char* modes, int flags, const char* text, ...)
862 {
863         if ((!text) || (!modes) || (!flags))
864         {
865                 log(DEFAULT,"*** BUG *** WriteMode was given an invalid parameter");
866                 return;
867         }
868
869         char textbuffer[MAXBUF];
870         va_list argsPtr;
871         va_start (argsPtr, text);
872         vsnprintf(textbuffer, MAXBUF, text, argsPtr);
873         va_end(argsPtr);
874
875         for (user_hash::const_iterator i = clientlist.begin(); i != clientlist.end(); i++)
876         {
877                 if (i->second)
878                 {
879                         bool send_to_user = false;
880                         
881                         if (flags == WM_AND)
882                         {
883                                 send_to_user = true;
884                                 for (int n = 0; n < strlen(modes); n++)
885                                 {
886                                         if (!hasumode(i->second,modes[n]))
887                                         {
888                                                 send_to_user = false;
889                                                 break;
890                                         }
891                                 }
892                         }
893                         else if (flags == WM_OR)
894                         {
895                                 send_to_user = false;
896                                 for (int n = 0; n < strlen(modes); n++)
897                                 {
898                                         if (hasumode(i->second,modes[n]))
899                                         {
900                                                 send_to_user = true;
901                                                 break;
902                                         }
903                                 }
904                         }
905
906                         if (send_to_user)
907                         {
908                                 WriteServ(i->second->fd,"NOTICE %s :%s",i->second->nick,textbuffer);
909                         }
910                 }
911         }
912 }
913
914
915 void WriteWallOps(userrec *source, bool local_only, char* text, ...)  
916 {  
917         if ((!text) || (!source))
918         {
919                 log(DEFAULT,"*** BUG *** WriteOpers was given an invalid parameter");
920                 return;
921         }
922
923         int i = 0;  
924         char textbuffer[MAXBUF];  
925         va_list argsPtr;  
926         va_start (argsPtr, text);  
927         vsnprintf(textbuffer, MAXBUF, text, argsPtr);  
928         va_end(argsPtr);  
929   
930         for (user_hash::const_iterator i = clientlist.begin(); i != clientlist.end(); i++)
931         {
932                 if (i->second)
933                 {
934                         if (strchr(i->second->modes,'w'))
935                         {
936                                 WriteTo(source,i->second,"WALLOPS %s",textbuffer);
937                         }
938                 }
939         }
940
941         if (!local_only)
942         {
943                 char buffer[MAXBUF];
944                 snprintf(buffer,MAXBUF,"@ %s :%s",source->nick,textbuffer);
945                 NetSendToAll(buffer);
946         }
947 }  
948
949 /* convert a string to lowercase. Note following special circumstances
950  * taken from RFC 1459. Many "official" server branches still hold to this
951  * rule so i will too;
952  *
953  *  Because of IRC's scandanavian origin, the characters {}| are
954  *  considered to be the lower case equivalents of the characters []\,
955  *  respectively. This is a critical issue when determining the
956  *  equivalence of two nicknames.
957  */
958
959 void strlower(char *n)
960 {
961         if (!n)
962         {
963                 return;
964         }
965         for (int i = 0; i != strlen(n); i++)
966         {
967                 n[i] = tolower(n[i]);
968                 if (n[i] == '[')
969                         n[i] = '{';
970                 if (n[i] == ']')
971                         n[i] = '}';
972                 if (n[i] == '\\')
973                         n[i] = '|';
974         }
975 }
976
977
978
979 /* Find a user record by nickname and return a pointer to it */
980
981 userrec* Find(string nick)
982 {
983         user_hash::iterator iter = clientlist.find(nick);
984
985         if (iter == clientlist.end())
986                 /* Couldn't find it */
987                 return NULL;
988
989         return iter->second;
990 }
991
992 void update_stats_l(int fd,int data_out) /* add one line-out to stats L for this fd */
993 {
994         for (user_hash::const_iterator i = clientlist.begin(); i != clientlist.end(); i++)
995         {
996                 if (i->second)
997                 {
998                         if (i->second->fd == fd)
999                         {
1000                                 i->second->bytes_out+=data_out;
1001                                 i->second->cmds_out++;
1002                         }
1003                 }
1004         }
1005 }
1006
1007
1008 /* find a channel record by channel name and return a pointer to it */
1009
1010 chanrec* FindChan(const char* chan)
1011 {
1012         if (!chan)
1013         {
1014                 log(DEFAULT,"*** BUG *** Findchan was given an invalid parameter");
1015                 return NULL;
1016         }
1017
1018         chan_hash::iterator iter = chanlist.find(chan);
1019
1020         if (iter == chanlist.end())
1021                 /* Couldn't find it */
1022                 return NULL;
1023
1024         return iter->second;
1025 }
1026
1027
1028 void purge_empty_chans(void)
1029 {
1030         int go_again = 1, purge = 0;
1031         
1032         while (go_again)
1033         {
1034                 go_again = 0;
1035                 for (chan_hash::iterator i = chanlist.begin(); i != chanlist.end(); i++)
1036                 {
1037                         if (i->second) {
1038                                 if (!usercount(i->second))
1039                                 {
1040                                         /* kill the record */
1041                                         if (i != chanlist.end())
1042                                         {
1043                                                 log(DEBUG,"del_channel: destroyed: %s",i->second->name);
1044                                                 delete i->second;
1045                                                 chanlist.erase(i);
1046                                                 go_again = 1;
1047                                                 purge++;
1048                                                 break;
1049                                         }
1050                                 }
1051                                 else
1052                                 {
1053                                         log(DEBUG,"skipped purge for %s",i->second->name);
1054                                 }
1055                         }
1056                 }
1057         }
1058         log(DEBUG,"completed channel purge, killed %d",purge);
1059 }
1060
1061
1062 char scratch[MAXBUF];
1063 char sparam[MAXBUF];
1064
1065 char* chanmodes(chanrec *chan)
1066 {
1067         if (!chan)
1068         {
1069                 log(DEFAULT,"*** BUG *** chanmodes was given an invalid parameter");
1070                 strcpy(scratch,"");
1071                 return scratch;
1072         }
1073
1074         strcpy(scratch,"");
1075         strcpy(sparam,"");
1076         if (chan->noexternal)
1077         {
1078                 strncat(scratch,"n",MAXMODES);
1079         }
1080         if (chan->topiclock)
1081         {
1082                 strncat(scratch,"t",MAXMODES);
1083         }
1084         if (strcmp(chan->key,""))
1085         {
1086                 strncat(scratch,"k",MAXMODES);
1087         }
1088         if (chan->limit)
1089         {
1090                 strncat(scratch,"l",MAXMODES);
1091         }
1092         if (chan->inviteonly)
1093         {
1094                 strncat(scratch,"i",MAXMODES);
1095         }
1096         if (chan->moderated)
1097         {
1098                 strncat(scratch,"m",MAXMODES);
1099         }
1100         if (chan->secret)
1101         {
1102                 strncat(scratch,"s",MAXMODES);
1103         }
1104         if (chan->c_private)
1105         {
1106                 strncat(scratch,"p",MAXMODES);
1107         }
1108         if (strcmp(chan->key,""))
1109         {
1110                 strncat(sparam,chan->key,MAXBUF);
1111         }
1112         if (chan->limit)
1113         {
1114                 char foo[24];
1115                 sprintf(foo," %d",chan->limit);
1116                 strncat(sparam,foo,MAXBUF);
1117         }
1118         if (strlen(chan->custom_modes))
1119         {
1120                 strncat(scratch,chan->custom_modes,MAXMODES);
1121                 for (int z = 0; z < strlen(chan->custom_modes); z++)
1122                 {
1123                         std::string extparam = chan->GetModeParameter(chan->custom_modes[z]);
1124                         if (extparam != "")
1125                         {
1126                                 strncat(sparam," ",MAXBUF);
1127                                 strncat(sparam,extparam.c_str(),MAXBUF);
1128                         }
1129                 }
1130         }
1131         log(DEBUG,"chanmodes: %s %s%s",chan->name,scratch,sparam);
1132         strncat(scratch,sparam,MAXMODES);
1133         return scratch;
1134 }
1135
1136
1137 /* compile a userlist of a channel into a string, each nick seperated by
1138  * spaces and op, voice etc status shown as @ and + */
1139
1140 void userlist(userrec *user,chanrec *c)
1141 {
1142         if ((!c) || (!user))
1143         {
1144                 log(DEFAULT,"*** BUG *** userlist was given an invalid parameter");
1145                 return;
1146         }
1147
1148         sprintf(list,"353 %s = %s :", user->nick, c->name);
1149         for (user_hash::const_iterator i = clientlist.begin(); i != clientlist.end(); i++)
1150         {
1151                 if (has_channel(i->second,c))
1152                 {
1153                         if (isnick(i->second->nick))
1154                         {
1155                                 if ((!has_channel(i->second,c)) && (strchr(i->second->modes,'i')))
1156                                 {
1157                                         /* user is +i, and source not on the channel, does not show
1158                                          * nick in NAMES list */
1159                                         continue;
1160                                 }
1161                                 strcat(list,cmode(i->second,c));
1162                                 strcat(list,i->second->nick);
1163                                 strcat(list," ");
1164                                 if (strlen(list)>(480-NICKMAX))
1165                                 {
1166                                         /* list overflowed into
1167                                          * multiple numerics */
1168                                         WriteServ(user->fd,list);
1169                                         sprintf(list,"353 %s = %s :", user->nick, c->name);
1170                                 }
1171                         }
1172                 }
1173         }
1174         /* if whats left in the list isnt empty, send it */
1175         if (list[strlen(list)-1] != ':')
1176         {
1177                 WriteServ(user->fd,list);
1178         }
1179 }
1180
1181 /* return a count of the users on a specific channel accounting for
1182  * invisible users who won't increase the count. e.g. for /LIST */
1183
1184 int usercount_i(chanrec *c)
1185 {
1186         int i = 0;
1187         int count = 0;
1188         
1189         if (!c)
1190         {
1191                 log(DEFAULT,"*** BUG *** usercount_i was given an invalid parameter");
1192                 return 0;
1193         }
1194
1195         strcpy(list,"");
1196         for (user_hash::const_iterator i = clientlist.begin(); i != clientlist.end(); i++)
1197         {
1198                 if (i->second)
1199                 {
1200                         if (has_channel(i->second,c))
1201                         {
1202                                 if (isnick(i->second->nick))
1203                                 {
1204                                         if ((!has_channel(i->second,c)) && (strchr(i->second->modes,'i')))
1205                                         {
1206                                                 /* user is +i, and source not on the channel, does not show
1207                                                  * nick in NAMES list */
1208                                                 continue;
1209                                         }
1210                                         count++;
1211                                 }
1212                         }
1213                 }
1214         }
1215         log(DEBUG,"usercount_i: %s %d",c->name,count);
1216         return count;
1217 }
1218
1219
1220 int usercount(chanrec *c)
1221 {
1222         int i = 0;
1223         int count = 0;
1224         
1225         if (!c)
1226         {
1227                 log(DEFAULT,"*** BUG *** usercount was given an invalid parameter");
1228                 return 0;
1229         }
1230
1231         strcpy(list,"");
1232         for (user_hash::const_iterator i = clientlist.begin(); i != clientlist.end(); i++)
1233         {
1234                 if (i->second)
1235                 {
1236                         if (has_channel(i->second,c))
1237                         {
1238                                 if ((isnick(i->second->nick)) && (i->second->registered == 7))
1239                                 {
1240                                         count++;
1241                                 }
1242                         }
1243                 }
1244         }
1245         log(DEBUG,"usercount: %s %d",c->name,count);
1246         return count;
1247 }
1248
1249
1250 /* add a channel to a user, creating the record for it if needed and linking
1251  * it to the user record */
1252
1253 chanrec* add_channel(userrec *user, const char* cn, const char* key, bool override)
1254 {
1255         if ((!user) || (!cn))
1256         {
1257                 log(DEFAULT,"*** BUG *** add_channel was given an invalid parameter");
1258                 return 0;
1259         }
1260
1261         int i = 0;
1262         chanrec* Ptr;
1263         int created = 0;
1264         char cname[MAXBUF];
1265
1266         strncpy(cname,cn,MAXBUF);
1267         
1268         // we MUST declare this wherever we use FOREACH_RESULT
1269         int MOD_RESULT = 0;
1270
1271         if (strlen(cname) > CHANMAX-1)
1272         {
1273                 cname[CHANMAX-1] = '\0';
1274         }
1275
1276         log(DEBUG,"add_channel: %s %s",user->nick,cname);
1277         
1278         if ((FindChan(cname)) && (has_channel(user,FindChan(cname))))
1279         {
1280                 return NULL; // already on the channel!
1281         }
1282
1283
1284         if (!FindChan(cname))
1285         {
1286                 FOREACH_RESULT(OnUserPreJoin(user,NULL,cname));
1287                 if (MOD_RESULT) {
1288                         return NULL;
1289                 }
1290
1291                 /* create a new one */
1292                 log(DEBUG,"add_channel: creating: %s",cname);
1293                 {
1294                         chanlist[cname] = new chanrec();
1295
1296                         strcpy(chanlist[cname]->name, cname);
1297                         chanlist[cname]->topiclock = 1;
1298                         chanlist[cname]->noexternal = 1;
1299                         chanlist[cname]->created = time(NULL);
1300                         strcpy(chanlist[cname]->topic, "");
1301                         strncpy(chanlist[cname]->setby, user->nick,NICKMAX);
1302                         chanlist[cname]->topicset = 0;
1303                         Ptr = chanlist[cname];
1304                         log(DEBUG,"add_channel: created: %s",cname);
1305                         /* set created to 2 to indicate user
1306                          * is the first in the channel
1307                          * and should be given ops */
1308                         created = 2;
1309                 }
1310         }
1311         else
1312         {
1313                 /* channel exists, just fish out a pointer to its struct */
1314                 Ptr = FindChan(cname);
1315                 if (Ptr)
1316                 {
1317                         log(DEBUG,"add_channel: joining to: %s",Ptr->name);
1318                         
1319                         // the override flag allows us to bypass channel modes
1320                         // and bans (used by servers)
1321                         if (!override)
1322                         {
1323                                 FOREACH_RESULT(OnUserPreJoin(user,Ptr,cname));
1324                                 if (MOD_RESULT) {
1325                                         return NULL;
1326                                 }
1327                                 
1328                                 if (strcmp(Ptr->key,""))
1329                                 {
1330                                         log(DEBUG,"add_channel: %s has key %s",Ptr->name,Ptr->key);
1331                                         if (!key)
1332                                         {
1333                                                 log(DEBUG,"add_channel: no key given in JOIN");
1334                                                 WriteServ(user->fd,"475 %s %s :Cannot join channel (Requires key)",user->nick, Ptr->name);
1335                                                 return NULL;
1336                                         }
1337                                         else
1338                                         {
1339                                                 log(DEBUG,"key at %p is %s",key,key);
1340                                                 if (strcasecmp(key,Ptr->key))
1341                                                 {
1342                                                         log(DEBUG,"add_channel: bad key given in JOIN");
1343                                                         WriteServ(user->fd,"475 %s %s :Cannot join channel (Incorrect key)",user->nick, Ptr->name);
1344                                                         return NULL;
1345                                                 }
1346                                         }
1347                                 }
1348                                 log(DEBUG,"add_channel: no key");
1349         
1350                                 if (Ptr->inviteonly)
1351                                 {
1352                                         log(DEBUG,"add_channel: channel is +i");
1353                                         if (user->IsInvited(Ptr->name))
1354                                         {
1355                                                 /* user was invited to channel */
1356                                                 /* there may be an optional channel NOTICE here */
1357                                         }
1358                                         else
1359                                         {
1360                                                 WriteServ(user->fd,"473 %s %s :Cannot join channel (Invite only)",user->nick, Ptr->name);
1361                                                 return NULL;
1362                                         }
1363                                 }
1364                                 log(DEBUG,"add_channel: channel is not +i");
1365         
1366                                 if (Ptr->limit)
1367                                 {
1368                                         if (usercount(Ptr) == Ptr->limit)
1369                                         {
1370                                                 WriteServ(user->fd,"471 %s %s :Cannot join channel (Channel is full)",user->nick, Ptr->name);
1371                                                 return NULL;
1372                                         }
1373                                 }
1374                                 
1375                                 log(DEBUG,"add_channel: about to walk banlist");
1376         
1377                                 /* check user against the channel banlist */
1378                                 if (Ptr)
1379                                 {
1380                                         if (Ptr->bans.size())
1381                                         {
1382                                                 for (BanList::iterator i = Ptr->bans.begin(); i != Ptr->bans.end(); i++)
1383                                                 {
1384                                                         if (match(user->GetFullHost(),i->data))
1385                                                         {
1386                                                                 WriteServ(user->fd,"474 %s %s :Cannot join channel (You're banned)",user->nick, Ptr->name);
1387                                                                 return NULL;
1388                                                         }
1389                                                 }
1390                                         }
1391                                 }
1392                                 
1393                                 log(DEBUG,"add_channel: bans checked");
1394                                 
1395
1396                                 if ((Ptr) && (user))
1397                                 {
1398                                         user->RemoveInvite(Ptr->name);
1399                                 }
1400         
1401                                 log(DEBUG,"add_channel: invites removed");
1402
1403                         }
1404                         else
1405                         {
1406                                 log(DEBUG,"Overridden checks");
1407                         }
1408
1409                         
1410                 }
1411                 created = 1;
1412         }
1413
1414         log(DEBUG,"Passed channel checks");
1415         
1416         for (int i =0; i != MAXCHANS; i++)
1417         {
1418                 log(DEBUG,"Check location %d",i);
1419                 if (user->chans[i].channel == NULL)
1420                 {
1421                         log(DEBUG,"Adding into their channel list at location %d",i);
1422
1423                         if (created == 2) 
1424                         {
1425                                 /* first user in is given ops */
1426                                 user->chans[i].uc_modes = UCMODE_OP;
1427                         }
1428                         else
1429                         {
1430                                 user->chans[i].uc_modes = 0;
1431                         }
1432                         user->chans[i].channel = Ptr;
1433                         WriteChannel(Ptr,user,"JOIN :%s",Ptr->name);
1434                         
1435                         if (!override) // we're not overriding... so this isnt part of a netburst, broadcast it.
1436                         {
1437                                 // use the stamdard J token with no privilages.
1438                                 char buffer[MAXBUF];
1439                                 snprintf(buffer,MAXBUF,"J %s :%s",user->nick,Ptr->name);
1440                                 NetSendToAll(buffer);
1441                         }
1442
1443                         log(DEBUG,"Sent JOIN to client");
1444
1445                         if (Ptr->topicset)
1446                         {
1447                                 WriteServ(user->fd,"332 %s %s :%s", user->nick, Ptr->name, Ptr->topic);
1448                                 WriteServ(user->fd,"333 %s %s %s %d", user->nick, Ptr->name, Ptr->setby, Ptr->topicset);
1449                         }
1450                         userlist(user,Ptr);
1451                         WriteServ(user->fd,"366 %s %s :End of /NAMES list.", user->nick, Ptr->name);
1452                         WriteServ(user->fd,"324 %s %s +%s",user->nick, Ptr->name,chanmodes(Ptr));
1453                         WriteServ(user->fd,"329 %s %s %d", user->nick, Ptr->name, Ptr->created);
1454                         FOREACH_MOD OnUserJoin(user,Ptr);
1455                         return Ptr;
1456                 }
1457         }
1458         log(DEBUG,"add_channel: user channel max exceeded: %s %s",user->nick,cname);
1459         WriteServ(user->fd,"405 %s %s :You are on too many channels",user->nick, cname);
1460         return NULL;
1461 }
1462
1463 /* remove a channel from a users record, and remove the record from memory
1464  * if the channel has become empty */
1465
1466 chanrec* del_channel(userrec *user, const char* cname, const char* reason, bool local)
1467 {
1468         if ((!user) || (!cname))
1469         {
1470                 log(DEFAULT,"*** BUG *** del_channel was given an invalid parameter");
1471                 return NULL;
1472         }
1473
1474         chanrec* Ptr;
1475         int created = 0;
1476
1477         if ((!cname) || (!user))
1478         {
1479                 return NULL;
1480         }
1481
1482         Ptr = FindChan(cname);
1483         
1484         if (!Ptr)
1485         {
1486                 return NULL;
1487         }
1488
1489         FOREACH_MOD OnUserPart(user,Ptr);
1490         log(DEBUG,"del_channel: removing: %s %s",user->nick,Ptr->name);
1491         
1492         for (int i =0; i != MAXCHANS; i++)
1493         {
1494                 /* zap it from the channel list of the user */
1495                 if (user->chans[i].channel == Ptr)
1496                 {
1497                         if (reason)
1498                         {
1499                                 WriteChannel(Ptr,user,"PART %s :%s",Ptr->name, reason);
1500
1501                                 if (!local)
1502                                 {
1503                                         char buffer[MAXBUF];
1504                                         snprintf(buffer,MAXBUF,"L %s %s :%s",user->nick,Ptr->name,reason);
1505                                         NetSendToAll(buffer);
1506                                 }
1507
1508                                 
1509                         }
1510                         else
1511                         {
1512                                 if (!local)
1513                                 {
1514                                         char buffer[MAXBUF];
1515                                         snprintf(buffer,MAXBUF,"L %s %s :",user->nick,Ptr->name);
1516                                         NetSendToAll(buffer);
1517                                 }
1518                         
1519                                 WriteChannel(Ptr,user,"PART :%s",Ptr->name);
1520                         }
1521                         user->chans[i].uc_modes = 0;
1522                         user->chans[i].channel = NULL;
1523                         log(DEBUG,"del_channel: unlinked: %s %s",user->nick,Ptr->name);
1524                         break;
1525                 }
1526         }
1527         
1528         /* if there are no users left on the channel */
1529         if (!usercount(Ptr))
1530         {
1531                 chan_hash::iterator iter = chanlist.find(Ptr->name);
1532
1533                 log(DEBUG,"del_channel: destroying channel: %s",Ptr->name);
1534
1535                 /* kill the record */
1536                 if (iter != chanlist.end())
1537                 {
1538                         log(DEBUG,"del_channel: destroyed: %s",Ptr->name);
1539                         delete iter->second;
1540                         chanlist.erase(iter);
1541                 }
1542         }
1543 }
1544
1545
1546 void kick_channel(userrec *src,userrec *user, chanrec *Ptr, char* reason)
1547 {
1548         if ((!src) || (!user) || (!Ptr) || (!reason))
1549         {
1550                 log(DEFAULT,"*** BUG *** kick_channel was given an invalid parameter");
1551                 return;
1552         }
1553
1554         int i = 0;
1555         int created = 0;
1556
1557         if ((!Ptr) || (!user) || (!src))
1558         {
1559                 return;
1560         }
1561
1562         log(DEBUG,"kick_channel: removing: %s %s %s",user->nick,Ptr->name,src->nick);
1563
1564         if (!has_channel(user,Ptr))
1565         {
1566                 WriteServ(src->fd,"441 %s %s %s :They are not on that channel",src->nick, user->nick, Ptr->name);
1567                 return;
1568         }
1569         if ((cstatus(src,Ptr) < STATUS_HOP) || (cstatus(src,Ptr) < cstatus(user,Ptr)))
1570         {
1571                 if (cstatus(src,Ptr) == STATUS_HOP)
1572                 {
1573                         WriteServ(src->fd,"482 %s %s :You must be a channel operator",src->nick, Ptr->name);
1574                 }
1575                 else
1576                 {
1577                         WriteServ(src->fd,"482 %s %s :You must be at least a half-operator to change modes on this channel",src->nick, Ptr->name);
1578                 }
1579                 
1580                 return;
1581         }
1582         
1583         for (int i =0; i != MAXCHANS; i++)
1584         {
1585                 /* zap it from the channel list of the user */
1586                 if (user->chans[i].channel)
1587                 if (!strcasecmp(user->chans[i].channel->name,Ptr->name))
1588                 {
1589                         WriteChannel(Ptr,src,"KICK %s %s :%s",Ptr->name, user->nick, reason);
1590                         user->chans[i].uc_modes = 0;
1591                         user->chans[i].channel = NULL;
1592                         log(DEBUG,"del_channel: unlinked: %s %s",user->nick,Ptr->name);
1593                         break;
1594                 }
1595         }
1596         
1597         /* if there are no users left on the channel */
1598         if (!usercount(Ptr))
1599         {
1600                 chan_hash::iterator iter = chanlist.find(Ptr->name);
1601
1602                 log(DEBUG,"del_channel: destroying channel: %s",Ptr->name);
1603
1604                 /* kill the record */
1605                 if (iter != chanlist.end())
1606                 {
1607                         log(DEBUG,"del_channel: destroyed: %s",Ptr->name);
1608                         delete iter->second;
1609                         chanlist.erase(iter);
1610                 }
1611         }
1612 }
1613
1614
1615
1616
1617 /* This function pokes and hacks at a parameter list like the following:
1618  *
1619  * PART #winbot, #darkgalaxy :m00!
1620  *
1621  * to turn it into a series of individual calls like this:
1622  *
1623  * PART #winbot :m00!
1624  * PART #darkgalaxy :m00!
1625  *
1626  * The seperate calls are sent to a callback function provided by the caller
1627  * (the caller will usually call itself recursively). The callback function
1628  * must be a command handler. Calling this function on a line with no list causes
1629  * no action to be taken. You must provide a starting and ending parameter number
1630  * where the range of the list can be found, useful if you have a terminating
1631  * parameter as above which is actually not part of the list, or parameters
1632  * before the actual list as well. This code is used by many functions which
1633  * can function as "one to list" (see the RFC) */
1634
1635 int loop_call(handlerfunc fn, char **parameters, int pcnt, userrec *u, int start, int end, int joins)
1636 {
1637         char plist[MAXBUF];
1638         char *param;
1639         char *pars[32];
1640         char blog[32][MAXBUF];
1641         char blog2[32][MAXBUF];
1642         int i = 0, j = 0, q = 0, total = 0, t = 0, t2 = 0, total2 = 0;
1643         char keystr[MAXBUF];
1644         char moo[MAXBUF];
1645
1646         for (int i = 0; i <32; i++)
1647                 strcpy(blog[i],"");
1648
1649         for (int i = 0; i <32; i++)
1650                 strcpy(blog2[i],"");
1651
1652         strcpy(moo,"");
1653         for (int i = 0; i <10; i++)
1654         {
1655                 if (!parameters[i])
1656                 {
1657                         parameters[i] = moo;
1658                 }
1659         }
1660         if (joins)
1661         {
1662                 if (pcnt > 1) /* we have a key to copy */
1663                 {
1664                         strcpy(keystr,parameters[1]);
1665                 }
1666         }
1667
1668         if (!parameters[start])
1669         {
1670                 return 0;
1671         }
1672         if (!strchr(parameters[start],','))
1673         {
1674                 return 0;
1675         }
1676         strcpy(plist,"");
1677         for (int i = start; i <= end; i++)
1678         {
1679                 if (parameters[i])
1680                 {
1681                         strcat(plist,parameters[i]);
1682                 }
1683         }
1684         
1685         j = 0;
1686         param = plist;
1687
1688         t = strlen(plist);
1689         for (int i = 0; i < t; i++)
1690         {
1691                 if (plist[i] == ',')
1692                 {
1693                         plist[i] = '\0';
1694                         strcpy(blog[j++],param);
1695                         param = plist+i+1;
1696                         if (j>20)
1697                         {
1698                                 WriteServ(u->fd,"407 %s %s :Too many targets in list, message not delivered.",u->nick,blog[j-1]);
1699                                 return 1;
1700                         }
1701                 }
1702         }
1703         strcpy(blog[j++],param);
1704         total = j;
1705
1706         if ((joins) && (keystr) && (total>0)) // more than one channel and is joining
1707         {
1708                 strcat(keystr,",");
1709         }
1710         
1711         if ((joins) && (keystr))
1712         {
1713                 if (strchr(keystr,','))
1714                 {
1715                         j = 0;
1716                         param = keystr;
1717                         t2 = strlen(keystr);
1718                         for (int i = 0; i < t2; i++)
1719                         {
1720                                 if (keystr[i] == ',')
1721                                 {
1722                                         keystr[i] = '\0';
1723                                         strcpy(blog2[j++],param);
1724                                         param = keystr+i+1;
1725                                 }
1726                         }
1727                         strcpy(blog2[j++],param);
1728                         total2 = j;
1729                 }
1730         }
1731
1732         for (j = 0; j < total; j++)
1733         {
1734                 if (blog[j])
1735                 {
1736                         pars[0] = blog[j];
1737                 }
1738                 for (q = end; q < pcnt-1; q++)
1739                 {
1740                         if (parameters[q+1])
1741                         {
1742                                 pars[q-end+1] = parameters[q+1];
1743                         }
1744                 }
1745                 if ((joins) && (parameters[1]))
1746                 {
1747                         if (pcnt > 1)
1748                         {
1749                                 pars[1] = blog2[j];
1750                         }
1751                         else
1752                         {
1753                                 pars[1] = NULL;
1754                         }
1755                 }
1756                 /* repeatedly call the function with the hacked parameter list */
1757                 if ((joins) && (pcnt > 1))
1758                 {
1759                         if (pars[1])
1760                         {
1761                                 // pars[1] already set up and containing key from blog2[j]
1762                                 fn(pars,2,u);
1763                         }
1764                         else
1765                         {
1766                                 pars[1] = parameters[1];
1767                                 fn(pars,2,u);
1768                         }
1769                 }
1770                 else
1771                 {
1772                         fn(pars,pcnt-(end-start),u);
1773                 }
1774         }
1775
1776         return 1;
1777 }
1778
1779
1780 void handle_join(char **parameters, int pcnt, userrec *user)
1781 {
1782         chanrec* Ptr;
1783         int i = 0;
1784         
1785         if (loop_call(handle_join,parameters,pcnt,user,0,0,1))
1786                 return;
1787         if (parameters[0][0] == '#')
1788         {
1789                 Ptr = add_channel(user,parameters[0],parameters[1],false);
1790         }
1791 }
1792
1793
1794 void handle_part(char **parameters, int pcnt, userrec *user)
1795 {
1796         chanrec* Ptr;
1797
1798         if (pcnt > 1)
1799         {
1800                 if (loop_call(handle_part,parameters,pcnt,user,0,pcnt-2,0))
1801                         return;
1802                 del_channel(user,parameters[0],parameters[1],false);
1803         }
1804         else
1805         {
1806                 if (loop_call(handle_part,parameters,pcnt,user,0,pcnt-1,0))
1807                         return;
1808                 del_channel(user,parameters[0],NULL,false);
1809         }
1810 }
1811
1812 void handle_kick(char **parameters, int pcnt, userrec *user)
1813 {
1814         chanrec* Ptr = FindChan(parameters[0]);
1815         userrec* u   = Find(parameters[1]);
1816
1817         if ((!u) || (!Ptr))
1818         {
1819                 WriteServ(user->fd,"401 %s %s :No suck nick/channel",user->nick, parameters[0]);
1820                 return;
1821         }
1822         
1823         if (!has_channel(u,Ptr))
1824         {
1825                 WriteServ(user->fd,"442 %s %s :You're not on that channel!",user->nick, parameters[0]);
1826                 return;
1827         }
1828
1829         char reason[MAXBUF];
1830         
1831         if (pcnt > 2)
1832         {
1833                 strncpy(reason,parameters[2],MAXBUF);
1834                 if (strlen(reason)>MAXKICK)
1835                 {
1836                         reason[MAXKICK-1] = '\0';
1837                 }
1838
1839                 kick_channel(user,u,Ptr,reason);
1840         }
1841         else
1842         {
1843                 strcpy(reason,user->nick);
1844                 kick_channel(user,u,Ptr,reason);
1845         }
1846         
1847         // this must be propogated so that channel membership is kept in step network-wide
1848         
1849         char buffer[MAXBUF];
1850         snprintf(buffer,MAXBUF,"k %s %s %s :%s",user->nick,u->nick,Ptr->name,reason);
1851         NetSendToAll(buffer);
1852 }
1853
1854
1855 void handle_die(char **parameters, int pcnt, userrec *user)
1856 {
1857         log(DEBUG,"die: %s",user->nick);
1858         if (!strcmp(parameters[0],diepass))
1859         {
1860                 WriteOpers("*** DIE command from %s!%s@%s, terminating...",user->nick,user->ident,user->host);
1861                 sleep(DieDelay);
1862                 Exit(ERROR);
1863         }
1864         else
1865         {
1866                 WriteOpers("*** Failed DIE Command from %s!%s@%s.",user->nick,user->ident,user->host);
1867         }
1868 }
1869
1870 void handle_restart(char **parameters, int pcnt, userrec *user)
1871 {
1872         log(DEBUG,"restart: %s",user->nick);
1873         if (!strcmp(parameters[0],restartpass))
1874         {
1875                 WriteOpers("*** RESTART command from %s!%s@%s, Pretending to restart till this is finished :D",user->nick,user->ident,user->host);
1876                 sleep(DieDelay);
1877                 Exit(ERROR);
1878                 /* Will finish this later when i can be arsed :) */
1879         }
1880         else
1881         {
1882                 WriteOpers("*** Failed RESTART Command from %s!%s@%s.",user->nick,user->ident,user->host);
1883         }
1884 }
1885
1886
1887 void kill_link(userrec *user,const char* r)
1888 {
1889         user_hash::iterator iter = clientlist.find(user->nick);
1890         
1891         char reason[MAXBUF];
1892         
1893         strncpy(reason,r,MAXBUF);
1894
1895         if (strlen(reason)>MAXQUIT)
1896         {
1897                 reason[MAXQUIT-1] = '\0';
1898         }
1899
1900         log(DEBUG,"kill_link: %s '%s'",user->nick,reason);
1901         Write(user->fd,"ERROR :Closing link (%s@%s) [%s]",user->ident,user->host,reason);
1902         log(DEBUG,"closing fd %d",user->fd);
1903
1904         /* bugfix, cant close() a nonblocking socket (sux!) */
1905         if (user->registered == 7) {
1906                 FOREACH_MOD OnUserQuit(user);
1907                 WriteCommonExcept(user,"QUIT :%s",reason);
1908
1909                 // Q token must go to ALL servers!!!
1910                 char buffer[MAXBUF];
1911                 snprintf(buffer,MAXBUF,"Q %s :%s",user->nick,reason);
1912                 NetSendToAll(buffer);
1913         }
1914
1915         /* push the socket on a stack of sockets due to be closed at the next opportunity
1916          * 'Client exited' is an exception to this as it means the client side has already
1917          * closed the socket, we don't need to do it.
1918          */
1919         fd_reap.push_back(user->fd);
1920         
1921         bool do_purge = false;
1922         
1923         if (user->registered == 7) {
1924                 WriteOpers("*** Client exiting: %s!%s@%s [%s]",user->nick,user->ident,user->host,reason);
1925                 AddWhoWas(user);
1926         }
1927
1928         if (iter != clientlist.end())
1929         {
1930                 log(DEBUG,"deleting user hash value %d",iter->second);
1931                 if ((iter->second) && (user->registered == 7)) {
1932                         delete iter->second;
1933                 }
1934                 clientlist.erase(iter);
1935         }
1936
1937         if (user->registered == 7) {
1938                 purge_empty_chans();
1939         }
1940 }
1941
1942
1943 void handle_kill(char **parameters, int pcnt, userrec *user)
1944 {
1945         userrec *u = Find(parameters[0]);
1946         char killreason[MAXBUF];
1947         
1948         log(DEBUG,"kill: %s %s",parameters[0],parameters[1]);
1949         if (u)
1950         {
1951                 if (strcmp(ServerName,u->server))
1952                 {
1953                         // remote kill
1954                         WriteOpers("*** Remote kill by %s: %s!%s@%s (%s)",user->nick,u->nick,u->ident,u->host,parameters[1]);
1955                         sprintf(killreason,"[%s] Killed (%s (%s))",ServerName,user->nick,parameters[1]);
1956                         WriteCommonExcept(u,"QUIT :%s",killreason);
1957                         // K token must go to ALL servers!!!
1958                         char buffer[MAXBUF];
1959                         snprintf(buffer,MAXBUF,"K %s %s :%s",user->nick,u->nick,killreason);
1960                         NetSendToAll(buffer);
1961                         
1962                         user_hash::iterator iter = clientlist.find(u->nick);
1963                         if (iter != clientlist.end())
1964                         {
1965                                 log(DEBUG,"deleting user hash value %d",iter->second);
1966                                 if ((iter->second) && (user->registered == 7)) {
1967                                         delete iter->second;
1968                                         }
1969                         clientlist.erase(iter);
1970                         }
1971                         purge_empty_chans();
1972                 }
1973                 else
1974                 {
1975                         // local kill
1976                         WriteTo(user, u, "KILL %s :%s!%s!%s (%s)", u->nick, ServerName,user->dhost,user->nick,parameters[1]);
1977                         WriteOpers("*** Local Kill by %s: %s!%s@%s (%s)",user->nick,u->nick,u->ident,u->host,parameters[1]);
1978                         sprintf(killreason,"Killed (%s (%s))",user->nick,parameters[1]);
1979                         kill_link(u,killreason);
1980                 }
1981         }
1982         else
1983         {
1984                 WriteServ(user->fd,"401 %s %s :No suck nick/channel",user->nick, parameters[0]);
1985         }
1986 }
1987
1988 void handle_summon(char **parameters, int pcnt, userrec *user)
1989 {
1990         WriteServ(user->fd,"445 %s :SUMMON has been disabled (depreciated command)",user->nick);
1991 }
1992
1993 void handle_users(char **parameters, int pcnt, userrec *user)
1994 {
1995         WriteServ(user->fd,"445 %s :USERS has been disabled (depreciated command)",user->nick);
1996 }
1997
1998
1999 // looks up a users password for their connection class (<ALLOW>/<DENY> tags)
2000
2001 char* Passwd(userrec *user)
2002 {
2003         for (ClassVector::iterator i = Classes.begin(); i != Classes.end(); i++)
2004         {
2005                 if (match(user->host,i->host) && (i->type == CC_ALLOW))
2006                 {
2007                         return i->pass;
2008                 }
2009         }
2010         return "";
2011 }
2012
2013 bool IsDenied(userrec *user)
2014 {
2015         for (ClassVector::iterator i = Classes.begin(); i != Classes.end(); i++)
2016         {
2017                 if (match(user->host,i->host) && (i->type == CC_DENY))
2018                 {
2019                         return true;
2020                 }
2021         }
2022         return false;
2023 }
2024
2025
2026
2027 void handle_pass(char **parameters, int pcnt, userrec *user)
2028 {
2029         // Check to make sure they havnt registered -- Fix by FCS
2030         if (user->registered == 7)
2031         {
2032                 WriteServ(user->fd,"462 %s :You may not reregister",user->nick);
2033                 return;
2034         }
2035         if (!strcasecmp(parameters[0],Passwd(user)))
2036         {
2037                 user->haspassed = true;
2038         }
2039 }
2040
2041 void handle_invite(char **parameters, int pcnt, userrec *user)
2042 {
2043         userrec* u = Find(parameters[0]);
2044         chanrec* c = FindChan(parameters[1]);
2045
2046         if ((!c) || (!u))
2047         {
2048                 if (!c)
2049                 {
2050                         WriteServ(user->fd,"401 %s %s :No such nick/channel",user->nick, parameters[1]);
2051                 }
2052                 else
2053                 {
2054                         if (c->inviteonly)
2055                         {
2056                                 WriteServ(user->fd,"401 %s %s :No such nick/channel",user->nick, parameters[0]);
2057                         }
2058                 }
2059
2060                 return;
2061         }
2062
2063         if (c->inviteonly)
2064         {
2065                 if (cstatus(user,c) < STATUS_HOP)
2066                 {
2067                         WriteServ(user->fd,"482 %s %s :You must be at least a half-operator to change modes on this channel",user->nick, c->name);
2068                         return;
2069                 }
2070         }
2071         if (has_channel(u,c))
2072         {
2073                 WriteServ(user->fd,"443 %s %s %s :Is already on channel %s",user->nick,u->nick,c->name,c->name);
2074                 return;
2075         }
2076         if (!has_channel(user,c))
2077         {
2078                 WriteServ(user->fd,"442 %s %s :You're not on that channel!",user->nick, c->name);
2079                 return;
2080         }
2081         u->InviteTo(c->name);
2082         WriteFrom(u->fd,user,"INVITE %s :%s",u->nick,c->name);
2083         WriteServ(user->fd,"341 %s %s %s",user->nick,u->nick,c->name);
2084         
2085         // i token must go to ALL servers!!!
2086         char buffer[MAXBUF];
2087         snprintf(buffer,MAXBUF,"i %s %s %s",u->nick,user->nick,c->name);
2088         NetSendToAll(buffer);
2089 }
2090
2091 void handle_topic(char **parameters, int pcnt, userrec *user)
2092 {
2093         chanrec* Ptr;
2094
2095         if (pcnt == 1)
2096         {
2097                 if (strlen(parameters[0]) <= CHANMAX)
2098                 {
2099                         Ptr = FindChan(parameters[0]);
2100                         if (Ptr)
2101                         {
2102                                 if (Ptr->topicset)
2103                                 {
2104                                         WriteServ(user->fd,"332 %s %s :%s", user->nick, Ptr->name, Ptr->topic);
2105                                         WriteServ(user->fd,"333 %s %s %s %d", user->nick, Ptr->name, Ptr->setby, Ptr->topicset);
2106                                 }
2107                                 else
2108                                 {
2109                                         WriteServ(user->fd,"331 %s %s :No topic is set.", user->nick, Ptr->name);
2110                                 }
2111                         }
2112                         else
2113                         {
2114                                 WriteServ(user->fd,"401 %s %s :No suck nick/channel",user->nick, parameters[0]);
2115                         }
2116                 }
2117                 return;
2118         }
2119         else if (pcnt>1)
2120         {
2121                 if (strlen(parameters[0]) <= CHANMAX)
2122                 {
2123                         Ptr = FindChan(parameters[0]);
2124                         if (Ptr)
2125                         {
2126                                 if ((Ptr->topiclock) && (cstatus(user,Ptr)<STATUS_HOP))
2127                                 {
2128                                         WriteServ(user->fd,"482 %s %s :You must be at least a half-operator to change modes on this channel", user->nick, Ptr->name);
2129                                         return;
2130                                 }
2131                                 
2132                                 char topic[MAXBUF];
2133                                 strncpy(topic,parameters[1],MAXBUF);
2134                                 if (strlen(topic)>MAXTOPIC)
2135                                 {
2136                                         topic[MAXTOPIC-1] = '\0';
2137                                 }
2138                                         
2139                                 strcpy(Ptr->topic,topic);
2140                                 strcpy(Ptr->setby,user->nick);
2141                                 Ptr->topicset = time(NULL);
2142                                 WriteChannel(Ptr,user,"TOPIC %s :%s",Ptr->name, Ptr->topic);
2143
2144                                 // t token must go to ALL servers!!!
2145                                 char buffer[MAXBUF];
2146                                 snprintf(buffer,MAXBUF,"t %s %s :%s",user->nick,Ptr->name,topic);
2147                                 NetSendToAll(buffer);
2148                         }
2149                         else
2150                         {
2151                                 WriteServ(user->fd,"401 %s %s :No suck nick/channel",user->nick, parameters[0]);
2152                         }
2153                 }
2154         }
2155 }
2156
2157 /* sends out an error notice to all connected clients (not to be used
2158  * lightly!) */
2159
2160 void send_error(char *s)
2161 {
2162         log(DEBUG,"send_error: %s",s);
2163         for (user_hash::const_iterator i = clientlist.begin(); i != clientlist.end(); i++)
2164         {
2165                 if (isnick(i->second->nick))
2166                 {
2167                         WriteServ(i->second->fd,"NOTICE %s :%s",i->second->nick,s);
2168                 }
2169                 else
2170                 {
2171                         // fix - unregistered connections receive ERROR, not NOTICE
2172                         Write(i->second->fd,"ERROR :%s",s);
2173                 }
2174         }
2175 }
2176
2177 void Error(int status)
2178 {
2179         signal (SIGALRM, SIG_IGN);
2180         signal (SIGPIPE, SIG_IGN);
2181         signal (SIGTERM, SIG_IGN);
2182         signal (SIGABRT, SIG_IGN);
2183         signal (SIGSEGV, SIG_IGN);
2184         signal (SIGURG, SIG_IGN);
2185         signal (SIGKILL, SIG_IGN);
2186         log(DEBUG,"*** fell down a pothole in the road to perfection ***");
2187         send_error("Error! Segmentation fault! save meeeeeeeeeeeeee *splat!*");
2188         exit(status);
2189 }
2190
2191
2192 int main(int argc, char *argv[])
2193 {
2194         Start();
2195         srand(time(NULL));
2196         log(DEBUG,"*** InspIRCd starting up!");
2197         if (!FileExists(CONFIG_FILE))
2198         {
2199                 printf("ERROR: Cannot open config file: %s\nExiting...\n",CONFIG_FILE);
2200                 log(DEBUG,"main: no config");
2201                 printf("ERROR: Your config file is missing, this IRCd will self destruct in 10 seconds!\n");
2202                 Exit(ERROR);
2203         }
2204         if (argc > 1) {
2205                 if (!strcmp(argv[1],"-nofork")) {
2206                         nofork = true;
2207                 }
2208         }
2209         if (InspIRCd() == ERROR)
2210         {
2211                 log(DEBUG,"main: daemon function bailed");
2212                 printf("ERROR: could not initialise. Shutting down.\n");
2213                 Exit(ERROR);
2214         }
2215         Exit(TRUE);
2216         return 0;
2217 }
2218
2219 template<typename T> inline string ConvToStr(const T &in)
2220 {
2221         stringstream tmp;
2222         if (!(tmp << in)) return string();
2223         return tmp.str();
2224 }
2225
2226 /* re-allocates a nick in the user_hash after they change nicknames,
2227  * returns a pointer to the new user as it may have moved */
2228
2229 userrec* ReHashNick(char* Old, char* New)
2230 {
2231         user_hash::iterator newnick;
2232         user_hash::iterator oldnick = clientlist.find(Old);
2233
2234         log(DEBUG,"ReHashNick: %s %s",Old,New);
2235         
2236         if (!strcasecmp(Old,New))
2237         {
2238                 log(DEBUG,"old nick is new nick, skipping");
2239                 return oldnick->second;
2240         }
2241         
2242         if (oldnick == clientlist.end()) return NULL; /* doesnt exist */
2243
2244         log(DEBUG,"ReHashNick: Found hashed nick %s",Old);
2245
2246         clientlist[New] = new userrec();
2247         clientlist[New] = oldnick->second;
2248         /*delete oldnick->second; */
2249         clientlist.erase(oldnick);
2250
2251         log(DEBUG,"ReHashNick: Nick rehashed as %s",New);
2252         
2253         return clientlist[New];
2254 }
2255
2256 /* adds or updates an entry in the whowas list */
2257 void AddWhoWas(userrec* u)
2258 {
2259         user_hash::iterator iter = whowas.find(u->nick);
2260         userrec *a = new userrec();
2261         strcpy(a->nick,u->nick);
2262         strcpy(a->ident,u->ident);
2263         strcpy(a->dhost,u->dhost);
2264         strcpy(a->host,u->host);
2265         strcpy(a->fullname,u->fullname);
2266         strcpy(a->server,u->server);
2267         a->signon = u->signon;
2268
2269         /* MAX_WHOWAS:   max number of /WHOWAS items
2270          * WHOWAS_STALE: number of hours before a WHOWAS item is marked as stale and
2271          *               can be replaced by a newer one
2272          */
2273         
2274         if (iter == whowas.end())
2275         {
2276                 if (whowas.size() == WHOWAS_MAX)
2277                 {
2278                         for (user_hash::iterator i = whowas.begin(); i != whowas.end(); i++)
2279                         {
2280                                 // 3600 seconds in an hour ;)
2281                                 if ((i->second->signon)<(time(NULL)-(WHOWAS_STALE*3600)))
2282                                 {
2283                                         delete i->second;
2284                                         i->second = a;
2285                                         log(DEBUG,"added WHOWAS entry, purged an old record");
2286                                         return;
2287                                 }
2288                         }
2289                 }
2290                 else
2291                 {
2292                         log(DEBUG,"added fresh WHOWAS entry");
2293                         whowas[a->nick] = a;
2294                 }
2295         }
2296         else
2297         {
2298                 log(DEBUG,"updated WHOWAS entry");
2299                 delete iter->second;
2300                 iter->second = a;
2301         }
2302 }
2303
2304
2305 /* add a client connection to the sockets list */
2306 void AddClient(int socket, char* host, int port, bool iscached)
2307 {
2308         int i;
2309         int blocking = 1;
2310         char resolved[MAXBUF];
2311         string tempnick;
2312         char tn2[MAXBUF];
2313         user_hash::iterator iter;
2314
2315         tempnick = ConvToStr(socket) + "-unknown";
2316         sprintf(tn2,"%d-unknown",socket);
2317
2318         iter = clientlist.find(tempnick);
2319
2320         if (iter != clientlist.end()) return;
2321
2322         /*
2323          * It is OK to access the value here this way since we know
2324          * it exists, we just created it above.
2325          *
2326          * At NO other time should you access a value in a map or a
2327          * hash_map this way.
2328          */
2329         clientlist[tempnick] = new userrec();
2330
2331         NonBlocking(socket);
2332         log(DEBUG,"AddClient: %d %s %d",socket,host,port);
2333
2334         clientlist[tempnick]->fd = socket;
2335         strncpy(clientlist[tempnick]->nick, tn2,NICKMAX);
2336         strncpy(clientlist[tempnick]->host, host,160);
2337         strncpy(clientlist[tempnick]->dhost, host,160);
2338         strncpy(clientlist[tempnick]->server, ServerName,256);
2339         strncpy(clientlist[tempnick]->ident, "unknown",9);
2340         clientlist[tempnick]->registered = 0;
2341         clientlist[tempnick]->signon = time(NULL);
2342         clientlist[tempnick]->nping = time(NULL)+240;
2343         clientlist[tempnick]->lastping = 1;
2344         clientlist[tempnick]->port = port;
2345
2346         if (iscached)
2347         {
2348                 WriteServ(socket,"NOTICE Auth :Found your hostname (cached)...");
2349         }
2350         else
2351         {
2352                 WriteServ(socket,"NOTICE Auth :Looking up your hostname...");
2353         }
2354
2355         // set the registration timeout for this user
2356         unsigned long class_regtimeout = 90;
2357         for (ClassVector::iterator i = Classes.begin(); i != Classes.end(); i++)
2358         {
2359                 if (match(clientlist[tempnick]->host,i->host) && (i->type == CC_ALLOW))
2360                 {
2361                         class_regtimeout = (unsigned long)i->registration_timeout;
2362                         break;
2363                 }
2364         }
2365
2366         int class_flood = 0;
2367         for (ClassVector::iterator i = Classes.begin(); i != Classes.end(); i++)
2368         {
2369                 if (match(clientlist[tempnick]->host,i->host) && (i->type == CC_ALLOW))
2370                 {
2371                         class_flood = i->flood;
2372                         break;
2373                 }
2374         }
2375
2376         clientlist[tempnick]->timeout = time(NULL)+class_regtimeout;
2377         clientlist[tempnick]->flood = class_flood;
2378
2379         for (int i = 0; i < MAXCHANS; i++)
2380         {
2381                 clientlist[tempnick]->chans[i].channel = NULL;
2382                 clientlist[tempnick]->chans[i].uc_modes = 0;
2383         }
2384
2385         if (clientlist.size() == MAXCLIENTS)
2386                 kill_link(clientlist[tempnick],"No more connections allowed in this class");
2387 }
2388
2389 void handle_names(char **parameters, int pcnt, userrec *user)
2390 {
2391         chanrec* c;
2392
2393         if (loop_call(handle_names,parameters,pcnt,user,0,pcnt-1,0))
2394                 return;
2395         c = FindChan(parameters[0]);
2396         if (c)
2397         {
2398                 /*WriteServ(user->fd,"353 %s = %s :%s", user->nick, c->name,*/
2399                 userlist(user,c);
2400                 WriteServ(user->fd,"366 %s %s :End of /NAMES list.", user->nick, c->name);
2401         }
2402         else
2403         {
2404                 WriteServ(user->fd,"401 %s %s :No suck nick/channel",user->nick, parameters[0]);
2405         }
2406 }
2407
2408 void handle_privmsg(char **parameters, int pcnt, userrec *user)
2409 {
2410         userrec *dest;
2411         chanrec *chan;
2412
2413         user->idle_lastmsg = time(NULL);
2414         
2415         if (loop_call(handle_privmsg,parameters,pcnt,user,0,pcnt-2,0))
2416                 return;
2417         if (parameters[0][0] == '#')
2418         {
2419                 chan = FindChan(parameters[0]);
2420                 if (chan)
2421                 {
2422                         if ((chan->noexternal) && (!has_channel(user,chan)))
2423                         {
2424                                 WriteServ(user->fd,"404 %s %s :Cannot send to channel (no external messages)", user->nick, chan->name);
2425                                 return;
2426                         }
2427                         if ((chan->moderated) && (cstatus(user,chan)<STATUS_VOICE))
2428                         {
2429                                 WriteServ(user->fd,"404 %s %s :Cannot send to channel (+m)", user->nick, chan->name);
2430                                 return;
2431                         }
2432                         
2433                         int MOD_RESULT = 0;
2434
2435                         FOREACH_RESULT(OnUserPreMessage(user,chan,TYPE_CHANNEL,std::string(parameters[1])));
2436                         if (MOD_RESULT) {
2437                                 return;
2438                         }
2439                         
2440                         ChanExceptSender(chan, user, "PRIVMSG %s :%s", chan->name, parameters[1]);
2441                         
2442                         // if any users of this channel are on remote servers, broadcast the packet
2443                         char buffer[MAXBUF];
2444                         snprintf(buffer,MAXBUF,"P %s %s :%s",user->nick,chan->name,parameters[1]);
2445                         NetSendToCommon(user,buffer);
2446                 }
2447                 else
2448                 {
2449                         /* no such nick/channel */
2450                         WriteServ(user->fd,"401 %s %s :No suck nick/channel",user->nick, parameters[0]);
2451                 }
2452                 return;
2453         }
2454         
2455         dest = Find(parameters[0]);
2456         if (dest)
2457         {
2458                 if (strcmp(dest->awaymsg,""))
2459                 {
2460                         /* auto respond with aweh msg */
2461                         WriteServ(user->fd,"301 %s %s :%s",user->nick,dest->nick,dest->awaymsg);
2462                 }
2463
2464                 int MOD_RESULT = 0;
2465                 
2466                 FOREACH_RESULT(OnUserPreMessage(user,dest,TYPE_USER,std::string(parameters[1])));
2467                 if (MOD_RESULT) {
2468                         return;
2469                 }
2470
2471
2472
2473                 if (!strcmp(dest->server,user->server))
2474                 {
2475                         // direct write, same server
2476                         WriteTo(user, dest, "PRIVMSG %s :%s", dest->nick, parameters[1]);
2477                 }
2478                 else
2479                 {
2480                         char buffer[MAXBUF];
2481                         snprintf(buffer,MAXBUF,"P %s %s :%s",user->nick,dest->nick,parameters[1]);
2482                         NetSendToOne(dest->server,buffer);
2483                 }
2484         }
2485         else
2486         {
2487                 /* no such nick/channel */
2488                 WriteServ(user->fd,"401 %s %s :No suck nick/channel",user->nick, parameters[0]);
2489         }
2490 }
2491
2492 void handle_notice(char **parameters, int pcnt, userrec *user)
2493 {
2494         userrec *dest;
2495         chanrec *chan;
2496
2497         user->idle_lastmsg = time(NULL);
2498         
2499         if (loop_call(handle_notice,parameters,pcnt,user,0,pcnt-2,0))
2500                 return;
2501         if (parameters[0][0] == '#')
2502         {
2503                 chan = FindChan(parameters[0]);
2504                 if (chan)
2505                 {
2506                         if ((chan->noexternal) && (!has_channel(user,chan)))
2507                         {
2508                                 WriteServ(user->fd,"404 %s %s :Cannot send to channel (no external messages)", user->nick, chan->name);
2509                                 return;
2510                         }
2511                         if ((chan->moderated) && (cstatus(user,chan)<STATUS_VOICE))
2512                         {
2513                                 WriteServ(user->fd,"404 %s %s :Cannot send to channel (+m)", user->nick, chan->name);
2514                                 return;
2515                         }
2516
2517                         int MOD_RESULT = 0;
2518                 
2519                         FOREACH_RESULT(OnUserPreNotice(user,chan,TYPE_CHANNEL,std::string(parameters[1])));
2520                         if (MOD_RESULT) {
2521                                 return;
2522                         }
2523
2524                         ChanExceptSender(chan, user, "NOTICE %s :%s", chan->name, parameters[1]);
2525
2526                         // if any users of this channel are on remote servers, broadcast the packet
2527                         char buffer[MAXBUF];
2528                         snprintf(buffer,MAXBUF,"V %s %s :%s",user->nick,chan->name,parameters[1]);
2529                         NetSendToCommon(user,buffer);
2530                 }
2531                 else
2532                 {
2533                         /* no such nick/channel */
2534                         WriteServ(user->fd,"401 %s %s :No suck nick/channel",user->nick, parameters[0]);
2535                 }
2536                 return;
2537         }
2538         
2539         dest = Find(parameters[0]);
2540         if (dest)
2541         {
2542                 int MOD_RESULT = 0;
2543                 
2544                 FOREACH_RESULT(OnUserPreNotice(user,dest,TYPE_USER,std::string(parameters[1])));
2545                 if (MOD_RESULT) {
2546                         return;
2547                 }
2548
2549                 if (!strcmp(dest->server,user->server))
2550                 {
2551                         // direct write, same server
2552                         WriteTo(user, dest, "NOTICE %s :%s", dest->nick, parameters[1]);
2553                 }
2554                 else
2555                 {
2556                         char buffer[MAXBUF];
2557                         snprintf(buffer,MAXBUF,"V %s %s :%s",user->nick,dest->nick,parameters[1]);
2558                         NetSendToOne(dest->server,buffer);
2559                 }
2560         }
2561         else
2562         {
2563                 /* no such nick/channel */
2564                 WriteServ(user->fd,"401 %s %s :No suck nick/channel",user->nick, parameters[0]);
2565         }
2566 }
2567
2568
2569 void handle_info(char **parameters, int pcnt, userrec *user)
2570 {
2571         WriteServ(user->fd,"371 %s :The Inspire IRCd Project Has been brought to you by the following people..",user->nick);
2572         WriteServ(user->fd,"371 %s :Craig Edwards, Craig McLure, and Others..",user->nick);
2573         WriteServ(user->fd,"371 %s :Will finish this later when i can be arsed :p",user->nick);
2574         FOREACH_MOD OnInfo(user);
2575         WriteServ(user->fd,"374 %s :End of /INFO list",user->nick);
2576 }
2577
2578 void handle_time(char **parameters, int pcnt, userrec *user)
2579 {
2580         time_t rawtime;
2581         struct tm * timeinfo;
2582
2583         time ( &rawtime );
2584         timeinfo = localtime ( &rawtime );
2585         WriteServ(user->fd,"391 %s %s :%s",user->nick,ServerName, asctime (timeinfo) );
2586   
2587 }
2588
2589 void handle_whois(char **parameters, int pcnt, userrec *user)
2590 {
2591         userrec *dest;
2592         char *t;
2593
2594         if (loop_call(handle_whois,parameters,pcnt,user,0,pcnt-1,0))
2595                 return;
2596         dest = Find(parameters[0]);
2597         if (dest)
2598         {
2599                 // bug found by phidjit - were able to whois an incomplete connection if it had sent a NICK or USER
2600                 if (dest->registered == 7)
2601                 {
2602                         WriteServ(user->fd,"311 %s %s %s %s * :%s",user->nick, dest->nick, dest->ident, dest->dhost, dest->fullname);
2603                         if ((user == dest) || (strchr(user->modes,'o')))
2604                         {
2605                                 WriteServ(user->fd,"378 %s %s :is connecting from *@%s",user->nick, dest->nick, dest->host);
2606                         }
2607                         if (strcmp(chlist(dest),""))
2608                         {
2609                                 WriteServ(user->fd,"319 %s %s :%s",user->nick, dest->nick, chlist(dest));
2610                         }
2611                         WriteServ(user->fd,"312 %s %s %s :%s",user->nick, dest->nick, dest->server, GetServerDescription(dest->server).c_str());
2612                         if (strcmp(dest->awaymsg,""))
2613                         {
2614                                 WriteServ(user->fd,"301 %s %s :%s",user->nick, dest->nick, dest->awaymsg);
2615                         }
2616                         if (strchr(dest->modes,'o'))
2617                         {
2618                                 WriteServ(user->fd,"313 %s %s :is an IRC operator",user->nick, dest->nick);
2619                         }
2620                         FOREACH_MOD OnWhois(user,dest);
2621                         if (!strcasecmp(user->server,dest->server))
2622                         {
2623                                 // idle time and signon line can only be sent if youre on the same server (according to RFC)
2624                                 WriteServ(user->fd,"317 %s %s %d %d :seconds idle, signon time",user->nick, dest->nick, abs((dest->idle_lastmsg)-time(NULL)), dest->signon);
2625                         }
2626                         
2627                         WriteServ(user->fd,"318 %s %s :End of /WHOIS list.",user->nick, dest->nick);
2628                 }
2629                 else
2630                 {
2631                         WriteServ(user->fd,"401 %s %s :No suck nick/channel",user->nick, parameters[0]);
2632                 }
2633         }
2634         else
2635         {
2636                 /* no such nick/channel */
2637                 WriteServ(user->fd,"401 %s %s :No suck nick/channel",user->nick, parameters[0]);
2638         }
2639 }
2640
2641 void handle_quit(char **parameters, int pcnt, userrec *user)
2642 {
2643         user_hash::iterator iter = clientlist.find(user->nick);
2644         char* reason;
2645
2646         if (user->registered == 7)
2647         {
2648                 /* theres more to do here, but for now just close the socket */
2649                 if (pcnt == 1)
2650                 {
2651                         if (parameters[0][0] == ':')
2652                         {
2653                                 *parameters[0]++;
2654                         }
2655                         reason = parameters[0];
2656
2657                         if (strlen(reason)>MAXQUIT)
2658                         {
2659                                 reason[MAXQUIT-1] = '\0';
2660                         }
2661
2662                         Write(user->fd,"ERROR :Closing link (%s@%s) [%s]",user->ident,user->host,parameters[0]);
2663                         WriteOpers("*** Client exiting: %s!%s@%s [%s]",user->nick,user->ident,user->host,parameters[0]);
2664                         WriteCommonExcept(user,"QUIT :%s%s",PrefixQuit,parameters[0]);
2665
2666                         char buffer[MAXBUF];
2667                         snprintf(buffer,MAXBUF,"Q %s :%s%s",user->nick,PrefixQuit,parameters[0]);
2668                         NetSendToAll(buffer);
2669                 }
2670                 else
2671                 {
2672                         Write(user->fd,"ERROR :Closing link (%s@%s) [QUIT]",user->ident,user->host);
2673                         WriteOpers("*** Client exiting: %s!%s@%s [Client exited]",user->nick,user->ident,user->host);
2674                         WriteCommonExcept(user,"QUIT :Client exited");
2675
2676                         char buffer[MAXBUF];
2677                         snprintf(buffer,MAXBUF,"Q %s :Client exited",user->nick);
2678                         NetSendToAll(buffer);
2679                 }
2680                 FOREACH_MOD OnUserQuit(user);
2681                 AddWhoWas(user);
2682         }
2683
2684         /* push the socket on a stack of sockets due to be closed at the next opportunity */
2685         fd_reap.push_back(user->fd);
2686         
2687         if (iter != clientlist.end())
2688         {
2689                 clientlist.erase(iter);
2690                 log(DEBUG,"deleting user hash value %d",iter->second);
2691                 //if ((user) && (user->registered == 7)) {
2692                         //delete user;
2693                 //}
2694         }
2695
2696         if (user->registered == 7) {
2697                 purge_empty_chans();
2698         }
2699 }
2700
2701 void handle_who(char **parameters, int pcnt, userrec *user)
2702 {
2703         chanrec* Ptr = NULL;
2704         
2705         /* theres more to do here, but for now just close the socket */
2706         if (pcnt == 1)
2707         {
2708                 if ((!strcmp(parameters[0],"0")) || (!strcmp(parameters[0],"*")))
2709                 {
2710                         if (user->chans[0].channel)
2711                         {
2712                                 Ptr = user->chans[0].channel;
2713                                 for (user_hash::const_iterator i = clientlist.begin(); i != clientlist.end(); i++)
2714                                 {
2715                                         if ((common_channels(user,i->second)) && (isnick(i->second->nick)))
2716                                         {
2717                                                 WriteServ(user->fd,"352 %s %s %s %s %s %s Hr@ :0 %s",user->nick, Ptr->name, i->second->ident, i->second->dhost, i->second->server, i->second->nick, i->second->fullname);
2718                                         }
2719                                 }
2720                         }
2721                         if (Ptr)
2722                         {
2723                                 WriteServ(user->fd,"315 %s %s :End of /WHO list.",user->nick, Ptr->name);
2724                         }
2725                         else
2726                         {
2727                                 WriteServ(user->fd,"315 %s %s :End of /WHO list.",user->nick, user->nick);
2728                         }
2729                         return;
2730                 }
2731                 if (parameters[0][0] == '#')
2732                 {
2733                         Ptr = FindChan(parameters[0]);
2734                         if (Ptr)
2735                         {
2736                                 for (user_hash::const_iterator i = clientlist.begin(); i != clientlist.end(); i++)
2737                                 {
2738                                         if ((has_channel(i->second,Ptr)) && (isnick(i->second->nick)))
2739                                         {
2740                                                 WriteServ(user->fd,"352 %s %s %s %s %s %s Hr@ :0 %s",user->nick, Ptr->name, i->second->ident, i->second->dhost, i->second->server, i->second->nick, i->second->fullname);
2741                                         }
2742                                 }
2743                                 WriteServ(user->fd,"315 %s %s :End of /WHO list.",user->nick, Ptr->name);
2744                         }
2745                         else
2746                         {
2747                                 WriteServ(user->fd,"401 %s %s :No suck nick/channel",user->nick, parameters[0]);
2748                         }
2749                 }
2750                 else
2751                 {
2752                         userrec* u = Find(parameters[0]);
2753                         WriteServ(user->fd,"352 %s %s %s %s %s %s Hr@ :0 %s",user->nick, u->nick, u->ident, u->dhost, u->server, u->nick, u->fullname);
2754                 }
2755         }
2756         if (pcnt == 2)
2757         {
2758                 if ((!strcmp(parameters[0],"0")) || (!strcmp(parameters[0],"*")) && (!strcmp(parameters[1],"o")))
2759                 {
2760                         Ptr = user->chans[0].channel;
2761                         for (user_hash::const_iterator i = clientlist.begin(); i != clientlist.end(); i++)
2762                         {
2763                                 if ((common_channels(user,i->second)) && (isnick(i->second->nick)))
2764                                 {
2765                                         if (strchr(i->second->modes,'o'))
2766                                         {
2767                                                 WriteServ(user->fd,"352 %s %s %s %s %s %s Hr@ :0 %s",user->nick, Ptr->name, i->second->ident, i->second->dhost, i->second->server, i->second->nick, i->second->fullname);
2768                                         }
2769                                 }
2770                         }
2771                         WriteServ(user->fd,"315 %s %s :End of /WHO list.",user->nick, Ptr->name);
2772                         return;
2773                 }
2774         }
2775 }
2776
2777 void handle_wallops(char **parameters, int pcnt, userrec *user)
2778 {
2779         WriteWallOps(user,false,"%s",parameters[0]);
2780 }
2781
2782 void handle_list(char **parameters, int pcnt, userrec *user)
2783 {
2784         chanrec* Ptr;
2785         
2786         WriteServ(user->fd,"321 %s Channel :Users Name",user->nick);
2787         for (chan_hash::const_iterator i = chanlist.begin(); i != chanlist.end(); i++)
2788         {
2789                 if ((!i->second->c_private) && (!i->second->secret))
2790                 {
2791                         WriteServ(user->fd,"322 %s %s %d :[+%s] %s",user->nick,i->second->name,usercount_i(i->second),chanmodes(i->second),i->second->topic);
2792                 }
2793         }
2794         WriteServ(user->fd,"323 %s :End of channel list.",user->nick);
2795 }
2796
2797
2798 void handle_rehash(char **parameters, int pcnt, userrec *user)
2799 {
2800         WriteServ(user->fd,"382 %s %s :Rehashing",user->nick,CleanFilename(CONFIG_FILE));
2801         ReadConfig();
2802         FOREACH_MOD OnRehash();
2803         WriteOpers("%s is rehashing config file %s",user->nick,CleanFilename(CONFIG_FILE));
2804 }
2805
2806
2807 int usercnt(void)
2808 {
2809         return clientlist.size();
2810 }
2811
2812
2813 int usercount_invisible(void)
2814 {
2815         int c = 0;
2816
2817         for (user_hash::const_iterator i = clientlist.begin(); i != clientlist.end(); i++)
2818         {
2819                 if ((i->second->fd) && (isnick(i->second->nick)) && (strchr(i->second->modes,'i'))) c++;
2820         }
2821         return c;
2822 }
2823
2824 int usercount_opers(void)
2825 {
2826         int c = 0;
2827
2828         for (user_hash::const_iterator i = clientlist.begin(); i != clientlist.end(); i++)
2829         {
2830                 if ((i->second->fd) && (isnick(i->second->nick)) && (strchr(i->second->modes,'o'))) c++;
2831         }
2832         return c;
2833 }
2834
2835 int usercount_unknown(void)
2836 {
2837         int c = 0;
2838
2839         for (user_hash::const_iterator i = clientlist.begin(); i != clientlist.end(); i++)
2840         {
2841                 if ((i->second->fd) && (i->second->registered != 7))
2842                         c++;
2843         }
2844         return c;
2845 }
2846
2847 long chancount(void)
2848 {
2849         return chanlist.size();
2850 }
2851
2852 long count_servs(void)
2853 {
2854         int c = 0;
2855         //for (int j = 0; j < 255; j++)
2856         //{
2857         //      if (servers[j] != NULL)
2858         //              c++;
2859         //}
2860         return c;
2861 }
2862
2863 long servercount(void)
2864 {
2865         return count_servs()+1;
2866 }
2867
2868 long local_count()
2869 {
2870         int c = 0;
2871         for (user_hash::const_iterator i = clientlist.begin(); i != clientlist.end(); i++)
2872         {
2873                 if ((i->second->fd) && (isnick(i->second->nick)) && (!strcasecmp(i->second->server,ServerName))) c++;
2874         }
2875         return c;
2876 }
2877
2878
2879 void handle_lusers(char **parameters, int pcnt, userrec *user)
2880 {
2881         WriteServ(user->fd,"251 %s :There are %d users and %d invisible on %d servers",user->nick,usercnt()-usercount_invisible(),usercount_invisible(),servercount());
2882         WriteServ(user->fd,"252 %s %d :operator(s) online",user->nick,usercount_opers());
2883         WriteServ(user->fd,"253 %s %d :unknown connections",user->nick,usercount_unknown());
2884         WriteServ(user->fd,"254 %s %d :channels formed",user->nick,chancount());
2885         WriteServ(user->fd,"254 %s :I have %d clients and %d servers",user->nick,local_count(),count_servs());
2886 }
2887
2888 void handle_admin(char **parameters, int pcnt, userrec *user)
2889 {
2890         WriteServ(user->fd,"256 %s :Administrative info for %s",user->nick,ServerName);
2891         WriteServ(user->fd,"257 %s :Name     - %s",user->nick,AdminName);
2892         WriteServ(user->fd,"258 %s :Nickname - %s",user->nick,AdminNick);
2893         WriteServ(user->fd,"258 %s :E-Mail   - %s",user->nick,AdminEmail);
2894 }
2895
2896 void ShowMOTD(userrec *user)
2897 {
2898         if (!MOTD.size())
2899         {
2900                 WriteServ(user->fd,"422 %s :Message of the day file is missing.",user->nick);
2901                 return;
2902         }
2903         WriteServ(user->fd,"375 %s :- %s message of the day",user->nick,ServerName);
2904         for (int i = 0; i != MOTD.size(); i++)
2905         {
2906                                 WriteServ(user->fd,"372 %s :- %s",user->nick,MOTD[i].c_str());
2907         }
2908         WriteServ(user->fd,"376 %s :End of %s message of the day.",user->nick,ServerName);
2909 }
2910
2911 void ShowRULES(userrec *user)
2912 {
2913         if (!RULES.size())
2914         {
2915                 WriteServ(user->fd,"NOTICE %s :Rules file is missing.",user->nick);
2916                 return;
2917         }
2918         WriteServ(user->fd,"NOTICE %s :%s rules",user->nick,ServerName);
2919         for (int i = 0; i != RULES.size(); i++)
2920         {
2921                                 WriteServ(user->fd,"NOTICE %s :%s",user->nick,RULES[i].c_str());
2922         }
2923         WriteServ(user->fd,"NOTICE %s :End of %s rules.",user->nick,ServerName);
2924 }
2925
2926 /* shows the message of the day, and any other on-logon stuff */
2927 void ConnectUser(userrec *user)
2928 {
2929         user->registered = 7;
2930         user->idle_lastmsg = time(NULL);
2931         log(DEBUG,"ConnectUser: %s",user->nick);
2932
2933         if (strcmp(Passwd(user),"") && (!user->haspassed))
2934         {
2935                 kill_link(user,"Invalid password");
2936                 return;
2937         }
2938         if (IsDenied(user))
2939         {
2940                 kill_link(user,"Unauthorised connection");
2941                 return;
2942         }
2943
2944         WriteServ(user->fd,"NOTICE Auth :Welcome to \002%s\002!",Network);
2945         WriteServ(user->fd,"001 %s :Welcome to the %s IRC Network %s!%s@%s",user->nick,Network,user->nick,user->ident,user->host);
2946         WriteServ(user->fd,"002 %s :Your host is %s, running version %s",user->nick,ServerName,VERSION);
2947         WriteServ(user->fd,"003 %s :This server was created %s %s",user->nick,__TIME__,__DATE__);
2948         WriteServ(user->fd,"004 %s :%s %s iowghraAsORVSxNCWqBzvdHtGI lvhopsmntikrRcaqOALQbSeKVfHGCuzN",user->nick,ServerName,VERSION);
2949         WriteServ(user->fd,"005 %s :MAP KNOCK SAFELIST HCN MAXCHANNELS=20 MAXBANS=60 NICKLEN=30 TOPICLEN=307 KICKLEN=307 MAXTARGETS=20 AWAYLEN=307 :are supported by this server",user->nick);
2950         WriteServ(user->fd,"005 %s :WALLCHOPS WATCH=128 SILENCE=5 MODES=13 CHANTYPES=# PREFIX=(ohv)@%c+ CHANMODES=ohvbeqa,kfL,l,psmntirRcOAQKVHGCuzN NETWORK=%s :are supported by this server",user->nick,'%',Network);
2951         ShowMOTD(user);
2952         FOREACH_MOD OnUserConnect(user);
2953         WriteOpers("*** Client connecting on port %d: %s!%s@%s",user->port,user->nick,user->ident,user->host);
2954         
2955         char buffer[MAXBUF];
2956         snprintf(buffer,MAXBUF,"N %d %s %s %s %s +%s %s :%s",user->age,user->nick,user->host,user->dhost,user->ident,user->modes,ServerName,user->fullname);
2957         NetSendToAll(buffer);
2958 }
2959
2960 void handle_version(char **parameters, int pcnt, userrec *user)
2961 {
2962         char Revision[] = "$Revision$";
2963
2964         char *s1 = Revision;
2965         char *savept;
2966         char *v1 = strtok_r(s1," ",&savept);
2967         s1 = savept;
2968         char *v2 = strtok_r(s1," ",&savept);
2969         s1 = savept;
2970         
2971         WriteServ(user->fd,"351 %s :%s Rev. %s %s :%s (O=%d)",user->nick,VERSION,v2,ServerName,SYSTEM,OPTIMISATION);
2972 }
2973
2974 void handle_ping(char **parameters, int pcnt, userrec *user)
2975 {
2976         WriteServ(user->fd,"PONG %s :%s",ServerName,parameters[0]);
2977 }
2978
2979 void handle_pong(char **parameters, int pcnt, userrec *user)
2980 {
2981         // set the user as alive so they survive to next ping
2982         user->lastping = 1;
2983 }
2984
2985 void handle_motd(char **parameters, int pcnt, userrec *user)
2986 {
2987         ShowMOTD(user);
2988 }
2989
2990 void handle_rules(char **parameters, int pcnt, userrec *user)
2991 {
2992         ShowRULES(user);
2993 }
2994
2995 void handle_user(char **parameters, int pcnt, userrec *user)
2996 {
2997         if (user->registered < 3)
2998         {
2999                 if (isident(parameters[0]) == 0) {
3000                         // This kinda Sucks, According to the RFC thou, its either this,
3001                         // or "You have already registered" :p -- Craig
3002                         WriteServ(user->fd,"461 %s USER :Not enough parameters",user->nick);
3003                 }
3004                 else {
3005                         WriteServ(user->fd,"NOTICE Auth :No ident response, ident prefixed with ~");
3006                         strcpy(user->ident,"~"); /* we arent checking ident... but these days why bother anyway? */
3007                         strncat(user->ident,parameters[0],IDENTMAX);
3008                         strncpy(user->fullname,parameters[3],128);
3009                         user->registered = (user->registered | 1);
3010                 }
3011         }
3012         else
3013         {
3014                 WriteServ(user->fd,"462 %s :You may not reregister",user->nick);
3015                 return;
3016         }
3017         /* parameters 2 and 3 are local and remote hosts, ignored when sent by client connection */
3018         if (user->registered == 3)
3019         {
3020                 /* user is registered now, bit 0 = USER command, bit 1 = sent a NICK command */
3021                 ConnectUser(user);
3022         }
3023 }
3024
3025 void handle_userhost(char **parameters, int pcnt, userrec *user)
3026 {
3027         char Return[MAXBUF],junk[MAXBUF];
3028         sprintf(Return,"302 %s :",user->nick);
3029         for (int i = 0; i < pcnt; i++)
3030         {
3031                 userrec *u = Find(parameters[i]);
3032                 if (u)
3033                 {
3034                         if (strchr(u->modes,'o'))
3035                         {
3036                                 sprintf(junk,"%s*=+%s@%s ",u->nick,u->ident,u->host);
3037                                 strcat(Return,junk);
3038                         }
3039                         else
3040                         {
3041                                 sprintf(junk,"%s=+%s@%s ",u->nick,u->ident,u->host);
3042                                 strcat(Return,junk);
3043                         }
3044                 }
3045         }
3046         WriteServ(user->fd,Return);
3047 }
3048
3049
3050 void handle_ison(char **parameters, int pcnt, userrec *user)
3051 {
3052         char Return[MAXBUF];
3053         sprintf(Return,"303 %s :",user->nick);
3054         for (int i = 0; i < pcnt; i++)
3055         {
3056                 userrec *u = Find(parameters[i]);
3057                 if (u)
3058                 {
3059                         strcat(Return,u->nick);
3060                         strcat(Return," ");
3061                 }
3062         }
3063         WriteServ(user->fd,Return);
3064 }
3065
3066
3067 void handle_away(char **parameters, int pcnt, userrec *user)
3068 {
3069         if (pcnt)
3070         {
3071                 strcpy(user->awaymsg,parameters[0]);
3072                 WriteServ(user->fd,"306 %s :You have been marked as being away",user->nick);
3073         }
3074         else
3075         {
3076                 strcpy(user->awaymsg,"");
3077                 WriteServ(user->fd,"305 %s :You are no longer marked as being away",user->nick);
3078         }
3079 }
3080
3081 void handle_whowas(char **parameters, int pcnt, userrec* user)
3082 {
3083         user_hash::iterator i = whowas.find(parameters[0]);
3084
3085         if (i == whowas.end())
3086         {
3087                 WriteServ(user->fd,"406 %s %s :There was no such nickname",user->nick,parameters[0]);
3088                 WriteServ(user->fd,"369 %s %s :End of WHOWAS",user->nick,parameters[0]);
3089         }
3090         else
3091         {
3092                 time_t rawtime = i->second->signon;
3093                 tm *timeinfo;
3094                 char b[MAXBUF];
3095                 
3096                 timeinfo = localtime(&rawtime);
3097                 strcpy(b,asctime(timeinfo));
3098                 b[strlen(b)-1] = '\0';
3099                 
3100                 WriteServ(user->fd,"314 %s %s %s %s * :%s",user->nick,i->second->nick,i->second->ident,i->second->dhost,i->second->fullname);
3101                 WriteServ(user->fd,"312 %s %s %s :%s",user->nick,i->second->nick,i->second->server,b);
3102                 WriteServ(user->fd,"369 %s %s :End of WHOWAS",user->nick,parameters[0]);
3103         }
3104
3105 }
3106
3107 void handle_trace(char **parameters, int pcnt, userrec *user)
3108 {
3109         for (user_hash::iterator i = clientlist.begin(); i != clientlist.end(); i++)
3110         {
3111                 if (i->second)
3112                 {
3113                         if (isnick(i->second->nick))
3114                         {
3115                                 if (strchr(i->second->modes,'o'))
3116                                 {
3117                                         WriteServ(user->fd,"205 %s :Oper 0 %s",user->nick,i->second->nick);
3118                                 }
3119                                 else
3120                                 {
3121                                         WriteServ(user->fd,"204 %s :User 0 %s",user->nick,i->second->nick);
3122                                 }
3123                         }
3124                         else
3125                         {
3126                                 WriteServ(user->fd,"203 %s :???? 0 [%s]",user->nick,i->second->host);
3127                         }
3128                 }
3129         }
3130 }
3131
3132 void handle_modules(char **parameters, int pcnt, userrec *user)
3133 {
3134         for (int i = 0; i < module_names.size(); i++)
3135         {
3136                         Version V = modules[i]->GetVersion();
3137                         char modulename[MAXBUF];
3138                         strncpy(modulename,module_names[i].c_str(),256);
3139                         WriteServ(user->fd,"900 %s :0x%08lx %d.%d.%d.%d %s",user->nick,modules[i],V.Major,V.Minor,V.Revision,V.Build,CleanFilename(modulename));
3140         }
3141 }
3142
3143 // calls a handler function for a command
3144
3145 void call_handler(const char* commandname,char **parameters, int pcnt, userrec *user)
3146 {
3147                 for (int i = 0; i < cmdlist.size(); i++)
3148                 {
3149                         if (!strcasecmp(cmdlist[i].command,commandname))
3150                         {
3151                                 if (cmdlist[i].handler_function)
3152                                 {
3153                                         if (pcnt>=cmdlist[i].min_params)
3154                                         {
3155                                                 if (strchr(user->modes,cmdlist[i].flags_needed))
3156                                                 {
3157                                                         cmdlist[i].handler_function(parameters,pcnt,user);
3158                                                 }
3159                                         }
3160                                 }
3161                         }
3162                 }
3163 }
3164
3165 void handle_stats(char **parameters, int pcnt, userrec *user)
3166 {
3167         if (pcnt != 1)
3168         {
3169                 return;
3170         }
3171         if (strlen(parameters[0])>1)
3172         {
3173                 /* make the stats query 1 character long */
3174                 parameters[0][1] = '\0';
3175         }
3176
3177         /* stats m (list number of times each command has been used, plus bytecount) */
3178         if (!strcasecmp(parameters[0],"m"))
3179         {
3180                 for (int i = 0; i < cmdlist.size(); i++)
3181                 {
3182                         if (cmdlist[i].handler_function)
3183                         {
3184                                 if (cmdlist[i].use_count)
3185                                 {
3186                                         /* RPL_STATSCOMMANDS */
3187                                         WriteServ(user->fd,"212 %s %s %d %d",user->nick,cmdlist[i].command,cmdlist[i].use_count,cmdlist[i].total_bytes);
3188                                 }
3189                         }
3190                 }
3191                         
3192         }
3193
3194         /* stats z (debug and memory info) */
3195         if (!strcasecmp(parameters[0],"z"))
3196         {
3197                 WriteServ(user->fd,"249 %s :Users(HASH_MAP) %d (%d bytes, %d buckets)",user->nick,clientlist.size(),clientlist.size()*sizeof(userrec),clientlist.bucket_count());
3198                 WriteServ(user->fd,"249 %s :Channels(HASH_MAP) %d (%d bytes, %d buckets)",user->nick,chanlist.size(),chanlist.size()*sizeof(chanrec),chanlist.bucket_count());
3199                 WriteServ(user->fd,"249 %s :Commands(VECTOR) %d (%d bytes)",user->nick,cmdlist.size(),cmdlist.size()*sizeof(command_t));
3200                 WriteServ(user->fd,"249 %s :MOTD(VECTOR) %d, RULES(VECTOR) %d",user->nick,MOTD.size(),RULES.size());
3201                 WriteServ(user->fd,"249 %s :address_cache(HASH_MAP) %d (%d buckets)",user->nick,IP.size(),IP.bucket_count());
3202                 WriteServ(user->fd,"249 %s :Modules(VECTOR) %d (%d)",user->nick,modules.size(),modules.size()*sizeof(Module));
3203                 WriteServ(user->fd,"249 %s :ClassFactories(VECTOR) %d (%d)",user->nick,factory.size(),factory.size()*sizeof(ircd_module));
3204                 WriteServ(user->fd,"249 %s :Ports(STATIC_ARRAY) %d",user->nick,boundPortCount);
3205         }
3206         
3207         /* stats o */
3208         if (!strcasecmp(parameters[0],"o"))
3209         {
3210                 for (int i = 0; i < ConfValueEnum("oper",&config_f); i++)
3211                 {
3212                         char LoginName[MAXBUF];
3213                         char HostName[MAXBUF];
3214                         char OperType[MAXBUF];
3215                         ConfValue("oper","name",i,LoginName,&config_f);
3216                         ConfValue("oper","host",i,HostName,&config_f);
3217                         ConfValue("oper","type",i,OperType,&config_f);
3218                         WriteServ(user->fd,"243 %s O %s * %s %s 0",user->nick,HostName,LoginName,OperType);
3219                 }
3220         }
3221         
3222         /* stats l (show user I/O stats) */
3223         if (!strcasecmp(parameters[0],"l"))
3224         {
3225                 WriteServ(user->fd,"211 %s :server:port nick bytes_in cmds_in bytes_out cmds_out",user->nick);
3226                 for (user_hash::iterator i = clientlist.begin(); i != clientlist.end(); i++)
3227                 {
3228                         if (isnick(i->second->nick))
3229                         {
3230                                 WriteServ(user->fd,"211 %s :%s:%d %s %d %d %d %d",user->nick,ServerName,i->second->port,i->second->nick,i->second->bytes_in,i->second->cmds_in,i->second->bytes_out,i->second->cmds_out);
3231                         }
3232                         else
3233                         {
3234                                 WriteServ(user->fd,"211 %s :%s:%d (unknown@%d) %d %d %d %d",user->nick,ServerName,i->second->port,i->second->fd,i->second->bytes_in,i->second->cmds_in,i->second->bytes_out,i->second->cmds_out);
3235                         }
3236                         
3237                 }
3238         }
3239         
3240         /* stats u (show server uptime) */
3241         if (!strcasecmp(parameters[0],"u"))
3242         {
3243                 time_t current_time = 0;
3244                 current_time = time(NULL);
3245                 time_t server_uptime = current_time - startup_time;
3246                 struct tm* stime;
3247                 stime = gmtime(&server_uptime);
3248                 /* i dont know who the hell would have an ircd running for over a year nonstop, but
3249                  * Craig suggested this, and it seemed a good idea so in it went */
3250                 if (stime->tm_year > 70)
3251                 {
3252                         WriteServ(user->fd,"242 %s :Server up %d years, %d days, %.2d:%.2d:%.2d",user->nick,(stime->tm_year-70),stime->tm_yday,stime->tm_hour,stime->tm_min,stime->tm_sec);
3253                 }
3254                 else
3255                 {
3256                         WriteServ(user->fd,"242 %s :Server up %d days, %.2d:%.2d:%.2d",user->nick,stime->tm_yday,stime->tm_hour,stime->tm_min,stime->tm_sec);
3257                 }
3258         }
3259
3260         WriteServ(user->fd,"219 %s %s :End of /STATS report",user->nick,parameters[0]);
3261         WriteOpers("*** Notice: Stats '%s' requested by %s (%s@%s)",parameters[0],user->nick,user->ident,user->host);
3262         
3263 }
3264
3265 void handle_connect(char **parameters, int pcnt, userrec *user)
3266 {
3267         char Link_ServerName[1024];
3268         char Link_IPAddr[1024];
3269         char Link_Port[1024];
3270         char Link_Pass[1024];
3271         int LinkPort;
3272         bool found = false;
3273         
3274         for (int i = 0; i < ConfValueEnum("link",&config_f); i++)
3275         {
3276                 if (!found)
3277                 {
3278                         ConfValue("link","name",i,Link_ServerName,&config_f);
3279                         ConfValue("link","ipaddr",i,Link_IPAddr,&config_f);
3280                         ConfValue("link","port",i,Link_Port,&config_f);
3281                         ConfValue("link","sendpass",i,Link_Pass,&config_f);
3282                         log(DEBUG,"(%d) Comparing against name='%s', ipaddr='%s', port='%s', recvpass='%s'",i,Link_ServerName,Link_IPAddr,Link_Port,Link_Pass);
3283                         LinkPort = atoi(Link_Port);
3284                         if (match(Link_ServerName,parameters[0])) {
3285                                 found = true;
3286                                 break;
3287                         }
3288                 }
3289         }
3290         
3291         if (!found) {
3292                 WriteServ(user->fd,"NOTICE %s :*** Failed to connect to %s: No servers matching this pattern are configured for linking.",user->nick,parameters[0]);
3293                 return;
3294         }
3295         
3296         // TODO: Perform a check here to stop a server being linked twice!
3297
3298         WriteServ(user->fd,"NOTICE %s :*** Connecting to %s (%s) port %s...",user->nick,Link_ServerName,Link_IPAddr,Link_Port);
3299
3300         if (me[defaultRoute])
3301         {
3302                 me[defaultRoute]->BeginLink(Link_IPAddr,LinkPort,Link_Pass,Link_ServerName,me[defaultRoute]->port);
3303                 return;
3304         }
3305         else
3306         {
3307                 WriteServ(user->fd,"NOTICE %s :No default route is defined for server connections on this server. You must define a server connection to be default route so that sockets can be bound to it.",user->nick);
3308         }
3309 }
3310
3311 void DoSplitEveryone()
3312 {
3313         bool go_again = true;
3314         while (go_again)
3315         {
3316                 go_again = false;
3317                 for (int i = 0; i < 32; i++)
3318                 {
3319                         if (me[i] != NULL)
3320                         {
3321                                 for (vector<ircd_connector>::iterator j = me[i]->connectors.begin(); j != me[i]->connectors.end(); j++)
3322                                 {
3323                                         if (strcasecmp(j->GetServerName().c_str(),ServerName))
3324                                         {
3325                                                 j->routes.clear();
3326                                                 j->CloseConnection();
3327                                                 me[i]->connectors.erase(j);
3328                                                 go_again = true;
3329                                                 break;
3330                                         }
3331                                 }
3332                         }
3333                 }
3334         }
3335         log(DEBUG,"Removed server. Will remove clients...");
3336         // iterate through the userlist and remove all users on this server.
3337         // because we're dealing with a mesh, we dont have to deal with anything
3338         // "down-route" from this server (nice huh)
3339         go_again = true;
3340         char reason[MAXBUF];
3341         while (go_again)
3342         {
3343                 go_again = false;
3344                 for (user_hash::const_iterator u = clientlist.begin(); u != clientlist.end(); u++)
3345                 {
3346                         if (strcasecmp(u->second->server,ServerName))
3347                         {
3348                                 snprintf(reason,MAXBUF,"%s %s",ServerName,u->second->server);
3349                                 kill_link(u->second,reason);
3350                                 go_again = true;
3351                                 break;
3352                         }
3353                 }
3354         }
3355 }
3356
3357
3358
3359 void handle_squit(char **parameters, int pcnt, userrec *user)
3360 {
3361         // send out an squit across the mesh and then clear the server list (for local squit)
3362         if (!pcnt)
3363         {
3364                 WriteOpers("SQUIT command issued by %s",user->nick);
3365                 char buffer[MAXBUF];
3366                 snprintf(buffer,MAXBUF,"& %s",ServerName);
3367                 NetSendToAll(buffer);
3368                 DoSplitEveryone();
3369         }
3370         else
3371         {
3372                 WriteServ(user->fd,"NOTICE :*** Remote SQUIT not supported yet.");
3373         }
3374 }
3375
3376 char islast(const char* s)
3377 {
3378         char c = '`';
3379         for (int j = 0; j < 32; j++)
3380         {
3381                 if (me[j] != NULL)
3382                 {
3383                         for (int k = 0; k < me[j]->connectors.size(); k++)
3384                         {
3385                                 if (strcasecmp(me[j]->connectors[k].GetServerName().c_str(),s))
3386                                 {
3387                                         c = '|';
3388                                 }
3389                                 if (!strcasecmp(me[j]->connectors[k].GetServerName().c_str(),s))
3390                                 {
3391                                         c = '`';
3392                                 }
3393                         }
3394                 }
3395         }
3396         return c;
3397 }
3398
3399 long map_count(const char* s)
3400 {
3401         int c = 0;
3402         for (user_hash::const_iterator i = clientlist.begin(); i != clientlist.end(); i++)
3403         {
3404                 if ((i->second->fd) && (isnick(i->second->nick)) && (!strcasecmp(i->second->server,s))) c++;
3405         }
3406         return c;
3407 }
3408
3409 void handle_links(char **parameters, int pcnt, userrec *user)
3410 {
3411         WriteServ(user->fd,"364 %s %s %s :0 %s",user->nick,ServerName,ServerName,ServerDesc);
3412         for (int j = 0; j < 32; j++)
3413         {
3414                 if (me[j] != NULL)
3415                 {
3416                         for (int k = 0; k < me[j]->connectors.size(); k++)
3417                         {
3418                                 WriteServ(user->fd,"364 %s %s %s :1 %s",user->nick,me[j]->connectors[k].GetServerName().c_str(),ServerName,me[j]->connectors[k].GetDescription().c_str());
3419                         }
3420                 }
3421         }
3422         WriteServ(user->fd,"365 %s * :End of /LINKS list.",user->nick);
3423 }
3424
3425 void handle_map(char **parameters, int pcnt, userrec *user)
3426 {
3427         char line[MAXBUF];
3428         snprintf(line,MAXBUF,"006 %s :%s",user->nick,ServerName);
3429         while (strlen(line) < 50)
3430                 strcat(line," ");
3431         WriteServ(user->fd,"%s%d (%.2f%%)",line,local_count(),(float)(((float)local_count()/(float)usercnt())*100));
3432         for (int j = 0; j < 32; j++)
3433         {
3434                 if (me[j] != NULL)
3435                 {
3436                         for (int k = 0; k < me[j]->connectors.size(); k++)
3437                         {
3438                                 snprintf(line,MAXBUF,"006 %s :%c-%s",user->nick,islast(me[j]->connectors[k].GetServerName().c_str()),me[j]->connectors[k].GetServerName().c_str());
3439                                 while (strlen(line) < 50)
3440                                         strcat(line," ");
3441                                 WriteServ(user->fd,"%s%d (%.2f%%)",line,map_count(me[j]->connectors[k].GetServerName().c_str()),(float)(((float)map_count(me[j]->connectors[k].GetServerName().c_str())/(float)usercnt())*100));
3442                         }
3443                 }
3444         }
3445         WriteServ(user->fd,"007 %s :End of /MAP",user->nick);
3446 }
3447
3448
3449 void handle_oper(char **parameters, int pcnt, userrec *user)
3450 {
3451         char LoginName[MAXBUF];
3452         char Password[MAXBUF];
3453         char OperType[MAXBUF];
3454         char TypeName[MAXBUF];
3455         char Hostname[MAXBUF];
3456         int i,j;
3457
3458         for (int i = 0; i < ConfValueEnum("oper",&config_f); i++)
3459         {
3460                 ConfValue("oper","name",i,LoginName,&config_f);
3461                 ConfValue("oper","password",i,Password,&config_f);
3462                 if ((!strcmp(LoginName,parameters[0])) && (!strcmp(Password,parameters[1])))
3463                 {
3464                         /* correct oper credentials */
3465                         ConfValue("oper","type",i,OperType,&config_f);
3466                         WriteOpers("*** %s (%s@%s) is now an IRC operator of type %s",user->nick,user->ident,user->host,OperType);
3467                         WriteServ(user->fd,"381 %s :You are now an IRC operator of type %s",user->nick,OperType);
3468                         WriteServ(user->fd,"MODE %s :+o",user->nick);
3469                         for (j =0; j < ConfValueEnum("type",&config_f); j++)
3470                         {
3471                                 ConfValue("type","name",j,TypeName,&config_f);
3472                                 if (!strcmp(TypeName,OperType))
3473                                 {
3474                                         /* found this oper's opertype */
3475                                         ConfValue("type","host",j,Hostname,&config_f);
3476                                         ChangeDisplayedHost(user,Hostname);
3477                                 }
3478                         }
3479                         if (!strchr(user->modes,'o'))
3480                         {
3481                                 strcat(user->modes,"o");
3482                         }
3483                         FOREACH_MOD OnOper(user);
3484                         return;
3485                 }
3486         }
3487         /* no such oper */
3488         WriteServ(user->fd,"491 %s :Invalid oper credentials",user->nick);
3489         WriteOpers("*** WARNING! Failed oper attempt by %s!%s@%s!",user->nick,user->ident,user->host);
3490 }
3491
3492 void handle_nick(char **parameters, int pcnt, userrec *user)
3493 {
3494         if (pcnt < 1) 
3495         {
3496                 log(DEBUG,"not enough params for handle_nick");
3497                 return;
3498         }
3499         if (!parameters[0])
3500         {
3501                 log(DEBUG,"invalid parameter passed to handle_nick");
3502                 return;
3503         }
3504         if (!strlen(parameters[0]))
3505         {
3506                 log(DEBUG,"zero length new nick passed to handle_nick");
3507                 return;
3508         }
3509         if (!user)
3510         {
3511                 log(DEBUG,"invalid user passed to handle_nick");
3512                 return;
3513         }
3514         if (!user->nick)
3515         {
3516                 log(DEBUG,"invalid old nick passed to handle_nick");
3517                 return;
3518         }
3519         if (!strcasecmp(user->nick,parameters[0]))
3520         {
3521                 log(DEBUG,"old nick is new nick, skipping");
3522                 return;
3523         }
3524         else
3525         {
3526                 if (strlen(parameters[0]) > 1)
3527                 {
3528                         if (parameters[0][0] == ':')
3529                         {
3530                                 *parameters[0]++;
3531                         }
3532                 }
3533                 if ((Find(parameters[0])) && (Find(parameters[0]) != user))
3534                 {
3535                         WriteServ(user->fd,"433 %s %s :Nickname is already in use.",user->nick,parameters[0]);
3536                         return;
3537                 }
3538         }
3539         if (isnick(parameters[0]) == 0)
3540         {
3541                 WriteServ(user->fd,"432 %s %s :Erroneous Nickname",user->nick,parameters[0]);
3542                 return;
3543         }
3544
3545         if (user->registered == 7)
3546         {
3547                 WriteCommon(user,"NICK %s",parameters[0]);
3548                 
3549                 // Q token must go to ALL servers!!!
3550                 char buffer[MAXBUF];
3551                 snprintf(buffer,MAXBUF,"n %s %s",user->nick,parameters[0]);
3552                 NetSendToAll(buffer);
3553                 
3554         }
3555         
3556         /* change the nick of the user in the users_hash */
3557         user = ReHashNick(user->nick, parameters[0]);
3558         /* actually change the nick within the record */
3559         if (!user) return;
3560         if (!user->nick) return;
3561
3562         strncpy(user->nick, parameters[0],NICKMAX);
3563
3564         log(DEBUG,"new nick set: %s",user->nick);
3565         
3566         if (user->registered < 3)
3567                 user->registered = (user->registered | 2);
3568         if (user->registered == 3)
3569         {
3570                 /* user is registered now, bit 0 = USER command, bit 1 = sent a NICK command */
3571                 ConnectUser(user);
3572         }
3573         log(DEBUG,"exit nickchange: %s",user->nick);
3574 }
3575
3576 void force_nickchange(userrec* user,const char* newnick)
3577 {
3578         char nick[MAXBUF];
3579         strcpy(nick,"");
3580         
3581         if (user)
3582         {
3583                 if (newnick)
3584                 {
3585                         strncpy(nick,newnick,MAXBUF);
3586                 }
3587                 if (user->registered == 7)
3588                 {
3589                         char* pars[1];
3590                         pars[0] = nick;
3591                         handle_nick(pars,1,user);
3592                 }
3593         }
3594 }
3595                                 
3596
3597 int process_parameters(char **command_p,char *parameters)
3598 {
3599         int i = 0;
3600         int j = 0;
3601         int q = 0;
3602         q = strlen(parameters);
3603         if (!q)
3604         {
3605                 /* no parameters, command_p invalid! */
3606                 return 0;
3607         }
3608         if (parameters[0] == ':')
3609         {
3610                 command_p[0] = parameters+1;
3611                 return 1;
3612         }
3613         if (q)
3614         {
3615                 if ((strchr(parameters,' ')==NULL) || (parameters[0] == ':'))
3616                 {
3617                         /* only one parameter */
3618                         command_p[0] = parameters;
3619                         if (parameters[0] == ':')
3620                         {
3621                                 if (strchr(parameters,' ') != NULL)
3622                                 {
3623                                         command_p[0]++;
3624                                 }
3625                         }
3626                         return 1;
3627                 }
3628         }
3629         command_p[j++] = parameters;
3630         for (int i = 0; i <= q; i++)
3631         {
3632                 if (parameters[i] == ' ')
3633                 {
3634                         command_p[j++] = parameters+i+1;
3635                         parameters[i] = '\0';
3636                         if (command_p[j-1][0] == ':')
3637                         {
3638                                 *command_p[j-1]++; /* remove dodgy ":" */
3639                                 break;
3640                                 /* parameter like this marks end of the sequence */
3641                         }
3642                 }
3643         }
3644         return j; /* returns total number of items in the list */
3645 }
3646
3647 void process_command(userrec *user, char* cmd)
3648 {
3649         char *parameters;
3650         char *command;
3651         char *command_p[127];
3652         char p[MAXBUF], temp[MAXBUF];
3653         int i, j, items, cmd_found;
3654
3655         for (int i = 0; i < 127; i++)
3656                 command_p[i] = NULL;
3657
3658         if (!user)
3659         {
3660                 return;
3661         }
3662         if (!cmd)
3663         {
3664                 return;
3665         }
3666         if (!strcmp(cmd,""))
3667         {
3668                 return;
3669         }
3670         
3671         int total_params = 0;
3672         if (strlen(cmd)>2)
3673         {
3674                 for (int q = 0; q < strlen(cmd)-1; q++)
3675                 {
3676                         if ((cmd[q] == ' ') && (cmd[q+1] == ':'))
3677                         {
3678                                 total_params++;
3679                                 // found a 'trailing', we dont count them after this.
3680                                 break;
3681                         }
3682                         if (cmd[q] == ' ')
3683                                 total_params++;
3684                 }
3685         }
3686         
3687         // another phidjit bug...
3688         if (total_params > 126)
3689         {
3690                 kill_link(user,"Protocol violation (1)");
3691                 return;
3692         }
3693         
3694         strcpy(temp,cmd);
3695
3696         std::string tmp = cmd;
3697         for (int i = 0; i <= MODCOUNT; i++)
3698         {
3699                 std::string oldtmp = tmp;
3700                 modules[i]->OnServerRaw(tmp,true);
3701                 if (oldtmp != tmp)
3702                 {
3703                         log(DEBUG,"A Module changed the input string!");
3704                         log(DEBUG,"New string: %s",tmp.c_str());
3705                         log(DEBUG,"Old string: %s",oldtmp.c_str());
3706                         break;
3707                 }
3708         }
3709         strncpy(cmd,tmp.c_str(),MAXBUF);
3710         strcpy(temp,cmd);
3711
3712         if (!strchr(cmd,' '))
3713         {
3714                 /* no parameters, lets skip the formalities and not chop up
3715                  * the string */
3716                 log(DEBUG,"About to preprocess command with no params");
3717                 items = 0;
3718                 command_p[0] = NULL;
3719                 parameters = NULL;
3720                 for (int i = 0; i <= strlen(cmd); i++)
3721                 {
3722                         cmd[i] = toupper(cmd[i]);
3723                 }
3724                 log(DEBUG,"Preprocess done length=%d",strlen(cmd));
3725                 command = cmd;
3726         }
3727         else
3728         {
3729                 strcpy(cmd,"");
3730                 j = 0;
3731                 /* strip out extraneous linefeeds through mirc's crappy pasting (thanks Craig) */
3732                 for (int i = 0; i < strlen(temp); i++)
3733                 {
3734                         if ((temp[i] != 10) && (temp[i] != 13) && (temp[i] != 0) && (temp[i] != 7))
3735                         {
3736                                 cmd[j++] = temp[i];
3737                                 cmd[j] = 0;
3738                         }
3739                 }
3740                 /* split the full string into a command plus parameters */
3741                 parameters = p;
3742                 strcpy(p," ");
3743                 command = cmd;
3744                 if (strchr(cmd,' '))
3745                 {
3746                         for (int i = 0; i <= strlen(cmd); i++)
3747                         {
3748                                 /* capitalise the command ONLY, leave params intact */
3749                                 cmd[i] = toupper(cmd[i]);
3750                                 /* are we nearly there yet?! :P */
3751                                 if (cmd[i] == ' ')
3752                                 {
3753                                         command = cmd;
3754                                         parameters = cmd+i+1;
3755                                         cmd[i] = '\0';
3756                                         break;
3757                                 }
3758                         }
3759                 }
3760                 else
3761                 {
3762                         for (int i = 0; i <= strlen(cmd); i++)
3763                         {
3764                                 cmd[i] = toupper(cmd[i]);
3765                         }
3766                 }
3767
3768         }
3769         cmd_found = 0;
3770         
3771         if (strlen(command)>MAXCOMMAND)
3772         {
3773                 kill_link(user,"Protocol violation (2)");
3774                 return;
3775         }
3776         
3777         for (int x = 0; x < strlen(command); x++)
3778         {
3779                 if (((command[x] < 'A') || (command[x] > 'Z')) && (command[x] != '.'))
3780                 {
3781                         if (((command[x] < '0') || (command[x]> '9')) && (command[x] != '-'))
3782                         {
3783                                 if (!strchr("@!\"$%^&*(){}[]_-=+;:'#~,.<>/?\\|`",command[x]))
3784                                 {
3785                                         kill_link(user,"Protocol violation (3)");
3786                                         return;
3787                                 }
3788                         }
3789                 }
3790         }
3791
3792         for (int i = 0; i != cmdlist.size(); i++)
3793         {
3794                 if (strcmp(cmdlist[i].command,""))
3795                 {
3796                         if (strlen(command)>=(strlen(cmdlist[i].command))) if (!strncmp(command, cmdlist[i].command,MAXCOMMAND))
3797                         {
3798                                 log(DEBUG,"Found matching command");
3799
3800                                 if (parameters)
3801                                 {
3802                                         if (strcmp(parameters,""))
3803                                         {
3804                                                 items = process_parameters(command_p,parameters);
3805                                         }
3806                                         else
3807                                         {
3808                                                 items = 0;
3809                                                 command_p[0] = NULL;
3810                                         }
3811                                 }
3812                                 else
3813                                 {
3814                                         items = 0;
3815                                         command_p[0] = NULL;
3816                                 }
3817                                 
3818                                 if (user)
3819                                 {
3820                                         log(DEBUG,"Processing command");
3821                                         
3822                                         /* activity resets the ping pending timer */
3823                                         user->nping = time(NULL) + 120;
3824                                         if ((items) < cmdlist[i].min_params)
3825                                         {
3826                                                 log(DEBUG,"process_command: not enough parameters: %s %s",user->nick,command);
3827                                                 WriteServ(user->fd,"461 %s %s :Not enough parameters",user->nick,command);
3828                                                 return;
3829                                         }
3830                                         if ((!strchr(user->modes,cmdlist[i].flags_needed)) && (cmdlist[i].flags_needed))
3831                                         {
3832                                                 log(DEBUG,"process_command: permission denied: %s %s",user->nick,command);
3833                                                 WriteServ(user->fd,"481 %s :Permission Denied- You do not have the required operator privilages",user->nick);
3834                                                 cmd_found = 1;
3835                                                 return;
3836                                         }
3837                                         /* if the command isnt USER, PASS, or NICK, and nick is empty,
3838                                          * deny command! */
3839                                         if ((strncmp(command,"USER",4)) && (strncmp(command,"NICK",4)) && (strncmp(command,"PASS",4)))
3840                                         {
3841                                                 if ((!isnick(user->nick)) || (user->registered != 7))
3842                                                 {
3843                                                         log(DEBUG,"process_command: not registered: %s %s",user->nick,command);
3844                                                         WriteServ(user->fd,"451 %s :You have not registered",command);
3845                                                         return;
3846                                                 }
3847                                         }
3848                                         if ((user->registered == 7) || (!strcmp(command,"USER")) || (!strcmp(command,"NICK")) || (!strcmp(command,"PASS")))
3849                                         {
3850                                                 log(DEBUG,"process_command: handler: %s %s %d",user->nick,command,items);
3851                                                 if (cmdlist[i].handler_function)
3852                                                 {
3853                                                         /* ikky /stats counters */
3854                                                         if (temp)
3855                                                         {
3856                                                                 if (user)
3857                                                                 {
3858                                                                         user->bytes_in += strlen(temp);
3859                                                                         user->cmds_in++;
3860                                                                 }
3861                                                                 cmdlist[i].use_count++;
3862                                                                 cmdlist[i].total_bytes+=strlen(temp);
3863                                                         }
3864
3865                                                         /* WARNING: nothing may come after the
3866                                                          * command handler call, as the handler
3867                                                          * may free the user structure! */
3868
3869                                                         cmdlist[i].handler_function(command_p,items,user);
3870                                                 }
3871                                                 return;
3872                                         }
3873                                         else
3874                                         {
3875                                                 log(DEBUG,"process_command: not registered: %s %s",user->nick,command);
3876                                                 WriteServ(user->fd,"451 %s :You have not registered",command);
3877                                                 return;
3878                                         }
3879                                 }
3880                                 cmd_found = 1;
3881                         }
3882                 }
3883         }
3884         if ((!cmd_found) && (user))
3885         {
3886                 log(DEBUG,"process_command: not in table: %s %s",user->nick,command);
3887                 WriteServ(user->fd,"421 %s %s :Unknown command",user->nick,command);
3888         }
3889 }
3890
3891
3892 void createcommand(char* cmd, handlerfunc f, char flags, int minparams)
3893 {
3894         command_t comm;
3895         /* create the command and push it onto the table */     
3896         strcpy(comm.command,cmd);
3897         comm.handler_function = f;
3898         comm.flags_needed = flags;
3899         comm.min_params = minparams;
3900         comm.use_count = 0;
3901         comm.total_bytes = 0;
3902         cmdlist.push_back(comm);
3903         log(DEBUG,"Added command %s (%d parameters)",cmd,minparams);
3904 }
3905
3906 void SetupCommandTable(void)
3907 {
3908         createcommand("USER",handle_user,0,4);
3909         createcommand("NICK",handle_nick,0,1);
3910         createcommand("QUIT",handle_quit,0,0);
3911         createcommand("VERSION",handle_version,0,0);
3912         createcommand("PING",handle_ping,0,1);
3913         createcommand("PONG",handle_pong,0,1);
3914         createcommand("ADMIN",handle_admin,0,0);
3915         createcommand("PRIVMSG",handle_privmsg,0,2);
3916         createcommand("INFO",handle_info,0,0);
3917         createcommand("TIME",handle_time,0,0);
3918         createcommand("WHOIS",handle_whois,0,1);
3919         createcommand("WALLOPS",handle_wallops,'o',1);
3920         createcommand("NOTICE",handle_notice,0,2);
3921         createcommand("JOIN",handle_join,0,1);
3922         createcommand("NAMES",handle_names,0,1);
3923         createcommand("PART",handle_part,0,1);
3924         createcommand("KICK",handle_kick,0,2);
3925         createcommand("MODE",handle_mode,0,1);
3926         createcommand("TOPIC",handle_topic,0,1);
3927         createcommand("WHO",handle_who,0,1);
3928         createcommand("MOTD",handle_motd,0,0);
3929         createcommand("RULES",handle_rules,0,0);
3930         createcommand("OPER",handle_oper,0,2);
3931         createcommand("LIST",handle_list,0,0);
3932         createcommand("DIE",handle_die,'o',1);
3933         createcommand("RESTART",handle_restart,'o',1);
3934         createcommand("KILL",handle_kill,'o',2);
3935         createcommand("REHASH",handle_rehash,'o',0);
3936         createcommand("LUSERS",handle_lusers,0,0);
3937         createcommand("STATS",handle_stats,0,1);
3938         createcommand("USERHOST",handle_userhost,0,1);
3939         createcommand("AWAY",handle_away,0,0);
3940         createcommand("ISON",handle_ison,0,0);
3941         createcommand("SUMMON",handle_summon,0,0);
3942         createcommand("USERS",handle_users,0,0);
3943         createcommand("INVITE",handle_invite,0,2);
3944         createcommand("PASS",handle_pass,0,1);
3945         createcommand("TRACE",handle_trace,'o',0);
3946         createcommand("WHOWAS",handle_whowas,0,1);
3947         createcommand("CONNECT",handle_connect,'o',1);
3948         createcommand("SQUIT",handle_squit,'o',0);
3949         createcommand("MODULES",handle_modules,'o',0);
3950         createcommand("LINKS",handle_links,0,0);
3951         createcommand("MAP",handle_map,0,0);
3952 }
3953
3954 void process_buffer(const char* cmdbuf,userrec *user)
3955 {
3956         if (!user)
3957         {
3958                 log(DEFAULT,"*** BUG *** process_buffer was given an invalid parameter");
3959                 return;
3960         }
3961         char cmd[MAXBUF];
3962         int i;
3963         if (!cmdbuf)
3964         {
3965                 log(DEFAULT,"*** BUG *** process_buffer was given an invalid parameter");
3966                 return;
3967         }
3968         if (!strcmp(cmdbuf,""))
3969         {
3970                 return;
3971         }
3972         strncpy(cmd,cmdbuf,MAXBUF);
3973         if (!strcmp(cmd,""))
3974         {
3975                 return;
3976         }
3977         if ((cmd[strlen(cmd)-1] == 13) || (cmd[strlen(cmd)-1] == 10))
3978         {
3979                 cmd[strlen(cmd)-1] = '\0';
3980         }
3981         if ((cmd[strlen(cmd)-1] == 13) || (cmd[strlen(cmd)-1] == 10))
3982         {
3983                 cmd[strlen(cmd)-1] = '\0';
3984         }
3985         if (!strcmp(cmd,""))
3986         {
3987                 return;
3988         }
3989         log(DEBUG,"InspIRCd: processing: %s %s",user->nick,cmd);
3990         tidystring(cmd);
3991         if ((user) && (cmd))
3992         {
3993                 process_command(user,cmd);
3994         }
3995 }
3996
3997 void DoSync(serverrec* serv, char* udp_host)
3998 {
3999         char data[MAXBUF];
4000         // send start of sync marker: Y <timestamp>
4001         // at this point the ircd receiving it starts broadcasting this netburst to all ircds
4002         // except the ones its receiving it from.
4003         snprintf(data,MAXBUF,"Y %d",time(NULL));
4004         serv->SendPacket(data,udp_host);
4005         // send users and channels
4006         for (user_hash::iterator u = clientlist.begin(); u != clientlist.end(); u++)
4007         {
4008                 snprintf(data,MAXBUF,"N %d %s %s %s %s +%s %s :%s",u->second->age,u->second->nick,u->second->host,u->second->dhost,u->second->ident,u->second->modes,u->second->server,u->second->fullname);
4009                 serv->SendPacket(data,udp_host);
4010                 if (strcmp(chlist(u->second),""))
4011                 {
4012                         snprintf(data,MAXBUF,"J %s %s",u->second->nick,chlist(u->second));
4013                         serv->SendPacket(data,udp_host);
4014                 }
4015         }
4016         // send channel modes, topics etc...
4017         for (chan_hash::iterator c = chanlist.begin(); c != chanlist.end(); c++)
4018         {
4019                 snprintf(data,MAXBUF,"M %s +%s",c->second->name,chanmodes(c->second));
4020                 serv->SendPacket(data,udp_host);
4021                 if (strcmp(c->second->topic,""))
4022                 {
4023                         snprintf(data,MAXBUF,"T %d %s %s :%s",c->second->topicset,c->second->setby,c->second->name,c->second->topic);
4024                         serv->SendPacket(data,udp_host);
4025                 }
4026                 // send current banlist
4027                 
4028                 for (BanList::iterator b = c->second->bans.begin(); b != c->second->bans.end(); b++)
4029                 {
4030                         snprintf(data,MAXBUF,"M %s +b %s",b->set_time,c->second->name,b->data);
4031                         serv->SendPacket(data,udp_host);
4032                 }
4033         }
4034         // send end of sync marker: E <timestamp>
4035         snprintf(data,MAXBUF,"F %d",time(NULL));
4036         serv->SendPacket(data,udp_host);
4037         // ircd sends its serverlist after the end of sync here
4038 }
4039
4040
4041 void handle_V(char token,char* params,serverrec* source,serverrec* reply, char* udp_host)
4042 {
4043         char* src = strtok(params," ");
4044         char* dest = strtok(NULL," :");
4045         char* text = strtok(NULL,"\r\n");
4046         text++;
4047         
4048         userrec* user = Find(src);
4049         if (user)
4050         {
4051                 userrec* dst = Find(dest);
4052                 
4053                 if (dst)
4054                 {
4055                         WriteTo(user, dst, "NOTICE %s :%s", dst->nick, text);
4056                 }
4057                 else
4058                 {
4059                         chanrec* d = FindChan(dest);
4060                         if (d)
4061                         {
4062                                 ChanExceptSender(d, user, "NOTICE %s :%s", d->name, text);
4063                         }
4064                 }
4065         }
4066         
4067 }
4068
4069
4070 void handle_P(char token,char* params,serverrec* source,serverrec* reply, char* udp_host)
4071 {
4072         char* src = strtok(params," ");
4073         char* dest = strtok(NULL," :");
4074         char* text = strtok(NULL,"\r\n");
4075         text++;
4076         
4077         userrec* user = Find(src);
4078         if (user)
4079         {
4080                 userrec* dst = Find(dest);
4081                 
4082                 if (dst)
4083                 {
4084                         WriteTo(user, dst, "PRIVMSG %s :%s", dst->nick, text);
4085                 }
4086                 else
4087                 {
4088                         chanrec* d = FindChan(dest);
4089                         if (d)
4090                         {
4091                                 ChanExceptSender(d, user, "PRIVMSG %s :%s", d->name, text);
4092                         }
4093                 }
4094         }
4095         
4096 }
4097
4098 void handle_i(char token,char* params,serverrec* source,serverrec* reply, char* udp_host)
4099 {
4100         char* nick = strtok(params," ");
4101         char* from = strtok(NULL," ");
4102         char* channel = strtok(NULL," ");
4103         userrec* u = Find(nick);
4104         userrec* user = Find(from);
4105         chanrec* c = FindChan(channel);
4106         if ((c) && (u) && (user))
4107         {
4108                 u->InviteTo(c->name);
4109                 WriteFrom(u->fd,user,"INVITE %s :%s",u->nick,c->name);
4110         }
4111 }
4112
4113 void handle_t(char token,char* params,serverrec* source,serverrec* reply, char* udp_host)
4114 {
4115         char* setby = strtok(params," ");
4116         char* channel = strtok(NULL," :");
4117         char* topic = strtok(NULL,"\r\n");
4118         topic++;
4119         userrec* u = Find(setby);
4120         chanrec* c = FindChan(channel);
4121         if ((c) && (u))
4122         {
4123                 WriteChannelLocal(c,u,"TOPIC %s :%s",c->name,topic);
4124                 strncpy(c->topic,topic,MAXTOPIC);
4125                 strncpy(c->setby,u->nick,NICKMAX);
4126         }       
4127 }
4128         
4129
4130 void handle_T(char token,char* params,serverrec* source,serverrec* reply, char* udp_host)
4131 {
4132         char* tm = strtok(params," ");
4133         char* setby = strtok(NULL," ");
4134         char* channel = strtok(NULL," :");
4135         char* topic = strtok(NULL,"\r\n");
4136         topic++;
4137         time_t TS = atoi(tm);
4138         chanrec* c = FindChan(channel);
4139         if (c)
4140         {
4141                 // in the case of topics and TS, the *NEWER* 
4142                 if (TS <= c->topicset)
4143                 {
4144                         WriteChannelLocal(c,NULL,"TOPIC %s :%s",c->name,topic);
4145                         strncpy(c->topic,topic,MAXTOPIC);
4146                         strncpy(c->setby,setby,NICKMAX);
4147                 }
4148         }       
4149 }
4150         
4151 void handle_M(char token,char* params,serverrec* source,serverrec* reply, char* udp_host)
4152 {
4153         char* pars[128];
4154         char original[MAXBUF],target[MAXBUF];
4155         strncpy(original,params,MAXBUF);
4156         int index = 0;
4157         char* parameter = strtok(params," ");
4158         strncpy(target,parameter,MAXBUF);
4159         while (parameter)
4160         {
4161                 pars[index++] = parameter;
4162                 parameter = strtok(NULL," ");
4163         }
4164         merge_mode(pars,index);
4165         if (FindChan(target))
4166         {
4167                 WriteChannelLocal(FindChan(target), NULL, "MODE %s",original);
4168         }
4169         if (Find(target))
4170         {
4171                 WriteTo(NULL,Find(target),"MODE %s",original);
4172         }
4173 }
4174
4175 // m is modes set by users only (not servers) valid targets are channels or users.
4176
4177 void handle_m(char token,char* params,serverrec* source,serverrec* reply, char* udp_host)
4178 {
4179         // m blah #chatspike +b *!test@*4
4180         char* pars[128];
4181         char original[MAXBUF];
4182         strncpy(original,params,MAXBUF);
4183         
4184         if (!strchr(params,' '))
4185         {
4186                 WriteOpers("WARNING! 'm' token in data stream without any parameters! Something fishy is going on!");
4187                 return;
4188         }
4189         
4190         int index = 0;
4191         
4192         char* src = strtok(params," ");
4193         userrec* user = Find(src);
4194         
4195         if (user)
4196         {
4197                 log(DEBUG,"Found user: %s",user->nick);
4198                 char* parameter = strtok(NULL," ");
4199                 while (parameter)
4200                 {
4201                         pars[index++] = parameter;
4202                         parameter = strtok(NULL," ");
4203                 }
4204                 
4205                 log(DEBUG,"Calling merge_mode2");
4206                 merge_mode2(pars,index,user);
4207         }
4208 }
4209
4210
4211 void handle_L(char token,char* params,serverrec* source,serverrec* reply, char* udp_host)
4212 {
4213         char* nick = strtok(params," ");
4214         char* channel = strtok(NULL," :");
4215         char* reason = strtok(NULL,"\r\n");
4216         userrec* user = Find(nick);
4217         reason++;
4218         if (user)
4219         {
4220                 if (strcmp(reason,""))
4221                 {
4222                         del_channel(user,channel,reason,true);
4223                 }
4224                 else
4225                 {
4226                         del_channel(user,channel,NULL,true);
4227                 }
4228         }
4229 }
4230
4231 void handle_K(char token,char* params,serverrec* source,serverrec* reply, char* udp_host)
4232 {
4233         char* src = strtok(params," ");
4234         char* nick = strtok(NULL," :");
4235         char* reason = strtok(NULL,"\r\n");
4236         char kreason[MAXBUF];
4237         reason++;
4238
4239         userrec* u = Find(nick);
4240         userrec* user = Find(src);
4241         
4242         if ((user) && (u))
4243         {
4244                 WriteTo(user, u, "KILL %s :%s!%s!%s!%s (%s)", u->nick, source->name, ServerName, user->dhost,user->nick,reason);
4245                 WriteOpers("*** Remote kill from %s by %s: %s!%s@%s (%s)",source->name,user->nick,u->nick,u->ident,u->host,reason);
4246                 snprintf(kreason,MAXBUF,"[%s] Killed (%s (%s))",source->name,user->nick,reason);
4247                 kill_link(u,kreason);
4248         }
4249 }
4250
4251 void handle_Q(char token,char* params,serverrec* source,serverrec* reply, char* udp_host)
4252 {
4253         char* nick = strtok(params," :");
4254         char* reason = strtok(NULL,"\r\n");
4255         reason++;
4256
4257         userrec* user = Find(nick);
4258         
4259         if (user)
4260         {
4261                 if (strlen(reason)>MAXQUIT)
4262                 {
4263                         reason[MAXQUIT-1] = '\0';
4264                 }
4265
4266
4267                 WriteCommonExcept(user,"QUIT :%s",reason);
4268
4269                 user_hash::iterator iter = clientlist.find(user->nick);
4270         
4271                 if (iter != clientlist.end())
4272                 {
4273                         log(DEBUG,"deleting user hash value %d",iter->second);
4274                         if ((iter->second) && (user->registered == 7)) {
4275                                 delete iter->second;
4276                         }
4277                         clientlist.erase(iter);
4278                 }
4279
4280                 purge_empty_chans();
4281         }
4282 }
4283
4284 void handle_n(char token,char* params,serverrec* source,serverrec* reply, char* udp_host)
4285 {
4286         char* oldnick = strtok(params," ");
4287         char* newnick = strtok(NULL," ");
4288         
4289         userrec* user = Find(oldnick);
4290         
4291         if (user)
4292         {
4293                 WriteCommon(user,"NICK %s",newnick);
4294                 user = ReHashNick(user->nick, newnick);
4295                 if (!user) return;
4296                 if (!user->nick) return;
4297                 strncpy(user->nick, newnick,NICKMAX);
4298                 log(DEBUG,"new nick set: %s",user->nick);
4299         }
4300 }
4301
4302 // k <SOURCE> <DEST> <CHANNEL> :<REASON>
4303 void handle_k(char token,char* params,serverrec* source,serverrec* reply, char* udp_host)
4304 {
4305         char* src = strtok(params," ");
4306         char* dest = strtok(NULL," ");
4307         char* channel = strtok(NULL," :");
4308         char* reason = strtok(NULL,"\r\n");
4309         reason++;
4310         userrec* s = Find(src);
4311         userrec* d = Find(dest);
4312         chanrec* c = FindChan(channel);
4313         if ((s) && (d) && (c))
4314         {
4315                 kick_channel(s,d,c,reason);
4316         }
4317 }
4318
4319 void handle_AT(char token,char* params,serverrec* source,serverrec* reply, char* udp_host)
4320 {
4321         char* who = strtok(params," :");
4322         char* text = strtok(NULL,"\r\n");
4323         text++;
4324         userrec* s = Find(who);
4325         if (s)
4326         {
4327                 WriteWallOps(s,true,text);
4328         }
4329 }
4330
4331
4332 void handle_N(char token,char* params,serverrec* source,serverrec* reply, char* udp_host)
4333 {
4334         char* tm = strtok(params," ");
4335         char* nick = strtok(NULL," ");
4336         char* host = strtok(NULL," ");
4337         char* dhost = strtok(NULL," ");
4338         char* ident = strtok(NULL," ");
4339         char* modes = strtok(NULL," ");
4340         char* server = strtok(NULL," :");
4341         char* gecos = strtok(NULL,"\r\n");
4342         gecos++;
4343         modes++;
4344         time_t TS = atoi(tm);
4345         user_hash::iterator iter = clientlist.find(nick);
4346         if (iter != clientlist.end())
4347         {
4348                 // nick collision
4349                 WriteOpers("Nickname collision: %s@%s != %s@%s",nick,server,iter->second->nick,iter->second->server);
4350                 char str[MAXBUF];
4351                 snprintf(str,MAXBUF,"Killed (Nick Collision (%s@%s < %s@%s))",nick,server,iter->second->nick,iter->second->server);
4352                 WriteServ(iter->second->fd, "KILL %s :%s",iter->second->nick,str);
4353                 kill_link(iter->second,str);
4354         }
4355         clientlist[nick] = new userrec();
4356         // remote users have an fd of -1. This is so that our Write abstraction
4357         // routines know to route any messages to this record away to whatever server
4358         // theyre on.
4359         clientlist[nick]->fd = -1;
4360         strncpy(clientlist[nick]->nick, nick,NICKMAX);
4361         strncpy(clientlist[nick]->host, host,160);
4362         strncpy(clientlist[nick]->dhost, dhost,160);
4363         strncpy(clientlist[nick]->server, server,256);
4364         strncpy(clientlist[nick]->ident, ident,10); // +1 char to compensate for tilde
4365         strncpy(clientlist[nick]->fullname, gecos,128);
4366         clientlist[nick]->signon = TS;
4367         clientlist[nick]->nping = 0; // this is ignored for a remote user anyway.
4368         clientlist[nick]->lastping = 1;
4369         clientlist[nick]->port = 0; // so is this...
4370         clientlist[nick]->registered = 7; // this however we need to set for them to receive messages and appear online
4371         clientlist[nick]->idle_lastmsg = time(NULL); // this is unrealiable and wont actually be used locally
4372         for (int i = 0; i < MAXCHANS; i++)
4373         {
4374                 clientlist[nick]->chans[i].channel = NULL;
4375                 clientlist[nick]->chans[i].uc_modes = 0;
4376         }
4377 }
4378
4379 void handle_F(char token,char* params,serverrec* source,serverrec* reply, char* udp_host)
4380 {
4381         long tdiff = time(NULL) - atoi(params);
4382         if (tdiff)
4383                 WriteOpers("TS split for %s -> %s: %d",source->name,reply->name,tdiff);
4384 }
4385
4386 void handle_a(char token,char* params,serverrec* source,serverrec* reply, char* udp_host)
4387 {
4388         char* nick = strtok(params," :");
4389         char* gecos = strtok(NULL,"\r\n");
4390         
4391         userrec* user = Find(nick);
4392
4393         if (user)
4394                 strncpy(user->fullname,gecos,MAXBUF);
4395 }
4396
4397 void handle_b(char token,char* params,serverrec* source,serverrec* reply, char* udp_host)
4398 {
4399         char* nick = strtok(params," ");
4400         char* host = strtok(NULL," ");
4401         
4402         userrec* user = Find(nick);
4403
4404         if (user)
4405                 strncpy(user->dhost,host,160);
4406 }
4407
4408 void handle_plus(char token,char* params,serverrec* source,serverrec* reply, char* udp_host)
4409 {
4410         // %s %s %d %d
4411         // + test3.chatspike.net 7010 -2016508415
4412         char* servername = strtok(params," ");
4413         char* ipaddr = strtok(NULL," ");
4414         char* ipport = strtok(NULL," ");
4415         char* cookie = strtok(NULL," ");
4416         log(DEBUG,"*** Connecting back to %s:%d",ipaddr,atoi(ipport));
4417
4418
4419         bool conn_already = false;
4420         for (int i = 0; i < 32; i++)
4421         {
4422                 if (me[i] != NULL)
4423                 {
4424                         for (int j = 0; j < me[i]->connectors.size(); j++)
4425                         {
4426                                 if (!strcasecmp(me[i]->connectors[j].GetServerName().c_str(),servername))
4427                                 {
4428                                         if (me[i]->connectors[j].GetServerPort() == atoi(ipport))
4429                                         {
4430                                                 log(DEBUG,"Already got a connection to %s:%d, ignoring +",ipaddr,atoi(ipport));
4431                                                 conn_already = true;
4432                                         }
4433                                 }
4434                         }
4435                 }
4436         }
4437         if (!conn_already)
4438                 me[defaultRoute]->MeshCookie(ipaddr,atoi(ipport),atoi(cookie),servername);
4439 }
4440
4441 void handle_R(char token,char* params,serverrec* source,serverrec* reply, char* udp_host)
4442 {
4443         char* server = strtok(params," ");
4444         char* data = strtok(NULL,"\r\n");
4445         log(DEBUG,"Forwarded packet '%s' to '%s'",data,server);
4446         NetSendToOne(server,data);
4447 }
4448
4449 void handle_J(char token,char* params,serverrec* source,serverrec* reply, char* udp_host)
4450 {
4451         // IMPORTANT NOTE
4452         // The J token currently has no timestamp - this needs looking at
4453         // because it will allow splitriding.
4454         char* nick = strtok(params," ");
4455         char* channel = strtok(NULL," ");
4456         userrec* user = Find(nick);
4457         while (channel)
4458         {
4459                 if ((user != NULL) && (strcmp(channel,"")))
4460                 {
4461                         char privilage = '\0';
4462                         if (channel[0] != '#')
4463                         {
4464                                 privilage = channel[0];
4465                                 channel++;
4466                         }
4467                         add_channel(user,channel,"",true);
4468
4469                         // now work out the privilages they should have on each channel
4470                         // and send the appropriate servermodes.
4471                         for (int i = 0; i != MAXCHANS; i++)
4472                         {
4473                                 if (user->chans[i].channel)
4474                                 {
4475                                         if (!strcasecmp(user->chans[i].channel->name,channel))
4476                                         {
4477                                                 if (privilage == '@')
4478                                                 {
4479                                                         user->chans[i].uc_modes = user->chans[i].uc_modes | UCMODE_OP;
4480                                                         WriteChannelLocal(user->chans[i].channel, NULL, "MODE %s +o %s",channel,user->nick);
4481                                                 }
4482                                                 if (privilage == '%')
4483                                                 {
4484                                                         user->chans[i].uc_modes = user->chans[i].uc_modes | UCMODE_HOP;
4485                                                         WriteChannelLocal(user->chans[i].channel, NULL, "MODE %s +h %s",channel,user->nick);
4486                                                 }
4487                                                 if (privilage == '+')
4488                                                 {
4489                                                         user->chans[i].uc_modes = user->chans[i].uc_modes | UCMODE_VOICE;
4490                                                         WriteChannelLocal(user->chans[i].channel, NULL, "MODE %s +v %s",channel,user->nick);
4491                                                 }
4492                                         }
4493                                 }
4494                         }
4495
4496                 }
4497                 channel = strtok(NULL," ");
4498         }
4499 }
4500
4501 void NetSendMyRoutingTable()
4502 {
4503         // send out a line saying what is reachable to us.
4504         // E.g. if A is linked to B C and D, send out:
4505         // $ A B C D
4506         // if its only linked to B and D send out:
4507         // $ A B D
4508         // if it has no links, dont even send out the line at all.
4509         char buffer[MAXBUF];
4510         sprintf(buffer,"$ %s",ServerName);
4511         bool sendit = false;
4512         for (int i = 0; i < 32; i++)
4513         {
4514                 if (me[i] != NULL)
4515                 {
4516                         for (int j = 0; j < me[i]->connectors.size(); j++)
4517                         {
4518                                 if (me[i]->connectors[j].GetState() != STATE_DISCONNECTED)
4519                                 {
4520                                         strncat(buffer," ",MAXBUF);
4521                                         strncat(buffer,me[i]->connectors[j].GetServerName().c_str(),MAXBUF);
4522                                         sendit = true;
4523                                 }
4524                         }
4525                 }
4526         }
4527         if (sendit)
4528                 NetSendToAll(buffer);
4529 }
4530
4531 void handle_dollar(char token,char* params,serverrec* source,serverrec* reply, char* udp_host)
4532 {
4533         log(DEBUG,"Storing routing table...");
4534         char* sourceserver = strtok(params," ");
4535         char* server = strtok(NULL," ");
4536         for (int i = 0; i < 32; i++)
4537         {
4538                 if (me[i] != NULL)
4539                 {
4540                         for (int j = 0; j < me[i]->connectors.size(); j++)
4541                         {
4542                                 if (!strcasecmp(me[i]->connectors[j].GetServerName().c_str(),sourceserver))
4543                                 {
4544                                         me[i]->connectors[j].routes.clear();
4545                                         log(DEBUG,"Found entry for source server.");
4546                                         while (server)
4547                                         {
4548                                                 // store each route
4549                                                 me[i]->connectors[j].routes.push_back(server);
4550                                                 log(DEBUG,"*** Stored route: %s -> %s -> %s",ServerName,sourceserver,server);
4551                                                 server = strtok(NULL," ");
4552                                         }
4553                                         return;
4554                                 }
4555                         }
4556                 }
4557         }
4558         log(DEBUG,"Warning! routing table received from nonexistent server!");
4559 }
4560
4561
4562 void DoSplit(const char* params)
4563 {
4564         bool go_again = true;
4565         while (go_again)
4566         {
4567                 go_again = false;
4568                 for (int i = 0; i < 32; i++)
4569                 {
4570                         if (me[i] != NULL)
4571                         {
4572                                 for (vector<ircd_connector>::iterator j = me[i]->connectors.begin(); j != me[i]->connectors.end(); j++)
4573                                 {
4574                                         if (!strcasecmp(j->GetServerName().c_str(),params))
4575                                         {
4576                                                 j->routes.clear();
4577                                                 j->CloseConnection();
4578                                                 me[i]->connectors.erase(j);
4579                                                 go_again = true;
4580                                                 break;
4581                                         }
4582                                 }
4583                         }
4584                 }
4585         }
4586         log(DEBUG,"Removed server. Will remove clients...");
4587         // iterate through the userlist and remove all users on this server.
4588         // because we're dealing with a mesh, we dont have to deal with anything
4589         // "down-route" from this server (nice huh)
4590         go_again = true;
4591         char reason[MAXBUF];
4592         snprintf(reason,MAXBUF,"%s %s",ServerName,params);
4593         while (go_again)
4594         {
4595                 go_again = false;
4596                 for (user_hash::const_iterator u = clientlist.begin(); u != clientlist.end(); u++)
4597                 {
4598                         if (!strcasecmp(u->second->server,params))
4599                         {
4600                                 kill_link(u->second,reason);
4601                                 go_again = true;
4602                                 break;
4603                         }
4604                 }
4605         }
4606 }
4607
4608 // removes a server. Will NOT remove its users!
4609
4610 void RemoveServer(const char* name)
4611 {
4612         bool go_again = true;
4613         while (go_again)
4614         {
4615                 go_again = false;
4616                 for (int i = 0; i < 32; i++)
4617                 {
4618                         if (me[i] != NULL)
4619                         {
4620                                 for (vector<ircd_connector>::iterator j = me[i]->connectors.begin(); j != me[i]->connectors.end(); j++)
4621                                 {
4622                                         if (!strcasecmp(j->GetServerName().c_str(),name))
4623                                         {
4624                                                 j->routes.clear();
4625                                                 j->CloseConnection();
4626                                                 me[i]->connectors.erase(j);
4627                                                 go_again = true;
4628                                                 break;
4629                                         }
4630                                 }
4631                         }
4632                 }
4633         }
4634 }
4635
4636 void handle_amp(char token,char* params,serverrec* source,serverrec* reply, char* udp_host)
4637 {
4638         log(DEBUG,"Netsplit! %s split from mesh, removing!",params);
4639         bool go_again = true;
4640         while (go_again)
4641         {
4642                 go_again = false;
4643                 for (int i = 0; i < 32; i++)
4644                 {
4645                         if (me[i] != NULL)
4646                         {
4647                                 for (vector<ircd_connector>::iterator j = me[i]->connectors.begin(); j != me[i]->connectors.end(); j++)
4648                                 {
4649                                         if (!strcasecmp(j->GetServerName().c_str(),params))
4650                                         {
4651                                                 j->routes.clear();
4652                                                 j->CloseConnection();
4653                                                 me[i]->connectors.erase(j);
4654                                                 go_again = true;
4655                                                 break;
4656                                         }
4657                                 }
4658                         }
4659                 }
4660         }
4661         log(DEBUG,"Removed server. Will remove clients...");
4662         // iterate through the userlist and remove all users on this server.
4663         // because we're dealing with a mesh, we dont have to deal with anything
4664         // "down-route" from this server (nice huh)
4665         go_again = true;
4666         char reason[MAXBUF];
4667         snprintf(reason,MAXBUF,"%s %s",ServerName,params);
4668         while (go_again)
4669         {
4670                 go_again = false;
4671                 for (user_hash::const_iterator u = clientlist.begin(); u != clientlist.end(); u++)
4672                 {
4673                         if (!strcasecmp(u->second->server,params))
4674                         {
4675                                 kill_link(u->second,reason);
4676                                 go_again = true;
4677                                 break;
4678                         }
4679                 }
4680         }
4681 }
4682
4683 long authcookie;
4684
4685
4686 void process_restricted_commands(char token,char* params,serverrec* source,serverrec* reply, char* udp_host,char* ipaddr,int port)
4687 {
4688         char buffer[MAXBUF];
4689
4690         switch(token)
4691         {
4692                 // Y <TS>
4693                 // start netburst
4694                 case 'Y':
4695                         nb_start = time(NULL);
4696                         WriteOpers("Server %s is starting netburst.",udp_host);
4697                         // now broadcast this new servers address out to all servers that are linked to us,
4698                         // except the newcomer. They'll all attempt to connect back to it.
4699                         authcookie = rand()*rand();
4700                         snprintf(buffer,MAXBUF,"~ %d",authcookie);
4701                         NetSendToAll(buffer);
4702                 break;
4703                 // ~
4704                 // Store authcookie
4705                 // once stored, this authcookie permits other servers to log in
4706                 // without user or password, using it.
4707                 case '~':
4708                         auth_cookies.push_back(atoi(params));
4709                         log(DEBUG,"*** Stored auth cookie, will permit servers with auth-cookie %d",atoi(params));
4710                 break;
4711                 // connect back to a server using an authcookie
4712                 case '+':
4713                         handle_plus(token,params,source,reply,udp_host);
4714                 break;
4715                 // routing table
4716                 case '$':
4717                         handle_dollar(token,params,source,reply,udp_host);
4718                 break;
4719                 // node unreachable - we cant route to a server, sooooo we slit it off.
4720                 // servers can generate these for themselves for an squit.
4721                 case '&':
4722                         handle_amp(token,params,source,reply,udp_host);
4723                 break;
4724                 // R <server> <data>
4725                 // redirect token, send all of <data> along to the given 
4726                 // server as this server has been found to still have a route to it
4727                 case 'R':
4728                         handle_R(token,params,source,reply,udp_host);
4729                 break;
4730                 // ?
4731                 // ping
4732                 case '?':
4733                         reply->SendPacket("!",udp_host);
4734                 break;
4735                 // ?
4736                 // pong
4737                 case '!':
4738                 break;
4739                 // *
4740                 // no operation
4741                 case '*':
4742                 break;
4743                 // N <TS> <NICK> <HOST> <DHOST> <IDENT> <MODES> <SERVER> :<GECOS>
4744                 // introduce remote client
4745                 case 'N':
4746                         handle_N(token,params,source,reply,udp_host);
4747                 break;
4748                 // a <NICK> :<GECOS>
4749                 // change GECOS (SETNAME)
4750                 case 'a':
4751                         handle_a(token,params,source,reply,udp_host);
4752                 break;
4753                 // b <NICK> :<HOST>
4754                 // change displayed host (SETHOST)
4755                 case 'b':
4756                         handle_b(token,params,source,reply,udp_host);
4757                 break;
4758                 // t <NICK> <CHANNEL> :<TOPIC>
4759                 // change a channel topic
4760                 case 't':
4761                         handle_t(token,params,source,reply,udp_host);
4762                 break;
4763                 // i <NICK> <CHANNEL>
4764                 // invite a user to a channel
4765                 case 'i':
4766                         handle_i(token,params,source,reply,udp_host);
4767                 break;
4768                 // k <SOURCE> <DEST> <CHANNEL> :<REASON>
4769                 // kick a user from a channel
4770                 case 'k':
4771                         handle_k(token,params,source,reply,udp_host);
4772                 break;
4773                 // n <NICK> <NEWNICK>
4774                 // change nickname of client -- a server should only be able to
4775                 // change the nicknames of clients that reside on it unless
4776                 // they are ulined.
4777                 case 'n':
4778                         handle_n(token,params,source,reply,udp_host);
4779                 break;
4780                 // J <NICK> <CHANLIST>
4781                 // Join user to channel list, merge channel permissions
4782                 case 'J':
4783                         handle_J(token,params,source,reply,udp_host);
4784                 break;
4785                 // T <TS> <CHANNEL> <TOPICSETTER> :<TOPIC>
4786                 // change channel topic (netburst only)
4787                 case 'T':
4788                         handle_T(token,params,source,reply,udp_host);
4789                 break;
4790                 // M <TARGET> <MODES> [MODE-PARAMETERS]
4791                 // Server setting modes on an object
4792                 case 'M':
4793                         handle_M(token,params,source,reply,udp_host);
4794                 break;
4795                 // m <SOURCE> <TARGET> <MODES> [MODE-PARAMETERS]
4796                 // User setting modes on an object
4797                 case 'm':
4798                         handle_m(token,params,source,reply,udp_host);
4799                 break;
4800                 // P <SOURCE> <TARGET> :<TEXT>
4801                 // Send a private/channel message
4802                 case 'P':
4803                         handle_P(token,params,source,reply,udp_host);
4804                 break;
4805                 // V <SOURCE> <TARGET> :<TEXT>
4806                 // Send a private/channel notice
4807                 case 'V':
4808                         handle_V(token,params,source,reply,udp_host);
4809                 break;
4810                 // L <SOURCE> <CHANNEL> :<REASON>
4811                 // User parting a channel
4812                 case 'L':
4813                         handle_L(token,params,source,reply,udp_host);
4814                 break;
4815                 // Q <SOURCE> :<REASON>
4816                 // user quitting
4817                 case 'Q':
4818                         handle_Q(token,params,source,reply,udp_host);
4819                 break;
4820                 // K <SOURCE> <DEST> :<REASON>
4821                 // remote kill
4822                 case 'K':
4823                         handle_K(token,params,source,reply,udp_host);
4824                 break;
4825                 // @ <SOURCE> :<TEXT>
4826                 // wallops
4827                 case '@':
4828                         handle_AT(token,params,source,reply,udp_host);
4829                 break;
4830                 // F <TS>
4831                 // end netburst
4832                 case 'F':
4833                         WriteOpers("Server %s has completed netburst. (%d secs)",udp_host,time(NULL)-nb_start);
4834                         handle_F(token,params,source,reply,udp_host);
4835                         nb_start = 0;
4836                         // tell all the other servers to use this authcookie to connect back again
4837                         // got '+ test3.chatspike.net 7010 -2016508415' from test.chatspike.net
4838                         snprintf(buffer,MAXBUF,"+ %s %s %d %d",udp_host,ipaddr,port,authcookie);
4839                         NetSendToAllExcept(udp_host,buffer);
4840                 break;
4841                 // X <reserved>
4842                 // Send netburst now
4843                 case 'X':
4844                         WriteOpers("Sending my netburst to %s",udp_host);
4845                         DoSync(source,udp_host);
4846                         WriteOpers("Send of netburst to %s completed",udp_host);
4847                         NetSendMyRoutingTable();
4848                 break;
4849                 // anything else
4850                 default:
4851                         WriteOpers("WARNING! Unknown datagram type '%c'",token);
4852                 break;
4853         }
4854 }
4855
4856
4857 void handle_link_packet(char* udp_msg, char* udp_host, serverrec *serv)
4858 {
4859         char response[10240];
4860         char token = udp_msg[0];
4861         char* params = udp_msg + 2;
4862         char finalparam[1024];
4863         strcpy(finalparam," :xxxx");
4864         if (strstr(udp_msg," :")) {
4865                 strncpy(finalparam,strstr(udp_msg," :"),1024);
4866         }
4867         if (token == '-') {
4868                 char* cookie = strtok(params," ");
4869                 char* servername = strtok(NULL," ");
4870                 char* serverdesc = finalparam+2;
4871
4872                 WriteOpers("AuthCookie CONNECT from %s (%s)",servername,udp_host);
4873
4874                 for (int u = 0; u < auth_cookies.size(); u++)
4875                 {
4876                         if (auth_cookies[u] == atoi(cookie))
4877                         {
4878                                 WriteOpers("Allowed cookie from %s, is now part of the mesh",servername);
4879
4880
4881                                 for (int j = 0; j < 32; j++)
4882                                 {
4883                                         if (me[j] != NULL)
4884                                         {
4885                                                 for (int k = 0; k < me[j]->connectors.size(); k++)
4886                                                 {
4887                                                         if (!strcasecmp(me[j]->connectors[k].GetServerName().c_str(),udp_host))
4888                                                         {
4889                                                                 me[j]->connectors[k].SetServerName(servername);
4890                                                                 me[j]->connectors[k].SetDescription(serverdesc);
4891                                                                 me[j]->connectors[k].SetState(STATE_CONNECTED);
4892                                                                 NetSendMyRoutingTable();
4893                                                                 return;
4894                                                         }
4895                                                 }
4896                                         }
4897                                         WriteOpers("\2WARNING!\2 %s sent us an authentication packet but we are not authenticating with this server right now! Possible intrusion attempt!",udp_host);
4898                                         return;
4899                                 }
4900
4901
4902                                 return;
4903                         }
4904                 }
4905                 // bad cookie, bad bad! go sit in the corner!
4906                 WriteOpers("Bad cookie from %s!",servername);
4907                 return;
4908         }
4909         else
4910         if (token == 'S') {
4911                 // S test.chatspike.net password portn :ChatSpike InspIRCd test server
4912                 char* servername = strtok(params," ");
4913                 char* password = strtok(NULL," ");
4914                 char* myport = strtok(NULL," ");
4915                 char* revision = strtok(NULL," ");
4916                 char* serverdesc = finalparam+2;
4917
4918                 WriteOpers("CONNECT from %s (%s) (their port: %d)",servername,udp_host,atoi(myport));
4919                 
4920                 ircd_connector* cn = serv->FindHost(servername);
4921                 
4922                 if (cn)
4923                 {
4924                         WriteOpers("CONNECT aborted: Server %s already exists from %s",servername,ServerName);
4925                         char buffer[MAXBUF];
4926                         sprintf(buffer,"E :Server %s already exists!",servername);
4927                         serv->SendPacket(buffer,udp_host);
4928                         RemoveServer(udp_host);
4929                         return;
4930                 }
4931
4932                 if (atoi(revision) != GetRevision())
4933                 {
4934                         WriteOpers("CONNECT aborted: Could not link to %s, is an incompatible version %s, our version is %d",servername,revision,GetRevision());
4935                         char buffer[MAXBUF];
4936                         sprintf(buffer,"E :Version number mismatch");
4937                         serv->SendPacket(buffer,udp_host);
4938                         RemoveServer(udp_host);
4939                         RemoveServer(servername);
4940                         return;
4941                 }
4942
4943                 for (int j = 0; j < serv->connectors.size(); j++)
4944                 {
4945                         if (!strcasecmp(serv->connectors[j].GetServerName().c_str(),udp_host))
4946                         {
4947                                 serv->connectors[j].SetServerName(servername);
4948                                 serv->connectors[j].SetDescription(serverdesc);
4949                                 serv->connectors[j].SetServerPort(atoi(myport));
4950                         }
4951                 }
4952                 
4953                 
4954                 char Link_ServerName[1024];
4955                 char Link_IPAddr[1024];
4956                 char Link_Port[1024];
4957                 char Link_Pass[1024];
4958                 char Link_SendPass[1024];
4959                 int LinkPort = 0;
4960                 
4961                 // search for a corresponding <link> block in the config files
4962                 for (int i = 0; i < ConfValueEnum("link",&config_f); i++)
4963                 {
4964                         ConfValue("link","name",i,Link_ServerName,&config_f);
4965                         ConfValue("link","ipaddr",i,Link_IPAddr,&config_f);
4966                         ConfValue("link","port",i,Link_Port,&config_f);
4967                         ConfValue("link","recvpass",i,Link_Pass,&config_f);
4968                         ConfValue("link","sendpass",i,Link_SendPass,&config_f);
4969                         log(DEBUG,"(%d) Comparing against name='%s', ipaddr='%s', port='%s', recvpass='%s'",i,Link_ServerName,Link_IPAddr,Link_Port,Link_Pass);
4970                         LinkPort = atoi(Link_Port);
4971                         if (!strcasecmp(Link_ServerName,servername))
4972                         {
4973                                 // we have a matching link line -
4974                                 // send a 'diminutive' server message back...
4975                                 snprintf(response,10240,"s %s %s :%s",ServerName,Link_SendPass,ServerDesc);
4976                                 serv->SendPacket(response,servername);
4977
4978                                 for (int t = 0; t < serv->connectors.size(); t++)
4979                                 {
4980                                         if (!strcasecmp(serv->connectors[t].GetServerName().c_str(),servername))
4981                                         {
4982                                                 serv->connectors[t].SetState(STATE_CONNECTED);
4983                                         }
4984                                 }
4985                 
4986                                 return;
4987                         }
4988                 }
4989                 char buffer[MAXBUF];
4990                 sprintf(buffer,"E :Access is denied (no matching link block)");
4991                 serv->SendPacket(buffer,udp_host);
4992                 WriteOpers("CONNECT from %s denied, no matching link block",servername);
4993                 RemoveServer(udp_host);
4994                 RemoveServer(servername);
4995                 return;
4996         }
4997         else
4998         if (token == 's') {
4999                 // S test.chatspike.net password :ChatSpike InspIRCd test server
5000                 char* servername = strtok(params," ");
5001                 char* password = strtok(NULL," ");
5002                 char* serverdesc = finalparam+2;
5003                 
5004                 // TODO: we should do a check here to ensure that this server is one we recently initiated a
5005                 // link with, and didnt hear an 's' or 'E' back from yet (these are the only two valid responses
5006                 // to an 'S' command. If we didn't recently send an 'S' to this server, theyre trying to spoof
5007                 // a connect, so put out an oper alert!
5008                 
5009                 // for now, just accept all, we'll fix that later.
5010                 WriteOpers("%s accepted our link credentials ",servername);
5011                 
5012                 char Link_ServerName[1024];
5013                 char Link_IPAddr[1024];
5014                 char Link_Port[1024];
5015                 char Link_Pass[1024];
5016                 char Link_SendPass[1024];
5017                 int LinkPort = 0;
5018                 
5019                 // search for a corresponding <link> block in the config files
5020                 for (int i = 0; i < ConfValueEnum("link",&config_f); i++)
5021                 {
5022                         ConfValue("link","name",i,Link_ServerName,&config_f);
5023                         ConfValue("link","ipaddr",i,Link_IPAddr,&config_f);
5024                         ConfValue("link","port",i,Link_Port,&config_f);
5025                         ConfValue("link","recvpass",i,Link_Pass,&config_f);
5026                         ConfValue("link","sendpass",i,Link_SendPass,&config_f);
5027                         log(DEBUG,"(%d) Comparing against name='%s', ipaddr='%s', port='%s', recvpass='%s'",i,Link_ServerName,Link_IPAddr,Link_Port,Link_Pass);
5028                         LinkPort = atoi(Link_Port);
5029                         if (!strcasecmp(Link_ServerName,servername))
5030                         {
5031                                 // matching link at this end too, we're all done!
5032                                 // at this point we must begin key exchange and insert this
5033                                 // server into our 'active' table.
5034                                 for (int j = 0; j < 32; j++)
5035                                 {
5036                                         if (me[j] != NULL)
5037                                         {
5038                                                 for (int k = 0; k < me[j]->connectors.size(); k++)
5039                                                 {
5040                                                         if (!strcasecmp(me[j]->connectors[k].GetServerName().c_str(),udp_host))
5041                                                         {
5042                                                                 char buffer[MAXBUF];
5043                                                                 me[j]->connectors[k].SetDescription(serverdesc);
5044                                                                 me[j]->connectors[k].SetState(STATE_CONNECTED);
5045                                                                 sprintf(buffer,"X 0");
5046                                                                 serv->SendPacket(buffer,udp_host);
5047                                                                 DoSync(me[j],udp_host);
5048                                                                 NetSendMyRoutingTable();
5049                                                                 return;
5050                                                         }
5051                                                 }
5052                                         }
5053                                         WriteOpers("\2WARNING!\2 %s sent us an authentication packet but we are not authenticating with this server right noe! Possible intrusion attempt!",udp_host);
5054                                         return;
5055                                 }
5056                         }
5057                         else {
5058                                 log(DEBUG,"Server names '%s' and '%s' don't match",Link_ServerName,servername);
5059                         }
5060                 }
5061                 char buffer[MAXBUF];
5062                 sprintf(buffer,"E :Access is denied (no matching link block)");
5063                 serv->SendPacket(buffer,udp_host);
5064                 WriteOpers("CONNECT from %s denied, no matching link block",servername);
5065                 RemoveServer(udp_host);
5066                 RemoveServer(servername);
5067                 return;
5068         }
5069         else
5070         if (token == 'E') {
5071                 char* error_message = finalparam+2;
5072                 WriteOpers("ERROR from %s: %s",udp_host,error_message);
5073                 return;
5074         }
5075         else {
5076
5077                 serverrec* source_server = NULL;
5078
5079                 for (int j = 0; j < 32; j++)
5080                 {
5081                         if (me[j] != NULL)
5082                         {
5083                                 for (int x = 0; x < me[j]->connectors.size(); x++)
5084                                 {
5085                                         log(DEBUG,"Servers are: '%s' '%s'",udp_host,me[j]->connectors[x].GetServerName().c_str());
5086                                         if (!strcasecmp(me[j]->connectors[x].GetServerName().c_str(),udp_host))
5087                                         {
5088                                                 if (me[j]->connectors[x].GetState() == STATE_CONNECTED)
5089                                                 {
5090                                                         // found a valid ircd_connector.
5091                                                         process_restricted_commands(token,params,me[j],serv,udp_host,me[j]->connectors[x].GetServerIP(),me[j]->connectors[x].GetServerPort());
5092                                                         return;
5093                                                 }
5094                                         }
5095                                 }
5096                         }
5097                 }
5098
5099                 log(DEBUG,"Unrecognised token or unauthenticated host in datagram from %s: %c",udp_host,token);
5100         }
5101 }
5102
5103 int reap_counter = 0;
5104
5105 int InspIRCd(void)
5106 {
5107         struct sockaddr_in client, server;
5108         char addrs[MAXBUF][255];
5109         int openSockfd[MAXSOCKS], incomingSockfd, result = TRUE;
5110         socklen_t length;
5111         int count = 0, scanDetectTrigger = TRUE, showBanner = FALSE;
5112         int selectResult = 0, selectResult2 = 0;
5113         char *temp, configToken[MAXBUF], stuff[MAXBUF], Addr[MAXBUF], Type[MAXBUF];
5114         char resolvedHost[MAXBUF];
5115         fd_set selectFds;
5116         struct timeval tv;
5117
5118         log_file = fopen("ircd.log","a+");
5119         if (!log_file)
5120         {
5121                 printf("ERROR: Could not write to logfile ircd.log, bailing!\n\n");
5122                 Exit(ERROR);
5123         }
5124
5125         log(DEBUG,"InspIRCd: startup: begin");
5126         log(DEBUG,"$Id$");
5127         if (geteuid() == 0)
5128         {
5129                 printf("WARNING!!! You are running an irc server as ROOT!!! DO NOT DO THIS!!!\n\n");
5130                 Exit(ERROR);
5131                 log(DEBUG,"InspIRCd: startup: not starting with UID 0!");
5132         }
5133         SetupCommandTable();
5134         log(DEBUG,"InspIRCd: startup: default command table set up");
5135         
5136         ReadConfig();
5137         if (strcmp(DieValue,"")) 
5138         { 
5139                 printf("WARNING: %s\n\n",DieValue);
5140                 exit(0); 
5141         }  
5142         log(DEBUG,"InspIRCd: startup: read config");
5143           
5144         int count2 = 0, count3 = 0;
5145
5146         for (count = 0; count < ConfValueEnum("bind",&config_f); count++)
5147         {
5148                 ConfValue("bind","port",count,configToken,&config_f);
5149                 ConfValue("bind","address",count,Addr,&config_f);
5150                 ConfValue("bind","type",count,Type,&config_f);
5151                 if (!strcmp(Type,"servers"))
5152                 {
5153                         char Default[MAXBUF];
5154                         strcpy(Default,"no");
5155                         ConfValue("bind","default",count,Default,&config_f);
5156                         if (strchr(Default,'y'))
5157                         {
5158                                 defaultRoute = count3;
5159                                 log(DEBUG,"InspIRCd: startup: binding '%s:%s' is default server route",Addr,configToken);
5160                         }
5161                         me[count3] = new serverrec(ServerName,100L,false);
5162                         me[count3]->CreateListener(Addr,atoi(configToken));
5163                         count3++;
5164                 }
5165                 else
5166                 {
5167                         ports[count2] = atoi(configToken);
5168                         strcpy(addrs[count2],Addr);
5169                         count2++;
5170                 }
5171                 log(DEBUG,"InspIRCd: startup: read binding %s:%s [%s] from config",Addr,configToken, Type);
5172         }
5173         portCount = count2;
5174         UDPportCount = count3;
5175           
5176         log(DEBUG,"InspIRCd: startup: read %d total client ports and %d total server ports",portCount,UDPportCount);
5177         
5178         log(DEBUG,"InspIRCd: startup: InspIRCd is now running!");
5179         
5180         printf("\n");
5181         
5182         /* BugFix By Craig! :p */
5183         count = 0;
5184         for (count2 = 0; count2 < ConfValueEnum("module",&config_f); count2++)
5185         {
5186                 char modfile[MAXBUF];
5187                 ConfValue("module","name",count2,configToken,&config_f);
5188                 sprintf(modfile,"%s/%s",MOD_PATH,configToken,&config_f);
5189                 printf("Loading module... \033[1;37m%s\033[0;37m\n",modfile);
5190                 log(DEBUG,"InspIRCd: startup: Loading module: %s",modfile);
5191                 /* If The File Doesnt exist, Trying to load it
5192                  * Will Segfault the IRCd.. So, check to see if
5193                  * it Exists, Before Proceeding. */
5194                 if (FileExists(modfile))
5195                 {
5196                         factory[count] = new ircd_module(modfile);
5197                         if (factory[count]->LastError())
5198                         {
5199                                 log(DEBUG,"Unable to load %s: %s",modfile,factory[count]->LastError());
5200                                 sprintf("Unable to load %s: %s\nExiting...\n",modfile,factory[count]->LastError());
5201                                 Exit(ERROR);
5202                         }
5203                         if (factory[count]->factory)
5204                         {
5205                                 modules[count] = factory[count]->factory->CreateModule();
5206                                 /* save the module and the module's classfactory, if
5207                                  * this isnt done, random crashes can occur :/ */
5208                                 module_names.push_back(modfile);        
5209                         }
5210                         else
5211                         {
5212                                 log(DEBUG,"Unable to load %s",modfile);
5213                                 sprintf("Unable to load %s\nExiting...\n",modfile);
5214                                 Exit(ERROR);
5215                         }
5216                         /* Increase the Count */
5217                         count++;
5218                 }
5219                 else
5220                 {
5221                         log(DEBUG,"InspIRCd: startup: Module Not Found %s",modfile);
5222                         printf("Module Not Found: \033[1;37m%s\033[0;37m, Skipping\n",modfile);
5223                 }
5224         }
5225         MODCOUNT = count - 1;
5226         log(DEBUG,"Total loaded modules: %d",MODCOUNT+1);
5227         
5228         printf("\nInspIRCd is now running!\n");
5229         
5230         startup_time = time(NULL);
5231           
5232         if (nofork)
5233         {
5234                 log(VERBOSE,"Not forking as -nofork was specified");
5235         }
5236         else
5237         {
5238                 if (DaemonSeed() == ERROR)
5239                 {
5240                         log(DEBUG,"InspIRCd: startup: can't daemonise");
5241                         printf("ERROR: could not go into daemon mode. Shutting down.\n");
5242                         Exit(ERROR);
5243                 }
5244         }
5245           
5246           
5247         /* setup select call */
5248         FD_ZERO(&selectFds);
5249         log(DEBUG,"InspIRCd: startup: zero selects");
5250         log(VERBOSE,"InspIRCd: startup: portCount = %d", portCount);
5251         
5252         for (count = 0; count < portCount; count++)
5253         {
5254                 if ((openSockfd[boundPortCount] = OpenTCPSocket()) == ERROR)
5255                 {
5256                         log(DEBUG,"InspIRCd: startup: bad fd %d",openSockfd[boundPortCount]);
5257                         return(ERROR);
5258                 }
5259                 if (BindSocket(openSockfd[boundPortCount],client,server,ports[count],addrs[count]) == ERROR)
5260                 {
5261                         log(DEBUG,"InspIRCd: startup: failed to bind port %d",ports[count]);
5262                 }
5263                 else    /* well we at least bound to one socket so we'll continue */
5264                 {
5265                         boundPortCount++;
5266                 }
5267         }
5268         
5269         log(DEBUG,"InspIRCd: startup: total bound ports %d",boundPortCount);
5270           
5271         /* if we didn't bind to anything then abort */
5272         if (boundPortCount == 0)
5273         {
5274                 log(DEBUG,"InspIRCd: startup: no ports bound, bailing!");
5275                 return (ERROR);
5276         }
5277         
5278
5279         length = sizeof (client);
5280         char udp_msg[MAXBUF], udp_host[MAXBUF];
5281           
5282         /* main loop, this never returns */
5283         for (;;)
5284         {
5285 #ifdef _POSIX_PRIORITY_SCHEDULING
5286                 sched_yield();
5287 #endif
5288
5289                 fd_set sfd;
5290                 timeval tval;
5291                 FD_ZERO(&sfd);
5292
5293                 user_hash::iterator count2 = clientlist.begin();
5294
5295                 // *FIX* Instead of closing sockets in kill_link when they receive the ERROR :blah line, we should queue
5296                 // them in a list, then reap the list every second or so.
5297                 if (reap_counter>300)
5298                 {
5299                         if (fd_reap.size() > 0)
5300                         {
5301                                 for( int n = 0; n < fd_reap.size(); n++)
5302                                 {
5303                                         Blocking(fd_reap[n]);
5304                                         close(fd_reap[n]);
5305                                         NonBlocking(fd_reap[n]);
5306                                 }
5307                         }
5308                         fd_reap.clear();
5309                         reap_counter=0;
5310                 }
5311                 reap_counter++;
5312
5313                 fd_set serverfds;
5314                 FD_ZERO(&serverfds);
5315                 timeval tvs;
5316                 
5317                 for (int x = 0; x != UDPportCount; x++)
5318                 {
5319                         FD_SET(me[x]->fd, &serverfds);
5320                 }
5321                 
5322                 tvs.tv_usec = 0;                
5323                 tvs.tv_sec = 0;
5324                 
5325                 int servresult = select(32767, &serverfds, NULL, NULL, &tvs);
5326                 if (servresult > 0)
5327                 {
5328                         for (int x = 0; x != UDPportCount; x++)
5329                         {
5330                                 if (FD_ISSET (me[x]->fd, &serverfds))
5331                                 {
5332                                         char remotehost[MAXBUF],resolved[MAXBUF];
5333                                         length = sizeof (client);
5334                                         incomingSockfd = accept (me[x]->fd, (sockaddr *) &client, &length);
5335                                         strncpy(remotehost,(char *)inet_ntoa(client.sin_addr),MAXBUF);
5336                                         if(CleanAndResolve(resolved, remotehost) != TRUE)
5337                                         {
5338                                                 strncpy(resolved,remotehost,MAXBUF);
5339                                         }
5340                                         // add to this connections ircd_connector vector
5341                                         // *FIX* - we need the LOCAL port not the remote port in &client!
5342                                         me[x]->AddIncoming(incomingSockfd,resolved,me[x]->port);
5343                                 }
5344                         }
5345                 }
5346      
5347                 for (int x = 0; x < UDPportCount; x++)
5348                 {
5349                         std::deque<std::string> msgs;
5350                         msgs.clear();
5351                         if (me[x]->RecvPacket(msgs, udp_host))
5352                         {
5353                                 for (int ctr = 0; ctr < msgs.size(); ctr++)
5354                                 {
5355                                         char udp_msg[MAXBUF];
5356                                         strncpy(udp_msg,msgs[ctr].c_str(),MAXBUF);
5357                                         if (strlen(udp_msg)<1)
5358                                                 {
5359                                                 log(DEBUG,"Invalid string from %s [route%d]",udp_host,x);
5360                                                 break;
5361                                         }
5362                                         // during a netburst, send all data to all other linked servers
5363                                         if ((nb_start>0) && (udp_msg[0] != 'Y') && (udp_msg[0] != 'X') && (udp_msg[0] != 'F'))
5364                                         {
5365                                                 NetSendToAllExcept(udp_host,udp_msg);
5366                                         }
5367                                         FOREACH_MOD OnPacketReceive(udp_msg);
5368                                         handle_link_packet(udp_msg, udp_host, me[x]);
5369                                 }
5370                                 goto label;
5371                         }
5372                 }
5373         
5374
5375         while (count2 != clientlist.end())
5376         {
5377                 char data[10240];
5378                 tval.tv_usec = tval.tv_sec = 0;
5379                 FD_ZERO(&sfd);
5380                 int total_in_this_set = 0;
5381
5382                 user_hash::iterator xcount = count2;
5383                 user_hash::iterator endingiter = count2;
5384
5385                 if (!count2->second) break;
5386                 
5387                 if (count2->second)
5388                 if (count2->second->fd != 0)
5389                 {
5390                         // assemble up to 64 sockets into an fd_set
5391                         // to implement a pooling mechanism.
5392                         //
5393                         // This should be up to 64x faster than the
5394                         // old implementation.
5395                         while (total_in_this_set < 64)
5396                         {
5397                                 if (count2 != clientlist.end())
5398                                 {
5399                                         // we don't check the state of remote users.
5400                                         if (count2->second->fd > 0)
5401                                         {
5402                                                 FD_SET (count2->second->fd, &sfd);
5403
5404                                                 // registration timeout -- didnt send USER/NICK/HOST in the time specified in
5405                                                 // their connection class.
5406                                                 if ((time(NULL) > count2->second->timeout) && (count2->second->registered != 7)) 
5407                                                 {
5408                                                         log(DEBUG,"InspIRCd: registration timeout: %s",count2->second->nick);
5409                                                         kill_link(count2->second,"Registration timeout");
5410                                                         goto label;
5411                                                 }
5412                                                 if (((time(NULL)) > count2->second->nping) && (isnick(count2->second->nick)) && (count2->second->registered == 7))
5413                                                 {
5414                                                         if ((!count2->second->lastping) && (count2->second->registered == 7))
5415                                                         {
5416                                                                 log(DEBUG,"InspIRCd: ping timeout: %s",count2->second->nick);
5417                                                                 kill_link(count2->second,"Ping timeout");
5418                                                                 goto label;
5419                                                         }
5420                                                         Write(count2->second->fd,"PING :%s",ServerName);
5421                                                         log(DEBUG,"InspIRCd: pinging: %s",count2->second->nick);
5422                                                         count2->second->lastping = 0;
5423                                                         count2->second->nping = time(NULL)+120;
5424                                                 }
5425                                         }
5426                                         count2++;
5427                                         total_in_this_set++;
5428                                 }
5429                                 else break;
5430                         }
5431    
5432                         endingiter = count2;
5433                         count2 = xcount; // roll back to where we were
5434         
5435                         int v = 0;
5436
5437                         tval.tv_usec = 0;
5438                         tval.tv_sec = 0;
5439                         selectResult2 = select(65535, &sfd, NULL, NULL, &tval);
5440                         
5441                         // now loop through all of the items in this pool if any are waiting
5442                         //if (selectResult2 > 0)
5443                         for (user_hash::iterator count2a = xcount; count2a != endingiter; count2a++)
5444                         {
5445
5446 #ifdef _POSIX_PRIORITY_SCHEDULING
5447                                 sched_yield();
5448 #endif
5449
5450                                 result = EAGAIN;
5451                                 if ((count2a->second->fd != -1) && (FD_ISSET (count2a->second->fd, &sfd)))
5452                                 {
5453                                         log(DEBUG,"Reading fd %d",count2a->second->fd);
5454                                         memset(data, 0, 10240);
5455                                         result = read(count2a->second->fd, data, 10240);
5456                                         
5457                                         if (result)
5458                                         {
5459                                                 if (result > 0)
5460                                                         log(DEBUG,"Read %d characters from socket",result);
5461                                                 userrec* current = count2a->second;
5462                                                 int currfd = current->fd;
5463                                                 char* l = strtok(data,"\n");
5464                                                 int floodlines = 0;
5465                                                 while (l)
5466                                                 {
5467                                                         floodlines++;
5468                                                         if ((floodlines > current->flood) && (current->flood != 0))
5469                                                         {
5470                                                                 log(DEFAULT,"Excess flood from: %s!%s@%s",current->nick,current->ident,current->host);
5471                                                                 WriteOpers("*** Excess flood from: %s!%s@%s",current->nick,current->ident,current->host);
5472                                                                 kill_link(current,"Excess flood");
5473                                                                 goto label;
5474                                                         }
5475                                                         char sanitized[NetBufferSize];
5476                                                         memset(sanitized, 0, NetBufferSize);
5477                                                         int ptt = 0;
5478                                                         for (int pt = 0; pt < strlen(l); pt++)
5479                                                         {
5480                                                                 if (l[pt] != '\r')
5481                                                                 {
5482                                                                         sanitized[ptt++] = l[pt];
5483                                                                 }
5484                                                         }
5485                                                         sanitized[ptt] = '\0';
5486                                                         if (strlen(sanitized))
5487                                                         {
5488
5489
5490                                                                 // we're gonna re-scan to check if the nick is gone, after every
5491                                                                 // command - if it has, we're gonna bail
5492                                                                 bool find_again = false;
5493                                                                 process_buffer(sanitized,current);
5494         
5495                                                                 // look for the user's record in case it's changed
5496                                                                 for (user_hash::iterator c2 = clientlist.begin(); c2 != clientlist.end(); c2++)
5497                                                                 {
5498                                                                         if (c2->second->fd == currfd)
5499                                                                         {
5500                                                                                 // found again, update pointer
5501                                                                                 current == c2->second;
5502                                                                                 find_again = true;
5503                                                                                 break;
5504                                                                         }
5505                                                                 }
5506                                                                 if (!find_again)
5507                                                                         goto label;
5508
5509                                                         }
5510                                                         l = strtok(NULL,"\n");
5511                                                 }
5512                                                 goto label;
5513                                         }
5514
5515                                         if ((result == -1) && (errno != EAGAIN) && (errno != EINTR))
5516                                         {
5517                                                 log(DEBUG,"killing: %s",count2a->second->nick);
5518                                                 kill_link(count2a->second,strerror(errno));
5519                                                 goto label;
5520                                         }
5521                                 }
5522                                 // result EAGAIN means nothing read
5523                                 if (result == EAGAIN)
5524                                 {
5525                                 }
5526                                 else
5527                                 if (result == 0)
5528                                 {
5529                                         if (count2->second)
5530                                         {
5531                                                 log(DEBUG,"InspIRCd: Exited: %s",count2a->second->nick);
5532                                                 kill_link(count2a->second,"Client exited");
5533                                                 // must bail here? kill_link removes the hash, corrupting the iterator
5534                                                 log(DEBUG,"Bailing from client exit");
5535                                                 goto label;
5536                                         }
5537                                 }
5538                                 else if (result > 0)
5539                                 {
5540                                 }
5541                         }
5542                 }
5543                 for (int q = 0; q < total_in_this_set; q++)
5544                 {
5545                         // there is no iterator += operator :(
5546                         //if (count2 != clientlist.end())
5547                         //{
5548                                 count2++;
5549                         //}
5550                 }
5551         }
5552         
5553         // set up select call
5554         for (count = 0; count < boundPortCount; count++)
5555         {
5556                 FD_SET (openSockfd[count], &selectFds);
5557         }
5558
5559         tv.tv_usec = 1;
5560         selectResult = select(MAXSOCKS, &selectFds, NULL, NULL, &tv);
5561
5562         /* select is reporting a waiting socket. Poll them all to find out which */
5563         if (selectResult > 0)
5564         {
5565                 char target[MAXBUF], resolved[MAXBUF];
5566                 for (count = 0; count < boundPortCount; count++)                
5567                 {
5568                         if (FD_ISSET (openSockfd[count], &selectFds))
5569                         {
5570                                 length = sizeof (client);
5571                                 incomingSockfd = accept (openSockfd[count], (struct sockaddr *) &client, &length);
5572                               
5573                                 address_cache::iterator iter = IP.find(client.sin_addr);
5574                                 bool iscached = false;
5575                                 if (iter == IP.end())
5576                                 {
5577                                         /* ip isn't in cache, add it */
5578                                         strncpy (target, (char *) inet_ntoa (client.sin_addr), MAXBUF);
5579                                         if(CleanAndResolve(resolved, target) != TRUE)
5580                                         {
5581                                                 strncpy(resolved,target,MAXBUF);
5582                                         }
5583                                         /* hostname now in 'target' */
5584                                         IP[client.sin_addr] = new string(resolved);
5585                                         /* hostname in cache */
5586                                 }
5587                                 else
5588                                 {
5589                                         /* found ip (cached) */
5590                                         strncpy(resolved, iter->second->c_str(), MAXBUF);
5591                                         iscached = true;
5592                                 }
5593                         
5594                                 if (incomingSockfd < 0)
5595                                 {
5596                                         WriteOpers("*** WARNING: Accept failed on port %d (%s)", ports[count],target);
5597                                         log(DEBUG,"InspIRCd: accept failed: %d",ports[count]);
5598                                 }
5599                                 else
5600                                 {
5601                                         AddClient(incomingSockfd, resolved, ports[count], iscached);
5602                                         log(DEBUG,"InspIRCd: adding client on port %d fd=%d",ports[count],incomingSockfd);
5603                                 }
5604                                 goto label;
5605                         }
5606                 }
5607         }
5608         label:
5609         if(0) {}; // "Label must be followed by a statement"... so i gave it one.
5610 }
5611 /* not reached */
5612 close (incomingSockfd);
5613 }
5614