Coverage Report

Created: 2025-04-03 08:38

/src/php-src/main/SAPI.c
Line
Count
Source (jump to first uncovered line)
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
   | Original design:  Shane Caraveo <shane@caraveo.com>                  |
14
   | Authors: Andi Gutmans <andi@php.net>                                 |
15
   |          Zeev Suraski <zeev@php.net>                                 |
16
   +----------------------------------------------------------------------+
17
*/
18
19
#include <ctype.h>
20
#include <sys/stat.h>
21
22
#include "php.h"
23
#include "SAPI.h"
24
#include "php_variables.h"
25
#include "php_ini.h"
26
#include "ext/standard/php_string.h"
27
#include "ext/standard/pageinfo.h"
28
#include "ext/pcre/php_pcre.h"
29
#ifdef ZTS
30
#include "TSRM.h"
31
#endif
32
#ifdef HAVE_SYS_TIME_H
33
#include <sys/time.h>
34
#elif defined(PHP_WIN32)
35
#include "win32/time.h"
36
#endif
37
38
#include "rfc1867.h"
39
40
#include "php_content_types.h"
41
42
#ifdef ZTS
43
SAPI_API int sapi_globals_id;
44
SAPI_API size_t sapi_globals_offset;
45
#else
46
sapi_globals_struct sapi_globals;
47
#endif
48
49
static void _type_dtor(zval *zv)
50
0
{
51
0
  free(Z_PTR_P(zv));
52
0
}
53
54
static void sapi_globals_ctor(sapi_globals_struct *sapi_globals)
55
77
{
56
77
  memset(sapi_globals, 0, sizeof(*sapi_globals));
57
77
  zend_hash_init(&sapi_globals->known_post_content_types, 8, NULL, _type_dtor, 1);
58
77
  php_setup_sapi_content_types();
59
77
}
60
61
static void sapi_globals_dtor(sapi_globals_struct *sapi_globals)
62
0
{
63
0
  zend_hash_destroy(&sapi_globals->known_post_content_types);
64
0
}
65
66
/* True globals (no need for thread safety) */
67
SAPI_API sapi_module_struct sapi_module;
68
69
70
SAPI_API void sapi_startup(sapi_module_struct *sf)
71
77
{
72
77
  sf->ini_entries = NULL;
73
77
  sapi_module = *sf;
74
75
#ifdef ZTS
76
  ts_allocate_fast_id(&sapi_globals_id, &sapi_globals_offset, sizeof(sapi_globals_struct), (ts_allocate_ctor) sapi_globals_ctor, (ts_allocate_dtor) sapi_globals_dtor);
77
# ifdef PHP_WIN32
78
  _configthreadlocale(_ENABLE_PER_THREAD_LOCALE);
79
# endif
80
#else
81
77
  sapi_globals_ctor(&sapi_globals);
82
77
#endif
83
84
#ifdef PHP_WIN32
85
  tsrm_win32_startup();
86
#endif
87
88
77
  reentrancy_startup();
89
77
}
90
91
SAPI_API void sapi_shutdown(void)
92
0
{
93
#ifdef ZTS
94
  ts_free_id(sapi_globals_id);
95
#else
96
0
  sapi_globals_dtor(&sapi_globals);
97
0
#endif
98
99
0
  reentrancy_shutdown();
100
101
#ifdef PHP_WIN32
102
  tsrm_win32_shutdown();
103
#endif
104
0
}
105
106
107
SAPI_API void sapi_free_header(sapi_header_struct *sapi_header)
108
40.1k
{
109
40.1k
  efree(sapi_header->header);
110
40.1k
}
111
112
/* {{{ call a header function */
113
PHP_FUNCTION(header_register_callback)
114
0
{
115
0
  zend_fcall_info fci;
116
0
  zend_fcall_info_cache fcc;
117
118
0
  if (zend_parse_parameters(ZEND_NUM_ARGS(), "f", &fci, &fcc) == FAILURE) {
119
0
    RETURN_THROWS();
120
0
  }
121
122
0
  if (Z_TYPE(SG(callback_func)) != IS_UNDEF) {
123
0
    zval_ptr_dtor(&SG(callback_func));
124
0
    SG(fci_cache) = empty_fcall_info_cache;
125
0
  }
126
127
  /* Don't store callback if headers have already been sent:
128
   * It won't get used and we won't have a chance to release it. */
129
0
  if (!SG(headers_sent)) {
130
0
    ZVAL_COPY(&SG(callback_func), &fci.function_name);
131
0
  }
132
133
0
  RETURN_TRUE;
134
0
}
135
/* }}} */
136
137
static void sapi_run_header_callback(zval *callback)
138
0
{
139
0
  int   error;
140
0
  zend_fcall_info fci;
141
0
  char *callback_error = NULL;
142
0
  zval retval;
143
144
0
  if (zend_fcall_info_init(callback, 0, &fci, &SG(fci_cache), NULL, &callback_error) == SUCCESS) {
145
0
    fci.retval = &retval;
146
147
0
    error = zend_call_function(&fci, &SG(fci_cache));
148
0
    if (error == FAILURE) {
149
0
      goto callback_failed;
150
0
    } else {
151
0
      zval_ptr_dtor(&retval);
152
0
    }
153
0
  } else {
154
0
callback_failed:
155
0
    php_error_docref(NULL, E_WARNING, "Could not call the sapi_header_callback");
156
0
  }
157
158
0
  if (callback_error) {
159
0
    efree(callback_error);
160
0
  }
161
0
}
162
163
SAPI_API void sapi_handle_post(void *arg)
164
0
{
165
0
  if (SG(request_info).post_entry && SG(request_info).content_type_dup) {
166
0
    SG(request_info).post_entry->post_handler(SG(request_info).content_type_dup, arg);
167
0
    efree(SG(request_info).content_type_dup);
168
0
    SG(request_info).content_type_dup = NULL;
169
0
  }
170
0
}
171
172
static void sapi_read_post_data(void)
173
0
{
174
0
  sapi_post_entry *post_entry;
175
0
  uint32_t content_type_length = (uint32_t)strlen(SG(request_info).content_type);
176
0
  char *content_type = estrndup(SG(request_info).content_type, content_type_length);
177
0
  char *p;
178
0
  char oldchar=0;
179
0
  void (*post_reader_func)(void) = NULL;
180
181
182
  /* dedicated implementation for increased performance:
183
   * - Make the content type lowercase
184
   * - Trim descriptive data, stay with the content-type only
185
   */
186
0
  for (p=content_type; p<content_type+content_type_length; p++) {
187
0
    switch (*p) {
188
0
      case ';':
189
0
      case ',':
190
0
      case ' ':
191
0
        content_type_length = p-content_type;
192
0
        oldchar = *p;
193
0
        *p = 0;
194
0
        break;
195
0
      default:
196
0
        *p = tolower(*p);
197
0
        break;
198
0
    }
199
0
  }
200
201
  /* now try to find an appropriate POST content handler */
202
0
  if ((post_entry = zend_hash_str_find_ptr(&SG(known_post_content_types), content_type,
203
0
      content_type_length)) != NULL) {
204
    /* found one, register it for use */
205
0
    SG(request_info).post_entry = post_entry;
206
0
    post_reader_func = post_entry->post_reader;
207
0
  } else {
208
    /* fallback */
209
0
    SG(request_info).post_entry = NULL;
210
0
    if (!sapi_module.default_post_reader) {
211
      /* no default reader ? */
212
0
      SG(request_info).content_type_dup = NULL;
213
0
      sapi_module.sapi_error(E_WARNING, "Unsupported content type:  '%s'", content_type);
214
0
      return;
215
0
    }
216
0
  }
217
0
  if (oldchar) {
218
0
    *(p-1) = oldchar;
219
0
  }
220
221
0
  SG(request_info).content_type_dup = content_type;
222
223
0
  if(post_reader_func) {
224
0
    post_reader_func();
225
0
  }
226
227
0
  if(sapi_module.default_post_reader) {
228
0
    sapi_module.default_post_reader();
229
0
  }
230
0
}
231
232
SAPI_API size_t sapi_read_post_block(char *buffer, size_t buflen)
233
0
{
234
0
  size_t read_bytes;
235
236
0
  if (!sapi_module.read_post) {
237
0
    return 0;
238
0
  }
239
240
0
  read_bytes = sapi_module.read_post(buffer, buflen);
241
242
0
  if (read_bytes > 0) {
243
    /* gogo */
244
0
    SG(read_post_bytes) += read_bytes;
245
0
  }
246
0
  if (read_bytes < buflen) {
247
    /* done */
248
0
    SG(post_read) = 1;
249
0
  }
250
251
0
  return read_bytes;
252
0
}
253
254
SAPI_API SAPI_POST_READER_FUNC(sapi_read_standard_form_data)
255
0
{
256
0
  if ((SG(post_max_size) > 0) && (SG(request_info).content_length > SG(post_max_size))) {
257
0
    php_error_docref(NULL, E_WARNING, "POST Content-Length of " ZEND_LONG_FMT " bytes exceeds the limit of " ZEND_LONG_FMT " bytes",
258
0
          SG(request_info).content_length, SG(post_max_size));
259
0
    return;
260
0
  }
261
262
263
0
  SG(request_info).request_body = php_stream_temp_create_ex(TEMP_STREAM_DEFAULT, SAPI_POST_BLOCK_SIZE, PG(upload_tmp_dir));
264
265
0
  if (sapi_module.read_post) {
266
0
    size_t read_bytes;
267
268
0
    for (;;) {
269
0
      char buffer[SAPI_POST_BLOCK_SIZE];
270
271
0
      read_bytes = sapi_read_post_block(buffer, SAPI_POST_BLOCK_SIZE);
272
273
0
      if (read_bytes > 0) {
274
0
        if (php_stream_write(SG(request_info).request_body, buffer, read_bytes) != read_bytes) {
275
          /* if parts of the stream can't be written, purge it completely */
276
0
          php_stream_truncate_set_size(SG(request_info).request_body, 0);
277
0
          php_error_docref(NULL, E_WARNING, "POST data can't be buffered; all data discarded");
278
0
          break;
279
0
        }
280
0
      }
281
282
0
      if ((SG(post_max_size) > 0) && (SG(read_post_bytes) > SG(post_max_size))) {
283
0
        php_error_docref(NULL, E_WARNING, "Actual POST length does not match Content-Length, and exceeds " ZEND_LONG_FMT " bytes", SG(post_max_size));
284
0
        break;
285
0
      }
286
287
0
      if (read_bytes < SAPI_POST_BLOCK_SIZE) {
288
        /* done */
289
0
        break;
290
0
      }
291
0
    }
292
0
    php_stream_rewind(SG(request_info).request_body);
293
0
  }
294
0
}
295
296
297
static inline char *get_default_content_type(uint32_t prefix_len, uint32_t *len)
298
20.0k
{
299
20.0k
  char *mimetype, *charset, *content_type;
300
20.0k
  uint32_t mimetype_len, charset_len;
301
302
20.0k
  if (SG(default_mimetype)) {
303
20.0k
    mimetype = SG(default_mimetype);
304
20.0k
    mimetype_len = (uint32_t)strlen(SG(default_mimetype));
305
20.0k
  } else {
306
0
    mimetype = SAPI_DEFAULT_MIMETYPE;
307
0
    mimetype_len = sizeof(SAPI_DEFAULT_MIMETYPE) - 1;
308
0
  }
309
20.0k
  if (SG(default_charset)) {
310
20.0k
    charset = SG(default_charset);
311
20.0k
    charset_len = (uint32_t)strlen(SG(default_charset));
312
20.0k
  } else {
313
0
    charset = SAPI_DEFAULT_CHARSET;
314
0
    charset_len = sizeof(SAPI_DEFAULT_CHARSET) - 1;
315
0
  }
316
317
20.0k
  if (*charset && strncasecmp(mimetype, "text/", 5) == 0) {
318
20.0k
    char *p;
319
320
20.0k
    *len = prefix_len + mimetype_len + sizeof("; charset=") - 1 + charset_len;
321
20.0k
    content_type = (char*)emalloc(*len + 1);
322
20.0k
    p = content_type + prefix_len;
323
20.0k
    memcpy(p, mimetype, mimetype_len);
324
20.0k
    p += mimetype_len;
325
20.0k
    memcpy(p, "; charset=", sizeof("; charset=") - 1);
326
20.0k
    p += sizeof("; charset=") - 1;
327
20.0k
    memcpy(p, charset, charset_len + 1);
328
20.0k
  } else {
329
0
    *len = prefix_len + mimetype_len;
330
0
    content_type = (char*)emalloc(*len + 1);
331
0
    memcpy(content_type + prefix_len, mimetype, mimetype_len + 1);
332
0
  }
333
20.0k
  return content_type;
334
20.0k
}
335
336
337
SAPI_API char *sapi_get_default_content_type(void)
338
0
{
339
0
  uint32_t len;
340
341
0
  return get_default_content_type(0, &len);
342
0
}
343
344
345
SAPI_API void sapi_get_default_content_type_header(sapi_header_struct *default_header)
346
20.0k
{
347
20.0k
  uint32_t len;
348
349
20.0k
  default_header->header = get_default_content_type(sizeof("Content-type: ")-1, &len);
350
20.0k
  default_header->header_len = len;
351
20.0k
  memcpy(default_header->header, "Content-type: ", sizeof("Content-type: ") - 1);
352
20.0k
}
353
354
/*
355
 * Add charset on content-type header if the MIME type starts with
356
 * "text/", the default_charset directive is not empty and
357
 * there is not already a charset option in there.
358
 *
359
 * If "mimetype" is non-NULL, it should point to a pointer allocated
360
 * with emalloc().  If a charset is added, the string will be
361
 * re-allocated and the new length is returned.  If mimetype is
362
 * unchanged, 0 is returned.
363
 *
364
 */
