Import upstream 0.7.1
[manu/suphp.git] / src / apache2 / mod_suphp.c
index c330844..35b2292 100644 (file)
@@ -22,6 +22,7 @@
 #include "apr_strings.h"
 #include "apr_thread_proc.h"
 #include "apr_buckets.h"
+#include "apr_poll.h"
 
 #define CORE_PRIVATE
 
@@ -33,6 +34,8 @@
 #include "util_script.h"
 #include "util_filter.h"
 
+/* needed for get_suexec_identity hook */
+#include "unixd.h"
 
 module AP_MODULE_DECLARE_DATA suphp_module;
 
@@ -41,46 +44,14 @@ module AP_MODULE_DECLARE_DATA suphp_module;
   Auxiliary functions
  *********************/
 
-static int suphp_bucket_read(apr_bucket *b, char *buf, int len)
-{
-    const char *dst_end = buf + len - 1;
-    char * dst = buf;
-    apr_status_t rv;
-    const char *bucket_data;
-    apr_size_t bucket_data_len;
-    const char *src;
-    const char *src_end;
-    int count = 0;
-    
-    if (APR_BUCKET_IS_EOS(b))
-        return -1;
-       
-    rv = apr_bucket_read(b, &bucket_data, &bucket_data_len, APR_BLOCK_READ);
-    if (!APR_STATUS_IS_SUCCESS(rv) || (bucket_data_len == 0))
-    {
-        return 0;
-    }
-    src = bucket_data;
-    src_end = bucket_data + bucket_data_len;
-    while ((src < src_end) && (dst < dst_end))
-    {
-       *dst = *src;
-     dst++;
-     src++;
-     count++;
-    }
-    *dst = 0;
-    return count;
-}
-
-
-static void suphp_log_script_err(request_rec *r, apr_file_t *script_err)
+static apr_status_t suphp_log_script_err(request_rec *r, apr_file_t *script_err)
 {
     char argsbuffer[HUGE_STRING_LEN];
     char *newline;
-
-    while (apr_file_gets(argsbuffer, HUGE_STRING_LEN,
-                         script_err) == APR_SUCCESS) {
+    apr_status_t rv;
+    
+    while ((rv = apr_file_gets(argsbuffer, HUGE_STRING_LEN,
+                         script_err)) == APR_SUCCESS) {
         newline = strchr(argsbuffer, '\n');
         if (newline) {
             *newline = '\0';
@@ -88,6 +59,43 @@ static void suphp_log_script_err(request_rec *r, apr_file_t *script_err)
         ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
                       "%s", argsbuffer);
     }
+    
+    return rv;
+}
+
+char *suphp_brigade_read(apr_pool_t *p, apr_bucket_brigade *bb, int bytes)
+{
+    char *target_buf;
+    char *next_byte;
+    char *last_byte;
+    apr_bucket *b;
+    
+    if (bytes == 0) {
+        return NULL;
+    }
+    
+    target_buf = (char *) apr_palloc(p, bytes + 1);
+    next_byte = target_buf;
+    last_byte = target_buf + bytes;
+    
+    for (b = APR_BRIGADE_FIRST(bb); b != APR_BRIGADE_SENTINEL(bb); b = APR_BUCKET_NEXT(b)) {
+        char *buf;
+        apr_size_t size;
+        apr_size_t i;
+        if (apr_bucket_read(b, &buf, &size, APR_BLOCK_READ) == APR_SUCCESS) {
+            for (i = 0; i < size; i++) {
+                *next_byte = *buf;
+                next_byte++;
+                buf++;
+                if (next_byte == last_byte) {
+                    *next_byte = 0;
+                    return target_buf;
+                }
+            }
+        }
+    }
+    next_byte = 0;
+    return target_buf;
 }
 
 
@@ -115,6 +123,7 @@ typedef struct {
     char *target_group;
 #endif
     apr_table_t *handlers;
+    char *php_path;
 } suphp_conf;
 
 
@@ -124,6 +133,7 @@ static void *suphp_create_dir_config(apr_pool_t *p, char *dir)
     
     cfg->php_config = NULL;
     cfg->engine = SUPHP_ENGINE_UNDEFINED;
+    cfg->php_path = NULL;
     cfg->cmode = SUPHP_CONFIG_MODE_DIRECTORY;
 
 #ifdef SUPHP_USE_USERGROUP
@@ -187,8 +197,13 @@ static void *suphp_create_server_config(apr_pool_t *p, server_rec *s)
     suphp_conf *cfg = (suphp_conf *) apr_pcalloc(p, sizeof(suphp_conf));
     
     cfg->engine = SUPHP_ENGINE_UNDEFINED;
+    cfg->php_path = NULL;
     cfg->cmode = SUPHP_CONFIG_MODE_SERVER;
     
+    /* Create table with 0 initial elements */
+    /* This size may be increased for performance reasons */
+    cfg->handlers = apr_table_make(p, 0);
+    
     return (void *) cfg;
 }
 
