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