/[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 - (hide 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 fielding 420983 /* 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 fielding 83751 *
8 nd 102525 * http://www.apache.org/licenses/LICENSE-2.0
9 fielding 83751 *
10 nd 102525 * 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 fielding 83751 */
16    
17 gstein 87080 #include "apr.h"
18     #include "apr_strings.h"
19     #include "apr_thread_proc.h"
20 rbb 87241 #include "apr_hash.h"
21 wrowe 87903 #include "apr_user.h"
22 gstein 88060 #include "apr_lib.h"
23 gstein 88061 #include "apr_optional.h"
24 gstein 87080
25 gstein 88061 #define APR_WANT_STRFUNC
26 nd 101048 #define APR_WANT_MEMFUNC
27 gstein 88061 #include "apr_want.h"
28    
29 rbb 84497 #include "ap_config.h"
30 rbb 86211 #include "util_filter.h"
31 fielding 83751 #include "httpd.h"
32     #include "http_config.h"
33 rbb 88417 #include "http_core.h"
34 fielding 83751 #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 rbb 85449 #include "http_core.h"
41 rbb 87067 #include "mod_include.h"
42 nd 101046
43 nd 101048 /* helper for Latin1 <-> entity encoding */
44 nd 101046 #if APR_CHARSET_EBCDIC
45 trawick 85222 #include "util_ebcdic.h"
46 nd 101046 #define RAW_ASCII_CHAR(ch) apr_xlate_conv_byte(ap_hdrs_from_ascii, \
47     (unsigned char)ch)
48 nd 101049 #else /* APR_CHARSET_EBCDIC */
49 nd 101046 #define RAW_ASCII_CHAR(ch) (ch)
50 nd 101049 #endif /* !APR_CHARSET_EBCDIC */
51 fielding 83751
52 nd 101049
53     /*
54     * +-------------------------------------------------------+
55     * | |
56     * | Types and Structures
57     * | |
58     * +-------------------------------------------------------+
59 jerenkrantz 90872 */
60    
61 nd 101070 /* 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 nd 101078 /* conditional expression parser stuff */
69 nd 101052 typedef enum {
70 nd 101078 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 nd 101115 TOKEN_GROUP,
80 nd 101078 TOKEN_GE,
81     TOKEN_LE,
82     TOKEN_GT,
83 minfrin 571872 TOKEN_LT,
84     TOKEN_ACCESS
85 nd 101078 } 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 nd 101087 #ifdef DEBUG_INCLUDE
103     int dump_done;
104     #endif
105 nd 101078 } parse_node_t;
106    
107     typedef enum {
108 nd 101052 XBITHACK_OFF,
109     XBITHACK_ON,
110     XBITHACK_FULL
111     } xbithack_t;
112 jerenkrantz 90872
113 nd 101046 typedef struct {
114 nd 101052 const char *default_error_msg;
115     const char *default_time_fmt;
116 nd 101129 const char *undefined_echo;
117 nd 101052 xbithack_t xbithack;
118 minfrin 571872 const int accessenable;
119 jerenkrantz 90872 } include_dir_config;
120    
121 ianh 92660 typedef struct {
122 nd 101052 const char *default_start_tag;
123     const char *default_end_tag;
124 ianh 92660 } include_server_config;
125    
126 nd 101036 /* 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 nd 101048 typedef struct arg_item {
149     struct arg_item *next;
150     char *name;
151     apr_size_t name_len;
152     char *value;
153 nd 101052 apr_size_t value_len;
154 nd 101048 } arg_item_t;
155 nd 101036
156 nd 101073 typedef struct {
157     const char *source;
158     const char *rexp;
159     apr_size_t nsub;
160 jorton 153384 ap_regmatch_t match[AP_MAX_REG_MATCH];
161 nd 101073 } backref_t;
162    
163 nd 101078 typedef struct {
164     unsigned int T[256];
165     unsigned int x;
166     apr_size_t pattern_len;
167     } bndm_t;
168    
169 nd 101046 struct ssi_internal_ctx {
170 nd 101036 parse_state_t state;
171     int seen_eos;
172     int error;
173     char quote; /* quote character value (or \0) */
174 nd 101052 apr_size_t parse_pos; /* parse position of partial matches */
175 nd 101046 apr_size_t bytes_read;
176 nd 101036
177     apr_bucket_brigade *tmp_bb;
178    
179 nd 101046 request_rec *r;
180     const char *start_seq;
181     bndm_t *start_seq_pat;
182     const char *end_seq;
183 nd 101036 apr_size_t end_seq_len;
184     char *directive; /* name of the current directive */
185 nd 101046 apr_size_t directive_len; /* length of the current directive name */
186 nd 101036
187 nd 101048 arg_item_t *current_arg; /* currently parsed argument */
188     arg_item_t *argv; /* all arguments */
189 nd 101036
190 nd 101073 backref_t *re; /* NULL if there wasn't a regex yet */
191 nd 101078
192 nd 101129 const char *undefined_echo;
193     apr_size_t undefined_echo_len;
194    
195 nd 101078 #ifdef DEBUG_INCLUDE
196     struct {
197     ap_filter_t *f;
198     apr_bucket_brigade *bb;
199     } debug;
200     #endif
201 nd 101046 };
202 nd 101036
203 nd 101049
204     /*
205     * +-------------------------------------------------------+
206     * | |
207 nd 101078 * | Debugging Utilities
208     * | |
209     * +-------------------------------------------------------+
210     */
211    
212     #ifdef DEBUG_INCLUDE
213 nd 101087
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 nd 101078 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 nd 101087 #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 nd 101115 case TOKEN_GROUP:
292 nd 101095 case TOKEN_RBRACE:
293     case TOKEN_LBRACE:
294 nd 101087 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 nd 101095 default:
310 nd 101087 if (!current->dump_done) {
311 nd 101095 debug_printf(ctx, "%s%s\n", is, current->token.s);
312 nd 101087 is = apr_pstrcat(ctx->dpool, is, " ", NULL);
313     current->dump_done = 1;
314     }
315    
316 nd 101095 DUMP__CHILD(ctx, is, current, left)
317 nd 101087 DUMP__CHILD(ctx, is, current, right)
318    
319 nd 101095 if ((!current->left || current->left->dump_done) &&
320     (!current->right || current->right->dump_done)) {
321    
322 nd 101087 is = apr_pstrmemdup(ctx->dpool, is, strlen(is) - 4);
323 nd 101095 if (current->left) current->left->dump_done = 0;
324 nd 101087 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 nd 101078 #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 nd 101095 DEBUG_PRINTF(((ctx), " Found: %s (%s)\n", d__t->s, d__t->value)); \
356 nd 101078 } \
357     else { \
358 nd 101095 DEBUG_PRINTF((ctx, " Found: %s\n", d__t->s)); \
359 nd 101078 } \
360     } while(0)
361    
362 nd 101107 #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 nd 101081 #define DEBUG_DUMP_UNMATCHED(ctx, unmatched) do { \
400     if (unmatched) { \
401     DEBUG_PRINTF(((ctx), " Unmatched %c\n", (char)(unmatched))); \
402     } \
403 nd 101078 } 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 nd 101087 #define DEBUG_DUMP_TREE(ctx, root) debug_dump_tree(ctx, root)
410    
411 nd 101078 #else /* DEBUG_INCLUDE */
412    
413     #define TYPE_TOKEN(token, ttype) (token)->type = ttype
414 nd 101087
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 nd 101078 #define DEBUG_INIT(ctx, f, bb)
422     #define DEBUG_PRINTF(arg)
423     #define DEBUG_DUMP_TOKEN(ctx, token)
424 nd 101107 #define DEBUG_DUMP_EVAL(ctx, node)
425 nd 101078 #define DEBUG_DUMP_UNMATCHED(ctx, unmatched)
426     #define DEBUG_DUMP_COND(ctx, text)
427 nd 101087 #define DEBUG_DUMP_TREE(ctx, root)
428 nd 101078
429     #endif /* !DEBUG_INCLUDE */
430    
431    
432     /*
433     * +-------------------------------------------------------+
434     * | |
435 nd 101049 * | 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 nd 101048 #define DEFAULT_START_SEQUENCE "<!--#"
457     #define DEFAULT_END_SEQUENCE "-->"
458 nd 101046 #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 nd 101036
462 jerenkrantz 90872 #ifdef XBITHACK
463 nd 101052 #define DEFAULT_XBITHACK XBITHACK_FULL
464 jerenkrantz 90872 #else
465 nd 101052 #define DEFAULT_XBITHACK XBITHACK_OFF
466 jerenkrantz 90872 #endif
467    
468 fielding 83751
469 nd 101049 /*
470     * +-------------------------------------------------------+
471     * | |
472     * | Environment/Expansion Functions
473     * | |
474     * +-------------------------------------------------------+
475 jerenkrantz 90872 */
476    
477 fielding 83751 /*
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 brianp 92284 char *p;
495 fielding 83751 const char *ents;
496     static const char * const entlist[MAXENTLEN + 1] =
497     {
498 nd 101074 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 fielding 83751 };
516    
517 brianp 92284 /* 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 fielding 83751 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 wrowe 86008 for (j = 2, val = 0; j < i && apr_isdigit(s[j]); j++) {
544 fielding 83751 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 nd 101052 static void add_include_vars(request_rec *r, const char *timefmt)
582 fielding 83751 {
583 nd 101049 apr_table_t *e = r->subprocess_env;
584     char *t;
585 nd 101036
586 nd 101049 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 nd 101036 }
593 nd 101049 apr_table_setn(e, "USER_NAME", LAZY_VALUE);
594 nd 101461 if (r->filename && (t = strrchr(r->filename, '/'))) {
595 nd 101049 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 nd 101036
603 nd 101049 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 nd 101036
609 nd 101049 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 trawick 101154 if (apr_uid_name_get(&val, r->finfo.user, r->pool) != APR_SUCCESS) {
632 nd 101049 val = "<unknown>";
633     }
634     }
635     else {
636     val = NULL;
637     }
638 nd 101036
639 nd 101049 if (val) {
640     apr_table_setn(r->subprocess_env, var, val);
641 nd 101036 }
642 nd 101049 return val;
643     }
644 nd 101036
645 nd 101070 static const char *get_include_var(const char *var, include_ctx_t *ctx)
646 nd 101049 {
647     const char *val;
648 nd 101073 request_rec *r = ctx->intern->r;
649    
650 nd 101049 if (apr_isdigit(*var) && !var[1]) {
651 nd 101100 apr_size_t idx = *var - '0';
652 nd 101073 backref_t *re = ctx->intern->re;
653    
654 nd 101049 /* 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 nd 101073 if (!re) {
659 fielding 101234 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 nd 101049 return NULL;
663     }
664     else {
665 striker 101556 if (re->nsub < idx || idx >= AP_MAX_REG_MATCH) {
666 nd 101073 ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r,
667 fielding 101234 "regex capture $%" APR_SIZE_T_FMT
668     " is out of range (last regex was: '%s') in %s",
669     idx, re->rexp, r->filename);
670 nd 101049 return NULL;
671     }
672 nd 101073
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 nd 101049 }
680     }
681     else {
682 nd 101073 val = apr_table_get(r->subprocess_env, var);
683 nd 101049
684 nd 101073 if (val == LAZY_VALUE) {
685     val = add_include_vars_lazy(r, var);
686     }
687 nd 101049 }
688 nd 101073
689 nd 101049 return val;
690 nd 101036 }
691    
692 fielding 83751 /*
693     * Do variable substitution on strings
694 nd 101070 *
695 brianp 92747 * (Note: If out==NULL, this function allocs a buffer for the resulting
696 nd 101070 * string from ctx->pool. The return value is always the parsed string)
697 fielding 83751 */
698 nd 101069 static char *ap_ssi_parse_string(include_ctx_t *ctx, const char *in, char *out,
699 brianp 92747 apr_size_t length, int leave_name)
700 fielding 83751 {
701 nd 101069 request_rec *r = ctx->intern->r;
702 nd 101070 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 fielding 83751
707 nd 101070 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 brianp 92747 }
723 nd 101070 else {
724     ret = apr_pstrmemdup(ctx->pool, in, (length && length <= inlen)
725     ? length - 1 : inlen);
726     }
727    
728     return ret;
729 brianp 92747 }
730 nd 101070
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 brianp 92747 else {
741 nd 101070 current = result = apr_palloc(ctx->dpool, sizeof(*result));
742     current->next = NULL;
743     current->string = in;
744     current->len = span;
745     outlen = span;
746 brianp 92747 }
747    
748 nd 101070 /* loop for specials */
749     do {
750     if ((out && out >= eout) || (length && outlen >= length)) {
751     break;
752     }
753 fielding 83751
754 nd 101070 /* 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 brianp 92747 }
791 nd 101070
792     if (p < ep) {
793     key = apr_pstrmemdup(ctx->dpool, p, ep - p);
794     newp = ep + 1;
795 brianp 92747 }
796 nd 101070 p -= 2;
797 jerenkrantz 90874 }
798 fielding 83751 else {
799 nd 101070 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 fielding 83751 }
810    
811 nd 101070 /* empty name results in a copy of '$' in the output string */
812     if (!key) {
813     if (out) {
814     *out++ = *p++;
815 jerenkrantz 90874 }
816     else {
817 nd 101070 current->len = 1;
818     current->string = p++;
819     ++outlen;
820 jerenkrantz 90874 }
821 nd 101070 }
822     else {
823     const char *val = get_include_var(key, ctx);
824     apr_size_t len = 0;
825 fielding 83751
826 nd 101070 if (val) {
827     len = strlen(val);
828 jerenkrantz 90874 }
829 nd 101070 else if (leave_name) {
830     val = p;
831     len = ep - p;
832 jerenkrantz 90874 }
833 nd 101070
834     if (val && len) {
835     if (out) {
836     memcpy(out, val, (out+len <= eout) ? len : (eout-out));
837     out += len;
838 brianp 92747 }
839 nd 101070 else {
840     current->len = len;
841     current->string = val;
842     outlen += len;
843 brianp 92747 }
844     }
845 nd 101070
846     p = newp;
847 jerenkrantz 90874 }
848 nd 101070 }
849    
850     if ((out && out >= eout) || (length && outlen >= length)) {
851 fielding 83751 break;
852     }
853 nd 101070
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 fielding 83751 }
885 nd 101070 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 fielding 83751 }
909    
910    
911 nd 101049 /*
912     * +-------------------------------------------------------+
913     * | |
914     * | Conditional Expression Parser
915     * | |
916     * +-------------------------------------------------------+
917 fielding 83751 */
918    
919 nd 101078 static APR_INLINE int re_check(include_ctx_t *ctx, const char *string,
920     const char *rexp)
921 fielding 83751 {
922 jorton 153384 ap_regex_t *compiled;
923 nd 101073 backref_t *re = ctx->intern->re;
924     int rc;
925 fielding 83751
926 jorton 153384 compiled = ap_pregcomp(ctx->dpool, rexp, AP_REG_EXTENDED);
927 nd 101073 if (!compiled) {
928     ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, ctx->intern->r, "unable to "
929     "compile pattern \"%s\"", rexp);
930 fielding 83751 return -1;
931     }
932 nd 101073
933     if (!re) {
934     re = ctx->intern->re = apr_palloc(ctx->pool, sizeof(*re));
935 wrowe 92676 }
936 nd 101073
937 nd 101078 re->source = apr_pstrdup(ctx->pool, string);
938     re->rexp = apr_pstrdup(ctx->pool, rexp);
939 nd 101073 re->nsub = compiled->re_nsub;
940 striker 101556 rc = !ap_regexec(compiled, string, AP_MAX_REG_MATCH, re->match, 0);
941 nd 101073
942     ap_pregfree(ctx->dpool, compiled);
943     return rc;
944 fielding 83751 }
945    
946 minfrin 571872 static int get_ptoken(include_ctx_t *ctx, const char **parse, token_t *token, token_t *previous)
947 fielding 83751 {
948 nd 101078 const char *p;
949     apr_size_t shift;
950     int unmatched;
951 fielding 83751
952 brianp 92838 token->value = NULL;
953    
954 nd 101078 if (!*parse) {
955     return 0;
956     }
957    
958 fielding 83751 /* Skip leading white space */
959 nd 101078 while (apr_isspace(**parse)) {
960     ++*parse;
961 fielding 83751 }
962 nd 101078
963     if (!**parse) {
964     *parse = NULL;
965     return 0;
966 fielding 83751 }
967    
968 nd 101078 TYPE_TOKEN(token, TOKEN_STRING); /* the default type */
969     p = *parse;
970     unmatched = 0;
971    
972     switch (*(*parse)++) {
973 fielding 83751 case '(':
974 nd 101078 TYPE_TOKEN(token, TOKEN_LBRACE);
975     return 0;
976 fielding 83751 case ')':
977 nd 101078 TYPE_TOKEN(token, TOKEN_RBRACE);
978     return 0;
979 fielding 83751 case '=':
980 nd 101120 if (**parse == '=') ++*parse;
981 nd 101078 TYPE_TOKEN(token, TOKEN_EQ);
982     return 0;
983 fielding 83751 case '!':
984 nd 101078 if (**parse == '=') {
985     TYPE_TOKEN(token, TOKEN_NE);
986     ++*parse;
987     return 0;
988 fielding 83751 }
989 nd 101078 TYPE_TOKEN(token, TOKEN_NOT);
990     return 0;
991 fielding 83751 case '\'':
992 nd 101078 unmatched = '\'';
993 fielding 83751 break;
994 wrowe 92676 case '/':
995 minfrin 571872 /* if last token was ACCESS, this token is STRING */
996     if (previous != NULL && TOKEN_ACCESS == previous->type) {
997     break;
998     }
999 nd 101078 TYPE_TOKEN(token, TOKEN_RE);
1000     unmatched = '/';
1001 wrowe 92676 break;
1002 fielding 83751 case '|':
1003 nd 101078 if (**parse == '|') {
1004     TYPE_TOKEN(token, TOKEN_OR);
1005     ++*parse;
1006     return 0;
1007 fielding 83751 }
1008     break;
1009     case '&':
1010 nd 101078 if (**parse == '&') {
1011     TYPE_TOKEN(token, TOKEN_AND);
1012     ++*parse;
1013     return 0;
1014 fielding 83751 }
1015     break;
1016     case '>':
1017 nd 101078 if (**parse == '=') {
1018     TYPE_TOKEN(token, TOKEN_GE);
1019     ++*parse;
1020     return 0;
1021 fielding 83751 }
1022 nd 101078 TYPE_TOKEN(token, TOKEN_GT);
1023     return 0;
1024 fielding 83751 case '<':
1025 nd 101078 if (**parse == '=') {
1026     TYPE_TOKEN(token, TOKEN_LE);
1027     ++*parse;
1028     return 0;
1029 fielding 83751 }
1030 nd 101078 TYPE_TOKEN(token, TOKEN_LT);
1031     return 0;
1032 minfrin 571872 case '-':
1033     if (**parse == 'A' && (ctx->accessenable)) {
1034     TYPE_TOKEN(token, TOKEN_ACCESS);
1035     ++*parse;
1036     return 0;
1037     }
1038     break;
1039 fielding 83751 }
1040    
1041 nd 101093 /* It's a string or regex token
1042     * Now search for the next token, which finishes this string
1043 fielding 83751 */
1044 nd 101078 shift = 0;
1045 nd 101093 p = *parse = token->value = unmatched ? *parse : p;
1046    
1047 nd 101078 for (; **parse; p = ++*parse) {
1048     if (**parse == '\\') {
1049     if (!*(++*parse)) {
1050     p = *parse;
1051     break;
1052 fielding 83751 }
1053 nd 101078
1054     ++shift;
1055 fielding 83751 }
1056 rbb 87067 else {
1057 nd 101078 if (unmatched) {
1058     if (**parse == unmatched) {
1059     unmatched = 0;
1060     ++*parse;
1061     break;
1062 rbb 87067 }
1063 nd 101078 } else if (apr_isspace(**parse)) {
1064     break;
1065 fielding 83751 }
1066 rbb 87067 else {
1067 nd 101078 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 fielding 83751 }
1086 nd 101078
1087     if (found) {
1088     break;
1089 fielding 83751 }
1090     }
1091     }
1092     }
1093 rbb 87067
1094 nd 101078 if (unmatched) {
1095 minfrin 571872 token->value = apr_pstrdup(ctx->dpool, "");
1096 fielding 83751 }
1097 nd 101078 else {
1098     apr_size_t len = p - token->value - shift;
1099 minfrin 571872 char *c = apr_palloc(ctx->dpool, len + 1);
1100 wrowe 92676
1101 nd 101078 p = token->value;
1102     token->value = c;
1103 fielding 83751
1104 nd 101078 while (shift--) {
1105     const char *e = ap_strchr_c(p, '\\');
1106 fielding 83751
1107 nd 101078 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 fielding 83751 {
1125 nd 101078 parse_node_t *new, *root = NULL, *current = NULL;
1126     request_rec *r = ctx->intern->r;
1127 minfrin 571872 request_rec *rr = NULL;
1128 nd 101118 const char *error = "Invalid expression \"%s\" in file %s";
1129 nd 101078 const char *parse = expr;
1130 nd 101116 int was_unmatched = 0;
1131 nd 101097 unsigned regex = 0;
1132    
1133 nd 101078 *was_error = 0;
1134 fielding 83751
1135 nd 101078 if (!parse) {
1136     return 0;
1137 fielding 83751 }
1138    
1139     /* Create Parse Tree */
1140     while (1) {
1141 nd 101107 /* uncomment this to see how the tree a built:
1142     *
1143     * DEBUG_DUMP_TREE(ctx, root);
1144     */
1145 nd 101087 CREATE_NODE(ctx, new);
1146 nd 101078
1147 minfrin 571872 was_unmatched = get_ptoken(ctx, &parse, &new->token,
1148     (current != NULL ? &current->token : NULL));
1149 nd 101078 if (!parse) {
1150 fielding 83751 break;
1151     }
1152    
1153 nd 101078 DEBUG_DUMP_UNMATCHED(ctx, was_unmatched);
1154     DEBUG_DUMP_TOKEN(ctx, &new->token);
1155    
1156 nd 101102 if (!current) {
1157     switch (new->token.type) {
1158     case TOKEN_STRING:
1159     case TOKEN_NOT:
1160 minfrin 571872 case TOKEN_ACCESS:
1161 nd 101102 case TOKEN_LBRACE:
1162 fielding 83751 root = current = new;
1163 nd 101102 continue;
1164    
1165     default:
1166 nd 101116 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, error, expr,
1167     r->filename);
1168 nd 101102 *was_error = 1;
1169 nd 101116 return 0;
1170 fielding 83751 }
1171 nd 101102 }
1172 nd 101078
1173 nd 101102 switch (new->token.type) {
1174     case TOKEN_STRING:
1175 fielding 83751 switch (current->token.type) {
1176 nd 101078 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 nd 101116 continue;
1182 nd 101078
1183 nd 101101 case TOKEN_RE:
1184 nd 101115 case TOKEN_RBRACE:
1185     case TOKEN_GROUP:
1186 nd 101116 break;
1187 nd 101101
1188     default:
1189     new->parent = current;
1190     current = current->right = new;
1191 nd 101116 continue;
1192 fielding 83751 }
1193     break;
1194    
1195 nd 101078 case TOKEN_RE:
1196 wrowe 92676 switch (current->token.type) {
1197 nd 101078 case TOKEN_EQ:
1198     case TOKEN_NE:
1199 wrowe 92676 new->parent = current;
1200     current = current->right = new;
1201 nd 101097 ++regex;
1202 nd 101116 continue;
1203 nd 101078
1204 wrowe 92676 default:
1205 nd 101116 break;
1206 wrowe 92676 }
1207     break;
1208    
1209 nd 101078 case TOKEN_AND:
1210     case TOKEN_OR:
1211 nd 101119 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 fielding 83751 break;
1229 nd 101119 }
1230 nd 101078
1231 nd 101119 if (!current) {
1232     new->left = root;
1233     root->parent = new;
1234     current = root = new;
1235 nd 101101 continue;
1236 fielding 83751 }
1237 jim 332306
1238 fielding 83751 new->left = current->right;
1239 nd 101088 new->left->parent = new;
1240 fielding 83751 new->parent = current;
1241 nd 101119 current = current->right = new;
1242     continue;
1243    
1244     default:
1245     break;
1246 fielding 83751 }
1247 nd 101119 break;
1248 fielding 83751
1249 nd 101078 case TOKEN_EQ:
1250     case TOKEN_NE:
1251     case TOKEN_GE:
1252     case TOKEN_GT:
1253     case TOKEN_LE:
1254     case TOKEN_LT:
1255 nd 101113 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 fielding 83751 continue;
1263 nd 101113 }
1264 nd 101078
1265 nd 101113 switch (current->token.type) {
1266 nd 101078 case TOKEN_LBRACE:
1267     case TOKEN_AND:
1268     case TOKEN_OR:
1269 nd 101113 new->left = current->right;
1270     new->left->parent = new;
1271     new->parent = current;
1272     current = current->right = new;
1273     continue;
1274 nd 101078
1275 fielding 83751 default:
1276 nd 101113 break;
1277 fielding 83751 }
1278     }
1279 nd 101116 break;
1280 nd 101078
1281     case TOKEN_RBRACE:
1282 nd 101111 while (current && current->token.type != TOKEN_LBRACE) {
1283 fielding 83751 current = current->parent;
1284     }
1285 nd 101111
1286 nd 101116 if (current) {
1287     TYPE_TOKEN(&current->token, TOKEN_GROUP);
1288     continue;
1289 fielding 83751 }
1290 nd 101111
1291 nd 101116 error = "Unmatched ')' in \"%s\" in file %s";
1292 fielding 83751 break;
1293    
1294 nd 101103 case TOKEN_NOT:
1295 minfrin 571872 case TOKEN_ACCESS:
1296 nd 101078 case TOKEN_LBRACE:
1297 nd 101091 switch (current->token.type) {
1298     case TOKEN_STRING:
1299     case TOKEN_RE:
1300 nd 101115 case TOKEN_RBRACE:
1301     case TOKEN_GROUP:
1302 nd 101116 break;
1303 nd 101078
1304 nd 101091 default:
1305 nd 101116 current->right = new;
1306     new->parent = current;
1307     current = new;
1308     continue;
1309 fielding 83751 }
1310     break;
1311 nd 101115
1312     default:
1313     break;
1314 fielding 83751 }
1315 nd 101116
1316     ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, error, expr, r->filename);
1317     *was_error = 1;
1318     return 0;
1319 fielding 83751 }
1320    
1321 nd 101107 DEBUG_DUMP_TREE(ctx, root);
1322    
1323 fielding 83751 /* Evaluate Parse Tree */
1324     current = root;
1325 nd 101116 error = NULL;
1326 nd 101078 while (current) {
1327 fielding 83751 switch (current->token.type) {
1328 nd 101078 case TOKEN_STRING:
1329 nd 101118 current->token.value =
1330     ap_ssi_parse_string(ctx, current->token.value, NULL, 0,
1331     SSI_EXPAND_DROP_NAME);
1332 nd 101078 current->value = !!*current->token.value;
1333 fielding 83751 break;
1334    
1335 nd 101078 case TOKEN_AND:
1336     case TOKEN_OR:
1337     if (!current->left || !current->right) {
1338 trawick 95151 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
1339 jerenkrantz 90874 "Invalid expression \"%s\" in file %s",
1340     expr, r->filename);
1341 rbb 87067 *was_error = 1;
1342 nd 101116 return 0;
1343 fielding 83751 }
1344 nd 101105
1345 nd 101119 if (!current->left->done) {
1346     switch (current->left->token.type) {
1347 nd 101078 case TOKEN_STRING:
1348 nd 101119 current->left->token.value =
1349     ap_ssi_parse_string(ctx, current->left->token.value,
1350 nd 101118 NULL, 0, SSI_EXPAND_DROP_NAME);
1351 nd 101119 current->left->value = !!*current->left->token.value;
1352     DEBUG_DUMP_EVAL(ctx, current->left);
1353     current->left->done = 1;
1354 fielding 83751 break;
1355 nd 101078
1356 fielding 83751 default:
1357 nd 101119 current = current->left;
1358 fielding 83751 continue;
1359     }
1360     }
1361 nd 101078
1362 nd 101097 /* short circuit evaluation */
1363 nd 101119 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 fielding 83751 }
1368 nd 101097 else {
1369 nd 101119 if (!current->right->done) {
1370     switch (current->right->token.type) {
1371 nd 101097 case TOKEN_STRING:
1372 nd 101119 current->right->token.value =
1373     ap_ssi_parse_string(ctx,current->right->token.value,
1374 nd 101118 NULL, 0, SSI_EXPAND_DROP_NAME);
1375 nd 101119 current->right->value = !!*current->right->token.value;
1376     DEBUG_DUMP_EVAL(ctx, current->right);
1377     current->right->done = 1;
1378 nd 101097 break;
1379    
1380     default:
1381 nd 101119 current = current->right;
1382 nd 101097 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 fielding 83751 }
1395     break;
1396    
1397 nd 101078 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 trawick 95151 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
1404 fielding 83751 "Invalid expression \"%s\" in file %s",
1405     expr, r->filename);
1406 rbb 87067 *was_error = 1;
1407 nd 101116 return 0;
1408 fielding 83751 }
1409 nd 101118 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 nd 101078
1416     if (current->right->token.type == TOKEN_RE) {
1417     current->value = re_check(ctx, current->left->token.value,
1418     current->right->token.value);
1419 nd 101097 --regex;
1420 fielding 83751 }
1421     else {
1422 nd 101078 current->value = !strcmp(current->left->token.value,
1423     current->right->token.value);
1424 fielding 83751 }
1425 nd 101078
1426     if (current->token.type == TOKEN_NE) {
1427 fielding 83751 current->value = !current->value;
1428     }
1429     break;
1430 nd 101078
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 trawick 95151 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
1439 nd 101078 "Invalid expression \"%s\" in file %s",
1440     expr, r->filename);
1441 rbb 87067 *was_error = 1;
1442 nd 101116 return 0;
1443 fielding 83751 }
1444 nd 101078
1445 nd 101118 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 nd 101078
1452     current->value = strcmp(current->left->token.value,
1453     current->right->token.value);
1454    
1455 nd 101116 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 fielding 83751 }
1462     break;
1463    
1464 nd 101078 case TOKEN_NOT:
1465 nd 101115 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 nd 101116 if (current->token.type == TOKEN_NOT) {
1478     current->value = !current->value;
1479     }
1480 nd 101115 break;
1481    
1482 minfrin 571872 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 nd 101116 case TOKEN_RE:
1511     if (!error) {
1512     error = "No operator before regex in expr \"%s\" in file %s";
1513     }
1514 nd 101078 case TOKEN_LBRACE:
1515 nd 101116 if (!error) {
1516     error = "Unmatched '(' in \"%s\" in file %s";
1517     }
1518 fielding 83751 default:
1519 nd 101116 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 rbb 87067 *was_error = 1;
1525 nd 101116 return 0;
1526 fielding 83751 }
1527 nd 101116
1528     DEBUG_DUMP_EVAL(ctx, current);
1529     current->done = 1;
1530     current = current->parent;
1531 fielding 83751 }
1532    
1533 nd 101078 return (root ? root->value : 0);
1534 fielding 83751 }
1535    
1536 nd 101049
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 nd 101080 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 jerenkrantz 104439 if (rv != APR_SUCCESS) {
1591 nd 101049 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 nd 101080 rr = ap_sub_req_lookup_file(newpath, r, NULL);
1598 nd 101049
1599     if (rr->status == HTTP_OK && rr->finfo.filetype != 0) {
1600     to_send = rr->filename;
1601 jim 332306 if ((rv = apr_stat(finfo, to_send,
1602 nd 101049 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 jim 332306
1622 nd 101049 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 nd 101080 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 nd 101049 ap_destroy_sub_req(rr);
1640     return -1;
1641     }
1642     }
1643     else {
1644 nd 101080 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "unknown parameter \"%s\" "
1645     "to tag %s in %s", tag, directive, r->filename);
1646 nd 101049 return -1;
1647     }
1648     }
1649    
1650 nd 101053 /*
1651     * <!--#include virtual|file="..." [virtual|file="..."] ... -->
1652     */
1653 nd 101049 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 nd 101053 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 nd 101049
1666 nd 101053 if (!(ctx->flags & SSI_FLAG_PRINTING)) {
1667     return APR_SUCCESS;
1668     }
1669 nd 101049
1670 nd 101053 if (!ctx->argc) {
1671     SSI_CREATE_ERROR_BUCKET(ctx, f, bb);
1672     return APR_SUCCESS;
1673     }
1674 nd 101049
1675 nd 101053 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 nd 101049
1682 nd 101053 ap_ssi_get_tag_and_value(ctx, &tag, &tag_val, SSI_VALUE_DECODED);
1683     if (!tag || !tag_val) {
1684     break;
1685     }
1686 nd 101049
1687 nd 101053 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 nd 101049
1694 nd 101079 parsed_string = ap_ssi_parse_string(ctx, tag_val, NULL, 0,
1695 nd 101053 SSI_EXPAND_DROP_NAME);
1696     if (tag[0] == 'f') {
1697 nd 101080 char *newpath;
1698     apr_status_t rv;
1699    
1700     /* be safe; only files in this directory or below allowed */
1701 nd 102147 rv = apr_filepath_merge(&newpath, NULL, parsed_string,
1702 nd 101080 APR_FILEPATH_SECUREROOTTEST |
1703     APR_FILEPATH_NOTABSOLUTE, ctx->dpool);
1704    
1705 jerenkrantz 104439 if (rv != APR_SUCCESS) {
1706 nd 101053 error_fmt = "unable to include file \"%s\" in parsed file %s";
1707     }
1708     else {
1709 nd 101080 rr = ap_sub_req_lookup_file(newpath, r, f->next);
1710 nd 101053 }
1711     }
1712     else {
1713     rr = ap_sub_req_lookup_uri(parsed_string, r, f->next);
1714     }
1715 nd 101049
1716 nd 101053 if (!error_fmt && rr->status != HTTP_OK) {
1717     error_fmt = "unable to include \"%s\" in parsed file %s";
1718     }
1719 nd 101049
1720 nd 101053 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 jorton 179763 /* 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 nd 101053
1750     if (error_fmt) {
1751     break;
1752     }
1753 nd 101049 }
1754    
1755     return APR_SUCCESS;
1756     }
1757    
1758 nd 101054 /*
1759     * <!--#echo [encoding="..."] var="..." [encoding="..."] var="..." ... -->
1760     */
1761 nd 101049 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 nd 101054 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 nd 101049 encode = E_ENTITY;
1785    
1786 nd 101054 while (1) {
1787     char *tag = NULL;
1788     char *tag_val = NULL;
1789 nd 101049
1790 nd 101054 ap_ssi_get_tag_and_value(ctx, &tag, &tag_val, SSI_VALUE_DECODED);
1791     if (!tag || !tag_val) {
1792     break;
1793     }
1794 nd 101049
1795 nd 101054 if (!strcmp(tag, "var")) {
1796     const char *val;
1797     const char *echo_text = NULL;
1798     apr_size_t e_len;
1799    
1800 nd 101070 val = get_include_var(ap_ssi_parse_string(ctx, tag_val, NULL,
1801 nd 101079 0, SSI_EXPAND_DROP_NAME),
1802 nd 101070 ctx);
1803    
1804 nd 101054 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 nd 101049 }
1816 nd 101054
1817     e_len = strlen(echo_text);
1818     }
1819     else {
1820 nd 101129 echo_text = ctx->intern->undefined_echo;
1821     e_len = ctx->intern->undefined_echo_len;
1822 nd 101049 }
1823 nd 101054
1824     APR_BRIGADE_INSERT_TAIL(bb, apr_bucket_pool_create(
1825 nd 101129 apr_pmemdup(ctx->pool, echo_text, e_len),
1826 nd 101054 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 nd 101049 }
1832 nd 101054 else if (!strcasecmp(tag_val, "url")) {
1833     encode = E_URL;
1834     }
1835     else if (!strcasecmp(tag_val, "entity")) {
1836     encode = E_ENTITY;
1837     }
1838 nd 101049 else {
1839 nd 101054 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 nd 101049 SSI_CREATE_ERROR_BUCKET(ctx, f, bb);
1843 nd 101054 break;
1844 nd 101049 }
1845     }
1846 nd 101054 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 nd 101049 }
1853    
1854     return APR_SUCCESS;
1855     }
1856    
1857 nd 101055 /*
1858 nd 101130 * <!--#config [timefmt="..."] [sizefmt="..."] [errmsg="..."]
1859     * [echomsg="..."] -->
1860 nd 101049 */
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 nd 101055 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