@@ -204,7 +219,12 @@ static void *suphp_merge_server_config(apr_pool_t *p, void *base,
         merged->engine = child->engine;
     else
         merged->engine = parent->engine;
-
+    
+    if (child->php_path != NULL)
+        merged->php_path = apr_pstrdup(p, child->php_path);
+    else
+        merged->php_path = apr_pstrdup(p, parent->php_path);
+    
 #ifdef SUPHP_USE_USERGROUP
     if (child->target_user)
         merged->target_user = apr_pstrdup(p, child->target_user);
@@ -221,6 +241,8 @@ static void *suphp_merge_server_config(apr_pool_t *p, void *base,
         merged->target_group = NULL;
 #endif
     
+    merged->handlers = apr_table_overlay(p, child->handlers, parent->handlers);
+    
     return (void*) merged;
 }
 
@@ -281,9 +303,14 @@ static const char *suphp_handle_cmd_user_group(cmd_parms *cmd, void *mconfig,
 
 
 static const char *suphp_handle_cmd_add_handler(cmd_parms *cmd, void *mconfig,
-                                            const char *arg)
+                                             const char *arg)
 {
-    suphp_conf *cfg = (suphp_conf *) mconfig;
+    suphp_conf *cfg;
+    if (mconfig)
+        cfg = (suphp_conf *) mconfig;
+    else
+        cfg = (suphp_conf *) ap_get_module_config(cmd->server->module_config, &suphp_module);
+    
     // Mark active handler with '1'
     apr_table_set(cfg->handlers, arg, "1");
 
@@ -292,10 +319,15 @@ static const char *suphp_handle_cmd_add_handler(cmd_parms *cmd, void *mconfig,
 
 
 static const char *suphp_handle_cmd_remove_handler(cmd_parms *cmd, 
-                                                  void *mconfig, 
-                                                  const char *arg)
+                                                   void *mconfig, 
+                                                   const char *arg)
 {
-    suphp_conf *cfg = (suphp_conf *) mconfig;
+    suphp_conf *cfg;
+    if (mconfig)
+        cfg = (suphp_conf *) mconfig;
+    else
+        cfg = (suphp_conf *) ap_get_module_config(cmd->server->module_config, &suphp_module);
+    
     // Mark deactivated handler with '0'
     apr_table_set(cfg->handlers, arg, "0");
 
@@ -303,6 +335,19 @@ static const char *suphp_handle_cmd_remove_handler(cmd_parms *cmd,
 }
 
 
+static const char *suphp_handle_cmd_phppath(cmd_parms *cmd, void* mconfig, const char *arg)
+{
+    server_rec *s = cmd->server;
+    suphp_conf *cfg;
+    
+    cfg = (suphp_conf *) ap_get_module_config(s->module_config, &suphp_module);
+    
+    cfg->php_path = apr_pstrdup(cmd->pool, arg);
+    
+    return NULL;
+}
+
+
 static const command_rec suphp_cmds[] =
 {
     AP_INIT_FLAG("suPHP_Engine", suphp_handle_cmd_engine, NULL, RSRC_CONF | ACCESS_CONF,
@@ -315,16 +360,374 @@ static const command_rec suphp_cmds[] =
 #endif
     AP_INIT_ITERATE("suPHP_AddHandler", suphp_handle_cmd_add_handler, NULL, RSRC_CONF | ACCESS_CONF, "Tells mod_suphp to handle these MIME-types"),
     AP_INIT_ITERATE("suPHP_RemoveHandler", suphp_handle_cmd_remove_handler, NULL, RSRC_CONF | ACCESS_CONF, "Tells mod_suphp not to handle these MIME-types"),
+    AP_INIT_TAKE1("suPHP_PHPPath", suphp_handle_cmd_phppath, NULL, RSRC_CONF, "Path to the PHP binary used to render source view"),
     {NULL}
 };
 
+/*****************************************
+  Code for reading script's stdout/stderr
+  based on mod_cgi's code
+ *****************************************/
+
+#if APR_FILES_AS_SOCKETS
+
+static const apr_bucket_type_t bucket_type_suphp;
+
+struct suphp_bucket_data {
+    apr_pollset_t *pollset;
+    request_rec *r;
+};
+
+static apr_bucket *suphp_bucket_create(request_rec *r, apr_file_t *out, apr_file_t *err, apr_bucket_alloc_t *list)
+{
+    apr_bucket *b = apr_bucket_alloc(sizeof(*b), list);
+    apr_status_t rv;
+    apr_pollfd_t fd;
+    struct suphp_bucket_data *data = apr_palloc(r->pool, sizeof(*data));
+    
+    APR_BUCKET_INIT(b);
+    b->free = apr_bucket_free;
+    b->list = list;
+    b->type = &bucket_type_suphp;
+    b->length = (apr_size_t) (-1);
+    b->start = (-1);
+    
+    /* Create the pollset */
+    rv = apr_pollset_create(&data->pollset, 2, r->pool, 0);
+    AP_DEBUG_ASSERT(rv == APR_SUCCESS);
+    
+    fd.desc_type = APR_POLL_FILE;
+    fd.reqevents = APR_POLLIN;
+    fd.p = r->pool;
+    fd.desc.f = out; /* script's stdout */
+    fd.client_data = (void *) 1;
+    rv = apr_pollset_add(data->pollset, &fd);
+    AP_DEBUG_ASSERT(rv == APR_SUCCESS);
+    
+    fd.desc.f = err; /* script's stderr */
+    fd.client_data = (void *) 2;
+    rv = apr_pollset_add(data->pollset, &fd);
+    AP_DEBUG_ASSERT(rv == APR_SUCCESS);
+    
+    data->r = r;
+    b->data = data;
+    return b;
+}
+
+static apr_bucket *suphp_bucket_dup(struct suphp_bucket_data *data, apr_bucket_alloc_t *list)
+{
+    apr_bucket *b = apr_bucket_alloc(sizeof(*b), list);
+    APR_BUCKET_INIT(b);
+    b->free = apr_bucket_free;
+    b->list = list;
+    b->type = &bucket_type_suphp;
+    b->length = (apr_size_t) (-1);
+    b->start = (-1);
+    b->data = data;
+    return b;
+}
+
+/* This utility method is needed, because APR's implementation for the
+   pipe bucket cannot handle or special bucket type                    */
+static apr_status_t suphp_read_fd(apr_bucket *b, apr_file_t *fd, const char **str, apr_size_t *len)
+{
+    char *buf;
+    apr_status_t rv;
+    
+    *str = NULL;
+    *len = APR_BUCKET_BUFF_SIZE;
+    buf = apr_bucket_alloc(*len, b->list);
+    
+    rv = apr_file_read(fd, buf, len);
+    
+    if (*len > 0) {
+        /* Got data */
+        struct suphp_bucket_data *data = b->data;
+        apr_bucket_heap *h;
+        
+        /* Change the current bucket to refer to what we read
+           and append the pipe bucket after it                */
+        b = apr_bucket_heap_make(b, buf, *len, apr_bucket_free);
+        /* Here, b->data is the new heap bucket data */
+        h = b->data;
+        h->alloc_len = APR_BUCKET_BUFF_SIZE; /* note the real buffer size */
+        *str = buf;
+        APR_BUCKET_INSERT_AFTER(b, suphp_bucket_dup(data, b->list));
+    } else {
+        /* Got no data */
+        apr_bucket_free(buf);
+        b = apr_bucket_immortal_make(b, "", 0);
+        /* Here, b->data is the reference to the empty string */
+        *str = b->data;
+    }
+    return rv;
+}
+
+/* Poll on stdout and stderr to make sure the process does not block
+   because of a full system (stderr) buffer                          */
+static apr_status_t suphp_bucket_read(apr_bucket *b, const char **str, apr_size_t *len, apr_read_type_e block) {
+  struct suphp_bucket_data *data = b->data;
+  apr_interval_time_t timeout;
+  apr_status_t rv;
+  int gotdata = 0;
+  
+  timeout = (block == APR_NONBLOCK_READ) ? 0 : data->r->server->timeout;
+  
+  do {
+      const apr_pollfd_t *results;
+      apr_int32_t num;
+      
+      rv = apr_pollset_poll(data->pollset, timeout, &num, &results);
+      if (APR_STATUS_IS_TIMEUP(rv)) {
+          return (timeout == 0) ? APR_EAGAIN : rv;
+      } else if (APR_STATUS_IS_EINTR(rv)) {
+          continue;
+      } else if (rv != APR_SUCCESS) {
+          ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, data->r, "Poll failed waiting for suPHP child process");
+          return rv;
+      }
+      
+      while (num > 0) {
+        if (results[0].client_data == (void *) 1) {
+            /* handle stdout */
+            rv = suphp_read_fd(b, results[0].desc.f, str, len);
+            if (APR_STATUS_IS_EOF(rv)) {
+              rv = APR_SUCCESS;
+            }
+            gotdata = 1;
+        } else {
+            /* handle stderr */
+            apr_status_t rv2 = suphp_log_script_err(data->r, results[0].desc.f);
+            if (APR_STATUS_IS_EOF(rv2)) {
+                apr_pollset_remove(data->pollset, &results[0]);
+            }
+        }
+        num--;
+        results++;
+      }
+  } while (!gotdata);
+  
+  return rv;
+}
+
+static const apr_bucket_type_t bucket_type_suphp = {
+    "SUPHP", 5, APR_BUCKET_DATA,
+    apr_bucket_destroy_noop,
+    suphp_bucket_read,
+    apr_bucket_setaside_notimpl,
+    apr_bucket_split_notimpl,
+    apr_bucket_copy_notimpl
+};
+
+#endif
+
+static void suphp_discard_output(apr_bucket_brigade *bb) {
+  apr_bucket *b;
+  const char *buf;
+  apr_size_t len;
+  apr_status_t rv;
+  for (b = APR_BRIGADE_FIRST(bb); b != APR_BRIGADE_SENTINEL(bb); b = APR_BUCKET_NEXT(b)) {
+      if (APR_BUCKET_IS_EOS(b)) {
+          break;
+      }
+      rv = apr_bucket_read(b, &buf, &len, APR_BLOCK_READ);
+      if (rv != APR_SUCCESS) {
+          break;
+      }
+  }
+}
+
 
 /******************
   Hooks / handlers
  ******************/
 
+static int suphp_script_handler(request_rec *r);
+static int suphp_source_handler(request_rec *r);
+
 static int suphp_handler(request_rec *r)
 {
+    suphp_conf *sconf, *dconf;
+    
+    sconf = ap_get_module_config(r->server->module_config, &suphp_module);
+    dconf = ap_get_module_config(r->per_dir_config, &suphp_module);
+    
+    /* only handle request if mod_suphp is active for this handler */
+    /* check only first byte of value (second has to be \0) */
+    if (apr_table_get(dconf->handlers, r->handler) != NULL)
+    {
+        if (*(apr_table_get(dconf->handlers, r->handler)) != '0')
+        {
+            return suphp_script_handler(r);
+        }
+    }
+    else
+    {
+        if ((apr_table_get(sconf->handlers, r->handler) != NULL)
+        && (*(apr_table_get(sconf->handlers, r->handler)) != '0'))
+        {
+            return suphp_script_handler(r);
+        }
+    }
+    
+    if (!strcmp(r->handler, "x-httpd-php-source") 
+    || !strcmp(r->handler, "application/x-httpd-php-source"))
+    {
+        return suphp_source_handler(r);
+    }
+    
+    return DECLINED;
+}
+
+static int suphp_source_handler(request_rec *r)
+{
+    suphp_conf *conf;
+    apr_status_t rv;
+    apr_pool_t *p;
+    apr_file_t *file;
+    apr_proc_t *proc;
+    apr_procattr_t *procattr;
+    char **argv;
+    char **env;
+    apr_bucket_brigade *bb;
+    apr_bucket *b;
+    char *phpexec;
+    
+    p = r->main ? r->main->pool : r->pool;
+    
+    if (strcmp(r->method, "GET"))
+    {
+        return DECLINED;
+    }
+    
+    conf = ap_get_module_config(r->server->module_config, &suphp_module);
+    phpexec = apr_pstrdup(p, conf->php_path);
+    if (phpexec == NULL)
+    {
+        return DECLINED;
+    }
+    
+    // Try to open file for reading to see whether is is accessible
+    rv = apr_file_open(&file, apr_pstrdup(p, r->filename), APR_READ, APR_OS_DEFAULT, p);
+    if (rv == APR_SUCCESS)
+    {
+        apr_file_close(file);
+        file = NULL;
+    }
+    else if (rv == EACCES)
+    {
+        ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r, "access to %s denied", r->filename);
+        return HTTP_FORBIDDEN;
+    }
+    else if (rv == ENOENT)
+    {
+        ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "File does not exist: %s", r->filename);
+        return HTTP_NOT_FOUND;
+    }
+    else
+    {
+        ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r, "Could not open file: %s", r->filename);
+        return HTTP_INTERNAL_SERVER_ERROR;
+    }
+    
+        env = ap_create_environment(p, r->subprocess_env);
+        
+    /* set attributes for new process */
+    
+    if (((rv = apr_procattr_create(&procattr, p)) != APR_SUCCESS)
+        || ((rv = apr_procattr_io_set(procattr, APR_CHILD_BLOCK, APR_CHILD_BLOCK, APR_CHILD_BLOCK)) != APR_SUCCESS)
+        || ((rv = apr_procattr_dir_set(procattr, ap_make_dirstr_parent(r->pool, r->filename))) != APR_SUCCESS)
+        || ((apr_procattr_cmdtype_set(procattr, APR_PROGRAM)) != APR_SUCCESS)
+        || ((apr_procattr_error_check_set(procattr, 1)) != APR_SUCCESS)
+        || ((apr_procattr_detach_set(procattr, 0)) != APR_SUCCESS))
+    {
+        ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r,
+                      "couldn't set child process attributes: %s", r->filename);
+        return HTTP_INTERNAL_SERVER_ERROR;
+    }
+
+    
+    /* create new process */
+    
+    argv = apr_palloc(p, 4 * sizeof(char *));
+    argv[0] = phpexec;
+    argv[1] = "-s";
+    argv[2] = apr_pstrdup(p, r->filename);
+    argv[3] = NULL;
+    
+    env = ap_create_environment(p, r->subprocess_env);
+    
+    proc = apr_pcalloc(p, sizeof(*proc));
+    rv = apr_proc_create(proc, phpexec, (const char *const *)argv, (const char *const *)env, procattr, p);
+    if (rv != APR_SUCCESS)
+    {
+       ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r,
+                     "couldn't create child process: %s for %s", phpexec, r->filename);
+       return HTTP_INTERNAL_SERVER_ERROR;
+    }
+    apr_pool_note_subprocess(p, proc, APR_KILL_AFTER_TIMEOUT);
+
+    if (!proc->out)
+        return APR_EBADF;
+    apr_file_pipe_timeout_set(proc->out, r->server->timeout);
+    
+    if (!proc->in)
+        return APR_EBADF;
+    apr_file_pipe_timeout_set(proc->in, r->server->timeout);
+    
+    if (!proc->err)
+        return APR_EBADF;
+    apr_file_pipe_timeout_set(proc->err, r->server->timeout);
+    
+    /* discard input */
+        
+    bb = apr_brigade_create(r->pool, r->connection->bucket_alloc);
+    
+    apr_file_flush(proc->in);
+    apr_file_close(proc->in);
+    
+    rv = ap_get_brigade(r->input_filters, bb, AP_MODE_READBYTES, APR_BLOCK_READ, HUGE_STRING_LEN);
+    if (rv != APR_SUCCESS)
+    {
+        ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r,
+                     "couldn't get input from filters: %s", r->filename);
+        return HTTP_INTERNAL_SERVER_ERROR;
+    }
+    suphp_discard_output(bb);
+    apr_brigade_cleanup(bb);
+    
+    /* get output from script */
+    
+#if APR_FILES_AS_SOCKETS
+    apr_file_pipe_timeout_set(proc->out, 0);
+    apr_file_pipe_timeout_set(proc->err, 0);
+    b = suphp_bucket_create(r, proc->out, proc->err, r->connection->bucket_alloc);
+#else
+    b = apr_bucket_pipe_create(proc->out, r->connection->bucket_alloc);
+#endif
+    APR_BRIGADE_INSERT_TAIL(bb, b);
+    
+    b = apr_bucket_eos_create(r->connection->bucket_alloc);
+    APR_BRIGADE_INSERT_TAIL(bb, b);
+
+    /* send output to browser (through filters) */
+    
+    r->content_type = "text/html";
+    rv = ap_pass_brigade(r->output_filters, bb);
+    
+    /* write errors to logfile */
+    
+    if (rv == APR_SUCCESS && !r->connection->aborted)
+    {
+        suphp_log_script_err(r, proc->err);
+        apr_file_close(proc->err);
+    }
+    
+    return OK;
+}
+
+static int suphp_script_handler(request_rec *r)
+{
     apr_pool_t *p;
     suphp_conf *sconf;
     suphp_conf *dconf;
@@ -345,10 +748,16 @@ static int suphp_handler(request_rec *r)
 #else
     char strbuf[MAX_STRING_LEN];
 #endif
+    char *tmpbuf;
     int nph = 0;
     int eos_reached = 0;
     char *auth_user = NULL;
     char *auth_pass = NULL;
+
+#ifdef SUPHP_USE_USERGROUP
+    char *ud_user = NULL;
+    char *ud_group = NULL;
+#endif
     
     apr_bucket_brigade *bb;
     apr_bucket *b;
@@ -360,12 +769,6 @@ static int suphp_handler(request_rec *r)
     dconf = ap_get_module_config(r->per_dir_config, &suphp_module);
     core_conf = (core_dir_config *) ap_get_module_config(r->per_dir_config, &core_module);
     
-    /* only handle request if mod_suphp is active for this handler */
-    /* check only first byte of value (second has to be \0) */
-    if ((apr_table_get(dconf->handlers, r->handler) == NULL)
-       || (*(apr_table_get(dconf->handlers, r->handler)) == '0'))
-       return DECLINED;
-    
     /* check if suPHP is enabled for this request */
     
     if (((sconf->engine != SUPHP_ENGINE_ON)
@@ -398,17 +801,25 @@ static int suphp_handler(request_rec *r)
     
     if (!(r->finfo.protection & APR_UREAD))
     {
-       ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "Insufficient permissions: %s", r->filename);
-       return HTTP_FORBIDDEN;
+        ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "Insufficient permissions: %s", r->filename);
+        return HTTP_FORBIDDEN;
     }
     
 #ifdef SUPHP_USE_USERGROUP
     if ((sconf->target_user == NULL || sconf->target_group == NULL)
         && (dconf->target_user == NULL || dconf->target_group == NULL))
     {
-        ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, 
-                      "No user or group set - set suPHP_UserGroup");
-        return HTTP_INTERNAL_SERVER_ERROR;
+        /* Check for userdir request */
+        ap_unix_identity_t *userdir_id = NULL;
+        userdir_id = ap_run_get_suexec_identity(r);
+        if (userdir_id != NULL && userdir_id->userdir) {
+            ud_user = apr_psprintf(r->pool, "#%ld", (long) userdir_id->uid);
+            ud_group = apr_psprintf(r->pool, "#%ld", (long) userdir_id->gid);
+        } else {
+            ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, 
+                          "No user or group set - set suPHP_UserGroup");
+            return HTTP_INTERNAL_SERVER_ERROR;
+        }
     }
 #endif
         
