Coverage Report

Created: 2026-10-28 06:20

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
20
{
68
20
  if (ini_entry->displayer) {
69
0
    ini_entry->displayer(ini_entry, type);
70
20
  } else {
71
20
    char *display_string;
72
20
    size_t display_string_length;
73
20
    int esc_html=0;
74
75
20
    if (type == ZEND_INI_DISPLAY_ORIG && ini_entry->modified) {
76
0
      if (ini_entry->orig_value && ZSTR_VAL(ini_entry->orig_value)[0]) {
77
0
        display_string = ZSTR_VAL(ini_entry->orig_value);
78
0
        display_string_length = ZSTR_LEN(ini_entry->orig_value);
79
0
        esc_html = !sapi_module.phpinfo_as_text;
80
0
      } 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
20
    } else if (ini_entry->value && ZSTR_VAL(ini_entry->value)[0]) {
90
20
      display_string = ZSTR_VAL(ini_entry->value);
91
20
      display_string_length = ZSTR_LEN(ini_entry->value);
92
20
      esc_html = !sapi_module.phpinfo_as_text;
93
20
    } else {
94
0
      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
0
      } else {
98
0
        display_string = "no value";
99
0
        display_string_length = sizeof("no value") - 1;
100
0
      }
101
0
    }
102
103
20
    if (esc_html) {
104
0
      php_html_puts(display_string, display_string_length);
105
20
    } else {
106
20
      PHPWRITE(display_string, display_string_length);
107
20
    }
108
20
  }
109
20
}
110
/* }}} */
111
112
/* {{{ display_ini_entries */
113
PHPAPI ZEND_COLD void display_ini_entries(zend_module_entry *module)
114
2
{
115
2
  int module_number;
116
2
  zend_ini_entry *ini_entry;
117
2
  bool first = 1;
118
119
2
  if (module) {
120
2
    module_number = module->module_number;
121
2
  } else {
122
0
    module_number = 0;
123
0
  }
124
125
724
  ZEND_HASH_MAP_FOREACH_PTR(EG(ini_directives), ini_entry) {
126
724
    if (ini_entry->module_number != module_number) {
127
350
      continue;
128
350
    }
129
10
    if (first) {
130
2
      php_info_print_table_start();
131
2
      php_info_print_table_header(3, "Directive", "Local Value", "Master Value");
132
2
      first = 0;
133
2
    }
134
10
    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
10
    } else {
144
10
      PHPWRITE(ZSTR_VAL(ini_entry->name), ZSTR_LEN(ini_entry->name));
145
10
      PUTS(" => ");
146
10
      php_ini_displayer_cb(ini_entry, ZEND_INI_DISPLAY_ACTIVE);
147
10
      PUTS(" => ");
148
10
      php_ini_displayer_cb(ini_entry, ZEND_INI_DISPLAY_ORIG);
149
10
      PUTS("\n");
150
10
    }
151
10
  } ZEND_HASH_FOREACH_END();
152
2
  if (!first) {
153
2
    php_info_print_table_end();
154
2
  }
155
2
}
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
16
#define RESET_ACTIVE_INI_HASH() do { \
174
16
  active_ini_hash = NULL;          \
175
16
  is_special_section = false;      \
176
16
} 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
212
{
182
212
  zval *entry;
183
212
  HashTable *active_hash;
184
212
  char *extension_name;
185
186
212
  if (active_ini_hash) {
187
0
    active_hash = active_ini_hash;
188
212
  } else {
189
212
    active_hash = target_hash;
190
212
  }
191
192
212
  switch (callback_type) {
193
212
    case ZEND_INI_PARSER_ENTRY: {
194
212
        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
212
        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
212
        } 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
212
        } else {
209
          /* Store in active hash */
210
212
          entry = zend_hash_update(active_hash, Z_STR_P(arg1), arg2);
211
212
          Z_STR_P(entry) = zend_string_dup(Z_STR_P(entry), true);
212
212
        }
213
212
      }
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
212
  }
