Enable building #WITHOUT_PROXY
[freeradius.git] / src / modules / rlm_policy / evaluate.c
index 672c8d9..3c1a000 100644 (file)
  *
  *   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
+ *   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 "rlm_policy.h"
+#include <freeradius-devel/ident.h>
+RCSID("$Id$")
 
-#include "modules.h"
+#include "rlm_policy.h"
 
 #ifdef HAVE_REGEX_H
 #include <regex.h>
 static void policy_print(const policy_item_t *item, int indent)
 {
        if (!item) {
-               if (indent) printf("%*s", indent, " ");
-               printf("[NULL]\n");
+               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) printf("%*s", indent, " ");
-                       printf("[BAD STATEMENT]");
+                       if (indent) fprintf(fr_log_fp, "%*s", indent, " ");
+                       fprintf(fr_log_fp, "[BAD STATEMENT]");
                        break;
-                       
+
                case POLICY_TYPE_PRINT:
-                       if (indent) printf("%*s", indent, " ");
+                       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) {
-                                       printf("print %s\n", this->rhs);
+                                       fprintf(fr_log_fp, "print %s\n", this->rhs);
                                } else {
-                                       printf("print \"%s\"\n", this->rhs);
+                                       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) printf("%*s", indent, " ");
+                               if (indent) fprintf(fr_log_fp, "%*s", indent, " ");
 
-                               printf("\t%s %s ", assign->lhs,
-                                      lrad_int2str(rlm_policy_tokens,
+                               fprintf(fr_log_fp, "\t%s %s ", assign->lhs,
+                                      fr_int2str(rlm_policy_tokens,
                                                    assign->assign, "?"));
                                if (assign->rhs_type == POLICY_LEX_BARE_WORD) {
-                                       printf("%s\n", assign->rhs);
+                                       fprintf(fr_log_fp, "%s\n", assign->rhs);
                                } else {
                                        /*
                                         *      FIXME: escape "
                                         */
-                                       printf("\"%s\"\n", assign->rhs);
+                                       fprintf(fr_log_fp, "\"%s\"\n", assign->rhs);
                                }
                        }
                        break;
@@ -90,60 +92,65 @@ static void policy_print(const policy_item_t *item, int indent)
 
                                condition = (const policy_condition_t *) item;
 
-                               printf("(");
+                               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);
-                                       printf(")");
+                                       fprintf(fr_log_fp, ")");
                                        break;
                                }
 
                                if (condition->compare == POLICY_LEX_L_NOT) {
-                                       printf("!");
+                                       fprintf(fr_log_fp, "!");
                                        policy_print(condition->child, indent);
-                                       printf(")");
+                                       fprintf(fr_log_fp, ")");
                                        break;
                                }
 
                                if (condition->compare == POLICY_LEX_CMP_TRUE) {
-                                       printf("%s)", condition->lhs);
+                                       fprintf(fr_log_fp, "%s)", condition->lhs);
                                        break;
                                }
 
-                               if (condition->lhs_type == POLICY_LEX_BARE_WORD) {
-                                       printf("%s", condition->lhs);
+                               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.
                                         */
-                                       printf("\"%s\"", condition->lhs);
+                                       fprintf(fr_log_fp, "\"%s\"", condition->lhs);
                                }
 
                                /*
                                 *      We always print this condition.
                                 */
-                               printf(" %s ", lrad_int2str(rlm_policy_tokens,
+                               fprintf(fr_log_fp, " %s ", fr_int2str(rlm_policy_tokens,
                                                            condition->compare,
                                                            "?"));
                                if (condition->rhs_type == POLICY_LEX_BARE_WORD) {
-                                       printf("%s", condition->rhs);
+                                       fprintf(fr_log_fp, "%s", condition->rhs);
                                } else {
                                        /*
                                         *      FIXME: escape ",
                                         *      and move all of this logic
                                         *      to a function.
                                         */
-                                       printf("\"%s\"", condition->rhs);
+                                       fprintf(fr_log_fp, "\"%s\"", condition->rhs);
                                }
-                               printf(")");
-                               
-                               if (condition->child_condition != POLICY_LEX_BAD) {
-                                       printf(" %s ", lrad_int2str(rlm_policy_tokens, condition->child_condition, "?"));
+                               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);
                                }
                        }
