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