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

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

Parent Directory Parent Directory | Revision Log Revision Log


Revision 592951 - (hide annotations)
Wed Nov 7 23:31:03 2007 UTC (2 years ago) by minfrin
File MIME type: text/plain
File size: 121169 byte(s)
core: Add the option to keep aside a request body up to a certain
size that would otherwise be discarded, to be consumed by filters
such as mod_include. When enabled for a directory, POST requests
to shtml files can be passed through to embedded scripts as POST
requests, rather being downgraded to GET requests.
1 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 minfrin 572136 int accessenable; /* is using the access tests allowed? */
196    
197 nd 101078 #ifdef DEBUG_INCLUDE
198     struct {
199     ap_filter_t *f;
200     apr_bucket_brigade *bb;
201     } debug;
202     #endif
203 nd 101046 };
204 nd 101036
205 nd 101049
206     /*
207     * +-------------------------------------------------------+
208     * | |
209 nd 101078 * | Debugging Utilities
210     * | |
211     * +-------------------------------------------------------+
212     */
213    
214     #ifdef DEBUG_INCLUDE
215 nd 101087
216     #define TYPE_TOKEN(token, ttype) do { \
217     (token)->type = ttype; \
218     (token)->s = #ttype; \
219     } while(0)
220    
221     #define CREATE_NODE(ctx, name) do { \
222     (name) = apr_palloc((ctx)->dpool, sizeof(*(name))); \
223     (name)->parent = (name)->left = (name)->right = NULL; \
224     (name)->done = 0; \
225     (name)->dump_done = 0; \
226     } while(0)
227    
228 nd 101078 static void debug_printf(include_ctx_t *ctx, const char *fmt, ...)
229     {
230     va_list ap;
231     char *debug__str;
232    
233     va_start(ap, fmt);
234     debug__str = apr_pvsprintf(ctx->pool, fmt, ap);
235     va_end(ap);
236    
237     APR_BRIGADE_INSERT_TAIL(ctx->intern->debug.bb, apr_bucket_pool_create(
238     debug__str, strlen(debug__str), ctx->pool,
239     ctx->intern->debug.f->c->bucket_alloc));
240     }
241    
242 nd 101087 #define DUMP__CHILD(ctx, is, node, child) if (1) { \
243     parse_node_t *d__c = node->child; \
244     if (d__c) { \
245     if (!d__c->dump_done) { \
246     if (d__c->parent != node) { \
247     debug_printf(ctx, "!!! Parse tree is not consistent !!!\n"); \
248     if (!d__c->parent) { \
249     debug_printf(ctx, "Parent of " #child " child node is " \
250     "NULL.\n"); \
251     } \
252     else { \
253     debug_printf(ctx, "Parent of " #child " child node " \
254     "points to another node (of type %s)!\n", \
255     d__c->parent->token.s); \
256     } \
257     return; \
258     } \
259     node = d__c; \
260     continue; \
261     } \
262     } \
263     else { \
264     debug_printf(ctx, "%s(missing)\n", is); \
265     } \
266     }
267    
268     static void debug_dump_tree(include_ctx_t *ctx, parse_node_t *root)
269     {
270     parse_node_t *current;
271     char *is;
272    
273     if (!root) {
274     debug_printf(ctx, " -- Parse Tree empty --\n\n");
275     return;
276     }
277    
278     debug_printf(ctx, " ----- Parse Tree -----\n");
279     current = root;
280     is = " ";
281    
282     while (current) {
283     switch (current->token.type) {
284     case TOKEN_STRING:
285     case TOKEN_RE:
286     debug_printf(ctx, "%s%s (%s)\n", is, current->token.s,
287     current->token.value);
288     current->dump_done = 1;
289     current = current->parent;
290     continue;
291    
292     case TOKEN_NOT:
293 nd 101115 case TOKEN_GROUP:
294 nd 101095 case TOKEN_RBRACE:
295     case TOKEN_LBRACE:
296 nd 101087 if (!current->dump_done) {
297     debug_printf(ctx, "%s%s\n", is, current->token.s);
298     is = apr_pstrcat(ctx->dpool, is, " ", NULL);
299     current->dump_done = 1;
300     }
301    
302     DUMP__CHILD(ctx, is, current, right)
303    
304     if (!current->right || current->right->dump_done) {
305     is = apr_pstrmemdup(ctx->dpool, is, strlen(is) - 4);
306     if (current->right) current->right->dump_done = 0;
307     current = current->parent;
308     }
309     continue;
310    
311 nd 101095 default:
312 nd 101087 if (!current->dump_done) {
313 nd 101095 debug_printf(ctx, "%s%s\n", is, current->token.s);
314 nd 101087 is = apr_pstrcat(ctx->dpool, is, " ", NULL);
315     current->dump_done = 1;
316     }
317    
318 nd 101095 DUMP__CHILD(ctx, is, current, left)
319 nd 101087 DUMP__CHILD(ctx, is, current, right)
320    
321 nd 101095 if ((!current->left || current->left->dump_done) &&
322     (!current->right || current->right->dump_done)) {
323    
324 nd 101087 is = apr_pstrmemdup(ctx->dpool, is, strlen(is) - 4);
325 nd 101095 if (current->left) current->left->dump_done = 0;
326 nd 101087 if (current->right) current->right->dump_done = 0;
327     current = current->parent;
328     }
329     continue;
330     }
331     }
332    
333     /* it is possible to call this function within the parser loop, to see
334     * how the tree is built. That way, we must cleanup after us to dump
335     * always the whole tree
336     */
337     root->dump_done = 0;
338     if (root->left) root->left->dump_done = 0;
339     if (root->right) root->right->dump_done = 0;
340    
341     debug_printf(ctx, " --- End Parse Tree ---\n\n");
342    
343     return;
344     }
345    
346 nd 101078 #define DEBUG_INIT(ctx, filter, brigade) do { \
347     (ctx)->intern->debug.f = filter; \
348     (ctx)->intern->debug.bb = brigade; \
349     } while(0)
350    
351     #define DEBUG_PRINTF(arg) debug_printf arg
352    
353     #define DEBUG_DUMP_TOKEN(ctx, token) do { \
354     token_t *d__t = (token); \
355     \
356     if (d__t->type == TOKEN_STRING || d__t->type == TOKEN_RE) { \
357 nd 101095 DEBUG_PRINTF(((ctx), " Found: %s (%s)\n", d__t->s, d__t->value)); \
358 nd 101078 } \
359     else { \
360 nd 101095 DEBUG_PRINTF((ctx, " Found: %s\n", d__t->s)); \
361 nd 101078 } \
362     } while(0)
363    
364 nd 101107 #define DEBUG_DUMP_EVAL(ctx, node) do { \
365     char c = '"'; \
366     switch ((node)->token.type) { \
367     case TOKEN_STRING: \
368     debug_printf((ctx), " Evaluate: %s (%s) -> %c\n", (node)->token.s,\
369     (node)->token.value, ((node)->value) ? '1':'0'); \
370     break; \
371     case TOKEN_AND: \
372     case TOKEN_OR: \
373     debug_printf((ctx), " Evaluate: %s (Left: %s; Right: %s) -> %c\n",\
374     (node)->token.s, \
375     (((node)->left->done) ? ((node)->left->value ?"1":"0") \
376     : "short circuited"), \
377     (((node)->right->done) ? ((node)->right->value?"1":"0") \
378     : "short circuited"), \
379     (node)->value ? '1' : '0'); \
380     break; \
381     case TOKEN_EQ: \
382     case TOKEN_NE: \
383     case TOKEN_GT: \
384     case TOKEN_GE: \
385     case TOKEN_LT: \
386     case TOKEN_LE: \
387     if ((node)->right->token.type == TOKEN_RE) c = '/'; \
388     debug_printf((ctx), " Compare: %s (\"%s\" with %c%s%c) -> %c\n", \
389     (node)->token.s, \
390     (node)->left->token.value, \
391     c, (node)->right->token.value, c, \
392     (node)->value ? '1' : '0'); \
393     break; \
394     default: \
395     debug_printf((ctx), " Evaluate: %s -> %c\n", (node)->token.s, \
396     (node)->value ? '1' : '0'); \
397     break; \
398     } \
399     } while(0)
400    
401 nd 101081 #define DEBUG_DUMP_UNMATCHED(ctx, unmatched) do { \
402     if (unmatched) { \
403     DEBUG_PRINTF(((ctx), " Unmatched %c\n", (char)(unmatched))); \
404     } \
405 nd 101078 } while(0)
406    
407     #define DEBUG_DUMP_COND(ctx, text) \
408     DEBUG_PRINTF(((ctx), "**** %s cond status=\"%c\"\n", (text), \
409     ((ctx)->flags & SSI_FLAG_COND_TRUE) ? '1' : '0'))
410    
411 nd 101087 #define DEBUG_DUMP_TREE(ctx, root) debug_dump_tree(ctx, root)
412    
413 nd 101078 #else /* DEBUG_INCLUDE */
414    
415     #define TYPE_TOKEN(token, ttype) (token)->type = ttype
416 nd 101087
417     #define CREATE_NODE(ctx, name) do { \
418     (name) = apr_palloc((ctx)->dpool, sizeof(*(name))); \
419     (name)->parent = (name)->left = (name)->right = NULL; \
420     (name)->done = 0; \
421     } while(0)
422    
423 nd 101078 #define DEBUG_INIT(ctx, f, bb)
424     #define DEBUG_PRINTF(arg)
425     #define DEBUG_DUMP_TOKEN(ctx, token)
426 nd 101107 #define DEBUG_DUMP_EVAL(ctx, node)
427 nd 101078 #define DEBUG_DUMP_UNMATCHED(ctx, unmatched)
428     #define DEBUG_DUMP_COND(ctx, text)
429 nd 101087 #define DEBUG_DUMP_TREE(ctx, root)
430 nd 101078
431     #endif /* !DEBUG_INCLUDE */
432    
433    
434     /*
435     * +-------------------------------------------------------+
436     * | |
437 nd 101049 * | Static Module Data
438     * | |
439     * +-------------------------------------------------------+
440     */
441    
442     /* global module structure */
443     module AP_MODULE_DECLARE_DATA include_module;
444    
445     /* function handlers for include directives */
446     static apr_hash_t *include_handlers;
447    
448     /* forward declaration of handler registry */
449     static APR_OPTIONAL_FN_TYPE(ap_register_include_handler) *ssi_pfn_register;
450    
451     /* Sentinel value to store in subprocess_env for items that
452     * shouldn't be evaluated until/unless they're actually used
453     */
454     static const char lazy_eval_sentinel;
455     #define LAZY_VALUE (&lazy_eval_sentinel)
456    
457     /* default values */
458 nd 101048 #define DEFAULT_START_SEQUENCE "<!--#"
459     #define DEFAULT_END_SEQUENCE "-->"
460 nd 101046 #define DEFAULT_ERROR_MSG "[an error occurred while processing this directive]"
461     #define DEFAULT_TIME_FORMAT "%A, %d-%b-%Y %H:%M:%S %Z"
462     #define DEFAULT_UNDEFINED_ECHO "(none)"
463 nd 101036
464 jerenkrantz 90872 #ifdef XBITHACK
465 nd 101052 #define DEFAULT_XBITHACK XBITHACK_FULL
466 jerenkrantz 90872 #else
467 nd 101052 #define DEFAULT_XBITHACK XBITHACK_OFF
468 jerenkrantz 90872 #endif
469    
470 fielding 83751
471 nd 101049 /*
472     * +-------------------------------------------------------+
473     * | |
474     * | Environment/Expansion Functions
475     * | |
476     * +-------------------------------------------------------+
477 jerenkrantz 90872 */
478    
479 fielding 83751 /*
480     * decodes a string containing html entities or numeric character references.
481     * 's' is overwritten with the decoded string.
482     * If 's' is syntatically incorrect, then the followed fixups will be made:
483     * unknown entities will be left undecoded;
484     * references to unused numeric characters will be deleted.
485     * In particular, &#00; will not be decoded, but will be deleted.
486     */
487    
488     /* maximum length of any ISO-LATIN-1 HTML entity name. */
489     #define MAXENTLEN (6)
490    
491     /* The following is a shrinking transformation, therefore safe. */
492    
493     static void decodehtml(char *s)
494     {
495     int val, i, j;
496 brianp 92284 char *p;
497 fielding 83751 const char *ents;
498     static const char * const entlist[MAXENTLEN + 1] =
499     {
500 nd 101074 NULL, /* 0 */
501     NULL, /* 1 */
502     "lt\074gt\076", /* 2 */
503     "amp\046ETH\320eth\360", /* 3 */
504     "quot\042Auml\304Euml\313Iuml\317Ouml\326Uuml\334auml\344euml"
505     "\353iuml\357ouml\366uuml\374yuml\377", /* 4 */
506    
507     "Acirc\302Aring\305AElig\306Ecirc\312Icirc\316Ocirc\324Ucirc"
508     "\333THORN\336szlig\337acirc\342aring\345aelig\346ecirc\352"
509     "icirc\356ocirc\364ucirc\373thorn\376", /* 5 */
510    
511     "Agrave\300Aacute\301Atilde\303Ccedil\307Egrave\310Eacute\311"
512     "Igrave\314Iacute\315Ntilde\321Ograve\322Oacute\323Otilde"
513     "\325Oslash\330Ugrave\331Uacute\332Yacute\335agrave\340"
514     "aacute\341atilde\343ccedil\347egrave\350eacute\351igrave"
515     "\354iacute\355ntilde\361ograve\362oacute\363otilde\365"
516     "oslash\370ugrave\371uacute\372yacute\375" /* 6 */
517 fielding 83751 };
518    
519 brianp 92284 /* Do a fast scan through the string until we find anything
520     * that needs more complicated handling
521     */
522     for (; *s != '&'; s++) {
523     if (*s == '\0') {
524     return;
525     }
526     }
527    
528     for (p = s; *s != '\0'; s++, p++) {
529 fielding 83751 if (*s != '&') {
530     *p = *s;
531     continue;
532     }
533     /* find end of entity */
534     for (i = 1; s[i] != ';' && s[i] != '\0'; i++) {
535     continue;
536     }
537    
538     if (s[i] == '\0') { /* treat as normal data */
539     *p = *s;
540     continue;
541     }
542    
543     /* is it numeric ? */
544     if (s[1] == '#') {
545 wrowe 86008 for (j = 2, val = 0; j < i && apr_isdigit(s[j]); j++) {
546 fielding 83751 val = val * 10 + s[j] - '0';
547     }
548     s += i;
549     if (j < i || val <= 8 || (val >= 11 && val <= 31) ||
550     (val >= 127 && val <= 160) || val >= 256) {
551     p--; /* no data to output */
552     }
553     else {
554     *p = RAW_ASCII_CHAR(val);
555     }
556     }
557     else {
558     j = i - 1;
559     if (j > MAXENTLEN || entlist[j] == NULL) {
560     /* wrong length */
561     *p = '&';
562     continue; /* skip it */
563     }
564     for (ents = entlist[j]; *ents != '\0'; ents += i) {
565     if (strncmp(s + 1, ents, j) == 0) {
566     break;
567     }
568     }
569    
570     if (*ents == '\0') {
571     *p = '&'; /* unknown */
572     }
573     else {
574     *p = RAW_ASCII_CHAR(((const unsigned char *) ents)[j]);
575     s += i;
576     }
577     }
578     }
579    
580     *p = '\0';
581     }
582    
583 nd 101052 static void add_include_vars(request_rec *r, const char *timefmt)
584 fielding 83751 {
585 nd 101049 apr_table_t *e = r->subprocess_env;
586     char *t;
587 nd 101036
588 nd 101049 apr_table_setn(e, "DATE_LOCAL", LAZY_VALUE);
589     apr_table_setn(e, "DATE_GMT", LAZY_VALUE);
590     apr_table_setn(e, "LAST_MODIFIED", LAZY_VALUE);
591     apr_table_setn(e, "DOCUMENT_URI", r->uri);
592     if (r->path_info && *r->path_info) {
593     apr_table_setn(e, "DOCUMENT_PATH_INFO", r->path_info);
594 nd 101036 }
595 nd 101049 apr_table_setn(e, "USER_NAME", LAZY_VALUE);
596 nd 101461 if (r->filename && (t = strrchr(r->filename, '/'))) {
597 nd 101049 apr_table_setn(e, "DOCUMENT_NAME", ++t);
598     }
599     else {
600     apr_table_setn(e, "DOCUMENT_NAME", r->uri);
601     }
602     if (r->args) {
603     char *arg_copy = apr_pstrdup(r->pool, r->args);
604 nd 101036
605 nd 101049 ap_unescape_url(arg_copy);
606     apr_table_setn(e, "QUERY_STRING_UNESCAPED",
607     ap_escape_shell_cmd(r->pool, arg_copy));
608     }
609     }
610 nd 101036
611 nd 101049 static const char *add_include_vars_lazy(request_rec *r, const char *var)
612     {
613     char *val;
614     if (!strcasecmp(var, "DATE_LOCAL")) {
615     include_dir_config *conf =
616     (include_dir_config *)ap_get_module_config(r->per_dir_config,
617     &include_module);
618     val = ap_ht_time(r->pool, r->request_time, conf->default_time_fmt, 0);
619     }
620     else if (!strcasecmp(var, "DATE_GMT")) {
621     include_dir_config *conf =
622     (include_dir_config *)ap_get_module_config(r->per_dir_config,
623     &include_module);
624     val = ap_ht_time(r->pool, r->request_time, conf->default_time_fmt, 1);
625     }
626     else if (!strcasecmp(var, "LAST_MODIFIED")) {
627     include_dir_config *conf =
628     (include_dir_config *)ap_get_module_config(r->per_dir_config,
629     &include_module);
630     val = ap_ht_time(r->pool, r->finfo.mtime, conf->default_time_fmt, 0);
631     }
632     else if (!strcasecmp(var, "USER_NAME")) {
633 trawick 101154 if (apr_uid_name_get(&val, r->finfo.user, r->pool) != APR_SUCCESS) {
634 nd 101049 val = "<unknown>";
635     }
636     }
637     else {
638     val = NULL;
639     }
640 nd 101036
641 nd 101049 if (val) {
642     apr_table_setn(r->subprocess_env, var, val);
643 nd 101036 }
644 nd 101049 return val;
645     }
646 nd 101036
647 nd 101070 static const char *get_include_var(const char *var, include_ctx_t *ctx)
648 nd 101049 {
649     const char *val;
650 nd 101073 request_rec *r = ctx->intern->r;
651    
652 nd 101049 if (apr_isdigit(*var) && !var[1]) {
653 nd 101100 apr_size_t idx = *var - '0';
654 nd 101073 backref_t *re = ctx->intern->re;
655    
656 nd 101049 /* Handle $0 .. $9 from the last regex evaluated.
657     * The choice of returning NULL strings on not-found,
658     * v.s. empty strings on an empty match is deliberate.
659     */
660 nd 101073 if (!re) {
661 fielding 101234 ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r,
662     "regex capture $%" APR_SIZE_T_FMT " refers to no regex in %s",
663     idx, r->filename);
664 nd 101049 return NULL;
665     }
666     else {
667 striker 101556 if (re->nsub < idx || idx >= AP_MAX_REG_MATCH) {
668 nd 101073 ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r,
669 fielding 101234 "regex capture $%" APR_SIZE_T_FMT
670     " is out of range (last regex was: '%s') in %s",
671     idx, re->rexp, r->filename);
672 nd 101049 return NULL;
673     }
674 nd 101073
675     if (re->match[idx].rm_so < 0 || re->match[idx].rm_eo < 0) {
676     return NULL;
677     }
678    
679     val = apr_pstrmemdup(ctx->dpool, re->source + re->match[idx].rm_so,
680     re->match[idx].rm_eo - re->match[idx].rm_so);
681 nd 101049 }
682     }
683     else {
684 nd 101073 val = apr_table_get(r->subprocess_env, var);
685 nd 101049
686 nd 101073 if (val == LAZY_VALUE) {
687     val = add_include_vars_lazy(r, var);
688     }
689 nd 101049 }
690 nd 101073
691 nd 101049 return val;
692 nd 101036 }
693    
694 fielding 83751 /*
695     * Do variable substitution on strings
696 nd 101070 *
697 brianp 92747 * (Note: If out==NULL, this function allocs a buffer for the resulting
698 nd 101070 * string from ctx->pool. The return value is always the parsed string)
699 fielding 83751 */
700 nd 101069 static char *ap_ssi_parse_string(include_ctx_t *ctx, const char *in, char *out,
701 brianp 92747 apr_size_t length, int leave_name)
702 fielding 83751 {
703 nd 101069 request_rec *r = ctx->intern->r;
704 nd 101070 result_item_t *result = NULL, *current = NULL;
705     apr_size_t outlen = 0, inlen, span;
706     char *ret = NULL, *eout = NULL;
707     const char *p;
708 fielding 83751
709 nd 101070 if (out) {
710     /* sanity check, out && !length is not supported */
711     ap_assert(out && length);
712    
713     ret = out;
714     eout = out + length - 1;
715     }
716    
717     span = strcspn(in, "\\$");
718     inlen = strlen(in);
719    
720     /* fast exit */
721     if (inlen == span) {
722     if (out) {
723     apr_cpystrn(out, in, length);
724 brianp 92747 }
725 nd 101070 else {
726     ret = apr_pstrmemdup(ctx->pool, in, (length && length <= inlen)
727     ? length - 1 : inlen);
728     }
729    
730     return ret;
731 brianp 92747 }
732 nd 101070
733     /* well, actually something to do */
734     p = in + span;
735    
736     if (out) {
737     if (span) {
738     memcpy(out, in, (out+span <= eout) ? span : (eout-out));
739     out += span;
740     }
741     }
742 brianp 92747 else {
743 nd 101070 current = result = apr_palloc(ctx->dpool, sizeof(*result));
744     current->next = NULL;
745     current->string = in;
746     current->len = span;
747     outlen = span;
748 brianp 92747 }
749    
750 nd 101070 /* loop for specials */
751     do {
752     if ((out && out >= eout) || (length && outlen >= length)) {
753     break;
754     }
755 fielding 83751
756 nd 101070 /* prepare next entry */
757     if (!out && current->len) {
758     current->next = apr_palloc(ctx->dpool, sizeof(*current->next));
759     current = current->next;
760     current->next = NULL;
761     current->len = 0;
762     }
763    
764     /*
765     * escaped character
766     */
767     if (*p == '\\') {
768     if (out) {
769     *out++ = (p[1] == '$') ? *++p : *p;
770     ++p;
771     }
772     else {
773     current->len = 1;
774     current->string = (p[1] == '$') ? ++p : p;
775     ++p;
776     ++outlen;
777     }
778     }
779    
780     /*
781     * variable expansion
782     */
783     else { /* *p == '$' */
784     const char *newp = NULL, *ep, *key = NULL;
785    
786     if (*++p == '{') {
787     ep = ap_strchr_c(++p, '}');
788     if (!ep) {
789     ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "Missing '}' on "
790     "variable \"%s\" in %s", p, r->filename);
791     break;
792 brianp 92747 }
793 nd 101070
794     if (p < ep) {
795     key = apr_pstrmemdup(ctx->dpool, p, ep - p);
796     newp = ep + 1;
797 brianp 92747 }
798 nd 101070 p -= 2;
799 jerenkrantz 90874 }
800 fielding 83751 else {
801 nd 101070 ep = p;
802     while (*ep == '_' || apr_isalnum(*ep)) {
803     ++ep;
804     }
805    
806     if (p < ep) {
807     key = apr_pstrmemdup(ctx->dpool, p, ep - p);
808     newp = ep;
809     }
810     --p;
811 fielding 83751 }
812    
813 nd 101070 /* empty name results in a copy of '$' in the output string */
814     if (!key) {
815     if (out) {
816     *out++ = *p++;
817 jerenkrantz 90874 }
818     else {
819 nd 101070 current->len = 1;
820     current->string = p++;
821     ++outlen;
822 jerenkrantz 90874 }
823 nd 101070 }
824     else {
825     const char *val = get_include_var(key, ctx);
826     apr_size_t len = 0;
827 fielding 83751
828 nd 101070 if (val) {
829     len = strlen(val);
830 jerenkrantz 90874 }
831 nd 101070 else if (leave_name) {
832     val = p;
833     len = ep - p;
834 jerenkrantz 90874 }
835 nd 101070
836     if (val && len) {
837     if (out) {
838     memcpy(out, val, (out+len <= eout) ? len : (eout-out));
839     out += len;
840 brianp 92747 }
841 nd 101070 else {
842     current->len = len;
843     current->string = val;
844     outlen += len;
845 brianp 92747 }
846     }
847 nd 101070
848     p = newp;
849 jerenkrantz 90874 }
850 nd 101070 }
851    
852     if ((out && out >= eout) || (length && outlen >= length)) {
853 fielding 83751 break;
854     }
855 nd 101070
856     /* check the remainder */
857     if (*p && (span = strcspn(p, "\\$")) > 0) {
858     if (!out && current->len) {
859     current->next = apr_palloc(ctx->dpool, sizeof(*current->next));
860     current = current->next;
861     current->next = NULL;
862     }
863    
864     if (out) {
865     memcpy(out, p, (out+span <= eout) ? span : (eout-out));
866     out += span;
867     }
868     else {
869     current->len = span;
870     current->string = p;
871     outlen += span;
872     }
873    
874     p += span;
875     }
876     } while (p < in+inlen);
877    
878     /* assemble result */
879     if (out) {
880     if (out > eout) {
881     *eout = '\0';
882     }
883     else {
884     *out = '\0';
885     }
886 fielding 83751 }
887 nd 101070 else {
888     const char *ep;
889    
890     if (length && outlen > length) {
891     outlen = length - 1;
892     }
893    
894     ret = out = apr_palloc(ctx->pool, outlen + 1);
895     ep = ret + outlen;
896    
897     do {
898     if (result->len) {
899     memcpy(out, result->string, (out+result->len <= ep)
900     ? result->len : (ep-out));
901     out += result->len;
902     }
903     result = result->next;
904     } while (result && out < ep);
905    
906     ret[outlen] = '\0';
907     }
908    
909     return ret;
910 fielding 83751 }
911    
912    
913 nd 101049 /*
914     * +-------------------------------------------------------+
915     * | |
916     * | Conditional Expression Parser
917     * | |
918     * +-------------------------------------------------------+
919 fielding 83751 */
920    
921 nd 101078 static APR_INLINE int re_check(include_ctx_t *ctx, const char *string,
922     const char *rexp)
923 fielding 83751 {
924 jorton 153384 ap_regex_t *compiled;
925 nd 101073 backref_t *re = ctx->intern->re;
926     int rc;
927 fielding 83751
928 jorton 153384 compiled = ap_pregcomp(ctx->dpool, rexp, AP_REG_EXTENDED);
929 nd 101073 if (!compiled) {
930     ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, ctx->intern->r, "unable to "
931     "compile pattern \"%s\"", rexp);
932 fielding 83751 return -1;
933     }
934 nd 101073
935     if (!re) {
936     re = ctx->intern->re = apr_palloc(ctx->pool, sizeof(*re));
937 wrowe 92676 }
938 nd 101073
939 nd 101078 re->source = apr_pstrdup(ctx->pool, string);
940     re->rexp = apr_pstrdup(ctx->pool, rexp);
941 nd 101073 re->nsub = compiled->re_nsub;
942 striker 101556 rc = !ap_regexec(compiled, string, AP_MAX_REG_MATCH, re->match, 0);
943 nd 101073
944     ap_pregfree(ctx->dpool, compiled);
945     return rc;
946 fielding 83751 }
947    
948 minfrin 571872 static int get_ptoken(include_ctx_t *ctx, const char **parse, token_t *token, token_t *previous)
949 fielding 83751 {
950 nd 101078 const char *p;
951     apr_size_t shift;
952     int unmatched;
953 fielding 83751
954 brianp 92838 token->value = NULL;
955    
956 nd 101078 if (!*parse) {
957     return 0;
958     }
959    
960 fielding 83751 /* Skip leading white space */
961 nd 101078 while (apr_isspace(**parse)) {
962     ++*parse;
963 fielding 83751 }
964 nd 101078
965     if (!**parse) {
966     *parse = NULL;
967     return 0;
968 fielding 83751 }
969    
970 nd 101078 TYPE_TOKEN(token, TOKEN_STRING); /* the default type */
971     p = *parse;
972     unmatched = 0;
973    
974     switch (*(*parse)++) {
975 fielding 83751 case '(':
976 nd 101078 TYPE_TOKEN(token, TOKEN_LBRACE);
977     return 0;
978 fielding 83751 case ')':
979 nd 101078 TYPE_TOKEN(token, TOKEN_RBRACE);
980     return 0;
981 fielding 83751 case '=':
982 nd 101120 if (**parse == '=') ++*parse;
983 nd 101078 TYPE_TOKEN(token, TOKEN_EQ);
984     return 0;
985 fielding 83751 case '!':
986 nd 101078 if (**parse == '=') {
987     TYPE_TOKEN(token, TOKEN_NE);
988     ++*parse;
989     return 0;
990 fielding 83751 }
991 nd 101078 TYPE_TOKEN(token, TOKEN_NOT);
992     return 0;
993 fielding 83751 case '\'':
994 nd 101078 unmatched = '\'';
995 fielding 83751 break;
996 wrowe 92676 case '/':
997 minfrin 571872 /* if last token was ACCESS, this token is STRING */
998     if (previous != NULL && TOKEN_ACCESS == previous->type) {
999     break;
1000     }
1001 nd 101078 TYPE_TOKEN(token, TOKEN_RE);
1002     unmatched = '/';
1003 wrowe 92676 break;
1004 fielding 83751 case '|':
1005 nd 101078 if (**parse == '|') {
1006     TYPE_TOKEN(token, TOKEN_OR);
1007     ++*parse;
1008     return 0;
1009 fielding 83751 }
1010     break;
1011     case '&':
1012 nd 101078 if (**parse == '&') {
1013     TYPE_TOKEN(token, TOKEN_AND);
1014     ++*parse;
1015     return 0;
1016 fielding 83751 }
1017     break;
1018     case '>':
1019 nd 101078 if (**parse == '=') {
1020     TYPE_TOKEN(token, TOKEN_GE);
1021     ++*parse;
1022     return 0;
1023 fielding 83751 }
1024 nd 101078 TYPE_TOKEN(token, TOKEN_GT);
1025     return 0;
1026 fielding 83751 case '<':
1027 nd 101078 if (**parse == '=') {
1028     TYPE_TOKEN(token, TOKEN_LE);
1029     ++*parse;
1030     return 0;
1031 fielding 83751 }
1032 nd 101078 TYPE_TOKEN(token, TOKEN_LT);
1033     return 0;
1034 minfrin 571872 case '-':
1035 minfrin 572136 if (**parse == 'A' && (ctx->intern->accessenable)) {
1036 minfrin 571872 TYPE_TOKEN(token, TOKEN_ACCESS);
1037     ++*parse;
1038     return 0;
1039     }
1040     break;
1041 fielding 83751 }
1042    
1043 nd 101093 /* It's a string or regex token
1044     * Now search for the next token, which finishes this string
1045 fielding 83751 */
1046 nd 101078 shift = 0;
1047 nd 101093 p = *parse = token->value = unmatched ? *parse : p;
1048    
1049 nd 101078 for (; **parse; p = ++*parse) {
1050     if (**parse == '\\') {
1051     if (!*(++*parse)) {
1052     p = *parse;
1053     break;
1054 fielding 83751 }
1055 nd 101078
1056     ++shift;
1057 fielding 83751 }
1058 rbb 87067 else {
1059 nd 101078 if (unmatched) {
1060     if (**parse == unmatched) {
1061     unmatched = 0;
1062     ++*parse;
1063     break;
1064 rbb 87067 }
1065 nd 101078 } else if (apr_isspace(**parse)) {
1066     break;
1067 fielding 83751 }
1068 rbb 87067 else {
1069 nd 101078 int found = 0;
1070    
1071     switch (**parse) {
1072     case '(':
1073     case ')':
1074     case '=':
1075     case '!':
1076     case '<':
1077     case '>':
1078     ++found;
1079     break;
1080    
1081     case '|':
1082     case '&':
1083     if ((*parse)[1] == **parse) {
1084     ++found;
1085     }
1086     break;
1087 fielding 83751 }
1088 nd 101078
1089     if (found) {
1090     break;
1091 fielding 83751 }
1092     }
1093     }
1094     }
1095 rbb 87067
1096 nd 101078 if (unmatched) {
1097 minfrin 571872 token->value = apr_pstrdup(ctx->dpool, "");
1098 fielding 83751 }
1099 nd 101078 else {
1100     apr_size_t len = p - token->value - shift;
1101 minfrin 571872 char *c = apr_palloc(ctx->dpool, len + 1);
1102 wrowe 92676
1103 nd 101078 p = token->value;
1104     token->value = c;
1105 fielding 83751
1106 nd 101078 while (shift--) {
1107     const char *e = ap_strchr_c(p, '\\');
1108 fielding 83751
1109 nd 101078 memcpy(c, p, e-p);
1110     c += e-p;
1111     *c++ = *++e;
1112     len -= e-p;
1113     p = e+1;
1114     }
1115    
1116     if (len) {
1117     memcpy(c, p, len);
1118     }
1119     c[len] = '\0';
1120     }
1121    
1122     return unmatched;
1123     }
1124    
1125     static int parse_expr(include_ctx_t *ctx, const char *expr, int *was_error)
1126 fielding 83751 {
1127 nd 101078 parse_node_t *new, *root = NULL, *current = NULL;
1128     request_rec *r = ctx->intern->r;
1129 minfrin 571872 request_rec *rr = NULL;
1130 nd 101118 const char *error = "Invalid expression \"%s\" in file %s";
1131 nd 101078 const char *parse = expr;
1132 nd 101116 int was_unmatched = 0;
1133 nd 101097 unsigned regex = 0;
1134    
1135 nd 101078 *was_error = 0;
1136 fielding 83751
1137 nd 101078 if (!parse) {
1138     return 0;
1139 fielding 83751 }
1140    
1141     /* Create Parse Tree */
1142     while (1) {
1143 nd 101107 /* uncomment this to see how the tree a built:
1144     *
1145     * DEBUG_DUMP_TREE(ctx, root);
1146     */
1147 nd 101087 CREATE_NODE(ctx, new);
1148 nd 101078
1149 minfrin 571872 was_unmatched = get_ptoken(ctx, &parse, &new->token,
1150     (current != NULL ? &current->token : NULL));
1151 nd 101078 if (!parse) {
1152 fielding 83751 break;
1153     }
1154    
1155 nd 101078 DEBUG_DUMP_UNMATCHED(ctx, was_unmatched);
1156     DEBUG_DUMP_TOKEN(ctx, &new->token);
1157    
1158 nd 101102 if (!current) {
1159     switch (new->token.type) {
1160     case TOKEN_STRING:
1161     case TOKEN_NOT:
1162 minfrin 571872 case TOKEN_ACCESS:
1163 nd 101102 case TOKEN_LBRACE:
1164 fielding 83751 root = current = new;
1165 nd 101102 continue;
1166    
1167     default:
1168 nd 101116 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, error, expr,
1169     r->filename);
1170 nd 101102 *was_error = 1;
1171 nd 101116 return 0;
1172 fielding 83751 }
1173 nd 101102 }
1174 nd 101078
1175 nd 101102 switch (new->token.type) {
1176     case TOKEN_STRING:
1177 fielding 83751 switch (current->token.type) {
1178 nd 101078 case TOKEN_STRING:
1179     current->token.value =
1180     apr_pstrcat(ctx->dpool, current->token.value,
1181     *current->token.value ? " " : "",
1182     new->token.value, NULL);
1183 nd 101116 continue;
1184 nd 101078
1185 nd 101101 case TOKEN_RE:
1186 nd 101115 case TOKEN_RBRACE:
1187     case TOKEN_GROUP:
1188 nd 101116 break;
1189 nd 101101
1190     default:
1191     new->parent = current;
1192     current = current->right = new;
1193 nd 101116 continue;
1194 fielding 83751 }
1195     break;
1196    
1197 nd 101078 case TOKEN_RE:
1198 wrowe 92676 switch (current->token.type) {
1199 nd 101078 case TOKEN_EQ:
1200     case TOKEN_NE:
1201 wrowe 92676 new->parent = current;
1202     current = current->right = new;
1203 nd 101097 ++regex;
1204 nd 101116 continue;
1205 nd 101078
1206 wrowe 92676 default:
1207 nd 101116 break;
1208 wrowe 92676 }
1209     break;
1210    
1211 nd 101078 case TOKEN_AND:
1212     case TOKEN_OR:
1213 nd 101119 switch (current->token.type) {
1214     case TOKEN_STRING:
1215     case TOKEN_RE:
1216     case TOKEN_GROUP:
1217     current = current->parent;
1218    
1219     while (current) {
1220     switch (current->token.type) {
1221     case TOKEN_AND:
1222     case TOKEN_OR:
1223     case TOKEN_LBRACE:
1224     break;
1225    
1226     default:
1227     current = current->parent;
1228     continue;
1229     }
1230 fielding 83751 break;
1231 nd 101119 }
1232 nd 101078
1233 nd 101119 if (!current) {
1234     new->left = root;
1235     root->parent = new;
1236     current = root = new;
1237 nd 101101 continue;
1238 fielding 83751 }
1239 jim 332306
1240 fielding 83751 new->left = current->right;
1241 nd 101088 new->left->parent = new;
1242 fielding 83751 new->parent = current;
1243 nd 101119 current = current->right = new;
1244     continue;
1245    
1246     default:
1247     break;
1248 fielding 83751 }
1249 nd 101119 break;
1250 fielding 83751
1251 nd 101078 case TOKEN_EQ:
1252     case TOKEN_NE:
1253     case TOKEN_GE:
1254     case TOKEN_GT:
1255     case TOKEN_LE:
1256     case TOKEN_LT:
1257 nd 101113 if (current->token.type == TOKEN_STRING) {
1258     current = current->parent;
1259    
1260     if (!current) {
1261     new->left = root;
1262     root->parent = new;
1263     current = root = new;
1264 fielding 83751 continue;
1265 nd 101113 }
1266 nd 101078
1267 nd 101113 switch (current->token.type) {
1268 nd 101078 case TOKEN_LBRACE:
1269     case TOKEN_AND:
1270     case TOKEN_OR:
1271 nd 101113 new->left = current->right;
1272     new->left->parent = new;
1273     new->parent = current;
1274     current = current->right = new;
1275     continue;
1276 nd 101078
1277 fielding 83751 default:
1278 nd 101113 break;
1279 fielding 83751 }
1280     }
1281 nd 101116 break;
1282 nd 101078
1283     case TOKEN_RBRACE:
1284 nd 101111 while (current && current->token.type != TOKEN_LBRACE) {
1285 fielding 83751 current = current->parent;
1286     }
1287 nd 101111
1288 nd 101116 if (current) {
1289     TYPE_TOKEN(&current->token, TOKEN_GROUP);
1290     continue;
1291 fielding 83751 }
1292 nd 101111
1293 nd 101116 error = "Unmatched ')' in \"%s\" in file %s";
1294 fielding 83751 break;
1295    
1296 nd 101103 case TOKEN_NOT:
1297 minfrin 571872 case TOKEN_ACCESS:
1298 nd 101078 case TOKEN_LBRACE:
1299 nd 101091 switch (current->token.type) {
1300     case TOKEN_STRING:
1301     case TOKEN_RE:
1302 nd 101115 case TOKEN_RBRACE:
1303     case TOKEN_GROUP:
1304 nd 101116 break;
1305 nd 101078
1306 nd 101091 default:
1307 nd 101116 current->right = new;
1308     new->parent = current;
1309     current = new;
1310     continue;
1311 fielding 83751 }
1312     break;
1313 nd 101115
1314     default:
1315     break;
1316 fielding 83751 }
1317 nd 101116
1318     ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, error, expr, r->filename);
1319     *was_error = 1;
1320     return 0;
1321 fielding 83751 }
1322    
1323 nd 101107 DEBUG_DUMP_TREE(ctx, root);
1324    
1325 fielding 83751 /* Evaluate Parse Tree */
1326     current = root;
1327 nd 101116 error = NULL;
1328 nd 101078 while (current) {
1329 fielding 83751 switch (current->token.type) {
1330 nd 101078 case TOKEN_STRING:
1331 nd 101118 current->token.value =
1332     ap_ssi_parse_string(ctx, current->token.value, NULL, 0,
1333     SSI_EXPAND_DROP_NAME);
1334 nd 101078 current->value = !!*current->token.value;
1335 fielding 83751 break;
1336    
1337 nd 101078 case TOKEN_AND:
1338     case TOKEN_OR:
1339     if (!current->left || !current->right) {
1340 trawick 95151 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
1341 jerenkrantz 90874 "Invalid expression \"%s\" in file %s",
1342     expr, r->filename);
1343 rbb 87067 *was_error = 1;
1344 nd 101116 return 0;
1345 fielding 83751 }
1346 nd 101105
1347 nd 101119 if (!current->left->done) {
1348     switch (current->left->token.type) {
1349 nd 101078 case TOKEN_STRING:
1350 nd 101119 current->left->token.value =
1351     ap_ssi_parse_string(ctx, current->left->token.value,
1352 nd 101118 NULL, 0, SSI_EXPAND_DROP_NAME);
1353 nd 101119 current->left->value = !!*current->left->token.value;
1354     DEBUG_DUMP_EVAL(ctx, current->left);
1355     current->left->done = 1;
1356 fielding 83751 break;
1357 nd 101078
1358 fielding 83751 default:
1359 nd 101119 current = current->left;
1360 fielding 83751 continue;
1361     }
1362     }
1363 nd 101078
1364 nd 101097 /* short circuit evaluation */
1365 nd 101119 if (!current->right->done && !regex &&
1366     ((current->token.type == TOKEN_AND && !current->left->value) ||
1367     (current->token.type == TOKEN_OR && current->left->value))) {
1368     current->value = current->left->value;
1369 fielding 83751 }
1370 nd 101097 else {
1371 nd 101119 if (!current->right->done) {
1372     switch (current->right->token.type) {
1373 nd 101097 case TOKEN_STRING:
1374 nd 101119 current->right->token.value =
1375     ap_ssi_parse_string(ctx,current->right->token.value,
1376 nd 101118 NULL, 0, SSI_EXPAND_DROP_NAME);
1377 nd 101119 current->right->value = !!*current->right->token.value;
1378     DEBUG_DUMP_EVAL(ctx, current->right);
1379     current->right->done = 1;
1380 nd 101097 break;
1381    
1382     default:
1383 nd 101119 current = current->right;
1384 nd 101097 continue;
1385     }
1386     }
1387    
1388     if (current->token.type == TOKEN_AND) {
1389     current->value = current->left->value &&
1390     current->right->value;
1391     }
1392     else {
1393     current->value = current->left->value ||
1394     current->right->value;
1395     }
1396 fielding 83751 }
1397     break;
1398    
1399 nd 101078 case TOKEN_EQ:
1400     case TOKEN_NE:
1401     if (!current->left || !current->right ||
1402     current->left->token.type != TOKEN_STRING ||
1403     (current->right->token.type != TOKEN_STRING &&
1404     current->right->token.type != TOKEN_RE)) {
1405 trawick 95151 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
1406 fielding 83751 "Invalid expression \"%s\" in file %s",
1407     expr, r->filename);
1408 rbb 87067 *was_error = 1;
1409 nd 101116 return 0;
1410 fielding 83751 }
1411 nd 101118 current->left->token.value =
1412     ap_ssi_parse_string(ctx, current->left->token.value, NULL, 0,
1413     SSI_EXPAND_DROP_NAME);
1414     current->right->token.value =
1415     ap_ssi_parse_string(ctx, current->right->token.value, NULL, 0,
1416     SSI_EXPAND_DROP_NAME);
1417 nd 101078
1418     if (current->right->token.type == TOKEN_RE) {
1419     current->value = re_check(ctx, current->left->token.value,
1420     current->right->token.value);
1421 nd 101097 --regex;
1422 fielding 83751 }
1423     else {
1424 nd 101078 current->value = !strcmp(current->left->token.value,
1425     current->right->token.value);
1426 fielding 83751 }
1427 nd 101078
1428     if (current->token.type == TOKEN_NE) {
1429 fielding 83751 current->value = !current->value;
1430     }
1431     break;
1432 nd 101078
1433     case TOKEN_GE:
1434     case TOKEN_GT:
1435     case TOKEN_LE:
1436     case TOKEN_LT:
1437     if (!current->left || !current->right ||
1438     current->left->token.type != TOKEN_STRING ||
1439     current->right->token.type != TOKEN_STRING) {
1440 trawick 95151 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
1441 nd 101078 "Invalid expression \"%s\" in file %s",
1442     expr, r->filename);
1443 rbb 87067 *was_error = 1;
1444 nd 101116 return 0;
1445 fielding 83751 }
1446 nd 101078
1447 nd 101118 current->left->token.value =
1448     ap_ssi_parse_string(ctx, current->left->token.value, NULL, 0,
1449     SSI_EXPAND_DROP_NAME);
1450     current->right->token.value =
1451     ap_ssi_parse_string(ctx, current->right->token.value, NULL, 0,
1452     SSI_EXPAND_DROP_NAME);
1453 nd 101078
1454     current->value = strcmp(current->left->token.value,
1455     current->right->token.value);
1456    
1457 nd 101116 switch (current->token.type) {
1458     case TOKEN_GE: current->value = current->value >= 0; break;
1459     case TOKEN_GT: current->value = current->value > 0; break;
1460     case TOKEN_LE: current->value = current->value <= 0; break;
1461     case TOKEN_LT: current->value = current->value < 0; break;
1462     default: current->value = 0; break; /* should not happen */
1463 fielding 83751 }
1464     break;
1465    
1466 nd 101078 case TOKEN_NOT:
1467 nd 101115 case TOKEN_GROUP:
1468     if (current->right) {
1469     if (!current->right->done) {
1470     current = current->right;
1471     continue;
1472     }
1473     current->value = current->right->value;
1474     }
1475     else {
1476     current->value = 1;
1477     }
1478    
1479 nd 101116 if (current->token.type == TOKEN_NOT) {
1480     current->value = !current->value;
1481     }
1482 nd 101115 break;
1483    
1484 minfrin 571872 case TOKEN_ACCESS:
1485     if (current->left || !current->right ||
1486     (current->right->token.type != TOKEN_STRING &&
1487     current->right->token.type != TOKEN_RE)) {
1488     ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
1489     "Invalid expression \"%s\" in file %s: Token '-A' must be followed by a URI string.",
1490     expr, r->filename);
1491     *was_error = 1;
1492     return 0;
1493     }
1494     current->right->token.value =
1495     ap_ssi_parse_string(ctx, current->right->token.value, NULL, 0,
1496     SSI_EXPAND_DROP_NAME);
1497     rr = ap_sub_req_lookup_uri(current->right->token.value, r, NULL);
1498     /* 400 and higher are considered access denied */
1499     if (rr->status < HTTP_BAD_REQUEST) {
1500     current->value = 1;
1501     }
1502     else {
1503     current->value = 0;
1504     ap_log_rerror(APLOG_MARK, APLOG_DEBUG, rr->status, r,
1505     "mod_include: The tested "
1506     "subrequest -A \"%s\" returned an error code.",
1507     current->right->token.value);
1508     }
1509     ap_destroy_sub_req(rr);
1510     break;
1511    
1512 nd 101116 case TOKEN_RE:
1513     if (!error) {
1514     error = "No operator before regex in expr \"%s\" in file %s";
1515     }
1516 nd 101078 case TOKEN_LBRACE:
1517 nd 101116 if (!error) {
1518     error = "Unmatched '(' in \"%s\" in file %s";
1519     }
1520 fielding 83751 default:
1521 nd 101116 if (!error) {
1522     error = "internal parser error in \"%s\" in file %s";
1523     }
1524    
1525     ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, error, expr,r->filename);
1526 rbb 87067 *was_error = 1;
1527 nd 101116 return 0;
1528 fielding 83751 }
1529 nd 101116
1530     DEBUG_DUMP_EVAL(ctx, current);
1531     current->done = 1;
1532     current = current->parent;
1533 fielding 83751 }
1534    
1535 nd 101078 return (root ? root->value : 0);
1536 fielding 83751 }
1537    
1538 nd 101049
1539     /*
1540     * +-------------------------------------------------------+
1541     * | |
1542     * | Action Handlers
1543     * | |
1544     * +-------------------------------------------------------+
1545     */
1546    
1547     /*
1548     * Extract the next tag name and value.
1549     * If there are no more tags, set the tag name to NULL.
1550     * The tag value is html decoded if dodecode is non-zero.
1551     * The tag value may be NULL if there is no tag value..
1552     */
1553     static void ap_ssi_get_tag_and_value(include_ctx_t *ctx, char **tag,
1554     char **tag_val, int dodecode)
1555     {
1556     if (!ctx->intern->argv) {
1557     *tag = NULL;
1558     *tag_val = NULL;
1559    
1560     return;
1561     }
1562    
1563     *tag_val = ctx->intern->argv->value;
1564     *tag = ctx->intern->argv->name;
1565    
1566     ctx->intern->argv = ctx->intern->argv->next;
1567    
1568     if (dodecode && *tag_val) {
1569     decodehtml(*tag_val);
1570     }
1571    
1572     return;
1573     }
1574    
1575     static int find_file(request_rec *r, const char *directive, const char *tag,
1576     char *tag_val, apr_finfo_t *finfo)
1577     {
1578     char *to_send = tag_val;
1579     request_rec *rr = NULL;
1580     int ret=0;
1581     char *error_fmt = NULL;
1582     apr_status_t rv = APR_SUCCESS;
1583    
1584     if (!strcmp(tag, "file")) {
1585 nd 101080 char *newpath;
1586    
1587     /* be safe; only files in this directory or below allowed */
1588     rv = apr_filepath_merge(&newpath, NULL, tag_val,
1589     APR_FILEPATH_SECUREROOTTEST |
1590     APR_FILEPATH_NOTABSOLUTE, r->pool);
1591    
1592 jerenkrantz 104439 if (rv != APR_SUCCESS) {
1593 nd 101049 error_fmt = "unable to access file \"%s\" "
1594     "in parsed file %s";
1595     }
1596     else {
1597     /* note: it is okay to pass NULL for the "next filter" since
1598     we never attempt to "run" this sub request. */
1599 nd 101080 rr = ap_sub_req_lookup_file(newpath, r, NULL);
1600 nd 101049
1601     if (rr->status == HTTP_OK && rr->finfo.filetype != 0) {
1602     to_send = rr->filename;
1603 jim 332306 if ((rv = apr_stat(finfo, to_send,
1604 nd 101049 APR_FINFO_GPROT | APR_FINFO_MIN, rr->pool)) != APR_SUCCESS
1605     && rv != APR_INCOMPLETE) {
1606     error_fmt = "unable to get information about \"%s\" "
1607     "in parsed file %s";
1608     }
1609     }
1610     else {
1611     error_fmt = "unable to lookup information about \"%s\" "
1612     "in parsed file %s";
1613     }
1614     }
1615    
1616     if (error_fmt) {
1617     ret = -1;
1618     ap_log_rerror(APLOG_MARK, APLOG_ERR,
1619     rv, r, error_fmt, to_send, r->filename);
1620     }
1621    
1622     if (rr) ap_destroy_sub_req(rr);
1623 jim 332306
1624 nd 101049 return ret;
1625     }
1626     else if (!strcmp(tag, "virtual")) {
1627     /* note: it is okay to pass NULL for the "next filter" since
1628     we never attempt to "run" this sub request. */
1629     rr = ap_sub_req_lookup_uri(tag_val, r, NULL);
1630    
1631     if (rr->status == HTTP_OK && rr->finfo.filetype != 0) {
1632     memcpy((char *) finfo, (const char *) &rr->finfo,
1633     sizeof(rr->finfo));
1634     ap_destroy_sub_req(rr);
1635     return 0;
1636     }
1637     else {
1638 nd 101080 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "unable to get "
1639     "information about \"%s\" in parsed file %s",
1640     tag_val, r->filename);
1641 nd 101049 ap_destroy_sub_req(rr);
1642     return -1;
1643     }
1644     }
1645     else {
1646 nd 101080 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "unknown parameter \"%s\" "
1647     "to tag %s in %s", tag, directive, r->filename);
1648 nd 101049 return -1;
1649     }
1650     }
1651    
1652 nd 101053 /*
1653     * <!--#include virtual|file="..." [virtual|file="..."] ... -->
1654     */
1655 nd 101049 static apr_status_t handle_include(include_ctx_t *ctx, ap_filter_t *f,
1656     apr_bucket_brigade *bb)
1657     {
1658     request_rec *r = f->r;
1659    
1660 nd 101053 if (!ctx->argc) {
1661     ap_log_rerror(APLOG_MARK,
1662     (ctx->flags & SSI_FLAG_PRINTING)
1663     ? APLOG_ERR : APLOG_WARNING,
1664     0, r, "missing argument for include element in %s",
1665     r->filename);
1666     }
1667 nd 101049
1668 nd 101053 if (!(ctx->flags & SSI_FLAG_PRINTING)) {
1669     return APR_SUCCESS;
1670     }
1671 nd 101049
1672 nd 101053 if (!ctx->argc) {
1673     SSI_CREATE_ERROR_BUCKET(ctx, f, bb);
1674     return APR_SUCCESS;
1675     }
1676 nd 101049
1677 nd 101053 while (1) {
1678     char *tag = NULL;
1679     char *tag_val = NULL;
1680     request_rec *rr = NULL;
1681     char *error_fmt = NULL;
1682     char *parsed_string;
1683 nd 101049
1684 nd 101053 ap_ssi_get_tag_and_value(ctx, &tag, &tag_val, SSI_VALUE_DECODED);
1685     if (!tag || !tag_val) {
1686     break;
1687     }
1688 nd 101049
1689 nd 101053 if (strcmp(tag, "virtual") && strcmp(tag, "file")) {
1690     ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "unknown parameter "
1691     "\"%s\" to tag include in %s", tag, r->filename);
1692     SSI_CREATE_ERROR_BUCKET(ctx, f, bb);
1693     break;
1694     }
1695 nd 101049
1696 nd 101079 parsed_string = ap_ssi_parse_string(ctx, tag_val, NULL, 0,
1697 nd 101053 SSI_EXPAND_DROP_NAME);
1698     if (tag[0] == 'f') {
1699 nd 101080 char *newpath;
1700     apr_status_t rv;
1701    
1702     /* be safe; only files in this directory or below allowed */
1703 nd 102147 rv = apr_filepath_merge(&newpath, NULL, parsed_string,
1704 nd 101080 APR_FILEPATH_SECUREROOTTEST |
1705     APR_FILEPATH_NOTABSOLUTE, ctx->dpool);
1706    
1707 jerenkrantz 104439 if (rv != APR_SUCCESS) {
1708 nd 101053 error_fmt = "unable to include file \"%s\" in parsed file %s";
1709     }
1710     else {
1711 nd 101080 rr = ap_sub_req_lookup_file(newpath, r, f->next);
1712 nd 101053 }
1713     }
1714     else {
1715 minfrin 592951 if (r->kept_body) {
1716     rr = ap_sub_req_method_uri(r->method, parsed_string, r, f->next);
1717     }
1718     else {
1719     rr = ap_sub_req_lookup_uri(parsed_string, r, f->next);
1720     }
1721 nd 101053 }
1722 nd 101049
1723 nd 101053 if (!error_fmt && rr->status != HTTP_OK) {
1724     error_fmt = "unable to include \"%s\" in parsed file %s";
1725     }
1726 nd 101049
1727 nd 101053 if (!error_fmt && (ctx->flags & SSI_FLAG_NO_EXEC) &&
1728     rr->content_type && strncmp(rr->content_type, "text/", 5)) {
1729    
1730     error_fmt = "unable to include potential exec \"%s\" in parsed "
1731     "file %s";
1732     }
1733    
1734     /* See the Kludge in includes_filter for why.
1735     * Basically, it puts a bread crumb in here, then looks
1736     * for the crumb later to see if its been here.
1737     */
1738     if (rr) {
1739     ap_set_module_config(rr->request_config, &include_module, r);
1740     }
1741    
1742     if (!error_fmt && ap_run_sub_req(rr)) {
1743     error_fmt = "unable to include \"%s\" in parsed file %s";
1744     }
1745    
1746     if (error_fmt) {
1747     ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, error_fmt, tag_val,
1748     r->filename);
1749     SSI_CREATE_ERROR_BUCKET(ctx, f, bb);
1750     }
1751    
1752 jorton 179763 /* Do *not* destroy the subrequest here; it may have allocated
1753     * variables in this r->subprocess_env in the subrequest's
1754     * r->pool, so that pool must survive as long as this request.
1755     * Yes, this is a memory leak. */
1756 nd 101053
1757     if (error_fmt) {
1758     break;
1759     }
1760 nd 101049 }
1761    
1762     return APR_SUCCESS;
1763     }
1764    
1765 nd 101054 /*
1766     * <!--#echo [encoding="..."] var="..." [encoding="..."] var="..." ... -->
1767     */
1768 nd 101049 static apr_status_t handle_echo(include_ctx_t *ctx, ap_filter_t *f,
1769     apr_bucket_brigade *bb)
1770     {
1771     enum {E_NONE, E_URL, E_ENTITY} encode;
1772     request_rec *r = f->r;
1773    
1774 nd 101054 if (!ctx->argc) {
1775     ap_log_rerror(APLOG_MARK,
1776     (ctx->flags & SSI_FLAG_PRINTING)
1777     ? APLOG_ERR : APLOG_WARNING,
1778     0, r, "missing argument for echo element in %s",
1779     r->filename);
1780     }
1781    
1782     if (!(ctx->flags & SSI_FLAG_PRINTING)) {
1783     return APR_SUCCESS;
1784     }
1785    
1786     if (!ctx->argc) {
1787     SSI_CREATE_ERROR_BUCKET(ctx, f, bb);
1788     return APR_SUCCESS;
1789     }
1790    
1791 nd 101049 encode = E_ENTITY;
1792    
1793 nd 101054 while (1) {
1794     char *tag = NULL;
1795     char *tag_val = NULL;
1796 nd 101049
1797 nd 101054 ap_ssi_get_tag_and_value(ctx, &tag, &tag_val, SSI_VALUE_DECODED);
1798     if (!tag || !tag_val) {
1799     break;
1800     }
1801 nd 101049
1802 nd 101054 if (!strcmp(tag, "var")) {
1803     const char *val;
1804     const char *echo_text = NULL;
1805     apr_size_t e_len;
1806    
1807 nd 101070 val = get_include_var(ap_ssi_parse_string(ctx, tag_val, NULL,
1808 nd 101079 0, SSI_EXPAND_DROP_NAME),
1809 nd 101070 ctx);
1810    
1811 nd 101054 if (val) {
1812     switch(encode) {
1813     case E_NONE:
1814     echo_text = val;
1815     break;
1816     case E_URL:
1817     echo_text = ap_escape_uri(ctx->dpool, val);
1818     break;
1819     case E_ENTITY:
1820     echo_text = ap_escape_html(ctx->dpool, val);
1821     break;
1822 nd 101049 }
1823 nd 101054
1824     e_len = strlen(echo_text);
1825     }
1826     else {
1827 nd 101129 echo_text = ctx->intern->undefined_echo;
1828     e_len = ctx->intern->undefined_echo_len;
1829 nd 101049 }
1830 nd 101054
1831     APR_BRIGADE_INSERT_TAIL(bb, apr_bucket_pool_create(
1832 nd 101129 apr_pmemdup(ctx->pool, echo_text, e_len),
1833 nd 101054 e_len, ctx->pool, f->c->bucket_alloc));
1834     }
1835     else if (!strcmp(tag, "encoding")) {
1836     if (!strcasecmp(tag_val, "none")) {
1837     encode = E_NONE;
1838 nd 101049 }
1839 nd 101054 else if (!strcasecmp(tag_val, "url")) {
1840     encode = E_URL;
1841     }
1842     else if (!strcasecmp(tag_val, "entity")) {
1843     encode = E_ENTITY;
1844     }
1845 nd 101049 else {
1846 nd 101054 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "unknown value "
1847     "\"%s\" to parameter \"encoding\" of tag echo in "
1848     "%s", tag_val, r->filename);
1849 nd 101049 SSI_CREATE_ERROR_BUCKET(ctx, f, bb);
1850 nd 101054 break;
1851 nd 101049 }
1852     }
1853 nd 101054 else {
1854     ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "unknown parameter "
1855     "\"%s\" in tag echo of %s", tag, r->filename);
1856     SSI_CREATE_ERROR_BUCKET(ctx, f, bb);
1857     break;
1858     }
1859 nd 101049 }
1860    
1861     return APR_SUCCESS;
1862     }
1863    
1864 nd 101055 /*
1865 nd 101130 * <!--#config [timefmt="..."] [sizefmt="..."] [errmsg="..."]
1866     * [echomsg="..."] -->
1867 nd 101049 */
1868     static apr_status_t handle_config(include_ctx_t *ctx, ap_filter_t *f,
1869     apr_bucket_brigade *bb)
1870     {
1871