@@ -155,24 +162,24 @@ static void policy_print(const policy_item_t *item, int indent)
 
                                statement = (const policy_if_t *) item;
 
-                               if (indent) printf("%*s", indent, " ");
-                               printf("if ");
+                               if (indent) fprintf(fr_log_fp, "%*s", indent, " ");
+                               fprintf(fr_log_fp, "if ");
                                policy_print(statement->condition, indent);
-                               printf(" {\n");
+                               fprintf(fr_log_fp, " {\n");
                                policy_print(statement->if_true, indent + 1);
-                               if (indent) printf("%*s", indent, " ");
+                               if (indent) fprintf(fr_log_fp, "%*s", indent, " ");
                                if (statement->if_false) {
-                                       printf("} else ");
+                                       fprintf(fr_log_fp, "} else ");
                                        if (statement->if_false->type == POLICY_TYPE_ASSIGNMENT) {
-                                               printf(" { ");
+                                               fprintf(fr_log_fp, " { ");
                                                policy_print(statement->if_false, indent + 1);
-                                               if (indent) printf("%*s", indent, " ");
-                                               printf(" }");
+                                               if (indent) fprintf(fr_log_fp, "%*s", indent, " ");
+                                               fprintf(fr_log_fp, " }");
                                        } else {
                                                policy_print(statement->if_false, indent + 1);
                                        }
                                } else {
-                                       printf("}\n");
+                                       fprintf(fr_log_fp, "}\n");
                                }
                        }
                        break;
@@ -183,15 +190,15 @@ static void policy_print(const policy_item_t *item, int indent)
 
                                this = (const policy_attributes_t *) item;
 
-                               if (indent) printf("%*s", indent, " ");
-                               printf("%s %s {\n",
-                                      lrad_int2str(policy_reserved_words,
+                               if (indent) fprintf(fr_log_fp, "%*s", indent, " ");
+                               fprintf(fr_log_fp, "%s %s {\n",
+                                      fr_int2str(policy_reserved_words,
                                                    this->where, "?"),
-                                      lrad_int2str(rlm_policy_tokens,
+                                      fr_int2str(rlm_policy_tokens,
                                                    this->how, "?"));
                                policy_print(this->attributes, indent + 1);
-                               if (indent) printf("%*s", indent, " ");
-                               printf("}\n");
+                               if (indent) fprintf(fr_log_fp, "%*s", indent, " ");
+                               fprintf(fr_log_fp, "}\n");
                        }
                        break;
 
@@ -200,11 +207,11 @@ static void policy_print(const policy_item_t *item, int indent)
                                const policy_named_t *this;
 
                                this = (const policy_named_t *) item;
-                               if (indent) printf("%*s", indent, " ");
-                               printf("policy %s {\n", this->name);
+                               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) printf("%*s", indent, " ");
-                               printf("}\n");
+                               if (indent) fprintf(fr_log_fp, "%*s", indent, " ");
+                               fprintf(fr_log_fp, "}\n");
                        }
                        break;
 
@@ -213,16 +220,40 @@ static void policy_print(const policy_item_t *item, int indent)
                                const policy_call_t *this;
 
                                this = (const policy_call_t *) item;
-                               if (indent) printf("%*s", indent, " ");
-                               printf("call %s\n", this->name);
+                               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) printf("%*s", indent, " ");
-                       printf("[HUH?]\n");
+                       if (indent) fprintf(fr_log_fp, "%*s", indent, " ");
+                       fprintf(fr_log_fp, "[HUH?]\n");
                        break;
