File:  [ELWIX - Embedded LightWeight unIX -] / embedaddon / php / sapi / cgi / cgi_main.c
Revision 1.1.1.4 (vendor branch): download - view: text, annotated - select for diffs - revision graph
Sun Jun 15 20:04:02 2014 UTC (10 years, 1 month ago) by misho
Branches: php, MAIN
CVS tags: v5_4_29, HEAD
php 5.4.29

    1: /*
    2:    +----------------------------------------------------------------------+
    3:    | PHP Version 5                                                        |
    4:    +----------------------------------------------------------------------+
    5:    | Copyright (c) 1997-2014 The PHP Group                                |
    6:    +----------------------------------------------------------------------+
    7:    | This source file is subject to version 3.01 of the PHP license,      |
    8:    | that is bundled with this package in the file LICENSE, and is        |
    9:    | available through the world-wide-web at the following url:           |
   10:    | http://www.php.net/license/3_01.txt                                  |
   11:    | If you did not receive a copy of the PHP license and are unable to   |
   12:    | obtain it through the world-wide-web, please send a note to          |
   13:    | license@php.net so we can mail you a copy immediately.               |
   14:    +----------------------------------------------------------------------+
   15:    | Authors: Rasmus Lerdorf <rasmus@lerdorf.on.ca>                       |
   16:    |          Stig Bakken <ssb@php.net>                                   |
   17:    |          Zeev Suraski <zeev@zend.com>                                |
   18:    | FastCGI: Ben Mansell <php@slimyhorror.com>                           |
   19:    |          Shane Caraveo <shane@caraveo.com>                           |
   20:    |          Dmitry Stogov <dmitry@zend.com>                             |
   21:    +----------------------------------------------------------------------+
   22: */
   23: 
   24: /* $Id: cgi_main.c,v 1.1.1.4 2014/06/15 20:04:02 misho Exp $ */
   25: 
   26: #include "php.h"
   27: #include "php_globals.h"
   28: #include "php_variables.h"
   29: #include "zend_modules.h"
   30: 
   31: #include "SAPI.h"
   32: 
   33: #include <stdio.h>
   34: #include "php.h"
   35: 
   36: #ifdef PHP_WIN32
   37: # include "win32/time.h"
   38: # include "win32/signal.h"
   39: # include <process.h>
   40: #endif
   41: 
   42: #if HAVE_SYS_TIME_H
   43: # include <sys/time.h>
   44: #endif
   45: 
   46: #if HAVE_UNISTD_H
   47: # include <unistd.h>
   48: #endif
   49: 
   50: #if HAVE_SIGNAL_H
   51: # include <signal.h>
   52: #endif
   53: 
   54: #if HAVE_SETLOCALE
   55: # include <locale.h>
   56: #endif
   57: 
   58: #if HAVE_SYS_TYPES_H
   59: # include <sys/types.h>
   60: #endif
   61: 
   62: #if HAVE_SYS_WAIT_H
   63: # include <sys/wait.h>
   64: #endif
   65: 
   66: #include "zend.h"
   67: #include "zend_extensions.h"
   68: #include "php_ini.h"
   69: #include "php_globals.h"
   70: #include "php_main.h"
   71: #include "fopen_wrappers.h"
   72: #include "ext/standard/php_standard.h"
   73: #include "ext/standard/url.h"
   74: 
   75: #ifdef PHP_WIN32
   76: # include <io.h>
   77: # include <fcntl.h>
   78: # include "win32/php_registry.h"
   79: #endif
   80: 
   81: #ifdef __riscos__
   82: # include <unixlib/local.h>
   83: int __riscosify_control = __RISCOSIFY_STRICT_UNIX_SPECS;
   84: #endif
   85: 
   86: #include "zend_compile.h"
   87: #include "zend_execute.h"
   88: #include "zend_highlight.h"
   89: #include "zend_indent.h"
   90: 
   91: #include "php_getopt.h"
   92: 
   93: #include "fastcgi.h"
   94: 
   95: #ifndef PHP_WIN32
   96: /* XXX this will need to change later when threaded fastcgi is implemented.  shane */
   97: struct sigaction act, old_term, old_quit, old_int;
   98: #endif
   99: 
  100: static void (*php_php_import_environment_variables)(zval *array_ptr TSRMLS_DC);
  101: 
  102: #ifndef PHP_WIN32
  103: /* these globals used for forking children on unix systems */
  104: /**
  105:  * Number of child processes that will get created to service requests
  106:  */
  107: static int children = 0;
  108: 
  109: 
  110: /**
  111:  * Set to non-zero if we are the parent process
  112:  */
  113: static int parent = 1;
  114: 
  115: /* Did parent received exit signals SIG_TERM/SIG_INT/SIG_QUIT */
  116: static int exit_signal = 0;
  117: 
  118: /* Is Parent waiting for children to exit */
  119: static int parent_waiting = 0;
  120: 
  121: /**
  122:  * Process group
  123:  */
  124: static pid_t pgroup;
  125: #endif
  126: 
  127: #define PHP_MODE_STANDARD	1
  128: #define PHP_MODE_HIGHLIGHT	2
  129: #define PHP_MODE_INDENT		3
  130: #define PHP_MODE_LINT		4
  131: #define PHP_MODE_STRIP		5
  132: 
  133: static char *php_optarg = NULL;
  134: static int php_optind = 1;
  135: static zend_module_entry cgi_module_entry;
  136: 
  137: static const opt_struct OPTIONS[] = {
  138: 	{'a', 0, "interactive"},
  139: 	{'b', 1, "bindpath"},
  140: 	{'C', 0, "no-chdir"},
  141: 	{'c', 1, "php-ini"},
  142: 	{'d', 1, "define"},
  143: 	{'e', 0, "profile-info"},
  144: 	{'f', 1, "file"},
  145: 	{'h', 0, "help"},
  146: 	{'i', 0, "info"},
  147: 	{'l', 0, "syntax-check"},
  148: 	{'m', 0, "modules"},
  149: 	{'n', 0, "no-php-ini"},
  150: 	{'q', 0, "no-header"},
  151: 	{'s', 0, "syntax-highlight"},
  152: 	{'s', 0, "syntax-highlighting"},
  153: 	{'w', 0, "strip"},
  154: 	{'?', 0, "usage"},/* help alias (both '?' and 'usage') */
  155: 	{'v', 0, "version"},
  156: 	{'z', 1, "zend-extension"},
  157:  	{'T', 1, "timing"},
  158: 	{'-', 0, NULL} /* end of args */
  159: };
  160: 
  161: typedef struct _php_cgi_globals_struct {
  162: 	zend_bool rfc2616_headers;
  163: 	zend_bool nph;
  164: 	zend_bool check_shebang_line;
  165: 	zend_bool fix_pathinfo;
  166: 	zend_bool force_redirect;
  167: 	zend_bool discard_path;
  168: 	zend_bool fcgi_logging;
  169: 	char *redirect_status_env;
  170: #ifdef PHP_WIN32
  171: 	zend_bool impersonate;
  172: #endif
  173: 	HashTable user_config_cache;
  174: } php_cgi_globals_struct;
  175: 
  176: /* {{{ user_config_cache
  177:  *
  178:  * Key for each cache entry is dirname(PATH_TRANSLATED).
  179:  *
  180:  * NOTE: Each cache entry config_hash contains the combination from all user ini files found in
  181:  *       the path starting from doc_root throught to dirname(PATH_TRANSLATED).  There is no point
  182:  *       storing per-file entries as it would not be possible to detect added / deleted entries
  183:  *       between separate files.
  184:  */
  185: typedef struct _user_config_cache_entry {
  186: 	time_t expires;
  187: 	HashTable *user_config;
  188: } user_config_cache_entry;
  189: 
  190: static void user_config_cache_entry_dtor(user_config_cache_entry *entry)
  191: {
  192: 	zend_hash_destroy(entry->user_config);
  193: 	free(entry->user_config);
  194: }
  195: /* }}} */
  196: 
  197: #ifdef ZTS
  198: static int php_cgi_globals_id;
  199: #define CGIG(v) TSRMG(php_cgi_globals_id, php_cgi_globals_struct *, v)
  200: #else
  201: static php_cgi_globals_struct php_cgi_globals;
  202: #define CGIG(v) (php_cgi_globals.v)
  203: #endif
  204: 
  205: #ifdef PHP_WIN32
  206: #define TRANSLATE_SLASHES(path) \
  207: 	{ \
  208: 		char *tmp = path; \
  209: 		while (*tmp) { \
  210: 			if (*tmp == '\\') *tmp = '/'; \
  211: 			tmp++; \
  212: 		} \
  213: 	}
  214: #else
  215: #define TRANSLATE_SLASHES(path)
  216: #endif
  217: 
  218: static int print_module_info(zend_module_entry *module, void *arg TSRMLS_DC)
  219: {
  220: 	php_printf("%s\n", module->name);
  221: 	return 0;
  222: }
  223: 
  224: static int module_name_cmp(const void *a, const void *b TSRMLS_DC)
  225: {
  226: 	Bucket *f = *((Bucket **) a);
  227: 	Bucket *s = *((Bucket **) b);
  228: 
  229: 	return strcasecmp(	((zend_module_entry *)f->pData)->name,
  230: 						((zend_module_entry *)s->pData)->name);
  231: }
  232: 
  233: static void print_modules(TSRMLS_D)
  234: {
  235: 	HashTable sorted_registry;
  236: 	zend_module_entry tmp;
  237: 
  238: 	zend_hash_init(&sorted_registry, 50, NULL, NULL, 1);
  239: 	zend_hash_copy(&sorted_registry, &module_registry, NULL, &tmp, sizeof(zend_module_entry));
  240: 	zend_hash_sort(&sorted_registry, zend_qsort, module_name_cmp, 0 TSRMLS_CC);
  241: 	zend_hash_apply_with_argument(&sorted_registry, (apply_func_arg_t) print_module_info, NULL TSRMLS_CC);
  242: 	zend_hash_destroy(&sorted_registry);
  243: }
  244: 
  245: static int print_extension_info(zend_extension *ext, void *arg TSRMLS_DC)
  246: {
  247: 	php_printf("%s\n", ext->name);
  248: 	return 0;
  249: }
  250: 
  251: static int extension_name_cmp(const zend_llist_element **f, const zend_llist_element **s TSRMLS_DC)
  252: {
  253: 	return strcmp(	((zend_extension *)(*f)->data)->name,
  254: 					((zend_extension *)(*s)->data)->name);
  255: }
  256: 
  257: static void print_extensions(TSRMLS_D)
  258: {
  259: 	zend_llist sorted_exts;
  260: 
  261: 	zend_llist_copy(&sorted_exts, &zend_extensions);
  262: 	sorted_exts.dtor = NULL;
  263: 	zend_llist_sort(&sorted_exts, extension_name_cmp TSRMLS_CC);
  264: 	zend_llist_apply_with_argument(&sorted_exts, (llist_apply_with_arg_func_t) print_extension_info, NULL TSRMLS_CC);
  265: 	zend_llist_destroy(&sorted_exts);
  266: }
  267: 
  268: #ifndef STDOUT_FILENO
  269: #define STDOUT_FILENO 1
  270: #endif
  271: 
  272: static inline size_t sapi_cgi_single_write(const char *str, uint str_length TSRMLS_DC)
  273: {
  274: #ifdef PHP_WRITE_STDOUT
  275: 	long ret;
  276: 
  277: 	ret = write(STDOUT_FILENO, str, str_length);
  278: 	if (ret <= 0) return 0;
  279: 	return ret;
  280: #else
  281: 	size_t ret;
  282: 
  283: 	ret = fwrite(str, 1, MIN(str_length, 16384), stdout);
  284: 	return ret;
  285: #endif
  286: }
  287: 
  288: static int sapi_cgi_ub_write(const char *str, uint str_length TSRMLS_DC)
  289: {
  290: 	const char *ptr = str;
  291: 	uint remaining = str_length;
  292: 	size_t ret;
  293: 
  294: 	while (remaining > 0) {
  295: 		ret = sapi_cgi_single_write(ptr, remaining TSRMLS_CC);
  296: 		if (!ret) {
  297: 			php_handle_aborted_connection();
  298: 			return str_length - remaining;
  299: 		}
  300: 		ptr += ret;
  301: 		remaining -= ret;
  302: 	}
  303: 
  304: 	return str_length;
  305: }
  306: 
  307: static int sapi_fcgi_ub_write(const char *str, uint str_length TSRMLS_DC)
  308: {
  309: 	const char *ptr = str;
  310: 	uint remaining = str_length;
  311: 	fcgi_request *request = (fcgi_request*) SG(server_context);
  312: 
  313: 	while (remaining > 0) {
  314: 		long ret = fcgi_write(request, FCGI_STDOUT, ptr, remaining);
  315: 
  316: 		if (ret <= 0) {
  317: 			php_handle_aborted_connection();
  318: 			return str_length - remaining;
  319: 		}
  320: 		ptr += ret;
  321: 		remaining -= ret;
  322: 	}
  323: 
  324: 	return str_length;
  325: }
  326: 
  327: static void sapi_cgi_flush(void *server_context)
  328: {
  329: 	if (fflush(stdout) == EOF) {
  330: 		php_handle_aborted_connection();
  331: 	}
  332: }
  333: 
  334: static void sapi_fcgi_flush(void *server_context)
  335: {
  336: 	fcgi_request *request = (fcgi_request*) server_context;
  337: 
  338: 	if (
  339: #ifndef PHP_WIN32
  340: 		!parent &&
  341: #endif
  342: 		request && !fcgi_flush(request, 0)) {
  343: 
  344: 		php_handle_aborted_connection();
  345: 	}
  346: }
  347: 
  348: #define SAPI_CGI_MAX_HEADER_LENGTH 1024
  349: 
  350: typedef struct _http_error {
  351:   int code;
  352:   const char* msg;
  353: } http_error;
  354: 
  355: static const http_error http_error_codes[] = {
  356: 	{100, "Continue"},
  357: 	{101, "Switching Protocols"},
  358: 	{200, "OK"},
  359: 	{201, "Created"},
  360: 	{202, "Accepted"},
  361: 	{203, "Non-Authoritative Information"},
  362: 	{204, "No Content"},
  363: 	{205, "Reset Content"},
  364: 	{206, "Partial Content"},
  365: 	{300, "Multiple Choices"},
  366: 	{301, "Moved Permanently"},
  367: 	{302, "Moved Temporarily"},
  368: 	{303, "See Other"},
  369: 	{304, "Not Modified"},
  370: 	{305, "Use Proxy"},
  371: 	{400, "Bad Request"},
  372: 	{401, "Unauthorized"},
  373: 	{402, "Payment Required"},
  374: 	{403, "Forbidden"},
  375: 	{404, "Not Found"},
  376: 	{405, "Method Not Allowed"},
  377: 	{406, "Not Acceptable"},
  378: 	{407, "Proxy Authentication Required"},
  379: 	{408, "Request Time-out"},
  380: 	{409, "Conflict"},
  381: 	{410, "Gone"},
  382: 	{411, "Length Required"},
  383: 	{412, "Precondition Failed"},
  384: 	{413, "Request Entity Too Large"},
  385: 	{414, "Request-URI Too Large"},
  386: 	{415, "Unsupported Media Type"},
  387: 	{428, "Precondition Required"},
  388: 	{429, "Too Many Requests"},
  389: 	{431, "Request Header Fields Too Large"},
  390: 	{500, "Internal Server Error"},
  391: 	{501, "Not Implemented"},
  392: 	{502, "Bad Gateway"},
  393: 	{503, "Service Unavailable"},
  394: 	{504, "Gateway Time-out"},
  395: 	{505, "HTTP Version not supported"},
  396: 	{511, "Network Authentication Required"},
  397: 	{0,   NULL}
  398: };
  399: 
  400: static int sapi_cgi_send_headers(sapi_headers_struct *sapi_headers TSRMLS_DC)
  401: {
  402: 	char buf[SAPI_CGI_MAX_HEADER_LENGTH];
  403: 	sapi_header_struct *h;
  404: 	zend_llist_position pos;
  405: 	zend_bool ignore_status = 0;
  406: 	int response_status = SG(sapi_headers).http_response_code;
  407: 
  408: 	if (SG(request_info).no_headers == 1) {
  409: 		return  SAPI_HEADER_SENT_SUCCESSFULLY;
  410: 	}
  411: 
  412: 	if (CGIG(nph) || SG(sapi_headers).http_response_code != 200)
  413: 	{
  414: 		int len;
  415: 		zend_bool has_status = 0;
  416: 
  417: 		if (CGIG(rfc2616_headers) && SG(sapi_headers).http_status_line) {
  418: 			char *s;
  419: 			len = slprintf(buf, SAPI_CGI_MAX_HEADER_LENGTH, "%s\r\n", SG(sapi_headers).http_status_line);
  420: 			if ((s = strchr(SG(sapi_headers).http_status_line, ' '))) {
  421: 				response_status = atoi((s + 1));
  422: 			}
  423: 
  424: 			if (len > SAPI_CGI_MAX_HEADER_LENGTH) {
  425: 				len = SAPI_CGI_MAX_HEADER_LENGTH;
  426: 			}
  427: 
  428: 		} else {
  429: 			char *s;
  430: 
  431: 			if (SG(sapi_headers).http_status_line &&
  432: 				(s = strchr(SG(sapi_headers).http_status_line, ' ')) != 0 &&
  433: 				(s - SG(sapi_headers).http_status_line) >= 5 &&
  434: 				strncasecmp(SG(sapi_headers).http_status_line, "HTTP/", 5) == 0
  435: 			) {
  436: 				len = slprintf(buf, sizeof(buf), "Status:%s\r\n", s);
  437: 				response_status = atoi((s + 1));
  438: 			} else {
  439: 				h = (sapi_header_struct*)zend_llist_get_first_ex(&sapi_headers->headers, &pos);
  440: 				while (h) {
  441: 					if (h->header_len > sizeof("Status:")-1 &&
  442: 						strncasecmp(h->header, "Status:", sizeof("Status:")-1) == 0
  443: 					) {
  444: 						has_status = 1;
  445: 						break;
  446: 					}
  447: 					h = (sapi_header_struct*)zend_llist_get_next_ex(&sapi_headers->headers, &pos);
  448: 				}
  449: 				if (!has_status) {
  450: 					http_error *err = (http_error*)http_error_codes;
  451: 
  452: 					while (err->code != 0) {
  453: 						if (err->code == SG(sapi_headers).http_response_code) {
  454: 							break;
  455: 						}
  456: 						err++;
  457: 					}
  458: 					if (err->msg) {
  459: 						len = slprintf(buf, sizeof(buf), "Status: %d %s\r\n", SG(sapi_headers).http_response_code, err->msg);
  460: 					} else {
  461: 						len = slprintf(buf, sizeof(buf), "Status: %d\r\n", SG(sapi_headers).http_response_code);
  462: 					}
  463: 				}
  464: 			}
  465: 		}
  466: 
  467: 		if (!has_status) {
  468: 			PHPWRITE_H(buf, len);
  469: 			ignore_status = 1;
  470: 		}
  471: 	}
  472: 
  473: 	h = (sapi_header_struct*)zend_llist_get_first_ex(&sapi_headers->headers, &pos);
  474: 	while (h) {
  475: 		/* prevent CRLFCRLF */
  476: 		if (h->header_len) {
  477: 			if (h->header_len > sizeof("Status:")-1 && 
  478: 				strncasecmp(h->header, "Status:", sizeof("Status:")-1) == 0
  479: 			) {
  480: 				if (!ignore_status) {
  481: 					ignore_status = 1;
  482: 					PHPWRITE_H(h->header, h->header_len);
  483: 					PHPWRITE_H("\r\n", 2);
  484: 				}
  485: 			} else if (response_status == 304 && h->header_len > sizeof("Content-Type:")-1 &&
  486: 				strncasecmp(h->header, "Content-Type:", sizeof("Content-Type:")-1) == 0
  487: 			) {
  488: 				h = (sapi_header_struct*)zend_llist_get_next_ex(&sapi_headers->headers, &pos);
  489: 				continue;
  490: 			} else {
  491: 				PHPWRITE_H(h->header, h->header_len);
  492: 				PHPWRITE_H("\r\n", 2);
  493: 			}
  494: 		}
  495: 		h = (sapi_header_struct*)zend_llist_get_next_ex(&sapi_headers->headers, &pos);
  496: 	}
  497: 	PHPWRITE_H("\r\n", 2);
  498: 
  499: 	return SAPI_HEADER_SENT_SUCCESSFULLY;
  500: }
  501: 
  502: #ifndef STDIN_FILENO
  503: # define STDIN_FILENO 0
  504: #endif
  505: 
  506: static int sapi_cgi_read_post(char *buffer, uint count_bytes TSRMLS_DC)
  507: {
  508: 	uint read_bytes = 0;
  509: 	int tmp_read_bytes;
  510: 
  511: 	count_bytes = MIN(count_bytes, (uint) SG(request_info).content_length - SG(read_post_bytes));
  512: 	while (read_bytes < count_bytes) {
  513: 		tmp_read_bytes = read(STDIN_FILENO, buffer + read_bytes, count_bytes - read_bytes);
  514: 		if (tmp_read_bytes <= 0) {
  515: 			break;
  516: 		}
  517: 		read_bytes += tmp_read_bytes;
  518: 	}
  519: 	return read_bytes;
  520: }
  521: 
  522: static int sapi_fcgi_read_post(char *buffer, uint count_bytes TSRMLS_DC)
  523: {
  524: 	uint read_bytes = 0;
  525: 	int tmp_read_bytes;
  526: 	fcgi_request *request = (fcgi_request*) SG(server_context);
  527: 
  528: 	count_bytes = MIN(count_bytes, (uint) SG(request_info).content_length - SG(read_post_bytes));
  529: 	while (read_bytes < count_bytes) {
  530: 		tmp_read_bytes = fcgi_read(request, buffer + read_bytes, count_bytes - read_bytes);
  531: 		if (tmp_read_bytes <= 0) {
  532: 			break;
  533: 		}
  534: 		read_bytes += tmp_read_bytes;
  535: 	}
  536: 	return read_bytes;
  537: }
  538: 
  539: static char *sapi_cgi_getenv(char *name, size_t name_len TSRMLS_DC)
  540: {
  541: 	return getenv(name);
  542: }
  543: 
  544: static char *sapi_fcgi_getenv(char *name, size_t name_len TSRMLS_DC)
  545: {
  546: 	/* when php is started by mod_fastcgi, no regular environment
  547: 	 * is provided to PHP.  It is always sent to PHP at the start
  548: 	 * of a request.  So we have to do our own lookup to get env
  549: 	 * vars.  This could probably be faster somehow.  */
  550: 	fcgi_request *request = (fcgi_request*) SG(server_context);
  551: 	char *ret = fcgi_getenv(request, name, name_len);
  552: 
  553: 	if (ret) return ret;
  554: 	/*  if cgi, or fastcgi and not found in fcgi env
  555: 		check the regular environment */
  556: 	return getenv(name);
  557: }
  558: 
  559: static char *_sapi_cgi_putenv(char *name, int name_len, char *value)
  560: {
  561: #if !HAVE_SETENV || !HAVE_UNSETENV
  562: 	int len;
  563: 	char *buf;
  564: #endif
  565: 
  566: #if HAVE_SETENV
  567: 	if (value) {
  568: 		setenv(name, value, 1);
  569: 	}
  570: #endif
  571: #if HAVE_UNSETENV
  572: 	if (!value) {
  573: 		unsetenv(name);
  574: 	}
  575: #endif
  576: 
  577: #if !HAVE_SETENV || !HAVE_UNSETENV
  578: 	/*  if cgi, or fastcgi and not found in fcgi env
  579: 		check the regular environment
  580: 		this leaks, but it's only cgi anyway, we'll fix
  581: 		it for 5.0
  582: 	*/
  583: 	len = name_len + (value ? strlen(value) : 0) + sizeof("=") + 2;
  584: 	buf = (char *) malloc(len);
  585: 	if (buf == NULL) {
  586: 		return getenv(name);
  587: 	}
  588: #endif
  589: #if !HAVE_SETENV
  590: 	if (value) {
  591: 		len = slprintf(buf, len - 1, "%s=%s", name, value);
  592: 		putenv(buf);
  593: 	}
  594: #endif
  595: #if !HAVE_UNSETENV
  596: 	if (!value) {
  597: 		len = slprintf(buf, len - 1, "%s=", name);
  598: 		putenv(buf);
  599: 	}
  600: #endif
  601: 	return getenv(name);
  602: }
  603: 
  604: static char *sapi_cgi_read_cookies(TSRMLS_D)
  605: {
  606: 	return getenv("HTTP_COOKIE");
  607: }
  608: 
  609: static char *sapi_fcgi_read_cookies(TSRMLS_D)
  610: {
  611: 	fcgi_request *request = (fcgi_request*) SG(server_context);
  612: 
  613: 	return FCGI_GETENV(request, "HTTP_COOKIE");
  614: }
  615: 
  616: static void cgi_php_load_env_var(char *var, unsigned int var_len, char *val, unsigned int val_len, void *arg TSRMLS_DC)
  617: {
  618: 	zval *array_ptr = (zval*)arg;	
  619: 	int filter_arg = (array_ptr == PG(http_globals)[TRACK_VARS_ENV])?PARSE_ENV:PARSE_SERVER;
  620: 	unsigned int new_val_len;
  621: 
  622: 	if (sapi_module.input_filter(filter_arg, var, &val, strlen(val), &new_val_len TSRMLS_CC)) {
  623: 		php_register_variable_safe(var, val, new_val_len, array_ptr TSRMLS_CC);
  624: 	}
  625: }
  626: 
  627: static void cgi_php_import_environment_variables(zval *array_ptr TSRMLS_DC)
  628: {
  629: 	if (PG(http_globals)[TRACK_VARS_ENV] &&
  630: 		array_ptr != PG(http_globals)[TRACK_VARS_ENV] &&
  631: 		Z_TYPE_P(PG(http_globals)[TRACK_VARS_ENV]) == IS_ARRAY &&
  632: 		zend_hash_num_elements(Z_ARRVAL_P(PG(http_globals)[TRACK_VARS_ENV])) > 0
  633: 	) {
  634: 		zval_dtor(array_ptr);
  635: 		*array_ptr = *PG(http_globals)[TRACK_VARS_ENV];
  636: 		INIT_PZVAL(array_ptr);
  637: 		zval_copy_ctor(array_ptr);
  638: 		return;
  639: 	} else if (PG(http_globals)[TRACK_VARS_SERVER] &&
  640: 		array_ptr != PG(http_globals)[TRACK_VARS_SERVER] &&
  641: 		Z_TYPE_P(PG(http_globals)[TRACK_VARS_SERVER]) == IS_ARRAY &&
  642: 		zend_hash_num_elements(Z_ARRVAL_P(PG(http_globals)[TRACK_VARS_SERVER])) > 0
  643: 	) {
  644: 		zval_dtor(array_ptr);
  645: 		*array_ptr = *PG(http_globals)[TRACK_VARS_SERVER];
  646: 		INIT_PZVAL(array_ptr);
  647: 		zval_copy_ctor(array_ptr);
  648: 		return;
  649: 	}
  650: 
  651: 	/* call php's original import as a catch-all */
  652: 	php_php_import_environment_variables(array_ptr TSRMLS_CC);
  653: 
  654: 	if (fcgi_is_fastcgi()) {
  655: 		fcgi_request *request = (fcgi_request*) SG(server_context);
  656: 		fcgi_loadenv(request, cgi_php_load_env_var, array_ptr TSRMLS_CC);
  657: 	}
  658: }
  659: 
  660: static void sapi_cgi_register_variables(zval *track_vars_array TSRMLS_DC)
  661: {
  662: 	unsigned int php_self_len;
  663: 	char *php_self;
  664: 
  665: 	/* In CGI mode, we consider the environment to be a part of the server
  666: 	 * variables
  667: 	 */
  668: 	php_import_environment_variables(track_vars_array TSRMLS_CC);
  669: 
  670: 	if (CGIG(fix_pathinfo)) {
  671: 		char *script_name = SG(request_info).request_uri;
  672: 		char *path_info;
  673: 		int free_php_self;
  674: 		ALLOCA_FLAG(use_heap)
  675: 
  676: 		if (fcgi_is_fastcgi()) {
  677: 			fcgi_request *request = (fcgi_request*) SG(server_context);
  678: 
  679: 			path_info = FCGI_GETENV(request, "PATH_INFO");
  680: 		} else {
  681: 			path_info = getenv("PATH_INFO");
  682: 		}
  683: 
  684: 		if (path_info) {
  685: 			unsigned int path_info_len = strlen(path_info);
  686: 
  687: 			if (script_name) {
  688: 				unsigned int script_name_len = strlen(script_name);
  689: 
  690: 				php_self_len = script_name_len + path_info_len;
  691: 				php_self = do_alloca(php_self_len + 1, use_heap);
  692: 				memcpy(php_self, script_name, script_name_len + 1);
  693: 				memcpy(php_self + script_name_len, path_info, path_info_len + 1);
  694: 				free_php_self = 1;
  695: 			}  else {
  696: 				php_self = path_info;
  697: 				php_self_len = path_info_len;
  698: 				free_php_self = 0;
  699: 			}
  700: 		} else if (script_name) {
  701: 			php_self = script_name;
  702: 			php_self_len = strlen(script_name);
  703: 			free_php_self = 0;
  704: 		} else {
  705: 			php_self = "";
  706: 			php_self_len = 0;
  707: 			free_php_self = 0;
  708: 		}
  709: 
  710: 		/* Build the special-case PHP_SELF variable for the CGI version */
  711: 		if (sapi_module.input_filter(PARSE_SERVER, "PHP_SELF", &php_self, php_self_len, &php_self_len TSRMLS_CC)) {
  712: 			php_register_variable_safe("PHP_SELF", php_self, php_self_len, track_vars_array TSRMLS_CC);
  713: 		}
  714: 		if (free_php_self) {
  715: 			free_alloca(php_self, use_heap);
  716: 		}
  717: 	} else {
  718: 		php_self = SG(request_info).request_uri ? SG(request_info).request_uri : "";
  719: 		php_self_len = strlen(php_self);
  720: 		if (sapi_module.input_filter(PARSE_SERVER, "PHP_SELF", &php_self, php_self_len, &php_self_len TSRMLS_CC)) {
  721: 			php_register_variable_safe("PHP_SELF", php_self, php_self_len, track_vars_array TSRMLS_CC);
  722: 		}
  723: 	}
  724: }
  725: 
  726: static void sapi_cgi_log_message(char *message TSRMLS_DC)
  727: {
  728: 	if (fcgi_is_fastcgi() && CGIG(fcgi_logging)) {
  729: 		fcgi_request *request;
  730: 
  731: 		request = (fcgi_request*) SG(server_context);
  732: 		if (request) {
  733: 			int len = strlen(message);
  734: 			char *buf = malloc(len+2);
  735: 
  736: 			memcpy(buf, message, len);
  737: 			memcpy(buf + len, "\n", sizeof("\n"));
  738: 			fcgi_write(request, FCGI_STDERR, buf, len+1);
  739: 			free(buf);
  740: 		} else {
  741: 			fprintf(stderr, "%s\n", message);
  742: 		}
  743: 		/* ignore return code */
  744: 	} else {
  745: 		fprintf(stderr, "%s\n", message);
  746: 	}
  747: }
  748: 
  749: /* {{{ php_cgi_ini_activate_user_config
  750:  */
  751: static void php_cgi_ini_activate_user_config(char *path, int path_len, const char *doc_root, int doc_root_len, int start TSRMLS_DC)
  752: {
  753: 	char *ptr;
  754: 	user_config_cache_entry *new_entry, *entry;
  755: 	time_t request_time = sapi_get_request_time(TSRMLS_C);
  756: 
  757: 	/* Find cached config entry: If not found, create one */
  758: 	if (zend_hash_find(&CGIG(user_config_cache), path, path_len + 1, (void **) &entry) == FAILURE) {
  759: 		new_entry = pemalloc(sizeof(user_config_cache_entry), 1);
  760: 		new_entry->expires = 0;
  761: 		new_entry->user_config = (HashTable *) pemalloc(sizeof(HashTable), 1);
  762: 		zend_hash_init(new_entry->user_config, 0, NULL, (dtor_func_t) config_zval_dtor, 1);
  763: 		zend_hash_update(&CGIG(user_config_cache), path, path_len + 1, new_entry, sizeof(user_config_cache_entry), (void **) &entry);
  764: 		free(new_entry);
  765: 	}
  766: 
  767: 	/* Check whether cache entry has expired and rescan if it is */
  768: 	if (request_time > entry->expires) {
  769: 		char *real_path = NULL;
  770: 		int real_path_len;
  771: 		char *s1, *s2;
  772: 		int s_len;
  773: 
  774: 		/* Clear the expired config */
  775: 		zend_hash_clean(entry->user_config);
  776: 
  777: 		if (!IS_ABSOLUTE_PATH(path, path_len)) {
  778: 			real_path = tsrm_realpath(path, NULL TSRMLS_CC);
  779: 			if (real_path == NULL) {
  780: 				return;
  781: 			}
  782: 			real_path_len = strlen(real_path);
  783: 			path = real_path;
  784: 			path_len = real_path_len;
  785: 		}
  786: 
  787: 		if (path_len > doc_root_len) {
  788: 			s1 = (char *) doc_root;
  789: 			s2 = path;
  790: 			s_len = doc_root_len;
  791: 		} else {
  792: 			s1 = path;
  793: 			s2 = (char *) doc_root;
  794: 			s_len = path_len;
  795: 		}
  796: 
  797: 		/* we have to test if path is part of DOCUMENT_ROOT.
  798: 		  if it is inside the docroot, we scan the tree up to the docroot 
  799: 			to find more user.ini, if not we only scan the current path.
  800: 		  */
  801: #ifdef PHP_WIN32
  802: 		if (strnicmp(s1, s2, s_len) == 0) {
  803: #else 
  804: 		if (strncmp(s1, s2, s_len) == 0) {
  805: #endif
  806: 			ptr = s2 + start;  /* start is the point where doc_root ends! */
  807: 			while ((ptr = strchr(ptr, DEFAULT_SLASH)) != NULL) {
  808: 				*ptr = 0;
  809: 				php_parse_user_ini_file(path, PG(user_ini_filename), entry->user_config TSRMLS_CC);
  810: 				*ptr = '/';
  811: 				ptr++;
  812: 			}
  813: 		} else {
  814: 			php_parse_user_ini_file(path, PG(user_ini_filename), entry->user_config TSRMLS_CC);
  815: 		}
  816: 
  817: 		if (real_path) {
  818: 			free(real_path);
  819: 		}
  820: 		entry->expires = request_time + PG(user_ini_cache_ttl);
  821: 	}
  822: 
  823: 	/* Activate ini entries with values from the user config hash */
  824: 	php_ini_activate_config(entry->user_config, PHP_INI_PERDIR, PHP_INI_STAGE_HTACCESS TSRMLS_CC);
  825: }
  826: /* }}} */
  827: 
  828: static int sapi_cgi_activate(TSRMLS_D)
  829: {
  830: 	char *path, *doc_root, *server_name;
  831: 	uint path_len, doc_root_len, server_name_len;
  832: 
  833: 	/* PATH_TRANSLATED should be defined at this stage but better safe than sorry :) */
  834: 	if (!SG(request_info).path_translated) {
  835: 		return FAILURE;
  836: 	}
  837: 
  838: 	if (php_ini_has_per_host_config()) {
  839: 		/* Activate per-host-system-configuration defined in php.ini and stored into configuration_hash during startup */
  840: 		if (fcgi_is_fastcgi()) {
  841: 			fcgi_request *request = (fcgi_request*) SG(server_context);
  842: 
  843: 			server_name = FCGI_GETENV(request, "SERVER_NAME");
  844: 		} else {
  845: 			server_name = getenv("SERVER_NAME");
  846: 		}
  847: 		/* SERVER_NAME should also be defined at this stage..but better check it anyway */
  848: 		if (server_name) {
  849: 			server_name_len = strlen(server_name);
  850: 			server_name = estrndup(server_name, server_name_len);
  851: 			zend_str_tolower(server_name, server_name_len);
  852: 			php_ini_activate_per_host_config(server_name, server_name_len + 1 TSRMLS_CC);
  853: 			efree(server_name);
  854: 		}
  855: 	}
  856: 
  857: 	if (php_ini_has_per_dir_config() ||
  858: 		(PG(user_ini_filename) && *PG(user_ini_filename))
  859: 	) {
  860: 		/* Prepare search path */
  861: 		path_len = strlen(SG(request_info).path_translated);
  862: 
  863: 		/* Make sure we have trailing slash! */
  864: 		if (!IS_SLASH(SG(request_info).path_translated[path_len])) {
  865: 			path = emalloc(path_len + 2);
  866: 			memcpy(path, SG(request_info).path_translated, path_len + 1);
  867: 			path_len = zend_dirname(path, path_len);
  868: 			path[path_len++] = DEFAULT_SLASH;
  869: 		} else {
  870: 			path = estrndup(SG(request_info).path_translated, path_len);
  871: 			path_len = zend_dirname(path, path_len);
  872: 		}
  873: 		path[path_len] = 0;
  874: 
  875: 		/* Activate per-dir-system-configuration defined in php.ini and stored into configuration_hash during startup */
  876: 		php_ini_activate_per_dir_config(path, path_len TSRMLS_CC); /* Note: for global settings sake we check from root to path */
  877: 
  878: 		/* Load and activate user ini files in path starting from DOCUMENT_ROOT */
  879: 		if (PG(user_ini_filename) && *PG(user_ini_filename)) {
  880: 			if (fcgi_is_fastcgi()) {
  881: 				fcgi_request *request = (fcgi_request*) SG(server_context);
  882: 
  883: 				doc_root = FCGI_GETENV(request, "DOCUMENT_ROOT");
  884: 			} else {
  885: 				doc_root = getenv("DOCUMENT_ROOT");
  886: 			}
  887: 
  888: 			/* DOCUMENT_ROOT should also be defined at this stage..but better check it anyway */
  889: 			if (doc_root) {
  890: 				doc_root_len = strlen(doc_root);
  891: 				if (doc_root_len > 0 && IS_SLASH(doc_root[doc_root_len - 1])) {
  892: 					--doc_root_len;
  893: 				}
  894: #ifdef PHP_WIN32
  895: 				/* paths on windows should be case-insensitive */
  896: 				doc_root = estrndup(doc_root, doc_root_len);
  897: 				zend_str_tolower(doc_root, doc_root_len);
  898: #endif
  899: 				php_cgi_ini_activate_user_config(path, path_len, doc_root, doc_root_len, doc_root_len - 1 TSRMLS_CC);
  900: 				
  901: #ifdef PHP_WIN32
  902: 				efree(doc_root);
  903: #endif
  904: 			}
  905: 		}
  906: 
  907: 		efree(path);
  908: 	}
  909: 
  910: 	return SUCCESS;
  911: }
  912: 
  913: static int sapi_cgi_deactivate(TSRMLS_D)
  914: {
  915: 	/* flush only when SAPI was started. The reasons are:
  916: 		1. SAPI Deactivate is called from two places: module init and request shutdown
  917: 		2. When the first call occurs and the request is not set up, flush fails on FastCGI.
  918: 	*/
  919: 	if (SG(sapi_started)) {
  920: 		if (fcgi_is_fastcgi()) {
  921: 			if (
  922: #ifndef PHP_WIN32
  923: 				!parent &&
  924: #endif
  925: 				!fcgi_finish_request((fcgi_request*)SG(server_context), 0)) {
  926: 				php_handle_aborted_connection();
  927: 			}
  928: 		} else {
  929: 			sapi_cgi_flush(SG(server_context));
  930: 		}
  931: 	}
  932: 	return SUCCESS;
  933: }
  934: 
  935: static int php_cgi_startup(sapi_module_struct *sapi_module)
  936: {
  937: 	if (php_module_startup(sapi_module, &cgi_module_entry, 1) == FAILURE) {
  938: 		return FAILURE;
  939: 	}
  940: 	return SUCCESS;
  941: }
  942: 
  943: /* {{{ sapi_module_struct cgi_sapi_module
  944:  */
  945: static sapi_module_struct cgi_sapi_module = {
  946: 	"cgi-fcgi",						/* name */
  947: 	"CGI/FastCGI",					/* pretty name */
  948: 
  949: 	php_cgi_startup,				/* startup */
  950: 	php_module_shutdown_wrapper,	/* shutdown */
  951: 
  952: 	sapi_cgi_activate,				/* activate */
  953: 	sapi_cgi_deactivate,			/* deactivate */
  954: 
  955: 	sapi_cgi_ub_write,				/* unbuffered write */
  956: 	sapi_cgi_flush,					/* flush */
  957: 	NULL,							/* get uid */
  958: 	sapi_cgi_getenv,				/* getenv */
  959: 
  960: 	php_error,						/* error handler */
  961: 
  962: 	NULL,							/* header handler */
  963: 	sapi_cgi_send_headers,			/* send headers handler */
  964: 	NULL,							/* send header handler */
  965: 
  966: 	sapi_cgi_read_post,				/* read POST data */
  967: 	sapi_cgi_read_cookies,			/* read Cookies */
  968: 
  969: 	sapi_cgi_register_variables,	/* register server variables */
  970: 	sapi_cgi_log_message,			/* Log message */
  971: 	NULL,							/* Get request time */
  972: 	NULL,							/* Child terminate */
  973: 
  974: 	STANDARD_SAPI_MODULE_PROPERTIES
  975: };
  976: /* }}} */
  977: 
  978: /* {{{ arginfo ext/standard/dl.c */
  979: ZEND_BEGIN_ARG_INFO(arginfo_dl, 0)
  980: 	ZEND_ARG_INFO(0, extension_filename)
  981: ZEND_END_ARG_INFO()
  982: /* }}} */
  983: 
  984: static const zend_function_entry additional_functions[] = {
  985: 	ZEND_FE(dl, arginfo_dl)
  986: 	{NULL, NULL, NULL}
  987: };
  988: 
  989: /* {{{ php_cgi_usage
  990:  */
  991: static void php_cgi_usage(char *argv0)
  992: {
  993: 	char *prog;
  994: 
  995: 	prog = strrchr(argv0, '/');
  996: 	if (prog) {
  997: 		prog++;
  998: 	} else {
  999: 		prog = "php";
 1000: 	}
 1001: 
 1002: 	php_printf(	"Usage: %s [-q] [-h] [-s] [-v] [-i] [-f <file>]\n"
 1003: 				"       %s <file> [args...]\n"
 1004: 				"  -a               Run interactively\n"
 1005: 				"  -b <address:port>|<port> Bind Path for external FASTCGI Server mode\n"
 1006: 				"  -C               Do not chdir to the script's directory\n"
 1007: 				"  -c <path>|<file> Look for php.ini file in this directory\n"
 1008: 				"  -n               No php.ini file will be used\n"
 1009: 				"  -d foo[=bar]     Define INI entry foo with value 'bar'\n"
 1010: 				"  -e               Generate extended information for debugger/profiler\n"
 1011: 				"  -f <file>        Parse <file>.  Implies `-q'\n"
 1012: 				"  -h               This help\n"
 1013: 				"  -i               PHP information\n"
 1014: 				"  -l               Syntax check only (lint)\n"
 1015: 				"  -m               Show compiled in modules\n"
 1016: 				"  -q               Quiet-mode.  Suppress HTTP Header output.\n"
 1017: 				"  -s               Display colour syntax highlighted source.\n"
 1018: 				"  -v               Version number\n"
 1019: 				"  -w               Display source with stripped comments and whitespace.\n"
 1020: 				"  -z <file>        Load Zend extension <file>.\n"
 1021: 				"  -T <count>       Measure execution time of script repeated <count> times.\n",
 1022: 				prog, prog);
 1023: }
 1024: /* }}} */
 1025: 
 1026: /* {{{ is_valid_path
 1027:  *
 1028:  * some server configurations allow '..' to slip through in the
 1029:  * translated path.   We'll just refuse to handle such a path.
 1030:  */
 1031: static int is_valid_path(const char *path)
 1032: {
 1033: 	const char *p = path;
 1034: 
 1035: 	if (UNEXPECTED(!p)) {
 1036: 		return 0;
 1037: 	}
 1038: 	if (UNEXPECTED(*p == '.') && *(p+1) == '.' && (!*(p+2) || IS_SLASH(*(p+2)))) {
 1039: 		return 0;
 1040: 	}
 1041: 	while (*p) {
 1042: 		if (IS_SLASH(*p)) {
 1043: 			p++;
 1044: 			if (UNEXPECTED(*p == '.')) {
 1045: 				p++;
 1046: 				if (UNEXPECTED(*p == '.')) {
 1047: 					p++;
 1048: 					if (UNEXPECTED(!*p) || UNEXPECTED(IS_SLASH(*p))) {
 1049: 						return 0;
 1050: 					}											
 1051: 				}
 1052: 			}
 1053: 		}
 1054: 		p++;
 1055: 	}
 1056: 	return 1;
 1057: }
 1058: /* }}} */
 1059: 
 1060: #define CGI_GETENV(name) \
 1061: 	((request) ? \
 1062: 		FCGI_GETENV(request, name) : \
 1063:     	getenv(name))
 1064: 
 1065: #define CGI_PUTENV(name, value) \
 1066: 	((request) ? \
 1067: 		FCGI_PUTENV(request, name, value) : \
 1068: 		_sapi_cgi_putenv(name, sizeof(name)-1, value))
 1069: 
 1070: /* {{{ init_request_info
 1071: 
 1072:   initializes request_info structure
 1073: 
 1074:   specificly in this section we handle proper translations
 1075:   for:
 1076: 
 1077:   PATH_INFO
 1078: 	derived from the portion of the URI path following
 1079: 	the script name but preceding any query data
 1080: 	may be empty
 1081: 
 1082:   PATH_TRANSLATED
 1083:     derived by taking any path-info component of the
 1084: 	request URI and performing any virtual-to-physical
 1085: 	translation appropriate to map it onto the server's
 1086: 	document repository structure
 1087: 
 1088: 	empty if PATH_INFO is empty
 1089: 
 1090: 	The env var PATH_TRANSLATED **IS DIFFERENT** than the
 1091: 	request_info.path_translated variable, the latter should
 1092: 	match SCRIPT_FILENAME instead.
 1093: 
 1094:   SCRIPT_NAME
 1095:     set to a URL path that could identify the CGI script
 1096: 	rather than the interpreter.  PHP_SELF is set to this
 1097: 
 1098:   REQUEST_URI
 1099:     uri section following the domain:port part of a URI
 1100: 
 1101:   SCRIPT_FILENAME
 1102:     The virtual-to-physical translation of SCRIPT_NAME (as per
 1103: 	PATH_TRANSLATED)
 1104: 
 1105:   These settings are documented at
 1106:   http://cgi-spec.golux.com/
 1107: 
 1108: 
 1109:   Based on the following URL request:
 1110: 
 1111:   http://localhost/info.php/test?a=b
 1112: 
 1113:   should produce, which btw is the same as if
 1114:   we were running under mod_cgi on apache (ie. not
 1115:   using ScriptAlias directives):
 1116: 
 1117:   PATH_INFO=/test
 1118:   PATH_TRANSLATED=/docroot/test
 1119:   SCRIPT_NAME=/info.php
 1120:   REQUEST_URI=/info.php/test?a=b
 1121:   SCRIPT_FILENAME=/docroot/info.php
 1122:   QUERY_STRING=a=b
 1123: 
 1124:   but what we get is (cgi/mod_fastcgi under apache):
 1125: 
 1126:   PATH_INFO=/info.php/test
 1127:   PATH_TRANSLATED=/docroot/info.php/test
 1128:   SCRIPT_NAME=/php/php-cgi  (from the Action setting I suppose)
 1129:   REQUEST_URI=/info.php/test?a=b
 1130:   SCRIPT_FILENAME=/path/to/php/bin/php-cgi  (Action setting translated)
 1131:   QUERY_STRING=a=b
 1132: 
 1133:   Comments in the code below refer to using the above URL in a request
 1134: 
 1135:  */
 1136: static void init_request_info(fcgi_request *request TSRMLS_DC)
 1137: {
 1138: 	char *env_script_filename = CGI_GETENV("SCRIPT_FILENAME");
 1139: 	char *env_path_translated = CGI_GETENV("PATH_TRANSLATED");
 1140: 	char *script_path_translated = env_script_filename;
 1141: 
 1142: 	/* some broken servers do not have script_filename or argv0
 1143: 	 * an example, IIS configured in some ways.  then they do more
 1144: 	 * broken stuff and set path_translated to the cgi script location */
 1145: 	if (!script_path_translated && env_path_translated) {
 1146: 		script_path_translated = env_path_translated;
 1147: 	}
 1148: 
 1149: 	/* initialize the defaults */
 1150: 	SG(request_info).path_translated = NULL;
 1151: 	SG(request_info).request_method = NULL;
 1152: 	SG(request_info).proto_num = 1000;
 1153: 	SG(request_info).query_string = NULL;
 1154: 	SG(request_info).request_uri = NULL;
 1155: 	SG(request_info).content_type = NULL;
 1156: 	SG(request_info).content_length = 0;
 1157: 	SG(sapi_headers).http_response_code = 200;
 1158: 
 1159: 	/* script_path_translated being set is a good indication that
 1160: 	 * we are running in a cgi environment, since it is always
 1161: 	 * null otherwise.  otherwise, the filename
 1162: 	 * of the script will be retreived later via argc/argv */
 1163: 	if (script_path_translated) {
 1164: 		const char *auth;
 1165: 		char *content_length = CGI_GETENV("CONTENT_LENGTH");
 1166: 		char *content_type = CGI_GETENV("CONTENT_TYPE");
 1167: 		char *env_path_info = CGI_GETENV("PATH_INFO");
 1168: 		char *env_script_name = CGI_GETENV("SCRIPT_NAME");
 1169: 
 1170: #ifdef PHP_WIN32
 1171: 		/* Hack for buggy IIS that sets incorrect PATH_INFO */
 1172: 		char *env_server_software = CGI_GETENV("SERVER_SOFTWARE");
 1173: 
 1174: 		if (env_server_software &&
 1175: 			env_script_name &&
 1176: 			env_path_info &&
 1177: 			strncmp(env_server_software, "Microsoft-IIS", sizeof("Microsoft-IIS")-1) == 0 &&
 1178: 			strncmp(env_path_info, env_script_name, strlen(env_script_name)) == 0
 1179: 		) {
 1180: 			env_path_info = CGI_PUTENV("ORIG_PATH_INFO", env_path_info);
 1181: 			env_path_info += strlen(env_script_name);
 1182: 			if (*env_path_info == 0) {
 1183: 				env_path_info = NULL;
 1184: 			}
 1185: 			env_path_info = CGI_PUTENV("PATH_INFO", env_path_info);
 1186: 		}
 1187: #endif
 1188: 
 1189: 		if (CGIG(fix_pathinfo)) {
 1190: 			struct stat st;
 1191: 			char *real_path = NULL;
 1192: 			char *env_redirect_url = CGI_GETENV("REDIRECT_URL");
 1193: 			char *env_document_root = CGI_GETENV("DOCUMENT_ROOT");
 1194: 			char *orig_path_translated = env_path_translated;
 1195: 			char *orig_path_info = env_path_info;
 1196: 			char *orig_script_name = env_script_name;
 1197: 			char *orig_script_filename = env_script_filename;
 1198: 			int script_path_translated_len;
 1199: 
 1200: 			if (!env_document_root && PG(doc_root)) {
 1201: 				env_document_root = CGI_PUTENV("DOCUMENT_ROOT", PG(doc_root));
 1202: 				/* fix docroot */
 1203: 				TRANSLATE_SLASHES(env_document_root);
 1204: 			}
 1205: 
 1206: 			if (env_path_translated != NULL && env_redirect_url != NULL &&
 1207:  			    env_path_translated != script_path_translated &&
 1208:  			    strcmp(env_path_translated, script_path_translated) != 0) {
 1209: 				/*
 1210: 				 * pretty much apache specific.  If we have a redirect_url
 1211: 				 * then our script_filename and script_name point to the
 1212: 				 * php executable
 1213: 				 */
 1214: 				script_path_translated = env_path_translated;
 1215: 				/* we correct SCRIPT_NAME now in case we don't have PATH_INFO */
 1216: 				env_script_name = env_redirect_url;
 1217: 			}
 1218: 
 1219: #ifdef __riscos__
 1220: 			/* Convert path to unix format*/
 1221: 			__riscosify_control |= __RISCOSIFY_DONT_CHECK_DIR;
 1222: 			script_path_translated = __unixify(script_path_translated, 0, NULL, 1, 0);
 1223: #endif
 1224: 
 1225: 			/*
 1226: 			 * if the file doesn't exist, try to extract PATH_INFO out
 1227: 			 * of it by stat'ing back through the '/'
 1228: 			 * this fixes url's like /info.php/test
 1229: 			 */
 1230: 			if (script_path_translated &&
 1231: 				(script_path_translated_len = strlen(script_path_translated)) > 0 &&
 1232: 				(script_path_translated[script_path_translated_len-1] == '/' ||
 1233: #ifdef PHP_WIN32
 1234: 				script_path_translated[script_path_translated_len-1] == '\\' ||
 1235: #endif
 1236: 				(real_path = tsrm_realpath(script_path_translated, NULL TSRMLS_CC)) == NULL)
 1237: 			) {
 1238: 				char *pt = estrndup(script_path_translated, script_path_translated_len);
 1239: 				int len = script_path_translated_len;
 1240: 				char *ptr;
 1241: 
 1242: 				while ((ptr = strrchr(pt, '/')) || (ptr = strrchr(pt, '\\'))) {
 1243: 					*ptr = 0;
 1244: 					if (stat(pt, &st) == 0 && S_ISREG(st.st_mode)) {
 1245: 						/*
 1246: 						 * okay, we found the base script!
 1247: 						 * work out how many chars we had to strip off;
 1248: 						 * then we can modify PATH_INFO
 1249: 						 * accordingly
 1250: 						 *
 1251: 						 * we now have the makings of
 1252: 						 * PATH_INFO=/test
 1253: 						 * SCRIPT_FILENAME=/docroot/info.php
 1254: 						 *
 1255: 						 * we now need to figure out what docroot is.
 1256: 						 * if DOCUMENT_ROOT is set, this is easy, otherwise,
 1257: 						 * we have to play the game of hide and seek to figure
 1258: 						 * out what SCRIPT_NAME should be
 1259: 						 */
 1260: 						int slen = len - strlen(pt);
 1261: 						int pilen = env_path_info ? strlen(env_path_info) : 0;
 1262: 						char *path_info = env_path_info ? env_path_info + pilen - slen : NULL;
 1263: 
 1264: 						if (orig_path_info != path_info) {
 1265: 							if (orig_path_info) {
 1266: 								char old;
 1267: 
 1268: 								CGI_PUTENV("ORIG_PATH_INFO", orig_path_info);
 1269: 								old = path_info[0];
 1270: 								path_info[0] = 0;
 1271: 								if (!orig_script_name ||
 1272: 									strcmp(orig_script_name, env_path_info) != 0) {
 1273: 									if (orig_script_name) {
 1274: 										CGI_PUTENV("ORIG_SCRIPT_NAME", orig_script_name);
 1275: 									}
 1276: 									SG(request_info).request_uri = CGI_PUTENV("SCRIPT_NAME", env_path_info);
 1277: 								} else {
 1278: 									SG(request_info).request_uri = orig_script_name;
 1279: 								}
 1280: 								path_info[0] = old;
 1281: 							}
 1282: 							env_path_info = CGI_PUTENV("PATH_INFO", path_info);
 1283: 						}
 1284: 						if (!orig_script_filename ||
 1285: 							strcmp(orig_script_filename, pt) != 0) {
 1286: 							if (orig_script_filename) {
 1287: 								CGI_PUTENV("ORIG_SCRIPT_FILENAME", orig_script_filename);
 1288: 							}
 1289: 							script_path_translated = CGI_PUTENV("SCRIPT_FILENAME", pt);
 1290: 						}
 1291: 						TRANSLATE_SLASHES(pt);
 1292: 
 1293: 						/* figure out docroot
 1294: 						 * SCRIPT_FILENAME minus SCRIPT_NAME
 1295: 						 */
 1296: 						if (env_document_root) {
 1297: 							int l = strlen(env_document_root);
 1298: 							int path_translated_len = 0;
 1299: 							char *path_translated = NULL;
 1300: 
 1301: 							if (l && env_document_root[l - 1] == '/') {
 1302: 								--l;
 1303: 							}
 1304: 
 1305: 							/* we have docroot, so we should have:
 1306: 							 * DOCUMENT_ROOT=/docroot
 1307: 							 * SCRIPT_FILENAME=/docroot/info.php
 1308: 							 */
 1309: 
 1310: 							/* PATH_TRANSLATED = DOCUMENT_ROOT + PATH_INFO */
 1311: 							path_translated_len = l + (env_path_info ? strlen(env_path_info) : 0);
 1312: 							path_translated = (char *) emalloc(path_translated_len + 1);
 1313: 							memcpy(path_translated, env_document_root, l);
 1314: 							if (env_path_info) {
 1315: 								memcpy(path_translated + l, env_path_info, (path_translated_len - l));
 1316: 							}
 1317: 							path_translated[path_translated_len] = '\0';
 1318: 							if (orig_path_translated) {
 1319: 								CGI_PUTENV("ORIG_PATH_TRANSLATED", orig_path_translated);
 1320: 							}
 1321: 							env_path_translated = CGI_PUTENV("PATH_TRANSLATED", path_translated);
 1322: 							efree(path_translated);
 1323: 						} else if (	env_script_name &&
 1324: 									strstr(pt, env_script_name)
 1325: 						) {
 1326: 							/* PATH_TRANSLATED = PATH_TRANSLATED - SCRIPT_NAME + PATH_INFO */
 1327: 							int ptlen = strlen(pt) - strlen(env_script_name);
 1328: 							int path_translated_len = ptlen + (env_path_info ? strlen(env_path_info) : 0);
 1329: 							char *path_translated = NULL;
 1330: 
 1331: 							path_translated = (char *) emalloc(path_translated_len + 1);
 1332: 							memcpy(path_translated, pt, ptlen);
 1333: 							if (env_path_info) {
 1334: 								memcpy(path_translated + ptlen, env_path_info, path_translated_len - ptlen);
 1335: 							}
 1336: 							path_translated[path_translated_len] = '\0';
 1337: 							if (orig_path_translated) {
 1338: 								CGI_PUTENV("ORIG_PATH_TRANSLATED", orig_path_translated);
 1339: 							}
 1340: 							env_path_translated = CGI_PUTENV("PATH_TRANSLATED", path_translated);
 1341: 							efree(path_translated);
 1342: 						}
 1343: 						break;
 1344: 					}
 1345: 				}
 1346: 				if (!ptr) {
 1347: 					/*
 1348: 					 * if we stripped out all the '/' and still didn't find
 1349: 					 * a valid path... we will fail, badly. of course we would
 1350: 					 * have failed anyway... we output 'no input file' now.
 1351: 					 */
 1352: 					if (orig_script_filename) {
 1353: 						CGI_PUTENV("ORIG_SCRIPT_FILENAME", orig_script_filename);
 1354: 					}
 1355: 					script_path_translated = CGI_PUTENV("SCRIPT_FILENAME", NULL);
 1356: 					SG(sapi_headers).http_response_code = 404;
 1357: 				}
 1358: 				if (!SG(request_info).request_uri) {
 1359: 					if (!orig_script_name ||
 1360: 						strcmp(orig_script_name, env_script_name) != 0) {
 1361: 						if (orig_script_name) {
 1362: 							CGI_PUTENV("ORIG_SCRIPT_NAME", orig_script_name);
 1363: 						}
 1364: 						SG(request_info).request_uri = CGI_PUTENV("SCRIPT_NAME", env_script_name);
 1365: 					} else {
 1366: 						SG(request_info).request_uri = orig_script_name;
 1367: 					}
 1368: 				}
 1369: 				if (pt) {
 1370: 					efree(pt);
 1371: 				}
 1372: 			} else {
 1373: 				/* make sure path_info/translated are empty */
 1374: 				if (!orig_script_filename ||
 1375: 					(script_path_translated != orig_script_filename &&
 1376: 					strcmp(script_path_translated, orig_script_filename) != 0)) {
 1377: 					if (orig_script_filename) {
 1378: 						CGI_PUTENV("ORIG_SCRIPT_FILENAME", orig_script_filename);
 1379: 					}
 1380: 					script_path_translated = CGI_PUTENV("SCRIPT_FILENAME", script_path_translated);
 1381: 				}
 1382: 				if (env_redirect_url) {
 1383: 					if (orig_path_info) {
 1384: 						CGI_PUTENV("ORIG_PATH_INFO", orig_path_info);
 1385: 						CGI_PUTENV("PATH_INFO", NULL);
 1386: 					}
 1387: 					if (orig_path_translated) {
 1388: 						CGI_PUTENV("ORIG_PATH_TRANSLATED", orig_path_translated);
 1389: 						CGI_PUTENV("PATH_TRANSLATED", NULL);
 1390: 					}
 1391: 				}
 1392: 				if (env_script_name != orig_script_name) {
 1393: 					if (orig_script_name) {
 1394: 						CGI_PUTENV("ORIG_SCRIPT_NAME", orig_script_name);
 1395: 					}
 1396: 					SG(request_info).request_uri = CGI_PUTENV("SCRIPT_NAME", env_script_name);
 1397: 				} else {
 1398: 					SG(request_info).request_uri = env_script_name;
 1399: 				}
 1400: 				free(real_path);
 1401: 			}
 1402: 		} else {
 1403: 			/* pre 4.3 behaviour, shouldn't be used but provides BC */
 1404: 			if (env_path_info) {
 1405: 				SG(request_info).request_uri = env_path_info;
 1406: 			} else {
 1407: 				SG(request_info).request_uri = env_script_name;
 1408: 			}
 1409: 			if (!CGIG(discard_path) && env_path_translated) {
 1410: 				script_path_translated = env_path_translated;
 1411: 			}
 1412: 		}
 1413: 
 1414: 		if (is_valid_path(script_path_translated)) {
 1415: 			SG(request_info).path_translated = estrdup(script_path_translated);
 1416: 		}
 1417: 
 1418: 		SG(request_info).request_method = CGI_GETENV("REQUEST_METHOD");
 1419: 		/* FIXME - Work out proto_num here */
 1420: 		SG(request_info).query_string = CGI_GETENV("QUERY_STRING");
 1421: 		SG(request_info).content_type = (content_type ? content_type : "" );
 1422: 		SG(request_info).content_length = (content_length ? atol(content_length) : 0);
 1423: 
 1424: 		/* The CGI RFC allows servers to pass on unvalidated Authorization data */
 1425: 		auth = CGI_GETENV("HTTP_AUTHORIZATION");
 1426: 		php_handle_auth_data(auth TSRMLS_CC);
 1427: 	}
 1428: }
 1429: /* }}} */
 1430: 
 1431: #ifndef PHP_WIN32
 1432: /**
 1433:  * Clean up child processes upon exit
 1434:  */
 1435: void fastcgi_cleanup(int signal)
 1436: {
 1437: #ifdef DEBUG_FASTCGI
 1438: 	fprintf(stderr, "FastCGI shutdown, pid %d\n", getpid());
 1439: #endif
 1440: 
 1441: 	sigaction(SIGTERM, &old_term, 0);
 1442: 
 1443: 	/* Kill all the processes in our process group */
 1444: 	kill(-pgroup, SIGTERM);
 1445: 
 1446: 	if (parent && parent_waiting) {
 1447: 		exit_signal = 1;
 1448: 	} else {
 1449: 		exit(0);
 1450: 	}
 1451: }
 1452: #endif
 1453: 
 1454: PHP_INI_BEGIN()
 1455: 	STD_PHP_INI_ENTRY("cgi.rfc2616_headers",     "0",  PHP_INI_ALL,    OnUpdateBool,   rfc2616_headers, php_cgi_globals_struct, php_cgi_globals)
 1456: 	STD_PHP_INI_ENTRY("cgi.nph",                 "0",  PHP_INI_ALL,    OnUpdateBool,   nph, php_cgi_globals_struct, php_cgi_globals)
 1457: 	STD_PHP_INI_ENTRY("cgi.check_shebang_line",  "1",  PHP_INI_SYSTEM, OnUpdateBool,   check_shebang_line, php_cgi_globals_struct, php_cgi_globals)
 1458: 	STD_PHP_INI_ENTRY("cgi.force_redirect",      "1",  PHP_INI_SYSTEM, OnUpdateBool,   force_redirect, php_cgi_globals_struct, php_cgi_globals)
 1459: 	STD_PHP_INI_ENTRY("cgi.redirect_status_env", NULL, PHP_INI_SYSTEM, OnUpdateString, redirect_status_env, php_cgi_globals_struct, php_cgi_globals)
 1460: 	STD_PHP_INI_ENTRY("cgi.fix_pathinfo",        "1",  PHP_INI_SYSTEM, OnUpdateBool,   fix_pathinfo, php_cgi_globals_struct, php_cgi_globals)
 1461: 	STD_PHP_INI_ENTRY("cgi.discard_path",        "0",  PHP_INI_SYSTEM, OnUpdateBool,   discard_path, php_cgi_globals_struct, php_cgi_globals)
 1462: 	STD_PHP_INI_ENTRY("fastcgi.logging",         "1",  PHP_INI_SYSTEM, OnUpdateBool,   fcgi_logging, php_cgi_globals_struct, php_cgi_globals)
 1463: #ifdef PHP_WIN32
 1464: 	STD_PHP_INI_ENTRY("fastcgi.impersonate",     "0",  PHP_INI_SYSTEM, OnUpdateBool,   impersonate, php_cgi_globals_struct, php_cgi_globals)
 1465: #endif
 1466: PHP_INI_END()
 1467: 
 1468: /* {{{ php_cgi_globals_ctor
 1469:  */
 1470: static void php_cgi_globals_ctor(php_cgi_globals_struct *php_cgi_globals TSRMLS_DC)
 1471: {
 1472: 	php_cgi_globals->rfc2616_headers = 0;
 1473: 	php_cgi_globals->nph = 0;
 1474: 	php_cgi_globals->check_shebang_line = 1;
 1475: 	php_cgi_globals->force_redirect = 1;
 1476: 	php_cgi_globals->redirect_status_env = NULL;
 1477: 	php_cgi_globals->fix_pathinfo = 1;
 1478: 	php_cgi_globals->discard_path = 0;
 1479: 	php_cgi_globals->fcgi_logging = 1;
 1480: #ifdef PHP_WIN32
 1481: 	php_cgi_globals->impersonate = 0;
 1482: #endif
 1483: 	zend_hash_init(&php_cgi_globals->user_config_cache, 0, NULL, (dtor_func_t) user_config_cache_entry_dtor, 1);
 1484: }
 1485: /* }}} */
 1486: 
 1487: /* {{{ PHP_MINIT_FUNCTION
 1488:  */
 1489: static PHP_MINIT_FUNCTION(cgi)
 1490: {
 1491: #ifdef ZTS
 1492: 	ts_allocate_id(&php_cgi_globals_id, sizeof(php_cgi_globals_struct), (ts_allocate_ctor) php_cgi_globals_ctor, NULL);
 1493: #else
 1494: 	php_cgi_globals_ctor(&php_cgi_globals TSRMLS_CC);
 1495: #endif
 1496: 	REGISTER_INI_ENTRIES();
 1497: 	return SUCCESS;
 1498: }
 1499: /* }}} */
 1500: 
 1501: /* {{{ PHP_MSHUTDOWN_FUNCTION
 1502:  */
 1503: static PHP_MSHUTDOWN_FUNCTION(cgi)
 1504: {
 1505: 	zend_hash_destroy(&CGIG(user_config_cache));
 1506: 
 1507: 	UNREGISTER_INI_ENTRIES();
 1508: 	return SUCCESS;
 1509: }
 1510: /* }}} */
 1511: 
 1512: /* {{{ PHP_MINFO_FUNCTION
 1513:  */
 1514: static PHP_MINFO_FUNCTION(cgi)
 1515: {
 1516: 	DISPLAY_INI_ENTRIES();
 1517: }
 1518: /* }}} */
 1519: 
 1520: PHP_FUNCTION(apache_child_terminate) /* {{{ */
 1521: {
 1522: 	if (ZEND_NUM_ARGS() > 0) {
 1523: 		WRONG_PARAM_COUNT;
 1524: 	}
 1525: 	if (fcgi_is_fastcgi()) {
 1526: 		fcgi_terminate();
 1527: 	}
 1528: }
 1529: /* }}} */
 1530: 
 1531: static void add_request_header(char *var, unsigned int var_len, char *val, unsigned int val_len, void *arg TSRMLS_DC) /* {{{ */
 1532: {
 1533: 	zval *return_value = (zval*)arg;
 1534: 	char *str = NULL;
 1535: 	char *p;
 1536: 	ALLOCA_FLAG(use_heap)
 1537: 
 1538: 	if (var_len > 5 &&
 1539: 	    var[0] == 'H' &&
 1540: 	    var[1] == 'T' &&
 1541: 	    var[2] == 'T' &&
 1542: 	    var[3] == 'P' &&
 1543: 	    var[4] == '_') {
 1544: 
 1545: 		var_len -= 5;
 1546: 		p = var + 5;
 1547: 		var = str = do_alloca(var_len + 1, use_heap);
 1548: 		*str++ = *p++;
 1549: 		while (*p) {
 1550: 			if (*p == '_') {
 1551: 				*str++ = '-';
 1552: 				p++;
 1553: 				if (*p) {
 1554: 					*str++ = *p++;
 1555: 				}
 1556: 			} else if (*p >= 'A' && *p <= 'Z') {
 1557: 				*str++ = (*p++ - 'A' + 'a');
 1558: 			} else {
 1559: 				*str++ = *p++;
 1560: 			}
 1561: 		}
 1562: 		*str = 0;
 1563: 	} else if (var_len == sizeof("CONTENT_TYPE")-1 &&
 1564: 	           memcmp(var, "CONTENT_TYPE", sizeof("CONTENT_TYPE")-1) == 0) {
 1565: 		var = "Content-Type";
 1566: 	} else if (var_len == sizeof("CONTENT_LENGTH")-1 &&
 1567: 	           memcmp(var, "CONTENT_LENGTH", sizeof("CONTENT_LENGTH")-1) == 0) {
 1568: 		var = "Content-Length";
 1569: 	} else {
 1570: 		return;
 1571: 	}
 1572: 	add_assoc_stringl_ex(return_value, var, var_len+1, val, val_len, 1);
 1573: 	if (str) {
 1574: 		free_alloca(var, use_heap);
 1575: 	}
 1576: }
 1577: /* }}} */
 1578: 
 1579: PHP_FUNCTION(apache_request_headers) /* {{{ */
 1580: {
 1581: 	if (ZEND_NUM_ARGS() > 0) {
 1582: 		WRONG_PARAM_COUNT;
 1583: 	}
 1584: 	array_init(return_value);
 1585: 	if (fcgi_is_fastcgi()) {
 1586: 		fcgi_request *request = (fcgi_request*) SG(server_context);
 1587: 
 1588: 		fcgi_loadenv(request, add_request_header, return_value TSRMLS_CC);
 1589: 	} else {
 1590: 		char buf[128];
 1591: 		char **env, *p, *q, *var, *val, *t = buf;
 1592: 		size_t alloc_size = sizeof(buf);
 1593: 		unsigned long var_len;
 1594: 
 1595: 		for (env = environ; env != NULL && *env != NULL; env++) {
 1596: 			val = strchr(*env, '=');
 1597: 			if (!val) {				/* malformed entry? */
 1598: 				continue;
 1599: 			}
 1600: 			var_len = val - *env;
 1601: 			if (var_len >= alloc_size) {
 1602: 				alloc_size = var_len + 64;
 1603: 				t = (t == buf ? emalloc(alloc_size): erealloc(t, alloc_size));
 1604: 			}
 1605: 			var = *env;
 1606: 			if (var_len > 5 &&
 1607: 			    var[0] == 'H' &&
 1608: 			    var[1] == 'T' &&
 1609: 			    var[2] == 'T' &&
 1610: 			    var[3] == 'P' &&
 1611: 			    var[4] == '_') {
 1612: 
 1613: 				var_len -= 5;
 1614: 
 1615: 				if (var_len >= alloc_size) {
 1616: 					alloc_size = var_len + 64;
 1617: 					t = (t == buf ? emalloc(alloc_size): erealloc(t, alloc_size));
 1618: 				}
 1619: 				p = var + 5;
 1620: 
 1621: 				var = q = t;
 1622: 				/* First char keep uppercase */
 1623: 				*q++ = *p++;
 1624: 				while (*p) {
 1625: 					if (*p == '=') {
 1626: 						/* End of name */
 1627: 						break;
 1628: 					} else if (*p == '_') {
 1629: 						*q++ = '-';
 1630: 						p++;
 1631: 						/* First char after - keep uppercase */
 1632: 						if (*p && *p!='=') {
 1633: 							*q++ = *p++;
 1634: 						}
 1635: 					} else if (*p >= 'A' && *p <= 'Z') {
 1636: 						/* lowercase */
 1637: 						*q++ = (*p++ - 'A' + 'a');
 1638: 					} else {
 1639: 						*q++ = *p++;
 1640: 					}
 1641: 				}
 1642: 				*q = 0;
 1643: 			} else if (var_len == sizeof("CONTENT_TYPE")-1 &&
 1644: 			           memcmp(var, "CONTENT_TYPE", sizeof("CONTENT_TYPE")-1) == 0) {
 1645: 				var = "Content-Type";
 1646: 			} else if (var_len == sizeof("CONTENT_LENGTH")-1 &&
 1647: 			           memcmp(var, "CONTENT_LENGTH", sizeof("CONTENT_LENGTH")-1) == 0) {
 1648: 				var = "Content-Length";
 1649: 			} else {
 1650: 				continue;
 1651: 			}
 1652: 			val++;
 1653: 			add_assoc_string_ex(return_value, var, var_len+1, val, 1);
 1654: 		}
 1655: 		if (t != buf && t != NULL) {
 1656: 			efree(t);
 1657: 		}
 1658: 	}
 1659: }
 1660: /* }}} */
 1661: 
 1662: static void add_response_header(sapi_header_struct *h, zval *return_value TSRMLS_DC) /* {{{ */
 1663: {
 1664: 	char *s, *p;
 1665: 	int  len;
 1666: 	ALLOCA_FLAG(use_heap)
 1667: 
 1668: 	if (h->header_len > 0) {
 1669: 		p = strchr(h->header, ':');
 1670: 		len = p - h->header;
 1671: 		if (p && (len > 0)) {
 1672: 			while (len > 0 && (h->header[len-1] == ' ' || h->header[len-1] == '\t')) {
 1673: 				len--;
 1674: 			}
 1675: 			if (len) {
 1676: 				s = do_alloca(len + 1, use_heap);
 1677: 				memcpy(s, h->header, len);
 1678: 				s[len] = 0;
 1679: 				do {
 1680: 					p++;
 1681: 				} while (*p == ' ' || *p == '\t');
 1682: 				add_assoc_stringl_ex(return_value, s, len+1, p, h->header_len - (p - h->header), 1);
 1683: 				free_alloca(s, use_heap);
 1684: 			}
 1685: 		}
 1686: 	}
 1687: }
 1688: /* }}} */
 1689: 
 1690: PHP_FUNCTION(apache_response_headers) /* {{{ */
 1691: {
 1692: 	if (ZEND_NUM_ARGS() > 0) {
 1693: 		WRONG_PARAM_COUNT;
 1694: 	}
 1695: 
 1696: 	if (!&SG(sapi_headers).headers) {
 1697: 		RETURN_FALSE;
 1698: 	}
 1699: 	array_init(return_value);
 1700: 	zend_llist_apply_with_argument(&SG(sapi_headers).headers, (llist_apply_with_arg_func_t)add_response_header, return_value TSRMLS_CC);
 1701: }
 1702: /* }}} */
 1703: 
 1704: ZEND_BEGIN_ARG_INFO(arginfo_no_args, 0)
 1705: ZEND_END_ARG_INFO()
 1706: 
 1707: const zend_function_entry cgi_functions[] = {
 1708: 	PHP_FE(apache_child_terminate, arginfo_no_args)
 1709: 	PHP_FE(apache_request_headers, arginfo_no_args)
 1710: 	PHP_FE(apache_response_headers, arginfo_no_args)
 1711: 	PHP_FALIAS(getallheaders, apache_request_headers, arginfo_no_args)
 1712: 	{NULL, NULL, NULL}
 1713: };
 1714: 
 1715: static zend_module_entry cgi_module_entry = {
 1716: 	STANDARD_MODULE_HEADER,
 1717: 	"cgi-fcgi",
 1718: 	cgi_functions,
 1719: 	PHP_MINIT(cgi),
 1720: 	PHP_MSHUTDOWN(cgi),
 1721: 	NULL,
 1722: 	NULL,
 1723: 	PHP_MINFO(cgi),
 1724: 	NO_VERSION_YET,
 1725: 	STANDARD_MODULE_PROPERTIES
 1726: };
 1727: 
 1728: /* {{{ main
 1729:  */
 1730: int main(int argc, char *argv[])
 1731: {
 1732: 	int free_query_string = 0;
 1733: 	int exit_status = SUCCESS;
 1734: 	int cgi = 0, c, i, len;
 1735: 	zend_file_handle file_handle;
 1736: 	char *s;
 1737: 
 1738: 	/* temporary locals */
 1739: 	int behavior = PHP_MODE_STANDARD;
 1740: 	int no_headers = 0;
 1741: 	int orig_optind = php_optind;
 1742: 	char *orig_optarg = php_optarg;
 1743: 	char *script_file = NULL;
 1744: 	int ini_entries_len = 0;
 1745: 	/* end of temporary locals */
 1746: 
 1747: #ifdef ZTS
 1748: 	void ***tsrm_ls;
 1749: #endif
 1750: 
 1751: 	int max_requests = 500;
 1752: 	int requests = 0;
 1753: 	int fastcgi;
 1754: 	char *bindpath = NULL;
 1755: 	int fcgi_fd = 0;
 1756: 	fcgi_request *request = NULL;
 1757: 	int repeats = 1;
 1758: 	int benchmark = 0;
 1759: #if HAVE_GETTIMEOFDAY
 1760: 	struct timeval start, end;
 1761: #else
 1762: 	time_t start, end;
 1763: #endif
 1764: #ifndef PHP_WIN32
 1765: 	int status = 0;
 1766: #endif
 1767: 	char *query_string;
 1768: 	char *decoded_query_string;
 1769: 	int skip_getopt = 0;
 1770: 
 1771: #if 0 && defined(PHP_DEBUG)
 1772: 	/* IIS is always making things more difficult.  This allows
 1773: 	 * us to stop PHP and attach a debugger before much gets started */
 1774: 	{
 1775: 		char szMessage [256];
 1776: 		wsprintf (szMessage, "Please attach a debugger to the process 0x%X [%d] (%s) and click OK", GetCurrentProcessId(), GetCurrentProcessId(), argv[0]);
 1777: 		MessageBox(NULL, szMessage, "CGI Debug Time!", MB_OK|MB_SERVICE_NOTIFICATION);
 1778: 	}
 1779: #endif
 1780: 
 1781: #ifdef HAVE_SIGNAL_H
 1782: #if defined(SIGPIPE) && defined(SIG_IGN)
 1783: 	signal(SIGPIPE, SIG_IGN); /* ignore SIGPIPE in standalone mode so
 1784: 								that sockets created via fsockopen()
 1785: 								don't kill PHP if the remote site
 1786: 								closes it.  in apache|apxs mode apache
 1787: 								does that for us!  thies@thieso.net
 1788: 								20000419 */
 1789: #endif
 1790: #endif
 1791: 
 1792: #ifdef ZTS
 1793: 	tsrm_startup(1, 1, 0, NULL);
 1794: 	tsrm_ls = ts_resource(0);
 1795: #endif
 1796: 
 1797: 	sapi_startup(&cgi_sapi_module);
 1798: 	fastcgi = fcgi_is_fastcgi();
 1799: 	cgi_sapi_module.php_ini_path_override = NULL;
 1800: 
 1801: #ifdef PHP_WIN32
 1802: 	_fmode = _O_BINARY; /* sets default for file streams to binary */
 1803: 	setmode(_fileno(stdin),  O_BINARY);	/* make the stdio mode be binary */
 1804: 	setmode(_fileno(stdout), O_BINARY);	/* make the stdio mode be binary */
 1805: 	setmode(_fileno(stderr), O_BINARY);	/* make the stdio mode be binary */
 1806: #endif
 1807: 
 1808: 	if (!fastcgi) {
 1809: 		/* Make sure we detect we are a cgi - a bit redundancy here,
 1810: 		 * but the default case is that we have to check only the first one. */
 1811: 		if (getenv("SERVER_SOFTWARE") ||
 1812: 			getenv("SERVER_NAME") ||
 1813: 			getenv("GATEWAY_INTERFACE") ||
 1814: 			getenv("REQUEST_METHOD")
 1815: 		) {
 1816: 			cgi = 1;
 1817: 		}
 1818: 	}
 1819: 
 1820: 	if((query_string = getenv("QUERY_STRING")) != NULL && strchr(query_string, '=') == NULL) {
 1821: 		/* we've got query string that has no = - apache CGI will pass it to command line */
 1822: 		unsigned char *p;
 1823: 		decoded_query_string = strdup(query_string);
 1824: 		php_url_decode(decoded_query_string, strlen(decoded_query_string));
 1825: 		for (p = decoded_query_string; *p &&  *p <= ' '; p++) {
 1826: 			/* skip all leading spaces */
 1827: 		}
 1828: 		if(*p == '-') {
 1829: 			skip_getopt = 1;
 1830: 		}
 1831: 		free(decoded_query_string);
 1832: 	}
 1833: 
 1834: 	while (!skip_getopt && (c = php_getopt(argc, argv, OPTIONS, &php_optarg, &php_optind, 0, 2)) != -1) {
 1835: 		switch (c) {
 1836: 			case 'c':
 1837: 				if (cgi_sapi_module.php_ini_path_override) {
 1838: 					free(cgi_sapi_module.php_ini_path_override);
 1839: 				}
 1840: 				cgi_sapi_module.php_ini_path_override = strdup(php_optarg);
 1841: 				break;
 1842: 			case 'n':
 1843: 				cgi_sapi_module.php_ini_ignore = 1;
 1844: 				break;
 1845: 			case 'd': {
 1846: 				/* define ini entries on command line */
 1847: 				int len = strlen(php_optarg);
 1848: 				char *val;
 1849: 
 1850: 				if ((val = strchr(php_optarg, '='))) {
 1851: 					val++;
 1852: 					if (!isalnum(*val) && *val != '"' && *val != '\'' && *val != '\0') {
 1853: 						cgi_sapi_module.ini_entries = realloc(cgi_sapi_module.ini_entries, ini_entries_len + len + sizeof("\"\"\n\0"));
 1854: 						memcpy(cgi_sapi_module.ini_entries + ini_entries_len, php_optarg, (val - php_optarg));
 1855: 						ini_entries_len += (val - php_optarg);
 1856: 						memcpy(cgi_sapi_module.ini_entries + ini_entries_len, "\"", 1);
 1857: 						ini_entries_len++;
 1858: 						memcpy(cgi_sapi_module.ini_entries + ini_entries_len, val, len - (val - php_optarg));
 1859: 						ini_entries_len += len - (val - php_optarg);
 1860: 						memcpy(cgi_sapi_module.ini_entries + ini_entries_len, "\"\n\0", sizeof("\"\n\0"));
 1861: 						ini_entries_len += sizeof("\n\0\"") - 2;
 1862: 					} else {
 1863: 						cgi_sapi_module.ini_entries = realloc(cgi_sapi_module.ini_entries, ini_entries_len + len + sizeof("\n\0"));
 1864: 						memcpy(cgi_sapi_module.ini_entries + ini_entries_len, php_optarg, len);
 1865: 						memcpy(cgi_sapi_module.ini_entries + ini_entries_len + len, "\n\0", sizeof("\n\0"));
 1866: 						ini_entries_len += len + sizeof("\n\0") - 2;
 1867: 					}
 1868: 				} else {
 1869: 					cgi_sapi_module.ini_entries = realloc(cgi_sapi_module.ini_entries, ini_entries_len + len + sizeof("=1\n\0"));
 1870: 					memcpy(cgi_sapi_module.ini_entries + ini_entries_len, php_optarg, len);
 1871: 					memcpy(cgi_sapi_module.ini_entries + ini_entries_len + len, "=1\n\0", sizeof("=1\n\0"));
 1872: 					ini_entries_len += len + sizeof("=1\n\0") - 2;
 1873: 				}
 1874: 				break;
 1875: 			}
 1876: 			/* if we're started on command line, check to see if
 1877: 			 * we are being started as an 'external' fastcgi
 1878: 			 * server by accepting a bindpath parameter. */
 1879: 			case 'b':
 1880: 				if (!fastcgi) {
 1881: 					bindpath = strdup(php_optarg);
 1882: 				}
 1883: 				break;
 1884: 			case 's': /* generate highlighted HTML from source */
 1885: 				behavior = PHP_MODE_HIGHLIGHT;
 1886: 				break;
 1887: 		}
 1888: 	}
 1889: 	php_optind = orig_optind;
 1890: 	php_optarg = orig_optarg;
 1891: 
 1892: 	if (fastcgi || bindpath) {
 1893: 		/* Override SAPI callbacks */
 1894: 		cgi_sapi_module.ub_write     = sapi_fcgi_ub_write;
 1895: 		cgi_sapi_module.flush        = sapi_fcgi_flush;
 1896: 		cgi_sapi_module.read_post    = sapi_fcgi_read_post;
 1897: 		cgi_sapi_module.getenv       = sapi_fcgi_getenv;
 1898: 		cgi_sapi_module.read_cookies = sapi_fcgi_read_cookies;
 1899: 	}
 1900: 
 1901: #ifdef ZTS
 1902: 	SG(request_info).path_translated = NULL;
 1903: #endif
 1904: 
 1905: 	cgi_sapi_module.executable_location = argv[0];
 1906: 	if (!cgi && !fastcgi && !bindpath) {
 1907: 		cgi_sapi_module.additional_functions = additional_functions;
 1908: 	}
 1909: 
 1910: 	/* startup after we get the above ini override se we get things right */
 1911: 	if (cgi_sapi_module.startup(&cgi_sapi_module) == FAILURE) {
 1912: #ifdef ZTS
 1913: 		tsrm_shutdown();
 1914: #endif
 1915: 		return FAILURE;
 1916: 	}
 1917: 
 1918: 	/* check force_cgi after startup, so we have proper output */
 1919: 	if (cgi && CGIG(force_redirect)) {
 1920: 		/* Apache will generate REDIRECT_STATUS,
 1921: 		 * Netscape and redirect.so will generate HTTP_REDIRECT_STATUS.
 1922: 		 * redirect.so and installation instructions available from
 1923: 		 * http://www.koehntopp.de/php.
 1924: 		 *   -- kk@netuse.de
 1925: 		 */
 1926: 		if (!getenv("REDIRECT_STATUS") &&
 1927: 			!getenv ("HTTP_REDIRECT_STATUS") &&
 1928: 			/* this is to allow a different env var to be configured
 1929: 			 * in case some server does something different than above */
 1930: 			(!CGIG(redirect_status_env) || !getenv(CGIG(redirect_status_env)))
 1931: 		) {
 1932: 			zend_try {
 1933: 				SG(sapi_headers).http_response_code = 400;
 1934: 				PUTS("<b>Security Alert!</b> The PHP CGI cannot be accessed directly.\n\n\
 1935: <p>This PHP CGI binary was compiled with force-cgi-redirect enabled.  This\n\
 1936: means that a page will only be served up if the REDIRECT_STATUS CGI variable is\n\
 1937: set, e.g. via an Apache Action directive.</p>\n\
 1938: <p>For more information as to <i>why</i> this behaviour exists, see the <a href=\"http://php.net/security.cgi-bin\">\
 1939: manual page for CGI security</a>.</p>\n\
 1940: <p>For more information about changing this behaviour or re-enabling this webserver,\n\
 1941: consult the installation file that came with this distribution, or visit \n\
 1942: <a href=\"http://php.net/install.windows\">the manual page</a>.</p>\n");
 1943: 			} zend_catch {
 1944: 			} zend_end_try();
 1945: #if defined(ZTS) && !defined(PHP_DEBUG)
 1946: 			/* XXX we're crashing here in msvc6 debug builds at
 1947: 			 * php_message_handler_for_zend:839 because
 1948: 			 * SG(request_info).path_translated is an invalid pointer.
 1949: 			 * It still happens even though I set it to null, so something
 1950: 			 * weird is going on.
 1951: 			 */
 1952: 			tsrm_shutdown();
 1953: #endif
 1954: 			return FAILURE;
 1955: 		}
 1956: 	}
 1957: 
 1958: 	if (bindpath) {
 1959: 		fcgi_fd = fcgi_listen(bindpath, 128);
 1960: 		if (fcgi_fd < 0) {
 1961: 			fprintf(stderr, "Couldn't create FastCGI listen socket on port %s\n", bindpath);
 1962: #ifdef ZTS
 1963: 			tsrm_shutdown();
 1964: #endif
 1965: 			return FAILURE;
 1966: 		}
 1967: 		fastcgi = fcgi_is_fastcgi();
 1968: 	}
 1969: 	if (fastcgi) {
 1970: 		/* How many times to run PHP scripts before dying */
 1971: 		if (getenv("PHP_FCGI_MAX_REQUESTS")) {
 1972: 			max_requests = atoi(getenv("PHP_FCGI_MAX_REQUESTS"));
 1973: 			if (max_requests < 0) {
 1974: 				fprintf(stderr, "PHP_FCGI_MAX_REQUESTS is not valid\n");
 1975: 				return FAILURE;
 1976: 			}
 1977: 		}
 1978: 
 1979: 		/* make php call us to get _ENV vars */
 1980: 		php_php_import_environment_variables = php_import_environment_variables;
 1981: 		php_import_environment_variables = cgi_php_import_environment_variables;
 1982: 
 1983: 		/* library is already initialized, now init our request */
 1984: 		request = fcgi_init_request(fcgi_fd);
 1985: 
 1986: #ifndef PHP_WIN32
 1987: 	/* Pre-fork, if required */
 1988: 	if (getenv("PHP_FCGI_CHILDREN")) {
 1989: 		char * children_str = getenv("PHP_FCGI_CHILDREN");
 1990: 		children = atoi(children_str);
 1991: 		if (children < 0) {
 1992: 			fprintf(stderr, "PHP_FCGI_CHILDREN is not valid\n");
 1993: 			return FAILURE;
 1994: 		}
 1995: 		fcgi_set_mgmt_var("FCGI_MAX_CONNS", sizeof("FCGI_MAX_CONNS")-1, children_str, strlen(children_str));
 1996: 		/* This is the number of concurrent requests, equals FCGI_MAX_CONNS */
 1997: 		fcgi_set_mgmt_var("FCGI_MAX_REQS",  sizeof("FCGI_MAX_REQS")-1,  children_str, strlen(children_str));
 1998: 	} else {
 1999: 		fcgi_set_mgmt_var("FCGI_MAX_CONNS", sizeof("FCGI_MAX_CONNS")-1, "1", sizeof("1")-1);
 2000: 		fcgi_set_mgmt_var("FCGI_MAX_REQS",  sizeof("FCGI_MAX_REQS")-1,  "1", sizeof("1")-1);
 2001: 	}
 2002: 
 2003: 	if (children) {
 2004: 		int running = 0;
 2005: 		pid_t pid;
 2006: 
 2007: 		/* Create a process group for ourself & children */
 2008: 		setsid();
 2009: 		pgroup = getpgrp();
 2010: #ifdef DEBUG_FASTCGI
 2011: 		fprintf(stderr, "Process group %d\n", pgroup);
 2012: #endif
 2013: 
 2014: 		/* Set up handler to kill children upon exit */
 2015: 		act.sa_flags = 0;
 2016: 		act.sa_handler = fastcgi_cleanup;
 2017: 		if (sigaction(SIGTERM, &act, &old_term) ||
 2018: 			sigaction(SIGINT,  &act, &old_int)  ||
 2019: 			sigaction(SIGQUIT, &act, &old_quit)
 2020: 		) {
 2021: 			perror("Can't set signals");
 2022: 			exit(1);
 2023: 		}
 2024: 
 2025: 		if (fcgi_in_shutdown()) {
 2026: 			goto parent_out;
 2027: 		}
 2028: 
 2029: 		while (parent) {
 2030: 			do {
 2031: #ifdef DEBUG_FASTCGI
 2032: 				fprintf(stderr, "Forking, %d running\n", running);
 2033: #endif
 2034: 				pid = fork();
 2035: 				switch (pid) {
 2036: 				case 0:
 2037: 					/* One of the children.
 2038: 					 * Make sure we don't go round the
 2039: 					 * fork loop any more
 2040: 					 */
 2041: 					parent = 0;
 2042: 
 2043: 					/* don't catch our signals */
 2044: 					sigaction(SIGTERM, &old_term, 0);
 2045: 					sigaction(SIGQUIT, &old_quit, 0);
 2046: 					sigaction(SIGINT,  &old_int,  0);
 2047: 					break;
 2048: 				case -1:
 2049: 					perror("php (pre-forking)");
 2050: 					exit(1);
 2051: 					break;
 2052: 				default:
 2053: 					/* Fine */
 2054: 					running++;
 2055: 					break;
 2056: 				}
 2057: 			} while (parent && (running < children));
 2058: 
 2059: 			if (parent) {
 2060: #ifdef DEBUG_FASTCGI
 2061: 				fprintf(stderr, "Wait for kids, pid %d\n", getpid());
 2062: #endif
 2063: 				parent_waiting = 1;
 2064: 				while (1) {
 2065: 					if (wait(&status) >= 0) {
 2066: 						running--;
 2067: 						break;
 2068: 					} else if (exit_signal) {
 2069: 						break;
 2070: 					}
 2071: 				}
 2072: 				if (exit_signal) {
 2073: #if 0
 2074: 					while (running > 0) {
 2075: 						while (wait(&status) < 0) {
 2076: 						}
 2077: 						running--;
 2078: 					}
 2079: #endif
 2080: 					goto parent_out;
 2081: 				}
 2082: 			}
 2083: 		}
 2084: 	} else {
 2085: 		parent = 0;
 2086: 	}
 2087: 
 2088: #endif /* WIN32 */
 2089: 	}
 2090: 
 2091: 	zend_first_try {
 2092: 		while (!skip_getopt && (c = php_getopt(argc, argv, OPTIONS, &php_optarg, &php_optind, 1, 2)) != -1) {
 2093: 			switch (c) {
 2094: 				case 'T':
 2095: 					benchmark = 1;
 2096: 					repeats = atoi(php_optarg);
 2097: #ifdef HAVE_GETTIMEOFDAY
 2098: 					gettimeofday(&start, NULL);
 2099: #else
 2100: 					time(&start);
 2101: #endif
 2102: 					break;
 2103: 				case 'h':
 2104: 				case '?':
 2105: 					if (request) {
 2106: 						fcgi_destroy_request(request);
 2107: 					}
 2108: 					fcgi_shutdown();
 2109: 					no_headers = 1;
 2110: 					SG(headers_sent) = 1;
 2111: 					php_cgi_usage(argv[0]);
 2112: 					php_output_end_all(TSRMLS_C);
 2113: 					exit_status = 0;
 2114: 					goto out;
 2115: 			}
 2116: 		}
 2117: 		php_optind = orig_optind;
 2118: 		php_optarg = orig_optarg;
 2119: 
 2120: 		/* start of FAST CGI loop */
 2121: 		/* Initialise FastCGI request structure */
 2122: #ifdef PHP_WIN32
 2123: 		/* attempt to set security impersonation for fastcgi
 2124: 		 * will only happen on NT based OS, others will ignore it. */
 2125: 		if (fastcgi && CGIG(impersonate)) {
 2126: 			fcgi_impersonate();
 2127: 		}
 2128: #endif
 2129: 		while (!fastcgi || fcgi_accept_request(request) >= 0) {
 2130: 			SG(server_context) = fastcgi ? (void *) request : (void *) 1;
 2131: 			init_request_info(request TSRMLS_CC);
 2132: 			CG(interactive) = 0;
 2133: 
 2134: 			if (!cgi && !fastcgi) {
 2135: 				while ((c = php_getopt(argc, argv, OPTIONS, &php_optarg, &php_optind, 0, 2)) != -1) {
 2136: 					switch (c) {
 2137: 
 2138: 						case 'a':	/* interactive mode */
 2139: 							printf("Interactive mode enabled\n\n");
 2140: 							CG(interactive) = 1;
 2141: 							break;
 2142: 
 2143: 						case 'C': /* don't chdir to the script directory */
 2144: 							SG(options) |= SAPI_OPTION_NO_CHDIR;
 2145: 							break;
 2146: 
 2147: 						case 'e': /* enable extended info output */
 2148: 							CG(compiler_options) |= ZEND_COMPILE_EXTENDED_INFO;
 2149: 							break;
 2150: 
 2151: 						case 'f': /* parse file */
 2152: 							if (script_file) {
 2153: 								efree(script_file);
 2154: 							}
 2155: 							script_file = estrdup(php_optarg);
 2156: 							no_headers = 1;
 2157: 							break;
 2158: 
 2159: 						case 'i': /* php info & quit */
 2160: 							if (script_file) {
 2161: 								efree(script_file);
 2162: 							}
 2163: 							if (php_request_startup(TSRMLS_C) == FAILURE) {
 2164: 								SG(server_context) = NULL;
 2165: 								php_module_shutdown(TSRMLS_C);
 2166: 								return FAILURE;
 2167: 							}
 2168: 							if (no_headers) {
 2169: 								SG(headers_sent) = 1;
 2170: 								SG(request_info).no_headers = 1;
 2171: 							}
 2172: 							php_print_info(0xFFFFFFFF TSRMLS_CC);
 2173: 							php_request_shutdown((void *) 0);
 2174: 							fcgi_shutdown();
 2175: 							exit_status = 0;
 2176: 							goto out;
 2177: 
 2178: 						case 'l': /* syntax check mode */
 2179: 							no_headers = 1;
 2180: 							behavior = PHP_MODE_LINT;
 2181: 							break;
 2182: 
 2183: 						case 'm': /* list compiled in modules */
 2184: 							if (script_file) {
 2185: 								efree(script_file);
 2186: 							}
 2187: 							SG(headers_sent) = 1;
 2188: 							php_printf("[PHP Modules]\n");
 2189: 							print_modules(TSRMLS_C);
 2190: 							php_printf("\n[Zend Modules]\n");
 2191: 							print_extensions(TSRMLS_C);
 2192: 							php_printf("\n");
 2193: 							php_output_end_all(TSRMLS_C);
 2194: 							fcgi_shutdown();
 2195: 							exit_status = 0;
 2196: 							goto out;
 2197: 
 2198: #if 0 /* not yet operational, see also below ... */
 2199: 						case '': /* generate indented source mode*/
 2200: 							behavior=PHP_MODE_INDENT;
 2201: 							break;
 2202: #endif
 2203: 
 2204: 						case 'q': /* do not generate HTTP headers */
 2205: 							no_headers = 1;
 2206: 							break;
 2207: 
 2208: 						case 'v': /* show php version & quit */
 2209: 							if (script_file) {
 2210: 								efree(script_file);
 2211: 							}
 2212: 							no_headers = 1;
 2213: 							if (php_request_startup(TSRMLS_C) == FAILURE) {
 2214: 								SG(server_context) = NULL;
 2215: 								php_module_shutdown(TSRMLS_C);
 2216: 								return FAILURE;
 2217: 							}
 2218: 							if (no_headers) {
 2219: 								SG(headers_sent) = 1;
 2220: 								SG(request_info).no_headers = 1;
 2221: 							}
 2222: #if ZEND_DEBUG
 2223: 							php_printf("PHP %s (%s) (built: %s %s) (DEBUG)\nCopyright (c) 1997-2014 The PHP Group\n%s", PHP_VERSION, sapi_module.name, __DATE__, __TIME__, get_zend_version());
 2224: #else
 2225: 							php_printf("PHP %s (%s) (built: %s %s)\nCopyright (c) 1997-2014 The PHP Group\n%s", PHP_VERSION, sapi_module.name, __DATE__, __TIME__, get_zend_version());
 2226: #endif
 2227: 							php_request_shutdown((void *) 0);
 2228: 							fcgi_shutdown();
 2229: 							exit_status = 0;
 2230: 							goto out;
 2231: 
 2232: 						case 'w':
 2233: 							behavior = PHP_MODE_STRIP;
 2234: 							break;
 2235: 
 2236: 						case 'z': /* load extension file */
 2237: 							zend_load_extension(php_optarg);
 2238: 							break;
 2239: 
 2240: 						default:
 2241: 							break;
 2242: 					}
 2243: 				}
 2244: 
 2245: 				if (script_file) {
 2246: 					/* override path_translated if -f on command line */
 2247: 					STR_FREE(SG(request_info).path_translated);
 2248: 					SG(request_info).path_translated = script_file;
 2249: 					/* before registering argv to module exchange the *new* argv[0] */
 2250: 					/* we can achieve this without allocating more memory */
 2251: 					SG(request_info).argc = argc - (php_optind - 1);
 2252: 					SG(request_info).argv = &argv[php_optind - 1];
 2253: 					SG(request_info).argv[0] = script_file;
 2254: 				} else if (argc > php_optind) {
 2255: 					/* file is on command line, but not in -f opt */
 2256: 					STR_FREE(SG(request_info).path_translated);
 2257: 					SG(request_info).path_translated = estrdup(argv[php_optind]);
 2258: 					/* arguments after the file are considered script args */
 2259: 					SG(request_info).argc = argc - php_optind;
 2260: 					SG(request_info).argv = &argv[php_optind];
 2261: 				}
 2262: 
 2263: 				if (no_headers) {
 2264: 					SG(headers_sent) = 1;
 2265: 					SG(request_info).no_headers = 1;
 2266: 				}
 2267: 
 2268: 				/* all remaining arguments are part of the query string
 2269: 				 * this section of code concatenates all remaining arguments
 2270: 				 * into a single string, seperating args with a &
 2271: 				 * this allows command lines like:
 2272: 				 *
 2273: 				 *  test.php v1=test v2=hello+world!
 2274: 				 *  test.php "v1=test&v2=hello world!"
 2275: 				 *  test.php v1=test "v2=hello world!"
 2276: 				*/
 2277: 				if (!SG(request_info).query_string && argc > php_optind) {
 2278: 					int slen = strlen(PG(arg_separator).input);
 2279: 					len = 0;
 2280: 					for (i = php_optind; i < argc; i++) {
 2281: 						if (i < (argc - 1)) {
 2282: 							len += strlen(argv[i]) + slen;
 2283: 						} else {
 2284: 							len += strlen(argv[i]);
 2285: 						}
 2286: 					}
 2287: 
 2288: 					len += 2;
 2289: 					s = malloc(len);
 2290: 					*s = '\0';			/* we are pretending it came from the environment  */
 2291: 					for (i = php_optind; i < argc; i++) {
 2292: 						strlcat(s, argv[i], len);
 2293: 						if (i < (argc - 1)) {
 2294: 							strlcat(s, PG(arg_separator).input, len);
 2295: 						}
 2296: 					}
 2297: 					SG(request_info).query_string = s;
 2298: 					free_query_string = 1;
 2299: 				}
 2300: 			} /* end !cgi && !fastcgi */
 2301: 
 2302: 			/*
 2303: 				we never take stdin if we're (f)cgi, always
 2304: 				rely on the web server giving us the info
 2305: 				we need in the environment.
 2306: 			*/
 2307: 			if (SG(request_info).path_translated || cgi || fastcgi) {
 2308: 				file_handle.type = ZEND_HANDLE_FILENAME;
 2309: 				file_handle.filename = SG(request_info).path_translated;
 2310: 				file_handle.handle.fp = NULL;
 2311: 			} else {
 2312: 				file_handle.filename = "-";
 2313: 				file_handle.type = ZEND_HANDLE_FP;
 2314: 				file_handle.handle.fp = stdin;
 2315: 			}
 2316: 
 2317: 			file_handle.opened_path = NULL;
 2318: 			file_handle.free_filename = 0;
 2319: 
 2320: 			/* request startup only after we've done all we can to
 2321: 			 * get path_translated */
 2322: 			if (php_request_startup(TSRMLS_C) == FAILURE) {
 2323: 				if (fastcgi) {
 2324: 					fcgi_finish_request(request, 1);
 2325: 				}
 2326: 				SG(server_context) = NULL;
 2327: 				php_module_shutdown(TSRMLS_C);
 2328: 				return FAILURE;
 2329: 			}
 2330: 			if (no_headers) {
 2331: 				SG(headers_sent) = 1;
 2332: 				SG(request_info).no_headers = 1;
 2333: 			}
 2334: 
 2335: 			/*
 2336: 				at this point path_translated will be set if:
 2337: 				1. we are running from shell and got filename was there
 2338: 				2. we are running as cgi or fastcgi
 2339: 			*/
 2340: 			if (cgi || fastcgi || SG(request_info).path_translated) {
 2341: 				if (php_fopen_primary_script(&file_handle TSRMLS_CC) == FAILURE) {
 2342: 					zend_try {
 2343: 						if (errno == EACCES) {
 2344: 							SG(sapi_headers).http_response_code = 403;
 2345: 							PUTS("Access denied.\n");
 2346: 						} else {
 2347: 							SG(sapi_headers).http_response_code = 404;
 2348: 							PUTS("No input file specified.\n");
 2349: 						}
 2350: 					} zend_catch {
 2351: 					} zend_end_try();
 2352: 					/* we want to serve more requests if this is fastcgi
 2353: 					 * so cleanup and continue, request shutdown is
 2354: 					 * handled later */
 2355: 					if (fastcgi) {
 2356: 						goto fastcgi_request_done;
 2357: 					}
 2358: 
 2359: 					STR_FREE(SG(request_info).path_translated);
 2360: 
 2361: 					if (free_query_string && SG(request_info).query_string) {
 2362: 						free(SG(request_info).query_string);
 2363: 						SG(request_info).query_string = NULL;
 2364: 					}
 2365: 
 2366: 					php_request_shutdown((void *) 0);
 2367: 					SG(server_context) = NULL;
 2368: 					php_module_shutdown(TSRMLS_C);
 2369: 					sapi_shutdown();
 2370: #ifdef ZTS
 2371: 					tsrm_shutdown();
 2372: #endif
 2373: 					return FAILURE;
 2374: 				}
 2375: 			}
 2376: 
 2377: 			if (CGIG(check_shebang_line)) {
 2378: 				/* #!php support */
 2379: 				switch (file_handle.type) {
 2380: 					case ZEND_HANDLE_FD:
 2381: 						if (file_handle.handle.fd < 0) {
 2382: 							break;
 2383: 						}
 2384: 						file_handle.type = ZEND_HANDLE_FP;
 2385: 						file_handle.handle.fp = fdopen(file_handle.handle.fd, "rb");
 2386: 						/* break missing intentionally */
 2387: 					case ZEND_HANDLE_FP:
 2388: 						if (!file_handle.handle.fp ||
 2389: 						    (file_handle.handle.fp == stdin)) {
 2390: 							break;
 2391: 						}
 2392: 						c = fgetc(file_handle.handle.fp);
 2393: 						if (c == '#') {
 2394: 							while (c != '\n' && c != '\r' && c != EOF) {
 2395: 								c = fgetc(file_handle.handle.fp);	/* skip to end of line */
 2396: 							}
 2397: 							/* handle situations where line is terminated by \r\n */
 2398: 							if (c == '\r') {
 2399: 								if (fgetc(file_handle.handle.fp) != '\n') {
 2400: 									long pos = ftell(file_handle.handle.fp);
 2401: 									fseek(file_handle.handle.fp, pos - 1, SEEK_SET);
 2402: 								}
 2403: 							}
 2404: 							CG(start_lineno) = 2;
 2405: 						} else {
 2406: 							rewind(file_handle.handle.fp);
 2407: 						}
 2408: 						break;
 2409: 					case ZEND_HANDLE_STREAM:
 2410: 						c = php_stream_getc((php_stream*)file_handle.handle.stream.handle);
 2411: 						if (c == '#') {
 2412: 							while (c != '\n' && c != '\r' && c != EOF) {
 2413: 								c = php_stream_getc((php_stream*)file_handle.handle.stream.handle);	/* skip to end of line */
 2414: 							}
 2415: 							/* handle situations where line is terminated by \r\n */
 2416: 							if (c == '\r') {
 2417: 								if (php_stream_getc((php_stream*)file_handle.handle.stream.handle) != '\n') {
 2418: 									long pos = php_stream_tell((php_stream*)file_handle.handle.stream.handle);
 2419: 									php_stream_seek((php_stream*)file_handle.handle.stream.handle, pos - 1, SEEK_SET);
 2420: 								}
 2421: 							}
 2422: 							CG(start_lineno) = 2;
 2423: 						} else {
 2424: 							php_stream_rewind((php_stream*)file_handle.handle.stream.handle);
 2425: 						}
 2426: 						break;
 2427: 					case ZEND_HANDLE_MAPPED:
 2428: 						if (file_handle.handle.stream.mmap.buf[0] == '#') {
 2429: 						    int i = 1;
 2430: 
 2431: 						    c = file_handle.handle.stream.mmap.buf[i++];
 2432: 							while (c != '\n' && c != '\r' && c != EOF) {
 2433: 								c = file_handle.handle.stream.mmap.buf[i++];
 2434: 							}
 2435: 							if (c == '\r') {
 2436: 								if (file_handle.handle.stream.mmap.buf[i] == '\n') {
 2437: 									i++;
 2438: 								}
 2439: 							}
 2440: 							file_handle.handle.stream.mmap.buf += i;
 2441: 							file_handle.handle.stream.mmap.len -= i;
 2442: 						}
 2443: 						break;
 2444: 					default:
 2445: 						break;
 2446: 				}
 2447: 			}
 2448: 
 2449: 			switch (behavior) {
 2450: 				case PHP_MODE_STANDARD:
 2451: 					php_execute_script(&file_handle TSRMLS_CC);
 2452: 					break;
 2453: 				case PHP_MODE_LINT:
 2454: 					PG(during_request_startup) = 0;
 2455: 					exit_status = php_lint_script(&file_handle TSRMLS_CC);
 2456: 					if (exit_status == SUCCESS) {
 2457: 						zend_printf("No syntax errors detected in %s\n", file_handle.filename);
 2458: 					} else {
 2459: 						zend_printf("Errors parsing %s\n", file_handle.filename);
 2460: 					}
 2461: 					break;
 2462: 				case PHP_MODE_STRIP:
 2463: 					if (open_file_for_scanning(&file_handle TSRMLS_CC) == SUCCESS) {
 2464: 						zend_strip(TSRMLS_C);
 2465: 						zend_file_handle_dtor(&file_handle TSRMLS_CC);
 2466: 						php_output_teardown();
 2467: 					}
 2468: 					return SUCCESS;
 2469: 					break;
 2470: 				case PHP_MODE_HIGHLIGHT:
 2471: 					{
 2472: 						zend_syntax_highlighter_ini syntax_highlighter_ini;
 2473: 
 2474: 						if (open_file_for_scanning(&file_handle TSRMLS_CC) == SUCCESS) {
 2475: 							php_get_highlight_struct(&syntax_highlighter_ini);
 2476: 							zend_highlight(&syntax_highlighter_ini TSRMLS_CC);
 2477: 							if (fastcgi) {
 2478: 								goto fastcgi_request_done;
 2479: 							}
 2480: 							zend_file_handle_dtor(&file_handle TSRMLS_CC);
 2481: 							php_output_teardown();
 2482: 						}
 2483: 						return SUCCESS;
 2484: 					}
 2485: 					break;
 2486: #if 0
 2487: 				/* Zeev might want to do something with this one day */
 2488: 				case PHP_MODE_INDENT:
 2489: 					open_file_for_scanning(&file_handle TSRMLS_CC);
 2490: 					zend_indent();
 2491: 					zend_file_handle_dtor(&file_handle TSRMLS_CC);
 2492: 					php_output_teardown();
 2493: 					return SUCCESS;
 2494: 					break;
 2495: #endif
 2496: 			}
 2497: 
 2498: fastcgi_request_done:
 2499: 			{
 2500: 				STR_FREE(SG(request_info).path_translated);
 2501: 
 2502: 				php_request_shutdown((void *) 0);
 2503: 
 2504: 				if (exit_status == 0) {
 2505: 					exit_status = EG(exit_status);
 2506: 				}
 2507: 
 2508: 				if (free_query_string && SG(request_info).query_string) {
 2509: 					free(SG(request_info).query_string);
 2510: 					SG(request_info).query_string = NULL;
 2511: 				}
 2512: 			}
 2513: 
 2514: 			if (!fastcgi) {
 2515: 				if (benchmark) {
 2516: 					repeats--;
 2517: 					if (repeats > 0) {
 2518: 						script_file = NULL;
 2519: 						php_optind = orig_optind;
 2520: 						php_optarg = orig_optarg;
 2521: 						continue;
 2522: 					}
 2523: 				}
 2524: 				break;
 2525: 			}
 2526: 
 2527: 			/* only fastcgi will get here */
 2528: 			requests++;
 2529: 			if (max_requests && (requests == max_requests)) {
 2530: 				fcgi_finish_request(request, 1);
 2531: 				if (bindpath) {
 2532: 					free(bindpath);
 2533: 				}
 2534: 				if (max_requests != 1) {
 2535: 					/* no need to return exit_status of the last request */
 2536: 					exit_status = 0;
 2537: 				}
 2538: 				break;
 2539: 			}
 2540: 			/* end of fastcgi loop */
 2541: 		}
 2542: 		if (request) {
 2543: 			fcgi_destroy_request(request);
 2544: 		}
 2545: 		fcgi_shutdown();
 2546: 
 2547: 		if (cgi_sapi_module.php_ini_path_override) {
 2548: 			free(cgi_sapi_module.php_ini_path_override);
 2549: 		}
 2550: 		if (cgi_sapi_module.ini_entries) {
 2551: 			free(cgi_sapi_module.ini_entries);
 2552: 		}
 2553: 	} zend_catch {
 2554: 		exit_status = 255;
 2555: 	} zend_end_try();
 2556: 
 2557: out:
 2558: 	if (benchmark) {
 2559: 		int sec;
 2560: #ifdef HAVE_GETTIMEOFDAY
 2561: 		int usec;
 2562: 
 2563: 		gettimeofday(&end, NULL);
 2564: 		sec = (int)(end.tv_sec - start.tv_sec);
 2565: 		if (end.tv_usec >= start.tv_usec) {
 2566: 			usec = (int)(end.tv_usec - start.tv_usec);
 2567: 		} else {
 2568: 			sec -= 1;
 2569: 			usec = (int)(end.tv_usec + 1000000 - start.tv_usec);
 2570: 		}
 2571: 		fprintf(stderr, "\nElapsed time: %d.%06d sec\n", sec, usec);
 2572: #else
 2573: 		time(&end);
 2574: 		sec = (int)(end - start);
 2575: 		fprintf(stderr, "\nElapsed time: %d sec\n", sec);
 2576: #endif
 2577: 	}
 2578: 
 2579: #ifndef PHP_WIN32
 2580: parent_out:
 2581: #endif
 2582: 
 2583: 	SG(server_context) = NULL;
 2584: 	php_module_shutdown(TSRMLS_C);
 2585: 	sapi_shutdown();
 2586: 
 2587: #ifdef ZTS
 2588: 	tsrm_shutdown();
 2589: #endif
 2590: 
 2591: #if defined(PHP_WIN32) && ZEND_DEBUG && 0
 2592: 	_CrtDumpMemoryLeaks();
 2593: #endif
 2594: 
 2595: 	return exit_status;
 2596: }
 2597: /* }}} */
 2598: 
 2599: /*
 2600:  * Local variables:
 2601:  * tab-width: 4
 2602:  * c-basic-offset: 4
 2603:  * End:
 2604:  * vim600: sw=4 ts=4 fdm=marker
 2605:  * vim<600: sw=4 ts=4
 2606:  */

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