1 /* Licensed to the Apache Software Foundation (ASF) under one or more
2  * contributor license agreements.  See the NOTICE file distributed with
3  * this work for additional information regarding copyright ownership.
4  * The ASF licenses this file to You under the Apache License, Version 2.0
5  * (the "License"); you may not use this file except in compliance with
6  * the License.  You may obtain a copy of the License at
7  *
8  *     http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #include "apr.h"
18 #include "apr_strings.h"
19 #include "apr_thread_proc.h"
20 #include "apr_hash.h"
21 #include "apr_user.h"
22 #include "apr_lib.h"
23 #include "apr_optional.h"
24 
25 #define APR_WANT_STRFUNC
26 #define APR_WANT_MEMFUNC
27 #include "apr_want.h"
28 
29 #include "ap_config.h"
30 #include "util_filter.h"
31 #include "httpd.h"
32 #include "http_config.h"
33 #include "http_core.h"
34 #include "http_request.h"
35 #include "http_core.h"
36 #include "http_protocol.h"
37 #include "http_log.h"
38 #include "http_main.h"
39 #include "util_script.h"
40 #include "http_core.h"
41 #include "mod_include.h"
42 #include "ap_expr.h"
43 
44 /* helper for Latin1 <-> entity encoding */
45 #if APR_CHARSET_EBCDIC
46 #include "util_ebcdic.h"
47 #define RAW_ASCII_CHAR(ch)  apr_xlate_conv_byte(ap_hdrs_from_ascii, \
48                                                 (unsigned char)ch)
49 #else /* APR_CHARSET_EBCDIC */
50 #define RAW_ASCII_CHAR(ch)  (ch)
51 #endif /* !APR_CHARSET_EBCDIC */
52 
53 
54 /*
55  * +-------------------------------------------------------+
56  * |                                                       |
57  * |                 Types and Structures
58  * |                                                       |
59  * +-------------------------------------------------------+
60  */
61 
62 /* sll used for string expansion */
63 typedef struct result_item {
64     struct result_item *next;
65     apr_size_t len;
66     const char *string;
67 } result_item_t;
68 
69 /* conditional expression parser stuff */
70 typedef enum {
71     TOKEN_STRING,
72     TOKEN_RE,
73     TOKEN_AND,
74     TOKEN_OR,
75     TOKEN_NOT,
76     TOKEN_EQ,
77     TOKEN_NE,
78     TOKEN_RBRACE,
79     TOKEN_LBRACE,
80     TOKEN_GROUP,
81     TOKEN_GE,
82     TOKEN_LE,
83     TOKEN_GT,
84     TOKEN_LT,
85     TOKEN_ACCESS
86 } token_type_t;
87 
88 typedef struct {
89     token_type_t  type;
90     const char   *value;
91 #ifdef DEBUG_INCLUDE
92     const char   *s;
93 #endif
94 } token_t;
95 
96 typedef struct parse_node {
97     struct parse_node *parent;
98     struct parse_node *left;
99     struct parse_node *right;
100     token_t token;
101     int value;
102     int done;
103 #ifdef DEBUG_INCLUDE
104     int dump_done;
105 #endif
106 } parse_node_t;
107 
108 typedef enum {
109     XBITHACK_OFF,
110     XBITHACK_ON,
111     XBITHACK_FULL,
112     XBITHACK_UNSET
113 } xbithack_t;
114 
115 typedef struct {
116     const char *default_error_msg;
117     const char *default_time_fmt;
118     const char *undefined_echo;
119     xbithack_t  xbithack;
120     signed char lastmodified;
121     signed char etag;
122     signed char legacy_expr;
123 } include_dir_config;
124 
125 typedef struct {
126     const char *default_start_tag;
127     const char *default_end_tag;
128 } include_server_config;
129 
130 /* main parser states */
131 typedef enum {
132     PARSE_PRE_HEAD,
133     PARSE_HEAD,
134     PARSE_DIRECTIVE,
135     PARSE_DIRECTIVE_POSTNAME,
136     PARSE_DIRECTIVE_TAIL,
137     PARSE_DIRECTIVE_POSTTAIL,
138     PARSE_PRE_ARG,
139     PARSE_ARG,
140     PARSE_ARG_NAME,
141     PARSE_ARG_POSTNAME,
142     PARSE_ARG_EQ,
143     PARSE_ARG_PREVAL,
144     PARSE_ARG_VAL,
145     PARSE_ARG_VAL_ESC,
146     PARSE_ARG_POSTVAL,
147     PARSE_TAIL,
148     PARSE_TAIL_SEQ,
149     PARSE_EXECUTE
150 } parse_state_t;
151 
152 typedef struct arg_item {
153     struct arg_item  *next;
154     char             *name;
155     apr_size_t        name_len;
156     char             *value;
157     apr_size_t        value_len;
158 } arg_item_t;
159 
160 typedef struct {
161     const char *source;
162     const char *rexp;
163     apr_size_t  nsub;
164     ap_regmatch_t match[AP_MAX_REG_MATCH];
165     int have_match;
166 } backref_t;
167 
168 typedef struct {
169     unsigned int T[256];
170     unsigned int x;
171     apr_size_t pattern_len;
172 } bndm_t;
173 
174 struct ssi_internal_ctx {
175     parse_state_t state;
176     int           seen_eos;
177     int           error;
178     char          quote;         /* quote character value (or \0) */
179     apr_size_t    parse_pos;     /* parse position of partial matches */
180     apr_size_t    bytes_read;
181 
182     apr_bucket_brigade *tmp_bb;
183 
184     const char   *start_seq;
185     bndm_t       *start_seq_pat;
186     const char   *end_seq;
187     apr_size_t    end_seq_len;
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;
197     apr_size_t    undefined_echo_len;
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;
208         apr_bucket_brigade *bb;
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 
debug_printf(include_ctx_t * ctx,const char * fmt,...)236 static 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 
245     APR_BRIGADE_INSERT_TAIL(ctx->intern->debug.bb, apr_bucket_pool_create(
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 
debug_dump_tree(include_ctx_t * ctx,parse_node_t * root)276 static void debug_dump_tree(include_ctx_t *ctx, parse_node_t *root)
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 */
449 module AP_MODULE_DECLARE_DATA include_module;
450 
451 /* function handlers for include directives */
452 static apr_hash_t *include_handlers;
453 
454 /* forward declaration of handler registry */
455 static APR_OPTIONAL_FN_TYPE(ap_register_include_handler) *ssi_pfn_register;
456 
457 /* Sentinel value to store in subprocess_env for items that
458  * shouldn't be evaluated until/unless they're actually used
459  */
460 static 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 */
decodehtml(char * s)502 static 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 
add_include_vars(request_rec * r)592 static void add_include_vars(request_rec *r)
593 {
594     apr_table_t *e = r->subprocess_env;
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 
615         ap_unescape_url(arg_copy);
616         apr_table_setn(e, "QUERY_STRING_UNESCAPED",
617                   ap_escape_shell_cmd(r->pool, arg_copy));
618     }
619 }
620 
add_include_vars_lazy(request_rec * r,const char * var,const char * timefmt)621 static 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")) {
625         val = ap_ht_time(r->pool, r->request_time, timefmt, 0);
626     }
627     else if (!strcasecmp(var, "DATE_GMT")) {
628         val = ap_ht_time(r->pool, r->request_time, timefmt, 1);
629     }
630     else if (!strcasecmp(var, "LAST_MODIFIED")) {
631         val = ap_ht_time(r->pool, r->finfo.mtime, timefmt, 0);
632     }
633     else if (!strcasecmp(var, "USER_NAME")) {
634         if (apr_uid_name_get(&val, r->finfo.user, r->pool) != APR_SUCCESS) {
635             val = "<unknown>";
636         }
637     }
638     else {
639         val = NULL;
640     }
641 
642     if (val) {
643         apr_table_setn(r->subprocess_env, var, val);
644     }
645     return val;
646 }
647 
get_include_var(const char * var,include_ctx_t * ctx)648 static 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) {
662             ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r, APLOGNO(01329)
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) {
668             ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r, APLOGNO(01330)
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 {
684         val = apr_table_get(r->subprocess_env, var);
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 
include_expr_var_fn(ap_expr_eval_ctx_t * eval_ctx,const void * data,const char * arg)694 static const char *include_expr_var_fn(ap_expr_eval_ctx_t *eval_ctx,
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 
include_expr_lookup(ap_expr_lookup_parms * parms)714 static int include_expr_lookup(ap_expr_lookup_parms *parms)
715 {
716     switch (parms->type) {
717     case AP_EXPR_FUNC_STRING:
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     }
732     return ap_run_expr_lookup(parms);
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  */
ap_ssi_parse_string(include_ctx_t * ctx,const char * in,char * out,apr_size_t length,int leave_name)742 static char *ap_ssi_parse_string(include_ctx_t *ctx, const char *in, char *out,
743                                  apr_size_t length, int leave_name)
744 {
745     request_rec *r = ctx->r;
746     result_item_t *result = NULL, *current = NULL;
747     apr_size_t outlen = 0, inlen, span;
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) {
765             apr_cpystrn(out, in, length);
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 
re_check(include_ctx_t * ctx,const char * string,const char * rexp)963 static APR_INLINE int re_check(include_ctx_t *ctx, const char *string,
964                                const char *rexp)
965 {
966     ap_regex_t *compiled;
967     backref_t *re = ctx->intern->re;
968 
969     compiled = ap_pregcomp(ctx->dpool, rexp, AP_REG_EXTENDED);
970     if (!compiled) {
971         ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, ctx->r, APLOGNO(02667)
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;
983     re->have_match = !ap_regexec(compiled, string, AP_MAX_REG_MATCH,
984                                  re->match, 0);
985 
986     ap_pregfree(ctx->dpool, compiled);
987     return re->have_match;
988 }
989 
get_ptoken(include_ctx_t * ctx,const char ** parse,token_t * token,token_t * previous)990 static int get_ptoken(include_ctx_t *ctx, const char **parse, token_t *token, token_t *previous)
991 {
992     const char *p;
993     apr_size_t shift;
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 
parse_expr(include_ctx_t * ctx,const char * expr,int * was_error)1167 static 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 
1199             DEBUG_DUMP_UNMATCHED(ctx, was_unmatched);
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 */
1215                 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, error, expr,
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 */
1367         ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, error, expr, r->filename);
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,
1382                                     SSI_EXPAND_DROP_NAME);
1383             current->value = !!*current->token.value;
1384             break;
1385 
1386         case TOKEN_AND:
1387         case TOKEN_OR:
1388             if (!current->left || !current->right) {
1389                 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(01332)
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,
1401                                             NULL, 0, SSI_EXPAND_DROP_NAME);
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,
1425                                                 NULL, 0, SSI_EXPAND_DROP_NAME);
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)) {
1454                 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(01333)
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,
1462                                     SSI_EXPAND_DROP_NAME);
1463             current->right->token.value =
1464                 ap_ssi_parse_string(ctx, current->right->token.value, NULL, 0,
1465                                     SSI_EXPAND_DROP_NAME);
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) {
1489                 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(01334)
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,
1498                                     SSI_EXPAND_DROP_NAME);
1499             current->right->token.value =
1500                 ap_ssi_parse_string(ctx, current->right->token.value, NULL, 0,
1501                                     SSI_EXPAND_DROP_NAME);
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)) {
1537                 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(01335)
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,
1545                                     SSI_EXPAND_DROP_NAME);
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             }
1558             ap_destroy_sub_req(rr);
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 */
1576             ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, error, expr, r->filename);
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 */
parse_ap_expr(include_ctx_t * ctx,const char * expr,int * was_error)1590 static int parse_ap_expr(include_ctx_t *ctx, const char *expr, int *was_error)
1591 {
1592     ap_expr_info_t *expr_info = apr_pcalloc(ctx->pool, sizeof (*expr_info));
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;
1601     expr_info->flags = AP_EXPR_FLAG_RESTRICTED;
1602     err = ap_expr_parse(ctx->r->pool, ctx->r->pool, expr_info, expr,
1603                         include_expr_lookup);
1604     if (err) {
1605         ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, ctx->r, APLOGNO(01337)
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;
1639     ret = ap_expr_exec_ctx(eval_ctx);
1640     if (ret < 0) {
1641         ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, ctx->r, APLOGNO(01338)
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  */
ap_ssi_get_tag_and_value(include_ctx_t * ctx,char ** tag,char ** tag_val,int dodecode)1667 static void ap_ssi_get_tag_and_value(include_ctx_t *ctx, char **tag,
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) {
1683         decodehtml(*tag_val);
1684     }
1685 }
1686 
find_file(request_rec * r,const char * directive,const char * tag,char * tag_val,apr_finfo_t * finfo)1687 static 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;
1694     apr_status_t rv = APR_SUCCESS;
1695 
1696     if (!strcmp(tag, "file")) {
1697         char *newpath;
1698 
1699         /* be safe; only files in this directory or below allowed */
1700         rv = apr_filepath_merge(&newpath, NULL, tag_val,
1701                                 APR_FILEPATH_SECUREROOTTEST |
1702                                 APR_FILEPATH_NOTABSOLUTE, r->pool);
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. */
1711             rr = ap_sub_req_lookup_file(newpath, r, NULL);
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,
1716                     APR_FINFO_GPROT | APR_FINFO_MIN, rr->pool)) != APR_SUCCESS
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 */
1732             ap_log_rerror(APLOG_MARK, APLOG_ERR,
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. */
1743         rr = ap_sub_req_lookup_uri(tag_val, r, NULL);
1744 
1745         if (rr->status == HTTP_OK && rr->finfo.filetype != APR_NOFILE) {
1746             memcpy((char *) finfo, (const char *) &rr->finfo,
1747                    sizeof(rr->finfo));
1748             ap_destroy_sub_req(rr);
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);
1755             ap_destroy_sub_req(rr);
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  */
handle_comment(include_ctx_t * ctx,ap_filter_t * f,apr_bucket_brigade * bb)1769 static apr_status_t handle_comment(include_ctx_t *ctx, ap_filter_t *f,
1770                                    apr_bucket_brigade *bb)
1771 {
1772     return APR_SUCCESS;
1773 }
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  */
handle_include(include_ctx_t * ctx,ap_filter_t * f,apr_bucket_brigade * bb)1784 static apr_status_t handle_include(include_ctx_t *ctx, ap_filter_t *f,
1785                                    apr_bucket_brigade *bb)
1786 {
1787     request_rec *r = f->r;
1788     char *last_error;
1789 
1790     if (!ctx->argc) {
1791         ap_log_rerror(APLOG_MARK,
1792                       (ctx->flags & SSI_FLAG_PRINTING)
1793                           ? APLOG_ERR : APLOG_WARNING,
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) {
1804         SSI_CREATE_ERROR_BUCKET(ctx, f, bb);
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;
1815         apr_status_t rv = APR_SUCCESS;
1816         int status = 0;
1817 
1818         ap_ssi_get_tag_and_value(ctx, &tag, &tag_val, SSI_VALUE_DECODED);
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);
1827             SSI_CREATE_ERROR_BUCKET(ctx, f, bb);
1828             break;
1829         }
1830 
1831         parsed_string = ap_ssi_parse_string(ctx, tag_val, NULL, 0,
1832                                             SSI_EXPAND_DROP_NAME);
1833         if (tag[0] == 'f') {
1834             char *newpath;
1835 
1836             /* be safe; only files in this directory or below allowed */
1837             rv = apr_filepath_merge(&newpath, NULL, parsed_string,
1838                                     APR_FILEPATH_SECUREROOTTEST |
1839                                     APR_FILEPATH_NOTABSOLUTE, ctx->dpool);
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) {
1851                 rr = ap_sub_req_method_uri(r->method, parsed_string, r, f->next);
1852             }
1853             else {
1854                 rr = ap_sub_req_lookup_uri(parsed_string, r, f->next);
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 */
1887             ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r, error_fmt, tag_val,
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) {
1907         SSI_CREATE_ERROR_BUCKET(ctx, f, bb);
1908     }
1909 
1910     return APR_SUCCESS;
1911 }
1912 
1913 /*
1914  * <!--#echo [decoding="..."] [encoding="..."] var="..." [decoding="..."]
1915  *  [encoding="..."] var="..." ... -->
1916  */
handle_echo(include_ctx_t * ctx,ap_filter_t * f,apr_bucket_brigade * bb)1917 static apr_status_t handle_echo(include_ctx_t *ctx, ap_filter_t *f,
1918                                 apr_bucket_brigade *bb)
1919 {
1920     const char *encoding = "entity", *decoding = "none";
1921     request_rec *r = f->r;
1922     int error = 0;
1923 
1924     if (!ctx->argc) {
1925         ap_log_rerror(APLOG_MARK,
1926                       (ctx->flags & SSI_FLAG_PRINTING)
1927                           ? APLOG_ERR : APLOG_WARNING,
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) {
1938         SSI_CREATE_ERROR_BUCKET(ctx, f, bb);
1939         return APR_SUCCESS;
1940     }
1941 
1942     while (1) {
1943         char *tag = NULL;
1944         char *tag_val = NULL;
1945 
1946         ap_ssi_get_tag_and_value(ctx, &tag, &tag_val, SSI_VALUE_DECODED);
1947         if (!tag || !tag_val) {
1948             break;
1949         }
1950 
1951         if (!strcmp(tag, "var")) {
1952             const char *val;
1953             const char *echo_text = NULL;
1954             apr_size_t e_len;
1955 
1956             val = get_include_var(ap_ssi_parse_string(ctx, tag_val, NULL,
1957                                                       0, SSI_EXPAND_DROP_NAME),
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);
1975                         ap_unescape_url(buf);
1976                         echo_text = buf;
1977                     }
1978                     else if (!ap_cstr_casecmp(token, "urlencoded")) {
1979                         char *buf = apr_pstrdup(ctx->pool, echo_text);
1980                         ap_unescape_urlencoded(buf);
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")) {
1989                         echo_text = ap_pbase64decode(ctx->dpool, echo_text);
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);
1995                         SSI_CREATE_ERROR_BUCKET(ctx, f, bb);
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")) {
2010                         echo_text = ap_escape_uri(ctx->dpool, echo_text);
2011                     }
2012                     else if (!ap_cstr_casecmp(token, "urlencoded")) {
2013                         echo_text = ap_escape_urlencoded(ctx->dpool, echo_text);
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);
2027                         SSI_CREATE_ERROR_BUCKET(ctx, f, bb);
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 
2045             APR_BRIGADE_INSERT_TAIL(bb, apr_bucket_pool_create(
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);
2058             SSI_CREATE_ERROR_BUCKET(ctx, f, bb);
2059             break;
2060         }
2061     }
2062 
2063     return APR_SUCCESS;
2064 }
2065 
2066 /*
2067  * <!--#config [timefmt="..."] [sizefmt="..."] [errmsg="..."]
2068  *             [echomsg="..."] -->
2069  */
handle_config(include_ctx_t * ctx,ap_filter_t * f,apr_bucket_brigade * bb)2070 static apr_status_t handle_config(include_ctx_t *ctx, ap_filter_t *f,
2071                                   apr_bucket_brigade *bb)
2072 {
2073     request_rec *r = f->r;
2074     apr_table_t *env = r->subprocess_env;
2075 
2076     if (!ctx->argc) {
2077         ap_log_rerror(APLOG_MARK,
2078                       (ctx->flags & SSI_FLAG_PRINTING)
2079                           ? APLOG_ERR : APLOG_WARNING,
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) {
2090         SSI_CREATE_ERROR_BUCKET(ctx, f, bb);
2091         return APR_SUCCESS;
2092     }
2093 
2094     while (1) {
2095         char *tag     = NULL;
2096         char *tag_val = NULL;
2097 
2098         ap_ssi_get_tag_and_value(ctx, &tag, &tag_val, SSI_VALUE_RAW);
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,
2105                                                  SSI_EXPAND_DROP_NAME);
2106         }
2107         else if (!strcmp(tag, "echomsg")) {
2108             ctx->intern->undefined_echo =
2109                 ap_ssi_parse_string(ctx, tag_val, NULL, 0,SSI_EXPAND_DROP_NAME);
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,
2116                                                 SSI_EXPAND_DROP_NAME);
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",
2123                            ap_ht_time(r->pool, r->finfo.mtime,
2124                            ctx->time_str, 0));
2125         }
2126         else if (!strcmp(tag, "sizefmt")) {
2127             char *parsed_string;
2128 
2129             parsed_string = ap_ssi_parse_string(ctx, tag_val, NULL, 0,
2130                                                 SSI_EXPAND_DROP_NAME);
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);
2141                 SSI_CREATE_ERROR_BUCKET(ctx, f, bb);
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);
2148             SSI_CREATE_ERROR_BUCKET(ctx, f, bb);
2149             break;
2150         }
2151     }
2152 
2153     return APR_SUCCESS;
2154 }
2155 
2156 /*
2157  * <!--#fsize virtual|file="..." [virtual|file="..."] ... -->
2158  */
handle_fsize(include_ctx_t * ctx,ap_filter_t * f,apr_bucket_brigade * bb)2159 static apr_status_t handle_fsize(include_ctx_t *ctx, ap_filter_t *f,
2160                                  apr_bucket_brigade *bb)
2161 {
2162     request_rec *r = f->r;
2163 
2164     if (!ctx->argc) {
2165         ap_log_rerror(APLOG_MARK,
2166                       (ctx->flags & SSI_FLAG_PRINTING)
2167                           ? APLOG_ERR : APLOG_WARNING,
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) {
2178         SSI_CREATE_ERROR_BUCKET(ctx, f, bb);
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 
2188         ap_ssi_get_tag_and_value(ctx, &tag, &tag_val, SSI_VALUE_DECODED);
2189         if (!tag || !tag_val) {
2190             break;
2191         }
2192 
2193         parsed_string = ap_ssi_parse_string(ctx, tag_val, NULL, 0,
2194                                             SSI_EXPAND_DROP_NAME);
2195 
2196         if (!find_file(r, "fsize", tag, parsed_string, &finfo)) {
2197             char *buf;
2198             apr_size_t len;
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 
2232             APR_BRIGADE_INSERT_TAIL(bb, apr_bucket_pool_create(buf, len,
2233                                     ctx->pool, f->c->bucket_alloc));
2234         }
2235         else {
2236             SSI_CREATE_ERROR_BUCKET(ctx, f, bb);
2237             break;
2238         }
2239     }
2240 
2241     return APR_SUCCESS;
2242 }
2243 
2244 /*
2245  * <!--#flastmod virtual|file="..." [virtual|file="..."] ... -->
2246  */
handle_flastmod(include_ctx_t * ctx,ap_filter_t * f,apr_bucket_brigade * bb)2247 static apr_status_t handle_flastmod(include_ctx_t *ctx, ap_filter_t *f,
2248                                     apr_bucket_brigade *bb)
2249 {
2250     request_rec *r = f->r;
2251 
2252     if (!ctx->argc) {
2253         ap_log_rerror(APLOG_MARK,
2254                       (ctx->flags & SSI_FLAG_PRINTING)
2255                           ? APLOG_ERR : APLOG_WARNING,
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) {
2266         SSI_CREATE_ERROR_BUCKET(ctx, f, bb);
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 
2276         ap_ssi_get_tag_and_value(ctx, &tag, &tag_val, SSI_VALUE_DECODED);
2277         if (!tag || !tag_val) {
2278             break;
2279         }
2280 
2281         parsed_string = ap_ssi_parse_string(ctx, tag_val, NULL, 0,
2282                                             SSI_EXPAND_DROP_NAME);
2283 
2284         if (!find_file(r, "flastmod", tag, parsed_string, &finfo)) {
2285             char *t_val;
2286             apr_size_t t_len;
2287 
2288             t_val = ap_ht_time(ctx->pool, finfo.mtime, ctx->time_str, 0);
2289             t_len = strlen(t_val);
2290 
2291             APR_BRIGADE_INSERT_TAIL(bb, apr_bucket_pool_create(t_val, t_len,
2292                                     ctx->pool, f->c->bucket_alloc));
2293         }
2294         else {
2295             SSI_CREATE_ERROR_BUCKET(ctx, f, bb);
2296             break;
2297         }
2298     }
2299 
2300     return APR_SUCCESS;
2301 }
2302 
2303 /*
2304  * <!--#if expr="..." -->
2305  */
handle_if(include_ctx_t * ctx,ap_filter_t * f,apr_bucket_brigade * bb)2306 static apr_status_t handle_if(include_ctx_t *ctx, ap_filter_t *f,
2307                               apr_bucket_brigade *bb)
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) {
2315         ap_log_rerror(APLOG_MARK,
2316                       (ctx->flags & SSI_FLAG_PRINTING)
2317                           ? APLOG_ERR : APLOG_WARNING,
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) {
2331         SSI_CREATE_ERROR_BUCKET(ctx, f, bb);
2332         return APR_SUCCESS;
2333     }
2334 
2335     ap_ssi_get_tag_and_value(ctx, &tag, &expr, SSI_VALUE_RAW);
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);
2340         SSI_CREATE_ERROR_BUCKET(ctx, f, bb);
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);
2347         SSI_CREATE_ERROR_BUCKET(ctx, f, bb);
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
2356         expr_ret = parse_ap_expr(ctx, expr, &was_error);
2357 
2358     if (was_error) {
2359         SSI_CREATE_ERROR_BUCKET(ctx, f, bb);
2360         return APR_SUCCESS;
2361     }
2362 
2363     if (expr_ret) {
2364         ctx->flags |= (SSI_FLAG_PRINTING | SSI_FLAG_COND_TRUE);
2365     }
2366     else {
2367         ctx->flags &= SSI_FLAG_CLEAR_PRINT_COND;
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  */
handle_elif(include_ctx_t * ctx,ap_filter_t * f,apr_bucket_brigade * bb)2380 static apr_status_t handle_elif(include_ctx_t *ctx, ap_filter_t *f,
2381                                 apr_bucket_brigade *bb)
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) {
2389         ap_log_rerror(APLOG_MARK,
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) {
2403         SSI_CREATE_ERROR_BUCKET(ctx, f, bb);
2404         return APR_SUCCESS;
2405     }
2406 
2407     ap_ssi_get_tag_and_value(ctx, &tag, &expr, SSI_VALUE_RAW);
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);
2412         SSI_CREATE_ERROR_BUCKET(ctx, f, bb);
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);
2419         SSI_CREATE_ERROR_BUCKET(ctx, f, bb);
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
2434         expr_ret = parse_ap_expr(ctx, expr, &was_error);
2435 
2436     if (was_error) {
2437         SSI_CREATE_ERROR_BUCKET(ctx, f, bb);
2438         return APR_SUCCESS;
2439     }
2440 
2441     if (expr_ret) {
2442         ctx->flags |= (SSI_FLAG_PRINTING | SSI_FLAG_COND_TRUE);
2443     }
2444     else {
2445         ctx->flags &= SSI_FLAG_CLEAR_PRINT_COND;
2446     }
2447 
2448     DEBUG_DUMP_COND(ctx, " elif");
2449 
2450     return APR_SUCCESS;
2451 }
2452 
2453 /*
2454  * <!--#else -->
2455  */
handle_else(include_ctx_t * ctx,ap_filter_t * f,apr_bucket_brigade * bb)2456 static apr_status_t handle_else(include_ctx_t *ctx, ap_filter_t *f,
2457                                 apr_bucket_brigade *bb)
2458 {
2459     request_rec *r = f->r;
2460 
2461     if (ctx->argc) {
2462         ap_log_rerror(APLOG_MARK,
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) {
2475             SSI_CREATE_ERROR_BUCKET(ctx, f, bb);
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 {
2487         ctx->flags |= (SSI_FLAG_PRINTING | SSI_FLAG_COND_TRUE);
2488     }
2489 
2490     return APR_SUCCESS;
2491 }
2492 
2493 /*
2494  * <!--#endif -->
2495  */
handle_endif(include_ctx_t * ctx,ap_filter_t * f,apr_bucket_brigade * bb)2496 static apr_status_t handle_endif(include_ctx_t *ctx, ap_filter_t *f,
2497                                  apr_bucket_brigade *bb)
2498 {
2499     request_rec *r = f->r;
2500 
2501     if (ctx->argc) {
2502         ap_log_rerror(APLOG_MARK,
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) {
2515         SSI_CREATE_ERROR_BUCKET(ctx, f, bb);
2516         return APR_SUCCESS;
2517     }
2518 
2519     DEBUG_DUMP_COND(ctx, "endif");
2520 
2521     ctx->flags |= (SSI_FLAG_PRINTING | SSI_FLAG_COND_TRUE);
2522 
2523     return APR_SUCCESS;
2524 }
2525 
2526 /*
2527  * <!--#set var="..." value="..." ... -->
2528  */
handle_set(include_ctx_t * ctx,ap_filter_t * f,apr_bucket_brigade * bb)2529 static apr_status_t handle_set(include_ctx_t *ctx, ap_filter_t *f,
2530                                apr_bucket_brigade *bb)
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) {
2540         ap_log_rerror(APLOG_MARK,
2541                       (ctx->flags & SSI_FLAG_PRINTING)
2542                           ? APLOG_ERR : APLOG_WARNING,
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) {
2553         SSI_CREATE_ERROR_BUCKET(ctx, f, bb);
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 
2569         ap_ssi_get_tag_and_value(ctx, &tag, &tag_val, SSI_VALUE_RAW);
2570 
2571         if (!tag || !tag_val) {
2572             break;
2573         }
2574 
2575         if (!strcmp(tag, "var")) {
2576             decodehtml(tag_val);
2577             var = ap_ssi_parse_string(ctx, tag_val, NULL, 0,
2578                                       SSI_EXPAND_DROP_NAME);
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);
2593                 SSI_CREATE_ERROR_BUCKET(ctx, f, bb);
2594                 break;
2595             }
2596 
2597             parsed_string = ap_ssi_parse_string(ctx, tag_val, NULL, 0,
2598                                                 SSI_EXPAND_DROP_NAME);
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);
2613                         ap_unescape_url(buf);
2614                         parsed_string = buf;
2615                     }
2616                     else if (!ap_cstr_casecmp(token, "urlencoded")) {
2617                         char *buf = apr_pstrdup(ctx->pool, parsed_string);
2618                         ap_unescape_urlencoded(buf);
2619                         parsed_string = buf;
2620                     }
2621                     else if (!ap_cstr_casecmp(token, "entity")) {
2622                         char *buf = apr_pstrdup(ctx->pool, parsed_string);
2623                         decodehtml(buf);
2624                         parsed_string = buf;
2625                     }
2626                     else if (!ap_cstr_casecmp(token, "base64")) {
2627                         parsed_string = ap_pbase64decode(ctx->dpool, parsed_string);
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);
2633                         SSI_CREATE_ERROR_BUCKET(ctx, f, bb);
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")) {
2648                         parsed_string = ap_escape_uri(ctx->dpool, parsed_string);
2649                     }
2650                     else if (!ap_cstr_casecmp(token, "urlencoded")) {
2651                         parsed_string = ap_escape_urlencoded(ctx->dpool, parsed_string);
2652                     }
2653                     else if (!ap_cstr_casecmp(token, "entity")) {
2654                         parsed_string = ap_escape_html2(ctx->dpool, parsed_string, 0);
2655                     }
2656                     else if (!ap_cstr_casecmp(token, "base64")) {
2657                         char *buf;
2658                         buf = ap_pbase64encode(ctx->dpool, (char *)parsed_string);
2659                         parsed_string = buf;
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);
2665                         SSI_CREATE_ERROR_BUCKET(ctx, f, bb);
2666                         error = 1;
2667                         break;
2668                     }
2669                     token = apr_strtok(NULL, ", \t", &last);
2670                 }
2671 
2672             }
2673 
2674             if (error) {
2675                 break;
2676             }
2677 
2678             apr_table_setn(r->subprocess_env, apr_pstrdup(p, var),
2679                            apr_pstrdup(p, parsed_string));
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);
2684             SSI_CREATE_ERROR_BUCKET(ctx, f, bb);
2685             break;
2686         }
2687     }
2688 
2689     return APR_SUCCESS;
2690 }
2691 
2692 /*
2693  * <!--#printenv -->
2694  */
handle_printenv(include_ctx_t * ctx,ap_filter_t * f,apr_bucket_brigade * bb)2695 static apr_status_t handle_printenv(include_ctx_t *ctx, ap_filter_t *f,
2696                                     apr_bucket_brigade *bb)
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) {
2704         ap_log_rerror(APLOG_MARK,
2705                       (ctx->flags & SSI_FLAG_PRINTING)
2706                           ? APLOG_ERR : APLOG_WARNING,
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) {
2717         SSI_CREATE_ERROR_BUCKET(ctx, f, bb);
2718         return APR_SUCCESS;
2719     }
2720 
2721     arr = apr_table_elts(r->subprocess_env);
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);
2734         val_text = ap_escape_html(ctx->dpool, val_text);
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. */
bndm_compile(apr_pool_t * pool,const char * n,apr_size_t nl)2765 static 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  */
bndm(bndm_t * t,const char * h,apr_size_t hl)2792 static 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  */
find_start_sequence(include_ctx_t * ctx,const char * data,apr_size_t len)2838 static apr_size_t find_start_sequence(include_ctx_t *ctx, const char *data,
2839                                       apr_size_t len)
2840 {
2841     struct ssi_internal_ctx *intern = ctx->intern;
2842     apr_size_t slen = intern->start_seq_pat->pattern_len;
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 {
2870         while (p < ep && *p != *intern->start_seq) {
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;
2881             while (p < ep && *p == intern->start_seq[pos]) {
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  */
find_partial_start_sequence(include_ctx_t * ctx,const char * data,apr_size_t len,apr_size_t * release)2911 static apr_size_t find_partial_start_sequence(include_ctx_t *ctx,
2912                                               const char *data,
2913                                               apr_size_t len,
2914                                               apr_size_t *release)
2915 {
2916     struct ssi_internal_ctx *intern = ctx->intern;
2917     apr_size_t pos, spos = 0;
2918     apr_size_t slen = intern->start_seq_pat->pattern_len;
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          */
2954         if (spos < intern->parse_pos) {
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;
2975                     while (t < pos && *p == intern->start_seq[t]) {
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  */
find_directive(include_ctx_t * ctx,const char * data,apr_size_t len,char *** store,apr_size_t ** store_len)3007 static apr_size_t find_directive(include_ctx_t *ctx, const char *data,
3008                                  apr_size_t len, char ***store,
3009                                  apr_size_t **store_len)
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 */
3032             intern->state = PARSE_DIRECTIVE_POSTNAME;
3033             *store = &intern->directive;
3034             *store_len = &intern->directive_len;
3035         }
3036 
3037         break;
3038 
3039     case PARSE_DIRECTIVE_TAIL:
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) {
3050             intern->state = PARSE_DIRECTIVE_POSTTAIL;
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 
3066     case PARSE_DIRECTIVE_POSTTAIL:
3067         intern->state = PARSE_EXECUTE;
3068         intern->directive_len -= intern->end_seq_len;
3069         /* continue immediately with the next state */
3070 
3071     case PARSE_DIRECTIVE_POSTNAME:
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  */
find_arg_or_tail(include_ctx_t * ctx,const char * data,apr_size_t len)3107 static apr_size_t find_arg_or_tail(include_ctx_t *ctx, const char *data,
3108                                    apr_size_t len)
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  */
find_tail(include_ctx_t * ctx,const char * data,apr_size_t len)3131 static apr_size_t find_tail(include_ctx_t *ctx, const char *data,
3132                             apr_size_t len)
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  */
find_argument(include_ctx_t * ctx,const char * data,apr_size_t len,char *** store,apr_size_t ** store_len)3173 static apr_size_t find_argument(include_ctx_t *ctx, const char *data,
3174                                 apr_size_t len, char ***store,
3175                                 apr_size_t **store_len)
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 {
3255             ap_str_tolower(intern->current_arg->name);
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  */
send_parsed_content(ap_filter_t * f,apr_bucket_brigade * bb)3388 static apr_status_t send_parsed_content(ap_filter_t *f, apr_bucket_brigade *bb)
3389 {
3390     include_ctx_t *ctx = f->ctx;
3391     struct ssi_internal_ctx *intern = ctx->intern;
3392     request_rec *r = f->r;
3393     apr_bucket *b = APR_BRIGADE_FIRST(bb);
3394     apr_bucket_brigade *pass_bb;
3395     apr_status_t rv = APR_SUCCESS;
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;
3424         apr_size_t *store_len = NULL;
3425 
3426         /* handle meta buckets before reading any data */
3427         if (APR_BUCKET_IS_METADATA(b)) {
3428             newb = APR_BUCKET_NEXT(b);
3429 
3430             APR_BUCKET_REMOVE(b);
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) {
3453                     APR_BUCKET_INSERT_BEFORE(newb, b);
3454                 }
3455                 else {
3456                     break; /* END OF STREAM */
3457                 }
3458             }
3459             else {
3460                 APR_BRIGADE_INSERT_TAIL(pass_bb, b);
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 ||
3473             intern->bytes_read > AP_MIN_BYTES_TO_WRITE) {
3474 
3475             if (!APR_BRIGADE_EMPTY(pass_bb)) {
3476                 rv = ap_pass_brigade(f->next, pass_bb);
3477                 if (rv != APR_SUCCESS) {
3478                     apr_brigade_destroy(pass_bb);
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) {
3491                 rv = apr_bucket_read(b, &data, &len, APR_NONBLOCK_READ);
3492                 if (APR_STATUS_IS_EAGAIN(rv)) {
3493                     ctx->flush_now = 1;
3494                     continue;
3495                 }
3496             }
3497 
3498             if (!len || rv != APR_SUCCESS) {
3499                 rv = apr_bucket_read(b, &data, &len, APR_BLOCK_READ);
3500             }
3501 
3502             if (rv != APR_SUCCESS) {
3503                 apr_brigade_destroy(pass_bb);
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 
3529             newb = APR_BUCKET_NEXT(b);
3530             if (ctx->flags & SSI_FLAG_PRINTING) {
3531                 APR_BUCKET_REMOVE(b);
3532                 APR_BRIGADE_INSERT_TAIL(pass_bb, b);
3533             }
3534             else {
3535                 apr_bucket_delete(b);
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 */
3541                     apr_bucket_split(newb, intern->start_seq_pat->pattern_len);
3542                     ctx->flush_now = 1; /* pass pre-tag stuff */
3543                 }
3544 
3545                 b = APR_BUCKET_NEXT(newb);
3546                 apr_bucket_delete(newb);
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);
3564                 APR_BRIGADE_INSERT_TAIL(pass_bb, newb);
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                 }
3573                 newb = APR_BUCKET_NEXT(b);
3574                 apr_bucket_delete(b);
3575                 b = newb;
3576             }
3577 
3578             break;
3579 
3580         /* we're currently grabbing the directive name */
3581         case PARSE_DIRECTIVE:
3582         case PARSE_DIRECTIVE_POSTNAME:
3583         case PARSE_DIRECTIVE_TAIL:
3584         case PARSE_DIRECTIVE_POSTTAIL:
3585             index = find_directive(ctx, data, len, &store, &store_len);
3586 
3587             if (index) {
3588                 apr_bucket_split(b, index);
3589                 newb = APR_BUCKET_NEXT(b);
3590             }
3591 
3592             if (store) {
3593                 if (index) {
3594                     APR_BUCKET_REMOVE(b);
3595                     apr_bucket_setaside(b, r->pool);
3596                     APR_BRIGADE_INSERT_TAIL(intern->tmp_bb, b);
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) {
3608                 apr_bucket_delete(b);
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                 }
3622                 newb = APR_BUCKET_NEXT(b);
3623                 apr_bucket_delete(b);
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);
3642                 newb = APR_BUCKET_NEXT(b);
3643             }
3644 
3645             if (store) {
3646                 if (index) {
3647                     APR_BUCKET_REMOVE(b);
3648                     apr_bucket_setaside(b, r->pool);
3649                     APR_BRIGADE_INSERT_TAIL(intern->tmp_bb, b);
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) {
3661                 apr_bucket_delete(b);
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);
3675                 newb = APR_BUCKET_NEXT(b);
3676                 apr_bucket_delete(b);
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 */
3687                 newb = APR_BUCKET_NEXT(b);
3688                 APR_BUCKET_REMOVE(b);
3689                 apr_bucket_setaside(b, r->pool);
3690                 APR_BRIGADE_INSERT_TAIL(intern->tmp_bb, b);
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) {
3704                     SSI_CREATE_ERROR_BUCKET(ctx, f, pass_bb);
3705                     intern->error = 0;
3706                 }
3707             }
3708             else {
3709                 include_handler_fn_t *handle_func;
3710 
3711                 handle_func =
3712                     (include_handler_fn_t *)apr_hash_get(include_handlers, intern->directive,
3713                                                          intern->directive_len);
3714 
3715                 if (handle_func) {
3716                     DEBUG_INIT(ctx, f, pass_bb);
3717                     rv = handle_func(ctx, f, pass_bb);
3718                     if (rv != APR_SUCCESS) {
3719                         apr_brigade_destroy(pass_bb);
3720                         return rv;
3721                     }
3722                 }
3723                 else {
3724                     ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(01371)
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) {
3730                         SSI_CREATE_ERROR_BUCKET(ctx, f, pass_bb);
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 
3754                 APR_BRIGADE_INSERT_TAIL(pass_bb,
3755                                         apr_bucket_pool_create(to_release,
3756                                         intern->parse_pos, ctx->pool,
3757                                         f->c->bucket_alloc));
3758             }
3759         }
3760         else if (PARSE_PRE_HEAD != intern->state) {
3761             ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(01372)
3762                           "SSI directive was not properly finished at the end "
3763                           "of parsed document %s", r->filename);
3764             if (ctx->flags & SSI_FLAG_PRINTING) {
3765                 SSI_CREATE_ERROR_BUCKET(ctx, f, pass_bb);
3766             }
3767         }
3768 
3769         if (!(ctx->flags & SSI_FLAG_PRINTING)) {
3770             ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r, APLOGNO(01373)
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 */
3780         APR_BRIGADE_INSERT_TAIL(pass_bb, b);
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;
3789         apr_brigade_destroy(pass_bb);
3790     }
3791     return rv;
3792 }
3793 
3794 
3795 /*
3796  * +-------------------------------------------------------+
3797  * |                                                       |
3798  * |                     Runtime Hooks
3799  * |                                                       |
3800  * +-------------------------------------------------------+
3801  */
3802 
includes_setup(ap_filter_t * f)3803 static int includes_setup(ap_filter_t *f)
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 
includes_filter(ap_filter_t * f,apr_bucket_brigade * b)3830 static apr_status_t includes_filter(ap_filter_t *f, apr_bucket_brigade *b)
3831 {
3832     request_rec *r = f->r;
3833     request_rec *parent;
3834     include_dir_config *conf = ap_get_module_config(r->per_dir_config,
3835                                                     &include_module);
3836 
3837     include_server_config *sconf= ap_get_module_config(r->server->module_config,
3838                                                        &include_module);
3839 
3840     if (!(ap_allow_options(r) & OPT_INCLUDES)) {
3841         ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r, APLOGNO(01374)
3842                       "mod_include: Options +Includes (or IncludesNoExec) "
3843                       "wasn't set, INCLUDES filter removed: %s", r->uri);
3844         ap_remove_output_filter(f);
3845         return ap_pass_brigade(f->next, b);
3846     }
3847 
3848     if (!f->ctx) {
3849         struct ssi_internal_ctx *intern;
3850         include_ctx_t *ctx;
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;
3864         ctx->flags = (SSI_FLAG_PRINTING | SSI_FLAG_COND_TRUE);
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 :
3877                          DEFAULT_ERROR_MSG;
3878         ctx->time_str = conf->default_time_fmt ? conf->default_time_fmt :
3879                         DEFAULT_TIME_FORMAT;
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 :
3886                                  DEFAULT_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          */
3897         r->subprocess_env = r->main->subprocess_env;
3898         apr_pool_join(r->main->pool, r->pool);
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 */
3904         ap_add_common_vars(r);
3905         ap_add_cgi_vars(r);
3906         add_include_vars(r);
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          */
3936         if (r->finfo.valid & APR_FINFO_MTIME
3937                 && !apr_table_get(f->r->headers_out, "Last-Modified")) {
3938             ap_update_mtime(r, r->finfo.mtime);
3939             ap_set_last_modified(r);
3940         }
3941 
3942     }
3943 
3944     /* Assure the platform supports Group protections */
3945     else if (((conf->xbithack == XBITHACK_FULL ||
3946                (conf->xbithack == XBITHACK_UNSET &&
3947                 DEFAULT_XBITHACK == XBITHACK_FULL))
3948         && (r->finfo.valid & APR_FINFO_GPROT)
3949         && (r->finfo.protection & APR_GEXECUTE))) {
3950         ap_update_mtime(r, r->finfo.mtime);
3951         ap_set_last_modified(r);
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);
3962         ap_unescape_url(arg_copy);
3963         apr_table_setn(r->subprocess_env, "QUERY_STRING_UNESCAPED",
3964                   ap_escape_shell_cmd(r->pool, arg_copy));
3965     }
3966 
3967     return send_parsed_content(f, b);
3968 }
3969 
include_fixup(request_rec * r)3970 static int include_fixup(request_rec *r)
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(r, "text/html");
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     {
3987         include_dir_config *conf = ap_get_module_config(r->per_dir_config,
3988                                                         &include_module);
3989 
3990         if (conf->xbithack == XBITHACK_OFF ||
3991             (DEFAULT_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 
create_includes_dir_config(apr_pool_t * p,char * dummy)4023 static void *create_includes_dir_config(apr_pool_t *p, char *dummy)
4024 {
4025     include_dir_config *result = apr_pcalloc(p, sizeof(include_dir_config));
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
merge_includes_dir_config(apr_pool_t * p,void * basev,void * overridesv)4036 static void *merge_includes_dir_config(apr_pool_t *p, void *basev, void *overridesv)
4037 {
4038     include_dir_config *base = (include_dir_config *)basev,
4039                        *over = (include_dir_config *)overridesv,
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);
4043     MERGE(base, over, new, undefined_echo,    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 
create_includes_server_config(apr_pool_t * p,server_rec * server)4051 static void *create_includes_server_config(apr_pool_t *p, server_rec *server)
4052 {
4053     include_server_config *result;
4054 
4055     result = apr_palloc(p, sizeof(include_server_config));
4056     result->default_end_tag    = DEFAULT_END_SEQUENCE;
4057     result->default_start_tag  = DEFAULT_START_SEQUENCE;
4058 
4059     return result;
4060 }
4061 
set_xbithack(cmd_parms * cmd,void * mconfig,const char * arg)4062 static const char *set_xbithack(cmd_parms *cmd, void *mconfig, const char *arg)
4063 {
4064     include_dir_config *conf = mconfig;
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 
set_default_start_tag(cmd_parms * cmd,void * mconfig,const char * tag)4082 static const char *set_default_start_tag(cmd_parms *cmd, void *mconfig,
4083                                          const char *tag)
4084 {
4085     include_server_config *conf;
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 
set_default_end_tag(cmd_parms * cmd,void * mconfig,const char * tag)4102 static const char *set_default_end_tag(cmd_parms *cmd, void *mconfig,
4103                                        const char *tag)
4104 {
4105     include_server_config *conf;
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 
set_undefined_echo(cmd_parms * cmd,void * mconfig,const char * msg)4122 static const char *set_undefined_echo(cmd_parms *cmd, void *mconfig,
4123                                       const char *msg)
4124 {
4125     include_dir_config *conf = mconfig;
4126     conf->undefined_echo = msg;
4127 
4128     return NULL;
4129 }
4130 
set_default_error_msg(cmd_parms * cmd,void * mconfig,const char * msg)4131 static const char *set_default_error_msg(cmd_parms *cmd, void *mconfig,
4132                                          const char *msg)
4133 {
4134     include_dir_config *conf = mconfig;
4135     conf->default_error_msg = msg;
4136 
4137     return NULL;
4138 }
4139 
set_default_time_fmt(cmd_parms * cmd,void * mconfig,const char * fmt)4140 static const char *set_default_time_fmt(cmd_parms *cmd, void *mconfig,
4141                                         const char *fmt)
4142 {
4143     include_dir_config *conf = mconfig;
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 
include_post_config(apr_pool_t * p,apr_pool_t * plog,apr_pool_t * ptemp,server_rec * s)4158 static int include_post_config(apr_pool_t *p, apr_pool_t *plog,
4159                                apr_pool_t *ptemp, server_rec *s)
4160 {
4161     include_handlers = apr_hash_make(p);
4162 
4163     ssi_pfn_register = APR_RETRIEVE_OPTIONAL_FN(ap_register_include_handler);
4164 
4165     if (ssi_pfn_register) {
4166         ssi_pfn_register("if", handle_if);
4167         ssi_pfn_register("set", handle_set);
4168         ssi_pfn_register("else", handle_else);
4169         ssi_pfn_register("elif", handle_elif);
4170         ssi_pfn_register("echo", handle_echo);
4171         ssi_pfn_register("endif", handle_endif);
4172         ssi_pfn_register("fsize", handle_fsize);
4173         ssi_pfn_register("config", handle_config);
4174         ssi_pfn_register("comment", handle_comment);
4175         ssi_pfn_register("include", handle_include);
4176         ssi_pfn_register("flastmod", handle_flastmod);
4177         ssi_pfn_register("printenv", handle_printenv);
4178     }
4179 
4180     return OK;
4181 }
4182 
4183 static const command_rec includes_cmds[] =
4184 {
4185     AP_INIT_TAKE1("XBitHack", set_xbithack, NULL, OR_OPTIONS,
4186                   "Off, On, or Full"),
4187     AP_INIT_TAKE1("SSIErrorMsg", set_default_error_msg, NULL, OR_ALL,
4188                   "a string"),
4189     AP_INIT_TAKE1("SSITimeFormat", set_default_time_fmt, NULL, OR_ALL,
4190                   "a strftime(3) formatted string"),
4191     AP_INIT_TAKE1("SSIStartTag", set_default_start_tag, NULL, RSRC_CONF,
4192                   "SSI Start String Tag"),
4193     AP_INIT_TAKE1("SSIEndTag", set_default_end_tag, NULL, RSRC_CONF,
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,
4198                   (void *)APR_OFFSETOF(include_dir_config, legacy_expr),
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'"),
4206     AP_INIT_FLAG("SSIEtag", ap_set_flag_slot_char,
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 
ap_register_include_handler(char * tag,include_handler_fn_t * func)4213 static void ap_register_include_handler(char *tag, include_handler_fn_t *func)
4214 {
4215     apr_hash_set(include_handlers, tag, strlen(tag), (const void *)func);
4216 }
4217 
register_hooks(apr_pool_t * p)4218 static void register_hooks(apr_pool_t *p)
4219 {
4220     APR_REGISTER_OPTIONAL_FN(ap_ssi_get_tag_and_value);
4221     APR_REGISTER_OPTIONAL_FN(ap_ssi_parse_string);
4222     APR_REGISTER_OPTIONAL_FN(ap_register_include_handler);
4223     ap_hook_post_config(include_post_config, NULL, NULL, APR_HOOK_REALLY_FIRST);
4224     ap_hook_fixups(include_fixup, NULL, NULL, APR_HOOK_LAST);
4225     ap_register_output_filter("INCLUDES", includes_filter, includes_setup,
4226                               AP_FTYPE_RESOURCE);
4227 }
4228 
4229 AP_DECLARE_MODULE(include) =
4230 {
4231     STANDARD20_MODULE_STUFF,
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 };
4239