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