diff options
author | Ondřej Surý <ondrej@sury.org> | 2012-02-01 21:25:15 +0100 |
---|---|---|
committer | Ondřej Surý <ondrej@sury.org> | 2012-02-01 21:25:15 +0100 |
commit | 96fb2ff5760132a915766f1d9ec7c63001feacd8 (patch) | |
tree | 160904a89a8f3522fa4e47632db101b045e7814a /sapi | |
parent | 8f1428d29ef91d74b4d272af171675f2971eb15b (diff) | |
download | php-96fb2ff5760132a915766f1d9ec7c63001feacd8.tar.gz |
Imported Upstream version 5.4.0~rc6upstream/5.4.0_rc6
Diffstat (limited to 'sapi')
70 files changed, 6672 insertions, 1374 deletions
diff --git a/sapi/apache/mod_php5.c b/sapi/apache/mod_php5.c index 76139575b..f399eb2bf 100644 --- a/sapi/apache/mod_php5.c +++ b/sapi/apache/mod_php5.c @@ -278,9 +278,6 @@ static void sapi_apache_register_server_variables(zval *track_vars_array TSRMLS_ /* If PATH_TRANSLATED doesn't exist, copy it from SCRIPT_FILENAME */ if (track_vars_array) { symbol_table = track_vars_array->value.ht; - } else if (PG(register_globals)) { - /* should never happen nowadays */ - symbol_table = EG(active_symbol_table); } else { symbol_table = NULL; } @@ -310,10 +307,8 @@ static int php_apache_startup(sapi_module_struct *sapi_module) /* {{{ php_apache_log_message */ -static void php_apache_log_message(char *message) +static void php_apache_log_message(char *message TSRMLS_DC) { - TSRMLS_FETCH(); - if (SG(server_context)) { #if MODULE_MAGIC_NUMBER >= 19970831 aplog_error(NULL, 0, APLOG_ERR | APLOG_NOERRNO, ((request_rec *) SG(server_context))->server, "%s", message); @@ -332,7 +327,7 @@ static void php_apache_request_shutdown(void *dummy) { TSRMLS_FETCH(); - php_output_set_status(0 TSRMLS_CC); + php_output_set_status(PHP_OUTPUT_DISABLED TSRMLS_CC); if (AP(in_request)) { AP(in_request) = 0; php_request_shutdown(dummy); @@ -443,9 +438,9 @@ static int sapi_apache_get_target_gid(gid_t *obj TSRMLS_DC) /* {{{ php_apache_get_request_time */ -static time_t php_apache_get_request_time(TSRMLS_D) +static double php_apache_get_request_time(TSRMLS_D) { - return ((request_rec *)SG(server_context))->request_time; + return (double) ((request_rec *)SG(server_context))->request_time; } /* }}} */ @@ -504,6 +499,7 @@ static sapi_module_struct apache_sapi_module = { NULL, /* treat data */ NULL, /* exe location */ 0, /* ini ignore */ + 0, /* ini ignore cwd */ sapi_apache_get_fd, sapi_apache_force_http_10, sapi_apache_get_target_uid, @@ -545,7 +541,7 @@ static void init_request_info(TSRMLS_D) SG(request_info).auth_password = NULL; SG(request_info).auth_digest = NULL; - if (authorization && (!PG(safe_mode) || (PG(safe_mode) && !auth_type(r)))) { + if (authorization) { char *p = getword(r->pool, &authorization, ' '); if (!strcasecmp(p, "Basic")) { tmp = uudecode(r->pool, authorization); diff --git a/sapi/apache/php_apache.c b/sapi/apache/php_apache.c index d171d56d4..dde864e15 100644 --- a/sapi/apache/php_apache.c +++ b/sapi/apache/php_apache.c @@ -272,7 +272,7 @@ PHP_MINFO_FUNCTION(apache) env_arr = table_elts(r->headers_in); env = (table_entry *)env_arr->elts; for (i = 0; i < env_arr->nelts; ++i) { - if (env[i].key && (!PG(safe_mode) || (PG(safe_mode) && strncasecmp(env[i].key, "authorization", 13)))) { + if (env[i].key) { php_info_print_table_row(2, env[i].key, env[i].val); } } @@ -350,7 +350,7 @@ PHP_FUNCTION(virtual) int filename_len; request_rec *rr = NULL; - if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &filename, &filename_len) == FAILURE) { + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "p", &filename, &filename_len) == FAILURE) { return; } @@ -368,7 +368,7 @@ PHP_FUNCTION(virtual) RETURN_FALSE; } - php_end_ob_buffers(1 TSRMLS_CC); + php_output_end_all(TSRMLS_C); php_header(TSRMLS_C); if (run_sub_req(rr)) { @@ -401,9 +401,7 @@ PHP_FUNCTION(apache_request_headers) env_arr = table_elts(((request_rec *) SG(server_context))->headers_in); tenv = (table_entry *)env_arr->elts; for (i = 0; i < env_arr->nelts; ++i) { - if (!tenv[i].key || - (PG(safe_mode) && - !strncasecmp(tenv[i].key, "authorization", 13))) { + if (!tenv[i].key) { continue; } if (add_assoc_string(return_value, tenv[i].key, (tenv[i].val==NULL) ? "" : tenv[i].val, 1)==FAILURE) { @@ -533,14 +531,14 @@ PHP_FUNCTION(apache_lookup_uri) #if 0 +/* This function is most likely a bad idea. Just playing with it for now. - +*/ PHP_FUNCTION(apache_exec_uri) { char *filename; int filename_len; request_rec *rr=NULL; - TSRMLS_FETCH(); if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &filename, &filename_len) == FAILURE) { return; @@ -594,11 +592,6 @@ PHP_FUNCTION(apache_get_modules) Reset the Apache write timer */ PHP_FUNCTION(apache_reset_timeout) { - if (PG(safe_mode)) { - php_error_docref(NULL TSRMLS_CC, E_WARNING, "Cannot reset the Apache timeout in safe mode"); - RETURN_FALSE; - } - ap_reset_timeout((request_rec *)SG(server_context)); RETURN_TRUE; } diff --git a/sapi/apache2filter/php_functions.c b/sapi/apache2filter/php_functions.c index cbb0e4cf8..f7b1f693c 100644 --- a/sapi/apache2filter/php_functions.c +++ b/sapi/apache2filter/php_functions.c @@ -63,7 +63,7 @@ PHP_FUNCTION(virtual) int filename_len; request_rec *rr; - if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &filename, &filename_len) == FAILURE) { + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "p", &filename, &filename_len) == FAILURE) { return; } @@ -101,7 +101,7 @@ PHP_FUNCTION(apache_lookup_uri) char *filename; int filename_len; - if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &filename, &filename_len) == FAILURE) { + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "p", &filename, &filename_len) == FAILURE) { return; } diff --git a/sapi/apache2filter/sapi_apache2.c b/sapi/apache2filter/sapi_apache2.c index 6597057f3..c7da1a7c6 100644 --- a/sapi/apache2filter/sapi_apache2.c +++ b/sapi/apache2filter/sapi_apache2.c @@ -282,10 +282,9 @@ php_apache_sapi_flush(void *server_context) } } -static void php_apache_sapi_log_message(char *msg) +static void php_apache_sapi_log_message(char *msg TSRMLS_DC) { php_struct *ctx; - TSRMLS_FETCH(); ctx = SG(server_context); @@ -309,10 +308,10 @@ php_apache_disable_caching(ap_filter_t *f) return OK; } -static time_t php_apache_sapi_get_request_time(TSRMLS_D) +static double php_apache_sapi_get_request_time(TSRMLS_D) { php_struct *ctx = SG(server_context); - return apr_time_sec(ctx->r->request_time); + return apr_time_as_msec(ctx->r->request_time); } extern zend_module_entry php_apache_module; @@ -426,17 +425,16 @@ static void php_apache_request_ctor(ap_filter_t *f, php_struct *ctx TSRMLS_DC) apr_table_unset(f->r->headers_out, "Last-Modified"); apr_table_unset(f->r->headers_out, "Expires"); apr_table_unset(f->r->headers_out, "ETag"); - if (!PG(safe_mode) || (PG(safe_mode) && !ap_auth_type(f->r))) { - auth = apr_table_get(f->r->headers_in, "Authorization"); - php_handle_auth_data(auth TSRMLS_CC); - if (SG(request_info).auth_user == NULL && f->r->user) { - SG(request_info).auth_user = estrdup(f->r->user); - } - ctx->r->user = apr_pstrdup(ctx->r->pool, SG(request_info).auth_user); - } else { - SG(request_info).auth_user = NULL; - SG(request_info).auth_password = NULL; + + auth = apr_table_get(f->r->headers_in, "Authorization"); + php_handle_auth_data(auth TSRMLS_CC); + + if (SG(request_info).auth_user == NULL && f->r->user) { + SG(request_info).auth_user = estrdup(f->r->user); } + + ctx->r->user = apr_pstrdup(ctx->r->pool, SG(request_info).auth_user); + php_request_startup(TSRMLS_C); } diff --git a/sapi/apache2handler/config.w32 b/sapi/apache2handler/config.w32 index ad3191823..08fdae190 100644 --- a/sapi/apache2handler/config.w32 +++ b/sapi/apache2handler/config.w32 @@ -1,11 +1,11 @@ // vim:ft=javascript -// $Id: config.w32 259731 2008-05-14 03:13:17Z auroraeosrose $ +// $Id: config.w32 313001 2011-07-06 11:13:38Z pajoye $ ARG_ENABLE('apache2handler', 'Build Apache 2.x handler', 'no'); if (PHP_APACHE2HANDLER != "no") { if (PHP_ZTS == "no") { - WARNING("Apache2 module requires an --enable-zts build of PHP on windows"); + WARNING("Apache 2.0 module requires an --enable-zts build of PHP on windows"); } else if (CHECK_HEADER_ADD_INCLUDE("httpd.h", "CFLAGS_APACHE2HANDLER", PHP_PHP_BUILD + "\\include\\apache2") && CHECK_LIB("libhttpd.lib", "apache2handler", PHP_PHP_BUILD + "\\lib\\apache2") && CHECK_LIB("libapr.lib", "apache2handler", PHP_PHP_BUILD + "\\lib\\apache2") && @@ -23,7 +23,7 @@ ARG_ENABLE('apache2-2handler', 'Build Apache 2.2.x handler', 'no'); if (PHP_APACHE2_2HANDLER != "no") { if (PHP_ZTS == "no") { - WARNING("Apache2 module requires an --enable-zts build of PHP on windows"); + WARNING("Apache 2.2 module requires an --enable-zts build of PHP on windows"); } else if (CHECK_HEADER_ADD_INCLUDE("httpd.h", "CFLAGS_APACHE2_2HANDLER", PHP_PHP_BUILD + "\\include\\apache2_2") && CHECK_LIB("libhttpd.lib", "apache2_2handler", PHP_PHP_BUILD + "\\lib\\apache2_2") && CHECK_LIB("libapr-1.lib", "apache2_2handler", PHP_PHP_BUILD + "\\lib\\apache2_2") && @@ -37,3 +37,22 @@ if (PHP_APACHE2_2HANDLER != "no") { WARNING("Could not find apache2.2 libraries/headers"); } } + +ARG_ENABLE('apache2-3handler', 'Build Apache 2.3.x handler', 'no'); +if (PHP_APACHE2_3HANDLER != "no") { + if (PHP_ZTS == "no") { + WARNING("Apache 2.3 module requires an --enable-zts build of PHP on windows"); + } else if (CHECK_HEADER_ADD_INCLUDE("httpd.h", "CFLAGS_APACHE2_3HANDLER", PHP_PHP_BUILD + "\\include\\apache2_3") && + CHECK_LIB("libhttpd.lib", "apache2_3handler", PHP_PHP_BUILD + "\\lib\\apache2_3") && + CHECK_LIB("libapr-1.lib", "apache2_3handler", PHP_PHP_BUILD + "\\lib\\apache2_3") && + CHECK_LIB("libaprutil-1.lib", "apache2_3handler", PHP_PHP_BUILD + "\\lib\\apache2_3") + ) { + SAPI('apache2_3handler', 'mod_php5.c sapi_apache2.c apache_config.c php_functions.c', + 'php' + PHP_VERSION + 'apache2_3.dll', + '/D PHP_APACHE2_EXPORTS /I win32', + 'sapi\\apache2handler'); + } else { + WARNING("Could not find apache2.3 libraries/headers"); + } +} + diff --git a/sapi/apache2handler/php_functions.c b/sapi/apache2handler/php_functions.c index ed9a03165..df1585400 100644 --- a/sapi/apache2handler/php_functions.c +++ b/sapi/apache2handler/php_functions.c @@ -75,7 +75,7 @@ PHP_FUNCTION(virtual) int filename_len; request_rec *rr; - if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &filename, &filename_len) == FAILURE) { + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "p", &filename, &filename_len) == FAILURE) { return; } @@ -91,7 +91,7 @@ PHP_FUNCTION(virtual) } /* Flush everything. */ - php_end_ob_buffers(1 TSRMLS_CC); + php_output_end_all(TSRMLS_C); php_header(TSRMLS_C); /* Ensure that the ap_r* layer for the main request is flushed, to @@ -121,7 +121,7 @@ PHP_FUNCTION(apache_lookup_uri) char *filename; int filename_len; - if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &filename, &filename_len) == FAILURE) { + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "p", &filename, &filename_len) == FAILURE) { return; } diff --git a/sapi/apache2handler/sapi_apache2.c b/sapi/apache2handler/sapi_apache2.c index d373adb50..7112e022f 100644 --- a/sapi/apache2handler/sapi_apache2.c +++ b/sapi/apache2handler/sapi_apache2.c @@ -313,10 +313,9 @@ php_apache_sapi_flush(void *server_context) } } -static void php_apache_sapi_log_message(char *msg) +static void php_apache_sapi_log_message(char *msg TSRMLS_DC) { php_struct *ctx; - TSRMLS_FETCH(); ctx = SG(server_context); @@ -327,19 +326,19 @@ static void php_apache_sapi_log_message(char *msg) } } -static void php_apache_sapi_log_message_ex(char *msg, request_rec *r) +static void php_apache_sapi_log_message_ex(char *msg, request_rec *r TSRMLS_DC) { if (r) { ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, msg, r->filename); } else { - php_apache_sapi_log_message(msg); + php_apache_sapi_log_message(msg TSRMLS_CC); } } -static time_t php_apache_sapi_get_request_time(TSRMLS_D) +static double php_apache_sapi_get_request_time(TSRMLS_D) { php_struct *ctx = SG(server_context); - return apr_time_sec(ctx->r->request_time); + return ((double) apr_time_as_msec(ctx->r->request_time)) / 1000.0; } extern zend_module_entry php_apache_module; @@ -490,17 +489,16 @@ static int php_apache_request_ctor(request_rec *r, php_struct *ctx TSRMLS_DC) apr_table_unset(r->headers_out, "Last-Modified"); apr_table_unset(r->headers_out, "Expires"); apr_table_unset(r->headers_out, "ETag"); - if (!PG(safe_mode) || (PG(safe_mode) && !ap_auth_type(r))) { - auth = apr_table_get(r->headers_in, "Authorization"); - php_handle_auth_data(auth TSRMLS_CC); - if (SG(request_info).auth_user == NULL && r->user) { - SG(request_info).auth_user = estrdup(r->user); - } - ctx->r->user = apr_pstrdup(ctx->r->pool, SG(request_info).auth_user); - } else { - SG(request_info).auth_user = NULL; - SG(request_info).auth_password = NULL; + + auth = apr_table_get(r->headers_in, "Authorization"); + php_handle_auth_data(auth TSRMLS_CC); + + if (SG(request_info).auth_user == NULL && r->user) { + SG(request_info).auth_user = estrdup(r->user); } + + ctx->r->user = apr_pstrdup(ctx->r->pool, SG(request_info).auth_user); + return php_request_startup(TSRMLS_C); } @@ -590,12 +588,12 @@ normal: } if (r->finfo.filetype == 0) { - php_apache_sapi_log_message_ex("script '%s' not found or unable to stat", r); + php_apache_sapi_log_message_ex("script '%s' not found or unable to stat", r TSRMLS_CC); PHPAP_INI_OFF; return HTTP_NOT_FOUND; } if (r->finfo.filetype == APR_DIR) { - php_apache_sapi_log_message_ex("attempt to invoke directory '%s' as script", r); + php_apache_sapi_log_message_ex("attempt to invoke directory '%s' as script", r TSRMLS_CC); PHPAP_INI_OFF; return HTTP_FORBIDDEN; } @@ -672,7 +670,7 @@ zend_first_try { } apr_table_set(r->notes, "mod_php_memory_usage", - apr_psprintf(ctx->r->pool, "%u", zend_memory_peak_usage(1 TSRMLS_CC))); + apr_psprintf(ctx->r->pool, "%zu", zend_memory_peak_usage(1 TSRMLS_CC))); } } zend_end_try(); diff --git a/sapi/apache_hooks/mod_php5.c b/sapi/apache_hooks/mod_php5.c index db68e5c9b..6937fe6da 100644 --- a/sapi/apache_hooks/mod_php5.c +++ b/sapi/apache_hooks/mod_php5.c @@ -1,20 +1,20 @@ /* +----------------------------------------------------------------------+ - | PHP Version 5 | + | PHP Version 5 | +----------------------------------------------------------------------+ - | Copyright (c) 1997-2012 The PHP Group | + | Copyright (c) 1997-2012 The PHP Group | +----------------------------------------------------------------------+ | This source file is subject to version 3.01 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/3_01.txt | + | 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/3_01.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. | + | obtain it through the world-wide-web, please send a note to | + | license@php.net so we can mail you a copy immediately. | +----------------------------------------------------------------------+ - | Authors: Rasmus Lerdorf <rasmus@php.net> | - | (with helpful hints from Dean Gaudet <dgaudet@arctic.org> | - | PHP 4.0 patches by Zeev Suraski <zeev@zend.com> | + | Authors: Rasmus Lerdorf <rasmus@php.net> | + | (with helpful hints from Dean Gaudet <dgaudet@arctic.org> | + | PHP 4.0 patches by Zeev Suraski <zeev@zend.com> | +----------------------------------------------------------------------+ */ /* $Id: mod_php5.c 321634 2012-01-01 13:15:04Z felipe $ */ @@ -403,9 +403,6 @@ static void sapi_apache_register_server_variables(zval *track_vars_array TSRMLS_ /* If PATH_TRANSLATED doesn't exist, copy it from SCRIPT_FILENAME */ if (track_vars_array) { symbol_table = track_vars_array->value.ht; - } else if (PG(register_globals)) { - /* should never happen nowadays */ - symbol_table = EG(active_symbol_table); } else { symbol_table = NULL; } @@ -433,10 +430,8 @@ static int php_apache_startup(sapi_module_struct *sapi_module) /* {{{ php_apache_log_message */ -static void php_apache_log_message(char *message) +static void php_apache_log_message(char *message TSRMLS_DC) { - TSRMLS_FETCH(); - if (SG(server_context)) { #if MODULE_MAGIC_NUMBER >= 19970831 aplog_error(NULL, 0, APLOG_ERR | APLOG_NOERRNO, ((request_rec *) SG(server_context))->server, "%s", message); @@ -456,7 +451,7 @@ static void php_apache_request_shutdown(void *dummy) { TSRMLS_FETCH(); AP(current_hook) = AP_CLEANUP; - php_output_set_status(0 TSRMLS_CC); + php_output_set_status(PHP_OUTPUT_DISABLED TSRMLS_CC); SG(server_context) = NULL; /* The server context (request) is invalid by the time run_cleanups() is called */ if(SG(sapi_started)) { php_request_shutdown(dummy); diff --git a/sapi/apache_hooks/php_apache.c b/sapi/apache_hooks/php_apache.c index bfcc629f0..8e03f9200 100644 --- a/sapi/apache_hooks/php_apache.c +++ b/sapi/apache_hooks/php_apache.c @@ -44,7 +44,7 @@ extern module **ap_loaded_modules; static int le_apachereq; static zend_class_entry *apacherequest_class_entry; -static void apache_table_to_zval(table *, int safe_mode, zval *return_value); +static void apache_table_to_zval(table *, zval *return_value); PHP_FUNCTION(virtual); PHP_FUNCTION(apache_request_headers); @@ -567,7 +567,7 @@ PHP_FUNCTION(apache_request_headers_in) APREQ_GET_REQUEST(id, r); - apache_table_to_zval(r->headers_in, 0, return_value); + apache_table_to_zval(r->headers_in, return_value); } /* }}} */ @@ -664,7 +664,7 @@ PHP_FUNCTION(apache_request_headers_out) add_header_to_table(r->headers_out, INTERNAL_FUNCTION_PARAM_PASSTHRU); } - apache_table_to_zval(r->headers_out, 0, return_value); + apache_table_to_zval(r->headers_out, return_value); } /* }}} */ @@ -683,7 +683,7 @@ PHP_FUNCTION(apache_request_err_headers_out) add_header_to_table(r->err_headers_out, INTERNAL_FUNCTION_PARAM_PASSTHRU); } - apache_table_to_zval(r->err_headers_out, 0, return_value); + apache_table_to_zval(r->err_headers_out, return_value); } /* }}} */ @@ -1683,7 +1683,7 @@ PHP_MINFO_FUNCTION(apache) env_arr = table_elts(r->headers_in); env = (table_entry *)env_arr->elts; for (i = 0; i < env_arr->nelts; ++i) { - if (env[i].key && (!PG(safe_mode) || (PG(safe_mode) && strncasecmp(env[i].key, "authorization", 13)))) { + if (env[i].key) { php_info_print_table_row(2, env[i].key, env[i].val); } } @@ -1734,7 +1734,7 @@ PHP_FUNCTION(virtual) RETURN_FALSE; } - php_end_ob_buffers(1 TSRMLS_CC); + php_output_end_all(TSRMLS_C); php_header(TSRMLS_C); if (run_sub_req(rr)) { @@ -1751,9 +1751,9 @@ PHP_FUNCTION(virtual) /* }}} */ -/* {{{ apache_table_to_zval(table *, int safe_mode, zval *return_value) +/* {{{ apache_table_to_zval(table *, zval *return_value) Fetch all HTTP request headers */ -static void apache_table_to_zval(table *t, int safe_mode, zval *return_value) +static void apache_table_to_zval(table *t, zval *return_value) { array_header *env_arr; table_entry *tenv; @@ -1763,8 +1763,7 @@ static void apache_table_to_zval(table *t, int safe_mode, zval *return_value) env_arr = table_elts(t); tenv = (table_entry *)env_arr->elts; for (i = 0; i < env_arr->nelts; ++i) { - if (!tenv[i].key || - (safe_mode && !strncasecmp(tenv[i].key, "authorization", 13))) { + if (!tenv[i].key) { continue; } if (add_assoc_string(return_value, tenv[i].key, (tenv[i].val==NULL) ? "" : tenv[i].val, 1)==FAILURE) { @@ -1789,7 +1788,7 @@ PHP_FUNCTION(apache_request_headers) return; } - apache_table_to_zval(((request_rec *)SG(server_context))->headers_in, PG(safe_mode), return_value); + apache_table_to_zval(((request_rec *)SG(server_context))->headers_in, return_value); } /* }}} */ @@ -1801,7 +1800,7 @@ PHP_FUNCTION(apache_response_headers) return; } - apache_table_to_zval(((request_rec *) SG(server_context))->headers_out, 0, return_value); + apache_table_to_zval(((request_rec *) SG(server_context))->headers_out, return_value); } /* }}} */ @@ -1906,13 +1905,14 @@ PHP_FUNCTION(apache_lookup_uri) #if 0 +/* This function is most likely a bad idea. Just playing with it for now. +*/ PHP_FUNCTION(apache_exec_uri) { zval **filename; request_rec *rr=NULL; - TSRMLS_FETCH(); if (ZEND_NUM_ARGS() != 1 || zend_get_parameters_ex(1, &filename) == FAILURE) { WRONG_PARAM_COUNT; diff --git a/sapi/apache_hooks/sapi_apache.c b/sapi/apache_hooks/sapi_apache.c index 568f10c4a..7e31479fc 100644 --- a/sapi/apache_hooks/sapi_apache.c +++ b/sapi/apache_hooks/sapi_apache.c @@ -78,13 +78,9 @@ int apache_php_module_hook(request_rec *r, php_handler *handler, zval **ret TSRM return FAILURE; } - req = php_apache_request_new(r); - if(PG(register_globals)) { - php_register_variable_ex("request", req, NULL TSRMLS_CC); - } - else { - php_register_variable_ex("request", req, PG(http_globals)[TRACK_VARS_SERVER] TSRMLS_CC); - } + req = php_apache_request_new(r); + php_register_variable_ex("request", req, PG(http_globals)[TRACK_VARS_SERVER] TSRMLS_CC); + switch(handler->type) { case AP_HANDLER_TYPE_FILE: php_register_variable("PHP_SELF_HOOK", handler->name, PG(http_globals)[TRACK_VARS_SERVER] TSRMLS_CC); diff --git a/sapi/cgi/Makefile.frag b/sapi/cgi/Makefile.frag index 57a3b2937..505119e57 100644 --- a/sapi/cgi/Makefile.frag +++ b/sapi/cgi/Makefile.frag @@ -1,2 +1,9 @@ -$(SAPI_CGI_PATH): $(PHP_GLOBAL_OBJS) $(PHP_SAPI_OBJS) +cgi: $(SAPI_CGI_PATH) + +$(SAPI_CGI_PATH): $(PHP_GLOBAL_OBJS) $(PHP_BINARY_OBJS) $(PHP_CGI_OBJS) $(BUILD_CGI) + +install-cgi: $(SAPI_CGI_PATH) + @echo "Installing PHP CGI binary: $(INSTALL_ROOT)$(bindir)/" + @$(INSTALL) -m 0755 $(SAPI_CGI_PATH) $(INSTALL_ROOT)$(bindir)/$(program_prefix)php-cgi$(program_suffix)$(EXEEXT) + diff --git a/sapi/cgi/cgi_main.c b/sapi/cgi/cgi_main.c index 083c6e353..8b4fc1f69 100644 --- a/sapi/cgi/cgi_main.c +++ b/sapi/cgi/cgi_main.c @@ -105,6 +105,7 @@ static void (*php_php_import_environment_variables)(zval *array_ptr TSRMLS_DC); */ static int children = 0; + /** * Set to non-zero if we are the parent process */ @@ -267,41 +268,30 @@ static void print_extensions(TSRMLS_D) #define STDOUT_FILENO 1 #endif -static inline size_t sapi_cgibin_single_write(const char *str, uint str_length TSRMLS_DC) +static inline size_t sapi_cgi_single_write(const char *str, uint str_length TSRMLS_DC) { #ifdef PHP_WRITE_STDOUT long ret; -#else - size_t ret; -#endif - - if (fcgi_is_fastcgi()) { - fcgi_request *request = (fcgi_request*) SG(server_context); - long ret = fcgi_write(request, FCGI_STDOUT, str, str_length); - if (ret <= 0) { - return 0; - } - return ret; - } -#ifdef PHP_WRITE_STDOUT ret = write(STDOUT_FILENO, str, str_length); if (ret <= 0) return 0; return ret; #else + size_t ret; + ret = fwrite(str, 1, MIN(str_length, 16384), stdout); return ret; #endif } -static int sapi_cgibin_ub_write(const char *str, uint str_length TSRMLS_DC) +static int sapi_cgi_ub_write(const char *str, uint str_length TSRMLS_DC) { const char *ptr = str; uint remaining = str_length; size_t ret; while (remaining > 0) { - ret = sapi_cgibin_single_write(ptr, remaining TSRMLS_CC); + ret = sapi_cgi_single_write(ptr, remaining TSRMLS_CC); if (!ret) { php_handle_aborted_connection(); return str_length - remaining; @@ -313,21 +303,43 @@ static int sapi_cgibin_ub_write(const char *str, uint str_length TSRMLS_DC) return str_length; } +static int sapi_fcgi_ub_write(const char *str, uint str_length TSRMLS_DC) +{ + const char *ptr = str; + uint remaining = str_length; + fcgi_request *request = (fcgi_request*) SG(server_context); + + while (remaining > 0) { + long ret = fcgi_write(request, FCGI_STDOUT, ptr, remaining); -static void sapi_cgibin_flush(void *server_context) + if (ret <= 0) { + php_handle_aborted_connection(); + return str_length - remaining; + } + ptr += ret; + remaining -= ret; + } + + return str_length; +} + +static void sapi_cgi_flush(void *server_context) { - if (fcgi_is_fastcgi()) { - fcgi_request *request = (fcgi_request*) server_context; - if ( + if (fflush(stdout) == EOF) { + php_handle_aborted_connection(); + } +} + +static void sapi_fcgi_flush(void *server_context) +{ + fcgi_request *request = (fcgi_request*) server_context; + + if ( #ifndef PHP_WIN32 !parent && #endif request && !fcgi_flush(request, 0)) { - php_handle_aborted_connection(); - } - return; - } - if (fflush(stdout) == EOF) { + php_handle_aborted_connection(); } } @@ -493,12 +505,24 @@ static int sapi_cgi_read_post(char *buffer, uint count_bytes TSRMLS_DC) count_bytes = MIN(count_bytes, (uint) SG(request_info).content_length - SG(read_post_bytes)); while (read_bytes < count_bytes) { - if (fcgi_is_fastcgi()) { - fcgi_request *request = (fcgi_request*) SG(server_context); - tmp_read_bytes = fcgi_read(request, buffer + read_bytes, count_bytes - read_bytes); - } else { - tmp_read_bytes = read(STDIN_FILENO, buffer + read_bytes, count_bytes - read_bytes); + tmp_read_bytes = read(STDIN_FILENO, buffer + read_bytes, count_bytes - read_bytes); + if (tmp_read_bytes <= 0) { + break; } + read_bytes += tmp_read_bytes; + } + return read_bytes; +} + +static int sapi_fcgi_read_post(char *buffer, uint count_bytes TSRMLS_DC) +{ + uint read_bytes = 0; + int tmp_read_bytes; + fcgi_request *request = (fcgi_request*) SG(server_context); + + count_bytes = MIN(count_bytes, (uint) SG(request_info).content_length - SG(read_post_bytes)); + while (read_bytes < count_bytes) { + tmp_read_bytes = fcgi_read(request, buffer + read_bytes, count_bytes - read_bytes); if (tmp_read_bytes <= 0) { break; } @@ -507,43 +531,33 @@ static int sapi_cgi_read_post(char *buffer, uint count_bytes TSRMLS_DC) return read_bytes; } -static char *sapi_cgibin_getenv(char *name, size_t name_len TSRMLS_DC) +static char *sapi_cgi_getenv(char *name, size_t name_len TSRMLS_DC) +{ + return getenv(name); +} + +static char *sapi_fcgi_getenv(char *name, size_t name_len TSRMLS_DC) { /* when php is started by mod_fastcgi, no regular environment * is provided to PHP. It is always sent to PHP at the start * of a request. So we have to do our own lookup to get env * vars. This could probably be faster somehow. */ - if (fcgi_is_fastcgi()) { - fcgi_request *request = (fcgi_request*) SG(server_context); - return fcgi_getenv(request, name, name_len); - } + fcgi_request *request = (fcgi_request*) SG(server_context); + char *ret = fcgi_getenv(request, name, name_len); + + if (ret) return ret; /* if cgi, or fastcgi and not found in fcgi env check the regular environment */ return getenv(name); } -static char *_sapi_cgibin_putenv(char *name, char *value TSRMLS_DC) +static char *_sapi_cgi_putenv(char *name, int name_len, char *value) { - int name_len; #if !HAVE_SETENV || !HAVE_UNSETENV int len; char *buf; #endif - if (!name) { - return NULL; - } - name_len = strlen(name); - - /* when php is started by mod_fastcgi, no regular environment - * is provided to PHP. It is always sent to PHP at the start - * of a request. So we have to do our own lookup to get env - * vars. This could probably be faster somehow. */ - if (fcgi_is_fastcgi()) { - fcgi_request *request = (fcgi_request*) SG(server_context); - return fcgi_putenv(request, name, name_len, value); - } - #if HAVE_SETENV if (value) { setenv(name, value, 1); @@ -584,10 +598,28 @@ static char *_sapi_cgibin_putenv(char *name, char *value TSRMLS_DC) static char *sapi_cgi_read_cookies(TSRMLS_D) { - return sapi_cgibin_getenv((char *) "HTTP_COOKIE", sizeof("HTTP_COOKIE")-1 TSRMLS_CC); + return getenv("HTTP_COOKIE"); } -void cgi_php_import_environment_variables(zval *array_ptr TSRMLS_DC) +static char *sapi_fcgi_read_cookies(TSRMLS_D) +{ + fcgi_request *request = (fcgi_request*) SG(server_context); + + return FCGI_GETENV(request, "HTTP_COOKIE"); +} + +static void cgi_php_load_env_var(char *var, unsigned int var_len, char *val, unsigned int val_len, void *arg TSRMLS_DC) +{ + zval *array_ptr = (zval*)arg; + int filter_arg = (array_ptr == PG(http_globals)[TRACK_VARS_ENV])?PARSE_ENV:PARSE_SERVER; + unsigned int new_val_len; + + if (sapi_module.input_filter(filter_arg, var, &val, strlen(val), &new_val_len TSRMLS_CC)) { + php_register_variable_safe(var, val, new_val_len, array_ptr TSRMLS_CC); + } +} + +static void cgi_php_import_environment_variables(zval *array_ptr TSRMLS_DC) { if (PG(http_globals)[TRACK_VARS_ENV] && array_ptr != PG(http_globals)[TRACK_VARS_ENV] && @@ -616,27 +648,7 @@ void cgi_php_import_environment_variables(zval *array_ptr TSRMLS_DC) if (fcgi_is_fastcgi()) { fcgi_request *request = (fcgi_request*) SG(server_context); - HashPosition pos; - int magic_quotes_gpc = PG(magic_quotes_gpc); - char *var, **val; - uint var_len; - ulong idx; - int filter_arg = (array_ptr == PG(http_globals)[TRACK_VARS_ENV])?PARSE_ENV:PARSE_SERVER; - - /* turn off magic_quotes while importing environment variables */ - PG(magic_quotes_gpc) = 0; - for (zend_hash_internal_pointer_reset_ex(request->env, &pos); - zend_hash_get_current_key_ex(request->env, &var, &var_len, &idx, 0, &pos) == HASH_KEY_IS_STRING && - zend_hash_get_current_data_ex(request->env, (void **) &val, &pos) == SUCCESS; - zend_hash_move_forward_ex(request->env, &pos) - ) { - unsigned int new_val_len; - - if (sapi_module.input_filter(filter_arg, var, val, strlen(*val), &new_val_len TSRMLS_CC)) { - php_register_variable_safe(var, *val, new_val_len, array_ptr TSRMLS_CC); - } - } - PG(magic_quotes_gpc) = magic_quotes_gpc; + fcgi_loadenv(request, cgi_php_load_env_var, array_ptr TSRMLS_CC); } } @@ -652,25 +664,51 @@ static void sapi_cgi_register_variables(zval *track_vars_array TSRMLS_DC) if (CGIG(fix_pathinfo)) { char *script_name = SG(request_info).request_uri; - unsigned int script_name_len = script_name ? strlen(script_name) : 0; - char *path_info = sapi_cgibin_getenv("PATH_INFO", sizeof("PATH_INFO")-1 TSRMLS_CC); - unsigned int path_info_len = path_info ? strlen(path_info) : 0; + char *path_info; + int free_php_self; + ALLOCA_FLAG(use_heap) - php_self_len = script_name_len + path_info_len; - php_self = emalloc(php_self_len + 1); + if (fcgi_is_fastcgi()) { + fcgi_request *request = (fcgi_request*) SG(server_context); - if (script_name) { - memcpy(php_self, script_name, script_name_len + 1); + path_info = FCGI_GETENV(request, "PATH_INFO"); + } else { + path_info = getenv("PATH_INFO"); } + if (path_info) { - memcpy(php_self + script_name_len, path_info, path_info_len + 1); + unsigned int path_info_len = strlen(path_info); + + if (script_name) { + unsigned int script_name_len = strlen(script_name); + + php_self_len = script_name_len + path_info_len; + php_self = do_alloca(php_self_len + 1, use_heap); + memcpy(php_self, script_name, script_name_len + 1); + memcpy(php_self + script_name_len, path_info, path_info_len + 1); + free_php_self = 1; + } else { + php_self = path_info; + php_self_len = path_info_len; + free_php_self = 0; + } + } else if (script_name) { + php_self = script_name; + php_self_len = strlen(script_name); + free_php_self = 0; + } else { + php_self = ""; + php_self_len = 0; + free_php_self = 0; } /* Build the special-case PHP_SELF variable for the CGI version */ if (sapi_module.input_filter(PARSE_SERVER, "PHP_SELF", &php_self, php_self_len, &php_self_len TSRMLS_CC)) { php_register_variable_safe("PHP_SELF", php_self, php_self_len, track_vars_array TSRMLS_CC); } - efree(php_self); + if (free_php_self) { + free_alloca(php_self, use_heap); + } } else { php_self = SG(request_info).request_uri ? SG(request_info).request_uri : ""; php_self_len = strlen(php_self); @@ -680,10 +718,8 @@ static void sapi_cgi_register_variables(zval *track_vars_array TSRMLS_DC) } } -static void sapi_cgi_log_message(char *message) +static void sapi_cgi_log_message(char *message TSRMLS_DC) { - TSRMLS_FETCH(); - if (fcgi_is_fastcgi() && CGIG(fcgi_logging)) { fcgi_request *request; @@ -735,7 +771,6 @@ static void php_cgi_ini_activate_user_config(char *path, int path_len, const cha if (!IS_ABSOLUTE_PATH(path, path_len)) { real_path = tsrm_realpath(path, NULL TSRMLS_CC); - /* see #51688, looks like we may get invalid path as doc root using cgi with apache */ if (real_path == NULL) { return; } @@ -797,7 +832,13 @@ static int sapi_cgi_activate(TSRMLS_D) if (php_ini_has_per_host_config()) { /* Activate per-host-system-configuration defined in php.ini and stored into configuration_hash during startup */ - server_name = sapi_cgibin_getenv("SERVER_NAME", sizeof("SERVER_NAME") - 1 TSRMLS_CC); + if (fcgi_is_fastcgi()) { + fcgi_request *request = (fcgi_request*) SG(server_context); + + server_name = FCGI_GETENV(request, "SERVER_NAME"); + } else { + server_name = getenv("SERVER_NAME"); + } /* SERVER_NAME should also be defined at this stage..but better check it anyway */ if (server_name) { server_name_len = strlen(server_name); @@ -831,7 +872,14 @@ static int sapi_cgi_activate(TSRMLS_D) /* Load and activate user ini files in path starting from DOCUMENT_ROOT */ if (PG(user_ini_filename) && *PG(user_ini_filename)) { - doc_root = sapi_cgibin_getenv("DOCUMENT_ROOT", sizeof("DOCUMENT_ROOT") - 1 TSRMLS_CC); + if (fcgi_is_fastcgi()) { + fcgi_request *request = (fcgi_request*) SG(server_context); + + doc_root = FCGI_GETENV(request, "DOCUMENT_ROOT"); + } else { + doc_root = getenv("DOCUMENT_ROOT"); + } + /* DOCUMENT_ROOT should also be defined at this stage..but better check it anyway */ if (doc_root) { doc_root_len = strlen(doc_root); @@ -872,7 +920,7 @@ static int sapi_cgi_deactivate(TSRMLS_D) php_handle_aborted_connection(); } } else { - sapi_cgibin_flush(SG(server_context)); + sapi_cgi_flush(SG(server_context)); } } return SUCCESS; @@ -898,10 +946,10 @@ static sapi_module_struct cgi_sapi_module = { sapi_cgi_activate, /* activate */ sapi_cgi_deactivate, /* deactivate */ - sapi_cgibin_ub_write, /* unbuffered write */ - sapi_cgibin_flush, /* flush */ + sapi_cgi_ub_write, /* unbuffered write */ + sapi_cgi_flush, /* flush */ NULL, /* get uid */ - sapi_cgibin_getenv, /* getenv */ + sapi_cgi_getenv, /* getenv */ php_error, /* error handler */ @@ -976,34 +1024,43 @@ static void php_cgi_usage(char *argv0) */ static int is_valid_path(const char *path) { - const char *p; + const char *p = path; - if (!path) { + if (UNEXPECTED(!p)) { return 0; } - p = strstr(path, ".."); - if (p) { - if ((p == path || IS_SLASH(*(p-1))) && - (*(p+2) == 0 || IS_SLASH(*(p+2))) - ) { - return 0; - } - while (1) { - p = strstr(p+1, ".."); - if (!p) { - break; - } - if (IS_SLASH(*(p-1)) && - (*(p+2) == 0 || IS_SLASH(*(p+2))) - ) { - return 0; + if (UNEXPECTED(*p == '.') && *(p+1) == '.' && (!*(p+2) || IS_SLASH(*(p+2)))) { + return 0; + } + while (*p) { + if (IS_SLASH(*p)) { + p++; + if (UNEXPECTED(*p == '.')) { + p++; + if (UNEXPECTED(*p == '.')) { + p++; + if (UNEXPECTED(!*p) || UNEXPECTED(IS_SLASH(*p))) { + return 0; + } + } } } + p++; } return 1; } /* }}} */ +#define CGI_GETENV(name) \ + ((request) ? \ + FCGI_GETENV(request, name) : \ + getenv(name)) + +#define CGI_PUTENV(name, value) \ + ((request) ? \ + FCGI_PUTENV(request, name, value) : \ + _sapi_cgi_putenv(name, sizeof(name)-1, value)) + /* {{{ init_request_info initializes request_info structure @@ -1070,10 +1127,10 @@ static int is_valid_path(const char *path) Comments in the code below refer to using the above URL in a request */ -static void init_request_info(TSRMLS_D) +static void init_request_info(fcgi_request *request TSRMLS_DC) { - char *env_script_filename = sapi_cgibin_getenv("SCRIPT_FILENAME", sizeof("SCRIPT_FILENAME")-1 TSRMLS_CC); - char *env_path_translated = sapi_cgibin_getenv("PATH_TRANSLATED", sizeof("PATH_TRANSLATED")-1 TSRMLS_CC); + char *env_script_filename = CGI_GETENV("SCRIPT_FILENAME"); + char *env_path_translated = CGI_GETENV("PATH_TRANSLATED"); char *script_path_translated = env_script_filename; /* some broken servers do not have script_filename or argv0 @@ -1099,32 +1156,35 @@ static void init_request_info(TSRMLS_D) * of the script will be retreived later via argc/argv */ if (script_path_translated) { const char *auth; - char *content_length = sapi_cgibin_getenv("CONTENT_LENGTH", sizeof("CONTENT_LENGTH")-1 TSRMLS_CC); - char *content_type = sapi_cgibin_getenv("CONTENT_TYPE", sizeof("CONTENT_TYPE")-1 TSRMLS_CC); - char *env_path_info = sapi_cgibin_getenv("PATH_INFO", sizeof("PATH_INFO")-1 TSRMLS_CC); - char *env_script_name = sapi_cgibin_getenv("SCRIPT_NAME", sizeof("SCRIPT_NAME")-1 TSRMLS_CC); + char *content_length = CGI_GETENV("CONTENT_LENGTH"); + char *content_type = CGI_GETENV("CONTENT_TYPE"); + char *env_path_info = CGI_GETENV("PATH_INFO"); + char *env_script_name = CGI_GETENV("SCRIPT_NAME"); +#ifdef PHP_WIN32 /* Hack for buggy IIS that sets incorrect PATH_INFO */ - char *env_server_software = sapi_cgibin_getenv("SERVER_SOFTWARE", sizeof("SERVER_SOFTWARE")-1 TSRMLS_CC); + char *env_server_software = CGI_GETENV("SERVER_SOFTWARE"); + if (env_server_software && env_script_name && env_path_info && strncmp(env_server_software, "Microsoft-IIS", sizeof("Microsoft-IIS")-1) == 0 && strncmp(env_path_info, env_script_name, strlen(env_script_name)) == 0 ) { - env_path_info = _sapi_cgibin_putenv("ORIG_PATH_INFO", env_path_info TSRMLS_CC); + env_path_info = CGI_PUTENV("ORIG_PATH_INFO", env_path_info); env_path_info += strlen(env_script_name); if (*env_path_info == 0) { env_path_info = NULL; } - env_path_info = _sapi_cgibin_putenv("PATH_INFO", env_path_info TSRMLS_CC); + env_path_info = CGI_PUTENV("PATH_INFO", env_path_info); } +#endif if (CGIG(fix_pathinfo)) { struct stat st; char *real_path = NULL; - char *env_redirect_url = sapi_cgibin_getenv("REDIRECT_URL", sizeof("REDIRECT_URL")-1 TSRMLS_CC); - char *env_document_root = sapi_cgibin_getenv("DOCUMENT_ROOT", sizeof("DOCUMENT_ROOT")-1 TSRMLS_CC); + char *env_redirect_url = CGI_GETENV("REDIRECT_URL"); + char *env_document_root = CGI_GETENV("DOCUMENT_ROOT"); char *orig_path_translated = env_path_translated; char *orig_path_info = env_path_info; char *orig_script_name = env_script_name; @@ -1132,7 +1192,7 @@ static void init_request_info(TSRMLS_D) int script_path_translated_len; if (!env_document_root && PG(doc_root)) { - env_document_root = _sapi_cgibin_putenv("DOCUMENT_ROOT", PG(doc_root) TSRMLS_CC); + env_document_root = CGI_PUTENV("DOCUMENT_ROOT", PG(doc_root)); /* fix docroot */ TRANSLATE_SLASHES(env_document_root); } @@ -1199,28 +1259,28 @@ static void init_request_info(TSRMLS_D) if (orig_path_info) { char old; - _sapi_cgibin_putenv("ORIG_PATH_INFO", orig_path_info TSRMLS_CC); + CGI_PUTENV("ORIG_PATH_INFO", orig_path_info); old = path_info[0]; path_info[0] = 0; if (!orig_script_name || strcmp(orig_script_name, env_path_info) != 0) { if (orig_script_name) { - _sapi_cgibin_putenv("ORIG_SCRIPT_NAME", orig_script_name TSRMLS_CC); + CGI_PUTENV("ORIG_SCRIPT_NAME", orig_script_name); } - SG(request_info).request_uri = _sapi_cgibin_putenv("SCRIPT_NAME", env_path_info TSRMLS_CC); + SG(request_info).request_uri = CGI_PUTENV("SCRIPT_NAME", env_path_info); } else { SG(request_info).request_uri = orig_script_name; } path_info[0] = old; } - env_path_info = _sapi_cgibin_putenv("PATH_INFO", path_info TSRMLS_CC); + env_path_info = CGI_PUTENV("PATH_INFO", path_info); } if (!orig_script_filename || strcmp(orig_script_filename, pt) != 0) { if (orig_script_filename) { - _sapi_cgibin_putenv("ORIG_SCRIPT_FILENAME", orig_script_filename TSRMLS_CC); + CGI_PUTENV("ORIG_SCRIPT_FILENAME", orig_script_filename); } - script_path_translated = _sapi_cgibin_putenv("SCRIPT_FILENAME", pt TSRMLS_CC); + script_path_translated = CGI_PUTENV("SCRIPT_FILENAME", pt); } TRANSLATE_SLASHES(pt); @@ -1250,9 +1310,9 @@ static void init_request_info(TSRMLS_D) } path_translated[path_translated_len] = '\0'; if (orig_path_translated) { - _sapi_cgibin_putenv("ORIG_PATH_TRANSLATED", orig_path_translated TSRMLS_CC); + CGI_PUTENV("ORIG_PATH_TRANSLATED", orig_path_translated); } - env_path_translated = _sapi_cgibin_putenv("PATH_TRANSLATED", path_translated TSRMLS_CC); + env_path_translated = CGI_PUTENV("PATH_TRANSLATED", path_translated); efree(path_translated); } else if ( env_script_name && strstr(pt, env_script_name) @@ -1269,9 +1329,9 @@ static void init_request_info(TSRMLS_D) } path_translated[path_translated_len] = '\0'; if (orig_path_translated) { - _sapi_cgibin_putenv("ORIG_PATH_TRANSLATED", orig_path_translated TSRMLS_CC); + CGI_PUTENV("ORIG_PATH_TRANSLATED", orig_path_translated); } - env_path_translated = _sapi_cgibin_putenv("PATH_TRANSLATED", path_translated TSRMLS_CC); + env_path_translated = CGI_PUTENV("PATH_TRANSLATED", path_translated); efree(path_translated); } break; @@ -1284,18 +1344,18 @@ static void init_request_info(TSRMLS_D) * have failed anyway... we output 'no input file' now. */ if (orig_script_filename) { - _sapi_cgibin_putenv("ORIG_SCRIPT_FILENAME", orig_script_filename TSRMLS_CC); + CGI_PUTENV("ORIG_SCRIPT_FILENAME", orig_script_filename); } - script_path_translated = _sapi_cgibin_putenv("SCRIPT_FILENAME", NULL TSRMLS_CC); + script_path_translated = CGI_PUTENV("SCRIPT_FILENAME", NULL); SG(sapi_headers).http_response_code = 404; } if (!SG(request_info).request_uri) { if (!orig_script_name || strcmp(orig_script_name, env_script_name) != 0) { if (orig_script_name) { - _sapi_cgibin_putenv("ORIG_SCRIPT_NAME", orig_script_name TSRMLS_CC); + CGI_PUTENV("ORIG_SCRIPT_NAME", orig_script_name); } - SG(request_info).request_uri = _sapi_cgibin_putenv("SCRIPT_NAME", env_script_name TSRMLS_CC); + SG(request_info).request_uri = CGI_PUTENV("SCRIPT_NAME", env_script_name); } else { SG(request_info).request_uri = orig_script_name; } @@ -1309,25 +1369,25 @@ static void init_request_info(TSRMLS_D) (script_path_translated != orig_script_filename && strcmp(script_path_translated, orig_script_filename) != 0)) { if (orig_script_filename) { - _sapi_cgibin_putenv("ORIG_SCRIPT_FILENAME", orig_script_filename TSRMLS_CC); + CGI_PUTENV("ORIG_SCRIPT_FILENAME", orig_script_filename); } - script_path_translated = _sapi_cgibin_putenv("SCRIPT_FILENAME", script_path_translated TSRMLS_CC); + script_path_translated = CGI_PUTENV("SCRIPT_FILENAME", script_path_translated); } if (env_redirect_url) { if (orig_path_info) { - _sapi_cgibin_putenv("ORIG_PATH_INFO", orig_path_info TSRMLS_CC); - _sapi_cgibin_putenv("PATH_INFO", NULL TSRMLS_CC); + CGI_PUTENV("ORIG_PATH_INFO", orig_path_info); + CGI_PUTENV("PATH_INFO", NULL); } if (orig_path_translated) { - _sapi_cgibin_putenv("ORIG_PATH_TRANSLATED", orig_path_translated TSRMLS_CC); - _sapi_cgibin_putenv("PATH_TRANSLATED", NULL TSRMLS_CC); + CGI_PUTENV("ORIG_PATH_TRANSLATED", orig_path_translated); + CGI_PUTENV("PATH_TRANSLATED", NULL); } } if (env_script_name != orig_script_name) { if (orig_script_name) { - _sapi_cgibin_putenv("ORIG_SCRIPT_NAME", orig_script_name TSRMLS_CC); + CGI_PUTENV("ORIG_SCRIPT_NAME", orig_script_name); } - SG(request_info).request_uri = _sapi_cgibin_putenv("SCRIPT_NAME", env_script_name TSRMLS_CC); + SG(request_info).request_uri = CGI_PUTENV("SCRIPT_NAME", env_script_name); } else { SG(request_info).request_uri = env_script_name; } @@ -1349,14 +1409,14 @@ static void init_request_info(TSRMLS_D) SG(request_info).path_translated = estrdup(script_path_translated); } - SG(request_info).request_method = sapi_cgibin_getenv("REQUEST_METHOD", sizeof("REQUEST_METHOD")-1 TSRMLS_CC); + SG(request_info).request_method = CGI_GETENV("REQUEST_METHOD"); /* FIXME - Work out proto_num here */ - SG(request_info).query_string = sapi_cgibin_getenv("QUERY_STRING", sizeof("QUERY_STRING")-1 TSRMLS_CC); + SG(request_info).query_string = CGI_GETENV("QUERY_STRING"); SG(request_info).content_type = (content_type ? content_type : "" ); SG(request_info).content_length = (content_length ? atol(content_length) : 0); /* The CGI RFC allows servers to pass on unvalidated Authorization data */ - auth = sapi_cgibin_getenv("HTTP_AUTHORIZATION", sizeof("HTTP_AUTHORIZATION")-1 TSRMLS_CC); + auth = CGI_GETENV("HTTP_AUTHORIZATION"); php_handle_auth_data(auth TSRMLS_CC); } } @@ -1451,10 +1511,199 @@ static PHP_MINFO_FUNCTION(cgi) } /* }}} */ +PHP_FUNCTION(apache_child_terminate) /* {{{ */ +{ + if (ZEND_NUM_ARGS() > 0) { + WRONG_PARAM_COUNT; + } + if (fcgi_is_fastcgi()) { + fcgi_terminate(); + } +} +/* }}} */ + +static void add_request_header(char *var, unsigned int var_len, char *val, unsigned int val_len, void *arg TSRMLS_DC) /* {{{ */ +{ + zval *return_value = (zval*)arg; + char *str = NULL; + char *p; + ALLOCA_FLAG(use_heap) + + if (var_len > 5 && + var[0] == 'H' && + var[1] == 'T' && + var[2] == 'T' && + var[3] == 'P' && + var[4] == '_') { + + var_len -= 5; + p = var + 5; + var = str = do_alloca(var_len + 1, use_heap); + *str++ = *p++; + while (*p) { + if (*p == '_') { + *str++ = '-'; + p++; + if (*p) { + *str++ = *p++; + } + } else if (*p >= 'A' && *p <= 'Z') { + *str++ = (*p++ - 'A' + 'a'); + } else { + *str++ = *p++; + } + } + *str = 0; + } else if (var_len == sizeof("CONTENT_TYPE")-1 && + memcmp(var, "CONTENT_TYPE", sizeof("CONTENT_TYPE")-1) == 0) { + var = "Content-Type"; + } else if (var_len == sizeof("CONTENT_LENGTH")-1 && + memcmp(var, "CONTENT_LENGTH", sizeof("CONTENT_LENGTH")-1) == 0) { + var = "Content-Length"; + } else { + return; + } + add_assoc_stringl_ex(return_value, var, var_len+1, val, val_len, 1); + if (str) { + free_alloca(var, use_heap); + } +} +/* }}} */ + +PHP_FUNCTION(apache_request_headers) /* {{{ */ +{ + if (ZEND_NUM_ARGS() > 0) { + WRONG_PARAM_COUNT; + } + array_init(return_value); + if (fcgi_is_fastcgi()) { + fcgi_request *request = (fcgi_request*) SG(server_context); + + fcgi_loadenv(request, add_request_header, return_value TSRMLS_CC); + } else { + char buf[128]; + char **env, *p, *q, *var, *val, *t = buf; + size_t alloc_size = sizeof(buf); + unsigned long var_len; + + for (env = environ; env != NULL && *env != NULL; env++) { + val = strchr(*env, '='); + if (!val) { /* malformed entry? */ + continue; + } + var_len = val - *env; + if (var_len >= alloc_size) { + alloc_size = var_len + 64; + t = (t == buf ? emalloc(alloc_size): erealloc(t, alloc_size)); + } + var = *env; + if (var_len > 5 && + var[0] == 'H' && + var[1] == 'T' && + var[2] == 'T' && + var[3] == 'P' && + var[4] == '_') { + + var_len -= 5; + + if (var_len >= alloc_size) { + alloc_size = var_len + 64; + t = (t == buf ? emalloc(alloc_size): erealloc(t, alloc_size)); + } + p = var + 5; + + var = q = t; + *q++ = *p++; + while (*p) { + if (*p == '_') { + *q++ = '-'; + p++; + if (*p) { + *q++ = *p++; + } + } else if (*p >= 'A' && *p <= 'Z') { + *q++ = (*p++ - 'A' + 'a'); + } else { + *q++ = *p++; + } + } + *q = 0; + } else if (var_len == sizeof("CONTENT_TYPE")-1 && + memcmp(var, "CONTENT_TYPE", sizeof("CONTENT_TYPE")-1) == 0) { + var = "Content-Type"; + } else if (var_len == sizeof("CONTENT_LENGTH")-1 && + memcmp(var, "CONTENT_LENGTH", sizeof("CONTENT_LENGTH")-1) == 0) { + var = "Content-Length"; + } else { + continue; + } + val++; + add_assoc_string_ex(return_value, var, var_len+1, val, 1); + } + if (t != buf && t != NULL) { + efree(t); + } + } +} +/* }}} */ + +static void add_response_header(sapi_header_struct *h, zval *return_value TSRMLS_DC) /* {{{ */ +{ + char *s, *p; + int len; + ALLOCA_FLAG(use_heap) + + if (h->header_len > 0) { + p = strchr(h->header, ':'); + len = p - h->header; + if (p && (len > 0)) { + while (len > 0 && (h->header[len-1] == ' ' || h->header[len-1] == '\t')) { + len--; + } + if (len) { + s = do_alloca(len + 1, use_heap); + memcpy(s, h->header, len); + s[len] = 0; + do { + p++; + } while (*p == ' ' || *p == '\t'); + add_assoc_stringl_ex(return_value, s, len+1, p, h->header_len - (p - h->header), 1); + free_alloca(s, use_heap); + } + } + } +} +/* }}} */ + +PHP_FUNCTION(apache_response_headers) /* {{{ */ +{ + if (ZEND_NUM_ARGS() > 0) { + WRONG_PARAM_COUNT; + } + + if (!&SG(sapi_headers).headers) { + RETURN_FALSE; + } + array_init(return_value); + zend_llist_apply_with_argument(&SG(sapi_headers).headers, (llist_apply_with_arg_func_t)add_response_header, return_value TSRMLS_CC); +} +/* }}} */ + +ZEND_BEGIN_ARG_INFO(arginfo_no_args, 0) +ZEND_END_ARG_INFO() + +const zend_function_entry cgi_functions[] = { + PHP_FE(apache_child_terminate, arginfo_no_args) + PHP_FE(apache_request_headers, arginfo_no_args) + PHP_FE(apache_response_headers, arginfo_no_args) + PHP_FALIAS(getallheaders, apache_request_headers, arginfo_no_args) + {NULL, NULL, NULL} +}; + static zend_module_entry cgi_module_entry = { STANDARD_MODULE_HEADER, "cgi-fcgi", - NULL, + cgi_functions, PHP_MINIT(cgi), PHP_MSHUTDOWN(cgi), NULL, @@ -1489,10 +1738,10 @@ int main(int argc, char *argv[]) int max_requests = 500; int requests = 0; - int fastcgi = fcgi_is_fastcgi(); + int fastcgi; char *bindpath = NULL; int fcgi_fd = 0; - fcgi_request request; + fcgi_request *request = NULL; int repeats = 1; int benchmark = 0; #if HAVE_GETTIMEOFDAY @@ -1531,6 +1780,7 @@ int main(int argc, char *argv[]) #endif sapi_startup(&cgi_sapi_module); + fastcgi = fcgi_is_fastcgi(); cgi_sapi_module.php_ini_path_override = NULL; #ifdef PHP_WIN32 @@ -1679,6 +1929,13 @@ consult the installation file that came with this distribution, or visit \n\ fastcgi = fcgi_is_fastcgi(); } if (fastcgi) { + /* Override SAPI callbacks */ + sapi_module.ub_write = sapi_fcgi_ub_write; + sapi_module.flush = sapi_fcgi_flush; + sapi_module.read_post = sapi_fcgi_read_post; + sapi_module.getenv = sapi_fcgi_getenv; + sapi_module.read_cookies = sapi_fcgi_read_cookies; + /* How many times to run PHP scripts before dying */ if (getenv("PHP_FCGI_MAX_REQUESTS")) { max_requests = atoi(getenv("PHP_FCGI_MAX_REQUESTS")); @@ -1693,7 +1950,7 @@ consult the installation file that came with this distribution, or visit \n\ php_import_environment_variables = cgi_php_import_environment_variables; /* library is already initialized, now init our request */ - fcgi_init_request(&request, fcgi_fd); + request = fcgi_init_request(fcgi_fd); #ifndef PHP_WIN32 /* Pre-fork, if required */ @@ -1814,13 +2071,14 @@ consult the installation file that came with this distribution, or visit \n\ break; case 'h': case '?': + if (request) { + fcgi_destroy_request(request); + } fcgi_shutdown(); no_headers = 1; - php_output_startup(); - php_output_activate(TSRMLS_C); SG(headers_sent) = 1; php_cgi_usage(argv[0]); - php_end_ob_buffers(1 TSRMLS_CC); + php_output_end_all(TSRMLS_C); exit_status = 0; goto out; } @@ -1837,9 +2095,9 @@ consult the installation file that came with this distribution, or visit \n\ fcgi_impersonate(); } #endif - while (!fastcgi || fcgi_accept_request(&request) >= 0) { - SG(server_context) = (void *) &request; - init_request_info(TSRMLS_C); + while (!fastcgi || fcgi_accept_request(request) >= 0) { + SG(server_context) = fastcgi ? (void *) request : (void *) 1; + init_request_info(request TSRMLS_CC); CG(interactive) = 0; if (!cgi && !fastcgi) { @@ -1895,15 +2153,13 @@ consult the installation file that came with this distribution, or visit \n\ if (script_file) { efree(script_file); } - php_output_startup(); - php_output_activate(TSRMLS_C); SG(headers_sent) = 1; php_printf("[PHP Modules]\n"); print_modules(TSRMLS_C); php_printf("\n[Zend Modules]\n"); print_extensions(TSRMLS_C); php_printf("\n"); - php_end_ob_buffers(1 TSRMLS_CC); + php_output_end_all(TSRMLS_C); fcgi_shutdown(); exit_status = 0; goto out; @@ -2034,7 +2290,7 @@ consult the installation file that came with this distribution, or visit \n\ * get path_translated */ if (php_request_startup(TSRMLS_C) == FAILURE) { if (fastcgi) { - fcgi_finish_request(&request, 1); + fcgi_finish_request(request, 1); } SG(server_context) = NULL; php_module_shutdown(TSRMLS_C); @@ -2087,23 +2343,75 @@ consult the installation file that came with this distribution, or visit \n\ } } - if (CGIG(check_shebang_line) && file_handle.handle.fp && (file_handle.handle.fp != stdin)) { + if (CGIG(check_shebang_line)) { /* #!php support */ - c = fgetc(file_handle.handle.fp); - if (c == '#') { - while (c != '\n' && c != '\r' && c != EOF) { - c = fgetc(file_handle.handle.fp); /* skip to end of line */ - } - /* handle situations where line is terminated by \r\n */ - if (c == '\r') { - if (fgetc(file_handle.handle.fp) != '\n') { - long pos = ftell(file_handle.handle.fp); - fseek(file_handle.handle.fp, pos - 1, SEEK_SET); + switch (file_handle.type) { + case ZEND_HANDLE_FD: + if (file_handle.handle.fd < 0) { + break; } - } - CG(start_lineno) = 2; - } else { - rewind(file_handle.handle.fp); + file_handle.type = ZEND_HANDLE_FP; + file_handle.handle.fp = fdopen(file_handle.handle.fd, "rb"); + /* break missing intentionally */ + case ZEND_HANDLE_FP: + if (!file_handle.handle.fp || + (file_handle.handle.fp == stdin)) { + break; + } + c = fgetc(file_handle.handle.fp); + if (c == '#') { + while (c != '\n' && c != '\r' && c != EOF) { + c = fgetc(file_handle.handle.fp); /* skip to end of line */ + } + /* handle situations where line is terminated by \r\n */ + if (c == '\r') { + if (fgetc(file_handle.handle.fp) != '\n') { + long pos = ftell(file_handle.handle.fp); + fseek(file_handle.handle.fp, pos - 1, SEEK_SET); + } + } + CG(start_lineno) = 2; + } else { + rewind(file_handle.handle.fp); + } + break; + case ZEND_HANDLE_STREAM: + c = php_stream_getc((php_stream*)file_handle.handle.stream.handle); + if (c == '#') { + while (c != '\n' && c != '\r' && c != EOF) { + c = php_stream_getc((php_stream*)file_handle.handle.stream.handle); /* skip to end of line */ + } + /* handle situations where line is terminated by \r\n */ + if (c == '\r') { + if (php_stream_getc((php_stream*)file_handle.handle.stream.handle) != '\n') { + long pos = php_stream_tell((php_stream*)file_handle.handle.stream.handle); + php_stream_seek((php_stream*)file_handle.handle.stream.handle, pos - 1, SEEK_SET); + } + } + CG(start_lineno) = 2; + } else { + php_stream_rewind((php_stream*)file_handle.handle.stream.handle); + } + break; + case ZEND_HANDLE_MAPPED: + if (file_handle.handle.stream.mmap.buf[0] == '#') { + int i = 1; + + c = file_handle.handle.stream.mmap.buf[i++]; + while (c != '\n' && c != '\r' && c != EOF) { + c = file_handle.handle.stream.mmap.buf[i++]; + } + if (c == '\r') { + if (file_handle.handle.stream.mmap.buf[i] == '\n') { + i++; + } + } + file_handle.handle.stream.mmap.buf += i; + file_handle.handle.stream.mmap.len -= i; + } + break; + default: + break; } } @@ -2124,7 +2432,7 @@ consult the installation file that came with this distribution, or visit \n\ if (open_file_for_scanning(&file_handle TSRMLS_CC) == SUCCESS) { zend_strip(TSRMLS_C); zend_file_handle_dtor(&file_handle TSRMLS_CC); - php_end_ob_buffers(1 TSRMLS_CC); + php_output_teardown(); } return SUCCESS; break; @@ -2139,7 +2447,7 @@ consult the installation file that came with this distribution, or visit \n\ goto fastcgi_request_done; } zend_file_handle_dtor(&file_handle TSRMLS_CC); - php_end_ob_buffers(1 TSRMLS_CC); + php_output_teardown(); } return SUCCESS; } @@ -2150,6 +2458,7 @@ consult the installation file that came with this distribution, or visit \n\ open_file_for_scanning(&file_handle TSRMLS_CC); zend_indent(); zend_file_handle_dtor(&file_handle TSRMLS_CC); + php_output_teardown(); return SUCCESS; break; #endif @@ -2187,7 +2496,7 @@ fastcgi_request_done: /* only fastcgi will get here */ requests++; if (max_requests && (requests == max_requests)) { - fcgi_finish_request(&request, 1); + fcgi_finish_request(request, 1); if (bindpath) { free(bindpath); } @@ -2199,6 +2508,9 @@ fastcgi_request_done: } /* end of fastcgi loop */ } + if (request) { + fcgi_destroy_request(request); + } fcgi_shutdown(); if (cgi_sapi_module.php_ini_path_override) { diff --git a/sapi/cgi/config9.m4 b/sapi/cgi/config9.m4 index 5943c632b..392184a0d 100644 --- a/sapi/cgi/config9.m4 +++ b/sapi/cgi/config9.m4 @@ -1,5 +1,5 @@ dnl -dnl $Id: config9.m4 243300 2007-10-01 12:40:54Z jani $ +dnl $Id: config9.m4 305331 2010-11-13 23:13:07Z jani $ dnl PHP_ARG_ENABLE(cgi,, @@ -8,11 +8,9 @@ PHP_ARG_ENABLE(cgi,, dnl dnl CGI setup dnl -if test "$PHP_SAPI" = "default"; then - AC_MSG_CHECKING(whether to build CGI binary) - if test "$PHP_CGI" != "no"; then +AC_MSG_CHECKING(for CGI build) +if test "$PHP_CGI" != "no"; then AC_MSG_RESULT(yes) - AC_MSG_CHECKING([for socklen_t in sys/socket.h]) AC_EGREP_HEADER([socklen_t], [sys/socket.h], [AC_MSG_RESULT([yes]) @@ -50,31 +48,29 @@ if test "$PHP_SAPI" = "default"; then SAPI_CGI_PATH=sapi/cgi/php-cgi ;; esac - PHP_SUBST(SAPI_CGI_PATH) - dnl Set install target and select SAPI - INSTALL_IT="@echo \"Installing PHP CGI binary: \$(INSTALL_ROOT)\$(bindir)/\"; \$(INSTALL) -m 0755 \$(SAPI_CGI_PATH) \$(INSTALL_ROOT)\$(bindir)/\$(program_prefix)php-cgi\$(program_suffix)\$(EXEEXT)" + dnl Select SAPI PHP_SELECT_SAPI(cgi, program, cgi_main.c fastcgi.c,, '$(SAPI_CGI_PATH)') case $host_alias in *aix*) - BUILD_CGI="echo '\#! .' > php.sym && echo >>php.sym && nm -BCpg \`echo \$(PHP_GLOBAL_OBJS) \$(PHP_SAPI_OBJS) | sed 's/\([A-Za-z0-9_]*\)\.lo/\1.o/g'\` | \$(AWK) '{ if (((\$\$2 == \"T\") || (\$\$2 == \"D\") || (\$\$2 == \"B\")) && (substr(\$\$3,1,1) != \".\")) { print \$\$3 } }' | sort -u >> php.sym && \$(LIBTOOL) --mode=link \$(CC) -export-dynamic \$(CFLAGS_CLEAN) \$(EXTRA_CFLAGS) \$(EXTRA_LDFLAGS_PROGRAM) \$(LDFLAGS) -Wl,-brtl -Wl,-bE:php.sym \$(PHP_RPATHS) \$(PHP_GLOBAL_OBJS) \$(PHP_SAPI_OBJS) \$(EXTRA_LIBS) \$(ZEND_EXTRA_LIBS) -o \$(SAPI_CGI_PATH)" + if test "$php_sapi_module" = "shared"; then + BUILD_CGI="echo '\#! .' > php.sym && echo >>php.sym && nm -BCpg \`echo \$(PHP_GLOBAL_OBJS) \$(PHP_BINARY_OBJS) \$(PHP_CGI_OBJS) | sed 's/\([A-Za-z0-9_]*\)\.lo/.libs\/\1.o/g'\` | \$(AWK) '{ if (((\$\$2 == \"T\") || (\$\$2 == \"D\") || (\$\$2 == \"B\")) && (substr(\$\$3,1,1) != \".\")) { print \$\$3 } }' | sort -u >> php.sym && \$(LIBTOOL) --mode=link \$(CC) -export-dynamic \$(CFLAGS_CLEAN) \$(EXTRA_CFLAGS) \$(EXTRA_LDFLAGS_PROGRAM) \$(LDFLAGS) -Wl,-brtl -Wl,-bE:php.sym \$(PHP_RPATHS) \$(PHP_GLOBAL_OBJS) \$(PHP_CGI_OBJS) \$(EXTRA_LIBS) \$(ZEND_EXTRA_LIBS) -o \$(SAPI_CGI_PATH)" + else + BUILD_CGI="echo '\#! .' > php.sym && echo >>php.sym && nm -BCpg \`echo \$(PHP_GLOBAL_OBJS) \$(PHP_BINARY_OBJS) \$(PHP_CGI_OBJS) | sed 's/\([A-Za-z0-9_]*\)\.lo/\1.o/g'\` | \$(AWK) '{ if (((\$\$2 == \"T\") || (\$\$2 == \"D\") || (\$\$2 == \"B\")) && (substr(\$\$3,1,1) != \".\")) { print \$\$3 } }' | sort -u >> php.sym && \$(LIBTOOL) --mode=link \$(CC) -export-dynamic \$(CFLAGS_CLEAN) \$(EXTRA_CFLAGS) \$(EXTRA_LDFLAGS_PROGRAM) \$(LDFLAGS) -Wl,-brtl -Wl,-bE:php.sym \$(PHP_RPATHS) \$(PHP_GLOBAL_OBJS) \$(PHP_CGI_OBJS) \$(EXTRA_LIBS) \$(ZEND_EXTRA_LIBS) -o \$(SAPI_CGI_PATH)" + fi ;; *darwin*) - BUILD_CGI="\$(CC) \$(CFLAGS_CLEAN) \$(EXTRA_CFLAGS) \$(EXTRA_LDFLAGS_PROGRAM) \$(LDFLAGS) \$(NATIVE_RPATHS) \$(PHP_GLOBAL_OBJS:.lo=.o) \$(PHP_SAPI_OBJS:.lo=.o) \$(PHP_FRAMEWORKS) \$(EXTRA_LIBS) \$(ZEND_EXTRA_LIBS) -o \$(SAPI_CGI_PATH)" + BUILD_CGI="\$(CC) \$(CFLAGS_CLEAN) \$(EXTRA_CFLAGS) \$(EXTRA_LDFLAGS_PROGRAM) \$(LDFLAGS) \$(NATIVE_RPATHS) \$(PHP_GLOBAL_OBJS:.lo=.o) \$(PHP_BINARY_OBJS:.lo=.o) \$(PHP_CGI_OBJS:.lo=.o) \$(PHP_FRAMEWORKS) \$(EXTRA_LIBS) \$(ZEND_EXTRA_LIBS) -o \$(SAPI_CGI_PATH)" ;; *) - BUILD_CGI="\$(LIBTOOL) --mode=link \$(CC) -export-dynamic \$(CFLAGS_CLEAN) \$(EXTRA_CFLAGS) \$(EXTRA_LDFLAGS_PROGRAM) \$(LDFLAGS) \$(PHP_RPATHS) \$(PHP_GLOBAL_OBJS) \$(PHP_SAPI_OBJS) \$(EXTRA_LIBS) \$(ZEND_EXTRA_LIBS) -o \$(SAPI_CGI_PATH)" + BUILD_CGI="\$(LIBTOOL) --mode=link \$(CC) -export-dynamic \$(CFLAGS_CLEAN) \$(EXTRA_CFLAGS) \$(EXTRA_LDFLAGS_PROGRAM) \$(LDFLAGS) \$(PHP_RPATHS) \$(PHP_GLOBAL_OBJS) \$(PHP_BINARY_OBJS) \$(PHP_CGI_OBJS) \$(EXTRA_LIBS) \$(ZEND_EXTRA_LIBS) -o \$(SAPI_CGI_PATH)" ;; esac + dnl Expose to Makefile + PHP_SUBST(SAPI_CGI_PATH) PHP_SUBST(BUILD_CGI) - - elif test "$PHP_CLI" != "no"; then - AC_MSG_RESULT(no) - OVERALL_TARGET= - PHP_SAPI=cli - else - AC_MSG_ERROR([No SAPIs selected.]) - fi +else + AC_MSG_RESULT(yes) fi diff --git a/sapi/cgi/fastcgi.c b/sapi/cgi/fastcgi.c index 66628f60c..9d3c5671f 100644 --- a/sapi/cgi/fastcgi.c +++ b/sapi/cgi/fastcgi.c @@ -67,6 +67,7 @@ # include <sys/socket.h> # include <sys/un.h> # include <netinet/in.h> +# include <netinet/tcp.h> # include <arpa/inet.h> # include <netdb.h> # include <signal.h> @@ -140,6 +141,224 @@ static int is_fastcgi = 0; static int in_shutdown = 0; static in_addr_t *allowed_clients = NULL; +/* hash table */ + +#define FCGI_HASH_TABLE_SIZE 128 +#define FCGI_HASH_TABLE_MASK (FCGI_HASH_TABLE_SIZE - 1) +#define FCGI_HASH_SEG_SIZE 4096 + +typedef struct _fcgi_hash_bucket { + unsigned int hash_value; + unsigned int var_len; + char *var; + unsigned int val_len; + char *val; + struct _fcgi_hash_bucket *next; + struct _fcgi_hash_bucket *list_next; +} fcgi_hash_bucket; + +typedef struct _fcgi_hash_buckets { + unsigned int idx; + struct _fcgi_hash_buckets *next; + struct _fcgi_hash_bucket data[FCGI_HASH_TABLE_SIZE]; +} fcgi_hash_buckets; + +typedef struct _fcgi_data_seg { + char *pos; + char *end; + struct _fcgi_data_seg *next; + char data[1]; +} fcgi_data_seg; + +typedef struct _fcgi_hash { + fcgi_hash_bucket *hash_table[FCGI_HASH_TABLE_SIZE]; + fcgi_hash_bucket *list; + fcgi_hash_buckets *buckets; + fcgi_data_seg *data; +} fcgi_hash; + +static void fcgi_hash_init(fcgi_hash *h) +{ + memset(h->hash_table, 0, sizeof(h->hash_table)); + h->list = NULL; + h->buckets = (fcgi_hash_buckets*)malloc(sizeof(fcgi_hash_buckets)); + h->buckets->idx = 0; + h->buckets->next = NULL; + h->data = (fcgi_data_seg*)malloc(sizeof(fcgi_data_seg) - 1 + FCGI_HASH_SEG_SIZE); + h->data->pos = h->data->data; + h->data->end = h->data->pos + FCGI_HASH_SEG_SIZE; + h->data->next = NULL; +} + +static void fcgi_hash_destroy(fcgi_hash *h) +{ + fcgi_hash_buckets *b; + fcgi_data_seg *p; + + b = h->buckets; + while (b) { + fcgi_hash_buckets *q = b; + b = b->next; + free(q); + } + p = h->data; + while (p) { + fcgi_data_seg *q = p; + p = p->next; + free(q); + } +} + +static void fcgi_hash_clean(fcgi_hash *h) +{ + memset(h->hash_table, 0, sizeof(h->hash_table)); + h->list = NULL; + /* delete all bucket blocks except the first one */ + while (h->buckets->next) { + fcgi_hash_buckets *q = h->buckets; + + h->buckets = h->buckets->next; + free(q); + } + h->buckets->idx = 0; + /* delete all data segments except the first one */ + while (h->data->next) { + fcgi_data_seg *q = h->data; + + h->data = h->data->next; + free(q); + } + h->data->pos = h->data->data; +} + +static inline char* fcgi_hash_strndup(fcgi_hash *h, char *str, unsigned int str_len) +{ + char *ret; + + if (UNEXPECTED(h->data->pos + str_len + 1 >= h->data->end)) { + unsigned int seg_size = (str_len + 1 > FCGI_HASH_SEG_SIZE) ? str_len + 1 : FCGI_HASH_SEG_SIZE; + fcgi_data_seg *p = (fcgi_data_seg*)malloc(sizeof(fcgi_data_seg) - 1 + seg_size); + + p->pos = p->data; + p->end = p->pos + seg_size; + p->next = h->data; + h->data = p; + } + ret = h->data->pos; + memcpy(ret, str, str_len); + ret[str_len] = 0; + h->data->pos += str_len + 1; + return ret; +} + +static char* fcgi_hash_set(fcgi_hash *h, unsigned int hash_value, char *var, unsigned int var_len, char *val, unsigned int val_len) +{ + unsigned int idx = hash_value & FCGI_HASH_TABLE_MASK; + fcgi_hash_bucket *p = h->hash_table[idx]; + + while (UNEXPECTED(p != NULL)) { + if (UNEXPECTED(p->hash_value == hash_value) && + p->var_len == var_len && + memcmp(p->var, var, var_len) == 0) { + + p->val_len = val_len; + p->val = fcgi_hash_strndup(h, val, val_len); + return p->val; + } + p = p->next; + } + + if (UNEXPECTED(h->buckets->idx >= FCGI_HASH_TABLE_SIZE)) { + fcgi_hash_buckets *b = (fcgi_hash_buckets*)malloc(sizeof(fcgi_hash_buckets)); + b->idx = 0; + b->next = h->buckets; + h->buckets = b; + } + p = h->buckets->data + h->buckets->idx; + h->buckets->idx++; + p->next = h->hash_table[idx]; + h->hash_table[idx] = p; + p->list_next = h->list; + h->list = p; + p->hash_value = hash_value; + p->var_len = var_len; + p->var = fcgi_hash_strndup(h, var, var_len); + p->val_len = val_len; + p->val = fcgi_hash_strndup(h, val, val_len); + return p->val; +} + +static void fcgi_hash_del(fcgi_hash *h, unsigned int hash_value, char *var, unsigned int var_len) +{ + unsigned int idx = hash_value & FCGI_HASH_TABLE_MASK; + fcgi_hash_bucket **p = &h->hash_table[idx]; + + while (*p != NULL) { + if ((*p)->hash_value == hash_value && + (*p)->var_len == var_len && + memcmp((*p)->var, var, var_len) == 0) { + + (*p)->val = NULL; /* NULL value means deleted */ + (*p)->val_len = 0; + *p = (*p)->next; + return; + } + p = &(*p)->next; + } +} + +static char *fcgi_hash_get(fcgi_hash *h, unsigned int hash_value, char *var, unsigned int var_len, unsigned int *val_len) +{ + unsigned int idx = hash_value & FCGI_HASH_TABLE_MASK; + fcgi_hash_bucket *p = h->hash_table[idx]; + + while (p != NULL) { + if (p->hash_value == hash_value && + p->var_len == var_len && + memcmp(p->var, var, var_len) == 0) { + *val_len = p->val_len; + return p->val; + } + p = p->next; + } + return NULL; +} + +static void fcgi_hash_apply(fcgi_hash *h, fcgi_apply_func func, void *arg TSRMLS_DC) +{ + fcgi_hash_bucket *p = h->list; + + while (p) { + if (EXPECTED(p->val != NULL)) { + func(p->var, p->var_len, p->val, p->val_len, arg TSRMLS_CC); + } + p = p->list_next; + } +} + +struct _fcgi_request { + int listen_socket; + int tcp; + int fd; + int id; + int keep; +#ifdef TCP_NODELAY + int nodelay; +#endif + int closed; + + int in_len; + int in_pad; + + fcgi_header *out_hdr; + unsigned char *out_pos; + unsigned char out_buf[1024*8]; + unsigned char reserved[sizeof(fcgi_end_request_rec)]; + + int has_env; + fcgi_hash env; +}; + #ifdef _WIN32 static DWORD WINAPI fcgi_shutdown_thread(LPVOID arg) @@ -180,6 +399,11 @@ int fcgi_in_shutdown(void) return in_shutdown; } +void fcgi_terminate(void) +{ + in_shutdown = 1; +} + int fcgi_init(void) { if (!is_initialized) { @@ -417,7 +641,7 @@ int fcgi_listen(const char *path, int backlog) 8192, 8192, 0, &saw); if (namedPipe == INVALID_HANDLE_VALUE) { return -1; - } + } listen_socket = _open_osfhandle((long)namedPipe, 0); if (!is_initialized) { fcgi_init(); @@ -459,35 +683,35 @@ int fcgi_listen(const char *path, int backlog) if (!tcp) { chmod(path, 0777); } else { - char *ip = getenv("FCGI_WEB_SERVER_ADDRS"); - char *cur, *end; - int n; - - if (ip) { - ip = strdup(ip); - cur = ip; - n = 0; - while (*cur) { - if (*cur == ',') n++; - cur++; + char *ip = getenv("FCGI_WEB_SERVER_ADDRS"); + char *cur, *end; + int n; + + if (ip) { + ip = strdup(ip); + cur = ip; + n = 0; + while (*cur) { + if (*cur == ',') n++; + cur++; + } + allowed_clients = malloc(sizeof(in_addr_t) * (n+2)); + n = 0; + cur = ip; + while (cur) { + end = strchr(cur, ','); + if (end) { + *end = 0; + end++; } - allowed_clients = malloc(sizeof(in_addr_t) * (n+2)); - n = 0; - cur = ip; - while (cur) { - end = strchr(cur, ','); - if (end) { - *end = 0; - end++; - } - allowed_clients[n] = inet_addr(cur); - if (allowed_clients[n] == INADDR_NONE) { + allowed_clients[n] = inet_addr(cur); + if (allowed_clients[n] == INADDR_NONE) { fprintf(stderr, "Wrong IP address '%s' in FCGI_WEB_SERVER_ADDRS\n", cur); - } - n++; - cur = end; } - allowed_clients[n] = INADDR_NONE; + n++; + cur = end; + } + allowed_clients[n] = INADDR_NONE; free(ip); } } @@ -507,9 +731,9 @@ int fcgi_listen(const char *path, int backlog) return listen_socket; } -void fcgi_init_request(fcgi_request *req, int listen_socket) +fcgi_request *fcgi_init_request(int listen_socket) { - memset(req, 0, sizeof(fcgi_request)); + fcgi_request *req = (fcgi_request*)calloc(1, sizeof(fcgi_request)); req->listen_socket = listen_socket; req->fd = -1; req->id = -1; @@ -523,6 +747,20 @@ void fcgi_init_request(fcgi_request *req, int listen_socket) #ifdef _WIN32 req->tcp = !GetNamedPipeInfo((HANDLE)_get_osfhandle(req->listen_socket), NULL, NULL, NULL, NULL); #endif + +#ifdef TCP_NODELAY + req->nodelay = 0; +#endif + + fcgi_hash_init(&req->env); + + return req; +} + +void fcgi_destroy_request(fcgi_request *req) +{ + fcgi_hash_destroy(&req->env); + free(req); } static inline ssize_t safe_write(fcgi_request *req, const void *buf, size_t count) @@ -603,64 +841,34 @@ static inline int fcgi_make_header(fcgi_header *hdr, fcgi_request_type type, int static int fcgi_get_params(fcgi_request *req, unsigned char *p, unsigned char *end) { - char buf[128]; - char *tmp = buf; - size_t buf_size = sizeof(buf); unsigned int name_len, val_len; - char *s; - int ret = 1; while (p < end) { name_len = *p++; - if (name_len >= 128) { - if (p + 3 >= end) { - ret = 0; - break; - } + if (UNEXPECTED(name_len >= 128)) { + if (UNEXPECTED(p + 3 >= end)) return 0; name_len = ((name_len & 0x7f) << 24); name_len |= (*p++ << 16); name_len |= (*p++ << 8); name_len |= *p++; } - if (p >= end) { - ret = 0; - break; - } + if (UNEXPECTED(p >= end)) return 0; val_len = *p++; - if (val_len >= 128) { - if (p + 3 >= end) { - ret = 0; - break; - } + if (UNEXPECTED(val_len >= 128)) { + if (UNEXPECTED(p + 3 >= end)) return 0; val_len = ((val_len & 0x7f) << 24); val_len |= (*p++ << 16); val_len |= (*p++ << 8); val_len |= *p++; } - if (name_len + val_len > end - p) { + if (UNEXPECTED(name_len + val_len > (unsigned int) (end - p))) { /* Malformated request */ - ret = 0; - break; - } - if (name_len+1 >= buf_size) { - buf_size = name_len + 64; - tmp = (tmp == buf ? emalloc(buf_size): erealloc(tmp, buf_size)); + return 0; } - memcpy(tmp, p, name_len); - tmp[name_len] = 0; - s = estrndup((char*)p + name_len, val_len); - zend_hash_update(req->env, tmp, name_len+1, &s, sizeof(char*), NULL); + fcgi_hash_set(&req->env, FCGI_HASH_FUNC(p, name_len), (char*)p, name_len, (char*)p + name_len, val_len); p += name_len + val_len; } - if (tmp != buf && tmp != NULL) { - efree(tmp); - } - return ret; -} - -static void fcgi_free_var(char **s) -{ - efree(*s); + return 1; } static int fcgi_read_request(fcgi_request *req) @@ -674,8 +882,7 @@ static int fcgi_read_request(fcgi_request *req) req->in_len = 0; req->out_hdr = NULL; req->out_pos = req->out_buf; - ALLOC_HASHTABLE(req->env); - zend_hash_init(req->env, 0, NULL, (void (*)(void *)) fcgi_free_var, 0); + req->has_env = 1; if (safe_read(req, &hdr, sizeof(fcgi_header)) != sizeof(fcgi_header) || hdr.version < FCGI_VERSION_1) { @@ -702,25 +909,32 @@ static int fcgi_read_request(fcgi_request *req) req->id = (hdr.requestIdB1 << 8) + hdr.requestIdB0; if (hdr.type == FCGI_BEGIN_REQUEST && len == sizeof(fcgi_begin_request)) { - char *val; - if (safe_read(req, buf, len+padding) != len+padding) { return 0; } req->keep = (((fcgi_begin_request*)buf)->flags & FCGI_KEEP_CONN); +#ifdef TCP_NODELAY + if (req->keep && req->tcp && !req->nodelay) { +# ifdef _WIN32 + BOOL on = 1; +# else + int on = 1; +# endif + + setsockopt(req->fd, IPPROTO_TCP, TCP_NODELAY, (char*)&on, sizeof(on)); + req->nodelay = 1; + } +#endif switch ((((fcgi_begin_request*)buf)->roleB1 << 8) + ((fcgi_begin_request*)buf)->roleB0) { case FCGI_RESPONDER: - val = estrdup("RESPONDER"); - zend_hash_update(req->env, "FCGI_ROLE", sizeof("FCGI_ROLE"), &val, sizeof(char*), NULL); + fcgi_hash_set(&req->env, FCGI_HASH_FUNC("FCGI_ROLE", sizeof("FCGI_ROLE")-1), "FCGI_ROLE", sizeof("FCGI_ROLE")-1, "RESPONDER", sizeof("RESPONDER")-1); break; case FCGI_AUTHORIZER: - val = estrdup("AUTHORIZER"); - zend_hash_update(req->env, "FCGI_ROLE", sizeof("FCGI_ROLE"), &val, sizeof(char*), NULL); + fcgi_hash_set(&req->env, FCGI_HASH_FUNC("FCGI_ROLE", sizeof("FCGI_ROLE")-1), "FCGI_ROLE", sizeof("FCGI_ROLE")-1, "AUTHORIZER", sizeof("AUTHORIZER")-1); break; case FCGI_FILTER: - val = estrdup("FILTER"); - zend_hash_update(req->env, "FCGI_ROLE", sizeof("FCGI_ROLE"), &val, sizeof(char*), NULL); + fcgi_hash_set(&req->env, FCGI_HASH_FUNC("FCGI_ROLE", sizeof("FCGI_ROLE")-1), "FCGI_ROLE", sizeof("FCGI_ROLE")-1, "FILTER", sizeof("FILTER")-1); break; default: return 0; @@ -759,12 +973,9 @@ static int fcgi_read_request(fcgi_request *req) } } else if (hdr.type == FCGI_GET_VALUES) { unsigned char *p = buf + sizeof(fcgi_header); - HashPosition pos; - char * str_index; - uint str_length; - ulong num_index; - int key_type; zval ** value; + unsigned int zlen; + fcgi_hash_bucket *q; if (safe_read(req, buf, len+padding) != len+padding) { req->keep = 0; @@ -776,28 +987,22 @@ static int fcgi_read_request(fcgi_request *req) return 0; } - zend_hash_internal_pointer_reset_ex(req->env, &pos); - while ((key_type = zend_hash_get_current_key_ex(req->env, &str_index, &str_length, &num_index, 0, &pos)) != HASH_KEY_NON_EXISTANT) { - int zlen; - zend_hash_move_forward_ex(req->env, &pos); - if (key_type != HASH_KEY_IS_STRING) { - continue; - } - if (zend_hash_find(&fcgi_mgmt_vars, str_index, str_length, (void**) &value) != SUCCESS) { + q = req->env.list; + while (q != NULL) { + if (zend_hash_find(&fcgi_mgmt_vars, q->var, q->var_len, (void**) &value) != SUCCESS) { continue; } - --str_length; zlen = Z_STRLEN_PP(value); - if ((p + 4 + 4 + str_length + zlen) >= (buf + sizeof(buf))) { + if ((p + 4 + 4 + q->var_len + zlen) >= (buf + sizeof(buf))) { break; } - if (str_length < 0x80) { - *p++ = str_length; + if (q->var_len < 0x80) { + *p++ = q->var_len; } else { - *p++ = ((str_length >> 24) & 0xff) | 0x80; - *p++ = (str_length >> 16) & 0xff; - *p++ = (str_length >> 8) & 0xff; - *p++ = str_length & 0xff; + *p++ = ((q->var_len >> 24) & 0xff) | 0x80; + *p++ = (q->var_len >> 16) & 0xff; + *p++ = (q->var_len >> 8) & 0xff; + *p++ = q->var_len & 0xff; } if (zlen < 0x80) { *p++ = zlen; @@ -807,8 +1012,8 @@ static int fcgi_read_request(fcgi_request *req) *p++ = (zlen >> 8) & 0xff; *p++ = zlen & 0xff; } - memcpy(p, str_index, str_length); - p += str_length; + memcpy(p, q->var, q->var_len); + p += q->var_len; memcpy(p, Z_STRVAL_PP(value), zlen); p += zlen; } @@ -881,10 +1086,9 @@ int fcgi_read(fcgi_request *req, char *str, int len) static inline void fcgi_close(fcgi_request *req, int force, int destroy) { - if (destroy && req->env) { - zend_hash_destroy(req->env); - FREE_HASHTABLE(req->env); - req->env = NULL; + if (destroy && req->has_env) { + fcgi_hash_clean(&req->env); + req->has_env = 0; } #ifdef _WIN32 @@ -904,22 +1108,27 @@ static inline void fcgi_close(fcgi_request *req, int force, int destroy) DisconnectNamedPipe(pipe); } else { if (!force) { - char buf[8]; + fcgi_header buf; shutdown(req->fd, 1); - while (recv(req->fd, buf, sizeof(buf), 0) > 0) {} + /* read the last FCGI_STDIN header (it may be omitted) */ + recv(req->fd, &buf, sizeof(buf), 0); } closesocket(req->fd); } #else if (!force) { - char buf[8]; + fcgi_header buf; shutdown(req->fd, 1); - while (recv(req->fd, buf, sizeof(buf), 0) > 0) {} + /* read the last FCGI_STDIN header (it may be omitted) */ + recv(req->fd, &buf, sizeof(buf), 0); } close(req->fd); #endif +#ifdef TCP_NODELAY + req->nodelay = 0; +#endif req->fd = -1; } } @@ -970,22 +1179,33 @@ int fcgi_accept_request(fcgi_request *req) FCGI_LOCK(req->listen_socket); req->fd = accept(listen_socket, (struct sockaddr *)&sa, &len); FCGI_UNLOCK(req->listen_socket); - if (req->fd >= 0 && allowed_clients) { - int n = 0; - int allowed = 0; - - while (allowed_clients[n] != INADDR_NONE) { - if (allowed_clients[n] == sa.sa_inet.sin_addr.s_addr) { - allowed = 1; - break; + if (req->fd >= 0) { + if (((struct sockaddr *)&sa)->sa_family == AF_INET) { +#ifndef _WIN32 + req->tcp = 1; +#endif + if (allowed_clients) { + int n = 0; + int allowed = 0; + + while (allowed_clients[n] != INADDR_NONE) { + if (allowed_clients[n] == sa.sa_inet.sin_addr.s_addr) { + allowed = 1; + break; + } + n++; + } + if (!allowed) { + fprintf(stderr, "Connection from disallowed IP address '%s' is dropped.\n", inet_ntoa(sa.sa_inet.sin_addr)); + closesocket(req->fd); + req->fd = -1; + continue; } - n++; } - if (!allowed) { - fprintf(stderr, "Connection from disallowed IP address '%s' is dropped.\n", inet_ntoa(sa.sa_inet.sin_addr)); - closesocket(req->fd); - req->fd = -1; - continue; +#ifndef _WIN32 + } else { + req->tcp = 0; +#endif } } } @@ -1195,8 +1415,8 @@ int fcgi_write(fcgi_request *req, fcgi_request_type type, const char *str, int l return -1; } pos += 0xfff8; - } - + } + pad = (((len - pos) + 7) & ~7) - (len - pos); rest = pad ? 8 - pad : 0; @@ -1236,31 +1456,44 @@ int fcgi_finish_request(fcgi_request *req, int force_close) char* fcgi_getenv(fcgi_request *req, const char* var, int var_len) { - char **val; + unsigned int val_len; if (!req) return NULL; - if (zend_hash_find(req->env, (char*)var, var_len+1, (void**)&val) == SUCCESS) { - return *val; - } - return NULL; + return fcgi_hash_get(&req->env, FCGI_HASH_FUNC(var, var_len), (char*)var, var_len, &val_len); +} + +char* fcgi_quick_getenv(fcgi_request *req, const char* var, int var_len, unsigned int hash_value) +{ + unsigned int val_len; + + return fcgi_hash_get(&req->env, hash_value, (char*)var, var_len, &val_len); } char* fcgi_putenv(fcgi_request *req, char* var, int var_len, char* val) { - if (var && req) { - if (val == NULL) { - zend_hash_del(req->env, var, var_len+1); - } else { - char **ret; + if (!req) return NULL; + if (val == NULL) { + fcgi_hash_del(&req->env, FCGI_HASH_FUNC(var, var_len), var, var_len); + return NULL; + } else { + return fcgi_hash_set(&req->env, FCGI_HASH_FUNC(var, var_len), var, var_len, val, strlen(val)); + } +} - val = estrdup(val); - if (zend_hash_update(req->env, var, var_len+1, &val, sizeof(char*), (void**)&ret) == SUCCESS) { - return *ret; - } - } +char* fcgi_quick_putenv(fcgi_request *req, char* var, int var_len, unsigned int hash_value, char* val) +{ + if (val == NULL) { + fcgi_hash_del(&req->env, hash_value, var, var_len); + return NULL; + } else { + return fcgi_hash_set(&req->env, hash_value, var, var_len, val, strlen(val)); } - return NULL; +} + +void fcgi_loadenv(fcgi_request *req, fcgi_apply_func func, zval *array TSRMLS_DC) +{ + fcgi_hash_apply(&req->env, func, array TSRMLS_CC); } #ifdef _WIN32 @@ -1282,7 +1515,7 @@ void fcgi_set_mgmt_var(const char * name, size_t name_len, const char * value, s Z_TYPE_P(zvalue) = IS_STRING; Z_STRVAL_P(zvalue) = pestrndup(value, value_len, 1); Z_STRLEN_P(zvalue) = value_len; - zend_hash_add(&fcgi_mgmt_vars, name, name_len + 1, &zvalue, sizeof(zvalue), NULL); + zend_hash_add(&fcgi_mgmt_vars, name, name_len, &zvalue, sizeof(zvalue), NULL); } void fcgi_free_mgmt_var_cb(void * ptr) diff --git a/sapi/cgi/fastcgi.h b/sapi/cgi/fastcgi.h index 9b42f07a0..db0922a77 100644 --- a/sapi/cgi/fastcgi.h +++ b/sapi/cgi/fastcgi.h @@ -26,6 +26,23 @@ #define FCGI_KEEP_CONN 1 +/* this is near the perfect hash function for most useful FastCGI variables + * which combines efficiency and minimal hash collisions + */ + +#define FCGI_HASH_FUNC(var, var_len) \ + (UNEXPECTED(var_len < 3) ? var_len : \ + (((unsigned int)var[3]) << 2) + \ + (((unsigned int)var[var_len-2]) << 4) + \ + (((unsigned int)var[var_len-1]) << 2) + \ + var_len) + +#define FCGI_GETENV(request, name) \ + fcgi_quick_getenv(request, name, sizeof(name)-1, FCGI_HASH_FUNC(name, sizeof(name)-1)) + +#define FCGI_PUTENV(request, name, value) \ + fcgi_quick_putenv(request, name, sizeof(name)-1, FCGI_HASH_FUNC(name, sizeof(name)-1), value) + typedef enum _fcgi_role { FCGI_RESPONDER = 1, FCGI_AUTHORIZER = 2, @@ -91,38 +108,26 @@ typedef struct _fcgi_end_request_rec { /* FastCGI client API */ -typedef struct _fcgi_request { - int listen_socket; -#ifdef _WIN32 - int tcp; -#endif - int fd; - int id; - int keep; - int closed; - - int in_len; - int in_pad; - - fcgi_header *out_hdr; - unsigned char *out_pos; - unsigned char out_buf[1024*8]; - unsigned char reserved[sizeof(fcgi_end_request_rec)]; +typedef void (*fcgi_apply_func)(char *var, unsigned int var_len, char *val, unsigned int val_len, void *arg TSRMLS_DC); - HashTable *env; -} fcgi_request; +typedef struct _fcgi_request fcgi_request; int fcgi_init(void); void fcgi_shutdown(void); int fcgi_is_fastcgi(void); int fcgi_in_shutdown(void); +void fcgi_terminate(void); int fcgi_listen(const char *path, int backlog); -void fcgi_init_request(fcgi_request *req, int listen_socket); +fcgi_request* fcgi_init_request(int listen_socket); +void fcgi_destroy_request(fcgi_request *req); int fcgi_accept_request(fcgi_request *req); int fcgi_finish_request(fcgi_request *req, int force_close); char* fcgi_getenv(fcgi_request *req, const char* var, int var_len); char* fcgi_putenv(fcgi_request *req, char* var, int var_len, char* val); +char* fcgi_quick_getenv(fcgi_request *req, const char* var, int var_len, unsigned int hash_value); +char* fcgi_quick_putenv(fcgi_request *req, char* var, int var_len, unsigned int hash_value, char* val); +void fcgi_loadenv(fcgi_request *req, fcgi_apply_func load_func, zval *array TSRMLS_DC); int fcgi_read(fcgi_request *req, char *str, int len); diff --git a/sapi/cli/Makefile.frag b/sapi/cli/Makefile.frag index 6903ca1fc..8f4f40040 100644 --- a/sapi/cli/Makefile.frag +++ b/sapi/cli/Makefile.frag @@ -1,11 +1,13 @@ cli: $(SAPI_CLI_PATH) -$(SAPI_CLI_PATH): $(PHP_GLOBAL_OBJS) $(PHP_CLI_OBJS) +$(SAPI_CLI_PATH): $(PHP_GLOBAL_OBJS) $(PHP_BINARY_OBJS) $(PHP_CLI_OBJS) $(BUILD_CLI) install-cli: $(SAPI_CLI_PATH) @echo "Installing PHP CLI binary: $(INSTALL_ROOT)$(bindir)/" - @$(INSTALL_CLI) + @$(mkinstalldirs) $(INSTALL_ROOT)$(bindir) + @$(INSTALL) -m 0755 $(SAPI_CLI_PATH) $(INSTALL_ROOT)$(bindir)/$(program_prefix)php$(program_suffix)$(EXEEXT) @echo "Installing PHP CLI man page: $(INSTALL_ROOT)$(mandir)/man1/" @$(mkinstalldirs) $(INSTALL_ROOT)$(mandir)/man1 - @$(INSTALL_DATA) $(builddir)/php.1 $(INSTALL_ROOT)$(mandir)/man1/$(program_prefix)php$(program_suffix).1 + @$(INSTALL_DATA) sapi/cli/php.1 $(INSTALL_ROOT)$(mandir)/man1/$(program_prefix)php$(program_suffix).1 + diff --git a/sapi/cli/README b/sapi/cli/README index 9e519e9bd..8720250f6 100644 --- a/sapi/cli/README +++ b/sapi/cli/README @@ -11,8 +11,8 @@ The main differences between the two: * It does not change the working directory to that of the script. (-C switch kept for compatibility) * Plain text error message -* $argc and $argv registered irrespective of register_globals - and register_argc_argv php.ini settings. +* $argc and $argv registered irrespective of the register_argc_argv + php.ini setting. * implicit_flush always on * -r option which allows execution of PHP code directly from the command line (e.g. php -r 'echo md5("test");' ) diff --git a/sapi/cli/cli.h b/sapi/cli/cli.h new file mode 100644 index 000000000..56fa9dbe2 --- /dev/null +++ b/sapi/cli/cli.h @@ -0,0 +1,52 @@ +/* + +----------------------------------------------------------------------+ + | PHP Version 5 | + +----------------------------------------------------------------------+ + | Copyright (c) 1997-2010 The PHP Group | + +----------------------------------------------------------------------+ + | This source file is subject to version 3.01 of the PHP license, | + | that is bundled with this package in the file LICENSE, and is | + | available through the world-wide-web at the following url: | + | http://www.php.net/license/3_01.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. | + +----------------------------------------------------------------------+ + | Author: Johannes Schlueter <johannes@php.net> | + +----------------------------------------------------------------------+ +*/ + +/* $Id: cli.h 308482 2011-02-19 16:04:30Z johannes $ */ + +#ifndef CLI_H +#define CLI_H + +#ifdef PHP_WIN32 +# define PHP_CLI_API __declspec(dllexport) +#elif defined(__GNUC__) && __GNUC__ >= 4 +# define PHP_CLI_API __attribute__ ((visibility("default"))) +#else +# define PHP_CLI_API +#endif + + +extern PHP_CLI_API size_t sapi_cli_single_write(const char *str, uint str_length TSRMLS_DC); + +typedef struct { + size_t (*cli_shell_write)(const char *str, uint str_length TSRMLS_DC); + int (*cli_shell_ub_write)(const char *str, uint str_length TSRMLS_DC); + int (*cli_shell_run)(TSRMLS_D); +} cli_shell_callbacks_t; + +extern PHP_CLI_API cli_shell_callbacks_t *php_cli_get_shell_callbacks(); + +#endif /* CLI_H */ + +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * End: + * vim600: noet sw=4 ts=4 fdm=marker + * vim<600: noet sw=4 ts=4 + */ diff --git a/sapi/cli/config.m4 b/sapi/cli/config.m4 index 882cab4ba..3eeeab60a 100644 --- a/sapi/cli/config.m4 +++ b/sapi/cli/config.m4 @@ -1,5 +1,5 @@ dnl -dnl $Id: config.m4 265790 2008-09-01 13:15:31Z dmitry $ +dnl $Id: config.m4 312344 2011-06-20 20:27:39Z moriyoshi $ dnl PHP_ARG_ENABLE(cli,, @@ -8,32 +8,41 @@ PHP_ARG_ENABLE(cli,, AC_MSG_CHECKING(for CLI build) if test "$PHP_CLI" != "no"; then - PHP_ADD_MAKEFILE_FRAGMENT($abs_srcdir/sapi/cli/Makefile.frag,$abs_srcdir/sapi/cli,sapi/cli) + PHP_ADD_MAKEFILE_FRAGMENT($abs_srcdir/sapi/cli/Makefile.frag) + + dnl Set filename SAPI_CLI_PATH=sapi/cli/php - PHP_SUBST(SAPI_CLI_PATH) + + dnl Select SAPI + PHP_SELECT_SAPI(cli, program, php_cli.c php_http_parser.c php_cli_server.c,, '$(SAPI_CLI_PATH)') case $host_alias in *aix*) - if test "$php_build_target" = "shared"; then - BUILD_CLI="echo '\#! .' > php.sym && echo >>php.sym && nm -BCpg \`echo \$(PHP_GLOBAL_OBJS) \$(PHP_CLI_OBJS) | sed 's/\([A-Za-z0-9_]*\)\.lo/.libs\/\1.o/g'\` | \$(AWK) '{ if (((\$\$2 == \"T\") || (\$\$2 == \"D\") || (\$\$2 == \"B\")) && (substr(\$\$3,1,1) != \".\")) { print \$\$3 } }' | sort -u >> php.sym && \$(LIBTOOL) --mode=link \$(CC) -export-dynamic \$(CFLAGS_CLEAN) \$(EXTRA_CFLAGS) \$(EXTRA_LDFLAGS_PROGRAM) \$(LDFLAGS) -Wl,-brtl -Wl,-bE:php.sym \$(PHP_RPATHS) \$(PHP_GLOBAL_OBJS) \$(PHP_CLI_OBJS) \$(EXTRA_LIBS) \$(ZEND_EXTRA_LIBS) -o \$(SAPI_CLI_PATH)" + if test "$php_sapi_module" = "shared"; then + BUILD_CLI="echo '\#! .' > php.sym && echo >>php.sym && nm -BCpg \`echo \$(PHP_GLOBAL_OBJS) \$(PHP_BINARY_OBJS) \$(PHP_CLI_OBJS) | sed 's/\([A-Za-z0-9_]*\)\.lo/.libs\/\1.o/g'\` | \$(AWK) '{ if (((\$\$2 == \"T\") || (\$\$2 == \"D\") || (\$\$2 == \"B\")) && (substr(\$\$3,1,1) != \".\")) { print \$\$3 } }' | sort -u >> php.sym && \$(LIBTOOL) --mode=link \$(CC) -export-dynamic \$(CFLAGS_CLEAN) \$(EXTRA_CFLAGS) \$(EXTRA_LDFLAGS_PROGRAM) \$(LDFLAGS) -Wl,-brtl -Wl,-bE:php.sym \$(PHP_RPATHS) \$(PHP_GLOBAL_OBJS) \$(PHP_CLI_OBJS) \$(EXTRA_LIBS) \$(ZEND_EXTRA_LIBS) -o \$(SAPI_CLI_PATH)" else - BUILD_CLI="echo '\#! .' > php.sym && echo >>php.sym && nm -BCpg \`echo \$(PHP_GLOBAL_OBJS) \$(PHP_CLI_OBJS) | sed 's/\([A-Za-z0-9_]*\)\.lo/\1.o/g'\` | \$(AWK) '{ if (((\$\$2 == \"T\") || (\$\$2 == \"D\") || (\$\$2 == \"B\")) && (substr(\$\$3,1,1) != \".\")) { print \$\$3 } }' | sort -u >> php.sym && \$(LIBTOOL) --mode=link \$(CC) -export-dynamic \$(CFLAGS_CLEAN) \$(EXTRA_CFLAGS) \$(EXTRA_LDFLAGS_PROGRAM) \$(LDFLAGS) -Wl,-brtl -Wl,-bE:php.sym \$(PHP_RPATHS) \$(PHP_GLOBAL_OBJS) \$(PHP_CLI_OBJS) \$(EXTRA_LIBS) \$(ZEND_EXTRA_LIBS) -o \$(SAPI_CLI_PATH)" + BUILD_CLI="echo '\#! .' > php.sym && echo >>php.sym && nm -BCpg \`echo \$(PHP_GLOBAL_OBJS) \$(PHP_BINARY_OBJS) \$(PHP_CLI_OBJS) | sed 's/\([A-Za-z0-9_]*\)\.lo/\1.o/g'\` | \$(AWK) '{ if (((\$\$2 == \"T\") || (\$\$2 == \"D\") || (\$\$2 == \"B\")) && (substr(\$\$3,1,1) != \".\")) { print \$\$3 } }' | sort -u >> php.sym && \$(LIBTOOL) --mode=link \$(CC) -export-dynamic \$(CFLAGS_CLEAN) \$(EXTRA_CFLAGS) \$(EXTRA_LDFLAGS_PROGRAM) \$(LDFLAGS) -Wl,-brtl -Wl,-bE:php.sym \$(PHP_RPATHS) \$(PHP_GLOBAL_OBJS) \$(PHP_CLI_OBJS) \$(EXTRA_LIBS) \$(ZEND_EXTRA_LIBS) -o \$(SAPI_CLI_PATH)" fi ;; *darwin*) - BUILD_CLI="\$(CC) \$(CFLAGS_CLEAN) \$(EXTRA_CFLAGS) \$(EXTRA_LDFLAGS_PROGRAM) \$(LDFLAGS) \$(NATIVE_RPATHS) \$(PHP_GLOBAL_OBJS:.lo=.o) \$(PHP_CLI_OBJS:.lo=.o) \$(PHP_FRAMEWORKS) \$(EXTRA_LIBS) \$(ZEND_EXTRA_LIBS) -o \$(SAPI_CLI_PATH)" + BUILD_CLI="\$(CC) \$(CFLAGS_CLEAN) \$(EXTRA_CFLAGS) \$(EXTRA_LDFLAGS_PROGRAM) \$(LDFLAGS) \$(NATIVE_RPATHS) \$(PHP_GLOBAL_OBJS:.lo=.o) \$(PHP_BINARY_OBJS:.lo=.o) \$(PHP_CLI_OBJS:.lo=.o) \$(PHP_FRAMEWORKS) \$(EXTRA_LIBS) \$(ZEND_EXTRA_LIBS) -o \$(SAPI_CLI_PATH)" ;; *netware*) - BUILD_CLI="\$(LIBTOOL) --mode=link \$(CC) -export-dynamic \$(CFLAGS_CLEAN) \$(EXTRA_CFLAGS) \$(EXTRA_LDFLAGS_PROGRAM) \$(LDFLAGS) \$(PHP_RPATHS) \$(PHP_CLI_OBJS) \$(EXTRA_LIBS) \$(ZEND_EXTRA_LIBS) -Lnetware -lphp5lib -o \$(SAPI_CLI_PATH)" + BUILD_CLI="\$(LIBTOOL) --mode=link \$(CC) -export-dynamic \$(CFLAGS_CLEAN) \$(EXTRA_CFLAGS) \$(EXTRA_LDFLAGS_PROGRAM) \$(LDFLAGS) \$(PHP_RPATHS) \$(PHP_BINARY_OBJS) \$(PHP_CLI_OBJS) \$(EXTRA_LIBS) \$(ZEND_EXTRA_LIBS) -Lnetware -lphp5lib -o \$(SAPI_CLI_PATH)" ;; *) - BUILD_CLI="\$(LIBTOOL) --mode=link \$(CC) -export-dynamic \$(CFLAGS_CLEAN) \$(EXTRA_CFLAGS) \$(EXTRA_LDFLAGS_PROGRAM) \$(LDFLAGS) \$(PHP_RPATHS) \$(PHP_GLOBAL_OBJS) \$(PHP_CLI_OBJS) \$(EXTRA_LIBS) \$(ZEND_EXTRA_LIBS) -o \$(SAPI_CLI_PATH)" + BUILD_CLI="\$(LIBTOOL) --mode=link \$(CC) -export-dynamic \$(CFLAGS_CLEAN) \$(EXTRA_CFLAGS) \$(EXTRA_LDFLAGS_PROGRAM) \$(LDFLAGS) \$(PHP_RPATHS) \$(PHP_GLOBAL_OBJS) \$(PHP_BINARY_OBJS) \$(PHP_CLI_OBJS) \$(EXTRA_LIBS) \$(ZEND_EXTRA_LIBS) -o \$(SAPI_CLI_PATH)" ;; esac - INSTALL_CLI="\$(mkinstalldirs) \$(INSTALL_ROOT)\$(bindir); \$(INSTALL) -m 0755 \$(SAPI_CLI_PATH) \$(INSTALL_ROOT)\$(bindir)/\$(program_prefix)php\$(program_suffix)\$(EXEEXT)" + dnl Set executable for tests + PHP_EXECUTABLE="\$(top_builddir)/\$(SAPI_CLI_PATH)" + PHP_SUBST(PHP_EXECUTABLE) + + dnl Expose to Makefile + PHP_SUBST(SAPI_CLI_PATH) PHP_SUBST(BUILD_CLI) - PHP_SUBST(INSTALL_CLI) + PHP_OUTPUT(sapi/cli/php.1) fi AC_MSG_RESULT($PHP_CLI) diff --git a/sapi/cli/config.w32 b/sapi/cli/config.w32 index 1a210ab68..91218f1d1 100644 --- a/sapi/cli/config.w32 +++ b/sapi/cli/config.w32 @@ -1,12 +1,13 @@ // vim:ft=javascript -// $Id: config.w32 302123 2010-08-11 22:38:15Z kalle $ +// $Id: config.w32 312344 2011-06-20 20:27:39Z moriyoshi $ ARG_ENABLE('cli', 'Build CLI version of PHP', 'yes'); ARG_ENABLE('crt-debug', 'Enable CRT memory dumps for debugging sent to STDERR', 'no'); ARG_ENABLE('cli-win32', 'Build console-less CLI version of PHP', 'no'); if (PHP_CLI == "yes") { - SAPI('cli', 'php_cli.c php_cli_readline.c', 'php.exe'); + SAPI('cli', 'php_cli.c php_http_parser.c php_cli_server.c', 'php.exe'); + ADD_FLAG("LIBS_CLI", "ws2_32.lib"); if (PHP_CRT_DEBUG == "yes") { ADD_FLAG("CFLAGS_CLI", "/D PHP_WIN32_DEBUG_HEAP"); } @@ -14,7 +15,7 @@ if (PHP_CLI == "yes") { } if (PHP_CLI_WIN32 == "yes") { - SAPI('cli_win32', 'cli_win32.c php_cli_readline.c', 'php-win.exe'); + SAPI('cli_win32', 'cli_win32.c', 'php-win.exe'); ADD_FLAG("LDFLAGS_CLI_WIN32", "/stack:8388608"); } diff --git a/sapi/cli/php.1.in b/sapi/cli/php.1.in index c1fcb27e5..186b128f8 100644 --- a/sapi/cli/php.1.in +++ b/sapi/cli/php.1.in @@ -323,6 +323,16 @@ Shows information about extension .B name .TP .PD 0 +.B \-\-rzendextension +.IR name +.TP +.PD 1 +.B \-\-rz +.IR name +Shows information about Zend extension +.B name +.TP +.PD 0 .B \-\-rextinfo .IR name .TP diff --git a/sapi/cli/php_cli.c b/sapi/cli/php_cli.c index 3fbce7476..429ff5512 100644 --- a/sapi/cli/php_cli.c +++ b/sapi/cli/php_cli.c @@ -59,6 +59,7 @@ #include "php_main.h" #include "fopen_wrappers.h" #include "ext/standard/php_standard.h" +#include "cli.h" #ifdef PHP_WIN32 #include <io.h> #include <fcntl.h> @@ -73,16 +74,6 @@ #include <unixlib/local.h> #endif -#if (HAVE_LIBREADLINE || HAVE_LIBEDIT) && !defined(COMPILE_DL_READLINE) -#if HAVE_LIBEDIT -#include <editline/readline.h> -#else -#include <readline/readline.h> -#include <readline/history.h> -#endif -#include "php_cli_readline.h" -#endif /* HAVE_LIBREADLINE || HAVE_LIBEDIT */ - #include "zend_compile.h" #include "zend_execute.h" #include "zend_highlight.h" @@ -91,6 +82,10 @@ #include "php_getopt.h" +#ifndef PHP_CLI_WIN32_NO_CONSOLE +#include "php_cli_server.h" +#endif + #ifndef PHP_WIN32 # define php_select(m, r, w, e, t) select(m, r, w, e, t) #else @@ -116,7 +111,14 @@ PHPAPI extern char *php_ini_scanned_files; #define PHP_MODE_REFLECTION_CLASS 9 #define PHP_MODE_REFLECTION_EXTENSION 10 #define PHP_MODE_REFLECTION_EXT_INFO 11 -#define PHP_MODE_SHOW_INI_CONFIG 12 +#define PHP_MODE_REFLECTION_ZEND_EXTENSION 12 +#define PHP_MODE_SHOW_INI_CONFIG 13 + +cli_shell_callbacks_t cli_shell_callbacks = { NULL, NULL, NULL }; +PHP_CLI_API cli_shell_callbacks_t *php_cli_get_shell_callbacks() +{ + return &cli_shell_callbacks; +} const char HARDCODED_INI[] = "html_errors=0\n" @@ -126,13 +128,8 @@ const char HARDCODED_INI[] = "max_execution_time=0\n" "max_input_time=-1\n\0"; -static char *php_optarg = NULL; -static int php_optind = 1; -#if (HAVE_LIBREADLINE || HAVE_LIBEDIT) && !defined(COMPILE_DL_READLINE) -static char php_last_char = '\0'; -#endif -static const opt_struct OPTIONS[] = { +const opt_struct OPTIONS[] = { {'a', 0, "interactive"}, {'B', 1, "process-begin"}, {'C', 0, "no-chdir"}, /* for compatibility with CGI (do not chdir to script directory) */ @@ -153,6 +150,8 @@ static const opt_struct OPTIONS[] = { {'r', 1, "run"}, {'s', 0, "syntax-highlight"}, {'s', 0, "syntax-highlighting"}, + {'S', 1, "server"}, + {'t', 1, "docroot"}, {'w', 0, "strip"}, {'?', 0, "usage"},/* help alias (both '?' and 'usage') */ {'v', 0, "version"}, @@ -163,9 +162,11 @@ static const opt_struct OPTIONS[] = { {11, 1, "rclass"}, {12, 1, "re"}, {12, 1, "rextension"}, - {13, 1, "ri"}, - {13, 1, "rextinfo"}, - {14, 0, "ini"}, + {13, 1, "rz"}, + {13, 1, "rzendextension"}, + {14, 1, "ri"}, + {14, 1, "rextinfo"}, + {15, 0, "ini"}, {'-', 0, NULL} /* end of args */ }; @@ -248,11 +249,23 @@ static inline int sapi_cli_select(int fd TSRMLS_DC) return ret != -1; } -static inline size_t sapi_cli_single_write(const char *str, uint str_length TSRMLS_DC) /* {{{ */ +PHP_CLI_API size_t sapi_cli_single_write(const char *str, uint str_length TSRMLS_DC) /* {{{ */ { #ifdef PHP_WRITE_STDOUT long ret; +#else + size_t ret; +#endif + + if (cli_shell_callbacks.cli_shell_write) { + size_t shell_wrote; + shell_wrote = cli_shell_callbacks.cli_shell_write(str, str_length TSRMLS_CC); + if (shell_wrote > -1) { + return shell_wrote; + } + } +#ifdef PHP_WRITE_STDOUT do { ret = write(STDOUT_FILENO, str, str_length); } while (ret <= 0 && errno == EAGAIN && sapi_cli_select(STDOUT_FILENO TSRMLS_CC)); @@ -263,8 +276,6 @@ static inline size_t sapi_cli_single_write(const char *str, uint str_length TSRM return ret; #else - size_t ret; - ret = fwrite(str, 1, MIN(str_length, 16384), stdout); return ret; #endif @@ -277,12 +288,17 @@ static int sapi_cli_ub_write(const char *str, uint str_length TSRMLS_DC) /* {{{ uint remaining = str_length; size_t ret; -#if (HAVE_LIBREADLINE || HAVE_LIBEDIT) && !defined(COMPILE_DL_READLINE) if (!str_length) { return 0; } - php_last_char = str[str_length-1]; -#endif + + if (cli_shell_callbacks.cli_shell_ub_write) { + int ub_wrote; + ub_wrote = cli_shell_callbacks.cli_shell_ub_write(str, str_length TSRMLS_CC); + if (ub_wrote > -1) { + return ub_wrote; + } + } while (remaining > 0) { @@ -351,7 +367,7 @@ static void sapi_cli_register_variables(zval *track_vars_array TSRMLS_DC) /* {{{ } /* }}} */ -static void sapi_cli_log_message(char *message) /* {{{ */ +static void sapi_cli_log_message(char *message TSRMLS_DC) /* {{{ */ { fprintf(stderr, "%s\n", message); } @@ -449,7 +465,7 @@ static sapi_module_struct cli_sapi_module = { sapi_cli_log_message, /* Log message */ NULL, /* Get request time */ NULL, /* Child terminate */ - + STANDARD_SAPI_MODULE_PROPERTIES }; /* }}} */ @@ -478,13 +494,13 @@ static void php_cli_usage(char *argv0) prog = "php"; } - php_printf( "Usage: %s [options] [-f] <file> [--] [args...]\n" - " %s [options] -r <code> [--] [args...]\n" - " %s [options] [-B <begin_code>] -R <code> [-E <end_code>] [--] [args...]\n" - " %s [options] [-B <begin_code>] -F <file> [-E <end_code>] [--] [args...]\n" - " %s [options] -- [args...]\n" - " %s [options] -a\n" - "\n" + printf( "Usage: %s [options] [-f] <file> [--] [args...]\n" + " %s [options] -r <code> [--] [args...]\n" + " %s [options] [-B <begin_code>] -R <code> [-E <end_code>] [--] [args...]\n" + " %s [options] [-B <begin_code>] -F <file> [-E <end_code>] [--] [args...]\n" + " %s [options] -- [args...]\n" + " %s [options] -a\n" + "\n" #if (HAVE_LIBREADLINE || HAVE_LIBEDIT) && !defined(COMPILE_DL_READLINE) " -a Run as interactive shell\n" #else @@ -505,6 +521,8 @@ static void php_cli_usage(char *argv0) " -F <file> Parse and execute <file> for every input line\n" " -E <end_code> Run PHP <end_code> after processing all input lines\n" " -H Hide any passed arguments from external tools.\n" + " -S <addr>:<port> Run with built-in web server.\n" + " -t <docroot> Specify document root <docroot> for built-in web server.\n" " -s Output HTML syntax highlighted source.\n" " -v Version number\n" " -w Output source with stripped comments and whitespace.\n" @@ -518,6 +536,7 @@ static void php_cli_usage(char *argv0) " --rf <name> Show information about function <name>.\n" " --rc <name> Show information about class <name>.\n" " --re <name> Show information about extension <name>.\n" + " --rz <name> Show information about Zend extension <name>.\n" " --ri <name> Show configuration for extension <name>.\n" "\n" , prog, prog, prog, prog, prog, prog); @@ -631,203 +650,45 @@ static int cli_seek_file_begin(zend_file_handle *file_handle, char *script_file, } /* }}} */ -/* {{{ main - */ -#ifdef PHP_CLI_WIN32_NO_CONSOLE -int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nShowCmd) -#else -int main(int argc, char *argv[]) -#endif +static int do_cli(int argc, char **argv TSRMLS_DC) /* {{{ */ { - volatile int exit_status = SUCCESS; int c; zend_file_handle file_handle; -/* temporary locals */ - int behavior=PHP_MODE_STANDARD; + int behavior = PHP_MODE_STANDARD; char *reflection_what = NULL; - int orig_optind=php_optind; - char *orig_optarg=php_optarg; + volatile int request_started = 0; + volatile int exit_status = 0; + char *php_optarg = NULL, *orig_optarg = NULL; + int php_optind = 1, orig_optind = 1; + char *exec_direct=NULL, *exec_run=NULL, *exec_begin=NULL, *exec_end=NULL; char *arg_free=NULL, **arg_excp=&arg_free; char *script_file=NULL; int interactive=0; - volatile int module_started = 0; - volatile int request_started = 0; int lineno = 0; - char *exec_direct=NULL, *exec_run=NULL, *exec_begin=NULL, *exec_end=NULL; const char *param_error=NULL; int hide_argv = 0; -/* end of temporary locals */ -#ifdef ZTS - void ***tsrm_ls; -#endif -#ifdef PHP_CLI_WIN32_NO_CONSOLE - int argc = __argc; - char **argv = __argv; -#endif - int ini_entries_len = 0; - -#if defined(PHP_WIN32) && defined(_DEBUG) && defined(PHP_WIN32_DEBUG_HEAP) - { - int tmp_flag; - _CrtSetReportMode(_CRT_WARN, _CRTDBG_MODE_FILE); - _CrtSetReportFile(_CRT_WARN, _CRTDBG_FILE_STDERR); - _CrtSetReportMode(_CRT_ERROR, _CRTDBG_MODE_FILE); - _CrtSetReportFile(_CRT_ERROR, _CRTDBG_FILE_STDERR); - _CrtSetReportMode(_CRT_ASSERT, _CRTDBG_MODE_FILE); - _CrtSetReportFile(_CRT_ASSERT, _CRTDBG_FILE_STDERR); - tmp_flag = _CrtSetDbgFlag(_CRTDBG_REPORT_FLAG); - tmp_flag |= _CRTDBG_DELAY_FREE_MEM_DF; - tmp_flag |= _CRTDBG_LEAK_CHECK_DF; - - _CrtSetDbgFlag(tmp_flag); - } -#endif - -#ifdef HAVE_SIGNAL_H -#if defined(SIGPIPE) && defined(SIG_IGN) - signal(SIGPIPE, SIG_IGN); /* ignore SIGPIPE in standalone mode so - that sockets created via fsockopen() - don't kill PHP if the remote site - closes it. in apache|apxs mode apache - does that for us! thies@thieso.net - 20000419 */ -#endif -#endif - - -#ifdef ZTS - tsrm_startup(1, 1, 0, NULL); - tsrm_ls = ts_resource(0); -#endif - - cli_sapi_module.ini_defaults = sapi_cli_ini_defaults; - cli_sapi_module.php_ini_path_override = NULL; - cli_sapi_module.phpinfo_as_text = 1; - sapi_startup(&cli_sapi_module); - -#ifdef PHP_WIN32 - _fmode = _O_BINARY; /*sets default for file streams to binary */ - setmode(_fileno(stdin), O_BINARY); /* make the stdio mode be binary */ - setmode(_fileno(stdout), O_BINARY); /* make the stdio mode be binary */ - setmode(_fileno(stderr), O_BINARY); /* make the stdio mode be binary */ -#endif - ini_entries_len = sizeof(HARDCODED_INI)-2; - cli_sapi_module.ini_entries = malloc(sizeof(HARDCODED_INI)); - memcpy(cli_sapi_module.ini_entries, HARDCODED_INI, sizeof(HARDCODED_INI)); - - while ((c = php_getopt(argc, argv, OPTIONS, &php_optarg, &php_optind, 0, 2))!=-1) { - switch (c) { - case 'c': - if (cli_sapi_module.php_ini_path_override) { - free(cli_sapi_module.php_ini_path_override); - } - cli_sapi_module.php_ini_path_override = strdup(php_optarg); - break; - case 'n': - cli_sapi_module.php_ini_ignore = 1; - break; - case 'd': { - /* define ini entries on command line */ - int len = strlen(php_optarg); - char *val; - - if ((val = strchr(php_optarg, '='))) { - val++; - if (!isalnum(*val) && *val != '"' && *val != '\'' && *val != '\0') { - cli_sapi_module.ini_entries = realloc(cli_sapi_module.ini_entries, ini_entries_len + len + sizeof("\"\"\n\0")); - memcpy(cli_sapi_module.ini_entries + ini_entries_len, php_optarg, (val - php_optarg)); - ini_entries_len += (val - php_optarg); - memcpy(cli_sapi_module.ini_entries + ini_entries_len, "\"", 1); - ini_entries_len++; - memcpy(cli_sapi_module.ini_entries + ini_entries_len, val, len - (val - php_optarg)); - ini_entries_len += len - (val - php_optarg); - memcpy(cli_sapi_module.ini_entries + ini_entries_len, "\"\n\0", sizeof("\"\n\0")); - ini_entries_len += sizeof("\n\0\"") - 2; - } else { - cli_sapi_module.ini_entries = realloc(cli_sapi_module.ini_entries, ini_entries_len + len + sizeof("\n\0")); - memcpy(cli_sapi_module.ini_entries + ini_entries_len, php_optarg, len); - memcpy(cli_sapi_module.ini_entries + ini_entries_len + len, "\n\0", sizeof("\n\0")); - ini_entries_len += len + sizeof("\n\0") - 2; - } - } else { - cli_sapi_module.ini_entries = realloc(cli_sapi_module.ini_entries, ini_entries_len + len + sizeof("=1\n\0")); - memcpy(cli_sapi_module.ini_entries + ini_entries_len, php_optarg, len); - memcpy(cli_sapi_module.ini_entries + ini_entries_len + len, "=1\n\0", sizeof("=1\n\0")); - ini_entries_len += len + sizeof("=1\n\0") - 2; - } - break; - } - } - } - php_optind = orig_optind; - php_optarg = orig_optarg; - - cli_sapi_module.executable_location = argv[0]; - cli_sapi_module.additional_functions = additional_functions; - - /* startup after we get the above ini override se we get things right */ - if (cli_sapi_module.startup(&cli_sapi_module)==FAILURE) { - /* there is no way to see if we must call zend_ini_deactivate() - * since we cannot check if EG(ini_directives) has been initialised - * because the executor's constructor does not set initialize it. - * Apart from that there seems no need for zend_ini_deactivate() yet. - * So we goto out_err.*/ - exit_status = 1; - goto out_err; - } - module_started = 1; - - zend_first_try { + zend_try { + CG(in_compilation) = 0; /* not initialized but needed for several options */ EG(uninitialized_zval_ptr) = NULL; while ((c = php_getopt(argc, argv, OPTIONS, &php_optarg, &php_optind, 0, 2)) != -1) { switch (c) { - case 'h': /* help & quit */ - case '?': - if (php_request_startup(TSRMLS_C)==FAILURE) { - goto err; - } - request_started = 1; - php_cli_usage(argv[0]); - php_end_ob_buffers(1 TSRMLS_CC); - exit_status = (c == '?' && argc > 1 && !strchr(argv[1], c)); - goto out; - case 'i': /* php info & quit */ if (php_request_startup(TSRMLS_C)==FAILURE) { goto err; } request_started = 1; php_print_info(0xFFFFFFFF TSRMLS_CC); - php_end_ob_buffers(1 TSRMLS_CC); - exit_status=0; - goto out; - - case 'm': /* list compiled in modules */ - if (php_request_startup(TSRMLS_C)==FAILURE) { - goto err; - } - request_started = 1; - php_printf("[PHP Modules]\n"); - print_modules(TSRMLS_C); - php_printf("\n[Zend Modules]\n"); - print_extensions(TSRMLS_C); - php_printf("\n"); - php_end_ob_buffers(1 TSRMLS_CC); - exit_status=0; + php_output_end_all(TSRMLS_C); + exit_status = (c == '?' && argc > 1 && !strchr(argv[1], c)); goto out; case 'v': /* show php version & quit */ - if (php_request_startup(TSRMLS_C) == FAILURE) { - goto err; - } - - request_started = 1; php_printf("PHP %s (%s) (built: %s %s) %s\nCopyright (c) 1997-2012 The PHP Group\n%s", - PHP_VERSION, sapi_module.name, __DATE__, __TIME__, + PHP_VERSION, cli_sapi_module.name, __DATE__, __TIME__, #if ZEND_DEBUG && defined(HAVE_GCOV) "(DEBUG GCOV)", #elif ZEND_DEBUG @@ -839,7 +700,20 @@ int main(int argc, char *argv[]) #endif get_zend_version() ); - php_end_ob_buffers(1 TSRMLS_CC); + sapi_deactivate(TSRMLS_C); + goto out; + + case 'm': /* list compiled in modules */ + if (php_request_startup(TSRMLS_C)==FAILURE) { + goto err; + } + request_started = 1; + php_printf("[PHP Modules]\n"); + print_modules(TSRMLS_C); + php_printf("\n[Zend Modules]\n"); + print_extensions(TSRMLS_C); + php_printf("\n"); + php_output_end_all(TSRMLS_C); exit_status=0; goto out; @@ -871,10 +745,6 @@ int main(int argc, char *argv[]) /* This is default so NOP */ break; - case 'e': /* enable extended info output */ - CG(compiler_options) |= ZEND_COMPILE_EXTENDED_INFO; - break; - case 'F': if (behavior == PHP_MODE_PROCESS_STDIN) { if (exec_run || script_file) { @@ -1012,10 +882,14 @@ int main(int argc, char *argv[]) reflection_what = php_optarg; break; case 13: - behavior=PHP_MODE_REFLECTION_EXT_INFO; + behavior=PHP_MODE_REFLECTION_ZEND_EXTENSION; reflection_what = php_optarg; break; case 14: + behavior=PHP_MODE_REFLECTION_EXT_INFO; + reflection_what = php_optarg; + break; + case 15: behavior = PHP_MODE_SHOW_INI_CONFIG; break; default: @@ -1066,15 +940,15 @@ int main(int argc, char *argv[]) file_handle.type = ZEND_HANDLE_FP; file_handle.opened_path = NULL; file_handle.free_filename = 0; - php_self = file_handle.filename; + php_self = (char*)file_handle.filename; /* before registering argv to module exchange the *new* argv[0] */ /* we can achieve this without allocating more memory */ SG(request_info).argc=argc-php_optind+1; arg_excp = argv+php_optind-1; arg_free = argv[php_optind-1]; - SG(request_info).path_translated = file_handle.filename; - argv[php_optind-1] = file_handle.filename; + SG(request_info).path_translated = (char*)file_handle.filename; + argv[php_optind-1] = (char*)file_handle.filename; SG(request_info).argv=argv+php_optind-1; if (php_request_startup(TSRMLS_C)==FAILURE) { @@ -1103,86 +977,12 @@ int main(int argc, char *argv[]) cli_register_file_handles(TSRMLS_C); } -#if (HAVE_LIBREADLINE || HAVE_LIBEDIT) && !defined(COMPILE_DL_READLINE) - if (interactive) { - char *line; - size_t size = 4096, pos = 0, len; - char *code = emalloc(size); - char *prompt = "php > "; - char *history_file; - - if (PG(auto_prepend_file) && PG(auto_prepend_file)[0]) { - zend_file_handle *prepend_file_p; - zend_file_handle prepend_file = {0}; - - prepend_file.filename = PG(auto_prepend_file); - prepend_file.opened_path = NULL; - prepend_file.free_filename = 0; - prepend_file.type = ZEND_HANDLE_FILENAME; - prepend_file_p = &prepend_file; - - zend_execute_scripts(ZEND_REQUIRE TSRMLS_CC, NULL, 1, prepend_file_p); - } - - history_file = tilde_expand("~/.php_history"); - rl_attempted_completion_function = cli_code_completion; - rl_special_prefixes = "$"; - read_history(history_file); - - EG(exit_status) = 0; - while ((line = readline(prompt)) != NULL) { - if (strcmp(line, "exit") == 0 || strcmp(line, "quit") == 0) { - free(line); - break; - } - - if (!pos && !*line) { - free(line); - continue; - } - - len = strlen(line); - if (pos + len + 2 > size) { - size = pos + len + 2; - code = erealloc(code, size); - } - memcpy(&code[pos], line, len); - pos += len; - code[pos] = '\n'; - code[++pos] = '\0'; - - if (*line) { - add_history(line); - } - - free(line); - - if (!cli_is_valid_code(code, pos, &prompt TSRMLS_CC)) { - continue; - } - - zend_eval_stringl(code, pos, NULL, "php shell code" TSRMLS_CC); - pos = 0; - - if (php_last_char != '\0' && php_last_char != '\n') { - sapi_cli_single_write("\n", 1 TSRMLS_CC); - } - - if (EG(exception)) { - zend_exception_error(EG(exception), E_WARNING TSRMLS_CC); - } - - php_last_char = '\0'; - } - write_history(history_file); - free(history_file); - efree(code); + if (interactive && cli_shell_callbacks.cli_shell_run) { + exit_status = cli_shell_callbacks.cli_shell_run(TSRMLS_C); + } else { + php_execute_script(&file_handle TSRMLS_CC); exit_status = EG(exit_status); - break; } -#endif /* HAVE_LIBREADLINE || HAVE_LIBEDIT */ - php_execute_script(&file_handle TSRMLS_CC); - exit_status = EG(exit_status); break; case PHP_MODE_LINT: exit_status = php_lint_script(&file_handle TSRMLS_CC); @@ -1232,7 +1032,7 @@ int main(int argc, char *argv[]) zval *argn, *argi; cli_register_file_handles(TSRMLS_C); - + if (exec_begin && zend_eval_string_ex(exec_begin, NULL, "Command line begin code", 1 TSRMLS_CC) == FAILURE) { exit_status=254; } @@ -1279,6 +1079,7 @@ int main(int argc, char *argv[]) case PHP_MODE_REFLECTION_FUNCTION: case PHP_MODE_REFLECTION_CLASS: case PHP_MODE_REFLECTION_EXTENSION: + case PHP_MODE_REFLECTION_ZEND_EXTENSION: { zend_class_entry *pce = NULL; zval *arg, *ref; @@ -1300,6 +1101,9 @@ int main(int argc, char *argv[]) case PHP_MODE_REFLECTION_EXTENSION: pce = reflection_extension_ptr; break; + case PHP_MODE_REFLECTION_ZEND_EXTENSION: + pce = reflection_zend_extension_ptr; + break; } MAKE_STD_ZVAL(arg); @@ -1350,44 +1154,230 @@ int main(int argc, char *argv[]) { zend_printf("Configuration File (php.ini) Path: %s\n", PHP_CONFIG_FILE_PATH); zend_printf("Loaded Configuration File: %s\n", php_ini_opened_path ? php_ini_opened_path : "(none)"); - zend_printf("Scan for additional .ini files in: %s\n", php_ini_scanned_path ? php_ini_scanned_path : "(none)"); + zend_printf("Scan for additional .ini files in: %s\n", php_ini_scanned_path ? php_ini_scanned_path : "(none)"); zend_printf("Additional .ini files parsed: %s\n", php_ini_scanned_files ? php_ini_scanned_files : "(none)"); break; } } - } zend_end_try(); out: + if (exit_status == 0) { + exit_status = EG(exit_status); + } if (request_started) { php_request_shutdown((void *) 0); } - if (exit_status == 0) { - exit_status = EG(exit_status); + return exit_status; +err: + sapi_deactivate(TSRMLS_C); + zend_ini_deactivate(TSRMLS_C); + exit_status = 1; + goto out; +} +/* }}} */ + +/* {{{ main + */ +#ifdef PHP_CLI_WIN32_NO_CONSOLE +int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nShowCmd) +#else +int main(int argc, char *argv[]) +#endif +{ +#ifdef ZTS + void ***tsrm_ls; +#endif +#ifdef PHP_CLI_WIN32_NO_CONSOLE + int argc = __argc; + char **argv = __argv; +#endif + int c; + int exit_status = SUCCESS; + int module_started = 0, sapi_started = 0; + char *php_optarg = NULL; + int php_optind = 1, use_extended_info = 0; + char *ini_path_override = NULL; + char *ini_entries = NULL; + int ini_entries_len = 0; + int ini_ignore = 0; + sapi_module_struct *sapi_module = &cli_sapi_module; + + cli_sapi_module.additional_functions = additional_functions; + +#if defined(PHP_WIN32) && defined(_DEBUG) && defined(PHP_WIN32_DEBUG_HEAP) + { + int tmp_flag; + _CrtSetReportMode(_CRT_WARN, _CRTDBG_MODE_FILE); + _CrtSetReportFile(_CRT_WARN, _CRTDBG_FILE_STDERR); + _CrtSetReportMode(_CRT_ERROR, _CRTDBG_MODE_FILE); + _CrtSetReportFile(_CRT_ERROR, _CRTDBG_FILE_STDERR); + _CrtSetReportMode(_CRT_ASSERT, _CRTDBG_MODE_FILE); + _CrtSetReportFile(_CRT_ASSERT, _CRTDBG_FILE_STDERR); + tmp_flag = _CrtSetDbgFlag(_CRTDBG_REPORT_FLAG); + tmp_flag |= _CRTDBG_DELAY_FREE_MEM_DF; + tmp_flag |= _CRTDBG_LEAK_CHECK_DF; + + _CrtSetDbgFlag(tmp_flag); + } +#endif + +#ifdef HAVE_SIGNAL_H +#if defined(SIGPIPE) && defined(SIG_IGN) + signal(SIGPIPE, SIG_IGN); /* ignore SIGPIPE in standalone mode so + that sockets created via fsockopen() + don't kill PHP if the remote site + closes it. in apache|apxs mode apache + does that for us! thies@thieso.net + 20000419 */ +#endif +#endif + + +#ifdef ZTS + tsrm_startup(1, 1, 0, NULL); + tsrm_ls = ts_resource(0); +#endif + +#ifdef PHP_WIN32 + _fmode = _O_BINARY; /*sets default for file streams to binary */ + setmode(_fileno(stdin), O_BINARY); /* make the stdio mode be binary */ + setmode(_fileno(stdout), O_BINARY); /* make the stdio mode be binary */ + setmode(_fileno(stderr), O_BINARY); /* make the stdio mode be binary */ +#endif + + while ((c = php_getopt(argc, argv, OPTIONS, &php_optarg, &php_optind, 0, 2))!=-1) { + switch (c) { + case 'c': + if (ini_path_override) { + free(ini_path_override); + } + ini_path_override = strdup(php_optarg); + break; + case 'n': + ini_ignore = 1; + break; + case 'd': { + /* define ini entries on command line */ + int len = strlen(php_optarg); + char *val; + + if ((val = strchr(php_optarg, '='))) { + val++; + if (!isalnum(*val) && *val != '"' && *val != '\'' && *val != '\0') { + ini_entries = realloc(ini_entries, ini_entries_len + len + sizeof("\"\"\n\0")); + memcpy(ini_entries + ini_entries_len, php_optarg, (val - php_optarg)); + ini_entries_len += (val - php_optarg); + memcpy(ini_entries + ini_entries_len, "\"", 1); + ini_entries_len++; + memcpy(ini_entries + ini_entries_len, val, len - (val - php_optarg)); + ini_entries_len += len - (val - php_optarg); + memcpy(ini_entries + ini_entries_len, "\"\n\0", sizeof("\"\n\0")); + ini_entries_len += sizeof("\n\0\"") - 2; + } else { + ini_entries = realloc(ini_entries, ini_entries_len + len + sizeof("\n\0")); + memcpy(ini_entries + ini_entries_len, php_optarg, len); + memcpy(ini_entries + ini_entries_len + len, "\n\0", sizeof("\n\0")); + ini_entries_len += len + sizeof("\n\0") - 2; + } + } else { + ini_entries = realloc(ini_entries, ini_entries_len + len + sizeof("=1\n\0")); + memcpy(ini_entries + ini_entries_len, php_optarg, len); + memcpy(ini_entries + ini_entries_len + len, "=1\n\0", sizeof("=1\n\0")); + ini_entries_len += len + sizeof("=1\n\0") - 2; + } + break; + } +#ifndef PHP_CLI_WIN32_NO_CONSOLE + case 'S': + sapi_module = &cli_server_sapi_module; + break; +#endif + case 'h': /* help & quit */ + case '?': + php_cli_usage(argv[0]); + goto out; + case 'i': case 'v': case 'm': + sapi_module = &cli_sapi_module; + goto exit_loop; + case 'e': /* enable extended info output */ + use_extended_info = 1; + break; + } + } +exit_loop: + + sapi_module->ini_defaults = sapi_cli_ini_defaults; + sapi_module->php_ini_path_override = ini_path_override; + sapi_module->phpinfo_as_text = 1; + sapi_module->php_ini_ignore_cwd = 1; + sapi_startup(sapi_module); + sapi_started = 1; + + sapi_module->php_ini_ignore = ini_ignore; + + sapi_module->executable_location = argv[0]; + + if (sapi_module == &cli_sapi_module) { + if (ini_entries) { + ini_entries = realloc(ini_entries, ini_entries_len + sizeof(HARDCODED_INI)); + memmove(ini_entries + sizeof(HARDCODED_INI) - 2, ini_entries, ini_entries_len + 1); + memcpy(ini_entries, HARDCODED_INI, sizeof(HARDCODED_INI) - 2); + } else { + ini_entries = malloc(sizeof(HARDCODED_INI)); + memcpy(ini_entries, HARDCODED_INI, sizeof(HARDCODED_INI)); + } + ini_entries_len += sizeof(HARDCODED_INI) - 2; } -out_err: - if (cli_sapi_module.php_ini_path_override) { - free(cli_sapi_module.php_ini_path_override); + + sapi_module->ini_entries = ini_entries; + + /* startup after we get the above ini override se we get things right */ + if (sapi_module->startup(sapi_module) == FAILURE) { + /* there is no way to see if we must call zend_ini_deactivate() + * since we cannot check if EG(ini_directives) has been initialised + * because the executor's constructor does not set initialize it. + * Apart from that there seems no need for zend_ini_deactivate() yet. + * So we goto out_err.*/ + exit_status = 1; + goto out; } - if (cli_sapi_module.ini_entries) { - free(cli_sapi_module.ini_entries); + module_started = 1; + + /* -e option */ + if (use_extended_info) { + CG(compiler_options) |= ZEND_COMPILE_EXTENDED_INFO; } + zend_first_try { +#ifndef PHP_CLI_WIN32_NO_CONSOLE + if (sapi_module == &cli_sapi_module) { +#endif + exit_status = do_cli(argc, argv TSRMLS_CC); +#ifndef PHP_CLI_WIN32_NO_CONSOLE + } else { + exit_status = do_cli_server(argc, argv TSRMLS_CC); + } +#endif + } zend_end_try(); +out: + if (ini_path_override) { + free(ini_path_override); + } + if (ini_entries) { + free(ini_entries); + } if (module_started) { php_module_shutdown(TSRMLS_C); } - sapi_shutdown(); + if (sapi_started) { + sapi_shutdown(); + } #ifdef ZTS tsrm_shutdown(); #endif exit(exit_status); - -err: - sapi_deactivate(TSRMLS_C); - zend_ini_deactivate(TSRMLS_C); - exit_status = 1; - goto out_err; } /* }}} */ diff --git a/sapi/cli/php_cli_readline.c b/sapi/cli/php_cli_readline.c deleted file mode 100644 index 888f4b60b..000000000 --- a/sapi/cli/php_cli_readline.c +++ /dev/null @@ -1,448 +0,0 @@ -/* - +----------------------------------------------------------------------+ - | PHP Version 5 | - +----------------------------------------------------------------------+ - | Copyright (c) 1997-2012 The PHP Group | - +----------------------------------------------------------------------+ - | This source file is subject to version 3.01 of the PHP license, | - | that is bundled with this package in the file LICENSE, and is | - | available through the world-wide-web at the following url: | - | http://www.php.net/license/3_01.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. | - +----------------------------------------------------------------------+ - | Author: Marcus Boerger <helly@php.net> | - | Johannes Schlueter <johannes@php.net> | - +----------------------------------------------------------------------+ -*/ - -/* $Id: php_cli_readline.c 321634 2012-01-01 13:15:04Z felipe $ */ - -#include "php.h" - -#if (HAVE_LIBREADLINE || HAVE_LIBEDIT) && !defined(COMPILE_DL_READLINE) - -#ifndef HAVE_RL_COMPLETION_MATCHES -#define rl_completion_matches completion_matches -#endif - -#include "php_globals.h" -#include "php_variables.h" -#include "zend_hash.h" -#include "zend_modules.h" - -#include "SAPI.h" - -#if HAVE_SETLOCALE -#include <locale.h> -#endif -#include "zend.h" -#include "zend_extensions.h" -#include "php_ini.h" -#include "php_globals.h" -#include "php_main.h" -#include "fopen_wrappers.h" -#include "ext/standard/php_standard.h" - -#ifdef __riscos__ -#include <unixlib/local.h> -#endif - -#if HAVE_LIBEDIT -#include <editline/readline.h> -#else -#include <readline/readline.h> -#include <readline/history.h> -#endif - -#include "zend_compile.h" -#include "zend_execute.h" -#include "zend_highlight.h" -#include "zend_indent.h" - -typedef enum { - body, - sstring, - dstring, - sstring_esc, - dstring_esc, - comment_line, - comment_block, - heredoc_start, - heredoc, - outside, -} php_code_type; - -int cli_is_valid_code(char *code, int len, char **prompt TSRMLS_DC) /* {{{ */ -{ - int valid_end = 1, last_valid_end; - int brackets_count = 0; - int brace_count = 0; - int i; - php_code_type code_type = body; - char *heredoc_tag; - int heredoc_len; - - for (i = 0; i < len; ++i) { - switch(code_type) { - default: - switch(code[i]) { - case '{': - brackets_count++; - valid_end = 0; - break; - case '}': - if (brackets_count > 0) { - brackets_count--; - } - valid_end = brackets_count ? 0 : 1; - break; - case '(': - brace_count++; - valid_end = 0; - break; - case ')': - if (brace_count > 0) { - brace_count--; - } - valid_end = 0; - break; - case ';': - valid_end = brace_count == 0 && brackets_count == 0; - break; - case ' ': - case '\r': - case '\n': - case '\t': - break; - case '\'': - code_type = sstring; - break; - case '"': - code_type = dstring; - break; - case '#': - code_type = comment_line; - break; - case '/': - if (code[i+1] == '/') { - i++; - code_type = comment_line; - break; - } - if (code[i+1] == '*') { - last_valid_end = valid_end; - valid_end = 0; - code_type = comment_block; - i++; - break; - } - valid_end = 0; - break; - case '%': - if (!CG(asp_tags)) { - valid_end = 0; - break; - } - /* no break */ - case '?': - if (code[i+1] == '>') { - i++; - code_type = outside; - break; - } - valid_end = 0; - break; - case '<': - valid_end = 0; - if (i + 2 < len && code[i+1] == '<' && code[i+2] == '<') { - i += 2; - code_type = heredoc_start; - heredoc_len = 0; - } - break; - default: - valid_end = 0; - break; - } - break; - case sstring: - if (code[i] == '\\') { - code_type = sstring_esc; - } else { - if (code[i] == '\'') { - code_type = body; - } - } - break; - case sstring_esc: - code_type = sstring; - break; - case dstring: - if (code[i] == '\\') { - code_type = dstring_esc; - } else { - if (code[i] == '"') { - code_type = body; - } - } - break; - case dstring_esc: - code_type = dstring; - break; - case comment_line: - if (code[i] == '\n') { - code_type = body; - } - break; - case comment_block: - if (code[i-1] == '*' && code[i] == '/') { - code_type = body; - valid_end = last_valid_end; - } - break; - case heredoc_start: - switch(code[i]) { - case ' ': - case '\t': - break; - case '\r': - case '\n': - code_type = heredoc; - break; - default: - if (!heredoc_len) { - heredoc_tag = code+i; - } - heredoc_len++; - break; - } - break; - case heredoc: - if (code[i - (heredoc_len + 1)] == '\n' && !strncmp(code + i - heredoc_len, heredoc_tag, heredoc_len) && code[i] == '\n') { - code_type = body; - } else if (code[i - (heredoc_len + 2)] == '\n' && !strncmp(code + i - heredoc_len - 1, heredoc_tag, heredoc_len) && code[i-1] == ';' && code[i] == '\n') { - code_type = body; - valid_end = 1; - } - break; - case outside: - if ((CG(short_tags) && !strncmp(code+i-1, "<?", 2)) - || (CG(asp_tags) && !strncmp(code+i-1, "<%", 2)) - || (i > 3 && !strncmp(code+i-4, "<?php", 5)) - ) { - code_type = body; - } - break; - } - } - - switch (code_type) { - default: - if (brace_count) { - *prompt = "php ( "; - } else if (brackets_count) { - *prompt = "php { "; - } else { - *prompt = "php > "; - } - break; - case sstring: - case sstring_esc: - *prompt = "php ' "; - break; - case dstring: - case dstring_esc: - *prompt = "php \" "; - break; - case comment_block: - *prompt = "/* > "; - break; - case heredoc: - *prompt = "<<< > "; - break; - case outside: - *prompt = " > "; - break; - } - - if (!valid_end || brackets_count) { - return 0; - } else { - return 1; - } -} -/* }}} */ - -static char *cli_completion_generator_ht(const char *text, int textlen, int *state, HashTable *ht, void **pData TSRMLS_DC) /* {{{ */ -{ - char *name; - ulong number; - - if (!(*state % 2)) { - zend_hash_internal_pointer_reset(ht); - (*state)++; - } - while(zend_hash_has_more_elements(ht) == SUCCESS) { - zend_hash_get_current_key(ht, &name, &number, 0); - if (!textlen || !strncmp(name, text, textlen)) { - if (pData) { - zend_hash_get_current_data(ht, pData); - } - zend_hash_move_forward(ht); - return name; - } - if (zend_hash_move_forward(ht) == FAILURE) { - break; - } - } - (*state)++; - return NULL; -} /* }}} */ - -static char *cli_completion_generator_var(const char *text, int textlen, int *state TSRMLS_DC) /* {{{ */ -{ - char *retval, *tmp; - - tmp = retval = cli_completion_generator_ht(text + 1, textlen - 1, state, EG(active_symbol_table), NULL TSRMLS_CC); - if (retval) { - retval = malloc(strlen(tmp) + 2); - retval[0] = '$'; - strcpy(&retval[1], tmp); - rl_completion_append_character = '\0'; - } - return retval; -} /* }}} */ - -static char *cli_completion_generator_func(const char *text, int textlen, int *state, HashTable *ht TSRMLS_DC) /* {{{ */ -{ - zend_function *func; - char *retval = cli_completion_generator_ht(text, textlen, state, ht, (void**)&func TSRMLS_CC); - if (retval) { - rl_completion_append_character = '('; - retval = strdup(func->common.function_name); - } - - return retval; -} /* }}} */ - -static char *cli_completion_generator_class(const char *text, int textlen, int *state TSRMLS_DC) /* {{{ */ -{ - zend_class_entry **pce; - char *retval = cli_completion_generator_ht(text, textlen, state, EG(class_table), (void**)&pce TSRMLS_CC); - if (retval) { - rl_completion_append_character = '\0'; - retval = strdup((*pce)->name); - } - - return retval; -} /* }}} */ - -static char *cli_completion_generator_define(const char *text, int textlen, int *state, HashTable *ht TSRMLS_DC) /* {{{ */ -{ - zend_class_entry **pce; - char *retval = cli_completion_generator_ht(text, textlen, state, ht, (void**)&pce TSRMLS_CC); - if (retval) { - rl_completion_append_character = '\0'; - retval = strdup(retval); - } - - return retval; -} /* }}} */ - -static int cli_completion_state; - -static char *cli_completion_generator(const char *text, int index) /* {{{ */ -{ -/* -TODO: -- constants -- maybe array keys -- language constructs and other things outside a hashtable (echo, try, function, class, ...) -- object/class members - -- future: respect scope ("php > function foo() { $[tab]" should only expand to local variables...) -*/ - char *retval = NULL; - int textlen = strlen(text); - TSRMLS_FETCH(); - - if (!index) { - cli_completion_state = 0; - } - if (text[0] == '$') { - retval = cli_completion_generator_var(text, textlen, &cli_completion_state TSRMLS_CC); - } else { - char *lc_text, *class_name, *class_name_end; - int class_name_len; - zend_class_entry **pce = NULL; - - class_name_end = strstr(text, "::"); - if (class_name_end) { - class_name_len = class_name_end - text; - class_name = zend_str_tolower_dup(text, class_name_len); - class_name[class_name_len] = '\0'; /* not done automatically */ - if (zend_lookup_class(class_name, class_name_len, &pce TSRMLS_CC)==FAILURE) { - efree(class_name); - return NULL; - } - lc_text = zend_str_tolower_dup(class_name_end + 2, textlen - 2 - class_name_len); - textlen -= (class_name_len + 2); - } else { - lc_text = zend_str_tolower_dup(text, textlen); - } - - switch (cli_completion_state) { - case 0: - case 1: - retval = cli_completion_generator_func(lc_text, textlen, &cli_completion_state, pce ? &(*pce)->function_table : EG(function_table) TSRMLS_CC); - if (retval) { - break; - } - case 2: - case 3: - retval = cli_completion_generator_define(text, textlen, &cli_completion_state, pce ? &(*pce)->constants_table : EG(zend_constants) TSRMLS_CC); - if (retval || pce) { - break; - } - case 4: - case 5: - retval = cli_completion_generator_class(lc_text, textlen, &cli_completion_state TSRMLS_CC); - break; - default: - break; - } - efree(lc_text); - if (class_name_end) { - efree(class_name); - } - if (pce && retval) { - int len = class_name_len + 2 + strlen(retval) + 1; - char *tmp = malloc(len); - - snprintf(tmp, len, "%s::%s", (*pce)->name, retval); - free(retval); - retval = tmp; - } - } - - return retval; -} /* }}} */ - -char **cli_code_completion(const char *text, int start, int end) /* {{{ */ -{ - return rl_completion_matches(text, cli_completion_generator); -} -/* }}} */ - -#endif /* HAVE_LIBREADLINE || HAVE_LIBEDIT */ - -/* - * Local variables: - * tab-width: 4 - * c-basic-offset: 4 - * End: - * vim600: sw=4 ts=4 fdm=marker - * vim<600: sw=4 ts=4 - */ diff --git a/sapi/cli/php_cli_server.c b/sapi/cli/php_cli_server.c new file mode 100644 index 000000000..d702e8ad1 --- /dev/null +++ b/sapi/cli/php_cli_server.c @@ -0,0 +1,2431 @@ +/* + +----------------------------------------------------------------------+ + | PHP Version 5 | + +----------------------------------------------------------------------+ + | Copyright (c) 1997-2012 The PHP Group | + +----------------------------------------------------------------------+ + | This source file is subject to version 3.01 of the PHP license, | + | that is bundled with this package in the file LICENSE, and is | + | available through the world-wide-web at the following url: | + | http://www.php.net/license/3_01.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. | + +----------------------------------------------------------------------+ + | Author: Moriyoshi Koizumi <moriyoshi@php.net> | + +----------------------------------------------------------------------+ +*/ + +/* $Id: php_cli.c 306938 2011-01-01 02:17:06Z felipe $ */ + +#include <stdio.h> +#include <fcntl.h> +#include <assert.h> + +#ifdef PHP_WIN32 +#include <process.h> +#include <io.h> +#include "win32/time.h" +#include "win32/signal.h" +#include "win32/php_registry.h" +#else +# include "php_config.h" +#endif + +#ifdef __riscos__ +#include <unixlib/local.h> +#endif + + +#if HAVE_TIME_H +#include <time.h> +#endif +#if HAVE_SYS_TIME_H +#include <sys/time.h> +#endif +#if HAVE_UNISTD_H +#include <unistd.h> +#endif +#if HAVE_SIGNAL_H +#include <signal.h> +#endif +#if HAVE_SETLOCALE +#include <locale.h> +#endif +#if HAVE_DLFCN_H +#include <dlfcn.h> +#endif + +#include "SAPI.h" +#include "php.h" +#include "php_ini.h" +#include "php_main.h" +#include "php_globals.h" +#include "php_variables.h" +#include "zend_hash.h" +#include "zend_modules.h" +#include "fopen_wrappers.h" + +#include "zend_compile.h" +#include "zend_execute.h" +#include "zend_highlight.h" +#include "zend_indent.h" +#include "zend_exceptions.h" + +#include "php_getopt.h" + +#ifndef PHP_WIN32 +# define php_select(m, r, w, e, t) select(m, r, w, e, t) +# define SOCK_EINVAL EINVAL +# define SOCK_EAGAIN EAGAIN +# define SOCK_EINTR EINTR +# define SOCK_EADDRINUSE EADDRINUSE +#else +# include "win32/select.h" +# define SOCK_EINVAL WSAEINVAL +# define SOCK_EAGAIN WSAEWOULDBLOCK +# define SOCK_EINTR WSAEINTR +# define SOCK_EADDRINUSE WSAEADDRINUSE +#endif + +#ifndef S_ISDIR +#define S_ISDIR(mode) (((mode)&S_IFMT) == S_IFDIR) +#endif + +#include "ext/standard/file.h" /* for php_set_sock_blocking() :-( */ +#include "ext/standard/php_smart_str.h" +#include "ext/standard/html.h" +#include "ext/standard/url.h" /* for php_url_decode() */ +#include "ext/standard/php_string.h" /* for php_dirname() */ +#include "ext/standard/info.h" /* for php_info_print_style() */ +#include "php_network.h" + +#include "php_http_parser.h" +#include "php_cli_server.h" + +#define OUTPUT_NOT_CHECKED -1 +#define OUTPUT_IS_TTY 1 +#define OUTPUT_NOT_TTY 0 + +typedef struct php_cli_server_poller { + fd_set rfds, wfds; + struct { + fd_set rfds, wfds; + } active; + php_socket_t max_fd; +} php_cli_server_poller; + +typedef struct php_cli_server_request { + enum php_http_method request_method; + int protocol_version; + char *request_uri; + size_t request_uri_len; + char *vpath; + size_t vpath_len; + char *path_translated; + size_t path_translated_len; + char *path_info; + size_t path_info_len; + char *query_string; + size_t query_string_len; + HashTable headers; + char *content; + size_t content_len; + const char *ext; + size_t ext_len; + struct stat sb; +} php_cli_server_request; + +typedef struct php_cli_server_chunk { + struct php_cli_server_chunk *next; + enum php_cli_server_chunk_type { + PHP_CLI_SERVER_CHUNK_HEAP, + PHP_CLI_SERVER_CHUNK_IMMORTAL + } type; + union { + struct { void *block; char *p; size_t len; } heap; + struct { const char *p; size_t len; } immortal; + } data; +} php_cli_server_chunk; + +typedef struct php_cli_server_buffer { + php_cli_server_chunk *first; + php_cli_server_chunk *last; +} php_cli_server_buffer; + +typedef struct php_cli_server_content_sender { + php_cli_server_buffer buffer; +} php_cli_server_content_sender; + +typedef struct php_cli_server_client { + struct php_cli_server *server; + php_socket_t sock; + struct sockaddr *addr; + socklen_t addr_len; + char *addr_str; + size_t addr_str_len; + php_http_parser parser; + unsigned int request_read:1; + char *current_header_name; + size_t current_header_name_len; + unsigned int current_header_name_allocated:1; + size_t post_read_offset; + php_cli_server_request request; + unsigned int content_sender_initialized:1; + php_cli_server_content_sender content_sender; + php_cli_server_buffer capture_buffer; + unsigned int capturing:1; + int file_fd; +} php_cli_server_client; + +typedef struct php_cli_server { + php_socket_t server_sock; + php_cli_server_poller poller; + int is_running; + char *host; + int port; + int address_family; + char *document_root; + size_t document_root_len; + char *router; + size_t router_len; + socklen_t socklen; + HashTable clients; +} php_cli_server; + +typedef struct php_cli_server_http_reponse_status_code_pair { + int code; + const char *str; +} php_cli_server_http_reponse_status_code_pair; + +typedef struct php_cli_server_ext_mime_type_pair { + const char *ext; + const char *mime_type; +} php_cli_server_ext_mime_type_pair; + +static php_cli_server_http_reponse_status_code_pair status_map[] = { + { 100, "Continue" }, + { 101, "Switching Protocols" }, + { 200, "OK" }, + { 201, "Created" }, + { 202, "Accepted" }, + { 203, "Non-Authoritative Information" }, + { 204, "No Content" }, + { 205, "Reset Content" }, + { 206, "Partial Content" }, + { 300, "Multiple Choices" }, + { 301, "Moved Permanently" }, + { 302, "Found" }, + { 303, "See Other" }, + { 304, "Not Modified" }, + { 305, "Use Proxy" }, + { 307, "Temporary Redirect" }, + { 400, "Bad Request" }, + { 401, "Unauthorized" }, + { 402, "Payment Required" }, + { 403, "Forbidden" }, + { 404, "Not Found" }, + { 405, "Method Not Allowed" }, + { 406, "Not Acceptable" }, + { 407, "Proxy Authentication Required" }, + { 408, "Request Timeout" }, + { 409, "Conflict" }, + { 410, "Gone" }, + { 411, "Length Required" }, + { 412, "Precondition Failed" }, + { 413, "Request Entity Too Large" }, + { 414, "Request-URI Too Long" }, + { 415, "Unsupported Media Type" }, + { 416, "Requested Range Not Satisfiable" }, + { 417, "Expectation Failed" }, + { 500, "Internal Server Error" }, + { 501, "Not Implemented" }, + { 502, "Bad Gateway" }, + { 503, "Service Unavailable" }, + { 504, "Gateway Timeout" }, + { 505, "HTTP Version Not Supported" }, +}; + +static php_cli_server_http_reponse_status_code_pair template_map[] = { + { 400, "<h1 class=\"h\">%s</h1><p>Your browser sent a request that this server could not understand.</p>" }, + { 404, "<h1 class=\"h\">%s</h1><p>The requested resource %s was not found on this server.</p>" }, + { 500, "<h1 class=\"h\">%s</h1><p>The server is temporality unavaiable.</p>" } +}; + +static php_cli_server_ext_mime_type_pair mime_type_map[] = { + { "gif", "image/gif" }, + { "png", "image/png" }, + { "jpe", "image/jpeg" }, + { "jpg", "image/jpeg" }, + { "jpeg", "image/jpeg" }, + { "css", "text/css" }, + { "html", "text/html" }, + { "txt", "text/plain" }, + { "js", "text/javascript" }, + { NULL, NULL } +}; + +static int php_cli_output_is_tty = OUTPUT_NOT_CHECKED; + +static size_t php_cli_server_client_send_through(php_cli_server_client *client, const char *str, size_t str_len); +static php_cli_server_chunk *php_cli_server_chunk_heap_new_self_contained(size_t len); +static void php_cli_server_buffer_append(php_cli_server_buffer *buffer, php_cli_server_chunk *chunk); +static void php_cli_server_logf(const char *format TSRMLS_DC, ...); +static void php_cli_server_log_response(php_cli_server_client *client, int status, const char *message TSRMLS_DC); + +ZEND_DECLARE_MODULE_GLOBALS(cli_server); + +static void char_ptr_dtor_p(char **p) /* {{{ */ +{ + pefree(*p, 1); +} /* }}} */ + +static char *get_last_error() /* {{{ */ +{ + return pestrdup(strerror(errno), 1); +} /* }}} */ + +static const char *get_status_string(int code) /* {{{ */ +{ + size_t e = (sizeof(status_map) / sizeof(php_cli_server_http_reponse_status_code_pair)); + size_t s = 0; + + while (e != s) { + size_t c = MIN((e + s + 1) / 2, e - 1); + int d = status_map[c].code; + if (d > code) { + e = c; + } else if (d < code) { + s = c; + } else { + return status_map[c].str; + } + } + return NULL; +} /* }}} */ + +static const char *get_template_string(int code) /* {{{ */ +{ + size_t e = (sizeof(template_map) / sizeof(php_cli_server_http_reponse_status_code_pair)); + size_t s = 0; + + while (e != s) { + size_t c = MIN((e + s + 1) / 2, e - 1); + int d = template_map[c].code; + if (d > code) { + e = c; + } else if (d < code) { + s = c; + } else { + return template_map[c].str; + } + } + return NULL; +} /* }}} */ + +static void append_http_status_line(smart_str *buffer, int protocol_version, int response_code, int persistent) /* {{{ */ +{ + smart_str_appendl_ex(buffer, "HTTP", 4, persistent); + smart_str_appendc_ex(buffer, '/', persistent); + smart_str_append_generic_ex(buffer, protocol_version / 100, persistent, int, _unsigned); + smart_str_appendc_ex(buffer, '.', persistent); + smart_str_append_generic_ex(buffer, protocol_version % 100, persistent, int, _unsigned); + smart_str_appendc_ex(buffer, ' ', persistent); + smart_str_append_generic_ex(buffer, response_code, persistent, int, _unsigned); + smart_str_appendc_ex(buffer, ' ', persistent); + smart_str_appends_ex(buffer, get_status_string(response_code), persistent); + smart_str_appendl_ex(buffer, "\r\n", 2, persistent); +} /* }}} */ + +static void append_essential_headers(smart_str* buffer, php_cli_server_client *client, int persistent) /* {{{ */ +{ + { + char **val; + if (SUCCESS == zend_hash_find(&client->request.headers, "Host", sizeof("Host"), (void**)&val)) { + smart_str_appendl_ex(buffer, "Host", sizeof("Host") - 1, persistent); + smart_str_appendl_ex(buffer, ": ", sizeof(": ") - 1, persistent); + smart_str_appends_ex(buffer, *val, persistent); + smart_str_appendl_ex(buffer, "\r\n", 2, persistent); + } + } + smart_str_appendl_ex(buffer, "Connection: closed\r\n", sizeof("Connection: closed\r\n") - 1, persistent); +} /* }}} */ + +static const char *get_mime_type(const char *ext, size_t ext_len) /* {{{ */ +{ + php_cli_server_ext_mime_type_pair *pair; + for (pair = mime_type_map; pair->ext; pair++) { + size_t len = strlen(pair->ext); + if (len == ext_len && memcmp(pair->ext, ext, len) == 0) { + return pair->mime_type; + } + } + return NULL; +} /* }}} */ + +/* {{{ cli_server module + */ + +static void cli_server_init_globals(zend_cli_server_globals *cg TSRMLS_DC) +{ + cg->color = 0; +} + +PHP_INI_BEGIN() + STD_PHP_INI_BOOLEAN("cli_server.color", "0", PHP_INI_ALL, OnUpdateBool, color, zend_cli_server_globals, cli_server_globals) +PHP_INI_END() + +static PHP_MINIT_FUNCTION(cli_server) +{ + ZEND_INIT_MODULE_GLOBALS(cli_server, cli_server_init_globals, NULL); + REGISTER_INI_ENTRIES(); + return SUCCESS; +} + +static PHP_MSHUTDOWN_FUNCTION(cli_server) +{ + UNREGISTER_INI_ENTRIES(); + return SUCCESS; +} + +static PHP_MINFO_FUNCTION(cli_server) +{ + DISPLAY_INI_ENTRIES(); +} + +zend_module_entry cli_server_module_entry = { + STANDARD_MODULE_HEADER, + "cli_server", + NULL, + PHP_MINIT(cli_server), + PHP_MSHUTDOWN(cli_server), + NULL, + NULL, + PHP_MINFO(cli_server), + PHP_VERSION, + STANDARD_MODULE_PROPERTIES +}; +/* }}} */ + +static int sapi_cli_server_startup(sapi_module_struct *sapi_module) /* {{{ */ +{ + if (php_module_startup(sapi_module, &cli_server_module_entry, 1) == FAILURE) { + return FAILURE; + } + return SUCCESS; +} /* }}} */ + +static int sapi_cli_server_ub_write(const char *str, uint str_length TSRMLS_DC) /* {{{ */ +{ + php_cli_server_client *client = SG(server_context); + if (!client) { + return 0; + } + if (client->capturing) { + php_cli_server_chunk *chunk = php_cli_server_chunk_heap_new_self_contained(str_length); + if (!chunk) { + zend_bailout(); + } + memmove(chunk->data.heap.p, str, str_length); + php_cli_server_buffer_append(&client->capture_buffer, chunk); + return str_length; + } else { + return php_cli_server_client_send_through(client, str, str_length); + } +} /* }}} */ + +static void sapi_cli_server_flush(void *server_context) /* {{{ */ +{ + php_cli_server_client *client = server_context; + TSRMLS_FETCH(); + + if (!client) { + return; + } + + if (client->sock < 0) { + php_handle_aborted_connection(); + return; + } + + if (!SG(headers_sent)) { + sapi_send_headers(TSRMLS_C); + SG(headers_sent) = 1; + } +} /* }}} */ + +static int sapi_cli_server_discard_headers(sapi_headers_struct *sapi_headers TSRMLS_DC) /* {{{ */{ + return SAPI_HEADER_SENT_SUCCESSFULLY; +} +/* }}} */ + +static int sapi_cli_server_send_headers(sapi_headers_struct *sapi_headers TSRMLS_DC) /* {{{ */ +{ + php_cli_server_client *client = SG(server_context); + smart_str buffer = { 0 }; + sapi_header_struct *h; + zend_llist_position pos; + + if (client == NULL || client->capturing || SG(request_info).no_headers) { + return SAPI_HEADER_SENT_SUCCESSFULLY; + } + + if (SG(sapi_headers).http_status_line) { + smart_str_appends(&buffer, SG(sapi_headers).http_status_line); + smart_str_appendl(&buffer, "\r\n", 2); + } else { + append_http_status_line(&buffer, client->request.protocol_version, SG(sapi_headers).http_response_code, 0); + } + + append_essential_headers(&buffer, client, 0); + + h = (sapi_header_struct*)zend_llist_get_first_ex(&sapi_headers->headers, &pos); + while (h) { + if (!h->header_len) { + continue; + } + smart_str_appendl(&buffer, h->header, h->header_len); + smart_str_appendl(&buffer, "\r\n", 2); + h = (sapi_header_struct*)zend_llist_get_next_ex(&sapi_headers->headers, &pos); + } + smart_str_appendl(&buffer, "\r\n", 2); + + php_cli_server_client_send_through(client, buffer.c, buffer.len); + + smart_str_free(&buffer); + return SAPI_HEADER_SENT_SUCCESSFULLY; +} +/* }}} */ + +static char *sapi_cli_server_read_cookies(TSRMLS_D) /* {{{ */ +{ + php_cli_server_client *client = SG(server_context); + char **val; + if (FAILURE == zend_hash_find(&client->request.headers, "Cookie", sizeof("Cookie"), (void**)&val)) { + return NULL; + } + return *val; +} /* }}} */ + +static int sapi_cli_server_read_post(char *buf, uint count_bytes TSRMLS_DC) /* {{{ */ +{ + php_cli_server_client *client = SG(server_context); + if (client->request.content) { + size_t content_len = client->request.content_len; + size_t nbytes_copied = MIN(client->post_read_offset + count_bytes, content_len) - client->post_read_offset; + memmove(buf, client->request.content + client->post_read_offset, nbytes_copied); + client->post_read_offset += nbytes_copied; + return nbytes_copied; + } + return 0; +} /* }}} */ + +static void sapi_cli_server_register_variable(zval *track_vars_array, const char *key, const char *val TSRMLS_DC) /* {{{ */ +{ + char *new_val = (char *)val; + uint new_val_len; + if (sapi_module.input_filter(PARSE_SERVER, (char*)key, &new_val, strlen(val), &new_val_len TSRMLS_CC)) { + php_register_variable_safe((char *)key, new_val, new_val_len, track_vars_array TSRMLS_CC); + } +} /* }}} */ + +static int sapi_cli_server_register_entry_cb(char **entry TSRMLS_DC, int num_args, va_list args, zend_hash_key *hash_key) /* {{{ */ { + zval *track_vars_array = va_arg(args, zval *); + if (hash_key->nKeyLength) { + char *real_key, *key; + uint i; + key = estrndup(hash_key->arKey, hash_key->nKeyLength); + for(i=0; i<hash_key->nKeyLength; i++) { + if (key[i] == '-') { + key[i] = '_'; + } else { + key[i] = toupper(key[i]); + } + } + spprintf(&real_key, 0, "%s_%s", "HTTP", key); + sapi_cli_server_register_variable(track_vars_array, real_key, *entry TSRMLS_CC); + efree(key); + efree(real_key); + } + + return ZEND_HASH_APPLY_KEEP; +} +/* }}} */ + +static void sapi_cli_server_register_variables(zval *track_vars_array TSRMLS_DC) /* {{{ */ +{ + php_cli_server_client *client = SG(server_context); + sapi_cli_server_register_variable(track_vars_array, "DOCUMENT_ROOT", client->server->document_root TSRMLS_CC); + { + char *tmp; + if ((tmp = strrchr(client->addr_str, ':'))) { + char addr[64], port[8]; + strncpy(port, tmp + 1, 8); + port[7] = '\0'; + strncpy(addr, client->addr_str, tmp - client->addr_str); + addr[tmp - client->addr_str] = '\0'; + sapi_cli_server_register_variable(track_vars_array, "REMOTE_ADDR", addr TSRMLS_CC); + sapi_cli_server_register_variable(track_vars_array, "REMOTE_PORT", port TSRMLS_CC); + } else { + sapi_cli_server_register_variable(track_vars_array, "REMOTE_ADDR", client->addr_str TSRMLS_CC); + } + } + { + char *tmp; + spprintf(&tmp, 0, "PHP %s Development Server", PHP_VERSION); + sapi_cli_server_register_variable(track_vars_array, "SERVER_SOFTWARE", tmp TSRMLS_CC); + efree(tmp); + } + { + char *tmp; + spprintf(&tmp, 0, "HTTP/%d.%d", client->request.protocol_version / 100, client->request.protocol_version % 100); + sapi_cli_server_register_variable(track_vars_array, "SERVER_PROTOCOL", tmp TSRMLS_CC); + efree(tmp); + } + sapi_cli_server_register_variable(track_vars_array, "SERVER_NAME", client->server->host TSRMLS_CC); + { + char *tmp; + spprintf(&tmp, 0, "%i", client->server->port); + sapi_cli_server_register_variable(track_vars_array, "SERVER_PORT", tmp TSRMLS_CC); + efree(tmp); + } + + sapi_cli_server_register_variable(track_vars_array, "REQUEST_URI", client->request.request_uri TSRMLS_CC); + sapi_cli_server_register_variable(track_vars_array, "REQUEST_METHOD", SG(request_info).request_method TSRMLS_CC); + sapi_cli_server_register_variable(track_vars_array, "SCRIPT_NAME", client->request.vpath TSRMLS_CC); + if (SG(request_info).path_translated) { + sapi_cli_server_register_variable(track_vars_array, "SCRIPT_FILENAME", SG(request_info).path_translated TSRMLS_CC); + } + if (client->request.path_info) { + sapi_cli_server_register_variable(track_vars_array, "PATH_INFO", client->request.path_info TSRMLS_CC); + } + if (client->request.path_info_len) { + char *tmp; + spprintf(&tmp, 0, "%s%s", client->request.vpath, client->request.path_info); + sapi_cli_server_register_variable(track_vars_array, "PHP_SELF", tmp TSRMLS_CC); + efree(tmp); + } else { + sapi_cli_server_register_variable(track_vars_array, "PHP_SELF", client->request.vpath TSRMLS_CC); + } + if (client->request.query_string) { + sapi_cli_server_register_variable(track_vars_array, "QUERY_STRING", client->request.query_string TSRMLS_CC); + } + zend_hash_apply_with_arguments(&client->request.headers TSRMLS_CC, (apply_func_args_t)sapi_cli_server_register_entry_cb, 1, track_vars_array); +} /* }}} */ + +static void sapi_cli_server_log_message(char *msg TSRMLS_DC) /* {{{ */ +{ + struct timeval tv; + struct tm tm; + char buf[52]; + gettimeofday(&tv, NULL); + php_localtime_r(&tv.tv_sec, &tm); + php_asctime_r(&tm, buf); + { + size_t l = strlen(buf); + if (l > 0) { + buf[l - 1] = '\0'; + } else { + memmove(buf, "unknown", sizeof("unknown")); + } + } + fprintf(stderr, "[%s] %s\n", buf, msg); +} /* }}} */ + +/* {{{ sapi_module_struct cli_server_sapi_module + */ +sapi_module_struct cli_server_sapi_module = { + "cli-server", /* name */ + "Built-in HTTP server", /* pretty name */ + + sapi_cli_server_startup, /* startup */ + php_module_shutdown_wrapper, /* shutdown */ + + NULL, /* activate */ + NULL, /* deactivate */ + + sapi_cli_server_ub_write, /* unbuffered write */ + sapi_cli_server_flush, /* flush */ + NULL, /* get uid */ + NULL, /* getenv */ + + php_error, /* error handler */ + + NULL, /* header handler */ + sapi_cli_server_send_headers, /* send headers handler */ + NULL, /* send header handler */ + + sapi_cli_server_read_post, /* read POST data */ + sapi_cli_server_read_cookies, /* read Cookies */ + + sapi_cli_server_register_variables, /* register server variables */ + sapi_cli_server_log_message, /* Log message */ + NULL, /* Get request time */ + NULL, /* Child terminate */ + + STANDARD_SAPI_MODULE_PROPERTIES +}; /* }}} */ + +static int php_cli_server_poller_ctor(php_cli_server_poller *poller) /* {{{ */ +{ + FD_ZERO(&poller->rfds); + FD_ZERO(&poller->wfds); + poller->max_fd = -1; + return SUCCESS; +} /* }}} */ + +static void php_cli_server_poller_add(php_cli_server_poller *poller, int mode, int fd) /* {{{ */ +{ + if (mode & POLLIN) { + PHP_SAFE_FD_SET(fd, &poller->rfds); + } + if (mode & POLLOUT) { + PHP_SAFE_FD_SET(fd, &poller->wfds); + } + if (fd > poller->max_fd) { + poller->max_fd = fd; + } +} /* }}} */ + +static void php_cli_server_poller_remove(php_cli_server_poller *poller, int mode, int fd) /* {{{ */ +{ + if (mode & POLLIN) { + PHP_SAFE_FD_CLR(fd, &poller->rfds); + } + if (mode & POLLOUT) { + PHP_SAFE_FD_CLR(fd, &poller->wfds); + } +#ifndef PHP_WIN32 + if (fd == poller->max_fd) { + while (fd > 0) { + fd--; + if (((unsigned int *)&poller->rfds)[fd / (8 * sizeof(unsigned int))] || ((unsigned int *)&poller->wfds)[fd / (8 * sizeof(unsigned int))]) { + break; + } + fd -= fd % (8 * sizeof(unsigned int)); + } + poller->max_fd = fd; + } +#endif +} /* }}} */ + +static int php_cli_server_poller_poll(php_cli_server_poller *poller, const struct timeval *tv) /* {{{ */ +{ + memmove(&poller->active.rfds, &poller->rfds, sizeof(poller->rfds)); + memmove(&poller->active.wfds, &poller->wfds, sizeof(poller->wfds)); + return php_select(poller->max_fd + 1, &poller->active.rfds, &poller->active.wfds, NULL, (struct timeval *)tv); +} /* }}} */ + +static int php_cli_server_poller_iter_on_active(php_cli_server_poller *poller, void *opaque, int(*callback)(void *, int fd, int events)) /* {{{ */ +{ + int retval = SUCCESS; +#ifdef PHP_WIN32 + struct socket_entry { + SOCKET fd; + int events; + } entries[FD_SETSIZE * 2]; + php_socket_t fd = 0; + size_t i; + struct socket_entry *n = entries, *m; + + for (i = 0; i < poller->active.rfds.fd_count; i++) { + n->events = POLLIN; + n->fd = poller->active.rfds.fd_array[i]; + n++; + } + + m = n; + for (i = 0; i < poller->active.wfds.fd_count; i++) { + struct socket_entry *e; + SOCKET fd = poller->active.wfds.fd_array[i]; + for (e = entries; e < m; e++) { + if (e->fd == fd) { + e->events |= POLLOUT; + } + } + if (e == m) { + assert(n < entries + FD_SETSIZE * 2); + n->events = POLLOUT; + n->fd = fd; + n++; + } + } + + { + struct socket_entry *e = entries; + for (; e < n; e++) { + if (SUCCESS != callback(opaque, e->fd, e->events)) { + retval = FAILURE; + } + } + } + +#else + php_socket_t fd = 0; + const php_socket_t max_fd = poller->max_fd; + const unsigned int *pr = (unsigned int *)&poller->active.rfds, + *pw = (unsigned int *)&poller->active.wfds, + *e = pr + (max_fd + (8 * sizeof(unsigned int)) - 1) / (8 * sizeof(unsigned int)); + unsigned int mask; + while (pr < e && fd <= max_fd) { + for (mask = 1; mask; mask <<= 1, fd++) { + int events = (*pr & mask ? POLLIN: 0) | (*pw & mask ? POLLOUT: 0); + if (events) { + if (SUCCESS != callback(opaque, fd, events)) { + retval = FAILURE; + } + } + } + pr++; + pw++; + } +#endif + return retval; +} /* }}} */ + +static size_t php_cli_server_chunk_size(const php_cli_server_chunk *chunk) /* {{{ */ +{ + switch (chunk->type) { + case PHP_CLI_SERVER_CHUNK_HEAP: + return chunk->data.heap.len; + case PHP_CLI_SERVER_CHUNK_IMMORTAL: + return chunk->data.immortal.len; + } + return 0; +} /* }}} */ + +static void php_cli_server_chunk_dtor(php_cli_server_chunk *chunk) /* {{{ */ +{ + switch (chunk->type) { + case PHP_CLI_SERVER_CHUNK_HEAP: + if (chunk->data.heap.block != chunk) { + pefree(chunk->data.heap.block, 1); + } + break; + case PHP_CLI_SERVER_CHUNK_IMMORTAL: + break; + } +} /* }}} */ + +static void php_cli_server_buffer_dtor(php_cli_server_buffer *buffer) /* {{{ */ +{ + php_cli_server_chunk *chunk, *next; + for (chunk = buffer->first; chunk; chunk = next) { + next = chunk->next; + php_cli_server_chunk_dtor(chunk); + pefree(chunk, 1); + } +} /* }}} */ + +static void php_cli_server_buffer_ctor(php_cli_server_buffer *buffer) /* {{{ */ +{ + buffer->first = NULL; + buffer->last = NULL; +} /* }}} */ + +static void php_cli_server_buffer_append(php_cli_server_buffer *buffer, php_cli_server_chunk *chunk) /* {{{ */ +{ + php_cli_server_chunk *last; + for (last = chunk; last->next; last = last->next); + if (!buffer->last) { + buffer->first = chunk; + } else { + buffer->last->next = chunk; + } + buffer->last = last; +} /* }}} */ + +static void php_cli_server_buffer_prepend(php_cli_server_buffer *buffer, php_cli_server_chunk *chunk) /* {{{ */ +{ + php_cli_server_chunk *last; + for (last = chunk; last->next; last = last->next); + last->next = buffer->first; + if (!buffer->last) { + buffer->last = last; + } + buffer->first = chunk; +} /* }}} */ + +static size_t php_cli_server_buffer_size(const php_cli_server_buffer *buffer) /* {{{ */ +{ + php_cli_server_chunk *chunk; + size_t retval = 0; + for (chunk = buffer->first; chunk; chunk = chunk->next) { + retval += php_cli_server_chunk_size(chunk); + } + return retval; +} /* }}} */ + +static php_cli_server_chunk *php_cli_server_chunk_immortal_new(const char *buf, size_t len) /* {{{ */ +{ + php_cli_server_chunk *chunk = pemalloc(sizeof(php_cli_server_chunk), 1); + if (!chunk) { + return NULL; + } + + chunk->type = PHP_CLI_SERVER_CHUNK_IMMORTAL; + chunk->next = NULL; + chunk->data.immortal.p = buf; + chunk->data.immortal.len = len; + return chunk; +} /* }}} */ + +static php_cli_server_chunk *php_cli_server_chunk_heap_new(char *block, char *buf, size_t len) /* {{{ */ +{ + php_cli_server_chunk *chunk = pemalloc(sizeof(php_cli_server_chunk), 1); + if (!chunk) { + return NULL; + } + + chunk->type = PHP_CLI_SERVER_CHUNK_HEAP; + chunk->next = NULL; + chunk->data.heap.block = block; + chunk->data.heap.p = buf; + chunk->data.heap.len = len; + return chunk; +} /* }}} */ + +static php_cli_server_chunk *php_cli_server_chunk_heap_new_self_contained(size_t len) /* {{{ */ +{ + php_cli_server_chunk *chunk = pemalloc(sizeof(php_cli_server_chunk) + len, 1); + if (!chunk) { + return NULL; + } + + chunk->type = PHP_CLI_SERVER_CHUNK_HEAP; + chunk->next = NULL; + chunk->data.heap.block = chunk; + chunk->data.heap.p = (char *)(chunk + 1); + chunk->data.heap.len = len; + return chunk; +} /* }}} */ + +static void php_cli_server_content_sender_dtor(php_cli_server_content_sender *sender) /* {{{ */ +{ + php_cli_server_buffer_dtor(&sender->buffer); +} /* }}} */ + +static void php_cli_server_content_sender_ctor(php_cli_server_content_sender *sender) /* {{{ */ +{ + php_cli_server_buffer_ctor(&sender->buffer); +} /* }}} */ + +static int php_cli_server_content_sender_send(php_cli_server_content_sender *sender, php_socket_t fd, size_t *nbytes_sent_total) /* {{{ */ +{ + php_cli_server_chunk *chunk, *next; + size_t _nbytes_sent_total = 0; + + for (chunk = sender->buffer.first; chunk; chunk = next) { + ssize_t nbytes_sent; + next = chunk->next; + + switch (chunk->type) { + case PHP_CLI_SERVER_CHUNK_HEAP: + nbytes_sent = send(fd, chunk->data.heap.p, chunk->data.heap.len, 0); + if (nbytes_sent < 0) { + *nbytes_sent_total = _nbytes_sent_total; + return php_socket_errno(); + } else if (nbytes_sent == chunk->data.heap.len) { + php_cli_server_chunk_dtor(chunk); + pefree(chunk, 1); + sender->buffer.first = next; + if (!next) { + sender->buffer.last = NULL; + } + } else { + chunk->data.heap.p += nbytes_sent; + chunk->data.heap.len -= nbytes_sent; + } + _nbytes_sent_total += nbytes_sent; + break; + + case PHP_CLI_SERVER_CHUNK_IMMORTAL: + nbytes_sent = send(fd, chunk->data.immortal.p, chunk->data.immortal.len, 0); + if (nbytes_sent < 0) { + *nbytes_sent_total = _nbytes_sent_total; + return php_socket_errno(); + } else if (nbytes_sent == chunk->data.immortal.len) { + php_cli_server_chunk_dtor(chunk); + pefree(chunk, 1); + sender->buffer.first = next; + if (!next) { + sender->buffer.last = NULL; + } + } else { + chunk->data.immortal.p += nbytes_sent; + chunk->data.immortal.len -= nbytes_sent; + } + _nbytes_sent_total += nbytes_sent; + break; + } + } + *nbytes_sent_total = _nbytes_sent_total; + return 0; +} /* }}} */ + +static int php_cli_server_content_sender_pull(php_cli_server_content_sender *sender, int fd, size_t *nbytes_read) /* {{{ */ +{ + ssize_t _nbytes_read; + php_cli_server_chunk *chunk = php_cli_server_chunk_heap_new_self_contained(131072); + + _nbytes_read = read(fd, chunk->data.heap.p, chunk->data.heap.len); + if (_nbytes_read < 0) { + char *errstr = get_last_error(); + TSRMLS_FETCH(); + php_cli_server_logf("%s" TSRMLS_CC, errstr); + pefree(errstr, 1); + php_cli_server_chunk_dtor(chunk); + pefree(chunk, 1); + return 1; + } + chunk->data.heap.len = _nbytes_read; + php_cli_server_buffer_append(&sender->buffer, chunk); + *nbytes_read = _nbytes_read; + return 0; +} /* }}} */ + +#if HAVE_UNISTD_H +static int php_cli_is_output_tty() /* {{{ */ +{ + if (php_cli_output_is_tty == OUTPUT_NOT_CHECKED) { + php_cli_output_is_tty = isatty(STDOUT_FILENO); + } + return php_cli_output_is_tty; +} /* }}} */ +#endif + +static void php_cli_server_log_response(php_cli_server_client *client, int status, const char *message TSRMLS_DC) /* {{{ */ +{ + int color = 0, effective_status = status; + char *basic_buf, *message_buf = "", *error_buf = ""; + zend_bool append_error_message = 0; + + if (PG(last_error_message)) { + switch (PG(last_error_type)) { + case E_ERROR: + case E_CORE_ERROR: + case E_COMPILE_ERROR: + case E_USER_ERROR: + case E_PARSE: + if (status == 200) { + /* the status code isn't changed by a fatal error, so fake it */ + effective_status = 500; + } + + append_error_message = 1; + break; + } + } + +#if HAVE_UNISTD_H + if (CLI_SERVER_G(color) && php_cli_is_output_tty() == OUTPUT_IS_TTY) { + if (effective_status >= 500) { + /* server error: red */ + color = 1; + } else if (effective_status >= 400) { + /* client error: yellow */ + color = 3; + } else if (effective_status >= 200) { + /* success: green */ + color = 2; + } + } +#endif + + /* basic */ + spprintf(&basic_buf, 0, "%s [%d]: %s", client->addr_str, status, client->request.request_uri); + if (!basic_buf) { + return; + } + + /* message */ + if (message) { + spprintf(&message_buf, 0, " - %s", message); + if (!message_buf) { + efree(basic_buf); + return; + } + } + + /* error */ + if (append_error_message) { + spprintf(&error_buf, 0, " - %s in %s on line %d", PG(last_error_message), PG(last_error_file), PG(last_error_lineno)); + if (!error_buf) { + efree(basic_buf); + if (message) { + efree(message_buf); + } + return; + } + } + + if (color) { + php_cli_server_logf("\x1b[3%dm%s%s%s\x1b[0m" TSRMLS_CC, color, basic_buf, message_buf, error_buf); + } else { + php_cli_server_logf("%s%s%s" TSRMLS_CC, basic_buf, message_buf, error_buf); + } + + efree(basic_buf); + if (message) { + efree(message_buf); + } + if (append_error_message) { + efree(error_buf); + } +} /* }}} */ + +static void php_cli_server_logf(const char *format TSRMLS_DC, ...) /* {{{ */ +{ + char *buf = NULL; + va_list ap; +#ifdef ZTS + va_start(ap, tsrm_ls); +#else + va_start(ap, format); +#endif + vspprintf(&buf, 0, format, ap); + va_end(ap); + + if (!buf) { + return; + } + + if (sapi_module.log_message) { + sapi_module.log_message(buf TSRMLS_CC); + } + + efree(buf); +} /* }}} */ + +static int php_network_listen_socket(const char *host, int *port, int socktype, int *af, socklen_t *socklen, char **errstr TSRMLS_DC) /* {{{ */ +{ + int retval = SOCK_ERR; + int err = 0; + struct sockaddr *sa = NULL, **p, **sal; + + int num_addrs = php_network_getaddresses(host, socktype, &sal, errstr TSRMLS_CC); + if (num_addrs == 0) { + return -1; + } + for (p = sal; *p; p++) { + if (sa) { + pefree(sa, 1); + sa = NULL; + } + + retval = socket((*p)->sa_family, socktype, 0); + if (retval == SOCK_ERR) { + continue; + } + + switch ((*p)->sa_family) { +#if HAVE_GETADDRINFO && HAVE_IPV6 + case AF_INET6: + sa = pemalloc(sizeof(struct sockaddr_in6), 1); + if (!sa) { + closesocket(retval); + retval = SOCK_ERR; + *errstr = NULL; + goto out; + } + *(struct sockaddr_in6 *)sa = *(struct sockaddr_in6 *)*p; + ((struct sockaddr_in6 *)sa)->sin6_port = htons(*port); + *socklen = sizeof(struct sockaddr_in6); + break; +#endif + case AF_INET: + sa = pemalloc(sizeof(struct sockaddr_in), 1); + if (!sa) { + closesocket(retval); + retval = SOCK_ERR; + *errstr = NULL; + goto out; + } + *(struct sockaddr_in *)sa = *(struct sockaddr_in *)*p; + ((struct sockaddr_in *)sa)->sin_port = htons(*port); + *socklen = sizeof(struct sockaddr_in); + break; + default: + /* Unknown family */ + *socklen = 0; + closesocket(retval); + continue; + } + +#ifdef SO_REUSEADDR + { + int val = 1; + setsockopt(retval, SOL_SOCKET, SO_REUSEADDR, (char*)&val, sizeof(val)); + } +#endif + + if (bind(retval, sa, *socklen) == SOCK_CONN_ERR) { + err = php_socket_errno(); + if (err == SOCK_EINVAL || err == SOCK_EADDRINUSE) { + goto out; + } + closesocket(retval); + retval = SOCK_ERR; + continue; + } + err = 0; + + *af = sa->sa_family; + if (*port == 0) { + if (getsockname(retval, sa, socklen)) { + err = php_socket_errno(); + goto out; + } + switch (sa->sa_family) { +#if HAVE_GETADDRINFO && HAVE_IPV6 + case AF_INET6: + *port = ntohs(((struct sockaddr_in6 *)sa)->sin6_port); + break; +#endif + case AF_INET: + *port = ntohs(((struct sockaddr_in *)sa)->sin_port); + break; + } + } + + break; + } + + if (retval == SOCK_ERR) { + goto out; + } + + if (listen(retval, SOMAXCONN)) { + err = php_socket_errno(); + goto out; + } + +out: + if (sa) { + pefree(sa, 1); + } + if (sal) { + php_network_freeaddresses(sal); + } + if (err) { + if (retval >= 0) { + closesocket(retval); + } + if (errstr) { + *errstr = php_socket_strerror(err, NULL, 0); + } + return SOCK_ERR; + } + return retval; +} /* }}} */ + +static int php_cli_server_request_ctor(php_cli_server_request *req) /* {{{ */ +{ + req->protocol_version = 0; + req->request_uri = NULL; + req->request_uri_len = 0; + req->vpath = NULL; + req->vpath_len = 0; + req->path_translated = NULL; + req->path_translated_len = 0; + req->path_info = NULL; + req->path_info_len = 0; + req->query_string = NULL; + req->query_string_len = 0; + zend_hash_init(&req->headers, 0, NULL, (void(*)(void*))char_ptr_dtor_p, 1); + req->content = NULL; + req->content_len = 0; + req->ext = NULL; + req->ext_len = 0; + return SUCCESS; +} /* }}} */ + +static void php_cli_server_request_dtor(php_cli_server_request *req) /* {{{ */ +{ + if (req->request_uri) { + pefree(req->request_uri, 1); + } + if (req->vpath) { + pefree(req->vpath, 1); + } + if (req->path_translated) { + pefree(req->path_translated, 1); + } + if (req->path_info) { + pefree(req->path_info, 1); + } + if (req->query_string) { + pefree(req->query_string, 1); + } + zend_hash_destroy(&req->headers); + if (req->content) { + pefree(req->content, 1); + } +} /* }}} */ + +static void php_cli_server_request_translate_vpath(php_cli_server_request *request, const char *document_root, size_t document_root_len) /* {{{ */ +{ + struct stat sb; + static const char *index_files[] = { "index.php", "index.html", NULL }; + char *buf = safe_pemalloc(1, request->vpath_len, 1 + document_root_len + 1 + sizeof("index.html"), 1); + char *p = buf, *prev_patch = 0, *q, *vpath; + size_t prev_patch_len; + int is_static_file = 0; + + memmove(p, document_root, document_root_len); + p += document_root_len; + vpath = p; + if (request->vpath_len > 0 && request->vpath[0] != '/') { + *p++ = DEFAULT_SLASH; + } + q = request->vpath + request->vpath_len; + while (q > request->vpath) { + if (*q-- == '.') { + is_static_file = 1; + break; + } + } + memmove(p, request->vpath, request->vpath_len); +#ifdef PHP_WIN32 + q = p + request->vpath_len; + do { + if (*q == '/') { + *q = '\\'; + } + } while (q-- > p); +#endif + p += request->vpath_len; + *p = '\0'; + q = p; + while (q > buf) { + if (!stat(buf, &sb)) { + if (sb.st_mode & S_IFDIR) { + const char **file = index_files; + if (q[-1] != DEFAULT_SLASH) { + *q++ = DEFAULT_SLASH; + } + while (*file) { + size_t l = strlen(*file); + memmove(q, *file, l + 1); + if (!stat(buf, &sb) && (sb.st_mode & S_IFREG)) { + q += l; + break; + } + file++; + } + if (!*file || is_static_file) { + if (prev_patch) { + pefree(prev_patch, 1); + } + pefree(buf, 1); + return; + } + } + break; /* regular file */ + } + if (prev_patch) { + pefree(prev_patch, 1); + *q = DEFAULT_SLASH; + } + while (q > buf && *(--q) != DEFAULT_SLASH); + prev_patch_len = p - q; + prev_patch = pestrndup(q, prev_patch_len, 1); + *q = '\0'; + } + if (prev_patch) { + request->path_info_len = prev_patch_len; +#ifdef PHP_WIN32 + while (prev_patch_len--) { + if (prev_patch[prev_patch_len] == '\\') { + prev_patch[prev_patch_len] = '/'; + } + } +#endif + request->path_info = prev_patch; + pefree(request->vpath, 1); + request->vpath = pestrndup(vpath, q - vpath, 1); + request->vpath_len = q - vpath; + request->path_translated = buf; + request->path_translated_len = q - buf; + } else { + pefree(request->vpath, 1); + request->vpath = pestrndup(vpath, q - vpath, 1); + request->vpath_len = q - vpath; + request->path_translated = buf; + request->path_translated_len = q - buf; + } +#ifdef PHP_WIN32 + { + uint i = 0; + for (;i<request->vpath_len;i++) { + if (request->vpath[i] == '\\') { + request->vpath[i] = '/'; + } + } + } +#endif + request->sb = sb; +} /* }}} */ + +static void normalize_vpath(char **retval, size_t *retval_len, const char *vpath, size_t vpath_len, int persistent) /* {{{ */ +{ + char *decoded_vpath = NULL; + char *decoded_vpath_end; + char *p; + + *retval = NULL; + + decoded_vpath = pestrndup(vpath, vpath_len, persistent); + if (!decoded_vpath) { + return; + } + + decoded_vpath_end = decoded_vpath + php_url_decode(decoded_vpath, vpath_len); + + p = decoded_vpath; + + if (p < decoded_vpath_end && *p == '/') { + char *n = p; + while (n < decoded_vpath_end && *n == '/') n++; + memmove(++p, n, decoded_vpath_end - n); + decoded_vpath_end -= n - p; + } + + while (p < decoded_vpath_end) { + char *n = p; + while (n < decoded_vpath_end && *n != '/') n++; + if (n - p == 2 && p[0] == '.' && p[1] == '.') { + if (p > decoded_vpath) { + --p; + for (;;) { + if (p == decoded_vpath) { + if (*p == '/') { + p++; + } + break; + } + if (*(--p) == '/') { + p++; + break; + } + } + } + while (n < decoded_vpath_end && *n == '/') n++; + memmove(p, n, decoded_vpath_end - n); + decoded_vpath_end -= n - p; + } else if (n - p == 1 && p[0] == '.') { + while (n < decoded_vpath_end && *n == '/') n++; + memmove(p, n, decoded_vpath_end - n); + decoded_vpath_end -= n - p; + } else { + if (n < decoded_vpath_end) { + char *nn = n; + while (nn < decoded_vpath_end && *nn == '/') nn++; + p = n + 1; + memmove(p, nn, decoded_vpath_end - nn); + decoded_vpath_end -= nn - p; + } else { + p = n; + } + } + } + + *decoded_vpath_end = '\0'; + *retval = decoded_vpath; + *retval_len = decoded_vpath_end - decoded_vpath; +} /* }}} */ + +/* {{{ php_cli_server_client_read_request */ +static int php_cli_server_client_read_request_on_message_begin(php_http_parser *parser) +{ + return 0; +} + +static int php_cli_server_client_read_request_on_path(php_http_parser *parser, const char *at, size_t length) +{ + php_cli_server_client *client = parser->data; + { + char *vpath; + size_t vpath_len; + normalize_vpath(&vpath, &vpath_len, at, length, 1); + client->request.vpath = vpath; + client->request.vpath_len = vpath_len; + } + return 0; +} + +static int php_cli_server_client_read_request_on_query_string(php_http_parser *parser, const char *at, size_t length) +{ + php_cli_server_client *client = parser->data; + client->request.query_string = pestrndup(at, length, 1); + client->request.query_string_len = length; + return 0; +} + +static int php_cli_server_client_read_request_on_url(php_http_parser *parser, const char *at, size_t length) +{ + php_cli_server_client *client = parser->data; + client->request.request_method = parser->method; + client->request.request_uri = pestrndup(at, length, 1); + client->request.request_uri_len = length; + return 0; +} + +static int php_cli_server_client_read_request_on_fragment(php_http_parser *parser, const char *at, size_t length) +{ + return 0; +} + +static int php_cli_server_client_read_request_on_header_field(php_http_parser *parser, const char *at, size_t length) +{ + php_cli_server_client *client = parser->data; + if (client->current_header_name_allocated) { + pefree(client->current_header_name, 1); + client->current_header_name_allocated = 0; + } + client->current_header_name = (char *)at; + client->current_header_name_len = length; + return 0; +} + +static int php_cli_server_client_read_request_on_header_value(php_http_parser *parser, const char *at, size_t length) +{ + php_cli_server_client *client = parser->data; + char *value = pestrndup(at, length, 1); + if (!value) { + return 1; + } + { + char *header_name = client->current_header_name; + size_t header_name_len = client->current_header_name_len; + char c = header_name[header_name_len]; + header_name[header_name_len] = '\0'; + zend_hash_add(&client->request.headers, header_name, header_name_len + 1, &value, sizeof(char *), NULL); + header_name[header_name_len] = c; + } + + if (client->current_header_name_allocated) { + pefree(client->current_header_name, 1); + client->current_header_name_allocated = 0; + } + return 0; +} + +static int php_cli_server_client_read_request_on_headers_complete(php_http_parser *parser) +{ + php_cli_server_client *client = parser->data; + if (client->current_header_name_allocated) { + pefree(client->current_header_name, 1); + client->current_header_name_allocated = 0; + } + client->current_header_name = NULL; + return 0; +} + +static int php_cli_server_client_read_request_on_body(php_http_parser *parser, const char *at, size_t length) +{ + php_cli_server_client *client = parser->data; + if (!client->request.content) { + client->request.content = pemalloc(parser->content_length, 1); + client->request.content_len = 0; + } + memmove(client->request.content + client->request.content_len, at, length); + client->request.content_len += length; + return 0; +} + +static int php_cli_server_client_read_request_on_message_complete(php_http_parser *parser) +{ + php_cli_server_client *client = parser->data; + client->request.protocol_version = parser->http_major * 100 + parser->http_minor; + php_cli_server_request_translate_vpath(&client->request, client->server->document_root, client->server->document_root_len); + { + const char *vpath = client->request.vpath, *end = vpath + client->request.vpath_len, *p = end; + client->request.ext = end; + client->request.ext_len = 0; + while (p > vpath) { + --p; + if (*p == '.') { + ++p; + client->request.ext = p; + client->request.ext_len = end - p; + break; + } + } + } + client->request_read = 1; + return 0; +} + +static int php_cli_server_client_read_request(php_cli_server_client *client, char **errstr TSRMLS_DC) +{ + char buf[16384]; + static const php_http_parser_settings settings = { + php_cli_server_client_read_request_on_message_begin, + php_cli_server_client_read_request_on_path, + php_cli_server_client_read_request_on_query_string, + php_cli_server_client_read_request_on_url, + php_cli_server_client_read_request_on_fragment, + php_cli_server_client_read_request_on_header_field, + php_cli_server_client_read_request_on_header_value, + php_cli_server_client_read_request_on_headers_complete, + php_cli_server_client_read_request_on_body, + php_cli_server_client_read_request_on_message_complete + }; + size_t nbytes_consumed; + int nbytes_read; + if (client->request_read) { + return 1; + } + nbytes_read = recv(client->sock, buf, sizeof(buf) - 1, 0); + if (nbytes_read < 0) { + int err = php_socket_errno(); + if (err == SOCK_EAGAIN) { + return 0; + } + *errstr = php_socket_strerror(err, NULL, 0); + return -1; + } else if (nbytes_read == 0) { + *errstr = estrdup("Unexpected EOF"); + return -1; + } + client->parser.data = client; + nbytes_consumed = php_http_parser_execute(&client->parser, &settings, buf, nbytes_read); + if (nbytes_consumed != nbytes_read) { + *errstr = estrdup("Malformed HTTP request"); + return -1; + } + if (client->current_header_name) { + char *header_name = safe_pemalloc(client->current_header_name_len, 1, 1, 1); + memmove(header_name, client->current_header_name, client->current_header_name_len); + client->current_header_name = header_name; + client->current_header_name_allocated = 1; + } + return client->request_read ? 1: 0; +} +/* }}} */ + +static size_t php_cli_server_client_send_through(php_cli_server_client *client, const char *str, size_t str_len) /* {{{ */ +{ + struct timeval tv = { 10, 0 }; + ssize_t nbytes_left = str_len; + do { + ssize_t nbytes_sent = send(client->sock, str + str_len - nbytes_left, nbytes_left, 0); + if (nbytes_sent < 0) { + int err = php_socket_errno(); + if (err == SOCK_EAGAIN) { + int nfds = php_pollfd_for(client->sock, POLLOUT, &tv); + if (nfds > 0) { + continue; + } else if (nfds < 0) { + /* error */ + php_handle_aborted_connection(); + return nbytes_left; + } else { + /* timeout */ + php_handle_aborted_connection(); + return nbytes_left; + } + } else { + php_handle_aborted_connection(); + return nbytes_left; + } + } + nbytes_left -= nbytes_sent; + } while (nbytes_left > 0); + + return str_len; +} /* }}} */ + +static void php_cli_server_client_populate_request_info(const php_cli_server_client *client, sapi_request_info *request_info) /* {{{ */ +{ + char **val; + + request_info->request_method = php_http_method_str(client->request.request_method); + request_info->proto_num = client->request.protocol_version; + request_info->request_uri = client->request.request_uri; + request_info->path_translated = client->request.path_translated; + request_info->query_string = client->request.query_string; + request_info->post_data = client->request.content; + request_info->content_length = request_info->post_data_length = client->request.content_len; + request_info->auth_user = request_info->auth_password = request_info->auth_digest = NULL; + if (SUCCESS == zend_hash_find(&client->request.headers, "Content-Type", sizeof("Content-Type"), (void**)&val)) { + request_info->content_type = *val; + } +} /* }}} */ + +static void destroy_request_info(sapi_request_info *request_info) /* {{{ */ +{ +} /* }}} */ + +static void php_cli_server_client_begin_capture(php_cli_server_client *client) /* {{{ */ +{ + php_cli_server_buffer_ctor(&client->capture_buffer); + client->capturing = 1; +} /* }}} */ + +static void php_cli_server_client_end_capture(php_cli_server_client *client) /* {{{ */ +{ + client->capturing = 0; + php_cli_server_buffer_dtor(&client->capture_buffer); +} /* }}} */ + +static int php_cli_server_client_ctor(php_cli_server_client *client, php_cli_server *server, int client_sock, struct sockaddr *addr, socklen_t addr_len TSRMLS_DC) /* {{{ */ +{ + client->server = server; + client->sock = client_sock; + client->addr = addr; + client->addr_len = addr_len; + { + char *addr_str = 0; + long addr_str_len = 0; + php_network_populate_name_from_sockaddr(addr, addr_len, &addr_str, &addr_str_len, NULL, 0 TSRMLS_CC); + client->addr_str = pestrndup(addr_str, addr_str_len, 1); + client->addr_str_len = addr_str_len; + efree(addr_str); + } + php_http_parser_init(&client->parser, PHP_HTTP_REQUEST); + client->request_read = 0; + client->current_header_name = NULL; + client->current_header_name_len = 0; + client->current_header_name_allocated = 0; + client->post_read_offset = 0; + if (FAILURE == php_cli_server_request_ctor(&client->request)) { + return FAILURE; + } + client->content_sender_initialized = 0; + client->capturing = 0; + client->file_fd = -1; + return SUCCESS; +} /* }}} */ + +static void php_cli_server_client_dtor(php_cli_server_client *client) /* {{{ */ +{ + php_cli_server_request_dtor(&client->request); + if (client->file_fd >= 0) { + close(client->file_fd); + client->file_fd = -1; + } + pefree(client->addr, 1); + pefree(client->addr_str, 1); + if (client->content_sender_initialized) { + php_cli_server_content_sender_dtor(&client->content_sender); + } + if (client->capturing) { + php_cli_server_buffer_dtor(&client->capture_buffer); + } +} /* }}} */ + +static void php_cli_server_close_connection(php_cli_server *server, php_cli_server_client *client TSRMLS_DC) /* {{{ */ +{ +#ifdef DEBUG + php_cli_server_logf("%s Closing" TSRMLS_CC, client->addr_str); +#endif + zend_hash_index_del(&server->clients, client->sock); +} /* }}} */ + +static int php_cli_server_send_error_page(php_cli_server *server, php_cli_server_client *client, int status TSRMLS_DC) /* {{{ */ +{ + char *escaped_request_uri = NULL; + size_t escaped_request_uri_len; + const char *status_string = get_status_string(status); + const char *content_template = get_template_string(status); + char *errstr = get_last_error(); + assert(status_string && content_template); + + php_cli_server_content_sender_ctor(&client->content_sender); + client->content_sender_initialized = 1; + + escaped_request_uri = php_escape_html_entities_ex((unsigned char *)client->request.request_uri, client->request.request_uri_len, &escaped_request_uri_len, 0, ENT_QUOTES, NULL, 0 TSRMLS_CC); + + { + static const char prologue_template[] = "<html><head><title>%d %s</title>"; + php_cli_server_chunk *chunk = php_cli_server_chunk_heap_new_self_contained(strlen(prologue_template) + 3 + strlen(status_string) + 1); + if (!chunk) { + goto fail; + } + snprintf(chunk->data.heap.p, chunk->data.heap.len, prologue_template, status, status_string, escaped_request_uri); + chunk->data.heap.len = strlen(chunk->data.heap.p); + php_cli_server_buffer_append(&client->content_sender.buffer, chunk); + } + { + int err = 0; + zval *style = NULL; + zend_try { + php_output_activate(TSRMLS_C); + php_output_start_user(NULL, 0, PHP_OUTPUT_HANDLER_STDFLAGS TSRMLS_CC); + php_info_print_style(TSRMLS_C); + MAKE_STD_ZVAL(style); + php_output_get_contents(style TSRMLS_CC); + php_output_discard(TSRMLS_C); + php_output_deactivate(TSRMLS_C); + if (style && Z_STRVAL_P(style)) { + char *block = pestrndup(Z_STRVAL_P(style), Z_STRLEN_P(style), 1); + php_cli_server_chunk *chunk = php_cli_server_chunk_heap_new(block, block, Z_STRLEN_P(style)); + if (!chunk) { + zval_ptr_dtor(&style); + goto fail; + } + php_cli_server_buffer_append(&client->content_sender.buffer, chunk); + zval_ptr_dtor(&style); + } else { + err = 1; + } + } zend_catch { + err = 1; + } zend_end_try(); + if (err) { + goto fail; + } + } + { + static const char template[] = "</head><body>"; + php_cli_server_chunk *chunk = php_cli_server_chunk_immortal_new(template, sizeof(template) - 1); + if (!chunk) { + goto fail; + } + php_cli_server_buffer_append(&client->content_sender.buffer, chunk); + } + { + php_cli_server_chunk *chunk = php_cli_server_chunk_heap_new_self_contained(strlen(content_template) + escaped_request_uri_len + 3 + strlen(status_string) + 1); + if (!chunk) { + goto fail; + } + snprintf(chunk->data.heap.p, chunk->data.heap.len, content_template, status_string, escaped_request_uri); + chunk->data.heap.len = strlen(chunk->data.heap.p); + php_cli_server_buffer_append(&client->content_sender.buffer, chunk); + } + { + static const char epilogue_template[] = "</body></html>"; + php_cli_server_chunk *chunk = php_cli_server_chunk_immortal_new(epilogue_template, sizeof(epilogue_template) - 1); + if (!chunk) { + goto fail; + } + php_cli_server_buffer_append(&client->content_sender.buffer, chunk); + } + + { + php_cli_server_chunk *chunk; + smart_str buffer = { 0 }; + append_http_status_line(&buffer, client->request.protocol_version, status, 1); + if (!buffer.c) { + /* out of memory */ + goto fail; + } + append_essential_headers(&buffer, client, 1); + smart_str_appends_ex(&buffer, "Content-Type: text/html; charset=UTF-8\r\n", 1); + smart_str_appends_ex(&buffer, "Content-Length: ", 1); + smart_str_append_generic_ex(&buffer, php_cli_server_buffer_size(&client->content_sender.buffer), 1, size_t, _unsigned); + smart_str_appendl_ex(&buffer, "\r\n", 2, 1); + smart_str_appendl_ex(&buffer, "\r\n", 2, 1); + + chunk = php_cli_server_chunk_heap_new(buffer.c, buffer.c, buffer.len); + if (!chunk) { + smart_str_free_ex(&buffer, 1); + goto fail; + } + php_cli_server_buffer_prepend(&client->content_sender.buffer, chunk); + } + + php_cli_server_log_response(client, status, errstr ? errstr : "?" TSRMLS_CC); + php_cli_server_poller_add(&server->poller, POLLOUT, client->sock); + if (errstr) { + pefree(errstr, 1); + } + efree(escaped_request_uri); + return SUCCESS; + +fail: + efree(escaped_request_uri); + return FAILURE; +} /* }}} */ + +static int php_cli_server_dispatch_script(php_cli_server *server, php_cli_server_client *client TSRMLS_DC) /* {{{ */ +{ + if (strlen(client->request.path_translated) != client->request.path_translated_len) { + /* can't handle paths that contain nul bytes */ + return php_cli_server_send_error_page(server, client, 400 TSRMLS_CC); + } + { + zend_file_handle zfd; + zfd.type = ZEND_HANDLE_FILENAME; + zfd.filename = SG(request_info).path_translated; + zfd.handle.fp = NULL; + zfd.free_filename = 0; + zfd.opened_path = NULL; + zend_try { + php_execute_script(&zfd TSRMLS_CC); + } zend_end_try(); + } + + php_cli_server_log_response(client, SG(sapi_headers).http_response_code, NULL TSRMLS_CC); + return SUCCESS; +} /* }}} */ + +static int php_cli_server_begin_send_static(php_cli_server *server, php_cli_server_client *client TSRMLS_DC) /* {{{ */ +{ + int fd; + int status = 200; + + if (client->request.path_translated && strlen(client->request.path_translated) != client->request.path_translated_len) { + /* can't handle paths that contain nul bytes */ + return php_cli_server_send_error_page(server, client, 400 TSRMLS_CC); + } + + fd = client->request.path_translated ? open(client->request.path_translated, O_RDONLY): -1; + if (fd < 0) { + return php_cli_server_send_error_page(server, client, 404 TSRMLS_CC); + } + + php_cli_server_content_sender_ctor(&client->content_sender); + client->content_sender_initialized = 1; + client->file_fd = fd; + + { + php_cli_server_chunk *chunk; + smart_str buffer = { 0 }; + const char *mime_type = get_mime_type(client->request.ext, client->request.ext_len); + if (!mime_type) { + mime_type = "application/octet-stream"; + } + + append_http_status_line(&buffer, client->request.protocol_version, status, 1); + if (!buffer.c) { + /* out of memory */ + php_cli_server_log_response(client, 500, NULL TSRMLS_CC); + return FAILURE; + } + append_essential_headers(&buffer, client, 1); + smart_str_appendl_ex(&buffer, "Content-Type: ", sizeof("Content-Type: ") - 1, 1); + smart_str_appends_ex(&buffer, mime_type, 1); + if (strncmp(mime_type, "text/", 5) == 0) { + smart_str_appends_ex(&buffer, "; charset=UTF-8", 1); + } + smart_str_appendl_ex(&buffer, "\r\n", 2, 1); + smart_str_appends_ex(&buffer, "Content-Length: ", 1); + smart_str_append_generic_ex(&buffer, client->request.sb.st_size, 1, size_t, _unsigned); + smart_str_appendl_ex(&buffer, "\r\n", 2, 1); + smart_str_appendl_ex(&buffer, "\r\n", 2, 1); + chunk = php_cli_server_chunk_heap_new(buffer.c, buffer.c, buffer.len); + if (!chunk) { + smart_str_free_ex(&buffer, 1); + php_cli_server_log_response(client, 500, NULL TSRMLS_CC); + return FAILURE; + } + php_cli_server_buffer_append(&client->content_sender.buffer, chunk); + } + php_cli_server_log_response(client, 200, NULL TSRMLS_CC); + php_cli_server_poller_add(&server->poller, POLLOUT, client->sock); + return SUCCESS; +} +/* }}} */ + +static int php_cli_server_request_startup(php_cli_server *server, php_cli_server_client *client TSRMLS_DC) { /* {{{ */ + char **auth; + php_cli_server_client_populate_request_info(client, &SG(request_info)); + if (SUCCESS == zend_hash_find(&client->request.headers, "Authorization", sizeof("Authorization"), (void**)&auth)) { + php_handle_auth_data(*auth TSRMLS_CC); + } + SG(sapi_headers).http_response_code = 200; + if (FAILURE == php_request_startup(TSRMLS_C)) { + /* should never be happen */ + destroy_request_info(&SG(request_info)); + return FAILURE; + } + PG(during_request_startup) = 0; + + return SUCCESS; +} +/* }}} */ + +static int php_cli_server_request_shutdown(php_cli_server *server, php_cli_server_client *client TSRMLS_DC) { /* {{{ */ + php_request_shutdown(0); + php_cli_server_close_connection(server, client TSRMLS_CC); + destroy_request_info(&SG(request_info)); + SG(server_context) = NULL; + SG(rfc1867_uploaded_files) = NULL; + return SUCCESS; +} +/* }}} */ + +static int php_cli_server_dispatch_router(php_cli_server *server, php_cli_server_client *client TSRMLS_DC) /* {{{ */ +{ + int decline = 0; + if (!php_handle_special_queries(TSRMLS_C)) { + zend_file_handle zfd; + char *old_cwd; + + ALLOCA_FLAG(use_heap) + old_cwd = do_alloca(MAXPATHLEN, use_heap); + old_cwd[0] = '\0'; + php_ignore_value(VCWD_GETCWD(old_cwd, MAXPATHLEN - 1)); + + zfd.type = ZEND_HANDLE_FILENAME; + zfd.filename = server->router; + zfd.handle.fp = NULL; + zfd.free_filename = 0; + zfd.opened_path = NULL; + + zend_try { + zval *retval = NULL; + if (SUCCESS == zend_execute_scripts(ZEND_REQUIRE TSRMLS_CC, &retval, 1, &zfd)) { + if (retval) { + decline = Z_TYPE_P(retval) == IS_BOOL && !Z_LVAL_P(retval); + zval_ptr_dtor(&retval); + } + } else { + decline = 1; + } + } zend_end_try(); + + if (old_cwd[0] != '\0') { + php_ignore_value(VCWD_CHDIR(old_cwd)); + } + + free_alloca(old_cwd, use_heap); + } + + return decline; +} +/* }}} */ + +static int php_cli_server_dispatch(php_cli_server *server, php_cli_server_client *client TSRMLS_DC) /* {{{ */ +{ + int is_static_file = 0; + + SG(server_context) = client; + if (client->request.ext_len != 3 || memcmp(client->request.ext, "php", 3) || !client->request.path_translated) { + is_static_file = 1; + } + + if (server->router || !is_static_file) { + if (FAILURE == php_cli_server_request_startup(server, client TSRMLS_CC)) { + SG(server_context) = NULL; + php_cli_server_close_connection(server, client TSRMLS_CC); + destroy_request_info(&SG(request_info)); + return SUCCESS; + } + } + + if (server->router) { + if (!php_cli_server_dispatch_router(server, client TSRMLS_CC)) { + php_cli_server_request_shutdown(server, client TSRMLS_CC); + return SUCCESS; + } + } + + if (!is_static_file) { + if (SUCCESS == php_cli_server_dispatch_script(server, client TSRMLS_CC) + || SUCCESS != php_cli_server_send_error_page(server, client, 500 TSRMLS_CC)) { + php_cli_server_request_shutdown(server, client TSRMLS_CC); + return SUCCESS; + } + } else { + if (server->router) { + static int (*send_header_func)(sapi_headers_struct * TSRMLS_DC); + send_header_func = sapi_module.send_headers; + /* we don't want the header to be sent now */ + sapi_module.send_headers = sapi_cli_server_discard_headers; + php_request_shutdown(0); + sapi_module.send_headers = send_header_func; + SG(rfc1867_uploaded_files) = NULL; + } + if (SUCCESS != php_cli_server_begin_send_static(server, client TSRMLS_CC)) { + php_cli_server_close_connection(server, client TSRMLS_CC); + } + SG(server_context) = NULL; + return SUCCESS; + } + + SG(server_context) = NULL; + destroy_request_info(&SG(request_info)); + return SUCCESS; +} +/* }}} */ + +static void php_cli_server_dtor(php_cli_server *server TSRMLS_DC) /* {{{ */ +{ + zend_hash_destroy(&server->clients); + if (server->server_sock >= 0) { + closesocket(server->server_sock); + } + if (server->host) { + pefree(server->host, 1); + } + if (server->document_root) { + pefree(server->document_root, 1); + } + if (server->router) { + pefree(server->router, 1); + } +} /* }}} */ + +static void php_cli_server_client_dtor_wrapper(php_cli_server_client **p) /* {{{ */ +{ + closesocket((*p)->sock); + php_cli_server_poller_remove(&(*p)->server->poller, POLLIN | POLLOUT, (*p)->sock); + php_cli_server_client_dtor(*p); + pefree(*p, 1); +} /* }}} */ + +static int php_cli_server_ctor(php_cli_server *server, const char *addr, const char *document_root, const char *router TSRMLS_DC) /* {{{ */ +{ + int retval = SUCCESS; + char *host = NULL; + char *errstr = NULL; + char *_document_root = NULL; + char *_router = NULL; + int err = 0; + int port = 3000; + php_socket_t server_sock = SOCK_ERR; + char *p = NULL; + + if (addr[0] == '[') { + host = pestrdup(addr + 1, 1); + if (!host) { + return FAILURE; + } + p = strchr(host, ']'); + if (p) { + *p++ = '\0'; + if (*p == ':') { + port = strtol(p + 1, &p, 10); + if (port <= 0) { + p = NULL; + } + } else if (*p != '\0') { + p = NULL; + } + } + } else { + host = pestrdup(addr, 1); + if (!host) { + return FAILURE; + } + p = strchr(host, ':'); + if (p) { + *p++ = '\0'; + port = strtol(p, &p, 10); + if (port <= 0) { + p = NULL; + } + } + } + if (!p) { + fprintf(stderr, "Invalid address: %s\n", addr); + retval = FAILURE; + goto out; + } + + server_sock = php_network_listen_socket(host, &port, SOCK_STREAM, &server->address_family, &server->socklen, &errstr TSRMLS_CC); + if (server_sock == SOCK_ERR) { + php_cli_server_logf("Failed to listen on %s:%d (reason: %s)" TSRMLS_CC, host, port, errstr ? errstr: "?"); + efree(errstr); + retval = FAILURE; + goto out; + } + server->server_sock = server_sock; + + err = php_cli_server_poller_ctor(&server->poller); + if (SUCCESS != err) { + goto out; + } + + php_cli_server_poller_add(&server->poller, POLLIN, server_sock); + + server->host = host; + server->port = port; + + zend_hash_init(&server->clients, 0, NULL, (void(*)(void*))php_cli_server_client_dtor_wrapper, 1); + + { + size_t document_root_len = strlen(document_root); + _document_root = pestrndup(document_root, document_root_len, 1); + if (!_document_root) { + retval = FAILURE; + goto out; + } + server->document_root = _document_root; + server->document_root_len = document_root_len; + } + + if (router) { + size_t router_len = strlen(router); + _router = pestrndup(router, router_len, 1); + if (!_router) { + retval = FAILURE; + goto out; + } + server->router = _router; + server->router_len = router_len; + } else { + server->router = NULL; + server->router_len = 0; + } + + server->is_running = 1; +out: + if (retval != SUCCESS) { + if (host) { + pefree(host, 1); + } + if (_document_root) { + pefree(_document_root, 1); + } + if (_router) { + pefree(_router, 1); + } + if (server_sock >= -1) { + closesocket(server_sock); + } + } + return retval; +} /* }}} */ + +static int php_cli_server_recv_event_read_request(php_cli_server *server, php_cli_server_client *client TSRMLS_DC) /* {{{ */ +{ + char *errstr = NULL; + int status = php_cli_server_client_read_request(client, &errstr TSRMLS_CC); + if (status < 0) { + php_cli_server_logf("%s Invalid request (%s)" TSRMLS_CC, client->addr_str, errstr); + efree(errstr); + php_cli_server_close_connection(server, client TSRMLS_CC); + return FAILURE; + } else if (status == 1) { + php_cli_server_poller_remove(&server->poller, POLLIN, client->sock); + php_cli_server_dispatch(server, client TSRMLS_CC); + } else { + php_cli_server_poller_add(&server->poller, POLLIN, client->sock); + } + + return SUCCESS; +} /* }}} */ + +static int php_cli_server_send_event(php_cli_server *server, php_cli_server_client *client TSRMLS_DC) /* {{{ */ +{ + if (client->content_sender_initialized) { + if (client->file_fd >= 0 && !client->content_sender.buffer.first) { + size_t nbytes_read; + if (php_cli_server_content_sender_pull(&client->content_sender, client->file_fd, &nbytes_read)) { + php_cli_server_close_connection(server, client TSRMLS_CC); + return FAILURE; + } + if (nbytes_read == 0) { + close(client->file_fd); + client->file_fd = -1; + } + } + { + size_t nbytes_sent; + int err = php_cli_server_content_sender_send(&client->content_sender, client->sock, &nbytes_sent); + if (err && err != SOCK_EAGAIN) { + php_cli_server_close_connection(server, client TSRMLS_CC); + return FAILURE; + } + } + if (!client->content_sender.buffer.first && client->file_fd < 0) { + php_cli_server_close_connection(server, client TSRMLS_CC); + } + } + return SUCCESS; +} +/* }}} */ + +typedef struct php_cli_server_do_event_for_each_fd_callback_params { +#ifdef ZTS + void ***tsrm_ls; +#endif + php_cli_server *server; + int(*rhandler)(php_cli_server*, php_cli_server_client* TSRMLS_DC); + int(*whandler)(php_cli_server*, php_cli_server_client* TSRMLS_DC); +} php_cli_server_do_event_for_each_fd_callback_params; + +static int php_cli_server_do_event_for_each_fd_callback(void *_params, int fd, int event) /* {{{ */ +{ + php_cli_server_do_event_for_each_fd_callback_params *params = _params; +#ifdef ZTS + void ***tsrm_ls = params->tsrm_ls; +#endif + php_cli_server *server = params->server; + if (server->server_sock == fd) { + php_cli_server_client *client = NULL; + php_socket_t client_sock; + socklen_t socklen = server->socklen; + struct sockaddr *sa = pemalloc(server->socklen, 1); + if (!sa) { + return FAILURE; + } + client_sock = accept(server->server_sock, sa, &socklen); + if (client_sock < 0) { + char *errstr; + errstr = php_socket_strerror(php_socket_errno(), NULL, 0); + php_cli_server_logf("Failed to accept a client (reason: %s)" TSRMLS_CC, errstr); + efree(errstr); + pefree(sa, 1); + return SUCCESS; + } + if (SUCCESS != php_set_sock_blocking(client_sock, 0 TSRMLS_CC)) { + pefree(sa, 1); + closesocket(client_sock); + return SUCCESS; + } + if (!(client = pemalloc(sizeof(php_cli_server_client), 1)) || FAILURE == php_cli_server_client_ctor(client, server, client_sock, sa, socklen TSRMLS_CC)) { + php_cli_server_logf("Failed to create a new request object" TSRMLS_CC); + pefree(sa, 1); + closesocket(client_sock); + return SUCCESS; + } +#ifdef DEBUG + php_cli_server_logf("%s Accepted" TSRMLS_CC, client->addr_str); +#endif + zend_hash_index_update(&server->clients, client_sock, &client, sizeof(client), NULL); + php_cli_server_recv_event_read_request(server, client TSRMLS_CC); + } else { + php_cli_server_client **client; + if (SUCCESS == zend_hash_index_find(&server->clients, fd, (void **)&client)) { + if (event & POLLIN) { + params->rhandler(server, *client TSRMLS_CC); + } + if (event & POLLOUT) { + params->whandler(server, *client TSRMLS_CC); + } + } + } + return SUCCESS; +} /* }}} */ + +static void php_cli_server_do_event_for_each_fd(php_cli_server *server, int(*rhandler)(php_cli_server*, php_cli_server_client* TSRMLS_DC), int(*whandler)(php_cli_server*, php_cli_server_client* TSRMLS_DC) TSRMLS_DC) /* {{{ */ +{ + php_cli_server_do_event_for_each_fd_callback_params params = { +#ifdef ZTS + tsrm_ls, +#endif + server, + rhandler, + whandler + }; + + php_cli_server_poller_iter_on_active(&server->poller, ¶ms, php_cli_server_do_event_for_each_fd_callback); +} /* }}} */ + +static int php_cli_server_do_event_loop(php_cli_server *server TSRMLS_DC) /* {{{ */ +{ + int retval = SUCCESS; + while (server->is_running) { + static const struct timeval tv = { 1, 0 }; + int n = php_cli_server_poller_poll(&server->poller, &tv); + if (n > 0) { + php_cli_server_do_event_for_each_fd(server, + php_cli_server_recv_event_read_request, + php_cli_server_send_event TSRMLS_CC); + } else if (n == 0) { + /* do nothing */ + } else { + int err = php_socket_errno(); + if (err != SOCK_EINTR) { + char *errstr = php_socket_strerror(err, NULL, 0); + php_cli_server_logf("%s" TSRMLS_CC, errstr); + efree(errstr); + retval = FAILURE; + goto out; + } + } + } +out: + return retval; +} /* }}} */ + +static php_cli_server server; + +static void php_cli_server_sigint_handler(int sig) /* {{{ */ +{ + server.is_running = 0; +} +/* }}} */ + +int do_cli_server(int argc, char **argv TSRMLS_DC) /* {{{ */ +{ + char *php_optarg = NULL; + int php_optind = 1; + int c; + const char *server_bind_address = NULL; + extern const opt_struct OPTIONS[]; + const char *document_root = NULL; + const char *router = NULL; + char document_root_buf[MAXPATHLEN]; + + while ((c = php_getopt(argc, argv, OPTIONS, &php_optarg, &php_optind, 0, 2))!=-1) { + switch (c) { + case 'S': + server_bind_address = php_optarg; + break; + case 't': + document_root = php_optarg; + break; + } + } + + if (document_root) { + struct stat sb; + + if (stat(document_root, &sb)) { + fprintf(stderr, "Directory %s does not exist.\n", document_root); + return 1; + } + if (!S_ISDIR(sb.st_mode)) { + fprintf(stderr, "%s is not a directory.\n", document_root); + return 1; + } + if (VCWD_REALPATH(document_root, document_root_buf)) { + document_root = document_root_buf; + } + } else { + char *ret = NULL; + +#if HAVE_GETCWD + ret = VCWD_GETCWD(document_root_buf, MAXPATHLEN); +#elif HAVE_GETWD + ret = VCWD_GETWD(document_root_buf); +#endif + document_root = ret ? document_root_buf: "."; + } + + if (argc > php_optind) { + router = argv[php_optind]; + } + + if (FAILURE == php_cli_server_ctor(&server, server_bind_address, document_root, router TSRMLS_CC)) { + return 1; + } + sapi_module.phpinfo_as_text = 0; + + { + struct timeval tv; + struct tm tm; + char buf[52]; + gettimeofday(&tv, NULL); + php_localtime_r(&tv.tv_sec, &tm); + php_asctime_r(&tm, buf); + printf("PHP %s Development Server started at %s" + "Listening on %s\n" + "Document root is %s\n" + "Press Ctrl-C to quit.\n", + PHP_VERSION, buf, server_bind_address, document_root); + } + +#if defined(HAVE_SIGNAL_H) && defined(SIGINT) + signal(SIGINT, php_cli_server_sigint_handler); +#endif + php_cli_server_do_event_loop(&server TSRMLS_CC); + php_cli_server_dtor(&server TSRMLS_CC); + return 0; +} /* }}} */ + +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * End: + * vim600: noet sw=4 ts=4 fdm=marker + * vim<600: noet sw=4 ts=4 + */ diff --git a/sapi/cli/php_cli_readline.h b/sapi/cli/php_cli_server.h index d62bb7d50..889ebf6f7 100644 --- a/sapi/cli/php_cli_readline.h +++ b/sapi/cli/php_cli_server.h @@ -12,14 +12,37 @@ | obtain it through the world-wide-web, please send a note to | | license@php.net so we can mail you a copy immediately. | +----------------------------------------------------------------------+ - | Author: Marcus Boerger <helly@php.net> | + | Author: Moriyoshi Koizumi <moriyoshi@php.net> | +----------------------------------------------------------------------+ */ -/* $Id: php_cli_readline.h 321634 2012-01-01 13:15:04Z felipe $ */ +/* $Id: php_cli.c 306938 2011-01-01 02:17:06Z felipe $ */ -#include "php.h" +#ifndef PHP_CLI_SERVER_H +#define PHP_CLI_SERVER_H -int cli_is_valid_code(char *code, int len, char **prompt TSRMLS_DC); +#include "SAPI.h" -char **cli_code_completion(const char *text, int start, int end); +extern sapi_module_struct cli_server_sapi_module; +extern int do_cli_server(int argc, char **argv TSRMLS_DC); + +ZEND_BEGIN_MODULE_GLOBALS(cli_server) + short color; +ZEND_END_MODULE_GLOBALS(cli_server) + +#ifdef ZTS +#define CLI_SERVER_G(v) TSRMG(cli_server_globals_id, zend_cli_server_globals *, v) +#else +#define CLI_SERVER_G(v) (cli_server_globals.v) +#endif + +#endif /* PHP_CLI_SERVER_H */ + +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * End: + * vim600: noet sw=4 ts=4 fdm=marker + * vim<600: noet sw=4 ts=4 + */ diff --git a/sapi/cli/php_http_parser.c b/sapi/cli/php_http_parser.c new file mode 100644 index 000000000..13b9ea12b --- /dev/null +++ b/sapi/cli/php_http_parser.c @@ -0,0 +1,1604 @@ +/* Copyright 2009,2010 Ryan Dahl <ry@tinyclouds.org> + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ +#include <assert.h> +#include <stddef.h> +#include "php_http_parser.h" + + +#ifndef MIN +# define MIN(a,b) ((a) < (b) ? (a) : (b)) +#endif + + +#define CALLBACK2(FOR) \ +do { \ + if (settings->on_##FOR) { \ + if (0 != settings->on_##FOR(parser)) return (p - data); \ + } \ +} while (0) + + +#define MARK(FOR) \ +do { \ + FOR##_mark = p; \ +} while (0) + +#define CALLBACK_NOCLEAR(FOR) \ +do { \ + if (FOR##_mark) { \ + if (settings->on_##FOR) { \ + if (0 != settings->on_##FOR(parser, \ + FOR##_mark, \ + p - FOR##_mark)) \ + { \ + return (p - data); \ + } \ + } \ + } \ +} while (0) + +#ifdef PHP_WIN32 +# undef CALLBACK +#endif +#define CALLBACK(FOR) \ +do { \ + CALLBACK_NOCLEAR(FOR); \ + FOR##_mark = NULL; \ +} while (0) + + +#define PROXY_CONNECTION "proxy-connection" +#define CONNECTION "connection" +#define CONTENT_LENGTH "content-length" +#define TRANSFER_ENCODING "transfer-encoding" +#define UPGRADE "upgrade" +#define CHUNKED "chunked" +#define KEEP_ALIVE "keep-alive" +#define CLOSE "close" + + +static const char *method_strings[] = + { "DELETE" + , "GET" + , "HEAD" + , "POST" + , "PUT" + , "CONNECT" + , "OPTIONS" + , "TRACE" + , "COPY" + , "LOCK" + , "MKCOL" + , "MOVE" + , "PROPFIND" + , "PROPPATCH" + , "UNLOCK" + , "REPORT" + , "MKACTIVITY" + , "CHECKOUT" + , "MERGE" + , "M-SEARCH" + , "NOTIFY" + , "SUBSCRIBE" + , "UNSUBSCRIBE" + }; + + +/* Tokens as defined by rfc 2616. Also lowercases them. + * token = 1*<any CHAR except CTLs or separators> + * separators = "(" | ")" | "<" | ">" | "@" + * | "," | ";" | ":" | "\" | <"> + * | "/" | "[" | "]" | "?" | "=" + * | "{" | "}" | SP | HT + */ +static const char tokens[256] = { +/* 0 nul 1 soh 2 stx 3 etx 4 eot 5 enq 6 ack 7 bel */ + 0, 0, 0, 0, 0, 0, 0, 0, +/* 8 bs 9 ht 10 nl 11 vt 12 np 13 cr 14 so 15 si */ + 0, 0, 0, 0, 0, 0, 0, 0, +/* 16 dle 17 dc1 18 dc2 19 dc3 20 dc4 21 nak 22 syn 23 etb */ + 0, 0, 0, 0, 0, 0, 0, 0, +/* 24 can 25 em 26 sub 27 esc 28 fs 29 gs 30 rs 31 us */ + 0, 0, 0, 0, 0, 0, 0, 0, +/* 32 sp 33 ! 34 " 35 # 36 $ 37 % 38 & 39 ' */ + ' ', '!', '"', '#', '$', '%', '&', '\'', +/* 40 ( 41 ) 42 * 43 + 44 , 45 - 46 . 47 / */ + 0, 0, '*', '+', 0, '-', '.', '/', +/* 48 0 49 1 50 2 51 3 52 4 53 5 54 6 55 7 */ + '0', '1', '2', '3', '4', '5', '6', '7', +/* 56 8 57 9 58 : 59 ; 60 < 61 = 62 > 63 ? */ + '8', '9', 0, 0, 0, 0, 0, 0, +/* 64 @ 65 A 66 B 67 C 68 D 69 E 70 F 71 G */ + 0, 'a', 'b', 'c', 'd', 'e', 'f', 'g', +/* 72 H 73 I 74 J 75 K 76 L 77 M 78 N 79 O */ + 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', +/* 80 P 81 Q 82 R 83 S 84 T 85 U 86 V 87 W */ + 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', +/* 88 X 89 Y 90 Z 91 [ 92 \ 93 ] 94 ^ 95 _ */ + 'x', 'y', 'z', 0, 0, 0, '^', '_', +/* 96 ` 97 a 98 b 99 c 100 d 101 e 102 f 103 g */ + '`', 'a', 'b', 'c', 'd', 'e', 'f', 'g', +/* 104 h 105 i 106 j 107 k 108 l 109 m 110 n 111 o */ + 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', +/* 112 p 113 q 114 r 115 s 116 t 117 u 118 v 119 w */ + 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', +/* 120 x 121 y 122 z 123 { 124 | 125 } 126 ~ 127 del */ + 'x', 'y', 'z', 0, '|', '}', '~', 0 }; + + +static const int8_t unhex[256] = + {-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1 + ,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1 + ,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1 + , 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,-1,-1,-1,-1,-1,-1 + ,-1,10,11,12,13,14,15,-1,-1,-1,-1,-1,-1,-1,-1,-1 + ,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1 + ,-1,10,11,12,13,14,15,-1,-1,-1,-1,-1,-1,-1,-1,-1 + ,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1 + }; + + +static const uint8_t normal_url_char[256] = { +/* 0 nul 1 soh 2 stx 3 etx 4 eot 5 enq 6 ack 7 bel */ + 0, 0, 0, 0, 0, 0, 0, 0, +/* 8 bs 9 ht 10 nl 11 vt 12 np 13 cr 14 so 15 si */ + 0, 0, 0, 0, 0, 0, 0, 0, +/* 16 dle 17 dc1 18 dc2 19 dc3 20 dc4 21 nak 22 syn 23 etb */ + 0, 0, 0, 0, 0, 0, 0, 0, +/* 24 can 25 em 26 sub 27 esc 28 fs 29 gs 30 rs 31 us */ + 0, 0, 0, 0, 0, 0, 0, 0, +/* 32 sp 33 ! 34 " 35 # 36 $ 37 % 38 & 39 ' */ + 0, 1, 1, 0, 1, 1, 1, 1, +/* 40 ( 41 ) 42 * 43 + 44 , 45 - 46 . 47 / */ + 1, 1, 1, 1, 1, 1, 1, 1, +/* 48 0 49 1 50 2 51 3 52 4 53 5 54 6 55 7 */ + 1, 1, 1, 1, 1, 1, 1, 1, +/* 56 8 57 9 58 : 59 ; 60 < 61 = 62 > 63 ? */ + 1, 1, 1, 1, 1, 1, 1, 0, +/* 64 @ 65 A 66 B 67 C 68 D 69 E 70 F 71 G */ + 1, 1, 1, 1, 1, 1, 1, 1, +/* 72 H 73 I 74 J 75 K 76 L 77 M 78 N 79 O */ + 1, 1, 1, 1, 1, 1, 1, 1, +/* 80 P 81 Q 82 R 83 S 84 T 85 U 86 V 87 W */ + 1, 1, 1, 1, 1, 1, 1, 1, +/* 88 X 89 Y 90 Z 91 [ 92 \ 93 ] 94 ^ 95 _ */ + 1, 1, 1, 1, 1, 1, 1, 1, +/* 96 ` 97 a 98 b 99 c 100 d 101 e 102 f 103 g */ + 1, 1, 1, 1, 1, 1, 1, 1, +/* 104 h 105 i 106 j 107 k 108 l 109 m 110 n 111 o */ + 1, 1, 1, 1, 1, 1, 1, 1, +/* 112 p 113 q 114 r 115 s 116 t 117 u 118 v 119 w */ + 1, 1, 1, 1, 1, 1, 1, 1, +/* 120 x 121 y 122 z 123 { 124 | 125 } 126 ~ 127 del */ + 1, 1, 1, 1, 1, 1, 1, 0 }; + + +enum state + { s_dead = 1 /* important that this is > 0 */ + + , s_start_req_or_res + , s_res_or_resp_H + , s_start_res + , s_res_H + , s_res_HT + , s_res_HTT + , s_res_HTTP + , s_res_first_http_major + , s_res_http_major + , s_res_first_http_minor + , s_res_http_minor + , s_res_first_status_code + , s_res_status_code + , s_res_status + , s_res_line_almost_done + + , s_start_req + + , s_req_method + , s_req_spaces_before_url + , s_req_schema + , s_req_schema_slash + , s_req_schema_slash_slash + , s_req_host + , s_req_port + , s_req_path + , s_req_query_string_start + , s_req_query_string + , s_req_fragment_start + , s_req_fragment + , s_req_http_start + , s_req_http_H + , s_req_http_HT + , s_req_http_HTT + , s_req_http_HTTP + , s_req_first_http_major + , s_req_http_major + , s_req_first_http_minor + , s_req_http_minor + , s_req_line_almost_done + + , s_header_field_start + , s_header_field + , s_header_value_start + , s_header_value + + , s_header_almost_done + + , s_headers_almost_done + /* Important: 's_headers_almost_done' must be the last 'header' state. All + * states beyond this must be 'body' states. It is used for overflow + * checking. See the PARSING_HEADER() macro. + */ + , s_chunk_size_start + , s_chunk_size + , s_chunk_size_almost_done + , s_chunk_parameters + , s_chunk_data + , s_chunk_data_almost_done + , s_chunk_data_done + + , s_body_identity + , s_body_identity_eof + }; + + +#define PARSING_HEADER(state) (state <= s_headers_almost_done && 0 == (parser->flags & F_TRAILING)) + + +enum header_states + { h_general = 0 + , h_C + , h_CO + , h_CON + + , h_matching_connection + , h_matching_proxy_connection + , h_matching_content_length + , h_matching_transfer_encoding + , h_matching_upgrade + + , h_connection + , h_content_length + , h_transfer_encoding + , h_upgrade + + , h_matching_transfer_encoding_chunked + , h_matching_connection_keep_alive + , h_matching_connection_close + + , h_transfer_encoding_chunked + , h_connection_keep_alive + , h_connection_close + }; + + +enum flags + { F_CHUNKED = 1 << 0 + , F_CONNECTION_KEEP_ALIVE = 1 << 1 + , F_CONNECTION_CLOSE = 1 << 2 + , F_TRAILING = 1 << 3 + , F_UPGRADE = 1 << 4 + , F_SKIPBODY = 1 << 5 + }; + + +#define CR '\r' +#define LF '\n' +#define LOWER(c) (unsigned char)(c | 0x20) +#define TOKEN(c) tokens[(unsigned char)c] + + +#define start_state (parser->type == PHP_HTTP_REQUEST ? s_start_req : s_start_res) + + +#if HTTP_PARSER_STRICT +# define STRICT_CHECK(cond) if (cond) goto error +# define NEW_MESSAGE() (http_should_keep_alive(parser) ? start_state : s_dead) +#else +# define STRICT_CHECK(cond) +# define NEW_MESSAGE() start_state +#endif + + +size_t php_http_parser_execute (php_http_parser *parser, + const php_http_parser_settings *settings, + const char *data, + size_t len) +{ + char c, ch; + const char *p = data, *pe; + size_t to_read; + + enum state state = (enum state) parser->state; + enum header_states header_state = (enum header_states) parser->header_state; + uint32_t index = parser->index; + uint32_t nread = parser->nread; + + /* technically we could combine all of these (except for url_mark) into one + variable, saving stack space, but it seems more clear to have them + separated. */ + const char *header_field_mark = 0; + const char *header_value_mark = 0; + const char *fragment_mark = 0; + const char *query_string_mark = 0; + const char *path_mark = 0; + const char *url_mark = 0; + + if (len == 0) { + if (state == s_body_identity_eof) { + CALLBACK2(message_complete); + } + return 0; + } + + if (state == s_header_field) + header_field_mark = data; + if (state == s_header_value) + header_value_mark = data; + if (state == s_req_fragment) + fragment_mark = data; + if (state == s_req_query_string) + query_string_mark = data; + if (state == s_req_path) + path_mark = data; + if (state == s_req_path || state == s_req_schema || state == s_req_schema_slash + || state == s_req_schema_slash_slash || state == s_req_port + || state == s_req_query_string_start || state == s_req_query_string + || state == s_req_host + || state == s_req_fragment_start || state == s_req_fragment) + url_mark = data; + + for (p=data, pe=data+len; p != pe; p++) { + ch = *p; + + if (PARSING_HEADER(state)) { + ++nread; + /* Buffer overflow attack */ + if (nread > PHP_HTTP_MAX_HEADER_SIZE) goto error; + } + + switch (state) { + + case s_dead: + /* this state is used after a 'Connection: close' message + * the parser will error out if it reads another message + */ + goto error; + + case s_start_req_or_res: + { + if (ch == CR || ch == LF) + break; + parser->flags = 0; + parser->content_length = -1; + + CALLBACK2(message_begin); + + if (ch == 'H') + state = s_res_or_resp_H; + else { + parser->type = PHP_HTTP_REQUEST; + goto start_req_method_assign; + } + break; + } + + case s_res_or_resp_H: + if (ch == 'T') { + parser->type = PHP_HTTP_RESPONSE; + state = s_res_HT; + } else { + if (ch != 'E') goto error; + parser->type = PHP_HTTP_REQUEST; + parser->method = PHP_HTTP_HEAD; + index = 2; + state = s_req_method; + } + break; + + case s_start_res: + { + parser->flags = 0; + parser->content_length = -1; + + CALLBACK2(message_begin); + + switch (ch) { + case 'H': + state = s_res_H; + break; + + case CR: + case LF: + break; + + default: + goto error; + } + break; + } + + case s_res_H: + STRICT_CHECK(ch != 'T'); + state = s_res_HT; + break; + + case s_res_HT: + STRICT_CHECK(ch != 'T'); + state = s_res_HTT; + break; + + case s_res_HTT: + STRICT_CHECK(ch != 'P'); + state = s_res_HTTP; + break; + + case s_res_HTTP: + STRICT_CHECK(ch != '/'); + state = s_res_first_http_major; + break; + + case s_res_first_http_major: + if (ch < '1' || ch > '9') goto error; + parser->http_major = ch - '0'; + state = s_res_http_major; + break; + + /* major HTTP version or dot */ + case s_res_http_major: + { + if (ch == '.') { + state = s_res_first_http_minor; + break; + } + + if (ch < '0' || ch > '9') goto error; + + parser->http_major *= 10; + parser->http_major += ch - '0'; + + if (parser->http_major > 999) goto error; + break; + } + + /* first digit of minor HTTP version */ + case s_res_first_http_minor: + if (ch < '0' || ch > '9') goto error; + parser->http_minor = ch - '0'; + state = s_res_http_minor; + break; + + /* minor HTTP version or end of request line */ + case s_res_http_minor: + { + if (ch == ' ') { + state = s_res_first_status_code; + break; + } + + if (ch < '0' || ch > '9') goto error; + + parser->http_minor *= 10; + parser->http_minor += ch - '0'; + + if (parser->http_minor > 999) goto error; + break; + } + + case s_res_first_status_code: + { + if (ch < '0' || ch > '9') { + if (ch == ' ') { + break; + } + goto error; + } + parser->status_code = ch - '0'; + state = s_res_status_code; + break; + } + + case s_res_status_code: + { + if (ch < '0' || ch > '9') { + switch (ch) { + case ' ': + state = s_res_status; + break; + case CR: + state = s_res_line_almost_done; + break; + case LF: + state = s_header_field_start; + break; + default: + goto error; + } + break; + } + + parser->status_code *= 10; + parser->status_code += ch - '0'; + + if (parser->status_code > 999) goto error; + break; + } + + case s_res_status: + /* the human readable status. e.g. "NOT FOUND" + * we are not humans so just ignore this */ + if (ch == CR) { + state = s_res_line_almost_done; + break; + } + + if (ch == LF) { + state = s_header_field_start; + break; + } + break; + + case s_res_line_almost_done: + STRICT_CHECK(ch != LF); + state = s_header_field_start; + break; + + case s_start_req: + { + if (ch == CR || ch == LF) + break; + parser->flags = 0; + parser->content_length = -1; + + CALLBACK2(message_begin); + + if (ch < 'A' || 'Z' < ch) goto error; + + start_req_method_assign: + parser->method = (enum php_http_method) 0; + index = 1; + switch (ch) { + case 'C': parser->method = PHP_HTTP_CONNECT; /* or COPY, CHECKOUT */ break; + case 'D': parser->method = PHP_HTTP_DELETE; break; + case 'G': parser->method = PHP_HTTP_GET; break; + case 'H': parser->method = PHP_HTTP_HEAD; break; + case 'L': parser->method = PHP_HTTP_LOCK; break; + case 'M': parser->method = PHP_HTTP_MKCOL; /* or MOVE, MKACTIVITY, MERGE, M-SEARCH */ break; + case 'N': parser->method = PHP_HTTP_NOTIFY; break; + case 'O': parser->method = PHP_HTTP_OPTIONS; break; + case 'P': parser->method = PHP_HTTP_POST; /* or PROPFIND or PROPPATCH or PUT */ break; + case 'R': parser->method = PHP_HTTP_REPORT; break; + case 'S': parser->method = PHP_HTTP_SUBSCRIBE; break; + case 'T': parser->method = PHP_HTTP_TRACE; break; + case 'U': parser->method = PHP_HTTP_UNLOCK; /* or UNSUBSCRIBE */ break; + default: goto error; + } + state = s_req_method; + break; + } + + case s_req_method: + { + const char *matcher; + if (ch == '\0') + goto error; + + matcher = method_strings[parser->method]; + if (ch == ' ' && matcher[index] == '\0') { + state = s_req_spaces_before_url; + } else if (ch == matcher[index]) { + ; /* nada */ + } else if (parser->method == PHP_HTTP_CONNECT) { + if (index == 1 && ch == 'H') { + parser->method = PHP_HTTP_CHECKOUT; + } else if (index == 2 && ch == 'P') { + parser->method = PHP_HTTP_COPY; + } + } else if (parser->method == PHP_HTTP_MKCOL) { + if (index == 1 && ch == 'O') { + parser->method = PHP_HTTP_MOVE; + } else if (index == 1 && ch == 'E') { + parser->method = PHP_HTTP_MERGE; + } else if (index == 1 && ch == '-') { + parser->method = PHP_HTTP_MSEARCH; + } else if (index == 2 && ch == 'A') { + parser->method = PHP_HTTP_MKACTIVITY; + } + } else if (index == 1 && parser->method == PHP_HTTP_POST && ch == 'R') { + parser->method = PHP_HTTP_PROPFIND; /* or HTTP_PROPPATCH */ + } else if (index == 1 && parser->method == PHP_HTTP_POST && ch == 'U') { + parser->method = PHP_HTTP_PUT; + } else if (index == 2 && parser->method == PHP_HTTP_UNLOCK && ch == 'S') { + parser->method = PHP_HTTP_UNSUBSCRIBE; + } else if (index == 4 && parser->method == PHP_HTTP_PROPFIND && ch == 'P') { + parser->method = PHP_HTTP_PROPPATCH; + } else { + goto error; + } + + ++index; + break; + } + case s_req_spaces_before_url: + { + if (ch == ' ') break; + + if (ch == '/' || ch == '*') { + MARK(url); + MARK(path); + state = s_req_path; + break; + } + + c = LOWER(ch); + + if (c >= 'a' && c <= 'z') { + MARK(url); + state = s_req_schema; + break; + } + + goto error; + } + + case s_req_schema: + { + c = LOWER(ch); + + if (c >= 'a' && c <= 'z') break; + + if (ch == ':') { + state = s_req_schema_slash; + break; + } else if (ch == '.') { + state = s_req_host; + break; + } else if ('0' <= ch && ch <= '9') { + state = s_req_host; + break; + } + + goto error; + } + + case s_req_schema_slash: + STRICT_CHECK(ch != '/'); + state = s_req_schema_slash_slash; + break; + + case s_req_schema_slash_slash: + STRICT_CHECK(ch != '/'); + state = s_req_host; + break; + + case s_req_host: + { + c = LOWER(ch); + if (c >= 'a' && c <= 'z') break; + if ((ch >= '0' && ch <= '9') || ch == '.' || ch == '-') break; + switch (ch) { + case ':': + state = s_req_port; + break; + case '/': + MARK(path); + state = s_req_path; + break; + case ' ': + /* The request line looks like: + * "GET http://foo.bar.com HTTP/1.1" + * That is, there is no path. + */ + CALLBACK(url); + state = s_req_http_start; + break; + default: + goto error; + } + break; + } + + case s_req_port: + { + if (ch >= '0' && ch <= '9') break; + switch (ch) { + case '/': + MARK(path); + state = s_req_path; + break; + case ' ': + /* The request line looks like: + * "GET http://foo.bar.com:1234 HTTP/1.1" + * That is, there is no path. + */ + CALLBACK(url); + state = s_req_http_start; + break; + default: + goto error; + } + break; + } + + case s_req_path: + { + if (normal_url_char[(unsigned char)ch]) break; + + switch (ch) { + case ' ': + CALLBACK(url); + CALLBACK(path); + state = s_req_http_start; + break; + case CR: + CALLBACK(url); + CALLBACK(path); + parser->http_major = 0; + parser->http_minor = 9; + state = s_req_line_almost_done; + break; + case LF: + CALLBACK(url); + CALLBACK(path); + parser->http_major = 0; + parser->http_minor = 9; + state = s_header_field_start; + break; + case '?': + CALLBACK(path); + state = s_req_query_string_start; + break; + case '#': + CALLBACK(path); + state = s_req_fragment_start; + break; + default: + goto error; + } + break; + } + + case s_req_query_string_start: + { + if (normal_url_char[(unsigned char)ch]) { + MARK(query_string); + state = s_req_query_string; + break; + } + + switch (ch) { + case '?': + break; /* XXX ignore extra '?' ... is this right? */ + case ' ': + CALLBACK(url); + state = s_req_http_start; + break; + case CR: + CALLBACK(url); + parser->http_major = 0; + parser->http_minor = 9; + state = s_req_line_almost_done; + break; + case LF: + CALLBACK(url); + parser->http_major = 0; + parser->http_minor = 9; + state = s_header_field_start; + break; + case '#': + state = s_req_fragment_start; + break; + default: + goto error; + } + break; + } + + case s_req_query_string: + { + if (normal_url_char[(unsigned char)ch]) break; + + switch (ch) { + case '?': + /* allow extra '?' in query string */ + break; + case ' ': + CALLBACK(url); + CALLBACK(query_string); + state = s_req_http_start; + break; + case CR: + CALLBACK(url); + CALLBACK(query_string); + parser->http_major = 0; + parser->http_minor = 9; + state = s_req_line_almost_done; + break; + case LF: + CALLBACK(url); + CALLBACK(query_string); + parser->http_major = 0; + parser->http_minor = 9; + state = s_header_field_start; + break; + case '#': + CALLBACK(query_string); + state = s_req_fragment_start; + break; + default: + goto error; + } + break; + } + + case s_req_fragment_start: + { + if (normal_url_char[(unsigned char)ch]) { + MARK(fragment); + state = s_req_fragment; + break; + } + + switch (ch) { + case ' ': + CALLBACK(url); + state = s_req_http_start; + break; + case CR: + CALLBACK(url); + parser->http_major = 0; + parser->http_minor = 9; + state = s_req_line_almost_done; + break; + case LF: + CALLBACK(url); + parser->http_major = 0; + parser->http_minor = 9; + state = s_header_field_start; + break; + case '?': + MARK(fragment); + state = s_req_fragment; + break; + case '#': + break; + default: + goto error; + } + break; + } + + case s_req_fragment: + { + if (normal_url_char[(unsigned char)ch]) break; + + switch (ch) { + case ' ': + CALLBACK(url); + CALLBACK(fragment); + state = s_req_http_start; + break; + case CR: + CALLBACK(url); + CALLBACK(fragment); + parser->http_major = 0; + parser->http_minor = 9; + state = s_req_line_almost_done; + break; + case LF: + CALLBACK(url); + CALLBACK(fragment); + parser->http_major = 0; + parser->http_minor = 9; + state = s_header_field_start; + break; + case '?': + case '#': + break; + default: + goto error; + } + break; + } + + case s_req_http_start: + switch (ch) { + case 'H': + state = s_req_http_H; + break; + case ' ': + break; + default: + goto error; + } + break; + + case s_req_http_H: + STRICT_CHECK(ch != 'T'); + state = s_req_http_HT; + break; + + case s_req_http_HT: + STRICT_CHECK(ch != 'T'); + state = s_req_http_HTT; + break; + + case s_req_http_HTT: + STRICT_CHECK(ch != 'P'); + state = s_req_http_HTTP; + break; + + case s_req_http_HTTP: + STRICT_CHECK(ch != '/'); + state = s_req_first_http_major; + break; + + /* first digit of major HTTP version */ + case s_req_first_http_major: + if (ch < '1' || ch > '9') goto error; + parser->http_major = ch - '0'; + state = s_req_http_major; + break; + + /* major HTTP version or dot */ + case s_req_http_major: + { + if (ch == '.') { + state = s_req_first_http_minor; + break; + } + + if (ch < '0' || ch > '9') goto error; + + parser->http_major *= 10; + parser->http_major += ch - '0'; + + if (parser->http_major > 999) goto error; + break; + } + + /* first digit of minor HTTP version */ + case s_req_first_http_minor: + if (ch < '0' || ch > '9') goto error; + parser->http_minor = ch - '0'; + state = s_req_http_minor; + break; + + /* minor HTTP version or end of request line */ + case s_req_http_minor: + { + if (ch == CR) { + state = s_req_line_almost_done; + break; + } + + if (ch == LF) { + state = s_header_field_start; + break; + } + + /* XXX allow spaces after digit? */ + + if (ch < '0' || ch > '9') goto error; + + parser->http_minor *= 10; + parser->http_minor += ch - '0'; + + if (parser->http_minor > 999) goto error; + break; + } + + /* end of request line */ + case s_req_line_almost_done: + { + if (ch != LF) goto error; + state = s_header_field_start; + break; + } + + case s_header_field_start: + { + if (ch == CR) { + state = s_headers_almost_done; + break; + } + + if (ch == LF) { + /* they might be just sending \n instead of \r\n so this would be + * the second \n to denote the end of headers*/ + state = s_headers_almost_done; + goto headers_almost_done; + } + + c = TOKEN(ch); + + if (!c) goto error; + + MARK(header_field); + + index = 0; + state = s_header_field; + + switch (c) { + case 'c': + header_state = h_C; + break; + + case 'p': + header_state = h_matching_proxy_connection; + break; + + case 't': + header_state = h_matching_transfer_encoding; + break; + + case 'u': + header_state = h_matching_upgrade; + break; + + default: + header_state = h_general; + break; + } + break; + } + + case s_header_field: + { + c = TOKEN(ch); + + if (c) { + switch (header_state) { + case h_general: + break; + + case h_C: + index++; + header_state = (c == 'o' ? h_CO : h_general); + break; + + case h_CO: + index++; + header_state = (c == 'n' ? h_CON : h_general); + break; + + case h_CON: + index++; + switch (c) { + case 'n': + header_state = h_matching_connection; + break; + case 't': + header_state = h_matching_content_length; + break; + default: + header_state = h_general; + break; + } + break; + + /* connection */ + + case h_matching_connection: + index++; + if (index > sizeof(CONNECTION)-1 + || c != CONNECTION[index]) { + header_state = h_general; + } else if (index == sizeof(CONNECTION)-2) { + header_state = h_connection; + } + break; + + /* proxy-connection */ + + case h_matching_proxy_connection: + index++; + if (index > sizeof(PROXY_CONNECTION)-1 + || c != PROXY_CONNECTION[index]) { + header_state = h_general; + } else if (index == sizeof(PROXY_CONNECTION)-2) { + header_state = h_connection; + } + break; + + /* content-length */ + + case h_matching_content_length: + index++; + if (index > sizeof(CONTENT_LENGTH)-1 + || c != CONTENT_LENGTH[index]) { + header_state = h_general; + } else if (index == sizeof(CONTENT_LENGTH)-2) { + header_state = h_content_length; + } + break; + + /* transfer-encoding */ + + case h_matching_transfer_encoding: + index++; + if (index > sizeof(TRANSFER_ENCODING)-1 + || c != TRANSFER_ENCODING[index]) { + header_state = h_general; + } else if (index == sizeof(TRANSFER_ENCODING)-2) { + header_state = h_transfer_encoding; + } + break; + + /* upgrade */ + + case h_matching_upgrade: + index++; + if (index > sizeof(UPGRADE)-1 + || c != UPGRADE[index]) { + header_state = h_general; + } else if (index == sizeof(UPGRADE)-2) { + header_state = h_upgrade; + } + break; + + case h_connection: + case h_content_length: + case h_transfer_encoding: + case h_upgrade: + if (ch != ' ') header_state = h_general; + break; + + default: + assert(0 && "Unknown header_state"); + break; + } + break; + } + + if (ch == ':') { + CALLBACK(header_field); + state = s_header_value_start; + break; + } + + if (ch == CR) { + state = s_header_almost_done; + CALLBACK(header_field); + break; + } + + if (ch == LF) { + CALLBACK(header_field); + state = s_header_field_start; + break; + } + + goto error; + } + + case s_header_value_start: + { + if (ch == ' ') break; + + MARK(header_value); + + state = s_header_value; + index = 0; + + c = LOWER(ch); + + if (ch == CR) { + CALLBACK(header_value); + header_state = h_general; + state = s_header_almost_done; + break; + } + + if (ch == LF) { + CALLBACK(header_value); + state = s_header_field_start; + break; + } + + switch (header_state) { + case h_upgrade: + parser->flags |= F_UPGRADE; + header_state = h_general; + break; + + case h_transfer_encoding: + /* looking for 'Transfer-Encoding: chunked' */ + if ('c' == c) { + header_state = h_matching_transfer_encoding_chunked; + } else { + header_state = h_general; + } + break; + + case h_content_length: + if (ch < '0' || ch > '9') goto error; + parser->content_length = ch - '0'; + break; + + case h_connection: + /* looking for 'Connection: keep-alive' */ + if (c == 'k') { + header_state = h_matching_connection_keep_alive; + /* looking for 'Connection: close' */ + } else if (c == 'c') { + header_state = h_matching_connection_close; + } else { + header_state = h_general; + } + break; + + default: + header_state = h_general; + break; + } + break; + } + + case s_header_value: + { + c = LOWER(ch); + + if (ch == CR) { + CALLBACK(header_value); + state = s_header_almost_done; + break; + } + + if (ch == LF) { + CALLBACK(header_value); + goto header_almost_done; + } + + switch (header_state) { + case h_general: + break; + + case h_connection: + case h_transfer_encoding: + assert(0 && "Shouldn't get here."); + break; + + case h_content_length: + if (ch == ' ') break; + if (ch < '0' || ch > '9') goto error; + parser->content_length *= 10; + parser->content_length += ch - '0'; + break; + + /* Transfer-Encoding: chunked */ + case h_matching_transfer_encoding_chunked: + index++; + if (index > sizeof(CHUNKED)-1 + || c != CHUNKED[index]) { + header_state = h_general; + } else if (index == sizeof(CHUNKED)-2) { + header_state = h_transfer_encoding_chunked; + } + break; + + /* looking for 'Connection: keep-alive' */ + case h_matching_connection_keep_alive: + index++; + if (index > sizeof(KEEP_ALIVE)-1 + || c != KEEP_ALIVE[index]) { + header_state = h_general; + } else if (index == sizeof(KEEP_ALIVE)-2) { + header_state = h_connection_keep_alive; + } + break; + + /* looking for 'Connection: close' */ + case h_matching_connection_close: + index++; + if (index > sizeof(CLOSE)-1 || c != CLOSE[index]) { + header_state = h_general; + } else if (index == sizeof(CLOSE)-2) { + header_state = h_connection_close; + } + break; + + case h_transfer_encoding_chunked: + case h_connection_keep_alive: + case h_connection_close: + if (ch != ' ') header_state = h_general; + break; + + default: + state = s_header_value; + header_state = h_general; + break; + } + break; + } + + case s_header_almost_done: + header_almost_done: + { + STRICT_CHECK(ch != LF); + + state = s_header_field_start; + + switch (header_state) { + case h_connection_keep_alive: + parser->flags |= F_CONNECTION_KEEP_ALIVE; + break; + case h_connection_close: + parser->flags |= F_CONNECTION_CLOSE; + break; + case h_transfer_encoding_chunked: + parser->flags |= F_CHUNKED; + break; + default: + break; + } + break; + } + + case s_headers_almost_done: + headers_almost_done: + { + STRICT_CHECK(ch != LF); + + if (parser->flags & F_TRAILING) { + /* End of a chunked request */ + CALLBACK2(message_complete); + state = NEW_MESSAGE(); + break; + } + + nread = 0; + + if (parser->flags & F_UPGRADE || parser->method == PHP_HTTP_CONNECT) { + parser->upgrade = 1; + } + + /* Here we call the headers_complete callback. This is somewhat + * different than other callbacks because if the user returns 1, we + * will interpret that as saying that this message has no body. This + * is needed for the annoying case of recieving a response to a HEAD + * request. + */ + if (settings->on_headers_complete) { + switch (settings->on_headers_complete(parser)) { + case 0: + break; + + case 1: + parser->flags |= F_SKIPBODY; + break; + + default: + return p - data; /* Error */ + } + } + + /* Exit, the rest of the connect is in a different protocol. */ + if (parser->upgrade) { + CALLBACK2(message_complete); + return (p - data); + } + + if (parser->flags & F_SKIPBODY) { + CALLBACK2(message_complete); + state = NEW_MESSAGE(); + } else if (parser->flags & F_CHUNKED) { + /* chunked encoding - ignore Content-Length header */ + state = s_chunk_size_start; + } else { + if (parser->content_length == 0) { + /* Content-Length header given but zero: Content-Length: 0\r\n */ + CALLBACK2(message_complete); + state = NEW_MESSAGE(); + } else if (parser->content_length > 0) { + /* Content-Length header given and non-zero */ + state = s_body_identity; + } else { + if (parser->type == PHP_HTTP_REQUEST || php_http_should_keep_alive(parser)) { + /* Assume content-length 0 - read the next */ + CALLBACK2(message_complete); + state = NEW_MESSAGE(); + } else { + /* Read body until EOF */ + state = s_body_identity_eof; + } + } + } + + break; + } + + case s_body_identity: + to_read = MIN(pe - p, (size_t)parser->content_length); + if (to_read > 0) { + if (settings->on_body) settings->on_body(parser, p, to_read); + p += to_read - 1; + parser->content_length -= to_read; + if (parser->content_length == 0) { + CALLBACK2(message_complete); + state = NEW_MESSAGE(); + } + } + break; + + /* read until EOF */ + case s_body_identity_eof: + to_read = pe - p; + if (to_read > 0) { + if (settings->on_body) settings->on_body(parser, p, to_read); + p += to_read - 1; + } + break; + + case s_chunk_size_start: + { + assert(parser->flags & F_CHUNKED); + + c = unhex[(unsigned char)ch]; + if (c == -1) goto error; + parser->content_length = c; + state = s_chunk_size; + break; + } + + case s_chunk_size: + { + assert(parser->flags & F_CHUNKED); + + if (ch == CR) { + state = s_chunk_size_almost_done; + break; + } + + c = unhex[(unsigned char)ch]; + + if (c == -1) { + if (ch == ';' || ch == ' ') { + state = s_chunk_parameters; + break; + } + goto error; + } + + parser->content_length *= 16; + parser->content_length += c; + break; + } + + case s_chunk_parameters: + { + assert(parser->flags & F_CHUNKED); + /* just ignore this shit. TODO check for overflow */ + if (ch == CR) { + state = s_chunk_size_almost_done; + break; + } + break; + } + + case s_chunk_size_almost_done: + { + assert(parser->flags & F_CHUNKED); + STRICT_CHECK(ch != LF); + + if (parser->content_length == 0) { + parser->flags |= F_TRAILING; + state = s_header_field_start; + } else { + state = s_chunk_data; + } + break; + } + + case s_chunk_data: + { + assert(parser->flags & F_CHUNKED); + + to_read = MIN(pe - p, (size_t)(parser->content_length)); + + if (to_read > 0) { + if (settings->on_body) settings->on_body(parser, p, to_read); + p += to_read - 1; + } + + if (to_read == parser->content_length) { + state = s_chunk_data_almost_done; + } + + parser->content_length -= to_read; + break; + } + + case s_chunk_data_almost_done: + assert(parser->flags & F_CHUNKED); + STRICT_CHECK(ch != CR); + state = s_chunk_data_done; + break; + + case s_chunk_data_done: + assert(parser->flags & F_CHUNKED); + STRICT_CHECK(ch != LF); + state = s_chunk_size_start; + break; + + default: + assert(0 && "unhandled state"); + goto error; + } + } + + CALLBACK_NOCLEAR(header_field); + CALLBACK_NOCLEAR(header_value); + CALLBACK_NOCLEAR(fragment); + CALLBACK_NOCLEAR(query_string); + CALLBACK_NOCLEAR(path); + CALLBACK_NOCLEAR(url); + + parser->state = state; + parser->header_state = header_state; + parser->index = index; + parser->nread = nread; + + return len; + +error: + parser->state = s_dead; + return (p - data); +} + + +int +php_http_should_keep_alive (php_http_parser *parser) +{ + if (parser->http_major > 0 && parser->http_minor > 0) { + /* HTTP/1.1 */ + if (parser->flags & F_CONNECTION_CLOSE) { + return 0; + } else { + return 1; + } + } else { + /* HTTP/1.0 or earlier */ + if (parser->flags & F_CONNECTION_KEEP_ALIVE) { + return 1; + } else { + return 0; + } + } +} + + +const char * php_http_method_str (enum php_http_method m) +{ + return method_strings[m]; +} + + +void +php_http_parser_init (php_http_parser *parser, enum php_http_parser_type t) +{ + parser->type = t; + parser->state = (t == PHP_HTTP_REQUEST ? s_start_req : (t == PHP_HTTP_RESPONSE ? s_start_res : s_start_req_or_res)); + parser->nread = 0; + parser->upgrade = 0; + parser->flags = 0; + parser->method = 0; +} diff --git a/sapi/cli/php_http_parser.h b/sapi/cli/php_http_parser.h new file mode 100644 index 000000000..b740a0995 --- /dev/null +++ b/sapi/cli/php_http_parser.h @@ -0,0 +1,174 @@ +/* Copyright 2009,2010 Ryan Dahl <ry@tinyclouds.org> + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ +/* modified by Moriyoshi Koizumi <moriyoshi@php.net> to make it fit to PHP source tree. */ +#ifndef php_http_parser_h +#define php_http_parser_h +#ifdef __cplusplus +extern "C" { +#endif + + +#include <sys/types.h> +#if defined(_WIN32) && !defined(__MINGW32__) +# include <windows.h> +# include "win32/php_stdint.h" +# include "config.w32.h" +#else +# include <stdint.h> +#endif + +/* Compile with -DPHP_HTTP_PARSER_STRICT=0 to make less checks, but run + * faster + */ +#ifndef PHP_HTTP_PARSER_STRICT +# define PHP_HTTP_PARSER_STRICT 1 +#else +# define PHP_HTTP_PARSER_STRICT 0 +#endif + + +/* Maximium header size allowed */ +#define PHP_HTTP_MAX_HEADER_SIZE (80*1024) + + +typedef struct php_http_parser php_http_parser; +typedef struct php_http_parser_settings php_http_parser_settings; + + +/* Callbacks should return non-zero to indicate an error. The parser will + * then halt execution. + * + * The one exception is on_headers_complete. In a PHP_HTTP_RESPONSE parser + * returning '1' from on_headers_complete will tell the parser that it + * should not expect a body. This is used when receiving a response to a + * HEAD request which may contain 'Content-Length' or 'Transfer-Encoding: + * chunked' headers that indicate the presence of a body. + * + * http_data_cb does not return data chunks. It will be call arbitrarally + * many times for each string. E.G. you might get 10 callbacks for "on_path" + * each providing just a few characters more data. + */ +typedef int (*php_http_data_cb) (php_http_parser*, const char *at, size_t length); +typedef int (*php_http_cb) (php_http_parser*); + + +/* Request Methods */ +enum php_http_method + { PHP_HTTP_DELETE = 0 + , PHP_HTTP_GET + , PHP_HTTP_HEAD + , PHP_HTTP_POST + , PHP_HTTP_PUT + /* pathological */ + , PHP_HTTP_CONNECT + , PHP_HTTP_OPTIONS + , PHP_HTTP_TRACE + /* webdav */ + , PHP_HTTP_COPY + , PHP_HTTP_LOCK + , PHP_HTTP_MKCOL + , PHP_HTTP_MOVE + , PHP_HTTP_PROPFIND + , PHP_HTTP_PROPPATCH + , PHP_HTTP_UNLOCK + /* subversion */ + , PHP_HTTP_REPORT + , PHP_HTTP_MKACTIVITY + , PHP_HTTP_CHECKOUT + , PHP_HTTP_MERGE + /* upnp */ + , PHP_HTTP_MSEARCH + , PHP_HTTP_NOTIFY + , PHP_HTTP_SUBSCRIBE + , PHP_HTTP_UNSUBSCRIBE + }; + + +enum php_http_parser_type { PHP_HTTP_REQUEST, PHP_HTTP_RESPONSE, PHP_HTTP_BOTH }; + + +struct php_http_parser { + /** PRIVATE **/ + unsigned char type : 2; + unsigned char flags : 6; + unsigned char state; + unsigned char header_state; + unsigned char index; + + uint32_t nread; + ssize_t content_length; + + /** READ-ONLY **/ + unsigned short http_major; + unsigned short http_minor; + unsigned short status_code; /* responses only */ + unsigned char method; /* requests only */ + + /* 1 = Upgrade header was present and the parser has exited because of that. + * 0 = No upgrade header present. + * Should be checked when http_parser_execute() returns in addition to + * error checking. + */ + char upgrade; + + /** PUBLIC **/ + void *data; /* A pointer to get hook to the "connection" or "socket" object */ +}; + + +struct php_http_parser_settings { + php_http_cb on_message_begin; + php_http_data_cb on_path; + php_http_data_cb on_query_string; + php_http_data_cb on_url; + php_http_data_cb on_fragment; + php_http_data_cb on_header_field; + php_http_data_cb on_header_value; + php_http_cb on_headers_complete; + php_http_data_cb on_body; + php_http_cb on_message_complete; +}; + + +void php_http_parser_init(php_http_parser *parser, enum php_http_parser_type type); + + +size_t php_http_parser_execute(php_http_parser *parser, + const php_http_parser_settings *settings, + const char *data, + size_t len); + + +/* If php_http_should_keep_alive() in the on_headers_complete or + * on_message_complete callback returns true, then this will be should be + * the last message on the connection. + * If you are the server, respond with the "Connection: close" header. + * If you are the client, close the connection. + */ +int php_http_should_keep_alive(php_http_parser *parser); + +/* Returns a string version of the HTTP method. */ +const char *php_http_method_str(enum php_http_method); + +#ifdef __cplusplus +} +#endif +#endif diff --git a/sapi/cli/tests/007.phpt b/sapi/cli/tests/007.phpt index 12fddee01..0bf40703d 100644 --- a/sapi/cli/tests/007.phpt +++ b/sapi/cli/tests/007.phpt @@ -45,8 +45,8 @@ string(81) " <?php class test { public $var = "test"; private $pri; function foo() { } } ?> " -Could not open input file: wrong -NULL +string(33) "Could not open input file: wrong +" string(43) "<?php class test { function foo() {} } ?> " Done diff --git a/sapi/cli/tests/008.phpt b/sapi/cli/tests/008.phpt index a83304309..e14338f5f 100644 --- a/sapi/cli/tests/008.phpt +++ b/sapi/cli/tests/008.phpt @@ -38,6 +38,6 @@ string(%d) " Fatal error: Cannot access private property test::$pri in %s on line %d " -Could not open input file: wrong -NULL +string(33) "Could not open input file: wrong +" Done diff --git a/sapi/cli/tests/009.phpt b/sapi/cli/tests/009.phpt index a881a0730..33f859fb3 100644 --- a/sapi/cli/tests/009.phpt +++ b/sapi/cli/tests/009.phpt @@ -13,8 +13,8 @@ var_dump(`$php -n -r "echo hello;" -a`); echo "Done\n"; ?> --EXPECTF-- -Either execute direct code, process stdin or use a file. -NULL -Either execute direct code, process stdin or use a file. -NULL +string(57) "Either execute direct code, process stdin or use a file. +" +string(57) "Either execute direct code, process stdin or use a file. +" Done diff --git a/sapi/cli/tests/011.phpt b/sapi/cli/tests/011.phpt index ef49666d3..615469351 100644 --- a/sapi/cli/tests/011.phpt +++ b/sapi/cli/tests/011.phpt @@ -49,8 +49,8 @@ echo "Done\n"; --EXPECTF-- string(%d) "No syntax errors detected in %s011.test.php " -Could not open input file: some.unknown -NULL +string(40) "Could not open input file: some.unknown +" string(%d) " Parse error: %s expecting %s{%s in %s on line %d Errors parsing %s011.test.php diff --git a/sapi/cli/tests/012.phpt b/sapi/cli/tests/012.phpt index 137e0bd78..c1e4f6a63 100644 --- a/sapi/cli/tests/012.phpt +++ b/sapi/cli/tests/012.phpt @@ -19,20 +19,20 @@ var_dump(`"$php" -n -r '' -r ''`); echo "Done\n"; ?> --EXPECTF-- -You can use -R or -F only once. -NULL -You can use -R or -F only once. -NULL -You can use -R or -F only once. -NULL -You can use -R or -F only once. -NULL -You can use -f only once. -NULL -You can use -B only once. -NULL -You can use -E only once. -NULL -You can use -r only once. -NULL +string(32) "You can use -R or -F only once. +" +string(32) "You can use -R or -F only once. +" +string(32) "You can use -R or -F only once. +" +string(32) "You can use -R or -F only once. +" +string(26) "You can use -f only once. +" +string(26) "You can use -B only once. +" +string(26) "You can use -E only once. +" +string(26) "You can use -r only once. +" Done diff --git a/sapi/cli/tests/014.phpt b/sapi/cli/tests/014.phpt index b20478a03..e8c5203f6 100644 --- a/sapi/cli/tests/014.phpt +++ b/sapi/cli/tests/014.phpt @@ -39,6 +39,6 @@ string(1478) "<code><span style="color: #000000"> <br /><span style="color: #0000BB"><?php<br />$test </span><span style="color: #007700">= </span><span style="color: #DD0000">"var"</span><span style="color: #007700">; </span><span style="color: #FF8000">//var<br />/* test class */<br /></span><span style="color: #007700">class </span><span style="color: #0000BB">test </span><span style="color: #007700">{<br /> private </span><span style="color: #0000BB">$var </span><span style="color: #007700">= array();<br /><br /> public static function </span><span style="color: #0000BB">foo</span><span style="color: #007700">(</span><span style="color: #0000BB">Test $arg</span><span style="color: #007700">) {<br /> echo </span><span style="color: #DD0000">"hello"</span><span style="color: #007700">;<br /> </span><span style="color: #0000BB">var_dump</span><span style="color: #007700">(</span><span style="color: #0000BB">$this</span><span style="color: #007700">);<br /> }<br />}<br /><br /></span><span style="color: #0000BB">$o </span><span style="color: #007700">= new </span><span style="color: #0000BB">test</span><span style="color: #007700">;<br /></span><span style="color: #0000BB">?><br /></span> </span> </code>" -Could not open input file: unknown -NULL +string(35) "Could not open input file: unknown +" Done diff --git a/sapi/cli/tests/016.phpt b/sapi/cli/tests/016.phpt index 9c28d15a3..31c1a40e4 100644 --- a/sapi/cli/tests/016.phpt +++ b/sapi/cli/tests/016.phpt @@ -59,6 +59,8 @@ foreach ($codes as $key => $code) { echo "\nDone\n"; ?> +--XFAIL-- +https://bugs.php.net/bug.php?id=55496 --EXPECTF-- -------------- Snippet no. 1: diff --git a/sapi/cli/tests/php_cli_server.inc b/sapi/cli/tests/php_cli_server.inc new file mode 100644 index 000000000..60ae3254d --- /dev/null +++ b/sapi/cli/tests/php_cli_server.inc @@ -0,0 +1,45 @@ +<?php +define ("PHP_CLI_SERVER_ADDRESS", "localhost:8964"); + +function php_cli_server_start($code = 'echo "Hello world";', $no_router = FALSE) { + $php_executable = getenv('TEST_PHP_EXECUTABLE'); + $doc_root = __DIR__; + $router = "index.php"; + if ($code) { + file_put_contents($doc_root . '/' . $router, '<?php ' . $code . ' ?>'); + } + + $descriptorspec = array( + 0 => STDIN, + 1 => STDOUT, + 2 => STDERR, + ); + + if (substr(PHP_OS, 0, 3) == 'WIN') { + $cmd = "{$php_executable} -t {$doc_root} -n -S " . PHP_CLI_SERVER_ADDRESS; + if (!$no_router) { + $cmd .= " {$router}"; + } + + $handle = proc_open(addslashes($cmd), $descriptorspec, $pipes, $doc_root, NULL, array("bypass_shell" => true, "suppress_errors" => true)); + } else { + $cmd = "exec {$php_executable} -t {$doc_root} -n -S " . PHP_CLI_SERVER_ADDRESS; + if (!$no_router) { + $cmd .= " {$router}"; + } + $cmd .= " 2>/dev/null"; + + $handle = proc_open($cmd, $descriptorspec, $pipes, $doc_root); + } + + register_shutdown_function( + function($handle) use($router) { + proc_terminate($handle); + @unlink(__DIR__ . "/{$router}"); + }, + $handle + ); + usleep(50000); +} +?> + diff --git a/sapi/cli/tests/php_cli_server_001.phpt b/sapi/cli/tests/php_cli_server_001.phpt new file mode 100644 index 000000000..3f1083e7a --- /dev/null +++ b/sapi/cli/tests/php_cli_server_001.phpt @@ -0,0 +1,16 @@ +--TEST-- +basic function +--INI-- +allow_url_fopen=1 +--SKIPIF-- +<?php +include "skipif.inc"; +?> +--FILE-- +<?php +include "php_cli_server.inc"; +php_cli_server_start(); +var_dump(file_get_contents("http://" . PHP_CLI_SERVER_ADDRESS)); +?> +--EXPECT-- +string(11) "Hello world" diff --git a/sapi/cli/tests/php_cli_server_002.phpt b/sapi/cli/tests/php_cli_server_002.phpt new file mode 100644 index 000000000..93151c15d --- /dev/null +++ b/sapi/cli/tests/php_cli_server_002.phpt @@ -0,0 +1,20 @@ +--TEST-- +$_SERVER variable +--INI-- +allow_url_fopen=1 +--SKIPIF-- +<?php +include "skipif.inc"; +?> +--FILE-- +<?php +include "php_cli_server.inc"; +php_cli_server_start('var_dump($_SERVER["DOCUMENT_ROOT"], $_SERVER["SERVER_SOFTWARE"], $_SERVER["SERVER_NAME"], $_SERVER["SERVER_PORT"]);'); +var_dump(file_get_contents("http://" . PHP_CLI_SERVER_ADDRESS)); +?> +--EXPECTF-- +string(%d) "string(%d) "%stests" +string(%d) "PHP %s Development Server" +string(%d) "localhost" +string(%d) "8964" +" diff --git a/sapi/cli/tests/php_cli_server_003.phpt b/sapi/cli/tests/php_cli_server_003.phpt new file mode 100644 index 000000000..d1e95fe6a --- /dev/null +++ b/sapi/cli/tests/php_cli_server_003.phpt @@ -0,0 +1,18 @@ +--TEST-- +Bug #55726 (Changing the working directory makes router script inaccessible) +--INI-- +allow_url_fopen=1 +--SKIPIF-- +<?php +include "skipif.inc"; +?> +--FILE-- +<?php +include "php_cli_server.inc"; +php_cli_server_start('chdir(__DIR__); echo "okey";'); +var_dump(file_get_contents("http://" . PHP_CLI_SERVER_ADDRESS)); +var_dump(file_get_contents("http://" . PHP_CLI_SERVER_ADDRESS)); +?> +--EXPECTF-- +string(4) "okey" +string(4) "okey" diff --git a/sapi/cli/tests/php_cli_server_004.phpt b/sapi/cli/tests/php_cli_server_004.phpt new file mode 100644 index 000000000..cb0d0604b --- /dev/null +++ b/sapi/cli/tests/php_cli_server_004.phpt @@ -0,0 +1,48 @@ +--TEST-- +Bug #55747 (request headers missed in $_SERVER) +--INI-- +allow_url_fopen=1 +--SKIPIF-- +<?php +include "skipif.inc"; +?> +--FILE-- +<?php +include "php_cli_server.inc"; +php_cli_server_start('foreach($_SERVER as $k=>$v) { if (!strncmp($k, "HTTP", 4)) var_dump( $k . ":" . $v); }'); + +list($host, $port) = explode(':', PHP_CLI_SERVER_ADDRESS); +$port = intval($port)?:80; + +$fp = fsockopen($host, $port, $errno, $errstr, 0.5); +if (!$fp) { + die("connect failed"); +} + +if(fwrite($fp, <<<HEADER +GET / HTTP/1.1 +Host:{$host} +User-Agent:dummy +Custom:foo +Referer:http://www.php.net/ + + +HEADER +)) { + while (!feof($fp)) { + echo fgets($fp); + } +} + +?> +--EXPECTF-- +HTTP/1.1 200 OK +Host: %s +Connection: closed +X-Powered-By: PHP/%s +Content-type: text/html + +string(19) "HTTP_HOST:localhost" +string(21) "HTTP_USER_AGENT:dummy" +string(15) "HTTP_CUSTOM:foo" +string(32) "HTTP_REFERER:http://www.php.net/" diff --git a/sapi/cli/tests/php_cli_server_005.phpt b/sapi/cli/tests/php_cli_server_005.phpt new file mode 100644 index 000000000..aa8b045a8 --- /dev/null +++ b/sapi/cli/tests/php_cli_server_005.phpt @@ -0,0 +1,71 @@ +--TEST-- +Post a file +--SKIPIF-- +<?php +include "skipif.inc"; +?> +--FILE-- +<?php +include "php_cli_server.inc"; +php_cli_server_start('var_dump($_FILES);'); + +list($host, $port) = explode(':', PHP_CLI_SERVER_ADDRESS); +$port = intval($port)?:80; + +$fp = fsockopen($host, $port, $errno, $errstr, 0.5); +if (!$fp) { + die("connect failed"); +} + +$post_data = <<<POST +-----------------------------114782935826962 +Content-Disposition: form-data; name="userfile"; filename="laruence.txt" +Content-Type: text/plain + +I am not sure about this. + +-----------------------------114782935826962-- + + +POST; + +$post_len = strlen($post_data); + +if(fwrite($fp, <<<HEADER +POST / HTTP/1.1 +Host: {$host} +Content-Type: multipart/form-data; boundary=---------------------------114782935826962 +Content-Length: {$post_len} + + +{$post_data} +HEADER +)) { + while (!feof($fp)) { + echo fgets($fp); + } +} + +?> +--EXPECTF-- +HTTP/1.1 200 OK +Host: %s +Connection: closed +X-Powered-By: PHP/%s +Content-type: text/html + +array(1) { + ["userfile"]=> + array(5) { + ["name"]=> + string(12) "laruence.txt" + ["type"]=> + string(10) "text/plain" + ["tmp_name"]=> + string(%d) "%s" + ["error"]=> + int(0) + ["size"]=> + int(26) + } +} diff --git a/sapi/cli/tests/php_cli_server_006.phpt b/sapi/cli/tests/php_cli_server_006.phpt new file mode 100644 index 000000000..d9f21d89b --- /dev/null +++ b/sapi/cli/tests/php_cli_server_006.phpt @@ -0,0 +1,42 @@ +--TEST-- +Bug #55755 (SegFault when outputting header WWW-Authenticate) +--SKIPIF-- +<?php +include "skipif.inc"; +?> +--FILE-- +<?php +include "php_cli_server.inc"; +php_cli_server_start('var_dump($_SERVER["PHP_AUTH_USER"], $_SERVER["PHP_AUTH_PW"]);'); + +list($host, $port) = explode(':', PHP_CLI_SERVER_ADDRESS); +$port = intval($port)?:80; + +$fp = fsockopen($host, $port, $errno, $errstr, 0.5); +if (!$fp) { + die("connect failed"); +} + +if(fwrite($fp, <<<HEADER +GET / HTTP/1.1 +Host: {$host} +Authorization: Basic Zm9vOmJhcg== + + +HEADER +)) { + while (!feof($fp)) { + echo fgets($fp); + } +} + +?> +--EXPECTF-- +HTTP/1.1 200 OK +Host: %s +Connection: closed +X-Powered-By: PHP/%s +Content-type: text/html + +string(3) "foo" +string(3) "bar" diff --git a/sapi/cli/tests/php_cli_server_007.phpt b/sapi/cli/tests/php_cli_server_007.phpt new file mode 100644 index 000000000..f355cedaf --- /dev/null +++ b/sapi/cli/tests/php_cli_server_007.phpt @@ -0,0 +1,40 @@ +--TEST-- +Bug #55758 (Digest Authenticate missed in 5.4) +--SKIPIF-- +<?php +include "skipif.inc"; +?> +--FILE-- +<?php +include "php_cli_server.inc"; +php_cli_server_start('header(\'WWW-Authenticate: Digest realm="foo",qop="auth",nonce="XXXXX",opaque="'.md5("foo").'"\');'); + +list($host, $port) = explode(':', PHP_CLI_SERVER_ADDRESS); +$port = intval($port)?:80; + +$fp = fsockopen($host, $port, $errno, $errstr, 0.5); +if (!$fp) { + die("connect failed"); +} + +if(fwrite($fp, <<<HEADER +GET / HTTP/1.1 +Host: {$host} +Authorization: Basic Zm9vOmJhcg== + + +HEADER +)) { + while (!feof($fp)) { + echo fgets($fp); + } +} + +?> +--EXPECTF-- +HTTP/1.1 401 Unauthorized +Host: %s +Connection: closed +X-Powered-By: PHP/%s +WWW-Authenticate: Digest realm="foo",qop="auth",nonce="XXXXX",opaque="acbd18db4cc2f85cedef654fccc4a4d8" +Content-type: text/html diff --git a/sapi/cli/tests/php_cli_server_008.phpt b/sapi/cli/tests/php_cli_server_008.phpt new file mode 100644 index 000000000..b2a4be1dc --- /dev/null +++ b/sapi/cli/tests/php_cli_server_008.phpt @@ -0,0 +1,68 @@ +--TEST-- +SERVER_PROTOCOL header availability +--SKIPIF-- +<?php +include "skipif.inc"; +?> +--FILE-- +<?php +include "php_cli_server.inc"; +php_cli_server_start('var_dump($_SERVER["SERVER_PROTOCOL"]);'); + +list($host, $port) = explode(':', PHP_CLI_SERVER_ADDRESS); +$port = intval($port)?:80; + +$fp = fsockopen($host, $port, $errno, $errstr, 0.5); +if (!$fp) { + die("connect failed"); +} + +if(fwrite($fp, <<<HEADER +GET / HTTP/1.1 +Host: {$host} + + +HEADER +)) { + while (!feof($fp)) { + echo fgets($fp); + } +} + +fclose($fp); + +$fp = fsockopen($host, $port, $errno, $errstr, 0.5); +if (!$fp) { + die("connect failed"); +} + + +if(fwrite($fp, <<<HEADER +GET / HTTP/1.0 +Host: {$host} + + +HEADER +)) { + while (!feof($fp)) { + echo fgets($fp); + } +} + +fclose($fp); +?> +--EXPECTF-- +HTTP/1.1 200 OK +Host: %s +Connection: closed +X-Powered-By: PHP/%s +Content-type: text/html + +string(8) "HTTP/1.1" +HTTP/1.0 200 OK +Host: %s +Connection: closed +X-Powered-By: PHP/%s +Content-type: text/html + +string(8) "HTTP/1.0" diff --git a/sapi/cli/tests/php_cli_server_009.phpt b/sapi/cli/tests/php_cli_server_009.phpt new file mode 100644 index 000000000..e28e72edb --- /dev/null +++ b/sapi/cli/tests/php_cli_server_009.phpt @@ -0,0 +1,93 @@ +--TEST-- +PATH_INFO (relevant to #60112) +--DESCRIPTION-- +After this fix(#60112), previously 404 request like "localhost/foo/bar" +now could serve correctly with request_uri "index.php" and PATH_INFO "/foo/bar/" +--SKIPIF-- +<?php +include "skipif.inc"; +?> +--FILE-- +<?php +include "php_cli_server.inc"; +php_cli_server_start('var_dump($_SERVER["PATH_INFO"]);', TRUE); + +list($host, $port) = explode(':', PHP_CLI_SERVER_ADDRESS); +$port = intval($port)?:80; + +$fp = fsockopen($host, $port, $errno, $errstr, 0.5); +if (!$fp) { + die("connect failed"); +} + +if(fwrite($fp, <<<HEADER +GET /foo/bar HTTP/1.1 +Host: {$host} + + +HEADER +)) { + while (!feof($fp)) { + echo fgets($fp); + } +} + +fclose($fp); + +$fp = fsockopen($host, $port, $errno, $errstr, 0.5); +if (!$fp) { + die("connect failed"); +} + + +if(fwrite($fp, <<<HEADER +GET /foo/bar/ HTTP/1.0 +Host: {$host} + + +HEADER +)) { + while (!feof($fp)) { + echo fgets($fp); + } +} + +fclose($fp); + +$fp = fsockopen($host, $port, $errno, $errstr, 0.5); +if (!$fp) { + die("connect failed"); +} + + +if(fwrite($fp, <<<HEADER +GET /foo/bar.js HTTP/1.0 +Host: {$host} + + +HEADER +)) { + while (!feof($fp)) { + echo fgets($fp); + break; + } +} + +fclose($fp); +?> +--EXPECTF-- +HTTP/1.1 200 OK +Host: %s +Connection: closed +X-Powered-By: PHP/%s +Content-type: text/html + +string(8) "/foo/bar" +HTTP/1.0 200 OK +Host: %s +Connection: closed +X-Powered-By: PHP/%s +Content-type: text/html + +string(9) "/foo/bar/" +HTTP/1.0 404 Not Found diff --git a/sapi/cli/tests/php_cli_server_010.phpt b/sapi/cli/tests/php_cli_server_010.phpt new file mode 100644 index 000000000..17bfb3863 --- /dev/null +++ b/sapi/cli/tests/php_cli_server_010.phpt @@ -0,0 +1,75 @@ +--TEST-- +Bug #60180 ($_SERVER["PHP_SELF"] incorrect) +--SKIPIF-- +<?php +include "skipif.inc"; +?> +--FILE-- +<?php +include "php_cli_server.inc"; +php_cli_server_start('var_dump($_SERVER["PHP_SELF"], $_SERVER["SCRIPT_NAME"], $_SERVER["PATH_INFO"], $_SERVER["QUERY_STRING"]);', TRUE); + +list($host, $port) = explode(':', PHP_CLI_SERVER_ADDRESS); +$port = intval($port)?:80; + +$fp = fsockopen($host, $port, $errno, $errstr, 0.5); +if (!$fp) { + die("connect failed"); +} + +if(fwrite($fp, <<<HEADER +GET /foo/bar?foo=bar HTTP/1.1 +Host: {$host} + + +HEADER +)) { + while (!feof($fp)) { + echo fgets($fp); + } +} + +fclose($fp); + +$fp = fsockopen($host, $port, $errno, $errstr, 0.5); +if (!$fp) { + die("connect failed"); +} + + +if(fwrite($fp, <<<HEADER +GET /index.php/foo/bar/?foo=bar HTTP/1.0 +Host: {$host} + + +HEADER +)) { + while (!feof($fp)) { + echo fgets($fp); + } +} + +fclose($fp); + +?> +--EXPECTF-- +HTTP/1.1 200 OK +Host: %s +Connection: closed +X-Powered-By: PHP/%s +Content-type: text/html + +string(18) "/index.php/foo/bar" +string(10) "/index.php" +string(8) "/foo/bar" +string(7) "foo=bar" +HTTP/1.0 200 OK +Host: %s +Connection: closed +X-Powered-By: PHP/%s +Content-type: text/html + +string(19) "/index.php/foo/bar/" +string(10) "/index.php" +string(9) "/foo/bar/" +string(7) "foo=bar" diff --git a/sapi/cli/tests/php_cli_server_011.phpt b/sapi/cli/tests/php_cli_server_011.phpt new file mode 100644 index 000000000..a957a8ed4 --- /dev/null +++ b/sapi/cli/tests/php_cli_server_011.phpt @@ -0,0 +1,41 @@ +--TEST-- +Bug #60180 ($_SERVER["PHP_SELF"] incorrect) +--SKIPIF-- +<?php +include "skipif.inc"; +?> +--FILE-- +<?php +include "php_cli_server.inc"; +php_cli_server_start('sytanx error;', TRUE); + +list($host, $port) = explode(':', PHP_CLI_SERVER_ADDRESS); +$port = intval($port)?:80; + +$fp = fsockopen($host, $port, $errno, $errstr, 0.5); +if (!$fp) { + die("connect failed"); +} + +$logo_id = php_logo_guid(); + +if(fwrite($fp, <<<HEADER +GET /?={$logo_id} HTTP/1.1 +Host: {$host} + + +HEADER +)) { + while (!feof($fp)) { + if (("Content-Type: image/gif") == trim(fgets($fp))) { + echo "okey"; + break; + } + } +} + +fclose($fp); + +?> +--EXPECTF-- +okey diff --git a/sapi/cli/tests/php_cli_server_012.phpt b/sapi/cli/tests/php_cli_server_012.phpt new file mode 100644 index 000000000..8d0f22ec7 --- /dev/null +++ b/sapi/cli/tests/php_cli_server_012.phpt @@ -0,0 +1,55 @@ +--TEST-- +Bug #60159 (Router returns false, but POST is not passed to requested resource) +--SKIPIF-- +<?php +include "skipif.inc"; +?> +--FILE-- +<?php +include "php_cli_server.inc"; +php_cli_server_start('print_r($_REQUEST); $_REQUEST["foo"] = "bar"; return FALSE;'); +$doc_root = __DIR__; +file_put_contents($doc_root . '/request.php', '<?php print_r($_REQUEST); ?>'); + +list($host, $port) = explode(':', PHP_CLI_SERVER_ADDRESS); +$port = intval($port)?:80; + +$fp = fsockopen($host, $port, $errno, $errstr, 0.5); +if (!$fp) { + die("connect failed"); +} + +if(fwrite($fp, <<<HEADER +POST /request.php HTTP/1.1 +Host: {$host} +Content-Type: application/x-www-form-urlencoded +Content-Length: 3 + +a=b +HEADER +)) { + while (!feof($fp)) { + echo fgets($fp); + } +} + +fclose($fp); +@unlink($doc_root . '/request.php'); + +?> +--EXPECTF-- +HTTP/1.1 200 OK +Host: %s +Connection: closed +X-Powered-By: PHP/%s +Content-type: text/html + +Array +( + [a] => b +) +Array +( + [a] => b + [foo] => bar +) diff --git a/sapi/cli/tests/php_cli_server_013.phpt b/sapi/cli/tests/php_cli_server_013.phpt new file mode 100644 index 000000000..caefdd48f --- /dev/null +++ b/sapi/cli/tests/php_cli_server_013.phpt @@ -0,0 +1,108 @@ +--TEST-- +No router, no script +--SKIPIF-- +<?php +include "skipif.inc"; +?> +--FILE-- +<?php +include "php_cli_server.inc"; +php_cli_server_start(NULL, TRUE); + +list($host, $port) = explode(':', PHP_CLI_SERVER_ADDRESS); +$port = intval($port)?:80; +$output = ''; + +$fp = fsockopen($host, $port, $errno, $errstr, 0.5); +if (!$fp) { + die("connect failed"); +} + + +if(fwrite($fp, <<<HEADER +POST / HTTP/1.1 +Host: {$host} +Content-Type: application/x-www-form-urlencoded +Content-Length: 3 + +a=b +HEADER +)) { + while (!feof($fp)) { + $output .= fgets($fp); + } +} + +echo preg_replace("/<style type=\"text\/css\">(.*?)<\/style>/s", "<style type=\"text/css\">AAA</style>", $output), "\n"; +fclose($fp); + + +$output = ''; +$fp = fsockopen($host, $port, $errno, $errstr, 0.5); +if (!$fp) { + die("connect failed"); +} + +if(fwrite($fp, <<<HEADER +GET /main/style.css HTTP/1.1 +Host: {$host} + + +HEADER +)) { + while (!feof($fp)) { + $output .= fgets($fp); + } +} + +echo preg_replace("/<style type=\"text\/css\">(.*?)<\/style>/s", "<style type=\"text/css\">AAA</style>", $output), "\n"; +fclose($fp); + +$output = ''; +$fp = fsockopen($host, $port, $errno, $errstr, 0.5); +if (!$fp) { + die("connect failed"); +} + +if(fwrite($fp, <<<HEADER +HEAD /main/foo/bar HTTP/1.1 +Host: {$host} + + +HEADER +)) { + while (!feof($fp)) { + $output .= fgets($fp); + } +} + +echo preg_replace("/<style type=\"text\/css\">(.*?)<\/style>/s", "<style type=\"text/css\">AAA</style>", $output), "\n"; +fclose($fp); +?> +--EXPECTF-- + +HTTP/1.1 404 Not Found +Host: %s +Connection: closed +Content-Type: text/html; charset=UTF-8 +Content-Length: %d + +<html><head><title>404 Not Found</title><style type="text/css">AAA</style> +</head><body><h1 class="h">Not Found</h1><p>The requested resource / was not found on this server.</p></body></html> +HTTP/1.1 404 Not Found +Host: %s +Connection: closed +Content-Type: text/html; charset=UTF-8 +Content-Length: %d + +<html><head><title>404 Not Found</title><style type="text/css">AAA</style> +</head><body><h1 class="h">Not Found</h1><p>The requested resource /main/style.css was not found on this server.</p></body></html> +HTTP/1.1 404 Not Found +Host: %s +Connection: closed +Content-Type: text/html; charset=UTF-8 +Content-Length: %d + +<html><head><title>404 Not Found</title><style type="text/css">AAA</style> +</head><body><h1 class="h">Not Found</h1><p>The requested resource /main/foo/bar was not found on this server.</p></body></html> + diff --git a/sapi/cli/tests/php_cli_server_014.phpt b/sapi/cli/tests/php_cli_server_014.phpt new file mode 100644 index 000000000..4e95040b5 --- /dev/null +++ b/sapi/cli/tests/php_cli_server_014.phpt @@ -0,0 +1,76 @@ +--TEST-- +Bug #60477: Segfault after two multipart/form-data POST requestes +--SKIPIF-- +<?php +include "skipif.inc"; +?> +--FILE-- +<?php +include "php_cli_server.inc"; +php_cli_server_start('echo done, "\n";', TRUE); + +list($host, $port) = explode(':', PHP_CLI_SERVER_ADDRESS); +$port = intval($port)?:80; +$output = ''; + +$fp = fsockopen($host, $port, $errno, $errstr, 0.5); +if (!$fp) { + die("connect failed"); +} + +if(fwrite($fp, <<<HEADER +POST /index.php HTTP/1.1 +Host: {$host} +Content-Type: multipart/form-data; boundary=---------123456789 +Content-Length: 70 + +---------123456789 +Content-Type: application/x-www-form-urlencoded +a=b +HEADER +)) { + while (!feof($fp)) { + $output .= fgets($fp); + } +} + +fclose($fp); + +$fp = fsockopen($host, $port, $errno, $errstr, 0.5); +if(fwrite($fp, <<<HEADER +POST /main/no-exists.php HTTP/1.1 +Host: {$host} +Content-Type: multipart/form-data; boundary=---------123456789 +Content-Length: 70 + +---------123456789 +Content-Type: application/x-www-form-urlencoded +a=b +HEADER +)) { + while (!feof($fp)) { + $output .= fgets($fp); + } +} + +echo preg_replace("/<style type=\"text\/css\">(.*?)<\/style>/s", "<style type=\"text/css\">AAA</style>", $output), "\n"; +fclose($fp); + +?> +--EXPECTF-- + +HTTP/1.1 200 OK +Host: %s +Connection: closed +X-Powered-By: %s +Content-type: %s + +done +HTTP/1.1 404 Not Found +Host: %s +Connection: closed +Content-Type: %s +Content-Length: %d + +<html><head><title>404 Not Found</title><style type="text/css">AAA</style> +</head><body><h1 class="h">Not Found</h1><p>The requested resource /main/no-exists.php was not found on this server.</p></body></html> diff --git a/sapi/cli/tests/php_cli_server_015.phpt b/sapi/cli/tests/php_cli_server_015.phpt new file mode 100644 index 000000000..9ee7c1b10 --- /dev/null +++ b/sapi/cli/tests/php_cli_server_015.phpt @@ -0,0 +1,49 @@ +--TEST-- +Bug #60523 (PHP Errors are not reported in browsers using built-in SAPI) +--SKIPIF-- +<?php +include "skipif.inc"; +?> +--INI-- +display_errors=1 +--FILE-- +<?php +include "php_cli_server.inc"; +php_cli_server_start('require("syntax_error.php");'); +$dir = realpath(dirname(__FILE__)); + +file_put_contents($dir . "/syntax_error.php", "<?php non_exists_function(); ?>"); + +list($host, $port) = explode(':', PHP_CLI_SERVER_ADDRESS); +$port = intval($port)?:80; +$output = ''; + +$fp = fsockopen($host, $port, $errno, $errstr, 0.5); +if (!$fp) { + die("connect failed"); +} + +if(fwrite($fp, <<<HEADER +GET /index.php HTTP/1.1 +Host: {$host} + + +HEADER +)) { + while (!feof($fp)) { + $output .= fgets($fp); + } +} +echo $output; +@unlink($dir . "/syntax_error.php"); +fclose($fp); +?> +--EXPECTF-- +HTTP/1.1 200 OK +Host: %s +Connection: closed +X-Powered-By: PHP/%s +Content-type: text/html + +<br /> +<b>Fatal error</b>: Call to undefined function non_exists_function() in <b>%ssyntax_error.php</b> on line <b>%s</b><br /> diff --git a/sapi/cli/tests/php_cli_server_016.phpt b/sapi/cli/tests/php_cli_server_016.phpt new file mode 100644 index 000000000..973292491 --- /dev/null +++ b/sapi/cli/tests/php_cli_server_016.phpt @@ -0,0 +1,46 @@ +--TEST-- +Bug #60591 (Memory leak when access a non-exists file) +--DESCRIPTION-- +this is a indirect test for bug 50691, since mem leak is reproted in the server side +and require php compiled with --enable-debug +--SKIPIF-- +<?php +include "skipif.inc"; +?> +--FILE-- +<?php +include "php_cli_server.inc"; +php_cli_server_start(<<<PHP +if (preg_match('/\.(?:png|jpg|jpeg|gif)$/', \$_SERVER["REQUEST_URI"])) + return false; // serve the requested resource as-is. +else { + echo "here"; +} +PHP +); + +list($host, $port) = explode(':', PHP_CLI_SERVER_ADDRESS); +$port = intval($port)?:80; + +$fp = fsockopen($host, $port, $errno, $errstr, 0.5); +if (!$fp) { + die("connect failed"); +} + +if(fwrite($fp, <<<HEADER +POST /no-exists.jpg HTTP/1.1 +Host: {$host} + + +HEADER +)) { + while (!feof($fp)) { + echo fgets($fp); + break; + } +} + +fclose($fp); +?> +--EXPECTF-- +HTTP/1.1 404 Not Found diff --git a/sapi/continuity/capi.c b/sapi/continuity/capi.c index 4253cc28f..1ad26742c 100644 --- a/sapi/continuity/capi.c +++ b/sapi/continuity/capi.c @@ -343,9 +343,8 @@ static void sapi_capi_register_server_variables(zval * track_vars_array TSRMLS_D } -static void capi_log_message(char *message) +static void capi_log_message(char *message TSRMLS_DC) { - TSRMLS_FETCH(); capi_request_context *rc = (capi_request_context *) SG(server_context); logFmsg(0, "mod/php: %s", message); } diff --git a/sapi/embed/config.w32 b/sapi/embed/config.w32 index 8effedd25..7e8389056 100644 --- a/sapi/embed/config.w32 +++ b/sapi/embed/config.w32 @@ -1,8 +1,9 @@ // vim:ft=javascript -// $Id: config.w32 146968 2003-12-19 23:19:19Z wez $ +// $Id: config.w32 306241 2010-12-11 22:18:10Z pajoye $ ARG_ENABLE('embed', 'Embedded SAPI library', 'no'); if (PHP_EMBED != "no") { SAPI('embed', 'php_embed.c', 'php' + PHP_VERSION + 'embed.lib'); + PHP_INSTALL_HEADERS("sapi/embed", "php_embed.h"); } diff --git a/sapi/embed/php_embed.c b/sapi/embed/php_embed.c index a412d0515..3ef5a3144 100644 --- a/sapi/embed/php_embed.c +++ b/sapi/embed/php_embed.c @@ -90,7 +90,7 @@ static void php_embed_send_header(sapi_header_struct *sapi_header, void *server_ { } -static void php_embed_log_message(char *message) +static void php_embed_log_message(char *message TSRMLS_DC) { fprintf (stderr, "%s\n", message); } diff --git a/sapi/fpm/Makefile.frag b/sapi/fpm/Makefile.frag index c5cea7e52..6ed9e4a24 100644 --- a/sapi/fpm/Makefile.frag +++ b/sapi/fpm/Makefile.frag @@ -1,17 +1,9 @@ fpm: $(SAPI_FPM_PATH) -$(builddir)/fpm: - @mkdir -p $(builddir)/fpm - @mkdir -p $(builddir)/fpm/events - -$(SAPI_FPM_PATH): $(builddir)/fpm $(PHP_GLOBAL_OBJS) $(PHP_SAPI_OBJS) $(SAPI_EXTRA_DEPS) +$(SAPI_FPM_PATH): $(PHP_GLOBAL_OBJS) $(PHP_BINARY_OBJS) $(PHP_FPM_OBJS) $(BUILD_FPM) -$(builddir)/fpm/fpm_conf.lo: $(builddir)/../../main/build-defs.h - -install-build: install-fpm - -install-fpm: install-sapi +install-fpm: $(SAPI_FPM_PATH) @echo "Installing PHP FPM binary: $(INSTALL_ROOT)$(sbindir)/" @$(mkinstalldirs) $(INSTALL_ROOT)$(sbindir) @$(mkinstalldirs) $(INSTALL_ROOT)$(localstatedir)/log @@ -20,7 +12,6 @@ install-fpm: install-sapi @echo "Installing PHP FPM config: $(INSTALL_ROOT)$(sysconfdir)/" && \ $(mkinstalldirs) $(INSTALL_ROOT)$(sysconfdir) || : - @$(INSTALL_DATA) sapi/fpm/php-fpm.conf $(INSTALL_ROOT)$(sysconfdir)/php-fpm.conf.default || : @echo "Installing PHP FPM man page: $(INSTALL_ROOT)$(mandir)/man8/" diff --git a/sapi/fpm/config.m4 b/sapi/fpm/config.m4 index 953fa1f7b..89628100b 100644 --- a/sapi/fpm/config.m4 +++ b/sapi/fpm/config.m4 @@ -3,7 +3,7 @@ dnl $Id$ dnl PHP_ARG_ENABLE(fpm,, -[ --enable-fpm EXPERIMENTAL: Enable building of the fpm SAPI executable], no, no) +[ --enable-fpm Enable building of the fpm SAPI executable], no, no) dnl configure checks {{{ AC_DEFUN([AC_FPM_STDLIBS], @@ -582,11 +582,12 @@ if test "$PHP_FPM" != "no"; then AC_DEFINE_UNQUOTED(PHP_FPM_USER, "$php_fpm_user", [fpm user name]) AC_DEFINE_UNQUOTED(PHP_FPM_GROUP, "$php_fpm_group", [fpm group name]) + PHP_ADD_BUILD_DIR(sapi/fpm/fpm) + PHP_ADD_BUILD_DIR(sapi/fpm/fpm/events) PHP_OUTPUT(sapi/fpm/php-fpm.conf sapi/fpm/init.d.php-fpm sapi/fpm/php-fpm.8 sapi/fpm/status.html) - PHP_ADD_MAKEFILE_FRAGMENT([$abs_srcdir/sapi/fpm/Makefile.frag], [$abs_srcdir/sapi/fpm], [sapi/fpm]) + PHP_ADD_MAKEFILE_FRAGMENT([$abs_srcdir/sapi/fpm/Makefile.frag]) SAPI_FPM_PATH=sapi/fpm/php-fpm - PHP_SUBST(SAPI_FPM_PATH) if test "$fpm_trace_type" && test -f "$abs_srcdir/sapi/fpm/fpm/fpm_trace_$fpm_trace_type.c"; then PHP_FPM_TRACE_FILES="fpm/fpm_trace.c fpm/fpm_trace_$fpm_trace_type.c" @@ -594,7 +595,6 @@ if test "$PHP_FPM" != "no"; then PHP_FPM_CFLAGS="-I$abs_srcdir/sapi/fpm" - INSTALL_IT=":" PHP_FPM_FILES="fpm/fastcgi.c \ fpm/fpm.c \ fpm/fpm_children.c \ @@ -630,17 +630,19 @@ if test "$PHP_FPM" != "no"; then case $host_alias in *aix*) - BUILD_FPM="echo '\#! .' > php.sym && echo >>php.sym && nm -BCpg \`echo \$(PHP_GLOBAL_OBJS) \$(PHP_SAPI_OBJS) | sed 's/\([A-Za-z0-9_]*\)\.lo/\1.o/g'\` | \$(AWK) '{ if (((\$\$2 == \"T\") || (\$\$2 == \"D\") || (\$\$2 == \"B\")) && (substr(\$\$3,1,1) != \".\")) { print \$\$3 } }' | sort -u >> php.sym && \$(LIBTOOL) --mode=link \$(CC) -export-dynamic \$(CFLAGS_CLEAN) \$(EXTRA_CFLAGS) \$(EXTRA_LDFLAGS_PROGRAM) \$(LDFLAGS) -Wl,-brtl -Wl,-bE:php.sym \$(PHP_RPATHS) \$(PHP_GLOBAL_OBJS) \$(PHP_SAPI_OBJS) \$(EXTRA_LIBS) \$(SAPI_EXTRA_LIBS) \$(ZEND_EXTRA_LIBS) -o \$(SAPI_FPM_PATH)" + BUILD_FPM="echo '\#! .' > php.sym && echo >>php.sym && nm -BCpg \`echo \$(PHP_GLOBAL_OBJS) \$(PHP_BINARY_OBJS) \$(PHP_FPM_OBJS) | sed 's/\([A-Za-z0-9_]*\)\.lo/\1.o/g'\` | \$(AWK) '{ if (((\$\$2 == \"T\") || (\$\$2 == \"D\") || (\$\$2 == \"B\")) && (substr(\$\$3,1,1) != \".\")) { print \$\$3 } }' | sort -u >> php.sym && \$(LIBTOOL) --mode=link \$(CC) -export-dynamic \$(CFLAGS_CLEAN) \$(EXTRA_CFLAGS) \$(EXTRA_LDFLAGS_PROGRAM) \$(LDFLAGS) -Wl,-brtl -Wl,-bE:php.sym \$(PHP_RPATHS) \$(PHP_GLOBAL_OBJS) \$(PHP_FPM_OBJS) \$(EXTRA_LIBS) \$(FPM_EXTRA_LIBS) \$(ZEND_EXTRA_LIBS) -o \$(SAPI_FPM_PATH)" ;; *darwin*) - BUILD_FPM="\$(CC) \$(CFLAGS_CLEAN) \$(EXTRA_CFLAGS) \$(EXTRA_LDFLAGS_PROGRAM) \$(LDFLAGS) \$(NATIVE_RPATHS) \$(PHP_GLOBAL_OBJS:.lo=.o) \$(PHP_SAPI_OBJS:.lo=.o) \$(PHP_FRAMEWORKS) \$(EXTRA_LIBS) \$(SAPI_EXTRA_LIBS) \$(ZEND_EXTRA_LIBS) -o \$(SAPI_FPM_PATH)" + BUILD_FPM="\$(CC) \$(CFLAGS_CLEAN) \$(EXTRA_CFLAGS) \$(EXTRA_LDFLAGS_PROGRAM) \$(LDFLAGS) \$(NATIVE_RPATHS) \$(PHP_GLOBAL_OBJS:.lo=.o) \$(PHP_BINARY_OBJS:.lo=.o) \$(PHP_FPM_OBJS:.lo=.o) \$(PHP_FRAMEWORKS) \$(EXTRA_LIBS) \$(FPM_EXTRA_LIBS) \$(ZEND_EXTRA_LIBS) -o \$(SAPI_FPM_PATH)" ;; *) - BUILD_FPM="\$(LIBTOOL) --mode=link \$(CC) -export-dynamic \$(CFLAGS_CLEAN) \$(EXTRA_CFLAGS) \$(EXTRA_LDFLAGS_PROGRAM) \$(LDFLAGS) \$(PHP_RPATHS) \$(PHP_GLOBAL_OBJS) \$(PHP_SAPI_OBJS) \$(EXTRA_LIBS) \$(SAPI_EXTRA_LIBS) \$(ZEND_EXTRA_LIBS) -o \$(SAPI_FPM_PATH)" + BUILD_FPM="\$(LIBTOOL) --mode=link \$(CC) -export-dynamic \$(CFLAGS_CLEAN) \$(EXTRA_CFLAGS) \$(EXTRA_LDFLAGS_PROGRAM) \$(LDFLAGS) \$(PHP_RPATHS) \$(PHP_GLOBAL_OBJS) \$(PHP_BINARY_OBJS) \$(PHP_FPM_OBJS) \$(EXTRA_LIBS) \$(FPM_EXTRA_LIBS) \$(ZEND_EXTRA_LIBS) -o \$(SAPI_FPM_PATH)" ;; esac + PHP_SUBST(SAPI_FPM_PATH) PHP_SUBST(BUILD_FPM) + else AC_MSG_RESULT(no) fi diff --git a/sapi/fpm/fpm/fpm_main.c b/sapi/fpm/fpm/fpm_main.c index ea0dd149f..14dccbd14 100644 --- a/sapi/fpm/fpm/fpm_main.c +++ b/sapi/fpm/fpm/fpm_main.c @@ -558,7 +558,6 @@ void cgi_php_import_environment_variables(zval *array_ptr TSRMLS_DC) { fcgi_request *request; HashPosition pos; - int magic_quotes_gpc;; char *var, **val; uint var_len; ulong idx; @@ -591,11 +590,8 @@ void cgi_php_import_environment_variables(zval *array_ptr TSRMLS_DC) php_php_import_environment_variables(array_ptr TSRMLS_CC); request = (fcgi_request*) SG(server_context); - magic_quotes_gpc = PG(magic_quotes_gpc); filter_arg = (array_ptr == PG(http_globals)[TRACK_VARS_ENV])?PARSE_ENV:PARSE_SERVER; - /* turn off magic_quotes while importing environment variables */ - PG(magic_quotes_gpc) = 0; for (zend_hash_internal_pointer_reset_ex(request->env, &pos); zend_hash_get_current_key_ex(request->env, &var, &var_len, &idx, 0, &pos) == HASH_KEY_IS_STRING && zend_hash_get_current_data_ex(request->env, (void **) &val, &pos) == SUCCESS; @@ -607,7 +603,6 @@ void cgi_php_import_environment_variables(zval *array_ptr TSRMLS_DC) php_register_variable_safe(var, *val, new_val_len, array_ptr TSRMLS_CC); } } - PG(magic_quotes_gpc) = magic_quotes_gpc; } static void sapi_cgi_register_variables(zval *track_vars_array TSRMLS_DC) @@ -651,10 +646,8 @@ static void sapi_cgi_register_variables(zval *track_vars_array TSRMLS_DC) } } -static void sapi_cgi_log_message(char *message) +static void sapi_cgi_log_message(char *message TSRMLS_DC) { - TSRMLS_FETCH(); - if (CGIG(fcgi_logging)) { zlog(ZLOG_NOTICE, "PHP message: %s", message); } @@ -1471,7 +1464,7 @@ PHP_FUNCTION(fastcgi_finish_request) /* {{{ */ if (request->fd >= 0) { - php_end_ob_buffers(1 TSRMLS_CC); + php_output_end_all(TSRMLS_C); php_header(TSRMLS_C); fcgi_flush(request, 1); @@ -1507,7 +1500,7 @@ static zend_module_entry cgi_module_entry = { int main(int argc, char *argv[]) { int exit_status = SUCCESS; - int cgi = 0, c; + int cgi = 0, c, use_extended_info = 0; zend_file_handle file_handle; /* temporary locals */ @@ -1530,8 +1523,6 @@ int main(int argc, char *argv[]) int test_conf = 0; int php_information = 0; - fcgi_init(); - #ifdef HAVE_SIGNAL_H #if defined(SIGPIPE) && defined(SIG_IGN) signal(SIGPIPE, SIG_IGN); /* ignore SIGPIPE in standalone mode so @@ -1550,6 +1541,9 @@ int main(int argc, char *argv[]) sapi_startup(&cgi_sapi_module); cgi_sapi_module.php_ini_path_override = NULL; + cgi_sapi_module.php_ini_ignore_cwd = 1; + + fcgi_init(); #ifdef PHP_WIN32 _fmode = _O_BINARY; /* sets default for file streams to binary */ @@ -1616,7 +1610,7 @@ int main(int argc, char *argv[]) break; case 'e': /* enable extended info output */ - CG(compiler_options) |= ZEND_COMPILE_EXTENDED_INFO; + use_extended_info = 1; break; case 't': @@ -1625,7 +1619,6 @@ int main(int argc, char *argv[]) case 'm': /* list compiled in modules */ cgi_sapi_module.startup(&cgi_sapi_module); - php_output_startup(); php_output_activate(TSRMLS_C); SG(headers_sent) = 1; php_printf("[PHP Modules]\n"); @@ -1633,7 +1626,8 @@ int main(int argc, char *argv[]) php_printf("\n[Zend Modules]\n"); print_extensions(TSRMLS_C); php_printf("\n"); - php_end_ob_buffers(1 TSRMLS_CC); + php_output_end_all(TSRMLS_C); + php_output_deactivate(TSRMLS_C); fcgi_shutdown(); exit_status = 0; goto out; @@ -1646,11 +1640,11 @@ int main(int argc, char *argv[]) case 'h': case '?': cgi_sapi_module.startup(&cgi_sapi_module); - php_output_startup(); php_output_activate(TSRMLS_C); SG(headers_sent) = 1; php_cgi_usage(argv[0]); - php_end_ob_buffers(1 TSRMLS_CC); + php_output_end_all(TSRMLS_C); + php_output_deactivate(TSRMLS_C); fcgi_shutdown(); exit_status = 0; goto out; @@ -1694,16 +1688,16 @@ int main(int argc, char *argv[]) goto out; } - /* No other args are permitted here as there is not interactive mode */ + /* No other args are permitted here as there is no interactive mode */ if (argc != php_optind) { cgi_sapi_module.startup(&cgi_sapi_module); - php_output_startup(); php_output_activate(TSRMLS_C); SG(headers_sent) = 1; php_cgi_usage(argv[0]); - php_end_ob_buffers(1 TSRMLS_CC); - exit_status = 0; + php_output_end_all(TSRMLS_C); + php_output_deactivate(TSRMLS_C); fcgi_shutdown(); + exit_status = 0; goto out; } @@ -1724,6 +1718,10 @@ int main(int argc, char *argv[]) #endif return FAILURE; } + + if (use_extended_info) { + CG(compiler_options) |= ZEND_COMPILE_EXTENDED_INFO; + } /* check force_cgi after startup, so we have proper output */ if (cgi && CGIG(force_redirect)) { diff --git a/sapi/fpm/fpm/fpm_status.c b/sapi/fpm/fpm/fpm_status.c index a20e2dd89..8dbc26d00 100644 --- a/sapi/fpm/fpm/fpm_status.c +++ b/sapi/fpm/fpm/fpm_status.c @@ -1,5 +1,5 @@ - /* $Id: fpm_status.c 317901 2011-10-08 14:04:09Z fat $ */ + /* $Id: fpm_status.c 313454 2011-07-19 22:18:08Z fat $ */ /* (c) 2009 Jerome Loyet */ #include "php.h" @@ -377,7 +377,8 @@ int fpm_status_handle_request(TSRMLS_D) /* {{{ */ /* no need to test the var 'full' */ if (full_syntax) { - int i, len, first; + int i, first; + size_t len; char *query_string; struct timeval duration, now; #ifdef HAVE_FPM_LQ diff --git a/sapi/fpm/php-fpm.conf.in b/sapi/fpm/php-fpm.conf.in index 4e7952bdd..2dad9d7f5 100644 --- a/sapi/fpm/php-fpm.conf.in +++ b/sapi/fpm/php-fpm.conf.in @@ -142,9 +142,9 @@ group = @php_fpm_group@ ; Note: This value is mandatory. listen = 127.0.0.1:9000 -; Set listen(2) backlog. A value of '-1' means unlimited. +; Set listen(2) backlog. ; Default Value: 128 (-1 on FreeBSD and OpenBSD) -;listen.backlog = -1 +;listen.backlog = 128 ; Set permissions for unix socket, if one is used. In Linux, read/write ; permissions must be set in order to allow connections from a web server. Many @@ -399,7 +399,7 @@ pm.max_spare_servers = 3 ; %u: remote user ; ; Default: "%R - %u %t \"%m %r\" %s" -;access.format = %R - %u %t "%m %r%Q%q" %s %f %{mili}d %{kilo}M %C%% +;access.format = "%R - %u %t \"%m %r%Q%q\" %s %f %{mili}d %{kilo}M %C%%" ; The log file for slow requests ; Default Value: not set diff --git a/sapi/litespeed/Makefile.frag b/sapi/litespeed/Makefile.frag index e1af2b90c..b70e5e870 100644 --- a/sapi/litespeed/Makefile.frag +++ b/sapi/litespeed/Makefile.frag @@ -1,3 +1,9 @@ -$(SAPI_LITESPEED_PATH): $(PHP_GLOBAL_OBJS) $(PHP_SAPI_OBJS) +litespeed: $(SAPI_LITESPEED_PATH) + +$(SAPI_LITESPEED_PATH): $(PHP_GLOBAL_OBJS) $(PHP_BINARY_OBJS) $(PHP_LITESPEED_OBJS) $(BUILD_LITESPEED) +install-litespeed: $(SAPI_LITESPEED_PATH) + @echo "Installing PHP LitSpeed binary: $(INSTALL_ROOT)$(bindir)/" + @$(INSTALL) -m 0755 $(SAPI_LITESPEED_PATH) $(INSTALL_ROOT)$(bindir)/lsphp + diff --git a/sapi/litespeed/config.m4 b/sapi/litespeed/config.m4 index 3a54bb05e..d1c97a1b6 100644 --- a/sapi/litespeed/config.m4 +++ b/sapi/litespeed/config.m4 @@ -1,5 +1,5 @@ dnl -dnl $Id: config.m4 301063 2010-07-07 20:03:04Z gwang $ +dnl $Id: config.m4 305331 2010-11-13 23:13:07Z jani $ dnl AC_MSG_CHECKING(for LiteSpeed support) @@ -10,22 +10,21 @@ PHP_ARG_WITH(litespeed,, if test "$PHP_LITESPEED" != "no"; then PHP_ADD_MAKEFILE_FRAGMENT($abs_srcdir/sapi/litespeed/Makefile.frag,$abs_srcdir/sapi/litespeed,sapi/litespeed) SAPI_LITESPEED_PATH=sapi/litespeed/php - PHP_SUBST(SAPI_LITESPEED_PATH) PHP_SELECT_SAPI(litespeed, program, lsapi_main.c lsapilib.c, "", '$(SAPI_LITESPEED_PATH)') - INSTALL_IT="@echo \"Installing PHP LiteSpeed into: \$(INSTALL_ROOT)\$(bindir)/\"; \$(INSTALL) -m 0755 \$(SAPI_LITESPEED_PATH) \$(INSTALL_ROOT)\$(bindir)/lsphp" case $host_alias in *darwin*) - BUILD_LITESPEED="\$(CC) \$(CFLAGS_CLEAN) \$(EXTRA_CFLAGS) \$(EXTRA_LDFLAGS_PROGRAM) \$(LDFLAGS) \$(NATIVE_RPATHS) \$(PHP_GLOBAL_OBJS:.lo=.o) \$(PHP_SAPI_OBJS:.lo=.o) \$(PHP_FRAMEWORKS) \$(EXTRA_LIBS) \$(ZEND_EXTRA_LIBS) -o \$(SAPI_LITESPEED_PATH)" + BUILD_LITESPEED="\$(CC) \$(CFLAGS_CLEAN) \$(EXTRA_CFLAGS) \$(EXTRA_LDFLAGS_PROGRAM) \$(LDFLAGS) \$(NATIVE_RPATHS) \$(PHP_GLOBAL_OBJS:.lo=.o) \$(PHP_BINARY_OBJS:.lo=.o) \$(PHP_LITESPEED_OBJS:.lo=.o) \$(PHP_FRAMEWORKS) \$(EXTRA_LIBS) \$(ZEND_EXTRA_LIBS) -o \$(SAPI_LITESPEED_PATH)" ;; *cygwin*) SAPI_LITESPEED_PATH=sapi/litespeed/php.exe - BUILD_LITESPEED="\$(LIBTOOL) --mode=link \$(CC) -export-dynamic \$(CFLAGS_CLEAN) \$(EXTRA_CFLAGS) \$(EXTRA_LDFLAGS_PROGRAM) \$(LDFLAGS) \$(PHP_RPATHS) \$(PHP_GLOBAL_OBJS) \$(PHP_SAPI_OBJS) \$(EXTRA_LIBS) \$(ZEND_EXTRA_LIBS) -o \$(SAPI_LITESPEED_PATH)" + BUILD_LITESPEED="\$(LIBTOOL) --mode=link \$(CC) -export-dynamic \$(CFLAGS_CLEAN) \$(EXTRA_CFLAGS) \$(EXTRA_LDFLAGS_PROGRAM) \$(LDFLAGS) \$(PHP_RPATHS) \$(PHP_GLOBAL_OBJS) \$(PHP_BINARY_OBJS) \$(PHP_LITESPEED_OBJS) \$(EXTRA_LIBS) \$(ZEND_EXTRA_LIBS) -o \$(SAPI_LITESPEED_PATH)" ;; *) - BUILD_LITESPEED="\$(LIBTOOL) --mode=link \$(CC) -export-dynamic \$(CFLAGS_CLEAN) \$(EXTRA_CFLAGS) \$(EXTRA_LDFLAGS_PROGRAM) \$(LDFLAGS) \$(PHP_RPATHS) \$(PHP_GLOBAL_OBJS) \$(PHP_SAPI_OBJS) \$(EXTRA_LIBS) \$(ZEND_EXTRA_LIBS) -o \$(SAPI_LITESPEED_PATH)" + BUILD_LITESPEED="\$(LIBTOOL) --mode=link \$(CC) -export-dynamic \$(CFLAGS_CLEAN) \$(EXTRA_CFLAGS) \$(EXTRA_LDFLAGS_PROGRAM) \$(LDFLAGS) \$(PHP_RPATHS) \$(PHP_GLOBAL_OBJS) \$(PHP_BINARY_OBJS) \$(PHP_LITESPEED_OBJS) \$(EXTRA_LIBS) \$(ZEND_EXTRA_LIBS) -o \$(SAPI_LITESPEED_PATH)" ;; esac + PHP_SUBST(SAPI_LITESPEED_PATH) PHP_SUBST(BUILD_LITESPEED) fi diff --git a/sapi/litespeed/lsapi_main.c b/sapi/litespeed/lsapi_main.c index 1796f6382..5636381e3 100644 --- a/sapi/litespeed/lsapi_main.c +++ b/sapi/litespeed/lsapi_main.c @@ -16,7 +16,7 @@ +----------------------------------------------------------------------+ */ -/* $Id: lsapi_main.c 321616 2011-12-31 18:15:06Z gwang $ */ +/* $Id: lsapi_main.c 321617 2011-12-31 18:15:22Z gwang $ */ #include "php.h" #include "SAPI.h" diff --git a/sapi/litespeed/lsapilib.c b/sapi/litespeed/lsapilib.c index ac5892cd6..5a546d0e8 100644 --- a/sapi/litespeed/lsapilib.c +++ b/sapi/litespeed/lsapilib.c @@ -16,7 +16,7 @@ +----------------------------------------------------------------------+ */ -/* $Id: lsapilib.c 311935 2011-06-08 16:51:59Z gwang $ */ +/* $Id: lsapilib.c 319292 2011-11-16 03:46:54Z gwang $ */ /* Copyright (c) 2007, Lite Speed Technologies Inc. diff --git a/sapi/milter/Makefile.frag b/sapi/milter/Makefile.frag index 8dbdf7a8f..26200a196 100644 --- a/sapi/milter/Makefile.frag +++ b/sapi/milter/Makefile.frag @@ -1,2 +1,8 @@ -$(SAPI_MILTER_PATH): $(PHP_GLOBAL_OBJS) $(PHP_SAPI_OBJS) +milter: $(SAPI_MILTER_PATH) + +$(SAPI_MILTER_PATH): $(PHP_GLOBAL_OBJS) $(PHP_BINARY_OBJS) $(PHP_SAPI_OBJS) $(BUILD_MILTER) + +install-milter: $(SAPI_MILTER_PATH) + @$(INSTALL) -m 0755 $(SAPI_MILTER_PATH) $(bindir)/php-milter + diff --git a/sapi/milter/config.m4 b/sapi/milter/config.m4 index 6c212dca4..ce73b02fd 100644 --- a/sapi/milter/config.m4 +++ b/sapi/milter/config.m4 @@ -1,5 +1,5 @@ dnl -dnl $Id: config.m4 242949 2007-09-26 15:44:16Z cvs2svn $ +dnl $Id: config.m4 305331 2010-11-13 23:13:07Z jani $ dnl PHP_ARG_WITH(milter, for Milter support, @@ -25,8 +25,7 @@ if test "$PHP_MILTER" != "no"; then PHP_ADD_MAKEFILE_FRAGMENT($abs_srcdir/sapi/milter/Makefile.frag) PHP_SELECT_SAPI(milter, program, php_milter.c getopt.c,,'$(SAPI_MILTER_PATH)') PHP_ADD_LIBRARY_WITH_PATH(milter, $MILTERPATH,) - BUILD_MILTER="\$(LIBTOOL) --mode=link \$(CC) -export-dynamic \$(CFLAGS_CLEAN) \$(EXTRA_CFLAGS) \$(EXTRA_LDFLAGS) \$(LDFLAGS) \$(PHP_RPATHS) \$(PHP_GLOBAL_OBJS) \$(PHP_SAPI_OBJS) \$(EXTRA_LIBS) \$(ZEND_EXTRA_LIBS) -o \$(SAPI_MILTER_PATH)" - INSTALL_IT="\$(INSTALL) -m 0755 \$(SAPI_MILTER_PATH) \$(bindir)/php-milter" + BUILD_MILTER="\$(LIBTOOL) --mode=link \$(CC) -export-dynamic \$(CFLAGS_CLEAN) \$(EXTRA_CFLAGS) \$(EXTRA_LDFLAGS) \$(LDFLAGS) \$(PHP_RPATHS) \$(PHP_GLOBAL_OBJS) \$(PHP_BINARY_OBJS) \$(PHP_MILTER_OBJS) \$(EXTRA_LIBS) \$(ZEND_EXTRA_LIBS) -o \$(SAPI_MILTER_PATH)" PHP_SUBST(SAPI_MILTER_PATH) PHP_SUBST(BUILD_MILTER) fi diff --git a/sapi/milter/php_milter.c b/sapi/milter/php_milter.c index ce39501be..4ae1c528b 100644 --- a/sapi/milter/php_milter.c +++ b/sapi/milter/php_milter.c @@ -1040,11 +1040,10 @@ int main(int argc, char *argv[]) while ((c=ap_php_getopt(argc, argv, OPTSTRING))!=-1) { switch (c) { case '?': - php_output_startup(); - php_output_activate(TSRMLS_C); + php_output_tearup(); SG(headers_sent) = 1; php_milter_usage(argv[0]); - php_end_ob_buffers(1 TSRMLS_CC); + php_output_teardown(); exit(1); break; } @@ -1088,11 +1087,10 @@ int main(int argc, char *argv[]) case 'h': /* help & quit */ case '?': - php_output_startup(); - php_output_activate(TSRMLS_C); + php_output_tearup(); SG(headers_sent) = 1; php_milter_usage(argv[0]); - php_end_ob_buffers(1 TSRMLS_CC); + php_output_teardown(); exit(1); break; @@ -1112,7 +1110,7 @@ int main(int argc, char *argv[]) SG(headers_sent) = 1; SG(request_info).no_headers = 1; php_printf("PHP %s (%s) (built: %s %s)\nCopyright (c) 1997-2012 The PHP Group\n%s", PHP_VERSION, sapi_module.name, __DATE__, __TIME__, get_zend_version()); - php_end_ob_buffers(1 TSRMLS_CC); + php_output_teardown(); exit(1); break; diff --git a/sapi/nsapi/nsapi.c b/sapi/nsapi/nsapi.c index 353de227f..c4d016f0c 100644 --- a/sapi/nsapi/nsapi.c +++ b/sapi/nsapi/nsapi.c @@ -347,7 +347,7 @@ PHP_FUNCTION(nsapi_virtual) php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unable to include uri '%s' - Sub-requests do not work with zlib.output_compression", uri); RETURN_FALSE; } else { - php_end_ob_buffers(1 TSRMLS_CC); + php_output_end_all(TSRMLS_C); php_header(TSRMLS_C); /* do the sub-request */ @@ -414,9 +414,7 @@ PHP_FUNCTION(nsapi_request_headers) for (i=0; i < rc->rq->headers->hsize; i++) { entry=rc->rq->headers->ht[i]; while (entry) { - if (!PG(safe_mode) || strncasecmp(entry->param->name, "authorization", 13)) { - add_assoc_string(return_value, entry->param->name, entry->param->value, 1); - } + add_assoc_string(return_value, entry->param->name, entry->param->value, 1); entry=entry->next; } } @@ -676,24 +674,22 @@ static void sapi_nsapi_register_server_variables(zval *track_vars_array TSRMLS_D for (i=0; i < rc->rq->headers->hsize; i++) { entry=rc->rq->headers->ht[i]; while (entry) { - if (!PG(safe_mode) || strncasecmp(entry->param->name, "authorization", 13)) { - if (strcasecmp(entry->param->name, "content-length")==0 || strcasecmp(entry->param->name, "content-type")==0) { - value=estrdup(entry->param->name); - pos = 0; - } else { - spprintf(&value, 0, "HTTP_%s", entry->param->name); - pos = 5; - } - if (value) { - for(p = value + pos; *p; p++) { - *p = toupper(*p); - if (!isalnum(*p)) { - *p = '_'; - } + if (strcasecmp(entry->param->name, "content-length")==0 || strcasecmp(entry->param->name, "content-type")==0) { + value=estrdup(entry->param->name); + pos = 0; + } else { + spprintf(&value, 0, "HTTP_%s", entry->param->name); + pos = 5; + } + if (value) { + for(p = value + pos; *p; p++) { + *p = toupper(*p); + if (!isalnum(*p)) { + *p = '_'; } - php_register_variable(value, entry->param->value, track_vars_array TSRMLS_CC); - efree(value); } + php_register_variable(value, entry->param->value, track_vars_array TSRMLS_CC); + efree(value); } entry=entry->next; } @@ -779,9 +775,8 @@ static void sapi_nsapi_register_server_variables(zval *track_vars_array TSRMLS_D } } -static void nsapi_log_message(char *message) +static void nsapi_log_message(char *message TSRMLS_DC) { - TSRMLS_FETCH(); nsapi_request_context *rc = (nsapi_request_context *)SG(server_context); if (rc) { @@ -791,7 +786,7 @@ static void nsapi_log_message(char *message) } } -static time_t sapi_nsapi_get_request_time(TSRMLS_D) +static double sapi_nsapi_get_request_time(TSRMLS_D) { return REQ_TIME( ((nsapi_request_context *)SG(server_context))->rq ); } @@ -1035,7 +1030,7 @@ int NSAPI_PUBLIC php5_execute(pblock *pb, Session *sn, Request *rq) nsapi_php_ini_entries(NSLS_C TSRMLS_CC); - if (!PG(safe_mode)) php_handle_auth_data(pblock_findval("authorization", rq->headers) TSRMLS_CC); + php_handle_auth_data(pblock_findval("authorization", rq->headers) TSRMLS_CC); file_handle.type = ZEND_HANDLE_FILENAME; file_handle.filename = SG(request_info).path_translated; diff --git a/sapi/phttpd/phttpd.c b/sapi/phttpd/phttpd.c index 3aa196446..5930a1942 100644 --- a/sapi/phttpd/phttpd.c +++ b/sapi/phttpd/phttpd.c @@ -71,7 +71,6 @@ php_phttpd_sapi_header_handler(sapi_header_struct *sapi_header, sapi_headers_str { char *header_name, *header_content; char *p; - TSRMLS_FETCH(); http_sendheaders(PHG(cip)->fd, PHG(cip), SG(sapi_headers).http_response_code, NULL); @@ -97,8 +96,6 @@ php_phttpd_sapi_header_handler(sapi_header_struct *sapi_header, sapi_headers_str static int php_phttpd_sapi_send_headers(sapi_headers_struct *sapi_headers TSRMLS_DC) { - TSRMLS_FETCH(); - if (SG(sapi_headers).send_default_content_type) { fd_printf(PHG(cip)->fd,"Content-Type: text/html\n"); } @@ -115,7 +112,6 @@ php_phttpd_sapi_read_cookies(TSRMLS_D) /* int i; char *http_cookie = NULL; - NTSRMLS_FETCH(); i = Ns_SetIFind(NSG(conn->headers), "cookie"); if(i != -1) { @@ -135,7 +131,6 @@ php_phttpd_sapi_read_post(char *buf, uint count_bytes TSRMLS_DC) /* uint max_read; uint total_read = 0; - NTSRMLS_FETCH(); max_read = MIN(NSG(data_avail), count_bytes); @@ -243,12 +238,11 @@ php_phttpd_request_dtor(TSRMLS_D TSRMLS_DC) } -int php_doit(TSRMLS_D TSRMLS_DC) +int php_doit(TSRMLS_D) { struct stat sb; zend_file_handle file_handle; struct httpinfo *hip = PHG(cip)->hip; - TSRMLS_FETCH(); if (php_request_startup(TSRMLS_C) == FAILURE) { return -1; diff --git a/sapi/thttpd/README b/sapi/thttpd/README index b0245841a..02ac482e1 100644 --- a/sapi/thttpd/README +++ b/sapi/thttpd/README @@ -1,5 +1,5 @@ README FOR THTTPD MODULE (by Sascha Schumann) -($Date: 2003-02-19 11:57:21 +0100 (Wed, 19 Feb 2003) $) +($Date: 2003-02-19 02:57:21 -0800 (Wed, 19 Feb 2003) $) This is a SAPI module for PHP 4.x supporting thttpd, the tiny, turbo, throttling HTTP server by Jef Poskanzer. diff --git a/sapi/tux/README b/sapi/tux/README index 3a5294c77..d57f406dc 100644 --- a/sapi/tux/README +++ b/sapi/tux/README @@ -1,5 +1,5 @@ README FOR THE TUX MODULE (by Sascha Schumann) -($Date: 2004-01-17 14:00:38 +0100 (Sat, 17 Jan 2004) $) +($Date: 2004-01-17 05:00:38 -0800 (Sat, 17 Jan 2004) $) This is a SAPI module for the TUX web-server by Ingo Molnar. |