1/*
2   +----------------------------------------------------------------------+
3   | PHP Version 7                                                        |
4   +----------------------------------------------------------------------+
5   | Copyright (c) 1997-2016 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