/* +----------------------------------------------------------------------+ | PHP version 4.0 | +----------------------------------------------------------------------+ | Copyright (c) 1997, 1998, 1999, 2000 The PHP Group | +----------------------------------------------------------------------+ | This source file is subject to version 2.0 of the PHP license, | | that is bundled with this package in the file LICENSE, and is | | available at through the world-wide-web at | | http://www.php.net/license/2_0.txt. | | If you did not receive a copy of the PHP license and are unable to | | obtain it through the world-wide-web, please send a note to | | license@php.net so we can mail you a copy immediately. | +----------------------------------------------------------------------+ | Original design: Shane Caraveo | | Authors: Andi Gutmans | | Zeev Suraski | +----------------------------------------------------------------------+ */ #include #include #include "php.h" #include "SAPI.h" #ifdef ZTS #include "TSRM.h" #endif #include "rfc1867.h" #if PHP_WIN32 #define STRCASECMP stricmp #else #define STRCASECMP strcasecmp #endif SAPI_POST_READER_FUNC(sapi_read_standard_form_data); #define DEFAULT_POST_CONTENT_TYPE "application/x-www-form-urlencoded" static sapi_post_content_type_reader supported_post_content_types[] = { { DEFAULT_POST_CONTENT_TYPE, sizeof(DEFAULT_POST_CONTENT_TYPE)-1, sapi_read_standard_form_data }, #if HAVE_FDFLIB { "application/vnd.fdf", sizeof("application/vnd.fdf")-1, sapi_read_standard_form_data }, #endif { NULL, 0, NULL } }; static HashTable known_post_content_types; SAPI_API void (*sapi_error)(int error_type, const char *message, ...); #ifdef ZTS SAPI_API int sapi_globals_id; #else sapi_globals_struct sapi_globals; #endif /* True globals (no need for thread safety) */ sapi_module_struct sapi_module; SAPI_API void (*sapi_error)(int error_type, const char *message, ...); SAPI_API void sapi_startup(sapi_module_struct *sf) { sapi_module = *sf; zend_hash_init(&known_post_content_types, 5, NULL, NULL, 1); sapi_register_post_readers(supported_post_content_types); #ifdef ZTS sapi_globals_id = ts_allocate_id(sizeof(sapi_globals_struct), NULL, NULL); #endif reentrancy_startup(); php_global_startup_internal_extensions(); } SAPI_API void sapi_shutdown(void) { reentrancy_shutdown(); php_global_shutdown_internal_extensions(); zend_hash_destroy(&known_post_content_types); } SAPI_API void sapi_free_header(sapi_header_struct *sapi_header) { efree(sapi_header->header); } static void sapi_read_post_data(SLS_D) { sapi_post_content_type_reader *post_content_type_reader; uint content_type_length = strlen(SG(request_info).content_type); char *content_type = estrndup(SG(request_info).content_type, content_type_length); char *p; char oldchar=0; void (*post_reader_func)(char *content_type_dup SLS_DC); /* dedicated implementation for increased performance: * - Make the content type lowercase * - Trim descriptive data, stay with the content-type only */ for (p=content_type; ppost_reader; } else { if (!sapi_module.default_post_reader) { sapi_module.sapi_error(E_COMPILE_ERROR, "Unsupported content type: '%s'", content_type); return; } post_reader_func = sapi_module.default_post_reader; } if (oldchar) { *(p-1) = oldchar; } post_reader_func(content_type SLS_CC); efree(content_type); } SAPI_POST_READER_FUNC(sapi_read_standard_form_data) { int read_bytes, total_read_bytes=0; int allocated_bytes=SAPI_POST_BLOCK_SIZE+1; SG(request_info).post_data = emalloc(allocated_bytes); for (;;) { read_bytes = sapi_module.read_post(SG(request_info).post_data+total_read_bytes, SAPI_POST_BLOCK_SIZE SLS_CC); if (read_bytes<=0) { break; } total_read_bytes += read_bytes; if (read_bytes < SAPI_POST_BLOCK_SIZE) { break; } if (total_read_bytes+SAPI_POST_BLOCK_SIZE >= allocated_bytes) { allocated_bytes = total_read_bytes+SAPI_POST_BLOCK_SIZE+1; SG(request_info).post_data = erealloc(SG(request_info).post_data, allocated_bytes); } } SG(request_info).post_data[total_read_bytes] = 0; /* terminating NULL */ SG(request_info).post_data_length = total_read_bytes; } SAPI_API void sapi_activate(SLS_D) { zend_llist_init(&SG(sapi_headers).headers, sizeof(sapi_header_struct), (void (*)(void *)) sapi_free_header, 0); SG(sapi_headers).send_default_content_type = 1; SG(sapi_headers).http_response_code = 200; SG(sapi_headers).http_status_line = NULL; SG(headers_sent) = 0; SG(read_post_bytes) = 0; SG(request_info).post_data = NULL; SG(request_info).current_user = NULL; SG(request_info).current_user_length = 0; if (SG(request_info).request_method && !strcmp(SG(request_info).request_method, "HEAD")) { SG(request_info).headers_only = 1; } else { SG(request_info).headers_only = 0; } if (SG(server_context)) { if (SG(request_info).request_method && !strcmp(SG(request_info).request_method, "POST")) { if (!SG(request_info).content_type) { sapi_module.sapi_error(E_COMPILE_ERROR, "No content-type in POST request"); } sapi_read_post_data(SLS_C); } SG(request_info).cookie_data = sapi_module.read_cookies(SLS_C); if (sapi_module.activate) { sapi_module.activate(SLS_C); } } } SAPI_API void sapi_deactivate(SLS_D) { zend_llist_destroy(&SG(sapi_headers).headers); if (SG(request_info).post_data) { efree(SG(request_info).post_data); } if (SG(request_info).current_user) { efree(SG(request_info).current_user); } if (sapi_module.deactivate) { sapi_module.deactivate(SLS_C); } } static int sapi_extract_response_code(const char *header_line) { int code = 200; const char *ptr; for (ptr = header_line; *ptr; ptr++) { if (*ptr == ' ' && *(ptr + 1) != ' ') { code = atoi(ptr + 1); break; } } return code; } /* This function expects a *duplicated* string, that was previously emalloc()'d. * Pointers sent to this functions will be automatically freed by the framework. */ SAPI_API int sapi_add_header(char *header_line, uint header_line_len) { int retval; sapi_header_struct sapi_header; char *colon_offset; SLS_FETCH(); if (SG(headers_sent)) { char *output_start_filename = php_get_output_start_filename(); int output_start_lineno = php_get_output_start_lineno(); if (output_start_filename) { sapi_module.sapi_error(E_WARNING, "Cannot add header information - headers already sent by (output started at %s:%d)", output_start_filename, output_start_lineno); } else { sapi_module.sapi_error(E_WARNING, "Cannot add header information - headers already sent"); } efree(header_line); return FAILURE; } sapi_header.header = header_line; sapi_header.header_len = header_line_len; /* Check the header for a few cases that we have special support for in SAPI */ if (header_line_len>=5 && !memcmp(header_line, "HTTP/", 5)) { /* filter out the response code */ SG(sapi_headers).http_response_code = sapi_extract_response_code(header_line); SG(sapi_headers).http_status_line = header_line; return SUCCESS; } else { colon_offset = strchr(header_line, ':'); if (colon_offset) { *colon_offset = 0; if (!STRCASECMP(header_line, "Content-Type")) { SG(sapi_headers).send_default_content_type = 0; } else if (!STRCASECMP(header_line, "Location")) { SG(sapi_headers).http_response_code = 302; /* redirect */ } else if (!STRCASECMP(header_line, "WWW-Authenticate")) { /* HTTP Authentication */ SG(sapi_headers).http_response_code = 401; /* authentication-required */ } *colon_offset = ':'; } } if (sapi_module.header_handler) { retval = sapi_module.header_handler(&sapi_header, &SG(sapi_headers) SLS_CC); } else { retval = SAPI_HEADER_ADD; } if (retval & SAPI_HEADER_DELETE_ALL) { zend_llist_clean(&SG(sapi_headers).headers); } if (retval & SAPI_HEADER_ADD) { zend_llist_add_element(&SG(sapi_headers).headers, (void *) &sapi_header); } return SUCCESS; } SAPI_API int sapi_send_headers() { int retval; int ret = FAILURE; sapi_header_struct default_header = { SAPI_DEFAULT_CONTENT_TYPE, sizeof(SAPI_DEFAULT_CONTENT_TYPE)-1 }; SLS_FETCH(); if (SG(headers_sent)) { return SUCCESS; } if (sapi_module.send_headers) { retval = sapi_module.send_headers(&SG(sapi_headers) SLS_CC); } else { retval = SAPI_HEADER_DO_SEND; } switch (retval) { case SAPI_HEADER_SENT_SUCCESSFULLY: SG(headers_sent) = 1; ret = SUCCESS; break; case SAPI_HEADER_DO_SEND: if (SG(sapi_headers).http_status_line) { sapi_header_struct http_status_line; http_status_line.header = SG(sapi_headers).http_status_line; http_status_line.header_len = strlen(SG(sapi_headers).http_status_line); sapi_module.send_header(&http_status_line, SG(server_context)); } zend_llist_apply_with_argument(&SG(sapi_headers).headers, (void (*)(void *, void *)) sapi_module.send_header, SG(server_context)); if(SG(sapi_headers).send_default_content_type) { sapi_module.send_header(&default_header,SG(server_context)); } sapi_module.send_header(NULL, SG(server_context)); SG(headers_sent) = 1; ret = SUCCESS; break; case SAPI_HEADER_SEND_FAILED: ret = FAILURE; break; } if (SG(sapi_headers).http_status_line) { efree(SG(sapi_headers).http_status_line); } return ret; } SAPI_API int sapi_register_post_readers(sapi_post_content_type_reader *post_content_type_readers) { sapi_post_content_type_reader *p=post_content_type_readers; while (p->content_type) { if (sapi_register_post_reader(p)==FAILURE) { return FAILURE; } p++; } return SUCCESS; } SAPI_API int sapi_register_post_reader(sapi_post_content_type_reader *post_content_type_reader) { return zend_hash_add(&known_post_content_types, post_content_type_reader->content_type, post_content_type_reader->content_type_len+1, (void *) post_content_type_reader, sizeof(sapi_post_content_type_reader), NULL); } SAPI_API void sapi_unregister_post_reader(sapi_post_content_type_reader *post_content_type_reader) { zend_hash_del(&known_post_content_types, post_content_type_reader->content_type, post_content_type_reader->content_type_len+1); } SAPI_API int sapi_register_default_post_reader(void (*default_post_reader)(char *content_type_dup SLS_DC)) { sapi_module.default_post_reader = default_post_reader; return SUCCESS; } SAPI_API int sapi_flush() { if (sapi_module.flush) { SLS_FETCH(); sapi_module.flush(SG(server_context)); return SUCCESS; } else { return FAILURE; } } SAPI_API struct stat *sapi_get_stat() { SLS_FETCH(); if (sapi_module.get_stat) { return sapi_module.get_stat(SLS_C); } else { if (!SG(request_info).path_translated || (stat(SG(request_info).path_translated, &SG(global_stat))==-1)) { return NULL; } return &SG(global_stat); } } SAPI_API char *sapi_getenv(char *name, int name_len) { if (sapi_module.getenv) { SLS_FETCH(); return sapi_module.getenv(name, name_len SLS_CC); } else { return NULL; } }