365
SAPI_API size_t sapi_apply_default_charset(char **mimetype, size_t len)
366
0
{
367
0
  char *charset, *newtype;
368
0
  size_t newlen;
369
0
  charset = SG(default_charset) ? SG(default_charset) : SAPI_DEFAULT_CHARSET;
370
371
0
  if (*mimetype != NULL) {
372
0
    if (*charset && strncmp(*mimetype, "text/", 5) == 0 && strstr(*mimetype, "charset=") == NULL) {
373
0
      newlen = len + (sizeof(";charset=")-1) + strlen(charset);
374
0
      newtype = emalloc(newlen + 1);
375
0
      PHP_STRLCPY(newtype, *mimetype, newlen + 1, len);
376
0
      strlcat(newtype, ";charset=", newlen + 1);
377
0
      strlcat(newtype, charset, newlen + 1);
378
0
      efree(*mimetype);
379
0
      *mimetype = newtype;
380
0
      return newlen;
381
0
    }
382
0
  }
383
0
  return 0;
384
0
}
385
386
SAPI_API void sapi_activate_headers_only(void)
387
0
{
388
0
  if (SG(request_info).headers_read == 1)
389
0
    return;
390
0
  SG(request_info).headers_read = 1;
391
0
  zend_llist_init(&SG(sapi_headers).headers, sizeof(sapi_header_struct),
392
0
      (void (*)(void *)) sapi_free_header, 0);
393
0
  SG(sapi_headers).send_default_content_type = 1;
394
395
  /* SG(sapi_headers).http_response_code = 200; */
396
0
  SG(sapi_headers).http_status_line = NULL;
397
0
  SG(sapi_headers).mimetype = NULL;
398
0
  SG(read_post_bytes) = 0;
399
0
  SG(request_info).request_body = NULL;
400
0
  SG(request_info).current_user = NULL;
401
0
  SG(request_info).current_user_length = 0;
402
0
  SG(request_info).no_headers = 0;
403
0
  SG(request_info).post_entry = NULL;
404
0
  SG(global_request_time) = 0;
405
406
  /*
407
   * It's possible to override this general case in the activate() callback,
408
   * if necessary.
409
   */
410
0
  if (SG(request_info).request_method && !strcmp(SG(request_info).request_method, "HEAD")) {
411
0
    SG(request_info).headers_only = 1;
412
0
  } else {
413
0
    SG(request_info).headers_only = 0;
414
0
  }
415
0
  if (SG(server_context)) {
416
0
    SG(request_info).cookie_data = sapi_module.read_cookies();
417
0
    if (sapi_module.activate) {
418
0
      sapi_module.activate();
419
0
    }
420
0
  }
421
0
  if (sapi_module.input_filter_init ) {
422
0
    sapi_module.input_filter_init();
423
0
  }
424
0
}
425
426
/*
427
 * Called from php_request_startup() for every request.
428
 */
