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