]> git.netwichtig.de Git - user/henk/code/inspircd.git/blob - src/configreader.cpp
Send %#chan messages to all members ranked at least halfop, not just those that have...
[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: tools/gdbargs $(BASE)/.gdbargs */
27
28 #include "inspircd.h"
29 #include <fstream>
30 #include "xline.h"
31 #include "exitcodes.h"
32 #include "commands/cmd_whowas.h"
33 #include "configparser.h"
34
35 ServerConfig::ServerConfig()
36 {
37         WhoWasGroupSize = WhoWasMaxGroups = WhoWasMaxKeep = 0;
38         NoUserDns = OperSpyWhois = HideBans = HideSplits = UndernetMsgPrefix = false;
39         CycleHosts = InvBypassModes = true;
40         dns_timeout = DieDelay = 5;
41         MaxTargets = 20;
42         NetBufferSize = 10240;
43         SoftLimit = ServerInstance->SE->GetMaxFds();
44         MaxConn = SOMAXCONN;
45         MaxWhoResults = 0;
46         MaxChans = 20;
47         OperMaxChans = 30;
48         c_ipv4_range = 32;
49         c_ipv6_range = 128;
50 }
51
52 void ServerConfig::Update005()
53 {
54         std::stringstream out(data005);
55         std::string token;
56         std::string line5;
57         int token_counter = 0;
58         isupport.clear();
59         while (out >> token)
60         {
61                 line5 = line5 + token + " ";
62                 token_counter++;
63                 if (token_counter >= 13)
64                 {
65                         char buf[MAXBUF];
66                         snprintf(buf, MAXBUF, "%s:are supported by this server", line5.c_str());
67                         isupport.push_back(buf);
68                         line5.clear();
69                         token_counter = 0;
70                 }
71         }
72         if (!line5.empty())
73         {
74                 char buf[MAXBUF];
75                 snprintf(buf, MAXBUF, "%s:are supported by this server", line5.c_str());
76                 isupport.push_back(buf);
77         }
78 }
79
80 void ServerConfig::Send005(User* user)
81 {
82         for (std::vector<std::string>::iterator line = ServerInstance->Config->isupport.begin(); line != ServerInstance->Config->isupport.end(); line++)
83                 user->WriteNumeric(RPL_ISUPPORT, "%s %s", user->nick.c_str(), line->c_str());
84 }
85
86 static void ReqRead(ServerConfig* src, const std::string& tag, const std::string& key, std::string& dest)
87 {
88         ConfigTag* t = src->ConfValue(tag);
89         if (!t || !t->readString(key, dest))
90                 throw CoreException("You must specify a value for <" + tag + ":" + key + ">");
91 }
92
93 template<typename T, typename V>
94 static void range(T& value, V min, V max, V def, const char* msg)
95 {
96         if (value >= (T)min && value <= (T)max)
97                 return;
98         ServerInstance->Logs->Log("CONFIG", DEFAULT,
99                 "WARNING: %s value of %ld is not between %ld and %ld; set to %ld.",
100                 msg, (long)value, (long)min, (long)max, (long)def);
101         value = def;
102 }
103
104
105 /* 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,
106  * even in strerror(errno). They just return 'yes' or 'no' to an address without such detail as to whats WRONG with the address.
107  * Because ircd users arent as technical as they used to be (;)) we are going to give more of a useful error message.
108  */
109 static void ValidIP(const std::string& ip, const std::string& key)
110 {
111         const char* p = ip.c_str();
112         int num_dots = 0;
113         int num_seps = 0;
114         int not_numbers = false;
115         int not_hex = false;
116
117         if (*p)
118         {
119                 if (*p == '.')
120                         throw CoreException("The value of "+key+" is not an IP address");
121
122                 for (const char* ptr = p; *ptr; ++ptr)
123                 {
124                         if (*ptr != ':' && *ptr != '.')
125                         {
126                                 if (*ptr < '0' || *ptr > '9')
127                                         not_numbers = true;
128                                 if ((*ptr < '0' || *ptr > '9') && (toupper(*ptr) < 'A' || toupper(*ptr) > 'F'))
129                                         not_hex = true;
130                         }
131                         switch (*ptr)
132                         {
133                                 case ' ':
134                                         throw CoreException("The value of "+key+" is not an IP address");
135                                 case '.':
136                                         num_dots++;
137                                 break;
138                                 case ':':
139                                         num_seps++;
140                                 break;
141                         }
142                 }
143
144                 if (num_dots > 3)
145                         throw CoreException("The value of "+key+" is an IPv4 address with too many fields!");
146
147                 if (num_seps > 8)
148                         throw CoreException("The value of "+key+" is an IPv6 address with too many fields!");
149
150                 if (num_seps == 0 && num_dots < 3)
151                         throw CoreException("The value of "+key+" looks to be a malformed IPv4 address");
152
153                 if (num_seps == 0 && num_dots == 3 && not_numbers)
154                         throw CoreException("The value of "+key+" contains non-numeric characters in an IPv4 address");
155
156                 if (num_seps != 0 && not_hex)
157                         throw CoreException("The value of "+key+" contains non-hexdecimal characters in an IPv6 address");
158
159                 if (num_seps != 0 && num_dots != 3 && num_dots != 0)
160                         throw CoreException("The value of "+key+" is a malformed IPv6 4in6 address");
161         }
162 }
163
164 static void ValidHost(const std::string& p, const std::string& msg)
165 {
166         int num_dots = 0;
167         if (p.empty() || p[0] == '.')
168                 throw CoreException("The value of "+msg+" is not a valid hostname");
169         for (unsigned int i=0;i < p.length();i++)
170         {
171                 switch (p[i])
172                 {
173                         case ' ':
174                                 throw CoreException("The value of "+msg+" is not a valid hostname");
175                         case '.':
176                                 num_dots++;
177                         break;
178                 }
179         }
180         if (num_dots == 0)
181                 throw CoreException("The value of "+msg+" is not a valid hostname");
182 }
183
184 // Specialized validators
185
186 bool ServerConfig::ApplyDisabledCommands(const std::string& data)
187 {
188         std::stringstream dcmds(data);
189         std::string thiscmd;
190
191         /* Enable everything first */
192         for (Commandtable::iterator x = ServerInstance->Parser->cmdlist.begin(); x != ServerInstance->Parser->cmdlist.end(); x++)
193                 x->second->Disable(false);
194
195         /* Now disable all the ones which the user wants disabled */
196         while (dcmds >> thiscmd)
197         {
198                 Commandtable::iterator cm = ServerInstance->Parser->cmdlist.find(thiscmd);
199                 if (cm != ServerInstance->Parser->cmdlist.end())
200                 {
201                         cm->second->Disable(true);
202                 }
203         }
204         return true;
205 }
206
207 #ifdef WINDOWS
208 // Note: the windows validator is in win32wrapper.cpp
209 void FindDNS(std::string& server);
210 #else
211 static void FindDNS(std::string& server)
212 {
213         if (!server.empty())
214                 return;
215
216         // attempt to look up their nameserver from /etc/resolv.conf
217         ServerInstance->Logs->Log("CONFIG",DEFAULT,"WARNING: <dns:server> not defined, attempting to find working server in /etc/resolv.conf...");
218
219         std::ifstream resolv("/etc/resolv.conf");
220
221         while (resolv >> server)
222         {
223                 if (server == "nameserver")
224                 {
225                         resolv >> server;
226                         ServerInstance->Logs->Log("CONFIG",DEFAULT,"<dns:server> set to '%s' as first resolver in /etc/resolv.conf.",server.c_str());
227                         return;
228                 }
229         }
230
231         ServerInstance->Logs->Log("CONFIG",DEFAULT,"/etc/resolv.conf contains no viable nameserver entries! Defaulting to nameserver '127.0.0.1'!");
232         server = "127.0.0.1";
233 }
234 #endif
235
236 static void ReadXLine(ServerConfig* conf, const std::string& tag, const std::string& key, XLineFactory* make)
237 {
238         ConfigTagList tags = conf->ConfTags(tag);
239         for(ConfigIter i = tags.first; i != tags.second; ++i)
240         {
241                 ConfigTag* ctag = i->second;
242                 std::string mask;
243                 if (!ctag->readString(key, mask))
244                         throw CoreException("<"+tag+":"+key+"> missing at " + ctag->getTagLocation());
245                 std::string reason = ctag->getString("reason", "<Config>");
246                 XLine* xl = make->Generate(ServerInstance->Time(), 0, "<Config>", reason, mask);
247                 if (!ServerInstance->XLines->AddLine(xl, NULL))
248                         delete xl;
249         }
250 }
251
252 typedef std::map<std::string, ConfigTag*> LocalIndex;
253 void ServerConfig::CrossCheckOperClassType()
254 {
255         LocalIndex operclass;
256         ConfigTagList tags = ConfTags("class");
257         for(ConfigIter i = tags.first; i != tags.second; ++i)
258         {
259                 ConfigTag* tag = i->second;
260                 std::string name = tag->getString("name");
261                 if (name.empty())
262                         throw CoreException("<class:name> missing from tag at " + tag->getTagLocation());
263                 operclass[name] = tag;
264         }
265         tags = ConfTags("type");
266         for(ConfigIter i = tags.first; i != tags.second; ++i)
267         {
268                 ConfigTag* tag = i->second;
269                 std::string name = tag->getString("name");
270                 if (name.empty())
271                         throw CoreException("<type:name> is missing from tag at " + tag->getTagLocation());
272
273                 if (!ServerInstance->IsNick(name.c_str(), Limits.NickMax))
274                         throw CoreException("<type:name> is invalid (value '" + name + "')");
275
276                 OperInfo* ifo = new OperInfo;
277                 oper_blocks[" " + name] = ifo;
278                 ifo->type_block = tag;
279
280                 std::string classname;
281                 irc::spacesepstream str(tag->getString("classes"));
282                 while (str.GetToken(classname))
283                 {
284                         LocalIndex::iterator cls = operclass.find(classname);
285                         if (cls == operclass.end())
286                                 throw CoreException("Oper type " + name + " has missing class " + classname);
287                         ifo->class_blocks.push_back(cls->second);
288                 }
289         }
290
291         tags = ConfTags("oper");
292         for(ConfigIter i = tags.first; i != tags.second; ++i)
293         {
294                 ConfigTag* tag = i->second;
295
296                 std::string name = tag->getString("name");
297                 if (name.empty())
298                         throw CoreException("<oper:name> missing from tag at " + tag->getTagLocation());
299
300                 std::string type = tag->getString("type");
301                 OperIndex::iterator tblk = oper_blocks.find(" " + type);
302                 if (tblk == oper_blocks.end())
303                         throw CoreException("Oper block " + name + " has missing type " + type);
304                 if (oper_blocks.find(name) != oper_blocks.end())
305                         throw CoreException("Duplicate oper block with name " + name);
306
307                 OperInfo* ifo = new OperInfo;
308                 ifo->name = type;
309                 ifo->oper_block = tag;
310                 ifo->type_block = tblk->second->type_block;
311                 ifo->class_blocks.assign(tblk->second->class_blocks.begin(), tblk->second->class_blocks.end());
312                 oper_blocks[name] = ifo;
313         }
314 }
315
316 void ServerConfig::CrossCheckConnectBlocks(ServerConfig* current)
317 {
318         typedef std::map<std::string, ConnectClass*> ClassMap;
319         ClassMap oldBlocksByMask;
320         if (current)
321         {
322                 for(ClassVector::iterator i = current->Classes.begin(); i != current->Classes.end(); ++i)
323                 {
324                         ConnectClass* c = *i;
325                         std::string typeMask = (c->type == CC_ALLOW) ? "a" : "d";
326                         typeMask += c->host;
327                         oldBlocksByMask[typeMask] = c;
328                 }
329         }
330
331         ClassMap newBlocksByMask;
332         Classes.resize(config_data.count("connect"));
333         std::map<std::string, int> names;
334
335         bool try_again = true;
336         for(int tries=0; try_again; tries++)
337         {
338                 try_again = false;
339                 ConfigTagList tags = ConfTags("connect");
340                 int i=0;
341                 for(ConfigIter it = tags.first; it != tags.second; ++it, ++i)
342                 {
343                         ConfigTag* tag = it->second;
344                         if (Classes[i])
345                                 continue;
346
347                         ConnectClass* parent = NULL;
348                         std::string parentName = tag->getString("parent");
349                         if (!parentName.empty())
350                         {
351                                 std::map<std::string,int>::iterator parentIter = names.find(parentName);
352                                 if (parentIter == names.end())
353                                 {
354                                         try_again = true;
355                                         // couldn't find parent this time. If it's the last time, we'll never find it.
356                                         if (tries == 50)
357                                                 throw CoreException("Could not find parent connect class \"" + parentName + "\" for connect block " + ConvToStr(i));
358                                         continue;
359                                 }
360                                 parent = Classes[parentIter->second];
361                         }
362
363                         std::string name = tag->getString("name");
364                         if (!name.empty())
365                         {
366                                 if (names.find(name) != names.end())
367                                         throw CoreException("Two connect classes with name \"" + name + "\" defined!");
368                                 names[name] = i;
369                         }
370
371                         std::string mask, typeMask;
372                         char type;
373
374                         if (tag->readString("allow", mask, false))
375                         {
376                                 type = CC_ALLOW;
377                                 typeMask = 'a' + mask;
378                         }
379                         else if (tag->readString("deny", mask, false))
380                         {
381                                 type = CC_DENY;
382                                 typeMask = 'd' + mask;
383                         }
384                         else
385                         {
386                                 throw CoreException("Connect class must have an allow or deny mask at " + tag->getTagLocation());
387                         }
388                         ClassMap::iterator dupMask = newBlocksByMask.find(typeMask);
389                         if (dupMask != newBlocksByMask.end())
390                                 throw CoreException("Two connect classes cannot have the same mask (" + mask + ")");
391
392                         ConnectClass* me = parent ? 
393                                 new ConnectClass(tag, type, mask, *parent) :
394                                 new ConnectClass(tag, type, mask);
395
396                         if (!name.empty())
397                                 me->name = name;
398
399                         tag->readString("password", me->pass);
400                         tag->readString("hash", me->hash);
401                         me->registration_timeout = tag->getInt("timeout", me->registration_timeout);
402                         me->pingtime = tag->getInt("pingfreq", me->pingtime);
403                         std::string sendq;
404                         if (tag->readString("sendq", sendq))
405                         {
406                                 // attempt to guess a good hard/soft sendq from a single value
407                                 long value = atol(sendq.c_str());
408                                 if (value > 16384)
409                                         me->softsendqmax = value / 16;
410                                 else
411                                         me->softsendqmax = value;
412                                 me->hardsendqmax = value * 8;
413                         }
414                         me->softsendqmax = tag->getInt("softsendq", me->softsendqmax);
415                         me->hardsendqmax = tag->getInt("hardsendq", me->hardsendqmax);
416                         me->recvqmax = tag->getInt("recvq", me->recvqmax);
417                         me->penaltythreshold = tag->getInt("threshold", me->penaltythreshold);
418                         me->maxlocal = tag->getInt("localmax", me->maxlocal);
419                         me->maxglobal = tag->getInt("globalmax", me->maxglobal);
420                         me->port = tag->getInt("port", me->port);
421                         me->maxchans = tag->getInt("maxchans", me->maxchans);
422                         me->limit = tag->getInt("limit", me->limit);
423
424                         ClassMap::iterator oldMask = oldBlocksByMask.find(typeMask);
425                         if (oldMask != oldBlocksByMask.end())
426                         {
427                                 ConnectClass* old = oldMask->second;
428                                 oldBlocksByMask.erase(oldMask);
429                                 old->Update(me);
430                                 delete me;
431                                 me = old;
432                         }
433                         newBlocksByMask[typeMask] = me;
434                         Classes[i] = me;
435                 }
436         }
437 }
438
439 /** Represents a deprecated configuration tag.
440  */
441 struct Deprecated
442 {
443         /** Tag name
444          */
445         const char* tag;
446         /** Tag value
447          */
448         const char* value;
449         /** Reason for deprecation
450          */
451         const char* reason;
452 };
453
454 static const Deprecated ChangedConfig[] = {
455         {"options", "hidelinks",                "has been moved to <security:hidelinks> as of 1.2a3"},
456         {"options", "hidewhois",                "has been moved to <security:hidewhois> as of 1.2a3"},
457         {"options", "userstats",                "has been moved to <security:userstats> as of 1.2a3"},
458         {"options", "customversion",    "has been moved to <security:customversion> as of 1.2a3"},
459         {"options", "hidesplits",               "has been moved to <security:hidesplits> as of 1.2a3"},
460         {"options", "hidebans",         "has been moved to <security:hidebans> as of 1.2a3"},
461         {"options", "hidekills",                "has been moved to <security:hidekills> as of 1.2a3"},
462         {"options", "operspywhois",             "has been moved to <security:operspywhois> as of 1.2a3"},
463         {"options", "announceinvites",  "has been moved to <security:announceinvites> as of 1.2a3"},
464         {"options", "hidemodes",                "has been moved to <security:hidemodes> as of 1.2a3"},
465         {"options", "maxtargets",               "has been moved to <security:maxtargets> as of 1.2a3"},
466         {"options",     "nouserdns",            "has been moved to <performance:nouserdns> as of 1.2a3"},
467         {"options",     "maxwho",               "has been moved to <performance:maxwho> as of 1.2a3"},
468         {"options",     "softlimit",            "has been moved to <performance:softlimit> as of 1.2a3"},
469         {"options", "somaxconn",                "has been moved to <performance:somaxconn> as of 1.2a3"},
470         {"options", "netbuffersize",    "has been moved to <performance:netbuffersize> as of 1.2a3"},
471         {"options", "maxwho",           "has been moved to <performance:maxwho> as of 1.2a3"},
472         {"options",     "loglevel",             "1.2 does not use the loglevel value. Please define <log> tags instead."},
473         {"die",     "value",            "has always been deprecated"},
474 };
475
476 void ServerConfig::Fill()
477 {
478         ReqRead(this, "server", "name", ServerName);
479         ReqRead(this, "power", "diepass", diepass);
480         ReqRead(this, "power", "restartpass", restartpass);
481
482         ConfigTag* options = ConfValue("options");
483         ConfigTag* security = ConfValue("security");
484         powerhash = ConfValue("power")->getString("hash");
485         DieDelay = ConfValue("power")->getInt("pause");
486         PrefixQuit = options->getString("prefixquit");
487         SuffixQuit = options->getString("suffixquit");
488         FixedQuit = options->getString("fixedquit");
489         PrefixPart = options->getString("prefixpart");
490         SuffixPart = options->getString("suffixpart");
491         FixedPart = options->getString("fixedpart");
492         SoftLimit = ConfValue("performance")->getInt("softlimit", ServerInstance->SE->GetMaxFds());
493         MaxConn = ConfValue("performance")->getInt("somaxconn", SOMAXCONN);
494         MoronBanner = options->getString("moronbanner", "You're banned!");
495         ServerDesc = ConfValue("server")->getString("description", "Configure Me");
496         Network = ConfValue("server")->getString("network", "Network");
497         sid = ConfValue("server")->getString("id", "");
498         AdminName = ConfValue("admin")->getString("name", "");
499         AdminEmail = ConfValue("admin")->getString("email", "null@example.com");
500         AdminNick = ConfValue("admin")->getString("nick", "admin");
501         ModPath = ConfValue("path")->getString("moduledir", MOD_PATH);
502         NetBufferSize = ConfValue("performance")->getInt("netbuffersize", 10240);
503         MaxWhoResults = ConfValue("performance")->getInt("maxwho", 1024);
504         dns_timeout = ConfValue("dns")->getInt("timeout", 5);
505         DisabledCommands = ConfValue("disabled")->getString("commands", "");
506         DisabledDontExist = ConfValue("disabled")->getBool("fakenonexistant");
507         UserStats = security->getString("userstats");
508         CustomVersion = security->getString("customversion");
509         HideSplits = security->getBool("hidesplits");
510         HideBans = security->getBool("hidebans");
511         HideWhoisServer = security->getString("hidewhois");
512         HideKillsServer = security->getString("hidekills");
513         OperSpyWhois = security->getBool("operspywhois");
514         RestrictBannedUsers = security->getBool("restrictbannedusers", true);
515         GenericOper = security->getBool("genericoper");
516         NoUserDns = ConfValue("performance")->getBool("nouserdns");
517         SyntaxHints = options->getBool("syntaxhints");
518         CycleHosts = options->getBool("cyclehosts");
519         UndernetMsgPrefix = options->getBool("ircumsgprefix");
520         FullHostInTopic = options->getBool("hostintopic");
521         MaxTargets = security->getInt("maxtargets", 20);
522         DefaultModes = options->getString("defaultmodes", "nt");
523         PID = ConfValue("pid")->getString("file");
524         WhoWasGroupSize = ConfValue("whowas")->getInt("groupsize");
525         WhoWasMaxGroups = ConfValue("whowas")->getInt("maxgroups");
526         WhoWasMaxKeep = ServerInstance->Duration(ConfValue("whowas")->getString("maxkeep"));
527         DieValue = ConfValue("die")->getString("value");
528         MaxChans = ConfValue("channels")->getInt("users");
529         OperMaxChans = ConfValue("channels")->getInt("opers");
530         c_ipv4_range = ConfValue("cidr")->getInt("ipv4clone");
531         c_ipv6_range = ConfValue("cidr")->getInt("ipv6clone");
532         Limits.NickMax = ConfValue("limits")->getInt("maxnick", 32);
533         Limits.ChanMax = ConfValue("limits")->getInt("maxchan", 64);
534         Limits.MaxModes = ConfValue("limits")->getInt("maxmodes", 20);
535         Limits.IdentMax = ConfValue("limits")->getInt("maxident", 11);
536         Limits.MaxQuit = ConfValue("limits")->getInt("maxquit", 255);
537         Limits.MaxTopic = ConfValue("limits")->getInt("maxtopic", 307);
538         Limits.MaxKick = ConfValue("limits")->getInt("maxkick", 255);
539         Limits.MaxGecos = ConfValue("limits")->getInt("maxgecos", 128);
540         Limits.MaxAway = ConfValue("limits")->getInt("maxaway", 200);
541         InvBypassModes = options->getBool("invitebypassmodes", true);
542
543         range(SoftLimit, 10, ServerInstance->SE->GetMaxFds(), ServerInstance->SE->GetMaxFds(), "<performance:softlimit>");
544         range(MaxConn, 0, SOMAXCONN, SOMAXCONN, "<performance:somaxconn>");
545         range(MaxTargets, 1, 31, 20, "<security:maxtargets>");
546         range(NetBufferSize, 1024, 65534, 10240, "<performance:netbuffersize>");
547         range(MaxWhoResults, 1, 65535, 1024, "<performace:maxwho>");
548         range(WhoWasGroupSize, 0, 10000, 10, "<whowas:groupsize>");
549         range(WhoWasMaxGroups, 0, 1000000, 10240, "<whowas:maxgroups>");
550         range(WhoWasMaxKeep, 3600, INT_MAX, 3600, "<whowas:maxkeep>");
551
552         ValidIP(DNSServer, "<dns:server>");
553         ValidHost(ServerName, "<server:name>");
554         if (!sid.empty() && !ServerInstance->IsSID(sid))
555                 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.");
556
557         ConfigTagList tags = ConfTags("uline");
558         for(ConfigIter i = tags.first; i != tags.second; ++i)
559         {
560                 ConfigTag* tag = i->second;
561                 std::string server;
562                 if (!tag->readString("server", server))
563                         throw CoreException("<uline> tag missing server at " + tag->getTagLocation());
564                 ulines[assign(server)] = tag->getBool("silent");
565         }
566
567         tags = ConfTags("banlist");
568         for(ConfigIter i = tags.first; i != tags.second; ++i)
569         {
570                 ConfigTag* tag = i->second;
571                 std::string chan;
572                 if (!tag->readString("chan", chan))
573                         throw CoreException("<banlist> tag missing chan at " + tag->getTagLocation());
574                 maxbans[chan] = tag->getInt("limit");
575         }
576
577         ReadXLine(this, "badip", "ipmask", ServerInstance->XLines->GetFactory("Z"));
578         ReadXLine(this, "badnick", "nick", ServerInstance->XLines->GetFactory("Q"));
579         ReadXLine(this, "badhost", "host", ServerInstance->XLines->GetFactory("K"));
580         ReadXLine(this, "exception", "host", ServerInstance->XLines->GetFactory("E"));
581
582         memset(DisabledUModes, 0, sizeof(DisabledUModes));
583         for (const unsigned char* p = (const unsigned char*)ConfValue("disabled")->getString("usermodes").c_str(); *p; ++p)
584         {
585                 if (*p < 'A' || *p > ('A' + 64)) throw CoreException(std::string("Invalid usermode ")+(char)*p+" was found.");
586                 DisabledUModes[*p - 'A'] = 1;
587         }
588
589         memset(DisabledCModes, 0, sizeof(DisabledCModes));
590         for (const unsigned char* p = (const unsigned char*)ConfValue("disabled")->getString("chanmodes").c_str(); *p; ++p)
591         {
592                 if (*p < 'A' || *p > ('A' + 64)) throw CoreException(std::string("Invalid chanmode ")+(char)*p+" was found.");
593                 DisabledCModes[*p - 'A'] = 1;
594         }
595
596         memset(HideModeLists, 0, sizeof(HideModeLists));
597         for (const unsigned char* p = (const unsigned char*)ConfValue("security")->getString("hidemodes").c_str(); *p; ++p)
598                 HideModeLists[*p] = true;
599
600         std::string v = security->getString("announceinvites");
601
602         if (v == "ops")
603                 AnnounceInvites = ServerConfig::INVITE_ANNOUNCE_OPS;
604         else if (v == "all")
605                 AnnounceInvites = ServerConfig::INVITE_ANNOUNCE_ALL;
606         else if (v == "dynamic")
607                 AnnounceInvites = ServerConfig::INVITE_ANNOUNCE_DYNAMIC;
608         else
609                 AnnounceInvites = ServerConfig::INVITE_ANNOUNCE_NONE;
610
611         Limits.Finalise();
612 }
613
614 /* These tags MUST occur and must ONLY occur once in the config file */
615 static const char* const Once[] = { "server", "admin", "files", "power", "options" };
616
617 // WARNING: it is not safe to use most of the codebase in this function, as it
618 // will run in the config reader thread
619 void ServerConfig::Read()
620 {
621         /* Load and parse the config file, if there are any errors then explode */
622
623         ParseStack stack(this);
624         try
625         {
626                 valid = stack.ParseFile(ServerInstance->ConfigFileName, 0);
627         }
628         catch (CoreException& err)
629         {
630                 valid = false;
631                 errstr << err.GetReason();
632         }
633         if (valid)
634         {
635                 ReadFile(MOTD, ConfValue("files")->getString("motd"));
636                 ReadFile(RULES, ConfValue("files")->getString("rules"));
637                 DNSServer = ConfValue("dns")->getString("server");
638                 FindDNS(DNSServer);
639         }
640 }
641
642 void ServerConfig::Apply(ServerConfig* old, const std::string &useruid)
643 {
644         valid = true;
645
646         /* The stuff in here may throw CoreException, be sure we're in a position to catch it. */
647         try
648         {
649                 /* Check we dont have more than one of singular tags, or any of them missing
650                  */
651                 for (int Index = 0; Index * sizeof(*Once) < sizeof(Once); Index++)
652                 {
653                         std::string tag = Once[Index];
654                         ConfigTagList tags = ConfTags(tag);
655                         if (tags.first == tags.second)
656                                 throw CoreException("You have not defined a <"+tag+"> tag, this is required.");
657                         tags.first++;
658                         if (tags.first != tags.second)
659                         {
660                                 errstr << "You have more than one <" << tag << "> tag.\n"
661                                         << "First occurrence at " << ConfValue(tag)->getTagLocation()
662                                         << "; second occurrence at " << tags.first->second->getTagLocation() << std::endl;
663                         }
664                 }
665
666                 for (int Index = 0; Index * sizeof(Deprecated) < sizeof(ChangedConfig); Index++)
667                 {
668                         std::string dummy;
669                         if (ConfValue(ChangedConfig[Index].tag)->readString(ChangedConfig[Index].value, dummy, true))
670                                 errstr << "Your configuration contains a deprecated value: <"
671                                         << ChangedConfig[Index].tag << ":" << ChangedConfig[Index].value << "> - " << ChangedConfig[Index].reason
672                                         << " (at " << ConfValue(ChangedConfig[Index].tag)->getTagLocation() << ")\n";
673                 }
674
675                 Fill();
676
677                 // Handle special items
678                 CrossCheckOperClassType();
679                 CrossCheckConnectBlocks(old);
680         }
681         catch (CoreException &ce)
682         {
683                 errstr << ce.GetReason();
684         }
685
686         // write once here, to try it out and make sure its ok
687         ServerInstance->WritePID(this->PID);
688
689         /*
690          * These values can only be set on boot. Keep their old values. Do it before we send messages so we actually have a servername.
691          */
692         if (old)
693         {
694                 this->ServerName = old->ServerName;
695                 this->sid = old->sid;
696                 this->cmdline = old->cmdline;
697
698                 // Same for ports... they're bound later on first run.
699                 FailedPortList pl;
700                 ServerInstance->BindPorts(pl);
701                 if (pl.size())
702                 {
703                         errstr << "Not all your client ports could be bound.\nThe following port(s) failed to bind:\n";
704
705                         int j = 1;
706                         for (FailedPortList::iterator i = pl.begin(); i != pl.end(); i++, j++)
707                         {
708                                 char buf[MAXBUF];
709                                 snprintf(buf, MAXBUF, "%d.   Address: %s   Reason: %s\n", j, i->first.empty() ? "<all>" : i->first.c_str(), i->second.c_str());
710                                 errstr << buf;
711                         }
712                 }
713         }
714
715         User* user = useruid.empty() ? NULL : ServerInstance->FindNick(useruid);
716
717         valid = errstr.str().empty();
718         if (!valid)
719                 ServerInstance->Logs->Log("CONFIG",DEFAULT, "There were errors in your configuration file:");
720
721         while (errstr.good())
722         {
723                 std::string line;
724                 getline(errstr, line, '\n');
725                 if (!line.empty())
726                 {
727                         if (user)
728                                 user->WriteServ("NOTICE %s :*** %s", user->nick.c_str(), line.c_str());
729                         else
730                                 ServerInstance->SNO->WriteGlobalSno('a', line);
731                 }
732
733                 if (!old)
734                 {
735                         // Starting up, so print it out so it's seen. XXX this is a bit of a hack.
736                         printf("%s\n", line.c_str());
737                 }
738         }
739
740         errstr.clear();
741         errstr.str(std::string());
742
743         /* No old configuration -> initial boot, nothing more to do here */
744         if (!old)
745         {
746                 if (!valid)
747                 {
748                         ServerInstance->Exit(EXIT_STATUS_CONFIG);
749                 }
750
751                 return;
752         }
753
754         // If there were errors processing configuration, don't touch modules.
755         if (!valid)
756                 return;
757
758         ApplyModules(user);
759
760         if (user)
761                 user->WriteServ("NOTICE %s :*** Successfully rehashed server.", user->nick.c_str());
762         ServerInstance->SNO->WriteGlobalSno('a', "*** Successfully rehashed server.");
763 }
764
765 void ServerConfig::ApplyModules(User* user)
766 {
767         Module* whowas = ServerInstance->Modules->Find("cmd_whowas.so");
768         if (whowas)
769                 WhowasRequest(NULL, whowas, WhowasRequest::WHOWAS_PRUNE).Send();
770
771         const std::vector<std::string> v = ServerInstance->Modules->GetAllModuleNames(0);
772         std::vector<std::string> added_modules;
773         std::set<std::string> removed_modules(v.begin(), v.end());
774
775         ConfigTagList tags = ConfTags("module");
776         for(ConfigIter i = tags.first; i != tags.second; ++i)
777         {
778                 ConfigTag* tag = i->second;
779                 std::string name;
780                 if (tag->readString("name", name))
781                 {
782                         // if this module is already loaded, the erase will succeed, so we need do nothing
783                         // otherwise, we need to add the module (which will be done later)
784                         if (removed_modules.erase(name) == 0)
785                                 added_modules.push_back(name);
786                 }
787         }
788
789         if (ConfValue("options")->getBool("allowhalfop") && removed_modules.erase("m_halfop.so") == 0)
790                 added_modules.push_back("m_halfop.so");
791
792         for (std::set<std::string>::iterator removing = removed_modules.begin(); removing != removed_modules.end(); removing++)
793         {
794                 // Don't remove cmd_*.so, just remove m_*.so
795                 if (removing->c_str()[0] == 'c')
796                         continue;
797                 Module* m = ServerInstance->Modules->Find(*removing);
798                 if (m && ServerInstance->Modules->Unload(m))
799                 {
800                         ServerInstance->SNO->WriteGlobalSno('a', "*** REHASH UNLOADED MODULE: %s",removing->c_str());
801
802                         if (user)
803                                 user->WriteNumeric(RPL_UNLOADEDMODULE, "%s %s :Module %s successfully unloaded.",user->nick.c_str(), removing->c_str(), removing->c_str());
804                         else
805                                 ServerInstance->SNO->WriteGlobalSno('a', "Module %s successfully unloaded.", removing->c_str());
806                 }
807                 else
808                 {
809                         if (user)
810                                 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());
811                         else
812                                  ServerInstance->SNO->WriteGlobalSno('a', "Failed to unload module %s: %s", removing->c_str(), ServerInstance->Modules->LastError().c_str());
813                 }
814         }
815
816         for (std::vector<std::string>::iterator adding = added_modules.begin(); adding != added_modules.end(); adding++)
817         {
818                 if (ServerInstance->Modules->Load(adding->c_str()))
819                 {
820                         ServerInstance->SNO->WriteGlobalSno('a', "*** REHASH LOADED MODULE: %s",adding->c_str());
821                         if (user)
822                                 user->WriteNumeric(RPL_LOADEDMODULE, "%s %s :Module %s successfully loaded.",user->nick.c_str(), adding->c_str(), adding->c_str());
823                         else
824                                 ServerInstance->SNO->WriteGlobalSno('a', "Module %s successfully loaded.", adding->c_str());
825                 }
826                 else
827                 {
828                         if (user)
829                                 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());
830                         else
831                                 ServerInstance->SNO->WriteGlobalSno('a', "Failed to load module %s: %s", adding->c_str(), ServerInstance->Modules->LastError().c_str());
832                 }
833         }
834 }
835
836 bool ServerConfig::StartsWithWindowsDriveLetter(const std::string &path)
837 {
838         return (path.length() > 2 && isalpha(path[0]) && path[1] == ':');
839 }
840
841 ConfigTag* ServerConfig::ConfValue(const std::string &tag)
842 {
843         ConfigDataHash::iterator iter = config_data.find(tag);
844         if (iter == config_data.end())
845                 return NULL;
846         return iter->second;
847 }
848
849 ConfigTagList ServerConfig::ConfTags(const std::string& tag)
850 {
851         return config_data.equal_range(tag);
852 }
853
854 /** Read the contents of a file located by `fname' into a file_cache pointed at by `F'.
855  */
856 bool ServerConfig::ReadFile(file_cache &F, const std::string& fname)
857 {
858         if (fname.empty())
859                 return false;
860
861         char linebuf[MAXBUF];
862
863         F.clear();
864
865         FileWrapper file(fopen(fname.c_str(), "r"));
866
867         if (!file)
868                 return false;
869         while (!feof(file))
870         {
871                 if (fgets(linebuf, sizeof(linebuf), file))
872                         linebuf[strlen(linebuf)-1] = 0;
873                 else
874                         *linebuf = 0;
875
876                 F.push_back(*linebuf ? linebuf : " ");
877         }
878
879         return true;
880 }
881
882 bool ServerConfig::FileExists(const char* file)
883 {
884         struct stat sb;
885         if (stat(file, &sb) == -1)
886                 return false;
887
888         if ((sb.st_mode & S_IFDIR) > 0)
889                 return false;
890
891         FILE *input = fopen(file, "r");
892         if (input == NULL)
893                 return false;
894         else
895         {
896                 fclose(input);
897                 return true;
898         }
899 }
900
901 const char* ServerConfig::CleanFilename(const char* name)
902 {
903         const char* p = name + strlen(name);
904         while ((p != name) && (*p != '/') && (*p != '\\')) p--;
905         return (p != name ? ++p : p);
906 }
907
908 std::string ServerConfig::GetSID()
909 {
910         return sid;
911 }
912
913 void ConfigReaderThread::Run()
914 {
915         Config->Read();
916         done = true;
917 }
918
919 void ConfigReaderThread::Finish()
920 {
921         ServerConfig* old = ServerInstance->Config;
922         ServerInstance->Logs->Log("CONFIG",DEBUG,"Switching to new configuration...");
923         ServerInstance->Logs->CloseLogs();
924         ServerInstance->Config = this->Config;
925         ServerInstance->Logs->OpenFileLogs();
926         Config->Apply(old, TheUserUID);
927
928         if (Config->valid)
929         {
930                 /*
931                  * Apply the changed configuration from the rehash.
932                  *
933                  * XXX: The order of these is IMPORTANT, do not reorder them without testing
934                  * thoroughly!!!
935                  */
936                 ServerInstance->XLines->CheckELines();
937                 ServerInstance->XLines->CheckELines();
938                 ServerInstance->XLines->ApplyLines();
939                 ServerInstance->Res->Rehash();
940                 ServerInstance->ResetMaxBans();
941                 Config->ApplyDisabledCommands(Config->DisabledCommands);
942                 User* user = TheUserUID.empty() ? ServerInstance->FindNick(TheUserUID) : NULL;
943                 FOREACH_MOD(I_OnRehash, OnRehash(user));
944                 ServerInstance->BuildISupport();
945
946                 Config = old;
947         }
948         else
949         {
950                 // whoops, abort!
951                 ServerInstance->Logs->CloseLogs();
952                 ServerInstance->Config = old;
953                 ServerInstance->Logs->OpenFileLogs();
954         }
955 }