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