]> git.netwichtig.de Git - user/henk/code/inspircd.git/blob - src/inspircd.cpp
1398ec90c131795f9d7ea595b17849dfdf2272c9
[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                         if (u)
2754                         {
2755                                 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);
2756                         }
2757                         WriteServ(user->fd,"315 %s %s :End of /WHO list.",user->nick, parameters[0]);
2758                 }
2759         }
2760         if (pcnt == 2)
2761         {
2762                 if ((!strcmp(parameters[0],"0")) || (!strcmp(parameters[0],"*")) && (!strcmp(parameters[1],"o")))
2763                 {
2764                         for (user_hash::const_iterator i = clientlist.begin(); i != clientlist.end(); i++)
2765                         {
2766                                 if ((common_channels(user,i->second)) && (isnick(i->second->nick)))
2767                                 {
2768                                         if (strchr(i->second->modes,'o'))
2769                                         {
2770                                                 WriteServ(user->fd,"352 %s %s %s %s %s %s Hr@ :0 %s",user->nick, user->nick, i->second->ident, i->second->dhost, i->second->server, i->second->nick, i->second->fullname);
2771                                         }
2772                                 }
2773                         }
2774                         WriteServ(user->fd,"315 %s %s :End of /WHO list.",user->nick, user->nick);
2775                         return;
2776                 }
2777         }
2778 }
2779
2780 void handle_wallops(char **parameters, int pcnt, userrec *user)
2781 {
2782         WriteWallOps(user,false,"%s",parameters[0]);
2783 }
2784
2785 void handle_list(char **parameters, int pcnt, userrec *user)
2786 {
2787         chanrec* Ptr;
2788         
2789         WriteServ(user->fd,"321 %s Channel :Users Name",user->nick);
2790         for (chan_hash::const_iterator i = chanlist.begin(); i != chanlist.end(); i++)
2791         {
2792                 if ((!i->second->c_private) && (!i->second->secret))
2793                 {
2794                         WriteServ(user->fd,"322 %s %s %d :[+%s] %s",user->nick,i->second->name,usercount_i(i->second),chanmodes(i->second),i->second->topic);
2795                 }
2796         }
2797         WriteServ(user->fd,"323 %s :End of channel list.",user->nick);
2798 }
2799
2800
2801 void handle_rehash(char **parameters, int pcnt, userrec *user)
2802 {
2803         WriteServ(user->fd,"382 %s %s :Rehashing",user->nick,CleanFilename(CONFIG_FILE));
2804         ReadConfig();
2805         FOREACH_MOD OnRehash();
2806         WriteOpers("%s is rehashing config file %s",user->nick,CleanFilename(CONFIG_FILE));
2807 }
2808
2809
2810 int usercnt(void)
2811 {
2812         return clientlist.size();
2813 }
2814
2815
2816 int usercount_invisible(void)
2817 {
2818         int c = 0;
2819
2820         for (user_hash::const_iterator i = clientlist.begin(); i != clientlist.end(); i++)
2821         {
2822                 if ((i->second->fd) && (isnick(i->second->nick)) && (strchr(i->second->modes,'i'))) c++;
2823         }
2824         return c;
2825 }
2826
2827 int usercount_opers(void)
2828 {
2829         int c = 0;
2830
2831         for (user_hash::const_iterator i = clientlist.begin(); i != clientlist.end(); i++)
2832         {
2833                 if ((i->second->fd) && (isnick(i->second->nick)) && (strchr(i->second->modes,'o'))) c++;
2834         }
2835         return c;
2836 }
2837
2838 int usercount_unknown(void)
2839 {
2840         int c = 0;
2841
2842         for (user_hash::const_iterator i = clientlist.begin(); i != clientlist.end(); i++)
2843         {
2844                 if ((i->second->fd) && (i->second->registered != 7))
2845                         c++;
2846         }
2847         return c;
2848 }
2849
2850 long chancount(void)
2851 {
2852         return chanlist.size();
2853 }
2854
2855 long count_servs(void)
2856 {
2857         int c = 0;
2858         //for (int j = 0; j < 255; j++)
2859         //{
2860         //      if (servers[j] != NULL)
2861         //              c++;
2862         //}
2863         return c;
2864 }
2865
2866 long servercount(void)
2867 {
2868         return count_servs()+1;
2869 }
2870
2871 long local_count()
2872 {
2873         int c = 0;
2874         for (user_hash::const_iterator i = clientlist.begin(); i != clientlist.end(); i++)
2875         {
2876                 if ((i->second->fd) && (isnick(i->second->nick)) && (!strcasecmp(i->second->server,ServerName))) c++;
2877         }
2878         return c;
2879 }
2880
2881
2882 void handle_lusers(char **parameters, int pcnt, userrec *user)
2883 {
2884         WriteServ(user->fd,"251 %s :There are %d users and %d invisible on %d servers",user->nick,usercnt()-usercount_invisible(),usercount_invisible(),servercount());
2885         WriteServ(user->fd,"252 %s %d :operator(s) online",user->nick,usercount_opers());
2886         WriteServ(user->fd,"253 %s %d :unknown connections",user->nick,usercount_unknown());
2887         WriteServ(user->fd,"254 %s %d :channels formed",user->nick,chancount());
2888         WriteServ(user->fd,"254 %s :I have %d clients and %d servers",user->nick,local_count(),count_servs());
2889 }
2890
2891 void handle_admin(char **parameters, int pcnt, userrec *user)
2892 {
2893         WriteServ(user->fd,"256 %s :Administrative info for %s",user->nick,ServerName);
2894         WriteServ(user->fd,"257 %s :Name     - %s",user->nick,AdminName);
2895         WriteServ(user->fd,"258 %s :Nickname - %s",user->nick,AdminNick);
2896         WriteServ(user->fd,"258 %s :E-Mail   - %s",user->nick,AdminEmail);
2897 }
2898
2899 void ShowMOTD(userrec *user)
2900 {
2901         if (!MOTD.size())
2902         {
2903                 WriteServ(user->fd,"422 %s :Message of the day file is missing.",user->nick);
2904                 return;
2905         }
2906         WriteServ(user->fd,"375 %s :- %s message of the day",user->nick,ServerName);
2907         for (int i = 0; i != MOTD.size(); i++)
2908         {
2909                                 WriteServ(user->fd,"372 %s :- %s",user->nick,MOTD[i].c_str());
2910         }
2911         WriteServ(user->fd,"376 %s :End of %s message of the day.",user->nick,ServerName);
2912 }
2913
2914 void ShowRULES(userrec *user)
2915 {
2916         if (!RULES.size())
2917         {
2918                 WriteServ(user->fd,"NOTICE %s :Rules file is missing.",user->nick);
2919                 return;
2920         }
2921         WriteServ(user->fd,"NOTICE %s :%s rules",user->nick,ServerName);
2922         for (int i = 0; i != RULES.size(); i++)
2923         {
2924                                 WriteServ(user->fd,"NOTICE %s :%s",user->nick,RULES[i].c_str());
2925         }
2926         WriteServ(user->fd,"NOTICE %s :End of %s rules.",user->nick,ServerName);
2927 }
2928
2929 /* shows the message of the day, and any other on-logon stuff */
2930 void ConnectUser(userrec *user)
2931 {
2932         user->registered = 7;
2933         user->idle_lastmsg = time(NULL);
2934         log(DEBUG,"ConnectUser: %s",user->nick);
2935
2936         if (strcmp(Passwd(user),"") && (!user->haspassed))
2937         {
2938                 kill_link(user,"Invalid password");
2939                 return;
2940         }
2941         if (IsDenied(user))
2942         {
2943                 kill_link(user,"Unauthorised connection");
2944                 return;
2945         }
2946
2947         WriteServ(user->fd,"NOTICE Auth :Welcome to \002%s\002!",Network);
2948         WriteServ(user->fd,"001 %s :Welcome to the %s IRC Network %s!%s@%s",user->nick,Network,user->nick,user->ident,user->host);
2949         WriteServ(user->fd,"002 %s :Your host is %s, running version %s",user->nick,ServerName,VERSION);
2950         WriteServ(user->fd,"003 %s :This server was created %s %s",user->nick,__TIME__,__DATE__);
2951         WriteServ(user->fd,"004 %s :%s %s iowghraAsORVSxNCWqBzvdHtGI lvhopsmntikrRcaqOALQbSeKVfHGCuzN",user->nick,ServerName,VERSION);
2952         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);
2953         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);
2954         ShowMOTD(user);
2955         FOREACH_MOD OnUserConnect(user);
2956         WriteOpers("*** Client connecting on port %d: %s!%s@%s",user->port,user->nick,user->ident,user->host);
2957         
2958         char buffer[MAXBUF];
2959         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);
2960         NetSendToAll(buffer);
2961 }
2962
2963 void handle_version(char **parameters, int pcnt, userrec *user)
2964 {
2965         char Revision[] = "$Revision$";
2966
2967         char *s1 = Revision;
2968         char *savept;
2969         char *v1 = strtok_r(s1," ",&savept);
2970         s1 = savept;
2971         char *v2 = strtok_r(s1," ",&savept);
2972         s1 = savept;
2973         
2974         WriteServ(user->fd,"351 %s :%s Rev. %s %s :%s (O=%d)",user->nick,VERSION,v2,ServerName,SYSTEM,OPTIMISATION);
2975 }
2976
2977 void handle_ping(char **parameters, int pcnt, userrec *user)
2978 {
2979         WriteServ(user->fd,"PONG %s :%s",ServerName,parameters[0]);
2980 }
2981
2982 void handle_pong(char **parameters, int pcnt, userrec *user)
2983 {
2984         // set the user as alive so they survive to next ping
2985         user->lastping = 1;
2986 }
2987
2988 void handle_motd(char **parameters, int pcnt, userrec *user)
2989 {
2990         ShowMOTD(user);
2991 }
2992
2993 void handle_rules(char **parameters, int pcnt, userrec *user)
2994 {
2995         ShowRULES(user);
2996 }
2997
2998 void handle_user(char **parameters, int pcnt, userrec *user)
2999 {
3000         if (user->registered < 3)
3001         {
3002                 if (isident(parameters[0]) == 0) {
3003                         // This kinda Sucks, According to the RFC thou, its either this,
3004                         // or "You have already registered" :p -- Craig
3005                         WriteServ(user->fd,"461 %s USER :Not enough parameters",user->nick);
3006                 }
3007                 else {
3008                         WriteServ(user->fd,"NOTICE Auth :No ident response, ident prefixed with ~");
3009                         strcpy(user->ident,"~"); /* we arent checking ident... but these days why bother anyway? */
3010                         strncat(user->ident,parameters[0],IDENTMAX);
3011                         strncpy(user->fullname,parameters[3],128);
3012                         user->registered = (user->registered | 1);
3013                 }
3014         }
3015         else
3016         {
3017                 WriteServ(user->fd,"462 %s :You may not reregister",user->nick);
3018                 return;
3019         }
3020         /* parameters 2 and 3 are local and remote hosts, ignored when sent by client connection */
3021         if (user->registered == 3)
3022         {
3023                 /* user is registered now, bit 0 = USER command, bit 1 = sent a NICK command */
3024                 ConnectUser(user);
3025         }
3026 }
3027
3028 void handle_userhost(char **parameters, int pcnt, userrec *user)
3029 {
3030         char Return[MAXBUF],junk[MAXBUF];
3031         sprintf(Return,"302 %s :",user->nick);
3032         for (int i = 0; i < pcnt; i++)
3033         {
3034                 userrec *u = Find(parameters[i]);
3035                 if (u)
3036                 {
3037                         if (strchr(u->modes,'o'))
3038                         {
3039                                 sprintf(junk,"%s*=+%s@%s ",u->nick,u->ident,u->host);
3040                                 strcat(Return,junk);
3041                         }
3042                         else
3043                         {
3044                                 sprintf(junk,"%s=+%s@%s ",u->nick,u->ident,u->host);
3045                                 strcat(Return,junk);
3046                         }
3047                 }
3048         }
3049         WriteServ(user->fd,Return);
3050 }
3051
3052
3053 void handle_ison(char **parameters, int pcnt, userrec *user)
3054 {
3055         char Return[MAXBUF];
3056         sprintf(Return,"303 %s :",user->nick);
3057         for (int i = 0; i < pcnt; i++)
3058         {
3059                 userrec *u = Find(parameters[i]);
3060                 if (u)
3061                 {
3062                         strcat(Return,u->nick);
3063                         strcat(Return," ");
3064                 }
3065         }
3066         WriteServ(user->fd,Return);
3067 }
3068
3069
3070 void handle_away(char **parameters, int pcnt, userrec *user)
3071 {
3072         if (pcnt)
3073         {
3074                 strcpy(user->awaymsg,parameters[0]);
3075                 WriteServ(user->fd,"306 %s :You have been marked as being away",user->nick);
3076         }
3077         else
3078         {
3079                 strcpy(user->awaymsg,"");
3080                 WriteServ(user->fd,"305 %s :You are no longer marked as being away",user->nick);
3081         }
3082 }
3083
3084 void handle_whowas(char **parameters, int pcnt, userrec* user)
3085 {
3086         user_hash::iterator i = whowas.find(parameters[0]);
3087
3088         if (i == whowas.end())
3089         {
3090                 WriteServ(user->fd,"406 %s %s :There was no such nickname",user->nick,parameters[0]);
3091                 WriteServ(user->fd,"369 %s %s :End of WHOWAS",user->nick,parameters[0]);
3092         }
3093         else
3094         {
3095                 time_t rawtime = i->second->signon;
3096                 tm *timeinfo;
3097                 char b[MAXBUF];
3098                 
3099                 timeinfo = localtime(&rawtime);
3100                 strcpy(b,asctime(timeinfo));
3101                 b[strlen(b)-1] = '\0';
3102                 
3103                 WriteServ(user->fd,"314 %s %s %s %s * :%s",user->nick,i->second->nick,i->second->ident,i->second->dhost,i->second->fullname);
3104                 WriteServ(user->fd,"312 %s %s %s :%s",user->nick,i->second->nick,i->second->server,b);
3105                 WriteServ(user->fd,"369 %s %s :End of WHOWAS",user->nick,parameters[0]);
3106         }
3107
3108 }
3109
3110 void handle_trace(char **parameters, int pcnt, userrec *user)
3111 {
3112         for (user_hash::iterator i = clientlist.begin(); i != clientlist.end(); i++)
3113         {
3114                 if (i->second)
3115                 {
3116                         if (isnick(i->second->nick))
3117                         {
3118                                 if (strchr(i->second->modes,'o'))
3119                                 {
3120                                         WriteServ(user->fd,"205 %s :Oper 0 %s",user->nick,i->second->nick);
3121                                 }
3122                                 else
3123                                 {
3124                                         WriteServ(user->fd,"204 %s :User 0 %s",user->nick,i->second->nick);
3125                                 }
3126                         }
3127                         else
3128                         {
3129                                 WriteServ(user->fd,"203 %s :???? 0 [%s]",user->nick,i->second->host);
3130                         }
3131                 }
3132         }
3133 }
3134
3135 void handle_modules(char **parameters, int pcnt, userrec *user)
3136 {
3137         for (int i = 0; i < module_names.size(); i++)
3138         {
3139                         Version V = modules[i]->GetVersion();
3140                         char modulename[MAXBUF];
3141                         strncpy(modulename,module_names[i].c_str(),256);
3142                         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));
3143         }
3144 }
3145
3146 // calls a handler function for a command
3147
3148 void call_handler(const char* commandname,char **parameters, int pcnt, userrec *user)
3149 {
3150                 for (int i = 0; i < cmdlist.size(); i++)
3151                 {
3152                         if (!strcasecmp(cmdlist[i].command,commandname))
3153                         {
3154                                 if (cmdlist[i].handler_function)
3155                                 {
3156                                         if (pcnt>=cmdlist[i].min_params)
3157                                         {
3158                                                 if (strchr(user->modes,cmdlist[i].flags_needed))
3159                                                 {
3160                                                         cmdlist[i].handler_function(parameters,pcnt,user);
3161                                                 }
3162                                         }
3163                                 }
3164                         }
3165                 }
3166 }
3167
3168 void handle_stats(char **parameters, int pcnt, userrec *user)
3169 {
3170         if (pcnt != 1)
3171         {
3172                 return;
3173         }
3174         if (strlen(parameters[0])>1)
3175         {
3176                 /* make the stats query 1 character long */
3177                 parameters[0][1] = '\0';
3178         }
3179
3180         /* stats m (list number of times each command has been used, plus bytecount) */
3181         if (!strcasecmp(parameters[0],"m"))
3182         {
3183                 for (int i = 0; i < cmdlist.size(); i++)
3184                 {
3185                         if (cmdlist[i].handler_function)
3186                         {
3187                                 if (cmdlist[i].use_count)
3188                                 {
3189                                         /* RPL_STATSCOMMANDS */
3190                                         WriteServ(user->fd,"212 %s %s %d %d",user->nick,cmdlist[i].command,cmdlist[i].use_count,cmdlist[i].total_bytes);
3191                                 }
3192                         }
3193                 }
3194                         
3195         }
3196
3197         /* stats z (debug and memory info) */
3198         if (!strcasecmp(parameters[0],"z"))
3199         {
3200                 WriteServ(user->fd,"249 %s :Users(HASH_MAP) %d (%d bytes, %d buckets)",user->nick,clientlist.size(),clientlist.size()*sizeof(userrec),clientlist.bucket_count());
3201                 WriteServ(user->fd,"249 %s :Channels(HASH_MAP) %d (%d bytes, %d buckets)",user->nick,chanlist.size(),chanlist.size()*sizeof(chanrec),chanlist.bucket_count());
3202                 WriteServ(user->fd,"249 %s :Commands(VECTOR) %d (%d bytes)",user->nick,cmdlist.size(),cmdlist.size()*sizeof(command_t));
3203                 WriteServ(user->fd,"249 %s :MOTD(VECTOR) %d, RULES(VECTOR) %d",user->nick,MOTD.size(),RULES.size());
3204                 WriteServ(user->fd,"249 %s :address_cache(HASH_MAP) %d (%d buckets)",user->nick,IP.size(),IP.bucket_count());
3205                 WriteServ(user->fd,"249 %s :Modules(VECTOR) %d (%d)",user->nick,modules.size(),modules.size()*sizeof(Module));
3206                 WriteServ(user->fd,"249 %s :ClassFactories(VECTOR) %d (%d)",user->nick,factory.size(),factory.size()*sizeof(ircd_module));
3207                 WriteServ(user->fd,"249 %s :Ports(STATIC_ARRAY) %d",user->nick,boundPortCount);
3208         }
3209         
3210         /* stats o */
3211         if (!strcasecmp(parameters[0],"o"))
3212         {
3213                 for (int i = 0; i < ConfValueEnum("oper",&config_f); i++)
3214                 {
3215                         char LoginName[MAXBUF];
3216                         char HostName[MAXBUF];
3217                         char OperType[MAXBUF];
3218                         ConfValue("oper","name",i,LoginName,&config_f);
3219                         ConfValue("oper","host",i,HostName,&config_f);
3220                         ConfValue("oper","type",i,OperType,&config_f);
3221                         WriteServ(user->fd,"243 %s O %s * %s %s 0",user->nick,HostName,LoginName,OperType);
3222                 }
3223         }
3224         
3225         /* stats l (show user I/O stats) */
3226         if (!strcasecmp(parameters[0],"l"))
3227         {
3228                 WriteServ(user->fd,"211 %s :server:port nick bytes_in cmds_in bytes_out cmds_out",user->nick);
3229                 for (user_hash::iterator i = clientlist.begin(); i != clientlist.end(); i++)
3230                 {
3231                         if (isnick(i->second->nick))
3232                         {
3233                                 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);
3234                         }
3235                         else
3236                         {
3237                                 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);
3238                         }
3239                         
3240                 }
3241         }
3242         
3243         /* stats u (show server uptime) */
3244         if (!strcasecmp(parameters[0],"u"))
3245         {
3246                 time_t current_time = 0;
3247                 current_time = time(NULL);
3248                 time_t server_uptime = current_time - startup_time;
3249                 struct tm* stime;
3250                 stime = gmtime(&server_uptime);
3251                 /* i dont know who the hell would have an ircd running for over a year nonstop, but
3252                  * Craig suggested this, and it seemed a good idea so in it went */
3253                 if (stime->tm_year > 70)
3254                 {
3255                         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);
3256                 }
3257                 else
3258                 {
3259                         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);
3260                 }
3261         }
3262
3263         WriteServ(user->fd,"219 %s %s :End of /STATS report",user->nick,parameters[0]);
3264         WriteOpers("*** Notice: Stats '%s' requested by %s (%s@%s)",parameters[0],user->nick,user->ident,user->host);
3265         
3266 }
3267
3268 void handle_connect(char **parameters, int pcnt, userrec *user)
3269 {
3270         char Link_ServerName[1024];
3271         char Link_IPAddr[1024];
3272         char Link_Port[1024];
3273         char Link_Pass[1024];
3274         int LinkPort;
3275         bool found = false;
3276         
3277         for (int i = 0; i < ConfValueEnum("link",&config_f); i++)
3278         {
3279                 if (!found)
3280                 {
3281                         ConfValue("link","name",i,Link_ServerName,&config_f);
3282                         ConfValue("link","ipaddr",i,Link_IPAddr,&config_f);
3283                         ConfValue("link","port",i,Link_Port,&config_f);
3284                         ConfValue("link","sendpass",i,Link_Pass,&config_f);
3285                         log(DEBUG,"(%d) Comparing against name='%s', ipaddr='%s', port='%s', recvpass='%s'",i,Link_ServerName,Link_IPAddr,Link_Port,Link_Pass);
3286                         LinkPort = atoi(Link_Port);
3287                         if (match(Link_ServerName,parameters[0])) {
3288                                 found = true;
3289                                 break;
3290                         }
3291                 }
3292         }
3293         
3294         if (!found) {
3295                 WriteServ(user->fd,"NOTICE %s :*** Failed to connect to %s: No servers matching this pattern are configured for linking.",user->nick,parameters[0]);
3296                 return;
3297         }
3298         
3299         // TODO: Perform a check here to stop a server being linked twice!
3300
3301         WriteServ(user->fd,"NOTICE %s :*** Connecting to %s (%s) port %s...",user->nick,Link_ServerName,Link_IPAddr,Link_Port);
3302
3303         if (me[defaultRoute])
3304         {
3305                 me[defaultRoute]->BeginLink(Link_IPAddr,LinkPort,Link_Pass,Link_ServerName,me[defaultRoute]->port);
3306                 return;
3307         }
3308         else
3309         {
3310                 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);
3311         }
3312 }
3313
3314 void DoSplitEveryone()
3315 {
3316         bool go_again = true;
3317         while (go_again)
3318         {
3319                 go_again = false;
3320                 for (int i = 0; i < 32; i++)
3321                 {
3322                         if (me[i] != NULL)
3323                         {
3324                                 for (vector<ircd_connector>::iterator j = me[i]->connectors.begin(); j != me[i]->connectors.end(); j++)
3325                                 {
3326                                         if (strcasecmp(j->GetServerName().c_str(),ServerName))
3327                                         {
3328                                                 j->routes.clear();
3329                                                 j->CloseConnection();
3330                                                 me[i]->connectors.erase(j);
3331                                                 go_again = true;
3332                                                 break;
3333                                         }
3334                                 }
3335                         }
3336                 }
3337         }
3338         log(DEBUG,"Removed server. Will remove clients...");
3339         // iterate through the userlist and remove all users on this server.
3340         // because we're dealing with a mesh, we dont have to deal with anything
3341         // "down-route" from this server (nice huh)
3342         go_again = true;
3343         char reason[MAXBUF];
3344         while (go_again)
3345         {
3346                 go_again = false;
3347                 for (user_hash::const_iterator u = clientlist.begin(); u != clientlist.end(); u++)
3348                 {
3349                         if (strcasecmp(u->second->server,ServerName))
3350                         {
3351                                 snprintf(reason,MAXBUF,"%s %s",ServerName,u->second->server);
3352                                 kill_link(u->second,reason);
3353                                 go_again = true;
3354                                 break;
3355                         }
3356                 }
3357         }
3358 }
3359
3360
3361
3362 void handle_squit(char **parameters, int pcnt, userrec *user)
3363 {
3364         // send out an squit across the mesh and then clear the server list (for local squit)
3365         if (!pcnt)
3366         {
3367                 WriteOpers("SQUIT command issued by %s",user->nick);
3368                 char buffer[MAXBUF];
3369                 snprintf(buffer,MAXBUF,"& %s",ServerName);
3370                 NetSendToAll(buffer);
3371                 DoSplitEveryone();
3372         }
3373         else
3374         {
3375                 WriteServ(user->fd,"NOTICE :*** Remote SQUIT not supported yet.");
3376         }
3377 }
3378
3379 char islast(const char* s)
3380 {
3381         char c = '`';
3382         for (int j = 0; j < 32; j++)
3383         {
3384                 if (me[j] != NULL)
3385                 {
3386                         for (int k = 0; k < me[j]->connectors.size(); k++)
3387                         {
3388                                 if (strcasecmp(me[j]->connectors[k].GetServerName().c_str(),s))
3389                                 {
3390                                         c = '|';
3391                                 }
3392                                 if (!strcasecmp(me[j]->connectors[k].GetServerName().c_str(),s))
3393                                 {
3394                                         c = '`';
3395                                 }
3396                         }
3397                 }
3398         }
3399         return c;
3400 }
3401
3402 long map_count(const char* s)
3403 {
3404         int c = 0;
3405         for (user_hash::const_iterator i = clientlist.begin(); i != clientlist.end(); i++)
3406         {
3407                 if ((i->second->fd) && (isnick(i->second->nick)) && (!strcasecmp(i->second->server,s))) c++;
3408         }
3409         return c;
3410 }
3411
3412 void handle_links(char **parameters, int pcnt, userrec *user)
3413 {
3414         WriteServ(user->fd,"364 %s %s %s :0 %s",user->nick,ServerName,ServerName,ServerDesc);
3415         for (int j = 0; j < 32; j++)
3416         {
3417                 if (me[j] != NULL)
3418                 {
3419                         for (int k = 0; k < me[j]->connectors.size(); k++)
3420                         {
3421                                 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());
3422                         }
3423                 }
3424         }
3425         WriteServ(user->fd,"365 %s * :End of /LINKS list.",user->nick);
3426 }
3427
3428 void handle_map(char **parameters, int pcnt, userrec *user)
3429 {
3430         char line[MAXBUF];
3431         snprintf(line,MAXBUF,"006 %s :%s",user->nick,ServerName);
3432         while (strlen(line) < 50)
3433                 strcat(line," ");
3434         WriteServ(user->fd,"%s%d (%.2f%%)",line,local_count(),(float)(((float)local_count()/(float)usercnt())*100));
3435         for (int j = 0; j < 32; j++)
3436         {
3437                 if (me[j] != NULL)
3438                 {
3439                         for (int k = 0; k < me[j]->connectors.size(); k++)
3440                         {
3441                                 snprintf(line,MAXBUF,"006 %s :%c-%s",user->nick,islast(me[j]->connectors[k].GetServerName().c_str()),me[j]->connectors[k].GetServerName().c_str());
3442                                 while (strlen(line) < 50)
3443                                         strcat(line," ");
3444                                 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));
3445                         }
3446                 }
3447         }
3448         WriteServ(user->fd,"007 %s :End of /MAP",user->nick);
3449 }
3450
3451
3452 void handle_oper(char **parameters, int pcnt, userrec *user)
3453 {
3454         char LoginName[MAXBUF];
3455         char Password[MAXBUF];
3456         char OperType[MAXBUF];
3457         char TypeName[MAXBUF];
3458         char Hostname[MAXBUF];
3459         int i,j;
3460
3461         for (int i = 0; i < ConfValueEnum("oper",&config_f); i++)
3462         {
3463                 ConfValue("oper","name",i,LoginName,&config_f);
3464                 ConfValue("oper","password",i,Password,&config_f);
3465                 if ((!strcmp(LoginName,parameters[0])) && (!strcmp(Password,parameters[1])))
3466                 {
3467                         /* correct oper credentials */
3468                         ConfValue("oper","type",i,OperType,&config_f);
3469                         WriteOpers("*** %s (%s@%s) is now an IRC operator of type %s",user->nick,user->ident,user->host,OperType);
3470                         WriteServ(user->fd,"381 %s :You are now an IRC operator of type %s",user->nick,OperType);
3471                         WriteServ(user->fd,"MODE %s :+o",user->nick);
3472                         for (j =0; j < ConfValueEnum("type",&config_f); j++)
3473                         {
3474                                 ConfValue("type","name",j,TypeName,&config_f);
3475                                 if (!strcmp(TypeName,OperType))
3476                                 {
3477                                         /* found this oper's opertype */
3478                                         ConfValue("type","host",j,Hostname,&config_f);
3479                                         ChangeDisplayedHost(user,Hostname);
3480                                 }
3481                         }
3482                         if (!strchr(user->modes,'o'))
3483                         {
3484                                 strcat(user->modes,"o");
3485                         }
3486                         FOREACH_MOD OnOper(user);
3487                         return;
3488                 }
3489         }
3490         /* no such oper */
3491         WriteServ(user->fd,"491 %s :Invalid oper credentials",user->nick);
3492         WriteOpers("*** WARNING! Failed oper attempt by %s!%s@%s!",user->nick,user->ident,user->host);
3493 }
3494
3495 void handle_nick(char **parameters, int pcnt, userrec *user)
3496 {
3497         if (pcnt < 1) 
3498         {
3499                 log(DEBUG,"not enough params for handle_nick");
3500                 return;
3501         }
3502         if (!parameters[0])
3503         {
3504                 log(DEBUG,"invalid parameter passed to handle_nick");
3505                 return;
3506         }
3507         if (!strlen(parameters[0]))
3508         {
3509                 log(DEBUG,"zero length new nick passed to handle_nick");
3510                 return;
3511         }
3512         if (!user)
3513         {
3514                 log(DEBUG,"invalid user passed to handle_nick");
3515                 return;
3516         }
3517         if (!user->nick)
3518         {
3519                 log(DEBUG,"invalid old nick passed to handle_nick");
3520                 return;
3521         }
3522         if (!strcasecmp(user->nick,parameters[0]))
3523         {
3524                 log(DEBUG,"old nick is new nick, skipping");
3525                 return;
3526         }
3527         else
3528         {
3529                 if (strlen(parameters[0]) > 1)
3530                 {
3531                         if (parameters[0][0] == ':')
3532                         {
3533                                 *parameters[0]++;
3534                         }
3535                 }
3536                 if ((Find(parameters[0])) && (Find(parameters[0]) != user))
3537                 {
3538                         WriteServ(user->fd,"433 %s %s :Nickname is already in use.",user->nick,parameters[0]);
3539                         return;
3540                 }
3541         }
3542         if (isnick(parameters[0]) == 0)
3543         {
3544                 WriteServ(user->fd,"432 %s %s :Erroneous Nickname",user->nick,parameters[0]);
3545                 return;
3546         }
3547
3548         if (user->registered == 7)
3549         {
3550                 WriteCommon(user,"NICK %s",parameters[0]);
3551                 
3552                 // Q token must go to ALL servers!!!
3553                 char buffer[MAXBUF];
3554                 snprintf(buffer,MAXBUF,"n %s %s",user->nick,parameters[0]);
3555                 NetSendToAll(buffer);
3556                 
3557         }
3558         
3559         /* change the nick of the user in the users_hash */
3560         user = ReHashNick(user->nick, parameters[0]);
3561         /* actually change the nick within the record */
3562         if (!user) return;
3563         if (!user->nick) return;
3564
3565         strncpy(user->nick, parameters[0],NICKMAX);
3566
3567         log(DEBUG,"new nick set: %s",user->nick);
3568         
3569         if (user->registered < 3)
3570                 user->registered = (user->registered | 2);
3571         if (user->registered == 3)
3572         {
3573                 /* user is registered now, bit 0 = USER command, bit 1 = sent a NICK command */
3574                 ConnectUser(user);
3575         }
3576         log(DEBUG,"exit nickchange: %s",user->nick);
3577 }
3578
3579 void force_nickchange(userrec* user,const char* newnick)
3580 {
3581         char nick[MAXBUF];
3582         strcpy(nick,"");
3583         
3584         if (user)
3585         {
3586                 if (newnick)
3587                 {
3588                         strncpy(nick,newnick,MAXBUF);
3589                 }
3590                 if (user->registered == 7)
3591                 {
3592                         char* pars[1];
3593                         pars[0] = nick;
3594                         handle_nick(pars,1,user);
3595                 }
3596         }
3597 }
3598                                 
3599
3600 int process_parameters(char **command_p,char *parameters)
3601 {
3602         int i = 0;
3603         int j = 0;
3604         int q = 0;
3605         q = strlen(parameters);
3606         if (!q)
3607         {
3608                 /* no parameters, command_p invalid! */
3609                 return 0;
3610         }
3611         if (parameters[0] == ':')
3612         {
3613                 command_p[0] = parameters+1;
3614                 return 1;
3615         }
3616         if (q)
3617         {
3618                 if ((strchr(parameters,' ')==NULL) || (parameters[0] == ':'))
3619                 {
3620                         /* only one parameter */
3621                         command_p[0] = parameters;
3622                         if (parameters[0] == ':')
3623                         {
3624                                 if (strchr(parameters,' ') != NULL)
3625                                 {
3626                                         command_p[0]++;
3627                                 }
3628                         }
3629                         return 1;
3630                 }
3631         }
3632         command_p[j++] = parameters;
3633         for (int i = 0; i <= q; i++)
3634         {
3635                 if (parameters[i] == ' ')
3636                 {
3637                         command_p[j++] = parameters+i+1;
3638                         parameters[i] = '\0';
3639                         if (command_p[j-1][0] == ':')
3640                         {
3641                                 *command_p[j-1]++; /* remove dodgy ":" */
3642                                 break;
3643                                 /* parameter like this marks end of the sequence */
3644                         }
3645                 }
3646         }
3647         return j; /* returns total number of items in the list */
3648 }
3649
3650 void process_command(userrec *user, char* cmd)
3651 {
3652         char *parameters;
3653         char *command;
3654         char *command_p[127];
3655         char p[MAXBUF], temp[MAXBUF];
3656         int i, j, items, cmd_found;
3657
3658         for (int i = 0; i < 127; i++)
3659                 command_p[i] = NULL;
3660
3661         if (!user)
3662         {
3663                 return;
3664         }
3665         if (!cmd)
3666         {
3667                 return;
3668         }
3669         if (!strcmp(cmd,""))
3670         {
3671                 return;
3672         }
3673         
3674         int total_params = 0;
3675         if (strlen(cmd)>2)
3676         {
3677                 for (int q = 0; q < strlen(cmd)-1; q++)
3678                 {
3679                         if ((cmd[q] == ' ') && (cmd[q+1] == ':'))
3680                         {
3681                                 total_params++;
3682                                 // found a 'trailing', we dont count them after this.
3683                                 break;
3684                         }
3685                         if (cmd[q] == ' ')
3686                                 total_params++;
3687                 }
3688         }
3689         
3690         // another phidjit bug...
3691         if (total_params > 126)
3692         {
3693                 kill_link(user,"Protocol violation (1)");
3694                 return;
3695         }
3696         
3697         strcpy(temp,cmd);
3698
3699         std::string tmp = cmd;
3700         for (int i = 0; i <= MODCOUNT; i++)
3701         {
3702                 std::string oldtmp = tmp;
3703                 modules[i]->OnServerRaw(tmp,true);
3704                 if (oldtmp != tmp)
3705                 {
3706                         log(DEBUG,"A Module changed the input string!");
3707                         log(DEBUG,"New string: %s",tmp.c_str());
3708                         log(DEBUG,"Old string: %s",oldtmp.c_str());
3709                         break;
3710                 }
3711         }
3712         strncpy(cmd,tmp.c_str(),MAXBUF);
3713         strcpy(temp,cmd);
3714
3715         if (!strchr(cmd,' '))
3716         {
3717                 /* no parameters, lets skip the formalities and not chop up
3718                  * the string */
3719                 log(DEBUG,"About to preprocess command with no params");
3720                 items = 0;
3721                 command_p[0] = NULL;
3722                 parameters = NULL;
3723                 for (int i = 0; i <= strlen(cmd); i++)
3724                 {
3725                         cmd[i] = toupper(cmd[i]);
3726                 }
3727                 log(DEBUG,"Preprocess done length=%d",strlen(cmd));
3728                 command = cmd;
3729         }
3730         else
3731         {
3732                 strcpy(cmd,"");
3733                 j = 0;
3734                 /* strip out extraneous linefeeds through mirc's crappy pasting (thanks Craig) */
3735                 for (int i = 0; i < strlen(temp); i++)
3736                 {
3737                         if ((temp[i] != 10) && (temp[i] != 13) && (temp[i] != 0) && (temp[i] != 7))
3738                         {
3739                                 cmd[j++] = temp[i];
3740                                 cmd[j] = 0;
3741                         }
3742                 }
3743                 /* split the full string into a command plus parameters */
3744                 parameters = p;
3745                 strcpy(p," ");
3746                 command = cmd;
3747                 if (strchr(cmd,' '))
3748                 {
3749                         for (int i = 0; i <= strlen(cmd); i++)
3750                         {
3751                                 /* capitalise the command ONLY, leave params intact */
3752                                 cmd[i] = toupper(cmd[i]);
3753                                 /* are we nearly there yet?! :P */
3754                                 if (cmd[i] == ' ')
3755                                 {
3756                                         command = cmd;
3757                                         parameters = cmd+i+1;
3758                                         cmd[i] = '\0';
3759                                         break;
3760                                 }
3761                         }
3762                 }
3763                 else
3764                 {
3765                         for (int i = 0; i <= strlen(cmd); i++)
3766                         {
3767                                 cmd[i] = toupper(cmd[i]);
3768                         }
3769                 }
3770
3771         }
3772         cmd_found = 0;
3773         
3774         if (strlen(command)>MAXCOMMAND)
3775         {
3776                 kill_link(user,"Protocol violation (2)");
3777                 return;
3778         }
3779         
3780         for (int x = 0; x < strlen(command); x++)
3781         {
3782                 if (((command[x] < 'A') || (command[x] > 'Z')) && (command[x] != '.'))
3783                 {
3784                         if (((command[x] < '0') || (command[x]> '9')) && (command[x] != '-'))
3785                         {
3786                                 if (!strchr("@!\"$%^&*(){}[]_-=+;:'#~,.<>/?\\|`",command[x]))
3787                                 {
3788                                         kill_link(user,"Protocol violation (3)");
3789                                         return;
3790                                 }
3791                         }
3792                 }
3793         }
3794
3795         for (int i = 0; i != cmdlist.size(); i++)
3796         {
3797                 if (strcmp(cmdlist[i].command,""))
3798                 {
3799                         if (strlen(command)>=(strlen(cmdlist[i].command))) if (!strncmp(command, cmdlist[i].command,MAXCOMMAND))
3800                         {
3801                                 log(DEBUG,"Found matching command");
3802
3803                                 if (parameters)
3804                                 {
3805                                         if (strcmp(parameters,""))
3806                                         {
3807                                                 items = process_parameters(command_p,parameters);
3808                                         }
3809                                         else
3810                                         {
3811                                                 items = 0;
3812                                                 command_p[0] = NULL;
3813                                         }
3814                                 }
3815                                 else
3816                                 {
3817                                         items = 0;
3818                                         command_p[0] = NULL;
3819                                 }
3820                                 
3821                                 if (user)
3822                                 {
3823                                         log(DEBUG,"Processing command");
3824                                         
3825                                         /* activity resets the ping pending timer */
3826                                         user->nping = time(NULL) + 120;
3827                                         if ((items) < cmdlist[i].min_params)
3828                                         {
3829                                                 log(DEBUG,"process_command: not enough parameters: %s %s",user->nick,command);
3830                                                 WriteServ(user->fd,"461 %s %s :Not enough parameters",user->nick,command);
3831                                                 return;
3832                                         }
3833                                         if ((!strchr(user->modes,cmdlist[i].flags_needed)) && (cmdlist[i].flags_needed))
3834                                         {
3835                                                 log(DEBUG,"process_command: permission denied: %s %s",user->nick,command);
3836                                                 WriteServ(user->fd,"481 %s :Permission Denied- You do not have the required operator privilages",user->nick);
3837                                                 cmd_found = 1;
3838                                                 return;
3839                                         }
3840                                         /* if the command isnt USER, PASS, or NICK, and nick is empty,
3841                                          * deny command! */
3842                                         if ((strncmp(command,"USER",4)) && (strncmp(command,"NICK",4)) && (strncmp(command,"PASS",4)))
3843                                         {
3844                                                 if ((!isnick(user->nick)) || (user->registered != 7))
3845                                                 {
3846                                                         log(DEBUG,"process_command: not registered: %s %s",user->nick,command);
3847                                                         WriteServ(user->fd,"451 %s :You have not registered",command);
3848                                                         return;
3849                                                 }
3850                                         }
3851                                         if ((user->registered == 7) || (!strcmp(command,"USER")) || (!strcmp(command,"NICK")) || (!strcmp(command,"PASS")))
3852                                         {
3853                                                 log(DEBUG,"process_command: handler: %s %s %d",user->nick,command,items);
3854                                                 if (cmdlist[i].handler_function)
3855                                                 {
3856                                                         /* ikky /stats counters */
3857                                                         if (temp)
3858                                                         {
3859                                                                 if (user)
3860                                                                 {
3861                                                                         user->bytes_in += strlen(temp);
3862                                                                         user->cmds_in++;
3863                                                                 }
3864                                                                 cmdlist[i].use_count++;
3865                                                                 cmdlist[i].total_bytes+=strlen(temp);
3866                                                         }
3867
3868                                                         /* WARNING: nothing may come after the
3869                                                          * command handler call, as the handler
3870                                                          * may free the user structure! */
3871
3872                                                         cmdlist[i].handler_function(command_p,items,user);
3873                                                 }
3874                                                 return;
3875                                         }
3876                                         else
3877                                         {
3878                                                 log(DEBUG,"process_command: not registered: %s %s",user->nick,command);
3879                                                 WriteServ(user->fd,"451 %s :You have not registered",command);
3880                                                 return;
3881                                         }
3882                                 }
3883                                 cmd_found = 1;
3884                         }
3885                 }
3886         }
3887         if ((!cmd_found) && (user))
3888         {
3889                 log(DEBUG,"process_command: not in table: %s %s",user->nick,command);
3890                 WriteServ(user->fd,"421 %s %s :Unknown command",user->nick,command);
3891         }
3892 }
3893
3894
3895 void createcommand(char* cmd, handlerfunc f, char flags, int minparams)
3896 {
3897         command_t comm;
3898         /* create the command and push it onto the table */     
3899         strcpy(comm.command,cmd);
3900         comm.handler_function = f;
3901         comm.flags_needed = flags;
3902         comm.min_params = minparams;
3903         comm.use_count = 0;
3904         comm.total_bytes = 0;
3905         cmdlist.push_back(comm);
3906         log(DEBUG,"Added command %s (%d parameters)",cmd,minparams);
3907 }
3908
3909 void SetupCommandTable(void)
3910 {
3911         createcommand("USER",handle_user,0,4);
3912         createcommand("NICK",handle_nick,0,1);
3913         createcommand("QUIT",handle_quit,0,0);
3914         createcommand("VERSION",handle_version,0,0);
3915         createcommand("PING",handle_ping,0,1);
3916         createcommand("PONG",handle_pong,0,1);
3917         createcommand("ADMIN",handle_admin,0,0);
3918         createcommand("PRIVMSG",handle_privmsg,0,2);
3919         createcommand("INFO",handle_info,0,0);
3920         createcommand("TIME",handle_time,0,0);
3921         createcommand("WHOIS",handle_whois,0,1);
3922         createcommand("WALLOPS",handle_wallops,'o',1);
3923         createcommand("NOTICE",handle_notice,0,2);
3924         createcommand("JOIN",handle_join,0,1);
3925         createcommand("NAMES",handle_names,0,1);
3926         createcommand("PART",handle_part,0,1);
3927         createcommand("KICK",handle_kick,0,2);
3928         createcommand("MODE",handle_mode,0,1);
3929         createcommand("TOPIC",handle_topic,0,1);
3930         createcommand("WHO",handle_who,0,1);
3931         createcommand("MOTD",handle_motd,0,0);
3932         createcommand("RULES",handle_rules,0,0);
3933         createcommand("OPER",handle_oper,0,2);
3934         createcommand("LIST",handle_list,0,0);
3935         createcommand("DIE",handle_die,'o',1);
3936         createcommand("RESTART",handle_restart,'o',1);
3937         createcommand("KILL",handle_kill,'o',2);
3938         createcommand("REHASH",handle_rehash,'o',0);
3939         createcommand("LUSERS",handle_lusers,0,0);
3940         createcommand("STATS",handle_stats,0,1);
3941         createcommand("USERHOST",handle_userhost,0,1);
3942         createcommand("AWAY",handle_away,0,0);
3943         createcommand("ISON",handle_ison,0,0);
3944         createcommand("SUMMON",handle_summon,0,0);
3945         createcommand("USERS",handle_users,0,0);
3946         createcommand("INVITE",handle_invite,0,2);
3947         createcommand("PASS",handle_pass,0,1);
3948         createcommand("TRACE",handle_trace,'o',0);
3949         createcommand("WHOWAS",handle_whowas,0,1);
3950         createcommand("CONNECT",handle_connect,'o',1);
3951         createcommand("SQUIT",handle_squit,'o',0);
3952         createcommand("MODULES",handle_modules,'o',0);
3953         createcommand("LINKS",handle_links,0,0);
3954         createcommand("MAP",handle_map,0,0);
3955 }
3956
3957 void process_buffer(const char* cmdbuf,userrec *user)
3958 {
3959         if (!user)
3960         {
3961                 log(DEFAULT,"*** BUG *** process_buffer was given an invalid parameter");
3962                 return;
3963         }
3964         char cmd[MAXBUF];
3965         int i;
3966         if (!cmdbuf)
3967         {
3968                 log(DEFAULT,"*** BUG *** process_buffer was given an invalid parameter");
3969                 return;
3970         }
3971         if (!strcmp(cmdbuf,""))
3972         {
3973                 return;
3974         }
3975         strncpy(cmd,cmdbuf,MAXBUF);
3976         if (!strcmp(cmd,""))
3977         {
3978                 return;
3979         }
3980         if ((cmd[strlen(cmd)-1] == 13) || (cmd[strlen(cmd)-1] == 10))
3981         {
3982                 cmd[strlen(cmd)-1] = '\0';
3983         }
3984         if ((cmd[strlen(cmd)-1] == 13) || (cmd[strlen(cmd)-1] == 10))
3985         {
3986                 cmd[strlen(cmd)-1] = '\0';
3987         }
3988         if (!strcmp(cmd,""))
3989         {
3990                 return;
3991         }
3992         log(DEBUG,"InspIRCd: processing: %s %s",user->nick,cmd);
3993         tidystring(cmd);
3994         if ((user) && (cmd))
3995         {
3996                 process_command(user,cmd);
3997         }
3998 }
3999
4000 void DoSync(serverrec* serv, char* udp_host)
4001 {
4002         char data[MAXBUF];
4003         // send start of sync marker: Y <timestamp>
4004         // at this point the ircd receiving it starts broadcasting this netburst to all ircds
4005         // except the ones its receiving it from.
4006         snprintf(data,MAXBUF,"Y %d",time(NULL));
4007         serv->SendPacket(data,udp_host);
4008         // send users and channels
4009         for (user_hash::iterator u = clientlist.begin(); u != clientlist.end(); u++)
4010         {
4011                 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);
4012                 serv->SendPacket(data,udp_host);
4013                 if (strcmp(chlist(u->second),""))
4014                 {
4015                         snprintf(data,MAXBUF,"J %s %s",u->second->nick,chlist(u->second));
4016                         serv->SendPacket(data,udp_host);
4017                 }
4018         }
4019         // send channel modes, topics etc...
4020         for (chan_hash::iterator c = chanlist.begin(); c != chanlist.end(); c++)
4021         {
4022                 snprintf(data,MAXBUF,"M %s +%s",c->second->name,chanmodes(c->second));
4023                 serv->SendPacket(data,udp_host);
4024                 if (strcmp(c->second->topic,""))
4025                 {
4026                         snprintf(data,MAXBUF,"T %d %s %s :%s",c->second->topicset,c->second->setby,c->second->name,c->second->topic);
4027                         serv->SendPacket(data,udp_host);
4028                 }
4029                 // send current banlist
4030                 
4031                 for (BanList::iterator b = c->second->bans.begin(); b != c->second->bans.end(); b++)
4032                 {
4033                         snprintf(data,MAXBUF,"M %s +b %s",b->set_time,c->second->name,b->data);
4034                         serv->SendPacket(data,udp_host);
4035                 }
4036         }
4037         // send end of sync marker: E <timestamp>
4038         snprintf(data,MAXBUF,"F %d",time(NULL));
4039         serv->SendPacket(data,udp_host);
4040         // ircd sends its serverlist after the end of sync here
4041 }
4042
4043
4044 void handle_V(char token,char* params,serverrec* source,serverrec* reply, char* udp_host)
4045 {
4046         char* src = strtok(params," ");
4047         char* dest = strtok(NULL," :");
4048         char* text = strtok(NULL,"\r\n");
4049         text++;
4050         
4051         userrec* user = Find(src);
4052         if (user)
4053         {
4054                 userrec* dst = Find(dest);
4055                 
4056                 if (dst)
4057                 {
4058                         WriteTo(user, dst, "NOTICE %s :%s", dst->nick, text);
4059                 }
4060                 else
4061                 {
4062                         chanrec* d = FindChan(dest);
4063                         if (d)
4064                         {
4065                                 ChanExceptSender(d, user, "NOTICE %s :%s", d->name, text);
4066                         }
4067                 }
4068         }
4069         
4070 }
4071
4072
4073 void handle_P(char token,char* params,serverrec* source,serverrec* reply, char* udp_host)
4074 {
4075         char* src = strtok(params," ");
4076         char* dest = strtok(NULL," :");
4077         char* text = strtok(NULL,"\r\n");
4078         text++;
4079         
4080         userrec* user = Find(src);
4081         if (user)
4082         {
4083                 userrec* dst = Find(dest);
4084                 
4085                 if (dst)
4086                 {
4087                         WriteTo(user, dst, "PRIVMSG %s :%s", dst->nick, text);
4088                 }
4089                 else
4090                 {
4091                         chanrec* d = FindChan(dest);
4092                         if (d)
4093                         {
4094                                 ChanExceptSender(d, user, "PRIVMSG %s :%s", d->name, text);
4095                         }
4096                 }
4097         }
4098         
4099 }
4100
4101 void handle_i(char token,char* params,serverrec* source,serverrec* reply, char* udp_host)
4102 {
4103         char* nick = strtok(params," ");
4104         char* from = strtok(NULL," ");
4105         char* channel = strtok(NULL," ");
4106         userrec* u = Find(nick);
4107         userrec* user = Find(from);
4108         chanrec* c = FindChan(channel);
4109         if ((c) && (u) && (user))
4110         {
4111                 u->InviteTo(c->name);
4112                 WriteFrom(u->fd,user,"INVITE %s :%s",u->nick,c->name);
4113         }
4114 }
4115
4116 void handle_t(char token,char* params,serverrec* source,serverrec* reply, char* udp_host)
4117 {
4118         char* setby = strtok(params," ");
4119         char* channel = strtok(NULL," :");
4120         char* topic = strtok(NULL,"\r\n");
4121         topic++;
4122         userrec* u = Find(setby);
4123         chanrec* c = FindChan(channel);
4124         if ((c) && (u))
4125         {
4126                 WriteChannelLocal(c,u,"TOPIC %s :%s",c->name,topic);
4127                 strncpy(c->topic,topic,MAXTOPIC);
4128                 strncpy(c->setby,u->nick,NICKMAX);
4129         }       
4130 }
4131         
4132
4133 void handle_T(char token,char* params,serverrec* source,serverrec* reply, char* udp_host)
4134 {
4135         char* tm = strtok(params," ");
4136         char* setby = strtok(NULL," ");
4137         char* channel = strtok(NULL," :");
4138         char* topic = strtok(NULL,"\r\n");
4139         topic++;
4140         time_t TS = atoi(tm);
4141         chanrec* c = FindChan(channel);
4142         if (c)
4143         {
4144                 // in the case of topics and TS, the *NEWER* 
4145                 if (TS <= c->topicset)
4146                 {
4147                         WriteChannelLocal(c,NULL,"TOPIC %s :%s",c->name,topic);
4148                         strncpy(c->topic,topic,MAXTOPIC);
4149                         strncpy(c->setby,setby,NICKMAX);
4150                 }
4151         }       
4152 }
4153         
4154 void handle_M(char token,char* params,serverrec* source,serverrec* reply, char* udp_host)
4155 {
4156         char* pars[128];
4157         char original[MAXBUF],target[MAXBUF];
4158         strncpy(original,params,MAXBUF);
4159         int index = 0;
4160         char* parameter = strtok(params," ");
4161         strncpy(target,parameter,MAXBUF);
4162         while (parameter)
4163         {
4164                 pars[index++] = parameter;
4165                 parameter = strtok(NULL," ");
4166         }
4167         merge_mode(pars,index);
4168         if (FindChan(target))
4169         {
4170                 WriteChannelLocal(FindChan(target), NULL, "MODE %s",original);
4171         }
4172         if (Find(target))
4173         {
4174                 WriteTo(NULL,Find(target),"MODE %s",original);
4175         }
4176 }
4177
4178 // m is modes set by users only (not servers) valid targets are channels or users.
4179
4180 void handle_m(char token,char* params,serverrec* source,serverrec* reply, char* udp_host)
4181 {
4182         // m blah #chatspike +b *!test@*4
4183         char* pars[128];
4184         char original[MAXBUF];
4185         strncpy(original,params,MAXBUF);
4186         
4187         if (!strchr(params,' '))
4188         {
4189                 WriteOpers("WARNING! 'm' token in data stream without any parameters! Something fishy is going on!");
4190                 return;
4191         }
4192         
4193         int index = 0;
4194         
4195         char* src = strtok(params," ");
4196         userrec* user = Find(src);
4197         
4198         if (user)
4199         {
4200                 log(DEBUG,"Found user: %s",user->nick);
4201                 char* parameter = strtok(NULL," ");
4202                 while (parameter)
4203                 {
4204                         pars[index++] = parameter;
4205                         parameter = strtok(NULL," ");
4206                 }
4207                 
4208                 log(DEBUG,"Calling merge_mode2");
4209                 merge_mode2(pars,index,user);
4210         }
4211 }
4212
4213
4214 void handle_L(char token,char* params,serverrec* source,serverrec* reply, char* udp_host)
4215 {
4216         char* nick = strtok(params," ");
4217         char* channel = strtok(NULL," :");
4218         char* reason = strtok(NULL,"\r\n");
4219         userrec* user = Find(nick);
4220         reason++;
4221         if (user)
4222         {
4223                 if (strcmp(reason,""))
4224                 {
4225                         del_channel(user,channel,reason,true);
4226                 }
4227                 else
4228                 {
4229                         del_channel(user,channel,NULL,true);
4230                 }
4231         }
4232 }
4233
4234 void handle_K(char token,char* params,serverrec* source,serverrec* reply, char* udp_host)
4235 {
4236         char* src = strtok(params," ");
4237         char* nick = strtok(NULL," :");
4238         char* reason = strtok(NULL,"\r\n");
4239         char kreason[MAXBUF];
4240         reason++;
4241
4242         userrec* u = Find(nick);
4243         userrec* user = Find(src);
4244         
4245         if ((user) && (u))
4246         {
4247                 WriteTo(user, u, "KILL %s :%s!%s!%s!%s (%s)", u->nick, source->name, ServerName, user->dhost,user->nick,reason);
4248                 WriteOpers("*** Remote kill from %s by %s: %s!%s@%s (%s)",source->name,user->nick,u->nick,u->ident,u->host,reason);
4249                 snprintf(kreason,MAXBUF,"[%s] Killed (%s (%s))",source->name,user->nick,reason);
4250                 kill_link(u,kreason);
4251         }
4252 }
4253
4254 void handle_Q(char token,char* params,serverrec* source,serverrec* reply, char* udp_host)
4255 {
4256         char* nick = strtok(params," :");
4257         char* reason = strtok(NULL,"\r\n");
4258         reason++;
4259
4260         userrec* user = Find(nick);
4261         
4262         if (user)
4263         {
4264                 if (strlen(reason)>MAXQUIT)
4265                 {
4266                         reason[MAXQUIT-1] = '\0';
4267                 }
4268
4269
4270                 WriteCommonExcept(user,"QUIT :%s",reason);
4271
4272                 user_hash::iterator iter = clientlist.find(user->nick);
4273         
4274                 if (iter != clientlist.end())
4275                 {
4276                         log(DEBUG,"deleting user hash value %d",iter->second);
4277                         if ((iter->second) && (user->registered == 7)) {
4278                                 delete iter->second;
4279                         }
4280                         clientlist.erase(iter);
4281                 }
4282
4283                 purge_empty_chans();
4284         }
4285 }
4286
4287 void handle_n(char token,char* params,serverrec* source,serverrec* reply, char* udp_host)
4288 {
4289         char* oldnick = strtok(params," ");
4290         char* newnick = strtok(NULL," ");
4291         
4292         userrec* user = Find(oldnick);
4293         
4294         if (user)
4295         {
4296                 WriteCommon(user,"NICK %s",newnick);
4297                 user = ReHashNick(user->nick, newnick);
4298                 if (!user) return;
4299                 if (!user->nick) return;
4300                 strncpy(user->nick, newnick,NICKMAX);
4301                 log(DEBUG,"new nick set: %s",user->nick);
4302         }
4303 }
4304
4305 // k <SOURCE> <DEST> <CHANNEL> :<REASON>
4306 void handle_k(char token,char* params,serverrec* source,serverrec* reply, char* udp_host)
4307 {
4308         char* src = strtok(params," ");
4309         char* dest = strtok(NULL," ");
4310         char* channel = strtok(NULL," :");
4311         char* reason = strtok(NULL,"\r\n");
4312         reason++;
4313         userrec* s = Find(src);
4314         userrec* d = Find(dest);
4315         chanrec* c = FindChan(channel);
4316         if ((s) && (d) && (c))
4317         {
4318                 kick_channel(s,d,c,reason);
4319         }
4320 }
4321
4322 void handle_AT(char token,char* params,serverrec* source,serverrec* reply, char* udp_host)
4323 {
4324         char* who = strtok(params," :");
4325         char* text = strtok(NULL,"\r\n");
4326         text++;
4327         userrec* s = Find(who);
4328         if (s)
4329         {
4330                 WriteWallOps(s,true,text);
4331         }
4332 }
4333
4334
4335 void handle_N(char token,char* params,serverrec* source,serverrec* reply, char* udp_host)
4336 {
4337         char* tm = strtok(params," ");
4338         char* nick = strtok(NULL," ");
4339         char* host = strtok(NULL," ");
4340         char* dhost = strtok(NULL," ");
4341         char* ident = strtok(NULL," ");
4342         char* modes = strtok(NULL," ");
4343         char* server = strtok(NULL," :");
4344         char* gecos = strtok(NULL,"\r\n");
4345         gecos++;
4346         modes++;
4347         time_t TS = atoi(tm);
4348         user_hash::iterator iter = clientlist.find(nick);
4349         if (iter != clientlist.end())
4350         {
4351                 // nick collision
4352                 WriteOpers("Nickname collision: %s@%s != %s@%s",nick,server,iter->second->nick,iter->second->server);
4353                 char str[MAXBUF];
4354                 snprintf(str,MAXBUF,"Killed (Nick Collision (%s@%s < %s@%s))",nick,server,iter->second->nick,iter->second->server);
4355                 WriteServ(iter->second->fd, "KILL %s :%s",iter->second->nick,str);
4356                 kill_link(iter->second,str);
4357         }
4358         clientlist[nick] = new userrec();
4359         // remote users have an fd of -1. This is so that our Write abstraction
4360         // routines know to route any messages to this record away to whatever server
4361         // theyre on.
4362         clientlist[nick]->fd = -1;
4363         strncpy(clientlist[nick]->nick, nick,NICKMAX);
4364         strncpy(clientlist[nick]->host, host,160);
4365         strncpy(clientlist[nick]->dhost, dhost,160);
4366         strncpy(clientlist[nick]->server, server,256);
4367         strncpy(clientlist[nick]->ident, ident,10); // +1 char to compensate for tilde
4368         strncpy(clientlist[nick]->fullname, gecos,128);
4369         clientlist[nick]->signon = TS;
4370         clientlist[nick]->nping = 0; // this is ignored for a remote user anyway.
4371         clientlist[nick]->lastping = 1;
4372         clientlist[nick]->port = 0; // so is this...
4373         clientlist[nick]->registered = 7; // this however we need to set for them to receive messages and appear online
4374         clientlist[nick]->idle_lastmsg = time(NULL); // this is unrealiable and wont actually be used locally
4375         for (int i = 0; i < MAXCHANS; i++)
4376         {
4377                 clientlist[nick]->chans[i].channel = NULL;
4378                 clientlist[nick]->chans[i].uc_modes = 0;
4379         }
4380 }
4381
4382 void handle_F(char token,char* params,serverrec* source,serverrec* reply, char* udp_host)
4383 {
4384         long tdiff = time(NULL) - atoi(params);
4385         if (tdiff)
4386                 WriteOpers("TS split for %s -> %s: %d",source->name,reply->name,tdiff);
4387 }
4388
4389 void handle_a(char token,char* params,serverrec* source,serverrec* reply, char* udp_host)
4390 {
4391         char* nick = strtok(params," :");
4392         char* gecos = strtok(NULL,"\r\n");
4393         
4394         userrec* user = Find(nick);
4395
4396         if (user)
4397                 strncpy(user->fullname,gecos,MAXBUF);
4398 }
4399
4400 void handle_b(char token,char* params,serverrec* source,serverrec* reply, char* udp_host)
4401 {
4402         char* nick = strtok(params," ");
4403         char* host = strtok(NULL," ");
4404         
4405         userrec* user = Find(nick);
4406
4407         if (user)
4408                 strncpy(user->dhost,host,160);
4409 }
4410
4411 void handle_plus(char token,char* params,serverrec* source,serverrec* reply, char* udp_host)
4412 {
4413         // %s %s %d %d
4414         // + test3.chatspike.net 7010 -2016508415
4415         char* servername = strtok(params," ");
4416         char* ipaddr = strtok(NULL," ");
4417         char* ipport = strtok(NULL," ");
4418         char* cookie = strtok(NULL," ");
4419         log(DEBUG,"*** Connecting back to %s:%d",ipaddr,atoi(ipport));
4420
4421
4422         bool conn_already = false;
4423         for (int i = 0; i < 32; i++)
4424         {
4425                 if (me[i] != NULL)
4426                 {
4427                         for (int j = 0; j < me[i]->connectors.size(); j++)
4428                         {
4429                                 if (!strcasecmp(me[i]->connectors[j].GetServerName().c_str(),servername))
4430                                 {
4431                                         if (me[i]->connectors[j].GetServerPort() == atoi(ipport))
4432                                         {
4433                                                 log(DEBUG,"Already got a connection to %s:%d, ignoring +",ipaddr,atoi(ipport));
4434                                                 conn_already = true;
4435                                         }
4436                                 }
4437                         }
4438                 }
4439         }
4440         if (!conn_already)
4441                 me[defaultRoute]->MeshCookie(ipaddr,atoi(ipport),atoi(cookie),servername);
4442 }
4443
4444 void handle_R(char token,char* params,serverrec* source,serverrec* reply, char* udp_host)
4445 {
4446         char* server = strtok(params," ");
4447         char* data = strtok(NULL,"\r\n");
4448         log(DEBUG,"Forwarded packet '%s' to '%s'",data,server);
4449         NetSendToOne(server,data);
4450 }
4451
4452 void handle_J(char token,char* params,serverrec* source,serverrec* reply, char* udp_host)
4453 {
4454         // IMPORTANT NOTE
4455         // The J token currently has no timestamp - this needs looking at
4456         // because it will allow splitriding.
4457         char* nick = strtok(params," ");
4458         char* channel = strtok(NULL," ");
4459         userrec* user = Find(nick);
4460         while (channel)
4461         {
4462                 if ((user != NULL) && (strcmp(channel,"")))
4463                 {
4464                         char privilage = '\0';
4465                         if (channel[0] != '#')
4466                         {
4467                                 privilage = channel[0];
4468                                 channel++;
4469                         }
4470                         add_channel(user,channel,"",true);
4471
4472                         // now work out the privilages they should have on each channel
4473                         // and send the appropriate servermodes.
4474                         for (int i = 0; i != MAXCHANS; i++)
4475                         {
4476                                 if (user->chans[i].channel)
4477                                 {
4478                                         if (!strcasecmp(user->chans[i].channel->name,channel))
4479                                         {
4480                                                 if (privilage == '@')
4481                                                 {
4482                                                         user->chans[i].uc_modes = user->chans[i].uc_modes | UCMODE_OP;
4483                                                         WriteChannelLocal(user->chans[i].channel, NULL, "MODE %s +o %s",channel,user->nick);
4484                                                 }
4485                                                 if (privilage == '%')
4486                                                 {
4487                                                         user->chans[i].uc_modes = user->chans[i].uc_modes | UCMODE_HOP;
4488                                                         WriteChannelLocal(user->chans[i].channel, NULL, "MODE %s +h %s",channel,user->nick);
4489                                                 }
4490                                                 if (privilage == '+')
4491                                                 {
4492                                                         user->chans[i].uc_modes = user->chans[i].uc_modes | UCMODE_VOICE;
4493                                                         WriteChannelLocal(user->chans[i].channel, NULL, "MODE %s +v %s",channel,user->nick);
4494                                                 }
4495                                         }
4496                                 }
4497                         }
4498
4499                 }
4500                 channel = strtok(NULL," ");
4501         }
4502 }
4503
4504 void NetSendMyRoutingTable()
4505 {
4506         // send out a line saying what is reachable to us.
4507         // E.g. if A is linked to B C and D, send out:
4508         // $ A B C D
4509         // if its only linked to B and D send out:
4510         // $ A B D
4511         // if it has no links, dont even send out the line at all.
4512         char buffer[MAXBUF];
4513         sprintf(buffer,"$ %s",ServerName);
4514         bool sendit = false;
4515         for (int i = 0; i < 32; i++)
4516         {
4517                 if (me[i] != NULL)
4518                 {
4519                         for (int j = 0; j < me[i]->connectors.size(); j++)
4520                         {
4521                                 if (me[i]->connectors[j].GetState() != STATE_DISCONNECTED)
4522                                 {
4523                                         strncat(buffer," ",MAXBUF);
4524                                         strncat(buffer,me[i]->connectors[j].GetServerName().c_str(),MAXBUF);
4525                                         sendit = true;
4526                                 }
4527                         }
4528                 }
4529         }
4530         if (sendit)
4531                 NetSendToAll(buffer);
4532 }
4533
4534 void handle_dollar(char token,char* params,serverrec* source,serverrec* reply, char* udp_host)
4535 {
4536         log(DEBUG,"Storing routing table...");
4537         char* sourceserver = strtok(params," ");
4538         char* server = strtok(NULL," ");
4539         for (int i = 0; i < 32; i++)
4540         {
4541                 if (me[i] != NULL)
4542                 {
4543                         for (int j = 0; j < me[i]->connectors.size(); j++)
4544                         {
4545                                 if (!strcasecmp(me[i]->connectors[j].GetServerName().c_str(),sourceserver))
4546                                 {
4547                                         me[i]->connectors[j].routes.clear();
4548                                         log(DEBUG,"Found entry for source server.");
4549                                         while (server)
4550                                         {
4551                                                 // store each route
4552                                                 me[i]->connectors[j].routes.push_back(server);
4553                                                 log(DEBUG,"*** Stored route: %s -> %s -> %s",ServerName,sourceserver,server);
4554                                                 server = strtok(NULL," ");
4555                                         }
4556                                         return;
4557                                 }
4558                         }
4559                 }
4560         }
4561         log(DEBUG,"Warning! routing table received from nonexistent server!");
4562 }
4563
4564
4565 void DoSplit(const char* params)
4566 {
4567         bool go_again = true;
4568         while (go_again)
4569         {
4570                 go_again = false;
4571                 for (int i = 0; i < 32; i++)
4572                 {
4573                         if (me[i] != NULL)
4574                         {
4575                                 for (vector<ircd_connector>::iterator j = me[i]->connectors.begin(); j != me[i]->connectors.end(); j++)
4576                                 {
4577                                         if (!strcasecmp(j->GetServerName().c_str(),params))
4578                                         {
4579                                                 j->routes.clear();
4580                                                 j->CloseConnection();
4581                                                 me[i]->connectors.erase(j);
4582                                                 go_again = true;
4583                                                 break;
4584                                         }
4585                                 }
4586                         }
4587                 }
4588         }
4589         log(DEBUG,"Removed server. Will remove clients...");
4590         // iterate through the userlist and remove all users on this server.
4591         // because we're dealing with a mesh, we dont have to deal with anything
4592         // "down-route" from this server (nice huh)
4593         go_again = true;
4594         char reason[MAXBUF];
4595         snprintf(reason,MAXBUF,"%s %s",ServerName,params);
4596         while (go_again)
4597         {
4598                 go_again = false;
4599                 for (user_hash::const_iterator u = clientlist.begin(); u != clientlist.end(); u++)
4600                 {
4601                         if (!strcasecmp(u->second->server,params))
4602                         {
4603                                 kill_link(u->second,reason);
4604                                 go_again = true;
4605                                 break;
4606                         }
4607                 }
4608         }
4609 }
4610
4611 // removes a server. Will NOT remove its users!
4612
4613 void RemoveServer(const char* name)
4614 {
4615         bool go_again = true;
4616         while (go_again)
4617         {
4618                 go_again = false;
4619                 for (int i = 0; i < 32; i++)
4620                 {
4621                         if (me[i] != NULL)
4622                         {
4623                                 for (vector<ircd_connector>::iterator j = me[i]->connectors.begin(); j != me[i]->connectors.end(); j++)
4624                                 {
4625                                         if (!strcasecmp(j->GetServerName().c_str(),name))
4626                                         {
4627                                                 j->routes.clear();
4628                                                 j->CloseConnection();
4629                                                 me[i]->connectors.erase(j);
4630                                                 go_again = true;
4631                                                 break;
4632                                         }
4633                                 }
4634                         }
4635                 }
4636         }
4637 }
4638
4639 void handle_amp(char token,char* params,serverrec* source,serverrec* reply, char* udp_host)
4640 {
4641         log(DEBUG,"Netsplit! %s split from mesh, removing!",params);
4642         bool go_again = true;
4643         while (go_again)
4644         {
4645                 go_again = false;
4646                 for (int i = 0; i < 32; i++)
4647                 {
4648                         if (me[i] != NULL)
4649                         {
4650                                 for (vector<ircd_connector>::iterator j = me[i]->connectors.begin(); j != me[i]->connectors.end(); j++)
4651                                 {
4652                                         if (!strcasecmp(j->GetServerName().c_str(),params))
4653                                         {
4654                                                 j->routes.clear();
4655                                                 j->CloseConnection();
4656                                                 me[i]->connectors.erase(j);
4657                                                 go_again = true;
4658                                                 break;
4659                                         }
4660                                 }
4661                         }
4662                 }
4663         }
4664         log(DEBUG,"Removed server. Will remove clients...");
4665         // iterate through the userlist and remove all users on this server.
4666         // because we're dealing with a mesh, we dont have to deal with anything
4667         // "down-route" from this server (nice huh)
4668         go_again = true;
4669         char reason[MAXBUF];
4670         snprintf(reason,MAXBUF,"%s %s",ServerName,params);
4671         while (go_again)
4672         {
4673                 go_again = false;
4674                 for (user_hash::const_iterator u = clientlist.begin(); u != clientlist.end(); u++)
4675                 {
4676                         if (!strcasecmp(u->second->server,params))
4677                         {
4678                                 kill_link(u->second,reason);
4679                                 go_again = true;
4680                                 break;
4681                         }
4682                 }
4683         }
4684 }
4685
4686 long authcookie;
4687
4688
4689 void process_restricted_commands(char token,char* params,serverrec* source,serverrec* reply, char* udp_host,char* ipaddr,int port)
4690 {
4691         char buffer[MAXBUF];
4692
4693         switch(token)
4694         {
4695                 // Y <TS>
4696                 // start netburst
4697                 case 'Y':
4698                         nb_start = time(NULL);
4699                         WriteOpers("Server %s is starting netburst.",udp_host);
4700                         // now broadcast this new servers address out to all servers that are linked to us,
4701                         // except the newcomer. They'll all attempt to connect back to it.
4702                         authcookie = rand()*rand();
4703                         snprintf(buffer,MAXBUF,"~ %d",authcookie);
4704                         NetSendToAll(buffer);
4705                 break;
4706                 // ~
4707                 // Store authcookie
4708                 // once stored, this authcookie permits other servers to log in
4709                 // without user or password, using it.
4710                 case '~':
4711                         auth_cookies.push_back(atoi(params));
4712                         log(DEBUG,"*** Stored auth cookie, will permit servers with auth-cookie %d",atoi(params));
4713                 break;
4714                 // connect back to a server using an authcookie
4715                 case '+':
4716                         handle_plus(token,params,source,reply,udp_host);
4717                 break;
4718                 // routing table
4719                 case '$':
4720                         handle_dollar(token,params,source,reply,udp_host);
4721                 break;
4722                 // node unreachable - we cant route to a server, sooooo we slit it off.
4723                 // servers can generate these for themselves for an squit.
4724                 case '&':
4725                         handle_amp(token,params,source,reply,udp_host);
4726                 break;
4727                 // R <server> <data>
4728                 // redirect token, send all of <data> along to the given 
4729                 // server as this server has been found to still have a route to it
4730                 case 'R':
4731                         handle_R(token,params,source,reply,udp_host);
4732                 break;
4733                 // ?
4734                 // ping
4735                 case '?':
4736                         reply->SendPacket("!",udp_host);
4737                 break;
4738                 // ?
4739                 // pong
4740                 case '!':
4741                 break;
4742                 // *
4743                 // no operation
4744                 case '*':
4745                 break;
4746                 // N <TS> <NICK> <HOST> <DHOST> <IDENT> <MODES> <SERVER> :<GECOS>
4747                 // introduce remote client
4748                 case 'N':
4749                         handle_N(token,params,source,reply,udp_host);
4750                 break;
4751                 // a <NICK> :<GECOS>
4752                 // change GECOS (SETNAME)
4753                 case 'a':
4754                         handle_a(token,params,source,reply,udp_host);
4755                 break;
4756                 // b <NICK> :<HOST>
4757                 // change displayed host (SETHOST)
4758                 case 'b':
4759                         handle_b(token,params,source,reply,udp_host);
4760                 break;
4761                 // t <NICK> <CHANNEL> :<TOPIC>
4762                 // change a channel topic
4763                 case 't':
4764                         handle_t(token,params,source,reply,udp_host);
4765                 break;
4766                 // i <NICK> <CHANNEL>
4767                 // invite a user to a channel
4768                 case 'i':
4769                         handle_i(token,params,source,reply,udp_host);
4770                 break;
4771                 // k <SOURCE> <DEST> <CHANNEL> :<REASON>
4772                 // kick a user from a channel
4773                 case 'k':
4774                         handle_k(token,params,source,reply,udp_host);
4775                 break;
4776                 // n <NICK> <NEWNICK>
4777                 // change nickname of client -- a server should only be able to
4778                 // change the nicknames of clients that reside on it unless
4779                 // they are ulined.
4780                 case 'n':
4781                         handle_n(token,params,source,reply,udp_host);
4782                 break;
4783                 // J <NICK> <CHANLIST>
4784                 // Join user to channel list, merge channel permissions
4785                 case 'J':
4786                         handle_J(token,params,source,reply,udp_host);
4787                 break;
4788                 // T <TS> <CHANNEL> <TOPICSETTER> :<TOPIC>
4789                 // change channel topic (netburst only)
4790                 case 'T':
4791                         handle_T(token,params,source,reply,udp_host);
4792                 break;
4793                 // M <TARGET> <MODES> [MODE-PARAMETERS]
4794                 // Server setting modes on an object
4795                 case 'M':
4796                         handle_M(token,params,source,reply,udp_host);
4797                 break;
4798                 // m <SOURCE> <TARGET> <MODES> [MODE-PARAMETERS]
4799                 // User setting modes on an object
4800                 case 'm':
4801                         handle_m(token,params,source,reply,udp_host);
4802                 break;
4803                 // P <SOURCE> <TARGET> :<TEXT>
4804                 // Send a private/channel message
4805                 case 'P':
4806                         handle_P(token,params,source,reply,udp_host);
4807                 break;
4808                 // V <SOURCE> <TARGET> :<TEXT>
4809                 // Send a private/channel notice
4810                 case 'V':
4811                         handle_V(token,params,source,reply,udp_host);
4812                 break;
4813                 // L <SOURCE> <CHANNEL> :<REASON>
4814                 // User parting a channel
4815                 case 'L':
4816                         handle_L(token,params,source,reply,udp_host);
4817                 break;
4818                 // Q <SOURCE> :<REASON>
4819                 // user quitting
4820                 case 'Q':
4821                         handle_Q(token,params,source,reply,udp_host);
4822                 break;
4823                 // K <SOURCE> <DEST> :<REASON>
4824                 // remote kill
4825                 case 'K':
4826                         handle_K(token,params,source,reply,udp_host);
4827                 break;
4828                 // @ <SOURCE> :<TEXT>
4829                 // wallops
4830                 case '@':
4831                         handle_AT(token,params,source,reply,udp_host);
4832                 break;
4833                 // F <TS>
4834                 // end netburst
4835                 case 'F':
4836                         WriteOpers("Server %s has completed netburst. (%d secs)",udp_host,time(NULL)-nb_start);
4837                         handle_F(token,params,source,reply,udp_host);
4838                         nb_start = 0;
4839                         // tell all the other servers to use this authcookie to connect back again
4840                         // got '+ test3.chatspike.net 7010 -2016508415' from test.chatspike.net
4841                         snprintf(buffer,MAXBUF,"+ %s %s %d %d",udp_host,ipaddr,port,authcookie);
4842                         NetSendToAllExcept(udp_host,buffer);
4843                 break;
4844                 // X <reserved>
4845                 // Send netburst now
4846                 case 'X':
4847                         WriteOpers("Sending my netburst to %s",udp_host);
4848                         DoSync(source,udp_host);
4849                         WriteOpers("Send of netburst to %s completed",udp_host);
4850                         NetSendMyRoutingTable();
4851                 break;
4852                 // anything else
4853                 default:
4854                         WriteOpers("WARNING! Unknown datagram type '%c'",token);
4855                 break;
4856         }
4857 }
4858
4859
4860 void handle_link_packet(char* udp_msg, char* udp_host, serverrec *serv)
4861 {
4862         char response[10240];
4863         char token = udp_msg[0];
4864         char* params = udp_msg + 2;
4865         char finalparam[1024];
4866         strcpy(finalparam," :xxxx");
4867         if (strstr(udp_msg," :")) {
4868                 strncpy(finalparam,strstr(udp_msg," :"),1024);
4869         }
4870         if (token == '-') {
4871                 char* cookie = strtok(params," ");
4872                 char* servername = strtok(NULL," ");
4873                 char* serverdesc = finalparam+2;
4874
4875                 WriteOpers("AuthCookie CONNECT from %s (%s)",servername,udp_host);
4876
4877                 for (int u = 0; u < auth_cookies.size(); u++)
4878                 {
4879                         if (auth_cookies[u] == atoi(cookie))
4880                         {
4881                                 WriteOpers("Allowed cookie from %s, is now part of the mesh",servername);
4882
4883
4884                                 for (int j = 0; j < 32; j++)
4885                                 {
4886                                         if (me[j] != NULL)
4887                                         {
4888                                                 for (int k = 0; k < me[j]->connectors.size(); k++)
4889                                                 {
4890                                                         if (!strcasecmp(me[j]->connectors[k].GetServerName().c_str(),udp_host))
4891                                                         {
4892                                                                 me[j]->connectors[k].SetServerName(servername);
4893                                                                 me[j]->connectors[k].SetDescription(serverdesc);
4894                                                                 me[j]->connectors[k].SetState(STATE_CONNECTED);
4895                                                                 NetSendMyRoutingTable();
4896                                                                 return;
4897                                                         }
4898                                                 }
4899                                         }
4900                                         WriteOpers("\2WARNING!\2 %s sent us an authentication packet but we are not authenticating with this server right now! Possible intrusion attempt!",udp_host);
4901                                         return;
4902                                 }
4903
4904
4905                                 return;
4906                         }
4907                 }
4908                 // bad cookie, bad bad! go sit in the corner!
4909                 WriteOpers("Bad cookie from %s!",servername);
4910                 return;
4911         }
4912         else
4913         if (token == 'S') {
4914                 // S test.chatspike.net password portn :ChatSpike InspIRCd test server
4915                 char* servername = strtok(params," ");
4916                 char* password = strtok(NULL," ");
4917                 char* myport = strtok(NULL," ");
4918                 char* revision = strtok(NULL," ");
4919                 char* serverdesc = finalparam+2;
4920
4921                 WriteOpers("CONNECT from %s (%s) (their port: %d)",servername,udp_host,atoi(myport));
4922                 
4923                 ircd_connector* cn = serv->FindHost(servername);
4924                 
4925                 if (cn)
4926                 {
4927                         WriteOpers("CONNECT aborted: Server %s already exists from %s",servername,ServerName);
4928                         char buffer[MAXBUF];
4929                         sprintf(buffer,"E :Server %s already exists!",servername);
4930                         serv->SendPacket(buffer,udp_host);
4931                         RemoveServer(udp_host);
4932                         return;
4933                 }
4934
4935                 if (atoi(revision) != GetRevision())
4936                 {
4937                         WriteOpers("CONNECT aborted: Could not link to %s, is an incompatible version %s, our version is %d",servername,revision,GetRevision());
4938                         char buffer[MAXBUF];
4939                         sprintf(buffer,"E :Version number mismatch");
4940                         serv->SendPacket(buffer,udp_host);
4941                         RemoveServer(udp_host);
4942                         RemoveServer(servername);
4943                         return;
4944                 }
4945
4946                 for (int j = 0; j < serv->connectors.size(); j++)
4947                 {
4948                         if (!strcasecmp(serv->connectors[j].GetServerName().c_str(),udp_host))
4949                         {
4950                                 serv->connectors[j].SetServerName(servername);
4951                                 serv->connectors[j].SetDescription(serverdesc);
4952                                 serv->connectors[j].SetServerPort(atoi(myport));
4953                         }
4954                 }
4955                 
4956                 
4957                 char Link_ServerName[1024];
4958                 char Link_IPAddr[1024];
4959                 char Link_Port[1024];
4960                 char Link_Pass[1024];
4961                 char Link_SendPass[1024];
4962                 int LinkPort = 0;
4963                 
4964                 // search for a corresponding <link> block in the config files
4965                 for (int i = 0; i < ConfValueEnum("link",&config_f); i++)
4966                 {
4967                         ConfValue("link","name",i,Link_ServerName,&config_f);
4968                         ConfValue("link","ipaddr",i,Link_IPAddr,&config_f);
4969                         ConfValue("link","port",i,Link_Port,&config_f);
4970                         ConfValue("link","recvpass",i,Link_Pass,&config_f);
4971                         ConfValue("link","sendpass",i,Link_SendPass,&config_f);
4972                         log(DEBUG,"(%d) Comparing against name='%s', ipaddr='%s', port='%s', recvpass='%s'",i,Link_ServerName,Link_IPAddr,Link_Port,Link_Pass);
4973                         LinkPort = atoi(Link_Port);
4974                         if (!strcasecmp(Link_ServerName,servername))
4975                         {
4976                                 // we have a matching link line -
4977                                 // send a 'diminutive' server message back...
4978                                 snprintf(response,10240,"s %s %s :%s",ServerName,Link_SendPass,ServerDesc);
4979                                 serv->SendPacket(response,servername);
4980
4981                                 for (int t = 0; t < serv->connectors.size(); t++)
4982                                 {
4983                                         if (!strcasecmp(serv->connectors[t].GetServerName().c_str(),servername))
4984                                         {
4985                                                 serv->connectors[t].SetState(STATE_CONNECTED);
4986                                         }
4987                                 }
4988                 
4989                                 return;
4990                         }
4991                 }
4992                 char buffer[MAXBUF];
4993                 sprintf(buffer,"E :Access is denied (no matching link block)");
4994                 serv->SendPacket(buffer,udp_host);
4995                 WriteOpers("CONNECT from %s denied, no matching link block",servername);
4996                 RemoveServer(udp_host);
4997                 RemoveServer(servername);
4998                 return;
4999         }
5000         else
5001         if (token == 's') {
5002                 // S test.chatspike.net password :ChatSpike InspIRCd test server
5003                 char* servername = strtok(params," ");
5004                 char* password = strtok(NULL," ");
5005                 char* serverdesc = finalparam+2;
5006                 
5007                 // TODO: we should do a check here to ensure that this server is one we recently initiated a
5008                 // link with, and didnt hear an 's' or 'E' back from yet (these are the only two valid responses
5009                 // to an 'S' command. If we didn't recently send an 'S' to this server, theyre trying to spoof
5010                 // a connect, so put out an oper alert!
5011                 
5012                 // for now, just accept all, we'll fix that later.
5013                 WriteOpers("%s accepted our link credentials ",servername);
5014                 
5015                 char Link_ServerName[1024];
5016                 char Link_IPAddr[1024];
5017                 char Link_Port[1024];
5018                 char Link_Pass[1024];
5019                 char Link_SendPass[1024];
5020                 int LinkPort = 0;
5021                 
5022                 // search for a corresponding <link> block in the config files
5023                 for (int i = 0; i < ConfValueEnum("link",&config_f); i++)
5024                 {
5025                         ConfValue("link","name",i,Link_ServerName,&config_f);
5026                         ConfValue("link","ipaddr",i,Link_IPAddr,&config_f);
5027                         ConfValue("link","port",i,Link_Port,&config_f);
5028                         ConfValue("link","recvpass",i,Link_Pass,&config_f);
5029                         ConfValue("link","sendpass",i,Link_SendPass,&config_f);
5030                         log(DEBUG,"(%d) Comparing against name='%s', ipaddr='%s', port='%s', recvpass='%s'",i,Link_ServerName,Link_IPAddr,Link_Port,Link_Pass);
5031                         LinkPort = atoi(Link_Port);
5032                         if (!strcasecmp(Link_ServerName,servername))
5033                         {
5034                                 // matching link at this end too, we're all done!
5035                                 // at this point we must begin key exchange and insert this
5036                                 // server into our 'active' table.
5037                                 for (int j = 0; j < 32; j++)
5038                                 {
5039                                         if (me[j] != NULL)
5040                                         {
5041                                                 for (int k = 0; k < me[j]->connectors.size(); k++)
5042                                                 {
5043                                                         if (!strcasecmp(me[j]->connectors[k].GetServerName().c_str(),udp_host))
5044                                                         {
5045                                                                 char buffer[MAXBUF];
5046                                                                 me[j]->connectors[k].SetDescription(serverdesc);
5047                                                                 me[j]->connectors[k].SetState(STATE_CONNECTED);
5048                                                                 sprintf(buffer,"X 0");
5049                                                                 serv->SendPacket(buffer,udp_host);
5050                                                                 DoSync(me[j],udp_host);
5051                                                                 NetSendMyRoutingTable();
5052                                                                 return;
5053                                                         }
5054                                                 }
5055                                         }
5056                                         WriteOpers("\2WARNING!\2 %s sent us an authentication packet but we are not authenticating with this server right noe! Possible intrusion attempt!",udp_host);
5057                                         return;
5058                                 }
5059                         }
5060                         else {
5061                                 log(DEBUG,"Server names '%s' and '%s' don't match",Link_ServerName,servername);
5062                         }
5063                 }
5064                 char buffer[MAXBUF];
5065                 sprintf(buffer,"E :Access is denied (no matching link block)");
5066                 serv->SendPacket(buffer,udp_host);
5067                 WriteOpers("CONNECT from %s denied, no matching link block",servername);
5068                 RemoveServer(udp_host);
5069                 RemoveServer(servername);
5070                 return;
5071         }
5072         else
5073         if (token == 'E') {
5074                 char* error_message = finalparam+2;
5075                 WriteOpers("ERROR from %s: %s",udp_host,error_message);
5076                 return;
5077         }
5078         else {
5079
5080                 serverrec* source_server = NULL;
5081
5082                 for (int j = 0; j < 32; j++)
5083                 {
5084                         if (me[j] != NULL)
5085                         {
5086                                 for (int x = 0; x < me[j]->connectors.size(); x++)
5087                                 {
5088                                         log(DEBUG,"Servers are: '%s' '%s'",udp_host,me[j]->connectors[x].GetServerName().c_str());
5089                                         if (!strcasecmp(me[j]->connectors[x].GetServerName().c_str(),udp_host))
5090                                         {
5091                                                 if (me[j]->connectors[x].GetState() == STATE_CONNECTED)
5092                                                 {
5093                                                         // found a valid ircd_connector.
5094                                                         process_restricted_commands(token,params,me[j],serv,udp_host,me[j]->connectors[x].GetServerIP(),me[j]->connectors[x].GetServerPort());
5095                                                         return;
5096                                                 }
5097                                         }
5098                                 }
5099                         }
5100                 }
5101
5102                 log(DEBUG,"Unrecognised token or unauthenticated host in datagram from %s: %c",udp_host,token);
5103         }
5104 }
5105
5106 int reap_counter = 0;
5107
5108 int InspIRCd(void)
5109 {
5110         struct sockaddr_in client, server;
5111         char addrs[MAXBUF][255];
5112         int openSockfd[MAXSOCKS], incomingSockfd, result = TRUE;
5113         socklen_t length;
5114         int count = 0, scanDetectTrigger = TRUE, showBanner = FALSE;
5115         int selectResult = 0, selectResult2 = 0;
5116         char *temp, configToken[MAXBUF], stuff[MAXBUF], Addr[MAXBUF], Type[MAXBUF];
5117         char resolvedHost[MAXBUF];
5118         fd_set selectFds;
5119         struct timeval tv;
5120
5121         log_file = fopen("ircd.log","a+");
5122         if (!log_file)
5123         {
5124                 printf("ERROR: Could not write to logfile ircd.log, bailing!\n\n");
5125                 Exit(ERROR);
5126         }
5127
5128         log(DEBUG,"InspIRCd: startup: begin");
5129         log(DEBUG,"$Id$");
5130         if (geteuid() == 0)
5131         {
5132                 printf("WARNING!!! You are running an irc server as ROOT!!! DO NOT DO THIS!!!\n\n");
5133                 Exit(ERROR);
5134                 log(DEBUG,"InspIRCd: startup: not starting with UID 0!");
5135         }
5136         SetupCommandTable();
5137         log(DEBUG,"InspIRCd: startup: default command table set up");
5138         
5139         ReadConfig();
5140         if (strcmp(DieValue,"")) 
5141         { 
5142                 printf("WARNING: %s\n\n",DieValue);
5143                 exit(0); 
5144         }  
5145         log(DEBUG,"InspIRCd: startup: read config");
5146           
5147         int count2 = 0, count3 = 0;
5148
5149         for (count = 0; count < ConfValueEnum("bind",&config_f); count++)
5150         {
5151                 ConfValue("bind","port",count,configToken,&config_f);
5152                 ConfValue("bind","address",count,Addr,&config_f);
5153                 ConfValue("bind","type",count,Type,&config_f);
5154                 if (!strcmp(Type,"servers"))
5155                 {
5156                         char Default[MAXBUF];
5157                         strcpy(Default,"no");
5158                         ConfValue("bind","default",count,Default,&config_f);
5159                         if (strchr(Default,'y'))
5160                         {
5161                                 defaultRoute = count3;
5162                                 log(DEBUG,"InspIRCd: startup: binding '%s:%s' is default server route",Addr,configToken);
5163                         }
5164                         me[count3] = new serverrec(ServerName,100L,false);
5165                         me[count3]->CreateListener(Addr,atoi(configToken));
5166                         count3++;
5167                 }
5168                 else
5169                 {
5170                         ports[count2] = atoi(configToken);
5171                         strcpy(addrs[count2],Addr);
5172                         count2++;
5173                 }
5174                 log(DEBUG,"InspIRCd: startup: read binding %s:%s [%s] from config",Addr,configToken, Type);
5175         }
5176         portCount = count2;
5177         UDPportCount = count3;
5178           
5179         log(DEBUG,"InspIRCd: startup: read %d total client ports and %d total server ports",portCount,UDPportCount);
5180         
5181         log(DEBUG,"InspIRCd: startup: InspIRCd is now running!");
5182         
5183         printf("\n");
5184         
5185         /* BugFix By Craig! :p */
5186         count = 0;
5187         for (count2 = 0; count2 < ConfValueEnum("module",&config_f); count2++)
5188         {
5189                 char modfile[MAXBUF];
5190                 ConfValue("module","name",count2,configToken,&config_f);
5191                 sprintf(modfile,"%s/%s",MOD_PATH,configToken,&config_f);
5192                 printf("Loading module... \033[1;37m%s\033[0;37m\n",modfile);
5193                 log(DEBUG,"InspIRCd: startup: Loading module: %s",modfile);
5194                 /* If The File Doesnt exist, Trying to load it
5195                  * Will Segfault the IRCd.. So, check to see if
5196                  * it Exists, Before Proceeding. */
5197                 if (FileExists(modfile))
5198                 {
5199                         factory[count] = new ircd_module(modfile);
5200                         if (factory[count]->LastError())
5201                         {
5202                                 log(DEBUG,"Unable to load %s: %s",modfile,factory[count]->LastError());
5203                                 sprintf("Unable to load %s: %s\nExiting...\n",modfile,factory[count]->LastError());
5204                                 Exit(ERROR);
5205                         }
5206                         if (factory[count]->factory)
5207                         {
5208                                 modules[count] = factory[count]->factory->CreateModule();
5209                                 /* save the module and the module's classfactory, if
5210                                  * this isnt done, random crashes can occur :/ */
5211                                 module_names.push_back(modfile);        
5212                         }
5213                         else
5214                         {
5215                                 log(DEBUG,"Unable to load %s",modfile);
5216                                 sprintf("Unable to load %s\nExiting...\n",modfile);
5217                                 Exit(ERROR);
5218                         }
5219                         /* Increase the Count */
5220                         count++;
5221                 }
5222                 else
5223                 {
5224                         log(DEBUG,"InspIRCd: startup: Module Not Found %s",modfile);
5225                         printf("Module Not Found: \033[1;37m%s\033[0;37m, Skipping\n",modfile);
5226                 }
5227         }
5228         MODCOUNT = count - 1;
5229         log(DEBUG,"Total loaded modules: %d",MODCOUNT+1);
5230         
5231         printf("\nInspIRCd is now running!\n");
5232         
5233         startup_time = time(NULL);
5234           
5235         if (nofork)
5236         {
5237                 log(VERBOSE,"Not forking as -nofork was specified");
5238         }
5239         else
5240         {
5241                 if (DaemonSeed() == ERROR)
5242                 {
5243                         log(DEBUG,"InspIRCd: startup: can't daemonise");
5244                         printf("ERROR: could not go into daemon mode. Shutting down.\n");
5245                         Exit(ERROR);
5246                 }
5247         }
5248           
5249           
5250         /* setup select call */
5251         FD_ZERO(&selectFds);
5252         log(DEBUG,"InspIRCd: startup: zero selects");
5253         log(VERBOSE,"InspIRCd: startup: portCount = %d", portCount);
5254         
5255         for (count = 0; count < portCount; count++)
5256         {
5257                 if ((openSockfd[boundPortCount] = OpenTCPSocket()) == ERROR)
5258                 {
5259                         log(DEBUG,"InspIRCd: startup: bad fd %d",openSockfd[boundPortCount]);
5260                         return(ERROR);
5261                 }
5262                 if (BindSocket(openSockfd[boundPortCount],client,server,ports[count],addrs[count]) == ERROR)
5263                 {
5264                         log(DEBUG,"InspIRCd: startup: failed to bind port %d",ports[count]);
5265                 }
5266                 else    /* well we at least bound to one socket so we'll continue */
5267                 {
5268                         boundPortCount++;
5269                 }
5270         }
5271         
5272         log(DEBUG,"InspIRCd: startup: total bound ports %d",boundPortCount);
5273           
5274         /* if we didn't bind to anything then abort */
5275         if (boundPortCount == 0)
5276         {
5277                 log(DEBUG,"InspIRCd: startup: no ports bound, bailing!");
5278                 return (ERROR);
5279         }
5280         
5281
5282         length = sizeof (client);
5283         char udp_msg[MAXBUF], udp_host[MAXBUF];
5284           
5285         /* main loop, this never returns */
5286         for (;;)
5287         {
5288 #ifdef _POSIX_PRIORITY_SCHEDULING
5289                 sched_yield();
5290 #endif
5291
5292                 fd_set sfd;
5293                 timeval tval;
5294                 FD_ZERO(&sfd);
5295
5296                 user_hash::iterator count2 = clientlist.begin();
5297
5298                 // *FIX* Instead of closing sockets in kill_link when they receive the ERROR :blah line, we should queue
5299                 // them in a list, then reap the list every second or so.
5300                 if (reap_counter>300)
5301                 {
5302                         if (fd_reap.size() > 0)
5303                         {
5304                                 for( int n = 0; n < fd_reap.size(); n++)
5305                                 {
5306                                         Blocking(fd_reap[n]);
5307                                         close(fd_reap[n]);
5308                                         NonBlocking(fd_reap[n]);
5309                                 }
5310                         }
5311                         fd_reap.clear();
5312                         reap_counter=0;
5313                 }
5314                 reap_counter++;
5315
5316                 fd_set serverfds;
5317                 FD_ZERO(&serverfds);
5318                 timeval tvs;
5319                 
5320                 for (int x = 0; x != UDPportCount; x++)
5321                 {
5322                         FD_SET(me[x]->fd, &serverfds);
5323                 }
5324                 
5325                 tvs.tv_usec = 0;                
5326                 tvs.tv_sec = 0;
5327                 
5328                 int servresult = select(32767, &serverfds, NULL, NULL, &tvs);
5329                 if (servresult > 0)
5330                 {
5331                         for (int x = 0; x != UDPportCount; x++)
5332                         {
5333                                 if (FD_ISSET (me[x]->fd, &serverfds))
5334                                 {
5335                                         char remotehost[MAXBUF],resolved[MAXBUF];
5336                                         length = sizeof (client);
5337                                         incomingSockfd = accept (me[x]->fd, (sockaddr *) &client, &length);
5338                                         strncpy(remotehost,(char *)inet_ntoa(client.sin_addr),MAXBUF);
5339                                         if(CleanAndResolve(resolved, remotehost) != TRUE)
5340                                         {
5341                                                 strncpy(resolved,remotehost,MAXBUF);
5342                                         }
5343                                         // add to this connections ircd_connector vector
5344                                         // *FIX* - we need the LOCAL port not the remote port in &client!
5345                                         me[x]->AddIncoming(incomingSockfd,resolved,me[x]->port);
5346                                 }
5347                         }
5348                 }
5349      
5350                 for (int x = 0; x < UDPportCount; x++)
5351                 {
5352                         std::deque<std::string> msgs;
5353                         msgs.clear();
5354                         if (me[x]->RecvPacket(msgs, udp_host))
5355                         {
5356                                 for (int ctr = 0; ctr < msgs.size(); ctr++)
5357                                 {
5358                                         char udp_msg[MAXBUF];
5359                                         strncpy(udp_msg,msgs[ctr].c_str(),MAXBUF);
5360                                         if (strlen(udp_msg)<1)
5361                                         {
5362                                                 log(DEBUG,"Invalid string from %s [route%d]",udp_host,x);
5363                                                 break;
5364                                         }
5365                                         // during a netburst, send all data to all other linked servers
5366                                         if ((nb_start>0) && (udp_msg[0] != 'Y') && (udp_msg[0] != 'X') && (udp_msg[0] != 'F'))
5367                                         {
5368                                                 NetSendToAllExcept(udp_host,udp_msg);
5369                                         }
5370                                         FOREACH_MOD OnPacketReceive(udp_msg);
5371                                         handle_link_packet(udp_msg, udp_host, me[x]);
5372                                 }
5373                                 goto label;
5374                         }
5375                 }
5376         
5377
5378         while (count2 != clientlist.end())
5379         {
5380                 char data[10240];
5381                 tval.tv_usec = tval.tv_sec = 0;
5382                 FD_ZERO(&sfd);
5383                 int total_in_this_set = 0;
5384
5385                 user_hash::iterator xcount = count2;
5386                 user_hash::iterator endingiter = count2;
5387
5388                 if (!count2->second) break;
5389                 
5390                 if (count2->second)
5391                 if (count2->second->fd != 0)
5392                 {
5393                         // assemble up to 64 sockets into an fd_set
5394                         // to implement a pooling mechanism.
5395                         //
5396                         // This should be up to 64x faster than the
5397                         // old implementation.
5398                         while (total_in_this_set < 64)
5399                         {
5400                                 if (count2 != clientlist.end())
5401                                 {
5402                                         // we don't check the state of remote users.
5403                                         if (count2->second->fd > 0)
5404                                         {
5405                                                 FD_SET (count2->second->fd, &sfd);
5406
5407                                                 // registration timeout -- didnt send USER/NICK/HOST in the time specified in
5408                                                 // their connection class.
5409                                                 if ((time(NULL) > count2->second->timeout) && (count2->second->registered != 7)) 
5410                                                 {
5411                                                         log(DEBUG,"InspIRCd: registration timeout: %s",count2->second->nick);
5412                                                         kill_link(count2->second,"Registration timeout");
5413                                                         goto label;
5414                                                 }
5415                                                 if (((time(NULL)) > count2->second->nping) && (isnick(count2->second->nick)) && (count2->second->registered == 7))
5416                                                 {
5417                                                         if ((!count2->second->lastping) && (count2->second->registered == 7))
5418                                                         {
5419                                                                 log(DEBUG,"InspIRCd: ping timeout: %s",count2->second->nick);
5420                                                                 kill_link(count2->second,"Ping timeout");
5421                                                                 goto label;
5422                                                         }
5423                                                         Write(count2->second->fd,"PING :%s",ServerName);
5424                                                         log(DEBUG,"InspIRCd: pinging: %s",count2->second->nick);
5425                                                         count2->second->lastping = 0;
5426                                                         count2->second->nping = time(NULL)+120;
5427                                                 }
5428                                         }
5429                                         count2++;
5430                                         total_in_this_set++;
5431                                 }
5432                                 else break;
5433                         }
5434    
5435                         endingiter = count2;
5436                         count2 = xcount; // roll back to where we were
5437         
5438                         int v = 0;
5439
5440                         tval.tv_usec = 0;
5441                         tval.tv_sec = 0;
5442                         selectResult2 = select(65535, &sfd, NULL, NULL, &tval);
5443                         
5444                         // now loop through all of the items in this pool if any are waiting
5445                         //if (selectResult2 > 0)
5446                         for (user_hash::iterator count2a = xcount; count2a != endingiter; count2a++)
5447                         {
5448
5449 #ifdef _POSIX_PRIORITY_SCHEDULING
5450                                 sched_yield();
5451 #endif
5452
5453                                 result = EAGAIN;
5454                                 if ((count2a->second->fd != -1) && (FD_ISSET (count2a->second->fd, &sfd)))
5455                                 {
5456                                         log(DEBUG,"Reading fd %d",count2a->second->fd);
5457                                         memset(data, 0, 10240);
5458                                         result = read(count2a->second->fd, data, 10240);
5459                                         
5460                                         if (result)
5461                                         {
5462                                                 if (result > 0)
5463                                                         log(DEBUG,"Read %d characters from socket",result);
5464                                                 userrec* current = count2a->second;
5465                                                 int currfd = current->fd;
5466                                                 char* l = strtok(data,"\n");
5467                                                 int floodlines = 0;
5468                                                 while (l)
5469                                                 {
5470                                                         floodlines++;
5471                                                         if ((floodlines > current->flood) && (current->flood != 0))
5472                                                         {
5473                                                                 log(DEFAULT,"Excess flood from: %s!%s@%s",current->nick,current->ident,current->host);
5474                                                                 WriteOpers("*** Excess flood from: %s!%s@%s",current->nick,current->ident,current->host);
5475                                                                 kill_link(current,"Excess flood");
5476                                                                 goto label;
5477                                                         }
5478                                                         char sanitized[NetBufferSize];
5479                                                         memset(sanitized, 0, NetBufferSize);
5480                                                         int ptt = 0;
5481                                                         for (int pt = 0; pt < strlen(l); pt++)
5482                                                         {
5483                                                                 if (l[pt] != '\r')
5484                                                                 {
5485                                                                         sanitized[ptt++] = l[pt];
5486                                                                 }
5487                                                         }
5488                                                         sanitized[ptt] = '\0';
5489                                                         if (strlen(sanitized))
5490                                                         {
5491
5492
5493                                                                 // we're gonna re-scan to check if the nick is gone, after every
5494                                                                 // command - if it has, we're gonna bail
5495                                                                 bool find_again = false;
5496                                                                 process_buffer(sanitized,current);
5497         
5498                                                                 // look for the user's record in case it's changed
5499                                                                 for (user_hash::iterator c2 = clientlist.begin(); c2 != clientlist.end(); c2++)
5500                                                                 {
5501                                                                         if (c2->second->fd == currfd)
5502                                                                         {
5503                                                                                 // found again, update pointer
5504                                                                                 current == c2->second;
5505                                                                                 find_again = true;
5506                                                                                 break;
5507                                                                         }
5508                                                                 }
5509                                                                 if (!find_again)
5510                                                                         goto label;
5511
5512                                                         }
5513                                                         l = strtok(NULL,"\n");
5514                                                 }
5515                                                 goto label;
5516                                         }
5517
5518                                         if ((result == -1) && (errno != EAGAIN) && (errno != EINTR))
5519                                         {
5520                                                 log(DEBUG,"killing: %s",count2a->second->nick);
5521                                                 kill_link(count2a->second,strerror(errno));
5522                                                 goto label;
5523                                         }
5524                                 }
5525                                 // result EAGAIN means nothing read
5526                                 if (result == EAGAIN)
5527                                 {
5528                                 }
5529                                 else
5530                                 if (result == 0)
5531                                 {
5532                                         if (count2->second)
5533                                         {
5534                                                 log(DEBUG,"InspIRCd: Exited: %s",count2a->second->nick);
5535                                                 kill_link(count2a->second,"Client exited");
5536                                                 // must bail here? kill_link removes the hash, corrupting the iterator
5537                                                 log(DEBUG,"Bailing from client exit");
5538                                                 goto label;
5539                                         }
5540                                 }
5541                                 else if (result > 0)
5542                                 {
5543                                 }
5544                         }
5545                 }
5546                 for (int q = 0; q < total_in_this_set; q++)
5547                 {
5548                         // there is no iterator += operator :(
5549                         //if (count2 != clientlist.end())
5550                         //{
5551                                 count2++;
5552                         //}
5553                 }
5554         }
5555         
5556         // set up select call
5557         for (count = 0; count < boundPortCount; count++)
5558         {
5559                 FD_SET (openSockfd[count], &selectFds);
5560         }
5561
5562         tv.tv_usec = 1;
5563         selectResult = select(MAXSOCKS, &selectFds, NULL, NULL, &tv);
5564
5565         /* select is reporting a waiting socket. Poll them all to find out which */
5566         if (selectResult > 0)
5567         {
5568                 char target[MAXBUF], resolved[MAXBUF];
5569                 for (count = 0; count < boundPortCount; count++)                
5570                 {
5571                         if (FD_ISSET (openSockfd[count], &selectFds))
5572                         {
5573                                 length = sizeof (client);
5574                                 incomingSockfd = accept (openSockfd[count], (struct sockaddr *) &client, &length);
5575                               
5576                                 address_cache::iterator iter = IP.find(client.sin_addr);
5577                                 bool iscached = false;
5578                                 if (iter == IP.end())
5579                                 {
5580                                         /* ip isn't in cache, add it */
5581                                         strncpy (target, (char *) inet_ntoa (client.sin_addr), MAXBUF);
5582                                         if(CleanAndResolve(resolved, target) != TRUE)
5583                                         {
5584                                                 strncpy(resolved,target,MAXBUF);
5585                                         }
5586                                         /* hostname now in 'target' */
5587                                         IP[client.sin_addr] = new string(resolved);
5588                                         /* hostname in cache */
5589                                 }
5590                                 else
5591                                 {
5592                                         /* found ip (cached) */
5593                                         strncpy(resolved, iter->second->c_str(), MAXBUF);
5594                                         iscached = true;
5595                                 }
5596                         
5597                                 if (incomingSockfd < 0)
5598                                 {
5599                                         WriteOpers("*** WARNING: Accept failed on port %d (%s)", ports[count],target);
5600                                         log(DEBUG,"InspIRCd: accept failed: %d",ports[count]);
5601                                 }
5602                                 else
5603                                 {
5604                                         AddClient(incomingSockfd, resolved, ports[count], iscached);
5605                                         log(DEBUG,"InspIRCd: adding client on port %d fd=%d",ports[count],incomingSockfd);
5606                                 }
5607                                 goto label;
5608                         }
5609                 }
5610         }
5611         label:
5612         if(0) {}; // "Label must be followed by a statement"... so i gave it one.
5613 }
5614 /* not reached */
5615 close (incomingSockfd);
5616 }
5617