1/*
2   +----------------------------------------------------------------------+
3   | PHP Version 7                                                        |
4   +----------------------------------------------------------------------+
5   | Copyright (c) 1997-2014 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   | Author: Sascha Schumann <sascha@schumann.cx>                         |
16   +----------------------------------------------------------------------+
17 */
18
19/* $Id$ */
20
21#define ZEND_INCLUDE_FULL_WINDOWS_HEADERS
22
23#include "php.h"
24#include "zend_smart_str.h"
25#include "ext/standard/info.h"
26#include "ext/standard/head.h"
27#include "php_ini.h"
28#include "SAPI.h"
29
30#define CORE_PRIVATE
31#include "apr_strings.h"
32#include "apr_time.h"
33#include "ap_config.h"
34#include "util_filter.h"
35#include "httpd.h"
36#include "http_config.h"
37#include "http_request.h"
38#include "http_core.h"
39#include "http_protocol.h"
40#include "http_log.h"
41#include "http_main.h"
42#include "util_script.h"
43#include "http_core.h"
44#include "ap_mpm.h"
45#if !defined(WIN32) && !defined(WINNT) && !defined(NETWARE)
46#include "unixd.h"
47#endif
48
49#include "php_apache.h"
50
51#ifdef ZTS
52int php_apache2_info_id;
53#else
54php_apache2_info_struct php_apache2_info;
55#endif
56
57#define SECTION(name)  PUTS("<h2>" name "</h2>\n")
58
59static request_rec *php_apache_lookup_uri(char *filename)
60{
61    php_struct *ctx = SG(server_context);
62
63    if (!filename || !ctx || !ctx->r) {
64        return NULL;
65    }
66
67    return ap_sub_req_lookup_uri(filename, ctx->r, ctx->r->output_filters);
68}
69
70/* {{{ proto bool virtual(string uri)
71 Perform an apache sub-request */
72PHP_FUNCTION(virtual)
73{
74    char *filename;
75    size_t filename_len;
76    request_rec *rr;
77
78    if (zend_parse_parameters(ZEND_NUM_ARGS(), "p", &filename, &filename_len) == FAILURE) {
79        return;
80    }
81
82    if (!(rr = php_apache_lookup_uri(filename))) {
83        php_error_docref(NULL, E_WARNING, "Unable to include '%s' - URI lookup failed", filename);
84        RETURN_FALSE;
85    }
86
87    if (rr->status != HTTP_OK) {
88        php_error_docref(NULL, E_WARNING, "Unable to include '%s' - error finding URI", filename);
89        ap_destroy_sub_req(rr);
90        RETURN_FALSE;
91    }
92
93    /* Flush everything. */
94    php_output_end_all();
95    php_header();
96
97    /* Ensure that the ap_r* layer for the main request is flushed, to
98     * work around http://issues.apache.org/bugzilla/show_bug.cgi?id=17629 */
99    ap_rflush(rr->main);
100
101    if (ap_run_sub_req(rr)) {
102        php_error_docref(NULL, E_WARNING, "Unable to include '%s' - request execution failed", filename);
103        ap_destroy_sub_req(rr);
104        RETURN_FALSE;
105    }
106    ap_destroy_sub_req(rr);
107    RETURN_TRUE;
108}
109/* }}} */
110
111#define ADD_LONG(name) \
112        add_property_long(return_value, #name, rr->name)
113#define ADD_TIME(name) \
114        add_property_long(return_value, #name, apr_time_sec(rr->name));
115#define ADD_STRING(name) \
116        if (rr->name) add_property_string(return_value, #name, (char *) rr->name)
117
118PHP_FUNCTION(apache_lookup_uri)
119{
120    request_rec *rr;
121    char *filename;
122    size_t filename_len;
123
124    if (zend_parse_parameters(ZEND_NUM_ARGS(), "p", &filename, &filename_len) == FAILURE) {
125        return;
126    }
127
128    if (!(rr = php_apache_lookup_uri(filename))) {
129        php_error_docref(NULL, E_WARNING, "Unable to include '%s' - URI lookup failed", filename);
130        RETURN_FALSE;
131    }
132
133    if (rr->status == HTTP_OK) {
134        object_init(return_value);
135
136        ADD_LONG(status);
137        ADD_STRING(the_request);
138        ADD_STRING(status_line);
139        ADD_STRING(method);
140        ADD_TIME(mtime);
141        ADD_LONG(clength);
142#if MODULE_MAGIC_NUMBER < 20020506
143        ADD_STRING(boundary);
144#endif
145        ADD_STRING(range);
146        ADD_LONG(chunked);
147        ADD_STRING(content_type);
148        ADD_STRING(handler);
149        ADD_LONG(no_cache);
150        ADD_LONG(no_local_copy);
151        ADD_STRING(unparsed_uri);
152        ADD_STRING(uri);
153        ADD_STRING(filename);
154        ADD_STRING(path_info);
155        ADD_STRING(args);
156        ADD_LONG(allowed);
157        ADD_LONG(sent_bodyct);
158        ADD_LONG(bytes_sent);
159        ADD_LONG(mtime);
160        ADD_TIME(request_time);
161
162        ap_destroy_sub_req(rr);
163        return;
164    }
165
166    php_error_docref(NULL, E_WARNING, "Unable to include '%s' - error finding URI", filename);
167    ap_destroy_sub_req(rr);
168    RETURN_FALSE;
169}
170
171/* {{{ proto array getallheaders(void)
172   Fetch all HTTP request headers */
173PHP_FUNCTION(apache_request_headers)
174{
175    php_struct *ctx;
176    const apr_array_header_t *arr;
177    char *key, *val;
178
179    if (zend_parse_parameters_none() == FAILURE) {
180        return;
181    }
182
183    array_init(return_value);
184
185    ctx = SG(server_context);
186    arr = apr_table_elts(ctx->r->headers_in);
187
188    APR_ARRAY_FOREACH_OPEN(arr, key, val)
189        if (!val) val = "";
190        add_assoc_string(return_value, key, val);
191    APR_ARRAY_FOREACH_CLOSE()
192}
193/* }}} */
194
195/* {{{ proto array apache_response_headers(void)
196   Fetch all HTTP response headers */
197PHP_FUNCTION(apache_response_headers)
198{
199    php_struct *ctx;
200    const apr_array_header_t *arr;
201    char *key, *val;
202
203    if (zend_parse_parameters_none() == FAILURE) {
204        return;
205    }
206
207    array_init(return_value);
208
209    ctx = SG(server_context);
210    arr = apr_table_elts(ctx->r->headers_out);
211
212    APR_ARRAY_FOREACH_OPEN(arr, key, val)
213        if (!val) val = "";
214        add_assoc_string(return_value, key, val);
215    APR_ARRAY_FOREACH_CLOSE()
216}
217/* }}} */
218
219/* {{{ proto string apache_note(string note_name [, string note_value])
220   Get and set Apache request notes */
221PHP_FUNCTION(apache_note)
222{
223    php_struct *ctx;
224    char *note_name, *note_val = NULL;
225    size_t note_name_len, note_val_len;
226    char *old_note_val=NULL;
227
228    if (zend_parse_parameters(ZEND_NUM_ARGS(), "s|s", &note_name, &note_name_len, &note_val, &note_val_len) == FAILURE) {
229        return;
230    }
231
232    ctx = SG(server_context);
233
234    old_note_val = (char *) apr_table_get(ctx->r->notes, note_name);
235
236    if (note_val) {
237        apr_table_set(ctx->r->notes, note_name, note_val);
238    }
239
240    if (old_note_val) {
241        RETURN_STRING(old_note_val);
242    }
243
244    RETURN_FALSE;
245}
246/* }}} */
247
248
249/* {{{ proto bool apache_setenv(string variable, string value [, bool walk_to_top])
250   Set an Apache subprocess_env variable */
251/*
252 * XXX this doesn't look right. shouldn't it be the parent ?*/
253PHP_FUNCTION(apache_setenv)
254{
255    php_struct *ctx;
256    char *variable=NULL, *string_val=NULL;
257    size_t variable_len, string_val_len;
258    zend_bool walk_to_top = 0;
259    int arg_count = ZEND_NUM_ARGS();
260    request_rec *r;
261
262    if (zend_parse_parameters(arg_count, "ss|b", &variable, &variable_len, &string_val, &string_val_len, &walk_to_top) == FAILURE) {
263        return;
264    }
265
266    ctx = SG(server_context);
267
268    r = ctx->r;
269    if (arg_count == 3) {
270        if (walk_to_top) {
271            while(r->prev) {
272                r = r->prev;
273            }
274        }
275    }
276
277    apr_table_set(r->subprocess_env, variable, string_val);
278
279    RETURN_TRUE;
280}
281/* }}} */
282
283/* {{{ proto bool apache_getenv(string variable [, bool walk_to_top])
284   Get an Apache subprocess_env variable */
285/*
286 * XXX: shouldn't this be the parent not the 'prev'
287 */
288PHP_FUNCTION(apache_getenv)
289{
290    php_struct *ctx;
291    char *variable;
292    size_t variable_len;
293    zend_bool walk_to_top = 0;
294    int arg_count = ZEND_NUM_ARGS();
295    char *env_val=NULL;
296    request_rec *r;
297
298    if (zend_parse_parameters(arg_count, "s|b", &variable, &variable_len, &walk_to_top) == FAILURE) {
299        return;
300    }
301
302    ctx = SG(server_context);
303
304    r = ctx->r;
305    if (arg_count == 2) {
306        if (walk_to_top) {
307            while(r->prev) {
308                r = r->prev;
309            }
310        }
311    }
312
313    env_val = (char*) apr_table_get(r->subprocess_env, variable);
314
315    if (env_val != NULL) {
316        RETURN_STRING(env_val);
317    }
318
319    RETURN_FALSE;
320}
321/* }}} */
322
323static char *php_apache_get_version()
324{
325#if MODULE_MAGIC_NUMBER_MAJOR >= 20060905
326    return (char *) ap_get_server_banner();
327#else
328    return (char *) ap_get_server_version();
329#endif
330}
331
332/* {{{ proto string apache_get_version(void)
333   Fetch Apache version */
334PHP_FUNCTION(apache_get_version)
335{
336    char *apv = php_apache_get_version();
337
338    if (apv && *apv) {
339        RETURN_STRING(apv);
340    } else {
341        RETURN_FALSE;
342    }
343}
344/* }}} */
345
346/* {{{ proto array apache_get_modules(void)
347   Get a list of loaded Apache modules */
348PHP_FUNCTION(apache_get_modules)
349{
350    int n;
351    char *p;
352
353    array_init(return_value);
354
355    for (n = 0; ap_loaded_modules[n]; ++n) {
356        char *s = (char *) ap_loaded_modules[n]->name;
357        if ((p = strchr(s, '.'))) {
358            add_next_index_stringl(return_value, s, (p - s));
359        } else {
360            add_next_index_string(return_value, s);
361        }
362    }
363}
364/* }}} */
365
366PHP_MINFO_FUNCTION(apache)
367{
368    char *apv = php_apache_get_version();
369    smart_str tmp1 = {0};
370    char tmp[1024];
371    int n, max_requests;
372    char *p;
373    server_rec *serv = ((php_struct *) SG(server_context))->r->server;
374#if !defined(WIN32) && !defined(WINNT) && !defined(NETWARE)
375#if MODULE_MAGIC_NUMBER_MAJOR >= 20081201
376    AP_DECLARE_DATA extern unixd_config_rec ap_unixd_config;
377#else
378    AP_DECLARE_DATA extern unixd_config_rec unixd_config;
379#endif
380#endif
381
382    for (n = 0; ap_loaded_modules[n]; ++n) {
383        char *s = (char *) ap_loaded_modules[n]->name;
384        if ((p = strchr(s, '.'))) {
385            smart_str_appendl(&tmp1, s, (p - s));
386        } else {
387            smart_str_appends(&tmp1, s);
388        }
389        smart_str_appendc(&tmp1, ' ');
390    }
391    if (tmp1.s) {
392        if (tmp1.s->len > 0) {
393            tmp1.s->val[tmp1.s->len - 1] = '\0';
394        } else {
395            tmp1.s->val[0] = '\0';
396        }
397    }
398
399    php_info_print_table_start();
400    if (apv && *apv) {
401        php_info_print_table_row(2, "Apache Version", apv);
402    }
403    snprintf(tmp, sizeof(tmp), "%d", MODULE_MAGIC_NUMBER);
404    php_info_print_table_row(2, "Apache API Version", tmp);
405
406    if (serv->server_admin && *(serv->server_admin)) {
407        php_info_print_table_row(2, "Server Administrator", serv->server_admin);
408    }
409
410    snprintf(tmp, sizeof(tmp), "%s:%u", serv->server_hostname, serv->port);
411    php_info_print_table_row(2, "Hostname:Port", tmp);
412
413#if !defined(WIN32) && !defined(WINNT) && !defined(NETWARE)
414#if MODULE_MAGIC_NUMBER_MAJOR >= 20081201
415    snprintf(tmp, sizeof(tmp), "%s(%d)/%d", ap_unixd_config.user_name, ap_unixd_config.user_id, ap_unixd_config.group_id);
416#else
417    snprintf(tmp, sizeof(tmp), "%s(%d)/%d", unixd_config.user_name, unixd_config.user_id, unixd_config.group_id);
418#endif
419    php_info_print_table_row(2, "User/Group", tmp);
420#endif
421
422    ap_mpm_query(AP_MPMQ_MAX_REQUESTS_DAEMON, &max_requests);
423    snprintf(tmp, sizeof(tmp), "Per Child: %d - Keep Alive: %s - Max Per Connection: %d", max_requests, (serv->keep_alive ? "on":"off"), serv->keep_alive_max);
424    php_info_print_table_row(2, "Max Requests", tmp);
425
426    apr_snprintf(tmp, sizeof tmp,
427                 "Connection: %" APR_TIME_T_FMT " - Keep-Alive: %" APR_TIME_T_FMT,
428                 apr_time_sec(serv->timeout), apr_time_sec(serv->keep_alive_timeout));
429    php_info_print_table_row(2, "Timeouts", tmp);
430
431    php_info_print_table_row(2, "Virtual Server", (serv->is_virtual ? "Yes" : "No"));
432    php_info_print_table_row(2, "Server Root", ap_server_root);
433    php_info_print_table_row(2, "Loaded Modules", tmp1.s->val);
434
435    smart_str_free(&tmp1);
436    php_info_print_table_end();
437
438    DISPLAY_INI_ENTRIES();
439
440    {
441        const apr_array_header_t *arr = apr_table_elts(((php_struct *) SG(server_context))->r->subprocess_env);
442        char *key, *val;
443
444        SECTION("Apache Environment");
445        php_info_print_table_start();
446        php_info_print_table_header(2, "Variable", "Value");
447        APR_ARRAY_FOREACH_OPEN(arr, key, val)
448            if (!val) {
449                val = "";
450            }
451            php_info_print_table_row(2, key, val);
452        APR_ARRAY_FOREACH_CLOSE()
453
454        php_info_print_table_end();
455
456        SECTION("HTTP Headers Information");
457        php_info_print_table_start();
458        php_info_print_table_colspan_header(2, "HTTP Request Headers");
459        php_info_print_table_row(2, "HTTP Request", ((php_struct *) SG(server_context))->r->the_request);
460
461        arr = apr_table_elts(((php_struct *) SG(server_context))->r->headers_in);
462        APR_ARRAY_FOREACH_OPEN(arr, key, val)
463            if (!val) {
464                val = "";
465            }
466                php_info_print_table_row(2, key, val);
467        APR_ARRAY_FOREACH_CLOSE()
468
469        php_info_print_table_colspan_header(2, "HTTP Response Headers");
470        arr = apr_table_elts(((php_struct *) SG(server_context))->r->headers_out);
471        APR_ARRAY_FOREACH_OPEN(arr, key, val)
472            if (!val) {
473                val = "";
474            }
475                php_info_print_table_row(2, key, val);
476        APR_ARRAY_FOREACH_CLOSE()
477
478        php_info_print_table_end();
479    }
480}
481
482/* {{{ arginfo */
483ZEND_BEGIN_ARG_INFO_EX(arginfo_apache2handler_lookup_uri, 0, 0, 1)
484    ZEND_ARG_INFO(0, filename)
485ZEND_END_ARG_INFO()
486
487ZEND_BEGIN_ARG_INFO_EX(arginfo_apache2handler_virtual, 0, 0, 1)
488    ZEND_ARG_INFO(0, uri)
489ZEND_END_ARG_INFO()
490
491ZEND_BEGIN_ARG_INFO(arginfo_apache2handler_response_headers, 0)
492ZEND_END_ARG_INFO()
493
494ZEND_BEGIN_ARG_INFO(arginfo_apache2handler_getallheaders, 0)
495ZEND_END_ARG_INFO()
496
497ZEND_BEGIN_ARG_INFO_EX(arginfo_apache2handler_note, 0, 0, 1)
498    ZEND_ARG_INFO(0, note_name)
499    ZEND_ARG_INFO(0, note_value)
500ZEND_END_ARG_INFO()
501
502ZEND_BEGIN_ARG_INFO_EX(arginfo_apache2handler_setenv, 0, 0, 2)
503    ZEND_ARG_INFO(0, variable)
504    ZEND_ARG_INFO(0, value)
505    ZEND_ARG_INFO(0, walk_to_top)
506ZEND_END_ARG_INFO()
507
508ZEND_BEGIN_ARG_INFO_EX(arginfo_apache2handler_getenv, 0, 0, 1)
509    ZEND_ARG_INFO(0, variable)
510    ZEND_ARG_INFO(0, walk_to_top)
511ZEND_END_ARG_INFO()
512
513ZEND_BEGIN_ARG_INFO(arginfo_apache2handler_get_version, 0)
514ZEND_END_ARG_INFO()
515
516ZEND_BEGIN_ARG_INFO(arginfo_apache2handler_get_modules, 0)
517ZEND_END_ARG_INFO()
518/* }}} */
519
520static const zend_function_entry apache_functions[] = {
521    PHP_FE(apache_lookup_uri,       arginfo_apache2handler_lookup_uri)
522    PHP_FE(virtual,                 arginfo_apache2handler_virtual)
523    PHP_FE(apache_request_headers,  arginfo_apache2handler_getallheaders)
524    PHP_FE(apache_response_headers, arginfo_apache2handler_response_headers)
525    PHP_FE(apache_setenv,       arginfo_apache2handler_setenv)
526    PHP_FE(apache_getenv,       arginfo_apache2handler_getenv)
527    PHP_FE(apache_note,         arginfo_apache2handler_note)
528    PHP_FE(apache_get_version,  arginfo_apache2handler_get_version)
529    PHP_FE(apache_get_modules,  arginfo_apache2handler_get_modules)
530    PHP_FALIAS(getallheaders,   apache_request_headers, arginfo_apache2handler_getallheaders)
531    {NULL, NULL, NULL}
532};
533
534PHP_INI_BEGIN()
535    STD_PHP_INI_ENTRY("xbithack",       "0",    PHP_INI_ALL,    OnUpdateBool,   xbithack,   php_apache2_info_struct, php_apache2_info)
536    STD_PHP_INI_ENTRY("engine",     "1",    PHP_INI_ALL,    OnUpdateBool,   engine,     php_apache2_info_struct, php_apache2_info)
537    STD_PHP_INI_ENTRY("last_modified",  "0",    PHP_INI_ALL,    OnUpdateBool,   last_modified,  php_apache2_info_struct, php_apache2_info)
538PHP_INI_END()
539
540static PHP_MINIT_FUNCTION(apache)
541{
542#ifdef ZTS
543    ts_allocate_id(&php_apache2_info_id, sizeof(php_apache2_info_struct), (ts_allocate_ctor) NULL, NULL);
544#endif
545    REGISTER_INI_ENTRIES();
546    return SUCCESS;
547}
548
549static PHP_MSHUTDOWN_FUNCTION(apache)
550{
551    UNREGISTER_INI_ENTRIES();
552    return SUCCESS;
553}
554
555zend_module_entry php_apache_module = {
556    STANDARD_MODULE_HEADER,
557    "apache2handler",
558    apache_functions,
559    PHP_MINIT(apache),
560    PHP_MSHUTDOWN(apache),
561    NULL,
562    NULL,
563    PHP_MINFO(apache),
564    NULL,
565    STANDARD_MODULE_PROPERTIES
566};
567
568/*
569 * Local variables:
570 * tab-width: 4
571 * c-basic-offset: 4
572 * End:
573 * vim600: sw=4 ts=4 fdm=marker
574 * vim<600: sw=4 ts=4
575 */
576