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

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

Parent Directory Parent Directory | Revision Log Revision Log


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