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