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