]> git.pld-linux.org Git - packages/apache.git/blob - httpd-2.0.48-include.patch
- new
[packages/apache.git] / httpd-2.0.48-include.patch
1  
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]
7
8 --- httpd-2.0.48/modules/filters/mod_include.h.include
9 +++ httpd-2.0.48/modules/filters/mod_include.h
10 @@ -131,7 +131,17 @@
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.
13   */
14 -typedef enum {PRE_HEAD, PARSE_HEAD, PARSE_DIRECTIVE, PARSE_TAG, PARSE_TAIL, PARSED} states;
15 +
16 +/* I keep this stuff here, because of binary compat. It probably doesn't care,
17 + * but who knows ...?
18 + */
19 +#ifdef MOD_INCLUDE_REDESIGN
20 +typedef enum {PRE_HEAD, BLOW_PARSE_HEAD, BLOW_PARSE_DIRECTIVE, PARSE_TAG,
21 +              BLOW_PARSE_TAIL, PARSED} states;
22 +#else
23 +typedef enum {PRE_HEAD, PARSE_HEAD, PARSE_DIRECTIVE, PARSE_TAG, PARSE_TAIL,
24 +              PARSED} states;
25 +#endif
26  
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
31 @@ -1,7 +1,7 @@
32  /* ====================================================================
33   * The Apache Software License, Version 1.1
34   *
35 - * Copyright (c) 2000-2003 The Apache Software Foundation.  All rights
36 + * Copyright (c) 2000-2004 The Apache Software Foundation.  All rights
37   * reserved.
38   *
39   * Redistribution and use in source and binary forms, with or without
40 @@ -73,6 +73,7 @@
41  #include "apr_optional.h"
42  
43  #define APR_WANT_STRFUNC
44 +#define APR_WANT_MEMFUNC
45  #include "apr_want.h"
46  
47  #define CORE_PRIVATE
48 @@ -89,6 +90,8 @@
49  #include "http_main.h"
50  #include "util_script.h"
51  #include "http_core.h"
52 +
53 +#define MOD_INCLUDE_REDESIGN
54  #include "mod_include.h"
55  #include "util_ebcdic.h"
56  
57 @@ -126,6 +129,58 @@
58      int  undefinedEchoLen;
59  } include_server_config;
60  
61 +/* main parser states */
62 +typedef enum {
63 +    PARSE_PRE_HEAD,
64 +    PARSE_HEAD,
65 +    PARSE_DIRECTIVE,
66 +    PARSE_DIRECTIVE_POSTNAME,
67 +    PARSE_DIRECTIVE_TAIL,
68 +    PARSE_DIRECTIVE_POSTTAIL,
69 +    PARSE_PRE_ARG,
70 +    PARSE_ARG,
71 +    PARSE_ARG_NAME,
72 +    PARSE_ARG_POSTNAME,
73 +    PARSE_ARG_EQ,
74 +    PARSE_ARG_PREVAL,
75 +    PARSE_ARG_VAL,
76 +    PARSE_ARG_VAL_ESC,
77 +    PARSE_ARG_POSTVAL,
78 +    PARSE_TAIL,
79 +    PARSE_TAIL_SEQ,
80 +    PARSE_EXECUTE
81 +} parse_state_t;
82 +
83 +typedef struct ssi_arg_item {
84 +    struct ssi_arg_item *next;
85 +    char                *name;
86 +    apr_size_t           name_len;
87 +    char                *value;
88 +    apr_size_t           value_len;
89 +} ssi_arg_item_t;
90 +
91 +typedef struct {
92 +    parse_state_t state;
93 +    int           seen_eos;
94 +    int           error;
95 +    char          quote;         /* quote character value (or \0) */
96 +
97 +    apr_bucket_brigade *tmp_bb;
98 +
99 +    apr_size_t    end_seq_len;
100 +    char         *directive;     /* name of the current directive */
101 +
102 +    unsigned        argc;        /* argument counter (of the current
103 +                                  * directive)
104 +                                  */
105 +    ssi_arg_item_t *argv;        /* all arguments */
106 +    ssi_arg_item_t *current_arg; /* currently parsed argument */
107 +    request_rec    *r;
108 +    include_ctx_t  *ctx;         /* public part of the context structure */
109 +
110 +    apr_pool_t     *dpool;
111 +} ssi_ctx_t;
112 +
113  #ifdef XBITHACK
114  #define DEFAULT_XBITHACK xbithack_full
115  #else
116 @@ -134,6 +189,11 @@
117  
118  #define BYTE_COUNT_THRESHOLD AP_MIN_BYTES_TO_WRITE
119  
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))
124 +
125  /* ------------------------ Environment function -------------------------- */
126  
127  /* Sentinel value to store in subprocess_env for items that
128 @@ -316,463 +376,6 @@
129      return hl;
130  }
131  
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,
136 -                                        apr_size_t len)
137 -{
138 -    /* We want to split the bucket at the '<'. */
139 -    ctx->state = PARSE_DIRECTIVE;
140 -    ctx->tag_length = 0;
141 -    ctx->parse_pos = 0;
142 -
143 -    /* If tagStart indexes the end of the bucket, then tag_start_bucket
144 -     * should be the next bucket
145 -     */
146 -    if (tagStart < len) {
147 -        ctx->tag_start_bucket = dptr;
148 -        ctx->tag_start_index = tagStart;
149 -    }
150 -    else {
151 -        ctx->tag_start_bucket = APR_BUCKET_NEXT(dptr);
152 -        ctx->tag_start_index = 0;
153 -    }
154 -
155 -    if (ctx->head_start_index > 0) {
156 -        apr_bucket *tmp_bkt;
157 -
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;
165 -        }
166 -        ctx->head_start_bucket = tmp_bkt;
167 -        ctx->head_start_index = 0;
168 -    }
169 -    return ctx->head_start_bucket;
170 -}
171 -
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.
175 - */
176 -static apr_bucket *find_start_sequence(apr_bucket *dptr, include_ctx_t *ctx,
177 -                                       apr_bucket_brigade *bb, int *do_cleanup)
178 -{
179 -    apr_size_t len;
180 -    const char *c;
181 -    const char *buf;
182 -    const char *str = ctx->start_seq ;
183 -    apr_size_t slen = ctx->start_seq_len;
184 -    apr_size_t pos;
185 -
186 -    *do_cleanup = 0;
187 -
188 -    do {
189 -        apr_status_t rv = 0;
190 -        int read_done = 0;
191 -
192 -        if (APR_BUCKET_IS_EOS(dptr)) {
193 -            break;
194 -        }
195 -
196 -#if 0
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;
205 -        }
206 -        else
207 -#endif /* 0 */
208 -        if (ctx->bytes_parsed >= BYTE_COUNT_THRESHOLD) {
209 -            ctx->output_now = 1;
210 -        }
211 -        else if (ctx->bytes_parsed > 0) {
212 -            rv = apr_bucket_read(dptr, &buf, &len, APR_NONBLOCK_READ);
213 -            read_done = 1;
214 -            if (APR_STATUS_IS_EAGAIN(rv)) {
215 -                ctx->output_now = 1;
216 -            }
217 -        }
218 -
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;
229 -            }
230 -            else {
231 -                start_bucket = dptr;
232 -            }
233 -            return start_bucket;
234 -        }
235 -
236 -        if (!read_done) {
237 -            rv = apr_bucket_read(dptr, &buf, &len, APR_BLOCK_READ);
238 -        }
239 -        if (!APR_STATUS_IS_SUCCESS(rv)) {
240 -            ctx->status = rv;
241 -            return NULL;
242 -        }
243 -
244 -        if (len == 0) { /* end of pipe? */
245 -            dptr = APR_BUCKET_NEXT(dptr);
246 -            continue;
247 -        }
248 -
249 -        /* Set our buffer to use. */
250 -        c = buf;
251 -
252 -        /* The last bucket had a left over partial match that we need to
253 -         * complete. 
254 -         */
255 -        if (ctx->state == PARSE_HEAD)
256 -        {
257 -            apr_size_t tmpLen;
258 -            tmpLen = (len < (slen - 1)) ? len : (slen - 1);
259 -
260 -            while (c < buf + tmpLen && *c == str[ctx->parse_pos])
261 -            {
262 -                c++; 
263 -                ctx->parse_pos++;
264 -            }
265 -
266 -            if (str[ctx->parse_pos] == '\0')
267 -            {
268 -                ctx->bytes_parsed += c - buf;
269 -                return found_start_sequence(dptr, ctx, c - buf, len);
270 -            }
271 -            else if (c == buf + tmpLen) {
272 -                dptr = APR_BUCKET_NEXT(dptr);
273 -                continue;
274 -            }
275 -
276 -            /* False alarm... 
277 -             */
278 -            APR_BRIGADE_PREPEND(bb, ctx->ssi_tag_brigade);
279 -
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.
286 -             */
287 -            ctx->state = PRE_HEAD;
288 -            ctx->head_start_bucket = NULL;
289 -            ctx->head_start_index = 0;
290 -        }
291 -
292 -        if (len)
293 -        {
294 -            pos = bndm(str, slen, buf, len, ctx->start_seq_pat);
295 -            if (pos != len)
296 -            {
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);
301 -            }
302 -        }
303 -        
304 -        /* Consider the case where we have <!-- at the end of the bucket. */
305 -        if (len > slen) {
306 -            ctx->bytes_parsed += (len - slen);
307 -            c = buf + len - slen;
308 -        }
309 -        else {
310 -            c = buf;
311 -        }
312 -        ctx->parse_pos = 0;
313 -
314 -        while (c < buf + len)
315 -        {
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;
321 -                }
322 -                ctx->parse_pos++;
323 -                c++;
324 -                ctx->bytes_parsed++;
325 -            }
326 -            else if (ctx->parse_pos != 0) 
327 -            {
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.
341 -                 */
342 -                *do_cleanup = 1;
343 -                ctx->parse_pos = 0;
344 -                ctx->state = PRE_HEAD;
345 -                ctx->head_start_bucket = NULL;
346 -                ctx->head_start_index = 0;
347 -            }
348 -            else {
349 -               c++;
350 -               ctx->bytes_parsed++;
351 -            }
352 -        }
353 -        dptr = APR_BUCKET_NEXT(dptr);
354 -    } while (dptr != APR_BRIGADE_SENTINEL(bb));
355 -          
356 -  
357 -    return NULL;
358 -}
359 -
360 -static apr_bucket *find_end_sequence(apr_bucket *dptr, include_ctx_t *ctx, 
361 -                                     apr_bucket_brigade *bb)
362 -{
363 -    apr_size_t len;
364 -    const char *c;
365 -    const char *buf;
366 -    const char *str = ctx->end_seq;
367 -    const char *start;
368 -
369 -    do {
370 -        apr_status_t rv = 0;
371 -        int read_done = 0;
372 -
373 -        if (APR_BUCKET_IS_EOS(dptr)) {
374 -            break;
375 -        }
376 -#if 0
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;
385 -        }
386 -        else
387 -#endif /* 0 */
388 -        if (ctx->bytes_parsed >= BYTE_COUNT_THRESHOLD) {
389 -            ctx->output_now = 1;
390 -        }
391 -        else if (ctx->bytes_parsed > 0) {
392 -            rv = apr_bucket_read(dptr, &buf, &len, APR_NONBLOCK_READ);
393 -            read_done = 1;
394 -            if (APR_STATUS_IS_EAGAIN(rv)) {
395 -                ctx->output_now = 1;
396 -            }
397 -        }
398 -
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;
404 -            }
405 -            return dptr;
406 -        }
407 -
408 -        if (!read_done) {
409 -            rv = apr_bucket_read(dptr, &buf, &len, APR_BLOCK_READ);
410 -        }
411 -        if (!APR_STATUS_IS_SUCCESS(rv)) {
412 -            ctx->status = rv;
413 -            return NULL;
414 -        }
415 -
416 -        if (len == 0) { /* end of pipe? */
417 -            dptr = APR_BUCKET_NEXT(dptr);
418 -            continue;
419 -        }
420 -        if (dptr == ctx->tag_start_bucket) {
421 -            c = buf + ctx->tag_start_index;
422 -        }
423 -        else {
424 -            c = buf;
425 -        }
426 -        start = c;
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;
433 -                }
434 -                ctx->parse_pos++;
435 -                if (str[ctx->parse_pos] == '\0') {
436 -                        apr_bucket *tmp_buck = dptr;
437 -
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.
441 -                         */
442 -                        c++;
443 -                        ctx->bytes_parsed += (c - start);
444 -                        ctx->state = PARSED;
445 -                        apr_bucket_split(dptr, c - buf);
446 -                        tmp_buck = APR_BUCKET_NEXT(dptr);
447 -                        return (tmp_buck);
448 -                    }           
449 -            }
450 -            else {
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;
457 -                            do {
458 -                                c++;
459 -                            } while ((c < buf + len) && !apr_isspace(*c) &&
460 -                                     *c != *str);
461 -                            ctx->tag_length = ctx->directive_length = c - tmp;
462 -                            continue;
463 -                        }
464 -                    }
465 -                    else {
466 -                        if (!apr_isspace(*c)) {
467 -                            ctx->directive_length++;
468 -                        }
469 -                        else {
470 -                            ctx->state = PARSE_TAG;
471 -                        }
472 -                        ctx->tag_length++;
473 -                    }
474 -                }
475 -                else if (ctx->state == PARSE_TAG) {
476 -                    const char *tmp = c;
477 -                    do {
478 -                        c++;
479 -                    } while ((c < buf + len) && (*c != *str));
480 -                    ctx->tag_length += (c - tmp);
481 -                    continue;
482 -                }
483 -                else {
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 
489 -                         * this.
490 -                         */
491 -                         ctx->tag_length += ctx->parse_pos;
492 -
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;
498 -                         }
499 -                         else {
500 -                             ctx->tag_length++;
501 -                             if (ctx->tag_length > ctx->directive_length) {
502 -                                 ctx->state = PARSE_TAG;
503 -                             }
504 -                             else {
505 -                                 ctx->state = PARSE_DIRECTIVE;
506 -                                 ctx->directive_length += ctx->parse_pos;
507 -                             }
508 -                             ctx->tail_start_bucket = NULL;
509 -                             ctx->tail_start_index = 0;
510 -                             ctx->parse_pos = 0;
511 -                         }
512 -                    }
513 -                }
514 -            }
515 -            c++;
516 -        }
517 -        ctx->bytes_parsed += (c - start);
518 -        dptr = APR_BUCKET_NEXT(dptr);
519 -    } while (dptr != APR_BRIGADE_SENTINEL(bb));
520 -    return NULL;
521 -}
522 -
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).
526 - */
527 -static apr_status_t get_combined_directive (include_ctx_t *ctx,
528 -                                            request_rec *r,
529 -                                            apr_bucket_brigade *bb,
530 -                                            char *tmp_buf, 
531 -                                            apr_size_t tmp_buf_size)
532 -{
533 -    int        done = 0;
534 -    apr_bucket *dptr;
535 -    const char *tmp_from;
536 -    apr_size_t tmp_from_len;
537 -
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);
543 -        }
544 -    }     /* Else, just use the temp buffer. */
545 -    else {
546 -        ctx->combined_tag = tmp_buf;
547 -    }
548 -
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);
553 -
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);
557 -    }
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;
561 -
562 -    /* Loop through the buckets from the tag_start_bucket until before
563 -     * the tail_start_bucket copying the contents into the buffer.
564 -     */
565 -    do {
566 -        memcpy (ctx->curr_tag_pos, tmp_from, tmp_from_len);
567 -        ctx->curr_tag_pos += tmp_from_len;
568 -
569 -        if (dptr == ctx->tail_start_bucket) {
570 -            done = 1;
571 -        }
572 -        else {
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);
578 -            }
579 -        }
580 -    } while ((!done) &&
581 -             (ctx->curr_tag_pos < ctx->combined_tag + ctx->tag_length));
582 -
583 -    ctx->combined_tag[ctx->tag_length] = '\0';
584 -    ctx->curr_tag_pos = ctx->combined_tag;
585 -
586 -    return (APR_SUCCESS);
587 -}
588 -
589  /*
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)
595  {
596 -    char *c = ctx->curr_tag_pos;
597 -    int   shift_val = 0; 
598 -    char  term = '\0';
599 -
600      *tag_val = NULL;
601 -    if (ctx->curr_tag_pos > ctx->combined_tag + ctx->tag_length) {
602 +    if (ctx->curr_tag_pos >= ctx->combined_tag + ctx->tag_length) {
603          *tag = NULL;
604          return;
605      }
606 -    SKIP_TAG_WHITESPACE(c);
607 -    *tag = c;             /* First non-whitespace character (could be NULL). */
608 -
609 -    while (apr_islower(*c)) {
610 -        c++;  /* Optimization for the common case where the tag */
611 -    }         /* is already lowercase */
612  
613 -    while ((*c != '=') && (!apr_isspace(*c)) && (*c != '\0')) {
614 -        *c = apr_tolower(*c);    /* find end of tag, lowercasing as we go... */
615 -        c++;
616 +    *tag = ctx->curr_tag_pos;
617 +    if (!**tag) {
618 +        *tag = NULL;
619 +        /* finitio */
620 +        ctx->curr_tag_pos = ctx->combined_tag + ctx->tag_length;
621 +        return;
622      }
623  
624 -    if ((*c == '\0') || (**tag == '=')) {
625 -        if ((**tag == '\0') || (**tag == '=')) {
626 -            *tag = NULL;
627 -        }
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. */
631 -
632 -    if (*c == '=') {
633 -        *c++ = '\0'; /* Overwrite the '=' with a terminating byte after tag. */
634 -    }
635 -    else {                               /* Try skipping WS to find the '='. */
636 -        *c++ = '\0';                                 /* Terminate the tag... */
637 -        SKIP_TAG_WHITESPACE(c);
638 -        
639 -        /* There needs to be an equal sign if there's a value. */
640 -        if (*c != '=') {
641 -            ctx->curr_tag_pos = c;
642 -            return; /* There apparently was no value. */
643 -        }
644 -        else {
645 -            c++; /* Skip the equals sign. */
646 -        }
647 +    *tag_val = ap_strchr(*tag, '=');
648 +    if (!*tag_val) {
649 +        ctx->curr_tag_pos = ctx->combined_tag + ctx->tag_length;
650 +        return;
651      }
652  
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.
657 -         */
658 -        term = *c++;
659 -    }
660 -    
661 -    *tag_val = c;
662 -    if (!term) {
663 -        while (!apr_isspace(*c) && (*c != '\0')) {
664 -            c++;
665 -        }
666 +    /* if it starts with '=' there was no tag name, just a value */
667 +    if (*tag_val == *tag) {
668 +        *tag = NULL;
669      }
670 -    else {
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.
676 -             */
677 -            c++;
678 -        }
679 -        if (*c == '\\') {
680 -            do {
681 -                /* Accept \" (or ' or `) as valid quotation of string. 
682 -                 */
683 -                if (*c == '\\') {  
684 -                    /* Overwrite the "\" during the embedded 
685 -                     * escape sequence of '"'. "\'" or '`'. 
686 -                     * Shift bytes from here to next delimiter.     
687 -                     */
688 -                    c++;
689 -                    if (*c == term) {
690 -                        shift_val++;
691 -                    }
692 -                    if (shift_val > 0) {
693 -                        *(c-shift_val) = *c;
694 -                    }
695 -                    if (*c == '\0') {
696 -                        break;
697 -                    }
698 -                }
699  
700 -                c++;
701 -                if (shift_val > 0) {
702 -                    *(c-shift_val) = *c;
703 -                }
704 -            } while ((*c != term) && (*c != '\0'));
705 -        }
706 -    }
707 -    
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 */
712 +
713      if (dodecode) {
714          decodehtml(*tag_val);
715      }
716 @@ -1354,6 +885,7 @@
717                              "unknown parameter \"%s\" to tag include in %s",
718                              tag, r->filename);
719                  CREATE_ERROR_BUCKET(ctx, tmp_buck, head_ptr, *inserted_head);
720 +                return 1;
721              }
722          }
723      }
724 @@ -1432,6 +964,7 @@
725                             "tag echo in %s", tag_val, r->filename);
726                      CREATE_ERROR_BUCKET(ctx, tmp_buck, head_ptr, 
727                                          *inserted_head);
728 +                    return 1;
729                  }
730              }
731              else {
732 @@ -1439,6 +972,7 @@
733                              "unknown parameter \"%s\" in tag echo of %s",
734                              tag, r->filename);
735                  CREATE_ERROR_BUCKET(ctx, tmp_buck, head_ptr, *inserted_head);
736 +                return 1;
737              }
738  
739          }
740 @@ -1514,6 +1048,7 @@
741                                "unknown parameter \"%s\" to tag config in %s",
742                                tag, r->filename);
743                  CREATE_ERROR_BUCKET(ctx, tmp_buck, head_ptr, *inserted_head);
744 +                return 1;
745              }
746          }
747      }
748 @@ -1663,6 +1198,7 @@
749                  else {
750                      CREATE_ERROR_BUCKET(ctx, tmp_buck, head_ptr, 
751                                          *inserted_head);
752 +                    return 1;
753                  }
754              }
755          }
756 @@ -1712,6 +1248,7 @@
757                  else {
758                      CREATE_ERROR_BUCKET(ctx, tmp_buck, head_ptr, 
759                                          *inserted_head);
760 +                    return 1;
761                  }
762              }
763          }
764 @@ -2642,6 +2179,7 @@
765                              "unknown parameter \"%s\" to tag if in %s", tag, 
766                              r->filename);
767                  CREATE_ERROR_BUCKET(ctx, tmp_buck, head_ptr, *inserted_head);
768 +                return 1;
769              }
770  
771          }
772 @@ -2678,7 +2216,7 @@
773                                    r->filename);
774                      CREATE_ERROR_BUCKET(ctx, tmp_buck, head_ptr, 
775                                          *inserted_head);
776 -                    return (1);
777 +                    return 1;
778                  }
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, 
783                                 r->filename);
784                  CREATE_ERROR_BUCKET(ctx, tmp_buck, head_ptr, *inserted_head);
785 +                return 1;
786              }
787          }
788      }
789 @@ -2914,362 +2453,1032 @@
790  
791  /* -------------------------- The main function --------------------------- */
792  
793 -static apr_status_t send_parsed_content(apr_bucket_brigade **bb, 
794 -                                        request_rec *r, ap_filter_t *f)
795 +/*
796 + * returns the index position of the first byte of start_seq (or the len of
797 + * the buffer as non-match)
798 + */
799 +static apr_size_t find_start_sequence(ssi_ctx_t *ctx, const char *data,
800 +                                      apr_size_t len)
801  {
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;
807 -
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;
811 +    apr_size_t index;
812 +    const char *p, *ep;
813  
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));
818 +    if (len < slen) {
819 +        p = data; /* try partial match at the end of the buffer (below) */
820      }
821 +    else {
822 +        /* try fast bndm search over the buffer
823 +         * (hopefully the whole start sequence can be found in this buffer)
824 +         */
825 +        index = bndm(ctx->ctx->start_seq, ctx->ctx->start_seq_len, data, len,
826 +                     ctx->ctx->start_seq_pat);
827  
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;
833 -
834 -            tmp_dptr = find_start_sequence(dptr, ctx, *bb, &do_cleanup);
835 -            if (!APR_STATUS_IS_SUCCESS(ctx->status)) {
836 -                return ctx->status;
837 -            }
838 -
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. */
843 +        if (index < len) {
844 +            ctx->state = PARSE_DIRECTIVE;
845 +            return index;
846 +        }
847 +        else {
848 +            /* ok, the pattern can't be found as whole in the buffer,
849 +             * check the end for a partial match
850               */
851 -            if ((do_cleanup) && (!APR_BRIGADE_EMPTY(ctx->ssi_tag_brigade))) {
852 -                apr_bucket *tmp_bkt;
853 +            p = data + len - slen + 1;
854 +        }
855 +    }
856  
857 -                tmp_bkt = apr_bucket_immortal_create(ctx->start_seq,
858 -                                                  cleanup_bytes,
859 -                                                  r->connection->bucket_alloc);
860 -                APR_BRIGADE_INSERT_HEAD(*bb, tmp_bkt);
861 -                apr_brigade_cleanup(ctx->ssi_tag_brigade);
862 -            }
863 +    ep = data + len;
864 +    do {
865 +        while (p < ep && *p != *ctx->ctx->start_seq) {
866 +            ++p;
867 +        }
868  
869 -            /* If I am inside a conditional (if, elif, else) that is false
870 -             *   then I need to throw away anything contained in it.
871 -             */
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
876 -                                   : tmp_dptr;
877 +        index = p - data;
878  
879 -                while ((dptr != APR_BRIGADE_SENTINEL(*bb)) && (dptr != stop)) {
880 -                    apr_bucket *free_bucket = dptr;
881 +        /* found a possible start_seq start */
882 +        if (p < ep) {
883 +            apr_size_t pos = 1;
884  
885 -                    dptr = APR_BUCKET_NEXT(dptr);
886 -                    if (!APR_BUCKET_IS_METADATA(free_bucket)) {
887 -                        apr_bucket_delete(free_bucket);
888 -                    }
889 -                }
890 +            ++p;
891 +            while (p < ep && *p == ctx->ctx->start_seq[pos]) {
892 +                ++p;
893 +                ++pos;
894              }
895  
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;
900 -                }
901 -                else {
902 -                    dptr = APR_BRIGADE_SENTINEL(*bb);
903 -                }
904 +            /* partial match found. Store the info for the next round */
905 +            if (p == ep) {
906 +                ctx->state = PARSE_HEAD;
907 +                ctx->ctx->parse_pos = pos;
908 +                return index;
909              }
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));
917 -                }
918 +        }
919  
920 -                rv = ap_pass_brigade(f->next, *bb);
921 -                if (rv != APR_SUCCESS) {
922 -                    return rv;
923 -                }
924 -                *bb  = tag_and_after;
925 -                dptr = tmp_dptr;
926 -                ctx->output_flush = 0;
927 -                ctx->bytes_parsed = 0;
928 -                ctx->output_now = 0;
929 -            }
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);  
934 -            }
935 +        /* we must try all combinations; consider (e.g.) SSIStartTag "--->"
936 +         * and a string data of "--.-" and the end of the buffer
937 +         */
938 +        p = data + index + 1;
939 +    } while (p < ep);
940 +
941 +    /* no match */
942 +    return len;
943 +}
944 +
945 +/*
946 + * returns the first byte *after* the partial (or final) match.
947 + *
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.
951 + */
952 +static apr_size_t find_partial_start_sequence(ssi_ctx_t *ctx,
953 +                                              const char *data,
954 +                                              apr_size_t len,
955 +                                              apr_size_t *release)
956 +{
957 +    apr_size_t pos, spos = 0;
958 +    apr_size_t slen = ctx->ctx->start_seq_len;
959 +    const char *p, *ep;
960 +
961 +    pos = ctx->ctx->parse_pos;
962 +    ep = data + len;
963 +    *release = 0;
964 +
965 +    do {
966 +        p = data;
967 +
968 +        while (p < ep && pos < slen && *p == ctx->ctx->start_seq[pos]) {
969 +            ++p;
970 +            ++pos;
971          }
972  
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;
981 -            }
982 +        /* full match */
983 +        if (pos == slen) {
984 +            ctx->state = PARSE_DIRECTIVE;
985 +            return (p - data);
986 +        }
987  
988 -            if (tmp_dptr != NULL) {
989 -                dptr = tmp_dptr;  /* Adjust bucket pos... */
990 -                
991 -                /* If some of the tag has already been set aside then set
992 -                 * aside remainder of tag. Now the full tag is in 
993 -                 * ssi_tag_brigade.
994 -                 * If none has yet been set aside, then leave it all where it 
995 -                 * is.
996 -                 * In any event after this the entire set of tag buckets will 
997 -                 * be in one place or another.
998 -                 */
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;
1003 -                }
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) {
1008 -                        return rv;
1009 -                    }
1010 -                    ctx->output_flush = 0;
1011 -                    ctx->output_now = 0;
1012 -                }
1013 -            }
1014 -            else {
1015 -                /* remainder of this brigade...    */
1016 -                dptr = APR_BRIGADE_SENTINEL(*bb);  
1017 -            }
1018 +        /* the whole buffer is a partial match */
1019 +        if (p == ep) {
1020 +            ctx->ctx->parse_pos = pos;
1021 +            return (p - data);
1022          }
1023  
1024 -        /* State to processed the directive... */
1025 -        if (ctx->state == PARSED) {
1026 -            apr_bucket    *content_head = NULL, *tmp_bkt;
1027 -            apr_size_t    tmp_i;
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 *,
1031 -                               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
1035 +         *
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)
1040 +         */
1041 +        if (spos < ctx->ctx->parse_pos) {
1042 +            do {
1043 +                ++spos;
1044 +                ++*release;
1045 +                p = ctx->ctx->start_seq + spos;
1046 +                pos = ctx->ctx->parse_pos - spos;
1047 +
1048 +                while (pos && *p != *ctx->ctx->start_seq) {
1049 +                    ++p;
1050 +                    ++spos;
1051 +                    ++*release;
1052 +                    --pos;
1053 +                }
1054  
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.
1058 -             */
1059 +                /* if a matching beginning char was found, try to match the
1060 +                 * remainder of the old buffer.
1061 +                 */
1062 +                if (pos > 1) {
1063 +                    apr_size_t t = 1;
1064  
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.
1068 -             */
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",
1073 -                            r->filename);
1074 -                CREATE_ERROR_BUCKET(ctx, tmp_bkt, dptr, content_head);
1075 +                    ++p;
1076 +                    while (t < pos && *p == ctx->ctx->start_seq[t]) {
1077 +                        ++p;
1078 +                        ++t;
1079 +                    }
1080  
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);
1085 -                }
1086 -                else {
1087 -                    do {
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)));
1093 +                    if (t == pos) {
1094 +                        /* yeah, another partial match found in the *old*
1095 +                         * buffer, now test the *current* buffer for
1096 +                         * continuing match
1097 +                         */
1098 +                        break;
1099 +                    }
1100                  }
1101 +            } while (pos > 1);
1102  
1103 -                return APR_SUCCESS;
1104 +            if (pos) {
1105 +                continue;
1106              }
1107 +        }
1108  
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!
1112 -             */
1113 +        break;
1114 +    } while (1); /* work hard to find a match ;-) */
1115  
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.
1120 -             */
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]);
1124 -            }
1125 -            ctx->combined_tag[ctx->directive_length] = '\0';
1126 -            ctx->curr_tag_pos = &ctx->combined_tag[ctx->directive_length+1];
1127 -
1128 -            handle_func = 
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)) {
1135 -                    return (rv);
1136 -                }
1137 -            }
1138 -            else {
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);
1143 -            }
1144 -
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.
1149 -             *
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.
1153 -             *
1154 -             * The value of dptr should have been set during the
1155 -             * PARSE_TAIL state to the first bucket after the
1156 -             * ENDING_SEQUENCE.
1157 -             *
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;
1165 +    return 0;
1166 +}
1167 +
1168 +/*
1169 + * returns the position after the directive
1170 + */
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)
1174 +{
1175 +    const char *p = data;
1176 +    const char *ep = data + len;
1177 +    apr_size_t pos;
1178 +
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-->
1184               */
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;
1190 +                ++p;
1191 +                return (p - data);
1192 +            }
1193 +            ++p;
1194 +        }
1195 +
1196 +        if (p < ep) { /* found delimiter whitespace */
1197 +            ctx->state = PARSE_DIRECTIVE_POSTNAME;
1198 +            *store = &ctx->directive;
1199 +            *store_len = &ctx->ctx->directive_length;
1200 +        }
1201 +
1202 +        break;
1203 +
1204 +    case PARSE_DIRECTIVE_TAIL:
1205 +        pos = ctx->ctx->parse_pos;
1206 +
1207 +        while (p < ep && pos < ctx->end_seq_len &&
1208 +               *p == ctx->ctx->end_seq[pos]) {
1209 +            ++p;
1210 +            ++pos;
1211 +        }
1212 +
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;
1218 +            break;
1219 +        }
1220 +
1221 +        /* partial match, the buffer is too small to match fully */
1222 +        if (p == ep) {
1223 +            ctx->ctx->parse_pos = pos;
1224 +            break;
1225 +        }
1226 +
1227 +        /* no match. continue normal parsing */
1228 +        ctx->state = PARSE_DIRECTIVE;
1229 +        return 0;
1230 +
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 */
1235 +
1236 +    case PARSE_DIRECTIVE_POSTNAME:
1237 +        if (PARSE_DIRECTIVE_POSTNAME == ctx->state) {
1238 +            ctx->state = PARSE_PRE_ARG;
1239 +        }
1240 +        ctx->argc = 0;
1241 +        ctx->argv = NULL;
1242 +
1243 +        if (!ctx->ctx->directive_length) {
1244 +            ctx->error = 1;
1245 +            ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, ctx->r, "missing directive "
1246 +                          "name in parsed document %s", ctx->r->filename);
1247 +        }
1248 +        else {
1249 +            char *sp = ctx->directive;
1250 +            char *sep = ctx->directive + ctx->ctx->directive_length;
1251 +
1252 +            /* normalize directive name */
1253 +            for (; sp < sep; ++sp) {
1254 +                *sp = apr_tolower(*sp);
1255              }
1256 -            tmp_dptr = ctx->head_start_bucket;
1257 -            if (!APR_BRIGADE_EMPTY(ctx->ssi_tag_brigade)) {
1258 -                apr_brigade_cleanup(ctx->ssi_tag_brigade);
1259 +        }
1260 +
1261 +        return 0;
1262 +
1263 +    default:
1264 +        /* get a rid of a gcc warning about unhandled enumerations */
1265 +        break;
1266 +    }
1267 +
1268 +    return (p - data);
1269 +}
1270 +
1271 +/*
1272 + * find out whether the next token is (a possible) end_seq or an argument
1273 + */
1274 +static apr_size_t find_arg_or_tail(ssi_ctx_t *ctx, const char *data,
1275 +                                   apr_size_t len)
1276 +{
1277 +    const char *p = data;
1278 +    const char *ep = data + len;
1279 +
1280 +    /* skip leading WS */
1281 +    while (p < ep && apr_isspace(*p)) {
1282 +        ++p;
1283 +    }
1284 +
1285 +    /* buffer doesn't consist of whitespaces only */
1286 +    if (p < ep) {
1287 +        ctx->state = (*p == *ctx->ctx->end_seq) ? PARSE_TAIL : PARSE_ARG;
1288 +    }
1289 +
1290 +    return (p - data);
1291 +}
1292 +
1293 +/*
1294 + * test the stream for end_seq. If it doesn't match at all, it must be an
1295 + * argument
1296 + */
1297 +static apr_size_t find_tail(ssi_ctx_t *ctx, const char *data,
1298 +                            apr_size_t len)
1299 +{
1300 +    const char *p = data;
1301 +    const char *ep = data + len;
1302 +    apr_size_t pos = ctx->ctx->parse_pos;
1303 +
1304 +    if (PARSE_TAIL == ctx->state) {
1305 +        ctx->state = PARSE_TAIL_SEQ;
1306 +        pos = ctx->ctx->parse_pos = 0;
1307 +    }
1308 +
1309 +    while (p < ep && pos < ctx->end_seq_len && *p == ctx->ctx->end_seq[pos]) {
1310 +        ++p;
1311 +        ++pos;
1312 +    }
1313 +
1314 +    /* bingo, full match */
1315 +    if (pos == ctx->end_seq_len) {
1316 +        ctx->state = PARSE_EXECUTE;
1317 +        return (p - data);
1318 +    }
1319 +
1320 +    /* partial match, the buffer is too small to match fully */
1321 +    if (p == ep) {
1322 +        ctx->ctx->parse_pos = pos;
1323 +        return (p - data);
1324 +    }
1325 +
1326 +    /* no match. It must be an argument string then */
1327 +    ctx->state = PARSE_ARG;
1328 +    return 0;
1329 +}
1330 +
1331 +/*
1332 + * extract name=value from the buffer
1333 + * A pcre-pattern could look (similar to):
1334 + * name\s*(?:=\s*(["'`]?)value\1(?>\s*))?
1335 + */
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)
1339 +{
1340 +    const char *p = data;
1341 +    const char *ep = data + len;
1342 +
1343 +    switch (ctx->state) {
1344 +    case PARSE_ARG:
1345 +        /*
1346 +         * create argument structure and append it to the current list
1347 +         */
1348 +        ctx->current_arg = apr_palloc(ctx->dpool,
1349 +                                      sizeof(*ctx->current_arg));
1350 +        ctx->current_arg->next = NULL;
1351 +
1352 +        ++(ctx->argc);
1353 +        if (!ctx->argv) {
1354 +            ctx->argv = ctx->current_arg;
1355 +        }
1356 +        else {
1357 +            ssi_arg_item_t *newarg = ctx->argv;
1358 +
1359 +            while (newarg->next) {
1360 +                newarg = newarg->next;
1361              }
1362 -            else {
1363 -                do {
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;
1370 +        }
1371 +
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
1374 +         */
1375 +        switch (*p) {
1376 +        case '"': case '\'': case '`':
1377 +            *store = NULL;
1378 +
1379 +            ctx->state = PARSE_ARG_VAL;
1380 +            ctx->quote = *p++;
1381 +            ctx->current_arg->name = NULL;
1382 +            ctx->current_arg->name_len = 0;
1383 +            ctx->error = 1;
1384 +
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);
1390 +
1391 +            return (p - data);
1392 +
1393 +        default:
1394 +            ctx->state = PARSE_ARG_NAME;
1395 +        }
1396 +        /* continue immediately with next state */
1397 +
1398 +    case PARSE_ARG_NAME:
1399 +        while (p < ep && !apr_isspace(*p) && *p != '=') {
1400 +            ++p;
1401 +        }
1402 +
1403 +        if (p < ep) {
1404 +            ctx->state = PARSE_ARG_POSTNAME;
1405 +            *store = &ctx->current_arg->name;
1406 +            *store_len = &ctx->current_arg->name_len;
1407 +            return (p - data);
1408 +        }
1409 +        break;
1410 +
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) {
1416 +            ctx->error = 1;
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);
1422 +        }
1423 +        else {
1424 +            char *sp = ctx->current_arg->name;
1425 +
1426 +            /* normalize the name */
1427 +            while (*sp) {
1428 +                *sp = apr_tolower(*sp);
1429 +                ++sp;
1430 +            }
1431 +        }
1432 +
1433 +        ctx->state = PARSE_ARG_EQ;
1434 +        /* continue with next state immediately */
1435 +
1436 +    case PARSE_ARG_EQ:
1437 +        *store = NULL;
1438 +
1439 +        while (p < ep && apr_isspace(*p)) {
1440 +            ++p;
1441 +        }
1442 +
1443 +        if (p < ep) {
1444 +            if (*p == '=') {
1445 +                ctx->state = PARSE_ARG_PREVAL;
1446 +                ++p;
1447              }
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;
1453              }
1454  
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);
1467 +        }
1468 +        break;
1469 +
1470 +    case PARSE_ARG_PREVAL:
1471 +        *store = NULL;
1472  
1473 -            if (!APR_BRIGADE_EMPTY(ctx->ssi_tag_brigade)) {
1474 -                apr_brigade_cleanup(ctx->ssi_tag_brigade);
1475 +        while (p < ep && apr_isspace(*p)) {
1476 +            ++p;
1477 +        }
1478 +
1479 +        /* buffer doesn't consist of whitespaces only */
1480 +        if (p < ep) {
1481 +            ctx->state = PARSE_ARG_VAL;
1482 +            switch (*p) {
1483 +            case '"': case '\'': case '`':
1484 +                ctx->quote = *p++;
1485 +                break;
1486 +            default:
1487 +                ctx->quote = '\0';
1488 +                break;
1489              }
1490  
1491 -            ctx->state     = PRE_HEAD;
1492 +            return (p - data);
1493          }
1494 -    }
1495 +        break;
1496 +
1497 +    case PARSE_ARG_VAL_ESC:
1498 +        if (*p == ctx->quote) {
1499 +            ++p;
1500 +        }
1501 +        ctx->state = PARSE_ARG_VAL;
1502 +        /* continue with next state immediately */
1503 +
1504 +    case PARSE_ARG_VAL:
1505 +        for (; p < ep; ++p) {
1506 +            if (ctx->quote && *p == '\\') {
1507 +                ++p;
1508 +                if (p == ep) {
1509 +                    ctx->state = PARSE_ARG_VAL_ESC;
1510 +                    break;
1511 +                }
1512  
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) {
1523 +                    --p;
1524 +                }
1525 +            }
1526 +            else if (ctx->quote && *p == ctx->quote) {
1527 +                ++p;
1528 +                *store = &ctx->current_arg->value;
1529 +                *store_len = &ctx->current_arg->value_len;
1530 +                ctx->state = PARSE_ARG_POSTVAL;
1531 +                break;
1532 +            }
1533 +            else if (!ctx->quote && apr_isspace(*p)) {
1534 +                ++p;
1535 +                *store = &ctx->current_arg->value;
1536 +                *store_len = &ctx->current_arg->value_len;
1537 +                ctx->state = PARSE_ARG_POSTVAL;
1538 +                break;
1539 +            }
1540          }
1541 -        return ap_pass_brigade(f->next, *bb);
1542 -    }
1543  
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.
1549 -     */
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) {
1555 -                return rv;
1556 +        return (p - data);
1557 +
1558 +    case PARSE_ARG_POSTVAL:
1559 +        /*
1560 +         * The value is still the raw input string. Finally clean it up.
1561 +         */
1562 +        --(ctx->current_arg->value_len);
1563 +
1564 +        /* strip quote escaping \ from the string */
1565 +        if (ctx->quote) {
1566 +            apr_size_t shift = 0;
1567 +            char *sp;
1568 +
1569 +            sp = ctx->current_arg->value;
1570 +            ep = ctx->current_arg->value + ctx->current_arg->value_len;
1571 +            while (sp < ep && *sp != '\\') {
1572 +                ++sp;
1573              }
1574 -            ctx->bytes_parsed = 0;
1575 +            for (; sp < ep; ++sp) {
1576 +                if (*sp == '\\' && sp[1] == ctx->quote) {
1577 +                    ++sp;
1578 +                    ++shift;
1579 +                }
1580 +                if (shift) {
1581 +                    *(sp-shift) = *sp;
1582 +                }
1583 +            }
1584 +
1585 +            ctx->current_arg->value_len -= shift;
1586          }
1587 +
1588 +        ctx->current_arg->value[ctx->current_arg->value_len] = '\0';
1589 +        ctx->state = PARSE_PRE_ARG;
1590 +
1591 +        return 0;
1592 +
1593 +    default:
1594 +        /* get a rid of a gcc warning about unhandled enumerations */
1595 +        break;
1596      }
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);
1602 +
1603 +    return len; /* partial match of something */
1604 +}
1605 +
1606 +/*
1607 + * This is the main loop over the current bucket brigade.
1608 + */
1609 +static apr_status_t send_parsed_content(ap_filter_t *f, apr_bucket_brigade *bb)
1610 +{
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 */
1617 +
1618 +    /* fast exit */
1619 +    if (APR_BRIGADE_EMPTY(bb)) {
1620 +        return APR_SUCCESS;
1621      }
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);
1625 -        }
1626 -        else {                  /* End of brigade contains part of SSI tag... */
1627 -            apr_bucket *last;
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;
1633 -            }
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) {
1638 +
1639 +    /* we may crash, since already cleaned up; hand over the responsibility
1640 +     * to the next filter;-)
1641 +     */
1642 +    if (ctx->seen_eos) {
1643 +        return ap_pass_brigade(f->next, bb);
1644 +    }
1645 +
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;
1650 +    ctx->error = 0;
1651 +
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;
1659 +
1660 +        /* handle meta buckets before reading any data */
1661 +        if (APR_BUCKET_IS_METADATA(b)) {
1662 +            newb = APR_BUCKET_NEXT(b);
1663 +
1664 +            APR_BUCKET_REMOVE(b);
1665 +
1666 +            if (APR_BUCKET_IS_EOS(b)) {
1667 +                ctx->seen_eos = 1;
1668 +
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.
1673 +                 *
1674 +                 * PARSE_EXECUTE means, we've hit a directive just before the
1675 +                 *    EOS, which is now waiting for execution.
1676 +                 *
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
1683 +                 *    last) round.
1684 +                 */
1685 +                if (PARSE_EXECUTE            == ctx->state ||
1686 +                    PARSE_DIRECTIVE_POSTTAIL == ctx->state) {
1687 +                    APR_BUCKET_INSERT_BEFORE(newb, b);
1688 +                }
1689 +                else {
1690 +                    break; /* END OF STREAM */
1691 +                }
1692 +            }
1693 +            else {
1694 +                APR_BRIGADE_INSERT_TAIL(pass_bb, b);
1695 +
1696 +                if (APR_BUCKET_IS_FLUSH(b)) {
1697 +                    ctx->ctx->output_now = 1;
1698 +                }
1699 +
1700 +                b = newb;
1701 +                continue;
1702 +            }
1703 +        }
1704 +
1705 +        /* enough is enough ... */
1706 +        if (ctx->ctx->output_now ||
1707 +            ctx->ctx->bytes_parsed > AP_MIN_BYTES_TO_WRITE) {
1708 +
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);
1713 +                    return rv;
1714 +                }
1715 +            }
1716 +
1717 +            ctx->ctx->output_now = 0;
1718 +            ctx->ctx->bytes_parsed = 0;
1719 +        }
1720 +
1721 +        /* read the current bucket data */
1722 +        len = 0;
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;
1728 +                    continue;
1729 +                }
1730 +            }
1731 +
1732 +            if (!len || !APR_STATUS_IS_SUCCESS(rv)) {
1733 +                rv = apr_bucket_read(b, &data, &len, APR_BLOCK_READ);
1734 +            }
1735 +
1736 +            if (!APR_STATUS_IS_SUCCESS(rv)) {
1737 +                apr_brigade_destroy(pass_bb);
1738                  return rv;
1739              }
1740 -            
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
1744 -             */
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 */
1749 -                apr_bucket *b;
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)) {
1754 +
1755 +            ctx->ctx->bytes_parsed += len;
1756 +        }
1757 +
1758 +        /* zero length bucket, fetch next one */
1759 +        if (!len && !ctx->seen_eos) {
1760 +            b = APR_BUCKET_NEXT(b);
1761 +            continue;
1762 +        }
1763 +
1764 +        /*
1765 +         * it's actually a data containing bucket, start/continue parsing
1766 +         */
1767 +
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);
1772 +
1773 +            if (index < len) {
1774 +                apr_bucket_split(b, index);
1775 +            }
1776 +
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);
1781 +            }
1782 +            else {
1783 +                apr_bucket_delete(b);
1784 +            }
1785 +
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 */
1791 +                }
1792 +
1793 +                b = APR_BUCKET_NEXT(newb);
1794 +                apr_bucket_delete(newb);
1795 +            }
1796 +            else {
1797 +                b = newb;
1798 +            }
1799 +
1800 +            break;
1801 +
1802 +        /* we're currently looking for the end of the start sequence */
1803 +        case PARSE_HEAD:
1804 +            index = find_partial_start_sequence(ctx, data, len, &release);
1805 +
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);
1809 +
1810 +                memcpy(to_release, ctx->ctx->start_seq, release);
1811 +                newb = apr_bucket_pool_create(to_release, release,
1812 +                                              ctx->ctx->pool,
1813 +                                              f->c->bucket_alloc);
1814 +                APR_BRIGADE_INSERT_TAIL(pass_bb, newb);
1815 +            }
1816 +
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 */
1822 +                }
1823 +                newb = APR_BUCKET_NEXT(b);
1824 +                apr_bucket_delete(b);
1825 +                b = newb;
1826 +            }
1827 +
1828 +            break;
1829 +
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);
1836 +
1837 +            if (index) {
1838 +                apr_bucket_split(b, index);
1839 +                newb = APR_BUCKET_NEXT(b);
1840 +            }
1841 +
1842 +            if (store) {
1843 +                if (index) {
1844                      APR_BUCKET_REMOVE(b);
1845 -                    apr_bucket_destroy(b);
1846 +                    APR_BRIGADE_INSERT_TAIL(ctx->tmp_bb, b);
1847 +                    b = newb;
1848 +                }
1849 +
1850 +                /* time for cleanup? */
1851 +                if (store != &magic) {
1852 +                    apr_brigade_pflatten(ctx->tmp_bb, store, store_len,
1853 +                                         ctx->dpool);
1854 +                    apr_brigade_cleanup(ctx->tmp_bb);
1855 +                }
1856 +            }
1857 +            else if (index) {
1858 +                apr_bucket_delete(b);
1859 +                b = newb;
1860 +            }
1861 +
1862 +            break;
1863 +
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);
1867 +
1868 +            if (index) { /* skipped whitespaces */
1869 +                if (index < len) {
1870 +                    apr_bucket_split(b, index);
1871 +                }
1872 +                newb = APR_BUCKET_NEXT(b);
1873 +                apr_bucket_delete(b);
1874 +                b = newb;
1875 +            }
1876 +
1877 +            break;
1878 +
1879 +        /* currently parsing name[=val] */
1880 +        case PARSE_ARG:
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);
1889 +
1890 +            if (index) {
1891 +                apr_bucket_split(b, index);
1892 +                newb = APR_BUCKET_NEXT(b);
1893 +            }
1894 +
1895 +            if (store) {
1896 +                if (index) {
1897 +                    APR_BUCKET_REMOVE(b);
1898 +                    APR_BRIGADE_INSERT_TAIL(ctx->tmp_bb, b);
1899 +                    b = newb;
1900 +                }
1901 +
1902 +                /* time for cleanup? */
1903 +                if (store != &magic) {
1904 +                    apr_brigade_pflatten(ctx->tmp_bb, store, store_len,
1905 +                                         ctx->dpool);
1906 +                    apr_brigade_cleanup(ctx->tmp_bb);
1907 +                }
1908 +            }
1909 +            else if (index) {
1910 +                apr_bucket_delete(b);
1911 +                b = newb;
1912 +            }
1913 +
1914 +            break;
1915 +
1916 +        /* try to match end_seq at current pos. */
1917 +        case PARSE_TAIL:
1918 +        case PARSE_TAIL_SEQ:
1919 +            index = find_tail(ctx, data, len);
1920 +
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);
1926 +                b = newb;
1927 +                break;
1928 +
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);
1933 +                break;
1934 +
1935 +            default:             /* partial match */
1936 +                newb = APR_BUCKET_NEXT(b);
1937 +                APR_BUCKET_REMOVE(b);
1938 +                APR_BRIGADE_INSERT_TAIL(ctx->tmp_bb, b);
1939 +                b = newb;
1940 +                break;
1941 +            }
1942 +
1943 +            break;
1944 +
1945 +        /* now execute the parsed directive, cleanup the space and
1946 +         * start again with PARSE_PRE_HEAD
1947 +         */
1948 +        case PARSE_EXECUTE:
1949 +            /* if there was an error, it was already logged; just stop here */
1950 +            if (ctx->error) {
1951 +                if (ctx->ctx->flags & FLAG_PRINTING) {
1952 +                    SSI_CREATE_ERROR_BUCKET(ctx->ctx, f, pass_bb);
1953 +                    ctx->error = 0;
1954                  }
1955 -                CREATE_ERROR_BUCKET(ctx, err_bucket, b, err_bucket);
1956 -                rv = ap_pass_brigade(f->next, tag_and_after);
1957              }
1958              else {
1959 -                ap_save_brigade(f, &ctx->ssi_tag_brigade,
1960 -                                &tag_and_after, r->pool);
1961 +                include_handler_fn_t *handle_func;
1962 +
1963 +                handle_func =
1964 +                    (include_handler_fn_t *) apr_hash_get(include_hash,
1965 +                                                    ctx->directive,
1966 +                                                    ctx->ctx->directive_length);
1967 +                if (handle_func) {
1968 +                    apr_bucket *dummy;
1969 +                    char *tag;
1970 +                    apr_size_t tag_len = 0;
1971 +                    ssi_arg_item_t *carg = ctx->argv;
1972 +
1973 +                    /* legacy wrapper code */
1974 +                    while (carg) {
1975 +                        /* +1 \0 byte (either after tag or value)
1976 +                         * +1 =  byte (before value)
1977 +                         */
1978 +                        tag_len += (carg->name  ? carg->name_len      : 0) +
1979 +                                   (carg->value ? carg->value_len + 1 : 0) + 1;
1980 +                        carg = carg->next;
1981 +                    }
1982 +
1983 +                    tag = ctx->ctx->combined_tag = ctx->ctx->curr_tag_pos =
1984 +                        apr_palloc(ctx->dpool, tag_len);
1985 +
1986 +                    carg = ctx->argv;
1987 +                    while (carg) {
1988 +                        if (carg->name) {
1989 +                            memcpy(tag, carg->name, carg->name_len);
1990 +                            tag += carg->name_len;
1991 +                        }
1992 +                        if (carg->value) {
1993 +                            *tag++ = '=';
1994 +                            memcpy(tag, carg->value, carg->value_len);
1995 +                            tag += carg->value_len;
1996 +                        }
1997 +                        *tag++ = '\0';
1998 +                        carg = carg->next;
1999 +                    }
2000 +                    ctx->ctx->tag_length = tag_len;
2001 +
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,
2008 +                                               ctx->ctx->pool,
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,
2017 +                                               ctx->ctx->pool,
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),
2025 +                                               ctx->end_seq_len,
2026 +                                               ctx->ctx->pool,
2027 +                                               f->c->bucket_alloc);
2028 +                    APR_BRIGADE_INSERT_TAIL(ctx->ctx->ssi_tag_brigade,
2029 +                                            ctx->ctx->tail_start_bucket);
2030 +
2031 +                    rv = handle_func(ctx->ctx, &bb, r, f, b, &dummy);
2032 +
2033 +                    apr_brigade_cleanup(ctx->ctx->ssi_tag_brigade);
2034 +
2035 +                    if (rv != 0 && rv != 1 && rv != -1) {
2036 +                        apr_brigade_destroy(pass_bb);
2037 +                        return rv;
2038 +                    }
2039 +
2040 +                    if (dummy) {
2041 +                        apr_bucket_brigade *remain;
2042 +
2043 +                        remain = apr_brigade_split(bb, b);
2044 +                        APR_BRIGADE_CONCAT(pass_bb, bb);
2045 +                        bb = remain;
2046 +                    }
2047 +                }
2048 +                else {
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),
2053 +                                                 r->filename);
2054 +                    if (ctx->ctx->flags & FLAG_PRINTING) {
2055 +                        SSI_CREATE_ERROR_BUCKET(ctx->ctx, f, pass_bb);
2056 +                    }
2057 +                }
2058              }
2059 -            if (rv != APR_SUCCESS) {
2060 -                return rv;
2061 +
2062 +            /* cleanup */
2063 +            apr_pool_clear(ctx->dpool);
2064 +            apr_brigade_cleanup(ctx->tmp_bb);
2065 +
2066 +            /* Oooof. Done here, start next round */
2067 +            ctx->state = PARSE_PRE_HEAD;
2068 +            break;
2069 +        }
2070 +
2071 +    } /* while (brigade) */
2072 +
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);
2079 +
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));
2085              }
2086 -            ctx->bytes_parsed = 0;
2087          }
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);
2094 +            }
2095 +        }
2096 +
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);
2101 +        }
2102 +
2103 +        /* cleanup our temporary memory */
2104 +        apr_brigade_destroy(ctx->tmp_bb);
2105 +        apr_pool_destroy(ctx->dpool);
2106 +
2107 +        /* don't forget to finally insert the EOS bucket */
2108 +        APR_BRIGADE_INSERT_TAIL(pass_bb, b);
2109      }
2110 -    return APR_SUCCESS;
2111 +
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);
2115 +    }
2116 +    else {
2117 +        rv = APR_SUCCESS;
2118 +    }
2119 +
2120 +    apr_brigade_destroy(pass_bb);
2121 +    return rv;
2122  }
2123  
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;
2128      }
2129 -    
2130 +
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.
2134 +     */
2135 +    apr_table_setn(f->r->notes, "no-etag", "");
2136 +
2137      return OK;
2138  }
2139  
2140  static apr_status_t includes_filter(ap_filter_t *f, apr_bucket_brigade *b)
2141  {
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 @@
2149                                                                &include_module);
2150  
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);
2157      }
2158  
2159      if (!f->ctx) {
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;
2165 -        }
2166 -        ctx->ssi_tag_brigade = apr_brigade_create(f->c->pool,
2167 -                                                  f->c->bucket_alloc);
2168 -        ctx->status = APR_SUCCESS;
2169 -
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);
2182 +
2183 +        /* configuration data */
2184 +        ctx->end_seq_len = strlen(sconf->default_end_tag);
2185 +        ctx->r = f->r;
2186 +
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;
2194 +        }
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;
2199 +
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;
2206 +
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;
2215      }
2216      else {
2217 -        ctx->bytes_parsed = 0;
2218 +        ctx->ctx->bytes_parsed = 0;
2219      }
2220  
2221      if ((parent = ap_get_module_config(r->request_config, &include_module))) {
2222 @@ -3405,14 +3645,13 @@
2223       */
2224      apr_table_unset(f->r->headers_out, "Content-Length");
2225  
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
2231       * headers.
2232       * Exception: XBitHack full means we *should* set the Last-Modified field.
2233       */
2234 -    apr_table_unset(f->r->headers_out, "ETag");
2235  
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");
2240      }
2241  
2242 -    return send_parsed_content(&b, r, f);
2243 +    /* add QUERY stuff to env cause it ain't yet */
2244 +    if (r->args) {
2245 +        char *arg_copy = apr_pstrdup(r->pool, r->args);
2246 +
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));
2251 +    }
2252 +
2253 +    return send_parsed_content(f, b);
2254  }
2255  
2256  static void ap_register_include_handler(char *tag, include_handler_fn_t *func)
This page took 0.226595 seconds and 3 git commands to generate.