Apache HTTPD
mod_include.c
Go to the documentation of this file.
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 */
63typedef struct result_item {
66 const char *string;
68
69/* conditional expression parser stuff */
87
88typedef struct {
90 const char *value;
91#ifdef DEBUG_INCLUDE
92 const char *s;
93#endif
94} token_t;
95
96typedef struct parse_node {
101 int value;
102 int done;
103#ifdef DEBUG_INCLUDE
104 int dump_done;
105#endif
107
114
115typedef struct {
116 const char *default_error_msg;
117 const char *default_time_fmt;
118 const char *undefined_echo;
120 signed char lastmodified;
121 signed char etag;
122 signed char legacy_expr;
124
125typedef struct {
126 const char *default_start_tag;
127 const char *default_end_tag;
129
130/* main parser states */
151
159
167
168typedef struct {
169 unsigned int T[256];
170 unsigned int x;
172} bndm_t;
173
177 int error;
178 char quote; /* quote character value (or \0) */
179 apr_size_t parse_pos; /* parse position of partial matches */
181
183
184 const char *start_seq;
186 const char *end_seq;
188 char *directive; /* name of the current directive */
189 apr_size_t directive_len; /* length of the current directive name */
190
191 arg_item_t *current_arg; /* currently parsed argument */
192 arg_item_t *argv; /* all arguments */
193
194 backref_t *re; /* NULL if there wasn't a regex yet */
195
196 const char *undefined_echo;
198
199 char legacy_expr; /* use ap_expr or legacy mod_include
200 expression parser? */
201
202 ap_expr_eval_ctx_t *expr_eval_ctx; /* NULL if there wasn't an ap_expr yet */
203 const char *expr_vary_this; /* for use by ap_expr_eval_ctx */
204 const char *expr_err; /* for use by ap_expr_eval_ctx */
205#ifdef DEBUG_INCLUDE
206 struct {
207 ap_filter_t *f;
209 } debug;
210#endif
211};
212
213
214/*
215 * +-------------------------------------------------------+
216 * | |
217 * | Debugging Utilities
218 * | |
219 * +-------------------------------------------------------+
220 */
221
222#ifdef DEBUG_INCLUDE
223
224#define TYPE_TOKEN(token, ttype) do { \
225 (token)->type = ttype; \
226 (token)->s = #ttype; \
227} while(0)
228
229#define CREATE_NODE(ctx, name) do { \
230 (name) = apr_palloc((ctx)->dpool, sizeof(*(name))); \
231 (name)->parent = (name)->left = (name)->right = NULL; \
232 (name)->done = 0; \
233 (name)->dump_done = 0; \
234} while(0)
235
236static void debug_printf(include_ctx_t *ctx, const char *fmt, ...)
237{
238 va_list ap;
239 char *debug__str;
240
241 va_start(ap, fmt);
242 debug__str = apr_pvsprintf(ctx->pool, fmt, ap);
243 va_end(ap);
244
246 debug__str, strlen(debug__str), ctx->pool,
247 ctx->intern->debug.f->c->bucket_alloc));
248}
249
250#define DUMP__CHILD(ctx, is, node, child) if (1) { \
251 parse_node_t *d__c = node->child; \
252 if (d__c) { \
253 if (!d__c->dump_done) { \
254 if (d__c->parent != node) { \
255 debug_printf(ctx, "!!! Parse tree is not consistent !!!\n"); \
256 if (!d__c->parent) { \
257 debug_printf(ctx, "Parent of " #child " child node is " \
258 "NULL.\n"); \
259 } \
260 else { \
261 debug_printf(ctx, "Parent of " #child " child node " \
262 "points to another node (of type %s)!\n", \
263 d__c->parent->token.s); \
264 } \
265 return; \
266 } \
267 node = d__c; \
268 continue; \
269 } \
270 } \
271 else { \
272 debug_printf(ctx, "%s(missing)\n", is); \
273 } \
274}
275
277{
278 parse_node_t *current;
279 char *is;
280
281 if (!root) {
282 debug_printf(ctx, " -- Parse Tree empty --\n\n");
283 return;
284 }
285
286 debug_printf(ctx, " ----- Parse Tree -----\n");
287 current = root;
288 is = " ";
289
290 while (current) {
291 switch (current->token.type) {
292 case TOKEN_STRING:
293 case TOKEN_RE:
294 debug_printf(ctx, "%s%s (%s)\n", is, current->token.s,
295 current->token.value);
296 current->dump_done = 1;
297 current = current->parent;
298 continue;
299
300 case TOKEN_NOT:
301 case TOKEN_GROUP:
302 case TOKEN_RBRACE:
303 case TOKEN_LBRACE:
304 if (!current->dump_done) {
305 debug_printf(ctx, "%s%s\n", is, current->token.s);
306 is = apr_pstrcat(ctx->dpool, is, " ", NULL);
307 current->dump_done = 1;
308 }
309
310 DUMP__CHILD(ctx, is, current, right)
311
312 if (!current->right || current->right->dump_done) {
313 is = apr_pstrmemdup(ctx->dpool, is, strlen(is) - 4);
314 if (current->right) current->right->dump_done = 0;
315 current = current->parent;
316 }
317 continue;
318
319 default:
320 if (!current->dump_done) {
321 debug_printf(ctx, "%s%s\n", is, current->token.s);
322 is = apr_pstrcat(ctx->dpool, is, " ", NULL);
323 current->dump_done = 1;
324 }
325
326 DUMP__CHILD(ctx, is, current, left)
327 DUMP__CHILD(ctx, is, current, right)
328
329 if ((!current->left || current->left->dump_done) &&
330 (!current->right || current->right->dump_done)) {
331
332 is = apr_pstrmemdup(ctx->dpool, is, strlen(is) - 4);
333 if (current->left) current->left->dump_done = 0;
334 if (current->right) current->right->dump_done = 0;
335 current = current->parent;
336 }
337 continue;
338 }
339 }
340
341 /* it is possible to call this function within the parser loop, to see
342 * how the tree is built. That way, we must cleanup after us to dump
343 * always the whole tree
344 */
345 root->dump_done = 0;
346 if (root->left) root->left->dump_done = 0;
347 if (root->right) root->right->dump_done = 0;
348
349 debug_printf(ctx, " --- End Parse Tree ---\n\n");
350}
351
352#define DEBUG_INIT(ctx, filter, brigade) do { \
353 (ctx)->intern->debug.f = filter; \
354 (ctx)->intern->debug.bb = brigade; \
355} while(0)
356
357#define DEBUG_PRINTF(arg) debug_printf arg
358
359#define DEBUG_DUMP_TOKEN(ctx, token) do { \
360 token_t *d__t = (token); \
361 \
362 if (d__t->type == TOKEN_STRING || d__t->type == TOKEN_RE) { \
363 DEBUG_PRINTF(((ctx), " Found: %s (%s)\n", d__t->s, d__t->value)); \
364 } \
365 else { \
366 DEBUG_PRINTF((ctx, " Found: %s\n", d__t->s)); \
367 } \
368} while(0)
369
370#define DEBUG_DUMP_EVAL(ctx, node) do { \
371 char c = '"'; \
372 switch ((node)->token.type) { \
373 case TOKEN_STRING: \
374 debug_printf((ctx), " Evaluate: %s (%s) -> %c\n", (node)->token.s,\
375 (node)->token.value, ((node)->value) ? '1':'0'); \
376 break; \
377 case TOKEN_AND: \
378 case TOKEN_OR: \
379 debug_printf((ctx), " Evaluate: %s (Left: %s; Right: %s) -> %c\n",\
380 (node)->token.s, \
381 (((node)->left->done) ? ((node)->left->value ?"1":"0") \
382 : "short circuited"), \
383 (((node)->right->done) ? ((node)->right->value?"1":"0") \
384 : "short circuited"), \
385 (node)->value ? '1' : '0'); \
386 break; \
387 case TOKEN_EQ: \
388 case TOKEN_NE: \
389 case TOKEN_GT: \
390 case TOKEN_GE: \
391 case TOKEN_LT: \
392 case TOKEN_LE: \
393 if ((node)->right->token.type == TOKEN_RE) c = '/'; \
394 debug_printf((ctx), " Compare: %s (\"%s\" with %c%s%c) -> %c\n", \
395 (node)->token.s, \
396 (node)->left->token.value, \
397 c, (node)->right->token.value, c, \
398 (node)->value ? '1' : '0'); \
399 break; \
400 default: \
401 debug_printf((ctx), " Evaluate: %s -> %c\n", (node)->token.s, \
402 (node)->value ? '1' : '0'); \
403 break; \
404 } \
405} while(0)
406
407#define DEBUG_DUMP_UNMATCHED(ctx, unmatched) do { \
408 if (unmatched) { \
409 DEBUG_PRINTF(((ctx), " Unmatched %c\n", (char)(unmatched))); \
410 } \
411} while(0)
412
413#define DEBUG_DUMP_COND(ctx, text) \
414 DEBUG_PRINTF(((ctx), "**** %s cond status=\"%c\"\n", (text), \
415 ((ctx)->flags & SSI_FLAG_COND_TRUE) ? '1' : '0'))
416
417#define DEBUG_DUMP_TREE(ctx, root) debug_dump_tree(ctx, root)
418
419#else /* DEBUG_INCLUDE */
420
421#define TYPE_TOKEN(token, ttype) (token)->type = ttype
422
423#define CREATE_NODE(ctx, name) do { \
424 (name) = apr_palloc((ctx)->dpool, sizeof(*(name))); \
425 (name)->parent = (name)->left = (name)->right = NULL; \
426 (name)->done = 0; \
427} while(0)
428
429#define DEBUG_INIT(ctx, f, bb)
430#define DEBUG_PRINTF(arg)
431#define DEBUG_DUMP_TOKEN(ctx, token)
432#define DEBUG_DUMP_EVAL(ctx, node)
433#define DEBUG_DUMP_UNMATCHED(ctx, unmatched)
434#define DEBUG_DUMP_COND(ctx, text)
435#define DEBUG_DUMP_TREE(ctx, root)
436
437#endif /* !DEBUG_INCLUDE */
438
439
440/*
441 * +-------------------------------------------------------+
442 * | |
443 * | Static Module Data
444 * | |
445 * +-------------------------------------------------------+
446 */
447
448/* global module structure */
449module AP_MODULE_DECLARE_DATA include_module;
450
451/* function handlers for include directives */
453
454/* forward declaration of handler registry */
456
457/* Sentinel value to store in subprocess_env for items that
458 * shouldn't be evaluated until/unless they're actually used
459 */
460static const char lazy_eval_sentinel = '\0';
461#define LAZY_VALUE (&lazy_eval_sentinel)
462
463/* default values */
464#define DEFAULT_START_SEQUENCE "<!--#"
465#define DEFAULT_END_SEQUENCE "-->"
466#define DEFAULT_ERROR_MSG "[an error occurred while processing this directive]"
467#define DEFAULT_TIME_FORMAT "%A, %d-%b-%Y %H:%M:%S %Z"
468#define DEFAULT_UNDEFINED_ECHO "(none)"
469
470#define UNSET -1
471
472#ifdef XBITHACK
473#define DEFAULT_XBITHACK XBITHACK_FULL
474#else
475#define DEFAULT_XBITHACK XBITHACK_OFF
476#endif
477
478
479/*
480 * +-------------------------------------------------------+
481 * | |
482 * | Environment/Expansion Functions
483 * | |
484 * +-------------------------------------------------------+
485 */
486
487/*
488 * decodes a string containing html entities or numeric character references.
489 * 's' is overwritten with the decoded string.
490 * If 's' is syntatically incorrect, then the followed fixups will be made:
491 * unknown entities will be left undecoded;
492 * references to unused numeric characters will be deleted.
493 * In particular, &#00; will not be decoded, but will be deleted.
494 */
495
496/* maximum length of any ISO-LATIN-1 HTML entity name. */
497#define MAXENTLEN (6)
498
499/* The following is a shrinking transformation, therefore safe. */
500
501/* Note: this function is deprecated in favour of apr_unescape_entity() in APR */
502static void decodehtml(char *s)
503{
504 int val, i, j;
505 char *p;
506 const char *ents;
507 static const char * const entlist[MAXENTLEN + 1] =
508 {
509 NULL, /* 0 */
510 NULL, /* 1 */
511 "lt\074gt\076", /* 2 */
512 "amp\046ETH\320eth\360", /* 3 */
513 "quot\042Auml\304Euml\313Iuml\317Ouml\326Uuml\334auml\344euml"
514 "\353iuml\357ouml\366uuml\374yuml\377", /* 4 */
515
516 "Acirc\302Aring\305AElig\306Ecirc\312Icirc\316Ocirc\324Ucirc"
517 "\333THORN\336szlig\337acirc\342aring\345aelig\346ecirc\352"
518 "icirc\356ocirc\364ucirc\373thorn\376", /* 5 */
519
520 "Agrave\300Aacute\301Atilde\303Ccedil\307Egrave\310Eacute\311"
521 "Igrave\314Iacute\315Ntilde\321Ograve\322Oacute\323Otilde"
522 "\325Oslash\330Ugrave\331Uacute\332Yacute\335agrave\340"
523 "aacute\341atilde\343ccedil\347egrave\350eacute\351igrave"
524 "\354iacute\355ntilde\361ograve\362oacute\363otilde\365"
525 "oslash\370ugrave\371uacute\372yacute\375" /* 6 */
526 };
527
528 /* Do a fast scan through the string until we find anything
529 * that needs more complicated handling
530 */
531 for (; *s != '&'; s++) {
532 if (*s == '\0') {
533 return;
534 }
535 }
536
537 for (p = s; *s != '\0'; s++, p++) {
538 if (*s != '&') {
539 *p = *s;
540 continue;
541 }
542 /* find end of entity */
543 for (i = 1; s[i] != ';' && s[i] != '\0'; i++) {
544 continue;
545 }
546
547 if (s[i] == '\0') { /* treat as normal data */
548 *p = *s;
549 continue;
550 }
551
552 /* is it numeric ? */
553 if (s[1] == '#') {
554 for (j = 2, val = 0; j < i && apr_isdigit(s[j]); j++) {
555 val = val * 10 + s[j] - '0';
556 }
557 s += i;
558 if (j < i || val <= 8 || (val >= 11 && val <= 31) ||
559 (val >= 127 && val <= 160) || val >= 256) {
560 p--; /* no data to output */
561 }
562 else {
563 *p = RAW_ASCII_CHAR(val);
564 }
565 }
566 else {
567 j = i - 1;
568 if (j > MAXENTLEN || entlist[j] == NULL) {
569 /* wrong length */
570 *p = '&';
571 continue; /* skip it */
572 }
573 for (ents = entlist[j]; *ents != '\0'; ents += i) {
574 if (strncmp(s + 1, ents, j) == 0) {
575 break;
576 }
577 }
578
579 if (*ents == '\0') {
580 *p = '&'; /* unknown */
581 }
582 else {
583 *p = RAW_ASCII_CHAR(((const unsigned char *) ents)[j]);
584 s += i;
585 }
586 }
587 }
588
589 *p = '\0';
590}
591
593{
595 char *t;
596
597 apr_table_setn(e, "DATE_LOCAL", LAZY_VALUE);
598 apr_table_setn(e, "DATE_GMT", LAZY_VALUE);
599 apr_table_setn(e, "LAST_MODIFIED", LAZY_VALUE);
600 apr_table_setn(e, "DOCUMENT_URI", r->uri);
601 apr_table_setn(e, "DOCUMENT_ARGS", r->args ? r->args : "");
602 if (r->path_info && *r->path_info) {
603 apr_table_setn(e, "DOCUMENT_PATH_INFO", r->path_info);
604 }
605 apr_table_setn(e, "USER_NAME", LAZY_VALUE);
606 if (r->filename && (t = strrchr(r->filename, '/'))) {
607 apr_table_setn(e, "DOCUMENT_NAME", ++t);
608 }
609 else {
610 apr_table_setn(e, "DOCUMENT_NAME", r->uri);
611 }
612 if (r->args) {
613 char *arg_copy = apr_pstrdup(r->pool, r->args);
614
616 apr_table_setn(e, "QUERY_STRING_UNESCAPED",
618 }
619}
620
621static const char *add_include_vars_lazy(request_rec *r, const char *var, const char *timefmt)
622{
623 char *val;
624 if (!strcasecmp(var, "DATE_LOCAL")) {
626 }
627 else if (!strcasecmp(var, "DATE_GMT")) {
629 }
630 else if (!strcasecmp(var, "LAST_MODIFIED")) {
632 }
633 else if (!strcasecmp(var, "USER_NAME")) {
635 val = "<unknown>";
636 }
637 }
638 else {
639 val = NULL;
640 }
641
642 if (val) {
644 }
645 return val;
646}
647
648static const char *get_include_var(const char *var, include_ctx_t *ctx)
649{
650 const char *val;
651 request_rec *r = ctx->r;
652
653 if (apr_isdigit(*var) && !var[1]) {
654 apr_size_t idx = *var - '0';
655 backref_t *re = ctx->intern->re;
656
657 /* Handle $0 .. $9 from the last regex evaluated.
658 * The choice of returning NULL strings on not-found,
659 * v.s. empty strings on an empty match is deliberate.
660 */
661 if (!re || !re->have_match) {
663 "regex capture $%" APR_SIZE_T_FMT " refers to no regex in %s",
664 idx, r->filename);
665 return NULL;
666 }
667 else if (re->nsub < idx || idx >= AP_MAX_REG_MATCH) {
669 "regex capture $%" APR_SIZE_T_FMT
670 " is out of range (last regex was: '%s') in %s",
671 idx, re->rexp, r->filename);
672 return NULL;
673 }
674 else if (re->match[idx].rm_so < 0 || re->match[idx].rm_eo < 0) {
675 /* This particular subpattern was not used by the regex */
676 return NULL;
677 }
678 else {
679 val = apr_pstrmemdup(ctx->dpool, re->source + re->match[idx].rm_so,
680 re->match[idx].rm_eo - re->match[idx].rm_so);
681 }
682 }
683 else {
685
686 if (val == LAZY_VALUE) {
687 val = add_include_vars_lazy(r, var, ctx->time_str);
688 }
689 }
690
691 return val;
692}
693
695 const void *data,
696 const char *arg)
697{
698 const char *res, *name = data;
699 include_ctx_t *ctx = eval_ctx->data;
700 if ((name[0] == 'e') || (name[0] == 'E')) {
701 /* keep legacy "env" semantics */
702 if ((res = apr_table_get(ctx->r->notes, arg)) != NULL)
703 return res;
704 else if ((res = get_include_var(arg, ctx)) != NULL)
705 return res;
706 else
707 return getenv(arg);
708 }
709 else {
710 return get_include_var(arg, ctx);
711 }
712}
713
715{
716 switch (parms->type) {
718 if (strcasecmp(parms->name, "v") == 0 ||
719 strcasecmp(parms->name, "reqenv") == 0 ||
720 strcasecmp(parms->name, "env") == 0) {
721 *parms->func = include_expr_var_fn;
722 *parms->data = parms->name;
723 return OK;
724 }
725 break;
726 /*
727 * We could also make the SSI vars available as %{...} style variables
728 * (AP_EXPR_FUNC_VAR), but this would create problems if we ever want
729 * to cache parsed expressions for performance reasons.
730 */
731 }
733}
734
735
736/*
737 * Do variable substitution on strings
738 *
739 * (Note: If out==NULL, this function allocs a buffer for the resulting
740 * string from ctx->pool. The return value is always the parsed string)
741 */
742static char *ap_ssi_parse_string(include_ctx_t *ctx, const char *in, char *out,
744{
745 request_rec *r = ctx->r;
746 result_item_t *result = NULL, *current = NULL;
748 char *ret = NULL, *eout = NULL;
749 const char *p;
750
751 if (out) {
752 /* sanity check, out && !length is not supported */
753 ap_assert(out && length);
754
755 ret = out;
756 eout = out + length - 1;
757 }
758
759 span = strcspn(in, "\\$");
760 inlen = strlen(in);
761
762 /* fast exit */
763 if (inlen == span) {
764 if (out) {
766 }
767 else {
768 ret = apr_pstrmemdup(ctx->pool, in, (length && length <= inlen)
769 ? length - 1 : inlen);
770 }
771
772 return ret;
773 }
774
775 /* well, actually something to do */
776 p = in + span;
777
778 if (out) {
779 if (span) {
780 memcpy(out, in, (out+span <= eout) ? span : (eout-out));
781 out += span;
782 }
783 }
784 else {
785 current = result = apr_palloc(ctx->dpool, sizeof(*result));
786 current->next = NULL;
787 current->string = in;
788 current->len = span;
789 outlen = span;
790 }
791
792 /* loop for specials */
793 do {
794 if ((out && out >= eout) || (length && outlen >= length)) {
795 break;
796 }
797
798 /* prepare next entry */
799 if (!out && current->len) {
800 current->next = apr_palloc(ctx->dpool, sizeof(*current->next));
801 current = current->next;
802 current->next = NULL;
803 current->len = 0;
804 }
805
806 /*
807 * escaped character
808 */
809 if (*p == '\\') {
810 if (out) {
811 *out++ = (p[1] == '$') ? *++p : *p;
812 ++p;
813 }
814 else {
815 current->len = 1;
816 current->string = (p[1] == '$') ? ++p : p;
817 ++p;
818 ++outlen;
819 }
820 }
821
822 /*
823 * variable expansion
824 */
825 else { /* *p == '$' */
826 const char *newp = NULL, *ep, *key = NULL;
827
828 if (*++p == '{') {
829 ep = ap_strchr_c(++p, '}');
830 if (!ep) {
831 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(01331) "Missing '}' on "
832 "variable \"%s\" in %s", p, r->filename);
833 break;
834 }
835
836 if (p < ep) {
837 key = apr_pstrmemdup(ctx->dpool, p, ep - p);
838 newp = ep + 1;
839 }
840 p -= 2;
841 }
842 else {
843 ep = p;
844 while (*ep == '_' || apr_isalnum(*ep)) {
845 ++ep;
846 }
847
848 if (p < ep) {
849 key = apr_pstrmemdup(ctx->dpool, p, ep - p);
850 newp = ep;
851 }
852 --p;
853 }
854
855 /* empty name results in a copy of '$' in the output string */
856 if (!key) {
857 if (out) {
858 *out++ = *p++;
859 }
860 else {
861 current->len = 1;
862 current->string = p++;
863 ++outlen;
864 }
865 }
866 else {
867 const char *val = get_include_var(key, ctx);
868 apr_size_t len = 0;
869
870 if (val) {
871 len = strlen(val);
872 }
873 else if (leave_name) {
874 val = p;
875 len = ep - p;
876 }
877
878 if (val && len) {
879 if (out) {
880 memcpy(out, val, (out+len <= eout) ? len : (eout-out));
881 out += len;
882 }
883 else {
884 current->len = len;
885 current->string = val;
886 outlen += len;
887 }
888 }
889
890 p = newp;
891 }
892 }
893
894 if ((out && out >= eout) || (length && outlen >= length)) {
895 break;
896 }
897
898 /* check the remainder */
899 if (*p && (span = strcspn(p, "\\$")) > 0) {
900 if (!out && current->len) {
901 current->next = apr_palloc(ctx->dpool, sizeof(*current->next));
902 current = current->next;
903 current->next = NULL;
904 }
905
906 if (out) {
907 memcpy(out, p, (out+span <= eout) ? span : (eout-out));
908 out += span;
909 }
910 else {
911 current->len = span;
912 current->string = p;
913 outlen += span;
914 }
915
916 p += span;
917 }
918 } while (p < in+inlen);
919
920 /* assemble result */
921 if (out) {
922 if (out > eout) {
923 *eout = '\0';
924 }
925 else {
926 *out = '\0';
927 }
928 }
929 else {
930 const char *ep;
931
932 if (length && outlen > length) {
933 outlen = length - 1;
934 }
935
936 ret = out = apr_palloc(ctx->pool, outlen + 1);
937 ep = ret + outlen;
938
939 do {
940 if (result->len) {
941 memcpy(out, result->string, (out+result->len <= ep)
942 ? result->len : (ep-out));
943 out += result->len;
944 }
945 result = result->next;
946 } while (result && out < ep);
947
948 ret[outlen] = '\0';
949 }
950
951 return ret;
952}
953
954
955/*
956 * +-------------------------------------------------------+
957 * | |
958 * | Conditional Expression Parser
959 * | |
960 * +-------------------------------------------------------+
961 */
962
963static APR_INLINE int re_check(include_ctx_t *ctx, const char *string,
964 const char *rexp)
965{
967 backref_t *re = ctx->intern->re;
968
969 compiled = ap_pregcomp(ctx->dpool, rexp, AP_REG_EXTENDED);
970 if (!compiled) {
972 "unable to compile pattern \"%s\"", rexp);
973 return -1;
974 }
975
976 if (!re) {
977 re = ctx->intern->re = apr_palloc(ctx->pool, sizeof(*re));
978 }
979
980 re->source = apr_pstrdup(ctx->pool, string);
981 re->rexp = apr_pstrdup(ctx->pool, rexp);
982 re->nsub = compiled->re_nsub;
984 re->match, 0);
985
986 ap_pregfree(ctx->dpool, compiled);
987 return re->have_match;
988}
989
990static int get_ptoken(include_ctx_t *ctx, const char **parse, token_t *token, token_t *previous)
991{
992 const char *p;
994 int unmatched;
995
996 token->value = NULL;
997
998 if (!*parse) {
999 return 0;
1000 }
1001
1002 /* Skip leading white space */
1003 while (apr_isspace(**parse)) {
1004 ++*parse;
1005 }
1006
1007 if (!**parse) {
1008 *parse = NULL;
1009 return 0;
1010 }
1011
1012 TYPE_TOKEN(token, TOKEN_STRING); /* the default type */
1013 p = *parse;
1014 unmatched = 0;
1015
1016 switch (*(*parse)++) {
1017 case '(':
1018 TYPE_TOKEN(token, TOKEN_LBRACE);
1019 return 0;
1020 case ')':
1021 TYPE_TOKEN(token, TOKEN_RBRACE);
1022 return 0;
1023 case '=':
1024 if (**parse == '=') ++*parse;
1025 TYPE_TOKEN(token, TOKEN_EQ);
1026 return 0;
1027 case '!':
1028 if (**parse == '=') {
1029 TYPE_TOKEN(token, TOKEN_NE);
1030 ++*parse;
1031 return 0;
1032 }
1033 TYPE_TOKEN(token, TOKEN_NOT);
1034 return 0;
1035 case '\'':
1036 unmatched = '\'';
1037 break;
1038 case '/':
1039 /* if last token was ACCESS, this token is STRING */
1040 if (previous != NULL && TOKEN_ACCESS == previous->type) {
1041 break;
1042 }
1043 TYPE_TOKEN(token, TOKEN_RE);
1044 unmatched = '/';
1045 break;
1046 case '|':
1047 if (**parse == '|') {
1048 TYPE_TOKEN(token, TOKEN_OR);
1049 ++*parse;
1050 return 0;
1051 }
1052 break;
1053 case '&':
1054 if (**parse == '&') {
1055 TYPE_TOKEN(token, TOKEN_AND);
1056 ++*parse;
1057 return 0;
1058 }
1059 break;
1060 case '>':
1061 if (**parse == '=') {
1062 TYPE_TOKEN(token, TOKEN_GE);
1063 ++*parse;
1064 return 0;
1065 }
1066 TYPE_TOKEN(token, TOKEN_GT);
1067 return 0;
1068 case '<':
1069 if (**parse == '=') {
1070 TYPE_TOKEN(token, TOKEN_LE);
1071 ++*parse;
1072 return 0;
1073 }
1074 TYPE_TOKEN(token, TOKEN_LT);
1075 return 0;
1076 case '-':
1077 if (**parse == 'A') {
1078 TYPE_TOKEN(token, TOKEN_ACCESS);
1079 ++*parse;
1080 return 0;
1081 }
1082 break;
1083 }
1084
1085 /* It's a string or regex token
1086 * Now search for the next token, which finishes this string
1087 */
1088 shift = 0;
1089 p = *parse = token->value = unmatched ? *parse : p;
1090
1091 for (; **parse; p = ++*parse) {
1092 if (**parse == '\\') {
1093 if (!*(++*parse)) {
1094 p = *parse;
1095 break;
1096 }
1097
1098 ++shift;
1099 }
1100 else {
1101 if (unmatched) {
1102 if (**parse == unmatched) {
1103 unmatched = 0;
1104 ++*parse;
1105 break;
1106 }
1107 } else if (apr_isspace(**parse)) {
1108 break;
1109 }
1110 else {
1111 int found = 0;
1112
1113 switch (**parse) {
1114 case '(':
1115 case ')':
1116 case '=':
1117 case '!':
1118 case '<':
1119 case '>':
1120 ++found;
1121 break;
1122
1123 case '|':
1124 case '&':
1125 if ((*parse)[1] == **parse) {
1126 ++found;
1127 }
1128 break;
1129 }
1130
1131 if (found) {
1132 break;
1133 }
1134 }
1135 }
1136 }
1137
1138 if (unmatched) {
1139 token->value = apr_pstrdup(ctx->dpool, "");
1140 }
1141 else {
1142 apr_size_t len = p - token->value - shift;
1143 char *c = apr_palloc(ctx->dpool, len + 1);
1144
1145 p = token->value;
1146 token->value = c;
1147
1148 while (shift--) {
1149 const char *e = ap_strchr_c(p, '\\');
1150
1151 memcpy(c, p, e-p);
1152 c += e-p;
1153 *c++ = *++e;
1154 len -= e-p;
1155 p = e+1;
1156 }
1157
1158 if (len) {
1159 memcpy(c, p, len);
1160 }
1161 c[len] = '\0';
1162 }
1163
1164 return unmatched;
1165}
1166
1167static int parse_expr(include_ctx_t *ctx, const char *expr, int *was_error)
1168{
1169 parse_node_t *new, *root = NULL, *current = NULL;
1170 request_rec *r = ctx->r;
1171 request_rec *rr = NULL;
1172 const char *error = APLOGNO(03188) "Invalid expression \"%s\" in file %s";
1173 const char *parse = expr;
1174 unsigned regex = 0;
1175
1176 *was_error = 0;
1177
1178 if (!parse) {
1179 return 0;
1180 }
1181
1182 /* Create Parse Tree */
1183 while (1) {
1184 /* uncomment this to see how the tree a built:
1185 *
1186 * DEBUG_DUMP_TREE(ctx, root);
1187 */
1188 CREATE_NODE(ctx, new);
1189
1190 {
1191#ifdef DEBUG_INCLUDE
1192 int was_unmatched =
1193#endif
1194 get_ptoken(ctx, &parse, &new->token,
1195 (current != NULL ? &current->token : NULL));
1196 if (!parse)
1197 break;
1198
1200 DEBUG_DUMP_TOKEN(ctx, &new->token);
1201 }
1202
1203 if (!current) {
1204 switch (new->token.type) {
1205 case TOKEN_STRING:
1206 case TOKEN_NOT:
1207 case TOKEN_ACCESS:
1208 case TOKEN_LBRACE:
1209 root = current = new;
1210 continue;
1211
1212 default:
1213 /* Intentional no APLOGNO */
1214 /* error text provides APLOGNO */
1216 r->filename);
1217 *was_error = 1;
1218 return 0;
1219 }
1220 }
1221
1222 switch (new->token.type) {
1223 case TOKEN_STRING:
1224 switch (current->token.type) {
1225 case TOKEN_STRING:
1226 current->token.value =
1227 apr_pstrcat(ctx->dpool, current->token.value,
1228 *current->token.value ? " " : "",
1229 new->token.value, NULL);
1230 continue;
1231
1232 case TOKEN_RE:
1233 case TOKEN_RBRACE:
1234 case TOKEN_GROUP:
1235 break;
1236
1237 default:
1238 new->parent = current;
1239 current = current->right = new;
1240 continue;
1241 }
1242 break;
1243
1244 case TOKEN_RE:
1245 switch (current->token.type) {
1246 case TOKEN_EQ:
1247 case TOKEN_NE:
1248 new->parent = current;
1249 current = current->right = new;
1250 ++regex;
1251 continue;
1252
1253 default:
1254 break;
1255 }
1256 break;
1257
1258 case TOKEN_AND:
1259 case TOKEN_OR:
1260 switch (current->token.type) {
1261 case TOKEN_STRING:
1262 case TOKEN_RE:
1263 case TOKEN_GROUP:
1264 current = current->parent;
1265
1266 while (current) {
1267 switch (current->token.type) {
1268 case TOKEN_AND:
1269 case TOKEN_OR:
1270 case TOKEN_LBRACE:
1271 break;
1272
1273 default:
1274 current = current->parent;
1275 continue;
1276 }
1277 break;
1278 }
1279
1280 if (!current) {
1281 new->left = root;
1282 root->parent = new;
1283 current = root = new;
1284 continue;
1285 }
1286
1287 new->left = current->right;
1288 new->left->parent = new;
1289 new->parent = current;
1290 current = current->right = new;
1291 continue;
1292
1293 default:
1294 break;
1295 }
1296 break;
1297
1298 case TOKEN_EQ:
1299 case TOKEN_NE:
1300 case TOKEN_GE:
1301 case TOKEN_GT:
1302 case TOKEN_LE:
1303 case TOKEN_LT:
1304 if (current->token.type == TOKEN_STRING) {
1305 current = current->parent;
1306
1307 if (!current) {
1308 new->left = root;
1309 root->parent = new;
1310 current = root = new;
1311 continue;
1312 }
1313
1314 switch (current->token.type) {
1315 case TOKEN_LBRACE:
1316 case TOKEN_AND:
1317 case TOKEN_OR:
1318 new->left = current->right;
1319 new->left->parent = new;
1320 new->parent = current;
1321 current = current->right = new;
1322 continue;
1323
1324 default:
1325 break;
1326 }
1327 }
1328 break;
1329
1330 case TOKEN_RBRACE:
1331 while (current && current->token.type != TOKEN_LBRACE) {
1332 current = current->parent;
1333 }
1334
1335 if (current) {
1336 TYPE_TOKEN(&current->token, TOKEN_GROUP);
1337 continue;
1338 }
1339
1340 error = APLOGNO(03189) "Unmatched ')' in \"%s\" in file %s";
1341 break;
1342
1343 case TOKEN_NOT:
1344 case TOKEN_ACCESS:
1345 case TOKEN_LBRACE:
1346 switch (current->token.type) {
1347 case TOKEN_STRING:
1348 case TOKEN_RE:
1349 case TOKEN_RBRACE:
1350 case TOKEN_GROUP:
1351 break;
1352
1353 default:
1354 current->right = new;
1355 new->parent = current;
1356 current = new;
1357 continue;
1358 }
1359 break;
1360
1361 default:
1362 break;
1363 }
1364
1365 /* Intentional no APLOGNO */
1366 /* error text provides APLOGNO */
1368 *was_error = 1;
1369 return 0;
1370 }
1371
1372 DEBUG_DUMP_TREE(ctx, root);
1373
1374 /* Evaluate Parse Tree */
1375 current = root;
1376 error = NULL;
1377 while (current) {
1378 switch (current->token.type) {
1379 case TOKEN_STRING:
1380 current->token.value =
1381 ap_ssi_parse_string(ctx, current->token.value, NULL, 0,
1383 current->value = !!*current->token.value;
1384 break;
1385
1386 case TOKEN_AND:
1387 case TOKEN_OR:
1388 if (!current->left || !current->right) {
1390 "Invalid expression \"%s\" in file %s",
1391 expr, r->filename);
1392 *was_error = 1;
1393 return 0;
1394 }
1395
1396 if (!current->left->done) {
1397 switch (current->left->token.type) {
1398 case TOKEN_STRING:
1399 current->left->token.value =
1400 ap_ssi_parse_string(ctx, current->left->token.value,
1402 current->left->value = !!*current->left->token.value;
1403 DEBUG_DUMP_EVAL(ctx, current->left);
1404 current->left->done = 1;
1405 break;
1406
1407 default:
1408 current = current->left;
1409 continue;
1410 }
1411 }
1412
1413 /* short circuit evaluation */
1414 if (!current->right->done && !regex &&
1415 ((current->token.type == TOKEN_AND && !current->left->value) ||
1416 (current->token.type == TOKEN_OR && current->left->value))) {
1417 current->value = current->left->value;
1418 }
1419 else {
1420 if (!current->right->done) {
1421 switch (current->right->token.type) {
1422 case TOKEN_STRING:
1423 current->right->token.value =
1424 ap_ssi_parse_string(ctx,current->right->token.value,
1426 current->right->value = !!*current->right->token.value;
1427 DEBUG_DUMP_EVAL(ctx, current->right);
1428 current->right->done = 1;
1429 break;
1430
1431 default:
1432 current = current->right;
1433 continue;
1434 }
1435 }
1436
1437 if (current->token.type == TOKEN_AND) {
1438 current->value = current->left->value &&
1439 current->right->value;
1440 }
1441 else {
1442 current->value = current->left->value ||
1443 current->right->value;
1444 }
1445 }
1446 break;
1447
1448 case TOKEN_EQ:
1449 case TOKEN_NE:
1450 if (!current->left || !current->right ||
1451 current->left->token.type != TOKEN_STRING ||
1452 (current->right->token.type != TOKEN_STRING &&
1453 current->right->token.type != TOKEN_RE)) {
1455 "Invalid expression \"%s\" in file %s",
1456 expr, r->filename);
1457 *was_error = 1;
1458 return 0;
1459 }
1460 current->left->token.value =
1461 ap_ssi_parse_string(ctx, current->left->token.value, NULL, 0,
1463 current->right->token.value =
1464 ap_ssi_parse_string(ctx, current->right->token.value, NULL, 0,
1466
1467 if (current->right->token.type == TOKEN_RE) {
1468 current->value = re_check(ctx, current->left->token.value,
1469 current->right->token.value);
1470 --regex;
1471 }
1472 else {
1473 current->value = !strcmp(current->left->token.value,
1474 current->right->token.value);
1475 }
1476
1477 if (current->token.type == TOKEN_NE) {
1478 current->value = !current->value;
1479 }
1480 break;
1481
1482 case TOKEN_GE:
1483 case TOKEN_GT:
1484 case TOKEN_LE:
1485 case TOKEN_LT:
1486 if (!current->left || !current->right ||
1487 current->left->token.type != TOKEN_STRING ||
1488 current->right->token.type != TOKEN_STRING) {
1490 "Invalid expression \"%s\" in file %s",
1491 expr, r->filename);
1492 *was_error = 1;
1493 return 0;
1494 }
1495
1496 current->left->token.value =
1497 ap_ssi_parse_string(ctx, current->left->token.value, NULL, 0,
1499 current->right->token.value =
1500 ap_ssi_parse_string(ctx, current->right->token.value, NULL, 0,
1502
1503 current->value = strcmp(current->left->token.value,
1504 current->right->token.value);
1505
1506 switch (current->token.type) {
1507 case TOKEN_GE: current->value = current->value >= 0; break;
1508 case TOKEN_GT: current->value = current->value > 0; break;
1509 case TOKEN_LE: current->value = current->value <= 0; break;
1510 case TOKEN_LT: current->value = current->value < 0; break;
1511 default: current->value = 0; break; /* should not happen */
1512 }
1513 break;
1514
1515 case TOKEN_NOT:
1516 case TOKEN_GROUP:
1517 if (current->right) {
1518 if (!current->right->done) {
1519 current = current->right;
1520 continue;
1521 }
1522 current->value = current->right->value;
1523 }
1524 else {
1525 current->value = 1;
1526 }
1527
1528 if (current->token.type == TOKEN_NOT) {
1529 current->value = !current->value;
1530 }
1531 break;
1532
1533 case TOKEN_ACCESS:
1534 if (current->left || !current->right ||
1535 (current->right->token.type != TOKEN_STRING &&
1536 current->right->token.type != TOKEN_RE)) {
1538 "Invalid expression \"%s\" in file %s: Token '-A' must be followed by a URI string.",
1539 expr, r->filename);
1540 *was_error = 1;
1541 return 0;
1542 }
1543 current->right->token.value =
1544 ap_ssi_parse_string(ctx, current->right->token.value, NULL, 0,
1546 rr = ap_sub_req_lookup_uri(current->right->token.value, r, NULL);
1547 /* 400 and higher are considered access denied */
1548 if (rr->status < HTTP_BAD_REQUEST) {
1549 current->value = 1;
1550 }
1551 else {
1552 current->value = 0;
1553 ap_log_rerror(APLOG_MARK, APLOG_DEBUG, rr->status, r, APLOGNO(01336)
1554 "mod_include: The tested "
1555 "subrequest -A \"%s\" returned an error code.",
1556 current->right->token.value);
1557 }
1559 break;
1560
1561 case TOKEN_RE:
1562 if (!error) {
1563 error = APLOGNO(03190) "No operator before regex in expr \"%s\" in file %s";
1564 }
1565 case TOKEN_LBRACE:
1566 if (!error) {
1567 error = APLOGNO(03191) "Unmatched '(' in \"%s\" in file %s";
1568 }
1569 default:
1570 if (!error) {
1571 error = APLOGNO(03192) "internal parser error in \"%s\" in file %s";
1572 }
1573
1574 /* Intentional no APLOGNO */
1575 /* error text provides APLOGNO */
1577 *was_error = 1;
1578 return 0;
1579 }
1580
1581 DEBUG_DUMP_EVAL(ctx, current);
1582 current->done = 1;
1583 current = current->parent;
1584 }
1585
1586 return (root ? root->value : 0);
1587}
1588
1589/* same as above, but use common ap_expr syntax / API */
1590static int parse_ap_expr(include_ctx_t *ctx, const char *expr, int *was_error)
1591{
1593 const char *err;
1594 int ret;
1595 backref_t *re = ctx->intern->re;
1596 ap_expr_eval_ctx_t *eval_ctx = ctx->intern->expr_eval_ctx;
1597
1598 expr_info->filename = ctx->r->filename;
1599 expr_info->line_number = 0;
1600 expr_info->module_index = APLOG_MODULE_INDEX;
1602 err = ap_expr_parse(ctx->r->pool, ctx->r->pool, expr_info, expr,
1604 if (err) {
1606 "Could not parse expr \"%s\" in %s: %s", expr,
1607 ctx->r->filename, err);
1608 *was_error = 1;
1609 return 0;
1610 }
1611
1612 if (!re) {
1613 ctx->intern->re = re = apr_pcalloc(ctx->pool, sizeof(*re));
1614 }
1615 else {
1616 /* ap_expr_exec_ctx() does not care about re->have_match but only about
1617 * re->source
1618 */
1619 if (!re->have_match)
1620 re->source = NULL;
1621 }
1622
1623 if (!eval_ctx) {
1624 eval_ctx = apr_pcalloc(ctx->pool, sizeof(*eval_ctx));
1625 ctx->intern->expr_eval_ctx = eval_ctx;
1626 eval_ctx->r = ctx->r;
1627 eval_ctx->c = ctx->r->connection;
1628 eval_ctx->s = ctx->r->server;
1629 eval_ctx->p = ctx->r->pool;
1630 eval_ctx->data = ctx;
1631 eval_ctx->err = &ctx->intern->expr_err;
1632 eval_ctx->vary_this = &ctx->intern->expr_vary_this;
1633 eval_ctx->re_nmatch = AP_MAX_REG_MATCH;
1634 eval_ctx->re_pmatch = re->match;
1635 eval_ctx->re_source = &re->source;
1636 }
1637
1638 eval_ctx->info = expr_info;
1640 if (ret < 0) {
1642 "Could not evaluate expr \"%s\" in %s: %s", expr,
1643 ctx->r->filename, ctx->intern->expr_err);
1644 *was_error = 1;
1645 return 0;
1646 }
1647 *was_error = 0;
1648 if (re->source)
1649 re->have_match = 1;
1650 return ret;
1651}
1652
1653/*
1654 * +-------------------------------------------------------+
1655 * | |
1656 * | Action Handlers
1657 * | |
1658 * +-------------------------------------------------------+
1659 */
1660
1661/*
1662 * Extract the next tag name and value.
1663 * If there are no more tags, set the tag name to NULL.
1664 * The tag value is html decoded if dodecode is non-zero.
1665 * The tag value may be NULL if there is no tag value..
1666 */
1668 char **tag_val, int dodecode)
1669{
1670 if (!ctx->intern->argv) {
1671 *tag = NULL;
1672 *tag_val = NULL;
1673
1674 return;
1675 }
1676
1677 *tag_val = ctx->intern->argv->value;
1678 *tag = ctx->intern->argv->name;
1679
1680 ctx->intern->argv = ctx->intern->argv->next;
1681
1682 if (dodecode && *tag_val) {
1684 }
1685}
1686
1687static int find_file(request_rec *r, const char *directive, const char *tag,
1688 char *tag_val, apr_finfo_t *finfo)
1689{
1690 char *to_send = tag_val;
1691 request_rec *rr = NULL;
1692 int ret=0;
1693 char *error_fmt = NULL;
1695
1696 if (!strcmp(tag, "file")) {
1697 char *newpath;
1698
1699 /* be safe; only files in this directory or below allowed */
1703
1704 if (rv != APR_SUCCESS) {
1705 error_fmt = APLOGNO(02668) "unable to access file \"%s\" "
1706 "in parsed file %s";
1707 }
1708 else {
1709 /* note: it is okay to pass NULL for the "next filter" since
1710 we never attempt to "run" this sub request. */
1712
1713 if (rr->status == HTTP_OK && rr->finfo.filetype != APR_NOFILE) {
1714 to_send = rr->filename;
1715 if ((rv = apr_stat(finfo, to_send,
1717 && rv != APR_INCOMPLETE) {
1718 error_fmt = APLOGNO(02669) "unable to get information "
1719 "about \"%s\" in parsed file %s";
1720 }
1721 }
1722 else {
1723 error_fmt = APLOGNO(02670) "unable to lookup information "
1724 "about \"%s\" in parsed file %s";
1725 }
1726 }
1727
1728 if (error_fmt) {
1729 ret = -1;
1730 /* Intentional no APLOGNO */
1731 /* error_fmt provides APLOGNO */
1733 rv, r, error_fmt, to_send, r->filename);
1734 }
1735
1736 if (rr) ap_destroy_sub_req(rr);
1737
1738 return ret;
1739 }
1740 else if (!strcmp(tag, "virtual")) {
1741 /* note: it is okay to pass NULL for the "next filter" since
1742 we never attempt to "run" this sub request. */
1744
1745 if (rr->status == HTTP_OK && rr->finfo.filetype != APR_NOFILE) {
1746 memcpy((char *) finfo, (const char *) &rr->finfo,
1747 sizeof(rr->finfo));
1749 return 0;
1750 }
1751 else {
1752 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(01339) "unable to get "
1753 "information about \"%s\" in parsed file %s",
1754 tag_val, r->filename);
1756 return -1;
1757 }
1758 }
1759 else {
1760 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(01340) "unknown parameter \"%s\" "
1761 "to tag %s in %s", tag, directive, r->filename);
1762 return -1;
1763 }
1764}
1765
1766/*
1767 * <!--#comment blah blah blah ... -->
1768 */
1774
1775/*
1776 * <!--#include virtual|file="..." [onerror|virtual|file="..."] ... -->
1777 *
1778 * Output each file/virtual in turn until one of them returns an error.
1779 * On error, ignore all further file/virtual attributes until we reach
1780 * an onerror attribute, where we make an attempt to serve the onerror
1781 * virtual url. If onerror fails, or no onerror is present, the default
1782 * error string is inserted into the stream.
1783 */
1786{
1787 request_rec *r = f->r;
1788 char *last_error;
1789
1790 if (!ctx->argc) {
1792 (ctx->flags & SSI_FLAG_PRINTING)
1794 0, r, APLOGNO(01341)
1795 "missing argument for include element in %s",
1796 r->filename);
1797 }
1798
1799 if (!(ctx->flags & SSI_FLAG_PRINTING)) {
1800 return APR_SUCCESS;
1801 }
1802
1803 if (!ctx->argc) {
1805 return APR_SUCCESS;
1806 }
1807
1808 last_error = NULL;
1809 while (1) {
1810 char *tag = NULL;
1811 char *tag_val = NULL;
1812 request_rec *rr = NULL;
1813 char *error_fmt = NULL;
1814 char *parsed_string;
1816 int status = 0;
1817
1819 if (!tag || !tag_val) {
1820 break;
1821 }
1822
1823 if (strcmp(tag, "virtual") && strcmp(tag, "file") && strcmp(tag,
1824 "onerror")) {
1825 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(01342) "unknown parameter "
1826 "\"%s\" to tag include in %s", tag, r->filename);
1828 break;
1829 }
1830
1833 if (tag[0] == 'f') {
1834 char *newpath;
1835
1836 /* be safe; only files in this directory or below allowed */
1840
1841 if (rv != APR_SUCCESS) {
1842 error_fmt = "unable to include file \"%s\" in parsed file %s";
1843 }
1844 else {
1845 rr = ap_sub_req_lookup_file(newpath, r, f->next);
1846 }
1847 }
1848 else if ((tag[0] == 'v' && !last_error)
1849 || (tag[0] == 'o' && last_error)) {
1850 if (r->kept_body) {
1852 }
1853 else {
1855 }
1856 }
1857 else {
1858 continue;
1859 }
1860
1861 if (!error_fmt && rr->status != HTTP_OK) {
1862 error_fmt = "unable to include \"%s\" in parsed file %s, subrequest setup returned %d";
1863 }
1864
1865 if (!error_fmt && (ctx->flags & SSI_FLAG_NO_EXEC) &&
1866 rr->content_type && strncmp(rr->content_type, "text/", 5)) {
1867
1868 error_fmt = "unable to include potential exec \"%s\" in parsed "
1869 "file %s, content type not text/*";
1870 }
1871
1872 /* See the Kludge in includes_filter for why.
1873 * Basically, it puts a bread crumb in here, then looks
1874 * for the crumb later to see if its been here.
1875 */
1876 if (rr) {
1877 ap_set_module_config(rr->request_config, &include_module, r);
1878 }
1879
1880 if (!error_fmt && ((status = ap_run_sub_req(rr)))) {
1881 error_fmt = "unable to include \"%s\" in parsed file %s, subrequest returned %d";
1882 }
1883
1884 if (error_fmt) {
1885 /* Intentional no APLOGNO */
1886 /* error text is also sent to client */
1888 r->filename, status ? status : rr ? rr->status : 0);
1889 if (last_error) {
1890 /* onerror threw an error, give up completely */
1891 break;
1892 }
1893 last_error = error_fmt;
1894 }
1895 else {
1896 last_error = NULL;
1897 }
1898
1899 /* Do *not* destroy the subrequest here; it may have allocated
1900 * variables in this r->subprocess_env in the subrequest's
1901 * r->pool, so that pool must survive as long as this request.
1902 * Yes, this is a memory leak. */
1903
1904 }
1905
1906 if (last_error) {
1908 }
1909
1910 return APR_SUCCESS;
1911}
1912
1913/*
1914 * <!--#echo [decoding="..."] [encoding="..."] var="..." [decoding="..."]
1915 * [encoding="..."] var="..." ... -->
1916 */
1919{
1920 const char *encoding = "entity", *decoding = "none";
1921 request_rec *r = f->r;
1922 int error = 0;
1923
1924 if (!ctx->argc) {
1926 (ctx->flags & SSI_FLAG_PRINTING)
1928 0, r, APLOGNO(01343)
1929 "missing argument for echo element in %s",
1930 r->filename);
1931 }
1932
1933 if (!(ctx->flags & SSI_FLAG_PRINTING)) {
1934 return APR_SUCCESS;
1935 }
1936
1937 if (!ctx->argc) {
1939 return APR_SUCCESS;
1940 }
1941
1942 while (1) {
1943 char *tag = NULL;
1944 char *tag_val = NULL;
1945
1947 if (!tag || !tag_val) {
1948 break;
1949 }
1950
1951 if (!strcmp(tag, "var")) {
1952 const char *val;
1953 const char *echo_text = NULL;
1955
1958 ctx);
1959
1960 if (val) {
1961 char *last = NULL;
1962 char *e, *d, *token;
1963
1964 echo_text = val;
1965
1966 d = apr_pstrdup(ctx->pool, decoding);
1967 token = apr_strtok(d, ", \t", &last);
1968
1969 while (token) {
1970 if (!ap_cstr_casecmp(token, "none")) {
1971 /* do nothing */
1972 }
1973 else if (!ap_cstr_casecmp(token, "url")) {
1974 char *buf = apr_pstrdup(ctx->pool, echo_text);
1976 echo_text = buf;
1977 }
1978 else if (!ap_cstr_casecmp(token, "urlencoded")) {
1979 char *buf = apr_pstrdup(ctx->pool, echo_text);
1981 echo_text = buf;
1982 }
1983 else if (!ap_cstr_casecmp(token, "entity")) {
1984 char *buf = apr_pstrdup(ctx->pool, echo_text);
1985 decodehtml(buf);
1986 echo_text = buf;
1987 }
1988 else if (!ap_cstr_casecmp(token, "base64")) {
1990 }
1991 else {
1992 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(01344) "unknown value "
1993 "\"%s\" to parameter \"decoding\" of tag echo in "
1994 "%s", token, r->filename);
1996 error = 1;
1997 break;
1998 }
1999 token = apr_strtok(NULL, ", \t", &last);
2000 }
2001
2002 e = apr_pstrdup(ctx->pool, encoding);
2003 token = apr_strtok(e, ", \t", &last);
2004
2005 while (token) {
2006 if (!ap_cstr_casecmp(token, "none")) {
2007 /* do nothing */
2008 }
2009 else if (!ap_cstr_casecmp(token, "url")) {
2011 }
2012 else if (!ap_cstr_casecmp(token, "urlencoded")) {
2014 }
2015 else if (!ap_cstr_casecmp(token, "entity")) {
2016 echo_text = ap_escape_html2(ctx->dpool, echo_text, 0);
2017 }
2018 else if (!ap_cstr_casecmp(token, "base64")) {
2019 char *buf;
2020 buf = ap_pbase64encode(ctx->dpool, (char *)echo_text);
2021 echo_text = buf;
2022 }
2023 else {
2024 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(01345) "unknown value "
2025 "\"%s\" to parameter \"encoding\" of tag echo in "
2026 "%s", token, r->filename);
2028 error = 1;
2029 break;
2030 }
2031 token = apr_strtok(NULL, ", \t", &last);
2032 }
2033
2034 e_len = strlen(echo_text);
2035 }
2036 else {
2037 echo_text = ctx->intern->undefined_echo;
2038 e_len = ctx->intern->undefined_echo_len;
2039 }
2040
2041 if (error) {
2042 break;
2043 }
2044
2046 apr_pmemdup(ctx->pool, echo_text, e_len),
2047 e_len, ctx->pool, f->c->bucket_alloc));
2048 }
2049 else if (!strcmp(tag, "decoding")) {
2050 decoding = tag_val;
2051 }
2052 else if (!strcmp(tag, "encoding")) {
2053 encoding = tag_val;
2054 }
2055 else {
2056 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(01346) "unknown parameter "
2057 "\"%s\" in tag echo of %s", tag, r->filename);
2059 break;
2060 }
2061 }
2062
2063 return APR_SUCCESS;
2064}
2065
2066/*
2067 * <!--#config [timefmt="..."] [sizefmt="..."] [errmsg="..."]
2068 * [echomsg="..."] -->
2069 */
2072{
2073 request_rec *r = f->r;
2075
2076 if (!ctx->argc) {
2078 (ctx->flags & SSI_FLAG_PRINTING)
2080 0, r, APLOGNO(01347)
2081 "missing argument for config element in %s",
2082 r->filename);
2083 }
2084
2085 if (!(ctx->flags & SSI_FLAG_PRINTING)) {
2086 return APR_SUCCESS;
2087 }
2088
2089 if (!ctx->argc) {
2091 return APR_SUCCESS;
2092 }
2093
2094 while (1) {
2095 char *tag = NULL;
2096 char *tag_val = NULL;
2097
2099 if (!tag || !tag_val) {
2100 break;
2101 }
2102
2103 if (!strcmp(tag, "errmsg")) {
2104 ctx->error_str = ap_ssi_parse_string(ctx, tag_val, NULL, 0,
2106 }
2107 else if (!strcmp(tag, "echomsg")) {
2108 ctx->intern->undefined_echo =
2110 ctx->intern->undefined_echo_len=strlen(ctx->intern->undefined_echo);
2111 }
2112 else if (!strcmp(tag, "timefmt")) {
2113 apr_time_t date = r->request_time;
2114
2115 ctx->time_str = ap_ssi_parse_string(ctx, tag_val, NULL, 0,
2117
2118 apr_table_setn(env, "DATE_LOCAL", ap_ht_time(r->pool, date,
2119 ctx->time_str, 0));
2120 apr_table_setn(env, "DATE_GMT", ap_ht_time(r->pool, date,
2121 ctx->time_str, 1));
2122 apr_table_setn(env, "LAST_MODIFIED",
2124 ctx->time_str, 0));
2125 }
2126 else if (!strcmp(tag, "sizefmt")) {
2127 char *parsed_string;
2128
2131 if (!strcmp(parsed_string, "bytes")) {
2132 ctx->flags |= SSI_FLAG_SIZE_IN_BYTES;
2133 }
2134 else if (!strcmp(parsed_string, "abbrev")) {
2135 ctx->flags &= SSI_FLAG_SIZE_ABBREV;
2136 }
2137 else {
2138 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(01348) "unknown value "
2139 "\"%s\" to parameter \"sizefmt\" of tag config "
2140 "in %s", parsed_string, r->filename);
2142 break;
2143 }
2144 }
2145 else {
2146 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(01349) "unknown parameter "
2147 "\"%s\" to tag config in %s", tag, r->filename);
2149 break;
2150 }
2151 }
2152
2153 return APR_SUCCESS;
2154}
2155
2156/*
2157 * <!--#fsize virtual|file="..." [virtual|file="..."] ... -->
2158 */
2161{
2162 request_rec *r = f->r;
2163
2164 if (!ctx->argc) {
2166 (ctx->flags & SSI_FLAG_PRINTING)
2168 0, r, APLOGNO(01350)
2169 "missing argument for fsize element in %s",
2170 r->filename);
2171 }
2172
2173 if (!(ctx->flags & SSI_FLAG_PRINTING)) {
2174 return APR_SUCCESS;
2175 }
2176
2177 if (!ctx->argc) {
2179 return APR_SUCCESS;
2180 }
2181
2182 while (1) {
2183 char *tag = NULL;
2184 char *tag_val = NULL;
2185 apr_finfo_t finfo;
2186 char *parsed_string;
2187
2189 if (!tag || !tag_val) {
2190 break;
2191 }
2192
2195
2196 if (!find_file(r, "fsize", tag, parsed_string, &finfo)) {
2197 char *buf;
2199
2200 if (!(ctx->flags & SSI_FLAG_SIZE_IN_BYTES)) {
2201 buf = apr_strfsize(finfo.size, apr_palloc(ctx->pool, 5));
2202 len = 4; /* omit the \0 terminator */
2203 }
2204 else {
2205 apr_size_t l, x, pos;
2206 char *tmp;
2207
2208 tmp = apr_psprintf(ctx->dpool, "%" APR_OFF_T_FMT, finfo.size);
2209 len = l = strlen(tmp);
2210
2211 for (x = 0; x < l; ++x) {
2212 if (x && !((l - x) % 3)) {
2213 ++len;
2214 }
2215 }
2216
2217 if (len == l) {
2218 buf = apr_pstrmemdup(ctx->pool, tmp, len);
2219 }
2220 else {
2221 buf = apr_palloc(ctx->pool, len);
2222
2223 for (pos = x = 0; x < l; ++x) {
2224 if (x && !((l - x) % 3)) {
2225 buf[pos++] = ',';
2226 }
2227 buf[pos++] = tmp[x];
2228 }
2229 }
2230 }
2231
2233 ctx->pool, f->c->bucket_alloc));
2234 }
2235 else {
2237 break;
2238 }
2239 }
2240
2241 return APR_SUCCESS;
2242}
2243
2244/*
2245 * <!--#flastmod virtual|file="..." [virtual|file="..."] ... -->
2246 */
2249{
2250 request_rec *r = f->r;
2251
2252 if (!ctx->argc) {
2254 (ctx->flags & SSI_FLAG_PRINTING)
2256 0, r, APLOGNO(01351)
2257 "missing argument for flastmod element in %s",
2258 r->filename);
2259 }
2260
2261 if (!(ctx->flags & SSI_FLAG_PRINTING)) {
2262 return APR_SUCCESS;
2263 }
2264
2265 if (!ctx->argc) {
2267 return APR_SUCCESS;
2268 }
2269
2270 while (1) {
2271 char *tag = NULL;
2272 char *tag_val = NULL;
2273 apr_finfo_t finfo;
2274 char *parsed_string;
2275
2277 if (!tag || !tag_val) {
2278 break;
2279 }
2280
2283
2284 if (!find_file(r, "flastmod", tag, parsed_string, &finfo)) {
2285 char *t_val;
2287
2288 t_val = ap_ht_time(ctx->pool, finfo.mtime, ctx->time_str, 0);
2289 t_len = strlen(t_val);
2290
2292 ctx->pool, f->c->bucket_alloc));
2293 }
2294 else {
2296 break;
2297 }
2298 }
2299
2300 return APR_SUCCESS;
2301}
2302
2303/*
2304 * <!--#if expr="..." -->
2305 */
2308{
2309 char *tag = NULL;
2310 char *expr = NULL;
2311 request_rec *r = f->r;
2312 int expr_ret, was_error;
2313
2314 if (ctx->argc != 1) {
2316 (ctx->flags & SSI_FLAG_PRINTING)
2318 0, r,
2319 (ctx->argc)
2320 ? APLOGNO(01352) "too many arguments for if element in %s"
2321 : APLOGNO(01353) "missing expr argument for if element in %s",
2322 r->filename);
2323 }
2324
2325 if (!(ctx->flags & SSI_FLAG_PRINTING)) {
2326 ++(ctx->if_nesting_level);
2327 return APR_SUCCESS;
2328 }
2329
2330 if (ctx->argc != 1) {
2332 return APR_SUCCESS;
2333 }
2334
2336
2337 if (strcmp(tag, "expr")) {
2338 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(01354) "unknown parameter \"%s\" "
2339 "to tag if in %s", tag, r->filename);
2341 return APR_SUCCESS;
2342 }
2343
2344 if (!expr) {
2345 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(01355) "missing expr value for if "
2346 "element in %s", r->filename);
2348 return APR_SUCCESS;
2349 }
2350
2351 DEBUG_PRINTF((ctx, "**** if expr=\"%s\"\n", expr));
2352
2353 if (ctx->intern->legacy_expr)
2354 expr_ret = parse_expr(ctx, expr, &was_error);
2355 else
2357
2358 if (was_error) {
2360 return APR_SUCCESS;
2361 }
2362
2363 if (expr_ret) {
2365 }
2366 else {
2368 }
2369
2370 DEBUG_DUMP_COND(ctx, " if");
2371
2372 ctx->if_nesting_level = 0;
2373
2374 return APR_SUCCESS;
2375}
2376
2377/*
2378 * <!--#elif expr="..." -->
2379 */
2382{
2383 char *tag = NULL;
2384 char *expr = NULL;
2385 request_rec *r = f->r;
2386 int expr_ret, was_error;
2387
2388 if (ctx->argc != 1) {
2390 (!(ctx->if_nesting_level)) ? APLOG_ERR : APLOG_WARNING,
2391 0, r,
2392 (ctx->argc)
2393 ? APLOGNO(01356) "too many arguments for if element in %s"
2394 : APLOGNO(01357) "missing expr argument for if element in %s",
2395 r->filename);
2396 }
2397
2398 if (ctx->if_nesting_level) {
2399 return APR_SUCCESS;
2400 }
2401
2402 if (ctx->argc != 1) {
2404 return APR_SUCCESS;
2405 }
2406
2408
2409 if (strcmp(tag, "expr")) {
2410 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(01358) "unknown parameter \"%s\" "
2411 "to tag if in %s", tag, r->filename);
2413 return APR_SUCCESS;
2414 }
2415
2416 if (!expr) {
2417 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(01359) "missing expr in elif "
2418 "statement: %s", r->filename);
2420 return APR_SUCCESS;
2421 }
2422
2423 DEBUG_PRINTF((ctx, "**** elif expr=\"%s\"\n", expr));
2424 DEBUG_DUMP_COND(ctx, " elif");
2425
2426 if (ctx->flags & SSI_FLAG_COND_TRUE) {
2427 ctx->flags &= SSI_FLAG_CLEAR_PRINTING;
2428 return APR_SUCCESS;
2429 }
2430
2431 if (ctx->intern->legacy_expr)
2432 expr_ret = parse_expr(ctx, expr, &was_error);
2433 else
2435
2436 if (was_error) {
2438 return APR_SUCCESS;
2439 }
2440
2441 if (expr_ret) {
2443 }
2444 else {
2446 }
2447
2448 DEBUG_DUMP_COND(ctx, " elif");
2449
2450 return APR_SUCCESS;
2451}
2452
2453/*
2454 * <!--#else -->
2455 */
2458{
2459 request_rec *r = f->r;
2460
2461 if (ctx->argc) {
2463 (!(ctx->if_nesting_level)) ? APLOG_ERR : APLOG_WARNING,
2464 0, r, APLOGNO(01360)
2465 "else directive does not take tags in %s",
2466 r->filename);
2467 }
2468
2469 if (ctx->if_nesting_level) {
2470 return APR_SUCCESS;
2471 }
2472
2473 if (ctx->argc) {
2474 if (ctx->flags & SSI_FLAG_PRINTING) {
2476 }
2477
2478 return APR_SUCCESS;
2479 }
2480
2481 DEBUG_DUMP_COND(ctx, " else");
2482
2483 if (ctx->flags & SSI_FLAG_COND_TRUE) {
2484 ctx->flags &= SSI_FLAG_CLEAR_PRINTING;
2485 }
2486 else {
2488 }
2489
2490 return APR_SUCCESS;
2491}
2492
2493/*
2494 * <!--#endif -->
2495 */
2498{
2499 request_rec *r = f->r;
2500
2501 if (ctx->argc) {
2503 (!(ctx->if_nesting_level)) ? APLOG_ERR : APLOG_WARNING,
2504 0, r, APLOGNO(01361)
2505 "endif directive does not take tags in %s",
2506 r->filename);
2507 }
2508
2509 if (ctx->if_nesting_level) {
2510 --(ctx->if_nesting_level);
2511 return APR_SUCCESS;
2512 }
2513
2514 if (ctx->argc) {
2516 return APR_SUCCESS;
2517 }
2518
2519 DEBUG_DUMP_COND(ctx, "endif");
2520
2522
2523 return APR_SUCCESS;
2524}
2525
2526/*
2527 * <!--#set var="..." value="..." ... -->
2528 */
2531{
2532 const char *encoding = "none", *decoding = "none";
2533 char *var = NULL;
2534 request_rec *r = f->r;
2535 request_rec *sub = r->main;
2536 apr_pool_t *p = r->pool;
2537 int error = 0;
2538
2539 if (ctx->argc < 2) {
2541 (ctx->flags & SSI_FLAG_PRINTING)
2543 0, r,
2544 APLOGNO(01362) "missing argument for set element in %s",
2545 r->filename);
2546 }
2547
2548 if (!(ctx->flags & SSI_FLAG_PRINTING)) {
2549 return APR_SUCCESS;
2550 }
2551
2552 if (ctx->argc < 2) {
2554 return APR_SUCCESS;
2555 }
2556
2557 /* we need to use the 'main' request pool to set notes as that is
2558 * a notes lifetime
2559 */
2560 while (sub) {
2561 p = sub->pool;
2562 sub = sub->main;
2563 }
2564
2565 while (1) {
2566 char *tag = NULL;
2567 char *tag_val = NULL;
2568
2570
2571 if (!tag || !tag_val) {
2572 break;
2573 }
2574
2575 if (!strcmp(tag, "var")) {
2579 }
2580 else if (!strcmp(tag, "decoding")) {
2581 decoding = tag_val;
2582 }
2583 else if (!strcmp(tag, "encoding")) {
2584 encoding = tag_val;
2585 }
2586 else if (!strcmp(tag, "value")) {
2587 char *parsed_string;
2588
2589 if (!var) {
2590 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(01363) "variable must "
2591 "precede value in set directive in %s",
2592 r->filename);
2594 break;
2595 }
2596
2599
2600 if (parsed_string) {
2601 char *last = NULL;
2602 char *e, *d, *token;
2603
2604 d = apr_pstrdup(ctx->pool, decoding);
2605 token = apr_strtok(d, ", \t", &last);
2606
2607 while (token) {
2608 if (!ap_cstr_casecmp(token, "none")) {
2609 /* do nothing */
2610 }
2611 else if (!ap_cstr_casecmp(token, "url")) {
2612 char *buf = apr_pstrdup(ctx->pool, parsed_string);
2615 }
2616 else if (!ap_cstr_casecmp(token, "urlencoded")) {
2617 char *buf = apr_pstrdup(ctx->pool, parsed_string);
2620 }
2621 else if (!ap_cstr_casecmp(token, "entity")) {
2622 char *buf = apr_pstrdup(ctx->pool, parsed_string);
2623 decodehtml(buf);
2625 }
2626 else if (!ap_cstr_casecmp(token, "base64")) {
2628 }
2629 else {
2630 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(01364) "unknown value "
2631 "\"%s\" to parameter \"decoding\" of tag set in "
2632 "%s", token, r->filename);
2634 error = 1;
2635 break;
2636 }
2637 token = apr_strtok(NULL, ", \t", &last);
2638 }
2639
2640 e = apr_pstrdup(ctx->pool, encoding);
2641 token = apr_strtok(e, ", \t", &last);
2642
2643 while (token) {
2644 if (!ap_cstr_casecmp(token, "none")) {
2645 /* do nothing */
2646 }
2647 else if (!ap_cstr_casecmp(token, "url")) {
2649 }
2650 else if (!ap_cstr_casecmp(token, "urlencoded")) {
2652 }
2653 else if (!ap_cstr_casecmp(token, "entity")) {
2655 }
2656 else if (!ap_cstr_casecmp(token, "base64")) {
2657 char *buf;
2658 buf = ap_pbase64encode(ctx->dpool, (char *)parsed_string);
2660 }
2661 else {
2662 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(01365) "unknown value "
2663 "\"%s\" to parameter \"encoding\" of tag set in "
2664 "%s", token, r->filename);
2666 error = 1;
2667 break;
2668 }
2669 token = apr_strtok(NULL, ", \t", &last);
2670 }
2671
2672 }
2673
2674 if (error) {
2675 break;
2676 }
2677
2680 }
2681 else {
2682 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(01366) "Invalid tag for set "
2683 "directive in %s", r->filename);
2685 break;
2686 }
2687 }
2688
2689 return APR_SUCCESS;
2690}
2691
2692/*
2693 * <!--#printenv -->
2694 */
2697{
2698 request_rec *r = f->r;
2699 const apr_array_header_t *arr;
2700 const apr_table_entry_t *elts;
2701 int i;
2702
2703 if (ctx->argc) {
2705 (ctx->flags & SSI_FLAG_PRINTING)
2707 0, r,
2708 APLOGNO(01367) "printenv directive does not take tags in %s",
2709 r->filename);
2710 }
2711
2712 if (!(ctx->flags & SSI_FLAG_PRINTING)) {
2713 return APR_SUCCESS;
2714 }
2715
2716 if (ctx->argc) {
2718 return APR_SUCCESS;
2719 }
2720
2722 elts = (apr_table_entry_t *)arr->elts;
2723
2724 for (i = 0; i < arr->nelts; ++i) {
2725 const char *key_text, *val_text;
2726
2727 /* get key */
2728 key_text = ap_escape_html(ctx->dpool, elts[i].key);
2729
2730 /* get value */
2731 val_text = elts[i].val;
2732 if (val_text == LAZY_VALUE)
2733 val_text = add_include_vars_lazy(r, elts[i].key, ctx->time_str);
2735
2736 apr_brigade_putstrs(bb, NULL, NULL, key_text, "=", val_text, "\n",
2737 NULL);
2738 }
2739
2740 ctx->flush_now = 1;
2741 return APR_SUCCESS;
2742}
2743
2744
2745/*
2746 * +-------------------------------------------------------+
2747 * | |
2748 * | Main Includes-Filter Engine
2749 * | |
2750 * +-------------------------------------------------------+
2751 */
2752
2753/* This is an implementation of the BNDM search algorithm.
2754 *
2755 * Fast and Flexible String Matching by Combining Bit-parallelism and
2756 * Suffix Automata (2001)
2757 * Gonzalo Navarro, Mathieu Raffinot
2758 *
2759 * http://www-igm.univ-mlv.fr/~raffinot/ftp/jea2001.ps.gz
2760 *
2761 * Initial code submitted by Sascha Schumann.
2762 */
2763
2764/* Precompile the bndm_t data structure. */
2765static bndm_t *bndm_compile(apr_pool_t *pool, const char *n, apr_size_t nl)
2766{
2767 unsigned int x;
2768 const char *ne = n + nl;
2769 bndm_t *t = apr_palloc(pool, sizeof(*t));
2770
2771 memset(t->T, 0, sizeof(unsigned int) * 256);
2772 t->pattern_len = nl;
2773
2774 for (x = 1; n < ne; x <<= 1) {
2775 t->T[(unsigned char) *n++] |= x;
2776 }
2777
2778 t->x = x - 1;
2779
2780 return t;
2781}
2782
2783/* Implements the BNDM search algorithm (as described above).
2784 *
2785 * h - the string to look in
2786 * hl - length of the string to look for
2787 * t - precompiled bndm structure against the pattern
2788 *
2789 * Returns the count of character that is the first match or hl if no
2790 * match is found.
2791 */
2792static apr_size_t bndm(bndm_t *t, const char *h, apr_size_t hl)
2793{
2794 const char *skip;
2795 const char *he, *p, *pi;
2796 unsigned int *T, x, d;
2797 apr_size_t nl;
2798
2799 he = h + hl;
2800
2801 T = t->T;
2802 x = t->x;
2803 nl = t->pattern_len;
2804
2805 pi = h - 1; /* pi: p initial */
2806 p = pi + nl; /* compare window right to left. point to the first char */
2807
2808 while (p < he) {
2809 skip = p;
2810 d = x;
2811 do {
2812 d &= T[(unsigned char) *p--];
2813 if (!d) {
2814 break;
2815 }
2816 if ((d & 1)) {
2817 if (p != pi) {
2818 skip = p;
2819 }
2820 else {
2821 return p - h + 1;
2822 }
2823 }
2824 d >>= 1;
2825 } while (d);
2826
2827 pi = skip;
2828 p = pi + nl;
2829 }
2830
2831 return hl;
2832}
2833
2834/*
2835 * returns the index position of the first byte of start_seq (or the len of
2836 * the buffer as non-match)
2837 */
2840{
2841 struct ssi_internal_ctx *intern = ctx->intern;
2843 apr_size_t index;
2844 const char *p, *ep;
2845
2846 if (len < slen) {
2847 p = data; /* try partial match at the end of the buffer (below) */
2848 }
2849 else {
2850 /* try fast bndm search over the buffer
2851 * (hopefully the whole start sequence can be found in this buffer)
2852 */
2853 index = bndm(intern->start_seq_pat, data, len);
2854
2855 /* wow, found it. ready. */
2856 if (index < len) {
2857 intern->state = PARSE_DIRECTIVE;
2858 return index;
2859 }
2860 else {
2861 /* ok, the pattern can't be found as whole in the buffer,
2862 * check the end for a partial match
2863 */
2864 p = data + len - slen + 1;
2865 }
2866 }
2867
2868 ep = data + len;
2869 do {
2871 ++p;
2872 }
2873
2874 index = p - data;
2875
2876 /* found a possible start_seq start */
2877 if (p < ep) {
2878 apr_size_t pos = 1;
2879
2880 ++p;
2882 ++p;
2883 ++pos;
2884 }
2885
2886 /* partial match found. Store the info for the next round */
2887 if (p == ep) {
2888 intern->state = PARSE_HEAD;
2889 intern->parse_pos = pos;
2890 return index;
2891 }
2892 }
2893
2894 /* we must try all combinations; consider (e.g.) SSIStartTag "--->"
2895 * and a string data of "--.-" and the end of the buffer
2896 */
2897 p = data + index + 1;
2898 } while (p < ep);
2899
2900 /* no match */
2901 return len;
2902}
2903
2904/*
2905 * returns the first byte *after* the partial (or final) match.
2906 *
2907 * If we had to trick with the start_seq start, 'release' returns the
2908 * number of chars of the start_seq which appeared not to be part of a
2909 * full tag and may have to be passed down the filter chain.
2910 */
2912 const char *data,
2914 apr_size_t *release)
2915{
2916 struct ssi_internal_ctx *intern = ctx->intern;
2917 apr_size_t pos, spos = 0;
2919 const char *p, *ep;
2920
2921 pos = intern->parse_pos;
2922 ep = data + len;
2923 *release = 0;
2924
2925 do {
2926 p = data;
2927
2928 while (p < ep && pos < slen && *p == intern->start_seq[pos]) {
2929 ++p;
2930 ++pos;
2931 }
2932
2933 /* full match */
2934 if (pos == slen) {
2935 intern->state = PARSE_DIRECTIVE;
2936 return (p - data);
2937 }
2938
2939 /* the whole buffer is a partial match */
2940 if (p == ep) {
2941 intern->parse_pos = pos;
2942 return (p - data);
2943 }
2944
2945 /* No match so far, but again:
2946 * We must try all combinations, since the start_seq is a random
2947 * user supplied string
2948 *
2949 * So: look if the first char of start_seq appears somewhere within
2950 * the current partial match. If it does, try to start a match that
2951 * begins with this offset. (This can happen, if a strange
2952 * start_seq like "---->" spans buffers)
2953 */
2955 do {
2956 ++spos;
2957 ++*release;
2958 p = intern->start_seq + spos;
2959 pos = intern->parse_pos - spos;
2960
2961 while (pos && *p != *intern->start_seq) {
2962 ++p;
2963 ++spos;
2964 ++*release;
2965 --pos;
2966 }
2967
2968 /* if a matching beginning char was found, try to match the
2969 * remainder of the old buffer.
2970 */
2971 if (pos > 1) {
2972 apr_size_t t = 1;
2973
2974 ++p;
2976 ++p;
2977 ++t;
2978 }
2979
2980 if (t == pos) {
2981 /* yeah, another partial match found in the *old*
2982 * buffer, now test the *current* buffer for
2983 * continuing match
2984 */
2985 break;
2986 }
2987 }
2988 } while (pos > 1);
2989
2990 if (pos) {
2991 continue;
2992 }
2993 }
2994
2995 break;
2996 } while (1); /* work hard to find a match ;-) */
2997
2998 /* no match at all, release all (wrongly) matched chars so far */
2999 *release = intern->parse_pos;
3000 intern->state = PARSE_PRE_HEAD;
3001 return 0;
3002}
3003
3004/*
3005 * returns the position after the directive
3006 */
3008 apr_size_t len, char ***store,
3010{
3011 struct ssi_internal_ctx *intern = ctx->intern;
3012 const char *p = data;
3013 const char *ep = data + len;
3014 apr_size_t pos;
3015
3016 switch (intern->state) {
3017 case PARSE_DIRECTIVE:
3018 while (p < ep && !apr_isspace(*p)) {
3019 /* we have to consider the case of missing space between directive
3020 * and end_seq (be somewhat lenient), e.g. <!--#printenv-->
3021 */
3022 if (*p == *intern->end_seq) {
3023 intern->state = PARSE_DIRECTIVE_TAIL;
3024 intern->parse_pos = 1;
3025 ++p;
3026 return (p - data);
3027 }
3028 ++p;
3029 }
3030
3031 if (p < ep) { /* found delimiter whitespace */
3033 *store = &intern->directive;
3034 *store_len = &intern->directive_len;
3035 }
3036
3037 break;
3038
3040 pos = intern->parse_pos;
3041
3042 while (p < ep && pos < intern->end_seq_len &&
3043 *p == intern->end_seq[pos]) {
3044 ++p;
3045 ++pos;
3046 }
3047
3048 /* full match, we're done */
3049 if (pos == intern->end_seq_len) {
3051 *store = &intern->directive;
3052 *store_len = &intern->directive_len;
3053 break;
3054 }
3055
3056 /* partial match, the buffer is too small to match fully */
3057 if (p == ep) {
3058 intern->parse_pos = pos;
3059 break;
3060 }
3061
3062 /* no match. continue normal parsing */
3063 intern->state = PARSE_DIRECTIVE;
3064 return 0;
3065
3067 intern->state = PARSE_EXECUTE;
3068 intern->directive_len -= intern->end_seq_len;
3069 /* continue immediately with the next state */
3070
3072 if (PARSE_DIRECTIVE_POSTNAME == intern->state) {
3073 intern->state = PARSE_PRE_ARG;
3074 }
3075 ctx->argc = 0;
3076 intern->argv = NULL;
3077
3078 if (!intern->directive_len) {
3079 intern->error = 1;
3080 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, ctx->r, APLOGNO(01368) "missing "
3081 "directive name in parsed document %s",
3082 ctx->r->filename);
3083 }
3084 else {
3085 char *sp = intern->directive;
3086 char *sep = intern->directive + intern->directive_len;
3087
3088 /* normalize directive name */
3089 for (; sp < sep; ++sp) {
3090 *sp = apr_tolower(*sp);
3091 }
3092 }
3093
3094 return 0;
3095
3096 default:
3097 /* get a rid of a gcc warning about unhandled enumerations */
3098 break;
3099 }
3100
3101 return (p - data);
3102}
3103
3104/*
3105 * find out whether the next token is (a possible) end_seq or an argument
3106 */
3109{
3110 struct ssi_internal_ctx *intern = ctx->intern;
3111 const char *p = data;
3112 const char *ep = data + len;
3113
3114 /* skip leading WS */
3115 while (p < ep && apr_isspace(*p)) {
3116 ++p;
3117 }
3118
3119 /* buffer doesn't consist of whitespaces only */
3120 if (p < ep) {
3121 intern->state = (*p == *intern->end_seq) ? PARSE_TAIL : PARSE_ARG;
3122 }
3123
3124 return (p - data);
3125}
3126
3127/*
3128 * test the stream for end_seq. If it doesn't match at all, it must be an
3129 * argument
3130 */
3133{
3134 struct ssi_internal_ctx *intern = ctx->intern;
3135 const char *p = data;
3136 const char *ep = data + len;
3137 apr_size_t pos = intern->parse_pos;
3138
3139 if (PARSE_TAIL == intern->state) {
3140 intern->state = PARSE_TAIL_SEQ;
3141 pos = intern->parse_pos = 0;
3142 }
3143
3144 while (p < ep && pos < intern->end_seq_len && *p == intern->end_seq[pos]) {
3145 ++p;
3146 ++pos;
3147 }
3148
3149 /* bingo, full match */
3150 if (pos == intern->end_seq_len) {
3151 intern->state = PARSE_EXECUTE;
3152 return (p - data);
3153 }
3154
3155 /* partial match, the buffer is too small to match fully */
3156 if (p == ep) {
3157 intern->parse_pos = pos;
3158 return (p - data);
3159 }
3160
3161 /* no match. It must be an argument string then
3162 * The caller should cleanup and rewind to the reparse point
3163 */
3164 intern->state = PARSE_ARG;
3165 return 0;
3166}
3167
3168/*
3169 * extract name=value from the buffer
3170 * A pcre-pattern could look (similar to):
3171 * name\s*(?:=\s*(["'`]?)value\1(?>\s*))?
3172 */
3174 apr_size_t len, char ***store,
3176{
3177 struct ssi_internal_ctx *intern = ctx->intern;
3178 const char *p = data;
3179 const char *ep = data + len;
3180
3181 switch (intern->state) {
3182 case PARSE_ARG:
3183 /*
3184 * create argument structure and append it to the current list
3185 */
3186 intern->current_arg = apr_palloc(ctx->dpool,
3187 sizeof(*intern->current_arg));
3188 intern->current_arg->next = NULL;
3189
3190 ++(ctx->argc);
3191 if (!intern->argv) {
3192 intern->argv = intern->current_arg;
3193 }
3194 else {
3195 arg_item_t *newarg = intern->argv;
3196
3197 while (newarg->next) {
3198 newarg = newarg->next;
3199 }
3200 newarg->next = intern->current_arg;
3201 }
3202
3203 /* check whether it's a valid one. If it begins with a quote, we
3204 * can safely assume, someone forgot the name of the argument
3205 */
3206 switch (*p) {
3207 case '"': case '\'': case '`':
3208 *store = NULL;
3209
3210 intern->state = PARSE_ARG_VAL;
3211 intern->quote = *p++;
3212 intern->current_arg->name = NULL;
3213 intern->current_arg->name_len = 0;
3214 intern->error = 1;
3215
3216 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, ctx->r, APLOGNO(01369) "missing "
3217 "argument name for value to tag %s in %s",
3218 apr_pstrmemdup(ctx->r->pool, intern->directive,
3219 intern->directive_len),
3220 ctx->r->filename);
3221
3222 return (p - data);
3223
3224 default:
3225 intern->state = PARSE_ARG_NAME;
3226 }
3227 /* continue immediately with next state */
3228
3229 case PARSE_ARG_NAME:
3230 while (p < ep && !apr_isspace(*p) && *p != '=') {
3231 ++p;
3232 }
3233
3234 if (p < ep) {
3235 intern->state = PARSE_ARG_POSTNAME;
3236 *store = &intern->current_arg->name;
3237 *store_len = &intern->current_arg->name_len;
3238 return (p - data);
3239 }
3240 break;
3241
3242 case PARSE_ARG_POSTNAME:
3243 intern->current_arg->name = apr_pstrmemdup(ctx->dpool,
3244 intern->current_arg->name,
3245 intern->current_arg->name_len);
3246 if (!intern->current_arg->name_len) {
3247 intern->error = 1;
3248 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, ctx->r, APLOGNO(01370) "missing "
3249 "argument name for value to tag %s in %s",
3250 apr_pstrmemdup(ctx->r->pool, intern->directive,
3251 intern->directive_len),
3252 ctx->r->filename);
3253 }
3254 else {
3256 }
3257
3258 intern->state = PARSE_ARG_EQ;
3259 /* continue with next state immediately */
3260
3261 case PARSE_ARG_EQ:
3262 *store = NULL;
3263
3264 while (p < ep && apr_isspace(*p)) {
3265 ++p;
3266 }
3267
3268 if (p < ep) {
3269 if (*p == '=') {
3270 intern->state = PARSE_ARG_PREVAL;
3271 ++p;
3272 }
3273 else { /* no value */
3274 intern->current_arg->value = NULL;
3275 intern->state = PARSE_PRE_ARG;
3276 }
3277
3278 return (p - data);
3279 }
3280 break;
3281
3282 case PARSE_ARG_PREVAL:
3283 *store = NULL;
3284
3285 while (p < ep && apr_isspace(*p)) {
3286 ++p;
3287 }
3288
3289 /* buffer doesn't consist of whitespaces only */
3290 if (p < ep) {
3291 intern->state = PARSE_ARG_VAL;
3292 switch (*p) {
3293 case '"': case '\'': case '`':
3294 intern->quote = *p++;
3295 break;
3296 default:
3297 intern->quote = '\0';
3298 break;
3299 }
3300
3301 return (p - data);
3302 }
3303 break;
3304
3305 case PARSE_ARG_VAL_ESC:
3306 if (*p == intern->quote) {
3307 ++p;
3308 }
3309 intern->state = PARSE_ARG_VAL;
3310 /* continue with next state immediately */
3311
3312 case PARSE_ARG_VAL:
3313 for (; p < ep; ++p) {
3314 if (intern->quote && *p == '\\') {
3315 ++p;
3316 if (p == ep) {
3317 intern->state = PARSE_ARG_VAL_ESC;
3318 break;
3319 }
3320
3321 if (*p != intern->quote) {
3322 --p;
3323 }
3324 }
3325 else if (intern->quote && *p == intern->quote) {
3326 ++p;
3327 *store = &intern->current_arg->value;
3328 *store_len = &intern->current_arg->value_len;
3329 intern->state = PARSE_ARG_POSTVAL;
3330 break;
3331 }
3332 else if (!intern->quote && apr_isspace(*p)) {
3333 ++p;
3334 *store = &intern->current_arg->value;
3335 *store_len = &intern->current_arg->value_len;
3336 intern->state = PARSE_ARG_POSTVAL;
3337 break;
3338 }
3339 }
3340
3341 return (p - data);
3342
3343 case PARSE_ARG_POSTVAL:
3344 /*
3345 * The value is still the raw input string. Finally clean it up.
3346 */
3347 --(intern->current_arg->value_len);
3348
3349 /* strip quote escaping \ from the string */
3350 if (intern->quote) {
3351 apr_size_t shift = 0;
3352 char *sp;
3353
3354 sp = intern->current_arg->value;
3355 ep = intern->current_arg->value + intern->current_arg->value_len;
3356 while (sp < ep && *sp != '\\') {
3357 ++sp;
3358 }
3359 for (; sp < ep; ++sp) {
3360 if (*sp == '\\' && sp[1] == intern->quote) {
3361 ++sp;
3362 ++shift;
3363 }
3364 if (shift) {
3365 *(sp-shift) = *sp;
3366 }
3367 }
3368
3369 intern->current_arg->value_len -= shift;
3370 }
3371
3372 intern->current_arg->value[intern->current_arg->value_len] = '\0';
3373 intern->state = PARSE_PRE_ARG;
3374
3375 return 0;
3376
3377 default:
3378 /* get a rid of a gcc warning about unhandled enumerations */
3379 break;
3380 }
3381
3382 return len; /* partial match of something */
3383}
3384
3385/*
3386 * This is the main loop over the current bucket brigade.
3387 */
3389{
3390 include_ctx_t *ctx = f->ctx;
3391 struct ssi_internal_ctx *intern = ctx->intern;
3392 request_rec *r = f->r;
3396 char *magic; /* magic pointer for sentinel use */
3397
3398 /* fast exit */
3399 if (APR_BRIGADE_EMPTY(bb)) {
3400 return APR_SUCCESS;
3401 }
3402
3403 /* we may crash, since already cleaned up; hand over the responsibility
3404 * to the next filter;-)
3405 */
3406 if (intern->seen_eos) {
3407 return ap_pass_brigade(f->next, bb);
3408 }
3409
3410 /* All stuff passed along has to be put into that brigade */
3411 pass_bb = apr_brigade_create(ctx->pool, f->c->bucket_alloc);
3412
3413 /* initialization for this loop */
3414 intern->bytes_read = 0;
3415 intern->error = 0;
3416 ctx->flush_now = 0;
3417
3418 /* loop over the current bucket brigade */
3419 while (b != APR_BRIGADE_SENTINEL(bb)) {
3420 const char *data = NULL;
3421 apr_size_t len, index, release;
3422 apr_bucket *newb = NULL;
3423 char **store = &magic;
3425
3426 /* handle meta buckets before reading any data */
3429
3431
3432 if (APR_BUCKET_IS_EOS(b)) {
3433 intern->seen_eos = 1;
3434
3435 /* Hit end of stream, time for cleanup ... But wait!
3436 * Perhaps we're not ready yet. We may have to loop one or
3437 * two times again to finish our work. In that case, we
3438 * just re-insert the EOS bucket to allow for an extra loop.
3439 *
3440 * PARSE_EXECUTE means, we've hit a directive just before the
3441 * EOS, which is now waiting for execution.
3442 *
3443 * PARSE_DIRECTIVE_POSTTAIL means, we've hit a directive with
3444 * no argument and no space between directive and end_seq
3445 * just before the EOS. (consider <!--#printenv--> as last
3446 * or only string within the stream). This state, however,
3447 * just cleans up and turns itself to PARSE_EXECUTE, which
3448 * will be passed through within the next (and actually
3449 * last) round.
3450 */
3451 if (PARSE_EXECUTE == intern->state ||
3452 PARSE_DIRECTIVE_POSTTAIL == intern->state) {
3454 }
3455 else {
3456 break; /* END OF STREAM */
3457 }
3458 }
3459 else {
3461
3462 if (APR_BUCKET_IS_FLUSH(b)) {
3463 ctx->flush_now = 1;
3464 }
3465
3466 b = newb;
3467 continue;
3468 }
3469 }
3470
3471 /* enough is enough ... */
3472 if (ctx->flush_now ||
3474
3475 if (!APR_BRIGADE_EMPTY(pass_bb)) {
3476 rv = ap_pass_brigade(f->next, pass_bb);
3477 if (rv != APR_SUCCESS) {
3479 return rv;
3480 }
3481 }
3482
3483 ctx->flush_now = 0;
3484 intern->bytes_read = 0;
3485 }
3486
3487 /* read the current bucket data */
3488 len = 0;
3489 if (!intern->seen_eos) {
3490 if (intern->bytes_read > 0) {
3492 if (APR_STATUS_IS_EAGAIN(rv)) {
3493 ctx->flush_now = 1;
3494 continue;
3495 }
3496 }
3497
3498 if (!len || rv != APR_SUCCESS) {
3500 }
3501
3502 if (rv != APR_SUCCESS) {
3504 return rv;
3505 }
3506
3507 intern->bytes_read += len;
3508 }
3509
3510 /* zero length bucket, fetch next one */
3511 if (!len && !intern->seen_eos) {
3512 b = APR_BUCKET_NEXT(b);
3513 continue;
3514 }
3515
3516 /*
3517 * it's actually a data containing bucket, start/continue parsing
3518 */
3519
3520 switch (intern->state) {
3521 /* no current tag; search for start sequence */
3522 case PARSE_PRE_HEAD:
3523 index = find_start_sequence(ctx, data, len);
3524
3525 if (index < len) {
3526 apr_bucket_split(b, index);
3527 }
3528
3530 if (ctx->flags & SSI_FLAG_PRINTING) {
3533 }
3534 else {
3536 }
3537
3538 if (index < len) {
3539 /* now delete the start_seq stuff from the remaining bucket */
3540 if (PARSE_DIRECTIVE == intern->state) { /* full match */
3542 ctx->flush_now = 1; /* pass pre-tag stuff */
3543 }
3544
3547 }
3548 else {
3549 b = newb;
3550 }
3551
3552 break;
3553
3554 /* we're currently looking for the end of the start sequence */
3555 case PARSE_HEAD:
3556 index = find_partial_start_sequence(ctx, data, len, &release);
3557
3558 /* check if we mismatched earlier and have to release some chars */
3559 if (release && (ctx->flags & SSI_FLAG_PRINTING)) {
3560 char *to_release = apr_pmemdup(ctx->pool, intern->start_seq, release);
3561
3562 newb = apr_bucket_pool_create(to_release, release, ctx->pool,
3563 f->c->bucket_alloc);
3565 }
3566
3567 if (index) { /* any match */
3568 /* now delete the start_seq stuff from the remaining bucket */
3569 if (PARSE_DIRECTIVE == intern->state) { /* final match */
3570 apr_bucket_split(b, index);
3571 ctx->flush_now = 1; /* pass pre-tag stuff */
3572 }
3575 b = newb;
3576 }
3577
3578 break;
3579
3580 /* we're currently grabbing the directive name */
3581 case PARSE_DIRECTIVE:
3585 index = find_directive(ctx, data, len, &store, &store_len);
3586
3587 if (index) {
3588 apr_bucket_split(b, index);
3590 }
3591
3592 if (store) {
3593 if (index) {
3597 b = newb;
3598 }
3599
3600 /* time for cleanup? */
3601 if (store != &magic) {
3602 apr_brigade_pflatten(intern->tmp_bb, store, store_len,
3603 ctx->dpool);
3604 apr_brigade_cleanup(intern->tmp_bb);
3605 }
3606 }
3607 else if (index) {
3609 b = newb;
3610 }
3611
3612 break;
3613
3614 /* skip WS and find out what comes next (arg or end_seq) */
3615 case PARSE_PRE_ARG:
3616 index = find_arg_or_tail(ctx, data, len);
3617
3618 if (index) { /* skipped whitespaces */
3619 if (index < len) {
3620 apr_bucket_split(b, index);
3621 }
3624 b = newb;
3625 }
3626
3627 break;
3628
3629 /* currently parsing name[=val] */
3630 case PARSE_ARG:
3631 case PARSE_ARG_NAME:
3632 case PARSE_ARG_POSTNAME:
3633 case PARSE_ARG_EQ:
3634 case PARSE_ARG_PREVAL:
3635 case PARSE_ARG_VAL:
3636 case PARSE_ARG_VAL_ESC:
3637 case PARSE_ARG_POSTVAL:
3638 index = find_argument(ctx, data, len, &store, &store_len);
3639
3640 if (index) {
3641 apr_bucket_split(b, index);
3643 }
3644
3645 if (store) {
3646 if (index) {
3650 b = newb;
3651 }
3652
3653 /* time for cleanup? */
3654 if (store != &magic) {
3655 apr_brigade_pflatten(intern->tmp_bb, store, store_len,
3656 ctx->dpool);
3657 apr_brigade_cleanup(intern->tmp_bb);
3658 }
3659 }
3660 else if (index) {
3662 b = newb;
3663 }
3664
3665 break;
3666
3667 /* try to match end_seq at current pos. */
3668 case PARSE_TAIL:
3669 case PARSE_TAIL_SEQ:
3670 index = find_tail(ctx, data, len);
3671
3672 switch (intern->state) {
3673 case PARSE_EXECUTE: /* full match */
3674 apr_bucket_split(b, index);
3677 b = newb;
3678 break;
3679
3680 case PARSE_ARG: /* no match */
3681 /* PARSE_ARG must reparse at the beginning */
3682 APR_BRIGADE_PREPEND(bb, intern->tmp_bb);
3683 b = APR_BRIGADE_FIRST(bb);
3684 break;
3685
3686 default: /* partial match */
3691 b = newb;
3692 break;
3693 }
3694
3695 break;
3696
3697 /* now execute the parsed directive, cleanup the space and
3698 * start again with PARSE_PRE_HEAD
3699 */
3700 case PARSE_EXECUTE:
3701 /* if there was an error, it was already logged; just stop here */
3702 if (intern->error) {
3703 if (ctx->flags & SSI_FLAG_PRINTING) {
3705 intern->error = 0;
3706 }
3707 }
3708 else {
3710
3711 handle_func =
3713 intern->directive_len);
3714
3715 if (handle_func) {
3717 rv = handle_func(ctx, f, pass_bb);
3718 if (rv != APR_SUCCESS) {
3720 return rv;
3721 }
3722 }
3723 else {
3725 "unknown directive \"%s\" in parsed doc %s",
3726 apr_pstrmemdup(r->pool, intern->directive,
3727 intern->directive_len),
3728 r->filename);
3729 if (ctx->flags & SSI_FLAG_PRINTING) {
3731 }
3732 }
3733 }
3734
3735 /* cleanup */
3736 apr_pool_clear(ctx->dpool);
3737 apr_brigade_cleanup(intern->tmp_bb);
3738
3739 /* Oooof. Done here, start next round */
3740 intern->state = PARSE_PRE_HEAD;
3741 break;
3742
3743 } /* switch(ctx->state) */
3744
3745 } /* while (brigade) */
3746
3747 /* End of stream. Final cleanup */
3748 if (intern->seen_eos) {
3749 if (PARSE_HEAD == intern->state) {
3750 if (ctx->flags & SSI_FLAG_PRINTING) {
3751 char *to_release = apr_pmemdup(ctx->pool, intern->start_seq,
3752 intern->parse_pos);
3753
3756 intern->parse_pos, ctx->pool,
3757 f->c->bucket_alloc));
3758 }
3759 }
3760 else if (PARSE_PRE_HEAD != intern->state) {
3762 "SSI directive was not properly finished at the end "
3763 "of parsed document %s", r->filename);
3764 if (ctx->flags & SSI_FLAG_PRINTING) {
3766 }
3767 }
3768
3769 if (!(ctx->flags & SSI_FLAG_PRINTING)) {
3771 "missing closing endif directive in parsed document"
3772 " %s", r->filename);
3773 }
3774
3775 /* cleanup our temporary memory */
3776 apr_brigade_destroy(intern->tmp_bb);
3777 apr_pool_destroy(ctx->dpool);
3778
3779 /* don't forget to finally insert the EOS bucket */
3781 }
3782
3783 /* if something's left over, pass it along */
3784 if (!APR_BRIGADE_EMPTY(pass_bb)) {
3785 rv = ap_pass_brigade(f->next, pass_bb);
3786 }
3787 else {
3788 rv = APR_SUCCESS;
3790 }
3791 return rv;
3792}
3793
3794
3795/*
3796 * +-------------------------------------------------------+
3797 * | |
3798 * | Runtime Hooks
3799 * | |
3800 * +-------------------------------------------------------+
3801 */
3802
3804{
3805 include_dir_config *conf = ap_get_module_config(f->r->per_dir_config,
3806 &include_module);
3807
3808 /* When our xbithack value isn't set to full or our platform isn't
3809 * providing group-level protection bits or our group-level bits do not
3810 * have group-execite on, we will set the no_local_copy value to 1 so
3811 * that we will not send 304s.
3812 */
3813 if ((conf->xbithack != XBITHACK_FULL)
3814 || !(f->r->finfo.valid & APR_FINFO_GPROT)
3815 || !(f->r->finfo.protection & APR_GEXECUTE)) {
3816 f->r->no_local_copy = 1;
3817 }
3818
3819 /* Don't allow ETag headers to be generated - see RFC2616 - 13.3.4.
3820 * We don't know if we are going to be including a file or executing
3821 * a program - in either case a strong ETag header will likely be invalid.
3822 */
3823 if (conf->etag <= 0) {
3824 apr_table_setn(f->r->notes, "no-etag", "");
3825 }
3826
3827 return OK;
3828}
3829
3831{
3832 request_rec *r = f->r;
3835 &include_module);
3836
3838 &include_module);
3839
3840 if (!(ap_allow_options(r) & OPT_INCLUDES)) {
3842 "mod_include: Options +Includes (or IncludesNoExec) "
3843 "wasn't set, INCLUDES filter removed: %s", r->uri);
3845 return ap_pass_brigade(f->next, b);
3846 }
3847
3848 if (!f->ctx) {
3849 struct ssi_internal_ctx *intern;
3851
3852 /* create context for this filter */
3853 f->ctx = ctx = apr_palloc(r->pool, sizeof(*ctx));
3854 ctx->r = r;
3855 ctx->intern = intern = apr_palloc(r->pool, sizeof(*ctx->intern));
3856 ctx->pool = r->pool;
3857 apr_pool_create(&ctx->dpool, ctx->pool);
3858 apr_pool_tag(ctx->dpool, "includes_dpool");
3859
3860 /* runtime data */
3861 intern->tmp_bb = apr_brigade_create(ctx->pool, f->c->bucket_alloc);
3862 intern->seen_eos = 0;
3863 intern->state = PARSE_PRE_HEAD;
3865 if ((ap_allow_options(r) & OPT_INC_WITH_EXEC) == 0) {
3866 ctx->flags |= SSI_FLAG_NO_EXEC;
3867 }
3868 intern->legacy_expr = (conf->legacy_expr > 0);
3869 intern->expr_eval_ctx = NULL;
3870 intern->expr_err = NULL;
3871 intern->expr_vary_this = NULL;
3872
3873 ctx->if_nesting_level = 0;
3874 intern->re = NULL;
3875
3876 ctx->error_str = conf->default_error_msg ? conf->default_error_msg :
3878 ctx->time_str = conf->default_time_fmt ? conf->default_time_fmt :
3880 intern->start_seq = sconf->default_start_tag;
3881 intern->start_seq_pat = bndm_compile(ctx->pool, intern->start_seq,
3882 strlen(intern->start_seq));
3883 intern->end_seq = sconf->default_end_tag;
3884 intern->end_seq_len = strlen(intern->end_seq);
3885 intern->undefined_echo = conf->undefined_echo ? conf->undefined_echo :
3887 intern->undefined_echo_len = strlen(intern->undefined_echo);
3888 }
3889
3890 if ((parent = ap_get_module_config(r->request_config, &include_module))) {
3891 /* Kludge --- for nested includes, we want to keep the subprocess
3892 * environment of the base document (for compatibility); that means
3893 * torquing our own last_modified date as well so that the
3894 * LAST_MODIFIED variable gets reset to the proper value if the
3895 * nested document resets <!--#config timefmt -->.
3896 */
3899 r->finfo.mtime = r->main->finfo.mtime;
3900 }
3901 else {
3902 /* we're not a nested include, so we create an initial
3903 * environment */
3907 }
3908 /* Always unset the content-length. There is no way to know if
3909 * the content will be modified at some point by send_parsed_content.
3910 * It is very possible for us to not find any content in the first
3911 * 9k of the file, but still have to modify the content of the file.
3912 * If we are going to pass the file through send_parsed_content, then
3913 * the content-length should just be unset.
3914 */
3915 apr_table_unset(f->r->headers_out, "Content-Length");
3916
3917 /* Always unset the Last-Modified field - see RFC2616 - 13.3.4.
3918 * We don't know if we are going to be including a file or executing
3919 * a program which may change the Last-Modified header or make the
3920 * content completely dynamic. Therefore, we can't support these
3921 * headers.
3922 *
3923 * Exception: XBitHack full means we *should* set the
3924 * Last-Modified field.
3925 *
3926 * SSILastModified on means we *should* set the Last-Modified field
3927 * if not present, or respect an existing value if present.
3928 */
3929
3930 /* Must we respect the last modified header? By default, no */
3931 if (conf->lastmodified > 0) {
3932
3933 /* update the last modified if we have a valid time, and only if
3934 * we don't already have a valid last modified.
3935 */
3937 && !apr_table_get(f->r->headers_out, "Last-Modified")) {
3940 }
3941
3942 }
3943
3944 /* Assure the platform supports Group protections */
3945 else if (((conf->xbithack == XBITHACK_FULL ||
3946 (conf->xbithack == XBITHACK_UNSET &&
3949 && (r->finfo.protection & APR_GEXECUTE))) {
3952 }
3953 else {
3954 apr_table_unset(f->r->headers_out, "Last-Modified");
3955 }
3956
3957 /* add QUERY stuff to env cause it ain't yet */
3958 if (r->args) {
3959 char *arg_copy = apr_pstrdup(r->pool, r->args);
3960
3961 apr_table_setn(r->subprocess_env, "QUERY_STRING", r->args);
3963 apr_table_setn(r->subprocess_env, "QUERY_STRING_UNESCAPED",
3965 }
3966
3967 return send_parsed_content(f, b);
3968}
3969
3971{
3972 if (r->handler && (strcmp(r->handler, "server-parsed") == 0))
3973 {
3974 if (!r->content_type || !*r->content_type) {
3975 ap_set_content_type_ex(r, "text/html", 1);
3976 }
3977 r->handler = "default-handler";
3978 }
3979 else
3980#if defined(OS2) || defined(WIN32) || defined(NETWARE)
3981 /* These OS's don't support xbithack. This is being worked on. */
3982 {
3983 return DECLINED;
3984 }
3985#else
3986 {
3988 &include_module);
3989
3990 if (conf->xbithack == XBITHACK_OFF ||
3992 conf->xbithack == XBITHACK_UNSET))
3993 {
3994 return DECLINED;
3995 }
3996
3997 if (!(r->finfo.protection & APR_UEXECUTE)) {
3998 return DECLINED;
3999 }
4000
4001 if (!r->content_type || strncmp(r->content_type, "text/html", 9)) {
4002 return DECLINED;
4003 }
4004 }
4005#endif
4006
4007 /* We always return declined, because the default handler actually
4008 * serves the file. All we have to do is add the filter.
4009 */
4010 ap_add_output_filter("INCLUDES", NULL, r, r->connection);
4011 return DECLINED;
4012}
4013
4014
4015/*
4016 * +-------------------------------------------------------+
4017 * | |
4018 * | Configuration Handling
4019 * | |
4020 * +-------------------------------------------------------+
4021 */
4022
4024{
4026
4027 result->xbithack = XBITHACK_UNSET;
4028 result->lastmodified = UNSET;
4029 result->etag = UNSET;
4030 result->legacy_expr = UNSET;
4031
4032 return result;
4033}
4034
4035#define MERGE(b,o,n,val,unset) n->val = o->val != unset ? o->val : b->val
4037{
4040 *new = apr_palloc(p, sizeof(include_dir_config));
4041 MERGE(base, over, new, default_error_msg, NULL);
4042 MERGE(base, over, new, default_time_fmt, NULL);
4044 MERGE(base, over, new, xbithack, XBITHACK_UNSET);
4045 MERGE(base, over, new, lastmodified, UNSET);
4046 MERGE(base, over, new, etag, UNSET);
4047 MERGE(base, over, new, legacy_expr, UNSET);
4048 return new;
4049}
4050
4052{
4054
4056 result->default_end_tag = DEFAULT_END_SEQUENCE;
4057 result->default_start_tag = DEFAULT_START_SEQUENCE;
4058
4059 return result;
4060}
4061
4062static const char *set_xbithack(cmd_parms *cmd, void *mconfig, const char *arg)
4063{
4065
4066 if (!strcasecmp(arg, "off")) {
4067 conf->xbithack = XBITHACK_OFF;
4068 }
4069 else if (!strcasecmp(arg, "on")) {
4070 conf->xbithack = XBITHACK_ON;
4071 }
4072 else if (!strcasecmp(arg, "full")) {
4073 conf->xbithack = XBITHACK_FULL;
4074 }
4075 else {
4076 return "XBitHack must be set to Off, On, or Full";
4077 }
4078
4079 return NULL;
4080}
4081
4082static const char *set_default_start_tag(cmd_parms *cmd, void *mconfig,
4083 const char *tag)
4084{
4086 const char *p = tag;
4087
4088 /* be consistent. (See below in set_default_end_tag) */
4089 while (*p) {
4090 if (apr_isspace(*p)) {
4091 return "SSIStartTag may not contain any whitespaces";
4092 }
4093 ++p;
4094 }
4095
4096 conf= ap_get_module_config(cmd->server->module_config , &include_module);
4097 conf->default_start_tag = tag;
4098
4099 return NULL;
4100}
4101
4102static const char *set_default_end_tag(cmd_parms *cmd, void *mconfig,
4103 const char *tag)
4104{
4106 const char *p = tag;
4107
4108 /* sanity check. The parser may fail otherwise */
4109 while (*p) {
4110 if (apr_isspace(*p)) {
4111 return "SSIEndTag may not contain any whitespaces";
4112 }
4113 ++p;
4114 }
4115
4116 conf= ap_get_module_config(cmd->server->module_config , &include_module);
4117 conf->default_end_tag = tag;
4118
4119 return NULL;
4120}
4121
4122static const char *set_undefined_echo(cmd_parms *cmd, void *mconfig,
4123 const char *msg)
4124{
4126 conf->undefined_echo = msg;
4127
4128 return NULL;
4129}
4130
4131static const char *set_default_error_msg(cmd_parms *cmd, void *mconfig,
4132 const char *msg)
4133{
4135 conf->default_error_msg = msg;
4136
4137 return NULL;
4138}
4139
4140static const char *set_default_time_fmt(cmd_parms *cmd, void *mconfig,
4141 const char *fmt)
4142{
4144 conf->default_time_fmt = fmt;
4145
4146 return NULL;
4147}
4148
4149
4150/*
4151 * +-------------------------------------------------------+
4152 * | |
4153 * | Module Initialization and Configuration
4154 * | |
4155 * +-------------------------------------------------------+
4156 */
4157
4182
4184{
4186 "Off, On, or Full"),
4188 "a string"),
4189 AP_INIT_TAKE1("SSITimeFormat", set_default_time_fmt, NULL, OR_ALL,
4190 "a strftime(3) formatted string"),
4192 "SSI Start String Tag"),
4194 "SSI End String Tag"),
4195 AP_INIT_TAKE1("SSIUndefinedEcho", set_undefined_echo, NULL, OR_ALL,
4196 "String to be displayed if an echoed variable is undefined"),
4197 AP_INIT_FLAG("SSILegacyExprParser", ap_set_flag_slot_char,
4199 OR_LIMIT,
4200 "Whether to use the legacy expression parser compatible "
4201 "with <= 2.2.x. Limited to 'on' or 'off'"),
4202 AP_INIT_FLAG("SSILastModified", ap_set_flag_slot_char,
4203 (void *)APR_OFFSETOF(include_dir_config, lastmodified),
4204 OR_LIMIT, "Whether to set the last modified header or respect "
4205 "an existing header. Limited to 'on' or 'off'"),
4207 (void *)APR_OFFSETOF(include_dir_config, etag),
4208 OR_LIMIT, "Whether to allow the generation of ETags within the server. "
4209 "Existing ETags will be preserved. Limited to 'on' or 'off'"),
4210 {NULL}
4211};
4212
4214{
4215 apr_hash_set(include_handlers, tag, strlen(tag), (const void *)func);
4216}
4217
4228
4230{
4232 create_includes_dir_config, /* dir config creater */
4233 merge_includes_dir_config, /* dir config merger */
4234 create_includes_server_config,/* server config */
4235 NULL, /* merge server config */
4236 includes_cmds, /* command apr_table_t */
4237 register_hooks /* register hooks */
4238};
Symbol export macros and hook functions.
Expression parser.
#define AP_EXPR_FUNC_STRING
Definition ap_expr.h:259
int n
Definition ap_regex.h:278
int int const char ** match
Definition ap_regex.h:279
const char apr_size_t len
Definition ap_regex.h:187
#define AP_REG_EXTENDED
Definition ap_regex.h:78
APR Hash Tables.
APR general purpose library routines.
APR-UTIL registration of functions exported by modules.
apr_size_t const unsigned char unsigned int unsigned int d
Definition apr_siphash.h:72
APR Strings library.
APR Thread and Process Library.
APR User ID Services.
APR Standard Headers Support.
return found
Definition core.c:2840
#define AP_INIT_TAKE1(directive, func, mconfig, where, help)
#define ap_get_module_config(v, m)
void ap_hook_post_config(ap_HOOK_post_config_t *pf, const char *const *aszPre, const char *const *aszSucc, int nOrder)
Definition config.c:105
#define AP_DECLARE_MODULE(foo)
#define AP_INIT_FLAG(directive, func, mconfig, where, help)
ap_conf_vector_t * base
const char * ap_set_flag_slot_char(cmd_parms *cmd, void *struct_ptr, int arg)
Definition config.c:1523
#define ap_set_module_config(v, m, val)
request_rec * r
#define AP_MAX_REG_MATCH
Definition httpd.h:309
#define DECLINED
Definition httpd.h:457
#define OK
Definition httpd.h:456
apr_status_t ap_pass_brigade(ap_filter_t *filter, apr_bucket_brigade *bucket)
ap_filter_rec_t * ap_register_output_filter(const char *name, ap_out_filter_func filter_func, ap_init_filter_func filter_init, ap_filter_type ftype)
ap_filter_t * ap_add_output_filter(const char *name, void *ctx, request_rec *r, conn_rec *c)
void ap_remove_output_filter(ap_filter_t *f)
@ AP_FTYPE_RESOURCE
#define OPT_INC_WITH_EXEC
Definition http_core.h:82
#define OPT_INCLUDES
Definition http_core.h:74
#define AP_MIN_BYTES_TO_WRITE
Definition http_core.h:131
int ap_allow_options(request_rec *r)
Definition core.c:771
#define APLOGNO(n)
Definition http_log.h:117
#define ap_log_rerror
Definition http_log.h:454
#define APLOG_ERR
Definition http_log.h:67
#define APLOG_MARK
Definition http_log.h:283
#define APLOG_WARNING
Definition http_log.h:68
#define APLOG_DEBUG
Definition http_log.h:71
#define APLOG_MODULE_INDEX
Definition http_log.h:168
const unsigned char * buf
Definition util_md5.h:50
void ap_set_last_modified(request_rec *r)
Definition protocol.c:2280
void ap_set_content_type_ex(request_rec *r, const char *ct, int trusted)
request_rec * ap_sub_req_lookup_uri(const char *new_uri, const request_rec *r, ap_filter_t *next_filter)
Definition request.c:2297
int ap_run_sub_req(request_rec *r)
Definition request.c:2528
request_rec * ap_sub_req_lookup_file(const char *new_file, const request_rec *r, ap_filter_t *next_filter)
Definition request.c:2433
void ap_hook_fixups(ap_HOOK_fixups_t *pf, const char *const *aszPre, const char *const *aszSucc, int nOrder)
Definition request.c:87
void ap_destroy_sub_req(request_rec *r)
Definition request.c:2547
request_rec * ap_sub_req_method_uri(const char *method, const char *new_uri, const request_rec *r, ap_filter_t *next_filter)
Definition request.c:2242
void ap_update_mtime(request_rec *r, apr_time_t dependency_mtime)
Definition request.c:2557
void ap_add_common_vars(request_rec *r)
void ap_add_cgi_vars(request_rec *r)
void * dummy
Definition http_vhost.h:62
void const char * arg
Definition http_vhost.h:63
#define APR_INCOMPLETE
Definition apr_errno.h:452
#define APR_STATUS_IS_EAGAIN(s)
Definition apr_errno.h:1272
apr_file_t * f
#define APR_BUCKET_IS_FLUSH(e)
#define APR_BUCKET_REMOVE(e)
#define APR_BUCKET_IS_METADATA(e)
#define APR_BRIGADE_PREPEND(a, b)
#define APR_BRIGADE_INSERT_TAIL(b, e)
#define apr_bucket_split(e, point)
#define APR_BUCKET_NEXT(e)
apr_bucket * e
#define APR_BRIGADE_EMPTY(b)
#define APR_BRIGADE_SENTINEL(b)
#define apr_bucket_delete(e)
#define APR_BUCKET_IS_EOS(e)
apr_brigade_flush void * ctx
#define apr_bucket_setaside(e, p)
#define APR_BRIGADE_FIRST(b)
#define APR_BUCKET_INSERT_BEFORE(a, b)
#define apr_bucket_read(e, str, len, block)
int apr_off_t * length
@ APR_BLOCK_READ
Definition apr_buckets.h:58
@ APR_NONBLOCK_READ
Definition apr_buckets.h:59
apr_pool_t const char apr_dbd_t const char ** error
Definition apr_dbd.h:143
apr_pool_t apr_dbd_t apr_dbd_results_t ** res
Definition apr_dbd.h:287
const char apr_ssize_t slen
Definition apr_encode.h:168
#define APR_HOOK_LAST
Definition apr_hooks.h:305
#define APR_HOOK_REALLY_FIRST
Definition apr_hooks.h:299
apr_memcache_server_t * server
#define APR_RETRIEVE_OPTIONAL_FN(name)
#define APR_OPTIONAL_FN_TYPE(name)
#define APR_REGISTER_OPTIONAL_FN(name)
const char * ap_expr_parse(apr_pool_t *pool, apr_pool_t *ptemp, ap_expr_info_t *info, const char *expr, ap_expr_lookup_fn_t *lookup_fn)
int ap_run_expr_lookup(ap_expr_lookup_parms *parms)
#define AP_EXPR_FLAG_RESTRICTED
Definition ap_expr.h:66
int ap_expr_exec_ctx(ap_expr_eval_ctx_t *ctx)
#define OR_LIMIT
#define OR_ALL
#define RSRC_CONF
#define OR_OPTIONS
#define HTTP_OK
Definition httpd.h:490
#define HTTP_BAD_REQUEST
Definition httpd.h:508
#define SSI_FLAG_CLEAR_PRINTING
Definition mod_include.h:64
#define SSI_FLAG_CLEAR_PRINT_COND
Definition mod_include.h:62
#define SSI_FLAG_PRINTING
Definition mod_include.h:56
#define SSI_FLAG_COND_TRUE
Definition mod_include.h:57
#define SSI_FLAG_SIZE_ABBREV
Definition mod_include.h:61
#define SSI_EXPAND_DROP_NAME
Definition mod_include.h:42
#define SSI_VALUE_RAW
Definition mod_include.h:36
#define SSI_CREATE_ERROR_BUCKET(ctx, f, bb)
Definition mod_include.h:48
#define SSI_VALUE_DECODED
Definition mod_include.h:35
#define SSI_FLAG_SIZE_IN_BYTES
Definition mod_include.h:58
apr_status_t() include_handler_fn_t(include_ctx_t *, ap_filter_t *, apr_bucket_brigade *)
#define SSI_FLAG_NO_EXEC
Definition mod_include.h:59
#define STANDARD20_MODULE_STUFF
#define apr_pool_join(a, b)
Definition apr_pools.h:800
int ap_cstr_casecmp(const char *s1, const char *s2)
Definition util.c:3542
char * ap_pbase64encode(apr_pool_t *p, char *string)
Definition util.c:2487
#define ap_escape_uri(ppool, path)
Definition httpd.h:1836
int ap_unescape_urlencoded(char *query)
Definition util.c:1977
char * ap_escape_shell_cmd(apr_pool_t *p, const char *s)
Definition util.c:1812
#define ap_strchr_c(s, c)
Definition httpd.h:2353
char * ap_pbase64decode(apr_pool_t *p, const char *bufcoded)
Definition util.c:2477
char * ap_escape_html2(apr_pool_t *p, const char *s, int toasc)
Definition util.c:2131
char * ap_ht_time(apr_pool_t *p, apr_time_t t, const char *fmt, int gmt)
Definition util.c:109
#define ap_escape_html(p, s)
Definition httpd.h:1860
void ap_str_tolower(char *s)
Definition util.c:2410
char * ap_escape_urlencoded(apr_pool_t *p, const char *s)
Definition util.c:2124
#define ap_assert(exp)
Definition httpd.h:2271
int ap_unescape_url(char *url)
Definition util.c:1939
ap_regex_t * ap_pregcomp(apr_pool_t *p, const char *pattern, int cflags)
Definition util.c:262
void ap_pregfree(apr_pool_t *p, ap_regex_t *reg)
Definition util.c:279
apr_size_t size
apr_uint32_t val
Definition apr_atomic.h:66
const char int apr_pool_t * pool
Definition apr_cstr.h:84
#define apr_isspace(c)
Definition apr_lib.h:225
#define apr_isalnum(c)
Definition apr_lib.h:203
#define apr_isdigit(c)
Definition apr_lib.h:209
#define apr_tolower(c)
Definition apr_lib.h:231
#define APR_SUCCESS
Definition apr_errno.h:225
int apr_status_t
Definition apr_errno.h:44
@ APR_NOFILE
const char * key
void * data
#define APR_UEXECUTE
#define APR_GEXECUTE
#define APR_FINFO_MIN
#define APR_FINFO_MTIME
#define APR_FINFO_GPROT
#define APR_FILEPATH_SECUREROOTTEST
#define APR_FILEPATH_NOTABSOLUTE
apr_array_header_t ** result
int strcasecmp(const char *a, const char *b)
const apr_hash_t * h
Definition apr_hash.h:97
apr_vformatter_buff_t const char * fmt
Definition apr_lib.h:175
apr_vformatter_buff_t * c
Definition apr_lib.h:175
apr_vformatter_buff_t const char va_list ap
Definition apr_lib.h:176
char const *const char const *const ** env
apr_interval_time_t t
apr_interval_time_t apr_pollcb_cb_t func
Definition apr_poll.h:422
apr_pool_t * b
Definition apr_pools.h:529
apr_pool_t * parent
Definition apr_pools.h:197
#define apr_pool_create(newpool, parent)
Definition apr_pools.h:322
#define apr_pcalloc(p, size)
Definition apr_pools.h:465
const char * sep
const char char ** last
const char * s
Definition apr_strings.h:95
const apr_array_header_t * arr
Definition apr_tables.h:187
apr_int32_t apr_int32_t apr_int32_t err
apr_int32_t in
apr_cmdtype_e cmd
int int status
apr_int64_t apr_time_t
Definition apr_time.h:45
Apache Configuration.
CORE HTTP Daemon.
Apache Logging library.
Command line options.
HTTP protocol handling.
Apache Request library.
HTTP Daemon routines.
apr_pool_t * p
Definition md_event.c:32
#define DEFAULT_TIME_FORMAT
#define DEBUG_DUMP_TREE(ctx, root)
static apr_status_t handle_echo(include_ctx_t *ctx, ap_filter_t *f, apr_bucket_brigade *bb)
static void ap_register_include_handler(char *tag, include_handler_fn_t *func)
static const char * get_include_var(const char *var, include_ctx_t *ctx)
static apr_status_t handle_printenv(include_ctx_t *ctx, ap_filter_t *f, apr_bucket_brigade *bb)
parse_state_t
@ PARSE_ARG_NAME
@ PARSE_DIRECTIVE_POSTTAIL
@ PARSE_ARG_VAL_ESC
@ PARSE_TAIL_SEQ
@ PARSE_DIRECTIVE_POSTNAME
@ PARSE_PRE_HEAD
@ PARSE_DIRECTIVE
@ PARSE_ARG_PREVAL
@ PARSE_ARG_EQ
@ PARSE_TAIL
@ PARSE_PRE_ARG
@ PARSE_ARG_VAL
@ PARSE_ARG
@ PARSE_ARG_POSTVAL
@ PARSE_HEAD
@ PARSE_ARG_POSTNAME
@ PARSE_DIRECTIVE_TAIL
@ PARSE_EXECUTE
#define DEBUG_DUMP_EVAL(ctx, node)
static void ap_ssi_get_tag_and_value(include_ctx_t *ctx, char **tag, char **tag_val, int dodecode)
struct arg_item arg_item_t
#define DEFAULT_UNDEFINED_ECHO
static APR_INLINE int re_check(include_ctx_t *ctx, const char *string, const char *rexp)
#define CREATE_NODE(ctx, name)
static const char * set_default_error_msg(cmd_parms *cmd, void *mconfig, const char *msg)
static apr_status_t send_parsed_content(ap_filter_t *f, apr_bucket_brigade *bb)
#define DEBUG_DUMP_COND(ctx, text)
static const char * include_expr_var_fn(ap_expr_eval_ctx_t *eval_ctx, const void *data, const char *arg)
static int parse_expr(include_ctx_t *ctx, const char *expr, int *was_error)
static apr_status_t handle_set(include_ctx_t *ctx, ap_filter_t *f, apr_bucket_brigade *bb)
static apr_OFN_ap_register_include_handler_t * ssi_pfn_register
static apr_size_t find_argument(include_ctx_t *ctx, const char *data, apr_size_t len, char ***store, apr_size_t **store_len)
static apr_status_t handle_comment(include_ctx_t *ctx, ap_filter_t *f, apr_bucket_brigade *bb)
static apr_size_t find_tail(include_ctx_t *ctx, const char *data, apr_size_t len)
static void add_include_vars(request_rec *r)
#define TYPE_TOKEN(token, ttype)
#define MERGE(b, o, n, val, unset)
static int includes_setup(ap_filter_t *f)
static apr_status_t includes_filter(ap_filter_t *f, apr_bucket_brigade *b)
static const char * set_default_end_tag(cmd_parms *cmd, void *mconfig, const char *tag)
static int include_expr_lookup(ap_expr_lookup_parms *parms)
static void * merge_includes_dir_config(apr_pool_t *p, void *basev, void *overridesv)
static apr_size_t find_arg_or_tail(include_ctx_t *ctx, const char *data, apr_size_t len)
static int include_post_config(apr_pool_t *p, apr_pool_t *plog, apr_pool_t *ptemp, server_rec *s)
static void * create_includes_server_config(apr_pool_t *p, server_rec *server)
static apr_size_t find_directive(include_ctx_t *ctx, const char *data, apr_size_t len, char ***store, apr_size_t **store_len)
static apr_status_t handle_if(include_ctx_t *ctx, ap_filter_t *f, apr_bucket_brigade *bb)
static const char * set_default_time_fmt(cmd_parms *cmd, void *mconfig, const char *fmt)
static int find_file(request_rec *r, const char *directive, const char *tag, char *tag_val, apr_finfo_t *finfo)
#define DEBUG_PRINTF(arg)
static int parse_ap_expr(include_ctx_t *ctx, const char *expr, int *was_error)
#define DEBUG_DUMP_UNMATCHED(ctx, unmatched)
static apr_size_t find_partial_start_sequence(include_ctx_t *ctx, const char *data, apr_size_t len, apr_size_t *release)
static apr_status_t handle_endif(include_ctx_t *ctx, ap_filter_t *f, apr_bucket_brigade *bb)
#define DEFAULT_END_SEQUENCE
static apr_status_t handle_config(include_ctx_t *ctx, ap_filter_t *f, apr_bucket_brigade *bb)
static apr_size_t bndm(bndm_t *t, const char *h, apr_size_t hl)
static apr_status_t handle_else(include_ctx_t *ctx, ap_filter_t *f, apr_bucket_brigade *bb)
static void register_hooks(apr_pool_t *p)
static const char lazy_eval_sentinel
static int get_ptoken(include_ctx_t *ctx, const char **parse, token_t *token, token_t *previous)
static const char * set_default_start_tag(cmd_parms *cmd, void *mconfig, const char *tag)
static void * create_includes_dir_config(apr_pool_t *p, char *dummy)
#define DEBUG_INIT(ctx, f, bb)
static void decodehtml(char *s)
static const char * set_xbithack(cmd_parms *cmd, void *mconfig, const char *arg)
#define RAW_ASCII_CHAR(ch)
Definition mod_include.c:50
#define MAXENTLEN
static const char * add_include_vars_lazy(request_rec *r, const char *var, const char *timefmt)
#define DEFAULT_ERROR_MSG
static bndm_t * bndm_compile(apr_pool_t *pool, const char *n, apr_size_t nl)
#define UNSET
static apr_status_t handle_elif(include_ctx_t *ctx, ap_filter_t *f, apr_bucket_brigade *bb)
#define DEFAULT_XBITHACK
static apr_status_t handle_include(include_ctx_t *ctx, ap_filter_t *f, apr_bucket_brigade *bb)
#define DEFAULT_START_SEQUENCE
struct result_item result_item_t
static apr_size_t find_start_sequence(include_ctx_t *ctx, const char *data, apr_size_t len)
static apr_hash_t * include_handlers
token_type_t
Definition mod_include.c:70
@ TOKEN_GE
Definition mod_include.c:81
@ TOKEN_NOT
Definition mod_include.c:75
@ TOKEN_RE
Definition mod_include.c:72
@ TOKEN_LE
Definition mod_include.c:82
@ TOKEN_ACCESS
Definition mod_include.c:85
@ TOKEN_LBRACE
Definition mod_include.c:79
@ TOKEN_AND
Definition mod_include.c:73
@ TOKEN_GROUP
Definition mod_include.c:80
@ TOKEN_NE
Definition mod_include.c:77
@ TOKEN_RBRACE
Definition mod_include.c:78
@ TOKEN_LT
Definition mod_include.c:84
@ TOKEN_GT
Definition mod_include.c:83
@ TOKEN_EQ
Definition mod_include.c:76
@ TOKEN_OR
Definition mod_include.c:74
@ TOKEN_STRING
Definition mod_include.c:71
static int include_fixup(request_rec *r)
struct parse_node parse_node_t
#define LAZY_VALUE
static apr_status_t handle_flastmod(include_ctx_t *ctx, ap_filter_t *f, apr_bucket_brigade *bb)
static const char * set_undefined_echo(cmd_parms *cmd, void *mconfig, const char *msg)
xbithack_t
@ XBITHACK_OFF
@ XBITHACK_ON
@ XBITHACK_FULL
@ XBITHACK_UNSET
static const command_rec includes_cmds[]
static char * ap_ssi_parse_string(include_ctx_t *ctx, const char *in, char *out, apr_size_t length, int leave_name)
#define DEBUG_DUMP_TOKEN(ctx, token)
static apr_status_t handle_fsize(include_ctx_t *ctx, ap_filter_t *f, apr_bucket_brigade *bb)
Server Side Include Filter Extension Module for Apache.
static apr_file_t * out
Definition mod_info.c:85
#define debug(stmt)
Definition mod_macro.c:43
static int parse(server_rec *, apr_pool_t *p, char *, int)
return NULL
Definition mod_so.c:359
int i
Definition mod_so.c:347
sconf
Definition mod_so.c:349
char * name
request_rec * r
Definition ap_expr.h:107
The representation of a filter chain.
apr_finfo_t * finfo
apr_off_t size
apr_pool_t * pool
apr_fileperms_t protection
apr_uid_t user
apr_time_t mtime
apr_int32_t valid
apr_pool_t * parent
Definition apr_pools.c:563
Definition apr_tables.h:81
char * val
Definition apr_tables.h:87
char * key
Definition apr_tables.h:83
char * value
apr_size_t name_len
struct arg_item * next
apr_size_t value_len
char * name
const char * source
ap_regmatch_t match[10]
const char * rexp
apr_size_t nsub
unsigned int x
apr_size_t pattern_len
const char * undefined_echo
const char * default_time_fmt
const char * default_error_msg
signed char lastmodified
xbithack_t xbithack
signed char legacy_expr
const char * default_end_tag
const char * default_start_tag
token_t token
struct parse_node * left
Definition mod_include.c:98
struct parse_node * parent
Definition mod_include.c:97
struct parse_node * right
Definition mod_include.c:99
A structure that represents the current request.
Definition httpd.h:845
char * uri
Definition httpd.h:1016
const char * content_type
Definition httpd.h:992
const char * handler
Definition httpd.h:994
apr_pool_t * pool
Definition httpd.h:847
char * filename
Definition httpd.h:1018
apr_time_t request_time
Definition httpd.h:886
conn_rec * connection
Definition httpd.h:849
apr_bucket_brigade * kept_body
Definition httpd.h:953
apr_finfo_t finfo
Definition httpd.h:1094
struct ap_conf_vector_t * request_config
Definition httpd.h:1049
request_rec * main
Definition httpd.h:860
apr_table_t * subprocess_env
Definition httpd.h:983
server_rec * server
Definition httpd.h:851
struct ap_conf_vector_t * per_dir_config
Definition httpd.h:1047
const char * method
Definition httpd.h:900
char * path_info
Definition httpd.h:1024
char * args
Definition httpd.h:1026
struct result_item * next
Definition mod_include.c:64
const char * string
Definition mod_include.c:66
apr_size_t len
Definition mod_include.c:65
A structure to store information for each virtual server.
Definition httpd.h:1322
struct ap_conf_vector_t * module_config
Definition httpd.h:1341
apr_size_t bytes_read
const char * expr_err
apr_size_t end_seq_len
parse_state_t state
apr_size_t parse_pos
const char * start_seq
bndm_t * start_seq_pat
apr_size_t directive_len
const char * end_seq
const char * expr_vary_this
arg_item_t * argv
backref_t * re
apr_bucket_brigade * tmp_bb
const char * undefined_echo
ap_expr_eval_ctx_t * expr_eval_ctx
apr_size_t undefined_echo_len
arg_item_t * current_arg
TAG_NAME name
Definition xmlparse.c:312
const char * value
Definition mod_include.c:90
token_type_t type
Definition mod_include.c:89
static void error_fmt(abts_case *tc, void *data)
Definition testfmt.c:131
Utilities for EBCDIC conversion.
#define var
#define regex
Apache filter library.
Apache script tools.
#define T(x)
Definition xmltchar.h:61