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