-/* ====================================================================
- * The Apache Software License, Version 1.1
- *
- * Copyright (c) 2000 The Apache Software Foundation. All rights
- * reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- *
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- *
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in
- * the documentation and/or other materials provided with the
- * distribution.
- *
- * 3. The end-user documentation included with the redistribution,
- * if any, must include the following acknowledgment:
- * "This product includes software developed by the
- * Apache Software Foundation (http://www.apache.org/)."
- * Alternately, this acknowledgment may appear in the software itself,
- * if and wherever such third-party acknowledgments normally appear.
- *
- * 4. The names "Apache" and "Apache Software Foundation" must
- * not be used to endorse or promote products derived from this
- * software without prior written permission. For written
- * permission, please contact apache@apache.org.
- *
- * 5. Products derived from this software may not be called "Apache",
- * nor may "Apache" appear in their name, without prior written
- * permission of the Apache Software Foundation.
- *
- * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
- * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
- * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
- * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
- * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
- * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
- * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
- * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
- * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
- * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
- * SUCH DAMAGE.
- * ====================================================================
- *
- * This software consists of voluntary contributions made by many
- * individuals on behalf of the Apache Software Foundation. For more
- * information on the Apache Software Foundation, please see
- * <http://www.apache.org/>.
- *
- * Portions of this software are based upon public domain software
- * originally written at the National Center for Supercomputing Applications,
- * University of Illinois, Urbana-Champaign.
- */
-
/*
- * mod_suphp is based on mod_cgi from the original Apache sources
- * Code for authorization environment variables is partly taken from mod_php
- *
- * mod_suphp was written by Sebastian Marsching <sebastian@marsching.com>
- * Feel free to contact me if you have bug-reports or suggestions
- */
+ suPHP - (c)2002-2005 Sebastian Marsching <sebastian@marsching.com>
+
+ This file is part of suPHP.
+
+ suPHP is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ suPHP is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with suPHP; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+*/
+
+
+#define CORE_PRIVATE
#include "httpd.h"
#include "http_config.h"
#include "http_main.h"
#include "http_log.h"
#include "util_script.h"
-#include "http_conf_globals.h"
-#ifndef PATH_TO_SUPHP
-#define PATH_TO_SUPHP "/usr/sbin/suphp"
+
+#define SUPHP_CONFIG_MODE_SERVER 1
+#define SUPHP_CONFIG_MODE_DIRECTORY 2
+
+#define SUPHP_ENGINE_OFF 0
+#define SUPHP_ENGINE_ON 1
+#define SUPHP_ENGINE_UNDEFINED 2
+
+#ifndef SUPHP_PATH_TO_SUPHP
+#define SUPHP_PATH_TO_SUPHP "/usr/sbin/suphp"
#endif
+
+/* Module declaration */
module MODULE_VAR_EXPORT suphp_module;
-/* KLUDGE --- for back-combatibility, we don't have to check ExecCGI
- * in ScriptAliased directories, which means we need to know if this
- * request came through ScriptAlias or not... so the Alias module
- * leaves a note for us.
- */
-static int is_scriptaliased(request_rec *r)
-{
- const char *t = ap_table_get(r->notes, "alias-forced-type");
- return t && (!strcasecmp(t, "cgi-script"));
-}
+/* Configuration structure */
+typedef struct {
+ int engine; // Status of suPHP_Engine
+ char *php_config;
+ int cmode; // Server of directory configuration?
+#ifdef SUPHP_USE_USERGROUP
+ char *target_user;
+ char *target_group;
+#endif
+ table *handlers;
+ char *php_path;
+} suphp_conf;
-/*
- * We have to use an own version of ap_call_exec() because suExec
- * does not allow to execute setuid-root programs
- */
+/* Configuration mergers/creators */
-static char **suphp_create_argv(pool *p, char *path, char *user, char *group,
- char *av0, const char *args)
-{
- int x, numwords;
- char **av;
- char *w;
- int idx = 0;
+static void *suphp_create_dir_config(pool *p, char *dir) {
+ suphp_conf *cfg = (suphp_conf *) ap_pcalloc(p, sizeof(suphp_conf));
+ cfg->php_config = NULL;
+ cfg->engine = SUPHP_ENGINE_UNDEFINED;
+ cfg->cmode = SUPHP_CONFIG_MODE_DIRECTORY;
+
+#ifdef SUPHP_USE_USERGROUP
+ cfg->target_user = NULL;
+ cfg->target_group = NULL;
+#endif
- /* count the number of keywords */
+ /* Create table with 0 initial elements */
+ /* This size may be increased for performance reasons */
+ cfg->handlers = ap_make_table(p, 0);
+
+ return (void *) cfg;
+}
- for (x = 0, numwords = 1; args[x]; x++) {
- if (args[x] == '+') {
- ++numwords;
- }
- }
+static void *suphp_merge_dir_config(pool *p, void *base, void *overrides) {
+ suphp_conf *parent = (suphp_conf *) base;
+ suphp_conf *child = (suphp_conf *) overrides;
+ suphp_conf *merged = (suphp_conf *) ap_pcalloc(p, sizeof(suphp_conf));
- if (numwords > APACHE_ARG_MAX - 5) {
- numwords = APACHE_ARG_MAX - 5; /* Truncate args to prevent overrun */
- }
- av = (char **) ap_palloc(p, (numwords + 5) * sizeof(char *));
+ merged->cmode = SUPHP_CONFIG_MODE_DIRECTORY;
+
+ if (child->php_config)
+ merged->php_config = ap_pstrdup(p, child->php_config);
+ else if (parent->php_config)
+ merged->php_config = ap_pstrdup(p, parent->php_config);
+ else
+ merged->php_config = NULL;
+
+ if (child->engine != SUPHP_ENGINE_UNDEFINED)
+ merged->engine = child->engine;
+ else
+ merged->engine = parent->engine;
- if (path) {
- av[idx++] = path;
- }
- if (user) {
- av[idx++] = user;
- }
- if (group) {
- av[idx++] = group;
- }
+#ifdef SUPHP_USE_USERGROUP
+ if (child->target_user)
+ merged->target_user = ap_pstrdup(p, child->target_user);
+ else if (parent->target_user)
+ merged->target_user = ap_pstrdup(p, parent->target_user);
+ else
+ merged->target_user = NULL;
- av[idx++] = av0;
+ if (child->target_group)
+ merged->target_group = ap_pstrdup(p, child->target_group);
+ else if (parent->target_group)
+ merged->target_group = ap_pstrdup(p, parent->target_group);
+ else
+ merged->target_group = NULL;
+#endif
+
+ merged->handlers = ap_overlay_tables(p, child->handlers, parent->handlers);
- for (x = 1; x <= numwords; x++) {
- w = ap_getword_nulls(p, &args, '+');
- ap_unescape_url(w);
- av[idx++] = ap_escape_shell_cmd(p, w);
- }
- av[idx] = NULL;
- return av;
+ return (void *) merged;
}
-int suphp_call_exec(request_rec *r, child_info *pinfo, char *argv0,
- char **env, int shellcmd)
-{
- int pid = 0;
-/*
+static void *suphp_create_server_config(pool *p, server_rec *s) {
+ suphp_conf *cfg = (suphp_conf *) ap_pcalloc(p, sizeof(suphp_conf));
+
+ cfg->engine = SUPHP_ENGINE_UNDEFINED;
+ cfg->php_path = NULL;
+ cfg->cmode = SUPHP_CONFIG_MODE_SERVER;
+
+#ifdef SUPHP_USE_USERGROUP
+ cfg->target_user = NULL;
+ cfg->target_group = NULL;
+#endif
+
+ /* Create table with 0 initial elements */
+ /* This size may be increased for performance reasons */
+ cfg->handlers = ap_make_table(p, 0);
-#if defined(RLIMIT_CPU) || defined(RLIMIT_NPROC) || \
- defined(RLIMIT_DATA) || defined(RLIMIT_VMEM) || defined (RLIMIT_AS)
+ return (void *) cfg;
+}
- core_dir_config *conf;
- conf = (core_dir_config *) ap_get_module_config(r->per_dir_config,
- &core_module);
-#endif
+static void *suphp_merge_server_config(pool *p, void *base, void *overrides) {
+ suphp_conf *parent = (suphp_conf *) base;
+ suphp_conf *child = (suphp_conf *) overrides;
+ suphp_conf *merged = (suphp_conf *) ap_pcalloc(p, sizeof(suphp_conf));
+
+ if (child->engine != SUPHP_ENGINE_UNDEFINED)
+ merged->engine = child->engine;
+ else
+ merged->engine = parent->engine;
+
+ if (child->php_path != NULL)
+ merged->php_path = ap_pstrdup(p, child->php_path);
+ else
+ merged->php_path = ap_pstrdup(p, parent->php_path);
-#ifdef RLIMIT_CPU
- if (conf->limit_cpu != NULL) {
- if ((setrlimit(RLIMIT_CPU, conf->limit_cpu)) != 0) {
- ap_log_error(APLOG_MARK, APLOG_ERR, r->server,
- "setrlimit: failed to set CPU usage limit");
- }
- }
-#endif
-#ifdef RLIMIT_NPROC
- if (conf->limit_nproc != NULL) {
- if ((setrlimit(RLIMIT_NPROC, conf->limit_nproc)) != 0) {
- ap_log_error(APLOG_MARK, APLOG_ERR, r->server,
- "setrlimit: failed to set process limit");
- }
- }
-#endif
-#if defined(RLIMIT_AS)
- if (conf->limit_mem != NULL) {
- if ((setrlimit(RLIMIT_AS, conf->limit_mem)) != 0) {
- ap_log_error(APLOG_MARK, APLOG_ERR, r->server,
- "setrlimit(RLIMIT_AS): failed to set memory "
- "usage limit");
- }
- }
-#elif defined(RLIMIT_DATA)
- if (conf->limit_mem != NULL) {
- if ((setrlimit(RLIMIT_DATA, conf->limit_mem)) != 0) {
- ap_log_error(APLOG_MARK, APLOG_ERR, r->server,
- "setrlimit(RLIMIT_DATA): failed to set memory "
- "usage limit");
- }
- }
-#elif defined(RLIMIT_VMEM)
- if (conf->limit_mem != NULL) {
- if ((setrlimit(RLIMIT_VMEM, conf->limit_mem)) != 0) {
- ap_log_error(APLOG_MARK, APLOG_ERR, r->server,
- "setrlimit(RLIMIT_VMEM): failed to set memory "
- "usage limit");
- }
- }
-#endif
+#ifdef SUPHP_USE_USERGROUP
+ if (child->target_user)
+ merged->target_user = ap_pstrdup(p, child->target_user);
+ else if (parent->target_user)
+ merged->target_user = ap_pstrdup(p, parent->target_user);
+ else
+ merged->target_user = NULL;
-*/
+ if (child->target_group)
+ merged->target_group = ap_pstrdup(p, child->target_group);
+ else if (parent->target_group)
+ merged->target_group = ap_pstrdup(p, parent->target_group);
+ else
+ merged->target_group = NULL;
+#endif
+
+ merged->handlers = ap_overlay_tables(p, child->handlers, parent->handlers);
-// execve("/usr/sbin/suphp",
-// suphp_create_argv(r->pool, NULL, NULL, NULL, "suphp", r->args),
-// env);
- execle(PATH_TO_SUPHP, "suphp", NULL, env);
- return (pid);
+ return (void *) merged;
}
+/* Command handlers */
-/* Configuration stuff */
+static const char *suphp_handle_cmd_engine(cmd_parms *cmd, void *mconfig,
+ int flag) {
+ suphp_conf *cfg;
-#define DEFAULT_LOGBYTES 10385760
-#define DEFAULT_BUFBYTES 1024
-
-#define CONFIG_MODE_SERVER 1
-#define CONFIG_MODE_DIRECTORY 2
-#define CONFIG_MODE_COMBO 3 /* Shouldn't ever happen. */
+ if (mconfig)
+ cfg = (suphp_conf *) mconfig;
+ else
+ cfg = (suphp_conf *) ap_get_module_config(cmd->server->module_config,
+ &suphp_module);
+
+ if (flag)
+ cfg->engine = SUPHP_ENGINE_ON;
+ else
+ cfg->engine = SUPHP_ENGINE_OFF;
-#define SUPHP_ENGINE_OFF 0
-#define SUPHP_ENGINE_ON 1
-#define SUPHP_ENGINE_UNDEFINED 2
+ return NULL;
+}
-typedef struct {
- int cmode;
- char *logname;
- long logbytes;
- int bufbytes;
- int engine;
- char *php_config;
-} suphp_conf;
-static void *suphp_create_dir_config(pool *p, char *dirspec)
-{
- suphp_conf *cfg;
- cfg = (suphp_conf *) ap_pcalloc(p, sizeof(suphp_conf));
- cfg->php_config = NULL;
- cfg->cmode = CONFIG_MODE_DIRECTORY;
- return (void *) cfg;
+static const char *suphp_handle_cmd_config(cmd_parms *cmd, void *mconfig,
+ const char *arg) {
+ suphp_conf *cfg = (suphp_conf *) mconfig;
+
+ cfg->php_config = ap_pstrdup(cmd->pool, arg);
+
+ return NULL;
}
-static void *suphp_merge_dir_config(pool *p, void *base_conf, void *new_conf)
-{
- suphp_conf *parent = (suphp_conf *) base_conf;
- suphp_conf *child = (suphp_conf *) new_conf;
- suphp_conf *merged = (suphp_conf *) ap_pcalloc(p, sizeof(suphp_conf));
-
- merged->php_config = child->php_config ? child->php_config : parent->php_config;
-
- return merged;
-}
-static void *suphp_create_server_config(pool *p, server_rec *s)
-{
- suphp_conf *cfg = (suphp_conf *) ap_pcalloc(p, sizeof(suphp_conf));
+#ifdef SUPHP_USE_USERGROUP
+static const char *suphp_handle_cmd_user_group(cmd_parms *cmd, void *mconfig,
+ const char *arg1,
+ const char *arg2) {
+ suphp_conf *cfg;
- cfg->logname = NULL;
- cfg->logbytes = DEFAULT_LOGBYTES;
- cfg->bufbytes = DEFAULT_BUFBYTES;
- cfg->engine = SUPHP_ENGINE_UNDEFINED;
+ if (mconfig)
+ cfg = (suphp_conf *) mconfig;
+ else
+ cfg = ap_get_module_config(cmd->server->module_config, &suphp_module);
+
+ cfg->target_user = ap_pstrdup(cmd->pool, arg1);
+ cfg->target_group = ap_pstrdup(cmd->pool, arg2);
- return (void *) cfg;
+ return NULL;
}
+#endif
-static void *suphp_merge_server_config(pool *p, void *base_conf, void *new_conf)
-{
- suphp_conf *merged = (suphp_conf *) ap_pcalloc(p, sizeof(suphp_conf));
- suphp_conf *parent = (suphp_conf *) base_conf;
- suphp_conf *child = (suphp_conf *) new_conf;
-
- if (child->logname)
- {
- merged->logname = child->logname;
- merged->logbytes = child->logbytes;
- merged->bufbytes = child->bufbytes;
- }
+
+static const char *suphp_handle_cmd_add_handler(cmd_parms *cmd, void *mconfig,
+ const char *arg) {
+ suphp_conf *cfg;
+ if (mconfig)
+ cfg = (suphp_conf *) mconfig;
else
- {
- merged->logname = parent->logname;
- merged->logbytes = parent->logbytes;
- merged->bufbytes = parent->bufbytes;
- }
+ cfg = ap_get_module_config(cmd->server->module_config, &suphp_module);
- if (child->engine != SUPHP_ENGINE_UNDEFINED)
- merged->engine = child->engine;
+ // Mark active handlers with '1'
+ ap_table_set(cfg->handlers, arg, "1");
+
+ return NULL;
+}
+
+static const char *suphp_handle_cmd_remove_handler(cmd_parms *cmd,
+ void *mconfig,
+ const char *arg) {
+ suphp_conf *cfg;
+ if (mconfig)
+ cfg = (suphp_conf *) mconfig;
else
- merged->engine = parent->engine;
+ cfg = ap_get_module_config(cmd->server->module_config, &suphp_module);
- return (void *) merged;
-}
+ // Mark deactivated handlers with '0'
+ ap_table_set(cfg->handlers, arg, "0");
-static const char *suphp_handle_cmd_engine(cmd_parms *cmd, void *mconfig, int flag)
-{
- suphp_conf *cfg = (suphp_conf *) ap_get_module_config(cmd->server->module_config, &suphp_module);
-
- if (flag)
- cfg->engine = SUPHP_ENGINE_ON;
- else
- cfg->engine = SUPHP_ENGINE_OFF;
-
- return NULL;
+ return NULL;
}
-static const char *suphp_handle_cmd_config(cmd_parms *parms, void *mconfig, const char *arg)
+
+static const char *suphp_handle_cmd_phppath(cmd_parms *cmd, void* mconfig, const char *arg)
{
- suphp_conf *cfg = (suphp_conf *) mconfig;
- cfg->php_config = (char*)ap_pstrdup(parms->pool, arg);
- return NULL;
+ suphp_conf *cfg;
+
+ cfg = (suphp_conf *) ap_get_module_config(cmd->server->module_config, &suphp_module);
+
+ cfg->php_path = ap_pstrdup(cmd->pool, arg);
+
+ return NULL;
}
-static const command_rec suphp_cmds[] =
-{
- {"suPHP_Engine", suphp_handle_cmd_engine, NULL, RSRC_CONF, FLAG, "Whether PHP is on or off, default is off"},
- {"suPHP_ConfigPath", suphp_handle_cmd_config, NULL, OR_OPTIONS, TAKE1, "Where the php.ini resists, default is to use PHP's default configuration"},
+
+/* Command table */
+
+static const command_rec suphp_cmds[] = {
+ {"suPHP_Engine", suphp_handle_cmd_engine, NULL, RSRC_CONF|ACCESS_CONF,
+ FLAG, "Whether suPHP is on or off, default is off"},
+ {"suPHP_ConfigPath", suphp_handle_cmd_config, NULL, OR_OPTIONS, TAKE1,
+ "Where the php.ini resides, default is the PHP default"},
+#ifdef SUPHP_USE_USERGROUP
+ {"suPHP_UserGroup", suphp_handle_cmd_user_group, NULL,
+ RSRC_CONF|ACCESS_CONF, TAKE2, "User and group scripts shall be run as"},
+#endif
+ {"suPHP_AddHandler", suphp_handle_cmd_add_handler, NULL, RSRC_CONF | ACCESS_CONF,
+ ITERATE, "Tells mod_suphp to handle these MIME-types"},
+ {"suphp_RemoveHandler", suphp_handle_cmd_remove_handler, NULL, RSRC_CONF | ACCESS_CONF,
+ ITERATE, "Tells mod_suphp not to handle these MIME-types"},
+ {"suPHP_PHPPath", suphp_handle_cmd_phppath, NULL, RSRC_CONF, TAKE1, "Path to the PHP binary used to render source view"},
{NULL}
};
-static int log_scripterror(request_rec *r, suphp_conf * conf, int ret,
- int show_errno, char *error)
-{
- FILE *f;
- struct stat finfo;
- ap_log_rerror(APLOG_MARK, show_errno|APLOG_ERR, r,
- "%s: %s", error, r->filename);
+/* Helper function which is called when spawning child process */
- if (!conf->logname ||
- ((stat(ap_server_root_relative(r->pool, conf->logname), &finfo) == 0)
- && (finfo.st_size > conf->logbytes)) ||
- ((f = ap_pfopen(r->pool, ap_server_root_relative(r->pool, conf->logname),
- "a")) == NULL)) {
- return ret;
- }
+int suphp_source_child(void *rp, child_info *cinfo) {
+ request_rec *r = (request_rec *) rp;
+ suphp_conf *conf;
+ pool *p = r->main ? r->main->pool : r->pool;
+ char **argv, **env;
+
+ conf = ap_get_module_config(r->server->module_config, &suphp_module);
+
+ /* We want to log output written to stderr */
+ ap_error_log2stderr(r->server);
- /* "%% [Wed Jun 19 10:53:21 1996] GET /cgi-bin/printenv HTTP/1.0" */
- fprintf(f, "%%%% [%s] %s %s%s%s %s\n", ap_get_time(), r->method, r->uri,
- r->args ? "?" : "", r->args ? r->args : "", r->protocol);
- /* "%% 500 /usr/local/apache/cgi-bin */
- fprintf(f, "%%%% %d %s\n", ret, r->filename);
+ /* prepare argv for new process */
+
+ argv = ap_palloc(p, 4 * sizeof(char *));
+ argv[0] = ap_pstrdup(p, conf->php_path);
+ argv[1] = "-s";
+ argv[2] = ap_pstrdup(p, r->filename);
+ argv[3] = NULL;
+
+ /* prepare environment */
- fprintf(f, "%%error\n%s\n", error);
+ env = ap_create_environment(p, r->subprocess_env);
- ap_pfclose(r->pool, f);
- return ret;
+ /* We cannot use ap_call_exec because of the interference with suExec */
+ /* So we do everything ourselves */
+
+ /* mandatory cleanup before execution */
+ ap_cleanup_for_exec();
+
+ execve(ap_pstrdup(p, conf->php_path), argv, env);
+
+ /* We are still here? Okay - exec failed */
+ ap_log_error(APLOG_MARK, APLOG_ERR, NULL, "exec of %s failed",
+ conf->php_path);
+ exit(0);
+ /* NOT REACHED */
+ return (0);
}
-static int log_script(request_rec *r, suphp_conf * conf, int ret,
- char *dbuf, const char *sbuf, BUFF *script_in, BUFF *script_err)
-{
- array_header *hdrs_arr = ap_table_elts(r->headers_in);
- table_entry *hdrs = (table_entry *) hdrs_arr->elts;
- char argsbuffer[HUGE_STRING_LEN];
- FILE *f;
- int i;
- struct stat finfo;
- if (!conf->logname ||
- ((stat(ap_server_root_relative(r->pool, conf->logname), &finfo) == 0)
- && (finfo.st_size > conf->logbytes)) ||
- ((f = ap_pfopen(r->pool, ap_server_root_relative(r->pool, conf->logname),
- "a")) == NULL)) {
- /* Soak up script output */
- while (ap_bgets(argsbuffer, HUGE_STRING_LEN, script_in) > 0)
- continue;
-#if defined(WIN32) || defined(NETWARE)
- /* Soak up stderr and redirect it to the error log.
- * Script output to stderr is already directed to the error log
- * on Unix, thanks to the magic of fork().
- */
- while (ap_bgets(argsbuffer, HUGE_STRING_LEN, script_err) > 0) {
- ap_log_rerror(APLOG_MARK, APLOG_ERR | APLOG_NOERRNO, r,
- "%s", argsbuffer);
+int suphp_child(void *rp, child_info *cinfo) {
+ request_rec *r = (request_rec *) rp;
+ core_dir_config *core_conf;
+ pool *p = r->main ? r->main->pool : r->pool;
+
+ char **argv, **env;
+
+ core_conf = (core_dir_config *) ap_get_module_config(
+ r->per_dir_config, &core_module);
+
+ /* We want to log output written to stderr */
+ ap_error_log2stderr(r->server);
+
+ /* prepare argv for new process */
+
+ argv = ap_palloc(p, 2 * sizeof(char *));
+ argv[0] = SUPHP_PATH_TO_SUPHP;
+ argv[1] = NULL;
+
+ /* prepare environment */
+
+ env = ap_create_environment(p, r->subprocess_env);
+
+ /* We cannot use ap_call_exec because of the interference with suExec */
+ /* So we do everything ourselves */
+
+ /* Set resource limits from core config */
+
+#ifdef RLIMIT_CPU
+ if (core_conf->limit_cpu != NULL) {
+ if ((setrlimit(RLIMIT_CPU, core_conf->limit_cpu)) != 0) {
+ ap_log_error(APLOG_MARK, APLOG_ERR, r->server,
+ "setrlimit: failed to set CPU usage limit");
}
-#else
- while (ap_bgets(argsbuffer, HUGE_STRING_LEN, script_err) > 0)
- continue;
-#endif
- return ret;
}
-
- /* "%% [Wed Jun 19 10:53:21 1996] GET /cgi-bin/printenv HTTP/1.0" */
- fprintf(f, "%%%% [%s] %s %s%s%s %s\n", ap_get_time(), r->method, r->uri,
- r->args ? "?" : "", r->args ? r->args : "", r->protocol);
- /* "%% 500 /usr/local/apache/cgi-bin" */
- fprintf(f, "%%%% %d %s\n", ret, r->filename);
-
- fputs("%request\n", f);
- for (i = 0; i < hdrs_arr->nelts; ++i) {
- if (!hdrs[i].key)
- continue;
- fprintf(f, "%s: %s\n", hdrs[i].key, hdrs[i].val);
+#endif /* RLIMIT_CPU */
+#ifdef RLIMIT_NPROC
+ if (core_conf->limit_nproc != NULL) {
+ if ((setrlimit(RLIMIT_NPROC, core_conf->limit_nproc)) != 0) {
+ ap_log_error(APLOG_MARK, APLOG_ERR, r->server,
+ "setrlimit: failed to set process limit");
+ }
}
- if ((r->method_number == M_POST || r->method_number == M_PUT)
- && *dbuf) {
- fprintf(f, "\n%s\n", dbuf);
+#endif /* RLIMIT_NPROC */
+#ifdef RLIMIT_AS
+ if (core_conf->limit_mem != NULL) {
+ if ((setrlimit(RLIMIT_AS, core_conf->limit_mem)) != 0) {
+ ap_log_error(APLOG_MARK, APLOG_ERR, r->server,
+ "setrlimit: failed to set memory limit");
+ }
+ }
+#endif /* RLIMIT_VMEM */
+#ifdef RLIMIT_DATA
+ if (core_conf->limit_mem != NULL) {
+ if ((setrlimit(RLIMIT_DATA, core_conf->limit_mem)) != 0) {
+ ap_log_error(APLOG_MARK, APLOG_ERR, r->server,
+ "setrlimit: failed to set memory limit");
+ }
+ }
+#endif /* RLIMIT_VMEM */
+#ifdef RLIMIT_VMEM
+ if (core_conf->limit_mem != NULL) {
+ if ((setrlimit(RLIMIT_VMEM, core_conf->limit_mem)) != 0) {
+ ap_log_error(APLOG_MARK, APLOG_ERR, r->server,
+ "setrlimit: failed to set memory limit");
+ }
}
+#endif /* RLIMIT_VMEM */
- fputs("%response\n", f);
- hdrs_arr = ap_table_elts(r->err_headers_out);
- hdrs = (table_entry *) hdrs_arr->elts;
+ /* mandatory cleanup before execution */
+ ap_cleanup_for_exec();
+
+ execve(SUPHP_PATH_TO_SUPHP, argv, env);
- for (i = 0; i < hdrs_arr->nelts; ++i) {
- if (!hdrs[i].key)
- continue;
- fprintf(f, "%s: %s\n", hdrs[i].key, hdrs[i].val);
- }
+ /* We are still here? Okay - exec failed */
+ ap_log_error(APLOG_MARK, APLOG_ERR, NULL, "exec of %s failed",
+ SUPHP_PATH_TO_SUPHP);
+ exit(0);
+ /* NOT REACHED */
+ return (0);
+}
- if (sbuf && *sbuf)
- fprintf(f, "%s\n", sbuf);
- if (ap_bgets(argsbuffer, HUGE_STRING_LEN, script_in) > 0) {
- fputs("%stdout\n", f);
- fputs(argsbuffer, f);
- while (ap_bgets(argsbuffer, HUGE_STRING_LEN, script_in) > 0)
- fputs(argsbuffer, f);
- fputs("\n", f);
+/* Handlers */
+
+static int suphp_source_handler(request_rec *r) {
+ suphp_conf *conf;
+ int rv;
+ pool *p;
+ int fd;
+ BUFF *script_in, *script_out, *script_err;
+ char buffer[HUGE_STRING_LEN];
+
+ if (strcmp(r->method, "GET")) {
+ return DECLINED;
+ }
+
+ conf = ap_get_module_config(r->server->module_config, &suphp_module);
+ if (conf->php_path == NULL) {
+ return DECLINED;
}
+
+ p = r->main ? r->main->pool : r->pool;
+
+ fd = open(r->filename, O_NOCTTY, O_RDONLY);
+ if (fd != -1) {
+ close(fd);
+ } else if (errno == EACCES) {
+ ap_log_rerror(APLOG_MARK, APLOG_ERR, r, "access to %s denied",
+ r->filename);
+ return HTTP_FORBIDDEN;
+ } else if (errno == ENOENT || errno == ENOTDIR) {
+ ap_log_rerror(APLOG_MARK, APLOG_ERR, r, "File does not exist: %s",
+ r->filename);
+ return HTTP_NOT_FOUND;
+ } else {
+ ap_log_rerror(APLOG_MARK, APLOG_ERR, r, "could open file: %s",
+ r->filename);
+ return HTTP_NOT_FOUND;
+ }
+
+ /* Fork child process */
+
+ if (!ap_bspawn_child(p, suphp_source_child, (void *) r, kill_after_timeout,
+ &script_in, &script_out, &script_err)) {
+ ap_log_rerror(APLOG_MARK, APLOG_ERR, r,
+ "couldn't spawn child process for: %s", r->filename);
+ return HTTP_INTERNAL_SERVER_ERROR;
+ }
+
+ /* Read request body */
+
+ if (ap_should_client_block(r)) {
+ char buffer[HUGE_STRING_LEN];
+ int len_read;
+
+ ap_hard_timeout("reading request body", r);
- if (ap_bgets(argsbuffer, HUGE_STRING_LEN, script_err) > 0) {
- fputs("%stderr\n", f);
- fputs(argsbuffer, f);
- while (ap_bgets(argsbuffer, HUGE_STRING_LEN, script_err) > 0)
- fputs(argsbuffer, f);
- fputs("\n", f);
+ while (ap_get_client_block(r, buffer, HUGE_STRING_LEN) > 0) {
+ ap_reset_timeout(r);
+ // Ignore input
+ }
+
+ ap_bflush(script_in);
+ ap_kill_timeout(r);
}
ap_bclose(script_in);
- ap_bclose(script_err);
-
- ap_pfclose(r->pool, f);
- return ret;
-}
+
+ /* Transfer output from PHP to client */
-/****************************************************************
- *
- * Actual suPHP handling...
- */
+ if (script_out) {
+ /* Output headers and body */
+ r->content_type = "text/html";
+ ap_send_http_header(r);
+ if (!r->header_only) {
+ ap_send_fb(script_out, r);
+ }
+ ap_bclose(script_out);
+ /* Errors have already been logged by child */
+ ap_bclose(script_err);
+ }
+
+ return OK;
-struct suphp_child_stuff {
-#ifdef TPF
- TPF_FORK_CHILD t;
-#endif
- request_rec *r;
- int nph;
- int debug;
- char *argv0;
-};
+}
-static int suphp_child(void *child_stuff, child_info *pinfo)
-{
- struct suphp_child_stuff *cld = (struct suphp_child_stuff *) child_stuff;
- request_rec *r = cld->r;
- char *argv0 = cld->argv0;
- int child_pid;
- const char *authorization=NULL;
- char *tmp=NULL;
- char *auth_user=NULL;
- char *auth_password=NULL;
- suphp_conf *cfg = (suphp_conf *) ap_get_module_config(r->per_dir_config, &suphp_module);
-
-#ifdef DEBUG_SUPHP
-#ifdef OS2
- /* Under OS/2 need to use device con. */
- FILE *dbg = fopen("con", "w");
-#else
- FILE *dbg = fopen("/dev/tty", "w");
-#endif
- int i;
-#endif
- char **env;
+static int suphp_handler(request_rec *r) {
+ suphp_conf *sconf;
+ suphp_conf *dconf;
- RAISE_SIGSTOP(SUPHP_CHILD);
-#ifdef DEBUG_SUPHP
- fprintf(dbg, "Attempting to exec %s as %ssuPHP child (argv0 = %s)\n",
- r->filename, cld->nph ? "NPH " : "", argv0);
+#ifdef SUPHP_USE_USERGROUP
+ char *ud_user = NULL;
+ char *ud_group = NULL;
#endif
-
- /*
- * Set environment variables with authorization information like mod_php
- */
- if (r->headers_in)
- {
- authorization = ap_table_get(r->headers_in, "Authorization");
- }
- if (authorization
- && !strcasecmp(ap_getword(r->pool, &authorization, ' '), "Basic"))
- {
- tmp = ap_uudecode(r->pool, authorization);
- auth_user = ap_getword_nulls_nc(r->pool, &tmp, ':');
-/*
- if (auth_user)
- {
- auth_user = estrdup(auth_user);
- }
-*/
- auth_password = tmp;
-/*
- if (auth_password)
- {
- auth_password = estrdup(auth_password);
- }
-*/
- if (auth_user)
- {
- ap_table_setn(r->subprocess_env, "PHP_AUTH_USER", auth_user);
- }
- if (auth_password)
- {
- ap_table_setn(r->subprocess_env, "PHP_AUTH_PW", auth_password);
- }
- }
+ struct stat finfo;
- if (cfg->php_config)
- {
- ap_table_setn(r->subprocess_env, "PHP_CONFIG", cfg->php_config);
- }
-
- ap_add_cgi_vars(r);
- env = ap_create_environment(r->pool, r->subprocess_env);
+ int rv;
+
+ char *auth_user = NULL;
+ char *auth_pass = NULL;
-#ifdef DEBUG_SUPHP
- fprintf(dbg, "Environment: \n");
- for (i = 0; env[i]; ++i)
- fprintf(dbg, "'%s'\n", env[i]);
-#endif
+ pool *p;
+ BUFF *script_in, *script_out, *script_err;
+
+ const char *handler;
- ap_chdir_file(r->filename);
- if (!cld->debug)
- ap_error_log2stderr(r->server);
+ sconf = ap_get_module_config(r->server->module_config, &suphp_module);
+ dconf = ap_get_module_config(r->per_dir_config, &suphp_module);
- /* Transumute outselves into the script.
- * NB only ISINDEX scripts get decoded arguments.
- */
+ p = r->main ? r->main->pool : r->pool;
-#ifdef TPF
- return (0);
-#else
- ap_cleanup_for_exec();
+ /* only handle request if mod_suphp is active for this handler */
+ /* check only first byte of value (second has to be \0) */
+ if (r->handler != NULL) {
+ handler = r->handler;
+ } else {
+ handler = r->content_type;
+ }
+ if ((ap_table_get(dconf->handlers, handler) == NULL)) {
+ if ((ap_table_get(sconf->handlers, handler) == NULL)
+ || (*(ap_table_get(sconf->handlers, handler)) == '0')) {
+ return DECLINED;
+ }
+ } else if (*(ap_table_get(dconf->handlers, handler)) == '0') {
+ return DECLINED;
+ }
- child_pid = suphp_call_exec(r, pinfo, argv0, env, 0);
- return (child_pid);
+ /* check if suPHP is enabled for this request */
- /* Uh oh. Still here. Where's the kaboom? There was supposed to be an
- * EARTH-shattering kaboom!
- *
- * Oh, well. Muddle through as best we can...
- *
- * Note that only stderr is available at this point, so don't pass in
- * a server to aplog_error.
- */
+ if (((sconf->engine != SUPHP_ENGINE_ON)
+ && (dconf->engine != SUPHP_ENGINE_ON))
+ || ((sconf->engine == SUPHP_ENGINE_ON)
+ && (dconf->engine == SUPHP_ENGINE_OFF)))
+ return DECLINED;
- ap_log_error(APLOG_MARK, APLOG_ERR, NULL, "exec of %s failed", r->filename);
- exit(0);
- /* NOT REACHED */
- return (0);
-#endif /* TPF */
-}
+ /* check if file is existing and accessible */
+
+ rv = stat(ap_pstrdup(p, r->filename), &finfo);
+ if (rv == 0) {
+ ; /* do nothing */
+ } else if (errno == EACCES) {
+ ap_log_rerror(APLOG_MARK, APLOG_ERR, r, "access to %s denied",
+ r->filename);
+ return HTTP_FORBIDDEN;
+ } else if (errno == ENOENT || errno == ENOTDIR) {
+ ap_log_rerror(APLOG_MARK, APLOG_ERR, r, "File does not exist: %s",
+ r->filename);
+ return HTTP_NOT_FOUND;
+ } else {
+ ap_log_rerror(APLOG_MARK, APLOG_ERR, r, "could not get fileinfo: %s",
+ r->filename);
+ return HTTP_NOT_FOUND;
+ }
-static int suphp_handler(request_rec *r)
-{
- int retval, nph, dbpos = 0;
- char *argv0, *dbuf = NULL;
- BUFF *script_out, *script_in, *script_err;
- char argsbuffer[HUGE_STRING_LEN];
- int is_included = !strcmp(r->protocol, "INCLUDED");
- void *sconf = r->server->module_config;
- suphp_conf *conf =
- (suphp_conf *) ap_get_module_config(sconf, &suphp_module);
- suphp_conf *mconf = (suphp_conf *) ap_get_module_config(r->server->module_config, &suphp_module);
-
- struct suphp_child_stuff cld;
-
- if ((mconf->engine == SUPHP_ENGINE_OFF) || (mconf->engine == SUPHP_ENGINE_UNDEFINED))
- return DECLINED;
-
- if (r->method_number == M_OPTIONS) {
- /* 99 out of 100 CGI scripts, this is all they support */
- r->allowed |= (1 << M_GET);
- r->allowed |= (1 << M_POST);
- return DECLINED;
+#ifdef SUPHP_USE_USERGROUP
+ if ((sconf->target_user == NULL || sconf->target_group == NULL)
+ && (dconf->target_user == NULL || dconf->target_group == NULL)) {
+
+ /* Identify mod_userdir request
+ As Apache 1.3 does not yet provide a clean way to see
+ whether a request was handled by mod_userdir, we assume
+ this is true for any request beginning with ~ */
+
+ int ud_success = 0; /* set to 1 on success */
+
+ if (!strncmp("/~", r->uri, 2)) {
+ char *username = ap_pstrdup(r->pool, r->uri + 2);
+ char *pos = strchr(username, '/');
+ if (pos) {
+ *pos = 0;
+ if (strlen(username)) {
+ struct passwd *pw;
+ struct group *gr;
+ gid_t gid;
+ char *grpname;
+ if ((pw = getpwnam(username)) != NULL) {
+ gid = pw->pw_gid;
+
+ if ((gr = getgrgid(gid)) != NULL) {
+ grpname = gr->gr_name;
+ } else {
+ if ((grpname = ap_palloc(r->pool, 16)) == NULL) {
+ return HTTP_INTERNAL_SERVER_ERROR;
+ }
+ ap_snprintf(grpname, 16, "#%ld", (long) gid);
+ }
+
+ ud_user = username;
+ ud_group = grpname;
+ ud_success = 1;
+ }
+ }
+ }
+ }
+
+ if (!ud_success) {
+ /* This is not a userdir request and user/group are not
+ set, so log the error and return */
+ ap_log_rerror(APLOG_MARK, APLOG_ERR, r,
+ "No user or group set - set suPHP_UserGroup");
+ return HTTP_INTERNAL_SERVER_ERROR;
+ }
}
+#endif /* SUPHP_USE_USERGROUP */
- if ((argv0 = strrchr(r->filename, '/')) != NULL)
- argv0++;
- else
- argv0 = r->filename;
-
-// nph = !(strncmp(argv0, "nph-", 4));
- nph = 0;
-
-// if (!(ap_allow_options(r) & OPT_EXECCGI) && !is_scriptaliased(r))
-// return log_scripterror(r, conf, FORBIDDEN, APLOG_NOERRNO,
-// "Options ExecCGI is off in this directory");
-// if (nph && is_included)
-// return log_scripterror(r, conf, FORBIDDEN, APLOG_NOERRNO,
-// "attempt to include NPH CGI script");
-//
- if (r->finfo.st_mode == 0)
- return log_scripterror(r, conf, NOT_FOUND, APLOG_NOERRNO,
- "script not found or unable to stat");
- if (S_ISDIR(r->finfo.st_mode))
- return log_scripterror(r, conf, FORBIDDEN, APLOG_NOERRNO,
- "attempt to invoke directory as script");
-
- if ((retval = ap_setup_client_block(r, REQUEST_CHUNKED_ERROR)))
- return retval;
+ /* prepare environment for new process */
+
ap_add_common_vars(r);
- cld.argv0 = argv0;
- cld.r = r;
- cld.nph = nph;
- cld.debug = conf->logname ? 1 : 0;
-#ifdef TPF
- cld.t.filename = r->filename;
- cld.t.subprocess_env = r->subprocess_env;
- cld.t.prog_type = FORK_FILE;
-#endif /* TPF */
-
-#ifdef CHARSET_EBCDIC
- /* The included MIME headers must ALWAYS be in text/ebcdic format.
- * Only after reading the MIME headers, we check the Content-Type
- * and switch to the necessary conversion mode.
- * Until then (and in case an nph- script was called), use the
- * configured default conversion:
- */
- ap_bsetflag(r->connection->client, B_EBCDIC2ASCII, r->ebcdic.conv_out);
-#endif /*CHARSET_EBCDIC*/
-
- /*
- * we spawn out of r->main if it's there so that we can avoid
- * waiting for free_proc_chain to cleanup in the middle of an
- * SSI request -djg
- */
- if (!ap_bspawn_child(r->main ? r->main->pool : r->pool, suphp_child,
- (void *) &cld, kill_after_timeout,
- &script_out, &script_in, &script_err)) {
- ap_log_rerror(APLOG_MARK, APLOG_ERR, r,
- "couldn't spawn child process: %s", r->filename);
- return HTTP_INTERNAL_SERVER_ERROR;
- }
+ ap_add_cgi_vars(r);
- /* Transfer any put/post args, CERN style...
- * Note that we already ignore SIGPIPE in the core server.
- */
+ ap_table_unset(r->subprocess_env, "SUPHP_PHP_CONFIG");
+ ap_table_unset(r->subprocess_env, "SUPHP_AUTH_USER");
+ ap_table_unset(r->subprocess_env, "SUPHP_AUTH_PW");
+
+#ifdef SUPHP_USE_USERGROUP
+ ap_table_unset(r->subprocess_env, "SUPHP_USER");
+ ap_table_unset(r->subprocess_env, "SUPHP_GROUP");
+#endif /* SUPHP_USE_USERGROUP */
- if (ap_should_client_block(r)) {
- int dbsize, len_read;
-
- if (conf->logname) {
- dbuf = ap_pcalloc(r->pool, conf->bufbytes + 1);
- dbpos = 0;
- }
-
- ap_hard_timeout("copy script args", r);
-
- while ((len_read =
- ap_get_client_block(r, argsbuffer, HUGE_STRING_LEN)) > 0) {
- if (conf->logname) {
- if ((dbpos + len_read) > conf->bufbytes) {
- dbsize = conf->bufbytes - dbpos;
- }
- else {
- dbsize = len_read;
- }
- memcpy(dbuf + dbpos, argsbuffer, dbsize);
- dbpos += dbsize;
- }
- ap_reset_timeout(r);
- if (ap_bwrite(script_out, argsbuffer, len_read) < len_read) {
- /* silly script stopped reading, soak up remaining message */
- while (ap_get_client_block(r, argsbuffer, HUGE_STRING_LEN) > 0) {
- /* dump it */
- }
- break;
- }
- }
-
- ap_bflush(script_out);
-
- ap_kill_timeout(r);
+ if (dconf->php_config) {
+ ap_table_set(r->subprocess_env, "SUPHP_PHP_CONFIG", dconf->php_config);
}
- ap_bclose(script_out);
-
- /* Handle script return... */
- if (script_in && !nph) {
- const char *location;
- char sbuf[MAX_STRING_LEN];
- int ret;
-
- if ((ret = ap_scan_script_header_err_buff(r, script_in, sbuf))) {
- return log_script(r, conf, ret, dbuf, sbuf, script_in, script_err);
- }
-
- location = ap_table_get(r->headers_out, "Location");
-
- if (location && location[0] == '/' && r->status == 200) {
-
- /* Soak up all the script output */
- ap_hard_timeout("read from script", r);
- while (ap_bgets(argsbuffer, HUGE_STRING_LEN, script_in) > 0) {
- continue;
- }
-#if defined(WIN32) || defined(NETWARE)
- /* Soak up stderr and redirect it to the error log.
- * Script output to stderr is already directed to the error log
- * on Unix, thanks to the magic of fork().
- */
- while (ap_bgets(argsbuffer, HUGE_STRING_LEN, script_err) > 0) {
- ap_log_rerror(APLOG_MARK, APLOG_ERR | APLOG_NOERRNO, r,
- "%s", argsbuffer);
+ ap_table_set(r->subprocess_env, "SUPHP_HANDLER", handler);
+
+ if (r->headers_in) {
+ const char *auth;
+ auth = ap_table_get(r->headers_in, "Authorization");
+ if (auth && auth[0] != 0 && strncmp(auth, "Basic ", 6) == 0) {
+ char *user;
+ char *pass;
+ user = ap_pbase64decode(p, auth + 6);
+ if (user) {
+ pass = strchr(user, ':');
+ if (pass) {
+ *pass++ = '\0';
+ auth_user = ap_pstrdup(p, user);
+ auth_pass = ap_pstrdup(p, pass);
+ }
}
-#else
- while (ap_bgets(argsbuffer, HUGE_STRING_LEN, script_err) > 0) {
- continue;
- }
-#endif
- ap_kill_timeout(r);
-
-
- /* This redirect needs to be a GET no matter what the original
- * method was.
- */
- r->method = ap_pstrdup(r->pool, "GET");
- r->method_number = M_GET;
-
- /* We already read the message body (if any), so don't allow
- * the redirected request to think it has one. We can ignore
- * Transfer-Encoding, since we used REQUEST_CHUNKED_ERROR.
- */
- ap_table_unset(r->headers_in, "Content-Length");
-
- ap_internal_redirect_handler(location, r);
- return OK;
- }
- else if (location && r->status == 200) {
- /* XX Note that if a script wants to produce its own Redirect
- * body, it now has to explicitly *say* "Status: 302"
- */
- return REDIRECT;
- }
-
- ap_send_http_header(r);
- if (!r->header_only) {
- ap_send_fb(script_in, r);
- }
- ap_bclose(script_in);
-
- ap_soft_timeout("soaking script stderr", r);
-#if defined(WIN32) || defined(NETWARE)
- /* Script output to stderr is already directed to the error log
- * on Unix, thanks to the magic of fork().
- */
- while (ap_bgets(argsbuffer, HUGE_STRING_LEN, script_err) > 0) {
- ap_log_rerror(APLOG_MARK, APLOG_ERR | APLOG_NOERRNO, r,
- "%s", argsbuffer);
}
-#else
- while (ap_bgets(argsbuffer, HUGE_STRING_LEN, script_err) > 0) {
- continue;
- }
-#endif
- ap_kill_timeout(r);
- ap_bclose(script_err);
+ }
+
+ if (auth_user && auth_pass) {
+ ap_table_setn(r->subprocess_env, "SUPHP_AUTH_USER", auth_user);
+ ap_table_setn(r->subprocess_env, "SUPHP_AUTH_PW", auth_pass);
}
- if (script_in && nph) {
- ap_send_fb(script_in, r);
+#ifdef SUPHP_USE_USERGROUP
+ if (dconf->target_user) {
+ ap_table_set(r->subprocess_env, "SUPHP_USER", dconf->target_user);
+ } else if (sconf->target_user) {
+ ap_table_set(r->subprocess_env, "SUPHP_USER", sconf->target_user);
+ } else {
+ ap_table_set(r->subprocess_env, "SUPHP_USER", ud_user);
+ }
+
+ if (dconf->target_group) {
+ ap_table_set(r->subprocess_env, "SUPHP_GROUP", dconf->target_group);
+ } else if (sconf->target_group) {
+ ap_table_set(r->subprocess_env, "SUPHP_GROUP", sconf->target_group);
+ } else {
+ ap_table_set(r->subprocess_env, "SUPHP_GROUP", ud_group);
+ }
+#endif /* SUPHP_USE_USERGROUP */
+
+ /* Fork child process */
+
+ if (!ap_bspawn_child(p, suphp_child, (void *) r, kill_after_timeout,
+ &script_in, &script_out, &script_err)) {
+ ap_log_rerror(APLOG_MARK, APLOG_ERR, r,
+ "couldn't spawn child process for: %s", r->filename);
+ return HTTP_INTERNAL_SERVER_ERROR;
+ }
+
+ /* Transfer request body to script */
+
+ if ((rv = ap_setup_client_block(r, REQUEST_CHUNKED_ERROR)) != OK) {
+ /* Call failed, return status */
+ return rv;
+ }
+
+ if (ap_should_client_block(r)) {
+ char buffer[HUGE_STRING_LEN];
+ int len_read;
+
+ ap_hard_timeout("reading request body", r);
+
+ while ((len_read = ap_get_client_block(r, buffer, HUGE_STRING_LEN))
+ > 0) {
+ ap_reset_timeout(r);
+ if (ap_bwrite(script_in, buffer, len_read) < len_read) {
+ /* silly script stopped reading, soak up remaining message */
+ while (ap_get_client_block(r, buffer, HUGE_STRING_LEN) > 0) {
+ /* dump it */
+ }
+ break;
+ }
+ }
+
+ ap_bflush(script_in);
+ ap_kill_timeout(r);
}
- return OK; /* NOT r->status, even if it has changed. */
+ ap_bclose(script_in);
+
+ /* Transfer output from script to client */
+
+ if (script_out) {
+ const char *location;
+ char hbuffer[MAX_STRING_LEN];
+ char buffer[HUGE_STRING_LEN];
+
+ rv = ap_scan_script_header_err_buff(r, script_out, hbuffer);
+ if (rv == HTTP_NOT_MODIFIED) {
+ return rv;
+ } else if (rv) {
+ return HTTP_INTERNAL_SERVER_ERROR;
+ }
+
+ location = ap_table_get(r->headers_out, "Location");
+ if (location && r->status == 200) {
+ /* Soak up all the script output */
+ ap_hard_timeout("reading from script", r);
+ while (ap_bgets(buffer, HUGE_STRING_LEN, script_out) > 0) {
+ continue;
+ }
+ ap_kill_timeout(r);
+ ap_bclose(script_out);
+ ap_bclose(script_err);
+
+ if (location[0] == '/') {
+ /* Redirect has always GET method */
+ r->method = ap_pstrdup(p, "GET");
+ r->method_number = M_GET;
+
+ /* Remove Content-Length - redirect should not read *
+ * request body */
+ ap_table_unset(r->headers_in, "Content-Length");
+
+ /* Do the redirect */
+ ap_internal_redirect_handler(location, r);
+ return OK;
+ } else {
+ /* Script did not set status 302 - so it does not want *
+ * to send its own body. Simply set redirect status */
+ return REDIRECT;
+ }
+ }
+
+ /* Output headers and body */
+
+ ap_send_http_header(r);
+ if (!r->header_only) {
+ ap_send_fb(script_out, r);
+ }
+ ap_bclose(script_out);
+ /* Errors have already been logged by child */
+ ap_bclose(script_err);
+ }
+
+ return OK;
}
-static const handler_rec suphp_handlers[] =
-{
- {"x-httpd-php", suphp_handler},
+
+/* Handlers table */
+
+static handler_rec suphp_handlers[] = {
+ {"*", suphp_handler},
+ {"x-httpd-php-source", suphp_source_handler},
+ {"application/x-httpd-php-source", suphp_source_handler},
{NULL}
};
-module MODULE_VAR_EXPORT suphp_module =
-{
+/* Module definition */
+
+module MODULE_VAR_EXPORT suphp_module = {
STANDARD_MODULE_STUFF,
- NULL, /* initializer */
- suphp_create_dir_config, /* dir config creater */
- suphp_merge_dir_config, /* dir merger --- default is to override */
- suphp_create_server_config, /* server config */
- suphp_merge_server_config, /* merge server config */
- suphp_cmds, /* command table */
- suphp_handlers, /* handlers */
- NULL, /* filename translation */
- NULL, /* check_user_id */
- NULL, /* check auth */
- NULL, /* check access */
- NULL, /* type_checker */
- NULL, /* fixups */
- NULL, /* logger */
- NULL, /* header parser */
- NULL, /* child_init */
- NULL, /* child_exit */
- NULL /* post read-request */
+ NULL, /* initializer */
+ suphp_create_dir_config, /* create directory config */
+ suphp_merge_dir_config, /* merge directory config */
+ suphp_create_server_config, /* create server config */
+ suphp_merge_server_config, /* merge server config */
+ suphp_cmds, /* command table */
+ suphp_handlers, /* content handlers */
+ NULL, /* URI-to-filename translation */
+ NULL, /* check/validate user_id */
+ NULL, /* check user_id is valid *here* */
+ NULL, /* check access by host address */
+ NULL, /* MIME type checker/setter */
+ NULL, /* fixups */
+ NULL, /* logger */
+ NULL, /* header parser */
+ NULL, /* process initialaization */
+ NULL, /* process exit/cleanup */
+ NULL /* post read_request handling */
};