1 /*
2 +----------------------------------------------------------------------+
3 | Copyright (c) The PHP Group |
4 +----------------------------------------------------------------------+
5 | This source file is subject to version 3.01 of the PHP license, |
6 | that is bundled with this package in the file LICENSE, and is |
7 | available through the world-wide-web at the following url: |
8 | https://www.php.net/license/3_01.txt |
9 | If you did not receive a copy of the PHP license and are unable to |
10 | obtain it through the world-wide-web, please send a note to |
11 | license@php.net so we can mail you a copy immediately. |
12 +----------------------------------------------------------------------+
13 | Author: Zeev Suraski <zeev@php.net> |
14 +----------------------------------------------------------------------+
15 */
16
17 #include "php.h"
18 #include "ext/standard/info.h"
19 #include "zend_ini.h"
20 #include "zend_ini_scanner.h"
21 #include "php_ini.h"
22 #include "ext/standard/dl.h"
23 #include "zend_extensions.h"
24 #include "zend_highlight.h"
25 #include "SAPI.h"
26 #include "php_main.h"
27 #include "php_scandir.h"
28 #ifdef PHP_WIN32
29 #include "win32/php_registry.h"
30 #include "win32/winutil.h"
31 #endif
32
33 #if HAVE_SCANDIR && HAVE_ALPHASORT && HAVE_DIRENT_H
34 #include <dirent.h>
35 #endif
36
37 #ifdef PHP_WIN32
38 #define TRANSLATE_SLASHES_LOWER(path) \
39 { \
40 char *tmp = path; \
41 while (*tmp) { \
42 if (*tmp == '\\') *tmp = '/'; \
43 else *tmp = tolower(*tmp); \
44 tmp++; \
45 } \
46 }
47 #else
48 #define TRANSLATE_SLASHES_LOWER(path)
49 #endif
50
51
52 typedef struct _php_extension_lists {
53 zend_llist engine;
54 zend_llist functions;
55 } php_extension_lists;
56
57 /* True globals */
58 static int is_special_section = 0;
59 static HashTable *active_ini_hash;
60 static HashTable configuration_hash;
61 static int has_per_dir_config = 0;
62 static int has_per_host_config = 0;
63 PHPAPI char *php_ini_opened_path=NULL;
64 static php_extension_lists extension_lists;
65 PHPAPI char *php_ini_scanned_path=NULL;
66 PHPAPI char *php_ini_scanned_files=NULL;
67
68 /* {{{ php_ini_displayer_cb */
php_ini_displayer_cb(zend_ini_entry * ini_entry,int type)69 static ZEND_COLD void php_ini_displayer_cb(zend_ini_entry *ini_entry, int type)
70 {
71 if (ini_entry->displayer) {
72 ini_entry->displayer(ini_entry, type);
73 } else {
74 char *display_string;
75 size_t display_string_length;
76 int esc_html=0;
77
78 if (type == ZEND_INI_DISPLAY_ORIG && ini_entry->modified) {
79 if (ini_entry->orig_value && ZSTR_VAL(ini_entry->orig_value)[0]) {
80 display_string = ZSTR_VAL(ini_entry->orig_value);
81 display_string_length = ZSTR_LEN(ini_entry->orig_value);
82 esc_html = !sapi_module.phpinfo_as_text;
83 } else {
84 if (!sapi_module.phpinfo_as_text) {
85 display_string = "<i>no value</i>";
86 display_string_length = sizeof("<i>no value</i>") - 1;
87 } else {
88 display_string = "no value";
89 display_string_length = sizeof("no value") - 1;
90 }
91 }
92 } else if (ini_entry->value && ZSTR_VAL(ini_entry->value)[0]) {
93 display_string = ZSTR_VAL(ini_entry->value);
94 display_string_length = ZSTR_LEN(ini_entry->value);
95 esc_html = !sapi_module.phpinfo_as_text;
96 } else {
97 if (!sapi_module.phpinfo_as_text) {
98 display_string = "<i>no value</i>";
99 display_string_length = sizeof("<i>no value</i>") - 1;
100 } else {
101 display_string = "no value";
102 display_string_length = sizeof("no value") - 1;
103 }
104 }
105
106 if (esc_html) {
107 php_html_puts(display_string, display_string_length);
108 } else {
109 PHPWRITE(display_string, display_string_length);
110 }
111 }
112 }
113 /* }}} */
114
115 /* {{{ display_ini_entries */
display_ini_entries(zend_module_entry * module)116 PHPAPI ZEND_COLD void display_ini_entries(zend_module_entry *module)
117 {
118 int module_number;
119 zend_ini_entry *ini_entry;
120 bool first = 1;
121
122 if (module) {
123 module_number = module->module_number;
124 } else {
125 module_number = 0;
126 }
127
128 ZEND_HASH_MAP_FOREACH_PTR(EG(ini_directives), ini_entry) {
129 if (ini_entry->module_number != module_number) {
130 continue;
131 }
132 if (first) {
133 php_info_print_table_start();
134 php_info_print_table_header(3, "Directive", "Local Value", "Master Value");
135 first = 0;
136 }
137 if (!sapi_module.phpinfo_as_text) {
138 PUTS("<tr>");
139 PUTS("<td class=\"e\">");
140 PHPWRITE(ZSTR_VAL(ini_entry->name), ZSTR_LEN(ini_entry->name));
141 PUTS("</td><td class=\"v\">");
142 php_ini_displayer_cb(ini_entry, ZEND_INI_DISPLAY_ACTIVE);
143 PUTS("</td><td class=\"v\">");
144 php_ini_displayer_cb(ini_entry, ZEND_INI_DISPLAY_ORIG);
145 PUTS("</td></tr>\n");
146 } else {
147 PHPWRITE(ZSTR_VAL(ini_entry->name), ZSTR_LEN(ini_entry->name));
148 PUTS(" => ");
149 php_ini_displayer_cb(ini_entry, ZEND_INI_DISPLAY_ACTIVE);
150 PUTS(" => ");
151 php_ini_displayer_cb(ini_entry, ZEND_INI_DISPLAY_ORIG);
152 PUTS("\n");
153 }
154 } ZEND_HASH_FOREACH_END();
155 if (!first) {
156 php_info_print_table_end();
157 }
158 }
159 /* }}} */
160
161 /* php.ini support */
162 #define PHP_EXTENSION_TOKEN "extension"
163 #define ZEND_EXTENSION_TOKEN "zend_extension"
164
165 /* {{{ config_zval_dtor */
config_zval_dtor(zval * zvalue)166 PHPAPI void config_zval_dtor(zval *zvalue)
167 {
168 if (Z_TYPE_P(zvalue) == IS_ARRAY) {
169 zend_hash_destroy(Z_ARRVAL_P(zvalue));
170 free(Z_ARR_P(zvalue));
171 } else if (Z_TYPE_P(zvalue) == IS_STRING) {
172 zend_string_release_ex(Z_STR_P(zvalue), 1);
173 }
174 }
175 /* Reset / free active_ini_section global */
176 #define RESET_ACTIVE_INI_HASH() do { \
177 active_ini_hash = NULL; \
178 is_special_section = 0; \
179 } while (0)
180 /* }}} */
181
182 /* {{{ php_ini_parser_cb */
php_ini_parser_cb(zval * arg1,zval * arg2,zval * arg3,int callback_type,HashTable * target_hash)183 static void php_ini_parser_cb(zval *arg1, zval *arg2, zval *arg3, int callback_type, HashTable *target_hash)
184 {
185 zval *entry;
186 HashTable *active_hash;
187 char *extension_name;
188
189 if (active_ini_hash) {
190 active_hash = active_ini_hash;
191 } else {
192 active_hash = target_hash;
193 }
194
195 switch (callback_type) {
196 case ZEND_INI_PARSER_ENTRY: {
197 if (!arg2) {
198 /* bare string - nothing to do */
199 break;
200 }
201
202 /* PHP and Zend extensions are not added into configuration hash! */
203 if (!is_special_section && zend_string_equals_literal_ci(Z_STR_P(arg1), PHP_EXTENSION_TOKEN)) { /* load PHP extension */
204 extension_name = estrndup(Z_STRVAL_P(arg2), Z_STRLEN_P(arg2));
205 zend_llist_add_element(&extension_lists.functions, &extension_name);
206 } else if (!is_special_section && zend_string_equals_literal_ci(Z_STR_P(arg1), ZEND_EXTENSION_TOKEN)) { /* load Zend extension */
207 extension_name = estrndup(Z_STRVAL_P(arg2), Z_STRLEN_P(arg2));
208 zend_llist_add_element(&extension_lists.engine, &extension_name);
209
210 /* All other entries are added into either configuration_hash or active ini section array */
211 } else {
212 /* Store in active hash */
213 entry = zend_hash_update(active_hash, Z_STR_P(arg1), arg2);
214 Z_STR_P(entry) = zend_string_dup(Z_STR_P(entry), 1);
215 }
216 }
217 break;
218
219 case ZEND_INI_PARSER_POP_ENTRY: {
220 zval option_arr;
221 zval *find_arr;
222
223 if (!arg2) {
224 /* bare string - nothing to do */
225 break;
226 }
227
228 /* fprintf(stdout, "ZEND_INI_PARSER_POP_ENTRY: %s[%s] = %s\n",Z_STRVAL_P(arg1), Z_STRVAL_P(arg3), Z_STRVAL_P(arg2)); */
229
230 /* If option not found in hash or is not an array -> create array, otherwise add to existing array */
231 if ((find_arr = zend_hash_find(active_hash, Z_STR_P(arg1))) == NULL || Z_TYPE_P(find_arr) != IS_ARRAY) {
232 ZVAL_NEW_PERSISTENT_ARR(&option_arr);
233 zend_hash_init(Z_ARRVAL(option_arr), 8, NULL, config_zval_dtor, 1);
234 find_arr = zend_hash_update(active_hash, Z_STR_P(arg1), &option_arr);
235 }
236
237 /* arg3 is possible option offset name */
238 if (arg3 && Z_STRLEN_P(arg3) > 0) {
239 entry = zend_symtable_update(Z_ARRVAL_P(find_arr), Z_STR_P(arg3), arg2);
240 } else {
241 entry = zend_hash_next_index_insert(Z_ARRVAL_P(find_arr), arg2);
242 }
243 Z_STR_P(entry) = zend_string_dup(Z_STR_P(entry), 1);
244 }
245 break;
246
247 case ZEND_INI_PARSER_SECTION: { /* Create an array of entries of each section */
248
249 /* fprintf(stdout, "ZEND_INI_PARSER_SECTION: %s\n",Z_STRVAL_P(arg1)); */
250
251 char *key = NULL;
252 size_t key_len;
253
254 /* PATH sections */
255 if (!zend_binary_strncasecmp(Z_STRVAL_P(arg1), Z_STRLEN_P(arg1), "PATH", sizeof("PATH") - 1, sizeof("PATH") - 1)) {
256 key = Z_STRVAL_P(arg1);
257 key = key + sizeof("PATH") - 1;
258 key_len = Z_STRLEN_P(arg1) - sizeof("PATH") + 1;
259 is_special_section = 1;
260 has_per_dir_config = 1;
261
262 /* make the path lowercase on Windows, for case insensitivity. Does nothing for other platforms */
263 TRANSLATE_SLASHES_LOWER(key);
264
265 /* HOST sections */
266 } else if (!zend_binary_strncasecmp(Z_STRVAL_P(arg1), Z_STRLEN_P(arg1), "HOST", sizeof("HOST") - 1, sizeof("HOST") - 1)) {
267 key = Z_STRVAL_P(arg1);
268 key = key + sizeof("HOST") - 1;
269 key_len = Z_STRLEN_P(arg1) - sizeof("HOST") + 1;
270 is_special_section = 1;
271 has_per_host_config = 1;
272 zend_str_tolower(key, key_len); /* host names are case-insensitive. */
273
274 } else {
275 is_special_section = 0;
276 }
277
278 if (key && key_len > 0) {
279 /* Strip any trailing slashes */
280 while (key_len > 0 && (key[key_len - 1] == '/' || key[key_len - 1] == '\\')) {
281 key_len--;
282 key[key_len] = 0;
283 }
284
285 /* Strip any leading whitespace and '=' */
286 while (*key && (
287 *key == '=' ||
288 *key == ' ' ||
289 *key == '\t'
290 )) {
291 key++;
292 key_len--;
293 }
294
295 /* Search for existing entry and if it does not exist create one */
296 if ((entry = zend_hash_str_find(target_hash, key, key_len)) == NULL) {
297 zval section_arr;
298
299 ZVAL_NEW_PERSISTENT_ARR(§ion_arr);
300 zend_hash_init(Z_ARRVAL(section_arr), 8, NULL, (dtor_func_t) config_zval_dtor, 1);
301 entry = zend_hash_str_update(target_hash, key, key_len, §ion_arr);
302 }
303 if (Z_TYPE_P(entry) == IS_ARRAY) {
304 active_ini_hash = Z_ARRVAL_P(entry);
305 }
306 }
307 }
308 break;
309 }
310 }
311 /* }}} */
312
313 /* {{{ php_load_php_extension_cb */
php_load_php_extension_cb(void * arg)314 static void php_load_php_extension_cb(void *arg)
315 {
316 #ifdef HAVE_LIBDL
317 php_load_extension(*((char **) arg), MODULE_PERSISTENT, 0);
318 #endif
319 }
320 /* }}} */
321
322 /* {{{ php_load_zend_extension_cb */
323 #ifdef HAVE_LIBDL
php_load_zend_extension_cb(void * arg)324 static void php_load_zend_extension_cb(void *arg)
325 {
326 char *filename = *((char **) arg);
327 const size_t length = strlen(filename);
328
329 #ifndef PHP_WIN32
330 (void) length;
331 #endif
332
333 if (IS_ABSOLUTE_PATH(filename, length)) {
334 zend_load_extension(filename);
335 } else {
336 DL_HANDLE handle;
337 char *libpath;
338 char *extension_dir = INI_STR("extension_dir");
339 int slash_suffix = 0;
340 char *err1, *err2;
341
342 if (extension_dir && extension_dir[0]) {
343 slash_suffix = IS_SLASH(extension_dir[strlen(extension_dir)-1]);
344 }
345
346 /* Try as filename first */
347 if (slash_suffix) {
348 spprintf(&libpath, 0, "%s%s", extension_dir, filename); /* SAFE */
349 } else {
350 spprintf(&libpath, 0, "%s%c%s", extension_dir, DEFAULT_SLASH, filename); /* SAFE */
351 }
352
353 handle = (DL_HANDLE)php_load_shlib(libpath, &err1);
354 if (!handle) {
355 /* If file does not exist, consider as extension name and build file name */
356 char *orig_libpath = libpath;
357
358 if (slash_suffix) {
359 spprintf(&libpath, 0, "%s" PHP_SHLIB_EXT_PREFIX "%s." PHP_SHLIB_SUFFIX, extension_dir, filename); /* SAFE */
360 } else {
361 spprintf(&libpath, 0, "%s%c" PHP_SHLIB_EXT_PREFIX "%s." PHP_SHLIB_SUFFIX, extension_dir, DEFAULT_SLASH, filename); /* SAFE */
362 }
363
364 handle = (DL_HANDLE)php_load_shlib(libpath, &err2);
365 if (!handle) {
366 php_error(E_CORE_WARNING, "Failed loading Zend extension '%s' (tried: %s (%s), %s (%s))",
367 filename, orig_libpath, err1, libpath, err2);
368 efree(orig_libpath);
369 efree(err1);
370 efree(libpath);
371 efree(err2);
372 return;
373 }
374
375 efree(orig_libpath);
376 efree(err1);
377 }
378
379 #ifdef PHP_WIN32
380 if (!php_win32_image_compatible(handle, &err1)) {
381 php_error(E_CORE_WARNING, err1);
382 efree(err1);
383 efree(libpath);
384 DL_UNLOAD(handle);
385 return;
386 }
387 #endif
388
389 zend_load_extension_handle(handle, libpath);
390 efree(libpath);
391 }
392 }
393 #else
php_load_zend_extension_cb(void * arg)394 static void php_load_zend_extension_cb(void *arg) { }
395 #endif
396 /* }}} */
397
append_ini_path(char * php_ini_search_path,int search_path_size,const char * path)398 static void append_ini_path(char *php_ini_search_path, int search_path_size, const char *path)
399 {
400 static const char paths_separator[] = { ZEND_PATHS_SEPARATOR, 0 };
401
402 if (*php_ini_search_path) {
403 strlcat(php_ini_search_path, paths_separator, search_path_size);
404 }
405
406 strlcat(php_ini_search_path, path, search_path_size);
407 }
408
409 /* {{{ php_init_config */
php_init_config(void)410 int php_init_config(void)
411 {
412 char *php_ini_file_name = NULL;
413 char *php_ini_search_path = NULL;
414 int php_ini_scanned_path_len;
415 char *open_basedir;
416 int free_ini_search_path = 0;
417 zend_string *opened_path = NULL;
418
419 zend_hash_init(&configuration_hash, 8, NULL, config_zval_dtor, 1);
420
421 if (sapi_module.ini_defaults) {
422 sapi_module.ini_defaults(&configuration_hash);
423 }
424
425 zend_llist_init(&extension_lists.engine, sizeof(char *), (llist_dtor_func_t) free_estring, 1);
426 zend_llist_init(&extension_lists.functions, sizeof(char *), (llist_dtor_func_t) free_estring, 1);
427
428 open_basedir = PG(open_basedir);
429
430 if (sapi_module.php_ini_path_override) {
431 php_ini_file_name = sapi_module.php_ini_path_override;
432 php_ini_search_path = sapi_module.php_ini_path_override;
433 free_ini_search_path = 0;
434 } else if (!sapi_module.php_ini_ignore) {
435 int search_path_size;
436 char *default_location;
437 char *env_location;
438 #ifdef PHP_WIN32
439 char *reg_location;
440 char phprc_path[MAXPATHLEN];
441 #endif
442
443 env_location = getenv("PHPRC");
444
445 #ifdef PHP_WIN32
446 if (!env_location) {
447 char dummybuf;
448 int size;
449
450 SetLastError(0);
451
452 /*If the given buffer is not large enough to hold the data, the return value is
453 the buffer size, in characters, required to hold the string and its terminating
454 null character. We use this return value to alloc the final buffer. */
455 size = GetEnvironmentVariableA("PHPRC", &dummybuf, 0);
456 if (GetLastError() == ERROR_ENVVAR_NOT_FOUND) {
457 /* The environment variable doesn't exist. */
458 env_location = "";
459 } else {
460 if (size == 0) {
461 env_location = "";
462 } else {
463 size = GetEnvironmentVariableA("PHPRC", phprc_path, size);
464 if (size == 0) {
465 env_location = "";
466 } else {
467 env_location = phprc_path;
468 }
469 }
470 }
471 }
472 #else
473 if (!env_location) {
474 env_location = "";
475 }
476 #endif
477 /*
478 * Prepare search path
479 */
480
481 search_path_size = MAXPATHLEN * 4 + (int)strlen(env_location) + 3 + 1;
482 php_ini_search_path = (char *) emalloc(search_path_size);
483 free_ini_search_path = 1;
484 php_ini_search_path[0] = 0;
485
486 /* Add environment location */
487 if (env_location[0]) {
488 append_ini_path(php_ini_search_path, search_path_size, env_location);
489 php_ini_file_name = env_location;
490 }
491
492 #ifdef PHP_WIN32
493 /* Add registry location */
494 reg_location = GetIniPathFromRegistry();
495 if (reg_location != NULL) {
496 append_ini_path(php_ini_search_path, search_path_size, reg_location);
497 efree(reg_location);
498 }
499 #endif
500
501 /* Add cwd (not with CLI) */
502 if (!sapi_module.php_ini_ignore_cwd) {
503 append_ini_path(php_ini_search_path, search_path_size, ".");
504 }
505
506 if (PG(php_binary)) {
507 char *separator_location, *binary_location;
508
509 binary_location = estrdup(PG(php_binary));
510 separator_location = strrchr(binary_location, DEFAULT_SLASH);
511
512 if (separator_location && separator_location != binary_location) {
513 *(separator_location) = 0;
514 }
515 append_ini_path(php_ini_search_path, search_path_size, binary_location);
516
517 efree(binary_location);
518 }
519
520 /* Add default location */
521 #ifdef PHP_WIN32
522 default_location = (char *) emalloc(MAXPATHLEN + 1);
523
524 if (0 < GetWindowsDirectory(default_location, MAXPATHLEN)) {
525 append_ini_path(php_ini_search_path, search_path_size, default_location);
526 }
527
528 /* For people running under terminal services, GetWindowsDirectory will
529 * return their personal Windows directory, so lets add the system
530 * windows directory too */
531 if (0 < GetSystemWindowsDirectory(default_location, MAXPATHLEN)) {
532 append_ini_path(php_ini_search_path, search_path_size, default_location);
533 }
534 efree(default_location);
535
536 #else
537 default_location = PHP_CONFIG_FILE_PATH;
538 append_ini_path(php_ini_search_path, search_path_size, default_location);
539 #endif
540 }
541
542 PG(open_basedir) = NULL;
543
544 /*
545 * Find and open actual ini file
546 */
547
548 FILE *fp = NULL;
549 char *filename = NULL;
550 bool free_filename = false;
551
552 /* If SAPI does not want to ignore all ini files OR an overriding file/path is given.
553 * This allows disabling scanning for ini files in the PHP_CONFIG_FILE_SCAN_DIR but still
554 * load an optional ini file. */
555 if (!sapi_module.php_ini_ignore || sapi_module.php_ini_path_override) {
556
557 /* Check if php_ini_file_name is a file and can be opened */
558 if (php_ini_file_name && php_ini_file_name[0]) {
559 zend_stat_t statbuf = {0};
560
561 if (!VCWD_STAT(php_ini_file_name, &statbuf)) {
562 if (!((statbuf.st_mode & S_IFMT) == S_IFDIR)) {
563 fp = VCWD_FOPEN(php_ini_file_name, "r");
564 if (fp) {
565 filename = expand_filepath(php_ini_file_name, NULL);
566 free_filename = true;
567 }
568 }
569 }
570 }
571
572 /* Otherwise search for php-%sapi-module-name%.ini file in search path */
573 if (!fp) {
574 const char *fmt = "php-%s.ini";
575 char *ini_fname;
576 spprintf(&ini_fname, 0, fmt, sapi_module.name);
577 fp = php_fopen_with_path(ini_fname, "r", php_ini_search_path, &opened_path);
578 efree(ini_fname);
579 if (fp) {
580 filename = ZSTR_VAL(opened_path);
581 }
582 }
583
584 /* If still no ini file found, search for php.ini file in search path */
585 if (!fp) {
586 fp = php_fopen_with_path("php.ini", "r", php_ini_search_path, &opened_path);
587 if (fp) {
588 filename = ZSTR_VAL(opened_path);
589 }
590 }
591 }
592
593 if (free_ini_search_path) {
594 efree(php_ini_search_path);
595 }
596
597 PG(open_basedir) = open_basedir;
598
599 if (fp) {
600 zend_file_handle fh;
601 zend_stream_init_fp(&fh, fp, filename);
602 RESET_ACTIVE_INI_HASH();
603
604 zend_parse_ini_file(&fh, 1, ZEND_INI_SCANNER_NORMAL, (zend_ini_parser_cb_t) php_ini_parser_cb, &configuration_hash);
605
606 {
607 zval tmp;
608
609 ZVAL_NEW_STR(&tmp, zend_string_init(filename, strlen(filename), 1));
610 zend_hash_str_update(&configuration_hash, "cfg_file_path", sizeof("cfg_file_path")-1, &tmp);
611 if (opened_path) {
612 zend_string_release_ex(opened_path, 0);
613 }
614 php_ini_opened_path = zend_strndup(Z_STRVAL(tmp), Z_STRLEN(tmp));
615 }
616 zend_destroy_file_handle(&fh);
617
618 if (free_filename) {
619 efree(filename);
620 }
621 }
622
623 /* Check for PHP_INI_SCAN_DIR environment variable to override/set config file scan directory */
624 php_ini_scanned_path = getenv("PHP_INI_SCAN_DIR");
625 if (!php_ini_scanned_path) {
626 /* Or fall back using possible --with-config-file-scan-dir setting (defaults to empty string!) */
627 php_ini_scanned_path = PHP_CONFIG_FILE_SCAN_DIR;
628 }
629 php_ini_scanned_path_len = (int)strlen(php_ini_scanned_path);
630
631 /* Scan and parse any .ini files found in scan path if path not empty. */
632 if (!sapi_module.php_ini_ignore && php_ini_scanned_path_len) {
633 struct dirent **namelist;
634 int ndir, i;
635 zend_stat_t sb = {0};
636 char ini_file[MAXPATHLEN];
637 char *p;
638 zend_llist scanned_ini_list;
639 zend_llist_element *element;
640 int l, total_l = 0;
641 char *bufpath, *debpath, *endpath;
642 int lenpath;
643
644 zend_llist_init(&scanned_ini_list, sizeof(char *), (llist_dtor_func_t) free_estring, 1);
645
646 bufpath = estrdup(php_ini_scanned_path);
647 for (debpath = bufpath ; debpath ; debpath=endpath) {
648 endpath = strchr(debpath, DEFAULT_DIR_SEPARATOR);
649 if (endpath) {
650 *(endpath++) = 0;
651 }
652 if (!debpath[0]) {
653 /* empty string means default builtin value
654 to allow "/foo/php.d:" or ":/foo/php.d" */
655 debpath = PHP_CONFIG_FILE_SCAN_DIR;
656 }
657 lenpath = (int)strlen(debpath);
658
659 if (lenpath > 0 && (ndir = php_scandir(debpath, &namelist, 0, php_alphasort)) > 0) {
660
661 for (i = 0; i < ndir; i++) {
662
663 /* check for any file with .ini extension */
664 if (!(p = strrchr(namelist[i]->d_name, '.')) || (p && strcmp(p, ".ini"))) {
665 free(namelist[i]);
666 continue;
667 }
668 /* Reset active ini section */
669 RESET_ACTIVE_INI_HASH();
670
671 if (IS_SLASH(debpath[lenpath - 1])) {
672 snprintf(ini_file, MAXPATHLEN, "%s%s", debpath, namelist[i]->d_name);
673 } else {
674 snprintf(ini_file, MAXPATHLEN, "%s%c%s", debpath, DEFAULT_SLASH, namelist[i]->d_name);
675 }
676 if (VCWD_STAT(ini_file, &sb) == 0) {
677 if (S_ISREG(sb.st_mode)) {
678 zend_file_handle fh;
679 FILE *file = VCWD_FOPEN(ini_file, "r");
680 if (file) {
681 zend_stream_init_fp(&fh, file, ini_file);
682 if (zend_parse_ini_file(&fh, 1, ZEND_INI_SCANNER_NORMAL, (zend_ini_parser_cb_t) php_ini_parser_cb, &configuration_hash) == SUCCESS) {
683 /* Here, add it to the list of ini files read */
684 l = (int)strlen(ini_file);
685 total_l += l + 2;
686 p = estrndup(ini_file, l);
687 zend_llist_add_element(&scanned_ini_list, &p);
688 }
689 zend_destroy_file_handle(&fh);
690 }
691 }
692 }
693 free(namelist[i]);
694 }
695 free(namelist);
696 }
697 }
698 efree(bufpath);
699
700 if (total_l) {
701 int php_ini_scanned_files_len = (php_ini_scanned_files) ? (int)strlen(php_ini_scanned_files) + 1 : 0;
702 php_ini_scanned_files = (char *) realloc(php_ini_scanned_files, php_ini_scanned_files_len + total_l + 1);
703 if (!php_ini_scanned_files_len) {
704 *php_ini_scanned_files = '\0';
705 }
706 total_l += php_ini_scanned_files_len;
707 for (element = scanned_ini_list.head; element; element = element->next) {
708 if (php_ini_scanned_files_len) {
709 strlcat(php_ini_scanned_files, ",\n", total_l);
710 }
711 strlcat(php_ini_scanned_files, *(char **)element->data, total_l);
712 strlcat(php_ini_scanned_files, element->next ? ",\n" : "\n", total_l);
713 }
714 }
715 zend_llist_destroy(&scanned_ini_list);
716 } else {
717 /* Make sure an empty php_ini_scanned_path ends up as NULL */
718 php_ini_scanned_path = NULL;
719 }
720
721 if (sapi_module.ini_entries) {
722 /* Reset active ini section */
723 RESET_ACTIVE_INI_HASH();
724 zend_parse_ini_string(sapi_module.ini_entries, 1, ZEND_INI_SCANNER_NORMAL, (zend_ini_parser_cb_t) php_ini_parser_cb, &configuration_hash);
725 }
726
727 return SUCCESS;
728 }
729 /* }}} */
730
731 /* {{{ php_shutdown_config */
php_shutdown_config(void)732 int php_shutdown_config(void)
733 {
734 zend_hash_destroy(&configuration_hash);
735 if (php_ini_opened_path) {
736 free(php_ini_opened_path);
737 php_ini_opened_path = NULL;
738 }
739 if (php_ini_scanned_files) {
740 free(php_ini_scanned_files);
741 php_ini_scanned_files = NULL;
742 }
743 return SUCCESS;
744 }
745 /* }}} */
746
747 /* {{{ php_ini_register_extensions */
php_ini_register_extensions(void)748 void php_ini_register_extensions(void)
749 {
750 zend_llist_apply(&extension_lists.engine, php_load_zend_extension_cb);
751 zend_llist_apply(&extension_lists.functions, php_load_php_extension_cb);
752
753 zend_llist_destroy(&extension_lists.engine);
754 zend_llist_destroy(&extension_lists.functions);
755 }
756 /* }}} */
757
758 /* {{{ php_parse_user_ini_file */
php_parse_user_ini_file(const char * dirname,const char * ini_filename,HashTable * target_hash)759 PHPAPI int php_parse_user_ini_file(const char *dirname, const char *ini_filename, HashTable *target_hash)
760 {
761 zend_stat_t sb = {0};
762 char ini_file[MAXPATHLEN];
763
764 snprintf(ini_file, MAXPATHLEN, "%s%c%s", dirname, DEFAULT_SLASH, ini_filename);
765
766 if (VCWD_STAT(ini_file, &sb) == 0) {
767 if (S_ISREG(sb.st_mode)) {
768 zend_file_handle fh;
769 int ret = FAILURE;
770
771 zend_stream_init_fp(&fh, VCWD_FOPEN(ini_file, "r"), ini_file);
772 if (fh.handle.fp) {
773 /* Reset active ini section */
774 RESET_ACTIVE_INI_HASH();
775
776 #if ZEND_RC_DEBUG
777 /* User inis are parsed during SAPI activate (part of the request),
778 * but persistently allocated to allow caching. This is fine as long as
779 * strings are duplicated in php_ini_activate_config(). */
780 bool orig_rc_debug = zend_rc_debug;
781 zend_rc_debug = false;
782 #endif
783 ret = zend_parse_ini_file(&fh, 1, ZEND_INI_SCANNER_NORMAL, (zend_ini_parser_cb_t) php_ini_parser_cb, target_hash);
784 #if ZEND_RC_DEBUG
785 zend_rc_debug = orig_rc_debug;
786 #endif
787 if (ret == SUCCESS) {
788 /* FIXME: Add parsed file to the list of user files read? */
789 }
790 }
791 zend_destroy_file_handle(&fh);
792 return ret;
793 }
794 }
795 return FAILURE;
796 }
797 /* }}} */
798
799 /* {{{ php_ini_activate_config */
php_ini_activate_config(HashTable * source_hash,int modify_type,int stage)800 PHPAPI void php_ini_activate_config(HashTable *source_hash, int modify_type, int stage)
801 {
802 zend_string *str;
803 zval *data;
804
805 /* Walk through config hash and alter matching ini entries using the values found in the hash */
806 ZEND_HASH_MAP_FOREACH_STR_KEY_VAL(source_hash, str, data) {
807 zend_string *data_str = zend_string_dup(Z_STR_P(data), 0);
808 zend_alter_ini_entry_ex(str, data_str, modify_type, stage, 0);
809 zend_string_release(data_str);
810 } ZEND_HASH_FOREACH_END();
811 }
812 /* }}} */
813
814 /* {{{ php_ini_has_per_dir_config */
php_ini_has_per_dir_config(void)815 PHPAPI int php_ini_has_per_dir_config(void)
816 {
817 return has_per_dir_config;
818 }
819 /* }}} */
820
821 /* {{{ php_ini_activate_per_dir_config */
php_ini_activate_per_dir_config(char * path,size_t path_len)822 PHPAPI void php_ini_activate_per_dir_config(char *path, size_t path_len)
823 {
824 zval *tmp2;
825 char *ptr;
826
827 #ifdef PHP_WIN32
828 char path_bak[MAXPATHLEN];
829 #endif
830
831 #ifdef PHP_WIN32
832 /* MAX_PATH is \0-terminated, path_len == MAXPATHLEN would overrun path_bak */
833 if (path_len >= MAXPATHLEN) {
834 #else
835 if (path_len > MAXPATHLEN) {
836 #endif
837 return;
838 }
839
840 #ifdef PHP_WIN32
841 memcpy(path_bak, path, path_len);
842 path_bak[path_len] = 0;
843 TRANSLATE_SLASHES_LOWER(path_bak);
844 path = path_bak;
845 #endif
846
847 /* Walk through each directory in path and apply any found per-dir-system-configuration from configuration_hash */
848 if (has_per_dir_config && path && path_len) {
849 ptr = path + 1;
850 while ((ptr = strchr(ptr, '/')) != NULL) {
851 *ptr = 0;
852 /* Search for source array matching the path from configuration_hash */
853 if ((tmp2 = zend_hash_str_find(&configuration_hash, path, strlen(path))) != NULL) {
854 php_ini_activate_config(Z_ARRVAL_P(tmp2), PHP_INI_SYSTEM, PHP_INI_STAGE_ACTIVATE);
855 }
856 *ptr = '/';
857 ptr++;
858 }
859 }
860 }
861 /* }}} */
862
863 /* {{{ php_ini_has_per_host_config */
864 PHPAPI int php_ini_has_per_host_config(void)
865 {
866 return has_per_host_config;
867 }
868 /* }}} */
869
870 /* {{{ php_ini_activate_per_host_config */
871 PHPAPI void php_ini_activate_per_host_config(const char *host, size_t host_len)
872 {
873 zval *tmp;
874
875 if (has_per_host_config && host && host_len) {
876 /* Search for source array matching the host from configuration_hash */
877 if ((tmp = zend_hash_str_find(&configuration_hash, host, host_len)) != NULL) {
878 php_ini_activate_config(Z_ARRVAL_P(tmp), PHP_INI_SYSTEM, PHP_INI_STAGE_ACTIVATE);
879 }
880 }
881 }
882 /* }}} */
883
884 /* {{{ cfg_get_entry */
885 PHPAPI zval *cfg_get_entry_ex(zend_string *name)
886 {
887 return zend_hash_find(&configuration_hash, name);
888 }
889 /* }}} */
890
891 /* {{{ cfg_get_entry */
892 PHPAPI zval *cfg_get_entry(const char *name, size_t name_length)
893 {
894 return zend_hash_str_find(&configuration_hash, name, name_length);
895 }
896 /* }}} */
897
898 /* {{{ cfg_get_long */
899 PHPAPI int cfg_get_long(const char *varname, zend_long *result)
900 {
901 zval *tmp;
902
903 if ((tmp = zend_hash_str_find(&configuration_hash, varname, strlen(varname))) == NULL) {
904 *result = 0;
905 return FAILURE;
906 }
907 *result = zval_get_long(tmp);
908 return SUCCESS;
909 }
910 /* }}} */
911
912 /* {{{ cfg_get_double */
913 PHPAPI int cfg_get_double(const char *varname, double *result)
914 {
915 zval *tmp;
916
917 if ((tmp = zend_hash_str_find(&configuration_hash, varname, strlen(varname))) == NULL) {
918 *result = (double) 0;
919 return FAILURE;
920 }
921 *result = zval_get_double(tmp);
922 return SUCCESS;
923 }
924 /* }}} */
925
926 /* {{{ cfg_get_string */
927 PHPAPI int cfg_get_string(const char *varname, char **result)
928 {
929 zval *tmp;
930
931 if ((tmp = zend_hash_str_find(&configuration_hash, varname, strlen(varname))) == NULL) {
932 *result = NULL;
933 return FAILURE;
934 }
935 *result = Z_STRVAL_P(tmp);
936 return SUCCESS;
937 }
938 /* }}} */
939
940 PHPAPI HashTable* php_ini_get_configuration_hash(void) /* {{{ */
941 {
942 return &configuration_hash;
943 } /* }}} */
944