- user->WriteNumeric(ERR_CANTLOADMODULE, "%s %s :Failed to load module %s: %s",user->nick.c_str(), adding->c_str(), adding->c_str(), ServerInstance->Modules->LastError().c_str());
- else
- ServerInstance->SNO->WriteToSnoMask('a', "Failed to load module %s: %s", adding->c_str(), ServerInstance->Modules->LastError().c_str());
- }
- }
-
- if (user)
- user->WriteServ("NOTICE %s :*** Successfully rehashed server.", user->nick.c_str());
- else
- ServerInstance->SNO->WriteToSnoMask('a', "*** Successfully rehashed server.");
-}
-
-bool ServerConfig::LoadConf(FILE* &conf, const char* filename)
-{
- std::string line;
- char ch;
- long linenumber = 1;
- long last_successful_parse = 1;
- bool in_tag;
- bool in_quote;
- bool in_comment;
- int character_count = 0;
-
- in_tag = false;
- in_quote = false;
- in_comment = false;
-
- ServerInstance->Logs->Log("CONFIG", DEBUG, "Reading %s", filename);
-
- /* Check if the file open failed first */
- if (!conf)
- {
- errstr << "LoadConf: Couldn't open config file: " << filename << std::endl;
- return false;
- }
-
- for (unsigned int t = 0; t < include_stack.size(); t++)
- {
- if (std::string(filename) == include_stack[t])
- {
- errstr << "File " << filename << " is included recursively (looped inclusion)." << std::endl;
- return false;
- }
- }
-
- /* It's not already included, add it to the list of files we've loaded */
- include_stack.push_back(filename);
-
- /* Start reading characters... */
- while ((ch = fgetc(conf)) != EOF)
- {
- /*
- * Fix for moronic windows issue spotted by Adremelech.
- * Some windows editors save text files as utf-16, which is
- * a total pain in the ass to parse. Users should save in the
- * right config format! If we ever see a file where the first
- * byte is 0xFF or 0xFE, or the second is 0xFF or 0xFE, then
- * this is most likely a utf-16 file. Bail out and insult user.
- */
- if ((character_count++ < 2) && (ch == '\xFF' || ch == '\xFE'))
- {
- errstr << "File " << filename << " cannot be read, as it is encoded in braindead UTF-16. Save your file as plain ASCII!" << std::endl;
- return false;
- }
-
- /*
- * Here we try and get individual tags on separate lines,
- * this would be so easy if we just made people format
- * their config files like that, but they don't so...
- * We check for a '<' and then know the line is over when
- * we get a '>' not inside quotes. If we find two '<' and
- * no '>' then die with an error.
- */
-
- if ((ch == '#') && !in_quote)
- in_comment = true;
-
- switch (ch)
- {
- case '\n':
- if (in_quote)
- line += '\n';
- linenumber++;
- case '\r':
- if (!in_quote)
- in_comment = false;
- case '\0':
- continue;
- case '\t':
- ch = ' ';
- }
-
- if(in_comment)
- continue;
-
- /* XXX: Added by Brain, May 1st 2006 - Escaping of characters.
- * Note that this WILL NOT usually allow insertion of newlines,
- * because a newline is two characters long. Use it primarily to
- * insert the " symbol.
- *
- * Note that this also involves a further check when parsing the line,
- * which can be found below.
- */
- if ((ch == '\\') && (in_quote) && (in_tag))
- {
- line += ch;
- char real_character;
- if (!feof(conf))
- {
- real_character = fgetc(conf);
- if (real_character == 'n')
- real_character = '\n';
- line += real_character;
- continue;
- }
- else
- {
- errstr << "End of file after a \\, what did you want to escape?: " << filename << ":" << linenumber << std::endl;
- return false;
- }
- }
-
- if (ch != '\r')
- line += ch;
-
- if ((ch != '<') && (!in_tag) && (!in_comment) && (ch > ' ') && (ch != 9))
- {
- errstr << "You have stray characters beyond the tag which starts at " << filename << ":" << last_successful_parse << std::endl;
- return false;
- }
-
- if (ch == '<')
- {
- if (in_tag)
- {
- if (!in_quote)
- {
- errstr << "The tag at location " << filename << ":" << last_successful_parse << " was valid, but there is an error in the tag which comes after it. You are possibly missing a \" or >. Please check this." << std::endl;
- return false;
- }
- }
- else
- {
- if (in_quote)
- {
- errstr << "Parser error: Inside a quote but not within the last valid tag, which was opened at: " << filename << ":" << last_successful_parse << std::endl;
- return false;
- }
- else
- {
- // errstr << "Opening new config tag on line " << linenumber << std::endl;
- in_tag = true;
- }
- }
- }
- else if (ch == '"')
- {
- if (in_tag)
- {
- if (in_quote)
- {
- // errstr << "Closing quote in config tag on line " << linenumber << std::endl;
- in_quote = false;
- }
- else
- {
- // errstr << "Opening quote in config tag on line " << linenumber << std::endl;
- in_quote = true;
- }
- }
- else
- {
- if (in_quote)
- {
- errstr << "The tag immediately after the one at " << filename << ":" << last_successful_parse << " has a missing closing \" symbol. Please check this." << std::endl;
- }
- else
- {
- errstr << "You have opened a quote (\") beyond the tag at " << filename << ":" << last_successful_parse << " without opening a new tag. Please check this." << std::endl;
- }
- }
- }
- else if (ch == '>')
- {
- if (!in_quote)
- {
- if (in_tag)
- {
- // errstr << "Closing config tag on line " << linenumber << std::endl;
- in_tag = false;
-
- /*
- * If this finds an <include> then ParseLine can simply call
- * LoadConf() and load the included config into the same ConfigDataHash
- */
- long bl = linenumber;
- if (!this->ParseLine(filename, line, linenumber))
- return false;
- last_successful_parse = linenumber;
-
- linenumber = bl;
-
- line.clear();
- }
- else
- {
- errstr << "You forgot to close the tag which comes immediately after the one at " << filename << ":" << last_successful_parse << std::endl;
- return false;
- }
- }
- }
- }
-
- /* Fix for bug #392 - if we reach the end of a file and we are still in a quote or comment, most likely the user fucked up */
- if (in_comment || in_quote)
- {
- errstr << "Reached end of file whilst still inside a quoted section or tag. This is most likely an error or there \
- is a newline missing from the end of the file: " << filename << ":" << linenumber << std::endl;
- }
-
- return true;
-}
-
-
-bool ServerConfig::LoadConf(FILE* &conf, const std::string &filename)
-{
- return this->LoadConf(conf, filename.c_str());
-}
-
-bool ServerConfig::ParseLine(const std::string &filename, std::string &line, long &linenumber)
-{
- std::string tagname;
- std::string current_key;
- std::string current_value;
- KeyValList results;
- char last_char = 0;
- bool got_name;
- bool got_key;
- bool in_quote;
-
- got_name = got_key = in_quote = false;
-
- for(std::string::iterator c = line.begin(); c != line.end(); c++)
- {
- if (!got_name)
- {
- /* We don't know the tag name yet. */
-
- if (*c != ' ')
- {
- if (*c != '<')
- {
- if ((*c >= 'a' && *c <= 'z') || (*c >= 'A' && *c <='Z') || (*c >= '0' && *c <= '9') || *c == '_')
- tagname += *c;
- else
- {
- errstr << "Invalid character in value name of tag: '" << *c << "' in value '" << tagname << "' in filename: " << filename << ":" << linenumber << std::endl;
- return false;
- }
- }
- }
- else
- {
- /* We got to a space, we should have the tagname now. */
- if(tagname.length())
- {
- got_name = true;
- }
- }
- }
- else
- {
- /* We have the tag name */
- if (!got_key)
- {
- /* We're still reading the key name */
- if ((*c != '=') && (*c != '>'))
- {
- if (*c != ' ')
- {
- if ((*c >= 'a' && *c <= 'z') || (*c >= 'A' && *c <='Z') || (*c >= '0' && *c <= '9') || *c == '_')
- current_key += *c;
- else
- {
- errstr << "Invalid character in key: '" << *c << "' in key '" << current_key << "' in filename: " << filename << ":" << linenumber << std::endl;
- return false;
- }
- }
- }
- else
- {
- /* We got an '=', end of the key name. */
- got_key = true;
- }
- }
- else
- {
- /* We have the key name, now we're looking for quotes and the value */
-
- /* Correctly handle escaped characters here.
- * See the XXX'ed section above.
- */
- if ((*c == '\\') && (in_quote))
- {
- c++;
- if (*c == 'n')
- current_value += '\n';
- else
- current_value += *c;
- continue;
- }
- else if ((*c == '\\') && (!in_quote))
- {
- errstr << "You can't have an escape sequence outside of a quoted section: " << filename << ":" << linenumber << std::endl;
- return false;
- }
- else if ((*c == '\n') && (in_quote))
- {
- /* Got a 'real' \n, treat it as part of the value */
- current_value += '\n';
- continue;
- }
- else if ((*c == '\r') && (in_quote))
- {
- /* Got a \r, drop it */
- continue;
- }
-
- if (*c == '"')
- {
- if (!in_quote)
- {
- /* We're not already in a quote. */
- in_quote = true;
- }
- else
- {
- /* Leaving the quotes, we have the current value */
- results.push_back(KeyVal(current_key, current_value));
-
- // std::cout << "<" << tagname << ":" << current_key << "> " << current_value << std::endl;
-
- in_quote = false;
- got_key = false;
-
- if ((tagname == "include") && (current_key == "file"))
- {
- if (!this->DoInclude(current_value))
- return false;
- }
- else if ((tagname == "include") && (current_key == "executable"))
- {
- /* Pipe an executable and use its stdout as config data */
- if (!this->DoPipe(current_value))
- return false;
- }
-
- current_key.clear();
- current_value.clear();
- }
- }
- else
- {
- if (in_quote)
- {
- last_char = *c;
- current_value += *c;
- }
- }
- }
- }
- }
-
- /* Finished parsing the tag, add it to the config hash */
- config_data.insert(std::pair<std::string, KeyValList > (tagname, results));
-
- return true;
-}
-
-bool ServerConfig::DoPipe(const std::string &file)
-{
- FILE* conf = popen(file.c_str(), "r");
- bool ret = false;
-
- if (conf)
- {
- ret = LoadConf(conf, file.c_str());
- pclose(conf);
- }
- else
- errstr << "Couldn't execute: " << file << std::endl;
-
- return ret;
-}
-
-bool ServerConfig::StartsWithWindowsDriveLetter(const std::string &path)
-{
- return (path.length() > 2 && isalpha(path[0]) && path[1] == ':');
-}
-
-bool ServerConfig::DoInclude(const std::string &file)
-{
- std::string confpath;
- std::string newfile;
- std::string::size_type pos;
-
- confpath = ServerInstance->ConfigFileName;
- newfile = file;
-
- std::replace(newfile.begin(),newfile.end(),'\\','/');
- std::replace(confpath.begin(),confpath.end(),'\\','/');
-
- if ((newfile[0] != '/') && (!StartsWithWindowsDriveLetter(newfile)))
- {
- if((pos = confpath.rfind("/")) != std::string::npos)
- {
- /* Leaves us with just the path */
- newfile = confpath.substr(0, pos) + std::string("/") + newfile;
- }
- else
- {
- errstr << "Couldn't get config path from: " << ServerInstance->ConfigFileName << std::endl;
- return false;
- }
- }
-
- FILE* conf = fopen(newfile.c_str(), "r");
- bool ret = false;
-
- if (conf)
- {
- ret = LoadConf(conf, newfile);
- fclose(conf);
- }
- else
- errstr << "Couldn't open config file: " << file << std::endl;
-
- return ret;
-}
-
-bool ServerConfig::ConfValue(const char* tag, const char* var, int index, char* result, int length, bool allow_linefeeds)
-{
- return ConfValue(tag, var, "", index, result, length, allow_linefeeds);
-}
-
-bool ServerConfig::ConfValue(const char* tag, const char* var, const char* default_value, int index, char* result, int length, bool allow_linefeeds)
-{
- std::string value;
- bool r = ConfValue(std::string(tag), std::string(var), std::string(default_value), index, value, allow_linefeeds);
- strlcpy(result, value.c_str(), length);
- return r;
-}
-
-bool ServerConfig::ConfValue(const std::string &tag, const std::string &var, int index, std::string &result, bool allow_linefeeds)
-{
- return ConfValue(tag, var, "", index, result, allow_linefeeds);
-}
-
-bool ServerConfig::ConfValue(const std::string &tag, const std::string &var, const std::string &default_value, int index, std::string &result, bool allow_linefeeds)
-{
- ConfigDataHash::size_type pos = index;
- if (pos < config_data.count(tag))
- {
- ConfigDataHash::iterator iter = config_data.find(tag);
-
- for(int i = 0; i < index; i++)
- iter++;
-
- for(KeyValList::iterator j = iter->second.begin(); j != iter->second.end(); j++)
- {
- if(j->first == var)
- {
- if ((!allow_linefeeds) && (j->second.find('\n') != std::string::npos))
- {
- ServerInstance->Logs->Log("CONFIG",DEFAULT, "Value of <" + tag + ":" + var+ "> contains a linefeed, and linefeeds in this value are not permitted -- stripped to spaces.");
- for (std::string::iterator n = j->second.begin(); n != j->second.end(); n++)
- if (*n == '\n')
- *n = ' ';
- }
- else
- {
- result = j->second;
- return true;
- }
- }
- }
- if (!default_value.empty())
- {
- result = default_value;
- return true;
- }
- }
- else if (pos == 0)
- {
- if (!default_value.empty())
- {
- result = default_value;
- return true;
- }
- }
- return false;
-}
-
-bool ServerConfig::ConfValueInteger(const char* tag, const char* var, int index, int &result)
-{
- return ConfValueInteger(std::string(tag), std::string(var), "", index, result);
-}
-
-bool ServerConfig::ConfValueInteger(const char* tag, const char* var, const char* default_value, int index, int &result)
-{
- return ConfValueInteger(std::string(tag), std::string(var), std::string(default_value), index, result);
-}
-
-bool ServerConfig::ConfValueInteger(const std::string &tag, const std::string &var, int index, int &result)
-{
- return ConfValueInteger(tag, var, "", index, result);
-}
-
-bool ServerConfig::ConfValueInteger(const std::string &tag, const std::string &var, const std::string &default_value, int index, int &result)
-{
- std::string value;
- std::istringstream stream;
- bool r = ConfValue(tag, var, default_value, index, value);
- stream.str(value);
- if(!(stream >> result))
- return false;
- else
- {
- if (!value.empty())
- {
- if (value.substr(0,2) == "0x")
- {
- char* endptr;
-
- value.erase(0,2);
- result = strtol(value.c_str(), &endptr, 16);
-
- /* No digits found */
- if (endptr == value.c_str())
- return false;
- }
- else
- {
- char denominator = *(value.end() - 1);
- switch (toupper(denominator))
- {
- case 'K':
- /* Kilobytes -> bytes */
- result = result * 1024;
- break;
- case 'M':
- /* Megabytes -> bytes */
- result = result * 1024 * 1024;
- break;
- case 'G':
- /* Gigabytes -> bytes */
- result = result * 1024 * 1024 * 1024;
- break;
- }
- }
- }
- }
- return r;
-}
-
-
-bool ServerConfig::ConfValueBool(const char* tag, const char* var, int index)
-{
- return ConfValueBool(std::string(tag), std::string(var), "", index);
-}
-
-bool ServerConfig::ConfValueBool(const char* tag, const char* var, const char* default_value, int index)
-{
- return ConfValueBool(std::string(tag), std::string(var), std::string(default_value), index);
-}
-
-bool ServerConfig::ConfValueBool(const std::string &tag, const std::string &var, int index)
-{
- return ConfValueBool(tag, var, "", index);
-}
-
-bool ServerConfig::ConfValueBool(const std::string &tag, const std::string &var, const std::string &default_value, int index)
-{
- std::string result;
- if(!ConfValue(tag, var, default_value, index, result))
- return false;
-
- return ((result == "yes") || (result == "true") || (result == "1"));
-}
-
-int ServerConfig::ConfValueEnum(const char* tag)
-{
- return config_data.count(tag);
-}
-
-int ServerConfig::ConfValueEnum(const std::string &tag)
-{
- return config_data.count(tag);
-}
-
-int ServerConfig::ConfVarEnum(const char* tag, int index)
-{
- return ConfVarEnum(std::string(tag), index);
-}
-
-int ServerConfig::ConfVarEnum(const std::string &tag, int index)
-{
- ConfigDataHash::size_type pos = index;
-
- if (pos < config_data.count(tag))
- {
- ConfigDataHash::const_iterator iter = config_data.find(tag);
-
- for(int i = 0; i < index; i++)
- iter++;
-
- return iter->second.size();
- }
-
- return 0;
-}
-
-/** Read the contents of a file located by `fname' into a file_cache pointed at by `F'.
- */
-bool ServerConfig::ReadFile(file_cache &F, const char* fname)
-{
- if (!fname || !*fname)
- return false;
-
- FILE* file = NULL;
- char linebuf[MAXBUF];
-
- F.clear();
-
- if ((*fname != '/') && (*fname != '\\') && (!StartsWithWindowsDriveLetter(fname)))
- {
- std::string::size_type pos;
- std::string confpath = ServerInstance->ConfigFileName;
- std::string newfile = fname;
-
- if (((pos = confpath.rfind("/"))) != std::string::npos)
- newfile = confpath.substr(0, pos) + std::string("/") + fname;
- else if (((pos = confpath.rfind("\\"))) != std::string::npos)
- newfile = confpath.substr(0, pos) + std::string("\\") + fname;
-
- ServerInstance->Logs->Log("config", DEBUG, "Filename: %s", newfile.c_str());
-
- if (!FileExists(newfile.c_str()))
- return false;
- file = fopen(newfile.c_str(), "r");
- }
- else
- {
- if (!FileExists(fname))
- return false;
- file = fopen(fname, "r");
- }
-
- if (file)
- {
- while (!feof(file))
- {
- if (fgets(linebuf, sizeof(linebuf), file))
- linebuf[strlen(linebuf)-1] = 0;