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