1 /*
2 +----------------------------------------------------------------------+
3 | Zend Engine |
4 +----------------------------------------------------------------------+
5 | Copyright (c) 1998-2014 Zend Technologies Ltd. (http://www.zend.com) |
6 +----------------------------------------------------------------------+
7 | This source file is subject to version 2.00 of the Zend license, |
8 | that is bundled with this package in the file LICENSE, and is |
9 | available through the world-wide-web at the following url: |
10 | http://www.zend.com/license/2_00.txt. |
11 | If you did not receive a copy of the Zend license and are unable to |
12 | obtain it through the world-wide-web, please send a note to |
13 | license@zend.com so we can mail you a copy immediately. |
14 +----------------------------------------------------------------------+
15 | Authors: Zeev Suraski <zeev@zend.com> |
16 | Jani Taskinen <jani@php.net> |
17 | Marcus Boerger <helly@php.net> |
18 | Nuno Lopes <nlopess@php.net> |
19 | Scott MacVicar <scottmac@php.net> |
20 +----------------------------------------------------------------------+
21 */
22
23 /* $Id$ */
24
25 #include <errno.h>
26 #include "zend.h"
27 #include "zend_globals.h"
28 #include <zend_ini_parser.h>
29 #include "zend_ini_scanner.h"
30
31 #if 0
32 # define YYDEBUG(s, c) printf("state: %d char: %c\n", s, c)
33 #else
34 # define YYDEBUG(s, c)
35 #endif
36
37 #include "zend_ini_scanner_defs.h"
38
39 #define YYCTYPE unsigned char
40 /* allow the scanner to read one null byte after the end of the string (from ZEND_MMAP_AHEAD)
41 * so that if will be able to terminate to match the current token (e.g. non-enclosed string) */
42 #define YYFILL(n) { if (YYCURSOR > YYLIMIT) return 0; }
43 #define YYCURSOR SCNG(yy_cursor)
44 #define YYLIMIT SCNG(yy_limit)
45 #define YYMARKER SCNG(yy_marker)
46
47 #define YYGETCONDITION() SCNG(yy_state)
48 #define YYSETCONDITION(s) SCNG(yy_state) = s
49
50 #define STATE(name) yyc##name
51
52 /* emulate flex constructs */
53 #define BEGIN(state) YYSETCONDITION(STATE(state))
54 #define YYSTATE YYGETCONDITION()
55 #define yytext ((char*)SCNG(yy_text))
56 #define yyleng SCNG(yy_leng)
57 #define yyless(x) do { YYCURSOR = (unsigned char*)yytext + x; \
58 yyleng = (unsigned int)x; } while(0)
59
60 /* #define yymore() goto yymore_restart */
61
62 /* perform sanity check. If this message is triggered you should
63 increase the ZEND_MMAP_AHEAD value in the zend_streams.h file */
64 /*!max:re2c */
65 #if ZEND_MMAP_AHEAD < (YYMAXFILL + 1)
66 # error ZEND_MMAP_AHEAD should be greater than YYMAXFILL
67 #endif
68
69
70 /* How it works (for the core ini directives):
71 * ===========================================
72 *
73 * 1. Scanner scans file for tokens and passes them to parser.
74 * 2. Parser parses the tokens and passes the name/value pairs to the callback
75 * function which stores them in the configuration hash table.
76 * 3. Later REGISTER_INI_ENTRIES() is called which triggers the actual
77 * registering of ini entries and uses zend_get_configuration_directive()
78 * to fetch the previously stored name/value pair from configuration hash table
79 * and registers the static ini entries which match the name to the value
80 * into EG(ini_directives) hash table.
81 * 4. PATH section entries are used per-request from down to top, each overriding
82 * previous if one exists. zend_alter_ini_entry() is called for each entry.
83 * Settings in PATH section are ZEND_INI_SYSTEM accessible and thus mimics the
84 * php_admin_* directives used within Apache httpd.conf when PHP is compiled as
85 * module for Apache.
86 * 5. User defined ini files (like .htaccess for apache) are parsed for each request and
87 * stored in separate hash defined by SAPI.
88 */
89
90 /* TODO: (ordered by importance :-)
91 * ===============================================================================
92 *
93 * - Separate constant lookup totally from plain strings (using CONSTANT pattern)
94 * - Add #if .. #else .. #endif and ==, !=, <, > , <=, >= operators
95 * - Add #include "some.ini"
96 * - Allow variables to refer to options also when using parse_ini_file()
97 *
98 */
99
100 /* Globals Macros */
101 #define SCNG INI_SCNG
102 #ifdef ZTS
103 ZEND_API ts_rsrc_id ini_scanner_globals_id;
104 #else
105 ZEND_API zend_ini_scanner_globals ini_scanner_globals;
106 #endif
107
108 /* Eat leading whitespace */
109 #define EAT_LEADING_WHITESPACE() \
110 while (yytext[0]) { \
111 if (yytext[0] == ' ' || yytext[0] == '\t') { \
112 SCNG(yy_text)++; \
113 yyleng--; \
114 } else { \
115 break; \
116 } \
117 }
118
119 /* Eat trailing whitespace + extra char */
120 #define EAT_TRAILING_WHITESPACE_EX(ch) \
121 while (yyleng > 0 && ( \
122 (ch != 'X' && yytext[yyleng - 1] == ch) || \
123 yytext[yyleng - 1] == '\n' || \
124 yytext[yyleng - 1] == '\r' || \
125 yytext[yyleng - 1] == '\t' || \
126 yytext[yyleng - 1] == ' ') \
127 ) { \
128 yyleng--; \
129 }
130
131 /* Eat trailing whitespace */
132 #define EAT_TRAILING_WHITESPACE() EAT_TRAILING_WHITESPACE_EX('X')
133
134 #define zend_ini_copy_value(retval, str, len) { \
135 Z_STRVAL_P(retval) = zend_strndup(str, len); \
136 Z_STRLEN_P(retval) = len; \
137 Z_TYPE_P(retval) = IS_STRING; \
138 }
139
140 #define RETURN_TOKEN(type, str, len) { \
141 zend_ini_copy_value(ini_lval, str, len); \
142 return type; \
143 }
144
_yy_push_state(int new_state TSRMLS_DC)145 static void _yy_push_state(int new_state TSRMLS_DC)
146 {
147 zend_stack_push(&SCNG(state_stack), (void *) &YYGETCONDITION(), sizeof(int));
148 YYSETCONDITION(new_state);
149 }
150
151 #define yy_push_state(state_and_tsrm) _yy_push_state(yyc##state_and_tsrm)
152
yy_pop_state(TSRMLS_D)153 static void yy_pop_state(TSRMLS_D)
154 {
155 int *stack_state;
156 zend_stack_top(&SCNG(state_stack), (void **) &stack_state);
157 YYSETCONDITION(*stack_state);
158 zend_stack_del_top(&SCNG(state_stack));
159 }
160
yy_scan_buffer(char * str,unsigned int len TSRMLS_DC)161 static void yy_scan_buffer(char *str, unsigned int len TSRMLS_DC)
162 {
163 YYCURSOR = (YYCTYPE*)str;
164 SCNG(yy_start) = YYCURSOR;
165 YYLIMIT = YYCURSOR + len;
166 }
167
168 #define ini_filename SCNG(filename)
169
170 /* {{{ init_ini_scanner()
171 */
init_ini_scanner(int scanner_mode,zend_file_handle * fh TSRMLS_DC)172 static int init_ini_scanner(int scanner_mode, zend_file_handle *fh TSRMLS_DC)
173 {
174 /* Sanity check */
175 if (scanner_mode != ZEND_INI_SCANNER_NORMAL && scanner_mode != ZEND_INI_SCANNER_RAW) {
176 zend_error(E_WARNING, "Invalid scanner mode");
177 return FAILURE;
178 }
179
180 SCNG(lineno) = 1;
181 SCNG(scanner_mode) = scanner_mode;
182 SCNG(yy_in) = fh;
183
184 if (fh != NULL) {
185 ini_filename = zend_strndup(fh->filename, strlen(fh->filename));
186 } else {
187 ini_filename = NULL;
188 }
189
190 zend_stack_init(&SCNG(state_stack));
191 BEGIN(INITIAL);
192
193 return SUCCESS;
194 }
195 /* }}} */
196
197 /* {{{ shutdown_ini_scanner()
198 */
shutdown_ini_scanner(TSRMLS_D)199 void shutdown_ini_scanner(TSRMLS_D)
200 {
201 zend_stack_destroy(&SCNG(state_stack));
202 if (ini_filename) {
203 free(ini_filename);
204 }
205 }
206 /* }}} */
207
208 /* {{{ zend_ini_scanner_get_lineno()
209 */
zend_ini_scanner_get_lineno(TSRMLS_D)210 int zend_ini_scanner_get_lineno(TSRMLS_D)
211 {
212 return SCNG(lineno);
213 }
214 /* }}} */
215
216 /* {{{ zend_ini_scanner_get_filename()
217 */
zend_ini_scanner_get_filename(TSRMLS_D)218 char *zend_ini_scanner_get_filename(TSRMLS_D)
219 {
220 return ini_filename ? ini_filename : "Unknown";
221 }
222 /* }}} */
223
224 /* {{{ zend_ini_open_file_for_scanning()
225 */
zend_ini_open_file_for_scanning(zend_file_handle * fh,int scanner_mode TSRMLS_DC)226 int zend_ini_open_file_for_scanning(zend_file_handle *fh, int scanner_mode TSRMLS_DC)
227 {
228 char *buf;
229 size_t size;
230
231 if (zend_stream_fixup(fh, &buf, &size TSRMLS_CC) == FAILURE) {
232 return FAILURE;
233 }
234
235 if (init_ini_scanner(scanner_mode, fh TSRMLS_CC) == FAILURE) {
236 zend_file_handle_dtor(fh TSRMLS_CC);
237 return FAILURE;
238 }
239
240 yy_scan_buffer(buf, size TSRMLS_CC);
241
242 return SUCCESS;
243 }
244 /* }}} */
245
246 /* {{{ zend_ini_prepare_string_for_scanning()
247 */
zend_ini_prepare_string_for_scanning(char * str,int scanner_mode TSRMLS_DC)248 int zend_ini_prepare_string_for_scanning(char *str, int scanner_mode TSRMLS_DC)
249 {
250 int len = strlen(str);
251
252 if (init_ini_scanner(scanner_mode, NULL TSRMLS_CC) == FAILURE) {
253 return FAILURE;
254 }
255
256 yy_scan_buffer(str, len TSRMLS_CC);
257
258 return SUCCESS;
259 }
260 /* }}} */
261
262 /* {{{ zend_ini_escape_string()
263 */
zend_ini_escape_string(zval * lval,char * str,int len,char quote_type TSRMLS_DC)264 static void zend_ini_escape_string(zval *lval, char *str, int len, char quote_type TSRMLS_DC)
265 {
266 register char *s, *t;
267 char *end;
268
269 zend_ini_copy_value(lval, str, len);
270
271 /* convert escape sequences */
272 s = t = Z_STRVAL_P(lval);
273 end = s + Z_STRLEN_P(lval);
274
275 while (s < end) {
276 if (*s == '\\') {
277 s++;
278 if (s >= end) {
279 *t++ = '\\';
280 continue;
281 }
282 switch (*s) {
283 case '"':
284 if (*s != quote_type) {
285 *t++ = '\\';
286 *t++ = *s;
287 break;
288 }
289 case '\\':
290 case '$':
291 *t++ = *s;
292 Z_STRLEN_P(lval)--;
293 break;
294 default:
295 *t++ = '\\';
296 *t++ = *s;
297 break;
298 }
299 } else {
300 *t++ = *s;
301 }
302 if (*s == '\n' || (*s == '\r' && (*(s+1) != '\n'))) {
303 SCNG(lineno)++;
304 }
305 s++;
306 }
307 *t = 0;
308 }
309 /* }}} */
310
ini_lex(zval * ini_lval TSRMLS_DC)311 int ini_lex(zval *ini_lval TSRMLS_DC)
312 {
313 restart:
314 SCNG(yy_text) = YYCURSOR;
315
316 /* yymore_restart: */
317 /* detect EOF */
318 if (YYCURSOR >= YYLIMIT) {
319 if (YYSTATE == STATE(ST_VALUE) || YYSTATE == STATE(ST_RAW)) {
320 BEGIN(INITIAL);
321 return END_OF_LINE;
322 }
323 return 0;
324 }
325
326 /* Eat any UTF-8 BOM we find in the first 3 bytes */
327 if (YYCURSOR == SCNG(yy_start) && YYCURSOR + 3 < YYLIMIT) {
328 if (memcmp(YYCURSOR, "\xef\xbb\xbf", 3) == 0) {
329 YYCURSOR += 3;
330 goto restart;
331 }
332 }
333 /*!re2c
334 re2c:yyfill:check = 0;
335 LNUM [0-9]+
336 DNUM ([0-9]*[\.][0-9]+)|([0-9]+[\.][0-9]*)
337 NUMBER [-]?{LNUM}|{DNUM}
338 ANY_CHAR (.|[\n\t])
339 NEWLINE ("\r"|"\n"|"\r\n")
340 TABS_AND_SPACES [ \t]
341 WHITESPACE [ \t]+
342 CONSTANT [a-zA-Z_][a-zA-Z0-9_]*
343 LABEL [^=\n\r\t;|&$~(){}!"\[]+
344 TOKENS [:,.\[\]"'()|^&+-/*=%$!~<>?@{}]
345 OPERATORS [&|~()!]
346 DOLLAR_CURLY "${"
347
348 SECTION_RAW_CHARS [^\]\n\r]
349 SINGLE_QUOTED_CHARS [^']
350 RAW_VALUE_CHARS [^\n\r;\000]
351
352 LITERAL_DOLLAR ("$"([^{\000]|("\\"{ANY_CHAR})))
353 VALUE_CHARS ([^$= \t\n\r;&|~()!"'\000]|{LITERAL_DOLLAR})
354 SECTION_VALUE_CHARS ([^$\n\r;"'\]\\]|("\\"{ANY_CHAR})|{LITERAL_DOLLAR})
355
356 <!*> := yyleng = YYCURSOR - SCNG(yy_text);
357
358 <INITIAL>"[" { /* Section start */
359 /* Enter section data lookup state */
360 if (SCNG(scanner_mode) == ZEND_INI_SCANNER_RAW) {
361 yy_push_state(ST_SECTION_RAW TSRMLS_CC);
362 } else {
363 yy_push_state(ST_SECTION_VALUE TSRMLS_CC);
364 }
365 return TC_SECTION;
366 }
367
368 <ST_VALUE,ST_SECTION_VALUE,ST_OFFSET>"'"{SINGLE_QUOTED_CHARS}+"'" { /* Raw string */
369 /* Eat leading and trailing single quotes */
370 if (yytext[0] == '\'' && yytext[yyleng - 1] == '\'') {
371 SCNG(yy_text)++;
372 yyleng = yyleng - 2;
373 }
374 RETURN_TOKEN(TC_RAW, yytext, yyleng);
375 }
376
377 <ST_SECTION_RAW,ST_SECTION_VALUE>"]"{TABS_AND_SPACES}*{NEWLINE}? { /* End of section */
378 BEGIN(INITIAL);
379 SCNG(lineno)++;
380 return ']';
381 }
382
383 <INITIAL>{LABEL}"["{TABS_AND_SPACES}* { /* Start of option with offset */
384 /* Eat leading whitespace */
385 EAT_LEADING_WHITESPACE();
386
387 /* Eat trailing whitespace and [ */
388 EAT_TRAILING_WHITESPACE_EX('[');
389
390 /* Enter offset lookup state */
391 yy_push_state(ST_OFFSET TSRMLS_CC);
392
393 RETURN_TOKEN(TC_OFFSET, yytext, yyleng);
394 }
395
396 <ST_OFFSET>{TABS_AND_SPACES}*"]" { /* End of section or an option offset */
397 BEGIN(INITIAL);
398 return ']';
399 }
400
401 <ST_DOUBLE_QUOTES,ST_SECTION_VALUE,ST_VALUE,ST_OFFSET>{DOLLAR_CURLY} { /* Variable start */
402 yy_push_state(ST_VARNAME TSRMLS_CC);
403 return TC_DOLLAR_CURLY;
404 }
405
406 <ST_VARNAME>{LABEL} { /* Variable name */
407 /* Eat leading whitespace */
408 EAT_LEADING_WHITESPACE();
409
410 /* Eat trailing whitespace */
411 EAT_TRAILING_WHITESPACE();
412
413 RETURN_TOKEN(TC_VARNAME, yytext, yyleng);
414 }
415
416 <ST_VARNAME>"}" { /* Variable end */
417 yy_pop_state(TSRMLS_C);
418 return '}';
419 }
420
421 <INITIAL,ST_VALUE>("true"|"on"|"yes"){TABS_AND_SPACES}* { /* TRUE value (when used outside option value/offset this causes parse error!) */
422 RETURN_TOKEN(BOOL_TRUE, "1", 1);
423 }
424
425 <INITIAL,ST_VALUE>("false"|"off"|"no"|"none"|"null"){TABS_AND_SPACES}* { /* FALSE value (when used outside option value/offset this causes parse error!)*/
426 RETURN_TOKEN(BOOL_FALSE, "", 0);
427 }
428
429 <INITIAL>{LABEL} { /* Get option name */
430 /* Eat leading whitespace */
431 EAT_LEADING_WHITESPACE();
432
433 /* Eat trailing whitespace */
434 EAT_TRAILING_WHITESPACE();
435
436 RETURN_TOKEN(TC_LABEL, yytext, yyleng);
437 }
438
439 <INITIAL>{TABS_AND_SPACES}*[=]{TABS_AND_SPACES}* { /* Start option value */
440 if (SCNG(scanner_mode) == ZEND_INI_SCANNER_RAW) {
441 yy_push_state(ST_RAW TSRMLS_CC);
442 } else {
443 yy_push_state(ST_VALUE TSRMLS_CC);
444 }
445 return '=';
446 }
447
448 <ST_RAW>{RAW_VALUE_CHARS} { /* Raw value, only used when SCNG(scanner_mode) == ZEND_INI_SCANNER_RAW. */
449 char *sc = NULL;
450 while (YYCURSOR < YYLIMIT) {
451 switch (*YYCURSOR) {
452 case '\n':
453 case '\r':
454 goto end_raw_value_chars;
455 break;
456 case ';':
457 if (sc == NULL) {
458 sc = YYCURSOR;
459 }
460 /* no break */
461 default:
462 YYCURSOR++;
463 break;
464 }
465 }
466 end_raw_value_chars:
467 yyleng = YYCURSOR - SCNG(yy_text);
468
469 /* Eat trailing semicolons */
470 while (yytext[yyleng - 1] == ';') {
471 yyleng--;
472 }
473
474 /* Eat leading and trailing double quotes */
475 if (yytext[0] == '"' && yytext[yyleng - 1] == '"') {
476 SCNG(yy_text)++;
477 yyleng = yyleng - 2;
478 } else if (sc) {
479 YYCURSOR = sc;
480 yyleng = YYCURSOR - SCNG(yy_text);
481 }
482 RETURN_TOKEN(TC_RAW, yytext, yyleng);
483 }
484
485 <ST_SECTION_RAW>{SECTION_RAW_CHARS}+ { /* Raw value, only used when SCNG(scanner_mode) == ZEND_INI_SCANNER_RAW. */
486 RETURN_TOKEN(TC_RAW, yytext, yyleng);
487 }
488
489 <ST_VALUE,ST_RAW>{TABS_AND_SPACES}*{NEWLINE} { /* End of option value */
490 BEGIN(INITIAL);
491 SCNG(lineno)++;
492 return END_OF_LINE;
493 }
494
495 <ST_SECTION_VALUE,ST_VALUE,ST_OFFSET>{CONSTANT} { /* Get constant option value */
496 RETURN_TOKEN(TC_CONSTANT, yytext, yyleng);
497 }
498
499 <ST_SECTION_VALUE,ST_VALUE,ST_OFFSET>{NUMBER} { /* Get number option value as string */
500 RETURN_TOKEN(TC_NUMBER, yytext, yyleng);
501 }
502
503 <INITIAL>{TOKENS} { /* Disallow these chars outside option values */
504 return yytext[0];
505 }
506
507 <ST_VALUE>{OPERATORS}{TABS_AND_SPACES}* { /* Boolean operators */
508 return yytext[0];
509 }
510
511 <ST_VALUE>[=] { /* Make = used in option value to trigger error */
512 yyless(0);
513 BEGIN(INITIAL);
514 return END_OF_LINE;
515 }
516
517 <ST_VALUE>{VALUE_CHARS}+ { /* Get everything else as option/offset value */
518 RETURN_TOKEN(TC_STRING, yytext, yyleng);
519 }
520
521 <ST_SECTION_VALUE,ST_OFFSET>{SECTION_VALUE_CHARS}+ { /* Get rest as section/offset value */
522 RETURN_TOKEN(TC_STRING, yytext, yyleng);
523 }
524
525 <ST_SECTION_VALUE,ST_VALUE,ST_OFFSET>{TABS_AND_SPACES}*["] { /* Double quoted '"' string start */
526 yy_push_state(ST_DOUBLE_QUOTES TSRMLS_CC);
527 return '"';
528 }
529
530 <ST_DOUBLE_QUOTES>["]{TABS_AND_SPACES}* { /* Double quoted '"' string ends */
531 yy_pop_state(TSRMLS_C);
532 return '"';
533 }
534
535 <ST_DOUBLE_QUOTES>[^] { /* Escape double quoted string contents */
536 if (YYCURSOR > YYLIMIT) {
537 return 0;
538 }
539
540 while (YYCURSOR < YYLIMIT) {
541 switch (*YYCURSOR++) {
542 case '"':
543 if (YYCURSOR < YYLIMIT && YYCURSOR[-2] == '\\' && *YYCURSOR != '\r' && *YYCURSOR != '\n') {
544 continue;
545 }
546 break;
547 case '$':
548 if (*YYCURSOR == '{') {
549 break;
550 }
551 continue;
552 case '\\':
553 if (YYCURSOR < YYLIMIT && *YYCURSOR != '"') {
554 YYCURSOR++;
555 }
556 /* fall through */
557 default:
558 continue;
559 }
560
561 YYCURSOR--;
562 break;
563 }
564
565 yyleng = YYCURSOR - SCNG(yy_text);
566
567 zend_ini_escape_string(ini_lval, yytext, yyleng, '"' TSRMLS_CC);
568 return TC_QUOTED_STRING;
569 }
570
571 <ST_SECTION_VALUE,ST_VALUE,ST_OFFSET>{WHITESPACE} {
572 RETURN_TOKEN(TC_WHITESPACE, yytext, yyleng);
573 }
574
575 <INITIAL,ST_RAW>{TABS_AND_SPACES}+ {
576 /* eat whitespace */
577 goto restart;
578 }
579
580 <INITIAL>{TABS_AND_SPACES}*{NEWLINE} {
581 SCNG(lineno)++;
582 return END_OF_LINE;
583 }
584
585 <INITIAL,ST_VALUE,ST_RAW>{TABS_AND_SPACES}*[;][^\r\n]*{NEWLINE} { /* Comment */
586 BEGIN(INITIAL);
587 SCNG(lineno)++;
588 return END_OF_LINE;
589 }
590
591 <INITIAL>{TABS_AND_SPACES}*[#][^\r\n]*{NEWLINE} { /* #Comment */
592 zend_error(E_DEPRECATED, "Comments starting with '#' are deprecated in %s on line %d", zend_ini_scanner_get_filename(TSRMLS_C), SCNG(lineno));
593 BEGIN(INITIAL);
594 SCNG(lineno)++;
595 return END_OF_LINE;
596 }
597
598 <ST_VALUE,ST_RAW>[^] { /* End of option value (if EOF is reached before EOL */
599 BEGIN(INITIAL);
600 return END_OF_LINE;
601 }
602
603 <*>[^] {
604 return 0;
605 }
606
607 */
608 }
609