ConfigParser.cc
Go to the documentation of this file.
1 /*
2  * Copyright (C) 1996-2017 The Squid Software Foundation and contributors
3  *
4  * Squid software is distributed under GPLv2+ license and includes
5  * contributions from numerous individuals and organizations.
6  * Please see the COPYING and CONTRIBUTORS files for details.
7  */
8 
9 #include "squid.h"
10 #include "cache_cf.h"
11 #include "ConfigParser.h"
12 #include "Debug.h"
13 #include "fatal.h"
14 #include "globals.h"
15 
17 bool ConfigParser::StrictMode = true;
18 std::stack<ConfigParser::CfgFile *> ConfigParser::CfgFiles;
20 const char *ConfigParser::CfgLine = NULL;
21 const char *ConfigParser::CfgPos = NULL;
22 std::queue<char *> ConfigParser::CfgLineTokens_;
23 std::queue<std::string> ConfigParser::Undo_;
24 bool ConfigParser::AllowMacros_ = false;
26 bool ConfigParser::ParseKvPair_ = false;
29 bool ConfigParser::PreviewMode_ = false;
30 
31 static const char *SQUID_ERROR_TOKEN = "[invalid token]";
32 
33 void
35 {
36  shutting_down = 1;
37  if (!CfgFiles.empty()) {
38  std::ostringstream message;
39  CfgFile *f = CfgFiles.top();
40  message << "Bungled " << f->filePath << " line " << f->lineNo <<
41  ": " << f->currentLine << std::endl;
42  CfgFiles.pop();
43  delete f;
44  while (!CfgFiles.empty()) {
45  f = CfgFiles.top();
46  message << " included from " << f->filePath << " line " <<
47  f->lineNo << ": " << f->currentLine << std::endl;
48  CfgFiles.pop();
49  delete f;
50  }
51  message << " included from " << cfg_filename << " line " <<
52  config_lineno << ": " << config_input_line << std::endl;
53  std::string msg = message.str();
54  fatalf("%s", msg.c_str());
55  } else
56  fatalf("Bungled %s line %d: %s",
58 }
59 
60 void
62 {
63  assert(tok);
64  Undo_.push(tok);
65 }
66 
67 char *
69 {
70  static char undoToken[CONFIG_LINE_LIMIT];
71  if (!Undo_.empty()) {
72  xstrncpy(undoToken, Undo_.front().c_str(), sizeof(undoToken));
73  undoToken[sizeof(undoToken) - 1] = '\0';
74  if (!PreviewMode_)
75  Undo_.pop();
76  return undoToken;
77  }
78  return NULL;
79 }
80 
81 char *
83 {
85  return ConfigParser::NextToken();
86 
87  static int fromFile = 0;
88  static FILE *wordFile = NULL;
89 
90  char *t;
91  static char buf[CONFIG_LINE_LIMIT];
92 
93  if ((t = ConfigParser::Undo()))
94  return t;
95 
96  do {
97 
98  if (!fromFile) {
99  ConfigParser::TokenType tokenType;
100  t = ConfigParser::NextElement(tokenType);
101  if (!t) {
102  return NULL;
103  } else if (*t == '\"' || *t == '\'') {
104  /* quote found, start reading from file */
105  debugs(3, 8,"Quoted token found : " << t);
106  char *fn = ++t;
107 
108  while (*t && *t != '\"' && *t != '\'')
109  ++t;
110 
111  *t = '\0';
112 
113  if ((wordFile = fopen(fn, "r")) == NULL) {
114  debugs(3, DBG_CRITICAL, "ERROR: Can not open file " << fn << " for reading");
115  return NULL;
116  }
117 
118 #if _SQUID_WINDOWS_
119  setmode(fileno(wordFile), O_TEXT);
120 #endif
121 
122  fromFile = 1;
123  } else {
124  return t;
125  }
126  }
127 
128  /* fromFile */
129  if (fgets(buf, sizeof(buf), wordFile) == NULL) {
130  /* stop reading from file */
131  fclose(wordFile);
132  wordFile = NULL;
133  fromFile = 0;
134  return NULL;
135  } else {
136  char *t2, *t3;
137  t = buf;
138  /* skip leading and trailing white space */
139  t += strspn(buf, w_space);
140  t2 = t + strcspn(t, w_space);
141  t3 = t2 + strspn(t2, w_space);
142 
143  while (*t3 && *t3 != '#') {
144  t2 = t3 + strcspn(t3, w_space);
145  t3 = t2 + strspn(t2, w_space);
146  }
147 
148  *t2 = '\0';
149  }
150 
151  /* skip comments */
152  /* skip blank lines */
153  } while ( *t == '#' || !*t );
154 
155  return t;
156 }
157 
158 char *
159 ConfigParser::UnQuote(const char *token, const char **next)
160 {
161  const char *errorStr = NULL;
162  const char *errorPos = NULL;
163  char quoteChar = *token;
164  assert(quoteChar == '"' || quoteChar == '\'');
165  LOCAL_ARRAY(char, UnQuoted, CONFIG_LINE_LIMIT);
166  const char *s = token + 1;
167  char *d = UnQuoted;
168  /* scan until the end of the quoted string, handling escape sequences*/
169  while (*s && *s != quoteChar && !errorStr && (size_t)(d - UnQuoted) < sizeof(UnQuoted)) {
170  if (*s == '\\') {
171  s++;
172  switch (*s) {
173  case 'r':
174  *d = '\r';
175  break;
176  case 'n':
177  *d = '\n';
178  break;
179  case 't':
180  *d = '\t';
181  break;
182  default:
183  if (isalnum(*s)) {
184  errorStr = "Unsupported escape sequence";
185  errorPos = s;
186  }
187  *d = *s;
188  break;
189  }
190 #if 0
191  } else if (*s == '$' && quoteChar == '"') {
192  errorStr = "Unsupported cfg macro";
193  errorPos = s;
194 #endif
195 #if 0
196  } else if (*s == '%' && quoteChar == '"' && (!AllowMacros_ )) {
197  errorStr = "Macros are not supported here";
198  errorPos = s;
199 #endif
200  } else
201  *d = *s;
202  ++s;
203  ++d;
204  }
205 
206  if (*s != quoteChar && !errorStr) {
207  errorStr = "missing quote char at the end of quoted string";
208  errorPos = s - 1;
209  }
210  // The end of token
211  *d = '\0';
212 
213  // We are expecting a separator after quoted string, space or one of "()#"
214  if (*(s + 1) != '\0' && !strchr(w_space "()#", *(s + 1)) && !errorStr) {
215  errorStr = "Expecting space after the end of quoted token";
216  errorPos = token;
217  }
218 
219  if (errorStr) {
220  if (PreviewMode_)
221  xstrncpy(UnQuoted, SQUID_ERROR_TOKEN, sizeof(UnQuoted));
222  else {
223  debugs(3, DBG_CRITICAL, "FATAL: " << errorStr << ": " << errorPos);
224  self_destruct();
225  }
226  }
227 
228  if (next)
229  *next = s + 1;
230  return UnQuoted;
231 }
232 
233 void
235 {
236  CfgLine = line;
237  CfgPos = line;
238  while (!CfgLineTokens_.empty()) {
239  char *token = CfgLineTokens_.front();
240  CfgLineTokens_.pop();
241  free(token);
242  }
243 }
244 
245 char *
247 {
248  if (!nextToken || *nextToken == '\0')
249  return NULL;
251  nextToken += strspn(nextToken, w_space);
252 
253  if (*nextToken == '#')
254  return NULL;
255 
256  if (ConfigParser::RecognizeQuotedValues && (*nextToken == '"' || *nextToken == '\'')) {
258  char *token = xstrdup(UnQuote(nextToken, &nextToken));
259  CfgLineTokens_.push(token);
260  return token;
261  }
262 
263  const char *tokenStart = nextToken;
264  const char *sep;
267  sep = "=";
268  else
269  sep = w_space;
271  sep = "\n";
273  sep = w_space "\\";
274  else if (!ConfigParser::RecognizeQuotedValues || *nextToken == '(')
275  sep = w_space;
276  else
277  sep = w_space "(";
278  nextToken += strcspn(nextToken, sep);
279 
280  while (ConfigParser::RecognizeQuotedPair_ && *nextToken == '\\') {
281  // NP: do not permit \0 terminator to be escaped.
282  if (*(nextToken+1) && *(nextToken+1) != '\r' && *(nextToken+1) != '\n') {
283  nextToken += 2; // skip the quoted-pair (\-escaped) character
284  nextToken += strcspn(nextToken, sep);
285  } else {
286  debugs(3, DBG_CRITICAL, "FATAL: Unescaped '\' character in regex pattern: " << tokenStart);
287  self_destruct();
288  }
289  }
290 
291  if (ConfigParser::RecognizeQuotedValues && *nextToken == '(') {
292  if (strncmp(tokenStart, "parameters", nextToken - tokenStart) == 0)
294  else {
295  if (PreviewMode_) {
296  char *err = xstrdup(SQUID_ERROR_TOKEN);
297  CfgLineTokens_.push(err);
298  return err;
299  } else {
300  debugs(3, DBG_CRITICAL, "FATAL: Unknown cfg function: " << tokenStart);
301  self_destruct();
302  }
303  }
304  } else
306 
307  char *token = NULL;
308  if (nextToken - tokenStart) {
310  bool tokenIsNumber = true;
311  for (const char *s = tokenStart; s != nextToken; ++s) {
312  const bool isValidChar = isalnum(*s) || strchr(".,()-=_/:+", *s) ||
313  (tokenIsNumber && *s == '%' && (s + 1 == nextToken));
314 
315  if (!isdigit(*s))
316  tokenIsNumber = false;
317 
318  if (!isValidChar) {
319  if (PreviewMode_) {
320  char *err = xstrdup(SQUID_ERROR_TOKEN);
321  CfgLineTokens_.push(err);
322  return err;
323  } else {
324  debugs(3, DBG_CRITICAL, "FATAL: Not alphanumeric character '"<< *s << "' in unquoted token " << tokenStart);
325  self_destruct();
326  }
327  }
328  }
329  }
330  token = xstrndup(tokenStart, nextToken - tokenStart + 1);
331  CfgLineTokens_.push(token);
332  }
333 
334  if (*nextToken != '\0' && *nextToken != '#') {
335  ++nextToken;
336  }
337 
338  return token;
339 }
340 
341 char *
343 {
344  const char *pos = CfgPos;
345  char *token = TokenParse(pos, type);
346  // If not in preview mode the next call of this method should start
347  // parsing after the end of current token.
348  // For function "parameters(...)" we need always to update current parsing
349  // position to allow parser read the arguments of "parameters(..)"
350  if (!PreviewMode_ || type == FunctionParameters)
351  CfgPos = pos;
352  // else next call will read the same token
353  return token;
354 }
355 
356 char *
358 {
359  char *token = NULL;
360  if ((token = ConfigParser::Undo())) {
361  debugs(3, 6, "TOKEN (undone): " << token);
362  return token;
363  }
364 
365  do {
366  while (token == NULL && !CfgFiles.empty()) {
367  ConfigParser::CfgFile *wordfile = CfgFiles.top();
368  token = wordfile->parse(LastTokenType);
369  if (!token) {
370  assert(!wordfile->isOpen());
371  CfgFiles.pop();
372  debugs(3, 4, "CfgFiles.pop " << wordfile->filePath);
373  delete wordfile;
374  }
375  }
376 
377  if (!token)
378  token = NextElement(LastTokenType);
379 
381  //Disable temporary preview mode, we need to parse function parameters
382  const bool savePreview = ConfigParser::PreviewMode_;
384 
385  char *path = NextToken();
387  debugs(3, DBG_CRITICAL, "FATAL: Quoted filename missing: " << token);
388  self_destruct();
389  return NULL;
390  }
391 
392  // The next token in current cfg file line must be a ")"
393  char *end = NextToken();
394  ConfigParser::PreviewMode_ = savePreview;
395  if (LastTokenType != ConfigParser::SimpleToken || strcmp(end, ")") != 0) {
396  debugs(3, DBG_CRITICAL, "FATAL: missing ')' after " << token << "(\"" << path << "\"");
397  self_destruct();
398  return NULL;
399  }
400 
401  if (CfgFiles.size() > 16) {
402  debugs(3, DBG_CRITICAL, "FATAL: can't open %s for reading parameters: includes are nested too deeply (>16)!\n" << path);
403  self_destruct();
404  return NULL;
405  }
406 
408  if (!path || !wordfile->startParse(path)) {
409  debugs(3, DBG_CRITICAL, "FATAL: Error opening config file: " << token);
410  delete wordfile;
411  self_destruct();
412  return NULL;
413  }
414  CfgFiles.push(wordfile);
415  token = NULL;
416  }
417  } while (token == NULL && !CfgFiles.empty());
418 
419  return token;
420 }
421 
422 char *
424 {
425  PreviewMode_ = true;
426  char *token = NextToken();
427  PreviewMode_ = false;
428  return token;
429 }
430 
431 char *
433 {
434  ParseQuotedOrToEol_ = true;
435  char *token = NextToken();
436  ParseQuotedOrToEol_ = false;
437 
438  // Assume end of current config line
439  // Close all open configuration files for this config line
440  while (!CfgFiles.empty()) {
441  ConfigParser::CfgFile *wordfile = CfgFiles.top();
442  CfgFiles.pop();
443  delete wordfile;
444  }
445 
446  return token;
447 }
448 
449 bool
450 ConfigParser::NextKvPair(char * &key, char * &value)
451 {
452  key = value = NULL;
453  ParseKvPair_ = true;
455  if ((key = NextToken()) != NULL) {
457  value = NextQuotedToken();
458  }
459  ParseKvPair_ = false;
460 
461  if (!key)
462  return false;
463  if (!value) {
464  debugs(3, DBG_CRITICAL, "Error while parsing key=value token. Value missing after: " << key);
465  return false;
466  }
467 
468  return true;
469 }
470 
471 char *
473 {
475  debugs(3, DBG_CRITICAL, "FATAL: Can not read regex expression while configuration_includes_quoted_values is enabled");
476  self_destruct();
477  }
479  char * token = strtokFile();
481  return token;
482 }
483 
484 char *
486 {
488  debugs(3, DBG_CRITICAL, "FATAL: Can not read regex expression while configuration_includes_quoted_values is enabled");
489  self_destruct();
490  }
492  char * token = NextToken();
494  return token;
495 }
496 
497 char *
499 {
500  const bool saveRecognizeQuotedValues = ConfigParser::RecognizeQuotedValues;
502  char *token = NextToken();
503  ConfigParser::RecognizeQuotedValues = saveRecognizeQuotedValues;
504  return token;
505 }
506 
507 const char *
509 {
510  static String quotedStr;
511  const char *s = var.termedBuf();
512  bool needQuote = false;
513 
514  for (const char *l = s; !needQuote && *l != '\0'; ++l )
515  needQuote = !isalnum(*l);
516 
517  if (!needQuote)
518  return s;
519 
520  quotedStr.clean();
521  quotedStr.append('"');
522  for (; *s != '\0'; ++s) {
523  if (*s == '"' || *s == '\\')
524  quotedStr.append('\\');
525  quotedStr.append(*s);
526  }
527  quotedStr.append('"');
528  return quotedStr.termedBuf();
529 }
530 
531 bool
533 {
534  assert(wordFile == NULL);
535  debugs(3, 3, "Parsing from " << path);
536  if ((wordFile = fopen(path, "r")) == NULL) {
537  debugs(3, DBG_CRITICAL, "WARNING: file :" << path << " not found");
538  return false;
539  }
540 
541 #if _SQUID_WINDOWS_
542  setmode(fileno(wordFile), O_TEXT);
543 #endif
544 
545  filePath = path;
546  return getFileLine();
547 }
548 
549 bool
551 {
552  // Else get the next line
553  if (fgets(parseBuffer, CONFIG_LINE_LIMIT, wordFile) == NULL) {
554  /* stop reading from file */
555  fclose(wordFile);
556  wordFile = NULL;
557  parseBuffer[0] = '\0';
558  return false;
559  }
560  parsePos = parseBuffer;
561  currentLine = parseBuffer;
562  lineNo++;
563  return true;
564 }
565 
566 char *
568 {
569  if (!wordFile)
570  return NULL;
571 
572  if (!*parseBuffer)
573  return NULL;
574 
575  char *token;
576  while (!(token = nextElement(type))) {
577  if (!getFileLine())
578  return NULL;
579  }
580  return token;
581 }
582 
583 char *
585 {
586  const char *pos = parsePos;
587  char *token = TokenParse(pos, type);
588  if (!PreviewMode_ || type == FunctionParameters)
589  parsePos = pos;
590  // else next call will read the same token;
591  return token;
592 }
593 
595 {
596  if (wordFile)
597  fclose(wordFile);
598 }
599 
static bool PreviewMode_
The next token will not poped from cfg files, will just previewd.
Definition: ConfigParser.h:211
static std::queue< std::string > Undo_
The list with TokenPutBack() queued elements.
Definition: ConfigParser.h:207
#define assert(EX)
Definition: assert.h:17
static const char * CfgLine
The current line to parse.
Definition: ConfigParser.h:204
char * nextElement(TokenType &type)
static std::queue< char * > CfgLineTokens_
Store the list of tokens for current configuration line.
Definition: ConfigParser.h:206
int type
Definition: errorpage.cc:79
void self_destruct(void)
Definition: cache_cf.cc:255
char * fn
Definition: membanger.c:36
#define xstrdup
static bool RecognizeQuotedPair_
The next tokens may contain quoted-pair (-escaped) characters.
Definition: ConfigParser.h:210
char * xstrndup(const char *s, size_t n)
Definition: xstring.cc:56
static char * TokenParse(const char *&nextToken, TokenType &type)
static char * RegexStrtokFile()
static char * PeekAtToken()
#define DBG_CRITICAL
Definition: Debug.h:44
static void TokenPutBack(const char *token)
Definition: ConfigParser.cc:61
static std::stack< CfgFile * > CfgFiles
The stack of open cfg files.
Definition: ConfigParser.h:202
static char * RegexPattern()
void append(char const *buf, int len)
Definition: String.cc:161
#define w_space
void fatalf(const char *fmt,...)
Definition: fatal.cc:79
static bool AllowMacros_
Definition: ConfigParser.h:208
int shutting_down
Definition: testAddress.cc:36
#define debugs(SECTION, LEVEL, CONTENT)
Definition: Debug.h:123
int config_lineno
int lineNo
Current line number.
Definition: ConfigParser.h:179
static void SetCfgLine(char *line)
Set the configuration file line to parse.
static char * NextQuotedToken()
std::string currentLine
The current line to parse.
Definition: ConfigParser.h:178
bool isOpen()
True if the configuration file is open.
Definition: ConfigParser.h:150
const char * cfg_filename
char const * termedBuf() const
Definition: SquidString.h:90
bool startParse(char *path)
static char * NextToken()
char * xstrncpy(char *dst, const char *src, size_t n)
Definition: xstring.cc:37
static char * NextElement(TokenType &type)
Wrapper method for TokenParse.
static enum ConfigParser::ParsingStates KvPairState_
Parsing state while parsing kv-pair tokens.
Definition: ConfigParser.cc:27
Definition: parse.c:160
#define LOCAL_ARRAY(type, name, size)
Definition: leakcheck.h:18
static const char * QuoteString(const String &var)
void const char * buf
Definition: stub_helper.cc:16
#define O_TEXT
Definition: defines.h:201
static bool ParseQuotedOrToEol_
The next tokens will be handled as quoted or to_eol token.
Definition: ConfigParser.h:209
static bool ParseKvPair_
The next token will be handled as kv-pair token.
Definition: ConfigParser.h:212
static const char * SQUID_ERROR_TOKEN
Definition: ConfigParser.cc:31
static char * UnQuote(const char *token, const char **next=NULL)
static const char * CfgPos
Pointer to the next element in cfgLine string.
Definition: ConfigParser.h:205
#define CONFIG_LINE_LIMIT
Definition: ConfigParser.h:27
char config_input_line[BUFSIZ]
FILE * wordFile
Pointer to the file.
Definition: ConfigParser.h:173
static char * strtokFile()
Definition: ConfigParser.cc:82
static bool NextKvPair(char *&key, char *&value)
static TokenType LastTokenType
The type of last parsed element.
Definition: ConfigParser.h:203
void destruct()
Definition: ConfigParser.cc:34
char * parse(TokenType &type)
static char * NextQuotedOrToEol()
void const cache_key * key
bool getFileLine()
Read the next line from the file.
void clean()
Definition: String.cc:125
static bool RecognizeQuotedValues
configuration_includes_quoted_values in squid.conf
Definition: ConfigParser.h:129
std::string filePath
The file path.
Definition: ConfigParser.h:177
static char * Undo()
Return the last TokenPutBack() queued element or NULL if none exist.
Definition: ConfigParser.cc:68
static bool StrictMode
Definition: ConfigParser.h:137
#define NULL
Definition: types.h:166
#define free(a)
Definition: hash.c:31

 

Introduction

Documentation

Support

Miscellaneous

Web Site Translations

Mirrors