]> granicus.if.org Git - apache/blob - server/util_expr.c
Turn static function get_server_name_for_url() into public function
[apache] / server / util_expr.c
1 /* Licensed to the Apache Software Foundation (ASF) under one or more
2  * contributor license agreements.  See the NOTICE file distributed with
3  * this work for additional information regarding copyright ownership.
4  * The ASF licenses this file to You under the Apache License, Version 2.0
5  * (the "License"); you may not use this file except in compliance with
6  * the License.  You may obtain a copy of the License at
7  *
8  *     http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16
17 #include "apr.h"
18 #include "apr_strings.h"
19 #include "apr_lib.h"
20
21 #define APR_WANT_STRFUNC
22 #define APR_WANT_MEMFUNC
23 #include "apr_want.h"
24
25 #include "httpd.h"
26 #include "http_log.h"
27 #include "http_core.h"
28
29 #include "ap_expr.h"
30 #if 1
31 /*
32  * +-------------------------------------------------------+
33  * |                                                       |
34  * |                  Debugging Utilities
35  * |                                                       |
36  * +-------------------------------------------------------+
37  */
38
39 #ifdef DEBUG_INCLUDE
40
41 #define TYPE_TOKEN(token, ttype) do { \
42     (token)->type = ttype;            \
43     (token)->s = #ttype;              \
44 } while(0)
45
46 #define CREATE_NODE(pool,name) do {                          \
47     (name) = apr_pcalloc(pool, sizeof(*(name)));
48 } while(0)
49
50 static void debug_printf(request_rec *r, const char *fmt, ...)
51 {
52     va_list ap;
53     char *debug__str;
54
55     va_start(ap, fmt);
56     debug__str = apr_pvsprintf(r->pool, fmt, ap);
57     va_end(ap);
58 /*
59     APR_BRIGADE_INSERT_TAIL(ctx->intern->debug.bb, apr_bucket_pool_create(
60                             debug__str, strlen(debug__str), ctx->pool,
61                             ctx->intern->debug.f->c->bucket_alloc));
62                             */
63 }
64
65 #define DUMP__CHILD(ctx, is, node, child) if (1) {                           \
66     ap_parse_node_t *d__c = node->child;                                     \
67     if (d__c) {                                                              \
68         if (!d__c->dump_done) {                                              \
69             if (d__c->parent != node) {                                      \
70                 debug_printf(ctx, "!!! Parse tree is not consistent !!!\n"); \
71                 if (!d__c->parent) {                                         \
72                     debug_printf(ctx, "Parent of " #child " child node is "  \
73                                  "NULL.\n");                                 \
74                 }                                                            \
75                 else {                                                       \
76                     debug_printf(ctx, "Parent of " #child " child node "     \
77                                  "points to another node (of type %s)!\n",   \
78                                  d__c->parent->token.s);                     \
79                 }                                                            \
80                 return;                                                      \
81             }                                                                \
82             node = d__c;                                                     \
83             continue;                                                        \
84         }                                                                    \
85     }                                                                        \
86     else {                                                                   \
87         debug_printf(ctx, "%s(missing)\n", is);                              \
88     }                                                                        \
89 }
90
91 static void debug_dump_tree(include_ctx_t *ctx, ap_parse_node_t *root)
92 {
93     ap_parse_node_t *current;
94     char *is;
95
96     if (!root) {
97         debug_printf(ctx, "     -- Parse Tree empty --\n\n");
98         return;
99     }
100
101     debug_printf(ctx, "     ----- Parse Tree -----\n");
102     current = root;
103     is = "     ";
104
105     while (current) {
106         switch (current->token.type) {
107         case TOKEN_STRING:
108         case TOKEN_RE:
109             debug_printf(ctx, "%s%s (%s)\n", is, current->token.s,
110                          current->token.value);
111             current->dump_done = 1;
112             current = current->parent;
113             continue;
114
115         case TOKEN_NOT:
116         case TOKEN_GROUP:
117         case TOKEN_RBRACE:
118         case TOKEN_LBRACE:
119             if (!current->dump_done) {
120                 debug_printf(ctx, "%s%s\n", is, current->token.s);
121                 is = apr_pstrcat(ctx->dpool, is, "    ", NULL);
122                 current->dump_done = 1;
123             }
124
125             DUMP__CHILD(ctx, is, current, right)
126
127             if (!current->right || current->right->dump_done) {
128                 is = apr_pstrmemdup(ctx->dpool, is, strlen(is) - 4);
129                 if (current->right) current->right->dump_done = 0;
130                 current = current->parent;
131             }
132             continue;
133
134         default:
135             if (!current->dump_done) {
136                 debug_printf(ctx, "%s%s\n", is, current->token.s);
137                 is = apr_pstrcat(ctx->dpool, is, "    ", NULL);
138                 current->dump_done = 1;
139             }
140
141             DUMP__CHILD(ctx, is, current, left)
142             DUMP__CHILD(ctx, is, current, right)
143
144             if ((!current->left || current->left->dump_done) &&
145                 (!current->right || current->right->dump_done)) {
146
147                 is = apr_pstrmemdup(ctx->dpool, is, strlen(is) - 4);
148                 if (current->left) current->left->dump_done = 0;
149                 if (current->right) current->right->dump_done = 0;
150                 current = current->parent;
151             }
152             continue;
153         }
154     }
155
156     /* it is possible to call this function within the parser loop, to see
157      * how the tree is built. That way, we must cleanup after us to dump
158      * always the whole tree
159      */
160     root->dump_done = 0;
161     if (root->left) root->left->dump_done = 0;
162     if (root->right) root->right->dump_done = 0;
163
164     debug_printf(ctx, "     --- End Parse Tree ---\n\n");
165
166     return;
167 }
168
169 #define DEBUG_INIT(ctx, filter, brigade) do { \
170     (ctx)->intern->debug.f = filter;          \
171     (ctx)->intern->debug.bb = brigade;        \
172 } while(0)
173
174 #define DEBUG_PRINTF(arg) debug_printf arg
175
176 #define DEBUG_DUMP_TOKEN(ctx, token) do {                                     \
177     token_t *d__t = (token);                                                  \
178                                                                               \
179     if (d__t->type == TOKEN_STRING || d__t->type == TOKEN_RE) {               \
180         DEBUG_PRINTF(((ctx), "     Found: %s (%s)\n", d__t->s, d__t->value)); \
181     }                                                                         \
182     else {                                                                    \
183         DEBUG_PRINTF((ctx, "     Found: %s\n", d__t->s));                     \
184     }                                                                         \
185 } while(0)
186
187 #define DEBUG_DUMP_EVAL(r, node) do {                                       \
188     char c = '"';                                                             \
189     switch ((node)->token.type) {                                             \
190     case TOKEN_STRING:                                                        \
191         debug_printf((r), "     Evaluate: %s (%s) -> %c\n", (node)->token.s,\
192                      (node)->token.value, ((node)->value) ? '1':'0');         \
193         break;                                                                \
194     case TOKEN_AND:                                                           \
195     case TOKEN_OR:                                                            \
196         debug_printf((r), "     Evaluate: %s (Left: %s; Right: %s) -> %c\n",\
197                      (node)->token.s,                                         \
198                      (((node)->left->done) ? ((node)->left->value ?"1":"0")   \
199                                           : "short circuited"),               \
200                      (((node)->right->done) ? ((node)->right->value?"1":"0")  \
201                                           : "short circuited"),               \
202                      (node)->value ? '1' : '0');                              \
203         break;                                                                \
204     case TOKEN_EQ:                                                            \
205     case TOKEN_NE:                                                            \
206     case TOKEN_GT:                                                            \
207     case TOKEN_GE:                                                            \
208     case TOKEN_LT:                                                            \
209     case TOKEN_LE:                                                            \
210         if ((node)->right->token.type == TOKEN_RE) c = '/';                   \
211         debug_printf((r), "     Compare:  %s (\"%s\" with %c%s%c) -> %c\n", \
212                      (node)->token.s,                                         \
213                      (node)->left->token.value,                               \
214                      c, (node)->right->token.value, c,                        \
215                      (node)->value ? '1' : '0');                              \
216         break;                                                                \
217     default:                                                                  \
218         debug_printf((r), "     Evaluate: %s -> %c\n", (node)->token.s,     \
219                      (node)->value ? '1' : '0');                              \
220         break;                                                                \
221     }                                                                         \
222 } while(0)
223
224 #define DEBUG_DUMP_UNMATCHED(r, unmatched) do {                        \
225     if (unmatched) {                                                     \
226         DEBUG_PRINTF(((r), "     Unmatched %c\n", (char)(unmatched))); \
227     }                                                                    \
228 } while(0)
229
230 #define DEBUG_DUMP_COND(ctx, text)                                 \
231     DEBUG_PRINTF(((ctx), "**** %s cond status=\"%c\"\n", (text),   \
232                   ((ctx)->flags & SSI_FLAG_COND_TRUE) ? '1' : '0'))
233
234 #define DEBUG_DUMP_TREE(ctx, root) debug_dump_tree(ctx, root)
235
236 #else /* DEBUG_INCLUDE */
237
238 #define TYPE_TOKEN(token, ttype) (token)->type = ttype
239
240 #define CREATE_NODE(pool,name) do {               \
241     (name) = apr_pcalloc(pool, sizeof(*(name)));  \
242 } while(0)
243
244 #define DEBUG_INIT(ctx, f, bb)
245 #define DEBUG_PRINTF(arg)
246 #define DEBUG_DUMP_TOKEN(ctx, token)
247 #define DEBUG_DUMP_EVAL(ctx, node)
248 #define DEBUG_DUMP_UNMATCHED(ctx, unmatched)
249 #define DEBUG_DUMP_COND(ctx, text)
250 #define DEBUG_DUMP_TREE(ctx, root)
251
252 #endif /* !DEBUG_INCLUDE */
253
254 #endif /* 0 */
255
256
257 /*
258  * +-------------------------------------------------------+
259  * |                                                       |
260  * |              Conditional Expression Parser
261  * |                                                       |
262  * +-------------------------------------------------------+
263  */
264 static APR_INLINE int re_check(request_rec *r, const char *string,
265                                const char *rexp, backref_t **reptr)
266 {
267     ap_regex_t *compiled;
268     backref_t *re = reptr ? *reptr : NULL;
269
270     compiled = ap_pregcomp(r->pool, rexp, AP_REG_EXTENDED);
271     if (!compiled) {
272         ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "unable to "
273                       "compile pattern \"%s\"", rexp);
274         return -1;
275     }
276
277     if (!re) {
278         re = apr_palloc(r->pool, sizeof(*re));
279         if (reptr) {
280             *reptr = re;
281         }
282     }
283
284     re->source = apr_pstrdup(r->pool, string);
285     re->rexp = apr_pstrdup(r->pool, rexp);
286     re->nsub = compiled->re_nsub;
287     re->have_match = !ap_regexec(compiled, string, AP_MAX_REG_MATCH,
288                                  re->match, 0);
289
290     ap_pregfree(r->pool, compiled);
291     return re->have_match;
292 }
293
294 static int get_ptoken(apr_pool_t *pool, const char **parse, token_t *token,
295                       token_t *previous)
296 {
297     const char *p;
298     apr_size_t shift;
299     int unmatched;
300
301     token->value = NULL;
302
303     if (!*parse) {
304         return 0;
305     }
306
307     /* Skip leading white space */
308     while (apr_isspace(**parse)) {
309         ++*parse;
310     }
311
312     if (!**parse) {
313         *parse = NULL;
314         return 0;
315     }
316
317     TYPE_TOKEN(token, TOKEN_STRING); /* the default type */
318     p = *parse;
319     unmatched = 0;
320
321     switch (*(*parse)++) {
322     case '(':
323         TYPE_TOKEN(token, TOKEN_LBRACE);
324         return 0;
325     case ')':
326         TYPE_TOKEN(token, TOKEN_RBRACE);
327         return 0;
328     case '=':
329         if (**parse == '=') ++*parse;
330         TYPE_TOKEN(token, TOKEN_EQ);
331         return 0;
332     case '!':
333         if (**parse == '=') {
334             TYPE_TOKEN(token, TOKEN_NE);
335             ++*parse;
336             return 0;
337         }
338         TYPE_TOKEN(token, TOKEN_NOT);
339         return 0;
340     case '\'':
341         unmatched = '\'';
342         break;
343     case '/':
344         /* if last token was ACCESS, this token is STRING */
345         if (previous != NULL && TOKEN_ACCESS == previous->type) {
346             break;
347         }
348         TYPE_TOKEN(token, TOKEN_RE);
349         unmatched = '/';
350         break;
351     case '|':
352         if (**parse == '|') {
353             TYPE_TOKEN(token, TOKEN_OR);
354             ++*parse;
355             return 0;
356         }
357         break;
358     case '&':
359         if (**parse == '&') {
360             TYPE_TOKEN(token, TOKEN_AND);
361             ++*parse;
362             return 0;
363         }
364         break;
365     case '>':
366         if (**parse == '=') {
367             TYPE_TOKEN(token, TOKEN_GE);
368             ++*parse;
369             return 0;
370         }
371         TYPE_TOKEN(token, TOKEN_GT);
372         return 0;
373     case '<':
374         if (**parse == '=') {
375             TYPE_TOKEN(token, TOKEN_LE);
376             ++*parse;
377             return 0;
378         }
379         TYPE_TOKEN(token, TOKEN_LT);
380         return 0;
381     case '-':
382         if (apr_isalnum(**parse) && apr_isspace((*parse)[1])) {
383             TYPE_TOKEN(token, TOKEN_ACCESS);
384             token->value = *parse;
385             ++*parse;
386             return 0;
387         }
388         break;
389     }
390
391     /* It's a string or regex token
392      * Now search for the next token, which finishes this string
393      */
394     shift = 0;
395     p = *parse = token->value = unmatched ? *parse : p;
396
397     for (; **parse; p = ++*parse) {
398         if (**parse == '\\') {
399             if (!*(++*parse)) {
400                 p = *parse;
401                 break;
402             }
403
404             ++shift;
405         }
406         else {
407             if (unmatched) {
408                 if (**parse == unmatched) {
409                     unmatched = 0;
410                     ++*parse;
411                     break;
412                 }
413             }
414             else if (apr_isspace(**parse)) {
415                 break;
416             }
417             else {
418                 int found = 0;
419
420                 switch (**parse) {
421                 case '(':
422                 case ')':
423                 case '=':
424                 case '!':
425                 case '<':
426                 case '>':
427                     ++found;
428                     break;
429
430                 case '|':
431                 case '&':
432                     if ((*parse)[1] == **parse) {
433                         ++found;
434                     }
435                     break;
436                 }
437
438                 if (found) {
439                     break;
440                 }
441             }
442         }
443     }
444
445     if (unmatched) {
446         token->value = apr_pstrdup(pool, "");
447     }
448     else {
449         apr_size_t len = p - token->value - shift;
450         char *c = apr_palloc(pool, len + 1);
451
452         p = token->value;
453         token->value = c;
454
455         while (shift--) {
456             const char *e = ap_strchr_c(p, '\\');
457
458             memcpy(c, p, e-p);
459             c   += e-p;
460             *c++ = *++e;
461             len -= e-p;
462             p    = e+1;
463         }
464
465         if (len) {
466             memcpy(c, p, len);
467         }
468         c[len] = '\0';
469     }
470
471     return unmatched;
472 }
473
474 /* This is what we export.  We can split it in two. */
475 AP_DECLARE(ap_parse_node_t*) ap_expr_parse(apr_pool_t* pool, const char *expr,
476                                            int *was_error)
477 {
478     ap_parse_node_t *new, *root = NULL, *current = NULL;
479     const char *error = "Invalid expression \"%s\" in file %s";
480     const char *parse = expr;
481     int was_unmatched = 0;
482     unsigned regex = 0;
483
484     *was_error = 0;
485
486     if (!parse) {
487         return 0;
488     }
489
490     /* Create Parse Tree */
491     while (1) {
492         /* uncomment this to see how the tree a built:
493          *
494          * DEBUG_DUMP_TREE(ctx, root);
495          */
496         CREATE_NODE(pool, new);
497
498         was_unmatched = get_ptoken(pool, &parse, &new->token,
499                      (current != NULL ? &current->token : NULL));
500         if (!parse) {
501             break;
502         }
503
504         DEBUG_DUMP_UNMATCHED(ctx, was_unmatched);
505         DEBUG_DUMP_TOKEN(ctx, &new->token);
506
507         if (!current) {
508             switch (new->token.type) {
509             case TOKEN_STRING:
510             case TOKEN_NOT:
511             case TOKEN_ACCESS:
512             case TOKEN_LBRACE:
513                 root = current = new;
514                 continue;
515
516             default:
517                 *was_error = 1;
518                 return 0;
519             }
520         }
521
522         switch (new->token.type) {
523         case TOKEN_STRING:
524             switch (current->token.type) {
525             case TOKEN_STRING:
526                 current->token.value =
527                     apr_pstrcat(pool, current->token.value,
528                                 *current->token.value ? " " : "",
529                                 new->token.value, NULL);
530                 continue;
531
532             case TOKEN_RE:
533             case TOKEN_RBRACE:
534             case TOKEN_GROUP:
535                 break;
536
537             default:
538                 new->parent = current;
539                 current = current->right = new;
540                 continue;
541             }
542             break;
543
544         case TOKEN_RE:
545             switch (current->token.type) {
546             case TOKEN_EQ:
547             case TOKEN_NE:
548                 new->parent = current;
549                 current = current->right = new;
550                 ++regex;
551                 continue;
552
553             default:
554                 break;
555             }
556             break;
557
558         case TOKEN_AND:
559         case TOKEN_OR:
560             switch (current->token.type) {
561             case TOKEN_STRING:
562             case TOKEN_RE:
563             case TOKEN_GROUP:
564                 current = current->parent;
565
566                 while (current) {
567                     switch (current->token.type) {
568                     case TOKEN_AND:
569                     case TOKEN_OR:
570                     case TOKEN_LBRACE:
571                         break;
572
573                     default:
574                         current = current->parent;
575                         continue;
576                     }
577                     break;
578                 }
579
580                 if (!current) {
581                     new->left = root;
582                     root->parent = new;
583                     current = root = new;
584                     continue;
585                 }
586
587                 new->left = current->right;
588                 new->left->parent = new;
589                 new->parent = current;
590                 current = current->right = new;
591                 continue;
592
593             default:
594                 break;
595             }
596             break;
597
598         case TOKEN_EQ:
599         case TOKEN_NE:
600         case TOKEN_GE:
601         case TOKEN_GT:
602         case TOKEN_LE:
603         case TOKEN_LT:
604             if (current->token.type == TOKEN_STRING) {
605                 current = current->parent;
606
607                 if (!current) {
608                     new->left = root;
609                     root->parent = new;
610                     current = root = new;
611                     continue;
612                 }
613
614                 switch (current->token.type) {
615                 case TOKEN_LBRACE:
616                 case TOKEN_AND:
617                 case TOKEN_OR:
618                     new->left = current->right;
619                     new->left->parent = new;
620                     new->parent = current;
621                     current = current->right = new;
622                     continue;
623
624                 default:
625                     break;
626                 }
627             }
628             break;
629
630         case TOKEN_RBRACE:
631             while (current && current->token.type != TOKEN_LBRACE) {
632                 current = current->parent;
633             }
634
635             if (current) {
636                 TYPE_TOKEN(&current->token, TOKEN_GROUP);
637                 continue;
638             }
639
640             error = "Unmatched ')' in \"%s\" in file %s";
641             break;
642
643         case TOKEN_NOT:
644         case TOKEN_ACCESS:
645         case TOKEN_LBRACE:
646             switch (current->token.type) {
647             case TOKEN_STRING:
648             case TOKEN_RE:
649             case TOKEN_RBRACE:
650             case TOKEN_GROUP:
651                 break;
652
653             default:
654                 current->right = new;
655                 new->parent = current;
656                 current = new;
657                 continue;
658             }
659             break;
660
661         default:
662             break;
663         }
664
665         *was_error = 1;
666         return 0;
667     }
668
669     DEBUG_DUMP_TREE(ctx, root);
670     return root;
671 }
672
673 static ap_parse_node_t *ap_expr_clone_tree(apr_pool_t *pool,
674                                            ap_parse_node_t *pnode,
675                                            ap_parse_node_t *parent)
676 {
677     ap_parse_node_t *ret;
678     ret = apr_pmemdup(pool, pnode, sizeof(ap_parse_node_t));
679     if (pnode->left) {
680         ret->left = ap_expr_clone_tree(pool, pnode->left, ret);
681     }
682     if (pnode->right) {
683         ret->right = ap_expr_clone_tree(pool, pnode->right, ret);
684     }
685     ret->parent = parent;
686     return ret;
687 }
688
689 #define PARSE_STRING(r,s) (string_func ? string_func((r),(s)) : (s))
690 static int expr_eval(request_rec *r, ap_parse_node_t *root,
691                      int *was_error, backref_t **reptr,
692                      string_func_t string_func, opt_func_t eval_func)
693 {
694     ap_parse_node_t *current = root;
695     const char *error = NULL;
696     unsigned int regex = 0;
697     const char *val;
698     const char *lval;
699     const char *rval;
700
701     /* Evaluate Parse Tree */
702     while (current) {
703         switch (current->token.type) {
704         case TOKEN_STRING:
705             val = PARSE_STRING(r, current->token.value);
706             current->value = !!*val;
707             break;
708
709         case TOKEN_AND:
710         case TOKEN_OR:
711             if (!current->left || !current->right) {
712                 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
713                               "Invalid expression in file %s", r->filename);
714                 *was_error = 1;
715                 return 0;
716             }
717
718             if (!current->left->done) {
719                 switch (current->left->token.type) {
720                 case TOKEN_STRING:
721                     lval = PARSE_STRING(r, current->left->token.value);
722                     current->left->value = !!*lval;
723                     DEBUG_DUMP_EVAL(ctx, current->left);
724                     current->left->done = 1;
725                     break;
726
727                 default:
728                     current = current->left;
729                     continue;
730                 }
731             }
732
733             /* short circuit evaluation */
734             if (!current->right->done && !regex &&
735                 ((current->token.type == TOKEN_AND && !current->left->value) ||
736                 (current->token.type == TOKEN_OR && current->left->value))) {
737                 current->value = current->left->value;
738             }
739             else {
740                 if (!current->right->done) {
741                     switch (current->right->token.type) {
742                     case TOKEN_STRING:
743                         rval = PARSE_STRING(r,current->right->token.value);
744                         current->right->value = !!*rval;
745                         DEBUG_DUMP_EVAL(r, current->right);
746                         current->right->done = 1;
747                         break;
748
749                     default:
750                         current = current->right;
751                         continue;
752                     }
753                 }
754
755                 if (current->token.type == TOKEN_AND) {
756                     current->value = current->left->value &&
757                                      current->right->value;
758                 }
759                 else {
760                     current->value = current->left->value ||
761                                      current->right->value;
762                 }
763             }
764             break;
765
766         case TOKEN_EQ:
767         case TOKEN_NE:
768             if (!current->left || !current->right ||
769                 current->left->token.type != TOKEN_STRING ||
770                 (current->right->token.type != TOKEN_STRING &&
771                  current->right->token.type != TOKEN_RE)) {
772                 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
773                             "Invalid expression in file %s", r->filename);
774                 *was_error = 1;
775                 return 0;
776             }
777             lval = PARSE_STRING(r, current->left->token.value);
778             rval = PARSE_STRING(r, current->right->token.value);
779
780             if (current->right->token.type == TOKEN_RE) {
781                 current->value = re_check(r, lval, rval, reptr);
782                 --regex;
783             }
784             else {
785                 current->value = !strcmp(lval, rval);
786             }
787
788             if (current->token.type == TOKEN_NE) {
789                 current->value = !current->value;
790             }
791             break;
792
793         case TOKEN_GE:
794         case TOKEN_GT:
795         case TOKEN_LE:
796         case TOKEN_LT:
797             if (!current->left || !current->right ||
798                 current->left->token.type != TOKEN_STRING ||
799                 current->right->token.type != TOKEN_STRING) {
800                 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
801                               "Invalid expression in file %s", r->filename);
802                 *was_error = 1;
803                 return 0;
804             }
805
806             lval = PARSE_STRING(r, current->left->token.value);
807             rval = PARSE_STRING(r, current->right->token.value);
808
809             current->value = strcmp(lval, rval);
810
811             switch (current->token.type) {
812             case TOKEN_GE: current->value = current->value >= 0; break;
813             case TOKEN_GT: current->value = current->value >  0; break;
814             case TOKEN_LE: current->value = current->value <= 0; break;
815             case TOKEN_LT: current->value = current->value <  0; break;
816             default: current->value = 0; break; /* should not happen */
817             }
818             break;
819
820         case TOKEN_NOT:
821         case TOKEN_GROUP:
822             if (current->right) {
823                 if (!current->right->done) {
824                     current = current->right;
825                     continue;
826                 }
827                 current->value = current->right->value;
828             }
829             else {
830                 current->value = 1;
831             }
832
833             if (current->token.type == TOKEN_NOT) {
834                 current->value = !current->value;
835             }
836             break;
837         case TOKEN_ACCESS:
838             if (eval_func) {
839                 *was_error = eval_func(r, current, string_func);
840                 if (*was_error) {
841                     return 0;
842                 }
843             }
844             break;
845
846         case TOKEN_RE:
847             if (!error) {
848                 error = "No operator before regex in file %s";
849             }
850         case TOKEN_LBRACE:
851             if (!error) {
852                 error = "Unmatched '(' in file %s";
853             }
854         default:
855             if (!error) {
856                 error = "internal parser error in file %s";
857             }
858
859             ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, error, r->filename);
860             *was_error = 1;
861             return 0;
862         }
863
864         DEBUG_DUMP_EVAL(r, current);
865         current->done = 1;
866         current = current->parent;
867     }
868
869     return (root ? root->value : 0);
870 }
871 AP_DECLARE(int) ap_expr_eval(request_rec *r, ap_parse_node_t *root,
872                              int *was_error, backref_t **reptr,
873                              string_func_t string_func, opt_func_t eval_func)
874 {
875     ap_parse_node_t *clone;
876     if (root == NULL) {  /* no condition == unconditional */
877         return 1;
878     }
879     clone = ap_expr_clone_tree(r->pool, root, NULL);
880     return expr_eval(r, clone, was_error, reptr, string_func, eval_func);
881 }
882 AP_DECLARE(int) ap_expr_evalstring(request_rec *r, const char *expr,
883                                    int *was_error, backref_t **reptr,
884                                    string_func_t string_func,
885                                    opt_func_t eval_func)
886 {
887     ap_parse_node_t *root = ap_expr_parse(r->pool, expr, was_error);
888     if (*was_error || !root) {
889         ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
890                       "Error parsing expression in %s", r->filename);   
891         return 0;
892     }
893     return expr_eval(r, root, was_error, reptr, string_func, eval_func);
894 }
895
896
897 static ap_regex_t *isvar = NULL;
898 AP_DECLARE_NONSTD(const char*) ap_expr_string(request_rec *r, 
899                                               const char *str)
900 {
901     /* a default string evaluator: support headers and env */
902     const char *ret = str;
903     ap_regmatch_t match[3];
904     const char *p;
905
906     ap_assert(isvar != NULL);
907     if (ap_regexec(isvar, str, 3, match, 0) == 0) {
908         apr_table_t *table = NULL;
909         int len = match[1].rm_eo-match[1].rm_so;
910         const char *name = str+match[1].rm_so;
911         if (!strncasecmp("req", name, len)) {
912             table = r->headers_in;
913         }
914         else if (!strncasecmp("resp", name, len)) {
915             table = r->headers_out;
916         }
917         else if (!strncasecmp("env", name, len)) {
918             table = r->subprocess_env;
919         }
920         if (table != NULL) {
921             char *key = apr_pstrndup(r->pool, str+match[2].rm_so,
922                                      match[2].rm_eo-match[2].rm_so);
923             ret = apr_table_get(table, key);
924         }
925     }
926     else if (str[0] == '$') {
927         if (!strcasecmp(str, "$handler")) {
928             ret = r->handler;
929         }
930         else if (!strcasecmp(str, "$content-type")) {
931             ret = r->content_type;
932         }
933     }
934
935     /* copy wholesale from mod_rewrite to support its %{varname} vars */
936     else if ((str[0] == '%') && (str[1] == '{')
937              && (p = ap_strchr_c(str, '}'), p != NULL)) {
938         char *ch, *var;
939         apr_time_exp_t tm;
940
941         var = apr_pstrndup(r->pool, str+2, p-str-3);
942         for (ch = var; *ch; ++ch) {
943             *ch = apr_toupper(*ch);
944         }
945
946         switch (strlen(var)) {
947         case  4:
948             if (!strcmp(var, "TIME")) {
949                 apr_time_exp_lt(&tm, apr_time_now());
950                 ret = apr_psprintf(r->pool, "%04d%02d%02d%02d%02d%02d",
951                                       tm.tm_year+1900, tm.tm_mon+1, tm.tm_mday,
952                                       tm.tm_hour, tm.tm_min, tm.tm_sec);
953                 return (char *)ret;
954             }
955             else if (!strcmp(var, "IPV6")) {
956                 int flag = FALSE;
957 #if APR_HAVE_IPV6
958                 apr_sockaddr_t *addr = r->connection->remote_addr;
959                 flag = (addr->family == AF_INET6 &&
960                         !IN6_IS_ADDR_V4MAPPED((struct in6_addr *)addr->ipaddr_ptr));
961 #endif
962                 ret = (flag ? "on" : "off");
963             }
964             break;
965
966 #if FIXME
967         case  5:
968             if (!strcmp(var, "HTTPS")) {
969                 int flag = rewrite_is_https && rewrite_is_https(r->connection);
970                 return apr_pstrdup(r->pool, flag ? "on" : "off");
971             }
972             break;
973 #endif
974         case  8:
975             switch (var[6]) {
976             case 'A':
977                 if (!strcmp(var, "TIME_DAY")) {
978                     apr_time_exp_lt(&tm, apr_time_now());
979                     return apr_psprintf(r->pool, "%02d", tm.tm_mday);
980                 }
981                 break;
982
983             case 'E':
984                 if (!strcmp(var, "TIME_SEC")) {
985                     apr_time_exp_lt(&tm, apr_time_now());
986                     return apr_psprintf(r->pool, "%02d", tm.tm_sec);
987                 }
988                 break;
989
990             case 'I':
991                 if (!strcmp(var, "TIME_MIN")) {
992                     apr_time_exp_lt(&tm, apr_time_now());
993                     return apr_psprintf(r->pool, "%02d", tm.tm_min);
994                 }
995                 break;
996
997             case 'O':
998                 if (!strcmp(var, "TIME_MON")) {
999                     apr_time_exp_lt(&tm, apr_time_now());
1000                     return apr_psprintf(r->pool, "%02d", tm.tm_mon+1);
1001                 }
1002                 break;
1003             }
1004             break;
1005
1006         case  9:
1007             switch (var[7]) {
1008             case 'A':
1009                 if (var[8] == 'Y' && !strcmp(var, "TIME_WDAY")) {
1010                     apr_time_exp_lt(&tm, apr_time_now());
1011                     return apr_psprintf(r->pool, "%d", tm.tm_wday);
1012                 }
1013                 else if (!strcmp(var, "TIME_YEAR")) {
1014                     apr_time_exp_lt(&tm, apr_time_now());
1015                     return apr_psprintf(r->pool, "%04d", tm.tm_year+1900);
1016                 }
1017                 break;
1018
1019             case 'E':
1020                 if (!strcmp(var, "IS_SUBREQ")) {
1021                     ret = (r->main ? "true" : "false");
1022                 }
1023                 break;
1024
1025             case 'F':
1026                 if (!strcmp(var, "PATH_INFO")) {
1027                     ret = r->path_info;
1028                 }
1029                 break;
1030
1031             case 'P':
1032                 if (!strcmp(var, "AUTH_TYPE")) {
1033                     ret = r->ap_auth_type;
1034                 }
1035                 break;
1036
1037             case 'S':
1038                 if (!strcmp(var, "HTTP_HOST")) {
1039                     ret = apr_table_get(r->headers_in, "Host");
1040                 }
1041                 break;
1042
1043             case 'U':
1044                 if (!strcmp(var, "TIME_HOUR")) {
1045                     apr_time_exp_lt(&tm, apr_time_now());
1046                     return apr_psprintf(r->pool, "%02d", tm.tm_hour);
1047                 }
1048                 break;
1049             }
1050             break;
1051
1052         case 11:
1053             switch (var[8]) {
1054             case 'A':
1055                 if (!strcmp(var, "SERVER_NAME")) {
1056                     ret = ap_get_server_name(r);
1057                 }
1058                 break;
1059
1060             case 'D':
1061                 if (*var == 'R' && !strcmp(var, "REMOTE_ADDR")) {
1062                     ret = r->connection->remote_ip;
1063                 }
1064                 else if (!strcmp(var, "SERVER_ADDR")) {
1065                     ret = r->connection->local_ip;
1066                 }
1067                 break;
1068
1069             case 'E':
1070                 if (*var == 'H' && !strcmp(var, "HTTP_ACCEPT")) {
1071                     ret = apr_table_get(r->headers_in, "Accept");
1072                 }
1073                 else if (!strcmp(var, "THE_REQUEST")) {
1074                     ret = r->the_request;
1075                 }
1076                 break;
1077
1078             case 'I':
1079                 if (!strcmp(var, "API_VERSION")) {
1080                     return apr_psprintf(r->pool, "%d:%d",
1081                                         MODULE_MAGIC_NUMBER_MAJOR,
1082                                         MODULE_MAGIC_NUMBER_MINOR);
1083                 }
1084                 break;
1085
1086             case 'K':
1087                 if (!strcmp(var, "HTTP_COOKIE")) {
1088                     ret = apr_table_get(r->headers_in, "Cookie");
1089                 }
1090                 break;
1091
1092             case 'O':
1093                 if (*var == 'S' && !strcmp(var, "SERVER_PORT")) {
1094                     return apr_psprintf(r->pool, "%u", ap_get_server_port(r));
1095                 }
1096                 else if (var[7] == 'H' && !strcmp(var, "REMOTE_HOST")) {
1097                     ret = ap_get_remote_host(r->connection,r->per_dir_config,
1098                                                 REMOTE_NAME, NULL);
1099                 }
1100                 else if (!strcmp(var, "REMOTE_PORT")) {
1101                     return apr_itoa(r->pool, r->connection->remote_addr->port);
1102                 }
1103                 break;
1104
1105             case 'S':
1106                 if (*var == 'R' && !strcmp(var, "REMOTE_USER")) {
1107                     ret = r->user;
1108                 }
1109                 else if (!strcmp(var, "SCRIPT_USER")) {
1110                     ret = "<unknown>";
1111                     if (r->finfo.valid & APR_FINFO_USER) {
1112                         apr_uid_name_get((char **)&ret, r->finfo.user,
1113                                          r->pool);
1114                     }
1115                 }
1116                 break;
1117
1118             case 'U':
1119                 if (!strcmp(var, "REQUEST_URI")) {
1120                     ret = r->uri;
1121                 }
1122                 break;
1123             }
1124             break;
1125
1126         case 12:
1127             switch (var[3]) {
1128             case 'I':
1129                 if (!strcmp(var, "SCRIPT_GROUP")) {
1130                     ret = "<unknown>";
1131                     if (r->finfo.valid & APR_FINFO_GROUP) {
1132                         apr_gid_name_get((char **)&ret, r->finfo.group,
1133                                          r->pool);
1134                     }
1135                 }
1136                 break;
1137
1138             case 'O':
1139                 if (!strcmp(var, "REMOTE_IDENT")) {
1140                     ret = ap_get_remote_logname(r);
1141                 }
1142                 break;
1143
1144             case 'P':
1145                 if (!strcmp(var, "HTTP_REFERER")) {
1146                     ret = apr_table_get(r->headers_in, "Referer");
1147                 }
1148                 break;
1149
1150             case 'R':
1151                 if (!strcmp(var, "QUERY_STRING")) {
1152                     ret = r->args;
1153                 }
1154                 break;
1155
1156             case 'V':
1157                 if (!strcmp(var, "SERVER_ADMIN")) {
1158                     ret = r->server->server_admin;
1159                 }
1160                 break;
1161             }
1162             break;
1163
1164         case 13:
1165             if (!strcmp(var, "DOCUMENT_ROOT")) {
1166                 ret = ap_document_root(r);
1167             }
1168             break;
1169
1170         case 14:
1171             if (*var == 'H' && !strcmp(var, "HTTP_FORWARDED")) {
1172                 ret = apr_table_get(r->headers_in, "Forwarded");
1173             }
1174             else if (!strcmp(var, "REQUEST_METHOD")) {
1175                 ret = r->method;
1176             }
1177             break;
1178
1179         case 15:
1180             switch (var[7]) {
1181             case 'E':
1182                 if (!strcmp(var, "HTTP_USER_AGENT")) {
1183                     ret = apr_table_get(r->headers_in, "User-Agent");
1184                 }
1185                 break;
1186
1187             case 'F':
1188                 if (!strcmp(var, "SCRIPT_FILENAME")) {
1189                     ret = r->filename; /* same as request_filename (16) */
1190                 }
1191                 break;
1192
1193             case 'P':
1194                 if (!strcmp(var, "SERVER_PROTOCOL")) {
1195                     ret = r->protocol;
1196                 }
1197                 break;
1198
1199             case 'S':
1200                 if (!strcmp(var, "SERVER_SOFTWARE")) {
1201                     ret = ap_get_server_banner();
1202                 }
1203                 break;
1204             }
1205             break;
1206
1207         case 16:
1208             if (!strcmp(var, "REQUEST_FILENAME")) {
1209                 ret = r->filename; /* same as script_filename (15) */
1210             }
1211             break;
1212
1213         case 21:
1214             if (!strcmp(var, "HTTP_PROXY_CONNECTION")) {
1215                 ret = apr_table_get(r->headers_in, "Proxy-Connection");
1216             }
1217             break;
1218         }
1219     }
1220
1221     /* TODO: provide a hook so modules can interpret other patterns */
1222     /* OhBugger, where's the regexp for backreferences ? */
1223     if (!ret) {
1224         ret = "";
1225     }
1226     return ret;  /* default - literal string as-is */
1227 }
1228 static apr_status_t ap_expr_term(void *expr)
1229 {
1230     if (isvar) {
1231         ap_regfree(isvar);
1232         isvar = NULL;
1233     }
1234     return APR_SUCCESS;
1235 }
1236 AP_DECLARE(apr_status_t) ap_expr_init(apr_pool_t *pool)
1237 {
1238     static ap_regex_t var;
1239     if (!isvar) {
1240         isvar = &var;
1241         if (ap_regcomp(isvar, "\\$([A-Za-z0-9]+)\\{([^\\}]+)\\}", 0)) {
1242             isvar = NULL;
1243         }
1244         else {
1245             apr_pool_cleanup_register(pool, isvar, ap_expr_term,
1246                                       apr_pool_cleanup_null);
1247         }
1248     }
1249     return isvar ? APR_SUCCESS : APR_EGENERAL;
1250 }