307
212
}
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
32
{
397
32
  static const char paths_separator[] = { ZEND_PATHS_SEPARATOR, 0 };
398
399
32
  if (*php_ini_search_path) {
400
16
    strlcat(php_ini_search_path, paths_separator, search_path_size);
401
16
  }
402
403
32
  strlcat(php_ini_search_path, path, search_path_size);
404
32
}
405
406
/* {{{ php_init_config */
407
void php_init_config(void)
408
16
{
409
16
  char *php_ini_file_name = NULL;
410
16
  char *php_ini_search_path = NULL;
411
16
  size_t php_ini_scanned_path_len;
412
16
  char *open_basedir;
413
16
  bool free_ini_search_path = false;
414
16
  zend_string *opened_path = NULL;
415
416
16
  zend_hash_init(&configuration_hash, 8, NULL, config_zval_dtor, true);
417
418
16
  if (sapi_module.ini_defaults) {
419
0
    sapi_module.ini_defaults(&configuration_hash);
420
0
  }
421
422
16
  zend_llist_init(&extension_lists.engine, sizeof(char *), (llist_dtor_func_t) free_estring, true);
423
16
  zend_llist_init(&extension_lists.functions, sizeof(char *), (llist_dtor_func_t) free_estring, true);
424
425
16
  open_basedir = PG(open_basedir);
426
427
16
  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
16
  } else if (!sapi_module.php_ini_ignore) {
432
16
    size_t search_path_size;
433
16
    char *default_location;
434
16
    char *env_location;
435
#ifdef PHP_WIN32
436
    char *reg_location;
437
    char phprc_path[MAXPATHLEN];
438
#endif
439
440
16
    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
16
    if (!env_location) {
471
16
      env_location = "";
472
16
    }
473
16
#endif
474
    /*
475
     * Prepare search path
476
     */
477
478
16
    search_path_size = MAXPATHLEN * 4 + strlen(env_location) + 3 + 1;
479
16
    php_ini_search_path = (char *) emalloc(search_path_size);
480
16
    free_ini_search_path = true;
481
16
    php_ini_search_path[0] = 0;
482
483
    /* Add environment location */
484
16
    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
16
    if (!sapi_module.php_ini_ignore_cwd) {
500
16
      append_ini_path(php_ini_search_path, search_path_size, ".");
501
16
    }
502
503
16
    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
16
    default_location = PHP_CONFIG_FILE_PATH;
535
16
    append_ini_path(php_ini_search_path, search_path_size, default_location);
536
16
#endif
537
16
  }
538
539
16
  PG(open_basedir) = NULL;
540
541
  /*
542
   * Find and open actual ini file
543
   */
544
545
16
  FILE *fp = NULL;
546
16
  char *filename = NULL;
547
16
  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
16
  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
16
    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
            if (filename) {
564
0
              free_filename = true;
565
0
            } else {
566
              /* Reject the file, like ZTS where VCWD_STAT() already fails */
567
0
              fclose(fp);
568
0
              fp = NULL;
569
0
            }
570
0
          }
571
0
        }
572
0
      }
573
0
    }
574
575
    /* Otherwise search for php-%sapi-module-name%.ini file in search path */
576
16
    if (!fp) {
577
16
      const char *fmt = "php-%s.ini";
578
16
      char *ini_fname;
579
16
      spprintf(&ini_fname, 0, fmt, sapi_module.name);
580
16
      fp = php_fopen_with_path(ini_fname, "r", php_ini_search_path, &opened_path);
581
16
      efree(ini_fname);
582
16
      if (fp) {
583
0
        filename = ZSTR_VAL(opened_path);
584
0
      }
585
16
    }
586
587
    /* If still no ini file found, search for php.ini file in search path */
588
16
    if (!fp) {
589
16
      fp = php_fopen_with_path("php.ini", "r", php_ini_search_path, &opened_path);
590
16
      if (fp) {
591
0
        filename = ZSTR_VAL(opened_path);
592
0
      }
593
16
    }
