]> granicus.if.org Git - apache/blobdiff - modules/filters/mod_include.c
Fix a segfault in mod_include when the original request has no
[apache] / modules / filters / mod_include.c
index 0354a97ba5c7b8d44bf7a6c01eec55ce40a5ad4a..b65a50ba10d1c576b664610ebcdf4c55fc63d4f0 100644 (file)
@@ -1,7 +1,7 @@
 /* ====================================================================
  * The Apache Software License, Version 1.1
  *
- * Copyright (c) 2000 The Apache Software Foundation.  All rights
+ * Copyright (c) 2000-2001 The Apache Software Foundation.  All rights
  * reserved.
  *
  * Redistribution and use in source and binary forms, with or without
 #include "apr_strings.h"
 #include "apr_thread_proc.h"
 #include "apr_hash.h"
+#include "apr_user.h"
+#include "apr_lib.h"
+#include "apr_optional.h"
+
+#define APR_WANT_STRFUNC
+#include "apr_want.h"
 
 #define CORE_PRIVATE
 
@@ -75,6 +81,7 @@
 #include "util_filter.h"
 #include "httpd.h"
 #include "http_config.h"
+#include "http_core.h"
 #include "http_request.h"
 #include "http_core.h"
 #include "http_protocol.h"
 #include "util_script.h"
 #include "http_core.h"
 #include "mod_include.h"
-#ifdef HAVE_STRING_H
-#include <string.h>
-#endif
-#ifdef HAVE_STRINGS_H
-#include <strings.h>
-#endif
-#ifdef HAVE_PWD_H
-#include <pwd.h>
-#endif
 #include "util_ebcdic.h"
 
-
+module AP_MODULE_DECLARE_DATA include_module;
 static apr_hash_t *include_hash;
+static APR_OPTIONAL_FN_TYPE(ap_register_include_handler) *ssi_pfn_register;
+
+#define BYTE_COUNT_THRESHOLD AP_MIN_BYTES_TO_WRITE
 
 /* ------------------------ Environment function -------------------------- */
 
 /* XXX: could use ap_table_overlap here */
 static void add_include_vars(request_rec *r, char *timefmt)
 {
-#ifndef WIN32
-    struct passwd *pw;
-#endif /* ndef WIN32 */
+    char *pwname;
     apr_table_t *e = r->subprocess_env;
     char *t;
     apr_time_t date = r->request_time;
@@ -115,17 +114,12 @@ static void add_include_vars(request_rec *r, char *timefmt)
               ap_ht_time(r->pool, r->finfo.mtime, timefmt, 0));
     apr_table_setn(e, "DOCUMENT_URI", r->uri);
     apr_table_setn(e, "DOCUMENT_PATH_INFO", r->path_info);
