]> git.netwichtig.de Git - user/henk/code/inspircd.git/blob - src/configreader.cpp
617d5fdd507cf759d32701247e6534a09e8c89c0
[user/henk/code/inspircd.git] / src / configreader.cpp
1 /*       +------------------------------------+
2  *       | Inspire Internet Relay Chat Daemon |
3  *       +------------------------------------+
4  *
5  *  InspIRCd: (C) 2002-2008 InspIRCd Development Team
6  * See: http://www.inspircd.org/wiki/index.php/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
27 #include "inspircd.h"
28 #include <fstream>
29 #include "xline.h"
30 #include "exitcodes.h"
31 #include "commands/cmd_whowas.h"
32
33 std::vector<std::string> old_module_names, new_module_names, added_modules, removed_modules;
34
35 /* Needs forward declaration */
36 bool ValidateDnsServer(ServerConfig* conf, const char* tag, const char* value, ValueItem &data);
37 bool DoneELine(ServerConfig* conf, const char* tag);
38
39 ServerConfig::ServerConfig(InspIRCd* Instance) : ServerInstance(Instance)
40 {
41         this->ClearStack();
42         *sid = *ServerName = *Network = *ServerDesc = *AdminName = '\0';
43         *HideWhoisServer = *AdminEmail = *AdminNick = *diepass = *restartpass = *FixedQuit = *HideKillsServer = '\0';
44         *DefaultModes = *CustomVersion = *motd = *rules = *PrefixQuit = *DieValue = *DNSServer = '\0';
45         *UserStats = *ModPath = *MyExecutable = *DisabledCommands = *PID = *SuffixQuit = '\0';
46         WhoWasGroupSize = WhoWasMaxGroups = WhoWasMaxKeep = 0;
47         log_file = NULL;
48         NoUserDns = forcedebug = OperSpyWhois = nofork = HideBans = HideSplits = UndernetMsgPrefix = false;
49         CycleHosts = writelog = AllowHalfop = InvBypassModes = true;
50         dns_timeout = DieDelay = 5;
51         MaxTargets = 20;
52         NetBufferSize = 10240;
53         SoftLimit = Instance->SE->GetMaxFds();
54         MaxConn = SOMAXCONN;
55         MaxWhoResults = 0;
56         debugging = 0;
57         MaxChans = 20;
58         OperMaxChans = 30;
59         c_ipv4_range = 32;
60         c_ipv6_range = 128;
61         maxbans.clear();
62         DNSServerValidator = &ValidateDnsServer;
63 }
64
65 void ServerConfig::ClearStack()
66 {
67         include_stack.clear();
68 }
69
70 void ServerConfig::Update005()
71 {
72         std::stringstream out(data005);
73         std::string token;
74         std::string line5;
75         int token_counter = 0;
76         isupport.clear();
77         while (out >> token)
78         {
79                 line5 = line5 + token + " ";
80                 token_counter++;
81                 if (token_counter >= 13)
82                 {
83                         char buf[MAXBUF];
84                         snprintf(buf, MAXBUF, "%s:are supported by this server", line5.c_str());
85                         isupport.push_back(buf);
86                         line5.clear();
87                         token_counter = 0;
88                 }
89         }
90         if (!line5.empty())
91         {
92                 char buf[MAXBUF];
93                 snprintf(buf, MAXBUF, "%s:are supported by this server", line5.c_str());
94                 isupport.push_back(buf);
95         }
96 }
97
98 void ServerConfig::Send005(User* user)
99 {
100         for (std::vector<std::string>::iterator line = ServerInstance->Config->isupport.begin(); line != ServerInstance->Config->isupport.end(); line++)
101                 user->WriteNumeric(RPL_ISUPPORT, "%s %s", user->nick.c_str(), line->c_str());
102 }
103
104 bool ServerConfig::CheckOnce(const char* tag, ConfigDataHash &newconf)
105 {
106         int count = ConfValueEnum(newconf, tag);
107
108         if (count > 1)
109                 throw CoreException("You have more than one <"+std::string(tag)+"> tag, this is not permitted.");
110         if (count < 1)
111                 throw CoreException("You have not defined a <"+std::string(tag)+"> tag, this is required.");
112         return true;
113 }
114
115 bool NoValidation(ServerConfig*, const char*, const char*, ValueItem&)
116 {
117         return true;
118 }
119
120 bool DoneConfItem(ServerConfig* conf, const char* tag)
121 {
122         return true;
123 }
124
125 void ServerConfig::ValidateNoSpaces(const char* p, const std::string &tag, const std::string &val)
126 {
127         for (const char* ptr = p; *ptr; ++ptr)
128         {
129                 if (*ptr == ' ')
130                         throw CoreException("The value of <"+tag+":"+val+"> cannot contain spaces");
131         }
132 }
133
134 /* 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,
135  * even in strerror(errno). They just return 'yes' or 'no' to an address without such detail as to whats WRONG with the address.
136  * Because ircd users arent as technical as they used to be (;)) we are going to give more of a useful error message.
137  */
138 void ServerConfig::ValidateIP(const char* p, const std::string &tag, const std::string &val, bool wild)
139 {
140         int num_dots = 0;
141         int num_seps = 0;
142         int not_numbers = false;
143         int not_hex = false;
144
145         if (*p)
146         {
147                 if (*p == '.')
148                         throw CoreException("The value of <"+tag+":"+val+"> is not an IP address");
149
150                 for (const char* ptr = p; *ptr; ++ptr)
151                 {
152                         if (wild && (*ptr == '*' || *ptr == '?' || *ptr == '/'))
153                                 continue;
154
155                         if (*ptr != ':' && *ptr != '.')
156                         {
157                                 if (*ptr < '0' || *ptr > '9')
158                                         not_numbers = true;
159                                 if ((*ptr < '0' || *ptr > '9') && (toupper(*ptr) < 'A' || toupper(*ptr) > 'F'))
160                                         not_hex = true;
161                         }
162                         switch (*ptr)
163                         {
164                                 case ' ':
165                                         throw CoreException("The value of <"+tag+":"+val+"> is not an IP address");
166                                 case '.':
167                                         num_dots++;
168                                 break;
169                                 case ':':
170                                         num_seps++;
171                                 break;
172                         }
173                 }
174
175                 if (num_dots > 3)
176                         throw CoreException("The value of <"+tag+":"+val+"> is an IPv4 address with too many fields!");
177
178                 if (num_seps > 8)
179                         throw CoreException("The value of <"+tag+":"+val+"> is an IPv6 address with too many fields!");
180
181                 if (num_seps == 0 && num_dots < 3 && !wild)
182                         throw CoreException("The value of <"+tag+":"+val+"> looks to be a malformed IPv4 address");
183
184                 if (num_seps == 0 && num_dots == 3 && not_numbers)
185                         throw CoreException("The value of <"+tag+":"+val+"> contains non-numeric characters in an IPv4 address");
186
187                 if (num_seps != 0 && not_hex)
188                         throw CoreException("The value of <"+tag+":"+val+"> contains non-hexdecimal characters in an IPv6 address");
189
190                 if (num_seps != 0 && num_dots != 3 && num_dots != 0 && !wild)
191                         throw CoreException("The value of <"+tag+":"+val+"> is a malformed IPv6 4in6 address");
192         }
193 }
194
195 void ServerConfig::ValidateHostname(const char* p, const std::string &tag, const std::string &val)
196 {
197         int num_dots = 0;
198         if (*p)
199         {
200                 if (*p == '.')
201                         throw CoreException("The value of <"+tag+":"+val+"> is not a valid hostname");
202                 for (const char* ptr = p; *ptr; ++ptr)
203                 {
204                         switch (*ptr)
205                         {
206                                 case ' ':
207                                         throw CoreException("The value of <"+tag+":"+val+"> is not a valid hostname");
208                                 case '.':
209                                         num_dots++;
210                                 break;
211                         }
212                 }
213                 if (num_dots == 0)
214                         throw CoreException("The value of <"+tag+":"+val+"> is not a valid hostname");
215         }
216 }
217
218 bool ValidateMaxTargets(ServerConfig* conf, const char*, const char*, ValueItem &data)
219 {
220         if ((data.GetInteger() < 0) || (data.GetInteger() > 31))
221         {
222                 conf->GetInstance()->Logs->Log("CONFIG",DEFAULT,"WARNING: <security:maxtargets> value is greater than 31 or less than 0, set to 20.");
223                 data.Set(20);
224         }
225         return true;
226 }
227
228 bool ValidateSoftLimit(ServerConfig* conf, const char*, const char*, ValueItem &data)
229 {
230         if ((data.GetInteger() < 1) || (data.GetInteger() > conf->GetInstance()->SE->GetMaxFds()))
231         {
232                 conf->GetInstance()->Logs->Log("CONFIG",DEFAULT,"WARNING: <performance:softlimit> value is greater than %d or less than 0, set to %d.",conf->GetInstance()->SE->GetMaxFds(),conf->GetInstance()->SE->GetMaxFds());
233                 data.Set(conf->GetInstance()->SE->GetMaxFds());
234         }
235         return true;
236 }
237
238 bool ValidateMaxConn(ServerConfig* conf, const char*, const char*, ValueItem &data)
239 {
240         if (data.GetInteger() > SOMAXCONN)
241                 conf->GetInstance()->Logs->Log("CONFIG",DEFAULT,"WARNING: <performance:somaxconn> value may be higher than the system-defined SOMAXCONN value!");
242         return true;
243 }
244
245 bool InitializeDisabledCommands(const char* data, InspIRCd* ServerInstance)
246 {
247         std::stringstream dcmds(data);
248         std::string thiscmd;
249
250         /* Enable everything first */
251         for (Commandtable::iterator x = ServerInstance->Parser->cmdlist.begin(); x != ServerInstance->Parser->cmdlist.end(); x++)
252                 x->second->Disable(false);
253
254         /* Now disable all the ones which the user wants disabled */
255         while (dcmds >> thiscmd)
256         {
257                 Commandtable::iterator cm = ServerInstance->Parser->cmdlist.find(thiscmd);
258                 if (cm != ServerInstance->Parser->cmdlist.end())
259                 {
260                         cm->second->Disable(true);
261                 }
262         }
263         return true;
264 }
265
266 bool ValidateDisabledUModes(ServerConfig* conf, const char*, const char*, ValueItem &data)
267 {
268         memset(conf->DisabledUModes, 0, sizeof(conf->DisabledUModes));
269         for (const unsigned char* p = (const unsigned char*)data.GetString(); *p; ++p)
270         {
271                 if (*p < 'A' || *p > ('A' + 64)) throw CoreException(std::string("Invalid usermode ")+(char)*p+" was found.");
272                 conf->DisabledUModes[*p - 'A'] = 1;
273         }
274         return true;
275 }
276
277 bool ValidateDisabledCModes(ServerConfig* conf, const char*, const char*, ValueItem &data)
278 {
279         memset(conf->DisabledCModes, 0, sizeof(conf->DisabledCModes));
280         for (const unsigned char* p = (const unsigned char*)data.GetString(); *p; ++p)
281         {
282                 if (*p < 'A' || *p > ('A' + 64)) throw CoreException(std::string("Invalid chanmode ")+(char)*p+" was found.");
283                 conf->DisabledCModes[*p - 'A'] = 1;
284         }
285         return true;
286 }
287
288 bool ValidateDnsServer(ServerConfig* conf, const char*, const char*, ValueItem &data)
289 {
290         if (!*(data.GetString()))
291         {
292                 std::string nameserver;
293                 // attempt to look up their nameserver from /etc/resolv.conf
294                 conf->GetInstance()->Logs->Log("CONFIG",DEFAULT,"WARNING: <dns:server> not defined, attempting to find working server in /etc/resolv.conf...");
295                 std::ifstream resolv("/etc/resolv.conf");
296                 bool found_server = false;
297
298                 if (resolv.is_open())
299                 {
300                         while (resolv >> nameserver)
301                         {
302                                 if ((nameserver == "nameserver") && (!found_server))
303                                 {
304                                         resolv >> nameserver;
305                                         data.Set(nameserver.c_str());
306                                         found_server = true;
307                                         conf->GetInstance()->Logs->Log("CONFIG",DEFAULT,"<dns:server> set to '%s' as first resolver in /etc/resolv.conf.",nameserver.c_str());
308                                 }
309                         }
310
311                         if (!found_server)
312                         {
313                                 conf->GetInstance()->Logs->Log("CONFIG",DEFAULT,"/etc/resolv.conf contains no viable nameserver entries! Defaulting to nameserver '127.0.0.1'!");
314                                 data.Set("127.0.0.1");
315                         }
316                 }
317                 else
318                 {
319                         conf->GetInstance()->Logs->Log("CONFIG",DEFAULT,"/etc/resolv.conf can't be opened! Defaulting to nameserver '127.0.0.1'!");
320                         data.Set("127.0.0.1");
321                 }
322         }
323         return true;
324 }
325
326 bool ValidateServerName(ServerConfig* conf, const char*, const char*, ValueItem &data)
327 {
328         conf->GetInstance()->Logs->Log("CONFIG",DEFAULT,"Validating server name");
329         /* If we already have a servername, and they changed it, we should throw an exception. */
330         if (!strchr(data.GetString(), '.'))
331         {
332                 conf->GetInstance()->Logs->Log("CONFIG",DEFAULT,"WARNING: <server:name> '%s' is not a fully-qualified domain name. Changed to '%s%c'",data.GetString(),data.GetString(),'.');
333                 std::string moo = std::string(data.GetString()).append(".");
334                 data.Set(moo.c_str());
335         }
336         conf->ValidateHostname(data.GetString(), "server", "name");
337         return true;
338 }
339
340 bool ValidateNetBufferSize(ServerConfig* conf, const char*, const char*, ValueItem &data)
341 {
342         // 65534 not 65535 because of null terminator
343         if ((!data.GetInteger()) || (data.GetInteger() > 65534) || (data.GetInteger() < 1024))
344         {
345                 conf->GetInstance()->Logs->Log("CONFIG",DEFAULT,"No NetBufferSize specified or size out of range, setting to default of 10240.");
346                 data.Set(10240);
347         }
348         return true;
349 }
350
351 bool ValidateMaxWho(ServerConfig* conf, const char*, const char*, ValueItem &data)
352 {
353         if ((data.GetInteger() > 65535) || (data.GetInteger() < 1))
354         {
355                 conf->GetInstance()->Logs->Log("CONFIG",DEFAULT,"<performance:maxwho> size out of range, setting to default of 128.");
356                 data.Set(128);
357         }
358         return true;
359 }
360
361 bool ValidateMotd(ServerConfig* conf, const char*, const char*, ValueItem &data)
362 {
363         conf->ReadFile(conf->MOTD, data.GetString());
364         return true;
365 }
366
367 bool ValidateNotEmpty(ServerConfig*, const char* tag, const char* val, ValueItem &data)
368 {
369         if (!*data.GetString())
370                 throw CoreException(std::string("The value for <")+tag+":"+val+"> cannot be empty!");
371         return true;
372 }
373
374 bool ValidateRules(ServerConfig* conf, const char*, const char*, ValueItem &data)
375 {
376         conf->ReadFile(conf->RULES, data.GetString());
377         return true;
378 }
379
380 bool ValidateModeLists(ServerConfig* conf, const char*, const char*, ValueItem &data)
381 {
382         memset(conf->HideModeLists, 0, sizeof(conf->HideModeLists));
383         for (const unsigned char* x = (const unsigned char*)data.GetString(); *x; ++x)
384                 conf->HideModeLists[*x] = true;
385         return true;
386 }
387
388 bool ValidateExemptChanOps(ServerConfig* conf, const char*, const char*, ValueItem &data)
389 {
390         memset(conf->ExemptChanOps, 0, sizeof(conf->ExemptChanOps));
391         for (const unsigned char* x = (const unsigned char*)data.GetString(); *x; ++x)
392                 conf->ExemptChanOps[*x] = true;
393         return true;
394 }
395
396 bool ValidateInvite(ServerConfig* conf, const char*, const char*, ValueItem &data)
397 {
398         std::string v = data.GetString();
399
400         if (v == "ops")
401                 conf->AnnounceInvites = ServerConfig::INVITE_ANNOUNCE_OPS;
402         else if (v == "all")
403                 conf->AnnounceInvites = ServerConfig::INVITE_ANNOUNCE_ALL;
404         else if (v == "dynamic")
405                 conf->AnnounceInvites = ServerConfig::INVITE_ANNOUNCE_DYNAMIC;
406         else
407                 conf->AnnounceInvites = ServerConfig::INVITE_ANNOUNCE_NONE;
408
409         return true;
410 }
411
412 bool ValidateSID(ServerConfig* conf, const char*, const char*, ValueItem &data)
413 {
414         conf->GetInstance()->Logs->Log("CONFIG",DEFAULT,"Validating server id");
415
416         const char *sid = data.GetString();
417
418         if (*sid && !conf->GetInstance()->IsSID(sid))
419         {
420                 throw CoreException(std::string(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.");
421         }
422
423         strlcpy(conf->sid, sid, 5);
424
425         return true;
426 }
427
428 bool ValidateWhoWas(ServerConfig* conf, const char*, const char*, ValueItem &data)
429 {
430         conf->WhoWasMaxKeep = conf->GetInstance()->Duration(data.GetString());
431
432         if (conf->WhoWasGroupSize < 0)
433                 conf->WhoWasGroupSize = 0;
434
435         if (conf->WhoWasMaxGroups < 0)
436                 conf->WhoWasMaxGroups = 0;
437
438         if (conf->WhoWasMaxKeep < 3600)
439         {
440                 conf->WhoWasMaxKeep = 3600;
441                 conf->GetInstance()->Logs->Log("CONFIG",DEFAULT,"WARNING: <whowas:maxkeep> value less than 3600, setting to default 3600");
442         }
443
444         Command* whowas_command = conf->GetInstance()->Parser->GetHandler("WHOWAS");
445         if (whowas_command)
446         {
447                 std::deque<classbase*> params;
448                 whowas_command->HandleInternal(WHOWAS_PRUNE, params);
449         }
450
451         return true;
452 }
453
454 /* Callback called before processing the first <connect> tag
455  */
456 bool InitConnect(ServerConfig* conf, const char*)
457 {
458         conf->GetInstance()->Logs->Log("CONFIG",DEFAULT,"Reading connect classes... class list is:");
459
460         for (ClassVector::iterator item = conf->Classes.begin(); item != conf->Classes.end(); item++)
461         {
462                 conf->GetInstance()->Logs->Log("CONFIG",DEFAULT, "class: %p", *(item));
463         }
464
465
466         /*
467          * Remove all connect classes.. we'll reset the pointers in user classes
468          * once all new classes have been read from config.
469          */
470         while (conf->Classes.begin() != conf->Classes.end())
471         {
472                 ConnectClass *c = *(conf->Classes.begin());
473                 conf->GetInstance()->Logs->Log("CONFIG",DEFAULT, "Deleting an old class! :) (%p)", c);
474
475
476                 conf->Classes.erase(conf->Classes.begin());
477                 delete c;
478         }
479
480         return true;
481 }
482
483 /* Callback called to process a single <connect> tag
484  */
485 bool DoConnect(ServerConfig* conf, const char*, char**, ValueList &values, int*)
486 {
487         ConnectClass c;
488         const char* allow = values[0].GetString(); /* Yeah, there are a lot of values. Live with it. */
489         const char* deny = values[1].GetString();
490         const char* password = values[2].GetString();
491         int timeout = values[3].GetInteger();
492         int pingfreq = values[4].GetInteger();
493         int flood = values[5].GetInteger();
494         int threshold = values[6].GetInteger();
495         int sendq = values[7].GetInteger();
496         int recvq = values[8].GetInteger();
497         int localmax = values[9].GetInteger();
498         int globalmax = values[10].GetInteger();
499         int port = values[11].GetInteger();
500         const char* name = values[12].GetString();
501         const char* parent = values[13].GetString();
502         int maxchans = values[14].GetInteger();
503         unsigned long limit = values[15].GetInteger();
504         const char* hashtype = values[16].GetString();
505
506         conf->GetInstance()->Logs->Log("CONFIG",DEFAULT,"Adding a connect class!");
507
508         ConnectClass *cc = NULL;
509
510         if (*parent)
511         {
512                 /* Find 'parent' and inherit a new class from it,
513                  * then overwrite any values that are set here
514                  */
515                 ClassVector::iterator item = conf->Classes.begin();
516                 for (; item != conf->Classes.end(); ++item)
517                 {
518                         cc = *item;
519                         conf->GetInstance()->Logs->Log("CONFIG",DEBUG,"Class: %s", cc->GetName().c_str());
520                         if (cc->GetName() == parent)
521                         {
522                                 cc = new ConnectClass(name, cc);
523                                 cc->Update(timeout, flood, *allow ? allow : deny, pingfreq, password, threshold, sendq, recvq, localmax, globalmax, maxchans, port, limit);
524                                 conf->Classes.push_back(cc);
525                                 break;
526                         }
527                 }
528                 if (item == conf->Classes.end())
529                         throw CoreException("Class name '" + std::string(name) + "' is configured to inherit from class '" + std::string(parent) + "' which cannot be found.");
530         }
531         else
532         {
533                 if (*allow)
534                 {
535                         /* Find existing class by mask, the mask should be unique */
536                         for (ClassVector::iterator item = conf->Classes.begin(); item != conf->Classes.end(); ++item)
537                         {
538                                 if ((*item)->GetHost() == allow)
539                                 {
540                                         (*item)->Update(timeout, flood, allow, pingfreq, password, threshold, sendq, recvq, localmax, globalmax, maxchans, port, limit);
541                                         return true;
542                                 }
543                         }
544                         cc = new ConnectClass(name, timeout, flood, allow, pingfreq, password, hashtype, threshold, sendq, recvq, localmax, globalmax, maxchans);
545                         cc->limit = limit;
546                         cc->SetPort(port);
547                         conf->Classes.push_back(cc);
548                 }
549                 else
550                 {
551                         /* Find existing class by mask, the mask should be unique */
552                         for (ClassVector::iterator item = conf->Classes.begin(); item != conf->Classes.end(); ++item)
553                         {
554                                 if ((*item)->GetHost() == deny)
555                                 {
556                                         (*item)->Update(name, deny);
557                                         (*item)->SetPort(port);
558                                         return true;
559                                 }
560                         }
561                         cc = new ConnectClass(name, deny);
562                         cc->SetPort(port);
563                         conf->Classes.push_back(cc);
564                 }
565         }
566
567         conf->GetInstance()->Logs->Log("CONFIG",DEFAULT, "Class added: %p", cc);
568         return true;
569 }
570
571 /* Callback called when there are no more <connect> tags
572  */
573 bool DoneConnect(ServerConfig *conf, const char*)
574 {
575         for (ClassVector::iterator item = conf->Classes.begin(); item != conf->Classes.end(); item++)
576         {
577                 conf->GetInstance()->Logs->Log("CONFIG",DEFAULT, "class: %p", *(item));
578         }
579
580         /*
581          * Update connect classes on all users.
582          */
583         conf->GetInstance()->Logs->Log("CONFIG",DEFAULT, "Resetting connect classes for users...");
584         for (std::vector<User*>::iterator n = conf->GetInstance()->Users->local_users.begin(); n != conf->GetInstance()->Users->local_users.end(); n++)
585         {
586                 User *u = *n;
587
588                 /*
589                  * Make their existing class go away so that SetClass doesn't touch a wild ptr, important!
590                  */
591                 u->MyClass = NULL;
592
593                 u->SetClass();
594
595                 /*
596                  * Check that the user falls into a valid class block.. if they don't,
597                  * they need to be quit, which CheckClass will do. -- w00t
598                  */
599                 u->CheckClass();
600                 conf->GetInstance()->Logs->Log("CONFIG",DEFAULT, "%s is now in %p", u->uuid.c_str(), u->MyClass);
601         }
602
603
604         conf->GetInstance()->Logs->Log("CONFIG",DEFAULT, "Done adding connect classes!");
605         return true;
606 }
607
608 /* Callback called before processing the first <uline> tag
609  */
610 bool InitULine(ServerConfig* conf, const char*)
611 {
612         conf->ulines.clear();
613         return true;
614 }
615
616 /* Callback called to process a single <uline> tag
617  */
618 bool DoULine(ServerConfig* conf, const char*, char**, ValueList &values, int*)
619 {
620         const char* server = values[0].GetString();
621         const bool silent = values[1].GetBool();
622         conf->ulines[server] = silent;
623         return true;
624 }
625
626 /* Callback called when there are no more <uline> tags
627  */
628 bool DoneULine(ServerConfig*, const char*)
629 {
630         return true;
631 }
632
633 /* Callback called before processing the first <module> tag
634  */
635 bool InitModule(ServerConfig* conf, const char*)
636 {
637         old_module_names = conf->GetInstance()->Modules->GetAllModuleNames(0);
638         new_module_names.clear();
639         added_modules.clear();
640         removed_modules.clear();
641         return true;
642 }
643
644 /* Callback called to process a single <module> tag
645  */
646 bool DoModule(ServerConfig*, const char*, char**, ValueList &values, int*)
647 {
648         const char* modname = values[0].GetString();
649         new_module_names.push_back(modname);
650         return true;
651 }
652
653 /* Callback called when there are no more <module> tags
654  */
655 bool DoneModule(ServerConfig*, const char*)
656 {
657         // now create a list of new modules that are due to be loaded
658         // and a seperate list of modules which are due to be unloaded
659         for (std::vector<std::string>::iterator _new = new_module_names.begin(); _new != new_module_names.end(); _new++)
660         {
661                 bool added = true;
662
663                 for (std::vector<std::string>::iterator old = old_module_names.begin(); old != old_module_names.end(); old++)
664                 {
665                         if (*old == *_new)
666                                 added = false;
667                 }
668
669                 if (added)
670                         added_modules.push_back(*_new);
671         }
672
673         for (std::vector<std::string>::iterator oldm = old_module_names.begin(); oldm != old_module_names.end(); oldm++)
674         {
675                 bool removed = true;
676                 for (std::vector<std::string>::iterator newm = new_module_names.begin(); newm != new_module_names.end(); newm++)
677                 {
678                         if (*newm == *oldm)
679                                 removed = false;
680                 }
681
682                 if (removed)
683                         removed_modules.push_back(*oldm);
684         }
685         return true;
686 }
687
688 /* Callback called before processing the first <banlist> tag
689  */
690 bool InitMaxBans(ServerConfig* conf, const char*)
691 {
692         conf->maxbans.clear();
693         return true;
694 }
695
696 /* Callback called to process a single <banlist> tag
697  */
698 bool DoMaxBans(ServerConfig* conf, const char*, char**, ValueList &values, int*)
699 {
700         const char* channel = values[0].GetString();
701         int limit = values[1].GetInteger();
702         conf->maxbans[channel] = limit;
703         return true;
704 }
705
706 /* Callback called when there are no more <banlist> tags.
707  */
708 bool DoneMaxBans(ServerConfig*, const char*)
709 {
710         return true;
711 }
712
713 void ServerConfig::ReportConfigError(const std::string &errormessage, bool bail, const std::string &useruid)
714 {
715         ServerInstance->Logs->Log("CONFIG",DEFAULT, "There were errors in your configuration file: %s", errormessage.c_str());
716         if (bail)
717         {
718                 /* Unneeded because of the ServerInstance->Log() aboive? */
719                 printf("There were errors in your configuration:\n%s\n\n",errormessage.c_str());
720                 ServerInstance->Exit(EXIT_STATUS_CONFIG);
721         }
722         else
723         {
724                 std::string errors = errormessage;
725                 std::string::size_type start;
726                 unsigned int prefixlen;
727                 start = 0;
728                 /* ":ServerInstance->Config->ServerName NOTICE user->nick :" */
729                 if (!useruid.empty())
730                 {
731                         User* user = ServerInstance->FindNick(useruid);
732                         if (user)
733                         {
734                                 prefixlen = strlen(this->ServerName) + user->nick.length() + 11;
735                                 user->WriteServ("NOTICE %s :There were errors in the configuration file:",user->nick.c_str());
736                                 while (start < errors.length())
737                                 {
738                                         user->WriteServ("NOTICE %s :%s",user->nick.c_str(), errors.substr(start, 510 - prefixlen).c_str());
739                                         start += 510 - prefixlen;
740                                 }
741                         }
742                 }
743                 else
744                 {
745                         ServerInstance->SNO->WriteToSnoMask('A', "There were errors in the configuration file:");
746                         while (start < errors.length())
747                         {
748                                 ServerInstance->SNO->WriteToSnoMask('A', errors.substr(start, 360));
749                                 start += 360;
750                         }
751                 }
752                 return;
753         }
754 }
755
756 void ServerConfig::Read(bool bail, const std::string &useruid)
757 {
758         int rem = 0, add = 0;      /* Number of modules added, number of modules removed */
759
760         static char maxkeep[MAXBUF];    /* Temporary buffer for WhoWasMaxKeep value */
761         static char hidemodes[MAXBUF];  /* Modes to not allow listing from users below halfop */
762         static char exemptchanops[MAXBUF];      /* Exempt channel ops from these modes */
763         static char announceinvites[MAXBUF];    /* options:announceinvites setting */
764         static char disabledumodes[MAXBUF]; /* Disabled usermodes */
765         static char disabledcmodes[MAXBUF]; /* Disabled chanmodes */
766         errstr.clear();
767
768         include_stack.clear();
769
770         /* These tags MUST occur and must ONLY occur once in the config file */
771         static const char* Once[] = { "server", "admin", "files", "power", "options", NULL };
772
773         Deprecated ChangedConfig[] = {
774                 {"options",     "hidelinks",            "has been moved to <security:hidelinks> as of 1.2a3"},
775                 {"options",     "hidewhois",            "has been moved to <security:hidewhois> as of 1.2a3"},
776                 {"options",     "userstats",            "has been moved to <security:userstats> as of 1.2a3"},
777                 {"options",     "customversion",        "has been moved to <security:customversion> as of 1.2a3"},
778                 {"options",     "hidesplits",           "has been moved to <security:hidesplits> as of 1.2a3"},
779                 {"options",     "hidebans",             "has been moved to <security:hidebans> as of 1.2a3"},
780                 {"options",     "hidekills",            "has been moved to <security:hidekills> as of 1.2a3"},
781                 {"options",     "operspywhois",         "has been moved to <security:operspywhois> as of 1.2a3"},
782                 {"options",     "announceinvites",      "has been moved to <security:announceinvites> as of 1.2a3"},
783                 {"options",     "hidemodes",            "has been moved to <security:hidemodes> as of 1.2a3"},
784                 {"options",     "maxtargets",           "has been moved to <security:maxtargets> as of 1.2a3"},
785                 {"options",     "nouserdns",            "has been moved to <performance:nouserdns> as of 1.2a3"},
786                 {"options",     "maxwho",               "has been moved to <performance:maxwho> as of 1.2a3"},
787                 {"options",     "softlimit",            "has been moved to <performance:softlimit> as of 1.2a3"},
788                 {"options",     "somaxconn",            "has been moved to <performance:somaxconn> as of 1.2a3"},
789                 {"options",     "netbuffersize",        "has been moved to <performance:netbuffersize> as of 1.2a3"},
790                 {"options",     "maxwho",               "has been moved to <performance:maxwho> as of 1.2a3"},
791                 {"options",     "loglevel",             "1.2 does not use the loglevel value. Please define <log> tags instead."},
792                 {NULL,          NULL,                   NULL}
793         };
794
795         /* These tags can occur ONCE or not at all */
796         InitialConfig Values[] = {
797                 {"performance", "softlimit",    "0",                    new ValueContainerUInt (&this->SoftLimit),              DT_INTEGER,  ValidateSoftLimit},
798                 {"performance", "somaxconn",    SOMAXCONN_S,            new ValueContainerInt  (&this->MaxConn),                DT_INTEGER,  ValidateMaxConn},
799                 {"options",     "moronbanner",  "Youre banned!",        new ValueContainerChar (this->MoronBanner),             DT_CHARPTR,  NoValidation},
800                 {"server",      "name",         "",                     new ValueContainerChar (this->ServerName),              DT_HOSTNAME|DT_BOOTONLY, ValidateServerName},
801                 {"server",      "description",  "Configure Me",         new ValueContainerChar (this->ServerDesc),              DT_CHARPTR,  NoValidation},
802                 {"server",      "network",      "Network",              new ValueContainerChar (this->Network),                 DT_NOSPACES, NoValidation},
803                 {"server",      "id",           "",                     new ValueContainerChar (this->sid),                     DT_CHARPTR|DT_BOOTONLY,  ValidateSID},
804                 {"admin",       "name",         "",                     new ValueContainerChar (this->AdminName),               DT_CHARPTR,  NoValidation},
805                 {"admin",       "email",        "Mis@configu.red",      new ValueContainerChar (this->AdminEmail),              DT_CHARPTR,  NoValidation},
806                 {"admin",       "nick",         "Misconfigured",        new ValueContainerChar (this->AdminNick),               DT_CHARPTR,  NoValidation},
807                 {"files",       "motd",         "",                     new ValueContainerChar (this->motd),                    DT_CHARPTR,  ValidateMotd},
808                 {"files",       "rules",        "",                     new ValueContainerChar (this->rules),                   DT_CHARPTR,  ValidateRules},
809                 {"power",       "diepass",      "",                     new ValueContainerChar (this->diepass),                 DT_CHARPTR,  ValidateNotEmpty},
810                 {"power",       "pause",        "",                     new ValueContainerInt  (&this->DieDelay),               DT_INTEGER,  NoValidation},
811                 {"power",       "hash",         "",                     new ValueContainerChar (this->powerhash),               DT_CHARPTR,  NoValidation},
812                 {"power",       "restartpass",  "",                     new ValueContainerChar (this->restartpass),             DT_CHARPTR,  ValidateNotEmpty},
813                 {"options",     "prefixquit",   "",                     new ValueContainerChar (this->PrefixQuit),              DT_CHARPTR,  NoValidation},
814                 {"options",     "suffixquit",   "",                     new ValueContainerChar (this->SuffixQuit),              DT_CHARPTR,  NoValidation},
815                 {"options",     "fixedquit",    "",                     new ValueContainerChar (this->FixedQuit),               DT_CHARPTR,  NoValidation},
816                 {"options",     "prefixpart",   "",                     new ValueContainerChar (this->PrefixPart),              DT_CHARPTR,  NoValidation},
817                 {"options",     "suffixpart",   "",                     new ValueContainerChar (this->SuffixPart),              DT_CHARPTR,  NoValidation},
818                 {"options",     "fixedpart",    "",                     new ValueContainerChar (this->FixedPart),               DT_CHARPTR,  NoValidation},
819                 {"performance", "netbuffersize","10240",                new ValueContainerInt  (&this->NetBufferSize),          DT_INTEGER,  ValidateNetBufferSize},
820                 {"performance", "maxwho",       "128",                  new ValueContainerInt  (&this->MaxWhoResults),          DT_INTEGER,  ValidateMaxWho},
821                 {"options",     "allowhalfop",  "0",                    new ValueContainerBool (&this->AllowHalfop),            DT_BOOLEAN,  NoValidation},
822                 {"dns",         "server",       "",                     new ValueContainerChar (this->DNSServer),               DT_IPADDRESS,DNSServerValidator},
823                 {"dns",         "timeout",      "5",                    new ValueContainerInt  (&this->dns_timeout),            DT_INTEGER,  NoValidation},
824                 {"options",     "moduledir",    MOD_PATH,               new ValueContainerChar (this->ModPath),                 DT_CHARPTR,  NoValidation},
825                 {"disabled",    "commands",     "",                     new ValueContainerChar (this->DisabledCommands),        DT_CHARPTR,  NoValidation},
826                 {"disabled",    "usermodes",    "",                     new ValueContainerChar (disabledumodes),                DT_CHARPTR,  ValidateDisabledUModes},
827                 {"disabled",    "chanmodes",    "",                     new ValueContainerChar (disabledcmodes),                DT_CHARPTR,  ValidateDisabledCModes},
828                 {"disabled",    "fakenonexistant",      "0",                    new ValueContainerBool (&this->DisabledDontExist),              DT_BOOLEAN,  NoValidation},
829
830                 {"security",            "runasuser",    "",             new ValueContainerChar(this->SetUser),                          DT_CHARPTR, NoValidation},
831                 {"security",            "runasgroup",   "",             new ValueContainerChar(this->SetGroup),                         DT_CHARPTR, NoValidation},
832                 {"security",    "userstats",    "",                     new ValueContainerChar (this->UserStats),               DT_CHARPTR,  NoValidation},
833                 {"security",    "customversion","",                     new ValueContainerChar (this->CustomVersion),           DT_CHARPTR,  NoValidation},
834                 {"security",    "hidesplits",   "0",                    new ValueContainerBool (&this->HideSplits),             DT_BOOLEAN,  NoValidation},
835                 {"security",    "hidebans",     "0",                    new ValueContainerBool (&this->HideBans),               DT_BOOLEAN,  NoValidation},
836                 {"security",    "hidewhois",    "",                     new ValueContainerChar (this->HideWhoisServer),         DT_NOSPACES, NoValidation},
837                 {"security",    "hidekills",    "",                     new ValueContainerChar (this->HideKillsServer),         DT_NOSPACES,  NoValidation},
838                 {"security",    "operspywhois", "0",                    new ValueContainerBool (&this->OperSpyWhois),           DT_BOOLEAN,  NoValidation},
839                 {"performance", "nouserdns",    "0",                    new ValueContainerBool (&this->NoUserDns),              DT_BOOLEAN,  NoValidation},
840                 {"options",     "syntaxhints",  "0",                    new ValueContainerBool (&this->SyntaxHints),            DT_BOOLEAN,  NoValidation},
841                 {"options",     "cyclehosts",   "0",                    new ValueContainerBool (&this->CycleHosts),             DT_BOOLEAN,  NoValidation},
842                 {"options",     "ircumsgprefix","0",                    new ValueContainerBool (&this->UndernetMsgPrefix),      DT_BOOLEAN,  NoValidation},
843                 {"security",    "announceinvites", "1",                 new ValueContainerChar (announceinvites),               DT_CHARPTR,  ValidateInvite},
844                 {"options",     "hostintopic",  "1",                    new ValueContainerBool (&this->FullHostInTopic),        DT_BOOLEAN,  NoValidation},
845                 {"security",    "hidemodes",    "",                     new ValueContainerChar (hidemodes),                     DT_CHARPTR,  ValidateModeLists},
846                 {"options",     "exemptchanops","",                     new ValueContainerChar (exemptchanops),                 DT_CHARPTR,  ValidateExemptChanOps},
847                 {"security",    "maxtargets",   "20",                   new ValueContainerUInt (&this->MaxTargets),             DT_INTEGER,  ValidateMaxTargets},
848                 {"options",     "defaultmodes", "nt",                   new ValueContainerChar (this->DefaultModes),            DT_CHARPTR,  NoValidation},
849                 {"pid",         "file",         "",                     new ValueContainerChar (this->PID),                     DT_CHARPTR,  NoValidation},
850                 {"whowas",      "groupsize",    "10",                   new ValueContainerInt  (&this->WhoWasGroupSize),        DT_INTEGER,  NoValidation},
851                 {"whowas",      "maxgroups",    "10240",                new ValueContainerInt  (&this->WhoWasMaxGroups),        DT_INTEGER,  NoValidation},
852                 {"whowas",      "maxkeep",      "3600",                 new ValueContainerChar (maxkeep),                       DT_CHARPTR,  ValidateWhoWas},
853                 {"die",         "value",        "",                     new ValueContainerChar (this->DieValue),                DT_CHARPTR,  NoValidation},
854                 {"channels",    "users",        "20",                   new ValueContainerUInt (&this->MaxChans),               DT_INTEGER,  NoValidation},
855                 {"channels",    "opers",        "60",                   new ValueContainerUInt (&this->OperMaxChans),           DT_INTEGER,  NoValidation},
856                 {"cidr",        "ipv4clone",    "32",                   new ValueContainerInt (&this->c_ipv4_range),            DT_INTEGER,  NoValidation},
857                 {"cidr",        "ipv6clone",    "128",                  new ValueContainerInt (&this->c_ipv6_range),            DT_INTEGER,  NoValidation},
858                 {"limits",      "maxnick",      "32",                   new ValueContainerST (&this->Limits.NickMax),           DT_INTEGER,  NoValidation},
859                 {"limits",      "maxchan",      "64",                   new ValueContainerST (&this->Limits.ChanMax),           DT_INTEGER,  NoValidation},
860                 {"limits",      "maxmodes",     "20",                   new ValueContainerST (&this->Limits.MaxModes),          DT_INTEGER,  NoValidation},
861                 {"limits",      "maxident",     "11",                   new ValueContainerST (&this->Limits.IdentMax),          DT_INTEGER,  NoValidation},
862                 {"limits",      "maxquit",      "255",                  new ValueContainerST (&this->Limits.MaxQuit),           DT_INTEGER,  NoValidation},
863                 {"limits",      "maxtopic",     "307",                  new ValueContainerST (&this->Limits.MaxTopic),          DT_INTEGER,  NoValidation},
864                 {"limits",      "maxkick",      "255",                  new ValueContainerST (&this->Limits.MaxKick),           DT_INTEGER,  NoValidation},
865                 {"limits",      "maxgecos",     "128",                  new ValueContainerST (&this->Limits.MaxGecos),          DT_INTEGER,  NoValidation},
866                 {"limits",      "maxaway",      "200",                  new ValueContainerST (&this->Limits.MaxAway),           DT_INTEGER,  NoValidation},
867                 {"options",     "invitebypassmodes",    "1",                    new ValueContainerBool (&this->InvBypassModes),         DT_BOOLEAN,  NoValidation},
868                 {NULL,          NULL,           NULL,                   NULL,                                                   DT_NOTHING,  NoValidation}
869         };
870
871
872         /* These tags can occur multiple times, and therefore they have special code to read them
873          * which is different to the code for reading the singular tags listed above.
874          */
875         MultiConfig MultiValues[] = {
876
877                 {"connect",
878                                 {"allow",       "deny",         "password",     "timeout",      "pingfreq",     "flood",
879                                 "threshold",    "sendq",        "recvq",        "localmax",     "globalmax",    "port",
880                                 "name",         "parent",       "maxchans",     "limit",        "hash",
881                                 NULL},
882                                 {"",            "",             "",             "",             "120",          "",
883                                  "",            "",             "",             "3",            "3",            "0",
884                                  "",            "",             "0",        "0",        "",
885                                  NULL},
886                                 {DT_IPADDRESS|DT_ALLOW_WILD,
887                                                 DT_IPADDRESS|DT_ALLOW_WILD,
888                                                                 DT_CHARPTR,     DT_INTEGER,     DT_INTEGER,     DT_INTEGER,
889                                 DT_INTEGER,     DT_INTEGER,     DT_INTEGER,     DT_INTEGER,     DT_INTEGER,     DT_INTEGER,
890                                 DT_NOSPACES,    DT_NOSPACES,    DT_INTEGER,     DT_INTEGER,     DT_CHARPTR},
891                                 InitConnect, DoConnect, DoneConnect},
892
893                 {"uline",
894                                 {"server",      "silent",       NULL},
895                                 {"",            "0",            NULL},
896                                 {DT_HOSTNAME,   DT_BOOLEAN},
897                                 InitULine,DoULine,DoneULine},
898
899                 {"banlist",
900                                 {"chan",        "limit",        NULL},
901                                 {"",            "",             NULL},
902                                 {DT_CHARPTR,    DT_INTEGER},
903                                 InitMaxBans, DoMaxBans, DoneMaxBans},
904
905                 {"module",
906                                 {"name",        NULL},
907                                 {"",            NULL},
908                                 {DT_CHARPTR},
909                                 InitModule, DoModule, DoneModule},
910
911                 {"badip",
912                                 {"reason",      "ipmask",       NULL},
913                                 {"No reason",   "",             NULL},
914                                 {DT_CHARPTR,    DT_IPADDRESS|DT_ALLOW_WILD},
915                                 InitXLine, DoZLine, DoneConfItem},
916
917                 {"badnick",
918                                 {"reason",      "nick",         NULL},
919                                 {"No reason",   "",             NULL},
920                                 {DT_CHARPTR,    DT_CHARPTR},
921                                 InitXLine, DoQLine, DoneConfItem},
922         
923                 {"badhost",
924                                 {"reason",      "host",         NULL},
925                                 {"No reason",   "",             NULL},
926                                 {DT_CHARPTR,    DT_CHARPTR},
927                                 InitXLine, DoKLine, DoneConfItem},
928
929                 {"exception",
930                                 {"reason",      "host",         NULL},
931                                 {"No reason",   "",             NULL},
932                                 {DT_CHARPTR,    DT_CHARPTR},
933                                 InitXLine, DoELine, DoneELine},
934         
935                 {"type",
936                                 {"name",        "classes",      NULL},
937                                 {"",            "",             NULL},
938                                 {DT_NOSPACES,   DT_CHARPTR},
939                                 InitTypes, DoType, DoneClassesAndTypes},
940
941                 {"class",
942                                 {"name",        "commands",     "usermodes",    "chanmodes",    "privs",        NULL},
943                                 {"",            "",                             "",                             "",                     "",                     NULL},
944                                 {DT_NOSPACES,   DT_CHARPTR,     DT_CHARPTR,     DT_CHARPTR, DT_CHARPTR},
945                                 InitClasses, DoClass, DoneClassesAndTypes},
946         
947                 {NULL,
948                                 {NULL},
949                                 {NULL},
950                                 {0},
951                                 NULL, NULL, NULL}
952         };
953
954         /* Load and parse the config file, if there are any errors then explode */
955
956         /* Make a copy here so if it fails then we can carry on running with an unaffected config */
957         newconfig.clear();
958
959         if (!this->DoInclude(newconfig, ServerInstance->ConfigFileName, errstr))
960         {
961                 ReportConfigError(errstr.str(), bail, useruid);
962                 return;
963         }
964
965         /* The stuff in here may throw CoreException, be sure we're in a position to catch it. */
966         try
967         {
968                 /* Check we dont have more than one of singular tags, or any of them missing
969                  */
970                 for (int Index = 0; Once[Index]; Index++)
971                         if (!CheckOnce(Once[Index], newconfig))
972                                 return;
973
974                 for (int Index = 0; ChangedConfig[Index].tag; Index++)
975                 {
976                         char item[MAXBUF];
977                         *item = 0;
978                         if (ConfValue(newconfig, ChangedConfig[Index].tag, ChangedConfig[Index].value, "", 0, item, MAXBUF, true) || *item)
979                                 throw CoreException(std::string("Your configuration contains a deprecated value: <") + ChangedConfig[Index].tag + ":" + ChangedConfig[Index].value + "> - " + ChangedConfig[Index].reason);
980                 }
981
982                 /* Read the values of all the tags which occur once or not at all, and call their callbacks.
983                  */
984                 for (int Index = 0; Values[Index].tag; ++Index)
985                 {
986                         char item[MAXBUF];
987                         int dt = Values[Index].datatype;
988                         bool allow_newlines = ((dt & DT_ALLOW_NEWLINE) > 0);
989                         bool allow_wild = ((dt & DT_ALLOW_WILD) > 0);
990                         bool bootonly = ((dt & DT_BOOTONLY) > 0);
991                         dt &= ~DT_ALLOW_NEWLINE;
992                         dt &= ~DT_ALLOW_WILD;
993                         dt &= ~DT_BOOTONLY;
994
995                         /* Silently ignore boot only values */
996                         if (bootonly && !bail)
997                                 continue;
998
999                         ConfValue(newconfig, Values[Index].tag, Values[Index].value, Values[Index].default_value, 0, item, MAXBUF, allow_newlines);
1000                         ValueItem vi(item);
1001                         
1002                         if (!Values[Index].validation_function(this, Values[Index].tag, Values[Index].value, vi))
1003                                 throw CoreException("One or more values in your configuration file failed to validate. Please see your ircd.log for more information.");
1004         
1005                         ServerInstance->Threads->Lock();
1006                         switch (dt)
1007                         {
1008                                 case DT_NOSPACES:
1009                                 {
1010                                         ValueContainerChar* vcc = (ValueContainerChar*)Values[Index].val;
1011                                         this->ValidateNoSpaces(vi.GetString(), Values[Index].tag, Values[Index].value);
1012                                         vcc->Set(vi.GetString(), strlen(vi.GetString()) + 1);
1013                                 }
1014                                 break;
1015                                 case DT_HOSTNAME:
1016                                 {
1017                                         ValueContainerChar* vcc = (ValueContainerChar*)Values[Index].val;
1018                                         this->ValidateHostname(vi.GetString(), Values[Index].tag, Values[Index].value);
1019                                         vcc->Set(vi.GetString(), strlen(vi.GetString()) + 1);
1020                                 }
1021                                 break;
1022                                 case DT_IPADDRESS:
1023                                 {
1024                                         ValueContainerChar* vcc = (ValueContainerChar*)Values[Index].val;
1025                                         this->ValidateIP(vi.GetString(), Values[Index].tag, Values[Index].value, allow_wild);
1026                                         vcc->Set(vi.GetString(), strlen(vi.GetString()) + 1);
1027                                 }
1028                                 break;
1029                                 case DT_CHANNEL:
1030                                 {
1031                                         ValueContainerChar* vcc = (ValueContainerChar*)Values[Index].val;
1032                                         if (*(vi.GetString()) && !ServerInstance->IsChannel(vi.GetString(), MAXBUF))
1033                                         {
1034                                                 ServerInstance->Threads->Unlock();
1035                                                 throw CoreException("The value of <"+std::string(Values[Index].tag)+":"+Values[Index].value+"> is not a valid channel name");
1036                                         }
1037                                         vcc->Set(vi.GetString(), strlen(vi.GetString()) + 1);
1038                                 }
1039                                 break;
1040                                 case DT_CHARPTR:
1041                                 {
1042                                         ValueContainerChar* vcc = (ValueContainerChar*)Values[Index].val;
1043                                         /* Make sure we also copy the null terminator */
1044                                         vcc->Set(vi.GetString(), strlen(vi.GetString()) + 1);
1045                                 }
1046                                 break;
1047                                 case DT_INTEGER:
1048                                 {
1049                                         int val = vi.GetInteger();
1050                                         ValueContainerInt* vci = (ValueContainerInt*)Values[Index].val;
1051                                         vci->Set(&val, sizeof(int));
1052                                 }
1053                                 break;
1054                                 case DT_BOOLEAN:
1055                                 {
1056                                         bool val = vi.GetBool();
1057                                         ValueContainerBool* vcb = (ValueContainerBool*)Values[Index].val;
1058                                         vcb->Set(&val, sizeof(bool));
1059                                 }
1060                                 break;
1061                                 default:
1062                                         /* You don't want to know what happens if someones bad code sends us here. */
1063                                 break;
1064                         }
1065                         /* We're done with this now */
1066                         delete Values[Index].val;
1067                         ServerInstance->Threads->Unlock();
1068                 }
1069
1070                 /* Read the multiple-tag items (class tags, connect tags, etc)
1071                  * and call the callbacks associated with them. We have three
1072                  * callbacks for these, a 'start', 'item' and 'end' callback.
1073                  */
1074                 for (int Index = 0; MultiValues[Index].tag; ++Index)
1075                 {
1076                         ServerInstance->Threads->Lock();
1077                         MultiValues[Index].init_function(this, MultiValues[Index].tag);
1078                         ServerInstance->Threads->Unlock();
1079
1080                         int number_of_tags = ConfValueEnum(newconfig, MultiValues[Index].tag);
1081
1082                         for (int tagnum = 0; tagnum < number_of_tags; ++tagnum)
1083                         {
1084                                 ValueList vl;
1085                                 for (int valuenum = 0; (MultiValues[Index].items[valuenum]) && (valuenum < MAX_VALUES_PER_TAG); ++valuenum)
1086                                 {
1087                                         int dt = MultiValues[Index].datatype[valuenum];
1088                                         bool allow_newlines =  ((dt & DT_ALLOW_NEWLINE) > 0);
1089                                         bool allow_wild = ((dt & DT_ALLOW_WILD) > 0);
1090                                         dt &= ~DT_ALLOW_NEWLINE;
1091                                         dt &= ~DT_ALLOW_WILD;
1092
1093                                         ServerInstance->Threads->Lock();
1094                                         /* We catch and rethrow any exception here just so we can free our mutex
1095                                          */
1096                                         try
1097                                         {
1098                                                 switch (dt)
1099                                                 {
1100                                                         case DT_NOSPACES:
1101                                                         {
1102                                                                 char item[MAXBUF];
1103                                                                 if (ConfValue(newconfig, MultiValues[Index].tag, MultiValues[Index].items[valuenum], MultiValues[Index].items_default[valuenum], tagnum, item, MAXBUF, allow_newlines))
1104                                                                         vl.push_back(ValueItem(item));
1105                                                                 else
1106                                                                         vl.push_back(ValueItem(""));
1107                                                                 this->ValidateNoSpaces(vl[vl.size()-1].GetString(), MultiValues[Index].tag, MultiValues[Index].items[valuenum]);
1108                                                         }
1109                                                         break;
1110                                                         case DT_HOSTNAME:
1111                                                         {
1112                                                                 char item[MAXBUF];
1113                                                                 if (ConfValue(newconfig, MultiValues[Index].tag, MultiValues[Index].items[valuenum], MultiValues[Index].items_default[valuenum], tagnum, item, MAXBUF, allow_newlines))
1114                                                                         vl.push_back(ValueItem(item));
1115                                                                 else
1116                                                                         vl.push_back(ValueItem(""));
1117                                                                 this->ValidateHostname(vl[vl.size()-1].GetString(), MultiValues[Index].tag, MultiValues[Index].items[valuenum]);
1118                                                         }
1119                                                         break;
1120                                                         case DT_IPADDRESS:
1121                                                         {
1122                                                                 char item[MAXBUF];
1123                                                                 if (ConfValue(newconfig, MultiValues[Index].tag, MultiValues[Index].items[valuenum], MultiValues[Index].items_default[valuenum], tagnum, item, MAXBUF, allow_newlines))
1124                                                                         vl.push_back(ValueItem(item));
1125                                                                 else
1126                                                                         vl.push_back(ValueItem(""));
1127                                                                 this->ValidateIP(vl[vl.size()-1].GetString(), MultiValues[Index].tag, MultiValues[Index].items[valuenum], allow_wild);
1128                                                         }
1129                                                         break;
1130                                                         case DT_CHANNEL:
1131                                                         {
1132                                                                 char item[MAXBUF];
1133                                                                 if (ConfValue(newconfig, MultiValues[Index].tag, MultiValues[Index].items[valuenum], MultiValues[Index].items_default[valuenum], tagnum, item, MAXBUF, allow_newlines))
1134                                                                         vl.push_back(ValueItem(item));
1135                                                                 else
1136                                                                         vl.push_back(ValueItem(""));
1137                                                                 if (!ServerInstance->IsChannel(vl[vl.size()-1].GetString(), MAXBUF))
1138                                                                         throw CoreException("The value of <"+std::string(MultiValues[Index].tag)+":"+MultiValues[Index].items[valuenum]+"> number "+ConvToStr(tagnum + 1)+" is not a valid channel name");
1139                                                         }
1140                                                         break;
1141                                                         case DT_CHARPTR:
1142                                                         {
1143                                                                 char item[MAXBUF];
1144                                                                 if (ConfValue(newconfig, MultiValues[Index].tag, MultiValues[Index].items[valuenum], MultiValues[Index].items_default[valuenum], tagnum, item, MAXBUF, allow_newlines))
1145                                                                         vl.push_back(ValueItem(item));
1146                                                                 else
1147                                                                         vl.push_back(ValueItem(""));
1148                                                         }
1149                                                         break;
1150                                                         case DT_INTEGER:
1151                                                         {
1152                                                                 int item = 0;
1153                                                                 if (ConfValueInteger(newconfig, MultiValues[Index].tag, MultiValues[Index].items[valuenum], MultiValues[Index].items_default[valuenum], tagnum, item))
1154                                                                         vl.push_back(ValueItem(item));
1155                                                                 else
1156                                                                         vl.push_back(ValueItem(0));
1157                                                         }
1158                                                         break;
1159                                                         case DT_BOOLEAN:
1160                                                         {
1161                                                                 bool item = ConfValueBool(newconfig, MultiValues[Index].tag, MultiValues[Index].items[valuenum], MultiValues[Index].items_default[valuenum], tagnum);
1162                                                                 vl.push_back(ValueItem(item));
1163                                                         }
1164                                                         break;
1165                                                         default:
1166                                                                 /* Someone was smoking craq if we got here, and we're all gonna die. */
1167                                                         break;
1168                                                 }
1169                                         }
1170                                         catch (CoreException &e)
1171                                         {
1172                                                 ServerInstance->Threads->Unlock();
1173                                                 throw e;
1174                                         }
1175                                         ServerInstance->Threads->Unlock();
1176                                 }
1177                                 MultiValues[Index].validation_function(this, MultiValues[Index].tag, (char**)MultiValues[Index].items, vl, MultiValues[Index].datatype);
1178                         }
1179                         MultiValues[Index].finish_function(this, MultiValues[Index].tag);
1180                 }
1181
1182                 /* Finalise the limits, increment them all by one so that we can just put assign(str, 0, val)
1183                  * rather than assign(str, 0, val + 1)
1184                  */
1185                 Limits.Finalise();
1186
1187         }
1188
1189         catch (CoreException &ce)
1190         {
1191                 ReportConfigError(ce.GetReason(), bail, useruid);
1192                 return;
1193         }
1194
1195         ServerInstance->Threads->Lock();
1196         for (int i = 0; i < ConfValueEnum(newconfig, "type"); ++i)
1197         {
1198                 char item[MAXBUF], classn[MAXBUF], classes[MAXBUF];
1199                 std::string classname;
1200                 ConfValue(newconfig, "type", "classes", "", i, classes, MAXBUF, false);
1201                 irc::spacesepstream str(classes);
1202                 ConfValue(newconfig, "type", "name", "", i, item, MAXBUF, false);
1203                 while (str.GetToken(classname))
1204                 {
1205                         std::string lost;
1206                         bool foundclass = false;
1207                         for (int j = 0; j < ConfValueEnum(newconfig, "class"); ++j)
1208                         {
1209                                 ConfValue(newconfig, "class", "name", "", j, classn, MAXBUF, false);
1210                                 if (!strcmp(classn, classname.c_str()))
1211                                 {
1212                                         foundclass = true;
1213                                         break;
1214                                 }
1215                         }
1216                         if (!foundclass)
1217                         {
1218                                 if (!useruid.empty())
1219                                 {
1220                                         User* user = ServerInstance->FindNick(useruid);
1221                                         if (user)
1222                                                 user->WriteServ("NOTICE %s :*** Warning: Oper type '%s' has a missing class named '%s', this does nothing!", user->nick.c_str(), item, classname.c_str());
1223                                 }
1224                                 else
1225                                 {
1226                                         if (bail)
1227                                                 printf("Warning: Oper type '%s' has a missing class named '%s', this does nothing!\n", item, classname.c_str());
1228                                         else
1229                                                 ServerInstance->SNO->WriteToSnoMask('A', "Warning: Oper type '%s' has a missing class named '%s', this does nothing!", item, classname.c_str());
1230                                 }
1231                         }
1232                 }
1233         }
1234
1235         /* If we succeeded, set the ircd config to the new one */
1236         this->config_data = newconfig;
1237
1238         ServerInstance->Threads->Unlock();
1239
1240         // write once here, to try it out and make sure its ok
1241         ServerInstance->WritePID(this->PID);
1242
1243         /* Switch over logfiles */
1244         ServerInstance->Logs->CloseLogs();
1245         ServerInstance->Logs->OpenFileLogs();
1246
1247         ServerInstance->Logs->Log("CONFIG", DEFAULT, "Done reading configuration file.");
1248
1249         /* If we're rehashing, let's load any new modules, and unload old ones
1250          */
1251         if (!bail)
1252         {
1253                 int found_ports = 0;
1254                 FailedPortList pl;
1255                 ServerInstance->BindPorts(false, found_ports, pl);
1256
1257                 if (pl.size() && !useruid.empty())
1258                 {
1259                         ServerInstance->Threads->Lock();
1260                         User* user = ServerInstance->FindNick(useruid);
1261                         if (user)
1262                         {
1263                                 user->WriteServ("NOTICE %s :*** Not all your client ports could be bound.", user->nick.c_str());
1264                                 user->WriteServ("NOTICE %s :*** The following port(s) failed to bind:", user->nick.c_str());
1265                                 int j = 1;
1266                                 for (FailedPortList::iterator i = pl.begin(); i != pl.end(); i++, j++)
1267                                 {
1268                                         user->WriteServ("NOTICE %s :*** %d.   Address: %s        Reason: %s", user->nick.c_str(), j, i->first.empty() ? "<all>" : i->first.c_str(), i->second.c_str());
1269                                 }
1270                         }
1271                         ServerInstance->Threads->Unlock();
1272                 }
1273
1274                 ServerInstance->Threads->Lock();
1275                 if (!removed_modules.empty())
1276                 {
1277                         for (std::vector<std::string>::iterator removing = removed_modules.begin(); removing != removed_modules.end(); removing++)
1278                         {
1279                                 if (ServerInstance->Modules->Unload(removing->c_str()))
1280                                 {
1281                                         ServerInstance->SNO->WriteToSnoMask('A', "*** REHASH UNLOADED MODULE: %s",removing->c_str());
1282
1283                                         if (!useruid.empty())
1284                                         {
1285                                                 User* user = ServerInstance->FindNick(useruid);
1286                                                 if (user)
1287                                                         user->WriteNumeric(RPL_UNLOADEDMODULE, "%s %s :Module %s successfully unloaded.",user->nick.c_str(), removing->c_str(), removing->c_str());
1288                                         }
1289                                         else
1290                                                 ServerInstance->SNO->WriteToSnoMask('A', "Module %s successfully unloaded.", removing->c_str());
1291
1292                                         rem++;
1293                                 }
1294                                 else
1295                                 {
1296                                         if (!useruid.empty())
1297                                         {
1298                                                 User* user = ServerInstance->FindNick(useruid);
1299                                                 if (user)
1300                                                         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());
1301                                         }
1302                                         else
1303                                                  ServerInstance->SNO->WriteToSnoMask('A', "Failed to unload module %s: %s", removing->c_str(), ServerInstance->Modules->LastError().c_str());
1304                                 }
1305                         }
1306                 }
1307
1308                 if (!added_modules.empty())
1309                 {
1310                         for (std::vector<std::string>::iterator adding = added_modules.begin(); adding != added_modules.end(); adding++)
1311                         {
1312                                 if (ServerInstance->Modules->Load(adding->c_str()))
1313                                 {
1314                                         ServerInstance->SNO->WriteToSnoMask('A', "*** REHASH LOADED MODULE: %s",adding->c_str());
1315                                         if (!useruid.empty())
1316                                         {
1317                                                 User* user = ServerInstance->FindNick(useruid);
1318                                                 if (user)
1319                                                         user->WriteNumeric(RPL_LOADEDMODULE, "%s %s :Module %s successfully loaded.",user->nick.c_str(), adding->c_str(), adding->c_str());
1320                                         }
1321                                         else
1322                                                 ServerInstance->SNO->WriteToSnoMask('A', "Module %s successfully loaded.", adding->c_str());
1323
1324                                         add++;
1325                                 }
1326                                 else
1327                                 {
1328                                         if (!useruid.empty())
1329                                         {
1330                                                 User* user = ServerInstance->FindNick(useruid);
1331                                                 if (user)
1332                                                         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());
1333                                         }
1334                                         else
1335                                                 ServerInstance->SNO->WriteToSnoMask('A', "Failed to load module %s: %s", adding->c_str(), ServerInstance->Modules->LastError().c_str());
1336                                 }
1337                         }
1338                 }
1339
1340                 ServerInstance->Logs->Log("CONFIG", DEFAULT, "Successfully unloaded %lu of %lu modules and loaded %lu of %lu modules.",(unsigned long)rem,(unsigned long)removed_modules.size(),(unsigned long)add,(unsigned long)added_modules.size());
1341
1342                 ServerInstance->Threads->Unlock();
1343
1344         }
1345
1346         if (bail)
1347         {
1348                 /** Note: This is safe, the method checks for user == NULL */
1349                 ServerInstance->Threads->Lock();
1350                 User* user = NULL;
1351                 if (!useruid.empty())
1352                         user = ServerInstance->FindNick(useruid);
1353                 ServerInstance->Parser->SetupCommandTable(user);
1354                 ServerInstance->Threads->Unlock();
1355         }
1356         else
1357         {
1358                 if (!useruid.empty())
1359                 {
1360                         User* user = ServerInstance->FindNick(useruid);
1361                         if (user)
1362                                 user->WriteServ("NOTICE %s :*** Successfully rehashed server.", user->nick.c_str());
1363                 }
1364                 else
1365                         ServerInstance->SNO->WriteToSnoMask('A', "*** Successfully rehashed server.");
1366         }
1367
1368 }
1369
1370
1371 bool ServerConfig::LoadConf(ConfigDataHash &target, FILE* &conf, const char* filename, std::ostringstream &errorstream)
1372 {
1373         std::string line;
1374         char ch;
1375         long linenumber = 1;
1376         long last_successful_parse = 1;
1377         bool in_tag;
1378         bool in_quote;
1379         bool in_comment;
1380         int character_count = 0;
1381
1382         in_tag = false;
1383         in_quote = false;
1384         in_comment = false;
1385
1386         ServerInstance->Logs->Log("CONFIG", DEBUG, "Reading %s", filename);
1387
1388         /* Check if the file open failed first */
1389         if (!conf)
1390         {
1391                 errorstream << "LoadConf: Couldn't open config file: " << filename << std::endl;
1392                 return false;
1393         }
1394
1395         for (unsigned int t = 0; t < include_stack.size(); t++)
1396         {
1397                 if (std::string(filename) == include_stack[t])
1398                 {
1399                         errorstream << "File " << filename << " is included recursively (looped inclusion)." << std::endl;
1400                         return false;
1401                 }
1402         }
1403
1404         /* It's not already included, add it to the list of files we've loaded */
1405         include_stack.push_back(filename);
1406
1407         /* Start reading characters... */
1408         while ((ch = fgetc(conf)) != EOF)
1409         {
1410                 /*
1411                  * Fix for moronic windows issue spotted by Adremelech.
1412                  * Some windows editors save text files as utf-16, which is
1413                  * a total pain in the ass to parse. Users should save in the
1414                  * right config format! If we ever see a file where the first
1415                  * byte is 0xFF or 0xFE, or the second is 0xFF or 0xFE, then
1416                  * this is most likely a utf-16 file. Bail out and insult user.
1417                  */
1418                 if ((character_count++ < 2) && (ch == '\xFF' || ch == '\xFE'))
1419                 {
1420                         errorstream << "File " << filename << " cannot be read, as it is encoded in braindead UTF-16. Save your file as plain ASCII!" << std::endl;
1421                         return false;
1422                 }
1423
1424                 /*
1425                  * Here we try and get individual tags on separate lines,
1426                  * this would be so easy if we just made people format
1427                  * their config files like that, but they don't so...
1428                  * We check for a '<' and then know the line is over when
1429                  * we get a '>' not inside quotes. If we find two '<' and
1430                  * no '>' then die with an error.
1431                  */
1432
1433                 if ((ch == '#') && !in_quote)
1434                         in_comment = true;
1435
1436                 switch (ch)
1437                 {
1438                         case '\n':
1439                                 if (in_quote)
1440                                         line += '\n';
1441                                 linenumber++;
1442                         case '\r':
1443                                 if (!in_quote)
1444                                         in_comment = false;
1445                         case '\0':
1446                                 continue;
1447                         case '\t':
1448                                 ch = ' ';
1449                 }
1450
1451                 if(in_comment)
1452                         continue;
1453
1454                 /* XXX: Added by Brain, May 1st 2006 - Escaping of characters.
1455                  * Note that this WILL NOT usually allow insertion of newlines,
1456                  * because a newline is two characters long. Use it primarily to
1457                  * insert the " symbol.
1458                  *
1459                  * Note that this also involves a further check when parsing the line,
1460                  * which can be found below.
1461                  */
1462                 if ((ch == '\\') && (in_quote) && (in_tag))
1463                 {
1464                         line += ch;
1465                         char real_character;
1466                         if (!feof(conf))
1467                         {
1468                                 real_character = fgetc(conf);
1469                                 if (real_character == 'n')
1470                                         real_character = '\n';
1471                                 line += real_character;
1472                                 continue;
1473                         }
1474                         else
1475                         {
1476                                 errorstream << "End of file after a \\, what did you want to escape?: " << filename << ":" << linenumber << std::endl;
1477                                 return false;
1478                         }
1479                 }
1480
1481                 if (ch != '\r')
1482                         line += ch;
1483
1484                 if ((ch != '<') && (!in_tag) && (!in_comment) && (ch > ' ') && (ch != 9))
1485                 {
1486                         errorstream << "You have stray characters beyond the tag which starts at " << filename << ":" << last_successful_parse << std::endl;
1487                         return false;
1488                 }
1489
1490                 if (ch == '<')
1491                 {
1492                         if (in_tag)
1493                         {
1494                                 if (!in_quote)
1495                                 {
1496                                         errorstream << "The tag at location " << filename << ":" << last_successful_parse << " was valid, but there is an error in the tag which comes after it. You are possibly missing a \" or >. Please check this." << std::endl;
1497                                         return false;
1498                                 }
1499                         }
1500                         else
1501                         {
1502                                 if (in_quote)
1503                                 {
1504                                         errorstream << "Parser error: Inside a quote but not within the last valid tag, which was opened at: " << filename << ":" << last_successful_parse << std::endl;
1505                                         return false;
1506                                 }
1507                                 else
1508                                 {
1509                                         // errorstream << "Opening new config tag on line " << linenumber << std::endl;
1510                                         in_tag = true;
1511                                 }
1512                         }
1513                 }
1514                 else if (ch == '"')
1515                 {
1516                         if (in_tag)
1517                         {
1518                                 if (in_quote)
1519                                 {
1520                                         // errorstream << "Closing quote in config tag on line " << linenumber << std::endl;
1521                                         in_quote = false;
1522                                 }
1523                                 else
1524                                 {
1525                                         // errorstream << "Opening quote in config tag on line " << linenumber << std::endl;
1526                                         in_quote = true;
1527                                 }
1528                         }
1529                         else
1530                         {
1531                                 if (in_quote)
1532                                 {
1533                                         errorstream << "The tag immediately after the one at " << filename << ":" << last_successful_parse << " has a missing closing \" symbol. Please check this." << std::endl;
1534                                 }
1535                                 else
1536                                 {
1537                                         errorstream << "You have opened a quote (\") beyond the tag at " << filename << ":" << last_successful_parse << " without opening a new tag. Please check this." << std::endl;
1538                                 }
1539                         }
1540                 }
1541                 else if (ch == '>')
1542                 {
1543                         if (!in_quote)
1544                         {
1545                                 if (in_tag)
1546                                 {
1547                                         // errorstream << "Closing config tag on line " << linenumber << std::endl;
1548                                         in_tag = false;
1549         
1550                                         /*
1551                                          * If this finds an <include> then ParseLine can simply call
1552                                          * LoadConf() and load the included config into the same ConfigDataHash
1553                                          */
1554                                         long bl = linenumber;
1555                                         if (!this->ParseLine(target, filename, line, linenumber, errorstream))
1556                                                 return false;
1557                                         last_successful_parse = linenumber;
1558
1559                                         linenumber = bl;
1560         
1561                                         line.clear();
1562                                 }
1563                                 else
1564                                 {
1565                                         errorstream << "You forgot to close the tag which comes immediately after the one at " << filename << ":" << last_successful_parse << std::endl;
1566                                         return false;
1567                                 }
1568                         }
1569                 }
1570         }
1571
1572         /* Fix for bug #392 - if we reach the end of a file and we are still in a quote or comment, most likely the user fucked up */
1573         if (in_comment || in_quote)
1574         {
1575                 errorstream << "Reached end of file whilst still inside a quoted section or tag. This is most likely an error or there \
1576                         is a newline missing from the end of the file: " << filename << ":" << linenumber << std::endl;
1577         }
1578
1579         return true;
1580 }
1581
1582
1583 bool ServerConfig::LoadConf(ConfigDataHash &target, FILE* &conf, const std::string &filename, std::ostringstream &errorstream)
1584 {
1585         return this->LoadConf(target, conf, filename.c_str(), errorstream);
1586 }
1587
1588 bool ServerConfig::ParseLine(ConfigDataHash &target, const std::string &filename, std::string &line, long &linenumber, std::ostringstream &errorstream)
1589 {
1590         std::string tagname;
1591         std::string current_key;
1592         std::string current_value;
1593         KeyValList results;
1594         char last_char = 0;
1595         bool got_name;
1596         bool got_key;
1597         bool in_quote;
1598
1599         got_name = got_key = in_quote = false;
1600
1601         for(std::string::iterator c = line.begin(); c != line.end(); c++)
1602         {
1603                 if (!got_name)
1604                 {
1605                         /* We don't know the tag name yet. */
1606
1607                         if (*c != ' ')
1608                         {
1609                                 if (*c != '<')
1610                                 {
1611                                         if ((*c >= 'a' && *c <= 'z') || (*c >= 'A' && *c <='Z') || (*c >= '0' && *c <= '9') || *c == '_')
1612                                                 tagname += *c;
1613                                         else
1614                                         {
1615                                                 errorstream << "Invalid character in value name of tag: '" << *c << "' in value '" << tagname << "' in filename: " << filename << ":" << linenumber << std::endl;
1616                                                 return false;
1617                                         }
1618                                 }
1619                         }
1620                         else
1621                         {
1622                                 /* We got to a space, we should have the tagname now. */
1623                                 if(tagname.length())
1624                                 {
1625                                         got_name = true;
1626                                 }
1627                         }
1628                 }
1629                 else
1630                 {
1631                         /* We have the tag name */
1632                         if (!got_key)
1633                         {
1634                                 /* We're still reading the key name */
1635                                 if ((*c != '=') && (*c != '>'))
1636                                 {
1637                                         if (*c != ' ')
1638                                         {
1639                                                 if ((*c >= 'a' && *c <= 'z') || (*c >= 'A' && *c <='Z') || (*c >= '0' && *c <= '9') || *c == '_')
1640                                                         current_key += *c;
1641                                                 else
1642                                                 {
1643                                                         errorstream << "Invalid character in key: '" << *c << "' in key '" << current_key << "' in filename: " << filename << ":" << linenumber << std::endl;
1644                                                         return false;
1645                                                 }
1646                                         }
1647                                 }
1648                                 else
1649                                 {
1650                                         /* We got an '=', end of the key name. */
1651                                         got_key = true;
1652                                 }
1653                         }
1654                         else
1655                         {
1656                                 /* We have the key name, now we're looking for quotes and the value */
1657
1658                                 /* Correctly handle escaped characters here.
1659                                  * See the XXX'ed section above.
1660                                  */
1661                                 if ((*c == '\\') && (in_quote))
1662                                 {
1663                                         c++;
1664                                         if (*c == 'n')
1665                                                 current_value += '\n';
1666                                         else
1667                                                 current_value += *c;
1668                                         continue;
1669                                 }
1670                                 else if ((*c == '\\') && (!in_quote))
1671                                 {
1672                                         errorstream << "You can't have an escape sequence outside of a quoted section: " << filename << ":" << linenumber << std::endl;
1673                                         return false;
1674                                 }
1675                                 else if ((*c == '\n') && (in_quote))
1676                                 {
1677                                         /* Got a 'real' \n, treat it as part of the value */
1678                                         current_value += '\n';
1679                                         continue;
1680                                 }
1681                                 else if ((*c == '\r') && (in_quote))
1682                                 {
1683                                         /* Got a \r, drop it */
1684                                         continue;
1685                                 }
1686
1687                                 if (*c == '"')
1688                                 {
1689                                         if (!in_quote)
1690                                         {
1691                                                 /* We're not already in a quote. */
1692                                                 in_quote = true;
1693                                         }
1694                                         else
1695                                         {
1696                                                 /* Leaving the quotes, we have the current value */
1697                                                 results.push_back(KeyVal(current_key, current_value));
1698
1699                                                 // std::cout << "<" << tagname << ":" << current_key << "> " << current_value << std::endl;
1700
1701                                                 in_quote = false;
1702                                                 got_key = false;
1703
1704                                                 if ((tagname == "include") && (current_key == "file"))
1705                                                 {       
1706                                                         if (!this->DoInclude(target, current_value, errorstream))
1707                                                                 return false;
1708                                                 }
1709                                                 else if ((tagname == "include") && (current_key == "executable"))
1710                                                 {
1711                                                         /* Pipe an executable and use its stdout as config data */
1712                                                         if (!this->DoPipe(target, current_value, errorstream))
1713                                                                 return false;
1714                                                 }
1715
1716                                                 current_key.clear();
1717                                                 current_value.clear();
1718                                         }
1719                                 }
1720                                 else
1721                                 {
1722                                         if (in_quote)
1723                                         {
1724                                                 last_char = *c;
1725                                                 current_value += *c;
1726                                         }
1727                                 }
1728                         }
1729                 }
1730         }
1731
1732         /* Finished parsing the tag, add it to the config hash */
1733         target.insert(std::pair<std::string, KeyValList > (tagname, results));
1734
1735         return true;
1736 }
1737
1738 bool ServerConfig::DoPipe(ConfigDataHash &target, const std::string &file, std::ostringstream &errorstream)
1739 {
1740         FILE* conf = popen(file.c_str(), "r");
1741         bool ret = false;
1742
1743         if (conf)
1744         {
1745                 ret = LoadConf(target, conf, file.c_str(), errorstream);
1746                 pclose(conf);
1747         }
1748         else
1749                 errorstream << "Couldn't execute: " << file << std::endl;
1750
1751         return ret;
1752 }
1753
1754 bool ServerConfig::StartsWithWindowsDriveLetter(const std::string &path)
1755 {
1756         return (path.length() > 2 && isalpha(path[0]) && path[1] == ':');
1757 }
1758
1759 bool ServerConfig::DoInclude(ConfigDataHash &target, const std::string &file, std::ostringstream &errorstream)
1760 {
1761         std::string confpath;
1762         std::string newfile;
1763         std::string::size_type pos;
1764
1765         confpath = ServerInstance->ConfigFileName;
1766         newfile = file;
1767
1768         std::replace(newfile.begin(),newfile.end(),'\\','/');
1769         std::replace(confpath.begin(),confpath.end(),'\\','/');
1770
1771         if ((newfile[0] != '/') && (!StartsWithWindowsDriveLetter(newfile)))
1772         {
1773                 if((pos = confpath.rfind("/")) != std::string::npos)
1774                 {
1775                         /* Leaves us with just the path */
1776                         newfile = confpath.substr(0, pos) + std::string("/") + newfile;
1777                 }
1778                 else
1779                 {
1780                         errorstream << "Couldn't get config path from: " << ServerInstance->ConfigFileName << std::endl;
1781                         return false;
1782                 }
1783         }
1784
1785         FILE* conf = fopen(newfile.c_str(), "r");
1786         bool ret = false;
1787
1788         if (conf)
1789         {
1790                 ret = LoadConf(target, conf, newfile, errorstream);
1791                 fclose(conf);
1792         }
1793         else
1794                 errorstream << "Couldn't open config file: " << file << std::endl;
1795
1796         return ret;
1797 }
1798
1799 bool ServerConfig::ConfValue(ConfigDataHash &target, const char* tag, const char* var, int index, char* result, int length, bool allow_linefeeds)
1800 {
1801         return ConfValue(target, tag, var, "", index, result, length, allow_linefeeds);
1802 }
1803
1804 bool ServerConfig::ConfValue(ConfigDataHash &target, const char* tag, const char* var, const char* default_value, int index, char* result, int length, bool allow_linefeeds)
1805 {
1806         std::string value;
1807         bool r = ConfValue(target, std::string(tag), std::string(var), std::string(default_value), index, value, allow_linefeeds);
1808         strlcpy(result, value.c_str(), length);
1809         return r;
1810 }
1811
1812 bool ServerConfig::ConfValue(ConfigDataHash &target, const std::string &tag, const std::string &var, int index, std::string &result, bool allow_linefeeds)
1813 {
1814         return ConfValue(target, tag, var, "", index, result, allow_linefeeds);
1815 }
1816
1817 bool ServerConfig::ConfValue(ConfigDataHash &target, const std::string &tag, const std::string &var, const std::string &default_value, int index, std::string &result, bool allow_linefeeds)
1818 {
1819         ConfigDataHash::size_type pos = index;
1820         if (pos < target.count(tag))
1821         {
1822                 ConfigDataHash::iterator iter = target.find(tag);
1823
1824                 for(int i = 0; i < index; i++)
1825                         iter++;
1826
1827                 for(KeyValList::iterator j = iter->second.begin(); j != iter->second.end(); j++)
1828                 {
1829                         if(j->first == var)
1830                         {
1831                                 if ((!allow_linefeeds) && (j->second.find('\n') != std::string::npos))
1832                                 {
1833                                         ServerInstance->Logs->Log("CONFIG",DEFAULT, "Value of <" + tag + ":" + var+ "> contains a linefeed, and linefeeds in this value are not permitted -- stripped to spaces.");
1834                                         for (std::string::iterator n = j->second.begin(); n != j->second.end(); n++)
1835                                                 if (*n == '\n')
1836                                                         *n = ' ';
1837                                 }
1838                                 else
1839                                 {
1840                                         result = j->second;
1841                                         return true;
1842                                 }
1843                         }
1844                 }
1845                 if (!default_value.empty())
1846                 {
1847                         result = default_value;
1848                         return true;
1849                 }
1850         }
1851         else if (pos == 0)
1852         {
1853                 if (!default_value.empty())
1854                 {
1855                         result = default_value;
1856                         return true;
1857                 }
1858         }
1859         return false;
1860 }
1861
1862 bool ServerConfig::ConfValueInteger(ConfigDataHash &target, const char* tag, const char* var, int index, int &result)
1863 {
1864         return ConfValueInteger(target, std::string(tag), std::string(var), "", index, result);
1865 }
1866
1867 bool ServerConfig::ConfValueInteger(ConfigDataHash &target, const char* tag, const char* var, const char* default_value, int index, int &result)
1868 {
1869         return ConfValueInteger(target, std::string(tag), std::string(var), std::string(default_value), index, result);
1870 }
1871
1872 bool ServerConfig::ConfValueInteger(ConfigDataHash &target, const std::string &tag, const std::string &var, int index, int &result)
1873 {
1874         return ConfValueInteger(target, tag, var, "", index, result);
1875 }
1876
1877 bool ServerConfig::ConfValueInteger(ConfigDataHash &target, const std::string &tag, const std::string &var, const std::string &default_value, int index, int &result)
1878 {
1879         std::string value;
1880         std::istringstream stream;
1881         bool r = ConfValue(target, tag, var, default_value, index, value);
1882         stream.str(value);
1883         if(!(stream >> result))
1884                 return false;
1885         else
1886         {
1887                 if (!value.empty())
1888                 {
1889                         if (value.substr(0,2) == "0x")
1890                         {
1891                                 char* endptr;
1892
1893                                 value.erase(0,2);
1894                                 result = strtol(value.c_str(), &endptr, 16);
1895
1896                                 /* No digits found */
1897                                 if (endptr == value.c_str())
1898                                         return false;
1899                         }
1900                         else
1901                         {
1902                                 char denominator = *(value.end() - 1);
1903                                 switch (toupper(denominator))
1904                                 {
1905                                         case 'K':
1906                                                 /* Kilobytes -> bytes */
1907                                                 result = result * 1024;
1908                                         break;
1909                                         case 'M':
1910                                                 /* Megabytes -> bytes */
1911                                                 result = result * 1024 * 1024;
1912                                         break;
1913                                         case 'G':
1914                                                 /* Gigabytes -> bytes */
1915                                                 result = result * 1024 * 1024 * 1024;
1916                                         break;
1917                                 }
1918                         }
1919                 }
1920         }
1921         return r;
1922 }
1923
1924
1925 bool ServerConfig::ConfValueBool(ConfigDataHash &target, const char* tag, const char* var, int index)
1926 {
1927         return ConfValueBool(target, std::string(tag), std::string(var), "", index);
1928 }
1929
1930 bool ServerConfig::ConfValueBool(ConfigDataHash &target, const char* tag, const char* var, const char* default_value, int index)
1931 {
1932         return ConfValueBool(target, std::string(tag), std::string(var), std::string(default_value), index);
1933 }
1934
1935 bool ServerConfig::ConfValueBool(ConfigDataHash &target, const std::string &tag, const std::string &var, int index)
1936 {
1937         return ConfValueBool(target, tag, var, "", index);
1938 }
1939
1940 bool ServerConfig::ConfValueBool(ConfigDataHash &target, const std::string &tag, const std::string &var, const std::string &default_value, int index)
1941 {
1942         std::string result;
1943         if(!ConfValue(target, tag, var, default_value, index, result))
1944                 return false;
1945
1946         return ((result == "yes") || (result == "true") || (result == "1"));
1947 }
1948
1949 int ServerConfig::ConfValueEnum(ConfigDataHash &target, const char* tag)
1950 {
1951         return target.count(tag);
1952 }
1953
1954 int ServerConfig::ConfValueEnum(ConfigDataHash &target, const std::string &tag)
1955 {
1956         return target.count(tag);
1957 }
1958
1959 int ServerConfig::ConfVarEnum(ConfigDataHash &target, const char* tag, int index)
1960 {
1961         return ConfVarEnum(target, std::string(tag), index);
1962 }
1963
1964 int ServerConfig::ConfVarEnum(ConfigDataHash &target, const std::string &tag, int index)
1965 {
1966         ConfigDataHash::size_type pos = index;
1967
1968         if (pos < target.count(tag))
1969         {
1970                 ConfigDataHash::const_iterator iter = target.find(tag);
1971
1972                 for(int i = 0; i < index; i++)
1973                         iter++;
1974
1975                 return iter->second.size();
1976         }
1977
1978         return 0;
1979 }
1980
1981 /** Read the contents of a file located by `fname' into a file_cache pointed at by `F'.
1982  */
1983 bool ServerConfig::ReadFile(file_cache &F, const char* fname)
1984 {
1985         if (!fname || !*fname)
1986                 return false;
1987
1988         FILE* file = NULL;
1989         char linebuf[MAXBUF];
1990
1991         F.clear();
1992
1993         if ((*fname != '/') && (*fname != '\\') && (!StartsWithWindowsDriveLetter(fname)))
1994         {
1995                 std::string::size_type pos;
1996                 std::string confpath = ServerInstance->ConfigFileName;
1997                 std::string newfile = fname;
1998
1999                 if (((pos = confpath.rfind("/"))) != std::string::npos)
2000                         newfile = confpath.substr(0, pos) + std::string("/") + fname;
2001                 else if (((pos = confpath.rfind("\\"))) != std::string::npos)
2002                         newfile = confpath.substr(0, pos) + std::string("\\") + fname;
2003
2004                 ServerInstance->Logs->Log("config", DEBUG, "Filename: %s", newfile.c_str());
2005
2006                 if (!FileExists(newfile.c_str()))
2007                         return false;
2008                 file =  fopen(newfile.c_str(), "r");
2009         }
2010         else
2011         {
2012                 if (!FileExists(fname))
2013                         return false;
2014                 file =  fopen(fname, "r");
2015         }
2016
2017         if (file)
2018         {
2019                 while (!feof(file))
2020                 {
2021                         if (fgets(linebuf, sizeof(linebuf), file))
2022                                 linebuf[strlen(linebuf)-1] = 0;
2023                         else
2024                                 *linebuf = 0;
2025
2026                         F.push_back(*linebuf ? linebuf : " ");
2027                 }
2028
2029                 fclose(file);
2030         }
2031         else
2032                 return false;
2033
2034         return true;
2035 }
2036
2037 bool ServerConfig::FileExists(const char* file)
2038 {
2039         struct stat sb;
2040         if (stat(file, &sb) == -1)
2041                 return false;
2042
2043         if ((sb.st_mode & S_IFDIR) > 0)
2044                 return false;
2045              
2046         FILE *input;
2047         if ((input = fopen (file, "r")) == NULL)
2048                 return false;
2049         else
2050         {
2051                 fclose(input);
2052                 return true;
2053         }
2054 }
2055
2056 char* ServerConfig::CleanFilename(char* name)
2057 {
2058         char* p = name + strlen(name);
2059         while ((p != name) && (*p != '/') && (*p != '\\')) p--;
2060         return (p != name ? ++p : p);
2061 }
2062
2063
2064 bool ServerConfig::DirValid(const char* dirandfile)
2065 {
2066 #ifdef WINDOWS
2067         return true;
2068 #else
2069
2070         char work[1024];
2071         char buffer[1024];
2072         char otherdir[1024];
2073         int p;
2074
2075         strlcpy(work, dirandfile, 1024);
2076         p = strlen(work);
2077
2078         // we just want the dir
2079         while (*work)
2080         {
2081                 if (work[p] == '/')
2082                 {
2083                         work[p] = '\0';
2084                         break;
2085                 }
2086
2087                 work[p--] = '\0';
2088         }
2089
2090         // Get the current working directory
2091         if (getcwd(buffer, 1024 ) == NULL )
2092                 return false;
2093
2094         if (chdir(work) == -1)
2095                 return false;
2096
2097         if (getcwd(otherdir, 1024 ) == NULL )
2098                 return false;
2099
2100         if (chdir(buffer) == -1)
2101                 return false;
2102
2103         size_t t = strlen(work);
2104
2105         if (strlen(otherdir) >= t)
2106         {
2107                 otherdir[t] = '\0';
2108                 if (!strcmp(otherdir,work))
2109                 {
2110                         return true;
2111                 }
2112
2113                 return false;
2114         }
2115         else
2116         {
2117                 return false;
2118         }
2119 #endif
2120 }
2121
2122 std::string ServerConfig::GetFullProgDir()
2123 {
2124         char buffer[PATH_MAX];
2125 #ifdef WINDOWS
2126         /* Windows has specific api calls to get the exe path that never fail.
2127          * For once, windows has something of use, compared to the POSIX code
2128          * for this, this is positively neato.
2129          */
2130         if (GetModuleFileName(NULL, buffer, MAX_PATH))
2131         {
2132                 std::string fullpath = buffer;
2133                 std::string::size_type n = fullpath.rfind("\\inspircd.exe");
2134                 return std::string(fullpath, 0, n);
2135         }
2136 #else
2137         // Get the current working directory
2138         if (getcwd(buffer, PATH_MAX))
2139         {
2140                 std::string remainder = this->argv[0];
2141
2142                 /* Does argv[0] start with /? its a full path, use it */
2143                 if (remainder[0] == '/')
2144                 {
2145                         std::string::size_type n = remainder.rfind("/inspircd");
2146                         return std::string(remainder, 0, n);
2147                 }
2148
2149                 std::string fullpath = std::string(buffer) + "/" + remainder;
2150                 std::string::size_type n = fullpath.rfind("/inspircd");
2151                 return std::string(fullpath, 0, n);
2152         }
2153 #endif
2154         return "/";
2155 }
2156
2157 InspIRCd* ServerConfig::GetInstance()
2158 {
2159         return ServerInstance;
2160 }
2161
2162 std::string ServerConfig::GetSID()
2163 {
2164         return sid;
2165 }
2166
2167 ValueItem::ValueItem(int value)
2168 {
2169         std::stringstream n;
2170         n << value;
2171         v = n.str();
2172 }
2173
2174 ValueItem::ValueItem(bool value)
2175 {
2176         std::stringstream n;
2177         n << value;
2178         v = n.str();
2179 }
2180
2181 ValueItem::ValueItem(const char* value)
2182 {
2183         v = value;
2184 }
2185
2186 void ValueItem::Set(const char* value)
2187 {
2188         v = value;
2189 }
2190
2191 void ValueItem::Set(int value)
2192 {
2193         std::stringstream n;
2194         n << value;
2195         v = n.str();
2196 }
2197
2198 int ValueItem::GetInteger()
2199 {
2200         if (v.empty())
2201                 return 0;
2202         return atoi(v.c_str());
2203 }
2204
2205 char* ValueItem::GetString()
2206 {
2207         return (char*)v.c_str();
2208 }
2209
2210 bool ValueItem::GetBool()
2211 {
2212         return (GetInteger() || v == "yes" || v == "true");
2213 }
2214
2215
2216
2217
2218 /*
2219  * XXX should this be in a class? -- w00t
2220  */
2221 bool InitTypes(ServerConfig* conf, const char*)
2222 {
2223         if (conf->opertypes.size())
2224         {
2225                 for (opertype_t::iterator n = conf->opertypes.begin(); n != conf->opertypes.end(); n++)
2226                 {
2227                         if (n->second)
2228                                 delete[] n->second;
2229                 }
2230         }
2231
2232         conf->opertypes.clear();
2233         return true;
2234 }
2235
2236 /*
2237  * XXX should this be in a class? -- w00t
2238  */
2239 bool InitClasses(ServerConfig* conf, const char*)
2240 {
2241         if (conf->operclass.size())
2242         {
2243                 for (operclass_t::iterator n = conf->operclass.begin(); n != conf->operclass.end(); n++)
2244                 {
2245                         if (n->second.commandlist)
2246                                 delete[] n->second.commandlist;
2247                         if (n->second.cmodelist)
2248                                 delete[] n->second.cmodelist;
2249                         if (n->second.umodelist)
2250                                 delete[] n->second.umodelist;
2251                         if (n->second.privs)
2252                                 delete[] n->second.privs;
2253                 }
2254         }
2255
2256         conf->operclass.clear();
2257         return true;
2258 }
2259
2260 /*
2261  * XXX should this be in a class? -- w00t
2262  */
2263 bool DoType(ServerConfig* conf, const char*, char**, ValueList &values, int*)
2264 {
2265         const char* TypeName = values[0].GetString();
2266         const char* Classes = values[1].GetString();
2267
2268         conf->opertypes[TypeName] = strnewdup(Classes);
2269         return true;
2270 }
2271
2272 /*
2273  * XXX should this be in a class? -- w00t
2274  */
2275 bool DoClass(ServerConfig* conf, const char* tag, char**, ValueList &values, int*)
2276 {
2277         const char* ClassName = values[0].GetString();
2278         const char* CommandList = values[1].GetString();
2279         const char* UModeList = values[2].GetString();
2280         const char* CModeList = values[3].GetString();
2281         const char *PrivsList = values[4].GetString();
2282
2283         for (const char* c = UModeList; *c; ++c)
2284         {
2285                 if ((*c < 'A' || *c > 'z') && *c != '*')
2286                 {
2287                         throw CoreException("Character " + std::string(1, *c) + " is not a valid mode in <class:usermodes>");
2288                 }
2289         }
2290         for (const char* c = CModeList; *c; ++c)
2291         {
2292                 if ((*c < 'A' || *c > 'z') && *c != '*')
2293                 {
2294                         throw CoreException("Character " + std::string(1, *c) + " is not a valid mode in <class:chanmodes>");
2295                 }
2296         }
2297
2298         conf->operclass[ClassName].commandlist = strnewdup(CommandList);
2299         conf->operclass[ClassName].umodelist = strnewdup(UModeList);
2300         conf->operclass[ClassName].cmodelist = strnewdup(CModeList);
2301         conf->operclass[ClassName].privs = strnewdup(PrivsList);
2302         return true;
2303 }
2304
2305 /*
2306  * XXX should this be in a class? -- w00t
2307  */
2308 bool DoneClassesAndTypes(ServerConfig*, const char*)
2309 {
2310         return true;
2311 }
2312
2313
2314
2315 bool InitXLine(ServerConfig* conf, const char* tag)
2316 {
2317         return true;
2318 }
2319
2320 bool DoZLine(ServerConfig* conf, const char* tag, char** entries, ValueList &values, int* types)
2321 {
2322         const char* reason = values[0].GetString();
2323         const char* ipmask = values[1].GetString();
2324
2325         ZLine* zl = new ZLine(conf->GetInstance(), conf->GetInstance()->Time(), 0, "<Config>", reason, ipmask);
2326         if (!conf->GetInstance()->XLines->AddLine(zl, NULL))
2327                 delete zl;
2328
2329         return true;
2330 }
2331
2332 bool DoQLine(ServerConfig* conf, const char* tag, char** entries, ValueList &values, int* types)
2333 {
2334         const char* reason = values[0].GetString();
2335         const char* nick = values[1].GetString();
2336
2337         QLine* ql = new QLine(conf->GetInstance(), conf->GetInstance()->Time(), 0, "<Config>", reason, nick);
2338         if (!conf->GetInstance()->XLines->AddLine(ql, NULL))
2339                 delete ql;
2340
2341         return true;
2342 }
2343
2344 bool DoKLine(ServerConfig* conf, const char* tag, char** entries, ValueList &values, int* types)
2345 {
2346         const char* reason = values[0].GetString();
2347         const char* host = values[1].GetString();
2348
2349         XLineManager* xlm = conf->GetInstance()->XLines;
2350
2351         IdentHostPair ih = xlm->IdentSplit(host);
2352
2353         KLine* kl = new KLine(conf->GetInstance(), conf->GetInstance()->Time(), 0, "<Config>", reason, ih.first.c_str(), ih.second.c_str());
2354         if (!xlm->AddLine(kl, NULL))
2355                 delete kl;
2356         return true;
2357 }
2358
2359 bool DoELine(ServerConfig* conf, const char* tag, char** entries, ValueList &values, int* types)
2360 {
2361         const char* reason = values[0].GetString();
2362         const char* host = values[1].GetString();
2363
2364         XLineManager* xlm = conf->GetInstance()->XLines;
2365
2366         IdentHostPair ih = xlm->IdentSplit(host);
2367
2368         ELine* el = new ELine(conf->GetInstance(), conf->GetInstance()->Time(), 0, "<Config>", reason, ih.first.c_str(), ih.second.c_str());
2369         if (!xlm->AddLine(el, NULL))
2370                 delete el;
2371         return true;
2372 }
2373
2374 // this should probably be moved to configreader, but atm it relies on CheckELines above.
2375 bool DoneELine(ServerConfig* conf, const char* tag)
2376 {
2377         for (std::vector<User*>::const_iterator u2 = conf->GetInstance()->Users->local_users.begin(); u2 != conf->GetInstance()->Users->local_users.end(); u2++)
2378         {
2379                 User* u = (User*)(*u2);
2380                 u->exempt = false;
2381         }
2382
2383         conf->GetInstance()->XLines->CheckELines();
2384         return true;
2385 }
2386
2387 void ConfigReaderThread::Run()
2388 {
2389         ServerInstance->Config->Read(do_bail, TheUserUID);
2390         ServerInstance->Threads->Lock();
2391         this->SetExitFlag();
2392         ServerInstance->Threads->Unlock();
2393 }
2394