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