]> git.netwichtig.de Git - user/henk/code/inspircd.git/blob - src/inspircd.cpp
Moving module_sockets into main class
[user/henk/code/inspircd.git] / src / inspircd.cpp
1 /*       +------------------------------------+
2  *       | Inspire Internet Relay Chat Daemon |
3  *       +------------------------------------+
4  *
5  *  Inspire is copyright (C) 2002-2005 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 #include <time.h>
31 #include <string>
32 #ifdef GCC3
33 #include <ext/hash_map>
34 #else
35 #include <hash_map>
36 #endif
37 #include <map>
38 #include <sstream>
39 #include <vector>
40 #include <deque>
41 #include <sched.h>
42 #ifdef THREADED_DNS
43 #include <pthread.h>
44 #endif
45 #include "users.h"
46 #include "ctables.h"
47 #include "globals.h"
48 #include "modules.h"
49 #include "dynamic.h"
50 #include "wildcard.h"
51 #include "message.h"
52 #include "mode.h"
53 #include "commands.h"
54 #include "xline.h"
55 #include "inspstring.h"
56 #include "dnsqueue.h"
57 #include "helperfuncs.h"
58 #include "hashcomp.h"
59 #include "socketengine.h"
60 #include "userprocess.h"
61 #include "socket.h"
62 #include "dns.h"
63 #include "typedefs.h"
64
65 InspIRCd* ServerInstance;
66
67 int WHOWAS_STALE = 48; // default WHOWAS Entries last 2 days before they go 'stale'
68 int WHOWAS_MAX = 100;  // default 100 people maximum in the WHOWAS list
69
70 extern std::vector<Module*> modules;
71 extern std::vector<ircd_module*> factory;
72
73 extern int MODCOUNT;
74 int openSockfd[MAXSOCKS];
75 sockaddr_in client,server;
76 socklen_t length;
77
78 extern InspSocket* socket_ref[65535];
79
80 time_t TIME = time(NULL), OLDTIME = time(NULL);
81
82 SocketEngine* SE = NULL;
83
84 // This table references users by file descriptor.
85 // its an array to make it VERY fast, as all lookups are referenced
86 // by an integer, meaning there is no need for a scan/search operation.
87 userrec* fd_ref_table[65536];
88
89 serverstats* stats = new serverstats;
90 Server* MyServer = new Server;
91 ServerConfig *Config = new ServerConfig;
92
93 user_hash clientlist;
94 chan_hash chanlist;
95 whowas_hash whowas;
96 command_table cmdlist;
97 servernamelist servernames;
98 int BoundPortCount = 0;
99 std::vector<userrec*> all_opers;
100 char lowermap[255];
101
102
103 void AddOper(userrec* user)
104 {
105         log(DEBUG,"Oper added to optimization list");
106         all_opers.push_back(user);
107 }
108
109 void AddServerName(std::string servername)
110 {
111         log(DEBUG,"Adding server name: %s",servername.c_str());
112         for (servernamelist::iterator a = servernames.begin(); a < servernames.end(); a++)
113         {
114                 if (*a == servername)
115                         return;
116         }
117         servernames.push_back(servername);
118 }
119
120 const char* FindServerNamePtr(std::string servername)
121 {
122         for (servernamelist::iterator a = servernames.begin(); a < servernames.end(); a++)
123         {
124                 if (*a == servername)
125                         return a->c_str();
126         }
127         AddServerName(servername);
128         return FindServerNamePtr(servername);
129 }
130
131 void DeleteOper(userrec* user)
132 {
133         for (std::vector<userrec*>::iterator a = all_opers.begin(); a < all_opers.end(); a++)
134         {
135                 if (*a == user)
136                 {
137                         log(DEBUG,"Oper removed from optimization list");
138                         all_opers.erase(a);
139                         return;
140                 }
141         }
142 }
143
144 std::string GetRevision()
145 {
146         /* w00t got me to replace a bunch of strtok_r
147          * with something nicer, so i did this. Its the
148          * same thing really, only in C++. It places the
149          * text into a std::stringstream which is a readable
150          * and writeable buffer stream, and then pops two
151          * words off it, space delimited. Because it reads
152          * into the same variable twice, the first word
153          * is discarded, and the second one returned.
154          */
155         std::stringstream Revision("$Revision$");
156         std::string single;
157         Revision >> single >> single;
158         return single;
159 }
160
161
162 /* This function pokes and hacks at a parameter list like the following:
163  *
164  * PART #winbot,#darkgalaxy :m00!
165  *
166  * to turn it into a series of individual calls like this:
167  *
168  * PART #winbot :m00!
169  * PART #darkgalaxy :m00!
170  *
171  * The seperate calls are sent to a callback function provided by the caller
172  * (the caller will usually call itself recursively). The callback function
173  * must be a command handler. Calling this function on a line with no list causes
174  * no action to be taken. You must provide a starting and ending parameter number
175  * where the range of the list can be found, useful if you have a terminating
176  * parameter as above which is actually not part of the list, or parameters
177  * before the actual list as well. This code is used by many functions which
178  * can function as "one to list" (see the RFC) */
179
180 int loop_call(handlerfunc fn, char **parameters, int pcnt, userrec *u, int start, int end, int joins)
181 {
182         char plist[MAXBUF];
183         char *param;
184         char *pars[32];
185         char blog[32][MAXBUF];
186         char blog2[32][MAXBUF];
187         int j = 0, q = 0, total = 0, t = 0, t2 = 0, total2 = 0;
188         char keystr[MAXBUF];
189         char moo[MAXBUF];
190
191         for (int i = 0; i <32; i++)
192                 strcpy(blog[i],"");
193
194         for (int i = 0; i <32; i++)
195                 strcpy(blog2[i],"");
196
197         strcpy(moo,"");
198         for (int i = 0; i <10; i++)
199         {
200                 if (!parameters[i])
201                 {
202                         parameters[i] = moo;
203                 }
204         }
205         if (joins)
206         {
207                 if (pcnt > 1) /* we have a key to copy */
208                 {
209                         strlcpy(keystr,parameters[1],MAXBUF);
210                 }
211         }
212
213         if (!parameters[start])
214         {
215                 return 0;
216         }
217         if (!strchr(parameters[start],','))
218         {
219                 return 0;
220         }
221         strcpy(plist,"");
222         for (int i = start; i <= end; i++)
223         {
224                 if (parameters[i])
225                 {
226                         strlcat(plist,parameters[i],MAXBUF);
227                 }
228         }
229         
230         j = 0;
231         param = plist;
232
233         t = strlen(plist);
234         for (int i = 0; i < t; i++)
235         {
236                 if (plist[i] == ',')
237                 {
238                         plist[i] = '\0';
239                         strlcpy(blog[j++],param,MAXBUF);
240                         param = plist+i+1;
241                         if (j>20)
242                         {
243                                 WriteServ(u->fd,"407 %s %s :Too many targets in list, message not delivered.",u->nick,blog[j-1]);
244                                 return 1;
245                         }
246                 }
247         }
248         strlcpy(blog[j++],param,MAXBUF);
249         total = j;
250
251         if ((joins) && (keystr) && (total>0)) // more than one channel and is joining
252         {
253                 strcat(keystr,",");
254         }
255         
256         if ((joins) && (keystr))
257         {
258                 if (strchr(keystr,','))
259                 {
260                         j = 0;
261                         param = keystr;
262                         t2 = strlen(keystr);
263                         for (int i = 0; i < t2; i++)
264                         {
265                                 if (keystr[i] == ',')
266                                 {
267                                         keystr[i] = '\0';
268                                         strlcpy(blog2[j++],param,MAXBUF);
269                                         param = keystr+i+1;
270                                 }
271                         }
272                         strlcpy(blog2[j++],param,MAXBUF);
273                         total2 = j;
274                 }
275         }
276
277         for (j = 0; j < total; j++)
278         {
279                 if (blog[j])
280                 {
281                         pars[0] = blog[j];
282                 }
283                 for (q = end; q < pcnt-1; q++)
284                 {
285                         if (parameters[q+1])
286                         {
287                                 pars[q-end+1] = parameters[q+1];
288                         }
289                 }
290                 if ((joins) && (parameters[1]))
291                 {
292                         if (pcnt > 1)
293                         {
294                                 pars[1] = blog2[j];
295                         }
296                         else
297                         {
298                                 pars[1] = NULL;
299                         }
300                 }
301                 /* repeatedly call the function with the hacked parameter list */
302                 if ((joins) && (pcnt > 1))
303                 {
304                         if (pars[1])
305                         {
306                                 // pars[1] already set up and containing key from blog2[j]
307                                 fn(pars,2,u);
308                         }
309                         else
310                         {
311                                 pars[1] = parameters[1];
312                                 fn(pars,2,u);
313                         }
314                 }
315                 else
316                 {
317                         fn(pars,pcnt-(end-start),u);
318                 }
319         }
320
321         return 1;
322 }
323
324
325
326 void kill_link(userrec *user,const char* r)
327 {
328         user_hash::iterator iter = clientlist.find(user->nick);
329         
330         char reason[MAXBUF];
331         
332         strncpy(reason,r,MAXBUF);
333
334         if (strlen(reason)>MAXQUIT)
335         {
336                 reason[MAXQUIT-1] = '\0';
337         }
338
339         log(DEBUG,"kill_link: %s '%s'",user->nick,reason);
340         Write(user->fd,"ERROR :Closing link (%s@%s) [%s]",user->ident,user->host,reason);
341         log(DEBUG,"closing fd %lu",(unsigned long)user->fd);
342
343         if (user->registered == 7) {
344                 FOREACH_MOD OnUserQuit(user,reason);
345                 WriteCommonExcept(user,"QUIT :%s",reason);
346         }
347
348         user->FlushWriteBuf();
349
350         FOREACH_MOD OnUserDisconnect(user);
351
352         if (user->fd > -1)
353         {
354                 FOREACH_MOD OnRawSocketClose(user->fd);
355                 SE->DelFd(user->fd);
356                 user->CloseSocket();
357         }
358
359         // this must come before the WriteOpers so that it doesnt try to fill their buffer with anything
360         // if they were an oper with +s.
361         if (user->registered == 7) {
362                 purge_empty_chans(user);
363                 // fix by brain: only show local quits because we only show local connects (it just makes SENSE)
364                 if (user->fd > -1)
365                         WriteOpers("*** Client exiting: %s!%s@%s [%s]",user->nick,user->ident,user->host,reason);
366                 AddWhoWas(user);
367         }
368
369         if (iter != clientlist.end())
370         {
371                 log(DEBUG,"deleting user hash value %lu",(unsigned long)user);
372                 if (user->fd > -1)
373                         fd_ref_table[user->fd] = NULL;
374                 clientlist.erase(iter);
375         }
376         delete user;
377 }
378
379 void kill_link_silent(userrec *user,const char* r)
380 {
381         user_hash::iterator iter = clientlist.find(user->nick);
382         
383         char reason[MAXBUF];
384         
385         strncpy(reason,r,MAXBUF);
386
387         if (strlen(reason)>MAXQUIT)
388         {
389                 reason[MAXQUIT-1] = '\0';
390         }
391
392         log(DEBUG,"kill_link: %s '%s'",user->nick,reason);
393         Write(user->fd,"ERROR :Closing link (%s@%s) [%s]",user->ident,user->host,reason);
394         log(DEBUG,"closing fd %lu",(unsigned long)user->fd);
395
396         user->FlushWriteBuf();
397
398         if (user->registered == 7) {
399                 FOREACH_MOD OnUserQuit(user,reason);
400                 WriteCommonExcept(user,"QUIT :%s",reason);
401         }
402
403         FOREACH_MOD OnUserDisconnect(user);
404
405         if (user->fd > -1)
406         {
407                 FOREACH_MOD OnRawSocketClose(user->fd);
408                 SE->DelFd(user->fd);
409                 user->CloseSocket();
410         }
411
412         if (user->registered == 7) {
413                 purge_empty_chans(user);
414         }
415         
416         if (iter != clientlist.end())
417         {
418                 log(DEBUG,"deleting user hash value %lu",(unsigned long)user);
419                 if (user->fd > -1)
420                         fd_ref_table[user->fd] = NULL;
421                 clientlist.erase(iter);
422         }
423         delete user;
424 }
425
426
427 InspIRCd::InspIRCd(int argc, char** argv)
428 {
429         Start();
430         this->startup_time = time(NULL);
431         srand(time(NULL));
432         log(DEBUG,"*** InspIRCd starting up!");
433         if (!FileExists(CONFIG_FILE))
434         {
435                 printf("ERROR: Cannot open config file: %s\nExiting...\n",CONFIG_FILE);
436                 log(DEFAULT,"main: no config");
437                 printf("ERROR: Your config file is missing, this IRCd will self destruct in 10 seconds!\n");
438                 Exit(ERROR);
439         }
440         if (argc > 1) {
441                 for (int i = 1; i < argc; i++)
442                 {
443                         if (!strcmp(argv[i],"-nofork")) {
444                                 Config->nofork = true;
445                         }
446                         if (!strcmp(argv[i],"-wait")) {
447                                 sleep(6);
448                         }
449                         if (!strcmp(argv[i],"-nolimit")) {
450                                 Config->unlimitcore = true;
451                         }
452                 }
453         }
454
455         strlcpy(Config->MyExecutable,argv[0],MAXBUF);
456         
457         // initialize the lowercase mapping table
458         for (unsigned int cn = 0; cn < 256; cn++)
459                 lowermap[cn] = cn;
460         // lowercase the uppercase chars
461         for (unsigned int cn = 65; cn < 91; cn++)
462                 lowermap[cn] = tolower(cn);
463         // now replace the specific chars for scandanavian comparison
464         lowermap[(unsigned)'['] = '{';
465         lowermap[(unsigned)']'] = '}';
466         lowermap[(unsigned)'\\'] = '|';
467
468
469         OpenLog(argv, argc);
470         Config->ClearStack();
471         Config->Read(true,NULL);
472         CheckRoot();
473         SetupCommandTable();
474         AddServerName(Config->ServerName);
475         CheckDie();
476         BoundPortCount = BindPorts();
477
478         printf("\n");
479         if (!Config->nofork)
480         {
481                 if (DaemonSeed() == ERROR)
482                 {
483                         printf("ERROR: could not go into daemon mode. Shutting down.\n");
484                         Exit(ERROR);
485                 }
486         }
487
488         /* Because of limitations in kqueue on freebsd, we must fork BEFORE we
489          * initialize the socket engine.
490          */
491         SE = new SocketEngine();
492
493         /* We must load the modules AFTER initializing the socket engine, now */
494         LoadAllModules();
495
496         printf("\nInspIRCd is now running!\n");
497
498         return;
499 }
500
501 template<typename T> inline string ConvToStr(const T &in)
502 {
503         stringstream tmp;
504         if (!(tmp << in)) return string();
505         return tmp.str();
506 }
507
508 /* re-allocates a nick in the user_hash after they change nicknames,
509  * returns a pointer to the new user as it may have moved */
510
511 userrec* ReHashNick(char* Old, char* New)
512 {
513         //user_hash::iterator newnick;
514         user_hash::iterator oldnick = clientlist.find(Old);
515
516         log(DEBUG,"ReHashNick: %s %s",Old,New);
517         
518         if (!strcasecmp(Old,New))
519         {
520                 log(DEBUG,"old nick is new nick, skipping");
521                 return oldnick->second;
522         }
523         
524         if (oldnick == clientlist.end()) return NULL; /* doesnt exist */
525
526         log(DEBUG,"ReHashNick: Found hashed nick %s",Old);
527
528         userrec* olduser = oldnick->second;
529         clientlist[New] = olduser;
530         clientlist.erase(oldnick);
531
532         log(DEBUG,"ReHashNick: Nick rehashed as %s",New);
533         
534         return clientlist[New];
535 }
536
537 /* adds or updates an entry in the whowas list */
538 void AddWhoWas(userrec* u)
539 {
540         whowas_hash::iterator iter = whowas.find(u->nick);
541         WhoWasUser *a = new WhoWasUser();
542         strlcpy(a->nick,u->nick,NICKMAX);
543         strlcpy(a->ident,u->ident,IDENTMAX);
544         strlcpy(a->dhost,u->dhost,160);
545         strlcpy(a->host,u->host,160);
546         strlcpy(a->fullname,u->fullname,MAXGECOS);
547         strlcpy(a->server,u->server,256);
548         a->signon = u->signon;
549
550         /* MAX_WHOWAS:   max number of /WHOWAS items
551          * WHOWAS_STALE: number of hours before a WHOWAS item is marked as stale and
552          *               can be replaced by a newer one
553          */
554         
555         if (iter == whowas.end())
556         {
557                 if (whowas.size() >= (unsigned)WHOWAS_MAX)
558                 {
559                         for (whowas_hash::iterator i = whowas.begin(); i != whowas.end(); i++)
560                         {
561                                 // 3600 seconds in an hour ;)
562                                 if ((i->second->signon)<(TIME-(WHOWAS_STALE*3600)))
563                                 {
564                                         // delete the old one
565                                         if (i->second) delete i->second;
566                                         // replace with new one
567                                         i->second = a;
568                                         log(DEBUG,"added WHOWAS entry, purged an old record");
569                                         return;
570                                 }
571                         }
572                         // no space left and user doesnt exist. Don't leave ram in use!
573                         log(DEBUG,"Not able to update whowas (list at WHOWAS_MAX entries and trying to add new?), freeing excess ram");
574                         delete a;
575                 }
576                 else
577                 {
578                         log(DEBUG,"added fresh WHOWAS entry");
579                         whowas[a->nick] = a;
580                 }
581         }
582         else
583         {
584                 log(DEBUG,"updated WHOWAS entry");
585                 if (iter->second) delete iter->second;
586                 iter->second = a;
587         }
588 }
589
590 #ifdef THREADED_DNS
591 void* dns_task(void* arg)
592 {
593         userrec* u = (userrec*)arg;
594         log(DEBUG,"DNS thread for user %s",u->nick);
595         DNS dns1;
596         DNS dns2;
597         std::string host;
598         std::string ip;
599         if (dns1.ReverseLookup(u->ip))
600         {
601                 log(DEBUG,"DNS Step 1");
602                 while (!dns1.HasResult())
603                 {
604                         usleep(100);
605                 }
606                 host = dns1.GetResult();
607                 if (host != "")
608                 {
609                         log(DEBUG,"DNS Step 2: '%s'",host.c_str());
610                         if (dns2.ForwardLookup(host))
611                         {
612                                 while (!dns2.HasResult())
613                                 {
614                                         usleep(100);
615                                 }
616                                 ip = dns2.GetResultIP();
617                                 log(DEBUG,"DNS Step 3 '%s'(%d) '%s'(%d)",ip.c_str(),ip.length(),u->ip,strlen(u->ip));
618                                 if (ip == std::string(u->ip))
619                                 {
620                                         log(DEBUG,"DNS Step 4");
621                                         if (host.length() < 160)
622                                         {
623                                                 log(DEBUG,"DNS Step 5");
624                                                 strcpy(u->host,host.c_str());
625                                                 strcpy(u->dhost,host.c_str());
626                                         }
627                                 }
628                         }
629                 }
630         }
631         u->dns_done = true;
632         return NULL;
633 }
634 #endif
635
636 /* add a client connection to the sockets list */
637 void AddClient(int socket, char* host, int port, bool iscached, char* ip)
638 {
639         string tempnick;
640         char tn2[MAXBUF];
641         user_hash::iterator iter;
642
643         tempnick = ConvToStr(socket) + "-unknown";
644         sprintf(tn2,"%lu-unknown",(unsigned long)socket);
645
646         iter = clientlist.find(tempnick);
647
648         // fix by brain.
649         // as these nicknames are 'RFC impossible', we can be sure nobody is going to be
650         // using one as a registered connection. As theyre per fd, we can also safely assume
651         // that we wont have collisions. Therefore, if the nick exists in the list, its only
652         // used by a dead socket, erase the iterator so that the new client may reclaim it.
653         // this was probably the cause of 'server ignores me when i hammer it with reconnects'
654         // issue in earlier alphas/betas
655         if (iter != clientlist.end())
656         {
657                 userrec* goner = iter->second;
658                 delete goner;
659                 clientlist.erase(iter);
660         }
661
662         /*
663          * It is OK to access the value here this way since we know
664          * it exists, we just created it above.
665          *
666          * At NO other time should you access a value in a map or a
667          * hash_map this way.
668          */
669         clientlist[tempnick] = new userrec();
670
671         NonBlocking(socket);
672         log(DEBUG,"AddClient: %lu %s %d %s",(unsigned long)socket,host,port,ip);
673
674         clientlist[tempnick]->fd = socket;
675         strlcpy(clientlist[tempnick]->nick, tn2,NICKMAX);
676         strlcpy(clientlist[tempnick]->host, host,160);
677         strlcpy(clientlist[tempnick]->dhost, host,160);
678         clientlist[tempnick]->server = (char*)FindServerNamePtr(Config->ServerName);
679         strlcpy(clientlist[tempnick]->ident, "unknown",IDENTMAX);
680         clientlist[tempnick]->registered = 0;
681         clientlist[tempnick]->signon = TIME + Config->dns_timeout;
682         clientlist[tempnick]->lastping = 1;
683         clientlist[tempnick]->port = port;
684         strlcpy(clientlist[tempnick]->ip,ip,16);
685
686         // set the registration timeout for this user
687         unsigned long class_regtimeout = 90;
688         int class_flood = 0;
689         long class_threshold = 5;
690         long class_sqmax = 262144;      // 256kb
691         long class_rqmax = 4096;        // 4k
692
693         for (ClassVector::iterator i = Config->Classes.begin(); i != Config->Classes.end(); i++)
694         {
695                 if (match(clientlist[tempnick]->host,i->host) && (i->type == CC_ALLOW))
696                 {
697                         class_regtimeout = (unsigned long)i->registration_timeout;
698                         class_flood = i->flood;
699                         clientlist[tempnick]->pingmax = i->pingtime;
700                         class_threshold = i->threshold;
701                         class_sqmax = i->sendqmax;
702                         class_rqmax = i->recvqmax;
703                         break;
704                 }
705         }
706
707         clientlist[tempnick]->nping = TIME+clientlist[tempnick]->pingmax + Config->dns_timeout;
708         clientlist[tempnick]->timeout = TIME+class_regtimeout;
709         clientlist[tempnick]->flood = class_flood;
710         clientlist[tempnick]->threshold = class_threshold;
711         clientlist[tempnick]->sendqmax = class_sqmax;
712         clientlist[tempnick]->recvqmax = class_rqmax;
713
714         ucrec a;
715         a.channel = NULL;
716         a.uc_modes = 0;
717         for (int i = 0; i < MAXCHANS; i++)
718                 clientlist[tempnick]->chans.push_back(a);
719
720         if (clientlist.size() > Config->SoftLimit)
721         {
722                 kill_link(clientlist[tempnick],"No more connections allowed");
723                 return;
724         }
725
726         if (clientlist.size() >= MAXCLIENTS)
727         {
728                 kill_link(clientlist[tempnick],"No more connections allowed");
729                 return;
730         }
731
732         // this is done as a safety check to keep the file descriptors within range of fd_ref_table.
733         // its a pretty big but for the moment valid assumption:
734         // file descriptors are handed out starting at 0, and are recycled as theyre freed.
735         // therefore if there is ever an fd over 65535, 65536 clients must be connected to the
736         // irc server at once (or the irc server otherwise initiating this many connections, files etc)
737         // which for the time being is a physical impossibility (even the largest networks dont have more
738         // than about 10,000 users on ONE server!)
739         if ((unsigned)socket > 65534)
740         {
741                 kill_link(clientlist[tempnick],"Server is full");
742                 return;
743         }
744                 
745
746         char* e = matches_exception(ip);
747         if (!e)
748         {
749                 char* r = matches_zline(ip);
750                 if (r)
751                 {
752                         char reason[MAXBUF];
753                         snprintf(reason,MAXBUF,"Z-Lined: %s",r);
754                         kill_link(clientlist[tempnick],reason);
755                         return;
756                 }
757         }
758         fd_ref_table[socket] = clientlist[tempnick];
759         SE->AddFd(socket,true,X_ESTAB_CLIENT);
760 }
761
762 /* shows the message of the day, and any other on-logon stuff */
763 void FullConnectUser(userrec* user)
764 {
765         stats->statsConnects++;
766         user->idle_lastmsg = TIME;
767         log(DEBUG,"ConnectUser: %s",user->nick);
768
769         if ((strcmp(Passwd(user),"")) && (!user->haspassed))
770         {
771                 kill_link(user,"Invalid password");
772                 return;
773         }
774         if (IsDenied(user))
775         {
776                 kill_link(user,"Unauthorised connection");
777                 return;
778         }
779
780         char match_against[MAXBUF];
781         snprintf(match_against,MAXBUF,"%s@%s",user->ident,user->host);
782         char* e = matches_exception(match_against);
783         if (!e)
784         {
785                 char* r = matches_gline(match_against);
786                 if (r)
787                 {
788                         char reason[MAXBUF];
789                         snprintf(reason,MAXBUF,"G-Lined: %s",r);
790                         kill_link_silent(user,reason);
791                         return;
792                 }
793                 r = matches_kline(user->host);
794                 if (r)
795                 {
796                         char reason[MAXBUF];
797                         snprintf(reason,MAXBUF,"K-Lined: %s",r);
798                         kill_link_silent(user,reason);
799                         return;
800                 }
801         }
802
803
804         WriteServ(user->fd,"NOTICE Auth :Welcome to \002%s\002!",Config->Network);
805         WriteServ(user->fd,"001 %s :Welcome to the %s IRC Network %s!%s@%s",user->nick,Config->Network,user->nick,user->ident,user->host);
806         WriteServ(user->fd,"002 %s :Your host is %s, running version %s",user->nick,Config->ServerName,VERSION);
807         WriteServ(user->fd,"003 %s :This server was created %s %s",user->nick,__TIME__,__DATE__);
808         WriteServ(user->fd,"004 %s %s %s iowghraAsORVSxNCWqBzvdHtGI lvhopsmntikrRcaqOALQbSeKVfHGCuzN",user->nick,Config->ServerName,VERSION);
809         // the neatest way to construct the initial 005 numeric, considering the number of configure constants to go in it...
810         std::stringstream v;
811         v << "WALLCHOPS MODES=13 CHANTYPES=# PREFIX=(ohv)@%+ MAP SAFELIST MAXCHANNELS=" << MAXCHANS;
812         v << " MAXBANS=60 NICKLEN=" << NICKMAX;
813         v << " TOPICLEN=" << MAXTOPIC << " KICKLEN=" << MAXKICK << " MAXTARGETS=20 AWAYLEN=" << MAXAWAY << " CHANMODES=ohvb,k,l,psmnti NETWORK=";
814         v << Config->Network;
815         std::string data005 = v.str();
816         FOREACH_MOD On005Numeric(data005);
817         // anfl @ #ratbox, efnet reminded me that according to the RFC this cant contain more than 13 tokens per line...
818         // so i'd better split it :)
819         std::stringstream out(data005);
820         std::string token = "";
821         std::string line5 = "";
822         int token_counter = 0;
823         while (!out.eof())
824         {
825                 out >> token;
826                 line5 = line5 + token + " ";
827                 token_counter++;
828                 if ((token_counter >= 13) || (out.eof() == true))
829                 {
830                         WriteServ(user->fd,"005 %s %s:are supported by this server",user->nick,line5.c_str());
831                         line5 = "";
832                         token_counter = 0;
833                 }
834         }
835         ShowMOTD(user);
836
837         // fix 3 by brain, move registered = 7 below these so that spurious modes and host changes dont go out
838         // onto the network and produce 'fake direction'
839         FOREACH_MOD OnUserConnect(user);
840         FOREACH_MOD OnGlobalConnect(user);
841         user->registered = 7;
842         WriteOpers("*** Client connecting on port %lu: %s!%s@%s [%s]",(unsigned long)user->port,user->nick,user->ident,user->host,user->ip);
843 }
844
845
846 /* shows the message of the day, and any other on-logon stuff */
847 void ConnectUser(userrec *user)
848 {
849         // dns is already done, things are fast. no need to wait for dns to complete just pass them straight on
850         if ((user->dns_done) && (user->registered >= 3) && (AllModulesReportReady(user)))
851         {
852                 FullConnectUser(user);
853         }
854 }
855
856 std::string GetVersionString()
857 {
858         char versiondata[MAXBUF];
859 #ifdef THREADED_DNS
860         char dnsengine[] = "multithread";
861 #else
862         char dnsengine[] = "singlethread";
863 #endif
864         snprintf(versiondata,MAXBUF,"%s Rev. %s %s :%s [FLAGS=%lu,%s,%s]",VERSION,GetRevision().c_str(),Config->ServerName,SYSTEM,(unsigned long)OPTIMISATION,SE->GetName().c_str(),dnsengine);
865         return versiondata;
866 }
867
868 void handle_version(char **parameters, int pcnt, userrec *user)
869 {
870         WriteServ(user->fd,"351 %s :%s",user->nick,GetVersionString().c_str());
871 }
872
873
874 bool is_valid_cmd(const char* commandname, int pcnt, userrec * user)
875 {
876         for (unsigned int i = 0; i < cmdlist.size(); i++)
877         {
878                 if (!strcasecmp(cmdlist[i].command,commandname))
879                 {
880                         if (cmdlist[i].handler_function)
881                         {
882                                 if ((pcnt>=cmdlist[i].min_params) && (strcasecmp(cmdlist[i].source,"<core>")))
883                                 {
884                                         if ((strchr(user->modes,cmdlist[i].flags_needed)) || (!cmdlist[i].flags_needed))
885                                         {
886                                                 if (cmdlist[i].flags_needed)
887                                                 {
888                                                         if ((user->HasPermission((char*)commandname)) || (is_uline(user->server)))
889                                                         {
890                                                                 return true;
891                                                         }
892                                                         else
893                                                         {
894                                                                 return false;
895                                                         }
896                                                 }
897                                                 return true;
898                                         }
899                                 }
900                         }
901                 }
902         }
903         return false;
904 }
905
906 // calls a handler function for a command
907
908 void call_handler(const char* commandname,char **parameters, int pcnt, userrec *user)
909 {
910         for (unsigned int i = 0; i < cmdlist.size(); i++)
911         {
912                 if (!strcasecmp(cmdlist[i].command,commandname))
913                 {
914                         if (cmdlist[i].handler_function)
915                         {
916                                 if (pcnt>=cmdlist[i].min_params)
917                                 {
918                                         if ((strchr(user->modes,cmdlist[i].flags_needed)) || (!cmdlist[i].flags_needed))
919                                         {
920                                                 if (cmdlist[i].flags_needed)
921                                                 {
922                                                         if ((user->HasPermission((char*)commandname)) || (is_uline(user->server)))
923                                                         {
924                                                                 cmdlist[i].handler_function(parameters,pcnt,user);
925                                                         }
926                                                 }
927                                                 else
928                                                 {
929                                                         cmdlist[i].handler_function(parameters,pcnt,user);
930                                                 }
931                                         }
932                                 }
933                         }
934                 }
935         }
936 }
937
938
939 void force_nickchange(userrec* user,const char* newnick)
940 {
941         char nick[MAXBUF];
942         int MOD_RESULT = 0;
943         
944         strcpy(nick,"");
945
946         FOREACH_RESULT(OnUserPreNick(user,newnick));
947         if (MOD_RESULT) {
948                 stats->statsCollisions++;
949                 kill_link(user,"Nickname collision");
950                 return;
951         }
952         if (matches_qline(newnick))
953         {
954                 stats->statsCollisions++;
955                 kill_link(user,"Nickname collision");
956                 return;
957         }
958         
959         if (user)
960         {
961                 if (newnick)
962                 {
963                         strncpy(nick,newnick,MAXBUF);
964                 }
965                 if (user->registered == 7)
966                 {
967                         char* pars[1];
968                         pars[0] = nick;
969                         handle_nick(pars,1,user);
970                 }
971         }
972 }
973                                 
974
975 int process_parameters(char **command_p,char *parameters)
976 {
977         int j = 0;
978         int q = strlen(parameters);
979         if (!q)
980         {
981                 /* no parameters, command_p invalid! */
982                 return 0;
983         }
984         if (parameters[0] == ':')
985         {
986                 command_p[0] = parameters+1;
987                 return 1;
988         }
989         if (q)
990         {
991                 if ((strchr(parameters,' ')==NULL) || (parameters[0] == ':'))
992                 {
993                         /* only one parameter */
994                         command_p[0] = parameters;
995                         if (parameters[0] == ':')
996                         {
997                                 if (strchr(parameters,' ') != NULL)
998                                 {
999                                         command_p[0]++;
1000                                 }
1001                         }
1002                         return 1;
1003                 }
1004         }
1005         command_p[j++] = parameters;
1006         for (int i = 0; i <= q; i++)
1007         {
1008                 if (parameters[i] == ' ')
1009                 {
1010                         command_p[j++] = parameters+i+1;
1011                         parameters[i] = '\0';
1012                         if (command_p[j-1][0] == ':')
1013                         {
1014                                 *command_p[j-1]++; /* remove dodgy ":" */
1015                                 break;
1016                                 /* parameter like this marks end of the sequence */
1017                         }
1018                 }
1019         }
1020         return j; /* returns total number of items in the list */
1021 }
1022
1023 void process_command(userrec *user, char* cmd)
1024 {
1025         char *parameters;
1026         char *command;
1027         char *command_p[127];
1028         char p[MAXBUF], temp[MAXBUF];
1029         int j, items, cmd_found;
1030
1031         for (int i = 0; i < 127; i++)
1032                 command_p[i] = NULL;
1033
1034         if (!user)
1035         {
1036                 return;
1037         }
1038         if (!cmd)
1039         {
1040                 return;
1041         }
1042         if (!cmd[0])
1043         {
1044                 return;
1045         }
1046         
1047         int total_params = 0;
1048         if (strlen(cmd)>2)
1049         {
1050                 for (unsigned int q = 0; q < strlen(cmd)-1; q++)
1051                 {
1052                         if ((cmd[q] == ' ') && (cmd[q+1] == ':'))
1053                         {
1054                                 total_params++;
1055                                 // found a 'trailing', we dont count them after this.
1056                                 break;
1057                         }
1058                         if (cmd[q] == ' ')
1059                                 total_params++;
1060                 }
1061         }
1062
1063         // another phidjit bug...
1064         if (total_params > 126)
1065         {
1066                 *(strchr(cmd,' ')) = '\0';
1067                 WriteServ(user->fd,"421 %s %s :Too many parameters given",user->nick,cmd);
1068                 return;
1069         }
1070
1071         strlcpy(temp,cmd,MAXBUF);
1072         
1073         std::string tmp = cmd;
1074         for (int i = 0; i <= MODCOUNT; i++)
1075         {
1076                 std::string oldtmp = tmp;
1077                 modules[i]->OnServerRaw(tmp,true,user);
1078                 if (oldtmp != tmp)
1079                 {
1080                         log(DEBUG,"A Module changed the input string!");
1081                         log(DEBUG,"New string: %s",tmp.c_str());
1082                         log(DEBUG,"Old string: %s",oldtmp.c_str());
1083                         break;
1084                 }
1085         }
1086         strlcpy(cmd,tmp.c_str(),MAXBUF);
1087         strlcpy(temp,cmd,MAXBUF);
1088
1089         if (!strchr(cmd,' '))
1090         {
1091                 /* no parameters, lets skip the formalities and not chop up
1092                  * the string */
1093                 log(DEBUG,"About to preprocess command with no params");
1094                 items = 0;
1095                 command_p[0] = NULL;
1096                 parameters = NULL;
1097                 for (unsigned int i = 0; i <= strlen(cmd); i++)
1098                 {
1099                         cmd[i] = toupper(cmd[i]);
1100                 }
1101                 command = cmd;
1102         }
1103         else
1104         {
1105                 strcpy(cmd,"");
1106                 j = 0;
1107                 /* strip out extraneous linefeeds through mirc's crappy pasting (thanks Craig) */
1108                 for (unsigned int i = 0; i < strlen(temp); i++)
1109                 {
1110                         if ((temp[i] != 10) && (temp[i] != 13) && (temp[i] != 0) && (temp[i] != 7))
1111                         {
1112                                 cmd[j++] = temp[i];
1113                                 cmd[j] = 0;
1114                         }
1115                 }
1116                 /* split the full string into a command plus parameters */
1117                 parameters = p;
1118                 strcpy(p," ");
1119                 command = cmd;
1120                 if (strchr(cmd,' '))
1121                 {
1122                         for (unsigned int i = 0; i <= strlen(cmd); i++)
1123                         {
1124                                 /* capitalise the command ONLY, leave params intact */
1125                                 cmd[i] = toupper(cmd[i]);
1126                                 /* are we nearly there yet?! :P */
1127                                 if (cmd[i] == ' ')
1128                                 {
1129                                         command = cmd;
1130                                         parameters = cmd+i+1;
1131                                         cmd[i] = '\0';
1132                                         break;
1133                                 }
1134                         }
1135                 }
1136                 else
1137                 {
1138                         for (unsigned int i = 0; i <= strlen(cmd); i++)
1139                         {
1140                                 cmd[i] = toupper(cmd[i]);
1141                         }
1142                 }
1143
1144         }
1145         cmd_found = 0;
1146         
1147         if (strlen(command)>MAXCOMMAND)
1148         {
1149                 WriteServ(user->fd,"421 %s %s :Command too long",user->nick,command);
1150                 return;
1151         }
1152         
1153         for (unsigned int x = 0; x < strlen(command); x++)
1154         {
1155                 if (((command[x] < 'A') || (command[x] > 'Z')) && (command[x] != '.'))
1156                 {
1157                         if (((command[x] < '0') || (command[x]> '9')) && (command[x] != '-'))
1158                         {
1159                                 if (strchr("@!\"$%^&*(){}[]_=+;:'#~,<>/?\\|`",command[x]))
1160                                 {
1161                                         stats->statsUnknown++;
1162                                         WriteServ(user->fd,"421 %s %s :Unknown command",user->nick,command);
1163                                         return;
1164                                 }
1165                         }
1166                 }
1167         }
1168
1169         for (unsigned int i = 0; i != cmdlist.size(); i++)
1170         {
1171                 if (cmdlist[i].command[0])
1172                 {
1173                         if (strlen(command)>=(strlen(cmdlist[i].command))) if (!strncmp(command, cmdlist[i].command,MAXCOMMAND))
1174                         {
1175                                 if (parameters)
1176                                 {
1177                                         if (parameters[0])
1178                                         {
1179                                                 items = process_parameters(command_p,parameters);
1180                                         }
1181                                         else
1182                                         {
1183                                                 items = 0;
1184                                                 command_p[0] = NULL;
1185                                         }
1186                                 }
1187                                 else
1188                                 {
1189                                         items = 0;
1190                                         command_p[0] = NULL;
1191                                 }
1192                                 
1193                                 if (user)
1194                                 {
1195                                         /* activity resets the ping pending timer */
1196                                         user->nping = TIME + user->pingmax;
1197                                         if ((items) < cmdlist[i].min_params)
1198                                         {
1199                                                 log(DEBUG,"process_command: not enough parameters: %s %s",user->nick,command);
1200                                                 WriteServ(user->fd,"461 %s %s :Not enough parameters",user->nick,command);
1201                                                 return;
1202                                         }
1203                                         if ((!strchr(user->modes,cmdlist[i].flags_needed)) && (cmdlist[i].flags_needed))
1204                                         {
1205                                                 log(DEBUG,"process_command: permission denied: %s %s",user->nick,command);
1206                                                 WriteServ(user->fd,"481 %s :Permission Denied- You do not have the required operator privilages",user->nick);
1207                                                 cmd_found = 1;
1208                                                 return;
1209                                         }
1210                                         if ((cmdlist[i].flags_needed) && (!user->HasPermission(command)))
1211                                         {
1212                                                 log(DEBUG,"process_command: permission denied: %s %s",user->nick,command);
1213                                                 WriteServ(user->fd,"481 %s :Permission Denied- Oper type %s does not have access to command %s",user->nick,user->oper,command);
1214                                                 cmd_found = 1;
1215                                                 return;
1216                                         }
1217                                         /* if the command isnt USER, PASS, or NICK, and nick is empty,
1218                                          * deny command! */
1219                                         if ((strncmp(command,"USER",4)) && (strncmp(command,"NICK",4)) && (strncmp(command,"PASS",4)))
1220                                         {
1221                                                 if ((!isnick(user->nick)) || (user->registered != 7))
1222                                                 {
1223                                                         log(DEBUG,"process_command: not registered: %s %s",user->nick,command);
1224                                                         WriteServ(user->fd,"451 %s :You have not registered",command);
1225                                                         return;
1226                                                 }
1227                                         }
1228                                         if ((user->registered == 7) && (!strchr(user->modes,'o')))
1229                                         {
1230                                                 std::stringstream dcmds(Config->DisabledCommands);
1231                                                 while (!dcmds.eof())
1232                                                 {
1233                                                         std::string thiscmd;
1234                                                         dcmds >> thiscmd;
1235                                                         if (!strcasecmp(thiscmd.c_str(),command))
1236                                                         {
1237                                                                 // command is disabled!
1238                                                                 WriteServ(user->fd,"421 %s %s :This command has been disabled.",user->nick,command);
1239                                                                 return;
1240                                                         }
1241                                                 }
1242                                         }
1243                                         if ((user->registered == 7) || (!strncmp(command,"USER",4)) || (!strncmp(command,"NICK",4)) || (!strncmp(command,"PASS",4)))
1244                                         {
1245                                                 if (cmdlist[i].handler_function)
1246                                                 {
1247                                                         
1248                                                         /* ikky /stats counters */
1249                                                         if (temp)
1250                                                         {
1251                                                                 cmdlist[i].use_count++;
1252                                                                 cmdlist[i].total_bytes+=strlen(temp);
1253                                                         }
1254
1255                                                         int MOD_RESULT = 0;
1256                                                         FOREACH_RESULT(OnPreCommand(command,command_p,items,user));
1257                                                         if (MOD_RESULT == 1) {
1258                                                                 return;
1259                                                         }
1260
1261                                                         /* WARNING: nothing may come after the
1262                                                          * command handler call, as the handler
1263                                                          * may free the user structure! */
1264
1265                                                         cmdlist[i].handler_function(command_p,items,user);
1266                                                 }
1267                                                 return;
1268                                         }
1269                                         else
1270                                         {
1271                                                 WriteServ(user->fd,"451 %s :You have not registered",command);
1272                                                 return;
1273                                         }
1274                                 }
1275                                 cmd_found = 1;
1276                         }
1277                 }
1278         }
1279         if ((!cmd_found) && (user))
1280         {
1281                 stats->statsUnknown++;
1282                 WriteServ(user->fd,"421 %s %s :Unknown command",user->nick,command);
1283         }
1284 }
1285
1286 bool removecommands(const char* source)
1287 {
1288         bool go_again = true;
1289         while (go_again)
1290         {
1291                 go_again = false;
1292                 for (std::deque<command_t>::iterator i = cmdlist.begin(); i != cmdlist.end(); i++)
1293                 {
1294                         if (!strcmp(i->source,source))
1295                         {
1296                                 log(DEBUG,"removecommands(%s) Removing dependent command: %s",i->source,i->command);
1297                                 cmdlist.erase(i);
1298                                 go_again = true;
1299                                 break;
1300                         }
1301                 }
1302         }
1303         return true;
1304 }
1305
1306
1307 void process_buffer(const char* cmdbuf,userrec *user)
1308 {
1309         if (!user)
1310         {
1311                 log(DEFAULT,"*** BUG *** process_buffer was given an invalid parameter");
1312                 return;
1313         }
1314         char cmd[MAXBUF];
1315         if (!cmdbuf)
1316         {
1317                 log(DEFAULT,"*** BUG *** process_buffer was given an invalid parameter");
1318                 return;
1319         }
1320         if (!cmdbuf[0])
1321         {
1322                 return;
1323         }
1324         while (*cmdbuf == ' ') cmdbuf++; // strip leading spaces
1325
1326         strlcpy(cmd,cmdbuf,MAXBUF);
1327         if (!cmd[0])
1328         {
1329                 return;
1330         }
1331         int sl = strlen(cmd)-1;
1332         if ((cmd[sl] == 13) || (cmd[sl] == 10))
1333         {
1334                 cmd[sl] = '\0';
1335         }
1336         sl = strlen(cmd)-1;
1337         if ((cmd[sl] == 13) || (cmd[sl] == 10))
1338         {
1339                 cmd[sl] = '\0';
1340         }
1341         sl = strlen(cmd)-1;
1342         while (cmd[sl] == ' ') // strip trailing spaces
1343         {
1344                 cmd[sl] = '\0';
1345                 sl = strlen(cmd)-1;
1346         }
1347
1348         if (!cmd[0])
1349         {
1350                 return;
1351         }
1352         log(DEBUG,"CMDIN: %s %s",user->nick,cmd);
1353         tidystring(cmd);
1354         if ((user) && (cmd))
1355         {
1356                 process_command(user,cmd);
1357         }
1358 }
1359
1360 char MODERR[MAXBUF];
1361
1362 char* ModuleError()
1363 {
1364         return MODERR;
1365 }
1366
1367 void InspIRCd::erase_factory(int j)
1368 {
1369         int v = 0;
1370         for (std::vector<ircd_module*>::iterator t = factory.begin(); t != factory.end(); t++)
1371         {
1372                 if (v == j)
1373                 {
1374                         factory.erase(t);
1375                         factory.push_back(NULL);
1376                         return;
1377                 }
1378                 v++;
1379         }
1380 }
1381
1382 void InspIRCd::erase_module(int j)
1383 {
1384         int v1 = 0;
1385         for (std::vector<Module*>::iterator m = modules.begin(); m!= modules.end(); m++)
1386         {
1387                 if (v1 == j)
1388                 {
1389                         delete *m;
1390                         modules.erase(m);
1391                         modules.push_back(NULL);
1392                         break;
1393                 }
1394                 v1++;
1395         }
1396         int v2 = 0;
1397         for (std::vector<std::string>::iterator v = Config->module_names.begin(); v != Config->module_names.end(); v++)
1398         {
1399                 if (v2 == j)
1400                 {
1401                        Config->module_names.erase(v);
1402                        break;
1403                 }
1404                 v2++;
1405         }
1406
1407 }
1408
1409 bool InspIRCd::UnloadModule(const char* filename)
1410 {
1411         std::string filename_str = filename;
1412         for (unsigned int j = 0; j != Config->module_names.size(); j++)
1413         {
1414                 if (Config->module_names[j] == filename_str)
1415                 {
1416                         if (modules[j]->GetVersion().Flags & VF_STATIC)
1417                         {
1418                                 log(DEFAULT,"Failed to unload STATIC module %s",filename);
1419                                 snprintf(MODERR,MAXBUF,"Module not unloadable (marked static)");
1420                                 return false;
1421                         }
1422                         /* Give the module a chance to tidy out all its metadata */
1423                         for (chan_hash::iterator c = chanlist.begin(); c != chanlist.end(); c++)
1424                         {
1425                                 modules[j]->OnCleanup(TYPE_CHANNEL,c->second);
1426                         }
1427                         for (user_hash::iterator u = clientlist.begin(); u != clientlist.end(); u++)
1428                         {
1429                                 modules[j]->OnCleanup(TYPE_USER,u->second);
1430                         }
1431                         FOREACH_MOD OnUnloadModule(modules[j],Config->module_names[j]);
1432                         // found the module
1433                         log(DEBUG,"Deleting module...");
1434                         erase_module(j);
1435                         log(DEBUG,"Erasing module entry...");
1436                         erase_factory(j);
1437                         log(DEBUG,"Removing dependent commands...");
1438                         removecommands(filename);
1439                         log(DEFAULT,"Module %s unloaded",filename);
1440                         MODCOUNT--;
1441                         return true;
1442                 }
1443         }
1444         log(DEFAULT,"Module %s is not loaded, cannot unload it!",filename);
1445         snprintf(MODERR,MAXBUF,"Module not loaded");
1446         return false;
1447 }
1448
1449 bool InspIRCd::LoadModule(const char* filename)
1450 {
1451         char modfile[MAXBUF];
1452 #ifdef STATIC_LINK
1453         snprintf(modfile,MAXBUF,"%s",filename);
1454 #else
1455         snprintf(modfile,MAXBUF,"%s/%s",Config->ModPath,filename);
1456 #endif
1457         std::string filename_str = filename;
1458 #ifndef STATIC_LINK
1459         if (!DirValid(modfile))
1460         {
1461                 log(DEFAULT,"Module %s is not within the modules directory.",modfile);
1462                 snprintf(MODERR,MAXBUF,"Module %s is not within the modules directory.",modfile);
1463                 return false;
1464         }
1465 #endif
1466         log(DEBUG,"Loading module: %s",modfile);
1467 #ifndef STATIC_LINK
1468         if (FileExists(modfile))
1469         {
1470 #endif
1471                 for (unsigned int j = 0; j < Config->module_names.size(); j++)
1472                 {
1473                         if (Config->module_names[j] == filename_str)
1474                         {
1475                                 log(DEFAULT,"Module %s is already loaded, cannot load a module twice!",modfile);
1476                                 snprintf(MODERR,MAXBUF,"Module already loaded");
1477                                 return false;
1478                         }
1479                 }
1480                 ircd_module* a = new ircd_module(modfile);
1481                 factory[MODCOUNT+1] = a;
1482                 if (factory[MODCOUNT+1]->LastError())
1483                 {
1484                         log(DEFAULT,"Unable to load %s: %s",modfile,factory[MODCOUNT+1]->LastError());
1485                         snprintf(MODERR,MAXBUF,"Loader/Linker error: %s",factory[MODCOUNT+1]->LastError());
1486                         MODCOUNT--;
1487                         return false;
1488                 }
1489                 if (factory[MODCOUNT+1]->factory)
1490                 {
1491                         Module* m = factory[MODCOUNT+1]->factory->CreateModule(MyServer);
1492                         modules[MODCOUNT+1] = m;
1493                         /* save the module and the module's classfactory, if
1494                          * this isnt done, random crashes can occur :/ */
1495                         Config->module_names.push_back(filename);
1496                 }
1497                 else
1498                 {
1499                         log(DEFAULT,"Unable to load %s",modfile);
1500                         snprintf(MODERR,MAXBUF,"Factory function failed!");
1501                         return false;
1502                 }
1503 #ifndef STATIC_LINK
1504         }
1505         else
1506         {
1507                 log(DEFAULT,"InspIRCd: startup: Module Not Found %s",modfile);
1508                 snprintf(MODERR,MAXBUF,"Module file could not be found");
1509                 return false;
1510         }
1511 #endif
1512         MODCOUNT++;
1513         FOREACH_MOD OnLoadModule(modules[MODCOUNT],filename_str);
1514         return true;
1515 }
1516
1517 int InspIRCd::Run()
1518 {
1519         bool expire_run = false;
1520         std::vector<int> activefds;
1521         int incomingSockfd;
1522         int in_port;
1523         userrec* cu = NULL;
1524         InspSocket* s = NULL;
1525         InspSocket* s_del = NULL;
1526         char* target;
1527         unsigned int numberactive;
1528         sockaddr_in sock_us;     // our port number
1529         socklen_t uslen;         // length of our port number
1530
1531         if (!Config->nofork)
1532         {
1533                 freopen("/dev/null","w",stdout);
1534                 freopen("/dev/null","w",stderr);
1535         }
1536
1537         /* Add the listening sockets used for client inbound connections
1538          * to the socket engine
1539          */
1540         for (int count = 0; count < BoundPortCount; count++)
1541                 SE->AddFd(openSockfd[count],true,X_LISTEN);
1542
1543         WritePID(Config->PID);
1544
1545         /* main loop, this never returns */
1546         for (;;)
1547         {
1548                 /* time() seems to be a pretty expensive syscall, so avoid calling it too much.
1549                  * Once per loop iteration is pleanty.
1550                  */
1551                 OLDTIME = TIME;
1552                 TIME = time(NULL);
1553
1554                 /* Run background module timers every few seconds
1555                  * (the docs say modules shouldnt rely on accurate
1556                  * timing using this event, so we dont have to
1557                  * time this exactly).
1558                  */
1559                 if (((TIME % 8) == 0) && (!expire_run))
1560                 {
1561                         expire_lines();
1562                         FOREACH_MOD OnBackgroundTimer(TIME);
1563                         expire_run = true;
1564                         continue;
1565                 }
1566                 if ((TIME % 8) == 1)
1567                         expire_run = false;
1568                 
1569                 /* Once a second, do the background processing */
1570                 if (TIME != OLDTIME)
1571                         while (DoBackgroundUserStuff(TIME));
1572
1573                 /* Call the socket engine to wait on the active
1574                  * file descriptors. The socket engine has everything's
1575                  * descriptors in its list... dns, modules, users,
1576                  * servers... so its nice and easy, just one call.
1577                  */
1578                 SE->Wait(activefds);
1579
1580                 /**
1581                  * Now process each of the fd's. For users, we have a fast
1582                  * lookup table which can find a user by file descriptor, so
1583                  * processing them by fd isnt expensive. If we have a lot of
1584                  * listening ports or module sockets though, things could get
1585                  * ugly.
1586                  */
1587                 numberactive = activefds.size();
1588                 for (unsigned int activefd = 0; activefd < numberactive; activefd++)
1589                 {
1590                         int socket_type = SE->GetType(activefds[activefd]);
1591                         switch (socket_type)
1592                         {
1593                                 case X_ESTAB_CLIENT:
1594
1595                                         cu = fd_ref_table[activefds[activefd]];
1596                                         if (cu)
1597                                                 ProcessUser(cu);
1598
1599                                 break;
1600
1601                                 case X_ESTAB_MODULE:
1602
1603                                         /* Process module-owned sockets.
1604                                          * Modules are encouraged to inherit their sockets from
1605                                          * InspSocket so we can process them neatly like this.
1606                                          */
1607                                         s = socket_ref[activefds[activefd]];
1608
1609                                         if ((s) && (!s->Poll()))
1610                                         {
1611                                                 log(DEBUG,"Socket poll returned false, close and bail");
1612                                                 SE->DelFd(s->GetFd());
1613                                                 for (std::vector<InspSocket*>::iterator a = this->module_sockets.begin(); a < this->module_sockets.end(); a++)
1614                                                 {
1615                                                         s_del = (InspSocket*)*a;
1616                                                         if ((s_del) && (s_del->GetFd() == activefds[activefd]))
1617                                                         {
1618                                                                 this->module_sockets.erase(a);
1619                                                                 break;
1620                                                         }
1621                                                 }
1622                                                 s->Close();
1623                                                 delete s;
1624                                         }
1625
1626                                 break;
1627
1628                                 case X_ESTAB_DNS:
1629
1630                                         /* When we are using single-threaded dns,
1631                                          * the sockets for dns end up in our mainloop.
1632                                          * When we are using multi-threaded dns,
1633                                          * each thread has its own basic poll() loop
1634                                          * within it, making them 'fire and forget'
1635                                          * and independent of the mainloop.
1636                                          */
1637 #ifndef THREADED_DNS
1638                                         dns_poll(activefds[activefd]);
1639 #endif
1640                                 break;
1641                                 
1642                                 case X_LISTEN:
1643
1644                                         /* It's a listener */
1645                                         uslen = sizeof(sock_us);
1646                                         length = sizeof(client);
1647                                         incomingSockfd = accept (activefds[activefd],(struct sockaddr*)&client,&length);
1648                                         if (!getsockname(incomingSockfd,(sockaddr*)&sock_us,&uslen))
1649                                         {
1650                                                 in_port = ntohs(sock_us.sin_port);
1651                                                 log(DEBUG,"Accepted socket %d",incomingSockfd);
1652                                                 target = (char*)inet_ntoa(client.sin_addr);
1653                                                 /* Years and years ago, we used to resolve here
1654                                                  * using gethostbyaddr(). That is sucky and we
1655                                                  * don't do that any more...
1656                                                  */
1657                                                 if (incomingSockfd >= 0)
1658                                                 {
1659                                                         FOREACH_MOD OnRawSocketAccept(incomingSockfd, target, in_port);
1660                                                         stats->statsAccept++;
1661                                                         AddClient(incomingSockfd, target, in_port, false, target);
1662                                                         log(DEBUG,"Adding client on port %lu fd=%lu",(unsigned long)in_port,(unsigned long)incomingSockfd);
1663                                                 }
1664                                                 else
1665                                                 {
1666                                                         WriteOpers("*** WARNING: accept() failed on port %lu (%s)",(unsigned long)in_port,target);
1667                                                         log(DEBUG,"accept failed: %lu",(unsigned long)in_port);
1668                                                         stats->statsRefused++;
1669                                                 }
1670                                         }
1671                                         else
1672                                         {
1673                                                 log(DEBUG,"Couldnt look up the port number for fd %lu (OS BROKEN?!)",incomingSockfd);
1674                                                 shutdown(incomingSockfd,2);
1675                                                 close(incomingSockfd);
1676                                         }
1677                                 break;
1678
1679                                 default:
1680                                         /* Something went wrong if we're in here.
1681                                          * In fact, so wrong, im not quite sure
1682                                          * what we would do, so for now, its going
1683                                          * to safely do bugger all.
1684                                          */
1685                                 break;
1686                         }
1687                 }
1688
1689         }
1690         /* This is never reached -- we hope! */
1691         return 0;
1692 }
1693
1694 /**********************************************************************************/
1695
1696 /**
1697  * An ircd in four lines! bwahahaha. ahahahahaha. ahahah *cough*.
1698  */
1699
1700 int main(int argc, char** argv)
1701 {
1702         ServerInstance = new InspIRCd(argc, argv);
1703         ServerInstance->Run();
1704         delete ServerInstance;
1705         return 0;
1706 }
1707