X-Git-Url: https://git.netwichtig.de/gitweb/?a=blobdiff_plain;f=src%2Fconfigreader.cpp;h=df7721e826adfe0d4bc5ac327fc3c318d8b49c0e;hb=92eda175245c3d3da6b41af5c52d1540a23a0dc3;hp=12e33ad34d216ce8a6e15b0488dc5d263675ca7c;hpb=b119a88b55e7f5f3260ff21e78a5c9d1cf491cc8;p=user%2Fhenk%2Fcode%2Finspircd.git diff --git a/src/configreader.cpp b/src/configreader.cpp index 12e33ad34..df7721e82 100644 --- a/src/configreader.cpp +++ b/src/configreader.cpp @@ -47,7 +47,7 @@ ServerConfig::ServerConfig(InspIRCd* Instance) : ServerInstance(Instance) dns_timeout = DieDelay = 5; MaxTargets = 20; NetBufferSize = 10240; - SoftLimit = MAXCLIENTS; + SoftLimit = Instance->SE->GetMaxFds(); MaxConn = SOMAXCONN; MaxWhoResults = 0; debugging = 0; @@ -63,32 +63,12 @@ void ServerConfig::ClearStack() include_stack.clear(); } -Module* ServerConfig::GetIOHook(int port) -{ - std::map::iterator x = IOHookModule.find(port); - return (x != IOHookModule.end() ? x->second : NULL); -} - Module* ServerConfig::GetIOHook(BufferedSocket* is) { std::map::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, BufferedSocket* is) { if (!GetIOHook(is)) @@ -104,17 +84,6 @@ bool ServerConfig::AddIOHook(Module* iomod, BufferedSocket* is) } } -bool ServerConfig::DelIOHook(int port) -{ - std::map::iterator x = IOHookModule.find(port); - if (x != IOHookModule.end()) - { - IOHookModule.erase(x); - return true; - } - return false; -} - bool ServerConfig::DelIOHook(BufferedSocket* is) { std::map::iterator x = SocketIOHookModule.find(is); @@ -157,7 +126,7 @@ void ServerConfig::Update005() void ServerConfig::Send005(User* user) { for (std::vector::iterator line = ServerInstance->Config->isupport.begin(); line != ServerInstance->Config->isupport.end(); line++) - user->WriteServ("005 %s %s", user->nick, line->c_str()); + user->WriteNumeric(005, "%s %s", user->nick, line->c_str()); } bool ServerConfig::CheckOnce(const char* tag) @@ -292,10 +261,10 @@ bool ValidateMaxTargets(ServerConfig* conf, const char*, const char*, ValueItem bool ValidateSoftLimit(ServerConfig* conf, const char*, const char*, ValueItem &data) { - if ((data.GetInteger() < 1) || (data.GetInteger() > MAXCLIENTS)) + if ((data.GetInteger() < 1) || (data.GetInteger() > conf->GetInstance()->SE->GetMaxFds())) { - conf->GetInstance()->Logs->Log("CONFIG",DEFAULT,"WARNING: value is greater than %d or less than 0, set to %d.",MAXCLIENTS,MAXCLIENTS); - data.Set(MAXCLIENTS); + conf->GetInstance()->Logs->Log("CONFIG",DEFAULT,"WARNING: value is greater than %d or less than 0, set to %d.",conf->GetInstance()->SE->GetMaxFds(),conf->GetInstance()->SE->GetMaxFds()); + data.Set(conf->GetInstance()->SE->GetMaxFds()); } return true; } @@ -368,14 +337,9 @@ bool ValidateDnsServer(ServerConfig* conf, const char*, const char*, ValueItem & bool ValidateServerName(ServerConfig* conf, const char*, const char*, ValueItem &data) { + conf->GetInstance()->Logs->Log("CONFIG",DEFAULT,"Validating server name"); /* 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."); - /* We don't actually reach this return of course... */ - return false; - } - if (!strchr(data.GetString(),'.')) + if (!strchr(data.GetString(), '.')) { conf->GetInstance()->Logs->Log("CONFIG",DEFAULT,"WARNING: '%s' is not a fully-qualified domain name. Changed to '%s%c'",data.GetString(),data.GetString(),'.'); std::string moo = std::string(data.GetString()).append("."); @@ -478,6 +442,8 @@ bool ValidateInvite(ServerConfig* conf, const char*, const char*, ValueItem &dat bool ValidateSID(ServerConfig* conf, const char*, const char*, ValueItem &data) { + conf->GetInstance()->Logs->Log("CONFIG",DEFAULT,"Validating server id"); + const char *sid = data.GetString(); if (*sid && !conf->GetInstance()->IsSID(sid)) @@ -485,6 +451,8 @@ bool ValidateSID(ServerConfig* conf, const char*, const char*, ValueItem &data) throw CoreException(std::string(sid) + " is not a valid server ID. A server ID must be 3 characters long, with the first character a digit and the next two characters a digit or letter."); } + strlcpy(conf->sid, sid, 5); + return true; } @@ -520,13 +488,6 @@ bool InitConnect(ServerConfig* conf, const char*) { conf->GetInstance()->Logs->Log("CONFIG",DEFAULT,"Reading connect classes..."); - for (ClassVector::iterator i = conf->Classes.begin(); i != conf->Classes.end(); i++) - { - ConnectClass *c = *i; - - conf->GetInstance()->Logs->Log("CONFIG",DEBUG, "Address of class is %p", c); - } - for (ClassVector::iterator i = conf->Classes.begin(); i != conf->Classes.end() ; ) { ConnectClass* c = *i; @@ -780,7 +741,7 @@ void ServerConfig::ReportConfigError(const std::string &errormessage, bool bail, ServerInstance->SNO->WriteToSnoMask('A', "There were errors in the configuration file:"); while (start < errors.length()) { - ServerInstance->SNO->WriteToSnoMask('A', errors.substr(start, 360).c_str()); + ServerInstance->SNO->WriteToSnoMask('A', errors.substr(start, 360)); start += 360; } } @@ -790,7 +751,7 @@ void ServerConfig::ReportConfigError(const std::string &errormessage, bool bail, void ServerConfig::Read(bool bail, User* user) { - int rem = 0, add = 0; /* Number of modules added, number of modules removed */ + int rem = 0, add = 0; /* Number of modules added, number of modules removed */ static char debug[MAXBUF]; /* Temporary buffer for debugging value */ static char maxkeep[MAXBUF]; /* Temporary buffer for WhoWasMaxKeep value */ @@ -806,13 +767,13 @@ void ServerConfig::Read(bool bail, User* user) /* These tags can occur ONCE or not at all */ InitialConfig Values[] = { - {"options", "softlimit", MAXCLIENTS_S, new ValueContainerUInt (&this->SoftLimit), DT_INTEGER, ValidateSoftLimit}, + {"options", "softlimit", "0", 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_HOSTNAME, ValidateServerName}, + {"server", "name", "", new ValueContainerChar (this->ServerName), DT_HOSTNAME|DT_BOOTONLY, ValidateServerName}, {"server", "description", "Configure Me", new ValueContainerChar (this->ServerDesc), DT_CHARPTR, NoValidation}, {"server", "network", "Network", new ValueContainerChar (this->Network), DT_NOSPACES, NoValidation}, - {"server", "id", "", new ValueContainerChar (this->sid), DT_CHARPTR, ValidateSID}, + {"server", "id", "", new ValueContainerChar (this->sid), DT_CHARPTR|DT_BOOTONLY, ValidateSID}, {"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}, @@ -933,9 +894,9 @@ void ServerConfig::Read(bool bail, User* user) InitTypes, DoType, DoneClassesAndTypes}, {"class", - {"name", "commands", NULL}, - {"", "", NULL}, - {DT_NOSPACES, DT_CHARPTR}, + {"name", "commands", "usermodes", "chanmodes", NULL}, + {"", "", "", "", NULL}, + {DT_NOSPACES, DT_CHARPTR, DT_CHARPTR, DT_CHARPTR}, InitClasses, DoClass, DoneClassesAndTypes}, {NULL, @@ -950,10 +911,12 @@ void ServerConfig::Read(bool bail, User* user) /* Make a copy here so if it fails then we can carry on running with an unaffected config */ newconfig.clear(); - if (this->LoadConf(newconfig, ServerInstance->ConfigFileName, errstr)) + if (this->DoInclude(newconfig, ServerInstance->ConfigFileName, errstr)) { /* If we succeeded, set the ircd config to the new one */ + ServerInstance->Threads->Mutex(true); this->config_data = newconfig; + ServerInstance->Threads->Mutex(false); } else { @@ -964,7 +927,7 @@ void ServerConfig::Read(bool bail, User* user) /* 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 + /* 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])) @@ -978,8 +941,14 @@ void ServerConfig::Read(bool bail, User* user) int dt = Values[Index].datatype; bool allow_newlines = ((dt & DT_ALLOW_NEWLINE) > 0); bool allow_wild = ((dt & DT_ALLOW_WILD) > 0); + bool bootonly = ((dt & DT_BOOTONLY) > 0); dt &= ~DT_ALLOW_NEWLINE; dt &= ~DT_ALLOW_WILD; + dt &= ~DT_BOOTONLY; + + /* Silently ignore boot only values */ + if (bootonly && !bail) + continue; ConfValue(this->config_data, Values[Index].tag, Values[Index].value, Values[Index].default_value, 0, item, MAXBUF, allow_newlines); ValueItem vi(item); @@ -987,7 +956,8 @@ void ServerConfig::Read(bool bail, User* user) 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) + ServerInstance->Threads->Mutex(true); + switch (dt) { case DT_NOSPACES: { @@ -1001,6 +971,7 @@ void ServerConfig::Read(bool bail, User* user) ValueContainerChar* vcc = (ValueContainerChar*)Values[Index].val; this->ValidateHostname(vi.GetString(), Values[Index].tag, Values[Index].value); vcc->Set(vi.GetString(), strlen(vi.GetString()) + 1); + ServerInstance->Logs->Log("CONFIG",DEFAULT,"Got %s", vi.GetString()); } break; case DT_IPADDRESS: @@ -1014,7 +985,10 @@ void ServerConfig::Read(bool bail, User* user) { ValueContainerChar* vcc = (ValueContainerChar*)Values[Index].val; if (*(vi.GetString()) && !ServerInstance->IsChannel(vi.GetString())) + { + ServerInstance->Threads->Mutex(false); throw CoreException("The value of <"+std::string(Values[Index].tag)+":"+Values[Index].value+"> is not a valid channel name"); + } vcc->Set(vi.GetString(), strlen(vi.GetString()) + 1); } break; @@ -1045,6 +1019,7 @@ void ServerConfig::Read(bool bail, User* user) } /* We're done with this now */ delete Values[Index].val; + ServerInstance->Threads->Mutex(false); } /* Read the multiple-tag items (class tags, connect tags, etc) @@ -1053,7 +1028,9 @@ void ServerConfig::Read(bool bail, User* user) */ for (int Index = 0; MultiValues[Index].tag; Index++) { + ServerInstance->Threads->Mutex(true); MultiValues[Index].init_function(this, MultiValues[Index].tag); + ServerInstance->Threads->Mutex(false); int number_of_tags = ConfValueEnum(this->config_data, MultiValues[Index].tag); @@ -1068,81 +1045,93 @@ void ServerConfig::Read(bool bail, User* user) dt &= ~DT_ALLOW_NEWLINE; dt &= ~DT_ALLOW_WILD; - switch (dt) + ServerInstance->Threads->Mutex(true); + /* We catch and rethrow any exception here just so we can free our mutex + */ + try { - case DT_NOSPACES: - { - 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("")); - this->ValidateNoSpaces(vl[vl.size()-1].GetString(), MultiValues[Index].tag, MultiValues[Index].items[valuenum]); - } - break; - case DT_HOSTNAME: - { - 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("")); - this->ValidateHostname(vl[vl.size()-1].GetString(), MultiValues[Index].tag, MultiValues[Index].items[valuenum]); - } - break; - case DT_IPADDRESS: - { - 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("")); - this->ValidateIP(vl[vl.size()-1].GetString(), MultiValues[Index].tag, MultiValues[Index].items[valuenum], allow_wild); - } - break; - case DT_CHANNEL: - { - 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("")); - if (!ServerInstance->IsChannel(vl[vl.size()-1].GetString())) - throw CoreException("The value of <"+std::string(MultiValues[Index].tag)+":"+MultiValues[Index].items[valuenum]+"> number "+ConvToStr(tagnum + 1)+" is not a valid channel name"); - } - break; - 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: + switch (dt) { - int item = 0; - if (ConfValueInteger(this->config_data, MultiValues[Index].tag, MultiValues[Index].items[valuenum], MultiValues[Index].items_default[valuenum], tagnum, item)) + case DT_NOSPACES: + { + 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("")); + this->ValidateNoSpaces(vl[vl.size()-1].GetString(), MultiValues[Index].tag, MultiValues[Index].items[valuenum]); + } + break; + case DT_HOSTNAME: + { + 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("")); + this->ValidateHostname(vl[vl.size()-1].GetString(), MultiValues[Index].tag, MultiValues[Index].items[valuenum]); + } + break; + case DT_IPADDRESS: + { + 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("")); + this->ValidateIP(vl[vl.size()-1].GetString(), MultiValues[Index].tag, MultiValues[Index].items[valuenum], allow_wild); + } + break; + case DT_CHANNEL: + { + 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("")); + if (!ServerInstance->IsChannel(vl[vl.size()-1].GetString())) + throw CoreException("The value of <"+std::string(MultiValues[Index].tag)+":"+MultiValues[Index].items[valuenum]+"> number "+ConvToStr(tagnum + 1)+" is not a valid channel name"); + } + break; + 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)); - 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; } - break; - default: - /* Someone was smoking craq if we got here, and we're all gonna die. */ - break; } + catch (CoreException &e) + { + ServerInstance->Threads->Mutex(false); + throw e; + } + ServerInstance->Threads->Mutex(false); } - MultiValues[Index].validation_function(this, MultiValues[Index].tag, (char**)MultiValues[Index].items, vl, MultiValues[Index].datatype); + MultiValues[Index].validation_function(this, MultiValues[Index].tag, (char**)MultiValues[Index].items, vl, MultiValues[Index].datatype); } - MultiValues[Index].finish_function(this, MultiValues[Index].tag); + MultiValues[Index].finish_function(this, MultiValues[Index].tag); } } @@ -1153,293 +1142,304 @@ void ServerConfig::Read(bool bail, User* 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) - { - 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() ? "" : i->first.c_str(), (unsigned long)i->second); - } - } - - if (!removed_modules.empty()) - { - for (std::vector::iterator removing = removed_modules.begin(); removing != removed_modules.end(); removing++) - { - if (ServerInstance->Modules->Unload(removing->c_str())) - { - ServerInstance->SNO->WriteToSnoMask('A', "*** 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->Modules->LastError().c_str()); - } - } - } - - if (!added_modules.empty()) - { - for (std::vector::iterator adding = added_modules.begin(); adding != added_modules.end(); adding++) - { - if (ServerInstance->Modules->Load(adding->c_str())) - { - ServerInstance->SNO->WriteToSnoMask('A', "*** 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->Modules->LastError().c_str()); - } - } - } - - 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()); - } - - /** Note: This is safe, the method checks for user == NULL */ - ServerInstance->Parser->SetupCommandTable(user); - - if (user) - user->WriteServ("NOTICE %s :*** Successfully rehashed server.", user->nick); - else - ServerInstance->SNO->WriteToSnoMask('A', "*** 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_tag) - { - // errorstream << "Closing config tag on line " << linenumber << std::endl; - in_tag = false; - - /* - * If this finds an 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; - } - } - } - } - - /* Fix for bug #392 - if we reach the end of a file and we are still in a quote or comment, most likely the user fucked up */ - if (in_comment || in_quote) - { - errorstream << "Reached end of file whilst still inside a quoted section or tag. This is most likely an error or there \ - is a newline missing from the end of the file: " << filename << ":" << linenumber << std::endl; - } - - return true; -} - - -bool ServerConfig::LoadConf(ConfigDataHash &target, const std::string &filename, std::ostringstream &errorstream) -{ - return this->LoadConf(target, filename.c_str(), errorstream); + // write once here, to try it out and make sure its ok + ServerInstance->WritePID(this->PID); + + /* Switch over logfiles */ + ServerInstance->Logs->CloseLogs(); + ServerInstance->Logs->OpenFileLogs(); + + ServerInstance->Logs->Log("CONFIG", 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) + { + ServerInstance->Threads->Mutex(true); + 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() ? "" : i->first.c_str(), (unsigned long)i->second); + } + ServerInstance->Threads->Mutex(false); + } + + ServerInstance->Threads->Mutex(true); + if (!removed_modules.empty()) + { + for (std::vector::iterator removing = removed_modules.begin(); removing != removed_modules.end(); removing++) + { + if (ServerInstance->Modules->Unload(removing->c_str())) + { + ServerInstance->SNO->WriteToSnoMask('A', "*** REHASH UNLOADED MODULE: %s",removing->c_str()); + + if (user) + user->WriteNumeric(973, "%s %s :Module %s successfully unloaded.",user->nick, removing->c_str(), removing->c_str()); + + rem++; + } + else + { + if (user) + user->WriteNumeric(972, "%s %s :Failed to unload module %s: %s",user->nick, removing->c_str(), removing->c_str(), ServerInstance->Modules->LastError().c_str()); + } + } + } + + if (!added_modules.empty()) + { + for (std::vector::iterator adding = added_modules.begin(); adding != added_modules.end(); adding++) + { + if (ServerInstance->Modules->Load(adding->c_str())) + { + ServerInstance->SNO->WriteToSnoMask('A', "*** REHASH LOADED MODULE: %s",adding->c_str()); + + if (user) + user->WriteNumeric(975, "%s %s :Module %s successfully loaded.",user->nick, adding->c_str(), adding->c_str()); + + add++; + } + else + { + if (user) + user->WriteNumeric(974, "%s %s :Failed to load module %s: %s",user->nick, adding->c_str(), adding->c_str(), ServerInstance->Modules->LastError().c_str()); + } + } + } + + ServerInstance->Logs->Log("CONFIG", DEFAULT, "Successfully unloaded %lu of %lu modules and loaded %lu of %lu modules.",(unsigned long)rem,(unsigned long)removed_modules.size(),(unsigned long)add,(unsigned long)added_modules.size()); + + ServerInstance->Threads->Mutex(false); + + } + + /** Note: This is safe, the method checks for user == NULL */ + ServerInstance->Threads->Mutex(true); + ServerInstance->Parser->SetupCommandTable(user); + ServerInstance->Threads->Mutex(false); + + if (user) + user->WriteServ("NOTICE %s :*** Successfully rehashed server.", user->nick); + else + ServerInstance->SNO->WriteToSnoMask('A', "*** Successfully rehashed server."); + +} + + +bool ServerConfig::LoadConf(ConfigDataHash &target, FILE* &conf, const char* filename, std::ostringstream &errorstream) +{ + 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; + + ServerInstance->Logs->Log("CONFIG", DEBUG, "Reading %s", filename); + + /* Check if the file open failed first */ + if (!conf) + { + errorstream << "LoadConf: Couldn't open config file: " << filename << std::endl; + return false; + } + + 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 (!feof(conf)) + { + ch = fgetc(conf); + /* + * 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 (!feof(conf)) + { + real_character = fgetc(conf); + 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_tag) + { + // errorstream << "Closing config tag on line " << linenumber << std::endl; + in_tag = false; + + /* + * If this finds an 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; + } + } + } + } + + /* Fix for bug #392 - if we reach the end of a file and we are still in a quote or comment, most likely the user fucked up */ + if (in_comment || in_quote) + { + errorstream << "Reached end of file whilst still inside a quoted section or tag. This is most likely an error or there \ + is a newline missing from the end of the file: " << filename << ":" << linenumber << std::endl; + } + + return true; +} + + +bool ServerConfig::LoadConf(ConfigDataHash &target, FILE* &conf, const std::string &filename, std::ostringstream &errorstream) +{ + return this->LoadConf(target, conf, filename.c_str(), errorstream); } bool ServerConfig::ParseLine(ConfigDataHash &target, std::string &line, long &linenumber, std::ostringstream &errorstream) @@ -1544,6 +1544,12 @@ bool ServerConfig::ParseLine(ConfigDataHash &target, std::string &line, long &li if (!this->DoInclude(target, current_value, errorstream)) return false; } + else if ((tagname == "include") && (current_key == "executable")) + { + /* Pipe an executable and use its stdout as config data */ + if (!this->DoPipe(target, current_value, errorstream)) + return false; + } current_key.clear(); current_value.clear(); @@ -1566,6 +1572,22 @@ bool ServerConfig::ParseLine(ConfigDataHash &target, std::string &line, long &li return true; } +bool ServerConfig::DoPipe(ConfigDataHash &target, const std::string &file, std::ostringstream &errorstream) +{ + FILE* conf = popen(file.c_str(), "r"); + bool ret = false; + + if (conf) + { + ret = LoadConf(target, conf, file.c_str(), errorstream); + pclose(conf); + } + else + errorstream << "Couldn't execute: " << file << std::endl; + + return ret; +} + bool ServerConfig::DoInclude(ConfigDataHash &target, const std::string &file, std::ostringstream &errorstream) { std::string confpath; @@ -1592,7 +1614,18 @@ bool ServerConfig::DoInclude(ConfigDataHash &target, const std::string &file, st } } - return LoadConf(target, newfile, errorstream); + FILE* conf = fopen(newfile.c_str(), "r"); + bool ret = false; + + if (conf) + { + ret = LoadConf(target, conf, newfile, errorstream); + fclose(conf); + } + else + errorstream << "Couldn't open config file: " << file << std::endl; + + return ret; } bool ServerConfig::ConfValue(ConfigDataHash &target, const char* tag, const char* var, int index, char* result, int length, bool allow_linefeeds) @@ -2041,8 +2074,12 @@ bool InitClasses(ServerConfig* conf, const char*) { for (operclass_t::iterator n = conf->operclass.begin(); n != conf->operclass.end(); n++) { - if (n->second) - delete[] n->second; + if (n->second.commandlist) + delete[] n->second.commandlist; + if (n->second.cmodelist) + delete[] n->second.cmodelist; + if (n->second.umodelist) + delete[] n->second.umodelist; } } @@ -2065,12 +2102,31 @@ bool DoType(ServerConfig* conf, const char*, char**, ValueList &values, int*) /* * XXX should this be in a class? -- w00t */ -bool DoClass(ServerConfig* conf, const char*, char**, ValueList &values, int*) +bool DoClass(ServerConfig* conf, const char* tag, char**, ValueList &values, int*) { const char* ClassName = values[0].GetString(); const char* CommandList = values[1].GetString(); + const char* UModeList = values[2].GetString(); + const char* CModeList = values[3].GetString(); + + for (const char* c = UModeList; *c; ++c) + { + if ((*c < 'A' || *c > 'z') && *c != '*') + { + throw CoreException("Character " + std::string(1, *c) + " is not a valid mode in "); + } + } + for (const char* c = CModeList; *c; ++c) + { + if ((*c < 'A' || *c > 'z') && *c != '*') + { + throw CoreException("Character " + std::string(1, *c) + " is not a valid mode in "); + } + } - conf->operclass[ClassName] = strnewdup(CommandList); + conf->operclass[ClassName].commandlist = strnewdup(CommandList); + conf->operclass[ClassName].umodelist = strnewdup(UModeList); + conf->operclass[ClassName].cmodelist = strnewdup(CModeList); return true; } @@ -2160,5 +2216,8 @@ void ConfigReaderThread::Run() { /* TODO: TheUser may be invalid by the time we get here! Check its validity, or pass a UID would be better */ ServerInstance->Config->Read(do_bail, TheUser); + ServerInstance->Threads->Mutex(true); + this->SetExitFlag(); + ServerInstance->Threads->Mutex(false); }