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