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