------------------------------------------------------------ revno: 12992 revision-id: chtsanti@users.sourceforge.net-20130829092153-63anu0uit9yp43tc parent: squid3@treenet.co.nz-20130826111458-sp2alv1ioil5kl4i committer: Christos Tsantilas branch nick: trunk timestamp: Thu 2013-08-29 12:21:53 +0300 message: Fix configuration file parsing bugs, related to quoted strings This is patch try to fix problems discussed under the "[PATCH] Unknown cfg function" mail thread on squid-dev mailing list Fixes/changes: - The quoted tokens mode (configuration_includes_quoted_values parameter) is set to off by default - If configuration_includes_quoted_values is off the quoted tokens parsed using the ConfigParser::NextToken include the quotes, to keep compatibility with older releases. - The methods ConfigParser::RegexPattern() and ConfigParser::RegexStrtokFile() added to parse regex tokens. The regex tokens can not be quoted values but have their own syntax. - For the cases where quoted strings are required (wordlists, Notes parsing, Headers with acl), the new ConfigParser::NextQuotedToken method added. The old wordlists parser allowed escaping any character, this patch will return an error if you try to escape alphanumeric characters. The \r \n and \t have the C semantics. - Removes the ConfigParser::TokenUndo method. The new method ConfigParser::NextTokenPreview() which can be used to preview the next token is added. This method if the next token is invalid (eg unquoted with special characters) instead of calling self_destruct it will return the "[invalid token]" as token. - A set of new flags defined under ConfigParser class to define the type of parsing: ParseRegex_ (next token is regex) ParseQuotedOrToEOL_ (next token is quoted or to-EOL), PreviewMode_ (just do preview do not pop next token) - Currently parser read a line, and the tokens stored on this line and the line modified while parsed. This patch extracts the tokens from line and store them to ConfigParser::CfgLineTokens_ member. This method selected because 1) We may need to parse again the line so we do not want to modify it. For example call the ConfigParser::PeekAtToken method to check if it is a flag token ( eg "-i"), and if not call the ConfigParser::RegexPattern() method to read next regex expression. 2) The current line tokens must stored somewhere to support the following: char *name = ConfigParser::NextToken(); char *value = ConfigParser::NextToken(); The ConfigParser::CfgLineTokens_ reset when a new config line is read. TODO: - The ConfigParser::TokenPutBack method probably should removed in the future together with the ConfigParser::Undo_ and ConfigParser::Undo(). This method is currently used only in one place (acl regex). ------------------------------------------------------------ # Bazaar merge directive format 2 (Bazaar 0.90) # revision_id: chtsanti@users.sourceforge.net-20130829092153-\ # 63anu0uit9yp43tc # target_branch: http://bzr.squid-cache.org/bzr/squid3/trunk/ # testament_sha1: b6d73a75e231f03e2f12a05ad2c03a8838c632a3 # timestamp: 2013-08-29 10:01:30 +0000 # source_branch: http://bzr.squid-cache.org/bzr/squid3/trunk/ # base_revision_id: squid3@treenet.co.nz-20130826111458-\ # sp2alv1ioil5kl4i # # Begin patch === modified file 'src/ConfigParser.cc' --- src/ConfigParser.cc 2013-07-22 01:26:09 +0000 +++ src/ConfigParser.cc 2013-08-29 09:21:53 +0000 @@ -38,14 +38,19 @@ #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() @@ -75,13 +80,6 @@ } void -ConfigParser::TokenUndo() -{ - assert(LastToken); - Undo_.push(LastToken); -} - -void ConfigParser::TokenPutBack(const char *tok) { assert(tok); @@ -95,7 +93,8 @@ 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; @@ -113,21 +112,27 @@ 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) { + } else if (*t == '\"' || *t == '\'') { /* quote found, start reading from file */ debugs(3, 8,"Quoted token found : " << t); - - if ((wordFile = fopen(t, "r")) == NULL) { + char *fn = ++t; + + 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; } @@ -138,7 +143,7 @@ fromFile = 1; } else { - return LastToken = t; + return t; } } @@ -169,39 +174,80 @@ /* 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; - } - - if (*s != quoteChar) { - debugs(3, DBG_CRITICAL, "missing '" << quoteChar << "' at the end of quoted string: " << (s-1)); - self_destruct(); - } - *end = s; - return (token+1); + ++d; + } + + if (*s != quoteChar && !errorStr) { + errorStr = "missing quote char at the end of quoted string"; + errorPos = s - 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 @@ -209,63 +255,115 @@ { 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'; + 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 (*nextToken != '\0' && *nextToken != '#') { ++nextToken; } - if (*token == '\0') - return NULL; - 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; + } - char *token = NULL; do { while (token == NULL && !CfgFiles.empty()) { ConfigParser::CfgFile *wordfile = CfgFiles.top(); @@ -273,6 +371,7 @@ if (!token) { assert(!wordfile->isOpen()); CfgFiles.pop(); + debugs(3, 4, "CfgFiles.pop " << wordfile->filePath); delete wordfile; } } @@ -280,7 +379,11 @@ 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); @@ -290,6 +393,7 @@ // 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(); @@ -311,40 +415,70 @@ } 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::PeekAtToken() +{ + PreviewMode_ = true; + char *token = NextToken(); + PreviewMode_ = false; + return token; } char * ConfigParser::NextQuotedOrToEol() { - char *token; - - if ((token = CfgPos) == NULL) { - debugs(3, DBG_CRITICAL, "token is missing"); - self_destruct(); - return NULL; - } - token += strspn(token, w_space); - - 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; - - CfgPos = NULL; - return (LastToken = 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; +} + +char * +ConfigParser::RegexStrtokFile() +{ + if (ConfigParser::RecognizeQuotedValues) { + debugs(3, DBG_CRITICAL, "Can not read regex expresion while configuration_includes_quoted_values is enabled"); + self_destruct(); + } + char * token = strtokFile(); + return token; +} + +char * +ConfigParser::RegexPattern() +{ + if (ConfigParser::RecognizeQuotedValues) { + debugs(3, DBG_CRITICAL, "Can not read regex expresion while configuration_includes_quoted_values is enabled"); + self_destruct(); + } + + 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 * @@ -375,6 +509,7 @@ 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; @@ -425,7 +560,12 @@ 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() === modified file 'src/ConfigParser.h' --- src/ConfigParser.h 2013-07-21 19:24:35 +0000 +++ src/ConfigParser.h 2013-08-29 09:21:53 +0000 @@ -70,7 +70,7 @@ * 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); @@ -93,6 +93,25 @@ */ 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);} @@ -104,12 +123,12 @@ 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 *PeekAtToken(); /** * The next NextToken call will return the token as next element @@ -127,7 +146,15 @@ 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: /** @@ -165,24 +192,21 @@ 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 @@ -190,19 +214,20 @@ * \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); === modified file 'src/Notes.cc' --- src/Notes.cc 2013-07-21 19:24:35 +0000 +++ src/Notes.cc 2013-08-29 09:21:53 +0000 @@ -93,7 +93,7 @@ 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); === modified file 'src/acl/Acl.cc' --- src/acl/Acl.cc 2013-07-21 19:24:35 +0000 +++ src/acl/Acl.cc 2013-08-29 09:21:53 +0000 @@ -55,8 +55,8 @@ ACLFlags::parseFlags() { char *nextToken; - while ((nextToken = ConfigParser::strtokFile()) != NULL && nextToken[0] == '-') { - + while ((nextToken = ConfigParser::PeekAtToken()) != NULL && nextToken[0] == '-') { + (void)ConfigParser::NextToken(); //Get token from cfg line //if token is the "--" break flag if (strcmp(nextToken, "--") == 0) break; @@ -74,9 +74,6 @@ /*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 * === modified file 'src/acl/RegexData.cc' --- src/acl/RegexData.cc 2012-09-06 11:56:46 +0000 +++ src/acl/RegexData.cc 2013-08-29 09:21:53 +0000 @@ -322,7 +322,7 @@ 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); === modified file 'src/cache_cf.cc' --- src/cache_cf.cc 2013-08-03 07:57:44 +0000 +++ src/cache_cf.cc 2013-08-29 09:21:53 +0000 @@ -260,6 +260,10 @@ 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. @@ -1798,7 +1802,7 @@ return; } - const char *value = t + strlen(t) + 1; + const char *value = ConfigParser::NextQuotedOrToEol(); if (!*pm) *pm = new HeaderManglers; @@ -2703,7 +2707,7 @@ void parse_pipelinePrefetch(int *var) { - char *token = ConfigParser::strtokFile(); + char *token = ConfigParser::PeekAtToken(); if (token == NULL) self_destruct(); @@ -2711,13 +2715,15 @@ 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 @@ -2808,14 +2814,14 @@ 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(); } } @@ -3250,7 +3256,7 @@ parse_wordlist(wordlist ** list) { char *token; - while ((token = ConfigParser::NextToken())) + while ((token = ConfigParser::NextQuotedToken())) wordlistAdd(list, token); } @@ -4081,14 +4087,14 @@ cl->filename = xstrdup(filename); cl->type = Log::Format::CLF_UNKNOWN; - const char *token = ConfigParser::strtokFile(); + const char *token = ConfigParser::PeekAtToken(); 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) { @@ -4106,14 +4112,16 @@ } 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::PeekAtToken()) != NULL); } // set format if it has not been specified explicitly @@ -4765,7 +4773,7 @@ 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(); @@ -4814,3 +4822,33 @@ { 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-29 09:21:53 +0000 @@ -14,6 +14,7 @@ cachedir cache_replacement_policy cachemgrpasswd ConfigAclTos +configuration_includes_quoted_values CpuAffinityMap debug delay_pool_access acl delay_class === modified file 'src/cf.data.pre' --- src/cf.data.pre 2013-08-21 08:43:27 +0000 +++ src/cf.data.pre 2013-08-29 09:21:53 +0000 @@ -8482,8 +8482,8 @@ 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