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 | Author: Zeev Suraski <zeev@php.net> |
16 +----------------------------------------------------------------------+
17 */
18
19 #include "zend.h"
20 #include "zend_sort.h"
21 #include "zend_API.h"
22 #include "zend_ini.h"
23 #include "zend_alloc.h"
24 #include "zend_operators.h"
25 #include "zend_strtod.h"
26 #include "zend_modules.h"
27 #include "zend_smart_str.h"
28 #include <ctype.h>
29
30 static HashTable *registered_zend_ini_directives;
31
32 #define NO_VALUE_PLAINTEXT "no value"
33 #define NO_VALUE_HTML "<i>no value</i>"
34
zend_is_whitespace(char c)35 static inline bool zend_is_whitespace(char c) {
36 return c == ' ' || c == '\t' || c == '\n' || c == '\r' || c == '\v' || c == '\f';
37 }
38
39 /*
40 * hash_apply functions
41 */
zend_remove_ini_entries(zval * el,void * arg)42 static int zend_remove_ini_entries(zval *el, void *arg) /* {{{ */
43 {
44 zend_ini_entry *ini_entry = (zend_ini_entry *)Z_PTR_P(el);
45 int module_number = *(int *)arg;
46
47 return ini_entry->module_number == module_number;
48 }
49 /* }}} */
50
zend_restore_ini_entry_cb(zend_ini_entry * ini_entry,int stage)51 static zend_result zend_restore_ini_entry_cb(zend_ini_entry *ini_entry, int stage) /* {{{ */
52 {
53 zend_result result = FAILURE;
54
55 if (ini_entry->modified) {
56 if (ini_entry->on_modify) {
57 zend_try {
58 /* even if on_modify bails out, we have to continue on with restoring,
59 since there can be allocated variables that would be freed on MM shutdown
60 and would lead to memory corruption later ini entry is modified again */
61 result = ini_entry->on_modify(ini_entry, ini_entry->orig_value, ini_entry->mh_arg1, ini_entry->mh_arg2, ini_entry->mh_arg3, stage);
62 } zend_end_try();
63 }
64 if (stage == ZEND_INI_STAGE_RUNTIME && result == FAILURE) {
65 /* runtime failure is OK */
66 return FAILURE;
67 }
68 if (ini_entry->value != ini_entry->orig_value) {
69 zend_string_release(ini_entry->value);
70 }
71 ini_entry->value = ini_entry->orig_value;
72 ini_entry->modifiable = ini_entry->orig_modifiable;
73 ini_entry->modified = 0;
74 ini_entry->orig_value = NULL;
75 ini_entry->orig_modifiable = 0;
76 }
77 return SUCCESS;
78 }
79 /* }}} */
80
free_ini_entry(zval * zv)81 static void free_ini_entry(zval *zv) /* {{{ */
82 {
83 zend_ini_entry *entry = (zend_ini_entry*)Z_PTR_P(zv);
84
85 zend_string_release_ex(entry->name, 1);
86 if (entry->value) {
87 zend_string_release(entry->value);
88 }
89 if (entry->orig_value) {
90 zend_string_release_ex(entry->orig_value, 1);
91 }
92 free(entry);
93 }
94 /* }}} */
95
96 /*
97 * Startup / shutdown
98 */
zend_ini_startup(void)99 ZEND_API void zend_ini_startup(void) /* {{{ */
100 {
101 registered_zend_ini_directives = (HashTable *) malloc(sizeof(HashTable));
102
103 EG(ini_directives) = registered_zend_ini_directives;
104 EG(modified_ini_directives) = NULL;
105 EG(error_reporting_ini_entry) = NULL;
106 zend_hash_init(registered_zend_ini_directives, 128, NULL, free_ini_entry, 1);
107 }
108 /* }}} */
109
zend_ini_shutdown(void)110 ZEND_API void zend_ini_shutdown(void) /* {{{ */
111 {
112 zend_ini_dtor(EG(ini_directives));
113 }
114 /* }}} */
115
zend_ini_dtor(HashTable * ini_directives)116 ZEND_API void zend_ini_dtor(HashTable *ini_directives) /* {{{ */
117 {
118 zend_hash_destroy(ini_directives);
119 free(ini_directives);
120 }
121 /* }}} */
122
zend_ini_global_shutdown(void)123 ZEND_API void zend_ini_global_shutdown(void) /* {{{ */
124 {
125 zend_hash_destroy(registered_zend_ini_directives);
126 free(registered_zend_ini_directives);
127 }
128 /* }}} */
129
zend_ini_deactivate(void)130 ZEND_API void zend_ini_deactivate(void) /* {{{ */
131 {
132 if (EG(modified_ini_directives)) {
133 zend_ini_entry *ini_entry;
134
135 ZEND_HASH_MAP_FOREACH_PTR(EG(modified_ini_directives), ini_entry) {
136 zend_restore_ini_entry_cb(ini_entry, ZEND_INI_STAGE_DEACTIVATE);
137 } ZEND_HASH_FOREACH_END();
138 zend_hash_destroy(EG(modified_ini_directives));
139 FREE_HASHTABLE(EG(modified_ini_directives));
140 EG(modified_ini_directives) = NULL;
141 }
142 }
143 /* }}} */
144
145 #ifdef ZTS
copy_ini_entry(zval * zv)146 static void copy_ini_entry(zval *zv) /* {{{ */
147 {
148 zend_ini_entry *old_entry = (zend_ini_entry*)Z_PTR_P(zv);
149 zend_ini_entry *new_entry = pemalloc(sizeof(zend_ini_entry), 1);
150
151 Z_PTR_P(zv) = new_entry;
152 memcpy(new_entry, old_entry, sizeof(zend_ini_entry));
153 if (old_entry->name) {
154 new_entry->name = zend_string_dup(old_entry->name, 1);
155 }
156 if (old_entry->value) {
157 new_entry->value = zend_string_dup(old_entry->value, 1);
158 }
159 if (old_entry->orig_value) {
160 new_entry->orig_value = zend_string_dup(old_entry->orig_value, 1);
161 }
162 }
163 /* }}} */
164
zend_copy_ini_directives(void)165 ZEND_API void zend_copy_ini_directives(void) /* {{{ */
166 {
167 EG(modified_ini_directives) = NULL;
168 EG(error_reporting_ini_entry) = NULL;
169 EG(ini_directives) = (HashTable *) malloc(sizeof(HashTable));
170 zend_hash_init(EG(ini_directives), registered_zend_ini_directives->nNumOfElements, NULL, free_ini_entry, 1);
171 zend_hash_copy(EG(ini_directives), registered_zend_ini_directives, copy_ini_entry);
172 }
173 /* }}} */
174 #endif
175
ini_key_compare(Bucket * f,Bucket * s)176 static int ini_key_compare(Bucket *f, Bucket *s) /* {{{ */
177 {
178 if (!f->key && !s->key) { /* both numeric */
179 if (f->h > s->h) {
180 return -1;
181 } else if (f->h < s->h) {
182 return 1;
183 }
184 return 0;
185 } else if (!f->key) { /* f is numeric, s is not */
186 return -1;
187 } else if (!s->key) { /* s is numeric, f is not */
188 return 1;
189 } else { /* both strings */
190 return zend_binary_strcasecmp(ZSTR_VAL(f->key), ZSTR_LEN(f->key), ZSTR_VAL(s->key), ZSTR_LEN(s->key));
191 }
192 }
193 /* }}} */
194
zend_ini_sort_entries(void)195 ZEND_API void zend_ini_sort_entries(void) /* {{{ */
196 {
197 zend_hash_sort(EG(ini_directives), ini_key_compare, 0);
198 }
199 /* }}} */
200
201 /*
202 * Registration / unregistration
203 */
zend_register_ini_entries_ex(const zend_ini_entry_def * ini_entry,int module_number,int module_type)204 ZEND_API zend_result zend_register_ini_entries_ex(const zend_ini_entry_def *ini_entry, int module_number, int module_type) /* {{{ */
205 {
206 zend_ini_entry *p;
207 zval *default_value;
208 HashTable *directives = registered_zend_ini_directives;
209
210 #ifdef ZTS
211 /* if we are called during the request, eg: from dl(),
212 * then we should not touch the global directives table,
213 * and should update the per-(request|thread) version instead.
214 * This solves two problems: one is that ini entries for dl()'d
215 * extensions will now work, and the second is that updating the
216 * global hash here from dl() is not mutex protected and can
217 * lead to death.
218 */
219 if (directives != EG(ini_directives)) {
220 directives = EG(ini_directives);
221 } else {
222 ZEND_ASSERT(module_type == MODULE_PERSISTENT);
223 }
224 #endif
225
226 while (ini_entry->name) {
227 p = pemalloc(sizeof(zend_ini_entry), 1);
228 p->name = zend_string_init_interned(ini_entry->name, ini_entry->name_length, 1);
229 p->on_modify = ini_entry->on_modify;
230 p->mh_arg1 = ini_entry->mh_arg1;
231 p->mh_arg2 = ini_entry->mh_arg2;
232 p->mh_arg3 = ini_entry->mh_arg3;
233 p->value = NULL;
234 p->orig_value = NULL;
235 p->displayer = ini_entry->displayer;
236 p->modifiable = ini_entry->modifiable;
237
238 p->orig_modifiable = 0;
239 p->modified = 0;
240 p->module_number = module_number;
241
242 if (zend_hash_add_ptr(directives, p->name, (void*)p) == NULL) {
243 if (p->name) {
244 zend_string_release_ex(p->name, 1);
245 }
246 pefree(p, true);
247 zend_unregister_ini_entries_ex(module_number, module_type);
248 return FAILURE;
249 }
250 if (((default_value = zend_get_configuration_directive(p->name)) != NULL) &&
251 (!p->on_modify || p->on_modify(p, Z_STR_P(default_value), p->mh_arg1, p->mh_arg2, p->mh_arg3, ZEND_INI_STAGE_STARTUP) == SUCCESS)) {
252
253 p->value = zend_new_interned_string(zend_string_copy(Z_STR_P(default_value)));
254 } else {
255 p->value = ini_entry->value ?
256 zend_string_init_interned(ini_entry->value, ini_entry->value_length, 1) : NULL;
257
258 if (p->on_modify) {
259 p->on_modify(p, p->value, p->mh_arg1, p->mh_arg2, p->mh_arg3, ZEND_INI_STAGE_STARTUP);
260 }
261 }
262 ini_entry++;
263 }
264 return SUCCESS;
265 }
266 /* }}} */
267
zend_register_ini_entries(const zend_ini_entry_def * ini_entry,int module_number)268 ZEND_API zend_result zend_register_ini_entries(const zend_ini_entry_def *ini_entry, int module_number) /* {{{ */
269 {
270 zend_module_entry *module;
271
272 /* Module is likely to be the last one in the list */
273 ZEND_HASH_REVERSE_FOREACH_PTR(&module_registry, module) {
274 if (module->module_number == module_number) {
275 return zend_register_ini_entries_ex(ini_entry, module_number, module->type);
276 }
277 } ZEND_HASH_FOREACH_END();
278
279 return FAILURE;
280 }
281 /* }}} */
282
zend_unregister_ini_entries_ex(int module_number,int module_type)283 ZEND_API void zend_unregister_ini_entries_ex(int module_number, int module_type) /* {{{ */
284 {
285 static HashTable *ini_directives;
286
287 if (module_type == MODULE_TEMPORARY) {
288 ini_directives = EG(ini_directives);
289 } else {
290 ini_directives = registered_zend_ini_directives;
291 }
292
293 zend_hash_apply_with_argument(ini_directives, zend_remove_ini_entries, (void *) &module_number);
294 }
295 /* }}} */
296
zend_unregister_ini_entries(int module_number)297 ZEND_API void zend_unregister_ini_entries(int module_number) /* {{{ */
298 {
299 zend_module_entry *module;
300
301 /* Module is likely to be the last one in the list */
302 ZEND_HASH_REVERSE_FOREACH_PTR(&module_registry, module) {
303 if (module->module_number == module_number) {
304 zend_unregister_ini_entries_ex(module_number, module->type);
305 return;
306 }
307 } ZEND_HASH_FOREACH_END();
308 }
309 /* }}} */
310
311 #ifdef ZTS
zend_ini_refresh_caches(int stage)312 ZEND_API void zend_ini_refresh_caches(int stage) /* {{{ */
313 {
314 zend_ini_entry *p;
315
316 ZEND_HASH_MAP_FOREACH_PTR(EG(ini_directives), p) {
317 if (p->on_modify) {
318 p->on_modify(p, p->value, p->mh_arg1, p->mh_arg2, p->mh_arg3, stage);
319 }
320 } ZEND_HASH_FOREACH_END();
321 }
322 /* }}} */
323 #endif
324
zend_alter_ini_entry(zend_string * name,zend_string * new_value,int modify_type,int stage)325 ZEND_API zend_result zend_alter_ini_entry(zend_string *name, zend_string *new_value, int modify_type, int stage) /* {{{ */
326 {
327
328 return zend_alter_ini_entry_ex(name, new_value, modify_type, stage, 0);
329 }
330 /* }}} */
331
zend_alter_ini_entry_chars(zend_string * name,const char * value,size_t value_length,int modify_type,int stage)332 ZEND_API zend_result zend_alter_ini_entry_chars(zend_string *name, const char *value, size_t value_length, int modify_type, int stage) /* {{{ */
333 {
334 zend_result ret;
335 zend_string *new_value;
336
337 new_value = zend_string_init(value, value_length, !(stage & ZEND_INI_STAGE_IN_REQUEST));
338 ret = zend_alter_ini_entry_ex(name, new_value, modify_type, stage, 0);
339 zend_string_release(new_value);
340 return ret;
341 }
342 /* }}} */
343
zend_alter_ini_entry_chars_ex(zend_string * name,const char * value,size_t value_length,int modify_type,int stage,int force_change)344 ZEND_API zend_result zend_alter_ini_entry_chars_ex(zend_string *name, const char *value, size_t value_length, int modify_type, int stage, int force_change) /* {{{ */
345 {
346 zend_result ret;
347 zend_string *new_value;
348
349 new_value = zend_string_init(value, value_length, !(stage & ZEND_INI_STAGE_IN_REQUEST));
350 ret = zend_alter_ini_entry_ex(name, new_value, modify_type, stage, force_change);
351 zend_string_release(new_value);
352 return ret;
353 }
354 /* }}} */
355
zend_alter_ini_entry_ex(zend_string * name,zend_string * new_value,int modify_type,int stage,bool force_change)356 ZEND_API zend_result zend_alter_ini_entry_ex(zend_string *name, zend_string *new_value, int modify_type, int stage, bool force_change) /* {{{ */
357 {
358 zend_ini_entry *ini_entry;
359 zend_string *duplicate;
360 uint8_t modifiable;
361 bool modified;
362
363 if ((ini_entry = zend_hash_find_ptr(EG(ini_directives), name)) == NULL) {
364 return FAILURE;
365 }
366
367 modifiable = ini_entry->modifiable;
368 modified = ini_entry->modified;
369
370 if (stage == ZEND_INI_STAGE_ACTIVATE && modify_type == ZEND_INI_SYSTEM) {
371 ini_entry->modifiable = ZEND_INI_SYSTEM;
372 }
373
374 if (!force_change) {
375 if (!(ini_entry->modifiable & modify_type)) {
376 return FAILURE;
377 }
378 }
379
380 if (!EG(modified_ini_directives)) {
381 ALLOC_HASHTABLE(EG(modified_ini_directives));
382 zend_hash_init(EG(modified_ini_directives), 8, NULL, NULL, 0);
383 }
384 if (!modified) {
385 ini_entry->orig_value = ini_entry->value;
386 ini_entry->orig_modifiable = modifiable;
387 ini_entry->modified = 1;
388 zend_hash_add_ptr(EG(modified_ini_directives), ini_entry->name, ini_entry);
389 }
390
391 duplicate = zend_string_copy(new_value);
392
393 if (!ini_entry->on_modify
394 || ini_entry->on_modify(ini_entry, duplicate, ini_entry->mh_arg1, ini_entry->mh_arg2, ini_entry->mh_arg3, stage) == SUCCESS) {
395 if (modified && ini_entry->orig_value != ini_entry->value) { /* we already changed the value, free the changed value */
396 zend_string_release(ini_entry->value);
397 }
398 ini_entry->value = duplicate;
399 } else {
400 zend_string_release(duplicate);
401 return FAILURE;
402 }
403
404 return SUCCESS;
405 }
406 /* }}} */
407
zend_restore_ini_entry(zend_string * name,int stage)408 ZEND_API zend_result zend_restore_ini_entry(zend_string *name, int stage) /* {{{ */
409 {
410 zend_ini_entry *ini_entry;
411
412 if ((ini_entry = zend_hash_find_ptr(EG(ini_directives), name)) == NULL ||
413 (stage == ZEND_INI_STAGE_RUNTIME && (ini_entry->modifiable & ZEND_INI_USER) == 0)) {
414 return FAILURE;
415 }
416
417 if (EG(modified_ini_directives)) {
418 if (zend_restore_ini_entry_cb(ini_entry, stage) == 0) {
419 zend_hash_del(EG(modified_ini_directives), name);
420 } else {
421 return FAILURE;
422 }
423 }
424
425 return SUCCESS;
426 }
427 /* }}} */
428
zend_ini_register_displayer(const char * name,uint32_t name_length,void (* displayer)(zend_ini_entry * ini_entry,int type))429 ZEND_API zend_result zend_ini_register_displayer(const char *name, uint32_t name_length, void (*displayer)(zend_ini_entry *ini_entry, int type)) /* {{{ */
430 {
431 zend_ini_entry *ini_entry;
432
433 ini_entry = zend_hash_str_find_ptr(registered_zend_ini_directives, name, name_length);
434 if (ini_entry == NULL) {
435 return FAILURE;
436 }
437
438 ini_entry->displayer = displayer;
439 return SUCCESS;
440 }
441 /* }}} */
442
443 /*
444 * Data retrieval
445 */
446
zend_ini_long(const char * name,size_t name_length,int orig)447 ZEND_API zend_long zend_ini_long(const char *name, size_t name_length, int orig) /* {{{ */
448 {
449 zend_ini_entry *ini_entry;
450
451 ini_entry = zend_hash_str_find_ptr(EG(ini_directives), name, name_length);
452 if (ini_entry) {
453 if (orig && ini_entry->modified) {
454 return (ini_entry->orig_value ? ZEND_STRTOL(ZSTR_VAL(ini_entry->orig_value), NULL, 0) : 0);
455 } else {
456 return (ini_entry->value ? ZEND_STRTOL(ZSTR_VAL(ini_entry->value), NULL, 0) : 0);
457 }
458 }
459
460 return 0;
461 }
462 /* }}} */
463
zend_ini_double(const char * name,size_t name_length,int orig)464 ZEND_API double zend_ini_double(const char *name, size_t name_length, int orig) /* {{{ */
465 {
466 zend_ini_entry *ini_entry;
467
468 ini_entry = zend_hash_str_find_ptr(EG(ini_directives), name, name_length);
469 if (ini_entry) {
470 if (orig && ini_entry->modified) {
471 return (double) (ini_entry->orig_value ? zend_strtod(ZSTR_VAL(ini_entry->orig_value), NULL) : 0.0);
472 } else {
473 return (double) (ini_entry->value ? zend_strtod(ZSTR_VAL(ini_entry->value), NULL) : 0.0);
474 }
475 }
476
477 return 0.0;
478 }
479 /* }}} */
480
zend_ini_string_ex(const char * name,size_t name_length,int orig,bool * exists)481 ZEND_API char *zend_ini_string_ex(const char *name, size_t name_length, int orig, bool *exists) /* {{{ */
482 {
483 zend_ini_entry *ini_entry;
484
485 ini_entry = zend_hash_str_find_ptr(EG(ini_directives), name, name_length);
486 if (ini_entry) {
487 if (exists) {
488 *exists = 1;
489 }
490
491 if (orig && ini_entry->modified) {
492 return ini_entry->orig_value ? ZSTR_VAL(ini_entry->orig_value) : NULL;
493 } else {
494 return ini_entry->value ? ZSTR_VAL(ini_entry->value) : NULL;
495 }
496 } else {
497 if (exists) {
498 *exists = 0;
499 }
500 return NULL;
501 }
502 }
503 /* }}} */
504
zend_ini_string(const char * name,size_t name_length,int orig)505 ZEND_API char *zend_ini_string(const char *name, size_t name_length, int orig) /* {{{ */
506 {
507 bool exists = 1;
508 char *return_value;
509
510 return_value = zend_ini_string_ex(name, name_length, orig, &exists);
511 if (!exists) {
512 return NULL;
513 } else if (!return_value) {
514 return_value = "";
515 }
516 return return_value;
517 }
518 /* }}} */
519
520
zend_ini_str_ex(const char * name,size_t name_length,bool orig,bool * exists)521 ZEND_API zend_string *zend_ini_str_ex(const char *name, size_t name_length, bool orig, bool *exists) /* {{{ */
522 {
523 zend_ini_entry *ini_entry;
524
525 ini_entry = zend_hash_str_find_ptr(EG(ini_directives), name, name_length);
526 if (ini_entry) {
527 if (exists) {
528 *exists = 1;
529 }
530
531 if (orig && ini_entry->modified) {
532 return ini_entry->orig_value ? ini_entry->orig_value : NULL;
533 } else {
534 return ini_entry->value ? ini_entry->value : NULL;
535 }
536 } else {
537 if (exists) {
538 *exists = 0;
539 }
540 return NULL;
541 }
542 }
543 /* }}} */
544
zend_ini_str(const char * name,size_t name_length,bool orig)545 ZEND_API zend_string *zend_ini_str(const char *name, size_t name_length, bool orig) /* {{{ */
546 {
547 bool exists = 1;
548 zend_string *return_value;
549
550 return_value = zend_ini_str_ex(name, name_length, orig, &exists);
551 if (!exists) {
552 return NULL;
553 } else if (!return_value) {
554 return_value = ZSTR_EMPTY_ALLOC();
555 }
556 return return_value;
557 }
558 /* }}} */
559
zend_ini_get_value(zend_string * name)560 ZEND_API zend_string *zend_ini_get_value(zend_string *name) /* {{{ */
561 {
562 zend_ini_entry *ini_entry;
563
564 ini_entry = zend_hash_find_ptr(EG(ini_directives), name);
565 if (ini_entry) {
566 return ini_entry->value ? ini_entry->value : ZSTR_EMPTY_ALLOC();
567 } else {
568 return NULL;
569 }
570 }
571 /* }}} */
572
zend_ini_parse_bool(zend_string * str)573 ZEND_API bool zend_ini_parse_bool(zend_string *str)
574 {
575 if (zend_string_equals_literal_ci(str, "true")
576 || zend_string_equals_literal_ci(str, "yes")
577 || zend_string_equals_literal_ci(str, "on")
578 ) {
579 return 1;
580 } else {
581 return atoi(ZSTR_VAL(str)) != 0;
582 }
583 }
584
585 typedef enum {
586 ZEND_INI_PARSE_QUANTITY_SIGNED,
587 ZEND_INI_PARSE_QUANTITY_UNSIGNED,
588 } zend_ini_parse_quantity_signed_result_t;
589
zend_ini_consume_quantity_prefix(const char * const digits,const char * const str_end)590 static const char *zend_ini_consume_quantity_prefix(const char *const digits, const char *const str_end) {
591 const char *digits_consumed = digits;
592 /* Ignore leading whitespace. */
593 while (digits_consumed < str_end && zend_is_whitespace(*digits_consumed)) {++digits_consumed;}
594 if (digits_consumed[0] == '+' || digits_consumed[0] == '-') {
595 ++digits_consumed;
596 }
597
598 if (digits_consumed[0] == '0' && !isdigit(digits_consumed[1])) {
599 /* Value is just 0 */
600 if ((digits_consumed+1) == str_end) {
601 return digits;
602 }
603
604 switch (digits_consumed[1]) {
605 case 'x':
606 case 'X':
607 case 'o':
608 case 'O':
609 case 'b':
610 case 'B':
611 digits_consumed += 2;
612 break;
613 }
614 }
615 return digits_consumed;
616 }
617
zend_ini_parse_quantity_internal(zend_string * value,zend_ini_parse_quantity_signed_result_t signed_result,zend_string ** errstr)618 static zend_ulong zend_ini_parse_quantity_internal(zend_string *value, zend_ini_parse_quantity_signed_result_t signed_result, zend_string **errstr) /* {{{ */
619 {
620 char *digits_end = NULL;
621 char *str = ZSTR_VAL(value);
622 char *str_end = &str[ZSTR_LEN(value)];
623 char *digits = str;
624 bool overflow = false;
625 zend_ulong factor;
626 smart_str invalid = {0};
627 smart_str interpreted = {0};
628 smart_str chr = {0};
629
630 /* Ignore leading whitespace. ZEND_STRTOL() also skips leading whitespaces,
631 * but we need the position of the first non-whitespace later. */
632 while (digits < str_end && zend_is_whitespace(*digits)) {++digits;}
633
634 /* Ignore trailing whitespace */
635 while (digits < str_end && zend_is_whitespace(*(str_end-1))) {--str_end;}
636
637 if (digits == str_end) {
638 *errstr = NULL;
639 return 0;
640 }
641
642 bool is_negative = false;
643 if (digits[0] == '+') {
644 ++digits;
645 } else if (digits[0] == '-') {
646 is_negative = true;
647 ++digits;
648 }
649
650 /* if there is no digit after +/- */
651 if (!isdigit(digits[0])) {
652 /* Escape the string to avoid null bytes and to make non-printable chars
653 * visible */
654 smart_str_append_escaped(&invalid, ZSTR_VAL(value), ZSTR_LEN(value));
655 smart_str_0(&invalid);
656
657 *errstr = zend_strpprintf(0, "Invalid quantity \"%s\": no valid leading digits, interpreting as \"0\" for backwards compatibility",
658 ZSTR_VAL(invalid.s));
659
660 smart_str_free(&invalid);
661 return 0;
662 }
663
664 int base = 0;
665 if (digits[0] == '0' && !isdigit(digits[1])) {
666 /* Value is just 0 */
667 if ((digits+1) == str_end) {
668 *errstr = NULL;
669 return 0;
670 }
671
672 switch (digits[1]) {
673 /* Multiplier suffixes */
674 case 'g':
675 case 'G':
676 case 'm':
677 case 'M':
678 case 'k':
679 case 'K':
680 goto evaluation;
681 case 'x':
682 case 'X':
683 base = 16;
684 break;
685 case 'o':
686 case 'O':
687 base = 8;
688 break;
689 case 'b':
690 case 'B':
691 base = 2;
692 break;
693 default:
694 *errstr = zend_strpprintf(0, "Invalid prefix \"0%c\", interpreting as \"0\" for backwards compatibility",
695 digits[1]);
696 return 0;
697 }
698 digits += 2;
699 if (UNEXPECTED(digits == str_end)) {
700 /* Escape the string to avoid null bytes and to make non-printable chars
701 * visible */
702 smart_str_append_escaped(&invalid, ZSTR_VAL(value), ZSTR_LEN(value));
703 smart_str_0(&invalid);
704
705 *errstr = zend_strpprintf(0, "Invalid quantity \"%s\": no digits after base prefix, interpreting as \"0\" for backwards compatibility",
706 ZSTR_VAL(invalid.s));
707
708 smart_str_free(&invalid);
709 return 0;
710 }
711 if (UNEXPECTED(digits != zend_ini_consume_quantity_prefix(digits, str_end))) {
712 /* Escape the string to avoid null bytes and to make non-printable chars
713 * visible */
714 smart_str_append_escaped(&invalid, ZSTR_VAL(value), ZSTR_LEN(value));
715 smart_str_0(&invalid);
716
717 *errstr = zend_strpprintf(0, "Invalid quantity \"%s\": no digits after base prefix, interpreting as \"0\" for backwards compatibility",
718 ZSTR_VAL(invalid.s));
719
720 smart_str_free(&invalid);
721 return 0;
722 }
723 }
724 evaluation:
725
726 errno = 0;
727 zend_ulong retval = ZEND_STRTOUL(digits, &digits_end, base);
728
729 if (errno == ERANGE) {
730 overflow = true;
731 } else if (signed_result == ZEND_INI_PARSE_QUANTITY_UNSIGNED) {
732 if (is_negative) {
733 /* Ignore "-1" as it is commonly used as max value, for instance in memory_limit=-1. */
734 if (retval == 1 && digits_end == str_end) {
735 retval = -1;
736 } else {
737 overflow = true;
738 }
739 }
740 } else if (signed_result == ZEND_INI_PARSE_QUANTITY_SIGNED) {
741 /* Handle PHP_INT_MIN case */
742 if (is_negative && retval == ((zend_ulong)ZEND_LONG_MAX +1)) {
743 retval = 0u - retval;
744 } else if ((zend_long) retval < 0) {
745 overflow = true;
746 } else if (is_negative) {
747 retval = 0u - retval;
748 }
749 }
750
751 if (UNEXPECTED(digits_end == digits)) {
752 /* No leading digits */
753
754 /* Escape the string to avoid null bytes and to make non-printable chars
755 * visible */
756 smart_str_append_escaped(&invalid, ZSTR_VAL(value), ZSTR_LEN(value));
757 smart_str_0(&invalid);
758
759 *errstr = zend_strpprintf(0, "Invalid quantity \"%s\": no valid leading digits, interpreting as \"0\" for backwards compatibility",
760 ZSTR_VAL(invalid.s));
761
762 smart_str_free(&invalid);
763 return 0;
764 }
765
766 /* Allow for whitespace between integer portion and any suffix character */
767 while (digits_end < str_end && zend_is_whitespace(*digits_end)) ++digits_end;
768
769 /* No exponent suffix. */
770 if (digits_end == str_end) {
771 goto end;
772 }
773
774 switch (*(str_end-1)) {
775 case 'g':
776 case 'G':
777 factor = 1<<30;
778 break;
779 case 'm':
780 case 'M':
781 factor = 1<<20;
782 break;
783 case 'k':
784 case 'K':
785 factor = 1<<10;
786 break;
787 default:
788 /* Unknown suffix */
789 smart_str_append_escaped(&invalid, ZSTR_VAL(value), ZSTR_LEN(value));
790 smart_str_0(&invalid);
791 smart_str_append_escaped(&interpreted, str, digits_end - str);
792 smart_str_0(&interpreted);
793 smart_str_append_escaped(&chr, str_end-1, 1);
794 smart_str_0(&chr);
795
796 *errstr = zend_strpprintf(0, "Invalid quantity \"%s\": unknown multiplier \"%s\", interpreting as \"%s\" for backwards compatibility",
797 ZSTR_VAL(invalid.s), ZSTR_VAL(chr.s), ZSTR_VAL(interpreted.s));
798
799 smart_str_free(&invalid);
800 smart_str_free(&interpreted);
801 smart_str_free(&chr);
802
803 return retval;
804 }
805
806 if (!overflow) {
807 if (signed_result == ZEND_INI_PARSE_QUANTITY_SIGNED) {
808 zend_long sretval = (zend_long)retval;
809 if (sretval > 0) {
810 overflow = (zend_long)retval > ZEND_LONG_MAX / (zend_long)factor;
811 } else {
812 overflow = (zend_long)retval < ZEND_LONG_MIN / (zend_long)factor;
813 }
814 } else {
815 overflow = retval > ZEND_ULONG_MAX / factor;
816 }
817 }
818
819 retval *= factor;
820
821 if (UNEXPECTED(digits_end != str_end-1)) {
822 /* More than one character in suffix */
823 smart_str_append_escaped(&invalid, ZSTR_VAL(value), ZSTR_LEN(value));
824 smart_str_0(&invalid);
825 smart_str_append_escaped(&interpreted, str, digits_end - str);
826 smart_str_0(&interpreted);
827 smart_str_append_escaped(&chr, str_end-1, 1);
828 smart_str_0(&chr);
829
830 *errstr = zend_strpprintf(0, "Invalid quantity \"%s\", interpreting as \"%s%s\" for backwards compatibility",
831 ZSTR_VAL(invalid.s), ZSTR_VAL(interpreted.s), ZSTR_VAL(chr.s));
832
833 smart_str_free(&invalid);
834 smart_str_free(&interpreted);
835 smart_str_free(&chr);
836
837 return retval;
838 }
839
840 end:
841 if (UNEXPECTED(overflow)) {
842 smart_str_append_escaped(&invalid, ZSTR_VAL(value), ZSTR_LEN(value));
843 smart_str_0(&invalid);
844
845 /* Not specifying the resulting value here because the caller may make
846 * additional conversions. Not specifying the allowed range
847 * because the caller may do narrower range checks. */
848 *errstr = zend_strpprintf(0, "Invalid quantity \"%s\": value is out of range, using overflow result for backwards compatibility",
849 ZSTR_VAL(invalid.s));
850
851 smart_str_free(&invalid);
852 smart_str_free(&interpreted);
853 smart_str_free(&chr);
854
855 return retval;
856 }
857
858 *errstr = NULL;
859 return retval;
860 }
861 /* }}} */
862
zend_ini_parse_quantity(zend_string * value,zend_string ** errstr)863 ZEND_API zend_long zend_ini_parse_quantity(zend_string *value, zend_string **errstr) /* {{{ */
864 {
865 return (zend_long) zend_ini_parse_quantity_internal(value, ZEND_INI_PARSE_QUANTITY_SIGNED, errstr);
866 }
867 /* }}} */
868
zend_ini_parse_uquantity(zend_string * value,zend_string ** errstr)869 ZEND_API zend_ulong zend_ini_parse_uquantity(zend_string *value, zend_string **errstr) /* {{{ */
870 {
871 return zend_ini_parse_quantity_internal(value, ZEND_INI_PARSE_QUANTITY_UNSIGNED, errstr);
872 }
873 /* }}} */
874
zend_ini_parse_quantity_warn(zend_string * value,zend_string * setting)875 ZEND_API zend_long zend_ini_parse_quantity_warn(zend_string *value, zend_string *setting) /* {{{ */
876 {
877 zend_string *errstr;
878 zend_long retval = zend_ini_parse_quantity(value, &errstr);
879
880 if (errstr) {
881 zend_error(E_WARNING, "Invalid \"%s\" setting. %s", ZSTR_VAL(setting), ZSTR_VAL(errstr));
882 zend_string_release(errstr);
883 }
884
885 return retval;
886 }
887 /* }}} */
888
zend_ini_parse_uquantity_warn(zend_string * value,zend_string * setting)889 ZEND_API zend_ulong zend_ini_parse_uquantity_warn(zend_string *value, zend_string *setting) /* {{{ */
890 {
891 zend_string *errstr;
892 zend_ulong retval = zend_ini_parse_uquantity(value, &errstr);
893
894 if (errstr) {
895 zend_error(E_WARNING, "Invalid \"%s\" setting. %s", ZSTR_VAL(setting), ZSTR_VAL(errstr));
896 zend_string_release(errstr);
897 }
898
899 return retval;
900 }
901 /* }}} */
902
ZEND_INI_DISP(zend_ini_boolean_displayer_cb)903 ZEND_INI_DISP(zend_ini_boolean_displayer_cb) /* {{{ */
904 {
905 int value;
906 zend_string *tmp_value;
907
908 if (type == ZEND_INI_DISPLAY_ORIG && ini_entry->modified) {
909 tmp_value = (ini_entry->orig_value ? ini_entry->orig_value : NULL );
910 } else if (ini_entry->value) {
911 tmp_value = ini_entry->value;
912 } else {
913 tmp_value = NULL;
914 }
915
916 if (tmp_value) {
917 value = zend_ini_parse_bool(tmp_value);
918 } else {
919 value = 0;
920 }
921
922 if (value) {
923 ZEND_PUTS("On");
924 } else {
925 ZEND_PUTS("Off");
926 }
927 }
928 /* }}} */
929
ZEND_INI_DISP(zend_ini_color_displayer_cb)930 ZEND_INI_DISP(zend_ini_color_displayer_cb) /* {{{ */
931 {
932 char *value;
933
934 if (type == ZEND_INI_DISPLAY_ORIG && ini_entry->modified) {
935 value = ZSTR_VAL(ini_entry->orig_value);
936 } else if (ini_entry->value) {
937 value = ZSTR_VAL(ini_entry->value);
938 } else {
939 value = NULL;
940 }
941 if (value) {
942 if (zend_uv.html_errors) {
943 zend_printf("<span style=\"color: %s\">%s</span>", value, value);
944 } else {
945 ZEND_PUTS(value);
946 }
947 } else {
948 if (zend_uv.html_errors) {
949 ZEND_PUTS(NO_VALUE_HTML);
950 } else {
951 ZEND_PUTS(NO_VALUE_PLAINTEXT);
952 }
953 }
954 }
955 /* }}} */
956
ZEND_INI_DISP(display_link_numbers)957 ZEND_INI_DISP(display_link_numbers) /* {{{ */
958 {
959 char *value;
960
961 if (type == ZEND_INI_DISPLAY_ORIG && ini_entry->modified) {
962 value = ZSTR_VAL(ini_entry->orig_value);
963 } else if (ini_entry->value) {
964 value = ZSTR_VAL(ini_entry->value);
965 } else {
966 value = NULL;
967 }
968
969 if (value) {
970 if (atoi(value) == -1) {
971 ZEND_PUTS("Unlimited");
972 } else {
973 zend_printf("%s", value);
974 }
975 }
976 }
977 /* }}} */
978
979 /* Standard message handlers */
ZEND_INI_MH(OnUpdateBool)980 ZEND_API ZEND_INI_MH(OnUpdateBool) /* {{{ */
981 {
982 bool *p = (bool *) ZEND_INI_GET_ADDR();
983 *p = zend_ini_parse_bool(new_value);
984 return SUCCESS;
985 }
986 /* }}} */
987
ZEND_INI_MH(OnUpdateLong)988 ZEND_API ZEND_INI_MH(OnUpdateLong) /* {{{ */
989 {
990 zend_long *p = (zend_long *) ZEND_INI_GET_ADDR();
991 *p = zend_ini_parse_quantity_warn(new_value, entry->name);
992 return SUCCESS;
993 }
994 /* }}} */
995
ZEND_INI_MH(OnUpdateLongGEZero)996 ZEND_API ZEND_INI_MH(OnUpdateLongGEZero) /* {{{ */
997 {
998 zend_long tmp = zend_ini_parse_quantity_warn(new_value, entry->name);
999 if (tmp < 0) {
1000 return FAILURE;
1001 }
1002
1003 zend_long *p = (zend_long *) ZEND_INI_GET_ADDR();
1004 *p = tmp;
1005
1006 return SUCCESS;
1007 }
1008 /* }}} */
1009
ZEND_INI_MH(OnUpdateReal)1010 ZEND_API ZEND_INI_MH(OnUpdateReal) /* {{{ */
1011 {
1012 double *p = (double *) ZEND_INI_GET_ADDR();
1013 *p = zend_strtod(ZSTR_VAL(new_value), NULL);
1014 return SUCCESS;
1015 }
1016 /* }}} */
1017
ZEND_INI_MH(OnUpdateString)1018 ZEND_API ZEND_INI_MH(OnUpdateString) /* {{{ */
1019 {
1020 char **p = (char **) ZEND_INI_GET_ADDR();
1021 *p = new_value ? ZSTR_VAL(new_value) : NULL;
1022 return SUCCESS;
1023 }
1024 /* }}} */
1025
ZEND_INI_MH(OnUpdateStringUnempty)1026 ZEND_API ZEND_INI_MH(OnUpdateStringUnempty) /* {{{ */
1027 {
1028 if (new_value && !ZSTR_VAL(new_value)[0]) {
1029 return FAILURE;
1030 }
1031
1032 char **p = (char **) ZEND_INI_GET_ADDR();
1033 *p = new_value ? ZSTR_VAL(new_value) : NULL;
1034 return SUCCESS;
1035 }
1036 /* }}} */
1037
ZEND_INI_MH(OnUpdateStr)1038 ZEND_API ZEND_INI_MH(OnUpdateStr) /* {{{ */
1039 {
1040 zend_string **p = (zend_string **) ZEND_INI_GET_ADDR();
1041 *p = new_value;
1042 return SUCCESS;
1043 }
1044 /* }}} */
1045
ZEND_INI_MH(OnUpdateStrNotEmpty)1046 ZEND_API ZEND_INI_MH(OnUpdateStrNotEmpty) /* {{{ */
1047 {
1048 if (new_value && ZSTR_LEN(new_value) == 0) {
1049 return FAILURE;
1050 }
1051
1052 zend_string **p = (zend_string **) ZEND_INI_GET_ADDR();
1053 *p = new_value;
1054 return SUCCESS;
1055 }
1056 /* }}} */
1057