]> git.netwichtig.de Git - user/henk/code/inspircd.git/blobdiff - src/configparser.cpp
Fix a bunch of weird indentation and spacing issues.
[user/henk/code/inspircd.git] / src / configparser.cpp
index 8250530443c7b38c96aadd93336c21929f8910a5..32b705e8b666a8e98228d3d44616f774ce727606 100644 (file)
@@ -1,6 +1,12 @@
 /*
  * InspIRCd -- Internet Relay Chat Daemon
  *
+ *   Copyright (C) 2018 linuxdaemon <linuxdaemon.irc@gmail.com>
+ *   Copyright (C) 2013-2014, 2016-2020 Sadie Powell <sadie@witchery.services>
+ *   Copyright (C) 2013 ChrisTX <xpipe@hotmail.de>
+ *   Copyright (C) 2012-2014 Attila Molnar <attilamolnar@hush.com>
+ *   Copyright (C) 2012 Robby <robby@chatbelgie.be>
+ *   Copyright (C) 2010 Craig Edwards <brain@inspircd.org>
  *   Copyright (C) 2009-2010 Daniel De Graaf <danieldg@inspircd.org>
  *
  * This file is part of InspIRCd.  InspIRCd is free software: you can
 #include <fstream>
 #include "configparser.h"
 
+enum ParseFlags
+{
+       // Legacy config parsing should be used.
+       FLAG_USE_COMPAT = 1,
+
+       // Executable includes are disabled.
+       FLAG_NO_EXEC = 2,
+
+       // All includes are disabled.
+       FLAG_NO_INC = 4,
+
+       // &env.FOO; is disabled.
+       FLAG_NO_ENV = 8,
+
+       // It's okay if an include doesn't exist.
+       FLAG_MISSING_OKAY = 16
+};
+
+// Represents the position within a config file.
+struct FilePosition
+{
+       // The name of the file which is being read.
+       std::string name;
+
+       // The line of the file that this position points to.
+       unsigned int line;
+
+       // The column of the file that this position points to.
+       unsigned int column;
+
+       FilePosition(const std::string& Name)
+               : name(Name)
+               , line(1)
+               , column(1)
+       {
+       }
+
+       /** Returns a string that represents this file position. */
+       std::string str()
+       {
+               return name + ":" + ConvToStr(line) + ":" + ConvToStr(column);
+       }
+};
+
+// RAII wrapper for FILE* which closes the file when it goes out of scope.
+class FileWrapper
+{
+ private:
+       // Whether this file handle should be closed with pclose.
+       bool close_with_pclose;
+
+       // The file handle which is being wrapped.
+       FILE* const file;
+
+ public:
+       FileWrapper(FILE* File, bool CloseWithPClose = false)
+               : close_with_pclose(CloseWithPClose)
+               , file(File)
+       {
+       }
+
+       // Operator which determines whether the file is open.
+       operator bool() { return (file != NULL); }
+
+       // Operator which retrieves the underlying FILE pointer.
+       operator FILE*() { return file; }
+
+       ~FileWrapper()
+       {
+               if (!file)
+                       return;
+
+               if (close_with_pclose)
+                       pclose(file);
+               else
+                       fclose(file);
+       }
+};
+
+
 struct Parser
 {
        ParseStack& stack;
        int flags;
        FILE* const file;
-       fpos current;
-       fpos last_tag;
+       FilePosition current;
+       FilePosition last_tag;
        reference<ConfigTag> tag;
        int ungot;
        std::string mandatory_tag;
@@ -52,11 +138,11 @@ struct Parser
                else if (ch == '\n')
                {
                        current.line++;
-                       current.col = 0;
+                       current.column = 0;
                }
                else
                {
-                       current.col++;
+                       current.column++;
                }
                return ch;
        }
@@ -78,12 +164,20 @@ struct Parser
                }
        }
 
