Named policies are items just like any other.
authoraland <aland>
Tue, 8 Feb 2005 20:36:23 +0000 (20:36 +0000)
committeraland <aland>
Tue, 8 Feb 2005 20:36:23 +0000 (20:36 +0000)
Remove DOS LF's.

src/modules/rlm_policy/evaluate.c
src/modules/rlm_policy/parse.c
src/modules/rlm_policy/rlm_policy.c
src/modules/rlm_policy/rlm_policy.h

index 53cec7c..672c8d9 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
-       const rlm_policy_name_t *policy;\r
-\r
-       this = (const policy_call_t *) item;\r
-\r
-       policy = rlm_policy_find(state->inst->policies, this->name);\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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ * Copyright 2004  Alan DeKok <aland@ox.org>
+ */
+
+#include "rlm_policy.h"
+
+#include "modules.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) printf("%*s", indent, " ");
+               printf("[NULL]\n");
+               return;
+       }
+       
+       while (item) {
+               switch (item->type) {
+               case POLICY_TYPE_BAD:
+                       if (indent) printf("%*s", indent, " ");
+                       printf("[BAD STATEMENT]");
+                       break;
+                       
+               case POLICY_TYPE_PRINT:
+                       if (indent) printf("%*s", indent, " ");
+                       {
+                               const policy_print_t *this;
+
+                               this = (const policy_print_t *) item;
+                               
+                               if (this->rhs_type == POLICY_LEX_BARE_WORD) {
+                                       printf("print %s\n", this->rhs);
+                               } else {
+                                       printf("print \"%s\"\n", this->rhs);
+                               }
+                       }
+                       break;
+                       
+               case POLICY_TYPE_ASSIGNMENT:
+                       {
+                               const policy_assignment_t *assign;
+                               
+                               assign = (const policy_assignment_t *) item;
+                               if (indent) printf("%*s", indent, " ");
+
+                               printf("\t%s %s ", assign->lhs,
+                                      lrad_int2str(rlm_policy_tokens,
+                                                   assign->assign, "?"));
+                               if (assign->rhs_type == POLICY_LEX_BARE_WORD) {
+                                       printf("%s\n", assign->rhs);
+                               } else {
+                                       /*
+                                        *      FIXME: escape "
+                                        */
+                                       printf("\"%s\"\n", assign->rhs);
+                               }
+                       }
+                       break;
+
+               case POLICY_TYPE_CONDITIONAL: /* no indentation here */
+                       {
+                               const policy_condition_t *condition;
+
+                               condition = (const policy_condition_t *) item;
+
+                               printf("(");
+
+                               /*
+                                *      Nested conditions.
+                                */
+                               if (condition->compare == POLICY_LEX_L_BRACKET) {
+                                       policy_print(condition->child, indent);
+                                       printf(")");
+                                       break;
+                               }
+
+                               if (condition->compare == POLICY_LEX_L_NOT) {
+                                       printf("!");
+                                       policy_print(condition->child, indent);
+                                       printf(")");
+                                       break;
+                               }
+
+                               if (condition->compare == POLICY_LEX_CMP_TRUE) {
+                                       printf("%s)", condition->lhs);
+                                       break;
+                               }
+
+                               if (condition->lhs_type == POLICY_LEX_BARE_WORD) {
+                                       printf("%s", condition->lhs);
+                               } else {
+                                       /*
+                                        *      FIXME: escape ",
+                                        *      and move all of this logic
+                                        *      to a function.
+                                        */
+                                       printf("\"%s\"", condition->lhs);
+                               }
+
+                               /*
+                                *      We always print this condition.
+                                */
+                               printf(" %s ", lrad_int2str(rlm_policy_tokens,
+                                                           condition->compare,
+                                                           "?"));
+                               if (condition->rhs_type == POLICY_LEX_BARE_WORD) {
+                                       printf("%s", condition->rhs);
+                               } else {
+                                       /*
+                                        *      FIXME: escape ",
+                                        *      and move all of this logic
+                                        *      to a function.
+                                        */
+                                       printf("\"%s\"", condition->rhs);
+                               }
+                               printf(")");
+                               
+                               if (condition->child_condition != POLICY_LEX_BAD) {
+                                       printf(" %s ", lrad_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) printf("%*s", indent, " ");
+                               printf("if ");
+                               policy_print(statement->condition, indent);
+                               printf(" {\n");
+                               policy_print(statement->if_true, indent + 1);
+                               if (indent) printf("%*s", indent, " ");
+                               if (statement->if_false) {
+                                       printf("} else ");
+                                       if (statement->if_false->type == POLICY_TYPE_ASSIGNMENT) {
+                                               printf(" { ");
+                                               policy_print(statement->if_false, indent + 1);
+                                               if (indent) printf("%*s", indent, " ");
+                                               printf(" }");
+                                       } else {
+                                               policy_print(statement->if_false, indent + 1);
+                                       }
+                               } else {
+                                       printf("}\n");
+                               }
+                       }
+                       break;
+
+               case POLICY_TYPE_ATTRIBUTE_LIST:
+                       {
+                               const policy_attributes_t *this;
+
+                               this = (const policy_attributes_t *) item;
+
+                               if (indent) printf("%*s", indent, " ");
+                               printf("%s %s {\n",
+                                      lrad_int2str(policy_reserved_words,
+                                                   this->where, "?"),
+                                      lrad_int2str(rlm_policy_tokens,
+                                                   this->how, "?"));
+                               policy_print(this->attributes, indent + 1);
+                               if (indent) printf("%*s", indent, " ");
+                               printf("}\n");
+                       }
+                       break;
+
+               case POLICY_TYPE_NAMED_POLICY:
+                       {
+                               const policy_named_t *this;
+
+                               this = (const policy_named_t *) item;
+                               if (indent) printf("%*s", indent, " ");
+                               printf("policy %s {\n", this->name);
+                               policy_print(this->policy, indent + 1);
+                               if (indent) printf("%*s", indent, " ");
+                               printf("}\n");
+                       }
+                       break;
+
+               case POLICY_TYPE_CALL:
+                       {
+                               const policy_call_t *this;
+
+                               this = (const policy_call_t *) item;
+                               if (indent) printf("%*s", indent, " ");
+                               printf("call %s\n", this->name);
+                       }
+                       break;
+
+               default:
+                       if (indent) printf("%*s", indent, " ");
+                       printf("[HUH?]\n");
+                       break;
+                       
+               }
+
+               item = item->next;
+       }
+}
+
+
+void rlm_policy_print(const policy_item_t *item)
+{
+       printf("----------------------------------------------------------\n");
+       policy_print(item, 0);
+       printf("----------------------------------------------------------\n");
+}
+
+/*
+ *     Internal stack of things to do.
+ *
+ *     When a function is about to be pushed onto the stack, we walk
+ *     backwards through the stack, and ensure that the function is
+ *     not already there.  This prevents infinite recursion.
+ *
+ *     This means that we NEVER pop functions.  Rather, we push the
+ *     function, and then immediately push it's first element.
+ *
+ *     When we've finished popping all of the elements, we pop the
+ *     function, realize it's a function, ignore it, and pop one more
+ *     entry.
+ */
+#define POLICY_MAX_STACK 16
+typedef struct policy_state_t {
+       rlm_policy_t    *inst;
+       int             depth;
+       REQUEST         *request; /* so it's not passed on the C stack */
+       const policy_item_t *stack[POLICY_MAX_STACK];
+} policy_state_t;
+
+
+/*
+ *     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);
+
+       if (state->depth == 0) {
+               *pitem = NULL;
+               return 0;
+       }
+
+       *pitem = state->stack[state->depth - 1];
+
+       /*
+        *      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;
+
+       this = (const policy_print_t *) item;
+
+       if (this->rhs_type == POLICY_LEX_BARE_WORD) {
+               printf("%s\n", this->rhs);
+       } else {
+               char buffer[1024];
+
+               radius_xlat(buffer, sizeof(buffer), this->rhs,
+                           state->request, NULL);
+               printf("%s", buffer);
+       }
+
+       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;
+       } 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;
+               }
+       } 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);
+}
+
+
+/*
+ *     Evaluate an assignment
+ */
+static int evaluate_assignment(policy_state_t *state, const policy_item_t *item)
+{
+       const policy_assignment_t *this;
+       const DICT_ATTR *dattr;
+
+       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;
+       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) &&
+           (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_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);
+                       /*
+                        *      FIXME: Move sanity checks to
+                        *      post-parse code, so we don't do
+                        *      it on every packet.
+                        */
+                       if (vp) {
+                               vp_prints_value(buffer, sizeof(buffer), vp, 0);
+                               myvp = pairmake(vp->name, this->rhs, T_OP_EQ);
+                       } else {
+                               buffer[0] = '\0';
+                               myvp = pairmake(this->lhs, this->rhs, T_OP_EQ);
+                       }
+                       data = buffer;
+                       if (!myvp) {
+                               return FALSE;
+                       }
+
+                       /*
+                        *      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 = simplepaircmp(state->request,
+                                               vp, myvp);
+                       pairfree(&myvp);
+                       
+               } else {
+                       /*
+                        *      FIXME: Do something for RHS type?
+                        */
+                       printf("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) {
+                               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 */
+       }
+
+       /*
+        *      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;
+       LRAD_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",
+                       lrad_int2str(rlm_policy_tokens,
+                                    assign->assign, "?"),
+                       assign->item.lineno);
+               return NULL;
+       }
+       
+       vp = pairmake(assign->lhs, value, operator);
+       if (!vp) {
+               fprintf(stderr, "SHIT: %s %s\n", value, librad_errstr);
+       }
+
+       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;
+
+       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;
+
+       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;
+
+       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);
+       }
+
+       switch (this->how) {
+       case POLICY_LEX_SET_EQUALS: /* dangerous: removes all previous things! */
+               pairfree(vps);
+               *vps = head;
+               break;
+
+       case POLICY_LEX_ASSIGN: /* 'union' */
+               pairmove(vps, &head);
+               pairfree(&head);
+               break;
+
+       case POLICY_LEX_CONCAT_EQUALS:
+               pairadd(vps, head);
+               break;
+
+       default:
+               fprintf(stderr, "HUH?\n");
+               pairfree(&head);
+               return 0;
+       }
+
+       return 1;
+}
+
+
+/*
+ *     Evaluate an 'call foo' statement
+ */
+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 it onto the stack.  Other code will take care of
+        *      calling it.
+        */
+       rcode = policy_stack_push(state, policy->policy);
+       if (!rcode) {
+               return rcode;
+       }
+
+       /*
+        *      Function calls always succeed?
+        *
+        *      FIXME: Push the function name, etc. onto the stack,
+        *      so we can check for infinite recursion above, and
+        *      so we can also check for return codes from functions
+        *      we call...
+        */
+       return 1;
+}
+
+
+/*
+ *     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 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 RLM_MODULE_OK;
+}
+
+
+/*
+ *     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;
+
+       rcode = policy_evaluate_name(state, name);
+
+       free(state);
+
+       return rcode;           /* evaluated OK. */
+}
index 3a2589f..b43d82f 100644 (file)
@@ -862,30 +862,29 @@ static int parse_named_policy(policy_lex_file_t *lexer, policy_item_t **tail)
                return 0;
        }
 