594
16
  }
595
596
16
  if (free_ini_search_path) {
597
16
    efree(php_ini_search_path);
598
16
  }
599
600
16
  PG(open_basedir) = open_basedir;
601
602
16
  if (fp) {
603
0
    zend_file_handle fh;
604
0
    zend_stream_init_fp(&fh, fp, filename);
605
0
    RESET_ACTIVE_INI_HASH();
606
607
0
    zend_parse_ini_file(&fh, true, ZEND_INI_SCANNER_NORMAL, (zend_ini_parser_cb_t) php_ini_parser_cb, &configuration_hash);
608
609
0
    {
610
0
      zval tmp;
611
612
0
      ZVAL_NEW_STR(&tmp, zend_string_init(filename, strlen(filename), true));
613
0
      zend_hash_str_update(&configuration_hash, "cfg_file_path", sizeof("cfg_file_path")-1, &tmp);
614
0
      if (opened_path) {
615
0
        zend_string_release_ex(opened_path, false);
616
0
      }
617
0
      php_ini_opened_path = zend_strndup(Z_STRVAL(tmp), Z_STRLEN(tmp));
618
0
    }
619
0
    zend_destroy_file_handle(&fh);
620
621
0
    if (free_filename) {
622
0
      efree(filename);
623
0
    }
624
0
  }
625
626
  /* Check for PHP_INI_SCAN_DIR environment variable to override/set config file scan directory */
627
16
  php_ini_scanned_path = getenv("PHP_INI_SCAN_DIR");
628
16
  if (!php_ini_scanned_path) {
629
    /* Or fall back using possible --with-config-file-scan-dir setting (defaults to empty string!) */
630
16
    php_ini_scanned_path = PHP_CONFIG_FILE_SCAN_DIR;
631
16
  }
632
16
  php_ini_scanned_path_len = strlen(php_ini_scanned_path);
633
634
  /* Scan and parse any .ini files found in scan path if path not empty. */
