]> git.netwichtig.de Git - user/henk/code/inspircd.git/blob - src/configreader.cpp
Fix DNSServer not being read on initial config read
[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         dns_timeout = ConfValue("dns")->getInt("timeout", 5);
485         DisabledCommands = ConfValue("disabled")->getString("commands", "");
486         DisabledDontExist = ConfValue("disabled")->getBool("fakenonexistant");
487         SetUser = security->getString("runasuser");
488         SetGroup = security->getString("runasgroup");
489         UserStats = security->getString("userstats");
490         CustomVersion = security->getString("customversion");
491         HideSplits = security->getBool("hidesplits");
492         HideBans = security->getBool("hidebans");
493         HideWhoisServer = security->getString("hidewhois");
494         HideKillsServer = security->getString("hidekills");
495         OperSpyWhois = security->getBool("operspywhois");
496         RestrictBannedUsers = security->getBool("restrictbannedusers", true);
497         GenericOper = security->getBool("genericoper");
498         NoUserDns = ConfValue("performance")->getBool("nouserdns");
499         SyntaxHints = options->getBool("syntaxhints");
500         CycleHosts = options->getBool("cyclehosts");
501         UndernetMsgPrefix = options->getBool("ircumsgprefix");
502         FullHostInTopic = options->getBool("hostintopic");
503         MaxTargets = security->getInt("maxtargets", 20);
504         DefaultModes = options->getString("defaultmodes", "nt");
505         PID = ConfValue("pid")->getString("file");
506         WhoWasGroupSize = ConfValue("whowas")->getInt("groupsize");
507         WhoWasMaxGroups = ConfValue("whowas")->getInt("maxgroups");
508         WhoWasMaxKeep = ServerInstance->Duration(ConfValue("whowas")->getString("maxkeep"));
509         DieValue = ConfValue("die")->getString("value");
510         MaxChans = ConfValue("channels")->getInt("users");
511         OperMaxChans = ConfValue("channels")->getInt("opers");
512         c_ipv4_range = ConfValue("cidr")->getInt("ipv4clone");
513         c_ipv6_range = ConfValue("cidr")->getInt("ipv6clone");
514         Limits.NickMax = ConfValue("limits")->getInt("maxnick", 32);
515         Limits.ChanMax = ConfValue("limits")->getInt("maxchan", 64);
516         Limits.MaxModes = ConfValue("limits")->getInt("maxmodes", 20);
517         Limits.IdentMax = ConfValue("limits")->getInt("maxident", 11);
518         Limits.MaxQuit = ConfValue("limits")->getInt("maxquit", 255);
519         Limits.MaxTopic = ConfValue("limits")->getInt("maxtopic", 307);
520         Limits.MaxKick = ConfValue("limits")->getInt("maxkick", 255);
521         Limits.MaxGecos = ConfValue("limits")->getInt("maxgecos", 128);
522         Limits.MaxAway = ConfValue("limits")->getInt("maxaway", 200);
523         InvBypassModes = options->getBool("invitebypassmodes", true);
524
525         range(SoftLimit, 10, ServerInstance->SE->GetMaxFds(), ServerInstance->SE->GetMaxFds(), "<performance:softlimit>");
526         range(MaxConn, 0, SOMAXCONN, SOMAXCONN, "<performance:somaxconn>");
527         range(MaxTargets, 1, 31, 20, "<security:maxtargets>");
528         range(NetBufferSize, 1024, 65534, 10240, "<performance:netbuffersize>");
529         range(MaxWhoResults, 1, 65535, 1024, "<performace:maxwho>");
530         range(WhoWasGroupSize, 0, 10000, 10, "<whowas:groupsize>");
531         range(WhoWasMaxGroups, 0, 1000000, 10240, "<whowas:maxgroups>");
532         range(WhoWasMaxKeep, 3600, INT_MAX, 3600, "<whowas:maxkeep>");
533
534         ValidIP(DNSServer, "<dns:server>");
535         ValidHost(ServerName, "<server:name>");
536         if (!sid.empty() && !ServerInstance->IsSID(sid))
537                 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.");
538         
539         for (int i = 0;; ++i)
540         {
541                 ConfigTag* tag = ConfValue("uline", i);
542                 if (!tag)
543                         break;
544                 std::string server;
545                 if (!tag->readString("server", server))
546                         throw CoreException("<uline> tag missing server");
547                 ulines[assign(server)] = tag->getBool("silent");
548         }
549
550         for(int i=0;; ++i)
551         {
552                 ConfigTag* tag = ConfValue("banlist", i);
553                 if (!tag)
554                         break;
555                 std::string chan;
556                 if (!tag->readString("chan", chan))
557                         throw CoreException("<banlist> tag missing chan");
558                 maxbans[chan] = tag->getInt("limit");
559         }
560
561         ReadXLine(this, "badip", "ipmask", ServerInstance->XLines->GetFactory("Z"));
562         ReadXLine(this, "badnick", "nick", ServerInstance->XLines->GetFactory("Q"));
563         ReadXLine(this, "badhost", "host", ServerInstance->XLines->GetFactory("K"));
564         ReadXLine(this, "exception", "host", ServerInstance->XLines->GetFactory("E"));
565
566         memset(DisabledUModes, 0, sizeof(DisabledUModes));
567         for (const unsigned char* p = (const unsigned char*)ConfValue("disabled")->getString("usermodes").c_str(); *p; ++p)
568         {
569                 if (*p < 'A' || *p > ('A' + 64)) throw CoreException(std::string("Invalid usermode ")+(char)*p+" was found.");
570                 DisabledUModes[*p - 'A'] = 1;
571         }
572
573         memset(DisabledCModes, 0, sizeof(DisabledCModes));
574         for (const unsigned char* p = (const unsigned char*)ConfValue("disabled")->getString("chanmodes").c_str(); *p; ++p)
575         {
576                 if (*p < 'A' || *p > ('A' + 64)) throw CoreException(std::string("Invalid chanmode ")+(char)*p+" was found.");
577                 DisabledCModes[*p - 'A'] = 1;
578         }
579
580         memset(HideModeLists, 0, sizeof(HideModeLists));
581         for (const unsigned char* p = (const unsigned char*)ConfValue("security")->getString("hidemodes").c_str(); *p; ++p)
582                 HideModeLists[*p] = true;
583         
584         std::string v = security->getString("announceinvites");
585
586         if (v == "ops")
587                 AnnounceInvites = ServerConfig::INVITE_ANNOUNCE_OPS;
588         else if (v == "all")
589                 AnnounceInvites = ServerConfig::INVITE_ANNOUNCE_ALL;
590         else if (v == "dynamic")
591                 AnnounceInvites = ServerConfig::INVITE_ANNOUNCE_DYNAMIC;
592         else
593                 AnnounceInvites = ServerConfig::INVITE_ANNOUNCE_NONE;
594
595         bool AllowHalfOp = options->getBool("allowhalfop");
596         ModeHandler* mh = ServerInstance->Modes->FindMode('h', MODETYPE_CHANNEL);
597         if (AllowHalfOp && !mh) {
598                 ServerInstance->Logs->Log("CONFIG", DEFAULT, "Enabling halfop mode.");
599                 mh = new ModeChannelHalfOp;
600                 ServerInstance->Modes->AddMode(mh);
601         } else if (!AllowHalfOp && mh) {
602                 ServerInstance->Logs->Log("CONFIG", DEFAULT, "Disabling halfop mode.");
603                 ServerInstance->Modes->DelMode(mh);
604                 delete mh;
605         }
606
607         Module* whowas = ServerInstance->Modules->Find("cmd_whowas.so");
608         if (whowas)
609                 WhowasRequest(NULL, whowas, WhowasRequest::WHOWAS_PRUNE).Send();
610         Limits.Finalise();
611
612 }
613
614 /* These tags MUST occur and must ONLY occur once in the config file */
615 static const char* const Once[] = { "server", "admin", "files", "power", "options" };
616
617 // WARNING: it is not safe to use most of the codebase in this function, as it
618 // will run in the config reader thread
619 void ServerConfig::Read()
620 {
621         /* Load and parse the config file, if there are any errors then explode */
622
623         valid = DoInclude(ServerInstance->ConfigFileName, true);
624         if (valid)
625         {
626                 ReadFile(MOTD, ConfValue("files")->getString("motd"));
627                 ReadFile(RULES, ConfValue("files")->getString("rules"));
628                 DNSServer = ConfValue("dns")->getString("server");
629                 FindDNS(DNSServer);
630         }
631 }
632
633 void ServerConfig::Apply(ServerConfig* old, const std::string &useruid)
634 {
635         valid = true;
636         /* std::ostringstream::clear() does not clear the string itself, only the error flags. */
637         errstr.clear();
638         errstr.str().clear();
639         include_stack.clear();
640
641         /* The stuff in here may throw CoreException, be sure we're in a position to catch it. */
642         try
643         {
644                 /* Check we dont have more than one of singular tags, or any of them missing
645                  */
646                 for (int Index = 0; Index * sizeof(*Once) < sizeof(Once); Index++)
647                         CheckOnce(Once[Index]);
648
649                 for (int Index = 0; Index * sizeof(Deprecated) < sizeof(ChangedConfig); Index++)
650                 {
651                         std::string dummy;
652                         if (ConfValue(ChangedConfig[Index].tag)->readString(ChangedConfig[Index].value, dummy, true))
653                                 throw CoreException(std::string("Your configuration contains a deprecated value: <") + ChangedConfig[Index].tag + ":" + ChangedConfig[Index].value + "> - " + ChangedConfig[Index].reason);
654                 }
655
656                 Fill();
657
658                 // Handle special items
659                 CrossCheckOperClassType();
660                 CrossCheckConnectBlocks(old);
661         }
662         catch (CoreException &ce)
663         {
664                 errstr << ce.GetReason();
665                 valid = false;
666         }
667
668         // write once here, to try it out and make sure its ok
669         ServerInstance->WritePID(this->PID);
670
671         /*
672          * These values can only be set on boot. Keep their old values. Do it before we send messages so we actually have a servername.
673          */
674         if (old)
675         {
676                 this->ServerName = old->ServerName;
677                 this->sid = old->sid;
678                 this->argv = old->argv;
679                 this->argc = old->argc;
680
681                 // Same for ports... they're bound later on first run.
682                 FailedPortList pl;
683                 ServerInstance->BindPorts(pl);
684                 if (pl.size())
685                 {
686                         errstr << "Not all your client ports could be bound.\nThe following port(s) failed to bind:\n";
687
688                         int j = 1;
689                         for (FailedPortList::iterator i = pl.begin(); i != pl.end(); i++, j++)
690                         {
691                                 char buf[MAXBUF];
692                                 snprintf(buf, MAXBUF, "%d.   Address: %s   Reason: %s\n", j, i->first.empty() ? "<all>" : i->first.c_str(), i->second.c_str());
693                                 errstr << buf;
694                         }
695                 }
696         }
697
698         User* user = useruid.empty() ? NULL : ServerInstance->FindNick(useruid);
699
700         valid = errstr.str().empty();
701         if (!valid)
702                 ServerInstance->Logs->Log("CONFIG",DEFAULT, "There were errors in your configuration file:");
703
704         while (errstr.good())
705         {
706                 std::string line;
707                 getline(errstr, line, '\n');
708                 if (!line.empty())
709                 {
710                         if (user)
711                                 user->WriteServ("NOTICE %s :*** %s", user->nick.c_str(), line.c_str());
712                         else
713                                 ServerInstance->SNO->WriteGlobalSno('a', line);
714                 }
715
716                 if (!old)
717                 {
718                         // Starting up, so print it out so it's seen. XXX this is a bit of a hack.
719                         printf("%s\n", line.c_str());
720                 }
721         }
722
723         errstr.clear();
724         errstr.str(std::string());
725
726         /* No old configuration -> initial boot, nothing more to do here */
727         if (!old)
728         {
729                 if (!valid)
730                 {
731                         ServerInstance->Exit(EXIT_STATUS_CONFIG);
732                 }
733
734                 return;
735         }
736
737         // If there were errors processing configuration, don't touch modules.
738         if (!valid)
739                 return;
740
741         ApplyModules(user);
742 }
743
744 void ServerConfig::ApplyModules(User* user)
745 {
746         const std::vector<std::string> v = ServerInstance->Modules->GetAllModuleNames(0);
747         std::vector<std::string> added_modules;
748         std::set<std::string> removed_modules(v.begin(), v.end());
749
750         for(int i=0; ; i++)
751         {
752                 ConfigTag* tag = ConfValue("module", i);
753                 if (!tag)
754                         break;
755                 std::string name;
756                 if (tag->readString("name", name))
757                 {
758                         // if this module is already loaded, the erase will succeed, so we need do nothing
759                         // otherwise, we need to add the module (which will be done later)
760                         if (removed_modules.erase(name) == 0)
761                                 added_modules.push_back(name);
762                 }
763         }
764
765         for (std::set<std::string>::iterator removing = removed_modules.begin(); removing != removed_modules.end(); removing++)
766         {
767                 // Don't remove cmd_*.so, just remove m_*.so
768                 if (removing->c_str()[0] == 'c')
769                         continue;
770                 Module* m = ServerInstance->Modules->Find(*removing);
771                 if (m && ServerInstance->Modules->Unload(m))
772                 {
773                         ServerInstance->SNO->WriteGlobalSno('a', "*** REHASH UNLOADED MODULE: %s",removing->c_str());
774
775                         if (user)
776                                 user->WriteNumeric(RPL_UNLOADEDMODULE, "%s %s :Module %s successfully unloaded.",user->nick.c_str(), removing->c_str(), removing->c_str());
777                         else
778                                 ServerInstance->SNO->WriteGlobalSno('a', "Module %s successfully unloaded.", removing->c_str());
779                 }
780                 else
781                 {
782                         if (user)
783                                 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());
784                         else
785                                  ServerInstance->SNO->WriteGlobalSno('a', "Failed to unload module %s: %s", removing->c_str(), ServerInstance->Modules->LastError().c_str());
786                 }
787         }
788
789         for (std::vector<std::string>::iterator adding = added_modules.begin(); adding != added_modules.end(); adding++)
790         {
791                 if (ServerInstance->Modules->Load(adding->c_str()))
792                 {
793                         ServerInstance->SNO->WriteGlobalSno('a', "*** REHASH LOADED MODULE: %s",adding->c_str());
794                         if (user)
795                                 user->WriteNumeric(RPL_LOADEDMODULE, "%s %s :Module %s successfully loaded.",user->nick.c_str(), adding->c_str(), adding->c_str());
796                         else
797                                 ServerInstance->SNO->WriteGlobalSno('a', "Module %s successfully loaded.", adding->c_str());
798                 }
799                 else
800                 {
801                         if (user)
802                                 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());
803                         else
804                                 ServerInstance->SNO->WriteGlobalSno('a', "Failed to load module %s: %s", adding->c_str(), ServerInstance->Modules->LastError().c_str());
805                 }
806         }
807
808         if (user)
809                 user->WriteServ("NOTICE %s :*** Successfully rehashed server.", user->nick.c_str());
810         else
811                 ServerInstance->SNO->WriteGlobalSno('a', "*** Successfully rehashed server.");
812 }
813
814 bool ServerConfig::LoadConf(FILE* &conf, const char* filename, bool allowexeinc)
815 {
816         std::string line;
817         char ch;
818         long linenumber = 1;
819         long last_successful_parse = 1;
820         bool in_tag;
821         bool in_quote;
822         bool in_comment;
823         int character_count = 0;
824
825         in_tag = false;
826         in_quote = false;
827         in_comment = false;
828
829         ServerInstance->Logs->Log("CONFIG", DEBUG, "Reading %s", filename);
830
831         /* Check if the file open failed first */
832         if (!conf)
833         {
834                 errstr << "LoadConf: Couldn't open config file: " << filename << std::endl;
835                 return false;
836         }
837
838         for (unsigned int t = 0; t < include_stack.size(); t++)
839         {
840                 if (std::string(filename) == include_stack[t])
841                 {
842                         errstr << "File " << filename << " is included recursively (looped inclusion)." << std::endl;
843                         return false;
844                 }
845         }
846
847         /* It's not already included, add it to the list of files we've loaded */
848         include_stack.push_back(filename);
849
850         /* Start reading characters... */
851         while ((ch = fgetc(conf)) != EOF)
852         {
853                 /*
854                  * Fix for moronic windows issue spotted by Adremelech.
855                  * Some windows editors save text files as utf-16, which is
856                  * a total pain in the ass to parse. Users should save in the
857                  * right config format! If we ever see a file where the first
858                  * byte is 0xFF or 0xFE, or the second is 0xFF or 0xFE, then
859                  * this is most likely a utf-16 file. Bail out and insult user.
860                  */
861                 if ((character_count++ < 2) && (ch == '\xFF' || ch == '\xFE'))
862                 {
863                         errstr << "File " << filename << " cannot be read, as it is encoded in braindead UTF-16. Save your file as plain ASCII!" << std::endl;
864                         return false;
865                 }
866
867                 /*
868                  * Here we try and get individual tags on separate lines,
869                  * this would be so easy if we just made people format
870                  * their config files like that, but they don't so...
871                  * We check for a '<' and then know the line is over when
872                  * we get a '>' not inside quotes. If we find two '<' and
873                  * no '>' then die with an error.
874                  */
875
876                 if ((ch == '#') && !in_quote)
877                         in_comment = true;
878
879                 switch (ch)
880                 {
881                         case '\n':
882                                 if (in_quote)
883                                         line += '\n';
884                                 linenumber++;
885                         case '\r':
886                                 if (!in_quote)
887                                         in_comment = false;
888                         case '\0':
889                                 continue;
890                         case '\t':
891                                 ch = ' ';
892                 }
893
894                 if(in_comment)
895                         continue;
896
897                 /* XXX: Added by Brain, May 1st 2006 - Escaping of characters.
898                  * Note that this WILL NOT usually allow insertion of newlines,
899                  * because a newline is two characters long. Use it primarily to
900                  * insert the " symbol.
901                  *
902                  * Note that this also involves a further check when parsing the line,
903                  * which can be found below.
904                  */
905                 if ((ch == '\\') && (in_quote) && (in_tag))
906                 {
907                         line += ch;
908                         char real_character;
909                         if (!feof(conf))
910                         {
911                                 real_character = fgetc(conf);
912                                 if (real_character == 'n')
913                                         real_character = '\n';
914                                 line += real_character;
915                                 continue;
916                         }
917                         else
918                         {
919                                 errstr << "End of file after a \\, what did you want to escape?: " << filename << ":" << linenumber << std::endl;
920                                 return false;
921                         }
922                 }
923
924                 if (ch != '\r')
925                         line += ch;
926
927                 if ((ch != '<') && (!in_tag) && (!in_comment) && (ch > ' ') && (ch != 9))
928                 {
929                         errstr << "You have stray characters beyond the tag which starts at " << filename << ":" << last_successful_parse << std::endl;
930                         return false;
931                 }
932
933                 if (ch == '<')
934                 {
935                         if (in_tag)
936                         {
937                                 if (!in_quote)
938                                 {
939                                         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;
940                                         return false;
941                                 }
942                         }
943                         else
944                         {
945                                 if (in_quote)
946                                 {
947                                         errstr << "Parser error: Inside a quote but not within the last valid tag, which was opened at: " << filename << ":" << last_successful_parse << std::endl;
948                                         return false;
949                                 }
950                                 else
951                                 {
952                                         // errstr << "Opening new config tag on line " << linenumber << std::endl;
953                                         in_tag = true;
954                                 }
955                         }
956                 }
957                 else if (ch == '"')
958                 {
959                         if (in_tag)
960                         {
961                                 if (in_quote)
962                                 {
963                                         // errstr << "Closing quote in config tag on line " << linenumber << std::endl;
964                                         in_quote = false;
965                                 }
966                                 else
967                                 {
968                                         // errstr << "Opening quote in config tag on line " << linenumber << std::endl;
969                                         in_quote = true;
970                                 }
971                         }
972                         else
973                         {
974                                 if (in_quote)
975                                 {
976                                         errstr << "The tag immediately after the one at " << filename << ":" << last_successful_parse << " has a missing closing \" symbol. Please check this." << std::endl;
977                                 }
978                                 else
979                                 {
980                                         errstr << "You have opened a quote (\") beyond the tag at " << filename << ":" << last_successful_parse << " without opening a new tag. Please check this." << std::endl;
981                                 }
982                         }
983                 }
984                 else if (ch == '>')
985                 {
986                         if (!in_quote)
987                         {
988                                 if (in_tag)
989                                 {
990                                         // errstr << "Closing config tag on line " << linenumber << std::endl;
991                                         in_tag = false;
992
993                                         /*
994                                          * If this finds an <include> then ParseLine can simply call
995                                          * LoadConf() and load the included config into the same ConfigDataHash
996                                          */
997                                         long bl = linenumber;
998                                         if (!this->ParseLine(filename, line, linenumber, allowexeinc))
999                                                 return false;
1000                                         last_successful_parse = linenumber;
1001
1002                                         linenumber = bl;
1003
1004                                         line.clear();
1005                                 }
1006                                 else
1007                                 {
1008                                         errstr << "You forgot to close the tag which comes immediately after the one at " << filename << ":" << last_successful_parse << std::endl;
1009                                         return false;
1010                                 }
1011                         }
1012                 }
1013         }
1014
1015         /* 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 */
1016         if (in_comment || in_quote)
1017         {
1018                 errstr << "Reached end of file whilst still inside a quoted section or tag. This is most likely an error or there \
1019                         is a newline missing from the end of the file: " << filename << ":" << linenumber << std::endl;
1020         }
1021
1022         return true;
1023 }
1024
1025
1026 bool ServerConfig::LoadConf(FILE* &conf, const std::string &filename, bool allowexeinc)
1027 {
1028         return this->LoadConf(conf, filename.c_str(), allowexeinc);
1029 }
1030
1031 bool ServerConfig::ParseLine(const std::string &filename, std::string &line, long &linenumber, bool allowexeinc)
1032 {
1033         std::string tagname;
1034         std::string current_key;
1035         std::string current_value;
1036         reference<ConfigTag> result;
1037         char last_char = 0;
1038         bool got_key;
1039         bool in_quote;
1040
1041         got_key = in_quote = false;
1042
1043         for(std::string::iterator c = line.begin(); c != line.end(); c++)
1044         {
1045                 if (!result)
1046                 {
1047                         /* We don't know the tag name yet. */
1048
1049                         if (*c != ' ')
1050                         {
1051                                 if (*c != '<')
1052                                 {
1053                                         if ((*c >= 'a' && *c <= 'z') || (*c >= 'A' && *c <='Z') || (*c >= '0' && *c <= '9') || *c == '_')
1054                                                 tagname += *c;
1055                                         else
1056                                         {
1057                                                 errstr << "Invalid character in value name of tag: '" << *c << "' in value '" << tagname << "' in filename: " << filename << ":" << linenumber << std::endl;
1058                                                 return false;
1059                                         }
1060                                 }
1061                         }
1062                         else
1063                         {
1064                                 /* We got to a space, we should have the tagname now. */
1065                                 if(tagname.length())
1066                                 {
1067                                         result = new ConfigTag(tagname);
1068                                 }
1069                         }
1070                 }
1071                 else
1072                 {
1073                         /* We have the tag name */
1074                         if (!got_key)
1075                         {
1076                                 /* We're still reading the key name */
1077                                 if ((*c != '=') && (*c != '>'))
1078                                 {
1079                                         if (*c != ' ')
1080                                         {
1081                                                 if ((*c >= 'a' && *c <= 'z') || (*c >= 'A' && *c <='Z') || (*c >= '0' && *c <= '9') || *c == '_')
1082                                                         current_key += *c;
1083                                                 else
1084                                                 {
1085                                                         errstr << "Invalid character in key: '" << *c << "' in key '" << current_key << "' in filename: " << filename << ":" << linenumber << std::endl;
1086                                                         return false;
1087                                                 }
1088                                         }
1089                                 }
1090                                 else
1091                                 {
1092                                         /* We got an '=', end of the key name. */
1093                                         got_key = true;
1094                                 }
1095                         }
1096                         else
1097                         {
1098                                 /* We have the key name, now we're looking for quotes and the value */
1099
1100                                 /* Correctly handle escaped characters here.
1101                                  * See the XXX'ed section above.
1102                                  */
1103                                 if ((*c == '\\') && (in_quote))
1104                                 {
1105                                         c++;
1106                                         if (*c == 'n')
1107                                                 current_value += '\n';
1108                                         else
1109                                                 current_value += *c;
1110                                         continue;
1111                                 }
1112                                 else if ((*c == '\\') && (!in_quote))
1113                                 {
1114                                         errstr << "You can't have an escape sequence outside of a quoted section: " << filename << ":" << linenumber << std::endl;
1115                                         return false;
1116                                 }
1117                                 else if ((*c == '\n') && (in_quote))
1118                                 {
1119                                         /* Got a 'real' \n, treat it as part of the value */
1120                                         current_value += '\n';
1121                                         continue;
1122                                 }
1123                                 else if ((*c == '\r') && (in_quote))
1124                                 {
1125                                         /* Got a \r, drop it */
1126                                         continue;
1127                                 }
1128
1129                                 if (*c == '"')
1130                                 {
1131                                         if (!in_quote)
1132                                         {
1133                                                 /* We're not already in a quote. */
1134                                                 in_quote = true;
1135                                         }
1136                                         else
1137                                         {
1138                                                 /* Leaving the quotes, we have the current value */
1139                                                 result->items.push_back(KeyVal(current_key, current_value));
1140
1141                                                 // std::cout << "<" << tagname << ":" << current_key << "> " << current_value << std::endl;
1142
1143                                                 in_quote = false;
1144                                                 got_key = false;
1145
1146                                                 if ((tagname == "include") && (current_key == "file"))
1147                                                 {
1148                                                         if (!this->DoInclude(current_value, allowexeinc))
1149                                                                 return false;
1150                                                 }
1151                                                 else if ((tagname == "include") && (current_key == "executable"))
1152                                                 {
1153                                                         if (!allowexeinc)
1154                                                         {
1155                                                                 errstr << "Executable includes are not allowed to use <include:executable>\n"
1156                                                                         "This could be an attempt to execute commands from a malicious remote include.\n"
1157                                                                         "If you need multiple levels of remote include, create a script to assemble the "
1158                                                                         "contents locally or include files using <include:file>\n";
1159                                                                 return false;
1160                                                         }
1161
1162                                                         /* Pipe an executable and use its stdout as config data */
1163                                                         if (!this->DoPipe(current_value))
1164                                                                 return false;
1165                                                 }
1166
1167                                                 current_key.clear();
1168                                                 current_value.clear();
1169                                         }
1170                                 }
1171                                 else
1172                                 {
1173                                         if (in_quote)
1174                                         {
1175                                                 last_char = *c;
1176                                                 current_value += *c;
1177                                         }
1178                                 }
1179                         }
1180                 }
1181         }
1182
1183         /* Finished parsing the tag, add it to the config hash */
1184         config_data.insert(std::make_pair(tagname, result));
1185
1186         return true;
1187 }
1188
1189 bool ServerConfig::DoPipe(const std::string &file)
1190 {
1191         FILE* conf = popen(file.c_str(), "r");
1192         bool ret = false;
1193
1194         if (conf)
1195         {
1196                 ret = LoadConf(conf, file.c_str(), false);
1197                 pclose(conf);
1198         }
1199         else
1200                 errstr << "Couldn't execute: " << file << std::endl;
1201
1202         return ret;
1203 }
1204
1205 bool ServerConfig::StartsWithWindowsDriveLetter(const std::string &path)
1206 {
1207         return (path.length() > 2 && isalpha(path[0]) && path[1] == ':');
1208 }
1209
1210 bool ServerConfig::DoInclude(const std::string &file, bool allowexeinc)
1211 {
1212         FILE* conf = fopen(file.c_str(), "r");
1213         bool ret = false;
1214
1215         if (conf)
1216         {
1217                 ret = LoadConf(conf, file, allowexeinc);
1218                 fclose(conf);
1219         }
1220         else
1221                 errstr << "Couldn't open config file: " << file << std::endl;
1222
1223         return ret;
1224 }
1225
1226 ConfigTag* ServerConfig::ConfValue(const std::string &tag, int offset)
1227 {
1228         ConfigDataHash::size_type pos = offset;
1229         if (pos >= config_data.count(tag))
1230                 return NULL;
1231         
1232         ConfigDataHash::iterator iter = config_data.find(tag);
1233
1234         for(int i = 0; i < offset; i++)
1235                 iter++;
1236         
1237         return iter->second;
1238 }
1239
1240 bool ConfigTag::readString(const std::string& key, std::string& value, bool allow_lf)
1241 {
1242         if (!this)
1243                 return false;
1244         for(std::vector<KeyVal>::iterator j = items.begin(); j != items.end(); ++j)
1245         {
1246                 if(j->first != key)
1247                         continue;
1248                 value = j->second;
1249                 if (!allow_lf && (value.find('\n') != std::string::npos))
1250                 {
1251                         ServerInstance->Logs->Log("CONFIG",DEFAULT, "Value of <" + tag + ":" + key + "> contains a linefeed, and linefeeds in this value are not permitted -- stripped to spaces.");
1252                         for (std::string::iterator n = value.begin(); n != value.end(); n++)
1253                                 if (*n == '\n')
1254                                         *n = ' ';
1255                 }
1256                 return true;
1257         }
1258         return false;
1259 }
1260
1261 std::string ConfigTag::getString(const std::string& key, const std::string& def)
1262 {
1263         std::string res = def;
1264         if (this)
1265                 readString(key, res);
1266         return res;
1267 }
1268
1269 long ConfigTag::getInt(const std::string &key, long def)
1270 {
1271         std::string result;
1272         if(!this || !readString(key, result))
1273                 return def;
1274
1275         const char* res_cstr = result.c_str();
1276         char* res_tail = NULL;
1277         long res = strtol(res_cstr, &res_tail, 0);
1278         if (res_tail == res_cstr)
1279                 return def;
1280         switch (toupper(*res_tail))
1281         {
1282                 case 'K':
1283                         res= res* 1024;
1284                         break;
1285                 case 'M':
1286                         res= res* 1024 * 1024;
1287                         break;
1288                 case 'G':
1289                         res= res* 1024 * 1024 * 1024;
1290                         break;
1291         }
1292         return res;
1293 }
1294
1295 double ConfigTag::getFloat(const std::string &key, double def)
1296 {
1297         std::string result;
1298         if (!readString(key, result))
1299                 return def;
1300         return strtod(result.c_str(), NULL);
1301 }
1302
1303 bool ConfigTag::getBool(const std::string &key, bool def)
1304 {
1305         std::string result;
1306         if(!readString(key, result))
1307                 return def;
1308
1309         return (result == "yes" || result == "true" || result == "1" || result == "on");
1310 }
1311
1312 /** Read the contents of a file located by `fname' into a file_cache pointed at by `F'.
1313  */
1314 bool ServerConfig::ReadFile(file_cache &F, const std::string& fname)
1315 {
1316         if (fname.empty())
1317                 return false;
1318
1319         FILE* file = NULL;
1320         char linebuf[MAXBUF];
1321
1322         F.clear();
1323
1324         if (!FileExists(fname.c_str()))
1325                 return false;
1326         file = fopen(fname.c_str(), "r");
1327
1328         if (file)
1329         {
1330                 while (!feof(file))
1331                 {
1332                         if (fgets(linebuf, sizeof(linebuf), file))
1333                                 linebuf[strlen(linebuf)-1] = 0;
1334                         else
1335                                 *linebuf = 0;
1336
1337                         F.push_back(*linebuf ? linebuf : " ");
1338                 }
1339
1340                 fclose(file);
1341         }
1342         else
1343                 return false;
1344
1345         return true;
1346 }
1347
1348 bool ServerConfig::FileExists(const char* file)
1349 {
1350         struct stat sb;
1351         if (stat(file, &sb) == -1)
1352                 return false;
1353
1354         if ((sb.st_mode & S_IFDIR) > 0)
1355                 return false;
1356
1357         FILE *input = fopen(file, "r");
1358         if (input == NULL)
1359                 return false;
1360         else
1361         {
1362                 fclose(input);
1363                 return true;
1364         }
1365 }
1366
1367 const char* ServerConfig::CleanFilename(const char* name)
1368 {
1369         const char* p = name + strlen(name);
1370         while ((p != name) && (*p != '/') && (*p != '\\')) p--;
1371         return (p != name ? ++p : p);
1372 }
1373
1374
1375 std::string ServerConfig::GetSID()
1376 {
1377         return sid;
1378 }
1379
1380 void ConfigReaderThread::Run()
1381 {
1382         Config = new ServerConfig;
1383         Config->Read();
1384         done = true;
1385 }
1386
1387 void ConfigReaderThread::Finish()
1388 {
1389         ServerConfig* old = ServerInstance->Config;
1390         ServerInstance->Logs->Log("CONFIG",DEBUG,"Switching to new configuration...");
1391         ServerInstance->Logs->CloseLogs();
1392         ServerInstance->Config = this->Config;
1393         ServerInstance->Logs->OpenFileLogs();
1394         Config->Apply(old, TheUserUID);
1395
1396         if (Config->valid)
1397         {
1398                 /*
1399                  * Apply the changed configuration from the rehash.
1400                  *
1401                  * XXX: The order of these is IMPORTANT, do not reorder them without testing
1402                  * thoroughly!!!
1403                  */
1404                 ServerInstance->XLines->CheckELines();
1405                 ServerInstance->XLines->CheckELines();
1406                 ServerInstance->XLines->ApplyLines();
1407                 ServerInstance->Res->Rehash();
1408                 ServerInstance->ResetMaxBans();
1409                 Config->ApplyDisabledCommands(Config->DisabledCommands);
1410                 User* user = TheUserUID.empty() ? ServerInstance->FindNick(TheUserUID) : NULL;
1411                 FOREACH_MOD(I_OnRehash, OnRehash(user));
1412                 ServerInstance->BuildISupport();
1413
1414                 delete old;
1415         }
1416         else
1417         {
1418                 // whoops, abort!
1419                 ServerInstance->Logs->CloseLogs();
1420                 ServerInstance->Config = old;
1421                 ServerInstance->Logs->OpenFileLogs();
1422                 delete this->Config;
1423         }
1424 }