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