429
430
SAPI_API void sapi_activate(void)
431
20.2k
{
432
20.2k
  zend_llist_init(&SG(sapi_headers).headers, sizeof(sapi_header_struct), (void (*)(void *)) sapi_free_header, 0);
433
20.2k
  SG(sapi_headers).send_default_content_type = 1;
434
435
  /*
436
  SG(sapi_headers).http_response_code = 200;
437
  */
438
20.2k
  SG(sapi_headers).http_status_line = NULL;
439
20.2k
  SG(sapi_headers).mimetype = NULL;
440
20.2k
  SG(headers_sent) = 0;
441
20.2k
  ZVAL_UNDEF(&SG(callback_func));
442
20.2k
  SG(read_post_bytes) = 0;
443
20.2k
  SG(request_info).request_body = NULL;
444
20.2k
  SG(request_info).current_user = NULL;
445
20.2k
  SG(request_info).current_user_length = 0;
446
20.2k
  SG(request_info).no_headers = 0;
447
20.2k
  SG(request_info).post_entry = NULL;
448
20.2k
  SG(request_info).proto_num = 1000; /* Default to HTTP 1.0 */
449
20.2k
  SG(global_request_time) = 0;
450
20.2k
  SG(post_read) = 0;
451
  /* It's possible to override this general case in the activate() callback, if necessary. */
452
20.2k
  if (SG(request_info).request_method && !strcmp(SG(request_info).request_method, "HEAD")) {
453
0
    SG(request_info).headers_only = 1;
454
20.2k
  } else {
455
20.2k
    SG(request_info).headers_only = 0;
456
20.2k
  }
457
20.2k
  SG(rfc1867_uploaded_files) = NULL;
458
459
  /* Handle request method */
460
20.2k
  if (SG(server_context)) {
461
0
    if (PG(enable_post_data_reading)
462
0
    &&  SG(request_info).content_type
463
0
    &&  SG(request_info).request_method
464
0
    && !strcmp(SG(request_info).request_method, "POST")) {
465
      /* HTTP POST may contain form data to be processed into variables
466
       * depending on given content type */
467
0
      sapi_read_post_data();
468
0
    } else {
469
0
      SG(request_info).content_type_dup = NULL;
470
0
    }
471
472
    /* Cookies */
473
0
    SG(request_info).cookie_data = sapi_module.read_cookies();
474
0
  }
475
20.2k
  if (sapi_module.activate) {
476
0
    sapi_module.activate();
477
0
  }
478
20.2k
  if (sapi_module.input_filter_init) {
479
0
    sapi_module.input_filter_init();
480
0
  }
481
20.2k
}
482
483
484
static void sapi_send_headers_free(void)
485
40.2k
{
486
40.2k
  if (SG(sapi_headers).http_status_line) {
487
1
    efree(SG(sapi_headers).http_status_line);
488
1
    SG(sapi_headers).http_status_line = NULL;
489
1
  }
490
40.2k
}
491
492
SAPI_API void sapi_deactivate(void)
493
20.1k
{
494
20.1k
  zend_llist_destroy(&SG(sapi_headers).headers);
495
20.1k
  if (SG(request_info).request_body) {
496
0
    SG(request_info).request_body = NULL;
497
20.1k
  } else if (SG(server_context)) {
498
0
    if (!SG(post_read)) {
499
      /* make sure we've consumed all request input data */
500
0
      char dummy[SAPI_POST_BLOCK_SIZE];
501
0
      size_t read_bytes;
502
503
0
      do {
504
0
        read_bytes = sapi_read_post_block(dummy, SAPI_POST_BLOCK_SIZE);
505
0
      } while (SAPI_POST_BLOCK_SIZE == read_bytes);
506
0
    }
507
0
  }
508
20.1k
  if (SG(request_info).auth_user) {
509
0
    efree(SG(request_info).auth_user);
510
0
  }
511
20.1k
  if (SG(request_info).auth_password) {
512
0
    efree(SG(request_info).auth_password);
513
0
  }
514
20.1k
  if (SG(request_info).auth_digest) {
515
0
    efree(SG(request_info).auth_digest);
516
0
  }
517
20.1k
  if (SG(request_info).content_type_dup) {
518
0
    efree(SG(request_info).content_type_dup);
519
0
  }
520
20.1k
  if (SG(request_info).current_user) {
521
0
    efree(SG(request_info).current_user);
522
0
  }
523
20.1k
  if (sapi_module.deactivate) {
524
0
    sapi_module.deactivate();
525
0
  }
526
20.1k
  if (SG(rfc1867_uploaded_files)) {
527
0
    destroy_uploaded_files_hash();
528
0
  }
529
20.1k
  if (SG(sapi_headers).mimetype) {
530
0
    efree(SG(sapi_headers).mimetype);
531
0
    SG(sapi_headers).mimetype = NULL;
532
0
  }
533
20.1k
  sapi_send_headers_free();
534
20.1k
  SG(sapi_started) = 0;
535
20.1k
  SG(headers_sent) = 0;
536
20.1k
  SG(request_info).headers_read = 0;
537
20.1k
  SG(global_request_time) = 0;
538
20.1k
}
539
540
541
SAPI_API void sapi_initialize_empty_request(void)
542
77
{
543
77
  SG(server_context) = NULL;
544
77
  SG(request_info).request_method = NULL;
545
77
  SG(request_info).auth_digest = SG(request_info).auth_user = SG(request_info).auth_password = NULL;
546
77
  SG(request_info).content_type_dup = NULL;
547
77
}
548
549
550
static int sapi_extract_response_code(const char *header_line)
551
1
{
552
1
  int code = 200;
553
1
  const char *ptr;
554
555
67
  for (ptr = header_line; *ptr; ptr++) {
556
66
    if (*ptr == ' ' && *(ptr + 1) != ' ') {
557
0
      code = atoi(ptr + 1);
558
0
      break;
559
0
    }
560
66
  }
561
562
1
  return code;
563
1
}
564
565
566
static void sapi_update_response_code(int ncode)
567
1
{
568
  /* if the status code did not change, we do not want
569
     to change the status line, and no need to change the code */
570
1
  if (SG(sapi_headers).http_response_code == ncode) {
571
0
    return;
572
0
  }
573
574
1
  if (SG(sapi_headers).http_status_line) {
575
0
    efree(SG(sapi_headers).http_status_line);
576
0
    SG(sapi_headers).http_status_line = NULL;
577
0
  }
578
1
  SG(sapi_headers).http_response_code = ncode;
579
1
}
580
581
/*
582
 * since zend_llist_del_element only removes one matched item once,
583
 * we should remove them manually
584
 */
