You are viewing a plain text version of this content. The canonical link for it is here.
Posted to cvs@httpd.apache.org by ni...@apache.org on 2008/03/29 18:18:26 UTC

svn commit: r642559 - in /httpd/httpd/trunk: CHANGES modules/filters/mod_include.c

Author: niq
Date: Sat Mar 29 10:18:21 2008
New Revision: 642559

URL: http://svn.apache.org/viewvc?rev=642559&view=rev
Log:
Update mod_include to use ap_expr API

Modified:
    httpd/httpd/trunk/CHANGES
    httpd/httpd/trunk/modules/filters/mod_include.c

Modified: httpd/httpd/trunk/CHANGES
URL: http://svn.apache.org/viewvc/httpd/httpd/trunk/CHANGES?rev=642559&r1=642558&r2=642559&view=diff
==============================================================================
--- httpd/httpd/trunk/CHANGES [utf-8] (original)
+++ httpd/httpd/trunk/CHANGES [utf-8] Sat Mar 29 10:18:21 2008
@@ -3,6 +3,9 @@
 [ When backported to 2.2.x, remove entry from this file ]
 
   *) Introduced ap_expr API for expression evaluation.
+     This is adapted from mod_include, which is the first module
+     to use the new API.
+     [Nick Kew]
 
   *) mod_authz_dbd: When redirecting after successful login/logout per
      AuthzDBDRedirectQuery, do not report authorization failure, and use

Modified: httpd/httpd/trunk/modules/filters/mod_include.c
URL: http://svn.apache.org/viewvc/httpd/httpd/trunk/modules/filters/mod_include.c?rev=642559&r1=642558&r2=642559&view=diff
==============================================================================
--- httpd/httpd/trunk/modules/filters/mod_include.c (original)
+++ httpd/httpd/trunk/modules/filters/mod_include.c Sat Mar 29 10:18:21 2008
@@ -39,6 +39,7 @@
 #include "util_script.h"
 #include "http_core.h"
 #include "mod_include.h"
+#include "ap_expr.h"
 
 /* helper for Latin1 <-> entity encoding */
 #if APR_CHARSET_EBCDIC
@@ -65,45 +66,6 @@
     const char *string;
 } result_item_t;
 
