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