585
20.1k
static void sapi_remove_header(zend_llist *l, char *name, size_t len) {
586
20.1k
  sapi_header_struct *header;
587
20.1k
  zend_llist_element *next;
588
20.1k
  zend_llist_element *current=l->head;
589
590
20.1k
  while (current) {
591
3
    header = (sapi_header_struct *)(current->data);
592
3
    next = current->next;
593
3
    if (header->header_len > len && header->header[len] == ':'
594
3
        && !strncasecmp(header->header, name, len)) {
595
1
      if (current->prev) {
596
1
        current->prev->next = next;
597
1
      } else {
598
0
        l->head = next;
599
0
      }
600
1
      if (next) {
601
0
        next->prev = current->prev;
602
1
      } else {
603
1
        l->tail = current->prev;
604
1
      }
605
1
      sapi_free_header(header);
606
1
      efree(current);
607
1
      --l->count;
608
1
    }
609
3
    current = next;
610
3
  }
611
20.1k
}
612
613
SAPI_API int sapi_add_header_ex(const char *header_line, size_t header_line_len, bool duplicate, bool replace)
614
20.1k
{
615
20.1k
  sapi_header_line ctr = {0};
616
20.1k
  int r;
617
618
20.1k
  ctr.line = header_line;
619
20.1k
  ctr.line_len = header_line_len;
620
621
20.1k
  r = sapi_header_op(replace ? SAPI_HEADER_REPLACE : SAPI_HEADER_ADD,
622
20.1k
      &ctr);
623
624
20.1k
  if (!duplicate)
625
0
    efree((void *) header_line);
626
627
20.1k
  return r;
628
20.1k
}
629
630
static void sapi_header_add_op(sapi_header_op_enum op, sapi_header_struct *sapi_header)
631
20.1k
{
632
20.1k
  if (!sapi_module.header_handler ||
633
20.1k
    (SAPI_HEADER_ADD & sapi_module.header_handler(sapi_header, op, &SG(sapi_headers)))) {
634
20.1k
    if (op == SAPI_HEADER_REPLACE) {
635
20.1k
      char *colon_offset = strchr(sapi_header->header, ':');
636
637
20.1k
      if (colon_offset) {
638
20.1k
        char sav = *colon_offset;
639
640
20.1k
        *colon_offset = 0;
641
20.1k
            sapi_remove_header(&SG(sapi_headers).headers, sapi_header->header, strlen(sapi_header->header));
642
20.1k
        *colon_offset = sav;
643
20.1k
      }
644
20.1k
    }
645
20.1k
    zend_llist_add_element(&SG(sapi_headers).headers, (void *) sapi_header);
646
20.1k
  } else {
647
0
    sapi_free_header(sapi_header);
648
0
  }
649
20.1k
}
650
651
SAPI_API int sapi_header_op(sapi_header_op_enum op, void *arg)
652
20.1k
{
653
20.1k
  sapi_header_struct sapi_header;
654
20.1k
  char *colon_offset;
655
20.1k
  char *header_line;
656
20.1k
  size_t header_line_len;
657
20.1k
  int http_response_code;
658
659
20.1k
  if (SG(headers_sent) && !SG(request_info).no_headers) {
660
1
    const char *output_start_filename = php_output_get_start_filename();
661
1
    int output_start_lineno = php_output_get_start_lineno();
662
663
1
    if (output_start_filename) {
664
1
      sapi_module.sapi_error(E_WARNING, "Cannot modify header information - headers already sent by (output started at %s:%d)",
665
1
        output_start_filename, output_start_lineno);
666
1
    } else {
667
0
      sapi_module.sapi_error(E_WARNING, "Cannot modify header information - headers already sent");
668
0
    }
669
1
    return FAILURE;
670
1
  }
671
672
20.1k
  switch (op) {
673
0
    case SAPI_HEADER_SET_STATUS:
674
0
      sapi_update_response_code((int)(zend_intptr_t) arg);
675
0
      return SUCCESS;
676
677
0
    case SAPI_HEADER_ADD:
678
20.1k
    case SAPI_HEADER_REPLACE:
679
20.1k
    case SAPI_HEADER_DELETE: {
680
20.1k
        sapi_header_line *p = arg;
681
682
20.1k
        if (!p->line || !p->line_len) {
683
0
          return FAILURE;
684
0
        }
685
20.1k
        header_line = estrndup(p->line, p->line_len);
686
20.1k
        header_line_len = p->line_len;
687
20.1k
        http_response_code = p->response_code;
688
20.1k
        break;
689
20.1k
      }
690
691
0
    case SAPI_HEADER_DELETE_ALL:
692
0
      if (sapi_module.header_handler) {
693
0
        sapi_module.header_handler(&sapi_header, op, &SG(sapi_headers));
694
0
      }
695
0
      zend_llist_clean(&SG(sapi_headers).headers);
696
0
      return SUCCESS;
697
698
0
    default:
699
0
      return FAILURE;
700
20.1k
  }
701
702
  /* cut off trailing spaces, linefeeds and carriage-returns */
703
20.1k
  if (header_line_len && isspace(header_line[header_line_len-1])) {
704
1
    do {
705
1
      header_line_len--;
706
1
    } while(header_line_len && isspace(header_line[header_line_len-1]));
707
1
    header_line[header_line_len]='\0';
708
1
  }
709
710
20.1k
  if (op == SAPI_HEADER_DELETE) {
711
0
    if (strchr(header_line, ':')) {
712
0
      efree(header_line);
713
0
      sapi_module.sapi_error(E_WARNING, "Header to delete may not contain colon.");
714
0
      return FAILURE;
715
0
    }
716
0
    if (sapi_module.header_handler) {
717
0
      sapi_header.header = header_line;
718
0
      sapi_header.header_len = header_line_len;
719
0
      sapi_module.header_handler(&sapi_header, op, &SG(sapi_headers));
720
0
    }
721
0
    sapi_remove_header(&SG(sapi_headers).headers, header_line, header_line_len);
722
0
    efree(header_line);
723
0
    return SUCCESS;
724
20.1k
  } else {
725
    /* new line/NUL character safety check */
726
20.1k
    uint32_t i;
727
563k
    for (i = 0; i < header_line_len; i++) {
728
      /* RFC 7230 ch. 3.2.4 deprecates folding support */
729
543k
      if (header_line[i] == '\n' || header_line[i] == '\r') {
730
0
        efree(header_line);
731
0
        sapi_module.sapi_error(E_WARNING, "Header may not contain "
732
0
            "more than a single header, new line detected");
733
0
        return FAILURE;
734
0
      }
735
543k
      if (header_line[i] == '\0') {
736
1
        efree(header_line);
737
1
        sapi_module.sapi_error(E_WARNING, "Header may not contain NUL bytes");
738
1
        return FAILURE;
739
1
      }
740
543k
    }
741
20.1k
  }
742
743
20.1k
  sapi_header.header = header_line;
744
20.1k
  sapi_header.header_len = header_line_len;
745
746
  /* Check the header for a few cases that we have special support for in SAPI */
747
20.1k
  if (header_line_len>=5
748
20.1k
    && !strncasecmp(header_line, "HTTP/", 5)) {
749
    /* filter out the response code */
750
1
    sapi_update_response_code(sapi_extract_response_code(header_line));
751
    /* sapi_update_response_code doesn't free the status line if the code didn't change */
752
1
    if (SG(sapi_headers).http_status_line) {
753
0
      efree(SG(sapi_headers).http_status_line);
754
0
    }
755
1
    SG(sapi_headers).http_status_line = header_line;
756
1
    return SUCCESS;
757
20.1k
  } else {
758
20.1k
    colon_offset = strchr(header_line, ':');
759
20.1k
    if (colon_offset) {
760
20.1k
      *colon_offset = 0;
761
20.1k
      if (!strcasecmp(header_line, "Content-Type")) {
762
0
        char *ptr = colon_offset+1, *mimetype = NULL, *newheader;
763
0
        size_t len = header_line_len - (ptr - header_line), newlen;
764
0
        while (*ptr == ' ') {
765
0
          ptr++;
766
0
          len--;
767
0
        }
768
769
0
        mimetype = estrdup(ptr);
770
0
        newlen = sapi_apply_default_charset(&mimetype, len);
771
0
        if (!SG(sapi_headers).mimetype){
772
0
          SG(sapi_headers).mimetype = estrdup(mimetype);
773
0
        }
774
775
0
        if (newlen != 0) {
776
0
          newlen += sizeof("Content-type: ");
777
0
          newheader = emalloc(newlen);
778
0
          PHP_STRLCPY(newheader, "Content-type: ", newlen, sizeof("Content-type: ")-1);
779
0
          strlcat(newheader, mimetype, newlen);
780
0
          sapi_header.header = newheader;
781
0
          sapi_header.header_len = (uint32_t)(newlen - 1);
782
0
          efree(header_line);
783
0
        }
784
0
        efree(mimetype);
785
0
        SG(sapi_headers).send_default_content_type = 0;
786
20.1k
      } else if (!strcasecmp(header_line, "Content-Length")) {
787
        /* Script is setting Content-length. The script cannot reasonably
788
         * know the size of the message body after compression, so it's best
789
         * do disable compression altogether. This contributes to making scripts
790
         * portable between setups that have and don't have zlib compression
791
         * enabled globally. See req #44164 */
792
0
        zend_string *key = zend_string_init("zlib.output_compression", sizeof("zlib.output_compression")-1, 0);
793
0
        zend_alter_ini_entry_chars(key,
794
0
          "0", sizeof("0") - 1, PHP_INI_USER, PHP_INI_STAGE_RUNTIME);
795
0
        zend_string_release_ex(key, 0);
796
20.1k
      } else if (!strcasecmp(header_line, "Location")) {
797
0
        if ((SG(sapi_headers).http_response_code < 300 ||
798
0
          SG(sapi_headers).http_response_code > 399) &&
799
0
          SG(sapi_headers).http_response_code != 201) {
800
          /* Return a Found Redirect if one is not already specified */
801
0
          if (http_response_code) { /* user specified redirect code */
802
0
            sapi_update_response_code(http_response_code);
803
0
          } else if (SG(request_info).proto_num > 1000 &&
804
0
             SG(request_info).request_method &&
805
0
             strcmp(SG(request_info).request_method, "HEAD") &&
806
0
             strcmp(SG(request_info).request_method, "GET")) {
807
0
            sapi_update_response_code(303);
808
0
          } else {
809
0
            sapi_update_response_code(302);
810
0
          }
811
0
        }
812
20.1k
      } else if (!strcasecmp(header_line, "WWW-Authenticate")) { /* HTTP Authentication */
813
0
        sapi_update_response_code(401); /* authentication-required */
814
0
      }
815
20.1k
      if (sapi_header.header==header_line) {
816
20.1k
        *colon_offset = ':';
817
20.1k
      }
818
20.1k
    }
819
20.1k
  }
820
20.1k
  if (http_response_code) {
821
0
    sapi_update_response_code(http_response_code);
822
0
  }
823
20.1k
  sapi_header_add_op(op, &sapi_header);
824
20.1k
  return SUCCESS;
825
20.1k
}
826
827
828
SAPI_API int sapi_send_headers(void)
829
20.0k
{
830
20.0k
  int retval;
831
20.0k
  int ret = FAILURE;
832
833
20.0k
  if (SG(headers_sent) || SG(request_info).no_headers) {
834
0
    return SUCCESS;
835
0
  }
836
837
  /* Success-oriented.  We set headers_sent to 1 here to avoid an infinite loop
838
   * in case of an error situation.
839
   */
840
20.0k
  if (SG(sapi_headers).send_default_content_type && sapi_module.send_headers) {
841
0
      uint32_t len = 0;
842
0
    char *default_mimetype = get_default_content_type(0, &len);
843
844
0
    if (default_mimetype && len) {
845
0
      sapi_header_struct default_header;
846
847
0
      SG(sapi_headers).mimetype = default_mimetype;
848
849
0
      default_header.header_len = sizeof("Content-type: ") - 1 + len;
850
0
      default_header.header = emalloc(default_header.header_len + 1);
851
852
0
      memcpy(default_header.header, "Content-type: ", sizeof("Content-type: ") - 1);
853
0
      memcpy(default_header.header + sizeof("Content-type: ") - 1, SG(sapi_headers).mimetype, len + 1);
854
855
0
      sapi_header_add_op(SAPI_HEADER_ADD, &default_header);
856
0
    } else {
857
0
      efree(default_mimetype);
858
0
    }
859
0
    SG(sapi_headers).send_default_content_type = 0;
860
0
  }
861
862
20.0k
  if (Z_TYPE(SG(callback_func)) != IS_UNDEF) {
863
0
    zval cb;
864
0
    ZVAL_COPY_VALUE(&cb, &SG(callback_func));
865
0
    ZVAL_UNDEF(&SG(callback_func));
866
0
    sapi_run_header_callback(&cb);
867
0
    zval_ptr_dtor(&cb);
868
0
  }
869
870
20.0k
  SG(headers_sent) = 1;
871
872
20.0k
  if (sapi_module.send_headers) {
873
0
    retval = sapi_module.send_headers(&SG(sapi_headers));
874
20.0k
  } else {
875
20.0k
    retval = SAPI_HEADER_DO_SEND;
876
20.0k
  }
877
878
20.0k
  switch (retval) {
879
0
    case SAPI_HEADER_SENT_SUCCESSFULLY:
880
0
      ret = SUCCESS;
881
0
      break;
882
20.0k
    case SAPI_HEADER_DO_SEND: {
883
20.0k
        sapi_header_struct http_status_line;
884
20.0k
        char buf[255];
885
886
20.0k
        if (SG(sapi_headers).http_status_line) {
887
1
          http_status_line.header = SG(sapi_headers).http_status_line;
888
1
          http_status_line.header_len = (uint32_t)strlen(SG(sapi_headers).http_status_line);
889
20.0k
        } else {
890
20.0k
          http_status_line.header = buf;
891
20.0k
          http_status_line.header_len = slprintf(buf, sizeof(buf), "HTTP/1.0 %d X", SG(sapi_headers).http_response_code);
892
20.0k
        }
893
20.0k
        sapi_module.send_header(&http_status_line, SG(server_context));
894
20.0k
      }
895
20.0k
      zend_llist_apply_with_argument(&SG(sapi_headers).headers, (llist_apply_with_arg_func_t) sapi_module.send_header, SG(server_context));
896
20.0k
      if(SG(sapi_headers).send_default_content_type) {
897
20.0k
        sapi_header_struct default_header;
898
899
20.0k
        sapi_get_default_content_type_header(&default_header);
900
20.0k
        sapi_module.send_header(&default_header, SG(server_context));
901
20.0k
        sapi_free_header(&default_header);
902
20.0k
      }
903
20.0k
      sapi_module.send_header(NULL, SG(server_context));
904
20.0k
      ret = SUCCESS;
905
20.0k
      break;
906
0
    case SAPI_HEADER_SEND_FAILED:
907
0
      SG(headers_sent) = 0;
908
0
      ret = FAILURE;
909
0
      break;
910
20.0k
  }
911
912
20.0k
  sapi_send_headers_free();
913
914
20.0k
  return ret;
915
20.0k
}
916
917
918
SAPI_API int sapi_register_post_entries(const sapi_post_entry *post_entries)
919
77
{
920
77
  const sapi_post_entry *p=post_entries;
921
922
231
  while (p->content_type) {
923
154
    if (sapi_register_post_entry(p) == FAILURE) {
924
0
      return FAILURE;
925
0
    }
926
154
    p++;
927
154
  }
928
77
  return SUCCESS;
929
77
}
930
931
932
SAPI_API int sapi_register_post_entry(const sapi_post_entry *post_entry)
933
154
{
934
154
  int ret;
935
154
  zend_string *key;
936
154
  if (SG(sapi_started) && EG(current_execute_data)) {
937
0
    return FAILURE;
938
0
  }
939
154
  key = zend_string_init(post_entry->content_type, post_entry->content_type_len, 1);
940
154
  GC_MAKE_PERSISTENT_LOCAL(key);
941
154
  ret = zend_hash_add_mem(&SG(known_post_content_types), key,
942
154
      (void *) post_entry, sizeof(sapi_post_entry)) ? SUCCESS : FAILURE;
943
154
  zend_string_release_ex(key, 1);
944
154
  return ret;
945
154
}
946
947
SAPI_API void sapi_unregister_post_entry(const sapi_post_entry *post_entry)
948
0
{
949
0
  if (SG(sapi_started) && EG(current_execute_data)) {
950
0
    return;
951
0
  }
952
0
  zend_hash_str_del(&SG(known_post_content_types), post_entry->content_type,
953
0
      post_entry->content_type_len);
954
0
}
955
956
957
SAPI_API int sapi_register_default_post_reader(void (*default_post_reader)(void))
958
77
{
959
77
  if (SG(sapi_started) && EG(current_execute_data)) {
960
0
    return FAILURE;
961
0
  }
962
77
  sapi_module.default_post_reader = default_post_reader;
963
77
  return SUCCESS;
964
77
}
965
966
967
SAPI_API int sapi_register_treat_data(void (*treat_data)(int arg, char *str, zval *destArray))
968
77
{
969
77
  if (SG(sapi_started) && EG(current_execute_data)) {
970
0
    return FAILURE;
971
0
  }
972
77
  sapi_module.treat_data = treat_data;
973
77
  return SUCCESS;
974
77
}
975
976
SAPI_API int sapi_register_input_filter(unsigned int (*input_filter)(int arg, const char *var, char **val, size_t val_len, size_t *new_val_len), unsigned int (*input_filter_init)(void))
977
77
{
978
77
  if (SG(sapi_started) && EG(current_execute_data)) {
979
0
    return FAILURE;
980
0
  }
981
77
  sapi_module.input_filter = input_filter;
982
77
  sapi_module.input_filter_init = input_filter_init;
983
77
  return SUCCESS;
984
77
}
985
986
SAPI_API int sapi_flush(void)
987
13.8M
{
988
13.8M
  if (sapi_module.flush) {
989
13.8M
    sapi_module.flush(SG(server_context));
990
13.8M
    return SUCCESS;
991
13.8M
  } else {
992
0
    return FAILURE;
993
0
  }
994
13.8M
}
995
996
SAPI_API zend_stat_t *sapi_get_stat(void)
997
0
{
998
0
  if (sapi_module.get_stat) {
999
0
    return sapi_module.get_stat();
1000
0
  } else {
1001
0
    if (!SG(request_info).path_translated || (VCWD_STAT(SG(request_info).path_translated, &SG(global_stat)) == -1)) {
1002
0
      return NULL;
1003
0
    }
1004
0
    return &SG(global_stat);
1005
0
  }
1006
0
}
1007
1008
SAPI_API char *sapi_getenv(const char *name, size_t name_len)
1009
1
{
1010
1
  char *value, *tmp;
1011
  
1012
1
  if (!sapi_module.getenv) {
1013
1
    return NULL;
1014
1
  }
1015
0
  if (!strncasecmp(name, "HTTP_PROXY", name_len)) {
1016
    /* Ugly fix for HTTP_PROXY issue, see bug #72573 */
1017
0
    return NULL;
1018
0
  }
1019
0
  tmp = sapi_module.getenv(name, name_len);
1020
0
  if (!tmp) {
1021
0
    return NULL;
1022
0
  }
1023
0
  value = estrdup(tmp);
1024
#ifdef PHP_WIN32
1025
  if (strlen(sapi_module.name) == sizeof("cgi-fcgi") - 1 && !strcmp(sapi_module.name, "cgi-fcgi")) {
1026
    /* XXX more modules to go, if needed. */
1027
    free(tmp);
1028
  }
1029
#endif
1030
0
  if (sapi_module.input_filter) {
1031
0
    sapi_module.input_filter(PARSE_STRING, name, &value, strlen(value), NULL);
1032
0
  }
1033
0
  return value;
1034
0
}
1035
1036
SAPI_API int sapi_get_fd(int *fd)
1037
0
{
1038
0
  if (sapi_module.get_fd) {
1039
0
    return sapi_module.get_fd(fd);
1040
0
  } else {
1041
0
    return FAILURE;
1042
0
  }
1043
0
}
1044
1045
SAPI_API int sapi_force_http_10(void)
1046
0
{
1047
0
  if (sapi_module.force_http_10) {
1048
0
    return sapi_module.force_http_10();
1049
0
  } else {
1050
0
    return FAILURE;
1051
0
  }
1052
0
}
1053
1054
1055
SAPI_API int sapi_get_target_uid(uid_t *obj)
1056
0
{
1057
0
  if (sapi_module.get_target_uid) {
1058
0
    return sapi_module.get_target_uid(obj);
1059
0
  } else {
1060
0
    return FAILURE;
1061
0
  }
1062
0
}
1063
1064
SAPI_API int sapi_get_target_gid(gid_t *obj)
1065
0
{
1066
0
  if (sapi_module.get_target_gid) {
1067
0
    return sapi_module.get_target_gid(obj);
1068
0
  } else {
1069
0
    return FAILURE;
1070
0
  }
1071
0
}
1072
1073
SAPI_API double sapi_get_request_time(void)
1074
7
{
1075
7
  if(SG(global_request_time)) return SG(global_request_time);
1076
1077
7
  if (!sapi_module.get_request_time
1078
7
      || sapi_module.get_request_time(&SG(global_request_time)) == FAILURE) {
1079
7
    struct timeval tp = {0};
1080
7
    if (!gettimeofday(&tp, NULL)) {
1081
7
      SG(global_request_time) = (double)(tp.tv_sec + tp.tv_usec / 1000000.00);
1082
7
    } else {
1083
0
      SG(global_request_time) = (double)time(0);
1084
0
    }
1085
7
  }
1086
7
  return SG(global_request_time);
1087
7
}
1088
1089
0
SAPI_API void sapi_terminate_process(void) {
1090
0
  if (sapi_module.terminate_process) {
1091
0
    sapi_module.terminate_process();
1092
0
  }
1093
0
}
1094
1095
SAPI_API void sapi_add_request_header(const char *var, unsigned int var_len, char *val, unsigned int val_len, void *arg) /* {{{ */
1096
0
{
1097
0
  zval *return_value = (zval*)arg;
1098
0
  char *buf = NULL;
1099
1100
0
  ALLOCA_FLAG(use_heap)
1101
1102
0
  if (var_len > 5 &&
1103
0
      var[0] == 'H' &&
1104
0
      var[1] == 'T' &&
1105
0
      var[2] == 'T' &&
1106
0
      var[3] == 'P' &&
1107
0
      var[4] == '_') {
1108
1109
0
    const char *p;
1110
0
    char *str;
1111
1112
0
    var_len -= 5;
1113
0
    p = var + 5;
1114
0
    var = str = buf = do_alloca(var_len + 1, use_heap);
1115
0
    *str++ = *p++;
1116
0
    while (*p) {
1117
0
      if (*p == '_') {
1118
0
        *str++ = '-';
1119
0
        p++;
1120
0
        if (*p) {
1121
0
          *str++ = *p++;
1122
0
        }
1123
0
      } else if (*p >= 'A' && *p <= 'Z') {
1124
0
        *str++ = (*p++ - 'A' + 'a');
1125
0
      } else {
1126
0
        *str++ = *p++;
1127
0
      }
1128
0
    }
1129
0
    *str = 0;
1130
0
  } else if (var_len == sizeof("CONTENT_TYPE")-1 &&
1131
0
             memcmp(var, "CONTENT_TYPE", sizeof("CONTENT_TYPE")-1) == 0) {
1132
0
    var = "Content-Type";
1133
0
  } else if (var_len == sizeof("CONTENT_LENGTH")-1 &&
1134
0
             memcmp(var, "CONTENT_LENGTH", sizeof("CONTENT_LENGTH")-1) == 0) {
1135
0
    var = "Content-Length";
1136
0
  } else {
1137
0
    return;
1138
0
  }
1139
0
  add_assoc_stringl_ex(return_value, var, var_len, val, val_len);
1140
0
  if (buf) {
1141
0
    free_alloca(buf, use_heap);
1142
0
  }
1143
0
}
1144
/* }}} */