1/*
2   +----------------------------------------------------------------------+
3   | PHP Version 5                                                        |
4   +----------------------------------------------------------------------+
5   | Copyright (c) 1997-2014 The PHP Group                                |
6   +----------------------------------------------------------------------+
7   | This source file is subject to version 3.01 of the PHP license,      |
8   | that is bundled with this package in the file LICENSE, and is        |
9   | available through the world-wide-web at the following url:           |
10   | http://www.php.net/license/3_01.txt                                  |
11   | If you did not receive a copy of the PHP license and are unable to   |
12   | obtain it through the world-wide-web, please send a note to          |
13   | license@php.net so we can mail you a copy immediately.               |
14   +----------------------------------------------------------------------+
15   | Author: Zeev Suraski <zeev@zend.com>                                 |
16   +----------------------------------------------------------------------+
17 */
18
19/* $Id$ */
20
21#include "php.h"
22#include "ext/standard/info.h"
23#include "zend_ini.h"
24#include "zend_ini_scanner.h"
25#include "php_ini.h"
26#include "ext/standard/dl.h"
27#include "zend_extensions.h"
28#include "zend_highlight.h"
29#include "SAPI.h"
30#include "php_main.h"
31#include "php_scandir.h"
32#ifdef PHP_WIN32
33#include "win32/php_registry.h"
34#endif
35
36#if HAVE_SCANDIR && HAVE_ALPHASORT && HAVE_DIRENT_H
37#include <dirent.h>
38#endif
39
40#ifndef S_ISREG
41#define S_ISREG(mode)   (((mode) & S_IFMT) == S_IFREG)
42#endif
43
44#ifdef PHP_WIN32
45#define TRANSLATE_SLASHES_LOWER(path) \
46    { \
47        char *tmp = path; \
48        while (*tmp) { \
49            if (*tmp == '\\') *tmp = '/'; \
50            else *tmp = tolower(*tmp); \
51                tmp++; \
52        } \
53    }
54#else
55#define TRANSLATE_SLASHES_LOWER(path)
56#endif
57
58
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        uint display_string_length, esc_html=0;
84
85        if (type == ZEND_INI_DISPLAY_ORIG && ini_entry->modified) {
86            if (ini_entry->orig_value && ini_entry->orig_value[0]) {
87                display_string = ini_entry->orig_value;
88                display_string_length = ini_entry->orig_value_length;
89                esc_html = !sapi_module.phpinfo_as_text;
90            } else {
91                if (!sapi_module.phpinfo_as_text) {
92                    display_string = "<i>no value</i>";
93                    display_string_length = sizeof("<i>no value</i>") - 1;
94                } else {
95                    display_string = "no value";
96                    display_string_length = sizeof("no value") - 1;
97                }
98            }
99        } else if (ini_entry->value && ini_entry->value[0]) {
100            display_string = ini_entry->value;
101            display_string_length = ini_entry->value_length;
102            esc_html = !sapi_module.phpinfo_as_text;
103        } else {
104            if (!sapi_module.phpinfo_as_text) {
105                display_string = "<i>no value</i>";
106                display_string_length = sizeof("<i>no value</i>") - 1;
107            } else {
108                display_string = "no value";
109                display_string_length = sizeof("no value") - 1;
110            }
111        }
112
113        if (esc_html) {
114            php_html_puts(display_string, display_string_length TSRMLS_CC);
115        } else {
116            PHPWRITE(display_string, display_string_length);
117        }
118    }
119}
120/* }}} */
121
122/* {{{ php_ini_displayer
123 */
124static int php_ini_displayer(zend_ini_entry *ini_entry, int module_number TSRMLS_DC)
125{
126    if (ini_entry->module_number != module_number) {
127        return 0;
128    }
129    if (!sapi_module.phpinfo_as_text) {
130        PUTS("<tr>");
131        PUTS("<td class=\"e\">");
132        PHPWRITE(ini_entry->name, ini_entry->name_length - 1);
133        PUTS("</td><td class=\"v\">");
134        php_ini_displayer_cb(ini_entry, ZEND_INI_DISPLAY_ACTIVE TSRMLS_CC);
135        PUTS("</td><td class=\"v\">");
136        php_ini_displayer_cb(ini_entry, ZEND_INI_DISPLAY_ORIG TSRMLS_CC);
137        PUTS("</td></tr>\n");
138    } else {
139        PHPWRITE(ini_entry->name, ini_entry->name_length - 1);
140        PUTS(" => ");
141        php_ini_displayer_cb(ini_entry, ZEND_INI_DISPLAY_ACTIVE TSRMLS_CC);
142        PUTS(" => ");
143        php_ini_displayer_cb(ini_entry, ZEND_INI_DISPLAY_ORIG TSRMLS_CC);
144        PUTS("\n");
145    }
146    return 0;
147}
148/* }}} */
149
150/* {{{ php_ini_available
151 */
152static int php_ini_available(zend_ini_entry *ini_entry, int *module_number_available TSRMLS_DC)
153{
154    if (ini_entry->module_number == *module_number_available) {
155        *module_number_available = -1;
156        return ZEND_HASH_APPLY_STOP;
157    } else {
158        return ZEND_HASH_APPLY_KEEP;
159    }
160}
161/* }}} */
162
163/* {{{ display_ini_entries
164 */
165PHPAPI void display_ini_entries(zend_module_entry *module)
166{
167    int module_number, module_number_available;
168    TSRMLS_FETCH();
169
170    if (module) {
171        module_number = module->module_number;
172    } else {
173        module_number = 0;
174    }
175    module_number_available = module_number;
176    zend_hash_apply_with_argument(EG(ini_directives), (apply_func_arg_t) php_ini_available, &module_number_available TSRMLS_CC);
177    if (module_number_available == -1) {
178        php_info_print_table_start();
179        php_info_print_table_header(3, "Directive", "Local Value", "Master Value");
180        zend_hash_apply_with_argument(EG(ini_directives), (apply_func_arg_t) php_ini_displayer, (void *) (zend_intptr_t) module_number TSRMLS_CC);
181        php_info_print_table_end();
182    }
183}
184/* }}} */
185
186/* php.ini support */
187#define PHP_EXTENSION_TOKEN     "extension"
188#define ZEND_EXTENSION_TOKEN    "zend_extension"
189
190/* {{{ config_zval_dtor
191 */
192PHPAPI void config_zval_dtor(zval *zvalue)
193{
194    if (Z_TYPE_P(zvalue) == IS_ARRAY) {
195        zend_hash_destroy(Z_ARRVAL_P(zvalue));
196        free(Z_ARRVAL_P(zvalue));
197    } else if (Z_TYPE_P(zvalue) == IS_STRING) {
198        free(Z_STRVAL_P(zvalue));
199    }
200}
201/* Reset / free active_ini_sectin global */
202#define RESET_ACTIVE_INI_HASH() do { \
203    active_ini_hash = NULL;          \
204    is_special_section = 0;          \
205} while (0)
206/* }}} */
207
208/* {{{ php_ini_parser_cb
209 */
210static void php_ini_parser_cb(zval *arg1, zval *arg2, zval *arg3, int callback_type, HashTable *target_hash)
211{
212    zval *entry;
213    HashTable *active_hash;
214    char *extension_name;
215
216    if (active_ini_hash) {
217        active_hash = active_ini_hash;
218    } else {
219        active_hash = target_hash;
220    }
221
222    switch (callback_type) {
223        case ZEND_INI_PARSER_ENTRY: {
224                if (!arg2) {
225                    /* bare string - nothing to do */
226                    break;
227                }
228
229                /* PHP and Zend extensions are not added into configuration hash! */
230                if (!is_special_section && !strcasecmp(Z_STRVAL_P(arg1), PHP_EXTENSION_TOKEN)) { /* load PHP extension */
231                    extension_name = estrndup(Z_STRVAL_P(arg2), Z_STRLEN_P(arg2));
232                    zend_llist_add_element(&extension_lists.functions, &extension_name);
233                } else if (!is_special_section && !strcasecmp(Z_STRVAL_P(arg1), ZEND_EXTENSION_TOKEN)) { /* load Zend extension */
234                    extension_name = estrndup(Z_STRVAL_P(arg2), Z_STRLEN_P(arg2));
235                    zend_llist_add_element(&extension_lists.engine, &extension_name);
236
237                /* All other entries are added into either configuration_hash or active ini section array */
238                } else {
239                    /* Store in active hash */
240                    zend_hash_update(active_hash, Z_STRVAL_P(arg1), Z_STRLEN_P(arg1) + 1, arg2, sizeof(zval), (void **) &entry);
241                    Z_STRVAL_P(entry) = zend_strndup(Z_STRVAL_P(entry), Z_STRLEN_P(entry));
242                }
243            }
244            break;
245
246        case ZEND_INI_PARSER_POP_ENTRY: {
247                zval *option_arr;
248                zval *find_arr;
249
250                if (!arg2) {
251                    /* bare string - nothing to do */
252                    break;
253                }
254
255/* fprintf(stdout, "ZEND_INI_PARSER_POP_ENTRY: %s[%s] = %s\n",Z_STRVAL_P(arg1), Z_STRVAL_P(arg3), Z_STRVAL_P(arg2)); */
256
257                /* If option not found in hash or is not an array -> create array, otherwise add to existing array */
258                if (zend_hash_find(active_hash, Z_STRVAL_P(arg1), Z_STRLEN_P(arg1) + 1, (void **) &find_arr) == FAILURE || Z_TYPE_P(find_arr) != IS_ARRAY) {
259                    option_arr = (zval *) pemalloc(sizeof(zval), 1);
260                    INIT_PZVAL(option_arr);
261                    Z_TYPE_P(option_arr) = IS_ARRAY;
262                    Z_ARRVAL_P(option_arr) = (HashTable *) pemalloc(sizeof(HashTable), 1);
263                    zend_hash_init(Z_ARRVAL_P(option_arr), 0, NULL, (dtor_func_t) config_zval_dtor, 1);
264                    zend_hash_update(active_hash, Z_STRVAL_P(arg1), Z_STRLEN_P(arg1) + 1, option_arr, sizeof(zval), (void **) &find_arr);
265                    free(option_arr);
266                }
267
268                /* arg3 is possible option offset name */
269                if (arg3 && Z_STRLEN_P(arg3) > 0) {
270                    zend_symtable_update(Z_ARRVAL_P(find_arr), Z_STRVAL_P(arg3), Z_STRLEN_P(arg3) + 1, arg2, sizeof(zval), (void **) &entry);
271                } else {
272                    zend_hash_next_index_insert(Z_ARRVAL_P(find_arr), arg2, sizeof(zval), (void **) &entry);
273                }
274                Z_STRVAL_P(entry) = zend_strndup(Z_STRVAL_P(entry), Z_STRLEN_P(entry));
275            }
276            break;
277
278        case ZEND_INI_PARSER_SECTION: { /* Create an array of entries of each section */
279
280/* fprintf(stdout, "ZEND_INI_PARSER_SECTION: %s\n",Z_STRVAL_P(arg1)); */
281
282                char *key = NULL;
283                uint key_len;
284
285                /* PATH sections */
286                if (!strncasecmp(Z_STRVAL_P(arg1), "PATH", sizeof("PATH") - 1)) {
287                    key = Z_STRVAL_P(arg1);
288                    key = key + sizeof("PATH") - 1;
289                    key_len = Z_STRLEN_P(arg1) - sizeof("PATH") + 1;
290                    is_special_section = 1;
291                    has_per_dir_config = 1;
292
293                    /* make the path lowercase on Windows, for case insensitivity. Does nothing for other platforms */
294                    TRANSLATE_SLASHES_LOWER(key);
295
296                /* HOST sections */
297                } else if (!strncasecmp(Z_STRVAL_P(arg1), "HOST", sizeof("HOST") - 1)) {
298                    key = Z_STRVAL_P(arg1);
299                    key = key + sizeof("HOST") - 1;
300                    key_len = Z_STRLEN_P(arg1) - sizeof("HOST") + 1;
301                    is_special_section = 1;
302                    has_per_host_config = 1;
303                    zend_str_tolower(key, key_len); /* host names are case-insensitive. */
304
305                } else {
306                    is_special_section = 0;
307                }
308
309                if (key && key_len > 0) {
310                    /* Strip any trailing slashes */
311                    while (key_len > 0 && (key[key_len - 1] == '/' || key[key_len - 1] == '\\')) {
312                        key_len--;
313                        key[key_len] = 0;
314                    }
315
316                    /* Strip any leading whitespace and '=' */
317                    while (*key && (
318                        *key == '=' ||
319                        *key == ' ' ||
320                        *key == '\t'
321                    )) {
322                        key++;
323                        key_len--;
324                    }
325
326                    /* Search for existing entry and if it does not exist create one */
327                    if (zend_hash_find(target_hash, key, key_len + 1, (void **) &entry) == FAILURE) {
328                        zval *section_arr;
329
330                        section_arr = (zval *) pemalloc(sizeof(zval), 1);
331                        INIT_PZVAL(section_arr);
332                        Z_TYPE_P(section_arr) = IS_ARRAY;
333                        Z_ARRVAL_P(section_arr) = (HashTable *) pemalloc(sizeof(HashTable), 1);
334                        zend_hash_init(Z_ARRVAL_P(section_arr), 0, NULL, (dtor_func_t) config_zval_dtor, 1);
335                        zend_hash_update(target_hash, key, key_len + 1, section_arr, sizeof(zval), (void **) &entry);
336                        free(section_arr);
337                    }
338                    active_ini_hash = Z_ARRVAL_P(entry);
339                }
340            }
341            break;
342    }
343}
344/* }}} */
345
346/* {{{ php_load_php_extension_cb
347 */
348static void php_load_php_extension_cb(void *arg TSRMLS_DC)
349{
350#ifdef HAVE_LIBDL
351    php_load_extension(*((char **) arg), MODULE_PERSISTENT, 0 TSRMLS_CC);
352#endif
353}
354/* }}} */
355
356/* {{{ php_load_zend_extension_cb
357 */
358static void php_load_zend_extension_cb(void *arg TSRMLS_DC)
359{
360    char *filename = *((char **) arg);
361    int length = strlen(filename);
362
363    if (IS_ABSOLUTE_PATH(filename, length)) {
364        zend_load_extension(filename TSRMLS_CC);
365    } else {
366        char *libpath;
367        char *extension_dir = INI_STR("extension_dir");
368        int extension_dir_len = strlen(extension_dir);
369
370        if (IS_SLASH(extension_dir[extension_dir_len-1])) {
371            spprintf(&libpath, 0, "%s%s", extension_dir, filename);
372        } else {
373            spprintf(&libpath, 0, "%s%c%s", extension_dir, DEFAULT_SLASH, filename);
374        }
375        zend_load_extension(libpath TSRMLS_CC);
376        efree(libpath);
377    }
378}
379/* }}} */
380
381/* {{{ php_init_config
382 */
383int php_init_config(TSRMLS_D)
384{
385    char *php_ini_file_name = NULL;
386    char *php_ini_search_path = NULL;
387    int php_ini_scanned_path_len;
388    char *open_basedir;
389    int free_ini_search_path = 0;
390    zend_file_handle fh;
391
392    if (zend_hash_init(&configuration_hash, 0, NULL, (dtor_func_t) config_zval_dtor, 1) == FAILURE) {
393        return FAILURE;
394    }
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 + 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            struct stat 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 = php_ini_opened_path = expand_filepath(php_ini_file_name, NULL TSRMLS_CC);
560                    }
561                }
562            }
563        }
564
565        /* Otherwise search for php-%sapi-module-name%.ini file in search path */
566        if (!fh.handle.fp) {
567            const char *fmt = "php-%s.ini";
568            char *ini_fname;
569            spprintf(&ini_fname, 0, fmt, sapi_module.name);
570            fh.handle.fp = php_fopen_with_path(ini_fname, "r", php_ini_search_path, &php_ini_opened_path TSRMLS_CC);
571            efree(ini_fname);
572            if (fh.handle.fp) {
573                fh.filename = php_ini_opened_path;
574            }
575        }
576
577        /* If still no ini file found, search for php.ini file in search path */
578        if (!fh.handle.fp) {
579            fh.handle.fp = php_fopen_with_path("php.ini", "r", php_ini_search_path, &php_ini_opened_path TSRMLS_CC);
580            if (fh.handle.fp) {
581                fh.filename = php_ini_opened_path;
582            }
583        }
584    }
585
586    if (free_ini_search_path) {
587        efree(php_ini_search_path);
588    }
589
590    PG(open_basedir) = open_basedir;
591
592    if (fh.handle.fp) {
593        fh.type = ZEND_HANDLE_FP;
594        RESET_ACTIVE_INI_HASH();
595
596        zend_parse_ini_file(&fh, 1, ZEND_INI_SCANNER_NORMAL, (zend_ini_parser_cb_t) php_ini_parser_cb, &configuration_hash TSRMLS_CC);
597
598        {
599            zval tmp;
600
601            Z_STRLEN(tmp) = strlen(fh.filename);
602            Z_STRVAL(tmp) = zend_strndup(fh.filename, Z_STRLEN(tmp));
603            Z_TYPE(tmp) = IS_STRING;
604            Z_SET_REFCOUNT(tmp, 0);
605
606            zend_hash_update(&configuration_hash, "cfg_file_path", sizeof("cfg_file_path"), (void *) &tmp, sizeof(zval), NULL);
607            if (php_ini_opened_path) {
608                efree(php_ini_opened_path);
609            }
610            php_ini_opened_path = zend_strndup(Z_STRVAL(tmp), Z_STRLEN(tmp));
611        }
612    }
613
614    /* Check for PHP_INI_SCAN_DIR environment variable to override/set config file scan directory */
615    php_ini_scanned_path = getenv("PHP_INI_SCAN_DIR");
616    if (!php_ini_scanned_path) {
617        /* Or fall back using possible --with-config-file-scan-dir setting (defaults to empty string!) */
618        php_ini_scanned_path = PHP_CONFIG_FILE_SCAN_DIR;
619    }
620    php_ini_scanned_path_len = strlen(php_ini_scanned_path);
621
622    /* Scan and parse any .ini files found in scan path if path not empty. */
623    if (!sapi_module.php_ini_ignore && php_ini_scanned_path_len) {
624        struct dirent **namelist;
625        int ndir, i;
626        struct stat sb;
627        char ini_file[MAXPATHLEN];
628        char *p;
629        zend_file_handle fh2;
630        zend_llist scanned_ini_list;
631        zend_llist_element *element;
632        int l, total_l = 0;
633        char *bufpath, *debpath, *endpath;
634        int lenpath;
635
636        zend_llist_init(&scanned_ini_list, sizeof(char *), (llist_dtor_func_t) free_estring, 1);
637        memset(&fh2, 0, sizeof(fh2));
638
639        bufpath = estrdup(php_ini_scanned_path);
640        for (debpath = bufpath ; debpath ; debpath=endpath) {
641            endpath = strchr(debpath, DEFAULT_DIR_SEPARATOR);
642            if (endpath) {
643                *(endpath++) = 0;
644            }
645            if (!debpath[0]) {
646                /* empty string means default builtin value
647                   to allow "/foo/phd.d:" or ":/foo/php.d" */
648                debpath = PHP_CONFIG_FILE_SCAN_DIR;
649            }
650            lenpath = strlen(debpath);
651
652            if (lenpath > 0 && (ndir = php_scandir(debpath, &namelist, 0, php_alphasort)) > 0) {
653
654                for (i = 0; i < ndir; i++) {
655
656                    /* check for any file with .ini extension */
657                    if (!(p = strrchr(namelist[i]->d_name, '.')) || (p && strcmp(p, ".ini"))) {
658                        free(namelist[i]);
659                        continue;
660                    }
661                    /* Reset active ini section */
662                    RESET_ACTIVE_INI_HASH();
663
664                    if (IS_SLASH(debpath[lenpath - 1])) {
665                        snprintf(ini_file, MAXPATHLEN, "%s%s", debpath, namelist[i]->d_name);
666                    } else {
667                        snprintf(ini_file, MAXPATHLEN, "%s%c%s", debpath, DEFAULT_SLASH, namelist[i]->d_name);
668                    }
669                    if (VCWD_STAT(ini_file, &sb) == 0) {
670                        if (S_ISREG(sb.st_mode)) {
671                            if ((fh2.handle.fp = VCWD_FOPEN(ini_file, "r"))) {
672                                fh2.filename = ini_file;
673                                fh2.type = ZEND_HANDLE_FP;
674
675                                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) {
676                                    /* Here, add it to the list of ini files read */
677                                    l = strlen(ini_file);
678                                    total_l += l + 2;
679                                    p = estrndup(ini_file, l);
680                                    zend_llist_add_element(&scanned_ini_list, &p);
681                                }
682                            }
683                        }
684                    }
685                    free(namelist[i]);
686                }
687                free(namelist);
688            }
689        }
690        efree(bufpath);
691
692        if (total_l) {
693            int php_ini_scanned_files_len = (php_ini_scanned_files) ? strlen(php_ini_scanned_files) + 1 : 0;
694            php_ini_scanned_files = (char *) realloc(php_ini_scanned_files, php_ini_scanned_files_len + total_l + 1);
695            if (!php_ini_scanned_files_len) {
696                *php_ini_scanned_files = '\0';
697            }
698            total_l += php_ini_scanned_files_len;
699            for (element = scanned_ini_list.head; element; element = element->next) {
700                if (php_ini_scanned_files_len) {
701                    strlcat(php_ini_scanned_files, ",\n", total_l);
702                }
703                strlcat(php_ini_scanned_files, *(char **)element->data, total_l);
704                strlcat(php_ini_scanned_files, element->next ? ",\n" : "\n", total_l);
705            }
706        }
707        zend_llist_destroy(&scanned_ini_list);
708    } else {
709        /* Make sure an empty php_ini_scanned_path ends up as NULL */
710        php_ini_scanned_path = NULL;
711    }
712
713    if (sapi_module.ini_entries) {
714        /* Reset active ini section */
715        RESET_ACTIVE_INI_HASH();
716        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);
717    }
718
719    return SUCCESS;
720}
721/* }}} */
722
723/* {{{ php_shutdown_config
724 */
725int php_shutdown_config(void)
726{
727    zend_hash_destroy(&configuration_hash);
728    if (php_ini_opened_path) {
729        free(php_ini_opened_path);
730        php_ini_opened_path = NULL;
731    }
732    if (php_ini_scanned_files) {
733        free(php_ini_scanned_files);
734        php_ini_scanned_files = NULL;
735    }
736    return SUCCESS;
737}
738/* }}} */
739
740/* {{{ php_ini_register_extensions
741 */
742void php_ini_register_extensions(TSRMLS_D)
743{
744    zend_llist_apply(&extension_lists.engine, php_load_zend_extension_cb TSRMLS_CC);
745    zend_llist_apply(&extension_lists.functions, php_load_php_extension_cb TSRMLS_CC);
746
747    zend_llist_destroy(&extension_lists.engine);
748    zend_llist_destroy(&extension_lists.functions);
749}
750/* }}} */
751
752/* {{{ php_parse_user_ini_file
753 */
754PHPAPI int php_parse_user_ini_file(const char *dirname, char *ini_filename, HashTable *target_hash TSRMLS_DC)
755{
756    struct stat sb;
757    char ini_file[MAXPATHLEN];
758    zend_file_handle fh;
759
760    snprintf(ini_file, MAXPATHLEN, "%s%c%s", dirname, DEFAULT_SLASH, ini_filename);
761
762    if (VCWD_STAT(ini_file, &sb) == 0) {
763        if (S_ISREG(sb.st_mode)) {
764            memset(&fh, 0, sizeof(fh));
765            if ((fh.handle.fp = VCWD_FOPEN(ini_file, "r"))) {
766                fh.filename = ini_file;
767                fh.type = ZEND_HANDLE_FP;
768
769                /* Reset active ini section */
770                RESET_ACTIVE_INI_HASH();
771
772                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) {
773                    /* FIXME: Add parsed file to the list of user files read? */
774                    return SUCCESS;
775                }
776                return FAILURE;
777            }
778        }
779    }
780    return FAILURE;
781}
782/* }}} */
783
784/* {{{ php_ini_activate_config
785 */
786PHPAPI void php_ini_activate_config(HashTable *source_hash, int modify_type, int stage TSRMLS_DC)
787{
788    char *str;
789    zval *data;
790    uint str_len;
791    ulong num_index;
792
793    /* Walk through config hash and alter matching ini entries using the values found in the hash */
794    for (zend_hash_internal_pointer_reset(source_hash);
795        zend_hash_get_current_key_ex(source_hash, &str, &str_len, &num_index, 0, NULL) == HASH_KEY_IS_STRING;
796        zend_hash_move_forward(source_hash)
797    ) {
798        zend_hash_get_current_data(source_hash, (void **) &data);
799        zend_alter_ini_entry_ex(str, str_len, Z_STRVAL_P(data), Z_STRLEN_P(data), modify_type, stage, 0 TSRMLS_CC);
800    }
801}
802/* }}} */
803
804/* {{{ php_ini_has_per_dir_config
805 */
806PHPAPI int php_ini_has_per_dir_config(void)
807{
808    return has_per_dir_config;
809}
810/* }}} */
811
812/* {{{ php_ini_activate_per_dir_config
813 */
814PHPAPI void php_ini_activate_per_dir_config(char *path, uint path_len TSRMLS_DC)
815{
816    zval *tmp2;
817    char *ptr;
818
819#if PHP_WIN32
820    char path_bak[MAXPATHLEN];
821#endif
822
823#if PHP_WIN32
824    /* MAX_PATH is \0-terminated, path_len == MAXPATHLEN would overrun path_bak */
825    if (path_len >= MAXPATHLEN) {
826#else
827    if (path_len > MAXPATHLEN) {
828#endif
829        return;
830    }
831
832#if PHP_WIN32
833    memcpy(path_bak, path, path_len);
834    path_bak[path_len] = 0;
835    TRANSLATE_SLASHES_LOWER(path_bak);
836    path = path_bak;
837#endif
838
839    /* Walk through each directory in path and apply any found per-dir-system-configuration from configuration_hash */
840    if (has_per_dir_config && path && path_len) {
841        ptr = path + 1;
842        while ((ptr = strchr(ptr, '/')) != NULL) {
843            *ptr = 0;
844            /* Search for source array matching the path from configuration_hash */
845            if (zend_hash_find(&configuration_hash, path, strlen(path) + 1, (void **) &tmp2) == SUCCESS) {
846                php_ini_activate_config(Z_ARRVAL_P(tmp2), PHP_INI_SYSTEM, PHP_INI_STAGE_ACTIVATE TSRMLS_CC);
847            }
848            *ptr = '/';
849            ptr++;
850        }
851    }
852}
853/* }}} */
854
855/* {{{ php_ini_has_per_host_config
856 */
857PHPAPI int php_ini_has_per_host_config(void)
858{
859    return has_per_host_config;
860}
861/* }}} */
862
863/* {{{ php_ini_activate_per_host_config
864 */
865PHPAPI void php_ini_activate_per_host_config(const char *host, uint host_len TSRMLS_DC)
866{
867    zval *tmp;
868
869    if (has_per_host_config && host && host_len) {
870        /* Search for source array matching the host from configuration_hash */
871        if (zend_hash_find(&configuration_hash, host, host_len, (void **) &tmp) == SUCCESS) {
872            php_ini_activate_config(Z_ARRVAL_P(tmp), PHP_INI_SYSTEM, PHP_INI_STAGE_ACTIVATE TSRMLS_CC);
873        }
874    }
875}
876/* }}} */
877
878/* {{{ cfg_get_entry
879 */
880PHPAPI zval *cfg_get_entry(const char *name, uint name_length)
881{
882    zval *tmp;
883
884    if (zend_hash_find(&configuration_hash, name, name_length, (void **) &tmp) == SUCCESS) {
885        return tmp;
886    } else {
887        return NULL;
888    }
889}
890/* }}} */
891
892/* {{{ cfg_get_long
893 */
894PHPAPI int cfg_get_long(const char *varname, long *result)
895{
896    zval *tmp, var;
897
898    if (zend_hash_find(&configuration_hash, varname, strlen(varname) + 1, (void **) &tmp) == FAILURE) {
899        *result = 0;
900        return FAILURE;
901    }
902    var = *tmp;
903    zval_copy_ctor(&var);
904    convert_to_long(&var);
905    *result = Z_LVAL(var);
906    return SUCCESS;
907}
908/* }}} */
909
910/* {{{ cfg_get_double
911 */
912PHPAPI int cfg_get_double(const char *varname, double *result)
913{
914    zval *tmp, var;
915
916    if (zend_hash_find(&configuration_hash, varname, strlen(varname) + 1, (void **) &tmp) == FAILURE) {
917        *result = (double) 0;
918        return FAILURE;
919    }
920    var = *tmp;
921    zval_copy_ctor(&var);
922    convert_to_double(&var);
923    *result = Z_DVAL(var);
924    return SUCCESS;
925}
926/* }}} */
927
928/* {{{ cfg_get_string
929 */
930PHPAPI int cfg_get_string(const char *varname, char **result)
931{
932    zval *tmp;
933
934    if (zend_hash_find(&configuration_hash, varname, strlen(varname)+1, (void **) &tmp) == FAILURE) {
935        *result = NULL;
936        return FAILURE;
937    }
938    *result = Z_STRVAL_P(tmp);
939    return SUCCESS;
940}
941/* }}} */
942
943PHPAPI HashTable* php_ini_get_configuration_hash(void) /* {{{ */
944{
945    return &configuration_hash;
946} /* }}} */
947
948/*
949 * Local variables:
950 * tab-width: 4
951 * c-basic-offset: 4
952 * indent-tabs-mode: t
953 * End:
954 * vim600: sw=4 ts=4 fdm=marker
955 * vim<600: sw=4 ts=4
956 */
957