summaryrefslogtreecommitdiff
path: root/src/configparser.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/configparser.cpp')
-rw-r--r--src/configparser.cpp306
1 files changed, 198 insertions, 108 deletions
diff --git a/src/configparser.cpp b/src/configparser.cpp
index 409ebdd39..437b3cdb0 100644
--- a/src/configparser.cpp
+++ b/src/configparser.cpp
@@ -91,7 +91,7 @@ struct Parser
unget(ch);
}
- bool kv(std::vector<KeyVal>* items, std::set<std::string>& seen)
+ bool kv(ConfigItems* items)
{
std::string key;
nextword(key);
@@ -119,13 +119,13 @@ struct Parser
while (1)
{
ch = next();
- if (ch == '&' && (flags & FLAG_USE_XML))
+ 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;
@@ -136,12 +136,32 @@ struct Parser
throw CoreException("Parse error");
}
}
- std::map<std::string, std::string>::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<char>(lvalue));
+ }
+ else
+ {
+ insp::flat_map<std::string, std::string>::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_XML))
+ else if (ch == '\\' && (flags & FLAG_USE_COMPAT))
{
int esc = next();
if (esc == 'n')
@@ -153,14 +173,14 @@ struct Parser
}
else if (ch == '"')
break;
- else
+ else if (ch != '\r')
value.push_back(ch);
}
- if (!seen.insert(key).second)
+ if (items->find(key) != items->end())
throw CoreException("Duplicate key '" + key + "' found");
- items->push_back(KeyVal(key, value));
+ (*items)[key] = value;
return true;
}
@@ -179,11 +199,13 @@ struct Parser
if (name.empty())
throw CoreException("Empty tag name");
- std::vector<KeyVal>* items;
- std::set<std::string> seen;
+ ConfigItems* items;
tag = ConfigTag::create(name, current.filename, current.line, items);
- while (kv(items, seen));
+ while (kv(items))
+ {
+ // Do nothing here (silences a GCC warning).
+ }
if (name == mandatory_tag)
{
@@ -191,27 +213,27 @@ struct Parser
mandatory_tag.clear();
}
- if (name == "include")
+ if (stdalgo::string::equalsci(name, "include"))
{
stack.DoInclude(tag, flags);
}
- else if (name == "files")
+ else if (stdalgo::string::equalsci(name, "files"))
{
- for(std::vector<KeyVal>::iterator i = items->begin(); i != items->end(); i++)
+ for(ConfigItems::iterator i = items->begin(); i != items->end(); i++)
{
stack.DoReadFile(i->first, i->second, flags, false);
}
}
- else if (name == "execfiles")
+ else if (stdalgo::string::equalsci(name, "execfiles"))
{
- for(std::vector<KeyVal>::iterator i = items->begin(); i != items->end(); i++)
+ for(ConfigItems::iterator i = items->begin(); i != items->end(); i++)
{
stack.DoReadFile(i->first, i->second, flags, true);
}
}
- else if (name == "define")
+ else if (stdalgo::string::equalsci(name, "define"))
{
- if (!(flags & FLAG_USE_XML))
+ if (flags & FLAG_USE_COMPAT)
throw CoreException("<define> tags may only be used in XML-style config (add <config format=\"xml\">)");
std::string varname = tag->getString("name");
std::string value = tag->getString("value");
@@ -219,13 +241,13 @@ struct Parser
throw CoreException("Variable definition must include variable name");
stack.vars[varname] = value;
}
- else if (name == "config")
+ else if (stdalgo::string::equalsci(name, "config"))
{
std::string format = tag->getString("format");
- if (format == "xml")
- flags |= FLAG_USE_XML;
- else if (format == "compat")
- flags &= ~FLAG_USE_XML;
+ if (stdalgo::string::equalsci(format, "xml"))
+ flags &= ~FLAG_USE_COMPAT;
+ else if (stdalgo::string::equalsci(format, "compat"))
+ flags |= FLAG_USE_COMPAT;
else if (!format.empty())
throw CoreException("Unknown configuration format " + format);
}
@@ -264,7 +286,8 @@ struct Parser
break;
case 0xFE:
case 0xFF:
- stack.errstr << "Do not save your files as UTF-16; use ASCII!\n";
+ stack.errstr << "Do not save your files as UTF-16 or UTF-32, use UTF-8!\n";
+ /*@fallthrough@*/
default:
throw CoreException("Syntax error - start of tag expected");
}
@@ -297,7 +320,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, mandatorytag))
+ if (!ParseFile(ServerInstance->Config->Paths.PrependConfig(name), flags, mandatorytag))
throw CoreException("Included");
}
else if (tag->readString("executable", name))
@@ -308,7 +331,7 @@ void ParseStack::DoInclude(ConfigTag* tag, int flags)
flags |= FLAG_NO_INC;
if (tag->getBool("noexec", true))
flags |= FLAG_NO_EXEC;
- if (!ParseExec(name, flags, mandatorytag))
+ if (!ParseFile(name, flags, mandatorytag, true))
throw CoreException("Included");
}
}
@@ -320,14 +343,15 @@ void ParseStack::DoReadFile(const std::string& key, const std::string& name, int
if (exec && (flags & FLAG_NO_EXEC))
throw CoreException("Invalid <execfiles> tag in file included with noexec=\"yes\"");
- FileWrapper file(exec ? popen(name.c_str(), "r") : fopen(name.c_str(), "r"), exec);
+ 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 \"" + key + "\" file");
+ throw CoreException("Could not read \"" + path + "\" for \"" + key + "\" file");
file_cache& cache = FilesOutput[key];
cache.clear();
- char linebuf[MAXBUF*10];
+ char linebuf[5120];
while (fgets(linebuf, sizeof(linebuf), file))
{
size_t len = strlen(linebuf);
@@ -340,76 +364,35 @@ void ParseStack::DoReadFile(const std::string& key, const std::string& name, int
}
}
-bool ParseStack::ParseFile(const std::string& name, int flags, const std::string& mandatory_tag)
-{
- 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 */
-
- FileWrapper file(fopen(name.c_str(), "r"));
- if (!file)
- throw CoreException("Could not read \"" + name + "\" for include");
-
- reading.push_back(name);
- Parser p(*this, flags, file, name, mandatory_tag);
- bool ok = p.outer_parse();
- reading.pop_back();
- return ok;
-}
-
-bool ParseStack::ParseExec(const std::string& name, int flags, const std::string& mandatory_tag)
+bool ParseStack::ParseFile(const std::string& path, int flags, const std::string& mandatory_tag, bool isexec)
{
- ServerInstance->Logs->Log("CONFIG", DEBUG, "Reading executable %s", name.c_str());
- for (unsigned int t = 0; t < reading.size(); t++)
- {
- if (std::string(name) == reading[t])
- {
- throw CoreException("Executable " + name + " is included recursively (looped inclusion)");
- }
- }
+ 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"), true);
+ 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, mandatory_tag);
+ reading.push_back(path);
+ Parser p(*this, flags, file, path, mandatory_tag);
bool ok = p.outer_parse();
reading.pop_back();
return ok;
}
-#ifdef __clang__
-# pragma clang diagnostic push
-# pragma clang diagnostic ignored "-Wunknown-pragmas"
-# pragma clang diagnostic ignored "-Wundefined-bool-conversion"
-#elif defined __GNUC__
-# pragma GCC diagnostic push
-# pragma GCC diagnostic ignored "-Wpragmas"
-# pragma GCC diagnostic ignored "-Wnonnull-compare"
-#endif
bool ConfigTag::readString(const std::string& key, std::string& value, bool allow_lf)
{
- // TODO: this is undefined behaviour but changing the API is too risky for 2.0.
- if (!this)
- return false;
- for(std::vector<KeyVal>::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')
@@ -419,20 +402,100 @@ bool ConfigTag::readString(const std::string& key, std::string& value, bool allo
}
return false;
}
-#ifdef __clang__
-# pragma clang diagnostic pop
-#elif defined __GNUC__
-# pragma GCC diagnostic pop
-#endif
-std::string ConfigTag::getString(const std::string& key, const std::string& def)
+std::string ConfigTag::getString(const std::string& key, const std::string& def, const TR1NS::function<bool(const std::string&)>& validator)
+{
+ std::string res;
+ if (!readString(key, res))
+ return def;
+
+ if (!validator(res))
+ {
+ ServerInstance->Logs->Log("CONFIG", LOG_DEFAULT, "WARNING: The value of <%s:%s> is not valid; value set to %s.",
+ tag.c_str(), key.c_str(), def.c_str());
+ return def;
+ }
+ return res;
+}
+
+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 <typename Numeric>
+ 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 <typename Numeric>
+ 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))
@@ -443,27 +506,49 @@ 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;
}
-double ConfigTag::getFloat(const std::string &key, double def)
+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;
- return strtod(result.c_str(), NULL);
+
+ 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, double min, double max)
+{
+ std::string result;
+ if (!readString(key, result))
+ return def;
+
+ double res = strtod(result.c_str(), NULL);
+ CheckRange(tag, key, res, def, min, max);
+ return res;
}
bool ConfigTag::getBool(const std::string &key, bool def)
@@ -477,7 +562,7 @@ bool ConfigTag::getBool(const std::string &key, bool def)
if (result == "no" || result == "false" || result == "0" || result == "off")
return false;
- ServerInstance->Logs->Log("CONFIG",DEFAULT, "Value of <" + tag + ":" + key + "> at " + getTagLocation() +
+ ServerInstance->Logs->Log("CONFIG", LOG_DEFAULT, "Value of <" + tag + ":" + key + "> at " + getTagLocation() +
" is not valid, ignoring");
return def;
}
@@ -487,7 +572,7 @@ 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<KeyVal>*& 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;
@@ -499,6 +584,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;