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