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