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