diff options
author | hongbotian <hongbo.tianhongbo@huawei.com> | 2015-11-30 03:10:21 -0500 |
---|---|---|
committer | hongbotian <hongbo.tianhongbo@huawei.com> | 2015-11-30 03:10:21 -0500 |
commit | c0b7206652b2852bc574694e7ba07ba1c2acdc00 (patch) | |
tree | 5cb95cb0e19e03610525903df46279df2c3b7eb1 /rubbos/app/httpd-2.0.64/modules/filters/mod_include.c | |
parent | b6d3d6e668b793220f2d3af1bc3e828553dc3fe6 (diff) |
delete app
Change-Id: Id4c572809969ebe89e946e88063eaed262cff3f2
Signed-off-by: hongbotian <hongbo.tianhongbo@huawei.com>
Diffstat (limited to 'rubbos/app/httpd-2.0.64/modules/filters/mod_include.c')
-rw-r--r-- | rubbos/app/httpd-2.0.64/modules/filters/mod_include.c | 3751 |
1 files changed, 0 insertions, 3751 deletions
diff --git a/rubbos/app/httpd-2.0.64/modules/filters/mod_include.c b/rubbos/app/httpd-2.0.64/modules/filters/mod_include.c deleted file mode 100644 index 38dc3213..00000000 --- a/rubbos/app/httpd-2.0.64/modules/filters/mod_include.c +++ /dev/null @@ -1,3751 +0,0 @@ -/* Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/* - * http_include.c: Handles the server-parsed HTML documents - * - * Original by Rob McCool; substantial fixups by David Robinson; - * incorporated into the Apache module framework by rst. - * - */ - -#include "apr.h" -#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 -#define APR_WANT_MEMFUNC -#include "apr_want.h" - -#define CORE_PRIVATE - -#include "ap_config.h" -#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 "http_log.h" -#include "http_main.h" -#include "util_script.h" -#include "http_core.h" - -#define MOD_INCLUDE_REDESIGN -#include "mod_include.h" -#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; - -/***************************************************************** - * - * XBITHACK. Sigh... NB it's configurable per-directory; the compile-time - * option only changes the default. - */ - -enum xbithack { - xbithack_off, xbithack_on, xbithack_full -}; - -struct bndm_t { - unsigned int T[256]; - unsigned int x; -} ; - -typedef struct { - char *default_error_msg; - char *default_time_fmt; - enum xbithack *xbithack; -} include_dir_config; - -typedef struct { - char *default_start_tag; - char *default_end_tag; - int start_tag_len; - bndm_t start_seq_pat; - char *undefinedEcho; - int undefinedEchoLen; -} include_server_config; - -/* main parser states */ -typedef enum { - PARSE_PRE_HEAD, - PARSE_HEAD, - PARSE_DIRECTIVE, - PARSE_DIRECTIVE_POSTNAME, - PARSE_DIRECTIVE_TAIL, - PARSE_DIRECTIVE_POSTTAIL, - PARSE_PRE_ARG, - PARSE_ARG, - PARSE_ARG_NAME, - PARSE_ARG_POSTNAME, - PARSE_ARG_EQ, - PARSE_ARG_PREVAL, - PARSE_ARG_VAL, - PARSE_ARG_VAL_ESC, - PARSE_ARG_POSTVAL, - PARSE_TAIL, - PARSE_TAIL_SEQ, - PARSE_EXECUTE -} parse_state_t; - -typedef struct ssi_arg_item { - struct ssi_arg_item *next; - char *name; - apr_size_t name_len; - char *value; - apr_size_t value_len; -} ssi_arg_item_t; - -typedef struct { - parse_state_t state; - int seen_eos; - int error; - char quote; /* quote character value (or \0) */ - - apr_bucket_brigade *tmp_bb; - - apr_size_t end_seq_len; - char *directive; /* name of the current directive */ - - unsigned argc; /* argument counter (of the current - * directive) - */ - ssi_arg_item_t *argv; /* all arguments */ - ssi_arg_item_t *current_arg; /* currently parsed argument */ - request_rec *r; - include_ctx_t *ctx; /* public part of the context structure */ - - apr_pool_t *dpool; -} ssi_ctx_t; - -#ifdef XBITHACK -#define DEFAULT_XBITHACK xbithack_full -#else -#define DEFAULT_XBITHACK xbithack_off -#endif - -#define BYTE_COUNT_THRESHOLD AP_MIN_BYTES_TO_WRITE - -#define SSI_CREATE_ERROR_BUCKET(ctx, f, bb) APR_BRIGADE_INSERT_TAIL((bb), \ - apr_bucket_pool_create(apr_pstrdup((ctx)->pool, (ctx)->error_str), \ - strlen((ctx)->error_str), (ctx)->pool, \ - (f)->c->bucket_alloc)) - -/* ------------------------ Environment function -------------------------- */ - -/* Sentinel value to store in subprocess_env for items that - * shouldn't be evaluated until/unless they're actually used - */ -static const char lazy_eval_sentinel; -#define LAZY_VALUE (&lazy_eval_sentinel) - -static void add_include_vars(request_rec *r, char *timefmt) -{ - apr_table_t *e = r->subprocess_env; - char *t; - - apr_table_setn(e, "DATE_LOCAL", LAZY_VALUE); - apr_table_setn(e, "DATE_GMT", LAZY_VALUE); - apr_table_setn(e, "LAST_MODIFIED", LAZY_VALUE); - apr_table_setn(e, "DOCUMENT_URI", r->uri); - if (r->path_info && *r->path_info) { - apr_table_setn(e, "DOCUMENT_PATH_INFO", r->path_info); - } - apr_table_setn(e, "USER_NAME", LAZY_VALUE); - if (r->filename && (t = strrchr(r->filename, '/'))) { - apr_table_setn(e, "DOCUMENT_NAME", ++t); - } - else { - apr_table_setn(e, "DOCUMENT_NAME", r->uri); - } - if (r->args) { - char *arg_copy = apr_pstrdup(r->pool, r->args); - - ap_unescape_url(arg_copy); - apr_table_setn(e, "QUERY_STRING_UNESCAPED", - ap_escape_shell_cmd(r->pool, arg_copy)); - } -} - -static const char *add_include_vars_lazy(request_rec *r, const char *var) -{ - char *val; - if (!strcasecmp(var, "DATE_LOCAL")) { - include_dir_config *conf = - (include_dir_config *)ap_get_module_config(r->per_dir_config, - &include_module); - val = ap_ht_time(r->pool, r->request_time, conf->default_time_fmt, 0); - } - else if (!strcasecmp(var, "DATE_GMT")) { - include_dir_config *conf = - (include_dir_config *)ap_get_module_config(r->per_dir_config, - &include_module); - val = ap_ht_time(r->pool, r->request_time, conf->default_time_fmt, 1); - } - else if (!strcasecmp(var, "LAST_MODIFIED")) { - include_dir_config *conf = - (include_dir_config *)ap_get_module_config(r->per_dir_config, - &include_module); - val = ap_ht_time(r->pool, r->finfo.mtime, conf->default_time_fmt, 0); - } - else if (!strcasecmp(var, "USER_NAME")) { - if (apr_get_username(&val, r->finfo.user, r->pool) != APR_SUCCESS) { - val = "<unknown>"; - } - } - else { - val = NULL; - } - - if (val) { - apr_table_setn(r->subprocess_env, var, val); - } - return val; -} - -static const char *get_include_var(request_rec *r, include_ctx_t *ctx, - const char *var) -{ - const char *val; - if (apr_isdigit(*var) && !var[1]) { - /* Handle $0 .. $9 from the last regex evaluated. - * The choice of returning NULL strings on not-found, - * v.s. empty strings on an empty match is deliberate. - */ - if (!ctx->re_result || !ctx->re_string) { - return NULL; - } - else { - int idx = atoi(var); - apr_size_t len = (*ctx->re_result)[idx].rm_eo - - (*ctx->re_result)[idx].rm_so; - if ( (*ctx->re_result)[idx].rm_so < 0 - || (*ctx->re_result)[idx].rm_eo < 0) { - return NULL; - } - val = apr_pstrmemdup(r->pool, ctx->re_string - + (*ctx->re_result)[idx].rm_so, len); - } - } - else { - val = apr_table_get(r->subprocess_env, var); - - if (val == LAZY_VALUE) - val = add_include_vars_lazy(r, var); - } - return val; -} - -/* --------------------------- Parser functions --------------------------- */ - -/* This is an implementation of the BNDM search algorithm. - * - * Fast and Flexible String Matching by Combining Bit-parallelism and - * Suffix Automata (2001) - * Gonzalo Navarro, Mathieu Raffinot - * - * http://www-igm.univ-mlv.fr/~raffinot/ftp/jea2001.ps.gz - * - * Initial code submitted by Sascha Schumann. - */ - -/* Precompile the bndm_t data structure. */ -static void bndm_compile(bndm_t *t, const char *n, apr_size_t nl) -{ - unsigned int x; - const char *ne = n + nl; - - memset(t->T, 0, sizeof(unsigned int) * 256); - - for (x = 1; n < ne; x <<= 1) - t->T[(unsigned char) *n++] |= x; - - t->x = x - 1; -} - -/* Implements the BNDM search algorithm (as described above). - * - * n - the pattern to search for - * nl - length of the pattern to search for - * h - the string to look in - * hl - length of the string to look for - * t - precompiled bndm structure against the pattern - * - * Returns the count of character that is the first match or hl if no - * match is found. - */ -static apr_size_t bndm(const char *n, apr_size_t nl, const char *h, - apr_size_t hl, bndm_t *t) -{ - const char *skip; - const char *he, *p, *pi; - unsigned int *T, x, d; - - he = h + hl; - - T = t->T; - x = t->x; - - pi = h - 1; /* pi: p initial */ - p = pi + nl; /* compare window right to left. point to the first char */ - - while (p < he) { - skip = p; - d = x; - do { - d &= T[(unsigned char) *p--]; - if (!d) { - break; - } - if ((d & 1)) { - if (p != pi) - skip = p; - else - return p - h + 1; - } - d >>= 1; - } while (d); - - pi = skip; - p = pi + nl; - } - - return hl; -} - -/* - * decodes a string containing html entities or numeric character references. - * 's' is overwritten with the decoded string. - * If 's' is syntatically incorrect, then the followed fixups will be made: - * unknown entities will be left undecoded; - * references to unused numeric characters will be deleted. - * In particular, � will not be decoded, but will be deleted. - * - * drtr - */ - -/* maximum length of any ISO-LATIN-1 HTML entity name. */ -#define MAXENTLEN (6) - -/* The following is a shrinking transformation, therefore safe. */ - -static void decodehtml(char *s) -{ - int val, i, j; - char *p; - const char *ents; - static const char * const entlist[MAXENTLEN + 1] = - { - NULL, /* 0 */ - NULL, /* 1 */ - "lt\074gt\076", /* 2 */ - "amp\046ETH\320eth\360", /* 3 */ - "quot\042Auml\304Euml\313Iuml\317Ouml\326Uuml\334auml\344euml\353\ -iuml\357ouml\366uuml\374yuml\377", /* 4 */ - "Acirc\302Aring\305AElig\306Ecirc\312Icirc\316Ocirc\324Ucirc\333\ -THORN\336szlig\337acirc\342aring\345aelig\346ecirc\352icirc\356ocirc\364\ -ucirc\373thorn\376", /* 5 */ - "Agrave\300Aacute\301Atilde\303Ccedil\307Egrave\310Eacute\311\ -Igrave\314Iacute\315Ntilde\321Ograve\322Oacute\323Otilde\325Oslash\330\ -Ugrave\331Uacute\332Yacute\335agrave\340aacute\341atilde\343ccedil\347\ -egrave\350eacute\351igrave\354iacute\355ntilde\361ograve\362oacute\363\ -otilde\365oslash\370ugrave\371uacute\372yacute\375" /* 6 */ - }; - - /* Do a fast scan through the string until we find anything - * that needs more complicated handling - */ - for (; *s != '&'; s++) { - if (*s == '\0') { - return; - } - } - - for (p = s; *s != '\0'; s++, p++) { - if (*s != '&') { - *p = *s; - continue; - } - /* find end of entity */ - for (i = 1; s[i] != ';' && s[i] != '\0'; i++) { - continue; - } - - if (s[i] == '\0') { /* treat as normal data */ - *p = *s; - continue; - } - - /* is it numeric ? */ - if (s[1] == '#') { - for (j = 2, val = 0; j < i && apr_isdigit(s[j]); j++) { - val = val * 10 + s[j] - '0'; - } - s += i; - if (j < i || val <= 8 || (val >= 11 && val <= 31) || - (val >= 127 && val <= 160) || val >= 256) { - p--; /* no data to output */ - } - else { - *p = RAW_ASCII_CHAR(val); - } - } - else { - j = i - 1; - if (j > MAXENTLEN || entlist[j] == NULL) { - /* wrong length */ - *p = '&'; - continue; /* skip it */ - } - for (ents = entlist[j]; *ents != '\0'; ents += i) { - if (strncmp(s + 1, ents, j) == 0) { - break; - } - } - - if (*ents == '\0') { - *p = '&'; /* unknown */ - } - else { - *p = RAW_ASCII_CHAR(((const unsigned char *) ents)[j]); - s += i; - } - } - } - - *p = '\0'; -} - -/* - * Extract the next tag name and value. - * If there are no more tags, set the tag name to NULL. - * The tag value is html decoded if dodecode is non-zero. - * The tag value may be NULL if there is no tag value.. - * format: - * [WS]<Tag>[WS]=[WS]['|"|`]<Value>[['|"|`|]|WS] - */ - -#define SKIP_TAG_WHITESPACE(ptr) while ((*ptr != '\0') && (apr_isspace (*ptr))) ptr++ - -static void ap_ssi_get_tag_and_value(include_ctx_t *ctx, char **tag, - char **tag_val, int dodecode) -{ - *tag_val = NULL; - if (ctx->curr_tag_pos >= ctx->combined_tag + ctx->tag_length) { - *tag = NULL; - return; - } - - *tag = ctx->curr_tag_pos; - if (!**tag) { - *tag = NULL; - /* finitio */ - ctx->curr_tag_pos = ctx->combined_tag + ctx->tag_length; - return; - } - - *tag_val = ap_strchr(*tag, '='); - if (!*tag_val) { - ctx->curr_tag_pos = ctx->combined_tag + ctx->tag_length; - return; - } - - /* if it starts with '=' there was no tag name, just a value */ - if (*tag_val == *tag) { - *tag = NULL; - } - - *(*tag_val)++ = '\0'; - ctx->curr_tag_pos = *tag_val + strlen(*tag_val) + 1; /* skip \0 byte */ - - if (dodecode) { - decodehtml(*tag_val); - } - - return; -} - -/* initial buffer size for power-of-two allocator in ap_ssi_parse_string */ -#define PARSE_STRING_INITIAL_SIZE 64 - -/* - * Do variable substitution on strings - * (Note: If out==NULL, this function allocs a buffer for the resulting - * string from r->pool. The return value is the parsed string) - */ -static char *ap_ssi_parse_string(request_rec *r, include_ctx_t *ctx, - const char *in, char *out, - apr_size_t length, int leave_name) -{ - char ch; - char *next; - char *end_out; - apr_size_t out_size; - - /* allocate an output buffer if needed */ - if (!out) { - out_size = PARSE_STRING_INITIAL_SIZE; - if (out_size > length) { - out_size = length; - } - out = apr_palloc(r->pool, out_size); - } - else { - out_size = length; - } - - /* leave room for nul terminator */ - end_out = out + out_size - 1; - - next = out; - while ((ch = *in++) != '\0') { - switch (ch) { - case '\\': - if (next == end_out) { - if (out_size < length) { - /* double the buffer size */ - apr_size_t new_out_size = out_size * 2; - apr_size_t current_length = next - out; - char *new_out; - if (new_out_size > length) { - new_out_size = length; - } - new_out = apr_palloc(r->pool, new_out_size); - memcpy(new_out, out, current_length); - out = new_out; - out_size = new_out_size; - end_out = out + out_size - 1; - next = out + current_length; - } - else { - /* truncated */ - *next = '\0'; - return out; - } - } - if (*in == '$') { - *next++ = *in++; - } - else { - *next++ = ch; - } - break; - case '$': - { - const char *start_of_var_name; - char *end_of_var_name; /* end of var name + 1 */ - const char *expansion, *temp_end, *val; - char tmp_store; - apr_size_t l; - - /* guess that the expansion won't happen */ - expansion = in - 1; - if (*in == '{') { - ++in; - start_of_var_name = in; - in = ap_strchr_c(in, '}'); - if (in == NULL) { - ap_log_rerror(APLOG_MARK, APLOG_ERR, - 0, r, "Missing '}' on variable \"%s\"", - expansion); - *next = '\0'; - return out; - } - temp_end = in; - end_of_var_name = (char *)temp_end; - ++in; - } - else { - start_of_var_name = in; - while (apr_isalnum(*in) || *in == '_') { - ++in; - } - temp_end = in; - end_of_var_name = (char *)temp_end; - } - /* what a pain, too bad there's no table_getn where you can - * pass a non-nul terminated string */ - l = end_of_var_name - start_of_var_name; - if (l != 0) { - tmp_store = *end_of_var_name; - *end_of_var_name = '\0'; - val = get_include_var(r, ctx, start_of_var_name); - *end_of_var_name = tmp_store; - - if (val) { - expansion = val; - l = strlen(expansion); - } - else if (leave_name) { - l = in - expansion; - } - else { - /* no expansion to be done */ - break; - } - } - else { - /* zero-length variable name causes just the $ to be - * copied */ - l = 1; - } - if ((next + l > end_out) && (out_size < length)) { - /* increase the buffer size to accommodate l more chars */ - apr_size_t new_out_size = out_size; - apr_size_t current_length = next - out; - char *new_out; - do { - new_out_size *= 2; - } while (new_out_size < current_length + l + 1); /* +1 for NUL */ - if (new_out_size > length) { - new_out_size = length; - } - new_out = apr_palloc(r->pool, new_out_size); - memcpy(new_out, out, current_length); - out = new_out; - out_size = new_out_size; - end_out = out + out_size - 1; - next = out + current_length; - } - l = ((int)l > end_out - next) ? (end_out - next) : l; - memcpy(next, expansion, l); - next += l; - break; - } - default: - if (next == end_out) { - if (out_size < length) { - /* double the buffer size */ - apr_size_t new_out_size = out_size * 2; - apr_size_t current_length = next - out; - char *new_out; - if (new_out_size > length) { - new_out_size = length; - } - new_out = apr_palloc(r->pool, new_out_size); - memcpy(new_out, out, current_length); - out = new_out; - out_size = new_out_size; - end_out = out + out_size - 1; - next = out + current_length; - } - else { - /* truncated */ - *next = '\0'; - return out; - } - } - *next++ = ch; - break; - } - } - *next = '\0'; - return out; -} - -/* --------------------------- Action handlers ---------------------------- */ - -/* ensure that path is relative, and does not contain ".." elements - * ensentially ensure that it does not match the regex: - * (^/|(^|/)\.\.(/|$)) - * XXX: Simply replace with apr_filepath_merge - */ -static int is_only_below(const char *path) -{ -#ifdef HAVE_DRIVE_LETTERS - if (path[1] == ':') - return 0; -#endif -#ifdef NETWARE - if (ap_strchr_c(path, ':')) - return 0; -#endif - if (path[0] == '/') { - return 0; - } - while (*path) { - int dots = 0; - while (path[dots] == '.') - ++dots; -#if defined(WIN32) - /* If the name is canonical this is redundant - * but in security, redundancy is worthwhile. - * Does OS2 belong here (accepts ... for ..)? - */ - if (dots > 1 && (!path[dots] || path[dots] == '/')) - return 0; -#else - if (dots == 2 && (!path[dots] || path[dots] == '/')) - return 0; -#endif - path += dots; - /* Advance to either the null byte at the end of the - * string or the character right after the next slash, - * whichever comes first - */ - while (*path && (*path++ != '/')) { - continue; - } - } - return 1; -} - -static int handle_include(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; - apr_bucket *tmp_buck; - char *parsed_string; - int loglevel = APLOG_ERR; - - *inserted_head = NULL; - if (ctx->flags & FLAG_PRINTING) { - while (1) { - ap_ssi_get_tag_and_value(ctx, &tag, &tag_val, 1); - if (tag_val == NULL) { - if (tag == NULL) { - return (0); - } - else { - return (1); - } - } - if (!strcmp(tag, "virtual") || !strcmp(tag, "file")) { - request_rec *rr = NULL; - char *error_fmt = NULL; - apr_status_t rc = APR_SUCCESS; - - SPLIT_AND_PASS_PRETAG_BUCKETS(*bb, ctx, f->next, rc); - if (rc != APR_SUCCESS) { - return rc; - } - - parsed_string = ap_ssi_parse_string(r, ctx, tag_val, NULL, - MAX_STRING_LEN, 0); - if (tag[0] == 'f') { - /* XXX: Port to apr_filepath_merge - * be safe; only files in this directory or below allowed - */ - if (!is_only_below(parsed_string)) { - error_fmt = "unable to include file \"%s\" " - "in parsed file %s"; - } - else { - rr = ap_sub_req_lookup_uri(parsed_string, r, f->next); - } - } - else { - rr = ap_sub_req_lookup_uri(parsed_string, r, f->next); - } - - if (!error_fmt && rr->status != HTTP_OK) { - error_fmt = "unable to include \"%s\" in parsed file %s"; - } - - if (!error_fmt && (ctx->flags & FLAG_NO_EXEC) && - rr->content_type && - (strncmp(rr->content_type, "text/", 5))) { - error_fmt = "unable to include potential exec \"%s\" " - "in parsed file %s"; - } - - /* See the Kludge in send_parsed_file for why */ - /* 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); - - if (!error_fmt && ap_run_sub_req(rr)) { - error_fmt = "unable to include \"%s\" in parsed file %s"; - } - if (error_fmt) { - ap_log_rerror(APLOG_MARK, loglevel, - 0, r, error_fmt, tag_val, r->filename); - CREATE_ERROR_BUCKET(ctx, tmp_buck, head_ptr, - *inserted_head); - } - - /* Do *not* destroy the subrequest here; it may have allocated - * variables in this r->subprocess_env in the subrequest's - * r->pool, so that pool must survive as long as this request. - * Yes, this is a memory leak. */ - } - else { - ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, - "unknown parameter \"%s\" to tag include in %s", - tag, r->filename); - CREATE_ERROR_BUCKET(ctx, tmp_buck, head_ptr, *inserted_head); - return 1; - } - } - } - 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) -{ - char *tag = NULL; - char *tag_val = NULL; - const char *echo_text = NULL; - apr_bucket *tmp_buck; - apr_size_t e_len; - enum {E_NONE, E_URL, E_ENTITY} encode; - - encode = E_ENTITY; - - *inserted_head = NULL; - if (ctx->flags & FLAG_PRINTING) { - while (1) { - ap_ssi_get_tag_and_value(ctx, &tag, &tag_val, 1); - if (tag_val == NULL) { - if (tag != NULL) { - return 1; - } - else { - return 0; - } - } - if (!strcmp(tag, "var")) { - conn_rec *c = r->connection; - const char *val = - get_include_var(r, ctx, - ap_ssi_parse_string(r, ctx, tag_val, NULL, - MAX_STRING_LEN, 0)); - if (val) { - switch(encode) { - 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_pool_create(echo_text, e_len, - r->pool, c->bucket_alloc); - } - else { - include_server_config *sconf= - ap_get_module_config(r->server->module_config, - &include_module); - tmp_buck = apr_bucket_pool_create(sconf->undefinedEcho, - sconf->undefinedEchoLen, - r->pool, c->bucket_alloc); - } - APR_BUCKET_INSERT_BEFORE(head_ptr, tmp_buck); - if (*inserted_head == NULL) { - *inserted_head = tmp_buck; - } - } - else if (!strcmp(tag, "encoding")) { - if (!strcasecmp(tag_val, "none")) encode = E_NONE; - else if (!strcasecmp(tag_val, "url")) encode = E_URL; - else if (!strcasecmp(tag_val, "entity")) encode = E_ENTITY; - else { - ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, - "unknown value \"%s\" to parameter \"encoding\" of " - "tag echo in %s", tag_val, r->filename); - CREATE_ERROR_BUCKET(ctx, tmp_buck, head_ptr, - *inserted_head); - return 1; - } - } - else { - ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, - "unknown parameter \"%s\" in tag echo of %s", - tag, r->filename); - CREATE_ERROR_BUCKET(ctx, tmp_buck, head_ptr, *inserted_head); - return 1; - } - - } - } - return 0; -} - -/* error and tf must point to a string with room for at - * least MAX_STRING_LEN characters - */ -static int handle_config(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 *parsed_string; - apr_table_t *env = r->subprocess_env; - - *inserted_head = NULL; - if (ctx->flags & FLAG_PRINTING) { - while (1) { - 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. */ - } - else { - return 1; /* tags must have values. */ - } - } - if (!strcmp(tag, "errmsg")) { - if (ctx->error_str_override == NULL) { - ctx->error_str_override = (char *)apr_palloc(ctx->pool, - MAX_STRING_LEN); - ctx->error_str = ctx->error_str_override; - } - ap_ssi_parse_string(r, ctx, tag_val, ctx->error_str_override, - MAX_STRING_LEN, 0); - } - else if (!strcmp(tag, "timefmt")) { - apr_time_t date = r->request_time; - if (ctx->time_str_override == NULL) { - ctx->time_str_override = (char *)apr_palloc(ctx->pool, - MAX_STRING_LEN); - ctx->time_str = ctx->time_str_override; - } - ap_ssi_parse_string(r, ctx, tag_val, ctx->time_str_override, - 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")) { - parsed_string = ap_ssi_parse_string(r, ctx, tag_val, NULL, - MAX_STRING_LEN, 0); - decodehtml(parsed_string); - if (!strcmp(parsed_string, "bytes")) { - ctx->flags |= FLAG_SIZE_IN_BYTES; - } - else if (!strcmp(parsed_string, "abbrev")) { - ctx->flags &= FLAG_SIZE_ABBREV; - } - } - else { - apr_bucket *tmp_buck; - - ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, - "unknown parameter \"%s\" to tag config in %s", - tag, r->filename); - CREATE_ERROR_BUCKET(ctx, tmp_buck, head_ptr, *inserted_head); - return 1; - } - } - } - return 0; -} - - -static int find_file(request_rec *r, const char *directive, const char *tag, - char *tag_val, apr_finfo_t *finfo) -{ - char *to_send = tag_val; - request_rec *rr = NULL; - int ret=0; - char *error_fmt = NULL; - apr_status_t rv = APR_SUCCESS; - - if (!strcmp(tag, "file")) { - /* XXX: Port to apr_filepath_merge - * be safe; only files in this directory or below allowed - */ - if (!is_only_below(tag_val)) { - error_fmt = "unable to access file \"%s\" " - "in parsed file %s"; - } - else { - ap_getparents(tag_val); /* get rid of any nasties */ - - /* note: it is okay to pass NULL for the "next filter" since - 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.filetype != 0) { - to_send = rr->filename; - 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"; - } - } - else { - error_fmt = "unable to lookup information about \"%s\" " - "in parsed file %s"; - } - } - - if (error_fmt) { - ret = -1; - ap_log_rerror(APLOG_MARK, APLOG_ERR, - rv, r, error_fmt, to_send, r->filename); - } - - if (rr) ap_destroy_sub_req(rr); - - return ret; - } - else if (!strcmp(tag, "virtual")) { - /* note: it is okay to pass NULL for the "next filter" since - 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.filetype != 0) { - memcpy((char *) finfo, (const char *) &rr->finfo, - sizeof(rr->finfo)); - ap_destroy_sub_req(rr); - return 0; - } - else { - ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, - "unable to get information about \"%s\" " - "in parsed file %s", - tag_val, r->filename); - ap_destroy_sub_req(rr); - return -1; - } - } - else { - ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, - "unknown parameter \"%s\" to tag %s in %s", - tag, directive, r->filename); - return -1; - } -} - -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) -{ - char *tag = NULL; - char *tag_val = NULL; - apr_finfo_t finfo; - apr_size_t s_len; - apr_bucket *tmp_buck; - char *parsed_string; - - *inserted_head = NULL; - if (ctx->flags & FLAG_PRINTING) { - while (1) { - ap_ssi_get_tag_and_value(ctx, &tag, &tag_val, 1); - if (tag_val == NULL) { - if (tag == NULL) { - return 0; - } - else { - return 1; - } - } - else { - parsed_string = ap_ssi_parse_string(r, ctx, tag_val, NULL, - MAX_STRING_LEN, 0); - if (!find_file(r, "fsize", tag, parsed_string, &finfo)) { - /* XXX: if we *know* we're going to have to copy the - * thing off of the stack anyway, why not palloc buff - * instead of sticking it on the stack; then we can just - * use a pool bucket and skip the copy - */ - char buff[50]; - - if (!(ctx->flags & FLAG_SIZE_IN_BYTES)) { - apr_strfsize(finfo.size, buff); - s_len = strlen (buff); - } - else { - int l, x, pos = 0; - char tmp_buff[50]; - - apr_snprintf(tmp_buff, sizeof(tmp_buff), - "%" APR_OFF_T_FMT, finfo.size); - l = strlen(tmp_buff); /* grrr */ - for (x = 0; x < l; x++) { - if (x && (!((l - x) % 3))) { - buff[pos++] = ','; - } - buff[pos++] = tmp_buff[x]; - } - buff[pos] = '\0'; - s_len = pos; - } - - tmp_buck = apr_bucket_heap_create(buff, s_len, NULL, - r->connection->bucket_alloc); - APR_BUCKET_INSERT_BEFORE(head_ptr, tmp_buck); - if (*inserted_head == NULL) { - *inserted_head = tmp_buck; - } - } - else { - CREATE_ERROR_BUCKET(ctx, tmp_buck, head_ptr, - *inserted_head); - return 1; - } - } - } - } - return 0; -} - -static int handle_flastmod(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; - apr_finfo_t finfo; - apr_size_t t_len; - apr_bucket *tmp_buck; - char *parsed_string; - - *inserted_head = NULL; - if (ctx->flags & FLAG_PRINTING) { - while (1) { - ap_ssi_get_tag_and_value(ctx, &tag, &tag_val, 1); - if (tag_val == NULL) { - if (tag == NULL) { - return 0; - } - else { - return 1; - } - } - else { - parsed_string = ap_ssi_parse_string(r, ctx, tag_val, NULL, - MAX_STRING_LEN, 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_pool_create(t_val, t_len, r->pool, - r->connection->bucket_alloc); - APR_BUCKET_INSERT_BEFORE(head_ptr, tmp_buck); - if (*inserted_head == NULL) { - *inserted_head = tmp_buck; - } - } - else { - CREATE_ERROR_BUCKET(ctx, tmp_buck, head_ptr, - *inserted_head); - return 1; - } - } - } - } - return 0; -} - -static int re_check(request_rec *r, include_ctx_t *ctx, - char *string, char *rexp) -{ - regex_t *compiled; - const apr_size_t nres = sizeof(*ctx->re_result) / sizeof(regmatch_t); - int regex_error; - - compiled = ap_pregcomp(r->pool, rexp, REG_EXTENDED | REG_NOSUB); - if (compiled == NULL) { - ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, - "unable to compile pattern \"%s\"", rexp); - return -1; - } - if (!ctx->re_result) { - ctx->re_result = apr_pcalloc(r->pool, sizeof(*ctx->re_result)); - } - ctx->re_string = string; - regex_error = ap_regexec(compiled, string, nres, *ctx->re_result, 0); - ap_pregfree(r->pool, compiled); - return (!regex_error); -} - -enum token_type { - 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 -}; -struct token { - enum token_type type; - char* value; -}; - -static const char *get_ptoken(request_rec *r, const char *string, - struct token *token, int *unmatched) -{ - char ch; - int next = 0; - char qs = 0; - int tkn_fnd = 0; - - token->value = NULL; - - /* Skip leading white space */ - if (string == (char *) NULL) { - return (char *) NULL; - } - while ((ch = *string++)) { - if (!apr_isspace(ch)) { - break; - } - } - if (ch == '\0') { - return (char *) NULL; - } - - token->type = token_string; /* the default type */ - switch (ch) { - case '(': - token->type = token_lbrace; - return (string); - case ')': - token->type = token_rbrace; - return (string); - case '=': - token->type = token_eq; - return (string); - case '!': - if (*string == '=') { - token->type = token_ne; - return (string + 1); - } - else { - token->type = token_not; - return (string); - } - case '\'': - /* already token->type == token_string */ - qs = '\''; - break; - case '/': - token->type = token_re; - qs = '/'; - break; - case '|': - if (*string == '|') { - token->type = token_or; - return (string + 1); - } - break; - case '&': - if (*string == '&') { - token->type = token_and; - return (string + 1); - } - break; - case '>': - if (*string == '=') { - token->type = token_ge; - return (string + 1); - } - else { - token->type = token_gt; - return (string); - } - case '<': - if (*string == '=') { - token->type = token_le; - return (string + 1); - } - else { - token->type = token_lt; - return (string); - } - default: - /* already token->type == token_string */ - break; - } - /* We should only be here if we are in a string */ - token->value = apr_palloc(r->pool, strlen(string) + 2); /* 2 for ch plus - trailing null */ - if (!qs) { - --string; - } - - /* - * I used the ++string throughout this section so that string - * ends up pointing to the next token and I can just return it - */ - for (ch = *string; ((ch != '\0') && (!tkn_fnd)); ch = *++string) { - if (ch == '\\') { - if ((ch = *++string) == '\0') { - tkn_fnd = 1; - } - else { - token->value[next++] = ch; - } - } - else { - if (!qs) { - if (apr_isspace(ch)) { - tkn_fnd = 1; - } - else { - switch (ch) { - case '(': - case ')': - case '=': - case '!': - case '<': - case '>': - tkn_fnd = 1; - break; - case '|': - if (*(string + 1) == '|') { - tkn_fnd = 1; - } - break; - case '&': - if (*(string + 1) == '&') { - tkn_fnd = 1; - } - break; - } - if (!tkn_fnd) { - token->value[next++] = ch; - } - } - } - else { - if (ch == qs) { - qs = 0; - tkn_fnd = 1; - string++; - } - else { - token->value[next++] = ch; - } - } - } - if (tkn_fnd) { - break; - } - } - - /* If qs is still set, we have an unmatched quote */ - if (qs) { - *unmatched = 1; - next = 0; - } - token->value[next] = '\0'; - - return (string); -} - - -/* there is an implicit assumption here that expr is at most MAX_STRING_LEN-1 - * characters long... - */ -static int parse_expr(request_rec *r, include_ctx_t *ctx, const char *expr, - int *was_error, int *was_unmatched, char *debug) -{ - struct parse_node { - struct parse_node *left, *right, *parent; - struct token token; - int value, done; - } *root, *current, *new; - const char *parse; - char* buffer; - int retval = 0; - apr_size_t debug_pos = 0; - - debug[debug_pos] = '\0'; - *was_error = 0; - *was_unmatched = 0; - if ((parse = expr) == (char *) NULL) { - return (0); - } - root = current = (struct parse_node *) NULL; - - /* Create Parse Tree */ - while (1) { - new = (struct parse_node *) apr_palloc(r->pool, - sizeof(struct parse_node)); - new->parent = new->left = new->right = (struct parse_node *) NULL; - new->done = 0; - if ((parse = get_ptoken(r, parse, &new->token, was_unmatched)) == - (char *) NULL) { - break; - } - switch (new->token.type) { - - case token_string: -#ifdef DEBUG_INCLUDE - debug_pos += sprintf (&debug[debug_pos], - " Token: string (%s)\n", - new->token.value); -#endif - if (current == (struct parse_node *) NULL) { - root = current = new; - break; - } - switch (current->token.type) { - case token_string: - current->token.value = apr_pstrcat(r->pool, - current->token.value, - current->token.value[0] ? " " : "", - new->token.value, - NULL); - - break; - case token_eq: - case token_ne: - case token_and: - case token_or: - case token_lbrace: - case token_not: - case token_ge: - case token_gt: - case token_le: - case token_lt: - new->parent = current; - current = current->right = new; - break; - default: - ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, - "Invalid expression \"%s\" in file %s", - expr, r->filename); - *was_error = 1; - return retval; - } - break; - - case token_re: -#ifdef DEBUG_INCLUDE - debug_pos += sprintf (&debug[debug_pos], - " Token: regex (%s)\n", - new->token.value); -#endif - if (current == (struct parse_node *) NULL) { - root = current = new; - break; - } - switch (current->token.type) { - case token_eq: - case token_ne: - case token_and: - case token_or: - case token_lbrace: - case token_not: - new->parent = current; - current = current->right = new; - break; - default: - ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, - "Invalid expression \"%s\" in file %s", - expr, r->filename); - *was_error = 1; - return retval; - } - break; - - case token_and: - case token_or: -#ifdef DEBUG_INCLUDE - memcpy (&debug[debug_pos], " Token: and/or\n", - sizeof (" Token: and/or\n")); - debug_pos += sizeof (" Token: and/or\n"); -#endif - if (current == (struct parse_node *) NULL) { - ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, - "Invalid expression \"%s\" in file %s", - expr, r->filename); - *was_error = 1; - return retval; - } - /* Percolate upwards */ - while (current != (struct parse_node *) NULL) { - switch (current->token.type) { - case token_string: - case token_re: - case token_group: - case token_not: - case token_eq: - case token_ne: - case token_and: - case token_or: - case token_ge: - case token_gt: - case token_le: - case token_lt: - current = current->parent; - continue; - case token_lbrace: - break; - default: - ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, - "Invalid expression \"%s\" in file %s", - expr, r->filename); - *was_error = 1; - return retval; - } - break; - } - if (current == (struct parse_node *) NULL) { - new->left = root; - new->left->parent = new; - new->parent = (struct parse_node *) NULL; - root = new; - } - else { - new->left = current->right; - new->left->parent = new; - current->right = new; - new->parent = current; - } - current = new; - break; - - case token_not: -#ifdef DEBUG_INCLUDE - memcpy(&debug[debug_pos], " Token: not\n", - sizeof(" Token: not\n")); - debug_pos += sizeof(" Token: not\n"); -#endif - if (current == (struct parse_node *) NULL) { - root = current = new; - break; - } - /* Percolate upwards */ - if (current != (struct parse_node *) NULL) { - switch (current->token.type) { - case token_not: - case token_eq: - case token_ne: - case token_and: - case token_or: - case token_lbrace: - case token_ge: - case token_gt: - case token_le: - case token_lt: - break; - default: - ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, - "Invalid expression \"%s\" in file %s", - expr, r->filename); - *was_error = 1; - return retval; - } - } - if (current == (struct parse_node *) NULL) { - new->left = root; - new->left->parent = new; - new->parent = (struct parse_node *) NULL; - root = new; - } - else { - new->left = current->right; - current->right = new; - new->parent = current; - } - current = new; - break; - - case token_eq: - case token_ne: - case token_ge: - case token_gt: - case token_le: - case token_lt: -#ifdef DEBUG_INCLUDE - memcpy(&debug[debug_pos], " Token: eq/ne/ge/gt/le/lt\n", - sizeof(" Token: eq/ne/ge/gt/le/lt\n")); - debug_pos += sizeof(" Token: eq/ne/ge/gt/le/lt\n"); -#endif - if (current == (struct parse_node *) NULL) { - ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, - "Invalid expression \"%s\" in file %s", - expr, r->filename); - *was_error = 1; - return retval; - } - /* Percolate upwards */ - while (current != (struct parse_node *) NULL) { - switch (current->token.type) { - case token_string: - case token_re: - case token_group: - current = current->parent; - continue; - case token_lbrace: - case token_and: - case token_or: - break; - case token_not: - case token_eq: - case token_ne: - case token_ge: - case token_gt: - case token_le: - case token_lt: - default: - ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, - "Invalid expression \"%s\" in file %s", - expr, r->filename); - *was_error = 1; - return retval; - } - break; - } - if (current == (struct parse_node *) NULL) { - new->left = root; - new->left->parent = new; - new->parent = (struct parse_node *) NULL; - root = new; - } - else { - new->left = current->right; - new->left->parent = new; - current->right = new; - new->parent = current; - } - current = new; - break; - - case token_rbrace: -#ifdef DEBUG_INCLUDE - memcpy (&debug[debug_pos], " Token: rbrace\n", - sizeof (" Token: rbrace\n")); - debug_pos += sizeof (" Token: rbrace\n"); -#endif - while (current != (struct parse_node *) NULL) { - if (current->token.type == token_lbrace) { - current->token.type = token_group; - break; - } - current = current->parent; - } - if (current == (struct parse_node *) NULL) { - ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, - "Unmatched ')' in \"%s\" in file %s", - expr, r->filename); - *was_error = 1; - return retval; - } - break; - - case token_lbrace: -#ifdef DEBUG_INCLUDE - memcpy (&debug[debug_pos], " Token: lbrace\n", - sizeof (" Token: lbrace\n")); - debug_pos += sizeof (" Token: lbrace\n"); -#endif - if (current == (struct parse_node *) NULL) { - root = current = new; - break; - } - /* Percolate upwards */ - if (current != (struct parse_node *) NULL) { - switch (current->token.type) { - case token_not: - case token_eq: - case token_ne: - case token_and: - case token_or: - case token_lbrace: - case token_ge: - case token_gt: - case token_le: - case token_lt: - break; - case token_string: - case token_re: - case token_group: - default: - ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, - "Invalid expression \"%s\" in file %s", - expr, r->filename); - *was_error = 1; - return retval; - } - } - if (current == (struct parse_node *) NULL) { - new->left = root; - new->left->parent = new; - new->parent = (struct parse_node *) NULL; - root = new; - } - else { - new->left = current->right; - current->right = new; - new->parent = current; - } - current = new; - break; - default: - break; - } - } - - /* Evaluate Parse Tree */ - current = root; - while (current != (struct parse_node *) NULL) { - switch (current->token.type) { - case token_string: -#ifdef DEBUG_INCLUDE - memcpy (&debug[debug_pos], " Evaluate string\n", - sizeof (" Evaluate string\n")); - debug_pos += sizeof (" Evaluate string\n"); -#endif - buffer = ap_ssi_parse_string(r, ctx, current->token.value, NULL, - MAX_STRING_LEN, 0); - current->token.value = buffer; - current->value = (current->token.value[0] != '\0'); - current->done = 1; - current = current->parent; - break; - - case token_re: - ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, - "No operator before regex of expr \"%s\" in file %s", - expr, r->filename); - *was_error = 1; - return retval; - - case token_and: - case token_or: -#ifdef DEBUG_INCLUDE - memcpy(&debug[debug_pos], " Evaluate and/or\n", - sizeof(" Evaluate and/or\n")); - debug_pos += sizeof(" Evaluate and/or\n"); -#endif - if (current->left == (struct parse_node *) NULL || - current->right == (struct parse_node *) NULL) { - ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, - "Invalid expression \"%s\" in file %s", - expr, r->filename); - *was_error = 1; - return retval; - } - if (!current->left->done) { - switch (current->left->token.type) { - case token_string: - buffer = ap_ssi_parse_string(r, ctx, current->left->token.value, - NULL, MAX_STRING_LEN, 0); - current->left->token.value = buffer; - current->left->value = - (current->left->token.value[0] != '\0'); - current->left->done = 1; - break; - default: - current = current->left; - continue; - } - } - if (!current->right->done) { - switch (current->right->token.type) { - case token_string: - buffer = ap_ssi_parse_string(r, ctx, current->right->token.value, - NULL, MAX_STRING_LEN, 0); - current->right->token.value = buffer; - current->right->value = - (current->right->token.value[0] != '\0'); - current->right->done = 1; - break; - default: - current = current->right; - continue; - } - } -#ifdef DEBUG_INCLUDE - debug_pos += sprintf (&debug[debug_pos], " Left: %c\n", - current->left->value ? '1' : '0'); - debug_pos += sprintf (&debug[debug_pos], " Right: %c\n", - current->right->value ? '1' : '0'); -#endif - if (current->token.type == token_and) { - current->value = current->left->value && current->right->value; - } - else { - current->value = current->left->value || current->right->value; - } -#ifdef DEBUG_INCLUDE - debug_pos += sprintf (&debug[debug_pos], " Returning %c\n", - current->value ? '1' : '0'); -#endif - current->done = 1; - current = current->parent; - break; - - case token_eq: - case token_ne: -#ifdef DEBUG_INCLUDE - memcpy (&debug[debug_pos], " Evaluate eq/ne\n", - sizeof (" Evaluate eq/ne\n")); - debug_pos += sizeof (" Evaluate eq/ne\n"); -#endif - if ((current->left == (struct parse_node *) NULL) || - (current->right == (struct parse_node *) NULL) || - (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 retval; - } - buffer = ap_ssi_parse_string(r, ctx, current->left->token.value, - NULL, MAX_STRING_LEN, 0); - current->left->token.value = buffer; - buffer = ap_ssi_parse_string(r, ctx, current->right->token.value, - NULL, MAX_STRING_LEN, 0); - current->right->token.value = buffer; - if (current->right->token.type == token_re) { -#ifdef DEBUG_INCLUDE - debug_pos += sprintf (&debug[debug_pos], - " Re Compare (%s) with /%s/\n", - current->left->token.value, - current->right->token.value); -#endif - current->value = - re_check(r, ctx, current->left->token.value, - current->right->token.value); - } - else { -#ifdef DEBUG_INCLUDE - debug_pos += sprintf (&debug[debug_pos], - " Compare (%s) with (%s)\n", - current->left->token.value, - current->right->token.value); -#endif - current->value = - (strcmp(current->left->token.value, - current->right->token.value) == 0); - } - if (current->token.type == token_ne) { - current->value = !current->value; - } -#ifdef DEBUG_INCLUDE - debug_pos += sprintf (&debug[debug_pos], " Returning %c\n", - current->value ? '1' : '0'); -#endif - current->done = 1; - current = current->parent; - break; - case token_ge: - case token_gt: - case token_le: - case token_lt: -#ifdef DEBUG_INCLUDE - memcpy (&debug[debug_pos], " Evaluate ge/gt/le/lt\n", - sizeof (" Evaluate ge/gt/le/lt\n")); - debug_pos += sizeof (" Evaluate ge/gt/le/lt\n"); -#endif - if ((current->left == (struct parse_node *) NULL) || - (current->right == (struct parse_node *) NULL) || - (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 retval; - } - buffer = ap_ssi_parse_string(r, ctx, current->left->token.value, - NULL, MAX_STRING_LEN, 0); - current->left->token.value = buffer; - buffer = ap_ssi_parse_string(r, ctx, current->right->token.value, - NULL, MAX_STRING_LEN, 0); - current->right->token.value = buffer; -#ifdef DEBUG_INCLUDE - debug_pos += sprintf (&debug[debug_pos], - " Compare (%s) with (%s)\n", - current->left->token.value, - current->right->token.value); -#endif - current->value = - strcmp(current->left->token.value, - current->right->token.value); - if (current->token.type == token_ge) { - current->value = current->value >= 0; - } - else if (current->token.type == token_gt) { - current->value = current->value > 0; - } - else if (current->token.type == token_le) { - current->value = current->value <= 0; - } - else if (current->token.type == token_lt) { - current->value = current->value < 0; - } - else { - current->value = 0; /* Don't return -1 if unknown token */ - } -#ifdef DEBUG_INCLUDE - debug_pos += sprintf (&debug[debug_pos], " Returning %c\n", - current->value ? '1' : '0'); -#endif - current->done = 1; - current = current->parent; - break; - - case token_not: - if (current->right != (struct parse_node *) NULL) { - if (!current->right->done) { - current = current->right; - continue; - } - current->value = !current->right->value; - } - else { - current->value = 0; - } -#ifdef DEBUG_INCLUDE - debug_pos += sprintf (&debug[debug_pos], " Evaluate !: %c\n", - current->value ? '1' : '0'); -#endif - current->done = 1; - current = current->parent; - break; - - case token_group: - if (current->right != (struct parse_node *) NULL) { - if (!current->right->done) { - current = current->right; - continue; - } - current->value = current->right->value; - } - else { - current->value = 1; - } -#ifdef DEBUG_INCLUDE - debug_pos += sprintf (&debug[debug_pos], " Evaluate (): %c\n", - current->value ? '1' : '0'); -#endif - current->done = 1; - current = current->parent; - break; - - case token_lbrace: - ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, - "Unmatched '(' in \"%s\" in file %s", - expr, r->filename); - *was_error = 1; - return retval; - - case token_rbrace: - ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, - "Unmatched ')' in \"%s\" in file %s", - expr, r->filename); - *was_error = 1; - return retval; - - default: - ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, - "bad token type"); - *was_error = 1; - return retval; - } - } - - retval = (root == (struct parse_node *) NULL) ? 0 : root->value; - return (retval); -} - -/*-------------------------------------------------------------------------*/ -#ifdef DEBUG_INCLUDE - -#define MAX_DEBUG_SIZE MAX_STRING_LEN -#define LOG_COND_STATUS(cntx, t_buck, h_ptr, ins_head, tag_text) \ -{ \ - char cond_txt[] = "**** X conditional_status=\"0\"\n"; \ - \ - if (cntx->flags & FLAG_COND_TRUE) { \ - cond_txt[31] = '1'; \ - } \ - memcpy(&cond_txt[5], tag_text, sizeof(tag_text)-1); \ - t_buck = apr_bucket_heap_create(cond_txt, sizeof(cond_txt)-1, \ - NULL, h_ptr->list); \ - APR_BUCKET_INSERT_BEFORE(h_ptr, t_buck); \ - \ - if (ins_head == NULL) { \ - ins_head = t_buck; \ - } \ -} -#define DUMP_PARSE_EXPR_DEBUG(t_buck, h_ptr, d_buf, ins_head) \ -{ \ - if (d_buf[0] != '\0') { \ - t_buck = apr_bucket_heap_create(d_buf, strlen(d_buf), \ - NULL, h_ptr->list); \ - APR_BUCKET_INSERT_BEFORE(h_ptr, t_buck); \ - \ - if (ins_head == NULL) { \ - ins_head = t_buck; \ - } \ - } \ -} -#else - -#define MAX_DEBUG_SIZE 10 -#define LOG_COND_STATUS(cntx, t_buck, h_ptr, ins_head, tag_text) -#define DUMP_PARSE_EXPR_DEBUG(t_buck, h_ptr, d_buf, ins_head) - -#endif -/*-------------------------------------------------------------------------*/ - -/* pjr - These seem to allow expr="fred" expr="joe" where joe overwrites fred. */ -static int handle_if(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 *expr = NULL; - int expr_ret, was_error, was_unmatched; - apr_bucket *tmp_buck; - char debug_buf[MAX_DEBUG_SIZE]; - - *inserted_head = NULL; - if (!(ctx->flags & FLAG_PRINTING)) { - ctx->if_nesting_level++; - } - else { - while (1) { - ap_ssi_get_tag_and_value(ctx, &tag, &tag_val, 0); - if (tag == NULL) { - if (expr == NULL) { - ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, - "missing expr in if statement: %s", - r->filename); - CREATE_ERROR_BUCKET(ctx, tmp_buck, head_ptr, - *inserted_head); - return 1; - } - expr_ret = parse_expr(r, ctx, expr, &was_error, - &was_unmatched, debug_buf); - if (was_error) { - CREATE_ERROR_BUCKET(ctx, tmp_buck, head_ptr, - *inserted_head); - return 1; - } - if (was_unmatched) { - DUMP_PARSE_EXPR_DEBUG(tmp_buck, head_ptr, - "\nUnmatched '\n", *inserted_head); - } - DUMP_PARSE_EXPR_DEBUG(tmp_buck, head_ptr, debug_buf, - *inserted_head); - - if (expr_ret) { - ctx->flags |= (FLAG_PRINTING | FLAG_COND_TRUE); - } - else { - ctx->flags &= FLAG_CLEAR_PRINT_COND; - } - LOG_COND_STATUS(ctx, tmp_buck, head_ptr, *inserted_head, - " if"); - ctx->if_nesting_level = 0; - return 0; - } - else if (!strcmp(tag, "expr")) { - expr = tag_val; -#ifdef DEBUG_INCLUDE - if (1) { - apr_size_t d_len = 0; - d_len = sprintf(debug_buf, "**** if expr=\"%s\"\n", expr); - tmp_buck = apr_bucket_heap_create(debug_buf, d_len, NULL, - r->connection->bucket_alloc); - APR_BUCKET_INSERT_BEFORE(head_ptr, tmp_buck); - - if (*inserted_head == NULL) { - *inserted_head = tmp_buck; - } - } -#endif - } - else { - ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, - "unknown parameter \"%s\" to tag if in %s", tag, - r->filename); - CREATE_ERROR_BUCKET(ctx, tmp_buck, head_ptr, *inserted_head); - return 1; - } - - } - } - return 0; -} - -static int handle_elif(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 *expr = NULL; - int expr_ret, was_error, was_unmatched; - apr_bucket *tmp_buck; - char debug_buf[MAX_DEBUG_SIZE]; - - *inserted_head = NULL; - if (!ctx->if_nesting_level) { - while (1) { - 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"); - - if (ctx->flags & FLAG_COND_TRUE) { - ctx->flags &= FLAG_CLEAR_PRINTING; - return (0); - } - if (expr == NULL) { - ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, - "missing expr in elif statement: %s", - r->filename); - CREATE_ERROR_BUCKET(ctx, tmp_buck, head_ptr, - *inserted_head); - return 1; - } - expr_ret = parse_expr(r, ctx, expr, &was_error, - &was_unmatched, debug_buf); - if (was_error) { - CREATE_ERROR_BUCKET(ctx, tmp_buck, head_ptr, - *inserted_head); - return 1; - } - if (was_unmatched) { - DUMP_PARSE_EXPR_DEBUG(tmp_buck, head_ptr, - "\nUnmatched '\n", *inserted_head); - } - DUMP_PARSE_EXPR_DEBUG(tmp_buck, head_ptr, debug_buf, - *inserted_head); - - if (expr_ret) { - ctx->flags |= (FLAG_PRINTING | FLAG_COND_TRUE); - } - else { - ctx->flags &= FLAG_CLEAR_PRINT_COND; - } - LOG_COND_STATUS(ctx, tmp_buck, head_ptr, *inserted_head, - " elif"); - return (0); - } - else if (!strcmp(tag, "expr")) { - expr = tag_val; -#ifdef DEBUG_INCLUDE - if (1) { - apr_size_t d_len = 0; - d_len = sprintf(debug_buf, "**** elif expr=\"%s\"\n", expr); - tmp_buck = apr_bucket_heap_create(debug_buf, d_len, NULL, - r->connection->bucket_alloc); - APR_BUCKET_INSERT_BEFORE(head_ptr, tmp_buck); - - if (*inserted_head == NULL) { - *inserted_head = tmp_buck; - } - } -#endif - } - else { - ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, - "unknown parameter \"%s\" to tag if in %s", tag, - r->filename); - CREATE_ERROR_BUCKET(ctx, tmp_buck, head_ptr, *inserted_head); - return 1; - } - } - } - return 0; -} - -static int handle_else(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; - apr_bucket *tmp_buck; - - *inserted_head = NULL; - if (!ctx->if_nesting_level) { - ap_ssi_get_tag_and_value(ctx, &tag, &tag_val, 1); - if ((tag != NULL) || (tag_val != NULL)) { - ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, - "else directive does not take tags in %s", r->filename); - if (ctx->flags & FLAG_PRINTING) { - CREATE_ERROR_BUCKET(ctx, tmp_buck, head_ptr, *inserted_head); - } - return -1; - } - else { - LOG_COND_STATUS(ctx, tmp_buck, head_ptr, *inserted_head, " else"); - - if (ctx->flags & FLAG_COND_TRUE) { - ctx->flags &= FLAG_CLEAR_PRINTING; - } - else { - ctx->flags |= (FLAG_PRINTING | FLAG_COND_TRUE); - } - return 0; - } - } - return 0; -} - -static int handle_endif(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; - apr_bucket *tmp_buck; - - *inserted_head = NULL; - if (!ctx->if_nesting_level) { - ap_ssi_get_tag_and_value(ctx, &tag, &tag_val, 1); - if ((tag != NULL) || (tag_val != NULL)) { - ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, - "endif directive does not take tags in %s", r->filename); - CREATE_ERROR_BUCKET(ctx, tmp_buck, head_ptr, *inserted_head); - return -1; - } - else { - LOG_COND_STATUS(ctx, tmp_buck, head_ptr, *inserted_head, "endif"); - ctx->flags |= (FLAG_PRINTING | FLAG_COND_TRUE); - return 0; - } - } - else { - ctx->if_nesting_level--; - return 0; - } -} - -static int handle_set(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 *var = NULL; - apr_bucket *tmp_buck; - char *parsed_string; - request_rec *sub = r->main; - apr_pool_t *p = r->pool; - - /* we need to use the 'main' request pool to set notes as that is - * a notes lifetime - */ - while (sub) { - p = sub->pool; - sub = sub->main; - } - - *inserted_head = NULL; - if (ctx->flags & FLAG_PRINTING) { - while (1) { - ap_ssi_get_tag_and_value(ctx, &tag, &tag_val, 1); - if ((tag == NULL) && (tag_val == NULL)) { - return 0; - } - else if (tag_val == NULL) { - return 1; - } - else if (!strcmp(tag, "var")) { - var = ap_ssi_parse_string(r, ctx, tag_val, NULL, - MAX_STRING_LEN, 0); - } - else if (!strcmp(tag, "value")) { - if (var == (char *) NULL) { - ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, - "variable must precede value in set directive in %s", - r->filename); - CREATE_ERROR_BUCKET(ctx, tmp_buck, head_ptr, - *inserted_head); - return (-1); - } - parsed_string = ap_ssi_parse_string(r, ctx, tag_val, NULL, - MAX_STRING_LEN, 0); - apr_table_setn(r->subprocess_env, apr_pstrdup(p, var), - apr_pstrdup(p, parsed_string)); - } - else { - ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, - "Invalid tag for set directive in %s", r->filename); - CREATE_ERROR_BUCKET(ctx, tmp_buck, head_ptr, *inserted_head); - return -1; - } - } - } - return 0; -} - -static int handle_printenv(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; - apr_bucket *tmp_buck; - - if (ctx->flags & FLAG_PRINTING) { - ap_ssi_get_tag_and_value(ctx, &tag, &tag_val, 1); - if ((tag == NULL) && (tag_val == NULL)) { - const apr_array_header_t *arr = apr_table_elts(r->subprocess_env); - const apr_table_entry_t *elts = (const apr_table_entry_t *)arr->elts; - int i; - const char *key_text, *val_text; - char *key_val, *next; - apr_size_t k_len, v_len, kv_length; - - *inserted_head = NULL; - for (i = 0; i < arr->nelts; ++i) { - key_text = ap_escape_html(r->pool, elts[i].key); - val_text = elts[i].val; - if (val_text == LAZY_VALUE) { - val_text = add_include_vars_lazy(r, elts[i].key); - } - val_text = ap_escape_html(r->pool, elts[i].val); - k_len = strlen(key_text); - v_len = strlen(val_text); - kv_length = k_len + v_len + sizeof("=\n"); - key_val = apr_palloc(r->pool, kv_length); - next = key_val; - memcpy(next, key_text, k_len); - next += k_len; - *next++ = '='; - memcpy(next, val_text, v_len); - next += v_len; - *next++ = '\n'; - *next = 0; - tmp_buck = apr_bucket_pool_create(key_val, kv_length - 1, - r->pool, - r->connection->bucket_alloc); - APR_BUCKET_INSERT_BEFORE(head_ptr, tmp_buck); - if (*inserted_head == NULL) { - *inserted_head = tmp_buck; - } - } - return 0; - } - else { - ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, - "printenv directive does not take tags in %s", - r->filename); - CREATE_ERROR_BUCKET(ctx, tmp_buck, head_ptr, *inserted_head); - return -1; - } - } - return 0; -} - -/* -------------------------- The main function --------------------------- */ - -/* - * returns the index position of the first byte of start_seq (or the len of - * the buffer as non-match) - */ -static apr_size_t find_start_sequence(ssi_ctx_t *ctx, const char *data, - apr_size_t len) -{ - apr_size_t slen = ctx->ctx->start_seq_len; - apr_size_t index; - const char *p, *ep; - - if (len < slen) { - p = data; /* try partial match at the end of the buffer (below) */ - } - else { - /* try fast bndm search over the buffer - * (hopefully the whole start sequence can be found in this buffer) - */ - index = bndm(ctx->ctx->start_seq, ctx->ctx->start_seq_len, data, len, - ctx->ctx->start_seq_pat); - - /* wow, found it. ready. */ - if (index < len) { - ctx->state = PARSE_DIRECTIVE; - return index; - } - else { - /* ok, the pattern can't be found as whole in the buffer, - * check the end for a partial match - */ - p = data + len - slen + 1; - } - } - - ep = data + len; - do { - while (p < ep && *p != *ctx->ctx->start_seq) { - ++p; - } - - index = p - data; - - /* found a possible start_seq start */ - if (p < ep) { - apr_size_t pos = 1; - - ++p; - while (p < ep && *p == ctx->ctx->start_seq[pos]) { - ++p; - ++pos; - } - - /* partial match found. Store the info for the next round */ - if (p == ep) { - ctx->state = PARSE_HEAD; - ctx->ctx->parse_pos = pos; - return index; - } - } - - /* we must try all combinations; consider (e.g.) SSIStartTag "--->" - * and a string data of "--.-" and the end of the buffer - */ - p = data + index + 1; - } while (p < ep); - - /* no match */ - return len; -} - -/* - * returns the first byte *after* the partial (or final) match. - * - * If we had to trick with the start_seq start, 'release' returns the - * number of chars of the start_seq which appeared not to be part of a - * full tag and may have to be passed down the filter chain. - */ -static apr_size_t find_partial_start_sequence(ssi_ctx_t *ctx, - const char *data, - apr_size_t len, - apr_size_t *release) -{ - apr_size_t pos, spos = 0; - apr_size_t slen = ctx->ctx->start_seq_len; - const char *p, *ep; - - pos = ctx->ctx->parse_pos; - ep = data + len; - *release = 0; - - do { - p = data; - - while (p < ep && pos < slen && *p == ctx->ctx->start_seq[pos]) { - ++p; - ++pos; - } - - /* full match */ - if (pos == slen) { - ctx->state = PARSE_DIRECTIVE; - return (p - data); - } - - /* the whole buffer is a partial match */ - if (p == ep) { - ctx->ctx->parse_pos = pos; - return (p - data); - } - - /* No match so far, but again: - * We must try all combinations, since the start_seq is a random - * user supplied string - * - * So: look if the first char of start_seq appears somewhere within - * the current partial match. If it does, try to start a match that - * begins with this offset. (This can happen, if a strange - * start_seq like "---->" spans buffers) - */ - if (spos < ctx->ctx->parse_pos) { - do { - ++spos; - ++*release; - p = ctx->ctx->start_seq + spos; - pos = ctx->ctx->parse_pos - spos; - - while (pos && *p != *ctx->ctx->start_seq) { - ++p; - ++spos; - ++*release; - --pos; - } - - /* if a matching beginning char was found, try to match the - * remainder of the old buffer. - */ - if (pos > 1) { - apr_size_t t = 1; - - ++p; - while (t < pos && *p == ctx->ctx->start_seq[t]) { - ++p; - ++t; - } - - if (t == pos) { - /* yeah, another partial match found in the *old* - * buffer, now test the *current* buffer for - * continuing match - */ - break; - } - } - } while (pos > 1); - - if (pos) { - continue; - } - } - - break; - } while (1); /* work hard to find a match ;-) */ - - /* no match at all, release all (wrongly) matched chars so far */ - *release = ctx->ctx->parse_pos; - ctx->state = PARSE_PRE_HEAD; - return 0; -} - -/* - * returns the position after the directive - */ -static apr_size_t find_directive(ssi_ctx_t *ctx, const char *data, - apr_size_t len, char ***store, - apr_size_t **store_len) -{ - const char *p = data; - const char *ep = data + len; - apr_size_t pos; - - switch (ctx->state) { - case PARSE_DIRECTIVE: - while (p < ep && !apr_isspace(*p)) { - /* we have to consider the case of missing space between directive - * and end_seq (be somewhat lenient), e.g. <!--#printenv--> - */ - if (*p == *ctx->ctx->end_seq) { - ctx->state = PARSE_DIRECTIVE_TAIL; - ctx->ctx->parse_pos = 1; - ++p; - return (p - data); - } - ++p; - } - - if (p < ep) { /* found delimiter whitespace */ - ctx->state = PARSE_DIRECTIVE_POSTNAME; - *store = &ctx->directive; - *store_len = &ctx->ctx->directive_length; - } - - break; - - case PARSE_DIRECTIVE_TAIL: - pos = ctx->ctx->parse_pos; - - while (p < ep && pos < ctx->end_seq_len && - *p == ctx->ctx->end_seq[pos]) { - ++p; - ++pos; - } - - /* full match, we're done */ - if (pos == ctx->end_seq_len) { - ctx->state = PARSE_DIRECTIVE_POSTTAIL; - *store = &ctx->directive; - *store_len = &ctx->ctx->directive_length; - break; - } - - /* partial match, the buffer is too small to match fully */ - if (p == ep) { - ctx->ctx->parse_pos = pos; - break; - } - - /* no match. continue normal parsing */ - ctx->state = PARSE_DIRECTIVE; - return 0; - - case PARSE_DIRECTIVE_POSTTAIL: - ctx->state = PARSE_EXECUTE; - ctx->ctx->directive_length -= ctx->end_seq_len; - /* continue immediately with the next state */ - - case PARSE_DIRECTIVE_POSTNAME: - if (PARSE_DIRECTIVE_POSTNAME == ctx->state) { - ctx->state = PARSE_PRE_ARG; - } - ctx->argc = 0; - ctx->argv = NULL; - - if (!ctx->ctx->directive_length) { - ctx->error = 1; - ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, ctx->r, "missing directive " - "name in parsed document %s", ctx->r->filename); - } - else { - char *sp = ctx->directive; - char *sep = ctx->directive + ctx->ctx->directive_length; - - /* normalize directive name */ - for (; sp < sep; ++sp) { - *sp = apr_tolower(*sp); - } - } - - return 0; - - default: - /* get a rid of a gcc warning about unhandled enumerations */ - break; - } - - return (p - data); -} - -/* - * find out whether the next token is (a possible) end_seq or an argument - */ -static apr_size_t find_arg_or_tail(ssi_ctx_t *ctx, const char *data, - apr_size_t len) -{ - const char *p = data; - const char *ep = data + len; - - /* skip leading WS */ - while (p < ep && apr_isspace(*p)) { - ++p; - } - - /* buffer doesn't consist of whitespaces only */ - if (p < ep) { - ctx->state = (*p == *ctx->ctx->end_seq) ? PARSE_TAIL : PARSE_ARG; - } - - return (p - data); -} - -/* - * test the stream for end_seq. If it doesn't match at all, it must be an - * argument - */ -static apr_size_t find_tail(ssi_ctx_t *ctx, const char *data, - apr_size_t len) -{ - const char *p = data; - const char *ep = data + len; - apr_size_t pos = ctx->ctx->parse_pos; - - if (PARSE_TAIL == ctx->state) { - ctx->state = PARSE_TAIL_SEQ; - pos = ctx->ctx->parse_pos = 0; - } - - while (p < ep && pos < ctx->end_seq_len && *p == ctx->ctx->end_seq[pos]) { - ++p; - ++pos; - } - - /* bingo, full match */ - if (pos == ctx->end_seq_len) { - ctx->state = PARSE_EXECUTE; - return (p - data); - } - - /* partial match, the buffer is too small to match fully */ - if (p == ep) { - ctx->ctx->parse_pos = pos; - return (p - data); - } - - /* no match. It must be an argument string then */ - ctx->state = PARSE_ARG; - return 0; -} - -/* - * extract name=value from the buffer - * A pcre-pattern could look (similar to): - * name\s*(?:=\s*(["'`]?)value\1(?>\s*))? - */ -static apr_size_t find_argument(ssi_ctx_t *ctx, const char *data, - apr_size_t len, char ***store, - apr_size_t **store_len) -{ - const char *p = data; - const char *ep = data + len; - - switch (ctx->state) { - case PARSE_ARG: - /* - * create argument structure and append it to the current list - */ - ctx->current_arg = apr_palloc(ctx->dpool, - sizeof(*ctx->current_arg)); - ctx->current_arg->next = NULL; - - ++(ctx->argc); - if (!ctx->argv) { - ctx->argv = ctx->current_arg; - } - else { - ssi_arg_item_t *newarg = ctx->argv; - - while (newarg->next) { - newarg = newarg->next; - } - newarg->next = ctx->current_arg; - } - - /* check whether it's a valid one. If it begins with a quote, we - * can safely assume, someone forgot the name of the argument - */ - switch (*p) { - case '"': case '\'': case '`': - *store = NULL; - - ctx->state = PARSE_ARG_VAL; - ctx->quote = *p++; - ctx->current_arg->name = NULL; - ctx->current_arg->name_len = 0; - ctx->error = 1; - - ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, ctx->r, "missing argument " - "name for value to tag %s in %s", - apr_pstrmemdup(ctx->r->pool, ctx->directive, - ctx->ctx->directive_length), - ctx->r->filename); - - return (p - data); - - default: - ctx->state = PARSE_ARG_NAME; - } - /* continue immediately with next state */ - - case PARSE_ARG_NAME: - while (p < ep && !apr_isspace(*p) && *p != '=') { - ++p; - } - - if (p < ep) { - ctx->state = PARSE_ARG_POSTNAME; - *store = &ctx->current_arg->name; - *store_len = &ctx->current_arg->name_len; - return (p - data); - } - break; - - case PARSE_ARG_POSTNAME: - ctx->current_arg->name = apr_pstrmemdup(ctx->dpool, - ctx->current_arg->name, - ctx->current_arg->name_len); - if (!ctx->current_arg->name_len) { - ctx->error = 1; - ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, ctx->r, "missing argument " - "name for value to tag %s in %s", - apr_pstrmemdup(ctx->r->pool, ctx->directive, - ctx->ctx->directive_length), - ctx->r->filename); - } - else { - char *sp = ctx->current_arg->name; - - /* normalize the name */ - while (*sp) { - *sp = apr_tolower(*sp); - ++sp; - } - } - - ctx->state = PARSE_ARG_EQ; - /* continue with next state immediately */ - - case PARSE_ARG_EQ: - *store = NULL; - - while (p < ep && apr_isspace(*p)) { - ++p; - } - - if (p < ep) { - if (*p == '=') { - ctx->state = PARSE_ARG_PREVAL; - ++p; - } - else { /* no value */ - ctx->current_arg->value = NULL; - ctx->state = PARSE_PRE_ARG; - } - - return (p - data); - } - break; - - case PARSE_ARG_PREVAL: - *store = NULL; - - while (p < ep && apr_isspace(*p)) { - ++p; - } - - /* buffer doesn't consist of whitespaces only */ - if (p < ep) { - ctx->state = PARSE_ARG_VAL; - switch (*p) { - case '"': case '\'': case '`': - ctx->quote = *p++; - break; - default: - ctx->quote = '\0'; - break; - } - - return (p - data); - } - break; - - case PARSE_ARG_VAL_ESC: - if (*p == ctx->quote) { - ++p; - } - ctx->state = PARSE_ARG_VAL; - /* continue with next state immediately */ - - case PARSE_ARG_VAL: - for (; p < ep; ++p) { - if (ctx->quote && *p == '\\') { - ++p; - if (p == ep) { - ctx->state = PARSE_ARG_VAL_ESC; - break; - } - - if (*p != ctx->quote) { - --p; - } - } - else if (ctx->quote && *p == ctx->quote) { - ++p; - *store = &ctx->current_arg->value; - *store_len = &ctx->current_arg->value_len; - ctx->state = PARSE_ARG_POSTVAL; - break; - } - else if (!ctx->quote && apr_isspace(*p)) { - ++p; - *store = &ctx->current_arg->value; - *store_len = &ctx->current_arg->value_len; - ctx->state = PARSE_ARG_POSTVAL; - break; - } - } - - return (p - data); - - case PARSE_ARG_POSTVAL: - /* - * The value is still the raw input string. Finally clean it up. - */ - --(ctx->current_arg->value_len); - - /* strip quote escaping \ from the string */ - if (ctx->quote) { - apr_size_t shift = 0; - char *sp; - - sp = ctx->current_arg->value; - ep = ctx->current_arg->value + ctx->current_arg->value_len; - while (sp < ep && *sp != '\\') { - ++sp; - } - for (; sp < ep; ++sp) { - if (*sp == '\\' && sp[1] == ctx->quote) { - ++sp; - ++shift; - } - if (shift) { - *(sp-shift) = *sp; - } - } - - ctx->current_arg->value_len -= shift; - } - - ctx->current_arg->value[ctx->current_arg->value_len] = '\0'; - ctx->state = PARSE_PRE_ARG; - - return 0; - - default: - /* get a rid of a gcc warning about unhandled enumerations */ - break; - } - - return len; /* partial match of something */ -} - -/* - * This is the main loop over the current bucket brigade. - */ -static apr_status_t send_parsed_content(ap_filter_t *f, apr_bucket_brigade *bb) -{ - ssi_ctx_t *ctx = f->ctx; - request_rec *r = f->r; - apr_bucket *b = APR_BRIGADE_FIRST(bb); - apr_bucket_brigade *pass_bb; - apr_status_t rv = APR_SUCCESS; - char *magic; /* magic pointer for sentinel use */ - - /* fast exit */ - if (APR_BRIGADE_EMPTY(bb)) { - return APR_SUCCESS; - } - - /* we may crash, since already cleaned up; hand over the responsibility - * to the next filter;-) - */ - if (ctx->seen_eos) { - return ap_pass_brigade(f->next, bb); - } - - /* All stuff passed along has to be put into that brigade */ - pass_bb = apr_brigade_create(ctx->ctx->pool, f->c->bucket_alloc); - ctx->ctx->bytes_parsed = 0; - ctx->ctx->output_now = 0; - ctx->error = 0; - - /* loop over the current bucket brigade */ - while (b != APR_BRIGADE_SENTINEL(bb)) { - const char *data = NULL; - apr_size_t len, index, release; - apr_bucket *newb = NULL; - char **store = &magic; - apr_size_t *store_len; - - /* handle meta buckets before reading any data */ - if (APR_BUCKET_IS_METADATA(b)) { - newb = APR_BUCKET_NEXT(b); - - APR_BUCKET_REMOVE(b); - - if (APR_BUCKET_IS_EOS(b)) { - ctx->seen_eos = 1; - - /* Hit end of stream, time for cleanup ... But wait! - * Perhaps we're not ready yet. We may have to loop one or - * two times again to finish our work. In that case, we - * just re-insert the EOS bucket to allow for an extra loop. - * - * PARSE_EXECUTE means, we've hit a directive just before the - * EOS, which is now waiting for execution. - * - * PARSE_DIRECTIVE_POSTTAIL means, we've hit a directive with - * no argument and no space between directive and end_seq - * just before the EOS. (consider <!--#printenv--> as last - * or only string within the stream). This state, however, - * just cleans up and turns itself to PARSE_EXECUTE, which - * will be passed through within the next (and actually - * last) round. - */ - if (PARSE_EXECUTE == ctx->state || - PARSE_DIRECTIVE_POSTTAIL == ctx->state) { - APR_BUCKET_INSERT_BEFORE(newb, b); - } - else { - break; /* END OF STREAM */ - } - } - else { - APR_BRIGADE_INSERT_TAIL(pass_bb, b); - - if (APR_BUCKET_IS_FLUSH(b)) { - ctx->ctx->output_now = 1; - } - - b = newb; - continue; - } - } - - /* enough is enough ... */ - if (ctx->ctx->output_now || - ctx->ctx->bytes_parsed > AP_MIN_BYTES_TO_WRITE) { - - if (!APR_BRIGADE_EMPTY(pass_bb)) { - rv = ap_pass_brigade(f->next, pass_bb); - if (!APR_STATUS_IS_SUCCESS(rv)) { - apr_brigade_destroy(pass_bb); - return rv; - } - } - - ctx->ctx->output_now = 0; - ctx->ctx->bytes_parsed = 0; - } - - /* read the current bucket data */ - len = 0; - if (!ctx->seen_eos) { - if (ctx->ctx->bytes_parsed > 0) { - rv = apr_bucket_read(b, &data, &len, APR_NONBLOCK_READ); - if (APR_STATUS_IS_EAGAIN(rv)) { - ctx->ctx->output_now = 1; - continue; - } - } - - if (!len || !APR_STATUS_IS_SUCCESS(rv)) { - rv = apr_bucket_read(b, &data, &len, APR_BLOCK_READ); - } - - if (!APR_STATUS_IS_SUCCESS(rv)) { - apr_brigade_destroy(pass_bb); - return rv; - } - - ctx->ctx->bytes_parsed += len; - } - - /* zero length bucket, fetch next one */ - if (!len && !ctx->seen_eos) { - b = APR_BUCKET_NEXT(b); - continue; - } - - /* - * it's actually a data containing bucket, start/continue parsing - */ - - switch (ctx->state) { - /* no current tag; search for start sequence */ - case PARSE_PRE_HEAD: - index = find_start_sequence(ctx, data, len); - - if (index < len) { - apr_bucket_split(b, index); - } - - newb = APR_BUCKET_NEXT(b); - if (ctx->ctx->flags & FLAG_PRINTING) { - APR_BUCKET_REMOVE(b); - APR_BRIGADE_INSERT_TAIL(pass_bb, b); - } - else { - apr_bucket_delete(b); - } - - if (index < len) { - /* now delete the start_seq stuff from the remaining bucket */ - if (PARSE_DIRECTIVE == ctx->state) { /* full match */ - apr_bucket_split(newb, ctx->ctx->start_seq_len); - ctx->ctx->output_now = 1; /* pass pre-tag stuff */ - } - - b = APR_BUCKET_NEXT(newb); - apr_bucket_delete(newb); - } - else { - b = newb; - } - - break; - - /* we're currently looking for the end of the start sequence */ - case PARSE_HEAD: - index = find_partial_start_sequence(ctx, data, len, &release); - - /* check if we mismatched earlier and have to release some chars */ - if (release && (ctx->ctx->flags & FLAG_PRINTING)) { - char *to_release = apr_palloc(ctx->ctx->pool, release); - - memcpy(to_release, ctx->ctx->start_seq, release); - newb = apr_bucket_pool_create(to_release, release, - ctx->ctx->pool, - f->c->bucket_alloc); - APR_BRIGADE_INSERT_TAIL(pass_bb, newb); - } - - if (index) { /* any match */ - /* now delete the start_seq stuff from the remaining bucket */ - if (PARSE_DIRECTIVE == ctx->state) { /* final match */ - apr_bucket_split(b, index); - ctx->ctx->output_now = 1; /* pass pre-tag stuff */ - } - newb = APR_BUCKET_NEXT(b); - apr_bucket_delete(b); - b = newb; - } - - break; - - /* we're currently grabbing the directive name */ - case PARSE_DIRECTIVE: - case PARSE_DIRECTIVE_POSTNAME: - case PARSE_DIRECTIVE_TAIL: - case PARSE_DIRECTIVE_POSTTAIL: - index = find_directive(ctx, data, len, &store, &store_len); - - if (index) { - apr_bucket_split(b, index); - newb = APR_BUCKET_NEXT(b); - } - - if (store) { - if (index) { - APR_BUCKET_REMOVE(b); - APR_BRIGADE_INSERT_TAIL(ctx->tmp_bb, b); - b = newb; - } - - /* time for cleanup? */ - if (store != &magic) { - apr_brigade_pflatten(ctx->tmp_bb, store, store_len, - ctx->dpool); - apr_brigade_cleanup(ctx->tmp_bb); - } - } - else if (index) { - apr_bucket_delete(b); - b = newb; - } - - break; - - /* skip WS and find out what comes next (arg or end_seq) */ - case PARSE_PRE_ARG: - index = find_arg_or_tail(ctx, data, len); - - if (index) { /* skipped whitespaces */ - if (index < len) { - apr_bucket_split(b, index); - } - newb = APR_BUCKET_NEXT(b); - apr_bucket_delete(b); - b = newb; - } - - break; - - /* currently parsing name[=val] */ - case PARSE_ARG: - case PARSE_ARG_NAME: - case PARSE_ARG_POSTNAME: - case PARSE_ARG_EQ: - case PARSE_ARG_PREVAL: - case PARSE_ARG_VAL: - case PARSE_ARG_VAL_ESC: - case PARSE_ARG_POSTVAL: - index = find_argument(ctx, data, len, &store, &store_len); - - if (index) { - apr_bucket_split(b, index); - newb = APR_BUCKET_NEXT(b); - } - - if (store) { - if (index) { - APR_BUCKET_REMOVE(b); - APR_BRIGADE_INSERT_TAIL(ctx->tmp_bb, b); - b = newb; - } - - /* time for cleanup? */ - if (store != &magic) { - apr_brigade_pflatten(ctx->tmp_bb, store, store_len, - ctx->dpool); - apr_brigade_cleanup(ctx->tmp_bb); - } - } - else if (index) { - apr_bucket_delete(b); - b = newb; - } - - break; - - /* try to match end_seq at current pos. */ - case PARSE_TAIL: - case PARSE_TAIL_SEQ: - index = find_tail(ctx, data, len); - - switch (ctx->state) { - case PARSE_EXECUTE: /* full match */ - apr_bucket_split(b, index); - newb = APR_BUCKET_NEXT(b); - apr_bucket_delete(b); - b = newb; - break; - - case PARSE_ARG: /* no match */ - /* PARSE_ARG must reparse at the beginning */ - APR_BRIGADE_PREPEND(bb, ctx->tmp_bb); - b = APR_BRIGADE_FIRST(bb); - break; - - default: /* partial match */ - newb = APR_BUCKET_NEXT(b); - APR_BUCKET_REMOVE(b); - APR_BRIGADE_INSERT_TAIL(ctx->tmp_bb, b); - b = newb; - break; - } - - break; - - /* now execute the parsed directive, cleanup the space and - * start again with PARSE_PRE_HEAD - */ - case PARSE_EXECUTE: - /* if there was an error, it was already logged; just stop here */ - if (ctx->error) { - if (ctx->ctx->flags & FLAG_PRINTING) { - SSI_CREATE_ERROR_BUCKET(ctx->ctx, f, pass_bb); - ctx->error = 0; - } - } - else { - include_handler_fn_t *handle_func; - - handle_func = - (include_handler_fn_t *) apr_hash_get(include_hash, - ctx->directive, - ctx->ctx->directive_length); - if (handle_func) { - apr_bucket *dummy; - char *tag; - apr_size_t tag_len = 0; - ssi_arg_item_t *carg = ctx->argv; - - /* legacy wrapper code */ - while (carg) { - /* +1 \0 byte (either after tag or value) - * +1 = byte (before value) - */ - tag_len += (carg->name ? carg->name_len : 0) + - (carg->value ? carg->value_len + 1 : 0) + 1; - carg = carg->next; - } - - tag = ctx->ctx->combined_tag = ctx->ctx->curr_tag_pos = - apr_palloc(ctx->dpool, tag_len); - - carg = ctx->argv; - while (carg) { - if (carg->name) { - memcpy(tag, carg->name, carg->name_len); - tag += carg->name_len; - } - if (carg->value) { - *tag++ = '='; - memcpy(tag, carg->value, carg->value_len); - tag += carg->value_len; - } - *tag++ = '\0'; - carg = carg->next; - } - ctx->ctx->tag_length = tag_len; - - /* create dummy buckets for backards compat */ - ctx->ctx->head_start_bucket = - apr_bucket_pool_create(apr_pmemdup(ctx->ctx->pool, - ctx->ctx->start_seq, - ctx->ctx->start_seq_len), - ctx->ctx->start_seq_len, - ctx->ctx->pool, - f->c->bucket_alloc); - APR_BRIGADE_INSERT_TAIL(ctx->ctx->ssi_tag_brigade, - ctx->ctx->head_start_bucket); - ctx->ctx->tag_start_bucket = - apr_bucket_pool_create(apr_pmemdup(ctx->ctx->pool, - ctx->ctx->combined_tag, - ctx->ctx->tag_length), - ctx->ctx->tag_length, - ctx->ctx->pool, - f->c->bucket_alloc); - APR_BRIGADE_INSERT_TAIL(ctx->ctx->ssi_tag_brigade, - ctx->ctx->tag_start_bucket); - ctx->ctx->tail_start_bucket = - apr_bucket_pool_create(apr_pmemdup(ctx->ctx->pool, - ctx->ctx->end_seq, - ctx->end_seq_len), - ctx->end_seq_len, - ctx->ctx->pool, - f->c->bucket_alloc); - APR_BRIGADE_INSERT_TAIL(ctx->ctx->ssi_tag_brigade, - ctx->ctx->tail_start_bucket); - - rv = handle_func(ctx->ctx, &bb, r, f, b, &dummy); - - apr_brigade_cleanup(ctx->ctx->ssi_tag_brigade); - - if (rv != 0 && rv != 1 && rv != -1) { - apr_brigade_destroy(pass_bb); - return rv; - } - - if (dummy) { - apr_bucket_brigade *remain; - - remain = apr_brigade_split(bb, b); - APR_BRIGADE_CONCAT(pass_bb, bb); - bb = remain; - } - } - else { - ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, - "unknown directive \"%s\" in parsed doc %s", - apr_pstrmemdup(r->pool, ctx->directive, - ctx->ctx->directive_length), - r->filename); - if (ctx->ctx->flags & FLAG_PRINTING) { - SSI_CREATE_ERROR_BUCKET(ctx->ctx, f, pass_bb); - } - } - } - - /* cleanup */ - apr_pool_clear(ctx->dpool); - apr_brigade_cleanup(ctx->tmp_bb); - - /* Oooof. Done here, start next round */ - ctx->state = PARSE_PRE_HEAD; - break; - } - - } /* while (brigade) */ - - /* End of stream. Final cleanup */ - if (ctx->seen_eos) { - if (PARSE_HEAD == ctx->state) { - if (ctx->ctx->flags & FLAG_PRINTING) { - char *to_release = apr_palloc(ctx->ctx->pool, - ctx->ctx->parse_pos); - - memcpy(to_release, ctx->ctx->start_seq, ctx->ctx->parse_pos); - APR_BRIGADE_INSERT_TAIL(pass_bb, - apr_bucket_pool_create(to_release, - ctx->ctx->parse_pos, ctx->ctx->pool, - f->c->bucket_alloc)); - } - } - else if (PARSE_PRE_HEAD != ctx->state) { - ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, - "SSI directive was not properly finished at the end " - "of parsed document %s", r->filename); - if (ctx->ctx->flags & FLAG_PRINTING) { - SSI_CREATE_ERROR_BUCKET(ctx->ctx, f, pass_bb); - } - } - - if (!(ctx->ctx->flags & FLAG_PRINTING)) { - ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r, - "missing closing endif directive in parsed document" - " %s", r->filename); - } - - /* cleanup our temporary memory */ - apr_brigade_destroy(ctx->tmp_bb); - apr_pool_destroy(ctx->dpool); - - /* don't forget to finally insert the EOS bucket */ - APR_BRIGADE_INSERT_TAIL(pass_bb, b); - } - - /* if something's left over, pass it along */ - if (!APR_BRIGADE_EMPTY(pass_bb)) { - rv = ap_pass_brigade(f->next, pass_bb); - } - else { - rv = APR_SUCCESS; - } - - apr_brigade_destroy(pass_bb); - return rv; -} - -static void *create_includes_dir_config(apr_pool_t *p, char *dummy) -{ - 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 void *create_includes_server_config(apr_pool_t*p, server_rec *server) -{ - include_server_config *result = - (include_server_config *)apr_palloc(p, sizeof(include_server_config)); - result->default_end_tag = ENDING_SEQUENCE; - result->default_start_tag =STARTING_SEQUENCE; - result->start_tag_len = sizeof(STARTING_SEQUENCE)-1; - /* compile the pattern used by find_start_sequence */ - bndm_compile(&result->start_seq_pat, result->default_start_tag, - result->start_tag_len); - - result->undefinedEcho = apr_pstrdup(p,"(none)"); - result->undefinedEchoLen = strlen( result->undefinedEcho); - return result; -} -static const char *set_xbithack(cmd_parms *cmd, void *xbp, const char *arg) -{ - include_dir_config *conf = (include_dir_config *)xbp; - - if (!strcasecmp(arg, "off")) { - *conf->xbithack = xbithack_off; - } - else if (!strcasecmp(arg, "on")) { - *conf->xbithack = xbithack_on; - } - else if (!strcasecmp(arg, "full")) { - *conf->xbithack = xbithack_full; - } - else { - return "XBitHack must be set to Off, On, or Full"; - } - - return NULL; -} - -static int includes_setup(ap_filter_t *f) -{ - include_dir_config *conf = - (include_dir_config *)ap_get_module_config(f->r->per_dir_config, - &include_module); - - /* When our xbithack value isn't set to full or our platform isn't - * providing group-level protection bits or our group-level bits do not - * have group-execite on, we will set the no_local_copy value to 1 so - * that we will not send 304s. - */ - if ((*conf->xbithack != xbithack_full) - || !(f->r->finfo.valid & APR_FINFO_GPROT) - || !(f->r->finfo.protection & APR_GEXECUTE)) { - f->r->no_local_copy = 1; - } - - /* Don't allow ETag headers to be generated - see RFC2616 - 13.3.4. - * We don't know if we are going to be including a file or executing - * a program - in either case a strong ETag header will likely be invalid. - */ - apr_table_setn(f->r->notes, "no-etag", ""); - - return OK; -} - -static apr_status_t includes_filter(ap_filter_t *f, apr_bucket_brigade *b) -{ - request_rec *r = f->r; - ssi_ctx_t *ctx = f->ctx; - request_rec *parent; - include_dir_config *conf = - (include_dir_config *)ap_get_module_config(r->per_dir_config, - &include_module); - - include_server_config *sconf= ap_get_module_config(r->server->module_config, - &include_module); - - if (!(ap_allow_options(r) & OPT_INCLUDES)) { - ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r, - "mod_include: Options +Includes (or IncludesNoExec) " - "wasn't set, INCLUDES filter removed"); - ap_remove_output_filter(f); - return ap_pass_brigade(f->next, b); - } - - if (!f->ctx) { - /* create context for this filter */ - f->ctx = ctx = apr_palloc(f->c->pool, sizeof(*ctx)); - ctx->ctx = apr_pcalloc(f->c->pool, sizeof(*ctx->ctx)); - ctx->ctx->pool = f->r->pool; - apr_pool_create(&ctx->dpool, ctx->ctx->pool); - - /* configuration data */ - ctx->end_seq_len = strlen(sconf->default_end_tag); - ctx->r = f->r; - - /* runtime data */ - ctx->tmp_bb = apr_brigade_create(ctx->ctx->pool, f->c->bucket_alloc); - ctx->seen_eos = 0; - ctx->state = PARSE_PRE_HEAD; - ctx->ctx->flags = (FLAG_PRINTING | FLAG_COND_TRUE); - if (ap_allow_options(f->r) & OPT_INCNOEXEC) { - ctx->ctx->flags |= FLAG_NO_EXEC; - } - ctx->ctx->if_nesting_level = 0; - ctx->ctx->re_string = NULL; - ctx->ctx->error_str_override = NULL; - ctx->ctx->time_str_override = NULL; - - ctx->ctx->error_str = conf->default_error_msg; - ctx->ctx->time_str = conf->default_time_fmt; - ctx->ctx->start_seq_pat = &sconf->start_seq_pat; - ctx->ctx->start_seq = sconf->default_start_tag; - ctx->ctx->start_seq_len = sconf->start_tag_len; - ctx->ctx->end_seq = sconf->default_end_tag; - - /* legacy compat stuff */ - ctx->ctx->state = PARSED; /* dummy */ - ctx->ctx->ssi_tag_brigade = apr_brigade_create(f->c->pool, - f->c->bucket_alloc); - ctx->ctx->status = APR_SUCCESS; - ctx->ctx->head_start_index = 0; - ctx->ctx->tag_start_index = 0; - ctx->ctx->tail_start_index = 0; - } - else { - ctx->ctx->bytes_parsed = 0; - } - - 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 - * LAST_MODIFIED variable gets reset to the proper value if the - * nested document resets <!--#config timefmt -->. - */ - r->subprocess_env = r->main->subprocess_env; - apr_pool_join(r->main->pool, r->pool); - r->finfo.mtime = r->main->finfo.mtime; - } - else { - /* we're not a nested include, so we create an initial - * environment */ - ap_add_common_vars(r); - ap_add_cgi_vars(r); - add_include_vars(r, conf->default_time_fmt); - } - /* 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"); - - /* Always unset the Last-Modified field - see RFC2616 - 13.3.4. - * We don't know if we are going to be including a file or executing - * a program which may change the Last-Modified header or make the - * content completely dynamic. Therefore, we can't support these - * headers. - * Exception: XBitHack full means we *should* set the Last-Modified field. - */ - - /* 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); - } - else { - apr_table_unset(f->r->headers_out, "Last-Modified"); - } - - /* add QUERY stuff to env cause it ain't yet */ - if (r->args) { - char *arg_copy = apr_pstrdup(r->pool, r->args); - - apr_table_setn(r->subprocess_env, "QUERY_STRING", r->args); - ap_unescape_url(arg_copy); - apr_table_setn(r->subprocess_env, "QUERY_STRING_UNESCAPED", - ap_escape_shell_cmd(r->pool, arg_copy)); - } - - return send_parsed_content(f, b); -} - -static void ap_register_include_handler(char *tag, include_handler_fn_t *func) -{ - apr_hash_set(include_hash, tag, strlen(tag), (const void *)func); -} - -static int include_post_config(apr_pool_t *p, apr_pool_t *plog, - apr_pool_t *ptemp, server_rec *s) -{ - 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); - } - return OK; -} - -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_start_tag(cmd_parms *cmd, void *mconfig, const char *msg) -{ - include_server_config *conf; - conf= ap_get_module_config(cmd->server->module_config , &include_module); - conf->default_start_tag = apr_pstrdup(cmd->pool, msg); - conf->start_tag_len = strlen(conf->default_start_tag ); - bndm_compile(&conf->start_seq_pat, conf->default_start_tag, - conf->start_tag_len); - - return NULL; -} -static const char *set_undefined_echo(cmd_parms *cmd, void *mconfig, const char *msg) -{ - include_server_config *conf; - conf = ap_get_module_config(cmd->server->module_config, &include_module); - conf->undefinedEcho = apr_pstrdup(cmd->pool, msg); - conf->undefinedEchoLen = strlen(msg); - - return NULL; -} - - -static const char *set_default_end_tag(cmd_parms *cmd, void *mconfig, const char *msg) -{ - include_server_config *conf; - conf= ap_get_module_config(cmd->server->module_config , &include_module); - conf->default_end_tag = 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; -} - -/* - * Module definition and configuration data structs... - */ -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"), - AP_INIT_TAKE1("SSIStartTag", set_default_start_tag, NULL, RSRC_CONF, - "SSI Start String Tag"), - AP_INIT_TAKE1("SSIEndTag", set_default_end_tag, NULL, RSRC_CONF, - "SSI End String Tag"), - AP_INIT_TAKE1("SSIUndefinedEcho", set_undefined_echo, NULL, RSRC_CONF, - "SSI Start String Tag"), - - {NULL} -}; - -static int include_fixup(request_rec *r) -{ - include_dir_config *conf; - - conf = (include_dir_config *) ap_get_module_config(r->per_dir_config, - &include_module); - - if (r->handler && (strcmp(r->handler, "server-parsed") == 0)) - { - if (!r->content_type || !*r->content_type) { - ap_set_content_type(r, "text/html"); - } - r->handler = "default-handler"; - } - else -#if defined(OS2) || defined(WIN32) || defined(NETWARE) - /* These OS's don't support xbithack. This is being worked on. */ - { - return DECLINED; - } -#else - { - if (*conf->xbithack == xbithack_off) { - return DECLINED; - } - - if (!(r->finfo.protection & APR_UEXECUTE)) { - return DECLINED; - } - - if (!r->content_type || strcmp(r->content_type, "text/html")) { - return DECLINED; - } - } -#endif - - /* We always return declined, because the default handler actually - * serves the file. All we have to do is add the filter. - */ - ap_add_output_filter("INCLUDES", NULL, r, r->connection); - return DECLINED; -} - -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_fixups(include_fixup, NULL, NULL, APR_HOOK_LAST); - ap_register_output_filter("INCLUDES", includes_filter, includes_setup, - AP_FTYPE_RESOURCE); -} - -module AP_MODULE_DECLARE_DATA include_module = -{ - STANDARD20_MODULE_STUFF, - create_includes_dir_config, /* dir config creater */ - NULL, /* dir merger --- default is to override */ - create_includes_server_config,/* server config */ - NULL, /* merge server config */ - includes_cmds, /* command apr_table_t */ - register_hooks /* register hooks */ -}; |