]> git.netwichtig.de Git - user/henk/code/inspircd.git/blob - src/inspircd.cpp
1b7e552728ce8a5f3116c85ccffbf19f6f61d59d
[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 #ifdef USE_KQUEUE
31 #include <sys/types.h>
32 #include <sys/event.h>
33 #include <sys/time.h>
34 #endif
35 #include <cstdio>
36 #include <time.h>
37 #include <string>
38 #ifdef GCC3
39 #include <ext/hash_map>
40 #else
41 #include <hash_map>
42 #endif
43 #include <map>
44 #include <sstream>
45 #include <vector>
46 #include <errno.h>
47 #include <deque>
48 #include <errno.h>
49 #include <unistd.h>
50 #include <sched.h>
51 #include "connection.h"
52 #include "users.h"
53 #include "servers.h"
54 #include "ctables.h"
55 #include "globals.h"
56 #include "modules.h"
57 #include "dynamic.h"
58 #include "wildcard.h"
59 #include "message.h"
60 #include "mode.h"
61 #include "commands.h"
62 #include "xline.h"
63 #include "inspstring.h"
64 #include "dnsqueue.h"
65
66 #ifdef GCC3
67 #define nspace __gnu_cxx
68 #else
69 #define nspace std
70 #endif
71
72 int LogLevel = DEFAULT;
73 char ServerName[MAXBUF];
74 char Network[MAXBUF];
75 char ServerDesc[MAXBUF];
76 char AdminName[MAXBUF];
77 char AdminEmail[MAXBUF];
78 char AdminNick[MAXBUF];
79 char diepass[MAXBUF];
80 char restartpass[MAXBUF];
81 char motd[MAXBUF];
82 char rules[MAXBUF];
83 char list[MAXBUF];
84 char PrefixQuit[MAXBUF];
85 char DieValue[MAXBUF];
86 char DNSServer[MAXBUF];
87 int debugging =  0;
88 int WHOWAS_STALE = 48; // default WHOWAS Entries last 2 days before they go 'stale'
89 int WHOWAS_MAX = 100;  // default 100 people maximum in the WHOWAS list
90 int DieDelay  =  5;
91 time_t startup_time = time(NULL);
92 int NetBufferSize = 10240; // NetBufferSize used as the buffer size for all read() ops
93 extern int MaxWhoResults;
94 time_t nb_start = 0;
95 int dns_timeout = 5;
96
97 char DisabledCommands[MAXBUF];
98
99 bool AllowHalfop = true;
100 bool AllowProtect = true;
101 bool AllowFounder = true;
102
103 extern std::vector<Module*> modules;
104 std::vector<std::string> module_names;
105 extern std::vector<ircd_module*> factory;
106
107 extern int MODCOUNT;
108 int openSockfd[MAXSOCKS];
109 bool nofork = false;
110 bool unlimitcore = false;
111
112 time_t TIME = time(NULL), OLDTIME = time(NULL);
113
114 #ifdef USE_KQUEUE
115 int kq, lkq, skq;
116 #endif
117
118 namespace nspace
119 {
120 #ifdef GCC34
121         template<> struct hash<in_addr>
122 #else
123         template<> struct nspace::hash<in_addr>
124 #endif
125         {
126                 size_t operator()(const struct in_addr &a) const
127                 {
128                         size_t q;
129                         memcpy(&q,&a,sizeof(size_t));
130                         return q;
131                 }
132         };
133 #ifdef GCC34
134         template<> struct hash<string>
135 #else
136         template<> struct nspace::hash<string>
137 #endif
138         {
139                 size_t operator()(const string &s) const
140                 {
141                         char a[MAXBUF];
142                         static struct hash<const char *> strhash;
143                         strlcpy(a,s.c_str(),MAXBUF);
144                         strlower(a);
145                         return strhash(a);
146                 }
147         };
148 }
149
150
151 struct StrHashComp
152 {
153
154         bool operator()(const string& s1, const string& s2) const
155         {
156                 char a[MAXBUF],b[MAXBUF];
157                 strlcpy(a,s1.c_str(),MAXBUF);
158                 strlcpy(b,s2.c_str(),MAXBUF);
159                 strlower(a);
160                 strlower(b);
161                 return (strcasecmp(a,b) == 0);
162         }
163
164 };
165
166 struct InAddr_HashComp
167 {
168
169         bool operator()(const in_addr &s1, const in_addr &s2) const
170         {
171                 size_t q;
172                 size_t p;
173                 
174                 memcpy(&q,&s1,sizeof(size_t));
175                 memcpy(&p,&s2,sizeof(size_t));
176                 
177                 return (q == p);
178         }
179
180 };
181
182
183 typedef nspace::hash_map<std::string, userrec*, nspace::hash<string>, StrHashComp> user_hash;
184 typedef nspace::hash_map<std::string, chanrec*, nspace::hash<string>, StrHashComp> chan_hash;
185 typedef nspace::hash_map<in_addr,string*, nspace::hash<in_addr>, InAddr_HashComp> address_cache;
186 typedef nspace::hash_map<std::string, WhoWasUser*, nspace::hash<string>, StrHashComp> whowas_hash;
187 typedef std::deque<command_t> command_table;
188
189 // This table references users by file descriptor.
190 // its an array to make it VERY fast, as all lookups are referenced
191 // by an integer, meaning there is no need for a scan/search operation.
192 userrec* fd_ref_table[65536];
193
194 int statsAccept = 0, statsRefused = 0, statsUnknown = 0, statsCollisions = 0, statsDns = 0, statsDnsGood = 0, statsDnsBad = 0, statsConnects = 0, statsSent= 0, statsRecv = 0;
195
196 serverrec* me[32];
197
198 FILE *log_file;
199
200 user_hash clientlist;
201 chan_hash chanlist;
202 whowas_hash whowas;
203 command_table cmdlist;
204 file_cache MOTD;
205 file_cache RULES;
206 address_cache IP;
207
208 ClassVector Classes;
209
210 struct linger linger = { 0 };
211 char MyExecutable[1024];
212 int boundPortCount = 0;
213 int portCount = 0, SERVERportCount = 0, ports[MAXSOCKS];
214 int defaultRoute = 0;
215 char ModPath[MAXBUF];
216
217 /* prototypes */
218
219 int has_channel(userrec *u, chanrec *c);
220 int usercount(chanrec *c);
221 int usercount_i(chanrec *c);
222 char* Passwd(userrec *user);
223 bool IsDenied(userrec *user);
224 void AddWhoWas(userrec* u);
225
226 std::vector<long> auth_cookies;
227 std::stringstream config_f(stringstream::in | stringstream::out);
228
229 std::vector<userrec*> all_opers;
230
231 static char already_sent[65536];
232
233 char lowermap[255];
234
235 void AddOper(userrec* user)
236 {
237         log(DEBUG,"Oper added to optimization list");
238         all_opers.push_back(user);
239 }
240
241 void DeleteOper(userrec* user)
242 {
243         for (std::vector<userrec*>::iterator a = all_opers.begin(); a < all_opers.end(); a++)
244         {
245                 if (*a == user)
246                 {
247                         log(DEBUG,"Oper removed from optimization list");
248                         all_opers.erase(a);
249                         return;
250                 }
251         }
252 }
253
254 long GetRevision()
255 {
256         char Revision[] = "$Revision$";
257         char *s1 = Revision;
258         char *savept;
259         char *v2 = strtok_r(s1," ",&savept);
260         s1 = savept;
261         v2 = strtok_r(s1," ",&savept);
262         s1 = savept;
263         return (long)(atof(v2)*10000);
264 }
265
266
267 std::string getservername()
268 {
269         return ServerName;
270 }
271
272 std::string getserverdesc()
273 {
274         return ServerDesc;
275 }
276
277 std::string getnetworkname()
278 {
279         return Network;
280 }
281
282 std::string getadminname()
283 {
284         return AdminName;
285 }
286
287 std::string getadminemail()
288 {
289         return AdminEmail;
290 }
291
292 std::string getadminnick()
293 {
294         return AdminNick;
295 }
296
297 void log(int level,char *text, ...)
298 {
299         char textbuffer[MAXBUF];
300         va_list argsPtr;
301         time_t rawtime;
302         struct tm * timeinfo;
303         if (level < LogLevel)
304                 return;
305
306         time(&rawtime);
307         timeinfo = localtime (&rawtime);
308
309         if (log_file)
310         {
311                 char b[MAXBUF];
312                 va_start (argsPtr, text);
313                 vsnprintf(textbuffer, MAXBUF, text, argsPtr);
314                 va_end(argsPtr);
315                 strlcpy(b,asctime(timeinfo),MAXBUF);
316                 b[24] = ':';    // we know this is the end of the time string
317                 fprintf(log_file,"%s %s\n",b,textbuffer);
318                 if (nofork)
319                 {
320                         // nofork enabled? display it on terminal too
321                         printf("%s %s\n",b,textbuffer);
322                 }
323         }
324 }
325
326 void readfile(file_cache &F, const char* fname)
327 {
328         FILE* file;
329         char linebuf[MAXBUF];
330         
331         log(DEBUG,"readfile: loading %s",fname);
332         F.clear();
333         file =  fopen(fname,"r");
334         if (file)
335         {
336                 while (!feof(file))
337                 {
338                         fgets(linebuf,sizeof(linebuf),file);
339                         linebuf[strlen(linebuf)-1]='\0';
340                         if (linebuf[0] == 0)
341                         {
342                                 strcpy(linebuf,"  ");
343                         }
344                         if (!feof(file))
345                         {
346                                 F.push_back(linebuf);
347                         }
348                 }
349                 fclose(file);
350         }
351         else
352         {
353                 log(DEBUG,"readfile: failed to load file: %s",fname);
354         }
355         log(DEBUG,"readfile: loaded %s, %lu lines",fname,(unsigned long)F.size());
356 }
357
358 void ReadConfig(bool bail, userrec* user)
359 {
360         char dbg[MAXBUF],pauseval[MAXBUF],Value[MAXBUF],timeout[MAXBUF],NB[MAXBUF],flood[MAXBUF],MW[MAXBUF];
361         char AH[MAXBUF],AP[MAXBUF],AF[MAXBUF],DNT[MAXBUF],pfreq[MAXBUF],thold[MAXBUF],sqmax[MAXBUF],rqmax[MAXBUF];
362         ConnectClass c;
363         std::stringstream errstr;
364         
365         if (!LoadConf(CONFIG_FILE,&config_f,&errstr))
366         {
367                 errstr.seekg(0);
368                 if (bail)
369                 {
370                         printf("There were errors in your configuration:\n%s",errstr.str().c_str());
371                         Exit(0);
372                 }
373                 else
374                 {
375                         char dataline[1024];
376                         if (user)
377                         {
378                                 WriteServ(user->fd,"NOTICE %s :There were errors in the configuration file:",user->nick);
379                                 while (!errstr.eof())
380                                 {
381                                         errstr.getline(dataline,1024);
382                                         WriteServ(user->fd,"NOTICE %s :%s",user->nick,dataline);
383                                 }
384                         }
385                         else
386                         {
387                                 WriteOpers("There were errors in the configuration file:",user->nick);
388                                 while (!errstr.eof())
389                                 {
390                                         errstr.getline(dataline,1024);
391                                         WriteOpers(dataline);
392                                 }
393                         }
394                         return;
395                 }
396         }
397           
398         ConfValue("server","name",0,ServerName,&config_f);
399         ConfValue("server","description",0,ServerDesc,&config_f);
400         ConfValue("server","network",0,Network,&config_f);
401         ConfValue("admin","name",0,AdminName,&config_f);
402         ConfValue("admin","email",0,AdminEmail,&config_f);
403         ConfValue("admin","nick",0,AdminNick,&config_f);
404         ConfValue("files","motd",0,motd,&config_f);
405         ConfValue("files","rules",0,rules,&config_f);
406         ConfValue("power","diepass",0,diepass,&config_f);
407         ConfValue("power","pause",0,pauseval,&config_f);
408         ConfValue("power","restartpass",0,restartpass,&config_f);
409         ConfValue("options","prefixquit",0,PrefixQuit,&config_f);
410         ConfValue("die","value",0,DieValue,&config_f);
411         ConfValue("options","loglevel",0,dbg,&config_f);
412         ConfValue("options","netbuffersize",0,NB,&config_f);
413         ConfValue("options","maxwho",0,MW,&config_f);
414         ConfValue("options","allowhalfop",0,AH,&config_f);
415         ConfValue("options","allowprotect",0,AP,&config_f);
416         ConfValue("options","allowfounder",0,AF,&config_f);
417         ConfValue("dns","server",0,DNSServer,&config_f);
418         ConfValue("dns","timeout",0,DNT,&config_f);
419         ConfValue("options","moduledir",0,ModPath,&config_f);
420         ConfValue("disabled","commands",0,DisabledCommands,&config_f);
421
422         NetBufferSize = atoi(NB);
423         MaxWhoResults = atoi(MW);
424         dns_timeout = atoi(DNT);
425         if (!dns_timeout)
426                 dns_timeout = 5;
427         if (!DNSServer[0])
428                 strlcpy(DNSServer,"127.0.0.1",MAXBUF);
429         if (!ModPath[0])
430                 strlcpy(ModPath,MOD_PATH,MAXBUF);
431         AllowHalfop = ((!strcasecmp(AH,"true")) || (!strcasecmp(AH,"1")) || (!strcasecmp(AH,"yes")));
432         AllowProtect = ((!strcasecmp(AP,"true")) || (!strcasecmp(AP,"1")) || (!strcasecmp(AP,"yes")));
433         AllowFounder = ((!strcasecmp(AF,"true")) || (!strcasecmp(AF,"1")) || (!strcasecmp(AF,"yes")));
434         if ((!NetBufferSize) || (NetBufferSize > 65535) || (NetBufferSize < 1024))
435         {
436                 log(DEFAULT,"No NetBufferSize specified or size out of range, setting to default of 10240.");
437                 NetBufferSize = 10240;
438         }
439         if ((!MaxWhoResults) || (MaxWhoResults > 65535) || (MaxWhoResults < 1))
440         {
441                 log(DEFAULT,"No MaxWhoResults specified or size out of range, setting to default of 128.");
442                 MaxWhoResults = 128;
443         }
444         if (!strcmp(dbg,"debug"))
445                 LogLevel = DEBUG;
446         if (!strcmp(dbg,"verbose"))
447                 LogLevel = VERBOSE;
448         if (!strcmp(dbg,"default"))
449                 LogLevel = DEFAULT;
450         if (!strcmp(dbg,"sparse"))
451                 LogLevel = SPARSE;
452         if (!strcmp(dbg,"none"))
453                 LogLevel = NONE;
454         readfile(MOTD,motd);
455         log(DEFAULT,"Reading message of the day...");
456         readfile(RULES,rules);
457         log(DEFAULT,"Reading connect classes...");
458         Classes.clear();
459         for (int i = 0; i < ConfValueEnum("connect",&config_f); i++)
460         {
461                 strcpy(Value,"");
462                 ConfValue("connect","allow",i,Value,&config_f);
463                 ConfValue("connect","timeout",i,timeout,&config_f);
464                 ConfValue("connect","flood",i,flood,&config_f);
465                 ConfValue("connect","pingfreq",i,pfreq,&config_f);
466                 ConfValue("connect","threshold",i,thold,&config_f);
467                 ConfValue("connect","sendq",i,sqmax,&config_f);
468                 ConfValue("connect","recvq",i,rqmax,&config_f);
469                 if (Value[0])
470                 {
471                         strlcpy(c.host,Value,MAXBUF);
472                         c.type = CC_ALLOW;
473                         strlcpy(Value,"",MAXBUF);
474                         ConfValue("connect","password",i,Value,&config_f);
475                         strlcpy(c.pass,Value,MAXBUF);
476                         c.registration_timeout = 90; // default is 2 minutes
477                         c.pingtime = 120;
478                         c.flood = atoi(flood);
479                         c.threshold = 5;
480                         c.sendqmax = 262144; // 256k
481                         c.recvqmax = 4096;   // 4k
482                         if (atoi(thold)>0)
483                         {
484                                 c.threshold = atoi(thold);
485                         }
486                         if (atoi(sqmax)>0)
487                         {
488                                 c.sendqmax = atoi(sqmax);
489                         }
490                         if (atoi(rqmax)>0)
491                         {
492                                 c.recvqmax = atoi(rqmax);
493                         }
494                         if (atoi(timeout)>0)
495                         {
496                                 c.registration_timeout = atoi(timeout);
497                         }
498                         if (atoi(pfreq)>0)
499                         {
500                                 c.pingtime = atoi(pfreq);
501                         }
502                         Classes.push_back(c);
503                         log(DEBUG,"Read connect class type ALLOW, host=%s password=%s timeout=%lu flood=%lu",c.host,c.pass,(unsigned long)c.registration_timeout,(unsigned long)c.flood);
504                 }
505                 else
506                 {
507                         ConfValue("connect","deny",i,Value,&config_f);
508                         strlcpy(c.host,Value,MAXBUF);
509                         c.type = CC_DENY;
510                         Classes.push_back(c);
511                         log(DEBUG,"Read connect class type DENY, host=%s",c.host);
512                 }
513         
514         }
515         log(DEFAULT,"Reading K lines,Q lines and Z lines from config...");
516         read_xline_defaults();
517         log(DEFAULT,"Applying K lines, Q lines and Z lines...");
518         apply_lines();
519         log(DEFAULT,"Done reading configuration file, InspIRCd is now starting.");
520         if (!bail)
521         {
522                 log(DEFAULT,"Adding and removing modules due to rehash...");
523
524                 std::vector<std::string> old_module_names, new_module_names, added_modules, removed_modules;
525
526                 // store the old module names
527                 for (std::vector<std::string>::iterator t = module_names.begin(); t != module_names.end(); t++)
528                 {
529                         old_module_names.push_back(*t);
530                 }
531
532                 // get the new module names
533                 for (int count2 = 0; count2 < ConfValueEnum("module",&config_f); count2++)
534                 {
535                         ConfValue("module","name",count2,Value,&config_f);
536                         new_module_names.push_back(Value);
537                 }
538
539                 // now create a list of new modules that are due to be loaded
540                 // and a seperate list of modules which are due to be unloaded
541                 for (std::vector<std::string>::iterator _new = new_module_names.begin(); _new != new_module_names.end(); _new++)
542                 {
543                         bool added = true;
544                         for (std::vector<std::string>::iterator old = old_module_names.begin(); old != old_module_names.end(); old++)
545                         {
546                                 if (*old == *_new)
547                                         added = false;
548                         }
549                         if (added)
550                                 added_modules.push_back(*_new);
551                 }
552                 for (std::vector<std::string>::iterator oldm = old_module_names.begin(); oldm != old_module_names.end(); oldm++)
553                 {
554                         bool removed = true;
555                         for (std::vector<std::string>::iterator newm = new_module_names.begin(); newm != new_module_names.end(); newm++)
556                         {
557                                 if (*newm == *oldm)
558                                         removed = false;
559                         }
560                         if (removed)
561                                 removed_modules.push_back(*oldm);
562                 }
563                 // now we have added_modules, a vector of modules to be loaded, and removed_modules, a vector of modules
564                 // to be removed.
565                 int rem = 0, add = 0;
566                 if (!removed_modules.empty())
567                 for (std::vector<std::string>::iterator removing = removed_modules.begin(); removing != removed_modules.end(); removing++)
568                 {
569                         if (UnloadModule(removing->c_str()))
570                         {
571                                 WriteOpers("*** REHASH UNLOADED MODULE: %s",removing->c_str());
572                                 WriteServ(user->fd,"973 %s %s :Module %s successfully unloaded.",user->nick, removing->c_str(), removing->c_str());
573                                 rem++;
574                         }
575                         else
576                         {
577                                 WriteServ(user->fd,"972 %s %s :Failed to unload module %s: %s",user->nick, removing->c_str(), removing->c_str(), ModuleError());
578                         }
579                 }
580                 if (!added_modules.empty())
581                 for (std::vector<std::string>::iterator adding = added_modules.begin(); adding != added_modules.end(); adding++)
582                 {
583                         if (LoadModule(adding->c_str()))
584                         {
585                                 WriteOpers("*** REHASH LOADED MODULE: %s",adding->c_str());
586                                 WriteServ(user->fd,"975 %s %s :Module %s successfully loaded.",user->nick, adding->c_str(), adding->c_str());
587                                 add++;
588                         }
589                         else
590                         {
591                                 WriteServ(user->fd,"974 %s %s :Failed to load module %s: %s",user->nick, adding->c_str(), adding->c_str(), ModuleError());
592                         }
593                 }
594                 log(DEFAULT,"Successfully unloaded %lu of %lu modules and loaded %lu of %lu modules.",(unsigned long)rem,(unsigned long)removed_modules.size(),(unsigned long)add,(unsigned long)added_modules.size());
595         }
596 }
597
598 /* write formatted text to a socket, in same format as printf
599  * New in 1.0 Beta 5 - Nothing is written directly to a users fd any more.
600  * Instead, data builds up in the users sendq and each time around the mainloop
601  * this data is flushed to the user's socket (see userrec::FlushWriteBuf).
602  */
603
604 void Write(int sock,char *text, ...)
605 {
606         if (sock < 0)
607                 return;
608         if (!text)
609         {
610                 log(DEFAULT,"*** BUG *** Write was given an invalid parameter");
611                 return;
612         }
613         char textbuffer[MAXBUF];
614         va_list argsPtr;
615         char tb[MAXBUF];
616         int res;
617         
618         va_start (argsPtr, text);
619         vsnprintf(textbuffer, MAXBUF, text, argsPtr);
620         va_end(argsPtr);
621         int bytes = snprintf(tb,MAXBUF,"%s\r\n",textbuffer);
622         chop(tb);
623         if (fd_ref_table[sock])
624         {
625                 int MOD_RESULT = 0;
626                 FOREACH_RESULT(OnRawSocketWrite(sock,tb,bytes));
627                 fd_ref_table[sock]->AddWriteBuf(tb);
628                 statsSent += bytes;
629         }
630         else log(DEFAULT,"ERROR! attempted write to a user with no fd_ref_table entry!!!");
631 }
632
633 /* write a server formatted numeric response to a single socket */
634
635 void WriteServ(int sock, char* text, ...)
636 {
637         if (sock < 0)
638                 return;
639         if (!text)
640         {
641                 log(DEFAULT,"*** BUG *** WriteServ was given an invalid parameter");
642                 return;
643         }
644         char textbuffer[MAXBUF],tb[MAXBUF];
645         int res;
646         va_list argsPtr;
647         va_start (argsPtr, text);
648         
649         vsnprintf(textbuffer, MAXBUF, text, argsPtr);
650         va_end(argsPtr);
651         int bytes = snprintf(tb,MAXBUF,":%s %s\r\n",ServerName,textbuffer);
652         chop(tb);
653         if (fd_ref_table[sock])
654         {
655                 int MOD_RESULT = 0;
656                 FOREACH_RESULT(OnRawSocketWrite(sock,tb,bytes));
657                 fd_ref_table[sock]->AddWriteBuf(tb);
658                 statsSent += bytes;
659         }
660         else log(DEFAULT,"ERROR! attempted write to a user with no fd_ref_table entry!!!");
661 }
662
663 /* write text from an originating user to originating user */
664
665 void WriteFrom(int sock, userrec *user,char* text, ...)
666 {
667         if (sock < 0)
668                 return;
669         if ((!text) || (!user))
670         {
671                 log(DEFAULT,"*** BUG *** WriteFrom was given an invalid parameter");
672                 return;
673         }
674         char textbuffer[MAXBUF],tb[MAXBUF];
675         va_list argsPtr;
676         int res;
677         va_start (argsPtr, text);
678         
679         vsnprintf(textbuffer, MAXBUF, text, argsPtr);
680         va_end(argsPtr);
681         int bytes = snprintf(tb,MAXBUF,":%s!%s@%s %s\r\n",user->nick,user->ident,user->dhost,textbuffer);
682         chop(tb);
683         if (fd_ref_table[sock])
684         {
685                 int MOD_RESULT = 0;
686                 FOREACH_RESULT(OnRawSocketWrite(sock,tb,bytes));
687                 fd_ref_table[sock]->AddWriteBuf(tb);
688                 statsSent += bytes;
689         }
690         else log(DEFAULT,"ERROR! attempted write to a user with no fd_ref_table entry!!!");
691 }
692
693 /* write text to an destination user from a source user (e.g. user privmsg) */
694
695 void WriteTo(userrec *source, userrec *dest,char *data, ...)
696 {
697         if ((!dest) || (!data))
698         {
699                 log(DEFAULT,"*** BUG *** WriteTo was given an invalid parameter");
700                 return;
701         }
702         if (dest->fd == FD_MAGIC_NUMBER)
703                 return;
704         char textbuffer[MAXBUF],tb[MAXBUF];
705         va_list argsPtr;
706         va_start (argsPtr, data);
707         vsnprintf(textbuffer, MAXBUF, data, argsPtr);
708         va_end(argsPtr);
709         chop(tb);
710
711         // if no source given send it from the server.
712         if (!source)
713         {
714                 WriteServ(dest->fd,":%s %s",ServerName,textbuffer);
715         }
716         else
717         {
718                 WriteFrom(dest->fd,source,"%s",textbuffer);
719         }
720 }
721
722 /* write formatted text from a source user to all users on a channel
723  * including the sender (NOT for privmsg, notice etc!) */
724
725 void WriteChannel(chanrec* Ptr, userrec* user, char* text, ...)
726 {
727         if ((!Ptr) || (!user) || (!text))
728         {
729                 log(DEFAULT,"*** BUG *** WriteChannel was given an invalid parameter");
730                 return;
731         }
732         char textbuffer[MAXBUF];
733         va_list argsPtr;
734         va_start (argsPtr, text);
735         vsnprintf(textbuffer, MAXBUF, text, argsPtr);
736         va_end(argsPtr);
737
738         std::vector<char*> *ulist = Ptr->GetUsers();
739         for (int j = 0; j < ulist->size(); j++)
740         {
741                 char* o = (*ulist)[j];
742                 userrec* otheruser = (userrec*)o;
743                 if (otheruser->fd != FD_MAGIC_NUMBER)
744                         WriteTo(user,otheruser,"%s",textbuffer);
745         }
746 }
747
748 /* write formatted text from a source user to all users on a channel
749  * including the sender (NOT for privmsg, notice etc!) doesnt send to
750  * users on remote servers */
751
752 void WriteChannelLocal(chanrec* Ptr, userrec* user, char* text, ...)
753 {
754         if ((!Ptr) || (!text))
755         {
756                 log(DEFAULT,"*** BUG *** WriteChannel was given an invalid parameter");
757                 return;
758         }
759         char textbuffer[MAXBUF];
760         va_list argsPtr;
761         va_start (argsPtr, text);
762         vsnprintf(textbuffer, MAXBUF, text, argsPtr);
763         va_end(argsPtr);
764
765         std::vector<char*> *ulist = Ptr->GetUsers();
766         for (int j = 0; j < ulist->size(); j++)
767         {
768                 char* o = (*ulist)[j];
769                 userrec* otheruser = (userrec*)o;
770                 if ((otheruser->fd != FD_MAGIC_NUMBER) && (otheruser->fd != -1) && (otheruser != user))
771                 {
772                         if (!user)
773                         {
774                                 WriteServ(otheruser->fd,"%s",textbuffer);
775                         }
776                         else
777                         {
778                                 WriteTo(user,otheruser,"%s",textbuffer);
779                         }
780                 }
781         }
782 }
783
784
785 void WriteChannelWithServ(char* ServName, chanrec* Ptr, char* text, ...)
786 {
787         if ((!Ptr) || (!text))
788         {
789                 log(DEFAULT,"*** BUG *** WriteChannelWithServ was given an invalid parameter");
790                 return;
791         }
792         char textbuffer[MAXBUF];
793         va_list argsPtr;
794         va_start (argsPtr, text);
795         vsnprintf(textbuffer, MAXBUF, text, argsPtr);
796         va_end(argsPtr);
797
798
799         std::vector<char*> *ulist = Ptr->GetUsers();
800         for (int j = 0; j < ulist->size(); j++)
801         {
802                 char* o = (*ulist)[j];
803                 userrec* otheruser = (userrec*)o;
804                 if (otheruser->fd != FD_MAGIC_NUMBER)
805                         WriteServ(otheruser->fd,"%s",textbuffer);
806         }
807 }
808
809
810 /* write formatted text from a source user to all users on a channel except
811  * for the sender (for privmsg etc) */
812
813 void ChanExceptSender(chanrec* Ptr, userrec* user, char* text, ...)
814 {
815         if ((!Ptr) || (!user) || (!text))
816         {
817                 log(DEFAULT,"*** BUG *** ChanExceptSender was given an invalid parameter");
818                 return;
819         }
820         char textbuffer[MAXBUF];
821         va_list argsPtr;
822         va_start (argsPtr, text);
823         vsnprintf(textbuffer, MAXBUF, text, argsPtr);
824         va_end(argsPtr);
825
826         std::vector<char*> *ulist = Ptr->GetUsers();
827         for (int j = 0; j < ulist->size(); j++)
828         {
829                 char* o = (*ulist)[j];
830                 userrec* otheruser = (userrec*)o;
831                 if ((otheruser->fd != FD_MAGIC_NUMBER) && (user != otheruser))
832                         WriteFrom(otheruser->fd,user,"%s",textbuffer);
833         }
834 }
835
836
837 std::string GetServerDescription(char* servername)
838 {
839         for (int j = 0; j < 32; j++)
840         {
841                 if (me[j] != NULL)
842                 {
843                         for (int k = 0; k < me[j]->connectors.size(); k++)
844                         {
845                                 if (!strcasecmp(me[j]->connectors[k].GetServerName().c_str(),servername))
846                                 {
847                                         return me[j]->connectors[k].GetDescription();
848                                 }
849                         }
850                 }
851                 return ServerDesc; // not a remote server that can be found, it must be me.
852         }
853 }
854
855
856 /* write a formatted string to all users who share at least one common
857  * channel, including the source user e.g. for use in NICK */
858
859 void WriteCommon(userrec *u, char* text, ...)
860 {
861         if (!u)
862         {
863                 log(DEFAULT,"*** BUG *** WriteCommon was given an invalid parameter");
864                 return;
865         }
866
867         if (u->registered != 7) {
868                 log(DEFAULT,"*** BUG *** WriteCommon on an unregistered user");
869                 return;
870         }
871         
872         char textbuffer[MAXBUF];
873         va_list argsPtr;
874         va_start (argsPtr, text);
875         vsnprintf(textbuffer, MAXBUF, text, argsPtr);
876         va_end(argsPtr);
877
878         // FIX: Stops a message going to the same person more than once
879         bzero(&already_sent,65536);
880
881         bool sent_to_at_least_one = false;
882
883         for (int i = 0; i < MAXCHANS; i++)
884         {
885                 if (u->chans[i].channel)
886                 {
887                         std::vector<char*> *ulist = u->chans[i].channel->GetUsers();
888                         for (int j = 0; j < ulist->size(); j++)
889                         {
890                                 char* o = (*ulist)[j];
891                                 userrec* otheruser = (userrec*)o;
892                                 if ((otheruser->fd > 0) && (!already_sent[otheruser->fd]))
893                                 {
894                                         already_sent[otheruser->fd] = 1;
895                                         WriteFrom(otheruser->fd,u,"%s",textbuffer);
896                                         sent_to_at_least_one = true;
897                                 }
898                         }
899                 }
900         }
901         // if the user was not in any channels, no users will receive the text. Make sure the user
902         // receives their OWN message for WriteCommon
903         if (!sent_to_at_least_one)
904         {
905                 WriteFrom(u->fd,u,"%s",textbuffer);
906         }
907 }
908
909 /* write a formatted string to all users who share at least one common
910  * channel, NOT including the source user e.g. for use in QUIT */
911
912 void WriteCommonExcept(userrec *u, char* text, ...)
913 {
914         if (!u)
915         {
916                 log(DEFAULT,"*** BUG *** WriteCommon was given an invalid parameter");
917                 return;
918         }
919
920         if (u->registered != 7) {
921                 log(DEFAULT,"*** BUG *** WriteCommon on an unregistered user");
922                 return;
923         }
924
925         char textbuffer[MAXBUF];
926         va_list argsPtr;
927         va_start (argsPtr, text);
928         vsnprintf(textbuffer, MAXBUF, text, argsPtr);
929         va_end(argsPtr);
930
931         bzero(&already_sent,65536);
932
933         for (int i = 0; i < MAXCHANS; i++)
934         {
935                 if (u->chans[i].channel)
936                 {
937                         std::vector<char*> *ulist = u->chans[i].channel->GetUsers();
938                         for (int j = 0; j < ulist->size(); j++)
939                         {
940                                 char* o = (*ulist)[j];
941                                 userrec* otheruser = (userrec*)o;
942                                 if (u != otheruser)
943                                 {
944                                         if ((otheruser->fd > 0) && (!already_sent[otheruser->fd]))
945                                         {
946                                                 already_sent[otheruser->fd] = 1;
947                                                 WriteFrom(otheruser->fd,u,"%s",textbuffer);
948                                         }
949                                 }
950                         }
951                 }
952         }
953 }
954
955 void WriteOpers(char* text, ...)
956 {
957         if (!text)
958         {
959                 log(DEFAULT,"*** BUG *** WriteOpers was given an invalid parameter");
960                 return;
961         }
962
963         char textbuffer[MAXBUF];
964         va_list argsPtr;
965         va_start (argsPtr, text);
966         vsnprintf(textbuffer, MAXBUF, text, argsPtr);
967         va_end(argsPtr);
968
969         for (std::vector<userrec*>::iterator i = all_opers.begin(); i != all_opers.end(); i++)
970         {
971                 userrec* a = *i;
972                 if ((a) && (a->fd != FD_MAGIC_NUMBER))
973                 {
974                         if (strchr(a->modes,'s'))
975                         {
976                                 // send server notices to all with +s
977                                 WriteServ(a->fd,"NOTICE %s :%s",a->nick,textbuffer);
978                         }
979                 }
980         }
981 }
982
983 void NoticeAllOpers(userrec *source, bool local_only, char* text, ...)
984 {
985         if ((!text) || (!source))
986         {
987                 log(DEFAULT,"*** BUG *** NoticeAllOpers was given an invalid parameter");
988                 return;
989         }
990
991         char textbuffer[MAXBUF];
992         va_list argsPtr;
993         va_start (argsPtr, text);
994         vsnprintf(textbuffer, MAXBUF, text, argsPtr);
995         va_end(argsPtr);
996
997         for (std::vector<userrec*>::iterator i = all_opers.begin(); i != all_opers.end(); i++)
998         {
999                 userrec* a = *i;
1000                 if ((a) && (a->fd != FD_MAGIC_NUMBER))
1001                 {
1002                         if (strchr(a->modes,'s'))
1003                         {
1004                                 // send server notices to all with +s
1005                                 WriteServ(a->fd,"NOTICE %s :*** Notice From %s: %s",a->nick,source->nick,textbuffer);
1006                         }
1007                 }
1008         }
1009
1010         if (!local_only)
1011         {
1012                 char buffer[MAXBUF];
1013                 snprintf(buffer,MAXBUF,"V %s @* :%s",source->nick,textbuffer);
1014                 NetSendToAll(buffer);
1015         }
1016 }
1017
1018 // returns TRUE of any users on channel C occupy server 'servername'.
1019
1020 bool ChanAnyOnThisServer(chanrec *c,char* servername)
1021 {
1022         log(DEBUG,"ChanAnyOnThisServer");
1023
1024         std::vector<char*> *ulist = c->GetUsers();
1025         for (int j = 0; j < ulist->size(); j++)
1026         {
1027                 char* o = (*ulist)[j];
1028                 userrec* user = (userrec*)o;
1029                 if (!strcasecmp(user->server,servername))
1030                         return true;
1031         }
1032         return false;
1033 }
1034
1035 // returns true if user 'u' shares any common channels with any users on server 'servername'
1036
1037 bool CommonOnThisServer(userrec* u,const char* servername)
1038 {
1039         log(DEBUG,"ChanAnyOnThisServer");
1040
1041         for (int i = 0; i < MAXCHANS; i++)
1042         {
1043                 if (u->chans[i].channel)
1044                 {
1045                         std::vector<char*> *ulist = u->chans[i].channel->GetUsers();
1046                         for (int j = 0; j < ulist->size(); j++)
1047                         {
1048                                 char* o = (*ulist)[j];
1049                                 userrec* user = (userrec*)o;
1050                                 if (!strcasecmp(user->server,servername))
1051                                         return true;
1052                         }
1053                 }
1054         }
1055         return false;
1056 }
1057
1058
1059 void NetSendToCommon(userrec* u, char* s)
1060 {
1061         char buffer[MAXBUF];
1062         snprintf(buffer,MAXBUF,"%s",s);
1063         
1064         log(DEBUG,"NetSendToCommon: '%s' '%s'",u->nick,s);
1065
1066         std::string msg = buffer;
1067         FOREACH_MOD OnPacketTransmit(msg,s);
1068         strlcpy(buffer,msg.c_str(),MAXBUF);
1069
1070         for (int j = 0; j < 32; j++)
1071         {
1072                 if (me[j] != NULL)
1073                 {
1074                         for (int k = 0; k < me[j]->connectors.size(); k++)
1075                         {
1076                                 if (CommonOnThisServer(u,me[j]->connectors[k].GetServerName().c_str()))
1077                                 {
1078                                         me[j]->SendPacket(buffer,me[j]->connectors[k].GetServerName().c_str());
1079                                 }
1080                         }
1081                 }
1082         }
1083 }
1084
1085
1086 void NetSendToAll(char* s)
1087 {
1088         char buffer[MAXBUF];
1089         snprintf(buffer,MAXBUF,"%s",s);
1090         
1091         log(DEBUG,"NetSendToAll: '%s'",s);
1092
1093         std::string msg = buffer;
1094         FOREACH_MOD OnPacketTransmit(msg,s);
1095         strlcpy(buffer,msg.c_str(),MAXBUF);
1096
1097         for (int j = 0; j < 32; j++)
1098         {
1099                 if (me[j] != NULL)
1100                 {
1101                         for (int k = 0; k < me[j]->connectors.size(); k++)
1102                         {
1103                                 me[j]->SendPacket(buffer,me[j]->connectors[k].GetServerName().c_str());
1104                         }
1105                 }
1106         }
1107 }
1108
1109 void NetSendToAllAlive(char* s)
1110 {
1111         char buffer[MAXBUF];
1112         snprintf(buffer,MAXBUF,"%s",s);
1113         
1114         log(DEBUG,"NetSendToAllAlive: '%s'",s);
1115
1116         std::string msg = buffer;
1117         FOREACH_MOD OnPacketTransmit(msg,s);
1118         strlcpy(buffer,msg.c_str(),MAXBUF);
1119
1120         for (int j = 0; j < 32; j++)
1121         {
1122                 if (me[j] != NULL)
1123                 {
1124                         for (int k = 0; k < me[j]->connectors.size(); k++)
1125                         {
1126                                 if (me[j]->connectors[k].GetState() != STATE_DISCONNECTED)
1127                                 {
1128                                         me[j]->SendPacket(buffer,me[j]->connectors[k].GetServerName().c_str());
1129                                 }
1130                                 else
1131                                 {
1132                                         log(DEBUG,"%s is dead, not sending to it.",me[j]->connectors[k].GetServerName().c_str());
1133                                 }
1134                         }
1135                 }
1136         }
1137 }
1138
1139
1140 void NetSendToOne(char* target,char* s)
1141 {
1142         char buffer[MAXBUF];
1143         snprintf(buffer,MAXBUF,"%s",s);
1144         
1145         log(DEBUG,"NetSendToOne: '%s' '%s'",target,s);
1146
1147         std::string msg = buffer;
1148         FOREACH_MOD OnPacketTransmit(msg,s);
1149         strlcpy(buffer,msg.c_str(),MAXBUF);
1150
1151         for (int j = 0; j < 32; j++)
1152         {
1153                 if (me[j] != NULL)
1154                 {
1155                         for (int k = 0; k < me[j]->connectors.size(); k++)
1156                         {
1157                                 if (!strcasecmp(me[j]->connectors[k].GetServerName().c_str(),target))
1158                                 {
1159                                         me[j]->SendPacket(buffer,me[j]->connectors[k].GetServerName().c_str());
1160                                 }
1161                         }
1162                 }
1163         }
1164 }
1165
1166 void NetSendToAllExcept(const char* target,char* s)
1167 {
1168         char buffer[MAXBUF];
1169         snprintf(buffer,MAXBUF,"%s",s);
1170         
1171         log(DEBUG,"NetSendToAllExcept: '%s' '%s'",target,s);
1172         
1173         std::string msg = buffer;
1174         FOREACH_MOD OnPacketTransmit(msg,s);
1175         strlcpy(buffer,msg.c_str(),MAXBUF);
1176
1177         for (int j = 0; j < 32; j++)
1178         {
1179                 if (me[j] != NULL)
1180                 {
1181                         for (int k = 0; k < me[j]->connectors.size(); k++)
1182                         {
1183                                 if (strcasecmp(me[j]->connectors[k].GetServerName().c_str(),target))
1184                                 {
1185                                         me[j]->SendPacket(buffer,me[j]->connectors[k].GetServerName().c_str());
1186                                 }
1187                         }
1188                 }
1189         }
1190 }
1191
1192
1193 void WriteMode(const char* modes, int flags, const char* text, ...)
1194 {
1195         if ((!text) || (!modes) || (!flags))
1196         {
1197                 log(DEFAULT,"*** BUG *** WriteMode was given an invalid parameter");
1198                 return;
1199         }
1200
1201         char textbuffer[MAXBUF];
1202         va_list argsPtr;
1203         va_start (argsPtr, text);
1204         vsnprintf(textbuffer, MAXBUF, text, argsPtr);
1205         va_end(argsPtr);
1206         int modelen = strlen(modes);
1207
1208         for (user_hash::const_iterator i = clientlist.begin(); i != clientlist.end(); i++)
1209         {
1210                 if ((i->second) && (i->second->fd != FD_MAGIC_NUMBER))
1211                 {
1212                         bool send_to_user = false;
1213                         
1214                         if (flags == WM_AND)
1215                         {
1216                                 send_to_user = true;
1217                                 for (int n = 0; n < modelen; n++)
1218                                 {
1219                                         if (!hasumode(i->second,modes[n]))
1220                                         {
1221                                                 send_to_user = false;
1222                                                 break;
1223                                         }
1224                                 }
1225                         }
1226                         else if (flags == WM_OR)
1227                         {
1228                                 send_to_user = false;
1229                                 for (int n = 0; n < modelen; n++)
1230                                 {
1231                                         if (hasumode(i->second,modes[n]))
1232                                         {
1233                                                 send_to_user = true;
1234                                                 break;
1235                                         }
1236                                 }
1237                         }
1238
1239                         if (send_to_user)
1240                         {
1241                                 WriteServ(i->second->fd,"NOTICE %s :%s",i->second->nick,textbuffer);
1242                         }
1243                 }
1244         }
1245 }
1246
1247
1248 void NoticeAll(userrec *source, bool local_only, char* text, ...)
1249 {
1250         if ((!text) || (!source))
1251         {
1252                 log(DEFAULT,"*** BUG *** NoticeAll was given an invalid parameter");
1253                 return;
1254         }
1255
1256         char textbuffer[MAXBUF];
1257         va_list argsPtr;
1258         va_start (argsPtr, text);
1259         vsnprintf(textbuffer, MAXBUF, text, argsPtr);
1260         va_end(argsPtr);
1261
1262         for (user_hash::const_iterator i = clientlist.begin(); i != clientlist.end(); i++)
1263         {
1264                 if ((i->second) && (i->second->fd != FD_MAGIC_NUMBER))
1265                 {
1266                         WriteFrom(i->second->fd,source,"NOTICE $* :%s",textbuffer);
1267                 }
1268         }
1269
1270         if (!local_only)
1271         {
1272                 char buffer[MAXBUF];
1273                 snprintf(buffer,MAXBUF,"V %s * :%s",source->nick,textbuffer);
1274                 NetSendToAll(buffer);
1275         }
1276
1277 }
1278
1279 void WriteWallOps(userrec *source, bool local_only, char* text, ...)  
1280 {  
1281         if ((!text) || (!source))
1282         {
1283                 log(DEFAULT,"*** BUG *** WriteOpers was given an invalid parameter");
1284                 return;
1285         }
1286
1287         char textbuffer[MAXBUF];  
1288         va_list argsPtr;  
1289         va_start (argsPtr, text);  
1290         vsnprintf(textbuffer, MAXBUF, text, argsPtr);  
1291         va_end(argsPtr);  
1292   
1293         for (user_hash::const_iterator i = clientlist.begin(); i != clientlist.end(); i++)
1294         {
1295                 if ((i->second) && (i->second->fd != FD_MAGIC_NUMBER))
1296                 {
1297                         if (strchr(i->second->modes,'w'))
1298                         {
1299                                 WriteTo(source,i->second,"WALLOPS :%s",textbuffer);
1300                         }
1301                 }
1302         }
1303
1304         if (!local_only)
1305         {
1306                 char buffer[MAXBUF];
1307                 snprintf(buffer,MAXBUF,"@ %s :%s",source->nick,textbuffer);
1308                 NetSendToAll(buffer);
1309         }
1310 }  
1311
1312 /* convert a string to lowercase. Note following special circumstances
1313  * taken from RFC 1459. Many "official" server branches still hold to this
1314  * rule so i will too;
1315  *
1316  *  Because of IRC's scandanavian origin, the characters {}| are
1317  *  considered to be the lower case equivalents of the characters []\,
1318  *  respectively. This is a critical issue when determining the
1319  *  equivalence of two nicknames.
1320  */
1321
1322 void strlower(char *n)
1323 {
1324         if (n)
1325         {
1326                 for (char* t = n; *t; t++)
1327                         *t = lowermap[*t];
1328         }
1329 }
1330
1331
1332
1333 /* Find a user record by nickname and return a pointer to it */
1334
1335 userrec* Find(std::string nick)
1336 {
1337         user_hash::iterator iter = clientlist.find(nick);
1338
1339         if (iter == clientlist.end())
1340                 /* Couldn't find it */
1341                 return NULL;
1342
1343         return iter->second;
1344 }
1345
1346 /* find a channel record by channel name and return a pointer to it */
1347
1348 chanrec* FindChan(const char* chan)
1349 {
1350         if (!chan)
1351         {
1352                 log(DEFAULT,"*** BUG *** Findchan was given an invalid parameter");
1353                 return NULL;
1354         }
1355
1356         chan_hash::iterator iter = chanlist.find(chan);
1357
1358         if (iter == chanlist.end())
1359                 /* Couldn't find it */
1360                 return NULL;
1361
1362         return iter->second;
1363 }
1364
1365
1366 long GetMaxBans(char* name)
1367 {
1368         char CM[MAXBUF];
1369         for (int count = 0; count < ConfValueEnum("banlist",&config_f); count++)
1370         {
1371                 ConfValue("banlist","chan",count,CM,&config_f);
1372                 if (match(name,CM))
1373                 {
1374                         ConfValue("banlist","limit",count,CM,&config_f);
1375                         return atoi(CM);
1376                 }
1377         }
1378         return 64;
1379 }
1380
1381
1382 void purge_empty_chans(userrec* u)
1383 {
1384
1385         int go_again = 1, purge = 0;
1386
1387         // firstly decrement the count on each channel
1388         for (int f = 0; f < MAXCHANS; f++)
1389         {
1390                 if (u->chans[f].channel)
1391                 {
1392                         u->chans[f].channel->DelUser((char*)u);
1393                 }
1394         }
1395
1396         for (int i = 0; i < MAXCHANS; i++)
1397         {
1398                 if (u->chans[i].channel)
1399                 {
1400                         if (!usercount(u->chans[i].channel))
1401                         {
1402                                 chan_hash::iterator i2 = chanlist.find(u->chans[i].channel->name);
1403                                 /* kill the record */
1404                                 if (i2 != chanlist.end())
1405                                 {
1406                                         log(DEBUG,"del_channel: destroyed: %s",i2->second->name);
1407                                         if (i2->second)
1408                                                 delete i2->second;
1409                                         chanlist.erase(i2);
1410                                         go_again = 1;
1411                                         purge++;
1412                                         u->chans[i].channel = NULL;
1413                                 }
1414                         }
1415                         else
1416                         {
1417                                 log(DEBUG,"skipped purge for %s",u->chans[i].channel->name);
1418                         }
1419                 }
1420         }
1421         log(DEBUG,"completed channel purge, killed %lu",(unsigned long)purge);
1422
1423         DeleteOper(u);
1424 }
1425
1426
1427 char scratch[MAXBUF];
1428 char sparam[MAXBUF];
1429
1430 char* chanmodes(chanrec *chan)
1431 {
1432         if (!chan)
1433         {
1434                 log(DEFAULT,"*** BUG *** chanmodes was given an invalid parameter");
1435                 strcpy(scratch,"");
1436                 return scratch;
1437         }
1438
1439         strcpy(scratch,"");
1440         strcpy(sparam,"");
1441         if (chan->binarymodes & CM_NOEXTERNAL)
1442         {
1443                 strlcat(scratch,"n",MAXMODES);
1444         }
1445         if (chan->binarymodes & CM_TOPICLOCK)
1446         {
1447                 strlcat(scratch,"t",MAXMODES);
1448         }
1449         if (chan->key[0])
1450         {
1451                 strlcat(scratch,"k",MAXMODES);
1452         }
1453         if (chan->limit)
1454         {
1455                 strlcat(scratch,"l",MAXMODES);
1456         }
1457         if (chan->binarymodes & CM_INVITEONLY)
1458         {
1459                 strlcat(scratch,"i",MAXMODES);
1460         }
1461         if (chan->binarymodes & CM_MODERATED)
1462         {
1463                 strlcat(scratch,"m",MAXMODES);
1464         }
1465         if (chan->binarymodes & CM_SECRET)
1466         {
1467                 strlcat(scratch,"s",MAXMODES);
1468         }
1469         if (chan->binarymodes & CM_PRIVATE)
1470         {
1471                 strlcat(scratch,"p",MAXMODES);
1472         }
1473         if (chan->key[0])
1474         {
1475                 strlcat(sparam," ",MAXBUF);
1476                 strlcat(sparam,chan->key,MAXBUF);
1477         }
1478         if (chan->limit)
1479         {
1480                 char foo[24];
1481                 sprintf(foo," %lu",(unsigned long)chan->limit);
1482                 strlcat(sparam,foo,MAXBUF);
1483         }
1484         if (*chan->custom_modes)
1485         {
1486                 strlcat(scratch,chan->custom_modes,MAXMODES);
1487                 for (int z = 0; chan->custom_modes[z] != 0; z++)
1488                 {
1489                         std::string extparam = chan->GetModeParameter(chan->custom_modes[z]);
1490                         if (extparam != "")
1491                         {
1492                                 strlcat(sparam," ",MAXBUF);
1493                                 strlcat(sparam,extparam.c_str(),MAXBUF);
1494                         }
1495                 }
1496         }
1497         log(DEBUG,"chanmodes: %s %s%s",chan->name,scratch,sparam);
1498         strlcat(scratch,sparam,MAXMODES);
1499         return scratch;
1500 }
1501
1502
1503 /* compile a userlist of a channel into a string, each nick seperated by
1504  * spaces and op, voice etc status shown as @ and + */
1505
1506 void userlist(userrec *user,chanrec *c)
1507 {
1508         if ((!c) || (!user))
1509         {
1510                 log(DEFAULT,"*** BUG *** userlist was given an invalid parameter");
1511                 return;
1512         }
1513
1514         snprintf(list,MAXBUF,"353 %s = %s :", user->nick, c->name);
1515
1516         std::vector<char*> *ulist = c->GetUsers();
1517         for (int i = 0; i < ulist->size(); i++)
1518         {
1519                 char* o = (*ulist)[i];
1520                 userrec* otheruser = (userrec*)o;
1521                 if ((!has_channel(user,c)) && (strchr(otheruser->modes,'i')))
1522                 {
1523                         /* user is +i, and source not on the channel, does not show
1524                          * nick in NAMES list */
1525                         continue;
1526                 }
1527                 strlcat(list,cmode(otheruser,c),MAXBUF);
1528                 strlcat(list,otheruser->nick,MAXBUF);
1529                 strlcat(list," ",MAXBUF);
1530                 if (strlen(list)>(480-NICKMAX))
1531                 {
1532                         /* list overflowed into
1533                          * multiple numerics */
1534                         WriteServ(user->fd,"%s",list);
1535                         snprintf(list,MAXBUF,"353 %s = %s :", user->nick, c->name);
1536                 }
1537         }
1538         /* if whats left in the list isnt empty, send it */
1539         if (list[strlen(list)-1] != ':')
1540         {
1541                 WriteServ(user->fd,"%s",list);
1542         }
1543 }
1544
1545 /* return a count of the users on a specific channel accounting for
1546  * invisible users who won't increase the count. e.g. for /LIST */
1547
1548 int usercount_i(chanrec *c)
1549 {
1550         int count = 0;
1551         
1552         if (!c)
1553         {
1554                 log(DEFAULT,"*** BUG *** usercount_i was given an invalid parameter");
1555                 return 0;
1556         }
1557
1558         strcpy(list,"");
1559         for (user_hash::const_iterator i = clientlist.begin(); i != clientlist.end(); i++)
1560         {
1561                 if (i->second)
1562                 {
1563                         if (has_channel(i->second,c))
1564                         {
1565                                 if (isnick(i->second->nick))
1566                                 {
1567                                         if ((!has_channel(i->second,c)) && (strchr(i->second->modes,'i')))
1568                                         {
1569                                                 /* user is +i, and source not on the channel, does not show
1570                                                  * nick in NAMES list */
1571                                                 continue;
1572                                         }
1573                                         count++;
1574                                 }
1575                         }
1576                 }
1577         }
1578         log(DEBUG,"usercount_i: %s %lu",c->name,(unsigned long)count);
1579         return count;
1580 }
1581
1582
1583 int usercount(chanrec *c)
1584 {
1585         if (!c)
1586         {
1587                 log(DEFAULT,"*** BUG *** usercount was given an invalid parameter");
1588                 return 0;
1589         }
1590         int count = c->GetUserCounter();
1591         log(DEBUG,"usercount: %s %lu",c->name,(unsigned long)count);
1592         return count;
1593 }
1594
1595
1596 /* add a channel to a user, creating the record for it if needed and linking
1597  * it to the user record */
1598
1599 chanrec* add_channel(userrec *user, const char* cn, const char* key, bool override)
1600 {
1601         if ((!user) || (!cn))
1602         {
1603                 log(DEFAULT,"*** BUG *** add_channel was given an invalid parameter");
1604                 return 0;
1605         }
1606
1607         chanrec* Ptr;
1608         int created = 0;
1609         char cname[MAXBUF];
1610
1611         strncpy(cname,cn,MAXBUF);
1612         
1613         // we MUST declare this wherever we use FOREACH_RESULT
1614         int MOD_RESULT = 0;
1615
1616         if (strlen(cname) > CHANMAX-1)
1617         {
1618                 cname[CHANMAX-1] = '\0';
1619         }
1620
1621         log(DEBUG,"add_channel: %s %s",user->nick,cname);
1622         
1623         if ((FindChan(cname)) && (has_channel(user,FindChan(cname))))
1624         {
1625                 return NULL; // already on the channel!
1626         }
1627
1628
1629         if (!FindChan(cname))
1630         {
1631                 MOD_RESULT = 0;
1632                 FOREACH_RESULT(OnUserPreJoin(user,NULL,cname));
1633                 if (MOD_RESULT == 1) {
1634                         return NULL;
1635                 }
1636
1637                 /* create a new one */
1638                 log(DEBUG,"add_channel: creating: %s",cname);
1639                 {
1640                         chanlist[cname] = new chanrec();
1641
1642                         strlcpy(chanlist[cname]->name, cname,CHANMAX);
1643                         chanlist[cname]->binarymodes = CM_TOPICLOCK | CM_NOEXTERNAL;
1644                         chanlist[cname]->created = TIME;
1645                         strcpy(chanlist[cname]->topic, "");
1646                         strncpy(chanlist[cname]->setby, user->nick,NICKMAX);
1647                         chanlist[cname]->topicset = 0;
1648                         Ptr = chanlist[cname];
1649                         log(DEBUG,"add_channel: created: %s",cname);
1650                         /* set created to 2 to indicate user
1651                          * is the first in the channel
1652                          * and should be given ops */
1653                         created = 2;
1654                 }
1655         }
1656         else
1657         {
1658                 /* channel exists, just fish out a pointer to its struct */
1659                 Ptr = FindChan(cname);
1660                 if (Ptr)
1661                 {
1662                         log(DEBUG,"add_channel: joining to: %s",Ptr->name);
1663                         
1664                         // the override flag allows us to bypass channel modes
1665                         // and bans (used by servers)
1666                         if ((!override) || (!strcasecmp(user->server,ServerName)))
1667                         {
1668                                 log(DEBUG,"Not overriding...");
1669                                 MOD_RESULT = 0;
1670                                 FOREACH_RESULT(OnUserPreJoin(user,Ptr,cname));
1671                                 if (MOD_RESULT == 1) {
1672                                         return NULL;
1673                                 }
1674                                 log(DEBUG,"MOD_RESULT=%d",MOD_RESULT);
1675                                 
1676                                 if (!MOD_RESULT) 
1677                                 {
1678                                         log(DEBUG,"add_channel: checking key, invite, etc");
1679                                         MOD_RESULT = 0;
1680                                         FOREACH_RESULT(OnCheckKey(user, Ptr, key ? key : ""));
1681                                         if (MOD_RESULT == 0)
1682                                         {
1683                                                 if (Ptr->key[0])
1684                                                 {
1685                                                         log(DEBUG,"add_channel: %s has key %s",Ptr->name,Ptr->key);
1686                                                         if (!key)
1687                                                         {
1688                                                                 log(DEBUG,"add_channel: no key given in JOIN");
1689                                                                 WriteServ(user->fd,"475 %s %s :Cannot join channel (Requires key)",user->nick, Ptr->name);
1690                                                                 return NULL;
1691                                                         }
1692                                                         else
1693                                                         {
1694                                                                 if (strcasecmp(key,Ptr->key))
1695                                                                 {
1696                                                                         log(DEBUG,"add_channel: bad key given in JOIN");
1697                                                                         WriteServ(user->fd,"475 %s %s :Cannot join channel (Incorrect key)",user->nick, Ptr->name);
1698                                                                         return NULL;
1699                                                                 }
1700                                                         }
1701                                                 }
1702                                                 log(DEBUG,"add_channel: no key");
1703                                         }
1704                                         MOD_RESULT = 0;
1705                                         FOREACH_RESULT(OnCheckInvite(user, Ptr));
1706                                         if (MOD_RESULT == 0)
1707                                         {
1708                                                 if (Ptr->binarymodes & CM_INVITEONLY)
1709                                                 {
1710                                                         log(DEBUG,"add_channel: channel is +i");
1711                                                         if (user->IsInvited(Ptr->name))
1712                                                         {
1713                                                                 /* user was invited to channel */
1714                                                                 /* there may be an optional channel NOTICE here */
1715                                                         }
1716                                                         else
1717                                                         {
1718                                                                 WriteServ(user->fd,"473 %s %s :Cannot join channel (Invite only)",user->nick, Ptr->name);
1719                                                                 return NULL;
1720                                                         }
1721                                                 }
1722                                                 log(DEBUG,"add_channel: channel is not +i");
1723                                         }
1724                                         MOD_RESULT = 0;
1725                                         FOREACH_RESULT(OnCheckLimit(user, Ptr));
1726                                         if (MOD_RESULT == 0)
1727                                         {
1728                                                 if (Ptr->limit)
1729                                                 {
1730                                                         if (usercount(Ptr) >= Ptr->limit)
1731                                                         {
1732                                                                 WriteServ(user->fd,"471 %s %s :Cannot join channel (Channel is full)",user->nick, Ptr->name);
1733                                                                 return NULL;
1734                                                         }
1735                                                 }
1736                                         }
1737                                         log(DEBUG,"add_channel: about to walk banlist");
1738                                         MOD_RESULT = 0;
1739                                         FOREACH_RESULT(OnCheckBan(user, Ptr));
1740                                         if (MOD_RESULT == 0)
1741                                         {
1742                                                 /* check user against the channel banlist */
1743                                                 if (Ptr)
1744                                                 {
1745                                                         if (Ptr->bans.size())
1746                                                         {
1747                                                                 for (BanList::iterator i = Ptr->bans.begin(); i != Ptr->bans.end(); i++)
1748                                                                 {
1749                                                                         if (match(user->GetFullHost(),i->data))
1750                                                                         {
1751                                                                                 WriteServ(user->fd,"474 %s %s :Cannot join channel (You're banned)",user->nick, Ptr->name);
1752                                                                                 return NULL;
1753                                                                         }
1754                                                                 }
1755                                                         }
1756                                                 }
1757                                                 log(DEBUG,"add_channel: bans checked");
1758                                         }
1759                                 
1760                                 }
1761                                 
1762
1763                                 if ((Ptr) && (user))
1764                                 {
1765                                         user->RemoveInvite(Ptr->name);
1766                                 }
1767         
1768                                 log(DEBUG,"add_channel: invites removed");
1769
1770                         }
1771                         else
1772                         {
1773                                 log(DEBUG,"Overridden checks");
1774                         }
1775
1776                         
1777                 }
1778                 created = 1;
1779         }
1780
1781         log(DEBUG,"Passed channel checks");
1782         
1783         for (int index =0; index != MAXCHANS; index++)
1784         {
1785                 log(DEBUG,"Check location %d",index);
1786                 if (user->chans[index].channel == NULL)
1787                 {
1788                         log(DEBUG,"Adding into their channel list at location %d",index);
1789
1790                         if (created == 2) 
1791                         {
1792                                 /* first user in is given ops */
1793                                 user->chans[index].uc_modes = UCMODE_OP;
1794                         }
1795                         else
1796                         {
1797                                 user->chans[index].uc_modes = 0;
1798                         }
1799                         user->chans[index].channel = Ptr;
1800                         Ptr->AddUser((char*)user);
1801                         WriteChannel(Ptr,user,"JOIN :%s",Ptr->name);
1802                         
1803                         if (!override) // we're not overriding... so this isnt part of a netburst, broadcast it.
1804                         {
1805                                 // use the stamdard J token with no privilages.
1806                                 char buffer[MAXBUF];
1807                                 if (created == 2)
1808                                 {
1809                                         snprintf(buffer,MAXBUF,"J %s @%s",user->nick,Ptr->name);
1810                                 }
1811                                 else
1812                                 {
1813                                         snprintf(buffer,MAXBUF,"J %s %s",user->nick,Ptr->name);
1814                                 }
1815                                 NetSendToAll(buffer);
1816                         }
1817
1818                         log(DEBUG,"Sent JOIN to client");
1819
1820                         if (Ptr->topicset)
1821                         {
1822                                 WriteServ(user->fd,"332 %s %s :%s", user->nick, Ptr->name, Ptr->topic);
1823                                 WriteServ(user->fd,"333 %s %s %s %lu", user->nick, Ptr->name, Ptr->setby, (unsigned long)Ptr->topicset);
1824                         }
1825                         userlist(user,Ptr);
1826                         WriteServ(user->fd,"366 %s %s :End of /NAMES list.", user->nick, Ptr->name);
1827                         //WriteServ(user->fd,"324 %s %s +%s",user->nick, Ptr->name,chanmodes(Ptr));
1828                         //WriteServ(user->fd,"329 %s %s %lu", user->nick, Ptr->name, (unsigned long)Ptr->created);
1829                         FOREACH_MOD OnUserJoin(user,Ptr);
1830                         return Ptr;
1831                 }
1832         }
1833         log(DEBUG,"add_channel: user channel max exceeded: %s %s",user->nick,cname);
1834         WriteServ(user->fd,"405 %s %s :You are on too many channels",user->nick, cname);
1835         return NULL;
1836 }
1837
1838 /* remove a channel from a users record, and remove the record from memory
1839  * if the channel has become empty */
1840
1841 chanrec* del_channel(userrec *user, const char* cname, const char* reason, bool local)
1842 {
1843         if ((!user) || (!cname))
1844         {
1845                 log(DEFAULT,"*** BUG *** del_channel was given an invalid parameter");
1846                 return NULL;
1847         }
1848
1849         chanrec* Ptr;
1850
1851         if ((!cname) || (!user))
1852         {
1853                 return NULL;
1854         }
1855
1856         Ptr = FindChan(cname);
1857         
1858         if (!Ptr)
1859         {
1860                 return NULL;
1861         }
1862
1863         FOREACH_MOD OnUserPart(user,Ptr);
1864         log(DEBUG,"del_channel: removing: %s %s",user->nick,Ptr->name);
1865         
1866         for (int i =0; i != MAXCHANS; i++)
1867         {
1868                 /* zap it from the channel list of the user */
1869                 if (user->chans[i].channel == Ptr)
1870                 {
1871                         if (reason)
1872                         {
1873                                 WriteChannel(Ptr,user,"PART %s :%s",Ptr->name, reason);
1874
1875                                 if (!local)
1876                                 {
1877                                         char buffer[MAXBUF];
1878                                         snprintf(buffer,MAXBUF,"L %s %s :%s",user->nick,Ptr->name,reason);
1879                                         NetSendToAll(buffer);
1880                                 }
1881
1882                                 
1883                         }
1884                         else
1885                         {
1886                                 if (!local)
1887                                 {
1888                                         char buffer[MAXBUF];
1889                                         snprintf(buffer,MAXBUF,"L %s %s :",user->nick,Ptr->name);
1890                                         NetSendToAll(buffer);
1891                                 }
1892                         
1893                                 WriteChannel(Ptr,user,"PART :%s",Ptr->name);
1894                         }
1895                         user->chans[i].uc_modes = 0;
1896                         user->chans[i].channel = NULL;
1897                         log(DEBUG,"del_channel: unlinked: %s %s",user->nick,Ptr->name);
1898                         break;
1899                 }
1900         }
1901
1902         Ptr->DelUser((char*)user);
1903         
1904         /* if there are no users left on the channel */
1905         if (!usercount(Ptr))
1906         {
1907                 chan_hash::iterator iter = chanlist.find(Ptr->name);
1908
1909                 log(DEBUG,"del_channel: destroying channel: %s",Ptr->name);
1910
1911                 /* kill the record */
1912                 if (iter != chanlist.end())
1913                 {
1914                         log(DEBUG,"del_channel: destroyed: %s",Ptr->name);
1915                         delete Ptr;
1916                         chanlist.erase(iter);
1917                 }
1918         }
1919 }
1920
1921
1922 void kick_channel(userrec *src,userrec *user, chanrec *Ptr, char* reason)
1923 {
1924         if ((!src) || (!user) || (!Ptr) || (!reason))
1925         {
1926                 log(DEFAULT,"*** BUG *** kick_channel was given an invalid parameter");
1927                 return;
1928         }
1929
1930         if ((!Ptr) || (!user) || (!src))
1931         {
1932                 return;
1933         }
1934
1935         log(DEBUG,"kick_channel: removing: %s %s %s",user->nick,Ptr->name,src->nick);
1936
1937         if (!has_channel(user,Ptr))
1938         {
1939                 WriteServ(src->fd,"441 %s %s %s :They are not on that channel",src->nick, user->nick, Ptr->name);
1940                 return;
1941         }
1942
1943         int MOD_RESULT = 0;
1944         FOREACH_RESULT(OnAccessCheck(src,user,Ptr,AC_KICK));
1945         if (MOD_RESULT == ACR_DENY)
1946                 return;
1947
1948         if (MOD_RESULT == ACR_DEFAULT)
1949         {
1950                 if (((cstatus(src,Ptr) < STATUS_HOP) || (cstatus(src,Ptr) < cstatus(user,Ptr))) && (!is_uline(src->server)))
1951                 {
1952                         if (cstatus(src,Ptr) == STATUS_HOP)
1953                         {
1954                                 WriteServ(src->fd,"482 %s %s :You must be a channel operator",src->nick, Ptr->name);
1955                         }
1956                         else
1957                         {
1958                                 WriteServ(src->fd,"482 %s %s :You must be at least a half-operator to change modes on this channel",src->nick, Ptr->name);
1959                         }
1960                         
1961                         return;
1962                 }
1963         }
1964
1965         MOD_RESULT = 0;
1966         FOREACH_RESULT(OnUserPreKick(src,user,Ptr,reason));
1967         if (MOD_RESULT)
1968                 return;
1969
1970         FOREACH_MOD OnUserKick(src,user,Ptr,reason);
1971
1972         for (int i =0; i != MAXCHANS; i++)
1973         {
1974                 /* zap it from the channel list of the user */
1975                 if (user->chans[i].channel)
1976                 if (!strcasecmp(user->chans[i].channel->name,Ptr->name))
1977                 {
1978                         WriteChannel(Ptr,src,"KICK %s %s :%s",Ptr->name, user->nick, reason);
1979                         user->chans[i].uc_modes = 0;
1980                         user->chans[i].channel = NULL;
1981                         log(DEBUG,"del_channel: unlinked: %s %s",user->nick,Ptr->name);
1982                         break;
1983                 }
1984         }
1985
1986         Ptr->DelUser((char*)user);
1987
1988         /* if there are no users left on the channel */
1989         if (!usercount(Ptr))
1990         {
1991                 chan_hash::iterator iter = chanlist.find(Ptr->name);
1992
1993                 log(DEBUG,"del_channel: destroying channel: %s",Ptr->name);
1994
1995                 /* kill the record */
1996                 if (iter != chanlist.end())
1997                 {
1998                         log(DEBUG,"del_channel: destroyed: %s",Ptr->name);
1999                         delete Ptr;
2000                         chanlist.erase(iter);
2001                 }
2002         }
2003 }
2004
2005
2006
2007
2008 /* This function pokes and hacks at a parameter list like the following:
2009  *
2010  * PART #winbot,#darkgalaxy :m00!
2011  *
2012  * to turn it into a series of individual calls like this:
2013  *
2014  * PART #winbot :m00!
2015  * PART #darkgalaxy :m00!
2016  *
2017  * The seperate calls are sent to a callback function provided by the caller
2018  * (the caller will usually call itself recursively). The callback function
2019  * must be a command handler. Calling this function on a line with no list causes
2020  * no action to be taken. You must provide a starting and ending parameter number
2021  * where the range of the list can be found, useful if you have a terminating
2022  * parameter as above which is actually not part of the list, or parameters
2023  * before the actual list as well. This code is used by many functions which
2024  * can function as "one to list" (see the RFC) */
2025
2026 int loop_call(handlerfunc fn, char **parameters, int pcnt, userrec *u, int start, int end, int joins)
2027 {
2028         char plist[MAXBUF];
2029         char *param;
2030         char *pars[32];
2031         char blog[32][MAXBUF];
2032         char blog2[32][MAXBUF];
2033         int j = 0, q = 0, total = 0, t = 0, t2 = 0, total2 = 0;
2034         char keystr[MAXBUF];
2035         char moo[MAXBUF];
2036
2037         for (int i = 0; i <32; i++)
2038                 strcpy(blog[i],"");
2039
2040         for (int i = 0; i <32; i++)
2041                 strcpy(blog2[i],"");
2042
2043         strcpy(moo,"");
2044         for (int i = 0; i <10; i++)
2045         {
2046                 if (!parameters[i])
2047                 {
2048                         parameters[i] = moo;
2049                 }
2050         }
2051         if (joins)
2052         {
2053                 if (pcnt > 1) /* we have a key to copy */
2054                 {
2055                         strlcpy(keystr,parameters[1],MAXBUF);
2056                 }
2057         }
2058
2059         if (!parameters[start])
2060         {
2061                 return 0;
2062         }
2063         if (!strchr(parameters[start],','))
2064         {
2065                 return 0;
2066         }
2067         strcpy(plist,"");
2068         for (int i = start; i <= end; i++)
2069         {
2070                 if (parameters[i])
2071                 {
2072                         strlcat(plist,parameters[i],MAXBUF);
2073                 }
2074         }
2075         
2076         j = 0;
2077         param = plist;
2078
2079         t = strlen(plist);
2080         for (int i = 0; i < t; i++)
2081         {
2082                 if (plist[i] == ',')
2083                 {
2084                         plist[i] = '\0';
2085                         strlcpy(blog[j++],param,MAXBUF);
2086                         param = plist+i+1;
2087                         if (j>20)
2088                         {
2089                                 WriteServ(u->fd,"407 %s %s :Too many targets in list, message not delivered.",u->nick,blog[j-1]);
2090                                 return 1;
2091                         }
2092                 }
2093         }
2094         strlcpy(blog[j++],param,MAXBUF);
2095         total = j;
2096
2097         if ((joins) && (keystr) && (total>0)) // more than one channel and is joining
2098         {
2099                 strcat(keystr,",");
2100         }
2101         
2102         if ((joins) && (keystr))
2103         {
2104                 if (strchr(keystr,','))
2105                 {
2106                         j = 0;
2107                         param = keystr;
2108                         t2 = strlen(keystr);
2109                         for (int i = 0; i < t2; i++)
2110                         {
2111                                 if (keystr[i] == ',')
2112                                 {
2113                                         keystr[i] = '\0';
2114                                         strlcpy(blog2[j++],param,MAXBUF);
2115                                         param = keystr+i+1;
2116                                 }
2117                         }
2118                         strlcpy(blog2[j++],param,MAXBUF);
2119                         total2 = j;
2120                 }
2121         }
2122
2123         for (j = 0; j < total; j++)
2124         {
2125                 if (blog[j])
2126                 {
2127                         pars[0] = blog[j];
2128                 }
2129                 for (q = end; q < pcnt-1; q++)
2130                 {
2131                         if (parameters[q+1])
2132                         {
2133                                 pars[q-end+1] = parameters[q+1];
2134                         }
2135                 }
2136                 if ((joins) && (parameters[1]))
2137                 {
2138                         if (pcnt > 1)
2139                         {
2140                                 pars[1] = blog2[j];
2141                         }
2142                         else
2143                         {
2144                                 pars[1] = NULL;
2145                         }
2146                 }
2147                 /* repeatedly call the function with the hacked parameter list */
2148                 if ((joins) && (pcnt > 1))
2149                 {
2150                         if (pars[1])
2151                         {
2152                                 // pars[1] already set up and containing key from blog2[j]
2153                                 fn(pars,2,u);
2154                         }
2155                         else
2156                         {
2157                                 pars[1] = parameters[1];
2158                                 fn(pars,2,u);
2159                         }
2160                 }
2161                 else
2162                 {
2163                         fn(pars,pcnt-(end-start),u);
2164                 }
2165         }
2166
2167         return 1;
2168 }
2169
2170
2171
2172 void kill_link(userrec *user,const char* r)
2173 {
2174         user_hash::iterator iter = clientlist.find(user->nick);
2175         
2176         char reason[MAXBUF];
2177         
2178         strncpy(reason,r,MAXBUF);
2179
2180         if (strlen(reason)>MAXQUIT)
2181         {
2182                 reason[MAXQUIT-1] = '\0';
2183         }
2184
2185         log(DEBUG,"kill_link: %s '%s'",user->nick,reason);
2186         Write(user->fd,"ERROR :Closing link (%s@%s) [%s]",user->ident,user->host,reason);
2187         log(DEBUG,"closing fd %lu",(unsigned long)user->fd);
2188
2189         if (user->registered == 7) {
2190                 FOREACH_MOD OnUserQuit(user);
2191                 WriteCommonExcept(user,"QUIT :%s",reason);
2192
2193                 // Q token must go to ALL servers!!!
2194                 char buffer[MAXBUF];
2195                 snprintf(buffer,MAXBUF,"Q %s :%s",user->nick,reason);
2196                 NetSendToAll(buffer);
2197         }
2198
2199         FOREACH_MOD OnUserDisconnect(user);
2200
2201         if (user->fd > -1)
2202         {
2203                 FOREACH_MOD OnRawSocketClose(user->fd);
2204 #ifdef USE_KQUEUE
2205                 struct kevent ke;
2206                 EV_SET(&ke, user->fd, EVFILT_READ, EV_DELETE, 0, 0, NULL);
2207                 int i = kevent(kq, &ke, 1, 0, 0, NULL);
2208                 if (i == -1)
2209                 {
2210                         log(DEBUG,"kqueue: Failed to remove user from queue!");
2211                 }
2212 #endif
2213                 shutdown(user->fd,2);
2214                 close(user->fd);
2215         }
2216         
2217         if (user->registered == 7) {
2218                 WriteOpers("*** Client exiting: %s!%s@%s [%s]",user->nick,user->ident,user->host,reason);
2219                 AddWhoWas(user);
2220         }
2221
2222         if (user->registered == 7) {
2223                 purge_empty_chans(user);
2224         }
2225
2226         if (iter != clientlist.end())
2227         {
2228                 log(DEBUG,"deleting user hash value %lu",(unsigned long)user);
2229                 if (user->fd > -1)
2230                         fd_ref_table[user->fd] = NULL;
2231                 clientlist.erase(iter);
2232         }
2233         delete user;
2234 }
2235
2236 void kill_link_silent(userrec *user,const char* r)
2237 {
2238         user_hash::iterator iter = clientlist.find(user->nick);
2239         
2240         char reason[MAXBUF];
2241         
2242         strncpy(reason,r,MAXBUF);
2243
2244         if (strlen(reason)>MAXQUIT)
2245         {
2246                 reason[MAXQUIT-1] = '\0';
2247         }
2248
2249         log(DEBUG,"kill_link: %s '%s'",user->nick,reason);
2250         Write(user->fd,"ERROR :Closing link (%s@%s) [%s]",user->ident,user->host,reason);
2251         log(DEBUG,"closing fd %lu",(unsigned long)user->fd);
2252
2253         if (user->registered == 7) {
2254                 FOREACH_MOD OnUserQuit(user);
2255                 WriteCommonExcept(user,"QUIT :%s",reason);
2256
2257                 // Q token must go to ALL servers!!!
2258                 char buffer[MAXBUF];
2259                 snprintf(buffer,MAXBUF,"Q %s :%s",user->nick,reason);
2260                 NetSendToAll(buffer);
2261         }
2262
2263         FOREACH_MOD OnUserDisconnect(user);
2264
2265         if (user->fd > -1)
2266         {
2267                 FOREACH_MOD OnRawSocketClose(user->fd);
2268 #ifdef USE_KQUEUE
2269                 struct kevent ke;
2270                 EV_SET(&ke, user->fd, EVFILT_READ, EV_DELETE, 0, 0, NULL);
2271                 int i = kevent(kq, &ke, 1, 0, 0, NULL);
2272                 if (i == -1)
2273                 {
2274                         log(DEBUG,"kqueue: Failed to remove user from queue!");
2275                 }
2276 #endif
2277                 shutdown(user->fd,2);
2278                 close(user->fd);
2279         }
2280
2281         if (user->registered == 7) {
2282                 purge_empty_chans(user);
2283         }
2284         
2285         if (iter != clientlist.end())
2286         {
2287                 log(DEBUG,"deleting user hash value %lu",(unsigned long)user);
2288                 if (user->fd > -1)
2289                         fd_ref_table[user->fd] = NULL;
2290                 clientlist.erase(iter);
2291         }
2292         delete user;
2293 }
2294
2295
2296
2297 // looks up a users password for their connection class (<ALLOW>/<DENY> tags)
2298
2299 char* Passwd(userrec *user)
2300 {
2301         for (ClassVector::iterator i = Classes.begin(); i != Classes.end(); i++)
2302         {
2303                 if (match(user->host,i->host) && (i->type == CC_ALLOW))
2304                 {
2305                         return i->pass;
2306                 }
2307         }
2308         return "";
2309 }
2310
2311 bool IsDenied(userrec *user)
2312 {
2313         for (ClassVector::iterator i = Classes.begin(); i != Classes.end(); i++)
2314         {
2315                 if (match(user->host,i->host) && (i->type == CC_DENY))
2316                 {
2317                         return true;
2318                 }
2319         }
2320         return false;
2321 }
2322
2323
2324
2325
2326 /* sends out an error notice to all connected clients (not to be used
2327  * lightly!) */
2328
2329 void send_error(char *s)
2330 {
2331         log(DEBUG,"send_error: %s",s);
2332         for (user_hash::const_iterator i = clientlist.begin(); i != clientlist.end(); i++)
2333         {
2334                 if (isnick(i->second->nick))
2335                 {
2336                         WriteServ(i->second->fd,"NOTICE %s :%s",i->second->nick,s);
2337                 }
2338                 else
2339                 {
2340                         // fix - unregistered connections receive ERROR, not NOTICE
2341                         Write(i->second->fd,"ERROR :%s",s);
2342                 }
2343         }
2344 }
2345
2346 void Error(int status)
2347 {
2348         signal (SIGALRM, SIG_IGN);
2349         signal (SIGPIPE, SIG_IGN);
2350         signal (SIGTERM, SIG_IGN);
2351         signal (SIGABRT, SIG_IGN);
2352         signal (SIGSEGV, SIG_IGN);
2353         signal (SIGURG, SIG_IGN);
2354         signal (SIGKILL, SIG_IGN);
2355         log(DEFAULT,"*** fell down a pothole in the road to perfection ***");
2356         send_error("Error! Segmentation fault! save meeeeeeeeeeeeee *splat!*");
2357         Exit(status);
2358 }
2359
2360
2361 int main(int argc, char** argv)
2362 {
2363         Start();
2364         srand(time(NULL));
2365         log(DEBUG,"*** InspIRCd starting up!");
2366         if (!FileExists(CONFIG_FILE))
2367         {
2368                 printf("ERROR: Cannot open config file: %s\nExiting...\n",CONFIG_FILE);
2369                 log(DEFAULT,"main: no config");
2370                 printf("ERROR: Your config file is missing, this IRCd will self destruct in 10 seconds!\n");
2371                 Exit(ERROR);
2372         }
2373         if (argc > 1) {
2374                 for (int i = 1; i < argc; i++)
2375                 {
2376                         if (!strcmp(argv[i],"-nofork")) {
2377                                 nofork = true;
2378                         }
2379                         if (!strcmp(argv[i],"-wait")) {
2380                                 sleep(6);
2381                         }
2382                         if (!strcmp(argv[i],"-nolimit")) {
2383                                 unlimitcore = true;
2384                         }
2385                 }
2386         }
2387         strlcpy(MyExecutable,argv[0],MAXBUF);
2388         
2389         // initialize the lowercase mapping table
2390         for (int cn = 0; cn < 256; cn++)
2391                 lowermap[cn] = cn;
2392         // lowercase the uppercase chars
2393         for (int cn = 65; cn < 91; cn++)
2394                 lowermap[cn] = tolower(cn);
2395         // now replace the specific chars for scandanavian comparison
2396         lowermap['['] = '{';
2397         lowermap[']'] = '}';
2398         lowermap['\\'] = '|';
2399
2400         if (InspIRCd(argv,argc) == ERROR)
2401         {
2402                 log(DEFAULT,"main: daemon function bailed");
2403                 printf("ERROR: could not initialise. Shutting down.\n");
2404                 Exit(ERROR);
2405         }
2406         Exit(TRUE);
2407         return 0;
2408 }
2409
2410 template<typename T> inline string ConvToStr(const T &in)
2411 {
2412         stringstream tmp;
2413         if (!(tmp << in)) return string();
2414         return tmp.str();
2415 }
2416
2417 /* re-allocates a nick in the user_hash after they change nicknames,
2418  * returns a pointer to the new user as it may have moved */
2419
2420 userrec* ReHashNick(char* Old, char* New)
2421 {
2422         //user_hash::iterator newnick;
2423         user_hash::iterator oldnick = clientlist.find(Old);
2424
2425         log(DEBUG,"ReHashNick: %s %s",Old,New);
2426         
2427         if (!strcasecmp(Old,New))
2428         {
2429                 log(DEBUG,"old nick is new nick, skipping");
2430                 return oldnick->second;
2431         }
2432         
2433         if (oldnick == clientlist.end()) return NULL; /* doesnt exist */
2434
2435         log(DEBUG,"ReHashNick: Found hashed nick %s",Old);
2436
2437         userrec* olduser = oldnick->second;
2438         clientlist[New] = olduser;
2439         clientlist.erase(oldnick);
2440
2441         log(DEBUG,"ReHashNick: Nick rehashed as %s",New);
2442         
2443         return clientlist[New];
2444 }
2445
2446 /* adds or updates an entry in the whowas list */
2447 void AddWhoWas(userrec* u)
2448 {
2449         whowas_hash::iterator iter = whowas.find(u->nick);
2450         WhoWasUser *a = new WhoWasUser();
2451         strlcpy(a->nick,u->nick,NICKMAX);
2452         strlcpy(a->ident,u->ident,15);
2453         strlcpy(a->dhost,u->dhost,160);
2454         strlcpy(a->host,u->host,160);
2455         strlcpy(a->fullname,u->fullname,128);
2456         strlcpy(a->server,u->server,256);
2457         a->signon = u->signon;
2458
2459         /* MAX_WHOWAS:   max number of /WHOWAS items
2460          * WHOWAS_STALE: number of hours before a WHOWAS item is marked as stale and
2461          *               can be replaced by a newer one
2462          */
2463         
2464         if (iter == whowas.end())
2465         {
2466                 if (whowas.size() >= WHOWAS_MAX)
2467                 {
2468                         for (whowas_hash::iterator i = whowas.begin(); i != whowas.end(); i++)
2469                         {
2470                                 // 3600 seconds in an hour ;)
2471                                 if ((i->second->signon)<(TIME-(WHOWAS_STALE*3600)))
2472                                 {
2473                                         // delete the old one
2474                                         if (i->second) delete i->second;
2475                                         // replace with new one
2476                                         i->second = a;
2477                                         log(DEBUG,"added WHOWAS entry, purged an old record");
2478                                         return;
2479                                 }
2480                         }
2481                         // no space left and user doesnt exist. Don't leave ram in use!
2482                         log(DEBUG,"Not able to update whowas (list at WHOWAS_MAX entries and trying to add new?), freeing excess ram");
2483                         delete a;
2484                 }
2485                 else
2486                 {
2487                         log(DEBUG,"added fresh WHOWAS entry");
2488                         whowas[a->nick] = a;
2489                 }
2490         }
2491         else
2492         {
2493                 log(DEBUG,"updated WHOWAS entry");
2494                 if (iter->second) delete iter->second;
2495                 iter->second = a;
2496         }
2497 }
2498
2499
2500 /* add a client connection to the sockets list */
2501 void AddClient(int socket, char* host, int port, bool iscached, char* ip)
2502 {
2503         string tempnick;
2504         char tn2[MAXBUF];
2505         user_hash::iterator iter;
2506
2507         tempnick = ConvToStr(socket) + "-unknown";
2508         sprintf(tn2,"%lu-unknown",(unsigned long)socket);
2509
2510         iter = clientlist.find(tempnick);
2511
2512         // fix by brain.
2513         // as these nicknames are 'RFC impossible', we can be sure nobody is going to be
2514         // using one as a registered connection. As theyre per fd, we can also safely assume
2515         // that we wont have collisions. Therefore, if the nick exists in the list, its only
2516         // used by a dead socket, erase the iterator so that the new client may reclaim it.
2517         // this was probably the cause of 'server ignores me when i hammer it with reconnects'
2518         // issue in earlier alphas/betas
2519         if (iter != clientlist.end())
2520         {
2521                 userrec* goner = iter->second;
2522                 delete goner;
2523                 clientlist.erase(iter);
2524         }
2525
2526         /*
2527          * It is OK to access the value here this way since we know
2528          * it exists, we just created it above.
2529          *
2530          * At NO other time should you access a value in a map or a
2531          * hash_map this way.
2532          */
2533         clientlist[tempnick] = new userrec();
2534
2535         NonBlocking(socket);
2536         log(DEBUG,"AddClient: %lu %s %d %s",(unsigned long)socket,host,port,ip);
2537
2538         clientlist[tempnick]->fd = socket;
2539         strncpy(clientlist[tempnick]->nick, tn2,NICKMAX);
2540         strncpy(clientlist[tempnick]->host, host,160);
2541         strncpy(clientlist[tempnick]->dhost, host,160);
2542         strncpy(clientlist[tempnick]->server, ServerName,256);
2543         strncpy(clientlist[tempnick]->ident, "unknown",15);
2544         clientlist[tempnick]->registered = 0;
2545         clientlist[tempnick]->signon = TIME+dns_timeout;
2546         clientlist[tempnick]->lastping = 1;
2547         clientlist[tempnick]->port = port;
2548         strncpy(clientlist[tempnick]->ip,ip,16);
2549
2550         // set the registration timeout for this user
2551         unsigned long class_regtimeout = 90;
2552         int class_flood = 0;
2553         long class_threshold = 5;
2554         long class_sqmax = 262144;      // 256kb
2555         long class_rqmax = 4096;        // 4k
2556
2557         for (ClassVector::iterator i = Classes.begin(); i != Classes.end(); i++)
2558         {
2559                 if (match(clientlist[tempnick]->host,i->host) && (i->type == CC_ALLOW))
2560                 {
2561                         class_regtimeout = (unsigned long)i->registration_timeout;
2562                         class_flood = i->flood;
2563                         clientlist[tempnick]->pingmax = i->pingtime;
2564                         class_threshold = i->threshold;
2565                         class_sqmax = i->sendqmax;
2566                         class_rqmax = i->recvqmax;
2567                         break;
2568                 }
2569         }
2570
2571         clientlist[tempnick]->nping = TIME+clientlist[tempnick]->pingmax+dns_timeout;
2572         clientlist[tempnick]->timeout = TIME+class_regtimeout;
2573         clientlist[tempnick]->flood = class_flood;
2574         clientlist[tempnick]->threshold = class_threshold;
2575         clientlist[tempnick]->sendqmax = class_sqmax;
2576         clientlist[tempnick]->recvqmax = class_rqmax;
2577
2578         for (int i = 0; i < MAXCHANS; i++)
2579         {
2580                 clientlist[tempnick]->chans[i].channel = NULL;
2581                 clientlist[tempnick]->chans[i].uc_modes = 0;
2582         }
2583
2584         if (clientlist.size() == MAXCLIENTS)
2585         {
2586                 kill_link(clientlist[tempnick],"No more connections allowed in this class");
2587                 return;
2588         }
2589
2590         // this is done as a safety check to keep the file descriptors within range of fd_ref_table.
2591         // its a pretty big but for the moment valid assumption:
2592         // file descriptors are handed out starting at 0, and are recycled as theyre freed.
2593         // therefore if there is ever an fd over 65535, 65536 clients must be connected to the
2594         // irc server at once (or the irc server otherwise initiating this many connections, files etc)
2595         // which for the time being is a physical impossibility (even the largest networks dont have more
2596         // than about 10,000 users on ONE server!)
2597         if (socket > 65534)
2598         {
2599                 kill_link(clientlist[tempnick],"Server is full");
2600                 return;
2601         }
2602                 
2603
2604         char* e = matches_exception(ip);
2605         if (!e)
2606         {
2607                 char* r = matches_zline(ip);
2608                 if (r)
2609                 {
2610                         char reason[MAXBUF];
2611                         snprintf(reason,MAXBUF,"Z-Lined: %s",r);
2612                         kill_link(clientlist[tempnick],reason);
2613                         return;
2614                 }
2615         }
2616         fd_ref_table[socket] = clientlist[tempnick];
2617
2618 #ifdef USE_KQUEUE
2619         struct kevent ke;
2620         log(DEBUG,"kqueue: Add user to events, kq=%d socket=%d",kq,socket);
2621         EV_SET(&ke, socket, EVFILT_READ, EV_ADD, 0, 0, NULL);
2622         int i = kevent(kq, &ke, 1, 0, 0, NULL);
2623         if (i == -1)
2624         {
2625                 switch (errno)
2626                 {
2627                         case EACCES:
2628                                 log(DEBUG,"kqueue: EACCES");
2629                         break;
2630                         case EFAULT:
2631                                 log(DEBUG,"kqueue: EFAULT");
2632                         break;
2633                         case EBADF:
2634                                 log(DEBUG,"kqueue: EBADF=%d",ke.ident);
2635                         break;
2636                         case EINTR:
2637                                 log(DEBUG,"kqueue: EINTR");
2638                         break;
2639                         case EINVAL:
2640                                 log(DEBUG,"kqueue: EINVAL");
2641                         break;
2642                         case ENOENT:
2643                                 log(DEBUG,"kqueue: ENOENT");
2644                         break;
2645                         case ENOMEM:
2646                                 log(DEBUG,"kqueue: ENOMEM");
2647                         break;
2648                         case ESRCH:
2649                                 log(DEBUG,"kqueue: ESRCH");
2650                         break;
2651                         default:
2652                                 log(DEBUG,"kqueue: UNKNOWN!");
2653                         break;
2654                 }
2655                 log(DEBUG,"kqueue: Failed to add user to queue!");
2656         }
2657
2658 #endif
2659 }
2660
2661 // this function counts all users connected, wether they are registered or NOT.
2662 int usercnt(void)
2663 {
2664         return clientlist.size();
2665 }
2666
2667 // this counts only registered users, so that the percentages in /MAP don't mess up when users are sitting in an unregistered state
2668 int registered_usercount(void)
2669 {
2670         int c = 0;
2671         for (user_hash::const_iterator i = clientlist.begin(); i != clientlist.end(); i++)
2672         {
2673                 if ((i->second->fd) && (isnick(i->second->nick))) c++;
2674         }
2675         return c;
2676 }
2677
2678 int usercount_invisible(void)
2679 {
2680         int c = 0;
2681
2682         for (user_hash::const_iterator i = clientlist.begin(); i != clientlist.end(); i++)
2683         {
2684                 if ((i->second->fd) && (isnick(i->second->nick)) && (strchr(i->second->modes,'i'))) c++;
2685         }
2686         return c;
2687 }
2688
2689 int usercount_opers(void)
2690 {
2691         int c = 0;
2692
2693         for (user_hash::const_iterator i = clientlist.begin(); i != clientlist.end(); i++)
2694         {
2695                 if ((i->second->fd) && (isnick(i->second->nick)) && (strchr(i->second->modes,'o'))) c++;
2696         }
2697         return c;
2698 }
2699
2700 int usercount_unknown(void)
2701 {
2702         int c = 0;
2703
2704         for (user_hash::const_iterator i = clientlist.begin(); i != clientlist.end(); i++)
2705         {
2706                 if ((i->second->fd) && (i->second->registered != 7))
2707                         c++;
2708         }
2709         return c;
2710 }
2711
2712 long chancount(void)
2713 {
2714         return chanlist.size();
2715 }
2716
2717 long count_servs(void)
2718 {
2719         int c = 0;
2720         for (int i = 0; i < 32; i++)
2721         {
2722                 if (me[i] != NULL)
2723                 {
2724                         for (vector<ircd_connector>::iterator j = me[i]->connectors.begin(); j != me[i]->connectors.end(); j++)
2725                         {
2726                                 if (strcasecmp(j->GetServerName().c_str(),ServerName))
2727                                 {
2728                                         c++;
2729                                 }
2730                         }
2731                 }
2732         }
2733         return c;
2734 }
2735
2736 long servercount(void)
2737 {
2738         return count_servs()+1;
2739 }
2740
2741 long local_count()
2742 {
2743         int c = 0;
2744         for (user_hash::const_iterator i = clientlist.begin(); i != clientlist.end(); i++)
2745         {
2746                 if ((i->second->fd) && (isnick(i->second->nick)) && (!strcasecmp(i->second->server,ServerName))) c++;
2747         }
2748         return c;
2749 }
2750
2751
2752 void ShowMOTD(userrec *user)
2753 {
2754         char buf[65536];
2755         std::string WholeMOTD = "";
2756         if (!MOTD.size())
2757         {
2758                 WriteServ(user->fd,"422 %s :Message of the day file is missing.",user->nick);
2759                 return;
2760         }
2761         snprintf(buf,65535,":%s 375 %s :- %s message of the day\r\n", ServerName, user->nick, ServerName);
2762         WholeMOTD = WholeMOTD + buf;
2763         for (int i = 0; i != MOTD.size(); i++)
2764         {
2765                 snprintf(buf,65535,":%s 372 %s :- %s\r\n", ServerName, user->nick, MOTD[i].c_str());
2766                 WholeMOTD = WholeMOTD + buf;
2767         }
2768         snprintf(buf,65535,":%s 376 %s :End of message of the day.\r\n", ServerName, user->nick);
2769         WholeMOTD = WholeMOTD + buf;
2770         // only one write operation
2771         user->AddWriteBuf(WholeMOTD);
2772         statsSent += WholeMOTD.length();
2773 }
2774
2775 void ShowRULES(userrec *user)
2776 {
2777         if (!RULES.size())
2778         {
2779                 WriteServ(user->fd,"NOTICE %s :Rules file is missing.",user->nick);
2780                 return;
2781         }
2782         WriteServ(user->fd,"NOTICE %s :%s rules",user->nick,ServerName);
2783         for (int i = 0; i != RULES.size(); i++)
2784         {
2785                                 WriteServ(user->fd,"NOTICE %s :%s",user->nick,RULES[i].c_str());
2786         }
2787         WriteServ(user->fd,"NOTICE %s :End of %s rules.",user->nick,ServerName);
2788 }
2789
2790 /* shows the message of the day, and any other on-logon stuff */
2791 void FullConnectUser(userrec* user)
2792 {
2793         statsConnects++;
2794         user->idle_lastmsg = TIME;
2795         log(DEBUG,"ConnectUser: %s",user->nick);
2796
2797         if ((strcmp(Passwd(user),"")) && (!user->haspassed))
2798         {
2799                 kill_link(user,"Invalid password");
2800                 return;
2801         }
2802         if (IsDenied(user))
2803         {
2804                 kill_link(user,"Unauthorised connection");
2805                 return;
2806         }
2807
2808         char match_against[MAXBUF];
2809         snprintf(match_against,MAXBUF,"%s@%s",user->ident,user->host);
2810         char* e = matches_exception(match_against);
2811         if (!e)
2812         {
2813                 char* r = matches_gline(match_against);
2814                 if (r)
2815                 {
2816                         char reason[MAXBUF];
2817                         snprintf(reason,MAXBUF,"G-Lined: %s",r);
2818                         kill_link_silent(user,reason);
2819                         return;
2820                 }
2821                 r = matches_kline(user->host);
2822                 if (r)
2823                 {
2824                         char reason[MAXBUF];
2825                         snprintf(reason,MAXBUF,"K-Lined: %s",r);
2826                         kill_link_silent(user,reason);
2827                         return;
2828                 }
2829         }
2830
2831         // fix by brain: move this below the xline checks to prevent spurious quits going onto the net that dont belong
2832         user->registered = 7;
2833
2834         WriteServ(user->fd,"NOTICE Auth :Welcome to \002%s\002!",Network);
2835         WriteServ(user->fd,"001 %s :Welcome to the %s IRC Network %s!%s@%s",user->nick,Network,user->nick,user->ident,user->host);
2836         WriteServ(user->fd,"002 %s :Your host is %s, running version %s",user->nick,ServerName,VERSION);
2837         WriteServ(user->fd,"003 %s :This server was created %s %s",user->nick,__TIME__,__DATE__);
2838         WriteServ(user->fd,"004 %s %s %s iowghraAsORVSxNCWqBzvdHtGI lvhopsmntikrRcaqOALQbSeKVfHGCuzN",user->nick,ServerName,VERSION);
2839         // the neatest way to construct the initial 005 numeric, considering the number of configure constants to go in it...
2840         std::stringstream v;
2841         v << "MESHED WALLCHOPS MODES=13 CHANTYPES=# PREFIX=(ohv)@%+ MAP SAFELIST MAXCHANNELS=" << MAXCHANS;
2842         v << " MAXBANS=60 NICKLEN=" << NICKMAX;
2843         v << " TOPICLEN=307 KICKLEN=307 MAXTARGETS=20 AWAYLEN=307 CHANMODES=ohvb,k,l,psmnti NETWORK=";
2844         v << Network;
2845         std::string data005 = v.str();
2846         FOREACH_MOD On005Numeric(data005);
2847         // anfl @ #ratbox, efnet reminded me that according to the RFC this cant contain more than 13 tokens per line...
2848         // so i'd better split it :)
2849         std::stringstream out(data005);
2850         std::string token = "";
2851         std::string line5 = "";
2852         int token_counter = 0;
2853         while (!out.eof())
2854         {
2855                 out >> token;
2856                 line5 = line5 + token + " ";
2857                 token_counter++;
2858                 if ((token_counter >= 13) || (out.eof() == true))
2859                 {
2860                         WriteServ(user->fd,"005 %s %s:are supported by this server",user->nick,line5.c_str());
2861                         line5 = "";
2862                         token_counter = 0;
2863                 }
2864         }
2865         ShowMOTD(user);
2866
2867         char buffer[MAXBUF];
2868         snprintf(buffer,MAXBUF,"N %lu %s %s %s %s +%s %s %s :%s",(unsigned long)user->age,user->nick,user->host,user->dhost,user->ident,user->modes,user->ip,ServerName,user->fullname);
2869         NetSendToAll(buffer);
2870
2871         // fix by brain: these should be AFTER the N token, so other servers know what the HELL we're on about... :)
2872         FOREACH_MOD OnUserConnect(user);
2873         WriteOpers("*** Client connecting on port %lu: %s!%s@%s [%s]",(unsigned long)user->port,user->nick,user->ident,user->host,user->ip);
2874 }
2875
2876
2877 // this returns 1 when all modules are satisfied that the user should be allowed onto the irc server
2878 // (until this returns true, a user will block in the waiting state, waiting to connect up to the
2879 // registration timeout maximum seconds)
2880 bool AllModulesReportReady(userrec* user)
2881 {
2882         for (int i = 0; i <= MODCOUNT; i++)
2883         {
2884                 int res = modules[i]->OnCheckReady(user);
2885                         if (!res)
2886                                 return false;
2887         }
2888         return true;
2889 }
2890
2891 /* shows the message of the day, and any other on-logon stuff */
2892 void ConnectUser(userrec *user)
2893 {
2894         // dns is already done, things are fast. no need to wait for dns to complete just pass them straight on
2895         if ((user->dns_done) && (user->registered >= 3) && (AllModulesReportReady(user)))
2896         {
2897                 FullConnectUser(user);
2898         }
2899 }
2900
2901 std::string GetVersionString()
2902 {
2903         char Revision[] = "$Revision$";
2904         char versiondata[MAXBUF];
2905         char *s1 = Revision;
2906         char *savept;
2907         char *v2 = strtok_r(s1," ",&savept);
2908         s1 = savept;
2909         v2 = strtok_r(s1," ",&savept);
2910         s1 = savept;
2911 #ifdef USE_KQUEUE
2912         char socketengine[] = "kqueue";
2913 #else
2914         char socketengine[] = "select";
2915 #endif
2916         snprintf(versiondata,MAXBUF,"%s Rev. %s %s :%s (O=%lu) [SE=%s]",VERSION,v2,ServerName,SYSTEM,(unsigned long)OPTIMISATION,socketengine);
2917         return versiondata;
2918 }
2919
2920 void handle_version(char **parameters, int pcnt, userrec *user)
2921 {
2922         if (!pcnt)
2923         {
2924                 WriteServ(user->fd,"351 %s :%s",user->nick,GetVersionString().c_str());
2925         }
2926         else
2927         {
2928                 if (!strcmp(parameters[0],"*"))
2929                 {
2930                         for (int j = 0; j < 32; j++)
2931                         {
2932                                 if (me[j] != NULL)
2933                                 {
2934                                         for (int x = 0; x < me[j]->connectors.size(); x++)
2935                                         {
2936                                                 WriteServ(user->fd,"351 %s :Server %d:%d (%s): %s",user->nick,j,x,me[j]->connectors[x].GetServerName().c_str(),me[j]->connectors[x].GetVersionString().c_str());
2937                                         }
2938                                 }
2939                         }
2940                         return;
2941                 }
2942                 if (match(ServerName,parameters[0]))
2943                 {
2944                         WriteServ(user->fd,"351 %s :%s",user->nick,GetVersionString().c_str());
2945                         return;
2946                 }
2947                 bool displayed = false, found = false;
2948                 for (int j = 0; j < 32; j++)
2949                 {
2950                         if (me[j] != NULL)
2951                         {
2952                                 for (int x = 0; x < me[j]->connectors.size(); x++)
2953                                 {
2954                                         if (match(me[j]->connectors[x].GetServerName().c_str(),parameters[0]))
2955                                         {
2956                                                 found = true;
2957                                                 if ((me[j]->connectors[x].GetVersionString() != "") && (!displayed))
2958                                                 {
2959                                                         displayed = true;
2960                                                         WriteServ(user->fd,"351 %s :%s",user->nick,me[j]->connectors[x].GetVersionString().c_str());
2961                                                 }
2962                                         }
2963                                 }
2964                         }
2965                 }
2966                 if ((!displayed) && (found))
2967                 {
2968                         WriteServ(user->fd,"402 %s %s :Server %s has no version information",user->nick,parameters[0],parameters[0]);
2969                         return;
2970                 }
2971                 if (!found)
2972                 {
2973                         WriteServ(user->fd,"402 %s %s :No such server",user->nick,parameters[0]);
2974                 }
2975         }
2976         return;
2977 }
2978
2979
2980 // calls a handler function for a command
2981
2982 void call_handler(const char* commandname,char **parameters, int pcnt, userrec *user)
2983 {
2984                 for (int i = 0; i < cmdlist.size(); i++)
2985                 {
2986                         if (!strcasecmp(cmdlist[i].command,commandname))
2987                         {
2988                                 if (cmdlist[i].handler_function)
2989                                 {
2990                                         if (pcnt>=cmdlist[i].min_params)
2991                                         {
2992                                                 if (strchr(user->modes,cmdlist[i].flags_needed))
2993                                                 {
2994                                                         cmdlist[i].handler_function(parameters,pcnt,user);
2995                                                 }
2996                                         }
2997                                 }
2998                         }
2999                 }
3000 }
3001
3002 void DoSplitEveryone()
3003 {
3004         bool go_again = true;
3005         while (go_again)
3006         {
3007                 go_again = false;
3008                 for (int i = 0; i < 32; i++)
3009                 {
3010                         if (me[i] != NULL)
3011                         {
3012                                 for (vector<ircd_connector>::iterator j = me[i]->connectors.begin(); j != me[i]->connectors.end(); j++)
3013                                 {
3014                                         if (strcasecmp(j->GetServerName().c_str(),ServerName))
3015                                         {
3016                                                 j->routes.clear();
3017                                                 j->CloseConnection();
3018                                                 me[i]->connectors.erase(j);
3019                                                 go_again = true;
3020                                                 break;
3021                                         }
3022                                 }
3023                         }
3024                 }
3025         }
3026         log(DEBUG,"Removed server. Will remove clients...");
3027         // iterate through the userlist and remove all users on this server.
3028         // because we're dealing with a mesh, we dont have to deal with anything
3029         // "down-route" from this server (nice huh)
3030         go_again = true;
3031         char reason[MAXBUF];
3032         while (go_again)
3033         {
3034                 go_again = false;
3035                 for (user_hash::const_iterator u = clientlist.begin(); u != clientlist.end(); u++)
3036                 {
3037                         if (strcasecmp(u->second->server,ServerName))
3038                         {
3039                                 snprintf(reason,MAXBUF,"%s %s",ServerName,u->second->server);
3040                                 kill_link(u->second,reason);
3041                                 go_again = true;
3042                                 break;
3043                         }
3044                 }
3045         }
3046 }
3047
3048
3049
3050 char islast(const char* s)
3051 {
3052         char c = '`';
3053         for (int j = 0; j < 32; j++)
3054         {
3055                 if (me[j] != NULL)
3056                 {
3057                         for (int k = 0; k < me[j]->connectors.size(); k++)
3058                         {
3059                                 if (strcasecmp(me[j]->connectors[k].GetServerName().c_str(),s))
3060                                 {
3061                                         c = '|';
3062                                 }
3063                                 if (!strcasecmp(me[j]->connectors[k].GetServerName().c_str(),s))
3064                                 {
3065                                         c = '`';
3066                                 }
3067                         }
3068                 }
3069         }
3070         return c;
3071 }
3072
3073 long map_count(const char* s)
3074 {
3075         int c = 0;
3076         for (user_hash::const_iterator i = clientlist.begin(); i != clientlist.end(); i++)
3077         {
3078                 if ((i->second->fd) && (isnick(i->second->nick)) && (!strcasecmp(i->second->server,s))) c++;
3079         }
3080         return c;
3081 }
3082
3083
3084 void force_nickchange(userrec* user,const char* newnick)
3085 {
3086         char nick[MAXBUF];
3087         int MOD_RESULT = 0;
3088         
3089         strcpy(nick,"");
3090
3091         FOREACH_RESULT(OnUserPreNick(user,newnick));
3092         if (MOD_RESULT) {
3093                 statsCollisions++;
3094                 kill_link(user,"Nickname collision");
3095                 return;
3096         }
3097         if (matches_qline(newnick))
3098         {
3099                 statsCollisions++;
3100                 kill_link(user,"Nickname collision");
3101                 return;
3102         }
3103         
3104         if (user)
3105         {
3106                 if (newnick)
3107                 {
3108                         strncpy(nick,newnick,MAXBUF);
3109                 }
3110                 if (user->registered == 7)
3111                 {
3112                         char* pars[1];
3113                         pars[0] = nick;
3114                         handle_nick(pars,1,user);
3115                 }
3116         }
3117 }
3118                                 
3119
3120 int process_parameters(char **command_p,char *parameters)
3121 {
3122         int j = 0;
3123         int q = strlen(parameters);
3124         if (!q)
3125         {
3126                 /* no parameters, command_p invalid! */
3127                 return 0;
3128         }
3129         if (parameters[0] == ':')
3130         {
3131                 command_p[0] = parameters+1;
3132                 return 1;
3133         }
3134         if (q)
3135         {
3136                 if ((strchr(parameters,' ')==NULL) || (parameters[0] == ':'))
3137                 {
3138                         /* only one parameter */
3139                         command_p[0] = parameters;
3140                         if (parameters[0] == ':')
3141                         {
3142                                 if (strchr(parameters,' ') != NULL)
3143                                 {
3144                                         command_p[0]++;
3145                                 }
3146                         }
3147                         return 1;
3148                 }
3149         }
3150         command_p[j++] = parameters;
3151         for (int i = 0; i <= q; i++)
3152         {
3153                 if (parameters[i] == ' ')
3154                 {
3155                         command_p[j++] = parameters+i+1;
3156                         parameters[i] = '\0';
3157                         if (command_p[j-1][0] == ':')
3158                         {
3159                                 *command_p[j-1]++; /* remove dodgy ":" */
3160                                 break;
3161                                 /* parameter like this marks end of the sequence */
3162                         }
3163                 }
3164         }
3165         return j; /* returns total number of items in the list */
3166 }
3167
3168 void process_command(userrec *user, char* cmd)
3169 {
3170         char *parameters;
3171         char *command;
3172         char *command_p[127];
3173         char p[MAXBUF], temp[MAXBUF];
3174         int j, items, cmd_found;
3175
3176         for (int i = 0; i < 127; i++)
3177                 command_p[i] = NULL;
3178
3179         if (!user)
3180         {
3181                 return;
3182         }
3183         if (!cmd)
3184         {
3185                 return;
3186         }
3187         if (!cmd[0])
3188         {
3189                 return;
3190         }
3191         
3192         int total_params = 0;
3193         if (strlen(cmd)>2)
3194         {
3195                 for (int q = 0; q < strlen(cmd)-1; q++)
3196                 {
3197                         if ((cmd[q] == ' ') && (cmd[q+1] == ':'))
3198                         {
3199                                 total_params++;
3200                                 // found a 'trailing', we dont count them after this.
3201                                 break;
3202                         }
3203                         if (cmd[q] == ' ')
3204                                 total_params++;
3205                 }
3206         }
3207
3208         // another phidjit bug...
3209         if (total_params > 126)
3210         {
3211                 *(strchr(cmd,' ')) = '\0';
3212                 WriteServ(user->fd,"421 %s %s :Too many parameters given",user->nick,cmd);
3213                 return;
3214         }
3215
3216         strlcpy(temp,cmd,MAXBUF);
3217         
3218         std::string tmp = cmd;
3219         for (int i = 0; i <= MODCOUNT; i++)
3220         {
3221                 std::string oldtmp = tmp;
3222                 modules[i]->OnServerRaw(tmp,true,user);
3223                 if (oldtmp != tmp)
3224                 {
3225                         log(DEBUG,"A Module changed the input string!");
3226                         log(DEBUG,"New string: %s",tmp.c_str());
3227                         log(DEBUG,"Old string: %s",oldtmp.c_str());
3228                         break;
3229                 }
3230         }
3231         strlcpy(cmd,tmp.c_str(),MAXBUF);
3232         strlcpy(temp,cmd,MAXBUF);
3233
3234         if (!strchr(cmd,' '))
3235         {
3236                 /* no parameters, lets skip the formalities and not chop up
3237                  * the string */
3238                 log(DEBUG,"About to preprocess command with no params");
3239                 items = 0;
3240                 command_p[0] = NULL;
3241                 parameters = NULL;
3242                 for (int i = 0; i <= strlen(cmd); i++)
3243                 {
3244                         cmd[i] = toupper(cmd[i]);
3245                 }
3246                 command = cmd;
3247         }
3248         else
3249         {
3250                 strcpy(cmd,"");
3251                 j = 0;
3252                 /* strip out extraneous linefeeds through mirc's crappy pasting (thanks Craig) */
3253                 for (int i = 0; i < strlen(temp); i++)
3254                 {
3255                         if ((temp[i] != 10) && (temp[i] != 13) && (temp[i] != 0) && (temp[i] != 7))
3256                         {
3257                                 cmd[j++] = temp[i];
3258                                 cmd[j] = 0;
3259                         }
3260                 }
3261                 /* split the full string into a command plus parameters */
3262                 parameters = p;
3263                 strcpy(p," ");
3264                 command = cmd;
3265                 if (strchr(cmd,' '))
3266                 {
3267                         for (int i = 0; i <= strlen(cmd); i++)
3268                         {
3269                                 /* capitalise the command ONLY, leave params intact */
3270                                 cmd[i] = toupper(cmd[i]);
3271                                 /* are we nearly there yet?! :P */
3272                                 if (cmd[i] == ' ')
3273                                 {
3274                                         command = cmd;
3275                                         parameters = cmd+i+1;
3276                                         cmd[i] = '\0';
3277                                         break;
3278                                 }
3279                         }
3280                 }
3281                 else
3282                 {
3283                         for (int i = 0; i <= strlen(cmd); i++)
3284                         {
3285                                 cmd[i] = toupper(cmd[i]);
3286                         }
3287                 }
3288
3289         }
3290         cmd_found = 0;
3291         
3292         if (strlen(command)>MAXCOMMAND)
3293         {
3294                 WriteServ(user->fd,"421 %s %s :Command too long",user->nick,command);
3295                 return;
3296         }
3297         
3298         for (int x = 0; x < strlen(command); x++)
3299         {
3300                 if (((command[x] < 'A') || (command[x] > 'Z')) && (command[x] != '.'))
3301                 {
3302                         if (((command[x] < '0') || (command[x]> '9')) && (command[x] != '-'))
3303                         {
3304                                 if (strchr("@!\"$%^&*(){}[]_=+;:'#~,<>/?\\|`",command[x]))
3305                                 {
3306                                         statsUnknown++;
3307                                         WriteServ(user->fd,"421 %s %s :Unknown command",user->nick,command);
3308                                         return;
3309                                 }
3310                         }
3311                 }
3312         }
3313
3314         for (int i = 0; i != cmdlist.size(); i++)
3315         {
3316                 if (cmdlist[i].command[0])
3317                 {
3318                         if (strlen(command)>=(strlen(cmdlist[i].command))) if (!strncmp(command, cmdlist[i].command,MAXCOMMAND))
3319                         {
3320                                 if (parameters)
3321                                 {
3322                                         if (parameters[0])
3323                                         {
3324                                                 items = process_parameters(command_p,parameters);
3325                                         }
3326                                         else
3327                                         {
3328                                                 items = 0;
3329                                                 command_p[0] = NULL;
3330                                         }
3331                                 }
3332                                 else
3333                                 {
3334                                         items = 0;
3335                                         command_p[0] = NULL;
3336                                 }
3337                                 
3338                                 if (user)
3339                                 {
3340                                         /* activity resets the ping pending timer */
3341                                         user->nping = TIME + user->pingmax;
3342                                         if ((items) < cmdlist[i].min_params)
3343                                         {
3344                                                 log(DEBUG,"process_command: not enough parameters: %s %s",user->nick,command);
3345                                                 WriteServ(user->fd,"461 %s %s :Not enough parameters",user->nick,command);
3346                                                 return;
3347                                         }
3348                                         if ((!strchr(user->modes,cmdlist[i].flags_needed)) && (cmdlist[i].flags_needed))
3349                                         {
3350                                                 log(DEBUG,"process_command: permission denied: %s %s",user->nick,command);
3351                                                 WriteServ(user->fd,"481 %s :Permission Denied- You do not have the required operator privilages",user->nick);
3352                                                 cmd_found = 1;
3353                                                 return;
3354                                         }
3355                                         if ((cmdlist[i].flags_needed) && (!user->HasPermission(command)))
3356                                         {
3357                                                 log(DEBUG,"process_command: permission denied: %s %s",user->nick,command);
3358                                                 WriteServ(user->fd,"481 %s :Permission Denied- Oper type %s does not have access to command %s",user->nick,user->oper,command);
3359                                                 cmd_found = 1;
3360                                                 return;
3361                                         }
3362                                         /* if the command isnt USER, PASS, or NICK, and nick is empty,
3363                                          * deny command! */
3364                                         if ((strncmp(command,"USER",4)) && (strncmp(command,"NICK",4)) && (strncmp(command,"PASS",4)))
3365                                         {
3366                                                 if ((!isnick(user->nick)) || (user->registered != 7))
3367                                                 {
3368                                                         log(DEBUG,"process_command: not registered: %s %s",user->nick,command);
3369                                                         WriteServ(user->fd,"451 %s :You have not registered",command);
3370                                                         return;
3371                                                 }
3372                                         }
3373                                         if ((user->registered == 7) && (!strchr(user->modes,'o')))
3374                                         {
3375                                                 char* mycmd;
3376                                                 char* savept2;
3377                                                 mycmd = strtok_r(DisabledCommands," ",&savept2);
3378                                                 while (mycmd)
3379                                                 {
3380                                                         if (!strcasecmp(mycmd,command))
3381                                                         {
3382                                                                 // command is disabled!
3383                                                                 WriteServ(user->fd,"421 %s %s :This command has been disabled.",user->nick,command);
3384                                                                 return;
3385                                                         }
3386                                                         mycmd = strtok_r(NULL," ",&savept2);
3387                                                 }
3388         
3389
3390                                         }
3391                                         if ((user->registered == 7) || (!strncmp(command,"USER",4)) || (!strncmp(command,"NICK",4)) || (!strncmp(command,"PASS",4)))
3392                                         {
3393                                                 if (cmdlist[i].handler_function)
3394                                                 {
3395                                                         
3396                                                         /* ikky /stats counters */
3397                                                         if (temp)
3398                                                         {
3399                                                                 cmdlist[i].use_count++;
3400                                                                 cmdlist[i].total_bytes+=strlen(temp);
3401                                                         }
3402
3403                                                         int MOD_RESULT = 0;
3404                                                         FOREACH_RESULT(OnPreCommand(command,command_p,items,user));
3405                                                         if (MOD_RESULT == 1) {
3406                                                                 return;
3407                                                         }
3408
3409                                                         /* WARNING: nothing may come after the
3410                                                          * command handler call, as the handler
3411                                                          * may free the user structure! */
3412
3413                                                         cmdlist[i].handler_function(command_p,items,user);
3414                                                 }
3415                                                 return;
3416                                         }
3417                                         else
3418                                         {
3419                                                 WriteServ(user->fd,"451 %s :You have not registered",command);
3420                                                 return;
3421                                         }
3422                                 }
3423                                 cmd_found = 1;
3424                         }
3425                 }
3426         }
3427         if ((!cmd_found) && (user))
3428         {
3429                 statsUnknown++;
3430                 WriteServ(user->fd,"421 %s %s :Unknown command",user->nick,command);
3431         }
3432 }
3433
3434
3435 void createcommand(char* cmd, handlerfunc f, char flags, int minparams,char* source)
3436 {
3437         command_t comm;
3438         /* create the command and push it onto the table */     
3439         strlcpy(comm.command,cmd,MAXBUF);
3440         strlcpy(comm.source,source,MAXBUF);
3441         comm.handler_function = f;
3442         comm.flags_needed = flags;
3443         comm.min_params = minparams;
3444         comm.use_count = 0;
3445         comm.total_bytes = 0;
3446         cmdlist.push_back(comm);
3447         log(DEBUG,"Added command %s (%lu parameters)",cmd,(unsigned long)minparams);
3448 }
3449
3450 bool removecommands(const char* source)
3451 {
3452         bool go_again = true;
3453         while (go_again)
3454         {
3455                 go_again = false;
3456                 for (std::deque<command_t>::iterator i = cmdlist.begin(); i != cmdlist.end(); i++)
3457                 {
3458                         if (!strcmp(i->source,source))
3459                         {
3460                                 log(DEBUG,"removecommands(%s) Removing dependent command: %s",i->source,i->command);
3461                                 cmdlist.erase(i);
3462                                 go_again = true;
3463                                 break;
3464                         }
3465                 }
3466         }
3467         return true;
3468 }
3469
3470 void SetupCommandTable(void)
3471 {
3472         createcommand("USER",handle_user,0,4,"<core>");
3473         createcommand("NICK",handle_nick,0,1,"<core>");
3474         createcommand("QUIT",handle_quit,0,0,"<core>");
3475         createcommand("VERSION",handle_version,0,0,"<core>");
3476         createcommand("PING",handle_ping,0,1,"<core>");
3477         createcommand("PONG",handle_pong,0,1,"<core>");
3478         createcommand("ADMIN",handle_admin,0,0,"<core>");
3479         createcommand("PRIVMSG",handle_privmsg,0,2,"<core>");
3480         createcommand("INFO",handle_info,0,0,"<core>");
3481         createcommand("TIME",handle_time,0,0,"<core>");
3482         createcommand("WHOIS",handle_whois,0,1,"<core>");
3483         createcommand("WALLOPS",handle_wallops,'o',1,"<core>");
3484         createcommand("NOTICE",handle_notice,0,2,"<core>");
3485         createcommand("JOIN",handle_join,0,1,"<core>");
3486         createcommand("NAMES",handle_names,0,0,"<core>");
3487         createcommand("PART",handle_part,0,1,"<core>");
3488         createcommand("KICK",handle_kick,0,2,"<core>");
3489         createcommand("MODE",handle_mode,0,1,"<core>");
3490         createcommand("TOPIC",handle_topic,0,1,"<core>");
3491         createcommand("WHO",handle_who,0,1,"<core>");
3492         createcommand("MOTD",handle_motd,0,0,"<core>");
3493         createcommand("RULES",handle_rules,0,0,"<core>");
3494         createcommand("OPER",handle_oper,0,2,"<core>");
3495         createcommand("LIST",handle_list,0,0,"<core>");
3496         createcommand("DIE",handle_die,'o',1,"<core>");
3497         createcommand("RESTART",handle_restart,'o',1,"<core>");
3498         createcommand("KILL",handle_kill,'o',2,"<core>");
3499         createcommand("REHASH",handle_rehash,'o',0,"<core>");
3500         createcommand("LUSERS",handle_lusers,0,0,"<core>");
3501         createcommand("STATS",handle_stats,0,1,"<core>");
3502         createcommand("USERHOST",handle_userhost,0,1,"<core>");
3503         createcommand("AWAY",handle_away,0,0,"<core>");
3504         createcommand("ISON",handle_ison,0,0,"<core>");
3505         createcommand("SUMMON",handle_summon,0,0,"<core>");
3506         createcommand("USERS",handle_users,0,0,"<core>");
3507         createcommand("INVITE",handle_invite,0,2,"<core>");
3508         createcommand("PASS",handle_pass,0,1,"<core>");
3509         createcommand("TRACE",handle_trace,'o',0,"<core>");
3510         createcommand("WHOWAS",handle_whowas,0,1,"<core>");
3511         createcommand("CONNECT",handle_connect,'o',1,"<core>");
3512         createcommand("SQUIT",handle_squit,'o',0,"<core>");
3513         createcommand("MODULES",handle_modules,0,0,"<core>");
3514         createcommand("LINKS",handle_links,0,0,"<core>");
3515         createcommand("MAP",handle_map,0,0,"<core>");
3516         createcommand("KLINE",handle_kline,'o',1,"<core>");
3517         createcommand("GLINE",handle_gline,'o',1,"<core>");
3518         createcommand("ZLINE",handle_zline,'o',1,"<core>");
3519         createcommand("QLINE",handle_qline,'o',1,"<core>");
3520         createcommand("ELINE",handle_eline,'o',1,"<core>");
3521         createcommand("LOADMODULE",handle_loadmodule,'o',1,"<core>");
3522         createcommand("UNLOADMODULE",handle_unloadmodule,'o',1,"<core>");
3523         createcommand("SERVER",handle_server,0,0,"<core>");
3524 }
3525
3526 void process_buffer(const char* cmdbuf,userrec *user)
3527 {
3528         if (!user)
3529         {
3530                 log(DEFAULT,"*** BUG *** process_buffer was given an invalid parameter");
3531                 return;
3532         }
3533         char cmd[MAXBUF];
3534         if (!cmdbuf)
3535         {
3536                 log(DEFAULT,"*** BUG *** process_buffer was given an invalid parameter");
3537                 return;
3538         }
3539         if (!cmdbuf[0])
3540         {
3541                 return;
3542         }
3543         while (*cmdbuf == ' ') cmdbuf++; // strip leading spaces
3544
3545         strlcpy(cmd,cmdbuf,MAXBUF);
3546         if (!cmd[0])
3547         {
3548                 return;
3549         }
3550         int sl = strlen(cmd)-1;
3551         if ((cmd[sl] == 13) || (cmd[sl] == 10))
3552         {
3553                 cmd[sl] = '\0';
3554         }
3555         sl = strlen(cmd)-1;
3556         if ((cmd[sl] == 13) || (cmd[sl] == 10))
3557         {
3558                 cmd[sl] = '\0';
3559         }
3560         sl = strlen(cmd)-1;
3561         while (cmd[sl] == ' ') // strip trailing spaces
3562         {
3563                 cmd[sl] = '\0';
3564                 sl = strlen(cmd)-1;
3565         }
3566
3567         if (!cmd[0])
3568         {
3569                 return;
3570         }
3571         log(DEBUG,"CMDIN: %s %s",user->nick,cmd);
3572         tidystring(cmd);
3573         if ((user) && (cmd))
3574         {
3575                 process_command(user,cmd);
3576         }
3577 }
3578
3579 void DoSync(serverrec* serv, char* tcp_host)
3580 {
3581         char data[MAXBUF];
3582         log(DEBUG,"Sending sync");
3583         // send start of sync marker: Y <timestamp>
3584         // at this point the ircd receiving it starts broadcasting this netburst to all ircds
3585         // except the ones its receiving it from.
3586         snprintf(data,MAXBUF,"Y %lu",(unsigned long)TIME);
3587         serv->SendPacket(data,tcp_host);
3588         // send users and channels
3589
3590         NetSendMyRoutingTable();
3591
3592         // send all routing table and uline voodoo. The ordering of these commands is IMPORTANT!
3593         for (int j = 0; j < 32; j++)
3594         {
3595                 if (me[j] != NULL)
3596                 {
3597                         for (int k = 0; k < me[j]->connectors.size(); k++)
3598                         {
3599                                 if (is_uline(me[j]->connectors[k].GetServerName().c_str()))
3600                                 {
3601                                         snprintf(data,MAXBUF,"H %s",me[j]->connectors[k].GetServerName().c_str());
3602                                         serv->SendPacket(data,tcp_host);
3603                                 }
3604                         }
3605                 }
3606         }
3607
3608         // send our version for the remote side to cache
3609         snprintf(data,MAXBUF,"v %s %s",ServerName,GetVersionString().c_str());
3610         serv->SendPacket(data,tcp_host);
3611
3612         // sync the users and channels, give the modules a look-in.
3613         for (user_hash::iterator u = clientlist.begin(); u != clientlist.end(); u++)
3614         {
3615                 snprintf(data,MAXBUF,"N %lu %s %s %s %s +%s %s %s :%s",(unsigned long)u->second->age,u->second->nick,u->second->host,u->second->dhost,u->second->ident,u->second->modes,u->second->ip,u->second->server,u->second->fullname);
3616                 serv->SendPacket(data,tcp_host);
3617                 if (strchr(u->second->modes,'o'))
3618                 {
3619                         snprintf(data,MAXBUF,"| %s %s",u->second->nick,u->second->oper);
3620                         serv->SendPacket(data,tcp_host);
3621                 }
3622                 for (int i = 0; i <= MODCOUNT; i++)
3623                 {
3624                         string_list l = modules[i]->OnUserSync(u->second);
3625                         for (int j = 0; j < l.size(); j++)
3626                         {
3627                                 strlcpy(data,l[j].c_str(),MAXBUF);
3628                                 serv->SendPacket(data,tcp_host);
3629                         }
3630                 }
3631                 char* chl = chlist(u->second,u->second);
3632                 if (strcmp(chl,""))
3633                 {
3634                         snprintf(data,MAXBUF,"J %s %s",u->second->nick,chl);
3635                         serv->SendPacket(data,tcp_host);
3636                 }
3637         }
3638         // send channel modes, topics etc...
3639         for (chan_hash::iterator c = chanlist.begin(); c != chanlist.end(); c++)
3640         {
3641                 snprintf(data,MAXBUF,"M %s +%s",c->second->name,chanmodes(c->second));
3642                 serv->SendPacket(data,tcp_host);
3643                 for (int i = 0; i <= MODCOUNT; i++)
3644                 {
3645                         string_list l = modules[i]->OnChannelSync(c->second);
3646                         for (int j = 0; j < l.size(); j++)
3647                         {
3648                                 strlcpy(data,l[j].c_str(),MAXBUF);
3649                                 serv->SendPacket(data,tcp_host);
3650                         }
3651                 }
3652                 if (c->second->topic[0])
3653                 {
3654                         snprintf(data,MAXBUF,"T %lu %s %s :%s",(unsigned long)c->second->topicset,c->second->setby,c->second->name,c->second->topic);
3655                         serv->SendPacket(data,tcp_host);
3656                 }
3657                 // send current banlist
3658                 
3659                 for (BanList::iterator b = c->second->bans.begin(); b != c->second->bans.end(); b++)
3660                 {
3661                         snprintf(data,MAXBUF,"M %s +b %s",c->second->name,b->data);
3662                         serv->SendPacket(data,tcp_host);
3663                 }
3664         }
3665         // sync global zlines, glines, etc
3666         sync_xlines(serv,tcp_host);
3667
3668         snprintf(data,MAXBUF,"F %lu",(unsigned long)TIME);
3669         serv->SendPacket(data,tcp_host);
3670         log(DEBUG,"Sent sync");
3671         // ircd sends its serverlist after the end of sync here
3672 }
3673
3674
3675 void NetSendMyRoutingTable()
3676 {
3677         // send out a line saying what is reachable to us.
3678         // E.g. if A is linked to B C and D, send out:
3679         // $ A B C D
3680         // if its only linked to B and D send out:
3681         // $ A B D
3682         // if it has no links, dont even send out the line at all.
3683         char buffer[MAXBUF];
3684         snprintf(buffer,MAXBUF,"$ %s",ServerName);
3685         bool sendit = false;
3686         for (int i = 0; i < 32; i++)
3687         {
3688                 if (me[i] != NULL)
3689                 {
3690                         for (int j = 0; j < me[i]->connectors.size(); j++)
3691                         {
3692                                 if ((me[i]->connectors[j].GetState() != STATE_DISCONNECTED) || (is_uline(me[i]->connectors[j].GetServerName().c_str())))
3693                                 {
3694                                         strlcat(buffer," ",MAXBUF);
3695                                         strlcat(buffer,me[i]->connectors[j].GetServerName().c_str(),MAXBUF);
3696                                         sendit = true;
3697                                 }
3698                         }
3699                 }
3700         }
3701         if (sendit)
3702                 NetSendToAll(buffer);
3703 }
3704
3705
3706 void DoSplit(const char* params)
3707 {
3708         bool go_again = true;
3709         while (go_again)
3710         {
3711                 go_again = false;
3712                 for (int i = 0; i < 32; i++)
3713                 {
3714                         if (me[i] != NULL)
3715                         {
3716                                 for (vector<ircd_connector>::iterator j = me[i]->connectors.begin(); j != me[i]->connectors.end(); j++)
3717                                 {
3718                                         if (!strcasecmp(j->GetServerName().c_str(),params))
3719                                         {
3720                                                 j->routes.clear();
3721                                                 j->CloseConnection();
3722                                                 me[i]->connectors.erase(j);
3723                                                 go_again = true;
3724                                                 break;
3725                                         }
3726                                 }
3727                         }
3728                 }
3729         }
3730         log(DEBUG,"Removed server. Will remove clients...");
3731         // iterate through the userlist and remove all users on this server.
3732         // because we're dealing with a mesh, we dont have to deal with anything
3733         // "down-route" from this server (nice huh)
3734         go_again = true;
3735         char reason[MAXBUF];
3736         snprintf(reason,MAXBUF,"%s %s",ServerName,params);
3737         while (go_again)
3738         {
3739                 go_again = false;
3740                 for (user_hash::const_iterator u = clientlist.begin(); u != clientlist.end(); u++)
3741                 {
3742                         if (!strcasecmp(u->second->server,params))
3743                         {
3744                                 kill_link(u->second,reason);
3745                                 go_again = true;
3746                                 break;
3747                         }
3748                 }
3749         }
3750 }
3751
3752 // removes a server. Will NOT remove its users!
3753
3754 void RemoveServer(const char* name)
3755 {
3756         bool go_again = true;
3757         while (go_again)
3758         {
3759                 go_again = false;
3760                 for (int i = 0; i < 32; i++)
3761                 {
3762                         if (me[i] != NULL)
3763                         {
3764                                 for (vector<ircd_connector>::iterator j = me[i]->connectors.begin(); j != me[i]->connectors.end(); j++)
3765                                 {
3766                                         if (!strcasecmp(j->GetServerName().c_str(),name))
3767                                         {
3768                                                 j->routes.clear();
3769                                                 j->CloseConnection();
3770                                                 me[i]->connectors.erase(j);
3771                                                 go_again = true;
3772                                                 break;
3773                                         }
3774                                 }
3775                         }
3776                 }
3777         }
3778 }
3779
3780
3781 char MODERR[MAXBUF];
3782
3783 char* ModuleError()
3784 {
3785         return MODERR;
3786 }
3787
3788 void erase_factory(int j)
3789 {
3790         int v = 0;
3791         for (std::vector<ircd_module*>::iterator t = factory.begin(); t != factory.end(); t++)
3792         {
3793                 if (v == j)
3794                 {
3795                         factory.erase(t);
3796                         factory.push_back(NULL);
3797                         return;
3798                 }
3799                 v++;
3800         }
3801 }
3802
3803 void erase_module(int j)
3804 {
3805         int v1 = 0;
3806         for (std::vector<Module*>::iterator m = modules.begin(); m!= modules.end(); m++)
3807         {
3808                 if (v1 == j)
3809                 {
3810                         delete *m;
3811                         modules.erase(m);
3812                         modules.push_back(NULL);
3813                         break;
3814                 }
3815                 v1++;
3816         }
3817         int v2 = 0;
3818         for (std::vector<std::string>::iterator v = module_names.begin(); v != module_names.end(); v++)
3819         {
3820                 if (v2 == j)
3821                 {
3822                        module_names.erase(v);
3823                        break;
3824                 }
3825                 v2++;
3826         }
3827
3828 }
3829
3830 bool UnloadModule(const char* filename)
3831 {
3832         std::string filename_str = filename;
3833         for (int j = 0; j != module_names.size(); j++)
3834         {
3835                 if (module_names[j] == filename_str)
3836                 {
3837                         if (modules[j]->GetVersion().Flags & VF_STATIC)
3838                         {
3839                                 log(DEFAULT,"Failed to unload STATIC module %s",filename);
3840                                 snprintf(MODERR,MAXBUF,"Module not unloadable (marked static)");
3841                                 return false;
3842                         }
3843                         // found the module
3844                         log(DEBUG,"Deleting module...");
3845                         erase_module(j);
3846                         log(DEBUG,"Erasing module entry...");
3847                         erase_factory(j);
3848                         log(DEBUG,"Removing dependent commands...");
3849                         removecommands(filename);
3850                         log(DEFAULT,"Module %s unloaded",filename);
3851                         MODCOUNT--;
3852                         return true;
3853                 }
3854         }
3855         log(DEFAULT,"Module %s is not loaded, cannot unload it!",filename);
3856         snprintf(MODERR,MAXBUF,"Module not loaded");
3857         return false;
3858 }
3859
3860 bool DirValid(char* dirandfile)
3861 {
3862         char work[MAXBUF];
3863         strlcpy(work,dirandfile,MAXBUF);
3864         int p = strlen(work);
3865         // we just want the dir
3866         while (strlen(work))
3867         {
3868                 if (work[p] == '/')
3869                 {
3870                         work[p] = '\0';
3871                         break;
3872                 }
3873                 work[p--] = '\0';
3874         }
3875         char buffer[MAXBUF], otherdir[MAXBUF];
3876         // Get the current working directory
3877         if( getcwd( buffer, MAXBUF ) == NULL )
3878                 return false;
3879         chdir(work);
3880         if( getcwd( otherdir, MAXBUF ) == NULL )
3881                 return false;
3882         chdir(buffer);
3883         if (strlen(otherdir) >= strlen(work))
3884         {
3885                 otherdir[strlen(work)] = '\0';
3886                 if (!strcmp(otherdir,work))
3887                 {
3888                         return true;
3889                 }
3890                 return false;
3891         }
3892         else return false;
3893 }
3894
3895 std::string GetFullProgDir(char** argv, int argc)
3896 {
3897         char work[MAXBUF];
3898         strlcpy(work,argv[0],MAXBUF);
3899         int p = strlen(work);
3900         // we just want the dir
3901         while (strlen(work))
3902         {
3903                 if (work[p] == '/')
3904                 {
3905                         work[p] = '\0';
3906                         break;
3907                 }
3908                 work[p--] = '\0';
3909         }
3910         char buffer[MAXBUF], otherdir[MAXBUF];
3911         // Get the current working directory
3912         if( getcwd( buffer, MAXBUF ) == NULL )
3913                 return "";
3914         chdir(work);
3915         if( getcwd( otherdir, MAXBUF ) == NULL )
3916                 return "";
3917         chdir(buffer);
3918         return otherdir;
3919 }
3920
3921 bool LoadModule(const char* filename)
3922 {
3923         char modfile[MAXBUF];
3924         snprintf(modfile,MAXBUF,"%s/%s",ModPath,filename);
3925         std::string filename_str = filename;
3926         if (!DirValid(modfile))
3927         {
3928                 log(DEFAULT,"Module %s is not within the modules directory.",modfile);
3929                 snprintf(MODERR,MAXBUF,"Module %s is not within the modules directory.",modfile);
3930                 return false;
3931         }
3932         log(DEBUG,"Loading module: %s",modfile);
3933         if (FileExists(modfile))
3934         {
3935                 for (int j = 0; j < module_names.size(); j++)
3936                 {
3937                         if (module_names[j] == filename_str)
3938                         {
3939                                 log(DEFAULT,"Module %s is already loaded, cannot load a module twice!",modfile);
3940                                 snprintf(MODERR,MAXBUF,"Module already loaded");
3941                                 return false;
3942                         }
3943                 }
3944                 ircd_module* a = new ircd_module(modfile);
3945                 factory[MODCOUNT+1] = a;
3946                 if (factory[MODCOUNT+1]->LastError())
3947                 {
3948                         log(DEFAULT,"Unable to load %s: %s",modfile,factory[MODCOUNT+1]->LastError());
3949                         snprintf(MODERR,MAXBUF,"Loader/Linker error: %s",factory[MODCOUNT+1]->LastError());
3950                         MODCOUNT--;
3951                         return false;
3952                 }
3953                 if (factory[MODCOUNT+1]->factory)
3954                 {
3955                         Module* m = factory[MODCOUNT+1]->factory->CreateModule();
3956                         modules[MODCOUNT+1] = m;
3957                         /* save the module and the module's classfactory, if
3958                          * this isnt done, random crashes can occur :/ */
3959                         module_names.push_back(filename);
3960                 }
3961                 else
3962                 {
3963                         log(DEFAULT,"Unable to load %s",modfile);
3964                         snprintf(MODERR,MAXBUF,"Factory function failed!");
3965                         return false;
3966                 }
3967         }
3968         else
3969         {
3970                 log(DEFAULT,"InspIRCd: startup: Module Not Found %s",modfile);
3971                 snprintf(MODERR,MAXBUF,"Module file could not be found");
3972                 return false;
3973         }
3974         MODCOUNT++;
3975         return true;
3976 }
3977
3978 int InspIRCd(char** argv, int argc)
3979 {
3980         struct sockaddr_in client,server;
3981         char addrs[MAXBUF][255];
3982         int incomingSockfd, result = TRUE;
3983         socklen_t length;
3984         int count = 0;
3985         int selectResult = 0, selectResult2 = 0;
3986         char configToken[MAXBUF], Addr[MAXBUF], Type[MAXBUF];
3987         fd_set selectFds;
3988         timeval tv;
3989
3990         std::string logpath = GetFullProgDir(argv,argc) + "/ircd.log";
3991         log_file = fopen(logpath.c_str(),"a+");
3992         if (!log_file)
3993         {
3994                 printf("ERROR: Could not write to logfile %s, bailing!\n\n",logpath.c_str());
3995                 Exit(ERROR);
3996         }
3997         printf("Logging to %s...\n",logpath.c_str());
3998
3999         log(DEFAULT,"$Id$");
4000         if (geteuid() == 0)
4001         {
4002                 printf("WARNING!!! You are running an irc server as ROOT!!! DO NOT DO THIS!!!\n\n");
4003                 Exit(ERROR);
4004                 log(DEFAULT,"InspIRCd: startup: not starting with UID 0!");
4005         }
4006         SetupCommandTable();
4007         log(DEBUG,"InspIRCd: startup: default command table set up");
4008         
4009         ReadConfig(true,NULL);
4010         if (DieValue[0])
4011         { 
4012                 printf("WARNING: %s\n\n",DieValue);
4013                 log(DEFAULT,"Ut-Oh, somebody didn't read their config file: '%s'",DieValue);
4014                 exit(0); 
4015         }  
4016         log(DEBUG,"InspIRCd: startup: read config");
4017
4018         int clientportcount = 0, serverportcount = 0;
4019
4020         for (count = 0; count < ConfValueEnum("bind",&config_f); count++)
4021         {
4022                 ConfValue("bind","port",count,configToken,&config_f);
4023                 ConfValue("bind","address",count,Addr,&config_f);
4024                 ConfValue("bind","type",count,Type,&config_f);
4025                 if (!strcmp(Type,"servers"))
4026                 {
4027                         char Default[MAXBUF];
4028                         strcpy(Default,"no");
4029                         ConfValue("bind","default",count,Default,&config_f);
4030                         if (strchr(Default,'y'))
4031                         {
4032                                 defaultRoute = serverportcount;
4033                                 log(DEBUG,"InspIRCd: startup: binding '%s:%s' is default server route",Addr,configToken);
4034                         }
4035                         me[serverportcount] = new serverrec(ServerName,100L,false);
4036                         if (!me[serverportcount]->CreateListener(Addr,atoi(configToken)))
4037                         {
4038                                 log(DEFAULT,"Warning: Failed to bind port %lu",(unsigned long)atoi(configToken));
4039                                 printf("Warning: Failed to bind port %lu\n",(unsigned long)atoi(configToken));
4040                         }
4041                         else
4042                         {
4043                                 serverportcount++;
4044                         }
4045                 }
4046                 else
4047                 {
4048                         ports[clientportcount] = atoi(configToken);
4049                         strlcpy(addrs[clientportcount],Addr,256);
4050                         clientportcount++;
4051                 }
4052                 log(DEBUG,"InspIRCd: startup: read binding %s:%s [%s] from config",Addr,configToken, Type);
4053         }
4054         portCount = clientportcount;
4055         SERVERportCount = serverportcount;
4056           
4057         log(DEBUG,"InspIRCd: startup: read %lu total client ports and %lu total server ports",(unsigned long)portCount,(unsigned long)SERVERportCount);
4058         log(DEBUG,"InspIRCd: startup: InspIRCd is now starting!");
4059         
4060         printf("\n");
4061         
4062         /* BugFix By Craig! :p */
4063         MODCOUNT = -1;
4064         for (count = 0; count < ConfValueEnum("module",&config_f); count++)
4065         {
4066                 ConfValue("module","name",count,configToken,&config_f);
4067                 printf("Loading module... \033[1;32m%s\033[0m\n",configToken);
4068                 if (!LoadModule(configToken))
4069                 {
4070                         log(DEFAULT,"Exiting due to a module loader error.");
4071                         printf("\nThere was an error loading a module: %s\n\nYou might want to do './inspircd start' instead of 'bin/inspircd'\n\n",ModuleError());
4072                         Exit(0);
4073                 }
4074         }
4075         log(DEFAULT,"Total loaded modules: %lu",(unsigned long)MODCOUNT+1);
4076         
4077         startup_time = time(NULL);
4078           
4079         char PID[MAXBUF];
4080         ConfValue("pid","file",0,PID,&config_f);
4081         // write once here, to try it out and make sure its ok
4082         WritePID(PID);
4083           
4084         /* setup select call */
4085 #ifndef USE_KQUEUE
4086         FD_ZERO(&selectFds);
4087 #endif
4088         log(DEBUG,"InspIRCd: startup: zero selects");
4089         log(VERBOSE,"InspIRCd: startup: portCount = %lu", (unsigned long)portCount);
4090         
4091         for (count = 0; count < portCount; count++)
4092         {
4093                 if ((openSockfd[boundPortCount] = OpenTCPSocket()) == ERROR)
4094                 {
4095                         log(DEBUG,"InspIRCd: startup: bad fd %lu",(unsigned long)openSockfd[boundPortCount]);
4096                         return(ERROR);
4097                 }
4098                 if (BindSocket(openSockfd[boundPortCount],client,server,ports[count],addrs[count]) == ERROR)
4099                 {
4100                         log(DEFAULT,"InspIRCd: startup: failed to bind port %lu",(unsigned long)ports[count]);
4101                 }
4102                 else    /* well we at least bound to one socket so we'll continue */
4103                 {
4104                         boundPortCount++;
4105                 }
4106         }
4107         
4108         log(DEBUG,"InspIRCd: startup: total bound ports %lu",(unsigned long)boundPortCount);
4109           
4110         /* if we didn't bind to anything then abort */
4111         if (boundPortCount == 0)
4112         {
4113                 log(DEFAULT,"InspIRCd: startup: no ports bound, bailing!");
4114                 printf("\nERROR: Was not able to bind any of %lu ports! Please check your configuration.\n\n", (unsigned long)portCount);
4115                 return (ERROR);
4116         }
4117         
4118
4119         printf("\nInspIRCd is now running!\n");
4120
4121         if (nofork)
4122         {
4123                 log(VERBOSE,"Not forking as -nofork was specified");
4124         }
4125         else
4126         {
4127                 if (DaemonSeed() == ERROR)
4128                 {
4129                         log(DEFAULT,"InspIRCd: startup: can't daemonise");
4130                         printf("ERROR: could not go into daemon mode. Shutting down.\n");
4131                         Exit(ERROR);
4132                 }
4133         }
4134
4135         // BUGFIX: We cannot initialize this before forking, as the kqueue data is not inherited by child processes!
4136 #ifdef USE_KQUEUE
4137         kq = kqueue();
4138         lkq = kqueue();
4139         skq = kqueue();
4140         if ((kq == -1) || (lkq == -1) || (skq == -1))
4141         {
4142                 log(DEFAULT,"main: kqueue() failed!");
4143                 printf("ERROR: could not initialise kqueue event system. Shutting down.\n");
4144                 Exit(ERROR);
4145         }
4146 #endif
4147
4148
4149 #ifdef USE_KQUEUE
4150         log(DEFAULT,"kqueue socket engine is enabled. Filling listen list.");
4151         for (count = 0; count < boundPortCount; count++)
4152         {
4153                 struct kevent ke;
4154                 log(DEBUG,"kqueue: Add listening socket to events, kq=%d socket=%d",lkq,openSockfd[count]);
4155                 EV_SET(&ke, openSockfd[count], EVFILT_READ, EV_ADD, 0, 5, NULL);
4156                 int i = kevent(lkq, &ke, 1, 0, 0, NULL);
4157                 if (i == -1)
4158                 {
4159                         log(DEFAULT,"main: add listen ports to kqueue failed!");
4160                         printf("ERROR: could not initialise listening sockets in kqueue. Shutting down.\n");
4161                 }
4162         }
4163         for (int t = 0; t != SERVERportCount; t++)
4164         {
4165                 struct kevent ke;
4166                 if (me[t])
4167                 {
4168                         log(DEBUG,"kqueue: Add listening SERVER socket to events, kq=%d socket=%d",skq,me[t]->fd);
4169                         EV_SET(&ke, me[t]->fd, EVFILT_READ, EV_ADD, 0, 5, NULL);
4170                         int i = kevent(skq, &ke, 1, 0, 0, NULL);
4171                         if (i == -1)
4172                         {
4173                                 log(DEFAULT,"main: add server listen ports to kqueue failed!");
4174                                 printf("ERROR: could not initialise listening server sockets in kqueue. Shutting down.\n");
4175                         }
4176                 }
4177         }
4178
4179
4180 #else
4181         log(DEFAULT,"Using standard select socket engine.");
4182 #endif
4183
4184         WritePID(PID);
4185
4186         length = sizeof (client);
4187         char tcp_msg[MAXBUF],tcp_host[MAXBUF];
4188
4189 #ifdef USE_KQUEUE
4190         struct kevent ke;
4191         struct kevent ke_list[33];
4192         struct timespec ts;
4193 #endif
4194         fd_set serverfds;
4195         timeval tvs;
4196         tvs.tv_usec = 10000L;
4197         tvs.tv_sec = 0;
4198         tv.tv_sec = 0;
4199         tv.tv_usec = 10000L;
4200         char data[65536];
4201         timeval tval;
4202         fd_set sfd;
4203         tval.tv_usec = 10000L;
4204         tval.tv_sec = 0;
4205         int total_in_this_set = 0;
4206         int i = 0, v = 0, j = 0, cycle_iter = 0;
4207         bool expire_run = false;
4208           
4209         /* main loop, this never returns */
4210         for (;;)
4211         {
4212 #ifdef _POSIX_PRIORITY_SCHEDULING
4213                 sched_yield();
4214 #endif
4215 #ifndef USE_KQUEUE
4216                 FD_ZERO(&sfd);
4217 #endif
4218
4219                 // we only read time() once per iteration rather than tons of times!
4220                 OLDTIME = TIME;
4221                 TIME = time(NULL);
4222
4223                 dns_poll();
4224
4225                 // *FIX* Instead of closing sockets in kill_link when they receive the ERROR :blah line, we should queue
4226                 // them in a list, then reap the list every second or so.
4227                 if (((TIME % 5) == 0) && (!expire_run))
4228                 {
4229                         expire_lines();
4230                         FOREACH_MOD OnBackgroundTimer(TIME);
4231                         expire_run = true;
4232                         continue;
4233                 }
4234                 if ((TIME % 5) == 1)
4235                         expire_run = false;
4236                 
4237                 // fix by brain - this must be below any manipulation of the hashmap by modules
4238                 user_hash::iterator count2 = clientlist.begin();
4239
4240 #ifdef USE_KQUEUE
4241                 ts.tv_sec = 0;
4242                 ts.tv_nsec = 30000L;
4243                 i = kevent(skq, NULL, 0, &ke, 1, &ts);
4244                 if (i > 0)
4245                 {
4246                         log(DEBUG,"kqueue: Listening server socket event, i=%d, ke.ident=%d",i,ke.ident);
4247                         for (int x = 0; x != SERVERportCount; x++)
4248                         {
4249                                 if ((me[x]) && (ke.ident == me[x]->fd))
4250                                 {
4251
4252 #else
4253                 FD_ZERO(&serverfds);
4254                 for (int x = 0; x != SERVERportCount; x++)
4255                 {
4256                         if (me[x])
4257                                 FD_SET(me[x]->fd, &serverfds);
4258                 }
4259                 tvs.tv_usec = 30000L;
4260                 tvs.tv_sec = 0;
4261                 int servresult = select(32767, &serverfds, NULL, NULL, &tvs);
4262                 if (servresult > 0)
4263                 {
4264                         for (int x = 0; x != SERVERportCount; x++)
4265                         {
4266                                 if ((me[x]) && (FD_ISSET (me[x]->fd, &serverfds)))
4267                                 {
4268 #endif
4269                                         char remotehost[MAXBUF],resolved[MAXBUF];
4270                                         length = sizeof (client);
4271                                         incomingSockfd = accept (me[x]->fd, (sockaddr *) &client, &length);
4272                                         if (incomingSockfd != -1)
4273                                         {
4274                                                 strlcpy(remotehost,(char *)inet_ntoa(client.sin_addr),MAXBUF);
4275                                                 if(CleanAndResolve(resolved, remotehost) != TRUE)
4276                                                 {
4277                                                         strlcpy(resolved,remotehost,MAXBUF);
4278                                                 }
4279                                                 // add to this connections ircd_connector vector
4280                                                 // *FIX* - we need the LOCAL port not the remote port in &client!
4281                                                 me[x]->AddIncoming(incomingSockfd,resolved,me[x]->port);
4282                                         }
4283                                 }
4284                         }
4285                 }
4286      
4287                 for (int x = 0; x < SERVERportCount; x++)
4288                 {
4289                         std::deque<std::string> msgs;
4290                         msgs.clear();
4291                         if ((me[x]) && (me[x]->RecvPacket(msgs, tcp_host)))
4292                         {
4293                                 for (int ctr = 0; ctr < msgs.size(); ctr++)
4294                                 {
4295                                         strlcpy(tcp_msg,msgs[ctr].c_str(),MAXBUF);
4296                                         log(DEBUG,"Processing: %s",tcp_msg);
4297                                         if (!tcp_msg[0])
4298                                         {
4299                                                 log(DEBUG,"Invalid string from %s [route%lu]",tcp_host,(unsigned long)x);
4300                                                 break;
4301                                         }
4302                                         // during a netburst, send all data to all other linked servers
4303                                         if ((((nb_start>0) && (tcp_msg[0] != 'Y') && (tcp_msg[0] != 'X') && (tcp_msg[0] != 'F'))) || (is_uline(tcp_host)))
4304                                         {
4305                                                 if (is_uline(tcp_host))
4306                                                 {
4307                                                         if ((tcp_msg[0] != 'Y') && (tcp_msg[0] != 'X') && (tcp_msg[0] != 'F'))
4308                                                         {
4309                                                                 NetSendToAllExcept(tcp_host,tcp_msg);
4310                                                         }
4311                                                 }
4312                                                 else
4313                                                         NetSendToAllExcept(tcp_host,tcp_msg);
4314                                         }
4315                                         std::string msg = tcp_msg;
4316                                         FOREACH_MOD OnPacketReceive(msg,tcp_host);
4317                                         strlcpy(tcp_msg,msg.c_str(),MAXBUF);
4318                                         handle_link_packet(tcp_msg, tcp_host, me[x]);
4319                                 }
4320                                 goto label;
4321                         }
4322                 }
4323         
4324         while (count2 != clientlist.end())
4325         {
4326 #ifndef USE_KQUEUE
4327                 FD_ZERO(&sfd);
4328 #endif
4329
4330                 total_in_this_set = 0;
4331
4332                 user_hash::iterator xcount = count2;
4333                 user_hash::iterator endingiter = count2;
4334
4335                 if (count2 == clientlist.end()) break;
4336
4337                 userrec* curr = NULL;
4338
4339                 if (count2->second)
4340                         curr = count2->second;
4341
4342                 if ((curr) && (curr->fd != 0))
4343                 {
4344 #ifdef _POSIX_PRIORITY_SCHEDULING
4345         sched_yield();
4346 #endif
4347                         // assemble up to 64 sockets into an fd_set
4348                         // to implement a pooling mechanism.
4349                         //
4350                         // This should be up to 64x faster than the
4351                         // old implementation.
4352 #ifndef USE_KQUEUE
4353                         while (total_in_this_set < 1024)
4354                         {
4355                                 if (count2 != clientlist.end())
4356                                 {
4357                                         curr = count2->second;
4358                                         // we don't check the state of remote users.
4359                                         if ((curr->fd != -1) && (curr->fd != FD_MAGIC_NUMBER))
4360                                         {
4361                                                 curr->FlushWriteBuf();
4362                                                 if (curr->GetWriteError() != "")
4363                                                 {
4364                                                         log(DEBUG,"InspIRCd: write error: %s",curr->GetWriteError().c_str());
4365                                                         kill_link(curr,curr->GetWriteError().c_str());
4366                                                         goto label;
4367                                                 }
4368
4369                                                 FD_SET (curr->fd, &sfd);
4370
4371                                                 // registration timeout -- didnt send USER/NICK/HOST in the time specified in
4372                                                 // their connection class.
4373                                                 if ((TIME > curr->timeout) && (curr->registered != 7)) 
4374                                                 {
4375                                                         log(DEBUG,"InspIRCd: registration timeout: %s",curr->nick);
4376                                                         kill_link(curr,"Registration timeout");
4377                                                         goto label;
4378                                                 }
4379                                                 if ((TIME > curr->signon) && (curr->registered == 3) && (AllModulesReportReady(curr)))
4380                                                 {
4381                                                         log(DEBUG,"signon exceed, registered=3, and modules ready, OK");
4382                                                         curr->dns_done = true;
4383                                                         statsDnsBad++;
4384                                                         FullConnectUser(curr);
4385                                                         goto label;
4386                                                 }
4387                                                 if ((curr->dns_done) && (curr->registered == 3) && (AllModulesReportReady(curr))) // both NICK and USER... and DNS
4388                                                 {
4389                                                         log(DEBUG,"dns done, registered=3, and modules ready, OK");
4390                                                         FullConnectUser(curr);
4391                                                         goto label;
4392                                                 }
4393                                                 if ((TIME > curr->nping) && (isnick(curr->nick)) && (curr->registered == 7))
4394                                                 {
4395                                                         if ((!curr->lastping) && (curr->registered == 7))
4396                                                         {
4397                                                                 log(DEBUG,"InspIRCd: ping timeout: %s",curr->nick);
4398                                                                 kill_link(curr,"Ping timeout");
4399                                                                 goto label;
4400                                                         }
4401                                                         Write(curr->fd,"PING :%s",ServerName);
4402                                                         log(DEBUG,"InspIRCd: pinging: %s",curr->nick);
4403                                                         curr->lastping = 0;
4404                                                         curr->nping = TIME+curr->pingmax;       // was hard coded to 120
4405                                                 }
4406                                         }
4407                                         count2++;
4408                                         total_in_this_set++;
4409                                 }
4410                                 else break;
4411                         }
4412                         endingiter = count2;
4413                         count2 = xcount; // roll back to where we were
4414 #else
4415                         // KQUEUE: We don't go through a loop to fill the fd_set so instead we must manually do this loop every now and again.
4416                         // TODO: We dont need to do all this EVERY loop iteration, tone down the visits to this if we're using kqueue.
4417                         cycle_iter++;
4418                         if (cycle_iter > 10) while (count2 != clientlist.end())
4419                         {
4420                                 cycle_iter = 0;
4421                                 if (count2 != clientlist.end())
4422                                 {
4423                                         curr = count2->second;
4424                                         // we don't check the state of remote users.
4425                                         if ((curr->fd != -1) && (curr->fd != FD_MAGIC_NUMBER))
4426                                         {
4427
4428                                                 curr->FlushWriteBuf();
4429                                                 if (curr->GetWriteError() != "")
4430                                                 {
4431                                                         log(DEBUG,"InspIRCd: write error: %s",curr->GetWriteError().c_str());
4432                                                         kill_link(curr,curr->GetWriteError().c_str());
4433                                                         goto label;
4434                                                 }
4435
4436                                                 // registration timeout -- didnt send USER/NICK/HOST in the time specified in
4437                                                 // their connection class.
4438                                                 if ((TIME > curr->timeout) && (curr->registered != 7))
4439                                                 {
4440                                                         log(DEBUG,"InspIRCd: registration timeout: %s",curr->nick);
4441                                                         kill_link(curr,"Registration timeout");
4442                                                         goto label;
4443                                                 }
4444                                                 if ((TIME > curr->signon) && (curr->registered == 3) && (AllModulesReportReady(curr)))
4445                                                 {
4446                                                         log(DEBUG,"signon exceed, registered=3, and modules ready, OK: %d %d",TIME,curr->signon);
4447                                                         curr->dns_done = true;
4448                                                         statsDnsBad++;
4449                                                         FullConnectUser(curr);
4450                                                         goto label;
4451                                                 }
4452                                                 if ((curr->dns_done) && (curr->registered == 3) && (AllModulesReportReady(curr)))
4453                                                 {
4454                                                         log(DEBUG,"dns done, registered=3, and modules ready, OK");
4455                                                         FullConnectUser(curr);
4456                                                         goto label;
4457                                                 }
4458                                                 if ((TIME > curr->nping) && (isnick(curr->nick)) && (curr->registered == 7))
4459                                                 {
4460                                                         if ((!curr->lastping) && (curr->registered == 7))
4461                                                         {
4462                                                                 log(DEBUG,"InspIRCd: ping timeout: %s",curr->nick);
4463                                                                 kill_link(curr,"Ping timeout");
4464                                                                 goto label;
4465                                                         }
4466                                                         Write(curr->fd,"PING :%s",ServerName);
4467                                                         log(DEBUG,"InspIRCd: pinging: %s",curr->nick);
4468                                                         curr->lastping = 0;
4469                                                         curr->nping = TIME+curr->pingmax;       // was hard coded to 120
4470                                                 }
4471                                         }
4472                                 }
4473                                 else break;
4474                                 count2++;
4475                         }
4476                         // increment the counter right to the end of the list, as kqueue processes everything in one go
4477 #endif
4478         
4479                         v = 0;
4480
4481 #ifdef USE_KQUEUE
4482                         ts.tv_sec = 0;
4483                         ts.tv_nsec = 1000L;
4484                         // for now, we only read 1 event. We could read soooo many more :)
4485                         int i = kevent(kq, NULL, 0, &ke, 1, &ts);
4486                         if (i > 0)
4487                         {
4488                                 log(DEBUG,"kevent call: kq=%d, i=%d",kq,i);
4489                                 // KQUEUE: kevent gives us ONE fd which is ready to have something done to it. Do something to it.
4490                                 userrec* cu = fd_ref_table[ke.ident];
4491 #else
4492                         tval.tv_usec = 1000L;
4493                         selectResult2 = select(65535, &sfd, NULL, NULL, &tval);
4494                         
4495                         // now loop through all of the items in this pool if any are waiting
4496                         if (selectResult2 > 0)
4497                         for (user_hash::iterator count2a = xcount; count2a != endingiter; count2a++)
4498                         {
4499                                 // SELECT: we have to iterate...
4500                                 userrec* cu = count2a->second;
4501 #endif
4502
4503 #ifdef _POSIX_PRIORITY_SCHEDULING
4504                                 sched_yield();
4505 #endif
4506                                 result = EAGAIN;
4507 #ifdef USE_KQUEUE
4508                                 // KQUEUE: We already know we have a valid FD. No checks needed.
4509                                 if ((cu->fd != FD_MAGIC_NUMBER) && (cu->fd != -1))
4510 #else
4511                                 // SELECT: We don't know if our FD is valid.
4512                                 if ((cu->fd != FD_MAGIC_NUMBER) && (cu->fd != -1) && (FD_ISSET (cu->fd, &sfd)))
4513 #endif
4514                                 {
4515                                         log(DEBUG,"Data waiting on socket %d",cu->fd);
4516                                         int MOD_RESULT = 0;
4517                                         int result2 = 0;
4518                                         FOREACH_RESULT(OnRawSocketRead(cu->fd,data,65535,result2));
4519                                         if (!MOD_RESULT)
4520                                         {
4521                                                 result = read(cu->fd, data, 65535);
4522                                         }
4523                                         else result = result2;
4524                                         log(DEBUG,"Read result: %d",result);
4525                                         if (result)
4526                                         {
4527                                                 statsRecv += result;
4528                                                 // perform a check on the raw buffer as an array (not a string!) to remove
4529                                                 // characters 0 and 7 which are illegal in the RFC - replace them with spaces.
4530                                                 // hopefully this should stop even more people whining about "Unknown command: *"
4531                                                 for (int checker = 0; checker < result; checker++)
4532                                                 {
4533                                                         if ((data[checker] == 0) || (data[checker] == 7))
4534                                                                 data[checker] = ' ';
4535                                                 }
4536                                                 if (result > 0)
4537                                                         data[result] = '\0';
4538                                                 userrec* current = cu;
4539                                                 int currfd = current->fd;
4540                                                 int floodlines = 0;
4541                                                 // add the data to the users buffer
4542                                                 if (result > 0)
4543                                                 if (!current->AddBuffer(data))
4544                                                 {
4545                                                         // AddBuffer returned false, theres too much data in the user's buffer and theyre up to no good.
4546                                                         if (current->registered == 7)
4547                                                         {
4548                                                                 kill_link(current,"RecvQ exceeded");
4549                                                         }
4550                                                         else
4551                                                         {
4552                                                                 WriteOpers("*** Excess flood from %s",current->ip);
4553                                                                 log(DEFAULT,"Excess flood from: %s",current->ip);
4554                                                                 add_zline(120,ServerName,"Flood from unregistered connection",current->ip);
4555                                                                 apply_lines();
4556                                                         }
4557                                                         goto label;
4558                                                 }
4559                                                 if (current->recvq.length() > NetBufferSize)
4560                                                 {
4561                                                         if (current->registered == 7)
4562                                                         {
4563                                                                 kill_link(current,"RecvQ exceeded");
4564                                                         }
4565                                                         else
4566                                                         {
4567                                                                 WriteOpers("*** Excess flood from %s",current->ip);
4568                                                                 log(DEFAULT,"Excess flood from: %s",current->ip);
4569                                                                 add_zline(120,ServerName,"Flood from unregistered connection",current->ip);
4570                                                                 apply_lines();
4571                                                         }
4572                                                         goto label;
4573                                                 }
4574                                                 // while there are complete lines to process...
4575                                                 while (current->BufferIsReady())
4576                                                 {
4577                                                         floodlines++;
4578                                                         if (TIME > current->reset_due)
4579                                                         {
4580                                                                 current->reset_due = TIME + current->threshold;
4581                                                                 current->lines_in = 0;
4582                                                         }
4583                                                         current->lines_in++;
4584                                                         if (current->lines_in > current->flood)
4585                                                         {
4586                                                                 log(DEFAULT,"Excess flood from: %s!%s@%s",current->nick,current->ident,current->host);
4587                                                                 WriteOpers("*** Excess flood from: %s!%s@%s",current->nick,current->ident,current->host);
4588                                                                 kill_link(current,"Excess flood");
4589                                                                 goto label;
4590                                                         }
4591                                                         if ((floodlines > current->flood) && (current->flood != 0))
4592                                                         {
4593                                                                 if (current->registered == 7)
4594                                                                 {
4595                                                                         log(DEFAULT,"Excess flood from: %s!%s@%s",current->nick,current->ident,current->host);
4596                                                                         WriteOpers("*** Excess flood from: %s!%s@%s",current->nick,current->ident,current->host);
4597                                                                         kill_link(current,"Excess flood");
4598                                                                 }
4599                                                                 else
4600                                                                 {
4601                                                                         add_zline(120,ServerName,"Flood from unregistered connection",current->ip);
4602                                                                         apply_lines();
4603                                                                 }
4604                                                                 goto label;
4605                                                         }
4606                                                         char sanitized[MAXBUF];
4607                                                         // use GetBuffer to copy single lines into the sanitized string
4608                                                         std::string single_line = current->GetBuffer();
4609                                                         current->bytes_in += single_line.length();
4610                                                         current->cmds_in++;
4611                                                         if (single_line.length()>512)
4612                                                         {
4613                                                                 log(DEFAULT,"Excess flood from: %s!%s@%s",current->nick,current->ident,current->host);
4614                                                                 WriteOpers("*** Excess flood from: %s!%s@%s",current->nick,current->ident,current->host);
4615                                                                 kill_link(current,"Excess flood");
4616                                                                 goto label;
4617                                                         }
4618                                                         strlcpy(sanitized,single_line.c_str(),MAXBUF);
4619                                                         if (*sanitized)
4620                                                         {
4621                                                                 userrec* old_comp = fd_ref_table[currfd];
4622                                                                 // we're gonna re-scan to check if the nick is gone, after every
4623                                                                 // command - if it has, we're gonna bail
4624                                                                 process_buffer(sanitized,current);
4625                                                                 // look for the user's record in case it's changed... if theyve quit,
4626                                                                 // we cant do anything more with their buffer, so bail.
4627                                                                 // there used to be an ugly, slow loop here. Now we have a reference
4628                                                                 // table, life is much easier (and FASTER)
4629                                                                 userrec* new_comp = fd_ref_table[currfd];
4630                                                                 if ((currfd < 0) || (!fd_ref_table[currfd]) || (old_comp != new_comp))
4631                                                                         goto label;
4632
4633                                                         }
4634                                                 }
4635                                                 goto label;
4636                                         }
4637
4638                                         if ((result == -1) && (errno != EAGAIN) && (errno != EINTR))
4639                                         {
4640                                                 log(DEBUG,"killing: %s",cu->nick);
4641                                                 kill_link(cu,strerror(errno));
4642                                                 goto label;
4643                                         }
4644                                 }
4645                                 // result EAGAIN means nothing read
4646                                 if (result == EAGAIN)
4647                                 {
4648                                 }
4649                                 else
4650                                 if (result == 0)
4651                                 {
4652 #ifndef USE_KQUEUE
4653                                         if (count2->second)
4654                                         {
4655 #endif
4656                                                 log(DEBUG,"InspIRCd: Exited: %s",cu->nick);
4657                                                 kill_link(cu,"Client exited");
4658                                                 // must bail here? kill_link removes the hash, corrupting the iterator
4659                                                 log(DEBUG,"Bailing from client exit");
4660                                                 goto label;
4661 #ifndef USE_KQUEUE
4662                                         }
4663 #endif
4664                                 }
4665                                 else if (result > 0)
4666                                 {
4667                                 }
4668                         }
4669                 }
4670                 for (int q = 0; q < total_in_this_set; q++)
4671                 {
4672                         count2++;
4673                 }
4674         }
4675
4676 #ifdef _POSIX_PRIORITY_SCHEDULING
4677         sched_yield();
4678 #endif
4679         
4680 #ifndef USE_KQUEUE
4681         // set up select call
4682         for (count = 0; count < boundPortCount; count++)
4683         {
4684                 FD_SET (openSockfd[count], &selectFds);
4685         }
4686
4687         tv.tv_usec = 30000L;
4688         selectResult = select(MAXSOCKS, &selectFds, NULL, NULL, &tv);
4689
4690         /* select is reporting a waiting socket. Poll them all to find out which */
4691         if (selectResult > 0)
4692         {
4693                 for (count = 0; count < boundPortCount; count++)
4694                 {
4695                         if (FD_ISSET (openSockfd[count], &selectFds))
4696                         {
4697 #else
4698         ts.tv_sec = 0;
4699         ts.tv_nsec = 30000L;
4700         i = kevent(lkq, NULL, 0, ke_list, 32, &ts);
4701         if (i > 0) for (j = 0; j < i; j++)
4702         {
4703                 log(DEBUG,"kqueue: Listening socket event, i=%d, ke.ident=%d",i,ke.ident);
4704                 // this isnt as efficient as it could be, we could create a reference table
4705                 // to reference bound ports by fd, but this isnt a big bottleneck as the actual
4706                 // number of listening ports on the average ircd is a small number (less than 20)
4707                 // compared to the number of clients (possibly over 2000)
4708                 for (count = 0; count < boundPortCount; count++)
4709                 {
4710                         if (ke_list[j].ident == openSockfd[count])
4711                         {
4712 #endif
4713                                 char target[MAXBUF], resolved[MAXBUF];
4714                                 length = sizeof (client);
4715                                 incomingSockfd = accept (openSockfd[count], (struct sockaddr *) &client, &length);
4716                               
4717                                 strlcpy (target, (char *) inet_ntoa (client.sin_addr), MAXBUF);
4718                                 strlcpy (resolved, target, MAXBUF);
4719                         
4720                                 if (incomingSockfd < 0)
4721                                 {
4722                                         WriteOpers("*** WARNING: Accept failed on port %lu (%s)",(unsigned long)ports[count],target);
4723                                         log(DEBUG,"InspIRCd: accept failed: %lu",(unsigned long)ports[count]);
4724                                         statsRefused++;
4725                                 }
4726                                 else
4727                                 {
4728                                         FOREACH_MOD OnRawSocketAccept(incomingSockfd, resolved, ports[count]);
4729                                         statsAccept++;
4730                                         AddClient(incomingSockfd, resolved, ports[count], false, inet_ntoa (client.sin_addr));
4731                                         log(DEBUG,"InspIRCd: adding client on port %lu fd=%lu",(unsigned long)ports[count],(unsigned long)incomingSockfd);
4732                                 }
4733                                 //goto label;
4734                         }
4735                 }
4736         }
4737         label:
4738         if (0) {};
4739 #ifdef _POSIX_PRIORITY_SCHEDULING
4740         sched_yield();
4741         sched_yield();
4742 #endif
4743 }
4744 /* not reached */
4745 close (incomingSockfd);
4746 }
4747