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