]> git.ipfire.org Git - thirdparty/squid.git/blob - src/ConfigParser.cc
Quoted values in squid.conf
[thirdparty/squid.git] / src / ConfigParser.cc
1
2 /*
3 *
4 * SQUID Web Proxy Cache http://www.squid-cache.org/
5 * ----------------------------------------------------------
6 *
7 * Squid is the result of efforts by numerous individuals from
8 * the Internet community; see the CONTRIBUTORS file for full
9 * details. Many organizations have provided support for Squid's
10 * development; see the SPONSORS file for full details. Squid is
11 * Copyrighted (C) 2001 by the Regents of the University of
12 * California; see the COPYRIGHT file for full details. Squid
13 * incorporates software developed and/or copyrighted by other
14 * sources; see the CREDITS file for full details.
15 *
16 * This program is free software; you can redistribute it and/or modify
17 * it under the terms of the GNU General Public License as published by
18 * the Free Software Foundation; either version 2 of the License, or
19 * (at your option) any later version.
20 *
21 * This program is distributed in the hope that it will be useful,
22 * but WITHOUT ANY WARRANTY; without even the implied warranty of
23 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
24 * GNU General Public License for more details.
25 *
26 * You should have received a copy of the GNU General Public License
27 * along with this program; if not, write to the Free Software
28 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA.
29 *
30 *
31 * Copyright (c) 2003, Robert Collins <robertc@squid-cache.org>
32 */
33
34 #include "squid.h"
35 #include "cache_cf.h"
36 #include "ConfigParser.h"
37 #include "Debug.h"
38 #include "fatal.h"
39 #include "globals.h"
40
41 int ConfigParser::RecognizeQuotedValues = true;
42 std::stack<ConfigParser::CfgFile *> ConfigParser::CfgFiles;
43 ConfigParser::TokenType ConfigParser::LastTokenType = ConfigParser::SimpleToken;
44 char *ConfigParser::LastToken = NULL;
45 char *ConfigParser::CfgLine = NULL;
46 char *ConfigParser::CfgPos = NULL;
47 std::queue<std::string> ConfigParser::Undo_;
48 bool ConfigParser::AllowMacros_ = false;
49
50 void
51 ConfigParser::destruct()
52 {
53 shutting_down = 1;
54 if (!CfgFiles.empty()) {
55 std::ostringstream message;
56 CfgFile *f = CfgFiles.top();
57 message << "Bungled " << f->filePath << " line " << f->lineNo <<
58 ": " << f->currentLine << std::endl;
59 CfgFiles.pop();
60 delete f;
61 while (!CfgFiles.empty()) {
62 f = CfgFiles.top();
63 message << " included from " << f->filePath << " line " <<
64 f->lineNo << ": " << f->currentLine << std::endl;
65 CfgFiles.pop();
66 delete f;
67 }
68 message << " included from " << cfg_filename << " line " <<
69 config_lineno << ": " << config_input_line << std::endl;
70 std::string msg = message.str();
71 fatalf("%s", msg.c_str());
72 } else
73 fatalf("Bungled %s line %d: %s",
74 cfg_filename, config_lineno, config_input_line);
75 }
76
77 void
78 ConfigParser::TokenUndo()
79 {
80 assert(LastToken);
81 Undo_.push(LastToken);
82 }
83
84 void
85 ConfigParser::TokenPutBack(const char *tok)
86 {
87 assert(tok);
88 Undo_.push(tok);
89 }
90
91 char *
92 ConfigParser::Undo()
93 {
94 LOCAL_ARRAY(char, undoToken, CONFIG_LINE_LIMIT);
95 if (!Undo_.empty()) {
96 strncpy(undoToken, Undo_.front().c_str(), sizeof(undoToken));
97 undoToken[sizeof(undoToken) - 1] = '\0';
98 Undo_.pop();
99 return undoToken;
100 }
101 return NULL;
102 }
103
104 char *
105 ConfigParser::strtokFile()
106 {
107 if (RecognizeQuotedValues)
108 return ConfigParser::NextToken();
109
110 static int fromFile = 0;
111 static FILE *wordFile = NULL;
112
113 char *t;
114 LOCAL_ARRAY(char, buf, CONFIG_LINE_LIMIT);
115
116 if ((LastToken = ConfigParser::Undo()))
117 return LastToken;
118
119 do {
120
121 if (!fromFile) {
122 ConfigParser::TokenType tokenType;
123 t = ConfigParser::NextElement(tokenType, true);
124 if (!t) {
125 return NULL;
126 } else if (tokenType == ConfigParser::QuotedToken) {
127 /* quote found, start reading from file */
128 debugs(3, 8,"Quoted token found : " << t);
129
130 if ((wordFile = fopen(t, "r")) == NULL) {
131 debugs(3, DBG_CRITICAL, "Can not open file " << t << " for reading");
132 return false;
133 }
134
135 #if _SQUID_WINDOWS_
136 setmode(fileno(wordFile), O_TEXT);
137 #endif
138
139 fromFile = 1;
140 } else {
141 return LastToken = t;
142 }
143 }
144
145 /* fromFile */
146 if (fgets(buf, CONFIG_LINE_LIMIT, wordFile) == NULL) {
147 /* stop reading from file */
148 fclose(wordFile);
149 wordFile = NULL;
150 fromFile = 0;
151 return NULL;
152 } else {
153 char *t2, *t3;
154 t = buf;
155 /* skip leading and trailing white space */
156 t += strspn(buf, w_space);
157 t2 = t + strcspn(t, w_space);
158 t3 = t2 + strspn(t2, w_space);
159
160 while (*t3 && *t3 != '#') {
161 t2 = t3 + strcspn(t3, w_space);
162 t3 = t2 + strspn(t2, w_space);
163 }
164
165 *t2 = '\0';
166 }
167
168 /* skip comments */
169 /* skip blank lines */
170 } while ( *t == '#' || !*t );
171
172 return LastToken = t;
173 }
174
175 char *
176 ConfigParser::UnQuote(char *token, char **end)
177 {
178 char quoteChar = *token;
179 assert(quoteChar == '"' || quoteChar == '\'');
180 char *s = token + 1;
181 /* scan until the end of the quoted string, unescaping " and \ */
182 while (*s && *s != quoteChar) {
183 if (*s == '\\' && isalnum(*( s + 1))) {
184 debugs(3, DBG_CRITICAL, "Unsupported escape sequence: " << s);
185 self_destruct();
186 } else if (*s == '$' && quoteChar == '"') {
187 debugs(3, DBG_CRITICAL, "Unsupported cfg macro: " << s);
188 self_destruct();
189 } else if (*s == '%' && quoteChar == '"' && (!AllowMacros_ )) {
190 debugs(3, DBG_CRITICAL, "Macros are not supported here: " << s);
191 self_destruct();
192 } else if (*s == '\\') {
193 const char * next = s+1; // may point to 0
194 memmove(s, next, strlen(next) + 1);
195 }
196 ++s;
197 }
198
199 if (*s != quoteChar) {
200 debugs(3, DBG_CRITICAL, "missing '" << quoteChar << "' at the end of quoted string: " << (s-1));
201 self_destruct();
202 }
203 *end = s;
204 return (token+1);
205 }
206
207 void
208 ConfigParser::SetCfgLine(char *line)
209 {
210 CfgLine = line;
211 CfgPos = line;
212 }
213
214 char *
215 ConfigParser::TokenParse(char * &nextToken, ConfigParser::TokenType &type, bool legacy)
216 {
217 if (!nextToken || *nextToken == '\0')
218 return NULL;
219 type = ConfigParser::SimpleToken;
220 nextToken += strspn(nextToken, w_space);
221 if (*nextToken == '"' || *nextToken == '\'') {
222 type = ConfigParser::QuotedToken;
223 char *token = UnQuote(nextToken, &nextToken);
224 *nextToken = '\0';
225 ++nextToken;
226 return token;
227 }
228
229 char *token = nextToken;
230 if (char *t = strchr(nextToken, '#'))
231 *t = '\0';
232 const char *sep;
233 if (legacy)
234 sep = w_space;
235 else
236 sep = w_space "(";
237 nextToken += strcspn(nextToken, sep);
238
239 if (!legacy && *nextToken == '(')
240 type = ConfigParser::FunctionNameToken;
241 else
242 type = ConfigParser::SimpleToken;
243
244 if (*nextToken != '\0') {
245 *nextToken = '\0';
246 ++nextToken;
247 }
248
249 if (*token == '\0')
250 return NULL;
251
252 return token;
253 }
254
255 char *
256 ConfigParser::NextElement(ConfigParser::TokenType &type, bool legacy)
257 {
258 char *token = TokenParse(CfgPos, type, legacy);
259 return token;
260 }
261
262 char *
263 ConfigParser::NextToken()
264 {
265 if ((LastToken = ConfigParser::Undo()))
266 return LastToken;
267
268 char *token = NULL;
269 do {
270 while (token == NULL && !CfgFiles.empty()) {
271 ConfigParser::CfgFile *wordfile = CfgFiles.top();
272 token = wordfile->parse(LastTokenType);
273 if (!token) {
274 assert(!wordfile->isOpen());
275 CfgFiles.pop();
276 delete wordfile;
277 }
278 }
279
280 if (!token)
281 token = NextElement(LastTokenType);
282
283 if (token && LastTokenType == ConfigParser::FunctionNameToken && strcmp("parameters", token) == 0) {
284 char *path = NextToken();
285 if (LastTokenType != ConfigParser::QuotedToken) {
286 debugs(3, DBG_CRITICAL, "Quoted filename missing: " << token);
287 self_destruct();
288 return NULL;
289 }
290
291 // The next token in current cfg file line must be a ")"
292 char *end = NextToken();
293 if (LastTokenType != ConfigParser::SimpleToken || strcmp(end, ")") != 0) {
294 debugs(3, DBG_CRITICAL, "missing ')' after " << token << "(\"" << path << "\"");
295 self_destruct();
296 return NULL;
297 }
298
299 if (CfgFiles.size() > 16) {
300 debugs(3, DBG_CRITICAL, "WARNING: can't open %s for reading parameters: includes are nested too deeply (>16)!\n" << path);
301 self_destruct();
302 return NULL;
303 }
304
305 ConfigParser::CfgFile *wordfile = new ConfigParser::CfgFile();
306 if (!path || !wordfile->startParse(path)) {
307 debugs(3, DBG_CRITICAL, "Error opening config file: " << token);
308 delete wordfile;
309 self_destruct();
310 return NULL;
311 }
312 CfgFiles.push(wordfile);
313 token = NULL;
314 } else if (token && LastTokenType == ConfigParser::FunctionNameToken) {
315 debugs(3, DBG_CRITICAL, "Unknown cfg function: " << token);
316 self_destruct();
317 return NULL;
318 }
319 } while (token == NULL && !CfgFiles.empty());
320
321 return (LastToken = token);
322 }
323
324 char *
325 ConfigParser::NextQuotedOrToEol()
326 {
327 char *token;
328
329 if ((token = CfgPos) == NULL) {
330 debugs(3, DBG_CRITICAL, "token is missing");
331 self_destruct();
332 return NULL;
333 }
334 token += strspn(token, w_space);
335
336 if (*token == '\"' || *token == '\'') {
337 //TODO: eat the spaces at the end and check if it is untill the end of file.
338 char *end;
339 token = UnQuote(token, &end);
340 *end = '\0';
341 CfgPos = end + 1;
342 LastTokenType = ConfigParser::QuotedToken;
343 } else
344 LastTokenType = ConfigParser::SimpleToken;
345
346 CfgPos = NULL;
347 return (LastToken = token);
348 }
349
350 const char *
351 ConfigParser::QuoteString(const String &var)
352 {
353 static String quotedStr;
354 const char *s = var.termedBuf();
355 bool needQuote = false;
356
357 for (const char *l = s; !needQuote && *l != '\0'; ++l )
358 needQuote = !isalnum(*l);
359
360 if (!needQuote)
361 return s;
362
363 quotedStr.clean();
364 quotedStr.append('"');
365 for (; *s != '\0'; ++s) {
366 if (*s == '"' || *s == '\\')
367 quotedStr.append('\\');
368 quotedStr.append(*s);
369 }
370 quotedStr.append('"');
371 return quotedStr.termedBuf();
372 }
373
374 bool
375 ConfigParser::CfgFile::startParse(char *path)
376 {
377 assert(wordFile == NULL);
378 if ((wordFile = fopen(path, "r")) == NULL) {
379 debugs(3, DBG_CRITICAL, "file :" << path << " not found");
380 return false;
381 }
382
383 #if _SQUID_WINDOWS_
384 setmode(fileno(wordFile), O_TEXT);
385 #endif
386
387 filePath = path;
388 return getFileLine();
389 }
390
391 bool
392 ConfigParser::CfgFile::getFileLine()
393 {
394 // Else get the next line
395 if (fgets(parseBuffer, CONFIG_LINE_LIMIT, wordFile) == NULL) {
396 /* stop reading from file */
397 fclose(wordFile);
398 wordFile = NULL;
399 parseBuffer[0] = '\0';
400 return false;
401 }
402 parsePos = parseBuffer;
403 currentLine = parseBuffer;
404 lineNo++;
405 return true;
406 }
407
408 char *
409 ConfigParser::CfgFile::parse(ConfigParser::TokenType &type)
410 {
411 if (!wordFile)
412 return NULL;
413
414 if (!*parseBuffer)
415 return NULL;
416
417 char *token;
418 while (!(token = nextElement(type))) {
419 if (!getFileLine())
420 return NULL;
421 }
422 return token;
423 }
424
425 char *
426 ConfigParser::CfgFile::nextElement(ConfigParser::TokenType &type)
427 {
428 return TokenParse(parsePos, type);
429 }
430
431 ConfigParser::CfgFile::~CfgFile()
432 {
433 if (wordFile)
434 fclose(wordFile);
435 }