]> granicus.if.org Git - apache/blob - server/util_expr.c
mod_session_cookie: Add a session implementation capable of storing
[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
28 #include "ap_expr.h"
29 #if 1
30 /*
31  * +-------------------------------------------------------+
32  * |                                                       |
33  * |                  Debugging Utilities
34  * |                                                       |
35  * +-------------------------------------------------------+
36  */
37
38 #ifdef DEBUG_INCLUDE
39
40 #define TYPE_TOKEN(token, ttype) do { \
41     (token)->type = ttype;            \
42     (token)->s = #ttype;              \
43 } while(0)
44
45 #define CREATE_NODE(pool,name) do {                          \
46     (name) = apr_pcalloc(pool, sizeof(*(name)));
47 } while(0)
48
49 static void debug_printf(request_rec *r, const char *fmt, ...)
50 {
51     va_list ap;
52     char *debug__str;
53
54     va_start(ap, fmt);
55     debug__str = apr_pvsprintf(r->pool, fmt, ap);
56     va_end(ap);
57 /*
58     APR_BRIGADE_INSERT_TAIL(ctx->intern->debug.bb, apr_bucket_pool_create(
59                             debug__str, strlen(debug__str), ctx->pool,
60                             ctx->intern->debug.f->c->bucket_alloc));
61                             */
62 }
63
64 #define DUMP__CHILD(ctx, is, node, child) if (1) {                           \
65     ap_parse_node_t *d__c = node->child;                                     \
66     if (d__c) {                                                              \
67         if (!d__c->dump_done) {                                              \
68             if (d__c->parent != node) {                                      \
69                 debug_printf(ctx, "!!! Parse tree is not consistent !!!\n"); \
70                 if (!d__c->parent) {                                         \
71                     debug_printf(ctx, "Parent of " #child " child node is "  \
72                                  "NULL.\n");                                 \
73                 }                                                            \
74                 else {                                                       \
75                     debug_printf(ctx, "Parent of " #child " child node "     \
76                                  "points to another node (of type %s)!\n",   \
77                                  d__c->parent->token.s);                     \
78                 }                                                            \
79                 return;                                                      \
80             }                                                                \
81             node = d__c;                                                     \
82             continue;                                                        \
83         }                                                                    \
84     }                                                                        \
85     else {                                                                   \
86         debug_printf(ctx, "%s(missing)\n", is);                              \
87     }                                                                        \
88 }
89
90 static void debug_dump_tree(include_ctx_t *ctx, ap_parse_node_t *root)
91 {
92     ap_parse_node_t *current;
93     char *is;
94
95     if (!root) {
96         debug_printf(ctx, "     -- Parse Tree empty --\n\n");
97         return;
98     }
99
100     debug_printf(ctx, "     ----- Parse Tree -----\n");
101     current = root;
102     is = "     ";
103
104     while (current) {
105         switch (current->token.type) {
106         case TOKEN_STRING:
107         case TOKEN_RE:
108             debug_printf(ctx, "%s%s (%s)\n", is, current->token.s,
109                          current->token.value);
110             current->dump_done = 1;
111             current = current->parent;
112             continue;
113
114         case TOKEN_NOT:
115         case TOKEN_GROUP:
116         case TOKEN_RBRACE:
117         case TOKEN_LBRACE:
118             if (!current->dump_done) {
119                 debug_printf(ctx, "%s%s\n", is, current->token.s);
120                 is = apr_pstrcat(ctx->dpool, is, "    ", NULL);
121                 current->dump_done = 1;
122             }
123
124             DUMP__CHILD(ctx, is, current, right)
125
126             if (!current->right || current->right->dump_done) {
127                 is = apr_pstrmemdup(ctx->dpool, is, strlen(is) - 4);
128                 if (current->right) current->right->dump_done = 0;
129                 current = current->parent;
130             }
131             continue;
132
133         default:
134             if (!current->dump_done) {
135                 debug_printf(ctx, "%s%s\n", is, current->token.s);
136                 is = apr_pstrcat(ctx->dpool, is, "    ", NULL);
137                 current->dump_done = 1;
138             }
139
140             DUMP__CHILD(ctx, is, current, left)
141             DUMP__CHILD(ctx, is, current, right)
142
143             if ((!current->left || current->left->dump_done) &&
144                 (!current->right || current->right->dump_done)) {
145
146                 is = apr_pstrmemdup(ctx->dpool, is, strlen(is) - 4);
147                 if (current->left) current->left->dump_done = 0;
148                 if (current->right) current->right->dump_done = 0;
149                 current = current->parent;
150             }
151             continue;
152         }
153     }
154
155     /* it is possible to call this function within the parser loop, to see
156      * how the tree is built. That way, we must cleanup after us to dump
157      * always the whole tree
158      */
159     root->dump_done = 0;
160     if (root->left) root->left->dump_done = 0;
161     if (root->right) root->right->dump_done = 0;
162
163     debug_printf(ctx, "     --- End Parse Tree ---\n\n");
164
165     return;
166 }
167
168 #define DEBUG_INIT(ctx, filter, brigade) do { \
169     (ctx)->intern->debug.f = filter;          \
170     (ctx)->intern->debug.bb = brigade;        \
171 } while(0)
172
173 #define DEBUG_PRINTF(arg) debug_printf arg
174
175 #define DEBUG_DUMP_TOKEN(ctx, token) do {                                     \
176     token_t *d__t = (token);                                                  \
177                                                                               \
178     if (d__t->type == TOKEN_STRING || d__t->type == TOKEN_RE) {               \
179         DEBUG_PRINTF(((ctx), "     Found: %s (%s)\n", d__t->s, d__t->value)); \
180     }                                                                         \
181     else {                                                                    \
182         DEBUG_PRINTF((ctx, "     Found: %s\n", d__t->s));                     \
183     }                                                                         \
184 } while(0)
185
186 #define DEBUG_DUMP_EVAL(r, node) do {                                       \
187     char c = '"';                                                             \
188     switch ((node)->token.type) {                                             \
189     case TOKEN_STRING:                                                        \
190         debug_printf((r), "     Evaluate: %s (%s) -> %c\n", (node)->token.s,\
191                      (node)->token.value, ((node)->value) ? '1':'0');         \
192         break;                                                                \
193     case TOKEN_AND:                                                           \
194     case TOKEN_OR:                                                            \
195         debug_printf((r), "     Evaluate: %s (Left: %s; Right: %s) -> %c\n",\
196                      (node)->token.s,                                         \
197                      (((node)->left->done) ? ((node)->left->value ?"1":"0")   \
198                                           : "short circuited"),               \
199                      (((node)->right->done) ? ((node)->right->value?"1":"0")  \
200                                           : "short circuited"),               \
201                      (node)->value ? '1' : '0');                              \
202         break;                                                                \
203     case TOKEN_EQ:                                                            \
204     case TOKEN_NE:                                                            \
205     case TOKEN_GT:                                                            \
206     case TOKEN_GE:                                                            \
207     case TOKEN_LT:                                                            \
208     case TOKEN_LE:                                                            \
209         if ((node)->right->token.type == TOKEN_RE) c = '/';                   \
210         debug_printf((r), "     Compare:  %s (\"%s\" with %c%s%c) -> %c\n", \
211                      (node)->token.s,                                         \
212                      (node)->left->token.value,                               \
213                      c, (node)->right->token.value, c,                        \
214                      (node)->value ? '1' : '0');                              \
215         break;                                                                \
216     default:                                                                  \
217         debug_printf((r), "     Evaluate: %s -> %c\n", (node)->token.s,     \
218                      (node)->value ? '1' : '0');                              \
219         break;                                                                \
220     }                                                                         \
221 } while(0)
222
223 #define DEBUG_DUMP_UNMATCHED(r, unmatched) do {                        \
224     if (unmatched) {                                                     \
225         DEBUG_PRINTF(((r), "     Unmatched %c\n", (char)(unmatched))); \
226     }                                                                    \
227 } while(0)
228
229 #define DEBUG_DUMP_COND(ctx, text)                                 \
230     DEBUG_PRINTF(((ctx), "**** %s cond status=\"%c\"\n", (text),   \
231                   ((ctx)->flags & SSI_FLAG_COND_TRUE) ? '1' : '0'))
232
233 #define DEBUG_DUMP_TREE(ctx, root) debug_dump_tree(ctx, root)
234
235 #else /* DEBUG_INCLUDE */
236
237 #define TYPE_TOKEN(token, ttype) (token)->type = ttype
238
239 #define CREATE_NODE(pool,name) do {                       \
240     (name) = apr_pcalloc(pool, sizeof(*(name)));        \
241 } while(0)
242
243 #define DEBUG_INIT(ctx, f, bb)
244 #define DEBUG_PRINTF(arg)
245 #define DEBUG_DUMP_TOKEN(ctx, token)
246 #define DEBUG_DUMP_EVAL(ctx, node)
247 #define DEBUG_DUMP_UNMATCHED(ctx, unmatched)
248 #define DEBUG_DUMP_COND(ctx, text)
249 #define DEBUG_DUMP_TREE(ctx, root)
250
251 #endif /* !DEBUG_INCLUDE */
252
253 #endif /* 0 */
254
255
256 /*
257  * +-------------------------------------------------------+
258  * |                                                       |
259  * |              Conditional Expression Parser
260  * |                                                       |
261  * +-------------------------------------------------------+
262  */
263 static APR_INLINE int re_check(request_rec *r, const char *string,
264                                const char *rexp, backref_t **reptr)
265 {
266     ap_regex_t *compiled;
267     backref_t *re = reptr ? *reptr : NULL;
268     int rc;
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     rc = !ap_regexec(compiled, string, AP_MAX_REG_MATCH, re->match, 0);
288
289     ap_pregfree(r->pool, compiled);
290     return rc;
291 }
292
293 static int get_ptoken(apr_pool_t *pool, const char **parse, token_t *token,
294                       token_t *previous)
295 {
296     const char *p;
297     apr_size_t shift;
298     int unmatched;
299
300     token->value = NULL;
301
302     if (!*parse) {
303         return 0;
304     }
305
306     /* Skip leading white space */
307     while (apr_isspace(**parse)) {
308         ++*parse;
309     }
310
311     if (!**parse) {
312         *parse = NULL;
313         return 0;
314     }
315
316     TYPE_TOKEN(token, TOKEN_STRING); /* the default type */
317     p = *parse;
318     unmatched = 0;
319
320     switch (*(*parse)++) {
321     case '(':
322         TYPE_TOKEN(token, TOKEN_LBRACE);
323         return 0;
324     case ')':
325         TYPE_TOKEN(token, TOKEN_RBRACE);
326         return 0;
327     case '=':
328         if (**parse == '=') ++*parse;
329         TYPE_TOKEN(token, TOKEN_EQ);
330         return 0;
331     case '!':
332         if (**parse == '=') {
333             TYPE_TOKEN(token, TOKEN_NE);
334             ++*parse;
335             return 0;
336         }
337         TYPE_TOKEN(token, TOKEN_NOT);
338         return 0;
339     case '\'':
340         unmatched = '\'';
341         break;
342     case '/':
343         /* if last token was ACCESS, this token is STRING */
344         if (previous != NULL && TOKEN_ACCESS == previous->type) {
345             break;
346         }
347         TYPE_TOKEN(token, TOKEN_RE);
348         unmatched = '/';
349         break;
350     case '|':
351         if (**parse == '|') {
352             TYPE_TOKEN(token, TOKEN_OR);
353             ++*parse;
354             return 0;
355         }
356         break;
357     case '&':
358         if (**parse == '&') {
359             TYPE_TOKEN(token, TOKEN_AND);
360             ++*parse;
361             return 0;
362         }
363         break;
364     case '>':
365         if (**parse == '=') {
366             TYPE_TOKEN(token, TOKEN_GE);
367             ++*parse;
368             return 0;
369         }
370         TYPE_TOKEN(token, TOKEN_GT);
371         return 0;
372     case '<':
373         if (**parse == '=') {
374             TYPE_TOKEN(token, TOKEN_LE);
375             ++*parse;
376             return 0;
377         }
378         TYPE_TOKEN(token, TOKEN_LT);
379         return 0;
380     case '-':
381         if (apr_isalnum(**parse) && apr_isspace((*parse)[1])) {
382             TYPE_TOKEN(token, TOKEN_ACCESS);
383             token->value = *parse;
384             ++*parse;
385             return 0;
386         }
387         break;
388     }
389
390     /* It's a string or regex token
391      * Now search for the next token, which finishes this string
392      */
393     shift = 0;
394     p = *parse = token->value = unmatched ? *parse : p;
395
396     for (; **parse; p = ++*parse) {
397         if (**parse == '\\') {
398             if (!*(++*parse)) {
399                 p = *parse;
400                 break;
401             }
402
403             ++shift;
404         }
405         else {
406             if (unmatched) {
407                 if (**parse == unmatched) {
408                     unmatched = 0;
409                     ++*parse;
410                     break;
411                 }
412             }
413             else if (apr_isspace(**parse)) {
414                 break;
415             }
416             else {
417                 int found = 0;
418
419                 switch (**parse) {
420                 case '(':
421                 case ')':
422                 case '=':
423                 case '!':
424                 case '<':
425                 case '>':
426                     ++found;
427                     break;
428
429                 case '|':
430                 case '&':
431                     if ((*parse)[1] == **parse) {
432                         ++found;
433                     }
434                     break;
435                 }
436
437                 if (found) {
438                     break;
439                 }
440             }
441         }
442     }
443
444     if (unmatched) {
445         token->value = apr_pstrdup(pool, "");
446     }
447     else {
448         apr_size_t len = p - token->value - shift;
449         char *c = apr_palloc(pool, len + 1);
450
451         p = token->value;
452         token->value = c;
453
454         while (shift--) {
455             const char *e = ap_strchr_c(p, '\\');
456
457             memcpy(c, p, e-p);
458             c   += e-p;
459             *c++ = *++e;
460             len -= e-p;
461             p    = e+1;
462         }
463
464         if (len) {
465             memcpy(c, p, len);
466         }
467         c[len] = '\0';
468     }
469
470     return unmatched;
471 }
472
473 /* This is what we export.  We can split it in two. */
474 AP_DECLARE(ap_parse_node_t*) ap_expr_parse(apr_pool_t* pool, const char *expr,
475                                            int *was_error)
476 {
477     ap_parse_node_t *new, *root = NULL, *current = NULL;
478     const char *error = "Invalid expression \"%s\" in file %s";
479     const char *parse = expr;
480     int was_unmatched = 0;
481     unsigned regex = 0;
482
483     *was_error = 0;
484
485     if (!parse) {
486         return 0;
487     }
488
489     /* Create Parse Tree */
490     while (1) {
491         /* uncomment this to see how the tree a built:
492          *
493          * DEBUG_DUMP_TREE(ctx, root);
494          */
495         CREATE_NODE(pool, new);
496
497         was_unmatched = get_ptoken(pool, &parse, &new->token,
498                      (current != NULL ? &current->token : NULL));
499         if (!parse) {
500             break;
501         }
502
503         DEBUG_DUMP_UNMATCHED(ctx, was_unmatched);
504         DEBUG_DUMP_TOKEN(ctx, &new->token);
505
506         if (!current) {
507             switch (new->token.type) {
508             case TOKEN_STRING:
509             case TOKEN_NOT:
510             case TOKEN_ACCESS:
511             case TOKEN_LBRACE:
512                 root = current = new;
513                 continue;
514
515             default:
516                 *was_error = 1;
517                 return 0;
518             }
519         }
520
521         switch (new->token.type) {
522         case TOKEN_STRING:
523             switch (current->token.type) {
524             case TOKEN_STRING:
525                 current->token.value =
526                     apr_pstrcat(pool, current->token.value,
527                                 *current->token.value ? " " : "",
528                                 new->token.value, NULL);
529                 continue;
530
531             case TOKEN_RE:
532             case TOKEN_RBRACE:
533             case TOKEN_GROUP:
534                 break;
535
536             default:
537                 new->parent = current;
538                 current = current->right = new;
539                 continue;
540             }
541             break;
542
543         case TOKEN_RE:
544             switch (current->token.type) {
545             case TOKEN_EQ:
546             case TOKEN_NE:
547                 new->parent = current;
548                 current = current->right = new;
549                 ++regex;
550                 continue;
551
552             default:
553                 break;
554             }
555             break;
556
557         case TOKEN_AND:
558         case TOKEN_OR:
559             switch (current->token.type) {
560             case TOKEN_STRING:
561             case TOKEN_RE:
562             case TOKEN_GROUP:
563                 current = current->parent;
564
565                 while (current) {
566                     switch (current->token.type) {
567                     case TOKEN_AND:
568                     case TOKEN_OR:
569                     case TOKEN_LBRACE:
570                         break;
571
572                     default:
573                         current = current->parent;
574                         continue;
575                     }
576                     break;
577                 }
578
579                 if (!current) {
580                     new->left = root;
581                     root->parent = new;
582                     current = root = new;
583                     continue;
584                 }
585
586                 new->left = current->right;
587                 new->left->parent = new;
588                 new->parent = current;
589                 current = current->right = new;
590                 continue;
591
592             default:
593                 break;
594             }
595             break;
596
597         case TOKEN_EQ:
598         case TOKEN_NE:
599         case TOKEN_GE:
600         case TOKEN_GT:
601         case TOKEN_LE:
602         case TOKEN_LT:
603             if (current->token.type == TOKEN_STRING) {
604                 current = current->parent;
605
606                 if (!current) {
607                     new->left = root;
608                     root->parent = new;
609                     current = root = new;
610                     continue;
611                 }
612
613                 switch (current->token.type) {
614                 case TOKEN_LBRACE:
615                 case TOKEN_AND:
616                 case TOKEN_OR:
617                     new->left = current->right;
618                     new->left->parent = new;
619                     new->parent = current;
620                     current = current->right = new;
621                     continue;
622
623                 default:
624                     break;
625                 }
626             }
627             break;
628
629         case TOKEN_RBRACE:
630             while (current && current->token.type != TOKEN_LBRACE) {
631                 current = current->parent;
632             }
633
634             if (current) {
635                 TYPE_TOKEN(&current->token, TOKEN_GROUP);
636                 continue;
637             }
638
639             error = "Unmatched ')' in \"%s\" in file %s";
640             break;
641
642         case TOKEN_NOT:
643         case TOKEN_ACCESS:
644         case TOKEN_LBRACE:
645             switch (current->token.type) {
646             case TOKEN_STRING:
647             case TOKEN_RE:
648             case TOKEN_RBRACE:
649             case TOKEN_GROUP:
650                 break;
651
652             default:
653                 current->right = new;
654                 new->parent = current;
655                 current = new;
656                 continue;
657             }
658             break;
659
660         default:
661             break;
662         }
663
664         *was_error = 1;
665         return 0;
666     }
667
668     DEBUG_DUMP_TREE(ctx, root);
669     return root;
670 }
671
672 static ap_parse_node_t *ap_expr_clone_tree(apr_pool_t *pool,
673                                            ap_parse_node_t *pnode,
674                                            ap_parse_node_t *parent)
675 {
676     ap_parse_node_t *ret;
677     ret = apr_pmemdup(pool, pnode, sizeof(ap_parse_node_t));
678     if (pnode->left) {
679         ret->left = ap_expr_clone_tree(pool, pnode->left, ret);
680     }
681     if (pnode->right) {
682         ret->right = ap_expr_clone_tree(pool, pnode->right, ret);
683     }
684     ret->parent = parent;
685     return ret;
686 }
687
688 #define PARSE_STRING(r,s) (string_func ? string_func((r),(s)) : (s))
689 static int expr_eval(request_rec *r, ap_parse_node_t *root,
690                      int *was_error, backref_t **reptr,
691                      string_func_t string_func, opt_func_t eval_func)
692 {
693     ap_parse_node_t *current = root;
694     const char *error = NULL;
695     unsigned int regex = 0;
696     const char *val;
697     const char *lval;
698     const char *rval;
699
700     /* Evaluate Parse Tree */
701     while (current) {
702         switch (current->token.type) {
703         case TOKEN_STRING:
704             val = PARSE_STRING(r, current->token.value);
705             current->value = !!*val;
706             break;
707
708         case TOKEN_AND:
709         case TOKEN_OR:
710             if (!current->left || !current->right) {
711                 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
712                               "Invalid expression in file %s", r->filename);
713                 *was_error = 1;
714                 return 0;
715             }
716
717             if (!current->left->done) {
718                 switch (current->left->token.type) {
719                 case TOKEN_STRING:
720                     lval = PARSE_STRING(r, current->left->token.value);
721                     current->left->value = !!*lval;
722                     DEBUG_DUMP_EVAL(ctx, current->left);
723                     current->left->done = 1;
724                     break;
725
726                 default:
727                     current = current->left;
728                     continue;
729                 }
730             }
731
732             /* short circuit evaluation */
733             if (!current->right->done && !regex &&
734                 ((current->token.type == TOKEN_AND && !current->left->value) ||
735                 (current->token.type == TOKEN_OR && current->left->value))) {
736                 current->value = current->left->value;
737             }
738             else {
739                 if (!current->right->done) {
740                     switch (current->right->token.type) {
741                     case TOKEN_STRING:
742                         rval = PARSE_STRING(r,current->right->token.value);
743                         current->right->value = !!*rval;
744                         DEBUG_DUMP_EVAL(r, current->right);
745                         current->right->done = 1;
746                         break;
747
748                     default:
749                         current = current->right;
750                         continue;
751                     }
752                 }
753
754                 if (current->token.type == TOKEN_AND) {
755                     current->value = current->left->value &&
756                                      current->right->value;
757                 }
758                 else {
759                     current->value = current->left->value ||
760                                      current->right->value;
761                 }
762             }
763             break;
764
765         case TOKEN_EQ:
766         case TOKEN_NE:
767             if (!current->left || !current->right ||
768                 current->left->token.type != TOKEN_STRING ||
769                 (current->right->token.type != TOKEN_STRING &&
770                  current->right->token.type != TOKEN_RE)) {
771                 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
772                             "Invalid expression in file %s", r->filename);
773                 *was_error = 1;
774                 return 0;
775             }
776             lval = PARSE_STRING(r, current->left->token.value);
777             rval = PARSE_STRING(r, current->right->token.value);
778
779             if (current->right->token.type == TOKEN_RE) {
780                 current->value = re_check(r, lval, rval, reptr);
781                 --regex;
782             }
783             else {
784                 current->value = !strcmp(lval, rval);
785             }
786
787             if (current->token.type == TOKEN_NE) {
788                 current->value = !current->value;
789             }
790             break;
791
792         case TOKEN_GE:
793         case TOKEN_GT:
794         case TOKEN_LE:
795         case TOKEN_LT:
796             if (!current->left || !current->right ||
797                 current->left->token.type != TOKEN_STRING ||
798                 current->right->token.type != TOKEN_STRING) {
799                 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
800                               "Invalid expression in file %s", r->filename);
801                 *was_error = 1;
802                 return 0;
803             }
804
805             lval = PARSE_STRING(r, current->left->token.value);
806             rval = PARSE_STRING(r, current->right->token.value);
807
808             current->value = strcmp(lval, rval);
809
810             switch (current->token.type) {
811             case TOKEN_GE: current->value = current->value >= 0; break;
812             case TOKEN_GT: current->value = current->value >  0; break;
813             case TOKEN_LE: current->value = current->value <= 0; break;
814             case TOKEN_LT: current->value = current->value <  0; break;
815             default: current->value = 0; break; /* should not happen */
816             }
817             break;
818
819         case TOKEN_NOT:
820         case TOKEN_GROUP:
821             if (current->right) {
822                 if (!current->right->done) {
823                     current = current->right;
824                     continue;
825                 }
826                 current->value = current->right->value;
827             }
828             else {
829                 current->value = 1;
830             }
831
832             if (current->token.type == TOKEN_NOT) {
833                 current->value = !current->value;
834             }
835             break;
836         case TOKEN_ACCESS:
837             if (eval_func) {
838                 *was_error = eval_func(r, current, string_func);
839                 if (*was_error) {
840                     return 0;
841                 }
842             }
843             break;
844
845         case TOKEN_RE:
846             if (!error) {
847                 error = "No operator before regex in file %s";
848             }
849         case TOKEN_LBRACE:
850             if (!error) {
851                 error = "Unmatched '(' in file %s";
852             }
853         default:
854             if (!error) {
855                 error = "internal parser error in file %s";
856             }
857
858             ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, error, r->filename);
859             *was_error = 1;
860             return 0;
861         }
862
863         DEBUG_DUMP_EVAL(r, current);
864         current->done = 1;
865         current = current->parent;
866     }
867
868     return (root ? root->value : 0);
869 }
870 AP_DECLARE(int) ap_expr_eval(request_rec *r, ap_parse_node_t *root,
871                              int *was_error, backref_t **reptr,
872                              string_func_t string_func, opt_func_t eval_func)
873 {
874     ap_parse_node_t *clone;
875     if (root == NULL) {  /* no condition == unconditional */
876         return 1;
877     }
878     clone = ap_expr_clone_tree(r->pool, root, NULL);
879     return expr_eval(r, clone, was_error, reptr, string_func, eval_func);
880 }
881 AP_DECLARE(int) ap_expr_evalstring(request_rec *r, const char *expr,
882                                    int *was_error, backref_t **reptr,
883                                    string_func_t string_func,
884                                    opt_func_t eval_func)
885 {
886     ap_parse_node_t *root = ap_expr_parse(r->pool, expr, was_error);
887     if (*was_error || !root) {
888         ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
889                       "Error parsing expression in %s", r->filename);   
890         return 0;
891     }
892     return expr_eval(r, root, was_error, reptr, string_func, eval_func);
893 }
894
895
896 static ap_regex_t *isvar = NULL;
897 AP_DECLARE(const char*) ap_expr_string(request_rec *r, const char *str)
898 {
899     /* a default string evaluator: support headers and env */
900     const char *ret = str;
901     ap_regmatch_t match[3];
902     ap_assert(isvar != NULL);
903     if (ap_regexec(isvar, str, 3, match, 0) == 0) {
904         apr_table_t *table = NULL;
905         int len = match[1].rm_eo-match[1].rm_so;
906         const char *name = str+match[1].rm_so;
907         if (!strncasecmp("req", name, len)) {
908             table = r->headers_in;
909         }
910         else if (!strncasecmp("resp", name, len)) {
911             table = r->headers_out;
912         }
913         else if (!strncasecmp("env", name, len)) {
914             table = r->subprocess_env;
915         }
916         if (table != NULL) {
917             char *key = apr_pstrndup(r->pool, str+match[2].rm_so,
918                                      match[2].rm_eo-match[2].rm_so);
919             ret = apr_table_get(table, key);
920         }
921     }
922     else if (str[0] == '$') {
923         if (!strcasecmp(str, "$handler")) {
924             ret = r->handler;
925         }
926         else if (!strcasecmp(str, "$content-type")) {
927             ret = r->content_type;
928         }
929     }
930     /* TODO: provide a hook so modules can interpret other patterns */
931     /* OhBugger, where's the regexp for backreferences ? */
932     if (!ret) {
933         ret = "";
934     }
935     return ret;  /* default - literal string as-is */
936 }
937 static apr_status_t ap_expr_term(void *expr)
938 {
939     if (isvar) {
940         ap_regfree(isvar);
941         isvar = NULL;
942     }
943     return APR_SUCCESS;
944 }
945 AP_DECLARE(apr_status_t) ap_expr_init(apr_pool_t *pool)
946 {
947     static ap_regex_t var;
948     if (!isvar) {
949         isvar = &var;
950         if (ap_regcomp(isvar, "\\$([A-Za-z0-9]+)\\{([^\\}]+)\\}", 0)) {
951             isvar = NULL;
952         }
953         else {
954             apr_pool_cleanup_register(pool, isvar, ap_expr_term,
955                                       apr_pool_cleanup_null);
956         }
957     }
958     return isvar ? APR_SUCCESS : APR_EGENERAL;
959 }