diff options
-rw-r--r-- | include/configparser.h | 52 | ||||
-rw-r--r-- | src/configparser.cpp | 387 | ||||
-rw-r--r-- | src/configreader.cpp | 425 |
3 files changed, 440 insertions, 424 deletions
diff --git a/include/configparser.h b/include/configparser.h new file mode 100644 index 000000000..a1d7aff5b --- /dev/null +++ b/include/configparser.h @@ -0,0 +1,52 @@ +struct fpos +{ + std::string filename; + int line; + int col; + fpos(const std::string& name, int l = 1, int c = 1) : filename(name), line(l), col(c) {} + std::string str() + { + return filename + ":" + ConvToStr(line) + ":" + ConvToStr(col); + } +}; + +enum ParseFlags +{ + FLAG_NO_EXEC = 1, + FLAG_NO_INC = 2 +}; + +struct ParseStack +{ + std::vector<std::string> reading; + std::map<std::string, std::string> vars; + ConfigDataHash& output; + std::stringstream& errstr; + + ParseStack(ServerConfig* conf) + : output(conf->config_data), errstr(conf->errstr) + { + vars["amp"] = "&"; + vars["quot"] = "\""; + vars["newline"] = vars["nl"] = "\n"; + } + bool ParseFile(const std::string& name, int flags); + bool ParseExec(const std::string& name, int flags); + void DoInclude(ConfigTag* includeTag, int flags); +}; + +/** RAII wrapper on FILE* to close files on exceptions */ +struct FileWrapper +{ + FILE* const f; + FileWrapper(FILE* file) : f(file) {} + operator bool() { return f; } + operator FILE*() { return f; } + ~FileWrapper() + { + if (f) + fclose(f); + } +}; + + diff --git a/src/configparser.cpp b/src/configparser.cpp new file mode 100644 index 000000000..22c3679ab --- /dev/null +++ b/src/configparser.cpp @@ -0,0 +1,387 @@ +/* +------------------------------------+ + * | Inspire Internet Relay Chat Daemon | + * +------------------------------------+ + * + * InspIRCd: (C) 2002-2009 InspIRCd Development Team + * See: http://wiki.inspircd.org/Credits + * + * This program is free but copyrighted software; see + * the file COPYING for details. + * + * --------------------------------------------------- + */ + +#include "inspircd.h" +#include <fstream> +#include "configparser.h" + +struct Parser +{ + ParseStack& stack; + const int flags; + FILE* const file; + fpos current; + fpos last_tag; + reference<ConfigTag> tag; + int ungot; + + Parser(ParseStack& me, int myflags, FILE* conf, const std::string& name) + : stack(me), flags(myflags), file(conf), current(name), last_tag(name), ungot(-1) + { } + + int next(bool eof_ok = false) + { + if (ungot != -1) + { + int ch = ungot; + ungot = -1; + return ch; + } + int ch = fgetc(file); + if (ch == EOF && !eof_ok) + { + throw CoreException("Unexpected end-of-file"); + } + else if (ch == '\n') + { + current.line++; + current.col = 0; + } + else + { + current.col++; + } + return ch; + } + + void unget(int ch) + { + if (ungot != -1) + throw CoreException("INTERNAL ERROR: cannot unget twice"); + ungot = ch; + } + + void comment() + { + while (1) + { + int ch = next(); + if (ch == '\n') + return; + } + } + + void nextword(std::string& rv) + { + int ch = next(); + while (isspace(ch)) + ch = next(); + while (isalnum(ch) || ch == '_') + { + rv.push_back(ch); + ch = next(); + } + unget(ch); + } + + bool kv() + { + std::string key; + nextword(key); + int ch = next(); + if (ch == '>' && key.empty()) + { + return false; + } + else if (ch == '#' && key.empty()) + { + comment(); + return true; + } + else if (ch != '=') + { + throw CoreException("Invalid character " + std::string(1, ch) + " in key (" + key + ")"); + } + + std::string value; + ch = next(); + if (ch != '"') + { + throw CoreException("Invalid character in value of <" + tag->tag + ":" + key + ">"); + } + while (1) + { + ch = next(); + if (ch == '&') + { + std::string varname; + while (1) + { + ch = next(); + if (isalnum(ch)) + varname.push_back(ch); + else if (ch == ';') + break; + else + { + stack.errstr << "Invalid XML entity name in value of <" + tag->tag + ":" + key + ">\n" + << "To include an ampersand or quote, use & or "\n"; + 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); + } + else if (ch == '"') + break; + value.push_back(ch); + } + tag->items.push_back(KeyVal(key, value)); + return true; + } + + void dotag() + { + last_tag = current; + std::string name; + nextword(name); + + int spc = next(); + if (spc == '>') + unget(spc); + else if (!isspace(spc)) + throw CoreException("Invalid character in tag name"); + + if (name.empty()) + throw CoreException("Empty tag name"); + + tag = new ConfigTag(name, current.filename, current.line); + + while (kv()); + + if (name == "include") + { + stack.DoInclude(tag, flags); + } + else if (name == "define") + { + 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 + { + stack.output.insert(std::make_pair(name, tag)); + } + // this is not a leak; reference<> takes care of the delete + tag = NULL; + } + + bool outer_parse() + { + try + { + while (1) + { + int ch = next(true); + switch (ch) + { + case EOF: + // this is the one place where an EOF is not an error + return true; + case '#': + comment(); + break; + case '<': + dotag(); + break; + case ' ': + case '\r': + case '\t': + case '\n': + break; + case 0xFE: + case 0xFF: + stack.errstr << "Do not save your files as UTF-16; use ASCII!\n"; + default: + throw CoreException("Syntax error - start of tag expected"); + } + } + } + catch (CoreException& err) + { + stack.errstr << err.GetReason() << " at " << current.str(); + if (tag) + stack.errstr << " (inside tag " << tag->tag << " at line " << tag->src_line << ")\n"; + else + stack.errstr << " (last tag was on line " << last_tag.line << ")\n"; + } + return false; + } +}; + +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 name; + if (tag->readString("file", name)) + { + if (tag->getBool("noinclude", false)) + flags |= FLAG_NO_INC; + if (tag->getBool("noexec", false)) + flags |= FLAG_NO_EXEC; + if (!ParseFile(name, flags)) + throw CoreException("Included"); + } + else if (tag->readString("executable", name)) + { + if (flags & FLAG_NO_EXEC) + throw CoreException("Invalid <include:executable> tag in file included with noexec=\"yes\""); + if (tag->getBool("noinclude", false)) + flags |= FLAG_NO_INC; + if (tag->getBool("noexec", true)) + flags |= FLAG_NO_EXEC; + if (!ParseExec(name, flags)) + throw CoreException("Included"); + } +} + +bool ParseStack::ParseFile(const std::string& name, int flags) +{ + 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); + bool ok = p.outer_parse(); + reading.pop_back(); + return ok; +} + +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++) + { + if (std::string(name) == reading[t]) + { + throw CoreException("Executable " + name + " 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")); + if (!file) + throw CoreException("Could not open executable \"" + name + "\" for include"); + + reading.push_back(name); + Parser p(*this, flags, file, name); + bool ok = p.outer_parse(); + reading.pop_back(); + return ok; +} + +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) + continue; + value = j->second; + if (!allow_lf && (value.find('\n') != std::string::npos)) + { + ServerInstance->Logs->Log("CONFIG",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') + *n = ' '; + } + return true; + } + return false; +} + +std::string ConfigTag::getString(const std::string& key, const std::string& def) +{ + std::string res = def; + readString(key, res); + return res; +} + +long ConfigTag::getInt(const std::string &key, long def) +{ + std::string result; + if(!readString(key, result)) + return def; + + const char* res_cstr = result.c_str(); + char* res_tail = NULL; + 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; + } + return res; +} + +double ConfigTag::getFloat(const std::string &key, double def) +{ + std::string result; + if (!readString(key, result)) + return def; + return strtod(result.c_str(), NULL); +} + +bool ConfigTag::getBool(const std::string &key, bool def) +{ + std::string result; + if(!readString(key, result)) + return def; + + return (result == "yes" || result == "true" || result == "1" || result == "on"); +} + +std::string ConfigTag::getTagLocation() +{ + return src_name + ":" + ConvToStr(src_line); +} + +std::string OperInfo::getConfig(const std::string& key) +{ + std::string rv; + if (type_block) + type_block->readString(key, rv); + if (oper_block) + oper_block->readString(key, rv); + return rv; +} diff --git a/src/configreader.cpp b/src/configreader.cpp index 9a315fe0b..5fa56ba8f 100644 --- a/src/configreader.cpp +++ b/src/configreader.cpp @@ -31,343 +31,7 @@ #include "exitcodes.h" #include "commands/cmd_whowas.h" #include "modes/cmode_h.h" - -struct fpos -{ - std::string filename; - int line; - int col; - fpos(const std::string& name, int l = 1, int c = 1) : filename(name), line(l), col(c) {} - std::string str() - { - return filename + ":" + ConvToStr(line) + ":" + ConvToStr(col); - } -}; - -enum ParseFlags -{ - FLAG_NO_EXEC = 1, - FLAG_NO_INC = 2 -}; - -struct ParseStack -{ - std::vector<std::string> reading; - std::map<std::string, std::string> vars; - ConfigDataHash& output; - std::stringstream& errstr; - - ParseStack(ServerConfig* conf) - : output(conf->config_data), errstr(conf->errstr) - { - vars["amp"] = "&"; - vars["quot"] = "\""; - vars["newline"] = vars["nl"] = "\n"; - } - bool ParseFile(const std::string& name, int flags); - bool ParseExec(const std::string& name, int flags); - void DoInclude(ConfigTag* includeTag, int flags); -}; - -struct Parser -{ - ParseStack& stack; - const int flags; - FILE* const file; - fpos current; - fpos last_tag; - reference<ConfigTag> tag; - int ungot; - - Parser(ParseStack& me, int myflags, FILE* conf, const std::string& name) - : stack(me), flags(myflags), file(conf), current(name), last_tag(name), ungot(-1) - { } - - int next(bool eof_ok = false) - { - if (ungot != -1) - { - int ch = ungot; - ungot = -1; - return ch; - } - int ch = fgetc(file); - if (ch == EOF && !eof_ok) - { - throw CoreException("Unexpected end-of-file"); - } - else if (ch == '\n') - { - current.line++; - current.col = 0; - } - else - { - current.col++; - } - return ch; - } - - void unget(int ch) - { - if (ungot != -1) - throw CoreException("INTERNAL ERROR: cannot unget twice"); - ungot = ch; - } - - void comment() - { - while (1) - { - int ch = next(); - if (ch == '\n') - return; - } - } - - void nextword(std::string& rv) - { - int ch = next(); - while (isspace(ch)) - ch = next(); - while (isalnum(ch) || ch == '_') - { - rv.push_back(ch); - ch = next(); - } - unget(ch); - } - - bool kv() - { - std::string key; - nextword(key); - int ch = next(); - if (ch == '>' && key.empty()) - { - return false; - } - else if (ch == '#' && key.empty()) - { - comment(); - return true; - } - else if (ch != '=') - { - throw CoreException("Invalid character " + std::string(1, ch) + " in key (" + key + ")"); - } - - std::string value; - ch = next(); - if (ch != '"') - { - throw CoreException("Invalid character in value of <" + tag->tag + ":" + key + ">"); - } - while (1) - { - ch = next(); - if (ch == '&') - { - std::string varname; - while (1) - { - ch = next(); - if (isalnum(ch)) - varname.push_back(ch); - else if (ch == ';') - break; - else - { - stack.errstr << "Invalid XML entity name in value of <" + tag->tag + ":" + key + ">\n" - << "To include an ampersand or quote, use & or "\n"; - 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); - } - else if (ch == '"') - break; - value.push_back(ch); - } - tag->items.push_back(KeyVal(key, value)); - return true; - } - - void dotag() - { - last_tag = current; - std::string name; - nextword(name); - - int spc = next(); - if (spc == '>') - unget(spc); - else if (!isspace(spc)) - throw CoreException("Invalid character in tag name"); - - if (name.empty()) - throw CoreException("Empty tag name"); - - tag = new ConfigTag(name, current.filename, current.line); - - while (kv()); - - if (name == "include") - { - stack.DoInclude(tag, flags); - } - else if (name == "define") - { - 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 - { - stack.output.insert(std::make_pair(name, tag)); - } - // this is not a leak; reference<> takes care of the delete - tag = NULL; - } - - bool outer_parse() - { - try - { - while (1) - { - int ch = next(true); - switch (ch) - { - case EOF: - // this is the one place where an EOF is not an error - return true; - case '#': - comment(); - break; - case '<': - dotag(); - break; - case ' ': - case '\r': - case '\t': - case '\n': - break; - case 0xFE: - case 0xFF: - stack.errstr << "Do not save your files as UTF-16; use ASCII!\n"; - default: - throw CoreException("Syntax error - start of tag expected"); - } - } - } - catch (CoreException& err) - { - stack.errstr << err.GetReason() << " at " << current.str(); - if (tag) - stack.errstr << " (inside tag " << tag->tag << " at line " << tag->src_line << ")\n"; - else - stack.errstr << " (last tag was on line " << last_tag.line << ")\n"; - } - return false; - } -}; - -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 name; - if (tag->readString("file", name)) - { - if (tag->getBool("noinclude", false)) - flags |= FLAG_NO_INC; - if (tag->getBool("noexec", false)) - flags |= FLAG_NO_EXEC; - if (!ParseFile(name, flags)) - throw CoreException("Included"); - } - else if (tag->readString("executable", name)) - { - if (flags & FLAG_NO_EXEC) - throw CoreException("Invalid <include:executable> tag in file included with noexec=\"yes\""); - if (tag->getBool("noinclude", false)) - flags |= FLAG_NO_INC; - if (tag->getBool("noexec", true)) - flags |= FLAG_NO_EXEC; - if (!ParseExec(name, flags)) - throw CoreException("Included"); - } -} - -/** RAII wrapper on FILE* to close files on exceptions */ -struct FileWrapper -{ - FILE* const f; - FileWrapper(FILE* file) : f(file) {} - operator bool() { return f; } - operator FILE*() { return f; } - ~FileWrapper() - { - if (f) - fclose(f); - } -}; - -bool ParseStack::ParseFile(const std::string& name, int flags) -{ - 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); - bool ok = p.outer_parse(); - reading.pop_back(); - return ok; -} - -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++) - { - if (std::string(name) == reading[t]) - { - throw CoreException("Executable " + name + " 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")); - if (!file) - throw CoreException("Could not open executable \"" + name + "\" for include"); - - reading.push_back(name); - Parser p(*this, flags, file, name); - bool ok = p.outer_parse(); - reading.pop_back(); - return ok; -} - -///////////////////////////////////////////////////////////////////////////// +#include "configparser.h" ServerConfig::ServerConfig() { @@ -1200,93 +864,6 @@ ConfigTagList ServerConfig::ConfTags(const std::string& tag) return config_data.equal_range(tag); } -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) - continue; - value = j->second; - if (!allow_lf && (value.find('\n') != std::string::npos)) - { - ServerInstance->Logs->Log("CONFIG",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') - *n = ' '; - } - return true; - } - return false; -} - -std::string ConfigTag::getString(const std::string& key, const std::string& def) -{ - std::string res = def; - readString(key, res); - return res; -} - -long ConfigTag::getInt(const std::string &key, long def) -{ - std::string result; - if(!readString(key, result)) - return def; - - const char* res_cstr = result.c_str(); - char* res_tail = NULL; - 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; - } - return res; -} - -double ConfigTag::getFloat(const std::string &key, double def) -{ - std::string result; - if (!readString(key, result)) - return def; - return strtod(result.c_str(), NULL); -} - -bool ConfigTag::getBool(const std::string &key, bool def) -{ - std::string result; - if(!readString(key, result)) - return def; - - return (result == "yes" || result == "true" || result == "1" || result == "on"); -} - -std::string ConfigTag::getTagLocation() -{ - return src_name + ":" + ConvToStr(src_line); -} - -std::string OperInfo::getConfig(const std::string& key) -{ - std::string rv; - if (type_block) - type_block->readString(key, rv); - if (oper_block) - oper_block->readString(key, rv); - return rv; -} - /** Read the contents of a file located by `fname' into a file_cache pointed at by `F'. */ bool ServerConfig::ReadFile(file_cache &F, const std::string& fname) |