File:  [ELWIX - Embedded LightWeight unIX -] / embedaddon / php / main / SAPI.c
Revision 1.1.1.3 (vendor branch): download - view: text, annotated - select for diffs - revision graph
Mon Jul 22 01:32:11 2013 UTC (10 years, 11 months ago) by misho
Branches: php, MAIN
CVS tags: v5_4_29p0, v5_4_20p0, v5_4_20, v5_4_17, HEAD
5.4.17

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

FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>