Coverage Report

Created: 2025-12-14 06:09

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
1.42k
{
70
1.42k
  if (ini_entry->displayer) {
71
432
    ini_entry->displayer(ini_entry, type);
72
992
  } else {
73
992
    char *display_string;
74
992
    size_t display_string_length;
75
992
    int esc_html=0;
76
77
992
    if (type == ZEND_INI_DISPLAY_ORIG && ini_entry->modified) {
78
4
      if (ini_entry->orig_value && ZSTR_VAL(ini_entry->orig_value)[0]) {
79
4
        display_string = ZSTR_VAL(ini_entry->orig_value);
80
4
        display_string_length = ZSTR_LEN(ini_entry->orig_value);
81
4
        esc_html = !sapi_module.phpinfo_as_text;
82
4
      } 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
988
    } else if (ini_entry->value && ZSTR_VAL(ini_entry->value)[0]) {
92
700
      display_string = ZSTR_VAL(ini_entry->value);
93
700
      display_string_length = ZSTR_LEN(ini_entry->value);
94
700
      esc_html = !sapi_module.phpinfo_as_text;
95
700
    } else {
96
288
      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
288
      } else {
100
288
        display_string = "no value";
101
288
        display_string_length = sizeof("no value") - 1;
102
288
      }
103
288
    }
104
105
992
    if (esc_html) {
106
0
      php_html_puts(display_string, display_string_length);
107
992
    } else {
108
992
      PHPWRITE(display_string, display_string_length);
109
992
    }
110
992
  }
111
1.42k
}
112
/* }}} */
113
114
/* {{{ display_ini_entries */
115
PHPAPI ZEND_COLD void display_ini_entries(zend_module_entry *module)
116
28
{
117
28
  int module_number;
118
28
  zend_ini_entry *ini_entry;
119
28
  bool first = 1;
120
121
28
  if (module) {
122
28
    module_number = module->module_number;
123
28
  } else {
124
0
    module_number = 0;
125
0
  }
126
127
10.0k
  ZEND_HASH_MAP_FOREACH_PTR(EG(ini_directives), ini_entry) {
128
10.0k
    if (ini_entry->module_number != module_number) {
129
4.27k
      continue;
130
4.27k
    }
131
712
    if (first) {
132
24
      php_info_print_table_start();
133
24
      php_info_print_table_header(3, "Directive", "Local Value", "Master Value");
134
24
      first = 0;
135
24
    }
136
712
    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
712
    } else {
146
712
      PHPWRITE(ZSTR_VAL(ini_entry->name), ZSTR_LEN(ini_entry->name));
147
712
      PUTS(" => ");
148
712
      php_ini_displayer_cb(ini_entry, ZEND_INI_DISPLAY_ACTIVE);
149
712
      PUTS(" => ");
150
712
      php_ini_displayer_cb(ini_entry, ZEND_INI_DISPLAY_ORIG);
151
712
      PUTS("\n");
152
712
    }
153
712
  } ZEND_HASH_FOREACH_END();
154
28
  if (!first) {
155
24
    php_info_print_table_end();
156
24
  }
157
28
}
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
2
#define RESET_ACTIVE_INI_HASH() do { \
176
2
  active_ini_hash = NULL;          \
177
2
  is_special_section = false;      \
178
2
} 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
30
{
184
30
  zval *entry;
185
30
  HashTable *active_hash;
186
30
  char *extension_name;
187
188
30
  if (active_ini_hash) {
189
0
    active_hash = active_ini_hash;
190
30
  } else {
191
30
    active_hash = target_hash;
192
30
  }
193
194
30
  switch (callback_type) {
195
30
    case ZEND_INI_PARSER_ENTRY: {
196
30
        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
30
        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
30
        } 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
30
        } else {
211
          /* Store in active hash */
212
30
          entry = zend_hash_update(active_hash, Z_STR_P(arg1), arg2);
213
30
          Z_STR_P(entry) = zend_string_dup(Z_STR_P(entry), true);
214
30
        }
215
30
      }
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
30
  }
