summaryrefslogtreecommitdiff
path: root/src/configparser.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/configparser.cpp')
-rw-r--r--src/configparser.cpp191
1 files changed, 102 insertions, 89 deletions
diff --git a/src/configparser.cpp b/src/configparser.cpp
index 409ebdd39..2af796b21 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)
{
@@ -197,21 +219,21 @@ struct Parser
}
else if (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")
{
- 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")
{
- 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");
@@ -223,9 +245,9 @@ struct Parser
{
std::string format = tag->getString("format");
if (format == "xml")
- flags |= FLAG_USE_XML;
+ flags &= ~FLAG_USE_COMPAT;
else if (format == "compat")
- flags &= ~FLAG_USE_XML;
+ flags |= FLAG_USE_COMPAT;
else if (!format.empty())
throw CoreException("Unknown configuration format " + format);
}
@@ -297,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, mandatorytag))
+ if (!ParseFile(ServerInstance->Config->Paths.PrependConfig(name), flags, mandatorytag))
throw CoreException("Included");
}
else if (tag->readString("executable", name))
@@ -308,7 +330,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 +342,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 +363,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 +401,23 @@ 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, 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)
+long ConfigTag::getInt(const std::string &key, long def, long min, long max)
{
std::string result;
if(!readString(key, result))
@@ -446,18 +431,41 @@ long ConfigTag::getInt(const std::string &key, long def)
switch (toupper(*res_tail))
{
case 'K':
- res= res* 1024;
+ res = res * 1024;
break;
case 'M':
- res= res* 1024 * 1024;
+ res = res * 1024 * 1024;
break;
case 'G':
- res= res* 1024 * 1024 * 1024;
+ res = res * 1024 * 1024 * 1024;
break;
}
+
+ CheckRange(key, res, def, min, max);
return res;
}
+void ConfigTag::CheckRange(const std::string& key, long& res, long def, long min, long max)
+{
+ if (res < min || res > max)
+ {
+ ServerInstance->Logs->Log("CONFIG", LOG_DEFAULT, "WARNING: <%s:%s> value of %ld is not between %ld and %ld; set to %ld.",
+ tag.c_str(), key.c_str(), res, min, max, def);
+ res = def;
+ }
+}
+
+long ConfigTag::getDuration(const std::string& key, long def, long min, long max)
+{
+ std::string duration;
+ if (!readString(key, duration))
+ return def;
+
+ long ret = InspIRCd::Duration(duration);
+ CheckRange(key, ret, def, min, max);
+ return ret;
+}
+
double ConfigTag::getFloat(const std::string &key, double def)
{
std::string result;
@@ -477,7 +485,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 +495,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 +507,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;