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