]> git.netwichtig.de Git - user/henk/code/inspircd.git/blob - src/configreader.cpp
37153afdd945641f7826f1eb2e72bafaf3d23168
[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);
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                 data.Set(std::string(data.GetString()) + ".");
260         }
261         return true;
262 }
263
264 bool ValidateNetBufferSize(ServerConfig* conf, const char* tag, const char* value, ValueItem &data)
265 {
266         if ((!data.GetInteger()) || (data.GetInteger() > 65535) || (data.GetInteger() < 1024))
267         {
268                 conf->GetInstance()->Log(DEFAULT,"No NetBufferSize specified or size out of range, setting to default of 10240.");
269                 data.Set(10240);
270         }
271         return true;
272 }
273
274 bool ValidateMaxWho(ServerConfig* conf, const char* tag, const char* value, ValueItem &data)
275 {
276         if ((!data.GetInteger()) || (data.GetInteger() > 65535) || (data.GetInteger() < 1))
277         {
278                 conf->GetInstance()->Log(DEFAULT,"No MaxWhoResults specified or size out of range, setting to default of 128.");
279                 data.Set(128);
280         }
281         return true;
282 }
283
284 bool ValidateLogLevel(ServerConfig* conf, const char* tag, const char* value, ValueItem &data)
285 {
286         std::string dbg = data.GetString();
287         conf->LogLevel = DEFAULT;
288
289         if (dbg == "debug")
290                 conf->LogLevel = DEBUG;
291         else if (dbg  == "verbose")
292                 conf->LogLevel = VERBOSE;
293         else if (dbg == "default")
294                 conf->LogLevel = DEFAULT;
295         else if (dbg == "sparse")
296                 conf->LogLevel = SPARSE;
297         else if (dbg == "none")
298                 conf->LogLevel = NONE;
299
300         conf->debugging = (conf->LogLevel == DEBUG);
301
302         return true;
303 }
304
305 bool ValidateMotd(ServerConfig* conf, const char* tag, const char* value, ValueItem &data)
306 {
307         conf->ReadFile(conf->MOTD, data.GetString());
308         return true;
309 }
310
311 bool ValidateRules(ServerConfig* conf, const char* tag, const char* value, ValueItem &data)
312 {
313         conf->ReadFile(conf->RULES, data.GetString());
314         return true;
315 }
316
317 bool ValidateWhoWas(ServerConfig* conf, const char* tag, const char* value, ValueItem &data)
318 {
319         conf->WhoWasMaxKeep = conf->GetInstance()->Duration(data.GetString());
320
321         if (conf->WhoWasGroupSize < 0)
322                 conf->WhoWasGroupSize = 0;
323
324         if (conf->WhoWasMaxGroups < 0)
325                 conf->WhoWasMaxGroups = 0;
326
327         if (conf->WhoWasMaxKeep < 3600)
328         {
329                 conf->WhoWasMaxKeep = 3600;
330                 conf->GetInstance()->Log(DEFAULT,"WARNING: <whowas:maxkeep> value less than 3600, setting to default 3600");
331         }
332         conf->GetInstance()->Log(DEBUG,"whowas:groupsize:%d maxgroups:%d maxkeep:%d",conf->WhoWasGroupSize,conf->WhoWasMaxGroups,conf->WhoWasMaxKeep);
333         irc::whowas::PruneWhoWas(conf->GetInstance(), conf->GetInstance()->Time());
334         return true;
335 }
336
337 /* Callback called before processing the first <connect> tag
338  */
339 bool InitConnect(ServerConfig* conf, const char* tag)
340 {
341         conf->GetInstance()->Log(DEFAULT,"Reading connect classes...");
342         conf->Classes.clear();
343         return true;
344 }
345
346 /* Callback called to process a single <connect> tag
347  */
348 bool DoConnect(ServerConfig* conf, const char* tag, char** entries, ValueList &values, int* types)
349 {
350         ConnectClass c;
351         const char* allow = values[0].GetString(); /* Yeah, there are a lot of values. Live with it. */
352         const char* deny = values[1].GetString();
353         const char* password = values[2].GetString();
354         int timeout = values[3].GetInteger();
355         int pingfreq = values[4].GetInteger();
356         int flood = values[5].GetInteger();
357         int threshold = values[6].GetInteger();
358         int sendq = values[7].GetInteger();
359         int recvq = values[8].GetInteger();
360         int localmax = values[9].GetInteger();
361         int globalmax = values[10].GetInteger();
362
363         if (*allow)
364         {
365                 c.host = allow;
366                 c.type = CC_ALLOW;
367                 c.pass = password;
368                 c.registration_timeout = timeout;
369                 c.pingtime = pingfreq;
370                 c.flood = flood;
371                 c.threshold = threshold;
372                 c.sendqmax = sendq;
373                 c.recvqmax = recvq;
374                 c.maxlocal = localmax;
375                 c.maxglobal = globalmax;
376
377
378                 if (c.maxlocal == 0)
379                         c.maxlocal = 3;
380                 if (c.maxglobal == 0)
381                         c.maxglobal = 3;
382                 if (c.threshold == 0)
383                 {
384                         c.threshold = 1;
385                         c.flood = 999;
386                         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());
387                 }
388                 if (c.sendqmax == 0)
389                         c.sendqmax = 262114;
390                 if (c.recvqmax == 0)
391                         c.recvqmax = 4096;
392                 if (c.registration_timeout == 0)
393                         c.registration_timeout = 90;
394                 if (c.pingtime == 0)
395                         c.pingtime = 120;
396                 conf->Classes.push_back(c);
397         }
398         else
399         {
400                 c.host = deny;
401                 c.type = CC_DENY;
402                 conf->Classes.push_back(c);
403                 conf->GetInstance()->Log(DEBUG,"Read connect class type DENY, host=%s",deny);
404         }
405
406         return true;
407 }
408
409 /* Callback called when there are no more <connect> tags
410  */
411 bool DoneConnect(ServerConfig* conf, const char* tag)
412 {
413         conf->GetInstance()->Log(DEBUG,"DoneConnect called for tag: %s",tag);
414         return true;
415 }
416
417 /* Callback called before processing the first <uline> tag
418  */
419 bool InitULine(ServerConfig* conf, const char* tag)
420 {
421         conf->ulines.clear();
422         return true;
423 }
424
425 /* Callback called to process a single <uline> tag
426  */
427 bool DoULine(ServerConfig* conf, const char* tag, char** entries, ValueList &values, int* types)
428 {
429         const char* server = values[0].GetString();
430         conf->GetInstance()->Log(DEBUG,"Read ULINE '%s'",server);
431         conf->ulines.push_back(server);
432         return true;
433 }
434
435 /* Callback called when there are no more <uline> tags
436  */
437 bool DoneULine(ServerConfig* conf, const char* tag)
438 {
439         return true;
440 }
441
442 /* Callback called before processing the first <module> tag
443  */
444 bool InitModule(ServerConfig* conf, const char* tag)
445 {
446         old_module_names.clear();
447         new_module_names.clear();
448         added_modules.clear();
449         removed_modules.clear();
450         for (std::vector<std::string>::iterator t = conf->module_names.begin(); t != conf->module_names.end(); t++)
451         {
452                 old_module_names.push_back(*t);
453         }
454         return true;
455 }
456
457 /* Callback called to process a single <module> tag
458  */
459 bool DoModule(ServerConfig* conf, const char* tag, char** entries, ValueList &values, int* types)
460 {
461         const char* modname = values[0].GetString();
462         new_module_names.push_back(modname);
463         return true;
464 }
465
466 /* Callback called when there are no more <module> tags
467  */
468 bool DoneModule(ServerConfig* conf, const char* tag)
469 {
470         // now create a list of new modules that are due to be loaded
471         // and a seperate list of modules which are due to be unloaded
472         for (std::vector<std::string>::iterator _new = new_module_names.begin(); _new != new_module_names.end(); _new++)
473         {
474                 bool added = true;
475
476                 for (std::vector<std::string>::iterator old = old_module_names.begin(); old != old_module_names.end(); old++)
477                 {
478                         if (*old == *_new)
479                                 added = false;
480                 }
481
482                 if (added)
483                         added_modules.push_back(*_new);
484         }
485
486         for (std::vector<std::string>::iterator oldm = old_module_names.begin(); oldm != old_module_names.end(); oldm++)
487         {
488                 bool removed = true;
489                 for (std::vector<std::string>::iterator newm = new_module_names.begin(); newm != new_module_names.end(); newm++)
490                 {
491                         if (*newm == *oldm)
492                                 removed = false;
493                 }
494
495                 if (removed)
496                         removed_modules.push_back(*oldm);
497         }
498         return true;
499 }
500
501 /* Callback called before processing the first <banlist> tag
502  */
503 bool InitMaxBans(ServerConfig* conf, const char* tag)
504 {
505         conf->maxbans.clear();
506         return true;
507 }
508
509 /* Callback called to process a single <banlist> tag
510  */
511 bool DoMaxBans(ServerConfig* conf, const char* tag, char** entries, ValueList &values, int* types)
512 {
513         const char* channel = values[0].GetString();
514         int limit = values[1].GetInteger();
515         conf->maxbans[channel] = limit;
516         return true;
517 }
518
519 /* Callback called when there are no more <banlist> tags.
520  */
521 bool DoneMaxBans(ServerConfig* conf, const char* tag)
522 {
523         return true;
524 }
525
526 void ServerConfig::Read(bool bail, userrec* user)
527 {
528         static char debug[MAXBUF];      /* Temporary buffer for debugging value */
529         static char maxkeep[MAXBUF];    /* Temporary buffer for WhoWasMaxKeep value */
530         int rem = 0, add = 0;           /* Number of modules added, number of modules removed */
531         std::ostringstream errstr;      /* String stream containing the error output */
532
533         /* These tags MUST occur and must ONLY occur once in the config file */
534         static char* Once[] = { "server", "admin", "files", "power", "options", NULL };
535
536         /* These tags can occur ONCE or not at all */
537         static InitialConfig Values[] = {
538                 {"options",             "softlimit",                    &this->SoftLimit,               DT_INTEGER, ValidateSoftLimit},
539                 {"options",             "somaxconn",                    &this->MaxConn,                 DT_INTEGER, ValidateMaxConn},
540                 {"server",              "name",                         &this->ServerName,              DT_CHARPTR, ValidateServerName},
541                 {"server",              "description",                  &this->ServerDesc,              DT_CHARPTR, NoValidation},
542                 {"server",              "network",                      &this->Network,                 DT_CHARPTR, NoValidation},
543                 {"admin",               "name",                         &this->AdminName,               DT_CHARPTR, NoValidation},
544                 {"admin",               "email",                        &this->AdminEmail,              DT_CHARPTR, NoValidation},
545                 {"admin",               "nick",                         &this->AdminNick,               DT_CHARPTR, NoValidation},
546                 {"files",               "motd",                         &this->motd,                    DT_CHARPTR, ValidateMotd},
547                 {"files",               "rules",                        &this->rules,                   DT_CHARPTR, ValidateRules},
548                 {"power",               "diepass",                      &this->diepass,                 DT_CHARPTR, NoValidation},      
549                 {"power",               "pause",                        &this->DieDelay,                DT_INTEGER, NoValidation},
550                 {"power",               "restartpass",                  &this->restartpass,             DT_CHARPTR, NoValidation},
551                 {"options",             "prefixquit",                   &this->PrefixQuit,              DT_CHARPTR, NoValidation},
552                 {"die",                 "value",                        &this->DieValue,                DT_CHARPTR, NoValidation},
553                 {"options",             "loglevel",                     &debug,                         DT_CHARPTR, ValidateLogLevel},
554                 {"options",             "netbuffersize",                &this->NetBufferSize,           DT_INTEGER, ValidateNetBufferSize},
555                 {"options",             "maxwho",                       &this->MaxWhoResults,           DT_INTEGER, ValidateMaxWho},
556                 {"options",             "allowhalfop",                  &this->AllowHalfop,             DT_BOOLEAN, NoValidation},
557                 {"dns",                 "server",                       &this->DNSServer,               DT_CHARPTR, ValidateDnsServer},
558                 {"dns",                 "timeout",                      &this->dns_timeout,             DT_INTEGER, ValidateDnsTimeout},
559                 {"options",             "moduledir",                    &this->ModPath,                 DT_CHARPTR, ValidateModPath},
560                 {"disabled",            "commands",                     &this->DisabledCommands,        DT_CHARPTR, NoValidation},
561                 {"options",             "userstats",                    &this->UserStats,               DT_CHARPTR, NoValidation},
562                 {"options",             "customversion",                &this->CustomVersion,           DT_CHARPTR, NoValidation},
563                 {"options",             "hidesplits",                   &this->HideSplits,              DT_BOOLEAN, NoValidation},
564                 {"options",             "hidebans",                     &this->HideBans,                DT_BOOLEAN, NoValidation},
565                 {"options",             "hidewhois",                    &this->HideWhoisServer,         DT_CHARPTR, NoValidation},
566                 {"options",             "operspywhois",                 &this->OperSpyWhois,            DT_BOOLEAN, NoValidation},
567                 {"options",             "tempdir",                      &this->TempDir,                 DT_CHARPTR, ValidateTempDir},
568                 {"options",             "nouserdns",                    &this->NoUserDns,               DT_BOOLEAN, NoValidation},
569                 {"options",             "syntaxhints",                  &this->SyntaxHints,             DT_BOOLEAN, NoValidation},
570                 {"options",             "cyclehosts",                   &this->CycleHosts,              DT_BOOLEAN, NoValidation},
571                 {"pid",                 "file",                         &this->PID,                     DT_CHARPTR, NoValidation},
572                 {"whowas",              "groupsize",                    &this->WhoWasGroupSize,         DT_INTEGER, NoValidation},
573                 {"whowas",              "maxgroups",                    &this->WhoWasMaxGroups,         DT_INTEGER, NoValidation},
574                 {"whowas",              "maxkeep",                      &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                                 strlcpy((char*)Values[Index].val, vi.GetString(), MAXBUF);
715                         break;
716                         case DT_INTEGER:
717                                 *((int*)Values[Index].val) = vi.GetInteger();
718                         break;
719                         case DT_BOOLEAN:
720                                 *((bool*)(Values[Index].val)) = vi.GetBool();
721                         break;
722                         default:
723                         break;
724                 }
725         }
726
727         /* Read the multiple-tag items (class tags, connect tags, etc)
728          * and call the callbacks associated with them. We have three
729          * callbacks for these, a 'start', 'item' and 'end' callback.
730          */
731         for (int Index = 0; MultiValues[Index].tag; Index++)
732         {
733                 MultiValues[Index].init_function(this, MultiValues[Index].tag);
734
735                 int number_of_tags = ConfValueEnum(this->config_data, MultiValues[Index].tag);
736
737                 for (int tagnum = 0; tagnum < number_of_tags; tagnum++)
738                 {
739                         ValueList vl;
740                         for (int valuenum = 0; MultiValues[Index].items[valuenum]; valuenum++)
741                         {
742                                 switch (MultiValues[Index].datatype[valuenum])
743                                 {
744                                         case DT_CHARPTR:
745                                                 {
746                                                         char item[MAXBUF];
747                                                         ConfValue(this->config_data, MultiValues[Index].tag, MultiValues[Index].items[valuenum], tagnum, item, MAXBUF);
748                                                         ServerInstance->Log(DEBUG,"Data type DT_CHARPTR multi-item <%s:%s>[%d] = '%s'", MultiValues[Index].tag, MultiValues[Index].items[valuenum],tagnum, item);
749                                                         vl.push_back(ValueItem(item));
750                                                 }
751                                         break;
752                                         case DT_INTEGER:
753                                                 {
754                                                         int item;
755                                                         ConfValueInteger(this->config_data, MultiValues[Index].tag, MultiValues[Index].items[valuenum], tagnum, item);
756                                                         ServerInstance->Log(DEBUG,"Data type DT_INTEGER multi-item <%s:%s>[%d] = '%d'", MultiValues[Index].tag, MultiValues[Index].items[valuenum],tagnum, item);
757                                                         vl.push_back(ValueItem(item));
758                                                 }
759                                         break;
760                                         case DT_BOOLEAN:
761                                                 {
762                                                         bool item = ConfValueBool(this->config_data, MultiValues[Index].tag, MultiValues[Index].items[valuenum], tagnum);
763                                                         ServerInstance->Log(DEBUG,"Data type DT_BOOLEAN multi-item <%s:%s>[%d] = '%d'", MultiValues[Index].tag, MultiValues[Index].items[valuenum],tagnum, item);
764                                                         vl.push_back(ValueItem(item));
765                                                 }
766                                         break;
767                                         default:
768                                         break;
769                                 }
770                         }
771                         ServerInstance->Log(DEBUG,"Call validation function for multi-value <%s>", MultiValues[Index].tag);
772                         MultiValues[Index].validation_function(this, MultiValues[Index].tag, (char**)MultiValues[Index].items, vl, MultiValues[Index].datatype);
773                 }
774
775                 MultiValues[Index].finish_function(this, MultiValues[Index].tag);
776         }
777
778         // write once here, to try it out and make sure its ok
779         ServerInstance->WritePID(this->PID);
780
781         ServerInstance->Log(DEFAULT,"Done reading configuration file.");
782
783         /* If we're rehashing, let's load any new modules, and unload old ones
784          */
785         if (!bail)
786         {
787                 int found_ports = 0;
788                 FailedPortList pl;
789                 ServerInstance->stats->BoundPortCount = ServerInstance->BindPorts(false, found_ports, pl);
790
791                 if (pl.size())
792                 {
793                         user->WriteServ("NOTICE %s :*** Not all your client ports could be bound.", user->nick);
794                         user->WriteServ("NOTICE %s :*** The following port%s failed to bind:", user->nick, found_ports - ServerInstance->stats->BoundPortCount != 1 ? "s" : "");
795                         int j = 1;
796                         for (FailedPortList::iterator i = pl.begin(); i != pl.end(); i++, j++)
797                         {
798                                 user->WriteServ("NOTICE %s :*** %d.   IP: %s     Port: %lu", user->nick, j, i->first.empty() ? "<all>" : i->first.c_str(), (unsigned long)i->second);
799                         }
800                 }
801
802                 if (!removed_modules.empty())
803                         for (std::vector<std::string>::iterator removing = removed_modules.begin(); removing != removed_modules.end(); removing++)
804                         {
805                                 if (ServerInstance->UnloadModule(removing->c_str()))
806                                 {
807                                         ServerInstance->WriteOpers("*** REHASH UNLOADED MODULE: %s",removing->c_str());
808
809                                         if (user)
810                                                 user->WriteServ("973 %s %s :Module %s successfully unloaded.",user->nick, removing->c_str(), removing->c_str());
811
812                                         rem++;
813                                 }
814                                 else
815                                 {
816                                         if (user)
817                                                 user->WriteServ("972 %s %s :Failed to unload module %s: %s",user->nick, removing->c_str(), removing->c_str(), ServerInstance->ModuleError());
818                                 }
819                         }
820
821                 if (!added_modules.empty())
822                 for (std::vector<std::string>::iterator adding = added_modules.begin(); adding != added_modules.end(); adding++)
823                 {
824                         if (ServerInstance->LoadModule(adding->c_str()))
825                         {
826                                 ServerInstance->WriteOpers("*** REHASH LOADED MODULE: %s",adding->c_str());
827
828                                 if (user)
829                                         user->WriteServ("975 %s %s :Module %s successfully loaded.",user->nick, adding->c_str(), adding->c_str());
830
831                                 add++;
832                         }
833                         else
834                         {
835                                 if (user)
836                                         user->WriteServ("974 %s %s :Failed to load module %s: %s",user->nick, adding->c_str(), adding->c_str(), ServerInstance->ModuleError());
837                         }
838                 }
839
840                 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());
841         }
842 }
843
844 bool ServerConfig::LoadConf(ConfigDataHash &target, const char* filename, std::ostringstream &errorstream)
845 {
846         std::ifstream conf(filename);
847         std::string line;
848         char ch;
849         long linenumber;
850         bool in_tag;
851         bool in_quote;
852         bool in_comment;
853         
854         linenumber = 1;
855         in_tag = false;
856         in_quote = false;
857         in_comment = false;
858         
859         /* Check if the file open failed first */
860         if (!conf)
861         {
862                 errorstream << "LoadConf: Couldn't open config file: " << filename << std::endl;
863                 return false;
864         }
865         
866         /* Fix the chmod of the file to restrict it to the current user and group */
867         chmod(filename,0600);
868         
869         for (unsigned int t = 0; t < include_stack.size(); t++)
870         {
871                 if (std::string(filename) == include_stack[t])
872                 {
873                         errorstream << "File " << filename << " is included recursively (looped inclusion)." << std::endl;
874                         return false;
875                 }
876         }
877         
878         /* It's not already included, add it to the list of files we've loaded */
879         include_stack.push_back(filename);
880         
881         /* Start reading characters... */       
882         while(conf.get(ch))
883         {
884                 /*
885                  * Here we try and get individual tags on separate lines,
886                  * this would be so easy if we just made people format
887                  * their config files like that, but they don't so...
888                  * We check for a '<' and then know the line is over when
889                  * we get a '>' not inside quotes. If we find two '<' and
890                  * no '>' then die with an error.
891                  */
892                 
893                 if((ch == '#') && !in_quote)
894                         in_comment = true;
895                 
896                 if(((ch == '\n') || (ch == '\r')) && in_quote)
897                 {
898                         errorstream << "Got a newline within a quoted section, this is probably a typo: " << filename << ":" << linenumber << std::endl;
899                         return false;
900                 }
901                 
902                 switch(ch)
903                 {
904                         case '\n':
905                                 linenumber++;
906                         case '\r':
907                                 in_comment = false;
908                         case '\0':
909                                 continue;
910                         case '\t':
911                                 ch = ' ';
912                 }
913                 
914                 if(in_comment)
915                         continue;
916
917                 /* XXX: Added by Brain, May 1st 2006 - Escaping of characters.
918                  * Note that this WILL NOT usually allow insertion of newlines,
919                  * because a newline is two characters long. Use it primarily to
920                  * insert the " symbol.
921                  *
922                  * Note that this also involves a further check when parsing the line,
923                  * which can be found below.
924                  */
925                 if ((ch == '\\') && (in_quote) && (in_tag))
926                 {
927                         line += ch;
928                         ServerInstance->Log(DEBUG,"Escape sequence in config line.");
929                         char real_character;
930                         if (conf.get(real_character))
931                         {
932                                 ServerInstance->Log(DEBUG,"Escaping %c", real_character);
933                                 if (real_character == 'n')
934                                         real_character = '\n';
935                                 line += real_character;
936                                 continue;
937                         }
938                         else
939                         {
940                                 errorstream << "End of file after a \\, what did you want to escape?: " << filename << ":" << linenumber << std::endl;
941                                 return false;
942                         }
943                 }
944
945                 line += ch;
946                 
947                 if(ch == '<')
948                 {
949                         if(in_tag)
950                         {
951                                 if(!in_quote)
952                                 {
953                                         errorstream << "Got another opening < when the first one wasn't closed: " << filename << ":" << linenumber << std::endl;
954                                         return false;
955                                 }
956                         }
957                         else
958                         {
959                                 if(in_quote)
960                                 {
961                                         errorstream << "We're in a quote but outside a tag, interesting. " << filename << ":" << linenumber << std::endl;
962                                         return false;
963                                 }
964                                 else
965                                 {
966                                         // errorstream << "Opening new config tag on line " << linenumber << std::endl;
967                                         in_tag = true;
968                                 }
969                         }
970                 }
971                 else if(ch == '"')
972                 {
973                         if(in_tag)
974                         {
975                                 if(in_quote)
976                                 {
977                                         // errorstream << "Closing quote in config tag on line " << linenumber << std::endl;
978                                         in_quote = false;
979                                 }
980                                 else
981                                 {
982                                         // errorstream << "Opening quote in config tag on line " << linenumber << std::endl;
983                                         in_quote = true;
984                                 }
985                         }
986                         else
987                         {
988                                 if(in_quote)
989                                 {
990                                         errorstream << "Found a (closing) \" outside a tag: " << filename << ":" << linenumber << std::endl;
991                                 }
992                                 else
993                                 {
994                                         errorstream << "Found a (opening) \" outside a tag: " << filename << ":" << linenumber << std::endl;
995                                 }
996                         }
997                 }
998                 else if(ch == '>')
999                 {
1000                         if(!in_quote)
1001                         {
1002                                 if(in_tag)
1003                                 {
1004                                         // errorstream << "Closing config tag on line " << linenumber << std::endl;
1005                                         in_tag = false;
1006
1007                                         /*
1008                                          * If this finds an <include> then ParseLine can simply call
1009                                          * LoadConf() and load the included config into the same ConfigDataHash
1010                                          */
1011                                         
1012                                         if(!this->ParseLine(target, line, linenumber, errorstream))
1013                                                 return false;
1014                                         
1015                                         line.clear();
1016                                 }
1017                                 else
1018                                 {
1019                                         errorstream << "Got a closing > when we weren't inside a tag: " << filename << ":" << linenumber << std::endl;
1020                                         return false;
1021                                 }
1022                         }
1023                 }
1024         }
1025         
1026         return true;
1027 }
1028
1029 bool ServerConfig::LoadConf(ConfigDataHash &target, const std::string &filename, std::ostringstream &errorstream)
1030 {
1031         return this->LoadConf(target, filename.c_str(), errorstream);
1032 }
1033
1034 bool ServerConfig::ParseLine(ConfigDataHash &target, std::string &line, long linenumber, std::ostringstream &errorstream)
1035 {
1036         std::string tagname;
1037         std::string current_key;
1038         std::string current_value;
1039         KeyValList results;
1040         bool got_name;
1041         bool got_key;
1042         bool in_quote;
1043         
1044         got_name = got_key = in_quote = false;
1045         
1046         // std::cout << "ParseLine(data, '" << line << "', " << linenumber << ", stream)" << std::endl;
1047         
1048         for(std::string::iterator c = line.begin(); c != line.end(); c++)
1049         {
1050                 if(!got_name)
1051                 {
1052                         /* We don't know the tag name yet. */
1053                         
1054                         if(*c != ' ')
1055                         {
1056                                 if(*c != '<')
1057                                 {
1058                                         tagname += *c;
1059                                 }
1060                         }
1061                         else
1062                         {
1063                                 /* We got to a space, we should have the tagname now. */
1064                                 if(tagname.length())
1065                                 {
1066                                         got_name = true;
1067                                 }
1068                         }
1069                 }
1070                 else
1071                 {
1072                         /* We have the tag name */
1073                         if (!got_key)
1074                         {
1075                                 /* We're still reading the key name */
1076                                 if (*c != '=')
1077                                 {
1078                                         if (*c != ' ')
1079                                         {
1080                                                 current_key += *c;
1081                                         }
1082                                 }
1083                                 else
1084                                 {
1085                                         /* We got an '=', end of the key name. */
1086                                         got_key = true;
1087                                 }
1088                         }
1089                         else
1090                         {
1091                                 /* We have the key name, now we're looking for quotes and the value */
1092
1093                                 /* Correctly handle escaped characters here.
1094                                  * See the XXX'ed section above.
1095                                  */
1096                                 if ((*c == '\\') && (in_quote))
1097                                 {
1098                                         c++;
1099                                         if (*c == 'n')
1100                                                 current_value += '\n';
1101                                         else
1102                                                 current_value += *c;
1103                                         continue;
1104                                 }
1105                                 if (*c == '"')
1106                                 {
1107                                         if (!in_quote)
1108                                         {
1109                                                 /* We're not already in a quote. */
1110                                                 in_quote = true;
1111                                         }
1112                                         else
1113                                         {
1114                                                 /* Leaving quotes, we have the value */
1115                                                 results.push_back(KeyVal(current_key, current_value));
1116                                                 
1117                                                 // std::cout << "<" << tagname << ":" << current_key << "> " << current_value << std::endl;
1118                                                 
1119                                                 in_quote = false;
1120                                                 got_key = false;
1121                                                 
1122                                                 if((tagname == "include") && (current_key == "file"))
1123                                                 {
1124                                                         if(!this->DoInclude(target, current_value, errorstream))
1125                                                                 return false;
1126                                                 }
1127                                                 
1128                                                 current_key.clear();
1129                                                 current_value.clear();
1130                                         }
1131                                 }
1132                                 else
1133                                 {
1134                                         if(in_quote)
1135                                         {
1136                                                 current_value += *c;
1137                                         }
1138                                 }
1139                         }
1140                 }
1141         }
1142         
1143         /* Finished parsing the tag, add it to the config hash */
1144         target.insert(std::pair<std::string, KeyValList > (tagname, results));
1145         
1146         return true;
1147 }
1148
1149 bool ServerConfig::DoInclude(ConfigDataHash &target, const std::string &file, std::ostringstream &errorstream)
1150 {
1151         std::string confpath;
1152         std::string newfile;
1153         std::string::size_type pos;
1154         
1155         confpath = CONFIG_FILE;
1156         newfile = file;
1157         
1158         for (std::string::iterator c = newfile.begin(); c != newfile.end(); c++)
1159         {
1160                 if (*c == '\\')
1161                 {
1162                         *c = '/';
1163                 }
1164         }
1165
1166         if (file[0] != '/')
1167         {
1168                 if((pos = confpath.find("/inspircd.conf")) != std::string::npos)
1169                 {
1170                         /* Leaves us with just the path */
1171                         newfile = confpath.substr(0, pos) + std::string("/") + newfile;
1172                 }
1173                 else
1174                 {
1175                         errorstream << "Couldn't get config path from: " << confpath << std::endl;
1176                         return false;
1177                 }
1178         }
1179         
1180         return LoadConf(target, newfile, errorstream);
1181 }
1182
1183 bool ServerConfig::ConfValue(ConfigDataHash &target, const char* tag, const char* var, int index, char* result, int length)
1184 {
1185         std::string value;
1186         bool r = ConfValue(target, std::string(tag), std::string(var), index, value);
1187         strlcpy(result, value.c_str(), length);
1188         return r;
1189 }
1190
1191 bool ServerConfig::ConfValue(ConfigDataHash &target, const std::string &tag, const std::string &var, int index, std::string &result)
1192 {
1193         ConfigDataHash::size_type pos = index;
1194         if((pos >= 0) && (pos < target.count(tag)))
1195         {
1196                 ConfigDataHash::const_iterator iter = target.find(tag);
1197                 
1198                 for(int i = 0; i < index; i++)
1199                         iter++;
1200                 
1201                 for(KeyValList::const_iterator j = iter->second.begin(); j != iter->second.end(); j++)
1202                 {
1203                         if(j->first == var)
1204                         {
1205                                 result = j->second;
1206                                 return true;
1207                         }
1208                 }
1209         }
1210         else if(pos == 0)
1211         {
1212                 ServerInstance->Log(DEBUG, "No <%s> tags in config file.", tag.c_str());
1213         }
1214         else
1215         {
1216                 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());
1217         }
1218         
1219         return false;
1220 }
1221         
1222 bool ServerConfig::ConfValueInteger(ConfigDataHash &target, const char* tag, const char* var, int index, int &result)
1223 {
1224         return ConfValueInteger(target, std::string(tag), std::string(var), index, result);
1225 }
1226
1227 bool ServerConfig::ConfValueInteger(ConfigDataHash &target, const std::string &tag, const std::string &var, int index, int &result)
1228 {
1229         std::string value;
1230         std::istringstream stream;
1231         bool r = ConfValue(target, tag, var, index, value);
1232         stream.str(value);
1233         if(!(stream >> result))
1234                 return false;
1235         return r;
1236 }
1237         
1238 bool ServerConfig::ConfValueBool(ConfigDataHash &target, const char* tag, const char* var, int index)
1239 {
1240         return ConfValueBool(target, std::string(tag), std::string(var), index);
1241 }
1242
1243 bool ServerConfig::ConfValueBool(ConfigDataHash &target, const std::string &tag, const std::string &var, int index)
1244 {
1245         std::string result;
1246         if(!ConfValue(target, tag, var, index, result))
1247                 return false;
1248         
1249         return ((result == "yes") || (result == "true") || (result == "1"));
1250 }
1251         
1252 int ServerConfig::ConfValueEnum(ConfigDataHash &target, const char* tag)
1253 {
1254         return target.count(tag);
1255 }
1256
1257 int ServerConfig::ConfValueEnum(ConfigDataHash &target, const std::string &tag)
1258 {
1259         return target.count(tag);
1260 }
1261         
1262 int ServerConfig::ConfVarEnum(ConfigDataHash &target, const char* tag, int index)
1263 {
1264         return ConfVarEnum(target, std::string(tag), index);
1265 }
1266
1267 int ServerConfig::ConfVarEnum(ConfigDataHash &target, const std::string &tag, int index)
1268 {
1269         ConfigDataHash::size_type pos = index;
1270         
1271         if((pos >= 0) && (pos < target.count(tag)))
1272         {
1273                 ConfigDataHash::const_iterator iter = target.find(tag);
1274                 
1275                 for(int i = 0; i < index; i++)
1276                         iter++;
1277                 
1278                 return iter->second.size();
1279         }
1280         else if(pos == 0)
1281         {
1282                 ServerInstance->Log(DEBUG, "No <%s> tags in config file.", tag.c_str());
1283         }
1284         else
1285         {
1286                 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());
1287         }
1288         
1289         return 0;
1290 }
1291
1292 /** Read the contents of a file located by `fname' into a file_cache pointed at by `F'.
1293  */
1294 bool ServerConfig::ReadFile(file_cache &F, const char* fname)
1295 {
1296         FILE* file;
1297         char linebuf[MAXBUF];
1298
1299         F.clear();
1300         
1301         if (*fname != '/')
1302         {
1303                 std::string::size_type pos;
1304                 std::string confpath = CONFIG_FILE;
1305                 if((pos = confpath.find("/inspircd.conf")) != std::string::npos)
1306                 {
1307                         /* Leaves us with just the path */
1308                         std::string newfile = confpath.substr(0, pos) + std::string("/") + fname;
1309                         file =  fopen(newfile.c_str(), "r");
1310                         
1311                 }
1312         }
1313         else
1314                 file =  fopen(fname, "r");
1315
1316         if (file)
1317         {
1318                 while (!feof(file))
1319                 {
1320                         fgets(linebuf, sizeof(linebuf), file);
1321                         linebuf[strlen(linebuf)-1] = 0;
1322
1323                         if (!feof(file))
1324                         {
1325                                 F.push_back(*linebuf ? linebuf : " ");
1326                         }
1327                 }
1328
1329                 fclose(file);
1330         }
1331         else
1332                 return false;
1333
1334         return true;
1335 }
1336
1337 bool ServerConfig::FileExists(const char* file)
1338 {
1339         FILE *input;
1340         if ((input = fopen (file, "r")) == NULL)
1341         {
1342                 return false;
1343         }
1344         else
1345         {
1346                 fclose(input);
1347                 return true;
1348         }
1349 }
1350
1351 char* ServerConfig::CleanFilename(char* name)
1352 {
1353         char* p = name + strlen(name);
1354         while ((p != name) && (*p != '/')) p--;
1355         return (p != name ? ++p : p);
1356 }
1357
1358
1359 bool ServerConfig::DirValid(const char* dirandfile)
1360 {
1361         char work[MAXBUF];
1362         char buffer[MAXBUF];
1363         char otherdir[MAXBUF];
1364         int p;
1365
1366         strlcpy(work, dirandfile, MAXBUF);
1367         p = strlen(work);
1368
1369         // we just want the dir
1370         while (*work)
1371         {
1372                 if (work[p] == '/')
1373                 {
1374                         work[p] = '\0';
1375                         break;
1376                 }
1377
1378                 work[p--] = '\0';
1379         }
1380
1381         // Get the current working directory
1382         if (getcwd(buffer, MAXBUF ) == NULL )
1383                 return false;
1384
1385         chdir(work);
1386
1387         if (getcwd(otherdir, MAXBUF ) == NULL )
1388                 return false;
1389
1390         chdir(buffer);
1391
1392         size_t t = strlen(work);
1393
1394         if (strlen(otherdir) >= t)
1395         {
1396                 otherdir[t] = '\0';
1397
1398                 if (!strcmp(otherdir,work))
1399                 {
1400                         return true;
1401                 }
1402
1403                 return false;
1404         }
1405         else
1406         {
1407                 return false;
1408         }
1409 }
1410
1411 std::string ServerConfig::GetFullProgDir(char** argv, int argc)
1412 {
1413         char work[MAXBUF];
1414         char buffer[MAXBUF];
1415         char otherdir[MAXBUF];
1416         int p;
1417
1418         strlcpy(work,argv[0],MAXBUF);
1419         p = strlen(work);
1420
1421         // we just want the dir
1422         while (*work)
1423         {
1424                 if (work[p] == '/')
1425                 {
1426                         work[p] = '\0';
1427                         break;
1428                 }
1429
1430                 work[p--] = '\0';
1431         }
1432
1433         // Get the current working directory
1434         if (getcwd(buffer, MAXBUF) == NULL)
1435                 return "";
1436
1437         chdir(work);
1438
1439         if (getcwd(otherdir, MAXBUF) == NULL)
1440                 return "";
1441
1442         chdir(buffer);
1443         return otherdir;
1444 }
1445
1446 InspIRCd* ServerConfig::GetInstance()
1447 {
1448         return ServerInstance;
1449 }
1450