00001
00002
00003
00004
00005
00006
00007
00008
00009
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);
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
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;
00117 }
00118
00119
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
00193
00194
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
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
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
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
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
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
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
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
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
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;
00895
00896
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
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
01085 const Item Items[] = {
01086
01087
01088
01089
01090
01091
01092
01093
01094
01095
01096
01097
01098
01099
01100
01101
01102
01103
01104
01105
01106
01107
01108
01109
01110
01111
01112
01113
01114
01115
01116
01117
01118
01119
01120
01121
01122
01123
01124
01125
01126
01127
01128
01129
01130
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
01285
01286 MultiItem MultiItems[] = {
01287
01288
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
01376 static const Anope::string Once[] = {"serverinfo", "networkinfo", "options", ""};
01377
01378 this->LoadConf(ServicesConf);
01379
01380 ConfigItems configitems(this);
01381
01382
01383
01384
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
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
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
01608 if (c == 0)
01609 {
01610 while (c < len && isspace(line[c]))
01611 ++c;
01612 ch = line[c];
01613 }
01614
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;
01633 else if (ch == '/' && c + 1 < len && line[c + 1] == '*')
01634 {
01635
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
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
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
01716 continue;
01717
01718 if (in_quote)
01719 {
01720
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
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
01855 result = result * 1024;
01856 break;
01857 case 'M':
01858
01859 result = result * 1048576;
01860 break;
01861 case 'G':
01862
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