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