2 suPHP - (c)2002-2005 Sebastian Marsching <sebastian@marsching.com>
4 This file is part of suPHP.
6 suPHP is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2 of the License, or
9 (at your option) any later version.
11 suPHP is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
16 You should have received a copy of the GNU General Public License
17 along with suPHP; if not, write to the Free Software
18 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
22 #include "apr_strings.h"
23 #include "apr_thread_proc.h"
24 #include "apr_buckets.h"
30 #include "http_config.h"
31 #include "http_core.h"
34 #include "util_script.h"
35 #include "util_filter.h"
37 /* needed for get_suexec_identity hook */
40 module AP_MODULE_DECLARE_DATA suphp_module;
43 /*********************
45 *********************/
47 static apr_status_t suphp_log_script_err(request_rec *r, apr_file_t *script_err)
49 char argsbuffer[HUGE_STRING_LEN];
53 while ((rv = apr_file_gets(argsbuffer, HUGE_STRING_LEN,
54 script_err)) == APR_SUCCESS) {
55 newline = strchr(argsbuffer, '\n');
59 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
66 char *suphp_brigade_read(apr_pool_t *p, apr_bucket_brigade *bb, int bytes)
77 target_buf = (char *) apr_palloc(p, bytes + 1);
78 next_byte = target_buf;
79 last_byte = target_buf + bytes;
81 for (b = APR_BRIGADE_FIRST(bb); b != APR_BRIGADE_SENTINEL(bb); b = APR_BUCKET_NEXT(b)) {
85 if (apr_bucket_read(b, &buf, &size, APR_BLOCK_READ) == APR_SUCCESS) {
86 for (i = 0; i < size; i++) {
90 if (next_byte == last_byte) {
102 /**************************
103 Configuration processing
104 **************************/
106 #define SUPHP_CONFIG_MODE_SERVER 1
107 #define SUPHP_CONFIG_MODE_DIRECTORY 2
109 #define SUPHP_ENGINE_OFF 0
110 #define SUPHP_ENGINE_ON 1
111 #define SUPHP_ENGINE_UNDEFINED 2
113 #ifndef SUPHP_PATH_TO_SUPHP
114 #define SUPHP_PATH_TO_SUPHP "/usr/sbin/suphp"
118 int engine; // Status of suPHP_Engine
120 int cmode; // Server of directory configuration?
121 #ifdef SUPHP_USE_USERGROUP
125 apr_table_t *handlers;
129 static void *suphp_create_dir_config(apr_pool_t *p, char *dir)
131 suphp_conf *cfg = (suphp_conf *) apr_pcalloc(p, sizeof(suphp_conf));
133 cfg->php_config = NULL;
134 cfg->engine = SUPHP_ENGINE_UNDEFINED;
135 cfg->cmode = SUPHP_CONFIG_MODE_DIRECTORY;
137 #ifdef SUPHP_USE_USERGROUP
138 cfg->target_user = NULL;
139 cfg->target_group = NULL;
142 /* Create table with 0 initial elements */
143 /* This size may be increased for performance reasons */
144 cfg->handlers = apr_table_make(p, 0);
150 static void *suphp_merge_dir_config(apr_pool_t *p, void *base,
153 suphp_conf *parent = (suphp_conf *) base;
154 suphp_conf *child = (suphp_conf *) overrides;
155 suphp_conf *merged = (suphp_conf *) apr_pcalloc(p, sizeof(suphp_conf));
157 merged->cmode = SUPHP_CONFIG_MODE_DIRECTORY;
159 if (child->php_config)
160 merged->php_config = apr_pstrdup(p, child->php_config);
161 else if (parent->php_config)
162 merged->php_config = apr_pstrdup(p, parent->php_config);
164 merged->php_config = NULL;
166 if (child->engine != SUPHP_ENGINE_UNDEFINED)
167 merged->engine = child->engine;
169 merged->engine = parent->engine;
171 #ifdef SUPHP_USE_USERGROUP
172 if (child->target_user)
173 merged->target_user = apr_pstrdup(p, child->target_user);
174 else if (parent->target_user)
175 merged->target_user = apr_pstrdup(p, parent->target_user);
177 merged->target_user = NULL;
179 if (child->target_group)
180 merged->target_group = apr_pstrdup(p, child->target_group);
181 else if (parent->target_group)
182 merged->target_group = apr_pstrdup(p, parent->target_group);
184 merged->target_group = NULL;
187 merged->handlers = apr_table_overlay(p, child->handlers, parent->handlers);
189 return (void *) merged;
193 static void *suphp_create_server_config(apr_pool_t *p, server_rec *s)
195 suphp_conf *cfg = (suphp_conf *) apr_pcalloc(p, sizeof(suphp_conf));
197 cfg->engine = SUPHP_ENGINE_UNDEFINED;
198 cfg->cmode = SUPHP_CONFIG_MODE_SERVER;
204 static void *suphp_merge_server_config(apr_pool_t *p, void *base,
207 suphp_conf *parent = (suphp_conf *) base;
208 suphp_conf *child = (suphp_conf *) overrides;
209 suphp_conf *merged = (suphp_conf *) apr_pcalloc(p, sizeof(suphp_conf));
211 if (child->engine != SUPHP_ENGINE_UNDEFINED)
212 merged->engine = child->engine;
214 merged->engine = parent->engine;
216 #ifdef SUPHP_USE_USERGROUP
217 if (child->target_user)
218 merged->target_user = apr_pstrdup(p, child->target_user);
219 else if (parent->target_user)
220 merged->target_user = apr_pstrdup(p, parent->target_user);
222 merged->target_user = NULL;
224 if (child->target_group)
225 merged->target_group = apr_pstrdup(p, child->target_group);
226 else if (parent->target_group)
227 merged->target_group = apr_pstrdup(p, parent->target_group);
229 merged->target_group = NULL;
232 return (void*) merged;
240 static const char *suphp_handle_cmd_engine(cmd_parms *cmd, void *mconfig,
243 server_rec *s = cmd->server;
247 cfg = (suphp_conf *) mconfig;
249 cfg = (suphp_conf *) ap_get_module_config(s->module_config, &suphp_module);
252 cfg->engine = SUPHP_ENGINE_ON;
254 cfg->engine = SUPHP_ENGINE_OFF;
260 static const char *suphp_handle_cmd_config(cmd_parms *cmd, void *mconfig,
263 server_rec *s = cmd->server;
267 cfg = (suphp_conf *) mconfig;
269 cfg = (suphp_conf *) ap_get_module_config(s->module_config, &suphp_module);
271 cfg->php_config = apr_pstrdup(cmd->pool, arg);
277 #ifdef SUPHP_USE_USERGROUP
278 static const char *suphp_handle_cmd_user_group(cmd_parms *cmd, void *mconfig,
279 const char *arg1, const char *arg2)
281 suphp_conf *cfg = (suphp_conf *) mconfig;
283 cfg->target_user = apr_pstrdup(cmd->pool, arg1);
284 cfg->target_group = apr_pstrdup(cmd->pool, arg2);
291 static const char *suphp_handle_cmd_add_handler(cmd_parms *cmd, void *mconfig,
294 suphp_conf *cfg = (suphp_conf *) mconfig;
295 // Mark active handler with '1'
296 apr_table_set(cfg->handlers, arg, "1");
302 static const char *suphp_handle_cmd_remove_handler(cmd_parms *cmd,
306 suphp_conf *cfg = (suphp_conf *) mconfig;
307 // Mark deactivated handler with '0'
308 apr_table_set(cfg->handlers, arg, "0");
314 static const command_rec suphp_cmds[] =
316 AP_INIT_FLAG("suPHP_Engine", suphp_handle_cmd_engine, NULL, RSRC_CONF | ACCESS_CONF,
317 "Whether suPHP is on or off, default is off"),
318 AP_INIT_TAKE1("suPHP_ConfigPath", suphp_handle_cmd_config, NULL, OR_OPTIONS,
319 "Wheres the php.ini resides, default is the PHP default"),
320 #ifdef SUPHP_USE_USERGROUP
321 AP_INIT_TAKE2("suPHP_UserGroup", suphp_handle_cmd_user_group, NULL, RSRC_CONF | ACCESS_CONF,
322 "User and group scripts shall be run as"),
324 AP_INIT_ITERATE("suPHP_AddHandler", suphp_handle_cmd_add_handler, NULL, ACCESS_CONF, "Tells mod_suphp to handle these MIME-types"),
325 AP_INIT_ITERATE("suPHP_RemoveHandler", suphp_handle_cmd_remove_handler, NULL, ACCESS_CONF, "Tells mod_suphp not to handle these MIME-types"),
329 /*****************************************
330 Code for reading script's stdout/stderr
331 based on mod_cgi's code
332 *****************************************/
334 #if APR_FILES_AS_SOCKETS
336 static const apr_bucket_type_t bucket_type_suphp;
338 struct suphp_bucket_data {
339 apr_pollset_t *pollset;
343 static apr_bucket *suphp_bucket_create(request_rec *r, apr_file_t *out, apr_file_t *err, apr_bucket_alloc_t *list)
345 apr_bucket *b = apr_bucket_alloc(sizeof(*b), list);
348 struct suphp_bucket_data *data = apr_palloc(r->pool, sizeof(*data));
351 b->free = apr_bucket_free;
353 b->type = &bucket_type_suphp;
354 b->length = (apr_size_t) (-1);
357 /* Create the pollset */
358 rv = apr_pollset_create(&data->pollset, 2, r->pool, 0);
359 AP_DEBUG_ASSERT(rv == APR_SUCCESS);
361 fd.desc_type = APR_POLL_FILE;
362 fd.reqevents = APR_POLLIN;
364 fd.desc.f = out; /* script's stdout */
365 fd.client_data = (void *) 1;
366 rv = apr_pollset_add(data->pollset, &fd);
367 AP_DEBUG_ASSERT(rv == APR_SUCCESS);
369 fd.desc.f = err; /* script's stderr */
370 fd.client_data = (void *) 2;
371 rv = apr_pollset_add(data->pollset, &fd);
372 AP_DEBUG_ASSERT(rv == APR_SUCCESS);
379 static apr_bucket *suphp_bucket_dup(struct suphp_bucket_data *data, apr_bucket_alloc_t *list)
381 apr_bucket *b = apr_bucket_alloc(sizeof(*b), list);
383 b->free = apr_bucket_free;
385 b->type = &bucket_type_suphp;
386 b->length = (apr_size_t) (-1);
392 /* This utility method is needed, because APR's implementation for the
393 pipe bucket cannot handle or special bucket type */
394 static apr_status_t suphp_read_fd(apr_bucket *b, apr_file_t *fd, const char **str, apr_size_t *len)
400 *len = APR_BUCKET_BUFF_SIZE;
401 buf = apr_bucket_alloc(*len, b->list);
403 rv = apr_file_read(fd, buf, len);
407 struct suphp_bucket_data *data = b->data;
410 /* Change the current bucket to refer to what we read
411 and append the pipe bucket after it */
412 b = apr_bucket_heap_make(b, buf, *len, apr_bucket_free);
413 /* Here, b->data is the new heap bucket data */
415 h->alloc_len = APR_BUCKET_BUFF_SIZE; /* note the real buffer size */
417 APR_BUCKET_INSERT_AFTER(b, suphp_bucket_dup(data, b->list));
420 apr_bucket_free(buf);
421 b = apr_bucket_immortal_make(b, "", 0);
422 /* Here, b->data is the reference to the empty string */
428 /* Poll on stdout and stderr to make sure the process does not block
429 because of a full system (stderr) buffer */
430 static apr_status_t suphp_bucket_read(apr_bucket *b, const char **str, apr_size_t *len, apr_read_type_e block) {
431 struct suphp_bucket_data *data = b->data;
432 apr_interval_time_t timeout;
436 timeout = (block == APR_NONBLOCK_READ) ? 0 : data->r->server->timeout;
439 const apr_pollfd_t *results;
442 rv = apr_pollset_poll(data->pollset, timeout, &num, &results);
443 if (APR_STATUS_IS_TIMEUP(rv)) {
444 return (timeout == 0) ? APR_EAGAIN : rv;
445 } else if (APR_STATUS_IS_EINTR(rv)) {
447 } else if (rv != APR_SUCCESS) {
448 ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, data->r, "Poll failed waiting for suPHP child process");
453 if (results[0].client_data == (void *) 1) {
455 rv = suphp_read_fd(b, results[0].desc.f, str, len);
456 if (APR_STATUS_IS_EOF(rv)) {
462 apr_status_t rv2 = suphp_log_script_err(data->r, results[0].desc.f);
463 if (APR_STATUS_IS_EOF(rv2)) {
464 apr_pollset_remove(data->pollset, &results[0]);
475 static const apr_bucket_type_t bucket_type_suphp = {
476 "SUPHP", 5, APR_BUCKET_DATA,
477 apr_bucket_destroy_noop,
479 apr_bucket_setaside_notimpl,
480 apr_bucket_split_notimpl,
481 apr_bucket_copy_notimpl
486 static void suphp_discard_output(apr_bucket_brigade *bb) {
491 for (b = APR_BRIGADE_FIRST(bb); b != APR_BRIGADE_SENTINEL(bb); b = APR_BUCKET_NEXT(b)) {
492 if (APR_BUCKET_IS_EOS(b)) {
495 rv = apr_bucket_read(b, &buf, &len, APR_BLOCK_READ);
496 if (rv != APR_SUCCESS) {
507 static int suphp_handler(request_rec *r)
512 core_dir_config *core_conf;
516 apr_procattr_t *procattr;
524 #if MAX_STRING_LEN < 1024
527 char strbuf[MAX_STRING_LEN];
532 char *auth_user = NULL;
533 char *auth_pass = NULL;
535 #ifdef SUPHP_USE_USERGROUP
536 char *ud_user = NULL;
537 char *ud_group = NULL;
540 apr_bucket_brigade *bb;
543 /* load configuration */
545 p = r->main ? r->main->pool : r->pool;
546 sconf = ap_get_module_config(r->server->module_config, &suphp_module);
547 dconf = ap_get_module_config(r->per_dir_config, &suphp_module);
548 core_conf = (core_dir_config *) ap_get_module_config(r->per_dir_config, &core_module);
550 /* only handle request if mod_suphp is active for this handler */
551 /* check only first byte of value (second has to be \0) */
552 if ((apr_table_get(dconf->handlers, r->handler) == NULL)
553 || (*(apr_table_get(dconf->handlers, r->handler)) == '0'))
556 /* check if suPHP is enabled for this request */
558 if (((sconf->engine != SUPHP_ENGINE_ON)
559 && (dconf->engine != SUPHP_ENGINE_ON))
560 || ((sconf->engine == SUPHP_ENGINE_ON)
561 && (dconf->engine == SUPHP_ENGINE_OFF)))
564 /* check if file is existing and acessible */
566 rv = apr_stat(&finfo, apr_pstrdup(p, r->filename), APR_FINFO_NORM, p);
568 if (rv == APR_SUCCESS)
570 else if (rv == EACCES)
572 return HTTP_FORBIDDEN;
573 ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r, "access to %s denied", r->filename);
575 else if (rv == ENOENT)
577 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "File does not exist: %s", r->filename);
578 return HTTP_NOT_FOUND;
582 ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r, "could not get fileinfo: %s", r->filename);
583 return HTTP_NOT_FOUND;
586 if (!(r->finfo.protection & APR_UREAD))
588 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "Insufficient permissions: %s", r->filename);
589 return HTTP_FORBIDDEN;
592 #ifdef SUPHP_USE_USERGROUP
593 if ((sconf->target_user == NULL || sconf->target_group == NULL)
594 && (dconf->target_user == NULL || dconf->target_group == NULL))
596 /* Check for userdir request */
597 ap_unix_identity_t *userdir_id = NULL;
598 userdir_id = ap_run_get_suexec_identity(r);
599 if (userdir_id != NULL && userdir_id->userdir) {
600 ud_user = apr_psprintf(r->pool, "#%ld", (long) userdir_id->uid);
601 ud_group = apr_psprintf(r->pool, "#%ld", (long) userdir_id->gid);
603 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
604 "No user or group set - set suPHP_UserGroup");
605 return HTTP_INTERNAL_SERVER_ERROR;
610 /* prepare argv for new process */
612 argv = apr_palloc(p, 2 * sizeof(char *));
613 argv[0] = SUPHP_PATH_TO_SUPHP;
616 /* prepare environment for new process */
618 ap_add_common_vars(r);
621 apr_table_unset(r->subprocess_env, "SUPHP_PHP_CONFIG");
622 apr_table_unset(r->subprocess_env, "SUPHP_AUTH_USER");
623 apr_table_unset(r->subprocess_env, "SUPHP_AUTH_PW");
625 #ifdef SUPHP_USE_USERGROUP
626 apr_table_unset(r->subprocess_env, "SUPHP_USER");
627 apr_table_unset(r->subprocess_env, "SUPHP_GROUP");
630 if (dconf->php_config)
632 apr_table_setn(r->subprocess_env, "SUPHP_PHP_CONFIG", apr_pstrdup(p, dconf->php_config));
635 apr_table_setn(r->subprocess_env, "SUPHP_HANDLER", r->handler);
639 const char *auth = NULL;
640 auth = apr_table_get(r->headers_in, "Authorization");
641 if (auth && auth[0] != 0 && strncmp(auth, "Basic ", 6) == 0)
645 user = ap_pbase64decode(p, auth + 6);
648 pass = strchr(user, ':');
652 auth_user = apr_pstrdup(r->pool, user);
653 auth_pass = apr_pstrdup(r->pool, pass);
659 if (auth_user && auth_pass)
661 apr_table_setn(r->subprocess_env, "SUPHP_AUTH_USER", auth_user);
662 apr_table_setn(r->subprocess_env, "SUPHP_AUTH_PW", auth_pass);
665 #ifdef SUPHP_USE_USERGROUP
666 if (dconf->target_user)
668 apr_table_setn(r->subprocess_env, "SUPHP_USER",
669 apr_pstrdup(r->pool, dconf->target_user));
671 else if (sconf->target_user)
673 apr_table_setn(r->subprocess_env, "SUPHP_USER",
674 apr_pstrdup(r->pool, sconf->target_user));
678 apr_table_setn(r->subprocess_env, "SUPHP_USER",
679 apr_pstrdup(r->pool, ud_user));
682 if (dconf->target_group)
684 apr_table_setn(r->subprocess_env, "SUPHP_GROUP",
685 apr_pstrdup(r->pool, dconf->target_group));
687 else if (sconf->target_group)
689 apr_table_setn(r->subprocess_env, "SUPHP_GROUP",
690 apr_pstrdup(r->pool, sconf->target_group));
694 apr_table_setn(r->subprocess_env, "SUPHP_GROUP",
695 apr_pstrdup(r->pool, ud_group));
699 env = ap_create_environment(p, r->subprocess_env);
701 /* set attributes for new process */
703 if (((rv = apr_procattr_create(&procattr, p)) != APR_SUCCESS)
704 || ((rv = apr_procattr_io_set(procattr, APR_CHILD_BLOCK, APR_CHILD_BLOCK, APR_CHILD_BLOCK)) != APR_SUCCESS)
705 || ((rv = apr_procattr_dir_set(procattr, ap_make_dirstr_parent(r->pool, r->filename))) != APR_SUCCESS)
707 /* set resource limits */
710 || ((rv = apr_procattr_limit_set(procattr, APR_LIMIT_CPU, core_conf->limit_cpu)) != APR_SUCCESS)
712 #if defined(RLIMIT_DATA) || defined(RLIMIT_VMEM) || defined(RLIMIT_AS)
713 || ((rv = apr_procattr_limit_set(procattr, APR_LIMIT_MEM, core_conf->limit_mem)) != APR_SUCCESS)
716 || ((apr_procattr_limit_set(procattr, APR_LIMIT_NPROC, core_conf->limit_nproc)) != APR_SUCCESS)
719 || ((apr_procattr_cmdtype_set(procattr, APR_PROGRAM)) != APR_SUCCESS)
720 || ((apr_procattr_error_check_set(procattr, 1)) != APR_SUCCESS)
721 || ((apr_procattr_detach_set(procattr, 0)) != APR_SUCCESS))
723 ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r,
724 "couldn't set child process attributes: %s", r->filename);
725 return HTTP_INTERNAL_SERVER_ERROR;
728 /* create new process */
731 proc = apr_pcalloc(p, sizeof(*proc));
732 rv = apr_proc_create(proc, SUPHP_PATH_TO_SUPHP, (const char *const *)argv, (const char *const *)env, procattr, p);
733 if (rv != APR_SUCCESS)
735 ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r,
736 "couldn't create child process: %s for %s", SUPHP_PATH_TO_SUPHP, r->filename);
737 return HTTP_INTERNAL_SERVER_ERROR;
739 apr_pool_note_subprocess(p, proc, APR_KILL_AFTER_TIMEOUT);
743 apr_file_pipe_timeout_set(proc->out, r->server->timeout);
747 apr_file_pipe_timeout_set(proc->in, r->server->timeout);
751 apr_file_pipe_timeout_set(proc->err, r->server->timeout);
753 /* send request body to script */
755 bb = apr_brigade_create(r->pool, r->connection->bucket_alloc);
759 rv = ap_get_brigade(r->input_filters, bb, AP_MODE_READBYTES, APR_BLOCK_READ, HUGE_STRING_LEN);
761 if (rv != APR_SUCCESS)
766 for (bucket = APR_BRIGADE_FIRST(bb); bucket != APR_BRIGADE_SENTINEL(bb); bucket = APR_BUCKET_NEXT(bucket))
770 int child_stopped_reading = 0;
772 if (APR_BUCKET_IS_EOS(bucket))
778 if (APR_BUCKET_IS_FLUSH(bucket) || child_stopped_reading)
783 apr_bucket_read(bucket, &data, &len, APR_BLOCK_READ);
785 rv = apr_file_write_full(proc->in, data, len, NULL);
786 if (rv != APR_SUCCESS)
788 child_stopped_reading = 1;
791 apr_brigade_cleanup(bb);
793 while (!eos_reached);
795 apr_file_flush(proc->in);
796 apr_file_close(proc->in);
798 /* get output from script and check if non-parsed headers are used */
800 #if APR_FILES_AS_SOCKETS
801 apr_file_pipe_timeout_set(proc->out, 0);
802 apr_file_pipe_timeout_set(proc->err, 0);
803 b = suphp_bucket_create(r, proc->out, proc->err, r->connection->bucket_alloc);
805 b = apr_bucket_pipe_create(proc->out, r->connection->bucket_alloc);
808 APR_BRIGADE_INSERT_TAIL(bb, b);
810 b = apr_bucket_eos_create(r->connection->bucket_alloc);
811 APR_BRIGADE_INSERT_TAIL(bb, b);
813 tmpbuf = suphp_brigade_read(p, bb, 8);
814 if (strlen(tmpbuf) == 8 && !(strncmp(tmpbuf, "HTTP/1.0", 8) && strncmp(tmpbuf, "HTTP/1.1", 8)))
821 /* normal cgi headers, so we have to create the real headers ourselves */
824 const char *location;
826 ret = ap_scan_script_header_err_brigade(r, bb, strbuf);
827 if (ret == HTTP_NOT_MODIFIED)
831 else if (ret != APR_SUCCESS)
833 suphp_discard_output(bb);
834 apr_brigade_destroy(bb);
835 suphp_log_script_err(r, proc->err);
837 /* ap_scan_script_header_err_brigade does logging itself,
840 return HTTP_INTERNAL_SERVER_ERROR;
843 location = apr_table_get(r->headers_out, "Location");
844 if (location && location[0] == '/' && r->status == 200)
846 /* empty brigade (script output) and modify headers */
848 suphp_discard_output(bb);
849 apr_brigade_destroy(bb);
850 suphp_log_script_err(r, proc->err);
851 r->method = apr_pstrdup(r->pool, "GET");
852 r->method_number = M_GET;
853 apr_table_unset(r->headers_in, "Content-Length");
855 ap_internal_redirect_handler(location, r);
858 else if (location && r->status == 200)
860 /* empty brigade (script output) */
861 suphp_discard_output(bb);
862 apr_brigade_destroy(bb);
863 suphp_log_script_err(r, proc->err);
864 return HTTP_MOVED_TEMPORARILY;
867 /* send output to browser (through filters) */
869 rv = ap_pass_brigade(r->output_filters, bb);
871 /* write errors to logfile */
873 if (rv == APR_SUCCESS && !r->connection->aborted)
874 suphp_log_script_err(r, proc->err);
876 apr_file_close(proc->err);
879 if (proc->out && nph)
881 /* use non-parsed headers (direct output) */
883 struct ap_filter_t *cur;
885 /* get rid of output filters */
887 cur = r->proto_output_filters;
888 while (cur && cur->frec->ftype < AP_FTYPE_CONNECTION)
892 r->output_filters = r->proto_output_filters = cur;
894 /* send output to browser (directly) */
896 rv = ap_pass_brigade(r->output_filters, bb);
899 if (rv == APR_SUCCESS && !r->connection->aborted)
900 suphp_log_script_err(r, proc->err);
902 apr_file_close(proc->err);
908 static void suphp_register_hooks(apr_pool_t *p)
910 ap_hook_handler(suphp_handler, NULL, NULL, APR_HOOK_MIDDLE);
914 /********************
916 ********************/
918 module AP_MODULE_DECLARE_DATA suphp_module =
920 STANDARD20_MODULE_STUFF,
921 suphp_create_dir_config,
922 suphp_merge_dir_config,
923 suphp_create_server_config,
924 suphp_merge_server_config,