config.cpp

Go to the documentation of this file.
00001 /* Configuration file handling.
00002  *
00003  * (C) 2003-2013 Anope Team
00004  * Contact us at team@anope.org
00005  *
00006  * Please read COPYING and README for further details.
00007  *
00008  * Based on the original code of Epona by Lara.
00009  * Based on the original code of Services by Andy Church.
00010  *
00011  */
00012 
00013 #include "services.h"
00014 #include "config.h"
00015 #include "bots.h"
00016 #include "access.h"
00017 #include "opertype.h"
00018 #include "channels.h"
00019 #include "hashcomp.h"
00020 
00021 #ifndef _WIN32
00022 #include <errno.h>
00023 #include <sys/types.h>
00024 #include <pwd.h>
00025 #include <grp.h>
00026 #endif
00027 
00028 /*************************************************************************/
00029 
00030 ConfigurationFile ServicesConf("services.conf", false); // Services configuration file name
00031 ServerConfig *Config = NULL;
00032 
00033 static Anope::string UlineServers;
00034 static Anope::string BSDefaults;
00035 static Anope::string CSDefaults;
00036 static Anope::string NSDefaults;
00037 
00038 /*************************************************************************/
00039 
00040 ServerConfig::ServerConfig()
00041 {
00042         this->Read();
00043 
00044         if (NSDefaults.empty())
00045         {
00046                 this->NSDefFlags.insert("SECURE");
00047                 this->NSDefFlags.insert("MEMO_SIGNON");
00048                 this->NSDefFlags.insert("MEMO_RECEIVE");
00049         }
00050         else if (!NSDefaults.equals_ci("none"))
00051         {
00052                 spacesepstream options(NSDefaults);
00053                 Anope::string option;
00054                 while (options.GetToken(option))
00055                 {
00056                         if (option.equals_ci("msg"))
00057                         {
00058                                 if (!this->UsePrivmsg)
00059                                         Log() << "msg in <nickserv:defaults> can only be used when options:useprivmsg is set";
00060                                 else
00061                                         this->NSDefFlags.insert(option.upper());
00062                         }
00063                         else
00064                                 this->NSDefFlags.insert(option.upper());
00065                 }
00066         }
00067 
00068         if (this->CSDefBantype < 0 || this->CSDefBantype > 3)
00069         {
00070                 throw ConfigException("Value of CSDefBantype must be between 0 and 3 included");
00071         }
00072 
00073         if (CSDefaults.empty())
00074         {
00075                 this->CSDefFlags.insert("KEEPTOPIC");
00076                 this->CSDefFlags.insert("SECURE");
00077                 this->CSDefFlags.insert("SECUREFOUNDER");
00078                 this->CSDefFlags.insert("SIGNKICK");
00079         }
00080         else if (!CSDefaults.equals_ci("none"))
00081         {
00082                 spacesepstream options(CSDefaults);
00083                 Anope::string option;
00084                 while (options.GetToken(option))
00085                         this->CSDefFlags.insert(option.upper());
00086         }
00087 
00088         if (UseStrictPrivMsg)
00089                 UseStrictPrivMsgString = "/";
00090         else
00091                 UseStrictPrivMsgString ="/msg ";
00092 
00093 
00094         if (!BSDefaults.empty())
00095         {
00096                 spacesepstream options(BSDefaults);
00097                 Anope::string option;
00098                 while (options.GetToken(option))
00099                         this->BSDefFlags.insert("BS_" + option.upper());
00100         }
00101 
00102         /* Ulines */
00103         if (!UlineServers.empty())
00104         {
00105                 this->Ulines.clear();
00106 
00107                 spacesepstream ulines(UlineServers);
00108                 Anope::string uline;
00109                 while (ulines.GetToken(uline))
00110                         this->Ulines.push_back(uline);
00111         }
00112 
00113         if (this->LimitSessions)
00114         {
00115                 if (this->MaxSessionKill && !this->SessionAutoKillExpiry)
00116                         this->SessionAutoKillExpiry = 1800; /* 30 minutes */
00117         }
00118 
00119         /* Check the user keys */
00120         if (this->Seed == 0)
00121                 Log() << "Configuration option options:seed should be set. It's for YOUR safety! Remember that!";
00122 
00123         ModeManager::UpdateDefaultMLock(this);
00124 
00125         if (this->CaseMap == "ascii")
00126                 Anope::casemap = std::locale(std::locale(), new Anope::ascii_ctype<char>());
00127         else if (this->CaseMap == "rfc1459")
00128                 Anope::casemap = std::locale(std::locale(), new Anope::rfc1459_ctype<char>());
00129         else
00130         {
00131                 try
00132                 {
00133                         Anope::casemap = std::locale(this->CaseMap.c_str());
00134                 }
00135                 catch (const std::runtime_error &)
00136                 {
00137                         Log() << "Unknown casemap " << this->CaseMap << " - casemap not changed";
00138                 }
00139         }
00140 
00141         if (this->SessionIPv4CIDR > 32 || this->SessionIPv6CIDR > 128)
00142                 throw ConfigException("Session CIDR value out of range");
00143 
00144 #ifndef _WIN32
00145         if (!this->User.empty())
00146         {
00147                 errno = 0;
00148                 struct passwd *u = getpwnam(this->User.c_str());
00149                 if (u == NULL)
00150                         Log() << "Unable to setuid to " << this->User << ": " << Anope::LastError();
00151                 else if (setuid(u->pw_uid) == -1)
00152                         Log() << "Unable to setuid to " << this->User << ": " << Anope::LastError();
00153                 else
00154                         Log() << "Successfully set user to " << this->User;
00155         }
00156         if (!this->Group.empty())
00157         {
00158                 errno = 0;
00159                 struct group *g = getgrnam(this->Group.c_str());
00160                 if (g == NULL)
00161                         Log() << "Unable to setgid to " << this->Group << ": " << Anope::LastError();
00162                 else if (setuid(g->gr_gid) == -1)
00163                         Log() << "Unable to setgid to " << this->Group << ": " << Anope::LastError();
00164                 else
00165                         Log() << "Successfully set group to " << this->Group;
00166         }
00167 #endif
00168 }
00169 
00170 bool ServerConfig::CheckOnce(const Anope::string &tag)
00171 {
00172         int count = ConfValueEnum(config_data, tag);
00173         if (count > 1)
00174                 throw ConfigException("You have more than one <" + tag + "> tag, this is not permitted.");
00175         if (count < 1)
00176                 throw ConfigException("You have not defined a <" + tag + "> tag, this is required.");
00177         return true;
00178 }
00179 
00180 bool NoValidation(ServerConfig *, const Anope::string &, const Anope::string &, ValueItem &)
00181 {
00182         return true;
00183 }
00184 
00185 void ServerConfig::ValidateNoSpaces(const Anope::string &p, const Anope::string &tag, const Anope::string &val) const
00186 {
00187         for (Anope::string::const_iterator ptr = p.begin(), end = p.end(); ptr != end; ++ptr)
00188                 if (*ptr == ' ')
00189                         throw ConfigException("The value of <" + tag + ":" + val + "> cannot contain spaces");
00190 }
00191 
00192 /* 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,
00193  * even in LastError(). They just return 'yes' or 'no' to an address without such detail as to whats WRONG with the address.
00194  * Because ircd users arent as technical as they used to be (;)) we are going to give more of a useful error message.
00195  */
00196 void ServerConfig::ValidateIP(const Anope::string &p, const Anope::string &tag, const Anope::string &val, bool wild) const
00197 {
00198         int num_dots = 0, num_seps = 0;
00199         bool not_numbers = false, not_hex = false;
00200 
00201         if (!p.empty())
00202         {
00203                 if (p[0] == '.')
00204                         throw ConfigException("The value of <" + tag + ":" + val + "> is not an IP address");
00205 
00206                 for (Anope::string::const_iterator ptr = p.begin(), end = p.end(); ptr != end; ++ptr)
00207                 {
00208                         if (wild && (*ptr == '*' || *ptr == '?' || *ptr == '/'))
00209                                 continue;
00210 
00211                         if (*ptr != ':' && *ptr != '.' && (*ptr < '0' || *ptr > '9'))
00212                         {
00213                                 not_numbers = true;
00214                                 if (toupper(*ptr) < 'A' || toupper(*ptr) > 'F')
00215                                         not_hex = true;
00216                         }
00217                         switch (*ptr)
00218                         {
00219                                 case ' ':
00220                                         throw ConfigException("The value of <" + tag + ":" + val + "> is not an IP address");
00221                                 case '.':
00222                                         ++num_dots;
00223                                         break;
00224                                 case ':':
00225                                         ++num_seps;
00226                         }
00227                 }
00228                 if (num_dots > 3)
00229                         throw ConfigException("The value of <" + tag + ":" + val + "> is an IPv4 address with too many fields!");
00230 
00231                 if (num_seps > 8)
00232                         throw ConfigException("The value of <" + tag + ":" + val + "> is an IPv6 address with too many fields!");
00233 
00234                 if (!num_seps && num_dots < 3 && !wild)
00235                         throw ConfigException("The value of <" + tag + ":" + val + "> looks to be a malformed IPv4 address");
00236 
00237                 if (!num_seps && num_dots == 3 && not_numbers)
00238                         throw ConfigException("The value of <" + tag + ":" + val + "> contains non-numeric characters in an IPv4 address");
00239 
00240                 if (num_seps && not_hex)
00241                         throw ConfigException("The value of <" + tag + ":" + val + "> contains non-hexdecimal characters in an IPv6 address");
00242 
00243                 if (num_seps && num_dots != 3 && num_dots && !wild)
00244                         throw ConfigException("The value of <" + tag + ":" + val + "> is a malformed IPv6 4in6 address");
00245         }
00246 }
00247 
00248 void ServerConfig::ValidateHostname(const Anope::string &p, const Anope::string &tag, const Anope::string &val) const
00249 {
00250         if (p.equals_ci("localhost"))
00251                 return;
00252 
00253         int num_dots = 0, num_seps = 0;
00254         if (!p.empty())
00255         {
00256                 if (p[0] == '.')
00257                         throw ConfigException("The value of <" + tag + ":" + val + "> is not a valid hostname");
00258                 for (unsigned i = 0, end = p.length(); i < end; ++i)
00259                 {
00260                         switch (p[i])
00261                         {
00262                                 case ' ':
00263                                         throw ConfigException("The value of <" + tag + ":" + val + "> is not a valid hostname");
00264                                 case '.':
00265                                         ++num_dots;
00266                                         break;
00267                                 case ':':
00268                                         ++num_seps;
00269                                         break;
00270                         }
00271                 }
00272                 if (!num_dots && !num_seps)
00273                         throw ConfigException("The value of <" + tag + ":" + val + "> is not a valid hostname");
00274         }
00275 }
00276 
00277 static bool ValidateNotEmpty(ServerConfig *, const Anope::string &tag, const Anope::string &value, ValueItem &data)
00278 {
00279         if (data.GetValue().empty())
00280                 throw ConfigException("The value for <" + tag + ":" + value + "> cannot be empty!");
00281         return true;
00282 }
00283 
00284 static bool ValidateNotZero(ServerConfig *, const Anope::string &tag, const Anope::string &value, ValueItem &data)
00285 {
00286         if (!data.GetInteger() && Anope::DoTime(data.GetValue()) <= 0)
00287                 throw ConfigException("The value for <" + tag + ":" + value + "> must be non-zero!");
00288         return true;
00289 }
00290 
00291 static bool ValidateEmailReg(ServerConfig *config, const Anope::string &tag, const Anope::string &value, ValueItem &data)
00292 {
00293         if (!config->NSRegistration.equals_ci("none") && !config->NSRegistration.equals_ci("disable"))
00294         {
00295                 if (value.equals_ci("unconfirmedexpire"))
00296                 {
00297                         if (!data.GetInteger() && Anope::DoTime(data.GetValue()) <= 0)
00298                                 throw ConfigException("The value for <" + tag + ":" + value + "> must be non-zero when e-mail or admin registration is enabled!");
00299                 }
00300                 else
00301                 {
00302                         if (!data.GetBool())
00303                                 throw ConfigException("The value for <" + tag + ":" + value + "> must be set to yes when e-mail or admin registrations is enabled!");
00304                 }
00305         }
00306         return true;
00307 }
00308 
00309 static bool ValidatePort(ServerConfig *, const Anope::string &tag, const Anope::string &value, ValueItem &data)
00310 {
00311         int port = data.GetInteger();
00312         if (!port)
00313                 return true;
00314         if (port < 1 || port > 65535)
00315                 throw ConfigException("The value for <" + tag + ":" + value + "> is not a value port, it must be between 1 and 65535!");
00316         return true;
00317 }
00318 
00319 static bool ValidateBantype(ServerConfig *, const Anope::string &, const Anope::string &, ValueItem &data)
00320 {
00321         int bantype = data.GetInteger();
00322         if (bantype < 0 || bantype > 3)
00323                 throw ConfigException("The value for <chanserv:defbantype> must be between 0 and 3!");
00324         return true;
00325 }
00326 
00327 static bool ValidateNickServ(ServerConfig *config, const Anope::string &tag, const Anope::string &value, ValueItem &data)
00328 {
00329         if (!config->NickServ.empty())
00330         {
00331                 if (value.equals_ci("releasetimeout") || value.equals_ci("accessmax") || value.equals_ci("listmax"))
00332                         return ValidateNotZero(config, tag, value, data);
00333                 else if (value.equals_ci("enforceruser") || value.equals_ci("enforcerhost"))
00334                         return ValidateNotEmpty(config, tag, value, data);
00335                 else if (value.equals_ci("guestnickprefix"))
00336                 {
00337                         ValidateNotEmpty(config, tag, value, data);
00338                         if (data.GetValue().length() > 21)
00339                                 throw ConfigException("The value for <nickserv:guestnickprefix> cannot exceed 21 characters in length!");
00340                 }
00341                 else if (value.equals_ci("registration"))
00342                         if (!data.GetValue().equals_ci("none") && !data.GetValue().equals_ci("mail") && !data.GetValue().equals_ci("admin") && !data.GetValue().equals_ci("disable"))
00343                                 throw ConfigException("The value for <nickserv:registration> must be one of \"none\", \"mail\", \"admin\", or \"disable\"");
00344         }
00345         return true;
00346 }
00347 
00348 static bool ValidateChanServ(ServerConfig *config, const Anope::string &tag, const Anope::string &value, ValueItem &data)
00349 {
00350         if (!config->ChanServ.empty())
00351         {
00352                 if ((value.equals_ci("decription") || value.equals_ci("autokickreason")) && data.GetValue().empty())
00353                         throw ConfigException("The value for <" + tag + ":" + value + "> cannot be empty when ChanServ is enabled!");
00354                 else if (value.equals_ci("defbantype"))
00355                         return ValidateBantype(config, tag, value, data);
00356                 else if (value.equals_ci("accessmax") || value.equals_ci("autokickmax") || value.equals_ci("inhabit") || value.equals_ci("listmax"))
00357                         return ValidateNotZero(config, tag, value, data);
00358         }
00359         return true;
00360 }
00361 
00362 static bool ValidateBotServ(ServerConfig *config, const Anope::string &tag, const Anope::string &value, ValueItem &data)
00363 {
00364         if (!config->BotServ.empty())
00365         {
00366                 if (value.equals_ci("badwordsmax") || value.equals_ci("keepdata"))
00367                 {
00368                         if (!data.GetInteger() && Anope::DoTime(data.GetValue()) <= 0)
00369                                 throw ConfigException("The value for <" + tag + ":" + value + "> must be non-zero when BotServ is enabled!");
00370                 }
00371                 else if (value.equals_ci("minusers"))
00372                 {
00373                         if (data.GetInteger() < 0)
00374                                 throw ConfigException("The value for <" + tag + ":" + value + "> must be greater than or equal to zero!");
00375                 }
00376         }
00377         return true;
00378 }
00379 
00380 static bool ValidateLimitSessions(ServerConfig *config, const Anope::string &tag, const Anope::string &value, ValueItem &data)
00381 {
00382         if (config->LimitSessions)
00383         {
00384                 if (value.equals_ci("maxsessionlimit") || value.equals_ci("exceptionexpiry"))
00385                 {
00386                         if (!data.GetInteger() && Anope::DoTime(data.GetValue()) <= 0)
00387                                 throw ConfigException("The value for <" + tag + ":" + value + "> must be non-zero when session limiting is enabled!");
00388                 }
00389         }
00390         return true;
00391 }
00392 
00393 static bool ValidateOperServ(ServerConfig *config, const Anope::string &tag, const Anope::string &value, ValueItem &data)
00394 {
00395         if (!config->OperServ.empty())
00396         {
00397                 if (value.equals_ci("autokillexpiry") || value.equals_ci("chankillexpiry") || value.equals_ci("snlineexpiry") || value.equals_ci("sqlineexpiry"))
00398                         return ValidateNotZero(config, tag, value, data);
00399                 else if (value.equals_ci("maxsessionlimit") || value.equals_ci("exceptionexpiry"))
00400                         return ValidateLimitSessions(config, tag, value, data);
00401         }
00402         return true;
00403 }
00404 
00405 static bool ValidateNickLen(ServerConfig *, const Anope::string &, const Anope::string &, ValueItem &data)
00406 {
00407         int nicklen = data.GetInteger();
00408         if (!nicklen)
00409         {
00410                 Log() << "You have not defined the <networkinfo:nicklen> directive. It is strongly";
00411                 Log() << "adviced that you do configure this correctly in your services.conf";
00412                 data.Set(31);
00413         }
00414         else if (nicklen < 1)
00415         {
00416                 Log() << "<networkinfo:nicklen> has an invalid value; setting to 31";
00417                 data.Set(31);
00418         }
00419         return true;
00420 }
00421 
00422 static bool ValidateMail(ServerConfig *config, const Anope::string &tag, const Anope::string &value, ValueItem &data)
00423 {
00424         if (config->UseMail)
00425         {
00426                 Anope::string check[] = { "sendmailpath", "sendfrom", "registration_subject", "registration_message", "emailchange_subject", "emailchange_message", "memo_subject", "memo_message", "" };
00427                 for (int i = 0; !check[i].empty(); ++i)
00428                         if (value.equals_ci(check[i]))
00429                                 if (data.GetValue().empty())
00430                                         throw ConfigException("The value for <" + tag + ":" + value + "> cannot be empty when e-mail is enabled!");
00431         }
00432         return true;
00433 }
00434 
00435 static bool ValidateGlobalOnCycle(ServerConfig *config, const Anope::string &tag, const Anope::string &value, ValueItem &data)
00436 {
00437         if (config->GlobalOnCycle)
00438         {
00439                 if (data.GetValue().empty())
00440                 {
00441                         Log() << "<" << tag << ":" << value << "> was undefined, disabling <options:globaloncycle>";
00442                         config->GlobalOnCycle = false;
00443                 }
00444         }
00445         return true;
00446 }
00447 
00448 static bool InitUplinks(ServerConfig *config, const Anope::string &)
00449 {
00450         if (!config->Uplinks.empty())
00451         {
00452                 std::vector<ServerConfig::Uplink *>::iterator curr_uplink = config->Uplinks.begin(), end_uplink = config->Uplinks.end();
00453                 for (; curr_uplink != end_uplink; ++curr_uplink)
00454                         delete *curr_uplink;
00455         }
00456         config->Uplinks.clear();
00457         return true;
00458 }
00459 
00460 static bool DoUplink(ServerConfig *config, const Anope::string &, const Anope::string *, ValueList &values, int *)
00461 {
00462         // Validation variables
00463         Anope::string host = values[0].GetValue(), password = values[3].GetValue();
00464         int port = values[2].GetInteger();
00465         bool ipv6 = values[1].GetBool();
00466         ValueItem vi_host(host), vi_port(port), vi_password(password);
00467         // Validate the host to make sure it is not empty
00468         if (!ValidateNotEmpty(config, "uplink", "host", vi_host))
00469                 throw ConfigException("One or more values in your configuration file failed to validate. Please see your log for more information.");
00470         // Validate the port to make sure it is a valid port
00471         if (!ValidatePort(config, "uplink", "port", vi_port))
00472                 throw ConfigException("One or more values in your configuration file failed to validate. Please see your log for more information.");
00473         // Validate the password to make sure it is not empty
00474         if (!ValidateNotEmpty(config, "uplink", "password", vi_password))
00475                 throw ConfigException("One or more values in your configuration file failed to validate. Please see your log for more information.");
00476         // If we get here, all the values are valid, we'll add it to the Uplinks list
00477         config->Uplinks.push_back(new ServerConfig::Uplink(host, port, password, ipv6));
00478         return true;
00479 }
00480 
00481 static bool DoneUplinks(ServerConfig *config, const Anope::string &)
00482 {
00483         if (config->Uplinks.empty())
00484                 throw ConfigException("You must define at least one uplink block!");
00485         return true;
00486 }
00487 
00488 static bool InitOperTypes(ServerConfig *config, const Anope::string &)
00489 {
00490         for (std::list<OperType *>::iterator it = config->MyOperTypes.begin(), it_end = config->MyOperTypes.end(); it != it_end; ++it)
00491                 delete *it;
00492 
00493         config->MyOperTypes.clear();
00494         return true;
00495 }
00496 
00497 static bool DoOperType(ServerConfig *config, const Anope::string &, const Anope::string *, ValueList &values, int *)
00498 {
00499         Anope::string name = values[0].GetValue();
00500         Anope::string inherits = values[1].GetValue();
00501         Anope::string commands = values[2].GetValue();
00502         Anope::string privs = values[3].GetValue();
00503         Anope::string modes = values[4].GetValue();
00504 
00505         ValueItem vi(name);
00506         if (!ValidateNotEmpty(config, "opertype", "name", vi))
00507                 throw ConfigException("One or more values in your configuration file failed to validate. Please see your log for more information.");
00508 
00509         OperType *ot = new OperType(name);
00510         ot->modes = modes;
00511 
00512         Anope::string tok;
00513         spacesepstream cmdstr(commands);
00514         while (cmdstr.GetToken(tok))
00515                 ot->AddCommand(tok);
00516 
00517         spacesepstream privstr(privs);
00518         while (privstr.GetToken(tok))
00519                 ot->AddPriv(tok);
00520 
00521         commasepstream inheritstr(inherits);
00522         while (inheritstr.GetToken(tok))
00523         {
00524                 /* Strip leading ' ' after , */
00525                 if (tok.length() > 1 && tok[0] == ' ')
00526                         tok.erase(tok.begin());
00527                 for (std::list<OperType *>::iterator it = config->MyOperTypes.begin(), it_end = config->MyOperTypes.end(); it != it_end; ++it)
00528                 {
00529                         if ((*it)->GetName().equals_ci(tok))
00530                         {
00531                                 Log() << "Inheriting commands and privs from " << (*it)->GetName() << " to " << ot->GetName();
00532                                 ot->Inherits(*it);
00533                                 break;
00534                         }
00535                 }
00536         }
00537 
00538         config->MyOperTypes.push_back(ot);
00539         return true;
00540 }
00541 
00542 static bool DoneOperTypes(ServerConfig *, const Anope::string &)
00543 {
00544         return true;
00545 }
00546 
00547 /*************************************************************************/
00548 
00549 static bool InitOpers(ServerConfig *config, const Anope::string &)
00550 {
00551         for (nickcore_map::const_iterator it = NickCoreList->begin(), it_end = NickCoreList->end(); it != it_end; ++it)
00552         {
00553                 NickCore *nc = it->second;
00554                 nc->QueueUpdate();
00555                 if (nc->o && nc->o->config)
00556                         nc->o = NULL;
00557         }
00558 
00559         for (unsigned i = 0; i < config->Opers.size(); ++i)
00560                 delete config->Opers[i];
00561         config->Opers.clear();
00562 
00563         return true;
00564 }
00565 
00566 static bool DoOper(ServerConfig *config, const Anope::string &, const Anope::string *, ValueList &values, int *)
00567 {
00568         Anope::string name = values[0].GetValue();
00569         Anope::string type = values[1].GetValue();
00570         bool require_oper = values[2].GetBool();
00571         Anope::string password = values[3].GetValue();
00572         Anope::string certfp = values[4].GetValue();
00573         Anope::string host = values[5].GetValue();
00574         Anope::string vhost = values[6].GetValue();
00575 
00576         ValueItem vi(name);
00577         if (!ValidateNotEmpty(config, "oper", "name", vi))
00578                 throw ConfigException("One or more values in your configuration file failed to validate. Please see your log for more information.");
00579 
00580         ValueItem vi2(type);
00581         if (!ValidateNotEmpty(config, "oper", "type", vi2))
00582                 throw ConfigException("One or more values in your configuration file failed to validate. Please see your log for more information.");
00583         
00584         OperType *ot = NULL;
00585         for (std::list<OperType *>::iterator it = config->MyOperTypes.begin(), it_end = config->MyOperTypes.end(); it != it_end; ++it)
00586                 if ((*it)->GetName() == type)
00587                         ot = *it;
00588         if (ot == NULL)
00589                 throw ConfigException("Oper block for " + name + " has invalid oper type " + type);
00590         
00591         Oper *o = new Oper(name, ot);
00592         o->require_oper = require_oper;
00593         o->config = true;
00594         o->password = password;
00595         o->certfp = certfp;
00596         spacesepstream(host).GetTokens(o->hosts);
00597         o->vhost = vhost;
00598         config->Opers.push_back(o);
00599 
00600         return true;
00601 }
00602 
00603 static bool DoneOpers(ServerConfig *config, const Anope::string &)
00604 {
00605         for (unsigned i = 0; i < config->Opers.size(); ++i)
00606         {
00607                 Oper *o = config->Opers[i];
00608 
00609                 const NickAlias *na = NickAlias::Find(o->name);
00610                 if (!na)
00611                         // Nonexistant nick
00612                         continue;
00613 
00614                 na->nc->o = o;
00615                 Log() << "Tied oper " << na->nc->display << " to type " << o->ot->GetName();
00616         }
00617 
00618         return true;
00619 }
00620 
00621 /*************************************************************************/
00622 
00623 static std::map<Anope::string, Anope::string> defines;
00624 static bool InitDefine(ServerConfig *config, const Anope::string &)
00625 {
00626         defines.clear();
00627         return true;
00628 }
00629 
00630 static bool DoDefine(ServerConfig *config, const Anope::string &, const Anope::string *, ValueList &values, int *)
00631 {
00632         Anope::string name = values[0].GetValue(), value = values[1].GetValue();
00633         defines[name] = value;
00634         return true;
00635 }
00636 
00637 static bool DoneDefine(ServerConfig *config, const Anope::string &)
00638 {
00639         return true;
00640 }
00641 
00642 /*************************************************************************/
00643 
00644 static bool InitInclude(ServerConfig *config, const Anope::string &)
00645 {
00646         return true;
00647 }
00648 
00649 static bool DoInclude(ServerConfig *config, const Anope::string &, const Anope::string *, ValueList &values, int *)
00650 {
00651         Anope::string type = values[0].GetValue();
00652         Anope::string file = values[1].GetValue();
00653 
00654         if (type != "file" && type != "executable")
00655                 throw ConfigException("include:type must be either \"file\" or \"executable\"");
00656         
00657         ConfigurationFile f(file, type == "executable");
00658         config->LoadConf(f);
00659 
00660         return true;
00661 }
00662 
00663 static bool DoneInclude(ServerConfig *config, const Anope::string &)
00664 {
00665         return true;
00666 }
00667 
00668 /*************************************************************************/
00669 
00670 static bool InitModules(ServerConfig *, const Anope::string &)
00671 {
00672         return true;
00673 }
00674 
00675 static bool DoModule(ServerConfig *conf, const Anope::string &, const Anope::string *, ValueList &values, int *)
00676 {
00677         // First we validate that there was a name in the module block
00678         Anope::string module = values[0].GetValue();
00679         ValueItem vi(module);
00680         if (!ValidateNotEmpty(conf, "module", "name", vi))
00681                 throw ConfigException("One or more values in your configuration file failed to validate. Please see your log for more information.");
00682         conf->ModulesAutoLoad.push_back(module);
00683         return true;
00684 }
00685 
00686 static bool DoneModules(ServerConfig *config, const Anope::string &)
00687 {
00688         if (Config)
00689         {
00690                 for (std::list<Anope::string>::iterator it = Config->ModulesAutoLoad.begin(); it != Config->ModulesAutoLoad.end(); ++it)
00691                         if (std::find(config->ModulesAutoLoad.begin(), config->ModulesAutoLoad.end(), *it) == config->ModulesAutoLoad.end())
00692                                 ModuleManager::UnloadModule(ModuleManager::FindModule(*it), NULL);
00693                 for (std::list<Anope::string>::iterator it = config->ModulesAutoLoad.begin(); it != config->ModulesAutoLoad.end(); ++it)
00694                         if (std::find(Config->ModulesAutoLoad.begin(), Config->ModulesAutoLoad.end(), *it) == Config->ModulesAutoLoad.end())
00695                                 ModuleManager::LoadModule(*it, NULL);
00696         }
00697         return true;
00698 }
00699 
00700 static bool InitLogs(ServerConfig *config, const Anope::string &)
00701 {
00702         config->LogInfos.clear();
00703         return true;
00704 }
00705 
00706 static bool DoLogs(ServerConfig *config, const Anope::string &, const Anope::string *, ValueList &values, int *)
00707 {
00708         //{"target", "source", "logage", "inhabit", "admin", "override", "commands", "servers", "channels", "users", "other", "rawio", "debug"},
00709         Anope::string targets = values[0].GetValue();
00710         ValueItem vi(targets);
00711         if (!ValidateNotEmpty(config, "log", "target", vi))
00712                 throw ConfigException("One or more values in your configuration file failed to validate. Please see your log for more information.");
00713         
00714         Anope::string source = values[1].GetValue();
00715         int logage = values[2].GetInteger();
00716         Anope::string admin = values[3].GetValue();
00717         Anope::string override = values[4].GetValue();
00718         Anope::string commands = values[5].GetValue();
00719         Anope::string servers = values[6].GetValue();
00720         Anope::string channels = values[7].GetValue();
00721         Anope::string users = values[8].GetValue();
00722         Anope::string normal = values[9].GetValue();
00723         bool rawio = values[10].GetBool();
00724         bool ldebug = values[11].GetBool();
00725 
00726         LogInfo *l = new LogInfo(logage, rawio, ldebug);
00727         spacesepstream(targets).GetTokens(l->targets);
00728         spacesepstream(source).GetTokens(l->sources);
00729         spacesepstream(admin).GetTokens(l->admin);
00730         spacesepstream(override).GetTokens(l->override);
00731         spacesepstream(commands).GetTokens(l->commands);
00732         spacesepstream(servers).GetTokens(l->servers);
00733         spacesepstream(channels).GetTokens(l->channels);
00734         spacesepstream(users).GetTokens(l->users);
00735         spacesepstream(normal).GetTokens(l->normal);
00736 
00737         config->LogInfos.push_back(l);
00738 
00739         return true;
00740 }
00741 
00742 static bool DoneLogs(ServerConfig *config, const Anope::string &)
00743 {
00744         Log() << "Loaded " << config->LogInfos.size() << " log blocks";
00745         return true;
00746 }
00747 
00748 /*************************************************************************/
00749 
00750 static bool InitCommands(ServerConfig *config, const Anope::string &)
00751 {
00752         for (botinfo_map::const_iterator it = BotListByNick->begin(), it_end = BotListByNick->end(); it != it_end; ++it)
00753         {
00754                 BotInfo *bi = it->second;
00755                 if (bi)
00756                 {
00757                         bi->QueueUpdate();
00758                         bi->commands.clear();
00759                 }
00760         }
00761         return true;
00762 }
00763 
00764 static bool DoCommands(ServerConfig *config, const Anope::string &, const Anope::string *, ValueList &values, int *)
00765 {
00766         Anope::string service = values[0].GetValue();
00767         Anope::string name = values[1].GetValue();
00768         Anope::string command = values[2].GetValue();
00769         Anope::string permission = values[3].GetValue();
00770         Anope::string group = values[4].GetValue();
00771         bool hide = values[5].GetBool();
00772 
00773         ValueItem vi(service);
00774         if (!ValidateNotEmpty(config, "command", "service", vi))
00775                 throw ConfigException("One or more values in your configuration file failed to validate. Please see your log for more information.");
00776         
00777         vi = ValueItem(name);
00778         if (!ValidateNotEmpty(config, "command", "name", vi))
00779                 throw ConfigException("One or more values in your configuration file failed to validate. Please see your log for more information.");
00780         
00781         vi = ValueItem(command);
00782         if (!ValidateNotEmpty(config, "command", "command", vi))
00783                 throw ConfigException("One or more values in your configuration file failed to validate. Please see your log for more information.");
00784         
00785         BotInfo *bi = BotInfo::Find(service);
00786         if (bi == NULL)
00787                 throw ConfigException("Command " + name + " exists for nonexistant service " + service);
00788 
00789         if (bi->commands.count(name))
00790                 throw ConfigException("Command name " + name + " already exists on " + bi->nick);
00791 
00792         CommandInfo &ci = bi->SetCommand(name, command, permission);
00793         ci.group = group;
00794         ci.hide = hide;
00795         return true;
00796 }
00797 
00798 static bool DoneCommands(ServerConfig *config, const Anope::string &)
00799 {
00800         return true;
00801 }
00802 
00803 /*************************************************************************/
00804 
00805 static bool InitPrivileges(ServerConfig *config, const Anope::string &)
00806 {
00807         PrivilegeManager::ClearPrivileges();
00808         return true;
00809 }
00810 
00811 static bool DoPrivileges(ServerConfig *config, const Anope::string &, const Anope::string *, ValueList &values, int *)
00812 {
00813         Anope::string name = values[0].GetValue();
00814         Anope::string desc = values[1].GetValue();
00815         int rank = values[2].GetInteger();
00816 
00817         ValueItem vi(name);
00818         if (!ValidateNotEmpty(config, "privilege", "name", vi))
00819                 throw ConfigException("One or more values in your configuration file failed to validate. Please see your log for more information.");
00820 
00821         vi = ValueItem(desc);
00822         if (!ValidateNotEmpty(config, "privilege", "desc", vi))
00823                 throw ConfigException("One or more values in your configuration file failed to validate. Please see your log for more information.");
00824         
00825         PrivilegeManager::AddPrivilege(Privilege(name, desc, rank));
00826         return true;
00827 }
00828 
00829 static bool DonePrivileges(ServerConfig *config, const Anope::string &)
00830 {
00831         Log(LOG_DEBUG) << "Loaded " << PrivilegeManager::GetPrivileges().size() << " privileges";
00832         return true;
00833 }
00834 
00835 /*************************************************************************/
00836 
00837 static std::set<Anope::string> services;
00838 static bool InitServices(ServerConfig *config, const Anope::string &)
00839 {
00840         services.clear();
00841         return true;
00842 }
00843 
00844 static bool DoServices(ServerConfig *config, const Anope::string &, const Anope::string *, ValueList &values, int *)
00845 {
00846         Anope::string nick = values[0].GetValue();
00847         Anope::string user = values[1].GetValue();
00848         Anope::string host = values[2].GetValue();
00849         Anope::string gecos = values[3].GetValue();
00850         Anope::string modes = values[4].GetValue();
00851         Anope::string channels = values[5].GetValue();
00852 
00853         ValueItem vi(nick);
00854         if (!ValidateNotEmpty(config, "service", "nick", vi))
00855                 throw ConfigException("One or more values in your configuration file failed to validate. Please see your log for more information.");
00856 
00857         vi = ValueItem(user);
00858         if (!ValidateNotEmpty(config, "service", "user", vi))
00859                 throw ConfigException("One or more values in your configuration file failed to validate. Please see your log for more information.");
00860 
00861         vi = ValueItem(host);
00862         if (!ValidateNotEmpty(config, "service", "host", vi))
00863                 throw ConfigException("One or more values in your configuration file failed to validate. Please see your log for more information.");
00864 
00865         vi = ValueItem(gecos);
00866         if (!ValidateNotEmpty(config, "service", "gecos", vi))
00867                 throw ConfigException("One or more values in your configuration file failed to validate. Please see your log for more information.");
00868 
00869         services.insert(nick);
00870         BotInfo* bi = BotInfo::Find(nick);
00871         if (!bi)
00872                 bi = new BotInfo(nick, user, host, gecos, modes);
00873         bi->conf = true;
00874 
00875         Anope::string token;
00876         commasepstream sep(channels);
00877         std::vector<Anope::string> oldchannels = bi->botchannels;
00878         bi->botchannels.clear();
00879         while (sep.GetToken(token))
00880         {
00881                 bi->botchannels.push_back(token);
00882                 size_t ch = token.find('#');
00883                 Anope::string chname, want_modes;
00884                 if (ch == Anope::string::npos)
00885                         chname = token;
00886                 else
00887                 {
00888                         want_modes = token.substr(0, ch);
00889                         chname = token.substr(ch);
00890                 }
00891                 bi->Join(chname);
00892                 Channel *c = Channel::Find(chname);
00893                 if (!c)
00894                         continue; // Can't happen
00895 
00896                 /* Remove all existing modes */
00897                 for (unsigned i = 0; i < ModeManager::ChannelModes.size(); ++i)
00898                 {
00899                         ChannelMode *cm = ModeManager::ChannelModes[i];
00900                         if (cm && cm->type == MODE_STATUS)
00901                                 c->RemoveMode(bi, cm, bi->GetUID());
00902                 }
00903                 /* Set the new modes */
00904                 for (unsigned j = 0; j < want_modes.length(); ++j)
00905                 {
00906                         ChannelMode *cm = ModeManager::FindChannelModeByChar(want_modes[j]);
00907                         if (cm == NULL)
00908                                 cm = ModeManager::FindChannelModeByChar(ModeManager::GetStatusChar(want_modes[j]));
00909                         if (cm && cm->type == MODE_STATUS)
00910                                 c->SetMode(bi, cm, bi->GetUID());
00911                 }
00912         }
00913         for (unsigned i = 0; i < oldchannels.size(); ++i)
00914         {
00915                 size_t ch = oldchannels[i].find('#');
00916                 Anope::string chname = oldchannels[i].substr(ch != Anope::string::npos ? ch : 0);
00917 
00918                 bool found = false;
00919                 for (unsigned j = 0; j < bi->botchannels.size(); ++j)
00920                 {
00921                         ch = bi->botchannels[j].find('#');
00922                         Anope::string ochname = bi->botchannels[j].substr(ch != Anope::string::npos ? ch : 0);
00923 
00924                         if (chname.equals_ci(ochname))
00925                                 found = true;
00926                 }
00927 
00928                 if (found)
00929                         continue;
00930 
00931                 Channel *c = Channel::Find(chname);
00932                 if (c)
00933                         bi->Part(c);
00934         }
00935 
00936         return true;
00937 }
00938 
00939 static bool DoneServices(ServerConfig *config, const Anope::string &)
00940 {
00941         for (botinfo_map::const_iterator it = BotListByNick->begin(), it_end = BotListByNick->end(); it != it_end;)
00942         {
00943                 BotInfo *bi = it->second;
00944                 ++it;
00945 
00946                 if (bi->conf && services.count(bi->nick) == 0)
00947                         delete bi;
00948         }
00949         services.clear();
00950         return true;
00951 }
00952 
00953 /*************************************************************************/
00954 
00955 static bool InitFantasy(ServerConfig *config, const Anope::string &)
00956 {
00957         config->Fantasy.clear();
00958         return true;
00959 }
00960 
00961 static bool DoFantasy(ServerConfig *config, const Anope::string &, const Anope::string *, ValueList &values, int *)
00962 {
00963         Anope::string name = values[0].GetValue();
00964         Anope::string service = values[1].GetValue();
00965         Anope::string permission = values[2].GetValue();
00966         Anope::string group = values[3].GetValue();
00967         bool hide = values[4].GetBool();
00968         bool prepend_channel = values[5].GetBool();
00969 
00970         CommandInfo &c = config->Fantasy[name];
00971 
00972         c.name = service;
00973         c.permission = permission;
00974         c.group = group;
00975         c.hide = hide;
00976         c.prepend_channel = prepend_channel;
00977 
00978         return true;
00979 }
00980 
00981 static bool DoneFantasy(ServerConfig *config, const Anope::string &)
00982 {
00983         return true;
00984 }
00985 
00986 /*************************************************************************/
00987 
00988 static bool InitCommandGroups(ServerConfig *config, const Anope::string &)
00989 {
00990         config->CommandGroups.clear();
00991         return true;
00992 }
00993 
00994 static bool DoCommandGroups(ServerConfig *config, const Anope::string &, const Anope::string *, ValueList &values, int *)
00995 {
00996         Anope::string name = values[0].GetValue();
00997         Anope::string description = values[1].GetValue();
00998 
00999         if (name.empty() || description.empty())
01000                 return true;
01001 
01002         CommandGroup gr;
01003         gr.name = name;
01004         gr.description = description;
01005 
01006         config->CommandGroups.push_back(gr);
01007         return true;
01008 }
01009 
01010 static bool DoneCommandGroups(ServerConfig *config, const Anope::string &)
01011 {
01012         return true;
01013 }
01014 
01015 /*************************************************************************/
01016 
01017 ConfigurationFile::ConfigurationFile(const Anope::string &n, bool e) : name(n), executable(e), fp(NULL)
01018 {
01019 }
01020 
01021 ConfigurationFile::~ConfigurationFile()
01022 {
01023         this->Close();
01024 }
01025 
01026 const Anope::string &ConfigurationFile::GetName() const
01027 {
01028         return this->name;
01029 }
01030 
01031 bool ConfigurationFile::IsOpen() const
01032 {
01033         return this->fp != NULL;
01034 }
01035 
01036 bool ConfigurationFile::Open()
01037 {
01038         this->Close();
01039         this->fp = (this->executable ? popen(this->name.c_str(), "r") : fopen((Anope::ConfigDir + "/" + this->name).c_str(), "r"));
01040         return this->fp != NULL;
01041 }
01042 
01043 void ConfigurationFile::Close()
01044 {
01045         if (this->fp != NULL)
01046         {
01047                 if (this->executable)
01048                         pclose(this->fp);
01049                 else
01050                         fclose(this->fp);
01051                 this->fp = NULL;
01052         }
01053 }
01054 
01055 bool ConfigurationFile::End() const
01056 {
01057         return !this->IsOpen() || feof(this->fp);
01058 }
01059 
01060 Anope::string ConfigurationFile::Read()
01061 {
01062         Anope::string ret;
01063         char buf[BUFSIZE];
01064         while (fgets(buf, sizeof(buf), this->fp) != NULL)
01065         {
01066                 char *nl = strchr(buf, '\n');
01067                 if (nl != NULL)
01068                         *nl = 0;
01069                 else if (!this->End())
01070                 {
01071                         ret += buf;
01072                         continue;
01073                 }
01074 
01075                 ret = buf;
01076                 break;
01077         }
01078 
01079         return ret;
01080 }
01081 
01082 ConfigItems::ConfigItems(ServerConfig *conf)
01083 {
01084         // These tags can occur ONCE or not at all
01085         const Item Items[] = {
01086                 /* The following comments are from CyberBotX to w00t as examples to use:
01087                  *
01088                  * The last argument, for validation, must use one of the functions with the following signature:
01089                  * bool <function>(ServerConfig *, const char *, const char *, ValueItem &)
01090                  * Examples are: NoValidation, ValidateNotEmpty, etc.
01091                  *
01092                  * If you want to create a directive using an integer:
01093                  * int blarg;
01094                  * {"tag", "value", "0", new ValueContainerInt(&conf->blarg), DT_INTEGER, <validation>},
01095                  *
01096                  * If you want to create a directive using an unsigned integer:
01097                  * unsigned blarg;
01098                  * {"tag", "value", "0", new ValueContainerUInt(&conf->blarg), DT_UINTEGER, <validation>},
01099                  *
01100                  * If you want to create a directive using a string:
01101                  * Anope::string blarg;
01102                  * {"tag", "value", "", new ValueContainerString(&conf->blarg), DT_STRING, <validation>},
01103                  *
01104                  * If you want to create a directive using a boolean:
01105                  * bool blarg;
01106                  * {"tag", "value", "no", new ValueContainerBool(&conf->blarg), DT_BOOLEAN, <validation>},
01107                  *
01108                  * If you want to create a directive using a character pointer specifically to hold a hostname (this will call ValidateHostname automatically):
01109                  * char *blarg;
01110                  * {"tag", "value", "", new ValueContainerChar(&conf->blarg), DT_HOSTNAME, <validation>},
01111                  *
01112                  * If you want to create a directive using a character pointer that specifically can not have spaces in it (this will call ValidateNoSpaces automatically):
01113                  * char *blarg;
01114                  * {"tag", "value", "", new ValueContainerChar(&conf->blarg), DT_NOSPACES, <validation>},
01115                  *
01116                  * If you want to create a directive using a character pointer specifically to hold an IP address (this will call ValidateIP automatically):
01117                  * char *blarg;
01118                  * {"tag", "value", "", new ValueContainerChar(&conf->blarg), DT_IPADDRESS, <validation>},
01119                  *
01120                  * If you want to create a directive using a time (a time_t variable converted from a string):
01121                  * time_t blarg;
01122                  * {"tag", "value", "", new ValueContainterTime(&conf->blarg), DT_TIME, <validation>},
01123                  *
01124                  * For the second-to-last argument, you can or (|) in the following values:
01125                  * DT_NORELOAD - The variable can't be changed on a reload of the configuration
01126                  * DT_ALLOW_WILD - Allows wildcards/CIDR in DT_IPADDRESS
01127                  * DT_ALLOW_NEWLINE - Allows new line characters in DT_STRING
01128                  *
01129                  * We may need to add some other validation functions to handle certain things, we can handle that later.
01130                  * Any questions about these, w00t, feel free to ask. */
01131                 {"serverinfo", "name", "", new ValueContainerString(&conf->ServerName), DT_HOSTNAME | DT_NORELOAD, ValidateNotEmpty},
01132                 {"serverinfo", "description", "", new ValueContainerString(&conf->ServerDesc), DT_STRING | DT_NORELOAD, ValidateNotEmpty},
01133                 {"serverinfo", "localhost", "", new ValueContainerString(&conf->LocalHost), DT_HOSTNAME | DT_NORELOAD, NoValidation},
01134                 {"serverinfo", "id", "", new ValueContainerString(&conf->Numeric), DT_NOSPACES | DT_NORELOAD, NoValidation},
01135                 {"serverinfo", "pid", "data/services.pid", new ValueContainerString(&conf->PIDFilename), DT_STRING | DT_NORELOAD, ValidateNotEmpty},
01136                 {"serverinfo", "motd", "conf/services.motd", new ValueContainerString(&conf->MOTDFilename), DT_STRING, ValidateNotEmpty},
01137                 {"networkinfo", "networkname", "", new ValueContainerString(&conf->NetworkName), DT_STRING, ValidateNotEmpty},
01138                 {"networkinfo", "nicklen", "31", new ValueContainerUInt(&conf->NickLen), DT_UINTEGER | DT_NORELOAD, ValidateNickLen},
01139                 {"networkinfo", "userlen", "10", new ValueContainerUInt(&conf->UserLen), DT_UINTEGER | DT_NORELOAD, NoValidation},
01140                 {"networkinfo", "hostlen", "64", new ValueContainerUInt(&conf->HostLen), DT_UINTEGER | DT_NORELOAD, NoValidation},
01141                 {"networkinfo", "chanlen", "32", new ValueContainerUInt(&conf->ChanLen), DT_UINTEGER | DT_NORELOAD, NoValidation},
01142                 {"options", "user", "", new ValueContainerString(&conf->User), DT_STRING, NoValidation},
01143                 {"options", "group", "", new ValueContainerString(&conf->Group), DT_STRING, NoValidation},
01144                 {"options", "casemap", "ascii", new ValueContainerString(&conf->CaseMap), DT_STRING, NoValidation},
01145                 {"options", "passlen", "32", new ValueContainerUInt(&conf->PassLen), DT_UINTEGER | DT_NORELOAD, NoValidation},
01146                 {"options", "seed", "0", new ValueContainerLUInt(&conf->Seed), DT_LUINTEGER, NoValidation},
01147                 {"options", "nobackupokay", "no", new ValueContainerBool(&conf->NoBackupOkay), DT_BOOLEAN, NoValidation},
01148                 {"options", "strictpasswords", "no", new ValueContainerBool(&conf->StrictPasswords), DT_BOOLEAN, NoValidation},
01149                 {"options", "badpasslimit", "0", new ValueContainerUInt(&conf->BadPassLimit), DT_UINTEGER, NoValidation},
01150                 {"options", "badpasstimeout", "0", new ValueContainerTime(&conf->BadPassTimeout), DT_TIME, NoValidation},
01151                 {"options", "updatetimeout", "0", new ValueContainerTime(&conf->UpdateTimeout), DT_TIME, ValidateNotZero},
01152                 {"options", "expiretimeout", "0", new ValueContainerTime(&conf->ExpireTimeout), DT_TIME, ValidateNotZero},
01153                 {"options", "readtimeout", "0", new ValueContainerTime(&conf->ReadTimeout), DT_TIME, ValidateNotZero},
01154                 {"options", "warningtimeout", "0", new ValueContainerTime(&conf->WarningTimeout), DT_TIME, ValidateNotZero},
01155                 {"options", "timeoutcheck", "0", new ValueContainerTime(&conf->TimeoutCheck), DT_TIME, NoValidation},
01156                 {"options", "keepbackups", "0", new ValueContainerInt(&conf->KeepBackups), DT_INTEGER, NoValidation},
01157                 {"options", "forceforbidreason", "no", new ValueContainerBool(&conf->ForceForbidReason), DT_BOOLEAN, NoValidation},
01158                 {"options", "useprivmsg", "no", new ValueContainerBool(&conf->UsePrivmsg), DT_BOOLEAN, NoValidation},
01159                 {"options", "usestrictprivmsg", "no", new ValueContainerBool(&conf->UseStrictPrivMsg), DT_BOOLEAN, NoValidation},
01160                 {"options", "hidestatso", "no", new ValueContainerBool(&conf->HideStatsO), DT_BOOLEAN, NoValidation},
01161                 {"options", "nickregdelay", "0", new ValueContainerUInt(&conf->NickRegDelay), DT_UINTEGER, NoValidation},
01162                 {"options", "restrictopernicks", "no", new ValueContainerBool(&conf->RestrictOperNicks), DT_BOOLEAN, NoValidation},
01163                 {"options", "newscount", "3", new ValueContainerUInt(&conf->NewsCount), DT_UINTEGER, NoValidation},
01164                 {"options", "ulineservers", "", new ValueContainerString(&UlineServers), DT_STRING, NoValidation},
01165                 {"options", "botmodes", "", new ValueContainerString(&conf->BotModes), DT_STRING, NoValidation},
01166                 {"options", "retrywait", "60", new ValueContainerInt(&conf->RetryWait), DT_INTEGER, ValidateNotZero},
01167                 {"options", "hideprivilegedcommands", "yes", new ValueContainerBool(&conf->HidePrivilegedCommands), DT_BOOLEAN, NoValidation},
01168                 {"options", "nonicknameownership", "no", new ValueContainerBool(&conf->NoNicknameOwnership), DT_BOOLEAN | DT_NORELOAD, NoValidation},
01169                 {"options", "regexengine", "", new ValueContainerString(&conf->RegexEngine), DT_STRING, NoValidation},
01170                 {"nickserv", "name", "", new ValueContainerString(&conf->NickServ), DT_STRING, NoValidation},
01171                 {"nickserv", "registration", "none", new ValueContainerString(&conf->NSRegistration), DT_STRING, ValidateNickServ},
01172                 {"nickserv", "unregistered_notice", "", new ValueContainerString(&conf->NSUnregisteredNotice), DT_STRING, NoValidation},
01173                 {"nickserv", "forceemail", "no", new ValueContainerBool(&conf->NSForceEmail), DT_BOOLEAN, ValidateEmailReg},
01174                 {"nickserv", "confirmemailchanges", "no", new ValueContainerBool(&conf->NSConfirmEmailChanges), DT_BOOLEAN, NoValidation},
01175                 {"nickserv", "defaults", "secure memo_signon memo_receive", new ValueContainerString(&NSDefaults), DT_STRING, NoValidation},
01176                 {"nickserv", "languages", "", new ValueContainerString(&conf->Languages), DT_STRING, NoValidation},
01177                 {"nickserv", "defaultlanguage", "0", new ValueContainerString(&conf->NSDefLanguage), DT_STRING, NoValidation},
01178                 {"nickserv", "regdelay", "0", new ValueContainerTime(&conf->NSRegDelay), DT_TIME, NoValidation},
01179                 {"nickserv", "resenddelay", "0", new ValueContainerTime(&conf->NSResendDelay), DT_TIME, NoValidation},
01180                 {"nickserv", "expire", "21d", new ValueContainerTime(&conf->NSExpire), DT_TIME, NoValidation},
01181                 {"nickserv", "suspendexpire", "0", new ValueContainerTime(&conf->NSSuspendExpire), DT_TIME, NoValidation},
01182                 {"nickserv", "unconfirmedexpire", "0", new ValueContainerTime(&conf->NSUnconfirmedExpire), DT_TIME, ValidateEmailReg},
01183                 {"nickserv", "maxaliases", "0", new ValueContainerUInt(&conf->NSMaxAliases), DT_UINTEGER, NoValidation},
01184                 {"nickserv", "accessmax", "0", new ValueContainerUInt(&conf->NSAccessMax), DT_UINTEGER, ValidateNickServ},
01185                 {"nickserv", "enforceruser", "", new ValueContainerString(&conf->NSEnforcerUser), DT_STRING, ValidateNickServ},
01186                 {"nickserv", "enforcerhost", "", new ValueContainerString(&conf->NSEnforcerHost), DT_STRING, ValidateNickServ},
01187                 {"nickserv", "releasetimeout", "0", new ValueContainerTime(&conf->NSReleaseTimeout), DT_TIME, ValidateNickServ},
01188                 {"nickserv", "allowkillimmed", "no", new ValueContainerBool(&conf->NSAllowKillImmed), DT_BOOLEAN | DT_NORELOAD, NoValidation},
01189                 {"nickserv", "nogroupchange", "no", new ValueContainerBool(&conf->NSNoGroupChange), DT_BOOLEAN, NoValidation},
01190                 {"nickserv", "listmax", "0", new ValueContainerUInt(&conf->NSListMax), DT_UINTEGER, ValidateNickServ},
01191                 {"nickserv", "guestnickprefix", "", new ValueContainerString(&conf->NSGuestNickPrefix), DT_STRING, ValidateNickServ},
01192                 {"nickserv", "secureadmins", "no", new ValueContainerBool(&conf->NSSecureAdmins), DT_BOOLEAN, NoValidation},
01193                 {"nickserv", "strictprivileges", "no", new ValueContainerBool(&conf->NSStrictPrivileges), DT_BOOLEAN, NoValidation},
01194                 {"nickserv", "modeonid", "no", new ValueContainerBool(&conf->NSModeOnID), DT_BOOLEAN, NoValidation},
01195                 {"nickserv", "addaccessonreg", "no", new ValueContainerBool(&conf->NSAddAccessOnReg), DT_BOOLEAN, NoValidation},
01196                 {"nickserv", "ajoinmax", "10", new ValueContainerUInt(&conf->AJoinMax), DT_UINTEGER, NoValidation},
01197                 {"nickserv", "kill_quick", "20", new ValueContainerTime(&conf->NSKillQuick), DT_TIME, NoValidation},
01198                 {"nickserv", "kill", "60", new ValueContainerTime(&conf->NSKill), DT_TIME, NoValidation},
01199                 {"nickserv", "modesonid", "", new ValueContainerString(&conf->NSModesOnID), DT_STRING, NoValidation},
01200                 {"nickserv", "restoreonrecover", "yes", new ValueContainerBool(&conf->NSRestoreOnRecover), DT_BOOLEAN, NoValidation},
01201                 {"nickserv", "sasl", "yes", new ValueContainerBool(&conf->NSSASL), DT_BOOLEAN, NoValidation},
01202                 {"nickserv", "hidenetsplitquit", "no", new ValueContainerBool(&conf->NSHideNetSplitQuit), DT_BOOLEAN, NoValidation},
01203                 {"mail", "usemail", "no", new ValueContainerBool(&conf->UseMail), DT_BOOLEAN, ValidateEmailReg},
01204                 {"mail", "sendmailpath", "", new ValueContainerString(&conf->SendMailPath), DT_STRING, ValidateMail},
01205                 {"mail", "sendfrom", "", new ValueContainerString(&conf->SendFrom), DT_STRING, ValidateMail},
01206                 {"mail", "restrict", "no", new ValueContainerBool(&conf->RestrictMail), DT_BOOLEAN, NoValidation},
01207                 {"mail", "delay", "0", new ValueContainerTime(&conf->MailDelay), DT_TIME, NoValidation},
01208                 {"mail", "dontquoteaddresses", "no", new ValueContainerBool(&conf->DontQuoteAddresses), DT_BOOLEAN, NoValidation},
01209                 {"mail", "registration_subject", "", new ValueContainerString(&conf->MailRegistrationSubject), DT_STRING, ValidateMail},
01210                 {"mail", "registration_message", "", new ValueContainerString(&conf->MailRegistrationMessage), DT_STRING | DT_ALLOW_NEWLINE, ValidateMail},
01211                 {"mail", "reset_subject", "", new ValueContainerString(&conf->MailResetSubject), DT_STRING, ValidateMail},
01212                 {"mail", "reset_message", "", new ValueContainerString(&conf->MailResetMessage), DT_STRING | DT_ALLOW_NEWLINE, ValidateMail},
01213                 {"mail", "emailchange_subject", "", new ValueContainerString(&conf->MailEmailchangeSubject), DT_STRING, ValidateMail},
01214                 {"mail", "emailchange_message", "", new ValueContainerString(&conf->MailEmailchangeMessage), DT_STRING | DT_ALLOW_NEWLINE, ValidateMail},
01215                 {"mail", "memo_subject", "", new ValueContainerString(&conf->MailMemoSubject), DT_STRING, ValidateMail},
01216                 {"mail", "memo_message", "", new ValueContainerString(&conf->MailMemoMessage), DT_STRING | DT_ALLOW_NEWLINE, ValidateMail},
01217                 {"chanserv", "name", "", new ValueContainerString(&conf->ChanServ), DT_STRING, NoValidation},
01218                 {"chanserv", "defaults", "keeptopic secure securefounder signkick", new ValueContainerString(&CSDefaults), DT_STRING, ValidateChanServ},
01219                 {"chanserv", "maxregistered", "0", new ValueContainerUInt(&conf->CSMaxReg), DT_UINTEGER, ValidateChanServ},
01220                 {"chanserv", "expire", "14d", new ValueContainerTime(&conf->CSExpire), DT_TIME, ValidateChanServ},
01221                 {"chanserv", "suspendexpire", "0", new ValueContainerTime(&conf->CSSuspendExpire), DT_TIME, NoValidation},
01222                 {"chanserv", "forbidexpire", "0", new ValueContainerTime(&conf->CSForbidExpire), DT_TIME, NoValidation},
01223                 {"chanserv", "defbantype", "2", new ValueContainerInt(&conf->CSDefBantype), DT_INTEGER, ValidateChanServ},
01224                 {"chanserv", "accessmax", "0", new ValueContainerUInt(&conf->CSAccessMax), DT_UINTEGER, ValidateChanServ},
01225                 {"chanserv", "autokickmax", "0", new ValueContainerUInt(&conf->CSAutokickMax), DT_UINTEGER, ValidateChanServ},
01226                 {"chanserv", "autokickreason", "User has been banned from the channel", new ValueContainerString(&conf->CSAutokickReason), DT_STRING, ValidateChanServ},
01227                 {"chanserv", "inhabit", "0", new ValueContainerTime(&conf->CSInhabit), DT_TIME, ValidateChanServ},
01228                 {"chanserv", "listmax", "0", new ValueContainerUInt(&conf->CSListMax), DT_UINTEGER, ValidateChanServ},
01229                 {"chanserv", "opersonly", "no", new ValueContainerBool(&conf->CSOpersOnly), DT_BOOLEAN, ValidateChanServ},
01230                 {"chanserv", "mlock", "+nrt", new ValueContainerString(&conf->MLock), DT_STRING | DT_ALLOW_EMPTY, NoValidation},
01231                 {"chanserv", "nomlock", "", new ValueContainerString(&conf->NoMLock), DT_STRING, NoValidation},
01232                 {"chanserv", "require", "", new ValueContainerString(&conf->CSRequire), DT_STRING, NoValidation},
01233                 {"chanserv", "use_server_side_mlock", "yes", new ValueContainerBool(&conf->UseServerSideMLock), DT_BOOLEAN, NoValidation},
01234                 {"chanserv", "use_server_side_topiclock", "yes", new ValueContainerBool(&conf->UseServerSideTopicLock), DT_BOOLEAN, NoValidation},
01235                 {"chanserv", "reasonmax", "200",  new ValueContainerUInt(&conf->CSReasonMax), DT_UINTEGER, NoValidation},
01236                 {"memoserv", "name", "", new ValueContainerString(&conf->MemoServ), DT_STRING, NoValidation},
01237                 {"memoserv", "maxmemos", "0", new ValueContainerUInt(&conf->MSMaxMemos), DT_UINTEGER, NoValidation},
01238                 {"memoserv", "senddelay", "0", new ValueContainerTime(&conf->MSSendDelay), DT_TIME, NoValidation},
01239                 {"memoserv", "notifyall", "no", new ValueContainerBool(&conf->MSNotifyAll), DT_BOOLEAN, NoValidation},
01240                 {"memoserv", "memoreceipt", "0", new ValueContainerUInt(&conf->MSMemoReceipt), DT_UINTEGER, NoValidation},
01241                 {"hostserv", "name", "", new ValueContainerString(&conf->HostServ), DT_STRING, NoValidation},
01242                 {"hostserv", "vhost_chars", "abcdefghijklmnopqrstuvwxyzABCDEFGHIJMLMNOPQRSTUVWXYZ0123456789.-", new ValueContainerString(&conf->VhostChars), DT_STRING, NoValidation},
01243                 {"hostserv", "allow_undotted_vhosts", "false", new ValueContainerBool(&conf->VhostUndotted), DT_BOOLEAN, NoValidation},
01244                 {"hostserv", "disallow_start_or_end", "", new ValueContainerString(&conf->VhostDisallowBE), DT_STRING, NoValidation},
01245                 {"botserv", "name", "", new ValueContainerString(&conf->BotServ), DT_STRING, NoValidation},
01246                 {"botserv", "defaults", "", new ValueContainerString(&BSDefaults), DT_STRING, NoValidation},
01247                 {"botserv", "minusers", "0", new ValueContainerUInt(&conf->BSMinUsers), DT_UINTEGER, ValidateBotServ},
01248                 {"botserv", "badwordsmax", "0", new ValueContainerUInt(&conf->BSBadWordsMax), DT_UINTEGER, ValidateBotServ},
01249                 {"botserv", "keepdata", "0", new ValueContainerTime(&conf->BSKeepData), DT_TIME, ValidateBotServ},
01250                 {"botserv", "smartjoin", "no", new ValueContainerBool(&conf->BSSmartJoin), DT_BOOLEAN, NoValidation},
01251                 {"botserv", "gentlebadwordreason", "no", new ValueContainerBool(&conf->BSGentleBWReason), DT_BOOLEAN, NoValidation},
01252                 {"botserv", "casesensitive", "no", new ValueContainerBool(&conf->BSCaseSensitive), DT_BOOLEAN, NoValidation},
01253                 {"botserv", "fantasycharacter", "!", new ValueContainerString(&conf->BSFantasyCharacter), DT_STRING | DT_ALLOW_EMPTY, NoValidation},
01254                 {"operserv", "name", "", new ValueContainerString(&conf->OperServ), DT_STRING, NoValidation},
01255                 {"operserv", "superadmin", "no", new ValueContainerBool(&conf->SuperAdmin), DT_BOOLEAN, NoValidation},
01256                 {"operserv", "autokillexpiry", "0", new ValueContainerTime(&conf->AutokillExpiry), DT_TIME, ValidateOperServ},
01257                 {"operserv", "chankillexpiry", "0", new ValueContainerTime(&conf->ChankillExpiry), DT_TIME, ValidateOperServ},
01258                 {"operserv", "snlineexpiry", "0", new ValueContainerTime(&conf->SNLineExpiry), DT_TIME, ValidateOperServ},
01259                 {"operserv", "sqlineexpiry", "0", new ValueContainerTime(&conf->SQLineExpiry), DT_TIME, ValidateOperServ},
01260                 {"operserv", "akillonadd", "no", new ValueContainerBool(&conf->AkillOnAdd), DT_BOOLEAN, NoValidation},
01261                 {"operserv", "killonsnline", "no", new ValueContainerBool(&conf->KillonSNline), DT_BOOLEAN, NoValidation},
01262                 {"operserv", "killonsqline", "no", new ValueContainerBool(&conf->KillonSQline), DT_BOOLEAN, NoValidation},
01263                 {"operserv", "limitsessions", "no", new ValueContainerBool(&conf->LimitSessions), DT_BOOLEAN, NoValidation},
01264                 {"operserv", "defaultsessionlimit", "0", new ValueContainerUInt(&conf->DefSessionLimit), DT_UINTEGER, NoValidation},
01265                 {"operserv", "maxsessionlimit", "0", new ValueContainerUInt(&conf->MaxSessionLimit), DT_UINTEGER, ValidateOperServ},
01266                 {"operserv", "exceptionexpiry", "0", new ValueContainerTime(&conf->ExceptionExpiry), DT_TIME, ValidateOperServ},
01267                 {"operserv", "sessionlimitexceeded", "", new ValueContainerString(&conf->SessionLimitExceeded), DT_STRING, NoValidation},
01268                 {"operserv", "sessionlimitdetailsloc", "", new ValueContainerString(&conf->SessionLimitDetailsLoc), DT_STRING, NoValidation},
01269                 {"operserv", "maxsessionkill", "0", new ValueContainerUInt(&conf->MaxSessionKill), DT_UINTEGER, NoValidation},
01270                 {"operserv", "sessionautokillexpiry", "0", new ValueContainerTime(&conf->SessionAutoKillExpiry), DT_TIME, NoValidation},
01271                 {"operserv", "session_ipv4_cidr", "32", new ValueContainerUInt(&conf->SessionIPv4CIDR), DT_UINTEGER | DT_NORELOAD, NoValidation},
01272                 {"operserv", "session_ipv6_cidr", "128", new ValueContainerUInt(&conf->SessionIPv6CIDR), DT_UINTEGER | DT_NORELOAD, NoValidation},
01273                 {"operserv", "addakiller", "no", new ValueContainerBool(&conf->AddAkiller), DT_BOOLEAN, NoValidation},
01274                 {"operserv", "akillids", "no", new ValueContainerBool(&conf->AkillIds), DT_BOOLEAN, NoValidation},
01275                 {"operserv", "opersonly", "no", new ValueContainerBool(&conf->OSOpersOnly), DT_BOOLEAN, NoValidation},
01276                 {"global", "name", "", new ValueContainerString(&conf->Global), DT_STRING, NoValidation},
01277                 {"global", "globaloncycle", "no", new ValueContainerBool(&conf->GlobalOnCycle), DT_BOOLEAN, NoValidation},
01278                 {"global", "globaloncycledown", "", new ValueContainerString(&conf->GlobalOnCycleMessage), DT_STRING, ValidateGlobalOnCycle},
01279                 {"global", "globaloncycleup", "", new ValueContainerString(&conf->GlobalOnCycleUP), DT_STRING, ValidateGlobalOnCycle},
01280                 {"global", "anonymousglobal", "no", new ValueContainerBool(&conf->AnonymousGlobal), DT_BOOLEAN, NoValidation},
01281                 {"", "", "", NULL, DT_NOTHING, NoValidation}
01282         };
01283 
01284         /* These tags can occur multiple times, and therefore they have special code to read them
01285          * which is different to the code for reading the singular tags listed above. */
01286         MultiItem MultiItems[] = {
01287                 /* Include must be first so we can pull in the extra files before processing
01288                  * anything else! */
01289                 {"include",
01290                         {"type", "name", ""},
01291                         {"", "", ""},
01292                         {DT_STRING, DT_STRING},
01293                         InitInclude, DoInclude, DoneInclude},
01294                 {"define",
01295                         {"name", "value", ""},
01296                         {"", "", ""},
01297                         {DT_STRING, DT_STRING},
01298                         InitDefine, DoDefine, DoneDefine},
01299                 {"uplink",
01300                         {"host", "ipv6", "port", "password", ""},
01301                         {"", "no", "0", "", ""},
01302                         {DT_HOSTNAME | DT_NORELOAD, DT_BOOLEAN | DT_NORELOAD, DT_UINTEGER | DT_NORELOAD, DT_NOSPACES | DT_NORELOAD},
01303                         InitUplinks, DoUplink, DoneUplinks},
01304                 {"module",
01305                         {"name", ""},
01306                         {"", ""},
01307                         {DT_STRING},
01308                         InitModules, DoModule, DoneModules},
01309                 {"opertype",
01310                         {"name", "inherits", "commands", "privs", "modes", ""},
01311                         {"", "", "", "", "", ""},
01312                         {DT_STRING, DT_STRING, DT_STRING, DT_STRING, DT_STRING},
01313                         InitOperTypes, DoOperType, DoneOperTypes},
01314                 {"oper",
01315                         {"name", "type", "require_oper", "password", "certfp", "host", "vhost", ""},
01316                         {"", "", "yes", "", "", "", "", ""},
01317                         {DT_STRING, DT_STRING, DT_BOOLEAN, DT_STRING, DT_STRING, DT_STRING, DT_STRING},
01318                         InitOpers, DoOper, DoneOpers},
01319                 {"service",
01320                         {"nick", "user", "host", "gecos", "modes", "channels", ""},
01321                         {"", "", "", "", "", "", ""},
01322                         {DT_STRING, DT_STRING, DT_STRING, DT_STRING, DT_STRING, DT_STRING},
01323                         InitServices, DoServices, DoneServices},
01324                 {"log",
01325                         {"target", "source", "logage", "admin", "override", "commands", "servers", "channels", "users", "other", "rawio", "debug", ""},
01326                         {"", "", "7", "", "", "", "", "", "", "", "no", "no", ""},
01327                         {DT_STRING, DT_STRING, DT_INTEGER, DT_STRING, DT_STRING, DT_STRING, DT_STRING, DT_STRING, DT_STRING, DT_STRING, DT_BOOLEAN, DT_BOOLEAN},
01328                         InitLogs, DoLogs, DoneLogs},
01329                 {"command",
01330                         {"service", "name", "command", "permission", "group", "hide", ""},
01331                         {"", "", "", "", "", "no", ""},
01332                         {DT_STRING, DT_STRING, DT_STRING, DT_STRING, DT_STRING, DT_BOOLEAN},
01333                         InitCommands, DoCommands, DoneCommands},
01334                 {"privilege",
01335                         {"name", "desc", "rank", ""},
01336                         {"", "", "", ""},
01337                         {DT_STRING, DT_STRING, DT_INTEGER, DT_STRING},
01338                         InitPrivileges, DoPrivileges, DonePrivileges},
01339                 {"fantasy",
01340                         {"name", "command", "permission", "group", "hide", "prepend_channel", ""},
01341                         {"", "", "", "", "no", "yes", ""},
01342                         {DT_STRING, DT_STRING, DT_STRING, DT_STRING, DT_BOOLEAN, DT_BOOLEAN},
01343                         InitFantasy, DoFantasy, DoneFantasy},
01344                 {"command_group",
01345                         {"name", "description", ""},
01346                         {"", "", ""},
01347                         {DT_STRING, DT_STRING},
01348                         InitCommandGroups, DoCommandGroups, DoneCommandGroups},
01349                 {"",
01350                         {""},
01351                         {""},
01352                         {0},
01353                         NULL, NULL, NULL}
01354         };
01355 
01356         this->Values = new Item[sizeof(Items) / sizeof(Item)];
01357         for (unsigned i = 0; i < sizeof(Items) / sizeof(Item); ++i)
01358                 this->Values[i] = Items[i];
01359 
01360         this->MultiValues = new MultiItem[sizeof(MultiItems) / sizeof(MultiItem)];
01361         for (unsigned i = 0; i < sizeof(MultiItems) / sizeof(MultiItem); ++i)
01362                 this->MultiValues[i] = MultiItems[i];
01363 }
01364 
01365 ConfigItems::~ConfigItems()
01366 {
01367         for (unsigned i = 0; !this->Values[i].tag.empty(); ++i)
01368                 delete this->Values[i].val;
01369         delete [] this->Values;
01370         delete [] this->MultiValues;
01371 }
01372 
01373 void ServerConfig::Read()
01374 {
01375         // These tags MUST occur and must ONLY occur once in the config file
01376         static const Anope::string Once[] = {"serverinfo", "networkinfo", "options", ""};
01377 
01378         this->LoadConf(ServicesConf);
01379 
01380         ConfigItems configitems(this);
01381 
01382         /* Read the multiple-tag items (class tags, connect tags, etc)
01383          * and call the callbacks associated with them. We have three
01384          * callbacks for these, a 'start', 'item' and 'end' callback. */
01385         for (int Index = 0; !configitems.MultiValues[Index].tag.empty(); ++Index)
01386         {
01387                 configitems.MultiValues[Index].init_function(this, configitems.MultiValues[Index].tag);
01388                 int number_of_tags = ConfValueEnum(config_data, configitems.MultiValues[Index].tag);
01389                 for (int tagnum = 0; tagnum < number_of_tags; ++tagnum)
01390                 {
01391                         ValueList vl;
01392                         vl.clear();
01393                         for (int valuenum = 0; !configitems.MultiValues[Index].items[valuenum].empty(); ++valuenum)
01394                         {
01395                                 int dt = configitems.MultiValues[Index].datatype[valuenum];
01396                                 bool allow_newlines =  dt & DT_ALLOW_NEWLINE, allow_wild = dt & DT_ALLOW_WILD, noreload = dt & DT_NORELOAD, allow_empty = dt & DT_ALLOW_EMPTY;
01397                                 dt &= ~DT_ALLOW_NEWLINE;
01398                                 dt &= ~DT_ALLOW_WILD;
01399                                 dt &= ~DT_NORELOAD;
01400                                 dt &= ~DT_ALLOW_EMPTY;
01401 
01402                                 ConfigDataHash &hash = (noreload && Config ? Config->config_data : this->config_data);
01403                                 Anope::string item;
01404                                 bool has_value = ConfValue(hash, configitems.MultiValues[Index].tag, configitems.MultiValues[Index].items[valuenum], configitems.MultiValues[Index].items_default[valuenum], tagnum, item, allow_newlines);
01405                                 if (defines.count(item) > 0)
01406                                         item = defines[item];
01407 
01408                                 if (has_value && item.empty() && !allow_empty)
01409                                         throw ConfigException("Item without value: " + configitems.MultiValues[Index].tag + ":" + configitems.MultiValues[Index].items[valuenum]);
01410 
01411                                 switch (dt)
01412                                 {
01413                                         case DT_NOSPACES:
01414                                         {
01415                                                 if (has_value)
01416                                                         vl.push_back(ValueItem(item));
01417                                                 else
01418                                                         vl.push_back(ValueItem());
01419                                                 ValidateNoSpaces(vl[vl.size() - 1].GetValue(), configitems.MultiValues[Index].tag, configitems.MultiValues[Index].items[valuenum]);
01420                                                 break;
01421                                         }
01422                                         case DT_HOSTNAME:
01423                                         {
01424                                                 if (has_value)
01425                                                         vl.push_back(ValueItem(item));
01426                                                 else
01427                                                         vl.push_back(ValueItem());
01428                                                 ValidateHostname(vl[vl.size() - 1].GetValue(), configitems.MultiValues[Index].tag, configitems.MultiValues[Index].items[valuenum]);
01429                                                 break;
01430                                         }
01431                                         case DT_IPADDRESS:
01432                                         {
01433                                                 if (has_value)
01434                                                         vl.push_back(ValueItem(item));
01435                                                 else
01436                                                         vl.push_back(ValueItem());
01437                                                 ValidateIP(vl[vl.size() - 1].GetValue(), configitems.MultiValues[Index].tag, configitems.MultiValues[Index].items[valuenum], allow_wild);
01438                                                 break;
01439                                         }
01440                                         case DT_STRING:
01441                                         {
01442                                                 if (has_value)
01443                                                         vl.push_back(ValueItem(item));
01444                                                 else
01445                                                         vl.push_back(ValueItem());
01446                                                 break;
01447                                         }
01448                                         case DT_INTEGER:
01449                                         case DT_UINTEGER:
01450                                         case DT_LUINTEGER:
01451                                         {
01452                                                 int titem = 0;
01453                                                 if (ConfValueInteger(hash, configitems.MultiValues[Index].tag, configitems.MultiValues[Index].items[valuenum], configitems.MultiValues[Index].items_default[valuenum], tagnum, titem))
01454                                                         vl.push_back(ValueItem(titem));
01455                                                 else
01456                                                         vl.push_back(ValueItem(0));
01457                                                 break;
01458                                         }
01459                                         case DT_TIME:
01460                                         {
01461                                                 if (has_value)
01462                                                 {
01463 #ifdef _WIN32
01464                                                         long time = static_cast<long>(Anope::DoTime(item));
01465 #else
01466                                                         time_t time = Anope::DoTime(item);
01467 #endif
01468                                                         vl.push_back(ValueItem(time));
01469                                                 }
01470                                                 else
01471                                                         vl.push_back(ValueItem(0));
01472                                                 break;
01473                                         }
01474                                         case DT_BOOLEAN:
01475                                         {
01476                                                 bool titem = ConfValueBool(hash, configitems.MultiValues[Index].tag, configitems.MultiValues[Index].items[valuenum], configitems.MultiValues[Index].items_default[valuenum], tagnum);
01477                                                 vl.push_back(ValueItem(titem));
01478                                         }
01479                                 }
01480                         }
01481                         configitems.MultiValues[Index].validation_function(this, configitems.MultiValues[Index].tag, configitems.MultiValues[Index].items, vl, configitems.MultiValues[Index].datatype);
01482                 }
01483                 configitems.MultiValues[Index].finish_function(this, configitems.MultiValues[Index].tag);
01484         }
01485 
01486         // Read the values of all the tags which occur once or not at all, and call their callbacks.
01487         for (int Index = 0; !configitems.Values[Index].tag.empty(); ++Index)
01488         {
01489                 Anope::string item;
01490                 int dt = configitems.Values[Index].datatype;
01491                 bool allow_newlines = dt & DT_ALLOW_NEWLINE, allow_wild = dt & DT_ALLOW_WILD, noreload = dt & DT_NORELOAD, allow_empty = dt & DT_ALLOW_EMPTY;
01492                 dt &= ~DT_ALLOW_NEWLINE;
01493                 dt &= ~DT_ALLOW_WILD;
01494                 dt &= ~DT_NORELOAD;
01495                 dt &= ~DT_ALLOW_EMPTY;
01496 
01497                 ConfigDataHash &hash = (noreload && Config ? Config->config_data : this->config_data);
01498                 bool has_value = ConfValue(hash, configitems.Values[Index].tag, configitems.Values[Index].value, configitems.Values[Index].default_value, 0, item, allow_newlines);
01499                 if (defines.count(item) > 0)
01500                         item = defines[item];
01501 
01502                 if (has_value && item.empty() && !allow_empty)
01503                         throw ConfigException("Item without value: " + configitems.Values[Index].tag + ":" + configitems.Values[Index].value);
01504 
01505                 ValueItem vi(item);
01506 
01507                 if (!configitems.Values[Index].validation_function(this, configitems.Values[Index].tag, configitems.Values[Index].value, vi))
01508                         throw ConfigException("One or more values in your configuration file failed to validate. Please see your logfiles for more information.");
01509 
01510                 switch (dt)
01511                 {
01512                         case DT_NOSPACES:
01513                         {
01514                                 ValueContainerString *vcs = anope_dynamic_static_cast<ValueContainerString *>(configitems.Values[Index].val);
01515                                 ValidateNoSpaces(vi.GetValue(), configitems.Values[Index].tag, configitems.Values[Index].value);
01516                                 vcs->Set(vi.GetValue());
01517                                 break;
01518                         }
01519                         case DT_HOSTNAME:
01520                         {
01521                                 ValueContainerString *vcs = anope_dynamic_static_cast<ValueContainerString *>(configitems.Values[Index].val);
01522                                 ValidateHostname(vi.GetValue(), configitems.Values[Index].tag, configitems.Values[Index].value);
01523                                 vcs->Set(vi.GetValue());
01524                                 break;
01525                         }
01526                         case DT_IPADDRESS:
01527                         {
01528                                 ValueContainerString *vcs = anope_dynamic_static_cast<ValueContainerString *>(configitems.Values[Index].val);
01529                                 ValidateIP(vi.GetValue(), configitems.Values[Index].tag, configitems.Values[Index].value, allow_wild);
01530                                 vcs->Set(vi.GetValue());
01531                                 break;
01532                         }
01533                         case DT_STRING:
01534                         {
01535                                 ValueContainerString *vcs = anope_dynamic_static_cast<ValueContainerString *>(configitems.Values[Index].val);
01536                                 vcs->Set(vi.GetValue());
01537                                 break;
01538                         }
01539                         case DT_INTEGER:
01540                         {
01541                                 int val = vi.GetInteger();
01542                                 ValueContainerInt *vci = anope_dynamic_static_cast<ValueContainerInt *>(configitems.Values[Index].val);
01543                                 vci->Set(&val, sizeof(int));
01544                                 break;
01545                         }
01546                         case DT_UINTEGER:
01547                         {
01548                                 unsigned val = vi.GetInteger();
01549                                 ValueContainerUInt *vci = anope_dynamic_static_cast<ValueContainerUInt *>(configitems.Values[Index].val);
01550                                 vci->Set(&val, sizeof(unsigned));
01551                                 break;
01552                         }
01553                         case DT_LUINTEGER:
01554                         {
01555                                 unsigned long val = vi.GetInteger();
01556                                 ValueContainerLUInt *vci = anope_dynamic_static_cast<ValueContainerLUInt *>(configitems.Values[Index].val);
01557                                 vci->Set(&val, sizeof(unsigned long));
01558                                 break;
01559                         }
01560                         case DT_TIME:
01561                         {
01562                                 time_t time = Anope::DoTime(vi.GetValue());
01563                                 ValueContainerTime *vci = anope_dynamic_static_cast<ValueContainerTime *>(configitems.Values[Index].val);
01564                                 vci->Set(&time, sizeof(time_t));
01565                                 break;
01566                         }
01567                         case DT_BOOLEAN:
01568                         {
01569                                 bool val = vi.GetBool();
01570                                 ValueContainerBool *vcb = anope_dynamic_static_cast<ValueContainerBool *>(configitems.Values[Index].val);
01571                                 vcb->Set(&val, sizeof(bool));
01572                                 break;
01573                         }
01574                         default:
01575                                 break;
01576                 }
01577         }
01578 
01579         Log(LOG_DEBUG) << "End config " << ServicesConf.GetName();
01580         for (int Index = 0; !Once[Index].empty(); ++Index)
01581                 CheckOnce(Once[Index]);
01582         Log() << "Done reading configuration file " << ServicesConf.GetName();
01583 }
01584 
01585 void ServerConfig::LoadConf(ConfigurationFile &file)
01586 {
01587         Anope::string section, wordbuffer, itemname;
01588         int linenumber = 0;
01589         bool in_word = false, in_quote = false, in_ml_comment = false;
01590         KeyValList sectiondata;
01591         if (!file.Open())
01592         {
01593                 throw ConfigException("File " + file.GetName() + " could not be opened.");
01594         }
01595         Log(LOG_DEBUG) << "Start to read conf " << file.GetName();
01596         // Start reading characters...
01597         while (!file.End())
01598         {       
01599                 Anope::string line = file.Read();
01600                 ++linenumber;
01601                 unsigned c = 0, len = line.length();
01602                 for (; c < len; ++c)
01603                 {
01604                         char ch = line[c];
01605                         if (in_quote)
01606                         {
01607                                 /* Strip leading white spaces from multi line comments */
01608                                 if (c == 0)
01609                                 {
01610                                         while (c < len && isspace(line[c]))
01611                                                 ++c;
01612                                         ch = line[c];
01613                                 }
01614                                 /* Allow \" in quotes */
01615                                 if (ch == '\\' && c + 1 < len && line[c + 1] == '"')
01616                                         wordbuffer += line[++c];
01617                                 else if (ch == '"')
01618                                         in_quote = in_word = false;
01619                                 else
01620                                         wordbuffer += ch;
01621                         }
01622                         else if (in_ml_comment)
01623                         {
01624                                 if (ch == '*' && c + 1 < len && line[c + 1] == '/')
01625                                 {
01626                                         in_ml_comment = false;
01627                                         ++c;
01628                                 }
01629                                 continue;
01630                         }
01631                         else if (ch == '#' || (ch == '/' && c + 1 < len && line[c + 1] == '/'))
01632                                 c = len - 1; // Line comment, ignore the rest of the line (much like this one!)
01633                         else if (ch == '/' && c + 1 < len && line[c + 1] == '*')
01634                         {
01635                                 // Multiline (or less than one line) comment
01636                                 in_ml_comment = true;
01637                                 ++c;
01638                                 continue;
01639                         }
01640                         else if (!in_word && (ch == '(' || ch == '_' || ch == ')'))
01641                                 ;
01642                         else if (ch == '"')
01643                         {
01644                                 // Quotes are valid only in the value position
01645                                 if (section.empty() || itemname.empty())
01646                                 {
01647                                         file.Close();
01648                                         throw ConfigException("Unexpected quoted string: " + file.GetName() + ":" + stringify(linenumber));
01649                                 }
01650                                 if (in_word || !wordbuffer.empty())
01651                                 {
01652                                         file.Close();
01653                                         throw ConfigException("Unexpected quoted string (prior unhandled words): " + file.GetName() + ":" + stringify(linenumber));
01654                                 }
01655                                 in_quote = in_word = true;
01656                         }
01657                         else if (ch == '=')
01658                         {
01659                                 if (section.empty())
01660                                 {
01661                                         file.Close();
01662                                         throw ConfigException("Config item outside of section (or stray '='): " + file.GetName() + ":" + stringify(linenumber));
01663                                 }
01664                                 if (!itemname.empty())
01665                                 {
01666                                         file.Close();
01667                                         throw ConfigException("Stray '=' sign or item without value: " + file.GetName() + ":" + stringify(linenumber));
01668                                 }
01669                                 if (in_word)
01670                                         in_word = false;
01671                                 itemname = wordbuffer;
01672                                 wordbuffer.clear();
01673                         }
01674                         else if (ch == '{')
01675                         {
01676                                 if (!section.empty())
01677                                 {
01678                                         file.Close();
01679                                         throw ConfigException("Section inside another section: " + file.GetName() + ":" + stringify(linenumber));
01680                                 }
01681                                 if (wordbuffer.empty())
01682                                 {
01683                                         file.Close();
01684                                         throw ConfigException("Section without a name or unexpected '{': " + file.GetName() + ":" + stringify(linenumber));
01685                                 }
01686                                 if (in_word)
01687                                         in_word = false;
01688                                 section = wordbuffer;
01689                                 wordbuffer.clear();
01690                                 continue;
01691                         }
01692                         else if (ch == ' ' || ch == '\r' || ch == '\t')
01693                         {
01694                                 // Terminate word
01695                                 in_word = false;
01696                         }
01697                         else if (ch == ';' || ch == '}')
01698                                 ;
01699                         else
01700                         {
01701                                 if (!in_word && !wordbuffer.empty())
01702                                 {
01703                                         file.Close();
01704                                         throw ConfigException("Unexpected word: " + file.GetName() + ":" + stringify(linenumber));
01705                                 }
01706                                 wordbuffer += ch;
01707                                 in_word = true;
01708                         }
01709 
01710                         if (ch == ';' || ch == '}' || c + 1 == len)
01711                         {
01712                                 bool eol = c + 1 == len;
01713 
01714                                 if (!eol && in_quote)
01715                                         // Allow ; and } in quoted strings
01716                                         continue;
01717 
01718                                 if (in_quote)
01719                                 {
01720                                         // Quotes can span multiple lines; all we need to do is go to the next line without clearing things
01721                                         wordbuffer += "\n";
01722                                         continue;
01723                                 }
01724                                 in_word = false;
01725                                 if (!itemname.empty())
01726                                 {
01727                                         Log(LOG_DEBUG) << "ln " << linenumber << " EOL: s='" << section << "' '" << itemname << "' set to '" << wordbuffer << "'";
01728                                         sectiondata.push_back(KeyVal(itemname, wordbuffer));
01729                                         wordbuffer.clear();
01730                                         itemname.clear();
01731                                 }
01732 
01733                                 if (ch == '}')
01734                                 {
01735                                         if (section.empty())
01736                                         {
01737                                                 file.Close();
01738                                                 throw ConfigException("Stray '}': " + file.GetName() + ":" + stringify(linenumber));
01739                                         }
01740                                         this->config_data.insert(std::pair<Anope::string, KeyValList>(section, sectiondata));
01741                                         section.clear();
01742                                         sectiondata.clear();
01743                                 }
01744                         }
01745                 }
01746         }
01747         if (in_ml_comment)
01748         {
01749                 file.Close();
01750                 throw ConfigException("Unterminated multiline comment at end of file: " + file.GetName());
01751         }
01752         if (in_quote)
01753         {
01754                 file.Close();
01755                 throw ConfigException("Unterminated quote at end of file: " + file.GetName());
01756         }
01757         if (!itemname.empty() || !wordbuffer.empty())
01758         {
01759                 file.Close();
01760                 throw ConfigException("Unexpected garbage at end of file: " + file.GetName());
01761         }
01762         if (!section.empty())
01763         {
01764                 file.Close();
01765                 throw ConfigException("Unterminated section at end of file: " + file.GetName());
01766         }
01767 
01768         file.Close();
01769 }
01770 
01771 bool ServerConfig::ConfValue(ConfigDataHash &target, const Anope::string &tag, const Anope::string &var, int index, Anope::string &result, bool allow_linefeeds)
01772 {
01773         return ConfValue(target, tag, var, "", index, result, allow_linefeeds);
01774 }
01775 
01776 bool ServerConfig::ConfValue(ConfigDataHash &target, const Anope::string &tag, const Anope::string &var, const Anope::string &default_value, int index, Anope::string &result, bool allow_linefeeds)
01777 {
01778         ConfigDataHash::size_type pos = index;
01779         if (pos < target.count(tag))
01780         {
01781                 ConfigDataHash::iterator iter = target.find(tag);
01782 
01783                 for (int i = 0; i < index; ++i)
01784                         ++iter;
01785 
01786                 KeyValList::iterator j = iter->second.begin(), jend = iter->second.end();
01787                 for (; j != jend; ++j)
01788                 {
01789                         if (j->first.equals_ci(var))
01790                         {
01791                                 if (!allow_linefeeds && j->second.find('\n') != Anope::string::npos)
01792                                 {
01793                                         Log(LOG_DEBUG) << "Value of <" << tag << ":" << var << "> contains a linefeed, and linefeeds in this value are not permitted -- stripped to spaces.";
01794                                         j->second.replace_all_cs("\n", " ");
01795                                 }
01796                                 else
01797                                 {
01798                                         result = j->second;
01799                                         return true;
01800                                 }
01801                         }
01802                 }
01803                 if (!default_value.empty())
01804                 {
01805                         result = default_value;
01806                         return true;
01807                 }
01808         }
01809         else if (!pos)
01810         {
01811                 if (!default_value.empty())
01812                 {
01813                         result = default_value;
01814                         return true;
01815                 }
01816         }
01817         return false;
01818 }
01819 
01820 bool ServerConfig::ConfValueInteger(ConfigDataHash &target, const Anope::string &tag, const Anope::string &var, int index, int &result)
01821 {
01822         return ConfValueInteger(target, tag, var, "", index, result);
01823 }
01824 
01825 bool ServerConfig::ConfValueInteger(ConfigDataHash &target, const Anope::string &tag, const Anope::string &var, const Anope::string &default_value, int index, int &result)
01826 {
01827         Anope::string value;
01828         std::istringstream stream;
01829         bool r = ConfValue(target, tag, var, default_value, index, value);
01830         stream.str(value.str());
01831         if (!(stream >> result))
01832                 return false;
01833         else
01834         {
01835                 if (!value.empty())
01836                 {
01837                         if (value.substr(0, 2).equals_ci("0x"))
01838                         {
01839                                 char *endptr;
01840 
01841                                 value.erase(0, 2);
01842                                 result = strtol(value.c_str(), &endptr, 16);
01843 
01844                                 /* No digits found */
01845                                 if (endptr == value.c_str())
01846                                         return false;
01847                         }
01848                         else
01849                         {
01850                                 char denominator = *(value.end() - 1);
01851                                 switch (toupper(denominator))
01852                                 {
01853                                         case 'K':
01854                                                 // Kilobytes -> bytes
01855                                                 result = result * 1024;
01856                                                 break;
01857                                         case 'M':
01858                                                 // Megabytes -> bytes
01859                                                 result = result * 1048576;
01860                                                 break;
01861                                         case 'G':
01862                                                 // Gigabytes -> bytes
01863                                                 result = result * 1073741824;
01864                                 }
01865                         }
01866                 }
01867         }
01868         return r;
01869 }
01870 
01871 bool ServerConfig::ConfValueBool(ConfigDataHash &target, const Anope::string &tag, const Anope::string &var, int index)
01872 {
01873         return ConfValueBool(target, tag, var, "", index);
01874 }
01875 
01876 bool ServerConfig::ConfValueBool(ConfigDataHash &target, const Anope::string &tag, const Anope::string &var, const Anope::string &default_value, int index)
01877 {
01878         Anope::string result;
01879         if (!ConfValue(target, tag, var, default_value, index, result))
01880                 return false;
01881 
01882         return result.equals_ci("yes") || result.equals_ci("true") || result.equals_ci("1");
01883 }
01884 
01885 int ServerConfig::ConfValueEnum(const ConfigDataHash &target, const Anope::string &tag)
01886 {
01887         return target.count(tag);
01888 }
01889 
01890 int ServerConfig::ConfVarEnum(ConfigDataHash &target, const Anope::string &tag, int index)
01891 {
01892         ConfigDataHash::size_type pos = index;
01893 
01894         if (pos < target.count(tag))
01895         {
01896                 ConfigDataHash::const_iterator iter = target.find(tag);
01897 
01898                 for (int i = 0; i < index; ++i)
01899                         ++iter;
01900 
01901                 return iter->second.size();
01902         }
01903 
01904         return 0;
01905 }
01906 
01907 ValueItem::ValueItem() { }
01908 
01909 ValueItem::ValueItem(int value) : v("")
01910 {
01911         std::stringstream n;
01912         n << value;
01913         v = n.str();
01914 }
01915 
01916 ValueItem::ValueItem(long value) : v("")
01917 {
01918         std::stringstream n;
01919         n << value;
01920         v = n.str();
01921 }
01922 
01923 ValueItem::ValueItem(bool value) : v("")
01924 {
01925         std::stringstream n;
01926         n << value;
01927         v = n.str();
01928 }
01929 
01930 ValueItem::ValueItem(const Anope::string &value) : v(value) { }
01931 
01932 void ValueItem::Set(const Anope::string &value)
01933 {
01934         v = value;
01935 }
01936 
01937 void ValueItem::Set(int value)
01938 {
01939         std::stringstream n;
01940         n << value;
01941         v = n.str();
01942 }
01943 
01944 int ValueItem::GetInteger() const
01945 {
01946         if (v.empty() || !v.is_number_only())
01947                 return 0;
01948         try
01949         {
01950                 return convertTo<int>(v);
01951         }
01952         catch (const ConvertException &)
01953         {
01954                 Log() << "Unable to convert configuration value " << this->v << " to an integer. Value too large?";
01955         }
01956 
01957         return 0;
01958 }
01959 
01960 const char *ValueItem::GetString() const
01961 {
01962         return v.c_str();
01963 }
01964 
01965 bool ValueItem::GetBool() const
01966 {
01967         return GetInteger() || v == "yes" || v == "true";
01968 }
01969 
01970