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 zend_unregister_ini_entries_ex(module_number, module_type);
247 return FAILURE;
248 }
249 if (((default_value = zend_get_configuration_directive(p->name)) != NULL) &&
250 (!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)) {
251
252 p->value = zend_new_interned_string(zend_string_copy(Z_STR_P(default_value)));
253 } else {
254 p->value = ini_entry->value ?
255 zend_string_init_interned(ini_entry->value, ini_entry->value_length, 1) : NULL;
256
257 if (p->on_modify) {
258 p->on_modify(p, p->value, p->mh_arg1, p->mh_arg2, p->mh_arg3, ZEND_INI_STAGE_STARTUP);
259 }
260 }
261 ini_entry++;
262 }
263 return SUCCESS;
264 }
265 /* }}} */
266
zend_register_ini_entries(const zend_ini_entry_def * ini_entry,int module_number)267 ZEND_API zend_result zend_register_ini_entries(const zend_ini_entry_def *ini_entry, int module_number) /* {{{ */
268 {
269 zend_module_entry *module;
270
271 /* Module is likely to be the last one in the list */
272 ZEND_HASH_REVERSE_FOREACH_PTR(&module_registry, module) {
273 if (module->module_number == module_number) {
274 return zend_register_ini_entries_ex(ini_entry, module_number, module->type);
275 }
276 } ZEND_HASH_FOREACH_END();
277
278 return FAILURE;
279 }
280 /* }}} */
281
zend_unregister_ini_entries_ex(int module_number,int module_type)282 ZEND_API void zend_unregister_ini_entries_ex(int module_number, int module_type) /* {{{ */
283 {
284 static HashTable *ini_directives;
285
286 if (module_type == MODULE_TEMPORARY) {
287 ini_directives = EG(ini_directives);
288 } else {
289 ini_directives = registered_zend_ini_directives;
290 }
291
292 zend_hash_apply_with_argument(ini_directives, zend_remove_ini_entries, (void *) &module_number);
293 }
294 /* }}} */
295
zend_unregister_ini_entries(int module_number)296 ZEND_API void zend_unregister_ini_entries(int module_number) /* {{{ */
297 {
298 zend_module_entry *module;
299
300 /* Module is likely to be the last one in the list */
301 ZEND_HASH_REVERSE_FOREACH_PTR(&module_registry, module) {
302 if (module->module_number == module_number) {
303 zend_unregister_ini_entries_ex(module_number, module->type);
304 return;
305 }
306 } ZEND_HASH_FOREACH_END();
307 }
308 /* }}} */
309
310 #ifdef ZTS
zend_ini_refresh_caches(int stage)311 ZEND_API void zend_ini_refresh_caches(int stage) /* {{{ */
312 {
313 zend_ini_entry *p;
314
315 ZEND_HASH_MAP_FOREACH_PTR(EG(ini_directives), p) {
316 if (p->on_modify) {
317 p->on_modify(p, p->value, p->mh_arg1, p->mh_arg2, p->mh_arg3, stage);
318 }
319 } ZEND_HASH_FOREACH_END();
320 }
321 /* }}} */
322 #endif
323
zend_alter_ini_entry(zend_string * name,zend_string * new_value,int modify_type,int stage)324 ZEND_API zend_result zend_alter_ini_entry(zend_string *name, zend_string *new_value, int modify_type, int stage) /* {{{ */
325 {
326
327 return zend_alter_ini_entry_ex(name, new_value, modify_type, stage, 0);
328 }
329 /* }}} */
330
zend_alter_ini_entry_chars(zend_string * name,const char * value,size_t value_length,int modify_type,int stage)331 ZEND_API zend_result zend_alter_ini_entry_chars(zend_string *name, const char *value, size_t value_length, int modify_type, int stage) /* {{{ */
332 {
333 zend_result ret;
334 zend_string *new_value;
335
336 new_value = zend_string_init(value, value_length, !(stage & ZEND_INI_STAGE_IN_REQUEST));
337 ret = zend_alter_ini_entry_ex(name, new_value, modify_type, stage, 0);
338 zend_string_release(new_value);
339 return ret;
340 }
341 /* }}} */
342
zend_alter_ini_entry_chars_ex(zend_string * name,const char * value,size_t value_length,int modify_type,int stage,int force_change)343 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) /* {{{ */
344 {
345 zend_result ret;
346 zend_string *new_value;
347
348 new_value = zend_string_init(value, value_length, !(stage & ZEND_INI_STAGE_IN_REQUEST));
349 ret = zend_alter_ini_entry_ex(name, new_value, modify_type, stage, force_change);
350 zend_string_release(new_value);
351 return ret;
352 }
353 /* }}} */
354
zend_alter_ini_entry_ex(zend_string * name,zend_string * new_value,int modify_type,int stage,bool force_change)355 ZEND_API zend_result zend_alter_ini_entry_ex(zend_string *name, zend_string *new_value, int modify_type, int stage, bool force_change) /* {{{ */
356 {
357 zend_ini_entry *ini_entry;
358 zend_string *duplicate;
359 uint8_t modifiable;
360 bool modified;
361
362 if ((ini_entry = zend_hash_find_ptr(EG(ini_directives), name)) == NULL) {
363 return FAILURE;
364 }
365
366 modifiable = ini_entry->modifiable;
367 modified = ini_entry->modified;
368
369 if (stage == ZEND_INI_STAGE_ACTIVATE && modify_type == ZEND_INI_SYSTEM) {
370 ini_entry->modifiable = ZEND_INI_SYSTEM;
371 }
372
373 if (!force_change) {
374 if (!(ini_entry->modifiable & modify_type)) {
375 return FAILURE;
376 }
377 }
378
379 if (!EG(modified_ini_directives)) {
380 ALLOC_HASHTABLE(EG(modified_ini_directives));
381 zend_hash_init(EG(modified_ini_directives), 8, NULL, NULL, 0);
382 }
383 if (!modified) {
384 ini_entry->orig_value = ini_entry->value;
385 ini_entry->orig_modifiable = modifiable;
386 ini_entry->modified = 1;
387 zend_hash_add_ptr(EG(modified_ini_directives), ini_entry->name, ini_entry);
388 }
389
390 duplicate = zend_string_copy(new_value);
391
392 if (!ini_entry->on_modify
393 || ini_entry->on_modify(ini_entry, duplicate, ini_entry->mh_arg1, ini_entry->mh_arg2, ini_entry->mh_arg3, stage) == SUCCESS) {
394 if (modified && ini_entry->orig_value != ini_entry->value) { /* we already changed the value, free the changed value */
395 zend_string_release(ini_entry->value);
396 }
397 ini_entry->value = duplicate;
398 } else {
399 zend_string_release(duplicate);
400 return FAILURE;
401 }
402
403 return SUCCESS;
404 }
405 /* }}} */
406
zend_restore_ini_entry(zend_string * name,int stage)407 ZEND_API zend_result zend_restore_ini_entry(zend_string *name, int stage) /* {{{ */
408 {
409 zend_ini_entry *ini_entry;
410
411 if ((ini_entry = zend_hash_find_ptr(EG(ini_directives), name)) == NULL ||
412 (stage == ZEND_INI_STAGE_RUNTIME && (ini_entry->modifiable & ZEND_INI_USER) == 0)) {
413 return FAILURE;
414 }
415
416 if (EG(modified_ini_directives)) {
417 if (zend_restore_ini_entry_cb(ini_entry, stage) == 0) {
418 zend_hash_del(EG(modified_ini_directives), name);
419 } else {
420 return FAILURE;
421 }
422 }
423
424 return SUCCESS;
425 }
426 /* }}} */
427
zend_ini_register_displayer(const char * name,uint32_t name_length,void (* displayer)(zend_ini_entry * ini_entry,int type))428 ZEND_API zend_result zend_ini_register_displayer(const char *name, uint32_t name_length, void (*displayer)(zend_ini_entry *ini_entry, int type)) /* {{{ */
429 {
430 zend_ini_entry *ini_entry;
431
432 ini_entry = zend_hash_str_find_ptr(registered_zend_ini_directives, name, name_length);
433 if (ini_entry == NULL) {
434 return FAILURE;
435 }
436
437 ini_entry->displayer = displayer;
438 return SUCCESS;
439 }
440 /* }}} */
441
442 /*
443 * Data retrieval
444 */
445
zend_ini_long(const char * name,size_t name_length,int orig)446 ZEND_API zend_long zend_ini_long(const char *name, size_t name_length, int orig) /* {{{ */
447 {
448 zend_ini_entry *ini_entry;
449
450 ini_entry = zend_hash_str_find_ptr(EG(ini_directives), name, name_length);
451 if (ini_entry) {
452 if (orig && ini_entry->modified) {
453 return (ini_entry->orig_value ? ZEND_STRTOL(ZSTR_VAL(ini_entry->orig_value), NULL, 0) : 0);
454 } else {
455 return (ini_entry->value ? ZEND_STRTOL(ZSTR_VAL(ini_entry->value), NULL, 0) : 0);
456 }
457 }
458
459 return 0;
460 }
461 /* }}} */
462
zend_ini_double(const char * name,size_t name_length,int orig)463 ZEND_API double zend_ini_double(const char *name, size_t name_length, int orig) /* {{{ */
464 {
465 zend_ini_entry *ini_entry;
466
467 ini_entry = zend_hash_str_find_ptr(EG(ini_directives), name, name_length);
468 if (ini_entry) {
469 if (orig && ini_entry->modified) {
470 return (double) (ini_entry->orig_value ? zend_strtod(ZSTR_VAL(ini_entry->orig_value), NULL) : 0.0);
471 } else {
472 return (double) (ini_entry->value ? zend_strtod(ZSTR_VAL(ini_entry->value), NULL) : 0.0);
473 }
474 }
475
476 return 0.0;
477 }
478 /* }}} */
479
zend_ini_string_ex(const char * name,size_t name_length,int orig,bool * exists)480 ZEND_API char *zend_ini_string_ex(const char *name, size_t name_length, int orig, bool *exists) /* {{{ */
481 {
482 zend_ini_entry *ini_entry;
483
484 ini_entry = zend_hash_str_find_ptr(EG(ini_directives), name, name_length);
485 if (ini_entry) {
486 if (exists) {
487 *exists = 1;
488 }
489
490 if (orig && ini_entry->modified) {
491 return ini_entry->orig_value ? ZSTR_VAL(ini_entry->orig_value) : NULL;
492 } else {
493 return ini_entry->value ? ZSTR_VAL(ini_entry->value) : NULL;
494 }
495 } else {
496 if (exists) {
497 *exists = 0;
498 }
499 return NULL;
500 }
501 }
502 /* }}} */
503
zend_ini_string(const char * name,size_t name_length,int orig)504 ZEND_API char *zend_ini_string(const char *name, size_t name_length, int orig) /* {{{ */
505 {
506 bool exists = 1;
507 char *return_value;
508
509 return_value = zend_ini_string_ex(name, name_length, orig, &exists);
510 if (!exists) {
511 return NULL;
512 } else if (!return_value) {
513 return_value = "";
514 }
515 return return_value;
516 }
517 /* }}} */
518
519
zend_ini_str_ex(const char * name,size_t name_length,bool orig,bool * exists)520 ZEND_API zend_string *zend_ini_str_ex(const char *name, size_t name_length, bool orig, bool *exists) /* {{{ */
521 {
522 zend_ini_entry *ini_entry;
523
524 ini_entry = zend_hash_str_find_ptr(EG(ini_directives), name, name_length);
525 if (ini_entry) {
526 if (exists) {
527 *exists = 1;
528 }
529
530 if (orig && ini_entry->modified) {
531 return ini_entry->orig_value ? ini_entry->orig_value : NULL;
532 } else {
533 return ini_entry->value ? ini_entry->value : NULL;
534 }
535 } else {
536 if (exists) {
537 *exists = 0;
538 }
539 return NULL;
540 }
541 }
542 /* }}} */
543
zend_ini_str(const char * name,size_t name_length,bool orig)544 ZEND_API zend_string *zend_ini_str(const char *name, size_t name_length, bool orig) /* {{{ */
545 {
546 bool exists = 1;
547 zend_string *return_value;
548
549 return_value = zend_ini_str_ex(name, name_length, orig, &exists);
550 if (!exists) {
551 return NULL;
552 } else if (!return_value) {
553 return_value = ZSTR_EMPTY_ALLOC();
554 }
555 return return_value;
556 }
557 /* }}} */
558
zend_ini_get_value(zend_string * name)559 ZEND_API zend_string *zend_ini_get_value(zend_string *name) /* {{{ */
560 {
561 zend_ini_entry *ini_entry;
562
563 ini_entry = zend_hash_find_ptr(EG(ini_directives), name);
564 if (ini_entry) {
565 return ini_entry->value ? ini_entry->value : ZSTR_EMPTY_ALLOC();
566 } else {
567 return NULL;
568 }
569 }
570 /* }}} */
571
zend_ini_parse_bool(zend_string * str)572 ZEND_API bool zend_ini_parse_bool(zend_string *str)
573 {
574 if (zend_string_equals_literal_ci(str, "true")
575 || zend_string_equals_literal_ci(str, "yes")
576 || zend_string_equals_literal_ci(str, "on")
577 ) {
578 return 1;
579 } else {
580 return atoi(ZSTR_VAL(str)) != 0;
581 }
582 }
583
584 typedef enum {
585 ZEND_INI_PARSE_QUANTITY_SIGNED,
586 ZEND_INI_PARSE_QUANTITY_UNSIGNED,
587 } zend_ini_parse_quantity_signed_result_t;
588
zend_ini_consume_quantity_prefix(const char * const digits,const char * const str_end)589 static const char *zend_ini_consume_quantity_prefix(const char *const digits, const char *const str_end) {
590 const char *digits_consumed = digits;
591 /* Ignore leading whitespace. */
592 while (digits_consumed < str_end && zend_is_whitespace(*digits_consumed)) {++digits_consumed;}
593 if (digits_consumed[0] == '+' || digits_consumed[0] == '-') {
594 ++digits_consumed;
595 }
596
597 if (digits_consumed[0] == '0' && !isdigit(digits_consumed[1])) {
598 /* Value is just 0 */
599 if ((digits_consumed+1) == str_end) {
600 return digits;
601 }
602
603 switch (digits_consumed[1]) {
604 case 'x':
605 case 'X':
606 case 'o':
607 case 'O':
608 case 'b':
609 case 'B':
610 digits_consumed += 2;
611 break;
612 }
613 }
614 return digits_consumed;
615 }
616
zend_ini_parse_quantity_internal(zend_string * value,zend_ini_parse_quantity_signed_result_t signed_result,zend_string ** errstr)617 static zend_ulong zend_ini_parse_quantity_internal(zend_string *value, zend_ini_parse_quantity_signed_result_t signed_result, zend_string **errstr) /* {{{ */
618 {
619 char *digits_end = NULL;
620 char *str = ZSTR_VAL(value);
621 char *str_end = &str[ZSTR_LEN(value)];
622 char *digits = str;
623 bool overflow = false;
624 zend_ulong factor;
625 smart_str invalid = {0};
626 smart_str interpreted = {0};
627 smart_str chr = {0};
628
629 /* Ignore leading whitespace. ZEND_STRTOL() also skips leading whitespaces,
630 * but we need the position of the first non-whitespace later. */
631 while (digits < str_end && zend_is_whitespace(*digits)) {++digits;}
632
633 /* Ignore trailing whitespace */
634 while (digits < str_end && zend_is_whitespace(*(str_end-1))) {--str_end;}
635
636 if (digits == str_end) {
637 *errstr = NULL;
638 return 0;
639 }
640
641 bool is_negative = false;
642 if (digits[0] == '+') {
643 ++digits;
644 } else if (digits[0] == '-') {
645 is_negative = true;
646 ++digits;
647 }
648
649 /* if there is no digit after +/- */
650 if (!isdigit(digits[0])) {
651 /* Escape the string to avoid null bytes and to make non-printable chars
652 * visible */
653 smart_str_append_escaped(&invalid, ZSTR_VAL(value), ZSTR_LEN(value));
654 smart_str_0(&invalid);
655
656 *errstr = zend_strpprintf(0, "Invalid quantity \"%s\": no valid leading digits, interpreting as \"0\" for backwards compatibility",
657 ZSTR_VAL(invalid.s));
658
659 smart_str_free(&invalid);
660 return 0;
661 }
662
663 int base = 0;
664 if (digits[0] == '0' && !isdigit(digits[1])) {
665 /* Value is just 0 */
666 if ((digits+1) == str_end) {
667 *errstr = NULL;
668 return 0;
669 }
670
671 switch (digits[1]) {
672 /* Multiplier suffixes */
673 case 'g':
674 case 'G':
675 case 'm':
676 case 'M':
677 case 'k':
678 case 'K':
679 goto evaluation;
680 case 'x':
681 case 'X':
682 base = 16;
683 break;
684 case 'o':
685 case 'O':
686 base = 8;
687 break;
688 case 'b':
689 case 'B':
690 base = 2;
691 break;
692 default:
693 *errstr = zend_strpprintf(0, "Invalid prefix \"0%c\", interpreting as \"0\" for backwards compatibility",
694 digits[1]);
695 return 0;
696 }
697 digits += 2;
698 if (UNEXPECTED(digits == str_end)) {
699 /* Escape the string to avoid null bytes and to make non-printable chars
700 * visible */
701 smart_str_append_escaped(&invalid, ZSTR_VAL(value), ZSTR_LEN(value));
702 smart_str_0(&invalid);
703
704 *errstr = zend_strpprintf(0, "Invalid quantity \"%s\": no digits after base prefix, interpreting as \"0\" for backwards compatibility",
705 ZSTR_VAL(invalid.s));
706
707 smart_str_free(&invalid);
708 return 0;
709 }
710 if (UNEXPECTED(digits != zend_ini_consume_quantity_prefix(digits, str_end))) {
711 /* Escape the string to avoid null bytes and to make non-printable chars
712 * visible */
713 smart_str_append_escaped(&invalid, ZSTR_VAL(value), ZSTR_LEN(value));
714 smart_str_0(&invalid);
715
716 *errstr = zend_strpprintf(0, "Invalid quantity \"%s\": no digits after base prefix, interpreting as \"0\" for backwards compatibility",
717 ZSTR_VAL(invalid.s));
718
719 smart_str_free(&invalid);
720 return 0;
721 }
722 }
723 evaluation:
724
725 errno = 0;
726 zend_ulong retval = ZEND_STRTOUL(digits, &digits_end, base);
727
728 if (errno == ERANGE) {
729 overflow = true;
730 } else if (signed_result == ZEND_INI_PARSE_QUANTITY_UNSIGNED) {
731 if (is_negative) {
732 /* Ignore "-1" as it is commonly used as max value, for instance in memory_limit=-1. */
733 if (retval == 1 && digits_end == str_end) {
734 retval = -1;
735 } else {
736 overflow = true;
737 }
738 }
739 } else if (signed_result == ZEND_INI_PARSE_QUANTITY_SIGNED) {
740 /* Handle PHP_INT_MIN case */
741 if (is_negative && retval == ((zend_ulong)ZEND_LONG_MAX +1)) {
742 retval = 0u - retval;
743 } else if ((zend_long) retval < 0) {
744 overflow = true;
745 } else if (is_negative) {
746 retval = 0u - retval;
747 }
748 }
749
750 if (UNEXPECTED(digits_end == digits)) {
751 /* No leading digits */
752
753 /* Escape the string to avoid null bytes and to make non-printable chars
754 * visible */
755 smart_str_append_escaped(&invalid, ZSTR_VAL(value), ZSTR_LEN(value));
756 smart_str_0(&invalid);
757
758 *errstr = zend_strpprintf(0, "Invalid quantity \"%s\": no valid leading digits, interpreting as \"0\" for backwards compatibility",
759 ZSTR_VAL(invalid.s));
760
761 smart_str_free(&invalid);
762 return 0;
763 }
764
765 /* Allow for whitespace between integer portion and any suffix character */
766 while (digits_end < str_end && zend_is_whitespace(*digits_end)) ++digits_end;
767
768 /* No exponent suffix. */
769 if (digits_end == str_end) {
770 goto end;
771 }
772
773 switch (*(str_end-1)) {
774 case 'g':
775 case 'G':
776 factor = 1<<30;
777 break;
778 case 'm':
779 case 'M':
780 factor = 1<<20;
781 break;
782 case 'k':
783 case 'K':
784 factor = 1<<10;
785 break;
786 default:
787 /* Unknown suffix */
788 smart_str_append_escaped(&invalid, ZSTR_VAL(value), ZSTR_LEN(value));
789 smart_str_0(&invalid);
790 smart_str_append_escaped(&interpreted, str, digits_end - str);
791 smart_str_0(&interpreted);
792 smart_str_append_escaped(&chr, str_end-1, 1);
793 smart_str_0(&chr);
794
795 *errstr = zend_strpprintf(0, "Invalid quantity \"%s\": unknown multiplier \"%s\", interpreting as \"%s\" for backwards compatibility",
796 ZSTR_VAL(invalid.s), ZSTR_VAL(chr.s), ZSTR_VAL(interpreted.s));
797
798 smart_str_free(&invalid);
799 smart_str_free(&interpreted);
800 smart_str_free(&chr);
801
802 return retval;
803 }
804
805 if (!overflow) {
806 if (signed_result == ZEND_INI_PARSE_QUANTITY_SIGNED) {
807 zend_long sretval = (zend_long)retval;
808 if (sretval > 0) {
809 overflow = (zend_long)retval > ZEND_LONG_MAX / (zend_long)factor;
810 } else {
811 overflow = (zend_long)retval < ZEND_LONG_MIN / (zend_long)factor;
812 }
813 } else {
814 overflow = retval > ZEND_ULONG_MAX / factor;
815 }
816 }
817
818 retval *= factor;
819
820 if (UNEXPECTED(digits_end != str_end-1)) {
821 /* More than one character in suffix */
822 smart_str_append_escaped(&invalid, ZSTR_VAL(value), ZSTR_LEN(value));
823 smart_str_0(&invalid);
824 smart_str_append_escaped(&interpreted, str, digits_end - str);
825 smart_str_0(&interpreted);
826 smart_str_append_escaped(&chr, str_end-1, 1);
827 smart_str_0(&chr);
828
829 *errstr = zend_strpprintf(0, "Invalid quantity \"%s\", interpreting as \"%s%s\" for backwards compatibility",
830 ZSTR_VAL(invalid.s), ZSTR_VAL(interpreted.s), ZSTR_VAL(chr.s));
831
832 smart_str_free(&invalid);
833 smart_str_free(&interpreted);
834 smart_str_free(&chr);
835
836 return retval;
837 }
838
839 end:
840 if (UNEXPECTED(overflow)) {
841 smart_str_append_escaped(&invalid, ZSTR_VAL(value), ZSTR_LEN(value));
842 smart_str_0(&invalid);
843
844 /* Not specifying the resulting value here because the caller may make
845 * additional conversions. Not specifying the allowed range
846 * because the caller may do narrower range checks. */
847 *errstr = zend_strpprintf(0, "Invalid quantity \"%s\": value is out of range, using overflow result for backwards compatibility",
848 ZSTR_VAL(invalid.s));
849
850 smart_str_free(&invalid);
851 smart_str_free(&interpreted);
852 smart_str_free(&chr);
853
854 return retval;
855 }
856
857 *errstr = NULL;
858 return retval;
859 }
860 /* }}} */
861
zend_ini_parse_quantity(zend_string * value,zend_string ** errstr)862 ZEND_API zend_long zend_ini_parse_quantity(zend_string *value, zend_string **errstr) /* {{{ */
863 {
864 return (zend_long) zend_ini_parse_quantity_internal(value, ZEND_INI_PARSE_QUANTITY_SIGNED, errstr);
865 }
866 /* }}} */
867
zend_ini_parse_uquantity(zend_string * value,zend_string ** errstr)868 ZEND_API zend_ulong zend_ini_parse_uquantity(zend_string *value, zend_string **errstr) /* {{{ */
869 {
870 return zend_ini_parse_quantity_internal(value, ZEND_INI_PARSE_QUANTITY_UNSIGNED, errstr);
871 }
872 /* }}} */
873
zend_ini_parse_quantity_warn(zend_string * value,zend_string * setting)874 ZEND_API zend_long zend_ini_parse_quantity_warn(zend_string *value, zend_string *setting) /* {{{ */
875 {
876 zend_string *errstr;
877 zend_long retval = zend_ini_parse_quantity(value, &errstr);
878
879 if (errstr) {
880 zend_error(E_WARNING, "Invalid \"%s\" setting. %s", ZSTR_VAL(setting), ZSTR_VAL(errstr));
881 zend_string_release(errstr);
882 }
883
884 return retval;
885 }
886 /* }}} */
887
zend_ini_parse_uquantity_warn(zend_string * value,zend_string * setting)888 ZEND_API zend_ulong zend_ini_parse_uquantity_warn(zend_string *value, zend_string *setting) /* {{{ */
889 {
890 zend_string *errstr;
891 zend_ulong retval = zend_ini_parse_uquantity(value, &errstr);
892
893 if (errstr) {
894 zend_error(E_WARNING, "Invalid \"%s\" setting. %s", ZSTR_VAL(setting), ZSTR_VAL(errstr));
895 zend_string_release(errstr);
896 }
897
898 return retval;
899 }
900 /* }}} */
901
ZEND_INI_DISP(zend_ini_boolean_displayer_cb)902 ZEND_INI_DISP(zend_ini_boolean_displayer_cb) /* {{{ */
903 {
904 int value;
905 zend_string *tmp_value;
906
907 if (type == ZEND_INI_DISPLAY_ORIG && ini_entry->modified) {
908 tmp_value = (ini_entry->orig_value ? ini_entry->orig_value : NULL );
909 } else if (ini_entry->value) {
910 tmp_value = ini_entry->value;
911 } else {
912 tmp_value = NULL;
913 }
914
915 if (tmp_value) {
916 value = zend_ini_parse_bool(tmp_value);
917 } else {
918 value = 0;
919 }
920
921 if (value) {
922 ZEND_PUTS("On");
923 } else {
924 ZEND_PUTS("Off");
925 }
926 }
927 /* }}} */
928
ZEND_INI_DISP(zend_ini_color_displayer_cb)929 ZEND_INI_DISP(zend_ini_color_displayer_cb) /* {{{ */
930 {
931 char *value;
932
933 if (type == ZEND_INI_DISPLAY_ORIG && ini_entry->modified) {
934 value = ZSTR_VAL(ini_entry->orig_value);
935 } else if (ini_entry->value) {
936 value = ZSTR_VAL(ini_entry->value);
937 } else {
938 value = NULL;
939 }
940 if (value) {
941 if (zend_uv.html_errors) {
942 zend_printf("<span style=\"color: %s\">%s</span>", value, value);
943 } else {
944 ZEND_PUTS(value);
945 }
946 } else {
947 if (zend_uv.html_errors) {
948 ZEND_PUTS(NO_VALUE_HTML);
949 } else {
950 ZEND_PUTS(NO_VALUE_PLAINTEXT);
951 }
952 }
953 }
954 /* }}} */
955
ZEND_INI_DISP(display_link_numbers)956 ZEND_INI_DISP(display_link_numbers) /* {{{ */
957 {
958 char *value;
959
960 if (type == ZEND_INI_DISPLAY_ORIG && ini_entry->modified) {
961 value = ZSTR_VAL(ini_entry->orig_value);
962 } else if (ini_entry->value) {
963 value = ZSTR_VAL(ini_entry->value);
964 } else {
965 value = NULL;
966 }
967
968 if (value) {
969 if (atoi(value) == -1) {
970 ZEND_PUTS("Unlimited");
971 } else {
972 zend_printf("%s", value);
973 }
974 }
975 }
976 /* }}} */
977
978 /* Standard message handlers */
ZEND_INI_MH(OnUpdateBool)979 ZEND_API ZEND_INI_MH(OnUpdateBool) /* {{{ */
980 {
981 bool *p = (bool *) ZEND_INI_GET_ADDR();
982 *p = zend_ini_parse_bool(new_value);
983 return SUCCESS;
984 }
985 /* }}} */
986
ZEND_INI_MH(OnUpdateLong)987 ZEND_API ZEND_INI_MH(OnUpdateLong) /* {{{ */
988 {
989 zend_long *p = (zend_long *) ZEND_INI_GET_ADDR();
990 *p = zend_ini_parse_quantity_warn(new_value, entry->name);
991 return SUCCESS;
992 }
993 /* }}} */
994
ZEND_INI_MH(OnUpdateLongGEZero)995 ZEND_API ZEND_INI_MH(OnUpdateLongGEZero) /* {{{ */
996 {
997 zend_long tmp = zend_ini_parse_quantity_warn(new_value, entry->name);
998 if (tmp < 0) {
999 return FAILURE;
1000 }
1001
1002 zend_long *p = (zend_long *) ZEND_INI_GET_ADDR();
1003 *p = tmp;
1004
1005 return SUCCESS;
1006 }
1007 /* }}} */
1008
ZEND_INI_MH(OnUpdateReal)1009 ZEND_API ZEND_INI_MH(OnUpdateReal) /* {{{ */
1010 {
1011 double *p = (double *) ZEND_INI_GET_ADDR();
1012 *p = zend_strtod(ZSTR_VAL(new_value), NULL);
1013 return SUCCESS;
1014 }
1015 /* }}} */
1016
ZEND_INI_MH(OnUpdateString)1017 ZEND_API ZEND_INI_MH(OnUpdateString) /* {{{ */
1018 {
1019 char **p = (char **) ZEND_INI_GET_ADDR();
1020 *p = new_value ? ZSTR_VAL(new_value) : NULL;
1021 return SUCCESS;
1022 }
1023 /* }}} */
1024
ZEND_INI_MH(OnUpdateStringUnempty)1025 ZEND_API ZEND_INI_MH(OnUpdateStringUnempty) /* {{{ */
1026 {
1027 if (new_value && !ZSTR_VAL(new_value)[0]) {
1028 return FAILURE;
1029 }
1030
1031 char **p = (char **) ZEND_INI_GET_ADDR();
1032 *p = new_value ? ZSTR_VAL(new_value) : NULL;
1033 return SUCCESS;
1034 }
1035 /* }}} */
1036
ZEND_INI_MH(OnUpdateStr)1037 ZEND_API ZEND_INI_MH(OnUpdateStr) /* {{{ */
1038 {
1039 zend_string **p = (zend_string **) ZEND_INI_GET_ADDR();
1040 *p = new_value;
1041 return SUCCESS;
1042 }
1043 /* }}} */
1044
ZEND_INI_MH(OnUpdateStrNotEmpty)1045 ZEND_API ZEND_INI_MH(OnUpdateStrNotEmpty) /* {{{ */
1046 {
1047 if (new_value && ZSTR_LEN(new_value) == 0) {
1048 return FAILURE;
1049 }
1050
1051 zend_string **p = (zend_string **) ZEND_INI_GET_ADDR();
1052 *p = new_value;
1053 return SUCCESS;
1054 }
1055 /* }}} */
1056