On 08/13/2013 11:57 PM, Alex Rousskov wrote:
> On 08/07/2013 03:11 AM, Tsantilas Christos wrote:
>> On 07/31/2013 07:59 PM, Alex Rousskov wrote:
>>> 2. When configuration_includes_quoted_values is on, new "strict syntax"
>>> rules are enforced:
> 
>>> 2c. By default, token delimiter is whitespace. Bare (i.e., unquoted)
>>> tokens containing any character other than alphanumeric, underscore,
>>> period, '(', ')', '=', and '-' terminate Squid. For example, foo{bar},
>>> foo@bar, and foo"bar" terminate Squid in strict syntax mode.
> 
>> OK.
>> In this patch allow the following ".,()-=_%/:"
>> We can easily add remove characters from this list.
>> If we remove from this list the ':' then we should require quotes to
>> define http, icap or ecap urls.
>> The % used to define percent values and the '/' to define filenames.
> 
> I am OK with adding ':' and '/' to the allowed set.
> 
> I think we should allow % at the end of a bare token (e.g., 100%) but
> not elsewhere (e.g., http://www.%host.com/). I am worried that by
> allowing %macro-like strings in bare tokens we would not be able to tell
> the admin when they forgot to quote a string that contains a macro.


OK. This patch allow only [0-9]+% tokens (eg 100%, 80% etc).

> 
> Another potential problem here is with the '=' character. We have to
> allow it because existing code expects it in directive options with
> values (e.g., bypass=on). However, making '=' a part of the token makes
> it difficult to support quoted option values:
> 
>   bypass="on" // illegal because a token cannot have quotes
> 
>   "bypass=on" // legal but looks kind of weird
>               // because there are actually several tokens here
> 
> For simple values such as "on", this is not a problem, but not all
> option values are that simple, and some of them will require quoting:
> 
>     uri="icap://example.com/?mode=reqmod" // illegal?!
> 
>     "uri=icap://example.com/?mode=reqmod" // weird
> 
> Changing all callers to treat name=value sequence as three tokens is
> probably too much work, right?

This is requires changes in many places...


> 
> 
> A similar problem exists for function() calls but we hack around it by
> parsing parameters() specially and requiring that file names are quoted,
> I guess. More on that below.
> 
> 
> 
> 
>> +    bool saveRecognizeQuotedValues = ConfigParser::RecognizeQuotedValues;
> ...
>> +            bool savePreview = ConfigParser::PreviewMode_;
> 
> 
> Please make those variables const.

ok

> 
> 
>> +bool ConfigParser::ParseQuotedOrToEOL_ = false;
> 
> Please s/EOL/Eol/.

ok

> 
> 
>> +static const char *SQUID_ERROR_TOKEN = "SQUID_ERROR_TOKEN";
> 
> The value should probably contain spaces and other special characters to
> minimize a chance of collision with the actual token. For example,
> "[invalid token]". Note that you never test for SQUID_ERROR_TOKEN being
> returned -- the code relies on known values to be different from the
> SQUID_ERROR_TOKEN value.

ok


> 
> 
>> +        (void)ConfigParser::NextToken(); //Get token fron cfg line
> 
> typo: s/fron/from/

fixed

> 
> 
>>      /* 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();
>> +    while (*s && *s != quoteChar && !errorStr && (d - UnQuoted) < 
>> sizeof(UnQuoted)) {
> 
> The old comment is now out of sync. You can say something like "scan
> until the end of the quoted string, handling escape sequences".

ok

> 
> 
>>          } else if (*s == '$' && quoteChar == '"') {
>> -            debugs(3, DBG_CRITICAL, "Unsupported cfg macro: " << s);
>> -            self_destruct();
>> +            errorStr = "Unsupported cfg macro";
>> +            errorPos = s;
> 
> If there is consensus that we do not need to support $macros now or in
> the future (except for pre-processor SMP macros that are already handled
> by the time the above code runs), then the above special $macor case can
> be removed.

I put the related code inside an "#if 0 ... #endif"
I will remove it if required...

> 
> 
>> +        char *token = xstrdup(UnQuote(nextToken, &nextToken));
>> +        CfgLineTokens_.push(token);
> ...
>> +    while (!CfgLineTokens_.empty()) {
>> +        char *token = CfgLineTokens_.front();
>> +        CfgLineTokens_.pop();
>> +        free(token);
>> +    }
> 
> Can we use String or std::string for these stored tokens so that we do
> not have to be so careful about allocating and deleting them?

Not easy.
This is requires to modify NextToken and related methods to return
"const char *" instead of "char *".
This is requires changes in many places. I remember I had problems when
I try it...



> 
> 
>>  4) Add the ConfigParser::RegexPattern() to get the next regex token
>>
>>  5) Add the ConfigParser::RegexStrtokFile() to get the next regex token
>> which is compatible with the old strtokFile
> 
>> +char *
>> +ConfigParser::RegexStrtokFile()
>> +{
>> +    ParseRegex_ = true;
>> +    char * token = strtokFile();
>> +    ParseRegex_ = false;
>> +    return token;
>> +}
>>  
>> +char *
>> +ConfigParser::RegexPattern()
>> +{
>> +    ParseRegex_ = true;
>> +    char * token = NextToken();
>> +    ParseRegex_ = false;
>> +    return token;
>> +}
> 
> 
> This feels wrong. It is good to have these methods so that we know which
> callers need REs, but their behavior should be different IMO:
> 
> * In legacy mode, they should call NextToken() and strtokFile() which
> will parse legacy REs as expected. No need for ParseRegex_.
> 
> * In strict mode, they should fail immediately so that we can introduce
> proper RE support later, without screwing up already working
> configurations that use strict mode. Supporting REs in strict mode is a
> TODO, just like supporting single quoted strings is. For now, we just
> need to make sure that we can add such support without breaking
> configurations that start using strict mode.

OK. I implement this behaviour for now.
The parser return the message:
 "Can not read regex expresion while
configuration_includes_quoted_values is enabled"

When we are trying to read regex expresions (eg parse regex acl) when
the configuration_includes_quoted_values is set to on.

The user should use something like the following:
# Enable quoted values on squid cfg file parsing
configuration_includes_quoted_values on
....
#Temporary disable quoted values to parse regex expresions
configuration_includes_quoted_values off
refresh_pattern ^ftp:           1440    20%     10080
refresh_pattern ^gopher:        1440    0%      1440
refresh_pattern -i (/cgi-bin/|\?) 0     0%      0
refresh_pattern .               0       20%     4320
configuration_includes_quoted_values on
...

> 
> For example, if we decide that REs in strict mode should start with '/'
> or m{} as they do in Perl, then we have to make sure _now_ that no
> legacy RE that starts with those characters sneaks into the strict mode
> while we are working on RE support. If we fail to do that, we will
> create a new set of fresh backward compatibility problems to deal with.

I see....

> 
> 
>>      /// 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
>> +    static bool StrictMode;
> 
> You may want to add a comment saying that StrictMode may remain false
> when the legacy ConfigParser::NextQuotedToken() call forces
> RecognizeQuotedValues to be temporary true. Otherwise, it is not clear
> why we need both members.

Ok

> 
> 
>> +    if (!PreviewMode_ || type == FunctionParameters)
>> +        parsePos = pos;
>> +    // else next call will read the same token;
> 
> Please add a comment explaining this logic. It is not clear why parsePos
> is updated if we are not in preview mode or if we are parsing
> FunctionParameters [in preview mode].

I add a comment.
The true is that the related code is a little complex. But for function
"parameters()" we need to update the current parsing position, even if
we are in preview mode, because we need to read the next quoted string
which is  the "parameters()" argument, the name of the file we want to
open to read the next valid token.
For preview the caller needs the next valid token not the "parameters()"
function and its argument.


> 
> 
>> -     * Return the last TokenUndo() or TokenPutBack() queued element, or NULL
>> +     * Return the last TokenPutBack() queued element, or NULL
> 
> No comma is needed after this change.

ok.

> 
> 
> Thank you,
> 
> Alex.
> 
> 

=== 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 <[email protected]>
  */
 
 #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::CfgFile *> 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<char *> ConfigParser::CfgLineTokens_;
 std::queue<std::string> 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<CfgFile *> 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<std::string> 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<char *> CfgLineTokens_; ///< Store the list of tokens for current configuration line
+    static std::queue<std::string> 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 &noteKey)
 {
     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, &noteValue->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<int> &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<SwapDir *>(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 &notes)
 {
     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

Reply via email to