]> git.netwichtig.de Git - user/henk/code/inspircd.git/blob - src/inspircd.cpp
Added some stuff to diffrentiate between global and local Q/Z lines
[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 void kill_link_silent(userrec *user,const char* r)
1844 {
1845         user_hash::iterator iter = clientlist.find(user->nick);
1846         
1847         char reason[MAXBUF];
1848         
1849         strncpy(reason,r,MAXBUF);
1850
1851         if (strlen(reason)>MAXQUIT)
1852         {
1853                 reason[MAXQUIT-1] = '\0';
1854         }
1855
1856         log(DEBUG,"kill_link: %s '%s'",user->nick,reason);
1857         Write(user->fd,"ERROR :Closing link (%s@%s) [%s]",user->ident,user->host,reason);
1858         log(DEBUG,"closing fd %d",user->fd);
1859
1860         /* bugfix, cant close() a nonblocking socket (sux!) */
1861         if (user->registered == 7) {
1862                 FOREACH_MOD OnUserQuit(user);
1863                 WriteCommonExcept(user,"QUIT :%s",reason);
1864
1865                 // Q token must go to ALL servers!!!
1866                 char buffer[MAXBUF];
1867                 snprintf(buffer,MAXBUF,"Q %s :%s",user->nick,reason);
1868                 NetSendToAll(buffer);
1869         }
1870
1871         /* push the socket on a stack of sockets due to be closed at the next opportunity
1872          * 'Client exited' is an exception to this as it means the client side has already
1873          * closed the socket, we don't need to do it.
1874          */
1875         fd_reap.push_back(user->fd);
1876         
1877         bool do_purge = false;
1878         
1879         if (iter != clientlist.end())
1880         {
1881                 log(DEBUG,"deleting user hash value %d",iter->second);
1882                 if ((iter->second) && (user->registered == 7)) {
1883                         delete iter->second;
1884                 }
1885                 clientlist.erase(iter);
1886         }
1887
1888         if (user->registered == 7) {
1889                 purge_empty_chans();
1890         }
1891 }
1892
1893
1894
1895 // looks up a users password for their connection class (<ALLOW>/<DENY> tags)
1896
1897 char* Passwd(userrec *user)
1898 {
1899         for (ClassVector::iterator i = Classes.begin(); i != Classes.end(); i++)
1900         {
1901                 if (match(user->host,i->host) && (i->type == CC_ALLOW))
1902                 {
1903                         return i->pass;
1904                 }
1905         }
1906         return "";
1907 }
1908
1909 bool IsDenied(userrec *user)
1910 {
1911         for (ClassVector::iterator i = Classes.begin(); i != Classes.end(); i++)
1912         {
1913                 if (match(user->host,i->host) && (i->type == CC_DENY))
1914                 {
1915                         return true;
1916                 }
1917         }
1918         return false;
1919 }
1920
1921
1922
1923
1924 /* sends out an error notice to all connected clients (not to be used
1925  * lightly!) */
1926
1927 void send_error(char *s)
1928 {
1929         log(DEBUG,"send_error: %s",s);
1930         for (user_hash::const_iterator i = clientlist.begin(); i != clientlist.end(); i++)
1931         {
1932                 if (isnick(i->second->nick))
1933                 {
1934                         WriteServ(i->second->fd,"NOTICE %s :%s",i->second->nick,s);
1935                 }
1936                 else
1937                 {
1938                         // fix - unregistered connections receive ERROR, not NOTICE
1939                         Write(i->second->fd,"ERROR :%s",s);
1940                 }
1941         }
1942 }
1943
1944 void Error(int status)
1945 {
1946         signal (SIGALRM, SIG_IGN);
1947         signal (SIGPIPE, SIG_IGN);
1948         signal (SIGTERM, SIG_IGN);
1949         signal (SIGABRT, SIG_IGN);
1950         signal (SIGSEGV, SIG_IGN);
1951         signal (SIGURG, SIG_IGN);
1952         signal (SIGKILL, SIG_IGN);
1953         log(DEBUG,"*** fell down a pothole in the road to perfection ***");
1954         send_error("Error! Segmentation fault! save meeeeeeeeeeeeee *splat!*");
1955         exit(status);
1956 }
1957
1958
1959 int main(int argc, char *argv[])
1960 {
1961         Start();
1962         srand(time(NULL));
1963         log(DEBUG,"*** InspIRCd starting up!");
1964         if (!FileExists(CONFIG_FILE))
1965         {
1966                 printf("ERROR: Cannot open config file: %s\nExiting...\n",CONFIG_FILE);
1967                 log(DEBUG,"main: no config");
1968                 printf("ERROR: Your config file is missing, this IRCd will self destruct in 10 seconds!\n");
1969                 Exit(ERROR);
1970         }
1971         if (argc > 1) {
1972                 if (!strcmp(argv[1],"-nofork")) {
1973                         nofork = true;
1974                 }
1975         }
1976         if (InspIRCd() == ERROR)
1977         {
1978                 log(DEBUG,"main: daemon function bailed");
1979                 printf("ERROR: could not initialise. Shutting down.\n");
1980                 Exit(ERROR);
1981         }
1982         Exit(TRUE);
1983         return 0;
1984 }
1985
1986 template<typename T> inline string ConvToStr(const T &in)
1987 {
1988         stringstream tmp;
1989         if (!(tmp << in)) return string();
1990         return tmp.str();
1991 }
1992
1993 /* re-allocates a nick in the user_hash after they change nicknames,
1994  * returns a pointer to the new user as it may have moved */
1995
1996 userrec* ReHashNick(char* Old, char* New)
1997 {
1998         user_hash::iterator newnick;
1999         user_hash::iterator oldnick = clientlist.find(Old);
2000
2001         log(DEBUG,"ReHashNick: %s %s",Old,New);
2002         
2003         if (!strcasecmp(Old,New))
2004         {
2005                 log(DEBUG,"old nick is new nick, skipping");
2006                 return oldnick->second;
2007         }
2008         
2009         if (oldnick == clientlist.end()) return NULL; /* doesnt exist */
2010
2011         log(DEBUG,"ReHashNick: Found hashed nick %s",Old);
2012
2013         clientlist[New] = new userrec();
2014         clientlist[New] = oldnick->second;
2015         /*delete oldnick->second; */
2016         clientlist.erase(oldnick);
2017
2018         log(DEBUG,"ReHashNick: Nick rehashed as %s",New);
2019         
2020         return clientlist[New];
2021 }
2022
2023 /* adds or updates an entry in the whowas list */
2024 void AddWhoWas(userrec* u)
2025 {
2026         user_hash::iterator iter = whowas.find(u->nick);
2027         userrec *a = new userrec();
2028         strcpy(a->nick,u->nick);
2029         strcpy(a->ident,u->ident);
2030         strcpy(a->dhost,u->dhost);
2031         strcpy(a->host,u->host);
2032         strcpy(a->fullname,u->fullname);
2033         strcpy(a->server,u->server);
2034         a->signon = u->signon;
2035
2036         /* MAX_WHOWAS:   max number of /WHOWAS items
2037          * WHOWAS_STALE: number of hours before a WHOWAS item is marked as stale and
2038          *               can be replaced by a newer one
2039          */
2040         
2041         if (iter == whowas.end())
2042         {
2043                 if (whowas.size() == WHOWAS_MAX)
2044                 {
2045                         for (user_hash::iterator i = whowas.begin(); i != whowas.end(); i++)
2046                         {
2047                                 // 3600 seconds in an hour ;)
2048                                 if ((i->second->signon)<(time(NULL)-(WHOWAS_STALE*3600)))
2049                                 {
2050                                         delete i->second;
2051                                         i->second = a;
2052                                         log(DEBUG,"added WHOWAS entry, purged an old record");
2053                                         return;
2054                                 }
2055                         }
2056                 }
2057                 else
2058                 {
2059                         log(DEBUG,"added fresh WHOWAS entry");
2060                         whowas[a->nick] = a;
2061                 }
2062         }
2063         else
2064         {
2065                 log(DEBUG,"updated WHOWAS entry");
2066                 delete iter->second;
2067                 iter->second = a;
2068         }
2069 }
2070
2071
2072 /* add a client connection to the sockets list */
2073 void AddClient(int socket, char* host, int port, bool iscached, char* ip)
2074 {
2075         int i;
2076         int blocking = 1;
2077         char resolved[MAXBUF];
2078         string tempnick;
2079         char tn2[MAXBUF];
2080         user_hash::iterator iter;
2081
2082         tempnick = ConvToStr(socket) + "-unknown";
2083         sprintf(tn2,"%d-unknown",socket);
2084
2085         iter = clientlist.find(tempnick);
2086
2087         if (iter != clientlist.end()) return;
2088
2089         /*
2090          * It is OK to access the value here this way since we know
2091          * it exists, we just created it above.
2092          *
2093          * At NO other time should you access a value in a map or a
2094          * hash_map this way.
2095          */
2096         clientlist[tempnick] = new userrec();
2097
2098         NonBlocking(socket);
2099         log(DEBUG,"AddClient: %d %s %d",socket,host,port);
2100
2101         clientlist[tempnick]->fd = socket;
2102         strncpy(clientlist[tempnick]->nick, tn2,NICKMAX);
2103         strncpy(clientlist[tempnick]->host, host,160);
2104         strncpy(clientlist[tempnick]->dhost, host,160);
2105         strncpy(clientlist[tempnick]->server, ServerName,256);
2106         strncpy(clientlist[tempnick]->ident, "unknown",9);
2107         clientlist[tempnick]->registered = 0;
2108         clientlist[tempnick]->signon = time(NULL);
2109         clientlist[tempnick]->nping = time(NULL)+240;
2110         clientlist[tempnick]->lastping = 1;
2111         clientlist[tempnick]->port = port;
2112         strncpy(clientlist[tempnick]->ip,ip,32);
2113
2114         if (iscached)
2115         {
2116                 WriteServ(socket,"NOTICE Auth :Found your hostname (cached)...");
2117         }
2118         else
2119         {
2120                 WriteServ(socket,"NOTICE Auth :Looking up your hostname...");
2121         }
2122
2123         // set the registration timeout for this user
2124         unsigned long class_regtimeout = 90;
2125         for (ClassVector::iterator i = Classes.begin(); i != Classes.end(); i++)
2126         {
2127                 if (match(clientlist[tempnick]->host,i->host) && (i->type == CC_ALLOW))
2128                 {
2129                         class_regtimeout = (unsigned long)i->registration_timeout;
2130                         break;
2131                 }
2132         }
2133
2134         int class_flood = 0;
2135         for (ClassVector::iterator i = Classes.begin(); i != Classes.end(); i++)
2136         {
2137                 if (match(clientlist[tempnick]->host,i->host) && (i->type == CC_ALLOW))
2138                 {
2139                         class_flood = i->flood;
2140                         break;
2141                 }
2142         }
2143
2144         clientlist[tempnick]->timeout = time(NULL)+class_regtimeout;
2145         clientlist[tempnick]->flood = class_flood;
2146
2147         for (int i = 0; i < MAXCHANS; i++)
2148         {
2149                 clientlist[tempnick]->chans[i].channel = NULL;
2150                 clientlist[tempnick]->chans[i].uc_modes = 0;
2151         }
2152
2153         if (clientlist.size() == MAXCLIENTS)
2154                 kill_link(clientlist[tempnick],"No more connections allowed in this class");
2155                 
2156         char* r = matches_zline(ip);
2157         if (r)
2158         {
2159                 char reason[MAXBUF];
2160                 snprintf(reason,MAXBUF,"Z-Lined: %s",r);
2161                 kill_link(clientlist[tempnick],reason);
2162         }
2163 }
2164
2165
2166 int usercnt(void)
2167 {
2168         return clientlist.size();
2169 }
2170
2171
2172 int usercount_invisible(void)
2173 {
2174         int c = 0;
2175
2176         for (user_hash::const_iterator i = clientlist.begin(); i != clientlist.end(); i++)
2177         {
2178                 if ((i->second->fd) && (isnick(i->second->nick)) && (strchr(i->second->modes,'i'))) c++;
2179         }
2180         return c;
2181 }
2182
2183 int usercount_opers(void)
2184 {
2185         int c = 0;
2186
2187         for (user_hash::const_iterator i = clientlist.begin(); i != clientlist.end(); i++)
2188         {
2189                 if ((i->second->fd) && (isnick(i->second->nick)) && (strchr(i->second->modes,'o'))) c++;
2190         }
2191         return c;
2192 }
2193
2194 int usercount_unknown(void)
2195 {
2196         int c = 0;
2197
2198         for (user_hash::const_iterator i = clientlist.begin(); i != clientlist.end(); i++)
2199         {
2200                 if ((i->second->fd) && (i->second->registered != 7))
2201                         c++;
2202         }
2203         return c;
2204 }
2205
2206 long chancount(void)
2207 {
2208         return chanlist.size();
2209 }
2210
2211 long count_servs(void)
2212 {
2213         int c = 0;
2214         //for (int j = 0; j < 255; j++)
2215         //{
2216         //      if (servers[j] != NULL)
2217         //              c++;
2218         //}
2219         return c;
2220 }
2221
2222 long servercount(void)
2223 {
2224         return count_servs()+1;
2225 }
2226
2227 long local_count()
2228 {
2229         int c = 0;
2230         for (user_hash::const_iterator i = clientlist.begin(); i != clientlist.end(); i++)
2231         {
2232                 if ((i->second->fd) && (isnick(i->second->nick)) && (!strcasecmp(i->second->server,ServerName))) c++;
2233         }
2234         return c;
2235 }
2236
2237
2238 void ShowMOTD(userrec *user)
2239 {
2240         if (!MOTD.size())
2241         {
2242                 WriteServ(user->fd,"422 %s :Message of the day file is missing.",user->nick);
2243                 return;
2244         }
2245         WriteServ(user->fd,"375 %s :- %s message of the day",user->nick,ServerName);
2246         for (int i = 0; i != MOTD.size(); i++)
2247         {
2248                                 WriteServ(user->fd,"372 %s :- %s",user->nick,MOTD[i].c_str());
2249         }
2250         WriteServ(user->fd,"376 %s :End of %s message of the day.",user->nick,ServerName);
2251 }
2252
2253 void ShowRULES(userrec *user)
2254 {
2255         if (!RULES.size())
2256         {
2257                 WriteServ(user->fd,"NOTICE %s :Rules file is missing.",user->nick);
2258                 return;
2259         }
2260         WriteServ(user->fd,"NOTICE %s :%s rules",user->nick,ServerName);
2261         for (int i = 0; i != RULES.size(); i++)
2262         {
2263                                 WriteServ(user->fd,"NOTICE %s :%s",user->nick,RULES[i].c_str());
2264         }
2265         WriteServ(user->fd,"NOTICE %s :End of %s rules.",user->nick,ServerName);
2266 }
2267
2268 /* shows the message of the day, and any other on-logon stuff */
2269 void ConnectUser(userrec *user)
2270 {
2271         user->registered = 7;
2272         user->idle_lastmsg = time(NULL);
2273         log(DEBUG,"ConnectUser: %s",user->nick);
2274
2275         if (strcmp(Passwd(user),"") && (!user->haspassed))
2276         {
2277                 kill_link(user,"Invalid password");
2278                 return;
2279         }
2280         if (IsDenied(user))
2281         {
2282                 kill_link(user,"Unauthorised connection");
2283                 return;
2284         }
2285
2286         char match_against[MAXBUF];
2287         snprintf(match_against,MAXBUF,"%s@%s",user->ident,user->host);
2288         char* r = matches_gline(match_against);
2289         if (r)
2290         {
2291                 char reason[MAXBUF];
2292                 snprintf(reason,MAXBUF,"G-Lined: %s",r);
2293                 kill_link_silent(user,reason);
2294                 return;
2295         }
2296
2297         r = matches_kline(user->host);
2298         if (r)
2299         {
2300                 char reason[MAXBUF];
2301                 snprintf(reason,MAXBUF,"K-Lined: %s",r);
2302                 kill_link_silent(user,reason);
2303                 return;
2304         }
2305
2306         WriteServ(user->fd,"NOTICE Auth :Welcome to \002%s\002!",Network);
2307         WriteServ(user->fd,"001 %s :Welcome to the %s IRC Network %s!%s@%s",user->nick,Network,user->nick,user->ident,user->host);
2308         WriteServ(user->fd,"002 %s :Your host is %s, running version %s",user->nick,ServerName,VERSION);
2309         WriteServ(user->fd,"003 %s :This server was created %s %s",user->nick,__TIME__,__DATE__);
2310         WriteServ(user->fd,"004 %s %s %s iowghraAsORVSxNCWqBzvdHtGI lvhopsmntikrRcaqOALQbSeKVfHGCuzN",user->nick,ServerName,VERSION);
2311         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);
2312         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);
2313         ShowMOTD(user);
2314         FOREACH_MOD OnUserConnect(user);
2315         WriteOpers("*** Client connecting on port %d: %s!%s@%s",user->port,user->nick,user->ident,user->host);
2316         
2317         char buffer[MAXBUF];
2318         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);
2319         NetSendToAll(buffer);
2320 }
2321
2322 void handle_version(char **parameters, int pcnt, userrec *user)
2323 {
2324         char Revision[] = "$Revision$";
2325
2326         char *s1 = Revision;
2327         char *savept;
2328         char *v1 = strtok_r(s1," ",&savept);
2329         s1 = savept;
2330         char *v2 = strtok_r(s1," ",&savept);
2331         s1 = savept;
2332         
2333         WriteServ(user->fd,"351 %s :%s Rev. %s %s :%s (O=%d)",user->nick,VERSION,v2,ServerName,SYSTEM,OPTIMISATION);
2334 }
2335
2336
2337 // calls a handler function for a command
2338
2339 void call_handler(const char* commandname,char **parameters, int pcnt, userrec *user)
2340 {
2341                 for (int i = 0; i < cmdlist.size(); i++)
2342                 {
2343                         if (!strcasecmp(cmdlist[i].command,commandname))
2344                         {
2345                                 if (cmdlist[i].handler_function)
2346                                 {
2347                                         if (pcnt>=cmdlist[i].min_params)
2348                                         {
2349                                                 if (strchr(user->modes,cmdlist[i].flags_needed))
2350                                                 {
2351                                                         cmdlist[i].handler_function(parameters,pcnt,user);
2352                                                 }
2353                                         }
2354                                 }
2355                         }
2356                 }
2357 }
2358
2359 void DoSplitEveryone()
2360 {
2361         bool go_again = true;
2362         while (go_again)
2363         {
2364                 go_again = false;
2365                 for (int i = 0; i < 32; i++)
2366                 {
2367                         if (me[i] != NULL)
2368                         {
2369                                 for (vector<ircd_connector>::iterator j = me[i]->connectors.begin(); j != me[i]->connectors.end(); j++)
2370                                 {
2371                                         if (strcasecmp(j->GetServerName().c_str(),ServerName))
2372                                         {
2373                                                 j->routes.clear();
2374                                                 j->CloseConnection();
2375                                                 me[i]->connectors.erase(j);
2376                                                 go_again = true;
2377                                                 break;
2378                                         }
2379                                 }
2380                         }
2381                 }
2382         }
2383         log(DEBUG,"Removed server. Will remove clients...");
2384         // iterate through the userlist and remove all users on this server.
2385         // because we're dealing with a mesh, we dont have to deal with anything
2386         // "down-route" from this server (nice huh)
2387         go_again = true;
2388         char reason[MAXBUF];
2389         while (go_again)
2390         {
2391                 go_again = false;
2392                 for (user_hash::const_iterator u = clientlist.begin(); u != clientlist.end(); u++)
2393                 {
2394                         if (strcasecmp(u->second->server,ServerName))
2395                         {
2396                                 snprintf(reason,MAXBUF,"%s %s",ServerName,u->second->server);
2397                                 kill_link(u->second,reason);
2398                                 go_again = true;
2399                                 break;
2400                         }
2401                 }
2402         }
2403 }
2404
2405
2406
2407 char islast(const char* s)
2408 {
2409         char c = '`';
2410         for (int j = 0; j < 32; j++)
2411         {
2412                 if (me[j] != NULL)
2413                 {
2414                         for (int k = 0; k < me[j]->connectors.size(); k++)
2415                         {
2416                                 if (strcasecmp(me[j]->connectors[k].GetServerName().c_str(),s))
2417                                 {
2418                                         c = '|';
2419                                 }
2420                                 if (!strcasecmp(me[j]->connectors[k].GetServerName().c_str(),s))
2421                                 {
2422                                         c = '`';
2423                                 }
2424                         }
2425                 }
2426         }
2427         return c;
2428 }
2429
2430 long map_count(const char* s)
2431 {
2432         int c = 0;
2433         for (user_hash::const_iterator i = clientlist.begin(); i != clientlist.end(); i++)
2434         {
2435                 if ((i->second->fd) && (isnick(i->second->nick)) && (!strcasecmp(i->second->server,s))) c++;
2436         }
2437         return c;
2438 }
2439
2440
2441 void force_nickchange(userrec* user,const char* newnick)
2442 {
2443         char nick[MAXBUF];
2444         int MOD_RESULT = 0;
2445         
2446         strcpy(nick,"");
2447
2448         FOREACH_RESULT(OnUserPreNick(user,newnick));
2449         if (MOD_RESULT) {
2450                 kill_link(user,"Nickname collision");
2451                 return;
2452         }
2453         if (matches_qline(newnick))
2454         {
2455                 kill_link(user,"Nickname collision");
2456                 return;
2457         }
2458         
2459         if (user)
2460         {
2461                 if (newnick)
2462                 {
2463                         strncpy(nick,newnick,MAXBUF);
2464                 }
2465                 if (user->registered == 7)
2466                 {
2467                         char* pars[1];
2468                         pars[0] = nick;
2469                         handle_nick(pars,1,user);
2470                 }
2471         }
2472 }
2473                                 
2474
2475 int process_parameters(char **command_p,char *parameters)
2476 {
2477         int i = 0;
2478         int j = 0;
2479         int q = 0;
2480         q = strlen(parameters);
2481         if (!q)
2482         {
2483                 /* no parameters, command_p invalid! */
2484                 return 0;
2485         }
2486         if (parameters[0] == ':')
2487         {
2488                 command_p[0] = parameters+1;
2489                 return 1;
2490         }
2491         if (q)
2492         {
2493                 if ((strchr(parameters,' ')==NULL) || (parameters[0] == ':'))
2494                 {
2495                         /* only one parameter */
2496                         command_p[0] = parameters;
2497                         if (parameters[0] == ':')
2498                         {
2499                                 if (strchr(parameters,' ') != NULL)
2500                                 {
2501                                         command_p[0]++;
2502                                 }
2503                         }
2504                         return 1;
2505                 }
2506         }
2507         command_p[j++] = parameters;
2508         for (int i = 0; i <= q; i++)
2509         {
2510                 if (parameters[i] == ' ')
2511                 {
2512                         command_p[j++] = parameters+i+1;
2513                         parameters[i] = '\0';
2514                         if (command_p[j-1][0] == ':')
2515                         {
2516                                 *command_p[j-1]++; /* remove dodgy ":" */
2517                                 break;
2518                                 /* parameter like this marks end of the sequence */
2519                         }
2520                 }
2521         }
2522         return j; /* returns total number of items in the list */
2523 }
2524
2525 void process_command(userrec *user, char* cmd)
2526 {
2527         char *parameters;
2528         char *command;
2529         char *command_p[127];
2530         char p[MAXBUF], temp[MAXBUF];
2531         int i, j, items, cmd_found;
2532
2533         for (int i = 0; i < 127; i++)
2534                 command_p[i] = NULL;
2535
2536         if (!user)
2537         {
2538                 return;
2539         }
2540         if (!cmd)
2541         {
2542                 return;
2543         }
2544         if (!strcmp(cmd,""))
2545         {
2546                 return;
2547         }
2548         
2549         int total_params = 0;
2550         if (strlen(cmd)>2)
2551         {
2552                 for (int q = 0; q < strlen(cmd)-1; q++)
2553                 {
2554                         if ((cmd[q] == ' ') && (cmd[q+1] == ':'))
2555                         {
2556                                 total_params++;
2557                                 // found a 'trailing', we dont count them after this.
2558                                 break;
2559                         }
2560                         if (cmd[q] == ' ')
2561                                 total_params++;
2562                 }
2563         }
2564         
2565         // another phidjit bug...
2566         if (total_params > 126)
2567         {
2568                 kill_link(user,"Protocol violation (1)");
2569                 return;
2570         }
2571         
2572         strcpy(temp,cmd);
2573
2574         std::string tmp = cmd;
2575         for (int i = 0; i <= MODCOUNT; i++)
2576         {
2577                 std::string oldtmp = tmp;
2578                 modules[i]->OnServerRaw(tmp,true);
2579                 if (oldtmp != tmp)
2580                 {
2581                         log(DEBUG,"A Module changed the input string!");
2582                         log(DEBUG,"New string: %s",tmp.c_str());
2583                         log(DEBUG,"Old string: %s",oldtmp.c_str());
2584                         break;
2585                 }
2586         }
2587         strncpy(cmd,tmp.c_str(),MAXBUF);
2588         strcpy(temp,cmd);
2589
2590         if (!strchr(cmd,' '))
2591         {
2592                 /* no parameters, lets skip the formalities and not chop up
2593                  * the string */
2594                 log(DEBUG,"About to preprocess command with no params");
2595                 items = 0;
2596                 command_p[0] = NULL;
2597                 parameters = NULL;
2598                 for (int i = 0; i <= strlen(cmd); i++)
2599                 {
2600                         cmd[i] = toupper(cmd[i]);
2601                 }
2602                 log(DEBUG,"Preprocess done length=%d",strlen(cmd));
2603                 command = cmd;
2604         }
2605         else
2606         {
2607                 strcpy(cmd,"");
2608                 j = 0;
2609                 /* strip out extraneous linefeeds through mirc's crappy pasting (thanks Craig) */
2610                 for (int i = 0; i < strlen(temp); i++)
2611                 {
2612                         if ((temp[i] != 10) && (temp[i] != 13) && (temp[i] != 0) && (temp[i] != 7))
2613                         {
2614                                 cmd[j++] = temp[i];
2615                                 cmd[j] = 0;
2616                         }
2617                 }
2618                 /* split the full string into a command plus parameters */
2619                 parameters = p;
2620                 strcpy(p," ");
2621                 command = cmd;
2622                 if (strchr(cmd,' '))
2623                 {
2624                         for (int i = 0; i <= strlen(cmd); i++)
2625                         {
2626                                 /* capitalise the command ONLY, leave params intact */
2627                                 cmd[i] = toupper(cmd[i]);
2628                                 /* are we nearly there yet?! :P */
2629                                 if (cmd[i] == ' ')
2630                                 {
2631                                         command = cmd;
2632                                         parameters = cmd+i+1;
2633                                         cmd[i] = '\0';
2634                                         break;
2635                                 }
2636                         }
2637                 }
2638                 else
2639                 {
2640                         for (int i = 0; i <= strlen(cmd); i++)
2641                         {
2642                                 cmd[i] = toupper(cmd[i]);
2643                         }
2644                 }
2645
2646         }
2647         cmd_found = 0;
2648         
2649         if (strlen(command)>MAXCOMMAND)
2650         {
2651                 kill_link(user,"Protocol violation (2)");
2652                 return;
2653         }
2654         
2655         for (int x = 0; x < strlen(command); x++)
2656         {
2657                 if (((command[x] < 'A') || (command[x] > 'Z')) && (command[x] != '.'))
2658                 {
2659                         if (((command[x] < '0') || (command[x]> '9')) && (command[x] != '-'))
2660                         {
2661                                 if (!strchr("@!\"$%^&*(){}[]_-=+;:'#~,.<>/?\\|`",command[x]))
2662                                 {
2663                                         kill_link(user,"Protocol violation (3)");
2664                                         return;
2665                                 }
2666                         }
2667                 }
2668         }
2669
2670         for (int i = 0; i != cmdlist.size(); i++)
2671         {
2672                 if (strcmp(cmdlist[i].command,""))
2673                 {
2674                         if (strlen(command)>=(strlen(cmdlist[i].command))) if (!strncmp(command, cmdlist[i].command,MAXCOMMAND))
2675                         {
2676                                 log(DEBUG,"Found matching command");
2677
2678                                 if (parameters)
2679                                 {
2680                                         if (strcmp(parameters,""))
2681                                         {
2682                                                 items = process_parameters(command_p,parameters);
2683                                         }
2684                                         else
2685                                         {
2686                                                 items = 0;
2687                                                 command_p[0] = NULL;
2688                                         }
2689                                 }
2690                                 else
2691                                 {
2692                                         items = 0;
2693                                         command_p[0] = NULL;
2694                                 }
2695                                 
2696                                 if (user)
2697                                 {
2698                                         log(DEBUG,"Processing command");
2699                                         
2700                                         /* activity resets the ping pending timer */
2701                                         user->nping = time(NULL) + 120;
2702                                         if ((items) < cmdlist[i].min_params)
2703                                         {
2704                                                 log(DEBUG,"process_command: not enough parameters: %s %s",user->nick,command);
2705                                                 WriteServ(user->fd,"461 %s %s :Not enough parameters",user->nick,command);
2706                                                 return;
2707                                         }
2708                                         if ((!strchr(user->modes,cmdlist[i].flags_needed)) && (cmdlist[i].flags_needed))
2709                                         {
2710                                                 log(DEBUG,"process_command: permission denied: %s %s",user->nick,command);
2711                                                 WriteServ(user->fd,"481 %s :Permission Denied- You do not have the required operator privilages",user->nick);
2712                                                 cmd_found = 1;
2713                                                 return;
2714                                         }
2715                                         /* if the command isnt USER, PASS, or NICK, and nick is empty,
2716                                          * deny command! */
2717                                         if ((strncmp(command,"USER",4)) && (strncmp(command,"NICK",4)) && (strncmp(command,"PASS",4)))
2718                                         {
2719                                                 if ((!isnick(user->nick)) || (user->registered != 7))
2720                                                 {
2721                                                         log(DEBUG,"process_command: not registered: %s %s",user->nick,command);
2722                                                         WriteServ(user->fd,"451 %s :You have not registered",command);
2723                                                         return;
2724                                                 }
2725                                         }
2726                                         if ((user->registered == 7) || (!strcmp(command,"USER")) || (!strcmp(command,"NICK")) || (!strcmp(command,"PASS")))
2727                                         {
2728                                                 log(DEBUG,"process_command: handler: %s %s %d",user->nick,command,items);
2729                                                 if (cmdlist[i].handler_function)
2730                                                 {
2731                                                         /* ikky /stats counters */
2732                                                         if (temp)
2733                                                         {
2734                                                                 if (user)
2735                                                                 {
2736                                                                         user->bytes_in += strlen(temp);
2737                                                                         user->cmds_in++;
2738                                                                 }
2739                                                                 cmdlist[i].use_count++;
2740                                                                 cmdlist[i].total_bytes+=strlen(temp);
2741                                                         }
2742
2743                                                         /* WARNING: nothing may come after the
2744                                                          * command handler call, as the handler
2745                                                          * may free the user structure! */
2746
2747                                                         cmdlist[i].handler_function(command_p,items,user);
2748                                                 }
2749                                                 return;
2750                                         }
2751                                         else
2752                                         {
2753                                                 log(DEBUG,"process_command: not registered: %s %s",user->nick,command);
2754                                                 WriteServ(user->fd,"451 %s :You have not registered",command);
2755                                                 return;
2756                                         }
2757                                 }
2758                                 cmd_found = 1;
2759                         }
2760                 }
2761         }
2762         if ((!cmd_found) && (user))
2763         {
2764                 log(DEBUG,"process_command: not in table: %s %s",user->nick,command);
2765                 WriteServ(user->fd,"421 %s %s :Unknown command",user->nick,command);
2766         }
2767 }
2768
2769
2770 void createcommand(char* cmd, handlerfunc f, char flags, int minparams)
2771 {
2772         command_t comm;
2773         /* create the command and push it onto the table */     
2774         strcpy(comm.command,cmd);
2775         comm.handler_function = f;
2776         comm.flags_needed = flags;
2777         comm.min_params = minparams;
2778         comm.use_count = 0;
2779         comm.total_bytes = 0;
2780         cmdlist.push_back(comm);
2781         log(DEBUG,"Added command %s (%d parameters)",cmd,minparams);
2782 }
2783
2784 void SetupCommandTable(void)
2785 {
2786         createcommand("USER",handle_user,0,4);
2787         createcommand("NICK",handle_nick,0,1);
2788         createcommand("QUIT",handle_quit,0,0);
2789         createcommand("VERSION",handle_version,0,0);
2790         createcommand("PING",handle_ping,0,1);
2791         createcommand("PONG",handle_pong,0,1);
2792         createcommand("ADMIN",handle_admin,0,0);
2793         createcommand("PRIVMSG",handle_privmsg,0,2);
2794         createcommand("INFO",handle_info,0,0);
2795         createcommand("TIME",handle_time,0,0);
2796         createcommand("WHOIS",handle_whois,0,1);
2797         createcommand("WALLOPS",handle_wallops,'o',1);
2798         createcommand("NOTICE",handle_notice,0,2);
2799         createcommand("JOIN",handle_join,0,1);
2800         createcommand("NAMES",handle_names,0,1);
2801         createcommand("PART",handle_part,0,1);
2802         createcommand("KICK",handle_kick,0,2);
2803         createcommand("MODE",handle_mode,0,1);
2804         createcommand("TOPIC",handle_topic,0,1);
2805         createcommand("WHO",handle_who,0,1);
2806         createcommand("MOTD",handle_motd,0,0);
2807         createcommand("RULES",handle_rules,0,0);
2808         createcommand("OPER",handle_oper,0,2);
2809         createcommand("LIST",handle_list,0,0);
2810         createcommand("DIE",handle_die,'o',1);
2811         createcommand("RESTART",handle_restart,'o',1);
2812         createcommand("KILL",handle_kill,'o',2);
2813         createcommand("REHASH",handle_rehash,'o',0);
2814         createcommand("LUSERS",handle_lusers,0,0);
2815         createcommand("STATS",handle_stats,0,1);
2816         createcommand("USERHOST",handle_userhost,0,1);
2817         createcommand("AWAY",handle_away,0,0);
2818         createcommand("ISON",handle_ison,0,0);
2819         createcommand("SUMMON",handle_summon,0,0);
2820         createcommand("USERS",handle_users,0,0);
2821         createcommand("INVITE",handle_invite,0,2);
2822         createcommand("PASS",handle_pass,0,1);
2823         createcommand("TRACE",handle_trace,'o',0);
2824         createcommand("WHOWAS",handle_whowas,0,1);
2825         createcommand("CONNECT",handle_connect,'o',1);
2826         createcommand("SQUIT",handle_squit,'o',0);
2827         createcommand("MODULES",handle_modules,'o',0);
2828         createcommand("LINKS",handle_links,0,0);
2829         createcommand("MAP",handle_map,0,0);
2830         createcommand("KLINE",handle_kline,'o',1);
2831         createcommand("GLINE",handle_gline,'o',1);
2832         createcommand("ZLINE",handle_zline,'o',1);
2833         createcommand("QLINE",handle_qline,'o',1);
2834 }
2835
2836 void process_buffer(const char* cmdbuf,userrec *user)
2837 {
2838         if (!user)
2839         {
2840                 log(DEFAULT,"*** BUG *** process_buffer was given an invalid parameter");
2841                 return;
2842         }
2843         char cmd[MAXBUF];
2844         int i;
2845         if (!cmdbuf)
2846         {
2847                 log(DEFAULT,"*** BUG *** process_buffer was given an invalid parameter");
2848                 return;
2849         }
2850         if (!strcmp(cmdbuf,""))
2851         {
2852                 return;
2853         }
2854         while ((cmdbuf[0] == ' ') && (strlen(cmdbuf)>0)) cmdbuf++; // strip leading spaces
2855
2856         strncpy(cmd,cmdbuf,MAXBUF);
2857         if (!strcmp(cmd,""))
2858         {
2859                 return;
2860         }
2861         if ((cmd[strlen(cmd)-1] == 13) || (cmd[strlen(cmd)-1] == 10))
2862         {
2863                 cmd[strlen(cmd)-1] = '\0';
2864         }
2865         if ((cmd[strlen(cmd)-1] == 13) || (cmd[strlen(cmd)-1] == 10))
2866         {
2867                 cmd[strlen(cmd)-1] = '\0';
2868         }
2869
2870         while ((cmd[strlen(cmd)-1] == ' ') && (strlen(cmd)>0)) // strip trailing spaces
2871         {
2872                 cmd[strlen(cmd)-1] = '\0';
2873         }
2874
2875         if (!strcmp(cmd,""))
2876         {
2877                 return;
2878         }
2879         log(DEBUG,"InspIRCd: processing: %s %s",user->nick,cmd);
2880         tidystring(cmd);
2881         if ((user) && (cmd))
2882         {
2883                 process_command(user,cmd);
2884         }
2885 }
2886
2887 void DoSync(serverrec* serv, char* tcp_host)
2888 {
2889         char data[MAXBUF];
2890         log(DEBUG,"Sending sync");
2891         // send start of sync marker: Y <timestamp>
2892         // at this point the ircd receiving it starts broadcasting this netburst to all ircds
2893         // except the ones its receiving it from.
2894         snprintf(data,MAXBUF,"Y %d",time(NULL));
2895         serv->SendPacket(data,tcp_host);
2896         // send users and channels
2897         for (user_hash::iterator u = clientlist.begin(); u != clientlist.end(); u++)
2898         {
2899                 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);
2900                 serv->SendPacket(data,tcp_host);
2901                 if (strcmp(chlist(u->second),""))
2902                 {
2903                         snprintf(data,MAXBUF,"J %s %s",u->second->nick,chlist(u->second));
2904                         serv->SendPacket(data,tcp_host);
2905                 }
2906         }
2907         // send channel modes, topics etc...
2908         for (chan_hash::iterator c = chanlist.begin(); c != chanlist.end(); c++)
2909         {
2910                 snprintf(data,MAXBUF,"M %s +%s",c->second->name,chanmodes(c->second));
2911                 serv->SendPacket(data,tcp_host);
2912                 if (strcmp(c->second->topic,""))
2913                 {
2914                         snprintf(data,MAXBUF,"T %d %s %s :%s",c->second->topicset,c->second->setby,c->second->name,c->second->topic);
2915                         serv->SendPacket(data,tcp_host);
2916                 }
2917                 // send current banlist
2918                 
2919                 for (BanList::iterator b = c->second->bans.begin(); b != c->second->bans.end(); b++)
2920                 {
2921                         snprintf(data,MAXBUF,"M %s +b %s",b->set_time,c->second->name,b->data);
2922                         serv->SendPacket(data,tcp_host);
2923                 }
2924         }
2925         // sync global zlines, glines, etc
2926         sync_xlines(serv,tcp_host);
2927         snprintf(data,MAXBUF,"F %d",time(NULL));
2928         serv->SendPacket(data,tcp_host);
2929         log(DEBUG,"Sent sync");
2930         // ircd sends its serverlist after the end of sync here
2931 }
2932
2933
2934 void NetSendMyRoutingTable()
2935 {
2936         // send out a line saying what is reachable to us.
2937         // E.g. if A is linked to B C and D, send out:
2938         // $ A B C D
2939         // if its only linked to B and D send out:
2940         // $ A B D
2941         // if it has no links, dont even send out the line at all.
2942         char buffer[MAXBUF];
2943         sprintf(buffer,"$ %s",ServerName);
2944         bool sendit = false;
2945         for (int i = 0; i < 32; i++)
2946         {
2947                 if (me[i] != NULL)
2948                 {
2949                         for (int j = 0; j < me[i]->connectors.size(); j++)
2950                         {
2951                                 if (me[i]->connectors[j].GetState() != STATE_DISCONNECTED)
2952                                 {
2953                                         strncat(buffer," ",MAXBUF);
2954                                         strncat(buffer,me[i]->connectors[j].GetServerName().c_str(),MAXBUF);
2955                                         sendit = true;
2956                                 }
2957                         }
2958                 }
2959         }
2960         if (sendit)
2961                 NetSendToAll(buffer);
2962 }
2963
2964
2965 void DoSplit(const char* params)
2966 {
2967         bool go_again = true;
2968         while (go_again)
2969         {
2970                 go_again = false;
2971                 for (int i = 0; i < 32; i++)
2972                 {
2973                         if (me[i] != NULL)
2974                         {
2975                                 for (vector<ircd_connector>::iterator j = me[i]->connectors.begin(); j != me[i]->connectors.end(); j++)
2976                                 {
2977                                         if (!strcasecmp(j->GetServerName().c_str(),params))
2978                                         {
2979                                                 j->routes.clear();
2980                                                 j->CloseConnection();
2981                                                 me[i]->connectors.erase(j);
2982                                                 go_again = true;
2983                                                 break;
2984                                         }
2985                                 }
2986                         }
2987                 }
2988         }
2989         log(DEBUG,"Removed server. Will remove clients...");
2990         // iterate through the userlist and remove all users on this server.
2991         // because we're dealing with a mesh, we dont have to deal with anything
2992         // "down-route" from this server (nice huh)
2993         go_again = true;
2994         char reason[MAXBUF];
2995         snprintf(reason,MAXBUF,"%s %s",ServerName,params);
2996         while (go_again)
2997         {
2998                 go_again = false;
2999                 for (user_hash::const_iterator u = clientlist.begin(); u != clientlist.end(); u++)
3000                 {
3001                         if (!strcasecmp(u->second->server,params))
3002                         {
3003                                 kill_link(u->second,reason);
3004                                 go_again = true;
3005                                 break;
3006                         }
3007                 }
3008         }
3009 }
3010
3011 // removes a server. Will NOT remove its users!
3012
3013 void RemoveServer(const char* name)
3014 {
3015         bool go_again = true;
3016         while (go_again)
3017         {
3018                 go_again = false;
3019                 for (int i = 0; i < 32; i++)
3020                 {
3021                         if (me[i] != NULL)
3022                         {
3023                                 for (vector<ircd_connector>::iterator j = me[i]->connectors.begin(); j != me[i]->connectors.end(); j++)
3024                                 {
3025                                         if (!strcasecmp(j->GetServerName().c_str(),name))
3026                                         {
3027                                                 j->routes.clear();
3028                                                 j->CloseConnection();
3029                                                 me[i]->connectors.erase(j);
3030                                                 go_again = true;
3031                                                 break;
3032                                         }
3033                                 }
3034                         }
3035                 }
3036         }
3037 }
3038
3039
3040 int reap_counter = 0;
3041
3042 int InspIRCd(void)
3043 {
3044         struct sockaddr_in client, server;
3045         char addrs[MAXBUF][255];
3046         int openSockfd[MAXSOCKS], incomingSockfd, result = TRUE;
3047         socklen_t length;
3048         int count = 0, scanDetectTrigger = TRUE, showBanner = FALSE;
3049         int selectResult = 0, selectResult2 = 0;
3050         char *temp, configToken[MAXBUF], stuff[MAXBUF], Addr[MAXBUF], Type[MAXBUF];
3051         char resolvedHost[MAXBUF];
3052         fd_set selectFds;
3053         struct timeval tv;
3054
3055         log_file = fopen("ircd.log","a+");
3056         if (!log_file)
3057         {
3058                 printf("ERROR: Could not write to logfile ircd.log, bailing!\n\n");
3059                 Exit(ERROR);
3060         }
3061
3062         log(DEBUG,"InspIRCd: startup: begin");
3063         log(DEBUG,"$Id$");
3064         if (geteuid() == 0)
3065         {
3066                 printf("WARNING!!! You are running an irc server as ROOT!!! DO NOT DO THIS!!!\n\n");
3067                 Exit(ERROR);
3068                 log(DEBUG,"InspIRCd: startup: not starting with UID 0!");
3069         }
3070         SetupCommandTable();
3071         log(DEBUG,"InspIRCd: startup: default command table set up");
3072         
3073         ReadConfig();
3074         if (strcmp(DieValue,"")) 
3075         { 
3076                 printf("WARNING: %s\n\n",DieValue);
3077                 exit(0); 
3078         }  
3079         log(DEBUG,"InspIRCd: startup: read config");
3080           
3081         int count2 = 0, count3 = 0;
3082
3083         for (count = 0; count < ConfValueEnum("bind",&config_f); count++)
3084         {
3085                 ConfValue("bind","port",count,configToken,&config_f);
3086                 ConfValue("bind","address",count,Addr,&config_f);
3087                 ConfValue("bind","type",count,Type,&config_f);
3088                 if (!strcmp(Type,"servers"))
3089                 {
3090                         char Default[MAXBUF];
3091                         strcpy(Default,"no");
3092                         ConfValue("bind","default",count,Default,&config_f);
3093                         if (strchr(Default,'y'))
3094                         {
3095                                 defaultRoute = count3;
3096                                 log(DEBUG,"InspIRCd: startup: binding '%s:%s' is default server route",Addr,configToken);
3097                         }
3098                         me[count3] = new serverrec(ServerName,100L,false);
3099                         me[count3]->CreateListener(Addr,atoi(configToken));
3100                         count3++;
3101                 }
3102                 else
3103                 {
3104                         ports[count2] = atoi(configToken);
3105                         strcpy(addrs[count2],Addr);
3106                         count2++;
3107                 }
3108                 log(DEBUG,"InspIRCd: startup: read binding %s:%s [%s] from config",Addr,configToken, Type);
3109         }
3110         portCount = count2;
3111         UDPportCount = count3;
3112           
3113         log(DEBUG,"InspIRCd: startup: read %d total client ports and %d total server ports",portCount,UDPportCount);
3114         
3115         log(DEBUG,"InspIRCd: startup: InspIRCd is now running!");
3116         
3117         printf("\n");
3118         
3119         /* BugFix By Craig! :p */
3120         count = 0;
3121         for (count2 = 0; count2 < ConfValueEnum("module",&config_f); count2++)
3122         {
3123                 char modfile[MAXBUF];
3124                 ConfValue("module","name",count2,configToken,&config_f);
3125                 sprintf(modfile,"%s/%s",MOD_PATH,configToken,&config_f);
3126                 printf("Loading module... \033[1;37m%s\033[0;37m\n",modfile);
3127                 log(DEBUG,"InspIRCd: startup: Loading module: %s",modfile);
3128                 /* If The File Doesnt exist, Trying to load it
3129                  * Will Segfault the IRCd.. So, check to see if
3130                  * it Exists, Before Proceeding. */
3131                 if (FileExists(modfile))
3132                 {
3133                         factory[count] = new ircd_module(modfile);
3134                         if (factory[count]->LastError())
3135                         {
3136                                 log(DEBUG,"Unable to load %s: %s",modfile,factory[count]->LastError());
3137                                 sprintf("Unable to load %s: %s\nExiting...\n",modfile,factory[count]->LastError());
3138                                 Exit(ERROR);
3139                         }
3140                         if (factory[count]->factory)
3141                         {
3142                                 modules[count] = factory[count]->factory->CreateModule();
3143                                 /* save the module and the module's classfactory, if
3144                                  * this isnt done, random crashes can occur :/ */
3145                                 module_names.push_back(modfile);        
3146                         }
3147                         else
3148                         {
3149                                 log(DEBUG,"Unable to load %s",modfile);
3150                                 sprintf("Unable to load %s\nExiting...\n",modfile);
3151                                 Exit(ERROR);
3152                         }
3153                         /* Increase the Count */
3154                         count++;
3155                 }
3156                 else
3157                 {
3158                         log(DEBUG,"InspIRCd: startup: Module Not Found %s",modfile);
3159                         printf("Module Not Found: \033[1;37m%s\033[0;37m, Skipping\n",modfile);
3160                 }
3161         }
3162         MODCOUNT = count - 1;
3163         log(DEBUG,"Total loaded modules: %d",MODCOUNT+1);
3164         
3165         printf("\nInspIRCd is now running!\n");
3166         
3167         startup_time = time(NULL);
3168           
3169         if (nofork)
3170         {
3171                 log(VERBOSE,"Not forking as -nofork was specified");
3172         }
3173         else
3174         {
3175                 if (DaemonSeed() == ERROR)
3176                 {
3177                         log(DEBUG,"InspIRCd: startup: can't daemonise");
3178                         printf("ERROR: could not go into daemon mode. Shutting down.\n");
3179                         Exit(ERROR);
3180                 }
3181         }
3182           
3183           
3184         /* setup select call */
3185         FD_ZERO(&selectFds);
3186         log(DEBUG,"InspIRCd: startup: zero selects");
3187         log(VERBOSE,"InspIRCd: startup: portCount = %d", portCount);
3188         
3189         for (count = 0; count < portCount; count++)
3190         {
3191                 if ((openSockfd[boundPortCount] = OpenTCPSocket()) == ERROR)
3192                 {
3193                         log(DEBUG,"InspIRCd: startup: bad fd %d",openSockfd[boundPortCount]);
3194                         return(ERROR);
3195                 }
3196                 if (BindSocket(openSockfd[boundPortCount],client,server,ports[count],addrs[count]) == ERROR)
3197                 {
3198                         log(DEBUG,"InspIRCd: startup: failed to bind port %d",ports[count]);
3199                 }
3200                 else    /* well we at least bound to one socket so we'll continue */
3201                 {
3202                         boundPortCount++;
3203                 }
3204         }
3205         
3206         log(DEBUG,"InspIRCd: startup: total bound ports %d",boundPortCount);
3207           
3208         /* if we didn't bind to anything then abort */
3209         if (boundPortCount == 0)
3210         {
3211                 log(DEBUG,"InspIRCd: startup: no ports bound, bailing!");
3212                 return (ERROR);
3213         }
3214         
3215
3216         length = sizeof (client);
3217         char udp_msg[MAXBUF], tcp_host[MAXBUF];
3218           
3219         /* main loop, this never returns */
3220         for (;;)
3221         {
3222 #ifdef _POSIX_PRIORITY_SCHEDULING
3223                 sched_yield();
3224 #endif
3225                 // update the status of klines, etc
3226                 expire_lines();
3227
3228                 fd_set sfd;
3229                 timeval tval;
3230                 FD_ZERO(&sfd);
3231
3232                 user_hash::iterator count2 = clientlist.begin();
3233
3234                 // *FIX* Instead of closing sockets in kill_link when they receive the ERROR :blah line, we should queue
3235                 // them in a list, then reap the list every second or so.
3236                 if (reap_counter>300)
3237                 {
3238                         if (fd_reap.size() > 0)
3239                         {
3240                                 for( int n = 0; n < fd_reap.size(); n++)
3241                                 {
3242                                         Blocking(fd_reap[n]);
3243                                         close(fd_reap[n]);
3244                                         NonBlocking(fd_reap[n]);
3245                                 }
3246                         }
3247                         fd_reap.clear();
3248                         reap_counter=0;
3249                 }
3250                 reap_counter++;
3251
3252                 fd_set serverfds;
3253                 FD_ZERO(&serverfds);
3254                 timeval tvs;
3255                 
3256                 for (int x = 0; x != UDPportCount; x++)
3257                 {
3258                         FD_SET(me[x]->fd, &serverfds);
3259                 }
3260                 
3261                 tvs.tv_usec = 0;                
3262                 tvs.tv_sec = 0;
3263                 
3264                 int servresult = select(32767, &serverfds, NULL, NULL, &tvs);
3265                 if (servresult > 0)
3266                 {
3267                         for (int x = 0; x != UDPportCount; x++)
3268                         {
3269                                 if (FD_ISSET (me[x]->fd, &serverfds))
3270                                 {
3271                                         char remotehost[MAXBUF],resolved[MAXBUF];
3272                                         length = sizeof (client);
3273                                         incomingSockfd = accept (me[x]->fd, (sockaddr *) &client, &length);
3274                                         strncpy(remotehost,(char *)inet_ntoa(client.sin_addr),MAXBUF);
3275                                         if(CleanAndResolve(resolved, remotehost) != TRUE)
3276                                         {
3277                                                 strncpy(resolved,remotehost,MAXBUF);
3278                                         }
3279                                         // add to this connections ircd_connector vector
3280                                         // *FIX* - we need the LOCAL port not the remote port in &client!
3281                                         me[x]->AddIncoming(incomingSockfd,resolved,me[x]->port);
3282                                 }
3283                         }
3284                 }
3285      
3286                 for (int x = 0; x < UDPportCount; x++)
3287                 {
3288                         std::deque<std::string> msgs;
3289                         msgs.clear();
3290                         if (me[x]->RecvPacket(msgs, tcp_host))
3291                         {
3292                                 for (int ctr = 0; ctr < msgs.size(); ctr++)
3293                                 {
3294                                         char udp_msg[MAXBUF];
3295                                         strncpy(udp_msg,msgs[ctr].c_str(),MAXBUF);
3296                                         if (strlen(udp_msg)<1)
3297                                         {
3298                                                 log(DEBUG,"Invalid string from %s [route%d]",tcp_host,x);
3299                                                 break;
3300                                         }
3301                                         // during a netburst, send all data to all other linked servers
3302                                         if ((((nb_start>0) && (udp_msg[0] != 'Y') && (udp_msg[0] != 'X') && (udp_msg[0] != 'F'))) || (is_uline(tcp_host)))
3303                                         {
3304                                                 if (is_uline(tcp_host))
3305                                                 {
3306                                                         if ((udp_msg[0] != 'Y') && (udp_msg[0] != 'X') && (udp_msg[0] != 'F'))
3307                                                         {
3308                                                                 NetSendToAllExcept(tcp_host,udp_msg);
3309                                                         }
3310                                                 }
3311                                                 else
3312                                                         NetSendToAllExcept(tcp_host,udp_msg);
3313                                         }
3314                                         FOREACH_MOD OnPacketReceive(udp_msg);
3315                                         handle_link_packet(udp_msg, tcp_host, me[x]);
3316                                 }
3317                                 goto label;
3318                         }
3319                 }
3320         
3321
3322         while (count2 != clientlist.end())
3323         {
3324                 char data[10240];
3325                 tval.tv_usec = tval.tv_sec = 0;
3326                 FD_ZERO(&sfd);
3327                 int total_in_this_set = 0;
3328
3329                 user_hash::iterator xcount = count2;
3330                 user_hash::iterator endingiter = count2;
3331
3332                 if (!count2->second) break;
3333                 
3334                 if (count2->second)
3335                 if (count2->second->fd != 0)
3336                 {
3337                         // assemble up to 64 sockets into an fd_set
3338                         // to implement a pooling mechanism.
3339                         //
3340                         // This should be up to 64x faster than the
3341                         // old implementation.
3342                         while (total_in_this_set < 64)
3343                         {
3344                                 if (count2 != clientlist.end())
3345                                 {
3346                                         // we don't check the state of remote users.
3347                                         if (count2->second->fd > 0)
3348                                         {
3349                                                 FD_SET (count2->second->fd, &sfd);
3350
3351                                                 // registration timeout -- didnt send USER/NICK/HOST in the time specified in
3352                                                 // their connection class.
3353                                                 if ((time(NULL) > count2->second->timeout) && (count2->second->registered != 7)) 
3354                                                 {
3355                                                         log(DEBUG,"InspIRCd: registration timeout: %s",count2->second->nick);
3356                                                         kill_link(count2->second,"Registration timeout");
3357                                                         goto label;
3358                                                 }
3359                                                 if (((time(NULL)) > count2->second->nping) && (isnick(count2->second->nick)) && (count2->second->registered == 7))
3360                                                 {
3361                                                         if ((!count2->second->lastping) && (count2->second->registered == 7))
3362                                                         {
3363                                                                 log(DEBUG,"InspIRCd: ping timeout: %s",count2->second->nick);
3364                                                                 kill_link(count2->second,"Ping timeout");
3365                                                                 goto label;
3366                                                         }
3367                                                         Write(count2->second->fd,"PING :%s",ServerName);
3368                                                         log(DEBUG,"InspIRCd: pinging: %s",count2->second->nick);
3369                                                         count2->second->lastping = 0;
3370                                                         count2->second->nping = time(NULL)+120;
3371                                                 }
3372                                         }
3373                                         count2++;
3374                                         total_in_this_set++;
3375                                 }
3376                                 else break;
3377                         }
3378    
3379                         endingiter = count2;
3380                         count2 = xcount; // roll back to where we were
3381         
3382                         int v = 0;
3383
3384                         tval.tv_usec = 0;
3385                         tval.tv_sec = 0;
3386                         selectResult2 = select(65535, &sfd, NULL, NULL, &tval);
3387                         
3388                         // now loop through all of the items in this pool if any are waiting
3389                         //if (selectResult2 > 0)
3390                         for (user_hash::iterator count2a = xcount; count2a != endingiter; count2a++)
3391                         {
3392
3393 #ifdef _POSIX_PRIORITY_SCHEDULING
3394                                 sched_yield();
3395 #endif
3396
3397                                 result = EAGAIN;
3398                                 if ((count2a->second->fd != -1) && (FD_ISSET (count2a->second->fd, &sfd)))
3399                                 {
3400                                         log(DEBUG,"Reading fd %d",count2a->second->fd);
3401                                         memset(data, 0, 10240);
3402                                         result = read(count2a->second->fd, data, 10240);
3403                                         
3404                                         if (result)
3405                                         {
3406                                                 if (result > 0)
3407                                                         log(DEBUG,"Read %d characters from socket",result);
3408                                                 userrec* current = count2a->second;
3409                                                 int currfd = current->fd;
3410                                                 char* l = strtok(data,"\n");
3411                                                 int floodlines = 0;
3412                                                 while (l)
3413                                                 {
3414                                                         floodlines++;
3415                                                         if ((floodlines > current->flood) && (current->flood != 0))
3416                                                         {
3417                                                                 log(DEFAULT,"Excess flood from: %s!%s@%s",current->nick,current->ident,current->host);
3418                                                                 WriteOpers("*** Excess flood from: %s!%s@%s",current->nick,current->ident,current->host);
3419                                                                 kill_link(current,"Excess flood");
3420                                                                 goto label;
3421                                                         }
3422                                                         char sanitized[NetBufferSize];
3423                                                         memset(sanitized, 0, NetBufferSize);
3424                                                         int ptt = 0;
3425                                                         for (int pt = 0; pt < strlen(l); pt++)
3426                                                         {
3427                                                                 if (l[pt] != '\r')
3428                                                                 {
3429                                                                         sanitized[ptt++] = l[pt];
3430                                                                 }
3431                                                         }
3432                                                         sanitized[ptt] = '\0';
3433                                                         if (strlen(sanitized))
3434                                                         {
3435
3436
3437                                                                 // we're gonna re-scan to check if the nick is gone, after every
3438                                                                 // command - if it has, we're gonna bail
3439                                                                 bool find_again = false;
3440                                                                 process_buffer(sanitized,current);
3441         
3442                                                                 // look for the user's record in case it's changed
3443                                                                 for (user_hash::iterator c2 = clientlist.begin(); c2 != clientlist.end(); c2++)
3444                                                                 {
3445                                                                         if (c2->second->fd == currfd)
3446                                                                         {
3447                                                                                 // found again, update pointer
3448                                                                                 current == c2->second;
3449                                                                                 find_again = true;
3450                                                                                 break;
3451                                                                         }
3452                                                                 }
3453                                                                 if (!find_again)
3454                                                                         goto label;
3455
3456                                                         }
3457                                                         l = strtok(NULL,"\n");
3458                                                 }
3459                                                 goto label;
3460                                         }
3461
3462                                         if ((result == -1) && (errno != EAGAIN) && (errno != EINTR))
3463                                         {
3464                                                 log(DEBUG,"killing: %s",count2a->second->nick);
3465                                                 kill_link(count2a->second,strerror(errno));
3466                                                 goto label;
3467                                         }
3468                                 }
3469                                 // result EAGAIN means nothing read
3470                                 if (result == EAGAIN)
3471                                 {
3472                                 }
3473                                 else
3474                                 if (result == 0)
3475                                 {
3476                                         if (count2->second)
3477                                         {
3478                                                 log(DEBUG,"InspIRCd: Exited: %s",count2a->second->nick);
3479                                                 kill_link(count2a->second,"Client exited");
3480                                                 // must bail here? kill_link removes the hash, corrupting the iterator
3481                                                 log(DEBUG,"Bailing from client exit");
3482                                                 goto label;
3483                                         }
3484                                 }
3485                                 else if (result > 0)
3486                                 {
3487                                 }
3488                         }
3489                 }
3490                 for (int q = 0; q < total_in_this_set; q++)
3491                 {
3492                         // there is no iterator += operator :(
3493                         //if (count2 != clientlist.end())
3494                         //{
3495                                 count2++;
3496                         //}
3497                 }
3498         }
3499         
3500         // set up select call
3501         for (count = 0; count < boundPortCount; count++)
3502         {
3503                 FD_SET (openSockfd[count], &selectFds);
3504         }
3505
3506         tv.tv_usec = 1;
3507         selectResult = select(MAXSOCKS, &selectFds, NULL, NULL, &tv);
3508
3509         /* select is reporting a waiting socket. Poll them all to find out which */
3510         if (selectResult > 0)
3511         {
3512                 char target[MAXBUF], resolved[MAXBUF];
3513                 for (count = 0; count < boundPortCount; count++)                
3514                 {
3515                         if (FD_ISSET (openSockfd[count], &selectFds))
3516                         {
3517                                 length = sizeof (client);
3518                                 incomingSockfd = accept (openSockfd[count], (struct sockaddr *) &client, &length);
3519                               
3520                                 address_cache::iterator iter = IP.find(client.sin_addr);
3521                                 bool iscached = false;
3522                                 if (iter == IP.end())
3523                                 {
3524                                         /* ip isn't in cache, add it */
3525                                         strncpy (target, (char *) inet_ntoa (client.sin_addr), MAXBUF);
3526                                         if(CleanAndResolve(resolved, target) != TRUE)
3527                                         {
3528                                                 strncpy(resolved,target,MAXBUF);
3529                                         }
3530                                         /* hostname now in 'target' */
3531                                         IP[client.sin_addr] = new string(resolved);
3532                                         /* hostname in cache */
3533                                 }
3534                                 else
3535                                 {
3536                                         /* found ip (cached) */
3537                                         strncpy(resolved, iter->second->c_str(), MAXBUF);
3538                                         iscached = true;
3539                                 }
3540                         
3541                                 if (incomingSockfd < 0)
3542                                 {
3543                                         WriteOpers("*** WARNING: Accept failed on port %d (%s)", ports[count],target);
3544                                         log(DEBUG,"InspIRCd: accept failed: %d",ports[count]);
3545                                 }
3546                                 else
3547                                 {
3548                                         AddClient(incomingSockfd, resolved, ports[count], iscached, target);
3549                                         log(DEBUG,"InspIRCd: adding client on port %d fd=%d",ports[count],incomingSockfd);
3550                                 }
3551                                 goto label;
3552                         }
3553                 }
3554         }
3555         label:
3556         if(0) {}; // "Label must be followed by a statement"... so i gave it one.
3557 }
3558 /* not reached */
3559 close (incomingSockfd);
3560 }
3561