]> git.netwichtig.de Git - user/henk/code/inspircd.git/blob - src/configreader.cpp
Merge insp20
[user/henk/code/inspircd.git] / src / configreader.cpp
1 /*
2  * InspIRCd -- Internet Relay Chat Daemon
3  *
4  *   Copyright (C) 2009-2010 Daniel De Graaf <danieldg@inspircd.org>
5  *   Copyright (C) 2007-2009 Robin Burchell <robin+git@viroteck.net>
6  *   Copyright (C) 2006-2009 Dennis Friis <peavey@inspircd.org>
7  *   Copyright (C) 2006-2008 Craig Edwards <craigedwards@brainbox.cc>
8  *   Copyright (C) 2008 Thomas Stagner <aquanight@inspircd.org>
9  *   Copyright (C) 2006 Oliver Lupton <oliverlupton@gmail.com>
10  *
11  * This file is part of InspIRCd.  InspIRCd is free software: you can
12  * redistribute it and/or modify it under the terms of the GNU General Public
13  * License as published by the Free Software Foundation, version 2.
14  *
15  * This program is distributed in the hope that it will be useful, but WITHOUT
16  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
17  * FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
18  * details.
19  *
20  * You should have received a copy of the GNU General Public License
21  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
22  */
23
24
25 #include "inspircd.h"
26 #include "xline.h"
27 #include "listmode.h"
28 #include "exitcodes.h"
29 #include "configparser.h"
30 #include <iostream>
31
32 ServerConfig::ServerConfig()
33 {
34         RawLog = HideBans = HideSplits = UndernetMsgPrefix = false;
35         WildcardIPv6 = InvBypassModes = true;
36         dns_timeout = 5;
37         MaxTargets = 20;
38         NetBufferSize = 10240;
39         MaxConn = SOMAXCONN;
40         MaxChans = 20;
41         OperMaxChans = 30;
42         c_ipv4_range = 32;
43         c_ipv6_range = 128;
44
45         std::vector<KeyVal>* items;
46         EmptyTag = ConfigTag::create("empty", "<auto>", 0, items);
47 }
48
49 ServerConfig::~ServerConfig()
50 {
51         delete EmptyTag;
52 }
53
54 static void ValidHost(const std::string& p, const std::string& msg)
55 {
56         int num_dots = 0;
57         if (p.empty() || p[0] == '.')
58                 throw CoreException("The value of "+msg+" is not a valid hostname");
59         for (unsigned int i=0;i < p.length();i++)
60         {
61                 switch (p[i])
62                 {
63                         case ' ':
64                                 throw CoreException("The value of "+msg+" is not a valid hostname");
65                         case '.':
66                                 num_dots++;
67                         break;
68                 }
69         }
70         if (num_dots == 0)
71                 throw CoreException("The value of "+msg+" is not a valid hostname");
72 }
73
74 bool ServerConfig::ApplyDisabledCommands(const std::string& data)
75 {
76         std::stringstream dcmds(data);
77         std::string thiscmd;
78
79         /* Enable everything first */
80         const CommandParser::CommandMap& commands = ServerInstance->Parser.GetCommands();
81         for (CommandParser::CommandMap::const_iterator x = commands.begin(); x != commands.end(); ++x)
82                 x->second->Disable(false);
83
84         /* Now disable all the ones which the user wants disabled */
85         while (dcmds >> thiscmd)
86         {
87                 Command* handler = ServerInstance->Parser.GetHandler(thiscmd);
88                 if (handler)
89                         handler->Disable(true);
90         }
91         return true;
92 }
93
94 static void ReadXLine(ServerConfig* conf, const std::string& tag, const std::string& key, XLineFactory* make)
95 {
96         ConfigTagList tags = conf->ConfTags(tag);
97         for(ConfigIter i = tags.first; i != tags.second; ++i)
98         {
99                 ConfigTag* ctag = i->second;
100                 std::string mask;
101                 if (!ctag->readString(key, mask))
102                         throw CoreException("<"+tag+":"+key+"> missing at " + ctag->getTagLocation());
103                 std::string reason = ctag->getString("reason", "<Config>");
104                 XLine* xl = make->Generate(ServerInstance->Time(), 0, "<Config>", reason, mask);
105                 if (!ServerInstance->XLines->AddLine(xl, NULL))
106                         delete xl;
107         }
108 }
109
110 typedef std::map<std::string, ConfigTag*> LocalIndex;
111 void ServerConfig::CrossCheckOperClassType()
112 {
113         LocalIndex operclass;
114         ConfigTagList tags = ConfTags("class");
115         for(ConfigIter i = tags.first; i != tags.second; ++i)
116         {
117                 ConfigTag* tag = i->second;
118                 std::string name = tag->getString("name");
119                 if (name.empty())
120                         throw CoreException("<class:name> missing from tag at " + tag->getTagLocation());
121                 if (operclass.find(name) != operclass.end())
122                         throw CoreException("Duplicate class block with name " + name + " at " + tag->getTagLocation());
123                 operclass[name] = tag;
124         }
125         tags = ConfTags("type");
126         for(ConfigIter i = tags.first; i != tags.second; ++i)
127         {
128                 ConfigTag* tag = i->second;
129                 std::string name = tag->getString("name");
130                 if (name.empty())
131                         throw CoreException("<type:name> is missing from tag at " + tag->getTagLocation());
132                 if (OperTypes.find(name) != OperTypes.end())
133                         throw CoreException("Duplicate type block with name " + name + " at " + tag->getTagLocation());
134
135                 OperInfo* ifo = new OperInfo;
136                 OperTypes[name] = ifo;
137                 ifo->name = name;
138                 ifo->type_block = tag;
139
140                 std::string classname;
141                 irc::spacesepstream str(tag->getString("classes"));
142                 while (str.GetToken(classname))
143                 {
144                         LocalIndex::iterator cls = operclass.find(classname);
145                         if (cls == operclass.end())
146                                 throw CoreException("Oper type " + name + " has missing class " + classname);
147                         ifo->class_blocks.push_back(cls->second);
148                 }
149         }
150
151         tags = ConfTags("oper");
152         for(ConfigIter i = tags.first; i != tags.second; ++i)
153         {
154                 ConfigTag* tag = i->second;
155
156                 std::string name = tag->getString("name");
157                 if (name.empty())
158                         throw CoreException("<oper:name> missing from tag at " + tag->getTagLocation());
159
160                 std::string type = tag->getString("type");
161                 OperIndex::iterator tblk = OperTypes.find(type);
162                 if (tblk == OperTypes.end())
163                         throw CoreException("Oper block " + name + " has missing type " + type);
164                 if (oper_blocks.find(name) != oper_blocks.end())
165                         throw CoreException("Duplicate oper block with name " + name + " at " + tag->getTagLocation());
166
167                 OperInfo* ifo = new OperInfo;
168                 ifo->name = type;
169                 ifo->oper_block = tag;
170                 ifo->type_block = tblk->second->type_block;
171                 ifo->class_blocks.assign(tblk->second->class_blocks.begin(), tblk->second->class_blocks.end());
172                 oper_blocks[name] = ifo;
173         }
174 }
175
176 void ServerConfig::CrossCheckConnectBlocks(ServerConfig* current)
177 {
178         typedef std::map<std::string, ConnectClass*> ClassMap;
179         ClassMap oldBlocksByMask;
180         if (current)
181         {
182                 for(ClassVector::iterator i = current->Classes.begin(); i != current->Classes.end(); ++i)
183                 {
184                         ConnectClass* c = *i;
185                         if (c->name.compare(0, 8, "unnamed-", 8))
186                         {
187                                 oldBlocksByMask["n" + c->name] = c;
188                         }
189                         else if (c->type == CC_ALLOW || c->type == CC_DENY)
190                         {
191                                 std::string typeMask = (c->type == CC_ALLOW) ? "a" : "d";
192                                 typeMask += c->host;
193                                 oldBlocksByMask[typeMask] = c;
194                         }
195                 }
196         }
197
198         int blk_count = config_data.count("connect");
199         if (blk_count == 0)
200         {
201                 // No connect blocks found; make a trivial default block
202                 std::vector<KeyVal>* items;
203                 ConfigTag* tag = ConfigTag::create("connect", "<auto>", 0, items);
204                 items->push_back(std::make_pair("allow", "*"));
205                 config_data.insert(std::make_pair("connect", tag));
206                 blk_count = 1;
207         }
208
209         Classes.resize(blk_count);
210         std::map<std::string, int> names;
211
212         bool try_again = true;
213         for(int tries=0; try_again; tries++)
214         {
215                 try_again = false;
216                 ConfigTagList tags = ConfTags("connect");
217                 int i=0;
218                 for(ConfigIter it = tags.first; it != tags.second; ++it, ++i)
219                 {
220                         ConfigTag* tag = it->second;
221                         if (Classes[i])
222                                 continue;
223
224                         ConnectClass* parent = NULL;
225                         std::string parentName = tag->getString("parent");
226                         if (!parentName.empty())
227                         {
228                                 std::map<std::string,int>::iterator parentIter = names.find(parentName);
229                                 if (parentIter == names.end())
230                                 {
231                                         try_again = true;
232                                         // couldn't find parent this time. If it's the last time, we'll never find it.
233                                         if (tries >= blk_count)
234                                                 throw CoreException("Could not find parent connect class \"" + parentName + "\" for connect block at " + tag->getTagLocation());
235                                         continue;
236                                 }
237                                 parent = Classes[parentIter->second];
238                         }
239
240                         std::string name = tag->getString("name");
241                         std::string mask, typeMask;
242                         char type;
243
244                         if (tag->readString("allow", mask, false))
245                         {
246                                 type = CC_ALLOW;
247                                 typeMask = 'a' + mask;
248                         }
249                         else if (tag->readString("deny", mask, false))
250                         {
251                                 type = CC_DENY;
252                                 typeMask = 'd' + mask;
253                         }
254                         else if (!name.empty())
255                         {
256                                 type = CC_NAMED;
257                                 mask = name;
258                                 typeMask = 'n' + mask;
259                         }
260                         else
261                         {
262                                 throw CoreException("Connect class must have allow, deny, or name specified at " + tag->getTagLocation());
263                         }
264
265                         if (name.empty())
266                         {
267                                 name = "unnamed-" + ConvToStr(i);
268                         }
269                         else
270                         {
271                                 typeMask = 'n' + name;
272                         }
273
274                         if (names.find(name) != names.end())
275                                 throw CoreException("Two connect classes with name \"" + name + "\" defined!");
276                         names[name] = i;
277
278                         ConnectClass* me = parent ?
279                                 new ConnectClass(tag, type, mask, *parent) :
280                                 new ConnectClass(tag, type, mask);
281
282                         me->name = name;
283
284                         me->registration_timeout = tag->getInt("timeout", me->registration_timeout);
285                         me->pingtime = tag->getInt("pingfreq", me->pingtime);
286                         std::string sendq;
287                         if (tag->readString("sendq", sendq))
288                         {
289                                 // attempt to guess a good hard/soft sendq from a single value
290                                 long value = atol(sendq.c_str());
291                                 if (value > 16384)
292                                         me->softsendqmax = value / 16;
293                                 else
294                                         me->softsendqmax = value;
295                                 me->hardsendqmax = value * 8;
296                         }
297                         me->softsendqmax = tag->getInt("softsendq", me->softsendqmax);
298                         me->hardsendqmax = tag->getInt("hardsendq", me->hardsendqmax);
299                         me->recvqmax = tag->getInt("recvq", me->recvqmax);
300                         me->penaltythreshold = tag->getInt("threshold", me->penaltythreshold);
301                         me->commandrate = tag->getInt("commandrate", me->commandrate);
302                         me->fakelag = tag->getBool("fakelag", me->fakelag);
303                         me->maxlocal = tag->getInt("localmax", me->maxlocal);
304                         me->maxglobal = tag->getInt("globalmax", me->maxglobal);
305                         me->maxchans = tag->getInt("maxchans", me->maxchans);
306                         me->maxconnwarn = tag->getBool("maxconnwarn", me->maxconnwarn);
307                         me->limit = tag->getInt("limit", me->limit);
308                         me->resolvehostnames = tag->getBool("resolvehostnames", me->resolvehostnames);
309
310                         ClassMap::iterator oldMask = oldBlocksByMask.find(typeMask);
311                         if (oldMask != oldBlocksByMask.end())
312                         {
313                                 ConnectClass* old = oldMask->second;
314                                 oldBlocksByMask.erase(oldMask);
315                                 old->Update(me);
316                                 delete me;
317                                 me = old;
318                         }
319                         Classes[i] = me;
320                 }
321         }
322 }
323
324 /** Represents a deprecated configuration tag.
325  */
326 struct DeprecatedConfig
327 {
328         /** Tag name. */
329         std::string tag;
330
331         /** Attribute key. */
332         std::string key;
333
334         /** Attribute value. */
335         std::string value;
336
337         /** Reason for deprecation. */
338         std::string reason;
339 };
340
341 static const DeprecatedConfig ChangedConfig[] = {
342         { "bind",        "transport",   "",                 "has been moved to <bind:ssl> as of 2.0" },
343         { "die",         "value",       "",                 "you need to reread your config" },
344         { "gnutls",      "starttls",    "",                 "has been replaced with m_starttls as of 2.2" },
345         { "link",        "autoconnect", "",                 "2.0+ does not use this attribute - define <autoconnect> tags instead" },
346         { "link",        "transport",   "",                 "has been moved to <link:ssl> as of 2.0" },
347         { "module",      "name",        "m_chanprotect.so", "has been replaced with m_customprefix as of 2.2" },
348         { "module",      "name",        "m_halfop.so",      "has been replaced with m_customprefix as of 2.2" },
349         { "options",     "cyclehosts",  "",                 "has been replaced with m_hostcycle as of 2.2" },
350         { "performance", "nouserdns",   "",                 "has been moved to <connect:resolvehostnames> as of 2.2" }
351 };
352
353 void ServerConfig::Fill()
354 {
355         ConfigTag* options = ConfValue("options");
356         ConfigTag* security = ConfValue("security");
357         if (sid.empty())
358         {
359                 ServerName = ConfValue("server")->getString("name", "irc.example.com");
360                 ValidHost(ServerName, "<server:name>");
361
362                 sid = ConfValue("server")->getString("id");
363                 if (!sid.empty() && !InspIRCd::IsSID(sid))
364                         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.");
365         }
366         else
367         {
368                 if (ServerName != ConfValue("server")->getString("name"))
369                         throw CoreException("You must restart to change the server name");
370
371                 std::string nsid = ConfValue("server")->getString("id");
372                 if (!nsid.empty() && nsid != sid)
373                         throw CoreException("You must restart to change the server id");
374         }
375         SoftLimit = ConfValue("performance")->getInt("softlimit", (SocketEngine::GetMaxFds() > 0 ? SocketEngine::GetMaxFds() : LONG_MAX), 10);
376         CCOnConnect = ConfValue("performance")->getBool("clonesonconnect", true);
377         MaxConn = ConfValue("performance")->getInt("somaxconn", SOMAXCONN);
378         XLineMessage = options->getString("xlinemessage", options->getString("moronbanner", "You're banned!"));
379         ServerDesc = ConfValue("server")->getString("description", "Configure Me");
380         Network = ConfValue("server")->getString("network", "Network");
381         NetBufferSize = ConfValue("performance")->getInt("netbuffersize", 10240, 1024, 65534);
382         dns_timeout = ConfValue("dns")->getInt("timeout", 5);
383         DisabledCommands = ConfValue("disabled")->getString("commands", "");
384         DisabledDontExist = ConfValue("disabled")->getBool("fakenonexistant");
385         UserStats = security->getString("userstats");
386         CustomVersion = security->getString("customversion");
387         HideSplits = security->getBool("hidesplits");
388         HideBans = security->getBool("hidebans");
389         HideWhoisServer = security->getString("hidewhois");
390         HideKillsServer = security->getString("hidekills");
391         RestrictBannedUsers = security->getBool("restrictbannedusers", true);
392         GenericOper = security->getBool("genericoper");
393         SyntaxHints = options->getBool("syntaxhints");
394         CycleHostsFromUser = options->getBool("cyclehostsfromuser");
395         UndernetMsgPrefix = options->getBool("ircumsgprefix");
396         FullHostInTopic = options->getBool("hostintopic");
397         MaxTargets = security->getInt("maxtargets", 20, 1, 31);
398         DefaultModes = options->getString("defaultmodes", "not");
399         PID = ConfValue("pid")->getString("file");
400         MaxChans = ConfValue("channels")->getInt("users", 20);
401         OperMaxChans = ConfValue("channels")->getInt("opers");
402         c_ipv4_range = ConfValue("cidr")->getInt("ipv4clone", 32);
403         c_ipv6_range = ConfValue("cidr")->getInt("ipv6clone", 128);
404         Limits.NickMax = ConfValue("limits")->getInt("maxnick", 32);
405         Limits.ChanMax = ConfValue("limits")->getInt("maxchan", 64);
406         Limits.MaxModes = ConfValue("limits")->getInt("maxmodes", 20);
407         Limits.IdentMax = ConfValue("limits")->getInt("maxident", 11);
408         Limits.MaxHost = ConfValue("limits")->getInt("maxhost", 64);
409         Limits.MaxQuit = ConfValue("limits")->getInt("maxquit", 255);
410         Limits.MaxTopic = ConfValue("limits")->getInt("maxtopic", 307);
411         Limits.MaxKick = ConfValue("limits")->getInt("maxkick", 255);
412         Limits.MaxGecos = ConfValue("limits")->getInt("maxgecos", 128);
413         Limits.MaxAway = ConfValue("limits")->getInt("maxaway", 200);
414         Limits.MaxLine = ConfValue("limits")->getInt("maxline", 512);
415         Paths.Config = ConfValue("path")->getString("configdir", INSPIRCD_CONFIG_PATH);
416         Paths.Data = ConfValue("path")->getString("datadir", INSPIRCD_DATA_PATH);
417         Paths.Log = ConfValue("path")->getString("logdir", INSPIRCD_LOG_PATH);
418         Paths.Module = ConfValue("path")->getString("moduledir", INSPIRCD_MODULE_PATH);
419         InvBypassModes = options->getBool("invitebypassmodes", true);
420         NoSnoticeStack = options->getBool("nosnoticestack", false);
421
422         if (Network.find(' ') != std::string::npos)
423                 throw CoreException(Network + " is not a valid network name. A network name must not contain spaces.");
424
425         std::string defbind = options->getString("defaultbind");
426         if (assign(defbind) == "ipv4")
427         {
428                 WildcardIPv6 = false;
429         }
430         else if (assign(defbind) == "ipv6")
431         {
432                 WildcardIPv6 = true;
433         }
434         else
435         {
436                 WildcardIPv6 = true;
437                 int socktest = socket(AF_INET6, SOCK_STREAM, 0);
438                 if (socktest < 0)
439                         WildcardIPv6 = false;
440                 else
441                         SocketEngine::Close(socktest);
442         }
443
444         ReadXLine(this, "badip", "ipmask", ServerInstance->XLines->GetFactory("Z"));
445         ReadXLine(this, "badnick", "nick", ServerInstance->XLines->GetFactory("Q"));
446         ReadXLine(this, "badhost", "host", ServerInstance->XLines->GetFactory("K"));
447         ReadXLine(this, "exception", "host", ServerInstance->XLines->GetFactory("E"));
448
449         memset(DisabledUModes, 0, sizeof(DisabledUModes));
450         std::string modes = ConfValue("disabled")->getString("usermodes");
451         for (std::string::const_iterator p = modes.begin(); p != modes.end(); ++p)
452         {
453                 // Complain when the character is not a-z or A-Z
454                 if ((*p < 'A') || (*p > 'z') || ((*p < 'a') && (*p > 'Z')))
455                         throw CoreException("Invalid usermode " + std::string(1, *p) + " was found.");
456                 DisabledUModes[*p - 'A'] = 1;
457         }
458
459         memset(DisabledCModes, 0, sizeof(DisabledCModes));
460         modes = ConfValue("disabled")->getString("chanmodes");
461         for (std::string::const_iterator p = modes.begin(); p != modes.end(); ++p)
462         {
463                 if ((*p < 'A') || (*p > 'z') || ((*p < 'a') && (*p > 'Z')))
464                         throw CoreException("Invalid chanmode " + std::string(1, *p) + " was found.");
465                 DisabledCModes[*p - 'A'] = 1;
466         }
467
468         std::string v = security->getString("announceinvites");
469
470         if (v == "ops")
471                 AnnounceInvites = ServerConfig::INVITE_ANNOUNCE_OPS;
472         else if (v == "all")
473                 AnnounceInvites = ServerConfig::INVITE_ANNOUNCE_ALL;
474         else if (v == "dynamic")
475                 AnnounceInvites = ServerConfig::INVITE_ANNOUNCE_DYNAMIC;
476         else
477                 AnnounceInvites = ServerConfig::INVITE_ANNOUNCE_NONE;
478
479         v = security->getString("operspywhois");
480         if (v == "splitmsg")
481                 OperSpyWhois = SPYWHOIS_SPLITMSG;
482         else if (v == "on" || v == "yes")
483                 OperSpyWhois = SPYWHOIS_SINGLEMSG;
484         else
485                 OperSpyWhois = SPYWHOIS_NONE;
486 }
487
488 // WARNING: it is not safe to use most of the codebase in this function, as it
489 // will run in the config reader thread
490 void ServerConfig::Read()
491 {
492         /* Load and parse the config file, if there are any errors then explode */
493
494         ParseStack stack(this);
495         try
496         {
497                 valid = stack.ParseFile(ServerInstance->ConfigFileName, 0);
498         }
499         catch (CoreException& err)
500         {
501                 valid = false;
502                 errstr << err.GetReason() << std::endl;
503         }
504 }
505
506 void ServerConfig::Apply(ServerConfig* old, const std::string &useruid)
507 {
508         valid = true;
509         if (old)
510         {
511                 /*
512                  * These values can only be set on boot. Keep their old values. Do it before we send messages so we actually have a servername.
513                  */
514                 this->ServerName = old->ServerName;
515                 this->sid = old->sid;
516                 this->cmdline = old->cmdline;
517         }
518
519         /* The stuff in here may throw CoreException, be sure we're in a position to catch it. */
520         try
521         {
522                 for (int index = 0; index * sizeof(DeprecatedConfig) < sizeof(ChangedConfig); index++)
523                 {
524                         std::string value;
525                         ConfigTagList tags = ConfTags(ChangedConfig[index].tag);
526                         for(ConfigIter i = tags.first; i != tags.second; ++i)
527                         {
528                                 if (i->second->readString(ChangedConfig[index].key, value, true)
529                                         && (ChangedConfig[index].value.empty() || value == ChangedConfig[index].value))
530                                 {
531                                         errstr << "Your configuration contains a deprecated value: <"  << ChangedConfig[index].tag;
532                                         if (ChangedConfig[index].value.empty())
533                                         {
534                                                 errstr << ':' << ChangedConfig[index].key;
535                                         }
536                                         else
537                                         {
538                                                 errstr << ' ' << ChangedConfig[index].key << "=\"" << ChangedConfig[index].value << "\"";
539                                         }
540                                         errstr << "> - " << ChangedConfig[index].reason << " (at " << i->second->getTagLocation() << ")" << std::endl;
541                                 }
542                         }
543                 }
544
545                 Fill();
546
547                 // Handle special items
548                 CrossCheckOperClassType();
549                 CrossCheckConnectBlocks(old);
550         }
551         catch (CoreException &ce)
552         {
553                 errstr << ce.GetReason();
554         }
555
556         // Check errors before dealing with failed binds, since continuing on failed bind is wanted in some circumstances.
557         valid = errstr.str().empty();
558
559         // write once here, to try it out and make sure its ok
560         if (valid)
561                 ServerInstance->WritePID(this->PID);
562
563         ConfigTagList binds = ConfTags("bind");
564         if (binds.first == binds.second)
565                  errstr << "Possible configuration error: you have not defined any <bind> blocks." << std::endl
566                          << "You will need to do this if you want clients to be able to connect!" << std::endl;
567
568         if (old)
569         {
570                 // On first run, ports are bound later on
571                 FailedPortList pl;
572                 ServerInstance->BindPorts(pl);
573                 if (pl.size())
574                 {
575                         errstr << "Not all your client ports could be bound." << std::endl
576                                 << "The following port(s) failed to bind:" << std::endl;
577
578                         int j = 1;
579                         for (FailedPortList::iterator i = pl.begin(); i != pl.end(); i++, j++)
580                         {
581                                 errstr << j << ".\tAddress: " << (i->first.empty() ? "<all>" : i->first.c_str()) << "\tReason: "
582                                         << i->second << std::endl;
583                         }
584                 }
585         }
586
587         User* user = useruid.empty() ? NULL : ServerInstance->FindNick(useruid);
588
589         if (!valid)
590         {
591                 ServerInstance->Logs->Log("CONFIG", LOG_DEFAULT, "There were errors in your configuration file:");
592                 Classes.clear();
593         }
594
595         while (errstr.good())
596         {
597                 std::string line;
598                 getline(errstr, line, '\n');
599                 if (line.empty())
600                         continue;
601                 // On startup, print out to console (still attached at this point)
602                 if (!old)
603                         std::cout << line << std::endl;
604                 // If a user is rehashing, tell them directly
605                 if (user)
606                         user->SendText(":%s NOTICE %s :*** %s", ServerInstance->Config->ServerName.c_str(), user->nick.c_str(), line.c_str());
607                 // Also tell opers
608                 ServerInstance->SNO->WriteGlobalSno('a', line);
609         }
610
611         errstr.clear();
612         errstr.str(std::string());
613
614         // Re-parse our MOTD and RULES files for colors -- Justasic
615         for (ClassVector::const_iterator it = this->Classes.begin(), it_end = this->Classes.end(); it != it_end; ++it)
616         {
617                 ConfigTag *tag = (*it)->config;
618                 // Make sure our connection class allows motd colors
619                 if(!tag->getBool("allowmotdcolors"))
620                       continue;
621
622                 ConfigFileCache::iterator file = this->Files.find(tag->getString("motd", "motd"));
623                 if (file != this->Files.end())
624                       InspIRCd::ProcessColors(file->second);
625         }
626
627         /* No old configuration -> initial boot, nothing more to do here */
628         if (!old)
629         {
630                 if (!valid)
631                 {
632                         ServerInstance->Exit(EXIT_STATUS_CONFIG);
633                 }
634
635                 return;
636         }
637
638
639         // If there were errors processing configuration, don't touch modules.
640         if (!valid)
641                 return;
642
643         ApplyModules(user);
644
645         if (user)
646                 user->SendText(":%s NOTICE %s :*** Successfully rehashed server.",
647                         ServerInstance->Config->ServerName.c_str(), user->nick.c_str());
648         ServerInstance->SNO->WriteGlobalSno('a', "*** Successfully rehashed server.");
649 }
650
651 void ServerConfig::ApplyModules(User* user)
652 {
653         std::vector<std::string> added_modules;
654         ModuleManager::ModuleMap removed_modules = ServerInstance->Modules->GetModules();
655
656         ConfigTagList tags = ConfTags("module");
657         for(ConfigIter i = tags.first; i != tags.second; ++i)
658         {
659                 ConfigTag* tag = i->second;
660                 std::string name;
661                 if (tag->readString("name", name))
662                 {
663                         // if this module is already loaded, the erase will succeed, so we need do nothing
664                         // otherwise, we need to add the module (which will be done later)
665                         if (removed_modules.erase(name) == 0)
666                                 added_modules.push_back(name);
667                 }
668         }
669
670         for (ModuleManager::ModuleMap::iterator i = removed_modules.begin(); i != removed_modules.end(); ++i)
671         {
672                 const std::string& modname = i->first;
673                 // Don't remove core_*.so, just remove m_*.so
674                 if (modname.c_str()[0] == 'c')
675                         continue;
676                 if (ServerInstance->Modules->Unload(i->second))
677                 {
678                         ServerInstance->SNO->WriteGlobalSno('a', "*** REHASH UNLOADED MODULE: %s", modname.c_str());
679
680                         if (user)
681                                 user->WriteNumeric(RPL_UNLOADEDMODULE, "%s :Module %s successfully unloaded.", modname.c_str(), modname.c_str());
682                         else
683                                 ServerInstance->SNO->WriteGlobalSno('a', "Module %s successfully unloaded.", modname.c_str());
684                 }
685                 else
686                 {
687                         if (user)
688                                 user->WriteNumeric(ERR_CANTUNLOADMODULE, "%s :Failed to unload module %s: %s", modname.c_str(), modname.c_str(), ServerInstance->Modules->LastError().c_str());
689                         else
690                                  ServerInstance->SNO->WriteGlobalSno('a', "Failed to unload module %s: %s", modname.c_str(), ServerInstance->Modules->LastError().c_str());
691                 }
692         }
693
694         for (std::vector<std::string>::iterator adding = added_modules.begin(); adding != added_modules.end(); adding++)
695         {
696                 if (ServerInstance->Modules->Load(adding->c_str()))
697                 {
698                         ServerInstance->SNO->WriteGlobalSno('a', "*** REHASH LOADED MODULE: %s",adding->c_str());
699                         if (user)
700                                 user->WriteNumeric(RPL_LOADEDMODULE, "%s :Module %s successfully loaded.", adding->c_str(), adding->c_str());
701                         else
702                                 ServerInstance->SNO->WriteGlobalSno('a', "Module %s successfully loaded.", adding->c_str());
703                 }
704                 else
705                 {
706                         if (user)
707                                 user->WriteNumeric(ERR_CANTLOADMODULE, "%s :Failed to load module %s: %s", adding->c_str(), adding->c_str(), ServerInstance->Modules->LastError().c_str());
708                         else
709                                 ServerInstance->SNO->WriteGlobalSno('a', "Failed to load module %s: %s", adding->c_str(), ServerInstance->Modules->LastError().c_str());
710                 }
711         }
712 }
713
714 ConfigTag* ServerConfig::ConfValue(const std::string &tag)
715 {
716         ConfigTagList found = config_data.equal_range(tag);
717         if (found.first == found.second)
718                 return EmptyTag;
719         ConfigTag* rv = found.first->second;
720         found.first++;
721         if (found.first != found.second)
722                 ServerInstance->Logs->Log("CONFIG", LOG_DEFAULT, "Multiple <" + tag + "> tags found; only first will be used "
723                         "(first at " + rv->getTagLocation() + "; second at " + found.first->second->getTagLocation() + ")");
724         return rv;
725 }
726
727 ConfigTagList ServerConfig::ConfTags(const std::string& tag)
728 {
729         return config_data.equal_range(tag);
730 }
731
732 std::string ServerConfig::Escape(const std::string& str, bool xml)
733 {
734         std::string escaped;
735         for (std::string::const_iterator it = str.begin(); it != str.end(); ++it)
736         {
737                 switch (*it)
738                 {
739                         case '"':
740                                 escaped += xml ? "&quot;" : "\"";
741                                 break;
742                         case '&':
743                                 escaped += xml ? "&amp;" : "&";
744                                 break;
745                         case '\\':
746                                 escaped += xml ? "\\" : "\\\\";
747                                 break;
748                         default:
749                                 escaped += *it;
750                                 break;
751                 }
752         }
753         return escaped;
754 }
755
756 void ConfigReaderThread::Run()
757 {
758         Config->Read();
759         done = true;
760 }
761
762 void ConfigReaderThread::Finish()
763 {
764         ServerConfig* old = ServerInstance->Config;
765         ServerInstance->Logs->Log("CONFIG", LOG_DEBUG, "Switching to new configuration...");
766         ServerInstance->Config = this->Config;
767         Config->Apply(old, TheUserUID);
768
769         if (Config->valid)
770         {
771                 /*
772                  * Apply the changed configuration from the rehash.
773                  *
774                  * XXX: The order of these is IMPORTANT, do not reorder them without testing
775                  * thoroughly!!!
776                  */
777                 ServerInstance->XLines->CheckELines();
778                 ServerInstance->XLines->ApplyLines();
779                 ChanModeReference ban(NULL, "ban");
780                 static_cast<ListModeBase*>(*ban)->DoRehash();
781                 Config->ApplyDisabledCommands(Config->DisabledCommands);
782                 User* user = ServerInstance->FindNick(TheUserUID);
783
784                 ConfigStatus status(user);
785                 const ModuleManager::ModuleMap& mods = ServerInstance->Modules->GetModules();
786                 for (ModuleManager::ModuleMap::const_iterator i = mods.begin(); i != mods.end(); ++i)
787                         i->second->ReadConfig(status);
788
789                 // The description of this server may have changed - update it for WHOIS etc.
790                 ServerInstance->FakeClient->server->description = Config->ServerDesc;
791
792                 ServerInstance->ISupport.Build();
793
794                 ServerInstance->Logs->CloseLogs();
795                 ServerInstance->Logs->OpenFileLogs();
796
797                 if (Config->RawLog && !old->RawLog)
798                         ServerInstance->Users->ServerNoticeAll("*** Raw I/O logging is enabled on this server. All messages, passwords, and commands are being recorded.");
799
800                 Config = old;
801         }
802         else
803         {
804                 // whoops, abort!
805                 ServerInstance->Config = old;
806         }
807 }