You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

2120 lines
62 KiB

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