X-Git-Url: https://git.netwichtig.de/gitweb/?a=blobdiff_plain;f=src%2Fconfigparser.cpp;h=5ee86c81178c49acd128ac940ae02aedc05757dc;hb=780dda83ba3857bc3c330d4b5d38bb251a9d879b;hp=1373ae5af3b91d5ec120fc0746117fe6dd49f6f3;hpb=a75e76305da98164f82d3d38730bbc31707503b1;p=user%2Fhenk%2Fcode%2Finspircd.git diff --git a/src/configparser.cpp b/src/configparser.cpp index 1373ae5af..5ee86c811 100644 --- a/src/configparser.cpp +++ b/src/configparser.cpp @@ -1,16 +1,22 @@ -/* +------------------------------------+ - * | Inspire Internet Relay Chat Daemon | - * +------------------------------------+ +/* + * InspIRCd -- Internet Relay Chat Daemon * - * InspIRCd: (C) 2002-2009 InspIRCd Development Team - * See: http://wiki.inspircd.org/Credits + * Copyright (C) 2009-2010 Daniel De Graaf * - * This program is free but copyrighted software; see - * the file COPYING for details. + * This file is part of InspIRCd. InspIRCd is free software: you can + * redistribute it and/or modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation, version 2. * - * --------------------------------------------------- + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . */ + #include "inspircd.h" #include #include "configparser.h" @@ -18,15 +24,16 @@ struct Parser { ParseStack& stack; - const int flags; + int flags; FILE* const file; fpos current; fpos last_tag; reference tag; int ungot; + std::string mandatory_tag; - Parser(ParseStack& me, int myflags, FILE* conf, const std::string& name) - : stack(me), flags(myflags), file(conf), current(name), last_tag(name), ungot(-1) + Parser(ParseStack& me, int myflags, FILE* conf, const std::string& name, const std::string& mandatorytag) + : stack(me), flags(myflags), file(conf), current(name), last_tag(name), ungot(-1), mandatory_tag(mandatorytag) { } int next(bool eof_ok = false) @@ -76,7 +83,7 @@ struct Parser int ch = next(); while (isspace(ch)) ch = next(); - while (isalnum(ch) || ch == '_') + while (isalnum(ch) || ch == '_'|| ch == '-') { rv.push_back(ch); ch = next(); @@ -84,7 +91,7 @@ struct Parser unget(ch); } - bool kv(std::vector* items) + bool kv(ConfigItems* items) { std::string key; nextword(key); @@ -112,13 +119,13 @@ struct Parser while (1) { ch = next(); - if (ch == '&') + if (ch == '&' && !(flags & FLAG_USE_COMPAT)) { std::string varname; while (1) { ch = next(); - if (isalnum(ch)) + if (isalnum(ch) || (varname.empty() && ch == '#')) varname.push_back(ch); else if (ch == ';') break; @@ -129,17 +136,51 @@ struct Parser throw CoreException("Parse error"); } } - std::map::iterator var = stack.vars.find(varname); - if (var == stack.vars.end()) - throw CoreException("Undefined XML entity reference '&" + varname + ";'"); - value.append(var->second); + if (varname.empty()) + throw CoreException("Empty XML entity reference"); + else if (varname[0] == '#' && (varname.size() == 1 || (varname.size() == 2 && varname[1] == 'x'))) + throw CoreException("Empty numeric character reference"); + else if (varname[0] == '#') + { + const char* cvarname = varname.c_str(); + char* endptr; + unsigned long lvalue; + if (cvarname[1] == 'x') + lvalue = strtoul(cvarname + 2, &endptr, 16); + else + lvalue = strtoul(cvarname + 1, &endptr, 10); + if (*endptr != '\0' || lvalue > 255) + throw CoreException("Invalid numeric character reference '&" + varname + ";'"); + value.push_back(static_cast(lvalue)); + } + else + { + insp::flat_map::iterator var = stack.vars.find(varname); + if (var == stack.vars.end()) + throw CoreException("Undefined XML entity reference '&" + varname + ";'"); + value.append(var->second); + } + } + else if (ch == '\\' && (flags & FLAG_USE_COMPAT)) + { + int esc = next(); + if (esc == 'n') + value.push_back('\n'); + else if (isalpha(esc)) + throw CoreException("Unknown escape character \\" + std::string(1, esc)); + else + value.push_back(esc); } else if (ch == '"') break; - else + else if (ch != '\r') value.push_back(ch); } - items->push_back(KeyVal(key, value)); + + if (items->find(key) != items->end()) + throw CoreException("Duplicate key '" + key + "' found"); + + (*items)[key] = value; return true; } @@ -158,23 +199,58 @@ struct Parser if (name.empty()) throw CoreException("Empty tag name"); - std::vector* items; + ConfigItems* items; tag = ConfigTag::create(name, current.filename, current.line, items); - while (kv(items)); + while (kv(items)) + { + // Do nothing here (silences a GCC warning). + } + + if (name == mandatory_tag) + { + // Found the mandatory tag + mandatory_tag.clear(); + } if (name == "include") { stack.DoInclude(tag, flags); } + else if (name == "files") + { + for(ConfigItems::iterator i = items->begin(); i != items->end(); i++) + { + stack.DoReadFile(i->first, i->second, flags, false); + } + } + else if (name == "execfiles") + { + for(ConfigItems::iterator i = items->begin(); i != items->end(); i++) + { + stack.DoReadFile(i->first, i->second, flags, true); + } + } else if (name == "define") { + if (flags & FLAG_USE_COMPAT) + throw CoreException(" tags may only be used in XML-style config (add )"); std::string varname = tag->getString("name"); std::string value = tag->getString("value"); if (varname.empty()) throw CoreException("Variable definition must include variable name"); stack.vars[varname] = value; } + else if (name == "config") + { + std::string format = tag->getString("format"); + if (format == "xml") + flags &= ~FLAG_USE_COMPAT; + else if (format == "compat") + flags |= FLAG_USE_COMPAT; + else if (!format.empty()) + throw CoreException("Unknown configuration format " + format); + } else { stack.output.insert(std::make_pair(name, tag)); @@ -194,6 +270,8 @@ struct Parser { case EOF: // this is the one place where an EOF is not an error + if (!mandatory_tag.empty()) + throw CoreException("Mandatory tag \"" + mandatory_tag + "\" not found"); return true; case '#': comment(); @@ -230,6 +308,10 @@ void ParseStack::DoInclude(ConfigTag* tag, int flags) { if (flags & FLAG_NO_INC) throw CoreException("Invalid tag in file included with noinclude=\"yes\""); + + std::string mandatorytag; + tag->readString("mandatorytag", mandatorytag); + std::string name; if (tag->readString("file", name)) { @@ -237,7 +319,7 @@ void ParseStack::DoInclude(ConfigTag* tag, int flags) flags |= FLAG_NO_INC; if (tag->getBool("noexec", false)) flags |= FLAG_NO_EXEC; - if (!ParseFile(name, flags)) + if (!ParseFile(ServerInstance->Config->Paths.PrependConfig(name), flags, mandatorytag)) throw CoreException("Included"); } else if (tag->readString("executable", name)) @@ -248,54 +330,53 @@ void ParseStack::DoInclude(ConfigTag* tag, int flags) flags |= FLAG_NO_INC; if (tag->getBool("noexec", true)) flags |= FLAG_NO_EXEC; - if (!ParseExec(name, flags)) + if (!ParseFile(name, flags, mandatorytag, true)) throw CoreException("Included"); } } -bool ParseStack::ParseFile(const std::string& name, int flags) +void ParseStack::DoReadFile(const std::string& key, const std::string& name, int flags, bool exec) { - ServerInstance->Logs->Log("CONFIG", DEBUG, "Reading file %s", name.c_str()); - for (unsigned int t = 0; t < reading.size(); t++) - { - if (std::string(name) == reading[t]) - { - throw CoreException("File " + name + " is included recursively (looped inclusion)"); - } - } - - /* It's not already included, add it to the list of files we've loaded */ + if (flags & FLAG_NO_INC) + throw CoreException("Invalid tag in file included with noinclude=\"yes\""); + if (exec && (flags & FLAG_NO_EXEC)) + throw CoreException("Invalid tag in file included with noexec=\"yes\""); - FileWrapper file(fopen(name.c_str(), "r")); + std::string path = ServerInstance->Config->Paths.PrependConfig(name); + FileWrapper file(exec ? popen(name.c_str(), "r") : fopen(path.c_str(), "r"), exec); if (!file) - throw CoreException("Could not read \"" + name + "\" for include"); + throw CoreException("Could not read \"" + path + "\" for \"" + key + "\" file"); - reading.push_back(name); - Parser p(*this, flags, file, name); - bool ok = p.outer_parse(); - reading.pop_back(); - return ok; -} + file_cache& cache = FilesOutput[key]; + cache.clear(); -bool ParseStack::ParseExec(const std::string& name, int flags) -{ - ServerInstance->Logs->Log("CONFIG", DEBUG, "Reading executable %s", name.c_str()); - for (unsigned int t = 0; t < reading.size(); t++) + char linebuf[5120]; + while (fgets(linebuf, sizeof(linebuf), file)) { - if (std::string(name) == reading[t]) + size_t len = strlen(linebuf); + if (len) { - throw CoreException("Executable " + name + " is included recursively (looped inclusion)"); + if (linebuf[len-1] == '\n') + len--; + cache.push_back(std::string(linebuf, len)); } } +} + +bool ParseStack::ParseFile(const std::string& path, int flags, const std::string& mandatory_tag, bool isexec) +{ + ServerInstance->Logs->Log("CONFIG", LOG_DEBUG, "Reading (isexec=%d) %s", isexec, path.c_str()); + if (stdalgo::isin(reading, path)) + throw CoreException((isexec ? "Executable " : "File ") + path + " is included recursively (looped inclusion)"); /* It's not already included, add it to the list of files we've loaded */ - FileWrapper file(popen(name.c_str(), "r")); + FileWrapper file((isexec ? popen(path.c_str(), "r") : fopen(path.c_str(), "r")), isexec); if (!file) - throw CoreException("Could not open executable \"" + name + "\" for include"); + throw CoreException("Could not read \"" + path + "\" for include"); - reading.push_back(name); - Parser p(*this, flags, file, name); + reading.push_back(path); + Parser p(*this, flags, file, path, mandatory_tag); bool ok = p.outer_parse(); reading.pop_back(); return ok; @@ -303,16 +384,14 @@ bool ParseStack::ParseExec(const std::string& name, int flags) bool ConfigTag::readString(const std::string& key, std::string& value, bool allow_lf) { - if (!this) - return false; - for(std::vector::iterator j = items.begin(); j != items.end(); ++j) + for(ConfigItems::iterator j = items.begin(); j != items.end(); ++j) { if(j->first != key) continue; value = j->second; if (!allow_lf && (value.find('\n') != std::string::npos)) { - ServerInstance->Logs->Log("CONFIG",DEFAULT, "Value of <" + tag + ":" + key + "> at " + getTagLocation() + + ServerInstance->Logs->Log("CONFIG", LOG_DEFAULT, "Value of <" + tag + ":" + key + "> at " + getTagLocation() + " contains a linefeed, and linefeeds in this value are not permitted -- stripped to spaces."); for (std::string::iterator n = value.begin(); n != value.end(); n++) if (*n == '\n') @@ -323,14 +402,84 @@ bool ConfigTag::readString(const std::string& key, std::string& value, bool allo return false; } -std::string ConfigTag::getString(const std::string& key, const std::string& def) +std::string ConfigTag::getString(const std::string& key, const std::string& def, size_t minlen, size_t maxlen) { - std::string res = def; - readString(key, res); + std::string res; + if (!readString(key, res)) + return def; + + if (res.length() < minlen || res.length() > maxlen) + { + ServerInstance->Logs->Log("CONFIG", LOG_DEFAULT, "WARNING: The length of <%s:%s> is not between %ld and %ld; value set to %s.", + tag.c_str(), key.c_str(), minlen, maxlen, def.c_str()); + return def; + } return res; } -long ConfigTag::getInt(const std::string &key, long def) +namespace +{ + /** Check for an invalid magnitude specifier. If one is found a warning is logged and the + * value is corrected (set to \p def). + * @param tag The tag name; used in the warning message. + * @param key The key name; used in the warning message. + * @param val The full value set in the config as a string. + * @param num The value to verify and modify if needed. + * @param def The default value, \p res will be set to this if \p tail does not contain a. + * valid magnitude specifier. + * @param tail The location in the config value at which the magnifier is located. + */ + template + void CheckMagnitude(const std::string& tag, const std::string& key, const std::string& val, Numeric& num, Numeric def, const char* tail) + { + // If this is NULL then no magnitude specifier was given. + if (!*tail) + return; + + switch (toupper(*tail)) + { + case 'K': + num *= 1024; + return; + + case 'M': + num *= 1024 * 1024; + return; + + case 'G': + num *= 1024 * 1024 * 1024; + return; + } + + const std::string message = "WARNING: <" + tag + ":" + key + "> value of " + val + " contains an invalid magnitude specifier '" + + tail + "'; value set to " + ConvToStr(def) + "."; + ServerInstance->Logs->Log("CONFIG", LOG_DEFAULT, message); + num = def; + } + + /** Check for an out of range value. If the value falls outside the boundaries a warning is + * logged and the value is corrected (set to \p def). + * @param tag The tag name; used in the warning message. + * @param key The key name; used in the warning message. + * @param num The value to verify and modify if needed. + * @param def The default value, \p res will be set to this if (min <= res <= max) doesn't hold true. + * @param min Minimum accepted value for \p res. + * @param max Maximum accepted value for \p res. + */ + template + void CheckRange(const std::string& tag, const std::string& key, Numeric& num, Numeric def, Numeric min, Numeric max) + { + if (num >= min && num <= max) + return; + + const std::string message = "WARNING: <" + tag + ":" + key + "> value of " + ConvToStr(num) + " is not between " + + ConvToStr(min) + " and " + ConvToStr(max) + "; value set to " + ConvToStr(def) + "."; + ServerInstance->Logs->Log("CONFIG", LOG_DEFAULT, message); + num = def; + } +} + +long ConfigTag::getInt(const std::string &key, long def, long min, long max) { std::string result; if(!readString(key, result)) @@ -341,21 +490,40 @@ long ConfigTag::getInt(const std::string &key, long def) long res = strtol(res_cstr, &res_tail, 0); if (res_tail == res_cstr) return def; - switch (toupper(*res_tail)) - { - case 'K': - res= res* 1024; - break; - case 'M': - res= res* 1024 * 1024; - break; - case 'G': - res= res* 1024 * 1024 * 1024; - break; - } + + CheckMagnitude(tag, key, result, res, def, res_tail); + CheckRange(tag, key, res, def, min, max); return res; } +unsigned long ConfigTag::getUInt(const std::string& key, unsigned long def, unsigned long min, unsigned long max) +{ + std::string result; + if (!readString(key, result)) + return def; + + const char* res_cstr = result.c_str(); + char* res_tail = NULL; + unsigned long res = strtoul(res_cstr, &res_tail, 0); + if (res_tail == res_cstr) + return def; + + CheckMagnitude(tag, key, result, res, def, res_tail); + CheckRange(tag, key, res, def, min, max); + return res; +} + +unsigned long ConfigTag::getDuration(const std::string& key, unsigned long def, unsigned long min, unsigned long max) +{ + std::string duration; + if (!readString(key, duration)) + return def; + + unsigned long ret = InspIRCd::Duration(duration); + CheckRange(tag, key, ret, def, min, max); + return ret; +} + double ConfigTag::getFloat(const std::string &key, double def) { std::string result; @@ -370,7 +538,14 @@ bool ConfigTag::getBool(const std::string &key, bool def) if(!readString(key, result)) return def; - return (result == "yes" || result == "true" || result == "1" || result == "on"); + if (result == "yes" || result == "true" || result == "1" || result == "on") + return true; + if (result == "no" || result == "false" || result == "0" || result == "off") + return false; + + ServerInstance->Logs->Log("CONFIG", LOG_DEFAULT, "Value of <" + tag + ":" + key + "> at " + getTagLocation() + + " is not valid, ignoring"); + return def; } std::string ConfigTag::getTagLocation() @@ -378,10 +553,10 @@ std::string ConfigTag::getTagLocation() return src_name + ":" + ConvToStr(src_line); } -ConfigTag* ConfigTag::create(const std::string& Tag, const std::string& file, int line, std::vector*&items) +ConfigTag* ConfigTag::create(const std::string& Tag, const std::string& file, int line, ConfigItems*& Items) { ConfigTag* rv = new ConfigTag(Tag, file, line); - items = &rv->items; + Items = &rv->items; return rv; } @@ -390,6 +565,11 @@ ConfigTag::ConfigTag(const std::string& Tag, const std::string& file, int line) { } +OperInfo::OperInfo(const std::string& Name) + : name(Name) +{ +} + std::string OperInfo::getConfig(const std::string& key) { std::string rv;