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