xref: /PHP-8.0/main/php_ini.c (revision 5998b2a3)
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    | http://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 	zend_bool first = 1;
121 
122 	if (module) {
123 		module_number = module->module_number;
124 	} else {
125 		module_number = 0;
126 	}
127 
128 	ZEND_HASH_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_sectin 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 && !strcasecmp(Z_STRVAL_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 && !strcasecmp(Z_STRVAL_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(&section_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, &section_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 
398 /* {{{ php_init_config */
php_init_config(void)399 int php_init_config(void)
400 {
401 	char *php_ini_file_name = NULL;
402 	char *php_ini_search_path = NULL;
403 	int php_ini_scanned_path_len;
404 	char *open_basedir;
405 	int free_ini_search_path = 0;
406 	zend_string *opened_path = NULL;
407 	FILE *fp;
408 	const char *filename;
409 
410 	zend_hash_init(&configuration_hash, 8, NULL, config_zval_dtor, 1);
411 
412 	if (sapi_module.ini_defaults) {
413 		sapi_module.ini_defaults(&configuration_hash);
414 	}
415 
416 	zend_llist_init(&extension_lists.engine, sizeof(char *), (llist_dtor_func_t) free_estring, 1);
417 	zend_llist_init(&extension_lists.functions, sizeof(char *), (llist_dtor_func_t) free_estring, 1);
418 
419 	open_basedir = PG(open_basedir);
420 
421 	if (sapi_module.php_ini_path_override) {
422 		php_ini_file_name = sapi_module.php_ini_path_override;
423 		php_ini_search_path = sapi_module.php_ini_path_override;
424 		free_ini_search_path = 0;
425 	} else if (!sapi_module.php_ini_ignore) {
426 		int search_path_size;
427 		char *default_location;
428 		char *env_location;
429 		static const char paths_separator[] = { ZEND_PATHS_SEPARATOR, 0 };
430 #ifdef PHP_WIN32
431 		char *reg_location;
432 		char phprc_path[MAXPATHLEN];
433 #endif
434 
435 		env_location = getenv("PHPRC");
436 
437 #ifdef PHP_WIN32
438 		if (!env_location) {
439 			char dummybuf;
440 			int size;
441 
442 			SetLastError(0);
443 
444 			/*If the given buffer is not large enough to hold the data, the return value is
445 			the buffer size,  in characters, required to hold the string and its terminating
446 			null character. We use this return value to alloc the final buffer. */
447 			size = GetEnvironmentVariableA("PHPRC", &dummybuf, 0);
448 			if (GetLastError() == ERROR_ENVVAR_NOT_FOUND) {
449 				/* The environment variable doesn't exist. */
450 				env_location = "";
451 			} else {
452 				if (size == 0) {
453 					env_location = "";
454 				} else {
455 					size = GetEnvironmentVariableA("PHPRC", phprc_path, size);
456 					if (size == 0) {
457 						env_location = "";
458 					} else {
459 						env_location = phprc_path;
460 					}
461 				}
462 			}
463 		}
464 #else
465 		if (!env_location) {
466 			env_location = "";
467 		}
468 #endif
469 		/*
470 		 * Prepare search path
471 		 */
472 
473 		search_path_size = MAXPATHLEN * 4 + (int)strlen(env_location) + 3 + 1;
474 		php_ini_search_path = (char *) emalloc(search_path_size);
475 		free_ini_search_path = 1;
476 		php_ini_search_path[0] = 0;
477 
478 		/* Add environment location */
479 		if (env_location[0]) {
480 			if (*php_ini_search_path) {
481 				strlcat(php_ini_search_path, paths_separator, search_path_size);
482 			}
483 			strlcat(php_ini_search_path, env_location, search_path_size);
484 			php_ini_file_name = env_location;
485 		}
486 
487 #ifdef PHP_WIN32
488 		/* Add registry location */
489 		reg_location = GetIniPathFromRegistry();
490 		if (reg_location != NULL) {
491 			if (*php_ini_search_path) {
492 				strlcat(php_ini_search_path, paths_separator, search_path_size);
493 			}
494 			strlcat(php_ini_search_path, reg_location, search_path_size);
495 			efree(reg_location);
496 		}
497 #endif
498 
499 		/* Add cwd (not with CLI) */
500 		if (!sapi_module.php_ini_ignore_cwd) {
501 			if (*php_ini_search_path) {
502 				strlcat(php_ini_search_path, paths_separator, search_path_size);
503 			}
504 			strlcat(php_ini_search_path, ".", search_path_size);
505 		}
506 
507 		if (PG(php_binary)) {
508 			char *separator_location, *binary_location;
509 
510 			binary_location = estrdup(PG(php_binary));
511 			separator_location = strrchr(binary_location, DEFAULT_SLASH);
512 
513 			if (separator_location && separator_location != binary_location) {
514 				*(separator_location) = 0;
515 			}
516 			if (*php_ini_search_path) {
517 				strlcat(php_ini_search_path, paths_separator, search_path_size);
518 			}
519 			strlcat(php_ini_search_path, binary_location, search_path_size);
520 			efree(binary_location);
521 		}
522 
523 		/* Add default location */
524 #ifdef PHP_WIN32
525 		default_location = (char *) emalloc(MAXPATHLEN + 1);
526 
527 		if (0 < GetWindowsDirectory(default_location, MAXPATHLEN)) {
528 			if (*php_ini_search_path) {
529 				strlcat(php_ini_search_path, paths_separator, search_path_size);
530 			}
531 			strlcat(php_ini_search_path, default_location, search_path_size);
532 		}
533 
534 		/* For people running under terminal services, GetWindowsDirectory will
535 		 * return their personal Windows directory, so lets add the system
536 		 * windows directory too */
537 		if (0 < GetSystemWindowsDirectory(default_location, MAXPATHLEN)) {
538 			if (*php_ini_search_path) {
539 				strlcat(php_ini_search_path, paths_separator, search_path_size);
540 			}
541 			strlcat(php_ini_search_path, default_location, search_path_size);
542 		}
543 		efree(default_location);
544 
545 #else
546 		default_location = PHP_CONFIG_FILE_PATH;
547 		if (*php_ini_search_path) {
548 			strlcat(php_ini_search_path, paths_separator, search_path_size);
549 		}
550 		strlcat(php_ini_search_path, default_location, search_path_size);
551 #endif
552 	}
553 
554 	PG(open_basedir) = NULL;
555 
556 	/*
557 	 * Find and open actual ini file
558 	 */
559 
560 	fp = NULL;
561 	filename = NULL;
562 
563 	/* If SAPI does not want to ignore all ini files OR an overriding file/path is given.
564 	 * This allows disabling scanning for ini files in the PHP_CONFIG_FILE_SCAN_DIR but still
565 	 * load an optional ini file. */
566 	if (!sapi_module.php_ini_ignore || sapi_module.php_ini_path_override) {
567 
568 		/* Check if php_ini_file_name is a file and can be opened */
569 		if (php_ini_file_name && php_ini_file_name[0]) {
570 			zend_stat_t statbuf;
571 
572 			if (!VCWD_STAT(php_ini_file_name, &statbuf)) {
573 				if (!((statbuf.st_mode & S_IFMT) == S_IFDIR)) {
574 					fp = VCWD_FOPEN(php_ini_file_name, "r");
575 					if (fp) {
576 						filename = expand_filepath(php_ini_file_name, NULL);
577 					}
578 				}
579 			}
580 		}
581 
582 		/* Otherwise search for php-%sapi-module-name%.ini file in search path */
583 		if (!fp) {
584 			const char *fmt = "php-%s.ini";
585 			char *ini_fname;
586 			spprintf(&ini_fname, 0, fmt, sapi_module.name);
587 			fp = php_fopen_with_path(ini_fname, "r", php_ini_search_path, &opened_path);
588 			efree(ini_fname);
589 			if (fp) {
590 				filename = ZSTR_VAL(opened_path);
591 			}
592 		}
593 
594 		/* If still no ini file found, search for php.ini file in search path */
595 		if (!fp) {
596 			fp = php_fopen_with_path("php.ini", "r", php_ini_search_path, &opened_path);
597 			if (fp) {
598 				filename = ZSTR_VAL(opened_path);
599 			}
600 		}
601 	}
602 
603 	if (free_ini_search_path) {
604 		efree(php_ini_search_path);
605 	}
606 
607 	PG(open_basedir) = open_basedir;
608 
609 	if (fp) {
610 		zend_file_handle fh;
611 		zend_stream_init_fp(&fh, fp, filename);
612 		RESET_ACTIVE_INI_HASH();
613 
614 		zend_parse_ini_file(&fh, 1, ZEND_INI_SCANNER_NORMAL, (zend_ini_parser_cb_t) php_ini_parser_cb, &configuration_hash);
615 
616 		{
617 			zval tmp;
618 
619 			ZVAL_NEW_STR(&tmp, zend_string_init(fh.filename, strlen(fh.filename), 1));
620 			zend_hash_str_update(&configuration_hash, "cfg_file_path", sizeof("cfg_file_path")-1, &tmp);
621 			if (opened_path) {
622 				zend_string_release_ex(opened_path, 0);
623 			} else {
624 				efree((char *)fh.filename);
625 			}
626 			php_ini_opened_path = zend_strndup(Z_STRVAL(tmp), Z_STRLEN(tmp));
627 		}
628 	}
629 
630 	/* Check for PHP_INI_SCAN_DIR environment variable to override/set config file scan directory */
631 	php_ini_scanned_path = getenv("PHP_INI_SCAN_DIR");
632 	if (!php_ini_scanned_path) {
633 		/* Or fall back using possible --with-config-file-scan-dir setting (defaults to empty string!) */
634 		php_ini_scanned_path = PHP_CONFIG_FILE_SCAN_DIR;
635 	}
636 	php_ini_scanned_path_len = (int)strlen(php_ini_scanned_path);
637 
638 	/* Scan and parse any .ini files found in scan path if path not empty. */
639 	if (!sapi_module.php_ini_ignore && php_ini_scanned_path_len) {
640 		struct dirent **namelist;
641 		int ndir, i;
642 		zend_stat_t sb;
643 		char ini_file[MAXPATHLEN];
644 		char *p;
645 		zend_llist scanned_ini_list;
646 		zend_llist_element *element;
647 		int l, total_l = 0;
648 		char *bufpath, *debpath, *endpath;
649 		int lenpath;
650 
651 		zend_llist_init(&scanned_ini_list, sizeof(char *), (llist_dtor_func_t) free_estring, 1);
652 
653 		bufpath = estrdup(php_ini_scanned_path);
654 		for (debpath = bufpath ; debpath ; debpath=endpath) {
655 			endpath = strchr(debpath, DEFAULT_DIR_SEPARATOR);
656 			if (endpath) {
657 				*(endpath++) = 0;
658 			}
659 			if (!debpath[0]) {
660 				/* empty string means default builtin value
661 				   to allow "/foo/php.d:" or ":/foo/php.d" */
662 				debpath = PHP_CONFIG_FILE_SCAN_DIR;
663 			}
664 			lenpath = (int)strlen(debpath);
665 
666 			if (lenpath > 0 && (ndir = php_scandir(debpath, &namelist, 0, php_alphasort)) > 0) {
667 
668 				for (i = 0; i < ndir; i++) {
669 
670 					/* check for any file with .ini extension */
671 					if (!(p = strrchr(namelist[i]->d_name, '.')) || (p && strcmp(p, ".ini"))) {
672 						free(namelist[i]);
673 						continue;
674 					}
675 					/* Reset active ini section */
676 					RESET_ACTIVE_INI_HASH();
677 
678 					if (IS_SLASH(debpath[lenpath - 1])) {
679 						snprintf(ini_file, MAXPATHLEN, "%s%s", debpath, namelist[i]->d_name);
680 					} else {
681 						snprintf(ini_file, MAXPATHLEN, "%s%c%s", debpath, DEFAULT_SLASH, namelist[i]->d_name);
682 					}
683 					if (VCWD_STAT(ini_file, &sb) == 0) {
684 						if (S_ISREG(sb.st_mode)) {
685 							zend_file_handle fh;
686 							zend_stream_init_fp(&fh, VCWD_FOPEN(ini_file, "r"), ini_file);
687 							if (fh.handle.fp) {
688 								if (zend_parse_ini_file(&fh, 1, ZEND_INI_SCANNER_NORMAL, (zend_ini_parser_cb_t) php_ini_parser_cb, &configuration_hash) == SUCCESS) {
689 									/* Here, add it to the list of ini files read */
690 									l = (int)strlen(ini_file);
691 									total_l += l + 2;
692 									p = estrndup(ini_file, l);
693 									zend_llist_add_element(&scanned_ini_list, &p);
694 								}
695 							}
696 						}
697 					}
698 					free(namelist[i]);
699 				}
700 				free(namelist);
701 			}
702 		}
703 		efree(bufpath);
704 
705 		if (total_l) {
706 			int php_ini_scanned_files_len = (php_ini_scanned_files) ? (int)strlen(php_ini_scanned_files) + 1 : 0;
707 			php_ini_scanned_files = (char *) realloc(php_ini_scanned_files, php_ini_scanned_files_len + total_l + 1);
708 			if (!php_ini_scanned_files_len) {
709 				*php_ini_scanned_files = '\0';
710 			}
711 			total_l += php_ini_scanned_files_len;
712 			for (element = scanned_ini_list.head; element; element = element->next) {
713 				if (php_ini_scanned_files_len) {
714 					strlcat(php_ini_scanned_files, ",\n", total_l);
715 				}
716 				strlcat(php_ini_scanned_files, *(char **)element->data, total_l);
717 				strlcat(php_ini_scanned_files, element->next ? ",\n" : "\n", total_l);
718 			}
719 		}
720 		zend_llist_destroy(&scanned_ini_list);
721 	} else {
722 		/* Make sure an empty php_ini_scanned_path ends up as NULL */
723 		php_ini_scanned_path = NULL;
724 	}
725 
726 	if (sapi_module.ini_entries) {
727 		/* Reset active ini section */
728 		RESET_ACTIVE_INI_HASH();
729 		zend_parse_ini_string(sapi_module.ini_entries, 1, ZEND_INI_SCANNER_NORMAL, (zend_ini_parser_cb_t) php_ini_parser_cb, &configuration_hash);
730 	}
731 
732 	return SUCCESS;
733 }
734 /* }}} */
735 
736 /* {{{ php_shutdown_config */
php_shutdown_config(void)737 int php_shutdown_config(void)
738 {
739 	zend_hash_destroy(&configuration_hash);
740 	if (php_ini_opened_path) {
741 		free(php_ini_opened_path);
742 		php_ini_opened_path = NULL;
743 	}
744 	if (php_ini_scanned_files) {
745 		free(php_ini_scanned_files);
746 		php_ini_scanned_files = NULL;
747 	}
748 	return SUCCESS;
749 }
750 /* }}} */
751 
752 /* {{{ php_ini_register_extensions */
php_ini_register_extensions(void)753 void php_ini_register_extensions(void)
754 {
755 	zend_llist_apply(&extension_lists.engine, php_load_zend_extension_cb);
756 	zend_llist_apply(&extension_lists.functions, php_load_php_extension_cb);
757 
758 	zend_llist_destroy(&extension_lists.engine);
759 	zend_llist_destroy(&extension_lists.functions);
760 }
761 /* }}} */
762 
763 /* {{{ php_parse_user_ini_file */
php_parse_user_ini_file(const char * dirname,const char * ini_filename,HashTable * target_hash)764 PHPAPI int php_parse_user_ini_file(const char *dirname, const char *ini_filename, HashTable *target_hash)
765 {
766 	zend_stat_t sb;
767 	char ini_file[MAXPATHLEN];
768 
769 	snprintf(ini_file, MAXPATHLEN, "%s%c%s", dirname, DEFAULT_SLASH, ini_filename);
770 
771 	if (VCWD_STAT(ini_file, &sb) == 0) {
772 		if (S_ISREG(sb.st_mode)) {
773 			zend_file_handle fh;
774 			zend_stream_init_fp(&fh, VCWD_FOPEN(ini_file, "r"), ini_file);
775 			if (fh.handle.fp) {
776 				/* Reset active ini section */
777 				RESET_ACTIVE_INI_HASH();
778 
779 				if (zend_parse_ini_file(&fh, 1, ZEND_INI_SCANNER_NORMAL, (zend_ini_parser_cb_t) php_ini_parser_cb, target_hash) == SUCCESS) {
780 					/* FIXME: Add parsed file to the list of user files read? */
781 					return SUCCESS;
782 				}
783 				return FAILURE;
784 			}
785 		}
786 	}
787 	return FAILURE;
788 }
789 /* }}} */
790 
791 /* {{{ php_ini_activate_config */
php_ini_activate_config(HashTable * source_hash,int modify_type,int stage)792 PHPAPI void php_ini_activate_config(HashTable *source_hash, int modify_type, int stage)
793 {
794 	zend_string *str;
795 	zval *data;
796 
797 	/* Walk through config hash and alter matching ini entries using the values found in the hash */
798 	ZEND_HASH_FOREACH_STR_KEY_VAL(source_hash, str, data) {
799 		zend_alter_ini_entry_ex(str, Z_STR_P(data), modify_type, stage, 0);
800 	} ZEND_HASH_FOREACH_END();
801 }
802 /* }}} */
803 
804 /* {{{ php_ini_has_per_dir_config */
php_ini_has_per_dir_config(void)805 PHPAPI int php_ini_has_per_dir_config(void)
806 {
807 	return has_per_dir_config;
808 }
809 /* }}} */
810 
811 /* {{{ php_ini_activate_per_dir_config */
php_ini_activate_per_dir_config(char * path,size_t path_len)812 PHPAPI void php_ini_activate_per_dir_config(char *path, size_t path_len)
813 {
814 	zval *tmp2;
815 	char *ptr;
816 
817 #ifdef PHP_WIN32
818 	char path_bak[MAXPATHLEN];
819 #endif
820 
821 #ifdef PHP_WIN32
822 	/* MAX_PATH is \0-terminated, path_len == MAXPATHLEN would overrun path_bak */
823 	if (path_len >= MAXPATHLEN) {
824 #else
825 	if (path_len > MAXPATHLEN) {
826 #endif
827 		return;
828 	}
829 
830 #ifdef PHP_WIN32
831 	memcpy(path_bak, path, path_len);
832 	path_bak[path_len] = 0;
833 	TRANSLATE_SLASHES_LOWER(path_bak);
834 	path = path_bak;
835 #endif
836 
837 	/* Walk through each directory in path and apply any found per-dir-system-configuration from configuration_hash */
838 	if (has_per_dir_config && path && path_len) {
839 		ptr = path + 1;
840 		while ((ptr = strchr(ptr, '/')) != NULL) {
841 			*ptr = 0;
842 			/* Search for source array matching the path from configuration_hash */
843 			if ((tmp2 = zend_hash_str_find(&configuration_hash, path, strlen(path))) != NULL) {
844 				php_ini_activate_config(Z_ARRVAL_P(tmp2), PHP_INI_SYSTEM, PHP_INI_STAGE_ACTIVATE);
845 			}
846 			*ptr = '/';
847 			ptr++;
848 		}
849 	}
850 }
851 /* }}} */
852 
853 /* {{{ php_ini_has_per_host_config */
854 PHPAPI int php_ini_has_per_host_config(void)
855 {
856 	return has_per_host_config;
857 }
858 /* }}} */
859 
860 /* {{{ php_ini_activate_per_host_config */
861 PHPAPI void php_ini_activate_per_host_config(const char *host, size_t host_len)
862 {
863 	zval *tmp;
864 
865 	if (has_per_host_config && host && host_len) {
866 		/* Search for source array matching the host from configuration_hash */
867 		if ((tmp = zend_hash_str_find(&configuration_hash, host, host_len)) != NULL) {
868 			php_ini_activate_config(Z_ARRVAL_P(tmp), PHP_INI_SYSTEM, PHP_INI_STAGE_ACTIVATE);
869 		}
870 	}
871 }
872 /* }}} */
873 
874 /* {{{ cfg_get_entry */
875 PHPAPI zval *cfg_get_entry_ex(zend_string *name)
876 {
877 	return zend_hash_find(&configuration_hash, name);
878 }
879 /* }}} */
880 
881 /* {{{ cfg_get_entry */
882 PHPAPI zval *cfg_get_entry(const char *name, size_t name_length)
883 {
884 	return zend_hash_str_find(&configuration_hash, name, name_length);
885 }
886 /* }}} */
887 
888 /* {{{ cfg_get_long */
889 PHPAPI int cfg_get_long(const char *varname, zend_long *result)
890 {
891 	zval *tmp;
892 
893 	if ((tmp = zend_hash_str_find(&configuration_hash, varname, strlen(varname))) == NULL) {
894 		*result = 0;
895 		return FAILURE;
896 	}
897 	*result = zval_get_long(tmp);
898 	return SUCCESS;
899 }
900 /* }}} */
901 
902 /* {{{ cfg_get_double */
903 PHPAPI int cfg_get_double(const char *varname, double *result)
904 {
905 	zval *tmp;
906 
907 	if ((tmp = zend_hash_str_find(&configuration_hash, varname, strlen(varname))) == NULL) {
908 		*result = (double) 0;
909 		return FAILURE;
910 	}
911 	*result = zval_get_double(tmp);
912 	return SUCCESS;
913 }
914 /* }}} */
915 
916 /* {{{ cfg_get_string */
917 PHPAPI int cfg_get_string(const char *varname, char **result)
918 {
919 	zval *tmp;
920 
921 	if ((tmp = zend_hash_str_find(&configuration_hash, varname, strlen(varname))) == NULL) {
922 		*result = NULL;
923 		return FAILURE;
924 	}
925 	*result = Z_STRVAL_P(tmp);
926 	return SUCCESS;
927 }
928 /* }}} */
929 
930 PHPAPI HashTable* php_ini_get_configuration_hash(void) /* {{{ */
931 {
932 	return &configuration_hash;
933 } /* }}} */
934