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