/[Apache-SVN]/httpd/httpd/trunk/modules/filters/mod_include.c
ViewVC logotype

Contents of /httpd/httpd/trunk/modules/filters/mod_include.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 757376 - (show annotations)
Mon Mar 23 11:12:23 2009 UTC (8 months ago) by jorton
File MIME type: text/plain
File size: 104097 byte(s)
Prevent a case of SSI timefmt-smashing with filter chains including
multiple INCLUDES filters:

* modules/filters/mod_include.c (add_include_vars): Drop unused
  timefmt argument.
  (add_include_vars_lazy): Take timefmt argument.
  (get_include_var, handle_printenv): Pass time format from context.

PR: 39369
1 /* Licensed to the Apache Software Foundation (ASF) under one or more
2 * contributor license agreements. See the NOTICE file distributed with
3 * this work for additional information regarding copyright ownership.
4 * The ASF licenses this file to You under the Apache License, Version 2.0
5 * (the "License"); you may not use this file except in compliance with
6 * the License. You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 #include "apr.h"
18 #include "apr_strings.h"
19 #include "apr_thread_proc.h"
20 #include "apr_hash.h"
21 #include "apr_user.h"
22 #include "apr_lib.h"
23 #include "apr_optional.h"
24
25 #define APR_WANT_STRFUNC
26 #define APR_WANT_MEMFUNC
27 #include "apr_want.h"
28
29 #include "ap_config.h"
30 #include "util_filter.h"
31 #include "httpd.h"
32 #include "http_config.h"
33 #include "http_core.h"
34 #include "http_request.h"
35 #include "http_core.h"
36 #include "http_protocol.h"
37 #include "http_log.h"
38 #include "http_main.h"
39 #include "util_script.h"
40 #include "http_core.h"
41 #include "mod_include.h"
42 #include "ap_expr.h"
43
44 /* helper for Latin1 <-> entity encoding */
45 #if APR_CHARSET_EBCDIC
46 #include "util_ebcdic.h"
47 #define RAW_ASCII_CHAR(ch) apr_xlate_conv_byte(ap_hdrs_from_ascii, \
48 (unsigned char)ch)
49 #else /* APR_CHARSET_EBCDIC */
50 #define RAW_ASCII_CHAR(ch) (ch)
51 #endif /* !APR_CHARSET_EBCDIC */
52
53
54 /*
55 * +-------------------------------------------------------+
56 * | |
57 * | Types and Structures
58 * | |
59 * +-------------------------------------------------------+
60 */
61
62 /* sll used for string expansion */
63 typedef struct result_item {
64 struct result_item *next;
65 apr_size_t len;
66 const char *string;
67 } result_item_t;
68
69 typedef enum {
70 XBITHACK_OFF,
71 XBITHACK_ON,
72 XBITHACK_FULL
73 } xbithack_t;
74
75 typedef struct {
76 const char *default_error_msg;
77 const char *default_time_fmt;
78 const char *undefined_echo;
79 xbithack_t xbithack;
80 const int accessenable;
81 } include_dir_config;
82
83 typedef struct {
84 const char *default_start_tag;
85 const char *default_end_tag;
86 } include_server_config;
87
88 /* main parser states */
89 typedef enum {
90 PARSE_PRE_HEAD,
91 PARSE_HEAD,
92 PARSE_DIRECTIVE,
93 PARSE_DIRECTIVE_POSTNAME,
94 PARSE_DIRECTIVE_TAIL,
95 PARSE_DIRECTIVE_POSTTAIL,
96 PARSE_PRE_ARG,
97 PARSE_ARG,
98 PARSE_ARG_NAME,
99 PARSE_ARG_POSTNAME,
100 PARSE_ARG_EQ,
101 PARSE_ARG_PREVAL,
102 PARSE_ARG_VAL,
103 PARSE_ARG_VAL_ESC,
104 PARSE_ARG_POSTVAL,
105 PARSE_TAIL,
106 PARSE_TAIL_SEQ,
107 PARSE_EXECUTE
108 } parse_state_t;
109
110 typedef struct arg_item {
111 struct arg_item *next;
112 char *name;
113 apr_size_t name_len;
114 char *value;
115 apr_size_t value_len;
116 } arg_item_t;
117
118 typedef struct {
119 unsigned int T[256];
120 unsigned int x;
121 apr_size_t pattern_len;
122 } bndm_t;
123
124 struct ssi_internal_ctx {
125 parse_state_t state;
126 int seen_eos;
127 int error;
128 char quote; /* quote character value (or \0) */
129 apr_size_t parse_pos; /* parse position of partial matches */
130 apr_size_t bytes_read;
131
132 apr_bucket_brigade *tmp_bb;
133
134 request_rec *r;
135 const char *start_seq;
136 bndm_t *start_seq_pat;
137 const char *end_seq;
138 apr_size_t end_seq_len;
139 char *directive; /* name of the current directive */
140 apr_size_t directive_len; /* length of the current directive name */
141
142 arg_item_t *current_arg; /* currently parsed argument */
143 arg_item_t *argv; /* all arguments */
144
145 backref_t *re; /* NULL if there wasn't a regex yet */
146
147 const char *undefined_echo;
148 apr_size_t undefined_echo_len;
149
150 opt_func_t access_func; /* is using the access tests allowed? */
151
152 /* breadcrumb to track whether child request should have parent's env */
153 request_rec *kludge_child;
154 #ifdef DEBUG_INCLUDE
155 struct {
156 ap_filter_t *f;
157 apr_bucket_brigade *bb;
158 } debug;
159 #endif
160 };
161
162
163 /*
164 * +-------------------------------------------------------+
165 * | |
166 * | Debugging Utilities
167 * | |
168 * +-------------------------------------------------------+
169 */
170
171 #ifdef DEBUG_INCLUDE
172
173 #define TYPE_TOKEN(token, ttype) do { \
174 (token)->type = ttype; \
175 (token)->s = #ttype; \
176 } while(0)
177
178 #define CREATE_NODE(ctx, name) do { \
179 (name) = apr_palloc((ctx)->dpool, sizeof(*(name))); \
180 (name)->parent = (name)->left = (name)->right = NULL; \
181 (name)->done = 0; \
182 (name)->dump_done = 0; \
183 } while(0)
184
185 static void debug_printf(include_ctx_t *ctx, const char *fmt, ...)
186 {
187 va_list ap;
188 char *debug__str;
189
190 va_start(ap, fmt);
191 debug__str = apr_pvsprintf(ctx->pool, fmt, ap);
192 va_end(ap);
193
194 APR_BRIGADE_INSERT_TAIL(ctx->intern->debug.bb, apr_bucket_pool_create(
195 debug__str, strlen(debug__str), ctx->pool,
196 ctx->intern->debug.f->c->bucket_alloc));
197 }
198
199 #define DUMP__CHILD(ctx, is, node, child) if (1) { \
200 parse_node_t *d__c = node->child; \
201 if (d__c) { \
202 if (!d__c->dump_done) { \
203 if (d__c->parent != node) { \
204 debug_printf(ctx, "!!! Parse tree is not consistent !!!\n"); \
205 if (!d__c->parent) { \
206 debug_printf(ctx, "Parent of " #child " child node is " \
207 "NULL.\n"); \
208 } \
209 else { \
210 debug_printf(ctx, "Parent of " #child " child node " \
211 "points to another node (of type %s)!\n", \
212 d__c->parent->token.s); \
213 } \
214 return; \
215 } \
216 node = d__c; \
217 continue; \
218 } \
219 } \
220 else { \
221 debug_printf(ctx, "%s(missing)\n", is); \
222 } \
223 }
224
225 static void debug_dump_tree(include_ctx_t *ctx, parse_node_t *root)
226 {
227 parse_node_t *current;
228 char *is;
229
230 if (!root) {
231 debug_printf(ctx, " -- Parse Tree empty --\n\n");
232 return;
233 }
234
235 debug_printf(ctx, " ----- Parse Tree -----\n");
236 current = root;
237 is = " ";
238
239 while (current) {
240 switch (current->token.type) {
241 case TOKEN_STRING:
242 case TOKEN_RE:
243 debug_printf(ctx, "%s%s (%s)\n", is, current->token.s,
244 current->token.value);
245 current->dump_done = 1;
246 current = current->parent;
247 continue;
248
249 case TOKEN_NOT:
250 case TOKEN_GROUP:
251 case TOKEN_RBRACE:
252 case TOKEN_LBRACE:
253 if (!current->dump_done) {
254 debug_printf(ctx, "%s%s\n", is, current->token.s);
255 is = apr_pstrcat(ctx->dpool, is, " ", NULL);
256 current->dump_done = 1;
257 }
258
259 DUMP__CHILD(ctx, is, current, right)
260
261 if (!current->right || current->right->dump_done) {
262 is = apr_pstrmemdup(ctx->dpool, is, strlen(is) - 4);
263 if (current->right) current->right->dump_done = 0;
264 current = current->parent;
265 }
266 continue;
267
268 default:
269 if (!current->dump_done) {
270 debug_printf(ctx, "%s%s\n", is, current->token.s);
271 is = apr_pstrcat(ctx->dpool, is, " ", NULL);
272 current->dump_done = 1;
273 }
274
275 DUMP__CHILD(ctx, is, current, left)
276 DUMP__CHILD(ctx, is, current, right)
277
278 if ((!current->left || current->left->dump_done) &&
279 (!current->right || current->right->dump_done)) {
280
281 is = apr_pstrmemdup(ctx->dpool, is, strlen(is) - 4);
282 if (current->left) current->left->dump_done = 0;
283 if (current->right) current->right->dump_done = 0;
284 current = current->parent;
285 }
286 continue;
287 }
288 }
289
290 /* it is possible to call this function within the parser loop, to see
291 * how the tree is built. That way, we must cleanup after us to dump
292 * always the whole tree
293 */
294 root->dump_done = 0;
295 if (root->left) root->left->dump_done = 0;
296 if (root->right) root->right->dump_done = 0;
297
298 debug_printf(ctx, " --- End Parse Tree ---\n\n");
299
300 return;
301 }
302
303 #define DEBUG_INIT(ctx, filter, brigade) do { \
304 (ctx)->intern->debug.f = filter; \
305 (ctx)->intern->debug.bb = brigade; \
306 } while(0)
307
308 #define DEBUG_PRINTF(arg) debug_printf arg
309
310 #define DEBUG_DUMP_TOKEN(ctx, token) do { \
311 token_t *d__t = (token); \
312 \
313 if (d__t->type == TOKEN_STRING || d__t->type == TOKEN_RE) { \
314 DEBUG_PRINTF(((ctx), " Found: %s (%s)\n", d__t->s, d__t->value)); \
315 } \
316 else { \
317 DEBUG_PRINTF((ctx, " Found: %s\n", d__t->s)); \
318 } \
319 } while(0)
320
321 #define DEBUG_DUMP_EVAL(ctx, node) do { \
322 char c = '"'; \
323 switch ((node)->token.type) { \
324 case TOKEN_STRING: \
325 debug_printf((ctx), " Evaluate: %s (%s) -> %c\n", (node)->token.s,\
326 (node)->token.value, ((node)->value) ? '1':'0'); \
327 break; \
328 case TOKEN_AND: \
329 case TOKEN_OR: \
330 debug_printf((ctx), " Evaluate: %s (Left: %s; Right: %s) -> %c\n",\
331 (node)->token.s, \
332 (((node)->left->done) ? ((node)->left->value ?"1":"0") \
333 : "short circuited"), \
334 (((node)->right->done) ? ((node)->right->value?"1":"0") \
335 : "short circuited"), \
336 (node)->value ? '1' : '0'); \
337 break; \
338 case TOKEN_EQ: \
339 case TOKEN_NE: \
340 case TOKEN_GT: \
341 case TOKEN_GE: \
342 case TOKEN_LT: \
343 case TOKEN_LE: \
344 if ((node)->right->token.type == TOKEN_RE) c = '/'; \
345 debug_printf((ctx), " Compare: %s (\"%s\" with %c%s%c) -> %c\n", \
346 (node)->token.s, \
347 (node)->left->token.value, \
348 c, (node)->right->token.value, c, \
349 (node)->value ? '1' : '0'); \
350 break; \
351 default: \
352 debug_printf((ctx), " Evaluate: %s -> %c\n", (node)->token.s, \
353 (node)->value ? '1' : '0'); \
354 break; \
355 } \
356 } while(0)
357
358 #define DEBUG_DUMP_UNMATCHED(ctx, unmatched) do { \
359 if (unmatched) { \
360 DEBUG_PRINTF(((ctx), " Unmatched %c\n", (char)(unmatched))); \
361 } \
362 } while(0)
363
364 #define DEBUG_DUMP_COND(ctx, text) \
365 DEBUG_PRINTF(((ctx), "**** %s cond status=\"%c\"\n", (text), \
366 ((ctx)->flags & SSI_FLAG_COND_TRUE) ? '1' : '0'))
367
368 #define DEBUG_DUMP_TREE(ctx, root) debug_dump_tree(ctx, root)
369
370 #else /* DEBUG_INCLUDE */
371
372 #define TYPE_TOKEN(token, ttype) (token)->type = ttype
373
374 #define CREATE_NODE(ctx, name) do { \
375 (name) = apr_palloc((ctx)->dpool, sizeof(*(name))); \
376 (name)->parent = (name)->left = (name)->right = NULL; \
377 (name)->done = 0; \
378 } while(0)
379
380 #define DEBUG_INIT(ctx, f, bb)
381 #define DEBUG_PRINTF(arg)
382 #define DEBUG_DUMP_TOKEN(ctx, token)
383 #define DEBUG_DUMP_EVAL(ctx, node)
384 #define DEBUG_DUMP_UNMATCHED(ctx, unmatched)
385 #define DEBUG_DUMP_COND(ctx, text)
386 #define DEBUG_DUMP_TREE(ctx, root)
387
388 #endif /* !DEBUG_INCLUDE */
389
390
391 /*
392 * +-------------------------------------------------------+
393 * | |
394 * | Static Module Data
395 * | |
396 * +-------------------------------------------------------+
397 */
398
399 /* global module structure */
400 module AP_MODULE_DECLARE_DATA include_module;
401
402 /* function handlers for include directives */
403 static apr_hash_t *include_handlers;
404
405 /* forward declaration of handler registry */
406 static APR_OPTIONAL_FN_TYPE(ap_register_include_handler) *ssi_pfn_register;
407
408 /* Sentinel value to store in subprocess_env for items that
409 * shouldn't be evaluated until/unless they're actually used
410 */
411 static const char lazy_eval_sentinel;
412 #define LAZY_VALUE (&lazy_eval_sentinel)
413
414 /* default values */
415 #define DEFAULT_START_SEQUENCE "<!--#"
416 #define DEFAULT_END_SEQUENCE "-->"
417 #define DEFAULT_ERROR_MSG "[an error occurred while processing this directive]"
418 #define DEFAULT_TIME_FORMAT "%A, %d-%b-%Y %H:%M:%S %Z"
419 #define DEFAULT_UNDEFINED_ECHO "(none)"
420
421 #ifdef XBITHACK
422 #define DEFAULT_XBITHACK XBITHACK_FULL
423 #else
424 #define DEFAULT_XBITHACK XBITHACK_OFF
425 #endif
426
427
428 /*
429 * +-------------------------------------------------------+
430 * | |
431 * | Environment/Expansion Functions
432 * | |
433 * +-------------------------------------------------------+
434 */
435
436 /*
437 * decodes a string containing html entities or numeric character references.
438 * 's' is overwritten with the decoded string.
439 * If 's' is syntatically incorrect, then the followed fixups will be made:
440 * unknown entities will be left undecoded;
441 * references to unused numeric characters will be deleted.
442 * In particular, &#00; will not be decoded, but will be deleted.
443 */
444
445 /* maximum length of any ISO-LATIN-1 HTML entity name. */
446 #define MAXENTLEN (6)
447
448 /* The following is a shrinking transformation, therefore safe. */
449
450 static void decodehtml(char *s)
451 {
452 int val, i, j;
453 char *p;
454 const char *ents;
455 static const char * const entlist[MAXENTLEN + 1] =
456 {
457 NULL, /* 0 */
458 NULL, /* 1 */
459 "lt\074gt\076", /* 2 */
460 "amp\046ETH\320eth\360", /* 3 */
461 "quot\042Auml\304Euml\313Iuml\317Ouml\326Uuml\334auml\344euml"
462 "\353iuml\357ouml\366uuml\374yuml\377", /* 4 */
463
464 "Acirc\302Aring\305AElig\306Ecirc\312Icirc\316Ocirc\324Ucirc"
465 "\333THORN\336szlig\337acirc\342aring\345aelig\346ecirc\352"
466 "icirc\356ocirc\364ucirc\373thorn\376", /* 5 */
467
468 "Agrave\300Aacute\301Atilde\303Ccedil\307Egrave\310Eacute\311"
469 "Igrave\314Iacute\315Ntilde\321Ograve\322Oacute\323Otilde"
470 "\325Oslash\330Ugrave\331Uacute\332Yacute\335agrave\340"
471 "aacute\341atilde\343ccedil\347egrave\350eacute\351igrave"
472 "\354iacute\355ntilde\361ograve\362oacute\363otilde\365"
473 "oslash\370ugrave\371uacute\372yacute\375" /* 6 */
474 };
475
476 /* Do a fast scan through the string until we find anything
477 * that needs more complicated handling
478 */
479 for (; *s != '&'; s++) {
480 if (*s == '\0') {
481 return;
482 }
483 }
484
485 for (p = s; *s != '\0'; s++, p++) {
486 if (*s != '&') {
487 *p = *s;
488 continue;
489 }
490 /* find end of entity */
491 for (i = 1; s[i] != ';' && s[i] != '\0'; i++) {
492 continue;
493 }
494
495 if (s[i] == '\0') { /* treat as normal data */
496 *p = *s;
497 continue;
498 }
499
500 /* is it numeric ? */
501 if (s[1] == '#') {
502 for (j = 2, val = 0; j < i && apr_isdigit(s[j]); j++) {
503 val = val * 10 + s[j] - '0';
504 }
505 s += i;
506 if (j < i || val <= 8 || (val >= 11 && val <= 31) ||
507 (val >= 127 && val <= 160) || val >= 256) {
508 p--; /* no data to output */
509 }
510 else {
511 *p = RAW_ASCII_CHAR(val);
512 }
513 }
514 else {
515 j = i - 1;
516 if (j > MAXENTLEN || entlist[j] == NULL) {
517 /* wrong length */
518 *p = '&';
519 continue; /* skip it */
520 }
521 for (ents = entlist[j]; *ents != '\0'; ents += i) {
522 if (strncmp(s + 1, ents, j) == 0) {
523 break;
524 }
525 }
526
527 if (*ents == '\0') {
528 *p = '&'; /* unknown */
529 }
530 else {
531 *p = RAW_ASCII_CHAR(((const unsigned char *) ents)[j]);
532 s += i;
533 }
534 }
535 }
536
537 *p = '\0';
538 }
539
540 static void add_include_vars(request_rec *r)
541 {
542 apr_table_t *e = r->subprocess_env;
543 char *t;
544
545 apr_table_setn(e, "DATE_LOCAL", LAZY_VALUE);
546 apr_table_setn(e, "DATE_GMT", LAZY_VALUE);
547 apr_table_setn(e, "LAST_MODIFIED", LAZY_VALUE);
548 apr_table_setn(e, "DOCUMENT_URI", r->uri);
549 if (r->path_info && *r->path_info) {
550 apr_table_setn(e, "DOCUMENT_PATH_INFO", r->path_info);
551 }
552 apr_table_setn(e, "USER_NAME", LAZY_VALUE);
553 if (r->filename && (t = strrchr(r->filename, '/'))) {
554 apr_table_setn(e, "DOCUMENT_NAME", ++t);
555 }
556 else {
557 apr_table_setn(e, "DOCUMENT_NAME", r->uri);
558 }
559 if (r->args) {
560 char *arg_copy = apr_pstrdup(r->pool, r->args);
561
562 ap_unescape_url(arg_copy);
563 apr_table_setn(e, "QUERY_STRING_UNESCAPED",
564 ap_escape_shell_cmd(r->pool, arg_copy));
565 }
566 }
567
568 static const char *add_include_vars_lazy(request_rec *r, const char *var, const char *timefmt)
569 {
570 char *val;
571 if (!strcasecmp(var, "DATE_LOCAL")) {
572 val = ap_ht_time(r->pool, r->request_time, timefmt, 0);
573 }
574 else if (!strcasecmp(var, "DATE_GMT")) {
575 val = ap_ht_time(r->pool, r->request_time, timefmt, 1);
576 }
577 else if (!strcasecmp(var, "LAST_MODIFIED")) {
578 val = ap_ht_time(r->pool, r->finfo.mtime, timefmt, 0);
579 }
580 else if (!strcasecmp(var, "USER_NAME")) {
581 if (apr_uid_name_get(&val, r->finfo.user, r->pool) != APR_SUCCESS) {
582 val = "<unknown>";
583 }
584 }
585 else {
586 val = NULL;
587 }
588
589 if (val) {
590 apr_table_setn(r->subprocess_env, var, val);
591 }
592 return val;
593 }
594
595 static const char *get_include_var(const char *var, include_ctx_t *ctx)
596 {
597 const char *val;
598 request_rec *r = ctx->intern->r;
599
600 if (apr_isdigit(*var) && !var[1]) {
601 apr_size_t idx = *var - '0';
602 backref_t *re = ctx->intern->re;
603
604 /* Handle $0 .. $9 from the last regex evaluated.
605 * The choice of returning NULL strings on not-found,
606 * v.s. empty strings on an empty match is deliberate.
607 */
608 if (!re) {
609 ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r,
610 "regex capture $%" APR_SIZE_T_FMT " refers to no regex in %s",
611 idx, r->filename);
612 return NULL;
613 }
614 else {
615 if (re->nsub < idx || idx >= AP_MAX_REG_MATCH) {
616 ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r,
617 "regex capture $%" APR_SIZE_T_FMT
618 " is out of range (last regex was: '%s') in %s",
619 idx, re->rexp, r->filename);
620 return NULL;
621 }
622
623 if (re->match[idx].rm_so < 0 || re->match[idx].rm_eo < 0) {
624 return NULL;
625 }
626
627 val = apr_pstrmemdup(ctx->dpool, re->source + re->match[idx].rm_so,
628 re->match[idx].rm_eo - re->match[idx].rm_so);
629 }
630 }
631 else {
632 val = apr_table_get(r->subprocess_env, var);
633
634 if (val == LAZY_VALUE) {
635 val = add_include_vars_lazy(r, var, ctx->time_str);
636 }
637 }
638
639 return val;
640 }
641
642 /*
643 * Do variable substitution on strings
644 *
645 * (Note: If out==NULL, this function allocs a buffer for the resulting
646 * string from ctx->pool. The return value is always the parsed string)
647 */
648 static char *ap_ssi_parse_string(include_ctx_t *ctx, const char *in, char *out,
649 apr_size_t length, int leave_name)
650 {
651 request_rec *r = ctx->intern->r;
652 result_item_t *result = NULL, *current = NULL;
653 apr_size_t outlen = 0, inlen, span;
654 char *ret = NULL, *eout = NULL;
655 const char *p;
656
657 if (out) {
658 /* sanity check, out && !length is not supported */
659 ap_assert(out && length);
660
661 ret = out;
662 eout = out + length - 1;
663 }
664
665 span = strcspn(in, "\\$");
666 inlen = strlen(in);
667
668 /* fast exit */
669 if (inlen == span) {
670 if (out) {
671 apr_cpystrn(out, in, length);
672 }
673 else {
674 ret = apr_pstrmemdup(ctx->pool, in, (length && length <= inlen)
675 ? length - 1 : inlen);
676 }
677
678 return ret;
679 }
680
681 /* well, actually something to do */
682 p = in + span;
683
684 if (out) {
685 if (span) {
686 memcpy(out, in, (out+span <= eout) ? span : (eout-out));
687 out += span;
688 }
689 }
690 else {
691 current = result = apr_palloc(ctx->dpool, sizeof(*result));
692 current->next = NULL;
693 current->string = in;
694 current->len = span;
695 outlen = span;
696 }
697
698 /* loop for specials */
699 do {
700 if ((out && out >= eout) || (length && outlen >= length)) {
701 break;
702 }
703
704 /* prepare next entry */
705 if (!out && current->len) {
706 current->next = apr_palloc(ctx->dpool, sizeof(*current->next));
707 current = current->next;
708 current->next = NULL;
709 current->len = 0;
710 }
711
712 /*
713 * escaped character
714 */
715 if (*p == '\\') {
716 if (out) {
717 *out++ = (p[1] == '$') ? *++p : *p;
718 ++p;
719 }
720 else {
721 current->len = 1;
722 current->string = (p[1] == '$') ? ++p : p;
723 ++p;
724 ++outlen;
725 }
726 }
727
728 /*
729 * variable expansion
730 */
731 else { /* *p == '$' */
732 const char *newp = NULL, *ep, *key = NULL;
733
734 if (*++p == '{') {
735 ep = ap_strchr_c(++p, '}');
736 if (!ep) {
737 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "Missing '}' on "
738 "variable \"%s\" in %s", p, r->filename);
739 break;
740 }
741
742 if (p < ep) {
743 key = apr_pstrmemdup(ctx->dpool, p, ep - p);
744 newp = ep + 1;
745 }
746 p -= 2;
747 }
748 else {
749 ep = p;
750 while (*ep == '_' || apr_isalnum(*ep)) {
751 ++ep;
752 }
753
754 if (p < ep) {
755 key = apr_pstrmemdup(ctx->dpool, p, ep - p);
756 newp = ep;
757 }
758 --p;
759 }
760
761 /* empty name results in a copy of '$' in the output string */
762 if (!key) {
763 if (out) {
764 *out++ = *p++;
765 }
766 else {
767 current->len = 1;
768 current->string = p++;
769 ++outlen;
770 }
771 }
772 else {
773 const char *val = get_include_var(key, ctx);
774 apr_size_t len = 0;
775
776 if (val) {
777 len = strlen(val);
778 }
779 else if (leave_name) {
780 val = p;
781 len = ep - p;
782 }
783
784 if (val && len) {
785 if (out) {
786 memcpy(out, val, (out+len <= eout) ? len : (eout-out));
787 out += len;
788 }
789 else {
790 current->len = len;
791 current->string = val;
792 outlen += len;
793 }
794 }
795
796 p = newp;
797 }
798 }
799
800 if ((out && out >= eout) || (length && outlen >= length)) {
801 break;
802 }
803
804 /* check the remainder */
805 if (*p && (span = strcspn(p, "\\$")) > 0) {
806 if (!out && current->len) {
807 current->next = apr_palloc(ctx->dpool, sizeof(*current->next));
808 current = current->next;
809 current->next = NULL;
810 }
811
812 if (out) {
813 memcpy(out, p, (out+span <= eout) ? span : (eout-out));
814 out += span;
815 }
816 else {
817 current->len = span;
818 current->string = p;
819 outlen += span;
820 }
821
822 p += span;
823 }
824 } while (p < in+inlen);
825
826 /* assemble result */
827 if (out) {
828 if (out > eout) {
829 *eout = '\0';
830 }
831 else {
832 *out = '\0';
833 }
834 }
835 else {
836 const char *ep;
837
838 if (length && outlen > length) {
839 outlen = length - 1;
840 }
841
842 ret = out = apr_palloc(ctx->pool, outlen + 1);
843 ep = ret + outlen;
844
845 do {
846 if (result->len) {
847 memcpy(out, result->string, (out+result->len <= ep)
848 ? result->len : (ep-out));
849 out += result->len;
850 }
851 result = result->next;
852 } while (result && out < ep);
853
854 ret[outlen] = '\0';
855 }
856
857 return ret;
858 }
859
860 static const char *ssi_parse_string(request_rec *r, const char *in)
861 {
862 include_ctx_t *ctx = ap_get_module_config(r->request_config,
863 &include_module);
864 return ap_ssi_parse_string(ctx, in, NULL, 0, SSI_EXPAND_DROP_NAME);
865 }
866 static int ssi_access(request_rec *r, ap_parse_node_t *current,
867 string_func_t parse_string)
868 {
869 request_rec *rr;
870 include_ctx_t *ctx = ap_get_module_config(r->request_config,
871 &include_module);
872
873 /* if this arg isn't -A, just return */
874 if (current->token.type != TOKEN_ACCESS || current->token.value[0] != 'A') {
875 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
876 "Unsupported option -%s in file %s",
877 current->token.value, r->filename);
878 return 1;
879 }
880 if (current->left || !current->right ||
881 (current->right->token.type != TOKEN_STRING &&
882 current->right->token.type != TOKEN_RE)) {
883 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
884 "Invalid expression in file %s: Token '-A' must be followed by a URI string.",
885 r->filename);
886 return 1; /* was_error */
887 }
888 current->right->token.value =
889 ap_ssi_parse_string(ctx, current->right->token.value, NULL, 0,
890 SSI_EXPAND_DROP_NAME);
891 rr = ap_sub_req_lookup_uri(current->right->token.value, r, NULL);
892 /* 400 and higher are considered access denied */
893 if (rr->status < HTTP_BAD_REQUEST) {
894 current->value = 1;
895 }
896 else {
897 current->value = 0;
898 ap_log_rerror(APLOG_MARK, APLOG_DEBUG, rr->status, r,
899 "mod_include: The tested "
900 "subrequest -A \"%s\" returned an error code.",
901 current->right->token.value);
902 }
903 ap_destroy_sub_req(rr);
904 return 0;
905 }
906
907 /*
908 * +-------------------------------------------------------+
909 * | |
910 * | Action Handlers
911 * | |
912 * +-------------------------------------------------------+
913 */
914
915 /*
916 * Extract the next tag name and value.
917 * If there are no more tags, set the tag name to NULL.
918 * The tag value is html decoded if dodecode is non-zero.
919 * The tag value may be NULL if there is no tag value..
920 */
921 static void ap_ssi_get_tag_and_value(include_ctx_t *ctx, char **tag,
922 char **tag_val, int dodecode)
923 {
924 if (!ctx->intern->argv) {
925 *tag = NULL;
926 *tag_val = NULL;
927
928 return;
929 }
930
931 *tag_val = ctx->intern->argv->value;
932 *tag = ctx->intern->argv->name;
933
934 ctx->intern->argv = ctx->intern->argv->next;
935
936 if (dodecode && *tag_val) {
937 decodehtml(*tag_val);
938 }
939
940 return;
941 }
942
943 static int find_file(request_rec *r, const char *directive, const char *tag,
944 char *tag_val, apr_finfo_t *finfo)
945 {
946 char *to_send = tag_val;
947 request_rec *rr = NULL;
948 int ret=0;
949 char *error_fmt = NULL;
950 apr_status_t rv = APR_SUCCESS;
951
952 if (!strcmp(tag, "file")) {
953 char *newpath;
954
955 /* be safe; only files in this directory or below allowed */
956 rv = apr_filepath_merge(&newpath, NULL, tag_val,
957 APR_FILEPATH_SECUREROOTTEST |
958 APR_FILEPATH_NOTABSOLUTE, r->pool);
959
960 if (rv != APR_SUCCESS) {
961 error_fmt = "unable to access file \"%s\" "
962 "in parsed file %s";
963 }
964 else {
965 /* note: it is okay to pass NULL for the "next filter" since
966 we never attempt to "run" this sub request. */
967 rr = ap_sub_req_lookup_file(newpath, r, NULL);
968
969 if (rr->status == HTTP_OK && rr->finfo.filetype != 0) {
970 to_send = rr->filename;
971 if ((rv = apr_stat(finfo, to_send,
972 APR_FINFO_GPROT | APR_FINFO_MIN, rr->pool)) != APR_SUCCESS
973 && rv != APR_INCOMPLETE) {
974 error_fmt = "unable to get information about \"%s\" "
975 "in parsed file %s";
976 }
977 }
978 else {
979 error_fmt = "unable to lookup information about \"%s\" "
980 "in parsed file %s";
981 }
982 }
983
984 if (error_fmt) {
985 ret = -1;
986 ap_log_rerror(APLOG_MARK, APLOG_ERR,
987 rv, r, error_fmt, to_send, r->filename);
988 }
989
990 if (rr) ap_destroy_sub_req(rr);
991
992 return ret;
993 }
994 else if (!strcmp(tag, "virtual")) {
995 /* note: it is okay to pass NULL for the "next filter" since
996 we never attempt to "run" this sub request. */
997 rr = ap_sub_req_lookup_uri(tag_val, r, NULL);
998
999 if (rr->status == HTTP_OK && rr->finfo.filetype != 0) {
1000 memcpy((char *) finfo, (const char *) &rr->finfo,
1001 sizeof(rr->finfo));
1002 ap_destroy_sub_req(rr);
1003 return 0;
1004 }
1005 else {
1006 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "unable to get "
1007 "information about \"%s\" in parsed file %s",
1008 tag_val, r->filename);
1009 ap_destroy_sub_req(rr);
1010 return -1;
1011 }
1012 }
1013 else {
1014 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "unknown parameter \"%s\" "
1015 "to tag %s in %s", tag, directive, r->filename);
1016 return -1;
1017 }
1018 }
1019
1020 /*
1021 * <!--#include virtual|file="..." [virtual|file="..."] ... -->
1022 */
1023 static apr_status_t handle_include(include_ctx_t *ctx, ap_filter_t *f,
1024 apr_bucket_brigade *bb)
1025 {
1026 request_rec *r = f->r;
1027
1028 if (!ctx->argc) {
1029 ap_log_rerror(APLOG_MARK,
1030 (ctx->flags & SSI_FLAG_PRINTING)
1031 ? APLOG_ERR : APLOG_WARNING,
1032 0, r, "missing argument for include element in %s",
1033 r->filename);
1034 }
1035
1036 if (!(ctx->flags & SSI_FLAG_PRINTING)) {
1037 return APR_SUCCESS;
1038 }
1039
1040 if (!ctx->argc) {
1041 SSI_CREATE_ERROR_BUCKET(ctx, f, bb);
1042 return APR_SUCCESS;
1043 }
1044
1045 while (1) {
1046 char *tag = NULL;
1047 char *tag_val = NULL;
1048 request_rec *rr = NULL;
1049 char *error_fmt = NULL;
1050 char *parsed_string;
1051
1052 ap_ssi_get_tag_and_value(ctx, &tag, &tag_val, SSI_VALUE_DECODED);
1053 if (!tag || !tag_val) {
1054 break;
1055 }
1056
1057 if (strcmp(tag, "virtual") && strcmp(tag, "file")) {
1058 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "unknown parameter "
1059 "\"%s\" to tag include in %s", tag, r->filename);
1060 SSI_CREATE_ERROR_BUCKET(ctx, f, bb);
1061 break;
1062 }
1063
1064 parsed_string = ap_ssi_parse_string(ctx, tag_val, NULL, 0,
1065 SSI_EXPAND_DROP_NAME);
1066 if (tag[0] == 'f') {
1067 char *newpath;
1068 apr_status_t rv;
1069
1070 /* be safe; only files in this directory or below allowed */
1071 rv = apr_filepath_merge(&newpath, NULL, parsed_string,
1072 APR_FILEPATH_SECUREROOTTEST |
1073 APR_FILEPATH_NOTABSOLUTE, ctx->dpool);
1074
1075 if (rv != APR_SUCCESS) {
1076 error_fmt = "unable to include file \"%s\" in parsed file %s";
1077 }
1078 else {
1079 rr = ap_sub_req_lookup_file(newpath, r, f->next);
1080 }
1081 }
1082 else {
1083 if (r->kept_body) {
1084 rr = ap_sub_req_method_uri(r->method, parsed_string, r, f->next);
1085 }
1086 else {
1087 rr = ap_sub_req_lookup_uri(parsed_string, r, f->next);
1088 }
1089 }
1090
1091 if (!error_fmt && rr->status != HTTP_OK) {
1092 error_fmt = "unable to include \"%s\" in parsed file %s";
1093 }
1094
1095 if (!error_fmt && (ctx->flags & SSI_FLAG_NO_EXEC) &&
1096 rr->content_type && strncmp(rr->content_type, "text/", 5)) {
1097
1098 error_fmt = "unable to include potential exec \"%s\" in parsed "
1099 "file %s";
1100 }
1101
1102 /* See the Kludge in includes_filter for why.
1103 * Basically, it puts a bread crumb in here, then looks
1104 * for the crumb later to see if its been here.
1105 */
1106 ctx->intern->kludge_child = rr;
1107
1108 if (!error_fmt && ap_run_sub_req(rr)) {
1109 error_fmt = "unable to include \"%s\" in parsed file %s";
1110 }
1111
1112 if (error_fmt) {
1113 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, error_fmt, tag_val,
1114 r->filename);
1115 SSI_CREATE_ERROR_BUCKET(ctx, f, bb);
1116 }
1117
1118 /* Do *not* destroy the subrequest here; it may have allocated
1119 * variables in this r->subprocess_env in the subrequest's
1120 * r->pool, so that pool must survive as long as this request.
1121 * Yes, this is a memory leak. */
1122
1123 if (error_fmt) {
1124 break;
1125 }
1126 }
1127
1128 return APR_SUCCESS;
1129 }
1130
1131 /*
1132 * <!--#echo [encoding="..."] var="..." [encoding="..."] var="..." ... -->
1133 */
1134 static apr_status_t handle_echo(include_ctx_t *ctx, ap_filter_t *f,
1135 apr_bucket_brigade *bb)
1136 {
1137 enum {E_NONE, E_URL, E_ENTITY} encode;
1138 request_rec *r = f->r;
1139
1140 if (!ctx->argc) {
1141 ap_log_rerror(APLOG_MARK,
1142 (ctx->flags & SSI_FLAG_PRINTING)
1143 ? APLOG_ERR : APLOG_WARNING,
1144 0, r, "missing argument for echo element in %s",
1145 r->filename);
1146 }
1147
1148 if (!(ctx->flags & SSI_FLAG_PRINTING)) {
1149 return APR_SUCCESS;
1150 }
1151
1152 if (!ctx->argc) {
1153 SSI_CREATE_ERROR_BUCKET(ctx, f, bb);
1154 return APR_SUCCESS;
1155 }
1156
1157 encode = E_ENTITY;
1158
1159 while (1) {
1160 char *tag = NULL;
1161 char *tag_val = NULL;
1162
1163 ap_ssi_get_tag_and_value(ctx, &tag, &tag_val, SSI_VALUE_DECODED);
1164 if (!tag || !tag_val) {
1165 break;
1166 }
1167
1168 if (!strcmp(tag, "var")) {
1169 const char *val;
1170 const char *echo_text = NULL;
1171 apr_size_t e_len;
1172
1173 val = get_include_var(ap_ssi_parse_string(ctx, tag_val, NULL,
1174 0, SSI_EXPAND_DROP_NAME),
1175 ctx);
1176
1177 if (val) {
1178 switch(encode) {
1179 case E_NONE:
1180 echo_text = val;
1181 break;
1182 case E_URL:
1183 echo_text = ap_escape_uri(ctx->dpool, val);
1184 break;
1185 case E_ENTITY:
1186 /* PR#25202: escape anything non-ascii here */
1187 echo_text = ap_escape_html2(ctx->dpool, val, 1);
1188 break;
1189 }
1190
1191 e_len = strlen(echo_text);
1192 }
1193 else {
1194 echo_text = ctx->intern->undefined_echo;
1195 e_len = ctx->intern->undefined_echo_len;
1196 }
1197
1198 APR_BRIGADE_INSERT_TAIL(bb, apr_bucket_pool_create(
1199 apr_pmemdup(ctx->pool, echo_text, e_len),
1200 e_len, ctx->pool, f->c->bucket_alloc));
1201 }
1202 else if (!strcmp(tag, "encoding")) {
1203 if (!strcasecmp(tag_val, "none")) {
1204 encode = E_NONE;
1205 }
1206 else if (!strcasecmp(tag_val, "url")) {
1207 encode = E_URL;
1208 }
1209 else if (!strcasecmp(tag_val, "entity")) {
1210 encode = E_ENTITY;
1211 }
1212 else {
1213 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "unknown value "
1214 "\"%s\" to parameter \"encoding\" of tag echo in "
1215 "%s", tag_val, r->filename);
1216 SSI_CREATE_ERROR_BUCKET(ctx, f, bb);
1217 break;
1218 }
1219 }
1220 else {
1221 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "unknown parameter "
1222 "\"%s\" in tag echo of %s", tag, r->filename);
1223 SSI_CREATE_ERROR_BUCKET(ctx, f, bb);
1224 break;
1225 }
1226 }
1227
1228 return APR_SUCCESS;
1229 }
1230
1231 /*
1232 * <!--#config [timefmt="..."] [sizefmt="..."] [errmsg="..."]
1233 * [echomsg="..."] -->
1234 */
1235 static apr_status_t handle_config(include_ctx_t *ctx, ap_filter_t *f,
1236 apr_bucket_brigade *bb)
1237 {
1238 request_rec *r = f->r;
1239 apr_table_t *env = r->subprocess_env;
1240
1241 if (!ctx->argc) {
1242 ap_log_rerror(APLOG_MARK,
1243 (ctx->flags & SSI_FLAG_PRINTING)
1244 ? APLOG_ERR : APLOG_WARNING,
1245 0, r, "missing argument for config element in %s",
1246 r->filename);
1247 }
1248
1249 if (!(ctx->flags & SSI_FLAG_PRINTING)) {
1250 return APR_SUCCESS;
1251 }
1252
1253 if (!ctx->argc) {
1254 SSI_CREATE_ERROR_BUCKET(ctx, f, bb);
1255 return APR_SUCCESS;
1256 }
1257
1258 while (1) {
1259 char *tag = NULL;
1260 char *tag_val = NULL;
1261
1262 ap_ssi_get_tag_and_value(ctx, &tag, &tag_val, SSI_VALUE_RAW);
1263 if (!tag || !tag_val) {
1264 break;
1265 }
1266
1267 if (!strcmp(tag, "errmsg")) {
1268 ctx->error_str = ap_ssi_parse_string(ctx, tag_val, NULL, 0,
1269 SSI_EXPAND_DROP_NAME);
1270 }
1271 else if (!strcmp(tag, "echomsg")) {
1272 ctx->intern->undefined_echo =
1273 ap_ssi_parse_string(ctx, tag_val, NULL, 0,SSI_EXPAND_DROP_NAME);
1274 ctx->intern->undefined_echo_len=strlen(ctx->intern->undefined_echo);
1275 }
1276 else if (!strcmp(tag, "timefmt")) {
1277 apr_time_t date = r->request_time;
1278
1279 ctx->time_str = ap_ssi_parse_string(ctx, tag_val, NULL, 0,
1280 SSI_EXPAND_DROP_NAME);
1281
1282 apr_table_setn(env, "DATE_LOCAL", ap_ht_time(r->pool, date,
1283 ctx->time_str, 0));
1284 apr_table_setn(env, "DATE_GMT", ap_ht_time(r->pool, date,
1285 ctx->time_str, 1));
1286 apr_table_setn(env, "LAST_MODIFIED",
1287 ap_ht_time(r->pool, r->finfo.mtime,
1288 ctx->time_str, 0));
1289 }
1290 else if (!strcmp(tag, "sizefmt")) {
1291 char *parsed_string;
1292
1293 parsed_string = ap_ssi_parse_string(ctx, tag_val, NULL, 0,
1294 SSI_EXPAND_DROP_NAME);
1295 if (!strcmp(parsed_string, "bytes")) {
1296 ctx->flags |= SSI_FLAG_SIZE_IN_BYTES;
1297 }
1298 else if (!strcmp(parsed_string, "abbrev")) {
1299 ctx->flags &= SSI_FLAG_SIZE_ABBREV;
1300 }
1301 else {
1302 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "unknown value "
1303 "\"%s\" to parameter \"sizefmt\" of tag config "
1304 "in %s", parsed_string, r->filename);
1305 SSI_CREATE_ERROR_BUCKET(ctx, f, bb);
1306 break;
1307 }
1308 }
1309 else {
1310 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "unknown parameter "
1311 "\"%s\" to tag config in %s", tag, r->filename);
1312 SSI_CREATE_ERROR_BUCKET(ctx, f, bb);
1313 break;
1314 }
1315 }
1316
1317 return APR_SUCCESS;
1318 }
1319
1320 /*
1321 * <!--#fsize virtual|file="..." [virtual|file="..."] ... -->
1322 */
1323 static apr_status_t handle_fsize(include_ctx_t *ctx, ap_filter_t *f,
1324 apr_bucket_brigade *bb)
1325 {
1326 request_rec *r = f->r;
1327
1328 if (!ctx->argc) {
1329 ap_log_rerror(APLOG_MARK,
1330 (ctx->flags & SSI_FLAG_PRINTING)
1331 ? APLOG_ERR : APLOG_WARNING,
1332 0, r, "missing argument for fsize element in %s",
1333 r->filename);
1334 }
1335
1336 if (!(ctx->flags & SSI_FLAG_PRINTING)) {
1337 return APR_SUCCESS;
1338 }
1339
1340 if (!ctx->argc) {
1341 SSI_CREATE_ERROR_BUCKET(ctx, f, bb);
1342 return APR_SUCCESS;
1343 }
1344
1345 while (1) {
1346 char *tag = NULL;
1347 char *tag_val = NULL;
1348 apr_finfo_t finfo;
1349 char *parsed_string;
1350
1351 ap_ssi_get_tag_and_value(ctx, &tag, &tag_val, SSI_VALUE_DECODED);
1352 if (!tag || !tag_val) {
1353 break;
1354 }
1355
1356 parsed_string = ap_ssi_parse_string(ctx, tag_val, NULL, 0,
1357 SSI_EXPAND_DROP_NAME);
1358
1359 if (!find_file(r, "fsize", tag, parsed_string, &finfo)) {
1360 char *buf;
1361 apr_size_t len;
1362
1363 if (!(ctx->flags & SSI_FLAG_SIZE_IN_BYTES)) {
1364 buf = apr_strfsize(finfo.size, apr_palloc(ctx->pool, 5));
1365 len = 4; /* omit the \0 terminator */
1366 }
1367 else {
1368 apr_size_t l, x, pos;
1369 char *tmp;
1370
1371 tmp = apr_psprintf(ctx->dpool, "%" APR_OFF_T_FMT, finfo.size);
1372 len = l = strlen(tmp);
1373
1374 for (x = 0; x < l; ++x) {
1375 if (x && !((l - x) % 3)) {
1376 ++len;
1377 }
1378 }
1379
1380 if (len == l) {
1381 buf = apr_pstrmemdup(ctx->pool, tmp, len);
1382 }
1383 else {
1384 buf = apr_palloc(ctx->pool, len);
1385
1386 for (pos = x = 0; x < l; ++x) {
1387 if (x && !((l - x) % 3)) {
1388 buf[pos++] = ',';
1389 }
1390 buf[pos++] = tmp[x];
1391 }
1392 }
1393 }
1394
1395 APR_BRIGADE_INSERT_TAIL(bb, apr_bucket_pool_create(buf, len,
1396 ctx->pool, f->c->bucket_alloc));
1397 }
1398 else {
1399 SSI_CREATE_ERROR_BUCKET(ctx, f, bb);
1400 break;
1401 }
1402 }
1403
1404 return APR_SUCCESS;
1405 }
1406
1407 /*
1408 * <!--#flastmod virtual|file="..." [virtual|file="..."] ... -->
1409 */
1410 static apr_status_t handle_flastmod(include_ctx_t *ctx, ap_filter_t *f,
1411 apr_bucket_brigade *bb)
1412 {
1413 request_rec *r = f->r;
1414
1415 if (!ctx->argc) {
1416 ap_log_rerror(APLOG_MARK,
1417 (ctx->flags & SSI_FLAG_PRINTING)
1418 ? APLOG_ERR : APLOG_WARNING,
1419 0, r, "missing argument for flastmod element in %s",
1420 r->filename);
1421 }
1422
1423 if (!(ctx->flags & SSI_FLAG_PRINTING)) {
1424 return APR_SUCCESS;
1425 }
1426
1427 if (!ctx->argc) {
1428 SSI_CREATE_ERROR_BUCKET(ctx, f, bb);
1429 return APR_SUCCESS;
1430 }
1431
1432 while (1) {
1433 char *tag = NULL;
1434 char *tag_val = NULL;
1435 apr_finfo_t finfo;
1436 char *parsed_string;
1437
1438 ap_ssi_get_tag_and_value(ctx, &tag, &tag_val, SSI_VALUE_DECODED);
1439 if (!tag || !tag_val) {
1440 break;
1441 }
1442
1443 parsed_string = ap_ssi_parse_string(ctx, tag_val, NULL, 0,
1444 SSI_EXPAND_DROP_NAME);
1445
1446 if (!find_file(r, "flastmod", tag, parsed_string, &finfo)) {
1447 char *t_val;
1448 apr_size_t t_len;
1449
1450 t_val = ap_ht_time(ctx->pool, finfo.mtime, ctx->time_str, 0);
1451 t_len = strlen(t_val);
1452
1453 APR_BRIGADE_INSERT_TAIL(bb, apr_bucket_pool_create(t_val, t_len,
1454 ctx->pool, f->c->bucket_alloc));
1455 }
1456 else {
1457 SSI_CREATE_ERROR_BUCKET(ctx, f, bb);
1458 break;
1459 }
1460 }
1461
1462 return APR_SUCCESS;
1463 }
1464
1465 /*
1466 * <!--#if expr="..." -->
1467 */
1468 static apr_status_t handle_if(include_ctx_t *ctx, ap_filter_t *f,
1469 apr_bucket_brigade *bb)
1470 {
1471 char *tag = NULL;
1472 char *expr = NULL;
1473 request_rec *r = f->r;
1474 int expr_ret, was_error;
1475
1476 if (ctx->argc != 1) {
1477 ap_log_rerror(APLOG_MARK,
1478 (ctx->flags & SSI_FLAG_PRINTING)
1479 ? APLOG_ERR : APLOG_WARNING,
1480 0, r, (ctx->argc)
1481 ? "too many arguments for if element in %s"
1482 : "missing expr argument for if element in %s",
1483 r->filename);
1484 }
1485
1486 if (!(ctx->flags & SSI_FLAG_PRINTING)) {
1487 ++(ctx->if_nesting_level);
1488 return APR_SUCCESS;
1489 }
1490
1491 if (ctx->argc != 1) {
1492 SSI_CREATE_ERROR_BUCKET(ctx, f, bb);
1493 return APR_SUCCESS;
1494 }
1495
1496 ap_ssi_get_tag_and_value(ctx, &tag, &expr, SSI_VALUE_RAW);
1497
1498 if (strcmp(tag, "expr")) {
1499 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "unknown parameter \"%s\" "
1500 "to tag if in %s", tag, r->filename);
1501 SSI_CREATE_ERROR_BUCKET(ctx, f, bb);
1502 return APR_SUCCESS;
1503 }
1504
1505 if (!expr) {
1506 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "missing expr value for if "
1507 "element in %s", r->filename);
1508 SSI_CREATE_ERROR_BUCKET(ctx, f, bb);
1509 return APR_SUCCESS;
1510 }
1511
1512 DEBUG_PRINTF((ctx, "**** if expr=\"%s\"\n", expr));
1513
1514 expr_ret = ap_expr_evalstring(r, expr, &was_error, &ctx->intern->re,
1515 ssi_parse_string, ctx->intern->access_func);
1516
1517 if (was_error) {
1518 SSI_CREATE_ERROR_BUCKET(ctx, f, bb);
1519 return APR_SUCCESS;
1520 }
1521
1522 if (expr_ret) {
1523 ctx->flags |= (SSI_FLAG_PRINTING | SSI_FLAG_COND_TRUE);
1524 }
1525 else {
1526 ctx->flags &= SSI_FLAG_CLEAR_PRINT_COND;
1527 }
1528
1529 DEBUG_DUMP_COND(ctx, " if");
1530
1531 ctx->if_nesting_level = 0;
1532
1533 return APR_SUCCESS;
1534 }
1535
1536 /*
1537 * <!--#elif expr="..." -->
1538 */
1539 static apr_status_t handle_elif(include_ctx_t *ctx, ap_filter_t *f,
1540 apr_bucket_brigade *bb)
1541 {
1542 char *tag = NULL;
1543 char *expr = NULL;
1544 request_rec *r = f->r;
1545 int expr_ret, was_error;
1546
1547 if (ctx->argc != 1) {
1548 ap_log_rerror(APLOG_MARK,
1549 (!(ctx->if_nesting_level)) ? APLOG_ERR : APLOG_WARNING,
1550 0, r, (ctx->argc)
1551 ? "too many arguments for if element in %s"
1552 : "missing expr argument for if element in %s",
1553 r->filename);
1554 }
1555
1556 if (ctx->if_nesting_level) {
1557 return APR_SUCCESS;
1558 }
1559
1560 if (ctx->argc != 1) {
1561 SSI_CREATE_ERROR_BUCKET(ctx, f, bb);
1562 return APR_SUCCESS;
1563 }
1564
1565 ap_ssi_get_tag_and_value(ctx, &tag, &expr, SSI_VALUE_RAW);
1566
1567 if (strcmp(tag, "expr")) {
1568 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "unknown parameter \"%s\" "
1569 "to tag if in %s", tag, r->filename);
1570 SSI_CREATE_ERROR_BUCKET(ctx, f, bb);
1571 return APR_SUCCESS;
1572 }
1573
1574 if (!expr) {
1575 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "missing expr in elif "
1576 "statement: %s", r->filename);
1577 SSI_CREATE_ERROR_BUCKET(ctx, f, bb);
1578 return APR_SUCCESS;
1579 }
1580
1581 DEBUG_PRINTF((ctx, "**** elif expr=\"%s\"\n", expr));
1582 DEBUG_DUMP_COND(ctx, " elif");
1583
1584 if (ctx->flags & SSI_FLAG_COND_TRUE) {
1585 ctx->flags &= SSI_FLAG_CLEAR_PRINTING;
1586 return APR_SUCCESS;
1587 }
1588
1589 expr_ret = ap_expr_evalstring(r, expr, &was_error, &ctx->intern->re,
1590 ssi_parse_string, ctx->intern->access_func);
1591
1592 if (was_error) {
1593 SSI_CREATE_ERROR_BUCKET(ctx, f, bb);
1594 return APR_SUCCESS;
1595 }
1596
1597 if (expr_ret) {
1598 ctx->flags |= (SSI_FLAG_PRINTING | SSI_FLAG_COND_TRUE);
1599 }
1600 else {
1601 ctx->flags &= SSI_FLAG_CLEAR_PRINT_COND;
1602 }
1603
1604 DEBUG_DUMP_COND(ctx, " elif");
1605
1606 return APR_SUCCESS;
1607 }
1608
1609 /*
1610 * <!--#else -->
1611 */
1612 static apr_status_t handle_else(include_ctx_t *ctx, ap_filter_t *f,
1613 apr_bucket_brigade *bb)
1614 {
1615 request_rec *r = f->r;
1616
1617 if (ctx->argc) {
1618 ap_log_rerror(APLOG_MARK,
1619 (!(ctx->if_nesting_level)) ? APLOG_ERR : APLOG_WARNING,
1620 0, r, "else directive does not take tags in %s",
1621 r->filename);
1622 }
1623
1624 if (ctx->if_nesting_level) {
1625 return APR_SUCCESS;
1626 }
1627
1628 if (ctx->argc) {
1629 if (ctx->flags & SSI_FLAG_PRINTING) {
1630 SSI_CREATE_ERROR_BUCKET(ctx, f, bb);
1631 }
1632
1633 return APR_SUCCESS;
1634 }
1635
1636 DEBUG_DUMP_COND(ctx, " else");
1637
1638 if (ctx->flags & SSI_FLAG_COND_TRUE) {
1639 ctx->flags &= SSI_FLAG_CLEAR_PRINTING;
1640 }
1641 else {
1642 ctx->flags |= (SSI_FLAG_PRINTING | SSI_FLAG_COND_TRUE);
1643 }
1644
1645 return APR_SUCCESS;
1646 }
1647
1648 /*
1649 * <!--#endif -->
1650 */
1651 static apr_status_t handle_endif(include_ctx_t *ctx, ap_filter_t *f,
1652 apr_bucket_brigade *bb)
1653 {
1654 request_rec *r = f->r;
1655
1656 if (ctx->argc) {
1657 ap_log_rerror(APLOG_MARK,
1658 (!(ctx->if_nesting_level)) ? APLOG_ERR : APLOG_WARNING,
1659 0, r, "endif directive does not take tags in %s",
1660 r->filename);
1661 }
1662
1663 if (ctx->if_nesting_level) {
1664 --(ctx->if_nesting_level);
1665 return APR_SUCCESS;
1666 }
1667
1668 if (ctx->argc) {
1669 SSI_CREATE_ERROR_BUCKET(ctx, f, bb);
1670 return APR_SUCCESS;
1671 }
1672
1673 DEBUG_DUMP_COND(ctx, "endif");
1674
1675 ctx->flags |= (SSI_FLAG_PRINTING | SSI_FLAG_COND_TRUE);
1676
1677 return APR_SUCCESS;
1678 }
1679
1680 /*
1681 * <!--#set var="..." value="..." ... -->
1682 */
1683 static apr_status_t handle_set(include_ctx_t *ctx, ap_filter_t *f,
1684 apr_bucket_brigade *bb)
1685 {
1686 char *var = NULL;
1687 request_rec *r = f->r;
1688 request_rec *sub = r->main;
1689 apr_pool_t *p = r->pool;
1690
1691 if (ctx->argc < 2) {
1692 ap_log_rerror(APLOG_MARK,
1693 (ctx->flags & SSI_FLAG_PRINTING)
1694 ? APLOG_ERR : APLOG_WARNING,
1695 0, r, "missing argument for set element in %s",
1696 r->filename);
1697 }
1698
1699 if (!(ctx->flags & SSI_FLAG_PRINTING)) {
1700 return APR_SUCCESS;
1701 }
1702
1703 if (ctx->argc < 2) {
1704 SSI_CREATE_ERROR_BUCKET(ctx, f, bb);
1705 return APR_SUCCESS;
1706 }
1707
1708 /* we need to use the 'main' request pool to set notes as that is
1709 * a notes lifetime
1710 */
1711 while (sub) {
1712 p = sub->pool;
1713 sub = sub->main;
1714 }
1715
1716 while (1) {
1717 char *tag = NULL;
1718 char *tag_val = NULL;
1719
1720 ap_ssi_get_tag_and_value(ctx, &tag, &tag_val, SSI_VALUE_DECODED);
1721
1722 if (!tag || !tag_val) {
1723 break;
1724 }
1725
1726 if (!strcmp(tag, "var")) {
1727 var = ap_ssi_parse_string(ctx, tag_val, NULL, 0,
1728 SSI_EXPAND_DROP_NAME);
1729 }
1730 else if (!strcmp(tag, "value")) {
1731 char *parsed_string;
1732
1733 if (!var) {
1734 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "variable must "
1735 "precede value in set directive in %s",
1736 r->filename);
1737 SSI_CREATE_ERROR_BUCKET(ctx, f, bb);
1738 break;
1739 }
1740
1741 parsed_string = ap_ssi_parse_string(ctx, tag_val, NULL, 0,
1742 SSI_EXPAND_DROP_NAME);
1743 apr_table_setn(r->subprocess_env, apr_pstrdup(p, var),
1744 apr_pstrdup(p, parsed_string));
1745 }
1746 else {
1747 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "Invalid tag for set "
1748 "directive in %s", r->filename);
1749 SSI_CREATE_ERROR_BUCKET(ctx, f, bb);
1750 break;
1751 }
1752 }
1753
1754 return APR_SUCCESS;
1755 }
1756
1757 /*
1758 * <!--#printenv -->
1759 */
1760 static apr_status_t handle_printenv(include_ctx_t *ctx, ap_filter_t *f,
1761 apr_bucket_brigade *bb)
1762 {
1763 request_rec *r = f->r;
1764 const apr_array_header_t *arr;
1765 const apr_table_entry_t *elts;
1766 int i;
1767
1768 if (ctx->argc) {
1769 ap_log_rerror(APLOG_MARK,
1770 (ctx->flags & SSI_FLAG_PRINTING)
1771 ? APLOG_ERR : APLOG_WARNING,
1772 0, r, "printenv directive does not take tags in %s",
1773 r->filename);
1774 }
1775
1776 if (!(ctx->flags & SSI_FLAG_PRINTING)) {
1777 return APR_SUCCESS;
1778 }
1779
1780 if (ctx->argc) {
1781 SSI_CREATE_ERROR_BUCKET(ctx, f, bb);
1782 return APR_SUCCESS;
1783 }
1784
1785 arr = apr_table_elts(r->subprocess_env);
1786 elts = (apr_table_entry_t *)arr->elts;
1787
1788 for (i = 0; i < arr->nelts; ++i) {
1789 const char *key_text, *val_text;
1790 char *key_val, *next;
1791 apr_size_t k_len, v_len, kv_length;
1792
1793 /* get key */
1794 key_text = ap_escape_html(ctx->dpool, elts[i].key);
1795 k_len = strlen(key_text);
1796
1797 /* get value */
1798 val_text = elts[i].val;
1799 if (val_text == LAZY_VALUE) {
1800 val_text = add_include_vars_lazy(r, elts[i].key, ctx->time_str);
1801 }
1802 val_text = ap_escape_html(ctx->dpool, elts[i].val);
1803 v_len = strlen(val_text);
1804
1805 /* assemble result */
1806 kv_length = k_len + v_len + sizeof("=\n");
1807 key_val = apr_palloc(ctx->pool, kv_length);
1808 next = key_val;
1809
1810 memcpy(next, key_text, k_len);
1811 next += k_len;
1812 *next++ = '=';
1813 memcpy(next, val_text, v_len);
1814 next += v_len;
1815 *next++ = '\n';
1816 *next = 0;
1817
1818 APR_BRIGADE_INSERT_TAIL(bb, apr_bucket_pool_create(key_val, kv_length-1,
1819 ctx->pool, f->c->bucket_alloc));
1820 }
1821
1822 ctx->flush_now = 1;
1823 return APR_SUCCESS;
1824 }
1825
1826
1827 /*
1828 * +-------------------------------------------------------+
1829 * | |
1830 * | Main Includes-Filter Engine
1831 * | |
1832 * +-------------------------------------------------------+
1833 */
1834
1835 /* This is an implementation of the BNDM search algorithm.
1836 *
1837 * Fast and Flexible String Matching by Combining Bit-parallelism and
1838 * Suffix Automata (2001)
1839 * Gonzalo Navarro, Mathieu Raffinot
1840 *
1841 * http://www-igm.univ-mlv.fr/~raffinot/ftp/jea2001.ps.gz
1842 *
1843 * Initial code submitted by Sascha Schumann.
1844 */
1845
1846 /* Precompile the bndm_t data structure. */
1847 static bndm_t *bndm_compile(apr_pool_t *pool, const char *n, apr_size_t nl)
1848 {
1849 unsigned int x;
1850 const char *ne = n + nl;
1851 bndm_t *t = apr_palloc(pool, sizeof(*t));
1852
1853 memset(t->T, 0, sizeof(unsigned int) * 256);
1854 t->pattern_len = nl;
1855
1856 for (x = 1; n < ne; x <<= 1) {
1857 t->T[(unsigned char) *n++] |= x;
1858 }
1859
1860 t->x = x - 1;
1861
1862 return t;
1863 }
1864
1865 /* Implements the BNDM search algorithm (as described above).
1866 *
1867 * h - the string to look in
1868 * hl - length of the string to look for
1869 * t - precompiled bndm structure against the pattern
1870 *
1871 * Returns the count of character that is the first match or hl if no
1872 * match is found.
1873 */
1874 static apr_size_t bndm(bndm_t *t, const char *h, apr_size_t hl)
1875 {
1876 const char *skip;
1877 const char *he, *p, *pi;
1878 unsigned int *T, x, d;
1879 apr_size_t nl;
1880
1881 he = h + hl;
1882
1883 T = t->T;
1884 x = t->x;
1885 nl = t->pattern_len;
1886
1887 pi = h - 1; /* pi: p initial */
1888 p = pi + nl; /* compare window right to left. point to the first char */
1889
1890 while (p < he) {
1891 skip = p;
1892 d = x;
1893 do {
1894 d &= T[(unsigned char) *p--];
1895 if (!d) {
1896 break;
1897 }
1898 if ((d & 1)) {
1899 if (p != pi) {
1900 skip = p;
1901 }
1902 else {
1903 return p - h + 1;
1904 }
1905 }
1906 d >>= 1;
1907 } while (d);
1908
1909 pi = skip;
1910 p = pi + nl;
1911 }
1912
1913 return hl;
1914 }
1915
1916 /*
1917 * returns the index position of the first byte of start_seq (or the len of
1918 * the buffer as non-match)
1919 */
1920 static apr_size_t find_start_sequence(include_ctx_t *ctx, const char *data,
1921 apr_size_t len)
1922 {
1923 struct ssi_internal_ctx *intern = ctx->intern;
1924 apr_size_t slen = intern->start_seq_pat->pattern_len;
1925 apr_size_t index;
1926 const char *p, *ep;
1927
1928 if (len < slen) {
1929 p = data; /* try partial match at the end of the buffer (below) */
1930 }
1931 else {
1932 /* try fast bndm search over the buffer
1933 * (hopefully the whole start sequence can be found in this buffer)
1934 */
1935 index = bndm(intern->start_seq_pat, data, len);
1936
1937 /* wow, found it. ready. */
1938 if (index < len) {
1939 intern->state = PARSE_DIRECTIVE;
1940 return index;
1941 }
1942 else {
1943 /* ok, the pattern can't be found as whole in the buffer,
1944 * check the end for a partial match
1945 */
1946 p = data + len - slen + 1;
1947 }
1948 }
1949
1950 ep = data + len;
1951 do {
1952 while (p < ep && *p != *intern->start_seq) {
1953 ++p;
1954 }
1955
1956 index = p - data;
1957
1958 /* found a possible start_seq start */
1959 if (p < ep) {
1960 apr_size_t pos = 1;
1961
1962 ++p;
1963 while (p < ep && *p == intern->start_seq[pos]) {
1964 ++p;
1965 ++pos;
1966 }
1967
1968 /* partial match found. Store the info for the next round */
1969 if (p == ep) {
1970 intern->state = PARSE_HEAD;
1971 intern->parse_pos = pos;
1972 return index;
1973 }
1974 }
1975
1976 /* we must try all combinations; consider (e.g.) SSIStartTag "--->"
1977 * and a string data of "--.-" and the end of the buffer
1978 */
1979 p = data + index + 1;
1980 } while (p < ep);
1981
1982 /* no match */
1983 return len;
1984 }
1985
1986 /*
1987 * returns the first byte *after* the partial (or final) match.
1988 *
1989 * If we had to trick with the start_seq start, 'release' returns the
1990 * number of chars of the start_seq which appeared not to be part of a
1991 * full tag and may have to be passed down the filter chain.
1992 */
1993 static apr_size_t find_partial_start_sequence(include_ctx_t *ctx,
1994 const char *data,
1995 apr_size_t len,
1996 apr_size_t *release)
1997 {
1998 struct ssi_internal_ctx *intern = ctx->intern;
1999 apr_size_t pos, spos = 0;
2000 apr_size_t slen = intern->start_seq_pat->pattern_len;
2001 const char *p, *ep;
2002
2003 pos = intern->parse_pos;
2004 ep = data + len;
2005 *release = 0;
2006
2007 do {
2008 p = data;
2009
2010 while (p < ep && pos < slen && *p == intern->start_seq[pos]) {
2011 ++p;
2012 ++pos;
2013 }
2014
2015 /* full match */
2016 if (pos == slen) {
2017 intern->state = PARSE_DIRECTIVE;
2018 return (p - data);
2019 }
2020
2021 /* the whole buffer is a partial match */
2022 if (p == ep) {
2023 intern->parse_pos = pos;
2024 return (p - data);
2025 }
2026
2027 /* No match so far, but again:
2028 * We must try all combinations, since the start_seq is a random
2029 * user supplied string
2030 *
2031 * So: look if the first char of start_seq appears somewhere within
2032 * the current partial match. If it does, try to start a match that
2033 * begins with this offset. (This can happen, if a strange
2034 * start_seq like "---->" spans buffers)
2035 */
2036 if (spos < intern->parse_pos) {
2037 do {
2038 ++spos;
2039 ++*release;
2040 p = intern->start_seq + spos;
2041 pos = intern->parse_pos - spos;
2042
2043 while (pos && *p != *intern->start_seq) {
2044 ++p;
2045 ++spos;
2046 ++*release;
2047 --pos;
2048 }
2049
2050 /* if a matching beginning char was found, try to match the
2051 * remainder of the old buffer.
2052 */
2053 if (pos > 1) {
2054 apr_size_t t = 1;
2055
2056 ++p;
2057 while (t < pos && *p == intern->start_seq[t]) {
2058 ++p;
2059 ++t;
2060 }
2061
2062 if (t == pos) {
2063 /* yeah, another partial match found in the *old*
2064 * buffer, now test the *current* buffer for
2065 * continuing match
2066 */
2067 break;
2068 }
2069 }
2070 } while (pos > 1);
2071
2072 if (pos) {
2073 continue;
2074 }
2075 }
2076
2077 break;
2078 } while (1); /* work hard to find a match ;-) */
2079
2080 /* no match at all, release all (wrongly) matched chars so far */
2081 *release = intern->parse_pos;
2082 intern->state = PARSE_PRE_HEAD;
2083 return 0;
2084 }
2085
2086 /*
2087 * returns the position after the directive
2088 */
2089 static apr_size_t find_directive(include_ctx_t *ctx, const char *data,
2090 apr_size_t len, char ***store,
2091 apr_size_t **store_len)
2092 {
2093 struct ssi_internal_ctx *intern = ctx->intern;
2094 const char *p = data;
2095 const char *ep = data + len;
2096 apr_size_t pos;
2097
2098 switch (intern->state) {
2099 case PARSE_DIRECTIVE:
2100 while (p < ep && !apr_isspace(*p)) {
2101 /* we have to consider the case of missing space between directive
2102 * and end_seq (be somewhat lenient), e.g. <!--#printenv-->
2103 */
2104 if (*p == *intern->end_seq) {
2105 intern->state = PARSE_DIRECTIVE_TAIL;
2106 intern->parse_pos = 1;
2107 ++p;
2108 return (p - data);
2109 }
2110 ++p;
2111 }
2112
2113 if (p < ep) { /* found delimiter whitespace */
2114 intern->state = PARSE_DIRECTIVE_POSTNAME;
2115 *store = &intern->directive;
2116 *store_len = &intern->directive_len;
2117 }
2118
2119 break;
2120
2121 case PARSE_DIRECTIVE_TAIL:
2122 pos = intern->parse_pos;
2123
2124 while (p < ep && pos < intern->end_seq_len &&
2125 *p == intern->end_seq[pos]) {
2126 ++p;
2127 ++pos;
2128 }
2129
2130 /* full match, we're done */
2131 if (pos == intern->end_seq_len) {
2132 intern->state = PARSE_DIRECTIVE_POSTTAIL;
2133 *store = &intern->directive;
2134 *store_len = &intern->directive_len;
2135 break;
2136 }
2137
2138 /* partial match, the buffer is too small to match fully */
2139 if (p == ep) {
2140 intern->parse_pos = pos;
2141 break;
2142 }
2143
2144 /* no match. continue normal parsing */
2145 intern->state = PARSE_DIRECTIVE;
2146 return 0;
2147
2148 case PARSE_DIRECTIVE_POSTTAIL:
2149 intern->state = PARSE_EXECUTE;
2150 intern->directive_len -= intern->end_seq_len;
2151 /* continue immediately with the next state */
2152
2153 case PARSE_DIRECTIVE_POSTNAME:
2154 if (PARSE_DIRECTIVE_POSTNAME == intern->state) {
2155 intern->state = PARSE_PRE_ARG;
2156 }
2157 ctx->argc = 0;
2158 intern->argv = NULL;
2159
2160 if (!intern->directive_len) {
2161 intern->error = 1;
2162 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, intern->r, "missing "
2163 "directive name in parsed document %s",
2164 intern->r->filename);
2165 }
2166 else {
2167 char *sp = intern->directive;
2168 char *sep = intern->directive + intern->directive_len;
2169
2170 /* normalize directive name */
2171 for (; sp < sep; ++sp) {
2172 *sp = apr_tolower(*sp);
2173 }
2174 }
2175
2176 return 0;
2177
2178 default:
2179 /* get a rid of a gcc warning about unhandled enumerations */
2180 break;
2181 }
2182
2183 return (p - data);
2184 }
2185
2186 /*
2187 * find out whether the next token is (a possible) end_seq or an argument
2188 */
2189 static apr_size_t find_arg_or_tail(include_ctx_t *ctx, const char *data,
2190 apr_size_t len)
2191 {
2192 struct ssi_internal_ctx *intern = ctx->intern;
2193 const char *p = data;
2194 const char *ep = data + len;
2195
2196 /* skip leading WS */
2197 while (p < ep && apr_isspace(*p)) {
2198 ++p;
2199 }
2200
2201 /* buffer doesn't consist of whitespaces only */
2202 if (p < ep) {
2203 intern->state = (*p == *intern->end_seq) ? PARSE_TAIL : PARSE_ARG;
2204 }
2205
2206 return (p - data);
2207 }
2208
2209 /*
2210 * test the stream for end_seq. If it doesn't match at all, it must be an
2211 * argument
2212 */
2213 static apr_size_t find_tail(include_ctx_t *ctx, const char *data,
2214 apr_size_t len)
2215 {
2216 struct ssi_internal_ctx *intern = ctx->intern;
2217 const char *p = data;
2218 const char *ep = data + len;
2219 apr_size_t pos = intern->parse_pos;
2220
2221 if (PARSE_TAIL == intern->state) {
2222 intern->state = PARSE_TAIL_SEQ;
2223 pos = intern->parse_pos = 0;
2224 }
2225
2226 while (p < ep && pos < intern->end_seq_len && *p == intern->end_seq[pos]) {
2227 ++p;
2228 ++pos;
2229 }
2230
2231 /* bingo, full match */
2232 if (pos == intern->end_seq_len) {
2233 intern->state = PARSE_EXECUTE;
2234 return (p - data);
2235 }
2236
2237 /* partial match, the buffer is too small to match fully */
2238 if (p == ep) {
2239 intern->parse_pos = pos;
2240 return (p - data);
2241 }
2242
2243 /* no match. It must be an argument string then
2244 * The caller should cleanup and rewind to the reparse point
2245 */
2246 intern->state = PARSE_ARG;
2247 return 0;
2248 }
2249
2250 /*
2251 * extract name=value from the buffer
2252 * A pcre-pattern could look (similar to):
2253 * name\s*(?:=\s*(["'`]?)value\1(?>\s*))?
2254 */
2255 static apr_size_t find_argument(include_ctx_t *ctx, const char *data,
2256 apr_size_t len, char ***store,
2257 apr_size_t **store_len)
2258 {
2259 struct ssi_internal_ctx *intern = ctx->intern;
2260 const char *p = data;
2261 const char *ep = data + len;
2262
2263 switch (intern->state) {
2264 case PARSE_ARG:
2265 /*
2266 * create argument structure and append it to the current list
2267 */
2268 intern->current_arg = apr_palloc(ctx->dpool,
2269 sizeof(*intern->current_arg));
2270 intern->current_arg->next = NULL;
2271
2272 ++(ctx->argc);
2273 if (!intern->argv) {
2274 intern->argv = intern->current_arg;
2275 }
2276 else {
2277 arg_item_t *newarg = intern->argv;
2278
2279 while (newarg->next) {
2280 newarg = newarg->next;
2281 }
2282 newarg->next = intern->current_arg;
2283 }
2284
2285 /* check whether it's a valid one. If it begins with a quote, we
2286 * can safely assume, someone forgot the name of the argument
2287 */
2288 switch (*p) {
2289 case '"': case '\'': case '`':
2290 *store = NULL;
2291
2292 intern->state = PARSE_ARG_VAL;
2293 intern->quote = *p++;
2294 intern->current_arg->name = NULL;
2295 intern->current_arg->name_len = 0;
2296 intern->error = 1;
2297
2298 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, intern->r, "missing "
2299 "argument name for value to tag %s in %s",
2300 apr_pstrmemdup(intern->r->pool, intern->directive,
2301 intern->directive_len),
2302 intern->r->filename);
2303
2304 return (p - data);
2305
2306 default:
2307 intern->state = PARSE_ARG_NAME;
2308 }
2309 /* continue immediately with next state */
2310
2311 case PARSE_ARG_NAME:
2312 while (p < ep && !apr_isspace(*p) && *p != '=') {
2313 ++p;
2314 }
2315
2316 if (p < ep) {
2317 intern->state = PARSE_ARG_POSTNAME;
2318 *store = &intern->current_arg->name;
2319 *store_len = &intern->current_arg->name_len;
2320 return (p - data);
2321 }
2322 break;
2323
2324 case PARSE_ARG_POSTNAME:
2325 intern->current_arg->name = apr_pstrmemdup(ctx->dpool,
2326 intern->current_arg->name,
2327 intern->current_arg->name_len);
2328 if (!intern->current_arg->name_len) {
2329 intern->error = 1;
2330 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, intern->r, "missing "
2331 "argument name for value to tag %s in %s",
2332 apr_pstrmemdup(intern->r->pool, intern->directive,
2333 intern->directive_len),
2334 intern->r->filename);
2335 }
2336 else {
2337 char *sp = intern->current_arg->name;
2338
2339 /* normalize the name */
2340 while (*sp) {
2341 *sp = apr_tolower(*sp);
2342 ++sp;
2343 }
2344 }
2345
2346 intern->state = PARSE_ARG_EQ;
2347 /* continue with next state immediately */
2348
2349 case PARSE_ARG_EQ:
2350 *store = NULL;
2351
2352 while (p < ep && apr_isspace(*p)) {
2353 ++p;
2354 }
2355
2356 if (p < ep) {
2357 if (*p == '=') {
2358 intern->state = PARSE_ARG_PREVAL;
2359 ++p;
2360 }
2361 else { /* no value */
2362 intern->current_arg->value = NULL;
2363 intern->state = PARSE_PRE_ARG;
2364 }
2365
2366 return (p - data);
2367 }
2368 break;
2369
2370 case PARSE_ARG_PREVAL:
2371 *store = NULL;
2372
2373 while (p < ep && apr_isspace(*p)) {
2374 ++p;
2375 }
2376
2377 /* buffer doesn't consist of whitespaces only */
2378 if (p < ep) {
2379 intern->state = PARSE_ARG_VAL;
2380 switch (*p) {
2381 case '"': case '\'': case '`':
2382 intern->quote = *p++;
2383 break;
2384 default:
2385 intern->quote = '\0';
2386 break;
2387 }
2388
2389 return (p - data);
2390 }
2391 break;
2392
2393 case PARSE_ARG_VAL_ESC:
2394 if (*p == intern->quote) {
2395 ++p;
2396 }
2397 intern->state = PARSE_ARG_VAL;
2398 /* continue with next state immediately */
2399
2400 case PARSE_ARG_VAL:
2401 for (; p < ep; ++p) {
2402 if (intern->quote && *p == '\\') {
2403 ++p;
2404 if (p == ep) {
2405 intern->state = PARSE_ARG_VAL_ESC;
2406 break;
2407 }
2408
2409 if (*p != intern->quote) {
2410 --p;
2411 }
2412 }
2413 else if (intern->quote && *p == intern->quote) {
2414 ++p;
2415 *store = &intern->current_arg->value;
2416 *store_len = &intern->current_arg->value_len;
2417 intern->state = PARSE_ARG_POSTVAL;
2418 break;
2419 }
2420 else if (!intern->quote && apr_isspace(*p)) {
2421 ++p;
2422 *store = &intern->current_arg->value;
2423 *store_len = &intern->current_arg->value_len;
2424 intern->state = PARSE_ARG_POSTVAL;
2425 break;
2426 }
2427 }
2428
2429 return (p - data);
2430
2431 case PARSE_ARG_POSTVAL:
2432 /*
2433 * The value is still the raw input string. Finally clean it up.
2434 */
2435 --(intern->current_arg->value_len);
2436
2437 /* strip quote escaping \ from the string */
2438 if (intern->quote) {
2439 apr_size_t shift = 0;
2440 char *sp;
2441
2442 sp = intern->current_arg->value;
2443 ep = intern->current_arg->value + intern->current_arg->value_len;
2444 while (sp < ep && *sp != '\\') {
2445 ++sp;
2446 }
2447 for (; sp < ep; ++sp) {
2448 if (*sp == '\\' && sp[1] == intern->quote) {
2449 ++sp;
2450 ++shift;
2451 }
2452 if (shift) {
2453 *(sp-shift) = *sp;
2454 }
2455 }
2456
2457 intern->current_arg->value_len -= shift;
2458 }
2459
2460 intern->current_arg->value[intern->current_arg->value_len] = '\0';
2461 intern->state = PARSE_PRE_ARG;
2462
2463 return 0;
2464
2465 default:
2466 /* get a rid of a gcc warning about unhandled enumerations */
2467 break;
2468 }
2469
2470 return len; /* partial match of something */
2471 }
2472
2473 /*
2474 * This is the main loop over the current bucket brigade.
2475 */
2476 static apr_status_t send_parsed_content(ap_filter_t *f, apr_bucket_brigade *bb)
2477 {
2478 include_ctx_t *ctx = f->ctx;
2479 struct ssi_internal_ctx *intern = ctx->intern;
2480 request_rec *r = f->r;
2481 apr_bucket *b = APR_BRIGADE_FIRST(bb);
2482 apr_bucket_brigade *pass_bb;
2483 apr_status_t rv = APR_SUCCESS;
2484 char *magic; /* magic pointer for sentinel use */
2485
2486 /* fast exit */
2487 if (APR_BRIGADE_EMPTY(bb)) {
2488 return APR_SUCCESS;
2489 }
2490
2491 /* we may crash, since already cleaned up; hand over the responsibility
2492 * to the next filter;-)
2493 */
2494 if (intern->seen_eos) {
2495 return ap_pass_brigade(f->next, bb);
2496 }
2497
2498 /* All stuff passed along has to be put into that brigade */
2499 pass_bb = apr_brigade_create(ctx->pool, f->c->bucket_alloc);
2500
2501 /* initialization for this loop */
2502 intern->bytes_read = 0;
2503 intern->error = 0;
2504 intern->r = r;
2505 ctx->flush_now = 0;
2506
2507 /* loop over the current bucket brigade */
2508 while (b != APR_BRIGADE_SENTINEL(bb)) {
2509 const char *data = NULL;
2510 apr_size_t len, index, release;
2511 apr_bucket *newb = NULL;
2512 char **store = &magic;
2513 apr_size_t *store_len = NULL;
2514
2515 /* handle meta buckets before reading any data */
2516 if (APR_BUCKET_IS_METADATA(b)) {
2517 newb = APR_BUCKET_NEXT(b);
2518
2519 APR_BUCKET_REMOVE(b);
2520
2521 if (APR_BUCKET_IS_EOS(b)) {
2522 intern->seen_eos = 1;
2523
2524 /* Hit end of stream, time for cleanup ... But wait!
2525 * Perhaps we're not ready yet. We may have to loop one or
2526 * two times again to finish our work. In that case, we
2527 * just re-insert the EOS bucket to allow for an extra loop.
2528 *
2529 * PARSE_EXECUTE means, we've hit a directive just before the
2530 * EOS, which is now waiting for execution.
2531 *
2532 * PARSE_DIRECTIVE_POSTTAIL means, we've hit a directive with
2533 * no argument and no space between directive and end_seq
2534 * just before the EOS. (consider <!--#printenv--> as last
2535 * or only string within the stream). This state, however,
2536 * just cleans up and turns itself to PARSE_EXECUTE, which
2537 * will be passed through within the next (and actually
2538 * last) round.
2539 */
2540 if (PARSE_EXECUTE == intern->state ||
2541 PARSE_DIRECTIVE_POSTTAIL == intern->state) {
2542 APR_BUCKET_INSERT_BEFORE(newb, b);
2543 }
2544 else {
2545 break; /* END OF STREAM */
2546 }
2547 }
2548 else {
2549 APR_BRIGADE_INSERT_TAIL(pass_bb, b);
2550
2551 if (APR_BUCKET_IS_FLUSH(b)) {
2552 ctx->flush_now = 1;
2553 }
2554
2555 b = newb;
2556 continue;
2557 }
2558 }
2559
2560 /* enough is enough ... */
2561 if (ctx->flush_now ||
2562 intern->bytes_read > AP_MIN_BYTES_TO_WRITE) {
2563
2564 if (!APR_BRIGADE_EMPTY(pass_bb)) {
2565 rv = ap_pass_brigade(f->next, pass_bb);
2566 if (rv != APR_SUCCESS) {
2567 apr_brigade_destroy(pass_bb);
2568 return rv;
2569 }
2570 }
2571
2572 ctx->flush_now = 0;
2573 intern->bytes_read = 0;
2574 }
2575
2576 /* read the current bucket data */
2577 len = 0;
2578 if (!intern->seen_eos) {
2579 if (intern->bytes_read > 0) {
2580 rv = apr_bucket_read(b, &data, &len, APR_NONBLOCK_READ);
2581 if (APR_STATUS_IS_EAGAIN(rv)) {
2582 ctx->flush_now = 1;
2583 continue;
2584 }
2585 }
2586
2587 if (!len || rv != APR_SUCCESS) {
2588 rv = apr_bucket_read(b, &data, &len, APR_BLOCK_READ);
2589 }
2590
2591 if (rv != APR_SUCCESS) {
2592 apr_brigade_destroy(pass_bb);
2593 return rv;
2594 }
2595
2596 intern->bytes_read += len;
2597 }
2598
2599 /* zero length bucket, fetch next one */
2600 if (!len && !intern->seen_eos) {
2601 b = APR_BUCKET_NEXT(b);
2602 continue;
2603 }
2604
2605 /*
2606 * it's actually a data containing bucket, start/continue parsing
2607 */
2608
2609 switch (intern->state) {
2610 /* no current tag; search for start sequence */
2611 case PARSE_PRE_HEAD:
2612 index = find_start_sequence(ctx, data, len);
2613
2614 if (index < len) {
2615 apr_bucket_split(b, index);
2616 }
2617
2618 newb = APR_BUCKET_NEXT(b);
2619 if (ctx->flags & SSI_FLAG_PRINTING) {
2620 APR_BUCKET_REMOVE(b);
2621 APR_BRIGADE_INSERT_TAIL(pass_bb, b);
2622 }
2623 else {
2624 apr_bucket_delete(b);
2625 }
2626
2627 if (index < len) {
2628 /* now delete the start_seq stuff from the remaining bucket */
2629 if (PARSE_DIRECTIVE == intern->state) { /* full match */
2630 apr_bucket_split(newb, intern->start_seq_pat->pattern_len);
2631 ctx->flush_now = 1; /* pass pre-tag stuff */
2632 }
2633
2634 b = APR_BUCKET_NEXT(newb);
2635 apr_bucket_delete(newb);
2636 }
2637 else {
2638 b = newb;
2639 }
2640
2641 break;
2642
2643 /* we're currently looking for the end of the start sequence */
2644 case PARSE_HEAD:
2645 index = find_partial_start_sequence(ctx, data, len, &release);
2646
2647 /* check if we mismatched earlier and have to release some chars */
2648 if (release && (ctx->flags & SSI_FLAG_PRINTING)) {
2649 char *to_release = apr_pmemdup(ctx->pool, intern->start_seq, release);
2650
2651 newb = apr_bucket_pool_create(to_release, release, ctx->pool,
2652 f->c->bucket_alloc);
2653 APR_BRIGADE_INSERT_TAIL(pass_bb, newb);
2654 }
2655
2656 if (index) { /* any match */
2657 /* now delete the start_seq stuff from the remaining bucket */
2658 if (PARSE_DIRECTIVE == intern->state) { /* final match */
2659 apr_bucket_split(b, index);
2660 ctx->flush_now = 1; /* pass pre-tag stuff */
2661 }
2662 newb = APR_BUCKET_NEXT(b);
2663 apr_bucket_delete(b);
2664 b = newb;
2665 }
2666
2667 break;
2668
2669 /* we're currently grabbing the directive name */
2670 case PARSE_DIRECTIVE:
2671 case PARSE_DIRECTIVE_POSTNAME:
2672 case PARSE_DIRECTIVE_TAIL:
2673 case PARSE_DIRECTIVE_POSTTAIL:
2674 index = find_directive(ctx, data, len, &store, &store_len);
2675
2676 if (index) {
2677 apr_bucket_split(b, index);
2678 newb = APR_BUCKET_NEXT(b);
2679 }
2680
2681 if (store) {
2682 if (index) {
2683 APR_BUCKET_REMOVE(b);
2684 apr_bucket_setaside(b, r->pool);
2685 APR_BRIGADE_INSERT_TAIL(intern->tmp_bb, b);
2686 b = newb;
2687 }
2688
2689 /* time for cleanup? */
2690 if (store != &magic) {
2691 apr_brigade_pflatten(intern->tmp_bb, store, store_len,
2692 ctx->dpool);
2693 apr_brigade_cleanup(intern->tmp_bb);
2694 }
2695 }
2696 else if (index) {
2697 apr_bucket_delete(b);
2698 b = newb;
2699 }
2700
2701 break;
2702
2703 /* skip WS and find out what comes next (arg or end_seq) */
2704 case PARSE_PRE_ARG:
2705 index = find_arg_or_tail(ctx, data, len);
2706
2707 if (index) { /* skipped whitespaces */
2708 if (index < len) {
2709 apr_bucket_split(b, index);
2710 }
2711 newb = APR_BUCKET_NEXT(b);
2712 apr_bucket_delete(b);
2713 b = newb;
2714 }
2715
2716 break;
2717
2718 /* currently parsing name[=val] */
2719 case PARSE_ARG:
2720 case PARSE_ARG_NAME:
2721 case PARSE_ARG_POSTNAME:
2722 case PARSE_ARG_EQ:
2723 case PARSE_ARG_PREVAL:
2724 case PARSE_ARG_VAL:
2725 case PARSE_ARG_VAL_ESC:
2726 case PARSE_ARG_POSTVAL:
2727 index = find_argument(ctx, data, len, &store, &store_len);
2728
2729 if (index) {
2730 apr_bucket_split(b, index);
2731 newb = APR_BUCKET_NEXT(b);
2732 }
2733
2734 if (store) {
2735 if (index) {
2736 APR_BUCKET_REMOVE(b);
2737 apr_bucket_setaside(b, r->pool);
2738 APR_BRIGADE_INSERT_TAIL(intern->tmp_bb, b);
2739 b = newb;
2740 }
2741
2742 /* time for cleanup? */
2743 if (store != &magic) {
2744 apr_brigade_pflatten(intern->tmp_bb, store, store_len,
2745 ctx->dpool);
2746 apr_brigade_cleanup(intern->tmp_bb);
2747 }
2748 }
2749 else if (index) {
2750 apr_bucket_delete(b);
2751 b = newb;
2752 }
2753
2754 break;
2755
2756 /* try to match end_seq at current pos. */
2757 case PARSE_TAIL:
2758 case PARSE_TAIL_SEQ:
2759 index = find_tail(ctx, data, len);
2760
2761 switch (intern->state) {
2762 case PARSE_EXECUTE: /* full match */
2763 apr_bucket_split(b, index);
2764 newb = APR_BUCKET_NEXT(b);
2765 apr_bucket_delete(b);
2766 b = newb;
2767 break;
2768
2769 case PARSE_ARG: /* no match */
2770 /* PARSE_ARG must reparse at the beginning */
2771 APR_BRIGADE_PREPEND(bb, intern->tmp_bb);
2772 b = APR_BRIGADE_FIRST(bb);
2773 break;
2774
2775 default: /* partial match */
2776 newb = APR_BUCKET_NEXT(b);
2777 APR_BUCKET_REMOVE(b);
2778 apr_bucket_setaside(b, r->pool);
2779 APR_BRIGADE_INSERT_TAIL(intern->tmp_bb, b);
2780 b = newb;
2781 break;
2782 }
2783
2784 break;
2785
2786 /* now execute the parsed directive, cleanup the space and
2787 * start again with PARSE_PRE_HEAD
2788 */
2789 case PARSE_EXECUTE:
2790 /* if there was an error, it was already logged; just stop here */
2791 if (intern->error) {
2792 if (ctx->flags & SSI_FLAG_PRINTING) {
2793 SSI_CREATE_ERROR_BUCKET(ctx, f, pass_bb);
2794 intern->error = 0;
2795 }
2796 }
2797 else {
2798 include_handler_fn_t *handle_func;
2799
2800 handle_func =
2801 (include_handler_fn_t *)apr_hash_get(include_handlers, intern->directive,
2802 intern->directive_len);
2803
2804 if (handle_func) {
2805 DEBUG_INIT(ctx, f, pass_bb);
2806 rv = handle_func(ctx, f, pass_bb);
2807 if (rv != APR_SUCCESS) {
2808 apr_brigade_destroy(pass_bb);
2809 return rv;
2810 }
2811 }
2812 else {
2813 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
2814 "unknown directive \"%s\" in parsed doc %s",
2815 apr_pstrmemdup(r->pool, intern->directive,
2816 intern->directive_len),
2817 r->filename);
2818 if (ctx->flags & SSI_FLAG_PRINTING) {
2819 SSI_CREATE_ERROR_BUCKET(ctx, f, pass_bb);
2820 }
2821 }
2822 }
2823
2824 /* cleanup */
2825 apr_pool_clear(ctx->dpool);
2826 apr_brigade_cleanup(intern->tmp_bb);
2827
2828 /* Oooof. Done here, start next round */
2829 intern->state = PARSE_PRE_HEAD;
2830 break;
2831
2832 } /* switch(ctx->state) */
2833
2834 } /* while(brigade) */
2835
2836 /* End of stream. Final cleanup */
2837 if (intern->seen_eos) {
2838 if (PARSE_HEAD == intern->state) {
2839 if (ctx->flags & SSI_FLAG_PRINTING) {
2840 char *to_release = apr_pmemdup(ctx->pool, intern->start_seq,
2841 intern->parse_pos);
2842
2843 APR_BRIGADE_INSERT_TAIL(pass_bb,
2844 apr_bucket_pool_create(to_release,
2845 intern->parse_pos, ctx->pool,
2846 f->c->bucket_alloc));
2847 }
2848 }
2849 else if (PARSE_PRE_HEAD != intern->state) {
2850 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
2851 "SSI directive was not properly finished at the end "
2852 "of parsed document %s", r->filename);
2853 if (ctx->flags & SSI_FLAG_PRINTING) {
2854 SSI_CREATE_ERROR_BUCKET(ctx, f, pass_bb);
2855 }
2856 }
2857
2858 if (!(ctx->flags & SSI_FLAG_PRINTING)) {
2859 ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r,
2860 "missing closing endif directive in parsed document"
2861 " %s", r->filename);
2862 }
2863
2864 /* cleanup our temporary memory */
2865 apr_brigade_destroy(intern->tmp_bb);
2866 apr_pool_destroy(ctx->dpool);
2867
2868 /* don't forget to finally insert the EOS bucket */
2869 APR_BRIGADE_INSERT_TAIL(pass_bb, b);
2870 }
2871
2872 /* if something's left over, pass it along */
2873 if (!APR_BRIGADE_EMPTY(pass_bb)) {
2874 rv = ap_pass_brigade(f->next, pass_bb);
2875 }
2876 else {
2877 rv = APR_SUCCESS;
2878 apr_brigade_destroy(pass_bb);
2879 }
2880 return rv;
2881 }
2882
2883
2884 /*
2885 * +-------------------------------------------------------+
2886 * | |
2887 * | Runtime Hooks
2888 * | |
2889 * +-------------------------------------------------------+
2890 */
2891
2892 static int includes_setup(ap_filter_t *f)
2893 {
2894 include_dir_config *conf = ap_get_module_config(f->r->per_dir_config,
2895 &include_module);
2896
2897 /* When our xbithack value isn't set to full or our platform isn't
2898 * providing group-level protection bits or our group-level bits do not
2899 * have group-execite on, we will set the no_local_copy value to 1 so
2900 * that we will not send 304s.
2901 */
2902 if ((conf->xbithack != XBITHACK_FULL)
2903 || !(f->r->finfo.valid & APR_FINFO_GPROT)
2904 || !(f->r->finfo.protection & APR_GEXECUTE)) {
2905 f->r->no_local_copy = 1;
2906 }
2907
2908 /* Don't allow ETag headers to be generated - see RFC2616 - 13.3.4.
2909 * We don't know if we are going to be including a file or executing
2910 * a program - in either case a strong ETag header will likely be invalid.
2911 */
2912 apr_table_setn(f->r->notes, "no-etag", "");
2913
2914 return OK;
2915 }
2916
2917 static apr_status_t includes_filter(ap_filter_t *f, apr_bucket_brigade *b)
2918 {
2919 request_rec *r = f->r;
2920 include_ctx_t *ctx = f->ctx;
2921 include_dir_config *conf = ap_get_module_config(r->per_dir_config,
2922 &include_module);
2923
2924 include_server_config *sconf= ap_get_module_config(r->server->module_config,
2925 &include_module);
2926
2927 if (!(ap_allow_options(r) & OPT_INCLUDES)) {
2928 ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r,
2929 "mod_include: Options +Includes (or IncludesNoExec) "
2930 "wasn't set, INCLUDES filter removed");
2931 ap_remove_output_filter(f);
2932 return ap_pass_brigade(f->next, b);
2933 }