-/* +------------------------------------+
- * | Inspire Internet Relay Chat Daemon |
- * +------------------------------------+
- *
- * InspIRCd: (C) 2002-2007 InspIRCd Development Team
- * See: http://www.inspircd.org/wiki/index.php/Credits
- *
- * This program is free but copyrighted software; see
- * the file COPYING for details.
- *
- * ---------------------------------------------------
- */
-
-#include "inspircd.h"
-#include "configreader.h"
-#include <sstream>
-#include <fstream>
-#include "xline.h"
-#include "exitcodes.h"
-#include "commands/cmd_whowas.h"
-
-std::vector<std::string> old_module_names, new_module_names, added_modules, removed_modules;
-
-ServerConfig::ServerConfig(InspIRCd* Instance) : ServerInstance(Instance)
-{
- this->ClearStack();
- *ServerName = *Network = *ServerDesc = *AdminName = '\0';
- *HideWhoisServer = *AdminEmail = *AdminNick = *diepass = *restartpass = *FixedQuit = *HideKillsServer = '\0';
- *DefaultModes = *CustomVersion = *motd = *rules = *PrefixQuit = *DieValue = *DNSServer = '\0';
- *UserStats = *ModPath = *MyExecutable = *DisabledCommands = *PID = *SuffixQuit = '\0';
- WhoWasGroupSize = WhoWasMaxGroups = WhoWasMaxKeep = 0;
- log_file = NULL;
- NoUserDns = forcedebug = OperSpyWhois = nofork = HideBans = HideSplits = UndernetMsgPrefix = false;
- CycleHosts = writelog = AllowHalfop = true;
- dns_timeout = DieDelay = 5;
- MaxTargets = 20;
- NetBufferSize = 10240;
- SoftLimit = MAXCLIENTS;
- MaxConn = SOMAXCONN;
- MaxWhoResults = 0;
- debugging = 0;
- MaxChans = 20;
- OperMaxChans = 30;
- LogLevel = DEFAULT;
- maxbans.clear();
-}
-
-void ServerConfig::ClearStack()
-{
- include_stack.clear();
-}
-
-Module* ServerConfig::GetIOHook(int port)
-{
- std::map<int,Module*>::iterator x = IOHookModule.find(port);
- return (x != IOHookModule.end() ? x->second : NULL);
-}
-
-Module* ServerConfig::GetIOHook(InspSocket* is)
-{
- std::map<InspSocket*,Module*>::iterator x = SocketIOHookModule.find(is);
- return (x != SocketIOHookModule.end() ? x->second : NULL);
-}
-
-bool ServerConfig::AddIOHook(int port, Module* iomod)
-{
- if (!GetIOHook(port))
- {
- IOHookModule[port] = iomod;
- return true;
- }
- else
- {
- throw ModuleException("Port already hooked by another module");
- return false;
- }
-}
-
-bool ServerConfig::AddIOHook(Module* iomod, InspSocket* is)
-{
- if (!GetIOHook(is))
- {
- SocketIOHookModule[is] = iomod;
- is->IsIOHooked = true;
- return true;
- }
- else
- {
- throw ModuleException("InspSocket derived class already hooked by another module");
- return false;
- }
-}
-
-bool ServerConfig::DelIOHook(int port)
-{
- std::map<int,Module*>::iterator x = IOHookModule.find(port);
- if (x != IOHookModule.end())
- {
- IOHookModule.erase(x);
- return true;
- }
- return false;
-}
-
-bool ServerConfig::DelIOHook(InspSocket* is)
-{
- std::map<InspSocket*,Module*>::iterator x = SocketIOHookModule.find(is);
- if (x != SocketIOHookModule.end())
- {
- SocketIOHookModule.erase(x);
- return true;
- }
- return false;
-}
-
-void ServerConfig::Update005()
-{
- std::stringstream out(data005);
- std::string token;
- std::string line5;
- int token_counter = 0;
- isupport.clear();
- while (out >> token)
- {
- line5 = line5 + token + " ";
- token_counter++;
- if (token_counter >= 13)
- {
- char buf[MAXBUF];
- snprintf(buf, MAXBUF, "%s:are supported by this server", line5.c_str());
- isupport.push_back(buf);
- line5.clear();
- token_counter = 0;
- }
- }
- if (!line5.empty())
- {
- char buf[MAXBUF];
- snprintf(buf, MAXBUF, "%s:are supported by this server", line5.c_str());
- isupport.push_back(buf);
- }
-}
-
-void ServerConfig::Send005(userrec* user)
-{
- for (std::vector<std::string>::iterator line = ServerInstance->Config->isupport.begin(); line != ServerInstance->Config->isupport.end(); line++)
- user->WriteServ("005 %s %s", user->nick, line->c_str());
-}
-
-bool ServerConfig::CheckOnce(char* tag, bool bail, userrec* user)
-{
- int count = ConfValueEnum(this->config_data, tag);
-
- if (count > 1)
- {
- throw CoreException("You have more than one <"+std::string(tag)+"> tag, this is not permitted.");
- return false;
- }
- if (count < 1)
- {
- throw CoreException("You have not defined a <"+std::string(tag)+"> tag, this is required.");
- return false;
- }
- return true;
-}
-
-bool NoValidation(ServerConfig* conf, const char* tag, const char* value, ValueItem &data)
-{
- return true;
-}
-
-bool ValidateMaxTargets(ServerConfig* conf, const char* tag, const char* value, ValueItem &data)
-{
- if ((data.GetInteger() < 0) || (data.GetInteger() > 31))
- {
- conf->GetInstance()->Log(DEFAULT,"WARNING: <options:maxtargets> value is greater than 31 or less than 0, set to 20.");
- data.Set(20);
- }
- return true;
-}
-
-bool ValidateSoftLimit(ServerConfig* conf, const char* tag, const char* value, ValueItem &data)
-{
- if ((data.GetInteger() < 1) || (data.GetInteger() > MAXCLIENTS))
- {
- conf->GetInstance()->Log(DEFAULT,"WARNING: <options:softlimit> value is greater than %d or less than 0, set to %d.",MAXCLIENTS,MAXCLIENTS);
- data.Set(MAXCLIENTS);
- }
- return true;
-}
-
-bool ValidateMaxConn(ServerConfig* conf, const char* tag, const char* value, ValueItem &data)
-{
- if (data.GetInteger() > SOMAXCONN)
- conf->GetInstance()->Log(DEFAULT,"WARNING: <options:somaxconn> value may be higher than the system-defined SOMAXCONN value!");
- return true;
-}
-
-bool InitializeDisabledCommands(const char* data, InspIRCd* ServerInstance)
-{
- std::stringstream dcmds(data);
- std::string thiscmd;
-
- /* Enable everything first */
- for (command_table::iterator x = ServerInstance->Parser->cmdlist.begin(); x != ServerInstance->Parser->cmdlist.end(); x++)
- x->second->Disable(false);
-
- /* Now disable all the ones which the user wants disabled */
- while (dcmds >> thiscmd)
- {
- command_table::iterator cm = ServerInstance->Parser->cmdlist.find(thiscmd);
- if (cm != ServerInstance->Parser->cmdlist.end())
- {
- cm->second->Disable(true);
- }
- }
- return true;
-}
-
-bool ValidateDnsServer(ServerConfig* conf, const char* tag, const char* value, ValueItem &data)
-{
- if (!*(data.GetString()))
- {
- std::string nameserver;
-#ifdef WINDOWS
- conf->GetInstance()->Log(DEFAULT,"WARNING: <dns:server> not defined, attempting to find working server in the registry...");
- nameserver = FindNameServerWin();
- /* Windows stacks multiple nameservers in one registry key, seperated by commas.
- * Spotted by Cataclysm.
- */
- if (nameserver.find(',') != std::string::npos)
- nameserver = nameserver.substr(0, nameserver.find(','));
- data.Set(nameserver.c_str());
- conf->GetInstance()->Log(DEFAULT,"<dns:server> set to '%s' as first active resolver in registry.", nameserver.c_str());
-#else
- // attempt to look up their nameserver from /etc/resolv.conf
- conf->GetInstance()->Log(DEFAULT,"WARNING: <dns:server> not defined, attempting to find working server in /etc/resolv.conf...");
- ifstream resolv("/etc/resolv.conf");
- bool found_server = false;
-
- if (resolv.is_open())
- {
- while (resolv >> nameserver)
- {
- if ((nameserver == "nameserver") && (!found_server))
- {
- resolv >> nameserver;
- data.Set(nameserver.c_str());
- found_server = true;
- conf->GetInstance()->Log(DEFAULT,"<dns:server> set to '%s' as first resolver in /etc/resolv.conf.",nameserver.c_str());
- }
- }
-
- if (!found_server)
- {
- conf->GetInstance()->Log(DEFAULT,"/etc/resolv.conf contains no viable nameserver entries! Defaulting to nameserver '127.0.0.1'!");
- data.Set("127.0.0.1");
- }
- }
- else
- {
- conf->GetInstance()->Log(DEFAULT,"/etc/resolv.conf can't be opened! Defaulting to nameserver '127.0.0.1'!");
- data.Set("127.0.0.1");
- }
-#endif
- }
- return true;
-}
-
-bool ValidateServerName(ServerConfig* conf, const char* tag, const char* value, ValueItem &data)
-{
- /* If we already have a servername, and they changed it, we should throw an exception. */
- if ((strcasecmp(conf->ServerName, data.GetString())) && (*conf->ServerName))
- {
- throw CoreException("Configuration error: You cannot change your servername at runtime! Please restart your server for this change to be applied.");
- /* XXX: We don't actually reach this return of course... */
- return false;
- }
- if (!strchr(data.GetString(),'.'))
- {
- conf->GetInstance()->Log(DEFAULT,"WARNING: <server:name> '%s' is not a fully-qualified domain name. Changed to '%s%c'",data.GetString(),data.GetString(),'.');
- std::string moo = std::string(data.GetString()).append(".");
- data.Set(moo.c_str());
- }
- return true;
-}
-
-bool ValidateNetBufferSize(ServerConfig* conf, const char* tag, const char* value, ValueItem &data)
-{
- if ((!data.GetInteger()) || (data.GetInteger() > 65535) || (data.GetInteger() < 1024))
- {
- conf->GetInstance()->Log(DEFAULT,"No NetBufferSize specified or size out of range, setting to default of 10240.");
- data.Set(10240);
- }
- return true;
-}
-
-bool ValidateMaxWho(ServerConfig* conf, const char* tag, const char* value, ValueItem &data)
-{
- if ((data.GetInteger() > 65535) || (data.GetInteger() < 1))
- {
- conf->GetInstance()->Log(DEFAULT,"<options:maxwhoresults> size out of range, setting to default of 128.");
- data.Set(128);
- }
- return true;
-}
-
-bool ValidateLogLevel(ServerConfig* conf, const char* tag, const char* value, ValueItem &data)
-{
- std::string dbg = data.GetString();
- conf->LogLevel = DEFAULT;
-
- if (dbg == "debug")
- conf->LogLevel = DEBUG;
- else if (dbg == "verbose")
- conf->LogLevel = VERBOSE;
- else if (dbg == "default")
- conf->LogLevel = DEFAULT;
- else if (dbg == "sparse")
- conf->LogLevel = SPARSE;
- else if (dbg == "none")
- conf->LogLevel = NONE;
-
- conf->debugging = (conf->LogLevel == DEBUG);
-
- return true;
-}
-
-bool ValidateMotd(ServerConfig* conf, const char* tag, const char* value, ValueItem &data)
-{
- conf->ReadFile(conf->MOTD, data.GetString());
- return true;
-}
-
-bool ValidateNotEmpty(ServerConfig* conf, const char* tag, const char* value, ValueItem &data)
-{
- if (!*data.GetString())
- throw CoreException(std::string("The value for ")+tag+" cannot be empty!");
- return true;
-}
-
-bool ValidateRules(ServerConfig* conf, const char* tag, const char* value, ValueItem &data)
-{
- conf->ReadFile(conf->RULES, data.GetString());
- return true;
-}
-
-bool ValidateModeLists(ServerConfig* conf, const char* tag, const char* value, ValueItem &data)
-{
- memset(conf->HideModeLists, 0, 256);
- for (const unsigned char* x = (const unsigned char*)data.GetString(); *x; ++x)
- conf->HideModeLists[*x] = true;
- return true;
-}
-
-bool ValidateExemptChanOps(ServerConfig* conf, const char* tag, const char* value, ValueItem &data)
-{
- memset(conf->ExemptChanOps, 0, 256);
- for (const unsigned char* x = (const unsigned char*)data.GetString(); *x; ++x)
- conf->ExemptChanOps[*x] = true;
- return true;
-}
-
-bool ValidateWhoWas(ServerConfig* conf, const char* tag, const char* value, ValueItem &data)
-{
- conf->WhoWasMaxKeep = conf->GetInstance()->Duration(data.GetString());
-
- if (conf->WhoWasGroupSize < 0)
- conf->WhoWasGroupSize = 0;
-
- if (conf->WhoWasMaxGroups < 0)
- conf->WhoWasMaxGroups = 0;
-
- if (conf->WhoWasMaxKeep < 3600)
- {
- conf->WhoWasMaxKeep = 3600;
- conf->GetInstance()->Log(DEFAULT,"WARNING: <whowas:maxkeep> value less than 3600, setting to default 3600");
- }
-
- command_t* whowas_command = conf->GetInstance()->Parser->GetHandler("WHOWAS");
- if (whowas_command)
- {
- std::deque<classbase*> params;
- whowas_command->HandleInternal(WHOWAS_PRUNE, params);
- }
-
- return true;
-}
-
-/* Callback called before processing the first <connect> tag
- */
-bool InitConnect(ServerConfig* conf, const char* tag)
-{
- conf->GetInstance()->Log(DEFAULT,"Reading connect classes...");
- conf->Classes.clear();
- return true;
-}
-
-/* Callback called to process a single <connect> tag
- */
-bool DoConnect(ServerConfig* conf, const char* tag, char** entries, ValueList &values, int* types)
-{
- ConnectClass c;
- const char* allow = values[0].GetString(); /* Yeah, there are a lot of values. Live with it. */
- const char* deny = values[1].GetString();
- const char* password = values[2].GetString();
- int timeout = values[3].GetInteger();
- int pingfreq = values[4].GetInteger();
- int flood = values[5].GetInteger();
- int threshold = values[6].GetInteger();
- int sendq = values[7].GetInteger();
- int recvq = values[8].GetInteger();
- int localmax = values[9].GetInteger();
- int globalmax = values[10].GetInteger();
-
- if (*allow)
- {
- ConnectClass c(timeout, flood, allow, pingfreq, password, threshold, sendq, recvq, localmax, globalmax);
- conf->Classes.push_back(c);
- }
- else
- {
- ConnectClass c(deny);
- conf->Classes.push_back(c);
- }
-
- return true;
-}
-
-/* Callback called when there are no more <connect> tags
- */
-bool DoneConnect(ServerConfig* conf, const char* tag)
-{
- return true;
-}
-
-/* Callback called before processing the first <uline> tag
- */
-bool InitULine(ServerConfig* conf, const char* tag)
-{
- conf->ulines.clear();
- return true;
-}
-
-/* Callback called to process a single <uline> tag
- */
-bool DoULine(ServerConfig* conf, const char* tag, char** entries, ValueList &values, int* types)
-{
- const char* server = values[0].GetString();
- const bool silent = values[1].GetBool();
- conf->ulines[server] = silent;
- return true;
-}
-
-/* Callback called when there are no more <uline> tags
- */
-bool DoneULine(ServerConfig* conf, const char* tag)
-{
- return true;
-}
-
-/* Callback called before processing the first <module> tag
- */
-bool InitModule(ServerConfig* conf, const char* tag)
-{
- old_module_names.clear();
- new_module_names.clear();
- added_modules.clear();
- removed_modules.clear();
- for (std::vector<std::string>::iterator t = conf->module_names.begin(); t != conf->module_names.end(); t++)
- {
- old_module_names.push_back(*t);
- }
- return true;
-}
-
-/* Callback called to process a single <module> tag
- */
-bool DoModule(ServerConfig* conf, const char* tag, char** entries, ValueList &values, int* types)
-{
- const char* modname = values[0].GetString();
- new_module_names.push_back(modname);
- return true;
-}
-
-/* Callback called when there are no more <module> tags
- */
-bool DoneModule(ServerConfig* conf, const char* tag)
-{
- // now create a list of new modules that are due to be loaded
- // and a seperate list of modules which are due to be unloaded
- for (std::vector<std::string>::iterator _new = new_module_names.begin(); _new != new_module_names.end(); _new++)
- {
- bool added = true;
-
- for (std::vector<std::string>::iterator old = old_module_names.begin(); old != old_module_names.end(); old++)
- {
- if (*old == *_new)
- added = false;
- }
-
- if (added)
- added_modules.push_back(*_new);
- }
-
- for (std::vector<std::string>::iterator oldm = old_module_names.begin(); oldm != old_module_names.end(); oldm++)
- {
- bool removed = true;
- for (std::vector<std::string>::iterator newm = new_module_names.begin(); newm != new_module_names.end(); newm++)
- {
- if (*newm == *oldm)
- removed = false;
- }
-
- if (removed)
- removed_modules.push_back(*oldm);
- }
- return true;
-}
-
-/* Callback called before processing the first <banlist> tag
- */
-bool InitMaxBans(ServerConfig* conf, const char* tag)
-{
- conf->maxbans.clear();
- return true;
-}
-
-/* Callback called to process a single <banlist> tag
- */
-bool DoMaxBans(ServerConfig* conf, const char* tag, char** entries, ValueList &values, int* types)
-{
- const char* channel = values[0].GetString();
- int limit = values[1].GetInteger();
- conf->maxbans[channel] = limit;
- return true;
-}
-
-/* Callback called when there are no more <banlist> tags.
- */
-bool DoneMaxBans(ServerConfig* conf, const char* tag)
-{
- return true;
-}
-
-void ServerConfig::ReportConfigError(const std::string &errormessage, bool bail, userrec* user)
-{
- ServerInstance->Log(DEFAULT, "There were errors in your configuration file: %s", errormessage.c_str());
- if (bail)
- {
- /* Unneeded because of the ServerInstance->Log() aboive? */
- printf("There were errors in your configuration:\n%s\n\n",errormessage.c_str());
- InspIRCd::Exit(EXIT_STATUS_CONFIG);
- }
- else
- {
- std::string errors = errormessage;
- std::string::size_type start;
- unsigned int prefixlen;
- start = 0;
- /* ":ServerInstance->Config->ServerName NOTICE user->nick :" */
- if (user)
- {
- prefixlen = strlen(this->ServerName) + strlen(user->nick) + 11;
- user->WriteServ("NOTICE %s :There were errors in the configuration file:",user->nick);
- while (start < errors.length())
- {
- user->WriteServ("NOTICE %s :%s",user->nick, errors.substr(start, 510 - prefixlen).c_str());
- start += 510 - prefixlen;
- }
- }
- else
- {
- ServerInstance->WriteOpers("There were errors in the configuration file:");
- while (start < errors.length())
- {
- ServerInstance->WriteOpers(errors.substr(start, 360).c_str());
- start += 360;
- }
- }
- return;
- }
-}
-
-void ServerConfig::Read(bool bail, userrec* user)
-{
- static char debug[MAXBUF]; /* Temporary buffer for debugging value */
- static char maxkeep[MAXBUF]; /* Temporary buffer for WhoWasMaxKeep value */
- static char hidemodes[MAXBUF]; /* Modes to not allow listing from users below halfop */
- static char exemptchanops[MAXBUF]; /* Exempt channel ops from these modes */
- int rem = 0, add = 0; /* Number of modules added, number of modules removed */
- std::ostringstream errstr; /* String stream containing the error output */
-
- /* These tags MUST occur and must ONLY occur once in the config file */
- static char* Once[] = { "server", "admin", "files", "power", "options", NULL };
-
- /* These tags can occur ONCE or not at all */
- InitialConfig Values[] = {
- {"options", "softlimit", MAXCLIENTS_S, new ValueContainerUInt (&this->SoftLimit), DT_INTEGER, ValidateSoftLimit},
- {"options", "somaxconn", SOMAXCONN_S, new ValueContainerInt (&this->MaxConn), DT_INTEGER, ValidateMaxConn},
- {"options", "moronbanner", "Youre banned!", new ValueContainerChar (this->MoronBanner), DT_CHARPTR, NoValidation},
- {"server", "name", "", new ValueContainerChar (this->ServerName), DT_CHARPTR, ValidateServerName},
- {"server", "description", "Configure Me", new ValueContainerChar (this->ServerDesc), DT_CHARPTR, NoValidation},
- {"server", "network", "Network", new ValueContainerChar (this->Network), DT_CHARPTR, NoValidation},
- {"admin", "name", "", new ValueContainerChar (this->AdminName), DT_CHARPTR, NoValidation},
- {"admin", "email", "Mis@configu.red", new ValueContainerChar (this->AdminEmail), DT_CHARPTR, NoValidation},
- {"admin", "nick", "Misconfigured", new ValueContainerChar (this->AdminNick), DT_CHARPTR, NoValidation},
- {"files", "motd", "", new ValueContainerChar (this->motd), DT_CHARPTR, ValidateMotd},
- {"files", "rules", "", new ValueContainerChar (this->rules), DT_CHARPTR, ValidateRules},
- {"power", "diepass", "", new ValueContainerChar (this->diepass), DT_CHARPTR, ValidateNotEmpty},
- {"power", "pause", "", new ValueContainerInt (&this->DieDelay), DT_INTEGER, NoValidation},
- {"power", "restartpass", "", new ValueContainerChar (this->restartpass), DT_CHARPTR, ValidateNotEmpty},
- {"options", "prefixquit", "", new ValueContainerChar (this->PrefixQuit), DT_CHARPTR, NoValidation},
- {"options", "suffixquit", "", new ValueContainerChar (this->SuffixQuit), DT_CHARPTR, NoValidation},
- {"options", "fixedquit", "", new ValueContainerChar (this->FixedQuit), DT_CHARPTR, NoValidation},
- {"options", "loglevel", "default", new ValueContainerChar (debug), DT_CHARPTR, ValidateLogLevel},
- {"options", "netbuffersize","10240", new ValueContainerInt (&this->NetBufferSize), DT_INTEGER, ValidateNetBufferSize},
- {"options", "maxwho", "128", new ValueContainerInt (&this->MaxWhoResults), DT_INTEGER, ValidateMaxWho},
- {"options", "allowhalfop", "0", new ValueContainerBool (&this->AllowHalfop), DT_BOOLEAN, NoValidation},
- {"dns", "server", "", new ValueContainerChar (this->DNSServer), DT_CHARPTR, ValidateDnsServer},
- {"dns", "timeout", "5", new ValueContainerInt (&this->dns_timeout), DT_INTEGER, NoValidation},
- {"options", "moduledir", MOD_PATH, new ValueContainerChar (this->ModPath), DT_CHARPTR, NoValidation},
- {"disabled", "commands", "", new ValueContainerChar (this->DisabledCommands), DT_CHARPTR, NoValidation},
- {"options", "userstats", "", new ValueContainerChar (this->UserStats), DT_CHARPTR, NoValidation},
- {"options", "customversion","", new ValueContainerChar (this->CustomVersion), DT_CHARPTR, NoValidation},
- {"options", "hidesplits", "0", new ValueContainerBool (&this->HideSplits), DT_BOOLEAN, NoValidation},
- {"options", "hidebans", "0", new ValueContainerBool (&this->HideBans), DT_BOOLEAN, NoValidation},
- {"options", "hidewhois", "", new ValueContainerChar (this->HideWhoisServer), DT_CHARPTR, NoValidation},
- {"options", "hidekills", "", new ValueContainerChar (this->HideKillsServer), DT_CHARPTR, NoValidation},
- {"options", "operspywhois", "0", new ValueContainerBool (&this->OperSpyWhois), DT_BOOLEAN, NoValidation},
- {"options", "nouserdns", "0", new ValueContainerBool (&this->NoUserDns), DT_BOOLEAN, NoValidation},
- {"options", "syntaxhints", "0", new ValueContainerBool (&this->SyntaxHints), DT_BOOLEAN, NoValidation},
- {"options", "cyclehosts", "0", new ValueContainerBool (&this->CycleHosts), DT_BOOLEAN, NoValidation},
- {"options", "ircumsgprefix","0", new ValueContainerBool (&this->UndernetMsgPrefix), DT_BOOLEAN, NoValidation},
- {"options", "announceinvites", "1", new ValueContainerBool (&this->AnnounceInvites), DT_BOOLEAN, NoValidation},
- {"options", "hostintopic", "1", new ValueContainerBool (&this->FullHostInTopic), DT_BOOLEAN, NoValidation},
- {"options", "hidemodes", "", new ValueContainerChar (hidemodes), DT_CHARPTR, ValidateModeLists},
- {"options", "exemptchanops","", new ValueContainerChar (exemptchanops), DT_CHARPTR, ValidateExemptChanOps},
- {"options", "defaultmodes", "nt", new ValueContainerChar (this->DefaultModes), DT_CHARPTR, NoValidation},
- {"pid", "file", "", new ValueContainerChar (this->PID), DT_CHARPTR, NoValidation},
- {"whowas", "groupsize", "10", new ValueContainerInt (&this->WhoWasGroupSize), DT_INTEGER, NoValidation},
- {"whowas", "maxgroups", "10240", new ValueContainerInt (&this->WhoWasMaxGroups), DT_INTEGER, NoValidation},
- {"whowas", "maxkeep", "3600", new ValueContainerChar (maxkeep), DT_CHARPTR, ValidateWhoWas},
- {"die", "value", "", new ValueContainerChar (this->DieValue), DT_CHARPTR, NoValidation},
- {"channels", "users", "20", new ValueContainerUInt (&this->MaxChans), DT_INTEGER, NoValidation},
- {"channels", "opers", "60", new ValueContainerUInt (&this->OperMaxChans), DT_INTEGER, NoValidation},
- {NULL}
- };
-
- /* These tags can occur multiple times, and therefore they have special code to read them
- * which is different to the code for reading the singular tags listed above.
- */
- MultiConfig MultiValues[] = {
-
- {"connect",
- {"allow", "deny", "password", "timeout", "pingfreq", "flood",
- "threshold", "sendq", "recvq", "localmax", "globalmax", "port",
- NULL},
- {"", "", "", "", "120", "",
- "", "", "", "3", "3", "0",
- NULL},
- {DT_CHARPTR, DT_CHARPTR, DT_CHARPTR, DT_INTEGER, DT_INTEGER, DT_INTEGER,
- DT_INTEGER, DT_INTEGER, DT_INTEGER, DT_INTEGER, DT_INTEGER, DT_INTEGER},
- InitConnect, DoConnect, DoneConnect},
-
- {"uline",
- {"server", "silent", NULL},
- {"", "0", NULL},
- {DT_CHARPTR, DT_BOOLEAN},
- InitULine,DoULine,DoneULine},
-
- {"banlist",
- {"chan", "limit", NULL},
- {"", "", NULL},
- {DT_CHARPTR, DT_INTEGER},
- InitMaxBans, DoMaxBans, DoneMaxBans},
-
- {"module",
- {"name", NULL},
- {"", NULL},
- {DT_CHARPTR},
- InitModule, DoModule, DoneModule},
-
- {"badip",
- {"reason", "ipmask", NULL},
- {"No reason", "", NULL},
- {DT_CHARPTR, DT_CHARPTR},
- InitXLine, DoZLine, DoneZLine},
-
- {"badnick",
- {"reason", "nick", NULL},
- {"No reason", "", NULL},
- {DT_CHARPTR, DT_CHARPTR},
- InitXLine, DoQLine, DoneQLine},
-
- {"badhost",
- {"reason", "host", NULL},
- {"No reason", "", NULL},
- {DT_CHARPTR, DT_CHARPTR},
- InitXLine, DoKLine, DoneKLine},
-
- {"exception",
- {"reason", "host", NULL},
- {"No reason", "", NULL},
- {DT_CHARPTR, DT_CHARPTR},
- InitXLine, DoELine, DoneELine},
-
- {"type",
- {"name", "classes", NULL},
- {"", "", NULL},
- {DT_CHARPTR, DT_CHARPTR},
- InitTypes, DoType, DoneClassesAndTypes},
-
- {"class",
- {"name", "commands", NULL},
- {"", "", NULL},
- {DT_CHARPTR, DT_CHARPTR},
- InitClasses, DoClass, DoneClassesAndTypes},
-
- {NULL}
- };
-
- include_stack.clear();
-
- /* Load and parse the config file, if there are any errors then explode */
-
- /* Make a copy here so if it fails then we can carry on running with an unaffected config */
- ConfigDataHash newconfig;
-
- if (this->LoadConf(newconfig, ServerInstance->ConfigFileName, errstr))
- {
- /* If we succeeded, set the ircd config to the new one */
- this->config_data = newconfig;
- }
- else
- {
- ReportConfigError(errstr.str(), bail, user);
- return;
- }
-
- /* The stuff in here may throw CoreException, be sure we're in a position to catch it. */
- try
- {
- /* Check we dont have more than one of singular tags, or any of them missing
- */
- for (int Index = 0; Once[Index]; Index++)
- if (!CheckOnce(Once[Index], bail, user))
- return;
-
- /* Read the values of all the tags which occur once or not at all, and call their callbacks.
- */
- for (int Index = 0; Values[Index].tag; Index++)
- {
- char item[MAXBUF];
- int dt = Values[Index].datatype;
- bool allow_newlines = ((dt & DT_ALLOW_NEWLINE) > 0);
- dt &= ~DT_ALLOW_NEWLINE;
-
- ConfValue(this->config_data, Values[Index].tag, Values[Index].value, Values[Index].default_value, 0, item, MAXBUF, allow_newlines);
- ValueItem vi(item);
-
- if (!Values[Index].validation_function(this, Values[Index].tag, Values[Index].value, vi))
- throw CoreException("One or more values in your configuration file failed to validate. Please see your ircd.log for more information.");
-
- switch (Values[Index].datatype)
- {
- case DT_CHARPTR:
- {
- ValueContainerChar* vcc = (ValueContainerChar*)Values[Index].val;
- /* Make sure we also copy the null terminator */
- vcc->Set(vi.GetString(), strlen(vi.GetString()) + 1);
- }
- break;
- case DT_INTEGER:
- {
- int val = vi.GetInteger();
- ValueContainerInt* vci = (ValueContainerInt*)Values[Index].val;
- vci->Set(&val, sizeof(int));
- }
- break;
- case DT_BOOLEAN:
- {
- bool val = vi.GetBool();
- ValueContainerBool* vcb = (ValueContainerBool*)Values[Index].val;
- vcb->Set(&val, sizeof(bool));
- }
- break;
- default:
- /* You don't want to know what happens if someones bad code sends us here. */
- break;
- }
-
- /* We're done with this now */
- delete Values[Index].val;
- }
-
- /* Read the multiple-tag items (class tags, connect tags, etc)
- * and call the callbacks associated with them. We have three
- * callbacks for these, a 'start', 'item' and 'end' callback.
- */
- for (int Index = 0; MultiValues[Index].tag; Index++)
- {
- MultiValues[Index].init_function(this, MultiValues[Index].tag);
-
- int number_of_tags = ConfValueEnum(this->config_data, MultiValues[Index].tag);
-
- for (int tagnum = 0; tagnum < number_of_tags; tagnum++)
- {
- ValueList vl;
- for (int valuenum = 0; MultiValues[Index].items[valuenum]; valuenum++)
- {
- int dt = MultiValues[Index].datatype[valuenum];
- bool allow_newlines = ((dt & DT_ALLOW_NEWLINE) > 0);
- dt &= ~DT_ALLOW_NEWLINE;
-
- switch (dt)
- {
- case DT_CHARPTR:
- {
- char item[MAXBUF];
- if (ConfValue(this->config_data, MultiValues[Index].tag, MultiValues[Index].items[valuenum], MultiValues[Index].items_default[valuenum], tagnum, item, MAXBUF, allow_newlines))
- vl.push_back(ValueItem(item));
- else
- vl.push_back(ValueItem(""));
- }
- break;
- case DT_INTEGER:
- {
- int item = 0;
- if (ConfValueInteger(this->config_data, MultiValues[Index].tag, MultiValues[Index].items[valuenum], MultiValues[Index].items_default[valuenum], tagnum, item))
- vl.push_back(ValueItem(item));
- else
- vl.push_back(ValueItem(0));
- }
- break;
- case DT_BOOLEAN:
- {
- bool item = ConfValueBool(this->config_data, MultiValues[Index].tag, MultiValues[Index].items[valuenum], MultiValues[Index].items_default[valuenum], tagnum);
- vl.push_back(ValueItem(item));
- }
- break;
- default:
- /* Someone was smoking craq if we got here, and we're all gonna die. */
- break;
- }
- }
-
- MultiValues[Index].validation_function(this, MultiValues[Index].tag, (char**)MultiValues[Index].items, vl, MultiValues[Index].datatype);
- }
-
- MultiValues[Index].finish_function(this, MultiValues[Index].tag);
- }
-
- }
-
- catch (CoreException &ce)
- {
- ReportConfigError(ce.GetReason(), bail, user);
- return;
- }
-
- // write once here, to try it out and make sure its ok
- ServerInstance->WritePID(this->PID);
-
- ServerInstance->Log(DEFAULT,"Done reading configuration file.");
-
- /* If we're rehashing, let's load any new modules, and unload old ones
- */
- if (!bail)
- {
- int found_ports = 0;
- FailedPortList pl;
- ServerInstance->BindPorts(false, found_ports, pl);
-
- if (pl.size())
- {
- user->WriteServ("NOTICE %s :*** Not all your client ports could be bound.", user->nick);
- user->WriteServ("NOTICE %s :*** The following port(s) failed to bind:", user->nick);
- int j = 1;
- for (FailedPortList::iterator i = pl.begin(); i != pl.end(); i++, j++)
- {
- user->WriteServ("NOTICE %s :*** %d. IP: %s Port: %lu", user->nick, j, i->first.empty() ? "<all>" : i->first.c_str(), (unsigned long)i->second);
- }
- }
-
- if (!removed_modules.empty())
- {
- for (std::vector<std::string>::iterator removing = removed_modules.begin(); removing != removed_modules.end(); removing++)
- {
- if (ServerInstance->UnloadModule(removing->c_str()))
- {
- ServerInstance->WriteOpers("*** REHASH UNLOADED MODULE: %s",removing->c_str());
-
- if (user)
- user->WriteServ("973 %s %s :Module %s successfully unloaded.",user->nick, removing->c_str(), removing->c_str());
-
- rem++;
- }
- else
- {
- if (user)
- user->WriteServ("972 %s %s :Failed to unload module %s: %s",user->nick, removing->c_str(), removing->c_str(), ServerInstance->ModuleError());
- }
- }
- }
-
- if (!added_modules.empty())
- {
- for (std::vector<std::string>::iterator adding = added_modules.begin(); adding != added_modules.end(); adding++)
- {
- if (ServerInstance->LoadModule(adding->c_str()))
- {
- ServerInstance->WriteOpers("*** REHASH LOADED MODULE: %s",adding->c_str());
-
- if (user)
- user->WriteServ("975 %s %s :Module %s successfully loaded.",user->nick, adding->c_str(), adding->c_str());
-
- add++;
- }
- else
- {
- if (user)
- user->WriteServ("974 %s %s :Failed to load module %s: %s",user->nick, adding->c_str(), adding->c_str(), ServerInstance->ModuleError());
- }
- }
- }
-
- ServerInstance->Log(DEFAULT,"Successfully unloaded %lu of %lu modules and loaded %lu of %lu modules.",(unsigned long)rem,(unsigned long)removed_modules.size(),(unsigned long)add,(unsigned long)added_modules.size());
- }
-
- if (user)
- user->WriteServ("NOTICE %s :*** Successfully rehashed server.", user->nick);
- else
- ServerInstance->WriteOpers("*** Successfully rehashed server.");
-}
-
-bool ServerConfig::LoadConf(ConfigDataHash &target, const char* filename, std::ostringstream &errorstream)
-{
- std::ifstream conf(filename);
- std::string line;
- char ch;
- long linenumber;
- bool in_tag;
- bool in_quote;
- bool in_comment;
- int character_count = 0;
-
- linenumber = 1;
- in_tag = false;
- in_quote = false;
- in_comment = false;
-
- /* Check if the file open failed first */
- if (!conf)
- {
- errorstream << "LoadConf: Couldn't open config file: " << filename << std::endl;
- return false;
- }
-
- /* Fix the chmod of the file to restrict it to the current user and group */
- chmod(filename,0600);
-
- for (unsigned int t = 0; t < include_stack.size(); t++)
- {
- if (std::string(filename) == include_stack[t])
- {
- errorstream << "File " << filename << " is included recursively (looped inclusion)." << std::endl;
- return false;
- }
- }
-
- /* It's not already included, add it to the list of files we've loaded */
- include_stack.push_back(filename);
-
- /* Start reading characters... */
- while(conf.get(ch))
- {
-
- /*
- * Fix for moronic windows issue spotted by Adremelech.
- * Some windows editors save text files as utf-16, which is
- * a total pain in the ass to parse. Users should save in the
- * right config format! If we ever see a file where the first
- * byte is 0xFF or 0xFE, or the second is 0xFF or 0xFE, then
- * this is most likely a utf-16 file. Bail out and insult user.
- */
- if ((character_count++ < 2) && (ch == '\xFF' || ch == '\xFE'))
- {
- errorstream << "File " << filename << " cannot be read, as it is encoded in braindead UTF-16. Save your file as plain ASCII!" << std::endl;
- return false;
- }
-
- /*
- * Here we try and get individual tags on separate lines,
- * this would be so easy if we just made people format
- * their config files like that, but they don't so...
- * We check for a '<' and then know the line is over when
- * we get a '>' not inside quotes. If we find two '<' and
- * no '>' then die with an error.
- */
-
- if((ch == '#') && !in_quote)
- in_comment = true;
-
- switch(ch)
- {
- case '\n':
- if (in_quote)
- line += '\n';
- linenumber++;
- case '\r':
- if (!in_quote)
- in_comment = false;
- case '\0':
- continue;
- case '\t':
- ch = ' ';
- }
-
- if(in_comment)
- continue;
-
- /* XXX: Added by Brain, May 1st 2006 - Escaping of characters.
- * Note that this WILL NOT usually allow insertion of newlines,
- * because a newline is two characters long. Use it primarily to
- * insert the " symbol.
- *
- * Note that this also involves a further check when parsing the line,
- * which can be found below.
- */
- if ((ch == '\\') && (in_quote) && (in_tag))
- {
- line += ch;
- char real_character;
- if (conf.get(real_character))
- {
- if (real_character == 'n')
- real_character = '\n';
- line += real_character;
- continue;
- }
- else
- {
- errorstream << "End of file after a \\, what did you want to escape?: " << filename << ":" << linenumber << std::endl;
- return false;
- }
- }
-
- if (ch != '\r')
- line += ch;
-
- if(ch == '<')
- {
- if(in_tag)
- {
- if(!in_quote)
- {
- errorstream << "Got another opening < when the first one wasn't closed: " << filename << ":" << linenumber << std::endl;
- return false;
- }
- }
- else
- {
- if(in_quote)
- {
- errorstream << "We're in a quote but outside a tag, interesting. " << filename << ":" << linenumber << std::endl;
- return false;
- }
- else
- {
- // errorstream << "Opening new config tag on line " << linenumber << std::endl;
- in_tag = true;
- }
- }
- }
- else if(ch == '"')
- {
- if(in_tag)
- {
- if(in_quote)
- {
- // errorstream << "Closing quote in config tag on line " << linenumber << std::endl;
- in_quote = false;
- }
- else
- {
- // errorstream << "Opening quote in config tag on line " << linenumber << std::endl;
- in_quote = true;
- }
- }
- else
- {
- if(in_quote)
- {
- errorstream << "Found a (closing) \" outside a tag: " << filename << ":" << linenumber << std::endl;
- }
- else
- {
- errorstream << "Found a (opening) \" outside a tag: " << filename << ":" << linenumber << std::endl;
- }
- }
- }
- else if(ch == '>')
- {
- if(!in_quote)
- {
- if(in_tag)
- {
- // errorstream << "Closing config tag on line " << linenumber << std::endl;
- in_tag = false;
-
- /*
- * If this finds an <include> then ParseLine can simply call
- * LoadConf() and load the included config into the same ConfigDataHash
- */
-
- if(!this->ParseLine(target, line, linenumber, errorstream))
- return false;
-
- line.clear();
- }
- else
- {
- errorstream << "Got a closing > when we weren't inside a tag: " << filename << ":" << linenumber << std::endl;
- return false;
- }
- }
- }
- }
-
- return true;
-}
-
-bool ServerConfig::LoadConf(ConfigDataHash &target, const std::string &filename, std::ostringstream &errorstream)
-{
- return this->LoadConf(target, filename.c_str(), errorstream);
-}
-
-bool ServerConfig::ParseLine(ConfigDataHash &target, std::string &line, long linenumber, std::ostringstream &errorstream)
-{
- std::string tagname;
- std::string current_key;
- std::string current_value;
- KeyValList results;
- bool got_name;
- bool got_key;
- bool in_quote;
-
- got_name = got_key = in_quote = false;
-
- //std::cout << "ParseLine(data, '" << line << "', " << linenumber << ", stream)" << std::endl;
-
- for(std::string::iterator c = line.begin(); c != line.end(); c++)
- {
- if(!got_name)
- {
- /* We don't know the tag name yet. */
-
- if(*c != ' ')
- {
- if(*c != '<')
- {
- tagname += *c;
- }
- }
- else
- {
- /* We got to a space, we should have the tagname now. */
- if(tagname.length())
- {
- got_name = true;
- }
- }
- }
- else
- {
- /* We have the tag name */
- if (!got_key)
- {
- /* We're still reading the key name */
- if (*c != '=')
- {
- if (*c != ' ')
- {
- current_key += *c;
- }
- }
- else
- {
- /* We got an '=', end of the key name. */
- got_key = true;
- }
- }
- else
- {
- /* We have the key name, now we're looking for quotes and the value */
-
- /* Correctly handle escaped characters here.
- * See the XXX'ed section above.
- */
- if ((*c == '\\') && (in_quote))
- {
- c++;
- if (*c == 'n')
- current_value += '\n';
- else
- current_value += *c;
- continue;
- }
- else if ((*c == '\n') && (in_quote))
- {
- /* Got a 'real' \n, treat it as part of the value */
- current_value += '\n';
- continue;
- }
- else if ((*c == '\r') && (in_quote))
- /* Got a \r, drop it */
- continue;
-
- if (*c == '"')
- {
- if (!in_quote)
- {
- /* We're not already in a quote. */
- in_quote = true;
- }
- else
- {
- /* Leaving quotes, we have the value */
- results.push_back(KeyVal(current_key, current_value));
-
- // std::cout << "<" << tagname << ":" << current_key << "> " << current_value << std::endl;
-
- in_quote = false;
- got_key = false;
-
- if((tagname == "include") && (current_key == "file"))
- {
- if(!this->DoInclude(target, current_value, errorstream))
- return false;
- }
-
- current_key.clear();
- current_value.clear();
- }
- }
- else
- {
- if(in_quote)
- {
- current_value += *c;
- }
- }
- }
- }
- }
-
- /* Finished parsing the tag, add it to the config hash */
- target.insert(std::pair<std::string, KeyValList > (tagname, results));
-
- return true;
-}
-
-bool ServerConfig::DoInclude(ConfigDataHash &target, const std::string &file, std::ostringstream &errorstream)
-{
- std::string confpath;
- std::string newfile;
- std::string::size_type pos;
-
- confpath = ServerInstance->ConfigFileName;
- newfile = file;
-
- for (std::string::iterator c = newfile.begin(); c != newfile.end(); c++)
- {
- if (*c == '\\')
- {
- *c = '/';
- }
- }
-
- if (file[0] != '/')
- {
- if((pos = confpath.rfind("/")) != std::string::npos)
- {
- /* Leaves us with just the path */
- newfile = confpath.substr(0, pos) + std::string("/") + newfile;
- }
- else
- {
- errorstream << "Couldn't get config path from: " << confpath << std::endl;
- return false;
- }
- }
-
- return LoadConf(target, newfile, errorstream);
-}
-
-bool ServerConfig::ConfValue(ConfigDataHash &target, const char* tag, const char* var, int index, char* result, int length, bool allow_linefeeds)
-{
- return ConfValue(target, tag, var, "", index, result, length, allow_linefeeds);
-}
-
-bool ServerConfig::ConfValue(ConfigDataHash &target, const char* tag, const char* var, const char* default_value, int index, char* result, int length, bool allow_linefeeds)
-{
- std::string value;
- bool r = ConfValue(target, std::string(tag), std::string(var), std::string(default_value), index, value, allow_linefeeds);
- strlcpy(result, value.c_str(), length);
- return r;
-}
-
-bool ServerConfig::ConfValue(ConfigDataHash &target, const std::string &tag, const std::string &var, int index, std::string &result, bool allow_linefeeds)
-{
- return ConfValue(target, tag, var, "", index, result, allow_linefeeds);
-}
-
-bool ServerConfig::ConfValue(ConfigDataHash &target, const std::string &tag, const std::string &var, const std::string &default_value, int index, std::string &result, bool allow_linefeeds)
-{
- ConfigDataHash::size_type pos = index;
- if((pos >= 0) && (pos < target.count(tag)))
- {
- ConfigDataHash::iterator iter = target.find(tag);
-
- for(int i = 0; i < index; i++)
- iter++;
-
- for(KeyValList::iterator j = iter->second.begin(); j != iter->second.end(); j++)
- {
- if(j->first == var)
- {
- if ((!allow_linefeeds) && (j->second.find('\n') != std::string::npos))
- {
- ServerInstance->Log(DEFAULT, "Value of <" + tag + ":" + var+ "> contains a linefeed, and linefeeds in this value are not permitted -- stripped to spaces.");
- for (std::string::iterator n = j->second.begin(); n != j->second.end(); n++)
- if (*n == '\n')
- *n = ' ';
- }
- else
- {
- result = j->second;
- return true;
- }
- }
- }
- if (!default_value.empty())
- {
- result = default_value;
- return true;
- }
- }
- else if(pos == 0)
- {
- if (!default_value.empty())
- {
- result = default_value;
- return true;
- }
- }
- return false;
-}
-
-bool ServerConfig::ConfValueInteger(ConfigDataHash &target, const char* tag, const char* var, int index, int &result)
-{
- return ConfValueInteger(target, std::string(tag), std::string(var), "", index, result);
-}
-
-bool ServerConfig::ConfValueInteger(ConfigDataHash &target, const char* tag, const char* var, const char* default_value, int index, int &result)
-{
- return ConfValueInteger(target, std::string(tag), std::string(var), std::string(default_value), index, result);
-}
-
-bool ServerConfig::ConfValueInteger(ConfigDataHash &target, const std::string &tag, const std::string &var, int index, int &result)
-{
- return ConfValueInteger(target, tag, var, "", index, result);
-}
-
-bool ServerConfig::ConfValueInteger(ConfigDataHash &target, const std::string &tag, const std::string &var, const std::string &default_value, int index, int &result)
-{
- std::string value;
- std::istringstream stream;
- bool r = ConfValue(target, tag, var, default_value, index, value);
- stream.str(value);
- if(!(stream >> result))
- return false;
- else
- {
- if (!value.empty())
- {
- if (value.substr(0,2) == "0x")
- {
- char* endptr;
-
- value.erase(0,2);
- result = strtol(value.c_str(), &endptr, 16);
-
- /* No digits found */
- if (endptr == value.c_str())
- return false;
- }
- else
- {
- char denominator = *(value.end() - 1);
- switch (toupper(denominator))
- {
- case 'K':
- /* Kilobytes -> bytes */
- result = result * 1024;
- break;
- case 'M':
- /* Megabytes -> bytes */
- result = result * 1024 * 1024;
- break;
- case 'G':
- /* Gigabytes -> bytes */
- result = result * 1024 * 1024 * 1024;
- break;
- }
- }
- }
- }
- return r;
-}
-
-
-bool ServerConfig::ConfValueBool(ConfigDataHash &target, const char* tag, const char* var, int index)
-{
- return ConfValueBool(target, std::string(tag), std::string(var), "", index);
-}
-
-bool ServerConfig::ConfValueBool(ConfigDataHash &target, const char* tag, const char* var, const char* default_value, int index)
-{
- return ConfValueBool(target, std::string(tag), std::string(var), std::string(default_value), index);
-}
-
-bool ServerConfig::ConfValueBool(ConfigDataHash &target, const std::string &tag, const std::string &var, int index)
-{
- return ConfValueBool(target, tag, var, "", index);
-}
-
-bool ServerConfig::ConfValueBool(ConfigDataHash &target, const std::string &tag, const std::string &var, const std::string &default_value, int index)
-{
- std::string result;
- if(!ConfValue(target, tag, var, default_value, index, result))
- return false;
-
- return ((result == "yes") || (result == "true") || (result == "1"));
-}
-
-int ServerConfig::ConfValueEnum(ConfigDataHash &target, const char* tag)
-{
- return target.count(tag);
-}
-
-int ServerConfig::ConfValueEnum(ConfigDataHash &target, const std::string &tag)
-{
- return target.count(tag);
-}
-
-int ServerConfig::ConfVarEnum(ConfigDataHash &target, const char* tag, int index)
-{
- return ConfVarEnum(target, std::string(tag), index);
-}
-
-int ServerConfig::ConfVarEnum(ConfigDataHash &target, const std::string &tag, int index)
-{
- ConfigDataHash::size_type pos = index;
-
- if((pos >= 0) && (pos < target.count(tag)))
- {
- ConfigDataHash::const_iterator iter = target.find(tag);
-
- for(int i = 0; i < index; i++)
- iter++;
-
- return iter->second.size();
- }
-
- return 0;
-}
-
-/** Read the contents of a file located by `fname' into a file_cache pointed at by `F'.
- */
-bool ServerConfig::ReadFile(file_cache &F, const char* fname)
-{
- if (!fname || !*fname)
- return false;
-
- FILE* file = NULL;
- char linebuf[MAXBUF];
-
- F.clear();
-
- if ((*fname != '/') && (*fname != '\\'))
- {
- std::string::size_type pos;
- std::string confpath = ServerInstance->ConfigFileName;
- std::string newfile = fname;
-
- if ((pos = confpath.rfind("/")) != std::string::npos)
- newfile = confpath.substr(0, pos) + std::string("/") + fname;
- else if ((pos = confpath.rfind("\\")) != std::string::npos)
- newfile = confpath.substr(0, pos) + std::string("\\") + fname;
-
- if (!FileExists(newfile.c_str()))
- return false;
- file = fopen(newfile.c_str(), "r");
- }
- else
- {
- if (!FileExists(fname))
- return false;
- file = fopen(fname, "r");
- }
-
- if (file)
- {
- while (!feof(file))
- {
- if (fgets(linebuf, sizeof(linebuf), file))
- linebuf[strlen(linebuf)-1] = 0;
- else
- *linebuf = 0;
-
- if (!feof(file))
- {
- F.push_back(*linebuf ? linebuf : " ");
- }
- }
-
- fclose(file);
- }
- else
- return false;
-
- return true;
-}
-
-bool ServerConfig::FileExists(const char* file)
-{
- struct stat sb;
- if (stat(file, &sb) == -1)
- return false;
-
- if ((sb.st_mode & S_IFDIR) > 0)
- return false;
-
- FILE *input;
- if ((input = fopen (file, "r")) == NULL)
- return false;
- else
- {
- fclose(input);
- return true;
- }
-}
-
-char* ServerConfig::CleanFilename(char* name)
-{
- char* p = name + strlen(name);
- while ((p != name) && (*p != '/') && (*p != '\\')) p--;
- return (p != name ? ++p : p);
-}
-
-
-bool ServerConfig::DirValid(const char* dirandfile)
-{
-#ifdef WINDOWS
- return true;
-#endif
-
- char work[1024];
- char buffer[1024];
- char otherdir[1024];
- int p;
-
- strlcpy(work, dirandfile, 1024);
- p = strlen(work);
-
- // we just want the dir
- while (*work)
- {
- if (work[p] == '/')
- {
- work[p] = '\0';
- break;
- }
-
- work[p--] = '\0';
- }
-
- // Get the current working directory
- if (getcwd(buffer, 1024 ) == NULL )
- return false;
-
- if (chdir(work) == -1)
- return false;
-
- if (getcwd(otherdir, 1024 ) == NULL )
- return false;
-
- if (chdir(buffer) == -1)
- return false;
-
- size_t t = strlen(work);
-
- if (strlen(otherdir) >= t)
- {
- otherdir[t] = '\0';
- if (!strcmp(otherdir,work))
- {
- return true;
- }
-
- return false;
- }
- else
- {
- return false;
- }
-}
-
-std::string ServerConfig::GetFullProgDir()
-{
- char buffer[PATH_MAX+1];
-#ifdef WINDOWS
- /* Windows has specific api calls to get the exe path that never fail.
- * For once, windows has something of use, compared to the POSIX code
- * for this, this is positively neato.
- */
- if (GetModuleFileName(NULL, buffer, MAX_PATH))
- {
- std::string fullpath = buffer;
- std::string::size_type n = fullpath.rfind("\\inspircd.exe");
- return std::string(fullpath, 0, n);
- }
-#else
- // Get the current working directory
- if (getcwd(buffer, PATH_MAX))
- {
- std::string remainder = this->argv[0];
-
- /* Does argv[0] start with /? its a full path, use it */
- if (remainder[0] == '/')
- {
- std::string::size_type n = remainder.rfind("/inspircd");
- return std::string(remainder, 0, n);
- }
-
- std::string fullpath = std::string(buffer) + "/" + remainder;
- std::string::size_type n = fullpath.rfind("/inspircd");
- return std::string(fullpath, 0, n);
- }
-#endif
- return "/";
-}
-
-InspIRCd* ServerConfig::GetInstance()
-{
- return ServerInstance;
-}
-
-
-ValueItem::ValueItem(int value)
-{
- std::stringstream n;
- n << value;
- v = n.str();
-}
-
-ValueItem::ValueItem(bool value)
-{
- std::stringstream n;
- n << value;
- v = n.str();
-}
-
-ValueItem::ValueItem(char* value)
-{
- v = value;
-}
-
-void ValueItem::Set(char* value)
-{
- v = value;
-}
-
-void ValueItem::Set(const char* value)
-{
- v = value;
-}
-
-void ValueItem::Set(int value)
-{
- std::stringstream n;
- n << value;
- v = n.str();
-}
-
-int ValueItem::GetInteger()
-{
- if (v.empty())
- return 0;
- return atoi(v.c_str());
-}
-
-char* ValueItem::GetString()
-{
- return (char*)v.c_str();
-}
-
-bool ValueItem::GetBool()
-{
- return (GetInteger() || v == "yes" || v == "true");
-}
-
+/* +------------------------------------+\r * | Inspire Internet Relay Chat Daemon |\r * +------------------------------------+\r *\r * InspIRCd: (C) 2002-2007 InspIRCd Development Team\r * See: http://www.inspircd.org/wiki/index.php/Credits\r *\r * This program is free but copyrighted software; see\r * the file COPYING for details.\r *\r * ---------------------------------------------------\r */\r\r#include "inspircd.h"\r#include "configreader.h"\r#include <sstream>\r#include <fstream>\r#include "xline.h"\r#include "exitcodes.h"\r#include "commands/cmd_whowas.h"\r\rstd::vector<std::string> old_module_names, new_module_names, added_modules, removed_modules;\r\rServerConfig::ServerConfig(InspIRCd* Instance) : ServerInstance(Instance)\r{\r this->ClearStack();\r *ServerName = *Network = *ServerDesc = *AdminName = '\0';\r *HideWhoisServer = *AdminEmail = *AdminNick = *diepass = *restartpass = *FixedQuit = *HideKillsServer = '\0';\r *DefaultModes = *CustomVersion = *motd = *rules = *PrefixQuit = *DieValue = *DNSServer = '\0';\r *UserStats = *ModPath = *MyExecutable = *DisabledCommands = *PID = *SuffixQuit = '\0';\r WhoWasGroupSize = WhoWasMaxGroups = WhoWasMaxKeep = 0;\r log_file = NULL;\r NoUserDns = forcedebug = OperSpyWhois = nofork = HideBans = HideSplits = UndernetMsgPrefix = false;\r CycleHosts = writelog = AllowHalfop = true;\r dns_timeout = DieDelay = 5;\r MaxTargets = 20;\r NetBufferSize = 10240;\r SoftLimit = MAXCLIENTS;\r MaxConn = SOMAXCONN;\r MaxWhoResults = 0;\r debugging = 0;\r MaxChans = 20;\r OperMaxChans = 30;\r LogLevel = DEFAULT;\r maxbans.clear();\r}\r\rvoid ServerConfig::ClearStack()\r{\r include_stack.clear();\r}\r\rModule* ServerConfig::GetIOHook(int port)\r{\r std::map<int,Module*>::iterator x = IOHookModule.find(port);\r return (x != IOHookModule.end() ? x->second : NULL);\r}\r\rModule* ServerConfig::GetIOHook(InspSocket* is)\r{\r std::map<InspSocket*,Module*>::iterator x = SocketIOHookModule.find(is);\r return (x != SocketIOHookModule.end() ? x->second : NULL);\r}\r\rbool ServerConfig::AddIOHook(int port, Module* iomod)\r{\r if (!GetIOHook(port))\r {\r IOHookModule[port] = iomod;\r return true;\r }\r else\r {\r throw ModuleException("Port already hooked by another module");\r return false;\r }\r}\r\rbool ServerConfig::AddIOHook(Module* iomod, InspSocket* is)\r{\r if (!GetIOHook(is))\r {\r SocketIOHookModule[is] = iomod;\r is->IsIOHooked = true;\r return true;\r }\r else\r {\r throw ModuleException("InspSocket derived class already hooked by another module");\r return false;\r }\r}\r\rbool ServerConfig::DelIOHook(int port)\r{\r std::map<int,Module*>::iterator x = IOHookModule.find(port);\r if (x != IOHookModule.end())\r {\r IOHookModule.erase(x);\r return true;\r }\r return false;\r}\r\rbool ServerConfig::DelIOHook(InspSocket* is)\r{\r std::map<InspSocket*,Module*>::iterator x = SocketIOHookModule.find(is);\r if (x != SocketIOHookModule.end())\r {\r SocketIOHookModule.erase(x);\r return true;\r }\r return false;\r}\r\rvoid ServerConfig::Update005()\r{\r std::stringstream out(data005);\r std::string token;\r std::string line5;\r int token_counter = 0;\r isupport.clear();\r while (out >> token)\r {\r line5 = line5 + token + " ";\r token_counter++;\r if (token_counter >= 13)\r {\r char buf[MAXBUF];\r snprintf(buf, MAXBUF, "%s:are supported by this server", line5.c_str());\r isupport.push_back(buf);\r line5.clear();\r token_counter = 0;\r }\r }\r if (!line5.empty())\r {\r char buf[MAXBUF];\r snprintf(buf, MAXBUF, "%s:are supported by this server", line5.c_str());\r isupport.push_back(buf);\r }\r}\r\rvoid ServerConfig::Send005(userrec* user)\r{\r for (std::vector<std::string>::iterator line = ServerInstance->Config->isupport.begin(); line != ServerInstance->Config->isupport.end(); line++)\r user->WriteServ("005 %s %s", user->nick, line->c_str());\r}\r\rbool ServerConfig::CheckOnce(char* tag, bool bail, userrec* user)\r{\r int count = ConfValueEnum(this->config_data, tag);\r\r if (count > 1)\r {\r throw CoreException("You have more than one <"+std::string(tag)+"> tag, this is not permitted.");\r return false;\r }\r if (count < 1)\r {\r throw CoreException("You have not defined a <"+std::string(tag)+"> tag, this is required.");\r return false;\r }\r return true;\r}\r\rbool NoValidation(ServerConfig* conf, const char* tag, const char* value, ValueItem &data)\r{\r return true;\r}\r\rbool ValidateMaxTargets(ServerConfig* conf, const char* tag, const char* value, ValueItem &data)\r{\r if ((data.GetInteger() < 0) || (data.GetInteger() > 31))\r {\r conf->GetInstance()->Log(DEFAULT,"WARNING: <options:maxtargets> value is greater than 31 or less than 0, set to 20.");\r data.Set(20);\r }\r return true;\r}\r\rbool ValidateSoftLimit(ServerConfig* conf, const char* tag, const char* value, ValueItem &data)\r{\r if ((data.GetInteger() < 1) || (data.GetInteger() > MAXCLIENTS))\r {\r conf->GetInstance()->Log(DEFAULT,"WARNING: <options:softlimit> value is greater than %d or less than 0, set to %d.",MAXCLIENTS,MAXCLIENTS);\r data.Set(MAXCLIENTS);\r }\r return true;\r}\r\rbool ValidateMaxConn(ServerConfig* conf, const char* tag, const char* value, ValueItem &data)\r{\r if (data.GetInteger() > SOMAXCONN)\r conf->GetInstance()->Log(DEFAULT,"WARNING: <options:somaxconn> value may be higher than the system-defined SOMAXCONN value!");\r return true;\r}\r\rbool InitializeDisabledCommands(const char* data, InspIRCd* ServerInstance)\r{\r std::stringstream dcmds(data);\r std::string thiscmd;\r\r /* Enable everything first */\r for (command_table::iterator x = ServerInstance->Parser->cmdlist.begin(); x != ServerInstance->Parser->cmdlist.end(); x++)\r x->second->Disable(false);\r\r /* Now disable all the ones which the user wants disabled */\r while (dcmds >> thiscmd)\r {\r command_table::iterator cm = ServerInstance->Parser->cmdlist.find(thiscmd);\r if (cm != ServerInstance->Parser->cmdlist.end())\r {\r cm->second->Disable(true);\r }\r }\r return true;\r}\r\rbool ValidateDnsServer(ServerConfig* conf, const char* tag, const char* value, ValueItem &data)\r{\r if (!*(data.GetString()))\r {\r std::string nameserver;\r#ifdef WINDOWS\r conf->GetInstance()->Log(DEFAULT,"WARNING: <dns:server> not defined, attempting to find working server in the registry...");\r nameserver = FindNameServerWin();\r /* Windows stacks multiple nameservers in one registry key, seperated by commas.\r * Spotted by Cataclysm.\r */\r if (nameserver.find(',') != std::string::npos)\r nameserver = nameserver.substr(0, nameserver.find(','));\r data.Set(nameserver.c_str());\r conf->GetInstance()->Log(DEFAULT,"<dns:server> set to '%s' as first active resolver in registry.", nameserver.c_str());\r#else\r // attempt to look up their nameserver from /etc/resolv.conf\r conf->GetInstance()->Log(DEFAULT,"WARNING: <dns:server> not defined, attempting to find working server in /etc/resolv.conf...");\r ifstream resolv("/etc/resolv.conf");\r bool found_server = false;\r\r if (resolv.is_open())\r {\r while (resolv >> nameserver)\r {\r if ((nameserver == "nameserver") && (!found_server))\r {\r resolv >> nameserver;\r data.Set(nameserver.c_str());\r found_server = true;\r conf->GetInstance()->Log(DEFAULT,"<dns:server> set to '%s' as first resolver in /etc/resolv.conf.",nameserver.c_str());\r }\r }\r\r if (!found_server)\r {\r conf->GetInstance()->Log(DEFAULT,"/etc/resolv.conf contains no viable nameserver entries! Defaulting to nameserver '127.0.0.1'!");\r data.Set("127.0.0.1");\r }\r }\r else\r {\r conf->GetInstance()->Log(DEFAULT,"/etc/resolv.conf can't be opened! Defaulting to nameserver '127.0.0.1'!");\r data.Set("127.0.0.1");\r }\r#endif\r }\r return true;\r}\r\rbool ValidateServerName(ServerConfig* conf, const char* tag, const char* value, ValueItem &data)\r{\r /* If we already have a servername, and they changed it, we should throw an exception. */\r if ((strcasecmp(conf->ServerName, data.GetString())) && (*conf->ServerName))\r {\r throw CoreException("Configuration error: You cannot change your servername at runtime! Please restart your server for this change to be applied.");\r /* XXX: We don't actually reach this return of course... */\r return false;\r }\r if (!strchr(data.GetString(),'.'))\r {\r conf->GetInstance()->Log(DEFAULT,"WARNING: <server:name> '%s' is not a fully-qualified domain name. Changed to '%s%c'",data.GetString(),data.GetString(),'.');\r std::string moo = std::string(data.GetString()).append(".");\r data.Set(moo.c_str());\r }\r return true;\r}\r\rbool ValidateNetBufferSize(ServerConfig* conf, const char* tag, const char* value, ValueItem &data)\r{\r if ((!data.GetInteger()) || (data.GetInteger() > 65535) || (data.GetInteger() < 1024))\r {\r conf->GetInstance()->Log(DEFAULT,"No NetBufferSize specified or size out of range, setting to default of 10240.");\r data.Set(10240);\r }\r return true;\r}\r\rbool ValidateMaxWho(ServerConfig* conf, const char* tag, const char* value, ValueItem &data)\r{\r if ((data.GetInteger() > 65535) || (data.GetInteger() < 1))\r {\r conf->GetInstance()->Log(DEFAULT,"<options:maxwhoresults> size out of range, setting to default of 128.");\r data.Set(128);\r }\r return true;\r}\r\rbool ValidateLogLevel(ServerConfig* conf, const char* tag, const char* value, ValueItem &data)\r{\r std::string dbg = data.GetString();\r conf->LogLevel = DEFAULT;\r\r if (dbg == "debug")\r conf->LogLevel = DEBUG;\r else if (dbg == "verbose")\r conf->LogLevel = VERBOSE;\r else if (dbg == "default")\r conf->LogLevel = DEFAULT;\r else if (dbg == "sparse")\r conf->LogLevel = SPARSE;\r else if (dbg == "none")\r conf->LogLevel = NONE;\r\r conf->debugging = (conf->LogLevel == DEBUG);\r\r return true;\r}\r\rbool ValidateMotd(ServerConfig* conf, const char* tag, const char* value, ValueItem &data)\r{\r conf->ReadFile(conf->MOTD, data.GetString());\r return true;\r}\r\rbool ValidateNotEmpty(ServerConfig* conf, const char* tag, const char* value, ValueItem &data)\r{\r if (!*data.GetString())\r throw CoreException(std::string("The value for ")+tag+" cannot be empty!");\r return true;\r}\r\rbool ValidateRules(ServerConfig* conf, const char* tag, const char* value, ValueItem &data)\r{\r conf->ReadFile(conf->RULES, data.GetString());\r return true;\r}\r\rbool ValidateModeLists(ServerConfig* conf, const char* tag, const char* value, ValueItem &data)\r{\r memset(conf->HideModeLists, 0, 256);\r for (const unsigned char* x = (const unsigned char*)data.GetString(); *x; ++x)\r conf->HideModeLists[*x] = true;\r return true;\r}\r\rbool ValidateExemptChanOps(ServerConfig* conf, const char* tag, const char* value, ValueItem &data)\r{\r memset(conf->ExemptChanOps, 0, 256);\r for (const unsigned char* x = (const unsigned char*)data.GetString(); *x; ++x)\r conf->ExemptChanOps[*x] = true;\r return true;\r}\r\rbool ValidateWhoWas(ServerConfig* conf, const char* tag, const char* value, ValueItem &data)\r{\r conf->WhoWasMaxKeep = conf->GetInstance()->Duration(data.GetString());\r\r if (conf->WhoWasGroupSize < 0)\r conf->WhoWasGroupSize = 0;\r\r if (conf->WhoWasMaxGroups < 0)\r conf->WhoWasMaxGroups = 0;\r\r if (conf->WhoWasMaxKeep < 3600)\r {\r conf->WhoWasMaxKeep = 3600;\r conf->GetInstance()->Log(DEFAULT,"WARNING: <whowas:maxkeep> value less than 3600, setting to default 3600");\r }\r\r command_t* whowas_command = conf->GetInstance()->Parser->GetHandler("WHOWAS");\r if (whowas_command)\r {\r std::deque<classbase*> params;\r whowas_command->HandleInternal(WHOWAS_PRUNE, params);\r }\r\r return true;\r}\r\r/* Callback called before processing the first <connect> tag\r */\rbool InitConnect(ServerConfig* conf, const char* tag)\r{\r conf->GetInstance()->Log(DEFAULT,"Reading connect classes...");\r conf->Classes.clear();\r return true;\r}\r\r/* Callback called to process a single <connect> tag\r */\rbool DoConnect(ServerConfig* conf, const char* tag, char** entries, ValueList &values, int* types)\r{\r ConnectClass c;\r const char* allow = values[0].GetString(); /* Yeah, there are a lot of values. Live with it. */\r const char* deny = values[1].GetString();\r const char* password = values[2].GetString();\r int timeout = values[3].GetInteger();\r int pingfreq = values[4].GetInteger();\r int flood = values[5].GetInteger();\r int threshold = values[6].GetInteger();\r int sendq = values[7].GetInteger();\r int recvq = values[8].GetInteger();\r int localmax = values[9].GetInteger();\r int globalmax = values[10].GetInteger();\r\r if (*allow)\r {\r ConnectClass c(timeout, flood, allow, pingfreq, password, threshold, sendq, recvq, localmax, globalmax);\r conf->Classes.push_back(c);\r }\r else\r {\r ConnectClass c(deny);\r conf->Classes.push_back(c);\r }\r\r return true;\r}\r\r/* Callback called when there are no more <connect> tags\r */\rbool DoneConnect(ServerConfig* conf, const char* tag)\r{\r return true;\r}\r\r/* Callback called before processing the first <uline> tag\r */\rbool InitULine(ServerConfig* conf, const char* tag)\r{\r conf->ulines.clear();\r return true;\r}\r\r/* Callback called to process a single <uline> tag\r */\rbool DoULine(ServerConfig* conf, const char* tag, char** entries, ValueList &values, int* types)\r{\r const char* server = values[0].GetString();\r const bool silent = values[1].GetBool();\r conf->ulines[server] = silent;\r return true;\r}\r\r/* Callback called when there are no more <uline> tags\r */\rbool DoneULine(ServerConfig* conf, const char* tag)\r{\r return true;\r}\r\r/* Callback called before processing the first <module> tag\r */\rbool InitModule(ServerConfig* conf, const char* tag)\r{\r old_module_names.clear();\r new_module_names.clear();\r added_modules.clear();\r removed_modules.clear();\r for (std::vector<std::string>::iterator t = conf->module_names.begin(); t != conf->module_names.end(); t++)\r {\r old_module_names.push_back(*t);\r }\r return true;\r}\r\r/* Callback called to process a single <module> tag\r */\rbool DoModule(ServerConfig* conf, const char* tag, char** entries, ValueList &values, int* types)\r{\r const char* modname = values[0].GetString();\r new_module_names.push_back(modname);\r return true;\r}\r\r/* Callback called when there are no more <module> tags\r */\rbool DoneModule(ServerConfig* conf, const char* tag)\r{\r // now create a list of new modules that are due to be loaded\r // and a seperate list of modules which are due to be unloaded\r for (std::vector<std::string>::iterator _new = new_module_names.begin(); _new != new_module_names.end(); _new++)\r {\r bool added = true;\r\r for (std::vector<std::string>::iterator old = old_module_names.begin(); old != old_module_names.end(); old++)\r {\r if (*old == *_new)\r added = false;\r }\r\r if (added)\r added_modules.push_back(*_new);\r }\r\r for (std::vector<std::string>::iterator oldm = old_module_names.begin(); oldm != old_module_names.end(); oldm++)\r {\r bool removed = true;\r for (std::vector<std::string>::iterator newm = new_module_names.begin(); newm != new_module_names.end(); newm++)\r {\r if (*newm == *oldm)\r removed = false;\r }\r\r if (removed)\r removed_modules.push_back(*oldm);\r }\r return true;\r}\r\r/* Callback called before processing the first <banlist> tag\r */\rbool InitMaxBans(ServerConfig* conf, const char* tag)\r{\r conf->maxbans.clear();\r return true;\r}\r\r/* Callback called to process a single <banlist> tag\r */\rbool DoMaxBans(ServerConfig* conf, const char* tag, char** entries, ValueList &values, int* types)\r{\r const char* channel = values[0].GetString();\r int limit = values[1].GetInteger();\r conf->maxbans[channel] = limit;\r return true;\r}\r\r/* Callback called when there are no more <banlist> tags.\r */\rbool DoneMaxBans(ServerConfig* conf, const char* tag)\r{\r return true;\r}\r\rvoid ServerConfig::ReportConfigError(const std::string &errormessage, bool bail, userrec* user)\r{\r ServerInstance->Log(DEFAULT, "There were errors in your configuration file: %s", errormessage.c_str());\r if (bail)\r {\r /* Unneeded because of the ServerInstance->Log() aboive? */\r printf("There were errors in your configuration:\n%s\n\n",errormessage.c_str());\r InspIRCd::Exit(EXIT_STATUS_CONFIG);\r }\r else\r {\r std::string errors = errormessage;\r std::string::size_type start;\r unsigned int prefixlen;\r start = 0;\r /* ":ServerInstance->Config->ServerName NOTICE user->nick :" */\r if (user)\r {\r prefixlen = strlen(this->ServerName) + strlen(user->nick) + 11;\r user->WriteServ("NOTICE %s :There were errors in the configuration file:",user->nick);\r while (start < errors.length())\r {\r user->WriteServ("NOTICE %s :%s",user->nick, errors.substr(start, 510 - prefixlen).c_str());\r start += 510 - prefixlen;\r }\r }\r else\r {\r ServerInstance->WriteOpers("There were errors in the configuration file:");\r while (start < errors.length())\r {\r ServerInstance->WriteOpers(errors.substr(start, 360).c_str());\r start += 360;\r }\r }\r return;\r }\r}\r\rvoid ServerConfig::Read(bool bail, userrec* user)\r{\r static char debug[MAXBUF]; /* Temporary buffer for debugging value */\r static char maxkeep[MAXBUF]; /* Temporary buffer for WhoWasMaxKeep value */\r static char hidemodes[MAXBUF]; /* Modes to not allow listing from users below halfop */\r static char exemptchanops[MAXBUF]; /* Exempt channel ops from these modes */\r int rem = 0, add = 0; /* Number of modules added, number of modules removed */\r std::ostringstream errstr; /* String stream containing the error output */\r\r /* These tags MUST occur and must ONLY occur once in the config file */\r static char* Once[] = { "server", "admin", "files", "power", "options", NULL };\r\r /* These tags can occur ONCE or not at all */\r InitialConfig Values[] = {\r {"options", "softlimit", MAXCLIENTS_S, new ValueContainerUInt (&this->SoftLimit), DT_INTEGER, ValidateSoftLimit},\r {"options", "somaxconn", SOMAXCONN_S, new ValueContainerInt (&this->MaxConn), DT_INTEGER, ValidateMaxConn},\r {"options", "moronbanner", "Youre banned!", new ValueContainerChar (this->MoronBanner), DT_CHARPTR, NoValidation},\r {"server", "name", "", new ValueContainerChar (this->ServerName), DT_CHARPTR, ValidateServerName},\r {"server", "description", "Configure Me", new ValueContainerChar (this->ServerDesc), DT_CHARPTR, NoValidation},\r {"server", "network", "Network", new ValueContainerChar (this->Network), DT_CHARPTR, NoValidation},\r {"admin", "name", "", new ValueContainerChar (this->AdminName), DT_CHARPTR, NoValidation},\r {"admin", "email", "Mis@configu.red", new ValueContainerChar (this->AdminEmail), DT_CHARPTR, NoValidation},\r {"admin", "nick", "Misconfigured", new ValueContainerChar (this->AdminNick), DT_CHARPTR, NoValidation},\r {"files", "motd", "", new ValueContainerChar (this->motd), DT_CHARPTR, ValidateMotd},\r {"files", "rules", "", new ValueContainerChar (this->rules), DT_CHARPTR, ValidateRules},\r {"power", "diepass", "", new ValueContainerChar (this->diepass), DT_CHARPTR, ValidateNotEmpty},\r {"power", "pause", "", new ValueContainerInt (&this->DieDelay), DT_INTEGER, NoValidation},\r {"power", "restartpass", "", new ValueContainerChar (this->restartpass), DT_CHARPTR, ValidateNotEmpty},\r {"options", "prefixquit", "", new ValueContainerChar (this->PrefixQuit), DT_CHARPTR, NoValidation},\r {"options", "suffixquit", "", new ValueContainerChar (this->SuffixQuit), DT_CHARPTR, NoValidation},\r {"options", "fixedquit", "", new ValueContainerChar (this->FixedQuit), DT_CHARPTR, NoValidation},\r {"options", "loglevel", "default", new ValueContainerChar (debug), DT_CHARPTR, ValidateLogLevel},\r {"options", "netbuffersize","10240", new ValueContainerInt (&this->NetBufferSize), DT_INTEGER, ValidateNetBufferSize},\r {"options", "maxwho", "128", new ValueContainerInt (&this->MaxWhoResults), DT_INTEGER, ValidateMaxWho},\r {"options", "allowhalfop", "0", new ValueContainerBool (&this->AllowHalfop), DT_BOOLEAN, NoValidation},\r {"dns", "server", "", new ValueContainerChar (this->DNSServer), DT_CHARPTR, ValidateDnsServer},\r {"dns", "timeout", "5", new ValueContainerInt (&this->dns_timeout), DT_INTEGER, NoValidation},\r {"options", "moduledir", MOD_PATH, new ValueContainerChar (this->ModPath), DT_CHARPTR, NoValidation},\r {"disabled", "commands", "", new ValueContainerChar (this->DisabledCommands), DT_CHARPTR, NoValidation},\r {"options", "userstats", "", new ValueContainerChar (this->UserStats), DT_CHARPTR, NoValidation},\r {"options", "customversion","", new ValueContainerChar (this->CustomVersion), DT_CHARPTR, NoValidation},\r {"options", "hidesplits", "0", new ValueContainerBool (&this->HideSplits), DT_BOOLEAN, NoValidation},\r {"options", "hidebans", "0", new ValueContainerBool (&this->HideBans), DT_BOOLEAN, NoValidation},\r {"options", "hidewhois", "", new ValueContainerChar (this->HideWhoisServer), DT_CHARPTR, NoValidation},\r {"options", "hidekills", "", new ValueContainerChar (this->HideKillsServer), DT_CHARPTR, NoValidation},\r {"options", "operspywhois", "0", new ValueContainerBool (&this->OperSpyWhois), DT_BOOLEAN, NoValidation},\r {"options", "nouserdns", "0", new ValueContainerBool (&this->NoUserDns), DT_BOOLEAN, NoValidation},\r {"options", "syntaxhints", "0", new ValueContainerBool (&this->SyntaxHints), DT_BOOLEAN, NoValidation},\r {"options", "cyclehosts", "0", new ValueContainerBool (&this->CycleHosts), DT_BOOLEAN, NoValidation},\r {"options", "ircumsgprefix","0", new ValueContainerBool (&this->UndernetMsgPrefix), DT_BOOLEAN, NoValidation},\r {"options", "announceinvites", "1", new ValueContainerBool (&this->AnnounceInvites), DT_BOOLEAN, NoValidation},\r {"options", "hostintopic", "1", new ValueContainerBool (&this->FullHostInTopic), DT_BOOLEAN, NoValidation},\r {"options", "hidemodes", "", new ValueContainerChar (hidemodes), DT_CHARPTR, ValidateModeLists},\r {"options", "exemptchanops","", new ValueContainerChar (exemptchanops), DT_CHARPTR, ValidateExemptChanOps},\r {"options", "defaultmodes", "nt", new ValueContainerChar (this->DefaultModes), DT_CHARPTR, NoValidation},\r {"pid", "file", "", new ValueContainerChar (this->PID), DT_CHARPTR, NoValidation},\r {"whowas", "groupsize", "10", new ValueContainerInt (&this->WhoWasGroupSize), DT_INTEGER, NoValidation},\r {"whowas", "maxgroups", "10240", new ValueContainerInt (&this->WhoWasMaxGroups), DT_INTEGER, NoValidation},\r {"whowas", "maxkeep", "3600", new ValueContainerChar (maxkeep), DT_CHARPTR, ValidateWhoWas},\r {"die", "value", "", new ValueContainerChar (this->DieValue), DT_CHARPTR, NoValidation},\r {"channels", "users", "20", new ValueContainerUInt (&this->MaxChans), DT_INTEGER, NoValidation},\r {"channels", "opers", "60", new ValueContainerUInt (&this->OperMaxChans), DT_INTEGER, NoValidation},\r {NULL}\r };\r\r /* These tags can occur multiple times, and therefore they have special code to read them\r * which is different to the code for reading the singular tags listed above.\r */\r MultiConfig MultiValues[] = {\r\r {"connect",\r {"allow", "deny", "password", "timeout", "pingfreq", "flood",\r "threshold", "sendq", "recvq", "localmax", "globalmax", "port",\r NULL},\r {"", "", "", "", "120", "",\r "", "", "", "3", "3", "0",\r NULL},\r {DT_CHARPTR, DT_CHARPTR, DT_CHARPTR, DT_INTEGER, DT_INTEGER, DT_INTEGER,\r DT_INTEGER, DT_INTEGER, DT_INTEGER, DT_INTEGER, DT_INTEGER, DT_INTEGER},\r InitConnect, DoConnect, DoneConnect},\r\r {"uline",\r {"server", "silent", NULL},\r {"", "0", NULL},\r {DT_CHARPTR, DT_BOOLEAN},\r InitULine,DoULine,DoneULine},\r\r {"banlist",\r {"chan", "limit", NULL},\r {"", "", NULL},\r {DT_CHARPTR, DT_INTEGER},\r InitMaxBans, DoMaxBans, DoneMaxBans},\r\r {"module",\r {"name", NULL},\r {"", NULL},\r {DT_CHARPTR},\r InitModule, DoModule, DoneModule},\r\r {"badip",\r {"reason", "ipmask", NULL},\r {"No reason", "", NULL},\r {DT_CHARPTR, DT_CHARPTR},\r InitXLine, DoZLine, DoneZLine},\r\r {"badnick",\r {"reason", "nick", NULL},\r {"No reason", "", NULL},\r {DT_CHARPTR, DT_CHARPTR},\r InitXLine, DoQLine, DoneQLine},\r\r {"badhost",\r {"reason", "host", NULL},\r {"No reason", "", NULL},\r {DT_CHARPTR, DT_CHARPTR},\r InitXLine, DoKLine, DoneKLine},\r\r {"exception",\r {"reason", "host", NULL},\r {"No reason", "", NULL},\r {DT_CHARPTR, DT_CHARPTR},\r InitXLine, DoELine, DoneELine},\r\r {"type",\r {"name", "classes", NULL},\r {"", "", NULL},\r {DT_CHARPTR, DT_CHARPTR},\r InitTypes, DoType, DoneClassesAndTypes},\r\r {"class",\r {"name", "commands", NULL},\r {"", "", NULL},\r {DT_CHARPTR, DT_CHARPTR},\r InitClasses, DoClass, DoneClassesAndTypes},\r\r {NULL}\r };\r\r include_stack.clear();\r\r /* Load and parse the config file, if there are any errors then explode */\r\r /* Make a copy here so if it fails then we can carry on running with an unaffected config */\r ConfigDataHash newconfig;\r\r if (this->LoadConf(newconfig, ServerInstance->ConfigFileName, errstr))\r {\r /* If we succeeded, set the ircd config to the new one */\r this->config_data = newconfig;\r }\r else\r {\r ReportConfigError(errstr.str(), bail, user);\r return;\r }\r\r /* The stuff in here may throw CoreException, be sure we're in a position to catch it. */\r try\r {\r /* Check we dont have more than one of singular tags, or any of them missing\r */\r for (int Index = 0; Once[Index]; Index++)\r if (!CheckOnce(Once[Index], bail, user))\r return;\r\r /* Read the values of all the tags which occur once or not at all, and call their callbacks.\r */\r for (int Index = 0; Values[Index].tag; Index++)\r {\r char item[MAXBUF];\r int dt = Values[Index].datatype;\r bool allow_newlines = ((dt & DT_ALLOW_NEWLINE) > 0);\r dt &= ~DT_ALLOW_NEWLINE;\r\r ConfValue(this->config_data, Values[Index].tag, Values[Index].value, Values[Index].default_value, 0, item, MAXBUF, allow_newlines);\r ValueItem vi(item);\r\r if (!Values[Index].validation_function(this, Values[Index].tag, Values[Index].value, vi))\r throw CoreException("One or more values in your configuration file failed to validate. Please see your ircd.log for more information.");\r\r switch (Values[Index].datatype)\r {\r case DT_CHARPTR:\r {\r ValueContainerChar* vcc = (ValueContainerChar*)Values[Index].val;\r /* Make sure we also copy the null terminator */\r vcc->Set(vi.GetString(), strlen(vi.GetString()) + 1);\r }\r break;\r case DT_INTEGER:\r {\r int val = vi.GetInteger();\r ValueContainerInt* vci = (ValueContainerInt*)Values[Index].val;\r vci->Set(&val, sizeof(int));\r }\r break;\r case DT_BOOLEAN:\r {\r bool val = vi.GetBool();\r ValueContainerBool* vcb = (ValueContainerBool*)Values[Index].val;\r vcb->Set(&val, sizeof(bool));\r }\r break;\r default:\r /* You don't want to know what happens if someones bad code sends us here. */\r break;\r }\r\r /* We're done with this now */\r delete Values[Index].val;\r }\r\r /* Read the multiple-tag items (class tags, connect tags, etc)\r * and call the callbacks associated with them. We have three\r * callbacks for these, a 'start', 'item' and 'end' callback.\r */\r for (int Index = 0; MultiValues[Index].tag; Index++)\r {\r MultiValues[Index].init_function(this, MultiValues[Index].tag);\r\r int number_of_tags = ConfValueEnum(this->config_data, MultiValues[Index].tag);\r\r for (int tagnum = 0; tagnum < number_of_tags; tagnum++)\r {\r ValueList vl;\r for (int valuenum = 0; MultiValues[Index].items[valuenum]; valuenum++)\r {\r int dt = MultiValues[Index].datatype[valuenum];\r bool allow_newlines = ((dt & DT_ALLOW_NEWLINE) > 0);\r dt &= ~DT_ALLOW_NEWLINE;\r\r switch (dt)\r {\r case DT_CHARPTR:\r {\r char item[MAXBUF];\r if (ConfValue(this->config_data, MultiValues[Index].tag, MultiValues[Index].items[valuenum], MultiValues[Index].items_default[valuenum], tagnum, item, MAXBUF, allow_newlines))\r vl.push_back(ValueItem(item));\r else\r vl.push_back(ValueItem(""));\r }\r break;\r case DT_INTEGER:\r {\r int item = 0;\r if (ConfValueInteger(this->config_data, MultiValues[Index].tag, MultiValues[Index].items[valuenum], MultiValues[Index].items_default[valuenum], tagnum, item))\r vl.push_back(ValueItem(item));\r else\r vl.push_back(ValueItem(0));\r }\r break;\r case DT_BOOLEAN:\r {\r bool item = ConfValueBool(this->config_data, MultiValues[Index].tag, MultiValues[Index].items[valuenum], MultiValues[Index].items_default[valuenum], tagnum);\r vl.push_back(ValueItem(item));\r }\r break;\r default:\r /* Someone was smoking craq if we got here, and we're all gonna die. */\r break;\r }\r }\r\r MultiValues[Index].validation_function(this, MultiValues[Index].tag, (char**)MultiValues[Index].items, vl, MultiValues[Index].datatype);\r }\r\r MultiValues[Index].finish_function(this, MultiValues[Index].tag);\r }\r\r }\r\r catch (CoreException &ce)\r {\r ReportConfigError(ce.GetReason(), bail, user);\r return;\r }\r\r // write once here, to try it out and make sure its ok\r ServerInstance->WritePID(this->PID);\r\r ServerInstance->Log(DEFAULT,"Done reading configuration file.");\r\r /* If we're rehashing, let's load any new modules, and unload old ones\r */\r if (!bail)\r {\r int found_ports = 0;\r FailedPortList pl;\r ServerInstance->BindPorts(false, found_ports, pl);\r\r if (pl.size())\r {\r user->WriteServ("NOTICE %s :*** Not all your client ports could be bound.", user->nick);\r user->WriteServ("NOTICE %s :*** The following port(s) failed to bind:", user->nick);\r int j = 1;\r for (FailedPortList::iterator i = pl.begin(); i != pl.end(); i++, j++)\r {\r user->WriteServ("NOTICE %s :*** %d. IP: %s Port: %lu", user->nick, j, i->first.empty() ? "<all>" : i->first.c_str(), (unsigned long)i->second);\r }\r }\r\r if (!removed_modules.empty())\r {\r for (std::vector<std::string>::iterator removing = removed_modules.begin(); removing != removed_modules.end(); removing++)\r {\r if (ServerInstance->UnloadModule(removing->c_str()))\r {\r ServerInstance->WriteOpers("*** REHASH UNLOADED MODULE: %s",removing->c_str());\r\r if (user)\r user->WriteServ("973 %s %s :Module %s successfully unloaded.",user->nick, removing->c_str(), removing->c_str());\r\r rem++;\r }\r else\r {\r if (user)\r user->WriteServ("972 %s %s :Failed to unload module %s: %s",user->nick, removing->c_str(), removing->c_str(), ServerInstance->ModuleError());\r }\r }\r }\r\r if (!added_modules.empty())\r {\r for (std::vector<std::string>::iterator adding = added_modules.begin(); adding != added_modules.end(); adding++)\r {\r if (ServerInstance->LoadModule(adding->c_str()))\r {\r ServerInstance->WriteOpers("*** REHASH LOADED MODULE: %s",adding->c_str());\r\r if (user)\r user->WriteServ("975 %s %s :Module %s successfully loaded.",user->nick, adding->c_str(), adding->c_str());\r\r add++;\r }\r else\r {\r if (user)\r user->WriteServ("974 %s %s :Failed to load module %s: %s",user->nick, adding->c_str(), adding->c_str(), ServerInstance->ModuleError());\r }\r }\r }\r\r ServerInstance->Log(DEFAULT,"Successfully unloaded %lu of %lu modules and loaded %lu of %lu modules.",(unsigned long)rem,(unsigned long)removed_modules.size(),(unsigned long)add,(unsigned long)added_modules.size());\r }\r\r if (user)\r user->WriteServ("NOTICE %s :*** Successfully rehashed server.", user->nick);\r else\r ServerInstance->WriteOpers("*** Successfully rehashed server.");\r}\r\rbool ServerConfig::LoadConf(ConfigDataHash &target, const char* filename, std::ostringstream &errorstream)\r{\r std::ifstream conf(filename);\r std::string line;\r char ch;\r long linenumber;\r bool in_tag;\r bool in_quote;\r bool in_comment;\r int character_count = 0;\r\r linenumber = 1;\r in_tag = false;\r in_quote = false;\r in_comment = false;\r\r /* Check if the file open failed first */\r if (!conf)\r {\r errorstream << "LoadConf: Couldn't open config file: " << filename << std::endl;\r return false;\r }\r\r /* Fix the chmod of the file to restrict it to the current user and group */\r chmod(filename,0600);\r\r for (unsigned int t = 0; t < include_stack.size(); t++)\r {\r if (std::string(filename) == include_stack[t])\r {\r errorstream << "File " << filename << " is included recursively (looped inclusion)." << std::endl;\r return false;\r }\r }\r\r /* It's not already included, add it to the list of files we've loaded */\r include_stack.push_back(filename);\r\r /* Start reading characters... */\r while(conf.get(ch))\r {\r\r /*\r * Fix for moronic windows issue spotted by Adremelech.\r * Some windows editors save text files as utf-16, which is\r * a total pain in the ass to parse. Users should save in the\r * right config format! If we ever see a file where the first\r * byte is 0xFF or 0xFE, or the second is 0xFF or 0xFE, then\r * this is most likely a utf-16 file. Bail out and insult user.\r */\r if ((character_count++ < 2) && (ch == '\xFF' || ch == '\xFE'))\r {\r errorstream << "File " << filename << " cannot be read, as it is encoded in braindead UTF-16. Save your file as plain ASCII!" << std::endl;\r return false;\r }\r\r /*\r * Here we try and get individual tags on separate lines,\r * this would be so easy if we just made people format\r * their config files like that, but they don't so...\r * We check for a '<' and then know the line is over when\r * we get a '>' not inside quotes. If we find two '<' and\r * no '>' then die with an error.\r */\r\r if((ch == '#') && !in_quote)\r in_comment = true;\r\r switch(ch)\r {\r case '\n':\r if (in_quote)\r line += '\n';\r linenumber++;\r case '\r':\r if (!in_quote)\r in_comment = false;\r case '\0':\r continue;\r case '\t':\r ch = ' ';\r }\r\r if(in_comment)\r continue;\r\r /* XXX: Added by Brain, May 1st 2006 - Escaping of characters.\r * Note that this WILL NOT usually allow insertion of newlines,\r * because a newline is two characters long. Use it primarily to\r * insert the " symbol.\r *\r * Note that this also involves a further check when parsing the line,\r * which can be found below.\r */\r if ((ch == '\\') && (in_quote) && (in_tag))\r {\r line += ch;\r char real_character;\r if (conf.get(real_character))\r {\r if (real_character == 'n')\r real_character = '\n';\r line += real_character;\r continue;\r }\r else\r {\r errorstream << "End of file after a \\, what did you want to escape?: " << filename << ":" << linenumber << std::endl;\r return false;\r }\r }\r\r if (ch != '\r')\r line += ch;\r\r if(ch == '<')\r {\r if(in_tag)\r {\r if(!in_quote)\r {\r errorstream << "Got another opening < when the first one wasn't closed: " << filename << ":" << linenumber << std::endl;\r return false;\r }\r }\r else\r {\r if(in_quote)\r {\r errorstream << "We're in a quote but outside a tag, interesting. " << filename << ":" << linenumber << std::endl;\r return false;\r }\r else\r {\r // errorstream << "Opening new config tag on line " << linenumber << std::endl;\r in_tag = true;\r }\r }\r }\r else if(ch == '"')\r {\r if(in_tag)\r {\r if(in_quote)\r {\r // errorstream << "Closing quote in config tag on line " << linenumber << std::endl;\r in_quote = false;\r }\r else\r {\r // errorstream << "Opening quote in config tag on line " << linenumber << std::endl;\r in_quote = true;\r }\r }\r else\r {\r if(in_quote)\r {\r errorstream << "Found a (closing) \" outside a tag: " << filename << ":" << linenumber << std::endl;\r }\r else\r {\r errorstream << "Found a (opening) \" outside a tag: " << filename << ":" << linenumber << std::endl;\r }\r }\r }\r else if(ch == '>')\r {\r if(!in_quote)\r {\r if(in_tag)\r {\r // errorstream << "Closing config tag on line " << linenumber << std::endl;\r in_tag = false;\r\r /*\r * If this finds an <include> then ParseLine can simply call\r * LoadConf() and load the included config into the same ConfigDataHash\r */\r\r if(!this->ParseLine(target, line, linenumber, errorstream))\r return false;\r\r line.clear();\r }\r else\r {\r errorstream << "Got a closing > when we weren't inside a tag: " << filename << ":" << linenumber << std::endl;\r return false;\r }\r }\r }\r }\r\r return true;\r}\r\rbool ServerConfig::LoadConf(ConfigDataHash &target, const std::string &filename, std::ostringstream &errorstream)\r{\r return this->LoadConf(target, filename.c_str(), errorstream);\r}\r\rbool ServerConfig::ParseLine(ConfigDataHash &target, std::string &line, long linenumber, std::ostringstream &errorstream)\r{\r std::string tagname;\r std::string current_key;\r std::string current_value;\r KeyValList results;\r bool got_name;\r bool got_key;\r bool in_quote;\r\r got_name = got_key = in_quote = false;\r\r //std::cout << "ParseLine(data, '" << line << "', " << linenumber << ", stream)" << std::endl;\r\r for(std::string::iterator c = line.begin(); c != line.end(); c++)\r {\r if(!got_name)\r {\r /* We don't know the tag name yet. */\r\r if(*c != ' ')\r {\r if(*c != '<')\r {\r tagname += *c;\r }\r }\r else\r {\r /* We got to a space, we should have the tagname now. */\r if(tagname.length())\r {\r got_name = true;\r }\r }\r }\r else\r {\r /* We have the tag name */\r if (!got_key)\r {\r /* We're still reading the key name */\r if (*c != '=')\r {\r if (*c != ' ')\r {\r current_key += *c;\r }\r }\r else\r {\r /* We got an '=', end of the key name. */\r got_key = true;\r }\r }\r else\r {\r /* We have the key name, now we're looking for quotes and the value */\r\r /* Correctly handle escaped characters here.\r * See the XXX'ed section above.\r */\r if ((*c == '\\') && (in_quote))\r {\r c++;\r if (*c == 'n')\r current_value += '\n';\r else\r current_value += *c;\r continue;\r }\r else if ((*c == '\n') && (in_quote))\r {\r /* Got a 'real' \n, treat it as part of the value */\r current_value += '\n';\r continue;\r }\r else if ((*c == '\r') && (in_quote))\r /* Got a \r, drop it */\r continue;\r\r if (*c == '"')\r {\r if (!in_quote)\r {\r /* We're not already in a quote. */\r in_quote = true;\r }\r else\r {\r /* Leaving quotes, we have the value */\r results.push_back(KeyVal(current_key, current_value));\r\r // std::cout << "<" << tagname << ":" << current_key << "> " << current_value << std::endl;\r\r in_quote = false;\r got_key = false;\r\r if((tagname == "include") && (current_key == "file"))\r {\r if(!this->DoInclude(target, current_value, errorstream))\r return false;\r }\r\r current_key.clear();\r current_value.clear();\r }\r }\r else\r {\r if(in_quote)\r {\r current_value += *c;\r }\r }\r }\r }\r }\r\r /* Finished parsing the tag, add it to the config hash */\r target.insert(std::pair<std::string, KeyValList > (tagname, results));\r\r return true;\r}\r\rbool ServerConfig::DoInclude(ConfigDataHash &target, const std::string &file, std::ostringstream &errorstream)\r{\r std::string confpath;\r std::string newfile;\r std::string::size_type pos;\r\r confpath = ServerInstance->ConfigFileName;\r newfile = file;\r\r for (std::string::iterator c = newfile.begin(); c != newfile.end(); c++)\r {\r if (*c == '\\')\r {\r *c = '/';\r }\r }\r\r if (file[0] != '/')\r {\r if((pos = confpath.rfind("/")) != std::string::npos)\r {\r /* Leaves us with just the path */\r newfile = confpath.substr(0, pos) + std::string("/") + newfile;\r }\r else\r {\r errorstream << "Couldn't get config path from: " << confpath << std::endl;\r return false;\r }\r }\r\r return LoadConf(target, newfile, errorstream);\r}\r\rbool ServerConfig::ConfValue(ConfigDataHash &target, const char* tag, const char* var, int index, char* result, int length, bool allow_linefeeds)\r{\r return ConfValue(target, tag, var, "", index, result, length, allow_linefeeds);\r}\r\rbool ServerConfig::ConfValue(ConfigDataHash &target, const char* tag, const char* var, const char* default_value, int index, char* result, int length, bool allow_linefeeds)\r{\r std::string value;\r bool r = ConfValue(target, std::string(tag), std::string(var), std::string(default_value), index, value, allow_linefeeds);\r strlcpy(result, value.c_str(), length);\r return r;\r}\r\rbool ServerConfig::ConfValue(ConfigDataHash &target, const std::string &tag, const std::string &var, int index, std::string &result, bool allow_linefeeds)\r{\r return ConfValue(target, tag, var, "", index, result, allow_linefeeds);\r}\r\rbool ServerConfig::ConfValue(ConfigDataHash &target, const std::string &tag, const std::string &var, const std::string &default_value, int index, std::string &result, bool allow_linefeeds)\r{\r ConfigDataHash::size_type pos = index;\r if((pos >= 0) && (pos < target.count(tag)))\r {\r ConfigDataHash::iterator iter = target.find(tag);\r\r for(int i = 0; i < index; i++)\r iter++;\r\r for(KeyValList::iterator j = iter->second.begin(); j != iter->second.end(); j++)\r {\r if(j->first == var)\r {\r if ((!allow_linefeeds) && (j->second.find('\n') != std::string::npos))\r {\r ServerInstance->Log(DEFAULT, "Value of <" + tag + ":" + var+ "> contains a linefeed, and linefeeds in this value are not permitted -- stripped to spaces.");\r for (std::string::iterator n = j->second.begin(); n != j->second.end(); n++)\r if (*n == '\n')\r *n = ' ';\r }\r else\r {\r result = j->second;\r return true;\r }\r }\r }\r if (!default_value.empty())\r {\r result = default_value;\r return true;\r }\r }\r else if(pos == 0)\r {\r if (!default_value.empty())\r {\r result = default_value;\r return true;\r }\r }\r return false;\r}\r\rbool ServerConfig::ConfValueInteger(ConfigDataHash &target, const char* tag, const char* var, int index, int &result)\r{\r return ConfValueInteger(target, std::string(tag), std::string(var), "", index, result);\r}\r\rbool ServerConfig::ConfValueInteger(ConfigDataHash &target, const char* tag, const char* var, const char* default_value, int index, int &result)\r{\r return ConfValueInteger(target, std::string(tag), std::string(var), std::string(default_value), index, result);\r}\r\rbool ServerConfig::ConfValueInteger(ConfigDataHash &target, const std::string &tag, const std::string &var, int index, int &result)\r{\r return ConfValueInteger(target, tag, var, "", index, result);\r}\r\rbool ServerConfig::ConfValueInteger(ConfigDataHash &target, const std::string &tag, const std::string &var, const std::string &default_value, int index, int &result)\r{\r std::string value;\r std::istringstream stream;\r bool r = ConfValue(target, tag, var, default_value, index, value);\r stream.str(value);\r if(!(stream >> result))\r return false;\r else\r {\r if (!value.empty())\r {\r if (value.substr(0,2) == "0x")\r {\r char* endptr;\r\r value.erase(0,2);\r result = strtol(value.c_str(), &endptr, 16);\r\r /* No digits found */\r if (endptr == value.c_str())\r return false;\r }\r else\r {\r char denominator = *(value.end() - 1);\r switch (toupper(denominator))\r {\r case 'K':\r /* Kilobytes -> bytes */\r result = result * 1024;\r break;\r case 'M':\r /* Megabytes -> bytes */\r result = result * 1024 * 1024;\r break;\r case 'G':\r /* Gigabytes -> bytes */\r result = result * 1024 * 1024 * 1024;\r break;\r }\r }\r }\r }\r return r;\r}\r\r\rbool ServerConfig::ConfValueBool(ConfigDataHash &target, const char* tag, const char* var, int index)\r{\r return ConfValueBool(target, std::string(tag), std::string(var), "", index);\r}\r\rbool ServerConfig::ConfValueBool(ConfigDataHash &target, const char* tag, const char* var, const char* default_value, int index)\r{\r return ConfValueBool(target, std::string(tag), std::string(var), std::string(default_value), index);\r}\r\rbool ServerConfig::ConfValueBool(ConfigDataHash &target, const std::string &tag, const std::string &var, int index)\r{\r return ConfValueBool(target, tag, var, "", index);\r}\r\rbool ServerConfig::ConfValueBool(ConfigDataHash &target, const std::string &tag, const std::string &var, const std::string &default_value, int index)\r{\r std::string result;\r if(!ConfValue(target, tag, var, default_value, index, result))\r return false;\r\r return ((result == "yes") || (result == "true") || (result == "1"));\r}\r\rint ServerConfig::ConfValueEnum(ConfigDataHash &target, const char* tag)\r{\r return target.count(tag);\r}\r\rint ServerConfig::ConfValueEnum(ConfigDataHash &target, const std::string &tag)\r{\r return target.count(tag);\r}\r\rint ServerConfig::ConfVarEnum(ConfigDataHash &target, const char* tag, int index)\r{\r return ConfVarEnum(target, std::string(tag), index);\r}\r\rint ServerConfig::ConfVarEnum(ConfigDataHash &target, const std::string &tag, int index)\r{\r ConfigDataHash::size_type pos = index;\r\r if((pos >= 0) && (pos < target.count(tag)))\r {\r ConfigDataHash::const_iterator iter = target.find(tag);\r\r for(int i = 0; i < index; i++)\r iter++;\r\r return iter->second.size();\r }\r\r return 0;\r}\r\r/** Read the contents of a file located by `fname' into a file_cache pointed at by `F'.\r */\rbool ServerConfig::ReadFile(file_cache &F, const char* fname)\r{\r if (!fname || !*fname)\r return false;\r\r FILE* file = NULL;\r char linebuf[MAXBUF];\r\r F.clear();\r\r if ((*fname != '/') && (*fname != '\\'))\r {\r std::string::size_type pos;\r std::string confpath = ServerInstance->ConfigFileName;\r std::string newfile = fname;\r\r if ((pos = confpath.rfind("/")) != std::string::npos)\r newfile = confpath.substr(0, pos) + std::string("/") + fname;\r else if ((pos = confpath.rfind("\\")) != std::string::npos)\r newfile = confpath.substr(0, pos) + std::string("\\") + fname;\r\r if (!FileExists(newfile.c_str()))\r return false;\r file = fopen(newfile.c_str(), "r");\r }\r else\r {\r if (!FileExists(fname))\r return false;\r file = fopen(fname, "r");\r }\r\r if (file)\r {\r while (!feof(file))\r {\r if (fgets(linebuf, sizeof(linebuf), file))\r linebuf[strlen(linebuf)-1] = 0;\r else\r *linebuf = 0;\r\r if (!feof(file))\r {\r F.push_back(*linebuf ? linebuf : " ");\r }\r }\r\r fclose(file);\r }\r else\r return false;\r\r return true;\r}\r\rbool ServerConfig::FileExists(const char* file)\r{\r struct stat sb;\r if (stat(file, &sb) == -1)\r return false;\r\r if ((sb.st_mode & S_IFDIR) > 0)\r return false;\r \r FILE *input;\r if ((input = fopen (file, "r")) == NULL)\r return false;\r else\r {\r fclose(input);\r return true;\r }\r}\r\rchar* ServerConfig::CleanFilename(char* name)\r{\r char* p = name + strlen(name);\r while ((p != name) && (*p != '/') && (*p != '\\')) p--;\r return (p != name ? ++p : p);\r}\r\r\rbool ServerConfig::DirValid(const char* dirandfile)\r{\r#ifdef WINDOWS\r return true;\r#endif\r\r char work[1024];\r char buffer[1024];\r char otherdir[1024];\r int p;\r\r strlcpy(work, dirandfile, 1024);\r p = strlen(work);\r\r // we just want the dir\r while (*work)\r {\r if (work[p] == '/')\r {\r work[p] = '\0';\r break;\r }\r\r work[p--] = '\0';\r }\r\r // Get the current working directory\r if (getcwd(buffer, 1024 ) == NULL )\r return false;\r\r if (chdir(work) == -1)\r return false;\r\r if (getcwd(otherdir, 1024 ) == NULL )\r return false;\r\r if (chdir(buffer) == -1)\r return false;\r\r size_t t = strlen(work);\r\r if (strlen(otherdir) >= t)\r {\r otherdir[t] = '\0';\r if (!strcmp(otherdir,work))\r {\r return true;\r }\r\r return false;\r }\r else\r {\r return false;\r }\r}\r\rstd::string ServerConfig::GetFullProgDir()\r{\r char buffer[PATH_MAX+1];\r#ifdef WINDOWS\r /* Windows has specific api calls to get the exe path that never fail.\r * For once, windows has something of use, compared to the POSIX code\r * for this, this is positively neato.\r */\r if (GetModuleFileName(NULL, buffer, MAX_PATH))\r {\r std::string fullpath = buffer;\r std::string::size_type n = fullpath.rfind("\\inspircd.exe");\r return std::string(fullpath, 0, n);\r }\r#else\r // Get the current working directory\r if (getcwd(buffer, PATH_MAX))\r {\r std::string remainder = this->argv[0];\r\r /* Does argv[0] start with /? its a full path, use it */\r if (remainder[0] == '/')\r {\r std::string::size_type n = remainder.rfind("/inspircd");\r return std::string(remainder, 0, n);\r }\r\r std::string fullpath = std::string(buffer) + "/" + remainder;\r std::string::size_type n = fullpath.rfind("/inspircd");\r return std::string(fullpath, 0, n);\r }\r#endif\r return "/";\r}\r\rInspIRCd* ServerConfig::GetInstance()\r{\r return ServerInstance;\r}\r\r\rValueItem::ValueItem(int value)\r{\r std::stringstream n;\r n << value;\r v = n.str();\r}\r\rValueItem::ValueItem(bool value)\r{\r std::stringstream n;\r n << value;\r v = n.str();\r}\r\rValueItem::ValueItem(char* value)\r{\r v = value;\r}\r\rvoid ValueItem::Set(char* value)\r{\r v = value;\r}\r\rvoid ValueItem::Set(const char* value)\r{\r v = value;\r}\r\rvoid ValueItem::Set(int value)\r{\r std::stringstream n;\r n << value;\r v = n.str();\r}\r\rint ValueItem::GetInteger()\r{\r if (v.empty())\r return 0;\r return atoi(v.c_str());\r}\r\rchar* ValueItem::GetString()\r{\r return (char*)v.c_str();\r}\r\rbool ValueItem::GetBool()\r{\r return (GetInteger() || v == "yes" || v == "true");\r}\r\r
\ No newline at end of file