=== modified file 'src/ConfigParser.cc' --- src/ConfigParser.cc 2013-07-22 01:26:09 +0000 +++ src/ConfigParser.cc 2013-08-21 15:20:29 +0000 @@ -21,377 +21,512 @@ * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA. * * * Copyright (c) 2003, Robert Collins */ #include "squid.h" #include "cache_cf.h" #include "ConfigParser.h" #include "Debug.h" #include "fatal.h" #include "globals.h" -int ConfigParser::RecognizeQuotedValues = true; +bool ConfigParser::RecognizeQuotedValues = true; +bool ConfigParser::StrictMode = true; std::stack ConfigParser::CfgFiles; ConfigParser::TokenType ConfigParser::LastTokenType = ConfigParser::SimpleToken; -char *ConfigParser::LastToken = NULL; -char *ConfigParser::CfgLine = NULL; -char *ConfigParser::CfgPos = NULL; +const char *ConfigParser::CfgLine = NULL; +const char *ConfigParser::CfgPos = NULL; +std::queue ConfigParser::CfgLineTokens_; std::queue ConfigParser::Undo_; bool ConfigParser::AllowMacros_ = false; +bool ConfigParser::ParseQuotedOrToEol_ = false; +bool ConfigParser::PreviewMode_ = false; + +static const char *SQUID_ERROR_TOKEN = "[invalid token]"; void ConfigParser::destruct() { shutting_down = 1; if (!CfgFiles.empty()) { std::ostringstream message; CfgFile *f = CfgFiles.top(); - message << "Bungled " << f->filePath << " line " << f->lineNo << + message << "Bungled (#1)" << f->filePath << " line " << f->lineNo << ": " << f->currentLine << std::endl; CfgFiles.pop(); delete f; while (!CfgFiles.empty()) { f = CfgFiles.top(); message << " included from " << f->filePath << " line " << f->lineNo << ": " << f->currentLine << std::endl; CfgFiles.pop(); delete f; } message << " included from " << cfg_filename << " line " << config_lineno << ": " << config_input_line << std::endl; std::string msg = message.str(); fatalf("%s", msg.c_str()); } else - fatalf("Bungled %s line %d: %s", + fatalf("Bungled (#2) %s line %d: %s", cfg_filename, config_lineno, config_input_line); } void -ConfigParser::TokenUndo() -{ - assert(LastToken); - Undo_.push(LastToken); -} - -void ConfigParser::TokenPutBack(const char *tok) { assert(tok); Undo_.push(tok); } char * ConfigParser::Undo() { LOCAL_ARRAY(char, undoToken, CONFIG_LINE_LIMIT); if (!Undo_.empty()) { strncpy(undoToken, Undo_.front().c_str(), sizeof(undoToken)); undoToken[sizeof(undoToken) - 1] = '\0'; - Undo_.pop(); + if (!PreviewMode_) + Undo_.pop(); return undoToken; } return NULL; } char * ConfigParser::strtokFile() { if (RecognizeQuotedValues) return ConfigParser::NextToken(); static int fromFile = 0; static FILE *wordFile = NULL; char *t; LOCAL_ARRAY(char, buf, CONFIG_LINE_LIMIT); - if ((LastToken = ConfigParser::Undo())) - return LastToken; + if ((t = ConfigParser::Undo())) + return t; do { if (!fromFile) { ConfigParser::TokenType tokenType; - t = ConfigParser::NextElement(tokenType, true); + t = ConfigParser::NextElement(tokenType); if (!t) { return NULL; - } else if (tokenType == ConfigParser::QuotedToken) { - /* quote found, start reading from file */ + } else if (*t == '\"' || *t == '\'') { + /* quote found, start reading from file */ debugs(3, 8,"Quoted token found : " << t); + char *fn = ++t; - if ((wordFile = fopen(t, "r")) == NULL) { + while (*t && *t != '\"' && *t != '\'') + ++t; + + *t = '\0'; + + if ((wordFile = fopen(fn, "r")) == NULL) { debugs(3, DBG_CRITICAL, "Can not open file " << t << " for reading"); return NULL; } #if _SQUID_WINDOWS_ setmode(fileno(wordFile), O_TEXT); #endif fromFile = 1; } else { - return LastToken = t; + return t; } } /* fromFile */ if (fgets(buf, CONFIG_LINE_LIMIT, wordFile) == NULL) { /* stop reading from file */ fclose(wordFile); wordFile = NULL; fromFile = 0; return NULL; } else { char *t2, *t3; t = buf; /* skip leading and trailing white space */ t += strspn(buf, w_space); t2 = t + strcspn(t, w_space); t3 = t2 + strspn(t2, w_space); while (*t3 && *t3 != '#') { t2 = t3 + strcspn(t3, w_space); t3 = t2 + strspn(t2, w_space); } *t2 = '\0'; } /* skip comments */ /* skip blank lines */ } while ( *t == '#' || !*t ); - return LastToken = t; + return t; } char * -ConfigParser::UnQuote(char *token, char **end) +ConfigParser::UnQuote(const char *token, const char **next) { + const char *errorStr = NULL; + const char *errorPos = NULL; char quoteChar = *token; assert(quoteChar == '"' || quoteChar == '\''); - char *s = token + 1; - /* scan until the end of the quoted string, unescaping " and \ */ - while (*s && *s != quoteChar) { - if (*s == '\\' && isalnum(*( s + 1))) { - debugs(3, DBG_CRITICAL, "Unsupported escape sequence: " << s); - self_destruct(); + LOCAL_ARRAY(char, UnQuoted, CONFIG_LINE_LIMIT); + const char *s = token + 1; + char *d = UnQuoted; + /* scan until the end of the quoted string, handling escape sequences*/ + while (*s && *s != quoteChar && !errorStr && (d - UnQuoted) < sizeof(UnQuoted)) { + if (*s == '\\') { + s++; + switch (*s) { + case 'r': + *d = '\r'; + break; + case 'n': + *d = '\n'; + break; + case 't': + *d = '\t'; + break; + default: + if (isalnum(*s)) { + errorStr = "Unsupported escape sequence"; + errorPos = s; + } + *d = *s; + break; + } +#if 0 } else if (*s == '$' && quoteChar == '"') { - debugs(3, DBG_CRITICAL, "Unsupported cfg macro: " << s); - self_destruct(); + errorStr = "Unsupported cfg macro"; + errorPos = s; +#endif } else if (*s == '%' && quoteChar == '"' && (!AllowMacros_ )) { - debugs(3, DBG_CRITICAL, "Macros are not supported here: " << s); - self_destruct(); - } else if (*s == '\\') { - const char * next = s+1; // may point to 0 - memmove(s, next, strlen(next) + 1); - } + errorStr = "Macros are not supported here"; + errorPos = s; + } else + *d = *s; ++s; + ++d; } - if (*s != quoteChar) { - debugs(3, DBG_CRITICAL, "missing '" << quoteChar << "' at the end of quoted string: " << (s-1)); - self_destruct(); + if (*s != quoteChar && !errorStr) { + errorStr = "missing quote char at the end of quoted string"; + errorPos = s - 1; } - *end = s; - return (token+1); + // The end of token + *d = '\0'; + + // We are expecting a separator after quoted string, space or one of "()#" + if (*(s + 1) != '\0' && !strchr(w_space "()#", *(s + 1)) && !errorStr) { + errorStr = "Expecting space after the end of quoted token"; + errorPos = token; + } + + if (errorStr) { + if (PreviewMode_) + strncpy(UnQuoted, SQUID_ERROR_TOKEN, sizeof(UnQuoted)); + else { + debugs(3, DBG_CRITICAL, errorStr << ": " << errorPos); + self_destruct(); + } + } + + if (next) + *next = s + 1; + return UnQuoted; } void ConfigParser::SetCfgLine(char *line) { CfgLine = line; CfgPos = line; + while (!CfgLineTokens_.empty()) { + char *token = CfgLineTokens_.front(); + CfgLineTokens_.pop(); + free(token); + } } char * -ConfigParser::TokenParse(char * &nextToken, ConfigParser::TokenType &type, bool legacy) +ConfigParser::TokenParse(const char * &nextToken, ConfigParser::TokenType &type) { if (!nextToken || *nextToken == '\0') return NULL; type = ConfigParser::SimpleToken; nextToken += strspn(nextToken, w_space); - if (*nextToken == '"' || *nextToken == '\'') { + + if (*nextToken == '#') + return NULL; + + if (ConfigParser::RecognizeQuotedValues && (*nextToken == '"' || *nextToken == '\'')) { type = ConfigParser::QuotedToken; - char *token = UnQuote(nextToken, &nextToken); - *nextToken = '\0'; - ++nextToken; + char *token = xstrdup(UnQuote(nextToken, &nextToken)); + CfgLineTokens_.push(token); return token; } - char *token = nextToken; - if (char *t = strchr(nextToken, '#')) - *t = '\0'; + const char *tokenStart = nextToken; const char *sep; - if (legacy) + if (ConfigParser::ParseQuotedOrToEol_) + sep = "\n"; + else if (!ConfigParser::RecognizeQuotedValues || *nextToken == '(') sep = w_space; else sep = w_space "("; nextToken += strcspn(nextToken, sep); - if (!legacy && *nextToken == '(') - type = ConfigParser::FunctionNameToken; - else + if (ConfigParser::RecognizeQuotedValues && *nextToken == '(') { + if (strncmp(tokenStart, "parameters", nextToken - tokenStart) == 0) + type = ConfigParser::FunctionParameters; + else { + if (PreviewMode_) { + char *err = xstrdup(SQUID_ERROR_TOKEN); + CfgLineTokens_.push(err); + return err; + } else { + debugs(3, DBG_CRITICAL, "Unknown cfg function: " << tokenStart); + self_destruct(); + } + } + } else type = ConfigParser::SimpleToken; - if (*nextToken != '\0') { - *nextToken = '\0'; - ++nextToken; + char *token = NULL; + if (nextToken - tokenStart) { + if (ConfigParser::StrictMode && type == ConfigParser::SimpleToken) { + bool tokenIsNumber = true; + for (const char *s = tokenStart; s != nextToken; ++s) { + const bool isValidChar = isalnum(*s) || strchr(".,()-=_/:", *s) || + (tokenIsNumber && *s == '%' && (s + 1 == nextToken)); + + if (!isdigit(*s)) + tokenIsNumber = false; + + if (!isValidChar) { + if (PreviewMode_) { + char *err = xstrdup(SQUID_ERROR_TOKEN); + CfgLineTokens_.push(err); + return err; + } else { + debugs(3, DBG_CRITICAL, "Not alphanumeric character '"<< *s << "' in unquoted token " << tokenStart); + self_destruct(); + } + } + } + } + token = xstrndup(tokenStart, nextToken - tokenStart + 1); + CfgLineTokens_.push(token); } - if (*token == '\0') - return NULL; + if (*nextToken != '\0' && *nextToken != '#') { + ++nextToken; + } return token; } char * -ConfigParser::NextElement(ConfigParser::TokenType &type, bool legacy) +ConfigParser::NextElement(ConfigParser::TokenType &type) { - char *token = TokenParse(CfgPos, type, legacy); + const char *pos = CfgPos; + char *token = TokenParse(pos, type); + // If not in preview mode the next call of this method should start + // parsing after the end of current token. + // For function "parameters(...)" we need always to update current parsing + // position to allow parser read the arguments of "parameters(..)" + if (!PreviewMode_ || type == FunctionParameters) + CfgPos = pos; + // else next call will read the same token return token; } char * ConfigParser::NextToken() { - if ((LastToken = ConfigParser::Undo())) - return LastToken; - char *token = NULL; + if ((token = ConfigParser::Undo())) { + debugs(3, 6, "TOKEN (undone): " << token); + return token; + } + do { while (token == NULL && !CfgFiles.empty()) { ConfigParser::CfgFile *wordfile = CfgFiles.top(); token = wordfile->parse(LastTokenType); if (!token) { assert(!wordfile->isOpen()); CfgFiles.pop(); + debugs(3, 4, "CfgFiles.pop " << wordfile->filePath); delete wordfile; } } if (!token) token = NextElement(LastTokenType); - if (token && LastTokenType == ConfigParser::FunctionNameToken && strcmp("parameters", token) == 0) { + if (token && LastTokenType == ConfigParser::FunctionParameters) { + //Disable temporary preview mode, we need to parse function parameters + const bool savePreview = ConfigParser::PreviewMode_; + ConfigParser::PreviewMode_ = false; + char *path = NextToken(); if (LastTokenType != ConfigParser::QuotedToken) { debugs(3, DBG_CRITICAL, "Quoted filename missing: " << token); self_destruct(); return NULL; } // The next token in current cfg file line must be a ")" char *end = NextToken(); + ConfigParser::PreviewMode_ = savePreview; if (LastTokenType != ConfigParser::SimpleToken || strcmp(end, ")") != 0) { debugs(3, DBG_CRITICAL, "missing ')' after " << token << "(\"" << path << "\""); self_destruct(); return NULL; } if (CfgFiles.size() > 16) { debugs(3, DBG_CRITICAL, "WARNING: can't open %s for reading parameters: includes are nested too deeply (>16)!\n" << path); self_destruct(); return NULL; } ConfigParser::CfgFile *wordfile = new ConfigParser::CfgFile(); if (!path || !wordfile->startParse(path)) { debugs(3, DBG_CRITICAL, "Error opening config file: " << token); delete wordfile; self_destruct(); return NULL; } CfgFiles.push(wordfile); token = NULL; - } else if (token && LastTokenType == ConfigParser::FunctionNameToken) { - debugs(3, DBG_CRITICAL, "Unknown cfg function: " << token); - self_destruct(); - return NULL; } } while (token == NULL && !CfgFiles.empty()); - return (LastToken = token); + return token; +} + +char * +ConfigParser::NextTokenPreview() +{ + PreviewMode_ = true; + char *token = NextToken(); + PreviewMode_ = false; + return token; } char * ConfigParser::NextQuotedOrToEol() { - char *token; + ParseQuotedOrToEol_ = true; + char *token = NextToken(); + ParseQuotedOrToEol_ = false; + + // Assume end of current config line + // Close all open configuration files for this config line + while (!CfgFiles.empty()) { + ConfigParser::CfgFile *wordfile = CfgFiles.top(); + CfgFiles.pop(); + delete wordfile; + } + + return token; +} - if ((token = CfgPos) == NULL) { - debugs(3, DBG_CRITICAL, "token is missing"); +char * +ConfigParser::RegexStrtokFile() +{ + if (ConfigParser::RecognizeQuotedValues) { + debugs(3, DBG_CRITICAL, "Can not read regex expresion while configuration_includes_quoted_values is enabled"); self_destruct(); - return NULL; } - token += strspn(token, w_space); + char * token = strtokFile(); + return token; +} - if (*token == '\"' || *token == '\'') { - //TODO: eat the spaces at the end and check if it is untill the end of file. - char *end; - token = UnQuote(token, &end); - *end = '\0'; - CfgPos = end + 1; - LastTokenType = ConfigParser::QuotedToken; - } else - LastTokenType = ConfigParser::SimpleToken; +char * +ConfigParser::RegexPattern() +{ + if (ConfigParser::RecognizeQuotedValues) { + debugs(3, DBG_CRITICAL, "Can not read regex expresion while configuration_includes_quoted_values is enabled"); + self_destruct(); + } - CfgPos = NULL; - return (LastToken = token); + char * token = NextToken(); + return token; +} + +char * +ConfigParser::NextQuotedToken() +{ + const bool saveRecognizeQuotedValues = ConfigParser::RecognizeQuotedValues; + ConfigParser::RecognizeQuotedValues = true; + char *token = NextToken(); + ConfigParser::RecognizeQuotedValues = saveRecognizeQuotedValues; + return token; } const char * ConfigParser::QuoteString(const String &var) { static String quotedStr; const char *s = var.termedBuf(); bool needQuote = false; for (const char *l = s; !needQuote && *l != '\0'; ++l ) needQuote = !isalnum(*l); if (!needQuote) return s; quotedStr.clean(); quotedStr.append('"'); for (; *s != '\0'; ++s) { if (*s == '"' || *s == '\\') quotedStr.append('\\'); quotedStr.append(*s); } quotedStr.append('"'); return quotedStr.termedBuf(); } bool ConfigParser::CfgFile::startParse(char *path) { assert(wordFile == NULL); + debugs(3, 3, "Parsing from " << path); if ((wordFile = fopen(path, "r")) == NULL) { debugs(3, DBG_CRITICAL, "file :" << path << " not found"); return false; } #if _SQUID_WINDOWS_ setmode(fileno(wordFile), O_TEXT); #endif filePath = path; return getFileLine(); } bool ConfigParser::CfgFile::getFileLine() { // Else get the next line if (fgets(parseBuffer, CONFIG_LINE_LIMIT, wordFile) == NULL) { /* stop reading from file */ fclose(wordFile); @@ -408,28 +543,33 @@ char * ConfigParser::CfgFile::parse(ConfigParser::TokenType &type) { if (!wordFile) return NULL; if (!*parseBuffer) return NULL; char *token; while (!(token = nextElement(type))) { if (!getFileLine()) return NULL; } return token; } char * ConfigParser::CfgFile::nextElement(ConfigParser::TokenType &type) { - return TokenParse(parsePos, type); + const char *pos = parsePos; + char *token = TokenParse(pos, type); + if (!PreviewMode_ || type == FunctionParameters) + parsePos = pos; + // else next call will read the same token; + return token; } ConfigParser::CfgFile::~CfgFile() { if (wordFile) fclose(wordFile); } === modified file 'src/ConfigParser.h' --- src/ConfigParser.h 2013-07-21 19:24:35 +0000 +++ src/ConfigParser.h 2013-08-20 14:44:22 +0000 @@ -53,158 +53,183 @@ #define CONFIG_LINE_LIMIT 2048 /** * A configuration file Parser. Instances of this class track * parsing state and perform tokenisation. Syntax is currently * taken care of outside this class. * * One reason for this class is to allow testing of configuration * using modules without linking cache_cf.o in - because that drags * in all of squid by reference. Instead the tokeniser only is * brought in. */ class ConfigParser { public: /** * Parsed tokens type: simple tokens, quoted tokens or function * like parameters. */ - enum TokenType {SimpleToken, QuotedToken, FunctionNameToken}; + enum TokenType {SimpleToken, QuotedToken, FunctionParameters}; void destruct(); static void ParseUShort(unsigned short *var); static void ParseBool(bool *var); static const char *QuoteString(const String &var); static void ParseWordList(wordlist **list); /** * Backward compatibility wrapper for the ConfigParser::NextToken method. * If the configuration_includes_quoted_values configuration parameter is * set to 'off' this interprets the quoted tokens as filenames. */ static char * strtokFile(); /** * Returns the body of the next element. The element is either a token or * a quoted string with optional escape sequences and/or macros. The body * of a quoted string element does not include quotes or escape sequences. * Future code will want to see Elements and not just their bodies. */ static char *NextToken(); + /** + * Backward compatibility wrapper for ConfigParser::RegexPattern method. + * If the configuration_includes_quoted_values configuration parameter is + * set to 'off' this interprets the quoted tokens as filenames. + */ + static char *RegexStrtokFile(); + + /** + * Parse the next token as a regex patern. The regex patterns are non quoted + * tokens. + */ + static char *RegexPattern(); + + /** + * Parse the next token with support for quoted values enabled even if + * the configuration_includes_quoted_values is set to off + */ + static char *NextQuotedToken(); + /// \return true if the last parsed token was quoted static bool LastTokenWasQuoted() {return (LastTokenType == ConfigParser::QuotedToken);} /** * \return the next quoted string or the raw string data until the end of line. * This method allows %macros in unquoted strings to keep compatibility * for the logformat option. */ static char *NextQuotedOrToEol(); /** - * Undo last NextToken call. The next call to NextToken() method will return - * again the last parsed element. - * Can not be called repeatedly to undo multiple NextToken calls. In this case - * the behaviour is undefined. + * Preview the next token. The next NextToken() and strtokFile() call + * will return the same token. + * On parse error (eg invalid characters in token) will return an + * error message as token. */ - static void TokenUndo(); + static char *NextTokenPreview(); /** * The next NextToken call will return the token as next element * It can be used repeatedly to add more than one tokens in a FIFO list. */ static void TokenPutBack(const char *token); /// Set the configuration file line to parse. static void SetCfgLine(char *line); /// Allow %macros inside quoted strings static void EnableMacros() {AllowMacros_ = true;} /// Do not allow %macros inside quoted strings static void DisableMacros() {AllowMacros_ = false;} /// configuration_includes_quoted_values in squid.conf - static int RecognizeQuotedValues; + static bool RecognizeQuotedValues; + + /** + * Strict syntax mode. Does not allow not alphanumeric characters in unquoted tokens. + * Controled by the configuration_includes_quoted_values in squid.conf but remains + * false when the the legacy ConfigParser::NextQuotedToken() call forces + * RecognizeQuotedValues to be temporary true. + */ + static bool StrictMode; protected: /** * Class used to store required information for the current * configuration file. */ class CfgFile { public: CfgFile(): wordFile(NULL), parsePos(NULL), lineNo(0) { parseBuffer[0] = '\0';} ~CfgFile(); /// True if the configuration file is open bool isOpen() {return wordFile != NULL;} /** * Open the file given by 'path' and initializes the CfgFile object * to start parsing */ bool startParse(char *path); /** * Do the next parsing step: * reads the next line from file if required. * \return the body of next element or a NULL pointer if there are no more token elements in the file. * \param type will be filled with the ConfigParse::TokenType for any element found, or left unchanged if NULL is returned. */ char *parse(TokenType &type); private: bool getFileLine(); ///< Read the next line from the file /** * Return the body of the next element. If the wasQuoted is given * set to true if the element was quoted. */ char *nextElement(TokenType &type); FILE *wordFile; ///< Pointer to the file. char parseBuffer[CONFIG_LINE_LIMIT]; ///< Temporary buffer to store data to parse - char *parsePos; ///< The next element position in parseBuffer string + const char *parsePos; ///< The next element position in parseBuffer string public: std::string filePath; ///< The file path std::string currentLine; ///< The current line to parse int lineNo; ///< Current line number }; - /** - * Return the last TokenUndo() or TokenPutBack() queued element, or NULL - * if none exist - */ + /// Return the last TokenPutBack() queued element or NULL if none exist static char *Undo(); /** * Unquotes the token, which must be quoted. - * \param end if it is not NULL, it is set to the end of token. + * \param next if it is not NULL, it is set after the end of token. */ - static char *UnQuote(char *token, char **end = NULL); + static char *UnQuote(const char *token, const char **next = NULL); /** * Does the real tokens parsing job: Ignore comments, unquote an * element if required. * \return the next token, or NULL if there are no available tokens in the nextToken string. * \param nextToken updated to point to the pos after parsed token. * \param type The token type - * \param legacy If it is true function-like parameters are not allowed */ - static char *TokenParse(char * &nextToken, TokenType &type, bool legacy = false); + static char *TokenParse(const char * &nextToken, TokenType &type); /// Wrapper method for TokenParse. - static char *NextElement(TokenType &type, bool legacy = false); + static char *NextElement(TokenType &type); static std::stack CfgFiles; ///< The stack of open cfg files static TokenType LastTokenType; ///< The type of last parsed element - static char *LastToken; ///< Points to the last parsed token - static char *CfgLine; ///< The current line to parse - static char *CfgPos; ///< Pointer to the next element in cfgLine string - static std::queue Undo_; ///< The list with TokenUndo() or TokenPutBack() queued elements + static const char *CfgLine; ///< The current line to parse + static const char *CfgPos; ///< Pointer to the next element in cfgLine string + static std::queue CfgLineTokens_; ///< Store the list of tokens for current configuration line + static std::queue Undo_; ///< The list with TokenPutBack() queued elements static bool AllowMacros_; + static bool ParseQuotedOrToEol_; ///< The next tokens will be handled as quoted or to_eol token + static bool PreviewMode_; ///< The next token will not poped from cfg files, will just previewd. }; int parseConfigFile(const char *file_name); #endif /* SQUID_CONFIGPARSER_H */ === modified file 'src/Notes.cc' --- src/Notes.cc 2013-07-21 19:24:35 +0000 +++ src/Notes.cc 2013-08-05 20:05:41 +0000 @@ -76,41 +76,41 @@ } Note::Pointer Notes::add(const String ¬eKey) { typedef Notes::NotesList::iterator AMLI; for (AMLI i = notes.begin(); i != notes.end(); ++i) { if ((*i)->key == noteKey) return (*i); } Note::Pointer note = new Note(noteKey); notes.push_back(note); return note; } Note::Pointer Notes::parse(ConfigParser &parser) { String key = ConfigParser::NextToken(); - String value = ConfigParser::NextToken(); + String value = ConfigParser::NextQuotedToken(); Note::Pointer note = add(key); Note::Value::Pointer noteValue = note->addValue(value); String label(key); label.append('='); label.append(value); aclParseAclList(parser, ¬eValue->aclList, label.termedBuf()); if (blacklisted) { for (int i = 0; blacklisted[i] != NULL; ++i) { if (note->key.caseCmp(blacklisted[i]) == 0) { fatalf("%s:%d: meta key \"%s\" is a reserved %s name", cfg_filename, config_lineno, note->key.termedBuf(), descr ? descr : ""); } } } return note; } === modified file 'src/acl/Acl.cc' --- src/acl/Acl.cc 2013-07-21 19:24:35 +0000 +++ src/acl/Acl.cc 2013-08-20 09:01:14 +0000 @@ -38,62 +38,59 @@ #include "dlink.h" #include "globals.h" #include "profiler/Profiler.h" #include "SquidConfig.h" const ACLFlag ACLFlags::NoFlags[1] = {ACL_F_END}; const char *AclMatchedName = NULL; bool ACLFlags::supported(const ACLFlag f) const { if (f == ACL_F_REGEX_CASE) return true; return (supported_.find(f) != std::string::npos); } void ACLFlags::parseFlags() { char *nextToken; - while ((nextToken = ConfigParser::strtokFile()) != NULL && nextToken[0] == '-') { - + while ((nextToken = ConfigParser::NextTokenPreview()) != NULL && nextToken[0] == '-') { + (void)ConfigParser::NextToken(); //Get token from cfg line //if token is the "--" break flag if (strcmp(nextToken, "--") == 0) break; for (const char *flg = nextToken+1; *flg!='\0'; flg++ ) { if (supported(*flg)) { makeSet(*flg); } else { debugs(28, 0, HERE << "Flag '" << *flg << "' not supported"); self_destruct(); } } } /*Regex code needs to parse -i file*/ if ( isSet(ACL_F_REGEX_CASE)) ConfigParser::TokenPutBack("-i"); - - if (nextToken != NULL && strcmp(nextToken, "--") != 0 ) - ConfigParser::TokenUndo(); } const char * ACLFlags::flagsStr() const { static char buf[64]; if (flags_ == 0) return ""; char *s = buf; *s++ = '-'; for (ACLFlag f = 'A'; f <= 'z'; f++) { // ACL_F_REGEX_CASE (-i) flag handled by ACLRegexData class, ignore if (isSet(f) && f != ACL_F_REGEX_CASE) *s++ = f; } *s = '\0'; return buf; } === modified file 'src/acl/RegexData.cc' --- src/acl/RegexData.cc 2012-09-06 11:56:46 +0000 +++ src/acl/RegexData.cc 2013-08-05 09:51:23 +0000 @@ -305,41 +305,41 @@ flags &= ~REG_ICASE; } else { newTail = compileRE( Tail, wl->key , flags ); if (newTail == NULL) debugs(28, DBG_CRITICAL, "ERROR: Skipping regular expression. Compile failed: '" << wl->key << "'"); else Tail = newTail; } wl = wl->next; } } static void aclParseRegexList(RegexList **curlist) { char *t; wordlist *wl = NULL; debugs(28, 2, HERE << "aclParseRegexList: new Regex line or file"); - while ((t = ConfigParser::strtokFile()) != NULL) { + while ((t = ConfigParser::RegexStrtokFile()) != NULL) { const char *clean = removeUnnecessaryWildcards(t); if (strlen(clean) > BUFSIZ-1) { debugs(28, DBG_CRITICAL, "" << cfg_filename << " line " << config_lineno << ": " << config_input_line); debugs(28, DBG_CRITICAL, "ERROR: Skipping regular expression. Larger than " << BUFSIZ-1 << " characters: '" << clean << "'"); } else { debugs(28, 3, "aclParseRegexList: buffering RE '" << clean << "'"); wordlistAdd(&wl, clean); } } if (!compileOptimisedREs(curlist, wl)) { debugs(28, DBG_IMPORTANT, "WARNING: optimisation of regular expressions failed; using fallback method without optimisation"); compileUnoptimisedREs(curlist, wl); } wordlistDestroy(&wl); } void ACLRegexData::parse() === modified file 'src/cache_cf.cc' --- src/cache_cf.cc 2013-08-03 07:57:44 +0000 +++ src/cache_cf.cc 2013-08-18 14:00:10 +0000 @@ -243,40 +243,44 @@ static void free_sslproxy_cert_sign(sslproxy_cert_sign **cert_sign); static void parse_sslproxy_cert_adapt(sslproxy_cert_adapt **cert_adapt); static void dump_sslproxy_cert_adapt(StoreEntry *entry, const char *name, sslproxy_cert_adapt *cert_adapt); static void free_sslproxy_cert_adapt(sslproxy_cert_adapt **cert_adapt); static void parse_sslproxy_ssl_bump(acl_access **ssl_bump); static void dump_sslproxy_ssl_bump(StoreEntry *entry, const char *name, acl_access *ssl_bump); static void free_sslproxy_ssl_bump(acl_access **ssl_bump); #endif /* USE_SSL */ static void parse_b_size_t(size_t * var); static void parse_b_int64_t(int64_t * var); static bool parseNamedIntList(const char *data, const String &name, Vector &list); static void parse_CpuAffinityMap(CpuAffinityMap **const cpuAffinityMap); static void dump_CpuAffinityMap(StoreEntry *const entry, const char *const name, const CpuAffinityMap *const cpuAffinityMap); static void free_CpuAffinityMap(CpuAffinityMap **const cpuAffinityMap); static int parseOneConfigFile(const char *file_name, unsigned int depth); +static void parse_configuration_includes_quoted_values(bool *recognizeQuotedValues); +static void dump_configuration_includes_quoted_values(StoreEntry *const entry, const char *const name, bool recognizeQuotedValues); +static void free_configuration_includes_quoted_values(bool *recognizeQuotedValues); + /* * LegacyParser is a parser for legacy code that uses the global * approach. This is static so that it is only exposed to cache_cf. * Other modules needing access to a ConfigParser should have it * provided to them in their parserFOO methods. */ static ConfigParser LegacyParser = ConfigParser(); void self_destruct(void) { LegacyParser.destruct(); } static void update_maxobjsize(void) { int64_t ms = -1; // determine the maximum size object that can be stored to disk @@ -1781,41 +1785,41 @@ } static void dump_http_header_replace(StoreEntry * entry, const char *name, const HeaderManglers *manglers) { if (manglers) manglers->dumpReplacement(entry, name); } static void parse_http_header_replace(HeaderManglers **pm) { char *t = NULL; if ((t = ConfigParser::NextToken()) == NULL) { debugs(3, DBG_CRITICAL, "" << cfg_filename << " line " << config_lineno << ": " << config_input_line); debugs(3, DBG_CRITICAL, "parse_http_header_replace: missing header name."); return; } - const char *value = t + strlen(t) + 1; + const char *value = ConfigParser::NextQuotedOrToEol(); if (!*pm) *pm = new HeaderManglers; HeaderManglers *manglers = *pm; manglers->setReplacement(t, value); } #endif static void dump_cachedir(StoreEntry * entry, const char *name, SquidConfig::_cacheSwap swap) { SwapDir *s; int i; assert (entry); for (i = 0; i < swap.n_configured; ++i) { s = dynamic_cast(swap.swapDirs[i].getRaw()); if (!s) continue; storeAppendPrintf(entry, "%s %s %s", name, s->type(), s->path); @@ -2686,55 +2690,57 @@ debugs(0, DBG_PARSE_NOTE(DBG_IMPORTANT), "WARNING: 'enable' is deprecated. Please update to use value 'on'."); *var = 1; } else if (!strcmp(token, "warn")) { *var = -1; } else if (!strcmp(token, "off")) { *var = 0; } else if (!strcmp(token, "disable")) { debugs(0, DBG_PARSE_NOTE(DBG_IMPORTANT), "WARNING: 'disable' is deprecated. Please update to use value 'off'."); *var = 0; } else { debugs(0, DBG_PARSE_NOTE(DBG_IMPORTANT), "ERROR: Invalid option: Tristate options can only be 'on', 'off', or 'warn'."); self_destruct(); } } #define free_tristate free_int void parse_pipelinePrefetch(int *var) { - char *token = ConfigParser::strtokFile(); + char *token = ConfigParser::NextTokenPreview(); if (token == NULL) self_destruct(); if (!strcmp(token, "on")) { debugs(0, DBG_PARSE_NOTE(DBG_IMPORTANT), "WARNING: 'pipeline_prefetch on' is deprecated. Please update to use 1 (or a higher number)."); *var = 1; + //pop the token + (void)ConfigParser::NextToken(); } else if (!strcmp(token, "off")) { debugs(0, DBG_PARSE_NOTE(2), "WARNING: 'pipeline_prefetch off' is deprecated. Please update to use '0'."); *var = 0; - } else { - ConfigParser::TokenUndo(); + //pop the token + (void)ConfigParser::NextToken(); + } else parse_int(var); - } } #define free_pipelinePrefetch free_int #define dump_pipelinePrefetch dump_int static void dump_refreshpattern(StoreEntry * entry, const char *name, RefreshPattern * head) { while (head != NULL) { storeAppendPrintf(entry, "%s%s %s %d %d%% %d", name, head->flags.icase ? " -i" : null_string, head->pattern, (int) head->min / 60, (int) (100.0 * head->pct + 0.5), (int) head->max / 60); if (head->max_stale >= 0) storeAppendPrintf(entry, " max-stale=%d", head->max_stale); @@ -2791,48 +2797,48 @@ int max_stale = -1; #if USE_HTTP_VIOLATIONS int override_expire = 0; int override_lastmod = 0; int reload_into_ims = 0; int ignore_reload = 0; int ignore_no_store = 0; int ignore_must_revalidate = 0; int ignore_private = 0; int ignore_auth = 0; #endif int i; RefreshPattern *t; regex_t comp; int errcode; int flags = REG_EXTENDED | REG_NOSUB; - if ((token = ConfigParser::NextToken()) != NULL) { + if ((token = ConfigParser::RegexPattern()) != NULL) { if (strcmp(token, "-i") == 0) { flags |= REG_ICASE; - token = ConfigParser::NextToken(); + token = ConfigParser::RegexPattern(); } else if (strcmp(token, "+i") == 0) { flags &= ~REG_ICASE; - token = ConfigParser::NextToken(); + token = ConfigParser::RegexPattern(); } } if (token == NULL) { debugs(3, DBG_CRITICAL, "FATAL: refresh_pattern missing the regex pattern parameter"); self_destruct(); return; } pattern = xstrdup(token); i = GetInteger(); /* token: min */ /* catch negative and insanely huge values close to 32-bit wrap */ if (i < 0) { debugs(3, DBG_IMPORTANT, "WARNING: refresh_pattern minimum age negative. Cropped back to zero."); i = 0; } if (i > 60*24*365) { @@ -3233,41 +3239,41 @@ static void dump_wordlist(StoreEntry * entry, const char *name, wordlist * list) { while (list != NULL) { storeAppendPrintf(entry, "%s %s\n", name, list->key); list = list->next; } } void ConfigParser::ParseWordList(wordlist ** list) { parse_wordlist(list); } void parse_wordlist(wordlist ** list) { char *token; - while ((token = ConfigParser::NextToken())) + while ((token = ConfigParser::NextQuotedToken())) wordlistAdd(list, token); } #if 0 /* now unused */ static int check_null_wordlist(wordlist * w) { return w == NULL; } #endif static int check_null_acl_access(acl_access * a) { return a == NULL; } #define free_wordlist wordlistDestroy #define free_uri_whitespace free_int @@ -4064,73 +4070,75 @@ /* determine configuration style */ const char *filename = ConfigParser::NextToken(); if (!filename) { self_destruct(); return; } if (strcmp(filename, "none") == 0) { cl->type = Log::Format::CLF_NONE; aclParseAclList(LegacyParser, &cl->aclList, filename); while (*logs) logs = &(*logs)->next; *logs = cl; return; } cl->filename = xstrdup(filename); cl->type = Log::Format::CLF_UNKNOWN; - const char *token = ConfigParser::strtokFile(); + const char *token = ConfigParser::NextTokenPreview(); if (!token) { // style #1 // no options to deal with } else if (!strchr(token, '=')) { // style #3 - // if logformat name is not recognized, - // put back the token; it must be an ACL name - if (!setLogformat(cl, token, false)) - ConfigParser::TokenUndo(); + // if logformat name is recognized, + // pop the previewed token; Else it must be an ACL name + if (setLogformat(cl, token, false)) + (void)ConfigParser::NextToken(); } else { // style #4 do { if (strncasecmp(token, "on-error=", 9) == 0) { if (strncasecmp(token+9, "die", 3) == 0) { cl->fatal = true; } else if (strncasecmp(token+9, "drop", 4) == 0) { cl->fatal = false; } else { debugs(3, DBG_CRITICAL, "Unknown value for on-error '" << token << "' expected 'drop' or 'die'"); self_destruct(); } } else if (strncasecmp(token, "buffer-size=", 12) == 0) { parseBytesOptionValue(&cl->bufferSize, B_BYTES_STR, token+12); } else if (strncasecmp(token, "logformat=", 10) == 0) { setLogformat(cl, token+10, true); } else if (!strchr(token, '=')) { - // put back the token; it must be an ACL name - ConfigParser::TokenUndo(); + // Do not pop the token; it must be an ACL name break; // done with name=value options, now to ACLs } else { debugs(3, DBG_CRITICAL, "Unknown access_log option " << token); self_destruct(); } - } while ((token = ConfigParser::strtokFile()) != NULL); + // Pop the token, it was a valid "name=value" option + (void)ConfigParser::NextToken(); + // Get next with preview ConfigParser::NextToken call. + } while ((token = ConfigParser::NextTokenPreview()) != NULL); } // set format if it has not been specified explicitly if (cl->type == Log::Format::CLF_UNKNOWN) setLogformat(cl, "squid", true); aclParseAclList(LegacyParser, &cl->aclList, cl->filename); while (*logs) logs = &(*logs)->next; *logs = cl; } /// sets CustomLog::type and, if needed, CustomLog::lf /// returns false iff there is no named log format static bool setLogformat(CustomLog *cl, const char *logdef_name, const bool dieWhenMissing) { assert(cl); @@ -4748,41 +4756,41 @@ } static void parse_HeaderWithAclList(HeaderWithAclList **headers) { char *fn; if (!*headers) { *headers = new HeaderWithAclList; } if ((fn = ConfigParser::NextToken()) == NULL) { self_destruct(); return; } HeaderWithAcl hwa; hwa.fieldName = fn; hwa.fieldId = httpHeaderIdByNameDef(fn, strlen(fn)); if (hwa.fieldId == HDR_BAD_HDR) hwa.fieldId = HDR_OTHER; Format::Format *nlf = new ::Format::Format("hdrWithAcl"); ConfigParser::EnableMacros(); - String buf = ConfigParser::NextToken(); + String buf = ConfigParser::NextQuotedToken(); ConfigParser::DisableMacros(); hwa.fieldValue = buf.termedBuf(); hwa.quoted = ConfigParser::LastTokenWasQuoted(); if (hwa.quoted) { if (!nlf->parse(hwa.fieldValue.c_str())) { self_destruct(); return; } hwa.valueFormat = nlf; } else delete nlf; aclParseAclList(LegacyParser, &hwa.aclList, (hwa.fieldName + ':' + hwa.fieldValue).c_str()); (*headers)->push_back(hwa); } static void free_HeaderWithAclList(HeaderWithAclList **header) { if (!(*header)) return; @@ -4797,20 +4805,50 @@ } delete *header; *header = NULL; } static void parse_note(Notes *notes) { assert(notes); notes->parse(LegacyParser); } static void dump_note(StoreEntry *entry, const char *name, Notes ¬es) { notes.dump(entry, name); } static void free_note(Notes *notes) { notes->clean(); } + +static void +parse_configuration_includes_quoted_values(bool *recognizeQuotedValues) +{ + int val = 0; + parse_onoff(&val); + + // If quoted values is set to on then enable new strict mode parsing + if (val) { + ConfigParser::RecognizeQuotedValues = true; + ConfigParser::StrictMode = true; + } else { + ConfigParser::RecognizeQuotedValues = false; + ConfigParser::StrictMode = false; + } +} + +static void +dump_configuration_includes_quoted_values(StoreEntry *const entry, const char *const name, bool recognizeQuotedValues) +{ + int val = ConfigParser::RecognizeQuotedValues ? 1 : 0; + dump_onoff(entry, name, val); +} + +static void +free_configuration_includes_quoted_values(bool *recognizeQuotedValues) +{ + ConfigParser::RecognizeQuotedValues = false; + ConfigParser::StrictMode = false; +} === modified file 'src/cf.data.depend' --- src/cf.data.depend 2013-05-26 01:57:47 +0000 +++ src/cf.data.depend 2013-08-06 06:47:45 +0000 @@ -1,36 +1,37 @@ # type dependencies access_log acl logformat acl external_acl_type auth_param acl_access acl acl_address acl acl_b_size_t acl acl_tos acl acl_nfmark acl address authparam b_int64_t b_size_t b_ssize_t cachedir cache_replacement_policy cachemgrpasswd ConfigAclTos +configuration_includes_quoted_values CpuAffinityMap debug delay_pool_access acl delay_class delay_pool_class delay_pools delay_pool_count delay_pool_rates delay_class client_delay_pool_access acl client_delay_pool_count client_delay_pool_rates denyinfo acl eol externalAclHelper auth_param HelperChildConfig hostdomain cache_peer hostdomaintype cache_peer http_header_access acl http_header_replace HeaderWithAclList acl adaptation_access_type adaptation_service_set adaptation_service_chain acl icap_service icap_class adaptation_service_set_type icap_service ecap_service === modified file 'src/cf.data.pre' --- src/cf.data.pre 2013-08-01 20:45:56 +0000 +++ src/cf.data.pre 2013-08-18 14:00:10 +0000 @@ -8465,42 +8465,42 @@ DOC_START The size, low-, and high-water marks for the IP cache. DOC_END NAME: fqdncache_size COMMENT: (number of entries) TYPE: int DEFAULT: 1024 LOC: Config.fqdncache.size DOC_START Maximum number of FQDN cache entries. DOC_END COMMENT_START MISCELLANEOUS ----------------------------------------------------------------------------- COMMENT_END NAME: configuration_includes_quoted_values COMMENT: on|off -TYPE: onoff -DEFAULT: on +TYPE: configuration_includes_quoted_values +DEFAULT: off LOC: ConfigParser::RecognizeQuotedValues DOC_START If set, Squid will recognize each "quoted string" after a configuration directive as a single parameter. The quotes are stripped before the parameter value is interpreted or used. See "Values with spaces, quotes, and other special characters" section for more details. DOC_END NAME: memory_pools COMMENT: on|off TYPE: onoff DEFAULT: on LOC: Config.onoff.mem_pools DOC_START If set, Squid will keep pools of allocated (but unused) memory available for future use. If memory is a premium on your system and you believe your malloc library outperforms Squid routines, disable this. DOC_END