]> git.netwichtig.de Git - user/henk/code/inspircd.git/blob - src/inspircd.cpp
f409fcc18625b00c4179991160b2c1481b7f0212
[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 std::vector<InspSocket*> module_sockets;
73
74 extern int MODCOUNT;
75 int openSockfd[MAXSOCKS];
76 sockaddr_in client,server;
77 socklen_t length;
78
79 extern InspSocket* socket_ref[65535];
80
81 time_t TIME = time(NULL), OLDTIME = time(NULL);
82
83 SocketEngine* SE = NULL;
84
85 // This table references users by file descriptor.
86 // its an array to make it VERY fast, as all lookups are referenced
87 // by an integer, meaning there is no need for a scan/search operation.
88 userrec* fd_ref_table[65536];
89
90 serverstats* stats = new serverstats;
91 Server* MyServer = new Server;
92 ServerConfig *Config = new ServerConfig;
93
94 user_hash clientlist;
95 chan_hash chanlist;
96 whowas_hash whowas;
97 command_table cmdlist;
98 servernamelist servernames;
99 int BoundPortCount = 0;
100 char lowermap[255];
101
102 void AddServerName(std::string servername)
103 {
104         log(DEBUG,"Adding server name: %s",servername.c_str());
105         for (servernamelist::iterator a = servernames.begin(); a < servernames.end(); a++)
106         {
107                 if (*a == servername)
108                         return;
109         }
110         servernames.push_back(servername);
111 }
112
113 const char* FindServerNamePtr(std::string servername)
114 {
115         for (servernamelist::iterator a = servernames.begin(); a < servernames.end(); a++)
116         {
117                 if (*a == servername)
118                         return a->c_str();
119         }
120         AddServerName(servername);
121         return FindServerNamePtr(servername);
122 }
123
124 std::string InspIRCd::GetRevision()
125 {
126         /* w00t got me to replace a bunch of strtok_r
127          * with something nicer, so i did this. Its the
128          * same thing really, only in C++. It places the
129          * text into a std::stringstream which is a readable
130          * and writeable buffer stream, and then pops two
131          * words off it, space delimited. Because it reads
132          * into the same variable twice, the first word
133          * is discarded, and the second one returned.
134          */
135         std::stringstream Revision("$Revision$");
136         std::string single;
137         Revision >> single >> single;
138         return single;
139 }
140
141
142 /* This function pokes and hacks at a parameter list like the following:
143  *
144  * PART #winbot,#darkgalaxy :m00!
145  *
146  * to turn it into a series of individual calls like this:
147  *
148  * PART #winbot :m00!
149  * PART #darkgalaxy :m00!
150  *
151  * The seperate calls are sent to a callback function provided by the caller
152  * (the caller will usually call itself recursively). The callback function
153  * must be a command handler. Calling this function on a line with no list causes
154  * no action to be taken. You must provide a starting and ending parameter number
155  * where the range of the list can be found, useful if you have a terminating
156  * parameter as above which is actually not part of the list, or parameters
157  * before the actual list as well. This code is used by many functions which
158  * can function as "one to list" (see the RFC) */
159
160 int loop_call(handlerfunc fn, char **parameters, int pcnt, userrec *u, int start, int end, int joins)
161 {
162         char plist[MAXBUF];
163         char *param;
164         char *pars[32];
165         char blog[32][MAXBUF];
166         char blog2[32][MAXBUF];
167         int j = 0, q = 0, total = 0, t = 0, t2 = 0, total2 = 0;
168         char keystr[MAXBUF];
169         char moo[MAXBUF];
170
171         for (int i = 0; i <32; i++)
172                 strcpy(blog[i],"");
173
174         for (int i = 0; i <32; i++)
175                 strcpy(blog2[i],"");
176
177         strcpy(moo,"");
178         for (int i = 0; i <10; i++)
179         {
180                 if (!parameters[i])
181                 {
182                         parameters[i] = moo;
183                 }
184         }
185         if (joins)
186         {
187                 if (pcnt > 1) /* we have a key to copy */
188                 {
189                         strlcpy(keystr,parameters[1],MAXBUF);
190                 }
191         }
192
193         if (!parameters[start])
194         {
195                 return 0;
196         }
197         if (!strchr(parameters[start],','))
198         {
199                 return 0;
200         }
201         strcpy(plist,"");
202         for (int i = start; i <= end; i++)
203         {
204                 if (parameters[i])
205                 {
206                         strlcat(plist,parameters[i],MAXBUF);
207                 }
208         }
209         
210         j = 0;
211         param = plist;
212
213         t = strlen(plist);
214         for (int i = 0; i < t; i++)
215         {
216                 if (plist[i] == ',')
217                 {
218                         plist[i] = '\0';
219                         strlcpy(blog[j++],param,MAXBUF);
220                         param = plist+i+1;
221                         if (j>20)
222                         {
223                                 WriteServ(u->fd,"407 %s %s :Too many targets in list, message not delivered.",u->nick,blog[j-1]);
224                                 return 1;
225                         }
226                 }
227         }
228         strlcpy(blog[j++],param,MAXBUF);
229         total = j;
230
231         if ((joins) && (keystr) && (total>0)) // more than one channel and is joining
232         {
233                 strcat(keystr,",");
234         }
235         
236         if ((joins) && (keystr))
237         {
238                 if (strchr(keystr,','))
239                 {
240                         j = 0;
241                         param = keystr;
242                         t2 = strlen(keystr);
243                         for (int i = 0; i < t2; i++)
244                         {
245                                 if (keystr[i] == ',')
246                                 {
247                                         keystr[i] = '\0';
248                                         strlcpy(blog2[j++],param,MAXBUF);
249                                         param = keystr+i+1;
250                                 }
251                         }
252                         strlcpy(blog2[j++],param,MAXBUF);
253                         total2 = j;
254                 }
255         }
256
257         for (j = 0; j < total; j++)
258         {
259                 if (blog[j])
260                 {
261                         pars[0] = blog[j];
262                 }
263                 for (q = end; q < pcnt-1; q++)
264                 {
265                         if (parameters[q+1])
266                         {
267                                 pars[q-end+1] = parameters[q+1];
268                         }
269                 }
270                 if ((joins) && (parameters[1]))
271                 {
272                         if (pcnt > 1)
273                         {
274                                 pars[1] = blog2[j];
275                         }
276                         else
277                         {
278                                 pars[1] = NULL;
279                         }
280                 }
281                 /* repeatedly call the function with the hacked parameter list */
282                 if ((joins) && (pcnt > 1))
283                 {
284                         if (pars[1])
285                         {
286                                 // pars[1] already set up and containing key from blog2[j]
287                                 fn(pars,2,u);
288                         }
289                         else
290                         {
291                                 pars[1] = parameters[1];
292                                 fn(pars,2,u);
293                         }
294                 }
295                 else
296                 {
297                         fn(pars,pcnt-(end-start),u);
298                 }
299         }
300
301         return 1;
302 }
303
304
305
306 InspIRCd::InspIRCd(int argc, char** argv)
307 {
308         Start();
309         module_sockets.clear();
310         this->startup_time = time(NULL);
311         srand(time(NULL));
312         log(DEBUG,"*** InspIRCd starting up!");
313         if (!FileExists(CONFIG_FILE))
314         {
315                 printf("ERROR: Cannot open config file: %s\nExiting...\n",CONFIG_FILE);
316                 log(DEFAULT,"main: no config");
317                 printf("ERROR: Your config file is missing, this IRCd will self destruct in 10 seconds!\n");
318                 Exit(ERROR);
319         }
320         if (argc > 1) {
321                 for (int i = 1; i < argc; i++)
322                 {
323                         if (!strcmp(argv[i],"-nofork")) {
324                                 Config->nofork = true;
325                         }
326                         if (!strcmp(argv[i],"-wait")) {
327                                 sleep(6);
328                         }
329                         if (!strcmp(argv[i],"-nolimit")) {
330                                 Config->unlimitcore = true;
331                         }
332                 }
333         }
334
335         strlcpy(Config->MyExecutable,argv[0],MAXBUF);
336         
337         // initialize the lowercase mapping table
338         for (unsigned int cn = 0; cn < 256; cn++)
339                 lowermap[cn] = cn;
340         // lowercase the uppercase chars
341         for (unsigned int cn = 65; cn < 91; cn++)
342                 lowermap[cn] = tolower(cn);
343         // now replace the specific chars for scandanavian comparison
344         lowermap[(unsigned)'['] = '{';
345         lowermap[(unsigned)']'] = '}';
346         lowermap[(unsigned)'\\'] = '|';
347
348
349         OpenLog(argv, argc);
350         Config->ClearStack();
351         Config->Read(true,NULL);
352         CheckRoot();
353         SetupCommandTable();
354         AddServerName(Config->ServerName);
355         CheckDie();
356         BoundPortCount = BindPorts();
357
358         printf("\n");
359         if (!Config->nofork)
360         {
361                 if (DaemonSeed() == ERROR)
362                 {
363                         printf("ERROR: could not go into daemon mode. Shutting down.\n");
364                         Exit(ERROR);
365                 }
366         }
367
368         /* Because of limitations in kqueue on freebsd, we must fork BEFORE we
369          * initialize the socket engine.
370          */
371         SE = new SocketEngine();
372
373         /* We must load the modules AFTER initializing the socket engine, now */
374         LoadAllModules();
375
376         printf("\nInspIRCd is now running!\n");
377
378         return;
379 }
380
381 template<typename T> inline string ConvToStr(const T &in)
382 {
383         stringstream tmp;
384         if (!(tmp << in)) return string();
385         return tmp.str();
386 }
387
388 /* re-allocates a nick in the user_hash after they change nicknames,
389  * returns a pointer to the new user as it may have moved */
390
391 userrec* ReHashNick(char* Old, char* New)
392 {
393         //user_hash::iterator newnick;
394         user_hash::iterator oldnick = clientlist.find(Old);
395
396         log(DEBUG,"ReHashNick: %s %s",Old,New);
397         
398         if (!strcasecmp(Old,New))
399         {
400                 log(DEBUG,"old nick is new nick, skipping");
401                 return oldnick->second;
402         }
403         
404         if (oldnick == clientlist.end()) return NULL; /* doesnt exist */
405
406         log(DEBUG,"ReHashNick: Found hashed nick %s",Old);
407
408         userrec* olduser = oldnick->second;
409         clientlist[New] = olduser;
410         clientlist.erase(oldnick);
411
412         log(DEBUG,"ReHashNick: Nick rehashed as %s",New);
413         
414         return clientlist[New];
415 }
416
417 #ifdef THREADED_DNS
418 void* dns_task(void* arg)
419 {
420         userrec* u = (userrec*)arg;
421         log(DEBUG,"DNS thread for user %s",u->nick);
422         DNS dns1;
423         DNS dns2;
424         std::string host;
425         std::string ip;
426         if (dns1.ReverseLookup(u->ip))
427         {
428                 log(DEBUG,"DNS Step 1");
429                 while (!dns1.HasResult())
430                 {
431                         usleep(100);
432                 }
433                 host = dns1.GetResult();
434                 if (host != "")
435                 {
436                         log(DEBUG,"DNS Step 2: '%s'",host.c_str());
437                         if (dns2.ForwardLookup(host))
438                         {
439                                 while (!dns2.HasResult())
440                                 {
441                                         usleep(100);
442                                 }
443                                 ip = dns2.GetResultIP();
444                                 log(DEBUG,"DNS Step 3 '%s'(%d) '%s'(%d)",ip.c_str(),ip.length(),u->ip,strlen(u->ip));
445                                 if (ip == std::string(u->ip))
446                                 {
447                                         log(DEBUG,"DNS Step 4");
448                                         if (host.length() < 160)
449                                         {
450                                                 log(DEBUG,"DNS Step 5");
451                                                 strcpy(u->host,host.c_str());
452                                                 strcpy(u->dhost,host.c_str());
453                                         }
454                                 }
455                         }
456                 }
457         }
458         u->dns_done = true;
459         return NULL;
460 }
461 #endif
462
463 std::string InspIRCd::GetVersionString()
464 {
465         char versiondata[MAXBUF];
466 #ifdef THREADED_DNS
467         char dnsengine[] = "multithread";
468 #else
469         char dnsengine[] = "singlethread";
470 #endif
471         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);
472         return versiondata;
473 }
474
475
476 bool is_valid_cmd(const char* commandname, int pcnt, userrec * user)
477 {
478         for (unsigned int i = 0; i < cmdlist.size(); i++)
479         {
480                 if (!strcasecmp(cmdlist[i].command,commandname))
481                 {
482                         if (cmdlist[i].handler_function)
483                         {
484                                 if ((pcnt>=cmdlist[i].min_params) && (strcasecmp(cmdlist[i].source,"<core>")))
485                                 {
486                                         if ((strchr(user->modes,cmdlist[i].flags_needed)) || (!cmdlist[i].flags_needed))
487                                         {
488                                                 if (cmdlist[i].flags_needed)
489                                                 {
490                                                         if ((user->HasPermission((char*)commandname)) || (is_uline(user->server)))
491                                                         {
492                                                                 return true;
493                                                         }
494                                                         else
495                                                         {
496                                                                 return false;
497                                                         }
498                                                 }
499                                                 return true;
500                                         }
501                                 }
502                         }
503                 }
504         }
505         return false;
506 }
507
508 // calls a handler function for a command
509
510 void call_handler(const char* commandname,char **parameters, int pcnt, userrec *user)
511 {
512         for (unsigned int i = 0; i < cmdlist.size(); i++)
513         {
514                 if (!strcasecmp(cmdlist[i].command,commandname))
515                 {
516                         if (cmdlist[i].handler_function)
517                         {
518                                 if (pcnt>=cmdlist[i].min_params)
519                                 {
520                                         if ((strchr(user->modes,cmdlist[i].flags_needed)) || (!cmdlist[i].flags_needed))
521                                         {
522                                                 if (cmdlist[i].flags_needed)
523                                                 {
524                                                         if ((user->HasPermission((char*)commandname)) || (is_uline(user->server)))
525                                                         {
526                                                                 cmdlist[i].handler_function(parameters,pcnt,user);
527                                                         }
528                                                 }
529                                                 else
530                                                 {
531                                                         cmdlist[i].handler_function(parameters,pcnt,user);
532                                                 }
533                                         }
534                                 }
535                         }
536                 }
537         }
538 }
539
540
541 void force_nickchange(userrec* user,const char* newnick)
542 {
543         char nick[MAXBUF];
544         int MOD_RESULT = 0;
545         
546         strcpy(nick,"");
547
548         FOREACH_RESULT(OnUserPreNick(user,newnick));
549         if (MOD_RESULT) {
550                 stats->statsCollisions++;
551                 kill_link(user,"Nickname collision");
552                 return;
553         }
554         if (matches_qline(newnick))
555         {
556                 stats->statsCollisions++;
557                 kill_link(user,"Nickname collision");
558                 return;
559         }
560         
561         if (user)
562         {
563                 if (newnick)
564                 {
565                         strncpy(nick,newnick,MAXBUF);
566                 }
567                 if (user->registered == 7)
568                 {
569                         char* pars[1];
570                         pars[0] = nick;
571                         handle_nick(pars,1,user);
572                 }
573         }
574 }
575                                 
576
577 int process_parameters(char **command_p,char *parameters)
578 {
579         int j = 0;
580         int q = strlen(parameters);
581         if (!q)
582         {
583                 /* no parameters, command_p invalid! */
584                 return 0;
585         }
586         if (parameters[0] == ':')
587         {
588                 command_p[0] = parameters+1;
589                 return 1;
590         }
591         if (q)
592         {
593                 if ((strchr(parameters,' ')==NULL) || (parameters[0] == ':'))
594                 {
595                         /* only one parameter */
596                         command_p[0] = parameters;
597                         if (parameters[0] == ':')
598                         {
599                                 if (strchr(parameters,' ') != NULL)
600                                 {
601                                         command_p[0]++;
602                                 }
603                         }
604                         return 1;
605                 }
606         }
607         command_p[j++] = parameters;
608         for (int i = 0; i <= q; i++)
609         {
610                 if (parameters[i] == ' ')
611                 {
612                         command_p[j++] = parameters+i+1;
613                         parameters[i] = '\0';
614                         if (command_p[j-1][0] == ':')
615                         {
616                                 *command_p[j-1]++; /* remove dodgy ":" */
617                                 break;
618                                 /* parameter like this marks end of the sequence */
619                         }
620                 }
621         }
622         return j; /* returns total number of items in the list */
623 }
624
625 void process_command(userrec *user, char* cmd)
626 {
627         char *parameters;
628         char *command;
629         char *command_p[127];
630         char p[MAXBUF], temp[MAXBUF];
631         int j, items, cmd_found;
632
633         for (int i = 0; i < 127; i++)
634                 command_p[i] = NULL;
635
636         if (!user)
637         {
638                 return;
639         }
640         if (!cmd)
641         {
642                 return;
643         }
644         if (!cmd[0])
645         {
646                 return;
647         }
648         
649         int total_params = 0;
650         if (strlen(cmd)>2)
651         {
652                 for (unsigned int q = 0; q < strlen(cmd)-1; q++)
653                 {
654                         if ((cmd[q] == ' ') && (cmd[q+1] == ':'))
655                         {
656                                 total_params++;
657                                 // found a 'trailing', we dont count them after this.
658                                 break;
659                         }
660                         if (cmd[q] == ' ')
661                                 total_params++;
662                 }
663         }
664
665         // another phidjit bug...
666         if (total_params > 126)
667         {
668                 *(strchr(cmd,' ')) = '\0';
669                 WriteServ(user->fd,"421 %s %s :Too many parameters given",user->nick,cmd);
670                 return;
671         }
672
673         strlcpy(temp,cmd,MAXBUF);
674         
675         std::string tmp = cmd;
676         for (int i = 0; i <= MODCOUNT; i++)
677         {
678                 std::string oldtmp = tmp;
679                 modules[i]->OnServerRaw(tmp,true,user);
680                 if (oldtmp != tmp)
681                 {
682                         log(DEBUG,"A Module changed the input string!");
683                         log(DEBUG,"New string: %s",tmp.c_str());
684                         log(DEBUG,"Old string: %s",oldtmp.c_str());
685                         break;
686                 }
687         }
688         strlcpy(cmd,tmp.c_str(),MAXBUF);
689         strlcpy(temp,cmd,MAXBUF);
690
691         if (!strchr(cmd,' '))
692         {
693                 /* no parameters, lets skip the formalities and not chop up
694                  * the string */
695                 log(DEBUG,"About to preprocess command with no params");
696                 items = 0;
697                 command_p[0] = NULL;
698                 parameters = NULL;
699                 for (unsigned int i = 0; i <= strlen(cmd); i++)
700                 {
701                         cmd[i] = toupper(cmd[i]);
702                 }
703                 command = cmd;
704         }
705         else
706         {
707                 strcpy(cmd,"");
708                 j = 0;
709                 /* strip out extraneous linefeeds through mirc's crappy pasting (thanks Craig) */
710                 for (unsigned int i = 0; i < strlen(temp); i++)
711                 {
712                         if ((temp[i] != 10) && (temp[i] != 13) && (temp[i] != 0) && (temp[i] != 7))
713                         {
714                                 cmd[j++] = temp[i];
715                                 cmd[j] = 0;
716                         }
717                 }
718                 /* split the full string into a command plus parameters */
719                 parameters = p;
720                 strcpy(p," ");
721                 command = cmd;
722                 if (strchr(cmd,' '))
723                 {
724                         for (unsigned int i = 0; i <= strlen(cmd); i++)
725                         {
726                                 /* capitalise the command ONLY, leave params intact */
727                                 cmd[i] = toupper(cmd[i]);
728                                 /* are we nearly there yet?! :P */
729                                 if (cmd[i] == ' ')
730                                 {
731                                         command = cmd;
732                                         parameters = cmd+i+1;
733                                         cmd[i] = '\0';
734                                         break;
735                                 }
736                         }
737                 }
738                 else
739                 {
740                         for (unsigned int i = 0; i <= strlen(cmd); i++)
741                         {
742                                 cmd[i] = toupper(cmd[i]);
743                         }
744                 }
745
746         }
747         cmd_found = 0;
748         
749         if (strlen(command)>MAXCOMMAND)
750         {
751                 WriteServ(user->fd,"421 %s %s :Command too long",user->nick,command);
752                 return;
753         }
754         
755         for (unsigned int x = 0; x < strlen(command); x++)
756         {
757                 if (((command[x] < 'A') || (command[x] > 'Z')) && (command[x] != '.'))
758                 {
759                         if (((command[x] < '0') || (command[x]> '9')) && (command[x] != '-'))
760                         {
761                                 if (strchr("@!\"$%^&*(){}[]_=+;:'#~,<>/?\\|`",command[x]))
762                                 {
763                                         stats->statsUnknown++;
764                                         WriteServ(user->fd,"421 %s %s :Unknown command",user->nick,command);
765                                         return;
766                                 }
767                         }
768                 }
769         }
770
771         for (unsigned int i = 0; i != cmdlist.size(); i++)
772         {
773                 if (cmdlist[i].command[0])
774                 {
775                         if (strlen(command)>=(strlen(cmdlist[i].command))) if (!strncmp(command, cmdlist[i].command,MAXCOMMAND))
776                         {
777                                 if (parameters)
778                                 {
779                                         if (parameters[0])
780                                         {
781                                                 items = process_parameters(command_p,parameters);
782                                         }
783                                         else
784                                         {
785                                                 items = 0;
786                                                 command_p[0] = NULL;
787                                         }
788                                 }
789                                 else
790                                 {
791                                         items = 0;
792                                         command_p[0] = NULL;
793                                 }
794                                 
795                                 if (user)
796                                 {
797                                         /* activity resets the ping pending timer */
798                                         user->nping = TIME + user->pingmax;
799                                         if ((items) < cmdlist[i].min_params)
800                                         {
801                                                 log(DEBUG,"process_command: not enough parameters: %s %s",user->nick,command);
802                                                 WriteServ(user->fd,"461 %s %s :Not enough parameters",user->nick,command);
803                                                 return;
804                                         }
805                                         if ((!strchr(user->modes,cmdlist[i].flags_needed)) && (cmdlist[i].flags_needed))
806                                         {
807                                                 log(DEBUG,"process_command: permission denied: %s %s",user->nick,command);
808                                                 WriteServ(user->fd,"481 %s :Permission Denied- You do not have the required operator privilages",user->nick);
809                                                 cmd_found = 1;
810                                                 return;
811                                         }
812                                         if ((cmdlist[i].flags_needed) && (!user->HasPermission(command)))
813                                         {
814                                                 log(DEBUG,"process_command: permission denied: %s %s",user->nick,command);
815                                                 WriteServ(user->fd,"481 %s :Permission Denied- Oper type %s does not have access to command %s",user->nick,user->oper,command);
816                                                 cmd_found = 1;
817                                                 return;
818                                         }
819                                         /* if the command isnt USER, PASS, or NICK, and nick is empty,
820                                          * deny command! */
821                                         if ((strncmp(command,"USER",4)) && (strncmp(command,"NICK",4)) && (strncmp(command,"PASS",4)))
822                                         {
823                                                 if ((!isnick(user->nick)) || (user->registered != 7))
824                                                 {
825                                                         log(DEBUG,"process_command: not registered: %s %s",user->nick,command);
826                                                         WriteServ(user->fd,"451 %s :You have not registered",command);
827                                                         return;
828                                                 }
829                                         }
830                                         if ((user->registered == 7) && (!strchr(user->modes,'o')))
831                                         {
832                                                 std::stringstream dcmds(Config->DisabledCommands);
833                                                 while (!dcmds.eof())
834                                                 {
835                                                         std::string thiscmd;
836                                                         dcmds >> thiscmd;
837                                                         if (!strcasecmp(thiscmd.c_str(),command))
838                                                         {
839                                                                 // command is disabled!
840                                                                 WriteServ(user->fd,"421 %s %s :This command has been disabled.",user->nick,command);
841                                                                 return;
842                                                         }
843                                                 }
844                                         }
845                                         if ((user->registered == 7) || (!strncmp(command,"USER",4)) || (!strncmp(command,"NICK",4)) || (!strncmp(command,"PASS",4)))
846                                         {
847                                                 if (cmdlist[i].handler_function)
848                                                 {
849                                                         
850                                                         /* ikky /stats counters */
851                                                         if (temp)
852                                                         {
853                                                                 cmdlist[i].use_count++;
854                                                                 cmdlist[i].total_bytes+=strlen(temp);
855                                                         }
856
857                                                         int MOD_RESULT = 0;
858                                                         FOREACH_RESULT(OnPreCommand(command,command_p,items,user));
859                                                         if (MOD_RESULT == 1) {
860                                                                 return;
861                                                         }
862
863                                                         /* WARNING: nothing may come after the
864                                                          * command handler call, as the handler
865                                                          * may free the user structure! */
866
867                                                         cmdlist[i].handler_function(command_p,items,user);
868                                                 }
869                                                 return;
870                                         }
871                                         else
872                                         {
873                                                 WriteServ(user->fd,"451 %s :You have not registered",command);
874                                                 return;
875                                         }
876                                 }
877                                 cmd_found = 1;
878                         }
879                 }
880         }
881         if ((!cmd_found) && (user))
882         {
883                 stats->statsUnknown++;
884                 WriteServ(user->fd,"421 %s %s :Unknown command",user->nick,command);
885         }
886 }
887
888 bool removecommands(const char* source)
889 {
890         bool go_again = true;
891         while (go_again)
892         {
893                 go_again = false;
894                 for (std::deque<command_t>::iterator i = cmdlist.begin(); i != cmdlist.end(); i++)
895                 {
896                         if (!strcmp(i->source,source))
897                         {
898                                 log(DEBUG,"removecommands(%s) Removing dependent command: %s",i->source,i->command);
899                                 cmdlist.erase(i);
900                                 go_again = true;
901                                 break;
902                         }
903                 }
904         }
905         return true;
906 }
907
908
909 void process_buffer(const char* cmdbuf,userrec *user)
910 {
911         if (!user)
912         {
913                 log(DEFAULT,"*** BUG *** process_buffer was given an invalid parameter");
914                 return;
915         }
916         char cmd[MAXBUF];
917         if (!cmdbuf)
918         {
919                 log(DEFAULT,"*** BUG *** process_buffer was given an invalid parameter");
920                 return;
921         }
922         if (!cmdbuf[0])
923         {
924                 return;
925         }
926         while (*cmdbuf == ' ') cmdbuf++; // strip leading spaces
927
928         strlcpy(cmd,cmdbuf,MAXBUF);
929         if (!cmd[0])
930         {
931                 return;
932         }
933         int sl = strlen(cmd)-1;
934         if ((cmd[sl] == 13) || (cmd[sl] == 10))
935         {
936                 cmd[sl] = '\0';
937         }
938         sl = strlen(cmd)-1;
939         if ((cmd[sl] == 13) || (cmd[sl] == 10))
940         {
941                 cmd[sl] = '\0';
942         }
943         sl = strlen(cmd)-1;
944         while (cmd[sl] == ' ') // strip trailing spaces
945         {
946                 cmd[sl] = '\0';
947                 sl = strlen(cmd)-1;
948         }
949
950         if (!cmd[0])
951         {
952                 return;
953         }
954         log(DEBUG,"CMDIN: %s %s",user->nick,cmd);
955         tidystring(cmd);
956         if ((user) && (cmd))
957         {
958                 process_command(user,cmd);
959         }
960 }
961
962 char* InspIRCd::ModuleError()
963 {
964         return MODERR;
965 }
966
967 void InspIRCd::erase_factory(int j)
968 {
969         int v = 0;
970         for (std::vector<ircd_module*>::iterator t = factory.begin(); t != factory.end(); t++)
971         {
972                 if (v == j)
973                 {
974                         factory.erase(t);
975                         factory.push_back(NULL);
976                         return;
977                 }
978                 v++;
979         }
980 }
981
982 void InspIRCd::erase_module(int j)
983 {
984         int v1 = 0;
985         for (std::vector<Module*>::iterator m = modules.begin(); m!= modules.end(); m++)
986         {
987                 if (v1 == j)
988                 {
989                         delete *m;
990                         modules.erase(m);
991                         modules.push_back(NULL);
992                         break;
993                 }
994                 v1++;
995         }
996         int v2 = 0;
997         for (std::vector<std::string>::iterator v = Config->module_names.begin(); v != Config->module_names.end(); v++)
998         {
999                 if (v2 == j)
1000                 {
1001                        Config->module_names.erase(v);
1002                        break;
1003                 }
1004                 v2++;
1005         }
1006
1007 }
1008
1009 bool InspIRCd::UnloadModule(const char* filename)
1010 {
1011         std::string filename_str = filename;
1012         for (unsigned int j = 0; j != Config->module_names.size(); j++)
1013         {
1014                 if (Config->module_names[j] == filename_str)
1015                 {
1016                         if (modules[j]->GetVersion().Flags & VF_STATIC)
1017                         {
1018                                 log(DEFAULT,"Failed to unload STATIC module %s",filename);
1019                                 snprintf(MODERR,MAXBUF,"Module not unloadable (marked static)");
1020                                 return false;
1021                         }
1022                         /* Give the module a chance to tidy out all its metadata */
1023                         for (chan_hash::iterator c = chanlist.begin(); c != chanlist.end(); c++)
1024                         {
1025                                 modules[j]->OnCleanup(TYPE_CHANNEL,c->second);
1026                         }
1027                         for (user_hash::iterator u = clientlist.begin(); u != clientlist.end(); u++)
1028                         {
1029                                 modules[j]->OnCleanup(TYPE_USER,u->second);
1030                         }
1031                         FOREACH_MOD OnUnloadModule(modules[j],Config->module_names[j]);
1032                         // found the module
1033                         log(DEBUG,"Deleting module...");
1034                         erase_module(j);
1035                         log(DEBUG,"Erasing module entry...");
1036                         erase_factory(j);
1037                         log(DEBUG,"Removing dependent commands...");
1038                         removecommands(filename);
1039                         log(DEFAULT,"Module %s unloaded",filename);
1040                         MODCOUNT--;
1041                         return true;
1042                 }
1043         }
1044         log(DEFAULT,"Module %s is not loaded, cannot unload it!",filename);
1045         snprintf(MODERR,MAXBUF,"Module not loaded");
1046         return false;
1047 }
1048
1049 bool InspIRCd::LoadModule(const char* filename)
1050 {
1051         char modfile[MAXBUF];
1052 #ifdef STATIC_LINK
1053         snprintf(modfile,MAXBUF,"%s",filename);
1054 #else
1055         snprintf(modfile,MAXBUF,"%s/%s",Config->ModPath,filename);
1056 #endif
1057         std::string filename_str = filename;
1058 #ifndef STATIC_LINK
1059         if (!DirValid(modfile))
1060         {
1061                 log(DEFAULT,"Module %s is not within the modules directory.",modfile);
1062                 snprintf(MODERR,MAXBUF,"Module %s is not within the modules directory.",modfile);
1063                 return false;
1064         }
1065 #endif
1066         log(DEBUG,"Loading module: %s",modfile);
1067 #ifndef STATIC_LINK
1068         if (FileExists(modfile))
1069         {
1070 #endif
1071                 for (unsigned int j = 0; j < Config->module_names.size(); j++)
1072                 {
1073                         if (Config->module_names[j] == filename_str)
1074                         {
1075                                 log(DEFAULT,"Module %s is already loaded, cannot load a module twice!",modfile);
1076                                 snprintf(MODERR,MAXBUF,"Module already loaded");
1077                                 return false;
1078                         }
1079                 }
1080                 ircd_module* a = new ircd_module(modfile);
1081                 factory[MODCOUNT+1] = a;
1082                 if (factory[MODCOUNT+1]->LastError())
1083                 {
1084                         log(DEFAULT,"Unable to load %s: %s",modfile,factory[MODCOUNT+1]->LastError());
1085                         snprintf(MODERR,MAXBUF,"Loader/Linker error: %s",factory[MODCOUNT+1]->LastError());
1086                         MODCOUNT--;
1087                         return false;
1088                 }
1089                 if (factory[MODCOUNT+1]->factory)
1090                 {
1091                         Module* m = factory[MODCOUNT+1]->factory->CreateModule(MyServer);
1092                         modules[MODCOUNT+1] = m;
1093                         /* save the module and the module's classfactory, if
1094                          * this isnt done, random crashes can occur :/ */
1095                         Config->module_names.push_back(filename);
1096                 }
1097                 else
1098                 {
1099                         log(DEFAULT,"Unable to load %s",modfile);
1100                         snprintf(MODERR,MAXBUF,"Factory function failed!");
1101                         return false;
1102                 }
1103 #ifndef STATIC_LINK
1104         }
1105         else
1106         {
1107                 log(DEFAULT,"InspIRCd: startup: Module Not Found %s",modfile);
1108                 snprintf(MODERR,MAXBUF,"Module file could not be found");
1109                 return false;
1110         }
1111 #endif
1112         MODCOUNT++;
1113         FOREACH_MOD OnLoadModule(modules[MODCOUNT],filename_str);
1114         return true;
1115 }
1116
1117 int InspIRCd::Run()
1118 {
1119         bool expire_run = false;
1120         std::vector<int> activefds;
1121         int incomingSockfd;
1122         int in_port;
1123         userrec* cu = NULL;
1124         InspSocket* s = NULL;
1125         InspSocket* s_del = NULL;
1126         char* target;
1127         unsigned int numberactive;
1128         sockaddr_in sock_us;     // our port number
1129         socklen_t uslen;         // length of our port number
1130
1131         if (!Config->nofork)
1132         {
1133                 freopen("/dev/null","w",stdout);
1134                 freopen("/dev/null","w",stderr);
1135         }
1136
1137         /* Add the listening sockets used for client inbound connections
1138          * to the socket engine
1139          */
1140         for (int count = 0; count < BoundPortCount; count++)
1141                 SE->AddFd(openSockfd[count],true,X_LISTEN);
1142
1143         WritePID(Config->PID);
1144
1145         /* main loop, this never returns */
1146         for (;;)
1147         {
1148                 /* time() seems to be a pretty expensive syscall, so avoid calling it too much.
1149                  * Once per loop iteration is pleanty.
1150                  */
1151                 OLDTIME = TIME;
1152                 TIME = time(NULL);
1153
1154                 /* Run background module timers every few seconds
1155                  * (the docs say modules shouldnt rely on accurate
1156                  * timing using this event, so we dont have to
1157                  * time this exactly).
1158                  */
1159                 if (((TIME % 8) == 0) && (!expire_run))
1160                 {
1161                         expire_lines();
1162                         FOREACH_MOD OnBackgroundTimer(TIME);
1163                         expire_run = true;
1164                         continue;
1165                 }
1166                 if ((TIME % 8) == 1)
1167                         expire_run = false;
1168                 
1169                 /* Once a second, do the background processing */
1170                 if (TIME != OLDTIME)
1171                         while (DoBackgroundUserStuff(TIME));
1172
1173                 /* Call the socket engine to wait on the active
1174                  * file descriptors. The socket engine has everything's
1175                  * descriptors in its list... dns, modules, users,
1176                  * servers... so its nice and easy, just one call.
1177                  */
1178                 SE->Wait(activefds);
1179
1180                 /**
1181                  * Now process each of the fd's. For users, we have a fast
1182                  * lookup table which can find a user by file descriptor, so
1183                  * processing them by fd isnt expensive. If we have a lot of
1184                  * listening ports or module sockets though, things could get
1185                  * ugly.
1186                  */
1187                 numberactive = activefds.size();
1188                 for (unsigned int activefd = 0; activefd < numberactive; activefd++)
1189                 {
1190                         int socket_type = SE->GetType(activefds[activefd]);
1191                         switch (socket_type)
1192                         {
1193                                 case X_ESTAB_CLIENT:
1194
1195                                         cu = fd_ref_table[activefds[activefd]];
1196                                         if (cu)
1197                                                 ProcessUser(cu);
1198
1199                                 break;
1200
1201                                 case X_ESTAB_MODULE:
1202
1203                                         /* Process module-owned sockets.
1204                                          * Modules are encouraged to inherit their sockets from
1205                                          * InspSocket so we can process them neatly like this.
1206                                          */
1207                                         s = socket_ref[activefds[activefd]];
1208
1209                                         if ((s) && (!s->Poll()))
1210                                         {
1211                                                 log(DEBUG,"Socket poll returned false, close and bail");
1212                                                 SE->DelFd(s->GetFd());
1213                                                 for (std::vector<InspSocket*>::iterator a = module_sockets.begin(); a < module_sockets.end(); a++)
1214                                                 {
1215                                                         s_del = (InspSocket*)*a;
1216                                                         if ((s_del) && (s_del->GetFd() == activefds[activefd]))
1217                                                         {
1218                                                                 module_sockets.erase(a);
1219                                                                 break;
1220                                                         }
1221                                                 }
1222                                                 s->Close();
1223                                                 delete s;
1224                                         }
1225
1226                                 break;
1227
1228                                 case X_ESTAB_DNS:
1229
1230                                         /* When we are using single-threaded dns,
1231                                          * the sockets for dns end up in our mainloop.
1232                                          * When we are using multi-threaded dns,
1233                                          * each thread has its own basic poll() loop
1234                                          * within it, making them 'fire and forget'
1235                                          * and independent of the mainloop.
1236                                          */
1237 #ifndef THREADED_DNS
1238                                         dns_poll(activefds[activefd]);
1239 #endif
1240                                 break;
1241                                 
1242                                 case X_LISTEN:
1243
1244                                         /* It's a listener */
1245                                         uslen = sizeof(sock_us);
1246                                         length = sizeof(client);
1247                                         incomingSockfd = accept (activefds[activefd],(struct sockaddr*)&client,&length);
1248                                         if (!getsockname(incomingSockfd,(sockaddr*)&sock_us,&uslen))
1249                                         {
1250                                                 in_port = ntohs(sock_us.sin_port);
1251                                                 log(DEBUG,"Accepted socket %d",incomingSockfd);
1252                                                 target = (char*)inet_ntoa(client.sin_addr);
1253                                                 /* Years and years ago, we used to resolve here
1254                                                  * using gethostbyaddr(). That is sucky and we
1255                                                  * don't do that any more...
1256                                                  */
1257                                                 if (incomingSockfd >= 0)
1258                                                 {
1259                                                         FOREACH_MOD OnRawSocketAccept(incomingSockfd, target, in_port);
1260                                                         stats->statsAccept++;
1261                                                         AddClient(incomingSockfd, target, in_port, false, target);
1262                                                         log(DEBUG,"Adding client on port %lu fd=%lu",(unsigned long)in_port,(unsigned long)incomingSockfd);
1263                                                 }
1264                                                 else
1265                                                 {
1266                                                         WriteOpers("*** WARNING: accept() failed on port %lu (%s)",(unsigned long)in_port,target);
1267                                                         log(DEBUG,"accept failed: %lu",(unsigned long)in_port);
1268                                                         stats->statsRefused++;
1269                                                 }
1270                                         }
1271                                         else
1272                                         {
1273                                                 log(DEBUG,"Couldnt look up the port number for fd %lu (OS BROKEN?!)",incomingSockfd);
1274                                                 shutdown(incomingSockfd,2);
1275                                                 close(incomingSockfd);
1276                                         }
1277                                 break;
1278
1279                                 default:
1280                                         /* Something went wrong if we're in here.
1281                                          * In fact, so wrong, im not quite sure
1282                                          * what we would do, so for now, its going
1283                                          * to safely do bugger all.
1284                                          */
1285                                 break;
1286                         }
1287                 }
1288
1289         }
1290         /* This is never reached -- we hope! */
1291         return 0;
1292 }
1293
1294 /**********************************************************************************/
1295
1296 /**
1297  * An ircd in four lines! bwahahaha. ahahahahaha. ahahah *cough*.
1298  */
1299
1300 int main(int argc, char** argv)
1301 {
1302         ServerInstance = new InspIRCd(argc, argv);
1303         ServerInstance->Run();
1304         delete ServerInstance;
1305         return 0;
1306 }
1307