]> git.netwichtig.de Git - user/henk/code/inspircd.git/blobdiff - src/configparser.cpp
Keep multiple IOHookProvider references in class ListenSocket
[user/henk/code/inspircd.git] / src / configparser.cpp
index 0e77cbb3679deaf445bf9e4dd2c1fe29745ed8a0..3be1ac9c53d39ce44f0a79dcd6d81defa55735f9 100644 (file)
@@ -1,16 +1,22 @@
-/*       +------------------------------------+
- *       | Inspire Internet Relay Chat Daemon |
- *       +------------------------------------+
+/*
+ * InspIRCd -- Internet Relay Chat Daemon
  *
- *  InspIRCd: (C) 2002-2010 InspIRCd Development Team
- * See: http://wiki.inspircd.org/Credits
+ *   Copyright (C) 2009-2010 Daniel De Graaf <danieldg@inspircd.org>
  *
- * 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 <http://www.gnu.org/licenses/>.
  */
 
+
 #include "inspircd.h"
 #include <fstream>
 #include "configparser.h"
@@ -24,9 +30,10 @@ struct Parser
        fpos last_tag;
        reference<ConfigTag> 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<KeyVal>* items)
+       bool kv(std::vector<KeyVal>* items, std::set<std::string>& seen)
        {
                std::string key;
                nextword(key);
@@ -112,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;
@@ -129,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')
@@ -149,6 +176,10 @@ struct Parser
                        else
                                value.push_back(ch);
                }
+
+               if (!seen.insert(key).second)
+                       throw CoreException("Duplicate key '" + key + "' found");
+
                items->push_back(KeyVal(key, value));
                return true;
        }
@@ -169,17 +200,41 @@ struct Parser
                        throw CoreException("Empty tag name");
 
                std::vector<KeyVal>* items;
+               std::set<std::string> seen;
                tag = ConfigTag::create(name, current.filename, current.line, items);
 
-               while (kv(items));
+               while (kv(items, seen))
+               {
+                       // 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(std::vector<KeyVal>::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++)
+                       {
+                               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");
@@ -191,9 +246,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);
                }
@@ -216,6 +271,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();
@@ -252,6 +309,10 @@ void ParseStack::DoInclude(ConfigTag* tag, int flags)
 {
        if (flags & FLAG_NO_INC)
                throw CoreException("Invalid <include> tag in file included with noinclude=\"yes\"");
+
+       std::string mandatorytag;
+       tag->readString("mandatorytag", mandatorytag);
+
        std::string name;
        if (tag->readString("file", name))
        {
@@ -259,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))
+               if (!ParseFile(ServerInstance->Config->Paths.PrependConfig(name), flags, mandatorytag))
                        throw CoreException("Included");
        }
        else if (tag->readString("executable", name))
@@ -270,54 +331,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 <files> tag in file included with noinclude=\"yes\"");
+       if (exec && (flags & FLAG_NO_EXEC))
+               throw CoreException("Invalid <execfiles> 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;
@@ -325,8 +385,6 @@ 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<KeyVal>::iterator j = items.begin(); j != items.end(); ++j)
        {
                if(j->first != key)
@@ -334,7 +392,7 @@ bool ConfigTag::readString(const std::string& key, std::string& value, bool allo
                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')
@@ -352,7 +410,7 @@ std::string ConfigTag::getString(const std::string& key, const std::string& 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))
@@ -366,18 +424,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;
@@ -392,7 +473,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()
@@ -400,10 +488,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<KeyVal>*&items)
+ConfigTag* ConfigTag::create(const std::string& Tag, const std::string& file, int line, std::vector<KeyVal>*& Items)
 {
        ConfigTag* rv = new ConfigTag(Tag, file, line);
-       items = &rv->items;
+       Items = &rv->items;
        return rv;
 }