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