Quoted values in squid.conf This patch : - adds support for quoted values in the entire squid.conf - warn about or prohibit values that can no longer be interpreted as either quoted strings or simple tokens - support file:"path/to/file.name" syntax to load external configuration files - support macros in "double quoted" values - support 'single quoted' values that do not expand macros - replaces the strtok() calls with calls to the new ConfigParser::NextToken() - modify strtokFile to use new ConfigParser::NextToken() - Add the new configuration_includes_quoted_values configuration option, to control the squid parser behaviour. If set to on Squid will recognize each "quoted string" after a configuration directive as a single parameter This is a Measurement Factory project === modified file 'src/ConfigParser.cc' --- src/ConfigParser.cc 2013-04-16 15:08:46 +0000 +++ src/ConfigParser.cc 2013-05-23 18:19:41 +0000 @@ -21,192 +21,363 @@ * 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" -char *ConfigParser::lastToken = NULL; -std::queue ConfigParser::undo; +int ConfigParser::RecognizeQuotedValues = true; +std::stack ConfigParser::CfgFiles; +bool ConfigParser::Quoted = false; +char *ConfigParser::LastToken = NULL; +char *ConfigParser::CfgLine = NULL; +char *ConfigParser::CfgPos = NULL; +std::queue ConfigParser::Undo_; +MacroUser *ConfigParser::MacroUser_ = NULL; void ConfigParser::destruct() { shutting_down = 1; fatalf("Bungled %s line %d: %s", cfg_filename, config_lineno, config_input_line); } void -ConfigParser::strtokFileUndo() +ConfigParser::TokenUndo() { - assert(lastToken); - undo.push(lastToken); + assert(LastToken); + Undo_.push(LastToken); } void -ConfigParser::strtokFilePutBack(const char *tok) +ConfigParser::TokenPutBack(const char *tok) { assert(tok); - undo.push(tok); + Undo_.push(tok); } char * -ConfigParser::strtokFile(void) +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(); + return undoToken; + } + return NULL; +} + +char * +ConfigParser::strtokFile() +{ + // XXX: add file:name support to the quoted string-aware parser + if (RecognizeQuotedValues) + return ConfigParser::NextToken(); + static int fromFile = 0; static FILE *wordFile = NULL; - LOCAL_ARRAY(char, undoToken, CONFIG_LINE_LIMIT); - char *t, *fn; + char *t; LOCAL_ARRAY(char, buf, CONFIG_LINE_LIMIT); - if (!undo.empty()) { - strncpy(undoToken, undo.front().c_str(), sizeof(undoToken)); - undoToken[sizeof(undoToken) - 1] = '\0'; - undo.pop(); - return undoToken; - } + if ((LastToken = ConfigParser::Undo())) + return LastToken; - lastToken = NULL; do { if (!fromFile) { - t = (strtok(NULL, w_space)); - - if (!t || *t == '#') { + bool wasQuoted; + t = ConfigParser::NextElement(&wasQuoted); + debugs(28, DBG_CRITICAL,"Token scanned for quote : " << t); + if (!t) { return NULL; - } else if (*t == '\"' || *t == '\'') { + } else if (wasQuoted) { /* quote found, start reading from file */ - fn = ++t; - - while (*t && *t != '\"' && *t != '\'') - ++t; + debugs(28, DBG_CRITICAL,"Quote found : " << t); - *t = '\0'; - - if ((wordFile = fopen(fn, "r")) == NULL) { - debugs(28, DBG_CRITICAL, "strtokFile: " << fn << " not found"); - return (NULL); + if ((wordFile = fopen(t, "r")) == NULL) { + debugs(28, DBG_CRITICAL, "strtokFile: " << t << " not found"); + return false; } #if _SQUID_WINDOWS_ setmode(fileno(wordFile), O_TEXT); #endif fromFile = 1; } else { - return lastToken = t; + return LastToken = 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 LastToken = t; } -void -ConfigParser::ParseQuotedString(char **var, bool *wasQuoted) +char * +ConfigParser::UnQuote(char *token, char **end) { - String sVar; - ParseQuotedString(&sVar, wasQuoted); - *var = xstrdup(sVar.termedBuf()); + 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(); + } else if (*s == '$') { + debugs(3, DBG_CRITICAL, "Unsupported cfg macro: " << s); + self_destruct(); + } else if (*s == '%' && quoteChar == '"' && + (!MacroUser_ || !MacroUser_->supportedMacro(s))) { + 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); + } + ++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); } void -ConfigParser::ParseQuotedString(String *var, bool *wasQuoted) +ConfigParser::SetCfgLine(char *line) { - // Get all of the remaining string - char *token = strtok(NULL, ""); - if (token == NULL) - self_destruct(); + CfgLine = line; + CfgPos = line; +} - if (*token != '"') { - token = strtok(token, w_space); - var->reset(token); +char * +ConfigParser::TokenParse(char * &nextToken, bool *wasQuoted) +{ + if (!nextToken || *nextToken == '\0') + return NULL; + nextToken += strspn(nextToken, w_space); + if (*nextToken == '"' || *nextToken == '\'') { if (wasQuoted) - *wasQuoted = false; - return; - } else if (wasQuoted) - *wasQuoted = true; + *wasQuoted = true; + char *token = UnQuote(nextToken, &nextToken); + *nextToken = '\0'; + ++nextToken; + return token; + } - char *s = token + 1; - /* scan until the end of the quoted string, unescaping " and \ */ - while (*s && *s != '"') { - if (*s == '\\') { - const char * next = s+1; // may point to 0 - memmove(s, next, strlen(next) + 1); - } - ++s; + if (wasQuoted) + *wasQuoted = false; + + char *token = nextToken; + if (char *t = strchr(nextToken, '#')) + *t = '\0'; + nextToken += strcspn(nextToken, w_space); + if (*nextToken != '\0') { + *nextToken = '\0'; + ++nextToken; } - if (*s != '"') { - debugs(3, DBG_CRITICAL, "ParseQuotedString: missing '\"' at the end of quoted string" ); + if (*token == '\0') + return NULL; + + return token; +} + +char * +ConfigParser::NextElement(bool *wasQuoted) +{ + char *token = TokenParse(CfgPos, wasQuoted); + return token; +} + +char * +ConfigParser::NextToken() +{ + if ((LastToken = ConfigParser::Undo())) + return LastToken; + + char *token = NULL; + do { + while (token == NULL && !CfgFiles.empty()) { + ConfigParser::CfgFile *wordfile = CfgFiles.top(); + token = wordfile->parse(&Quoted); + if (!token) { + assert(!wordfile->isOpen()); + CfgFiles.pop(); + delete wordfile; + } + } + + if (!token) + token = NextElement(&Quoted); + + if (token && strncmp("file:", token, 5) == 0 && !Quoted) { + char *path = token + 5; + if (*path != '"') { + debugs(3, DBG_CRITICAL, "Quoted filename missing: " << token); + self_destruct(); + return NULL; + } + char *end; + path = UnQuote(path, &end); + *end = '\0'; + 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; + } + } while(token == NULL && !CfgFiles.empty()); + + return (LastToken = token); +} + +char * +ConfigParser::NextQuotedOrToEol() +{ + char *token; + + if ((token = CfgPos) == NULL) { self_destruct(); + return NULL; } - strtok(s-1, "\""); /*Reset the strtok to point after the " */ - *s = '\0'; + token += strspn(token, w_space); - var->reset(token+1); + 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; + } + + CfgPos = NULL; + 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); + if ((wordFile = fopen(path, "r")) == NULL) { + debugs(3, DBG_CRITICAL, "strtokFile: " << path << " not found"); + return false; + } + +#if _SQUID_WINDOWS_ + setmode(fileno(wordFile), O_TEXT); +#endif + + return getFileLine(); +} + +bool +ConfigParser::CfgFile::getFileLine() +{ + // Else get the next line + if (fgets(parseLine, CONFIG_LINE_LIMIT, wordFile) == NULL) { + /* stop reading from file */ + fclose(wordFile); + wordFile = NULL; + parseLine[0] = '\0'; + return false; + } + parsePos = parseLine; + return true; +} + +char * +ConfigParser::CfgFile::parse(bool *wasQuoted) +{ + if (!wordFile) + return NULL; + + if (!*parseLine) + return NULL; + + char *token; + while(!(token = nextElement(wasQuoted))) { + if (!getFileLine()) + return NULL; + } + return token; +} + +char * +ConfigParser::CfgFile::nextElement(bool *wasQuoted) +{ + return TokenParse(parsePos, wasQuoted); +} === modified file 'src/ConfigParser.h' --- src/ConfigParser.h 2013-04-16 15:08:46 +0000 +++ src/ConfigParser.h 2013-05-23 19:24:10 +0000 @@ -19,74 +19,180 @@ * (at your option) any later version. * * 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 */ #ifndef SQUID_CONFIGPARSER_H #define SQUID_CONFIGPARSER_H #include "SquidString.h" #include +#include #if HAVE_STRING #include #endif class wordlist; /** * Limit to how long any given config line may be. * This affects squid.conf and all included files. * * Behaviour when setting larger than 2KB is unknown. * The config parser read mechanism can cope, but the other systems * receiving the data from its buffers on such lines may not. */ #define CONFIG_LINE_LIMIT 2048 +/// API for checking %macros found while parsing configuration options. +class MacroUser +{ +public: + /** + * Check if the %macro at the position pointed by token + * supported. + * Returns true if supported + */ + virtual bool supportedMacro(const char *token) = 0; + virtual ~MacroUser() {} +}; + /** * 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: void destruct(); static void ParseUShort(unsigned short *var); static void ParseBool(bool *var); - static void ParseString(char **var); - static void ParseString(String *var); - /// Parse an unquoted token (no spaces) or a "quoted string" that - /// may include spaces. In some contexts, quotes strings may also - /// include macros. Quoted strings may escape any character with - /// a backslash (\), which is currently only useful for inner - /// quotes. TODO: support quoted strings anywhere a token is accepted. - static void ParseQuotedString(char **var, bool *wasQuoted = NULL); - static void ParseQuotedString(String *var, bool *wasQuoted = NULL); 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 then understands the quoted tokens as filenames. + */ static char * strtokFile(); - static void strtokFileUndo(); - static void strtokFilePutBack(const char *); -private: - static char *lastToken; - static std::queue undo; + + /** + * 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(); + + /// Return true if the last parsed token was quoted + static bool LastTokenWasQuoted() {return Quoted;} + + /** + * Returns the next quoted string or the raw string data untill 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 of this function will return + * again the last parsed element. + */ + static void TokenUndo(); + + /** + * The next NextToken call will return the token as next element + */ + static void TokenPutBack(const char *token); + + /// Set the configuration file line to parse. + static void SetCfgLine(char *line); + + /// Set the macro user to user. If no macro user is set + /// the %macros inside quoted strings are not allowed. + static void SetMacroUser(MacroUser *user) {MacroUser_ = user;} + + /// configuration_includes_quoted_values in squid.conf + static int RecognizeQuotedValues; +protected: + /** + * Class used to store required information for the current + * configuration file. + */ + class CfgFile{ + public: + CfgFile(): wordFile(NULL), parsePos(NULL) { parseLine[0] = '\0';} + /// 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: + * read next line from file if required, and return the body of next + * element or NULL if there are not any new tokens in file. + */ + char *parse(bool *wasQuoted); + 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(bool *wasQuoted); + FILE *wordFile; ///< Pointer to the file. + char parseLine[CONFIG_LINE_LIMIT]; ///< The current line to parse + char *parsePos; ///< The next element position in parseLine string + }; + + /// Return the last undoed element, or NULL if none exist + static char *Undo(); + + /** + * Unquotes the token, which must be quoted. If the end is not NULL, + * it is set to the end of token. + */ + static char *UnQuote(char *token, char **end = NULL); + + /** + * Does the real tokens parsing job: Ignore comments, unquote an + * element if required. + * Returns NULL if there are no available tokens in nextToken string + * or the next token. + * The nextToken updated to point to the pos after parsed token. + */ + static char *TokenParse(char * &nextToken, bool *wasQuoted); + static char *NextElement(bool *wasQuoted); + static std::stack CfgFiles; ///< The stack of open cfg files + static bool Quoted; ///< True if the last parsed element was quoted + 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 undoed elements + static MacroUser *MacroUser_; }; int parseConfigFile(const char *file_name); #endif /* SQUID_CONFIGPARSER_H */ === modified file 'src/HelperChildConfig.cc' --- src/HelperChildConfig.cc 2013-01-25 01:26:21 +0000 +++ src/HelperChildConfig.cc 2013-02-21 14:11:34 +0000 @@ -1,22 +1,23 @@ #include "squid.h" #include "cache_cf.h" +#include "ConfigParser.h" #include "Debug.h" #include "HelperChildConfig.h" #include "globals.h" #include "Parsing.h" #include HelperChildConfig::HelperChildConfig(const unsigned int m): n_max(m), n_startup(0), n_idle(1), concurrency(0), n_running(0), n_active(0) {} HelperChildConfig & HelperChildConfig::updateLimits(const HelperChildConfig &rhs) { // Copy the limits only. @@ -27,55 +28,55 @@ concurrency = rhs.concurrency; return *this; } int HelperChildConfig::needNew() const { /* during the startup and reconfigure use our special amount... */ if (starting_up || reconfiguring) return n_startup; /* keep a minimum of n_idle helpers free... */ if ( (n_active + n_idle) < n_max) return n_idle; /* dont ever start more than n_max processes. */ return (n_max - n_active); } void HelperChildConfig::parseConfig() { - char const *token = strtok(NULL, w_space); + char const *token = ConfigParser::NextToken(); if (!token) self_destruct(); /* starts with a bare number for the max... back-compatible */ n_max = xatoui(token); if (n_max < 1) { debugs(0, DBG_CRITICAL, "ERROR: The maximum number of processes cannot be less than 1."); self_destruct(); } /* Parse extension options */ - for (; (token = strtok(NULL, w_space)) ;) { + for (; (token = ConfigParser::NextToken()) ;) { if (strncmp(token, "startup=", 8) == 0) { n_startup = xatoui(token + 8); } else if (strncmp(token, "idle=", 5) == 0) { n_idle = xatoui(token + 5); if (n_idle < 1) { debugs(0, DBG_CRITICAL, "WARNING OVERIDE: Using idle=0 for helpers causes request failures. Overiding to use idle=1 instead."); n_idle = 1; } } else if (strncmp(token, "concurrency=", 12) == 0) { concurrency = xatoui(token + 12); } else { debugs(0, DBG_PARSE_NOTE(DBG_IMPORTANT), "ERROR: Undefined option: " << token << "."); self_destruct(); } } /* simple sanity. */ if (n_startup > n_max) { debugs(0, DBG_CRITICAL, "WARNING OVERIDE: Capping startup=" << n_startup << " to the defined maximum (" << n_max <<")"); === modified file 'src/Notes.cc' --- src/Notes.cc 2013-04-30 00:13:26 +0000 +++ src/Notes.cc 2013-05-17 16:21:21 +0000 @@ -74,43 +74,42 @@ return NULL; } 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, value; - ConfigParser::ParseString(&key); - ConfigParser::ParseQuotedString(&value); + String key = ConfigParser::NextToken(); + String value = ConfigParser::NextToken(); Note::Pointer note = add(key); Note::Value::Pointer noteValue = note->addValue(value); aclParseAclList(parser, ¬eValue->aclList); 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; } void Notes::dump(StoreEntry *entry, const char *key) { === modified file 'src/Parsing.cc' --- src/Parsing.cc 2013-05-04 13:14:23 +0000 +++ src/Parsing.cc 2013-05-17 16:21:21 +0000 @@ -16,40 +16,41 @@ * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * 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. * */ #include "squid.h" #include "cache_cf.h" #include "compat/strtoll.h" +#include "ConfigParser.h" #include "Parsing.h" #include "globals.h" #include "Debug.h" /* * These functions is the same as atoi/l/f, except that they check for errors */ double xatof(const char *token) { char *end = NULL; double ret = strtod(token, &end); if (ret == 0 && end == token) { debugs(0, DBG_PARSE_NOTE(DBG_IMPORTANT), "ERROR: No digits were found in the input value '" << token << "'."); self_destruct(); } if (*end) { @@ -129,56 +130,56 @@ xatos(const char *token) { long port = xatol(token); if (port < 0) { debugs(0, DBG_PARSE_NOTE(DBG_IMPORTANT), "ERROR: The value '" << token << "' cannot be less than 0."); self_destruct(); } if (port & ~0xFFFF) { debugs(0, DBG_PARSE_NOTE(DBG_IMPORTANT), "ERROR: The value '" << token << "' is larger than the type 'short'."); self_destruct(); } return port; } int64_t GetInteger64(void) { - char *token = strtok(NULL, w_space); + char *token = ConfigParser::NextToken(); if (token == NULL) self_destruct(); return xatoll(token, 10); } /* * This function is different from others (e.g., GetInteger64, GetShort) * because it supports octal and hexadecimal numbers */ int GetInteger(void) { - char *token = strtok(NULL, w_space); + char *token = ConfigParser::NextToken(); int i; if (token == NULL) self_destruct(); // The conversion must honor 0 and 0x prefixes, which are important for things like umask int64_t ret = xatoll(token, 0); i = (int) ret; if (ret != static_cast(i)) { debugs(0, DBG_PARSE_NOTE(DBG_IMPORTANT), "ERROR: The value '" << token << "' is larger than the type 'int'."); self_destruct(); } return i; } /* * This function is similar as GetInteger() but the token might contain * the percentage symbol (%) and we check whether the value is in the range @@ -198,41 +199,41 @@ //if there is a % in the end of the digits, we remove it and go on. char* end = &token[strlen(token)-1]; if (*end == '%') { *end = '\0'; } p = xatoi(token); if (p < 0 || p > 100) { debugs(0, DBG_PARSE_NOTE(DBG_IMPORTANT), "ERROR: The value '" << token << "' is out of range. A percentage should be within [0, 100]."); self_destruct(); } return p; } unsigned short GetShort(void) { - char *token = strtok(NULL, w_space); + char *token = ConfigParser::NextToken(); if (token == NULL) self_destruct(); return xatos(token); } bool StringToInt(const char *s, int &result, const char **p, int base) { if (s) { char *ptr = 0; const int h = (int) strtol(s, &ptr, base); if (ptr != s && ptr) { result = h; if (p) *p = ptr; === modified file 'src/SwapDir.cc' --- src/SwapDir.cc 2013-02-09 00:44:07 +0000 +++ src/SwapDir.cc 2013-02-21 14:11:34 +0000 @@ -17,40 +17,41 @@ * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * 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. * */ #include "squid.h" #include "cache_cf.h" #include "compat/strtoll.h" #include "ConfigOption.h" +#include "ConfigParser.h" #include "globals.h" #include "Parsing.h" #include "SquidConfig.h" #include "StoreFileSystem.h" #include "SwapDir.h" #include "tools.h" SwapDir::SwapDir(char const *aType): theType(aType), max_size(0), min_objsize(0), max_objsize (-1), path(NULL), index(-1), disker(-1), repl(NULL), removals(0), scanned(0), cleanLog(NULL) { fs.blksize = 1024; } SwapDir::~SwapDir() { // TODO: should we delete repl? xfree(path); @@ -259,41 +260,41 @@ /* NOT performance critical. Really. Don't bother optimising for speed * - RBC 20030718 */ ConfigOption * SwapDir::getOptionTree() const { ConfigOptionVector *result = new ConfigOptionVector; result->options.push_back(new ConfigOptionAdapter(*const_cast(this), &SwapDir::optionReadOnlyParse, &SwapDir::optionReadOnlyDump)); result->options.push_back(new ConfigOptionAdapter(*const_cast(this), &SwapDir::optionObjectSizeParse, &SwapDir::optionObjectSizeDump)); return result; } void SwapDir::parseOptions(int isaReconfig) { const bool old_read_only = flags.read_only; char *name, *value; ConfigOption *newOption = getOptionTree(); - while ((name = strtok(NULL, w_space)) != NULL) { + while ((name = ConfigParser::NextToken()) != NULL) { value = strchr(name, '='); if (value) { *value = '\0'; /* cut on = */ ++value; } debugs(3,2, "SwapDir::parseOptions: parsing store option '" << name << "'='" << (value ? value : "") << "'"); if (newOption) if (!newOption->parse(name, value, isaReconfig)) self_destruct(); } delete newOption; /* * Handle notifications about reconfigured single-options with no value * where the removal of the option cannot be easily detected in the * parsing... === modified file 'src/acl/Acl.cc' --- src/acl/Acl.cc 2013-05-05 08:38:06 +0000 +++ src/acl/Acl.cc 2013-05-22 16:39:42 +0000 @@ -56,44 +56,44 @@ { char *nextToken; while ((nextToken = ConfigParser::strtokFile()) != NULL && nextToken[0] == '-') { //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::strtokFilePutBack("-i"); + ConfigParser::TokenPutBack("-i"); if (nextToken != NULL && strcmp(nextToken, "--") != 0 ) - ConfigParser::strtokFileUndo(); + 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; } @@ -142,58 +142,58 @@ { *name = 0; } bool ACL::valid () const { return true; } void ACL::ParseAclLine(ConfigParser &parser, ACL ** head) { /* we're already using strtok() to grok the line */ char *t = NULL; ACL *A = NULL; LOCAL_ARRAY(char, aclname, ACL_NAME_SZ); int new_acl = 0; /* snarf the ACL name */ - if ((t = strtok(NULL, w_space)) == NULL) { + if ((t = ConfigParser::NextToken()) == NULL) { debugs(28, DBG_CRITICAL, "aclParseAclLine: missing ACL name."); parser.destruct(); return; } if (strlen(t) >= ACL_NAME_SZ) { debugs(28, DBG_CRITICAL, "aclParseAclLine: aclParseAclLine: ACL name '" << t << "' too long, max " << ACL_NAME_SZ - 1 << " characters supported"); parser.destruct(); return; } xstrncpy(aclname, t, ACL_NAME_SZ); /* snarf the ACL type */ const char *theType; - if ((theType = strtok(NULL, w_space)) == NULL) { + if ((theType = ConfigParser::NextToken()) == NULL) { debugs(28, DBG_CRITICAL, "aclParseAclLine: missing ACL type."); parser.destruct(); return; } // Is this ACL going to work? if (strcmp(theType, "myip") == 0) { AnyP::PortCfg *p = Config.Sockaddr.http; while (p) { // Bug 3239: not reliable when there is interception traffic coming if (p->flags.natIntercept) debugs(28, DBG_CRITICAL, "WARNING: 'myip' ACL is not reliable for interception proxies. Please use 'myportname' instead."); p = p->next; } debugs(28, DBG_IMPORTANT, "UPGRADE: ACL 'myip' type is has been renamed to 'localip' and matches the IP the client connected to."); theType = "localip"; } else if (strcmp(theType, "myport") == 0) { AnyP::PortCfg *p = Config.Sockaddr.http; while (p) { // Bug 3239: not reliable when there is interception traffic coming === modified file 'src/acl/Gadgets.cc' --- src/acl/Gadgets.cc 2013-05-11 20:59:44 +0000 +++ src/acl/Gadgets.cc 2013-05-17 18:21:03 +0000 @@ -102,84 +102,84 @@ * get the info for redirecting "access denied" to info pages * TODO (probably ;-) * currently there is no optimization for * - more than one deny_info line with the same url * - a check, whether the given acl really is defined * - a check, whether an acl is added more than once for the same url */ void aclParseDenyInfoLine(AclDenyInfoList ** head) { char *t = NULL; AclDenyInfoList *A = NULL; AclDenyInfoList *B = NULL; AclDenyInfoList **T = NULL; AclNameList *L = NULL; AclNameList **Tail = NULL; /* first expect a page name */ - if ((t = strtok(NULL, w_space)) == NULL) { + if ((t = ConfigParser::NextToken()) == NULL) { debugs(28, DBG_CRITICAL, "aclParseDenyInfoLine: " << cfg_filename << " line " << config_lineno << ": " << config_input_line); debugs(28, DBG_CRITICAL, "aclParseDenyInfoLine: missing 'error page' parameter."); return; } A = (AclDenyInfoList *)memAllocate(MEM_ACL_DENY_INFO_LIST); A->err_page_id = errorReservePageId(t); A->err_page_name = xstrdup(t); A->next = (AclDenyInfoList *) NULL; /* next expect a list of ACL names */ Tail = &A->acl_list; - while ((t = strtok(NULL, w_space))) { + while ((t = ConfigParser::NextToken())) { L = (AclNameList *)memAllocate(MEM_ACL_NAME_LIST); xstrncpy(L->name, t, ACL_NAME_SZ); *Tail = L; Tail = &L->next; } if (A->acl_list == NULL) { debugs(28, DBG_CRITICAL, "aclParseDenyInfoLine: " << cfg_filename << " line " << config_lineno << ": " << config_input_line); debugs(28, DBG_CRITICAL, "aclParseDenyInfoLine: deny_info line contains no ACL's, skipping"); memFree(A, MEM_ACL_DENY_INFO_LIST); return; } for (B = *head, T = head; B; T = &B->next, B = B->next) ; /* find the tail */ *T = A; } void aclParseAccessLine(ConfigParser &parser, acl_access ** head) { char *t = NULL; acl_access *A = NULL; acl_access *B = NULL; acl_access **T = NULL; /* first expect either 'allow' or 'deny' */ - if ((t = strtok(NULL, w_space)) == NULL) { + if ((t = ConfigParser::NextToken()) == NULL) { debugs(28, DBG_CRITICAL, "aclParseAccessLine: " << cfg_filename << " line " << config_lineno << ": " << config_input_line); debugs(28, DBG_CRITICAL, "aclParseAccessLine: missing 'allow' or 'deny'."); return; } A = new acl_access; if (!strcmp(t, "allow")) A->allow = ACCESS_ALLOWED; else if (!strcmp(t, "deny")) A->allow = ACCESS_DENIED; else { debugs(28, DBG_CRITICAL, "aclParseAccessLine: " << cfg_filename << " line " << config_lineno << ": " << config_input_line); debugs(28, DBG_CRITICAL, "aclParseAccessLine: expecting 'allow' or 'deny', got '" << t << "'."); delete A; return; } aclParseAclList(parser, &A->aclList); === modified file 'src/adaptation/Config.cc' --- src/adaptation/Config.cc 2013-05-04 11:50:26 +0000 +++ src/adaptation/Config.cc 2013-05-17 16:21:21 +0000 @@ -247,42 +247,41 @@ void Adaptation::Config::FreeServiceGroups() { while (!AllGroups().empty()) { // groups are refcounted so we do not explicitly delete them AllGroups().pop_back(); } } void Adaptation::Config::DumpServiceGroups(StoreEntry *entry, const char *name) { typedef Groups::iterator GI; for (GI i = AllGroups().begin(); i != AllGroups().end(); ++i) storeAppendPrintf(entry, "%s " SQUIDSTRINGPH "\n", name, SQUIDSTRINGPRINT((*i)->id)); } void Adaptation::Config::ParseAccess(ConfigParser &parser) { - String groupId; - ConfigParser::ParseString(&groupId); + String groupId = ConfigParser::NextToken(); AccessRule *r; if (!(r=FindRuleByGroupId(groupId))) { r = new AccessRule(groupId); AllRules().push_back(r); } r->parse(parser); } void Adaptation::Config::FreeAccess() { while (!AllRules().empty()) { delete AllRules().back(); AllRules().pop_back(); } } void Adaptation::Config::DumpAccess(StoreEntry *entry, const char *name) { === modified file 'src/adaptation/ServiceConfig.cc' --- src/adaptation/ServiceConfig.cc 2013-03-03 07:10:22 +0000 +++ src/adaptation/ServiceConfig.cc 2013-05-17 16:21:21 +0000 @@ -44,56 +44,54 @@ Adaptation::ServiceConfig::parseVectPoint(const char *service_configConfig) const { const char *t = service_configConfig; const char *q = strchr(t, '_'); if (q) t = q + 1; if (!strcmp(t, "precache")) return Adaptation::pointPreCache; if (!strcmp(t, "postcache")) return Adaptation::pointPostCache; return Adaptation::pointNone; } bool Adaptation::ServiceConfig::parse() { - String method_point; - - ConfigParser::ParseString(&key); - ConfigParser::ParseString(&method_point); + key = ConfigParser::NextToken(); + String method_point = ConfigParser::NextToken(); method = parseMethod(method_point.termedBuf()); point = parseVectPoint(method_point.termedBuf()); // reset optional parameters in case we are reconfiguring bypass = routing = false; // handle optional service name=value parameters bool grokkedUri = false; bool onOverloadSet = false; std::set options; - while (char *option = strtok(NULL, w_space)) { + while (char *option = ConfigParser::NextToken()) { const char *name = option; const char *value = ""; if (strcmp(option, "0") == 0) { // backward compatibility name = "bypass"; value = "off"; debugs(3, DBG_PARSE_NOTE(DBG_IMPORTANT), "UPGRADE: Please use 'bypass=off' option to disable service bypass"); } else if (strcmp(option, "1") == 0) { // backward compatibility name = "bypass"; value = "on"; debugs(3, DBG_PARSE_NOTE(DBG_IMPORTANT), "UPGRADE: Please use 'bypass=on' option to enable service bypass"); } else { char *eq = strstr(option, "="); const char *sffx = strstr(option, "://"); if (!eq || (sffx && sffx < eq)) { //no "=" or has the form "icap://host?arg=val" name = "uri"; value = option; } else { // a normal name=value option *eq = '\0'; // terminate option name value = eq + 1; // skip '=' } === modified file 'src/adaptation/ServiceGroups.cc' --- src/adaptation/ServiceGroups.cc 2012-08-31 16:57:39 +0000 +++ src/adaptation/ServiceGroups.cc 2013-02-05 16:36:07 +0000 @@ -6,41 +6,41 @@ #include "adaptation/ServiceFilter.h" #include "adaptation/ServiceGroups.h" #include "ConfigParser.h" #include "Debug.h" #include "StrList.h" #include "wordlist.h" Adaptation::ServiceGroup::ServiceGroup(const String &aKind, bool allSame): kind(aKind), method(methodNone), point(pointNone), allServicesSame(allSame) { } Adaptation::ServiceGroup::~ServiceGroup() { } void Adaptation::ServiceGroup::parse() { - ConfigParser::ParseString(&id); + id = ConfigParser::NextToken(); wordlist *names = NULL; ConfigParser::ParseWordList(&names); for (wordlist *i = names; i; i = i->next) services.push_back(i->key); wordlistDestroy(&names); } // Note: configuration code aside, this method is called by DynamicServiceChain void Adaptation::ServiceGroup::finalize() { // 1) warn if services have different methods or vectoring point // 2) warn if all-same services have different bypass status // 3) warn if there are seemingly identical services in the group // TODO: optimize by remembering ServicePointers rather than IDs if (!removedServices.empty()) { String s; for (Store::iterator it = removedServices.begin(); it != removedServices.end(); ++it) { s.append(*it); === modified file 'src/cache_cf.cc' --- src/cache_cf.cc 2013-05-14 18:36:45 +0000 +++ src/cache_cf.cc 2013-05-23 18:12:58 +0000 @@ -998,50 +998,50 @@ int cval; parse_int(&cval); debugs(3, DBG_CRITICAL, "WARNING: url_rewrite_concurrency upgrade overriding url_rewrite_children settings."); Config.redirectChildren.concurrency = cval; } } /* Parse a time specification from the config file. Store the * result in 'tptr', after converting it to 'units' */ static void parseTimeLine(time_msec_t * tptr, const char *units, bool allowMsec) { char *token; double d; time_msec_t m; time_msec_t u; if ((u = parseTimeUnits(units, allowMsec)) == 0) self_destruct(); - if ((token = strtok(NULL, w_space)) == NULL) + if ((token = ConfigParser::NextToken()) == NULL) self_destruct(); d = xatof(token); m = u; /* default to 'units' if none specified */ if (0 == d) (void) 0; - else if ((token = strtok(NULL, w_space)) == NULL) + else if ((token = ConfigParser::NextToken()) == NULL) debugs(3, DBG_CRITICAL, "WARNING: No units on '" << config_input_line << "', assuming " << d << " " << units ); else if ((m = parseTimeUnits(token, allowMsec)) == 0) self_destruct(); *tptr = static_cast(m * d); if (static_cast(*tptr) * 2 != m * d * 2) { debugs(3, DBG_CRITICAL, "ERROR: Invalid value '" << d << " " << token << ": integer overflow (time_msec_t)."); self_destruct(); } } static uint64_t parseTimeUnits(const char *unit, bool allowMsec) { if (allowMsec && !strncasecmp(unit, T_MILLISECOND_STR, strlen(T_MILLISECOND_STR))) return 1; @@ -1074,152 +1074,152 @@ return static_cast(86400 * 1000 * 365.2522 * 10); debugs(3, DBG_IMPORTANT, "parseTimeUnits: unknown time unit '" << unit << "'"); return 0; } static void parseBytesLine64(int64_t * bptr, const char *units) { char *token; double d; int64_t m; int64_t u; if ((u = parseBytesUnits(units)) == 0) { self_destruct(); return; } - if ((token = strtok(NULL, w_space)) == NULL) { + if ((token = ConfigParser::NextToken()) == NULL) { self_destruct(); return; } if (strcmp(token, "none") == 0 || strcmp(token, "-1") == 0) { *bptr = -1; return; } d = xatof(token); m = u; /* default to 'units' if none specified */ if (0.0 == d) (void) 0; - else if ((token = strtok(NULL, w_space)) == NULL) + else if ((token = ConfigParser::NextToken()) == NULL) debugs(3, DBG_CRITICAL, "WARNING: No units on '" << config_input_line << "', assuming " << d << " " << units ); else if ((m = parseBytesUnits(token)) == 0) { self_destruct(); return; } *bptr = static_cast(m * d / u); if (static_cast(*bptr) * 2 != (m * d / u) * 2) { debugs(3, DBG_CRITICAL, "ERROR: Invalid value '" << d << " " << token << ": integer overflow (int64_t)."); self_destruct(); } } static void parseBytesLine(size_t * bptr, const char *units) { char *token; double d; int m; int u; if ((u = parseBytesUnits(units)) == 0) { self_destruct(); return; } - if ((token = strtok(NULL, w_space)) == NULL) { + if ((token = ConfigParser::NextToken()) == NULL) { self_destruct(); return; } if (strcmp(token, "none") == 0 || strcmp(token, "-1") == 0) { *bptr = static_cast(-1); return; } d = xatof(token); m = u; /* default to 'units' if none specified */ if (0.0 == d) (void) 0; - else if ((token = strtok(NULL, w_space)) == NULL) + else if ((token = ConfigParser::NextToken()) == NULL) debugs(3, DBG_CRITICAL, "WARNING: No units on '" << config_input_line << "', assuming " << d << " " << units ); else if ((m = parseBytesUnits(token)) == 0) { self_destruct(); return; } *bptr = static_cast(m * d / u); if (static_cast(*bptr) * 2 != (m * d / u) * 2) { debugs(3, DBG_CRITICAL, "ERROR: Invalid value '" << d << " " << token << ": integer overflow (size_t)."); self_destruct(); } } #if !USE_DNSHELPER static void parseBytesLineSigned(ssize_t * bptr, const char *units) { char *token; double d; int m; int u; if ((u = parseBytesUnits(units)) == 0) { self_destruct(); return; } - if ((token = strtok(NULL, w_space)) == NULL) { + if ((token = ConfigParser::NextToken()) == NULL) { self_destruct(); return; } if (strcmp(token, "none") == 0 || token[0] == '-' /* -N */) { *bptr = -1; return; } d = xatof(token); m = u; /* default to 'units' if none specified */ if (0.0 == d) (void) 0; - else if ((token = strtok(NULL, w_space)) == NULL) + else if ((token = ConfigParser::NextToken()) == NULL) debugs(3, DBG_CRITICAL, "WARNING: No units on '" << config_input_line << "', assuming " << d << " " << units ); else if ((m = parseBytesUnits(token)) == 0) { self_destruct(); return; } *bptr = static_cast(m * d / u); if (static_cast(*bptr) * 2 != (m * d / u) * 2) { debugs(3, DBG_CRITICAL, "ERROR: Invalid value '" << d << " " << token << ": integer overflow (ssize_t)."); self_destruct(); } } #endif /** * Parse bytes from a string. @@ -1281,40 +1281,41 @@ * Max *****************************************************************************/ static void dump_acl(StoreEntry * entry, const char *name, ACL * ae) { wordlist *w; wordlist *v; while (ae != NULL) { debugs(3, 3, "dump_acl: " << name << " " << ae->name); storeAppendPrintf(entry, "%s %s %s %s ", name, ae->name, ae->typeString(), ae->flags.flagsStr()); v = w = ae->dump(); while (v != NULL) { debugs(3, 3, "dump_acl: " << name << " " << ae->name << " " << v->key); + // XXX: use something like ConfigParser::QuoteString() here storeAppendPrintf(entry, "%s ", v->key); v = v->next; } storeAppendPrintf(entry, "\n"); wordlistDestroy(&w); ae = ae->next; } } static void parse_acl(ACL ** ae) { ACL::ParseAclLine(LegacyParser, ae); } static void free_acl(ACL ** ae) { aclDestroyAcls(ae); @@ -1351,41 +1352,41 @@ { aclParseAccessLine(LegacyParser, head); } static void free_acl_access(acl_access ** head) { aclDestroyAccessList(head); } static void dump_address(StoreEntry * entry, const char *name, Ip::Address &addr) { char buf[MAX_IPSTRLEN]; storeAppendPrintf(entry, "%s %s\n", name, addr.NtoA(buf,MAX_IPSTRLEN) ); } static void parse_address(Ip::Address *addr) { - char *token = strtok(NULL, w_space); + char *token = ConfigParser::NextToken(); if (!token) { self_destruct(); return; } if (!strcmp(token,"any_addr")) addr->SetAnyAddr(); else if ( (!strcmp(token,"no_addr")) || (!strcmp(token,"full_mask")) ) addr->SetNoAddr(); else if ( (*addr = token) ) // try parse numeric/IPA (void) 0; else addr->GetHostByName(token); // dont use ipcache } static void free_address(Ip::Address *addr) { addr->SetEmpty(); @@ -1459,41 +1460,41 @@ dump_acl_list(entry, l->aclList); storeAppendPrintf(entry, "\n"); } } static void freed_acl_tos(void *data) { acl_tos *l = static_cast(data); aclDestroyAclList(&l->aclList); } static void parse_acl_tos(acl_tos ** head) { acl_tos *l; acl_tos **tail = head; /* sane name below */ unsigned int tos; /* Initially uint for strtoui. Casted to tos_t before return */ - char *token = strtok(NULL, w_space); + char *token = ConfigParser::NextToken(); if (!token) { self_destruct(); return; } if (!xstrtoui(token, NULL, &tos, 0, std::numeric_limits::max())) { self_destruct(); return; } CBDATA_INIT_TYPE_FREECB(acl_tos, freed_acl_tos); l = cbdataAlloc(acl_tos); l->tos = (tos_t)tos; aclParseAclList(LegacyParser, &l->aclList); while (*tail) @@ -1530,41 +1531,41 @@ dump_acl_list(entry, l->aclList); storeAppendPrintf(entry, "\n"); } } static void freed_acl_nfmark(void *data) { acl_nfmark *l = static_cast(data); aclDestroyAclList(&l->aclList); } static void parse_acl_nfmark(acl_nfmark ** head) { acl_nfmark *l; acl_nfmark **tail = head; /* sane name below */ nfmark_t mark; - char *token = strtok(NULL, w_space); + char *token = ConfigParser::NextToken(); if (!token) { self_destruct(); return; } if (!xstrtoui(token, NULL, &mark, 0, std::numeric_limits::max())) { self_destruct(); return; } CBDATA_INIT_TYPE_FREECB(acl_nfmark, freed_acl_nfmark); l = cbdataAlloc(acl_nfmark); l->nfmark = mark; aclParseAclList(LegacyParser, &l->aclList); while (*tail) @@ -1732,77 +1733,77 @@ static void parse_client_delay_pool_access(ClientDelayConfig * cfg) { cfg->parsePoolAccess(LegacyParser); } #endif #if USE_HTTP_VIOLATIONS static void dump_http_header_access(StoreEntry * entry, const char *name, const HeaderManglers *manglers) { if (manglers) manglers->dumpAccess(entry, name); } static void parse_http_header_access(HeaderManglers **pm) { char *t = NULL; - if ((t = strtok(NULL, w_space)) == 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_access: missing header name."); return; } if (!*pm) *pm = new HeaderManglers; HeaderManglers *manglers = *pm; headerMangler *mangler = manglers->track(t); assert(mangler); parse_acl_access(&mangler->access_list); } static void free_HeaderManglers(HeaderManglers **pm) { // we delete the entire http_header_* mangler configuration at once if (const HeaderManglers *manglers = *pm) { delete manglers; *pm = NULL; } } 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 = strtok(NULL, w_space)) == 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; 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; @@ -1813,44 +1814,44 @@ if (!s) continue; storeAppendPrintf(entry, "%s %s %s", name, s->type(), s->path); s->dump(*entry); storeAppendPrintf(entry, "\n"); } } static int check_null_string(char *s) { return s == NULL; } #if USE_AUTH static void parse_authparam(Auth::ConfigVector * config) { char *type_str; char *param_str; - if ((type_str = strtok(NULL, w_space)) == NULL) + if ((type_str = ConfigParser::NextToken()) == NULL) self_destruct(); - if ((param_str = strtok(NULL, w_space)) == NULL) + if ((param_str = ConfigParser::NextToken()) == NULL) self_destruct(); /* find a configuration for the scheme in the currently parsed configs... */ Auth::Config *schemeCfg = Auth::Config::Find(type_str); if (schemeCfg == NULL) { /* Create a configuration based on the scheme info */ Auth::Scheme::Pointer theScheme = Auth::Scheme::Find(type_str); if (theScheme == NULL) { debugs(3, DBG_CRITICAL, "Parsing Config File: Unknown authentication scheme '" << type_str << "'."); self_destruct(); } config->push_back(theScheme->createConfig()); schemeCfg = Auth::Config::Find(type_str); if (schemeCfg == NULL) { debugs(3, DBG_CRITICAL, "Parsing Config File: Corruption configuring authentication scheme '" << type_str << "'."); self_destruct(); } @@ -1887,44 +1888,44 @@ /* TODO: just return the object, the # is irrelevant */ static int find_fstype(char *type) { for (size_t i = 0; i < StoreFileSystem::FileSystems().size(); ++i) if (strcasecmp(type, StoreFileSystem::FileSystems().items[i]->type()) == 0) return (int)i; return (-1); } static void parse_cachedir(SquidConfig::_cacheSwap * swap) { char *type_str; char *path_str; RefCount sd; int i; int fs; - if ((type_str = strtok(NULL, w_space)) == NULL) + if ((type_str = ConfigParser::NextToken()) == NULL) self_destruct(); - if ((path_str = strtok(NULL, w_space)) == NULL) + if ((path_str = ConfigParser::NextToken()) == NULL) self_destruct(); fs = find_fstype(type_str); if (fs < 0) { debugs(3, DBG_PARSE_NOTE(DBG_IMPORTANT), "ERROR: This proxy does not support the '" << type_str << "' cache type. Ignoring."); return; } /* reconfigure existing dir */ for (i = 0; i < swap->n_configured; ++i) { assert (swap->swapDirs[i].getRaw()); if ((strcasecmp(path_str, dynamic_cast(swap->swapDirs[i].getRaw())->path)) == 0) { /* this is specific to on-fs Stores. The right * way to handle this is probably to have a mapping * from paths to stores, and have on-fs stores * register with that, and lookip in that in their * own setup logic. RBC 20041225. TODO. @@ -2045,41 +2046,41 @@ isUnsignedNumeric(const char *str, size_t len) { if (len < 1) return false; for (; len >0 && *str; ++str, --len) { if (! isdigit(*str)) return false; } return true; } /** \param proto 'tcp' or 'udp' for protocol \returns Port the named service is supposed to be listening on. */ static unsigned short GetService(const char *proto) { struct servent *port = NULL; /** Parses a port number or service name from the squid.conf */ - char *token = strtok(NULL, w_space); + char *token = ConfigParser::NextToken(); if (token == NULL) { self_destruct(); return 0; /* NEVER REACHED */ } /** Returns either the service port number from /etc/services */ if ( !isUnsignedNumeric(token, strlen(token)) ) port = getservbyname(token, proto); if (port != NULL) { return ntohs((unsigned short)port->s_port); } /** Or a numeric translation of the config text. */ return xatos(token); } /** \returns Port the named TCP service is supposed to be listening on. \copydoc GetService(const char *proto) */ inline unsigned short GetTcpService(void) @@ -2093,66 +2094,66 @@ */ inline unsigned short GetUdpService(void) { return GetService("udp"); } static void parse_peer(CachePeer ** head) { char *token = NULL; CachePeer *p; CBDATA_INIT_TYPE_FREECB(CachePeer, peerDestroy); p = cbdataAlloc(CachePeer); p->http_port = CACHE_HTTP_PORT; p->icp.port = CACHE_ICP_PORT; p->weight = 1; p->basetime = 0; p->stats.logged_state = PEER_ALIVE; - if ((token = strtok(NULL, w_space)) == NULL) + if ((token = ConfigParser::NextToken()) == NULL) self_destruct(); p->host = xstrdup(token); p->name = xstrdup(token); - if ((token = strtok(NULL, w_space)) == NULL) + if ((token = ConfigParser::NextToken()) == NULL) self_destruct(); p->type = parseNeighborType(token); if (p->type == PEER_MULTICAST) { p->options.no_digest = true; p->options.no_netdb_exchange = true; } p->http_port = GetTcpService(); if (!p->http_port) self_destruct(); p->icp.port = GetUdpService(); p->connection_auth = 2; /* auto */ - while ((token = strtok(NULL, w_space))) { + while ((token = ConfigParser::NextToken())) { if (!strcmp(token, "proxy-only")) { p->options.proxy_only = true; } else if (!strcmp(token, "no-query")) { p->options.no_query = true; } else if (!strcmp(token, "background-ping")) { p->options.background_ping = true; } else if (!strcmp(token, "no-digest")) { p->options.no_digest = true; } else if (!strcmp(token, "no-tproxy")) { p->options.no_tproxy = true; } else if (!strcmp(token, "multicast-responder")) { p->options.mcast_responder = true; #if PEER_MULTICAST_SIBLINGS } else if (!strcmp(token, "multicast-siblings")) { p->options.mcast_siblings = true; #endif } else if (!strncmp(token, "weight=", 7)) { p->weight = xatoi(token + 7); } else if (!strncmp(token, "basetime=", 9)) { p->basetime = xatoi(token + 9); @@ -2502,99 +2503,99 @@ for (l = a->acl_list; l; l = l_next) { l_next = l->next; memFree(l, MEM_ACL_NAME_LIST); l = NULL; } a_next = a->next; memFree(a, MEM_ACL_DENY_INFO_LIST); a = NULL; } *list = NULL; } static void parse_peer_access(void) { char *host = NULL; CachePeer *p; - if (!(host = strtok(NULL, w_space))) + if (!(host = ConfigParser::NextToken())) self_destruct(); if ((p = peerFindByName(host)) == NULL) { debugs(15, DBG_CRITICAL, "" << cfg_filename << ", line " << config_lineno << ": No cache_peer '" << host << "'"); return; } aclParseAccessLine(LegacyParser, &p->access); } static void parse_hostdomain(void) { char *host = NULL; char *domain = NULL; - if (!(host = strtok(NULL, w_space))) + if (!(host = ConfigParser::NextToken())) self_destruct(); - while ((domain = strtok(NULL, list_sep))) { + while ((domain = ConfigParser::NextToken())) { CachePeerDomainList *l = NULL; CachePeerDomainList **L = NULL; CachePeer *p; if ((p = peerFindByName(host)) == NULL) { debugs(15, DBG_CRITICAL, "" << cfg_filename << ", line " << config_lineno << ": No cache_peer '" << host << "'"); continue; } l = static_cast(xcalloc(1, sizeof(CachePeerDomainList))); l->do_ping = true; if (*domain == '!') { /* check for !.edu */ l->do_ping = false; ++domain; } l->domain = xstrdup(domain); for (L = &(p->peer_domain); *L; L = &((*L)->next)); *L = l; } } static void parse_hostdomaintype(void) { char *host = NULL; char *type = NULL; char *domain = NULL; - if (!(host = strtok(NULL, w_space))) + if (!(host = ConfigParser::NextToken())) self_destruct(); - if (!(type = strtok(NULL, w_space))) + if (!(type = ConfigParser::NextToken())) self_destruct(); - while ((domain = strtok(NULL, list_sep))) { + while ((domain = ConfigParser::NextToken())) { NeighborTypeDomainList *l = NULL; NeighborTypeDomainList **L = NULL; CachePeer *p; if ((p = peerFindByName(host)) == NULL) { debugs(15, DBG_CRITICAL, "" << cfg_filename << ", line " << config_lineno << ": No cache_peer '" << host << "'"); return; } l = static_cast(xcalloc(1, sizeof(NeighborTypeDomainList))); l->type = parseNeighborType(type); l->domain = xstrdup(domain); for (L = &(p->typelist); *L; L = &((*L)->next)); *L = l; } } static void dump_int(StoreEntry * entry, const char *name, int var) @@ -2608,82 +2609,82 @@ int i; i = GetInteger(); *var = i; } static void free_int(int *var) { *var = 0; } static void dump_onoff(StoreEntry * entry, const char *name, int var) { storeAppendPrintf(entry, "%s %s\n", name, var ? "on" : "off"); } void parse_onoff(int *var) { - char *token = strtok(NULL, w_space); + char *token = ConfigParser::NextToken(); if (token == NULL) self_destruct(); if (!strcmp(token, "on")) { *var = 1; } else if (!strcmp(token, "enable")) { debugs(0, DBG_PARSE_NOTE(DBG_IMPORTANT), "WARNING: 'enable' is deprecated. Please update to use 'on'."); *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 'off'."); *var = 0; } else { debugs(0, DBG_PARSE_NOTE(DBG_IMPORTANT), "ERROR: Invalid option: Boolean options can only be 'on' or 'off'."); self_destruct(); } } #define free_onoff free_int static void dump_tristate(StoreEntry * entry, const char *name, int var) { const char *state; if (var > 0) state = "on"; else if (var < 0) state = "warn"; else state = "off"; storeAppendPrintf(entry, "%s %s\n", name, state); } static void parse_tristate(int *var) { - char *token = strtok(NULL, w_space); + char *token = ConfigParser::NextToken(); if (token == NULL) self_destruct(); if (!strcmp(token, "on")) { *var = 1; } else if (!strcmp(token, "enable")) { 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(); } @@ -2759,51 +2760,51 @@ 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 = strtok(NULL, w_space)) == NULL) { + if ((token = ConfigParser::NextToken()) == NULL) { self_destruct(); return; } if (strcmp(token, "-i") == 0) { flags |= REG_ICASE; - token = strtok(NULL, w_space); + token = ConfigParser::NextToken(); } else if (strcmp(token, "+i") == 0) { flags &= ~REG_ICASE; - token = strtok(NULL, w_space); + token = ConfigParser::NextToken(); } if (token == NULL) { 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) { debugs(3, DBG_IMPORTANT, "WARNING: refresh_pattern minimum age too high. Cropped back to 1 year."); i = 60*24*365; } @@ -2812,41 +2813,41 @@ i = GetPercentage(); /* token: pct */ pct = (double) i / 100.0; i = GetInteger(); /* token: max */ /* catch negative and insanely huge values close to 32-bit wrap */ if (i < 0) { debugs(3, DBG_IMPORTANT, "WARNING: refresh_pattern maximum age negative. Cropped back to zero."); i = 0; } if (i > 60*24*365) { debugs(3, DBG_IMPORTANT, "WARNING: refresh_pattern maximum age too high. Cropped back to 1 year."); i = 60*24*365; } max = (time_t) (i * 60); /* convert minutes to seconds */ /* Options */ - while ((token = strtok(NULL, w_space)) != NULL) { + while ((token = ConfigParser::NextToken()) != NULL) { if (!strcmp(token, "refresh-ims")) { refresh_ims = 1; } else if (!strcmp(token, "store-stale")) { store_stale = 1; } else if (!strncmp(token, "max-stale=", 10)) { max_stale = xatoi(token + 10); #if USE_HTTP_VIOLATIONS } else if (!strcmp(token, "override-expire")) override_expire = 1; else if (!strcmp(token, "override-lastmod")) override_lastmod = 1; else if (!strcmp(token, "ignore-no-store")) ignore_no_store = 1; else if (!strcmp(token, "ignore-must-revalidate")) ignore_must_revalidate = 1; else if (!strcmp(token, "ignore-private")) ignore_private = 1; else if (!strcmp(token, "ignore-auth")) ignore_auth = 1; @@ -2943,81 +2944,64 @@ regfree(&t->compiled_pattern); safe_free(t); } #if USE_HTTP_VIOLATIONS refresh_nocache_hack = 0; #endif } static void dump_string(StoreEntry * entry, const char *name, char *var) { if (var != NULL) storeAppendPrintf(entry, "%s %s\n", name, var); } static void parse_string(char **var) { - char *token = strtok(NULL, w_space); + char *token = ConfigParser::NextToken(); safe_free(*var); if (token == NULL) self_destruct(); *var = xstrdup(token); } -void -ConfigParser::ParseString(char **var) -{ - parse_string(var); -} - -void -ConfigParser::ParseString(String *var) -{ - char *token = strtok(NULL, w_space); - - if (token == NULL) - self_destruct(); - - var->reset(token); -} - static void free_string(char **var) { safe_free(*var); } void parse_eol(char *volatile *var) { if (!var) { self_destruct(); return; } - unsigned char *token = (unsigned char *) strtok(NULL, null_string); + unsigned char *token = (unsigned char *) ConfigParser::NextQuotedOrToEol(); safe_free(*var); if (!token) { self_destruct(); return; } while (*token && xisspace(*token)) ++token; if (!*token) { self_destruct(); return; } *var = xstrdup((char *) token); } #define dump_eol dump_string #define free_eol free_string @@ -3217,68 +3201,66 @@ 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; - char *t = strtok(NULL, ""); - - while ((token = strwordtok(NULL, &t))) + while ((token = ConfigParser::NextToken())) 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 static void parse_uri_whitespace(int *var) { - char *token = strtok(NULL, w_space); + char *token = ConfigParser::NextToken(); if (token == NULL) self_destruct(); if (!strcmp(token, "strip")) *var = URI_WHITESPACE_STRIP; else if (!strcmp(token, "deny")) *var = URI_WHITESPACE_DENY; else if (!strcmp(token, "allow")) *var = URI_WHITESPACE_ALLOW; else if (!strcmp(token, "encode")) *var = URI_WHITESPACE_ENCODE; else if (!strcmp(token, "chop")) *var = URI_WHITESPACE_CHOP; else { debugs(0, DBG_PARSE_NOTE(2), "ERROR: Invalid option '" << token << "': 'uri_whitespace' accepts 'strip', 'deny', 'allow', 'encode', and 'chop'."); self_destruct(); } } @@ -3357,41 +3339,41 @@ parse_onoff(&value); option->configure(value > 0); } static void dump_YesNoNone(StoreEntry * entry, const char *name, YesNoNone &option) { if (option.configured()) dump_onoff(entry, name, option ? 1 : 0); } static void free_memcachemode(SquidConfig * config) { return; } static void parse_memcachemode(SquidConfig * config) { - char *token = strtok(NULL, w_space); + char *token = ConfigParser::NextToken(); if (!token) self_destruct(); if (strcmp(token, "always") == 0) { Config.onoff.memory_cache_first = 1; Config.onoff.memory_cache_disk = 1; } else if (strcmp(token, "disk") == 0) { Config.onoff.memory_cache_first = 0; Config.onoff.memory_cache_disk = 1; } else if (strncmp(token, "net", 3) == 0) { Config.onoff.memory_cache_first = 1; Config.onoff.memory_cache_disk = 0; } else if (strcmp(token, "never") == 0) { Config.onoff.memory_cache_first = 0; Config.onoff.memory_cache_disk = 0; } else { debugs(0, DBG_PARSE_NOTE(2), "ERROR: Invalid option '" << token << "': 'memory_cache_mode' accepts 'always', 'disk', 'network', and 'never'."); self_destruct(); } } @@ -3427,41 +3409,41 @@ if (!strcmp(s, "sibling")) return PEER_SIBLING; if (!strcmp(s, "multicast")) return PEER_MULTICAST; debugs(15, DBG_CRITICAL, "WARNING: Unknown neighbor type: " << s); return PEER_SIBLING; } #if USE_WCCPv2 static void parse_IpAddress_list(Ip::Address_list ** head) { char *token; Ip::Address_list *s; Ip::Address ipa; - while ((token = strtok(NULL, w_space))) { + while ((token = ConfigParser::NextToken())) { if (GetHostWithPort(token, &ipa)) { while (*head) head = &(*head)->next; s = static_cast(xcalloc(1, sizeof(*s))); s->s = ipa; *head = s; } else self_destruct(); } } static void dump_IpAddress_list(StoreEntry * e, const char *n, const Ip::Address_list * s) { char ntoabuf[MAX_IPSTRLEN]; while (s) { @@ -3776,52 +3758,52 @@ assert(s->next == NULL); s->next = cbdataReference(Config.Sockaddr.http); cbdataReferenceDone(Config.Sockaddr.http); Config.Sockaddr.http = cbdataReference(s); } static void parsePortCfg(AnyP::PortCfg ** head, const char *optionName) { const char *protocol = NULL; if (strcmp(optionName, "http_port") == 0 || strcmp(optionName, "ascii_port") == 0) protocol = "http"; else if (strcmp(optionName, "https_port") == 0) protocol = "https"; if (!protocol) { self_destruct(); return; } - char *token = strtok(NULL, w_space); + char *token = ConfigParser::NextToken(); if (!token) { self_destruct(); return; } AnyP::PortCfg *s = new AnyP::PortCfg(protocol); parsePortSpecification(s, token); /* parse options ... */ - while ((token = strtok(NULL, w_space))) { + while ((token = ConfigParser::NextToken())) { parse_port_option(s, token); } #if USE_SSL if (strcmp(protocol, "https") == 0) { /* ssl-bump on https_port configuration requires either tproxy or intercept, and vice versa */ const bool hijacked = s->flags.isIntercepted(); if (s->flags.tunnelSslBumping && !hijacked) { debugs(3, DBG_CRITICAL, "FATAL: ssl-bump on https_port requires tproxy/intercept which is missing."); self_destruct(); } if (hijacked && !s->flags.tunnelSslBumping) { debugs(3, DBG_CRITICAL, "FATAL: tproxy/intercept on https_port requires ssl-bump which is missing."); self_destruct(); } } #endif if (Ip::EnableIpv6&IPV6_SPECIAL_SPLITSTACK && s->s.IsAnyAddr()) { // clone the port options from *s to *(s->next) @@ -4035,85 +4017,85 @@ * If no explicit logformat name is given, the first ACL name, if any, * should not be an existing logformat name or it will be treated as such. * access_log module:place [logformat_name] [acl ...] * * #4: Configurable logging module with name=value options such as logformat=x: * The first ACL name may not contain '='. * access_log module:place [option ...] [acl ...] * */ static void parse_access_log(CustomLog ** logs) { CustomLog *cl = (CustomLog *)xcalloc(1, sizeof(*cl)); // default buffer size and fatal settings cl->bufferSize = 8*MAX_URL; cl->fatal = true; /* determine configuration style */ - const char *filename = strtok(NULL, w_space); + 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); while (*logs) logs = &(*logs)->next; *logs = cl; return; } cl->filename = xstrdup(filename); cl->type = Log::Format::CLF_UNKNOWN; const char *token = ConfigParser::strtokFile(); 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::strtokFileUndo(); + ConfigParser::TokenUndo(); } 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::strtokFileUndo(); + ConfigParser::TokenUndo(); 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); } // set format if it has not been specified explicitly if (cl->type == Log::Format::CLF_UNKNOWN) setLogformat(cl, "squid", true); aclParseAclList(LegacyParser, &cl->aclList); while (*logs) logs = &(*logs)->next; *logs = cl; } @@ -4272,42 +4254,42 @@ if (*data == '\0' || *data != ',') break; } } } return data && *data == '\0'; } static void parse_CpuAffinityMap(CpuAffinityMap **const cpuAffinityMap) { #if !HAVE_CPU_AFFINITY debugs(3, DBG_CRITICAL, "FATAL: Squid built with no CPU affinity " << "support, do not set 'cpu_affinity_map'"); self_destruct(); #endif /* HAVE_CPU_AFFINITY */ if (!*cpuAffinityMap) *cpuAffinityMap = new CpuAffinityMap; - const char *const pToken = strtok(NULL, w_space); - const char *const cToken = strtok(NULL, w_space); + const char *const pToken = ConfigParser::NextToken(); + const char *const cToken = ConfigParser::NextToken(); Vector processes, cores; if (!parseNamedIntList(pToken, "process_numbers", processes)) { debugs(3, DBG_CRITICAL, "FATAL: bad 'process_numbers' parameter " << "in 'cpu_affinity_map'"); self_destruct(); } else if (!parseNamedIntList(cToken, "cores", cores)) { debugs(3, DBG_CRITICAL, "FATAL: bad 'cores' parameter in " << "'cpu_affinity_map'"); self_destruct(); } else if (!(*cpuAffinityMap)->add(processes, cores)) { debugs(3, DBG_CRITICAL, "FATAL: bad 'cpu_affinity_map'; " << "process_numbers and cores lists differ in length or " << "contain numbers <= 0"); self_destruct(); } } static void dump_CpuAffinityMap(StoreEntry *const entry, const char *const name, const CpuAffinityMap *const cpuAffinityMap) { @@ -4405,89 +4387,89 @@ { cfg->freeService(); } static void dump_ecap_service_type(StoreEntry * entry, const char *name, const Adaptation::Ecap::Config &cfg) { cfg.dumpService(entry, name); } #endif /* USE_ECAP */ #if ICAP_CLIENT static void parse_icap_service_failure_limit(Adaptation::Icap::Config *cfg) { char *token; time_t d; time_t m; cfg->service_failure_limit = GetInteger(); - if ((token = strtok(NULL, w_space)) == NULL) + if ((token = ConfigParser::NextToken()) == NULL) return; if (strcmp(token,"in") != 0) { debugs(3, DBG_CRITICAL, "expecting 'in' on'" << config_input_line << "'"); self_destruct(); } - if ((token = strtok(NULL, w_space)) == NULL) { + if ((token = ConfigParser::NextToken()) == NULL) { self_destruct(); } d = static_cast (xatoi(token)); m = static_cast (1); if (0 == d) (void) 0; - else if ((token = strtok(NULL, w_space)) == NULL) { + else if ((token = ConfigParser::NextToken()) == NULL) { debugs(3, DBG_CRITICAL, "No time-units on '" << config_input_line << "'"); self_destruct(); } else if ((m = parseTimeUnits(token, false)) == 0) self_destruct(); cfg->oldest_service_failure = (m * d); } static void dump_icap_service_failure_limit(StoreEntry *entry, const char *name, const Adaptation::Icap::Config &cfg) { storeAppendPrintf(entry, "%s %d", name, cfg.service_failure_limit); if (cfg.oldest_service_failure > 0) { storeAppendPrintf(entry, " in %d seconds", (int)cfg.oldest_service_failure); } storeAppendPrintf(entry, "\n"); } static void free_icap_service_failure_limit(Adaptation::Icap::Config *cfg) { cfg->oldest_service_failure = 0; cfg->service_failure_limit = 0; } #endif #if USE_SSL static void parse_sslproxy_cert_adapt(sslproxy_cert_adapt **cert_adapt) { char *al; sslproxy_cert_adapt *ca = (sslproxy_cert_adapt *) xcalloc(1, sizeof(sslproxy_cert_adapt)); - if ((al = strtok(NULL, w_space)) == NULL) { + if ((al = ConfigParser::NextToken()) == NULL) { self_destruct(); return; } const char *param; if ( char *s = strchr(al, '{')) { *s = '\0'; // terminate the al string ++s; param = s; s = strchr(s, '}'); if (!s) { self_destruct(); return; } *s = '\0'; } else param = NULL; if (strcmp(al, Ssl::CertAdaptAlgorithmStr[Ssl::algSetValidAfter]) == 0) { ca->alg = Ssl::algSetValidAfter; @@ -4531,41 +4513,41 @@ } static void free_sslproxy_cert_adapt(sslproxy_cert_adapt **cert_adapt) { while (*cert_adapt) { sslproxy_cert_adapt *ca = *cert_adapt; *cert_adapt = ca->next; safe_free(ca->param); if (ca->aclList) aclDestroyAclList(&ca->aclList); safe_free(ca); } } static void parse_sslproxy_cert_sign(sslproxy_cert_sign **cert_sign) { char *al; sslproxy_cert_sign *cs = (sslproxy_cert_sign *) xcalloc(1, sizeof(sslproxy_cert_sign)); - if ((al = strtok(NULL, w_space)) == NULL) { + if ((al = ConfigParser::NextToken()) == NULL) { self_destruct(); return; } if (strcmp(al, Ssl::CertSignAlgorithmStr[Ssl::algSignTrusted]) == 0) cs->alg = Ssl::algSignTrusted; else if (strcmp(al, Ssl::CertSignAlgorithmStr[Ssl::algSignUntrusted]) == 0) cs->alg = Ssl::algSignUntrusted; else if (strcmp(al, Ssl::CertSignAlgorithmStr[Ssl::algSignSelf]) == 0) cs->alg = Ssl::algSignSelf; else { debugs(3, DBG_CRITICAL, "FATAL: sslproxy_cert_sign: unknown cert signing algorithm: " << al); self_destruct(); return; } aclParseAclList(LegacyParser, &cs->aclList); while (*cert_sign) cert_sign = &(*cert_sign)->next; @@ -4623,41 +4605,41 @@ } else { strcpy(buf, "ssl_bump allow all"); debugs(3, DBG_CRITICAL, "SECURITY NOTICE: auto-converting deprecated implicit " "\"ssl_bump allow all\" to \"ssl_bump client-first all\" which is usually " "inferior to the newer server-first bumping mode. New ssl_bump" " configurations must not use implicit rules. Update your ssl_bump rules."); } parse_line(buf); } } static void parse_sslproxy_ssl_bump(acl_access **ssl_bump) { typedef const char *BumpCfgStyle; BumpCfgStyle bcsNone = NULL; BumpCfgStyle bcsNew = "new client/server-first/none"; BumpCfgStyle bcsOld = "deprecated allow/deny"; static BumpCfgStyle bumpCfgStyleLast = bcsNone; BumpCfgStyle bumpCfgStyleNow = bcsNone; char *bm; - if ((bm = strtok(NULL, w_space)) == NULL) { + if ((bm = ConfigParser::NextToken()) == NULL) { self_destruct(); return; } // if this is the first rule proccessed if (*ssl_bump == NULL) { bumpCfgStyleLast = bcsNone; sslBumpCfgRr::lastDeprecatedRule = Ssl::bumpEnd; } acl_access *A = new acl_access; A->allow = allow_t(ACCESS_ALLOWED); if (strcmp(bm, Ssl::BumpModeStr[Ssl::bumpClientFirst]) == 0) { A->allow.kind = Ssl::bumpClientFirst; bumpCfgStyleNow = bcsNew; } else if (strcmp(bm, Ssl::BumpModeStr[Ssl::bumpServerFirst]) == 0) { A->allow.kind = Ssl::bumpServerFirst; bumpCfgStyleNow = bcsNew; } else if (strcmp(bm, Ssl::BumpModeStr[Ssl::bumpNone]) == 0) { @@ -4722,63 +4704,64 @@ static void dump_HeaderWithAclList(StoreEntry * entry, const char *name, HeaderWithAclList *headers) { if (!headers) return; for (HeaderWithAclList::iterator hwa = headers->begin(); hwa != headers->end(); ++hwa) { storeAppendPrintf(entry, "%s ", hwa->fieldName.c_str()); storeAppendPrintf(entry, "%s ", hwa->fieldValue.c_str()); if (hwa->aclList) dump_acl_list(entry, hwa->aclList); storeAppendPrintf(entry, "\n"); } } static void parse_HeaderWithAclList(HeaderWithAclList **headers) { char *fn; if (!*headers) { *headers = new HeaderWithAclList; } - if ((fn = strtok(NULL, w_space)) == NULL) { + 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; - String buf; - bool wasQuoted; - ConfigParser::ParseQuotedString(&buf, &wasQuoted); + Format::Format *nlf = new ::Format::Format("hdrWithAcl"); + ConfigParser::SetMacroUser(nlf); + String buf = ConfigParser::NextToken(); + ConfigParser::SetMacroUser(NULL); hwa.fieldValue = buf.termedBuf(); - hwa.quoted = wasQuoted; + hwa.quoted = ConfigParser::LastTokenWasQuoted(); if (hwa.quoted) { - Format::Format *nlf = new ::Format::Format("hdrWithAcl"); if (!nlf->parse(hwa.fieldValue.c_str())) { self_destruct(); return; } hwa.valueFormat = nlf; - } + } else + delete nlf; aclParseAclList(LegacyParser, &hwa.aclList); (*headers)->push_back(hwa); } static void free_HeaderWithAclList(HeaderWithAclList **header) { if (!(*header)) return; for (HeaderWithAclList::iterator hwa = (*header)->begin(); hwa != (*header)->end(); ++hwa) { if (hwa->aclList) aclDestroyAclList(&hwa->aclList); if (hwa->valueFormat) { delete hwa->valueFormat; hwa->valueFormat = NULL; } } delete *header; *header = NULL; === modified file 'src/cf.data.pre' --- src/cf.data.pre 2013-05-14 17:53:18 +0000 +++ src/cf.data.pre 2013-05-23 17:29:59 +0000 @@ -48,40 +48,59 @@ while in other cases it refers to the value of the option - the comments for that keyword indicate if this is the case. COMMENT_END COMMENT_START Configuration options can be included using the "include" directive. Include takes a list of files to include. Quoting and wildcards are supported. For example, include /path/to/included/file/squid.acl.config Includes can be nested up to a hard-coded depth of 16 levels. This arbitrary restriction is to prevent recursive include references from causing Squid entering an infinite loop whilst trying to load configuration files. + Values with spaces, quotes, and other special characters + + Squid supports directive parameters with spaces, quotes, and other + special characters. Surround such parameters with "double quotes". Use + the configuration_includes_quoted_values directive to enable or + disable that support. + + Squid supports reading configuration option parameters from external + files using the syntax: + file:"/path/filename" + For example: + http_port file:"/etc/squid/http_port_options.conf" + + TODO: Document escaping and macro substitution rules inside quoted + strings. + + Someone can use single-quoted strings to prevent macro substitution. + + Conditional configuration If-statements can be used to make configuration directives depend on conditions: if ... regular configuration directives ... [else ... regular configuration directives ...] endif The else part is optional. The keywords "if", "else", and "endif" must be typed on their own lines, as if they were regular configuration directives. NOTE: An else-if condition is not supported. These individual conditions types are supported: true @@ -8389,40 +8408,53 @@ DEFAULT: 95 LOC: Config.ipcache.high 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 +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 NAME: memory_pools_limit COMMENT: (bytes) TYPE: b_int64_t DEFAULT: 5 MB LOC: Config.MemPools.limit DOC_START Used only with memory_pools on: memory_pools_limit 50 MB === modified file 'src/cf_gen.cc' --- src/cf_gen.cc 2013-04-23 12:35:44 +0000 +++ src/cf_gen.cc 2013-05-17 16:21:21 +0000 @@ -662,41 +662,42 @@ return; // Once for the current directive name genParseAlias(name, fout); // All accepted aliases for (EntryAliasList::const_iterator a = alias.begin(); a != alias.end(); ++a) { genParseAlias(*a, fout); } } static void gen_parse(const EntryList &head, std::ostream &fout) { fout << "static int\n" "parse_line(char *buff)\n" "{\n" "\tchar\t*token;\n" "\tif ((token = strtok(buff, w_space)) == NULL) \n" - "\t\treturn 1;\t/* ignore empty lines */\n"; + "\t\treturn 1;\t/* ignore empty lines */\n" + "\tConfigParser::SetCfgLine(strtok(NULL, \"\"));\n"; for (EntryList::const_iterator e = head.begin(); e != head.end(); ++e) e->genParse(fout); fout << "\treturn 0; /* failure */\n" "}\n\n"; } static void gen_dump(const EntryList &head, std::ostream &fout) { fout << "static void" << std::endl << "dump_config(StoreEntry *entry)" << std::endl << "{" << std::endl << " debugs(5, 4, HERE);" << std::endl; for (EntryList::const_iterator e = head.begin(); e != head.end(); ++e) { === modified file 'src/external_acl.cc' --- src/external_acl.cc 2013-05-14 17:53:18 +0000 +++ src/external_acl.cc 2013-05-23 19:00:34 +0000 @@ -27,40 +27,41 @@ * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * 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. * */ #include "squid.h" #include "acl/Acl.h" #include "acl/FilledChecklist.h" #include "cache_cf.h" #include "client_side.h" #include "comm/Connection.h" +#include "ConfigParser.h" #include "ExternalACL.h" #include "ExternalACLEntry.h" #include "fde.h" #include "helper.h" #include "HttpHeaderTools.h" #include "HttpReply.h" #include "HttpRequest.h" #include "ip/tools.h" #include "MemBuf.h" #include "mgr/Registration.h" #include "rfc1738.h" #include "SquidConfig.h" #include "SquidString.h" #include "SquidTime.h" #include "Store.h" #include "tools.h" #include "URL.h" #include "URLScheme.h" #include "wordlist.h" #if USE_SSL @@ -189,40 +190,50 @@ EXT_ACL_USER_CERT_RAW, EXT_ACL_USER_CERTCHAIN_RAW, #endif #if USE_AUTH EXT_ACL_EXT_USER, #endif EXT_ACL_EXT_LOG, EXT_ACL_TAG, EXT_ACL_ACLNAME, EXT_ACL_ACLDATA, EXT_ACL_PERCENT, EXT_ACL_END } type; external_acl_format *next; char *header; char *member; char separator; http_hdr_type header_id; }; +// Temporary MacroUser class to support quoted %macros +// It should be merged to external_acl or external_acl_format +// when these structs converted to real c++ classes +class ExtAclMacroUser: public MacroUser { +public: + /* MacroUser API */ + virtual bool supportedMacro(const char *token); + virtual ~ExtAclMacroUser() {}; +}; + /* FIXME: These are not really cbdata, but it is an easy way * to get them pooled, refcounted, accounted and freed properly... */ CBDATA_TYPE(external_acl); CBDATA_TYPE(external_acl_format); static void free_external_acl_format(void *data) { external_acl_format *p = static_cast(data); safe_free(p->header); } static void free_external_acl(void *data) { external_acl *p = static_cast(data); safe_free(p->name); while (p->format) { @@ -313,48 +324,51 @@ { external_acl *a; char *token; external_acl_format **p; CBDATA_INIT_TYPE_FREECB(external_acl, free_external_acl); CBDATA_INIT_TYPE_FREECB(external_acl_format, free_external_acl_format); a = cbdataAlloc(external_acl); /* set defaults */ a->ttl = DEFAULT_EXTERNAL_ACL_TTL; a->negative_ttl = -1; a->cache_size = 256*1024; a->children.n_max = DEFAULT_EXTERNAL_ACL_CHILDREN; a->children.n_startup = a->children.n_max; a->children.n_idle = 1; a->local_addr.SetLocalhost(); a->quote = external_acl::QUOTE_METHOD_URL; - token = strtok(NULL, w_space); + token = ConfigParser::NextToken(); if (!token) self_destruct(); a->name = xstrdup(token); - token = strtok(NULL, w_space); + // Allow supported %macros inside quoted tokens + ExtAclMacroUser extAclMacroUser; + ConfigParser::SetMacroUser(&extAclMacroUser); + token = ConfigParser::NextToken(); /* Parse options */ while (token) { if (strncmp(token, "ttl=", 4) == 0) { a->ttl = atoi(token + 4); } else if (strncmp(token, "negative_ttl=", 13) == 0) { a->negative_ttl = atoi(token + 13); } else if (strncmp(token, "children=", 9) == 0) { a->children.n_max = atoi(token + 9); debugs(0, DBG_CRITICAL, "WARNING: external_acl_type option children=N has been deprecated in favor of children-max=N and children-startup=N"); } else if (strncmp(token, "children-max=", 13) == 0) { a->children.n_max = atoi(token + 13); } else if (strncmp(token, "children-startup=", 17) == 0) { a->children.n_startup = atoi(token + 17); } else if (strncmp(token, "children-idle=", 14) == 0) { a->children.n_idle = atoi(token + 14); } else if (strncmp(token, "concurrency=", 12) == 0) { a->children.concurrency = atoi(token + 12); } else if (strncmp(token, "cache=", 6) == 0) { a->cache_size = atoi(token + 6); @@ -369,42 +383,43 @@ debugs(3, DBG_PARSE_NOTE(2), "WARNING: external_acl_type option quote=url is deprecated. Remove this from your config."); a->quote = external_acl::QUOTE_METHOD_URL; } else if (strcmp(token, "quote=shell") == 0) { debugs(3, DBG_PARSE_NOTE(2), "WARNING: external_acl_type option quote=shell is deprecated. Use protocol=2.5 if still needed."); a->quote = external_acl::QUOTE_METHOD_SHELL; /* INET6: allow admin to configure some helpers explicitly to bind to IPv4/v6 localhost port. */ } else if (strcmp(token, "ipv4") == 0) { if ( !a->local_addr.SetIPv4() ) { debugs(3, DBG_CRITICAL, "WARNING: Error converting " << a->local_addr << " to IPv4 in " << a->name ); } } else if (strcmp(token, "ipv6") == 0) { if (!Ip::EnableIpv6) debugs(3, DBG_CRITICAL, "WARNING: --enable-ipv6 required for external ACL helpers to use IPv6: " << a->name ); // else nothing to do. } else { break; } - token = strtok(NULL, w_space); + token = ConfigParser::NextToken(); } + ConfigParser::SetMacroUser(NULL); /* check that child startup value is sane. */ if (a->children.n_startup > a->children.n_max) a->children.n_startup = a->children.n_max; /* check that child idle value is sane. */ if (a->children.n_idle > a->children.n_max) a->children.n_idle = a->children.n_max; if (a->children.n_idle < 1) a->children.n_idle = 1; if (a->negative_ttl == -1) a->negative_ttl = a->ttl; /* Parse format */ p = &a->format; while (token) { external_acl_format *format; @@ -480,41 +495,41 @@ else if (strcmp(token, "%EXT_USER") == 0) format->type = _external_acl_format::EXT_ACL_EXT_USER; #endif else if (strcmp(token, "%EXT_LOG") == 0) format->type = _external_acl_format::EXT_ACL_EXT_LOG; else if (strcmp(token, "%TAG") == 0) format->type = _external_acl_format::EXT_ACL_TAG; else if (strcmp(token, "%ACL") == 0) format->type = _external_acl_format::EXT_ACL_ACLNAME; else if (strcmp(token, "%DATA") == 0) format->type = _external_acl_format::EXT_ACL_ACLDATA; else if (strcmp(token, "%%") == 0) format->type = _external_acl_format::EXT_ACL_PERCENT; else { debugs(0, DBG_CRITICAL, "ERROR: Unknown Format token " << token); self_destruct(); } *p = format; p = &format->next; - token = strtok(NULL, w_space); + token = ConfigParser::NextToken(); } /* There must be at least one format token */ if (!a->format) self_destruct(); /* helper */ if (!token) self_destruct(); wordlistAdd(&a->cmdline, token); /* arguments */ parse_wordlist(&a->cmdline); while (*list) list = &(*list)->next; *list = a; } @@ -664,40 +679,58 @@ } void external_acl::add(ExternalACLEntry *anEntry) { trimCache(); assert (anEntry->def == NULL); anEntry->def = this; hash_join(cache, anEntry); dlinkAdd(anEntry, &anEntry->lru, &lru_list); ++cache_entries; } void external_acl::trimCache() { if (cache_size && cache_entries >= cache_size) external_acl_cache_delete(this, static_cast(lru_list.tail->data)); } +bool +ExtAclMacroUser::supportedMacro(const char *token) +{ + // Not really a test required here because the + return true; + +#if 0 + //We may want to check if %macros supported by external_acl API. eg: + static const char *supportedMacros[] = { "%SRC", "%DST", NULL}; + for (int i = 0; supportedMacros[i] != NULL; i++) { + if (strncmp(token, supportedMacros[i], strlen(supportedMacros[i])) == 0) + return true; + } + return false; +#endif + +} + /****************************************************************** * external acl type */ struct _external_acl_data { external_acl *def; const char *name; wordlist *arguments; }; CBDATA_TYPE(external_acl_data); static void free_external_acl_data(void *data) { external_acl_data *p = static_cast(data); safe_free(p->name); wordlistDestroy(&p->arguments); cbdataReferenceDone(p->def); } === modified file 'src/format/Config.cc' --- src/format/Config.cc 2012-08-31 16:57:39 +0000 +++ src/format/Config.cc 2013-01-23 16:02:32 +0000 @@ -1,37 +1,38 @@ #include "squid.h" +#include "ConfigParser.h" #include "cache_cf.h" #include "Debug.h" #include "format/Config.h" #include Format::FmtConfig Format::TheConfig; void Format::FmtConfig::parseFormats() { char *name, *def; - if ((name = strtok(NULL, w_space)) == NULL) + if ((name = ConfigParser::NextToken()) == NULL) self_destruct(); - if ((def = strtok(NULL, "\r\n")) == NULL) { + if ((def = ConfigParser::NextQuotedOrToEol()) == NULL) { self_destruct(); return; } debugs(3, 2, "Custom Format for '" << name << "' is '" << def << "'"); Format *nlf = new Format(name); if (!nlf->parse(def)) { self_destruct(); return; } // add to global config list nlf->next = formats; formats = nlf; } void Format::FmtConfig::registerTokens(const String &nsName, TokenTableEntry const *tokenArray) === modified file 'src/format/Format.cc' --- src/format/Format.cc 2013-05-13 03:57:03 +0000 +++ src/format/Format.cc 2013-05-23 18:27:09 +0000 @@ -29,40 +29,49 @@ name = xstrdup(n); } Format::Format::~Format() { // erase the list without consuming stack space while (next) { // unlink the next entry for deletion Format *temp = next; next = temp->next; temp->next = NULL; delete temp; } // remove locals xfree(name); delete format; } bool +Format::Format::supportedMacro(const char *def) +{ + Token tk; + enum Quoting quote = LOG_QUOTE_NONE; + // Token::parse will call self_destruct if the macro is not supported + return tk.parse(def, "e) != 0; +} + +bool Format::Format::parse(const char *def) { const char *cur, *eos; Token *new_lt, *last_lt; enum Quoting quote = LOG_QUOTE_NONE; debugs(46, 2, HERE << "got definition '" << def << "'"); if (format) { debugs(46, DBG_IMPORTANT, "WARNING: existing format for '" << name << " " << def << "'"); return false; } /* very inefficent parser, but who cares, this needs to be simple */ /* First off, let's tokenize, we'll optimize in a second pass. * A token can either be a %-prefixed sequence (usually a dynamic * token but it can be an escaped sequence), or a string. */ cur = def; eos = def + strlen(def); format = new_lt = last_lt = new Token; === modified file 'src/format/Format.h' --- src/format/Format.h 2013-01-02 23:40:49 +0000 +++ src/format/Format.h 2013-05-23 17:50:13 +0000 @@ -1,52 +1,56 @@ #ifndef _SQUID_FORMAT_FORMAT_H #define _SQUID_FORMAT_FORMAT_H #include "base/RefCount.h" +#include "ConfigParser.h" /* * Squid configuration allows users to define custom formats in * several components. * - logging * - external ACL input * - deny page URL * * These enumerations and classes define the API for parsing of * format directives to define these patterns. Along with output * functionality to produce formatted buffers. */ class AccessLogEntry; typedef RefCount AccessLogEntryPointer; class MemBuf; class StoreEntry; namespace Format { class Token; // XXX: inherit from linked list -class Format +class Format : public MacroUser { public: Format(const char *name); - ~Format(); + virtual ~Format(); + + /* MacroUser API */ + virtual bool supportedMacro(const char *token); /* very inefficent parser, but who cares, this needs to be simple */ /* First off, let's tokenize, we'll optimize in a second pass. * A token can either be a %-prefixed sequence (usually a dynamic * token but it can be an escaped sequence), or a string. */ bool parse(const char *def); /// assemble the state information into a formatted line. void assemble(MemBuf &mb, const AccessLogEntryPointer &al, int logSequenceNumber) const; /// dump this whole list of formats into the provided StoreEntry void dump(StoreEntry * entry, const char *directiveName); char *name; Token *format; Format *next; }; } // namespace Format === modified file 'src/ip/QosConfig.cc' --- src/ip/QosConfig.cc 2012-12-31 01:13:06 +0000 +++ src/ip/QosConfig.cc 2013-01-18 16:22:48 +0000 @@ -192,41 +192,41 @@ tosToServer(NULL), tosToClient(NULL), nfmarkToServer(NULL), nfmarkToClient(NULL) { } void Ip::Qos::Config::parseConfigLine() { /* parse options ... */ char *token; /* These are set as appropriate and then used to check whether the initial loop has been done */ bool mark = false; bool tos = false; /* Assume preserve is true. We don't set at initialisation as this affects isHitTosActive(). We have to do this now, as we may never match the 'tos' parameter below */ #if !USE_QOS_TOS debugs(3, DBG_CRITICAL, "ERROR: Invalid option 'qos_flows'. QOS features not enabled in this build"); self_destruct(); #endif - while ( (token = strtok(NULL, w_space)) ) { + while ( (token = ConfigParser::NextToken()) ) { // Work out TOS or mark. Default to TOS for backwards compatibility if (!(mark || tos)) { if (strncmp(token, "mark",4) == 0) { #if SO_MARK && USE_LIBCAP mark = true; // Assume preserve is true. We don't set at initialisation as this affects isHitNfmarkActive() #if USE_LIBNETFILTERCONNTRACK preserveMissMark = true; # else // USE_LIBNETFILTERCONNTRACK preserveMissMark = false; debugs(3, DBG_IMPORTANT, "WARNING: Squid not compiled with Netfilter conntrack library. " << "Netfilter mark preservation not available."); #endif // USE_LIBNETFILTERCONNTRACK #elif SO_MARK // SO_MARK && USE_LIBCAP debugs(3, DBG_CRITICAL, "ERROR: Invalid parameter 'mark' in qos_flows option. " << "Linux Netfilter marking not available without LIBCAP support."); self_destruct(); #else // SO_MARK && USE_LIBCAP debugs(3, DBG_CRITICAL, "ERROR: Invalid parameter 'mark' in qos_flows option. " === modified file 'src/log/Config.cc' --- src/log/Config.cc 2012-08-31 16:57:39 +0000 +++ src/log/Config.cc 2013-05-23 18:09:52 +0000 @@ -1,33 +1,36 @@ #include "squid.h" #include "cache_cf.h" +#include "ConfigParser.h" #include "Debug.h" #include "log/Config.h" Log::LogConfig Log::TheConfig; void Log::LogConfig::parseFormats() { char *name, *def; - if ((name = strtok(NULL, w_space)) == NULL) + if ((name = ConfigParser::NextToken()) == NULL) self_destruct(); - if ((def = strtok(NULL, "\r\n")) == NULL) { + ::Format::Format *nlf = new ::Format::Format(name); + + ConfigParser::SetMacroUser(nlf); + if ((def = ConfigParser::NextQuotedOrToEol()) == NULL) { self_destruct(); return; } + ConfigParser::SetMacroUser(NULL); debugs(3, 2, "Log Format for '" << name << "' is '" << def << "'"); - ::Format::Format *nlf = new ::Format::Format(name); - if (!nlf->parse(def)) { self_destruct(); return; } // add to global config list nlf->next = logformats; logformats = nlf; } === modified file 'src/tests/stub_cache_cf.cc' --- src/tests/stub_cache_cf.cc 2012-10-26 19:42:31 +0000 +++ src/tests/stub_cache_cf.cc 2013-01-14 10:48:38 +0000 @@ -32,23 +32,24 @@ #include "squid.h" #include "acl/Acl.h" #include "ConfigParser.h" #include "wordlist.h" #include "YesNoNone.h" #define STUB_API "cache_cf.cc" #include "tests/STUB.h" void self_destruct(void) STUB void parse_int(int *var) STUB void parse_onoff(int *var) STUB void parse_eol(char *volatile *var) STUB void parse_wordlist(wordlist ** list) STUB void requirePathnameExists(const char *name, const char *path) STUB_NOP void parse_time_t(time_t * var) STUB char * strtokFile(void) STUB_RETVAL(NULL) void ConfigParser::ParseUShort(unsigned short *var) STUB void ConfigParser::ParseString(String*) STUB +char * ConfigParser::NextToken() STUB void dump_acl_access(StoreEntry * entry, const char *name, acl_access * head) STUB void dump_acl_list(StoreEntry*, ACLList*) STUB YesNoNone::operator void*() const { STUB_NOP; return NULL; } === modified file 'src/wccp2.cc' --- src/wccp2.cc 2013-01-25 01:26:21 +0000 +++ src/wccp2.cc 2013-02-21 14:11:34 +0000 @@ -22,40 +22,41 @@ * 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. * */ #include "squid.h" #if USE_WCCPv2 #include "cache_cf.h" #include "comm.h" #include "comm/Connection.h" #include "comm/Loops.h" #include "compat/strsep.h" +#include "ConfigParser.h" #include "event.h" #include "ip/Address.h" #include "md5.h" #include "Parsing.h" #include "Store.h" #include "SwapDir.h" #if HAVE_NETDB_H #include #endif #define WCCP_PORT 2048 #define WCCP_RESPONSE_SIZE 12448 #define WCCP_BUCKETS 256 static int theWccp2Connection = -1; static int wccp2_connected = 0; static PF wccp2HandleUdp; static EVH wccp2HereIam; @@ -1996,41 +1997,41 @@ service_list_ptr = service_list_ptr->next; } } /* * Configuration option parsing code */ /** * Parse wccp2_return_method and wccp2_forwarding_method options * they can be '1' aka 'gre' or '2' aka 'l2' * repesenting the integer numeric of the same. */ void parse_wccp2_method(int *method) { char *t; /* Snarf the method */ - if ((t = strtok(NULL, w_space)) == NULL) { + if ((t = ConfigParser::NextToken()) == NULL) { debugs(80, DBG_CRITICAL, "wccp2_*_method: missing setting."); self_destruct(); } /* update configuration if its valid */ if (strcmp(t, "gre") == 0 || strcmp(t, "1") == 0) { *method = WCCP2_METHOD_GRE; } else if (strcmp(t, "l2") == 0 || strcmp(t, "2") == 0) { *method = WCCP2_METHOD_L2; } else { debugs(80, DBG_CRITICAL, "wccp2_*_method: unknown setting, got " << t ); self_destruct(); } } void dump_wccp2_method(StoreEntry * e, const char *label, int v) { switch (v) { case WCCP2_METHOD_GRE: @@ -2043,41 +2044,41 @@ debugs(80, DBG_CRITICAL, "FATAL: WCCPv2 configured method (" << v << ") is not valid."); self_destruct(); } } void free_wccp2_method(int *v) { } /** * Parse wccp2_assignment_method option * they can be '1' aka 'hash' or '2' aka 'mask' * repesenting the integer numeric of the same. */ void parse_wccp2_amethod(int *method) { char *t; /* Snarf the method */ - if ((t = strtok(NULL, w_space)) == NULL) { + if ((t = ConfigParser::NextToken()) == NULL) { debugs(80, DBG_CRITICAL, "wccp2_assignment_method: missing setting."); self_destruct(); } /* update configuration if its valid */ if (strcmp(t, "hash") == 0 || strcmp(t, "1") == 0) { *method = WCCP2_ASSIGNMENT_METHOD_HASH; } else if (strcmp(t, "mask") == 0 || strcmp(t, "2") == 0) { *method = WCCP2_ASSIGNMENT_METHOD_MASK; } else { debugs(80, DBG_CRITICAL, "wccp2_assignment_method: unknown setting, got " << t ); self_destruct(); } } void dump_wccp2_amethod(StoreEntry * e, const char *label, int v) { switch (v) { case WCCP2_ASSIGNMENT_METHOD_HASH: @@ -2099,66 +2100,66 @@ /* * Format: * * wccp2_service {standard|dynamic} {id} (password=password) */ void parse_wccp2_service(void *v) { char *t; int service = 0; int service_id = 0; int security_type = WCCP2_NO_SECURITY; char wccp_password[WCCP2_PASSWORD_LEN + 1]; if (wccp2_connected == 1) { debugs(80, DBG_IMPORTANT, "WCCPv2: Somehow reparsing the configuration without having shut down WCCP! Try reloading squid again."); return; } /* Snarf the type */ - if ((t = strtok(NULL, w_space)) == NULL) { + if ((t = ConfigParser::NextToken()) == NULL) { debugs(80, DBG_CRITICAL, "wccp2ParseServiceInfo: missing service info type (standard|dynamic)"); self_destruct(); } if (strcmp(t, "standard") == 0) { service = WCCP2_SERVICE_STANDARD; } else if (strcmp(t, "dynamic") == 0) { service = WCCP2_SERVICE_DYNAMIC; } else { debugs(80, DBG_CRITICAL, "wccp2ParseServiceInfo: bad service info type (expected standard|dynamic, got " << t << ")"); self_destruct(); } /* Snarf the ID */ service_id = GetInteger(); if (service_id < 0 || service_id > 255) { debugs(80, DBG_CRITICAL, "ERROR: invalid WCCP service id " << service_id << " (must be between 0 .. 255)"); self_destruct(); } memset(wccp_password, 0, sizeof(wccp_password)); /* Handle password, if any */ - if ((t = strtok(NULL, w_space)) != NULL) { + if ((t = ConfigParser::NextToken()) != NULL) { if (strncmp(t, "password=", 9) == 0) { security_type = WCCP2_MD5_SECURITY; strncpy(wccp_password, t + 9, WCCP2_PASSWORD_LEN); } } /* Create a placeholder service record */ wccp2_add_service_list(service, service_id, 0, 0, 0, empty_portlist, security_type, wccp_password); } void dump_wccp2_service(StoreEntry * e, const char *label, void *v) { struct wccp2_service_list_t *srv; srv = wccp2_service_list_head; while (srv != NULL) { debugs(80, 3, "dump_wccp2_service: id " << srv->info.service_id << ", type " << srv->info.service); storeAppendPrintf(e, "%s %s %d", label, @@ -2300,41 +2301,41 @@ } debugs(80, 5, "parse_wccp2_service_info: called"); memset(portlist, 0, sizeof(portlist)); /* First argument: id */ service_id = GetInteger(); if (service_id < 0 || service_id > 255) { debugs(80, DBG_CRITICAL, "ERROR: invalid WCCP service id " << service_id << " (must be between 0 .. 255)"); self_destruct(); } /* Next: find the (hopefully!) existing service */ srv = wccp2_get_service_by_id(WCCP2_SERVICE_DYNAMIC, service_id); if (srv == NULL) { fatalf("parse_wccp2_service_info: unknown dynamic service id %d: you need to define it using wccp2_service (and make sure you wish to configure it as a dynamic service.)\n", service_id); } /* Next: loop until we don't have any more tokens */ - while ((t = strtok(NULL, w_space)) != NULL) { + while ((t = ConfigParser::NextToken()) != NULL) { if (strncmp(t, "flags=", 6) == 0) { /* XXX eww, string pointer math */ flags = parse_wccp2_service_flags(t + 6); } else if (strncmp(t, "ports=", 6) == 0) { parse_wccp2_service_ports(t + 6, portlist); flags |= WCCP2_SERVICE_PORTS_DEFINED; } else if (strncmp(t, "protocol=tcp", 12) == 0) { protocol = IPPROTO_TCP; } else if (strncmp(t, "protocol=udp", 12) == 0) { protocol = IPPROTO_UDP; } else if (strncmp(t, "protocol=", 9) == 0) { fatalf("parse_wccp2_service_info: id %d: unknown protocol (%s) - must be tcp or udp!\n", service_id, t); } else if (strncmp(t, "priority=", 9) == 0) { priority = strtol(t + 9, &end, 0); if (priority < 0 || priority > 255) { fatalf("parse_wccp2_service_info: id %d: %s out of range (0..255)!\n", service_id, t); } } else { fatalf("parse_wccp2_service_info: id %d: unknown option '%s'\n", service_id, t);