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