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
zend_ini_get_value(zend_string * name)520 ZEND_API zend_string *zend_ini_get_value(zend_string *name) /* {{{ */
521 {
522 zend_ini_entry *ini_entry;
523
524 ini_entry = zend_hash_find_ptr(EG(ini_directives), name);
525 if (ini_entry) {
526 return ini_entry->value ? ini_entry->value : ZSTR_EMPTY_ALLOC();
527 } else {
528 return NULL;
529 }
530 }
531 /* }}} */
532
zend_ini_parse_bool(zend_string * str)533 ZEND_API bool zend_ini_parse_bool(zend_string *str)
534 {
535 if (zend_string_equals_literal_ci(str, "true")
536 || zend_string_equals_literal_ci(str, "yes")
537 || zend_string_equals_literal_ci(str, "on")
538 ) {
539 return 1;
540 } else {
541 return atoi(ZSTR_VAL(str)) != 0;
542 }
543 }
544
545 typedef enum {
546 ZEND_INI_PARSE_QUANTITY_SIGNED,
547 ZEND_INI_PARSE_QUANTITY_UNSIGNED,
548 } zend_ini_parse_quantity_signed_result_t;
549
zend_ini_consume_quantity_prefix(const char * const digits,const char * const str_end)550 static const char *zend_ini_consume_quantity_prefix(const char *const digits, const char *const str_end) {
551 const char *digits_consumed = digits;
552 /* Ignore leading whitespace. */
553 while (digits_consumed < str_end && zend_is_whitespace(*digits_consumed)) {++digits_consumed;}
554 if (digits_consumed[0] == '+' || digits_consumed[0] == '-') {
555 ++digits_consumed;
556 }
557
558 if (digits_consumed[0] == '0' && !isdigit(digits_consumed[1])) {
559 /* Value is just 0 */
560 if ((digits_consumed+1) == str_end) {
561 return digits;
562 }
563
564 switch (digits_consumed[1]) {
565 case 'x':
566 case 'X':
567 case 'o':
568 case 'O':
569 case 'b':
570 case 'B':
571 digits_consumed += 2;
572 break;
573 }
574 }
575 return digits_consumed;
576 }
577
zend_ini_parse_quantity_internal(zend_string * value,zend_ini_parse_quantity_signed_result_t signed_result,zend_string ** errstr)578 static zend_ulong zend_ini_parse_quantity_internal(zend_string *value, zend_ini_parse_quantity_signed_result_t signed_result, zend_string **errstr) /* {{{ */
579 {
580 char *digits_end = NULL;
581 char *str = ZSTR_VAL(value);
582 char *str_end = &str[ZSTR_LEN(value)];
583 char *digits = str;
584 bool overflow = false;
585 zend_ulong factor;
586 smart_str invalid = {0};
587 smart_str interpreted = {0};
588 smart_str chr = {0};
589
590 /* Ignore leading whitespace. ZEND_STRTOL() also skips leading whitespaces,
591 * but we need the position of the first non-whitespace later. */
592 while (digits < str_end && zend_is_whitespace(*digits)) {++digits;}
593
594 /* Ignore trailing whitespace */
595 while (digits < str_end && zend_is_whitespace(*(str_end-1))) {--str_end;}
596
597 if (digits == str_end) {
598 *errstr = NULL;
599 return 0;
600 }
601
602 bool is_negative = false;
603 if (digits[0] == '+') {
604 ++digits;
605 } else if (digits[0] == '-') {
606 is_negative = true;
607 ++digits;
608 }
609
610 /* if there is no digit after +/- */
611 if (!isdigit(digits[0])) {
612 /* Escape the string to avoid null bytes and to make non-printable chars
613 * visible */
614 smart_str_append_escaped(&invalid, ZSTR_VAL(value), ZSTR_LEN(value));
615 smart_str_0(&invalid);
616
617 *errstr = zend_strpprintf(0, "Invalid quantity \"%s\": no valid leading digits, interpreting as \"0\" for backwards compatibility",
618 ZSTR_VAL(invalid.s));
619
620 smart_str_free(&invalid);
621 return 0;
622 }
623
624 int base = 0;
625 if (digits[0] == '0' && !isdigit(digits[1])) {
626 /* Value is just 0 */
627 if ((digits+1) == str_end) {
628 *errstr = NULL;
629 return 0;
630 }
631
632 switch (digits[1]) {
633 /* Multiplier suffixes */
634 case 'g':
635 case 'G':
636 case 'm':
637 case 'M':
638 case 'k':
639 case 'K':
640 goto evaluation;
641 case 'x':
642 case 'X':
643 base = 16;
644 break;
645 case 'o':
646 case 'O':
647 base = 8;
648 break;
649 case 'b':
650 case 'B':
651 base = 2;
652 break;
653 default:
654 *errstr = zend_strpprintf(0, "Invalid prefix \"0%c\", interpreting as \"0\" for backwards compatibility",
655 digits[1]);
656 return 0;
657 }
658 digits += 2;
659 if (UNEXPECTED(digits == str_end)) {
660 /* Escape the string to avoid null bytes and to make non-printable chars
661 * visible */
662 smart_str_append_escaped(&invalid, ZSTR_VAL(value), ZSTR_LEN(value));
663 smart_str_0(&invalid);
664
665 *errstr = zend_strpprintf(0, "Invalid quantity \"%s\": no digits after base prefix, interpreting as \"0\" for backwards compatibility",
666 ZSTR_VAL(invalid.s));
667
668 smart_str_free(&invalid);
669 return 0;
670 }
671 if (UNEXPECTED(digits != zend_ini_consume_quantity_prefix(digits, str_end))) {
672 /* Escape the string to avoid null bytes and to make non-printable chars
673 * visible */
674 smart_str_append_escaped(&invalid, ZSTR_VAL(value), ZSTR_LEN(value));
675 smart_str_0(&invalid);
676
677 *errstr = zend_strpprintf(0, "Invalid quantity \"%s\": no digits after base prefix, interpreting as \"0\" for backwards compatibility",
678 ZSTR_VAL(invalid.s));
679
680 smart_str_free(&invalid);
681 return 0;
682 }
683 }
684 evaluation:
685
686 errno = 0;
687 zend_ulong retval = ZEND_STRTOUL(digits, &digits_end, base);
688
689 if (errno == ERANGE) {
690 overflow = true;
691 } else if (signed_result == ZEND_INI_PARSE_QUANTITY_UNSIGNED) {
692 if (is_negative) {
693 /* Ignore "-1" as it is commonly used as max value, for instance in memory_limit=-1. */
694 if (retval == 1 && digits_end == str_end) {
695 retval = -1;
696 } else {
697 overflow = true;
698 }
699 }
700 } else if (signed_result == ZEND_INI_PARSE_QUANTITY_SIGNED) {
701 /* Handle PHP_INT_MIN case */
702 if (is_negative && retval == ((zend_ulong)ZEND_LONG_MAX +1)) {
703 retval = 0u - retval;
704 } else if ((zend_long) retval < 0) {
705 overflow = true;
706 } else if (is_negative) {
707 retval = 0u - retval;
708 }
709 }
710
711 if (UNEXPECTED(digits_end == digits)) {
712 /* No leading digits */
713
714 /* Escape the string to avoid null bytes and to make non-printable chars
715 * visible */
716 smart_str_append_escaped(&invalid, ZSTR_VAL(value), ZSTR_LEN(value));
717 smart_str_0(&invalid);
718
719 *errstr = zend_strpprintf(0, "Invalid quantity \"%s\": no valid leading digits, interpreting as \"0\" for backwards compatibility",
720 ZSTR_VAL(invalid.s));
721
722 smart_str_free(&invalid);
723 return 0;
724 }
725
726 /* Allow for whitespace between integer portion and any suffix character */
727 while (digits_end < str_end && zend_is_whitespace(*digits_end)) ++digits_end;
728
729 /* No exponent suffix. */
730 if (digits_end == str_end) {
731 goto end;
732 }
733
734 switch (*(str_end-1)) {
735 case 'g':
736 case 'G':
737 factor = 1<<30;
738 break;
739 case 'm':
740 case 'M':
741 factor = 1<<20;
742 break;
743 case 'k':
744 case 'K':
745 factor = 1<<10;
746 break;
747 default:
748 /* Unknown suffix */
749 smart_str_append_escaped(&invalid, ZSTR_VAL(value), ZSTR_LEN(value));
750 smart_str_0(&invalid);
751 smart_str_append_escaped(&interpreted, str, digits_end - str);
752 smart_str_0(&interpreted);
753 smart_str_append_escaped(&chr, str_end-1, 1);
754 smart_str_0(&chr);
755
756 *errstr = zend_strpprintf(0, "Invalid quantity \"%s\": unknown multiplier \"%s\", interpreting as \"%s\" for backwards compatibility",
757 ZSTR_VAL(invalid.s), ZSTR_VAL(chr.s), ZSTR_VAL(interpreted.s));
758
759 smart_str_free(&invalid);
760 smart_str_free(&interpreted);
761 smart_str_free(&chr);
762
763 return retval;
764 }
765
766 if (!overflow) {
767 if (signed_result == ZEND_INI_PARSE_QUANTITY_SIGNED) {
768 zend_long sretval = (zend_long)retval;
769 if (sretval > 0) {
770 overflow = (zend_long)retval > ZEND_LONG_MAX / (zend_long)factor;
771 } else {
772 overflow = (zend_long)retval < ZEND_LONG_MIN / (zend_long)factor;
773 }
774 } else {
775 overflow = retval > ZEND_ULONG_MAX / factor;
776 }
777 }
778
779 retval *= factor;
780
781 if (UNEXPECTED(digits_end != str_end-1)) {
782 /* More than one character in suffix */
783 smart_str_append_escaped(&invalid, ZSTR_VAL(value), ZSTR_LEN(value));
784 smart_str_0(&invalid);
785 smart_str_append_escaped(&interpreted, str, digits_end - str);
786 smart_str_0(&interpreted);
787 smart_str_append_escaped(&chr, str_end-1, 1);
788 smart_str_0(&chr);
789
790 *errstr = zend_strpprintf(0, "Invalid quantity \"%s\", interpreting as \"%s%s\" for backwards compatibility",
791 ZSTR_VAL(invalid.s), ZSTR_VAL(interpreted.s), ZSTR_VAL(chr.s));
792
793 smart_str_free(&invalid);
794 smart_str_free(&interpreted);
795 smart_str_free(&chr);
796
797 return retval;
798 }
799
800 end:
801 if (UNEXPECTED(overflow)) {
802 smart_str_append_escaped(&invalid, ZSTR_VAL(value), ZSTR_LEN(value));
803 smart_str_0(&invalid);
804
805 /* Not specifying the resulting value here because the caller may make
806 * additional conversions. Not specifying the allowed range
807 * because the caller may do narrower range checks. */
808 *errstr = zend_strpprintf(0, "Invalid quantity \"%s\": value is out of range, using overflow result for backwards compatibility",
809 ZSTR_VAL(invalid.s));
810
811 smart_str_free(&invalid);
812 smart_str_free(&interpreted);
813 smart_str_free(&chr);
814
815 return retval;
816 }
817
818 *errstr = NULL;
819 return retval;
820 }
821 /* }}} */
822
zend_ini_parse_quantity(zend_string * value,zend_string ** errstr)823 ZEND_API zend_long zend_ini_parse_quantity(zend_string *value, zend_string **errstr) /* {{{ */
824 {
825 return (zend_long) zend_ini_parse_quantity_internal(value, ZEND_INI_PARSE_QUANTITY_SIGNED, errstr);
826 }
827 /* }}} */
828
zend_ini_parse_uquantity(zend_string * value,zend_string ** errstr)829 ZEND_API zend_ulong zend_ini_parse_uquantity(zend_string *value, zend_string **errstr) /* {{{ */
830 {
831 return zend_ini_parse_quantity_internal(value, ZEND_INI_PARSE_QUANTITY_UNSIGNED, errstr);
832 }
833 /* }}} */
834
zend_ini_parse_quantity_warn(zend_string * value,zend_string * setting)835 ZEND_API zend_long zend_ini_parse_quantity_warn(zend_string *value, zend_string *setting) /* {{{ */
836 {
837 zend_string *errstr;
838 zend_long retval = zend_ini_parse_quantity(value, &errstr);
839
840 if (errstr) {
841 zend_error(E_WARNING, "Invalid \"%s\" setting. %s", ZSTR_VAL(setting), ZSTR_VAL(errstr));
842 zend_string_release(errstr);
843 }
844
845 return retval;
846 }
847 /* }}} */
848
zend_ini_parse_uquantity_warn(zend_string * value,zend_string * setting)849 ZEND_API zend_ulong zend_ini_parse_uquantity_warn(zend_string *value, zend_string *setting) /* {{{ */
850 {
851 zend_string *errstr;
852 zend_ulong retval = zend_ini_parse_uquantity(value, &errstr);
853
854 if (errstr) {
855 zend_error(E_WARNING, "Invalid \"%s\" setting. %s", ZSTR_VAL(setting), ZSTR_VAL(errstr));
856 zend_string_release(errstr);
857 }
858
859 return retval;
860 }
861 /* }}} */
862
ZEND_INI_DISP(zend_ini_boolean_displayer_cb)863 ZEND_INI_DISP(zend_ini_boolean_displayer_cb) /* {{{ */
864 {
865 int value;
866 zend_string *tmp_value;
867
868 if (type == ZEND_INI_DISPLAY_ORIG && ini_entry->modified) {
869 tmp_value = (ini_entry->orig_value ? ini_entry->orig_value : NULL );
870 } else if (ini_entry->value) {
871 tmp_value = ini_entry->value;
872 } else {
873 tmp_value = NULL;
874 }
875
876 if (tmp_value) {
877 value = zend_ini_parse_bool(tmp_value);
878 } else {
879 value = 0;
880 }
881
882 if (value) {
883 ZEND_PUTS("On");
884 } else {
885 ZEND_PUTS("Off");
886 }
887 }
888 /* }}} */
889
ZEND_INI_DISP(zend_ini_color_displayer_cb)890 ZEND_INI_DISP(zend_ini_color_displayer_cb) /* {{{ */
891 {
892 char *value;
893
894 if (type == ZEND_INI_DISPLAY_ORIG && ini_entry->modified) {
895 value = ZSTR_VAL(ini_entry->orig_value);
896 } else if (ini_entry->value) {
897 value = ZSTR_VAL(ini_entry->value);
898 } else {
899 value = NULL;
900 }
901 if (value) {
902 if (zend_uv.html_errors) {
903 zend_printf("<font style=\"color: %s\">%s</font>", value, value);
904 } else {
905 ZEND_PUTS(value);
906 }
907 } else {
908 if (zend_uv.html_errors) {
909 ZEND_PUTS(NO_VALUE_HTML);
910 } else {
911 ZEND_PUTS(NO_VALUE_PLAINTEXT);
912 }
913 }
914 }
915 /* }}} */
916
ZEND_INI_DISP(display_link_numbers)917 ZEND_INI_DISP(display_link_numbers) /* {{{ */
918 {
919 char *value;
920
921 if (type == ZEND_INI_DISPLAY_ORIG && ini_entry->modified) {
922 value = ZSTR_VAL(ini_entry->orig_value);
923 } else if (ini_entry->value) {
924 value = ZSTR_VAL(ini_entry->value);
925 } else {
926 value = NULL;
927 }
928
929 if (value) {
930 if (atoi(value) == -1) {
931 ZEND_PUTS("Unlimited");
932 } else {
933 zend_printf("%s", value);
934 }
935 }
936 }
937 /* }}} */
938
939 /* Standard message handlers */
ZEND_INI_MH(OnUpdateBool)940 ZEND_API ZEND_INI_MH(OnUpdateBool) /* {{{ */
941 {
942 bool *p = (bool *) ZEND_INI_GET_ADDR();
943 *p = zend_ini_parse_bool(new_value);
944 return SUCCESS;
945 }
946 /* }}} */
947
ZEND_INI_MH(OnUpdateLong)948 ZEND_API ZEND_INI_MH(OnUpdateLong) /* {{{ */
949 {
950 zend_long *p = (zend_long *) ZEND_INI_GET_ADDR();
951 *p = zend_ini_parse_quantity_warn(new_value, entry->name);
952 return SUCCESS;
953 }
954 /* }}} */
955
ZEND_INI_MH(OnUpdateLongGEZero)956 ZEND_API ZEND_INI_MH(OnUpdateLongGEZero) /* {{{ */
957 {
958 zend_long tmp = zend_ini_parse_quantity_warn(new_value, entry->name);
959 if (tmp < 0) {
960 return FAILURE;
961 }
962
963 zend_long *p = (zend_long *) ZEND_INI_GET_ADDR();
964 *p = tmp;
965
966 return SUCCESS;
967 }
968 /* }}} */
969
ZEND_INI_MH(OnUpdateReal)970 ZEND_API ZEND_INI_MH(OnUpdateReal) /* {{{ */
971 {
972 double *p = (double *) ZEND_INI_GET_ADDR();
973 *p = zend_strtod(ZSTR_VAL(new_value), NULL);
974 return SUCCESS;
975 }
976 /* }}} */
977
ZEND_INI_MH(OnUpdateString)978 ZEND_API ZEND_INI_MH(OnUpdateString) /* {{{ */
979 {
980 char **p = (char **) ZEND_INI_GET_ADDR();
981 *p = new_value ? ZSTR_VAL(new_value) : NULL;
982 return SUCCESS;
983 }
984 /* }}} */
985
ZEND_INI_MH(OnUpdateStringUnempty)986 ZEND_API ZEND_INI_MH(OnUpdateStringUnempty) /* {{{ */
987 {
988 if (new_value && !ZSTR_VAL(new_value)[0]) {
989 return FAILURE;
990 }
991
992 char **p = (char **) ZEND_INI_GET_ADDR();
993 *p = new_value ? ZSTR_VAL(new_value) : NULL;
994 return SUCCESS;
995 }
996 /* }}} */
997
ZEND_INI_MH(OnUpdateStr)998 ZEND_API ZEND_INI_MH(OnUpdateStr) /* {{{ */
999 {
1000 zend_string **p = (zend_string **) ZEND_INI_GET_ADDR();
1001 *p = new_value;
1002 return SUCCESS;
1003 }
1004 /* }}} */
1005
ZEND_INI_MH(OnUpdateStrNotEmpty)1006 ZEND_API ZEND_INI_MH(OnUpdateStrNotEmpty) /* {{{ */
1007 {
1008 if (new_value && ZSTR_LEN(new_value) == 0) {
1009 return FAILURE;
1010 }
1011
1012 zend_string **p = (zend_string **) ZEND_INI_GET_ADDR();
1013 *p = new_value;
1014 return SUCCESS;
1015 }
1016 /* }}} */
1017