-                       
+
                }
 
                item = item->next;
@@ -232,34 +263,31 @@ static void policy_print(const policy_item_t *item, int indent)
 
 void rlm_policy_print(const policy_item_t *item)
 {
-       printf("----------------------------------------------------------\n");
+       if (!fr_log_fp) return;
+
+       fprintf(fr_log_fp, "# rlm_policy \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.
+ *     Internal stack of things to do.  This lets us have function
+ *     calls...
  *
- *     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.
+ *     Yes, we should learn lex, yacc, etc.
  */
 #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 */
+       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.
  */
@@ -325,6 +353,7 @@ 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;
@@ -333,6 +362,14 @@ static int policy_stack_pop(policy_state_t *state, const policy_item_t **pitem)
        *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) {
@@ -354,18 +391,25 @@ 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) {
-               printf("%s\n", this->rhs);
+               fprintf(fr_log_fp, "%s\n", this->rhs);
        } else {
                char buffer[1024];
 
                radius_xlat(buffer, sizeof(buffer), this->rhs,
                            state->request, NULL);
-               printf("%s", buffer);
+               fprintf(fr_log_fp, "%s", buffer);
+               if (!strchr(buffer, '\n')) fprintf(fr_log_fp, "\n");
        }
 
+       /*
+        *      Doesn't change state->rcode
+        */
+
        return 1;
 }
 
@@ -394,6 +438,7 @@ static VALUE_PAIR *find_vp(REQUEST *request, const char *name)
        } 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) {
@@ -404,6 +449,7 @@ static VALUE_PAIR *find_vp(REQUEST *request, const char *name)
                if (request->proxy_reply) {
                        vps = request->proxy_reply->vps;
                }
+#endif
        } else if (strncasecmp(name, "control:", 8) == 0) {
                p += 8;
                vps = request->config_items;
@@ -419,17 +465,21 @@ static VALUE_PAIR *find_vp(REQUEST *request, const char *name)
                return NULL;    /* no such attribute */
        }
 
-       return pairfind(vps, dattr->attr);
+       return pairfind(vps, dattr->attr, dattr->vendor);
 }
 
 
 /*
  *     Evaluate an assignment
+ *
+ *     Not really used much...
  */
-static int evaluate_assignment(policy_state_t *state, const policy_item_t *item)
+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;
 
@@ -455,8 +505,8 @@ 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;
+       VALUE_PAIR *vp = NULL;
+       const char *data = NULL;
        int compare;
 #ifdef HAVE_REGEX_H
        regex_t reg;
@@ -470,14 +520,25 @@ static int evaluate_condition(policy_state_t *state, const policy_item_t *item)
        /*
         *      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;
+       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);
@@ -488,6 +549,15 @@ static int evaluate_condition(policy_state_t *state, const policy_item_t *item)
                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);
@@ -516,24 +586,26 @@ static int evaluate_condition(policy_state_t *state, const policy_item_t *item)
                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.
                         */
-                       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);
-                       }
+                       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;
-                       if (!myvp) {
-                               return FALSE;
-                       }
 
                        /*
                         *      FIXME: What to do about comparisons
@@ -547,56 +619,57 @@ static int evaluate_condition(policy_state_t *state, const policy_item_t *item)
                         *      error codes than "comparison is less
                         *      than, equal to, or greater than zero".
                         */
-                       compare = simplepaircmp(state->request,
-                                               vp, myvp);
+                       compare = radius_callback_compare(state->request,
+                                                         vp, myvp, NULL, NULL);
                        pairfree(&myvp);
-                       
+
                } else {
                        /*
                         *      FIXME: Do something for RHS type?
                         */
-                       printf("CMP %s %s\n", lhs_buffer, this->rhs);
+                       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);
@@ -605,14 +678,14 @@ static int evaluate_condition(policy_state_t *state, const policy_item_t *item)
                                        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.
