#include "util_ebcdic.h"
#define RAW_ASCII_CHAR(ch) apr_xlate_conv_byte(ap_hdrs_from_ascii, \
(unsigned char)ch)
-#else /*APR_CHARSET_EBCDIC*/
+#else /* APR_CHARSET_EBCDIC */
#define RAW_ASCII_CHAR(ch) (ch)
-#endif /*APR_CHARSET_EBCDIC*/
+#endif /* !APR_CHARSET_EBCDIC */
+
+
+/*
+ * +-------------------------------------------------------+
+ * | |
+ * | Debugging Macros
+ * | |
+ * +-------------------------------------------------------+
+ */
#ifdef DEBUG_INCLUDE
#endif
-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.
+/*
+ * +-------------------------------------------------------+
+ * | |
+ * | Types and Structures
+ * | |
+ * +-------------------------------------------------------+
*/
enum xbithack {
regmatch_t (*re_result)[10];
};
-/* some defaults */
+
+/*
+ * +-------------------------------------------------------+
+ * | |
+ * | Static Module Data
+ * | |
+ * +-------------------------------------------------------+
+ */
+
+/* global module structure */
+module AP_MODULE_DECLARE_DATA include_module;
+
+/* function handlers for include directives */
+static apr_hash_t *include_handlers;
+
+/* forward declaration of handler registry */
+static APR_OPTIONAL_FN_TYPE(ap_register_include_handler) *ssi_pfn_register;
+
+/* 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)
+
+/* default values */
#define DEFAULT_START_SEQUENCE "<!--#"
#define DEFAULT_END_SEQUENCE "-->"
#define DEFAULT_ERROR_MSG "[an error occurred while processing this directive]"
#define DEFAULT_XBITHACK xbithack_off
#endif
-/* ------------------------ 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 ((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->intern->re_result || !ctx->intern->re_string) {
- return NULL;
- }
- else {
- int idx = atoi(var);
- apr_size_t len = (*ctx->intern->re_result)[idx].rm_eo
- - (*ctx->intern->re_result)[idx].rm_so;
- if ( (*ctx->intern->re_result)[idx].rm_so < 0
- || (*ctx->intern->re_result)[idx].rm_eo < 0) {
- return NULL;
- }
- val = apr_pstrmemdup(r->pool, ctx->intern->re_string
- + (*ctx->intern->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.
+/*
+ * +-------------------------------------------------------+
+ * | |
+ * | Environment/Expansion Functions
+ * | |
+ * +-------------------------------------------------------+
*/
-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.
* 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. */
*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..
- */
-static void ap_ssi_get_tag_and_value(include_ctx_t *ctx, char **tag,
- char **tag_val, int dodecode)
+static void add_include_vars(request_rec *r, char *timefmt)
{
- if (!ctx->intern->argv) {
- *tag = NULL;
- *tag_val = NULL;
+ apr_table_t *e = r->subprocess_env;
+ char *t;
- return;
+ 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);
}
-
- *tag_val = ctx->intern->argv->value;
- *tag = ctx->intern->argv->name;
-
- ctx->intern->argv = ctx->intern->argv->next;
-
- if (dodecode && *tag_val) {
- decodehtml(*tag_val);
+ apr_table_setn(e, "USER_NAME", LAZY_VALUE);
+ if ((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);
- return;
+ ap_unescape_url(arg_copy);
+ apr_table_setn(e, "QUERY_STRING_UNESCAPED",
+ ap_escape_shell_cmd(r->pool, arg_copy));
+ }
}
-/* 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)
+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->intern->re_result || !ctx->intern->re_string) {
+ return NULL;
+ }
+ else {
+ int idx = atoi(var);
+ apr_size_t len = (*ctx->intern->re_result)[idx].rm_eo
+ - (*ctx->intern->re_result)[idx].rm_so;
+ if ( (*ctx->intern->re_result)[idx].rm_so < 0
+ || (*ctx->intern->re_result)[idx].rm_eo < 0) {
+ return NULL;
+ }
+ val = apr_pstrmemdup(r->pool, ctx->intern->re_string
+ + (*ctx->intern->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;
+}
+
+/* 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;
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
+/*
+ * +-------------------------------------------------------+
+ * | |
+ * | Conditional Expression Parser
+ * | |
+ * +-------------------------------------------------------+
*/
-static int is_only_below(const char *path)
+
+static int re_check(request_rec *r, include_ctx_t *ctx,
+ char *string, char *rexp)
{
-#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;
+ regex_t *compiled;
+ const apr_size_t nres = sizeof(*ctx->intern->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;
}
- 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;
- }
+ if (!ctx->intern->re_result) {
+ ctx->intern->re_result = apr_pcalloc(r->pool, sizeof(*ctx->intern->re_result));
}
- return 1;
+ ctx->intern->re_string = string;
+ regex_error = ap_regexec(compiled, string, nres, *ctx->intern->re_result, 0);
+ ap_pregfree(r->pool, compiled);
+ return (!regex_error);
}
-static apr_status_t handle_include(include_ctx_t *ctx, ap_filter_t *f,
- apr_bucket_brigade *bb)
-{
- char *tag = NULL;
- char *tag_val = NULL;
- char *parsed_string;
- int loglevel = APLOG_ERR;
- request_rec *r = f->r;
-
- if (ctx->flags & SSI_FLAG_PRINTING) {
- while (1) {
- ap_ssi_get_tag_and_value(ctx, &tag, &tag_val, SSI_VALUE_DECODED);
- if (!tag || !tag_val) {
- return APR_SUCCESS;
- }
-
- if (!strcmp(tag, "virtual") || !strcmp(tag, "file")) {
- request_rec *rr = NULL;
- char *error_fmt = NULL;
+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;
+};
- parsed_string = ap_ssi_parse_string(r, ctx, tag_val, NULL,
- MAX_STRING_LEN,
- SSI_EXPAND_DROP_NAME);
- 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);
- }
+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;
- if (!error_fmt && rr->status != HTTP_OK) {
- error_fmt = "unable to include \"%s\" in parsed file %s";
- }
+ token->value = NULL;
- if (!error_fmt && (ctx->flags & SSI_FLAG_NO_EXEC) &&
- rr->content_type &&
- (strncmp(rr->content_type, "text/", 5))) {
- error_fmt = "unable to include potential exec \"%s\" "
- "in parsed file %s";
- }
- if (error_fmt == NULL) {
- /* try to avoid recursive includes. We do this by walking
- * up the r->main list of subrequests, and at each level
- * walking back through any internal redirects. At each
- * step, we compare the filenames and the URIs.
- *
- * The filename comparison catches a recursive include
- * with an ever-changing URL, eg.
- * <!--#include virtual=
- * "$REQUEST_URI/$QUERY_STRING?$QUERY_STRING/x" -->
- * which, although they would eventually be caught because
- * we have a limit on the length of files, etc., can
- * recurse for a while.
- *
- * The URI comparison catches the case where the filename
- * is changed while processing the request, so the
- * current name is never the same as any previous one.
- * This can happen with "DocumentRoot /foo" when you
- * request "/" on the server and it includes "/".
- * This only applies to modules such as mod_dir that
- * (somewhat improperly) mess with r->filename outside
- * of a filename translation phase.
- */
- int founddupe = 0;
- request_rec *p;
- for (p = r; p != NULL && !founddupe; p = p->main) {
- request_rec *q;
- for (q = p; q != NULL; q = q->prev) {
- if ((q->filename && rr->filename &&
- (strcmp(q->filename, rr->filename) == 0)) ||
- ((*q->uri == '/') &&
- (strcmp(q->uri, rr->uri) == 0)))
- {
- founddupe = 1;
- break;
- }
- }
- }
+ /* 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;
+ }
- if (p != NULL) {
- error_fmt = "Recursive include of \"%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);
- SSI_CREATE_ERROR_BUCKET(ctx, f, bb);
- }
+ 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) {
+ token->value[next++] = ch;
+ }
- /* destroy the sub request */
- if (rr != NULL) {
- ap_destroy_sub_req(rr);
- }
+ /*
+ * 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 {
- ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
- "unknown parameter \"%s\" to tag include in %s",
- tag, r->filename);
- SSI_CREATE_ERROR_BUCKET(ctx, f, bb);
+ token->value[next++] = ch;
}
}
- }
-
- return APR_SUCCESS;
-}
-
-
-static apr_status_t handle_echo(include_ctx_t *ctx, ap_filter_t *f,
- apr_bucket_brigade *bb)
-{
- 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;
- request_rec *r = f->r;
-
- encode = E_ENTITY;
-
- if (ctx->flags & SSI_FLAG_PRINTING) {
- while (1) {
- ap_ssi_get_tag_and_value(ctx, &tag, &tag_val, SSI_VALUE_DECODED);
- if (!tag || !tag_val) {
- return APR_SUCCESS;
- }
-
- 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,
- SSI_EXPAND_DROP_NAME));
- if (val) {
- switch(encode) {
- case E_NONE:
- echo_text = val;
+ else {
+ if (!qs) {
+ if (apr_isspace(ch)) {
+ tkn_fnd = 1;
+ }
+ else {
+ switch (ch) {
+ case '(':
+ case ')':
+ case '=':
+ case '!':
+ case '<':
+ case '>':
+ tkn_fnd = 1;
break;
- case E_URL:
- echo_text = ap_escape_uri(r->pool, val);
+ case '|':
+ if (*(string + 1) == '|') {
+ tkn_fnd = 1;
+ }
break;
- case E_ENTITY:
- echo_text = ap_escape_html(r->pool, val);
+ case '&':
+ if (*(string + 1) == '&') {
+ tkn_fnd = 1;
+ }
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);
+ if (!tkn_fnd) {
+ token->value[next++] = ch;
+ }
}
- APR_BRIGADE_INSERT_TAIL(bb, 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 {
+ if (ch == qs) {
+ qs = 0;
+ tkn_fnd = 1;
+ string++;
+ }
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);
- SSI_CREATE_ERROR_BUCKET(ctx, f, bb);
+ token->value[next++] = ch;
}
}
- else {
- ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
- "unknown parameter \"%s\" in tag echo of %s",
- tag, r->filename);
- SSI_CREATE_ERROR_BUCKET(ctx, f, bb);
- }
-
+ }
+ if (tkn_fnd) {
+ break;
}
}
- return APR_SUCCESS;
+ /* If qs is still set, we have an unmatched quote */
+ if (qs) {
+ *unmatched = 1;
+ next = 0;
+ }
+ token->value[next] = '\0';
+
+ return (string);
}
-/* error and tf must point to a string with room for at
- * least MAX_STRING_LEN characters
+
+/* there is an implicit assumption here that expr is at most MAX_STRING_LEN-1
+ * characters long...
*/
-static apr_status_t handle_config(include_ctx_t *ctx, ap_filter_t *f,
- apr_bucket_brigade *bb)
+static int parse_expr(request_rec *r, include_ctx_t *ctx, const char *expr,
+ int *was_error, int *was_unmatched, char *debug)
{
- char *tag = NULL;
- char *tag_val = NULL;
- char *parsed_string;
- request_rec *r = f->r;
- apr_table_t *env = r->subprocess_env;
+ 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;
- if (ctx->flags & SSI_FLAG_PRINTING) {
- while (1) {
- ap_ssi_get_tag_and_value(ctx, &tag, &tag_val, SSI_VALUE_RAW);
- if (! tag || !tag_val) {
- return APR_SUCCESS;
- }
+ debug[debug_pos] = '\0';
+ *was_error = 0;
+ *was_unmatched = 0;
+ if ((parse = expr) == (char *) NULL) {
+ return (0);
+ }
+ root = current = (struct parse_node *) NULL;
- if (!strcmp(tag, "errmsg")) {
- if (ctx->intern->error_str_override == NULL) {
- ctx->intern->error_str_override = apr_palloc(ctx->pool,
- MAX_STRING_LEN);
- ctx->error_str = ctx->intern->error_str_override;
- }
- ap_ssi_parse_string(r, ctx, tag_val,
- ctx->intern->error_str_override,
- MAX_STRING_LEN, SSI_EXPAND_DROP_NAME);
+ /* 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;
}
- else if (!strcmp(tag, "timefmt")) {
- apr_time_t date = r->request_time;
- if (ctx->intern->time_str_override == NULL) {
- ctx->intern->time_str_override = apr_palloc(ctx->pool,
- MAX_STRING_LEN);
- ctx->time_str = ctx->intern->time_str_override;
- }
- ap_ssi_parse_string(r, ctx, tag_val,
- ctx->intern->time_str_override,
- MAX_STRING_LEN, SSI_EXPAND_DROP_NAME);
+ 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;
- 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));
+ 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;
}
- else if (!strcmp(tag, "sizefmt")) {
- parsed_string = ap_ssi_parse_string(r, ctx, tag_val, NULL,
- MAX_STRING_LEN,
- SSI_EXPAND_DROP_NAME);
- decodehtml(parsed_string);
- if (!strcmp(parsed_string, "bytes")) {
- ctx->flags |= SSI_FLAG_SIZE_IN_BYTES;
- }
- else if (!strcmp(parsed_string, "abbrev")) {
- ctx->flags &= SSI_FLAG_SIZE_ABBREV;
+ 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 {
- ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
- "unknown parameter \"%s\" to tag config in %s",
- tag, r->filename);
- SSI_CREATE_ERROR_BUCKET(ctx, f, bb);
+ new->left = current->right;
+ current->right = new;
+ new->parent = current;
}
- }
- }
-
- return APR_SUCCESS;
-}
-
-
-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);
+ current = new;
+ break;
- 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";
+ 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 {
- error_fmt = "unable to lookup information about \"%s\" "
- "in parsed file %s";
+ new->left = current->right;
+ current->right = new;
+ new->parent = current;
}
- }
-
- if (error_fmt) {
- ret = -1;
- ap_log_rerror(APLOG_MARK, APLOG_ERR,
- rv, r, error_fmt, to_send, r->filename);
- }
+ current = new;
+ break;
- 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 apr_status_t handle_fsize(include_ctx_t *ctx, ap_filter_t *f,
- apr_bucket_brigade *bb)
-{
- char *tag = NULL;
- char *tag_val = NULL;
- apr_finfo_t finfo;
- apr_size_t s_len;
- apr_bucket *tmp_buck;
- char *parsed_string;
- request_rec *r = f->r;
-
- if (ctx->flags & SSI_FLAG_PRINTING) {
- while (1) {
- ap_ssi_get_tag_and_value(ctx, &tag, &tag_val, SSI_VALUE_DECODED);
- if (!tag || !tag_val) {
- return APR_SUCCESS;
+ 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;
}
- else {
- parsed_string = ap_ssi_parse_string(r, ctx, tag_val, NULL,
- MAX_STRING_LEN,
- SSI_EXPAND_DROP_NAME);
- 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 & SSI_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_BRIGADE_INSERT_TAIL(bb, tmp_buck);
- }
- else {
- SSI_CREATE_ERROR_BUCKET(ctx, f, bb);
+ /* 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;
}
- }
- }
-
- return APR_SUCCESS;
-}
-
-static apr_status_t handle_flastmod(include_ctx_t *ctx, ap_filter_t *f,
- apr_bucket_brigade *bb)
-{
- char *tag = NULL;
- char *tag_val = NULL;
- apr_finfo_t finfo;
- apr_size_t t_len;
- apr_bucket *tmp_buck;
- char *parsed_string;
- request_rec *r = f->r;
-
- if (ctx->flags & SSI_FLAG_PRINTING) {
- while (1) {
- ap_ssi_get_tag_and_value(ctx, &tag, &tag_val, SSI_VALUE_DECODED);
- if (!tag || !tag_val) {
- return APR_SUCCESS;
+ if (current == (struct parse_node *) NULL) {
+ new->left = root;
+ new->left->parent = new;
+ new->parent = (struct parse_node *) NULL;
+ root = new;
}
else {
- parsed_string = ap_ssi_parse_string(r, ctx, tag_val, NULL,
- MAX_STRING_LEN,
- SSI_EXPAND_DROP_NAME);
- 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);
+ new->left = current->right;
+ current->right = new;
+ new->parent = current;
+ }
+ current = new;
+ break;
- tmp_buck = apr_bucket_pool_create(t_val, t_len, r->pool,
- r->connection->bucket_alloc);
- APR_BRIGADE_INSERT_TAIL(bb, tmp_buck);
+ 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;
}
- else {
- SSI_CREATE_ERROR_BUCKET(ctx, f, bb);
+ 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;
}
}
- return APR_SUCCESS;
-}
-
-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->intern->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->intern->re_result) {
- ctx->intern->re_result = apr_pcalloc(r->pool, sizeof(*ctx->intern->re_result));
- }
- ctx->intern->re_string = string;
- regex_error = ap_regexec(compiled, string, nres, *ctx->intern->re_result, 0);
- ap_pregfree(r->pool, compiled);
- return (!regex_error);
-}
+ /* 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;
-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;
-};
+ 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;
-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;
+ 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;
- token->value = NULL;
+ 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;
- /* Skip leading white space */
- if (string == (char *) NULL) {
- return (char *) NULL;
- }
- while ((ch = *string++)) {
- if (!apr_isspace(ch)) {
+ 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;
}
}
- 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;
+ retval = (root == (struct parse_node *) NULL) ? 0 : root->value;
+ return (retval);
+}
+
+
+/*
+ * +-------------------------------------------------------+
+ * | |
+ * | Action Handlers
+ * | |
+ * +-------------------------------------------------------+
+ */
+
+/*
+ * 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..
+ */
+static void ap_ssi_get_tag_and_value(include_ctx_t *ctx, char **tag,
+ char **tag_val, int dodecode)
+{
+ if (!ctx->intern->argv) {
+ *tag = NULL;
+ *tag_val = NULL;
+
+ return;
}
- /* 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) {
- token->value[next++] = ch;
+
+ *tag_val = ctx->intern->argv->value;
+ *tag = ctx->intern->argv->name;
+
+ ctx->intern->argv = ctx->intern->argv->next;
+
+ if (dodecode && *tag_val) {
+ decodehtml(*tag_val);
}
- /*
- * 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;
- }
+ return;
+}
+
+/* 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;
}
- 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;
- }
+ }
+ return 1;
+}
+
+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 {
- if (ch == qs) {
- qs = 0;
- tkn_fnd = 1;
- string++;
- }
- else {
- token->value[next++] = ch;
- }
+ error_fmt = "unable to lookup information about \"%s\" "
+ "in parsed file %s";
}
}
- if (tkn_fnd) {
- break;
+
+ if (error_fmt) {
+ ret = -1;
+ ap_log_rerror(APLOG_MARK, APLOG_ERR,
+ rv, r, error_fmt, to_send, r->filename);
}
- }
- /* If qs is still set, we have an unmatched quote */
- if (qs) {
- *unmatched = 1;
- next = 0;
+ if (rr) ap_destroy_sub_req(rr);
+
+ return ret;
}
- token->value[next] = '\0';
+ 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);
- return (string);
+ 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;
+ }
}
-
-/* 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)
+static apr_status_t handle_include(include_ctx_t *ctx, ap_filter_t *f,
+ apr_bucket_brigade *bb)
{
- 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;
+ char *tag = NULL;
+ char *tag_val = NULL;
+ char *parsed_string;
+ int loglevel = APLOG_ERR;
+ request_rec *r = f->r;
- debug[debug_pos] = '\0';
- *was_error = 0;
- *was_unmatched = 0;
- if ((parse = expr) == (char *) NULL) {
- return (0);
- }
- root = current = (struct parse_node *) NULL;
+ if (ctx->flags & SSI_FLAG_PRINTING) {
+ while (1) {
+ ap_ssi_get_tag_and_value(ctx, &tag, &tag_val, SSI_VALUE_DECODED);
+ if (!tag || !tag_val) {
+ return APR_SUCCESS;
+ }
- /* 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) {
+ if (!strcmp(tag, "virtual") || !strcmp(tag, "file")) {
+ request_rec *rr = NULL;
+ char *error_fmt = NULL;
- 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;
+ parsed_string = ap_ssi_parse_string(r, ctx, tag_val, NULL,
+ MAX_STRING_LEN,
+ SSI_EXPAND_DROP_NAME);
+ 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);
+ }
- 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;
+ if (!error_fmt && rr->status != HTTP_OK) {
+ error_fmt = "unable to include \"%s\" in parsed file %s";
+ }
- 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;
+ if (!error_fmt && (ctx->flags & SSI_FLAG_NO_EXEC) &&
+ rr->content_type &&
+ (strncmp(rr->content_type, "text/", 5))) {
+ error_fmt = "unable to include potential exec \"%s\" "
+ "in parsed file %s";
+ }
+ if (error_fmt == NULL) {
+ /* try to avoid recursive includes. We do this by walking
+ * up the r->main list of subrequests, and at each level
+ * walking back through any internal redirects. At each
+ * step, we compare the filenames and the URIs.
+ *
+ * The filename comparison catches a recursive include
+ * with an ever-changing URL, eg.
+ * <!--#include virtual=
+ * "$REQUEST_URI/$QUERY_STRING?$QUERY_STRING/x" -->
+ * which, although they would eventually be caught because
+ * we have a limit on the length of files, etc., can
+ * recurse for a while.
+ *
+ * The URI comparison catches the case where the filename
+ * is changed while processing the request, so the
+ * current name is never the same as any previous one.
+ * This can happen with "DocumentRoot /foo" when you
+ * request "/" on the server and it includes "/".
+ * This only applies to modules such as mod_dir that
+ * (somewhat improperly) mess with r->filename outside
+ * of a filename translation phase.
+ */
+ int founddupe = 0;
+ request_rec *p;
+ for (p = r; p != NULL && !founddupe; p = p->main) {
+ request_rec *q;
+ for (q = p; q != NULL; q = q->prev) {
+ if ((q->filename && rr->filename &&
+ (strcmp(q->filename, rr->filename) == 0)) ||
+ ((*q->uri == '/') &&
+ (strcmp(q->uri, rr->uri) == 0)))
+ {
+ founddupe = 1;
+ break;
+ }
+ }
+ }
+
+ if (p != NULL) {
+ error_fmt = "Recursive include of \"%s\" "
+ "in parsed file %s";
+ }
}
- 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;
- 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;
+ /* 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);
+ SSI_CREATE_ERROR_BUCKET(ctx, f, bb);
}
- }
- 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;
+ /* destroy the sub request */
+ if (rr != NULL) {
+ ap_destroy_sub_req(rr);
}
- 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;
- current->right = new;
- new->parent = current;
+ ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
+ "unknown parameter \"%s\" to tag include in %s",
+ tag, r->filename);
+ SSI_CREATE_ERROR_BUCKET(ctx, f, bb);
}
- 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;
+ return APR_SUCCESS;
+}
+
+static apr_status_t handle_echo(include_ctx_t *ctx, ap_filter_t *f,
+ apr_bucket_brigade *bb)
+{
+ 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;
+ request_rec *r = f->r;
+
+ encode = E_ENTITY;
+
+ if (ctx->flags & SSI_FLAG_PRINTING) {
+ while (1) {
+ ap_ssi_get_tag_and_value(ctx, &tag, &tag_val, SSI_VALUE_DECODED);
+ if (!tag || !tag_val) {
+ return APR_SUCCESS;
}
- 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;
+ 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,
+ SSI_EXPAND_DROP_NAME));
+ 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_BRIGADE_INSERT_TAIL(bb, tmp_buck);
}
- /* 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:
+ 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,
- "Invalid expression \"%s\" in file %s",
- expr, r->filename);
- *was_error = 1;
- return retval;
+ "unknown value \"%s\" to parameter \"encoding\" of "
+ "tag echo in %s", tag_val, r->filename);
+ SSI_CREATE_ERROR_BUCKET(ctx, f, bb);
}
}
- 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;
+ ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
+ "unknown parameter \"%s\" in tag echo of %s",
+ tag, r->filename);
+ SSI_CREATE_ERROR_BUCKET(ctx, f, bb);
}
- 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;
+ return APR_SUCCESS;
+}
- 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;
+/* error and tf must point to a string with room for at
+ * least MAX_STRING_LEN characters
+ */
+static apr_status_t handle_config(include_ctx_t *ctx, ap_filter_t *f,
+ apr_bucket_brigade *bb)
+{
+ char *tag = NULL;
+ char *tag_val = NULL;
+ char *parsed_string;
+ request_rec *r = f->r;
+ apr_table_t *env = r->subprocess_env;
- 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;
+ if (ctx->flags & SSI_FLAG_PRINTING) {
+ while (1) {
+ ap_ssi_get_tag_and_value(ctx, &tag, &tag_val, SSI_VALUE_RAW);
+ if (! tag || !tag_val) {
+ return APR_SUCCESS;
}
-#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;
+ if (!strcmp(tag, "errmsg")) {
+ if (ctx->intern->error_str_override == NULL) {
+ ctx->intern->error_str_override = apr_palloc(ctx->pool,
+ MAX_STRING_LEN);
+ ctx->error_str = ctx->intern->error_str_override;
+ }
+ ap_ssi_parse_string(r, ctx, tag_val,
+ ctx->intern->error_str_override,
+ MAX_STRING_LEN, SSI_EXPAND_DROP_NAME);
}
- else if (current->token.type == token_le) {
- current->value = current->value <= 0;
+ else if (!strcmp(tag, "timefmt")) {
+ apr_time_t date = r->request_time;
+ if (ctx->intern->time_str_override == NULL) {
+ ctx->intern->time_str_override = apr_palloc(ctx->pool,
+ MAX_STRING_LEN);
+ ctx->time_str = ctx->intern->time_str_override;
+ }
+ ap_ssi_parse_string(r, ctx, tag_val,
+ ctx->intern->time_str_override,
+ MAX_STRING_LEN, SSI_EXPAND_DROP_NAME);
+
+ 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 (current->token.type == token_lt) {
- current->value = current->value < 0;
+ else if (!strcmp(tag, "sizefmt")) {
+ parsed_string = ap_ssi_parse_string(r, ctx, tag_val, NULL,
+ MAX_STRING_LEN,
+ SSI_EXPAND_DROP_NAME);
+ decodehtml(parsed_string);
+ if (!strcmp(parsed_string, "bytes")) {
+ ctx->flags |= SSI_FLAG_SIZE_IN_BYTES;
+ }
+ else if (!strcmp(parsed_string, "abbrev")) {
+ ctx->flags &= SSI_FLAG_SIZE_ABBREV;
+ }
}
else {
- current->value = 0; /* Don't return -1 if unknown token */
+ ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
+ "unknown parameter \"%s\" to tag config in %s",
+ tag, r->filename);
+ SSI_CREATE_ERROR_BUCKET(ctx, f, bb);
}
-#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;
+ return APR_SUCCESS;
+}
+
+static apr_status_t handle_fsize(include_ctx_t *ctx, ap_filter_t *f,
+ apr_bucket_brigade *bb)
+{
+ char *tag = NULL;
+ char *tag_val = NULL;
+ apr_finfo_t finfo;
+ apr_size_t s_len;
+ apr_bucket *tmp_buck;
+ char *parsed_string;
+ request_rec *r = f->r;
+
+ if (ctx->flags & SSI_FLAG_PRINTING) {
+ while (1) {
+ ap_ssi_get_tag_and_value(ctx, &tag, &tag_val, SSI_VALUE_DECODED);
+ if (!tag || !tag_val) {
+ return APR_SUCCESS;
}
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;
+ parsed_string = ap_ssi_parse_string(r, ctx, tag_val, NULL,
+ MAX_STRING_LEN,
+ SSI_EXPAND_DROP_NAME);
+ 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];
- case token_group:
- if (current->right != (struct parse_node *) NULL) {
- if (!current->right->done) {
- current = current->right;
- continue;
+ if (!(ctx->flags & SSI_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_BRIGADE_INSERT_TAIL(bb, tmp_buck);
+ }
+ else {
+ SSI_CREATE_ERROR_BUCKET(ctx, f, bb);
}
- 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;
+ return APR_SUCCESS;
+}
- 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;
+static apr_status_t handle_flastmod(include_ctx_t *ctx, ap_filter_t *f,
+ apr_bucket_brigade *bb)
+{
+ char *tag = NULL;
+ char *tag_val = NULL;
+ apr_finfo_t finfo;
+ apr_size_t t_len;
+ apr_bucket *tmp_buck;
+ char *parsed_string;
+ request_rec *r = f->r;
- default:
- ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
- "bad token type");
- *was_error = 1;
- return retval;
+ if (ctx->flags & SSI_FLAG_PRINTING) {
+ while (1) {
+ ap_ssi_get_tag_and_value(ctx, &tag, &tag_val, SSI_VALUE_DECODED);
+ if (!tag || !tag_val) {
+ return APR_SUCCESS;
+ }
+ else {
+ parsed_string = ap_ssi_parse_string(r, ctx, tag_val, NULL,
+ MAX_STRING_LEN,
+ SSI_EXPAND_DROP_NAME);
+ 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_BRIGADE_INSERT_TAIL(bb, tmp_buck);
+ }
+ else {
+ SSI_CREATE_ERROR_BUCKET(ctx, f, bb);
+ }
+ }
}
}
- retval = (root == (struct parse_node *) NULL) ? 0 : root->value;
- return (retval);
+ return APR_SUCCESS;
}
/* pjr - These seem to allow expr="fred" expr="joe" where joe overwrites fred. */
r->connection->bucket_alloc);
APR_BRIGADE_INSERT_TAIL(bb, tmp_buck);
}
- return APR_SUCCESS;
- }
- else {
- ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
- "printenv directive does not take tags in %s",
- r->filename);
- SSI_CREATE_ERROR_BUCKET(ctx, f, bb);
- return APR_SUCCESS;
- }
+ return APR_SUCCESS;
+ }
+ else {
+ ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
+ "printenv directive does not take tags in %s",
+ r->filename);
+ SSI_CREATE_ERROR_BUCKET(ctx, f, bb);
+ return APR_SUCCESS;
+ }
+ }
+
+ return APR_SUCCESS;
+}
+
+
+/*
+ * +-------------------------------------------------------+
+ * | |
+ * | Main Includes-Filter Engine
+ * | |
+ * +-------------------------------------------------------+
+ */
+
+/* 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 APR_SUCCESS;
+ return hl;
}
-/* -------------------------- The main function --------------------------- */
-
/*
* returns the index position of the first byte of start_seq (or the len of
* the buffer as non-match)
else {
include_handler_fn_t *handle_func;
- handle_func = apr_hash_get(include_hash, intern->directive,
+ handle_func = apr_hash_get(include_handlers, intern->directive,
intern->directive_len);
if (handle_func) {
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 = DEFAULT_END_SEQUENCE;
- result->default_start_tag = DEFAULT_START_SEQUENCE;
- result->start_tag_len = sizeof(DEFAULT_START_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;
-}
+/*
+ * +-------------------------------------------------------+
+ * | |
+ * | Runtime Hooks
+ * | |
+ * +-------------------------------------------------------+
+ */
static int includes_setup(ap_filter_t *f)
{
return send_parsed_content(f, b);
}
-static void ap_register_include_handler(char *tag, include_handler_fn_t *func)
+static int include_fixup(request_rec *r)
{
- apr_hash_set(include_hash, tag, strlen(tag), (const void *)func);
+ 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 int include_post_config(apr_pool_t *p, apr_pool_t *plog,
- apr_pool_t *ptemp, server_rec *s)
+
+/*
+ * +-------------------------------------------------------+
+ * | |
+ * | Configuration Handling
+ * | |
+ * +-------------------------------------------------------+
+ */
+
+static void *create_includes_dir_config(apr_pool_t *p, char *dummy)
{
- include_hash = apr_hash_make(p);
-
- ssi_pfn_register = APR_RETRIEVE_OPTIONAL_FN(ap_register_include_handler);
+ 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;
+}
- if(ssi_pfn_register) {
- ssi_pfn_register("if", handle_if);
- ssi_pfn_register("set", handle_set);
- ssi_pfn_register("else", handle_else);
- ssi_pfn_register("elif", handle_elif);
- ssi_pfn_register("echo", handle_echo);
- ssi_pfn_register("endif", handle_endif);
- ssi_pfn_register("fsize", handle_fsize);
- ssi_pfn_register("config", handle_config);
- ssi_pfn_register("include", handle_include);
- ssi_pfn_register("flastmod", handle_flastmod);
- ssi_pfn_register("printenv", handle_printenv);
+static 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 = DEFAULT_END_SEQUENCE;
+ result->default_start_tag = DEFAULT_START_SEQUENCE;
+ result->start_tag_len = sizeof(DEFAULT_START_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;
}
- return OK;
+ 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 const char *set_default_error_msg(cmd_parms *cmd, void *mconfig, const char *msg)
return NULL;
}
+
/*
- * Module definition and configuration data structs...
+ * +-------------------------------------------------------+
+ * | |
+ * | Module Initialization and Configuration
+ * | |
+ * +-------------------------------------------------------+
*/
+
+static int include_post_config(apr_pool_t *p, apr_pool_t *plog,
+ apr_pool_t *ptemp, server_rec *s)
+{
+ include_handlers = 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 command_rec includes_cmds[] =
{
AP_INIT_TAKE1("XBitHack", set_xbithack, NULL, OR_OPTIONS,
{NULL}
};
-static int include_fixup(request_rec *r)
+static void ap_register_include_handler(char *tag, include_handler_fn_t *func)
{
- 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;
+ apr_hash_set(include_handlers, tag, strlen(tag), (const void *)func);
}
static void register_hooks(apr_pool_t *p)