+       bool wordchar(char ch)
+       {
+               return isalnum(ch)
+                       || ch == '-'
+                       || ch == '.'
+                       || ch == '_';
+       }
+
        void nextword(std::string& rv)
        {
                int ch = next();
                while (isspace(ch))
                        ch = next();
-               while (isalnum(ch) || ch == '_'|| ch == '-')
+               while (wordchar(ch))
                {
                        rv.push_back(ch);
                        ch = next();
@@ -125,7 +219,7 @@ struct Parser
                                while (1)
                                {
                                        ch = next();
-                                       if (isalnum(ch) || (varname.empty() && ch == '#'))
+                                       if (wordchar(ch) || (varname.empty() && ch == '#'))
                                                varname.push_back(ch);
                                        else if (ch == ';')
                                                break;
@@ -153,6 +247,17 @@ struct Parser
                                                throw CoreException("Invalid numeric character reference '&" + varname + ";'");
                                        value.push_back(static_cast<char>(lvalue));
                                }
+                               else if (varname.compare(0, 4, "env.") == 0)
+                               {
+                                       if (flags & FLAG_NO_ENV)
+                                               throw CoreException("XML environment entity reference in file included with noenv=\"yes\"");
+
+                                       const char* envstr = getenv(varname.c_str() + 4);
+                                       if (!envstr)
+                                               throw CoreException("Undefined XML environment entity reference '&" + varname + ";'");
+
+                                       value.append(envstr);
+                               }
                                else
                                {
                                        insp::flat_map<std::string, std::string>::iterator var = stack.vars.find(varname);
@@ -200,7 +305,7 @@ struct Parser
                        throw CoreException("Empty tag name");
 
                ConfigItems* items;
-               tag = ConfigTag::create(name, current.filename, current.line, items);
+               tag = ConfigTag::create(name, current.name, current.line, items);
 
                while (kv(items))
                {
@@ -286,7 +391,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");
                                }
@@ -317,11 +423,43 @@ void ParseStack::DoInclude(ConfigTag* tag, int flags)
        {
                if (tag->getBool("noinclude", false))
                        flags |= FLAG_NO_INC;
+
                if (tag->getBool("noexec", false))
                        flags |= FLAG_NO_EXEC;
+
+               if (tag->getBool("noenv", false))
+                       flags |= FLAG_NO_ENV;
+
+               if (tag->getBool("missingokay", false))
+                       flags |= FLAG_MISSING_OKAY;
+               else
+                       flags &= ~FLAG_MISSING_OKAY;
+
                if (!ParseFile(ServerInstance->Config->Paths.PrependConfig(name), flags, mandatorytag))
                        throw CoreException("Included");
        }
+       else if (tag->readString("directory", name))
+       {
+               if (tag->getBool("noinclude", false))
+                       flags |= FLAG_NO_INC;
+               if (tag->getBool("noexec", false))
+                       flags |= FLAG_NO_EXEC;
+               if (tag->getBool("noenv", false))
+                       flags |= FLAG_NO_ENV;
+
+               const std::string includedir = ServerInstance->Config->Paths.PrependConfig(name);
+               std::vector<std::string> files;
+               if (!FileSystem::GetFileList(includedir, files, "*.conf"))
+                       throw CoreException("Unable to read directory for include: " + includedir);
+
+               std::sort(files.begin(), files.end());
+               for (std::vector<std::string>::const_iterator iter = files.begin(); iter != files.end(); ++iter)
+               {
+                       const std::string path = includedir + '/' + *iter;
+                       if (!ParseFile(path, flags, mandatorytag))
+                               throw CoreException("Included");
+               }
+       }
        else if (tag->readString("executable", name))
        {
                if (flags & FLAG_NO_EXEC)
@@ -330,6 +468,9 @@ void ParseStack::DoInclude(ConfigTag* tag, int flags)
                        flags |= FLAG_NO_INC;
                if (tag->getBool("noexec", true))
                        flags |= FLAG_NO_EXEC;
+               if (tag->getBool("noenv", true))
+                       flags |= FLAG_NO_ENV;
+
                if (!ParseFile(name, flags, mandatorytag, true))
                        throw CoreException("Included");
        }
@@ -373,7 +514,12 @@ bool ParseStack::ParseFile(const std::string& path, int flags, const std::string
 
        FileWrapper file((isexec ? popen(path.c_str(), "r") : fopen(path.c_str(), "r")), isexec);
        if (!file)
+       {
+               if (flags & FLAG_MISSING_OKAY)
+                       return true;
+
                throw CoreException("Could not read \"" + path + "\" for include");
+       }
 
        reading.push_back(path);
        Parser p(*this, flags, file, path, mandatory_tag);
@@ -389,7 +535,7 @@ bool ConfigTag::readString(const std::string& key, std::string& value, bool allo
                if(j->first != key)
                        continue;
                value = j->second;
-               if (!allow_lf && (value.find('\n') != std::string::npos))
+               if (!allow_lf && (value.find('\n') != std::string::npos))
                {
                        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.");
@@ -497,7 +643,7 @@ namespace
 long ConfigTag::getInt(const std::string &key, long def, long min, long max)
 {
        std::string result;
-       if(!readString(key, result))
+       if(!readString(key, result) || result.empty())
                return def;
 
        const char* res_cstr = result.c_str();
@@ -514,7 +660,7 @@ long ConfigTag::getInt(const std::string &key, long def, long min, long max)
 unsigned long ConfigTag::getUInt(const std::string& key, unsigned long def, unsigned long min, unsigned long max)
 {
        std::string result;
-       if (!readString(key, result))
+       if (!readString(key, result) || result.empty())
                return def;
 
        const char* res_cstr = result.c_str();
@@ -531,10 +677,17 @@ unsigned long ConfigTag::getUInt(const std::string& key, unsigned long def, unsi
 unsigned long ConfigTag::getDuration(const std::string& key, unsigned long def, unsigned long min, unsigned long max)
 {
        std::string duration;
-       if (!readString(key, duration))
+       if (!readString(key, duration) || duration.empty())
+               return def;
+
+       unsigned long ret;
+       if (!InspIRCd::Duration(duration, ret))
+       {
+               ServerInstance->Logs->Log("CONFIG", LOG_DEFAULT, "Value of <" + tag + ":" + key + "> at " + getTagLocation() +
+                       " is not a duration; value set to " + ConvToStr(def) + ".");
                return def;
+       }
 
-       unsigned long ret = InspIRCd::Duration(duration);
        CheckRange(tag, key, ret, def, min, max);
        return ret;
 }
@@ -553,12 +706,13 @@ double ConfigTag::getFloat(const std::string& key, double def, double min, doubl
 bool ConfigTag::getBool(const std::string &key, bool def)
 {
        std::string result;
-       if(!readString(key, result))
+       if(!readString(key, result) || result.empty())
                return def;
 
-       if (result == "yes" || result == "true" || result == "1" || result == "on")
+       if (stdalgo::string::equalsci(result, "yes") || stdalgo::string::equalsci(result, "true") || stdalgo::string::equalsci(result, "on") || result == "1")
                return true;
-       if (result == "no" || result == "false" || result == "0" || result == "off")
+
+       if (stdalgo::string::equalsci(result, "no") || stdalgo::string::equalsci(result, "false") || stdalgo::string::equalsci(result, "off") || result == "0")
                return false;
 
        ServerInstance->Logs->Log("CONFIG", LOG_DEFAULT, "Value of <" + tag + ":" + key + "> at " + getTagLocation() +