635
16
  if (!sapi_module.php_ini_ignore && php_ini_scanned_path_len) {
636
0
    struct dirent **namelist;
637
0
    zend_stat_t sb = {0};
638
0
    char ini_file[MAXPATHLEN];
639
0
    char *p;
640
0
    zend_llist scanned_ini_list;
641
0
    size_t total_l = 0;
642
0
    char *bufpath, *debpath, *endpath;
643
0
    size_t lenpath;
644
645
0
    zend_llist_init(&scanned_ini_list, sizeof(char *), (llist_dtor_func_t) free_estring, true);
646
647
0
    bufpath = estrdup(php_ini_scanned_path);
648
0
    for (debpath = bufpath ; debpath ; debpath=endpath) {
649
0
      endpath = strchr(debpath, DEFAULT_DIR_SEPARATOR);
650
0
      if (endpath) {
651
0
        *(endpath++) = 0;
652
0
      }
653
0
      if (!debpath[0]) {
654
        /* empty string means default builtin value
655
           to allow "/foo/php.d:" or ":/foo/php.d" */
656
0
        debpath = PHP_CONFIG_FILE_SCAN_DIR;
657
0
      }
658
0
      lenpath = strlen(debpath);
659
660
0
      int ndir;
661
0
      if (lenpath > 0 && (ndir = php_scandir(debpath, &namelist, NULL, php_alphasort)) > 0) {
662
0
        for (int i = 0; i < ndir; i++) {
663
664
          /* check for any file with .ini extension */
665
0
          if (!(p = strrchr(namelist[i]->d_name, '.')) || (p && strcmp(p, ".ini"))) {
666
0
            free(namelist[i]);
667
0
            continue;
668
0
          }
669
          /* Reset active ini section */
670
0
          RESET_ACTIVE_INI_HASH();
671
672
0
          if (IS_SLASH(debpath[lenpath - 1])) {
673
0
            snprintf(ini_file, MAXPATHLEN, "%s%s", debpath, namelist[i]->d_name);
674
0
          } else {
675
0
            snprintf(ini_file, MAXPATHLEN, "%s%c%s", debpath, DEFAULT_SLASH, namelist[i]->d_name);
676
0
          }
677
0
          if (VCWD_STAT(ini_file, &sb) == 0) {
678
0
            if (S_ISREG(sb.st_mode)) {
679
0
              zend_file_handle fh;
680
0
              FILE *file = VCWD_FOPEN(ini_file, "r");
681
0
              if (file) {
682
0
                zend_stream_init_fp(&fh, file, ini_file);
683
0
                if (zend_parse_ini_file(&fh, true, ZEND_INI_SCANNER_NORMAL, (zend_ini_parser_cb_t) php_ini_parser_cb, &configuration_hash) == SUCCESS) {
684
                  /* Here, add it to the list of ini files read */
685
0
                  size_t l = strlen(ini_file);
686
0
                  total_l += l + 2;
687
0
                  p = estrndup(ini_file, l);
688
0
                  zend_llist_add_element(&scanned_ini_list, &p);
689
0
                }
690
0
                zend_destroy_file_handle(&fh);
691
0
              }
692
0
            }
693
0
          }
694
0
          free(namelist[i]);
695
0
        }
696
0
        free(namelist);
697
0
      }
698
0
    }
699
0
    efree(bufpath);
700
701
0
    if (total_l) {
702
0
      size_t php_ini_scanned_files_len = (php_ini_scanned_files) ? strlen(php_ini_scanned_files) + 1 : 0;
703
0
      php_ini_scanned_files = (char *) perealloc(php_ini_scanned_files, php_ini_scanned_files_len + total_l + 1, true);
704
0
      if (!php_ini_scanned_files_len) {
705
0
        *php_ini_scanned_files = '\0';
706
0
      }
707
0
      total_l += php_ini_scanned_files_len;
708
0
      for (zend_llist_element *element = scanned_ini_list.head; element; element = element->next) {
709
0
        if (php_ini_scanned_files_len) {
710
0
          strlcat(php_ini_scanned_files, ",\n", total_l);
711
0
        }
712
0
        strlcat(php_ini_scanned_files, *(char **)element->data, total_l);
713
0
        strlcat(php_ini_scanned_files, element->next ? ",\n" : "\n", total_l);
714
0
      }
715
0
    }
716
0
    zend_llist_destroy(&scanned_ini_list);
717
16
  } else {
718
    /* Make sure an empty php_ini_scanned_path ends up as NULL */
719
16
    php_ini_scanned_path = NULL;
720
16
  }
721
722
16
  if (sapi_module.ini_entries) {
723
    /* Reset active ini section */
724
16
    RESET_ACTIVE_INI_HASH();
725
16
    zend_parse_ini_string(sapi_module.ini_entries, true, ZEND_INI_SCANNER_NORMAL, (zend_ini_parser_cb_t) php_ini_parser_cb, &configuration_hash);
726
16
  }
