Enable building #WITHOUT_PROXY
[freeradius.git] / src / modules / rlm_policy / evaluate.c
index 1262bab..3c1a000 100644 (file)
-/*\r
- * evaluate.c          Evaluate a policy language\r
- *\r
- * Version:    $Id$\r
- *\r
- *   This program is free software; you can redistribute it and/or modify\r
- *   it under the terms of the GNU General Public License as published by\r
- *   the Free Software Foundation; either version 2 of the License, or\r
- *   (at your option) any later version.\r
- *\r
- *   This program is distributed in the hope that it will be useful,\r
- *   but WITHOUT ANY WARRANTY; without even the implied warranty of\r
- *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
- *   GNU General Public License for more details.\r
- *\r
- *   You should have received a copy of the GNU General Public License\r
- *   along with this program; if not, write to the Free Software\r
- *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA\r
- *\r
- * Copyright 2004  Alan DeKok <aland@ox.org>\r
- */\r
-\r
-#include "rlm_policy.h"\r
-\r
-#include "modules.h"\r
-\r
-#ifdef HAVE_REGEX_H\r
-#include <regex.h>\r
-#endif\r
-\r
-#define debug_evaluate if (0) printf\r
-\r
-/*\r
- *     Print stuff we've parsed\r
- */\r
-static void policy_print(const policy_item_t *item, int indent)\r
-{\r
-       if (!item) {\r
-               if (indent) printf("%*s", indent, " ");\r
-               printf("[NULL]\n");\r
-               return;\r
-       }\r
-       \r
-       while (item) {\r
-               switch (item->type) {\r
-               case POLICY_TYPE_BAD:\r
-                       if (indent) printf("%*s", indent, " ");\r
-                       printf("[BAD STATEMENT]");\r
-                       break;\r
-                       \r
-               case POLICY_TYPE_PRINT:\r
-                       if (indent) printf("%*s", indent, " ");\r
-                       {\r
-                               const policy_print_t *this;\r
-\r
-                               this = (const policy_print_t *) item;\r
-                               \r
-                               if (this->rhs_type == POLICY_LEX_BARE_WORD) {\r
-                                       printf("print %s\n", this->rhs);\r
-                               } else {\r
-                                       printf("print \"%s\"\n", this->rhs);\r
-                               }\r
-                       }\r
-                       break;\r
-                       \r
-               case POLICY_TYPE_ASSIGNMENT:\r
-                       {\r
-                               const policy_assignment_t *assign;\r
-                               \r
-                               assign = (const policy_assignment_t *) item;\r
-                               if (indent) printf("%*s", indent, " ");\r
-\r
-                               printf("\t%s %s ", assign->lhs,\r
-                                      lrad_int2str(rlm_policy_tokens,\r
-                                                   assign->assign, "?"));\r
-                               if (assign->rhs_type == POLICY_LEX_BARE_WORD) {\r
-                                       printf("%s\n", assign->rhs);\r
-                               } else {\r
-                                       /*\r
-                                        *      FIXME: escape "\r
-                                        */\r
-                                       printf("\"%s\"\n", assign->rhs);\r
-                               }\r
-                       }\r
-                       break;\r
-\r
-               case POLICY_TYPE_CONDITIONAL: /* no indentation here */\r
-                       {\r
-                               const policy_condition_t *condition;\r
-\r
-                               condition = (const policy_condition_t *) item;\r
-\r
-                               printf("(");\r
-\r
-                               /*\r
-                                *      Nested conditions.\r
-                                */\r
-                               if (condition->compare == POLICY_LEX_L_BRACKET) {\r
-                                       policy_print(condition->child, indent);\r
-                                       printf(")");\r
-                                       break;\r
-                               }\r
-\r
-                               if (condition->compare == POLICY_LEX_L_NOT) {\r
-                                       printf("!");\r
-                                       policy_print(condition->child, indent);\r
-                                       printf(")");\r
-                                       break;\r
-                               }\r
-\r
-                               if (condition->compare == POLICY_LEX_CMP_TRUE) {\r
-                                       printf("%s)", condition->lhs);\r
-                                       break;\r
-                               }\r
-\r
-                               if (condition->lhs_type == POLICY_LEX_BARE_WORD) {\r
-                                       printf("%s", condition->lhs);\r
-                               } else {\r
-                                       /*\r
-                                        *      FIXME: escape ",\r
-                                        *      and move all of this logic\r
-                                        *      to a function.\r
-                                        */\r
-                                       printf("\"%s\"", condition->lhs);\r
-                               }\r
-\r
-                               /*\r
-                                *      We always print this condition.\r
-                                */\r
-                               printf(" %s ", lrad_int2str(rlm_policy_tokens,\r
-                                                           condition->compare,\r
-                                                           "?"));\r
-                               if (condition->rhs_type == POLICY_LEX_BARE_WORD) {\r
-                                       printf("%s", condition->rhs);\r
-                               } else {\r
-                                       /*\r
-                                        *      FIXME: escape ",\r
-                                        *      and move all of this logic\r
-                                        *      to a function.\r
-                                        */\r
-                                       printf("\"%s\"", condition->rhs);\r
-                               }\r
-                               printf(")");\r
-                               \r
-                               if (condition->child_condition != POLICY_LEX_BAD) {\r
-                                       printf(" %s ", lrad_int2str(rlm_policy_tokens, condition->child_condition, "?"));\r
-                                       policy_print(condition->child, indent);\r
-                               }\r
-                       }\r
-                       break;\r
-\r
-               case POLICY_TYPE_IF:\r
-                       {\r
-                               const policy_if_t *statement;\r
-\r
-                               statement = (const policy_if_t *) item;\r
-\r
-                               if (indent) printf("%*s", indent, " ");\r
-                               printf("if ");\r
-                               policy_print(statement->condition, indent);\r
-                               printf(" {\n");\r
-                               policy_print(statement->if_true, indent + 1);\r
-                               if (indent) printf("%*s", indent, " ");\r
-                               if (statement->if_false) {\r
-                                       printf("} else ");\r
-                                       if (statement->if_false->type == POLICY_TYPE_ASSIGNMENT) {\r
-                                               printf(" { ");\r
-                                               policy_print(statement->if_false, indent + 1);\r
-                                               if (indent) printf("%*s", indent, " ");\r
-                                               printf(" }");\r
-                                       } else {\r
-                                               policy_print(statement->if_false, indent + 1);\r
-                                       }\r
-                               } else {\r
-                                       printf("}\n");\r
-                               }\r
-                       }\r
-                       break;\r
-\r
-               case POLICY_TYPE_ATTRIBUTE_LIST:\r
-                       {\r
-                               const policy_attributes_t *this;\r
-\r
-                               this = (const policy_attributes_t *) item;\r
-\r
-                               if (indent) printf("%*s", indent, " ");\r
-                               printf("%s %s {\n",\r
-                                      lrad_int2str(policy_reserved_words,\r
-                                                   this->where, "?"),\r
-                                      lrad_int2str(rlm_policy_tokens,\r
-                                                   this->how, "?"));\r
-                               policy_print(this->attributes, indent + 1);\r
-                               if (indent) printf("%*s", indent, " ");\r
-                               printf("}\n");\r
-                       }\r
-                       break;\r
-\r
-               case POLICY_TYPE_NAMED_POLICY:\r
-                       {\r
-                               const policy_named_t *this;\r
-\r
-                               this = (const policy_named_t *) item;\r
-                               if (indent) printf("%*s", indent, " ");\r
-                               printf("policy %s {\n", this->name);\r
-                               policy_print(this->policy, indent + 1);\r
-                               if (indent) printf("%*s", indent, " ");\r
-                               printf("}\n");\r
-                       }\r
-                       break;\r
-\r
-               case POLICY_TYPE_CALL:\r
-                       {\r
-                               const policy_call_t *this;\r
-\r
-                               this = (const policy_call_t *) item;\r
-                               if (indent) printf("%*s", indent, " ");\r
-                               printf("call %s\n", this->name);\r
-                       }\r
-                       break;\r
-\r
-               default:\r
-                       if (indent) printf("%*s", indent, " ");\r
-                       printf("[HUH?]\n");\r
-                       break;\r
-                       \r
-               }\r
-\r
-               item = item->next;\r
-       }\r
-}\r
-\r
-\r
-void rlm_policy_print(const policy_item_t *item)\r
-{\r
-       printf("----------------------------------------------------------\n");\r
-       policy_print(item, 0);\r
-       printf("----------------------------------------------------------\n");\r
-}\r
-\r
-/*\r
- *     Internal stack of things to do.\r
- *\r
- *     When a function is about to be pushed onto the stack, we walk\r
- *     backwards through the stack, and ensure that the function is\r
- *     not already there.  This prevents infinite recursion.\r
- *\r
- *     This means that we NEVER pop functions.  Rather, we push the\r
- *     function, and then immediately push it's first element.\r
- *\r
- *     When we've finished popping all of the elements, we pop the\r
- *     function, realize it's a function, ignore it, and pop one more\r
- *     entry.\r
- */\r
-#define POLICY_MAX_STACK 16\r
-typedef struct policy_state_t {\r
-       rlm_policy_t    *inst;\r
-       int             depth;\r
-       REQUEST         *request; /* so it's not passed on the C stack */\r
-       const policy_item_t *stack[POLICY_MAX_STACK];\r
-} policy_state_t;\r
-\r
-\r
-/*\r
- *     Push an item onto the state.\r
- */\r
-static int policy_stack_push(policy_state_t *state, const policy_item_t *item)\r
-{\r
-       rad_assert(state->depth >= 0);\r
-\r
-       /*\r
-        *      Asked to push nothing.  Don't push it.\r
-        */\r
-       if (!item) return 1;\r
-\r
-       /*\r
-        *      State is full.  Die.\r
-        */\r
-       if (state->depth >= POLICY_MAX_STACK) {\r
-               return 0;\r
-       }\r
-\r
-       /*\r
-        *      Walk back up the stack, looking for previous ocurrances\r
-        *      of this name.  If found, we have infinite recursion,\r
-        *      which we stop dead in the water!\r
-        */\r
-       if (item->type == POLICY_TYPE_NAMED_POLICY) {\r
-               int i;\r
-\r
-               for (i = 0; i < state->depth; i++) {\r
-                       if (state->stack[i]->type != POLICY_TYPE_NAMED_POLICY) {\r
-                               continue;\r
-                       }\r
-\r
-                       /*\r
-                        *      FIXME: check for more stuff.\r
-                        */\r
-               }\r
-       }\r
-\r
-       debug_evaluate("push %d %p\n", state->depth, item);\r
-\r
-       state->stack[state->depth] = item;\r
-       state->depth++;         /* points to unused entry */\r
-\r
-       return 1;\r
-}\r
-\r
-\r
-/*\r
- *     Pop an item from the state.\r
- */\r
-static int policy_stack_pop(policy_state_t *state, const policy_item_t **pitem)\r
-{\r
-       rad_assert(pitem != NULL);\r
-       rad_assert(state->depth >= 0);\r
-\r
-       if (state->depth == 0) {\r
-               *pitem = NULL;\r
-               return 0;\r
-       }\r
-\r
-       *pitem = state->stack[state->depth - 1];\r
-\r
-       /*\r
-        *      Process the whole item list.\r
-        */\r
-       if ((*pitem)->next) {\r
-               state->stack[state->depth - 1] = (*pitem)->next;\r
-               debug_evaluate("pop/push %d %p\n", state->depth - 1, *pitem);\r
-       } else {\r
-               state->depth--;         /* points to unused entry */\r
-               debug_evaluate("pop %d %p\n", state->depth, *pitem);\r
-       }\r
-\r
-       return 1;\r
-}\r
-\r
-\r
-/*\r
- *     Evaluate a print statement\r
- */\r
-static int evaluate_print(policy_state_t *state, const policy_item_t *item)\r
-{\r
-       const policy_print_t *this;\r
-\r
-       this = (const policy_print_t *) item;\r
-\r
-       if (this->rhs_type == POLICY_LEX_BARE_WORD) {\r
-               printf("%s\n", this->rhs);\r
-       } else {\r
-               char buffer[1024];\r
-\r
-               radius_xlat(buffer, sizeof(buffer), this->rhs,\r
-                           state->request, NULL);\r
-               printf("%s", buffer);\r
-       }\r
-\r
-       return 1;\r
-}\r
-\r
-/*\r
- *     Return a VALUE_PAIR, given an attribute name.\r
- *\r
- *     FIXME: Have it return the N'th one, too, like\r
- *     doc/variables.txt?\r
- *\r
- *     The amount of duplicated code is getting annoying...\r
- */\r
-static VALUE_PAIR *find_vp(REQUEST *request, const char *name)\r
-{\r
-       const char *p;\r
-       const DICT_ATTR *dattr;\r
-       VALUE_PAIR *vps;\r
-\r
-       p = name;\r
-       vps = request->packet->vps;;\r
-\r
-       /*\r
-        *      FIXME: use names from reserved word list?\r
-        */\r
-       if (strncasecmp(name, "request:", 8) == 0) {\r
-               p += 8;\r
-       } else if (strncasecmp(name, "reply:", 6) == 0) {\r
-               p += 6;\r
-               vps = request->reply->vps;\r
-       } else if (strncasecmp(name, "proxy-request:", 14) == 0) {\r
-               p += 14;\r
-               if (request->proxy) {\r
-                       vps = request->proxy->vps;\r
-               }\r
-       } else if (strncasecmp(name, "proxy-reply:", 12) == 0) {\r
-               p += 12;\r
-               if (request->proxy_reply) {\r
-                       vps = request->proxy_reply->vps;\r
-               }\r
-       } else if (strncasecmp(name, "control:", 8) == 0) {\r
-               p += 8;\r
-               vps = request->config_items;\r
-       } /* else it must be a bare attribute name */\r
-\r
-       if (!vps) {\r
-               return NULL;\r
-       }\r
-\r
-       dattr = dict_attrbyname(p);\r
-       if (!dattr) {\r
-               fprintf(stderr, "No such attribute %s\n", p);\r
-               return NULL;    /* no such attribute */\r
-       }\r
-\r
-       return pairfind(vps, dattr->attr);\r
-}\r
-\r
-\r
-/*\r
- *     Evaluate an assignment\r
- */\r
-static int evaluate_assignment(policy_state_t *state, const policy_item_t *item)\r
-{\r
-       const policy_assignment_t *this;\r
-       const DICT_ATTR *dattr;\r
-\r
-       this = (const policy_assignment_t *) item;\r
-\r
-       rad_assert(this->lhs != NULL);\r
-       rad_assert(this->rhs != NULL);\r
-\r
-#if 0\r
-       dattr = dict_attrbyname(this->lhs);\r
-       if (!dattr) {\r
-               fprintf(stderr, "HUH?\n");\r
-               return 0;\r
-       }\r
-#endif\r
-\r
-       return 1;\r
-}\r
-\r
-\r
-/*\r
- *     Evaluate a condition\r
- */\r
-static int evaluate_condition(policy_state_t *state, const policy_item_t *item)\r
-{\r
-       int rcode;\r
-       const policy_condition_t *this;\r
-       VALUE_PAIR *vp;\r
-       char *data = NULL;\r
-       int compare;\r
-#ifdef HAVE_REGEX_H\r
-       regex_t reg;\r
-#endif\r
-       char buffer[256];\r
-       char lhs_buffer[2048];\r
-\r
-       this = (const policy_condition_t *) item;\r
-\r
- redo:\r
-       /*\r
-        *      FIXME: Don't always do this...\r
-        */\r
-       if ((this->compare != POLICY_LEX_L_BRACKET) &&\r
-           (this->lhs_type == POLICY_LEX_DOUBLE_QUOTED_STRING)) {\r
-               if (radius_xlat(lhs_buffer, sizeof(lhs_buffer), this->lhs,\r
-                               state->request, NULL) > 0) {\r
-                       data = lhs_buffer;\r
-               }\r
-       }\r
-       \r
-       switch (this->compare) {\r
-       case POLICY_LEX_L_BRACKET: /* nested brackets are a special case */\r
-               rcode = evaluate_condition(state, this->child);\r
-               break;\r
-\r
-       case POLICY_LEX_L_NOT:\r
-               rcode = evaluate_condition(state, this->child);\r
-               rcode = (rcode == FALSE); /* reverse sense of test */\r
-               break;\r
-\r
-       case POLICY_LEX_CMP_TRUE: /* existence */\r
-               if (this->lhs_type == POLICY_LEX_BARE_WORD) {\r
-                       vp = find_vp(state->request, this->lhs);\r
-                       rcode = (vp != NULL);\r
-               } else {\r
-                       rcode = (data != NULL);\r
-               }\r
-               break;\r
-\r
-       default:                /* process other comparisons */\r
-               if ((this->compare != POLICY_LEX_CMP_EQUALS) &&\r
-#ifdef HAVE_REGEX_H\r
-                   (this->compare != POLICY_LEX_RX_EQUALS) &&\r
-                   (this->compare != POLICY_LEX_RX_NOT_EQUALS) &&\r
-#endif\r
-                   (this->compare != POLICY_LEX_LT) &&\r
-                   (this->compare != POLICY_LEX_GT) &&\r
-                   (this->compare != POLICY_LEX_LE) &&\r
-                   (this->compare != POLICY_LEX_GE) &&\r
-                   (this->compare != POLICY_LEX_CMP_NOT_EQUALS)) {\r
-                       fprintf(stderr, "%d: bad comparison\n",\r
-                               this->item.lineno);\r
-                       return FALSE;\r
-               }\r
-\r
-               if (this->lhs_type == POLICY_LEX_BARE_WORD) {\r
-                       VALUE_PAIR *myvp;\r
-\r
-\r
-                       vp = find_vp(state->request, this->lhs);\r
-                       /*\r
-                        *      FIXME: Move sanity checks to\r
-                        *      post-parse code, so we don't do\r
-                        *      it on every packet.\r
-                        */\r
-                       if (vp) {\r
-                               vp_prints_value(buffer, sizeof(buffer), vp, 0);\r
-                               myvp = pairmake(vp->name, this->rhs, T_OP_EQ);\r
-                       } else {\r
-                               buffer[0] = '\0';\r
-                               myvp = pairmake(this->lhs, this->rhs, T_OP_EQ);\r
-                       }\r
-                       data = buffer;\r
-                       if (!myvp) {\r
-                               return FALSE;\r
-                       }\r
-\r
-                       /*\r
-                        *      FIXME: What to do about comparisons\r
-                        *      where vp doesn't exist?  Right now,\r
-                        *      "simplepaircmp" returns -1, which is\r
-                        *      probably a bad idea.  it should\r
-                        *      instead take an operator, a pointer to\r
-                        *      the comparison result, and return\r
-                        *      "true/false" for "comparions\r
-                        *      succeeded/failed", which are different\r
-                        *      error codes than "comparison is less\r
-                        *      than, equal to, or greater than zero".\r
-                        */\r
-                       compare = simplepaircmp(state->request,\r
-                                               vp, myvp);\r
-                       pairfree(&myvp);\r
-                       \r
-               } else {\r
-                       /*\r
-                        *      FIXME: Do something for RHS type?\r
-                        */\r
-                       printf("CMP %s %s\n", lhs_buffer, this->rhs);\r
-                       compare = strcmp(lhs_buffer, this->rhs);\r
-               }\r
-\r
-               debug_evaluate("CONDITION COMPARE %d\n", compare);\r
-               \r
-               switch (this->compare) {\r
-               case POLICY_LEX_CMP_EQUALS:\r
-                       rcode = (compare == 0);\r
-                       break;\r
-                       \r
-               case POLICY_LEX_CMP_NOT_EQUALS:\r
-                       rcode = (compare != 0);\r
-                       break;\r
-                       \r
-               case POLICY_LEX_LT:\r
-                       rcode = (compare < 0);\r
-                       break;\r
-                       \r
-               case POLICY_LEX_GT:\r
-                       rcode = (compare > 0);\r
-                       break;\r
-                       \r
-               case POLICY_LEX_LE:\r
-                       rcode =(compare <= 0);\r
-                       break;\r
-                       \r
-               case POLICY_LEX_GE:\r
-                       rcode = (compare >= 0);\r
-                       break;\r
-                       \r
-#ifdef HAVE_REGEX_H\r
-               case POLICY_LEX_RX_EQUALS:\r
-               { /* FIXME: copied from src/main/valuepair.c */\r
-                       int i;\r
-                       regmatch_t rxmatch[REQUEST_MAX_REGEX + 1];\r
-                       \r
-                       /*\r
-                        *      Include substring matches.\r
-                        */\r
-                       if (regcomp(&reg, this->rhs,\r
-                                   REG_EXTENDED) != 0) {\r
-                               return FALSE;\r
-                       }\r
-                       rad_assert(data != NULL);\r
-                       rcode = regexec(&reg, data,\r
-                                       REQUEST_MAX_REGEX + 1,\r
-                                       rxmatch, 0);\r
-                       rcode = (rcode == 0);\r
-                       regfree(&reg);\r
-                       \r
-                       /*\r
-                        *      Add %{0}, %{1}, etc.\r
-                        */\r
-                       for (i = 0; i <= REQUEST_MAX_REGEX; i++) {\r
-                               char *p;\r
-                               char rxbuffer[256];\r
-                               \r
-                               /*\r
-                                *      Didn't match: delete old\r
-                                *      match, if it existed.\r
-                                */\r
-                               if (!rcode ||\r
-                                   (rxmatch[i].rm_so == -1)) {\r
-                                       p = request_data_get(state->request, state->request,\r
-                                                            REQUEST_DATA_REGEX | i);\r
-                                       if (p) {\r
-                                               free(p);\r
-                                               continue;\r
-                                       }\r
-                                               \r
-                                       /*\r
-                                        *      No previous match\r
-                                        *      to delete, stop.\r
-                                        */\r
-                                       break;\r
-                               }\r
-                               \r
-                               /*\r
-                                *      Copy substring into buffer.\r
-                                */\r
-                               memcpy(rxbuffer,\r
-                                      data + rxmatch[i].rm_so,\r
-                                      rxmatch[i].rm_eo - rxmatch[i].rm_so);\r
-                               rxbuffer[rxmatch[i].rm_eo - rxmatch[i].rm_so] = '\0';\r
-                               \r
-                               /*\r
-                                *      Copy substring, and add it to\r
-                                *      the request.\r
-                                *\r
-                                *      Note that we don't check\r
-                                *      for out of memory, which is\r
-                                *      the only error we can get...\r
-                                */\r
-                               p = strdup(rxbuffer);\r
-                               request_data_add(state->request,\r
-                                                state->request,\r
-                                                REQUEST_DATA_REGEX | i,\r
-                                                p, free);\r
-                       }\r
-                       \r
-               }\r
-               break;\r
-               \r
-               case POLICY_LEX_RX_NOT_EQUALS:\r
-                       regcomp(&reg, this->rhs, REG_EXTENDED|REG_NOSUB);\r
-                       rad_assert(data != NULL);\r
-                       rcode = regexec(&reg, data,\r
-                                       0, NULL, 0);\r
-                       rcode = (rcode != 0);\r
-                       regfree(&reg);\r
-                               break;\r
-#endif /* HAVE_REGEX_H */\r
-               default:\r
-                       rcode = FALSE;\r
-                       break;\r
-               } /* switch over comparison operators */\r
-               break;          /* default from first switch over compare */\r
-       }\r
-\r
-       /*\r
-        *      No trailing &&, ||\r
-        */\r
-       switch (this->child_condition) {\r
-       default:\r
-               return rcode;\r
-\r
-       case POLICY_LEX_L_AND:\r
-               if (!rcode) return rcode; /* FALSE && x == FALSE */\r
-               break;\r
-\r
-       case POLICY_LEX_L_OR:\r
-               if (rcode) return rcode; /* TRUE && x == TRUE */\r
-               break;\r
-       }\r
-\r
-       /*\r
-        *      Tail recursion.\r
-        */\r
-       this = (const policy_condition_t *) this->child;\r
-       goto redo;\r
-\r
-       return 1;               /* should never reach here */\r
-}\r
-\r
-\r
-/*\r
- *     Evaluate an 'if' statement\r
- */\r
-static int evaluate_if(policy_state_t *state, const policy_item_t *item)\r
-{\r
-       int rcode;\r
-       const policy_if_t *this;\r
-\r
-       this = (const policy_if_t *) item;\r
-\r
-       /*\r
-        *      evaluate_condition calls itself recursively.\r
-        *      We should probably allocate a new state, instead.\r
-        */\r
-       rcode = evaluate_condition(state, this->condition);\r
-       debug_evaluate("IF condition returned %s\n",\r
-              rcode ? "true" : "false");\r
-       if (rcode) {\r
-               rcode = policy_stack_push(state, this->if_true);\r
-               if (!rcode) return rcode;\r
-       } else if (this->if_false) {\r
-               rcode = policy_stack_push(state, this->if_false);\r
-               if (!rcode) return rcode;\r
-       }\r
-\r
-       /*\r
-        *      'if' can fail, if the block it's processing fails.\r
-        */\r
-       return 1;;\r
-}\r
-\r
-\r
-/*\r
- *     Make a VALUE_PAIR from a policy_assignment_t*\r
- *\r
- *     The assignment operator has to be '='.\r
- */\r
-static VALUE_PAIR *assign2vp(REQUEST *request,\r
-                            const policy_assignment_t *assign)\r
-{\r
-       VALUE_PAIR *vp;\r
-       LRAD_TOKEN operator = T_OP_EQ;\r
-       const char *value = assign->rhs;\r
-       char buffer[2048];\r
-\r
-       if ((assign->rhs_type == POLICY_LEX_DOUBLE_QUOTED_STRING) &&\r
-           (strchr(assign->rhs, '%') != NULL)) {\r
-               radius_xlat(buffer, sizeof(buffer), assign->rhs,\r
-                           request, NULL);\r
-               value = buffer;\r
-       }\r
-\r
-       /*\r
-        *      This is crappy.. fix it.\r
-        */\r
-       switch (assign->assign) {\r
-       case POLICY_LEX_ASSIGN:\r
-               operator = T_OP_EQ;\r
-               break;\r
-\r
-       case POLICY_LEX_SET_EQUALS:\r
-               operator = T_OP_SET;\r
-               break;\r
-       \r
-       case POLICY_LEX_PLUS_EQUALS:\r
-               operator = T_OP_ADD;\r
-               break;\r
-       \r
-       default:\r
-               fprintf(stderr, "Expected '=' for operator, not '%s' at line %d\n",\r
-                       lrad_int2str(rlm_policy_tokens,\r
-                                    assign->assign, "?"),\r
-                       assign->item.lineno);\r
-               return NULL;\r
-       }\r
-       \r
-       vp = pairmake(assign->lhs, value, operator);\r
-       if (!vp) {\r
-               fprintf(stderr, "SHIT: %s %s\n", value, librad_errstr);\r
-       }\r
-\r
-       return vp;\r
-}\r
-\r
-\r
-/*\r
- *     Evaluate a 'packet .= {attrs}' statement\r
- */\r
-static int evaluate_attr_list(policy_state_t *state, const policy_item_t *item)\r
-{\r
-       const policy_attributes_t *this;\r
-       VALUE_PAIR **vps = NULL;\r
-       VALUE_PAIR *vp, *head, **tail;\r
-       const policy_item_t *attr;\r
-\r
-       this = (const policy_attributes_t *) item;\r
-\r
-       switch (this->where) {\r
-       case POLICY_RESERVED_CONTROL:\r
-               vps = &(state->request->config_items);\r
-               break;\r
-\r
-       case POLICY_RESERVED_REQUEST:\r
-               vps = &(state->request->packet->vps);\r
-               break;\r
-\r
-       case POLICY_RESERVED_REPLY:\r
-               vps = &(state->request->reply->vps);\r
-               break;\r
-\r
-       case POLICY_RESERVED_PROXY_REQUEST:\r
-               if (!state->request->proxy) return 0; /* FIXME: print error */\r
-               vps = &(state->request->proxy->vps);\r
-               break;\r
-\r
-       case POLICY_RESERVED_PROXY_REPLY:\r
-               if (!state->request->proxy_reply) return 0; /* FIXME: print error */\r
-               vps = &(state->request->proxy_reply->vps);\r
-               break;\r
-\r
-       default:\r
-               return 0;\r
-       }\r
-\r
-       head = NULL;\r
-       tail = &head;\r
-\r
-       for (attr = this->attributes; attr != NULL; attr = attr->next) {\r
-               if (attr->type != POLICY_TYPE_ASSIGNMENT) {\r
-                       fprintf(stderr, "bad assignment in attribute list at line %d\n", attr->lineno);\r
-                       pairfree(&head);\r
-                       return 0;\r
-               }\r
-\r
-               vp = assign2vp(state->request, (const policy_assignment_t *) attr);\r
-               if (!vp) {\r
-                       fprintf(stderr, "Failed to allocate VP\n");\r
-                       pairfree(&head);\r
-                       return 0;\r
-               }\r
-               *tail = vp;\r
-               tail = &(vp->next);\r
-       }\r
-\r
-       switch (this->how) {\r
-       case POLICY_LEX_SET_EQUALS: /* dangerous: removes all previous things! */\r
-               pairfree(vps);\r
-               *vps = head;\r
-               break;\r
-\r
-       case POLICY_LEX_ASSIGN: /* 'union' */\r
-               pairmove(vps, &head);\r
-               pairfree(&head);\r
-               break;\r
-\r
-       case POLICY_LEX_CONCAT_EQUALS:\r
-               pairadd(vps, head);\r
-               break;\r
-\r
-       default:\r
-               fprintf(stderr, "HUH?\n");\r
-               pairfree(&head);\r
-               return 0;\r
-       }\r
-\r
-       return 1;\r
-}\r
-\r
-\r
-/*\r
- *     Evaluate an 'call foo' statement\r
- */\r
-static int evaluate_call(policy_state_t *state, const policy_item_t *item)\r
-{\r
-       int rcode;\r
-       const policy_call_t *this;\r
-       rlm_policy_name_t mypolicy;\r
-       const rlm_policy_name_t *policy;\r
-\r
-       this = (const policy_call_t *) item;\r
-\r
-       strNcpy(mypolicy.name, this->name, sizeof(mypolicy.name));\r
-       policy = rbtree_finddata(state->inst->policies, &mypolicy);\r
-       if (!policy) return 0;  /* not found... */\r
-       \r
-       DEBUG2("rlm_policy: Evaluating policy %s", this->name);\r
-       \r
-       rad_assert(policy->policy->type != POLICY_TYPE_BAD);\r
-       rad_assert(policy->policy->type < POLICY_TYPE_NUM_TYPES);\r
-       \r
-       /*\r
-        *      Push it onto the stack.  Other code will take care of\r
-        *      calling it.\r
-        */\r
-       rcode = policy_stack_push(state, policy->policy);\r
-       if (!rcode) {\r
-               return rcode;\r
-       }\r
-\r
-       /*\r
-        *      Function calls always succeed?\r
-        *\r
-        *      FIXME: Push the function name, etc. onto the stack,\r
-        *      so we can check for infinite recursion above, and\r
-        *      so we can also check for return codes from functions\r
-        *      we call...\r
-        */\r
-       return 1;\r
-}\r
-\r
-\r
-/*\r
- *     State machine stuff.\r
- */\r
-typedef int (*policy_evaluate_type_t)(policy_state_t *, const policy_item_t *);\r
-\r
-\r
-/*\r
- *     MUST be kept in sync with policy_type_t\r
- */\r
-static policy_evaluate_type_t evaluate_functions[POLICY_TYPE_NUM_TYPES] = {\r
-       NULL,                   /* POLICY_TYPE_BAD */\r
-       evaluate_if,\r
-       evaluate_condition,\r
-       evaluate_assignment,\r
-       evaluate_attr_list,\r
-       evaluate_print,\r
-       NULL,                   /* define a named policy.. */\r
-       evaluate_call\r
-};\r
-\r
-\r
-/*\r
- *     Evaluate a policy, keyed by name.\r
- */\r
-static int policy_evaluate_name(policy_state_t *state, const char *name)\r
-{\r
-       int rcode;\r
-       const policy_item_t *this;\r
-       rlm_policy_name_t mypolicy, *policy;\r
-       \r
-       strNcpy(mypolicy.name, name, sizeof(mypolicy.name));\r
-       policy = rbtree_finddata(state->inst->policies, &mypolicy);\r
-       if (!policy) return RLM_MODULE_FAIL;\r
-       \r
-       DEBUG2("rlm_policy: Evaluating policy %s", name);\r
-       \r
-       rad_assert(policy->policy->type != POLICY_TYPE_BAD);\r
-       rad_assert(policy->policy->type < POLICY_TYPE_NUM_TYPES);\r
-       \r
-       rcode = policy_stack_push(state, policy->policy);\r
-       if (!rcode) {\r
-               return RLM_MODULE_FAIL;\r
-       }\r
-\r
-       /*\r
-        *      FIXME: Look for magic keywords like "return",\r
-        *      where the packet gets accepted/rejected/whatever\r
-        */\r
-       while (policy_stack_pop(state, &this)) {\r
-               rad_assert(this != NULL);\r
-               rad_assert(this->type != POLICY_TYPE_BAD);\r
-               rad_assert(this->type < POLICY_TYPE_NUM_TYPES);\r
-               \r
-               debug_evaluate("Evaluating at line %d\n",\r
-                              this->lineno);\r
-               rcode = (*evaluate_functions[this->type])(state,\r
-                                                         this);\r
-               if (!rcode) {\r
-                       return RLM_MODULE_FAIL;\r
-               }\r
-       } /* loop until the stack is empty */\r
-\r
-       return RLM_MODULE_OK;\r
-}\r
-\r
-\r
-/*\r
- *     Evaluate, which is pretty close to print, but we look at what\r
- *     we're printing.\r
- */\r
-int rlm_policy_evaluate(rlm_policy_t *inst, REQUEST *request, const char *name)\r
-{\r
-       int rcode;\r
-       policy_state_t *state;\r
-\r
-       state = rad_malloc(sizeof(*state));\r
-       memset(state, 0, sizeof(*state));\r
-       state->request = request;\r
-       state->inst = inst;\r
-\r
-       rcode = policy_evaluate_name(state, name);\r
-\r
-       free(state);\r
-\r
-       return rcode;           /* evaluated OK. */\r
-}\r
+/*
+ * evaluate.c          Evaluate a policy language
+ *
+ * Version:    $Id$
+ *
+ *   This program is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation; either version 2 of the License, or
+ *   (at your option) any later version.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with this program; if not, write to the Free Software
+ *   Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+ *
+ * Copyright 2004  Alan DeKok <aland@ox.org>
+ * Copyright 2006  The FreeRADIUS server project
+ */
+
+#include <freeradius-devel/ident.h>
+RCSID("$Id$")
+
+#include "rlm_policy.h"
+
+#ifdef HAVE_REGEX_H
+#include <regex.h>
+#endif
+
+#define debug_evaluate if (0) printf
+
+/*
+ *     Print stuff we've parsed
+ */
+static void policy_print(const policy_item_t *item, int indent)
+{
+       if (!item) {
+               if (indent) fprintf(fr_log_fp, "%*s", indent, " ");
+               fprintf(fr_log_fp, "[NULL]\n");
+               return;
+       }
+
+       while (item) {
+               switch (item->type) {
+               case POLICY_TYPE_BAD:
+                       if (indent) fprintf(fr_log_fp, "%*s", indent, " ");
+                       fprintf(fr_log_fp, "[BAD STATEMENT]");
+                       break;
+
+               case POLICY_TYPE_PRINT:
+                       if (indent) fprintf(fr_log_fp, "%*s", indent, " ");
+                       {
+                               const policy_print_t *this;
+
+                               this = (const policy_print_t *) item;
+
+                               if (this->rhs_type == POLICY_LEX_BARE_WORD) {
+                                       fprintf(fr_log_fp, "print %s\n", this->rhs);
+                               } else {
+                                       fprintf(fr_log_fp, "print \"%s\"\n", this->rhs);
+                               }
+                       }
+                       break;
+
+               case POLICY_TYPE_ASSIGNMENT:
+                       {
+                               const policy_assignment_t *assign;
+
+                               assign = (const policy_assignment_t *) item;
+                               if (indent) fprintf(fr_log_fp, "%*s", indent, " ");
+
+                               fprintf(fr_log_fp, "\t%s %s ", assign->lhs,
+                                      fr_int2str(rlm_policy_tokens,
+                                                   assign->assign, "?"));
+                               if (assign->rhs_type == POLICY_LEX_BARE_WORD) {
+                                       fprintf(fr_log_fp, "%s\n", assign->rhs);
+                               } else {
+                                       /*
+                                        *      FIXME: escape "
+                                        */
+                                       fprintf(fr_log_fp, "\"%s\"\n", assign->rhs);
+                               }
+                       }
+                       break;
+
+               case POLICY_TYPE_CONDITIONAL: /* no indentation here */
+                       {
+                               const policy_condition_t *condition;
+
+                               condition = (const policy_condition_t *) item;
+
+                               fprintf(fr_log_fp, "(");
+
+                               if (condition->sense) {
+                                       fprintf(fr_log_fp, "!");
+                               }
+
+                               /*
+                                *      Nested conditions.
+                                */
+                               if (condition->compare == POLICY_LEX_L_BRACKET) {
+                                       policy_print(condition->child, indent);
+                                       fprintf(fr_log_fp, ")");
+                                       break;
+                               }
+
+                               if (condition->compare == POLICY_LEX_L_NOT) {
+                                       fprintf(fr_log_fp, "!");
+                                       policy_print(condition->child, indent);
+                                       fprintf(fr_log_fp, ")");
+                                       break;
+                               }
+
+                               if (condition->compare == POLICY_LEX_CMP_TRUE) {
+                                       fprintf(fr_log_fp, "%s)", condition->lhs);
+                                       break;
+                               }
+
+                               if (condition->lhs_type == POLICY_LEX_FUNCTION) {
+                                       fprintf(fr_log_fp, "%s()", condition->lhs);
+                               } else {
+                                       /*
+                                        *      FIXME: escape ",
+                                        *      and move all of this logic
+                                        *      to a function.
+                                        */
+                                       fprintf(fr_log_fp, "\"%s\"", condition->lhs);
+                               }
+
+                               /*
+                                *      We always print this condition.
+                                */
+                               fprintf(fr_log_fp, " %s ", fr_int2str(rlm_policy_tokens,
+                                                           condition->compare,
+                                                           "?"));
+                               if (condition->rhs_type == POLICY_LEX_BARE_WORD) {
+                                       fprintf(fr_log_fp, "%s", condition->rhs);
+                               } else {
+                                       /*
+                                        *      FIXME: escape ",
+                                        *      and move all of this logic
+                                        *      to a function.
+                                        */
+                                       fprintf(fr_log_fp, "\"%s\"", condition->rhs);
+                               }
+                               fprintf(fr_log_fp, ")");
+
+                               if ((condition->child_condition != POLICY_LEX_BAD) &&
+                                   (condition->child_condition != POLICY_LEX_BARE_WORD)) {
+                                       fprintf(fr_log_fp, " %s ", fr_int2str(rlm_policy_tokens, condition->child_condition, "?"));
+                                       policy_print(condition->child, indent);
+                               }
+                       }
+                       break;
+
+               case POLICY_TYPE_IF:
+                       {
+                               const policy_if_t *statement;
+
+                               statement = (const policy_if_t *) item;
+
+                               if (indent) fprintf(fr_log_fp, "%*s", indent, " ");
+                               fprintf(fr_log_fp, "if ");
+                               policy_print(statement->condition, indent);
+                               fprintf(fr_log_fp, " {\n");
+                               policy_print(statement->if_true, indent + 1);
+                               if (indent) fprintf(fr_log_fp, "%*s", indent, " ");
+                               if (statement->if_false) {
+                                       fprintf(fr_log_fp, "} else ");
+                                       if (statement->if_false->type == POLICY_TYPE_ASSIGNMENT) {
+                                               fprintf(fr_log_fp, " { ");
+                                               policy_print(statement->if_false, indent + 1);
+                                               if (indent) fprintf(fr_log_fp, "%*s", indent, " ");
+                                               fprintf(fr_log_fp, " }");
+                                       } else {
+                                               policy_print(statement->if_false, indent + 1);
+                                       }
+                               } else {
+                                       fprintf(fr_log_fp, "}\n");
+                               }
+                       }
+                       break;
+
+               case POLICY_TYPE_ATTRIBUTE_LIST:
+                       {
+                               const policy_attributes_t *this;
+
+                               this = (const policy_attributes_t *) item;
+
+                               if (indent) fprintf(fr_log_fp, "%*s", indent, " ");
+                               fprintf(fr_log_fp, "%s %s {\n",
+                                      fr_int2str(policy_reserved_words,
+                                                   this->where, "?"),
+                                      fr_int2str(rlm_policy_tokens,
+                                                   this->how, "?"));
+                               policy_print(this->attributes, indent + 1);
+                               if (indent) fprintf(fr_log_fp, "%*s", indent, " ");
+                               fprintf(fr_log_fp, "}\n");
+                       }
+                       break;
+
+               case POLICY_TYPE_NAMED_POLICY:
+                       {
+                               const policy_named_t *this;
+
+                               this = (const policy_named_t *) item;
+                               if (indent) fprintf(fr_log_fp, "%*s", indent, " ");
+                               fprintf(fr_log_fp, "policy %s {\n", this->name);
+                               policy_print(this->policy, indent + 1);
+                               if (indent) fprintf(fr_log_fp, "%*s", indent, " ");
+                               fprintf(fr_log_fp, "}\n");
+                       }
+                       break;
+
+               case POLICY_TYPE_CALL:
+                       {
+                               const policy_call_t *this;
+
+                               this = (const policy_call_t *) item;
+                               if (indent) fprintf(fr_log_fp, "%*s", indent, " ");
+                               fprintf(fr_log_fp, "call %s\n", this->name);
+                       }
+                       break;
+
+               case POLICY_TYPE_RETURN:
+                       {
+                               const policy_return_t *this;
+
+                               this = (const policy_return_t *) item;
+                               if (indent) fprintf(fr_log_fp, "%*s", indent, " ");
+                               fprintf(fr_log_fp, "return %s\n",
+                                      fr_int2str(policy_return_codes,
+                                                   this->rcode, "???"));
+                       }
+                       break;
+
+               case POLICY_TYPE_MODULE:
+                       {
+                               const policy_module_t *this;
+
+                               this = (const policy_module_t *) item;
+                               if (indent) fprintf(fr_log_fp, "%*s", indent, " ");
+                               fprintf(fr_log_fp, "module %s <stuff>\n",
+                                      fr_int2str(policy_component_names,
+                                                   this->component, "???"));
+                       }
+                       break;
+
+               default:
+                       if (indent) fprintf(fr_log_fp, "%*s", indent, " ");
+                       fprintf(fr_log_fp, "[HUH?]\n");
+                       break;
+
+               }
+
+               item = item->next;
+       }
+}
+
+
+void rlm_policy_print(const policy_item_t *item)
+{
+       if (!fr_log_fp) return;
+
+       fprintf(fr_log_fp, "# rlm_policy \n");
+       policy_print(item, 0);
+}
+
+/*
+ *     Internal stack of things to do.  This lets us have function
+ *     calls...
+ *
+ *     Yes, we should learn lex, yacc, etc.
+ */
+#define POLICY_MAX_STACK 16
+typedef struct policy_state_t {
+       rlm_policy_t    *inst;
+       REQUEST         *request; /* so it's not passed on the C stack */
+       int             rcode;  /* for functions, etc. */
+       int             component; /* for calling other modules */
+       int             depth;
+       const policy_item_t *stack[POLICY_MAX_STACK];
+} policy_state_t;
+
+
+static int policy_evaluate_name(policy_state_t *state, const char *name);
+
+/*
+ *     Push an item onto the state.
+ */
+static int policy_stack_push(policy_state_t *state, const policy_item_t *item)
+{
+       rad_assert(state->depth >= 0);
+
+       /*
+        *      Asked to push nothing.  Don't push it.
+        */
+       if (!item) return 1;
+
+       /*
+        *      State is full.  Die.
+        */
+       if (state->depth >= POLICY_MAX_STACK) {
+               return 0;
+       }
+
+       /*
+        *      Walk back up the stack, looking for previous ocurrances
+        *      of this name.  If found, we have infinite recursion,
+        *      which we stop dead in the water!
+        *
+        *      This isn't strictly necessary right now, as we look up
+        *      policies by name when they're first referenced.  This
+        *      means that ALL references are backwards (to the start
+        *      of the file), which means that there are no circular
+        *      references.
+        */
+       if (item->type == POLICY_TYPE_NAMED_POLICY) {
+               int i;
+
+               for (i = 0; i < state->depth; i++) {
+                       /*
+                        *      Check for circular references, by seeing
+                        *      if the function is already on the stack.
+                        *
+                        *      Hmmm... do we want to do this for any type?
+                        */
+                       if (state->stack[i] == item) {
+                               debug_evaluate("Circular call to policy %s\n",
+                                              ((const policy_named_t *) item)->name);
+                               return 0;
+                       }
+               }
+       }
+
+       debug_evaluate("push %d %p\n", state->depth, item);
+
+       state->stack[state->depth] = item;
+       state->depth++;         /* points to unused entry */
+
+       return 1;
+}
+
+
+/*
+ *     Pop an item from the state.
+ */
+static int policy_stack_pop(policy_state_t *state, const policy_item_t **pitem)
+{
+       rad_assert(pitem != NULL);
+       rad_assert(state->depth >= 0);
+
+ redo:
+       if (state->depth == 0) {
+               *pitem = NULL;
+               return 0;
+       }
+
+       *pitem = state->stack[state->depth - 1];
+
+       /*
+        *      Named policies are on the stack for catching recursion.
+        */
+       if ((*pitem)->type == POLICY_TYPE_NAMED_POLICY) {
+               state->depth--;
+               goto redo;
+       }
+
+       /*
+        *      Process the whole item list.
+        */
+       if ((*pitem)->next) {
+               state->stack[state->depth - 1] = (*pitem)->next;
+               debug_evaluate("pop/push %d %p\n", state->depth - 1, *pitem);
+       } else {
+               state->depth--;         /* points to unused entry */
+               debug_evaluate("pop %d %p\n", state->depth, *pitem);
+       }
+
+       return 1;
+}
+
+
+/*
+ *     Evaluate a print statement
+ */
+static int evaluate_print(policy_state_t *state, const policy_item_t *item)
+{
+       const policy_print_t *this;
+
+       if (!fr_log_fp) return 1;
+
+       this = (const policy_print_t *) item;
+
+       if (this->rhs_type == POLICY_LEX_BARE_WORD) {
+               fprintf(fr_log_fp, "%s\n", this->rhs);
+       } else {
+               char buffer[1024];
+
+               radius_xlat(buffer, sizeof(buffer), this->rhs,
+                           state->request, NULL);
+               fprintf(fr_log_fp, "%s", buffer);
+               if (!strchr(buffer, '\n')) fprintf(fr_log_fp, "\n");
+       }
+
+       /*
+        *      Doesn't change state->rcode
+        */
+
+       return 1;
+}
+
+/*
+ *     Return a VALUE_PAIR, given an attribute name.
+ *
+ *     FIXME: Have it return the N'th one, too, like
+ *     doc/variables.txt?
+ *
+ *     The amount of duplicated code is getting annoying...
+ */
+static VALUE_PAIR *find_vp(REQUEST *request, const char *name)
+{
+       const char *p;
+       const DICT_ATTR *dattr;
+       VALUE_PAIR *vps;
+
+       p = name;
+       vps = request->packet->vps;;
+
+       /*
+        *      FIXME: use names from reserved word list?
+        */
+       if (strncasecmp(name, "request:", 8) == 0) {
+               p += 8;
+       } else if (strncasecmp(name, "reply:", 6) == 0) {
+               p += 6;
+               vps = request->reply->vps;
+#ifdef WITH_PROXY
+       } else if (strncasecmp(name, "proxy-request:", 14) == 0) {
+               p += 14;
+               if (request->proxy) {
+                       vps = request->proxy->vps;
+               }
+       } else if (strncasecmp(name, "proxy-reply:", 12) == 0) {
+               p += 12;
+               if (request->proxy_reply) {
+                       vps = request->proxy_reply->vps;
+               }
+#endif
+       } else if (strncasecmp(name, "control:", 8) == 0) {
+               p += 8;
+               vps = request->config_items;
+       } /* else it must be a bare attribute name */
+
+       if (!vps) {
+               return NULL;
+       }
+
+       dattr = dict_attrbyname(p);
+       if (!dattr) {
+               fprintf(stderr, "No such attribute %s\n", p);
+               return NULL;    /* no such attribute */
+       }
+
+       return pairfind(vps, dattr->attr, dattr->vendor);
+}
+
+
+/*
+ *     Evaluate an assignment
+ *
+ *     Not really used much...
+ */
+static int evaluate_assignment(UNUSED policy_state_t *state, const policy_item_t *item)
+{
+       const policy_assignment_t *this;
+#if 0
+       const DICT_ATTR *dattr;
+#endif
+
+       this = (const policy_assignment_t *) item;
+
+       rad_assert(this->lhs != NULL);
+       rad_assert(this->rhs != NULL);
+
+#if 0
+       dattr = dict_attrbyname(this->lhs);
+       if (!dattr) {
+               fprintf(stderr, "HUH?\n");
+               return 0;
+       }
+#endif
+
+       return 1;
+}
+
+
+/*
+ *     Evaluate a condition
+ */
+static int evaluate_condition(policy_state_t *state, const policy_item_t *item)
+{
+       int rcode;
+       const policy_condition_t *this;
+       VALUE_PAIR *vp = NULL;
+       const char *data = NULL;
+       int compare;
+#ifdef HAVE_REGEX_H
+       regex_t reg;
+#endif
+       char buffer[256];
+       char lhs_buffer[2048];
+
+       this = (const policy_condition_t *) item;
+
+ redo:
+       /*
+        *      FIXME: Don't always do this...
+        */
+       if (this->compare != POLICY_LEX_L_BRACKET) {
+               if (this->lhs_type == POLICY_LEX_FUNCTION) {
+                       /*
+                        *      We can't call evaluate_call here,
+                        *      because that just pushes stuff onto
+                        *      the stack, and we want to actually
+                        *      evaluate all of it...
+                        */
+                       rcode = policy_evaluate_name(state, this->lhs);
+                       data = fr_int2str(policy_return_codes, rcode, "???");
+                       strlcpy(lhs_buffer, data, sizeof(lhs_buffer)); /* FIXME: yuck */
+               } else if (this->lhs_type == POLICY_LEX_DOUBLE_QUOTED_STRING) {
+                       if (radius_xlat(lhs_buffer, sizeof(lhs_buffer), this->lhs,
+                                       state->request, NULL) > 0) {
+                               data = lhs_buffer;
+                       }
+               }
+       }
+
+       switch (this->compare) {
+       case POLICY_LEX_L_BRACKET: /* nested brackets are a special case */
+               rcode = evaluate_condition(state, this->child);
+               break;
+
+       case POLICY_LEX_L_NOT:
+               rcode = evaluate_condition(state, this->child);
+               rcode = (rcode == FALSE); /* reverse sense of test */
+               break;
+
+       case POLICY_LEX_CMP_FALSE: /* non-existence */
+               if (this->lhs_type == POLICY_LEX_BARE_WORD) {
+                       vp = find_vp(state->request, this->lhs);
+                       rcode = (vp == NULL);
+               } else {
+                       rcode = (data == NULL);
+               }
+               break;
+
+       case POLICY_LEX_CMP_TRUE: /* existence */
+               if (this->lhs_type == POLICY_LEX_BARE_WORD) {
+                       vp = find_vp(state->request, this->lhs);
+                       rcode = (vp != NULL);
+               } else {
+                       rcode = (data != NULL);
+               }
+               break;
+
+       default:                /* process other comparisons */
+               if ((this->compare != POLICY_LEX_CMP_EQUALS) &&
+#ifdef HAVE_REGEX_H
+                   (this->compare != POLICY_LEX_RX_EQUALS) &&
+                   (this->compare != POLICY_LEX_RX_NOT_EQUALS) &&
+#endif
+                   (this->compare != POLICY_LEX_LT) &&
+                   (this->compare != POLICY_LEX_GT) &&
+                   (this->compare != POLICY_LEX_LE) &&
+                   (this->compare != POLICY_LEX_GE) &&
+                   (this->compare != POLICY_LEX_CMP_NOT_EQUALS)) {
+                       fprintf(stderr, "%d: bad comparison\n",
+                               this->item.lineno);
+                       return FALSE;
+               }
+
+               if (this->lhs_type == POLICY_LEX_BARE_WORD) {
+                       VALUE_PAIR *myvp;
+
+                       vp = find_vp(state->request, this->lhs);
+
+                       /*
+                        *      A op B is FALSE if A doesn't
+                        *      exist.
+                        */
+                       if (!vp) {
+                               rcode = FALSE;
+                               break;
+                       }
+
+                       /*
+                        *      FIXME: Move sanity checks to
+                        *      post-parse code, so we don't do
+                        *      it on every packet.
+                        */
+                       vp_prints_value(buffer, sizeof(buffer), vp, 0);
+                       myvp = pairmake(vp->name, this->rhs, T_OP_EQ);
+                       if (!myvp) return FALSE; /* memory failure */
+                       data = buffer;
+
+                       /*
+                        *      FIXME: What to do about comparisons
+                        *      where vp doesn't exist?  Right now,
+                        *      "simplepaircmp" returns -1, which is
+                        *      probably a bad idea.  it should
+                        *      instead take an operator, a pointer to
+                        *      the comparison result, and return
+                        *      "true/false" for "comparions
+                        *      succeeded/failed", which are different
+                        *      error codes than "comparison is less
+                        *      than, equal to, or greater than zero".
+                        */
+                       compare = radius_callback_compare(state->request,
+                                                         vp, myvp, NULL, NULL);
+                       pairfree(&myvp);
+
+               } else {
+                       /*
+                        *      FIXME: Do something for RHS type?
+                        */
+                       fr_printf_log("CMP %s %s\n", lhs_buffer, this->rhs);
+                       compare = strcmp(lhs_buffer, this->rhs);
+               }
+
+               debug_evaluate("CONDITION COMPARE %d\n", compare);
+
+               switch (this->compare) {
+               case POLICY_LEX_CMP_EQUALS:
+                       rcode = (compare == 0);
+                       break;
+
+               case POLICY_LEX_CMP_NOT_EQUALS:
+                       rcode = (compare != 0);
+                       break;
+
+               case POLICY_LEX_LT:
+                       rcode = (compare < 0);
+                       break;
+
+               case POLICY_LEX_GT:
+                       rcode = (compare > 0);
+                       break;
+
+               case POLICY_LEX_LE:
+                       rcode =(compare <= 0);
+                       break;
+
+               case POLICY_LEX_GE:
+                       rcode = (compare >= 0);
+                       break;
+
+#ifdef HAVE_REGEX_H
+               case POLICY_LEX_RX_EQUALS:
+               { /* FIXME: copied from src/main/valuepair.c */
+                       int i;
+                       regmatch_t rxmatch[REQUEST_MAX_REGEX + 1];
+
+                       /*
+                        *      Include substring matches.
+                        */
+                       if (regcomp(&reg, this->rhs,
+                                   REG_EXTENDED) != 0) {
+                               /* FIXME: print error */
+                               return FALSE;
+                       }
+                       rad_assert(data != NULL);
+                       rcode = regexec(&reg, data,
+                                       REQUEST_MAX_REGEX + 1,
+                                       rxmatch, 0);
+                       rcode = (rcode == 0);
+                       regfree(&reg);
+
+                       /*
+                        *      Add %{0}, %{1}, etc.
+                        */
+                       for (i = 0; i <= REQUEST_MAX_REGEX; i++) {
+                               char *p;
+                               char rxbuffer[256];
+
+                               /*
+                                *      Didn't match: delete old
+                                *      match, if it existed.
+                                */
+                               if (!rcode ||
+                                   (rxmatch[i].rm_so == -1)) {
+                                       p = request_data_get(state->request, state->request,
+                                                            REQUEST_DATA_REGEX | i);
+                                       if (p) {
+                                               free(p);
+                                               continue;
+                                       }
+
+                                       /*
+                                        *      No previous match
+                                        *      to delete, stop.
+                                        */
+                                       break;
+                               }
+
+                               /*
+                                *      Copy substring into buffer.
+                                */
+                               memcpy(rxbuffer,
+                                      data + rxmatch[i].rm_so,
+                                      rxmatch[i].rm_eo - rxmatch[i].rm_so);
+                               rxbuffer[rxmatch[i].rm_eo - rxmatch[i].rm_so] = '\0';
+
+                               /*
+                                *      Copy substring, and add it to
+                                *      the request.
+                                *
+                                *      Note that we don't check
+                                *      for out of memory, which is
+                                *      the only error we can get...
+                                */
+                               p = strdup(rxbuffer);
+                               request_data_add(state->request,
+                                                state->request,
+                                                REQUEST_DATA_REGEX | i,
+                                                p, free);
+                       }
+
+               }
+               break;
+
+               case POLICY_LEX_RX_NOT_EQUALS:
+                       regcomp(&reg, this->rhs, REG_EXTENDED|REG_NOSUB);
+                       rad_assert(data != NULL);
+                       rcode = regexec(&reg, data,
+                                       0, NULL, 0);
+                       rcode = (rcode != 0);
+                       regfree(&reg);
+                               break;
+#endif /* HAVE_REGEX_H */
+               default:
+                       rcode = FALSE;
+                       break;
+               } /* switch over comparison operators */
+               break;          /* default from first switch over compare */
+       }
+
+       if (this->sense) rcode = (rcode == FALSE); /* reverse sense of test */
+
+       /*
+        *      No trailing &&, ||
+        */
+       switch (this->child_condition) {
+       default:
+               return rcode;
+
+       case POLICY_LEX_L_AND:
+               if (!rcode) return rcode; /* FALSE && x == FALSE */
+               break;
+
+       case POLICY_LEX_L_OR:
+               if (rcode) return rcode; /* TRUE && x == TRUE */
+               break;
+       }
+
+       /*
+        *      Tail recursion.
+        */
+       this = (const policy_condition_t *) this->child;
+       goto redo;
+
+       return 1;               /* should never reach here */
+}
+
+
+/*
+ *     Evaluate an 'if' statement
+ */
+static int evaluate_if(policy_state_t *state, const policy_item_t *item)
+{
+       int rcode;
+       const policy_if_t *this;
+
+       this = (const policy_if_t *) item;
+
+       /*
+        *      evaluate_condition calls itself recursively.
+        *      We should probably allocate a new state, instead.
+        */
+       rcode = evaluate_condition(state, this->condition);
+       debug_evaluate("IF condition returned %s\n",
+              rcode ? "true" : "false");
+       if (rcode) {
+               rcode = policy_stack_push(state, this->if_true);
+               if (!rcode) return rcode;
+       } else if (this->if_false) {
+               rcode = policy_stack_push(state, this->if_false);
+               if (!rcode) return rcode;
+       }
+
+       /*
+        *      'if' can fail, if the block it's processing fails.
+        */
+       return 1;;
+}
+
+
+/*
+ *     Make a VALUE_PAIR from a policy_assignment_t*
+ *
+ *     The assignment operator has to be '='.
+ */
+static VALUE_PAIR *assign2vp(REQUEST *request,
+                            const policy_assignment_t *assign)
+{
+       VALUE_PAIR *vp;
+       FR_TOKEN operator = T_OP_EQ;
+       const char *value = assign->rhs;
+       char buffer[2048];
+
+       if ((assign->rhs_type == POLICY_LEX_DOUBLE_QUOTED_STRING) &&
+           (strchr(assign->rhs, '%') != NULL)) {
+               radius_xlat(buffer, sizeof(buffer), assign->rhs,
+                           request, NULL);
+               value = buffer;
+       }
+
+       /*
+        *      This is crappy.. fix it.
+        */
+       switch (assign->assign) {
+       case POLICY_LEX_ASSIGN:
+               operator = T_OP_EQ;
+               break;
+
+       case POLICY_LEX_SET_EQUALS:
+               operator = T_OP_SET;
+               break;
+
+       case POLICY_LEX_PLUS_EQUALS:
+               operator = T_OP_ADD;
+               break;
+
+       default:
+               fprintf(stderr, "Expected '=' for operator, not '%s' at line %d\n",
+                       fr_int2str(rlm_policy_tokens,
+                                    assign->assign, "?"),
+                       assign->item.lineno);
+               return NULL;
+       }
+
+       vp = pairmake(assign->lhs, value, operator);
+       if (!vp) {
+               fprintf(stderr, "Failed creating pair: %s %s\n", value, fr_strerror());
+       }
+
+       return vp;
+}
+
+
+/*
+ *     Evaluate a 'packet .= {attrs}' statement
+ */
+static int evaluate_attr_list(policy_state_t *state, const policy_item_t *item)
+{
+       const policy_attributes_t *this;
+       VALUE_PAIR **vps = NULL;
+       VALUE_PAIR *vp, *head, **tail;
+       const policy_item_t *attr;
+       policy_lex_t this_how;
+
+       this = (const policy_attributes_t *) item;
+
+       switch (this->where) {
+       case POLICY_RESERVED_CONTROL:
+               vps = &(state->request->config_items);
+               break;
+
+       case POLICY_RESERVED_REQUEST:
+               vps = &(state->request->packet->vps);
+               break;
+
+       case POLICY_RESERVED_REPLY:
+               vps = &(state->request->reply->vps);
+               break;
+
+#ifdef WITH_PROXY
+       case POLICY_RESERVED_PROXY_REQUEST:
+               if (!state->request->proxy) return 0; /* FIXME: print error */
+               vps = &(state->request->proxy->vps);
+               break;
+
+       case POLICY_RESERVED_PROXY_REPLY:
+               if (!state->request->proxy_reply) return 0; /* FIXME: print error */
+               vps = &(state->request->proxy_reply->vps);
+               break;
+#endif
+
+       default:
+               return 0;
+       }
+
+       head = NULL;
+       tail = &head;
+
+       for (attr = this->attributes; attr != NULL; attr = attr->next) {
+               if (attr->type != POLICY_TYPE_ASSIGNMENT) {
+                       fprintf(stderr, "bad assignment in attribute list at line %d\n", attr->lineno);
+                       pairfree(&head);
+                       return 0;
+               }
+
+               vp = assign2vp(state->request, (const policy_assignment_t *) attr);
+               if (!vp) {
+                       fprintf(stderr, "Failed to allocate VP\n");
+                       pairfree(&head);
+                       return 0;
+               }
+               *tail = vp;
+               tail = &(vp->next);
+       }
+
+       this_how = this->how;
+ retry_how:
+       switch (this_how) {
+       case POLICY_LEX_SET_EQUALS: /* dangerous: removes all previous things! */
+               pairfree(vps);
+               *vps = head;
+               break;
+
+       case POLICY_LEX_AFTER_TAIL_ASSIGN:
+               pairmove(vps, &head);
+               pairfree(&head);
+               break;
+
+       case POLICY_LEX_ASSIGN: /* 'union' */
+               pairmove(vps, &head);
+               pairfree(&head);
+               break;
+
+       case POLICY_LEX_BEFORE_HEAD_ASSIGN:
+               pairmove(&head, vps);
+               pairfree(vps);
+               *vps = head;
+               break;
+
+       case POLICY_LEX_AFTER_TAIL_EQUALS:
+       case POLICY_LEX_CONCAT_EQUALS:
+               pairadd(vps, head);
+               break;
+
+       case POLICY_LEX_BEFORE_HEAD_EQUALS:
+               pairadd(&head, *vps);
+               *vps = head;
+               break;
+
+       case POLICY_LEX_BEFORE_WHERE_EQUALS:
+       case POLICY_LEX_AFTER_WHERE_EQUALS:
+       case POLICY_LEX_BEFORE_WHERE_ASSIGN:
+       case POLICY_LEX_AFTER_WHERE_ASSIGN:
+               /* find location*/
+               {
+                       VALUE_PAIR *vpprev = NULL, *vpnext = NULL, *lvp;
+
+                       for(lvp = *vps; lvp; vpprev = lvp, lvp = lvp->next) {
+                               vpnext = lvp->next;
+                               lvp->next = NULL;
+                               if (evaluate_condition(state, this->where_loc))
+                                       break;
+                               lvp->next = vpnext;
+                       }
+
+                       if (lvp) { 
+                               switch(this_how) {
+                               case POLICY_LEX_BEFORE_WHERE_EQUALS:
+                               case POLICY_LEX_BEFORE_WHERE_ASSIGN:
+                                       if (vpprev) {
+                                               lvp->next = vpnext;
+                                               vpnext = lvp;
+                                               vpprev->next = NULL;
+                                               lvp = vpprev;
+                                       }
+                               default: /* always reached */
+                                       break;
+                               }
+
+                               switch(this_how) {
+                               case POLICY_LEX_BEFORE_WHERE_EQUALS:
+                                       if (vpprev) 
+                                               pairadd(&lvp, head);
+                                       else
+                                               *vps = lvp = head;
+                                       break;
+                               case POLICY_LEX_AFTER_WHERE_EQUALS:
+                                       pairadd(&lvp, head);
+                                       break;
+                               case POLICY_LEX_BEFORE_WHERE_ASSIGN:
+                                       if (vpprev) {
+                                               pairmove(&lvp, &head);
+                                               pairfree(&head);
+                                       }
+                                       else
+                                               *vps = lvp = head;
+                                       break;
+                               case POLICY_LEX_AFTER_WHERE_ASSIGN:
+                                       pairmove(&lvp, &head);
+                                       pairfree(&head);
+                                       break;
+                               default:/*never reached*/
+                                       break;
+                               }       
+                               for( ; lvp && lvp->next; lvp = lvp->next);
+                               if (lvp)
+                                       lvp->next = vpnext;
+                               break;
+                       }
+
+                       switch(this_how) {
+                               case POLICY_LEX_BEFORE_WHERE_EQUALS:
+                                       this_how = POLICY_LEX_BEFORE_HEAD_EQUALS;
+                                       break;
+                               case POLICY_LEX_AFTER_WHERE_EQUALS:
+                                       this_how = POLICY_LEX_AFTER_TAIL_EQUALS;
+                                       break;
+                               case POLICY_LEX_BEFORE_WHERE_ASSIGN:
+                                       this_how = POLICY_LEX_BEFORE_HEAD_ASSIGN;
+                                       break;
+                               case POLICY_LEX_AFTER_WHERE_ASSIGN:
+                                       this_how = POLICY_LEX_AFTER_TAIL_ASSIGN;
+                                       break;
+                               default: /*never reached*/
+                                       break;
+                       }
+                       goto retry_how; 
+               }
+               /* FALL-THROUGH */
+
+       default:
+               fprintf(stderr, "HUH?\n");
+               pairfree(&head);
+               return 0;
+       }
+
+       state->rcode = RLM_MODULE_UPDATED; /* we did stuff */
+
+       return 1;
+}
+
+
+/*
+ *     Evaluate a reference call to a module.
+ */
+static int evaluate_call(policy_state_t *state, const policy_item_t *item)
+{
+       int rcode;
+       const policy_call_t *this;
+       const policy_named_t *policy;
+
+       this = (const policy_call_t *) item;
+
+       policy = rlm_policy_find(state->inst->policies, this->name);
+       if (!policy) return 0;  /* not found... */
+
+       DEBUG2("rlm_policy: Evaluating policy %s", this->name);
+
+       rad_assert(policy->policy->type != POLICY_TYPE_BAD);
+       rad_assert(policy->policy->type < POLICY_TYPE_NUM_TYPES);
+
+       /*
+        *      Push the name of the function onto the stack,
+        *      so that we can catch recursive calls.
+        *
+        *      The "pop" function will skip over it when it sees it.
+        */
+       rcode = policy_stack_push(state, (const policy_item_t *) policy);
+       if (!rcode) {
+               return rcode;
+       }
+
+       /*
+        *      Push it onto the stack.  Other code will take care of
+        *      calling it.
+        */
+       rcode = policy_stack_push(state, policy->policy);
+       if (!rcode) {
+               return rcode;
+       }
+
+       return 1;
+}
+
+
+/*
+ *     Evaluate a return statement
+ */
+static int evaluate_return(policy_state_t *state, const policy_item_t *item)
+{
+       const policy_return_t *this;
+
+       this = (const policy_return_t *) item;
+       state->rcode = this->rcode;
+
+       return 1;               /* we succeeded */
+}
+
+
+/*
+ *     Evaluate a module statement
+ */
+static int evaluate_module(policy_state_t *state, const policy_item_t *item)
+{
+       const policy_module_t *this;
+
+       this = (const policy_module_t *) item;
+
+       /*
+        *      Just to be paranoid.  Maybe we want to loosen this
+        *      restriction in the future?
+        */
+       if (this->component != state->component) {
+               DEBUG2("rlm_policy: Cannot mix & match components");
+               return 0;
+       }
+
+       DEBUG2("rlm_policy: begin nested call");
+       state->rcode = modcall(this->component, this->mc, state->request);
+       DEBUG2("rlm_policy: end nested call");
+
+       return 1;               /* we succeeded */
+}
+
+
+/*
+ *     State machine stuff.
+ */
+typedef int (*policy_evaluate_type_t)(policy_state_t *, const policy_item_t *);
+
+
+/*
+ *     MUST be kept in sync with policy_type_t
+ */
+static policy_evaluate_type_t evaluate_functions[POLICY_TYPE_NUM_TYPES] = {
+       NULL,                   /* POLICY_TYPE_BAD */
+       evaluate_if,
+       evaluate_condition,
+       evaluate_assignment,
+       evaluate_attr_list,
+       evaluate_print,
+       NULL,                   /* define a named policy.. */
+       evaluate_call,
+       evaluate_return,
+       evaluate_module
+};
+
+
+/*
+ *     Evaluate a policy, keyed by name.
+ */
+static int policy_evaluate_name(policy_state_t *state, const char *name)
+{
+       int rcode;
+       const policy_item_t *this;
+       policy_named_t mypolicy, *policy;
+
+       mypolicy.name = name;
+       policy = rbtree_finddata(state->inst->policies, &mypolicy);
+       if (!policy) return RLM_MODULE_FAIL;
+
+       DEBUG2("rlm_policy: Evaluating policy %s", name);
+
+       rad_assert(policy->item.type != POLICY_TYPE_BAD);
+       rad_assert(policy->item.type < POLICY_TYPE_NUM_TYPES);
+
+       rcode = policy_stack_push(state, policy->policy);
+       if (!rcode) {
+               return RLM_MODULE_FAIL;
+       }
+
+       /*
+        *      FIXME: Look for magic keywords like "return",
+        *      where the packet gets accepted/rejected/whatever
+        */
+       while (policy_stack_pop(state, &this)) {
+               rad_assert(this != NULL);
+               rad_assert(this->type != POLICY_TYPE_BAD);
+               rad_assert(this->type < POLICY_TYPE_NUM_TYPES);
+
+               debug_evaluate("Evaluating at line %d\n",
+                              this->lineno);
+               rcode = (*evaluate_functions[this->type])(state,
+                                                         this);
+               if (!rcode) {
+                       return RLM_MODULE_FAIL;
+               }
+       } /* loop until the stack is empty */
+
+       return state->rcode;
+}
+
+
+/*
+ *     Evaluate, which is pretty close to print, but we look at what
+ *     we're printing.
+ */
+int rlm_policy_evaluate(rlm_policy_t *inst, REQUEST *request, const char *name)
+{
+       int rcode;
+       policy_state_t *state;
+
+       state = rad_malloc(sizeof(*state));
+       memset(state, 0, sizeof(*state));
+       state->request = request;
+       state->inst = inst;
+       state->rcode = RLM_MODULE_OK;
+       state->component = fr_str2int(policy_component_names, name,
+                                       RLM_COMPONENT_COUNT);
+
+       rcode = policy_evaluate_name(state, name);
+
+       free(state);
+
+       return rcode;           /* evaluated OK. */
+}