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