727
16
}
728
/* }}} */
729
730
/* {{{ php_shutdown_config */
731
void php_shutdown_config(void)
732
0
{
733
0
  zend_hash_destroy(&configuration_hash);
734
0
  if (php_ini_opened_path) {
735
0
    free(php_ini_opened_path);
736
0
    php_ini_opened_path = NULL;
737
0
  }
738
0
  if (php_ini_scanned_files) {
739
0
    free(php_ini_scanned_files);
740
0
    php_ini_scanned_files = NULL;
741
0
  }
742
0
}
743
/* }}} */
744
745
/* {{{ php_ini_register_extensions */
746
void php_ini_register_extensions(void)
747
16
{
748
16
  zend_llist_apply(&extension_lists.engine, php_load_zend_extension_cb);
749
16
  zend_llist_apply(&extension_lists.functions, php_load_php_extension_cb);
750
751
16
  zend_llist_destroy(&extension_lists.engine);
752
16
  zend_llist_destroy(&extension_lists.functions);
753
16
}
754
/* }}} */
755
756
/* {{{ php_parse_user_ini_file */
757
PHPAPI zend_result php_parse_user_ini_file(const char *dirname, const char *ini_filename, HashTable *target_hash)
758
0
{
759
0
  zend_stat_t sb = {0};
760
0
  char ini_file[MAXPATHLEN];
761
762
0
  snprintf(ini_file, MAXPATHLEN, "%s%c%s", dirname, DEFAULT_SLASH, ini_filename);
763
764
0
  if (VCWD_STAT(ini_file, &sb) == 0) {
765
0
    if (S_ISREG(sb.st_mode)) {
766
0
      zend_file_handle fh;
767
0
      zend_result ret = FAILURE;
768
769
0
      zend_stream_init_fp(&fh, VCWD_FOPEN(ini_file, "r"), ini_file);
770
0
      if (fh.handle.fp) {
771
        /* Reset active ini section */
772
0
        RESET_ACTIVE_INI_HASH();
773
774
#if ZEND_RC_DEBUG
775
        /* User inis are parsed during SAPI activate (part of the request),
776
         * but persistently allocated to allow caching. This is fine as long as
777
         * strings are duplicated in php_ini_activate_config(). */
778
        bool orig_rc_debug = zend_rc_debug;
779
        zend_rc_debug = false;
780
#endif
781
0
        ret = zend_parse_ini_file(&fh, true, ZEND_INI_SCANNER_NORMAL, (zend_ini_parser_cb_t) php_ini_parser_cb, target_hash);
782
#if ZEND_RC_DEBUG
783
        zend_rc_debug = orig_rc_debug;
784
#endif
785
0
        if (ret == SUCCESS) {
786
          /* FIXME: Add parsed file to the list of user files read? */
787
0
        }
788
0
      }
789
0
      zend_destroy_file_handle(&fh);
790
0
      return ret;
791
0
    }
792
0
  }
793
0
  return FAILURE;
794
0
}
795
/* }}} */
796
797
/* {{{ php_ini_activate_config */
798
PHPAPI void php_ini_activate_config(const HashTable *source_hash, int modify_type, int stage)
799
0
{
800
0
  zend_string *str;
801
0
  zval *data;
802
803
  /* Walk through config hash and alter matching ini entries using the values found in the hash */
804
0
  ZEND_HASH_MAP_FOREACH_STR_KEY_VAL(source_hash, str, data) {
805
0
    zend_string *data_str = zend_string_dup(Z_STR_P(data), false);
806
0
    zend_alter_ini_entry_ex(str, data_str, modify_type, stage, false);
807
0
    zend_string_release(data_str);
808
0
  } ZEND_HASH_FOREACH_END();
809
0
}
810
/* }}} */
811
812
/* {{{ php_ini_has_per_dir_config */
813
PHPAPI bool php_ini_has_per_dir_config(void)
814
0
{
815
0
  return has_per_dir_config;
816
0
}
817
/* }}} */
818
819
/* {{{ php_ini_activate_per_dir_config */
820
PHPAPI void php_ini_activate_per_dir_config(char *path, size_t path_len)
821
0
{
822
0
  zval *tmp2;
823
0
  char *ptr;
824
825
#ifdef PHP_WIN32
826
  char path_bak[MAXPATHLEN];
827
#endif
828
829
#ifdef PHP_WIN32
830
  /* MAX_PATH is \0-terminated, path_len == MAXPATHLEN would overrun path_bak */
831
  if (path_len >= MAXPATHLEN) {
832
#else
833
0
  if (path_len > MAXPATHLEN) {
834
0
#endif
835
0
    return;
836
0
  }
837
838
#ifdef PHP_WIN32
839
  memcpy(path_bak, path, path_len);
840
  path_bak[path_len] = 0;
841
  TRANSLATE_SLASHES_LOWER(path_bak);
842
  path = path_bak;
843
#endif
844
845
  /* Walk through each directory in path and apply any found per-dir-system-configuration from configuration_hash */
846
0
  if (has_per_dir_config && path && path_len) {
847
0
    ptr = path + 1;
848
0
    while ((ptr = strchr(ptr, '/')) != NULL) {
849
0
      *ptr = 0;
850
      /* Search for source array matching the path from configuration_hash */
851
0
      if ((tmp2 = zend_hash_str_find(&configuration_hash, path, strlen(path))) != NULL) {
852
0
        php_ini_activate_config(Z_ARRVAL_P(tmp2), PHP_INI_SYSTEM, PHP_INI_STAGE_ACTIVATE);
853
0
      }
854
0
      *ptr = '/';
855
0
      ptr++;
856
0
    }
857
0
  }
858
0
}
859
/* }}} */
860
861
/* {{{ php_ini_has_per_host_config */
862
PHPAPI bool php_ini_has_per_host_config(void)
863
0
{
864
0
  return has_per_host_config;
865
0
}
866
/* }}} */
867
868
/* {{{ php_ini_activate_per_host_config */
869
PHPAPI void php_ini_activate_per_host_config(const char *host, size_t host_len)
870
0
{
871
0
  zval *tmp;
872
873
0
  if (has_per_host_config && host && host_len) {
874
    /* Search for source array matching the host from configuration_hash */
875
0
    if ((tmp = zend_hash_str_find(&configuration_hash, host, host_len)) != NULL) {
876
0
      php_ini_activate_config(Z_ARRVAL_P(tmp), PHP_INI_SYSTEM, PHP_INI_STAGE_ACTIVATE);
877
0
    }
878
0
  }
879
0
}
880
/* }}} */
881
882
/* {{{ cfg_get_entry */
883
PHPAPI zval *cfg_get_entry_ex(zend_string *name)
884
2.88k
{
885
2.88k
  return zend_hash_find(&configuration_hash, name);
886
2.88k
}
887
/* }}} */
888
889
/* {{{ cfg_get_entry */
890
PHPAPI zval *cfg_get_entry(const char *name, size_t name_length)
891
0
{
892
0
  return zend_hash_str_find(&configuration_hash, name, name_length);
893
0
}
894
/* }}} */
895
896
/* {{{ cfg_get_long */
897
PHPAPI zend_result cfg_get_long(const char *varname, zend_long *result)
898
304
{
899
304
  zval *tmp;
900
901
304
  if ((tmp = zend_hash_str_find(&configuration_hash, varname, strlen(varname))) == NULL) {
902
288
    *result = 0;
903
288
    return FAILURE;
904
288
  }
905
16
  *result = zval_get_long(tmp);
906
16
  return SUCCESS;
907
304
}
908
/* }}} */
909
910
/* {{{ cfg_get_double */
911
PHPAPI zend_result cfg_get_double(const char *varname, double *result)
912
0
{
913
0
  zval *tmp;
914
915
0
  if ((tmp = zend_hash_str_find(&configuration_hash, varname, strlen(varname))) == NULL) {
916
0
    *result = (double) 0;
917
0
    return FAILURE;
918
0
  }
919
0
  *result = zval_get_double(tmp);
920
0
  return SUCCESS;
921
0
}
922
/* }}} */
923
924
/* {{{ cfg_get_string */
925
PHPAPI zend_result cfg_get_string(const char *varname, char **result)
926
0
{
927
0
  zval *tmp;
928
929
0
  if ((tmp = zend_hash_str_find(&configuration_hash, varname, strlen(varname))) == NULL) {
930
0
    *result = NULL;
931
0
    return FAILURE;
932
0
  }
933
0
  *result = Z_STRVAL_P(tmp);
934
0
  return SUCCESS;
935
0
}
936
/* }}} */
937
938
PHPAPI HashTable* php_ini_get_configuration_hash(void) /* {{{ */
939
0
{
940
0
  return &configuration_hash;
941
0
} /* }}} */