-/* conditional expression parser stuff */
-typedef enum {
-    TOKEN_STRING,
-    TOKEN_RE,
-    TOKEN_AND,
-    TOKEN_OR,
-    TOKEN_NOT,
-    TOKEN_EQ,
-    TOKEN_NE,
-    TOKEN_RBRACE,
-    TOKEN_LBRACE,
-    TOKEN_GROUP,
-    TOKEN_GE,
-    TOKEN_LE,
-    TOKEN_GT,
-    TOKEN_LT,
-    TOKEN_ACCESS
-} token_type_t;
-
-typedef struct {
-    token_type_t  type;
-    const char   *value;
-#ifdef DEBUG_INCLUDE
-    const char   *s;
-#endif
-} token_t;
-
-typedef struct parse_node {
-    struct parse_node *parent;
-    struct parse_node *left;
-    struct parse_node *right;
-    token_t token;
-    int value;
-    int done;
-#ifdef DEBUG_INCLUDE
-    int dump_done;
-#endif
-} parse_node_t;
-
 typedef enum {
     XBITHACK_OFF,
     XBITHACK_ON,
@@ -154,13 +116,6 @@
 } arg_item_t;
 
 typedef struct {
-    const char *source;
-    const char *rexp;
-    apr_size_t  nsub;
-    ap_regmatch_t match[AP_MAX_REG_MATCH];
-} backref_t;
-
-typedef struct {
     unsigned int T[256];
     unsigned int x;
     apr_size_t pattern_len;
@@ -192,8 +147,10 @@
     const char   *undefined_echo;
     apr_size_t    undefined_echo_len;
 
-    int         accessenable;    /* is using the access tests allowed? */
+    opt_func_t  access_func;    /* is using the access tests allowed? */
 
+    /* breadcrumb to track whether child request should have parent's env */
+    request_rec *kludge_child;
 #ifdef DEBUG_INCLUDE
     struct {
         ap_filter_t *f;
@@ -909,633 +866,53 @@
     return ret;
 }
 
-
-/*
- * +-------------------------------------------------------+
- * |                                                       |
- * |              Conditional Expression Parser
- * |                                                       |
- * +-------------------------------------------------------+
- */
-
-static APR_INLINE int re_check(include_ctx_t *ctx, const char *string,
-                               const char *rexp)
-{
-    ap_regex_t *compiled;
-    backref_t *re = ctx->intern->re;
-    int rc;
-
-    compiled = ap_pregcomp(ctx->dpool, rexp, AP_REG_EXTENDED);
-    if (!compiled) {
-        ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, ctx->intern->r, "unable to "
-                      "compile pattern \"%s\"", rexp);
-        return -1;
-    }
-
-    if (!re) {
-        re = ctx->intern->re = apr_palloc(ctx->pool, sizeof(*re));
-    }
-
-    re->source = apr_pstrdup(ctx->pool, string);
-    re->rexp = apr_pstrdup(ctx->pool, rexp);
-    re->nsub = compiled->re_nsub;
-    rc = !ap_regexec(compiled, string, AP_MAX_REG_MATCH, re->match, 0);
-
-    ap_pregfree(ctx->dpool, compiled);
-    return rc;
-}
-
-static int get_ptoken(include_ctx_t *ctx, const char **parse, token_t *token, token_t *previous)
+static char *ssi_parse_string(request_rec *r, const char *in)
 {
-    const char *p;
-    apr_size_t shift;
-    int unmatched;
-
-    token->value = NULL;
-
-    if (!*parse) {
-        return 0;
-    }
-
-    /* Skip leading white space */
-    while (apr_isspace(**parse)) {
-        ++*parse;
-    }
-
-    if (!**parse) {
-        *parse = NULL;
-        return 0;
-    }
-
-    TYPE_TOKEN(token, TOKEN_STRING); /* the default type */
-    p = *parse;
-    unmatched = 0;
-
-    switch (*(*parse)++) {
-    case '(':
-        TYPE_TOKEN(token, TOKEN_LBRACE);
-        return 0;
-    case ')':
-        TYPE_TOKEN(token, TOKEN_RBRACE);
-        return 0;
-    case '=':
-        if (**parse == '=') ++*parse;
-        TYPE_TOKEN(token, TOKEN_EQ);
-        return 0;
-    case '!':
-        if (**parse == '=') {
-            TYPE_TOKEN(token, TOKEN_NE);
-            ++*parse;
-            return 0;
-        }
-        TYPE_TOKEN(token, TOKEN_NOT);
-        return 0;
-    case '\'':
-        unmatched = '\'';
-        break;
-    case '/':
-        /* if last token was ACCESS, this token is STRING */
-        if (previous != NULL && TOKEN_ACCESS == previous->type) {
-            break;
-        }
-        TYPE_TOKEN(token, TOKEN_RE);
-        unmatched = '/';
-        break;
-    case '|':
-        if (**parse == '|') {
-            TYPE_TOKEN(token, TOKEN_OR);
-            ++*parse;
-            return 0;
-        }
-        break;
-    case '&':
-        if (**parse == '&') {
-            TYPE_TOKEN(token, TOKEN_AND);
-            ++*parse;
-            return 0;
-        }
-        break;
-    case '>':
-        if (**parse == '=') {
-            TYPE_TOKEN(token, TOKEN_GE);
-            ++*parse;
-            return 0;
-        }
-        TYPE_TOKEN(token, TOKEN_GT);
-        return 0;
-    case '<':
-        if (**parse == '=') {
-            TYPE_TOKEN(token, TOKEN_LE);
-            ++*parse;
-            return 0;
-        }
-        TYPE_TOKEN(token, TOKEN_LT);
-        return 0;
-    case '-':
-        if (**parse == 'A' && (ctx->intern->accessenable)) {
-            TYPE_TOKEN(token, TOKEN_ACCESS);
-            ++*parse;
-            return 0;
-        }
-        break;
-    }
-
-    /* It's a string or regex token
-     * Now search for the next token, which finishes this string
-     */
-    shift = 0;
-    p = *parse = token->value = unmatched ? *parse : p;
-
-    for (; **parse; p = ++*parse) {
-        if (**parse == '\\') {
-            if (!*(++*parse)) {
-                p = *parse;
-                break;
-            }
-
-            ++shift;
-        }
-        else {
-            if (unmatched) {
-                if (**parse == unmatched) {
-                    unmatched = 0;
-                    ++*parse;
-                    break;
-                }
-            } else if (apr_isspace(**parse)) {
-                break;
-            }
-            else {
-                int found = 0;
-
-                switch (**parse) {
-                case '(':
-                case ')':
-                case '=':
-                case '!':
-                case '<':
-                case '>':
-                    ++found;
-                    break;
-
-                case '|':
-                case '&':
-                    if ((*parse)[1] == **parse) {
-                        ++found;
-                    }
-                    break;
-                }
-
-                if (found) {
-                    break;
-                }
-            }
-        }
-    }
-
-    if (unmatched) {
-        token->value = apr_pstrdup(ctx->dpool, "");
+    include_ctx_t *ctx = ap_get_module_config(r->request_config,
+                                              &include_module);
+    return ap_ssi_parse_string(ctx, in, NULL, 0, SSI_EXPAND_DROP_NAME);
+}
+static int ssi_access(request_rec *r, parse_node_t *current,
+                      string_func_t parse_string)
+{
+    request_rec *rr;
+    include_ctx_t *ctx = ap_get_module_config(r->request_config,
+                                              &include_module);
+
+    /* if this arg isn't -A, just return */
+    if (current->token.type != TOKEN_ACCESS || current->token.value[0] != 'A') {
+        ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
+                      "Unsupported option -%s in file %s",
+                      current->token.value, r->filename);
+        return 1;
+    }
+    if (current->left || !current->right ||
+        (current->right->token.type != TOKEN_STRING &&
+         current->right->token.type != TOKEN_RE)) {
+        ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
+                    "Invalid expression in file %s: Token '-A' must be followed by a URI string.",
+                    r->filename);
+        return 1;    /* was_error */
+    }
+    current->right->token.value =
+        ap_ssi_parse_string(ctx, current->right->token.value, NULL, 0,
+                            SSI_EXPAND_DROP_NAME);
+    rr = ap_sub_req_lookup_uri(current->right->token.value, r, NULL);
+    /* 400 and higher are considered access denied */
+    if (rr->status < HTTP_BAD_REQUEST) {
+        current->value = 1;
     }
     else {
-        apr_size_t len = p - token->value - shift;
-        char *c = apr_palloc(ctx->dpool, len + 1);
-
-        p = token->value;
-        token->value = c;
-
-        while (shift--) {
-            const char *e = ap_strchr_c(p, '\\');
-
-            memcpy(c, p, e-p);
-            c   += e-p;
-            *c++ = *++e;
-            len -= e-p;
-            p    = e+1;
-        }
-
-        if (len) {
-            memcpy(c, p, len);
-        }
-        c[len] = '\0';
-    }
-
-    return unmatched;
-}
-
-static int parse_expr(include_ctx_t *ctx, const char *expr, int *was_error)
-{
-    parse_node_t *new, *root = NULL, *current = NULL;
-    request_rec *r = ctx->intern->r;
-    request_rec *rr = NULL;
-    const char *error = "Invalid expression \"%s\" in file %s";
-    const char *parse = expr;
-    int was_unmatched = 0;
-    unsigned regex = 0;
-
-    *was_error = 0;
-
-    if (!parse) {
-        return 0;
+        current->value = 0;
+        ap_log_rerror(APLOG_MARK, APLOG_DEBUG, rr->status, r, 
+                      "mod_include: The tested "
+                      "subrequest -A \"%s\" returned an error code.",
+                      current->right->token.value);
     }
-
-    /* Create Parse Tree */
-    while (1) {
-        /* uncomment this to see how the tree a built:
-         *
-         * DEBUG_DUMP_TREE(ctx, root);
-         */
-        CREATE_NODE(ctx, new);
-
-        was_unmatched = get_ptoken(ctx, &parse, &new->token,
-                         (current != NULL ? &current->token : NULL));
-        if (!parse) {
-            break;
-        }
-
-        DEBUG_DUMP_UNMATCHED(ctx, was_unmatched);
-        DEBUG_DUMP_TOKEN(ctx, &new->token);
-
-        if (!current) {
-            switch (new->token.type) {
-            case TOKEN_STRING:
-            case TOKEN_NOT:
-            case TOKEN_ACCESS:
-            case TOKEN_LBRACE:
-                root = current = new;
-                continue;
-
-            default:
-                ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, error, expr,
-                              r->filename);
-                *was_error = 1;
-                return 0;
-            }
-        }
-
-        switch (new->token.type) {
-        case TOKEN_STRING:
-            switch (current->token.type) {
-            case TOKEN_STRING:
-                current->token.value =
-                    apr_pstrcat(ctx->dpool, current->token.value,
-                                *current->token.value ? " " : "",
-                                new->token.value, NULL);
-                continue;
-
-            case TOKEN_RE:
-            case TOKEN_RBRACE:
-            case TOKEN_GROUP:
-                break;
-
-            default:
-                new->parent = current;
-                current = current->right = new;
-                continue;
-            }
-            break;
-
-        case TOKEN_RE:
-            switch (current->token.type) {
-            case TOKEN_EQ:
-            case TOKEN_NE:
-                new->parent = current;
-                current = current->right = new;
-                ++regex;
-                continue;
-
-            default:
-                break;
-            }
-            break;
-
-        case TOKEN_AND:
-        case TOKEN_OR:
-            switch (current->token.type) {
-            case TOKEN_STRING:
-            case TOKEN_RE:
-            case TOKEN_GROUP:
-                current = current->parent;
-
-                while (current) {
-                    switch (current->token.type) {
-                    case TOKEN_AND:
-                    case TOKEN_OR:
-                    case TOKEN_LBRACE:
-                        break;
-
-                    default:
-                        current = current->parent;
-                        continue;
-                    }
-                    break;
-                }
-
-                if (!current) {
-                    new->left = root;
-                    root->parent = new;
-                    current = root = new;
-                    continue;
-                }
-
-                new->left = current->right;
-                new->left->parent = new;
-                new->parent = current;
-                current = current->right = new;
-                continue;
-
-            default:
-                break;
-            }
-            break;
-
-        case TOKEN_EQ:
-        case TOKEN_NE:
-        case TOKEN_GE:
-        case TOKEN_GT:
-        case TOKEN_LE:
-        case TOKEN_LT:
-            if (current->token.type == TOKEN_STRING) {
-                current = current->parent;
-
-                if (!current) {
-                    new->left = root;
-                    root->parent = new;
-                    current = root = new;
-                    continue;
-                }
-
-                switch (current->token.type) {
-                case TOKEN_LBRACE:
-                case TOKEN_AND:
-                case TOKEN_OR:
-                    new->left = current->right;
-                    new->left->parent = new;
-                    new->parent = current;
-                    current = current->right = new;
-                    continue;
-
-                default:
-                    break;
-                }
-            }
-            break;
-
-        case TOKEN_RBRACE:
-            while (current && current->token.type != TOKEN_LBRACE) {
-                current = current->parent;
-            }
-
-            if (current) {
-                TYPE_TOKEN(&current->token, TOKEN_GROUP);
-                continue;
-            }
-
-            error = "Unmatched ')' in \"%s\" in file %s";
-            break;
-
-        case TOKEN_NOT:
-        case TOKEN_ACCESS:
-        case TOKEN_LBRACE:
-            switch (current->token.type) {
-            case TOKEN_STRING:
-            case TOKEN_RE:
-            case TOKEN_RBRACE:
-            case TOKEN_GROUP:
-                break;
-
-            default:
-                current->right = new;
-                new->parent = current;
-                current = new;
-                continue;
-            }
-            break;
-
-        default:
-            break;
-        }
-
-        ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, error, expr, r->filename);
-        *was_error = 1;
-        return 0;
-    }
-
-    DEBUG_DUMP_TREE(ctx, root);
-
-    /* Evaluate Parse Tree */
-    current = root;
-    error = NULL;
-    while (current) {
-        switch (current->token.type) {
-        case TOKEN_STRING:
-            current->token.value =
-                ap_ssi_parse_string(ctx, current->token.value, NULL, 0,
-                                    SSI_EXPAND_DROP_NAME);
-            current->value = !!*current->token.value;
-            break;
-
-        case TOKEN_AND:
-        case TOKEN_OR:
-            if (!current->left || !current->right) {
-                ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
-                              "Invalid expression \"%s\" in file %s",
-                              expr, r->filename);
-                *was_error = 1;
-                return 0;
-            }
-
-            if (!current->left->done) {
-                switch (current->left->token.type) {
-                case TOKEN_STRING:
-                    current->left->token.value =
-                        ap_ssi_parse_string(ctx, current->left->token.value,
-                                            NULL, 0, SSI_EXPAND_DROP_NAME);
-                    current->left->value = !!*current->left->token.value;
-                    DEBUG_DUMP_EVAL(ctx, current->left);
-                    current->left->done = 1;
-                    break;
-
-                default:
-                    current = current->left;
-                    continue;
-                }
-            }
-
-            /* short circuit evaluation */
-            if (!current->right->done && !regex &&
-                ((current->token.type == TOKEN_AND && !current->left->value) ||
-                (current->token.type == TOKEN_OR && current->left->value))) {
-                current->value = current->left->value;
-            }
-            else {
-                if (!current->right->done) {
-                    switch (current->right->token.type) {
-                    case TOKEN_STRING:
-                        current->right->token.value =
-                            ap_ssi_parse_string(ctx,current->right->token.value,
-                                                NULL, 0, SSI_EXPAND_DROP_NAME);
-                        current->right->value = !!*current->right->token.value;
-                        DEBUG_DUMP_EVAL(ctx, current->right);
-                        current->right->done = 1;
-                        break;
-
-                    default:
-                        current = current->right;
-                        continue;
-                    }
-                }
-
-                if (current->token.type == TOKEN_AND) {
-                    current->value = current->left->value &&
-                                     current->right->value;
-                }
-                else {
-                    current->value = current->left->value ||
-                                     current->right->value;
-                }
-            }
-            break;
-
-        case TOKEN_EQ:
-        case TOKEN_NE:
-            if (!current->left || !current->right ||
-                current->left->token.type != TOKEN_STRING ||
-                (current->right->token.type != TOKEN_STRING &&
-                 current->right->token.type != TOKEN_RE)) {
-                ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
-                            "Invalid expression \"%s\" in file %s",
-                            expr, r->filename);
-                *was_error = 1;
-                return 0;
-            }
-            current->left->token.value =
-                ap_ssi_parse_string(ctx, current->left->token.value, NULL, 0,
-                                    SSI_EXPAND_DROP_NAME);
-            current->right->token.value =
-                ap_ssi_parse_string(ctx, current->right->token.value, NULL, 0,
-                                    SSI_EXPAND_DROP_NAME);
-
-            if (current->right->token.type == TOKEN_RE) {
-                current->value = re_check(ctx, current->left->token.value,
-                                          current->right->token.value);
-                --regex;
-            }
-            else {
-                current->value = !strcmp(current->left->token.value,
-                                         current->right->token.value);
-            }
-
-            if (current->token.type == TOKEN_NE) {
-                current->value = !current->value;
-            }
-            break;
-
-        case TOKEN_GE:
-        case TOKEN_GT:
-        case TOKEN_LE:
-        case TOKEN_LT:
-            if (!current->left || !current->right ||
-                current->left->token.type != TOKEN_STRING ||
-                current->right->token.type != TOKEN_STRING) {
-                ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
-                              "Invalid expression \"%s\" in file %s",
-                              expr, r->filename);
-                *was_error = 1;
-                return 0;
-            }
-
-            current->left->token.value =
-                ap_ssi_parse_string(ctx, current->left->token.value, NULL, 0,
-                                    SSI_EXPAND_DROP_NAME);
-            current->right->token.value =
-                ap_ssi_parse_string(ctx, current->right->token.value, NULL, 0,
-                                    SSI_EXPAND_DROP_NAME);
-
-            current->value = strcmp(current->left->token.value,
-                                    current->right->token.value);
-
-            switch (current->token.type) {
-            case TOKEN_GE: current->value = current->value >= 0; break;
-            case TOKEN_GT: current->value = current->value >  0; break;
-            case TOKEN_LE: current->value = current->value <= 0; break;
-            case TOKEN_LT: current->value = current->value <  0; break;
-            default: current->value = 0; break; /* should not happen */
-            }
-            break;
-
-        case TOKEN_NOT:
-        case TOKEN_GROUP:
-            if (current->right) {
-                if (!current->right->done) {
-                    current = current->right;
-                    continue;
-                }
-                current->value = current->right->value;
-            }
-            else {
-                current->value = 1;
-            }
-
-            if (current->token.type == TOKEN_NOT) {
-                current->value = !current->value;
-            }
-            break;
-
-        case TOKEN_ACCESS:
-            if (current->left || !current->right ||
-                (current->right->token.type != TOKEN_STRING &&
-                 current->right->token.type != TOKEN_RE)) {
-                ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
-                            "Invalid expression \"%s\" in file %s: Token '-A' must be followed by a URI string.",
-                            expr, r->filename);
-                *was_error = 1;
-                return 0;
-            }
-            current->right->token.value =
-                ap_ssi_parse_string(ctx, current->right->token.value, NULL, 0,
-                                    SSI_EXPAND_DROP_NAME);
-            rr = ap_sub_req_lookup_uri(current->right->token.value, r, NULL);
-            /* 400 and higher are considered access denied */
-            if (rr->status < HTTP_BAD_REQUEST) {
-                current->value = 1;
-            }
-            else {
-                current->value = 0;
-                ap_log_rerror(APLOG_MARK, APLOG_DEBUG, rr->status, r, 
-                              "mod_include: The tested "
-                              "subrequest -A \"%s\" returned an error code.",
-                              current->right->token.value);
-            }
-            ap_destroy_sub_req(rr);
-            break;
-
-        case TOKEN_RE:
-            if (!error) {
-                error = "No operator before regex in expr \"%s\" in file %s";
-            }
-        case TOKEN_LBRACE:
-            if (!error) {
-                error = "Unmatched '(' in \"%s\" in file %s";
-            }
-        default:
-            if (!error) {
-                error = "internal parser error in \"%s\" in file %s";
-            }
-
-            ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, error, expr,r->filename);
-            *was_error = 1;
-            return 0;
-        }
-
-        DEBUG_DUMP_EVAL(ctx, current);
-        current->done = 1;
-        current = current->parent;
-    }
-
-    return (root ? root->value : 0);
+    ap_destroy_sub_req(rr);
+    return 0;
 }
 
