1/*
2   +----------------------------------------------------------------------+
3   | PHP Version 7                                                        |
4   +----------------------------------------------------------------------+
5   | Copyright (c) 1997-2014 The PHP Group                                |
6   +----------------------------------------------------------------------+
7   | This source file is subject to version 3.01 of the PHP license,      |
8   | that is bundled with this package in the file LICENSE, and is        |
9   | available through the world-wide-web at the following url:           |
10   | http://www.php.net/license/3_01.txt                                  |
11   | If you did not receive a copy of the PHP license and are unable to   |
12   | obtain it through the world-wide-web, please send a note to          |
13   | license@php.net so we can mail you a copy immediately.               |
14   +----------------------------------------------------------------------+
15   | Author: Zeev Suraski <zeev@zend.com>                                 |
16   +----------------------------------------------------------------------+
17 */
18
19/* $Id$ */
20
21#include "php.h"
22#include "ext/standard/info.h"
23#include "zend_ini.h"
24#include "zend_ini_scanner.h"
25#include "php_ini.h"
26#include "ext/standard/dl.h"
27#include "zend_extensions.h"
28#include "zend_highlight.h"
29#include "SAPI.h"
30#include "php_main.h"
31#include "php_scandir.h"
32#ifdef PHP_WIN32
33#include "win32/php_registry.h"
34#endif
35
36#if HAVE_SCANDIR && HAVE_ALPHASORT && HAVE_DIRENT_H
37#include <dirent.h>
38#endif
39
40#ifndef S_ISREG
41#define S_ISREG(mode)   (((mode) & S_IFMT) == S_IFREG)
42#endif
43
44#ifdef PHP_WIN32
45#define TRANSLATE_SLASHES_LOWER(path) \
46    { \
47        char *tmp = path; \
48        while (*tmp) { \
49            if (*tmp == '\\') *tmp = '/'; \
50            else *tmp = tolower(*tmp); \
51                tmp++; \
52        } \
53    }
54#else
55#define TRANSLATE_SLASHES_LOWER(path)
56#endif
57
58
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 TSRMLS_DC)
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 TSRMLS_CC);
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 TSRMLS_DC)
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 TSRMLS_CC);
139        PUTS("</td><td class=\"v\">");
140        php_ini_displayer_cb(ini_entry, ZEND_INI_DISPLAY_ORIG TSRMLS_CC);
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 TSRMLS_CC);
146        PUTS(" => ");
147        php_ini_displayer_cb(ini_entry, ZEND_INI_DISPLAY_ORIG TSRMLS_CC);
148        PUTS("\n");
149    }
150    return 0;
151}
152/* }}} */
153
154/* {{{ php_ini_available
155 */
156static int php_ini_available(zval *el, void *arg TSRMLS_DC)
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    TSRMLS_FETCH();
175
176    if (module) {
177        module_number = module->module_number;
178    } else {
179        module_number = 0;
180    }
181    module_number_available = module_number;
182    zend_hash_apply_with_argument(EG(ini_directives), php_ini_available, &module_number_available TSRMLS_CC);
183    if (module_number_available == -1) {
184        php_info_print_table_start();
185        php_info_print_table_header(3, "Directive", "Local Value", "Master Value");
186        zend_hash_apply_with_argument(EG(ini_directives), php_ini_displayer, (void *)&module_number TSRMLS_CC);
187        php_info_print_table_end();
188    }
189}
190/* }}} */
191
192/* php.ini support */
193#define PHP_EXTENSION_TOKEN     "extension"
194#define ZEND_EXTENSION_TOKEN    "zend_extension"
195
196/* {{{ config_zval_dtor
197 */
198PHPAPI void config_zval_dtor(zval *zvalue)
199{
200    if (Z_TYPE_P(zvalue) == IS_ARRAY) {
201        zend_hash_destroy(Z_ARRVAL_P(zvalue));
202        free(Z_ARR_P(zvalue));
203    } else if (Z_TYPE_P(zvalue) == IS_STRING) {
204        zend_string_release(Z_STR_P(zvalue));
205    }
206}
207/* Reset / free active_ini_sectin global */
208#define RESET_ACTIVE_INI_HASH() do { \
209    active_ini_hash = NULL;          \
210    is_special_section = 0;          \
211} while (0)
212/* }}} */
213
214/* {{{ php_ini_parser_cb
215 */
216static void php_ini_parser_cb(zval *arg1, zval *arg2, zval *arg3, int callback_type, HashTable *target_hash)
217{
218    zval *entry;
219    HashTable *active_hash;
220    char *extension_name;
221
222    if (active_ini_hash) {
223        active_hash = active_ini_hash;
224    } else {
225        active_hash = target_hash;
226    }
227
228    switch (callback_type) {
229        case ZEND_INI_PARSER_ENTRY: {
230                if (!arg2) {
231                    /* bare string - nothing to do */
232                    break;
233                }
234
235                /* PHP and Zend extensions are not added into configuration hash! */
236                if (!is_special_section && !strcasecmp(Z_STRVAL_P(arg1), PHP_EXTENSION_TOKEN)) { /* load PHP extension */
237                    extension_name = estrndup(Z_STRVAL_P(arg2), Z_STRLEN_P(arg2));
238                    zend_llist_add_element(&extension_lists.functions, &extension_name);
239                } else if (!is_special_section && !strcasecmp(Z_STRVAL_P(arg1), ZEND_EXTENSION_TOKEN)) { /* load Zend extension */
240                    extension_name = estrndup(Z_STRVAL_P(arg2), Z_STRLEN_P(arg2));
241                    zend_llist_add_element(&extension_lists.engine, &extension_name);
242
243                /* All other entries are added into either configuration_hash or active ini section array */
244                } else {
245                    /* Store in active hash */
246                    entry = zend_hash_update(active_hash, Z_STR_P(arg1), arg2);
247                    Z_STR_P(entry) = zend_string_dup(Z_STR_P(entry), 1);
248                }
249            }
250            break;
251
252        case ZEND_INI_PARSER_POP_ENTRY: {
253                zval option_arr;
254                zval *find_arr;
255
256                if (!arg2) {
257                    /* bare string - nothing to do */
258                    break;
259                }
260
261/* fprintf(stdout, "ZEND_INI_PARSER_POP_ENTRY: %s[%s] = %s\n",Z_STRVAL_P(arg1), Z_STRVAL_P(arg3), Z_STRVAL_P(arg2)); */
262
263                /* If option not found in hash or is not an array -> create array, otherwise add to existing array */
264                if ((find_arr = zend_hash_find(active_hash, Z_STR_P(arg1))) == NULL || Z_TYPE_P(find_arr) != IS_ARRAY) {
265                    ZVAL_NEW_PERSISTENT_ARR(&option_arr);
266                    zend_hash_init(Z_ARRVAL(option_arr), 8, NULL, config_zval_dtor, 1);
267                    find_arr = zend_hash_update(active_hash, Z_STR_P(arg1), &option_arr);
268                }
269
270                /* arg3 is possible option offset name */
271                if (arg3 && Z_STRLEN_P(arg3) > 0) {
272                    entry = zend_symtable_update(Z_ARRVAL_P(find_arr), Z_STR_P(arg3), arg2);
273                } else {
274                    entry = zend_hash_next_index_insert(Z_ARRVAL_P(find_arr), arg2);
275                }
276                Z_STR_P(entry) = zend_string_dup(Z_STR_P(entry), 1);
277            }
278            break;
279
280        case ZEND_INI_PARSER_SECTION: { /* Create an array of entries of each section */
281
282/* fprintf(stdout, "ZEND_INI_PARSER_SECTION: %s\n",Z_STRVAL_P(arg1)); */
283
284                char *key = NULL;
285                size_t key_len;
286
287                /* PATH sections */
288                if (!strncasecmp(Z_STRVAL_P(arg1), "PATH", sizeof("PATH") - 1)) {
289                    key = Z_STRVAL_P(arg1);
290                    key = key + sizeof("PATH") - 1;
291                    key_len = Z_STRLEN_P(arg1) - sizeof("PATH") + 1;
292                    is_special_section = 1;
293                    has_per_dir_config = 1;
294
295                    /* make the path lowercase on Windows, for case insensitivity. Does nothing for other platforms */
296                    TRANSLATE_SLASHES_LOWER(key);
297
298                /* HOST sections */
299                } else if (!strncasecmp(Z_STRVAL_P(arg1), "HOST", sizeof("HOST") - 1)) {
300                    key = Z_STRVAL_P(arg1);
301                    key = key + sizeof("HOST") - 1;
302                    key_len = Z_STRLEN_P(arg1) - sizeof("HOST") + 1;
303                    is_special_section = 1;
304                    has_per_host_config = 1;
305                    zend_str_tolower(key, key_len); /* host names are case-insensitive. */
306
307                } else {
308                    is_special_section = 0;
309                }
310
311                if (key && key_len > 0) {
312                    /* Strip any trailing slashes */
313                    while (key_len > 0 && (key[key_len - 1] == '/' || key[key_len - 1] == '\\')) {
314                        key_len--;
315                        key[key_len] = 0;
316                    }
317
318                    /* Strip any leading whitespace and '=' */
319                    while (*key && (
320                        *key == '=' ||
321                        *key == ' ' ||
322                        *key == '\t'
323                    )) {
324                        key++;
325                        key_len--;
326                    }
327
328                    /* Search for existing entry and if it does not exist create one */
329                    if ((entry = zend_hash_str_find(target_hash, key, key_len)) == NULL) {
330                        zval section_arr;
331
332                        ZVAL_NEW_PERSISTENT_ARR(&section_arr);
333                        zend_hash_init(Z_ARRVAL(section_arr), 8, NULL, (dtor_func_t) config_zval_dtor, 1);
334                        entry = zend_hash_str_update(target_hash, key, key_len, &section_arr);
335                    }
336                    active_ini_hash = Z_ARRVAL_P(entry);
337                }
338            }
339            break;
340    }
341}
342/* }}} */
343
344/* {{{ php_load_php_extension_cb
345 */
346static void php_load_php_extension_cb(void *arg TSRMLS_DC)
347{
348#ifdef HAVE_LIBDL
349    php_load_extension(*((char **) arg), MODULE_PERSISTENT, 0 TSRMLS_CC);
350#endif
351}
352/* }}} */
353
354/* {{{ php_load_zend_extension_cb
355 */
356static void php_load_zend_extension_cb(void *arg TSRMLS_DC)
357{
358    char *filename = *((char **) arg);
359    const int length = (int)strlen(filename);
360
361    if (IS_ABSOLUTE_PATH(filename, length)) {
362        zend_load_extension(filename TSRMLS_CC);
363    } else {
364        char *libpath;
365        char *extension_dir = INI_STR("extension_dir");
366        int extension_dir_len = (int)strlen(extension_dir);
367
368        if (IS_SLASH(extension_dir[extension_dir_len-1])) {
369            spprintf(&libpath, 0, "%s%s", extension_dir, filename);
370        } else {
371            spprintf(&libpath, 0, "%s%c%s", extension_dir, DEFAULT_SLASH, filename);
372        }
373        zend_load_extension(libpath TSRMLS_CC);
374        efree(libpath);
375    }
376}
377/* }}} */
378
379/* {{{ php_init_config
380 */
381int php_init_config(TSRMLS_D)
382{
383    char *php_ini_file_name = NULL;
384    char *php_ini_search_path = NULL;
385    int php_ini_scanned_path_len;
386    char *open_basedir;
387    int free_ini_search_path = 0;
388    zend_file_handle fh;
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 = php_ini_opened_path = expand_filepath(php_ini_file_name, NULL TSRMLS_CC);
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, &php_ini_opened_path TSRMLS_CC);
567            efree(ini_fname);
568            if (fh.handle.fp) {
569                fh.filename = php_ini_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, &php_ini_opened_path TSRMLS_CC);
576            if (fh.handle.fp) {
577                fh.filename = php_ini_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 TSRMLS_CC);
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 (php_ini_opened_path) {
600                efree(php_ini_opened_path);
601            }
602            php_ini_opened_path = zend_strndup(Z_STRVAL(tmp), Z_STRLEN(tmp));
603        }
604    }
605
606    /* Check for PHP_INI_SCAN_DIR environment variable to override/set config file scan directory */
607    php_ini_scanned_path = getenv("PHP_INI_SCAN_DIR");
608    if (!php_ini_scanned_path) {
609        /* Or fall back using possible --with-config-file-scan-dir setting (defaults to empty string!) */
610        php_ini_scanned_path = PHP_CONFIG_FILE_SCAN_DIR;
611    }
612    php_ini_scanned_path_len = (int)strlen(php_ini_scanned_path);
613
614    /* Scan and parse any .ini files found in scan path if path not empty. */
615    if (!sapi_module.php_ini_ignore && php_ini_scanned_path_len) {
616        struct dirent **namelist;
617        int ndir, i;
618        zend_stat_t sb;
619        char ini_file[MAXPATHLEN];
620        char *p;
621        zend_file_handle fh2;
622        zend_llist scanned_ini_list;
623        zend_llist_element *element;
624        int l, total_l = 0;
625        char *bufpath, *debpath, *endpath;
626        int lenpath;
627
628        zend_llist_init(&scanned_ini_list, sizeof(char *), (llist_dtor_func_t) free_estring, 1);
629        memset(&fh2, 0, sizeof(fh2));
630
631        bufpath = estrdup(php_ini_scanned_path);
632        for (debpath = bufpath ; debpath ; debpath=endpath) {
633            endpath = strchr(debpath, DEFAULT_DIR_SEPARATOR);
634            if (endpath) {
635                *(endpath++) = 0;
636            }
637            if (!debpath[0]) {
638                /* empty string means default builtin value
639                   to allow "/foo/phd.d:" or ":/foo/php.d" */
640                debpath = PHP_CONFIG_FILE_SCAN_DIR;
641            }
642            lenpath = (int)strlen(debpath);
643
644            if (lenpath > 0 && (ndir = php_scandir(debpath, &namelist, 0, php_alphasort)) > 0) {
645
646                for (i = 0; i < ndir; i++) {
647
648                    /* check for any file with .ini extension */
649                    if (!(p = strrchr(namelist[i]->d_name, '.')) || (p && strcmp(p, ".ini"))) {
650                        free(namelist[i]);
651                        continue;
652                    }
653                    /* Reset active ini section */
654                    RESET_ACTIVE_INI_HASH();
655
656                    if (IS_SLASH(debpath[lenpath - 1])) {
657                        snprintf(ini_file, MAXPATHLEN, "%s%s", debpath, namelist[i]->d_name);
658                    } else {
659                        snprintf(ini_file, MAXPATHLEN, "%s%c%s", debpath, DEFAULT_SLASH, namelist[i]->d_name);
660                    }
661                    if (VCWD_STAT(ini_file, &sb) == 0) {
662                        if (S_ISREG(sb.st_mode)) {
663                            if ((fh2.handle.fp = VCWD_FOPEN(ini_file, "r"))) {
664                                fh2.filename = ini_file;
665                                fh2.type = ZEND_HANDLE_FP;
666
667                                if (zend_parse_ini_file(&fh2, 1, ZEND_INI_SCANNER_NORMAL, (zend_ini_parser_cb_t) php_ini_parser_cb, &configuration_hash TSRMLS_CC) == SUCCESS) {
668                                    /* Here, add it to the list of ini files read */
669                                    l = (int)strlen(ini_file);
670                                    total_l += l + 2;
671                                    p = estrndup(ini_file, l);
672                                    zend_llist_add_element(&scanned_ini_list, &p);
673                                }
674                            }
675                        }
676                    }
677                    free(namelist[i]);
678                }
679                free(namelist);
680            }
681        }
682        efree(bufpath);
683
684        if (total_l) {
685            int php_ini_scanned_files_len = (php_ini_scanned_files) ? (int)strlen(php_ini_scanned_files) + 1 : 0;
686            php_ini_scanned_files = (char *) realloc(php_ini_scanned_files, php_ini_scanned_files_len + total_l + 1);
687            if (!php_ini_scanned_files_len) {
688                *php_ini_scanned_files = '\0';
689            }
690            total_l += php_ini_scanned_files_len;
691            for (element = scanned_ini_list.head; element; element = element->next) {
692                if (php_ini_scanned_files_len) {
693                    strlcat(php_ini_scanned_files, ",\n", total_l);
694                }
695                strlcat(php_ini_scanned_files, *(char **)element->data, total_l);
696                strlcat(php_ini_scanned_files, element->next ? ",\n" : "\n", total_l);
697            }
698        }
699        zend_llist_destroy(&scanned_ini_list);
700    } else {
701        /* Make sure an empty php_ini_scanned_path ends up as NULL */
702        php_ini_scanned_path = NULL;
703    }
704
705    if (sapi_module.ini_entries) {
706        /* Reset active ini section */
707        RESET_ACTIVE_INI_HASH();
708        zend_parse_ini_string(sapi_module.ini_entries, 1, ZEND_INI_SCANNER_NORMAL, (zend_ini_parser_cb_t) php_ini_parser_cb, &configuration_hash TSRMLS_CC);
709    }
710
711    return SUCCESS;
712}
713/* }}} */
714
715/* {{{ php_shutdown_config
716 */
717int php_shutdown_config(void)
718{
719    zend_hash_destroy(&configuration_hash);
720    if (php_ini_opened_path) {
721        free(php_ini_opened_path);
722        php_ini_opened_path = NULL;
723    }
724    if (php_ini_scanned_files) {
725        free(php_ini_scanned_files);
726        php_ini_scanned_files = NULL;
727    }
728    return SUCCESS;
729}
730/* }}} */
731
732/* {{{ php_ini_register_extensions
733 */
734void php_ini_register_extensions(TSRMLS_D)
735{
736    zend_llist_apply(&extension_lists.engine, php_load_zend_extension_cb TSRMLS_CC);
737    zend_llist_apply(&extension_lists.functions, php_load_php_extension_cb TSRMLS_CC);
738
739    zend_llist_destroy(&extension_lists.engine);
740    zend_llist_destroy(&extension_lists.functions);
741}
742/* }}} */
743
744/* {{{ php_parse_user_ini_file
745 */
746PHPAPI int php_parse_user_ini_file(const char *dirname, char *ini_filename, HashTable *target_hash TSRMLS_DC)
747{
748    zend_stat_t sb;
749    char ini_file[MAXPATHLEN];
750    zend_file_handle fh;
751
752    snprintf(ini_file, MAXPATHLEN, "%s%c%s", dirname, DEFAULT_SLASH, ini_filename);
753
754    if (VCWD_STAT(ini_file, &sb) == 0) {
755        if (S_ISREG(sb.st_mode)) {
756            memset(&fh, 0, sizeof(fh));
757            if ((fh.handle.fp = VCWD_FOPEN(ini_file, "r"))) {
758                fh.filename = ini_file;
759                fh.type = ZEND_HANDLE_FP;
760
761                /* Reset active ini section */
762                RESET_ACTIVE_INI_HASH();
763
764                if (zend_parse_ini_file(&fh, 1, ZEND_INI_SCANNER_NORMAL, (zend_ini_parser_cb_t) php_ini_parser_cb, target_hash TSRMLS_CC) == SUCCESS) {
765                    /* FIXME: Add parsed file to the list of user files read? */
766                    return SUCCESS;
767                }
768                return FAILURE;
769            }
770        }
771    }
772    return FAILURE;
773}
774/* }}} */
775
776/* {{{ php_ini_activate_config
777 */
778PHPAPI void php_ini_activate_config(HashTable *source_hash, int modify_type, int stage TSRMLS_DC)
779{
780    zend_string *str;
781    zval *data;
782    zend_ulong num_index;
783
784    /* Walk through config hash and alter matching ini entries using the values found in the hash */
785    for (zend_hash_internal_pointer_reset(source_hash);
786        zend_hash_get_current_key(source_hash, &str, &num_index, 0) == HASH_KEY_IS_STRING;
787        zend_hash_move_forward(source_hash)
788    ) {
789        data = zend_hash_get_current_data(source_hash);
790        zend_alter_ini_entry_ex(str, Z_STR_P(data), modify_type, stage, 0 TSRMLS_CC);
791    }
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, uint path_len TSRMLS_DC)
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 TSRMLS_CC);
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, uint host_len TSRMLS_DC)
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 TSRMLS_CC);
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, uint 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, var;
890
891    if ((tmp = zend_hash_str_find(&configuration_hash, varname, strlen(varname))) == NULL) {
892        *result = 0;
893        return FAILURE;
894    }
895    ZVAL_DUP(&var, tmp);
896    convert_to_long(&var);
897    *result = Z_LVAL(var);
898    return SUCCESS;
899}
900/* }}} */
901
902/* {{{ cfg_get_double
903 */
904PHPAPI int cfg_get_double(const char *varname, double *result)
905{
906    zval *tmp, var;
907
908    if ((tmp = zend_hash_str_find(&configuration_hash, varname, strlen(varname))) == NULL) {
909        *result = (double) 0;
910        return FAILURE;
911    }
912    ZVAL_DUP(&var, tmp);
913    convert_to_double(&var);
914    *result = Z_DVAL(var);
915    return SUCCESS;
916}
917/* }}} */
918
919/* {{{ cfg_get_string
920 */
921PHPAPI int cfg_get_string(const char *varname, char **result)
922{
923    zval *tmp;
924
925    if ((tmp = zend_hash_str_find(&configuration_hash, varname, strlen(varname))) == NULL) {
926        *result = NULL;
927        return FAILURE;
928    }
929    *result = Z_STRVAL_P(tmp);
930    return SUCCESS;
931}
932/* }}} */
933
934PHPAPI HashTable* php_ini_get_configuration_hash(void) /* {{{ */
935{
936    return &configuration_hash;
937} /* }}} */
938
939/*
940 * Local variables:
941 * tab-width: 4
942 * c-basic-offset: 4
943 * indent-tabs-mode: t
944 * End:
945 * vim600: sw=4 ts=4 fdm=marker
946 * vim<600: sw=4 ts=4
947 */
948