-
- if (user)
- user->WriteServ("NOTICE %s :*** Successfully rehashed server.", user->nick.c_str());
- else
- ServerInstance->SNO->WriteGlobalSno('a', "*** Successfully rehashed server.");
-}
-
-bool ServerConfig::LoadConf(FILE* &conf, const char* filename, bool allowexeinc)
-{
- 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, allowexeinc))
- 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, bool allowexeinc)
-{
- return this->LoadConf(conf, filename.c_str(), allowexeinc);
-}
-
-bool ServerConfig::ParseLine(const std::string &filename, std::string &line, long &linenumber, bool allowexeinc)
-{
- std::string tagname;
- std::string current_key;
- std::string current_value;
- reference<ConfigTag> result;
- char last_char = 0;
- bool got_key;
- bool in_quote;
-
- got_key = in_quote = false;
-
- for(std::string::iterator c = line.begin(); c != line.end(); c++)
- {
- if (!result)
- {
- /* 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())
- {
- result = new ConfigTag(tagname);
- }
- }
- }
- 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 */
- result->items.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, allowexeinc))
- return false;
- }
- else if ((tagname == "include") && (current_key == "executable"))
- {
- if (!allowexeinc)
- {
- errstr << "Executable includes are not allowed to use <include:executable>\n"
- "This could be an attempt to execute commands from a malicious remote include.\n"
- "If you need multiple levels of remote include, create a script to assemble the "
- "contents locally or include files using <include:file>\n";
- return false;
- }
-
- /* 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::make_pair(tagname, result));
-
- 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(), false);
- pclose(conf);
- }
- else
- errstr << "Couldn't execute: " << file << std::endl;
-
- return ret;