@@ -625,14 +698,14 @@ static int evaluate_condition(policy_state_t *state, const policy_item_t *item)
                                                free(p);
                                                continue;
                                        }
-                                               
+
                                        /*
                                         *      No previous match
                                         *      to delete, stop.
                                         */
                                        break;
                                }
-                               
+
                                /*
                                 *      Copy substring into buffer.
                                 */
@@ -640,7 +713,7 @@ static int evaluate_condition(policy_state_t *state, const policy_item_t *item)
                                       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.
@@ -655,10 +728,10 @@ static int evaluate_condition(policy_state_t *state, const policy_item_t *item)
                                                 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);
@@ -675,6 +748,8 @@ static int evaluate_condition(policy_state_t *state, const policy_item_t *item)
                break;          /* default from first switch over compare */
        }
 
+       if (this->sense) rcode = (rcode == FALSE); /* reverse sense of test */
+
        /*
         *      No trailing &&, ||
         */
@@ -742,7 +817,7 @@ static VALUE_PAIR *assign2vp(REQUEST *request,
                             const policy_assignment_t *assign)
 {
        VALUE_PAIR *vp;
-       LRAD_TOKEN operator = T_OP_EQ;
+       FR_TOKEN operator = T_OP_EQ;
        const char *value = assign->rhs;
        char buffer[2048];
 
@@ -764,22 +839,22 @@ static VALUE_PAIR *assign2vp(REQUEST *request,
        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,
+                       fr_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);
+               fprintf(stderr, "Failed creating pair: %s %s\n", value, fr_strerror());
        }
 
        return vp;
@@ -795,6 +870,7 @@ static int evaluate_attr_list(policy_state_t *state, const policy_item_t *item)
        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;
 
@@ -811,6 +887,7 @@ static int evaluate_attr_list(policy_state_t *state, const policy_item_t *item)
                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);
@@ -820,6 +897,7 @@ static int evaluate_attr_list(policy_state_t *state, const policy_item_t *item)
                if (!state->request->proxy_reply) return 0; /* FIXME: print error */
                vps = &(state->request->proxy_reply->vps);
                break;
+#endif
 
        default:
                return 0;
@@ -845,33 +923,135 @@ static int evaluate_attr_list(policy_state_t *state, const policy_item_t *item)
                tail = &(vp->next);
        }
 
-       switch (this->how) {
+       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 an 'call foo' statement
+ *     Evaluate a reference call to a module.
  */
 static int evaluate_call(policy_state_t *state, const policy_item_t *item)
 {
@@ -883,13 +1063,24 @@ static int evaluate_call(policy_state_t *state, const policy_item_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.
         */
@@ -898,15 +1089,47 @@ static int evaluate_call(policy_state_t *state, const policy_item_t *item)
                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;
+
        /*
-        *      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...
+        *      Just to be paranoid.  Maybe we want to loosen this
+        *      restriction in the future?
         */
-       return 1;
+       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 */
 }
 
 
@@ -927,7 +1150,9 @@ static policy_evaluate_type_t evaluate_functions[POLICY_TYPE_NUM_TYPES] = {
        evaluate_attr_list,
        evaluate_print,
        NULL,                   /* define a named policy.. */
-       evaluate_call
+       evaluate_call,
+       evaluate_return,
+       evaluate_module
 };
 
 
@@ -939,16 +1164,16 @@ 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;
@@ -962,7 +1187,7 @@ static int policy_evaluate_name(policy_state_t *state, const char *name)
                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,
@@ -972,7 +1197,7 @@ static int policy_evaluate_name(policy_state_t *state, const char *name)
                }
        } /* loop until the stack is empty */
 
-       return RLM_MODULE_OK;
+       return state->rcode;
 }
 
 
@@ -989,6 +1214,9 @@ int rlm_policy_evaluate(rlm_policy_t *inst, REQUEST *request, const char *name)
        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);