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