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