From 645eedbf30a8f4418944324d6a7eb0aa3778dab4 Mon Sep 17 00:00:00 2001 From: Ryan Bloom Date: Wed, 22 Nov 2000 19:49:20 +0000 Subject: [PATCH] A complete re-write of mod_include. This has served the entire manual now. This makes mod_include a filter that uses buckets directly. Submitted by: Paul J. Reder Reviewed by: Ryan Bloom git-svn-id: https://svn.apache.org/repos/asf/httpd/httpd/trunk@87067 13f79535-47bb-0310-9956-ffa450edef68 --- modules/filters/mod_include.c | 2387 ++++++++++++++++++++------------- modules/filters/mod_include.h | 206 +++ 2 files changed, 1685 insertions(+), 908 deletions(-) create mode 100644 modules/filters/mod_include.h diff --git a/modules/filters/mod_include.c b/modules/filters/mod_include.c index 2c65dc92b5..8731d65602 100644 --- a/modules/filters/mod_include.c +++ b/modules/filters/mod_include.c @@ -80,11 +80,6 @@ #ifdef USE_PERL_SSI #include "config.h" -#undef VOIDUSED -#ifdef USE_SFIO -#undef USE_SFIO -#define USE_STDIO -#endif #include "modules/perl/mod_perl.h" #else #include "apr_strings.h" @@ -99,7 +94,7 @@ #include "http_main.h" #include "util_script.h" #include "http_core.h" -#include "ap_mpm.h" +#include "mod_include.h" #ifdef HAVE_STRING_H #include #endif @@ -112,27 +107,6 @@ #endif #include "util_ebcdic.h" -#define STARTING_SEQUENCE "" - -#define DEFAULT_ERROR_MSG "[an error occurred while processing this directive]" -#define DEFAULT_TIME_FORMAT "%A, %d-%b-%Y %H:%M:%S %Z" -#define SIZEFMT_BYTES 0 -#define SIZEFMT_KMG 1 -#ifdef CHARSET_EBCDIC -#define RAW_ASCII_CHAR(ch) apr_xlate_conv_byte(ap_hdrs_from_ascii, (unsigned char)ch) -#else /*CHARSET_EBCDIC*/ -#define RAW_ASCII_CHAR(ch) (ch) -#endif /*CHARSET_EBCDIC*/ - -module AP_MODULE_DECLARE_DATA includes_module; - -/* just need some arbitrary non-NULL pointer which can't also be a request_rec */ -#define NESTED_INCLUDE_MAGIC (&includes_module) - -/* TODO: changing directory should be handled by CreateProcess */ -#define ap_chdir_file(x) do {} while(0) - /* ------------------------ Environment function -------------------------- */ /* XXX: could use ap_table_overlap here */ @@ -181,14 +155,19 @@ static void add_include_vars(request_rec *r, char *timefmt) /* --------------------------- Parser functions --------------------------- */ -#define OUTBUFSIZE 4096 - -static ap_bucket *find_string(ap_bucket *dptr, const char *str, ap_bucket *end) +/* This function returns either a pointer to the split bucket containing the + * first byte of the BEGINNING_SEQUENCE (after finding a complete match) or it + * returns NULL if no match found. + */ +static ap_bucket *find_start_sequence(ap_bucket *dptr, include_ctx_t *ctx, + ap_bucket_brigade *bb, int *do_cleanup) { - apr_size_t len; + apr_ssize_t len; const char *c; const char *buf; - int state = 0; + const char *str = STARTING_SEQUENCE; + + *do_cleanup = 0; do { if (AP_BUCKET_IS_EOS(dptr)) { @@ -201,41 +180,218 @@ static ap_bucket *find_string(ap_bucket *dptr, const char *str, ap_bucket *end) } c = buf; while (c - buf != len) { - if (*c == str[state]) { - state++; + if (*c == str[ctx->parse_pos]) { + if (ctx->state == PRE_HEAD) { + ctx->state = PARSE_HEAD; + ctx->head_start_bucket = dptr; + ctx->head_start_index = c - buf; + } + ctx->parse_pos++; } else { - if (str[state] == '\0') { - /* We want to split the bucket at the '<' and '>' - * respectively. That means adjusting where we split based - * on what we are searching for. + if (str[ctx->parse_pos] == '\0') { + ap_bucket *tmp_bkt; + apr_ssize_t start_index; + + /* We want to split the bucket at the '<'. */ + ctx->state = PARSE_TAG; + ctx->tag_length = 0; + ctx->parse_pos = 0; + ctx->tag_start_bucket = dptr; + ctx->tag_start_index = c - buf; + if (ctx->head_start_index > 0) { + start_index = (c - buf) - ctx->head_start_index; + ap_bucket_split(ctx->head_start_bucket, ctx->head_start_index); + tmp_bkt = AP_BUCKET_NEXT(ctx->head_start_bucket); + if (dptr == ctx->head_start_bucket) { + ctx->tag_start_bucket = tmp_bkt; + ctx->tag_start_index = start_index; + } + ctx->head_start_bucket = tmp_bkt; + ctx->head_start_index = 0; + } + return ctx->head_start_bucket; + } + else if (ctx->parse_pos != 0) { + /* The reason for this, is that we need to make sure + * that we catch cases like <. This makes the + * second check after the original check fails. + * If parse_pos was already 0 then we already checked this. + */ + ctx->tag_length += ctx->parse_pos; + + if (*c == str[0]) { + ctx->parse_pos = 1; + ctx->state = PARSE_TAIL; + ctx->tail_start_bucket = dptr; + ctx->tail_start_index = c - buf; + } + else { + ctx->parse_pos = 0; + ctx->state = PARSE_TAG; + ctx->tail_start_bucket = NULL; + ctx->tail_start_index = 0; + } + } } } c++; } dptr = AP_BUCKET_NEXT(dptr); - } while (AP_BUCKET_PREV(dptr) != end); + } while (dptr != AP_BRIGADE_SENTINEL(bb)); return NULL; } +/* This function culls through the buckets that have been set aside in the + * ssi_tag_brigade and copies just the directive part of the SSI tag (none + * of the start and end delimiter bytes are copied). + */ +static apr_status_t get_combined_directive (include_ctx_t *ctx, + request_rec *r, + ap_bucket_brigade *bb, + char *tmp_buf, int tmp_buf_size) +{ + int done = 0; + ap_bucket *dptr; + const char *tmp_from; + apr_ssize_t tmp_from_len; + + /* If the tag length is longer than the tmp buffer, allocate space. */ + if (ctx->tag_length > tmp_buf_size-1) { + if ((ctx->combined_tag = apr_pcalloc(r->pool, ctx->tag_length + 1)) == NULL) { + return (APR_ENOMEM); + } + } /* Else, just use the temp buffer. */ + else { + ctx->combined_tag = tmp_buf; + } + + /* Prime the pump. Start at the beginning of the tag... */ + dptr = ctx->tag_start_bucket; + ap_bucket_read (dptr, &tmp_from, &tmp_from_len, 0); /* Read the bucket... */ + + /* Adjust the pointer to start at the tag within the bucket... */ + if (dptr == ctx->tail_start_bucket) { + tmp_from_len -= (tmp_from_len - ctx->tail_start_index); + } + tmp_from = &tmp_from[ctx->tag_start_index]; + tmp_from_len -= ctx->tag_start_index; + ctx->curr_tag_pos = ctx->combined_tag; + + /* Loop through the buckets from the tag_start_bucket until before + * the tail_start_bucket copying the contents into the buffer. + */ + do { + memcpy (ctx->curr_tag_pos, tmp_from, tmp_from_len); + ctx->curr_tag_pos += tmp_from_len; + + if (dptr == ctx->tail_start_bucket) { + done = 1; + } + else { + dptr = AP_BUCKET_NEXT (dptr); + ap_bucket_read (dptr, &tmp_from, &tmp_from_len, 0); + /* Adjust the count to stop at the beginning of the tail. */ + if (dptr == ctx->tail_start_bucket) { + tmp_from_len -= (tmp_from_len - ctx->tail_start_index); + } + } + } while ((!done) && + ((ctx->curr_tag_pos - ctx->combined_tag) < ctx->tag_length)); + + ctx->combined_tag[ctx->tag_length] = '\0'; + ctx->curr_tag_pos = ctx->combined_tag; + + return (APR_SUCCESS); +} + /* * decodes a string containing html entities or numeric character references. * 's' is overwritten with the decoded string. @@ -331,193 +487,136 @@ otilde\365oslash\370ugrave\371uacute\372yacute\375" /* 6 */ } /* - * extract the next tag name and value. - * if there are no more tags, set the tag name to 'done' - * the tag value is html decoded if dodecode is non-zero + * 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][WS]=[WS]['|"]['|"|WS] */ -static char *get_tag(apr_pool_t *p, ap_bucket *in, char *tag, int tagbuf_len, int dodecode, apr_off_t *offset) +#define SKIP_TAG_WHITESPACE(ptr) while ((*ptr != '\0') && (apr_isspace (*ptr))) ptr++ + +static void get_tag_and_value(include_ctx_t *ctx, char **tag, + char **tag_val, int dodecode) { - ap_bucket *dptr = in; - const char *c; - const char *str; - apr_size_t length; - char *t = tag, *tag_val, term; + char *c = ctx->curr_tag_pos; + int shift_val = 0; + char term = '\0'; - /* makes code below a little less cluttered */ - --tagbuf_len; + *tag_val = NULL; + SKIP_TAG_WHITESPACE(c); + *tag = c; /* First non-whitespace character (could be NULL). */ - /* Remove all whitespace */ - do { - ap_bucket_read(dptr, &str, &length, 0); - c = str + *offset; - *offset = 0; - while (c - str < length) { - if (!apr_isspace(*c)) { - break; - } - c++; - } - if (!apr_isspace(*c)) { - break; - } - dptr = AP_BUCKET_NEXT(dptr); - } while (dptr); - - /* tags can't start with - */ - if (*c == '-') { + while ((*c != '\0') && (*c != '=') && (!apr_isspace(*c))) { + *c = apr_tolower(*c); /* find end of tag, lowercasing as we go... */ c++; - if (c == '\0') { - ap_bucket_read(dptr, &str, &length, 0); - c = str; - } - if (*c == '-') { - do { - c++; - if (c == '\0') { - ap_bucket_read(dptr, &str, &length, 0); - c = str; - } - } while (apr_isspace(*c)); - if (*c == '>') { - apr_cpystrn(tag, "done", tagbuf_len); - *offset = c - str; - return tag; - } - } - return NULL; /* failed */ } - /* find end of tag name */ - while (1) { - if (t - tag == tagbuf_len) { - *t = '\0'; - return NULL; - } - if (*c == '=' || apr_isspace(*c)) { - break; - } - *(t++) = apr_tolower(*c); - c++; - if (c == '\0') { - ap_bucket_read(dptr, &str, &length, 0); - c = str; + if ((*c == '\0') || (**tag == '=')) { + if ((**tag == '\0') || (**tag == '=')) { + *tag = NULL; } - } - - *t++ = '\0'; - tag_val = t; + ctx->curr_tag_pos = c; + return; /* We have found the end of the buffer. */ + } /* We might have a tag, but definitely no value. */ - while (apr_isspace(*c)) { - c++; - if (c == '\0') { - ap_bucket_read(dptr, &str, &length, 0); - c = str; - } - } - if (*c != '=') { - /* XXX may need to ungetc() here (see pre-bucketized code) */ - return NULL; + if (*c == '=') { + *c++ = '\0'; /* Overwrite the '=' with a terminating byte after tag. */ } - - do { - c++; - if (c == '\0') { - ap_bucket_read(dptr, &str, &length, 0); - c = str; + else { /* Try skipping WS to find the '='. */ + *c++ = '\0'; /* Terminate the tag... */ + SKIP_TAG_WHITESPACE(c); + + if (*c != '=') { /* There needs to be an equal sign if there's a value. */ + ctx->curr_tag_pos = c; + return; /* There apparently was no value. */ } - } while (apr_isspace(*c)); - - /* we should allow a 'name' as a value */ + else { + c++; /* Skip the equals sign. */ + } + } - if (*c != '"' && *c != '\'') { - return NULL; + SKIP_TAG_WHITESPACE(c); + if (*c == '"' || *c == '\'') { /* Allow quoted values for space inclusion. */ + term = *c++; /* NOTE: This does not pass the quotes on return. */ } - term = *c; - while (1) { - c++; - if (c == '\0') { - ap_bucket_read(dptr, &str, &length, 0); - c = str; - } - if (t - tag == tagbuf_len) { - *t = '\0'; - return NULL; - } -/* Want to accept \" as a valid character within a string. */ - if (*c == '\\') { - *(t++) = *c; /* Add backslash */ + + *tag_val = c; + while ((*c != '\0') && + (((term != '\0') && (*c != term)) || + ((term == '\0') && (!apr_isspace(*c))))) { + if (*c == '\\') { /* Accept \" and \' as valid char in string. */ c++; - if (c == '\0') { - ap_bucket_read(dptr, &str, &length, 0); - c = str; - } - if (*c == term) { /* Only if */ - *(--t) = *c; /* Replace backslash ONLY for terminator */ + if (*c == term) { /* Overwrite the "\" during the embedded */ + shift_val++; /* escape sequence of '\"' or "\'". Shift */ + } /* bytes from here to next delimiter. */ + if (shift_val > 0) { + *(c-shift_val) = *c; } } - else if (*c == term) { - break; + + c++; + if (shift_val > 0) { + *(c-shift_val) = *c; } - *(t++) = *c; } - *t = '\0'; + + *c++ = '\0'; /* Overwrites delimiter (term or WS) with NULL. */ + ctx->curr_tag_pos = c; if (dodecode) { - decodehtml(tag_val); + decodehtml(*tag_val); } - *offset = c - str + 1; - return apr_pstrdup(p, tag_val); + + return; } -static int get_directive(ap_bucket *in, char *dest, size_t len, apr_pool_t *p) +static char *get_directive(include_ctx_t *ctx, dir_token_id *fnd_token) { - ap_bucket *dptr = in; - char *d = dest; - const char *c; - const char *str; - apr_size_t length; + char *c = ctx->curr_tag_pos; + char *dest; + int len = 0; - /* make room for nul terminator */ - --len; + SKIP_TAG_WHITESPACE(c); - while (dptr) { - ap_bucket_read(dptr, &str, &length, 0); - /* need to start past the - * 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 ( (strcmp(q->filename, rr->filename) == 0) || - (strcmp(q->uri, rr->uri) == 0) ){ - founddupe = 1; - break; - } - } - } + if (!strcmp(tag, "file") || !strcmp(tag, "virtual")) { + request_rec *rr = NULL; + char *error_fmt = NULL; - if (p != NULL) { - error_fmt = "Recursive include of \"%s\" " + parse_string(r, tag_val, parsed_string, sizeof(parsed_string), 0); + if (tag[0] == 'f') { + /* be safe; only files in this directory or below allowed */ + if (!is_only_below(parsed_string)) { + error_fmt = "unable to include file \"%s\" " + "in parsed file %s"; + } + else { + rr = ap_sub_req_lookup_file(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"; } - } + 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. + * + * 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 ( (strcmp(q->filename, rr->filename) == 0) || + (strcmp(q->uri, rr->uri) == 0) ){ + founddupe = 1; + break; + } + } + } + + if (p != NULL) { + error_fmt = "Recursive include of \"%s\" " + "in parsed file %s"; + } + } - /* see the Kludge in send_parsed_file for why */ - if (rr) - ap_set_module_config(rr->request_config, &includes_module, r); + /* 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, &includes_module, r); - if (!error_fmt) { - /* The subrequest should inherit the remaining filters from - * this request. */ - rr->output_filters = next; - if (ap_run_sub_req(rr)) { - error_fmt = "unable to include \"%s\" in parsed file %s"; + if (!error_fmt) { + SPLIT_AND_PASS_PRETAG_BUCKETS(*bb, ctx); +/* + rr->output_filters = f->next; +*/ if (ap_run_sub_req(rr)) { + error_fmt = "unable to include \"%s\" in parsed file %s"; + } + } + ap_chdir_file(r->filename); + if (error_fmt) { + ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, + 0, r, error_fmt, tag_val, r->filename); + CREATE_ERROR_BUCKET(ctx, tmp_buck, head_ptr, *inserted_head); } - } - ap_chdir_file(r->filename); - if (error_fmt) { - ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, - 0, r, error_fmt, tag_val, r->filename); - ap_rputs(error, r); - } - /* destroy the sub request if it's not a nested include */ - if (rr != NULL - && ap_get_module_config(rr->request_config, &includes_module) - != NESTED_INCLUDE_MAGIC) { - ap_destroy_sub_req(rr); + /* destroy the sub request if it's not a nested include (crumb) */ + if (rr != NULL + && ap_get_module_config(rr->request_config, &includes_module) + != NESTED_INCLUDE_MAGIC) { + ap_destroy_sub_req(rr); + } + } + else { + ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|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); } - } - else if (!strcmp(tag, "done")) { - return 0; - } - else { - ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, 0, r, - "unknown parameter \"%s\" to tag include in %s", - tag, r->filename); - ap_rputs(error, r); } } + return 0; } typedef struct { @@ -879,7 +1012,8 @@ static apr_status_t build_argv_list(char ***argv, request_rec *r, apr_pool_t *p) -static int include_cmd(char *s, request_rec *r, ap_filter_t *next) +static int include_cmd(include_ctx_t *ctx, ap_bucket_brigade **bb, char *s, + request_rec *r, ap_filter_t *f) { include_cmd_arg arg; apr_procattr_t *procattr; @@ -908,7 +1042,7 @@ static int include_cmd(char *s, request_rec *r, ap_filter_t *next) apr_table_setn(env, "PATH_INFO", ap_escape_shell_cmd(r->pool, r->path_info)); - pa_req = ap_sub_req_lookup_uri(ap_escape_uri(r->pool, r->path_info), r); + pa_req = ap_sub_req_lookup_uri(ap_escape_uri(r->pool, r->path_info), r, f->next); if (pa_req->filename) { apr_table_setn(env, "PATH_TRANSLATED", apr_pstrcat(r->pool, pa_req->filename, pa_req->path_info, @@ -945,10 +1079,11 @@ static int include_cmd(char *s, request_rec *r, ap_filter_t *next) rc = !APR_SUCCESS; } else { + SPLIT_AND_PASS_PRETAG_BUCKETS(*bb, ctx); build_argv_list(&argv, r, r->pool); argv[0] = apr_pstrdup(r->pool, s); procnew = apr_pcalloc(r->pool, sizeof(*procnew)); - rc = ap_os_create_privileged_process(r, procnew, s, argv, ap_create_environment(r->pool, env), procattr, r->pool); + rc = apr_create_process(procnew, s, argv, ap_create_environment(r->pool, env), procattr, r->pool); if (rc != APR_SUCCESS) { /* Bad things happened. Everyone should have cleaned up. */ @@ -967,7 +1102,7 @@ static int include_cmd(char *s, request_rec *r, ap_filter_t *next) bcgi = ap_brigade_create(r->pool); b = ap_bucket_create_pipe(file); AP_BRIGADE_INSERT_TAIL(bcgi, b); - ap_pass_brigade(next, bcgi); + ap_pass_brigade(f->next, bcgi); /* We can't close the pipe here, because we may return before the * full CGI has been sent to the network. That's okay though, @@ -979,200 +1114,237 @@ static int include_cmd(char *s, request_rec *r, ap_filter_t *next) return 0; } -static int handle_exec(ap_bucket *in, request_rec *r, const char *error, - ap_filter_t *next) +static int handle_exec(include_ctx_t *ctx, ap_bucket_brigade **bb, request_rec *r, + ap_filter_t *f, ap_bucket *head_ptr, + ap_bucket **inserted_head) { - char tag[MAX_STRING_LEN]; - char *tag_val; + char *tag = NULL; + char *tag_val = NULL; char *file = r->filename; + ap_bucket *tmp_buck; char parsed_string[MAX_STRING_LEN]; - apr_off_t offset = strlen("exec ") + strlen(STARTING_SEQUENCE); - while (1) { - if (!(tag_val = get_tag(r->pool, in, tag, sizeof(tag), 1, &offset))) { - return 1; - } - if (!strcmp(tag, "cmd")) { - parse_string(r, tag_val, parsed_string, sizeof(parsed_string), 1); - if (include_cmd(parsed_string, r, next) == -1) { - ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, 0, r, - "execution failure for parameter \"%s\" " - "to tag exec in file %s", - tag, r->filename); - ap_rputs(error, r); - } - /* just in case some stooge changed directories */ - ap_chdir_file(r->filename); - } - else if (!strcmp(tag, "cgi")) { - parse_string(r, tag_val, parsed_string, sizeof(parsed_string), 0); - if (include_cgi(parsed_string, r, next) == -1) { - ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, 0, r, - "invalid CGI ref \"%s\" in %s", tag_val, file); - ap_rputs(error, r); - } - ap_chdir_file(r->filename); - } - else if (!strcmp(tag, "done")) { - return 0; + *inserted_head = NULL; + if (ctx->flags & FLAG_PRINTING) { + if (ctx->flags & FLAG_NO_EXEC) { + ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, 0, r, + "exec used but not allowed in %s", r->filename); + CREATE_ERROR_BUCKET(ctx, tmp_buck, head_ptr, *inserted_head); } else { - ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, 0, r, - "unknown parameter \"%s\" to tag exec in %s", - tag, file); - ap_rputs(error, r); + while (1) { + get_tag_and_value(ctx, &tag, &tag_val, 1); + if (tag_val == NULL) { + if (tag == NULL) { + return (0); + } + else { + return 1; + } + } + if (!strcmp(tag, "cmd")) { + parse_string(r, tag_val, parsed_string, sizeof(parsed_string), 1); + if (include_cmd(ctx, bb, parsed_string, r, f) == -1) { + ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, 0, r, + "execution failure for parameter \"%s\" " + "to tag exec in file %s", tag, r->filename); + CREATE_ERROR_BUCKET(ctx, tmp_buck, head_ptr, *inserted_head); + } + /* just in case some stooge changed directories */ + ap_chdir_file(r->filename); + } + else if (!strcmp(tag, "cgi")) { + parse_string(r, tag_val, parsed_string, sizeof(parsed_string), 0); + SPLIT_AND_PASS_PRETAG_BUCKETS(*bb, ctx); + if (include_cgi(parsed_string, r, f->next, head_ptr, inserted_head) == -1) { + ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, 0, r, + "invalid CGI ref \"%s\" in %s", tag_val, file); + CREATE_ERROR_BUCKET(ctx, tmp_buck, head_ptr, *inserted_head); + } + ap_chdir_file(r->filename); + } + else { + ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, 0, r, + "unknown parameter \"%s\" to tag exec in %s", tag, file); + CREATE_ERROR_BUCKET(ctx, tmp_buck, head_ptr, *inserted_head); + } + } } } - + return 0; } -static int handle_echo(ap_bucket *in, request_rec *r, const char *error) +static int handle_echo(include_ctx_t *ctx, request_rec *r, ap_bucket *head_ptr, + ap_bucket **inserted_head) { - char tag[MAX_STRING_LEN]; - char *tag_val; + char *tag = NULL; + char *tag_val = NULL; + const char *echo_text = NULL; + ap_bucket *tmp_buck; + apr_ssize_t e_len, e_wrt; enum {E_NONE, E_URL, E_ENTITY} encode; - apr_off_t offset = strlen("echo ") + strlen(STARTING_SEQUENCE); encode = E_ENTITY; - while (1) { - if (!(tag_val = get_tag(r->pool, in, tag, sizeof(tag), 1, &offset))) { - return 1; - } - if (!strcmp(tag, "var")) { - const char *val = apr_table_get(r->subprocess_env, tag_val); + *inserted_head = NULL; + if (ctx->flags & FLAG_PRINTING) { + while (1) { + 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")) { + const char *val = apr_table_get(r->subprocess_env, tag_val); + int b_copy = 0; - if (val) { - if (encode == E_NONE) { - ap_rputs(val, r); - } - else if (encode == E_URL) { - ap_rputs(ap_escape_uri(r->pool, val), r); - } - else if (encode == E_ENTITY) { - ap_rputs(ap_escape_html(r->pool, val), r); - } + if (val) { + switch(encode) { + case E_NONE: echo_text = val; b_copy = 1; 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 = ap_bucket_create_heap(echo_text, e_len, 1, &e_wrt); + } + else { + tmp_buck = ap_bucket_create_immortal("(none)", sizeof("none")); + } + AP_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_NOERRNO|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); + } } else { - ap_rputs("(none)", r); + ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|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); } - } - else if (!strcmp(tag, "done")) { - return 0; - } - 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_NOERRNO|APLOG_ERR, 0, r, - "unknown value \"%s\" to parameter \"encoding\" of " - "tag echo in %s", - tag_val, r->filename); - ap_rputs(error, r); - } - } - else { - ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, 0, r, - "unknown parameter \"%s\" to tag echo in %s", - tag, r->filename); - ap_rputs(error, r); } } + return 0; } #ifdef USE_PERL_SSI -static int handle_perl(ap_bucket *in, request_rec *r, const char *error) +static int handle_perl(include_ctx_t *ctx, request_rec *r) { - char tag[MAX_STRING_LEN]; + char *tag = NULL; + char *tag_val = NULL; char parsed_string[MAX_STRING_LEN]; - char *tag_val; SV *sub = Nullsv; AV *av = newAV(); - apr_off_t offset = strlen("perl ") + strlen(STARTING_SEQUENCE); - if (ap_allow_options(r) & OPT_INCNOEXEC) { - ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, 0, r, - "#perl SSI disallowed by IncludesNoExec in %s", - r->filename); - return DECLINED; - } - while (1) { - if (!(tag_val = get_tag(r->pool, in, tag, sizeof(tag), 1, &offset))) { - break; - } - if (strnEQ(tag, "sub", 3)) { - sub = newSVpv(tag_val, 0); - } - else if (strnEQ(tag, "arg", 3)) { - parse_string(r, tag_val, parsed_string, sizeof(parsed_string), 0); - av_push(av, newSVpv(parsed_string, 0)); + if (ctx->flags & FLAG_PRINTING) { + if (ctx->flags & FLAG_NO_EXEC) { + ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, 0, r, + "#perl SSI disallowed by IncludesNoExec in %s", + r->filename); + return (DECLINED); } - else if (strnEQ(tag, "done", 4)) { - break; + + while (1) { + get_tag_and_value(ctx, &tag, &tag_val, 1); + if (tag_val == NULL) { + break; + } + if (strnEQ(tag, "sub", 3)) { + sub = newSVpv(tag_val, 0); + } + else if (strnEQ(tag, "arg", 3)) { + parse_string(r, tag_val, parsed_string, sizeof(parsed_string), 0); + av_push(av, newSVpv(parsed_string, 0)); + } } + + perl_stdout2client(r); + perl_setup_env(r); + perl_call_handler(sub, r, av); } - perl_stdout2client(r); - perl_setup_env(r); - perl_call_handler(sub, r, av); - return OK; + + return (OK); } #endif /* error and tf must point to a string with room for at * least MAX_STRING_LEN characters */ -static int handle_config(ap_bucket *in, request_rec *r, char *error, char *tf, - int *sizefmt) +static int handle_config(include_ctx_t *ctx, request_rec *r, ap_bucket *head_ptr, + ap_bucket **inserted_head) { - char tag[MAX_STRING_LEN]; - char *tag_val; + char *tag = NULL; + char *tag_val = NULL; char parsed_string[MAX_STRING_LEN]; apr_table_t *env = r->subprocess_env; - apr_off_t offset = strlen("config ") + strlen(STARTING_SEQUENCE); - while (1) { - if (!(tag_val = get_tag(r->pool, in, tag, sizeof(tag), 0, &offset))) { - return 1; - } - if (!strcmp(tag, "errmsg")) { - parse_string(r, tag_val, error, MAX_STRING_LEN, 0); - } - else if (!strcmp(tag, "timefmt")) { - apr_time_t date = r->request_time; + *inserted_head = NULL; + if (ctx->flags & FLAG_PRINTING) { + while (1) { + 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")) { + parse_string(r, tag_val, ctx->error_str, MAX_STRING_LEN, 0); + ctx->error_length = strlen(ctx->error_str); + } + else if (!strcmp(tag, "timefmt")) { + apr_time_t date = r->request_time; - parse_string(r, tag_val, tf, MAX_STRING_LEN, 0); - apr_table_setn(env, "DATE_LOCAL", ap_ht_time(r->pool, date, tf, 0)); - apr_table_setn(env, "DATE_GMT", ap_ht_time(r->pool, date, tf, 1)); - apr_table_setn(env, "LAST_MODIFIED", - ap_ht_time(r->pool, r->finfo.mtime, tf, 0)); - } - else if (!strcmp(tag, "sizefmt")) { - parse_string(r, tag_val, parsed_string, sizeof(parsed_string), 0); - decodehtml(parsed_string); - if (!strcmp(parsed_string, "bytes")) { - *sizefmt = SIZEFMT_BYTES; + parse_string(r, tag_val, ctx->time_str, MAX_STRING_LEN, 0); + apr_table_setn(env, "DATE_LOCAL", ap_ht_time(r->pool, date, ctx->time_str, 0)); + apr_table_setn(env, "DATE_GMT", ap_ht_time(r->pool, date, ctx->time_str, 1)); + apr_table_setn(env, "LAST_MODIFIED", + ap_ht_time(r->pool, r->finfo.mtime, ctx->time_str, 0)); } - else if (!strcmp(parsed_string, "abbrev")) { - *sizefmt = SIZEFMT_KMG; + else if (!strcmp(tag, "sizefmt")) { + parse_string(r, tag_val, parsed_string, sizeof(parsed_string), 0); + decodehtml(parsed_string); + if (!strcmp(parsed_string, "bytes")) { + ctx->flags |= FLAG_SIZE_IN_BYTES; + } + else if (!strcmp(parsed_string, "abbrev")) { + ctx->flags &= FLAG_SIZE_ABBREV; + } + } + else { + ap_bucket *tmp_buck; + + ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|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); } - } - else if (!strcmp(tag, "done")) { - return 0; - } - else { - ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, 0, r, - "unknown parameter \"%s\" to tag config in %s", - tag, r->filename); - ap_rputs(error, r); } } + return 0; } static int find_file(request_rec *r, const char *directive, const char *tag, - char *tag_val, apr_finfo_t *finfo, const char *error) + char *tag_val, apr_finfo_t *finfo) { char *to_send = tag_val; request_rec *rr = NULL; @@ -1187,7 +1359,7 @@ static int find_file(request_rec *r, const char *directive, const char *tag, } else { ap_getparents(tag_val); /* get rid of any nasties */ - rr = ap_sub_req_lookup_file(tag_val, r); + rr = ap_sub_req_lookup_file(tag_val, r, NULL); if (rr->status == HTTP_OK && rr->finfo.protection != 0) { to_send = rr->filename; @@ -1208,7 +1380,6 @@ static int find_file(request_rec *r, const char *directive, const char *tag, * otherwise */ ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, error_fmt, to_send, r->filename); - ap_rputs(error, r); } if (rr) ap_destroy_sub_req(rr); @@ -1216,7 +1387,7 @@ static int find_file(request_rec *r, const char *directive, const char *tag, return ret; } else if (!strcmp(tag, "virtual")) { - rr = ap_sub_req_lookup_uri(tag_val, r); + rr = ap_sub_req_lookup_uri(tag_val, r, NULL); if (rr->status == HTTP_OK && rr->finfo.protection != 0) { memcpy((char *) finfo, (const char *) &rr->finfo, @@ -1229,7 +1400,6 @@ static int find_file(request_rec *r, const char *directive, const char *tag, "unable to get information about \"%s\" " "in parsed file %s", tag_val, r->filename); - ap_rputs(error, r); ap_destroy_sub_req(rr); return -1; } @@ -1238,71 +1408,142 @@ static int find_file(request_rec *r, const char *directive, const char *tag, ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, 0, r, "unknown parameter \"%s\" to tag %s in %s", tag, directive, r->filename); - ap_rputs(error, r); return -1; } } +#define NEG_SIGN " -" +#define ZERO_K " 0k" +#define ONE_K " 1k" + +static void generate_size(apr_ssize_t size, char *buff, apr_ssize_t buff_size) +{ + /* XXX: this -1 thing is a gross hack */ + if (size == (apr_ssize_t)-1) { + memcpy (buff, NEG_SIGN, sizeof(NEG_SIGN)+1); + } + else if (!size) { + memcpy (buff, ZERO_K, sizeof(ZERO_K)+1); + } + else if (size < 1024) { + memcpy (buff, ONE_K, sizeof(ONE_K)+1); + } + else if (size < 1048576) { + apr_snprintf(buff, buff_size, "%4" APR_SSIZE_T_FMT "k", (size + 512) / 1024); + } + else if (size < 103809024) { + apr_snprintf(buff, buff_size, "%4.1fM", size / 1048576.0); + } + else { + apr_snprintf(buff, buff_size, "%4" APR_SSIZE_T_FMT "M", (size + 524288) / 1048576); + } +} -static int handle_fsize(ap_bucket *in, request_rec *r, const char *error, int sizefmt) +static int handle_fsize(include_ctx_t *ctx, request_rec *r, ap_bucket *head_ptr, + ap_bucket **inserted_head) { - char tag[MAX_STRING_LEN]; - char *tag_val; - apr_finfo_t finfo; + char *tag = NULL; + char *tag_val = NULL; + apr_finfo_t finfo; + apr_ssize_t s_len, s_wrt; + ap_bucket *tmp_buck; char parsed_string[MAX_STRING_LEN]; - apr_off_t offset = strlen("fsize ") + strlen(STARTING_SEQUENCE); - while (1) { - if (!(tag_val = get_tag(r->pool, in, tag, sizeof(tag), 1, &offset))) { - return 1; - } - else if (!strcmp(tag, "done")) { - return 0; - } - else { - parse_string(r, tag_val, parsed_string, sizeof(parsed_string), 0); - if (!find_file(r, "fsize", tag, parsed_string, &finfo, error)) { - if (sizefmt == SIZEFMT_KMG) { - ap_send_size(finfo.size, r); + *inserted_head = NULL; + if (ctx->flags & FLAG_PRINTING) { + while (1) { + get_tag_and_value(ctx, &tag, &tag_val, 1); + if (tag_val == NULL) { + if (tag == NULL) { + return 0; } else { - int l, x; - apr_snprintf(tag, sizeof(tag), "%" APR_OFF_T_FMT, finfo.size); - l = strlen(tag); /* grrr */ - for (x = 0; x < l; x++) { - if (x && (!((l - x) % 3))) { - ap_rputc(',', r); + return 1; + } + } + else { + parse_string(r, tag_val, parsed_string, sizeof(parsed_string), 0); + if (!find_file(r, "fsize", tag, parsed_string, &finfo)) { + char buff[50]; + + if (!(ctx->flags & FLAG_SIZE_IN_BYTES)) { + generate_size(finfo.size, buff, sizeof(buff)); + 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]; } - ap_rputc(tag[x], r); + buff[pos] = '\0'; + s_len = pos; + } + + tmp_buck = ap_bucket_create_heap(buff, s_len, 1, &s_wrt); + AP_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 0; } -static int handle_flastmod(ap_bucket *in, request_rec *r, const char *error, const char *tf) +static int handle_flastmod(include_ctx_t *ctx, request_rec *r, ap_bucket *head_ptr, + ap_bucket **inserted_head) { - char tag[MAX_STRING_LEN]; - char *tag_val; - apr_finfo_t finfo; + char *tag = NULL; + char *tag_val = NULL; + apr_finfo_t finfo; + apr_ssize_t t_len, t_wrt; + ap_bucket *tmp_buck; char parsed_string[MAX_STRING_LEN]; - apr_off_t offset = strlen("flastmod ") + strlen(STARTING_SEQUENCE); - while (1) { - if (!(tag_val = get_tag(r->pool, in, tag, sizeof(tag), 1, &offset))) { - return 1; - } - else if (!strcmp(tag, "done")) { - return 0; - } - else { - parse_string(r, tag_val, parsed_string, sizeof(parsed_string), 0); - if (!find_file(r, "flastmod", tag, parsed_string, &finfo, error)) { - ap_rputs(ap_ht_time(r->pool, finfo.mtime, tf, 0), r); + *inserted_head = NULL; + if (ctx->flags & FLAG_PRINTING) { + while (1) { + get_tag_and_value(ctx, &tag, &tag_val, 1); + if (tag_val == NULL) { + if (tag == NULL) { + return 0; + } + else { + return 1; + } + } + else { + parse_string(r, tag_val, parsed_string, sizeof(parsed_string), 0); + if (!find_file(r, "flastmod", tag, parsed_string, &finfo)) { + char *t_val; + + t_val = ap_ht_time(r->pool, finfo.mtime, ctx->time_str, 0); + t_len = strlen(t_val); + + tmp_buck = ap_bucket_create_heap(t_val, t_len, 1, &t_wrt); + AP_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 0; } static int re_check(request_rec *r, char *string, char *rexp) @@ -1335,11 +1576,13 @@ struct token { /* there is an implicit assumption here that string is at most MAX_STRING_LEN-1 * characters long... */ -static const char *get_ptoken(request_rec *r, const char *string, struct token *token) +static const char *get_ptoken(request_rec *r, const char *string, struct token *token, + int *unmatched) { char ch; int next = 0; int qs = 0; + int tkn_fnd = 0; /* Skip leading white space */ if (string == (char *) NULL) { @@ -1426,57 +1669,62 @@ static const char *get_ptoken(request_rec *r, const char *string, struct token * * 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'; ch = *++string) { + for (ch = *string; ((ch != '\0') && (!tkn_fnd)); ch = *++string) { if (ch == '\\') { if ((ch = *++string) == '\0') { - goto TOKEN_DONE; + tkn_fnd = 1; + } + else { + token->value[next++] = ch; } - token->value[next++] = ch; - continue; } - if (!qs) { - if (apr_isspace(ch)) { - goto TOKEN_DONE; - } - switch (ch) { - case '(': - goto TOKEN_DONE; - case ')': - goto TOKEN_DONE; - case '=': - goto TOKEN_DONE; - case '!': - goto TOKEN_DONE; - case '|': - if (*(string + 1) == '|') { - goto TOKEN_DONE; + else { + if (!qs) { + if (apr_isspace(ch)) { + tkn_fnd = 1; } - break; - case '&': - if (*(string + 1) == '&') { - goto TOKEN_DONE; + 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; + } } - break; - case '<': - goto TOKEN_DONE; - case '>': - goto TOKEN_DONE; } - token->value[next++] = ch; - } - else { - if (ch == '\'') { - qs = 0; - ++string; - goto TOKEN_DONE; + else { + if (ch == '\'') { + qs = 0; + ++string; + tkn_fnd = 1; + } + else { + token->value[next++] = ch; + } } - token->value[next++] = ch; } } - TOKEN_DONE: + /* If qs is still set, I have an unmatched ' */ if (qs) { - ap_rputs("\nUnmatched '\n", r); + *unmatched = 1; next = 0; } token->value[next] = '\0'; @@ -1494,7 +1742,8 @@ static const char *get_ptoken(request_rec *r, const char *string, struct token * /* there is an implicit assumption here that expr is at most MAX_STRING_LEN-1 * characters long... */ -static int parse_expr(request_rec *r, const char *expr, const char *error) +static int parse_expr(request_rec *r, const char *expr, int *was_error, + int *was_unmatched, char *debug) { struct parse_node { struct parse_node *left, *right, *parent; @@ -1505,7 +1754,11 @@ static int parse_expr(request_rec *r, const char *expr, const char *error) char buffer[MAX_STRING_LEN]; apr_pool_t *expr_pool; int retval = 0; + apr_ssize_t debug_pos = 0; + debug[debug_pos] = '\0'; + *was_error = 0; + *was_unmatched = 0; if ((parse = expr) == (char *) NULL) { return (0); } @@ -1519,14 +1772,15 @@ static int parse_expr(request_rec *r, const char *expr, const char *error) 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)) == (char *) NULL) { + if ((parse = get_ptoken(r, parse, &new->token, was_unmatched)) == (char *) NULL) { break; } switch (new->token.type) { case token_string: #ifdef DEBUG_INCLUDE - ap_rvputs(r, " Token: string (", new->token.value, ")\n", NULL); + debug_pos += sprintf (&debug[debug_pos], " Token: string (%s)\n", + new->token.value); #endif if (current == (struct parse_node *) NULL) { root = current = new; @@ -1561,7 +1815,7 @@ static int parse_expr(request_rec *r, const char *expr, const char *error) ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, 0, r, "Invalid expression \"%s\" in file %s", expr, r->filename); - ap_rputs(error, r); + *was_error = 1; goto RETURN; } break; @@ -1569,13 +1823,15 @@ static int parse_expr(request_rec *r, const char *expr, const char *error) case token_and: case token_or: #ifdef DEBUG_INCLUDE - ap_rputs(" Token: and/or\n", r); + 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_NOERRNO|APLOG_ERR, 0, r, "Invalid expression \"%s\" in file %s", expr, r->filename); - ap_rputs(error, r); + *was_error = 1; goto RETURN; } /* Percolate upwards */ @@ -1600,7 +1856,7 @@ static int parse_expr(request_rec *r, const char *expr, const char *error) ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, 0, r, "Invalid expression \"%s\" in file %s", expr, r->filename); - ap_rputs(error, r); + *was_error = 1; goto RETURN; } break; @@ -1621,7 +1877,9 @@ static int parse_expr(request_rec *r, const char *expr, const char *error) case token_not: #ifdef DEBUG_INCLUDE - ap_rputs(" Token: not\n", r); + 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; @@ -1645,7 +1903,7 @@ static int parse_expr(request_rec *r, const char *expr, const char *error) ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, 0, r, "Invalid expression \"%s\" in file %s", expr, r->filename); - ap_rputs(error, r); + *was_error = 1; goto RETURN; } break; @@ -1671,13 +1929,15 @@ static int parse_expr(request_rec *r, const char *expr, const char *error) case token_le: case token_lt: #ifdef DEBUG_INCLUDE - ap_rputs(" Token: eq/ne/ge/gt/le/lt\n", r); + 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_NOERRNO|APLOG_ERR, 0, r, "Invalid expression \"%s\" in file %s", expr, r->filename); - ap_rputs(error, r); + *was_error = 1; goto RETURN; } /* Percolate upwards */ @@ -1702,7 +1962,7 @@ static int parse_expr(request_rec *r, const char *expr, const char *error) ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, 0, r, "Invalid expression \"%s\" in file %s", expr, r->filename); - ap_rputs(error, r); + *was_error = 1; goto RETURN; } break; @@ -1723,7 +1983,9 @@ static int parse_expr(request_rec *r, const char *expr, const char *error) case token_rbrace: #ifdef DEBUG_INCLUDE - ap_rputs(" Token: rbrace\n", r); + 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) { @@ -1736,14 +1998,16 @@ static int parse_expr(request_rec *r, const char *expr, const char *error) ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, 0, r, "Unmatched ')' in \"%s\" in file %s", expr, r->filename); - ap_rputs(error, r); + *was_error = 1; goto RETURN; } break; case token_lbrace: #ifdef DEBUG_INCLUDE - ap_rputs(" Token: lbrace\n", r); + 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; @@ -1769,7 +2033,7 @@ static int parse_expr(request_rec *r, const char *expr, const char *error) ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, 0, r, "Invalid expression \"%s\" in file %s", expr, r->filename); - ap_rputs(error, r); + *was_error = 1; goto RETURN; } break; @@ -1798,7 +2062,9 @@ static int parse_expr(request_rec *r, const char *expr, const char *error) switch (current->token.type) { case token_string: #ifdef DEBUG_INCLUDE - ap_rputs(" Evaluate string\n", r); + memcpy (&debug[debug_pos], " Evaluate string\n", + sizeof (" Evaluate string\n")); + debug_pos += sizeof (" Evaluate string\n"); #endif parse_string(r, current->token.value, buffer, sizeof(buffer), 0); apr_cpystrn(current->token.value, buffer, sizeof(current->token.value)); @@ -1810,14 +2076,16 @@ static int parse_expr(request_rec *r, const char *expr, const char *error) case token_and: case token_or: #ifdef DEBUG_INCLUDE - ap_rputs(" Evaluate and/or\n", r); + 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_NOERRNO|APLOG_ERR, 0, r, "Invalid expression \"%s\" in file %s", expr, r->filename); - ap_rputs(error, r); + *was_error = 1; goto RETURN; } if (!current->left->done) { @@ -1851,10 +2119,10 @@ static int parse_expr(request_rec *r, const char *expr, const char *error) } } #ifdef DEBUG_INCLUDE - ap_rvputs(r, " Left: ", current->left->value ? "1" : "0", - "\n", NULL); - ap_rvputs(r, " Right: ", current->right->value ? "1" : "0", - "\n", NULL); + 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; @@ -1863,8 +2131,8 @@ static int parse_expr(request_rec *r, const char *expr, const char *error) current->value = current->left->value || current->right->value; } #ifdef DEBUG_INCLUDE - ap_rvputs(r, " Returning ", current->value ? "1" : "0", - "\n", NULL); + debug_pos += sprintf (&debug[debug_pos], " Returning %c\n", + current->value ? '1' : '0'); #endif current->done = 1; current = current->parent; @@ -1873,7 +2141,9 @@ static int parse_expr(request_rec *r, const char *expr, const char *error) case token_eq: case token_ne: #ifdef DEBUG_INCLUDE - ap_rputs(" Evaluate eq/ne\n", r); + 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) || @@ -1882,7 +2152,7 @@ static int parse_expr(request_rec *r, const char *expr, const char *error) ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, 0, r, "Invalid expression \"%s\" in file %s", expr, r->filename); - ap_rputs(error, r); + *was_error = 1; goto RETURN; } parse_string(r, current->left->token.value, @@ -1903,12 +2173,14 @@ static int parse_expr(request_rec *r, const char *expr, const char *error) ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, 0, r, "Invalid rexp \"%s\" in file %s", current->right->token.value, r->filename); - ap_rputs(error, r); + *was_error = 1; goto RETURN; } #ifdef DEBUG_INCLUDE - ap_rvputs(r, " Re Compare (", current->left->token.value, - ") with /", ¤t->right->token.value[1], "/\n", NULL); + debug_pos += sprintf (&debug[debug_pos], + " Re Compare (%s) with /%s/\n", + current->left->token.value, + ¤t->right->token.value[1]); #endif current->value = re_check(r, current->left->token.value, @@ -1916,8 +2188,10 @@ static int parse_expr(request_rec *r, const char *expr, const char *error) } else { #ifdef DEBUG_INCLUDE - ap_rvputs(r, " Compare (", current->left->token.value, - ") with (", current->right->token.value, ")\n", NULL); + 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, @@ -1927,8 +2201,8 @@ static int parse_expr(request_rec *r, const char *expr, const char *error) current->value = !current->value; } #ifdef DEBUG_INCLUDE - ap_rvputs(r, " Returning ", current->value ? "1" : "0", - "\n", NULL); + debug_pos += sprintf (&debug[debug_pos], " Returning %c\n", + current->value ? '1' : '0'); #endif current->done = 1; current = current->parent; @@ -1938,7 +2212,9 @@ static int parse_expr(request_rec *r, const char *expr, const char *error) case token_le: case token_lt: #ifdef DEBUG_INCLUDE - ap_rputs(" Evaluate ge/gt/le/lt\n", r); + 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) || @@ -1947,7 +2223,7 @@ static int parse_expr(request_rec *r, const char *expr, const char *error) ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, 0, r, "Invalid expression \"%s\" in file %s", expr, r->filename); - ap_rputs(error, r); + *was_error = 1; goto RETURN; } parse_string(r, current->left->token.value, @@ -1959,8 +2235,10 @@ static int parse_expr(request_rec *r, const char *expr, const char *error) apr_cpystrn(current->right->token.value, buffer, sizeof(current->right->token.value)); #ifdef DEBUG_INCLUDE - ap_rvputs(r, " Compare (", current->left->token.value, - ") with (", current->right->token.value, ")\n", NULL); + 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, @@ -1981,8 +2259,8 @@ static int parse_expr(request_rec *r, const char *expr, const char *error) current->value = 0; /* Don't return -1 if unknown token */ } #ifdef DEBUG_INCLUDE - ap_rvputs(r, " Returning ", current->value ? "1" : "0", - "\n", NULL); + debug_pos += sprintf (&debug[debug_pos], " Returning %c\n", + current->value ? '1' : '0'); #endif current->done = 1; current = current->parent; @@ -2000,8 +2278,8 @@ static int parse_expr(request_rec *r, const char *expr, const char *error) current->value = 0; } #ifdef DEBUG_INCLUDE - ap_rvputs(r, " Evaluate !: ", current->value ? "1" : "0", - "\n", NULL); + debug_pos += sprintf (&debug[debug_pos], " Evaluate !: %c\n", + current->value ? '1' : '0'); #endif current->done = 1; current = current->parent; @@ -2019,8 +2297,8 @@ static int parse_expr(request_rec *r, const char *expr, const char *error) current->value = 1; } #ifdef DEBUG_INCLUDE - ap_rvputs(r, " Evaluate (): ", current->value ? "1" : "0", - "\n", NULL); + debug_pos += sprintf (&debug[debug_pos], " Evaluate (): %c\n", + current->value ? '1' : '0'); #endif current->done = 1; current = current->parent; @@ -2030,20 +2308,20 @@ static int parse_expr(request_rec *r, const char *expr, const char *error) ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, 0, r, "Unmatched '(' in \"%s\" in file %s", expr, r->filename); - ap_rputs(error, r); + *was_error = 1; goto RETURN; case token_rbrace: ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, 0, r, "Unmatched ')' in \"%s\" in file %s", expr, r->filename); - ap_rputs(error, r); + *was_error = 1; goto RETURN; default: ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, 0, r, - "bad token type"); - ap_rputs(error, r); + "bad token type"); + *was_error = 1; goto RETURN; } } @@ -2054,259 +2332,360 @@ static int parse_expr(request_rec *r, const char *expr, const char *error) return (retval); } -static int handle_if(ap_bucket *in, request_rec *r, const char *error, - int *conditional_status, int *printing) -{ - char tag[MAX_STRING_LEN]; - char *tag_val; - char *expr; - apr_off_t offset = strlen("if ") + strlen(STARTING_SEQUENCE); - - expr = NULL; - while (1) { - tag_val = get_tag(r->pool, in, tag, sizeof(tag), 0, &offset); - if (*tag == '\0') { - return 1; - } - else if (!strcmp(tag, "done")) { - if (expr == NULL) { - ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, 0, r, - "missing expr in if statement: %s", - r->filename); - ap_rputs(error, r); - return 1; - } - *printing = *conditional_status = parse_expr(r, expr, error); +/*-------------------------------------------------------------------------*/ #ifdef DEBUG_INCLUDE - ap_rvputs(r, "**** if conditional_status=\"", - *conditional_status ? "1" : "0", "\"\n", NULL); + +#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"; \ + apr_ssize_t c_wrt; \ + \ + if (cntx->flags & FLAG_COND_TRUE) { \ + cont_txt[31] = '1'; \ + } \ + memcpy(&cond_txt[5], tag_text, sizeof(tag_text)); \ + t_buck = ap_bucket_create_heap(cond_txt, sizeof(cond_txt), 1, &c_wrt); \ + AP_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) \ +{ \ + apr_ssize_t b_wrt; \ + if (d_buf[0] != '\0') { \ + t_buck = ap_bucket_create_heap(d_buf, strlen(d_buf), 1, &b_wrt); \ + AP_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 - return 0; - } - else if (!strcmp(tag, "expr")) { - expr = tag_val; +/*-------------------------------------------------------------------------*/ + +/* pjr - These seem to allow expr="fred" expr="joe" where joe overwrites fred. */ +static int handle_if(include_ctx_t *ctx, request_rec *r, ap_bucket *head_ptr, + ap_bucket **inserted_head) +{ + char *tag = NULL; + char *tag_val = NULL; + char *expr = NULL; + int expr_ret, was_error, was_unmatched; + ap_bucket *tmp_buck; + char debug_buf[MAX_DEBUG_SIZE]; + + *inserted_head = NULL; + if (!ctx->flags & FLAG_PRINTING) { + ctx->if_nesting_level++; + } + else { + while (1) { + get_tag_and_value(ctx, &tag, &tag_val, 0); + if (tag == NULL) { + if (expr == NULL) { + ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, 0, r, + "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, 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 - ap_rvputs(r, "**** if expr=\"", expr, "\"\n", NULL); + if (1) { + apr_ssize_t d_len = 0, d_wrt = 0; + d_len = sprintf(debug_buf, "**** if expr=\"%s\"\n", expr); + tmp_buck = ap_bucket_create_heap(debug_buf, d_len, 1, &d_wrt); + AP_BUCKET_INSERT_BEFORE(head_ptr, tmp_buck); + + if (*inserted_head == NULL) { + *inserted_head = tmp_buck; + } + } #endif - } - else { - ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, 0, r, - "unknown parameter \"%s\" to tag if in %s", - tag, r->filename); - ap_rputs(error, r); + } + else { + ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|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 0; } -static int handle_elif(ap_bucket *in, request_rec *r, const char *error, - int *conditional_status, int *printing) +static int handle_elif(include_ctx_t *ctx, request_rec *r, ap_bucket *head_ptr, + ap_bucket **inserted_head) { - char tag[MAX_STRING_LEN]; - char *tag_val; - char *expr; - apr_off_t offset = strlen("elif ") + strlen(STARTING_SEQUENCE); - - expr = NULL; - while (1) { - tag_val = get_tag(r->pool, in, tag, sizeof(tag), 0, &offset); - if (*tag == '\0') { - return 1; - } - else if (!strcmp(tag, "done")) { -#ifdef DEBUG_INCLUDE - ap_rvputs(r, "**** elif conditional_status=\"", - *conditional_status ? "1" : "0", "\"\n", NULL); -#endif - if (*conditional_status) { - *printing = 0; + char *tag = NULL; + char *tag_val = NULL; + char *expr = NULL; + int expr_ret, was_error, was_unmatched; + ap_bucket *tmp_buck; + char debug_buf[MAX_DEBUG_SIZE]; + + *inserted_head = NULL; + if (!ctx->if_nesting_level) { + while (1) { + 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_NOERRNO|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, 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); } - if (expr == NULL) { - ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, 0, r, - "missing expr in elif statement: %s", - r->filename); - ap_rputs(error, r); - return 1; - } - *printing = *conditional_status = parse_expr(r, expr, error); -#ifdef DEBUG_INCLUDE - ap_rvputs(r, "**** elif conditional_status=\"", - *conditional_status ? "1" : "0", "\"\n", NULL); -#endif - return 0; - } - else if (!strcmp(tag, "expr")) { - expr = tag_val; + else if (!strcmp(tag, "expr")) { + expr = tag_val; #ifdef DEBUG_INCLUDE - ap_rvputs(r, "**** if expr=\"", expr, "\"\n", NULL); + if (1) { + apr_ssize_t d_len = 0, d_wrt = 0; + d_len = sprintf(debug_buf, "**** elif expr=\"%s\"\n", expr); + tmp_buck = ap_bucket_create_heap(debug_buf, d_len, 1, &d_wrt); + AP_BUCKET_INSERT_BEFORE(head_ptr, tmp_buck); + + if (*inserted_head == NULL) { + *inserted_head = tmp_buck; + } + } #endif - } - else { - ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, 0, r, - "unknown parameter \"%s\" to tag if in %s", - tag, r->filename); - ap_rputs(error, r); + } + else { + ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|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 0; } -static int handle_else(ap_bucket *in, request_rec *r, const char *error, - int *conditional_status, int *printing) +static int handle_else(include_ctx_t *ctx, request_rec *r, ap_bucket *head_ptr, + ap_bucket **inserted_head) { - char tag[MAX_STRING_LEN]; - apr_off_t offset = strlen("else ") + strlen(STARTING_SEQUENCE); - - if (!get_tag(r->pool, in, tag, sizeof(tag), 1, &offset)) { - return 1; - } - else if (!strcmp(tag, "done")) { -#ifdef DEBUG_INCLUDE - ap_rvputs(r, "**** else conditional_status=\"", - *conditional_status ? "1" : "0", "\"\n", NULL); -#endif - *printing = !(*conditional_status); - *conditional_status = 1; - return 0; - } - else { - ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, 0, r, - "else directive does not take tags in %s", - r->filename); - if (*printing) { - ap_rputs(error, r); + char *tag = NULL; + char *tag_val = NULL; + ap_bucket *tmp_buck; + + *inserted_head = NULL; + if (!ctx->if_nesting_level) { + get_tag_and_value(ctx, &tag, &tag_val, 1); + if ((tag != NULL) || (tag_val != NULL)) { + ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, 0, r, + "else directive does not take tags in %s", r->filename); + 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 -1; } + return 0; } -static int handle_endif(ap_bucket *in, request_rec *r, const char *error, - int *conditional_status, int *printing) +static int handle_endif(include_ctx_t *ctx, request_rec *r, ap_bucket *head_ptr, + ap_bucket **inserted_head) { - char tag[MAX_STRING_LEN]; - apr_off_t offset = strlen("endif ") + strlen(STARTING_SEQUENCE); - - if (!get_tag(r->pool, in, tag, sizeof(tag), 1, &offset)) { - return 1; - } - else if (!strcmp(tag, "done")) { -#ifdef DEBUG_INCLUDE - ap_rvputs(r, "**** endif conditional_status=\"", - *conditional_status ? "1" : "0", "\"\n", NULL); -#endif - *printing = 1; - *conditional_status = 1; - return 0; + char *tag = NULL; + char *tag_val = NULL; + ap_bucket *tmp_buck; + + *inserted_head = NULL; + if (!ctx->if_nesting_level) { + get_tag_and_value(ctx, &tag, &tag_val, 1); + if ((tag != NULL) || (tag_val != NULL)) { + ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, 0, r, + "endif directive does not take tags in %s", r->filename); + 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 { - ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, 0, r, - "endif directive does not take tags in %s", - r->filename); - ap_rputs(error, r); - return -1; + ctx->if_nesting_level--; + return 0; } } -static int handle_set(ap_bucket *in, request_rec *r, const char *error) +static int handle_set(include_ctx_t *ctx, request_rec *r, ap_bucket *head_ptr, + ap_bucket **inserted_head) { - char tag[MAX_STRING_LEN]; + char *tag = NULL; + char *tag_val = NULL; + char *var = NULL; + ap_bucket *tmp_buck; char parsed_string[MAX_STRING_LEN]; - char *tag_val; - char *var; - apr_off_t offset = strlen("set ") + strlen(STARTING_SEQUENCE); - var = (char *) NULL; - while (1) { - if (!(tag_val = get_tag(r->pool, in, tag, sizeof(tag), 1, &offset))) { - return 1; - } - else if (!strcmp(tag, "done")) { - return 0; - } - else if (!strcmp(tag, "var")) { - var = tag_val; - } - else if (!strcmp(tag, "value")) { - if (var == (char *) NULL) { + *inserted_head = NULL; + if (ctx->flags & FLAG_PRINTING) { + while (1) { + 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 = tag_val; + } + else if (!strcmp(tag, "value")) { + if (var == (char *) NULL) { + ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|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); + } + parse_string(r, tag_val, parsed_string, sizeof(parsed_string), 0); + apr_table_setn(r->subprocess_env, apr_pstrdup(r->pool, var), + apr_pstrdup(r->pool, parsed_string)); + } + else { ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, 0, r, - "variable must precede value in set directive in %s", - r->filename); - ap_rputs(error, r); + "Invalid tag for set directive in %s", r->filename); + CREATE_ERROR_BUCKET(ctx, tmp_buck, head_ptr, *inserted_head); return -1; } - parse_string(r, tag_val, parsed_string, sizeof(parsed_string), 0); - apr_table_setn(r->subprocess_env, var, apr_pstrdup(r->pool, parsed_string)); - } - else { - ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, 0, r, - "Invalid tag for set directive in %s", r->filename); - ap_rputs(error, r); - return -1; } } + return 0; } -static int handle_printenv(ap_bucket *in, request_rec *r, const char *error) +static int handle_printenv(include_ctx_t *ctx, request_rec *r, ap_bucket *head_ptr, + ap_bucket **inserted_head) { - char tag[MAX_STRING_LEN]; - char *tag_val; - apr_array_header_t *arr = apr_table_elts(r->subprocess_env); - apr_table_entry_t *elts = (apr_table_entry_t *)arr->elts; - int i; - apr_off_t offset = strlen("printenv ") + strlen(STARTING_SEQUENCE); - - if (!(tag_val = get_tag(r->pool, in, tag, sizeof(tag), 1, &offset))) { - return 1; - } - else if (!strcmp(tag, "done")) { - for (i = 0; i < arr->nelts; ++i) { - ap_rvputs(r, ap_escape_html(r->pool, elts[i].key), "=", - ap_escape_html(r->pool, elts[i].val), "\n", NULL); + char *tag = NULL; + char *tag_val = NULL; + ap_bucket *tmp_buck; + + if (ctx->flags & FLAG_PRINTING) { + get_tag_and_value(ctx, &tag, &tag_val, 1); + if ((tag == NULL) && (tag_val == NULL)) { + apr_array_header_t *arr = apr_table_elts(r->subprocess_env); + apr_table_entry_t *elts = (apr_table_entry_t *)arr->elts; + int i; + char *key_text, *val_text; + apr_ssize_t k_len, v_len, t_wrt; + + *inserted_head = NULL; + for (i = 0; i < arr->nelts; ++i) { + key_text = ap_escape_html(r->pool, elts[i].key); + val_text = ap_escape_html(r->pool, elts[i].val); + k_len = strlen(key_text); + v_len = strlen(val_text); + + /* Key_text */ + tmp_buck = ap_bucket_create_heap(key_text, k_len, 1, &t_wrt); + AP_BUCKET_INSERT_BEFORE(head_ptr, tmp_buck); + if (*inserted_head == NULL) { + *inserted_head = tmp_buck; + } + /* = */ + tmp_buck = ap_bucket_create_immortal("=", 1); + AP_BUCKET_INSERT_BEFORE(head_ptr, tmp_buck); + /* Value_text */ + tmp_buck = ap_bucket_create_heap(val_text, v_len, 1, &t_wrt); + AP_BUCKET_INSERT_BEFORE(head_ptr, tmp_buck); + /* newline... */ + tmp_buck = ap_bucket_create_immortal("\n", 1); + AP_BUCKET_INSERT_BEFORE(head_ptr, tmp_buck); + } + return 0; + } + else { + ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|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; - } - else { - ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, 0, r, - "printenv directive does not take tags in %s", - r->filename); - ap_rputs(error, r); - return -1; } + return 0; } - /* -------------------------- The main function --------------------------- */ -/* This is a stub which parses a file descriptor. */ - -typedef struct include_ctx { - ap_bucket_brigade *bb; -} include_ctx; static void send_parsed_content(ap_bucket_brigade **bb, request_rec *r, ap_filter_t *f) { - char directive[MAX_STRING_LEN], error[MAX_STRING_LEN]; - char timefmt[MAX_STRING_LEN]; - int noexec = ap_allow_options(r) & OPT_INCNOEXEC; - int sizefmt; - int if_nesting; - int printing; - int conditional_status; + include_ctx_t *ctx = f->ctx; ap_bucket *dptr = AP_BRIGADE_FIRST(*bb); - ap_bucket *tagbuck, *dptr2; - ap_bucket *endsec; + ap_bucket *tmp_dptr; ap_bucket_brigade *tag_and_after; - include_ctx *ctx; int ret; - apr_cpystrn(error, DEFAULT_ERROR_MSG, sizeof(error)); - apr_cpystrn(timefmt, DEFAULT_TIME_FORMAT, sizeof(timefmt)); - sizefmt = SIZEFMT_KMG; - -/* Turn printing on */ - printing = conditional_status = 1; - if_nesting = 0; - ap_chdir_file(r->filename); if (r->args) { /* add QUERY stuff to env cause it ain't yet */ char *arg_copy = apr_pstrdup(r->pool, r->args); @@ -2317,128 +2696,303 @@ static void send_parsed_content(ap_bucket_brigade **bb, request_rec *r, ap_escape_shell_cmd(r->pool, arg_copy)); } - if (!f->ctx) { - f->ctx = ctx = apr_pcalloc(r->pool, sizeof(f->ctx)); - ctx->bb = ap_brigade_create(r->pool); - } - else { - ctx = f->ctx; - AP_BRIGADE_CONCAT(*bb, ctx->bb); - } + while (dptr != AP_BRIGADE_SENTINEL(*bb)) { + /* State to check for the STARTING_SEQUENCE. */ + if ((ctx->state == PRE_HEAD) || (ctx->state == PARSE_HEAD)) { + int do_cleanup = 0; + apr_ssize_t cleanup_bytes = ctx->parse_pos; - AP_BRIGADE_FOREACH(dptr, *bb) { - if ((tagbuck = find_string(dptr, STARTING_SEQUENCE, AP_BRIGADE_LAST(*bb))) != NULL) { - dptr2 = tagbuck; - dptr = tagbuck; - endsec = find_string(dptr2, ENDING_SEQUENCE, AP_BRIGADE_LAST(*bb)); - if (endsec == NULL) { - ap_save_brigade(f, &ctx->bb, bb); - } - - /* At this point, everything between tagbuck and endsec is an SSI - * directive, we just have to deal with it now. + tmp_dptr = find_start_sequence(dptr, ctx, *bb, &do_cleanup); + + /* The few bytes stored in the ssi_tag_brigade turned out not to + * be a tag after all. This can only happen if the starting + * tag actually spans brigades. This should be very rare. */ - if (get_directive(tagbuck, directive, sizeof(directive), r->pool)) { - ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, 0, r, - "mod_include: error reading directive in %s", - r->filename); - ap_rputs(error, r); - return; - } - tag_and_after = ap_brigade_split(*bb, dptr); - ap_pass_brigade(f->next, *bb); /* process what came before the tag */ - *bb = tag_and_after; - if (!strcmp(directive, "if")) { - if (!printing) { - if_nesting++; - } - else { - ret = handle_if(tagbuck, r, error, &conditional_status, - &printing); - if_nesting = 0; - } - continue; - } - else if (!strcmp(directive, "else")) { - if (!if_nesting) { - ret = handle_else(tagbuck, r, error, &conditional_status, - &printing); + if ((do_cleanup) && (!AP_BRIGADE_EMPTY(ctx->ssi_tag_brigade))) { + ap_bucket *tmp_bkt; + + tmp_bkt = ap_bucket_create_immortal(STARTING_SEQUENCE, cleanup_bytes); + AP_BRIGADE_INSERT_HEAD(*bb, tmp_bkt); + + while (!AP_BRIGADE_EMPTY(ctx->ssi_tag_brigade)) { + tmp_bkt = AP_BRIGADE_FIRST(ctx->ssi_tag_brigade); + AP_BUCKET_REMOVE(tmp_bkt); + ap_bucket_destroy(tmp_bkt); } - continue; } - else if (!strcmp(directive, "elif")) { - if (!if_nesting) { - ret = handle_elif(tagbuck, r, error, &conditional_status, - &printing); + + /* If I am inside a conditional (if, elif, else) that is false + * then I need to throw away anything contained in it. + */ + if ((!(ctx->flags & FLAG_PRINTING)) && (tmp_dptr != NULL) && + (dptr != AP_BRIGADE_SENTINEL(*bb))) { + while ((dptr != AP_BRIGADE_SENTINEL(*bb)) && + (dptr != tmp_dptr)) { + ap_bucket *free_bucket = dptr; + + dptr = AP_BUCKET_NEXT (dptr); + AP_BUCKET_REMOVE(free_bucket); + ap_bucket_destroy(free_bucket); } - continue; } - else if (!strcmp(directive, "endif")) { - if (!if_nesting) { - ret = handle_endif(tagbuck, r, error, &conditional_status, - &printing); + + /* Adjust the current bucket position based on what was found... */ + if ((tmp_dptr != NULL) && (ctx->state == PARSE_TAG)) { + if (ctx->tag_start_bucket != NULL) { + dptr = ctx->tag_start_bucket; } else { - if_nesting--; + dptr = AP_BRIGADE_SENTINEL(*bb); + } + } + else if (tmp_dptr == NULL) { /* There was no possible SSI tag in the */ + dptr = AP_BRIGADE_SENTINEL(*bb); /* remainder of this brigade... */ + } + } + + /* State to check for the ENDING_SEQUENCE. */ + if (((ctx->state == PARSE_TAG) || (ctx->state == PARSE_TAIL)) && + (dptr != AP_BRIGADE_SENTINEL(*bb))) { + tmp_dptr = find_end_sequence(dptr, ctx, *bb); + + if (tmp_dptr != NULL) { + dptr = tmp_dptr; /* Adjust bucket pos... */ + + /* If some of the tag has already been set aside then set + * aside remainder of tag. Now the full tag is in ssi_tag_brigade. + * If none has yet been set aside, then leave it all where it is. + * In any event after this the entire set of tag buckets will be + * in one place or another. + */ + if (!AP_BRIGADE_EMPTY(ctx->ssi_tag_brigade)) { + tag_and_after = ap_brigade_split(*bb, dptr); + AP_BRIGADE_CONCAT(ctx->ssi_tag_brigade, *bb); + *bb = tag_and_after; } - continue; } - if (!printing) { - continue; + else { + dptr = AP_BRIGADE_SENTINEL(*bb); /* remainder of this brigade... */ } - if (!strcmp(directive, "exec")) { - if (noexec) { - ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, 0, r, - "exec used but not allowed in %s", - r->filename); - if (printing) { - ap_rputs(error, r); + } + + /* State to processed the directive... */ + if (ctx->state == PARSED) { + ap_bucket *content_head = NULL, *tmp_bkt; + char tmp_buf[TMP_BUF_SIZE]; + char *directive_str = NULL; + dir_token_id directive_token; + + /* By now the full tag (all buckets) should either be set aside into + * ssi_tag_brigade or contained within the current bb. All tag + * processing from here on can assume that. + */ + + /* At this point, everything between ctx->head_start_bucket and + * ctx->tail_start_bucket is an SSI + * directive, we just have to deal with it now. + */ + if (get_combined_directive(ctx, r, *bb, tmp_buf, + TMP_BUF_SIZE) != APR_SUCCESS) { + ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, 0, r, + "mod_include: error copying directive in %s", + r->filename); + CREATE_ERROR_BUCKET(ctx, tmp_bkt, dptr, content_head); + + /* DO CLEANUP HERE!!!!! */ + tmp_dptr = ctx->head_start_bucket; + if (!AP_BRIGADE_EMPTY(ctx->ssi_tag_brigade)) { + while (!AP_BRIGADE_EMPTY(ctx->ssi_tag_brigade)) { + tmp_bkt = AP_BRIGADE_FIRST(ctx->ssi_tag_brigade); + AP_BUCKET_REMOVE(tmp_bkt); + ap_bucket_destroy(tmp_bkt); } } else { - ret = handle_exec(tagbuck, r, error, f->next); + do { + tmp_bkt = tmp_dptr; + tmp_dptr = AP_BUCKET_NEXT (tmp_dptr); + AP_BUCKET_REMOVE(tmp_bkt); + ap_bucket_destroy(tmp_bkt); + } while ((tmp_dptr != dptr) && + (tmp_dptr != AP_BRIGADE_SENTINEL(*bb))); } + + return; } - else if (!strcmp(directive, "config")) { - ret = handle_config(tagbuck, r, error, timefmt, &sizefmt); - } - else if (!strcmp(directive, "set")) { - ret = handle_set(tagbuck, r, error); - } - else if (!strcmp(directive, "include")) { - ret = handle_include(tagbuck, r, f->next, error, noexec); - } - else if (!strcmp(directive, "echo")) { - ret = handle_echo(tagbuck, r, error); - } - else if (!strcmp(directive, "fsize")) { - ret = handle_fsize(tagbuck, r, error, sizefmt); - } - else if (!strcmp(directive, "flastmod")) { - ret = handle_flastmod(tagbuck, r, error, timefmt); - } - else if (!strcmp(directive, "printenv")) { - ret = handle_printenv(tagbuck, r, error); - } -#ifdef USE_PERL_SSI - else if (!strcmp(directive, "perl")) { - ret = handle_perl(tagbuck, r, error); - } + + /* Even if I don't generate any content, I know at this point that + * I will at least remove the discovered SSI tag, thereby making + * the content shorter than it was. This is the safest point I can + * find to unset this field. + */ + apr_table_unset(f->r->headers_out, "Content-Length"); + + /* Can't destroy the tag buckets until I'm done processing + * because the combined_tag might just be pointing to + * the contents of a single bucket! + */ + directive_str = get_directive(ctx, &directive_token); + + switch (directive_token) { + case TOK_IF: + ret = handle_if(ctx, r, dptr, &content_head); + break; + case TOK_ELSE: + ret = handle_else(ctx, r, dptr, &content_head); + break; + case TOK_ELIF: + ret = handle_elif(ctx, r, dptr, &content_head); + break; + case TOK_ENDIF: + ret = handle_endif(ctx, r, dptr, &content_head); + break; + case TOK_EXEC: + ret = handle_exec(ctx, bb, r, f, dptr, &content_head); + break; + case TOK_INCLUDE: + ret = handle_include(ctx, bb, r, f, dptr, &content_head); + break; +#ifdef USE_PERL_SSI /* Leaving this as is for now... */ + case TOK_PERL: + SPLIT_AND_PASS_PRETAG_BUCKETS(*bb, ctx); + ret = handle_perl(ctx, r); + break; #endif - else { + case TOK_SET: + ret = handle_set(ctx, r, dptr, &content_head); + break; + case TOK_ECHO: + ret = handle_echo(ctx, r, dptr, &content_head); + break; + case TOK_FSIZE: + ret = handle_fsize(ctx, r, dptr, &content_head); + break; + case TOK_CONFIG: + ret = handle_config(ctx, r, dptr, &content_head); + break; + case TOK_FLASTMOD: + ret = handle_flastmod(ctx, r, dptr, &content_head); + break; + case TOK_PRINTENV: + ret = handle_printenv(ctx, r, dptr, &content_head); + break; + + case TOK_UNKNOWN: + default: ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, 0, r, - "unknown directive \"%s\" " - "in parsed doc %s", - directive, r->filename); - if (printing) { - ap_rputs(error, r); + "unknown directive \"%s\" in parsed doc %s", + directive_str, r->filename); + CREATE_ERROR_BUCKET(ctx, tmp_bkt, dptr, content_head); + } + + /* This chunk of code starts at the first bucket in the chain + * of tag buckets (assuming that by this point the bucket for + * the STARTING_SEQUENCE has been split) and loops through to + * the end of the tag buckets freeing them all. + * + * Remember that some part of this may have been set aside + * into the ssi_tag_brigade and the remainder (possibly as + * little as one byte) will be in the current brigade. + * + * The value of dptr should have been set during the + * PARSE_TAIL state to the first bucket after the + * ENDING_SEQUENCE. + * + * The value of content_head may have been set during processing + * of the directive. If so, the content was inserted in front + * of the dptr bucket. The inserted buckets should not be thrown + * away here, but they should also not be parsed later. + */ + if (content_head == NULL) { + content_head = dptr; + } + tmp_dptr = ctx->head_start_bucket; + if (!AP_BRIGADE_EMPTY(ctx->ssi_tag_brigade)) { + while (!AP_BRIGADE_EMPTY(ctx->ssi_tag_brigade)) { + tmp_bkt = AP_BRIGADE_FIRST(ctx->ssi_tag_brigade); + AP_BUCKET_REMOVE(tmp_bkt); + ap_bucket_destroy(tmp_bkt); + } + } + else { + do { + tmp_bkt = tmp_dptr; + tmp_dptr = AP_BUCKET_NEXT (tmp_dptr); + AP_BUCKET_REMOVE(tmp_bkt); + ap_bucket_destroy(tmp_bkt); + } while ((tmp_dptr != content_head) && + (tmp_dptr != AP_BRIGADE_SENTINEL(*bb))); + } + if (ctx->combined_tag == tmp_buf) { + memset (ctx->combined_tag, '\0', ctx->tag_length); + ctx->combined_tag = NULL; + } + + /* Don't reset the flags or the nesting level!!! */ + ctx->parse_pos = 0; + ctx->head_start_bucket = NULL; + ctx->head_start_index = 0; + ctx->tag_start_bucket = NULL; + ctx->tag_start_index = 0; + ctx->tail_start_bucket = NULL; + ctx->tail_start_index = 0; + ctx->curr_tag_pos = NULL; + ctx->tag_length = 0; + + if (!AP_BRIGADE_EMPTY(ctx->ssi_tag_brigade)) { + while (!AP_BRIGADE_EMPTY(ctx->ssi_tag_brigade)) { + tmp_bkt = AP_BRIGADE_FIRST(ctx->ssi_tag_brigade); + AP_BUCKET_REMOVE(tmp_bkt); + ap_bucket_destroy(tmp_bkt); } } - *bb = ap_brigade_split(tag_and_after, endsec); - dptr = AP_BUCKET_PREV(endsec); + + ctx->state = PRE_HEAD; } - else { - return; + } + + /* If I am in the middle of parsing an SSI tag then I need to set aside + * the pertinent trailing buckets and pass on the initial part of the + * brigade. The pertinent parts of the next brigades will be added to + * these set aside buckets to form the whole tag and will be processed + * once the whole tag has been found. + */ + if (ctx->state == PRE_HEAD) { + /* Inside a false conditional (if, elif, else), so toss it all... */ + if ((dptr != AP_BRIGADE_SENTINEL(*bb)) && + (!(ctx->flags & FLAG_PRINTING))) { + ap_bucket *free_bucket; + do { + free_bucket = dptr; + dptr = AP_BUCKET_NEXT (dptr); + AP_BUCKET_REMOVE(free_bucket); + ap_bucket_destroy(free_bucket); + } while (dptr != AP_BRIGADE_SENTINEL(*bb)); + } + else { /* Otherwise pass it along... */ + ap_pass_brigade(f->next, *bb); /* No SSI tags in this brigade... */ + } + } + else if (ctx->state == PARSED) { /* Invalid internal condition... */ + ap_bucket *content_head, *tmp_bkt; + ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, 0, r, + "Invalid mod_include state during file %s", r->filename); + CREATE_ERROR_BUCKET(ctx, tmp_bkt, AP_BRIGADE_FIRST(*bb), content_head); + } + else { /* Entire brigade is middle chunk of SSI tag... */ + if (!AP_BRIGADE_EMPTY(ctx->ssi_tag_brigade)) { + AP_BRIGADE_CONCAT(ctx->ssi_tag_brigade, *bb); + } + else { /* End of brigade contains part of SSI tag... */ + if (ctx->head_start_index > 0) { + ap_bucket_split(ctx->head_start_bucket, ctx->head_start_index); + ctx->head_start_bucket = AP_BUCKET_NEXT(ctx->head_start_bucket); + ctx->head_start_index = 0; + } + /* Set aside tag, pass pre-tag... */ + tag_and_after = ap_brigade_split(*bb, ctx->head_start_bucket); + ap_save_brigade(f, &ctx->ssi_tag_brigade, &tag_and_after); + ap_pass_brigade(f->next, *bb); } } } @@ -2490,6 +3044,7 @@ static const char *set_xbithack(cmd_parms *cmd, void *xbp, const char *arg) static int includes_filter(ap_filter_t *f, ap_bucket_brigade *b) { request_rec *r = f->r; + include_ctx_t *ctx = f->ctx; enum xbithack *state = (enum xbithack *) ap_get_module_config(r->per_dir_config, &includes_module); request_rec *parent; @@ -2502,6 +3057,26 @@ static int includes_filter(ap_filter_t *f, ap_bucket_brigade *b) return ap_pass_brigade(f->next, b); } + if (!f->ctx) { + f->ctx = ctx = apr_pcalloc(f->c->pool, sizeof(*ctx)); + if (ctx != NULL) { + ctx->state = PRE_HEAD; + ctx->flags = (FLAG_PRINTING | FLAG_COND_TRUE); + if (ap_allow_options(r) & OPT_INCNOEXEC) { + ctx->flags |= FLAG_NO_EXEC; + } + ctx->ssi_tag_brigade = ap_brigade_create(f->c->pool); + + apr_cpystrn(ctx->error_str, DEFAULT_ERROR_MSG, sizeof(ctx->error_str)); + apr_cpystrn(ctx->time_str, DEFAULT_TIME_FORMAT, sizeof(ctx->time_str)); + ctx->error_length = strlen(ctx->error_str); + } + else { + ap_pass_brigade(f->next, b); + return APR_ENOMEM; + } + } + if ((*state == xbithack_full) #if !defined(OS2) && !defined(WIN32) /* OS/2 dosen't support Groups. */ @@ -2538,8 +3113,8 @@ static int includes_filter(ap_filter_t *f, ap_bucket_brigade *b) * fix this, except to put alarm support into BUFF. -djg */ + send_parsed_content(&b, r, f); - ap_pass_brigade(f->next, b); if (parent) { /* signify that the sub request should not be killed */ @@ -2570,10 +3145,6 @@ module AP_MODULE_DECLARE_DATA includes_module = NULL, /* server config */ NULL, /* merge server config */ includes_cmds, /* command apr_table_t */ -#if 0 - includes_handlers, /* handlers */ -#else NULL, /* handlers */ -#endif register_hooks /* register hooks */ }; diff --git a/modules/filters/mod_include.h b/modules/filters/mod_include.h new file mode 100644 index 0000000000..554d9998bb --- /dev/null +++ b/modules/filters/mod_include.h @@ -0,0 +1,206 @@ +/* ==================================================================== + * The Apache Software License, Version 1.1 + * + * Copyright (c) 2000 The Apache Software Foundation. All rights + * reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. The end-user documentation included with the redistribution, + * if any, must include the following acknowledgment: + * "This product includes software developed by the + * Apache Software Foundation (http://www.apache.org/)." + * Alternately, this acknowledgment may appear in the software itself, + * if and wherever such third-party acknowledgments normally appear. + * + * 4. The names "Apache" and "Apache Software Foundation" must + * not be used to endorse or promote products derived from this + * software without prior written permission. For written + * permission, please contact apache@apache.org. + * + * 5. Products derived from this software may not be called "Apache", + * nor may "Apache" appear in their name, without prior written + * permission of the Apache Software Foundation. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR + * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF + * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + * Portions of this software are based upon public domain software + * originally written at the National Center for Supercomputing Applications, + * University of Illinois, Urbana-Champaign. + */ + +#ifndef _MOD_INCLUDE_H +#define _MOD_INCLUDE_H 1 + + + +#define STARTING_SEQUENCE "" + +#define DEFAULT_ERROR_MSG "[an error occurred while processing this directive]" +#define DEFAULT_TIME_FORMAT "%A, %d-%b-%Y %H:%M:%S %Z" +#define SIZEFMT_BYTES 0 +#define SIZEFMT_KMG 1 +#define TMP_BUF_SIZE 1024 +#ifdef CHARSET_EBCDIC +#define RAW_ASCII_CHAR(ch) apr_xlate_conv_byte(ap_hdrs_from_ascii, (unsigned char)ch) +#else /*CHARSET_EBCDIC*/ +#define RAW_ASCII_CHAR(ch) (ch) +#endif /*CHARSET_EBCDIC*/ + +module AP_MODULE_DECLARE_DATA includes_module; + +/* just need some arbitrary non-NULL pointer which can't also be a request_rec */ +#define NESTED_INCLUDE_MAGIC (&includes_module) + +/* TODO: changing directory should be handled by CreateProcess */ +#define ap_chdir_file(x) do {} while(0) + + +/**************************************************************************** + * Used to keep context information during parsing of a request for SSI tags. + * This is especially useful if the tag stretches across multiple buckets or + * brigades. This keeps track of which buckets need to be replaced with the + * content generated by the SSI tag. + * + * state: PRE_HEAD - State prior to finding the first character of the + * STARTING_SEQUENCE. Next state is PARSE_HEAD. + * PARSE_HEAD - State entered once the first character of the + * STARTING_SEQUENCE is found and exited when the + * the full STARTING_SEQUENCE has been matched or + * a match failure occurs. Next state is PRE_HEAD + * or PARSE_TAG. + * PARSE_TAG - State entered once the STARTING sequence has been + * matched. It is exited when the first character in + * ENDING_SEQUENCE is found. Next state is PARSE_TAIL. + * PARSE_TAIL - State entered from PARSE_TAG state when the first + * character in ENDING_SEQUENCE is encountered. This + * state is exited when the ENDING_SEQUENCE has been + * completely matched, or when a match failure occurs. + * Next state is PARSE_TAG or PARSED. + * PARSED - State entered from PARSE_TAIL once the complete + * ENDING_SEQUENCE has been matched. The SSI tag is + * processed and the SSI buckets are replaced with the + * SSI content during this state. + * parse_pos: Current matched position within the STARTING_SEQUENCE or + * ENDING_SEQUENCE during the PARSE_HEAD and PARSE_TAIL states. + * This is especially useful when the sequence spans brigades. + * X_start_bucket: These point to the buckets containing the first character + * of the STARTING_SEQUENCE, the first non-whitespace + * character of the tag, and the first character in the + * ENDING_SEQUENCE (head_, tag_, and tail_ respectively). + * The buckets are kept intact until the PARSED state is + * reached, at which time the tag is consolidated and the + * buckets are released. The buckets that these point to + * have all been set aside in the ssi_tag_brigade (along + * with all of the intervening buckets). + * X_start_index: The index points within the specified bucket contents + * where the first character of the STARTING_SEQUENCE, + * the first non-whitespace character of the tag, and the + * first character in the ENDING_SEQUENCE can be found + * (head_, tag_, and tail_ respectively). + * combined_tag: Once the PARSED state is reached the tag is collected from + * the bucket(s) in the ssi_tag_brigade into this contiguous + * buffer. The buckets in the ssi_tag_brigade are released + * and the tag is processed. + * curr_tag_pos: Ptr to the combined_tag buffer indicating the current + * parse position. + * tag_length: The number of bytes in the actual tag (excluding the + * STARTING_SEQUENCE, leading and trailing whitespace, + * and ENDING_SEQUENCE). This length is computed as the + * buckets are parsed and set aside during the PARSE_TAG state. + * ssi_tag_brigade: The temporary brigade used by this filter to set aside + * the buckets containing parts of the ssi tag and headers. + */ +typedef enum {PRE_HEAD, PARSE_HEAD, PARSE_TAG, PARSE_TAIL, PARSED} states; +typedef struct include_filter_ctx { + states state; + long flags; /* See the FLAG_XXXXX definitions. */ + int if_nesting_level; + apr_ssize_t parse_pos; + + ap_bucket *head_start_bucket; + apr_ssize_t head_start_index; + + ap_bucket *tag_start_bucket; + apr_ssize_t tag_start_index; + + ap_bucket *tail_start_bucket; + apr_ssize_t tail_start_index; + + char *combined_tag; + char *curr_tag_pos; + apr_ssize_t tag_length; + + apr_ssize_t error_length; + char error_str[MAX_STRING_LEN]; + char time_str[MAX_STRING_LEN]; + + ap_bucket_brigade *ssi_tag_brigade; +} include_ctx_t; + +/* These flags are used to set flag bits. */ +#define FLAG_PRINTING 0x00000001 /* Printing conditional lines. */ +#define FLAG_COND_TRUE 0x00000002 /* Conditional eval'd to true. */ +#define FLAG_SIZE_IN_BYTES 0x00000004 /* Sizes displayed in bytes. */ +#define FLAG_NO_EXEC 0x00000008 /* No Exec in current context. */ + +/* These flags are used to clear flag bits. */ +#define FLAG_SIZE_ABBREV 0xFFFFFFFB /* Reset SIZE_IN_BYTES bit. */ +#define FLAG_CLEAR_PRINT_COND 0xFFFFFFFC /* Reset PRINTING and COND_TRUE*/ +#define FLAG_CLEAR_PRINTING 0xFFFFFFFE /* Reset just PRINTING bit. */ + +typedef enum {TOK_UNKNOWN, TOK_IF, TOK_SET, TOK_ECHO, TOK_ELIF, TOK_ELSE, + TOK_EXEC, TOK_PERL, TOK_ENDIF, TOK_FSIZE, TOK_CONFIG, + TOK_INCLUDE, TOK_FLASTMOD, TOK_PRINTENV} dir_token_id; + +#define CREATE_ERROR_BUCKET(cntx, t_buck, h_ptr, ins_head) \ +{ \ + apr_ssize_t e_wrt; \ + t_buck = ap_bucket_create_heap(cntx->error_str, \ + ctx->error_length, 1, &e_wrt); \ + AP_BUCKET_INSERT_BEFORE(h_ptr, t_buck); \ + \ + if (ins_head == NULL) { \ + ins_head = t_buck; \ + } \ +} + +#define SPLIT_AND_PASS_PRETAG_BUCKETS(brgd, cntxt) \ +if ((AP_BRIGADE_EMPTY(cntxt->ssi_tag_brigade)) && \ + (cntxt->head_start_bucket != NULL)) { \ + ap_bucket_brigade *tag_plus; \ + \ + tag_plus = ap_brigade_split(brgd, cntxt->head_start_bucket); \ + ap_pass_brigade(f->next, brgd); \ + brgd = tag_plus; \ +} + +#endif /* MOD_INCLUDE */ -- 2.40.0