Coverage Report

Created: 2026-06-02 06:39

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