-
 /*
  * +-------------------------------------------------------+
  * |                                                       |
@@ -1735,9 +1112,7 @@
          * 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, &include_module, r);
-        }
+        ctx->intern->kludge_child = rr;
 
         if (!error_fmt && ap_run_sub_req(rr)) {
             error_fmt = "unable to include \"%s\" in parsed file %s";
@@ -2144,7 +1519,8 @@
 
     DEBUG_PRINTF((ctx, "****    if expr=\"%s\"\n", expr));
 
-    expr_ret = parse_expr(ctx, expr, &was_error);
+    expr_ret = ap_expr_evalstring(r, expr, &was_error, &ctx->intern->re,
+                                  ssi_parse_string, ctx->intern->access_func);
 
     if (was_error) {
         SSI_CREATE_ERROR_BUCKET(ctx, f, bb);
@@ -2218,7 +1594,8 @@
         return APR_SUCCESS;
     }
 
-    expr_ret = parse_expr(ctx, expr, &was_error);
+    expr_ret = ap_expr_evalstring(r, expr, &was_error, &ctx->intern->re,
+                                  ssi_parse_string, ctx->intern->access_func);
 
     if (was_error) {
         SSI_CREATE_ERROR_BUCKET(ctx, f, bb);
@@ -3549,7 +2926,6 @@
 {
     request_rec *r = f->r;
     include_ctx_t *ctx = f->ctx;
-    request_rec *parent;
     include_dir_config *conf = ap_get_module_config(r->per_dir_config,
                                                     &include_module);
 
@@ -3581,7 +2957,7 @@
         if (ap_allow_options(r) & OPT_INCNOEXEC) {
             ctx->flags |= SSI_FLAG_NO_EXEC;
         }
-        intern->accessenable = conf->accessenable;
+        intern->access_func = conf->accessenable ? ssi_access : NULL;
 
         ctx->if_nesting_level = 0;
         intern->re = NULL;
@@ -3595,9 +2971,24 @@
         intern->end_seq_len = strlen(intern->end_seq);
         intern->undefined_echo = conf->undefined_echo;
         intern->undefined_echo_len = strlen(conf->undefined_echo);
+        /* breadcrumb */
+        intern->kludge_child = NULL;
+        if (r->main != NULL) {
+            include_ctx_t *parent_ctx;
+            parent_ctx = ap_get_module_config(r->main->request_config,
+                                              &include_module);
+            /* if the subreq was created by mod_include then parent_ctx
+             * is not null.  If not ... well, we need to check.
+             */
+            if (parent_ctx) {
+                intern->kludge_child = parent_ctx->intern->kludge_child;
+            }
+        }
+        /* we need to be able to look up ctx in r for ssi_parse_string */
+        ap_set_module_config(r->request_config, &include_module, ctx);
     }
 
-    if ((parent = ap_get_module_config(r->request_config, &include_module))) {
+    if (ctx->intern->kludge_child == r) {
         /* 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