diff options
-rw-r--r-- | include/inspircd.h | 2 | ||||
-rw-r--r-- | include/inspircd_io.h | 2 | ||||
-rw-r--r-- | include/modules.h | 8 | ||||
-rw-r--r-- | src/commands.cpp | 2 | ||||
-rw-r--r-- | src/inspircd.cpp | 38 | ||||
-rw-r--r-- | src/inspircd_io.cpp | 150 | ||||
-rw-r--r-- | src/modules.cpp | 40 | ||||
-rw-r--r-- | src/modules/m_helpop.cpp | 14 |
8 files changed, 237 insertions, 19 deletions
diff --git a/include/inspircd.h b/include/inspircd.h index d55ccccf4..1a19584f6 100644 --- a/include/inspircd.h +++ b/include/inspircd.h @@ -89,7 +89,7 @@ int InspIRCd(void); int InitConfig(void); void Error(int status); void send_error(char *s); -void ReadConfig(void); +void ReadConfig(bool bail,userrec* user); void strlower(char *n); void WriteOpers(char* text, ...); diff --git a/include/inspircd_io.h b/include/inspircd_io.h index 28e55bd0b..2ff7568c0 100644 --- a/include/inspircd_io.h +++ b/include/inspircd_io.h @@ -23,7 +23,7 @@ bool FileExists (const char* file); int OpenTCPSocket (void); int BindSocket (int sockfd, struct sockaddr_in client, struct sockaddr_in server, int port, char* addr); -bool LoadConf(const char* filename, std::stringstream *target); +bool LoadConf(const char* filename, std::stringstream *target, std::stringstream* errorstream); int ConfValue(char* tag, char* var, int index, char *result, std::stringstream *config); int ReadConf(std::stringstream *config_f,const char* tag, const char* var, int index, char *result); int ConfValueEnum(char* tag,std::stringstream *config); diff --git a/include/modules.h b/include/modules.h index dc17b99c0..81d76d55d 100644 --- a/include/modules.h +++ b/include/modules.h @@ -647,6 +647,7 @@ class ConfigReader : public classbase * (such as comments) stripped from it. */ std::stringstream *cache; + std::stringstream *errorlog; /** Used to store errors */ bool readerror; @@ -703,6 +704,13 @@ class ConfigReader : public classbase * file does not exist or could not be opened. */ bool Verify(); + /** Dumps the list of errors in a config file to an output location. If bail is true, + * then the program will abort. If bail is false and user points to a valid user + * record, the error report will be spooled to the given user by means of NOTICE. + * if bool is false AND user is false, the error report will be spooled to all opers + * by means of a NOTICE to all opers. + */ + void DumpErrors(bool bail,userrec* user); /** Returns the number of items within a tag. * For example if the tag was <test tag="blah" data="foo"> then this diff --git a/src/commands.cpp b/src/commands.cpp index 0b9624ea8..85eb833c2 100644 --- a/src/commands.cpp +++ b/src/commands.cpp @@ -983,7 +983,7 @@ void handle_list(char **parameters, int pcnt, userrec *user) void handle_rehash(char **parameters, int pcnt, userrec *user) { WriteServ(user->fd,"382 %s %s :Rehashing",user->nick,CleanFilename(CONFIG_FILE)); - ReadConfig(); + ReadConfig(false,user); FOREACH_MOD OnRehash(); WriteOpers("%s is rehashing config file %s",user->nick,CleanFilename(CONFIG_FILE)); } diff --git a/src/inspircd.cpp b/src/inspircd.cpp index 92808bd0b..634e5ac3f 100644 --- a/src/inspircd.cpp +++ b/src/inspircd.cpp @@ -302,13 +302,45 @@ void readfile(file_cache &F, const char* fname) log(DEBUG,"readfile: loaded %s, %d lines",fname,F.size()); } -void ReadConfig(void) +void ReadConfig(bool bail, userrec* user) { char dbg[MAXBUF],pauseval[MAXBUF],Value[MAXBUF],timeout[MAXBUF],NB[MAXBUF],flood[MAXBUF],MW[MAXBUF]; char AH[MAXBUF],AP[MAXBUF],AF[MAXBUF]; ConnectClass c; + std::stringstream errstr; - LoadConf(CONFIG_FILE,&config_f); + if (!LoadConf(CONFIG_FILE,&config_f,&errstr)) + { + errstr.seekg(0); + if (bail) + { + printf("There were errors in your configuration:\n%s",errstr.str().c_str()); + exit(0); + } + else + { + char dataline[1024]; + if (user) + { + WriteServ(user->fd,"NOTICE %s :There were errors in the configuration file:",user->nick); + while (!errstr.eof()) + { + errstr.getline(dataline,1024); + WriteServ(user->fd,"NOTICE %s :%s",user->nick,dataline); + } + } + else + { + WriteOpers("There were errors in the configuration file:",user->nick); + while (!errstr.eof()) + { + errstr.getline(dataline,1024); + WriteOpers(dataline); + } + } + return; + } + } ConfValue("server","name",0,ServerName,&config_f); ConfValue("server","description",0,ServerDesc,&config_f); @@ -3172,7 +3204,7 @@ int InspIRCd(void) SetupCommandTable(); log(DEBUG,"InspIRCd: startup: default command table set up"); - ReadConfig(); + ReadConfig(true,NULL); if (strcmp(DieValue,"")) { printf("WARNING: %s\n\n",DieValue); diff --git a/src/inspircd_io.cpp b/src/inspircd_io.cpp index a7a180a88..2e9a350f1 100644 --- a/src/inspircd_io.cpp +++ b/src/inspircd_io.cpp @@ -50,8 +50,8 @@ void Killed(int status) void Rehash(int status) { - ReadConfig(); WriteOpers("Rehashing config file %s due to SIGHUP",CONFIG_FILE); + ReadConfig(false,NULL); } @@ -114,13 +114,148 @@ bool FileExists (const char* file) else { fclose (input); return(true); } } +/* ConfProcess does the following things to a config line in the following order: + * + * Processes the line for syntax errors as shown below + * (1) Line void of quotes or equals (a malformed, illegal tag format) + * (2) Odd number of quotes on the line indicating a missing quote + * (3) number of equals signs not equal to number of quotes / 2 (missing an equals sign) + * (4) Spaces between the opening bracket (<) and the keyword + * (5) Spaces between a keyword and an equals sign + * (6) Spaces between an equals sign and a quote + * Removes trailing spaces + * Removes leading spaces + * Converts tabs to spaces + * Turns multiple spaces that are outside of quotes into single spaces + */ -bool LoadConf(const char* filename, std::stringstream *target) +std::string ConfProcess(char* buffer, long linenumber, std::stringstream* errorstream, bool &error, std::string filename) +{ + long number_of_quotes = 0; + long number_of_equals = 0; + bool has_open_bracket = false; + bool in_quotes = false; + error = false; + if (!buffer) + { + return ""; + } + // firstly clean up the line by stripping spaces from the start and end + while ((buffer[0] == ' ') && (strlen(buffer)>0)) buffer++; + while ((buffer[strlen(buffer)-1] == ' ') && (strlen(buffer)>0)) buffer[strlen(buffer)-1] = '\0'; + // empty lines are syntactically valid + if (!strcmp(buffer,"")) + return ""; + else if (buffer[0] == '#') + return ""; + for (int c = 0; c < strlen(buffer); c++) + { + if (buffer[c] == 9) + buffer[c] = ' '; + // convert all spaces that are OUTSIDE quotes into hardspace (0xA0) as this will make them easier to + // search and replace later :) + if ((!in_quotes) && (buffer[c] == ' ')) + buffer[c] = '\xA0'; + if ((buffer[c] == '<') && (!in_quotes)) + { + has_open_bracket = true; + if (strlen(buffer) == 1) + { + *errorstream << "Tag without identifier at " << filename << ":" << linenumber << endl; + error = true; + return ""; + } + else if ((tolower(buffer[c+1]) < 'a') || (tolower(buffer[c+1]) > 'z')) + { + *errorstream << "Invalid characters in identifier at " << filename << ":" << linenumber << endl; + error = true; + return ""; + } + } + if (buffer[c] == '"') + { + number_of_quotes++; + in_quotes = (!in_quotes); + } + if ((buffer[c] == '=') && (!in_quotes)) + { + number_of_equals++; + if (strlen(buffer) == c) + { + *errorstream << "Variable without a value at " << filename << ":" << linenumber << endl; + error = true; + return ""; + } + else if (buffer[c+1] != '"') + { + *errorstream << "Variable name not followed immediately by its value at " << filename << ":" << linenumber << endl; + error = true; + return ""; + } + else if (!c) + { + *errorstream << "Value without a variable (line starts with '=') at " << filename << ":" << linenumber << endl; + error = true; + return ""; + } + else if (buffer[c-1] == '\xA0') + { + *errorstream << "Variable name not followed immediately by its value at " << filename << ":" << linenumber << endl; + error = true; + return ""; + } + } + } + // no quotes, and no equals. something freaky. + if ((!number_of_quotes) || (!number_of_equals)) + { + *errorstream << "Malformed tag at " << filename << ":" << linenumber << endl; + error = true; + return ""; + } + // odd number of quotes. thats just wrong. + if ((number_of_quotes % 2) != 0) + { + *errorstream << "Missing \" at " << filename << ":" << linenumber << endl; + error = true; + return ""; + } + if (number_of_equals < (number_of_quotes/2)) + { + *errorstream << "Missing '=' at " << filename << ":" << linenumber << endl; + } + if (number_of_equals > (number_of_quotes/2)) + { + *errorstream << "Too many '=' at " << filename << ":" << linenumber << endl; + } + + std::string parsedata = buffer; + // turn multispace into single space + while (parsedata.find("\xA0\xA0") != std::string::npos) + { + parsedata.erase(parsedata.find("\xA0\xA0"),1); + } + + // turn our hardspace back into softspace + for (int d = 0; d < parsedata.length(); d++) + { + if (parsedata[d] == '\xA0') + parsedata[d] = ' '; + } + + // and we're done, the line is fine! + return parsedata; +} + +bool LoadConf(const char* filename, std::stringstream *target, std::stringstream* errorstream) { target->str(""); + errorstream->str(""); + long linenumber = 1; FILE* conf = fopen(filename,"r"); if (!FileExists(filename)) { + *errorstream << "File " << filename << " not found." << endl; return false; } char buffer[MAXBUF]; @@ -132,10 +267,17 @@ bool LoadConf(const char* filename, std::stringstream *target) { if ((!feof(conf)) && (buffer) && (strlen(buffer))) { - if (buffer[0] != '#') + if ((buffer[0] != '#') && (buffer[0] != '\r') && (buffer[0] != '\n')) { - *target << std::string(buffer); + bool error = false; + std::string data = ConfProcess(buffer,linenumber++,errorstream,error,filename); + if (error) + { + return false; + } + *target << data; } + else linenumber++; } } } diff --git a/src/modules.cpp b/src/modules.cpp index aae7a53e2..15508ae19 100644 --- a/src/modules.cpp +++ b/src/modules.cpp @@ -566,7 +566,8 @@ int Server::CountUsers(chanrec* c) ConfigReader::ConfigReader() { this->cache = new std::stringstream(std::stringstream::in | std::stringstream::out); - this->readerror = LoadConf(CONFIG_FILE,this->cache); + this->errorlog = new std::stringstream(std::stringstream::in | std::stringstream::out); + this->readerror = LoadConf(CONFIG_FILE,this->cache,this->errorlog); if (!this->readerror) this->error = CONF_FILE_NOT_FOUND; } @@ -576,13 +577,16 @@ ConfigReader::~ConfigReader() { if (this->cache) delete this->cache; + if (this->errorlog) + delete this->errorlog; } ConfigReader::ConfigReader(std::string filename) { this->cache = new std::stringstream(std::stringstream::in | std::stringstream::out); - this->readerror = LoadConf(filename.c_str(),this->cache); + this->errorlog = new std::stringstream(std::stringstream::in | std::stringstream::out); + this->readerror = LoadConf(filename.c_str(),this->cache,this->errorlog); if (!this->readerror) this->error = CONF_FILE_NOT_FOUND; }; @@ -656,6 +660,38 @@ long ConfigReader::GetError() return olderr; } +void ConfigReader::DumpErrors(bool bail, userrec* user) +{ + if (bail) + { + printf("There were errors in your configuration:\n%s",errorlog->str().c_str()); + exit(0); + } + else + { + char dataline[1024]; + if (user) + { + WriteServ(user->fd,"NOTICE %s :There were errors in the configuration file:",user->nick); + while (!errorlog->eof()) + { + errorlog->getline(dataline,1024); + WriteServ(user->fd,"NOTICE %s :%s",user->nick,dataline); + } + } + else + { + WriteOpers("There were errors in the configuration file:",user->nick); + while (!errorlog->eof()) + { + errorlog->getline(dataline,1024); + WriteOpers(dataline); + } + } + return; + } +} + int ConfigReader::Enumerate(std::string tag) { diff --git a/src/modules/m_helpop.cpp b/src/modules/m_helpop.cpp index 3921b7f72..4b3526240 100644 --- a/src/modules/m_helpop.cpp +++ b/src/modules/m_helpop.cpp @@ -138,6 +138,11 @@ class ModuleHelpop : public Module { Srv = new Server; conf = new ConfigReader; + if (conf->GetError()) + { + // dump errors and ABORT + conf->DumpErrors(true,NULL); + } h_file = conf->ReadValue("helpop", "file", 0); @@ -147,19 +152,14 @@ class ModuleHelpop : public Module } helpop = new ConfigReader(h_file); - if (!helpop->Verify()) - { - printf("m_helpop: Invalid Helpop File. Please Ensure it exists and is error free."); - exit(0); - } - /*if ((helpop->ReadValue("nohelp", "line1", 0) == "") || + if ((helpop->ReadValue("nohelp", "line1", 0) == "") || (helpop->ReadValue("nohelpo", "line1", 0) == "") || (helpop->ReadValue("start", "line1", 0) == "")) { printf("m_helpop: Helpop file is missing important entries. Please check the example conf."); exit(0); - }*/ + } if (!Srv->AddExtendedMode('h',MT_CLIENT,true,0,0)) { |