-#ifndef WIN32
-    pw = getpwuid(r->finfo.user);
-    if (pw) {
-        apr_table_setn(e, "USER_NAME", apr_pstrdup(r->pool, pw->pw_name));
+    if (apr_get_username(&pwname, r->finfo.user, r->pool) == APR_SUCCESS) {
+        apr_table_setn(e, "USER_NAME", pwname);
     }
     else {
-        apr_table_setn(e, "USER_NAME", apr_psprintf(r->pool, "user#%lu",
-                    (unsigned long) r->finfo.user));
+        apr_table_setn(e, "USER_NAME", "<unknown>");
     }
-#endif /* ndef WIN32 */
-
     if ((t = strrchr(r->filename, '/'))) {
         apr_table_setn(e, "DOCUMENT_NAME", ++t);
     }
@@ -156,6 +150,8 @@ static apr_bucket *find_start_sequence(apr_bucket *dptr, include_ctx_t *ctx,
     const char *c;
     const char *buf;
     const char *str = STARTING_SEQUENCE;
+    apr_bucket *tmp_bkt;
+    apr_size_t  start_index;
 
     *do_cleanup = 0;
 
@@ -163,13 +159,36 @@ static apr_bucket *find_start_sequence(apr_bucket *dptr, include_ctx_t *ctx,
         if (APR_BUCKET_IS_EOS(dptr)) {
             break;
         }
-        apr_bucket_read(dptr, &buf, &len, 0);
+        apr_bucket_read(dptr, &buf, &len, APR_BLOCK_READ);
         /* XXX handle retcodes */
         if (len == 0) { /* end of pipe? */
             break;
         }
         c = buf;
-        while (c - buf != len) {
+        while (c < buf + len) {
+            if (ctx->bytes_parsed >= BYTE_COUNT_THRESHOLD) {
+                apr_bucket *start_bucket;
+
+                if (ctx->head_start_index > 0) {
+                    start_index  = ctx->head_start_index;
+                    start_bucket = ctx->head_start_bucket;
+                }
+                else {
+                    start_index  = (c - buf);
+                    start_bucket = dptr;
+                }
+                apr_bucket_split(start_bucket, start_index);
+                tmp_bkt = APR_BUCKET_NEXT(start_bucket);
+                if (ctx->head_start_index > 0) {
+                    ctx->head_start_index  = 0;
+                    ctx->head_start_bucket = tmp_bkt;
+                    ctx->parse_pos = 0;
+                    ctx->state = PRE_HEAD;
+                }
+
+                return tmp_bkt;
+            }
+
             if (*c == str[ctx->parse_pos]) {
                 if (ctx->state == PRE_HEAD) {
                     ctx->state             = PARSE_HEAD;
@@ -180,10 +199,8 @@ static apr_bucket *find_start_sequence(apr_bucket *dptr, include_ctx_t *ctx,
             }
             else {
                 if (str[ctx->parse_pos] == '\0') {
-                    apr_bucket   *tmp_bkt;
-                    apr_size_t  start_index;
-
                     /* We want to split the bucket at the '<'. */
+                    ctx->bytes_parsed++;
                     ctx->state            = PARSE_DIRECTIVE;
                     ctx->tag_length       = 0;
                     ctx->parse_pos        = 0;
@@ -224,6 +241,7 @@ static apr_bucket *find_start_sequence(apr_bucket *dptr, include_ctx_t *ctx,
                 }
             }
             c++;
+            ctx->bytes_parsed++;
         }
         dptr = APR_BUCKET_NEXT(dptr);
     } while (dptr != APR_BRIGADE_SENTINEL(bb));
@@ -241,7 +259,7 @@ static apr_bucket *find_end_sequence(apr_bucket *dptr, include_ctx_t *ctx, apr_b
         if (APR_BUCKET_IS_EOS(dptr)) {
             break;
         }
-        apr_bucket_read(dptr, &buf, &len, 0);
+        apr_bucket_read(dptr, &buf, &len, APR_BLOCK_READ);
         /* XXX handle retcodes */
         if (len == 0) { /* end of pipe? */
             break;
@@ -252,7 +270,16 @@ static apr_bucket *find_end_sequence(apr_bucket *dptr, include_ctx_t *ctx, apr_b
         else {
             c = buf;
         }
-        while (c - buf != len) {
+        while (c < buf + len) {
+            if (ctx->bytes_parsed >= BYTE_COUNT_THRESHOLD) {
+                if (ctx->state == PARSE_DIRECTIVE) {
+                    /* gonna start over parsing the directive next time through */
+                    ctx->directive_length = 0;
+                    ctx->tag_length       = 0;
+                }
+                return dptr;
+            }
+
             if (*c == str[ctx->parse_pos]) {
                 if (ctx->state != PARSE_TAIL) {
                     ctx->state             = PARSE_TAIL;
@@ -292,6 +319,7 @@ static apr_bucket *find_end_sequence(apr_bucket *dptr, include_ctx_t *ctx, apr_b
                          * end of the END_SEQUENCE is in the current bucket.
                          * The beginning might be in a previous bucket.
                          */
+                        ctx->bytes_parsed++;
                         ctx->state = PARSED;
                         if ((c - buf) > 0) {
                             apr_bucket_split(dptr, c - buf);
@@ -331,6 +359,7 @@ static apr_bucket *find_end_sequence(apr_bucket *dptr, include_ctx_t *ctx, apr_b
                 }
             }
             c++;
+            ctx->bytes_parsed++;
         }
         dptr = APR_BUCKET_NEXT(dptr);
     } while (dptr != APR_BRIGADE_SENTINEL(bb));
@@ -344,10 +373,11 @@ static apr_bucket *find_end_sequence(apr_bucket *dptr, include_ctx_t *ctx, apr_b
 static apr_status_t get_combined_directive (include_ctx_t *ctx,
                                             request_rec *r,
                                             apr_bucket_brigade *bb,
-                                            char *tmp_buf, int tmp_buf_size)
+                                            char *tmp_buf, 
+                                            apr_size_t tmp_buf_size)
 {
-    int         done = 0;
-    apr_bucket  *dptr;
+    int        done = 0;
+    apr_bucket *dptr;
     const char *tmp_from;
     apr_size_t tmp_from_len;
 
@@ -392,7 +422,7 @@ static apr_status_t get_combined_directive (include_ctx_t *ctx,
             }
         }
     } while ((!done) &&
-             ((ctx->curr_tag_pos - ctx->combined_tag) < ctx->tag_length));
+             (ctx->curr_tag_pos < ctx->combined_tag + ctx->tag_length));
 
     ctx->combined_tag[ctx->tag_length] = '\0';
     ctx->curr_tag_pos = ctx->combined_tag;
@@ -505,7 +535,7 @@ otilde\365oslash\370ugrave\371uacute\372yacute\375"     /* 6 */
 
 #define SKIP_TAG_WHITESPACE(ptr) while ((*ptr != '\0') && (apr_isspace (*ptr))) ptr++
 
-static void get_tag_and_value(include_ctx_t *ctx, char **tag,
+static void ap_ssi_get_tag_and_value(include_ctx_t *ctx, char **tag,
                               char **tag_val, int dodecode)
 {
     char *c = ctx->curr_tag_pos;
@@ -570,8 +600,8 @@ static void get_tag_and_value(include_ctx_t *ctx, char **tag,
         }
     }
     
-    *c++ = '\0'; /* Overwrites delimiter (term or WS) with NULL. */
-    ctx->curr_tag_pos = c;
+    *(c-shift_val) = '\0'; /* Overwrites delimiter (term or WS) with NULL. */
+    ctx->curr_tag_pos = ++c;
     if (dodecode) {
         decodehtml(*tag_val);
     }
@@ -583,8 +613,8 @@ static void get_tag_and_value(include_ctx_t *ctx, char **tag,
 /*
  * Do variable substitution on strings
  */
-static void parse_string(request_rec *r, const char *in, char *out,
-                       size_t length, int leave_name)
+static void ap_ssi_parse_string(request_rec *r, const char *in, char *out,
+                         size_t length, int leave_name)
 {
     char ch;
     char *next = out;
@@ -610,7 +640,6 @@ static void parse_string(request_rec *r, const char *in, char *out,
             break;
         case '$':
             {
-/* pjr hack     char var[MAX_STRING_LEN]; */
                const char *start_of_var_name;
                char *end_of_var_name;  /* end of var name + 1 */
                const char *expansion, *temp_end, *val;
@@ -646,17 +675,10 @@ static void parse_string(request_rec *r, const char *in, char *out,
                 * pass a non-nul terminated string */
                l = end_of_var_name - start_of_var_name;
                if (l != 0) {
-/* pjr - this is a test hack to avoid a memcpy. Make sure that this works...
-*                  l = (l > sizeof(var) - 1) ? (sizeof(var) - 1) : l;
-*                  memcpy(var, start_of_var_name, l);
-*                  var[l] = '\0';
-*
-*                  val = apr_table_get(r->subprocess_env, var);
-*/
-/* pjr hack */      tmp_store        = *end_of_var_name;
-/* pjr hack */      *end_of_var_name = '\0';
-/* pjr hack */      val = apr_table_get(r->subprocess_env, start_of_var_name);
-/* pjr hack */      *end_of_var_name = tmp_store;
+                    tmp_store        = *end_of_var_name;
+                    *end_of_var_name = '\0';
+                    val = apr_table_get(r->subprocess_env, start_of_var_name);
+                    *end_of_var_name = tmp_store;
 
                    if (val) {
                        expansion = val;
@@ -694,69 +716,6 @@ static void parse_string(request_rec *r, const char *in, char *out,
 
 /* --------------------------- Action handlers ---------------------------- */
 
-static int include_cgi(char *s, request_rec *r, ap_filter_t *next,
-                       apr_bucket *head_ptr, apr_bucket **inserted_head)
-{
-    request_rec *rr = ap_sub_req_lookup_uri(s, r, next);
-    int rr_status;
-    apr_bucket  *tmp_buck, *tmp2_buck;
-
-    if (rr->status != HTTP_OK) {
-        return -1;
-    }
-
-    /* No hardwired path info or query allowed */
-
-    if ((rr->path_info && rr->path_info[0]) || rr->args) {
-        return -1;
-    }
-    if (rr->finfo.protection == 0) {
-        return -1;
-    }
-
-    /* Script gets parameters of the *document*, for back compatibility */
-
-    rr->path_info = r->path_info;       /* hard to get right; see mod_cgi.c */
-    rr->args = r->args;
-
-    /* Force sub_req to be treated as a CGI request, even if ordinary
-     * typing rules would have called it something else.
-     */
-
-    rr->content_type = CGI_MAGIC_TYPE;
-
-    /* Run it. */
-
-    rr_status = ap_run_sub_req(rr);
-    if (ap_is_HTTP_REDIRECT(rr_status)) {
-        apr_size_t len_loc, h_wrt;
-        const char *location = apr_table_get(rr->headers_out, "Location");
-
-        location = ap_escape_html(rr->pool, location);
-        len_loc = strlen(location);
-
-        tmp_buck = apr_bucket_create_immortal("<A HREF=\"", sizeof("<A HREF=\""));
-        APR_BUCKET_INSERT_BEFORE(head_ptr, tmp_buck);
-        tmp2_buck = apr_bucket_create_heap(location, len_loc, 1, &h_wrt);
-        APR_BUCKET_INSERT_BEFORE(head_ptr, tmp2_buck);
-        tmp2_buck = apr_bucket_create_immortal("\">", sizeof("\">"));
-        APR_BUCKET_INSERT_BEFORE(head_ptr, tmp2_buck);
-        tmp2_buck = apr_bucket_create_heap(location, len_loc, 1, &h_wrt);
-        APR_BUCKET_INSERT_BEFORE(head_ptr, tmp2_buck);
-        tmp2_buck = apr_bucket_create_immortal("</A>", sizeof("</A>"));
-        APR_BUCKET_INSERT_BEFORE(head_ptr, tmp2_buck);
-
-        if (*inserted_head == NULL) {
-            *inserted_head = tmp_buck;
-        }
-    }
-
-    ap_destroy_sub_req(rr);
-    ap_chdir_file(r->filename);
-
-    return 0;
-}
-
 /* ensure that path is relative, and does not contain ".." elements
  * ensentially ensure that it does not match the regex:
  * (^/|(^|/)\.\.(/|$))
@@ -808,7 +767,7 @@ static int handle_include(include_ctx_t *ctx, apr_bucket_brigade **bb, request_r
     *inserted_head = NULL;
     if (ctx->flags & FLAG_PRINTING) {
         while (1) {
-            get_tag_and_value(ctx, &tag, &tag_val, 1);
+            ap_ssi_get_tag_and_value(ctx, &tag, &tag_val, 1);
             if (tag_val == NULL) {
                 if (tag == NULL) {
                     return (0);
@@ -821,7 +780,7 @@ static int handle_include(include_ctx_t *ctx, apr_bucket_brigade **bb, request_r
                 request_rec *rr = NULL;
                 char *error_fmt = NULL;
 
-                parse_string(r, tag_val, parsed_string, sizeof(parsed_string), 0);
+                ap_ssi_parse_string(r, tag_val, parsed_string, sizeof(parsed_string), 0);
                 if (tag[0] == 'f') {
                     /* be safe; only files in this directory or below allowed */
                if (!is_only_below(parsed_string)) {
@@ -873,8 +832,8 @@ static int handle_include(include_ctx_t *ctx, apr_bucket_brigade **bb, request_r
                     for (p = r; p != NULL && !founddupe; p = p->main) {
                    request_rec *q;
                    for (q = p; q != NULL; q = q->prev) {
-                       if ( (strcmp(q->filename, rr->filename) == 0) ||
-                            (strcmp(q->uri, rr->uri) == 0) ){
+                       if ((q->filename && rr->filename && (strcmp(q->filename, rr->filename) == 0)) ||
+                            (strcmp(q->uri, rr->uri) == 0){
                            founddupe = 1;
                            break;
                        }
@@ -891,17 +850,15 @@ static int handle_include(include_ctx_t *ctx, apr_bucket_brigade **bb, request_r
                 /* Basically, it puts a bread crumb in here, then looks */
                 /*   for the crumb later to see if its been here.       */
            if (rr) 
-               ap_set_module_config(rr->request_config, &includes_module, r);
+               ap_set_module_config(rr->request_config, &include_module, r);
 
                 if (!error_fmt) {
-                    SPLIT_AND_PASS_PRETAG_BUCKETS(*bb, ctx);
-/*
-                    rr->output_filters = f->next;
-*/                    if (ap_run_sub_req(rr)) {
+                    SPLIT_AND_PASS_PRETAG_BUCKETS(*bb, ctx, f->next);
+                    
+                    if (ap_run_sub_req(rr)) {
                         error_fmt = "unable to include \"%s\" in parsed file %s";
                     }
                 }
-                ap_chdir_file(r->filename);
                 if (error_fmt) {
                     ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR,
                            0, r, error_fmt, tag_val, r->filename);
@@ -910,7 +867,7 @@ static int handle_include(include_ctx_t *ctx, apr_bucket_brigade **bb, request_r
 
            /* destroy the sub request if it's not a nested include (crumb) */
                 if (rr != NULL
-               && ap_get_module_config(rr->request_config, &includes_module)
+               && ap_get_module_config(rr->request_config, &include_module)
                    != NESTED_INCLUDE_MAGIC) {
                ap_destroy_sub_req(rr);
                 }
@@ -926,215 +883,6 @@ static int handle_include(include_ctx_t *ctx, apr_bucket_brigade **bb, request_r
     return 0;
 }
 
-typedef struct {
-#ifdef TPF
-    TPF_FORK_CHILD t;
-#endif
-    request_rec *r;
-    char *s;
-} include_cmd_arg;
-
-
-
-static apr_status_t build_argv_list(char ***argv, request_rec *r, apr_pool_t *p)
-{
-    int numwords, x, idx;
-    char *w;
-    const char *args = r->args;
-
-    if (!args || !args[0] || ap_strchr_c(args, '=')) {
-       numwords = 1;
-    }
-    else {
-        /* count the number of keywords */
-        for (x = 0, numwords = 1; args[x]; x++) {
-            if (args[x] == '+') {
-                ++numwords;
-            }
-        }
-    }
-    /* Everything is - 1 to account for the first parameter which is the
-     * program name.  We didn't used to have to do this, but APR wants it.
-     */
-    if (numwords > APACHE_ARG_MAX - 1) {
-        numwords = APACHE_ARG_MAX - 1; /* Truncate args to prevent overrun */
-    }
-    *argv = (char **) apr_palloc(p, (numwords + 2) * sizeof(char *));
-    for (x = 1, idx = 1; x < numwords; x++) {
-        w = ap_getword_nulls(p, &args, '+');
-        ap_unescape_url(w);
-        (*argv)[idx++] = ap_escape_shell_cmd(p, w);
-    }
-    (*argv)[idx] = NULL;
-
-    return APR_SUCCESS;
-}
-
-
-
-static int include_cmd(include_ctx_t *ctx, apr_bucket_brigade **bb, char *s,
-                       request_rec *r, ap_filter_t *f)
-{
-    include_cmd_arg arg;
-    apr_procattr_t *procattr;
-    apr_proc_t *procnew;
-    apr_status_t rc;
-    apr_table_t *env = r->subprocess_env;
-    char **argv;
-    apr_file_t *file = NULL;
-#if defined(RLIMIT_CPU)  || defined(RLIMIT_NPROC) || \
-    defined(RLIMIT_DATA) || defined(RLIMIT_VMEM) || defined (RLIMIT_AS)
-    core_dir_config *conf; 
-    conf = (core_dir_config *) ap_get_module_config(r->per_dir_config,
-                                                    &core_module);
-#endif
-
-    arg.r = r;
-    arg.s = s;
-#ifdef TPF
-    arg.t.filename = r->filename;
-    arg.t.subprocess_env = r->subprocess_env;
-    arg.t.prog_type = FORK_FILE;
-#endif
-
-    if (r->path_info && r->path_info[0] != '\0') {
-        request_rec *pa_req;
-
-        apr_table_setn(env, "PATH_INFO", ap_escape_shell_cmd(r->pool, r->path_info));
-
-        pa_req = ap_sub_req_lookup_uri(ap_escape_uri(r->pool, r->path_info), r, f->next);
-        if (pa_req->filename) {
-            apr_table_setn(env, "PATH_TRANSLATED",
-                      apr_pstrcat(r->pool, pa_req->filename, pa_req->path_info,
-                              NULL));
-        }
-    }
-
-    if (r->args) {
-        char *arg_copy = apr_pstrdup(r->pool, r->args);
-
-        apr_table_setn(env, "QUERY_STRING", r->args);
-        ap_unescape_url(arg_copy);
-        apr_table_setn(env, "QUERY_STRING_UNESCAPED",
-                  ap_escape_shell_cmd(r->pool, arg_copy));
-    }
-
-    if (((rc = apr_createprocattr_init(&procattr, r->pool)) != APR_SUCCESS) ||
-        ((rc = apr_setprocattr_io(procattr, APR_NO_PIPE, 
-                                  APR_FULL_BLOCK, APR_NO_PIPE)) != APR_SUCCESS) ||
-        ((rc = apr_setprocattr_dir(procattr, ap_make_dirstr_parent(r->pool, r->filename))) != APR_SUCCESS) ||
-#ifdef RLIMIT_CPU
-        ((rc = apr_setprocattr_limit(procattr, APR_LIMIT_CPU, conf->limit_cpu)) != APR_SUCCESS) ||
-#endif
-#if defined(RLIMIT_DATA) || defined(RLIMIT_VMEM) || defined(RLIMIT_AS)
-        ((rc = apr_setprocattr_limit(procattr, APR_LIMIT_MEM, conf->limit_mem)) != APR_SUCCESS) ||
-#endif
-#ifdef RLIMIT_NPROC
-        ((rc = apr_setprocattr_limit(procattr, APR_LIMIT_NPROC, conf->limit_nproc)) != APR_SUCCESS) ||
-#endif
-        ((rc = apr_setprocattr_cmdtype(procattr, APR_SHELLCMD)) != APR_SUCCESS)) {
-        /* Something bad happened, tell the world. */
-       ap_log_rerror(APLOG_MARK, APLOG_ERR, rc, r,
-            "couldn't initialize proc attributes: %s %s", r->filename, s);
-        rc = !APR_SUCCESS;
-    }
-    else {
-        SPLIT_AND_PASS_PRETAG_BUCKETS(*bb, ctx);
-        build_argv_list(&argv, r, r->pool);
-        argv[0] = apr_pstrdup(r->pool, s);
-        procnew = apr_pcalloc(r->pool, sizeof(*procnew));
-        rc = apr_create_process(procnew, s, (const char * const *) argv,
-                                (const char * const *)ap_create_environment(r->pool, env),
-                                procattr, r->pool);
-
-        if (rc != APR_SUCCESS) {
-            /* Bad things happened. Everyone should have cleaned up. */
-            ap_log_rerror(APLOG_MARK, APLOG_ERR, rc, r,
-                        "couldn't create child process: %d: %s", rc, s);
-        }
-        else {
-            apr_bucket_brigade *bcgi;
-            apr_bucket *b;
-
-            apr_note_subprocess(r->pool, procnew, kill_after_timeout);
-            /* Fill in BUFF structure for parents pipe to child's stdout */
-            file = procnew->out;
-            if (!file)
-                return APR_EBADF;
-            bcgi = apr_brigade_create(r->pool);
-            b = apr_bucket_create_pipe(file);
-            APR_BRIGADE_INSERT_TAIL(bcgi, b);
-            ap_pass_brigade(f->next, bcgi);
-        
-            /* We can't close the pipe here, because we may return before the
-             * full CGI has been sent to the network.  That's okay though,
-             * because we can rely on the pool to close the pipe for us.
-             */
-        }
-    }
-
-    return 0;
-}
-
-static int handle_exec(include_ctx_t *ctx, apr_bucket_brigade **bb, request_rec *r,
-                       ap_filter_t *f, apr_bucket *head_ptr, apr_bucket **inserted_head)
-{
-    char *tag     = NULL;
-    char *tag_val = NULL;
-    char *file = r->filename;
-    apr_bucket  *tmp_buck;
-    char parsed_string[MAX_STRING_LEN];
-
-    *inserted_head = NULL;
-    if (ctx->flags & FLAG_PRINTING) {
-        if (ctx->flags & FLAG_NO_EXEC) {
-            ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, 0, r,
-                      "exec used but not allowed in %s", r->filename);
-            CREATE_ERROR_BUCKET(ctx, tmp_buck, head_ptr, *inserted_head);
-        }
-        else {
-            while (1) {
-                get_tag_and_value(ctx, &tag, &tag_val, 1);
-                if (tag_val == NULL) {
-                    if (tag == NULL) {
-                        return (0);
-                    }
-                    else {
-                        return 1;
-                    }
-                }
-                if (!strcmp(tag, "cmd")) {
-                    parse_string(r, tag_val, parsed_string, sizeof(parsed_string), 1);
-                    if (include_cmd(ctx, bb, parsed_string, r, f) == -1) {
-                        ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, 0, r,
-                                    "execution failure for parameter \"%s\" "
-                                    "to tag exec in file %s", tag, r->filename);
-                        CREATE_ERROR_BUCKET(ctx, tmp_buck, head_ptr, *inserted_head);
-                    }
-                    /* just in case some stooge changed directories */
-                    ap_chdir_file(r->filename);
-                }
-                else if (!strcmp(tag, "cgi")) {
-                    parse_string(r, tag_val, parsed_string, sizeof(parsed_string), 0);
-                    SPLIT_AND_PASS_PRETAG_BUCKETS(*bb, ctx);
-                    if (include_cgi(parsed_string, r, f->next, head_ptr, inserted_head) == -1) {
-                        ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, 0, r,
-                                    "invalid CGI ref \"%s\" in %s", tag_val, file);
-                        CREATE_ERROR_BUCKET(ctx, tmp_buck, head_ptr, *inserted_head);
-                    }
-                    ap_chdir_file(r->filename);
-                }
-                else {
-                    ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, 0, r,
-                                "unknown parameter \"%s\" to tag exec in %s", tag, file);
-                    CREATE_ERROR_BUCKET(ctx, tmp_buck, head_ptr, *inserted_head);
-                }
-            }
-        }
-    }
-    return 0;
-}
 
 static int handle_echo(include_ctx_t *ctx, apr_bucket_brigade **bb, request_rec *r,
                        ap_filter_t *f, apr_bucket *head_ptr, apr_bucket **inserted_head)
@@ -1151,7 +899,7 @@ static int handle_echo(include_ctx_t *ctx, apr_bucket_brigade **bb, request_rec
     *inserted_head = NULL;
     if (ctx->flags & FLAG_PRINTING) {
         while (1) {
-            get_tag_and_value(ctx, &tag, &tag_val, 1);
+            ap_ssi_get_tag_and_value(ctx, &tag, &tag_val, 1);
             if (tag_val == NULL) {
                 if (tag != NULL) {
                     return 1;
@@ -1162,20 +910,19 @@ static int handle_echo(include_ctx_t *ctx, apr_bucket_brigade **bb, request_rec
             }
             if (!strcmp(tag, "var")) {
                 const char *val = apr_table_get(r->subprocess_env, tag_val);
-                int b_copy = 0;
 
                 if (val) {
                     switch(encode) {
-                    case E_NONE:   echo_text = val;  b_copy = 1;             break;
+                    case E_NONE:   echo_text = val;                          break;
                     case E_URL:    echo_text = ap_escape_uri(r->pool, val);  break;
                     case E_ENTITY: echo_text = ap_escape_html(r->pool, val); break;
                }
 
                     e_len = strlen(echo_text);
-                    tmp_buck = apr_bucket_create_heap(echo_text, e_len, 1, &e_wrt);
+                    tmp_buck = apr_bucket_heap_create(echo_text, e_len, 1, &e_wrt);
                 }
                 else {
-                    tmp_buck = apr_bucket_create_immortal("(none)", sizeof("none"));
+                    tmp_buck = apr_bucket_immortal_create("(none)", sizeof("(none)")-1);
                 }
                 APR_BUCKET_INSERT_BEFORE(head_ptr, tmp_buck);
                 if (*inserted_head == NULL) {
@@ -1219,7 +966,7 @@ static int handle_config(include_ctx_t *ctx, apr_bucket_brigade **bb, request_re
     *inserted_head = NULL;
     if (ctx->flags & FLAG_PRINTING) {
         while (1) {
-            get_tag_and_value(ctx, &tag, &tag_val, 0);
+            ap_ssi_get_tag_and_value(ctx, &tag, &tag_val, 0);
             if (tag_val == NULL) {
                 if (tag == NULL) {
                     return 0;  /* Reached the end of the string. */
@@ -1229,20 +976,20 @@ static int handle_config(include_ctx_t *ctx, apr_bucket_brigade **bb, request_re
                 }
             }
             if (!strcmp(tag, "errmsg")) {
-                parse_string(r, tag_val, ctx->error_str, MAX_STRING_LEN, 0);
+                ap_ssi_parse_string(r, tag_val, ctx->error_str, MAX_STRING_LEN, 0);
                 ctx->error_length = strlen(ctx->error_str);
             }
             else if (!strcmp(tag, "timefmt")) {
                 apr_time_t date = r->request_time;
 
-                parse_string(r, tag_val, ctx->time_str, MAX_STRING_LEN, 0);
+                ap_ssi_parse_string(r, tag_val, ctx->time_str, MAX_STRING_LEN, 0);
                 apr_table_setn(env, "DATE_LOCAL", ap_ht_time(r->pool, date, ctx->time_str, 0));
                 apr_table_setn(env, "DATE_GMT", ap_ht_time(r->pool, date, ctx->time_str, 1));
                 apr_table_setn(env, "LAST_MODIFIED",
                                ap_ht_time(r->pool, r->finfo.mtime, ctx->time_str, 0));
             }
             else if (!strcmp(tag, "sizefmt")) {
-                parse_string(r, tag_val, parsed_string, sizeof(parsed_string), 0);
+                ap_ssi_parse_string(r, tag_val, parsed_string, sizeof(parsed_string), 0);
                 decodehtml(parsed_string);
                 if (!strcmp(parsed_string, "bytes")) {
                     ctx->flags |= FLAG_SIZE_IN_BYTES;
@@ -1272,6 +1019,7 @@ static int find_file(request_rec *r, const char *directive, const char *tag,
     request_rec *rr = NULL;
     int ret=0;
     char *error_fmt = NULL;
+    apr_status_t rv = APR_SUCCESS;
 
     if (!strcmp(tag, "file")) {
         /* be safe; only files in this directory or below allowed */
@@ -1286,9 +1034,11 @@ static int find_file(request_rec *r, const char *directive, const char *tag,
                we never attempt to "run" this sub request. */
             rr = ap_sub_req_lookup_file(tag_val, r, NULL);
 
-            if (rr->status == HTTP_OK && rr->finfo.protection != 0) {
+            if (rr->status == HTTP_OK && rr->finfo.filetype != 0) {
                 to_send = rr->filename;
-                if (apr_stat(finfo, to_send, rr->pool) != APR_SUCCESS) {
+                if ((rv = apr_stat(finfo, to_send, APR_FINFO_GPROT 
+                                | APR_FINFO_MIN, rr->pool)) != APR_SUCCESS
+                                                     && rv != APR_INCOMPLETE) {
                     error_fmt = "unable to get information about \"%s\" "
                         "in parsed file %s";
                 }
@@ -1301,10 +1051,8 @@ static int find_file(request_rec *r, const char *directive, const char *tag,
 
         if (error_fmt) {
             ret = -1;
-            /* TODO: pass APLOG_NOERRNO if no apr_stat() failure; pass rv from apr_stat()
-             * otherwise
-             */
-            ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, error_fmt, to_send, r->filename);
+            ap_log_rerror(APLOG_MARK, APLOG_ERR | (rv ? 0 : APLOG_NOERRNO),
+                          rv, r, error_fmt, to_send, r->filename);
         }
 
         if (rr) ap_destroy_sub_req(rr);
@@ -1316,7 +1064,7 @@ static int find_file(request_rec *r, const char *directive, const char *tag,
            we never attempt to "run" this sub request. */
         rr = ap_sub_req_lookup_uri(tag_val, r, NULL);
 
-        if (rr->status == HTTP_OK && rr->finfo.protection != 0) {
+        if (rr->status == HTTP_OK && rr->finfo.filetype != 0) {
             memcpy((char *) finfo, (const char *) &rr->finfo,
                    sizeof(rr->finfo));
             ap_destroy_sub_req(rr);
@@ -1339,33 +1087,6 @@ static int find_file(request_rec *r, const char *directive, const char *tag,
     }
 }
 
-#define NEG_SIGN  "    -"
-#define ZERO_K    "   0k"
-#define ONE_K     "   1k"
-
-static void generate_size(apr_ssize_t size, char *buff, apr_size_t buff_size)
-{
-    /* XXX: this -1 thing is a gross hack */
-    if (size == (apr_ssize_t)-1) {
-       memcpy (buff, NEG_SIGN, sizeof(NEG_SIGN)+1);
-    }
-    else if (!size) {
-       memcpy (buff, ZERO_K, sizeof(ZERO_K)+1);
-    }
-    else if (size < 1024) {
-       memcpy (buff, ONE_K, sizeof(ONE_K)+1);
-    }
-    else if (size < 1048576) {
-        apr_snprintf(buff, buff_size, "%4" APR_SSIZE_T_FMT "k", (size + 512) / 1024);
-    }
-    else if (size < 103809024) {
-        apr_snprintf(buff, buff_size, "%4.1fM", size / 1048576.0);
-    }
-    else {
-        apr_snprintf(buff, buff_size, "%4" APR_SSIZE_T_FMT "M", (size + 524288) / 1048576);
-    }
-}
-
 static int handle_fsize(include_ctx_t *ctx, apr_bucket_brigade **bb, request_rec *r,
                         ap_filter_t *f, apr_bucket *head_ptr, apr_bucket **inserted_head)
 {
@@ -1379,7 +1100,7 @@ static int handle_fsize(include_ctx_t *ctx, apr_bucket_brigade **bb, request_rec
     *inserted_head = NULL;
     if (ctx->flags & FLAG_PRINTING) {
         while (1) {
-            get_tag_and_value(ctx, &tag, &tag_val, 1);
+            ap_ssi_get_tag_and_value(ctx, &tag, &tag_val, 1);
             if (tag_val == NULL) {
                 if (tag == NULL) {
                     return 0;
@@ -1389,12 +1110,12 @@ static int handle_fsize(include_ctx_t *ctx, apr_bucket_brigade **bb, request_rec
                 }
             }
             else {
-                parse_string(r, tag_val, parsed_string, sizeof(parsed_string), 0);
+                ap_ssi_parse_string(r, tag_val, parsed_string, sizeof(parsed_string), 0);
                 if (!find_file(r, "fsize", tag, parsed_string, &finfo)) {
                     char buff[50];
 
                     if (!(ctx->flags & FLAG_SIZE_IN_BYTES)) {
-                        generate_size(finfo.size, buff, sizeof(buff));
+                        apr_strfsize(finfo.size, buff);
                         s_len = strlen (buff);
                     }
                     else {
@@ -1413,7 +1134,7 @@ static int handle_fsize(include_ctx_t *ctx, apr_bucket_brigade **bb, request_rec
                         s_len = pos;
                     }
 
-                    tmp_buck = apr_bucket_create_heap(buff, s_len, 1, &s_wrt);
+                    tmp_buck = apr_bucket_heap_create(buff, s_len, 1, &s_wrt);
                     APR_BUCKET_INSERT_BEFORE(head_ptr, tmp_buck);
                     if (*inserted_head == NULL) {
                         *inserted_head = tmp_buck;
@@ -1441,7 +1162,7 @@ static int handle_flastmod(include_ctx_t *ctx, apr_bucket_brigade **bb, request_
     *inserted_head = NULL;
     if (ctx->flags & FLAG_PRINTING) {
         while (1) {
-            get_tag_and_value(ctx, &tag, &tag_val, 1);
+            ap_ssi_get_tag_and_value(ctx, &tag, &tag_val, 1);
             if (tag_val == NULL) {
                 if (tag == NULL) {
                     return 0;
@@ -1451,14 +1172,14 @@ static int handle_flastmod(include_ctx_t *ctx, apr_bucket_brigade **bb, request_
                 }
             }
             else {
-                parse_string(r, tag_val, parsed_string, sizeof(parsed_string), 0);
+                ap_ssi_parse_string(r, tag_val, parsed_string, sizeof(parsed_string), 0);
                 if (!find_file(r, "flastmod", tag, parsed_string, &finfo)) {
                     char *t_val;
 
                     t_val = ap_ht_time(r->pool, finfo.mtime, ctx->time_str, 0);
                     t_len = strlen(t_val);
 
-                    tmp_buck = apr_bucket_create_heap(t_val, t_len, 1, &t_wrt);
+                    tmp_buck = apr_bucket_heap_create(t_val, t_len, 1, &t_wrt);
                     APR_BUCKET_INSERT_BEFORE(head_ptr, tmp_buck);
                     if (*inserted_head == NULL) {
                         *inserted_head = tmp_buck;
@@ -1690,7 +1411,7 @@ static int parse_expr(request_rec *r, const char *expr, int *was_error,
         return (0);
     }
     root = current = (struct parse_node *) NULL;
-    if (apr_create_pool(&expr_pool, r->pool) != APR_SUCCESS)
+    if (apr_pool_create(&expr_pool, r->pool) != APR_SUCCESS)
                return 0;
 
     /* Create Parse Tree */
@@ -1993,7 +1714,7 @@ static int parse_expr(request_rec *r, const char *expr, int *was_error,
                     sizeof ("     Evaluate string\n"));
             debug_pos += sizeof ("     Evaluate string\n");
 #endif
-            parse_string(r, current->token.value, buffer, sizeof(buffer), 0);
+            ap_ssi_parse_string(r, current->token.value, buffer, sizeof(buffer), 0);
            apr_cpystrn(current->token.value, buffer, sizeof(current->token.value));
             current->value = (current->token.value[0] != '\0');
             current->done = 1;
@@ -2018,7 +1739,7 @@ static int parse_expr(request_rec *r, const char *expr, int *was_error,
             if (!current->left->done) {
                 switch (current->left->token.type) {
                 case token_string:
-                    parse_string(r, current->left->token.value,
+                    ap_ssi_parse_string(r, current->left->token.value,
                                  buffer, sizeof(buffer), 0);
                     apr_cpystrn(current->left->token.value, buffer,
                             sizeof(current->left->token.value));
@@ -2033,7 +1754,7 @@ static int parse_expr(request_rec *r, const char *expr, int *was_error,
             if (!current->right->done) {
                 switch (current->right->token.type) {
                 case token_string:
-                    parse_string(r, current->right->token.value,
+                    ap_ssi_parse_string(r, current->right->token.value,
                                  buffer, sizeof(buffer), 0);
                     apr_cpystrn(current->right->token.value, buffer,
                             sizeof(current->right->token.value));
@@ -2082,11 +1803,11 @@ static int parse_expr(request_rec *r, const char *expr, int *was_error,
                 *was_error = 1;
                 goto RETURN;
             }
-            parse_string(r, current->left->token.value,
+            ap_ssi_parse_string(r, current->left->token.value,
                          buffer, sizeof(buffer), 0);
             apr_cpystrn(current->left->token.value, buffer,
                        sizeof(current->left->token.value));
-            parse_string(r, current->right->token.value,
+            ap_ssi_parse_string(r, current->right->token.value,
                          buffer, sizeof(buffer), 0);
             apr_cpystrn(current->right->token.value, buffer,
                        sizeof(current->right->token.value));
@@ -2153,11 +1874,11 @@ static int parse_expr(request_rec *r, const char *expr, int *was_error,
                 *was_error = 1;
                 goto RETURN;
             }
-            parse_string(r, current->left->token.value,
+            ap_ssi_parse_string(r, current->left->token.value,
                          buffer, sizeof(buffer), 0);
             apr_cpystrn(current->left->token.value, buffer,
                        sizeof(current->left->token.value));
-            parse_string(r, current->right->token.value,
+            ap_ssi_parse_string(r, current->right->token.value,
                          buffer, sizeof(buffer), 0);
             apr_cpystrn(current->right->token.value, buffer,
                        sizeof(current->right->token.value));
@@ -2255,7 +1976,7 @@ static int parse_expr(request_rec *r, const char *expr, int *was_error,
 
     retval = (root == (struct parse_node *) NULL) ? 0 : root->value;
   RETURN:
-    apr_destroy_pool(expr_pool);
+    apr_pool_destroy(expr_pool);
     return (retval);
 }
 
@@ -2274,7 +1995,7 @@ static int parse_expr(request_rec *r, const char *expr, int *was_error,
         cond_txt[31] = '1';                                                \
     }                                                                      \
     memcpy(&cond_txt[5], tag_text, sizeof(tag_text));                      \
-    t_buck = apr_bucket_create_heap(cond_txt, sizeof(cond_txt), 1, &c_wrt); \
+    t_buck = apr_bucket_heap_create(cond_txt, sizeof(cond_txt), 1, &c_wrt); \
     APR_BUCKET_INSERT_BEFORE(h_ptr, t_buck);                                \
                                                                            \
     if (ins_head == NULL) {                                                \
@@ -2285,7 +2006,7 @@ static int parse_expr(request_rec *r, const char *expr, int *was_error,
 {                                                                        \
     apr_size_t b_wrt;                                                    \
     if (d_buf[0] != '\0') {                                              \
-        t_buck = apr_bucket_create_heap(d_buf, strlen(d_buf), 1, &b_wrt); \
+        t_buck = apr_bucket_heap_create(d_buf, strlen(d_buf), 1, &b_wrt); \
         APR_BUCKET_INSERT_BEFORE(h_ptr, t_buck);                          \
                                                                          \
         if (ins_head == NULL) {                                          \
@@ -2319,7 +2040,7 @@ static int handle_if(include_ctx_t *ctx, apr_bucket_brigade **bb, request_rec *r
     }
     else {
         while (1) {
-            get_tag_and_value(ctx, &tag, &tag_val, 0);
+            ap_ssi_get_tag_and_value(ctx, &tag, &tag_val, 0);
             if (tag == NULL) {
                 if (expr == NULL) {
                     ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, 0, r,
@@ -2354,7 +2075,7 @@ static int handle_if(include_ctx_t *ctx, apr_bucket_brigade **bb, request_rec *r
                 if (1) {
                     apr_size_t d_len = 0, d_wrt = 0;
                     d_len = sprintf(debug_buf, "**** if expr=\"%s\"\n", expr);
-                    tmp_buck = apr_bucket_create_heap(debug_buf, d_len, 1, &d_wrt);
+                    tmp_buck = apr_bucket_heap_create(debug_buf, d_len, 1, &d_wrt);
                     APR_BUCKET_INSERT_BEFORE(head_ptr, tmp_buck);
 
                     if (*inserted_head == NULL) {
@@ -2387,7 +2108,7 @@ static int handle_elif(include_ctx_t *ctx, apr_bucket_brigade **bb, request_rec
     *inserted_head = NULL;
     if (!ctx->if_nesting_level) {
         while (1) {
-            get_tag_and_value(ctx, &tag, &tag_val, 0);
+            ap_ssi_get_tag_and_value(ctx, &tag, &tag_val, 0);
             if (tag == '\0') {
                 LOG_COND_STATUS(ctx, tmp_buck, head_ptr, *inserted_head, " elif");
                 
@@ -2427,7 +2148,7 @@ static int handle_elif(include_ctx_t *ctx, apr_bucket_brigade **bb, request_rec
                 if (1) {
                     apr_size_t d_len = 0, d_wrt = 0;
                     d_len = sprintf(debug_buf, "**** elif expr=\"%s\"\n", expr);
-                    tmp_buck = apr_bucket_create_heap(debug_buf, d_len, 1, &d_wrt);
+                    tmp_buck = apr_bucket_heap_create(debug_buf, d_len, 1, &d_wrt);
                     APR_BUCKET_INSERT_BEFORE(head_ptr, tmp_buck);
 
                     if (*inserted_head == NULL) {
@@ -2455,7 +2176,7 @@ static int handle_else(include_ctx_t *ctx, apr_bucket_brigade **bb, request_rec
 
     *inserted_head = NULL;
     if (!ctx->if_nesting_level) {
-        get_tag_and_value(ctx, &tag, &tag_val, 1);
+        ap_ssi_get_tag_and_value(ctx, &tag, &tag_val, 1);
         if ((tag != NULL) || (tag_val != NULL)) {
             ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, 0, r,
                         "else directive does not take tags in %s", r->filename);
@@ -2488,7 +2209,7 @@ static int handle_endif(include_ctx_t *ctx, apr_bucket_brigade **bb, request_rec
 
     *inserted_head = NULL;
     if (!ctx->if_nesting_level) {
-        get_tag_and_value(ctx, &tag, &tag_val, 1);
+        ap_ssi_get_tag_and_value(ctx, &tag, &tag_val, 1);
         if ((tag != NULL) || (tag_val != NULL)) {
             ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, 0, r,
                         "endif directive does not take tags in %s", r->filename);
@@ -2519,7 +2240,7 @@ static int handle_set(include_ctx_t *ctx, apr_bucket_brigade **bb, request_rec *
     *inserted_head = NULL;
     if (ctx->flags & FLAG_PRINTING) {
         while (1) {
-            get_tag_and_value(ctx, &tag, &tag_val, 1);
+            ap_ssi_get_tag_and_value(ctx, &tag, &tag_val, 1);
             if ((tag == NULL) && (tag_val == NULL)) {
                 return 0;
             }
@@ -2537,7 +2258,7 @@ static int handle_set(include_ctx_t *ctx, apr_bucket_brigade **bb, request_rec *
                     CREATE_ERROR_BUCKET(ctx, tmp_buck, head_ptr, *inserted_head);
                     return (-1);
                 }
-                parse_string(r, tag_val, parsed_string, sizeof(parsed_string), 0);
+                ap_ssi_parse_string(r, tag_val, parsed_string, sizeof(parsed_string), 0);
                 apr_table_setn(r->subprocess_env, apr_pstrdup(r->pool, var),
                                apr_pstrdup(r->pool, parsed_string));
             }
@@ -2560,7 +2281,7 @@ static int handle_printenv(include_ctx_t *ctx, apr_bucket_brigade **bb, request_
     apr_bucket *tmp_buck;
 
     if (ctx->flags & FLAG_PRINTING) {
-        get_tag_and_value(ctx, &tag, &tag_val, 1);
+        ap_ssi_get_tag_and_value(ctx, &tag, &tag_val, 1);
         if ((tag == NULL) && (tag_val == NULL)) {
             apr_array_header_t *arr = apr_table_elts(r->subprocess_env);
             apr_table_entry_t *elts = (apr_table_entry_t *)arr->elts;
@@ -2576,19 +2297,19 @@ static int handle_printenv(include_ctx_t *ctx, apr_bucket_brigade **bb, request_
                 v_len = strlen(val_text);
 
                 /*  Key_text                                               */
-                tmp_buck = apr_bucket_create_heap(key_text, k_len, 1, &t_wrt);
+                tmp_buck = apr_bucket_heap_create(key_text, k_len, 1, &t_wrt);
                 APR_BUCKET_INSERT_BEFORE(head_ptr, tmp_buck);
                 if (*inserted_head == NULL) {
                     *inserted_head = tmp_buck;
                 }
                 /*            =                                            */
-                tmp_buck = apr_bucket_create_immortal("=", 1);
+                tmp_buck = apr_bucket_immortal_create("=", 1);
                 APR_BUCKET_INSERT_BEFORE(head_ptr, tmp_buck);
                 /*              Value_text                                 */
-                tmp_buck = apr_bucket_create_heap(val_text, v_len, 1, &t_wrt);
+                tmp_buck = apr_bucket_heap_create(val_text, v_len, 1, &t_wrt);
                 APR_BUCKET_INSERT_BEFORE(head_ptr, tmp_buck);
                 /*                        newline...                       */
-                tmp_buck = apr_bucket_create_immortal("\n", 1);
+                tmp_buck = apr_bucket_immortal_create("\n", 1);
                 APR_BUCKET_INSERT_BEFORE(head_ptr, tmp_buck);
             }
             return 0;
@@ -2603,19 +2324,18 @@ static int handle_printenv(include_ctx_t *ctx, apr_bucket_brigade **bb, request_
     return 0;
 }
 
-
 /* -------------------------- The main function --------------------------- */
 
-static void send_parsed_content(apr_bucket_brigade **bb, request_rec *r
-                                ap_filter_t *f)
+static apr_status_t send_parsed_content(apr_bucket_brigade **bb
+                                        request_rec *r, ap_filter_t *f)
 {
     include_ctx_t *ctx = f->ctx;
     apr_bucket *dptr = APR_BRIGADE_FIRST(*bb);
     apr_bucket *tmp_dptr;
     apr_bucket_brigade *tag_and_after;
     int ret;
+    apr_status_t rv;
 
-    ap_chdir_file(r->filename);
     if (r->args) {              /* add QUERY stuff to env cause it ain't yet */
         char *arg_copy = apr_pstrdup(r->pool, r->args);
 
@@ -2640,14 +2360,10 @@ static void send_parsed_content(apr_bucket_brigade **bb, request_rec *r,
             if ((do_cleanup) && (!APR_BRIGADE_EMPTY(ctx->ssi_tag_brigade))) {
                 apr_bucket *tmp_bkt;
 
-                tmp_bkt = apr_bucket_create_immortal(STARTING_SEQUENCE, cleanup_bytes);
+                tmp_bkt = apr_bucket_immortal_create(STARTING_SEQUENCE,
+                                                     cleanup_bytes);
                 APR_BRIGADE_INSERT_HEAD(*bb, tmp_bkt);
-
-                while (!APR_BRIGADE_EMPTY(ctx->ssi_tag_brigade)) {
-                    tmp_bkt = APR_BRIGADE_FIRST(ctx->ssi_tag_brigade);
-                    APR_BUCKET_REMOVE(tmp_bkt);
-                    apr_bucket_destroy(tmp_bkt);
-                }
+                apr_brigade_cleanup(ctx->ssi_tag_brigade);
             }
 
             /* If I am inside a conditional (if, elif, else) that is false
@@ -2660,8 +2376,7 @@ static void send_parsed_content(apr_bucket_brigade **bb, request_rec *r,
                     apr_bucket *free_bucket = dptr;
 
                     dptr = APR_BUCKET_NEXT (dptr);
-                    APR_BUCKET_REMOVE(free_bucket);
-                    apr_bucket_destroy(free_bucket);
+                    apr_bucket_delete(free_bucket);
                 }
             }
 
@@ -2674,6 +2389,17 @@ static void send_parsed_content(apr_bucket_brigade **bb, request_rec *r,
                     dptr = APR_BRIGADE_SENTINEL(*bb);
                 }
             }
+            else if ((tmp_dptr != NULL) && (ctx->bytes_parsed >= BYTE_COUNT_THRESHOLD)) {
+                               /* Send the large chunk of pre-tag bytes...  */
+                tag_and_after = apr_brigade_split(*bb, tmp_dptr);
+                rv = ap_pass_brigade(f->next, *bb);
+                if (rv != APR_SUCCESS) {
+                    return rv;
+                }
+                *bb  = tag_and_after;
+                dptr = tmp_dptr;
+                ctx->bytes_parsed = 0;
+            }
             else if (tmp_dptr == NULL) { /* There was no possible SSI tag in the */
                 dptr = APR_BRIGADE_SENTINEL(*bb);  /* remainder of this brigade...    */
             }
@@ -2700,6 +2426,9 @@ static void send_parsed_content(apr_bucket_brigade **bb, request_rec *r,
                     APR_BRIGADE_CONCAT(ctx->ssi_tag_brigade, *bb);
                     *bb = tag_and_after;
                 }
+                else if (ctx->bytes_parsed >= BYTE_COUNT_THRESHOLD) {
+                    SPLIT_AND_PASS_PRETAG_BUCKETS(*bb, ctx, f->next);
+                }
             }
             else {
                 dptr = APR_BRIGADE_SENTINEL(*bb);  /* remainder of this brigade...    */
@@ -2733,32 +2462,20 @@ static void send_parsed_content(apr_bucket_brigade **bb, request_rec *r,
                 /* DO CLEANUP HERE!!!!! */
                 tmp_dptr = ctx->head_start_bucket;
                 if (!APR_BRIGADE_EMPTY(ctx->ssi_tag_brigade)) {
-                    while (!APR_BRIGADE_EMPTY(ctx->ssi_tag_brigade)) {
-                        tmp_bkt = APR_BRIGADE_FIRST(ctx->ssi_tag_brigade);
-                        APR_BUCKET_REMOVE(tmp_bkt);
-                        apr_bucket_destroy(tmp_bkt);
-                    }
+                    apr_brigade_cleanup(ctx->ssi_tag_brigade);
                 }
                 else {
                     do {
                         tmp_bkt  = tmp_dptr;
                         tmp_dptr = APR_BUCKET_NEXT (tmp_dptr);
-                        APR_BUCKET_REMOVE(tmp_bkt);
-                        apr_bucket_destroy(tmp_bkt);
+                        apr_bucket_delete(tmp_bkt);
                     } while ((tmp_dptr != dptr) &&
                              (tmp_dptr != APR_BRIGADE_SENTINEL(*bb)));
                 }
 
-                return;
+                return APR_SUCCESS;
             }
 
-            /* Even if I don't generate any content, I know at this point that
-             *   I will at least remove the discovered SSI tag, thereby making
-             *   the content shorter than it was. This is the safest point I can
-             *   find to unset this field.
-             */
-            apr_table_unset(f->r->headers_out, "Content-Length");
-
             /* Can't destroy the tag buckets until I'm done processing
              *  because the combined_tag might just be pointing to
              *  the contents of a single bucket!
@@ -2777,9 +2494,9 @@ static void send_parsed_content(apr_bucket_brigade **bb, request_rec *r,
             ctx->curr_tag_pos = &ctx->combined_tag[ctx->directive_length+1];
 
             handle_func = 
-                (int (*)(include_ctx_t *, apr_bucket_brigade **, request_rec *,
-                    ap_filter_t *, apr_bucket *, apr_bucket **))
-                apr_hash_get(include_hash, ctx->combined_tag, ctx->directive_length+1);
+                (include_handler_fn_t *)apr_hash_get(include_hash, 
+                                                     ctx->combined_tag, 
+                                                     ctx->directive_length+1);
             if (handle_func != NULL) {
                 ret = (*handle_func)(ctx, bb, r, f, dptr, &content_head);
             }
@@ -2813,18 +2530,13 @@ static void send_parsed_content(apr_bucket_brigade **bb, request_rec *r,
             }
             tmp_dptr = ctx->head_start_bucket;
             if (!APR_BRIGADE_EMPTY(ctx->ssi_tag_brigade)) {
-                while (!APR_BRIGADE_EMPTY(ctx->ssi_tag_brigade)) {
-                    tmp_bkt = APR_BRIGADE_FIRST(ctx->ssi_tag_brigade);
-                    APR_BUCKET_REMOVE(tmp_bkt);
-                    apr_bucket_destroy(tmp_bkt);
-                }
+                apr_brigade_cleanup(ctx->ssi_tag_brigade);
             }
             else {
                 do {
                     tmp_bkt  = tmp_dptr;
                     tmp_dptr = APR_BUCKET_NEXT (tmp_dptr);
-                    APR_BUCKET_REMOVE(tmp_bkt);
-                    apr_bucket_destroy(tmp_bkt);
+                    apr_bucket_delete(tmp_bkt);
                 } while ((tmp_dptr != content_head) &&
                          (tmp_dptr != APR_BRIGADE_SENTINEL(*bb)));
             }
@@ -2846,11 +2558,7 @@ static void send_parsed_content(apr_bucket_brigade **bb, request_rec *r,
             ctx->directive_length  = 0;
 
             if (!APR_BRIGADE_EMPTY(ctx->ssi_tag_brigade)) {
-                while (!APR_BRIGADE_EMPTY(ctx->ssi_tag_brigade)) {
-                    tmp_bkt = APR_BRIGADE_FIRST(ctx->ssi_tag_brigade);
-                    APR_BUCKET_REMOVE(tmp_bkt);
-                    apr_bucket_destroy(tmp_bkt);
-                }
+                apr_brigade_cleanup(ctx->ssi_tag_brigade);
             }
 
             ctx->state     = PRE_HEAD;
@@ -2871,12 +2579,15 @@ static void send_parsed_content(apr_bucket_brigade **bb, request_rec *r,
             do {
                 free_bucket = dptr;
                 dptr = APR_BUCKET_NEXT (dptr);
-                APR_BUCKET_REMOVE(free_bucket);
-                apr_bucket_destroy(free_bucket);
+                apr_bucket_delete(free_bucket);
             } while (dptr != APR_BRIGADE_SENTINEL(*bb));
         }
         else { /* Otherwise pass it along... */
-            ap_pass_brigade(f->next, *bb);  /* No SSI tags in this brigade... */
+            rv = ap_pass_brigade(f->next, *bb);  /* No SSI tags in this brigade... */
+            if (rv != APR_SUCCESS) {
+                return rv;
+            }
+            ctx->bytes_parsed = 0;
         }
     }
     else if (ctx->state == PARSED) {     /* Invalid internal condition... */
@@ -2897,10 +2608,15 @@ static void send_parsed_content(apr_bucket_brigade **bb, request_rec *r,
             }
                            /* Set aside tag, pass pre-tag... */
             tag_and_after = apr_brigade_split(*bb, ctx->head_start_bucket);
-            ap_save_brigade(f, &ctx->ssi_tag_brigade, &tag_and_after);
-            ap_pass_brigade(f->next, *bb);
+            ap_save_brigade(f, &ctx->ssi_tag_brigade, &tag_and_after, r->pool);
+            rv = ap_pass_brigade(f->next, *bb);
+            if (rv != APR_SUCCESS) {
+                return rv;
+            }
+            ctx->bytes_parsed = 0;
         }
     }
+    return APR_SUCCESS;
 }
 
 /*****************************************************************
@@ -2909,11 +2625,17 @@ static void send_parsed_content(apr_bucket_brigade **bb, request_rec *r,
  * option only changes the default.
  */
 
-module includes_module;
+module include_module;
 enum xbithack {
     xbithack_off, xbithack_on, xbithack_full
 };
 
+typedef struct {
+    char *default_error_msg;
+    char *default_time_fmt;
+    enum xbithack *xbithack;
+} include_dir_config;
+
 #ifdef XBITHACK
 #define DEFAULT_XBITHACK xbithack_full
 #else
@@ -2922,23 +2644,28 @@ enum xbithack {
 
 static void *create_includes_dir_config(apr_pool_t *p, char *dummy)
 {
-    enum xbithack *result = (enum xbithack *) apr_palloc(p, sizeof(enum xbithack));
-    *result = DEFAULT_XBITHACK;
+    include_dir_config *result =
+        (include_dir_config *)apr_palloc(p, sizeof(include_dir_config));
+    enum xbithack *xbh = (enum xbithack *) apr_palloc(p, sizeof(enum xbithack));
+    *xbh = DEFAULT_XBITHACK;
+    result->default_error_msg = DEFAULT_ERROR_MSG;
+    result->default_time_fmt = DEFAULT_TIME_FORMAT;
+    result->xbithack = xbh;
     return result;
 }
 
 static const char *set_xbithack(cmd_parms *cmd, void *xbp, const char *arg)
 {
-    enum xbithack *state = (enum xbithack *) xbp;
+    include_dir_config *conf = (include_dir_config *)xbp;
 
     if (!strcasecmp(arg, "off")) {
-        *state = xbithack_off;
+        *conf->xbithack = xbithack_off;
     }
     else if (!strcasecmp(arg, "on")) {
-        *state = xbithack_on;
+        *conf->xbithack = xbithack_on;
     }
     else if (!strcasecmp(arg, "full")) {
-        *state = xbithack_full;
+        *conf->xbithack = xbithack_full;
     }
     else {
         return "XBitHack must be set to Off, On, or Full";
@@ -2947,18 +2674,20 @@ static const char *set_xbithack(cmd_parms *cmd, void *xbp, const char *arg)
     return NULL;
 }
 
-static int includes_filter(ap_filter_t *f, apr_bucket_brigade *b)
+static apr_status_t includes_filter(ap_filter_t *f, apr_bucket_brigade *b)
 {
     request_rec *r = f->r;
     include_ctx_t *ctx = f->ctx;
-    enum xbithack *state =
-    (enum xbithack *) ap_get_module_config(r->per_dir_config, &includes_module);
     request_rec *parent;
+    apr_status_t rv;
+    include_dir_config *conf = 
+                   (include_dir_config *)ap_get_module_config(r->per_dir_config,
+                                                              &include_module);
 
     if (!(ap_allow_options(r) & OPT_INCLUDES)) {
         return ap_pass_brigade(f->next, b);
     }
-    r->allowed |= (1 << M_GET);
+    r->allowed |= (AP_METHOD_BIT << M_GET);
     if (r->method_number != M_GET) {
         return ap_pass_brigade(f->next, b);
     }
@@ -2973,27 +2702,27 @@ static int includes_filter(ap_filter_t *f, apr_bucket_brigade *b)
             }
             ctx->ssi_tag_brigade = apr_brigade_create(f->c->pool);
 
-            apr_cpystrn(ctx->error_str, DEFAULT_ERROR_MSG,   sizeof(ctx->error_str));
-            apr_cpystrn(ctx->time_str,  DEFAULT_TIME_FORMAT, sizeof(ctx->time_str));
+            apr_cpystrn(ctx->error_str, conf->default_error_msg, sizeof(ctx->error_str));
+            apr_cpystrn(ctx->time_str, conf->default_time_fmt, sizeof(ctx->time_str));
             ctx->error_length = strlen(ctx->error_str);
         }
         else {
-            ap_pass_brigade(f->next, b);
-            return APR_ENOMEM;
+            return ap_pass_brigade(f->next, b);
         }
     }
+    else {
+        ctx->bytes_parsed = 0;
+    }
 
-    if ((*state == xbithack_full)
-#if !defined(OS2) && !defined(WIN32)
-    /*  OS/2 dosen't support Groups. */
-        && (r->finfo.protection & APR_GEXECUTE)
-#endif
-        ) {
+    /* Assure the platform supports Group protections */
+    if ((*conf->xbithack == xbithack_full)
+        && (r->finfo.valid & APR_FINFO_GPROT)
+        && (r->finfo.protection & APR_GEXECUTE)) {
         ap_update_mtime(r, r->finfo.mtime);
         ap_set_last_modified(r);
     }
 
-    if ((parent = ap_get_module_config(r->request_config, &includes_module))) {
+    if ((parent = ap_get_module_config(r->request_config, &include_module))) {
        /* Kludge --- for nested includes, we want to keep the subprocess
         * environment of the base document (for compatibility); that means
         * torquing our own last_modified date as well so that the
@@ -3011,7 +2740,7 @@ static int includes_filter(ap_filter_t *f, apr_bucket_brigade *b)
         * environment */
         ap_add_common_vars(r);
         ap_add_cgi_vars(r);
-        add_include_vars(r, DEFAULT_TIME_FORMAT);
+        add_include_vars(r, conf->default_time_fmt);
     }
     /* XXX: this is bogus, at some point we're going to do a subrequest,
      * and when we do it we're going to be subjecting code that doesn't
@@ -3020,18 +2749,27 @@ static int includes_filter(ap_filter_t *f, apr_bucket_brigade *b)
      */
 
 
-    send_parsed_content(&b, r, f);
+    /* Always unset the content-length.  There is no way to know if
+     * the content will be modified at some point by send_parsed_content.
+     * It is very possible for us to not find any content in the first
+     * 9k of the file, but still have to modify the content of the file.
+     * If we are going to pass the file through send_parsed_content, then
+     * the content-length should just be unset.
+     */
+    apr_table_unset(f->r->headers_out, "Content-Length");
+
+    rv = send_parsed_content(&b, r, f);
 
     if (parent) {
        /* signify that the sub request should not be killed */
-       ap_set_module_config(r->request_config, &includes_module,
+       ap_set_module_config(r->request_config, &include_module,
            NESTED_INCLUDE_MAGIC);
     }
 
-    return OK;
+    return rv;
 }
 
-void ap_register_include_handler(char *tag, handler func)
+static void ap_register_include_handler(char *tag, include_handler_fn_t *func)
 {
     apr_hash_set(include_hash, tag, strlen(tag) + 1, (const void *)func);
 }
@@ -3039,20 +2777,37 @@ void ap_register_include_handler(char *tag, handler func)
 static void include_post_config(apr_pool_t *p, apr_pool_t *plog,
                                 apr_pool_t *ptemp, server_rec *s)
 {
-    include_hash = apr_make_hash(p);
-
-    ap_register_include_handler("if", handle_if);
-    ap_register_include_handler("set", handle_set);
-    ap_register_include_handler("else", handle_else);
-    ap_register_include_handler("elif", handle_elif);
-    ap_register_include_handler("exec", handle_exec);
-    ap_register_include_handler("echo", handle_echo);
-    ap_register_include_handler("endif", handle_endif);
-    ap_register_include_handler("fsize", handle_fsize);
-    ap_register_include_handler("config", handle_config);
-    ap_register_include_handler("include", handle_include);
-    ap_register_include_handler("flastmod", handle_flastmod);
-    ap_register_include_handler("printenv", handle_printenv);
+    include_hash = apr_hash_make(p);
+
+    ssi_pfn_register = APR_RETRIEVE_OPTIONAL_FN(ap_register_include_handler);
+
+    if(ssi_pfn_register) {
+        ssi_pfn_register("if", handle_if);
+        ssi_pfn_register("set", handle_set);
+        ssi_pfn_register("else", handle_else);
+        ssi_pfn_register("elif", handle_elif);
+        ssi_pfn_register("echo", handle_echo);
+        ssi_pfn_register("endif", handle_endif);
+        ssi_pfn_register("fsize", handle_fsize);
+        ssi_pfn_register("config", handle_config);
+        ssi_pfn_register("include", handle_include);
+        ssi_pfn_register("flastmod", handle_flastmod);
+        ssi_pfn_register("printenv", handle_printenv);
+    }
+}
+
+static const char *set_default_error_msg(cmd_parms *cmd, void *mconfig, const char *msg)
+{
+    include_dir_config *conf = (include_dir_config *)mconfig;
+    conf->default_error_msg = apr_pstrdup(cmd->pool, msg);
+    return NULL;
+}
+
+static const char *set_default_time_fmt(cmd_parms *cmd, void *mconfig, const char *fmt)
+{
+    include_dir_config *conf = (include_dir_config *)mconfig;
+    conf->default_time_fmt = apr_pstrdup(cmd->pool, fmt);
+    return NULL;
 }
 
 /*
@@ -3062,16 +2817,53 @@ static const command_rec includes_cmds[] =
 {
     AP_INIT_TAKE1("XBitHack", set_xbithack, NULL, OR_OPTIONS, 
                   "Off, On, or Full"),
+    AP_INIT_TAKE1("SSIErrorMsg", set_default_error_msg, NULL, OR_ALL, 
+                  "a string"),
+    AP_INIT_TAKE1("SSITimeFormat", set_default_time_fmt, NULL, OR_ALL,
+                  "a strftime(3) formatted string"),
     {NULL}
 };
 
+static int xbithack_handler(request_rec *r)
+{
+#if defined(OS2) || defined(WIN32) || defined(NETWARE)
+    /* OS/2 dosen't currently support the xbithack. This is being worked on. */
+    return DECLINED;
+#else
+    enum xbithack *state;
+    if (ap_strcmp_match(r->handler, "text/html")) {
+        return DECLINED;
+    }
+    if (!(r->finfo.protection & APR_UEXECUTE)) {
+        return DECLINED;
+    }
+    state = (enum xbithack *) ap_get_module_config(r->per_dir_config,
+                                                &include_module);
+    if (*state == xbithack_off) {
+        return DECLINED;
+    }
+    /* We always return declined, because the default handler will actually
+     * serve the file.  All we have to do is add the filter.
+     */
+    ap_add_output_filter("INCLUDES", NULL, r, r->connection);
+    return DECLINED;
+#endif
+}
+
 static void register_hooks(apr_pool_t *p)
 {
+    APR_REGISTER_OPTIONAL_FN(ap_ssi_get_tag_and_value);
+    APR_REGISTER_OPTIONAL_FN(ap_ssi_parse_string);
+    APR_REGISTER_OPTIONAL_FN(ap_register_include_handler);
     ap_hook_post_config(include_post_config, NULL, NULL, APR_HOOK_REALLY_FIRST);
+    ap_hook_handler(xbithack_handler, NULL, NULL, APR_HOOK_MIDDLE);
     ap_register_output_filter("INCLUDES", includes_filter, AP_FTYPE_CONTENT);
 }
 
-module AP_MODULE_DECLARE_DATA includes_module =
+module AP_MODULE_DECLARE_DATA include_module =
 {
     STANDARD20_MODULE_STUFF,
     create_includes_dir_config, /* dir config creater */