]> git.netwichtig.de Git - user/henk/code/inspircd.git/blob - src/inspircd.cpp
Q-lines fully working, can add and remove other types of ban (but not enforced yet)
[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 #include "message.h"
55 #include "mode.h"
56 #include "commands.h"
57 #include "xline.h"
58
59 #ifdef GCC3
60 #define nspace __gnu_cxx
61 #else
62 #define nspace std
63 #endif
64
65 int LogLevel = DEFAULT;
66 char ServerName[MAXBUF];
67 char Network[MAXBUF];
68 char ServerDesc[MAXBUF];
69 char AdminName[MAXBUF];
70 char AdminEmail[MAXBUF];
71 char AdminNick[MAXBUF];
72 char diepass[MAXBUF];
73 char restartpass[MAXBUF];
74 char motd[MAXBUF];
75 char rules[MAXBUF];
76 char list[MAXBUF];
77 char PrefixQuit[MAXBUF];
78 char DieValue[MAXBUF];
79 int debugging =  0;
80 int WHOWAS_STALE = 48; // default WHOWAS Entries last 2 days before they go 'stale'
81 int WHOWAS_MAX = 100;  // default 100 people maximum in the WHOWAS list
82 int DieDelay  =  5;
83 time_t startup_time = time(NULL);
84 int NetBufferSize = 10240; // NetBufferSize used as the buffer size for all read() ops
85 time_t nb_start = 0;
86
87 extern vector<Module*> modules;
88 std::vector<std::string> module_names;
89 extern vector<ircd_module*> factory;
90 std::vector<int> fd_reap;
91
92 extern int MODCOUNT;
93
94 bool nofork = false;
95
96 namespace nspace
97 {
98         template<> struct nspace::hash<in_addr>
99         {
100                 size_t operator()(const struct in_addr &a) const
101                 {
102                         size_t q;
103                         memcpy(&q,&a,sizeof(size_t));
104                         return q;
105                 }
106         };
107
108         template<> struct nspace::hash<string>
109         {
110                 size_t operator()(const string &s) const
111                 {
112                         char a[MAXBUF];
113                         static struct hash<const char *> strhash;
114                         strcpy(a,s.c_str());
115                         strlower(a);
116                         return strhash(a);
117                 }
118         };
119 }       
120
121
122 struct StrHashComp
123 {
124
125         bool operator()(const string& s1, const string& s2) const
126         {
127                 char a[MAXBUF],b[MAXBUF];
128                 strcpy(a,s1.c_str());
129                 strcpy(b,s2.c_str());
130                 return (strcasecmp(a,b) == 0);
131         }
132
133 };
134
135 struct InAddr_HashComp
136 {
137
138         bool operator()(const in_addr &s1, const in_addr &s2) const
139         {
140                 size_t q;
141                 size_t p;
142                 
143                 memcpy(&q,&s1,sizeof(size_t));
144                 memcpy(&p,&s2,sizeof(size_t));
145                 
146                 return (q == p);
147         }
148
149 };
150
151
152 typedef nspace::hash_map<std::string, userrec*, nspace::hash<string>, StrHashComp> user_hash;
153 typedef nspace::hash_map<std::string, chanrec*, nspace::hash<string>, StrHashComp> chan_hash;
154 typedef nspace::hash_map<in_addr,string*, nspace::hash<in_addr>, InAddr_HashComp> address_cache;
155 typedef std::deque<command_t> command_table;
156
157 serverrec* me[32];
158
159 FILE *log_file;
160
161 user_hash clientlist;
162 chan_hash chanlist;
163 user_hash whowas;
164 command_table cmdlist;
165 file_cache MOTD;
166 file_cache RULES;
167 address_cache IP;
168
169 ClassVector Classes;
170
171 struct linger linger = { 0 };
172 char bannerBuffer[MAXBUF];
173 int boundPortCount = 0;
174 int portCount = 0, UDPportCount = 0, ports[MAXSOCKS];
175 int defaultRoute = 0;
176
177 connection C;
178
179 long MyKey = C.GenKey();
180
181 /* prototypes */
182
183 int has_channel(userrec *u, chanrec *c);
184 int usercount(chanrec *c);
185 int usercount_i(chanrec *c);
186 void update_stats_l(int fd,int data_out);
187 char* Passwd(userrec *user);
188 bool IsDenied(userrec *user);
189 void AddWhoWas(userrec* u);
190
191 std::vector<long> auth_cookies;
192 std::stringstream config_f(stringstream::in | stringstream::out);
193
194
195
196 long GetRevision()
197 {
198         char Revision[] = "$Revision$";
199         char *s1 = Revision;
200         char *savept;
201         char *v1 = strtok_r(s1," ",&savept);
202         s1 = savept;
203         char *v2 = strtok_r(s1," ",&savept);
204         s1 = savept;
205         return (long)(atof(v2)*10000);
206 }
207
208
209 std::string getservername()
210 {
211         return ServerName;
212 }
213
214 std::string getserverdesc()
215 {
216         return ServerDesc;
217 }
218
219 std::string getnetworkname()
220 {
221         return Network;
222 }
223
224 std::string getadminname()
225 {
226         return AdminName;
227 }
228
229 std::string getadminemail()
230 {
231         return AdminEmail;
232 }
233
234 std::string getadminnick()
235 {
236         return AdminNick;
237 }
238
239 void log(int level,char *text, ...)
240 {
241         char textbuffer[MAXBUF];
242         va_list argsPtr;
243         time_t rawtime;
244         struct tm * timeinfo;
245         if (level < LogLevel)
246                 return;
247
248         time(&rawtime);
249         timeinfo = localtime (&rawtime);
250
251         if (log_file)
252         {
253                 char b[MAXBUF];
254                 va_start (argsPtr, text);
255                 vsnprintf(textbuffer, MAXBUF, text, argsPtr);
256                 va_end(argsPtr);
257                 strcpy(b,asctime(timeinfo));
258                 b[strlen(b)-1] = ':';
259                 fprintf(log_file,"%s %s\n",b,textbuffer);
260                 if (nofork)
261                 {
262                         // nofork enabled? display it on terminal too
263                         printf("%s %s\n",b,textbuffer);
264                 }
265         }
266 }
267
268 void readfile(file_cache &F, const char* fname)
269 {
270         FILE* file;
271         char linebuf[MAXBUF];
272         
273         log(DEBUG,"readfile: loading %s",fname);
274         F.clear();
275         file =  fopen(fname,"r");
276         if (file)
277         {
278                 while (!feof(file))
279                 {
280                         fgets(linebuf,sizeof(linebuf),file);
281                         linebuf[strlen(linebuf)-1]='\0';
282                         if (!strcmp(linebuf,""))
283                         {
284                                 strcpy(linebuf,"  ");
285                         }
286                         if (!feof(file))
287                         {
288                                 F.push_back(linebuf);
289                         }
290                 }
291                 fclose(file);
292         }
293         else
294         {
295                 log(DEBUG,"readfile: failed to load file: %s",fname);
296         }
297         log(DEBUG,"readfile: loaded %s, %d lines",fname,F.size());
298 }
299
300 void ReadConfig(void)
301 {
302         char dbg[MAXBUF],pauseval[MAXBUF],Value[MAXBUF],timeout[MAXBUF],NB[MAXBUF],flood[MAXBUF];
303         ConnectClass c;
304         
305         LoadConf(CONFIG_FILE,&config_f);
306           
307         ConfValue("server","name",0,ServerName,&config_f);
308         ConfValue("server","description",0,ServerDesc,&config_f);
309         ConfValue("server","network",0,Network,&config_f);
310         ConfValue("admin","name",0,AdminName,&config_f);
311         ConfValue("admin","email",0,AdminEmail,&config_f);
312         ConfValue("admin","nick",0,AdminNick,&config_f);
313         ConfValue("files","motd",0,motd,&config_f);
314         ConfValue("files","rules",0,rules,&config_f);
315         ConfValue("power","diepass",0,diepass,&config_f);
316         ConfValue("power","pause",0,pauseval,&config_f);
317         ConfValue("power","restartpass",0,restartpass,&config_f);
318         ConfValue("options","prefixquit",0,PrefixQuit,&config_f);
319         ConfValue("die","value",0,DieValue,&config_f);
320         ConfValue("options","loglevel",0,dbg,&config_f);
321         ConfValue("options","netbuffersize",0,NB,&config_f);
322         NetBufferSize = atoi(NB);
323         if ((!NetBufferSize) || (NetBufferSize > 65535) || (NetBufferSize < 1024))
324         {
325                 log(DEFAULT,"No NetBufferSize specified or size out of range, setting to default of 10240.");
326                 NetBufferSize = 10240;
327         }
328         if (!strcmp(dbg,"debug"))
329                 LogLevel = DEBUG;
330         if (!strcmp(dbg,"verbose"))
331                 LogLevel = VERBOSE;
332         if (!strcmp(dbg,"default"))
333                 LogLevel = DEFAULT;
334         if (!strcmp(dbg,"sparse"))
335                 LogLevel = SPARSE;
336         if (!strcmp(dbg,"none"))
337                 LogLevel = NONE;
338         readfile(MOTD,motd);
339         log(DEFAULT,"Reading message of the day...");
340         readfile(RULES,rules);
341         log(DEFAULT,"Reading connect classes...");
342         Classes.clear();
343         for (int i = 0; i < ConfValueEnum("connect",&config_f); i++)
344         {
345                 strcpy(Value,"");
346                 ConfValue("connect","allow",i,Value,&config_f);
347                 ConfValue("connect","timeout",i,timeout,&config_f);
348                 ConfValue("connect","flood",i,flood,&config_f);
349                 if (strcmp(Value,""))
350                 {
351                         strcpy(c.host,Value);
352                         c.type = CC_ALLOW;
353                         strcpy(Value,"");
354                         ConfValue("connect","password",i,Value,&config_f);
355                         strcpy(c.pass,Value);
356                         c.registration_timeout = 90; // default is 2 minutes
357                         c.flood = atoi(flood);
358                         if (atoi(timeout)>0)
359                         {
360                                 c.registration_timeout = atoi(timeout);
361                         }
362                         Classes.push_back(c);
363                         log(DEBUG,"Read connect class type ALLOW, host=%s password=%s timeout=%d flood=%d",c.host,c.pass,c.registration_timeout,c.flood);
364                 }
365                 else
366                 {
367                         ConfValue("connect","deny",i,Value,&config_f);
368                         strcpy(c.host,Value);
369                         c.type = CC_DENY;
370                         Classes.push_back(c);
371                         log(DEBUG,"Read connect class type DENY, host=%s",c.host);
372                 }
373         
374         }
375         log(DEFAULT,"Reading K lines,Q lines and Z lines from config...");
376         read_xline_defaults();
377         log(DEFAULT,"Applying K lines, Q lines and Z lines...");
378         apply_lines();
379         log(DEFAULT,"Done reading configuration file, InspIRCd is now running.");
380 }
381
382 /* write formatted text to a socket, in same format as printf */
383
384 void Write(int sock,char *text, ...)
385 {
386         if (!text)
387         {
388                 log(DEFAULT,"*** BUG *** Write was given an invalid parameter");
389                 return;
390         }
391         char textbuffer[MAXBUF];
392         va_list argsPtr;
393         char tb[MAXBUF];
394         
395         va_start (argsPtr, text);
396         vsnprintf(textbuffer, MAXBUF, text, argsPtr);
397         va_end(argsPtr);
398         sprintf(tb,"%s\r\n",textbuffer);
399         chop(tb);
400         if (sock != -1)
401         {
402                 write(sock,tb,strlen(tb));
403                 update_stats_l(sock,strlen(tb)); /* add one line-out to stats L for this fd */
404         }
405 }
406
407 /* write a server formatted numeric response to a single socket */
408
409 void WriteServ(int sock, char* text, ...)
410 {
411         if (!text)
412         {
413                 log(DEFAULT,"*** BUG *** WriteServ was given an invalid parameter");
414                 return;
415         }
416         char textbuffer[MAXBUF],tb[MAXBUF];
417         va_list argsPtr;
418         va_start (argsPtr, text);
419         
420         vsnprintf(textbuffer, MAXBUF, text, argsPtr);
421         va_end(argsPtr);
422         sprintf(tb,":%s %s\r\n",ServerName,textbuffer);
423         chop(tb);
424         if (sock != -1)
425         {
426                 write(sock,tb,strlen(tb));
427                 update_stats_l(sock,strlen(tb)); /* add one line-out to stats L for this fd */
428         }
429 }
430
431 /* write text from an originating user to originating user */
432
433 void WriteFrom(int sock, userrec *user,char* text, ...)
434 {
435         if ((!text) || (!user))
436         {
437                 log(DEFAULT,"*** BUG *** WriteFrom was given an invalid parameter");
438                 return;
439         }
440         char textbuffer[MAXBUF],tb[MAXBUF];
441         va_list argsPtr;
442         va_start (argsPtr, text);
443         
444         vsnprintf(textbuffer, MAXBUF, text, argsPtr);
445         va_end(argsPtr);
446         sprintf(tb,":%s!%s@%s %s\r\n",user->nick,user->ident,user->dhost,textbuffer);
447         chop(tb);
448         if (sock != -1)
449         {
450                 write(sock,tb,strlen(tb));
451                 update_stats_l(sock,strlen(tb)); /* add one line-out to stats L for this fd */
452         }
453 }
454
455 /* write text to an destination user from a source user (e.g. user privmsg) */
456
457 void WriteTo(userrec *source, userrec *dest,char *data, ...)
458 {
459         if ((!dest) || (!data))
460         {
461                 log(DEFAULT,"*** BUG *** WriteTo was given an invalid parameter");
462                 return;
463         }
464         char textbuffer[MAXBUF],tb[MAXBUF];
465         va_list argsPtr;
466         va_start (argsPtr, data);
467         vsnprintf(textbuffer, MAXBUF, data, argsPtr);
468         va_end(argsPtr);
469         chop(tb);
470
471         // if no source given send it from the server.
472         if (!source)
473         {
474                 WriteServ(dest->fd,":%s %s",ServerName,textbuffer);
475         }
476         else
477         {
478                 WriteFrom(dest->fd,source,"%s",textbuffer);
479         }
480 }
481
482 /* write formatted text from a source user to all users on a channel
483  * including the sender (NOT for privmsg, notice etc!) */
484
485 void WriteChannel(chanrec* Ptr, userrec* user, char* text, ...)
486 {
487         if ((!Ptr) || (!user) || (!text))
488         {
489                 log(DEFAULT,"*** BUG *** WriteChannel was given an invalid parameter");
490                 return;
491         }
492         char textbuffer[MAXBUF];
493         va_list argsPtr;
494         va_start (argsPtr, text);
495         vsnprintf(textbuffer, MAXBUF, text, argsPtr);
496         va_end(argsPtr);
497         for (user_hash::const_iterator i = clientlist.begin(); i != clientlist.end(); i++)
498         {
499                 if (has_channel(i->second,Ptr))
500                 {
501                         WriteTo(user,i->second,"%s",textbuffer);
502                 }
503         }
504 }
505
506 /* write formatted text from a source user to all users on a channel
507  * including the sender (NOT for privmsg, notice etc!) doesnt send to
508  * users on remote servers */
509
510 void WriteChannelLocal(chanrec* Ptr, userrec* user, char* text, ...)
511 {
512         if ((!Ptr) || (!text))
513         {
514                 log(DEFAULT,"*** BUG *** WriteChannel was given an invalid parameter");
515                 return;
516         }
517         char textbuffer[MAXBUF];
518         va_list argsPtr;
519         va_start (argsPtr, text);
520         vsnprintf(textbuffer, MAXBUF, text, argsPtr);
521         va_end(argsPtr);
522         for (user_hash::const_iterator i = clientlist.begin(); i != clientlist.end(); i++)
523         {
524                 if (has_channel(i->second,Ptr))
525                 {
526                         if (i->second->fd != -1)
527                         {
528                                 if (!user)
529                                 {
530                                         WriteServ(i->second->fd,"%s",textbuffer);
531                                 }
532                                 else
533                                 {
534                                         WriteTo(user,i->second,"%s",textbuffer);
535                                 }
536                         }       
537                 }
538         }
539 }
540
541
542 void WriteChannelWithServ(char* ServerName, chanrec* Ptr, userrec* user, char* text, ...)
543 {
544         if ((!Ptr) || (!user) || (!text))
545         {
546                 log(DEFAULT,"*** BUG *** WriteChannelWithServ was given an invalid parameter");
547                 return;
548         }
549         char textbuffer[MAXBUF];
550         va_list argsPtr;
551         va_start (argsPtr, text);
552         vsnprintf(textbuffer, MAXBUF, text, argsPtr);
553         va_end(argsPtr);
554         for (user_hash::const_iterator i = clientlist.begin(); i != clientlist.end(); i++)
555         {
556                 if (i->second)
557                 {
558                         if (has_channel(i->second,Ptr))
559                         {
560                                 WriteServ(i->second->fd,"%s",textbuffer);
561                         }
562                 }
563         }
564 }
565
566
567 /* write formatted text from a source user to all users on a channel except
568  * for the sender (for privmsg etc) */
569
570 void ChanExceptSender(chanrec* Ptr, userrec* user, char* text, ...)
571 {
572         if ((!Ptr) || (!user) || (!text))
573         {
574                 log(DEFAULT,"*** BUG *** ChanExceptSender was given an invalid parameter");
575                 return;
576         }
577         char textbuffer[MAXBUF];
578         va_list argsPtr;
579         va_start (argsPtr, text);
580         vsnprintf(textbuffer, MAXBUF, text, argsPtr);
581         va_end(argsPtr);
582
583         for (user_hash::const_iterator i = clientlist.begin(); i != clientlist.end(); i++)
584         {
585                 if (i->second)
586                 {
587                         if (has_channel(i->second,Ptr) && (user != i->second))
588                         {
589                                 WriteTo(user,i->second,"%s",textbuffer);
590                         }
591                 }
592         }
593 }
594
595
596 std::string GetServerDescription(char* servername)
597 {
598         for (int j = 0; j < 32; j++)
599         {
600                 if (me[j] != NULL)
601                 {
602                         for (int k = 0; k < me[j]->connectors.size(); k++)
603                         {
604                                 if (!strcasecmp(me[j]->connectors[k].GetServerName().c_str(),servername))
605                                 {
606                                         return me[j]->connectors[k].GetDescription();
607                                 }
608                         }
609                 }
610                 return ServerDesc; // not a remote server that can be found, it must be me.
611         }
612 }
613
614
615 /* write a formatted string to all users who share at least one common
616  * channel, including the source user e.g. for use in NICK */
617
618 void WriteCommon(userrec *u, char* text, ...)
619 {
620         if (!u)
621         {
622                 log(DEFAULT,"*** BUG *** WriteCommon was given an invalid parameter");
623                 return;
624         }
625
626         if (u->registered != 7) {
627                 log(DEFAULT,"*** BUG *** WriteCommon on an unregistered user");
628                 return;
629         }
630         
631         char textbuffer[MAXBUF];
632         va_list argsPtr;
633         va_start (argsPtr, text);
634         vsnprintf(textbuffer, MAXBUF, text, argsPtr);
635         va_end(argsPtr);
636
637         WriteFrom(u->fd,u,"%s",textbuffer);
638
639         for (user_hash::const_iterator i = clientlist.begin(); i != clientlist.end(); i++)
640         {
641                 if (i->second)
642                 {
643                         if (common_channels(u,i->second) && (i->second != u))
644                         {
645                                 WriteFrom(i->second->fd,u,"%s",textbuffer);
646                         }
647                 }
648         }
649 }
650
651 /* write a formatted string to all users who share at least one common
652  * channel, NOT including the source user e.g. for use in QUIT */
653
654 void WriteCommonExcept(userrec *u, char* text, ...)
655 {
656         if (!u)
657         {
658                 log(DEFAULT,"*** BUG *** WriteCommon was given an invalid parameter");
659                 return;
660         }
661
662         if (u->registered != 7) {
663                 log(DEFAULT,"*** BUG *** WriteCommon on an unregistered user");
664                 return;
665         }
666
667         char textbuffer[MAXBUF];
668         va_list argsPtr;
669         va_start (argsPtr, text);
670         vsnprintf(textbuffer, MAXBUF, text, argsPtr);
671         va_end(argsPtr);
672
673         for (user_hash::const_iterator i = clientlist.begin(); i != clientlist.end(); i++)
674         {
675                 if (i->second)
676                 {
677                         if ((common_channels(u,i->second)) && (u != i->second))
678                         {
679                                 WriteFrom(i->second->fd,u,"%s",textbuffer);
680                         }
681                 }
682         }
683 }
684
685 void WriteOpers(char* text, ...)
686 {
687         if (!text)
688         {
689                 log(DEFAULT,"*** BUG *** WriteOpers was given an invalid parameter");
690                 return;
691         }
692
693         char textbuffer[MAXBUF];
694         va_list argsPtr;
695         va_start (argsPtr, text);
696         vsnprintf(textbuffer, MAXBUF, text, argsPtr);
697         va_end(argsPtr);
698
699         for (user_hash::const_iterator i = clientlist.begin(); i != clientlist.end(); i++)
700         {
701                 if (i->second)
702                 {
703                         if (strchr(i->second->modes,'o'))
704                         {
705                                 if (strchr(i->second->modes,'s'))
706                                 {
707                                         // send server notices to all with +s
708                                         // (TODO: needs SNOMASKs)
709                                         WriteServ(i->second->fd,"NOTICE %s :%s",i->second->nick,textbuffer);
710                                 }
711                         }
712                 }
713         }
714 }
715
716 // returns TRUE of any users on channel C occupy server 'servername'.
717
718 bool ChanAnyOnThisServer(chanrec *c,char* servername)
719 {
720         log(DEBUG,"ChanAnyOnThisServer");
721         for (user_hash::iterator i = clientlist.begin(); i != clientlist.end(); i++)
722         {
723                 if (has_channel(i->second,c))
724                 {
725                         if (!strcasecmp(i->second->server,servername))
726                         {
727                                 return true;
728                         }
729                 }
730         }
731         return false;
732 }
733
734 // returns true if user 'u' shares any common channels with any users on server 'servername'
735
736 bool CommonOnThisServer(userrec* u,const char* servername)
737 {
738         log(DEBUG,"ChanAnyOnThisServer");
739         for (user_hash::iterator i = clientlist.begin(); i != clientlist.end(); i++)
740         {
741                 if ((common_channels(u,i->second)) && (u != i->second))
742                 {
743                         if (!strcasecmp(i->second->server,servername))
744                         {
745                                 log(DEBUG,"%s is common to %s sharing with %s",i->second->nick,servername,u->nick);
746                                 return true;
747                         }
748                 }
749         }
750         return false;
751 }
752
753
754 void NetSendToCommon(userrec* u, char* s)
755 {
756         char buffer[MAXBUF];
757         snprintf(buffer,MAXBUF,"%s",s);
758         
759         log(DEBUG,"NetSendToCommon: '%s' '%s'",u->nick,s);
760
761         for (int j = 0; j < 32; j++)
762         {
763                 if (me[j] != NULL)
764                 {
765                         for (int k = 0; k < me[j]->connectors.size(); k++)
766                         {
767                                 if (CommonOnThisServer(u,me[j]->connectors[k].GetServerName().c_str()))
768                                 {
769                                         me[j]->SendPacket(buffer,me[j]->connectors[k].GetServerName().c_str());
770                                 }
771                         }
772                 }
773         }
774 }
775
776
777 void NetSendToAll(char* s)
778 {
779         char buffer[MAXBUF];
780         snprintf(buffer,MAXBUF,"%s",s);
781         
782         log(DEBUG,"NetSendToAll: '%s'",s);
783
784         for (int j = 0; j < 32; j++)
785         {
786                 if (me[j] != NULL)
787                 {
788                         for (int k = 0; k < me[j]->connectors.size(); k++)
789                         {
790                                 me[j]->SendPacket(buffer,me[j]->connectors[k].GetServerName().c_str());
791                         }
792                 }
793         }
794 }
795
796 void NetSendToAllAlive(char* s)
797 {
798         char buffer[MAXBUF];
799         snprintf(buffer,MAXBUF,"%s",s);
800         
801         log(DEBUG,"NetSendToAllAlive: '%s'",s);
802
803         for (int j = 0; j < 32; j++)
804         {
805                 if (me[j] != NULL)
806                 {
807                         for (int k = 0; k < me[j]->connectors.size(); k++)
808                         {
809                                 if (me[j]->connectors[k].GetState() != STATE_DISCONNECTED)
810                                 {
811                                         me[j]->SendPacket(buffer,me[j]->connectors[k].GetServerName().c_str());
812                                 }
813                                 else
814                                 {
815                                         log(DEBUG,"%s is dead, not sending to it.",me[j]->connectors[k].GetServerName().c_str());
816                                 }
817                         }
818                 }
819         }
820 }
821
822
823 void NetSendToOne(char* target,char* s)
824 {
825         char buffer[MAXBUF];
826         snprintf(buffer,MAXBUF,"%s",s);
827         
828         log(DEBUG,"NetSendToOne: '%s' '%s'",target,s);
829
830         for (int j = 0; j < 32; j++)
831         {
832                 if (me[j] != NULL)
833                 {
834                         for (int k = 0; k < me[j]->connectors.size(); k++)
835                         {
836                                 if (!strcasecmp(me[j]->connectors[k].GetServerName().c_str(),target))
837                                 {
838                                         me[j]->SendPacket(buffer,me[j]->connectors[k].GetServerName().c_str());
839                                 }
840                         }
841                 }
842         }
843 }
844
845 void NetSendToAllExcept(const char* target,char* s)
846 {
847         char buffer[MAXBUF];
848         snprintf(buffer,MAXBUF,"%s",s);
849         
850         log(DEBUG,"NetSendToAllExcept: '%s' '%s'",target,s);
851         
852         for (int j = 0; j < 32; j++)
853         {
854                 if (me[j] != NULL)
855                 {
856                         for (int k = 0; k < me[j]->connectors.size(); k++)
857                         {
858                                 if (strcasecmp(me[j]->connectors[k].GetServerName().c_str(),target))
859                                 {
860                                         me[j]->SendPacket(buffer,me[j]->connectors[k].GetServerName().c_str());
861                                 }
862                         }
863                 }
864         }
865 }
866
867
868 void WriteMode(const char* modes, int flags, const char* text, ...)
869 {
870         if ((!text) || (!modes) || (!flags))
871         {
872                 log(DEFAULT,"*** BUG *** WriteMode was given an invalid parameter");
873                 return;
874         }
875
876         char textbuffer[MAXBUF];
877         va_list argsPtr;
878         va_start (argsPtr, text);
879         vsnprintf(textbuffer, MAXBUF, text, argsPtr);
880         va_end(argsPtr);
881
882         for (user_hash::const_iterator i = clientlist.begin(); i != clientlist.end(); i++)
883         {
884                 if (i->second)
885                 {
886                         bool send_to_user = false;
887                         
888                         if (flags == WM_AND)
889                         {
890                                 send_to_user = true;
891                                 for (int n = 0; n < strlen(modes); n++)
892                                 {
893                                         if (!hasumode(i->second,modes[n]))
894                                         {
895                                                 send_to_user = false;
896                                                 break;
897                                         }
898                                 }
899                         }
900                         else if (flags == WM_OR)
901                         {
902                                 send_to_user = false;
903                                 for (int n = 0; n < strlen(modes); n++)
904                                 {
905                                         if (hasumode(i->second,modes[n]))
906                                         {
907                                                 send_to_user = true;
908                                                 break;
909                                         }
910                                 }
911                         }
912
913                         if (send_to_user)
914                         {
915                                 WriteServ(i->second->fd,"NOTICE %s :%s",i->second->nick,textbuffer);
916                         }
917                 }
918         }
919 }
920
921
922 void WriteWallOps(userrec *source, bool local_only, char* text, ...)  
923 {  
924         if ((!text) || (!source))
925         {
926                 log(DEFAULT,"*** BUG *** WriteOpers was given an invalid parameter");
927                 return;
928         }
929
930         int i = 0;  
931         char textbuffer[MAXBUF];  
932         va_list argsPtr;  
933         va_start (argsPtr, text);  
934         vsnprintf(textbuffer, MAXBUF, text, argsPtr);  
935         va_end(argsPtr);  
936   
937         for (user_hash::const_iterator i = clientlist.begin(); i != clientlist.end(); i++)
938         {
939                 if (i->second)
940                 {
941                         if (strchr(i->second->modes,'w'))
942                         {
943                                 WriteTo(source,i->second,"WALLOPS %s",textbuffer);
944                         }
945                 }
946         }
947
948         if (!local_only)
949         {
950                 char buffer[MAXBUF];
951                 snprintf(buffer,MAXBUF,"@ %s :%s",source->nick,textbuffer);
952                 NetSendToAll(buffer);
953         }
954 }  
955
956 /* convert a string to lowercase. Note following special circumstances
957  * taken from RFC 1459. Many "official" server branches still hold to this
958  * rule so i will too;
959  *
960  *  Because of IRC's scandanavian origin, the characters {}| are
961  *  considered to be the lower case equivalents of the characters []\,
962  *  respectively. This is a critical issue when determining the
963  *  equivalence of two nicknames.
964  */
965
966 void strlower(char *n)
967 {
968         if (!n)
969         {
970                 return;
971         }
972         for (int i = 0; i != strlen(n); i++)
973         {
974                 n[i] = tolower(n[i]);
975                 if (n[i] == '[')
976                         n[i] = '{';
977                 if (n[i] == ']')
978                         n[i] = '}';
979                 if (n[i] == '\\')
980                         n[i] = '|';
981         }
982 }
983
984
985
986 /* Find a user record by nickname and return a pointer to it */
987
988 userrec* Find(string nick)
989 {
990         user_hash::iterator iter = clientlist.find(nick);
991
992         if (iter == clientlist.end())
993                 /* Couldn't find it */
994                 return NULL;
995
996         return iter->second;
997 }
998
999 void update_stats_l(int fd,int data_out) /* add one line-out to stats L for this fd */
1000 {
1001         for (user_hash::const_iterator i = clientlist.begin(); i != clientlist.end(); i++)
1002         {
1003                 if (i->second)
1004                 {
1005                         if (i->second->fd == fd)
1006                         {
1007                                 i->second->bytes_out+=data_out;
1008                                 i->second->cmds_out++;
1009                         }
1010                 }
1011         }
1012 }
1013
1014
1015 /* find a channel record by channel name and return a pointer to it */
1016
1017 chanrec* FindChan(const char* chan)
1018 {
1019         if (!chan)
1020         {
1021                 log(DEFAULT,"*** BUG *** Findchan was given an invalid parameter");
1022                 return NULL;
1023         }
1024
1025         chan_hash::iterator iter = chanlist.find(chan);
1026
1027         if (iter == chanlist.end())
1028                 /* Couldn't find it */
1029                 return NULL;
1030
1031         return iter->second;
1032 }
1033
1034
1035 void purge_empty_chans(void)
1036 {
1037         int go_again = 1, purge = 0;
1038         
1039         while (go_again)
1040         {
1041                 go_again = 0;
1042                 for (chan_hash::iterator i = chanlist.begin(); i != chanlist.end(); i++)
1043                 {
1044                         if (i->second) {
1045                                 if (!usercount(i->second))
1046                                 {
1047                                         /* kill the record */
1048                                         if (i != chanlist.end())
1049                                         {
1050                                                 log(DEBUG,"del_channel: destroyed: %s",i->second->name);
1051                                                 delete i->second;
1052                                                 chanlist.erase(i);
1053                                                 go_again = 1;
1054                                                 purge++;
1055                                                 break;
1056                                         }
1057                                 }
1058                                 else
1059                                 {
1060                                         log(DEBUG,"skipped purge for %s",i->second->name);
1061                                 }
1062                         }
1063                 }
1064         }
1065         log(DEBUG,"completed channel purge, killed %d",purge);
1066 }
1067
1068
1069 char scratch[MAXBUF];
1070 char sparam[MAXBUF];
1071
1072 char* chanmodes(chanrec *chan)
1073 {
1074         if (!chan)
1075         {
1076                 log(DEFAULT,"*** BUG *** chanmodes was given an invalid parameter");
1077                 strcpy(scratch,"");
1078                 return scratch;
1079         }
1080
1081         strcpy(scratch,"");
1082         strcpy(sparam,"");
1083         if (chan->noexternal)
1084         {
1085                 strncat(scratch,"n",MAXMODES);
1086         }
1087         if (chan->topiclock)
1088         {
1089                 strncat(scratch,"t",MAXMODES);
1090         }
1091         if (strcmp(chan->key,""))
1092         {
1093                 strncat(scratch,"k",MAXMODES);
1094         }
1095         if (chan->limit)
1096         {
1097                 strncat(scratch,"l",MAXMODES);
1098         }
1099         if (chan->inviteonly)
1100         {
1101                 strncat(scratch,"i",MAXMODES);
1102         }
1103         if (chan->moderated)
1104         {
1105                 strncat(scratch,"m",MAXMODES);
1106         }
1107         if (chan->secret)
1108         {
1109                 strncat(scratch,"s",MAXMODES);
1110         }
1111         if (chan->c_private)
1112         {
1113                 strncat(scratch,"p",MAXMODES);
1114         }
1115         if (strcmp(chan->key,""))
1116         {
1117                 strncat(sparam,chan->key,MAXBUF);
1118         }
1119         if (chan->limit)
1120         {
1121                 char foo[24];
1122                 sprintf(foo," %d",chan->limit);
1123                 strncat(sparam,foo,MAXBUF);
1124         }
1125         if (strlen(chan->custom_modes))
1126         {
1127                 strncat(scratch,chan->custom_modes,MAXMODES);
1128                 for (int z = 0; z < strlen(chan->custom_modes); z++)
1129                 {
1130                         std::string extparam = chan->GetModeParameter(chan->custom_modes[z]);
1131                         if (extparam != "")
1132                         {
1133                                 strncat(sparam," ",MAXBUF);
1134                                 strncat(sparam,extparam.c_str(),MAXBUF);
1135                         }
1136                 }
1137         }
1138         log(DEBUG,"chanmodes: %s %s%s",chan->name,scratch,sparam);
1139         strncat(scratch,sparam,MAXMODES);
1140         return scratch;
1141 }
1142
1143
1144 /* compile a userlist of a channel into a string, each nick seperated by
1145  * spaces and op, voice etc status shown as @ and + */
1146
1147 void userlist(userrec *user,chanrec *c)
1148 {
1149         if ((!c) || (!user))
1150         {
1151                 log(DEFAULT,"*** BUG *** userlist was given an invalid parameter");
1152                 return;
1153         }
1154
1155         sprintf(list,"353 %s = %s :", user->nick, c->name);
1156         for (user_hash::const_iterator i = clientlist.begin(); i != clientlist.end(); i++)
1157         {
1158                 if (has_channel(i->second,c))
1159                 {
1160                         if (isnick(i->second->nick))
1161                         {
1162                                 if ((!has_channel(i->second,c)) && (strchr(i->second->modes,'i')))
1163                                 {
1164                                         /* user is +i, and source not on the channel, does not show
1165                                          * nick in NAMES list */
1166                                         continue;
1167                                 }
1168                                 strcat(list,cmode(i->second,c));
1169                                 strcat(list,i->second->nick);
1170                                 strcat(list," ");
1171                                 if (strlen(list)>(480-NICKMAX))
1172                                 {
1173                                         /* list overflowed into
1174                                          * multiple numerics */
1175                                         WriteServ(user->fd,list);
1176                                         sprintf(list,"353 %s = %s :", user->nick, c->name);
1177                                 }
1178                         }
1179                 }
1180         }
1181         /* if whats left in the list isnt empty, send it */
1182         if (list[strlen(list)-1] != ':')
1183         {
1184                 WriteServ(user->fd,list);
1185         }
1186 }
1187
1188 /* return a count of the users on a specific channel accounting for
1189  * invisible users who won't increase the count. e.g. for /LIST */
1190
1191 int usercount_i(chanrec *c)
1192 {
1193         int i = 0;
1194         int count = 0;
1195         
1196         if (!c)
1197         {
1198                 log(DEFAULT,"*** BUG *** usercount_i was given an invalid parameter");
1199                 return 0;
1200         }
1201
1202         strcpy(list,"");
1203         for (user_hash::const_iterator i = clientlist.begin(); i != clientlist.end(); i++)
1204         {
1205                 if (i->second)
1206                 {
1207                         if (has_channel(i->second,c))
1208                         {
1209                                 if (isnick(i->second->nick))
1210                                 {
1211                                         if ((!has_channel(i->second,c)) && (strchr(i->second->modes,'i')))
1212                                         {
1213                                                 /* user is +i, and source not on the channel, does not show
1214                                                  * nick in NAMES list */
1215                                                 continue;
1216                                         }
1217                                         count++;
1218                                 }
1219                         }
1220                 }
1221         }
1222         log(DEBUG,"usercount_i: %s %d",c->name,count);
1223         return count;
1224 }
1225
1226
1227 int usercount(chanrec *c)
1228 {
1229         int i = 0;
1230         int count = 0;
1231         
1232         if (!c)
1233         {
1234                 log(DEFAULT,"*** BUG *** usercount was given an invalid parameter");
1235                 return 0;
1236         }
1237
1238         strcpy(list,"");
1239         for (user_hash::const_iterator i = clientlist.begin(); i != clientlist.end(); i++)
1240         {
1241                 if (i->second)
1242                 {
1243                         if (has_channel(i->second,c))
1244                         {
1245                                 if ((isnick(i->second->nick)) && (i->second->registered == 7))
1246                                 {
1247                                         count++;
1248                                 }
1249                         }
1250                 }
1251         }
1252         log(DEBUG,"usercount: %s %d",c->name,count);
1253         return count;
1254 }
1255
1256
1257 /* add a channel to a user, creating the record for it if needed and linking
1258  * it to the user record */
1259
1260 chanrec* add_channel(userrec *user, const char* cn, const char* key, bool override)
1261 {
1262         if ((!user) || (!cn))
1263         {
1264                 log(DEFAULT,"*** BUG *** add_channel was given an invalid parameter");
1265                 return 0;
1266         }
1267
1268         int i = 0;
1269         chanrec* Ptr;
1270         int created = 0;
1271         char cname[MAXBUF];
1272
1273         strncpy(cname,cn,MAXBUF);
1274         
1275         // we MUST declare this wherever we use FOREACH_RESULT
1276         int MOD_RESULT = 0;
1277
1278         if (strlen(cname) > CHANMAX-1)
1279         {
1280                 cname[CHANMAX-1] = '\0';
1281         }
1282
1283         log(DEBUG,"add_channel: %s %s",user->nick,cname);
1284         
1285         if ((FindChan(cname)) && (has_channel(user,FindChan(cname))))
1286         {
1287                 return NULL; // already on the channel!
1288         }
1289
1290
1291         if (!FindChan(cname))
1292         {
1293                 FOREACH_RESULT(OnUserPreJoin(user,NULL,cname));
1294                 if (MOD_RESULT) {
1295                         return NULL;
1296                 }
1297
1298                 /* create a new one */
1299                 log(DEBUG,"add_channel: creating: %s",cname);
1300                 {
1301                         chanlist[cname] = new chanrec();
1302
1303                         strcpy(chanlist[cname]->name, cname);
1304                         chanlist[cname]->topiclock = 1;
1305                         chanlist[cname]->noexternal = 1;
1306                         chanlist[cname]->created = time(NULL);
1307                         strcpy(chanlist[cname]->topic, "");
1308                         strncpy(chanlist[cname]->setby, user->nick,NICKMAX);
1309                         chanlist[cname]->topicset = 0;
1310                         Ptr = chanlist[cname];
1311                         log(DEBUG,"add_channel: created: %s",cname);
1312                         /* set created to 2 to indicate user
1313                          * is the first in the channel
1314                          * and should be given ops */
1315                         created = 2;
1316                 }
1317         }
1318         else
1319         {
1320                 /* channel exists, just fish out a pointer to its struct */
1321                 Ptr = FindChan(cname);
1322                 if (Ptr)
1323                 {
1324                         log(DEBUG,"add_channel: joining to: %s",Ptr->name);
1325                         
1326                         // the override flag allows us to bypass channel modes
1327                         // and bans (used by servers)
1328                         if (!override)
1329                         {
1330                                 FOREACH_RESULT(OnUserPreJoin(user,Ptr,cname));
1331                                 if (MOD_RESULT) {
1332                                         return NULL;
1333                                 }
1334                                 
1335                                 if (strcmp(Ptr->key,""))
1336                                 {
1337                                         log(DEBUG,"add_channel: %s has key %s",Ptr->name,Ptr->key);
1338                                         if (!key)
1339                                         {
1340                                                 log(DEBUG,"add_channel: no key given in JOIN");
1341                                                 WriteServ(user->fd,"475 %s %s :Cannot join channel (Requires key)",user->nick, Ptr->name);
1342                                                 return NULL;
1343                                         }
1344                                         else
1345                                         {
1346                                                 log(DEBUG,"key at %p is %s",key,key);
1347                                                 if (strcasecmp(key,Ptr->key))
1348                                                 {
1349                                                         log(DEBUG,"add_channel: bad key given in JOIN");
1350                                                         WriteServ(user->fd,"475 %s %s :Cannot join channel (Incorrect key)",user->nick, Ptr->name);
1351                                                         return NULL;
1352                                                 }
1353                                         }
1354                                 }
1355                                 log(DEBUG,"add_channel: no key");
1356         
1357                                 if (Ptr->inviteonly)
1358                                 {
1359                                         log(DEBUG,"add_channel: channel is +i");
1360                                         if (user->IsInvited(Ptr->name))
1361                                         {
1362                                                 /* user was invited to channel */
1363                                                 /* there may be an optional channel NOTICE here */
1364                                         }
1365                                         else
1366                                         {
1367                                                 WriteServ(user->fd,"473 %s %s :Cannot join channel (Invite only)",user->nick, Ptr->name);
1368                                                 return NULL;
1369                                         }
1370                                 }
1371                                 log(DEBUG,"add_channel: channel is not +i");
1372         
1373                                 if (Ptr->limit)
1374                                 {
1375                                         if (usercount(Ptr) == Ptr->limit)
1376                                         {
1377                                                 WriteServ(user->fd,"471 %s %s :Cannot join channel (Channel is full)",user->nick, Ptr->name);
1378                                                 return NULL;
1379                                         }
1380                                 }
1381                                 
1382                                 log(DEBUG,"add_channel: about to walk banlist");
1383         
1384                                 /* check user against the channel banlist */
1385                                 if (Ptr)
1386                                 {
1387                                         if (Ptr->bans.size())
1388                                         {
1389                                                 for (BanList::iterator i = Ptr->bans.begin(); i != Ptr->bans.end(); i++)
1390                                                 {
1391                                                         if (match(user->GetFullHost(),i->data))
1392                                                         {
1393                                                                 WriteServ(user->fd,"474 %s %s :Cannot join channel (You're banned)",user->nick, Ptr->name);
1394                                                                 return NULL;
1395                                                         }
1396                                                 }
1397                                         }
1398                                 }
1399                                 
1400                                 log(DEBUG,"add_channel: bans checked");
1401                                 
1402
1403                                 if ((Ptr) && (user))
1404                                 {
1405                                         user->RemoveInvite(Ptr->name);
1406                                 }
1407         
1408                                 log(DEBUG,"add_channel: invites removed");
1409
1410                         }
1411                         else
1412                         {
1413                                 log(DEBUG,"Overridden checks");
1414                         }
1415
1416                         
1417                 }
1418                 created = 1;
1419         }
1420
1421         log(DEBUG,"Passed channel checks");
1422         
1423         for (int i =0; i != MAXCHANS; i++)
1424         {
1425                 log(DEBUG,"Check location %d",i);
1426                 if (user->chans[i].channel == NULL)
1427                 {
1428                         log(DEBUG,"Adding into their channel list at location %d",i);
1429
1430                         if (created == 2) 
1431                         {
1432                                 /* first user in is given ops */
1433                                 user->chans[i].uc_modes = UCMODE_OP;
1434                         }
1435                         else
1436                         {
1437                                 user->chans[i].uc_modes = 0;
1438                         }
1439                         user->chans[i].channel = Ptr;
1440                         WriteChannel(Ptr,user,"JOIN :%s",Ptr->name);
1441                         
1442                         if (!override) // we're not overriding... so this isnt part of a netburst, broadcast it.
1443                         {
1444                                 // use the stamdard J token with no privilages.
1445                                 char buffer[MAXBUF];
1446                                 snprintf(buffer,MAXBUF,"J %s %s",user->nick,Ptr->name);
1447                                 NetSendToAll(buffer);
1448                         }
1449
1450                         log(DEBUG,"Sent JOIN to client");
1451
1452                         if (Ptr->topicset)
1453                         {
1454                                 WriteServ(user->fd,"332 %s %s :%s", user->nick, Ptr->name, Ptr->topic);
1455                                 WriteServ(user->fd,"333 %s %s %s %d", user->nick, Ptr->name, Ptr->setby, Ptr->topicset);
1456                         }
1457                         userlist(user,Ptr);
1458                         WriteServ(user->fd,"366 %s %s :End of /NAMES list.", user->nick, Ptr->name);
1459                         WriteServ(user->fd,"324 %s %s +%s",user->nick, Ptr->name,chanmodes(Ptr));
1460                         WriteServ(user->fd,"329 %s %s %d", user->nick, Ptr->name, Ptr->created);
1461                         FOREACH_MOD OnUserJoin(user,Ptr);
1462                         return Ptr;
1463                 }
1464         }
1465         log(DEBUG,"add_channel: user channel max exceeded: %s %s",user->nick,cname);
1466         WriteServ(user->fd,"405 %s %s :You are on too many channels",user->nick, cname);
1467         return NULL;
1468 }
1469
1470 /* remove a channel from a users record, and remove the record from memory
1471  * if the channel has become empty */
1472
1473 chanrec* del_channel(userrec *user, const char* cname, const char* reason, bool local)
1474 {
1475         if ((!user) || (!cname))
1476         {
1477                 log(DEFAULT,"*** BUG *** del_channel was given an invalid parameter");
1478                 return NULL;
1479         }
1480
1481         chanrec* Ptr;
1482         int created = 0;
1483
1484         if ((!cname) || (!user))
1485         {
1486                 return NULL;
1487         }
1488
1489         Ptr = FindChan(cname);
1490         
1491         if (!Ptr)
1492         {
1493                 return NULL;
1494         }
1495
1496         FOREACH_MOD OnUserPart(user,Ptr);
1497         log(DEBUG,"del_channel: removing: %s %s",user->nick,Ptr->name);
1498         
1499         for (int i =0; i != MAXCHANS; i++)
1500         {
1501                 /* zap it from the channel list of the user */
1502                 if (user->chans[i].channel == Ptr)
1503                 {
1504                         if (reason)
1505                         {
1506                                 WriteChannel(Ptr,user,"PART %s :%s",Ptr->name, reason);
1507
1508                                 if (!local)
1509                                 {
1510                                         char buffer[MAXBUF];
1511                                         snprintf(buffer,MAXBUF,"L %s %s :%s",user->nick,Ptr->name,reason);
1512                                         NetSendToAll(buffer);
1513                                 }
1514
1515                                 
1516                         }
1517                         else
1518                         {
1519                                 if (!local)
1520                                 {
1521                                         char buffer[MAXBUF];
1522                                         snprintf(buffer,MAXBUF,"L %s %s :",user->nick,Ptr->name);
1523                                         NetSendToAll(buffer);
1524                                 }
1525                         
1526                                 WriteChannel(Ptr,user,"PART :%s",Ptr->name);
1527                         }
1528                         user->chans[i].uc_modes = 0;
1529                         user->chans[i].channel = NULL;
1530                         log(DEBUG,"del_channel: unlinked: %s %s",user->nick,Ptr->name);
1531                         break;
1532                 }
1533         }
1534         
1535         /* if there are no users left on the channel */
1536         if (!usercount(Ptr))
1537         {
1538                 chan_hash::iterator iter = chanlist.find(Ptr->name);
1539
1540                 log(DEBUG,"del_channel: destroying channel: %s",Ptr->name);
1541
1542                 /* kill the record */
1543                 if (iter != chanlist.end())
1544                 {
1545                         log(DEBUG,"del_channel: destroyed: %s",Ptr->name);
1546                         delete iter->second;
1547                         chanlist.erase(iter);
1548                 }
1549         }
1550 }
1551
1552
1553 void kick_channel(userrec *src,userrec *user, chanrec *Ptr, char* reason)
1554 {
1555         if ((!src) || (!user) || (!Ptr) || (!reason))
1556         {
1557                 log(DEFAULT,"*** BUG *** kick_channel was given an invalid parameter");
1558                 return;
1559         }
1560
1561         int i = 0;
1562         int created = 0;
1563
1564         if ((!Ptr) || (!user) || (!src))
1565         {
1566                 return;
1567         }
1568
1569         log(DEBUG,"kick_channel: removing: %s %s %s",user->nick,Ptr->name,src->nick);
1570
1571         if (!has_channel(user,Ptr))
1572         {
1573                 WriteServ(src->fd,"441 %s %s %s :They are not on that channel",src->nick, user->nick, Ptr->name);
1574                 return;
1575         }
1576         if (((cstatus(src,Ptr) < STATUS_HOP) || (cstatus(src,Ptr) < cstatus(user,Ptr))) && (!is_uline(src->server)))
1577         {
1578                 if (cstatus(src,Ptr) == STATUS_HOP)
1579                 {
1580                         WriteServ(src->fd,"482 %s %s :You must be a channel operator",src->nick, Ptr->name);
1581                 }
1582                 else
1583                 {
1584                         WriteServ(src->fd,"482 %s %s :You must be at least a half-operator to change modes on this channel",src->nick, Ptr->name);
1585                 }
1586                 
1587                 return;
1588         }
1589         
1590         for (int i =0; i != MAXCHANS; i++)
1591         {
1592                 /* zap it from the channel list of the user */
1593                 if (user->chans[i].channel)
1594                 if (!strcasecmp(user->chans[i].channel->name,Ptr->name))
1595                 {
1596                         WriteChannel(Ptr,src,"KICK %s %s :%s",Ptr->name, user->nick, reason);
1597                         user->chans[i].uc_modes = 0;
1598                         user->chans[i].channel = NULL;
1599                         log(DEBUG,"del_channel: unlinked: %s %s",user->nick,Ptr->name);
1600                         break;
1601                 }
1602         }
1603         
1604         /* if there are no users left on the channel */
1605         if (!usercount(Ptr))
1606         {
1607                 chan_hash::iterator iter = chanlist.find(Ptr->name);
1608
1609                 log(DEBUG,"del_channel: destroying channel: %s",Ptr->name);
1610
1611                 /* kill the record */
1612                 if (iter != chanlist.end())
1613                 {
1614                         log(DEBUG,"del_channel: destroyed: %s",Ptr->name);
1615                         delete iter->second;
1616                         chanlist.erase(iter);
1617                 }
1618         }
1619 }
1620
1621
1622
1623
1624 /* This function pokes and hacks at a parameter list like the following:
1625  *
1626  * PART #winbot, #darkgalaxy :m00!
1627  *
1628  * to turn it into a series of individual calls like this:
1629  *
1630  * PART #winbot :m00!
1631  * PART #darkgalaxy :m00!
1632  *
1633  * The seperate calls are sent to a callback function provided by the caller
1634  * (the caller will usually call itself recursively). The callback function
1635  * must be a command handler. Calling this function on a line with no list causes
1636  * no action to be taken. You must provide a starting and ending parameter number
1637  * where the range of the list can be found, useful if you have a terminating
1638  * parameter as above which is actually not part of the list, or parameters
1639  * before the actual list as well. This code is used by many functions which
1640  * can function as "one to list" (see the RFC) */
1641
1642 int loop_call(handlerfunc fn, char **parameters, int pcnt, userrec *u, int start, int end, int joins)
1643 {
1644         char plist[MAXBUF];
1645         char *param;
1646         char *pars[32];
1647         char blog[32][MAXBUF];
1648         char blog2[32][MAXBUF];
1649         int i = 0, j = 0, q = 0, total = 0, t = 0, t2 = 0, total2 = 0;
1650         char keystr[MAXBUF];
1651         char moo[MAXBUF];
1652
1653         for (int i = 0; i <32; i++)
1654                 strcpy(blog[i],"");
1655
1656         for (int i = 0; i <32; i++)
1657                 strcpy(blog2[i],"");
1658
1659         strcpy(moo,"");
1660         for (int i = 0; i <10; i++)
1661         {
1662                 if (!parameters[i])
1663                 {
1664                         parameters[i] = moo;
1665                 }
1666         }
1667         if (joins)
1668         {
1669                 if (pcnt > 1) /* we have a key to copy */
1670                 {
1671                         strcpy(keystr,parameters[1]);
1672                 }
1673         }
1674
1675         if (!parameters[start])
1676         {
1677                 return 0;
1678         }
1679         if (!strchr(parameters[start],','))
1680         {
1681                 return 0;
1682         }
1683         strcpy(plist,"");
1684         for (int i = start; i <= end; i++)
1685         {
1686                 if (parameters[i])
1687                 {
1688                         strcat(plist,parameters[i]);
1689                 }
1690         }
1691         
1692         j = 0;
1693         param = plist;
1694
1695         t = strlen(plist);
1696         for (int i = 0; i < t; i++)
1697         {
1698                 if (plist[i] == ',')
1699                 {
1700                         plist[i] = '\0';
1701                         strcpy(blog[j++],param);
1702                         param = plist+i+1;
1703                         if (j>20)
1704                         {
1705                                 WriteServ(u->fd,"407 %s %s :Too many targets in list, message not delivered.",u->nick,blog[j-1]);
1706                                 return 1;
1707                         }
1708                 }
1709         }
1710         strcpy(blog[j++],param);
1711         total = j;
1712
1713         if ((joins) && (keystr) && (total>0)) // more than one channel and is joining
1714         {
1715                 strcat(keystr,",");
1716         }
1717         
1718         if ((joins) && (keystr))
1719         {
1720                 if (strchr(keystr,','))
1721                 {
1722                         j = 0;
1723                         param = keystr;
1724                         t2 = strlen(keystr);
1725                         for (int i = 0; i < t2; i++)
1726                         {
1727                                 if (keystr[i] == ',')
1728                                 {
1729                                         keystr[i] = '\0';
1730                                         strcpy(blog2[j++],param);
1731                                         param = keystr+i+1;
1732                                 }
1733                         }
1734                         strcpy(blog2[j++],param);
1735                         total2 = j;
1736                 }
1737         }
1738
1739         for (j = 0; j < total; j++)
1740         {
1741                 if (blog[j])
1742                 {
1743                         pars[0] = blog[j];
1744                 }
1745                 for (q = end; q < pcnt-1; q++)
1746                 {
1747                         if (parameters[q+1])
1748                         {
1749                                 pars[q-end+1] = parameters[q+1];
1750                         }
1751                 }
1752                 if ((joins) && (parameters[1]))
1753                 {
1754                         if (pcnt > 1)
1755                         {
1756                                 pars[1] = blog2[j];
1757                         }
1758                         else
1759                         {
1760                                 pars[1] = NULL;
1761                         }
1762                 }
1763                 /* repeatedly call the function with the hacked parameter list */
1764                 if ((joins) && (pcnt > 1))
1765                 {
1766                         if (pars[1])
1767                         {
1768                                 // pars[1] already set up and containing key from blog2[j]
1769                                 fn(pars,2,u);
1770                         }
1771                         else
1772                         {
1773                                 pars[1] = parameters[1];
1774                                 fn(pars,2,u);
1775                         }
1776                 }
1777                 else
1778                 {
1779                         fn(pars,pcnt-(end-start),u);
1780                 }
1781         }
1782
1783         return 1;
1784 }
1785
1786
1787
1788 void kill_link(userrec *user,const char* r)
1789 {
1790         user_hash::iterator iter = clientlist.find(user->nick);
1791         
1792         char reason[MAXBUF];
1793         
1794         strncpy(reason,r,MAXBUF);
1795
1796         if (strlen(reason)>MAXQUIT)
1797         {
1798                 reason[MAXQUIT-1] = '\0';
1799         }
1800
1801         log(DEBUG,"kill_link: %s '%s'",user->nick,reason);
1802         Write(user->fd,"ERROR :Closing link (%s@%s) [%s]",user->ident,user->host,reason);
1803         log(DEBUG,"closing fd %d",user->fd);
1804
1805         /* bugfix, cant close() a nonblocking socket (sux!) */
1806         if (user->registered == 7) {
1807                 FOREACH_MOD OnUserQuit(user);
1808                 WriteCommonExcept(user,"QUIT :%s",reason);
1809
1810                 // Q token must go to ALL servers!!!
1811                 char buffer[MAXBUF];
1812                 snprintf(buffer,MAXBUF,"Q %s :%s",user->nick,reason);
1813                 NetSendToAll(buffer);
1814         }
1815
1816         /* push the socket on a stack of sockets due to be closed at the next opportunity
1817          * 'Client exited' is an exception to this as it means the client side has already
1818          * closed the socket, we don't need to do it.
1819          */
1820         fd_reap.push_back(user->fd);
1821         
1822         bool do_purge = false;
1823         
1824         if (user->registered == 7) {
1825                 WriteOpers("*** Client exiting: %s!%s@%s [%s]",user->nick,user->ident,user->host,reason);
1826                 AddWhoWas(user);
1827         }
1828
1829         if (iter != clientlist.end())
1830         {
1831                 log(DEBUG,"deleting user hash value %d",iter->second);
1832                 if ((iter->second) && (user->registered == 7)) {
1833                         delete iter->second;
1834                 }
1835                 clientlist.erase(iter);
1836         }
1837
1838         if (user->registered == 7) {
1839                 purge_empty_chans();
1840         }
1841 }
1842
1843
1844
1845 // looks up a users password for their connection class (<ALLOW>/<DENY> tags)
1846
1847 char* Passwd(userrec *user)
1848 {
1849         for (ClassVector::iterator i = Classes.begin(); i != Classes.end(); i++)
1850         {
1851                 if (match(user->host,i->host) && (i->type == CC_ALLOW))
1852                 {
1853                         return i->pass;
1854                 }
1855         }
1856         return "";
1857 }
1858
1859 bool IsDenied(userrec *user)
1860 {
1861         for (ClassVector::iterator i = Classes.begin(); i != Classes.end(); i++)
1862         {
1863                 if (match(user->host,i->host) && (i->type == CC_DENY))
1864                 {
1865                         return true;
1866                 }
1867         }
1868         return false;
1869 }
1870
1871
1872
1873
1874 /* sends out an error notice to all connected clients (not to be used
1875  * lightly!) */
1876
1877 void send_error(char *s)
1878 {
1879         log(DEBUG,"send_error: %s",s);
1880         for (user_hash::const_iterator i = clientlist.begin(); i != clientlist.end(); i++)
1881         {
1882                 if (isnick(i->second->nick))
1883                 {
1884                         WriteServ(i->second->fd,"NOTICE %s :%s",i->second->nick,s);
1885                 }
1886                 else
1887                 {
1888                         // fix - unregistered connections receive ERROR, not NOTICE
1889                         Write(i->second->fd,"ERROR :%s",s);
1890                 }
1891         }
1892 }
1893
1894 void Error(int status)
1895 {
1896         signal (SIGALRM, SIG_IGN);
1897         signal (SIGPIPE, SIG_IGN);
1898         signal (SIGTERM, SIG_IGN);
1899         signal (SIGABRT, SIG_IGN);
1900         signal (SIGSEGV, SIG_IGN);
1901         signal (SIGURG, SIG_IGN);
1902         signal (SIGKILL, SIG_IGN);
1903         log(DEBUG,"*** fell down a pothole in the road to perfection ***");
1904         send_error("Error! Segmentation fault! save meeeeeeeeeeeeee *splat!*");
1905         exit(status);
1906 }
1907
1908
1909 int main(int argc, char *argv[])
1910 {
1911         Start();
1912         srand(time(NULL));
1913         log(DEBUG,"*** InspIRCd starting up!");
1914         if (!FileExists(CONFIG_FILE))
1915         {
1916                 printf("ERROR: Cannot open config file: %s\nExiting...\n",CONFIG_FILE);
1917                 log(DEBUG,"main: no config");
1918                 printf("ERROR: Your config file is missing, this IRCd will self destruct in 10 seconds!\n");
1919                 Exit(ERROR);
1920         }
1921         if (argc > 1) {
1922                 if (!strcmp(argv[1],"-nofork")) {
1923                         nofork = true;
1924                 }
1925         }
1926         if (InspIRCd() == ERROR)
1927         {
1928                 log(DEBUG,"main: daemon function bailed");
1929                 printf("ERROR: could not initialise. Shutting down.\n");
1930                 Exit(ERROR);
1931         }
1932         Exit(TRUE);
1933         return 0;
1934 }
1935
1936 template<typename T> inline string ConvToStr(const T &in)
1937 {
1938         stringstream tmp;
1939         if (!(tmp << in)) return string();
1940         return tmp.str();
1941 }
1942
1943 /* re-allocates a nick in the user_hash after they change nicknames,
1944  * returns a pointer to the new user as it may have moved */
1945
1946 userrec* ReHashNick(char* Old, char* New)
1947 {
1948         user_hash::iterator newnick;
1949         user_hash::iterator oldnick = clientlist.find(Old);
1950
1951         log(DEBUG,"ReHashNick: %s %s",Old,New);
1952         
1953         if (!strcasecmp(Old,New))
1954         {
1955                 log(DEBUG,"old nick is new nick, skipping");
1956                 return oldnick->second;
1957         }
1958         
1959         if (oldnick == clientlist.end()) return NULL; /* doesnt exist */
1960
1961         log(DEBUG,"ReHashNick: Found hashed nick %s",Old);
1962
1963         clientlist[New] = new userrec();
1964         clientlist[New] = oldnick->second;
1965         /*delete oldnick->second; */
1966         clientlist.erase(oldnick);
1967
1968         log(DEBUG,"ReHashNick: Nick rehashed as %s",New);
1969         
1970         return clientlist[New];
1971 }
1972
1973 /* adds or updates an entry in the whowas list */
1974 void AddWhoWas(userrec* u)
1975 {
1976         user_hash::iterator iter = whowas.find(u->nick);
1977         userrec *a = new userrec();
1978         strcpy(a->nick,u->nick);
1979         strcpy(a->ident,u->ident);
1980         strcpy(a->dhost,u->dhost);
1981         strcpy(a->host,u->host);
1982         strcpy(a->fullname,u->fullname);
1983         strcpy(a->server,u->server);
1984         a->signon = u->signon;
1985
1986         /* MAX_WHOWAS:   max number of /WHOWAS items
1987          * WHOWAS_STALE: number of hours before a WHOWAS item is marked as stale and
1988          *               can be replaced by a newer one
1989          */
1990         
1991         if (iter == whowas.end())
1992         {
1993                 if (whowas.size() == WHOWAS_MAX)
1994                 {
1995                         for (user_hash::iterator i = whowas.begin(); i != whowas.end(); i++)
1996                         {
1997                                 // 3600 seconds in an hour ;)
1998                                 if ((i->second->signon)<(time(NULL)-(WHOWAS_STALE*3600)))
1999                                 {
2000                                         delete i->second;
2001                                         i->second = a;
2002                                         log(DEBUG,"added WHOWAS entry, purged an old record");
2003                                         return;
2004                                 }
2005                         }
2006                 }
2007                 else
2008                 {
2009                         log(DEBUG,"added fresh WHOWAS entry");
2010                         whowas[a->nick] = a;
2011                 }
2012         }
2013         else
2014         {
2015                 log(DEBUG,"updated WHOWAS entry");
2016                 delete iter->second;
2017                 iter->second = a;
2018         }
2019 }
2020
2021
2022 /* add a client connection to the sockets list */
2023 void AddClient(int socket, char* host, int port, bool iscached, char* ip)
2024 {
2025         int i;
2026         int blocking = 1;
2027         char resolved[MAXBUF];
2028         string tempnick;
2029         char tn2[MAXBUF];
2030         user_hash::iterator iter;
2031
2032         tempnick = ConvToStr(socket) + "-unknown";
2033         sprintf(tn2,"%d-unknown",socket);
2034
2035         iter = clientlist.find(tempnick);
2036
2037         if (iter != clientlist.end()) return;
2038
2039         /*
2040          * It is OK to access the value here this way since we know
2041          * it exists, we just created it above.
2042          *
2043          * At NO other time should you access a value in a map or a
2044          * hash_map this way.
2045          */
2046         clientlist[tempnick] = new userrec();
2047
2048         NonBlocking(socket);
2049         log(DEBUG,"AddClient: %d %s %d",socket,host,port);
2050
2051         clientlist[tempnick]->fd = socket;
2052         strncpy(clientlist[tempnick]->nick, tn2,NICKMAX);
2053         strncpy(clientlist[tempnick]->host, host,160);
2054         strncpy(clientlist[tempnick]->dhost, host,160);
2055         strncpy(clientlist[tempnick]->server, ServerName,256);
2056         strncpy(clientlist[tempnick]->ident, "unknown",9);
2057         clientlist[tempnick]->registered = 0;
2058         clientlist[tempnick]->signon = time(NULL);
2059         clientlist[tempnick]->nping = time(NULL)+240;
2060         clientlist[tempnick]->lastping = 1;
2061         clientlist[tempnick]->port = port;
2062         strncpy(clientlist[tempnick]->ip,ip,32);
2063
2064         if (iscached)
2065         {
2066                 WriteServ(socket,"NOTICE Auth :Found your hostname (cached)...");
2067         }
2068         else
2069         {
2070                 WriteServ(socket,"NOTICE Auth :Looking up your hostname...");
2071         }
2072
2073         // set the registration timeout for this user
2074         unsigned long class_regtimeout = 90;
2075         for (ClassVector::iterator i = Classes.begin(); i != Classes.end(); i++)
2076         {
2077                 if (match(clientlist[tempnick]->host,i->host) && (i->type == CC_ALLOW))
2078                 {
2079                         class_regtimeout = (unsigned long)i->registration_timeout;
2080                         break;
2081                 }
2082         }
2083
2084         int class_flood = 0;
2085         for (ClassVector::iterator i = Classes.begin(); i != Classes.end(); i++)
2086         {
2087                 if (match(clientlist[tempnick]->host,i->host) && (i->type == CC_ALLOW))
2088                 {
2089                         class_flood = i->flood;
2090                         break;
2091                 }
2092         }
2093
2094         clientlist[tempnick]->timeout = time(NULL)+class_regtimeout;
2095         clientlist[tempnick]->flood = class_flood;
2096
2097         for (int i = 0; i < MAXCHANS; i++)
2098         {
2099                 clientlist[tempnick]->chans[i].channel = NULL;
2100                 clientlist[tempnick]->chans[i].uc_modes = 0;
2101         }
2102
2103         if (clientlist.size() == MAXCLIENTS)
2104                 kill_link(clientlist[tempnick],"No more connections allowed in this class");
2105 }
2106
2107
2108 int usercnt(void)
2109 {
2110         return clientlist.size();
2111 }
2112
2113
2114 int usercount_invisible(void)
2115 {
2116         int c = 0;
2117
2118         for (user_hash::const_iterator i = clientlist.begin(); i != clientlist.end(); i++)
2119         {
2120                 if ((i->second->fd) && (isnick(i->second->nick)) && (strchr(i->second->modes,'i'))) c++;
2121         }
2122         return c;
2123 }
2124
2125 int usercount_opers(void)
2126 {
2127         int c = 0;
2128
2129         for (user_hash::const_iterator i = clientlist.begin(); i != clientlist.end(); i++)
2130         {
2131                 if ((i->second->fd) && (isnick(i->second->nick)) && (strchr(i->second->modes,'o'))) c++;
2132         }
2133         return c;
2134 }
2135
2136 int usercount_unknown(void)
2137 {
2138         int c = 0;
2139
2140         for (user_hash::const_iterator i = clientlist.begin(); i != clientlist.end(); i++)
2141         {
2142                 if ((i->second->fd) && (i->second->registered != 7))
2143                         c++;
2144         }
2145         return c;
2146 }
2147
2148 long chancount(void)
2149 {
2150         return chanlist.size();
2151 }
2152
2153 long count_servs(void)
2154 {
2155         int c = 0;
2156         //for (int j = 0; j < 255; j++)
2157         //{
2158         //      if (servers[j] != NULL)
2159         //              c++;
2160         //}
2161         return c;
2162 }
2163
2164 long servercount(void)
2165 {
2166         return count_servs()+1;
2167 }
2168
2169 long local_count()
2170 {
2171         int c = 0;
2172         for (user_hash::const_iterator i = clientlist.begin(); i != clientlist.end(); i++)
2173         {
2174                 if ((i->second->fd) && (isnick(i->second->nick)) && (!strcasecmp(i->second->server,ServerName))) c++;
2175         }
2176         return c;
2177 }
2178
2179
2180 void ShowMOTD(userrec *user)
2181 {
2182         if (!MOTD.size())
2183         {
2184                 WriteServ(user->fd,"422 %s :Message of the day file is missing.",user->nick);
2185                 return;
2186         }
2187         WriteServ(user->fd,"375 %s :- %s message of the day",user->nick,ServerName);
2188         for (int i = 0; i != MOTD.size(); i++)
2189         {
2190                                 WriteServ(user->fd,"372 %s :- %s",user->nick,MOTD[i].c_str());
2191         }
2192         WriteServ(user->fd,"376 %s :End of %s message of the day.",user->nick,ServerName);
2193 }
2194
2195 void ShowRULES(userrec *user)
2196 {
2197         if (!RULES.size())
2198         {
2199                 WriteServ(user->fd,"NOTICE %s :Rules file is missing.",user->nick);
2200                 return;
2201         }
2202         WriteServ(user->fd,"NOTICE %s :%s rules",user->nick,ServerName);
2203         for (int i = 0; i != RULES.size(); i++)
2204         {
2205                                 WriteServ(user->fd,"NOTICE %s :%s",user->nick,RULES[i].c_str());
2206         }
2207         WriteServ(user->fd,"NOTICE %s :End of %s rules.",user->nick,ServerName);
2208 }
2209
2210 /* shows the message of the day, and any other on-logon stuff */
2211 void ConnectUser(userrec *user)
2212 {
2213         user->registered = 7;
2214         user->idle_lastmsg = time(NULL);
2215         log(DEBUG,"ConnectUser: %s",user->nick);
2216
2217         if (strcmp(Passwd(user),"") && (!user->haspassed))
2218         {
2219                 kill_link(user,"Invalid password");
2220                 return;
2221         }
2222         if (IsDenied(user))
2223         {
2224                 kill_link(user,"Unauthorised connection");
2225                 return;
2226         }
2227
2228         WriteServ(user->fd,"NOTICE Auth :Welcome to \002%s\002!",Network);
2229         WriteServ(user->fd,"001 %s :Welcome to the %s IRC Network %s!%s@%s",user->nick,Network,user->nick,user->ident,user->host);
2230         WriteServ(user->fd,"002 %s :Your host is %s, running version %s",user->nick,ServerName,VERSION);
2231         WriteServ(user->fd,"003 %s :This server was created %s %s",user->nick,__TIME__,__DATE__);
2232         WriteServ(user->fd,"004 %s %s %s iowghraAsORVSxNCWqBzvdHtGI lvhopsmntikrRcaqOALQbSeKVfHGCuzN",user->nick,ServerName,VERSION);
2233         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);
2234         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);
2235         ShowMOTD(user);
2236         FOREACH_MOD OnUserConnect(user);
2237         WriteOpers("*** Client connecting on port %d: %s!%s@%s",user->port,user->nick,user->ident,user->host);
2238         
2239         char buffer[MAXBUF];
2240         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);
2241         NetSendToAll(buffer);
2242 }
2243
2244 void handle_version(char **parameters, int pcnt, userrec *user)
2245 {
2246         char Revision[] = "$Revision$";
2247
2248         char *s1 = Revision;
2249         char *savept;
2250         char *v1 = strtok_r(s1," ",&savept);
2251         s1 = savept;
2252         char *v2 = strtok_r(s1," ",&savept);
2253         s1 = savept;
2254         
2255         WriteServ(user->fd,"351 %s :%s Rev. %s %s :%s (O=%d)",user->nick,VERSION,v2,ServerName,SYSTEM,OPTIMISATION);
2256 }
2257
2258
2259 // calls a handler function for a command
2260
2261 void call_handler(const char* commandname,char **parameters, int pcnt, userrec *user)
2262 {
2263                 for (int i = 0; i < cmdlist.size(); i++)
2264                 {
2265                         if (!strcasecmp(cmdlist[i].command,commandname))
2266                         {
2267                                 if (cmdlist[i].handler_function)
2268                                 {
2269                                         if (pcnt>=cmdlist[i].min_params)
2270                                         {
2271                                                 if (strchr(user->modes,cmdlist[i].flags_needed))
2272                                                 {
2273                                                         cmdlist[i].handler_function(parameters,pcnt,user);
2274                                                 }
2275                                         }
2276                                 }
2277                         }
2278                 }
2279 }
2280
2281 void DoSplitEveryone()
2282 {
2283         bool go_again = true;
2284         while (go_again)
2285         {
2286                 go_again = false;
2287                 for (int i = 0; i < 32; i++)
2288                 {
2289                         if (me[i] != NULL)
2290                         {
2291                                 for (vector<ircd_connector>::iterator j = me[i]->connectors.begin(); j != me[i]->connectors.end(); j++)
2292                                 {
2293                                         if (strcasecmp(j->GetServerName().c_str(),ServerName))
2294                                         {
2295                                                 j->routes.clear();
2296                                                 j->CloseConnection();
2297                                                 me[i]->connectors.erase(j);
2298                                                 go_again = true;
2299                                                 break;
2300                                         }
2301                                 }
2302                         }
2303                 }
2304         }
2305         log(DEBUG,"Removed server. Will remove clients...");
2306         // iterate through the userlist and remove all users on this server.
2307         // because we're dealing with a mesh, we dont have to deal with anything
2308         // "down-route" from this server (nice huh)
2309         go_again = true;
2310         char reason[MAXBUF];
2311         while (go_again)
2312         {
2313                 go_again = false;
2314                 for (user_hash::const_iterator u = clientlist.begin(); u != clientlist.end(); u++)
2315                 {
2316                         if (strcasecmp(u->second->server,ServerName))
2317                         {
2318                                 snprintf(reason,MAXBUF,"%s %s",ServerName,u->second->server);
2319                                 kill_link(u->second,reason);
2320                                 go_again = true;
2321                                 break;
2322                         }
2323                 }
2324         }
2325 }
2326
2327
2328
2329 char islast(const char* s)
2330 {
2331         char c = '`';
2332         for (int j = 0; j < 32; j++)
2333         {
2334                 if (me[j] != NULL)
2335                 {
2336                         for (int k = 0; k < me[j]->connectors.size(); k++)
2337                         {
2338                                 if (strcasecmp(me[j]->connectors[k].GetServerName().c_str(),s))
2339                                 {
2340                                         c = '|';
2341                                 }
2342                                 if (!strcasecmp(me[j]->connectors[k].GetServerName().c_str(),s))
2343                                 {
2344                                         c = '`';
2345                                 }
2346                         }
2347                 }
2348         }
2349         return c;
2350 }
2351
2352 long map_count(const char* s)
2353 {
2354         int c = 0;
2355         for (user_hash::const_iterator i = clientlist.begin(); i != clientlist.end(); i++)
2356         {
2357                 if ((i->second->fd) && (isnick(i->second->nick)) && (!strcasecmp(i->second->server,s))) c++;
2358         }
2359         return c;
2360 }
2361
2362
2363 void force_nickchange(userrec* user,const char* newnick)
2364 {
2365         char nick[MAXBUF];
2366         int MOD_RESULT = 0;
2367         
2368         strcpy(nick,"");
2369
2370         FOREACH_RESULT(OnUserPreNick(user,newnick));
2371         if (MOD_RESULT) {
2372                 kill_link(user,"Nickname collision");
2373                 return;
2374         }
2375         if (matches_qline(newnick))
2376         {
2377                 kill_link(user,"Nickname collision");
2378                 return;
2379         }
2380         
2381         if (user)
2382         {
2383                 if (newnick)
2384                 {
2385                         strncpy(nick,newnick,MAXBUF);
2386                 }
2387                 if (user->registered == 7)
2388                 {
2389                         char* pars[1];
2390                         pars[0] = nick;
2391                         handle_nick(pars,1,user);
2392                 }
2393         }
2394 }
2395                                 
2396
2397 int process_parameters(char **command_p,char *parameters)
2398 {
2399         int i = 0;
2400         int j = 0;
2401         int q = 0;
2402         q = strlen(parameters);
2403         if (!q)
2404         {
2405                 /* no parameters, command_p invalid! */
2406                 return 0;
2407         }
2408         if (parameters[0] == ':')
2409         {
2410                 command_p[0] = parameters+1;
2411                 return 1;
2412         }
2413         if (q)
2414         {
2415                 if ((strchr(parameters,' ')==NULL) || (parameters[0] == ':'))
2416                 {
2417                         /* only one parameter */
2418                         command_p[0] = parameters;
2419                         if (parameters[0] == ':')
2420                         {
2421                                 if (strchr(parameters,' ') != NULL)
2422                                 {
2423                                         command_p[0]++;
2424                                 }
2425                         }
2426                         return 1;
2427                 }
2428         }
2429         command_p[j++] = parameters;
2430         for (int i = 0; i <= q; i++)
2431         {
2432                 if (parameters[i] == ' ')
2433                 {
2434                         command_p[j++] = parameters+i+1;
2435                         parameters[i] = '\0';
2436                         if (command_p[j-1][0] == ':')
2437                         {
2438                                 *command_p[j-1]++; /* remove dodgy ":" */
2439                                 break;
2440                                 /* parameter like this marks end of the sequence */
2441                         }
2442                 }
2443         }
2444         return j; /* returns total number of items in the list */
2445 }
2446
2447 void process_command(userrec *user, char* cmd)
2448 {
2449         char *parameters;
2450         char *command;
2451         char *command_p[127];
2452         char p[MAXBUF], temp[MAXBUF];
2453         int i, j, items, cmd_found;
2454
2455         for (int i = 0; i < 127; i++)
2456                 command_p[i] = NULL;
2457
2458         if (!user)
2459         {
2460                 return;
2461         }
2462         if (!cmd)
2463         {
2464                 return;
2465         }
2466         if (!strcmp(cmd,""))
2467         {
2468                 return;
2469         }
2470         
2471         int total_params = 0;
2472         if (strlen(cmd)>2)
2473         {
2474                 for (int q = 0; q < strlen(cmd)-1; q++)
2475                 {
2476                         if ((cmd[q] == ' ') && (cmd[q+1] == ':'))
2477                         {
2478                                 total_params++;
2479                                 // found a 'trailing', we dont count them after this.
2480                                 break;
2481                         }
2482                         if (cmd[q] == ' ')
2483                                 total_params++;
2484                 }
2485         }
2486         
2487         // another phidjit bug...
2488         if (total_params > 126)
2489         {
2490                 kill_link(user,"Protocol violation (1)");
2491                 return;
2492         }
2493         
2494         strcpy(temp,cmd);
2495
2496         std::string tmp = cmd;
2497         for (int i = 0; i <= MODCOUNT; i++)
2498         {
2499                 std::string oldtmp = tmp;
2500                 modules[i]->OnServerRaw(tmp,true);
2501                 if (oldtmp != tmp)
2502                 {
2503                         log(DEBUG,"A Module changed the input string!");
2504                         log(DEBUG,"New string: %s",tmp.c_str());
2505                         log(DEBUG,"Old string: %s",oldtmp.c_str());
2506                         break;
2507                 }
2508         }
2509         strncpy(cmd,tmp.c_str(),MAXBUF);
2510         strcpy(temp,cmd);
2511
2512         if (!strchr(cmd,' '))
2513         {
2514                 /* no parameters, lets skip the formalities and not chop up
2515                  * the string */
2516                 log(DEBUG,"About to preprocess command with no params");
2517                 items = 0;
2518                 command_p[0] = NULL;
2519                 parameters = NULL;
2520                 for (int i = 0; i <= strlen(cmd); i++)
2521                 {
2522                         cmd[i] = toupper(cmd[i]);
2523                 }
2524                 log(DEBUG,"Preprocess done length=%d",strlen(cmd));
2525                 command = cmd;
2526         }
2527         else
2528         {
2529                 strcpy(cmd,"");
2530                 j = 0;
2531                 /* strip out extraneous linefeeds through mirc's crappy pasting (thanks Craig) */
2532                 for (int i = 0; i < strlen(temp); i++)
2533                 {
2534                         if ((temp[i] != 10) && (temp[i] != 13) && (temp[i] != 0) && (temp[i] != 7))
2535                         {
2536                                 cmd[j++] = temp[i];
2537                                 cmd[j] = 0;
2538                         }
2539                 }
2540                 /* split the full string into a command plus parameters */
2541                 parameters = p;
2542                 strcpy(p," ");
2543                 command = cmd;
2544                 if (strchr(cmd,' '))
2545                 {
2546                         for (int i = 0; i <= strlen(cmd); i++)
2547                         {
2548                                 /* capitalise the command ONLY, leave params intact */
2549                                 cmd[i] = toupper(cmd[i]);
2550                                 /* are we nearly there yet?! :P */
2551                                 if (cmd[i] == ' ')
2552                                 {
2553                                         command = cmd;
2554                                         parameters = cmd+i+1;
2555                                         cmd[i] = '\0';
2556                                         break;
2557                                 }
2558                         }
2559                 }
2560                 else
2561                 {
2562                         for (int i = 0; i <= strlen(cmd); i++)
2563                         {
2564                                 cmd[i] = toupper(cmd[i]);
2565                         }
2566                 }
2567
2568         }
2569         cmd_found = 0;
2570         
2571         if (strlen(command)>MAXCOMMAND)
2572         {
2573                 kill_link(user,"Protocol violation (2)");
2574                 return;
2575         }
2576         
2577         for (int x = 0; x < strlen(command); x++)
2578         {
2579                 if (((command[x] < 'A') || (command[x] > 'Z')) && (command[x] != '.'))
2580                 {
2581                         if (((command[x] < '0') || (command[x]> '9')) && (command[x] != '-'))
2582                         {
2583                                 if (!strchr("@!\"$%^&*(){}[]_-=+;:'#~,.<>/?\\|`",command[x]))
2584                                 {
2585                                         kill_link(user,"Protocol violation (3)");
2586                                         return;
2587                                 }
2588                         }
2589                 }
2590         }
2591
2592         for (int i = 0; i != cmdlist.size(); i++)
2593         {
2594                 if (strcmp(cmdlist[i].command,""))
2595                 {
2596                         if (strlen(command)>=(strlen(cmdlist[i].command))) if (!strncmp(command, cmdlist[i].command,MAXCOMMAND))
2597                         {
2598                                 log(DEBUG,"Found matching command");
2599
2600                                 if (parameters)
2601                                 {
2602                                         if (strcmp(parameters,""))
2603                                         {
2604                                                 items = process_parameters(command_p,parameters);
2605                                         }
2606                                         else
2607                                         {
2608                                                 items = 0;
2609                                                 command_p[0] = NULL;
2610                                         }
2611                                 }
2612                                 else
2613                                 {
2614                                         items = 0;
2615                                         command_p[0] = NULL;
2616                                 }
2617                                 
2618                                 if (user)
2619                                 {
2620                                         log(DEBUG,"Processing command");
2621                                         
2622                                         /* activity resets the ping pending timer */
2623                                         user->nping = time(NULL) + 120;
2624                                         if ((items) < cmdlist[i].min_params)
2625                                         {
2626                                                 log(DEBUG,"process_command: not enough parameters: %s %s",user->nick,command);
2627                                                 WriteServ(user->fd,"461 %s %s :Not enough parameters",user->nick,command);
2628                                                 return;
2629                                         }
2630                                         if ((!strchr(user->modes,cmdlist[i].flags_needed)) && (cmdlist[i].flags_needed))
2631                                         {
2632                                                 log(DEBUG,"process_command: permission denied: %s %s",user->nick,command);
2633                                                 WriteServ(user->fd,"481 %s :Permission Denied- You do not have the required operator privilages",user->nick);
2634                                                 cmd_found = 1;
2635                                                 return;
2636                                         }
2637                                         /* if the command isnt USER, PASS, or NICK, and nick is empty,
2638                                          * deny command! */
2639                                         if ((strncmp(command,"USER",4)) && (strncmp(command,"NICK",4)) && (strncmp(command,"PASS",4)))
2640                                         {
2641                                                 if ((!isnick(user->nick)) || (user->registered != 7))
2642                                                 {
2643                                                         log(DEBUG,"process_command: not registered: %s %s",user->nick,command);
2644                                                         WriteServ(user->fd,"451 %s :You have not registered",command);
2645                                                         return;
2646                                                 }
2647                                         }
2648                                         if ((user->registered == 7) || (!strcmp(command,"USER")) || (!strcmp(command,"NICK")) || (!strcmp(command,"PASS")))
2649                                         {
2650                                                 log(DEBUG,"process_command: handler: %s %s %d",user->nick,command,items);
2651                                                 if (cmdlist[i].handler_function)
2652                                                 {
2653                                                         /* ikky /stats counters */
2654                                                         if (temp)
2655                                                         {
2656                                                                 if (user)
2657                                                                 {
2658                                                                         user->bytes_in += strlen(temp);
2659                                                                         user->cmds_in++;
2660                                                                 }
2661                                                                 cmdlist[i].use_count++;
2662                                                                 cmdlist[i].total_bytes+=strlen(temp);
2663                                                         }
2664
2665                                                         /* WARNING: nothing may come after the
2666                                                          * command handler call, as the handler
2667                                                          * may free the user structure! */
2668
2669                                                         cmdlist[i].handler_function(command_p,items,user);
2670                                                 }
2671                                                 return;
2672                                         }
2673                                         else
2674                                         {
2675                                                 log(DEBUG,"process_command: not registered: %s %s",user->nick,command);
2676                                                 WriteServ(user->fd,"451 %s :You have not registered",command);
2677                                                 return;
2678                                         }
2679                                 }
2680                                 cmd_found = 1;
2681                         }
2682                 }
2683         }
2684         if ((!cmd_found) && (user))
2685         {
2686                 log(DEBUG,"process_command: not in table: %s %s",user->nick,command);
2687                 WriteServ(user->fd,"421 %s %s :Unknown command",user->nick,command);
2688         }
2689 }
2690
2691
2692 void createcommand(char* cmd, handlerfunc f, char flags, int minparams)
2693 {
2694         command_t comm;
2695         /* create the command and push it onto the table */     
2696         strcpy(comm.command,cmd);
2697         comm.handler_function = f;
2698         comm.flags_needed = flags;
2699         comm.min_params = minparams;
2700         comm.use_count = 0;
2701         comm.total_bytes = 0;
2702         cmdlist.push_back(comm);
2703         log(DEBUG,"Added command %s (%d parameters)",cmd,minparams);
2704 }
2705
2706 void SetupCommandTable(void)
2707 {
2708         createcommand("USER",handle_user,0,4);
2709         createcommand("NICK",handle_nick,0,1);
2710         createcommand("QUIT",handle_quit,0,0);
2711         createcommand("VERSION",handle_version,0,0);
2712         createcommand("PING",handle_ping,0,1);
2713         createcommand("PONG",handle_pong,0,1);
2714         createcommand("ADMIN",handle_admin,0,0);
2715         createcommand("PRIVMSG",handle_privmsg,0,2);
2716         createcommand("INFO",handle_info,0,0);
2717         createcommand("TIME",handle_time,0,0);
2718         createcommand("WHOIS",handle_whois,0,1);
2719         createcommand("WALLOPS",handle_wallops,'o',1);
2720         createcommand("NOTICE",handle_notice,0,2);
2721         createcommand("JOIN",handle_join,0,1);
2722         createcommand("NAMES",handle_names,0,1);
2723         createcommand("PART",handle_part,0,1);
2724         createcommand("KICK",handle_kick,0,2);
2725         createcommand("MODE",handle_mode,0,1);
2726         createcommand("TOPIC",handle_topic,0,1);
2727         createcommand("WHO",handle_who,0,1);
2728         createcommand("MOTD",handle_motd,0,0);
2729         createcommand("RULES",handle_rules,0,0);
2730         createcommand("OPER",handle_oper,0,2);
2731         createcommand("LIST",handle_list,0,0);
2732         createcommand("DIE",handle_die,'o',1);
2733         createcommand("RESTART",handle_restart,'o',1);
2734         createcommand("KILL",handle_kill,'o',2);
2735         createcommand("REHASH",handle_rehash,'o',0);
2736         createcommand("LUSERS",handle_lusers,0,0);
2737         createcommand("STATS",handle_stats,0,1);
2738         createcommand("USERHOST",handle_userhost,0,1);
2739         createcommand("AWAY",handle_away,0,0);
2740         createcommand("ISON",handle_ison,0,0);
2741         createcommand("SUMMON",handle_summon,0,0);
2742         createcommand("USERS",handle_users,0,0);
2743         createcommand("INVITE",handle_invite,0,2);
2744         createcommand("PASS",handle_pass,0,1);
2745         createcommand("TRACE",handle_trace,'o',0);
2746         createcommand("WHOWAS",handle_whowas,0,1);
2747         createcommand("CONNECT",handle_connect,'o',1);
2748         createcommand("SQUIT",handle_squit,'o',0);
2749         createcommand("MODULES",handle_modules,'o',0);
2750         createcommand("LINKS",handle_links,0,0);
2751         createcommand("MAP",handle_map,0,0);
2752         createcommand("KLINE",handle_kline,'o',1);
2753         createcommand("GLINE",handle_gline,'o',1);
2754         createcommand("ZLINE",handle_zline,'o',1);
2755         createcommand("QLINE",handle_qline,'o',1);
2756 }
2757
2758 void process_buffer(const char* cmdbuf,userrec *user)
2759 {
2760         if (!user)
2761         {
2762                 log(DEFAULT,"*** BUG *** process_buffer was given an invalid parameter");
2763                 return;
2764         }
2765         char cmd[MAXBUF];
2766         int i;
2767         if (!cmdbuf)
2768         {
2769                 log(DEFAULT,"*** BUG *** process_buffer was given an invalid parameter");
2770                 return;
2771         }
2772         if (!strcmp(cmdbuf,""))
2773         {
2774                 return;
2775         }
2776         while ((cmdbuf[0] == ' ') && (strlen(cmdbuf)>0)) cmdbuf++; // strip leading spaces
2777
2778         strncpy(cmd,cmdbuf,MAXBUF);
2779         if (!strcmp(cmd,""))
2780         {
2781                 return;
2782         }
2783         if ((cmd[strlen(cmd)-1] == 13) || (cmd[strlen(cmd)-1] == 10))
2784         {
2785                 cmd[strlen(cmd)-1] = '\0';
2786         }
2787         if ((cmd[strlen(cmd)-1] == 13) || (cmd[strlen(cmd)-1] == 10))
2788         {
2789                 cmd[strlen(cmd)-1] = '\0';
2790         }
2791
2792         while ((cmd[strlen(cmd)-1] == ' ') && (strlen(cmd)>0)) // strip trailing spaces
2793         {
2794                 cmd[strlen(cmd)-1] = '\0';
2795         }
2796
2797         if (!strcmp(cmd,""))
2798         {
2799                 return;
2800         }
2801         log(DEBUG,"InspIRCd: processing: %s %s",user->nick,cmd);
2802         tidystring(cmd);
2803         if ((user) && (cmd))
2804         {
2805                 process_command(user,cmd);
2806         }
2807 }
2808
2809 void DoSync(serverrec* serv, char* tcp_host)
2810 {
2811         char data[MAXBUF];
2812         log(DEBUG,"Sending sync");
2813         // send start of sync marker: Y <timestamp>
2814         // at this point the ircd receiving it starts broadcasting this netburst to all ircds
2815         // except the ones its receiving it from.
2816         snprintf(data,MAXBUF,"Y %d",time(NULL));
2817         serv->SendPacket(data,tcp_host);
2818         // send users and channels
2819         for (user_hash::iterator u = clientlist.begin(); u != clientlist.end(); u++)
2820         {
2821                 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);
2822                 serv->SendPacket(data,tcp_host);
2823                 if (strcmp(chlist(u->second),""))
2824                 {
2825                         snprintf(data,MAXBUF,"J %s %s",u->second->nick,chlist(u->second));
2826                         serv->SendPacket(data,tcp_host);
2827                 }
2828         }
2829         // send channel modes, topics etc...
2830         for (chan_hash::iterator c = chanlist.begin(); c != chanlist.end(); c++)
2831         {
2832                 snprintf(data,MAXBUF,"M %s +%s",c->second->name,chanmodes(c->second));
2833                 serv->SendPacket(data,tcp_host);
2834                 if (strcmp(c->second->topic,""))
2835                 {
2836                         snprintf(data,MAXBUF,"T %d %s %s :%s",c->second->topicset,c->second->setby,c->second->name,c->second->topic);
2837                         serv->SendPacket(data,tcp_host);
2838                 }
2839                 // send current banlist
2840                 
2841                 for (BanList::iterator b = c->second->bans.begin(); b != c->second->bans.end(); b++)
2842                 {
2843                         snprintf(data,MAXBUF,"M %s +b %s",b->set_time,c->second->name,b->data);
2844                         serv->SendPacket(data,tcp_host);
2845                 }
2846         }
2847         snprintf(data,MAXBUF,"F %d",time(NULL));
2848         serv->SendPacket(data,tcp_host);
2849         log(DEBUG,"Sent sync");
2850         // ircd sends its serverlist after the end of sync here
2851 }
2852
2853
2854 void NetSendMyRoutingTable()
2855 {
2856         // send out a line saying what is reachable to us.
2857         // E.g. if A is linked to B C and D, send out:
2858         // $ A B C D
2859         // if its only linked to B and D send out:
2860         // $ A B D
2861         // if it has no links, dont even send out the line at all.
2862         char buffer[MAXBUF];
2863         sprintf(buffer,"$ %s",ServerName);
2864         bool sendit = false;
2865         for (int i = 0; i < 32; i++)
2866         {
2867                 if (me[i] != NULL)
2868                 {
2869                         for (int j = 0; j < me[i]->connectors.size(); j++)
2870                         {
2871                                 if (me[i]->connectors[j].GetState() != STATE_DISCONNECTED)
2872                                 {
2873                                         strncat(buffer," ",MAXBUF);
2874                                         strncat(buffer,me[i]->connectors[j].GetServerName().c_str(),MAXBUF);
2875                                         sendit = true;
2876                                 }
2877                         }
2878                 }
2879         }
2880         if (sendit)
2881                 NetSendToAll(buffer);
2882 }
2883
2884
2885 void DoSplit(const char* params)
2886 {
2887         bool go_again = true;
2888         while (go_again)
2889         {
2890                 go_again = false;
2891                 for (int i = 0; i < 32; i++)
2892                 {
2893                         if (me[i] != NULL)
2894                         {
2895                                 for (vector<ircd_connector>::iterator j = me[i]->connectors.begin(); j != me[i]->connectors.end(); j++)
2896                                 {
2897                                         if (!strcasecmp(j->GetServerName().c_str(),params))
2898                                         {
2899                                                 j->routes.clear();
2900                                                 j->CloseConnection();
2901                                                 me[i]->connectors.erase(j);
2902                                                 go_again = true;
2903                                                 break;
2904                                         }
2905                                 }
2906                         }
2907                 }
2908         }
2909         log(DEBUG,"Removed server. Will remove clients...");
2910         // iterate through the userlist and remove all users on this server.
2911         // because we're dealing with a mesh, we dont have to deal with anything
2912         // "down-route" from this server (nice huh)
2913         go_again = true;
2914         char reason[MAXBUF];
2915         snprintf(reason,MAXBUF,"%s %s",ServerName,params);
2916         while (go_again)
2917         {
2918                 go_again = false;
2919                 for (user_hash::const_iterator u = clientlist.begin(); u != clientlist.end(); u++)
2920                 {
2921                         if (!strcasecmp(u->second->server,params))
2922                         {
2923                                 kill_link(u->second,reason);
2924                                 go_again = true;
2925                                 break;
2926                         }
2927                 }
2928         }
2929 }
2930
2931 // removes a server. Will NOT remove its users!
2932
2933 void RemoveServer(const char* name)
2934 {
2935         bool go_again = true;
2936         while (go_again)
2937         {
2938                 go_again = false;
2939                 for (int i = 0; i < 32; i++)
2940                 {
2941                         if (me[i] != NULL)
2942                         {
2943                                 for (vector<ircd_connector>::iterator j = me[i]->connectors.begin(); j != me[i]->connectors.end(); j++)
2944                                 {
2945                                         if (!strcasecmp(j->GetServerName().c_str(),name))
2946                                         {
2947                                                 j->routes.clear();
2948                                                 j->CloseConnection();
2949                                                 me[i]->connectors.erase(j);
2950                                                 go_again = true;
2951                                                 break;
2952                                         }
2953                                 }
2954                         }
2955                 }
2956         }
2957 }
2958
2959
2960 int reap_counter = 0;
2961
2962 int InspIRCd(void)
2963 {
2964         struct sockaddr_in client, server;
2965         char addrs[MAXBUF][255];
2966         int openSockfd[MAXSOCKS], incomingSockfd, result = TRUE;
2967         socklen_t length;
2968         int count = 0, scanDetectTrigger = TRUE, showBanner = FALSE;
2969         int selectResult = 0, selectResult2 = 0;
2970         char *temp, configToken[MAXBUF], stuff[MAXBUF], Addr[MAXBUF], Type[MAXBUF];
2971         char resolvedHost[MAXBUF];
2972         fd_set selectFds;
2973         struct timeval tv;
2974
2975         log_file = fopen("ircd.log","a+");
2976         if (!log_file)
2977         {
2978                 printf("ERROR: Could not write to logfile ircd.log, bailing!\n\n");
2979                 Exit(ERROR);
2980         }
2981
2982         log(DEBUG,"InspIRCd: startup: begin");
2983         log(DEBUG,"$Id$");
2984         if (geteuid() == 0)
2985         {
2986                 printf("WARNING!!! You are running an irc server as ROOT!!! DO NOT DO THIS!!!\n\n");
2987                 Exit(ERROR);
2988                 log(DEBUG,"InspIRCd: startup: not starting with UID 0!");
2989         }
2990         SetupCommandTable();
2991         log(DEBUG,"InspIRCd: startup: default command table set up");
2992         
2993         ReadConfig();
2994         if (strcmp(DieValue,"")) 
2995         { 
2996                 printf("WARNING: %s\n\n",DieValue);
2997                 exit(0); 
2998         }  
2999         log(DEBUG,"InspIRCd: startup: read config");
3000           
3001         int count2 = 0, count3 = 0;
3002
3003         for (count = 0; count < ConfValueEnum("bind",&config_f); count++)
3004         {
3005                 ConfValue("bind","port",count,configToken,&config_f);
3006                 ConfValue("bind","address",count,Addr,&config_f);
3007                 ConfValue("bind","type",count,Type,&config_f);
3008                 if (!strcmp(Type,"servers"))
3009                 {
3010                         char Default[MAXBUF];
3011                         strcpy(Default,"no");
3012                         ConfValue("bind","default",count,Default,&config_f);
3013                         if (strchr(Default,'y'))
3014                         {
3015                                 defaultRoute = count3;
3016                                 log(DEBUG,"InspIRCd: startup: binding '%s:%s' is default server route",Addr,configToken);
3017                         }
3018                         me[count3] = new serverrec(ServerName,100L,false);
3019                         me[count3]->CreateListener(Addr,atoi(configToken));
3020                         count3++;
3021                 }
3022                 else
3023                 {
3024                         ports[count2] = atoi(configToken);
3025                         strcpy(addrs[count2],Addr);
3026                         count2++;
3027                 }
3028                 log(DEBUG,"InspIRCd: startup: read binding %s:%s [%s] from config",Addr,configToken, Type);
3029         }
3030         portCount = count2;
3031         UDPportCount = count3;
3032           
3033         log(DEBUG,"InspIRCd: startup: read %d total client ports and %d total server ports",portCount,UDPportCount);
3034         
3035         log(DEBUG,"InspIRCd: startup: InspIRCd is now running!");
3036         
3037         printf("\n");
3038         
3039         /* BugFix By Craig! :p */
3040         count = 0;
3041         for (count2 = 0; count2 < ConfValueEnum("module",&config_f); count2++)
3042         {
3043                 char modfile[MAXBUF];
3044                 ConfValue("module","name",count2,configToken,&config_f);
3045                 sprintf(modfile,"%s/%s",MOD_PATH,configToken,&config_f);
3046                 printf("Loading module... \033[1;37m%s\033[0;37m\n",modfile);
3047                 log(DEBUG,"InspIRCd: startup: Loading module: %s",modfile);
3048                 /* If The File Doesnt exist, Trying to load it
3049                  * Will Segfault the IRCd.. So, check to see if
3050                  * it Exists, Before Proceeding. */
3051                 if (FileExists(modfile))
3052                 {
3053                         factory[count] = new ircd_module(modfile);
3054                         if (factory[count]->LastError())
3055                         {
3056                                 log(DEBUG,"Unable to load %s: %s",modfile,factory[count]->LastError());
3057                                 sprintf("Unable to load %s: %s\nExiting...\n",modfile,factory[count]->LastError());
3058                                 Exit(ERROR);
3059                         }
3060                         if (factory[count]->factory)
3061                         {
3062                                 modules[count] = factory[count]->factory->CreateModule();
3063                                 /* save the module and the module's classfactory, if
3064                                  * this isnt done, random crashes can occur :/ */
3065                                 module_names.push_back(modfile);        
3066                         }
3067                         else
3068                         {
3069                                 log(DEBUG,"Unable to load %s",modfile);
3070                                 sprintf("Unable to load %s\nExiting...\n",modfile);
3071                                 Exit(ERROR);
3072                         }
3073                         /* Increase the Count */
3074                         count++;
3075                 }
3076                 else
3077                 {
3078                         log(DEBUG,"InspIRCd: startup: Module Not Found %s",modfile);
3079                         printf("Module Not Found: \033[1;37m%s\033[0;37m, Skipping\n",modfile);
3080                 }
3081         }
3082         MODCOUNT = count - 1;
3083         log(DEBUG,"Total loaded modules: %d",MODCOUNT+1);
3084         
3085         printf("\nInspIRCd is now running!\n");
3086         
3087         startup_time = time(NULL);
3088           
3089         if (nofork)
3090         {
3091                 log(VERBOSE,"Not forking as -nofork was specified");
3092         }
3093         else
3094         {
3095                 if (DaemonSeed() == ERROR)
3096                 {
3097                         log(DEBUG,"InspIRCd: startup: can't daemonise");
3098                         printf("ERROR: could not go into daemon mode. Shutting down.\n");
3099                         Exit(ERROR);
3100                 }
3101         }
3102           
3103           
3104         /* setup select call */
3105         FD_ZERO(&selectFds);
3106         log(DEBUG,"InspIRCd: startup: zero selects");
3107         log(VERBOSE,"InspIRCd: startup: portCount = %d", portCount);
3108         
3109         for (count = 0; count < portCount; count++)
3110         {
3111                 if ((openSockfd[boundPortCount] = OpenTCPSocket()) == ERROR)
3112                 {
3113                         log(DEBUG,"InspIRCd: startup: bad fd %d",openSockfd[boundPortCount]);
3114                         return(ERROR);
3115                 }
3116                 if (BindSocket(openSockfd[boundPortCount],client,server,ports[count],addrs[count]) == ERROR)
3117                 {
3118                         log(DEBUG,"InspIRCd: startup: failed to bind port %d",ports[count]);
3119                 }
3120                 else    /* well we at least bound to one socket so we'll continue */
3121                 {
3122                         boundPortCount++;
3123                 }
3124         }
3125         
3126         log(DEBUG,"InspIRCd: startup: total bound ports %d",boundPortCount);
3127           
3128         /* if we didn't bind to anything then abort */
3129         if (boundPortCount == 0)
3130         {
3131                 log(DEBUG,"InspIRCd: startup: no ports bound, bailing!");
3132                 return (ERROR);
3133         }
3134         
3135
3136         length = sizeof (client);
3137         char udp_msg[MAXBUF], tcp_host[MAXBUF];
3138           
3139         /* main loop, this never returns */
3140         for (;;)
3141         {
3142 #ifdef _POSIX_PRIORITY_SCHEDULING
3143                 sched_yield();
3144 #endif
3145                 // update the status of klines, etc
3146                 expire_lines();
3147
3148                 fd_set sfd;
3149                 timeval tval;
3150                 FD_ZERO(&sfd);
3151
3152                 user_hash::iterator count2 = clientlist.begin();
3153
3154                 // *FIX* Instead of closing sockets in kill_link when they receive the ERROR :blah line, we should queue
3155                 // them in a list, then reap the list every second or so.
3156                 if (reap_counter>300)
3157                 {
3158                         if (fd_reap.size() > 0)
3159                         {
3160                                 for( int n = 0; n < fd_reap.size(); n++)
3161                                 {
3162                                         Blocking(fd_reap[n]);
3163                                         close(fd_reap[n]);
3164                                         NonBlocking(fd_reap[n]);
3165                                 }
3166                         }
3167                         fd_reap.clear();
3168                         reap_counter=0;
3169                 }
3170                 reap_counter++;
3171
3172                 fd_set serverfds;
3173                 FD_ZERO(&serverfds);
3174                 timeval tvs;
3175                 
3176                 for (int x = 0; x != UDPportCount; x++)
3177                 {
3178                         FD_SET(me[x]->fd, &serverfds);
3179                 }
3180                 
3181                 tvs.tv_usec = 0;                
3182                 tvs.tv_sec = 0;
3183                 
3184                 int servresult = select(32767, &serverfds, NULL, NULL, &tvs);
3185                 if (servresult > 0)
3186                 {
3187                         for (int x = 0; x != UDPportCount; x++)
3188                         {
3189                                 if (FD_ISSET (me[x]->fd, &serverfds))
3190                                 {
3191                                         char remotehost[MAXBUF],resolved[MAXBUF];
3192                                         length = sizeof (client);
3193                                         incomingSockfd = accept (me[x]->fd, (sockaddr *) &client, &length);
3194                                         strncpy(remotehost,(char *)inet_ntoa(client.sin_addr),MAXBUF);
3195                                         if(CleanAndResolve(resolved, remotehost) != TRUE)
3196                                         {
3197                                                 strncpy(resolved,remotehost,MAXBUF);
3198                                         }
3199                                         // add to this connections ircd_connector vector
3200                                         // *FIX* - we need the LOCAL port not the remote port in &client!
3201                                         me[x]->AddIncoming(incomingSockfd,resolved,me[x]->port);
3202                                 }
3203                         }
3204                 }
3205      
3206                 for (int x = 0; x < UDPportCount; x++)
3207                 {
3208                         std::deque<std::string> msgs;
3209                         msgs.clear();
3210                         if (me[x]->RecvPacket(msgs, tcp_host))
3211                         {
3212                                 for (int ctr = 0; ctr < msgs.size(); ctr++)
3213                                 {
3214                                         char udp_msg[MAXBUF];
3215                                         strncpy(udp_msg,msgs[ctr].c_str(),MAXBUF);
3216                                         if (strlen(udp_msg)<1)
3217                                         {
3218                                                 log(DEBUG,"Invalid string from %s [route%d]",tcp_host,x);
3219                                                 break;
3220                                         }
3221                                         // during a netburst, send all data to all other linked servers
3222                                         if ((((nb_start>0) && (udp_msg[0] != 'Y') && (udp_msg[0] != 'X') && (udp_msg[0] != 'F'))) || (is_uline(tcp_host)))
3223                                         {
3224                                                 if (is_uline(tcp_host))
3225                                                 {
3226                                                         if ((udp_msg[0] != 'Y') && (udp_msg[0] != 'X') && (udp_msg[0] != 'F'))
3227                                                         {
3228                                                                 NetSendToAllExcept(tcp_host,udp_msg);
3229                                                         }
3230                                                 }
3231                                                 else
3232                                                         NetSendToAllExcept(tcp_host,udp_msg);
3233                                         }
3234                                         FOREACH_MOD OnPacketReceive(udp_msg);
3235                                         handle_link_packet(udp_msg, tcp_host, me[x]);
3236                                 }
3237                                 goto label;
3238                         }
3239                 }
3240         
3241
3242         while (count2 != clientlist.end())
3243         {
3244                 char data[10240];
3245                 tval.tv_usec = tval.tv_sec = 0;
3246                 FD_ZERO(&sfd);
3247                 int total_in_this_set = 0;
3248
3249                 user_hash::iterator xcount = count2;
3250                 user_hash::iterator endingiter = count2;
3251
3252                 if (!count2->second) break;
3253                 
3254                 if (count2->second)
3255                 if (count2->second->fd != 0)
3256                 {
3257                         // assemble up to 64 sockets into an fd_set
3258                         // to implement a pooling mechanism.
3259                         //
3260                         // This should be up to 64x faster than the
3261                         // old implementation.
3262                         while (total_in_this_set < 64)
3263                         {
3264                                 if (count2 != clientlist.end())
3265                                 {
3266                                         // we don't check the state of remote users.
3267                                         if (count2->second->fd > 0)
3268                                         {
3269                                                 FD_SET (count2->second->fd, &sfd);
3270
3271                                                 // registration timeout -- didnt send USER/NICK/HOST in the time specified in
3272                                                 // their connection class.
3273                                                 if ((time(NULL) > count2->second->timeout) && (count2->second->registered != 7)) 
3274                                                 {
3275                                                         log(DEBUG,"InspIRCd: registration timeout: %s",count2->second->nick);
3276                                                         kill_link(count2->second,"Registration timeout");
3277                                                         goto label;
3278                                                 }
3279                                                 if (((time(NULL)) > count2->second->nping) && (isnick(count2->second->nick)) && (count2->second->registered == 7))
3280                                                 {
3281                                                         if ((!count2->second->lastping) && (count2->second->registered == 7))
3282                                                         {
3283                                                                 log(DEBUG,"InspIRCd: ping timeout: %s",count2->second->nick);
3284                                                                 kill_link(count2->second,"Ping timeout");
3285                                                                 goto label;
3286                                                         }
3287                                                         Write(count2->second->fd,"PING :%s",ServerName);
3288                                                         log(DEBUG,"InspIRCd: pinging: %s",count2->second->nick);
3289                                                         count2->second->lastping = 0;
3290                                                         count2->second->nping = time(NULL)+120;
3291                                                 }
3292                                         }
3293                                         count2++;
3294                                         total_in_this_set++;
3295                                 }
3296                                 else break;
3297                         }
3298    
3299                         endingiter = count2;
3300                         count2 = xcount; // roll back to where we were
3301         
3302                         int v = 0;
3303
3304                         tval.tv_usec = 0;
3305                         tval.tv_sec = 0;
3306                         selectResult2 = select(65535, &sfd, NULL, NULL, &tval);
3307                         
3308                         // now loop through all of the items in this pool if any are waiting
3309                         //if (selectResult2 > 0)
3310                         for (user_hash::iterator count2a = xcount; count2a != endingiter; count2a++)
3311                         {
3312
3313 #ifdef _POSIX_PRIORITY_SCHEDULING
3314                                 sched_yield();
3315 #endif
3316
3317                                 result = EAGAIN;
3318                                 if ((count2a->second->fd != -1) && (FD_ISSET (count2a->second->fd, &sfd)))
3319                                 {
3320                                         log(DEBUG,"Reading fd %d",count2a->second->fd);
3321                                         memset(data, 0, 10240);
3322                                         result = read(count2a->second->fd, data, 10240);
3323                                         
3324                                         if (result)
3325                                         {
3326                                                 if (result > 0)
3327                                                         log(DEBUG,"Read %d characters from socket",result);
3328                                                 userrec* current = count2a->second;
3329                                                 int currfd = current->fd;
3330                                                 char* l = strtok(data,"\n");
3331                                                 int floodlines = 0;
3332                                                 while (l)
3333                                                 {
3334                                                         floodlines++;
3335                                                         if ((floodlines > current->flood) && (current->flood != 0))
3336                                                         {
3337                                                                 log(DEFAULT,"Excess flood from: %s!%s@%s",current->nick,current->ident,current->host);
3338                                                                 WriteOpers("*** Excess flood from: %s!%s@%s",current->nick,current->ident,current->host);
3339                                                                 kill_link(current,"Excess flood");
3340                                                                 goto label;
3341                                                         }
3342                                                         char sanitized[NetBufferSize];
3343                                                         memset(sanitized, 0, NetBufferSize);
3344                                                         int ptt = 0;
3345                                                         for (int pt = 0; pt < strlen(l); pt++)
3346                                                         {
3347                                                                 if (l[pt] != '\r')
3348                                                                 {
3349                                                                         sanitized[ptt++] = l[pt];
3350                                                                 }
3351                                                         }
3352                                                         sanitized[ptt] = '\0';
3353                                                         if (strlen(sanitized))
3354                                                         {
3355
3356
3357                                                                 // we're gonna re-scan to check if the nick is gone, after every
3358                                                                 // command - if it has, we're gonna bail
3359                                                                 bool find_again = false;
3360                                                                 process_buffer(sanitized,current);
3361         
3362                                                                 // look for the user's record in case it's changed
3363                                                                 for (user_hash::iterator c2 = clientlist.begin(); c2 != clientlist.end(); c2++)
3364                                                                 {
3365                                                                         if (c2->second->fd == currfd)
3366                                                                         {
3367                                                                                 // found again, update pointer
3368                                                                                 current == c2->second;
3369                                                                                 find_again = true;
3370                                                                                 break;
3371                                                                         }
3372                                                                 }
3373                                                                 if (!find_again)
3374                                                                         goto label;
3375
3376                                                         }
3377                                                         l = strtok(NULL,"\n");
3378                                                 }
3379                                                 goto label;
3380                                         }
3381
3382                                         if ((result == -1) && (errno != EAGAIN) && (errno != EINTR))
3383                                         {
3384                                                 log(DEBUG,"killing: %s",count2a->second->nick);
3385                                                 kill_link(count2a->second,strerror(errno));
3386                                                 goto label;
3387                                         }
3388                                 }
3389                                 // result EAGAIN means nothing read
3390                                 if (result == EAGAIN)
3391                                 {
3392                                 }
3393                                 else
3394                                 if (result == 0)
3395                                 {
3396                                         if (count2->second)
3397                                         {
3398                                                 log(DEBUG,"InspIRCd: Exited: %s",count2a->second->nick);
3399                                                 kill_link(count2a->second,"Client exited");
3400                                                 // must bail here? kill_link removes the hash, corrupting the iterator
3401                                                 log(DEBUG,"Bailing from client exit");
3402                                                 goto label;
3403                                         }
3404                                 }
3405                                 else if (result > 0)
3406                                 {
3407                                 }
3408                         }
3409                 }
3410                 for (int q = 0; q < total_in_this_set; q++)
3411                 {
3412                         // there is no iterator += operator :(
3413                         //if (count2 != clientlist.end())
3414                         //{
3415                                 count2++;
3416                         //}
3417                 }
3418         }
3419         
3420         // set up select call
3421         for (count = 0; count < boundPortCount; count++)
3422         {
3423                 FD_SET (openSockfd[count], &selectFds);
3424         }
3425
3426         tv.tv_usec = 1;
3427         selectResult = select(MAXSOCKS, &selectFds, NULL, NULL, &tv);
3428
3429         /* select is reporting a waiting socket. Poll them all to find out which */
3430         if (selectResult > 0)
3431         {
3432                 char target[MAXBUF], resolved[MAXBUF];
3433                 for (count = 0; count < boundPortCount; count++)                
3434                 {
3435                         if (FD_ISSET (openSockfd[count], &selectFds))
3436                         {
3437                                 length = sizeof (client);
3438                                 incomingSockfd = accept (openSockfd[count], (struct sockaddr *) &client, &length);
3439                               
3440                                 address_cache::iterator iter = IP.find(client.sin_addr);
3441                                 bool iscached = false;
3442                                 if (iter == IP.end())
3443                                 {
3444                                         /* ip isn't in cache, add it */
3445                                         strncpy (target, (char *) inet_ntoa (client.sin_addr), MAXBUF);
3446                                         if(CleanAndResolve(resolved, target) != TRUE)
3447                                         {
3448                                                 strncpy(resolved,target,MAXBUF);
3449                                         }
3450                                         /* hostname now in 'target' */
3451                                         IP[client.sin_addr] = new string(resolved);
3452                                         /* hostname in cache */
3453                                 }
3454                                 else
3455                                 {
3456                                         /* found ip (cached) */
3457                                         strncpy(resolved, iter->second->c_str(), MAXBUF);
3458                                         iscached = true;
3459                                 }
3460                         
3461                                 if (incomingSockfd < 0)
3462                                 {
3463                                         WriteOpers("*** WARNING: Accept failed on port %d (%s)", ports[count],target);
3464                                         log(DEBUG,"InspIRCd: accept failed: %d",ports[count]);
3465                                 }
3466                                 else
3467                                 {
3468                                         AddClient(incomingSockfd, resolved, ports[count], iscached, target);
3469                                         log(DEBUG,"InspIRCd: adding client on port %d fd=%d",ports[count],incomingSockfd);
3470                                 }
3471                                 goto label;
3472                         }
3473                 }
3474         }
3475         label:
3476         if(0) {}; // "Label must be followed by a statement"... so i gave it one.
3477 }
3478 /* not reached */
3479 close (incomingSockfd);
3480 }
3481