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