]> git.netwichtig.de Git - user/henk/code/inspircd.git/blob - src/inspircd.cpp
Tidied up socket engine code, moved into a specialized header full of #defines
[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_config.h"
22 #include "inspircd.h"
23 #include "inspircd_io.h"
24 #include "inspircd_util.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
31 #ifdef USE_KQUEUE
32 #include <sys/types.h>
33 #include <sys/event.h>
34 #include <sys/time.h>
35 #endif
36
37 #ifdef USE_EPOLL
38 #include <sys/epoll.h>
39 #define EP_DELAY 50
40 #endif
41
42 #include <time.h>
43 #include <string>
44 #ifdef GCC3
45 #include <ext/hash_map>
46 #else
47 #include <hash_map>
48 #endif
49 #include <map>
50 #include <sstream>
51 #include <vector>
52 #include <deque>
53 #include <sched.h>
54 #include "connection.h"
55 #include "users.h"
56 #include "servers.h"
57 #include "ctables.h"
58 #include "globals.h"
59 #include "modules.h"
60 #include "dynamic.h"
61 #include "wildcard.h"
62 #include "message.h"
63 #include "mode.h"
64 #include "commands.h"
65 #include "xline.h"
66 #include "inspstring.h"
67 #include "dnsqueue.h"
68 #include "helperfuncs.h"
69 #include "hashcomp.h"
70 #include "socketengine.h"
71
72 int LogLevel = DEFAULT;
73 char ServerName[MAXBUF];
74 char Network[MAXBUF];
75 char ServerDesc[MAXBUF];
76 char AdminName[MAXBUF];
77 char AdminEmail[MAXBUF];
78 char AdminNick[MAXBUF];
79 char diepass[MAXBUF];
80 char restartpass[MAXBUF];
81 char motd[MAXBUF];
82 char rules[MAXBUF];
83 char list[MAXBUF];
84 char PrefixQuit[MAXBUF];
85 char DieValue[MAXBUF];
86 char DNSServer[MAXBUF];
87 int debugging =  0;
88 int WHOWAS_STALE = 48; // default WHOWAS Entries last 2 days before they go 'stale'
89 int WHOWAS_MAX = 100;  // default 100 people maximum in the WHOWAS list
90 int DieDelay  =  5;
91 time_t startup_time = time(NULL);
92 int NetBufferSize = 10240;      // NetBufferSize used as the buffer size for all read() ops
93 int MaxConn = SOMAXCONN;        // size of accept() backlog (128 by default on *BSD)
94 unsigned int SoftLimit = MAXCLIENTS;
95 extern int MaxWhoResults;
96 time_t nb_start = 0;
97 int dns_timeout = 5;
98
99 char DisabledCommands[MAXBUF];
100
101 bool AllowHalfop = true;
102 bool AllowProtect = true;
103 bool AllowFounder = true;
104
105 extern std::vector<Module*> modules;
106 std::vector<std::string> module_names;
107 extern std::vector<ircd_module*> factory;
108
109 extern int MODCOUNT;
110 int openSockfd[MAXSOCKS];
111 bool nofork = false;
112 bool unlimitcore = false;
113
114 time_t TIME = time(NULL), OLDTIME = time(NULL);
115
116 #ifdef USE_KQUEUE
117 int kq, lkq, skq;
118 #endif
119
120 #ifdef USE_EPOLL
121 int ep, lep, sep;
122 #endif
123
124 bool has_been_netsplit = false;
125 extern std::vector<std::string> include_stack;
126
127 typedef nspace::hash_map<std::string, userrec*, nspace::hash<string>, irc::StrHashComp> user_hash;
128 typedef nspace::hash_map<std::string, chanrec*, nspace::hash<string>, irc::StrHashComp> chan_hash;
129 typedef nspace::hash_map<in_addr,string*, nspace::hash<in_addr>, irc::InAddr_HashComp> address_cache;
130 typedef nspace::hash_map<std::string, WhoWasUser*, nspace::hash<string>, irc::StrHashComp> whowas_hash;
131 typedef std::deque<command_t> command_table;
132 typedef std::map<std::string,time_t> autoconnects;
133 typedef std::vector<std::string> servernamelist;
134
135 // This table references users by file descriptor.
136 // its an array to make it VERY fast, as all lookups are referenced
137 // by an integer, meaning there is no need for a scan/search operation.
138 userrec* fd_ref_table[65536];
139
140 int statsAccept = 0, statsRefused = 0, statsUnknown = 0, statsCollisions = 0, statsDns = 0, statsDnsGood = 0, statsDnsBad = 0, statsConnects = 0, statsSent= 0, statsRecv = 0;
141
142 serverrec* me[32];
143
144 FILE *log_file;
145
146 user_hash clientlist;
147 chan_hash chanlist;
148 whowas_hash whowas;
149 command_table cmdlist;
150 autoconnects autoconns;
151 file_cache MOTD;
152 file_cache RULES;
153 address_cache IP;
154
155 ClassVector Classes;
156 servernamelist servernames;
157
158 struct linger linger = { 0 };
159 char MyExecutable[1024];
160 int boundPortCount = 0;
161 int portCount = 0, SERVERportCount = 0, ports[MAXSOCKS];
162 int defaultRoute = 0;
163 char ModPath[MAXBUF];
164
165 /* prototypes */
166
167 int has_channel(userrec *u, chanrec *c);
168 int usercount(chanrec *c);
169 int usercount_i(chanrec *c);
170 char* Passwd(userrec *user);
171 bool IsDenied(userrec *user);
172 void AddWhoWas(userrec* u);
173
174 std::vector<long> auth_cookies;
175 std::stringstream config_f(stringstream::in | stringstream::out);
176
177 std::vector<userrec*> all_opers;
178
179 char lowermap[255];
180
181 void AddOper(userrec* user)
182 {
183         log(DEBUG,"Oper added to optimization list");
184         all_opers.push_back(user);
185 }
186
187 void AddServerName(std::string servername)
188 {
189         log(DEBUG,"Adding server name: %s",servername.c_str());
190         for (servernamelist::iterator a = servernames.begin(); a < servernames.end(); a++)
191         {
192                 if (*a == servername)
193                         return;
194         }
195         servernames.push_back(servername);
196 }
197
198 const char* FindServerNamePtr(std::string servername)
199 {
200         for (servernamelist::iterator a = servernames.begin(); a < servernames.end(); a++)
201         {
202                 if (*a == servername)
203                         return a->c_str();
204         }
205         return "";
206 }
207
208 void DeleteOper(userrec* user)
209 {
210         for (std::vector<userrec*>::iterator a = all_opers.begin(); a < all_opers.end(); a++)
211         {
212                 if (*a == user)
213                 {
214                         log(DEBUG,"Oper removed from optimization list");
215                         all_opers.erase(a);
216                         return;
217                 }
218         }
219 }
220
221 std::string GetRevision()
222 {
223         char Revision[] = "$Revision$";
224         char *s1 = Revision;
225         char *savept;
226         char *v2 = strtok_r(s1," ",&savept);
227         s1 = savept;
228         v2 = strtok_r(s1," ",&savept);
229         s1 = savept;
230         return std::string(v2);
231 }
232
233
234 std::string getservername()
235 {
236         return ServerName;
237 }
238
239 std::string getserverdesc()
240 {
241         return ServerDesc;
242 }
243
244 std::string getnetworkname()
245 {
246         return Network;
247 }
248
249 std::string getadminname()
250 {
251         return AdminName;
252 }
253
254 std::string getadminemail()
255 {
256         return AdminEmail;
257 }
258
259 std::string getadminnick()
260 {
261         return AdminNick;
262 }
263
264 void ReadConfig(bool bail, userrec* user)
265 {
266         char dbg[MAXBUF],pauseval[MAXBUF],Value[MAXBUF],timeout[MAXBUF],NB[MAXBUF],flood[MAXBUF],MW[MAXBUF],MCON[MAXBUF];
267         char AH[MAXBUF],AP[MAXBUF],AF[MAXBUF],DNT[MAXBUF],pfreq[MAXBUF],thold[MAXBUF],sqmax[MAXBUF],rqmax[MAXBUF],SLIMT[MAXBUF];
268         ConnectClass c;
269         std::stringstream errstr;
270         include_stack.clear();
271         
272         if (!LoadConf(CONFIG_FILE,&config_f,&errstr))
273         {
274                 errstr.seekg(0);
275                 log(DEFAULT,"There were errors in your configuration:\n%s",errstr.str().c_str());
276                 if (bail)
277                 {
278                         printf("There were errors in your configuration:\n%s",errstr.str().c_str());
279                         Exit(0);
280                 }
281                 else
282                 {
283                         char dataline[1024];
284                         if (user)
285                         {
286                                 WriteServ(user->fd,"NOTICE %s :There were errors in the configuration file:",user->nick);
287                                 while (!errstr.eof())
288                                 {
289                                         errstr.getline(dataline,1024);
290                                         WriteServ(user->fd,"NOTICE %s :%s",user->nick,dataline);
291                                 }
292                         }
293                         else
294                         {
295                                 WriteOpers("There were errors in the configuration file:",user->nick);
296                                 while (!errstr.eof())
297                                 {
298                                         errstr.getline(dataline,1024);
299                                         WriteOpers(dataline);
300                                 }
301                         }
302                         return;
303                 }
304         }
305           
306         ConfValue("server","name",0,ServerName,&config_f);
307         ConfValue("server","description",0,ServerDesc,&config_f);
308         ConfValue("server","network",0,Network,&config_f);
309         ConfValue("admin","name",0,AdminName,&config_f);
310         ConfValue("admin","email",0,AdminEmail,&config_f);
311         ConfValue("admin","nick",0,AdminNick,&config_f);
312         ConfValue("files","motd",0,motd,&config_f);
313         ConfValue("files","rules",0,rules,&config_f);
314         ConfValue("power","diepass",0,diepass,&config_f);
315         ConfValue("power","pause",0,pauseval,&config_f);
316         ConfValue("power","restartpass",0,restartpass,&config_f);
317         ConfValue("options","prefixquit",0,PrefixQuit,&config_f);
318         ConfValue("die","value",0,DieValue,&config_f);
319         ConfValue("options","loglevel",0,dbg,&config_f);
320         ConfValue("options","netbuffersize",0,NB,&config_f);
321         ConfValue("options","maxwho",0,MW,&config_f);
322         ConfValue("options","allowhalfop",0,AH,&config_f);
323         ConfValue("options","allowprotect",0,AP,&config_f);
324         ConfValue("options","allowfounder",0,AF,&config_f);
325         ConfValue("dns","server",0,DNSServer,&config_f);
326         ConfValue("dns","timeout",0,DNT,&config_f);
327         ConfValue("options","moduledir",0,ModPath,&config_f);
328         ConfValue("disabled","commands",0,DisabledCommands,&config_f);
329         ConfValue("options","somaxconn",0,MCON,&config_f);
330         ConfValue("options","softlimit",0,SLIMT,&config_f);
331
332         SoftLimit = atoi(SLIMT);
333         if ((SoftLimit < 1) || (SoftLimit > MAXCLIENTS))
334         {
335                 log(DEFAULT,"WARNING: <options:softlimit> value is greater than %d or less than 0, set to %d.",MAXCLIENTS,MAXCLIENTS);
336                 SoftLimit = MAXCLIENTS;
337         }
338         MaxConn = atoi(MCON);
339         if (MaxConn > SOMAXCONN)
340                 log(DEFAULT,"WARNING: <options:somaxconn> value may be higher than the system-defined SOMAXCONN value!");
341         NetBufferSize = atoi(NB);
342         MaxWhoResults = atoi(MW);
343         dns_timeout = atoi(DNT);
344         if (!dns_timeout)
345                 dns_timeout = 5;
346         if (!MaxConn)
347                 MaxConn = SOMAXCONN;
348         if (!DNSServer[0])
349                 strlcpy(DNSServer,"127.0.0.1",MAXBUF);
350         if (!ModPath[0])
351                 strlcpy(ModPath,MOD_PATH,MAXBUF);
352         AllowHalfop = ((!strcasecmp(AH,"true")) || (!strcasecmp(AH,"1")) || (!strcasecmp(AH,"yes")));
353         AllowProtect = ((!strcasecmp(AP,"true")) || (!strcasecmp(AP,"1")) || (!strcasecmp(AP,"yes")));
354         AllowFounder = ((!strcasecmp(AF,"true")) || (!strcasecmp(AF,"1")) || (!strcasecmp(AF,"yes")));
355         if ((!NetBufferSize) || (NetBufferSize > 65535) || (NetBufferSize < 1024))
356         {
357                 log(DEFAULT,"No NetBufferSize specified or size out of range, setting to default of 10240.");
358                 NetBufferSize = 10240;
359         }
360         if ((!MaxWhoResults) || (MaxWhoResults > 65535) || (MaxWhoResults < 1))
361         {
362                 log(DEFAULT,"No MaxWhoResults specified or size out of range, setting to default of 128.");
363                 MaxWhoResults = 128;
364         }
365         if (!strcmp(dbg,"debug"))
366                 LogLevel = DEBUG;
367         if (!strcmp(dbg,"verbose"))
368                 LogLevel = VERBOSE;
369         if (!strcmp(dbg,"default"))
370                 LogLevel = DEFAULT;
371         if (!strcmp(dbg,"sparse"))
372                 LogLevel = SPARSE;
373         if (!strcmp(dbg,"none"))
374                 LogLevel = NONE;
375         readfile(MOTD,motd);
376         log(DEFAULT,"Reading message of the day...");
377         readfile(RULES,rules);
378         log(DEFAULT,"Reading connect classes...");
379         Classes.clear();
380         for (int i = 0; i < ConfValueEnum("connect",&config_f); i++)
381         {
382                 strcpy(Value,"");
383                 ConfValue("connect","allow",i,Value,&config_f);
384                 ConfValue("connect","timeout",i,timeout,&config_f);
385                 ConfValue("connect","flood",i,flood,&config_f);
386                 ConfValue("connect","pingfreq",i,pfreq,&config_f);
387                 ConfValue("connect","threshold",i,thold,&config_f);
388                 ConfValue("connect","sendq",i,sqmax,&config_f);
389                 ConfValue("connect","recvq",i,rqmax,&config_f);
390                 if (Value[0])
391                 {
392                         strlcpy(c.host,Value,MAXBUF);
393                         c.type = CC_ALLOW;
394                         strlcpy(Value,"",MAXBUF);
395                         ConfValue("connect","password",i,Value,&config_f);
396                         strlcpy(c.pass,Value,MAXBUF);
397                         c.registration_timeout = 90; // default is 2 minutes
398                         c.pingtime = 120;
399                         c.flood = atoi(flood);
400                         c.threshold = 5;
401                         c.sendqmax = 262144; // 256k
402                         c.recvqmax = 4096;   // 4k
403                         if (atoi(thold)>0)
404                         {
405                                 c.threshold = atoi(thold);
406                         }
407                         if (atoi(sqmax)>0)
408                         {
409                                 c.sendqmax = atoi(sqmax);
410                         }
411                         if (atoi(rqmax)>0)
412                         {
413                                 c.recvqmax = atoi(rqmax);
414                         }
415                         if (atoi(timeout)>0)
416                         {
417                                 c.registration_timeout = atoi(timeout);
418                         }
419                         if (atoi(pfreq)>0)
420                         {
421                                 c.pingtime = atoi(pfreq);
422                         }
423                         Classes.push_back(c);
424                         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);
425                 }
426                 else
427                 {
428                         ConfValue("connect","deny",i,Value,&config_f);
429                         strlcpy(c.host,Value,MAXBUF);
430                         c.type = CC_DENY;
431                         Classes.push_back(c);
432                         log(DEBUG,"Read connect class type DENY, host=%s",c.host);
433                 }
434         
435         }
436         log(DEFAULT,"Reading K lines,Q lines and Z lines from config...");
437         read_xline_defaults();
438         log(DEFAULT,"Applying K lines, Q lines and Z lines...");
439         apply_lines();
440
441         autoconns.clear();
442         for (int i = 0; i < ConfValueEnum("link",&config_f); i++)
443         {
444                 char Link_ServerName[MAXBUF],Link_AConn[MAXBUF];
445                 ConfValue("link","name",i,Link_ServerName,&config_f);
446                 ConfValue("link","autoconnect",i,Link_AConn,&config_f);
447                 if (strcmp(Link_AConn,""))
448                 {
449                         autoconns[std::string(Link_ServerName)] = atoi(Link_AConn) + time(NULL);
450                 }
451         }
452
453
454         log(DEFAULT,"Done reading configuration file, InspIRCd is now starting.");
455         if (!bail)
456         {
457                 log(DEFAULT,"Adding and removing modules due to rehash...");
458
459                 std::vector<std::string> old_module_names, new_module_names, added_modules, removed_modules;
460
461                 // store the old module names
462                 for (std::vector<std::string>::iterator t = module_names.begin(); t != module_names.end(); t++)
463                 {
464                         old_module_names.push_back(*t);
465                 }
466
467                 // get the new module names
468                 for (int count2 = 0; count2 < ConfValueEnum("module",&config_f); count2++)
469                 {
470                         ConfValue("module","name",count2,Value,&config_f);
471                         new_module_names.push_back(Value);
472                 }
473
474                 // now create a list of new modules that are due to be loaded
475                 // and a seperate list of modules which are due to be unloaded
476                 for (std::vector<std::string>::iterator _new = new_module_names.begin(); _new != new_module_names.end(); _new++)
477                 {
478                         bool added = true;
479                         for (std::vector<std::string>::iterator old = old_module_names.begin(); old != old_module_names.end(); old++)
480                         {
481                                 if (*old == *_new)
482                                         added = false;
483                         }
484                         if (added)
485                                 added_modules.push_back(*_new);
486                 }
487                 for (std::vector<std::string>::iterator oldm = old_module_names.begin(); oldm != old_module_names.end(); oldm++)
488                 {
489                         bool removed = true;
490                         for (std::vector<std::string>::iterator newm = new_module_names.begin(); newm != new_module_names.end(); newm++)
491                         {
492                                 if (*newm == *oldm)
493                                         removed = false;
494                         }
495                         if (removed)
496                                 removed_modules.push_back(*oldm);
497                 }
498                 // now we have added_modules, a vector of modules to be loaded, and removed_modules, a vector of modules
499                 // to be removed.
500                 int rem = 0, add = 0;
501                 if (!removed_modules.empty())
502                 for (std::vector<std::string>::iterator removing = removed_modules.begin(); removing != removed_modules.end(); removing++)
503                 {
504                         if (UnloadModule(removing->c_str()))
505                         {
506                                 WriteOpers("*** REHASH UNLOADED MODULE: %s",removing->c_str());
507                                 WriteServ(user->fd,"973 %s %s :Module %s successfully unloaded.",user->nick, removing->c_str(), removing->c_str());
508                                 rem++;
509                         }
510                         else
511                         {
512                                 WriteServ(user->fd,"972 %s %s :Failed to unload module %s: %s",user->nick, removing->c_str(), removing->c_str(), ModuleError());
513                         }
514                 }
515                 if (!added_modules.empty())
516                 for (std::vector<std::string>::iterator adding = added_modules.begin(); adding != added_modules.end(); adding++)
517                 {
518                         if (LoadModule(adding->c_str()))
519                         {
520                                 WriteOpers("*** REHASH LOADED MODULE: %s",adding->c_str());
521                                 WriteServ(user->fd,"975 %s %s :Module %s successfully loaded.",user->nick, adding->c_str(), adding->c_str());
522                                 add++;
523                         }
524                         else
525                         {
526                                 WriteServ(user->fd,"974 %s %s :Failed to load module %s: %s",user->nick, adding->c_str(), adding->c_str(), ModuleError());
527                         }
528                 }
529                 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());
530         }
531 }
532
533
534 /* add a channel to a user, creating the record for it if needed and linking
535  * it to the user record */
536
537 chanrec* add_channel(userrec *user, const char* cn, const char* key, bool override)
538 {
539         if ((!user) || (!cn))
540         {
541                 log(DEFAULT,"*** BUG *** add_channel was given an invalid parameter");
542                 return 0;
543         }
544
545         chanrec* Ptr;
546         int created = 0;
547         char cname[MAXBUF];
548
549         strncpy(cname,cn,MAXBUF);
550         
551         // we MUST declare this wherever we use FOREACH_RESULT
552         int MOD_RESULT = 0;
553
554         if (strlen(cname) > CHANMAX)
555         {
556                 cname[CHANMAX] = '\0';
557         }
558
559         log(DEBUG,"add_channel: %s %s",user->nick,cname);
560         
561         if ((FindChan(cname)) && (has_channel(user,FindChan(cname))))
562         {
563                 return NULL; // already on the channel!
564         }
565
566
567         if (!FindChan(cname))
568         {
569                 MOD_RESULT = 0;
570                 FOREACH_RESULT(OnUserPreJoin(user,NULL,cname));
571                 if (MOD_RESULT == 1) {
572                         return NULL;
573                 }
574
575                 /* create a new one */
576                 log(DEBUG,"add_channel: creating: %s",cname);
577                 {
578                         chanlist[cname] = new chanrec();
579
580                         strlcpy(chanlist[cname]->name, cname,CHANMAX);
581                         chanlist[cname]->binarymodes = CM_TOPICLOCK | CM_NOEXTERNAL;
582                         chanlist[cname]->created = TIME;
583                         strcpy(chanlist[cname]->topic, "");
584                         strncpy(chanlist[cname]->setby, user->nick,NICKMAX);
585                         chanlist[cname]->topicset = 0;
586                         Ptr = chanlist[cname];
587                         log(DEBUG,"add_channel: created: %s",cname);
588                         /* set created to 2 to indicate user
589                          * is the first in the channel
590                          * and should be given ops */
591                         created = 2;
592                 }
593         }
594         else
595         {
596                 /* channel exists, just fish out a pointer to its struct */
597                 Ptr = FindChan(cname);
598                 if (Ptr)
599                 {
600                         log(DEBUG,"add_channel: joining to: %s",Ptr->name);
601                         
602                         // the override flag allows us to bypass channel modes
603                         // and bans (used by servers)
604                         if ((!override) || (!strcasecmp(user->server,ServerName)))
605                         {
606                                 log(DEBUG,"Not overriding...");
607                                 MOD_RESULT = 0;
608                                 FOREACH_RESULT(OnUserPreJoin(user,Ptr,cname));
609                                 if (MOD_RESULT == 1) {
610                                         return NULL;
611                                 }
612                                 log(DEBUG,"MOD_RESULT=%d",MOD_RESULT);
613                                 
614                                 if (!MOD_RESULT) 
615                                 {
616                                         log(DEBUG,"add_channel: checking key, invite, etc");
617                                         MOD_RESULT = 0;
618                                         FOREACH_RESULT(OnCheckKey(user, Ptr, key ? key : ""));
619                                         if (MOD_RESULT == 0)
620                                         {
621                                                 if (Ptr->key[0])
622                                                 {
623                                                         log(DEBUG,"add_channel: %s has key %s",Ptr->name,Ptr->key);
624                                                         if (!key)
625                                                         {
626                                                                 log(DEBUG,"add_channel: no key given in JOIN");
627                                                                 WriteServ(user->fd,"475 %s %s :Cannot join channel (Requires key)",user->nick, Ptr->name);
628                                                                 return NULL;
629                                                         }
630                                                         else
631                                                         {
632                                                                 if (strcasecmp(key,Ptr->key))
633                                                                 {
634                                                                         log(DEBUG,"add_channel: bad key given in JOIN");
635                                                                         WriteServ(user->fd,"475 %s %s :Cannot join channel (Incorrect key)",user->nick, Ptr->name);
636                                                                         return NULL;
637                                                                 }
638                                                         }
639                                                 }
640                                                 log(DEBUG,"add_channel: no key");
641                                         }
642                                         MOD_RESULT = 0;
643                                         FOREACH_RESULT(OnCheckInvite(user, Ptr));
644                                         if (MOD_RESULT == 0)
645                                         {
646                                                 if (Ptr->binarymodes & CM_INVITEONLY)
647                                                 {
648                                                         log(DEBUG,"add_channel: channel is +i");
649                                                         if (user->IsInvited(Ptr->name))
650                                                         {
651                                                                 /* user was invited to channel */
652                                                                 /* there may be an optional channel NOTICE here */
653                                                         }
654                                                         else
655                                                         {
656                                                                 WriteServ(user->fd,"473 %s %s :Cannot join channel (Invite only)",user->nick, Ptr->name);
657                                                                 return NULL;
658                                                         }
659                                                 }
660                                                 log(DEBUG,"add_channel: channel is not +i");
661                                         }
662                                         MOD_RESULT = 0;
663                                         FOREACH_RESULT(OnCheckLimit(user, Ptr));
664                                         if (MOD_RESULT == 0)
665                                         {
666                                                 if (Ptr->limit)
667                                                 {
668                                                         if (usercount(Ptr) >= Ptr->limit)
669                                                         {
670                                                                 WriteServ(user->fd,"471 %s %s :Cannot join channel (Channel is full)",user->nick, Ptr->name);
671                                                                 return NULL;
672                                                         }
673                                                 }
674                                         }
675                                         log(DEBUG,"add_channel: about to walk banlist");
676                                         MOD_RESULT = 0;
677                                         FOREACH_RESULT(OnCheckBan(user, Ptr));
678                                         if (MOD_RESULT == 0)
679                                         {
680                                                 /* check user against the channel banlist */
681                                                 if (Ptr)
682                                                 {
683                                                         if (Ptr->bans.size())
684                                                         {
685                                                                 for (BanList::iterator i = Ptr->bans.begin(); i != Ptr->bans.end(); i++)
686                                                                 {
687                                                                         if (match(user->GetFullHost(),i->data))
688                                                                         {
689                                                                                 WriteServ(user->fd,"474 %s %s :Cannot join channel (You're banned)",user->nick, Ptr->name);
690                                                                                 return NULL;
691                                                                         }
692                                                                 }
693                                                         }
694                                                 }
695                                                 log(DEBUG,"add_channel: bans checked");
696                                         }
697                                 
698                                 }
699                                 
700
701                                 if ((Ptr) && (user))
702                                 {
703                                         user->RemoveInvite(Ptr->name);
704                                 }
705         
706                                 log(DEBUG,"add_channel: invites removed");
707
708                         }
709                         else
710                         {
711                                 log(DEBUG,"Overridden checks");
712                         }
713
714                         
715                 }
716                 created = 1;
717         }
718
719         log(DEBUG,"Passed channel checks");
720         
721         for (int index =0; index != MAXCHANS; index++)
722         {
723                 log(DEBUG,"Check location %d",index);
724                 if (user->chans[index].channel == NULL)
725                 {
726                         log(DEBUG,"Adding into their channel list at location %d",index);
727
728                         if (created == 2) 
729                         {
730                                 /* first user in is given ops */
731                                 user->chans[index].uc_modes = UCMODE_OP;
732                         }
733                         else
734                         {
735                                 user->chans[index].uc_modes = 0;
736                         }
737                         user->chans[index].channel = Ptr;
738                         Ptr->AddUser((char*)user);
739                         WriteChannel(Ptr,user,"JOIN :%s",Ptr->name);
740                         
741                         if (!override) // we're not overriding... so this isnt part of a netburst, broadcast it.
742                         {
743                                 // use the stamdard J token with no privilages.
744                                 char buffer[MAXBUF];
745                                 if (created == 2)
746                                 {
747                                         snprintf(buffer,MAXBUF,"J %s @%s",user->nick,Ptr->name);
748                                 }
749                                 else
750                                 {
751                                         snprintf(buffer,MAXBUF,"J %s %s",user->nick,Ptr->name);
752                                 }
753                                 NetSendToAll(buffer);
754                         }
755
756                         log(DEBUG,"Sent JOIN to client");
757
758                         if (Ptr->topicset)
759                         {
760                                 WriteServ(user->fd,"332 %s %s :%s", user->nick, Ptr->name, Ptr->topic);
761                                 WriteServ(user->fd,"333 %s %s %s %lu", user->nick, Ptr->name, Ptr->setby, (unsigned long)Ptr->topicset);
762                         }
763                         userlist(user,Ptr);
764                         WriteServ(user->fd,"366 %s %s :End of /NAMES list.", user->nick, Ptr->name);
765                         //WriteServ(user->fd,"324 %s %s +%s",user->nick, Ptr->name,chanmodes(Ptr));
766                         //WriteServ(user->fd,"329 %s %s %lu", user->nick, Ptr->name, (unsigned long)Ptr->created);
767                         FOREACH_MOD OnUserJoin(user,Ptr);
768                         return Ptr;
769                 }
770         }
771         log(DEBUG,"add_channel: user channel max exceeded: %s %s",user->nick,cname);
772         WriteServ(user->fd,"405 %s %s :You are on too many channels",user->nick, cname);
773         return NULL;
774 }
775
776 /* remove a channel from a users record, and remove the record from memory
777  * if the channel has become empty */
778
779 chanrec* del_channel(userrec *user, const char* cname, const char* reason, bool local)
780 {
781         if ((!user) || (!cname))
782         {
783                 log(DEFAULT,"*** BUG *** del_channel was given an invalid parameter");
784                 return NULL;
785         }
786
787         chanrec* Ptr;
788
789         if ((!cname) || (!user))
790         {
791                 return NULL;
792         }
793
794         Ptr = FindChan(cname);
795         
796         if (!Ptr)
797         {
798                 return NULL;
799         }
800
801         FOREACH_MOD OnUserPart(user,Ptr);
802         log(DEBUG,"del_channel: removing: %s %s",user->nick,Ptr->name);
803         
804         for (int i =0; i != MAXCHANS; i++)
805         {
806                 /* zap it from the channel list of the user */
807                 if (user->chans[i].channel == Ptr)
808                 {
809                         if (reason)
810                         {
811                                 WriteChannel(Ptr,user,"PART %s :%s",Ptr->name, reason);
812
813                                 if (!local)
814                                 {
815                                         char buffer[MAXBUF];
816                                         snprintf(buffer,MAXBUF,"L %s %s :%s",user->nick,Ptr->name,reason);
817                                         NetSendToAll(buffer);
818                                 }
819
820                                 
821                         }
822                         else
823                         {
824                                 if (!local)
825                                 {
826                                         char buffer[MAXBUF];
827                                         snprintf(buffer,MAXBUF,"L %s %s :",user->nick,Ptr->name);
828                                         NetSendToAll(buffer);
829                                 }
830                         
831                                 WriteChannel(Ptr,user,"PART :%s",Ptr->name);
832                         }
833                         user->chans[i].uc_modes = 0;
834                         user->chans[i].channel = NULL;
835                         log(DEBUG,"del_channel: unlinked: %s %s",user->nick,Ptr->name);
836                         break;
837                 }
838         }
839
840         Ptr->DelUser((char*)user);
841         
842         /* if there are no users left on the channel */
843         if (!usercount(Ptr))
844         {
845                 chan_hash::iterator iter = chanlist.find(Ptr->name);
846
847                 log(DEBUG,"del_channel: destroying channel: %s",Ptr->name);
848
849                 /* kill the record */
850                 if (iter != chanlist.end())
851                 {
852                         log(DEBUG,"del_channel: destroyed: %s",Ptr->name);
853                         delete Ptr;
854                         chanlist.erase(iter);
855                 }
856         }
857
858         return NULL;
859 }
860
861
862 void kick_channel(userrec *src,userrec *user, chanrec *Ptr, char* reason)
863 {
864         if ((!src) || (!user) || (!Ptr) || (!reason))
865         {
866                 log(DEFAULT,"*** BUG *** kick_channel was given an invalid parameter");
867                 return;
868         }
869
870         if ((!Ptr) || (!user) || (!src))
871         {
872                 return;
873         }
874
875         log(DEBUG,"kick_channel: removing: %s %s %s",user->nick,Ptr->name,src->nick);
876
877         if (!has_channel(user,Ptr))
878         {
879                 WriteServ(src->fd,"441 %s %s %s :They are not on that channel",src->nick, user->nick, Ptr->name);
880                 return;
881         }
882
883         int MOD_RESULT = 0;
884         FOREACH_RESULT(OnAccessCheck(src,user,Ptr,AC_KICK));
885         if (MOD_RESULT == ACR_DENY)
886                 return;
887
888         if (MOD_RESULT == ACR_DEFAULT)
889         {
890                 if (((cstatus(src,Ptr) < STATUS_HOP) || (cstatus(src,Ptr) < cstatus(user,Ptr))) && (!is_uline(src->server)))
891                 {
892                         if (cstatus(src,Ptr) == STATUS_HOP)
893                         {
894                                 WriteServ(src->fd,"482 %s %s :You must be a channel operator",src->nick, Ptr->name);
895                         }
896                         else
897                         {
898                                 WriteServ(src->fd,"482 %s %s :You must be at least a half-operator to change modes on this channel",src->nick, Ptr->name);
899                         }
900                         
901                         return;
902                 }
903         }
904
905         MOD_RESULT = 0;
906         FOREACH_RESULT(OnUserPreKick(src,user,Ptr,reason));
907         if (MOD_RESULT)
908                 return;
909
910         FOREACH_MOD OnUserKick(src,user,Ptr,reason);
911
912         for (int i =0; i != MAXCHANS; i++)
913         {
914                 /* zap it from the channel list of the user */
915                 if (user->chans[i].channel)
916                 if (!strcasecmp(user->chans[i].channel->name,Ptr->name))
917                 {
918                         WriteChannel(Ptr,src,"KICK %s %s :%s",Ptr->name, user->nick, reason);
919                         user->chans[i].uc_modes = 0;
920                         user->chans[i].channel = NULL;
921                         log(DEBUG,"del_channel: unlinked: %s %s",user->nick,Ptr->name);
922                         break;
923                 }
924         }
925
926         Ptr->DelUser((char*)user);
927
928         /* if there are no users left on the channel */
929         if (!usercount(Ptr))
930         {
931                 chan_hash::iterator iter = chanlist.find(Ptr->name);
932
933                 log(DEBUG,"del_channel: destroying channel: %s",Ptr->name);
934
935                 /* kill the record */
936                 if (iter != chanlist.end())
937                 {
938                         log(DEBUG,"del_channel: destroyed: %s",Ptr->name);
939                         delete Ptr;
940                         chanlist.erase(iter);
941                 }
942         }
943 }
944
945
946
947
948 /* This function pokes and hacks at a parameter list like the following:
949  *
950  * PART #winbot,#darkgalaxy :m00!
951  *
952  * to turn it into a series of individual calls like this:
953  *
954  * PART #winbot :m00!
955  * PART #darkgalaxy :m00!
956  *
957  * The seperate calls are sent to a callback function provided by the caller
958  * (the caller will usually call itself recursively). The callback function
959  * must be a command handler. Calling this function on a line with no list causes
960  * no action to be taken. You must provide a starting and ending parameter number
961  * where the range of the list can be found, useful if you have a terminating
962  * parameter as above which is actually not part of the list, or parameters
963  * before the actual list as well. This code is used by many functions which
964  * can function as "one to list" (see the RFC) */
965
966 int loop_call(handlerfunc fn, char **parameters, int pcnt, userrec *u, int start, int end, int joins)
967 {
968         char plist[MAXBUF];
969         char *param;
970         char *pars[32];
971         char blog[32][MAXBUF];
972         char blog2[32][MAXBUF];
973         int j = 0, q = 0, total = 0, t = 0, t2 = 0, total2 = 0;
974         char keystr[MAXBUF];
975         char moo[MAXBUF];
976
977         for (int i = 0; i <32; i++)
978                 strcpy(blog[i],"");
979
980         for (int i = 0; i <32; i++)
981                 strcpy(blog2[i],"");
982
983         strcpy(moo,"");
984         for (int i = 0; i <10; i++)
985         {
986                 if (!parameters[i])
987                 {
988                         parameters[i] = moo;
989                 }
990         }
991         if (joins)
992         {
993                 if (pcnt > 1) /* we have a key to copy */
994                 {
995                         strlcpy(keystr,parameters[1],MAXBUF);
996                 }
997         }
998
999         if (!parameters[start])
1000         {
1001                 return 0;
1002         }
1003         if (!strchr(parameters[start],','))
1004         {
1005                 return 0;
1006         }
1007         strcpy(plist,"");
1008         for (int i = start; i <= end; i++)
1009         {
1010                 if (parameters[i])
1011                 {
1012                         strlcat(plist,parameters[i],MAXBUF);
1013                 }
1014         }
1015         
1016         j = 0;
1017         param = plist;
1018
1019         t = strlen(plist);
1020         for (int i = 0; i < t; i++)
1021         {
1022                 if (plist[i] == ',')
1023                 {
1024                         plist[i] = '\0';
1025                         strlcpy(blog[j++],param,MAXBUF);
1026                         param = plist+i+1;
1027                         if (j>20)
1028                         {
1029                                 WriteServ(u->fd,"407 %s %s :Too many targets in list, message not delivered.",u->nick,blog[j-1]);
1030                                 return 1;
1031                         }
1032                 }
1033         }
1034         strlcpy(blog[j++],param,MAXBUF);
1035         total = j;
1036
1037         if ((joins) && (keystr) && (total>0)) // more than one channel and is joining
1038         {
1039                 strcat(keystr,",");
1040         }
1041         
1042         if ((joins) && (keystr))
1043         {
1044                 if (strchr(keystr,','))
1045                 {
1046                         j = 0;
1047                         param = keystr;
1048                         t2 = strlen(keystr);
1049                         for (int i = 0; i < t2; i++)
1050                         {
1051                                 if (keystr[i] == ',')
1052                                 {
1053                                         keystr[i] = '\0';
1054                                         strlcpy(blog2[j++],param,MAXBUF);
1055                                         param = keystr+i+1;
1056                                 }
1057                         }
1058                         strlcpy(blog2[j++],param,MAXBUF);
1059                         total2 = j;
1060                 }
1061         }
1062
1063         for (j = 0; j < total; j++)
1064         {
1065                 if (blog[j])
1066                 {
1067                         pars[0] = blog[j];
1068                 }
1069                 for (q = end; q < pcnt-1; q++)
1070                 {
1071                         if (parameters[q+1])
1072                         {
1073                                 pars[q-end+1] = parameters[q+1];
1074                         }
1075                 }
1076                 if ((joins) && (parameters[1]))
1077                 {
1078                         if (pcnt > 1)
1079                         {
1080                                 pars[1] = blog2[j];
1081                         }
1082                         else
1083                         {
1084                                 pars[1] = NULL;
1085                         }
1086                 }
1087                 /* repeatedly call the function with the hacked parameter list */
1088                 if ((joins) && (pcnt > 1))
1089                 {
1090                         if (pars[1])
1091                         {
1092                                 // pars[1] already set up and containing key from blog2[j]
1093                                 fn(pars,2,u);
1094                         }
1095                         else
1096                         {
1097                                 pars[1] = parameters[1];
1098                                 fn(pars,2,u);
1099                         }
1100                 }
1101                 else
1102                 {
1103                         fn(pars,pcnt-(end-start),u);
1104                 }
1105         }
1106
1107         return 1;
1108 }
1109
1110
1111
1112 void kill_link(userrec *user,const char* r)
1113 {
1114         user_hash::iterator iter = clientlist.find(user->nick);
1115         
1116         char reason[MAXBUF];
1117         
1118         strncpy(reason,r,MAXBUF);
1119
1120         if (strlen(reason)>MAXQUIT)
1121         {
1122                 reason[MAXQUIT-1] = '\0';
1123         }
1124
1125         log(DEBUG,"kill_link: %s '%s'",user->nick,reason);
1126         Write(user->fd,"ERROR :Closing link (%s@%s) [%s]",user->ident,user->host,reason);
1127         log(DEBUG,"closing fd %lu",(unsigned long)user->fd);
1128
1129         if (user->registered == 7) {
1130                 FOREACH_MOD OnUserQuit(user);
1131                 WriteCommonExcept(user,"QUIT :%s",reason);
1132
1133                 // Q token must go to ALL servers!!!
1134                 char buffer[MAXBUF];
1135                 snprintf(buffer,MAXBUF,"Q %s :%s",user->nick,reason);
1136                 NetSendToAll(buffer);
1137         }
1138
1139         user->FlushWriteBuf();
1140
1141         FOREACH_MOD OnUserDisconnect(user);
1142
1143         if (user->fd > -1)
1144         {
1145                 FOREACH_MOD OnRawSocketClose(user->fd);
1146                 engine_delete_fd;
1147                 user->CloseSocket();
1148         }
1149
1150         // this must come before the WriteOpers so that it doesnt try to fill their buffer with anything
1151         // if they were an oper with +s.
1152         if (user->registered == 7) {
1153                 purge_empty_chans(user);
1154                 // fix by brain: only show local quits because we only show local connects (it just makes SENSE)
1155                 if (!strcmp(user->server,ServerName))
1156                         WriteOpers("*** Client exiting: %s!%s@%s [%s]",user->nick,user->ident,user->host,reason);
1157                 AddWhoWas(user);
1158         }
1159
1160         if (iter != clientlist.end())
1161         {
1162                 log(DEBUG,"deleting user hash value %lu",(unsigned long)user);
1163                 if (user->fd > -1)
1164                         fd_ref_table[user->fd] = NULL;
1165                 clientlist.erase(iter);
1166         }
1167         delete user;
1168 }
1169
1170 void kill_link_silent(userrec *user,const char* r)
1171 {
1172         user_hash::iterator iter = clientlist.find(user->nick);
1173         
1174         char reason[MAXBUF];
1175         
1176         strncpy(reason,r,MAXBUF);
1177
1178         if (strlen(reason)>MAXQUIT)
1179         {
1180                 reason[MAXQUIT-1] = '\0';
1181         }
1182
1183         log(DEBUG,"kill_link: %s '%s'",user->nick,reason);
1184         Write(user->fd,"ERROR :Closing link (%s@%s) [%s]",user->ident,user->host,reason);
1185         log(DEBUG,"closing fd %lu",(unsigned long)user->fd);
1186
1187         user->FlushWriteBuf();
1188
1189         if (user->registered == 7) {
1190                 FOREACH_MOD OnUserQuit(user);
1191                 WriteCommonExcept(user,"QUIT :%s",reason);
1192
1193                 // Q token must go to ALL servers!!!
1194                 char buffer[MAXBUF];
1195                 snprintf(buffer,MAXBUF,"Q %s :%s",user->nick,reason);
1196                 NetSendToAll(buffer);
1197         }
1198
1199         FOREACH_MOD OnUserDisconnect(user);
1200
1201         if (user->fd > -1)
1202         {
1203                 FOREACH_MOD OnRawSocketClose(user->fd);
1204                 engine_delete_fd;
1205                 user->CloseSocket();
1206         }
1207
1208         if (user->registered == 7) {
1209                 purge_empty_chans(user);
1210         }
1211         
1212         if (iter != clientlist.end())
1213         {
1214                 log(DEBUG,"deleting user hash value %lu",(unsigned long)user);
1215                 if (user->fd > -1)
1216                         fd_ref_table[user->fd] = NULL;
1217                 clientlist.erase(iter);
1218         }
1219         delete user;
1220 }
1221
1222
1223 int main(int argc, char** argv)
1224 {
1225         Start();
1226         srand(time(NULL));
1227         log(DEBUG,"*** InspIRCd starting up!");
1228         if (!FileExists(CONFIG_FILE))
1229         {
1230                 printf("ERROR: Cannot open config file: %s\nExiting...\n",CONFIG_FILE);
1231                 log(DEFAULT,"main: no config");
1232                 printf("ERROR: Your config file is missing, this IRCd will self destruct in 10 seconds!\n");
1233                 Exit(ERROR);
1234         }
1235         if (argc > 1) {
1236                 for (int i = 1; i < argc; i++)
1237                 {
1238                         if (!strcmp(argv[i],"-nofork")) {
1239                                 nofork = true;
1240                         }
1241                         if (!strcmp(argv[i],"-wait")) {
1242                                 sleep(6);
1243                         }
1244                         if (!strcmp(argv[i],"-nolimit")) {
1245                                 unlimitcore = true;
1246                         }
1247                 }
1248         }
1249         strlcpy(MyExecutable,argv[0],MAXBUF);
1250         
1251         // initialize the lowercase mapping table
1252         for (unsigned int cn = 0; cn < 256; cn++)
1253                 lowermap[cn] = cn;
1254         // lowercase the uppercase chars
1255         for (unsigned int cn = 65; cn < 91; cn++)
1256                 lowermap[cn] = tolower(cn);
1257         // now replace the specific chars for scandanavian comparison
1258         lowermap[(unsigned)'['] = '{';
1259         lowermap[(unsigned)']'] = '}';
1260         lowermap[(unsigned)'\\'] = '|';
1261
1262         if (InspIRCd(argv,argc) == ERROR)
1263         {
1264                 log(DEFAULT,"main: daemon function bailed");
1265                 printf("ERROR: could not initialise. Shutting down.\n");
1266                 Exit(ERROR);
1267         }
1268         Exit(TRUE);
1269         return 0;
1270 }
1271
1272 template<typename T> inline string ConvToStr(const T &in)
1273 {
1274         stringstream tmp;
1275         if (!(tmp << in)) return string();
1276         return tmp.str();
1277 }
1278
1279 /* re-allocates a nick in the user_hash after they change nicknames,
1280  * returns a pointer to the new user as it may have moved */
1281
1282 userrec* ReHashNick(char* Old, char* New)
1283 {
1284         //user_hash::iterator newnick;
1285         user_hash::iterator oldnick = clientlist.find(Old);
1286
1287         log(DEBUG,"ReHashNick: %s %s",Old,New);
1288         
1289         if (!strcasecmp(Old,New))
1290         {
1291                 log(DEBUG,"old nick is new nick, skipping");
1292                 return oldnick->second;
1293         }
1294         
1295         if (oldnick == clientlist.end()) return NULL; /* doesnt exist */
1296
1297         log(DEBUG,"ReHashNick: Found hashed nick %s",Old);
1298
1299         userrec* olduser = oldnick->second;
1300         clientlist[New] = olduser;
1301         clientlist.erase(oldnick);
1302
1303         log(DEBUG,"ReHashNick: Nick rehashed as %s",New);
1304         
1305         return clientlist[New];
1306 }
1307
1308 /* adds or updates an entry in the whowas list */
1309 void AddWhoWas(userrec* u)
1310 {
1311         whowas_hash::iterator iter = whowas.find(u->nick);
1312         WhoWasUser *a = new WhoWasUser();
1313         strlcpy(a->nick,u->nick,NICKMAX);
1314         strlcpy(a->ident,u->ident,IDENTMAX);
1315         strlcpy(a->dhost,u->dhost,160);
1316         strlcpy(a->host,u->host,160);
1317         strlcpy(a->fullname,u->fullname,MAXGECOS);
1318         strlcpy(a->server,u->server,256);
1319         a->signon = u->signon;
1320
1321         /* MAX_WHOWAS:   max number of /WHOWAS items
1322          * WHOWAS_STALE: number of hours before a WHOWAS item is marked as stale and
1323          *               can be replaced by a newer one
1324          */
1325         
1326         if (iter == whowas.end())
1327         {
1328                 if (whowas.size() >= (unsigned)WHOWAS_MAX)
1329                 {
1330                         for (whowas_hash::iterator i = whowas.begin(); i != whowas.end(); i++)
1331                         {
1332                                 // 3600 seconds in an hour ;)
1333                                 if ((i->second->signon)<(TIME-(WHOWAS_STALE*3600)))
1334                                 {
1335                                         // delete the old one
1336                                         if (i->second) delete i->second;
1337                                         // replace with new one
1338                                         i->second = a;
1339                                         log(DEBUG,"added WHOWAS entry, purged an old record");
1340                                         return;
1341                                 }
1342                         }
1343                         // no space left and user doesnt exist. Don't leave ram in use!
1344                         log(DEBUG,"Not able to update whowas (list at WHOWAS_MAX entries and trying to add new?), freeing excess ram");
1345                         delete a;
1346                 }
1347                 else
1348                 {
1349                         log(DEBUG,"added fresh WHOWAS entry");
1350                         whowas[a->nick] = a;
1351                 }
1352         }
1353         else
1354         {
1355                 log(DEBUG,"updated WHOWAS entry");
1356                 if (iter->second) delete iter->second;
1357                 iter->second = a;
1358         }
1359 }
1360
1361
1362 /* add a client connection to the sockets list */
1363 void AddClient(int socket, char* host, int port, bool iscached, char* ip)
1364 {
1365         string tempnick;
1366         char tn2[MAXBUF];
1367         user_hash::iterator iter;
1368
1369         tempnick = ConvToStr(socket) + "-unknown";
1370         sprintf(tn2,"%lu-unknown",(unsigned long)socket);
1371
1372         iter = clientlist.find(tempnick);
1373
1374         // fix by brain.
1375         // as these nicknames are 'RFC impossible', we can be sure nobody is going to be
1376         // using one as a registered connection. As theyre per fd, we can also safely assume
1377         // that we wont have collisions. Therefore, if the nick exists in the list, its only
1378         // used by a dead socket, erase the iterator so that the new client may reclaim it.
1379         // this was probably the cause of 'server ignores me when i hammer it with reconnects'
1380         // issue in earlier alphas/betas
1381         if (iter != clientlist.end())
1382         {
1383                 userrec* goner = iter->second;
1384                 delete goner;
1385                 clientlist.erase(iter);
1386         }
1387
1388         /*
1389          * It is OK to access the value here this way since we know
1390          * it exists, we just created it above.
1391          *
1392          * At NO other time should you access a value in a map or a
1393          * hash_map this way.
1394          */
1395         clientlist[tempnick] = new userrec();
1396
1397         NonBlocking(socket);
1398         log(DEBUG,"AddClient: %lu %s %d %s",(unsigned long)socket,host,port,ip);
1399
1400         clientlist[tempnick]->fd = socket;
1401         strlcpy(clientlist[tempnick]->nick, tn2,NICKMAX);
1402         strlcpy(clientlist[tempnick]->host, host,160);
1403         strlcpy(clientlist[tempnick]->dhost, host,160);
1404         clientlist[tempnick]->server = (char*)FindServerNamePtr(ServerName);
1405         strlcpy(clientlist[tempnick]->ident, "unknown",IDENTMAX);
1406         clientlist[tempnick]->registered = 0;
1407         clientlist[tempnick]->signon = TIME+dns_timeout;
1408         clientlist[tempnick]->lastping = 1;
1409         clientlist[tempnick]->port = port;
1410         strlcpy(clientlist[tempnick]->ip,ip,16);
1411
1412         // set the registration timeout for this user
1413         unsigned long class_regtimeout = 90;
1414         int class_flood = 0;
1415         long class_threshold = 5;
1416         long class_sqmax = 262144;      // 256kb
1417         long class_rqmax = 4096;        // 4k
1418
1419         for (ClassVector::iterator i = Classes.begin(); i != Classes.end(); i++)
1420         {
1421                 if (match(clientlist[tempnick]->host,i->host) && (i->type == CC_ALLOW))
1422                 {
1423                         class_regtimeout = (unsigned long)i->registration_timeout;
1424                         class_flood = i->flood;
1425                         clientlist[tempnick]->pingmax = i->pingtime;
1426                         class_threshold = i->threshold;
1427                         class_sqmax = i->sendqmax;
1428                         class_rqmax = i->recvqmax;
1429                         break;
1430                 }
1431         }
1432
1433         clientlist[tempnick]->nping = TIME+clientlist[tempnick]->pingmax+dns_timeout;
1434         clientlist[tempnick]->timeout = TIME+class_regtimeout;
1435         clientlist[tempnick]->flood = class_flood;
1436         clientlist[tempnick]->threshold = class_threshold;
1437         clientlist[tempnick]->sendqmax = class_sqmax;
1438         clientlist[tempnick]->recvqmax = class_rqmax;
1439
1440         for (int i = 0; i < MAXCHANS; i++)
1441         {
1442                 clientlist[tempnick]->chans[i].channel = NULL;
1443                 clientlist[tempnick]->chans[i].uc_modes = 0;
1444         }
1445
1446         if (clientlist.size() > SoftLimit)
1447         {
1448                 kill_link(clientlist[tempnick],"No more connections allowed");
1449                 return;
1450         }
1451
1452         if (clientlist.size() >= MAXCLIENTS)
1453         {
1454                 kill_link(clientlist[tempnick],"No more connections allowed");
1455                 return;
1456         }
1457
1458         // this is done as a safety check to keep the file descriptors within range of fd_ref_table.
1459         // its a pretty big but for the moment valid assumption:
1460         // file descriptors are handed out starting at 0, and are recycled as theyre freed.
1461         // therefore if there is ever an fd over 65535, 65536 clients must be connected to the
1462         // irc server at once (or the irc server otherwise initiating this many connections, files etc)
1463         // which for the time being is a physical impossibility (even the largest networks dont have more
1464         // than about 10,000 users on ONE server!)
1465         if ((unsigned)socket > 65534)
1466         {
1467                 kill_link(clientlist[tempnick],"Server is full");
1468                 return;
1469         }
1470                 
1471
1472         char* e = matches_exception(ip);
1473         if (!e)
1474         {
1475                 char* r = matches_zline(ip);
1476                 if (r)
1477                 {
1478                         char reason[MAXBUF];
1479                         snprintf(reason,MAXBUF,"Z-Lined: %s",r);
1480                         kill_link(clientlist[tempnick],reason);
1481                         return;
1482                 }
1483         }
1484         fd_ref_table[socket] = clientlist[tempnick];
1485         engine_add_fd;
1486 }
1487
1488 /* shows the message of the day, and any other on-logon stuff */
1489 void FullConnectUser(userrec* user)
1490 {
1491         statsConnects++;
1492         user->idle_lastmsg = TIME;
1493         log(DEBUG,"ConnectUser: %s",user->nick);
1494
1495         if ((strcmp(Passwd(user),"")) && (!user->haspassed))
1496         {
1497                 kill_link(user,"Invalid password");
1498                 return;
1499         }
1500         if (IsDenied(user))
1501         {
1502                 kill_link(user,"Unauthorised connection");
1503                 return;
1504         }
1505
1506         char match_against[MAXBUF];
1507         snprintf(match_against,MAXBUF,"%s@%s",user->ident,user->host);
1508         char* e = matches_exception(match_against);
1509         if (!e)
1510         {
1511                 char* r = matches_gline(match_against);
1512                 if (r)
1513                 {
1514                         char reason[MAXBUF];
1515                         snprintf(reason,MAXBUF,"G-Lined: %s",r);
1516                         kill_link_silent(user,reason);
1517                         return;
1518                 }
1519                 r = matches_kline(user->host);
1520                 if (r)
1521                 {
1522                         char reason[MAXBUF];
1523                         snprintf(reason,MAXBUF,"K-Lined: %s",r);
1524                         kill_link_silent(user,reason);
1525                         return;
1526                 }
1527         }
1528
1529         // fix by brain: move this below the xline checks to prevent spurious quits going onto the net that dont belong
1530         user->registered = 7;
1531
1532         WriteServ(user->fd,"NOTICE Auth :Welcome to \002%s\002!",Network);
1533         WriteServ(user->fd,"001 %s :Welcome to the %s IRC Network %s!%s@%s",user->nick,Network,user->nick,user->ident,user->host);
1534         WriteServ(user->fd,"002 %s :Your host is %s, running version %s",user->nick,ServerName,VERSION);
1535         WriteServ(user->fd,"003 %s :This server was created %s %s",user->nick,__TIME__,__DATE__);
1536         WriteServ(user->fd,"004 %s %s %s iowghraAsORVSxNCWqBzvdHtGI lvhopsmntikrRcaqOALQbSeKVfHGCuzN",user->nick,ServerName,VERSION);
1537         // the neatest way to construct the initial 005 numeric, considering the number of configure constants to go in it...
1538         std::stringstream v;
1539         v << "MESHED WALLCHOPS MODES=13 CHANTYPES=# PREFIX=(ohv)@%+ MAP SAFELIST MAXCHANNELS=" << MAXCHANS;
1540         v << " MAXBANS=60 NICKLEN=" << NICKMAX;
1541         v << " TOPICLEN=" << MAXTOPIC << " KICKLEN=" << MAXKICK << " MAXTARGETS=20 AWAYLEN=" << MAXAWAY << " CHANMODES=ohvb,k,l,psmnti NETWORK=";
1542         v << Network;
1543         std::string data005 = v.str();
1544         FOREACH_MOD On005Numeric(data005);
1545         // anfl @ #ratbox, efnet reminded me that according to the RFC this cant contain more than 13 tokens per line...
1546         // so i'd better split it :)
1547         std::stringstream out(data005);
1548         std::string token = "";
1549         std::string line5 = "";
1550         int token_counter = 0;
1551         while (!out.eof())
1552         {
1553                 out >> token;
1554                 line5 = line5 + token + " ";
1555                 token_counter++;
1556                 if ((token_counter >= 13) || (out.eof() == true))
1557                 {
1558                         WriteServ(user->fd,"005 %s %s:are supported by this server",user->nick,line5.c_str());
1559                         line5 = "";
1560                         token_counter = 0;
1561                 }
1562         }
1563         ShowMOTD(user);
1564
1565         char buffer[MAXBUF];
1566         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);
1567         NetSendToAll(buffer);
1568
1569         // fix by brain: these should be AFTER the N token, so other servers know what the HELL we're on about... :)
1570         FOREACH_MOD OnUserConnect(user);
1571         FOREACH_MOD OnGlobalConnect(user);
1572         WriteOpers("*** Client connecting on port %lu: %s!%s@%s [%s]",(unsigned long)user->port,user->nick,user->ident,user->host,user->ip);
1573 }
1574
1575
1576 /* shows the message of the day, and any other on-logon stuff */
1577 void ConnectUser(userrec *user)
1578 {
1579         // dns is already done, things are fast. no need to wait for dns to complete just pass them straight on
1580         if ((user->dns_done) && (user->registered >= 3) && (AllModulesReportReady(user)))
1581         {
1582                 FullConnectUser(user);
1583         }
1584 }
1585
1586 std::string GetVersionString()
1587 {
1588         char Revision[] = "$Revision$";
1589         char versiondata[MAXBUF];
1590         char *s1 = Revision;
1591         char *savept;
1592         char *v2 = strtok_r(s1," ",&savept);
1593         s1 = savept;
1594         v2 = strtok_r(s1," ",&savept);
1595         s1 = savept;
1596         char socketengine[] = engine_name;
1597         snprintf(versiondata,MAXBUF,"%s Rev. %s %s :%s (O=%lu) [SE=%s]",VERSION,v2,ServerName,SYSTEM,(unsigned long)OPTIMISATION,socketengine);
1598         return versiondata;
1599 }
1600
1601 void handle_version(char **parameters, int pcnt, userrec *user)
1602 {
1603         if (!pcnt)
1604         {
1605                 WriteServ(user->fd,"351 %s :%s",user->nick,GetVersionString().c_str());
1606         }
1607         else
1608         {
1609                 if (!strcmp(parameters[0],"*"))
1610                 {
1611                         for (int j = 0; j < 32; j++)
1612                         {
1613                                 if (me[j] != NULL)
1614                                 {
1615                                         for (unsigned int x = 0; x < me[j]->connectors.size(); x++)
1616                                         {
1617                                                 WriteServ(user->fd,"351 %s :Server %d:%d (%s): %s",user->nick,j,x,me[j]->connectors[x].GetServerName().c_str(),me[j]->connectors[x].GetVersionString().c_str());
1618                                         }
1619                                 }
1620                         }
1621                         return;
1622                 }
1623                 if (match(ServerName,parameters[0]))
1624                 {
1625                         WriteServ(user->fd,"351 %s :%s",user->nick,GetVersionString().c_str());
1626                         return;
1627                 }
1628                 bool displayed = false, found = false;
1629                 for (int j = 0; j < 32; j++)
1630                 {
1631                         if (me[j] != NULL)
1632                         {
1633                                 for (unsigned int x = 0; x < me[j]->connectors.size(); x++)
1634                                 {
1635                                         if (match(me[j]->connectors[x].GetServerName().c_str(),parameters[0]))
1636                                         {
1637                                                 found = true;
1638                                                 if ((me[j]->connectors[x].GetVersionString() != "") && (!displayed))
1639                                                 {
1640                                                         displayed = true;
1641                                                         WriteServ(user->fd,"351 %s :%s",user->nick,me[j]->connectors[x].GetVersionString().c_str());
1642                                                 }
1643                                         }
1644                                 }
1645                         }
1646                 }
1647                 if ((!displayed) && (found))
1648                 {
1649                         WriteServ(user->fd,"402 %s %s :Server %s has no version information",user->nick,parameters[0],parameters[0]);
1650                         return;
1651                 }
1652                 if (!found)
1653                 {
1654                         WriteServ(user->fd,"402 %s %s :No such server",user->nick,parameters[0]);
1655                 }
1656         }
1657         return;
1658 }
1659
1660
1661 // calls a handler function for a command
1662
1663 void call_handler(const char* commandname,char **parameters, int pcnt, userrec *user)
1664 {
1665                 for (unsigned int i = 0; i < cmdlist.size(); i++)
1666                 {
1667                         if (!strcasecmp(cmdlist[i].command,commandname))
1668                         {
1669                                 if (cmdlist[i].handler_function)
1670                                 {
1671                                         if (pcnt>=cmdlist[i].min_params)
1672                                         {
1673                                                 if (strchr(user->modes,cmdlist[i].flags_needed))
1674                                                 {
1675                                                         cmdlist[i].handler_function(parameters,pcnt,user);
1676                                                 }
1677                                         }
1678                                 }
1679                         }
1680                 }
1681 }
1682
1683 void DoSplitEveryone()
1684 {
1685         bool go_again = true;
1686         while (go_again)
1687         {
1688                 go_again = false;
1689                 for (int i = 0; i < 32; i++)
1690                 {
1691                         if (me[i] != NULL)
1692                         {
1693                                 for (vector<ircd_connector>::iterator j = me[i]->connectors.begin(); j != me[i]->connectors.end(); j++)
1694                                 {
1695                                         if (strcasecmp(j->GetServerName().c_str(),ServerName))
1696                                         {
1697                                                 j->routes.clear();
1698                                                 j->CloseConnection();
1699                                                 me[i]->connectors.erase(j);
1700                                                 go_again = true;
1701                                                 break;
1702                                         }
1703                                 }
1704                         }
1705                 }
1706         }
1707         log(DEBUG,"Removed server. Will remove clients...");
1708         // iterate through the userlist and remove all users on this server.
1709         // because we're dealing with a mesh, we dont have to deal with anything
1710         // "down-route" from this server (nice huh)
1711         go_again = true;
1712         char reason[MAXBUF];
1713         while (go_again)
1714         {
1715                 go_again = false;
1716                 for (user_hash::const_iterator u = clientlist.begin(); u != clientlist.end(); u++)
1717                 {
1718                         if (strcasecmp(u->second->server,ServerName))
1719                         {
1720                                 snprintf(reason,MAXBUF,"%s %s",ServerName,u->second->server);
1721                                 kill_link(u->second,reason);
1722                                 go_again = true;
1723                                 break;
1724                         }
1725                 }
1726         }
1727         has_been_netsplit = true;
1728         log(DEBUG,"Clients removed.");
1729 }
1730
1731
1732
1733 void force_nickchange(userrec* user,const char* newnick)
1734 {
1735         char nick[MAXBUF];
1736         int MOD_RESULT = 0;
1737         
1738         strcpy(nick,"");
1739
1740         FOREACH_RESULT(OnUserPreNick(user,newnick));
1741         if (MOD_RESULT) {
1742                 statsCollisions++;
1743                 kill_link(user,"Nickname collision");
1744                 return;
1745         }
1746         if (matches_qline(newnick))
1747         {
1748                 statsCollisions++;
1749                 kill_link(user,"Nickname collision");
1750                 return;
1751         }
1752         
1753         if (user)
1754         {
1755                 if (newnick)
1756                 {
1757                         strncpy(nick,newnick,MAXBUF);
1758                 }
1759                 if (user->registered == 7)
1760                 {
1761                         char* pars[1];
1762                         pars[0] = nick;
1763                         handle_nick(pars,1,user);
1764                 }
1765         }
1766 }
1767                                 
1768
1769 int process_parameters(char **command_p,char *parameters)
1770 {
1771         int j = 0;
1772         int q = strlen(parameters);
1773         if (!q)
1774         {
1775                 /* no parameters, command_p invalid! */
1776                 return 0;
1777         }
1778         if (parameters[0] == ':')
1779         {
1780                 command_p[0] = parameters+1;
1781                 return 1;
1782         }
1783         if (q)
1784         {
1785                 if ((strchr(parameters,' ')==NULL) || (parameters[0] == ':'))
1786                 {
1787                         /* only one parameter */
1788                         command_p[0] = parameters;
1789                         if (parameters[0] == ':')
1790                         {
1791                                 if (strchr(parameters,' ') != NULL)
1792                                 {
1793                                         command_p[0]++;
1794                                 }
1795                         }
1796                         return 1;
1797                 }
1798         }
1799         command_p[j++] = parameters;
1800         for (int i = 0; i <= q; i++)
1801         {
1802                 if (parameters[i] == ' ')
1803                 {
1804                         command_p[j++] = parameters+i+1;
1805                         parameters[i] = '\0';
1806                         if (command_p[j-1][0] == ':')
1807                         {
1808                                 *command_p[j-1]++; /* remove dodgy ":" */
1809                                 break;
1810                                 /* parameter like this marks end of the sequence */
1811                         }
1812                 }
1813         }
1814         return j; /* returns total number of items in the list */
1815 }
1816
1817 void process_command(userrec *user, char* cmd)
1818 {
1819         char *parameters;
1820         char *command;
1821         char *command_p[127];
1822         char p[MAXBUF], temp[MAXBUF];
1823         int j, items, cmd_found;
1824
1825         for (int i = 0; i < 127; i++)
1826                 command_p[i] = NULL;
1827
1828         if (!user)
1829         {
1830                 return;
1831         }
1832         if (!cmd)
1833         {
1834                 return;
1835         }
1836         if (!cmd[0])
1837         {
1838                 return;
1839         }
1840         
1841         int total_params = 0;
1842         if (strlen(cmd)>2)
1843         {
1844                 for (unsigned int q = 0; q < strlen(cmd)-1; q++)
1845                 {
1846                         if ((cmd[q] == ' ') && (cmd[q+1] == ':'))
1847                         {
1848                                 total_params++;
1849                                 // found a 'trailing', we dont count them after this.
1850                                 break;
1851                         }
1852                         if (cmd[q] == ' ')
1853                                 total_params++;
1854                 }
1855         }
1856
1857         // another phidjit bug...
1858         if (total_params > 126)
1859         {
1860                 *(strchr(cmd,' ')) = '\0';
1861                 WriteServ(user->fd,"421 %s %s :Too many parameters given",user->nick,cmd);
1862                 return;
1863         }
1864
1865         strlcpy(temp,cmd,MAXBUF);
1866         
1867         std::string tmp = cmd;
1868         for (int i = 0; i <= MODCOUNT; i++)
1869         {
1870                 std::string oldtmp = tmp;
1871                 modules[i]->OnServerRaw(tmp,true,user);
1872                 if (oldtmp != tmp)
1873                 {
1874                         log(DEBUG,"A Module changed the input string!");
1875                         log(DEBUG,"New string: %s",tmp.c_str());
1876                         log(DEBUG,"Old string: %s",oldtmp.c_str());
1877                         break;
1878                 }
1879         }
1880         strlcpy(cmd,tmp.c_str(),MAXBUF);
1881         strlcpy(temp,cmd,MAXBUF);
1882
1883         if (!strchr(cmd,' '))
1884         {
1885                 /* no parameters, lets skip the formalities and not chop up
1886                  * the string */
1887                 log(DEBUG,"About to preprocess command with no params");
1888                 items = 0;
1889                 command_p[0] = NULL;
1890                 parameters = NULL;
1891                 for (unsigned int i = 0; i <= strlen(cmd); i++)
1892                 {
1893                         cmd[i] = toupper(cmd[i]);
1894                 }
1895                 command = cmd;
1896         }
1897         else
1898         {
1899                 strcpy(cmd,"");
1900                 j = 0;
1901                 /* strip out extraneous linefeeds through mirc's crappy pasting (thanks Craig) */
1902                 for (unsigned int i = 0; i < strlen(temp); i++)
1903                 {
1904                         if ((temp[i] != 10) && (temp[i] != 13) && (temp[i] != 0) && (temp[i] != 7))
1905                         {
1906                                 cmd[j++] = temp[i];
1907                                 cmd[j] = 0;
1908                         }
1909                 }
1910                 /* split the full string into a command plus parameters */
1911                 parameters = p;
1912                 strcpy(p," ");
1913                 command = cmd;
1914                 if (strchr(cmd,' '))
1915                 {
1916                         for (unsigned int i = 0; i <= strlen(cmd); i++)
1917                         {
1918                                 /* capitalise the command ONLY, leave params intact */
1919                                 cmd[i] = toupper(cmd[i]);
1920                                 /* are we nearly there yet?! :P */
1921                                 if (cmd[i] == ' ')
1922                                 {
1923                                         command = cmd;
1924                                         parameters = cmd+i+1;
1925                                         cmd[i] = '\0';
1926                                         break;
1927                                 }
1928                         }
1929                 }
1930                 else
1931                 {
1932                         for (unsigned int i = 0; i <= strlen(cmd); i++)
1933                         {
1934                                 cmd[i] = toupper(cmd[i]);
1935                         }
1936                 }
1937
1938         }
1939         cmd_found = 0;
1940         
1941         if (strlen(command)>MAXCOMMAND)
1942         {
1943                 WriteServ(user->fd,"421 %s %s :Command too long",user->nick,command);
1944                 return;
1945         }
1946         
1947         for (unsigned int x = 0; x < strlen(command); x++)
1948         {
1949                 if (((command[x] < 'A') || (command[x] > 'Z')) && (command[x] != '.'))
1950                 {
1951                         if (((command[x] < '0') || (command[x]> '9')) && (command[x] != '-'))
1952                         {
1953                                 if (strchr("@!\"$%^&*(){}[]_=+;:'#~,<>/?\\|`",command[x]))
1954                                 {
1955                                         statsUnknown++;
1956                                         WriteServ(user->fd,"421 %s %s :Unknown command",user->nick,command);
1957                                         return;
1958                                 }
1959                         }
1960                 }
1961         }
1962
1963         for (unsigned int i = 0; i != cmdlist.size(); i++)
1964         {
1965                 if (cmdlist[i].command[0])
1966                 {
1967                         if (strlen(command)>=(strlen(cmdlist[i].command))) if (!strncmp(command, cmdlist[i].command,MAXCOMMAND))
1968                         {
1969                                 if (parameters)
1970                                 {
1971                                         if (parameters[0])
1972                                         {
1973                                                 items = process_parameters(command_p,parameters);
1974                                         }
1975                                         else
1976                                         {
1977                                                 items = 0;
1978                                                 command_p[0] = NULL;
1979                                         }
1980                                 }
1981                                 else
1982                                 {
1983                                         items = 0;
1984                                         command_p[0] = NULL;
1985                                 }
1986                                 
1987                                 if (user)
1988                                 {
1989                                         /* activity resets the ping pending timer */
1990                                         user->nping = TIME + user->pingmax;
1991                                         if ((items) < cmdlist[i].min_params)
1992                                         {
1993                                                 log(DEBUG,"process_command: not enough parameters: %s %s",user->nick,command);
1994                                                 WriteServ(user->fd,"461 %s %s :Not enough parameters",user->nick,command);
1995                                                 return;
1996                                         }
1997                                         if ((!strchr(user->modes,cmdlist[i].flags_needed)) && (cmdlist[i].flags_needed))
1998                                         {
1999                                                 log(DEBUG,"process_command: permission denied: %s %s",user->nick,command);
2000                                                 WriteServ(user->fd,"481 %s :Permission Denied- You do not have the required operator privilages",user->nick);
2001                                                 cmd_found = 1;
2002                                                 return;
2003                                         }
2004                                         if ((cmdlist[i].flags_needed) && (!user->HasPermission(command)))
2005                                         {
2006                                                 log(DEBUG,"process_command: permission denied: %s %s",user->nick,command);
2007                                                 WriteServ(user->fd,"481 %s :Permission Denied- Oper type %s does not have access to command %s",user->nick,user->oper,command);
2008                                                 cmd_found = 1;
2009                                                 return;
2010                                         }
2011                                         /* if the command isnt USER, PASS, or NICK, and nick is empty,
2012                                          * deny command! */
2013                                         if ((strncmp(command,"USER",4)) && (strncmp(command,"NICK",4)) && (strncmp(command,"PASS",4)))
2014                                         {
2015                                                 if ((!isnick(user->nick)) || (user->registered != 7))
2016                                                 {
2017                                                         log(DEBUG,"process_command: not registered: %s %s",user->nick,command);
2018                                                         WriteServ(user->fd,"451 %s :You have not registered",command);
2019                                                         return;
2020                                                 }
2021                                         }
2022                                         if ((user->registered == 7) && (!strchr(user->modes,'o')))
2023                                         {
2024                                                 std::stringstream dcmds(DisabledCommands);
2025                                                 while (!dcmds.eof())
2026                                                 {
2027                                                         std::string thiscmd;
2028                                                         dcmds >> thiscmd;
2029                                                         if (!strcasecmp(thiscmd.c_str(),command))
2030                                                         {
2031                                                                 // command is disabled!
2032                                                                 WriteServ(user->fd,"421 %s %s :This command has been disabled.",user->nick,command);
2033                                                                 return;
2034                                                         }
2035                                                 }
2036                                         }
2037                                         if ((user->registered == 7) || (!strncmp(command,"USER",4)) || (!strncmp(command,"NICK",4)) || (!strncmp(command,"PASS",4)))
2038                                         {
2039                                                 if (cmdlist[i].handler_function)
2040                                                 {
2041                                                         
2042                                                         /* ikky /stats counters */
2043                                                         if (temp)
2044                                                         {
2045                                                                 cmdlist[i].use_count++;
2046                                                                 cmdlist[i].total_bytes+=strlen(temp);
2047                                                         }
2048
2049                                                         int MOD_RESULT = 0;
2050                                                         FOREACH_RESULT(OnPreCommand(command,command_p,items,user));
2051                                                         if (MOD_RESULT == 1) {
2052                                                                 return;
2053                                                         }
2054
2055                                                         /* WARNING: nothing may come after the
2056                                                          * command handler call, as the handler
2057                                                          * may free the user structure! */
2058
2059                                                         cmdlist[i].handler_function(command_p,items,user);
2060                                                 }
2061                                                 return;
2062                                         }
2063                                         else
2064                                         {
2065                                                 WriteServ(user->fd,"451 %s :You have not registered",command);
2066                                                 return;
2067                                         }
2068                                 }
2069                                 cmd_found = 1;
2070                         }
2071                 }
2072         }
2073         if ((!cmd_found) && (user))
2074         {
2075                 statsUnknown++;
2076                 WriteServ(user->fd,"421 %s %s :Unknown command",user->nick,command);
2077         }
2078 }
2079
2080 bool removecommands(const char* source)
2081 {
2082         bool go_again = true;
2083         while (go_again)
2084         {
2085                 go_again = false;
2086                 for (std::deque<command_t>::iterator i = cmdlist.begin(); i != cmdlist.end(); i++)
2087                 {
2088                         if (!strcmp(i->source,source))
2089                         {
2090                                 log(DEBUG,"removecommands(%s) Removing dependent command: %s",i->source,i->command);
2091                                 cmdlist.erase(i);
2092                                 go_again = true;
2093                                 break;
2094                         }
2095                 }
2096         }
2097         return true;
2098 }
2099
2100
2101 void process_buffer(const char* cmdbuf,userrec *user)
2102 {
2103         if (!user)
2104         {
2105                 log(DEFAULT,"*** BUG *** process_buffer was given an invalid parameter");
2106                 return;
2107         }
2108         char cmd[MAXBUF];
2109         if (!cmdbuf)
2110         {
2111                 log(DEFAULT,"*** BUG *** process_buffer was given an invalid parameter");
2112                 return;
2113         }
2114         if (!cmdbuf[0])
2115         {
2116                 return;
2117         }
2118         while (*cmdbuf == ' ') cmdbuf++; // strip leading spaces
2119
2120         strlcpy(cmd,cmdbuf,MAXBUF);
2121         if (!cmd[0])
2122         {
2123                 return;
2124         }
2125         int sl = strlen(cmd)-1;
2126         if ((cmd[sl] == 13) || (cmd[sl] == 10))
2127         {
2128                 cmd[sl] = '\0';
2129         }
2130         sl = strlen(cmd)-1;
2131         if ((cmd[sl] == 13) || (cmd[sl] == 10))
2132         {
2133                 cmd[sl] = '\0';
2134         }
2135         sl = strlen(cmd)-1;
2136         while (cmd[sl] == ' ') // strip trailing spaces
2137         {
2138                 cmd[sl] = '\0';
2139                 sl = strlen(cmd)-1;
2140         }
2141
2142         if (!cmd[0])
2143         {
2144                 return;
2145         }
2146         log(DEBUG,"CMDIN: %s %s",user->nick,cmd);
2147         tidystring(cmd);
2148         if ((user) && (cmd))
2149         {
2150                 process_command(user,cmd);
2151         }
2152 }
2153
2154 void DoSync(serverrec* serv, char* tcp_host)
2155 {
2156         char data[MAXBUF];
2157         log(DEBUG,"Sending sync");
2158         // send start of sync marker: Y <timestamp>
2159         // at this point the ircd receiving it starts broadcasting this netburst to all ircds
2160         // except the ones its receiving it from.
2161         snprintf(data,MAXBUF,"%s Y %lu",CreateSum().c_str(),(unsigned long)TIME);
2162         serv->SendPacket(data,tcp_host);
2163         // send users and channels
2164
2165         NetSendMyRoutingTable();
2166
2167         // send all routing table and uline voodoo. The ordering of these commands is IMPORTANT!
2168         for (int j = 0; j < 32; j++)
2169         {
2170                 if (me[j] != NULL)
2171                 {
2172                         for (unsigned int k = 0; k < me[j]->connectors.size(); k++)
2173                         {
2174                                 if (is_uline(me[j]->connectors[k].GetServerName().c_str()))
2175                                 {
2176                                         snprintf(data,MAXBUF,"%s H %s",CreateSum().c_str(),me[j]->connectors[k].GetServerName().c_str());
2177                                         serv->SendPacket(data,tcp_host);
2178                                 }
2179                         }
2180                 }
2181         }
2182
2183         // send our version for the remote side to cache
2184         snprintf(data,MAXBUF,"%s v %s %s",CreateSum().c_str(),ServerName,GetVersionString().c_str());
2185         serv->SendPacket(data,tcp_host);
2186
2187         // sync the users and channels, give the modules a look-in.
2188         for (user_hash::iterator u = clientlist.begin(); u != clientlist.end(); u++)
2189         {
2190                 snprintf(data,MAXBUF,"%s N %lu %s %s %s %s +%s %s %s :%s",CreateSum().c_str(),(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);
2191                 serv->SendPacket(data,tcp_host);
2192                 if (strchr(u->second->modes,'o'))
2193                 {
2194                         snprintf(data,MAXBUF,"%s | %s %s",CreateSum().c_str(),u->second->nick,u->second->oper);
2195                         serv->SendPacket(data,tcp_host);
2196                 }
2197                 for (int i = 0; i <= MODCOUNT; i++)
2198                 {
2199                         string_list l = modules[i]->OnUserSync(u->second);
2200                         for (unsigned int j = 0; j < l.size(); j++)
2201                         {
2202                                 snprintf(data,MAXBUF,"%s %s",CreateSum().c_str(),l[j].c_str());
2203                                 serv->SendPacket(data,tcp_host);
2204                         }
2205                 }
2206                 char* chl = chlist(u->second,u->second);
2207                 if (strcmp(chl,""))
2208                 {
2209                         snprintf(data,MAXBUF,"%s J %s %s",CreateSum().c_str(),u->second->nick,chl);
2210                         serv->SendPacket(data,tcp_host);
2211                 }
2212         }
2213         // send channel modes, topics etc...
2214         for (chan_hash::iterator c = chanlist.begin(); c != chanlist.end(); c++)
2215         {
2216                 snprintf(data,MAXBUF,"M %s +%s",c->second->name,chanmodes(c->second));
2217                 serv->SendPacket(data,tcp_host);
2218                 for (int i = 0; i <= MODCOUNT; i++)
2219                 {
2220                         string_list l = modules[i]->OnChannelSync(c->second);
2221                         for (unsigned int j = 0; j < l.size(); j++)
2222                         {
2223                                 snprintf(data,MAXBUF,"%s %s",CreateSum().c_str(),l[j].c_str());
2224                                 serv->SendPacket(data,tcp_host);
2225                         }
2226                 }
2227                 if (c->second->topic[0])
2228                 {
2229                         snprintf(data,MAXBUF,"%s T %lu %s %s :%s",CreateSum().c_str(),(unsigned long)c->second->topicset,c->second->setby,c->second->name,c->second->topic);
2230                         serv->SendPacket(data,tcp_host);
2231                 }
2232                 // send current banlist
2233                 
2234                 for (BanList::iterator b = c->second->bans.begin(); b != c->second->bans.end(); b++)
2235                 {
2236                         snprintf(data,MAXBUF,"%s M %s +b %s",CreateSum().c_str(),c->second->name,b->data);
2237                         serv->SendPacket(data,tcp_host);
2238                 }
2239         }
2240         // sync global zlines, glines, etc
2241         sync_xlines(serv,tcp_host);
2242
2243         snprintf(data,MAXBUF,"%s F %lu",CreateSum().c_str(),(unsigned long)TIME);
2244         serv->SendPacket(data,tcp_host);
2245         log(DEBUG,"Sent sync");
2246         // ircd sends its serverlist after the end of sync here
2247 }
2248
2249
2250 void NetSendMyRoutingTable()
2251 {
2252         // send out a line saying what is reachable to us.
2253         // E.g. if A is linked to B C and D, send out:
2254         // $ A B C D
2255         // if its only linked to B and D send out:
2256         // $ A B D
2257         // if it has no links, dont even send out the line at all.
2258         char buffer[MAXBUF];
2259         snprintf(buffer,MAXBUF,"$ %s",ServerName);
2260         bool sendit = false;
2261         for (int i = 0; i < 32; i++)
2262         {
2263                 if (me[i] != NULL)
2264                 {
2265                         for (unsigned int j = 0; j < me[i]->connectors.size(); j++)
2266                         {
2267                                 if ((me[i]->connectors[j].GetState() != STATE_DISCONNECTED) || (is_uline(me[i]->connectors[j].GetServerName().c_str())))
2268                                 {
2269                                         strlcat(buffer," ",MAXBUF);
2270                                         strlcat(buffer,me[i]->connectors[j].GetServerName().c_str(),MAXBUF);
2271                                         sendit = true;
2272                                 }
2273                         }
2274                 }
2275         }
2276         if (sendit)
2277                 NetSendToAll(buffer);
2278 }
2279
2280
2281 void DoSplit(const char* params)
2282 {
2283         bool go_again = true;
2284         int x = 0;
2285         while (go_again)
2286         {
2287                 go_again = false;
2288                 for (int i = 0; i < 32; i++)
2289                 {
2290                         if (me[i] != NULL)
2291                         {
2292                                 for (vector<ircd_connector>::iterator j = me[i]->connectors.begin(); j != me[i]->connectors.end(); j++)
2293                                 {
2294                                         if (!strcasecmp(j->GetServerName().c_str(),params))
2295                                         {
2296                                                 log(DEBUG,"Removing %s",j->GetServerName().c_str());
2297                                                 j->routes.clear();
2298                                                 j->CloseConnection();
2299                                                 me[i]->connectors.erase(j);
2300                                                 go_again = true;
2301                                                 x++;
2302                                                 break;
2303                                         }
2304                                 }
2305                         }
2306                 }
2307         }
2308         if (!x)
2309         {
2310                 log(DEBUG,"No clients to remove.");
2311                 return;
2312         }
2313         log(DEBUG,"Removed server. Will remove clients...");
2314         // iterate through the userlist and remove all users on this server.
2315         // because we're dealing with a mesh, we dont have to deal with anything
2316         // "down-route" from this server (nice huh)
2317         go_again = true;
2318         char reason[MAXBUF];
2319         snprintf(reason,MAXBUF,"%s %s",ServerName,params);
2320         while (go_again)
2321         {
2322                 go_again = false;
2323                 for (user_hash::const_iterator u = clientlist.begin(); u != clientlist.end(); u++)
2324                 {
2325                         if (!strcasecmp(u->second->server,params))
2326                         {
2327                                 kill_link(u->second,reason);
2328                                 go_again = true;
2329                                 break;
2330                         }
2331                 }
2332         }
2333         has_been_netsplit = true;
2334         log(DEBUG,"Removed clients (DoSplit)");
2335 }
2336
2337 // removes a server. Will NOT remove its users!
2338
2339 void RemoveServer(const char* name)
2340 {
2341         bool go_again = true;
2342         while (go_again)
2343         {
2344                 go_again = false;
2345                 for (int i = 0; i < 32; i++)
2346                 {
2347                         if (me[i] != NULL)
2348                         {
2349                                 for (vector<ircd_connector>::iterator j = me[i]->connectors.begin(); j != me[i]->connectors.end(); j++)
2350                                 {
2351                                         if (!strcasecmp(j->GetServerName().c_str(),name))
2352                                         {
2353                                                 j->routes.clear();
2354                                                 j->CloseConnection();
2355                                                 me[i]->connectors.erase(j);
2356                                                 go_again = true;
2357                                                 break;
2358                                         }
2359                                 }
2360                         }
2361                 }
2362         }
2363 }
2364
2365
2366 char MODERR[MAXBUF];
2367
2368 char* ModuleError()
2369 {
2370         return MODERR;
2371 }
2372
2373 void erase_factory(int j)
2374 {
2375         int v = 0;
2376         for (std::vector<ircd_module*>::iterator t = factory.begin(); t != factory.end(); t++)
2377         {
2378                 if (v == j)
2379                 {
2380                         factory.erase(t);
2381                         factory.push_back(NULL);
2382                         return;
2383                 }
2384                 v++;
2385         }
2386 }
2387
2388 void erase_module(int j)
2389 {
2390         int v1 = 0;
2391         for (std::vector<Module*>::iterator m = modules.begin(); m!= modules.end(); m++)
2392         {
2393                 if (v1 == j)
2394                 {
2395                         delete *m;
2396                         modules.erase(m);
2397                         modules.push_back(NULL);
2398                         break;
2399                 }
2400                 v1++;
2401         }
2402         int v2 = 0;
2403         for (std::vector<std::string>::iterator v = module_names.begin(); v != module_names.end(); v++)
2404         {
2405                 if (v2 == j)
2406                 {
2407                        module_names.erase(v);
2408                        break;
2409                 }
2410                 v2++;
2411         }
2412
2413 }
2414
2415 bool UnloadModule(const char* filename)
2416 {
2417         std::string filename_str = filename;
2418         for (unsigned int j = 0; j != module_names.size(); j++)
2419         {
2420                 if (module_names[j] == filename_str)
2421                 {
2422                         if (modules[j]->GetVersion().Flags & VF_STATIC)
2423                         {
2424                                 log(DEFAULT,"Failed to unload STATIC module %s",filename);
2425                                 snprintf(MODERR,MAXBUF,"Module not unloadable (marked static)");
2426                                 return false;
2427                         }
2428                         FOREACH_MOD OnUnloadModule(modules[j],module_names[j]);
2429                         // found the module
2430                         log(DEBUG,"Deleting module...");
2431                         erase_module(j);
2432                         log(DEBUG,"Erasing module entry...");
2433                         erase_factory(j);
2434                         log(DEBUG,"Removing dependent commands...");
2435                         removecommands(filename);
2436                         log(DEFAULT,"Module %s unloaded",filename);
2437                         MODCOUNT--;
2438                         return true;
2439                 }
2440         }
2441         log(DEFAULT,"Module %s is not loaded, cannot unload it!",filename);
2442         snprintf(MODERR,MAXBUF,"Module not loaded");
2443         return false;
2444 }
2445
2446 bool LoadModule(const char* filename)
2447 {
2448         char modfile[MAXBUF];
2449 #ifdef STATIC_LINK
2450         snprintf(modfile,MAXBUF,"%s",filename);
2451 #else
2452         snprintf(modfile,MAXBUF,"%s/%s",ModPath,filename);
2453 #endif
2454         std::string filename_str = filename;
2455 #ifndef STATIC_LINK
2456         if (!DirValid(modfile))
2457         {
2458                 log(DEFAULT,"Module %s is not within the modules directory.",modfile);
2459                 snprintf(MODERR,MAXBUF,"Module %s is not within the modules directory.",modfile);
2460                 return false;
2461         }
2462 #endif
2463         log(DEBUG,"Loading module: %s",modfile);
2464 #ifndef STATIC_LINK
2465         if (FileExists(modfile))
2466         {
2467 #endif
2468                 for (unsigned int j = 0; j < module_names.size(); j++)
2469                 {
2470                         if (module_names[j] == filename_str)
2471                         {
2472                                 log(DEFAULT,"Module %s is already loaded, cannot load a module twice!",modfile);
2473                                 snprintf(MODERR,MAXBUF,"Module already loaded");
2474                                 return false;
2475                         }
2476                 }
2477                 ircd_module* a = new ircd_module(modfile);
2478                 factory[MODCOUNT+1] = a;
2479                 if (factory[MODCOUNT+1]->LastError())
2480                 {
2481                         log(DEFAULT,"Unable to load %s: %s",modfile,factory[MODCOUNT+1]->LastError());
2482                         snprintf(MODERR,MAXBUF,"Loader/Linker error: %s",factory[MODCOUNT+1]->LastError());
2483                         MODCOUNT--;
2484                         return false;
2485                 }
2486                 if (factory[MODCOUNT+1]->factory)
2487                 {
2488                         Module* m = factory[MODCOUNT+1]->factory->CreateModule();
2489                         modules[MODCOUNT+1] = m;
2490                         /* save the module and the module's classfactory, if
2491                          * this isnt done, random crashes can occur :/ */
2492                         module_names.push_back(filename);
2493                 }
2494                 else
2495                 {
2496                         log(DEFAULT,"Unable to load %s",modfile);
2497                         snprintf(MODERR,MAXBUF,"Factory function failed!");
2498                         return false;
2499                 }
2500 #ifndef STATIC_LINK
2501         }
2502         else
2503         {
2504                 log(DEFAULT,"InspIRCd: startup: Module Not Found %s",modfile);
2505                 snprintf(MODERR,MAXBUF,"Module file could not be found");
2506                 return false;
2507         }
2508 #endif
2509         MODCOUNT++;
2510         FOREACH_MOD OnLoadModule(modules[MODCOUNT],filename_str);
2511         return true;
2512 }
2513
2514
2515 bool GotServer(std::string name)
2516 {
2517         for (int j = 0; j < 32; j++)
2518         {
2519                 if (me[j] != NULL)
2520                 {
2521                         for (unsigned int k = 0; k < me[j]->connectors.size(); k++)
2522                         {
2523                                 if (name == me[j]->connectors[k].GetServerName())
2524                                 {
2525                                         return true;
2526                                 }
2527                         }
2528                 }
2529         }
2530         return false;
2531 }
2532
2533
2534 int InspIRCd(char** argv, int argc)
2535 {
2536         struct sockaddr_in client,server;
2537         char addrs[MAXBUF][255];
2538         int incomingSockfd, result = TRUE;
2539         socklen_t length;
2540         int count = 0;
2541 #ifdef USE_SELECT
2542         int selectResult = 0, selectResult2 = 0;
2543         fd_set selectFds;
2544 #endif
2545         char configToken[MAXBUF], Addr[MAXBUF], Type[MAXBUF];
2546         timeval tv;
2547
2548         std::string logpath = GetFullProgDir(argv,argc) + "/ircd.log";
2549         log_file = fopen(logpath.c_str(),"a+");
2550         if (!log_file)
2551         {
2552                 printf("ERROR: Could not write to logfile %s, bailing!\n\n",logpath.c_str());
2553                 Exit(ERROR);
2554         }
2555
2556 #ifdef IS_CYGWIN
2557         printf("Logging to ircd.log...\n");
2558 #else
2559         printf("Logging to %s...\n",logpath.c_str());
2560 #endif
2561
2562         log(DEFAULT,"$Id$");
2563         if (geteuid() == 0)
2564         {
2565                 printf("WARNING!!! You are running an irc server as ROOT!!! DO NOT DO THIS!!!\n\n");
2566                 Exit(ERROR);
2567                 log(DEFAULT,"InspIRCd: startup: not starting with UID 0!");
2568         }
2569         SetupCommandTable();
2570         log(DEBUG,"InspIRCd: startup: default command table set up");
2571         
2572         ReadConfig(true,NULL);
2573         if (DieValue[0])
2574         { 
2575                 printf("WARNING: %s\n\n",DieValue);
2576                 log(DEFAULT,"Ut-Oh, somebody didn't read their config file: '%s'",DieValue);
2577                 exit(0); 
2578         }  
2579         log(DEBUG,"InspIRCd: startup: read config");
2580
2581         AddServerName(ServerName);
2582
2583         int clientportcount = 0, serverportcount = 0;
2584
2585         for (count = 0; count < ConfValueEnum("bind",&config_f); count++)
2586         {
2587                 ConfValue("bind","port",count,configToken,&config_f);
2588                 ConfValue("bind","address",count,Addr,&config_f);
2589                 ConfValue("bind","type",count,Type,&config_f);
2590                 if (!strcmp(Type,"servers"))
2591                 {
2592                         char Default[MAXBUF];
2593                         strcpy(Default,"no");
2594                         ConfValue("bind","default",count,Default,&config_f);
2595                         if (strchr(Default,'y'))
2596                         {
2597                                 defaultRoute = serverportcount;
2598                                 log(DEBUG,"InspIRCd: startup: binding '%s:%s' is default server route",Addr,configToken);
2599                         }
2600                         me[serverportcount] = new serverrec(ServerName,100L,false);
2601                         if (!me[serverportcount]->CreateListener(Addr,atoi(configToken)))
2602                         {
2603                                 log(DEFAULT,"Warning: Failed to bind port %lu",(unsigned long)atoi(configToken));
2604                                 printf("Warning: Failed to bind port %lu\n",(unsigned long)atoi(configToken));
2605                         }
2606                         else
2607                         {
2608                                 serverportcount++;
2609                         }
2610                 }
2611                 else
2612                 {
2613                         ports[clientportcount] = atoi(configToken);
2614                         strlcpy(addrs[clientportcount],Addr,256);
2615                         clientportcount++;
2616                 }
2617                 log(DEBUG,"InspIRCd: startup: read binding %s:%s [%s] from config",Addr,configToken, Type);
2618         }
2619         portCount = clientportcount;
2620         SERVERportCount = serverportcount;
2621           
2622         log(DEBUG,"InspIRCd: startup: read %lu total client ports and %lu total server ports",(unsigned long)portCount,(unsigned long)SERVERportCount);
2623         log(DEBUG,"InspIRCd: startup: InspIRCd is now starting!");
2624         
2625         printf("\n");
2626         
2627         /* BugFix By Craig! :p */
2628         MODCOUNT = -1;
2629         for (count = 0; count < ConfValueEnum("module",&config_f); count++)
2630         {
2631                 ConfValue("module","name",count,configToken,&config_f);
2632                 printf("Loading module... \033[1;32m%s\033[0m\n",configToken);
2633                 if (!LoadModule(configToken))
2634                 {
2635                         log(DEFAULT,"Exiting due to a module loader error.");
2636                         printf("\nThere was an error loading a module: %s\n\nYou might want to do './inspircd start' instead of 'bin/inspircd'\n\n",ModuleError());
2637                         Exit(0);
2638                 }
2639         }
2640         log(DEFAULT,"Total loaded modules: %lu",(unsigned long)MODCOUNT+1);
2641         
2642         startup_time = time(NULL);
2643           
2644         char PID[MAXBUF];
2645         ConfValue("pid","file",0,PID,&config_f);
2646         // write once here, to try it out and make sure its ok
2647         WritePID(PID);
2648           
2649         /* setup select call */
2650 #ifdef USE_SELECT
2651         FD_ZERO(&selectFds);
2652 #endif
2653         log(DEBUG,"InspIRCd: startup: zero selects");
2654         log(VERBOSE,"InspIRCd: startup: portCount = %lu", (unsigned long)portCount);
2655         
2656         for (count = 0; count < portCount; count++)
2657         {
2658                 if ((openSockfd[boundPortCount] = OpenTCPSocket()) == ERROR)
2659                 {
2660                         log(DEBUG,"InspIRCd: startup: bad fd %lu",(unsigned long)openSockfd[boundPortCount]);
2661                         return(ERROR);
2662                 }
2663                 if (BindSocket(openSockfd[boundPortCount],client,server,ports[count],addrs[count]) == ERROR)
2664                 {
2665                         log(DEFAULT,"InspIRCd: startup: failed to bind port %lu",(unsigned long)ports[count]);
2666                 }
2667                 else    /* well we at least bound to one socket so we'll continue */
2668                 {
2669                         boundPortCount++;
2670                 }
2671         }
2672         
2673         log(DEBUG,"InspIRCd: startup: total bound ports %lu",(unsigned long)boundPortCount);
2674           
2675         /* if we didn't bind to anything then abort */
2676         if (boundPortCount == 0)
2677         {
2678                 log(DEFAULT,"InspIRCd: startup: no ports bound, bailing!");
2679                 printf("\nERROR: Was not able to bind any of %lu ports! Please check your configuration.\n\n", (unsigned long)portCount);
2680                 return (ERROR);
2681         }
2682         
2683
2684         printf("\nInspIRCd is now running!\n");
2685
2686         if (nofork)
2687         {
2688                 log(VERBOSE,"Not forking as -nofork was specified");
2689         }
2690         else
2691         {
2692                 if (DaemonSeed() == ERROR)
2693                 {
2694                         log(DEFAULT,"InspIRCd: startup: can't daemonise");
2695                         printf("ERROR: could not go into daemon mode. Shutting down.\n");
2696                         Exit(ERROR);
2697                 }
2698         }
2699
2700         engine_init;
2701         engine_server_fill;
2702
2703         WritePID(PID);
2704
2705         length = sizeof (client);
2706         char tcp_msg[MAXBUF],tcp_host[MAXBUF],tcp_sum[MAXBUF];
2707         engine_structs;
2708         timeval tvs;
2709         tvs.tv_usec = 10000L;
2710         tvs.tv_sec = 0;
2711         tv.tv_sec = 0;
2712         tv.tv_usec = 10000L;
2713         char data[65536];
2714         timeval tval;
2715         tval.tv_usec = 10000L;
2716         tval.tv_sec = 0;
2717         int total_in_this_set = 0;
2718         int i = 0, v = 0, j = 0, cycle_iter = 0;
2719         bool expire_run = false;
2720
2721         cycle_iter = i = j = 0;         // stop the compiler whining - these may be marked unused in certain socket engine types
2722           
2723         /* main loop, this never returns */
2724         for (;;)
2725         {
2726 #ifdef _POSIX_PRIORITY_SCHEDULING
2727                 sched_yield();
2728 #endif
2729 #ifdef USE_SELECT
2730                 FD_ZERO(&sfd);
2731 #endif
2732                 // we only read time() once per iteration rather than tons of times!
2733                 OLDTIME = TIME;
2734                 TIME = time(NULL);
2735
2736                 dns_poll();
2737
2738                 // *FIX* Instead of closing sockets in kill_link when they receive the ERROR :blah line, we should queue
2739                 // them in a list, then reap the list every second or so.
2740                 if (((TIME % 5) == 0) && (!expire_run))
2741                 {
2742                         for (int i = 0; i < ConfValueEnum("link",&config_f); i++)
2743                         {
2744                                 char Link_ServerName[MAXBUF],Link_AConn[MAXBUF];
2745                                 ConfValue("link","name",i,Link_ServerName,&config_f);
2746                                 ConfValue("link","autoconnect",i,Link_AConn,&config_f);
2747                                 if ((Link_AConn[0]) && (!GotServer(Link_ServerName)))
2748                                 {
2749                                         autoconnects::iterator a = autoconns.find(std::string(Link_ServerName));
2750                                         if (a != autoconns.end())
2751                                         {
2752                                                 if (TIME > a->second)
2753                                                 {
2754                                                         ConnectServer(Link_ServerName,NULL);
2755                                                         a->second = TIME + atoi(Link_AConn);
2756                                                 }
2757                                         }
2758                                 }
2759                         }
2760
2761                         expire_lines();
2762                         FOREACH_MOD OnBackgroundTimer(TIME);
2763                         expire_run = true;
2764                         continue;
2765                 }
2766                 if ((TIME % 5) == 1)
2767                         expire_run = false;
2768                 
2769                 // fix by brain - this must be below any manipulation of the hashmap by modules
2770                 user_hash::iterator count2 = clientlist.begin();
2771
2772                 engine_server_populate;
2773                                         char remotehost[MAXBUF],resolved[MAXBUF];
2774                                         length = sizeof (client);
2775                                         incomingSockfd = accept (me[x]->fd, (sockaddr *) &client, &length);
2776                                         if (incomingSockfd != -1)
2777                                         {
2778                                                 strlcpy(remotehost,(char *)inet_ntoa(client.sin_addr),MAXBUF);
2779                                                 if(CleanAndResolve(resolved, remotehost) != TRUE)
2780                                                 {
2781                                                         strlcpy(resolved,remotehost,MAXBUF);
2782                                                 }
2783                                                 // add to this connections ircd_connector vector
2784                                                 // *FIX* - we need the LOCAL port not the remote port in &client!
2785                                                 me[x]->AddIncoming(incomingSockfd,resolved,me[x]->port);
2786                                         }
2787                                 }
2788                         }
2789                 }
2790      
2791                 std::deque<std::string> msgs;
2792                 std::deque<std::string> sums;
2793                 for (int x = 0; x < SERVERportCount; x++)
2794                 {
2795                         if (me[x])
2796                                 me[x]->FlushWriteBuffers();
2797                         sums.clear();
2798                         msgs.clear();
2799                         if (me[x])
2800                         has_been_netsplit = false;
2801                         while (me[x]->RecvPacket(msgs, tcp_host, sums)) // returns 0 or more lines (can be multiple lines!)
2802                         {
2803                                 if (has_been_netsplit)
2804                                 {
2805                                         log(DEBUG,"Netsplit detected in recvpacket, aborting");
2806                                         goto label;
2807                                 }
2808                                 for (unsigned int ctr = 0; ctr < msgs.size(); ctr++)
2809                                 {
2810                                         strlcpy(tcp_msg,msgs[ctr].c_str(),MAXBUF);
2811                                         strlcpy(tcp_sum,sums[ctr].c_str(),MAXBUF);
2812                                         log(DEBUG,"Processing: %s",tcp_msg);
2813                                         if (!tcp_msg[0])
2814                                         {
2815                                                 log(DEBUG,"Invalid string from %s [route%lu]",tcp_host,(unsigned long)x);
2816                                                 break;
2817                                         }
2818                                         // during a netburst, send all data to all other linked servers
2819                                         if ((((nb_start>0) && (tcp_msg[0] != 'Y') && (tcp_msg[0] != 'X') && (tcp_msg[0] != 'F'))) || (is_uline(tcp_host)))
2820                                         {
2821                                                 if (is_uline(tcp_host))
2822                                                 {
2823                                                         if ((tcp_msg[0] != 'Y') && (tcp_msg[0] != 'X') && (tcp_msg[0] != 'F'))
2824                                                         {
2825                                                                 NetSendToAllExcept_WithSum(tcp_host,tcp_msg,tcp_sum);
2826                                                         }
2827                                                 }
2828                                                 else
2829                                                         NetSendToAllExcept_WithSum(tcp_host,tcp_msg,tcp_sum);
2830                                         }
2831                                         std::string msg = tcp_msg;
2832                                         FOREACH_MOD OnPacketReceive(msg,tcp_host);
2833                                         strlcpy(tcp_msg,msg.c_str(),MAXBUF);
2834                                         if (me[x])
2835                                                 handle_link_packet(tcp_msg, tcp_host, me[x], tcp_sum);
2836                                         if (!me[x]->FindHost(tcp_host))
2837                                         {
2838                                                 log(DEBUG,"Connector gone, bailing!");
2839                                                 goto label;
2840                                         }
2841                                 }
2842                                 goto label;
2843                         }
2844                 }
2845         
2846         while (count2 != clientlist.end())
2847         {
2848 #ifdef USE_SELECT
2849                 FD_ZERO(&sfd);
2850 #endif
2851
2852                 total_in_this_set = 0;
2853
2854                 user_hash::iterator xcount = count2;
2855                 user_hash::iterator endingiter = count2;
2856
2857                 if (count2 == clientlist.end()) break;
2858
2859                 userrec* curr = NULL;
2860
2861                 if (count2->second)
2862                         curr = count2->second;
2863
2864                 if ((long)curr == -1)
2865                         goto label;
2866
2867                 if ((curr) && (curr->fd != 0))
2868                 {
2869 #ifdef _POSIX_PRIORITY_SCHEDULING
2870         sched_yield();
2871 #endif
2872                         // assemble up to 64 sockets into an fd_set
2873                         // to implement a pooling mechanism.
2874                         //
2875                         // This should be up to 64x faster than the
2876                         // old implementation.
2877 #ifdef USE_SELECT
2878                         while (total_in_this_set < 1024)
2879                         {
2880                                 if (count2 != clientlist.end())
2881                                 {
2882                                         curr = count2->second;
2883                                         if ((long)curr == -1)
2884                                                 goto label;
2885                                         int currfd = curr->fd;
2886                                         // we don't check the state of remote users.
2887                                         if ((currfd != -1) && (currfd != FD_MAGIC_NUMBER))
2888                                         {
2889                                                 curr->FlushWriteBuf();
2890                                                 if (curr->GetWriteError() != "")
2891                                                 {
2892                                                         log(DEBUG,"InspIRCd: write error: %s",curr->GetWriteError().c_str());
2893                                                         kill_link(curr,curr->GetWriteError().c_str());
2894                                                         goto label;
2895                                                 }
2896
2897                                                 FD_SET (curr->fd, &sfd);
2898
2899                                                 // registration timeout -- didnt send USER/NICK/HOST in the time specified in
2900                                                 // their connection class.
2901                                                 if (((unsigned)TIME > (unsigned)curr->timeout) && (curr->registered != 7)) 
2902                                                 {
2903                                                         log(DEBUG,"InspIRCd: registration timeout: %s",curr->nick);
2904                                                         kill_link(curr,"Registration timeout");
2905                                                         goto label;
2906                                                 }
2907                                                 if ((TIME > curr->signon) && (curr->registered == 3) && (AllModulesReportReady(curr)))
2908                                                 {
2909                                                         log(DEBUG,"signon exceed, registered=3, and modules ready, OK");
2910                                                         curr->dns_done = true;
2911                                                         statsDnsBad++;
2912                                                         FullConnectUser(curr);
2913                                                         if (fd_ref_table[currfd] != curr) // something changed, bail pronto
2914                                                                 goto label;                                                        
2915                                                 }
2916                                                 if ((curr->dns_done) && (curr->registered == 3) && (AllModulesReportReady(curr))) // both NICK and USER... and DNS
2917                                                 {
2918                                                         log(DEBUG,"dns done, registered=3, and modules ready, OK");
2919                                                         FullConnectUser(curr);
2920                                                         if (fd_ref_table[currfd] != curr) // something changed, bail pronto
2921                                                                 goto label;
2922                                                 }
2923                                                 if ((TIME > curr->nping) && (isnick(curr->nick)) && (curr->registered == 7))
2924                                                 {
2925                                                         if ((!curr->lastping) && (curr->registered == 7))
2926                                                         {
2927                                                                 log(DEBUG,"InspIRCd: ping timeout: %s",curr->nick);
2928                                                                 kill_link(curr,"Ping timeout");
2929                                                                 goto label;
2930                                                         }
2931                                                         Write(curr->fd,"PING :%s",ServerName);
2932                                                         log(DEBUG,"InspIRCd: pinging: %s",curr->nick);
2933                                                         curr->lastping = 0;
2934                                                         curr->nping = TIME+curr->pingmax;       // was hard coded to 120
2935                                                 }
2936                                         }
2937                                         count2++;
2938                                         total_in_this_set++;
2939                                 }
2940                                 else break;
2941                         }
2942                         endingiter = count2;
2943                         count2 = xcount; // roll back to where we were
2944 #else
2945                         // KQUEUE and EPOLL: We don't go through a loop to fill the fd_set so instead we must manually do this loop every now and again.
2946                         // TODO: We dont need to do all this EVERY loop iteration, tone down the visits to this if we're using kqueue.
2947                         cycle_iter++;
2948                         if (cycle_iter > 20) while (count2 != clientlist.end())
2949                         {
2950                                 cycle_iter = 0;
2951                                 if (count2 != clientlist.end())
2952                                 {
2953                                         curr = count2->second;
2954                                         if ((long)curr == -1)
2955                                                 goto label;
2956                                         int currfd = curr->fd;
2957                                         // we don't check the state of remote users.
2958                                         if ((currfd != -1) && (currfd != FD_MAGIC_NUMBER))
2959                                         {
2960
2961                                                 curr->FlushWriteBuf();
2962                                                 if (curr->GetWriteError() != "")
2963                                                 {
2964                                                         log(DEBUG,"InspIRCd: write error: %s",curr->GetWriteError().c_str());
2965                                                         kill_link(curr,curr->GetWriteError().c_str());
2966                                                         goto label;
2967                                                 }
2968
2969                                                 // registration timeout -- didnt send USER/NICK/HOST in the time specified in
2970                                                 // their connection class.
2971                                                 if (((unsigned)TIME > (unsigned)curr->timeout) && (curr->registered != 7))
2972                                                 {
2973                                                         log(DEBUG,"InspIRCd: registration timeout: %s",curr->nick);
2974                                                         kill_link(curr,"Registration timeout");
2975                                                         goto label;
2976
2977                                                 }
2978                                                 if ((TIME > curr->signon) && (curr->registered == 3) && (AllModulesReportReady(curr)))
2979                                                 {
2980                                                         log(DEBUG,"signon exceed, registered=3, and modules ready, OK: %d %d",TIME,curr->signon);
2981                                                         curr->dns_done = true;
2982                                                         statsDnsBad++;
2983                                                         FullConnectUser(curr);
2984                                                         if (fd_ref_table[currfd] != curr) // something changed, bail pronto
2985                                                                 goto label;
2986                                                 }
2987                                                 if ((curr->dns_done) && (curr->registered == 3) && (AllModulesReportReady(curr)))
2988                                                 {
2989                                                         log(DEBUG,"dns done, registered=3, and modules ready, OK");
2990                                                         FullConnectUser(curr);
2991                                                         if (fd_ref_table[currfd] != curr) // something changed, bail pronto
2992                                                                 goto label;
2993                                                 }
2994                                                 if ((TIME > curr->nping) && (isnick(curr->nick)) && (curr->registered == 7))
2995                                                 {
2996                                                         if ((!curr->lastping) && (curr->registered == 7))
2997                                                         {
2998                                                                 log(DEBUG,"InspIRCd: ping timeout: %s",curr->nick);
2999                                                                 kill_link(curr,"Ping timeout");
3000                                                                 goto label;
3001                                                         }
3002                                                         Write(curr->fd,"PING :%s",ServerName);
3003                                                         log(DEBUG,"InspIRCd: pinging: %s",curr->nick);
3004                                                         curr->lastping = 0;
3005                                                         curr->nping = TIME+curr->pingmax;       // was hard coded to 120
3006                                                 }
3007                                         }
3008                                 }
3009                                 else break;
3010                                 count2++;
3011                         }
3012                         // increment the counter right to the end of the list, as kqueue processes everything in one go
3013 #endif
3014         
3015                         v = 0;
3016                         engine_fill;
3017
3018 #ifdef _POSIX_PRIORITY_SCHEDULING
3019                                 sched_yield();
3020 #endif
3021                                 result = EAGAIN;
3022                                 if (engine_check)
3023                                 {
3024                                         log(DEBUG,"Data waiting on socket %d",cu->fd);
3025                                         int MOD_RESULT = 0;
3026                                         int result2 = 0;
3027                                         FOREACH_RESULT(OnRawSocketRead(cu->fd,data,65535,result2));
3028                                         if (!MOD_RESULT)
3029                                         {
3030                                                 result = cu->ReadData(data, 65535);
3031                                         }
3032                                         else
3033                                         {
3034                                                 log(DEBUG,"Data result returned by module: %d",MOD_RESULT);
3035                                                 result = result2;
3036                                         }
3037                                         log(DEBUG,"Read result: %d",result);
3038                                         if (result)
3039                                         {
3040                                                 statsRecv += result;
3041                                                 // perform a check on the raw buffer as an array (not a string!) to remove
3042                                                 // characters 0 and 7 which are illegal in the RFC - replace them with spaces.
3043                                                 // hopefully this should stop even more people whining about "Unknown command: *"
3044                                                 for (int checker = 0; checker < result; checker++)
3045                                                 {
3046                                                         if ((data[checker] == 0) || (data[checker] == 7))
3047                                                                 data[checker] = ' ';
3048                                                 }
3049                                                 if (result > 0)
3050                                                         data[result] = '\0';
3051                                                 userrec* current = cu;
3052                                                 int currfd = current->fd;
3053                                                 int floodlines = 0;
3054                                                 // add the data to the users buffer
3055                                                 if (result > 0)
3056                                                 if (!current->AddBuffer(data))
3057                                                 {
3058                                                         // AddBuffer returned false, theres too much data in the user's buffer and theyre up to no good.
3059                                                         if (current->registered == 7)
3060                                                         {
3061                                                                 kill_link(current,"RecvQ exceeded");
3062                                                         }
3063                                                         else
3064                                                         {
3065                                                                 WriteOpers("*** Excess flood from %s",current->ip);
3066                                                                 log(DEFAULT,"Excess flood from: %s",current->ip);
3067                                                                 add_zline(120,ServerName,"Flood from unregistered connection",current->ip);
3068                                                                 apply_lines();
3069                                                         }
3070                                                         goto label;
3071                                                 }
3072                                                 if (current->recvq.length() > (unsigned)NetBufferSize)
3073                                                 {
3074                                                         if (current->registered == 7)
3075                                                         {
3076                                                                 kill_link(current,"RecvQ exceeded");
3077                                                         }
3078                                                         else
3079                                                         {
3080                                                                 WriteOpers("*** Excess flood from %s",current->ip);
3081                                                                 log(DEFAULT,"Excess flood from: %s",current->ip);
3082                                                                 add_zline(120,ServerName,"Flood from unregistered connection",current->ip);
3083                                                                 apply_lines();
3084                                                         }
3085                                                         goto label;
3086                                                 }
3087                                                 // while there are complete lines to process...
3088                                                 while (current->BufferIsReady())
3089                                                 {
3090                                                         floodlines++;
3091                                                         if (TIME > current->reset_due)
3092                                                         {
3093                                                                 current->reset_due = TIME + current->threshold;
3094                                                                 current->lines_in = 0;
3095                                                         }
3096                                                         current->lines_in++;
3097                                                         if (current->lines_in > current->flood)
3098                                                         {
3099                                                                 log(DEFAULT,"Excess flood from: %s!%s@%s",current->nick,current->ident,current->host);
3100                                                                 WriteOpers("*** Excess flood from: %s!%s@%s",current->nick,current->ident,current->host);
3101                                                                 kill_link(current,"Excess flood");
3102                                                                 goto label;
3103                                                         }
3104                                                         if ((floodlines > current->flood) && (current->flood != 0))
3105                                                         {
3106                                                                 if (current->registered == 7)
3107                                                                 {
3108                                                                         log(DEFAULT,"Excess flood from: %s!%s@%s",current->nick,current->ident,current->host);
3109                                                                         WriteOpers("*** Excess flood from: %s!%s@%s",current->nick,current->ident,current->host);
3110                                                                         kill_link(current,"Excess flood");
3111                                                                 }
3112                                                                 else
3113                                                                 {
3114                                                                         add_zline(120,ServerName,"Flood from unregistered connection",current->ip);
3115                                                                         apply_lines();
3116                                                                 }
3117                                                                 goto label;
3118                                                         }
3119                                                         char sanitized[MAXBUF];
3120                                                         // use GetBuffer to copy single lines into the sanitized string
3121                                                         std::string single_line = current->GetBuffer();
3122                                                         current->bytes_in += single_line.length();
3123                                                         current->cmds_in++;
3124                                                         if (single_line.length()>512)
3125                                                         {
3126                                                                 log(DEFAULT,"Excess flood from: %s!%s@%s",current->nick,current->ident,current->host);
3127                                                                 WriteOpers("*** Excess flood from: %s!%s@%s",current->nick,current->ident,current->host);
3128                                                                 kill_link(current,"Excess flood");
3129                                                                 goto label;
3130                                                         }
3131                                                         strlcpy(sanitized,single_line.c_str(),MAXBUF);
3132                                                         if (*sanitized)
3133                                                         {
3134                                                                 userrec* old_comp = fd_ref_table[currfd];
3135                                                                 // we're gonna re-scan to check if the nick is gone, after every
3136                                                                 // command - if it has, we're gonna bail
3137                                                                 process_buffer(sanitized,current);
3138                                                                 // look for the user's record in case it's changed... if theyve quit,
3139                                                                 // we cant do anything more with their buffer, so bail.
3140                                                                 // there used to be an ugly, slow loop here. Now we have a reference
3141                                                                 // table, life is much easier (and FASTER)
3142                                                                 userrec* new_comp = fd_ref_table[currfd];
3143                                                                 if ((currfd < 0) || (!fd_ref_table[currfd]) || (old_comp != new_comp))
3144                                                                         goto label;
3145
3146                                                         }
3147                                                 }
3148                                                 goto label;
3149                                         }
3150
3151                                         if ((result == -1) && (errno != EAGAIN) && (errno != EINTR))
3152                                         {
3153                                                 log(DEBUG,"killing: %s",cu->nick);
3154                                                 kill_link(cu,strerror(errno));
3155                                                 goto label;
3156                                         }
3157                                 }
3158                                 // result EAGAIN means nothing read
3159                                 if (result == EAGAIN)
3160                                 {
3161                                 }
3162                                 else
3163                                 if (result == 0)
3164                                 {
3165                                         engine_cleanup;
3166                                 }
3167                                 else if (result > 0)
3168                                 {
3169                                 }
3170                         }
3171                 }
3172                 for (int q = 0; q < total_in_this_set; q++)
3173                 {
3174                         count2++;
3175                 }
3176         }
3177
3178 #ifdef _POSIX_PRIORITY_SCHEDULING
3179         sched_yield();
3180 #endif
3181         
3182
3183                 engine_scanset;
3184                                 char target[MAXBUF], resolved[MAXBUF];
3185                                 length = sizeof (client);
3186                                 incomingSockfd = accept (openSockfd[count], (struct sockaddr *) &client, &length);
3187                               
3188                                 strlcpy (target, (char *) inet_ntoa (client.sin_addr), MAXBUF);
3189                                 strlcpy (resolved, target, MAXBUF);
3190                         
3191                                 if (incomingSockfd < 0)
3192                                 {
3193                                         WriteOpers("*** WARNING: Accept failed on port %lu (%s)",(unsigned long)ports[count],target);
3194                                         log(DEBUG,"InspIRCd: accept failed: %lu",(unsigned long)ports[count]);
3195                                         statsRefused++;
3196                                 }
3197                                 else
3198                                 {
3199                                         FOREACH_MOD OnRawSocketAccept(incomingSockfd, resolved, ports[count]);
3200                                         statsAccept++;
3201                                         AddClient(incomingSockfd, resolved, ports[count], false, inet_ntoa (client.sin_addr));
3202                                         log(DEBUG,"InspIRCd: adding client on port %lu fd=%lu",(unsigned long)ports[count],(unsigned long)incomingSockfd);
3203                                 }
3204                         }
3205                 }
3206         }
3207         label:
3208         if (0) {};
3209 #ifdef _POSIX_PRIORITY_SCHEDULING
3210         sched_yield();
3211         sched_yield();
3212 #endif
3213 }
3214 /* not reached */
3215 close (incomingSockfd);
3216 return 0;
3217 }
3218