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