xref: /PHP-5.3/main/SAPI.c (revision a2045ff3)
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