Coverage Report

Created: 2025-09-27 06:26

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