summaryrefslogtreecommitdiff
path: root/sapi/cgi
diff options
context:
space:
mode:
authorMark A. Hershberger <mah@debian.(none)>2009-03-25 00:37:27 -0400
committerMark A. Hershberger <mah@debian.(none)>2009-03-25 00:37:27 -0400
commit2d4e5b09576bb4f0ba716cc82cdf29ea04d9184b (patch)
tree41ccc042009cba53e4ce43e727fcba4c1cfbf7f3 /sapi/cgi
parentd29a4fd2dd3b5d4cf6e80b602544d7b71d794e76 (diff)
downloadphp-2d4e5b09576bb4f0ba716cc82cdf29ea04d9184b.tar.gz
Imported Upstream version 5.2.2upstream/5.2.2
Diffstat (limited to 'sapi/cgi')
-rw-r--r--sapi/cgi/CREDITS2
-rw-r--r--sapi/cgi/cgi_main.c439
-rw-r--r--sapi/cgi/config9.m434
-rw-r--r--sapi/cgi/fastcgi.c369
-rw-r--r--sapi/cgi/fastcgi.h8
-rw-r--r--sapi/cgi/getopt.c40
-rw-r--r--sapi/cgi/php_getopt.h11
-rw-r--r--sapi/cgi/tests/001.phpt22
-rw-r--r--sapi/cgi/tests/002.phpt52
-rw-r--r--sapi/cgi/tests/003.phpt62
-rw-r--r--sapi/cgi/tests/004.phpt43
-rw-r--r--sapi/cgi/tests/005.phpt30
-rw-r--r--sapi/cgi/tests/006.phpt60
-rw-r--r--sapi/cgi/tests/007.phpt22
-rw-r--r--sapi/cgi/tests/008.phpt54
-rw-r--r--sapi/cgi/tests/include.inc57
-rw-r--r--sapi/cgi/tests/skipif.inc17
17 files changed, 1043 insertions, 279 deletions
diff --git a/sapi/cgi/CREDITS b/sapi/cgi/CREDITS
index a1c6a0be5..1a2ec4901 100644
--- a/sapi/cgi/CREDITS
+++ b/sapi/cgi/CREDITS
@@ -1,2 +1,2 @@
CGI / FastCGI
-Rasmus Lerdorf, Stig Bakken, Shane Caraveo
+Rasmus Lerdorf, Stig Bakken, Shane Caraveo, Dmitry Stogov
diff --git a/sapi/cgi/cgi_main.c b/sapi/cgi/cgi_main.c
index 51fc7c4c3..7ce569466 100644
--- a/sapi/cgi/cgi_main.c
+++ b/sapi/cgi/cgi_main.c
@@ -2,7 +2,7 @@
+----------------------------------------------------------------------+
| PHP Version 5 |
+----------------------------------------------------------------------+
- | Copyright (c) 1997-2006 The PHP Group |
+ | Copyright (c) 1997-2007 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 |
@@ -21,7 +21,7 @@
+----------------------------------------------------------------------+
*/
-/* $Id: cgi_main.c,v 1.267.2.15.2.13 2006/09/23 12:27:40 tony2001 Exp $ */
+/* $Id: cgi_main.c,v 1.267.2.15.2.36 2007/04/17 20:00:53 sniper Exp $ */
#include "php.h"
#include "php_globals.h"
@@ -119,12 +119,11 @@ static pid_t pgroup;
static char *php_optarg = NULL;
static int php_optind = 1;
+static zend_module_entry cgi_module_entry;
static const opt_struct OPTIONS[] = {
{'a', 0, "interactive"},
-#ifndef PHP_WIN32
{'b', 1, "bindpath"},
-#endif
{'C', 0, "no-chdir"},
{'c', 1, "php-ini"},
{'d', 1, "define"},
@@ -145,17 +144,32 @@ static const opt_struct OPTIONS[] = {
{'-', 0, NULL} /* end of args */
};
+typedef struct _php_cgi_globals_struct {
+ zend_bool rfc2616_headers;
+ zend_bool nph;
+ zend_bool check_shebang_line;
#if ENABLE_PATHINFO_CHECK
-/* true global. this is retreived once only, even for fastcgi */
-long fix_pathinfo = 1;
+ zend_bool fix_pathinfo;
+#endif
+#if FORCE_CGI_REDIRECT
+ zend_bool force_redirect;
+ char *redirect_status_env;
#endif
-
#if PHP_FASTCGI
-long fcgi_logging = 1;
+ zend_bool fcgi_logging;
+# ifdef PHP_WIN32
+ zend_bool impersonate;
+# endif
#endif
+} php_cgi_globals_struct;
-static long rfc2616_headers = 0;
-static long cgi_nph = 0;
+#ifdef ZTS
+static int php_cgi_globals_id;
+#define CGIG(v) TSRMG(php_cgi_globals_id, php_cgi_globals_struct *, v)
+#else
+static php_cgi_globals_struct php_cgi_globals;
+#define CGIG(v) (php_cgi_globals.v)
+#endif
#ifdef PHP_WIN32
#define TRANSLATE_SLASHES(path) \
@@ -304,12 +318,12 @@ static int sapi_cgi_send_headers(sapi_headers_struct *sapi_headers TSRMLS_DC)
return SAPI_HEADER_SENT_SUCCESSFULLY;
}
- if (cgi_nph || SG(sapi_headers).http_response_code != 200)
+ if (CGIG(nph) || SG(sapi_headers).http_response_code != 200)
{
int len;
- if (rfc2616_headers && SG(sapi_headers).http_status_line) {
- len = snprintf(buf, SAPI_CGI_MAX_HEADER_LENGTH,
+ if (CGIG(rfc2616_headers) && SG(sapi_headers).http_status_line) {
+ len = slprintf(buf, SAPI_CGI_MAX_HEADER_LENGTH,
"%s\r\n", SG(sapi_headers).http_status_line);
if (len > SAPI_CGI_MAX_HEADER_LENGTH) {
@@ -317,7 +331,7 @@ static int sapi_cgi_send_headers(sapi_headers_struct *sapi_headers TSRMLS_DC)
}
} else {
- len = sprintf(buf, "Status: %d\r\n", SG(sapi_headers).http_response_code);
+ len = slprintf(buf, sizeof(buf), "Status: %d\r\n", SG(sapi_headers).http_response_code);
}
PHPWRITE_H(buf, len);
@@ -340,18 +354,14 @@ static int sapi_cgi_send_headers(sapi_headers_struct *sapi_headers TSRMLS_DC)
static int sapi_cgi_read_post(char *buffer, uint count_bytes TSRMLS_DC)
{
- uint read_bytes=0, tmp_read_bytes;
-#if PHP_FASTCGI
- char *pos = buffer;
-#endif
+ int read_bytes=0, tmp_read_bytes;
count_bytes = MIN(count_bytes, (uint) SG(request_info).content_length - SG(read_post_bytes));
while (read_bytes < count_bytes) {
#if PHP_FASTCGI
if (fcgi_is_fastcgi()) {
fcgi_request *request = (fcgi_request*) SG(server_context);
- tmp_read_bytes = fcgi_read(request, pos, count_bytes - read_bytes);
- pos += tmp_read_bytes;
+ tmp_read_bytes = fcgi_read(request, buffer + read_bytes, count_bytes - read_bytes);
} else {
tmp_read_bytes = read(0, buffer + read_bytes, count_bytes - read_bytes);
}
@@ -432,13 +442,13 @@ static char *_sapi_cgibin_putenv(char *name, char *value TSRMLS_DC)
#endif
#if !HAVE_SETENV
if (value) {
- len = snprintf(buf, len - 1, "%s=%s", name, value);
+ len = slprintf(buf, len - 1, "%s=%s", name, value);
putenv(buf);
}
#endif
#if !HAVE_UNSETENV
if (!value) {
- len = snprintf(buf, len - 1, "%s=", name);
+ len = slprintf(buf, len - 1, "%s=", name);
putenv(buf);
}
#endif
@@ -483,6 +493,7 @@ void cgi_php_import_environment_variables(zval *array_ptr TSRMLS_DC)
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;
@@ -490,7 +501,10 @@ void cgi_php_import_environment_variables(zval *array_ptr TSRMLS_DC)
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)) {
- php_register_variable(var, *val, array_ptr TSRMLS_CC);
+ 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;
}
@@ -499,20 +513,25 @@ void cgi_php_import_environment_variables(zval *array_ptr TSRMLS_DC)
static void sapi_cgi_register_variables(zval *track_vars_array TSRMLS_DC)
{
+ unsigned int new_val_len;
+ char *val = SG(request_info).request_uri ? SG(request_info).request_uri : "";
/* In CGI mode, we consider the environment to be a part of the server
* variables
*/
php_import_environment_variables(track_vars_array TSRMLS_CC);
/* Build the special-case PHP_SELF variable for the CGI version */
- php_register_variable("PHP_SELF", (SG(request_info).request_uri ? SG(request_info).request_uri : ""), track_vars_array TSRMLS_CC);
+ if (sapi_module.input_filter(PARSE_SERVER, "PHP_SELF", &val, strlen(val), &new_val_len TSRMLS_CC)) {
+ php_register_variable_safe("PHP_SELF", val, new_val_len, track_vars_array TSRMLS_CC);
+ }
}
static void sapi_cgi_log_message(char *message)
{
#if PHP_FASTCGI
- if (fcgi_is_fastcgi() && fcgi_logging) {
+ TSRMLS_FETCH();
+
+ if (fcgi_is_fastcgi() && CGIG(fcgi_logging)) {
fcgi_request *request;
- TSRMLS_FETCH();
request = (fcgi_request*) SG(server_context);
if (request) {
@@ -547,7 +566,7 @@ static int sapi_cgi_deactivate(TSRMLS_D)
static int php_cgi_startup(sapi_module_struct *sapi_module)
{
- if (php_module_startup(sapi_module, NULL, 0) == FAILURE) {
+ if (php_module_startup(sapi_module, &cgi_module_entry, 1) == FAILURE) {
return FAILURE;
}
return SUCCESS;
@@ -609,7 +628,7 @@ static void php_cgi_usage(char *argv0)
php_printf("Usage: %s [-q] [-h] [-s] [-v] [-i] [-f <file>]\n"
" %s <file> [args...]\n"
" -a Run interactively\n"
-#if PHP_FASTCGI && !defined(PHP_WIN32)
+#if PHP_FASTCGI
" -b <address:port>|<port> Bind Path for external FASTCGI Server mode\n"
#endif
" -C Do not chdir to the script's directory\n"
@@ -737,34 +756,18 @@ static void init_request_info(TSRMLS_D)
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);
- if (fix_pathinfo) {
+ if (CGIG(fix_pathinfo)) {
+ char *real_path;
+ char *orig_path_translated = env_path_translated;
+ char *orig_path_info = env_path_info;
+ char *orig_script_name = env_script_name;
+ char *orig_script_filename = env_script_filename;
- /* save the originals first for anything we change later */
- if (env_path_translated) {
- _sapi_cgibin_putenv("ORIG_PATH_TRANSLATED", env_path_translated TSRMLS_CC);
- }
- if (env_path_info) {
- _sapi_cgibin_putenv("ORIG_PATH_INFO", env_path_info TSRMLS_CC);
- }
- if (env_script_name) {
- _sapi_cgibin_putenv("ORIG_SCRIPT_NAME", env_script_name TSRMLS_CC);
- }
- if (env_script_filename) {
- _sapi_cgibin_putenv("ORIG_SCRIPT_FILENAME", env_script_filename TSRMLS_CC);
- }
- if (!env_document_root) {
- /* ini version of document root */
- if (!env_document_root) {
- env_document_root = PG(doc_root);
- }
- /* set the document root, this makes a more
- consistent env for php scripts */
- if (env_document_root) {
- env_document_root = _sapi_cgibin_putenv("DOCUMENT_ROOT", env_document_root TSRMLS_CC);
- /* fix docroot */
- TRANSLATE_SLASHES(env_document_root);
- }
- }
+ if (!env_document_root && PG(doc_root)) {
+ env_document_root = _sapi_cgibin_putenv("DOCUMENT_ROOT", PG(doc_root) TSRMLS_CC);
+ /* fix docroot */
+ TRANSLATE_SLASHES(env_document_root);
+ }
if (env_path_translated != NULL && env_redirect_url != NULL) {
/*
@@ -774,7 +777,7 @@ static void init_request_info(TSRMLS_D)
*/
script_path_translated = env_path_translated;
/* we correct SCRIPT_NAME now in case we don't have PATH_INFO */
- env_script_name = _sapi_cgibin_putenv("SCRIPT_NAME", env_redirect_url TSRMLS_CC);
+ env_script_name = env_redirect_url;
}
#ifdef __riscos__
@@ -788,7 +791,8 @@ static void init_request_info(TSRMLS_D)
* of it by stat'ing back through the '/'
* this fixes url's like /info.php/test
*/
- if (script_path_translated && stat(script_path_translated, &st) == -1 ) {
+ if (script_path_translated &&
+ (real_path = tsrm_realpath(script_path_translated, NULL TSRMLS_CC)) == NULL) {
char *pt = estrdup(script_path_translated);
int len = strlen(pt);
char *ptr;
@@ -815,8 +819,19 @@ static void init_request_info(TSRMLS_D)
int pilen = strlen(env_path_info);
char *path_info = env_path_info + pilen - slen;
- env_path_info = _sapi_cgibin_putenv("PATH_INFO", path_info TSRMLS_CC);
- script_path_translated = _sapi_cgibin_putenv("SCRIPT_FILENAME", pt TSRMLS_CC);
+ if (orig_path_info != path_info) {
+ if (orig_path_info) {
+ _sapi_cgibin_putenv("ORIG_PATH_INFO", orig_path_info TSRMLS_CC);
+ }
+ env_path_info = _sapi_cgibin_putenv("PATH_INFO", path_info TSRMLS_CC);
+ }
+ 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);
+ }
+ script_path_translated = _sapi_cgibin_putenv("SCRIPT_FILENAME", pt TSRMLS_CC);
+ }
TRANSLATE_SLASHES(pt);
/* figure out docroot
@@ -829,7 +844,7 @@ static void init_request_info(TSRMLS_D)
int path_translated_len = 0;
char *path_translated = NULL;
- if (env_document_root[l - 1] == '/') {
+ if (l && env_document_root[l - 1] == '/') {
--l;
}
@@ -839,14 +854,17 @@ static void init_request_info(TSRMLS_D)
*
* SCRIPT_NAME is the portion of the path beyond docroot
*/
- env_script_name = _sapi_cgibin_putenv("SCRIPT_NAME", pt + l TSRMLS_CC);
+ env_script_name = pt + l;
/* PATH_TRANSATED = DOCUMENT_ROOT + PATH_INFO */
- path_translated_len = l + strlen(env_path_info) + 2;
- path_translated = (char *) emalloc(path_translated_len);
- *path_translated = 0;
- strncat(path_translated, env_document_root, l);
- strcat(path_translated, env_path_info);
+ path_translated_len = l + strlen(env_path_info);
+ path_translated = (char *) emalloc(path_translated_len + 1);
+ memcpy(path_translated, env_document_root, l);
+ memcpy(path_translated + l, env_path_info, (path_translated_len - l));
+ path_translated[path_translated_len] = '\0';
+ if (orig_path_translated) {
+ _sapi_cgibin_putenv("ORIG_PATH_TRANSLATED", orig_path_translated TSRMLS_CC);
+ }
env_path_translated = _sapi_cgibin_putenv("PATH_TRANSLATED", path_translated TSRMLS_CC);
efree(path_translated);
} else if (env_script_name &&
@@ -854,13 +872,16 @@ static void init_request_info(TSRMLS_D)
) {
/* PATH_TRANSATED = PATH_TRANSATED - SCRIPT_NAME + PATH_INFO */
int ptlen = strlen(pt) - strlen(env_script_name);
- int path_translated_len = ptlen + strlen(env_path_info) + 2;
+ int path_translated_len = ptlen + strlen(env_path_info);
char *path_translated = NULL;
- path_translated = (char *) emalloc(path_translated_len);
- *path_translated = 0;
- strncat(path_translated, pt, ptlen);
- strcat(path_translated, env_path_info);
+ path_translated = (char *) emalloc(path_translated_len + 1);
+ memcpy(path_translated, pt, ptlen);
+ memcpy(path_translated + ptlen, env_path_info, path_translated_len - ptlen);
+ path_translated[path_translated_len] = '\0';
+ if (orig_path_translated) {
+ _sapi_cgibin_putenv("ORIG_PATH_TRANSLATED", orig_path_translated TSRMLS_CC);
+ }
env_path_translated = _sapi_cgibin_putenv("PATH_TRANSLATED", path_translated TSRMLS_CC);
efree(path_translated);
}
@@ -873,19 +894,52 @@ static void init_request_info(TSRMLS_D)
* a valid path... we will fail, badly. of course we would
* have failed anyway... we output 'no input file' now.
*/
+ if (orig_script_filename) {
+ _sapi_cgibin_putenv("ORIG_SCRIPT_FILENAME", orig_script_filename TSRMLS_CC);
+ }
script_path_translated = _sapi_cgibin_putenv("SCRIPT_FILENAME", NULL TSRMLS_CC);
SG(sapi_headers).http_response_code = 404;
}
+ 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);
+ }
+ SG(request_info).request_uri = _sapi_cgibin_putenv("SCRIPT_NAME", env_script_name TSRMLS_CC);
+ } else {
+ SG(request_info).request_uri = orig_script_name;
+ }
if (pt) {
efree(pt);
}
} else {
/* make sure path_info/translated are empty */
- script_path_translated = _sapi_cgibin_putenv("SCRIPT_FILENAME", script_path_translated TSRMLS_CC);
- _sapi_cgibin_putenv("PATH_INFO", NULL TSRMLS_CC);
- _sapi_cgibin_putenv("PATH_TRANSLATED", NULL TSRMLS_CC);
- }
- SG(request_info).request_uri = sapi_cgibin_getenv("SCRIPT_NAME", sizeof("SCRIPT_NAME")-1 TSRMLS_CC);
+ if (!orig_script_filename ||
+ (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);
+ }
+ script_path_translated = _sapi_cgibin_putenv("SCRIPT_FILENAME", script_path_translated TSRMLS_CC);
+ }
+ if (orig_path_info) {
+ _sapi_cgibin_putenv("ORIG_PATH_INFO", orig_path_info TSRMLS_CC);
+ _sapi_cgibin_putenv("PATH_INFO", NULL TSRMLS_CC);
+ }
+ if (orig_path_translated) {
+ _sapi_cgibin_putenv("ORIG_PATH_TRANSLATED", orig_path_translated TSRMLS_CC);
+ _sapi_cgibin_putenv("PATH_TRANSLATED", NULL TSRMLS_CC);
+ }
+ if (env_script_name != orig_script_name) {
+ if (orig_script_name) {
+ _sapi_cgibin_putenv("ORIG_SCRIPT_NAME", orig_script_name TSRMLS_CC);
+ }
+ SG(request_info).request_uri = _sapi_cgibin_putenv("SCRIPT_NAME", env_script_name TSRMLS_CC);
+ } else {
+ SG(request_info).request_uri = env_script_name;
+ }
+ }
+ free(real_path);
} else {
#endif
/* pre 4.3 behaviour, shouldn't be used but provides BC */
@@ -941,20 +995,95 @@ void fastcgi_cleanup(int signal)
}
#endif
+PHP_INI_BEGIN()
+ STD_PHP_INI_ENTRY("cgi.rfc2616_headers", "0", PHP_INI_ALL, OnUpdateBool, rfc2616_headers, php_cgi_globals_struct, php_cgi_globals)
+ STD_PHP_INI_ENTRY("cgi.nph", "0", PHP_INI_ALL, OnUpdateBool, nph, php_cgi_globals_struct, php_cgi_globals)
+ STD_PHP_INI_ENTRY("cgi.check_shebang_line", "1", PHP_INI_SYSTEM, OnUpdateBool, check_shebang_line, php_cgi_globals_struct, php_cgi_globals)
+#if FORCE_CGI_REDIRECT
+ STD_PHP_INI_ENTRY("cgi.force_redirect", "1", PHP_INI_SYSTEM, OnUpdateBool, force_redirect, php_cgi_globals_struct, php_cgi_globals)
+ STD_PHP_INI_ENTRY("cgi.redirect_status_env", NULL, PHP_INI_SYSTEM, OnUpdateString, redirect_status_env, php_cgi_globals_struct, php_cgi_globals)
+#endif
+#if ENABLE_PATHINFO_CHECK
+ STD_PHP_INI_ENTRY("cgi.fix_pathinfo", "1", PHP_INI_SYSTEM, OnUpdateBool, fix_pathinfo, php_cgi_globals_struct, php_cgi_globals)
+#endif
#if PHP_FASTCGI
-#ifndef PHP_WIN32
-static int is_port_number(const char *bindpath)
+ STD_PHP_INI_ENTRY("fastcgi.logging", "1", PHP_INI_SYSTEM, OnUpdateBool, fcgi_logging, php_cgi_globals_struct, php_cgi_globals)
+# ifdef PHP_WIN32
+ STD_PHP_INI_ENTRY("fastcgi.impersonate", "0", PHP_INI_SYSTEM, OnUpdateBool, impersonate, php_cgi_globals_struct, php_cgi_globals)
+# endif
+#endif
+PHP_INI_END()
+
+/* {{{ php_cgi_globals_ctor
+ */
+static void php_cgi_globals_ctor(php_cgi_globals_struct *php_cgi_globals TSRMLS_DC)
{
- while (*bindpath) {
- if (*bindpath < '0' || *bindpath > '9') {
- return 0;
- }
- bindpath++;
- }
- return 1;
-}
+ php_cgi_globals->rfc2616_headers = 0;
+ php_cgi_globals->nph = 0;
+ php_cgi_globals->check_shebang_line = 1;
+#if FORCE_CGI_REDIRECT
+ php_cgi_globals->force_redirect = 1;
+ php_cgi_globals->redirect_status_env = NULL;
+#endif
+#if ENABLE_PATHINFO_CHECK
+ php_cgi_globals->fix_pathinfo = 1;
#endif
+#if PHP_FASTCGI
+ php_cgi_globals->fcgi_logging = 1;
+# ifdef PHP_WIN32
+ php_cgi_globals->impersonate = 0;
+# endif
+#endif
+}
+/* }}} */
+
+/* {{{ PHP_MINIT_FUNCTION
+ */
+static PHP_MINIT_FUNCTION(cgi)
+{
+#ifdef ZTS
+ ts_allocate_id(&php_cgi_globals_id, sizeof(php_cgi_globals_struct), (ts_allocate_ctor) php_cgi_globals_ctor, NULL);
+#else
+ php_cgi_globals_ctor(&php_cgi_globals TSRMLS_CC);
#endif
+ REGISTER_INI_ENTRIES();
+ return SUCCESS;
+}
+/* }}} */
+
+/* {{{ PHP_MSHUTDOWN_FUNCTION
+ */
+static PHP_MSHUTDOWN_FUNCTION(cgi)
+{
+ UNREGISTER_INI_ENTRIES();
+ return SUCCESS;
+}
+/* }}} */
+
+/* {{{ PHP_MINFO_FUNCTION
+ */
+static PHP_MINFO_FUNCTION(cgi)
+{
+ DISPLAY_INI_ENTRIES();
+}
+/* }}} */
+
+static zend_module_entry cgi_module_entry = {
+ STANDARD_MODULE_HEADER,
+#if PHP_FASTCGI
+ "cgi-fcgi",
+#else
+ "cgi",
+#endif
+ NULL,
+ PHP_MINIT(cgi),
+ PHP_MSHUTDOWN(cgi),
+ NULL,
+ NULL,
+ PHP_MINFO(cgi),
+ NO_VERSION_YET,
+ STANDARD_MODULE_PROPERTIES
+};
/* {{{ main
*/
@@ -973,10 +1102,6 @@ int main(int argc, char *argv[])
char *orig_optarg = php_optarg;
char *script_file = NULL;
int ini_entries_len = 0;
-#if FORCE_CGI_REDIRECT
- long force_redirect = 1;
- char *redirect_status_env = NULL;
-#endif
/* end of temporary locals */
#ifdef ZTS
@@ -991,14 +1116,10 @@ int main(int argc, char *argv[])
int max_requests = 500;
int requests = 0;
int fastcgi = fcgi_is_fastcgi();
-#ifndef PHP_WIN32
char *bindpath = NULL;
-#endif
int fcgi_fd = 0;
fcgi_request request;
-#ifdef PHP_WIN32
- long impersonate = 0;
-#else
+#ifndef PHP_WIN32
int status = 0;
#endif
#endif /* PHP_FASTCGI */
@@ -1030,6 +1151,7 @@ int main(int argc, char *argv[])
#endif
sapi_startup(&cgi_sapi_module);
+ cgi_sapi_module.php_ini_path_override = NULL;
#ifdef PHP_WIN32
_fmode = _O_BINARY; /* sets default for file streams to binary */
@@ -1057,6 +1179,9 @@ int main(int argc, char *argv[])
while ((c = php_getopt(argc, argv, OPTIONS, &php_optarg, &php_optind, 0)) != -1) {
switch (c) {
case 'c':
+ if (cgi_sapi_module.php_ini_path_override) {
+ free(cgi_sapi_module.php_ini_path_override);
+ }
cgi_sapi_module.php_ini_path_override = strdup(php_optarg);
break;
case 'n':
@@ -1094,7 +1219,6 @@ int main(int argc, char *argv[])
break;
}
#if PHP_FASTCGI
-#ifndef PHP_WIN32
/* if we're started on command line, check to see if
we are being started as an 'external' fastcgi
server by accepting a bindpath parameter. */
@@ -1104,7 +1228,10 @@ int main(int argc, char *argv[])
}
break;
#endif
-#endif
+ case 's': /* generate highlighted HTML from source */
+ behavior = PHP_MODE_HIGHLIGHT;
+ break;
+
}
}
@@ -1123,7 +1250,7 @@ int main(int argc, char *argv[])
cgi_sapi_module.executable_location = argv[0];
/* startup after we get the above ini override se we get things right */
- if (php_module_startup(&cgi_sapi_module, NULL, 0) == FAILURE) {
+ if (cgi_sapi_module.startup(&cgi_sapi_module) == FAILURE) {
#ifdef ZTS
tsrm_shutdown();
#endif
@@ -1132,13 +1259,7 @@ int main(int argc, char *argv[])
#if FORCE_CGI_REDIRECT
/* check force_cgi after startup, so we have proper output */
- if (cfg_get_long("cgi.force_redirect", &force_redirect) == FAILURE) {
- force_redirect = 1;
- }
- if (cgi && force_redirect) {
- if (cfg_get_string("cgi.redirect_status_env", &redirect_status_env) == FAILURE) {
- redirect_status_env = NULL;
- }
+ if (cgi && CGIG(force_redirect)) {
/* Apache will generate REDIRECT_STATUS,
* Netscape and redirect.so will generate HTTP_REDIRECT_STATUS.
* redirect.so and installation instructions available from
@@ -1149,7 +1270,7 @@ int main(int argc, char *argv[])
&& !getenv ("HTTP_REDIRECT_STATUS")
/* this is to allow a different env var to be configured
in case some server does something different than above */
- && (!redirect_status_env || !getenv(redirect_status_env))
+ && (!CGIG(redirect_status_env) || !getenv(CGIG(redirect_status_env)))
) {
SG(sapi_headers).http_response_code = 400;
PUTS("<b>Security Alert!</b> The PHP CGI cannot be accessed directly.\n\n\
@@ -1176,52 +1297,11 @@ consult the installation file that came with this distribution, or visit \n\
}
#endif /* FORCE_CGI_REDIRECT */
-#if ENABLE_PATHINFO_CHECK
- if (cfg_get_long("cgi.fix_pathinfo", &fix_pathinfo) == FAILURE) {
- fix_pathinfo = 1;
- }
-#endif
-
-#if PHP_FASTCGI
- if (cfg_get_long("fastcgi.logging", &fcgi_logging) == FAILURE) {
- fcgi_logging = 1;
- }
-#endif
-
- /* Check wheater to send RFC2616 style headers compatible with
- * PHP versions 4.2.3 and earlier compatible with web servers
- * such as IIS. Default is informal CGI RFC header compatible
- * with Apache.
- */
- if (cfg_get_long("cgi.rfc2616_headers", &rfc2616_headers) == FAILURE) {
- rfc2616_headers = 0;
- }
-
- if (cfg_get_long("cgi.nph", &cgi_nph) == FAILURE) {
- cgi_nph = 0;
- }
-
#if PHP_FASTCGI
-#ifndef PHP_WIN32
/* for windows, socket listening is broken in the fastcgi library itself
so dissabling this feature on windows till time is available to fix it */
if (bindpath) {
- /* Pass on the arg to the FastCGI library, with one exception.
- * If just a port is specified, then we prepend a ':' onto the
- * path (it's what the fastcgi library expects)
- */
- if (strchr(bindpath, ':') == NULL && is_port_number(bindpath)) {
- char *tmp;
-
- tmp = malloc(strlen(bindpath) + 2);
- tmp[0] = ':';
- memcpy(tmp + 1, bindpath, strlen(bindpath) + 1);
-
- fcgi_fd = fcgi_listen(tmp, 128);
- free(tmp);
- } else {
- fcgi_fd = fcgi_listen(bindpath, 128);
- }
+ fcgi_fd = fcgi_listen(bindpath, 128);
if (fcgi_fd < 0) {
fprintf(stderr, "Couldn't create FastCGI listen socket on port %s\n", bindpath);
#ifdef ZTS
@@ -1231,7 +1311,7 @@ consult the installation file that came with this distribution, or visit \n\
}
fastcgi = fcgi_is_fastcgi();
}
-#endif
+
if (fastcgi) {
/* How many times to run PHP scripts before dying */
if (getenv("PHP_FCGI_MAX_REQUESTS")) {
@@ -1280,6 +1360,10 @@ consult the installation file that came with this distribution, or visit \n\
exit(1);
}
+ if (fcgi_in_shutdown()) {
+ exit(0);
+ }
+
while (parent) {
do {
#ifdef DEBUG_FASTCGI
@@ -1351,11 +1435,8 @@ consult the installation file that came with this distribution, or visit \n\
#ifdef PHP_WIN32
/* attempt to set security impersonation for fastcgi
will only happen on NT based OS, others will ignore it. */
- if (fastcgi) {
- if (cfg_get_long("fastcgi.impersonate", &impersonate) == FAILURE) {
- impersonate = 0;
- }
- if (impersonate) fcgi_impersonate();
+ if (fastcgi && CGIG(impersonate)) {
+ fcgi_impersonate();
}
#endif
while (!fastcgi || fcgi_accept_request(&request) >= 0) {
@@ -1401,6 +1482,9 @@ consult the installation file that came with this distribution, or visit \n\
break;
case 'f': /* parse file */
+ if (script_file) {
+ efree(script_file);
+ }
script_file = estrdup(php_optarg);
no_headers = 1;
/* arguments after the file are considered script args */
@@ -1410,6 +1494,7 @@ consult the installation file that came with this distribution, or visit \n\
case 'i': /* php info & quit */
if (php_request_startup(TSRMLS_C) == FAILURE) {
+ SG(server_context) = NULL;
php_module_shutdown(TSRMLS_C);
return FAILURE;
}
@@ -1450,13 +1535,10 @@ consult the installation file that came with this distribution, or visit \n\
no_headers = 1;
break;
- case 's': /* generate highlighted HTML from source */
- behavior = PHP_MODE_HIGHLIGHT;
- break;
-
case 'v': /* show php version & quit */
no_headers = 1;
if (php_request_startup(TSRMLS_C) == FAILURE) {
+ SG(server_context) = NULL;
php_module_shutdown(TSRMLS_C);
return FAILURE;
}
@@ -1465,9 +1547,9 @@ consult the installation file that came with this distribution, or visit \n\
SG(request_info).no_headers = 1;
}
#if ZEND_DEBUG
- php_printf("PHP %s (%s) (built: %s %s) (DEBUG)\nCopyright (c) 1997-2006 The PHP Group\n%s", PHP_VERSION, sapi_module.name, __DATE__, __TIME__, get_zend_version());
+ php_printf("PHP %s (%s) (built: %s %s) (DEBUG)\nCopyright (c) 1997-2007 The PHP Group\n%s", PHP_VERSION, sapi_module.name, __DATE__, __TIME__, get_zend_version());
#else
- php_printf("PHP %s (%s) (built: %s %s)\nCopyright (c) 1997-2006 The PHP Group\n%s", PHP_VERSION, sapi_module.name, __DATE__, __TIME__, get_zend_version());
+ php_printf("PHP %s (%s) (built: %s %s)\nCopyright (c) 1997-2007 The PHP Group\n%s", PHP_VERSION, sapi_module.name, __DATE__, __TIME__, get_zend_version());
#endif
php_end_ob_buffers(1 TSRMLS_CC);
exit(0);
@@ -1515,17 +1597,23 @@ consult the installation file that came with this distribution, or visit \n\
test.php v1=test "v2=hello world!"
*/
if (!SG(request_info).query_string && argc > php_optind) {
+ int slen = strlen(PG(arg_separator).input);
len = 0;
for (i = php_optind; i < argc; i++) {
- len += strlen(argv[i]) + 1;
+ if (i < (argc - 1)) {
+ len += strlen(argv[i]) + slen;
+ } else {
+ len += strlen(argv[i]);
+ }
}
- s = malloc(len + 1);
+ len += 2;
+ s = malloc(len);
*s = '\0'; /* we are pretending it came from the environment */
- for (i = php_optind, len = 0; i < argc; i++) {
- strcat(s, argv[i]);
+ for (i = php_optind; i < argc; i++) {
+ strlcat(s, argv[i], len);
if (i < (argc - 1)) {
- strcat(s, PG(arg_separator).input);
+ strlcat(s, PG(arg_separator).input, len);
}
}
SG(request_info).query_string = s;
@@ -1564,6 +1652,7 @@ consult the installation file that came with this distribution, or visit \n\
fcgi_finish_request(&request);
}
#endif
+ SG(server_context) = NULL;
php_module_shutdown(TSRMLS_C);
return FAILURE;
}
@@ -1577,6 +1666,7 @@ consult the installation file that came with this distribution, or visit \n\
1. we are running from shell and got filename was there
2. we are running as cgi or fastcgi
*/
+ retval = FAILURE;
if (cgi || SG(request_info).path_translated) {
retval = php_fopen_primary_script(&file_handle TSRMLS_CC);
}
@@ -1585,8 +1675,13 @@ consult the installation file that came with this distribution, or visit \n\
running from shell (so fp == NULL), then fail.
*/
if (retval == FAILURE && file_handle.handle.fp == NULL) {
- SG(sapi_headers).http_response_code = 404;
- PUTS("No input file specified.\n");
+ if (errno == EACCES) {
+ SG(sapi_headers).http_response_code = 403;
+ PUTS("Access denied.\n");
+ } else {
+ SG(sapi_headers).http_response_code = 404;
+ PUTS("No input file specified.\n");
+ }
#if PHP_FASTCGI
/* we want to serve more requests if this is fastcgi
so cleanup and continue, request shutdown is
@@ -1596,11 +1691,12 @@ consult the installation file that came with this distribution, or visit \n\
}
#endif
php_request_shutdown((void *) 0);
+ SG(server_context) = NULL;
php_module_shutdown(TSRMLS_C);
return FAILURE;
}
- if (file_handle.handle.fp && (file_handle.handle.fp != stdin)) {
+ if (CGIG(check_shebang_line) && file_handle.handle.fp && (file_handle.handle.fp != stdin)) {
/* #!php support */
c = fgetc(file_handle.handle.fp);
if (c == '#') {
@@ -1648,6 +1744,11 @@ consult the installation file that came with this distribution, or visit \n\
if (open_file_for_scanning(&file_handle TSRMLS_CC) == SUCCESS) {
php_get_highlight_struct(&syntax_highlighter_ini);
zend_highlight(&syntax_highlighter_ini TSRMLS_CC);
+#if PHP_FASTCGI
+ if (fastcgi) {
+ goto fastcgi_request_done;
+ }
+#endif
fclose(file_handle.handle.fp);
php_end_ob_buffers(1 TSRMLS_CC);
}
@@ -1702,11 +1803,9 @@ fastcgi_request_done:
requests++;
if (max_requests && (requests == max_requests)) {
fcgi_finish_request(&request);
-#ifndef PHP_WIN32
if (bindpath) {
free(bindpath);
}
-#endif
break;
}
/* end of fastcgi loop */
diff --git a/sapi/cgi/config9.m4 b/sapi/cgi/config9.m4
index f4f3ffe13..fd933576d 100644
--- a/sapi/cgi/config9.m4
+++ b/sapi/cgi/config9.m4
@@ -1,5 +1,5 @@
dnl
-dnl $Id: config9.m4,v 1.17.2.2.2.1 2006/09/28 09:37:52 dmitry Exp $
+dnl $Id: config9.m4,v 1.17.2.2.2.2 2007/02/20 20:11:11 tony2001 Exp $
dnl
AC_ARG_ENABLE(cgi,
@@ -50,36 +50,6 @@ AC_ARG_ENABLE(path-info-check,
PHP_ENABLE_PATHINFO_CHECK=yes
])
-AC_DEFUN([PHP_TEST_WRITE_STDOUT],[
- AC_CACHE_CHECK(whether writing to stdout works,ac_cv_write_stdout,[
- AC_TRY_RUN([
-#ifdef HAVE_UNISTD_H
-#include <unistd.h>
-#endif
-
-#define TEXT "This is the test message -- "
-
-main()
-{
- int n;
-
- n = write(1, TEXT, sizeof(TEXT)-1);
- return (!(n == sizeof(TEXT)-1));
-}
- ],[
- ac_cv_write_stdout=yes
- ],[
- ac_cv_write_stdout=no
- ],[
- ac_cv_write_stdout=no
- ])
- ])
- if test "$ac_cv_write_stdout" = "yes"; then
- AC_DEFINE(PHP_WRITE_STDOUT, 1, [whether write(2) works])
- fi
-])
-
-
if test "$PHP_SAPI" = "default"; then
AC_MSG_CHECKING(for CGI build)
if test "$PHP_SAPI_CGI" != "no"; then
@@ -96,8 +66,6 @@ if test "$PHP_SAPI" = "default"; then
esac
PHP_SUBST(SAPI_CGI_PATH)
- PHP_TEST_WRITE_STDOUT
-
AC_MSG_CHECKING(whether to force Apache CGI redirect)
if test "$PHP_FORCE_CGI_REDIRECT" = "yes"; then
REDIRECT=1
diff --git a/sapi/cgi/fastcgi.c b/sapi/cgi/fastcgi.c
index 6f528109f..7fd1fa86b 100644
--- a/sapi/cgi/fastcgi.c
+++ b/sapi/cgi/fastcgi.c
@@ -2,7 +2,7 @@
+----------------------------------------------------------------------+
| PHP Version 5 |
+----------------------------------------------------------------------+
- | Copyright (c) 1997-2006 The PHP Group |
+ | Copyright (c) 1997-2007 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 |
@@ -16,7 +16,7 @@
+----------------------------------------------------------------------+
*/
-/* $Id: fastcgi.c,v 1.4.2.13.2.11 2006/10/16 10:46:59 dmitry Exp $ */
+/* $Id: fastcgi.c,v 1.4.2.13.2.24 2007/04/09 15:39:59 dmitry Exp $ */
#include "php.h"
#include "fastcgi.h"
@@ -31,7 +31,7 @@
#include <windows.h>
- typedef unsigned int size_t;
+ typedef unsigned int in_addr_t;
struct sockaddr_un {
short sun_family;
@@ -71,6 +71,15 @@
# include <netdb.h>
# include <signal.h>
+# define closesocket(s) close(s)
+
+# if defined(HAVE_SYS_POLL_H) && defined(HAVE_POLL)
+# include <sys/poll.h>
+# endif
+# if defined(HAVE_SYS_SELECT_H)
+# include <sys/select.h>
+# endif
+
#ifndef INADDR_NONE
#define INADDR_NONE ((unsigned long) -1)
#endif
@@ -126,7 +135,7 @@ typedef union _sa_t {
typedef struct _fcgi_mgmt_rec {
char* name;
- size_t name_len;
+ char name_len;
char val;
} fcgi_mgmt_rec;
@@ -140,6 +149,7 @@ static const fcgi_mgmt_rec fcgi_mgmt_vars[] = {
static int is_initialized = 0;
static int is_fastcgi = 0;
static int in_shutdown = 0;
+static in_addr_t *allowed_clients = NULL;
#ifdef _WIN32
@@ -162,6 +172,11 @@ static void fcgi_signal_handler(int signo)
#endif
+int fcgi_in_shutdown(void)
+{
+ return in_shutdown;
+}
+
int fcgi_init(void)
{
if (!is_initialized) {
@@ -240,12 +255,89 @@ int fcgi_is_fastcgi(void)
}
}
+#ifdef _WIN32
+/* Do some black magic with the NT security API.
+ * We prepare a DACL (Discretionary Access Control List) so that
+ * we, the creator, are allowed all access, while "Everyone Else"
+ * is only allowed to read and write to the pipe.
+ * This avoids security issues on shared hosts where a luser messes
+ * with the lower-level pipe settings and screws up the FastCGI service.
+ */
+static PACL prepare_named_pipe_acl(PSECURITY_DESCRIPTOR sd, LPSECURITY_ATTRIBUTES sa)
+{
+ DWORD req_acl_size;
+ char everyone_buf[32], owner_buf[32];
+ PSID sid_everyone, sid_owner;
+ SID_IDENTIFIER_AUTHORITY
+ siaWorld = SECURITY_WORLD_SID_AUTHORITY,
+ siaCreator = SECURITY_CREATOR_SID_AUTHORITY;
+ PACL acl;
+
+ sid_everyone = (PSID)&everyone_buf;
+ sid_owner = (PSID)&owner_buf;
+
+ req_acl_size = sizeof(ACL) +
+ (2 * ((sizeof(ACCESS_ALLOWED_ACE) - sizeof(DWORD)) + GetSidLengthRequired(1)));
+
+ acl = malloc(req_acl_size);
+
+ if (acl == NULL) {
+ return NULL;
+ }
+
+ if (!InitializeSid(sid_everyone, &siaWorld, 1)) {
+ goto out_fail;
+ }
+ *GetSidSubAuthority(sid_everyone, 0) = SECURITY_WORLD_RID;
+
+ if (!InitializeSid(sid_owner, &siaCreator, 1)) {
+ goto out_fail;
+ }
+ *GetSidSubAuthority(sid_owner, 0) = SECURITY_CREATOR_OWNER_RID;
+
+ if (!InitializeAcl(acl, req_acl_size, ACL_REVISION)) {
+ goto out_fail;
+ }
+
+ if (!AddAccessAllowedAce(acl, ACL_REVISION, FILE_GENERIC_READ | FILE_GENERIC_WRITE, sid_everyone)) {
+ goto out_fail;
+ }
+
+ if (!AddAccessAllowedAce(acl, ACL_REVISION, FILE_ALL_ACCESS, sid_owner)) {
+ goto out_fail;
+ }
+
+ if (!InitializeSecurityDescriptor(sd, SECURITY_DESCRIPTOR_REVISION)) {
+ goto out_fail;
+ }
+
+ if (!SetSecurityDescriptorDacl(sd, TRUE, acl, FALSE)) {
+ goto out_fail;
+ }
+
+ sa->lpSecurityDescriptor = sd;
+
+ return acl;
+
+out_fail:
+ free(acl);
+ return NULL;
+}
+#endif
+
+static int is_port_number(const char *bindpath)
+{
+ while (*bindpath) {
+ if (*bindpath < '0' || *bindpath > '9') {
+ return 0;
+ }
+ bindpath++;
+ }
+ return 1;
+}
+
int fcgi_listen(const char *path, int backlog)
{
-#ifdef _WIN32
- /* TODO: Support for manual binding on TCP sockets (php -b <port>) */
- return -1;
-#else
char *s;
int tcp = 0;
char host[MAXPATHLEN];
@@ -261,6 +353,12 @@ int fcgi_listen(const char *path, int backlog)
host[s-path] = '\0';
tcp = 1;
}
+ } else if (is_port_number(path)) {
+ port = atoi(path);
+ if (port != 0) {
+ host[0] = '\0';
+ tcp = 1;
+ }
}
/* Prepare socket address */
@@ -289,6 +387,33 @@ int fcgi_listen(const char *path, int backlog)
}
}
} else {
+#ifdef _WIN32
+ SECURITY_DESCRIPTOR sd;
+ SECURITY_ATTRIBUTES sa;
+ PACL acl;
+ HANDLE namedPipe;
+
+ memset(&sa, 0, sizeof(sa));
+ sa.nLength = sizeof(sa);
+ sa.bInheritHandle = FALSE;
+ acl = prepare_named_pipe_acl(&sd, &sa);
+
+ namedPipe = CreateNamedPipe(path,
+ PIPE_ACCESS_DUPLEX | FILE_FLAG_OVERLAPPED,
+ PIPE_TYPE_BYTE | PIPE_WAIT | PIPE_READMODE_BYTE,
+ PIPE_UNLIMITED_INSTANCES,
+ 8192, 8192, 0, &sa);
+ if (namedPipe == INVALID_HANDLE_VALUE) {
+ return -1;
+ }
+ listen_socket = _open_osfhandle((long)namedPipe, 0);
+ if (!is_initialized) {
+ fcgi_init();
+ }
+ is_fastcgi = 1;
+ return listen_socket;
+
+#else
int path_len = strlen(path);
if (path_len >= sizeof(sa.sa_unix.sun_path)) {
@@ -304,6 +429,7 @@ int fcgi_listen(const char *path, int backlog)
sa.sa_unix.sun_len = sock_len;
#endif
unlink(path);
+#endif
}
/* Create, bind socket and start listen on it */
@@ -317,14 +443,51 @@ 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++;
+ }
+ 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) {
+ fprintf(stderr, "Wrong IP address '%s' in FCGI_WEB_SERVER_ADDRS\n", cur);
+ }
+ n++;
+ cur = end;
+ }
+ allowed_clients[n] = INADDR_NONE;
+ free(ip);
+ }
}
if (!is_initialized) {
fcgi_init();
}
is_fastcgi = 1;
- return listen_socket;
+
+#ifdef _WIN32
+ if (tcp) {
+ listen_socket = _open_osfhandle((long)listen_socket, 0);
+ }
#endif
+ return listen_socket;
}
void fcgi_init_request(fcgi_request *req, int listen_socket)
@@ -339,6 +502,10 @@ void fcgi_init_request(fcgi_request *req, int listen_socket)
req->out_hdr = NULL;
req->out_pos = req->out_buf;
+
+#ifdef _WIN32
+ req->tcp = !GetNamedPipeInfo((HANDLE)_get_osfhandle(req->listen_socket), NULL, NULL, NULL, NULL);
+#endif
}
static inline ssize_t safe_write(fcgi_request *req, const void *buf, size_t count)
@@ -347,7 +514,19 @@ static inline ssize_t safe_write(fcgi_request *req, const void *buf, size_t coun
size_t n = 0;
do {
+ errno = 0;
+#ifdef _WIN32
+ if (!req->tcp) {
+ ret = write(req->fd, ((char*)buf)+n, count-n);
+ } else {
+ ret = send(req->fd, ((char*)buf)+n, count-n, 0);
+ if (ret <= 0) {
+ errno = WSAGetLastError();
+ }
+ }
+#else
ret = write(req->fd, ((char*)buf)+n, count-n);
+#endif
if (ret > 0) {
n += ret;
} else if (ret <= 0 && errno != 0 && errno != EINTR) {
@@ -363,7 +542,19 @@ static inline ssize_t safe_read(fcgi_request *req, const void *buf, size_t count
size_t n = 0;
do {
+ errno = 0;
+#ifdef _WIN32
+ if (!req->tcp) {
+ ret = read(req->fd, ((char*)buf)+n, count-n);
+ } else {
+ ret = recv(req->fd, ((char*)buf)+n, count-n, 0);
+ if (ret <= 0) {
+ errno = WSAGetLastError();
+ }
+ }
+#else
ret = read(req->fd, ((char*)buf)+n, count-n);
+#endif
if (ret > 0) {
n += ret;
} else if (ret == 0 && errno == 0) {
@@ -540,8 +731,8 @@ static int fcgi_read_request(fcgi_request *req)
for (j = 0; j < sizeof(fcgi_mgmt_vars)/sizeof(fcgi_mgmt_vars[0]); j++) {
if (zend_hash_exists(&req->env, fcgi_mgmt_vars[j].name, fcgi_mgmt_vars[j].name_len+1) == 0) {
- sprintf((char*)p, "%c%c%s%c", fcgi_mgmt_vars[j].name_len, 1, fcgi_mgmt_vars[j].name, fcgi_mgmt_vars[j].val);
- p += fcgi_mgmt_vars[j].name_len + 3;
+ sprintf((char*)p, "%c%c%s%c", fcgi_mgmt_vars[j].name_len, 1, fcgi_mgmt_vars[j].name, fcgi_mgmt_vars[j].val);
+ p += fcgi_mgmt_vars[j].name_len + 3;
}
}
len = p - buf - sizeof(fcgi_header);
@@ -616,16 +807,30 @@ static inline void fcgi_close(fcgi_request *req, int force, int destroy)
if (destroy) {
zend_hash_destroy(&req->env);
}
+
+#ifdef _WIN32
+ if (is_impersonate && !req->tcp) {
+ RevertToSelf();
+ }
+#endif
+
if ((force || !req->keep) && req->fd >= 0) {
#ifdef _WIN32
- HANDLE pipe = (HANDLE)_get_osfhandle(req->fd);
+ if (!req->tcp) {
+ HANDLE pipe = (HANDLE)_get_osfhandle(req->fd);
- if (!force) {
- FlushFileBuffers(pipe);
- }
- DisconnectNamedPipe(pipe);
- if (is_impersonate) {
- RevertToSelf();
+ if (!force) {
+ FlushFileBuffers(pipe);
+ }
+ DisconnectNamedPipe(pipe);
+ } else {
+ if (!force) {
+ char buf[8];
+
+ shutdown(req->fd, 1);
+ while (recv(req->fd, buf, sizeof(buf), 0) > 0) {}
+ }
+ closesocket(req->fd);
}
#else
if (!force) {
@@ -655,41 +860,57 @@ int fcgi_accept_request(fcgi_request *req)
return -1;
}
#ifdef _WIN32
- pipe = (HANDLE)_get_osfhandle(req->listen_socket);
-
- FCGI_LOCK(req->listen_socket);
- ov.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
- if (!ConnectNamedPipe(pipe, &ov)) {
- errno = GetLastError();
- if (errno == ERROR_IO_PENDING) {
- while (WaitForSingleObject(ov.hEvent, 1000) == WAIT_TIMEOUT) {
- if (in_shutdown) {
- CloseHandle(ov.hEvent);
- FCGI_UNLOCK(req->listen_socket);
- return -1;
+ if (!req->tcp) {
+ pipe = (HANDLE)_get_osfhandle(req->listen_socket);
+ FCGI_LOCK(req->listen_socket);
+ ov.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
+ if (!ConnectNamedPipe(pipe, &ov)) {
+ errno = GetLastError();
+ if (errno == ERROR_IO_PENDING) {
+ while (WaitForSingleObject(ov.hEvent, 1000) == WAIT_TIMEOUT) {
+ if (in_shutdown) {
+ CloseHandle(ov.hEvent);
+ FCGI_UNLOCK(req->listen_socket);
+ return -1;
+ }
}
+ } else if (errno != ERROR_PIPE_CONNECTED) {
}
- } else if (errno != ERROR_PIPE_CONNECTED) {
}
- }
- CloseHandle(ov.hEvent);
- if (is_impersonate && !ImpersonateNamedPipeClient(pipe)) {
- DisconnectNamedPipe(pipe);
- req->fd = -1;
- } else {
+ CloseHandle(ov.hEvent);
req->fd = req->listen_socket;
- }
- FCGI_UNLOCK(req->listen_socket);
+ FCGI_UNLOCK(req->listen_socket);
+ } else {
+ SOCKET listen_socket = (SOCKET)_get_osfhandle(req->listen_socket);
#else
{
+ int listen_socket = req->listen_socket;
+#endif
sa_t sa;
socklen_t len = sizeof(sa);
FCGI_LOCK(req->listen_socket);
- req->fd = accept(req->listen_socket, (struct sockaddr *)&sa, &len);
+ 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;
+ }
+ 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;
+ }
+ }
}
-#endif
if (req->fd < 0 && (in_shutdown || errno != EINTR)) {
return -1;
@@ -699,18 +920,42 @@ int fcgi_accept_request(fcgi_request *req)
break;
#else
if (req->fd >= 0) {
- struct timeval tv = {5,0};
- fd_set set;
-
- FD_ZERO(&set);
- FD_SET(req->fd, &set);
-try_again:
- errno = 0;
- if (select(req->fd + 1, &set, NULL, NULL, &tv) >= 0 && FD_ISSET(req->fd, &set)) {
+#if defined(HAVE_SYS_POLL_H) && defined(HAVE_POLL)
+ struct pollfd fds;
+ int ret;
+
+ fds.fd = req->fd;
+ fds.events = POLLIN;
+ fds.revents = 0;
+ do {
+ errno = 0;
+ ret = poll(&fds, 1, 5000);
+ } while (ret < 0 && errno == EINTR);
+ if (ret > 0 && (fds.revents & POLLIN)) {
break;
}
- if (errno == EINTR) goto try_again;
fcgi_close(req, 1, 0);
+#else
+ if (req->fd < FD_SETSIZE) {
+ struct timeval tv = {5,0};
+ fd_set set;
+ int ret;
+
+ FD_ZERO(&set);
+ FD_SET(req->fd, &set);
+ do {
+ errno = 0;
+ ret = select(req->fd + 1, &set, NULL, NULL, &tv) >= 0;
+ } while (ret < 0 && errno == EINTR);
+ if (ret > 0 && FD_ISSET(req->fd, &set)) {
+ break;
+ }
+ fcgi_close(req, 1, 0);
+ } else {
+ fprintf(stderr, "Too many open file descriptors. FD_SETSIZE limit exceeded.");
+ fcgi_close(req, 1, 0);
+ }
+#endif
}
#endif
}
@@ -718,6 +963,15 @@ try_again:
return -1;
}
if (fcgi_read_request(req)) {
+#ifdef _WIN32
+ if (is_impersonate && !req->tcp) {
+ pipe = (HANDLE)_get_osfhandle(req->fd);
+ if (!ImpersonateNamedPipeClient(pipe)) {
+ fcgi_close(req, 1, 1);
+ continue;
+ }
+ }
+#endif
return req->fd;
} else {
fcgi_close(req, 1, 1);
@@ -908,14 +1162,15 @@ 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)
{
if (var && req) {
- char **ret;
-
if (val == NULL) {
- val = "";
- }
- val = strdup(val);
- if (zend_hash_update(&req->env, var, var_len+1, &val, sizeof(char*), (void**)&ret) == SUCCESS) {
- return *ret;
+ zend_hash_del(&req->env, var, var_len+1);
+ } else {
+ char **ret;
+
+ val = strdup(val);
+ if (zend_hash_update(&req->env, var, var_len+1, &val, sizeof(char*), (void**)&ret) == SUCCESS) {
+ return *ret;
+ }
}
}
return NULL;
diff --git a/sapi/cgi/fastcgi.h b/sapi/cgi/fastcgi.h
index 5a9fcb9de..43885c26a 100644
--- a/sapi/cgi/fastcgi.h
+++ b/sapi/cgi/fastcgi.h
@@ -2,7 +2,7 @@
+----------------------------------------------------------------------+
| PHP Version 5 |
+----------------------------------------------------------------------+
- | Copyright (c) 1997-2006 The PHP Group |
+ | Copyright (c) 1997-2007 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 |
@@ -16,7 +16,7 @@
+----------------------------------------------------------------------+
*/
-/* $Id: fastcgi.h,v 1.2.2.4.2.1 2006/05/15 14:30:31 dmitry Exp $ */
+/* $Id: fastcgi.h,v 1.2.2.4.2.4 2007/03/28 15:39:22 dmitry Exp $ */
/* FastCGI protocol */
@@ -93,6 +93,9 @@ typedef struct _fcgi_end_request_rec {
typedef struct _fcgi_request {
int listen_socket;
+#ifdef _WIN32
+ int tcp;
+#endif
int fd;
int id;
int keep;
@@ -110,6 +113,7 @@ typedef struct _fcgi_request {
int fcgi_init(void);
int fcgi_is_fastcgi(void);
+int fcgi_in_shutdown(void);
int fcgi_listen(const char *path, int backlog);
void fcgi_init_request(fcgi_request *req, int listen_socket);
int fcgi_accept_request(fcgi_request *req);
diff --git a/sapi/cgi/getopt.c b/sapi/cgi/getopt.c
index 1c86f1e4e..e2988fbf0 100644
--- a/sapi/cgi/getopt.c
+++ b/sapi/cgi/getopt.c
@@ -2,7 +2,7 @@
+----------------------------------------------------------------------+
| PHP Version 5 |
+----------------------------------------------------------------------+
- | Copyright (c) 1997-2006 The PHP Group |
+ | Copyright (c) 1997-2007 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 |
@@ -16,6 +16,8 @@
+----------------------------------------------------------------------+
*/
+/* $Id: getopt.c,v 1.9.2.1.2.4 2007/04/23 11:05:16 tony2001 Exp $ */
+
#include <stdio.h>
#include <string.h>
#include <assert.h>
@@ -77,29 +79,35 @@ int php_getopt(int argc, char* const *argv, const opt_struct opts[], char **opta
}
if ((argv[*optind][0] == '-') && (argv[*optind][1] == '-')) {
/* '--' indicates end of args if not followed by a known long option name */
+ if (argv[*optind][2] == '\0') {
+ (*optind)++;
+ return(EOF);
+ }
+
while (1) {
opts_idx++;
if (opts[opts_idx].opt_char == '-') {
(*optind)++;
- return(EOF);
+ return(php_opt_error(argc, argv, *optind-1, optchr, OPTERRARG, show_err));
} else if (opts[opts_idx].opt_name && !strcmp(&argv[*optind][2], opts[opts_idx].opt_name)) {
break;
}
}
optchr = 0;
- dash = 1;
- arg_start = 2 + strlen(opts[opts_idx].opt_name);
- }
- if (!dash) {
- dash = 1;
- optchr = 1;
- }
-
- /* Check if the guy tries to do a -: kind of flag */
- if (argv[*optind][optchr] == ':') {
dash = 0;
- (*optind)++;
- return (php_opt_error(argc, argv, *optind-1, optchr, OPTERRCOLON, show_err));
+ arg_start = 2 + strlen(opts[opts_idx].opt_name);
+ } else {
+ if (!dash) {
+ dash = 1;
+ optchr = 1;
+ }
+ /* Check if the guy tries to do a -: kind of flag */
+ if (argv[*optind][optchr] == ':') {
+ dash = 0;
+ (*optind)++;
+ return (php_opt_error(argc, argv, *optind-1, optchr, OPTERRCOLON, show_err));
+ }
+ arg_start = 1 + optchr;
}
if (opts_idx < 0) {
while (1) {
@@ -113,6 +121,7 @@ int php_getopt(int argc, char* const *argv, const opt_struct opts[], char **opta
(*optind)++;
} else {
optchr++;
+ arg_start++;
}
return(php_opt_error(argc, argv, errind, errchr, OPTERRNF, show_err));
} else if (argv[*optind][optchr] == opts[opts_idx].opt_char) {
@@ -136,7 +145,8 @@ int php_getopt(int argc, char* const *argv, const opt_struct opts[], char **opta
}
return opts[opts_idx].opt_char;
} else {
- if (arg_start == 2) {
+ /* multiple options specified as one (exclude long opts) */
+ if (arg_start >= 2 && !((argv[*optind][0] == '-') && (argv[*optind][1] == '-'))) {
if (!argv[*optind][optchr+1])
{
dash = 0;
diff --git a/sapi/cgi/php_getopt.h b/sapi/cgi/php_getopt.h
index ad10f423b..e040313da 100644
--- a/sapi/cgi/php_getopt.h
+++ b/sapi/cgi/php_getopt.h
@@ -2,7 +2,7 @@
+----------------------------------------------------------------------+
| PHP Version 5 |
+----------------------------------------------------------------------+
- | Copyright (c) 1997-2006 The PHP Group |
+ | Copyright (c) 1997-2007 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 |
@@ -16,8 +16,17 @@
+----------------------------------------------------------------------+
*/
+/* $Id: php_getopt.h,v 1.7.2.1.2.2 2007/04/15 22:50:58 sniper Exp $ */
+
#include "php.h"
+#ifdef NETWARE
+/*
+As NetWare LibC has optind and optarg macros defined in unistd.h our local variables were getting mistakenly preprocessed so undeffing optind and optarg
+*/
+#undef optarg
+#undef optind
+#endif
/* Define structure for one recognized option (both single char and long name).
* If short_open is '-' this is the last option.
*/
diff --git a/sapi/cgi/tests/001.phpt b/sapi/cgi/tests/001.phpt
new file mode 100644
index 000000000..74c694f7c
--- /dev/null
+++ b/sapi/cgi/tests/001.phpt
@@ -0,0 +1,22 @@
+--TEST--
+version string
+--SKIPIF--
+<?php include "skipif.inc"; ?>
+--FILE--
+<?php
+
+include "include.inc";
+
+$php = get_cgi_path();
+reset_env_vars();
+
+var_dump(`$php -n -v`);
+
+echo "Done\n";
+?>
+--EXPECTF--
+string(%d) "PHP %s (cgi%s (built: %s
+Copyright (c) 1997-20%s The PHP Group
+Zend Engine v%s, Copyright (c) 1998-20%s Zend Technologies
+"
+Done
diff --git a/sapi/cgi/tests/002.phpt b/sapi/cgi/tests/002.phpt
new file mode 100644
index 000000000..66e2424f2
--- /dev/null
+++ b/sapi/cgi/tests/002.phpt
@@ -0,0 +1,52 @@
+--TEST--
+defining INI options with -d
+--SKIPIF--
+<?php
+include "skipif.inc";
+?>
+--FILE--
+<?php
+include "include.inc";
+
+$php = get_cgi_path();
+reset_env_vars();
+
+$file = dirname(__FILE__)."/002.test.php";
+
+file_put_contents($file, '<?php var_dump(ini_get("max_execution_time")); ?>');
+
+var_dump(`$php -n -d max_execution_time=111 $file`);
+var_dump(`$php -n -d max_execution_time=500 $file`);
+var_dump(`$php -n -d max_execution_time=500 -d max_execution_time=555 $file`);
+
+file_put_contents($file, '<?php var_dump(ini_get("max_execution_time")); var_dump(ini_get("upload_tmp_dir")); ?>');
+
+var_dump(`$php -n -d upload_tmp_dir=/test/path -d max_execution_time=555 $file`);
+
+unlink($file);
+
+echo "Done\n";
+?>
+--EXPECTF--
+string(%d) "X-Powered-By: PHP/%s
+Content-type: text/html
+
+string(3) "111"
+"
+string(%d) "X-Powered-By: PHP/%s
+Content-type: text/html
+
+string(3) "500"
+"
+string(%d) "X-Powered-By: PHP/%s
+Content-type: text/html
+
+string(3) "555"
+"
+string(%d) "X-Powered-By: PHP/%s
+Content-type: text/html
+
+string(3) "555"
+string(10) "/test/path"
+"
+Done
diff --git a/sapi/cgi/tests/003.phpt b/sapi/cgi/tests/003.phpt
new file mode 100644
index 000000000..0da3ca860
--- /dev/null
+++ b/sapi/cgi/tests/003.phpt
@@ -0,0 +1,62 @@
+--TEST--
+strip comments and whitespace with -w
+--SKIPIF--
+<?php
+include "skipif.inc";
+?>
+--FILE--
+<?php
+
+include "include.inc";
+
+$php = get_cgi_path();
+reset_env_vars();
+
+$filename = dirname(__FILE__).'/003.test.php';
+$code ='
+<?php
+/* some test script */
+
+class test { /* {{{ */
+ public $var = "test"; //test var
+#perl style comment
+ private $pri; /* private attr */
+
+ function foo(/* void */) {
+ }
+}
+/* }}} */
+
+?>
+';
+
+file_put_contents($filename, $code);
+
+var_dump(`$php -n -w "$filename"`);
+var_dump(`$php -n -w "wrong"`);
+var_dump(`echo "<?php /* comment */ class test {\n // comment \n function foo() {} } ?>" | $php -n -w`);
+
+@unlink($filename);
+
+echo "Done\n";
+?>
+--EXPECTF--
+string(%d) "X-Powered-By: PHP/%s
+Content-type: text/html
+
+
+<?php
+ class test { public $var = "test"; private $pri; function foo() { } } ?>
+"
+string(%d) "Status: 404
+X-Powered-By: PHP/%s
+Content-type: text/html
+
+No input file specified.
+"
+string(%d) "X-Powered-By: PHP/%s
+Content-type: text/html
+
+<?php class test { function foo() {} } ?>
+"
+Done
diff --git a/sapi/cgi/tests/004.phpt b/sapi/cgi/tests/004.phpt
new file mode 100644
index 000000000..c841b68e0
--- /dev/null
+++ b/sapi/cgi/tests/004.phpt
@@ -0,0 +1,43 @@
+--TEST--
+execute a file with -f
+--SKIPIF--
+<?php
+include "skipif.inc";
+?>
+--FILE--
+<?php
+
+include "include.inc";
+
+$php = get_cgi_path();
+reset_env_vars();
+
+$filename = dirname(__FILE__).'/004.test.php';
+$code ='
+<?php
+
+class test {
+ private $pri;
+}
+
+var_dump(test::$pri);
+?>
+';
+
+file_put_contents($filename, $code);
+
+var_dump(`$php -n -f "$filename" 2>/dev/null`);
+var_dump(`$php -n -f "wrong"`);
+
+@unlink($filename);
+
+echo "Done\n";
+?>
+--EXPECTF--
+string(%d) "
+<br />
+<b>Fatal error</b>: Cannot access private property test::$pri in <b>%s004.test.php</b> on line <b>8</b><br />
+"
+string(25) "No input file specified.
+"
+Done
diff --git a/sapi/cgi/tests/005.phpt b/sapi/cgi/tests/005.phpt
new file mode 100644
index 000000000..6d82b0f31
--- /dev/null
+++ b/sapi/cgi/tests/005.phpt
@@ -0,0 +1,30 @@
+--TEST--
+using invalid combinations of cmdline options
+--SKIPIF--
+<?php include "skipif.inc"; ?>
+--FILE--
+<?php
+
+include "include.inc";
+
+$php = get_cgi_path();
+reset_env_vars();
+
+var_dump(`$php -n -c -f 'wrong'`);
+var_dump(`$php -n -a -f 'wrong'`);
+var_dump(`$php -n -f 'wrong' -a`);
+
+echo "Done\n";
+?>
+--EXPECTF--
+string(55) "You cannot use both -n and -c switch. Use -h for help.
+"
+string(51) "No input file specified.
+Interactive mode enabled
+
+"
+string(51) "No input file specified.
+Interactive mode enabled
+
+"
+Done
diff --git a/sapi/cgi/tests/006.phpt b/sapi/cgi/tests/006.phpt
new file mode 100644
index 000000000..107ddc774
--- /dev/null
+++ b/sapi/cgi/tests/006.phpt
@@ -0,0 +1,60 @@
+--TEST--
+syntax check
+--SKIPIF--
+<?php include "skipif.inc"; ?>
+--FILE--
+<?php
+include "include.inc";
+
+$php = get_cgi_path();
+reset_env_vars();
+
+$filename = dirname(__FILE__)."/006.test.php";
+
+$code = '
+<?php
+
+$test = "var";
+
+class test {
+ private $var;
+}
+
+echo test::$var;
+
+?>
+';
+
+file_put_contents($filename, $code);
+
+var_dump(`"$php" -n -l "$filename"`);
+var_dump(`"$php" -n -l some.unknown`);
+
+$code = '
+<?php
+
+class test
+ private $var;
+}
+
+?>
+';
+
+file_put_contents($filename, $code);
+
+var_dump(`"$php" -n -l "$filename" 2>/dev/null`);
+
+@unlink($filename);
+
+echo "Done\n";
+?>
+--EXPECTF--
+string(%d) "No syntax errors detected in %s006.test.php
+"
+string(%d) "No input file specified.
+"
+string(%d) "<br />
+<b>Parse error</b>: %s expecting %s{%s in <b>%s006.test.php</b> on line <b>5</b><br />
+Errors parsing %s006.test.php
+"
+Done
diff --git a/sapi/cgi/tests/007.phpt b/sapi/cgi/tests/007.phpt
new file mode 100644
index 000000000..c0f8df1bf
--- /dev/null
+++ b/sapi/cgi/tests/007.phpt
@@ -0,0 +1,22 @@
+--TEST--
+invalid arguments and error messages
+--SKIPIF--
+<?php include "skipif.inc"; ?>
+--FILE--
+<?php
+include "include.inc";
+
+$php = get_cgi_path();
+reset_env_vars();
+
+var_dump(`"$php" -n -f some.php -f some.php`);
+var_dump(`"$php" -s -w -l`);
+
+echo "Done\n";
+?>
+--EXPECTF--
+string(25) "No input file specified.
+"
+string(31) "No syntax errors detected in -
+"
+Done
diff --git a/sapi/cgi/tests/008.phpt b/sapi/cgi/tests/008.phpt
new file mode 100644
index 000000000..d541ca430
--- /dev/null
+++ b/sapi/cgi/tests/008.phpt
@@ -0,0 +1,54 @@
+--TEST--
+syntax highlighting
+--SKIPIF--
+<?php include "skipif.inc"; ?>
+--FILE--
+<?php
+
+include "include.inc";
+
+$php = get_cgi_path();
+reset_env_vars();
+
+$filename = dirname(__FILE__)."/008.test.php";
+$code = '
+<?php
+$test = "var"; //var
+/* test class */
+class test {
+ private $var = array();
+
+ public static function foo(Test $arg) {
+ echo "hello";
+ var_dump($this);
+ }
+}
+
+$o = new test;
+?>
+';
+
+file_put_contents($filename, $code);
+
+var_dump(`"$php" -n -s "$filename"`);
+var_dump(`"$php" -n -s "unknown"`);
+
+@unlink($filename);
+
+echo "Done\n";
+?>
+--EXPECTF--
+string(%d) "X-Powered-By: PHP/%s
+Content-type: text/html
+
+<code><span style="color: #000000">
+<br /><span style="color: #0000BB">&lt;?php<br />$test&nbsp;</span><span style="color: #007700">=&nbsp;</span><span style="color: #DD0000">"var"</span><span style="color: #007700">;&nbsp;</span><span style="color: #FF8000">//var<br />/*&nbsp;test&nbsp;class&nbsp;*/<br /></span><span style="color: #007700">class&nbsp;</span><span style="color: #0000BB">test&nbsp;</span><span style="color: #007700">{<br />&nbsp;&nbsp;&nbsp;&nbsp;private&nbsp;</span><span style="color: #0000BB">$var&nbsp;</span><span style="color: #007700">=&nbsp;array();<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;public&nbsp;static&nbsp;function&nbsp;</span><span style="color: #0000BB">foo</span><span style="color: #007700">(</span><span style="color: #0000BB">Test&nbsp;$arg</span><span style="color: #007700">)&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;echo&nbsp;</span><span style="color: #DD0000">"hello"</span><span style="color: #007700">;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000BB">var_dump</span><span style="color: #007700">(</span><span style="color: #0000BB">$this</span><span style="color: #007700">);<br />&nbsp;&nbsp;&nbsp;&nbsp;}<br />}<br /><br /></span><span style="color: #0000BB">$o&nbsp;</span><span style="color: #007700">=&nbsp;new&nbsp;</span><span style="color: #0000BB">test</span><span style="color: #007700">;<br /></span><span style="color: #0000BB">?&gt;<br /></span>
+</span>
+</code>"
+string(%d) "Status: 404
+X-Powered-By: PHP/%s
+Content-type: text/html
+
+No input file specified.
+"
+Done
diff --git a/sapi/cgi/tests/include.inc b/sapi/cgi/tests/include.inc
new file mode 100644
index 000000000..2a8732149
--- /dev/null
+++ b/sapi/cgi/tests/include.inc
@@ -0,0 +1,57 @@
+<?php
+
+function get_cgi_path() /* {{{ */
+{
+ $php = getenv("TEST_PHP_EXECUTABLE");
+
+ $cli = false;
+ $cgi = false;
+
+ if (file_exists($php) && is_executable($php)) {
+ $version = `$php -v`;
+ if (strstr($version, "(cli)")) {
+ /* that's cli */
+ $cli = true;
+ } else if (strpos($version, "(cgi")) {
+ /* that's cgi */
+ return $php;
+ }
+ }
+
+ if ($cli) {
+ /* trying to guess ... */
+ $php_path = $php;
+ for ($i = 0; $i < 2; $i++) {
+ $slash_pos = strrpos($php_path, "/");
+ if ($slash_pos) {
+ $php_path = substr($php_path, 0, $slash_pos);
+ } else {
+ return FALSE;
+ }
+ }
+
+ if ($php_path && is_dir($php_path) && file_exists($php_path."/cgi/php") && is_executable($php_path."/cgi/php")) {
+ /* gotcha */
+ return $php_path."/cgi/php";
+ }
+ return false;
+ }
+ /* uhm? what's that then? */
+ return false;
+}
+/* }}} */
+
+function reset_env_vars() /* {{{ */
+{
+ putenv("REDIRECT_STATUS");
+ putenv("QUERY_STRING");
+ putenv("PATH_TRANSLATED");
+ putenv("SCRIPT_FILENAME");
+ putenv("SERVER_SOFTWARE");
+ putenv("SERVER_NAME");
+ putenv("GATEWAY_INTERFACE");
+ putenv("REQUEST_METHOD");
+}
+/* }}} */
+
+?>
diff --git a/sapi/cgi/tests/skipif.inc b/sapi/cgi/tests/skipif.inc
new file mode 100644
index 000000000..9da8b7934
--- /dev/null
+++ b/sapi/cgi/tests/skipif.inc
@@ -0,0 +1,17 @@
+<?php
+
+if (substr(php_sapi_name(), 0, 3) == "cgi") {
+ exit;
+}
+
+if (substr(PHP_OS, 0, 3) == 'WIN') {
+ die ("skip not for Windows");
+}
+
+include dirname(__FILE__)."/include.inc";
+
+if (!get_cgi_path()) {
+ die("skip CGI not found");
+}
+
+?>