309
30
}
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
4
{
399
4
  static const char paths_separator[] = { ZEND_PATHS_SEPARATOR, 0 };
400
401
4
  if (*php_ini_search_path) {
402
2
    strlcat(php_ini_search_path, paths_separator, search_path_size);
403
2
  }
404
405
4
  strlcat(php_ini_search_path, path, search_path_size);
406
4
}
407
408
/* {{{ php_init_config */
409
void php_init_config(void)
410
2
{
411
2
  char *php_ini_file_name = NULL;
412
2
  char *php_ini_search_path = NULL;
413
2
  size_t php_ini_scanned_path_len;
414
2
  char *open_basedir;
415
2
  bool free_ini_search_path = false;
416
2
  zend_string *opened_path = NULL;
417
418
2
  zend_hash_init(&configuration_hash, 8, NULL, config_zval_dtor, true);
419
420
2
  if (sapi_module.ini_defaults) {
421
0
    sapi_module.ini_defaults(&configuration_hash);
422
0
  }
423
424
2
  zend_llist_init(&extension_lists.engine, sizeof(char *), (llist_dtor_func_t) free_estring, true);
425
2
  zend_llist_init(&extension_lists.functions, sizeof(char *), (llist_dtor_func_t) free_estring, true);
426
427
2
  open_basedir = PG(open_basedir);
428
429
2
  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
2
  } else if (!sapi_module.php_ini_ignore) {
434
2
    size_t search_path_size;
435
2
    char *default_location;
436
2
    char *env_location;
437
#ifdef PHP_WIN32
438
    char *reg_location;
439
    char phprc_path[MAXPATHLEN];
440
#endif
441
442
2
    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
2
    if (!env_location) {
473
2
      env_location = "";
474
2
    }
475
2
#endif
476
    /*
477
     * Prepare search path
478
     */
479
480
2
    search_path_size = MAXPATHLEN * 4 + strlen(env_location) + 3 + 1;
481
2
    php_ini_search_path = (char *) emalloc(search_path_size);
482
2
    free_ini_search_path = true;
483
2
    php_ini_search_path[0] = 0;
484
485
    /* Add environment location */
486
2
    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
2
    if (!sapi_module.php_ini_ignore_cwd) {
502
2
      append_ini_path(php_ini_search_path, search_path_size, ".");
503
2
    }
504
505
2
    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
2
    default_location = PHP_CONFIG_FILE_PATH;
537
2
    append_ini_path(php_ini_search_path, search_path_size, default_location);
538
2
#endif
539
2
  }
540
541
2
  PG(open_basedir) = NULL;
542
543
  /*
544
   * Find and open actual ini file
545
   */
546
547
2
  FILE *fp = NULL;
548
2
  char *filename = NULL;
549
2
  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
2
  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
2
    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
2
    if (!fp) {
573
2
      const char *fmt = "php-%s.ini";
574
2
      char *ini_fname;
575
2
      spprintf(&ini_fname, 0, fmt, sapi_module.name);
576
2
      fp = php_fopen_with_path(ini_fname, "r", php_ini_search_path, &opened_path);
577
2
      efree(ini_fname);
578
2
      if (fp) {
579
0
        filename = ZSTR_VAL(opened_path);
580
0
      }
581
2
    }
582
583
    /* If still no ini file found, search for php.ini file in search path */
584
2
    if (!fp) {
585
2
      fp = php_fopen_with_path("php.ini", "r", php_ini_search_path, &opened_path);
586
2
      if (fp) {
587
0
        filename = ZSTR_VAL(opened_path);
588
0
      }
589
2
    }
590
2
  }
591
592
2
  if (free_ini_search_path) {
593
2
    efree(php_ini_search_path);
594
2
  }
595
596
2
  PG(open_basedir) = open_basedir;
597
598
2
  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
2
  php_ini_scanned_path = getenv("PHP_INI_SCAN_DIR");
624
2
  if (!php_ini_scanned_path) {
625
    /* Or fall back using possible --with-config-file-scan-dir setting (defaults to empty string!) */
626
2
    php_ini_scanned_path = PHP_CONFIG_FILE_SCAN_DIR;
627
2
  }
628
2
  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
2
  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
2
  } else {
714
    /* Make sure an empty php_ini_scanned_path ends up as NULL */
715
2
    php_ini_scanned_path = NULL;
716
2
  }
717
718
2
  if (sapi_module.ini_entries) {
719
    /* Reset active ini section */
720
2
    RESET_ACTIVE_INI_HASH();
721
2
    zend_parse_ini_string(sapi_module.ini_entries, true, ZEND_INI_SCANNER_NORMAL, (zend_ini_parser_cb_t) php_ini_parser_cb, &configuration_hash);
722
2
  }
723
2
}
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
2
{
744
2
  zend_llist_apply(&extension_lists.engine, php_load_zend_extension_cb);
745
2
  zend_llist_apply(&extension_lists.functions, php_load_php_extension_cb);
746
747
2
  zend_llist_destroy(&extension_lists.engine);
748
2
  zend_llist_destroy(&extension_lists.functions);
749
2
}
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
356
{
881
356
  return zend_hash_find(&configuration_hash, name);
882
356
}
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
38
{
895
38
  zval *tmp;
896
897
38
  if ((tmp = zend_hash_str_find(&configuration_hash, varname, strlen(varname))) == NULL) {
898
36
    *result = 0;
899
36
    return FAILURE;
900
36
  }
901
2
  *result = zval_get_long(tmp);
902
2
  return SUCCESS;
903
38
}
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
} /* }}} */