]> git.netwichtig.de Git - user/henk/code/inspircd.git/blob - src/configreader.cpp
Add ModeHandler::cull() for auto-deletion, fixes call of virtual method on partially...
[user/henk/code/inspircd.git] / src / configreader.cpp
1 /*       +------------------------------------+
2  *       | Inspire Internet Relay Chat Daemon |
3  *       +------------------------------------+
4  *
5  *  InspIRCd: (C) 2002-2009 InspIRCd Development Team
6  * See: http://wiki.inspircd.org/Credits
7  *
8  * This program is free but copyrighted software; see
9  *          the file COPYING for details.
10  *
11  * ---------------------------------------------------
12  */
13
14 /* $Core */
15 /* $CopyInstall: conf/inspircd.quotes.example $(CONPATH) */
16 /* $CopyInstall: conf/inspircd.rules.example $(CONPATH) */
17 /* $CopyInstall: conf/inspircd.motd.example $(CONPATH) */
18 /* $CopyInstall: conf/inspircd.helpop-full.example $(CONPATH) */
19 /* $CopyInstall: conf/inspircd.helpop.example $(CONPATH) */
20 /* $CopyInstall: conf/inspircd.censor.example $(CONPATH) */
21 /* $CopyInstall: conf/inspircd.filter.example $(CONPATH) */
22 /* $CopyInstall: conf/inspircd.conf.example $(CONPATH) */
23 /* $CopyInstall: conf/modules.conf.example $(CONPATH) */
24 /* $CopyInstall: conf/opers.conf.example $(CONPATH) */
25 /* $CopyInstall: conf/links.conf.example $(CONPATH) */
26 /* $CopyInstall: .gdbargs $(BASE) */
27
28 #include "inspircd.h"
29 #include <fstream>
30 #include "xline.h"
31 #include "exitcodes.h"
32 #include "commands/cmd_whowas.h"
33 #include "modes/cmode_h.h"
34
35 static void ReqRead(ServerConfig* src, const std::string& tag, const std::string& key, std::string& dest)
36 {
37         ConfigTag* t = src->ConfValue(tag);
38         if (!t || !t->readString(key, dest))
39                 throw CoreException("You must specify a value for <" + tag + ":" + key + ">");
40 }
41
42 /** Represents a deprecated configuration tag.
43  */
44 struct Deprecated
45 {
46         /** Tag name
47          */
48         const char* tag;
49         /** Tag value
50          */
51         const char* value;
52         /** Reason for deprecation
53          */
54         const char* reason;
55 };
56
57 ServerConfig::ServerConfig()
58 {
59         WhoWasGroupSize = WhoWasMaxGroups = WhoWasMaxKeep = 0;
60         log_file = NULL;
61         NoUserDns = forcedebug = OperSpyWhois = nofork = HideBans = HideSplits = UndernetMsgPrefix = false;
62         CycleHosts = writelog = AllowHalfop = InvBypassModes = true;
63         dns_timeout = DieDelay = 5;
64         MaxTargets = 20;
65         NetBufferSize = 10240;
66         SoftLimit = ServerInstance->SE->GetMaxFds();
67         MaxConn = SOMAXCONN;
68         MaxWhoResults = 0;
69         debugging = 0;
70         MaxChans = 20;
71         OperMaxChans = 30;
72         c_ipv4_range = 32;
73         c_ipv6_range = 128;
74 }
75
76 void ServerConfig::Update005()
77 {
78         std::stringstream out(data005);
79         std::string token;
80         std::string line5;
81         int token_counter = 0;
82         isupport.clear();
83         while (out >> token)
84         {
85                 line5 = line5 + token + " ";
86                 token_counter++;
87                 if (token_counter >= 13)
88                 {
89                         char buf[MAXBUF];
90                         snprintf(buf, MAXBUF, "%s:are supported by this server", line5.c_str());
91                         isupport.push_back(buf);
92                         line5.clear();
93                         token_counter = 0;
94                 }
95         }
96         if (!line5.empty())
97         {
98                 char buf[MAXBUF];
99                 snprintf(buf, MAXBUF, "%s:are supported by this server", line5.c_str());
100                 isupport.push_back(buf);
101         }
102 }
103
104 void ServerConfig::Send005(User* user)
105 {
106         for (std::vector<std::string>::iterator line = ServerInstance->Config->isupport.begin(); line != ServerInstance->Config->isupport.end(); line++)
107                 user->WriteNumeric(RPL_ISUPPORT, "%s %s", user->nick.c_str(), line->c_str());
108 }
109
110 template<typename T, typename V>
111 static void range(T& value, V min, V max, V def, const char* msg)
112 {
113         if (value >= (T)min && value <= (T)max)
114                 return;
115         ServerInstance->Logs->Log("CONFIG", DEFAULT,
116                 "WARNING: %s value of %ld is not between %ld and %ld; set to %ld.",
117                 msg, (long)value, (long)min, (long)max, (long)def);
118         value = def;
119 }
120
121 bool ServerConfig::CheckOnce(const char* tag)
122 {
123         if (!ConfValue(tag))
124                 throw CoreException("You have not defined a <"+std::string(tag)+"> tag, this is required.");
125         if (ConfValue(tag, 1))
126                 throw CoreException("You have more than one <"+std::string(tag)+"> tag, this is not permitted.");
127         return true;
128 }
129
130 /* NOTE: Before anyone asks why we're not using inet_pton for this, it is because inet_pton and friends do not return so much detail,
131  * even in strerror(errno). They just return 'yes' or 'no' to an address without such detail as to whats WRONG with the address.
132  * Because ircd users arent as technical as they used to be (;)) we are going to give more of a useful error message.
133  */
134 static void ValidIP(const std::string& ip, const std::string& key)
135 {
136         const char* p = ip.c_str();
137         int num_dots = 0;
138         int num_seps = 0;
139         int not_numbers = false;
140         int not_hex = false;
141
142         if (*p)
143         {
144                 if (*p == '.')
145                         throw CoreException("The value of "+key+" is not an IP address");
146
147                 for (const char* ptr = p; *ptr; ++ptr)
148                 {
149                         if (*ptr != ':' && *ptr != '.')
150                         {
151                                 if (*ptr < '0' || *ptr > '9')
152                                         not_numbers = true;
153                                 if ((*ptr < '0' || *ptr > '9') && (toupper(*ptr) < 'A' || toupper(*ptr) > 'F'))
154                                         not_hex = true;
155                         }
156                         switch (*ptr)
157                         {
158                                 case ' ':
159                                         throw CoreException("The value of "+key+" is not an IP address");
160                                 case '.':
161                                         num_dots++;
162                                 break;
163                                 case ':':
164                                         num_seps++;
165                                 break;
166                         }
167                 }
168
169                 if (num_dots > 3)
170                         throw CoreException("The value of "+key+" is an IPv4 address with too many fields!");
171
172                 if (num_seps > 8)
173                         throw CoreException("The value of "+key+" is an IPv6 address with too many fields!");
174
175                 if (num_seps == 0 && num_dots < 3)
176                         throw CoreException("The value of "+key+" looks to be a malformed IPv4 address");
177
178                 if (num_seps == 0 && num_dots == 3 && not_numbers)
179                         throw CoreException("The value of "+key+" contains non-numeric characters in an IPv4 address");
180
181                 if (num_seps != 0 && not_hex)
182                         throw CoreException("The value of "+key+" contains non-hexdecimal characters in an IPv6 address");
183
184                 if (num_seps != 0 && num_dots != 3 && num_dots != 0)
185                         throw CoreException("The value of "+key+" is a malformed IPv6 4in6 address");
186         }
187 }
188
189 static void ValidHost(const std::string& p, const std::string& msg)
190 {
191         int num_dots = 0;
192         if (p.empty() || p[0] == '.')
193                 throw CoreException("The value of "+msg+" is not a valid hostname");
194         for (unsigned int i=0;i < p.length();i++)
195         {
196                 switch (p[i])
197                 {
198                         case ' ':
199                                 throw CoreException("The value of "+msg+" is not a valid hostname");
200                         case '.':
201                                 num_dots++;
202                         break;
203                 }
204         }
205         if (num_dots == 0)
206                 throw CoreException("The value of "+msg+" is not a valid hostname");
207 }
208
209 // Specialized validators
210
211 bool ServerConfig::ApplyDisabledCommands(const std::string& data)
212 {
213         std::stringstream dcmds(data);
214         std::string thiscmd;
215
216         /* Enable everything first */
217         for (Commandtable::iterator x = ServerInstance->Parser->cmdlist.begin(); x != ServerInstance->Parser->cmdlist.end(); x++)
218                 x->second->Disable(false);
219
220         /* Now disable all the ones which the user wants disabled */
221         while (dcmds >> thiscmd)
222         {
223                 Commandtable::iterator cm = ServerInstance->Parser->cmdlist.find(thiscmd);
224                 if (cm != ServerInstance->Parser->cmdlist.end())
225                 {
226                         cm->second->Disable(true);
227                 }
228         }
229         return true;
230 }
231
232 #ifdef WINDOWS
233 // Note: the windows validator is in win32wrapper.cpp
234 void FindDNS(std::string& server);
235 #else
236 static void FindDNS(std::string& server)
237 {
238         if (!server.empty())
239                 return;
240
241         // attempt to look up their nameserver from /etc/resolv.conf
242         ServerInstance->Logs->Log("CONFIG",DEFAULT,"WARNING: <dns:server> not defined, attempting to find working server in /etc/resolv.conf...");
243
244         std::ifstream resolv("/etc/resolv.conf");
245
246         while (resolv >> server)
247         {
248                 if (server == "nameserver")
249                 {
250                         resolv >> server;
251                         ServerInstance->Logs->Log("CONFIG",DEFAULT,"<dns:server> set to '%s' as first resolver in /etc/resolv.conf.",server.c_str());
252                         return;
253                 }
254         }
255
256         ServerInstance->Logs->Log("CONFIG",DEFAULT,"/etc/resolv.conf contains no viable nameserver entries! Defaulting to nameserver '127.0.0.1'!");
257         server = "127.0.0.1";
258 }
259 #endif
260
261 static void ReadXLine(ServerConfig* conf, const std::string& tag, const std::string& key, XLineFactory* make)
262 {
263         for(int i=0;; ++i)
264         {
265                 ConfigTag* ctag = conf->ConfValue(tag, i);
266                 if (!ctag)
267                         break;
268                 std::string mask;
269                 if (!ctag->readString(key, mask))
270                         throw CoreException("<"+tag+":"+key+"> missing");
271                 std::string reason = ctag->getString("reason", "<Config>");
272                 XLine* xl = make->Generate(ServerInstance->Time(), 0, "<Config>", reason, mask);
273                 if (!ServerInstance->XLines->AddLine(xl, NULL))
274                         delete xl;
275         }
276 }
277
278 void ServerConfig::CrossCheckOperClassType()
279 {
280         for (int i = 0;; ++i)
281         {
282                 ConfigTag* tag = ConfValue("class", i);
283                 if (!tag)
284                         break;
285                 std::string name = tag->getString("name");
286                 if (name.empty())
287                         throw CoreException("<class:name> is required for all <class> tags");
288                 operclass[name] = tag;
289         }
290         for (int i = 0;; ++i)
291         {
292                 ConfigTag* tag = ConfValue("type", i);
293                 if (!tag)
294                         break;
295
296                 std::string name = tag->getString("name");
297                 if (name.empty())
298                         throw CoreException("<type:name> is required for all <type> tags");
299                 opertypes[name] = tag;
300
301                 std::string classname;
302                 irc::spacesepstream str(tag->getString("classes"));
303                 while (str.GetToken(classname))
304                 {
305                         if (operclass.find(classname) == operclass.end())
306                                 throw CoreException("Oper type " + name + " has missing class " + classname);
307                 }
308         }
309 }
310
311 void ServerConfig::CrossCheckConnectBlocks(ServerConfig* current)
312 {
313         typedef std::map<std::string, ConnectClass*> ClassMap;
314         ClassMap oldBlocksByMask;
315         if (current)
316         {
317                 for(ClassVector::iterator i = current->Classes.begin(); i != current->Classes.end(); ++i)
318                 {
319                         ConnectClass* c = *i;
320                         std::string typeMask = (c->type == CC_ALLOW) ? "a" : "d";
321                         typeMask += c->host;
322                         oldBlocksByMask[typeMask] = c;
323                 }
324         }
325
326         ClassMap newBlocksByMask;
327         std::map<std::string, int> names;
328
329         bool try_again = true;
330         for(int tries=0; try_again; tries++)
331         {
332                 try_again = false;
333                 for(unsigned int i=0;; i++)
334                 {
335                         ConfigTag* tag = ConfValue("connect", i);
336                         if (!tag)
337                                 break;
338                         if (Classes.size() <= i)
339                                 Classes.resize(i+1);
340                         if (Classes[i])
341                                 continue;
342
343                         ConnectClass* parent = NULL;
344                         std::string parentName = tag->getString("parent");
345                         if (!parentName.empty())
346                         {
347                                 std::map<std::string,int>::iterator parentIter = names.find(parentName);
348                                 if (parentIter == names.end())
349                                 {
350                                         try_again = true;
351                                         // couldn't find parent this time. If it's the last time, we'll never find it.
352                                         if (tries == 50)
353                                                 throw CoreException("Could not find parent connect class \"" + parentName + "\" for connect block " + ConvToStr(i));
354                                         continue;
355                                 }
356                                 parent = Classes[parentIter->second];
357                         }
358
359                         std::string name = tag->getString("name");
360                         if (!name.empty())
361                         {
362                                 if (names.find(name) != names.end())
363                                         throw CoreException("Two connect classes with name \"" + name + "\" defined!");
364                                 names[name] = i;
365                         }
366
367                         std::string mask, typeMask;
368                         char type;
369
370                         if (tag->readString("allow", mask, false))
371                         {
372                                 type = CC_ALLOW;
373                                 typeMask = 'a' + mask;
374                         }
375                         else if (tag->readString("deny", mask, false))
376                         {
377                                 type = CC_DENY;
378                                 typeMask = 'd' + mask;
379                         }
380                         else
381                         {
382                                 throw CoreException("Connect class must have an allow or deny mask (#" + ConvToStr(i) + ")");
383                         }
384                         ClassMap::iterator dupMask = newBlocksByMask.find(typeMask);
385                         if (dupMask != newBlocksByMask.end())
386                                 throw CoreException("Two connect classes cannot have the same mask (" + mask + ")");
387
388                         ConnectClass* me = parent ? 
389                                 new ConnectClass(tag, type, mask, *parent) :
390                                 new ConnectClass(tag, type, mask);
391
392                         if (!name.empty())
393                                 me->name = name;
394
395                         tag->readString("password", me->pass);
396                         tag->readString("hash", me->hash);
397                         me->registration_timeout = tag->getInt("timeout", me->registration_timeout);
398                         me->pingtime = tag->getInt("pingfreq", me->pingtime);
399                         std::string sendq;
400                         if (tag->readString("sendq", sendq))
401                         {
402                                 // attempt to guess a good hard/soft sendq from a single value
403                                 long value = atol(sendq.c_str());
404                                 if (value > 16384)
405                                         me->softsendqmax = value / 16;
406                                 else
407                                         me->softsendqmax = value;
408                                 me->hardsendqmax = value * 8;
409                         }
410                         me->softsendqmax = tag->getInt("softsendq", me->softsendqmax);
411                         me->hardsendqmax = tag->getInt("hardsendq", me->hardsendqmax);
412                         me->recvqmax = tag->getInt("recvq", me->recvqmax);
413                         me->maxlocal = tag->getInt("localmax", me->maxlocal);
414                         me->maxglobal = tag->getInt("globalmax", me->maxglobal);
415                         me->port = tag->getInt("port", me->port);
416                         me->maxchans = tag->getInt("maxchans", me->maxchans);
417                         me->limit = tag->getInt("limit", me->limit);
418
419                         ClassMap::iterator oldMask = oldBlocksByMask.find(typeMask);
420                         if (oldMask != oldBlocksByMask.end())
421                         {
422                                 ConnectClass* old = oldMask->second;
423                                 oldBlocksByMask.erase(oldMask);
424                                 old->Update(me);
425                                 delete me;
426                                 me = old;
427                         }
428                         newBlocksByMask[typeMask] = me;
429                         Classes[i] = me;
430                 }
431         }
432 }
433
434 static const Deprecated ChangedConfig[] = {
435         {"options", "hidelinks",                "has been moved to <security:hidelinks> as of 1.2a3"},
436         {"options", "hidewhois",                "has been moved to <security:hidewhois> as of 1.2a3"},
437         {"options", "userstats",                "has been moved to <security:userstats> as of 1.2a3"},
438         {"options", "customversion",    "has been moved to <security:customversion> as of 1.2a3"},
439         {"options", "hidesplits",               "has been moved to <security:hidesplits> as of 1.2a3"},
440         {"options", "hidebans",         "has been moved to <security:hidebans> as of 1.2a3"},
441         {"options", "hidekills",                "has been moved to <security:hidekills> as of 1.2a3"},
442         {"options", "operspywhois",             "has been moved to <security:operspywhois> as of 1.2a3"},
443         {"options", "announceinvites",  "has been moved to <security:announceinvites> as of 1.2a3"},
444         {"options", "hidemodes",                "has been moved to <security:hidemodes> as of 1.2a3"},
445         {"options", "maxtargets",               "has been moved to <security:maxtargets> as of 1.2a3"},
446         {"options",     "nouserdns",            "has been moved to <performance:nouserdns> as of 1.2a3"},
447         {"options",     "maxwho",               "has been moved to <performance:maxwho> as of 1.2a3"},
448         {"options",     "softlimit",            "has been moved to <performance:softlimit> as of 1.2a3"},
449         {"options", "somaxconn",                "has been moved to <performance:somaxconn> as of 1.2a3"},
450         {"options", "netbuffersize",    "has been moved to <performance:netbuffersize> as of 1.2a3"},
451         {"options", "maxwho",           "has been moved to <performance:maxwho> as of 1.2a3"},
452         {"options",     "loglevel",             "1.2 does not use the loglevel value. Please define <log> tags instead."},
453         {"die",     "value",            "has always been deprecated"},
454 };
455
456 void ServerConfig::Fill()
457 {
458         ReqRead(this, "server", "name", ServerName);
459         ReqRead(this, "power", "diepass", diepass);
460         ReqRead(this, "power", "restartpass", restartpass);
461
462         ConfigTag* options = ConfValue("options");
463         ConfigTag* security = ConfValue("security");
464         powerhash = ConfValue("power")->getString("hash");
465         DieDelay = ConfValue("power")->getInt("pause");
466         PrefixQuit = options->getString("prefixquit");
467         SuffixQuit = options->getString("suffixquit");
468         FixedQuit = options->getString("fixedquit");
469         PrefixPart = options->getString("prefixpart");
470         SuffixPart = options->getString("suffixpart");
471         FixedPart = options->getString("fixedpart");
472         SoftLimit = ConfValue("performance")->getInt("softlimit", ServerInstance->SE->GetMaxFds());
473         MaxConn = ConfValue("performance")->getInt("somaxconn", SOMAXCONN);
474         MoronBanner = options->getString("moronbanner", "You're banned!");
475         ServerDesc = ConfValue("server")->getString("description", "Configure Me");
476         Network = ConfValue("server")->getString("network", "Network");
477         sid = ConfValue("server")->getString("id", "");
478         AdminName = ConfValue("admin")->getString("name", "");
479         AdminEmail = ConfValue("admin")->getString("email", "null@example.com");
480         AdminNick = ConfValue("admin")->getString("nick", "admin");
481         ModPath = options->getString("moduledir", MOD_PATH);
482         NetBufferSize = ConfValue("performance")->getInt("netbuffersize", 10240);
483         MaxWhoResults = ConfValue("performance")->getInt("maxwho", 1024);
484         DNSServer = ConfValue("dns")->getString("server");
485         dns_timeout = ConfValue("dns")->getInt("timeout", 5);
486         DisabledCommands = ConfValue("disabled")->getString("commands", "");
487         DisabledDontExist = ConfValue("disabled")->getBool("fakenonexistant");
488         SetUser = security->getString("runasuser");
489         SetGroup = security->getString("runasgroup");
490         UserStats = security->getString("userstats");
491         CustomVersion = security->getString("customversion");
492         HideSplits = security->getBool("hidesplits");
493         HideBans = security->getBool("hidebans");
494         HideWhoisServer = security->getString("hidewhois");
495         HideKillsServer = security->getString("hidekills");
496         OperSpyWhois = security->getBool("operspywhois");
497         RestrictBannedUsers = security->getBool("restrictbannedusers", true);
498         GenericOper = security->getBool("genericoper");
499         NoUserDns = ConfValue("performance")->getBool("nouserdns");
500         SyntaxHints = options->getBool("syntaxhints");
501         CycleHosts = options->getBool("cyclehosts");
502         UndernetMsgPrefix = options->getBool("ircumsgprefix");
503         FullHostInTopic = options->getBool("hostintopic");
504         MaxTargets = security->getInt("maxtargets", 20);
505         DefaultModes = options->getString("defaultmodes", "nt");
506         PID = ConfValue("pid")->getString("file");
507         WhoWasGroupSize = ConfValue("whowas")->getInt("groupsize");
508         WhoWasMaxGroups = ConfValue("whowas")->getInt("maxgroups");
509         WhoWasMaxKeep = ServerInstance->Duration(ConfValue("whowas")->getString("maxkeep"));
510         DieValue = ConfValue("die")->getString("value");
511         MaxChans = ConfValue("channels")->getInt("users");
512         OperMaxChans = ConfValue("channels")->getInt("opers");
513         c_ipv4_range = ConfValue("cidr")->getInt("ipv4clone");
514         c_ipv6_range = ConfValue("cidr")->getInt("ipv6clone");
515         Limits.NickMax = ConfValue("limits")->getInt("maxnick", 32);
516         Limits.ChanMax = ConfValue("limits")->getInt("maxchan", 64);
517         Limits.MaxModes = ConfValue("limits")->getInt("maxmodes", 20);
518         Limits.IdentMax = ConfValue("limits")->getInt("maxident", 11);
519         Limits.MaxQuit = ConfValue("limits")->getInt("maxquit", 255);
520         Limits.MaxTopic = ConfValue("limits")->getInt("maxtopic", 307);
521         Limits.MaxKick = ConfValue("limits")->getInt("maxkick", 255);
522         Limits.MaxGecos = ConfValue("limits")->getInt("maxgecos", 128);
523         Limits.MaxAway = ConfValue("limits")->getInt("maxaway", 200);
524         InvBypassModes = options->getBool("invitebypassmodes", true);
525
526         range(SoftLimit, 10, ServerInstance->SE->GetMaxFds(), ServerInstance->SE->GetMaxFds(), "<performance:softlimit>");
527         range(MaxConn, 0, SOMAXCONN, SOMAXCONN, "<performance:somaxconn>");
528         range(MaxTargets, 1, 31, 20, "<security:maxtargets>");
529         range(NetBufferSize, 1024, 65534, 10240, "<performance:netbuffersize>");
530         range(MaxWhoResults, 1, 65535, 1024, "<performace:maxwho>");
531         range(WhoWasGroupSize, 0, 10000, 10, "<whowas:groupsize>");
532         range(WhoWasMaxGroups, 0, 1000000, 10240, "<whowas:maxgroups>");
533         range(WhoWasMaxKeep, 3600, INT_MAX, 3600, "<whowas:maxkeep>");
534
535         ValidIP(DNSServer, "<dns:server>");
536         ValidHost(ServerName, "<server:name>");
537         if (!sid.empty() && !ServerInstance->IsSID(sid))
538                 throw CoreException(sid + " is not a valid server ID. A server ID must be 3 characters long, with the first character a digit and the next two characters a digit or letter.");
539         
540         for (int i = 0;; ++i)
541         {
542                 ConfigTag* tag = ConfValue("uline", i);
543                 if (!tag)
544                         break;
545                 std::string server;
546                 if (!tag->readString("server", server))
547                         throw CoreException("<uline> tag missing server");
548                 ulines[assign(server)] = tag->getBool("silent");
549         }
550
551         for(int i=0;; ++i)
552         {
553                 ConfigTag* tag = ConfValue("banlist", i);
554                 if (!tag)
555                         break;
556                 std::string chan;
557                 if (!tag->readString("chan", chan))
558                         throw CoreException("<banlist> tag missing chan");
559                 maxbans[chan] = tag->getInt("limit");
560         }
561
562         ReadXLine(this, "badip", "ipmask", ServerInstance->XLines->GetFactory("Z"));
563         ReadXLine(this, "badnick", "nick", ServerInstance->XLines->GetFactory("Q"));
564         ReadXLine(this, "badhost", "host", ServerInstance->XLines->GetFactory("K"));
565         ReadXLine(this, "exception", "host", ServerInstance->XLines->GetFactory("E"));
566
567         memset(DisabledUModes, 0, sizeof(DisabledUModes));
568         for (const unsigned char* p = (const unsigned char*)ConfValue("disabled")->getString("usermodes").c_str(); *p; ++p)
569         {
570                 if (*p < 'A' || *p > ('A' + 64)) throw CoreException(std::string("Invalid usermode ")+(char)*p+" was found.");
571                 DisabledUModes[*p - 'A'] = 1;
572         }
573
574         memset(DisabledCModes, 0, sizeof(DisabledCModes));
575         for (const unsigned char* p = (const unsigned char*)ConfValue("disabled")->getString("chanmodes").c_str(); *p; ++p)
576         {
577                 if (*p < 'A' || *p > ('A' + 64)) throw CoreException(std::string("Invalid chanmode ")+(char)*p+" was found.");
578                 DisabledCModes[*p - 'A'] = 1;
579         }
580
581         memset(HideModeLists, 0, sizeof(HideModeLists));
582         for (const unsigned char* p = (const unsigned char*)ConfValue("security")->getString("hidemodes").c_str(); *p; ++p)
583                 HideModeLists[*p] = true;
584         
585         std::string v = security->getString("announceinvites");
586
587         if (v == "ops")
588                 AnnounceInvites = ServerConfig::INVITE_ANNOUNCE_OPS;
589         else if (v == "all")
590                 AnnounceInvites = ServerConfig::INVITE_ANNOUNCE_ALL;
591         else if (v == "dynamic")
592                 AnnounceInvites = ServerConfig::INVITE_ANNOUNCE_DYNAMIC;
593         else
594                 AnnounceInvites = ServerConfig::INVITE_ANNOUNCE_NONE;
595
596         bool AllowHalfOp = options->getBool("allowhalfop");
597         ModeHandler* mh = ServerInstance->Modes->FindMode('h', MODETYPE_CHANNEL);
598         if (AllowHalfOp && !mh) {
599                 ServerInstance->Logs->Log("CONFIG", DEFAULT, "Enabling halfop mode.");
600                 mh = new ModeChannelHalfOp;
601                 ServerInstance->Modes->AddMode(mh);
602         } else if (!AllowHalfOp && mh) {
603                 ServerInstance->Logs->Log("CONFIG", DEFAULT, "Disabling halfop mode.");
604                 ServerInstance->Modes->DelMode(mh);
605                 delete mh;
606         }
607
608         Module* whowas = ServerInstance->Modules->Find("cmd_whowas.so");
609         if (whowas)
610                 WhowasRequest(NULL, whowas, WhowasRequest::WHOWAS_PRUNE).Send();
611         Limits.Finalise();
612
613 }
614
615 /* These tags MUST occur and must ONLY occur once in the config file */
616 static const char* const Once[] = { "server", "admin", "files", "power", "options" };
617
618 // WARNING: it is not safe to use most of the codebase in this function, as it
619 // will run in the config reader thread
620 void ServerConfig::Read()
621 {
622         /* Load and parse the config file, if there are any errors then explode */
623
624         if (!this->DoInclude(ServerInstance->ConfigFileName, true))
625         {
626                 valid = false;
627                 return;
628         }
629 }
630
631 void ServerConfig::Apply(ServerConfig* old, const std::string &useruid)
632 {
633         valid = true;
634         /* std::ostringstream::clear() does not clear the string itself, only the error flags. */
635         errstr.clear();
636         errstr.str().clear();
637         include_stack.clear();
638
639         /* The stuff in here may throw CoreException, be sure we're in a position to catch it. */
640         try
641         {
642                 /* Check we dont have more than one of singular tags, or any of them missing
643                  */
644                 for (int Index = 0; Index * sizeof(*Once) < sizeof(Once); Index++)
645                         CheckOnce(Once[Index]);
646
647                 for (int Index = 0; Index * sizeof(Deprecated) < sizeof(ChangedConfig); Index++)
648                 {
649                         std::string dummy;
650                         if (ConfValue(ChangedConfig[Index].tag)->readString(ChangedConfig[Index].value, dummy, true))
651                                 throw CoreException(std::string("Your configuration contains a deprecated value: <") + ChangedConfig[Index].tag + ":" + ChangedConfig[Index].value + "> - " + ChangedConfig[Index].reason);
652                 }
653
654                 Fill();
655
656                 // Handle special items
657                 CrossCheckOperClassType();
658                 CrossCheckConnectBlocks(old);
659         }
660         catch (CoreException &ce)
661         {
662                 errstr << ce.GetReason();
663                 valid = false;
664         }
665
666         // write once here, to try it out and make sure its ok
667         ServerInstance->WritePID(this->PID);
668
669         /*
670          * These values can only be set on boot. Keep their old values. Do it before we send messages so we actually have a servername.
671          */
672         if (old)
673         {
674                 this->ServerName = old->ServerName;
675                 this->sid = old->sid;
676                 this->argv = old->argv;
677                 this->argc = old->argc;
678
679                 // Same for ports... they're bound later on first run.
680                 FailedPortList pl;
681                 ServerInstance->BindPorts(pl);
682                 if (pl.size())
683                 {
684                         errstr << "Not all your client ports could be bound.\nThe following port(s) failed to bind:\n";
685
686                         int j = 1;
687                         for (FailedPortList::iterator i = pl.begin(); i != pl.end(); i++, j++)
688                         {
689                                 char buf[MAXBUF];
690                                 snprintf(buf, MAXBUF, "%d.   Address: %s   Reason: %s\n", j, i->first.empty() ? "<all>" : i->first.c_str(), i->second.c_str());
691                                 errstr << buf;
692                         }
693                 }
694         }
695
696         User* user = useruid.empty() ? NULL : ServerInstance->FindNick(useruid);
697
698         valid = errstr.str().empty();
699         if (!valid)
700                 ServerInstance->Logs->Log("CONFIG",DEFAULT, "There were errors in your configuration file:");
701
702         while (errstr.good())
703         {
704                 std::string line;
705                 getline(errstr, line, '\n');
706                 if (!line.empty())
707                 {
708                         if (user)
709                                 user->WriteServ("NOTICE %s :*** %s", user->nick.c_str(), line.c_str());
710                         else
711                                 ServerInstance->SNO->WriteGlobalSno('a', line);
712                 }
713
714                 if (!old)
715                 {
716                         // Starting up, so print it out so it's seen. XXX this is a bit of a hack.
717                         printf("%s\n", line.c_str());
718                 }
719         }
720
721         errstr.clear();
722         errstr.str(std::string());
723
724         /* No old configuration -> initial boot, nothing more to do here */
725         if (!old)
726         {
727                 if (!valid)
728                 {
729                         ServerInstance->Exit(EXIT_STATUS_CONFIG);
730                 }
731
732                 return;
733         }
734
735         // If there were errors processing configuration, don't touch modules.
736         if (!valid)
737                 return;
738
739         ApplyModules(user);
740 }
741
742 void ServerConfig::ApplyModules(User* user)
743 {
744         const std::vector<std::string> v = ServerInstance->Modules->GetAllModuleNames(0);
745         std::vector<std::string> added_modules;
746         std::set<std::string> removed_modules(v.begin(), v.end());
747
748         for(int i=0; ; i++)
749         {
750                 ConfigTag* tag = ConfValue("module", i);
751                 if (!tag)
752                         break;
753                 std::string name;
754                 if (tag->readString("name", name))
755                 {
756                         // if this module is already loaded, the erase will succeed, so we need do nothing
757                         // otherwise, we need to add the module (which will be done later)
758                         if (removed_modules.erase(name) == 0)
759                                 added_modules.push_back(name);
760                 }
761         }
762
763         for (std::set<std::string>::iterator removing = removed_modules.begin(); removing != removed_modules.end(); removing++)
764         {
765                 // Don't remove cmd_*.so, just remove m_*.so
766                 if (removing->c_str()[0] == 'c')
767                         continue;
768                 Module* m = ServerInstance->Modules->Find(*removing);
769                 if (m && ServerInstance->Modules->Unload(m))
770                 {
771                         ServerInstance->SNO->WriteGlobalSno('a', "*** REHASH UNLOADED MODULE: %s",removing->c_str());
772
773                         if (user)
774                                 user->WriteNumeric(RPL_UNLOADEDMODULE, "%s %s :Module %s successfully unloaded.",user->nick.c_str(), removing->c_str(), removing->c_str());
775                         else
776                                 ServerInstance->SNO->WriteGlobalSno('a', "Module %s successfully unloaded.", removing->c_str());
777                 }
778                 else
779                 {
780                         if (user)
781                                 user->WriteNumeric(ERR_CANTUNLOADMODULE, "%s %s :Failed to unload module %s: %s",user->nick.c_str(), removing->c_str(), removing->c_str(), ServerInstance->Modules->LastError().c_str());
782                         else
783                                  ServerInstance->SNO->WriteGlobalSno('a', "Failed to unload module %s: %s", removing->c_str(), ServerInstance->Modules->LastError().c_str());
784                 }
785         }
786
787         for (std::vector<std::string>::iterator adding = added_modules.begin(); adding != added_modules.end(); adding++)
788         {
789                 if (ServerInstance->Modules->Load(adding->c_str()))
790                 {
791                         ServerInstance->SNO->WriteGlobalSno('a', "*** REHASH LOADED MODULE: %s",adding->c_str());
792                         if (user)
793                                 user->WriteNumeric(RPL_LOADEDMODULE, "%s %s :Module %s successfully loaded.",user->nick.c_str(), adding->c_str(), adding->c_str());
794                         else
795                                 ServerInstance->SNO->WriteGlobalSno('a', "Module %s successfully loaded.", adding->c_str());
796                 }
797                 else
798                 {
799                         if (user)
800                                 user->WriteNumeric(ERR_CANTLOADMODULE, "%s %s :Failed to load module %s: %s",user->nick.c_str(), adding->c_str(), adding->c_str(), ServerInstance->Modules->LastError().c_str());
801                         else
802                                 ServerInstance->SNO->WriteGlobalSno('a', "Failed to load module %s: %s", adding->c_str(), ServerInstance->Modules->LastError().c_str());
803                 }
804         }
805
806         if (user)
807                 user->WriteServ("NOTICE %s :*** Successfully rehashed server.", user->nick.c_str());
808         else
809                 ServerInstance->SNO->WriteGlobalSno('a', "*** Successfully rehashed server.");
810 }
811
812 bool ServerConfig::LoadConf(FILE* &conf, const char* filename, bool allowexeinc)
813 {
814         std::string line;
815         char ch;
816         long linenumber = 1;
817         long last_successful_parse = 1;
818         bool in_tag;
819         bool in_quote;
820         bool in_comment;
821         int character_count = 0;
822
823         in_tag = false;
824         in_quote = false;
825         in_comment = false;
826
827         ServerInstance->Logs->Log("CONFIG", DEBUG, "Reading %s", filename);
828
829         /* Check if the file open failed first */
830         if (!conf)
831         {
832                 errstr << "LoadConf: Couldn't open config file: " << filename << std::endl;
833                 return false;
834         }
835
836         for (unsigned int t = 0; t < include_stack.size(); t++)
837         {
838                 if (std::string(filename) == include_stack[t])
839                 {
840                         errstr << "File " << filename << " is included recursively (looped inclusion)." << std::endl;
841                         return false;
842                 }
843         }
844
845         /* It's not already included, add it to the list of files we've loaded */
846         include_stack.push_back(filename);
847
848         /* Start reading characters... */
849         while ((ch = fgetc(conf)) != EOF)
850         {
851                 /*
852                  * Fix for moronic windows issue spotted by Adremelech.
853                  * Some windows editors save text files as utf-16, which is
854                  * a total pain in the ass to parse. Users should save in the
855                  * right config format! If we ever see a file where the first
856                  * byte is 0xFF or 0xFE, or the second is 0xFF or 0xFE, then
857                  * this is most likely a utf-16 file. Bail out and insult user.
858                  */
859                 if ((character_count++ < 2) && (ch == '\xFF' || ch == '\xFE'))
860                 {
861                         errstr << "File " << filename << " cannot be read, as it is encoded in braindead UTF-16. Save your file as plain ASCII!" << std::endl;
862                         return false;
863                 }
864
865                 /*
866                  * Here we try and get individual tags on separate lines,
867                  * this would be so easy if we just made people format
868                  * their config files like that, but they don't so...
869                  * We check for a '<' and then know the line is over when
870                  * we get a '>' not inside quotes. If we find two '<' and
871                  * no '>' then die with an error.
872                  */
873
874                 if ((ch == '#') && !in_quote)
875                         in_comment = true;
876
877                 switch (ch)
878                 {
879                         case '\n':
880                                 if (in_quote)
881                                         line += '\n';
882                                 linenumber++;
883                         case '\r':
884                                 if (!in_quote)
885                                         in_comment = false;
886                         case '\0':
887                                 continue;
888                         case '\t':
889                                 ch = ' ';
890                 }
891
892                 if(in_comment)
893                         continue;
894
895                 /* XXX: Added by Brain, May 1st 2006 - Escaping of characters.
896                  * Note that this WILL NOT usually allow insertion of newlines,
897                  * because a newline is two characters long. Use it primarily to
898                  * insert the " symbol.
899                  *
900                  * Note that this also involves a further check when parsing the line,
901                  * which can be found below.
902                  */
903                 if ((ch == '\\') && (in_quote) && (in_tag))
904                 {
905                         line += ch;
906                         char real_character;
907                         if (!feof(conf))
908                         {
909                                 real_character = fgetc(conf);
910                                 if (real_character == 'n')
911                                         real_character = '\n';
912                                 line += real_character;
913                                 continue;
914                         }
915                         else
916                         {
917                                 errstr << "End of file after a \\, what did you want to escape?: " << filename << ":" << linenumber << std::endl;
918                                 return false;
919                         }
920                 }
921
922                 if (ch != '\r')
923                         line += ch;
924
925                 if ((ch != '<') && (!in_tag) && (!in_comment) && (ch > ' ') && (ch != 9))
926                 {
927                         errstr << "You have stray characters beyond the tag which starts at " << filename << ":" << last_successful_parse << std::endl;
928                         return false;
929                 }
930
931                 if (ch == '<')
932                 {
933                         if (in_tag)
934                         {
935                                 if (!in_quote)
936                                 {
937                                         errstr << "The tag at location " << filename << ":" << last_successful_parse << " was valid, but there is an error in the tag which comes after it. You are possibly missing a \" or >. Please check this." << std::endl;
938                                         return false;
939                                 }
940                         }
941                         else
942                         {
943                                 if (in_quote)
944                                 {
945                                         errstr << "Parser error: Inside a quote but not within the last valid tag, which was opened at: " << filename << ":" << last_successful_parse << std::endl;
946                                         return false;
947                                 }
948                                 else
949                                 {
950                                         // errstr << "Opening new config tag on line " << linenumber << std::endl;
951                                         in_tag = true;
952                                 }
953                         }
954                 }
955                 else if (ch == '"')
956                 {
957                         if (in_tag)
958                         {
959                                 if (in_quote)
960                                 {
961                                         // errstr << "Closing quote in config tag on line " << linenumber << std::endl;
962                                         in_quote = false;
963                                 }
964                                 else
965                                 {
966                                         // errstr << "Opening quote in config tag on line " << linenumber << std::endl;
967                                         in_quote = true;
968                                 }
969                         }
970                         else
971                         {
972                                 if (in_quote)
973                                 {
974                                         errstr << "The tag immediately after the one at " << filename << ":" << last_successful_parse << " has a missing closing \" symbol. Please check this." << std::endl;
975                                 }
976                                 else
977                                 {
978                                         errstr << "You have opened a quote (\") beyond the tag at " << filename << ":" << last_successful_parse << " without opening a new tag. Please check this." << std::endl;
979                                 }
980                         }
981                 }
982                 else if (ch == '>')
983                 {
984                         if (!in_quote)
985                         {
986                                 if (in_tag)
987                                 {
988                                         // errstr << "Closing config tag on line " << linenumber << std::endl;
989                                         in_tag = false;
990
991                                         /*
992                                          * If this finds an <include> then ParseLine can simply call
993                                          * LoadConf() and load the included config into the same ConfigDataHash
994                                          */
995                                         long bl = linenumber;
996                                         if (!this->ParseLine(filename, line, linenumber, allowexeinc))
997                                                 return false;
998                                         last_successful_parse = linenumber;
999
1000                                         linenumber = bl;
1001
1002                                         line.clear();
1003                                 }
1004                                 else
1005                                 {
1006                                         errstr << "You forgot to close the tag which comes immediately after the one at " << filename << ":" << last_successful_parse << std::endl;
1007                                         return false;
1008                                 }
1009                         }
1010                 }
1011         }
1012
1013         /* Fix for bug #392 - if we reach the end of a file and we are still in a quote or comment, most likely the user fucked up */
1014         if (in_comment || in_quote)
1015         {
1016                 errstr << "Reached end of file whilst still inside a quoted section or tag. This is most likely an error or there \
1017                         is a newline missing from the end of the file: " << filename << ":" << linenumber << std::endl;
1018         }
1019
1020         return true;
1021 }
1022
1023
1024 bool ServerConfig::LoadConf(FILE* &conf, const std::string &filename, bool allowexeinc)
1025 {
1026         return this->LoadConf(conf, filename.c_str(), allowexeinc);
1027 }
1028
1029 bool ServerConfig::ParseLine(const std::string &filename, std::string &line, long &linenumber, bool allowexeinc)
1030 {
1031         std::string tagname;
1032         std::string current_key;
1033         std::string current_value;
1034         reference<ConfigTag> result;
1035         char last_char = 0;
1036         bool got_key;
1037         bool in_quote;
1038
1039         got_key = in_quote = false;
1040
1041         for(std::string::iterator c = line.begin(); c != line.end(); c++)
1042         {
1043                 if (!result)
1044                 {
1045                         /* We don't know the tag name yet. */
1046
1047                         if (*c != ' ')
1048                         {
1049                                 if (*c != '<')
1050                                 {
1051                                         if ((*c >= 'a' && *c <= 'z') || (*c >= 'A' && *c <='Z') || (*c >= '0' && *c <= '9') || *c == '_')
1052                                                 tagname += *c;
1053                                         else
1054                                         {
1055                                                 errstr << "Invalid character in value name of tag: '" << *c << "' in value '" << tagname << "' in filename: " << filename << ":" << linenumber << std::endl;
1056                                                 return false;
1057                                         }
1058                                 }
1059                         }
1060                         else
1061                         {
1062                                 /* We got to a space, we should have the tagname now. */
1063                                 if(tagname.length())
1064                                 {
1065                                         result = new ConfigTag(tagname);
1066                                 }
1067                         }
1068                 }
1069                 else
1070                 {
1071                         /* We have the tag name */
1072                         if (!got_key)
1073                         {
1074                                 /* We're still reading the key name */
1075                                 if ((*c != '=') && (*c != '>'))
1076                                 {
1077                                         if (*c != ' ')
1078                                         {
1079                                                 if ((*c >= 'a' && *c <= 'z') || (*c >= 'A' && *c <='Z') || (*c >= '0' && *c <= '9') || *c == '_')
1080                                                         current_key += *c;
1081                                                 else
1082                                                 {
1083                                                         errstr << "Invalid character in key: '" << *c << "' in key '" << current_key << "' in filename: " << filename << ":" << linenumber << std::endl;
1084                                                         return false;
1085                                                 }
1086                                         }
1087                                 }
1088                                 else
1089                                 {
1090                                         /* We got an '=', end of the key name. */
1091                                         got_key = true;
1092                                 }
1093                         }
1094                         else
1095                         {
1096                                 /* We have the key name, now we're looking for quotes and the value */
1097
1098                                 /* Correctly handle escaped characters here.
1099                                  * See the XXX'ed section above.
1100                                  */
1101                                 if ((*c == '\\') && (in_quote))
1102                                 {
1103                                         c++;
1104                                         if (*c == 'n')
1105                                                 current_value += '\n';
1106                                         else
1107                                                 current_value += *c;
1108                                         continue;
1109                                 }
1110                                 else if ((*c == '\\') && (!in_quote))
1111                                 {
1112                                         errstr << "You can't have an escape sequence outside of a quoted section: " << filename << ":" << linenumber << std::endl;
1113                                         return false;
1114                                 }
1115                                 else if ((*c == '\n') && (in_quote))
1116                                 {
1117                                         /* Got a 'real' \n, treat it as part of the value */
1118                                         current_value += '\n';
1119                                         continue;
1120                                 }
1121                                 else if ((*c == '\r') && (in_quote))
1122                                 {
1123                                         /* Got a \r, drop it */
1124                                         continue;
1125                                 }
1126
1127                                 if (*c == '"')
1128                                 {
1129                                         if (!in_quote)
1130                                         {
1131                                                 /* We're not already in a quote. */
1132                                                 in_quote = true;
1133                                         }
1134                                         else
1135                                         {
1136                                                 /* Leaving the quotes, we have the current value */
1137                                                 result->items.push_back(KeyVal(current_key, current_value));
1138
1139                                                 // std::cout << "<" << tagname << ":" << current_key << "> " << current_value << std::endl;
1140
1141                                                 in_quote = false;
1142                                                 got_key = false;
1143
1144                                                 if ((tagname == "include") && (current_key == "file"))
1145                                                 {
1146                                                         if (!this->DoInclude(current_value, allowexeinc))
1147                                                                 return false;
1148                                                 }
1149                                                 else if ((tagname == "include") && (current_key == "executable"))
1150                                                 {
1151                                                         if (!allowexeinc)
1152                                                         {
1153                                                                 errstr << "Executable includes are not allowed to use <include:executable>\n"
1154                                                                         "This could be an attempt to execute commands from a malicious remote include.\n"
1155                                                                         "If you need multiple levels of remote include, create a script to assemble the "
1156                                                                         "contents locally or include files using <include:file>\n";
1157                                                                 return false;
1158                                                         }
1159
1160                                                         /* Pipe an executable and use its stdout as config data */
1161                                                         if (!this->DoPipe(current_value))
1162                                                                 return false;
1163                                                 }
1164
1165                                                 current_key.clear();
1166                                                 current_value.clear();
1167                                         }
1168                                 }
1169                                 else
1170                                 {
1171                                         if (in_quote)
1172                                         {
1173                                                 last_char = *c;
1174                                                 current_value += *c;
1175                                         }
1176                                 }
1177                         }
1178                 }
1179         }
1180
1181         /* Finished parsing the tag, add it to the config hash */
1182         config_data.insert(std::make_pair(tagname, result));
1183
1184         return true;
1185 }
1186
1187 bool ServerConfig::DoPipe(const std::string &file)
1188 {
1189         FILE* conf = popen(file.c_str(), "r");
1190         bool ret = false;
1191
1192         if (conf)
1193         {
1194                 ret = LoadConf(conf, file.c_str(), false);
1195                 pclose(conf);
1196         }
1197         else
1198                 errstr << "Couldn't execute: " << file << std::endl;
1199
1200         return ret;
1201 }
1202
1203 bool ServerConfig::StartsWithWindowsDriveLetter(const std::string &path)
1204 {
1205         return (path.length() > 2 && isalpha(path[0]) && path[1] == ':');
1206 }
1207
1208 bool ServerConfig::DoInclude(const std::string &file, bool allowexeinc)
1209 {
1210         FILE* conf = fopen(file.c_str(), "r");
1211         bool ret = false;
1212
1213         if (conf)
1214         {
1215                 ret = LoadConf(conf, file, allowexeinc);
1216                 fclose(conf);
1217         }
1218         else
1219                 errstr << "Couldn't open config file: " << file << std::endl;
1220
1221         return ret;
1222 }
1223
1224 ConfigTag* ServerConfig::ConfValue(const std::string &tag, int offset)
1225 {
1226         ConfigDataHash::size_type pos = offset;
1227         if (pos >= config_data.count(tag))
1228                 return NULL;
1229         
1230         ConfigDataHash::iterator iter = config_data.find(tag);
1231
1232         for(int i = 0; i < offset; i++)
1233                 iter++;
1234         
1235         return iter->second;
1236 }
1237
1238 bool ConfigTag::readString(const std::string& key, std::string& value, bool allow_lf)
1239 {
1240         if (!this)
1241                 return false;
1242         for(std::vector<KeyVal>::iterator j = items.begin(); j != items.end(); ++j)
1243         {
1244                 if(j->first != key)
1245                         continue;
1246                 value = j->second;
1247                 if (!allow_lf && (value.find('\n') != std::string::npos))
1248                 {
1249                         ServerInstance->Logs->Log("CONFIG",DEFAULT, "Value of <" + tag + ":" + key + "> contains a linefeed, and linefeeds in this value are not permitted -- stripped to spaces.");
1250                         for (std::string::iterator n = value.begin(); n != value.end(); n++)
1251                                 if (*n == '\n')
1252                                         *n = ' ';
1253                 }
1254                 return true;
1255         }
1256         return false;
1257 }
1258
1259 std::string ConfigTag::getString(const std::string& key, const std::string& def)
1260 {
1261         std::string res = def;
1262         if (this)
1263                 readString(key, res);
1264         return res;
1265 }
1266
1267 long ConfigTag::getInt(const std::string &key, long def)
1268 {
1269         std::string result;
1270         if(!this || !readString(key, result))
1271                 return def;
1272
1273         const char* res_cstr = result.c_str();
1274         char* res_tail = NULL;
1275         long res = strtol(res_cstr, &res_tail, 0);
1276         if (res_tail == res_cstr)
1277                 return def;
1278         switch (toupper(*res_tail))
1279         {
1280                 case 'K':
1281                         res= res* 1024;
1282                         break;
1283                 case 'M':
1284                         res= res* 1024 * 1024;
1285                         break;
1286                 case 'G':
1287                         res= res* 1024 * 1024 * 1024;
1288                         break;
1289         }
1290         return res;
1291 }
1292
1293 double ConfigTag::getFloat(const std::string &key, double def)
1294 {
1295         std::string result;
1296         if (!readString(key, result))
1297                 return def;
1298         return strtod(result.c_str(), NULL);
1299 }
1300
1301 bool ConfigTag::getBool(const std::string &key, bool def)
1302 {
1303         std::string result;
1304         if(!readString(key, result))
1305                 return def;
1306
1307         return (result == "yes" || result == "true" || result == "1" || result == "on");
1308 }
1309
1310 /** Read the contents of a file located by `fname' into a file_cache pointed at by `F'.
1311  */
1312 bool ServerConfig::ReadFile(file_cache &F, const std::string& fname)
1313 {
1314         if (fname.empty())
1315                 return false;
1316
1317         FILE* file = NULL;
1318         char linebuf[MAXBUF];
1319
1320         F.clear();
1321
1322         if (!FileExists(fname.c_str()))
1323                 return false;
1324         file = fopen(fname.c_str(), "r");
1325
1326         if (file)
1327         {
1328                 while (!feof(file))
1329                 {
1330                         if (fgets(linebuf, sizeof(linebuf), file))
1331                                 linebuf[strlen(linebuf)-1] = 0;
1332                         else
1333                                 *linebuf = 0;
1334
1335                         F.push_back(*linebuf ? linebuf : " ");
1336                 }
1337
1338                 fclose(file);
1339         }
1340         else
1341                 return false;
1342
1343         return true;
1344 }
1345
1346 bool ServerConfig::FileExists(const char* file)
1347 {
1348         struct stat sb;
1349         if (stat(file, &sb) == -1)
1350                 return false;
1351
1352         if ((sb.st_mode & S_IFDIR) > 0)
1353                 return false;
1354
1355         FILE *input = fopen(file, "r");
1356         if (input == NULL)
1357                 return false;
1358         else
1359         {
1360                 fclose(input);
1361                 return true;
1362         }
1363 }
1364
1365 const char* ServerConfig::CleanFilename(const char* name)
1366 {
1367         const char* p = name + strlen(name);
1368         while ((p != name) && (*p != '/') && (*p != '\\')) p--;
1369         return (p != name ? ++p : p);
1370 }
1371
1372
1373 std::string ServerConfig::GetSID()
1374 {
1375         return sid;
1376 }
1377
1378 void ConfigReaderThread::Run()
1379 {
1380         Config = new ServerConfig;
1381         Config->Read();
1382         Config->ReadFile(Config->MOTD, Config->ConfValue("files")->getString("motd"));
1383         Config->ReadFile(Config->RULES, Config->ConfValue("files")->getString("rules"));
1384         FindDNS(Config->DNSServer);
1385
1386         done = true;
1387 }
1388
1389 void ConfigReaderThread::Finish()
1390 {
1391         ServerConfig* old = ServerInstance->Config;
1392         ServerInstance->Logs->Log("CONFIG",DEBUG,"Switching to new configuration...");
1393         ServerInstance->Logs->CloseLogs();
1394         ServerInstance->Config = this->Config;
1395         ServerInstance->Logs->OpenFileLogs();
1396         Config->Apply(old, TheUserUID);
1397
1398         if (Config->valid)
1399         {
1400                 /*
1401                  * Apply the changed configuration from the rehash.
1402                  *
1403                  * XXX: The order of these is IMPORTANT, do not reorder them without testing
1404                  * thoroughly!!!
1405                  */
1406                 ServerInstance->XLines->CheckELines();
1407                 ServerInstance->XLines->CheckELines();
1408                 ServerInstance->XLines->ApplyLines();
1409                 ServerInstance->Res->Rehash();
1410                 ServerInstance->ResetMaxBans();
1411                 Config->ApplyDisabledCommands(Config->DisabledCommands);
1412                 User* user = TheUserUID.empty() ? ServerInstance->FindNick(TheUserUID) : NULL;
1413                 FOREACH_MOD(I_OnRehash, OnRehash(user));
1414                 ServerInstance->BuildISupport();
1415
1416                 delete old;
1417         }
1418         else
1419         {
1420                 // whoops, abort!
1421                 ServerInstance->Logs->CloseLogs();
1422                 ServerInstance->Config = old;
1423                 ServerInstance->Logs->OpenFileLogs();
1424                 delete this->Config;
1425         }
1426 }