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