1/*
2   +----------------------------------------------------------------------+
3   | PHP Version 7                                                        |
4   +----------------------------------------------------------------------+
5   | Copyright (c) 1997-2018 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: Sascha Schumann <sascha@schumann.cx>                        |
16   |          Parts based on Apache 1.3 SAPI module by                    |
17   |          Rasmus Lerdorf and Zeev Suraski                             |
18   +----------------------------------------------------------------------+
19 */
20
21#define ZEND_INCLUDE_FULL_WINDOWS_HEADERS
22
23#include "php.h"
24#include "php_main.h"
25#include "php_ini.h"
26#include "php_variables.h"
27#include "SAPI.h"
28
29#include <fcntl.h>
30
31#include "zend_smart_str.h"
32#include "ext/standard/php_standard.h"
33
34#include "apr_strings.h"
35#include "ap_config.h"
36#include "util_filter.h"
37#include "httpd.h"
38#include "http_config.h"
39#include "http_request.h"
40#include "http_core.h"
41#include "http_protocol.h"
42#include "http_log.h"
43#include "http_main.h"
44#include "util_script.h"
45#include "http_core.h"
46#include "ap_mpm.h"
47
48#include "php_apache.h"
49
50/* UnixWare define shutdown to _shutdown, which causes problems later
51 * on when using a structure member named shutdown. Since this source
52 * file does not use the system call shutdown, it is safe to #undef it.
53 */
54#undef shutdown
55
56#define PHP_MAGIC_TYPE "application/x-httpd-php"
57#define PHP_SOURCE_MAGIC_TYPE "application/x-httpd-php-source"
58#define PHP_SCRIPT "php7-script"
59
60/* A way to specify the location of the php.ini dir in an apache directive */
61char *apache2_php_ini_path_override = NULL;
62#if defined(PHP_WIN32) && defined(ZTS)
63ZEND_TSRMLS_CACHE_DEFINE()
64#endif
65
66static size_t
67php_apache_sapi_ub_write(const char *str, size_t str_length)
68{
69	request_rec *r;
70	php_struct *ctx;
71
72	ctx = SG(server_context);
73	r = ctx->r;
74
75	if (ap_rwrite(str, str_length, r) < 0) {
76		php_handle_aborted_connection();
77	}
78
79	return str_length; /* we always consume all the data passed to us. */
80}
81
82static int
83php_apache_sapi_header_handler(sapi_header_struct *sapi_header, sapi_header_op_enum op, sapi_headers_struct *sapi_headers)
84{
85	php_struct *ctx;
86	char *val, *ptr;
87
88	ctx = SG(server_context);
89
90	switch (op) {
91		case SAPI_HEADER_DELETE:
92			apr_table_unset(ctx->r->headers_out, sapi_header->header);
93			return 0;
94
95		case SAPI_HEADER_DELETE_ALL:
96			apr_table_clear(ctx->r->headers_out);
97			return 0;
98
99		case SAPI_HEADER_ADD:
100		case SAPI_HEADER_REPLACE:
101			val = strchr(sapi_header->header, ':');
102
103			if (!val) {
104				return 0;
105			}
106			ptr = val;
107
108			*val = '\0';
109
110			do {
111				val++;
112			} while (*val == ' ');
113
114			if (!strcasecmp(sapi_header->header, "content-type")) {
115				if (ctx->content_type) {
116					efree(ctx->content_type);
117				}
118				ctx->content_type = estrdup(val);
119			} else if (!strcasecmp(sapi_header->header, "content-length")) {
120				apr_off_t clen = 0;
121
122				if (APR_SUCCESS != apr_strtoff(&clen, val, (char **) NULL, 10)) {
123					/* We'll fall back to strtol, since that's what we used to
124					 * do anyway. */
125					clen = (apr_off_t) strtol(val, (char **) NULL, 10);
126				}
127
128				ap_set_content_length(ctx->r, clen);
129			} else if (op == SAPI_HEADER_REPLACE) {
130				apr_table_set(ctx->r->headers_out, sapi_header->header, val);
131			} else {
132				apr_table_add(ctx->r->headers_out, sapi_header->header, val);
133			}
134
135			*ptr = ':';
136
137			return SAPI_HEADER_ADD;
138
139		default:
140			return 0;
141	}
142}
143
144static int
145php_apache_sapi_send_headers(sapi_headers_struct *sapi_headers)
146{
147	php_struct *ctx = SG(server_context);
148	const char *sline = SG(sapi_headers).http_status_line;
149
150	ctx->r->status = SG(sapi_headers).http_response_code;
151
152	/* httpd requires that r->status_line is set to the first digit of
153	 * the status-code: */
154	if (sline && strlen(sline) > 12 && strncmp(sline, "HTTP/1.", 7) == 0 && sline[8] == ' ') {
155		ctx->r->status_line = apr_pstrdup(ctx->r->pool, sline + 9);
156		ctx->r->proto_num = 1000 + (sline[7]-'0');
157		if ((sline[7]-'0') == 0) {
158			apr_table_set(ctx->r->subprocess_env, "force-response-1.0", "true");
159		}
160	}
161
162	/*	call ap_set_content_type only once, else each time we call it,
163		configured output filters for that content type will be added */
164	if (!ctx->content_type) {
165		ctx->content_type = sapi_get_default_content_type();
166	}
167	ap_set_content_type(ctx->r, apr_pstrdup(ctx->r->pool, ctx->content_type));
168	efree(ctx->content_type);
169	ctx->content_type = NULL;
170
171	return SAPI_HEADER_SENT_SUCCESSFULLY;
172}
173
174static apr_size_t
175php_apache_sapi_read_post(char *buf, size_t count_bytes)
176{
177	apr_size_t len, tlen=0;
178	php_struct *ctx = SG(server_context);
179	request_rec *r;
180	apr_bucket_brigade *brigade;
181
182	r = ctx->r;
183	brigade = ctx->brigade;
184	len = count_bytes;
185
186	/*
187	 * This loop is needed because ap_get_brigade() can return us partial data
188	 * which would cause premature termination of request read. Therefor we
189	 * need to make sure that if data is available we fill the buffer completely.
190	 */
191
192	while (ap_get_brigade(r->input_filters, brigade, AP_MODE_READBYTES, APR_BLOCK_READ, len) == APR_SUCCESS) {
193		apr_brigade_flatten(brigade, buf, &len);
194		apr_brigade_cleanup(brigade);
195		tlen += len;
196		if (tlen == count_bytes || !len) {
197			break;
198		}
199		buf += len;
200		len = count_bytes - tlen;
201	}
202
203	return tlen;
204}
205
206static zend_stat_t*
207php_apache_sapi_get_stat(void)
208{
209	php_struct *ctx = SG(server_context);
210
211#ifdef PHP_WIN32
212	ctx->finfo.st_uid = 0;
213	ctx->finfo.st_gid = 0;
214#else
215	ctx->finfo.st_uid = ctx->r->finfo.user;
216	ctx->finfo.st_gid = ctx->r->finfo.group;
217#endif
218	ctx->finfo.st_dev = ctx->r->finfo.device;
219	ctx->finfo.st_ino = ctx->r->finfo.inode;
220	ctx->finfo.st_atime = apr_time_sec(ctx->r->finfo.atime);
221	ctx->finfo.st_mtime = apr_time_sec(ctx->r->finfo.mtime);
222	ctx->finfo.st_ctime = apr_time_sec(ctx->r->finfo.ctime);
223	ctx->finfo.st_size = ctx->r->finfo.size;
224	ctx->finfo.st_nlink = ctx->r->finfo.nlink;
225
226	return &ctx->finfo;
227}
228
229static char *
230php_apache_sapi_read_cookies(void)
231{
232	php_struct *ctx = SG(server_context);
233	const char *http_cookie;
234
235	http_cookie = apr_table_get(ctx->r->headers_in, "cookie");
236
237	/* The SAPI interface should use 'const char *' */
238	return (char *) http_cookie;
239}
240
241static char *
242php_apache_sapi_getenv(char *name, size_t name_len)
243{
244	php_struct *ctx = SG(server_context);
245	const char *env_var;
246
247	if (ctx == NULL) {
248		return NULL;
249	}
250
251	env_var = apr_table_get(ctx->r->subprocess_env, name);
252
253	return (char *) env_var;
254}
255
256static void
257php_apache_sapi_register_variables(zval *track_vars_array)
258{
259	php_struct *ctx = SG(server_context);
260	const apr_array_header_t *arr = apr_table_elts(ctx->r->subprocess_env);
261	char *key, *val;
262	size_t new_val_len;
263
264	APR_ARRAY_FOREACH_OPEN(arr, key, val)
265		if (!val) {
266			val = "";
267		}
268		if (sapi_module.input_filter(PARSE_SERVER, key, &val, strlen(val), &new_val_len)) {
269			php_register_variable_safe(key, val, new_val_len, track_vars_array);
270		}
271	APR_ARRAY_FOREACH_CLOSE()
272
273	if (sapi_module.input_filter(PARSE_SERVER, "PHP_SELF", &ctx->r->uri, strlen(ctx->r->uri), &new_val_len)) {
274		php_register_variable_safe("PHP_SELF", ctx->r->uri, new_val_len, track_vars_array);
275	}
276}
277
278static void
279php_apache_sapi_flush(void *server_context)
280{
281	php_struct *ctx;
282	request_rec *r;
283
284	ctx = server_context;
285
286	/* If we haven't registered a server_context yet,
287	 * then don't bother flushing. */
288	if (!server_context) {
289		return;
290	}
291
292	r = ctx->r;
293
294	sapi_send_headers();
295
296	r->status = SG(sapi_headers).http_response_code;
297	SG(headers_sent) = 1;
298
299	if (ap_rflush(r) < 0 || r->connection->aborted) {
300		php_handle_aborted_connection();
301	}
302}
303
304static void php_apache_sapi_log_message(char *msg, int syslog_type_int)
305{
306	php_struct *ctx;
307	int aplog_type = APLOG_ERR;
308
309	ctx = SG(server_context);
310
311	switch (syslog_type_int) {
312#if LOG_EMERG != LOG_CRIT
313		case LOG_EMERG:
314			aplog_type = APLOG_EMERG;
315			break;
316#endif
317#if LOG_ALERT != LOG_CRIT
318		case LOG_ALERT:
319			aplog_type = APLOG_ALERT;
320			break;
321#endif
322		case LOG_CRIT:
323			aplog_type = APLOG_CRIT;
324			break;
325		case LOG_ERR:
326			aplog_type = APLOG_ERR;
327			break;
328		case LOG_WARNING:
329			aplog_type = APLOG_WARNING;
330			break;
331		case LOG_NOTICE:
332			aplog_type = APLOG_NOTICE;
333			break;
334#if LOG_INFO != LOG_NOTICE
335		case LOG_INFO:
336			aplog_type = APLOG_INFO;
337			break;
338#endif
339#if LOG_NOTICE != LOG_DEBUG
340		case LOG_DEBUG:
341			aplog_type = APLOG_DEBUG;
342			break;
343#endif
344	}
345
346	if (ctx == NULL) { /* we haven't initialized our ctx yet, oh well */
347		ap_log_error(APLOG_MARK, APLOG_ERR | APLOG_STARTUP, 0, NULL, "%s", msg);
348	} else {
349		ap_log_rerror(APLOG_MARK, aplog_type, 0, ctx->r, "%s", msg);
350	}
351}
352
353static void php_apache_sapi_log_message_ex(char *msg, request_rec *r)
354{
355	if (r) {
356		ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, msg, r->filename);
357	} else {
358		php_apache_sapi_log_message(msg, -1);
359	}
360}
361
362static double php_apache_sapi_get_request_time(void)
363{
364	php_struct *ctx = SG(server_context);
365	return ((double) ctx->r->request_time) / 1000000.0;
366}
367
368extern zend_module_entry php_apache_module;
369
370static int php_apache2_startup(sapi_module_struct *sapi_module)
371{
372	if (php_module_startup(sapi_module, &php_apache_module, 1)==FAILURE) {
373		return FAILURE;
374	}
375	return SUCCESS;
376}
377
378static sapi_module_struct apache2_sapi_module = {
379	"apache2handler",
380	"Apache 2.0 Handler",
381
382	php_apache2_startup,				/* startup */
383	php_module_shutdown_wrapper,			/* shutdown */
384
385	NULL,						/* activate */
386	NULL,						/* deactivate */
387
388	php_apache_sapi_ub_write,			/* unbuffered write */
389	php_apache_sapi_flush,				/* flush */
390	php_apache_sapi_get_stat,			/* get uid */
391	php_apache_sapi_getenv,				/* getenv */
392
393	php_error,					/* error handler */
394
395	php_apache_sapi_header_handler,			/* header handler */
396	php_apache_sapi_send_headers,			/* send headers handler */
397	NULL,						/* send header handler */
398
399	php_apache_sapi_read_post,			/* read POST data */
400	php_apache_sapi_read_cookies,			/* read Cookies */
401
402	php_apache_sapi_register_variables,
403	php_apache_sapi_log_message,			/* Log message */
404	php_apache_sapi_get_request_time,		/* Request Time */
405	NULL,						/* Child Terminate */
406
407	STANDARD_SAPI_MODULE_PROPERTIES
408};
409
410static apr_status_t php_apache_server_shutdown(void *tmp)
411{
412	apache2_sapi_module.shutdown(&apache2_sapi_module);
413	sapi_shutdown();
414#ifdef ZTS
415	tsrm_shutdown();
416#endif
417	return APR_SUCCESS;
418}
419
420static apr_status_t php_apache_child_shutdown(void *tmp)
421{
422	apache2_sapi_module.shutdown(&apache2_sapi_module);
423#if defined(ZTS) && !defined(PHP_WIN32)
424	tsrm_shutdown();
425#endif
426	return APR_SUCCESS;
427}
428
429static void php_apache_add_version(apr_pool_t *p)
430{
431	if (PG(expose_php)) {
432		ap_add_version_component(p, "PHP/" PHP_VERSION);
433	}
434}
435
436static int php_pre_config(apr_pool_t *pconf, apr_pool_t *plog, apr_pool_t *ptemp)
437{
438#ifndef ZTS
439	int threaded_mpm;
440
441	ap_mpm_query(AP_MPMQ_IS_THREADED, &threaded_mpm);
442	if(threaded_mpm) {
443		ap_log_error(APLOG_MARK, APLOG_CRIT, 0, 0, "Apache is running a threaded MPM, but your PHP Module is not compiled to be threadsafe.  You need to recompile PHP.");
444		return DONE;
445	}
446#endif
447	/* When this is NULL, apache won't override the hard-coded default
448	 * php.ini path setting. */
449	apache2_php_ini_path_override = NULL;
450	return OK;
451}
452
453static int
454php_apache_server_startup(apr_pool_t *pconf, apr_pool_t *plog, apr_pool_t *ptemp, server_rec *s)
455{
456	void *data = NULL;
457	const char *userdata_key = "apache2hook_post_config";
458
459	/* Apache will load, unload and then reload a DSO module. This
460	 * prevents us from starting PHP until the second load. */
461	apr_pool_userdata_get(&data, userdata_key, s->process->pool);
462	if (data == NULL) {
463		/* We must use set() here and *not* setn(), otherwise the
464		 * static string pointed to by userdata_key will be mapped
465		 * to a different location when the DSO is reloaded and the
466		 * pointers won't match, causing get() to return NULL when
467		 * we expected it to return non-NULL. */
468		apr_pool_userdata_set((const void *)1, userdata_key, apr_pool_cleanup_null, s->process->pool);
469		return OK;
470	}
471
472	/* Set up our overridden path. */
473	if (apache2_php_ini_path_override) {
474		apache2_sapi_module.php_ini_path_override = apache2_php_ini_path_override;
475	}
476#ifdef ZTS
477	tsrm_startup(1, 1, 0, NULL);
478	(void)ts_resource(0);
479	ZEND_TSRMLS_CACHE_UPDATE();
480#endif
481
482	zend_signal_startup();
483
484	sapi_startup(&apache2_sapi_module);
485	apache2_sapi_module.startup(&apache2_sapi_module);
486	apr_pool_cleanup_register(pconf, NULL, php_apache_server_shutdown, apr_pool_cleanup_null);
487	php_apache_add_version(pconf);
488
489	return OK;
490}
491
492static apr_status_t php_server_context_cleanup(void *data_)
493{
494	void **data = data_;
495	*data = NULL;
496	return APR_SUCCESS;
497}
498
499static int php_apache_request_ctor(request_rec *r, php_struct *ctx)
500{
501	char *content_length;
502	const char *auth;
503
504	SG(sapi_headers).http_response_code = !r->status ? HTTP_OK : r->status;
505	SG(request_info).content_type = apr_table_get(r->headers_in, "Content-Type");
506	SG(request_info).query_string = apr_pstrdup(r->pool, r->args);
507	SG(request_info).request_method = r->method;
508	SG(request_info).proto_num = r->proto_num;
509	SG(request_info).request_uri = apr_pstrdup(r->pool, r->uri);
510	SG(request_info).path_translated = apr_pstrdup(r->pool, r->filename);
511	r->no_local_copy = 1;
512
513	content_length = (char *) apr_table_get(r->headers_in, "Content-Length");
514	if (content_length) {
515		ZEND_ATOL(SG(request_info).content_length, content_length);
516	} else {
517		SG(request_info).content_length = 0;
518	}
519
520	apr_table_unset(r->headers_out, "Content-Length");
521	apr_table_unset(r->headers_out, "Last-Modified");
522	apr_table_unset(r->headers_out, "Expires");
523	apr_table_unset(r->headers_out, "ETag");
524
525	auth = apr_table_get(r->headers_in, "Authorization");
526	php_handle_auth_data(auth);
527
528	if (SG(request_info).auth_user == NULL && r->user) {
529		SG(request_info).auth_user = estrdup(r->user);
530	}
531
532	ctx->r->user = apr_pstrdup(ctx->r->pool, SG(request_info).auth_user);
533
534	return php_request_startup();
535}
536
537static void php_apache_request_dtor(request_rec *r)
538{
539	php_request_shutdown(NULL);
540}
541
542static void php_apache_ini_dtor(request_rec *r, request_rec *p)
543{
544	if (strcmp(r->protocol, "INCLUDED")) {
545		zend_try { zend_ini_deactivate(); } zend_end_try();
546	} else {
547typedef struct {
548	HashTable config;
549} php_conf_rec;
550		zend_string *str;
551		php_conf_rec *c = ap_get_module_config(r->per_dir_config, &php7_module);
552
553		ZEND_HASH_FOREACH_STR_KEY(&c->config, str) {
554			zend_restore_ini_entry(str, ZEND_INI_STAGE_SHUTDOWN);
555		} ZEND_HASH_FOREACH_END();
556	}
557	if (p) {
558		((php_struct *)SG(server_context))->r = p;
559	} else {
560		apr_pool_cleanup_run(r->pool, (void *)&SG(server_context), php_server_context_cleanup);
561	}
562}
563
564static int php_handler(request_rec *r)
565{
566	php_struct * volatile ctx;
567	void *conf;
568	apr_bucket_brigade * volatile brigade;
569	apr_bucket *bucket;
570	apr_status_t rv;
571	request_rec * volatile parent_req = NULL;
572#ifdef ZTS
573	/* initial resource fetch */
574	(void)ts_resource(0);
575	ZEND_TSRMLS_CACHE_UPDATE();
576#endif
577
578#define PHPAP_INI_OFF php_apache_ini_dtor(r, parent_req);
579
580	conf = ap_get_module_config(r->per_dir_config, &php7_module);
581
582	/* apply_config() needs r in some cases, so allocate server_context early */
583	ctx = SG(server_context);
584	if (ctx == NULL || (ctx && ctx->request_processed && !strcmp(r->protocol, "INCLUDED"))) {
585normal:
586		ctx = SG(server_context) = apr_pcalloc(r->pool, sizeof(*ctx));
587		/* register a cleanup so we clear out the SG(server_context)
588		 * after each request. Note: We pass in the pointer to the
589		 * server_context in case this is handled by a different thread.
590		 */
591		apr_pool_cleanup_register(r->pool, (void *)&SG(server_context), php_server_context_cleanup, apr_pool_cleanup_null);
592		ctx->r = r;
593		ctx = NULL; /* May look weird to null it here, but it is to catch the right case in the first_try later on */
594	} else {
595		parent_req = ctx->r;
596		ctx->r = r;
597	}
598	apply_config(conf);
599
600	if (strcmp(r->handler, PHP_MAGIC_TYPE) && strcmp(r->handler, PHP_SOURCE_MAGIC_TYPE) && strcmp(r->handler, PHP_SCRIPT)) {
601		/* Check for xbithack in this case. */
602		if (!AP2(xbithack) || strcmp(r->handler, "text/html") || !(r->finfo.protection & APR_UEXECUTE)) {
603			PHPAP_INI_OFF;
604			return DECLINED;
605		}
606	}
607
608	/* Give a 404 if PATH_INFO is used but is explicitly disabled in
609	 * the configuration; default behaviour is to accept. */
610	if (r->used_path_info == AP_REQ_REJECT_PATH_INFO
611		&& r->path_info && r->path_info[0]) {
612		PHPAP_INI_OFF;
613		return HTTP_NOT_FOUND;
614	}
615
616	/* handle situations where user turns the engine off */
617	if (!AP2(engine)) {
618		PHPAP_INI_OFF;
619		return DECLINED;
620	}
621
622	if (r->finfo.filetype == 0) {
623		php_apache_sapi_log_message_ex("script '%s' not found or unable to stat", r);
624		PHPAP_INI_OFF;
625		return HTTP_NOT_FOUND;
626	}
627	if (r->finfo.filetype == APR_DIR) {
628		php_apache_sapi_log_message_ex("attempt to invoke directory '%s' as script", r);
629		PHPAP_INI_OFF;
630		return HTTP_FORBIDDEN;
631	}
632
633	/* Setup the CGI variables if this is the main request */
634	if (r->main == NULL ||
635		/* .. or if the sub-request environment differs from the main-request. */
636		r->subprocess_env != r->main->subprocess_env
637	) {
638		/* setup standard CGI variables */
639		ap_add_common_vars(r);
640		ap_add_cgi_vars(r);
641	}
642
643zend_first_try {
644
645	if (ctx == NULL) {
646		brigade = apr_brigade_create(r->pool, r->connection->bucket_alloc);
647		ctx = SG(server_context);
648		ctx->brigade = brigade;
649
650		if (php_apache_request_ctor(r, ctx)!=SUCCESS) {
651			zend_bailout();
652		}
653	} else {
654		if (!parent_req) {
655			parent_req = ctx->r;
656		}
657		if (parent_req && parent_req->handler &&
658				strcmp(parent_req->handler, PHP_MAGIC_TYPE) &&
659				strcmp(parent_req->handler, PHP_SOURCE_MAGIC_TYPE) &&
660				strcmp(parent_req->handler, PHP_SCRIPT)) {
661			if (php_apache_request_ctor(r, ctx)!=SUCCESS) {
662				zend_bailout();
663			}
664		}
665
666		/*
667		 * check if coming due to ErrorDocument
668		 * We make a special exception of 413 (Invalid POST request) as the invalidity of the request occurs
669		 * during processing of the request by PHP during POST processing. Therefor we need to re-use the exiting
670		 * PHP instance to handle the request rather then creating a new one.
671		*/
672		if (parent_req && parent_req->status != HTTP_OK && parent_req->status != 413 && strcmp(r->protocol, "INCLUDED")) {
673			parent_req = NULL;
674			goto normal;
675		}
676		ctx->r = r;
677		brigade = ctx->brigade;
678	}
679
680	if (AP2(last_modified)) {
681		ap_update_mtime(r, r->finfo.mtime);
682		ap_set_last_modified(r);
683	}
684
685	/* Determine if we need to parse the file or show the source */
686	if (strncmp(r->handler, PHP_SOURCE_MAGIC_TYPE, sizeof(PHP_SOURCE_MAGIC_TYPE) - 1) == 0) {
687		zend_syntax_highlighter_ini syntax_highlighter_ini;
688		php_get_highlight_struct(&syntax_highlighter_ini);
689		highlight_file((char *)r->filename, &syntax_highlighter_ini);
690	} else {
691		zend_file_handle zfd;
692
693		zfd.type = ZEND_HANDLE_FILENAME;
694		zfd.filename = (char *) r->filename;
695		zfd.free_filename = 0;
696		zfd.opened_path = NULL;
697
698		if (!parent_req) {
699			php_execute_script(&zfd);
700		} else {
701			zend_execute_scripts(ZEND_INCLUDE, NULL, 1, &zfd);
702		}
703
704		apr_table_set(r->notes, "mod_php_memory_usage",
705			apr_psprintf(ctx->r->pool, "%" APR_SIZE_T_FMT, zend_memory_peak_usage(1)));
706	}
707
708} zend_end_try();
709
710	if (!parent_req) {
711		php_apache_request_dtor(r);
712		ctx->request_processed = 1;
713		apr_brigade_cleanup(brigade);
714		bucket = apr_bucket_eos_create(r->connection->bucket_alloc);
715		APR_BRIGADE_INSERT_TAIL(brigade, bucket);
716
717		rv = ap_pass_brigade(r->output_filters, brigade);
718		if (rv != APR_SUCCESS || r->connection->aborted) {
719zend_first_try {
720			php_handle_aborted_connection();
721} zend_end_try();
722		}
723		apr_brigade_cleanup(brigade);
724		apr_pool_cleanup_run(r->pool, (void *)&SG(server_context), php_server_context_cleanup);
725	} else {
726		ctx->r = parent_req;
727	}
728
729	return OK;
730}
731
732static void php_apache_child_init(apr_pool_t *pchild, server_rec *s)
733{
734	apr_pool_cleanup_register(pchild, NULL, php_apache_child_shutdown, apr_pool_cleanup_null);
735}
736
737#ifdef ZEND_SIGNALS
738static void php_apache_signal_init(apr_pool_t *pchild, server_rec *s)
739{
740	zend_signal_init();
741}
742#endif
743
744void php_ap2_register_hook(apr_pool_t *p)
745{
746	ap_hook_pre_config(php_pre_config, NULL, NULL, APR_HOOK_MIDDLE);
747	ap_hook_post_config(php_apache_server_startup, NULL, NULL, APR_HOOK_MIDDLE);
748	ap_hook_handler(php_handler, NULL, NULL, APR_HOOK_MIDDLE);
749#ifdef ZEND_SIGNALS
750	ap_hook_child_init(php_apache_signal_init, NULL, NULL, APR_HOOK_MIDDLE);
751#endif
752	ap_hook_child_init(php_apache_child_init, NULL, NULL, APR_HOOK_MIDDLE);
753}
754
755/*
756 * Local variables:
757 * tab-width: 4
758 * c-basic-offset: 4
759 * End:
760 * vim600: sw=4 ts=4 fdm=marker
761 * vim<600: sw=4 ts=4
762 */
763