@@ -473,22 +884,32 @@ static int suphp_handler(request_rec *r)
         apr_table_setn(r->subprocess_env, "SUPHP_USER",
                        apr_pstrdup(r->pool, dconf->target_user));
     }
-    else
+    else if (sconf->target_user)
     {
         apr_table_setn(r->subprocess_env, "SUPHP_USER",
                        apr_pstrdup(r->pool, sconf->target_user));
     }
+    else
+    {
+        apr_table_setn(r->subprocess_env, "SUPHP_USER",
+                       apr_pstrdup(r->pool, ud_user));
+    }
     
     if (dconf->target_group)
     {
         apr_table_setn(r->subprocess_env, "SUPHP_GROUP",
                        apr_pstrdup(r->pool, dconf->target_group));
     }
-    else
+    else if (sconf->target_group)
     {
         apr_table_setn(r->subprocess_env, "SUPHP_GROUP",
                        apr_pstrdup(r->pool, sconf->target_group));
     }
+    else
+    {
+        apr_table_setn(r->subprocess_env, "SUPHP_GROUP",
+                       apr_pstrdup(r->pool, ud_group));
+    }
 #endif
     
     env = ap_create_environment(p, r->subprocess_env);
@@ -558,7 +979,7 @@ static int suphp_handler(request_rec *r)
             return rv;
         }
         
