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