Coverage Report

Created: 2026-06-02 06:40

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.44k
{
68
1.44k
  if (ini_entry->displayer) {
69
440
    ini_entry->displayer(ini_entry, type);
70
1.00k
  } else {
71
1.00k
    char *display_string;
72
1.00k
    size_t display_string_length;
73
1.00k
    int esc_html=0;
74
75
1.00k
    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
1.00k
    } else if (ini_entry->value && ZSTR_VAL(ini_entry->value)[0]) {
90
712
      display_string = ZSTR_VAL(ini_entry->value);
91
712
      display_string_length = ZSTR_LEN(ini_entry->value);
92
712
      esc_html = !sapi_module.phpinfo_as_text;
93
712
    } else {
94
288
      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
288
      } else {
98
288
        display_string = "no value";
99
288
        display_string_length = sizeof("no value") - 1;
100
288
      }
101
288
    }
102
103
1.00k
    if (esc_html) {
104
0
      php_html_puts(display_string, display_string_length);
105
1.00k
    } else {
106
1.00k
      PHPWRITE(display_string, display_string_length);
107
1.00k
    }
108
1.00k
  }
109
1.44k
}
110
/* }}} */
111
112
/* {{{ display_ini_entries */
113
PHPAPI ZEND_COLD void display_ini_entries(zend_module_entry *module)
114
29
{
115
29
  int module_number;
116
29
  zend_ini_entry *ini_entry;
117
29
  bool first = 1;
118
119
29
  if (module) {
120
29
    module_number = module->module_number;
121
29
  } else {
122
0
    module_number = 0;
123
0
  }
124
125
10.4k
  ZEND_HASH_MAP_FOREACH_PTR(EG(ini_directives), ini_entry) {
126
10.4k
    if (ini_entry->module_number != module_number) {
127
4.47k
      continue;
128
4.47k
    }
129
721
    if (first) {
130
25
      php_info_print_table_start();
131
25
      php_info_print_table_header(3, "Directive", "Local Value", "Master Value");
132
25
      first = 0;
133
25
    }
134
721
    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
721
    } else {
144
721
      PHPWRITE(ZSTR_VAL(ini_entry->name), ZSTR_LEN(ini_entry->name));
145
721
      PUTS(" => ");
146
721
      php_ini_displayer_cb(ini_entry, ZEND_INI_DISPLAY_ACTIVE);
147
721
      PUTS(" => ");
148
721
      php_ini_displayer_cb(ini_entry, ZEND_INI_DISPLAY_ORIG);
149
721
      PUTS("\n");
150
721
    }
151
721
  } ZEND_HASH_FOREACH_END();
152
29
  if (!first) {
153
25
    php_info_print_table_end();
154
25
  }
155
29
}
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
208
{
182
208
  zval *entry;
183
208
  HashTable *active_hash;
184
208
  char *extension_name;
185
186
208
  if (active_ini_hash) {
187
0
    active_hash = active_ini_hash;
188
208
  } else {
189
208
    active_hash = target_hash;
190
208
  }
191
192
208
  switch (callback_type) {
193
208
    case ZEND_INI_PARSER_ENTRY: {
194
208
        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
208
        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
208
        } 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
208
        } else {
209
          /* Store in active hash */
210
208
          entry = zend_hash_update(active_hash, Z_STR_P(arg1), arg2);
211
208
          Z_STR_P(entry) = zend_string_dup(Z_STR_P(entry), true);
212
208
        }
213
208
      }
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
208
  }
307
208
}
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
            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
16
    if (!fp) {
571
16
      const char *fmt = "php-%s.ini";
572
16
      char *ini_fname;
573
16
      spprintf(&ini_fname, 0, fmt, sapi_module.name);
574
16
      fp = php_fopen_with_path(ini_fname, "r", php_ini_search_path, &opened_path);
575
16
      efree(ini_fname);
576
16
      if (fp) {
577
0
        filename = ZSTR_VAL(opened_path);
578
0
      }
579
16
    }
580
581
    /* If still no ini file found, search for php.ini file in search path */
582
16
    if (!fp) {
583
16
      fp = php_fopen_with_path("php.ini", "r", php_ini_search_path, &opened_path);
584
16
      if (fp) {
585
0
        filename = ZSTR_VAL(opened_path);
586
0
      }
587
16
    }
588
16
  }
589
590
16
  if (free_ini_search_path) {
591
16
    efree(php_ini_search_path);
592
16
  }
593
594
16
  PG(open_basedir) = open_basedir;
595
596
16
  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
16
  php_ini_scanned_path = getenv("PHP_INI_SCAN_DIR");
622
16
  if (!php_ini_scanned_path) {
623
    /* Or fall back using possible --with-config-file-scan-dir setting (defaults to empty string!) */
624
16
    php_ini_scanned_path = PHP_CONFIG_FILE_SCAN_DIR;
625
16
  }
626
16
  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
16
  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
16
  } else {
712
    /* Make sure an empty php_ini_scanned_path ends up as NULL */
713
16
    php_ini_scanned_path = NULL;
714
16
  }
715
716
16
  if (sapi_module.ini_entries) {
717
    /* Reset active ini section */
718
16
    RESET_ACTIVE_INI_HASH();
719
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);
720
16
  }
721
16
}
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
16
{
742
16
  zend_llist_apply(&extension_lists.engine, php_load_zend_extension_cb);
743
16
  zend_llist_apply(&extension_lists.functions, php_load_php_extension_cb);
744
745
16
  zend_llist_destroy(&extension_lists.engine);
746
16
  zend_llist_destroy(&extension_lists.functions);
747
16
}
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
2.87k
{
879
2.87k
  return zend_hash_find(&configuration_hash, name);
880
2.87k
}
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
304
{
893
304
  zval *tmp;
894
895
304
  if ((tmp = zend_hash_str_find(&configuration_hash, varname, strlen(varname))) == NULL) {
896
288
    *result = 0;
897
288
    return FAILURE;
898
288
  }
899
16
  *result = zval_get_long(tmp);
900
16
  return SUCCESS;
901
304
}
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
} /* }}} */