]> git.netwichtig.de Git - user/henk/code/inspircd.git/blob - src/configreader.cpp
Fix using std::cout instead of errstr when a port fails to bind.
[user/henk/code/inspircd.git] / src / configreader.cpp
1 /*
2  * InspIRCd -- Internet Relay Chat Daemon
3  *
4  *   Copyright (C) 2019 Matt Schatz <genius3000@g3k.solutions>
5  *   Copyright (C) 2013-2016 Attila Molnar <attilamolnar@hush.com>
6  *   Copyright (C) 2013-2014, 2016-2021 Sadie Powell <sadie@witchery.services>
7  *   Copyright (C) 2013 Daniel Vassdal <shutter@canternet.org>
8  *   Copyright (C) 2012 Robby <robby@chatbelgie.be>
9  *   Copyright (C) 2012 Justin Crawford <Justasic@Gmail.com>
10  *   Copyright (C) 2012 DjSlash <djslash@djslash.org>
11  *   Copyright (C) 2012 ChrisTX <xpipe@hotmail.de>
12  *   Copyright (C) 2009-2011 Daniel De Graaf <danieldg@inspircd.org>
13  *   Copyright (C) 2009 Uli Schlachter <psychon@inspircd.org>
14  *   Copyright (C) 2008 Thomas Stagner <aquanight@inspircd.org>
15  *   Copyright (C) 2007-2010 Robin Burchell <robin+git@viroteck.net>
16  *   Copyright (C) 2007 Dennis Friis <peavey@inspircd.org>
17  *   Copyright (C) 2006-2008 Craig Edwards <brain@inspircd.org>
18  *
19  * This file is part of InspIRCd.  InspIRCd is free software: you can
20  * redistribute it and/or modify it under the terms of the GNU General Public
21  * License as published by the Free Software Foundation, version 2.
22  *
23  * This program is distributed in the hope that it will be useful, but WITHOUT
24  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
25  * FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
26  * details.
27  *
28  * You should have received a copy of the GNU General Public License
29  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
30  */
31
32
33 #include "inspircd.h"
34 #include "xline.h"
35 #include "listmode.h"
36 #include "exitcodes.h"
37 #include "configparser.h"
38 #include <iostream>
39
40 ServerLimits::ServerLimits(ConfigTag* tag)
41         : MaxLine(tag->getUInt("maxline", 512, 512))
42         , NickMax(tag->getUInt("maxnick", 30, 1, MaxLine))
43         , ChanMax(tag->getUInt("maxchan", 64, 1, MaxLine))
44         , MaxModes(tag->getUInt("maxmodes", 20, 1))
45         , IdentMax(tag->getUInt("maxident", 10, 1))
46         , MaxQuit(tag->getUInt("maxquit", 255, 0, MaxLine))
47         , MaxTopic(tag->getUInt("maxtopic", 307, 1, MaxLine))
48         , MaxKick(tag->getUInt("maxkick", 255, 1, MaxLine))
49         , MaxReal(tag->getUInt("maxreal", tag->getUInt("maxgecos", 128), 1, MaxLine))
50         , MaxAway(tag->getUInt("maxaway", 200, 1, MaxLine))
51         , MaxHost(tag->getUInt("maxhost", 64, 1, MaxLine))
52 {
53 }
54
55 ServerConfig::ServerPaths::ServerPaths(ConfigTag* tag)
56         : Config(tag->getString("configdir", INSPIRCD_CONFIG_PATH, 1))
57         , Data(tag->getString("datadir", INSPIRCD_DATA_PATH, 1))
58         , Log(tag->getString("logdir", INSPIRCD_LOG_PATH, 1))
59         , Module(tag->getString("moduledir", INSPIRCD_MODULE_PATH, 1))
60         , Runtime(tag->getString("runtimedir", INSPIRCD_RUNTIME_PATH, 1))
61 {
62 }
63
64 static ConfigTag* CreateEmptyTag()
65 {
66         ConfigItems* items;
67         return ConfigTag::create("empty", "<auto>", 0, items);
68 }
69
70 ServerConfig::ServerConfig()
71         : EmptyTag(CreateEmptyTag())
72         , Limits(EmptyTag)
73         , Paths(EmptyTag)
74         , RawLog(false)
75         , NoSnoticeStack(false)
76 {
77 }
78
79 ServerConfig::~ServerConfig()
80 {
81         delete EmptyTag;
82 }
83
84 static void ReadXLine(ServerConfig* conf, const std::string& tag, const std::string& key, XLineFactory* make)
85 {
86         insp::flat_set<std::string> configlines;
87
88         ConfigTagList tags = conf->ConfTags(tag);
89         for(ConfigIter i = tags.first; i != tags.second; ++i)
90         {
91                 ConfigTag* ctag = i->second;
92
93                 const std::string mask = ctag->getString(key);
94                 if (mask.empty())
95                         throw CoreException("<" + tag + ":" + key + "> missing at " + ctag->getTagLocation());
96
97                 const std::string reason = ctag->getString("reason");
98                 if (reason.empty())
99                         throw CoreException("<" + tag + ":reason> missing at " + ctag->getTagLocation());
100
101                 XLine* xl = make->Generate(ServerInstance->Time(), 0, ServerInstance->Config->ServerName, reason, mask);
102                 xl->from_config = true;
103                 configlines.insert(xl->Displayable());
104                 if (!ServerInstance->XLines->AddLine(xl, NULL))
105                         delete xl;
106         }
107
108         ServerInstance->XLines->ExpireRemovedConfigLines(make->GetType(), configlines);
109 }
110
111 typedef std::map<std::string, ConfigTag*> LocalIndex;
112 void ServerConfig::CrossCheckOperClassType()
113 {
114         LocalIndex operclass;
115         ConfigTagList tags = ConfTags("class");
116         for(ConfigIter i = tags.first; i != tags.second; ++i)
117         {
118                 ConfigTag* tag = i->second;
119                 std::string name = tag->getString("name");
120                 if (name.empty())
121                         throw CoreException("<class:name> missing from tag at " + tag->getTagLocation());
122                 if (operclass.find(name) != operclass.end())
123                         throw CoreException("Duplicate class block with name " + name + " at " + tag->getTagLocation());
124                 operclass[name] = tag;
125         }
126         tags = ConfTags("type");
127         for(ConfigIter i = tags.first; i != tags.second; ++i)
128         {
129                 ConfigTag* tag = i->second;
130                 std::string name = tag->getString("name");
131                 if (name.empty())
132                         throw CoreException("<type:name> is missing from tag at " + tag->getTagLocation());
133                 if (OperTypes.find(name) != OperTypes.end())
134                         throw CoreException("Duplicate type block with name " + name + " at " + tag->getTagLocation());
135
136                 OperInfo* ifo = new OperInfo(name);
137                 OperTypes[name] = ifo;
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(type);
168                 ifo->oper_block = tag;
169                 ifo->type_block = tblk->second->type_block;
170                 ifo->class_blocks.assign(tblk->second->class_blocks.begin(), tblk->second->class_blocks.end());
171                 oper_blocks[name] = ifo;
172         }
173 }
174
175 void ServerConfig::CrossCheckConnectBlocks(ServerConfig* current)
176 {
177         typedef std::map<std::string, ConnectClass*> ClassMap;
178         ClassMap oldBlocksByMask;
179         if (current)
180         {
181                 for(ClassVector::iterator i = current->Classes.begin(); i != current->Classes.end(); ++i)
182                 {
183                         ConnectClass* c = *i;
184                         if (c->name.compare(0, 8, "unnamed-", 8))
185                         {
186                                 oldBlocksByMask["n" + c->name] = c;
187                         }
188                         else if (c->type == CC_ALLOW || c->type == CC_DENY)
189                         {
190                                 std::string typeMask = (c->type == CC_ALLOW) ? "a" : "d";
191                                 typeMask += c->host;
192                                 oldBlocksByMask[typeMask] = c;
193                         }
194                 }
195         }
196
197         size_t blk_count = config_data.count("connect");
198         if (blk_count == 0)
199         {
200                 // No connect blocks found; make a trivial default block
201                 ConfigItems* items;
202                 ConfigTag* tag = ConfigTag::create("connect", "<auto>", 0, items);
203                 (*items)["allow"] = "*";
204                 config_data.insert(std::make_pair("connect", tag));
205                 blk_count = 1;
206         }
207
208         Classes.resize(blk_count);
209         std::map<std::string, size_t> names;
210
211         bool try_again = true;
212         for(size_t tries = 0; try_again; tries++)
213         {
214                 try_again = false;
215                 ConfigTagList tags = ConfTags("connect");
216                 size_t i = 0;
217                 for(ConfigIter it = tags.first; it != tags.second; ++it, ++i)
218                 {
219                         ConfigTag* tag = it->second;
220                         if (Classes[i])
221                                 continue;
222
223                         ConnectClass* parent = NULL;
224                         std::string parentName = tag->getString("parent");
225                         if (!parentName.empty())
226                         {
227                                 std::map<std::string, size_t>::const_iterator parentIter = names.find(parentName);
228                                 if (parentIter == names.end())
229                                 {
230                                         try_again = true;
231                                         // couldn't find parent this time. If it's the last time, we'll never find it.
232                                         if (tries >= blk_count)
233                                                 throw CoreException("Could not find parent connect class \"" + parentName + "\" for connect block at " + tag->getTagLocation());
234                                         continue;
235                                 }
236                                 parent = Classes[parentIter->second];
237                         }
238
239                         std::string name = tag->getString("name");
240                         std::string mask, typeMask;
241                         char type;
242
243                         if (tag->readString("allow", mask, false))
244                         {
245                                 type = CC_ALLOW;
246                                 typeMask = 'a' + mask;
247                         }
248                         else if (tag->readString("deny", mask, false))
249                         {
250                                 type = CC_DENY;
251                                 typeMask = 'd' + mask;
252                         }
253                         else if (!name.empty())
254                         {
255                                 type = CC_NAMED;
256                                 mask = name;
257                                 typeMask = 'n' + mask;
258                         }
259                         else
260                         {
261                                 throw CoreException("Connect class must have allow, deny, or name specified at " + tag->getTagLocation());
262                         }
263
264                         if (name.empty())
265                         {
266                                 name = "unnamed-" + ConvToStr(i);
267                         }
268                         else
269                         {
270                                 typeMask = 'n' + name;
271                         }
272
273                         if (names.find(name) != names.end())
274                                 throw CoreException("Two connect classes with name \"" + name + "\" defined!");
275                         names[name] = i;
276
277                         ConnectClass* me = parent ?
278                                 new ConnectClass(tag, type, mask, *parent) :
279                                 new ConnectClass(tag, type, mask);
280
281                         me->name = name;
282
283                         me->registration_timeout = tag->getDuration("timeout", me->registration_timeout);
284                         me->pingtime = tag->getDuration("pingfreq", me->pingtime);
285                         std::string sendq;
286                         if (tag->readString("sendq", sendq))
287                         {
288                                 // attempt to guess a good hard/soft sendq from a single value
289                                 unsigned long value = strtoul(sendq.c_str(), NULL, 10);
290                                 if (value > 16384)
291                                         me->softsendqmax = value / 16;
292                                 else
293                                         me->softsendqmax = value;
294                                 me->hardsendqmax = value * 8;
295                         }
296                         me->softsendqmax = tag->getUInt("softsendq", me->softsendqmax);
297                         me->hardsendqmax = tag->getUInt("hardsendq", me->hardsendqmax);
298                         me->recvqmax = tag->getUInt("recvq", me->recvqmax);
299                         me->penaltythreshold = tag->getUInt("threshold", me->penaltythreshold);
300                         me->commandrate = tag->getUInt("commandrate", me->commandrate);
301                         me->fakelag = tag->getBool("fakelag", me->fakelag);
302                         me->maxlocal = tag->getUInt("localmax", me->maxlocal);
303                         me->maxglobal = tag->getUInt("globalmax", me->maxglobal);
304                         me->maxchans = tag->getUInt("maxchans", me->maxchans);
305                         me->maxconnwarn = tag->getBool("maxconnwarn", me->maxconnwarn);
306                         me->limit = tag->getUInt("limit", me->limit);
307                         me->resolvehostnames = tag->getBool("resolvehostnames", me->resolvehostnames);
308                         me->password = tag->getString("password", me->password);
309
310                         me->passwordhash = tag->getString("hash", me->passwordhash);
311                         if (!me->password.empty() && (me->passwordhash.empty() || stdalgo::string::equalsci(me->passwordhash, "plaintext")))
312                         {
313                                 ServerInstance->Logs->Log("CONNECTCLASS", LOG_DEFAULT, "<connect> tag '%s' at %s contains an plain text password, this is insecure!",
314                                         name.c_str(), tag->getTagLocation().c_str());
315                         }
316
317                         std::string ports = tag->getString("port");
318                         if (!ports.empty())
319                         {
320                                 irc::portparser portrange(ports, false);
321                                 while (int port = portrange.GetToken())
322                                         me->ports.insert(port);
323                         }
324
325                         ClassMap::iterator oldMask = oldBlocksByMask.find(typeMask);
326                         if (oldMask != oldBlocksByMask.end())
327                         {
328                                 ConnectClass* old = oldMask->second;
329                                 oldBlocksByMask.erase(oldMask);
330                                 old->Update(me);
331                                 delete me;
332                                 me = old;
333                         }
334                         Classes[i] = me;
335                 }
336         }
337 }
338
339 static std::string GetServerHost()
340 {
341 #ifndef _WIN32
342         char hostname[256];
343         if (gethostname(hostname, sizeof(hostname)) == 0)
344         {
345                 std::string name(hostname);
346                 if (name.find('.') == std::string::npos)
347                         name.push_back('.');
348
349                 if (name.length() <= ServerInstance->Config->Limits.MaxHost && InspIRCd::IsHost(name))
350                         return name;
351         }
352 #endif
353         return "irc.example.com";
354 }
355
356 void ServerConfig::Fill()
357 {
358         ConfigTag* options = ConfValue("options");
359         ConfigTag* security = ConfValue("security");
360         ConfigTag* server = ConfValue("server");
361         if (sid.empty())
362         {
363                 ServerName = server->getString("name", GetServerHost(), InspIRCd::IsHost);
364
365                 sid = server->getString("id");
366                 if (!sid.empty() && !InspIRCd::IsSID(sid))
367                         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.");
368
369                 CaseMapping = options->getString("casemapping", "rfc1459", 1);
370                 if (CaseMapping == "ascii")
371                         national_case_insensitive_map = ascii_case_insensitive_map;
372                 else if (CaseMapping == "rfc1459")
373                         national_case_insensitive_map = rfc_case_insensitive_map;
374                 else
375                         throw CoreException("<options:casemapping> must be set to 'ascii', or 'rfc1459'");
376         }
377         else
378         {
379                 std::string name = server->getString("name");
380                 if (!name.empty() && name != ServerName)
381                         throw CoreException("You must restart to change the server name");
382
383                 std::string nsid = server->getString("id");
384                 if (!nsid.empty() && nsid != sid)
385                         throw CoreException("You must restart to change the server id");
386
387                 std::string casemapping = options->getString("casemapping");
388                 // Ignore this value if CaseMapping is set to something the core doesn't provide (i.e., m_nationalchars).
389                 if (!casemapping.empty() && casemapping != CaseMapping && (CaseMapping == "ascii" || CaseMapping == "rfc1459"))
390                         throw CoreException("You must restart to change the server casemapping");
391
392         }
393         SoftLimit = ConfValue("performance")->getUInt("softlimit", (SocketEngine::GetMaxFds() > 0 ? SocketEngine::GetMaxFds() : LONG_MAX), 10);
394         CCOnConnect = ConfValue("performance")->getBool("clonesonconnect", true);
395         MaxConn = ConfValue("performance")->getUInt("somaxconn", SOMAXCONN);
396         TimeSkipWarn = ConfValue("performance")->getDuration("timeskipwarn", 2, 0, 30);
397         XLineMessage = options->getString("xlinemessage", options->getString("moronbanner", "You're banned!"));
398         ServerDesc = server->getString("description", "Configure Me", 1);
399         Network = server->getString("network", "Network", 1);
400         NetBufferSize = ConfValue("performance")->getInt("netbuffersize", 10240, 1024, 65534);
401         CustomVersion = security->getString("customversion");
402         HideBans = security->getBool("hidebans");
403         HideServer = security->getString("hideserver", security->getString("hidewhois"));
404         SyntaxHints = options->getBool("syntaxhints");
405         FullHostInTopic = options->getBool("hostintopic");
406         MaxTargets = security->getUInt("maxtargets", 20, 1, 31);
407         DefaultModes = options->getString("defaultmodes", "not");
408         PID = ConfValue("pid")->getString("file");
409         MaxChans = ConfValue("channels")->getUInt("users", 20);
410         OperMaxChans = ConfValue("channels")->getUInt("opers", 0);
411         c_ipv4_range = ConfValue("cidr")->getUInt("ipv4clone", 32, 1, 32);
412         c_ipv6_range = ConfValue("cidr")->getUInt("ipv6clone", 128, 1, 128);
413         Limits = ServerLimits(ConfValue("limits"));
414         Paths = ServerPaths(ConfValue("path"));
415         NoSnoticeStack = options->getBool("nosnoticestack", false);
416
417         std::string defbind = options->getString("defaultbind");
418         if (stdalgo::string::equalsci(defbind, "ipv4"))
419         {
420                 WildcardIPv6 = false;
421         }
422         else if (stdalgo::string::equalsci(defbind, "ipv6"))
423         {
424                 WildcardIPv6 = true;
425         }
426         else
427         {
428                 WildcardIPv6 = true;
429                 int socktest = socket(AF_INET6, SOCK_STREAM, 0);
430                 if (socktest < 0)
431                         WildcardIPv6 = false;
432                 else
433                         SocketEngine::Close(socktest);
434         }
435
436         ReadXLine(this, "badip", "ipmask", ServerInstance->XLines->GetFactory("Z"));
437         ReadXLine(this, "badnick", "nick", ServerInstance->XLines->GetFactory("Q"));
438         ReadXLine(this, "badhost", "host", ServerInstance->XLines->GetFactory("K"));
439         ReadXLine(this, "exception", "host", ServerInstance->XLines->GetFactory("E"));
440
441         const std::string restrictbannedusers = options->getString("restrictbannedusers", "yes", 1);
442         if (stdalgo::string::equalsci(restrictbannedusers, "no"))
443                 RestrictBannedUsers = ServerConfig::BUT_NORMAL;
444         else if (stdalgo::string::equalsci(restrictbannedusers, "silent"))
445                 RestrictBannedUsers = ServerConfig::BUT_RESTRICT_SILENT;
446         else if (stdalgo::string::equalsci(restrictbannedusers, "yes"))
447                 RestrictBannedUsers =  ServerConfig::BUT_RESTRICT_NOTIFY;
448         else
449                 throw CoreException(restrictbannedusers + " is an invalid <options:restrictbannedusers> value, at " + options->getTagLocation());
450 }
451
452 // WARNING: it is not safe to use most of the codebase in this function, as it
453 // will run in the config reader thread
454 void ServerConfig::Read()
455 {
456         /* Load and parse the config file, if there are any errors then explode */
457
458         ParseStack stack(this);
459         try
460         {
461                 valid = stack.ParseFile(ServerInstance->ConfigFileName, 0);
462         }
463         catch (CoreException& err)
464         {
465                 valid = false;
466                 errstr << err.GetReason() << std::endl;
467         }
468 }
469
470 void ServerConfig::Apply(ServerConfig* old, const std::string &useruid)
471 {
472         valid = true;
473         if (old)
474         {
475                 /*
476                  * These values can only be set on boot. Keep their old values. Do it before we send messages so we actually have a servername.
477                  */
478                 this->CaseMapping = old->CaseMapping;
479                 this->ServerName = old->ServerName;
480                 this->sid = old->sid;
481                 this->cmdline = old->cmdline;
482         }
483
484         /* The stuff in here may throw CoreException, be sure we're in a position to catch it. */
485         try
486         {
487                 // Ensure the user has actually edited ther config.
488                 ConfigTagList dietags = ConfTags("die");
489                 if (dietags.first != dietags.second)
490                 {
491                         errstr << "Your configuration has not been edited correctly!" << std::endl;
492                         for (ConfigIter iter = dietags.first; iter != dietags.second; ++iter)
493                         {
494                                 ConfigTag* tag = iter->second;
495                                 const std::string reason = tag->getString("reason", "You left a <die> tag in your config", 1);
496                                 errstr << reason <<  " (at " << tag->getTagLocation() << ")" << std::endl;
497                         }
498                 }
499
500                 Fill();
501
502                 // Handle special items
503                 CrossCheckOperClassType();
504                 CrossCheckConnectBlocks(old);
505         }
506         catch (CoreException &ce)
507         {
508                 errstr << ce.GetReason() << std::endl;
509         }
510
511         // Check errors before dealing with failed binds, since continuing on failed bind is wanted in some circumstances.
512         valid = errstr.str().empty();
513
514         // write once here, to try it out and make sure its ok
515         if (valid)
516                 ServerInstance->WritePID(this->PID, !old);
517
518         ConfigTagList binds = ConfTags("bind");
519         if (binds.first == binds.second)
520                  errstr << "Possible configuration error: you have not defined any <bind> blocks." << std::endl
521                          << "You will need to do this if you want clients to be able to connect!" << std::endl;
522
523         if (old && valid)
524         {
525                 // On first run, ports are bound later on
526                 FailedPortList pl;
527                 ServerInstance->BindPorts(pl);
528                 if (!pl.empty())
529                 {
530                         errstr << "Warning! Some of your listener" << (pl.size() == 1 ? "s" : "") << " failed to bind:" << std::endl;
531                         for (FailedPortList::const_iterator iter = pl.begin(); iter != pl.end(); ++iter)
532                         {
533                                 const FailedPort& fp = *iter;
534                                 errstr << "  " << fp.sa.str() << ": " << strerror(fp.error) << std::endl
535                                         << "  " << "Created from <bind> tag at " << fp.tag->getTagLocation() << std::endl;
536                         }
537                 }
538         }
539
540         User* user = useruid.empty() ? NULL : ServerInstance->FindUUID(useruid);
541
542         if (!valid)
543         {
544                 ServerInstance->Logs->Log("CONFIG", LOG_DEFAULT, "There were errors in your configuration file:");
545                 Classes.clear();
546         }
547
548         while (errstr.good())
549         {
550                 std::string line;
551                 getline(errstr, line, '\n');
552                 if (line.empty())
553                         continue;
554                 // On startup, print out to console (still attached at this point)
555                 if (!old)
556                         std::cout << line << std::endl;
557                 // If a user is rehashing, tell them directly
558                 if (user)
559                         user->WriteRemoteNotice(InspIRCd::Format("*** %s", line.c_str()));
560                 // Also tell opers
561                 ServerInstance->SNO->WriteGlobalSno('a', line);
562         }
563
564         errstr.clear();
565         errstr.str(std::string());
566
567         /* No old configuration -> initial boot, nothing more to do here */
568         if (!old)
569         {
570                 if (!valid)
571                 {
572                         ServerInstance->Exit(EXIT_STATUS_CONFIG);
573                 }
574
575                 return;
576         }
577
578
579         // If there were errors processing configuration, don't touch modules.
580         if (!valid)
581                 return;
582
583         ApplyModules(user);
584
585         if (user)
586                 user->WriteRemoteNotice("*** Successfully rehashed server.");
587         ServerInstance->SNO->WriteGlobalSno('a', "*** Successfully rehashed server.");
588 }
589
590 void ServerConfig::ApplyModules(User* user)
591 {
592         std::vector<std::string> added_modules;
593         ModuleManager::ModuleMap removed_modules = ServerInstance->Modules->GetModules();
594
595         ConfigTagList tags = ConfTags("module");
596         for(ConfigIter i = tags.first; i != tags.second; ++i)
597         {
598                 ConfigTag* tag = i->second;
599                 std::string name;
600                 if (tag->readString("name", name))
601                 {
602                         name = ModuleManager::ExpandModName(name);
603                         // if this module is already loaded, the erase will succeed, so we need do nothing
604                         // otherwise, we need to add the module (which will be done later)
605                         if (removed_modules.erase(name) == 0)
606                                 added_modules.push_back(name);
607                 }
608         }
609
610         for (ModuleManager::ModuleMap::iterator i = removed_modules.begin(); i != removed_modules.end(); ++i)
611         {
612                 const std::string& modname = i->first;
613                 // Don't remove core_*.so, just remove m_*.so
614                 if (InspIRCd::Match(modname, "core_*.so", ascii_case_insensitive_map))
615                         continue;
616                 if (ServerInstance->Modules->Unload(i->second))
617                 {
618                         ServerInstance->SNO->WriteGlobalSno('a', "*** REHASH UNLOADED MODULE: %s", modname.c_str());
619
620                         if (user)
621                                 user->WriteNumeric(RPL_UNLOADEDMODULE, modname, InspIRCd::Format("Module %s successfully unloaded.", modname.c_str()));
622                         else
623                                 ServerInstance->SNO->WriteGlobalSno('a', "Module %s successfully unloaded.", modname.c_str());
624                 }
625                 else
626                 {
627                         if (user)
628                                 user->WriteNumeric(ERR_CANTUNLOADMODULE, modname, InspIRCd::Format("Failed to unload module %s: %s", modname.c_str(), ServerInstance->Modules->LastError().c_str()));
629                         else
630                                 ServerInstance->SNO->WriteGlobalSno('a', "Failed to unload module %s: %s", modname.c_str(), ServerInstance->Modules->LastError().c_str());
631                 }
632         }
633
634         for (std::vector<std::string>::iterator adding = added_modules.begin(); adding != added_modules.end(); adding++)
635         {
636                 // Skip modules which are already loaded.
637                 if (ServerInstance->Modules->Find(*adding))
638                         continue;
639
640                 if (ServerInstance->Modules->Load(*adding))
641                 {
642                         ServerInstance->SNO->WriteGlobalSno('a', "*** REHASH LOADED MODULE: %s",adding->c_str());
643                         if (user)
644                                 user->WriteNumeric(RPL_LOADEDMODULE, *adding, InspIRCd::Format("Module %s successfully loaded.", adding->c_str()));
645                         else
646                                 ServerInstance->SNO->WriteGlobalSno('a', "Module %s successfully loaded.", adding->c_str());
647                 }
648                 else
649                 {
650                         if (user)
651                                 user->WriteNumeric(ERR_CANTLOADMODULE, *adding, InspIRCd::Format("Failed to load module %s: %s", adding->c_str(), ServerInstance->Modules->LastError().c_str()));
652                         else
653                                 ServerInstance->SNO->WriteGlobalSno('a', "Failed to load module %s: %s", adding->c_str(), ServerInstance->Modules->LastError().c_str());
654                 }
655         }
656 }
657
658 ConfigTag* ServerConfig::ConfValue(const std::string &tag)
659 {
660         ConfigTagList found = config_data.equal_range(tag);
661         if (found.first == found.second)
662                 return EmptyTag;
663         ConfigTag* rv = found.first->second;
664         found.first++;
665         if (found.first != found.second)
666                 ServerInstance->Logs->Log("CONFIG", LOG_DEFAULT, "Multiple <" + tag + "> tags found; only first will be used "
667                         "(first at " + rv->getTagLocation() + "; second at " + found.first->second->getTagLocation() + ")");
668         return rv;
669 }
670
671 ConfigTagList ServerConfig::ConfTags(const std::string& tag)
672 {
673         return config_data.equal_range(tag);
674 }
675
676 std::string ServerConfig::Escape(const std::string& str, bool xml)
677 {
678         std::string escaped;
679         for (std::string::const_iterator it = str.begin(); it != str.end(); ++it)
680         {
681                 switch (*it)
682                 {
683                         case '"':
684                                 escaped += xml ? "&quot;" : "\"";
685                                 break;
686                         case '&':
687                                 escaped += xml ? "&amp;" : "&";
688                                 break;
689                         case '\\':
690                                 escaped += xml ? "\\" : "\\\\";
691                                 break;
692                         default:
693                                 escaped += *it;
694                                 break;
695                 }
696         }
697         return escaped;
698 }
699
700 void ConfigReaderThread::Run()
701 {
702         Config->Read();
703         done = true;
704 }
705
706 void ConfigReaderThread::Finish()
707 {
708         ServerConfig* old = ServerInstance->Config;
709         ServerInstance->Logs->Log("CONFIG", LOG_DEBUG, "Switching to new configuration...");
710         ServerInstance->Config = this->Config;
711         Config->Apply(old, TheUserUID);
712
713         if (Config->valid)
714         {
715                 /*
716                  * Apply the changed configuration from the rehash.
717                  *
718                  * XXX: The order of these is IMPORTANT, do not reorder them without testing
719                  * thoroughly!!!
720                  */
721                 ServerInstance->Users.RehashCloneCounts();
722                 ServerInstance->XLines->CheckELines();
723                 ServerInstance->XLines->ApplyLines();
724                 User* user = ServerInstance->FindUUID(TheUserUID);
725
726                 ConfigStatus status(user);
727                 const ModuleManager::ModuleMap& mods = ServerInstance->Modules->GetModules();
728                 for (ModuleManager::ModuleMap::const_iterator i = mods.begin(); i != mods.end(); ++i)
729                 {
730                         try
731                         {
732                                 ServerInstance->Logs->Log("MODULE", LOG_DEBUG, "Rehashing " + i->first);
733                                 i->second->ReadConfig(status);
734                         }
735                         catch (CoreException& modex)
736                         {
737                                 ServerInstance->Logs->Log("MODULE", LOG_DEFAULT, "Exception caught: " + modex.GetReason());
738                                 if (user)
739                                         user->WriteNotice(i->first + ": " + modex.GetReason());
740                         }
741                 }
742
743                 // The description of this server may have changed - update it for WHOIS etc.
744                 ServerInstance->FakeClient->server->description = Config->ServerDesc;
745
746                 ServerInstance->ISupport.Build();
747
748                 ServerInstance->Logs->CloseLogs();
749                 ServerInstance->Logs->OpenFileLogs();
750
751                 if (Config->RawLog && !old->RawLog)
752                         ServerInstance->Users->ServerNoticeAll("*** Raw I/O logging is enabled on this server. All messages, passwords, and commands are being recorded.");
753
754                 Config = old;
755         }
756         else
757         {
758                 // whoops, abort!
759                 ServerInstance->Config = old;
760         }
761 }