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

 

Introduction

Documentation

Support

Miscellaneous

Web Site Translations

Mirrors