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