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