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