1/*
2   +----------------------------------------------------------------------+
3   | PHP Version 7                                                        |
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#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 bugger 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                        opened_path = zend_string_init(fh.filename, strlen(fh.filename), 0);
557                    }
558                }
559            }
560        }
561
562        /* Otherwise search for php-%sapi-module-name%.ini file in search path */
563        if (!fh.handle.fp) {
564            const char *fmt = "php-%s.ini";
565            char *ini_fname;
566            spprintf(&ini_fname, 0, fmt, sapi_module.name);
567            fh.handle.fp = php_fopen_with_path(ini_fname, "r", php_ini_search_path, &opened_path);
568            efree(ini_fname);
569            if (fh.handle.fp) {
570                fh.filename = ZSTR_VAL(opened_path);
571            }
572        }
573
574        /* If still no ini file found, search for php.ini file in search path */
575        if (!fh.handle.fp) {
576            fh.handle.fp = php_fopen_with_path("php.ini", "r", php_ini_search_path, &opened_path);
577            if (fh.handle.fp) {
578                fh.filename = ZSTR_VAL(opened_path);
579            }
580        }
581    }
582
583    if (free_ini_search_path) {
584        efree(php_ini_search_path);
585    }
586
587    PG(open_basedir) = open_basedir;
588
589    if (fh.handle.fp) {
590        fh.type = ZEND_HANDLE_FP;
591        RESET_ACTIVE_INI_HASH();
592
593        zend_parse_ini_file(&fh, 1, ZEND_INI_SCANNER_NORMAL, (zend_ini_parser_cb_t) php_ini_parser_cb, &configuration_hash);
594
595        {
596            zval tmp;
597
598            ZVAL_NEW_STR(&tmp, zend_string_init(fh.filename, strlen(fh.filename), 1));
599            zend_hash_str_update(&configuration_hash, "cfg_file_path", sizeof("cfg_file_path")-1, &tmp);
600            if (opened_path) {
601                zend_string_release(opened_path);
602            }
603            php_ini_opened_path = zend_strndup(Z_STRVAL(tmp), Z_STRLEN(tmp));
604        }
605    }
606
607    /* Check for PHP_INI_SCAN_DIR environment variable to override/set config file scan directory */
608    php_ini_scanned_path = getenv("PHP_INI_SCAN_DIR");
609    if (!php_ini_scanned_path) {
610        /* Or fall back using possible --with-config-file-scan-dir setting (defaults to empty string!) */
611        php_ini_scanned_path = PHP_CONFIG_FILE_SCAN_DIR;
612    }
613    php_ini_scanned_path_len = (int)strlen(php_ini_scanned_path);
614
615    /* Scan and parse any .ini files found in scan path if path not empty. */
616    if (!sapi_module.php_ini_ignore && php_ini_scanned_path_len) {
617        struct dirent **namelist;
618        int ndir, i;
619        zend_stat_t sb;
620        char ini_file[MAXPATHLEN];
621        char *p;
622        zend_file_handle fh2;
623        zend_llist scanned_ini_list;
624        zend_llist_element *element;
625        int l, total_l = 0;
626        char *bufpath, *debpath, *endpath;
627        int lenpath;
628
629        zend_llist_init(&scanned_ini_list, sizeof(char *), (llist_dtor_func_t) free_estring, 1);
630        memset(&fh2, 0, sizeof(fh2));
631
632        bufpath = estrdup(php_ini_scanned_path);
633        for (debpath = bufpath ; debpath ; debpath=endpath) {
634            endpath = strchr(debpath, DEFAULT_DIR_SEPARATOR);
635            if (endpath) {
636                *(endpath++) = 0;
637            }
638            if (!debpath[0]) {
639                /* empty string means default builtin value
640                   to allow "/foo/phd.d:" or ":/foo/php.d" */
641                debpath = PHP_CONFIG_FILE_SCAN_DIR;
642            }
643            lenpath = (int)strlen(debpath);
644
645            if (lenpath > 0 && (ndir = php_scandir(debpath, &namelist, 0, php_alphasort)) > 0) {
646
647                for (i = 0; i < ndir; i++) {
648
649                    /* check for any file with .ini extension */
650                    if (!(p = strrchr(namelist[i]->d_name, '.')) || (p && strcmp(p, ".ini"))) {
651                        free(namelist[i]);
652                        continue;
653                    }
654                    /* Reset active ini section */
655                    RESET_ACTIVE_INI_HASH();
656
657                    if (IS_SLASH(debpath[lenpath - 1])) {
658                        snprintf(ini_file, MAXPATHLEN, "%s%s", debpath, namelist[i]->d_name);
659                    } else {
660                        snprintf(ini_file, MAXPATHLEN, "%s%c%s", debpath, DEFAULT_SLASH, namelist[i]->d_name);
661                    }
662                    if (VCWD_STAT(ini_file, &sb) == 0) {
663                        if (S_ISREG(sb.st_mode)) {
664                            if ((fh2.handle.fp = VCWD_FOPEN(ini_file, "r"))) {
665                                fh2.filename = ini_file;
666                                fh2.type = ZEND_HANDLE_FP;
667
668                                if (zend_parse_ini_file(&fh2, 1, ZEND_INI_SCANNER_NORMAL, (zend_ini_parser_cb_t) php_ini_parser_cb, &configuration_hash) == SUCCESS) {
669                                    /* Here, add it to the list of ini files read */
670                                    l = (int)strlen(ini_file);
671                                    total_l += l + 2;
672                                    p = estrndup(ini_file, l);
673                                    zend_llist_add_element(&scanned_ini_list, &p);
674                                }
675                            }
676                        }
677                    }
678                    free(namelist[i]);
679                }
680                free(namelist);
681            }
682        }
683        efree(bufpath);
684
685        if (total_l) {
686            int php_ini_scanned_files_len = (php_ini_scanned_files) ? (int)strlen(php_ini_scanned_files) + 1 : 0;
687            php_ini_scanned_files = (char *) realloc(php_ini_scanned_files, php_ini_scanned_files_len + total_l + 1);
688            if (!php_ini_scanned_files_len) {
689                *php_ini_scanned_files = '\0';
690            }
691            total_l += php_ini_scanned_files_len;
692            for (element = scanned_ini_list.head; element; element = element->next) {
693                if (php_ini_scanned_files_len) {
694                    strlcat(php_ini_scanned_files, ",\n", total_l);
695                }
696                strlcat(php_ini_scanned_files, *(char **)element->data, total_l);
697                strlcat(php_ini_scanned_files, element->next ? ",\n" : "\n", total_l);
698            }
699        }
700        zend_llist_destroy(&scanned_ini_list);
701    } else {
702        /* Make sure an empty php_ini_scanned_path ends up as NULL */
703        php_ini_scanned_path = NULL;
704    }
705
706    if (sapi_module.ini_entries) {
707        /* Reset active ini section */
708        RESET_ACTIVE_INI_HASH();
709        zend_parse_ini_string(sapi_module.ini_entries, 1, ZEND_INI_SCANNER_NORMAL, (zend_ini_parser_cb_t) php_ini_parser_cb, &configuration_hash);
710    }
711
712    return SUCCESS;
713}
714/* }}} */
715
716/* {{{ php_shutdown_config
717 */
718int php_shutdown_config(void)
719{
720    zend_hash_destroy(&configuration_hash);
721    if (php_ini_opened_path) {
722        free(php_ini_opened_path);
723        php_ini_opened_path = NULL;
724    }
725    if (php_ini_scanned_files) {
726        free(php_ini_scanned_files);
727        php_ini_scanned_files = NULL;
728    }
729    return SUCCESS;
730}
731/* }}} */
732
733/* {{{ php_ini_register_extensions
734 */
735void php_ini_register_extensions(void)
736{
737    zend_llist_apply(&extension_lists.engine, php_load_zend_extension_cb);
738    zend_llist_apply(&extension_lists.functions, php_load_php_extension_cb);
739
740    zend_llist_destroy(&extension_lists.engine);
741    zend_llist_destroy(&extension_lists.functions);
742}
743/* }}} */
744
745/* {{{ php_parse_user_ini_file
746 */
747PHPAPI int php_parse_user_ini_file(const char *dirname, char *ini_filename, HashTable *target_hash)
748{
749    zend_stat_t sb;
750    char ini_file[MAXPATHLEN];
751    zend_file_handle fh;
752
753    snprintf(ini_file, MAXPATHLEN, "%s%c%s", dirname, DEFAULT_SLASH, ini_filename);
754
755    if (VCWD_STAT(ini_file, &sb) == 0) {
756        if (S_ISREG(sb.st_mode)) {
757            memset(&fh, 0, sizeof(fh));
758            if ((fh.handle.fp = VCWD_FOPEN(ini_file, "r"))) {
759                fh.filename = ini_file;
760                fh.type = ZEND_HANDLE_FP;
761
762                /* Reset active ini section */
763                RESET_ACTIVE_INI_HASH();
764
765                if (zend_parse_ini_file(&fh, 1, ZEND_INI_SCANNER_NORMAL, (zend_ini_parser_cb_t) php_ini_parser_cb, target_hash) == SUCCESS) {
766                    /* FIXME: Add parsed file to the list of user files read? */
767                    return SUCCESS;
768                }
769                return FAILURE;
770            }
771        }
772    }
773    return FAILURE;
774}
775/* }}} */
776
777/* {{{ php_ini_activate_config
778 */
779PHPAPI void php_ini_activate_config(HashTable *source_hash, int modify_type, int stage)
780{
781    zend_string *str;
782    zval *data;
783
784    /* Walk through config hash and alter matching ini entries using the values found in the hash */
785    ZEND_HASH_FOREACH_STR_KEY_VAL(source_hash, str, data) {
786        zend_alter_ini_entry_ex(str, Z_STR_P(data), modify_type, stage, 0);
787    } ZEND_HASH_FOREACH_END();
788}
789/* }}} */
790
791/* {{{ php_ini_has_per_dir_config
792 */
793PHPAPI int php_ini_has_per_dir_config(void)
794{
795    return has_per_dir_config;
796}
797/* }}} */
798
799/* {{{ php_ini_activate_per_dir_config
800 */
801PHPAPI void php_ini_activate_per_dir_config(char *path, size_t path_len)
802{
803    zval *tmp2;
804    char *ptr;
805
806#if PHP_WIN32
807    char path_bak[MAXPATHLEN];
808#endif
809
810#if PHP_WIN32
811    /* MAX_PATH is \0-terminated, path_len == MAXPATHLEN would overrun path_bak */
812    if (path_len >= MAXPATHLEN) {
813#else
814    if (path_len > MAXPATHLEN) {
815#endif
816        return;
817    }
818
819#if PHP_WIN32
820    memcpy(path_bak, path, path_len);
821    path_bak[path_len] = 0;
822    TRANSLATE_SLASHES_LOWER(path_bak);
823    path = path_bak;
824#endif
825
826    /* Walk through each directory in path and apply any found per-dir-system-configuration from configuration_hash */
827    if (has_per_dir_config && path && path_len) {
828        ptr = path + 1;
829        while ((ptr = strchr(ptr, '/')) != NULL) {
830            *ptr = 0;
831            /* Search for source array matching the path from configuration_hash */
832            if ((tmp2 = zend_hash_str_find(&configuration_hash, path, strlen(path))) != NULL) {
833                php_ini_activate_config(Z_ARRVAL_P(tmp2), PHP_INI_SYSTEM, PHP_INI_STAGE_ACTIVATE);
834            }
835            *ptr = '/';
836            ptr++;
837        }
838    }
839}
840/* }}} */
841
842/* {{{ php_ini_has_per_host_config
843 */
844PHPAPI int php_ini_has_per_host_config(void)
845{
846    return has_per_host_config;
847}
848/* }}} */
849
850/* {{{ php_ini_activate_per_host_config
851 */
852PHPAPI void php_ini_activate_per_host_config(const char *host, size_t host_len)
853{
854    zval *tmp;
855
856    if (has_per_host_config && host && host_len) {
857        /* Search for source array matching the host from configuration_hash */
858        if ((tmp = zend_hash_str_find(&configuration_hash, host, host_len)) != NULL) {
859            php_ini_activate_config(Z_ARRVAL_P(tmp), PHP_INI_SYSTEM, PHP_INI_STAGE_ACTIVATE);
860        }
861    }
862}
863/* }}} */
864
865/* {{{ cfg_get_entry
866 */
867PHPAPI zval *cfg_get_entry_ex(zend_string *name)
868{
869    return zend_hash_find(&configuration_hash, name);
870}
871/* }}} */
872
873/* {{{ cfg_get_entry
874 */
875PHPAPI zval *cfg_get_entry(const char *name, size_t name_length)
876{
877    return zend_hash_str_find(&configuration_hash, name, name_length);
878}
879/* }}} */
880
881/* {{{ cfg_get_long
882 */
883PHPAPI int cfg_get_long(const char *varname, zend_long *result)
884{
885    zval *tmp;
886
887    if ((tmp = zend_hash_str_find(&configuration_hash, varname, strlen(varname))) == NULL) {
888        *result = 0;
889        return FAILURE;
890    }
891    *result = zval_get_long(tmp);
892    return SUCCESS;
893}
894/* }}} */
895
896/* {{{ cfg_get_double
897 */
898PHPAPI int cfg_get_double(const char *varname, double *result)
899{
900    zval *tmp;
901
902    if ((tmp = zend_hash_str_find(&configuration_hash, varname, strlen(varname))) == NULL) {
903        *result = (double) 0;
904        return FAILURE;
905    }
906    *result = zval_get_double(tmp);
907    return SUCCESS;
908}
909/* }}} */
910
911/* {{{ cfg_get_string
912 */
913PHPAPI int cfg_get_string(const char *varname, char **result)
914{
915    zval *tmp;
916
917    if ((tmp = zend_hash_str_find(&configuration_hash, varname, strlen(varname))) == NULL) {
918        *result = NULL;
919        return FAILURE;
920    }
921    *result = Z_STRVAL_P(tmp);
922    return SUCCESS;
923}
924/* }}} */
925
926PHPAPI HashTable* php_ini_get_configuration_hash(void) /* {{{ */
927{
928    return &configuration_hash;
929} /* }}} */
930
931/*
932 * Local variables:
933 * tab-width: 4
934 * c-basic-offset: 4
935 * indent-tabs-mode: t
936 * End:
937 * vim600: sw=4 ts=4 fdm=marker
938 * vim<600: sw=4 ts=4
939 */
940