2 *) Backport major overhaul of mod_include's filter parser from 2.1.
3 The new parser code is expected to be more robust and should
4 catch all of the edge cases that were not handled by the previous one.
5 The 2.1 external API changes were hidden by a wrapper which is
6 expected to keep the API backwards compatible. [André Malo]
8 --- httpd-2.0.48/modules/filters/mod_include.h.include
9 +++ httpd-2.0.48/modules/filters/mod_include.h
11 * ssi_tag_brigade: The temporary brigade used by this filter to set aside
12 * the buckets containing parts of the ssi tag and headers.
14 -typedef enum {PRE_HEAD, PARSE_HEAD, PARSE_DIRECTIVE, PARSE_TAG, PARSE_TAIL, PARSED} states;
16 +/* I keep this stuff here, because of binary compat. It probably doesn't care,
17 + * but who knows ...?
19 +#ifdef MOD_INCLUDE_REDESIGN
20 +typedef enum {PRE_HEAD, BLOW_PARSE_HEAD, BLOW_PARSE_DIRECTIVE, PARSE_TAG,
21 + BLOW_PARSE_TAIL, PARSED} states;
23 +typedef enum {PRE_HEAD, PARSE_HEAD, PARSE_DIRECTIVE, PARSE_TAG, PARSE_TAIL,
27 /** forward referenced as it needs to be held on the context */
28 typedef struct bndm_t bndm_t;
29 --- httpd-2.0.48/modules/filters/mod_include.c.include
30 +++ httpd-2.0.48/modules/filters/mod_include.c
32 /* ====================================================================
33 * The Apache Software License, Version 1.1
35 - * Copyright (c) 2000-2003 The Apache Software Foundation. All rights
36 + * Copyright (c) 2000-2004 The Apache Software Foundation. All rights
39 * Redistribution and use in source and binary forms, with or without
41 #include "apr_optional.h"
43 #define APR_WANT_STRFUNC
44 +#define APR_WANT_MEMFUNC
49 #include "http_main.h"
50 #include "util_script.h"
51 #include "http_core.h"
53 +#define MOD_INCLUDE_REDESIGN
54 #include "mod_include.h"
55 #include "util_ebcdic.h"
59 } include_server_config;
61 +/* main parser states */
66 + PARSE_DIRECTIVE_POSTNAME,
67 + PARSE_DIRECTIVE_TAIL,
68 + PARSE_DIRECTIVE_POSTTAIL,
83 +typedef struct ssi_arg_item {
84 + struct ssi_arg_item *next;
86 + apr_size_t name_len;
88 + apr_size_t value_len;
92 + parse_state_t state;
95 + char quote; /* quote character value (or \0) */
97 + apr_bucket_brigade *tmp_bb;
99 + apr_size_t end_seq_len;
100 + char *directive; /* name of the current directive */
102 + unsigned argc; /* argument counter (of the current
105 + ssi_arg_item_t *argv; /* all arguments */
106 + ssi_arg_item_t *current_arg; /* currently parsed argument */
108 + include_ctx_t *ctx; /* public part of the context structure */
114 #define DEFAULT_XBITHACK xbithack_full
118 #define BYTE_COUNT_THRESHOLD AP_MIN_BYTES_TO_WRITE
120 +#define SSI_CREATE_ERROR_BUCKET(ctx, f, bb) APR_BRIGADE_INSERT_TAIL((bb), \
121 + apr_bucket_pool_create(apr_pstrdup((ctx)->pool, (ctx)->error_str), \
122 + strlen((ctx)->error_str), (ctx)->pool, \
123 + (f)->c->bucket_alloc))
125 /* ------------------------ Environment function -------------------------- */
127 /* Sentinel value to store in subprocess_env for items that
128 @@ -316,463 +376,6 @@
132 -/* We've now found a start sequence tag... */
133 -static apr_bucket* found_start_sequence(apr_bucket *dptr,
134 - include_ctx_t *ctx,
135 - apr_size_t tagStart,
138 - /* We want to split the bucket at the '<'. */
139 - ctx->state = PARSE_DIRECTIVE;
140 - ctx->tag_length = 0;
141 - ctx->parse_pos = 0;
143 - /* If tagStart indexes the end of the bucket, then tag_start_bucket
144 - * should be the next bucket
146 - if (tagStart < len) {
147 - ctx->tag_start_bucket = dptr;
148 - ctx->tag_start_index = tagStart;
151 - ctx->tag_start_bucket = APR_BUCKET_NEXT(dptr);
152 - ctx->tag_start_index = 0;
155 - if (ctx->head_start_index > 0) {
156 - apr_bucket *tmp_bkt;
158 - /* Split the bucket with the start of the tag in it */
159 - apr_bucket_split(ctx->head_start_bucket, ctx->head_start_index);
160 - tmp_bkt = APR_BUCKET_NEXT(ctx->head_start_bucket);
161 - /* If it was a one bucket match */
162 - if ((tagStart < len) && (dptr == ctx->head_start_bucket)) {
163 - ctx->tag_start_bucket = tmp_bkt;
164 - ctx->tag_start_index = tagStart - ctx->head_start_index;
166 - ctx->head_start_bucket = tmp_bkt;
167 - ctx->head_start_index = 0;
169 - return ctx->head_start_bucket;
172 -/* This function returns either a pointer to the split bucket containing the
173 - * first byte of the BEGINNING_SEQUENCE (after finding a complete match) or it
174 - * returns NULL if no match found.
176 -static apr_bucket *find_start_sequence(apr_bucket *dptr, include_ctx_t *ctx,
177 - apr_bucket_brigade *bb, int *do_cleanup)
182 - const char *str = ctx->start_seq ;
183 - apr_size_t slen = ctx->start_seq_len;
189 - apr_status_t rv = 0;
192 - if (APR_BUCKET_IS_EOS(dptr)) {
197 - /* XXX the bucket flush support is commented out for now
198 - * because it was causing a segfault */
199 - if (APR_BUCKET_IS_FLUSH(dptr)) {
200 - apr_bucket *old = dptr;
201 - dptr = APR_BUCKET_NEXT(old);
202 - APR_BUCKET_REMOVE(old);
203 - ctx->output_now = 1;
204 - ctx->output_flush = 1;
208 - if (ctx->bytes_parsed >= BYTE_COUNT_THRESHOLD) {
209 - ctx->output_now = 1;
211 - else if (ctx->bytes_parsed > 0) {
212 - rv = apr_bucket_read(dptr, &buf, &len, APR_NONBLOCK_READ);
214 - if (APR_STATUS_IS_EAGAIN(rv)) {
215 - ctx->output_now = 1;
219 - if (ctx->output_now) {
220 - apr_bucket *start_bucket;
221 - if (ctx->head_start_index > 0) {
222 - start_bucket = ctx->head_start_bucket;
223 - apr_bucket_split(start_bucket, ctx->head_start_index);
224 - start_bucket = APR_BUCKET_NEXT(start_bucket);
225 - ctx->head_start_index = 0;
226 - ctx->head_start_bucket = start_bucket;
227 - ctx->parse_pos = 0;
228 - ctx->state = PRE_HEAD;
231 - start_bucket = dptr;
233 - return start_bucket;
237 - rv = apr_bucket_read(dptr, &buf, &len, APR_BLOCK_READ);
239 - if (!APR_STATUS_IS_SUCCESS(rv)) {
244 - if (len == 0) { /* end of pipe? */
245 - dptr = APR_BUCKET_NEXT(dptr);
249 - /* Set our buffer to use. */
252 - /* The last bucket had a left over partial match that we need to
255 - if (ctx->state == PARSE_HEAD)
258 - tmpLen = (len < (slen - 1)) ? len : (slen - 1);
260 - while (c < buf + tmpLen && *c == str[ctx->parse_pos])
266 - if (str[ctx->parse_pos] == '\0')
268 - ctx->bytes_parsed += c - buf;
269 - return found_start_sequence(dptr, ctx, c - buf, len);
271 - else if (c == buf + tmpLen) {
272 - dptr = APR_BUCKET_NEXT(dptr);
278 - APR_BRIGADE_PREPEND(bb, ctx->ssi_tag_brigade);
280 - /* We know we are at the beginning of this bucket so
281 - * we can just prepend the saved bytes from the
282 - * ssi_tag_brigade (which empties the ssi_tag_brigade)
283 - * and continue processing.
284 - * We do not need to set do_cleanup beacuse the
285 - * prepend takes care of that.
287 - ctx->state = PRE_HEAD;
288 - ctx->head_start_bucket = NULL;
289 - ctx->head_start_index = 0;
294 - pos = bndm(str, slen, buf, len, ctx->start_seq_pat);
297 - ctx->head_start_bucket = dptr;
298 - ctx->head_start_index = pos;
299 - ctx->bytes_parsed += pos + slen;
300 - return found_start_sequence(dptr, ctx, pos + slen, len);
304 - /* Consider the case where we have <!-- at the end of the bucket. */
306 - ctx->bytes_parsed += (len - slen);
307 - c = buf + len - slen;
312 - ctx->parse_pos = 0;
314 - while (c < buf + len)
316 - if (*c == str[ctx->parse_pos]) {
317 - if (ctx->state == PRE_HEAD) {
318 - ctx->state = PARSE_HEAD;
319 - ctx->head_start_bucket = dptr;
320 - ctx->head_start_index = c - buf;
324 - ctx->bytes_parsed++;
326 - else if (ctx->parse_pos != 0)
328 - /* DO NOT INCREMENT c IN THIS BLOCK!
329 - * Don't increment bytes_parsed either.
330 - * This block is just to reset the indexes and
331 - * pointers related to parsing the tag start_sequence.
332 - * The value c needs to be checked again to handle
333 - * the case where we find "<<!--#". We are now
334 - * looking at the second "<" and need to restart
335 - * the start_sequence checking from parse_pos = 0.
336 - * do_cleanup causes the stored bytes in ssi_tag_brigade
337 - * to be forwarded on and cleaned up. We may not be
338 - * able to just prepend the ssi_tag_brigade because
339 - * we may have advanced too far before we noticed this
340 - * case, so just flag it and clean it up later.
343 - ctx->parse_pos = 0;
344 - ctx->state = PRE_HEAD;
345 - ctx->head_start_bucket = NULL;
346 - ctx->head_start_index = 0;
350 - ctx->bytes_parsed++;
353 - dptr = APR_BUCKET_NEXT(dptr);
354 - } while (dptr != APR_BRIGADE_SENTINEL(bb));
360 -static apr_bucket *find_end_sequence(apr_bucket *dptr, include_ctx_t *ctx,
361 - apr_bucket_brigade *bb)
366 - const char *str = ctx->end_seq;
370 - apr_status_t rv = 0;
373 - if (APR_BUCKET_IS_EOS(dptr)) {
377 - /* XXX the bucket flush support is commented out for now
378 - * because it was causing a segfault */
379 - if (APR_BUCKET_IS_FLUSH(dptr)) {
380 - apr_bucket *old = dptr;
381 - dptr = APR_BUCKET_NEXT(old);
382 - APR_BUCKET_REMOVE(old);
383 - ctx->output_now = 1;
384 - ctx->output_flush = 1;
388 - if (ctx->bytes_parsed >= BYTE_COUNT_THRESHOLD) {
389 - ctx->output_now = 1;
391 - else if (ctx->bytes_parsed > 0) {
392 - rv = apr_bucket_read(dptr, &buf, &len, APR_NONBLOCK_READ);
394 - if (APR_STATUS_IS_EAGAIN(rv)) {
395 - ctx->output_now = 1;
399 - if (ctx->output_now) {
400 - if (ctx->state == PARSE_DIRECTIVE) {
401 - /* gonna start over parsing the directive next time through */
402 - ctx->directive_length = 0;
403 - ctx->tag_length = 0;
409 - rv = apr_bucket_read(dptr, &buf, &len, APR_BLOCK_READ);
411 - if (!APR_STATUS_IS_SUCCESS(rv)) {
416 - if (len == 0) { /* end of pipe? */
417 - dptr = APR_BUCKET_NEXT(dptr);
420 - if (dptr == ctx->tag_start_bucket) {
421 - c = buf + ctx->tag_start_index;
427 - while (c < buf + len) {
428 - if (*c == str[ctx->parse_pos]) {
429 - if (ctx->state != PARSE_TAIL) {
430 - ctx->state = PARSE_TAIL;
431 - ctx->tail_start_bucket = dptr;
432 - ctx->tail_start_index = c - buf;
435 - if (str[ctx->parse_pos] == '\0') {
436 - apr_bucket *tmp_buck = dptr;
438 - /* We want to split the bucket at the '>'. The
439 - * end of the END_SEQUENCE is in the current bucket.
440 - * The beginning might be in a previous bucket.
443 - ctx->bytes_parsed += (c - start);
444 - ctx->state = PARSED;
445 - apr_bucket_split(dptr, c - buf);
446 - tmp_buck = APR_BUCKET_NEXT(dptr);
451 - if (ctx->state == PARSE_DIRECTIVE) {
452 - if (ctx->tag_length == 0) {
453 - if (!apr_isspace(*c)) {
454 - const char *tmp = c;
455 - ctx->tag_start_bucket = dptr;
456 - ctx->tag_start_index = c - buf;
459 - } while ((c < buf + len) && !apr_isspace(*c) &&
461 - ctx->tag_length = ctx->directive_length = c - tmp;
466 - if (!apr_isspace(*c)) {
467 - ctx->directive_length++;
470 - ctx->state = PARSE_TAG;
475 - else if (ctx->state == PARSE_TAG) {
476 - const char *tmp = c;
479 - } while ((c < buf + len) && (*c != *str));
480 - ctx->tag_length += (c - tmp);
484 - if (ctx->parse_pos != 0) {
485 - /* The reason for this, is that we need to make sure
486 - * that we catch cases like --->. This makes the
487 - * second check after the original check fails.
488 - * If parse_pos was already 0 then we already checked
491 - ctx->tag_length += ctx->parse_pos;
493 - if (*c == str[0]) {
494 - ctx->state = PARSE_TAIL;
495 - ctx->tail_start_bucket = dptr;
496 - ctx->tail_start_index = c - buf;
497 - ctx->parse_pos = 1;
501 - if (ctx->tag_length > ctx->directive_length) {
502 - ctx->state = PARSE_TAG;
505 - ctx->state = PARSE_DIRECTIVE;
506 - ctx->directive_length += ctx->parse_pos;
508 - ctx->tail_start_bucket = NULL;
509 - ctx->tail_start_index = 0;
510 - ctx->parse_pos = 0;
517 - ctx->bytes_parsed += (c - start);
518 - dptr = APR_BUCKET_NEXT(dptr);
519 - } while (dptr != APR_BRIGADE_SENTINEL(bb));
523 -/* This function culls through the buckets that have been set aside in the
524 - * ssi_tag_brigade and copies just the directive part of the SSI tag (none
525 - * of the start and end delimiter bytes are copied).
527 -static apr_status_t get_combined_directive (include_ctx_t *ctx,
529 - apr_bucket_brigade *bb,
531 - apr_size_t tmp_buf_size)
535 - const char *tmp_from;
536 - apr_size_t tmp_from_len;
538 - /* If the tag length is longer than the tmp buffer, allocate space. */
539 - if (ctx->tag_length > tmp_buf_size-1) {
540 - if ((ctx->combined_tag = apr_pcalloc(r->pool,
541 - ctx->tag_length + 1)) == NULL) {
542 - return (APR_ENOMEM);
544 - } /* Else, just use the temp buffer. */
546 - ctx->combined_tag = tmp_buf;
549 - /* Prime the pump. Start at the beginning of the tag... */
550 - dptr = ctx->tag_start_bucket;
551 - /* Read the bucket... */
552 - apr_bucket_read (dptr, &tmp_from, &tmp_from_len, 0);
554 - /* Adjust the pointer to start at the tag within the bucket... */
555 - if (dptr == ctx->tail_start_bucket) {
556 - tmp_from_len -= (tmp_from_len - ctx->tail_start_index);
558 - tmp_from = &tmp_from[ctx->tag_start_index];
559 - tmp_from_len -= ctx->tag_start_index;
560 - ctx->curr_tag_pos = ctx->combined_tag;
562 - /* Loop through the buckets from the tag_start_bucket until before
563 - * the tail_start_bucket copying the contents into the buffer.
566 - memcpy (ctx->curr_tag_pos, tmp_from, tmp_from_len);
567 - ctx->curr_tag_pos += tmp_from_len;
569 - if (dptr == ctx->tail_start_bucket) {
573 - dptr = APR_BUCKET_NEXT (dptr);
574 - apr_bucket_read (dptr, &tmp_from, &tmp_from_len, 0);
575 - /* Adjust the count to stop at the beginning of the tail. */
576 - if (dptr == ctx->tail_start_bucket) {
577 - tmp_from_len -= (tmp_from_len - ctx->tail_start_index);
580 - } while ((!done) &&
581 - (ctx->curr_tag_pos < ctx->combined_tag + ctx->tag_length));
583 - ctx->combined_tag[ctx->tag_length] = '\0';
584 - ctx->curr_tag_pos = ctx->combined_tag;
586 - return (APR_SUCCESS);
590 * decodes a string containing html entities or numeric character references.
591 * 's' is overwritten with the decoded string.
592 @@ -890,106 +493,34 @@
593 static void ap_ssi_get_tag_and_value(include_ctx_t *ctx, char **tag,
594 char **tag_val, int dodecode)
596 - char *c = ctx->curr_tag_pos;
601 - if (ctx->curr_tag_pos > ctx->combined_tag + ctx->tag_length) {
602 + if (ctx->curr_tag_pos >= ctx->combined_tag + ctx->tag_length) {
606 - SKIP_TAG_WHITESPACE(c);
607 - *tag = c; /* First non-whitespace character (could be NULL). */
609 - while (apr_islower(*c)) {
610 - c++; /* Optimization for the common case where the tag */
611 - } /* is already lowercase */
613 - while ((*c != '=') && (!apr_isspace(*c)) && (*c != '\0')) {
614 - *c = apr_tolower(*c); /* find end of tag, lowercasing as we go... */
616 + *tag = ctx->curr_tag_pos;
620 + ctx->curr_tag_pos = ctx->combined_tag + ctx->tag_length;
624 - if ((*c == '\0') || (**tag == '=')) {
625 - if ((**tag == '\0') || (**tag == '=')) {
628 - ctx->curr_tag_pos = c;
629 - return; /* We have found the end of the buffer. */
630 - } /* We might have a tag, but definitely no value. */
633 - *c++ = '\0'; /* Overwrite the '=' with a terminating byte after tag. */
635 - else { /* Try skipping WS to find the '='. */
636 - *c++ = '\0'; /* Terminate the tag... */
637 - SKIP_TAG_WHITESPACE(c);
639 - /* There needs to be an equal sign if there's a value. */
641 - ctx->curr_tag_pos = c;
642 - return; /* There apparently was no value. */
645 - c++; /* Skip the equals sign. */
647 + *tag_val = ap_strchr(*tag, '=');
649 + ctx->curr_tag_pos = ctx->combined_tag + ctx->tag_length;
653 - SKIP_TAG_WHITESPACE(c);
654 - if (*c == '"' || *c == '\'' || *c == '`') {
655 - /* Allow quoted values for space inclusion.
656 - * NOTE: This does not pass the quotes on return.
663 - while (!apr_isspace(*c) && (*c != '\0')) {
666 + /* if it starts with '=' there was no tag name, just a value */
667 + if (*tag_val == *tag) {
671 - while ((*c != term) && (*c != '\0') && (*c != '\\')) {
672 - /* Quickly scan past the string until we reach
673 - * either the end of the tag or a backslash. If
674 - * we find a backslash, we have to switch to the
675 - * more complicated parser loop that follows.
681 - /* Accept \" (or ' or `) as valid quotation of string.
684 - /* Overwrite the "\" during the embedded
685 - * escape sequence of '"'. "\'" or '`'.
686 - * Shift bytes from here to next delimiter.
692 - if (shift_val > 0) {
693 - *(c-shift_val) = *c;
701 - if (shift_val > 0) {
702 - *(c-shift_val) = *c;
704 - } while ((*c != term) && (*c != '\0'));
708 - *(c-shift_val) = '\0'; /* Overwrites delimiter (term or WS) with NULL. */
709 - ctx->curr_tag_pos = ++c;
710 + *(*tag_val)++ = '\0';
711 + ctx->curr_tag_pos = *tag_val + strlen(*tag_val) + 1; /* skip \0 byte */
714 decodehtml(*tag_val);
717 "unknown parameter \"%s\" to tag include in %s",
719 CREATE_ERROR_BUCKET(ctx, tmp_buck, head_ptr, *inserted_head);
725 "tag echo in %s", tag_val, r->filename);
726 CREATE_ERROR_BUCKET(ctx, tmp_buck, head_ptr,
733 "unknown parameter \"%s\" in tag echo of %s",
735 CREATE_ERROR_BUCKET(ctx, tmp_buck, head_ptr, *inserted_head);
740 @@ -1514,6 +1048,7 @@
741 "unknown parameter \"%s\" to tag config in %s",
743 CREATE_ERROR_BUCKET(ctx, tmp_buck, head_ptr, *inserted_head);
748 @@ -1663,6 +1198,7 @@
750 CREATE_ERROR_BUCKET(ctx, tmp_buck, head_ptr,
756 @@ -1712,6 +1248,7 @@
758 CREATE_ERROR_BUCKET(ctx, tmp_buck, head_ptr,
764 @@ -2642,6 +2179,7 @@
765 "unknown parameter \"%s\" to tag if in %s", tag,
767 CREATE_ERROR_BUCKET(ctx, tmp_buck, head_ptr, *inserted_head);
772 @@ -2678,7 +2216,7 @@
774 CREATE_ERROR_BUCKET(ctx, tmp_buck, head_ptr,
779 expr_ret = parse_expr(r, ctx, expr, &was_error,
780 &was_unmatched, debug_buf);
781 @@ -2725,6 +2263,7 @@
782 "unknown parameter \"%s\" to tag if in %s", tag,
784 CREATE_ERROR_BUCKET(ctx, tmp_buck, head_ptr, *inserted_head);
789 @@ -2914,362 +2453,1032 @@
791 /* -------------------------- The main function --------------------------- */
793 -static apr_status_t send_parsed_content(apr_bucket_brigade **bb,
794 - request_rec *r, ap_filter_t *f)
796 + * returns the index position of the first byte of start_seq (or the len of
797 + * the buffer as non-match)
799 +static apr_size_t find_start_sequence(ssi_ctx_t *ctx, const char *data,
802 - include_ctx_t *ctx = f->ctx;
803 - apr_bucket *dptr = APR_BRIGADE_FIRST(*bb);
804 - apr_bucket *tmp_dptr;
805 - apr_bucket_brigade *tag_and_after;
806 - apr_status_t rv = APR_SUCCESS;
808 - if (r->args) { /* add QUERY stuff to env cause it ain't yet */
809 - char *arg_copy = apr_pstrdup(r->pool, r->args);
810 + apr_size_t slen = ctx->ctx->start_seq_len;
812 + const char *p, *ep;
814 - apr_table_setn(r->subprocess_env, "QUERY_STRING", r->args);
815 - ap_unescape_url(arg_copy);
816 - apr_table_setn(r->subprocess_env, "QUERY_STRING_UNESCAPED",
817 - ap_escape_shell_cmd(r->pool, arg_copy));
819 + p = data; /* try partial match at the end of the buffer (below) */
822 + /* try fast bndm search over the buffer
823 + * (hopefully the whole start sequence can be found in this buffer)
825 + index = bndm(ctx->ctx->start_seq, ctx->ctx->start_seq_len, data, len,
826 + ctx->ctx->start_seq_pat);
828 - while (dptr != APR_BRIGADE_SENTINEL(*bb) && !APR_BUCKET_IS_EOS(dptr)) {
829 - /* State to check for the STARTING_SEQUENCE. */
830 - if ((ctx->state == PRE_HEAD) || (ctx->state == PARSE_HEAD)) {
831 - int do_cleanup = 0;
832 - apr_size_t cleanup_bytes = ctx->parse_pos;
834 - tmp_dptr = find_start_sequence(dptr, ctx, *bb, &do_cleanup);
835 - if (!APR_STATUS_IS_SUCCESS(ctx->status)) {
836 - return ctx->status;
839 - /* The few bytes stored in the ssi_tag_brigade turned out not to
840 - * be a tag after all. This can only happen if the starting
841 - * tag actually spans brigades. This should be very rare.
842 + /* wow, found it. ready. */
844 + ctx->state = PARSE_DIRECTIVE;
848 + /* ok, the pattern can't be found as whole in the buffer,
849 + * check the end for a partial match
851 - if ((do_cleanup) && (!APR_BRIGADE_EMPTY(ctx->ssi_tag_brigade))) {
852 - apr_bucket *tmp_bkt;
853 + p = data + len - slen + 1;
857 - tmp_bkt = apr_bucket_immortal_create(ctx->start_seq,
859 - r->connection->bucket_alloc);
860 - APR_BRIGADE_INSERT_HEAD(*bb, tmp_bkt);
861 - apr_brigade_cleanup(ctx->ssi_tag_brigade);
865 + while (p < ep && *p != *ctx->ctx->start_seq) {
869 - /* If I am inside a conditional (if, elif, else) that is false
870 - * then I need to throw away anything contained in it.
872 - if ((!(ctx->flags & FLAG_PRINTING)) &&
873 - (dptr != APR_BRIGADE_SENTINEL(*bb))) {
874 - apr_bucket *stop = (!tmp_dptr && ctx->state == PARSE_HEAD)
875 - ? ctx->head_start_bucket
879 - while ((dptr != APR_BRIGADE_SENTINEL(*bb)) && (dptr != stop)) {
880 - apr_bucket *free_bucket = dptr;
881 + /* found a possible start_seq start */
883 + apr_size_t pos = 1;
885 - dptr = APR_BUCKET_NEXT(dptr);
886 - if (!APR_BUCKET_IS_METADATA(free_bucket)) {
887 - apr_bucket_delete(free_bucket);
891 + while (p < ep && *p == ctx->ctx->start_seq[pos]) {
896 - /* Adjust the current bucket position based on what was found... */
897 - if ((tmp_dptr != NULL) && (ctx->state == PARSE_DIRECTIVE)) {
898 - if (ctx->tag_start_bucket != NULL) {
899 - dptr = ctx->tag_start_bucket;
902 - dptr = APR_BRIGADE_SENTINEL(*bb);
904 + /* partial match found. Store the info for the next round */
906 + ctx->state = PARSE_HEAD;
907 + ctx->ctx->parse_pos = pos;
910 - else if ((tmp_dptr != NULL) &&
911 - (ctx->output_now ||
912 - (ctx->bytes_parsed >= BYTE_COUNT_THRESHOLD))) {
913 - /* Send the large chunk of pre-tag bytes... */
914 - tag_and_after = apr_brigade_split(*bb, tmp_dptr);
915 - if (ctx->output_flush) {
916 - APR_BRIGADE_INSERT_TAIL(*bb, apr_bucket_flush_create((*bb)->bucket_alloc));
920 - rv = ap_pass_brigade(f->next, *bb);
921 - if (rv != APR_SUCCESS) {
924 - *bb = tag_and_after;
926 - ctx->output_flush = 0;
927 - ctx->bytes_parsed = 0;
928 - ctx->output_now = 0;
930 - else if (tmp_dptr == NULL) {
931 - /* There was no possible SSI tag in the
932 - * remainder of this brigade... */
933 - dptr = APR_BRIGADE_SENTINEL(*bb);
935 + /* we must try all combinations; consider (e.g.) SSIStartTag "--->"
936 + * and a string data of "--.-" and the end of the buffer
938 + p = data + index + 1;
946 + * returns the first byte *after* the partial (or final) match.
948 + * If we had to trick with the start_seq start, 'release' returns the
949 + * number of chars of the start_seq which appeared not to be part of a
950 + * full tag and may have to be passed down the filter chain.
952 +static apr_size_t find_partial_start_sequence(ssi_ctx_t *ctx,
955 + apr_size_t *release)
957 + apr_size_t pos, spos = 0;
958 + apr_size_t slen = ctx->ctx->start_seq_len;
959 + const char *p, *ep;
961 + pos = ctx->ctx->parse_pos;
968 + while (p < ep && pos < slen && *p == ctx->ctx->start_seq[pos]) {
973 - /* State to check for the ENDING_SEQUENCE. */
974 - if (((ctx->state == PARSE_DIRECTIVE) ||
975 - (ctx->state == PARSE_TAG) ||
976 - (ctx->state == PARSE_TAIL)) &&
977 - (dptr != APR_BRIGADE_SENTINEL(*bb))) {
978 - tmp_dptr = find_end_sequence(dptr, ctx, *bb);
979 - if (!APR_STATUS_IS_SUCCESS(ctx->status)) {
980 - return ctx->status;
984 + ctx->state = PARSE_DIRECTIVE;
988 - if (tmp_dptr != NULL) {
989 - dptr = tmp_dptr; /* Adjust bucket pos... */
991 - /* If some of the tag has already been set aside then set
992 - * aside remainder of tag. Now the full tag is in
994 - * If none has yet been set aside, then leave it all where it
996 - * In any event after this the entire set of tag buckets will
997 - * be in one place or another.
999 - if (!APR_BRIGADE_EMPTY(ctx->ssi_tag_brigade)) {
1000 - tag_and_after = apr_brigade_split(*bb, dptr);
1001 - APR_BRIGADE_CONCAT(ctx->ssi_tag_brigade, *bb);
1002 - *bb = tag_and_after;
1004 - else if (ctx->output_now ||
1005 - (ctx->bytes_parsed >= BYTE_COUNT_THRESHOLD)) {
1006 - SPLIT_AND_PASS_PRETAG_BUCKETS(*bb, ctx, f->next, rv);
1007 - if (rv != APR_SUCCESS) {
1010 - ctx->output_flush = 0;
1011 - ctx->output_now = 0;
1015 - /* remainder of this brigade... */
1016 - dptr = APR_BRIGADE_SENTINEL(*bb);
1018 + /* the whole buffer is a partial match */
1020 + ctx->ctx->parse_pos = pos;
1021 + return (p - data);
1024 - /* State to processed the directive... */
1025 - if (ctx->state == PARSED) {
1026 - apr_bucket *content_head = NULL, *tmp_bkt;
1028 - char tmp_buf[TMP_BUF_SIZE];
1029 - int (*handle_func)(include_ctx_t *, apr_bucket_brigade **,
1030 - request_rec *, ap_filter_t *, apr_bucket *,
1032 + /* No match so far, but again:
1033 + * We must try all combinations, since the start_seq is a random
1034 + * user supplied string
1036 + * So: look if the first char of start_seq appears somewhere within
1037 + * the current partial match. If it does, try to start a match that
1038 + * begins with this offset. (This can happen, if a strange
1039 + * start_seq like "---->" spans buffers)
1041 + if (spos < ctx->ctx->parse_pos) {
1045 + p = ctx->ctx->start_seq + spos;
1046 + pos = ctx->ctx->parse_pos - spos;
1048 + while (pos && *p != *ctx->ctx->start_seq) {
1055 - /* By now the full tag (all buckets) should either be set aside into
1056 - * ssi_tag_brigade or contained within the current bb. All tag
1057 - * processing from here on can assume that.
1059 + /* if a matching beginning char was found, try to match the
1060 + * remainder of the old buffer.
1065 - /* At this point, everything between ctx->head_start_bucket and
1066 - * ctx->tail_start_bucket is an SSI
1067 - * directive, we just have to deal with it now.
1069 - if (get_combined_directive(ctx, r, *bb, tmp_buf,
1070 - TMP_BUF_SIZE) != APR_SUCCESS) {
1071 - ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
1072 - "mod_include: error copying directive in %s",
1074 - CREATE_ERROR_BUCKET(ctx, tmp_bkt, dptr, content_head);
1076 + while (t < pos && *p == ctx->ctx->start_seq[t]) {
1081 - /* DO CLEANUP HERE!!!!! */
1082 - tmp_dptr = ctx->head_start_bucket;
1083 - if (!APR_BRIGADE_EMPTY(ctx->ssi_tag_brigade)) {
1084 - apr_brigade_cleanup(ctx->ssi_tag_brigade);
1088 - tmp_bkt = tmp_dptr;
1089 - tmp_dptr = APR_BUCKET_NEXT (tmp_dptr);
1090 - apr_bucket_delete(tmp_bkt);
1091 - } while ((tmp_dptr != dptr) &&
1092 - (tmp_dptr != APR_BRIGADE_SENTINEL(*bb)));
1094 + /* yeah, another partial match found in the *old*
1095 + * buffer, now test the *current* buffer for
1096 + * continuing match
1101 + } while (pos > 1);
1103 - return APR_SUCCESS;
1109 - /* Can't destroy the tag buckets until I'm done processing
1110 - * because the combined_tag might just be pointing to
1111 - * the contents of a single bucket!
1114 + } while (1); /* work hard to find a match ;-) */
1116 - /* Retrieve the handler function to be called for this directive
1117 - * from the functions registered in the hash table.
1118 - * Need to lower case the directive for proper matching. Also need
1119 - * to have it NULL terminated for proper hash matching.
1121 - for (tmp_i = 0; tmp_i < ctx->directive_length; tmp_i++) {
1122 - ctx->combined_tag[tmp_i] =
1123 - apr_tolower(ctx->combined_tag[tmp_i]);
1125 - ctx->combined_tag[ctx->directive_length] = '\0';
1126 - ctx->curr_tag_pos = &ctx->combined_tag[ctx->directive_length+1];
1129 - (include_handler_fn_t *)apr_hash_get(include_hash,
1130 - ctx->combined_tag,
1131 - ctx->directive_length);
1132 - if (handle_func != NULL) {
1133 - rv = (*handle_func)(ctx, bb, r, f, dptr, &content_head);
1134 - if ((rv != 0) && (rv != 1)) {
1139 - ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
1140 - "unknown directive \"%s\" in parsed doc %s",
1141 - ctx->combined_tag, r->filename);
1142 - CREATE_ERROR_BUCKET(ctx, tmp_bkt, dptr, content_head);
1145 - /* This chunk of code starts at the first bucket in the chain
1146 - * of tag buckets (assuming that by this point the bucket for
1147 - * the STARTING_SEQUENCE has been split) and loops through to
1148 - * the end of the tag buckets freeing them all.
1150 - * Remember that some part of this may have been set aside
1151 - * into the ssi_tag_brigade and the remainder (possibly as
1152 - * little as one byte) will be in the current brigade.
1154 - * The value of dptr should have been set during the
1155 - * PARSE_TAIL state to the first bucket after the
1156 - * ENDING_SEQUENCE.
1158 - * The value of content_head may have been set during processing
1159 - * of the directive. If so, the content was inserted in front
1160 - * of the dptr bucket. The inserted buckets should not be thrown
1161 - * away here, but they should also not be parsed later.
1162 + /* no match at all, release all (wrongly) matched chars so far */
1163 + *release = ctx->ctx->parse_pos;
1164 + ctx->state = PARSE_PRE_HEAD;
1169 + * returns the position after the directive
1171 +static apr_size_t find_directive(ssi_ctx_t *ctx, const char *data,
1172 + apr_size_t len, char ***store,
1173 + apr_size_t **store_len)
1175 + const char *p = data;
1176 + const char *ep = data + len;
1179 + switch (ctx->state) {
1180 + case PARSE_DIRECTIVE:
1181 + while (p < ep && !apr_isspace(*p)) {
1182 + /* we have to consider the case of missing space between directive
1183 + * and end_seq (be somewhat lenient), e.g. <!--#printenv-->
1185 - if (content_head == NULL) {
1186 - content_head = dptr;
1187 + if (*p == *ctx->ctx->end_seq) {
1188 + ctx->state = PARSE_DIRECTIVE_TAIL;
1189 + ctx->ctx->parse_pos = 1;
1191 + return (p - data);
1196 + if (p < ep) { /* found delimiter whitespace */
1197 + ctx->state = PARSE_DIRECTIVE_POSTNAME;
1198 + *store = &ctx->directive;
1199 + *store_len = &ctx->ctx->directive_length;
1204 + case PARSE_DIRECTIVE_TAIL:
1205 + pos = ctx->ctx->parse_pos;
1207 + while (p < ep && pos < ctx->end_seq_len &&
1208 + *p == ctx->ctx->end_seq[pos]) {
1213 + /* full match, we're done */
1214 + if (pos == ctx->end_seq_len) {
1215 + ctx->state = PARSE_DIRECTIVE_POSTTAIL;
1216 + *store = &ctx->directive;
1217 + *store_len = &ctx->ctx->directive_length;
1221 + /* partial match, the buffer is too small to match fully */
1223 + ctx->ctx->parse_pos = pos;
1227 + /* no match. continue normal parsing */
1228 + ctx->state = PARSE_DIRECTIVE;
1231 + case PARSE_DIRECTIVE_POSTTAIL:
1232 + ctx->state = PARSE_EXECUTE;
1233 + ctx->ctx->directive_length -= ctx->end_seq_len;
1234 + /* continue immediately with the next state */
1236 + case PARSE_DIRECTIVE_POSTNAME:
1237 + if (PARSE_DIRECTIVE_POSTNAME == ctx->state) {
1238 + ctx->state = PARSE_PRE_ARG;
1243 + if (!ctx->ctx->directive_length) {
1245 + ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, ctx->r, "missing directive "
1246 + "name in parsed document %s", ctx->r->filename);
1249 + char *sp = ctx->directive;
1250 + char *sep = ctx->directive + ctx->ctx->directive_length;
1252 + /* normalize directive name */
1253 + for (; sp < sep; ++sp) {
1254 + *sp = apr_tolower(*sp);
1256 - tmp_dptr = ctx->head_start_bucket;
1257 - if (!APR_BRIGADE_EMPTY(ctx->ssi_tag_brigade)) {
1258 - apr_brigade_cleanup(ctx->ssi_tag_brigade);
1264 + /* get a rid of a gcc warning about unhandled enumerations */
1268 + return (p - data);
1272 + * find out whether the next token is (a possible) end_seq or an argument
1274 +static apr_size_t find_arg_or_tail(ssi_ctx_t *ctx, const char *data,
1277 + const char *p = data;
1278 + const char *ep = data + len;
1280 + /* skip leading WS */
1281 + while (p < ep && apr_isspace(*p)) {
1285 + /* buffer doesn't consist of whitespaces only */
1287 + ctx->state = (*p == *ctx->ctx->end_seq) ? PARSE_TAIL : PARSE_ARG;
1290 + return (p - data);
1294 + * test the stream for end_seq. If it doesn't match at all, it must be an
1297 +static apr_size_t find_tail(ssi_ctx_t *ctx, const char *data,
1300 + const char *p = data;
1301 + const char *ep = data + len;
1302 + apr_size_t pos = ctx->ctx->parse_pos;
1304 + if (PARSE_TAIL == ctx->state) {
1305 + ctx->state = PARSE_TAIL_SEQ;
1306 + pos = ctx->ctx->parse_pos = 0;
1309 + while (p < ep && pos < ctx->end_seq_len && *p == ctx->ctx->end_seq[pos]) {
1314 + /* bingo, full match */
1315 + if (pos == ctx->end_seq_len) {
1316 + ctx->state = PARSE_EXECUTE;
1317 + return (p - data);
1320 + /* partial match, the buffer is too small to match fully */
1322 + ctx->ctx->parse_pos = pos;
1323 + return (p - data);
1326 + /* no match. It must be an argument string then */
1327 + ctx->state = PARSE_ARG;
1332 + * extract name=value from the buffer
1333 + * A pcre-pattern could look (similar to):
1334 + * name\s*(?:=\s*(["'`]?)value\1(?>\s*))?
1336 +static apr_size_t find_argument(ssi_ctx_t *ctx, const char *data,
1337 + apr_size_t len, char ***store,
1338 + apr_size_t **store_len)
1340 + const char *p = data;
1341 + const char *ep = data + len;
1343 + switch (ctx->state) {
1346 + * create argument structure and append it to the current list
1348 + ctx->current_arg = apr_palloc(ctx->dpool,
1349 + sizeof(*ctx->current_arg));
1350 + ctx->current_arg->next = NULL;
1354 + ctx->argv = ctx->current_arg;
1357 + ssi_arg_item_t *newarg = ctx->argv;
1359 + while (newarg->next) {
1360 + newarg = newarg->next;
1364 - tmp_bkt = tmp_dptr;
1365 - tmp_dptr = APR_BUCKET_NEXT (tmp_dptr);
1366 - apr_bucket_delete(tmp_bkt);
1367 - } while ((tmp_dptr != content_head) &&
1368 - (tmp_dptr != APR_BRIGADE_SENTINEL(*bb)));
1369 + newarg->next = ctx->current_arg;
1372 + /* check whether it's a valid one. If it begins with a quote, we
1373 + * can safely assume, someone forgot the name of the argument
1376 + case '"': case '\'': case '`':
1379 + ctx->state = PARSE_ARG_VAL;
1380 + ctx->quote = *p++;
1381 + ctx->current_arg->name = NULL;
1382 + ctx->current_arg->name_len = 0;
1385 + ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, ctx->r, "missing argument "
1386 + "name for value to tag %s in %s",
1387 + apr_pstrmemdup(ctx->r->pool, ctx->directive,
1388 + ctx->ctx->directive_length),
1389 + ctx->r->filename);
1391 + return (p - data);
1394 + ctx->state = PARSE_ARG_NAME;
1396 + /* continue immediately with next state */
1398 + case PARSE_ARG_NAME:
1399 + while (p < ep && !apr_isspace(*p) && *p != '=') {
1404 + ctx->state = PARSE_ARG_POSTNAME;
1405 + *store = &ctx->current_arg->name;
1406 + *store_len = &ctx->current_arg->name_len;
1407 + return (p - data);
1411 + case PARSE_ARG_POSTNAME:
1412 + ctx->current_arg->name = apr_pstrmemdup(ctx->dpool,
1413 + ctx->current_arg->name,
1414 + ctx->current_arg->name_len);
1415 + if (!ctx->current_arg->name_len) {
1417 + ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, ctx->r, "missing argument "
1418 + "name for value to tag %s in %s",
1419 + apr_pstrmemdup(ctx->r->pool, ctx->directive,
1420 + ctx->ctx->directive_length),
1421 + ctx->r->filename);
1424 + char *sp = ctx->current_arg->name;
1426 + /* normalize the name */
1428 + *sp = apr_tolower(*sp);
1433 + ctx->state = PARSE_ARG_EQ;
1434 + /* continue with next state immediately */
1436 + case PARSE_ARG_EQ:
1439 + while (p < ep && apr_isspace(*p)) {
1445 + ctx->state = PARSE_ARG_PREVAL;
1448 - if (ctx->combined_tag == tmp_buf) {
1449 - ctx->combined_tag = NULL;
1450 + else { /* no value */
1451 + ctx->current_arg->value = NULL;
1452 + ctx->state = PARSE_PRE_ARG;
1455 - /* Don't reset the flags or the nesting level!!! */
1456 - ctx->parse_pos = 0;
1457 - ctx->head_start_bucket = NULL;
1458 - ctx->head_start_index = 0;
1459 - ctx->tag_start_bucket = NULL;
1460 - ctx->tag_start_index = 0;
1461 - ctx->tail_start_bucket = NULL;
1462 - ctx->tail_start_index = 0;
1463 - ctx->curr_tag_pos = NULL;
1464 - ctx->tag_length = 0;
1465 - ctx->directive_length = 0;
1466 + return (p - data);
1470 + case PARSE_ARG_PREVAL:
1473 - if (!APR_BRIGADE_EMPTY(ctx->ssi_tag_brigade)) {
1474 - apr_brigade_cleanup(ctx->ssi_tag_brigade);
1475 + while (p < ep && apr_isspace(*p)) {
1479 + /* buffer doesn't consist of whitespaces only */
1481 + ctx->state = PARSE_ARG_VAL;
1483 + case '"': case '\'': case '`':
1484 + ctx->quote = *p++;
1487 + ctx->quote = '\0';
1491 - ctx->state = PRE_HEAD;
1492 + return (p - data);
1497 + case PARSE_ARG_VAL_ESC:
1498 + if (*p == ctx->quote) {
1501 + ctx->state = PARSE_ARG_VAL;
1502 + /* continue with next state immediately */
1504 + case PARSE_ARG_VAL:
1505 + for (; p < ep; ++p) {
1506 + if (ctx->quote && *p == '\\') {
1509 + ctx->state = PARSE_ARG_VAL_ESC;
1513 - /* We have nothing more to send, stop now. */
1514 - if (dptr != APR_BRIGADE_SENTINEL(*bb) &&
1515 - APR_BUCKET_IS_EOS(dptr)) {
1516 - /* We might have something saved that we never completed, but send
1517 - * down unparsed. This allows for <!-- at the end of files to be
1518 - * sent correctly. */
1519 - if (!APR_BRIGADE_EMPTY(ctx->ssi_tag_brigade)) {
1520 - APR_BRIGADE_CONCAT(ctx->ssi_tag_brigade, *bb);
1521 - return ap_pass_brigade(f->next, ctx->ssi_tag_brigade);
1522 + if (*p != ctx->quote) {
1526 + else if (ctx->quote && *p == ctx->quote) {
1528 + *store = &ctx->current_arg->value;
1529 + *store_len = &ctx->current_arg->value_len;
1530 + ctx->state = PARSE_ARG_POSTVAL;
1533 + else if (!ctx->quote && apr_isspace(*p)) {
1535 + *store = &ctx->current_arg->value;
1536 + *store_len = &ctx->current_arg->value_len;
1537 + ctx->state = PARSE_ARG_POSTVAL;
1541 - return ap_pass_brigade(f->next, *bb);
1544 - /* If I am in the middle of parsing an SSI tag then I need to set aside
1545 - * the pertinent trailing buckets and pass on the initial part of the
1546 - * brigade. The pertinent parts of the next brigades will be added to
1547 - * these set aside buckets to form the whole tag and will be processed
1548 - * once the whole tag has been found.
1550 - if (ctx->state == PRE_HEAD) {
1551 - if (!APR_BRIGADE_EMPTY(*bb)) {
1552 - /* pass it along... */
1553 - rv = ap_pass_brigade(f->next, *bb);
1554 - if (rv != APR_SUCCESS) {
1556 + return (p - data);
1558 + case PARSE_ARG_POSTVAL:
1560 + * The value is still the raw input string. Finally clean it up.
1562 + --(ctx->current_arg->value_len);
1564 + /* strip quote escaping \ from the string */
1566 + apr_size_t shift = 0;
1569 + sp = ctx->current_arg->value;
1570 + ep = ctx->current_arg->value + ctx->current_arg->value_len;
1571 + while (sp < ep && *sp != '\\') {
1574 - ctx->bytes_parsed = 0;
1575 + for (; sp < ep; ++sp) {
1576 + if (*sp == '\\' && sp[1] == ctx->quote) {
1581 + *(sp-shift) = *sp;
1585 + ctx->current_arg->value_len -= shift;
1588 + ctx->current_arg->value[ctx->current_arg->value_len] = '\0';
1589 + ctx->state = PARSE_PRE_ARG;
1594 + /* get a rid of a gcc warning about unhandled enumerations */
1597 - else if (ctx->state == PARSED) { /* Invalid internal condition... */
1598 - apr_bucket *content_head = NULL, *tmp_bkt;
1599 - ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
1600 - "Invalid mod_include state during file %s", r->filename);
1601 - CREATE_ERROR_BUCKET(ctx, tmp_bkt, APR_BRIGADE_FIRST(*bb), content_head);
1603 + return len; /* partial match of something */
1607 + * This is the main loop over the current bucket brigade.
1609 +static apr_status_t send_parsed_content(ap_filter_t *f, apr_bucket_brigade *bb)
1611 + ssi_ctx_t *ctx = f->ctx;
1612 + request_rec *r = f->r;
1613 + apr_bucket *b = APR_BRIGADE_FIRST(bb);
1614 + apr_bucket_brigade *pass_bb;
1615 + apr_status_t rv = APR_SUCCESS;
1616 + char *magic; /* magic pointer for sentinel use */
1619 + if (APR_BRIGADE_EMPTY(bb)) {
1620 + return APR_SUCCESS;
1622 - else { /* Entire brigade is middle chunk of SSI tag... */
1623 - if (!APR_BRIGADE_EMPTY(ctx->ssi_tag_brigade)) {
1624 - APR_BRIGADE_CONCAT(ctx->ssi_tag_brigade, *bb);
1626 - else { /* End of brigade contains part of SSI tag... */
1628 - if (ctx->head_start_index > 0) {
1629 - apr_bucket_split(ctx->head_start_bucket, ctx->head_start_index);
1630 - ctx->head_start_bucket =
1631 - APR_BUCKET_NEXT(ctx->head_start_bucket);
1632 - ctx->head_start_index = 0;
1634 - /* Set aside tag, pass pre-tag... */
1635 - tag_and_after = apr_brigade_split(*bb, ctx->head_start_bucket);
1636 - rv = ap_pass_brigade(f->next, *bb);
1637 - if (rv != APR_SUCCESS) {
1639 + /* we may crash, since already cleaned up; hand over the responsibility
1640 + * to the next filter;-)
1642 + if (ctx->seen_eos) {
1643 + return ap_pass_brigade(f->next, bb);
1646 + /* All stuff passed along has to be put into that brigade */
1647 + pass_bb = apr_brigade_create(ctx->ctx->pool, f->c->bucket_alloc);
1648 + ctx->ctx->bytes_parsed = 0;
1649 + ctx->ctx->output_now = 0;
1652 + /* loop over the current bucket brigade */
1653 + while (b != APR_BRIGADE_SENTINEL(bb)) {
1654 + const char *data = NULL;
1655 + apr_size_t len, index, release;
1656 + apr_bucket *newb = NULL;
1657 + char **store = &magic;
1658 + apr_size_t *store_len;
1660 + /* handle meta buckets before reading any data */
1661 + if (APR_BUCKET_IS_METADATA(b)) {
1662 + newb = APR_BUCKET_NEXT(b);
1664 + APR_BUCKET_REMOVE(b);
1666 + if (APR_BUCKET_IS_EOS(b)) {
1667 + ctx->seen_eos = 1;
1669 + /* Hit end of stream, time for cleanup ... But wait!
1670 + * Perhaps we're not ready yet. We may have to loop one or
1671 + * two times again to finish our work. In that case, we
1672 + * just re-insert the EOS bucket to allow for an extra loop.
1674 + * PARSE_EXECUTE means, we've hit a directive just before the
1675 + * EOS, which is now waiting for execution.
1677 + * PARSE_DIRECTIVE_POSTTAIL means, we've hit a directive with
1678 + * no argument and no space between directive and end_seq
1679 + * just before the EOS. (consider <!--#printenv--> as last
1680 + * or only string within the stream). This state, however,
1681 + * just cleans up and turns itself to PARSE_EXECUTE, which
1682 + * will be passed through within the next (and actually
1685 + if (PARSE_EXECUTE == ctx->state ||
1686 + PARSE_DIRECTIVE_POSTTAIL == ctx->state) {
1687 + APR_BUCKET_INSERT_BEFORE(newb, b);
1690 + break; /* END OF STREAM */
1694 + APR_BRIGADE_INSERT_TAIL(pass_bb, b);
1696 + if (APR_BUCKET_IS_FLUSH(b)) {
1697 + ctx->ctx->output_now = 1;
1705 + /* enough is enough ... */
1706 + if (ctx->ctx->output_now ||
1707 + ctx->ctx->bytes_parsed > AP_MIN_BYTES_TO_WRITE) {
1709 + if (!APR_BRIGADE_EMPTY(pass_bb)) {
1710 + rv = ap_pass_brigade(f->next, pass_bb);
1711 + if (!APR_STATUS_IS_SUCCESS(rv)) {
1712 + apr_brigade_destroy(pass_bb);
1717 + ctx->ctx->output_now = 0;
1718 + ctx->ctx->bytes_parsed = 0;
1721 + /* read the current bucket data */
1723 + if (!ctx->seen_eos) {
1724 + if (ctx->ctx->bytes_parsed > 0) {
1725 + rv = apr_bucket_read(b, &data, &len, APR_NONBLOCK_READ);
1726 + if (APR_STATUS_IS_EAGAIN(rv)) {
1727 + ctx->ctx->output_now = 1;
1732 + if (!len || !APR_STATUS_IS_SUCCESS(rv)) {
1733 + rv = apr_bucket_read(b, &data, &len, APR_BLOCK_READ);
1736 + if (!APR_STATUS_IS_SUCCESS(rv)) {
1737 + apr_brigade_destroy(pass_bb);
1741 - /* Set aside the partial tag
1742 - * Exception: if there's an EOS at the end of this brigade,
1743 - * the tag will never be completed, so send an error and EOS
1745 - last = APR_BRIGADE_LAST(tag_and_after);
1746 - if (APR_BUCKET_IS_EOS(last)) {
1747 - /* Remove everything before the EOS (i.e., the partial tag)
1748 - * and replace it with an error msg */
1750 - apr_bucket *err_bucket = NULL;
1751 - for (b = APR_BRIGADE_FIRST(tag_and_after);
1752 - !APR_BUCKET_IS_EOS(b);
1753 - b = APR_BRIGADE_FIRST(tag_and_after)) {
1755 + ctx->ctx->bytes_parsed += len;
1758 + /* zero length bucket, fetch next one */
1759 + if (!len && !ctx->seen_eos) {
1760 + b = APR_BUCKET_NEXT(b);
1765 + * it's actually a data containing bucket, start/continue parsing
1768 + switch (ctx->state) {
1769 + /* no current tag; search for start sequence */
1770 + case PARSE_PRE_HEAD:
1771 + index = find_start_sequence(ctx, data, len);
1773 + if (index < len) {
1774 + apr_bucket_split(b, index);
1777 + newb = APR_BUCKET_NEXT(b);
1778 + if (ctx->ctx->flags & FLAG_PRINTING) {
1779 + APR_BUCKET_REMOVE(b);
1780 + APR_BRIGADE_INSERT_TAIL(pass_bb, b);
1783 + apr_bucket_delete(b);
1786 + if (index < len) {
1787 + /* now delete the start_seq stuff from the remaining bucket */
1788 + if (PARSE_DIRECTIVE == ctx->state) { /* full match */
1789 + apr_bucket_split(newb, ctx->ctx->start_seq_len);
1790 + ctx->ctx->output_now = 1; /* pass pre-tag stuff */
1793 + b = APR_BUCKET_NEXT(newb);
1794 + apr_bucket_delete(newb);
1802 + /* we're currently looking for the end of the start sequence */
1804 + index = find_partial_start_sequence(ctx, data, len, &release);
1806 + /* check if we mismatched earlier and have to release some chars */
1807 + if (release && (ctx->ctx->flags & FLAG_PRINTING)) {
1808 + char *to_release = apr_palloc(ctx->ctx->pool, release);
1810 + memcpy(to_release, ctx->ctx->start_seq, release);
1811 + newb = apr_bucket_pool_create(to_release, release,
1813 + f->c->bucket_alloc);
1814 + APR_BRIGADE_INSERT_TAIL(pass_bb, newb);
1817 + if (index) { /* any match */
1818 + /* now delete the start_seq stuff from the remaining bucket */
1819 + if (PARSE_DIRECTIVE == ctx->state) { /* final match */
1820 + apr_bucket_split(b, index);
1821 + ctx->ctx->output_now = 1; /* pass pre-tag stuff */
1823 + newb = APR_BUCKET_NEXT(b);
1824 + apr_bucket_delete(b);
1830 + /* we're currently grabbing the directive name */
1831 + case PARSE_DIRECTIVE:
1832 + case PARSE_DIRECTIVE_POSTNAME:
1833 + case PARSE_DIRECTIVE_TAIL:
1834 + case PARSE_DIRECTIVE_POSTTAIL:
1835 + index = find_directive(ctx, data, len, &store, &store_len);
1838 + apr_bucket_split(b, index);
1839 + newb = APR_BUCKET_NEXT(b);
1844 APR_BUCKET_REMOVE(b);
1845 - apr_bucket_destroy(b);
1846 + APR_BRIGADE_INSERT_TAIL(ctx->tmp_bb, b);
1850 + /* time for cleanup? */
1851 + if (store != &magic) {
1852 + apr_brigade_pflatten(ctx->tmp_bb, store, store_len,
1854 + apr_brigade_cleanup(ctx->tmp_bb);
1858 + apr_bucket_delete(b);
1864 + /* skip WS and find out what comes next (arg or end_seq) */
1865 + case PARSE_PRE_ARG:
1866 + index = find_arg_or_tail(ctx, data, len);
1868 + if (index) { /* skipped whitespaces */
1869 + if (index < len) {
1870 + apr_bucket_split(b, index);
1872 + newb = APR_BUCKET_NEXT(b);
1873 + apr_bucket_delete(b);
1879 + /* currently parsing name[=val] */
1881 + case PARSE_ARG_NAME:
1882 + case PARSE_ARG_POSTNAME:
1883 + case PARSE_ARG_EQ:
1884 + case PARSE_ARG_PREVAL:
1885 + case PARSE_ARG_VAL:
1886 + case PARSE_ARG_VAL_ESC:
1887 + case PARSE_ARG_POSTVAL:
1888 + index = find_argument(ctx, data, len, &store, &store_len);
1891 + apr_bucket_split(b, index);
1892 + newb = APR_BUCKET_NEXT(b);
1897 + APR_BUCKET_REMOVE(b);
1898 + APR_BRIGADE_INSERT_TAIL(ctx->tmp_bb, b);
1902 + /* time for cleanup? */
1903 + if (store != &magic) {
1904 + apr_brigade_pflatten(ctx->tmp_bb, store, store_len,
1906 + apr_brigade_cleanup(ctx->tmp_bb);
1910 + apr_bucket_delete(b);
1916 + /* try to match end_seq at current pos. */
1918 + case PARSE_TAIL_SEQ:
1919 + index = find_tail(ctx, data, len);
1921 + switch (ctx->state) {
1922 + case PARSE_EXECUTE: /* full match */
1923 + apr_bucket_split(b, index);
1924 + newb = APR_BUCKET_NEXT(b);
1925 + apr_bucket_delete(b);
1929 + case PARSE_ARG: /* no match */
1930 + /* PARSE_ARG must reparse at the beginning */
1931 + APR_BRIGADE_PREPEND(bb, ctx->tmp_bb);
1932 + b = APR_BRIGADE_FIRST(bb);
1935 + default: /* partial match */
1936 + newb = APR_BUCKET_NEXT(b);
1937 + APR_BUCKET_REMOVE(b);
1938 + APR_BRIGADE_INSERT_TAIL(ctx->tmp_bb, b);
1945 + /* now execute the parsed directive, cleanup the space and
1946 + * start again with PARSE_PRE_HEAD
1948 + case PARSE_EXECUTE:
1949 + /* if there was an error, it was already logged; just stop here */
1951 + if (ctx->ctx->flags & FLAG_PRINTING) {
1952 + SSI_CREATE_ERROR_BUCKET(ctx->ctx, f, pass_bb);
1955 - CREATE_ERROR_BUCKET(ctx, err_bucket, b, err_bucket);
1956 - rv = ap_pass_brigade(f->next, tag_and_after);
1959 - ap_save_brigade(f, &ctx->ssi_tag_brigade,
1960 - &tag_and_after, r->pool);
1961 + include_handler_fn_t *handle_func;
1964 + (include_handler_fn_t *) apr_hash_get(include_hash,
1966 + ctx->ctx->directive_length);
1967 + if (handle_func) {
1968 + apr_bucket *dummy;
1970 + apr_size_t tag_len = 0;
1971 + ssi_arg_item_t *carg = ctx->argv;
1973 + /* legacy wrapper code */
1975 + /* +1 \0 byte (either after tag or value)
1976 + * +1 = byte (before value)
1978 + tag_len += (carg->name ? carg->name_len : 0) +
1979 + (carg->value ? carg->value_len + 1 : 0) + 1;
1980 + carg = carg->next;
1983 + tag = ctx->ctx->combined_tag = ctx->ctx->curr_tag_pos =
1984 + apr_palloc(ctx->dpool, tag_len);
1989 + memcpy(tag, carg->name, carg->name_len);
1990 + tag += carg->name_len;
1992 + if (carg->value) {
1994 + memcpy(tag, carg->value, carg->value_len);
1995 + tag += carg->value_len;
1998 + carg = carg->next;
2000 + ctx->ctx->tag_length = tag_len;
2002 + /* create dummy buckets for backards compat */
2003 + ctx->ctx->head_start_bucket =
2004 + apr_bucket_pool_create(apr_pmemdup(ctx->ctx->pool,
2005 + ctx->ctx->start_seq,
2006 + ctx->ctx->start_seq_len),
2007 + ctx->ctx->start_seq_len,
2009 + f->c->bucket_alloc);
2010 + APR_BRIGADE_INSERT_TAIL(ctx->ctx->ssi_tag_brigade,
2011 + ctx->ctx->head_start_bucket);
2012 + ctx->ctx->tag_start_bucket =
2013 + apr_bucket_pool_create(apr_pmemdup(ctx->ctx->pool,
2014 + ctx->ctx->combined_tag,
2015 + ctx->ctx->tag_length),
2016 + ctx->ctx->tag_length,
2018 + f->c->bucket_alloc);
2019 + APR_BRIGADE_INSERT_TAIL(ctx->ctx->ssi_tag_brigade,
2020 + ctx->ctx->tag_start_bucket);
2021 + ctx->ctx->tail_start_bucket =
2022 + apr_bucket_pool_create(apr_pmemdup(ctx->ctx->pool,
2023 + ctx->ctx->end_seq,
2024 + ctx->end_seq_len),
2027 + f->c->bucket_alloc);
2028 + APR_BRIGADE_INSERT_TAIL(ctx->ctx->ssi_tag_brigade,
2029 + ctx->ctx->tail_start_bucket);
2031 + rv = handle_func(ctx->ctx, &bb, r, f, b, &dummy);
2033 + apr_brigade_cleanup(ctx->ctx->ssi_tag_brigade);
2035 + if (rv != 0 && rv != 1 && rv != -1) {
2036 + apr_brigade_destroy(pass_bb);
2041 + apr_bucket_brigade *remain;
2043 + remain = apr_brigade_split(bb, b);
2044 + APR_BRIGADE_CONCAT(pass_bb, bb);
2049 + ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
2050 + "unknown directive \"%s\" in parsed doc %s",
2051 + apr_pstrmemdup(r->pool, ctx->directive,
2052 + ctx->ctx->directive_length),
2054 + if (ctx->ctx->flags & FLAG_PRINTING) {
2055 + SSI_CREATE_ERROR_BUCKET(ctx->ctx, f, pass_bb);
2059 - if (rv != APR_SUCCESS) {
2063 + apr_pool_clear(ctx->dpool);
2064 + apr_brigade_cleanup(ctx->tmp_bb);
2066 + /* Oooof. Done here, start next round */
2067 + ctx->state = PARSE_PRE_HEAD;
2071 + } /* while (brigade) */
2073 + /* End of stream. Final cleanup */
2074 + if (ctx->seen_eos) {
2075 + if (PARSE_HEAD == ctx->state) {
2076 + if (ctx->ctx->flags & FLAG_PRINTING) {
2077 + char *to_release = apr_palloc(ctx->ctx->pool,
2078 + ctx->ctx->parse_pos);
2080 + memcpy(to_release, ctx->ctx->start_seq, ctx->ctx->parse_pos);
2081 + APR_BRIGADE_INSERT_TAIL(pass_bb,
2082 + apr_bucket_pool_create(to_release,
2083 + ctx->ctx->parse_pos, ctx->ctx->pool,
2084 + f->c->bucket_alloc));
2086 - ctx->bytes_parsed = 0;
2088 + else if (PARSE_PRE_HEAD != ctx->state) {
2089 + ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
2090 + "SSI directive was not properly finished at the end "
2091 + "of parsed document %s", r->filename);
2092 + if (ctx->ctx->flags & FLAG_PRINTING) {
2093 + SSI_CREATE_ERROR_BUCKET(ctx->ctx, f, pass_bb);
2097 + if (!(ctx->ctx->flags & FLAG_PRINTING)) {
2098 + ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r,
2099 + "missing closing endif directive in parsed document"
2100 + " %s", r->filename);
2103 + /* cleanup our temporary memory */
2104 + apr_brigade_destroy(ctx->tmp_bb);
2105 + apr_pool_destroy(ctx->dpool);
2107 + /* don't forget to finally insert the EOS bucket */
2108 + APR_BRIGADE_INSERT_TAIL(pass_bb, b);
2110 - return APR_SUCCESS;
2112 + /* if something's left over, pass it along */
2113 + if (!APR_BRIGADE_EMPTY(pass_bb)) {
2114 + rv = ap_pass_brigade(f->next, pass_bb);
2120 + apr_brigade_destroy(pass_bb);
2124 static void *create_includes_dir_config(apr_pool_t *p, char *dummy)
2125 @@ -3335,14 +3544,20 @@
2126 || !(f->r->finfo.protection & APR_GEXECUTE)) {
2127 f->r->no_local_copy = 1;
2131 + /* Don't allow ETag headers to be generated - see RFC2616 - 13.3.4.
2132 + * We don't know if we are going to be including a file or executing
2133 + * a program - in either case a strong ETag header will likely be invalid.
2135 + apr_table_setn(f->r->notes, "no-etag", "");
2140 static apr_status_t includes_filter(ap_filter_t *f, apr_bucket_brigade *b)
2142 request_rec *r = f->r;
2143 - include_ctx_t *ctx = f->ctx;
2144 + ssi_ctx_t *ctx = f->ctx;
2145 request_rec *parent;
2146 include_dir_config *conf =
2147 (include_dir_config *)ap_get_module_config(r->per_dir_config,
2148 @@ -3352,30 +3567,55 @@
2151 if (!(ap_allow_options(r) & OPT_INCLUDES)) {
2152 + ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r,
2153 + "mod_include: Options +Includes (or IncludesNoExec) "
2154 + "wasn't set, INCLUDES filter removed");
2155 + ap_remove_output_filter(f);
2156 return ap_pass_brigade(f->next, b);
2160 - f->ctx = ctx = apr_pcalloc(f->c->pool, sizeof(*ctx));
2161 - ctx->state = PRE_HEAD;
2162 - ctx->flags = (FLAG_PRINTING | FLAG_COND_TRUE);
2163 - if (ap_allow_options(r) & OPT_INCNOEXEC) {
2164 - ctx->flags |= FLAG_NO_EXEC;
2166 - ctx->ssi_tag_brigade = apr_brigade_create(f->c->pool,
2167 - f->c->bucket_alloc);
2168 - ctx->status = APR_SUCCESS;
2170 - ctx->error_str = conf->default_error_msg;
2171 - ctx->time_str = conf->default_time_fmt;
2172 - ctx->pool = f->c->pool;
2173 - ctx->start_seq_pat = &sconf->start_seq_pat;
2174 - ctx->start_seq = sconf->default_start_tag;
2175 - ctx->start_seq_len = sconf->start_tag_len;
2176 - ctx->end_seq = sconf->default_end_tag;
2177 + /* create context for this filter */
2178 + f->ctx = ctx = apr_palloc(f->c->pool, sizeof(*ctx));
2179 + ctx->ctx = apr_pcalloc(f->c->pool, sizeof(*ctx->ctx));
2180 + ctx->ctx->pool = f->r->pool;
2181 + apr_pool_create(&ctx->dpool, ctx->ctx->pool);
2183 + /* configuration data */
2184 + ctx->end_seq_len = strlen(sconf->default_end_tag);
2187 + /* runtime data */
2188 + ctx->tmp_bb = apr_brigade_create(ctx->ctx->pool, f->c->bucket_alloc);
2189 + ctx->seen_eos = 0;
2190 + ctx->state = PARSE_PRE_HEAD;
2191 + ctx->ctx->flags = (FLAG_PRINTING | FLAG_COND_TRUE);
2192 + if (ap_allow_options(f->r) & OPT_INCNOEXEC) {
2193 + ctx->ctx->flags |= FLAG_NO_EXEC;
2195 + ctx->ctx->if_nesting_level = 0;
2196 + ctx->ctx->re_string = NULL;
2197 + ctx->ctx->error_str_override = NULL;
2198 + ctx->ctx->time_str_override = NULL;
2200 + ctx->ctx->error_str = conf->default_error_msg;
2201 + ctx->ctx->time_str = conf->default_time_fmt;
2202 + ctx->ctx->start_seq_pat = &sconf->start_seq_pat;
2203 + ctx->ctx->start_seq = sconf->default_start_tag;
2204 + ctx->ctx->start_seq_len = sconf->start_tag_len;
2205 + ctx->ctx->end_seq = sconf->default_end_tag;
2207 + /* legacy compat stuff */
2208 + ctx->ctx->state = PARSED; /* dummy */
2209 + ctx->ctx->ssi_tag_brigade = apr_brigade_create(f->c->pool,
2210 + f->c->bucket_alloc);
2211 + ctx->ctx->status = APR_SUCCESS;
2212 + ctx->ctx->head_start_index = 0;
2213 + ctx->ctx->tag_start_index = 0;
2214 + ctx->ctx->tail_start_index = 0;
2217 - ctx->bytes_parsed = 0;
2218 + ctx->ctx->bytes_parsed = 0;
2221 if ((parent = ap_get_module_config(r->request_config, &include_module))) {
2222 @@ -3405,14 +3645,13 @@
2224 apr_table_unset(f->r->headers_out, "Content-Length");
2226 - /* Always unset the ETag/Last-Modified fields - see RFC2616 - 13.3.4.
2227 + /* Always unset the Last-Modified field - see RFC2616 - 13.3.4.
2228 * We don't know if we are going to be including a file or executing
2229 * a program which may change the Last-Modified header or make the
2230 * content completely dynamic. Therefore, we can't support these
2232 * Exception: XBitHack full means we *should* set the Last-Modified field.
2234 - apr_table_unset(f->r->headers_out, "ETag");
2236 /* Assure the platform supports Group protections */
2237 if ((*conf->xbithack == xbithack_full)
2238 @@ -3425,7 +3664,17 @@
2239 apr_table_unset(f->r->headers_out, "Last-Modified");
2242 - return send_parsed_content(&b, r, f);
2243 + /* add QUERY stuff to env cause it ain't yet */
2245 + char *arg_copy = apr_pstrdup(r->pool, r->args);
2247 + apr_table_setn(r->subprocess_env, "QUERY_STRING", r->args);
2248 + ap_unescape_url(arg_copy);
2249 + apr_table_setn(r->subprocess_env, "QUERY_STRING_UNESCAPED",
2250 + ap_escape_shell_cmd(r->pool, arg_copy));
2253 + return send_parsed_content(f, b);
2256 static void ap_register_include_handler(char *tag, include_handler_fn_t *func)