]> git.netwichtig.de Git - user/henk/code/inspircd.git/blob - src/configreader.cpp
0b712ca6c93388c754d6961aaf3f2a772f7511c4
[user/henk/code/inspircd.git] / src / configreader.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 #include "inspircd_config.h"
18 #include "configreader.h"
19 #include <string>
20 #include <sstream>
21 #include <iostream>
22 #include <fstream>
23 #include "message.h"
24 #include "inspircd.h"
25 #include "inspstring.h"
26 #include "helperfuncs.h"
27 #include "userprocess.h"
28 #include "xline.h"
29
30 extern InspIRCd* ServerInstance;
31 extern time_t TIME;
32
33 extern int MODCOUNT;
34 extern std::vector<Module*> modules;
35 extern std::vector<ircd_module*> factory;
36
37 using irc::sockets::BindPorts;
38
39 std::vector<std::string> old_module_names, new_module_names, added_modules, removed_modules;
40
41 ServerConfig::ServerConfig()
42 {
43         this->ClearStack();
44         *TempDir = *ServerName = *Network = *ServerDesc = *AdminName = '\0';
45         *HideWhoisServer = *AdminEmail = *AdminNick = *diepass = *restartpass = '\0';
46         *CustomVersion = *motd = *rules = *PrefixQuit = *DieValue = *DNSServer = '\0';
47         *OperOnlyStats = *ModPath = *MyExecutable = *DisabledCommands = *PID = '\0';
48         log_file = NULL;
49         NoUserDns = forcedebug = OperSpyWhois = nofork = HideBans = HideSplits = false;
50         writelog = AllowHalfop = true;
51         dns_timeout = DieDelay = 5;
52         MaxTargets = 20;
53         NetBufferSize = 10240;
54         SoftLimit = MAXCLIENTS;
55         MaxConn = SOMAXCONN;
56         MaxWhoResults = 100;
57         debugging = 0;
58         LogLevel = DEFAULT;
59         maxbans.clear();
60 }
61
62 void ServerConfig::ClearStack()
63 {
64         include_stack.clear();
65 }
66
67 Module* ServerConfig::GetIOHook(int port)
68 {
69         std::map<int,Module*>::iterator x = IOHookModule.find(port);
70         return (x != IOHookModule.end() ? x->second : NULL);
71 }
72
73 bool ServerConfig::AddIOHook(int port, Module* iomod)
74 {
75         if (!GetIOHook(port))
76         {
77                 IOHookModule[port] = iomod;
78                 return true;
79         }
80         else
81         {
82                 ModuleException err("Port already hooked by another module");
83                 throw(err);
84                 return false;
85         }
86 }
87
88 bool ServerConfig::DelIOHook(int port)
89 {
90         std::map<int,Module*>::iterator x = IOHookModule.find(port);
91         if (x != IOHookModule.end())
92         {
93                 IOHookModule.erase(x);
94                 return true;
95         }
96         return false;
97 }
98
99 bool ServerConfig::CheckOnce(char* tag, bool bail, userrec* user)
100 {
101         int count = ConfValueEnum(this->config_data, tag);
102         
103         if (count > 1)
104         {
105                 if (bail)
106                 {
107                         printf("There were errors in your configuration:\nYou have more than one <%s> tag, this is not permitted.\n",tag);
108                         Exit(0);
109                 }
110                 else
111                 {
112                         if (user)
113                         {
114                                 user->WriteServ("There were errors in your configuration:");
115                                 user->WriteServ("You have more than one <%s> tag, this is not permitted.\n",tag);
116                         }
117                         else
118                         {
119                                 WriteOpers("There were errors in the configuration file:");
120                                 WriteOpers("You have more than one <%s> tag, this is not permitted.\n",tag);
121                         }
122                 }
123                 return false;
124         }
125         if (count < 1)
126         {
127                 if (bail)
128                 {
129                         printf("There were errors in your configuration:\nYou have not defined a <%s> tag, this is required.\n",tag);
130                         Exit(0);
131                 }
132                 else
133                 {
134                         if (user)
135                         {
136                                 user->WriteServ("There were errors in your configuration:");
137                                 user->WriteServ("You have not defined a <%s> tag, this is required.",tag);
138                         }
139                         else
140                         {
141                                 WriteOpers("There were errors in the configuration file:");
142                                 WriteOpers("You have not defined a <%s> tag, this is required.",tag);
143                         }
144                 }
145                 return false;
146         }
147         return true;
148 }
149
150 bool NoValidation(ServerConfig* conf, const char* tag, const char* value, void* data)
151 {
152         log(DEBUG,"No validation for <%s:%s>",tag,value);
153         return true;
154 }
155
156 bool ValidateTempDir(ServerConfig* conf, const char* tag, const char* value, void* data)
157 {
158         char* x = (char*)data;
159         if (!*x)
160                 strlcpy(x,"/tmp",1024);
161         return true;
162 }
163  
164 bool ValidateMaxTargets(ServerConfig* conf, const char* tag, const char* value, void* data)
165 {
166         int* x = (int*)data;
167         if ((*x < 0) || (*x > 31))
168         {
169                 log(DEFAULT,"WARNING: <options:maxtargets> value is greater than 31 or less than 0, set to 20.");
170                 *x = 20;
171         }
172         return true;
173 }
174
175 bool ValidateSoftLimit(ServerConfig* conf, const char* tag, const char* value, void* data)
176 {
177         int* x = (int*)data;    
178         if ((*x < 1) || (*x > MAXCLIENTS))
179         {
180                 log(DEFAULT,"WARNING: <options:softlimit> value is greater than %d or less than 0, set to %d.",MAXCLIENTS,MAXCLIENTS);
181                 *x = MAXCLIENTS;
182         }
183         return true;
184 }
185
186 bool ValidateMaxConn(ServerConfig* conf, const char* tag, const char* value, void* data)
187 {
188         int* x = (int*)data;    
189         if (*x > SOMAXCONN)
190                 log(DEFAULT,"WARNING: <options:somaxconn> value may be higher than the system-defined SOMAXCONN value!");
191         if (!*x)
192                 *x = SOMAXCONN;
193         return true;
194 }
195
196 bool ValidateDnsTimeout(ServerConfig* conf, const char* tag, const char* value, void* data)
197 {
198         int* x = (int*)data;
199         if (!*x)
200                 *x = 5;
201         return true;
202 }
203
204 bool InitializeDisabledCommands(const char* data, InspIRCd* ServerInstance)
205 {
206         std::stringstream dcmds(data);
207         std::string thiscmd;
208
209         /* Enable everything first */
210         for (nspace::hash_map<std::string,command_t*>::iterator x = ServerInstance->Parser->cmdlist.begin(); x != ServerInstance->Parser->cmdlist.end(); x++)
211                 x->second->Disable(false);
212
213         /* Now disable all the ones which the user wants disabled */
214         while (dcmds >> thiscmd)
215         {
216                 nspace::hash_map<std::string,command_t*>::iterator cm = ServerInstance->Parser->cmdlist.find(thiscmd);
217                 if (cm != ServerInstance->Parser->cmdlist.end())
218                 {
219                         log(DEBUG,"Disabling command '%s'",cm->second->command.c_str());
220                         cm->second->Disable(true);
221                 }
222         }
223         return true;
224 }
225
226 bool ValidateDnsServer(ServerConfig* conf, const char* tag, const char* value, void* data)
227 {
228         char* x = (char*)data;
229         if (!*x)
230         {
231                 // attempt to look up their nameserver from /etc/resolv.conf
232                 log(DEFAULT,"WARNING: <dns:server> not defined, attempting to find working server in /etc/resolv.conf...");
233                 ifstream resolv("/etc/resolv.conf");
234                 std::string nameserver;
235                 bool found_server = false;
236
237                 if (resolv.is_open())
238                 {
239                         while (resolv >> nameserver)
240                         {
241                                 if ((nameserver == "nameserver") && (!found_server))
242                                 {
243                                         resolv >> nameserver;
244                                         strlcpy(x,nameserver.c_str(),MAXBUF);
245                                         found_server = true;
246                                         log(DEFAULT,"<dns:server> set to '%s' as first resolver in /etc/resolv.conf.",nameserver.c_str());
247                                 }
248                         }
249
250                         if (!found_server)
251                         {
252                                 log(DEFAULT,"/etc/resolv.conf contains no viable nameserver entries! Defaulting to nameserver '127.0.0.1'!");
253                                 strlcpy(x,"127.0.0.1",MAXBUF);
254                         }
255                 }
256                 else
257                 {
258                         log(DEFAULT,"/etc/resolv.conf can't be opened! Defaulting to nameserver '127.0.0.1'!");
259                         strlcpy(x,"127.0.0.1",MAXBUF);
260                 }
261         }
262         return true;
263 }
264
265 bool ValidateModPath(ServerConfig* conf, const char* tag, const char* value, void* data)
266 {
267         char* x = (char*)data;  
268         if (!*x)
269                 strlcpy(x,MOD_PATH,MAXBUF);
270         return true;
271 }
272
273
274 bool ValidateServerName(ServerConfig* conf, const char* tag, const char* value, void* data)
275 {
276         char* x = (char*)data;
277         if (!strchr(x,'.'))
278         {
279                 log(DEFAULT,"WARNING: <server:name> '%s' is not a fully-qualified domain name. Changed to '%s%c'",x,x,'.');
280                 charlcat(x,'.',MAXBUF);
281         }
282         //strlower(x);
283         return true;
284 }
285
286 bool ValidateNetBufferSize(ServerConfig* conf, const char* tag, const char* value, void* data)
287 {
288         if ((!ServerInstance->Config->NetBufferSize) || (ServerInstance->Config->NetBufferSize > 65535) || (ServerInstance->Config->NetBufferSize < 1024))
289         {
290                 log(DEFAULT,"No NetBufferSize specified or size out of range, setting to default of 10240.");
291                 ServerInstance->Config->NetBufferSize = 10240;
292         }
293         return true;
294 }
295
296 bool ValidateMaxWho(ServerConfig* conf, const char* tag, const char* value, void* data)
297 {
298         if ((!ServerInstance->Config->MaxWhoResults) || (ServerInstance->Config->MaxWhoResults > 65535) || (ServerInstance->Config->MaxWhoResults < 1))
299         {
300                 log(DEFAULT,"No MaxWhoResults specified or size out of range, setting to default of 128.");
301                 ServerInstance->Config->MaxWhoResults = 128;
302         }
303         return true;
304 }
305
306 bool ValidateLogLevel(ServerConfig* conf, const char* tag, const char* value, void* data)
307 {
308         const char* dbg = (const char*)data;
309         ServerInstance->Config->LogLevel = DEFAULT;
310         if (!strcmp(dbg,"debug"))
311         {
312                 ServerInstance->Config->LogLevel = DEBUG;
313                 ServerInstance->Config->debugging = 1;
314         }
315         else if (!strcmp(dbg,"verbose"))
316                 ServerInstance->Config->LogLevel = VERBOSE;
317         else if (!strcmp(dbg,"default"))
318                 ServerInstance->Config->LogLevel = DEFAULT;
319         else if (!strcmp(dbg,"sparse"))
320                 ServerInstance->Config->LogLevel = SPARSE;
321         else if (!strcmp(dbg,"none"))
322                 ServerInstance->Config->LogLevel = NONE;
323         return true;
324 }
325
326 bool ValidateMotd(ServerConfig* conf, const char* tag, const char* value, void* data)
327 {
328         conf->ReadFile(ServerInstance->Config->MOTD,ServerInstance->Config->motd);
329         return true;
330 }
331
332 bool ValidateRules(ServerConfig* conf, const char* tag, const char* value, void* data)
333 {
334         conf->ReadFile(ServerInstance->Config->RULES,ServerInstance->Config->rules);
335         return true;
336 }
337
338 /* Callback called before processing the first <connect> tag
339  */
340 bool InitConnect(ServerConfig* conf, const char* tag)
341 {
342         log(DEFAULT,"Reading connect classes...");
343         ServerInstance->Config->Classes.clear();
344         return true;
345 }
346
347 /* Callback called to process a single <connect> tag
348  */
349 bool DoConnect(ServerConfig* conf, const char* tag, char** entries, void** values, int* types)
350 {
351         ConnectClass c;
352         char* allow = (char*)values[0]; /* Yeah, there are a lot of values. Live with it. */
353         char* deny = (char*)values[1];
354         char* password = (char*)values[2];
355         int* timeout = (int*)values[3];
356         int* pingfreq = (int*)values[4];
357         int* flood = (int*)values[5];
358         int* threshold = (int*)values[6];
359         int* sendq = (int*)values[7];
360         int* recvq = (int*)values[8];
361         int* localmax = (int*)values[9];
362         int* globalmax = (int*)values[10];
363
364         if (*allow)
365         {
366                 c.host = allow;
367                 c.type = CC_ALLOW;
368                 c.pass = password;
369                 c.registration_timeout = *timeout;
370                 c.pingtime = *pingfreq;
371                 c.flood = *flood;
372                 c.threshold = *threshold;
373                 c.sendqmax = *sendq;
374                 c.recvqmax = *recvq;
375                 c.maxlocal = *localmax;
376                 c.maxglobal = *globalmax;
377
378
379                 if (c.maxlocal == 0)
380                         c.maxlocal = 3;
381                 if (c.maxglobal == 0)
382                         c.maxglobal = 3;
383                 if (c.threshold == 0)
384                 {
385                         c.threshold = 1;
386                         c.flood = 999;
387                         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());
388                 }
389                 if (c.sendqmax == 0)
390                         c.sendqmax = 262114;
391                 if (c.recvqmax == 0)
392                         c.recvqmax = 4096;
393                 if (c.registration_timeout == 0)
394                         c.registration_timeout = 90;
395                 if (c.pingtime == 0)
396                         c.pingtime = 120;
397                 ServerInstance->Config->Classes.push_back(c);
398         }
399         else
400         {
401                 c.host = deny;
402                 c.type = CC_DENY;
403                 ServerInstance->Config->Classes.push_back(c);
404                 log(DEBUG,"Read connect class type DENY, host=%s",deny);
405         }
406
407         return true;
408 }
409
410 /* Callback called when there are no more <connect> tags
411  */
412 bool DoneConnect(ServerConfig* conf, const char* tag)
413 {
414         log(DEBUG,"DoneConnect called for tag: %s",tag);
415         return true;
416 }
417
418 /* Callback called before processing the first <uline> tag
419  */
420 bool InitULine(ServerConfig* conf, const char* tag)
421 {
422         ServerInstance->Config->ulines.clear();
423         return true;
424 }
425
426 /* Callback called to process a single <uline> tag
427  */
428 bool DoULine(ServerConfig* conf, const char* tag, char** entries, void** values, int* types)
429 {
430         char* server = (char*)values[0];
431         log(DEBUG,"Read ULINE '%s'",server);
432         ServerInstance->Config->ulines.push_back(server);
433         return true;
434 }
435
436 /* Callback called when there are no more <uline> tags
437  */
438 bool DoneULine(ServerConfig* conf, const char* tag)
439 {
440         return true;
441 }
442
443 /* Callback called before processing the first <module> tag
444  */
445 bool InitModule(ServerConfig* conf, const char* tag)
446 {
447         old_module_names.clear();
448         new_module_names.clear();
449         added_modules.clear();
450         removed_modules.clear();
451         for (std::vector<std::string>::iterator t = ServerInstance->Config->module_names.begin(); t != ServerInstance->Config->module_names.end(); t++)
452         {
453                 old_module_names.push_back(*t);
454         }
455         return true;
456 }
457
458 /* Callback called to process a single <module> tag
459  */
460 bool DoModule(ServerConfig* conf, const char* tag, char** entries, void** values, int* types)
461 {
462         char* modname = (char*)values[0];
463         new_module_names.push_back(modname);
464         return true;
465 }
466
467 /* Callback called when there are no more <module> tags
468  */
469 bool DoneModule(ServerConfig* conf, const char* tag)
470 {
471         // now create a list of new modules that are due to be loaded
472         // and a seperate list of modules which are due to be unloaded
473         for (std::vector<std::string>::iterator _new = new_module_names.begin(); _new != new_module_names.end(); _new++)
474         {
475                 bool added = true;
476
477                 for (std::vector<std::string>::iterator old = old_module_names.begin(); old != old_module_names.end(); old++)
478                 {
479                         if (*old == *_new)
480                                 added = false;
481                 }
482
483                 if (added)
484                         added_modules.push_back(*_new);
485         }
486
487         for (std::vector<std::string>::iterator oldm = old_module_names.begin(); oldm != old_module_names.end(); oldm++)
488         {
489                 bool removed = true;
490                 for (std::vector<std::string>::iterator newm = new_module_names.begin(); newm != new_module_names.end(); newm++)
491                 {
492                         if (*newm == *oldm)
493                                 removed = false;
494                 }
495
496                 if (removed)
497                         removed_modules.push_back(*oldm);
498         }
499         return true;
500 }
501
502 /* Callback called before processing the first <banlist> tag
503  */
504 bool InitMaxBans(ServerConfig* conf, const char* tag)
505 {
506         ServerInstance->Config->maxbans.clear();
507         return true;
508 }
509
510 /* Callback called to process a single <banlist> tag
511  */
512 bool DoMaxBans(ServerConfig* conf, const char* tag, char** entries, void** values, int* types)
513 {
514         char* channel = (char*)values[0];
515         int* limit = (int*)values[1];
516         ServerInstance->Config->maxbans[channel] = *limit;
517         return true;
518 }
519
520 /* Callback called when there are no more <banlist> tags.
521  */
522 bool DoneMaxBans(ServerConfig* conf, const char* tag)
523 {
524         return true;
525 }
526
527 void ServerConfig::Read(bool bail, userrec* user)
528 {
529         char debug[MAXBUF];             /* Temporary buffer for debugging value */
530         char* data[12];                 /* Temporary buffers for reading multiple occurance tags into */
531         void* ptr[12];                  /* Temporary pointers for passing to callbacks */
532         int r_i[12];                    /* Temporary array for casting */
533         int rem = 0, add = 0;           /* Number of modules added, number of modules removed */
534         std::ostringstream errstr;      /* String stream containing the error output */
535
536         /* These tags MUST occur and must ONLY occur once in the config file */
537         static char* Once[] = { "server", "admin", "files", "power", "options", "pid", NULL };
538
539         /* These tags can occur ONCE or not at all */
540         static InitialConfig Values[] = {
541                 {"options",             "softlimit",                    &this->SoftLimit,               DT_INTEGER, ValidateSoftLimit},
542                 {"options",             "somaxconn",                    &this->MaxConn,                 DT_INTEGER, ValidateMaxConn},
543                 {"server",              "name",                         &this->ServerName,              DT_CHARPTR, ValidateServerName},
544                 {"server",              "description",                  &this->ServerDesc,              DT_CHARPTR, NoValidation},
545                 {"server",              "network",                      &this->Network,                 DT_CHARPTR, NoValidation},
546                 {"admin",               "name",                         &this->AdminName,               DT_CHARPTR, NoValidation},
547                 {"admin",               "email",                        &this->AdminEmail,              DT_CHARPTR, NoValidation},
548                 {"admin",               "nick",                         &this->AdminNick,               DT_CHARPTR, NoValidation},
549                 {"files",               "motd",                         &this->motd,                    DT_CHARPTR, ValidateMotd},
550                 {"files",               "rules",                        &this->rules,                   DT_CHARPTR, ValidateRules},
551                 {"power",               "diepass",                      &this->diepass,                 DT_CHARPTR, NoValidation},      
552                 {"power",               "pauseval",                     &this->DieDelay,                DT_INTEGER, NoValidation},
553                 {"power",               "restartpass",                  &this->restartpass,             DT_CHARPTR, NoValidation},
554                 {"options",             "prefixquit",                   &this->PrefixQuit,              DT_CHARPTR, NoValidation},
555                 {"die",                 "value",                        &this->DieValue,                DT_CHARPTR, NoValidation},
556                 {"options",             "loglevel",                     &debug,                         DT_CHARPTR, ValidateLogLevel},
557                 {"options",             "netbuffersize",                &this->NetBufferSize,           DT_INTEGER, ValidateNetBufferSize},
558                 {"options",             "maxwho",                       &this->MaxWhoResults,           DT_INTEGER, ValidateMaxWho},
559                 {"options",             "allowhalfop",                  &this->AllowHalfop,             DT_BOOLEAN, NoValidation},
560                 {"dns",                 "server",                       &this->DNSServer,               DT_CHARPTR, ValidateDnsServer},
561                 {"dns",                 "timeout",                      &this->dns_timeout,             DT_INTEGER, ValidateDnsTimeout},
562                 {"options",             "moduledir",                    &this->ModPath,                 DT_CHARPTR, ValidateModPath},
563                 {"disabled",            "commands",                     &this->DisabledCommands,        DT_CHARPTR, NoValidation},
564                 {"options",             "operonlystats",                &this->OperOnlyStats,           DT_CHARPTR, NoValidation},
565                 {"options",             "customversion",                &this->CustomVersion,           DT_CHARPTR, NoValidation},
566                 {"options",             "hidesplits",                   &this->HideSplits,              DT_BOOLEAN, NoValidation},
567                 {"options",             "hidebans",                     &this->HideBans,                DT_BOOLEAN, NoValidation},
568                 {"options",             "hidewhois",                    &this->HideWhoisServer,         DT_CHARPTR, NoValidation},
569                 {"options",             "operspywhois",                 &this->OperSpyWhois,            DT_BOOLEAN, NoValidation},
570                 {"options",             "tempdir",                      &this->TempDir,                 DT_CHARPTR, ValidateTempDir},
571                 {"options",             "nouserdns",                    &this->NoUserDns,               DT_BOOLEAN, NoValidation},
572                 {"options",             "syntaxhints",                  &this->SyntaxHints,             DT_BOOLEAN, NoValidation},
573                 {"pid",                 "file",                         &this->PID,                     DT_CHARPTR, NoValidation},
574                 {NULL}
575         };
576
577         /* These tags can occur multiple times, and therefore they have special code to read them
578          * which is different to the code for reading the singular tags listed above.
579          */
580         static MultiConfig MultiValues[] = {
581
582                 {"connect",
583                                 {"allow",       "deny",         "password",     "timeout",      "pingfreq",     "flood",
584                                 "threshold",    "sendq",        "recvq",        "localmax",     "globalmax",    NULL},
585                                 {DT_CHARPTR,    DT_CHARPTR,     DT_CHARPTR,     DT_INTEGER,     DT_INTEGER,     DT_INTEGER,
586                                  DT_INTEGER,    DT_INTEGER,     DT_INTEGER,     DT_INTEGER,     DT_INTEGER},
587                                 InitConnect, DoConnect, DoneConnect},
588
589                 {"uline",
590                                 {"server",      NULL},
591                                 {DT_CHARPTR},
592                                 InitULine,DoULine,DoneULine},
593
594                 {"banlist",
595                                 {"chan",        "limit",        NULL},
596                                 {DT_CHARPTR,    DT_INTEGER},
597                                 InitMaxBans, DoMaxBans, DoneMaxBans},
598
599                 {"module",
600                                 {"name",        NULL},
601                                 {DT_CHARPTR},
602                                 InitModule, DoModule, DoneModule},
603
604                 {"badip",
605                                 {"reason",      "ipmask",       NULL},
606                                 {DT_CHARPTR,    DT_CHARPTR},
607                                 InitXLine, DoZLine, DoneXLine},
608
609                 {"badnick",
610                                 {"reason",      "nick",         NULL},
611                                 {DT_CHARPTR,    DT_CHARPTR},
612                                 InitXLine, DoQLine, DoneXLine},
613
614                 {"badhost",
615                                 {"reason",      "host",         NULL},
616                                 {DT_CHARPTR,    DT_CHARPTR},
617                                 InitXLine, DoKLine, DoneXLine},
618
619                 {"exception",
620                                 {"reason",      "host",         NULL},
621                                 {DT_CHARPTR,    DT_CHARPTR},
622                                 InitXLine, DoELine, DoneXLine},
623
624                 {"type",
625                                 {"name",        "classes",      NULL},
626                                 {DT_CHARPTR,    DT_CHARPTR},
627                                 InitTypes, DoType, DoneClassesAndTypes},
628
629                 {"class",
630                                 {"name",        "commands",     NULL},
631                                 {DT_CHARPTR,    DT_CHARPTR},
632                                 InitClasses, DoClass, DoneClassesAndTypes},
633
634                 {NULL}
635         };
636
637         include_stack.clear();
638
639         /* Load and parse the config file, if there are any errors then explode */
640         
641         /* Make a copy here so if it fails then we can carry on running with an unaffected config */
642         ConfigDataHash newconfig;
643         
644         if (this->LoadConf(newconfig, CONFIG_FILE, errstr))
645         {
646                 /* If we succeeded, set the ircd config to the new one */
647                 this->config_data = newconfig;
648                 
649 /*              int c = 1;
650                 std::string last;
651                 
652                 for(ConfigDataHash::const_iterator i = this->config_data.begin(); i != this->config_data.end(); i++)
653                 {
654                         c = (i->first != last) ? 1 : c+1;
655                         last = i->first;
656                         
657                         std::cout << "[" << i->first << " " << c << "/" << this->config_data.count(i->first) << "]" << std::endl;
658                         
659                         for(KeyValList::const_iterator j = i->second.begin(); j != i->second.end(); j++)
660                                 std::cout << "\t" << j->first << " = " << j->second << std::endl;
661                         
662                         std::cout << "[/" << i->first << " " << c << "/" << this->config_data.count(i->first) << "]" << std::endl;
663                 }
664  */     }
665         else
666         {
667                 log(DEFAULT, "There were errors in your configuration:\n%s", errstr.str().c_str());
668
669                 if (bail)
670                 {
671                         /* Unneeded because of the log() aboive? */
672                         printf("There were errors in your configuration:\n%s",errstr.str().c_str());
673                         Exit(0);
674                 }
675                 else
676                 {
677                         std::string errors = errstr.str();
678                         std::string::size_type start;
679                         unsigned int prefixlen;
680                         
681                         start = 0;
682                         /* ":ServerInstance->Config->ServerName NOTICE user->nick :" */
683                         prefixlen = strlen(this->ServerName) + strlen(user->nick) + 11;
684         
685                         if (user)
686                         {
687                                 user->WriteServ("NOTICE %s :There were errors in the configuration file:",user->nick);
688                                 
689                                 while(start < errors.length())
690                                 {
691                                         user->WriteServ("NOTICE %s :%s",user->nick, errors.substr(start, 510 - prefixlen).c_str());
692                                         start += 510 - prefixlen;
693                                 }
694                         }
695                         else
696                         {
697                                 WriteOpers("There were errors in the configuration file:");
698                                 
699                                 while(start < errors.length())
700                                 {
701                                         WriteOpers(errors.substr(start, 360).c_str());
702                                         start += 360;
703                                 }
704                         }
705
706                         return;
707                 }
708         }
709
710         /* Check we dont have more than one of singular tags, or any of them missing
711          */
712         for (int Index = 0; Once[Index]; Index++)
713                 if (!CheckOnce(Once[Index],bail,user))
714                         return;
715
716         /* Read the values of all the tags which occur once or not at all, and call their callbacks.
717          */
718         for (int Index = 0; Values[Index].tag; Index++)
719         {
720                 int* val_i = (int*) Values[Index].val;
721                 char* val_c = (char*) Values[Index].val;
722
723                 switch (Values[Index].datatype)
724                 {
725                         case DT_CHARPTR:
726                                 /* Assuming MAXBUF here, potentially unsafe */
727                                 ConfValue(this->config_data, Values[Index].tag, Values[Index].value, 0, val_c, MAXBUF);
728                         break;
729
730                         case DT_INTEGER:
731                                 ConfValueInteger(this->config_data, Values[Index].tag, Values[Index].value, 0, *val_i);
732                         break;
733
734                         case DT_BOOLEAN:
735                                 *val_i = ConfValueBool(this->config_data, Values[Index].tag, Values[Index].value, 0);
736                         break;
737
738                         case DT_NOTHING:
739                         break;
740                 }
741
742                 Values[Index].validation_function(this, Values[Index].tag, Values[Index].value, Values[Index].val);
743         }
744
745         /* Claim memory for use when reading multiple tags
746          */
747         for (int n = 0; n < 12; n++)
748                 data[n] = new char[MAXBUF];
749
750         /* Read the multiple-tag items (class tags, connect tags, etc)
751          * and call the callbacks associated with them. We have three
752          * callbacks for these, a 'start', 'item' and 'end' callback.
753          */
754         
755         /* XXX - Make this use ConfValueInteger and so on */
756         for (int Index = 0; MultiValues[Index].tag; Index++)
757         {
758                 MultiValues[Index].init_function(this, MultiValues[Index].tag);
759
760                 int number_of_tags = ConfValueEnum(this->config_data, MultiValues[Index].tag);
761
762                 for (int tagnum = 0; tagnum < number_of_tags; tagnum++)
763                 {
764                         for (int valuenum = 0; MultiValues[Index].items[valuenum]; valuenum++)
765                         {
766                                 ConfValue(this->config_data, MultiValues[Index].tag, MultiValues[Index].items[valuenum], tagnum, data[valuenum], MAXBUF);
767
768                                 switch (MultiValues[Index].datatype[valuenum])
769                                 {
770                                         case DT_CHARPTR:
771                                                 ptr[valuenum] = data[valuenum];
772                                         break;
773                                         case DT_INTEGER:
774                                                 r_i[valuenum] = atoi(data[valuenum]);
775                                                 ptr[valuenum] = &r_i[valuenum];
776                                         break;
777                                         case DT_BOOLEAN:
778                                                 r_i[valuenum] = ((*data[valuenum] == tolower('y')) || (*data[valuenum] == tolower('t')) || (*data[valuenum] == '1'));
779                                                 ptr[valuenum] = &r_i[valuenum];
780                                         break;
781                                         default:
782                                         break;
783                                 }
784                         }
785                         MultiValues[Index].validation_function(this, MultiValues[Index].tag, (char**)MultiValues[Index].items, ptr, MultiValues[Index].datatype);
786                 }
787
788                 MultiValues[Index].finish_function(this, MultiValues[Index].tag);
789         }
790
791         /* Free any memory we claimed
792          */
793         for (int n = 0; n < 12; n++)
794                 delete[] data[n];
795
796         // write once here, to try it out and make sure its ok
797         ServerInstance->WritePID(this->PID);
798
799         log(DEFAULT,"Done reading configuration file, InspIRCd is now starting.");
800
801         /* If we're rehashing, let's load any new modules, and unload old ones
802          */
803         if (!bail)
804         {
805                 ServerInstance->stats->BoundPortCount = BindPorts(false);
806
807                 if (!removed_modules.empty())
808                         for (std::vector<std::string>::iterator removing = removed_modules.begin(); removing != removed_modules.end(); removing++)
809                         {
810                                 if (ServerInstance->UnloadModule(removing->c_str()))
811                                 {
812                                         WriteOpers("*** REHASH UNLOADED MODULE: %s",removing->c_str());
813
814                                         if (user)
815                                                 user->WriteServ("973 %s %s :Module %s successfully unloaded.",user->nick, removing->c_str(), removing->c_str());
816
817                                         rem++;
818                                 }
819                                 else
820                                 {
821                                         if (user)
822                                                 user->WriteServ("972 %s %s :Failed to unload module %s: %s",user->nick, removing->c_str(), removing->c_str(), ServerInstance->ModuleError());
823                                 }
824                         }
825
826                 if (!added_modules.empty())
827                 for (std::vector<std::string>::iterator adding = added_modules.begin(); adding != added_modules.end(); adding++)
828                 {
829                         if (ServerInstance->LoadModule(adding->c_str()))
830                         {
831                                 WriteOpers("*** REHASH LOADED MODULE: %s",adding->c_str());
832
833                                 if (user)
834                                         user->WriteServ("975 %s %s :Module %s successfully loaded.",user->nick, adding->c_str(), adding->c_str());
835
836                                 add++;
837                         }
838                         else
839                         {
840                                 if (user)
841                                         user->WriteServ("974 %s %s :Failed to load module %s: %s",user->nick, adding->c_str(), adding->c_str(), ServerInstance->ModuleError());
842                         }
843                 }
844
845                 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());
846         }
847 }
848
849 bool ServerConfig::LoadConf(ConfigDataHash &target, const char* filename, std::ostringstream &errorstream)
850 {
851         std::ifstream conf(filename);
852         std::string line;
853         char ch;
854         long linenumber;
855         bool in_tag;
856         bool in_quote;
857         bool in_comment;
858         
859         linenumber = 1;
860         in_tag = false;
861         in_quote = false;
862         in_comment = false;
863         
864         /* Check if the file open failed first */
865         if (!conf)
866         {
867                 errorstream << "LoadConf: Couldn't open config file: " << filename << std::endl;
868                 return false;
869         }
870         
871         /* Fix the chmod of the file to restrict it to the current user and group */
872         chmod(filename,0600);
873         
874         for (unsigned int t = 0; t < include_stack.size(); t++)
875         {
876                 if (std::string(filename) == include_stack[t])
877                 {
878                         errorstream << "File " << filename << " is included recursively (looped inclusion)." << std::endl;
879                         return false;
880                 }
881         }
882         
883         /* It's not already included, add it to the list of files we've loaded */
884         include_stack.push_back(filename);
885         
886         /* Start reading characters... */       
887         while(conf.get(ch))
888         {
889                 /*
890                  * Here we try and get individual tags on separate lines,
891                  * this would be so easy if we just made people format
892                  * their config files like that, but they don't so...
893                  * We check for a '<' and then know the line is over when
894                  * we get a '>' not inside quotes. If we find two '<' and
895                  * no '>' then die with an error.
896                  */
897                 
898                 if((ch == '#') && !in_quote)
899                         in_comment = true;
900                 
901                 if(((ch == '\n') || (ch == '\r')) && in_quote)
902                 {
903                         errorstream << "Got a newline within a quoted section, this is probably a typo: " << filename << ":" << linenumber << std::endl;
904                         return false;
905                 }
906                 
907                 switch(ch)
908                 {
909                         case '\n':
910                                 linenumber++;
911                         case '\r':
912                                 in_comment = false;
913                         case '\0':
914                                 continue;
915                         case '\t':
916                                 ch = ' ';
917                 }
918                 
919                 if(in_comment)
920                         continue;
921
922                 /* XXX: Added by Brain, May 1st 2006 - Escaping of characters.
923                  * Note that this WILL NOT usually allow insertion of newlines,
924                  * because a newline is two characters long. Use it primarily to
925                  * insert the " symbol.
926                  *
927                  * Note that this also involves a further check when parsing the line,
928                  * which can be found below.
929                  */
930                 if ((ch == '\\') && (in_quote) && (in_tag))
931                 {
932                         line += ch;
933                         log(DEBUG,"Escape sequence in config line.");
934                         char real_character;
935                         if (conf.get(real_character))
936                         {
937                                 log(DEBUG,"Escaping %c", real_character);
938                                 line += real_character;
939                                 continue;
940                         }
941                         else
942                         {
943                                 errorstream << "End of file after a \\, what did you want to escape?: " << filename << ":" << linenumber << std::endl;
944                                 return false;
945                         }
946                 }
947
948                 line += ch;
949                 
950                 if(ch == '<')
951                 {
952                         if(in_tag)
953                         {
954                                 if(!in_quote)
955                                 {
956                                         errorstream << "Got another opening < when the first one wasn't closed: " << filename << ":" << linenumber << std::endl;
957                                         return false;
958                                 }
959                         }
960                         else
961                         {
962                                 if(in_quote)
963                                 {
964                                         errorstream << "We're in a quote but outside a tag, interesting. " << filename << ":" << linenumber << std::endl;
965                                         return false;
966                                 }
967                                 else
968                                 {
969                                         // errorstream << "Opening new config tag on line " << linenumber << std::endl;
970                                         in_tag = true;
971                                 }
972                         }
973                 }
974                 else if(ch == '"')
975                 {
976                         if(in_tag)
977                         {
978                                 if(in_quote)
979                                 {
980                                         // errorstream << "Closing quote in config tag on line " << linenumber << std::endl;
981                                         in_quote = false;
982                                 }
983                                 else
984                                 {
985                                         // errorstream << "Opening quote in config tag on line " << linenumber << std::endl;
986                                         in_quote = true;
987                                 }
988                         }
989                         else
990                         {
991                                 if(in_quote)
992                                 {
993                                         errorstream << "Found a (closing) \" outside a tag: " << filename << ":" << linenumber << std::endl;
994                                 }
995                                 else
996                                 {
997                                         errorstream << "Found a (opening) \" outside a tag: " << filename << ":" << linenumber << std::endl;
998                                 }
999                         }
1000                 }
1001                 else if(ch == '>')
1002                 {
1003                         if(!in_quote)
1004                         {
1005                                 if(in_tag)
1006                                 {
1007                                         // errorstream << "Closing config tag on line " << linenumber << std::endl;
1008                                         in_tag = false;
1009
1010                                         /*
1011                                          * If this finds an <include> then ParseLine can simply call
1012                                          * LoadConf() and load the included config into the same ConfigDataHash
1013                                          */
1014                                         
1015                                         if(!this->ParseLine(target, line, linenumber, errorstream))
1016                                                 return false;
1017                                         
1018                                         line.clear();
1019                                 }
1020                                 else
1021                                 {
1022                                         errorstream << "Got a closing > when we weren't inside a tag: " << filename << ":" << linenumber << std::endl;
1023                                         return false;
1024                                 }
1025                         }
1026                 }
1027         }
1028         
1029         return true;
1030 }
1031
1032 bool ServerConfig::LoadConf(ConfigDataHash &target, const std::string &filename, std::ostringstream &errorstream)
1033 {
1034         return this->LoadConf(target, filename.c_str(), errorstream);
1035 }
1036
1037 bool ServerConfig::ParseLine(ConfigDataHash &target, std::string &line, long linenumber, std::ostringstream &errorstream)
1038 {
1039         std::string tagname;
1040         std::string current_key;
1041         std::string current_value;
1042         KeyValList results;
1043         bool got_name;
1044         bool got_key;
1045         bool in_quote;
1046         
1047         got_name = got_key = in_quote = false;
1048         
1049         // std::cout << "ParseLine(data, '" << line << "', " << linenumber << ", stream)" << std::endl;
1050         
1051         for(std::string::iterator c = line.begin(); c != line.end(); c++)
1052         {
1053                 if(!got_name)
1054                 {
1055                         /* We don't know the tag name yet. */
1056                         
1057                         if(*c != ' ')
1058                         {
1059                                 if(*c != '<')
1060                                 {
1061                                         tagname += *c;
1062                                 }
1063                         }
1064                         else
1065                         {
1066                                 /* We got to a space, we should have the tagname now. */
1067                                 if(tagname.length())
1068                                 {
1069                                         got_name = true;
1070                                 }
1071                         }
1072                 }
1073                 else
1074                 {
1075                         /* We have the tag name */
1076                         if (!got_key)
1077                         {
1078                                 /* We're still reading the key name */
1079                                 if (*c != '=')
1080                                 {
1081                                         if (*c != ' ')
1082                                         {
1083                                                 current_key += *c;
1084                                         }
1085                                 }
1086                                 else
1087                                 {
1088                                         /* We got an '=', end of the key name. */
1089                                         got_key = true;
1090                                 }
1091                         }
1092                         else
1093                         {
1094                                 /* We have the key name, now we're looking for quotes and the value */
1095
1096                                 /* Correctly handle escaped characters here.
1097                                  * See the XXX'ed section above.
1098                                  */
1099                                 if ((*c == '\\') && (in_quote))
1100                                 {
1101                                         c++;
1102                                         current_value += *c;
1103                                         continue;
1104                                 }
1105                                 if (*c == '"')
1106                                 {
1107                                         if (!in_quote)
1108                                         {
1109                                                 /* We're not already in a quote. */
1110                                                 in_quote = true;
1111                                         }
1112                                         else
1113                                         {
1114                                                 /* Leaving quotes, we have the value */
1115                                                 results.push_back(KeyVal(current_key, current_value));
1116                                                 
1117                                                 // std::cout << "<" << tagname << ":" << current_key << "> " << current_value << std::endl;
1118                                                 
1119                                                 in_quote = false;
1120                                                 got_key = false;
1121                                                 
1122                                                 if((tagname == "include") && (current_key == "file"))
1123                                                 {
1124                                                         if(!this->DoInclude(target, current_value, errorstream))
1125                                                                 return false;
1126                                                 }
1127                                                 
1128                                                 current_key.clear();
1129                                                 current_value.clear();
1130                                         }
1131                                 }
1132                                 else
1133                                 {
1134                                         if(in_quote)
1135                                         {
1136                                                 current_value += *c;
1137                                         }
1138                                 }
1139                         }
1140                 }
1141         }
1142         
1143         /* Finished parsing the tag, add it to the config hash */
1144         target.insert(std::pair<std::string, KeyValList > (tagname, results));
1145         
1146         return true;
1147 }
1148
1149 bool ServerConfig::DoInclude(ConfigDataHash &target, const std::string &file, std::ostringstream &errorstream)
1150 {
1151         std::string confpath;
1152         std::string newfile;
1153         std::string::size_type pos;
1154         
1155         confpath = CONFIG_FILE;
1156         newfile = file;
1157         
1158         for (std::string::iterator c = newfile.begin(); c != newfile.end(); c++)
1159         {
1160                 if (*c == '\\')
1161                 {
1162                         *c = '/';
1163                 }
1164         }
1165
1166         if (file[0] != '/')
1167         {
1168                 if((pos = confpath.find("/inspircd.conf")) != std::string::npos)
1169                 {
1170                         /* Leaves us with just the path */
1171                         newfile = confpath.substr(0, pos) + std::string("/") + newfile;
1172                 }
1173                 else
1174                 {
1175                         errorstream << "Couldn't get config path from: " << confpath << std::endl;
1176                         return false;
1177                 }
1178         }
1179         
1180         return LoadConf(target, newfile, errorstream);
1181 }
1182
1183 bool ServerConfig::ConfValue(ConfigDataHash &target, const char* tag, const char* var, int index, char* result, int length)
1184 {
1185         std::string value;
1186         bool r = ConfValue(target, std::string(tag), std::string(var), index, value);
1187         strlcpy(result, value.c_str(), length);
1188         return r;
1189 }
1190
1191 bool ServerConfig::ConfValue(ConfigDataHash &target, const std::string &tag, const std::string &var, int index, std::string &result)
1192 {
1193         ConfigDataHash::size_type pos = index;
1194         if((pos >= 0) && (pos < target.count(tag)))
1195         {
1196                 ConfigDataHash::const_iterator iter = target.find(tag);
1197                 
1198                 for(int i = 0; i < index; i++)
1199                         iter++;
1200                 
1201                 for(KeyValList::const_iterator j = iter->second.begin(); j != iter->second.end(); j++)
1202                 {
1203                         if(j->first == var)
1204                         {
1205                                 result = j->second;
1206                                 return true;
1207                         }
1208                 }
1209         }
1210         else if(pos == 0)
1211         {
1212                 log(DEBUG, "No <%s> tags in config file.", tag.c_str());
1213         }
1214         else
1215         {
1216                 log(DEBUG, "ConfValue got an out-of-range index %d, there are only %d occurences of %s", pos, target.count(tag), tag.c_str());
1217         }
1218         
1219         return false;
1220 }
1221         
1222 bool ServerConfig::ConfValueInteger(ConfigDataHash &target, const char* tag, const char* var, int index, int &result)
1223 {
1224         return ConfValueInteger(target, std::string(tag), std::string(var), index, result);
1225 }
1226
1227 bool ServerConfig::ConfValueInteger(ConfigDataHash &target, const std::string &tag, const std::string &var, int index, int &result)
1228 {
1229         std::string value;
1230         std::istringstream stream;
1231         bool r = ConfValue(target, tag, var, index, value);
1232         stream.str(value);
1233         if(!(stream >> result))
1234                 return false;
1235         return r;
1236 }
1237         
1238 bool ServerConfig::ConfValueBool(ConfigDataHash &target, const char* tag, const char* var, int index)
1239 {
1240         return ConfValueBool(target, std::string(tag), std::string(var), index);
1241 }
1242
1243 bool ServerConfig::ConfValueBool(ConfigDataHash &target, const std::string &tag, const std::string &var, int index)
1244 {
1245         std::string result;
1246         if(!ConfValue(target, tag, var, index, result))
1247                 return false;
1248         
1249         return ((result == "yes") || (result == "true") || (result == "1"));
1250 }
1251         
1252 int ServerConfig::ConfValueEnum(ConfigDataHash &target, const char* tag)
1253 {
1254         return target.count(tag);
1255 }
1256
1257 int ServerConfig::ConfValueEnum(ConfigDataHash &target, const std::string &tag)
1258 {
1259         return target.count(tag);
1260 }
1261         
1262 int ServerConfig::ConfVarEnum(ConfigDataHash &target, const char* tag, int index)
1263 {
1264         return ConfVarEnum(target, std::string(tag), index);
1265 }
1266
1267 int ServerConfig::ConfVarEnum(ConfigDataHash &target, const std::string &tag, int index)
1268 {
1269         ConfigDataHash::size_type pos = index;
1270         
1271         if((pos >= 0) && (pos < target.count(tag)))
1272         {
1273                 ConfigDataHash::const_iterator iter = target.find(tag);
1274                 
1275                 for(int i = 0; i < index; i++)
1276                         iter++;
1277                 
1278                 return iter->second.size();
1279         }
1280         else if(pos == 0)
1281         {
1282                 log(DEBUG, "No <%s> tags in config file.", tag.c_str());
1283         }
1284         else
1285         {
1286                 log(DEBUG, "ConfVarEnum got an out-of-range index %d, there are only %d occurences of %s", pos, target.count(tag), tag.c_str());
1287         }
1288         
1289         return 0;
1290 }
1291
1292 /** Read the contents of a file located by `fname' into a file_cache pointed at by `F'.
1293  */
1294 bool ServerConfig::ReadFile(file_cache &F, const char* fname)
1295 {
1296         FILE* file;
1297         char linebuf[MAXBUF];
1298
1299         F.clear();
1300         file =  fopen(fname,"r");
1301
1302         if (file)
1303         {
1304                 while (!feof(file))
1305                 {
1306                         fgets(linebuf,sizeof(linebuf),file);
1307                         linebuf[strlen(linebuf)-1]='\0';
1308
1309                         if (!*linebuf)
1310                         {
1311                                 strcpy(linebuf," ");
1312                         }
1313
1314                         if (!feof(file))
1315                         {
1316                                 F.push_back(linebuf);
1317                         }
1318                 }
1319
1320                 fclose(file);
1321         }
1322         else
1323                 return false;
1324
1325         return true;
1326 }
1327