]> git.netwichtig.de Git - user/henk/code/inspircd.git/blob - src/inspircd_io.cpp
Renamed to chanrec::modes
[user/henk/code/inspircd.git] / src / inspircd_io.cpp
1 /*       +------------------------------------+
2  *       | Inspire Internet Relay Chat Daemon |
3  *       +------------------------------------+
4  *
5  *  InspIRCd is copyright (C) 2002-2006 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 using namespace std;
18
19 #include "inspircd_config.h"
20 #include <sys/time.h>
21 #include <sys/resource.h>
22 #include <sys/types.h>
23 #include <string>
24 #include <unistd.h>
25 #include <sstream>
26 #include <iostream>
27 #include <fstream>
28 #include "message.h"
29 #include "inspircd.h"
30 #include "inspircd_io.h"
31 #include "inspstring.h"
32 #include "helperfuncs.h"
33 #include "userprocess.h"
34 #include "xline.h"
35
36 extern ServerConfig *Config;
37 extern InspIRCd* ServerInstance;
38 extern int openSockfd[MAXSOCKS];
39 extern time_t TIME;
40
41 extern int MODCOUNT;
42 extern std::vector<Module*> modules;
43 extern std::vector<ircd_module*> factory;
44
45 std::vector<std::string> old_module_names, new_module_names, added_modules, removed_modules;
46
47 ServerConfig::ServerConfig()
48 {
49         this->ClearStack();
50         *TempDir = *ServerName = *Network = *ServerDesc = *AdminName = '\0';
51         *HideWhoisServer = *AdminEmail = *AdminNick = *diepass = *restartpass = '\0';
52         *CustomVersion = *motd = *rules = *PrefixQuit = *DieValue = *DNSServer = '\0';
53         *OperOnlyStats = *ModPath = *MyExecutable = *DisabledCommands = *PID = '\0';
54         log_file = NULL;
55         nofork = HideBans = HideSplits = unlimitcore = false;
56         AllowHalfop = true;
57         dns_timeout = DieDelay = 5;
58         MaxTargets = 20;
59         NetBufferSize = 10240;
60         SoftLimit = MAXCLIENTS;
61         MaxConn = SOMAXCONN;
62         MaxWhoResults = 100;
63         debugging = 0;
64         LogLevel = DEFAULT;
65         maxbans.clear();
66 }
67
68 void ServerConfig::ClearStack()
69 {
70         include_stack.clear();
71 }
72
73 Module* ServerConfig::GetIOHook(int port)
74 {
75         std::map<int,Module*>::iterator x = IOHookModule.find(port);
76         return (x != IOHookModule.end() ? x->second : NULL);
77 }
78
79 bool ServerConfig::AddIOHook(int port, Module* iomod)
80 {
81         if (!GetIOHook(port))
82         {
83                 IOHookModule[port] = iomod;
84                 return true;
85         }
86         else
87         {
88                 ModuleException err("Port already hooked by another module");
89                 throw(err);
90                 return false;
91         }
92 }
93
94 bool ServerConfig::DelIOHook(int port)
95 {
96         std::map<int,Module*>::iterator x = IOHookModule.find(port);
97         if (x != IOHookModule.end())
98         {
99                 IOHookModule.erase(x);
100                 return true;
101         }
102         return false;
103 }
104
105 bool ServerConfig::CheckOnce(char* tag, bool bail, userrec* user)
106 {
107         int count = ConfValueEnum(tag,&Config->config_f);
108         if (count > 1)
109         {
110                 if (bail)
111                 {
112                         printf("There were errors in your configuration:\nYou have more than one <%s> tag, this is not permitted.\n",tag);
113                         Exit(0);
114                 }
115                 else
116                 {
117                         if (user)
118                         {
119                                 WriteServ(user->fd,"There were errors in your configuration:");
120                                 WriteServ(user->fd,"You have more than one <%s> tag, this is not permitted.\n",tag);
121                         }
122                         else
123                         {
124                                 WriteOpers("There were errors in the configuration file:");
125                                 WriteOpers("You have more than one <%s> tag, this is not permitted.\n",tag);
126                         }
127                 }
128                 return false;
129         }
130         if (count < 1)
131         {
132                 if (bail)
133                 {
134                         printf("There were errors in your configuration:\nYou have not defined a <%s> tag, this is required.\n",tag);
135                         Exit(0);
136                 }
137                 else
138                 {
139                         if (user)
140                         {
141                                 WriteServ(user->fd,"There were errors in your configuration:");
142                                 WriteServ(user->fd,"You have not defined a <%s> tag, this is required.",tag);
143                         }
144                         else
145                         {
146                                 WriteOpers("There were errors in the configuration file:");
147                                 WriteOpers("You have not defined a <%s> tag, this is required.",tag);
148                         }
149                 }
150                 return false;
151         }
152         return true;
153 }
154
155 bool NoValidation(const char* tag, const char* value, void* data)
156 {
157         log(DEBUG,"No validation for <%s:%s>",tag,value);
158         return true;
159 }
160
161 bool ValidateTempDir(const char* tag, const char* value, void* data)
162 {
163         char* x = (char*)data;
164         if (!*x)
165                 strlcpy(x,"/tmp",1024);
166         return true;
167 }
168  
169 bool ValidateMaxTargets(const char* tag, const char* value, void* data)
170 {
171         int* x = (int*)data;
172         if ((*x < 0) || (*x > 31))
173         {
174                 log(DEFAULT,"WARNING: <options:maxtargets> value is greater than 31 or less than 0, set to 20.");
175                 *x = 20;
176         }
177         return true;
178 }
179
180 bool ValidateSoftLimit(const char* tag, const char* value, void* data)
181 {
182         int* x = (int*)data;    
183         if ((*x < 1) || (*x > MAXCLIENTS))
184         {
185                 log(DEFAULT,"WARNING: <options:softlimit> value is greater than %d or less than 0, set to %d.",MAXCLIENTS,MAXCLIENTS);
186                 *x = MAXCLIENTS;
187         }
188         return true;
189 }
190
191 bool ValidateMaxConn(const char* tag, const char* value, void* data)
192 {
193         int* x = (int*)data;    
194         if (*x > SOMAXCONN)
195                 log(DEFAULT,"WARNING: <options:somaxconn> value may be higher than the system-defined SOMAXCONN value!");
196         if (!*x)
197                 *x = SOMAXCONN;
198         return true;
199 }
200
201 bool ValidateDnsTimeout(const char* tag, const char* value, void* data)
202 {
203         int* x = (int*)data;
204         if (!*x)
205                 *x = 5;
206         return true;
207 }
208
209 bool ValidateDnsServer(const char* tag, const char* value, void* data)
210 {
211         char* x = (char*)data;
212         if (!*x)
213         {
214                 // attempt to look up their nameserver from /etc/resolv.conf
215                 log(DEFAULT,"WARNING: <dns:server> not defined, attempting to find working server in /etc/resolv.conf...");
216                 ifstream resolv("/etc/resolv.conf");
217                 std::string nameserver;
218                 bool found_server = false;
219
220                 if (resolv.is_open())
221                 {
222                         while (resolv >> nameserver)
223                         {
224                                 if ((nameserver == "nameserver") && (!found_server))
225                                 {
226                                         resolv >> nameserver;
227                                         strlcpy(x,nameserver.c_str(),MAXBUF);
228                                         found_server = true;
229                                         log(DEFAULT,"<dns:server> set to '%s' as first resolver in /etc/resolv.conf.",nameserver.c_str());
230                                 }
231                         }
232
233                         if (!found_server)
234                         {
235                                 log(DEFAULT,"/etc/resolv.conf contains no viable nameserver entries! Defaulting to nameserver '127.0.0.1'!");
236                                 strlcpy(x,"127.0.0.1",MAXBUF);
237                         }
238                 }
239                 else
240                 {
241                         log(DEFAULT,"/etc/resolv.conf can't be opened! Defaulting to nameserver '127.0.0.1'!");
242                         strlcpy(x,"127.0.0.1",MAXBUF);
243                 }
244         }
245         return true;
246 }
247
248 bool ValidateModPath(const char* tag, const char* value, void* data)
249 {
250         char* x = (char*)data;  
251         if (!*x)
252                 strlcpy(x,MOD_PATH,MAXBUF);
253         return true;
254 }
255
256
257 bool ValidateServerName(const char* tag, const char* value, void* data)
258 {
259         char* x = (char*)data;
260         if (!strchr(x,'.'))
261         {
262                 log(DEFAULT,"WARNING: <server:name> '%s' is not a fully-qualified domain name. Changed to '%s%c'",x,x,'.');
263                 charlcat(x,'.',MAXBUF);
264         }
265         strlower(x);
266         return true;
267 }
268
269 bool ValidateNetBufferSize(const char* tag, const char* value, void* data)
270 {
271         if ((!Config->NetBufferSize) || (Config->NetBufferSize > 65535) || (Config->NetBufferSize < 1024))
272         {
273                 log(DEFAULT,"No NetBufferSize specified or size out of range, setting to default of 10240.");
274                 Config->NetBufferSize = 10240;
275         }
276         return true;
277 }
278
279 bool ValidateMaxWho(const char* tag, const char* value, void* data)
280 {
281         if ((!Config->MaxWhoResults) || (Config->MaxWhoResults > 65535) || (Config->MaxWhoResults < 1))
282         {
283                 log(DEFAULT,"No MaxWhoResults specified or size out of range, setting to default of 128.");
284                 Config->MaxWhoResults = 128;
285         }
286         return true;
287 }
288
289 bool ValidateLogLevel(const char* tag, const char* value, void* data)
290 {
291         const char* dbg = (const char*)data;
292         Config->LogLevel = DEFAULT;
293         if (!strcmp(dbg,"debug"))
294         {
295                 Config->LogLevel = DEBUG;
296                 Config->debugging = 1;
297         }
298         else if (!strcmp(dbg,"verbose"))
299                 Config->LogLevel = VERBOSE;
300         else if (!strcmp(dbg,"default"))
301                 Config->LogLevel = DEFAULT;
302         else if (!strcmp(dbg,"sparse"))
303                 Config->LogLevel = SPARSE;
304         else if (!strcmp(dbg,"none"))
305                 Config->LogLevel = NONE;
306         return true;
307 }
308
309 bool ValidateMotd(const char* tag, const char* value, void* data)
310 {
311         readfile(Config->MOTD,Config->motd);
312         return true;
313 }
314
315 bool ValidateRules(const char* tag, const char* value, void* data)
316 {
317         readfile(Config->RULES,Config->rules);
318         return true;
319 }
320
321 /* Callback called before processing the first <connect> tag
322  */
323 bool InitConnect(const char* tag)
324 {
325         log(DEFAULT,"Reading connect classes...");
326         Config->Classes.clear();
327         return true;
328 }
329
330 /* Callback called to process a single <connect> tag
331  */
332 bool DoConnect(const char* tag, char** entries, void** values, int* types)
333 {
334         ConnectClass c;
335         char* allow = (char*)values[0]; /* Yeah, there are a lot of values. Live with it. */
336         char* deny = (char*)values[1];
337         char* password = (char*)values[2];
338         int* timeout = (int*)values[3];
339         int* pingfreq = (int*)values[4];
340         int* flood = (int*)values[5];
341         int* threshold = (int*)values[6];
342         int* sendq = (int*)values[7];
343         int* recvq = (int*)values[8];
344         int* localmax = (int*)values[9];
345         int* globalmax = (int*)values[10];
346
347         if (*allow)
348         {
349                 c.host = allow;
350                 c.type = CC_ALLOW;
351                 c.pass = password;
352                 c.registration_timeout = *timeout;
353                 c.pingtime = *pingfreq;
354                 c.flood = *flood;
355                 c.threshold = *threshold;
356                 c.sendqmax = *sendq;
357                 c.recvqmax = *recvq;
358                 c.maxlocal = *localmax;
359                 c.maxglobal = *globalmax;
360
361
362                 if (c.maxlocal == 0)
363                         c.maxlocal = 3;
364                 if (c.maxglobal == 0)
365                         c.maxglobal = 3;
366                 if (c.threshold == 0)
367                 {
368                         c.threshold = 1;
369                         c.flood = 999;
370                         log(DEFAULT,"Warning: Connect allow line '%s' has no flood/threshold settings. Setting this tag to 999 lines in 1 second.",c.host.c_str());
371                 }
372                 if (c.sendqmax == 0)
373                         c.sendqmax = 262114;
374                 if (c.recvqmax == 0)
375                         c.recvqmax = 4096;
376                 if (c.registration_timeout == 0)
377                         c.registration_timeout = 90;
378                 if (c.pingtime == 0)
379                         c.pingtime = 120;
380                 Config->Classes.push_back(c);
381         }
382         else
383         {
384                 c.host = deny;
385                 c.type = CC_DENY;
386                 Config->Classes.push_back(c);
387                 log(DEBUG,"Read connect class type DENY, host=%s",deny);
388         }
389
390         return true;
391 }
392
393 /* Callback called when there are no more <connect> tags
394  */
395 bool DoneConnect(const char* tag)
396 {
397         log(DEBUG,"DoneConnect called for tag: %s",tag);
398         return true;
399 }
400
401 /* Callback called before processing the first <uline> tag
402  */
403 bool InitULine(const char* tag)
404 {
405         Config->ulines.clear();
406         return true;
407 }
408
409 /* Callback called to process a single <uline> tag
410  */
411 bool DoULine(const char* tag, char** entries, void** values, int* types)
412 {
413         char* server = (char*)values[0];
414         log(DEBUG,"Read ULINE '%s'",server);
415         Config->ulines.push_back(server);
416         return true;
417 }
418
419 /* Callback called when there are no more <uline> tags
420  */
421 bool DoneULine(const char* tag)
422 {
423         return true;
424 }
425
426 /* Callback called before processing the first <module> tag
427  */
428 bool InitModule(const char* tag)
429 {
430         old_module_names.clear();
431         new_module_names.clear();
432         added_modules.clear();
433         removed_modules.clear();
434         for (std::vector<std::string>::iterator t = Config->module_names.begin(); t != Config->module_names.end(); t++)
435         {
436                 old_module_names.push_back(*t);
437         }
438         return true;
439 }
440
441 /* Callback called to process a single <module> tag
442  */
443 bool DoModule(const char* tag, char** entries, void** values, int* types)
444 {
445         char* modname = (char*)values[0];
446         new_module_names.push_back(modname);
447         return true;
448 }
449
450 /* Callback called when there are no more <module> tags
451  */
452 bool DoneModule(const char* tag)
453 {
454         // now create a list of new modules that are due to be loaded
455         // and a seperate list of modules which are due to be unloaded
456         for (std::vector<std::string>::iterator _new = new_module_names.begin(); _new != new_module_names.end(); _new++)
457         {
458                 bool added = true;
459
460                 for (std::vector<std::string>::iterator old = old_module_names.begin(); old != old_module_names.end(); old++)
461                 {
462                         if (*old == *_new)
463                                 added = false;
464                 }
465
466                 if (added)
467                         added_modules.push_back(*_new);
468         }
469
470         for (std::vector<std::string>::iterator oldm = old_module_names.begin(); oldm != old_module_names.end(); oldm++)
471         {
472                 bool removed = true;
473                 for (std::vector<std::string>::iterator newm = new_module_names.begin(); newm != new_module_names.end(); newm++)
474                 {
475                         if (*newm == *oldm)
476                                 removed = false;
477                 }
478
479                 if (removed)
480                         removed_modules.push_back(*oldm);
481         }
482         return true;
483 }
484
485 /* Callback called before processing the first <banlist> tag
486  */
487 bool InitMaxBans(const char* tag)
488 {
489         Config->maxbans.clear();
490         return true;
491 }
492
493 /* Callback called to process a single <banlist> tag
494  */
495 bool DoMaxBans(const char* tag, char** entries, void** values, int* types)
496 {
497         char* channel = (char*)values[0];
498         int* limit = (int*)values[1];
499         Config->maxbans[channel] = *limit;
500         return true;
501 }
502
503 /* Callback called when there are no more <banlist> tags.
504  */
505 bool DoneMaxBans(const char* tag)
506 {
507         return true;
508 }
509
510 void ServerConfig::Read(bool bail, userrec* user)
511 {
512         char debug[MAXBUF];             /* Temporary buffer for debugging value */
513         char dataline[1024];            /* Temporary buffer for error output */
514         char* convert;                  /* Temporary buffer used for reading singular values into */
515         char* data[12];                 /* Temporary buffers for reading multiple occurance tags into */
516         void* ptr[12];                  /* Temporary pointers for passing to callbacks */
517         int r_i[12];                    /* Temporary array for casting */
518         int rem = 0, add = 0;           /* Number of modules added, number of modules removed */
519         std::stringstream errstr;       /* String stream containing the error output */
520
521         /* These tags MUST occur and must ONLY occur once in the config file */
522         static char* Once[] = { "server", "admin", "files", "power", "options", "pid", NULL };
523
524         /* These tags can occur ONCE or not at all */
525         static InitialConfig Values[] = {
526                 {"options",     "softlimit",            &this->SoftLimit,               DT_INTEGER, ValidateSoftLimit},
527                 {"options",     "somaxconn",            &this->MaxConn,                 DT_INTEGER, ValidateMaxConn},
528                 {"server",      "name",                 &this->ServerName,              DT_CHARPTR, ValidateServerName},
529                 {"server",      "description",          &this->ServerDesc,              DT_CHARPTR, NoValidation},
530                 {"server",      "network",              &this->Network,                 DT_CHARPTR, NoValidation},
531                 {"admin",       "name",                 &this->AdminName,               DT_CHARPTR, NoValidation},
532                 {"admin",       "email",                &this->AdminEmail,              DT_CHARPTR, NoValidation},
533                 {"admin",       "nick",                 &this->AdminNick,               DT_CHARPTR, NoValidation},
534                 {"files",       "motd",                 &this->motd,                    DT_CHARPTR, ValidateMotd},
535                 {"files",       "rules",                &this->rules,                   DT_CHARPTR, ValidateRules},
536                 {"power",       "diepass",              &this->diepass,                 DT_CHARPTR, NoValidation},      
537                 {"power",       "pauseval",             &this->DieDelay,                DT_INTEGER, NoValidation},
538                 {"power",       "restartpass",          &this->restartpass,             DT_CHARPTR, NoValidation},
539                 {"options",     "prefixquit",           &this->PrefixQuit,              DT_CHARPTR, NoValidation},
540                 {"die",         "value",                &this->DieValue,                DT_CHARPTR, NoValidation},
541                 {"options",     "loglevel",             &debug,                         DT_CHARPTR, ValidateLogLevel},
542                 {"options",     "netbuffersize",        &this->NetBufferSize,           DT_INTEGER, ValidateNetBufferSize},
543                 {"options",     "maxwho",               &this->MaxWhoResults,           DT_INTEGER, ValidateMaxWho},
544                 {"options",     "allowhalfop",          &this->AllowHalfop,             DT_BOOLEAN, NoValidation},
545                 {"dns",         "server",               &this->DNSServer,               DT_CHARPTR, ValidateDnsServer},
546                 {"dns",         "timeout",              &this->dns_timeout,             DT_INTEGER, ValidateDnsTimeout},
547                 {"options",     "moduledir",            &this->ModPath,                 DT_CHARPTR, ValidateModPath},
548                 {"disabled",    "commands",             &this->DisabledCommands,        DT_CHARPTR, NoValidation},
549                 {"options",     "operonlystats",        &this->OperOnlyStats,           DT_CHARPTR, NoValidation},
550                 {"options",     "customversion",        &this->CustomVersion,           DT_CHARPTR, NoValidation},
551                 {"options",     "hidesplits",           &this->HideSplits,              DT_BOOLEAN, NoValidation},
552                 {"options",     "hidebans",             &this->HideBans,                DT_BOOLEAN, NoValidation},
553                 {"options",     "hidewhois",            &this->HideWhoisServer,         DT_CHARPTR, NoValidation},
554                 {"options",     "tempdir",              &this->TempDir,                 DT_CHARPTR, ValidateTempDir},
555                 {"pid",         "file",                 &this->PID,                     DT_CHARPTR, NoValidation},
556                 {NULL}
557         };
558
559         /* These tags can occur multiple times, and therefore they have special code to read them
560          * which is different to the code for reading the singular tags listed above.
561          */
562         static MultiConfig MultiValues[] = {
563
564                 {"connect",
565                                 {"allow",       "deny",         "password",     "timeout",      "pingfreq",     "flood",
566                                 "threshold",    "sendq",        "recvq",        "localmax",     "globalmax",    NULL},
567                                 {DT_CHARPTR,    DT_CHARPTR,     DT_CHARPTR,     DT_INTEGER,     DT_INTEGER,     DT_INTEGER,
568                                  DT_INTEGER,    DT_INTEGER,     DT_INTEGER,     DT_INTEGER,     DT_INTEGER},
569                                 InitConnect, DoConnect, DoneConnect},
570
571                 {"uline",
572                                 {"server",      NULL},
573                                 {DT_CHARPTR},
574                                 InitULine,DoULine,DoneULine},
575
576                 {"banlist",
577                                 {"chan",        "limit",        NULL},
578                                 {DT_CHARPTR,    DT_INTEGER},
579                                 InitMaxBans, DoMaxBans, DoneMaxBans},
580
581                 {"module",
582                                 {"name",        NULL},
583                                 {DT_CHARPTR},
584                                 InitModule, DoModule, DoneModule},
585
586                 {"badip",
587                                 {"reason",      "ipmask",       NULL},
588                                 {DT_CHARPTR,    DT_CHARPTR},
589                                 InitXLine, DoZLine, DoneXLine},
590
591                 {"badnick",
592                                 {"reason",      "nick",         NULL},
593                                 {DT_CHARPTR,    DT_CHARPTR},
594                                 InitXLine, DoQLine, DoneXLine},
595
596                 {"badhost",
597                                 {"reason",      "host",         NULL},
598                                 {DT_CHARPTR,    DT_CHARPTR},
599                                 InitXLine, DoKLine, DoneXLine},
600
601                 {"exception",
602                                 {"reason",      "host",         NULL},
603                                 {DT_CHARPTR,    DT_CHARPTR},
604                                 InitXLine, DoELine, DoneXLine},
605
606                 {"type",
607                                 {"name",        "classes",      NULL},
608                                 {DT_CHARPTR,    DT_CHARPTR},
609                                 InitTypes, DoType, DoneClassesAndTypes},
610
611                 {"class",
612                                 {"name",        "commands",     NULL},
613                                 {DT_CHARPTR,    DT_CHARPTR},
614                                 InitClasses, DoClass, DoneClassesAndTypes},
615
616                 {NULL}
617         };
618
619         include_stack.clear();
620
621         /* Initially, load the config into memory, bail if there are errors
622          */
623         if (!LoadConf(CONFIG_FILE,&Config->config_f,&errstr))
624         {
625                 errstr.seekg(0);
626                 log(DEFAULT,"There were errors in your configuration:\n%s",errstr.str().c_str());
627
628                 if (bail)
629                 {
630                         printf("There were errors in your configuration:\n%s",errstr.str().c_str());
631                         Exit(0);
632                 }
633                 else
634                 {
635                         if (user)
636                         {
637                                 WriteServ(user->fd,"NOTICE %s :There were errors in the configuration file:",user->nick);
638                                 while (!errstr.eof())
639                                 {
640                                         errstr.getline(dataline,1024);
641                                         WriteServ(user->fd,"NOTICE %s :%s",user->nick,dataline);
642                                 }
643                         }
644                         else
645                         {
646                                 WriteOpers("There were errors in the configuration file:");
647                                 while (!errstr.eof())
648                                 {
649                                         errstr.getline(dataline,1024);
650                                         WriteOpers(dataline);
651                                 }
652                         }
653
654                         return;
655                 }
656         }
657
658         /* Check we dont have more than one of singular tags, or any of them missing
659          */
660         for (int Index = 0; Once[Index]; Index++)
661                 if (!CheckOnce(Once[Index],bail,user))
662                         return;
663
664         /* Read the values of all the tags which occur once or not at all, and call their callbacks.
665          */
666         for (int Index = 0; Values[Index].tag; Index++)
667         {
668                 int* val_i = (int*) Values[Index].val;
669                 char* val_c = (char*) Values[Index].val;
670
671                 switch (Values[Index].datatype)
672                 {
673                         case DT_CHARPTR:
674                                 ConfValue(Values[Index].tag, Values[Index].value, 0, val_c, &this->config_f);
675                         break;
676
677                         case DT_INTEGER:
678                                 convert = new char[MAXBUF];
679                                 ConfValue(Values[Index].tag, Values[Index].value, 0, convert, &this->config_f);
680                                 *val_i = atoi(convert);
681                                 delete[] convert;
682                         break;
683
684                         case DT_BOOLEAN:
685                                 convert = new char[MAXBUF];
686                                 ConfValue(Values[Index].tag, Values[Index].value, 0, convert, &this->config_f);
687                                 *val_i = ((*convert == tolower('y')) || (*convert == tolower('t')) || (*convert == '1'));
688                                 delete[] convert;
689                         break;
690
691                         case DT_NOTHING:
692                         break;
693                 }
694
695                 Values[Index].validation_function(Values[Index].tag, Values[Index].value, Values[Index].val);
696         }
697
698         /* Claim memory for use when reading multiple tags
699          */
700         for (int n = 0; n < 12; n++)
701                 data[n] = new char[MAXBUF];
702
703         /* Read the multiple-tag items (class tags, connect tags, etc)
704          * and call the callbacks associated with them. We have three
705          * callbacks for these, a 'start', 'item' and 'end' callback.
706          */
707         for (int Index = 0; MultiValues[Index].tag; Index++)
708         {
709                 MultiValues[Index].init_function(MultiValues[Index].tag);
710
711                 int number_of_tags = ConfValueEnum((char*)MultiValues[Index].tag, &this->config_f);
712
713                 for (int tagnum = 0; tagnum < number_of_tags; tagnum++)
714                 {
715                         for (int valuenum = 0; MultiValues[Index].items[valuenum]; valuenum++)
716                         {
717                                 ConfValue((char*)MultiValues[Index].tag,(char*)MultiValues[Index].items[valuenum], tagnum, data[valuenum], &this->config_f);
718
719                                 switch (MultiValues[Index].datatype[valuenum])
720                                 {
721                                         case DT_CHARPTR:
722                                                 ptr[valuenum] = data[valuenum];
723                                         break;
724                                         case DT_INTEGER:
725                                                 r_i[valuenum] = atoi(data[valuenum]);
726                                                 ptr[valuenum] = &r_i[valuenum];
727                                         break;
728                                         case DT_BOOLEAN:
729                                                 r_i[valuenum] = ((*data[valuenum] == tolower('y')) || (*data[valuenum] == tolower('t')) || (*data[valuenum] == '1'));
730                                                 ptr[valuenum] = &r_i[valuenum];
731                                         break;
732                                         default:
733                                         break;
734                                 }
735                         }
736                         MultiValues[Index].validation_function(MultiValues[Index].tag, (char**)MultiValues[Index].items, ptr, MultiValues[Index].datatype);
737                 }
738
739                 MultiValues[Index].finish_function(MultiValues[Index].tag);
740         }
741
742         /* Free any memory we claimed
743          */
744         for (int n = 0; n < 12; n++)
745                 delete[] data[n];
746
747         // write once here, to try it out and make sure its ok
748         WritePID(Config->PID);
749
750         log(DEFAULT,"Done reading configuration file, InspIRCd is now starting.");
751
752         /* If we're rehashing, let's load any new modules, and unload old ones
753          */
754         if (!bail)
755         {
756                 if (!removed_modules.empty())
757                         for (std::vector<std::string>::iterator removing = removed_modules.begin(); removing != removed_modules.end(); removing++)
758                         {
759                                 if (ServerInstance->UnloadModule(removing->c_str()))
760                                 {
761                                         WriteOpers("*** REHASH UNLOADED MODULE: %s",removing->c_str());
762
763                                         if (user)
764                                                 WriteServ(user->fd,"973 %s %s :Module %s successfully unloaded.",user->nick, removing->c_str(), removing->c_str());
765
766                                         rem++;
767                                 }
768                                 else
769                                 {
770                                         if (user)
771                                                 WriteServ(user->fd,"972 %s %s :Failed to unload module %s: %s",user->nick, removing->c_str(), removing->c_str(), ServerInstance->ModuleError());
772                                 }
773                         }
774
775                 if (!added_modules.empty())
776                 for (std::vector<std::string>::iterator adding = added_modules.begin(); adding != added_modules.end(); adding++)
777                 {
778                         if (ServerInstance->LoadModule(adding->c_str()))
779                         {
780                                 WriteOpers("*** REHASH LOADED MODULE: %s",adding->c_str());
781
782                                 if (user)
783                                         WriteServ(user->fd,"975 %s %s :Module %s successfully loaded.",user->nick, adding->c_str(), adding->c_str());
784
785                                 add++;
786                         }
787                         else
788                         {
789                                 if (user)
790                                         WriteServ(user->fd,"974 %s %s :Failed to load module %s: %s",user->nick, adding->c_str(), adding->c_str(), ServerInstance->ModuleError());
791                         }
792                 }
793
794                 log(DEFAULT,"Successfully unloaded %lu of %lu modules and loaded %lu of %lu modules.",(unsigned long)rem,(unsigned long)removed_modules.size(),(unsigned long)add,(unsigned long)added_modules.size());
795         }
796 }
797
798 void Exit (int status)
799 {
800         if (Config->log_file)
801                 fclose(Config->log_file);
802         send_error("Server shutdown.");
803         exit (status);
804 }
805
806 void Killed(int status)
807 {
808         if (Config->log_file)
809                 fclose(Config->log_file);
810         send_error("Server terminated.");
811         exit(status);
812 }
813
814 char* CleanFilename(char* name)
815 {
816         char* p = name + strlen(name);
817         while ((p != name) && (*p != '/')) p--;
818         return (p != name ? ++p : p);
819 }
820
821
822 void Rehash(int status)
823 {
824         WriteOpers("Rehashing config file %s due to SIGHUP",CleanFilename(CONFIG_FILE));
825         fclose(Config->log_file);
826         OpenLog(NULL,0);
827         Config->Read(false,NULL);
828         FOREACH_MOD(I_OnRehash,OnRehash(""));
829 }
830
831
832
833 void Start (void)
834 {
835         printf("\033[1;32mInspire Internet Relay Chat Server, compiled %s at %s\n",__DATE__,__TIME__);
836         printf("(C) ChatSpike Development team.\033[0m\n\n");
837         printf("Developers:\t\t\033[1;32mBrain, FrostyCoolSlug, w00t, Om\033[0m\n");
838         printf("Others:\t\t\t\033[1;32mSee /INFO Output\033[0m\n");
839         printf("Name concept:\t\t\033[1;32mLord_Zathras\033[0m\n\n");
840 }
841
842 void WritePID(std::string filename)
843 {
844         ofstream outfile(filename.c_str());
845         if (outfile.is_open())
846         {
847                 outfile << getpid();
848                 outfile.close();
849         }
850         else
851         {
852                 printf("Failed to write PID-file '%s', exiting.\n",filename.c_str());
853                 log(DEFAULT,"Failed to write PID-file '%s', exiting.",filename.c_str());
854                 Exit(0);
855         }
856 }
857
858 void SetSignals()
859 {
860         signal (SIGALRM, SIG_IGN);
861         signal (SIGHUP, Rehash);
862         signal (SIGPIPE, SIG_IGN);
863         signal (SIGTERM, Exit);
864         signal (SIGSEGV, Error);
865 }
866
867
868 int DaemonSeed (void)
869 {
870         int childpid;
871         if ((childpid = fork ()) < 0)
872                 return (ERROR);
873         else if (childpid > 0)
874         {
875                 /* We wait a few seconds here, so that the shell prompt doesnt come back over the output */
876                 sleep(6);
877                 exit (0);
878         }
879         setsid ();
880         umask (007);
881         printf("InspIRCd Process ID: \033[1;32m%lu\033[0m\n",(unsigned long)getpid());
882
883         if (Config->unlimitcore)
884         {
885                 rlimit rl;
886                 if (getrlimit(RLIMIT_CORE, &rl) == -1)
887                 {
888                         log(DEFAULT,"Failed to getrlimit()!");
889                         return(FALSE);
890                 }
891                 else
892                 {
893                         rl.rlim_cur = rl.rlim_max;
894                         if (setrlimit(RLIMIT_CORE, &rl) == -1)
895                                 log(DEFAULT,"setrlimit() failed, cannot increase coredump size.");
896                 }
897         }
898   
899         return (TRUE);
900 }
901
902
903 /* Make Sure Modules Are Avaliable!
904  * (BugFix By Craig.. See? I do work! :p)
905  * Modified by brain, requires const char*
906  * to work with other API functions
907  */
908
909 bool FileExists (const char* file)
910 {
911         FILE *input;
912         if ((input = fopen (file, "r")) == NULL)
913         {
914                 return(false);
915         }
916         else
917         {
918                 fclose (input);
919                 return(true);
920         }
921 }
922
923 /* ConfProcess does the following things to a config line in the following order:
924  *
925  * Processes the line for syntax errors as shown below
926  *  (1) Line void of quotes or equals (a malformed, illegal tag format)
927  *  (2) Odd number of quotes on the line indicating a missing quote
928  *  (3) number of equals signs not equal to number of quotes / 2 (missing an equals sign)
929  *  (4) Spaces between the opening bracket (<) and the keyword
930  *  (5) Spaces between a keyword and an equals sign
931  *  (6) Spaces between an equals sign and a quote
932  * Removes trailing spaces
933  * Removes leading spaces
934  * Converts tabs to spaces
935  * Turns multiple spaces that are outside of quotes into single spaces
936  */
937
938 std::string ServerConfig::ConfProcess(char* buffer, long linenumber, std::stringstream* errorstream, bool &error, std::string filename)
939 {
940         long number_of_quotes = 0;
941         long number_of_equals = 0;
942         bool has_open_bracket = false;
943         bool in_quotes = false;
944         char* trailing;
945
946         error = false;
947         if (!buffer)
948         {
949                 return "";
950         }
951         // firstly clean up the line by stripping spaces from the start and end and converting tabs to spaces
952         for (char* d = buffer; *d; d++)
953                 if (*d == 9)
954                         *d = ' ';
955         while (*buffer == ' ') buffer++;
956         trailing = buffer + strlen(buffer) - 1;
957         while (*trailing == ' ') *trailing-- = '\0';
958
959         // empty lines are syntactically valid, as are comments
960         if (!(*buffer) || buffer[0] == '#')
961                 return "";
962
963         for (char* c = buffer; *c; c++)
964         {
965                 // convert all spaces that are OUTSIDE quotes into hardspace (0xA0) as this will make them easier to
966                 // search and replace later :)
967                 if ((!in_quotes) && (*c == ' '))
968                         *c = '\xA0';
969                 if ((*c == '<') && (!in_quotes))
970                 {
971                         has_open_bracket = true;
972                         if (!(*(buffer+1)))
973                         {
974                                 *errorstream << "Tag without identifier at " << filename << ":" << linenumber << endl;
975                                 error = true;
976                                 return "";
977                         }
978                         else if ((tolower(*(c+1)) < 'a') || (tolower(*(c+1)) > 'z'))
979                         {
980                                 *errorstream << "Invalid characters in identifier at " << filename << ":" << linenumber << endl;
981                                 error = true;
982                                 return "";
983                         }
984                 }
985                 if (*c == '"')
986                 {
987                         number_of_quotes++;
988                         in_quotes = (!in_quotes);
989                 }
990                 if ((*c == '=') && (!in_quotes))
991                 {
992                         number_of_equals++;
993                         if (*(c+1) == 0)
994                         {
995                                 *errorstream << "Variable without a value at " << filename << ":" << linenumber << endl;
996                                 error = true;
997                                 return "";
998                         }
999                         else if (*(c+1) != '"')
1000                         {
1001                                 *errorstream << "Variable name not followed immediately by its value at " << filename << ":" << linenumber << endl;
1002                                 error = true;
1003                                 return "";
1004                         }
1005                         else if (c == buffer)
1006                         {
1007                                 *errorstream << "Value without a variable (line starts with '=') at " << filename << ":" << linenumber << endl;
1008                                 error = true;
1009                                 return "";
1010                         }
1011                         else if (*(c-1) == '\xA0')
1012                         {
1013                                 *errorstream << "Variable name not followed immediately by its value at " << filename << ":" << linenumber << endl;
1014                                 error = true;
1015                                 return "";
1016                         }
1017                 }
1018         }
1019         // no quotes, and no equals. something freaky.
1020         if ((!number_of_quotes) || (!number_of_equals) && (strlen(buffer)>2) && (*buffer == '<'))
1021         {
1022                 *errorstream << "Malformed tag at " << filename << ":" << linenumber << endl;
1023                 error = true;
1024                 return "";
1025         }
1026         // odd number of quotes. thats just wrong.
1027         if ((number_of_quotes % 2) != 0)
1028         {
1029                 *errorstream << "Missing \" at " << filename << ":" << linenumber << endl;
1030                 error = true;
1031                 return "";
1032         }
1033         if (number_of_equals < (number_of_quotes/2))
1034         {
1035                 *errorstream << "Missing '=' at " << filename << ":" << linenumber << endl;
1036         }
1037         if (number_of_equals > (number_of_quotes/2))
1038         {
1039                 *errorstream << "Too many '=' at " << filename << ":" << linenumber << endl;
1040         }
1041
1042         std::string parsedata = buffer;
1043         // turn multispace into single space
1044         while (parsedata.find("\xA0\xA0") != std::string::npos)
1045         {
1046                 parsedata.erase(parsedata.find("\xA0\xA0"),1);
1047         }
1048
1049         // turn our hardspace back into softspace
1050         for (unsigned int d = 0; d < parsedata.length(); d++)
1051         {
1052                 if (parsedata[d] == '\xA0')
1053                         parsedata[d] = ' ';
1054         }
1055
1056         // and we're done, the line is fine!
1057         return parsedata;
1058 }
1059
1060 int ServerConfig::fgets_safe(char* buffer, size_t maxsize, FILE* &file)
1061 {
1062         char c_read = 0;
1063         size_t n = 0;
1064         char* bufptr = buffer;
1065         while ((!feof(file)) && (c_read != '\n') && (c_read != '\r') && (n < maxsize))
1066         {
1067                 c_read = fgetc(file);
1068                 if ((c_read != '\n') && (c_read != '\r'))
1069                 {
1070                         *bufptr++ = c_read;
1071                         n++;
1072                 }
1073         }
1074         *bufptr = 0;
1075         return bufptr - buffer;
1076 }
1077
1078 bool ServerConfig::LoadConf(const char* filename, std::stringstream *target, std::stringstream* errorstream)
1079 {
1080         target->str("");
1081         errorstream->str("");
1082         long linenumber = 1;
1083         // first, check that the file exists before we try to do anything with it
1084         if (!FileExists(filename))
1085         {
1086                 *errorstream << "File " << filename << " not found." << endl;
1087                 return false;
1088         }
1089         // Fix the chmod of the file to restrict it to the current user and group
1090         chmod(filename,0600);
1091         for (unsigned int t = 0; t < include_stack.size(); t++)
1092         {
1093                 if (std::string(filename) == include_stack[t])
1094                 {
1095                         *errorstream << "File " << filename << " is included recursively (looped inclusion)." << endl;
1096                         return false;
1097                 }
1098         }
1099         include_stack.push_back(filename);
1100         // now open it
1101         FILE* conf = fopen(filename,"r");
1102         char buffer[MAXBUF];
1103         if (conf)
1104         {
1105                 while (!feof(conf))
1106                 {
1107                         if (fgets_safe(buffer, MAXBUF, conf))
1108                         {
1109                                 if ((!feof(conf)) && (buffer) && (*buffer))
1110                                 {
1111                                         if ((buffer[0] != '#') && (buffer[0] != '\r')  && (buffer[0] != '\n'))
1112                                         {
1113                                                 if (!strncmp(buffer,"<include file=\"",15))
1114                                                 {
1115                                                         char* buf = buffer;
1116                                                         char confpath[10240],newconf[10240];
1117                                                         // include file directive
1118                                                         buf += 15;      // advance to filename
1119                                                         for (char* j = buf; *j; j++)
1120                                                         {
1121                                                                 if (*j == '\\')
1122                                                                         *j = '/';
1123                                                                 if (*j == '"')
1124                                                                 {
1125                                                                         *j = 0;
1126                                                                         break;
1127                                                                 }
1128                                                         }
1129                                                         log(DEBUG,"Opening included file '%s'",buf);
1130                                                         if (*buf != '/')
1131                                                         {
1132                                                                 strlcpy(confpath,CONFIG_FILE,10240);
1133                                                                 if (strstr(confpath,"/inspircd.conf"))
1134                                                                 {
1135                                                                         // leaves us with just the path
1136                                                                         *(strstr(confpath,"/inspircd.conf")) = '\0';
1137                                                                 }
1138                                                                 snprintf(newconf,10240,"%s/%s",confpath,buf);
1139                                                         }
1140                                                         else strlcpy(newconf,buf,10240);
1141                                                         std::stringstream merge(stringstream::in | stringstream::out);
1142                                                         // recursively call LoadConf and get the new data, use the same errorstream
1143                                                         if (LoadConf(newconf, &merge, errorstream))
1144                                                         {
1145                                                                 // append to the end of the file
1146                                                                 std::string newstuff = merge.str();
1147                                                                 *target << newstuff;
1148                                                         }
1149                                                         else
1150                                                         {
1151                                                                 // the error propogates up to its parent recursively
1152                                                                 // causing the config reader to bail at the top level.
1153                                                                 fclose(conf);
1154                                                                 return false;
1155                                                         }
1156                                                 }
1157                                                 else
1158                                                 {
1159                                                         bool error = false;
1160                                                         std::string data = this->ConfProcess(buffer,linenumber++,errorstream,error,filename);
1161                                                         if (error)
1162                                                         {
1163                                                                 return false;
1164                                                         }
1165                                                         *target << data;
1166                                                 }
1167                                         }
1168                                         else linenumber++;
1169                                 }
1170                         }
1171                 }
1172                 fclose(conf);
1173         }
1174         target->seekg(0);
1175         return true;
1176 }
1177
1178 /* Counts the number of tags of a certain type within the config file, e.g. to enumerate opers */
1179
1180 int ServerConfig::EnumConf(std::stringstream *config, const char* tag)
1181 {
1182         int ptr = 0;
1183         char buffer[MAXBUF], c_tag[MAXBUF], c, lastc;
1184         int in_token, in_quotes, tptr, idx = 0;
1185
1186         std::string x = config->str();
1187         const char* buf = x.c_str();
1188         char* bptr = (char*)buf;
1189         
1190         ptr = 0;
1191         in_token = 0;
1192         in_quotes = 0;
1193         lastc = '\0';
1194         while (*bptr)
1195         {
1196                 lastc = c;
1197                 c = *bptr++;
1198                 if ((c == '#') && (lastc == '\n'))
1199                 {
1200                         while ((c != '\n') && (*bptr))
1201                         {
1202                                 lastc = c;
1203                                 c = *bptr++;
1204                         }
1205                 }
1206                 if ((c == '<') && (!in_quotes))
1207                 {
1208                         tptr = 0;
1209                         in_token = 1;
1210                         do {
1211                                 c = *bptr++;
1212                                 if (c != ' ')
1213                                 {
1214                                         c_tag[tptr++] = c;
1215                                         c_tag[tptr] = '\0';
1216                                 }
1217                         } while (c != ' ');
1218                 }
1219                 if (c == '"')
1220                 {
1221                         in_quotes = (!in_quotes);
1222                 }
1223                 if ((c == '>') && (!in_quotes))
1224                 {
1225                         in_token = 0;
1226                         if (!strcmp(c_tag,tag))
1227                         {
1228                                 /* correct tag, but wrong index */
1229                                 idx++;
1230                         }
1231                         c_tag[0] = '\0';
1232                         buffer[0] = '\0';
1233                         ptr = 0;
1234                         tptr = 0;
1235                 }
1236                 if (c != '>')
1237                 {
1238                         if ((in_token) && (c != '\n') && (c != '\r'))
1239                         {
1240                                 buffer[ptr++] = c;
1241                                 buffer[ptr] = '\0';
1242                         }
1243                 }
1244         }
1245         return idx;
1246 }
1247
1248 /* Counts the number of values within a certain tag */
1249
1250 int ServerConfig::EnumValues(std::stringstream *config, const char* tag, int index)
1251 {
1252         int ptr = 0;
1253         char buffer[MAXBUF], c_tag[MAXBUF], c, lastc;
1254         int in_token, in_quotes, tptr, idx = 0;
1255         bool correct_tag = false;
1256         int num_items = 0;
1257         const char* buf = config->str().c_str();
1258         char* bptr = (char*)buf;
1259
1260         ptr = 0;
1261         in_token = 0;
1262         in_quotes = 0;
1263         lastc = 0;
1264
1265         while (*bptr)
1266         {
1267                 lastc = c;
1268                 c = *bptr++;
1269                 if ((c == '#') && (lastc == '\n'))
1270                 {
1271                         while ((c != '\n') && (*bptr))
1272                         {
1273                                 lastc = c;
1274                                 c = *bptr++;
1275                         }
1276                 }
1277                 if ((c == '<') && (!in_quotes))
1278                 {
1279                         tptr = 0;
1280                         in_token = 1;
1281                         do {
1282                                 c = *bptr++;
1283                                 if (c != ' ')
1284                                 {
1285                                         c_tag[tptr++] = c;
1286                                         c_tag[tptr] = '\0';
1287                                         
1288                                         if ((!strcmp(c_tag,tag)) && (idx == index))
1289                                         {
1290                                                 correct_tag = true;
1291                                         }
1292                                 }
1293                         } while (c != ' ');
1294                 }
1295                 if (c == '"')
1296                 {
1297                         in_quotes = (!in_quotes);
1298                 }
1299                 
1300                 if ( (correct_tag) && (!in_quotes) && ( (c == ' ') || (c == '\n') || (c == '\r') ) )
1301                 {
1302                         num_items++;
1303                 }
1304                 if ((c == '>') && (!in_quotes))
1305                 {
1306                         in_token = 0;
1307                         if (correct_tag)
1308                                 correct_tag = false;
1309                         if (!strcmp(c_tag,tag))
1310                         {
1311                                 /* correct tag, but wrong index */
1312                                 idx++;
1313                         }
1314                         c_tag[0] = '\0';
1315                         buffer[0] = '\0';
1316                         ptr = 0;
1317                         tptr = 0;
1318                 }
1319                 if (c != '>')
1320                 {
1321                         if ((in_token) && (c != '\n') && (c != '\r'))
1322                         {
1323                                 buffer[ptr++] = c;
1324                                 buffer[ptr] = '\0';
1325                         }
1326                 }
1327         }
1328         return num_items+1;
1329 }
1330
1331
1332 int ServerConfig::ConfValueEnum(char* tag, std::stringstream* config)
1333 {
1334         return EnumConf(config,tag);
1335 }
1336
1337
1338 int ServerConfig::ReadConf(std::stringstream *config, const char* tag, const char* var, int index, char *result)
1339 {
1340         int ptr = 0;
1341         char buffer[65535], c_tag[MAXBUF], c, lastc;
1342         int in_token, in_quotes, tptr, idx = 0;
1343         char* key;
1344         std::string x = config->str();
1345         const char* buf = x.c_str();
1346         char* bptr = (char*)buf;
1347         
1348         ptr = 0;
1349         in_token = 0;
1350         in_quotes = 0;
1351         lastc = 0;
1352         c_tag[0] = 0;
1353         buffer[0] = 0;
1354
1355         while (*bptr)
1356         {
1357                 lastc = c;
1358                 c = *bptr++;
1359                 // FIX: Treat tabs as spaces
1360                 if (c == 9)
1361                         c = 32;
1362                 if ((c == '<') && (!in_quotes))
1363                 {
1364                         tptr = 0;
1365                         in_token = 1;
1366                         do {
1367                                 c = *bptr++;
1368                                 if (c != ' ')
1369                                 {
1370                                         c_tag[tptr++] = c;
1371                                         c_tag[tptr] = '\0';
1372                                 }
1373                         // FIX: Tab can follow a tagname as well as space.
1374                         } while ((c != ' ') && (c != 9));
1375                 }
1376                 if (c == '"')
1377                 {
1378                         in_quotes = (!in_quotes);
1379                 }
1380                 if ((c == '>') && (!in_quotes))
1381                 {
1382                         in_token = 0;
1383                         if (idx == index)
1384                         {
1385                                 if (!strcmp(c_tag,tag))
1386                                 {
1387                                         if ((buffer) && (c_tag) && (var))
1388                                         {
1389                                                 key = strstr(buffer,var);
1390                                                 if (!key)
1391                                                 {
1392                                                         /* value not found in tag */
1393                                                         *result = 0;
1394                                                         return 0;
1395                                                 }
1396                                                 else
1397                                                 {
1398                                                         key+=strlen(var);
1399                                                         while (*key !='"')
1400                                                         {
1401                                                                 if (!*key)
1402                                                                 {
1403                                                                         /* missing quote */
1404                                                                         *result = 0;
1405                                                                         return 0;
1406                                                                 }
1407                                                                 key++;
1408                                                         }
1409                                                         key++;
1410                                                         for (char* j = key; *j; j++)
1411                                                         {
1412                                                                 if (*j == '"')
1413                                                                 {
1414                                                                         *j = 0;
1415                                                                         break;
1416                                                                 }
1417                                                         }
1418                                                         strlcpy(result,key,MAXBUF);
1419                                                         return 1;
1420                                                 }
1421                                         }
1422                                 }
1423                         }
1424                         if (!strcmp(c_tag,tag))
1425                         {
1426                                 /* correct tag, but wrong index */
1427                                 idx++;
1428                         }
1429                         c_tag[0] = '\0';
1430                         buffer[0] = '\0';
1431                         ptr = 0;
1432                         tptr = 0;
1433                 }
1434                 if (c != '>')
1435                 {
1436                         if ((in_token) && (c != '\n') && (c != '\r'))
1437                         {
1438                                 buffer[ptr++] = c;
1439                                 buffer[ptr] = '\0';
1440                         }
1441                 }
1442         }
1443         *result = 0; // value or its tag not found at all
1444         return 0;
1445 }
1446
1447
1448
1449 int ServerConfig::ConfValue(char* tag, char* var, int index, char *result,std::stringstream *config)
1450 {
1451         ReadConf(config, tag, var, index, result);
1452         return 0;
1453 }
1454
1455 int ServerConfig::ConfValueInteger(char* tag, char* var, int index, std::stringstream *config)
1456 {
1457         char result[MAXBUF];
1458         ReadConf(config, tag, var, index, result);
1459         return atoi(result);
1460 }
1461
1462 // This will bind a socket to a port. It works for UDP/TCP
1463 int BindSocket (int sockfd, struct sockaddr_in client, struct sockaddr_in server, int port, char* addr)
1464 {
1465         memset((char *)&server,0,sizeof(server));
1466         struct in_addr addy;
1467         bool resolved = false;
1468         char resolved_addr[128];
1469
1470         if (*addr == '*')
1471                 *addr = 0;
1472
1473         if (*addr && !inet_aton(addr,&addy))
1474         {
1475                 /* If they gave a hostname, bind to the IP it resolves to */
1476                 if (CleanAndResolve(resolved_addr, addr, true))
1477                 {
1478                         inet_aton(resolved_addr,&addy);
1479                         log(DEFAULT,"Resolved binding '%s' -> '%s'",addr,resolved_addr);
1480                         server.sin_addr = addy;
1481                         resolved = true;
1482                 }
1483                 else
1484                 {
1485                         log(DEFAULT,"WARNING: Could not resolve '%s' to an IP for binding to on port %d",addr,port);
1486                         return(FALSE);
1487                 }
1488         }
1489         server.sin_family = AF_INET;
1490         if (!resolved)
1491         {
1492                 if (!*addr)
1493                 {
1494                         server.sin_addr.s_addr = htonl(INADDR_ANY);
1495                 }
1496                 else
1497                 {
1498                         server.sin_addr = addy;
1499                 }
1500         }
1501         server.sin_port = htons(port);
1502         if (bind(sockfd,(struct sockaddr*)&server,sizeof(server)) < 0)
1503         {
1504                 return(ERROR);
1505         }
1506         else
1507         {
1508                 log(DEBUG,"Bound port %s:%d",*addr ? addr : "*",port);
1509                 if (listen(sockfd, Config->MaxConn) == -1)
1510                 {
1511                         log(DEFAULT,"ERROR in listen(): %s",strerror(errno));
1512                         return(FALSE);
1513                 }
1514                 else
1515                 {
1516                         return(TRUE);
1517                 }
1518         }
1519 }
1520
1521
1522 // Open a TCP Socket
1523 int OpenTCPSocket (void)
1524 {
1525         int sockfd;
1526         int on = 1;
1527         struct linger linger = { 0 };
1528   
1529         if ((sockfd = socket (AF_INET, SOCK_STREAM, 0)) < 0)
1530                 return (ERROR);
1531         else
1532         {
1533                 setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, (const char*)&on, sizeof(on));
1534                 /* This is BSD compatible, setting l_onoff to 0 is *NOT* http://web.irc.org/mla/ircd-dev/msg02259.html */
1535                 linger.l_onoff = 1;
1536                 linger.l_linger = 1;
1537                 setsockopt(sockfd, SOL_SOCKET, SO_LINGER, (const char*)&linger,sizeof(linger));
1538                 return (sockfd);
1539         }
1540 }
1541
1542 int BindPorts()
1543 {
1544         char configToken[MAXBUF], Addr[MAXBUF], Type[MAXBUF];
1545         sockaddr_in client,server;
1546         int clientportcount = 0;
1547         int BoundPortCount = 0;
1548
1549         for (int count = 0; count < Config->ConfValueEnum("bind",&Config->config_f); count++)
1550         {
1551                 Config->ConfValue("bind","port",count,configToken,&Config->config_f);
1552                 Config->ConfValue("bind","address",count,Addr,&Config->config_f);
1553                 Config->ConfValue("bind","type",count,Type,&Config->config_f);
1554
1555                 if ((!*Type) || (!strcmp(Type,"clients")))
1556                 {
1557                         // modules handle server bind types now
1558                         Config->ports[clientportcount] = atoi(configToken);
1559
1560                         // If the client put bind "*", this is an unrealism.
1561                         // We don't actually support this as documented, but
1562                         // i got fed up of people trying it, so now it converts
1563                         // it to an empty string meaning the same 'bind to all'.
1564                         if (*Addr == '*')
1565                                 *Addr = 0;
1566
1567                         strlcpy(Config->addrs[clientportcount],Addr,256);
1568                         clientportcount++;
1569                         log(DEBUG,"InspIRCd: startup: read binding %s:%s [%s] from config",Addr,configToken, Type);
1570                 }
1571         }
1572
1573         int PortCount = clientportcount;
1574
1575         for (int count = 0; count < PortCount; count++)
1576         {
1577                 if ((openSockfd[BoundPortCount] = OpenTCPSocket()) == ERROR)
1578                 {
1579                         log(DEBUG,"InspIRCd: startup: bad fd %lu binding port [%s:%d]",(unsigned long)openSockfd[BoundPortCount],Config->addrs[count],(unsigned long)Config->ports[count]);
1580                         return(ERROR);
1581                 }
1582
1583                 if (BindSocket(openSockfd[BoundPortCount],client,server,Config->ports[count],Config->addrs[count]) == ERROR)
1584                 {
1585                         log(DEFAULT,"InspIRCd: startup: failed to bind port [%s:%lu]: %s",Config->addrs[count],(unsigned long)Config->ports[count],strerror(errno));
1586                 }
1587                 else
1588                 {
1589                         /* well we at least bound to one socket so we'll continue */
1590                         BoundPortCount++;
1591                 }
1592         }
1593
1594         /* if we didn't bind to anything then abort */
1595         if (!BoundPortCount)
1596         {
1597                 log(DEFAULT,"InspIRCd: startup: no ports bound, bailing!");
1598                 printf("\nERROR: Was not able to bind any of %lu ports! Please check your configuration.\n\n", (unsigned long)PortCount);
1599                 return (ERROR);
1600         }
1601
1602         return BoundPortCount;
1603 }
1604