+       this->name = strdup(mystring);
        rcode = parse_block(lexer, &(this->policy));
        if (!rcode) {
                rlm_policy_free_item((policy_item_t *) this);
                return rcode;
        }
-       this->name = strdup(mystring);
 
        /*
         *      And insert it into the tree of policies.
         *
         *      For now, policy names aren't scoped, they're global.
         */
-       if (!rlm_policy_insert(lexer->policies, this->name, this->policy)) {
+       if (!rlm_policy_insert(lexer->policies, this)) {
                fprintf(stderr, "Failed to insert %s\n", this->name);
                rlm_policy_free_item((policy_item_t *) this);
                return 0;
        }
-       free(this);             /* FIXME: shouldn't have allocated it... */
 
        /*
-        *      Do NOT add it into the list here!  The above insertion
-        *      will take care of freeing it if anything goes wrong...
+        *      Do NOT add it into the list of parsed expressions!
+        *      The above insertion will take care of freeing it if
+        *      anything goes wrong...
         */
-
        return 1;
 }
 
@@ -1045,7 +1044,10 @@ static int parse_statement(policy_lex_file_t *lexer, policy_item_t **tail)
                        break;
 
                case POLICY_RESERVED_UNKNOWN: /* wasn't a reserved word */
-                       if (rlm_policy_find(lexer->policies, lhs)) {
+                       /*
+                        *      Is a named policy, parse the reference to it.
+                        */
+                       if (rlm_policy_find(lexer->policies, lhs) != NULL) {
                                if (parse_call(lexer, tail, lhs)) {
                                        return 1;
                                }
@@ -1248,7 +1250,7 @@ int rlm_policy_parse(rlm_policy_t *inst, const char *filename)
 
        if (rcode == 0) {
                fprintf(stderr, "Failed to parse %s\n", filename);
-               rlm_policy_free_item((policy_item_t *) &list);
+               rlm_policy_free_item((policy_item_t *) list);
                return 0;
        }
 
index 7579366..2f8b696 100644 (file)
@@ -50,16 +50,8 @@ static CONF_PARSER module_config[] = {
  */
 static int policyname_cmp(const void *a, const void *b)
 {
-       return strcasecmp(((const rlm_policy_name_t *)a)->name,
-                         ((const rlm_policy_name_t *)b)->name);
-}
-
-static void policyname_free(void *item)
-{
-       rlm_policy_name_t *this = (rlm_policy_name_t *) item;
-
-       rlm_policy_free_item(this->policy);
-       free(this);
+       return strcmp(((const policy_named_t *)a)->name,
+                     ((const policy_named_t *)b)->name);
 }
 
 
@@ -108,7 +100,7 @@ static int policy_instantiate(CONF_SECTION *conf, void **instance)
                return -1;
        }
 
-       inst->policies = rbtree_create(policyname_cmp, policyname_free, 0);
+       inst->policies = rbtree_create(policyname_cmp, rlm_policy_free_item, 0);
        if (!inst->policies) {
                policy_detach(inst);
                return -1;
@@ -131,19 +123,10 @@ static int policy_instantiate(CONF_SECTION *conf, void **instance)
 /*
  *     Insert a named policy into a list.
  */
-int rlm_policy_insert(rbtree_t *head, const char *name, policy_item_t *policy)
+int rlm_policy_insert(rbtree_t *head, policy_named_t *policy)
 {
-       rlm_policy_name_t *this;
-
-       /*
-        *      Get the names from the policy
-        */
-       this = rad_malloc(sizeof(*this));
-       strNcpy(this->name, name, sizeof(this->name));
-       this->policy = policy;
-
-       if (!rbtree_insert(head, this)) {
-               policyname_free(this);
+       if (!rbtree_insert(head, policy)) {
+               rlm_policy_free_item((policy_item_t *) policy);
                return 0;
        }
 
@@ -152,13 +135,14 @@ int rlm_policy_insert(rbtree_t *head, const char *name, policy_item_t *policy)
 
 
 /*
- *     Insert a named policy into a list.
+ *     Find a named policy
  */
-rlm_policy_name_t *rlm_policy_find(rbtree_t *head, const char *name)
+policy_named_t *rlm_policy_find(rbtree_t *head, const char *name)
 {
-       rlm_policy_name_t mypolicy;
+       policy_named_t mypolicy;
+
+       mypolicy.name = name;
 
-       strNcpy(mypolicy.name, name, sizeof(mypolicy.name));
        return rbtree_finddata(head, &mypolicy);
 }
 
@@ -211,10 +195,9 @@ static int policy_post_proxy(void *instance, REQUEST *request)
  */
 void rlm_policy_free_item(policy_item_t *item)
 {
-
        while (item) {
                policy_item_t *next = item->next;
-               
+
                switch (item->type) {
                default:
                case POLICY_TYPE_BAD:
@@ -279,6 +262,8 @@ void rlm_policy_free_item(policy_item_t *item)
                                policy_named_t *this;
                                
                                this = (policy_named_t *) item;
+                               rad_assert(this->name != NULL);
+                               free(this->name);
                                rlm_policy_free_item(this->policy);
                        }
                        break;
index f2db8c5..ca28da0 100644 (file)
@@ -154,7 +154,7 @@ typedef struct policy_attributes_t {
  */
 typedef struct policy_named_t {
        policy_item_t   item;
-       char            *name;
+       const char      *name;
        policy_item_t   *policy;
 } policy_named_t;
 
@@ -164,7 +164,7 @@ typedef struct policy_named_t {
  */
 typedef struct policy_call_t {
        policy_item_t   item;
-       char            *name;
+       const char      *name;
 } policy_call_t;
 
 
@@ -222,23 +222,13 @@ typedef struct rlm_policy_t {
 
 
 /*
- *     Policies can be named.
- */
-typedef struct rlm_policy_name_t {
-       char            name[32];
-       policy_item_t   *policy;
-} rlm_policy_name_t;
-
-
-/*
  *     Functions.
  */
 extern const LRAD_NAME_NUMBER rlm_policy_tokens[];
 extern const LRAD_NAME_NUMBER policy_reserved_words[];
 
-extern int rlm_policy_insert(rbtree_t *head, const char *name,
-                            policy_item_t *policy);
-extern rlm_policy_name_t *rlm_policy_find(rbtree_t *head, const char *name);
+extern int rlm_policy_insert(rbtree_t *head, policy_named_t *policy);
+extern policy_named_t *rlm_policy_find(rbtree_t *head, const char *name);
 
 extern int rlm_policy_parse(rlm_policy_t * inst, const char *filename);
 extern void rlm_policy_free_item(policy_item_t *item);