PageRenderTime 309ms CodeModel.GetById 14ms RepoModel.GetById 0ms app.codeStats 1ms

/sapi/cgi/cgi_main.c

http://github.com/infusion/PHP
C | 2267 lines | 1713 code | 228 blank | 326 comment | 350 complexity | 14e2d2b4e9ff8ecfc059ea3c39ac6ae4 MD5 | raw file
Possible License(s): MPL-2.0-no-copyleft-exception, LGPL-2.1, BSD-3-Clause

Large files files are truncated, but you can click here to view the full file

  1. /*
  2. +----------------------------------------------------------------------+
  3. | PHP Version 5 |
  4. +----------------------------------------------------------------------+
  5. | Copyright (c) 1997-2011 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. /* $Id: cgi_main.c 306939 2011-01-01 02:19:59Z felipe $ */
  24. #include "php.h"
  25. #include "php_globals.h"
  26. #include "php_variables.h"
  27. #include "zend_modules.h"
  28. #include "SAPI.h"
  29. #include <stdio.h>
  30. #include "php.h"
  31. #ifdef PHP_WIN32
  32. # include "win32/time.h"
  33. # include "win32/signal.h"
  34. # include <process.h>
  35. #endif
  36. #if HAVE_SYS_TIME_H
  37. # include <sys/time.h>
  38. #endif
  39. #if HAVE_UNISTD_H
  40. # include <unistd.h>
  41. #endif
  42. #if HAVE_SIGNAL_H
  43. # include <signal.h>
  44. #endif
  45. #if HAVE_SETLOCALE
  46. # include <locale.h>
  47. #endif
  48. #if HAVE_SYS_TYPES_H
  49. # include <sys/types.h>
  50. #endif
  51. #if HAVE_SYS_WAIT_H
  52. # include <sys/wait.h>
  53. #endif
  54. #include "zend.h"
  55. #include "zend_extensions.h"
  56. #include "php_ini.h"
  57. #include "php_globals.h"
  58. #include "php_main.h"
  59. #include "fopen_wrappers.h"
  60. #include "ext/standard/php_standard.h"
  61. #ifdef PHP_WIN32
  62. # include <io.h>
  63. # include <fcntl.h>
  64. # include "win32/php_registry.h"
  65. #endif
  66. #ifdef __riscos__
  67. # include <unixlib/local.h>
  68. int __riscosify_control = __RISCOSIFY_STRICT_UNIX_SPECS;
  69. #endif
  70. #include "zend_compile.h"
  71. #include "zend_execute.h"
  72. #include "zend_highlight.h"
  73. #include "zend_indent.h"
  74. #include "php_getopt.h"
  75. #include "fastcgi.h"
  76. #ifndef PHP_WIN32
  77. /* XXX this will need to change later when threaded fastcgi is implemented. shane */
  78. struct sigaction act, old_term, old_quit, old_int;
  79. #endif
  80. static void (*php_php_import_environment_variables)(zval *array_ptr TSRMLS_DC);
  81. #ifndef PHP_WIN32
  82. /* these globals used for forking children on unix systems */
  83. /**
  84. * Number of child processes that will get created to service requests
  85. */
  86. static int children = 0;
  87. /**
  88. * Set to non-zero if we are the parent process
  89. */
  90. static int parent = 1;
  91. /* Did parent received exit signals SIG_TERM/SIG_INT/SIG_QUIT */
  92. static int exit_signal = 0;
  93. /* Is Parent waiting for children to exit */
  94. static int parent_waiting = 0;
  95. /**
  96. * Process group
  97. */
  98. static pid_t pgroup;
  99. #endif
  100. #define PHP_MODE_STANDARD 1
  101. #define PHP_MODE_HIGHLIGHT 2
  102. #define PHP_MODE_INDENT 3
  103. #define PHP_MODE_LINT 4
  104. #define PHP_MODE_STRIP 5
  105. static char *php_optarg = NULL;
  106. static int php_optind = 1;
  107. static zend_module_entry cgi_module_entry;
  108. static const opt_struct OPTIONS[] = {
  109. {'a', 0, "interactive"},
  110. {'b', 1, "bindpath"},
  111. {'C', 0, "no-chdir"},
  112. {'c', 1, "php-ini"},
  113. {'d', 1, "define"},
  114. {'e', 0, "profile-info"},
  115. {'f', 1, "file"},
  116. {'h', 0, "help"},
  117. {'i', 0, "info"},
  118. {'l', 0, "syntax-check"},
  119. {'m', 0, "modules"},
  120. {'n', 0, "no-php-ini"},
  121. {'q', 0, "no-header"},
  122. {'s', 0, "syntax-highlight"},
  123. {'s', 0, "syntax-highlighting"},
  124. {'w', 0, "strip"},
  125. {'?', 0, "usage"},/* help alias (both '?' and 'usage') */
  126. {'v', 0, "version"},
  127. {'z', 1, "zend-extension"},
  128. {'T', 1, "timing"},
  129. {'-', 0, NULL} /* end of args */
  130. };
  131. typedef struct _php_cgi_globals_struct {
  132. zend_bool rfc2616_headers;
  133. zend_bool nph;
  134. zend_bool check_shebang_line;
  135. zend_bool fix_pathinfo;
  136. zend_bool force_redirect;
  137. zend_bool discard_path;
  138. zend_bool fcgi_logging;
  139. char *redirect_status_env;
  140. #ifdef PHP_WIN32
  141. zend_bool impersonate;
  142. #endif
  143. HashTable user_config_cache;
  144. } php_cgi_globals_struct;
  145. /* {{{ user_config_cache
  146. *
  147. * Key for each cache entry is dirname(PATH_TRANSLATED).
  148. *
  149. * NOTE: Each cache entry config_hash contains the combination from all user ini files found in
  150. * the path starting from doc_root throught to dirname(PATH_TRANSLATED). There is no point
  151. * storing per-file entries as it would not be possible to detect added / deleted entries
  152. * between separate files.
  153. */
  154. typedef struct _user_config_cache_entry {
  155. time_t expires;
  156. HashTable *user_config;
  157. } user_config_cache_entry;
  158. static void user_config_cache_entry_dtor(user_config_cache_entry *entry)
  159. {
  160. zend_hash_destroy(entry->user_config);
  161. free(entry->user_config);
  162. }
  163. /* }}} */
  164. #ifdef ZTS
  165. static int php_cgi_globals_id;
  166. #define CGIG(v) TSRMG(php_cgi_globals_id, php_cgi_globals_struct *, v)
  167. #else
  168. static php_cgi_globals_struct php_cgi_globals;
  169. #define CGIG(v) (php_cgi_globals.v)
  170. #endif
  171. #ifdef PHP_WIN32
  172. #define TRANSLATE_SLASHES(path) \
  173. { \
  174. char *tmp = path; \
  175. while (*tmp) { \
  176. if (*tmp == '\\') *tmp = '/'; \
  177. tmp++; \
  178. } \
  179. }
  180. #else
  181. #define TRANSLATE_SLASHES(path)
  182. #endif
  183. static int print_module_info(zend_module_entry *module, void *arg TSRMLS_DC)
  184. {
  185. php_printf("%s\n", module->name);
  186. return 0;
  187. }
  188. static int module_name_cmp(const void *a, const void *b TSRMLS_DC)
  189. {
  190. Bucket *f = *((Bucket **) a);
  191. Bucket *s = *((Bucket **) b);
  192. return strcasecmp( ((zend_module_entry *)f->pData)->name,
  193. ((zend_module_entry *)s->pData)->name);
  194. }
  195. static void print_modules(TSRMLS_D)
  196. {
  197. HashTable sorted_registry;
  198. zend_module_entry tmp;
  199. zend_hash_init(&sorted_registry, 50, NULL, NULL, 1);
  200. zend_hash_copy(&sorted_registry, &module_registry, NULL, &tmp, sizeof(zend_module_entry));
  201. zend_hash_sort(&sorted_registry, zend_qsort, module_name_cmp, 0 TSRMLS_CC);
  202. zend_hash_apply_with_argument(&sorted_registry, (apply_func_arg_t) print_module_info, NULL TSRMLS_CC);
  203. zend_hash_destroy(&sorted_registry);
  204. }
  205. static int print_extension_info(zend_extension *ext, void *arg TSRMLS_DC)
  206. {
  207. php_printf("%s\n", ext->name);
  208. return 0;
  209. }
  210. static int extension_name_cmp(const zend_llist_element **f, const zend_llist_element **s TSRMLS_DC)
  211. {
  212. return strcmp( ((zend_extension *)(*f)->data)->name,
  213. ((zend_extension *)(*s)->data)->name);
  214. }
  215. static void print_extensions(TSRMLS_D)
  216. {
  217. zend_llist sorted_exts;
  218. zend_llist_copy(&sorted_exts, &zend_extensions);
  219. sorted_exts.dtor = NULL;
  220. zend_llist_sort(&sorted_exts, extension_name_cmp TSRMLS_CC);
  221. zend_llist_apply_with_argument(&sorted_exts, (llist_apply_with_arg_func_t) print_extension_info, NULL TSRMLS_CC);
  222. zend_llist_destroy(&sorted_exts);
  223. }
  224. #ifndef STDOUT_FILENO
  225. #define STDOUT_FILENO 1
  226. #endif
  227. static inline size_t sapi_cgibin_single_write(const char *str, uint str_length TSRMLS_DC)
  228. {
  229. #ifdef PHP_WRITE_STDOUT
  230. long ret;
  231. #else
  232. size_t ret;
  233. #endif
  234. if (fcgi_is_fastcgi()) {
  235. fcgi_request *request = (fcgi_request*) SG(server_context);
  236. long ret = fcgi_write(request, FCGI_STDOUT, str, str_length);
  237. if (ret <= 0) {
  238. return 0;
  239. }
  240. return ret;
  241. }
  242. #ifdef PHP_WRITE_STDOUT
  243. ret = write(STDOUT_FILENO, str, str_length);
  244. if (ret <= 0) return 0;
  245. return ret;
  246. #else
  247. ret = fwrite(str, 1, MIN(str_length, 16384), stdout);
  248. return ret;
  249. #endif
  250. }
  251. static int sapi_cgibin_ub_write(const char *str, uint str_length TSRMLS_DC)
  252. {
  253. const char *ptr = str;
  254. uint remaining = str_length;
  255. size_t ret;
  256. while (remaining > 0) {
  257. ret = sapi_cgibin_single_write(ptr, remaining TSRMLS_CC);
  258. if (!ret) {
  259. php_handle_aborted_connection();
  260. return str_length - remaining;
  261. }
  262. ptr += ret;
  263. remaining -= ret;
  264. }
  265. return str_length;
  266. }
  267. static void sapi_cgibin_flush(void *server_context)
  268. {
  269. if (fcgi_is_fastcgi()) {
  270. fcgi_request *request = (fcgi_request*) server_context;
  271. if (
  272. #ifndef PHP_WIN32
  273. !parent &&
  274. #endif
  275. request && !fcgi_flush(request, 0)) {
  276. php_handle_aborted_connection();
  277. }
  278. return;
  279. }
  280. if (fflush(stdout) == EOF) {
  281. php_handle_aborted_connection();
  282. }
  283. }
  284. #define SAPI_CGI_MAX_HEADER_LENGTH 1024
  285. typedef struct _http_error {
  286. int code;
  287. const char* msg;
  288. } http_error;
  289. static const http_error http_error_codes[] = {
  290. {100, "Continue"},
  291. {101, "Switching Protocols"},
  292. {200, "OK"},
  293. {201, "Created"},
  294. {202, "Accepted"},
  295. {203, "Non-Authoritative Information"},
  296. {204, "No Content"},
  297. {205, "Reset Content"},
  298. {206, "Partial Content"},
  299. {300, "Multiple Choices"},
  300. {301, "Moved Permanently"},
  301. {302, "Moved Temporarily"},
  302. {303, "See Other"},
  303. {304, "Not Modified"},
  304. {305, "Use Proxy"},
  305. {400, "Bad Request"},
  306. {401, "Unauthorized"},
  307. {402, "Payment Required"},
  308. {403, "Forbidden"},
  309. {404, "Not Found"},
  310. {405, "Method Not Allowed"},
  311. {406, "Not Acceptable"},
  312. {407, "Proxy Authentication Required"},
  313. {408, "Request Time-out"},
  314. {409, "Conflict"},
  315. {410, "Gone"},
  316. {411, "Length Required"},
  317. {412, "Precondition Failed"},
  318. {413, "Request Entity Too Large"},
  319. {414, "Request-URI Too Large"},
  320. {415, "Unsupported Media Type"},
  321. {500, "Internal Server Error"},
  322. {501, "Not Implemented"},
  323. {502, "Bad Gateway"},
  324. {503, "Service Unavailable"},
  325. {504, "Gateway Time-out"},
  326. {505, "HTTP Version not supported"},
  327. {0, NULL}
  328. };
  329. static int sapi_cgi_send_headers(sapi_headers_struct *sapi_headers TSRMLS_DC)
  330. {
  331. char buf[SAPI_CGI_MAX_HEADER_LENGTH];
  332. sapi_header_struct *h;
  333. zend_llist_position pos;
  334. zend_bool ignore_status = 0;
  335. int response_status = SG(sapi_headers).http_response_code;
  336. if (SG(request_info).no_headers == 1) {
  337. return SAPI_HEADER_SENT_SUCCESSFULLY;
  338. }
  339. if (CGIG(nph) || SG(sapi_headers).http_response_code != 200)
  340. {
  341. int len;
  342. zend_bool has_status = 0;
  343. if (CGIG(rfc2616_headers) && SG(sapi_headers).http_status_line) {
  344. char *s;
  345. len = slprintf(buf, SAPI_CGI_MAX_HEADER_LENGTH, "%s\r\n", SG(sapi_headers).http_status_line);
  346. if ((s = strchr(SG(sapi_headers).http_status_line, ' '))) {
  347. response_status = atoi((s + 1));
  348. }
  349. if (len > SAPI_CGI_MAX_HEADER_LENGTH) {
  350. len = SAPI_CGI_MAX_HEADER_LENGTH;
  351. }
  352. } else {
  353. char *s;
  354. if (SG(sapi_headers).http_status_line &&
  355. (s = strchr(SG(sapi_headers).http_status_line, ' ')) != 0 &&
  356. (s - SG(sapi_headers).http_status_line) >= 5 &&
  357. strncasecmp(SG(sapi_headers).http_status_line, "HTTP/", 5) == 0
  358. ) {
  359. len = slprintf(buf, sizeof(buf), "Status:%s\r\n", s);
  360. response_status = atoi((s + 1));
  361. } else {
  362. h = (sapi_header_struct*)zend_llist_get_first_ex(&sapi_headers->headers, &pos);
  363. while (h) {
  364. if (h->header_len > sizeof("Status:")-1 &&
  365. strncasecmp(h->header, "Status:", sizeof("Status:")-1) == 0
  366. ) {
  367. has_status = 1;
  368. break;
  369. }
  370. h = (sapi_header_struct*)zend_llist_get_next_ex(&sapi_headers->headers, &pos);
  371. }
  372. if (!has_status) {
  373. http_error *err = (http_error*)http_error_codes;
  374. while (err->code != 0) {
  375. if (err->code == SG(sapi_headers).http_response_code) {
  376. break;
  377. }
  378. err++;
  379. }
  380. if (err->msg) {
  381. len = slprintf(buf, sizeof(buf), "Status: %d %s\r\n", SG(sapi_headers).http_response_code, err->msg);
  382. } else {
  383. len = slprintf(buf, sizeof(buf), "Status: %d\r\n", SG(sapi_headers).http_response_code);
  384. }
  385. }
  386. }
  387. }
  388. if (!has_status) {
  389. PHPWRITE_H(buf, len);
  390. ignore_status = 1;
  391. }
  392. }
  393. h = (sapi_header_struct*)zend_llist_get_first_ex(&sapi_headers->headers, &pos);
  394. while (h) {
  395. /* prevent CRLFCRLF */
  396. if (h->header_len) {
  397. if (h->header_len > sizeof("Status:")-1 &&
  398. strncasecmp(h->header, "Status:", sizeof("Status:")-1) == 0
  399. ) {
  400. if (!ignore_status) {
  401. ignore_status = 1;
  402. PHPWRITE_H(h->header, h->header_len);
  403. PHPWRITE_H("\r\n", 2);
  404. }
  405. } else if (response_status == 304 && h->header_len > sizeof("Content-Type:")-1 &&
  406. strncasecmp(h->header, "Content-Type:", sizeof("Content-Type:")-1) == 0
  407. ) {
  408. h = (sapi_header_struct*)zend_llist_get_next_ex(&sapi_headers->headers, &pos);
  409. continue;
  410. } else {
  411. PHPWRITE_H(h->header, h->header_len);
  412. PHPWRITE_H("\r\n", 2);
  413. }
  414. }
  415. h = (sapi_header_struct*)zend_llist_get_next_ex(&sapi_headers->headers, &pos);
  416. }
  417. PHPWRITE_H("\r\n", 2);
  418. return SAPI_HEADER_SENT_SUCCESSFULLY;
  419. }
  420. #ifndef STDIN_FILENO
  421. # define STDIN_FILENO 0
  422. #endif
  423. static int sapi_cgi_read_post(char *buffer, uint count_bytes TSRMLS_DC)
  424. {
  425. uint read_bytes = 0;
  426. int tmp_read_bytes;
  427. count_bytes = MIN(count_bytes, (uint) SG(request_info).content_length - SG(read_post_bytes));
  428. while (read_bytes < count_bytes) {
  429. if (fcgi_is_fastcgi()) {
  430. fcgi_request *request = (fcgi_request*) SG(server_context);
  431. tmp_read_bytes = fcgi_read(request, buffer + read_bytes, count_bytes - read_bytes);
  432. } else {
  433. tmp_read_bytes = read(STDIN_FILENO, buffer + read_bytes, count_bytes - read_bytes);
  434. }
  435. if (tmp_read_bytes <= 0) {
  436. break;
  437. }
  438. read_bytes += tmp_read_bytes;
  439. }
  440. return read_bytes;
  441. }
  442. static char *sapi_cgibin_getenv(char *name, size_t name_len TSRMLS_DC)
  443. {
  444. /* when php is started by mod_fastcgi, no regular environment
  445. * is provided to PHP. It is always sent to PHP at the start
  446. * of a request. So we have to do our own lookup to get env
  447. * vars. This could probably be faster somehow. */
  448. if (fcgi_is_fastcgi()) {
  449. fcgi_request *request = (fcgi_request*) SG(server_context);
  450. return fcgi_getenv(request, name, name_len);
  451. }
  452. /* if cgi, or fastcgi and not found in fcgi env
  453. check the regular environment */
  454. return getenv(name);
  455. }
  456. static char *_sapi_cgibin_putenv(char *name, char *value TSRMLS_DC)
  457. {
  458. int name_len;
  459. #if !HAVE_SETENV || !HAVE_UNSETENV
  460. int len;
  461. char *buf;
  462. #endif
  463. if (!name) {
  464. return NULL;
  465. }
  466. name_len = strlen(name);
  467. /* when php is started by mod_fastcgi, no regular environment
  468. * is provided to PHP. It is always sent to PHP at the start
  469. * of a request. So we have to do our own lookup to get env
  470. * vars. This could probably be faster somehow. */
  471. if (fcgi_is_fastcgi()) {
  472. fcgi_request *request = (fcgi_request*) SG(server_context);
  473. return fcgi_putenv(request, name, name_len, value);
  474. }
  475. #if HAVE_SETENV
  476. if (value) {
  477. setenv(name, value, 1);
  478. }
  479. #endif
  480. #if HAVE_UNSETENV
  481. if (!value) {
  482. unsetenv(name);
  483. }
  484. #endif
  485. #if !HAVE_SETENV || !HAVE_UNSETENV
  486. /* if cgi, or fastcgi and not found in fcgi env
  487. check the regular environment
  488. this leaks, but it's only cgi anyway, we'll fix
  489. it for 5.0
  490. */
  491. len = name_len + (value ? strlen(value) : 0) + sizeof("=") + 2;
  492. buf = (char *) malloc(len);
  493. if (buf == NULL) {
  494. return getenv(name);
  495. }
  496. #endif
  497. #if !HAVE_SETENV
  498. if (value) {
  499. len = slprintf(buf, len - 1, "%s=%s", name, value);
  500. putenv(buf);
  501. }
  502. #endif
  503. #if !HAVE_UNSETENV
  504. if (!value) {
  505. len = slprintf(buf, len - 1, "%s=", name);
  506. putenv(buf);
  507. }
  508. #endif
  509. return getenv(name);
  510. }
  511. static char *sapi_cgi_read_cookies(TSRMLS_D)
  512. {
  513. return sapi_cgibin_getenv((char *) "HTTP_COOKIE", sizeof("HTTP_COOKIE")-1 TSRMLS_CC);
  514. }
  515. void cgi_php_import_environment_variables(zval *array_ptr TSRMLS_DC)
  516. {
  517. if (PG(http_globals)[TRACK_VARS_ENV] &&
  518. array_ptr != PG(http_globals)[TRACK_VARS_ENV] &&
  519. Z_TYPE_P(PG(http_globals)[TRACK_VARS_ENV]) == IS_ARRAY &&
  520. zend_hash_num_elements(Z_ARRVAL_P(PG(http_globals)[TRACK_VARS_ENV])) > 0
  521. ) {
  522. zval_dtor(array_ptr);
  523. *array_ptr = *PG(http_globals)[TRACK_VARS_ENV];
  524. INIT_PZVAL(array_ptr);
  525. zval_copy_ctor(array_ptr);
  526. return;
  527. } else if (PG(http_globals)[TRACK_VARS_SERVER] &&
  528. array_ptr != PG(http_globals)[TRACK_VARS_SERVER] &&
  529. Z_TYPE_P(PG(http_globals)[TRACK_VARS_SERVER]) == IS_ARRAY &&
  530. zend_hash_num_elements(Z_ARRVAL_P(PG(http_globals)[TRACK_VARS_SERVER])) > 0
  531. ) {
  532. zval_dtor(array_ptr);
  533. *array_ptr = *PG(http_globals)[TRACK_VARS_SERVER];
  534. INIT_PZVAL(array_ptr);
  535. zval_copy_ctor(array_ptr);
  536. return;
  537. }
  538. /* call php's original import as a catch-all */
  539. php_php_import_environment_variables(array_ptr TSRMLS_CC);
  540. if (fcgi_is_fastcgi()) {
  541. fcgi_request *request = (fcgi_request*) SG(server_context);
  542. HashPosition pos;
  543. char *var, **val;
  544. uint var_len;
  545. ulong idx;
  546. int filter_arg = (array_ptr == PG(http_globals)[TRACK_VARS_ENV])?PARSE_ENV:PARSE_SERVER;
  547. for (zend_hash_internal_pointer_reset_ex(request->env, &pos);
  548. zend_hash_get_current_key_ex(request->env, &var, &var_len, &idx, 0, &pos) == HASH_KEY_IS_STRING &&
  549. zend_hash_get_current_data_ex(request->env, (void **) &val, &pos) == SUCCESS;
  550. zend_hash_move_forward_ex(request->env, &pos)
  551. ) {
  552. unsigned int new_val_len;
  553. if (sapi_module.input_filter(filter_arg, var, val, strlen(*val), &new_val_len TSRMLS_CC)) {
  554. php_register_variable_safe(var, *val, new_val_len, array_ptr TSRMLS_CC);
  555. }
  556. }
  557. }
  558. }
  559. static void sapi_cgi_register_variables(zval *track_vars_array TSRMLS_DC)
  560. {
  561. unsigned int php_self_len;
  562. char *php_self;
  563. /* In CGI mode, we consider the environment to be a part of the server
  564. * variables
  565. */
  566. php_import_environment_variables(track_vars_array TSRMLS_CC);
  567. if (CGIG(fix_pathinfo)) {
  568. char *script_name = SG(request_info).request_uri;
  569. unsigned int script_name_len = script_name ? strlen(script_name) : 0;
  570. char *path_info = sapi_cgibin_getenv("PATH_INFO", sizeof("PATH_INFO")-1 TSRMLS_CC);
  571. unsigned int path_info_len = path_info ? strlen(path_info) : 0;
  572. php_self_len = script_name_len + path_info_len;
  573. php_self = emalloc(php_self_len + 1);
  574. if (script_name) {
  575. memcpy(php_self, script_name, script_name_len + 1);
  576. }
  577. if (path_info) {
  578. memcpy(php_self + script_name_len, path_info, path_info_len + 1);
  579. }
  580. /* Build the special-case PHP_SELF variable for the CGI version */
  581. if (sapi_module.input_filter(PARSE_SERVER, "PHP_SELF", &php_self, php_self_len, &php_self_len TSRMLS_CC)) {
  582. php_register_variable_safe("PHP_SELF", php_self, php_self_len, track_vars_array TSRMLS_CC);
  583. }
  584. efree(php_self);
  585. } else {
  586. php_self = SG(request_info).request_uri ? SG(request_info).request_uri : "";
  587. php_self_len = strlen(php_self);
  588. if (sapi_module.input_filter(PARSE_SERVER, "PHP_SELF", &php_self, php_self_len, &php_self_len TSRMLS_CC)) {
  589. php_register_variable_safe("PHP_SELF", php_self, php_self_len, track_vars_array TSRMLS_CC);
  590. }
  591. }
  592. }
  593. static void sapi_cgi_log_message(char *message)
  594. {
  595. TSRMLS_FETCH();
  596. if (fcgi_is_fastcgi() && CGIG(fcgi_logging)) {
  597. fcgi_request *request;
  598. request = (fcgi_request*) SG(server_context);
  599. if (request) {
  600. int len = strlen(message);
  601. char *buf = malloc(len+2);
  602. memcpy(buf, message, len);
  603. memcpy(buf + len, "\n", sizeof("\n"));
  604. fcgi_write(request, FCGI_STDERR, buf, len+1);
  605. free(buf);
  606. } else {
  607. fprintf(stderr, "%s\n", message);
  608. }
  609. /* ignore return code */
  610. } else {
  611. fprintf(stderr, "%s\n", message);
  612. }
  613. }
  614. static time_t sapi_cgi_request_time(TSRMLS_D)
  615. {
  616. char *rq_time = sapi_cgibin_getenv("RAW_TIME", sizeof("RAW_TIME") - 1 TSRMLS_CC);
  617. if (rq_time) {
  618. return atol(rq_time);
  619. } else {
  620. return 0;
  621. }
  622. }
  623. /* {{{ php_cgi_ini_activate_user_config
  624. */
  625. 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)
  626. {
  627. char *ptr;
  628. user_config_cache_entry *new_entry, *entry;
  629. time_t request_time = sapi_get_request_time(TSRMLS_C);
  630. /* Find cached config entry: If not found, create one */
  631. if (zend_hash_find(&CGIG(user_config_cache), path, path_len + 1, (void **) &entry) == FAILURE) {
  632. new_entry = pemalloc(sizeof(user_config_cache_entry), 1);
  633. new_entry->expires = 0;
  634. new_entry->user_config = (HashTable *) pemalloc(sizeof(HashTable), 1);
  635. zend_hash_init(new_entry->user_config, 0, NULL, (dtor_func_t) config_zval_dtor, 1);
  636. zend_hash_update(&CGIG(user_config_cache), path, path_len + 1, new_entry, sizeof(user_config_cache_entry), (void **) &entry);
  637. free(new_entry);
  638. }
  639. /* Check whether cache entry has expired and rescan if it is */
  640. if (request_time > entry->expires) {
  641. char * real_path;
  642. int real_path_len;
  643. char *s1, *s2;
  644. int s_len;
  645. /* Clear the expired config */
  646. zend_hash_clean(entry->user_config);
  647. if (!IS_ABSOLUTE_PATH(path, path_len)) {
  648. real_path = tsrm_realpath(path, NULL TSRMLS_CC);
  649. /* see #51688, looks like we may get invalid path as doc root using cgi with apache */
  650. if (real_path == NULL) {
  651. return;
  652. }
  653. real_path_len = strlen(real_path);
  654. path = real_path;
  655. path_len = real_path_len;
  656. }
  657. if (path_len > doc_root_len) {
  658. s1 = (char *) doc_root;
  659. s2 = path;
  660. s_len = doc_root_len;
  661. } else {
  662. s1 = path;
  663. s2 = (char *) doc_root;
  664. s_len = path_len;
  665. }
  666. /* we have to test if path is part of DOCUMENT_ROOT.
  667. if it is inside the docroot, we scan the tree up to the docroot
  668. to find more user.ini, if not we only scan the current path.
  669. */
  670. #ifdef PHP_WIN32
  671. if (strnicmp(s1, s2, s_len) == 0) {
  672. #else
  673. if (strncmp(s1, s2, s_len) == 0) {
  674. #endif
  675. ptr = s2 + start; /* start is the point where doc_root ends! */
  676. while ((ptr = strchr(ptr, DEFAULT_SLASH)) != NULL) {
  677. *ptr = 0;
  678. php_parse_user_ini_file(path, PG(user_ini_filename), entry->user_config TSRMLS_CC);
  679. *ptr = '/';
  680. ptr++;
  681. }
  682. } else {
  683. php_parse_user_ini_file(path, PG(user_ini_filename), entry->user_config TSRMLS_CC);
  684. }
  685. entry->expires = request_time + PG(user_ini_cache_ttl);
  686. }
  687. /* Activate ini entries with values from the user config hash */
  688. php_ini_activate_config(entry->user_config, PHP_INI_PERDIR, PHP_INI_STAGE_HTACCESS TSRMLS_CC);
  689. }
  690. /* }}} */
  691. static int sapi_cgi_activate(TSRMLS_D)
  692. {
  693. char *path, *doc_root, *server_name;
  694. uint path_len, doc_root_len, server_name_len;
  695. /* PATH_TRANSLATED should be defined at this stage but better safe than sorry :) */
  696. if (!SG(request_info).path_translated) {
  697. return FAILURE;
  698. }
  699. if (php_ini_has_per_host_config()) {
  700. /* Activate per-host-system-configuration defined in php.ini and stored into configuration_hash during startup */
  701. server_name = sapi_cgibin_getenv("SERVER_NAME", sizeof("SERVER_NAME") - 1 TSRMLS_CC);
  702. /* SERVER_NAME should also be defined at this stage..but better check it anyway */
  703. if (server_name) {
  704. server_name_len = strlen(server_name);
  705. server_name = estrndup(server_name, server_name_len);
  706. zend_str_tolower(server_name, server_name_len);
  707. php_ini_activate_per_host_config(server_name, server_name_len + 1 TSRMLS_CC);
  708. efree(server_name);
  709. }
  710. }
  711. if (php_ini_has_per_dir_config() ||
  712. (PG(user_ini_filename) && *PG(user_ini_filename))
  713. ) {
  714. /* Prepare search path */
  715. path_len = strlen(SG(request_info).path_translated);
  716. /* Make sure we have trailing slash! */
  717. if (!IS_SLASH(SG(request_info).path_translated[path_len])) {
  718. path = emalloc(path_len + 2);
  719. memcpy(path, SG(request_info).path_translated, path_len + 1);
  720. path_len = zend_dirname(path, path_len);
  721. path[path_len++] = DEFAULT_SLASH;
  722. } else {
  723. path = estrndup(SG(request_info).path_translated, path_len);
  724. path_len = zend_dirname(path, path_len);
  725. }
  726. path[path_len] = 0;
  727. /* Activate per-dir-system-configuration defined in php.ini and stored into configuration_hash during startup */
  728. php_ini_activate_per_dir_config(path, path_len TSRMLS_CC); /* Note: for global settings sake we check from root to path */
  729. /* Load and activate user ini files in path starting from DOCUMENT_ROOT */
  730. if (PG(user_ini_filename) && *PG(user_ini_filename)) {
  731. doc_root = sapi_cgibin_getenv("DOCUMENT_ROOT", sizeof("DOCUMENT_ROOT") - 1 TSRMLS_CC);
  732. /* DOCUMENT_ROOT should also be defined at this stage..but better check it anyway */
  733. if (doc_root) {
  734. doc_root_len = strlen(doc_root);
  735. if (doc_root_len > 0 && IS_SLASH(doc_root[doc_root_len - 1])) {
  736. --doc_root_len;
  737. }
  738. #ifdef PHP_WIN32
  739. /* paths on windows should be case-insensitive */
  740. doc_root = estrndup(doc_root, doc_root_len);
  741. zend_str_tolower(doc_root, doc_root_len);
  742. #endif
  743. php_cgi_ini_activate_user_config(path, path_len, doc_root, doc_root_len, doc_root_len - 1 TSRMLS_CC);
  744. }
  745. }
  746. #ifdef PHP_WIN32
  747. efree(doc_root);
  748. #endif
  749. efree(path);
  750. }
  751. return SUCCESS;
  752. }
  753. static int sapi_cgi_deactivate(TSRMLS_D)
  754. {
  755. /* flush only when SAPI was started. The reasons are:
  756. 1. SAPI Deactivate is called from two places: module init and request shutdown
  757. 2. When the first call occurs and the request is not set up, flush fails on FastCGI.
  758. */
  759. if (SG(sapi_started)) {
  760. if (fcgi_is_fastcgi()) {
  761. if (
  762. #ifndef PHP_WIN32
  763. !parent &&
  764. #endif
  765. !fcgi_finish_request((fcgi_request*)SG(server_context), 0)) {
  766. php_handle_aborted_connection();
  767. }
  768. } else {
  769. sapi_cgibin_flush(SG(server_context));
  770. }
  771. }
  772. return SUCCESS;
  773. }
  774. static int php_cgi_startup(sapi_module_struct *sapi_module)
  775. {
  776. if (php_module_startup(sapi_module, &cgi_module_entry, 1) == FAILURE) {
  777. return FAILURE;
  778. }
  779. return SUCCESS;
  780. }
  781. /* {{{ sapi_module_struct cgi_sapi_module
  782. */
  783. static sapi_module_struct cgi_sapi_module = {
  784. "cgi-fcgi", /* name */
  785. "CGI/FastCGI", /* pretty name */
  786. php_cgi_startup, /* startup */
  787. php_module_shutdown_wrapper, /* shutdown */
  788. sapi_cgi_activate, /* activate */
  789. sapi_cgi_deactivate, /* deactivate */
  790. sapi_cgibin_ub_write, /* unbuffered write */
  791. sapi_cgibin_flush, /* flush */
  792. NULL, /* get uid */
  793. sapi_cgibin_getenv, /* getenv */
  794. php_error, /* error handler */
  795. NULL, /* header handler */
  796. sapi_cgi_send_headers, /* send headers handler */
  797. NULL, /* send header handler */
  798. sapi_cgi_read_post, /* read POST data */
  799. sapi_cgi_read_cookies, /* read Cookies */
  800. sapi_cgi_register_variables, /* register server variables */
  801. sapi_cgi_log_message, /* Log message */
  802. sapi_cgi_request_time, /* Get request time */
  803. NULL, /* Child terminate */
  804. STANDARD_SAPI_MODULE_PROPERTIES
  805. };
  806. /* }}} */
  807. /* {{{ arginfo ext/standard/dl.c */
  808. ZEND_BEGIN_ARG_INFO(arginfo_dl, 0)
  809. ZEND_ARG_INFO(0, extension_filename)
  810. ZEND_END_ARG_INFO()
  811. /* }}} */
  812. static const zend_function_entry additional_functions[] = {
  813. ZEND_FE(dl, arginfo_dl)
  814. {NULL, NULL, NULL}
  815. };
  816. /* {{{ php_cgi_usage
  817. */
  818. static void php_cgi_usage(char *argv0)
  819. {
  820. char *prog;
  821. prog = strrchr(argv0, '/');
  822. if (prog) {
  823. prog++;
  824. } else {
  825. prog = "php";
  826. }
  827. php_printf( "Usage: %s [-q] [-h] [-s] [-v] [-i] [-f <file>]\n"
  828. " %s <file> [args...]\n"
  829. " -a Run interactively\n"
  830. " -b <address:port>|<port> Bind Path for external FASTCGI Server mode\n"
  831. " -C Do not chdir to the script's directory\n"
  832. " -c <path>|<file> Look for php.ini file in this directory\n"
  833. " -n No php.ini file will be used\n"
  834. " -d foo[=bar] Define INI entry foo with value 'bar'\n"
  835. " -e Generate extended information for debugger/profiler\n"
  836. " -f <file> Parse <file>. Implies `-q'\n"
  837. " -h This help\n"
  838. " -i PHP information\n"
  839. " -l Syntax check only (lint)\n"
  840. " -m Show compiled in modules\n"
  841. " -q Quiet-mode. Suppress HTTP Header output.\n"
  842. " -s Display colour syntax highlighted source.\n"
  843. " -v Version number\n"
  844. " -w Display source with stripped comments and whitespace.\n"
  845. " -z <file> Load Zend extension <file>.\n"
  846. " -T <count> Measure execution time of script repeated <count> times.\n",
  847. prog, prog);
  848. }
  849. /* }}} */
  850. /* {{{ is_valid_path
  851. *
  852. * some server configurations allow '..' to slip through in the
  853. * translated path. We'll just refuse to handle such a path.
  854. */
  855. static int is_valid_path(const char *path)
  856. {
  857. const char *p;
  858. if (!path) {
  859. return 0;
  860. }
  861. p = strstr(path, "..");
  862. if (p) {
  863. if ((p == path || IS_SLASH(*(p-1))) &&
  864. (*(p+2) == 0 || IS_SLASH(*(p+2)))
  865. ) {
  866. return 0;
  867. }
  868. while (1) {
  869. p = strstr(p+1, "..");
  870. if (!p) {
  871. break;
  872. }
  873. if (IS_SLASH(*(p-1)) &&
  874. (*(p+2) == 0 || IS_SLASH(*(p+2)))
  875. ) {
  876. return 0;
  877. }
  878. }
  879. }
  880. return 1;
  881. }
  882. /* }}} */
  883. /* {{{ init_request_info
  884. initializes request_info structure
  885. specificly in this section we handle proper translations
  886. for:
  887. PATH_INFO
  888. derived from the portion of the URI path following
  889. the script name but preceding any query data
  890. may be empty
  891. PATH_TRANSLATED
  892. derived by taking any path-info component of the
  893. request URI and performing any virtual-to-physical
  894. translation appropriate to map it onto the server's
  895. document repository structure
  896. empty if PATH_INFO is empty
  897. The env var PATH_TRANSLATED **IS DIFFERENT** than the
  898. request_info.path_translated variable, the latter should
  899. match SCRIPT_FILENAME instead.
  900. SCRIPT_NAME
  901. set to a URL path that could identify the CGI script
  902. rather than the interpreter. PHP_SELF is set to this
  903. REQUEST_URI
  904. uri section following the domain:port part of a URI
  905. SCRIPT_FILENAME
  906. The virtual-to-physical translation of SCRIPT_NAME (as per
  907. PATH_TRANSLATED)
  908. These settings are documented at
  909. http://cgi-spec.golux.com/
  910. Based on the following URL request:
  911. http://localhost/info.php/test?a=b
  912. should produce, which btw is the same as if
  913. we were running under mod_cgi on apache (ie. not
  914. using ScriptAlias directives):
  915. PATH_INFO=/test
  916. PATH_TRANSLATED=/docroot/test
  917. SCRIPT_NAME=/info.php
  918. REQUEST_URI=/info.php/test?a=b
  919. SCRIPT_FILENAME=/docroot/info.php
  920. QUERY_STRING=a=b
  921. but what we get is (cgi/mod_fastcgi under apache):
  922. PATH_INFO=/info.php/test
  923. PATH_TRANSLATED=/docroot/info.php/test
  924. SCRIPT_NAME=/php/php-cgi (from the Action setting I suppose)
  925. REQUEST_URI=/info.php/test?a=b
  926. SCRIPT_FILENAME=/path/to/php/bin/php-cgi (Action setting translated)
  927. QUERY_STRING=a=b
  928. Comments in the code below refer to using the above URL in a request
  929. */
  930. static void init_request_info(TSRMLS_D)
  931. {
  932. char *env_script_filename = sapi_cgibin_getenv("SCRIPT_FILENAME", sizeof("SCRIPT_FILENAME")-1 TSRMLS_CC);
  933. char *env_path_translated = sapi_cgibin_getenv("PATH_TRANSLATED", sizeof("PATH_TRANSLATED")-1 TSRMLS_CC);
  934. char *script_path_translated = env_script_filename;
  935. /* some broken servers do not have script_filename or argv0
  936. * an example, IIS configured in some ways. then they do more
  937. * broken stuff and set path_translated to the cgi script location */
  938. if (!script_path_translated && env_path_translated) {
  939. script_path_translated = env_path_translated;
  940. }
  941. /* initialize the defaults */
  942. SG(request_info).path_translated = NULL;
  943. SG(request_info).request_method = NULL;
  944. SG(request_info).proto_num = 1000;
  945. SG(request_info).query_string = NULL;
  946. SG(request_info).request_uri = NULL;
  947. SG(request_info).content_type = NULL;
  948. SG(request_info).content_length = 0;
  949. SG(sapi_headers).http_response_code = 200;
  950. /* script_path_translated being set is a good indication that
  951. * we are running in a cgi environment, since it is always
  952. * null otherwise. otherwise, the filename
  953. * of the script will be retreived later via argc/argv */
  954. if (script_path_translated) {
  955. const char *auth;
  956. char *content_length = sapi_cgibin_getenv("CONTENT_LENGTH", sizeof("CONTENT_LENGTH")-1 TSRMLS_CC);
  957. char *content_type = sapi_cgibin_getenv("CONTENT_TYPE", sizeof("CONTENT_TYPE")-1 TSRMLS_CC);
  958. char *env_path_info = sapi_cgibin_getenv("PATH_INFO", sizeof("PATH_INFO")-1 TSRMLS_CC);
  959. char *env_script_name = sapi_cgibin_getenv("SCRIPT_NAME", sizeof("SCRIPT_NAME")-1 TSRMLS_CC);
  960. /* Hack for buggy IIS that sets incorrect PATH_INFO */
  961. char *env_server_software = sapi_cgibin_getenv("SERVER_SOFTWARE", sizeof("SERVER_SOFTWARE")-1 TSRMLS_CC);
  962. if (env_server_software &&
  963. env_script_name &&
  964. env_path_info &&
  965. strncmp(env_server_software, "Microsoft-IIS", sizeof("Microsoft-IIS")-1) == 0 &&
  966. strncmp(env_path_info, env_script_name, strlen(env_script_name)) == 0
  967. ) {
  968. env_path_info = _sapi_cgibin_putenv("ORIG_PATH_INFO", env_path_info TSRMLS_CC);
  969. env_path_info += strlen(env_script_name);
  970. if (*env_path_info == 0) {
  971. env_path_info = NULL;
  972. }
  973. env_path_info = _sapi_cgibin_putenv("PATH_INFO", env_path_info TSRMLS_CC);
  974. }
  975. if (CGIG(fix_pathinfo)) {
  976. struct stat st;
  977. char *real_path = NULL;
  978. char *env_redirect_url = sapi_cgibin_getenv("REDIRECT_URL", sizeof("REDIRECT_URL")-1 TSRMLS_CC);
  979. char *env_document_root = sapi_cgibin_getenv("DOCUMENT_ROOT", sizeof("DOCUMENT_ROOT")-1 TSRMLS_CC);
  980. char *orig_path_translated = env_path_translated;
  981. char *orig_path_info = env_path_info;
  982. char *orig_script_name = env_script_name;
  983. char *orig_script_filename = env_script_filename;
  984. int script_path_translated_len;
  985. if (!env_document_root && PG(doc_root)) {
  986. env_document_root = _sapi_cgibin_putenv("DOCUMENT_ROOT", PG(doc_root) TSRMLS_CC);
  987. /* fix docroot */
  988. TRANSLATE_SLASHES(env_document_root);
  989. }
  990. if (env_path_translated != NULL && env_redirect_url != NULL &&
  991. env_path_translated != script_path_translated &&
  992. strcmp(env_path_translated, script_path_translated) != 0) {
  993. /*
  994. * pretty much apache specific. If we have a redirect_url
  995. * then our script_filename and script_name point to the
  996. * php executable
  997. */
  998. script_path_translated = env_path_translated;
  999. /* we correct SCRIPT_NAME now in case we don't have PATH_INFO */
  1000. env_script_name = env_redirect_url;
  1001. }
  1002. #ifdef __riscos__
  1003. /* Convert path to unix format*/
  1004. __riscosify_control |= __RISCOSIFY_DONT_CHECK_DIR;
  1005. script_path_translated = __unixify(script_path_translated, 0, NULL, 1, 0);
  1006. #endif
  1007. /*
  1008. * if the file doesn't exist, try to extract PATH_INFO out
  1009. * of it by stat'ing back through the '/'
  1010. * this fixes url's like /info.php/test
  1011. */
  1012. if (script_path_translated &&
  1013. (script_path_translated_len = strlen(script_path_translated)) > 0 &&
  1014. (script_path_translated[script_path_translated_len-1] == '/' ||
  1015. #ifdef PHP_WIN32
  1016. script_path_translated[script_path_translated_len-1] == '\\' ||
  1017. #endif
  1018. (real_path = tsrm_realpath(script_path_translated, NULL TSRMLS_CC)) == NULL)
  1019. ) {
  1020. char *pt = estrndup(script_path_translated, script_path_translated_len);
  1021. int len = script_path_translated_len;
  1022. char *ptr;
  1023. while ((ptr = strrchr(pt, '/')) || (ptr = strrchr(pt, '\\'))) {
  1024. *ptr = 0;
  1025. if (stat(pt, &st) == 0 && S_ISREG(st.st_mode)) {
  1026. /*
  1027. * okay, we found the base script!
  1028. * work out how many chars we had to strip off;
  1029. * then we can modify PATH_INFO
  1030. * accordingly
  1031. *
  1032. * we now have the makings of
  1033. * PATH_INFO=/test
  1034. * SCRIPT_FILENAME=/docroot/info.php
  1035. *
  1036. * we now need to figure out what docroot is.
  1037. * if DOCUMENT_ROOT is set, this is easy, otherwise,
  1038. * we have to play the game of hide and seek to figure
  1039. * out what SCRIPT_NAME should be
  1040. */
  1041. int slen = len - strlen(pt);
  1042. int pilen = env_path_info ? strlen(env_path_info) : 0;
  1043. char *path_info = env_path_info ? env_path_info + pilen - slen : NULL;
  1044. if (orig_path_info != path_info) {
  1045. if (orig_path_info) {
  1046. char old;
  1047. _sapi_cgibin_putenv("ORIG_PATH_INFO", orig_path_info TSRMLS_CC);
  1048. old = path_info[0];
  1049. path_info[0] = 0;
  1050. if (!orig_script_name ||
  1051. strcmp(orig_script_name, env_path_info) != 0) {
  1052. if (orig_script_name) {
  1053. _sapi_cgibin_putenv("ORIG_SCRIPT_NAME", orig_script_name TSRMLS_CC);
  1054. }
  1055. SG(request_info).request_uri = _sapi_cgibin_putenv("SCRIPT_NAME", env_path_info TSRMLS_CC);
  1056. } else {
  1057. SG(request_info).request_uri = orig_script_name;
  1058. }
  1059. path_info[0] = old;
  1060. }
  1061. env_path_info = _sapi_cgibin_putenv("PATH_INFO", path_info TSRMLS_CC);
  1062. }
  1063. if (!orig_script_filename ||
  1064. strcmp(orig_script_filename, pt) != 0) {
  1065. if (orig_script_filename) {
  1066. _sapi_cgibin_putenv("ORIG_SCRIPT_FILENAME", orig_script_filename TSRMLS_CC);
  1067. }
  1068. script_path_translated = _sapi_cgibin_putenv("SCRIPT_FILENAME", pt TSRMLS_CC);
  1069. }
  1070. TRANSLATE_SLASHES(pt);
  1071. /* figure out docroot
  1072. * SCRIPT_FILENAME minus SCRIPT_NAME
  1073. */
  1074. if (env_document_root) {
  1075. int l = strlen(env_document_root);
  1076. int path_translated_len = 0;
  1077. char *path_translated = NULL;
  1078. if (l && env_document_root[l - 1] == '/') {
  1079. --l;
  1080. }
  1081. /* we have docroot, so we should have:
  1082. * DOCUMENT_ROOT=/docroot
  1083. * SCRIPT_FILENAME=/docroot/info.php
  1084. */
  1085. /* PATH_TRANSLATED = DOCUMENT_ROOT + PATH_INFO */
  1086. path_translated_len = l + (env_path_info ? strlen(env_path_info) : 0);
  1087. path_translated = (char *) emalloc(path_translated_len + 1);
  1088. memcpy(path_translated, env_document_root, l);
  1089. if (env_path_info) {
  1090. memcpy(path_translated + l, env_path_info, (path_translated_len - l));
  1091. }
  1092. path_translated[path_translated_len] = '\0';
  1093. if (orig_path_translated) {
  1094. _sapi_cgibin_putenv("ORIG_PATH_TRANSLATED", orig_path_translated TSRMLS_CC);
  1095. }
  1096. env_path_translated = _sapi_cgibin_putenv("PATH_TRANSLATED", path_translated TSRMLS_CC);
  1097. efree(path_translated);
  1098. } else if ( env_script_name &&
  1099. strstr(pt, env_script_name)
  1100. ) {
  1101. /* PATH_TRANSLATED = PATH_TRANSLATED - SCRIPT_NAME + PATH_INFO */
  1102. int ptlen = strlen(pt) - strlen(env_script_name);
  1103. int path_translated_len = ptlen + (env_path_info ? strlen(env_path_info) : 0);
  1104. char *path_translated = NULL;
  1105. path_translated = (char *) emalloc(path_translated_len + 1);
  1106. memcpy(path_translated, pt, ptlen);
  1107. if (env_path_info) {
  1108. memcpy(path_translated + ptlen, env_path_info, path_translated_len - ptlen);
  1109. }
  1110. path_translated[path_translated_len] = '\0';
  1111. if (orig_path_translated) {
  1112. _sapi_cgibin_putenv("ORIG_PATH_TRANSLATED", orig_path_translated TSRMLS_CC);
  1113. }
  1114. env_path_translated = _sapi_cgibin_putenv("PATH_TRANSLATED", path_translated TSRMLS_CC);
  1115. efree(path_translated);
  1116. }
  1117. break;
  1118. }
  1119. }
  1120. if (!ptr) {
  1121. /*
  1122. * if we stripped out all the '/' and still didn't find
  1123. * a valid path... we will fail, badly. of course we would
  1124. * have failed anyway... we output 'no input file' now.
  1125. */
  1126. if (orig_script_filename) {
  1127. _sapi_cgibin_putenv("ORIG_SCRIPT_FILENAME", orig_script_filename TSRMLS_CC);
  1128. }
  1129. script_path_translated = _sapi_cgibin_putenv("SCRIPT_FILENAME", NULL TSRMLS_CC);
  1130. SG(sapi_headers).http_response_code = 404;
  1131. }
  1132. if (!SG(request_info).request_uri) {
  1133. if (!orig_script_name ||
  1134. strcmp(orig_script_name, env_script_name) != 0) {
  1135. if (orig_script_name) {
  1136. _sapi_cgibin_putenv("ORIG_SCRIPT_NAME", orig_script_name TSRMLS_CC);
  1137. }
  1138. SG(request_info).request_uri = _sapi_cgibin_putenv("SCRIPT_NAME", env_script_name TSRMLS_CC);
  1139. } else {
  1140. SG(request_info).request_uri = orig_script_name;
  1141. }
  1142. }
  1143. if (pt) {
  1144. efree(pt);
  1145. }
  1146. } else {
  1147. /* make sure path_info/translated are empty */
  1148. if (!orig_script_filename ||
  1149. (script_path_translated != orig_script_filename &&
  1150. strcmp(script_path_translated, orig_script_filename) != 0)) {
  1151. if (orig_script_filename) {
  1152. _sapi_cgibin_putenv("ORIG_SCRIPT_FILENAME", orig_script_filename TSRMLS_CC);
  1153. }
  1154. script_path_translated = _sapi_cgibin_putenv("SCRIPT_FILENAME", script_path_translated TSRMLS_CC);
  1155. }
  1156. if (env_redirect_url) {
  1157. if (orig_path_info) {
  1158. _sapi_cgibin_putenv("ORIG_PATH_INFO", orig_path_info TSRMLS_CC);
  1159. _sapi_cgibin_putenv("PATH_INFO", NULL TSRMLS_CC);
  1160. }
  1161. if (orig_path_translated) {
  1162. _sapi_cgibin_putenv("ORIG_PATH_TRANSLATED", orig_path_translated TSRMLS_CC);
  1163. _sapi_cgibin_putenv("PATH_TRANSLATED", NULL TSRMLS_CC);
  1164. }
  1165. }
  1166. if (env_script_name != orig_script_name) {
  1167. if (orig_script_name) {
  1168. _sapi_cgibin_putenv("ORIG_SCRIPT_NAME", orig_script_name TSRMLS_CC);
  1169. }
  1170. SG(request_info).request_uri = _sapi_cgibin_putenv("SCRIPT_NAME", env_script_name TSRMLS_CC);
  1171. } else {
  1172. SG(request_info).request_uri = env_script_name;
  1173. }
  1174. free(real_path);
  1175. }
  1176. } else {
  1177. /* pre 4.3 behaviour, shouldn't be used but provides BC */
  1178. if (env_path_info) {
  1179. SG(request_info).request_uri = env_path_info;
  1180. } else {
  1181. SG(request_info).request_uri = env_script_name;
  1182. }
  1183. if (!CGIG(discard_path) && env_path_translated) {
  1184. script_path_translated = env_path_translated;
  1185. }
  1186. }
  1187. if (is_valid_path(script_path_translated)) {
  1188. SG(request_info).path_translated = estrdup(script_path_translated);
  1189. }
  1190. SG(request_info).request_method = sapi_cgibin_getenv("REQUEST_METHOD", sizeof("REQUEST_METHOD")-1 TSRMLS_CC);
  1191. /* FIXME - Work out proto_num here */
  1192. SG(request_info).query_string = sapi_cgibin_getenv("QUERY_STRING", sizeof("QUERY_STRING")-1 TSRMLS_CC);
  1193. SG(request_info).content_type = (content_type ? content_type : "" );
  1194. SG(request_info).content_length = (content_length ? atoi(content_length) : 0);
  1195. /* The CGI RFC allows servers to pass on unvalidated Authorization data */
  1196. auth = sapi_cgibin_getenv("HTTP_AUTHORIZATION", sizeof("HTTP_AUTHORIZATION")-1 TSRMLS_CC);
  1197. php_handle_auth_data(auth TSRMLS_CC);
  1198. }
  1199. }
  1200. /* }}} */
  1201. #ifndef PHP_WIN32
  1202. /**
  1203. * Clean up child processes upon exit
  1204. */
  1205. void fastcgi_cleanup(int signal)
  1206. {
  1207. #ifdef DEBUG_FASTCGI
  1208. fprintf(stderr, "FastCGI shutdown, pid %d\n", getpid());
  1209. #endif
  1210. sigaction(SIGTERM, &old_term, 0);
  1211. /* Kill all the processes in our process group */
  1212. kill(-pgroup, SIGTERM);
  1213. if (parent && parent_waiting) {
  1214. exit_signal = 1;
  1215. } else {
  1216. exit(0);
  1217. }
  1218. }
  1219. #endif
  1220. PHP_INI_BEGIN()
  1221. STD_PHP_INI_ENTRY("cgi.rfc2616_headers", "0", PHP_INI_ALL, OnUpdateBool, rfc2616_headers, php_cgi_globals_struct, php_cgi_globals)
  1222. STD_PHP_INI_ENTRY("cgi.nph", "0", PHP_INI_ALL, OnUpdateBool, nph, php_cgi_globals_struct, php_cgi_globals)
  1223. STD_PHP_INI_ENTRY("cgi.check_shebang_line", "1", PHP_INI_SYSTEM, OnUpdateBool, check_shebang_line, php_cgi_globals_struct, php_cgi_globals)
  1224. STD_PHP_INI_ENTRY("cgi.force_redirect", "1", PHP_INI_SYSTEM, OnUpdateBool, force_redirect, php_cgi_globals_struct, php_cgi_globals)
  1225. STD_PHP_INI_ENTRY("cgi.redirect_status_env", NULL, PHP_INI_SYSTEM, OnUpdateString, redirect_status_env, php_cgi_globals_struct, php_cgi_globals)
  1226. STD_PHP_INI_ENTRY("cgi.fix_pathinfo", "1", PHP_INI_SYSTEM, OnUpdateBool, fix_pathinfo, php_cgi_globals_struct, php_cgi_globals)
  1227. STD_PHP_INI_ENTRY("cgi.discard_path", "0", PHP_INI_SYSTEM, OnUpdateBool, discard_path, php_cgi_globals_struct, php_cgi_globals)
  1228. STD_PHP_INI_ENTRY("fastcgi.logging", "1", PHP_INI_SYSTEM, OnUpdateBool, fcgi_logging, php_cgi_globals_struct, php_cgi_globals)
  1229. #ifdef PHP_WIN32
  1230. STD_PHP_INI_ENTRY("fastcgi.impersonate", "0", PHP_INI_SYSTEM, OnUpdateBool, impersonate, php_cgi_globals_struct, php_cgi_globals)
  1231. #endif
  1232. PHP_INI_END()
  1233. /* {{{ php_cgi_globals_ctor
  1234. */
  1235. static void php_cgi_globals_ctor(php_cgi_globals_struct *php_cgi_globals TSRMLS_DC)
  1236. {
  1237. php_cgi_globals->rfc2616_headers = 0;
  1238. php_cgi_globals->nph = 0;
  1239. php_cgi_globals->check_shebang_line = 1;
  1240. php_cgi_globals->force_redirect = 1;
  1241. php_cgi_globals->redirect_status_env = NULL;
  1242. php_cgi_globals->fix_pathinfo = 1;
  1243. php_cgi_globals->discard_path = 0;
  1244. php_cgi_globals->fcgi_logging = 1;
  1245. #ifdef PHP_WIN32
  1246. php_cgi_globals->impersonate = 0;
  1247. #endif
  1248. zend_hash_init(&php_cgi_globals->user_config_cache, 0, NULL, (dtor_func_t) user_config_cache_entry_dtor, 1);
  1249. }
  1250. /* }}} */
  1251. /* {{{ PHP_MINIT_FUNCTION
  1252. */
  1253. static PHP_MINIT_FUNCTION(cgi)
  1254. {
  1255. #ifdef ZTS
  1256. ts_allocate_id(&php_cgi_globals_id, sizeof(php_cgi_globals_struct), (ts_allocate_ctor) php_cgi_globals_ctor, NULL);
  1257. #else
  1258. php_cgi_globals_ctor(&php_cgi_globals TSRMLS_CC);
  1259. #endif
  1260. REGISTER_INI_ENTRIES();
  1261. return SUCCESS;
  1262. }
  1263. /* }}} */
  1264. /* {{{ PHP_MSHUTDOWN_FUNCTION
  1265. */
  1266. static PHP_MSHUTDOWN_FUNCTION(cgi)
  1267. {
  1268. zend_hash_destroy(&CGIG(user_config_cache));
  1269. UNREGISTER_INI_ENTRIES();
  1270. return SUCCESS;
  1271. }
  1272. /* }}} */
  1273. /* {{{ PHP_MINFO_FUNCTION
  1274. */
  1275. static PHP_MINFO_FUNCTION(cgi)
  1276. {
  1277. DISPLAY_INI_ENTRIES();
  1278. }
  1279. /* }}} */
  1280. static zend_module_entry cgi_module_entry = {
  1281. STANDARD_MODULE_HEADER,
  1282. "cgi-fcgi",
  1283. NULL,
  1284. PHP_MINIT(cgi),
  1285. PHP_MSHUTDOWN(cgi),
  1286. NULL,
  1287. NULL,
  1288. PHP_MINFO(cgi),
  1289. NO_VERSION_YET,
  1290. STANDARD_MODULE_PROPERTIES
  1291. };
  1292. /* {{{ main
  1293. */
  1294. int main(int argc, char *argv[])
  1295. {
  1296. int free_query_string = 0;
  1297. int exit_status = SUCCESS;
  1298. int cgi = 0, c, i, len;
  1299. zend_file_handle file_handle;
  1300. char *s;
  1301. /* temporary locals */
  1302. int behavior = PHP_MODE_STANDARD;
  1303. int no_headers = 0;
  1304. int orig_optind = php_optind;
  1305. char *orig_optarg = php_optarg;
  1306. char *script_file = NULL;
  1307. int ini_entries_len = 0;
  1308. /* end of temporary locals */
  1309. #ifdef ZTS
  1310. void ***tsrm_ls;
  1311. #endif
  1312. int max_requests = 500;
  1313. int requests = 0;
  1314. int fastcgi = fcgi_is_fastcgi();
  1315. char *bindpath = NULL;
  1316. int fcgi_fd = 0;
  1317. fcgi_request request;
  1318. int repeats = 1;
  1319. int benchmark = 0;
  1320. #if HAVE_GETTIMEOFDAY
  1321. struct timeval start, end;
  1322. #else
  1323. time_t start, end;
  1324. #endif
  1325. #ifndef PHP_WIN32
  1326. int status = 0;
  1327. #endif
  1328. #if 0 && defined(PHP_DEBUG)
  1329. /* IIS is always making things more difficult. This allows
  1330. * us to stop PHP and attach a debugger before much gets started */
  1331. {
  1332. char szMessage [256];
  1333. wsprintf (szMessage, "Please attach a debugger to the process 0x%X [%d] (%s) and click OK", GetCurrentProcessId(), GetCurrentProcessId(), argv[0]);
  1334. MessageBox(NULL, szMessage, "CGI Debug Time!", MB_OK|MB_SERVICE_NOTIFICATION);
  1335. }
  1336. #endif
  1337. #ifdef HAVE_SIGNAL_H
  1338. #if defined(SIGPIPE) && defined(SIG_IGN)
  1339. signal(SIGPIPE, SIG_IGN); /* ignore SIGPIPE in standalone mode so
  1340. that sockets created via fsockopen()
  1341. don't kill PHP if the remote site
  1342. closes it. in apache|apxs mode apache
  1343. does that for us! thies@thieso.net
  1344. 20000419 */
  1345. #endif
  1346. #endif
  1347. #ifdef ZTS
  1348. tsrm_startup(1, 1, 0, NULL);
  1349. tsrm_ls = ts_resource(0);
  1350. #endif
  1351. sapi_startup(&cgi_sapi_module);
  1352. cgi_sapi_module.php_ini_path_override = NULL;
  1353. #ifdef PHP_WIN32
  1354. _fmode = _O_BINARY; /* sets default for file streams to binary */
  1355. setmode(_fileno(stdin), O_BINARY); /* make the stdio mode be binary */
  1356. setmode(_fileno(stdout), O_BINARY); /* make the stdio mode be binary */
  1357. setmode(_fileno(stderr), O_BINARY); /* make the stdio mode be binary */
  1358. #endif
  1359. if (!fastcgi) {
  1360. /* Make sure we detect we are a cgi - a bit redundancy here,
  1361. * but the default case is that we have to check only the first one. */
  1362. if (getenv("SERVER_SOFTWARE") ||
  1363. getenv("SERVER_NAME") ||
  1364. getenv("GATEWAY_INTERFACE") ||
  1365. getenv("REQUEST_METHOD")
  1366. ) {
  1367. cgi = 1;
  1368. }
  1369. }
  1370. while ((c = php_getopt(argc, argv, OPTIONS, &php_optarg, &php_optind, 0, 2)) != -1) {
  1371. switch (c) {
  1372. case 'c':
  1373. if (cgi_sapi_module.php_ini_path_override) {
  1374. free(cgi_sapi_module.php_ini_path_override);
  1375. }
  1376. cgi_sapi_module.php_ini_path_override = strdup(php_optarg);
  1377. break;
  1378. case 'n':
  1379. cgi_sapi_module.php_ini_ignore = 1;
  1380. break;
  1381. case 'd': {
  1382. /* define ini entries on command line */
  1383. int len = strlen(php_optarg);
  1384. char *val;
  1385. if ((val = strchr(php_optarg, '='))) {
  1386. val++;
  1387. if (!isalnum(*val) && *val != '"' && *val != '\'' && *val != '\0') {
  1388. cgi_sapi_module.ini_entries = realloc(cgi_sapi_module.ini_entries, ini_entries_len + len + sizeof("\"\"\n\0"));
  1389. memcpy(cgi_sapi_module.ini_entries + ini_entries_len, php_optarg, (val - php_optarg));
  1390. ini_entries_len += (val - php_optarg);
  1391. memcpy(cgi_sapi_module.ini_entries + ini_entries_len, "\"", 1);
  1392. ini_entries_len++;
  1393. memcpy(cgi_sapi_module.ini_entries + ini_entries_len, val, len - (val - php_optarg));
  1394. ini_entries_len += len - (val - php_optarg);
  1395. memcpy(cgi_sapi_module.ini_entries + ini_entries_len, "\"\n\0", sizeof("\"\n\0"));
  1396. ini_entries_len += sizeof("\n\0\"") - 2;
  1397. } else {
  1398. cgi_sapi_module.ini_entries = realloc(cgi_sapi_module.ini_entries, ini_entries_len + len + sizeof("\n\0"));
  1399. memcpy(cgi_sapi_module.ini_entries + ini_entries_len, php_optarg, len);
  1400. memcpy(cgi_sapi_module.ini_entries + ini_entries_len + len, "\n\0", sizeof("\n\0"));
  1401. ini_entries_len += len + sizeof("\n\0") - 2;
  1402. }
  1403. } else {
  1404. cgi_sapi_module.ini_entries = realloc(cgi_sapi_module.ini_entries, ini_entries_len + len + sizeof("=1\n\0"));
  1405. memcpy(cgi_sapi_module.ini_entries + ini_entries_len, php_optarg, len);
  1406. memcpy(cgi_sapi_module.ini_entries + ini_entries_len + len, "=1\n\0", sizeof("=1\n\0"));
  1407. ini_entries_len += len + sizeof("=1\n\0") - 2;
  1408. }
  1409. break;
  1410. }
  1411. /* if we're started on command line, check to see if
  1412. * we are being started as an 'external' fastcgi
  1413. * server by accepting a bindpath parameter. */
  1414. case 'b':
  1415. if (!fastcgi) {
  1416. bindpath = strdup(php_optarg);
  1417. }
  1418. break;
  1419. case 's': /* generate highlighted HTML from source */
  1420. behavior = PHP_MODE_HIGHLIGHT;
  1421. break;
  1422. }
  1423. }
  1424. php_optind = orig_optind;
  1425. php_optarg = orig_optarg;
  1426. #ifdef ZTS
  1427. SG(request_info).path_translated = NULL;
  1428. #endif
  1429. cgi_sapi_module.executable_location = argv[0];
  1430. if (!cgi && !fastcgi && !bindpath) {
  1431. cgi_sapi_module.additional_functions = additional_functions;
  1432. }
  1433. /* startup after we get the above ini override se we get things right */
  1434. if (cgi_sapi_module.startup(&cgi_sapi_module) == FAILURE) {
  1435. #ifdef ZTS
  1436. tsrm_shutdown();
  1437. #endif
  1438. return FAILURE;
  1439. }
  1440. /* check force_cgi after startup, so we have proper output */
  1441. if (cgi && CGIG(force_redirect)) {
  1442. /* Apache will generate REDIRECT_STATUS,
  1443. * Netscape and redirect.so will generate HTTP_REDIRECT_STATUS.
  1444. * redirect.so and installation instructions available from
  1445. * http://www.koehntopp.de/php.
  1446. * -- kk@netuse.de…

Large files files are truncated, but you can click here to view the full file