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