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