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