-        APR_BRIGADE_FOREACH(bucket, bb)
+        for (bucket = APR_BRIGADE_FIRST(bb); bucket != APR_BRIGADE_SENTINEL(bb); bucket = APR_BUCKET_NEXT(bucket))
         {
             const char *data;
             apr_size_t len;
@@ -592,33 +1013,41 @@ static int suphp_handler(request_rec *r)
     
     /* get output from script and check if non-parsed headers are used */
     
+#if APR_FILES_AS_SOCKETS
+    apr_file_pipe_timeout_set(proc->out, 0);
+    apr_file_pipe_timeout_set(proc->err, 0);
+    b = suphp_bucket_create(r, proc->out, proc->err, r->connection->bucket_alloc);
+#else
     b = apr_bucket_pipe_create(proc->out, r->connection->bucket_alloc);
+#endif
+
     APR_BRIGADE_INSERT_TAIL(bb, b);
     
-    len = 8;
-    if ((suphp_bucket_read(b, strbuf, len) == len)
-        && !(strcmp(strbuf, "HTTP/1.0") && strcmp(strbuf, "HTTP/1.1")))
+    b = apr_bucket_eos_create(r->connection->bucket_alloc);
+    APR_BRIGADE_INSERT_TAIL(bb, b);
+
+    tmpbuf = suphp_brigade_read(p, bb, 8);
+    if (strlen(tmpbuf) == 8 && !(strncmp(tmpbuf, "HTTP/1.0", 8) && strncmp(tmpbuf, "HTTP/1.1", 8)))
     {
         nph = 1;
     }
     
-    b = apr_bucket_eos_create(r->connection->bucket_alloc);
-    APR_BRIGADE_INSERT_TAIL(bb, b);
-    
-    if (proc->out && !nph)
+    if (!nph)
     {
-        /* normal cgi headers, so we have to create the real headers by hand */
+        /* normal cgi headers, so we have to create the real headers ourselves */
         
         int ret;
         const char *location;
-        
-       ret = ap_scan_script_header_err_brigade(r, bb, strbuf);
-       if (ret == HTTP_NOT_MODIFIED)
-       {
-           return ret;
-       }
+    
+        ret = ap_scan_script_header_err_brigade(r, bb, strbuf);
+        if (ret == HTTP_NOT_MODIFIED)
+        {
+            return ret;
+        }
         else if (ret != APR_SUCCESS)
         {
+            suphp_discard_output(bb);
+            apr_brigade_destroy(bb);
             suphp_log_script_err(r, proc->err);
             
             /* ap_scan_script_header_err_brigade does logging itself,
@@ -632,15 +1061,7 @@ static int suphp_handler(request_rec *r)
         {
             /* empty brigade (script output) and modify headers */
             
-            const char *buf;
-            apr_size_t blen;
-            APR_BRIGADE_FOREACH(b, bb)
-            {
-                if (APR_BUCKET_IS_EOS(b))
-                    break;
-                if (apr_bucket_read(b, &buf, &blen, APR_BLOCK_READ) != APR_SUCCESS)
-                    break;
-            }
+            suphp_discard_output(bb);
             apr_brigade_destroy(bb);
             suphp_log_script_err(r, proc->err);
             r->method = apr_pstrdup(r->pool, "GET");
@@ -653,16 +1074,9 @@ static int suphp_handler(request_rec *r)
         else if (location && r->status == 200)
         {
             /* empty brigade (script output) */
-            const char *buf;
-            apr_size_t blen;
-            APR_BRIGADE_FOREACH(b, bb)
-            {
-                if (APR_BUCKET_IS_EOS(b))
-                    break;
-                if (apr_bucket_read(b, &buf, &blen, APR_BLOCK_READ) != APR_SUCCESS)
-                    break;
-            }
+            suphp_discard_output(bb);
             apr_brigade_destroy(bb);
+            suphp_log_script_err(r, proc->err);
             return HTTP_MOVED_TEMPORARILY;
         }