Fix typos in previous commit
[freeradius.git] / src / main / evaluate.c
index 18694b1..10aeac4 100644 (file)
@@ -47,6 +47,7 @@ RCSID("$Id$")
 #endif
 #endif
 
+#ifdef WITH_UNLANG
 
 static int all_digits(const char *string)
 {
@@ -59,14 +60,6 @@ static int all_digits(const char *string)
        return (*p == '\0');
 }
 
-#ifndef NDEBUG
-#ifndef DEBUG4
-#define DEBUG4  if (debug_flag > 4)log_debug
-#endif
-#else
-#define DEBUG4 if (0) log_debug
-#endif
-
 static const char *filler = "????????????????????????????????????????????????????????????????";
 
 static const char *expand_string(char *buffer, size_t sizeof_buffer,
@@ -169,7 +162,7 @@ static FR_TOKEN getregex(const char **ptr, char *buffer, size_t buflen,
 
                        default:
                                if ((p[1] >= '0') && (p[1] <= '9') &&
-                                   (sscanf(p, "%3o", &x) == 1)) {
+                                   (sscanf(p + 1, "%3o", &x) == 1)) {
                                        *q++ = x;
                                        p += 2;
                                } else {
@@ -206,8 +199,7 @@ static const FR_NAME_NUMBER modreturn_table[] = {
 };
 
 
-static int radius_get_vp(REQUEST *request, const char *name, VALUE_PAIR **vp_p)
-                       
+int radius_get_vp(REQUEST *request, const char *name, VALUE_PAIR **vp_p)
 {
        const char *vp_name = name;
        REQUEST *myrequest = request;
@@ -233,6 +225,7 @@ static int radius_get_vp(REQUEST *request, const char *name, VALUE_PAIR **vp_p)
                vp_name += 6;
                vps = myrequest->reply->vps;
 
+#ifdef WITH_PROXY
        } else if (strncmp(vp_name, "proxy-request:", 14) == 0) {
                vp_name += 14;
                if (request->proxy) vps = myrequest->proxy->vps;
@@ -240,6 +233,7 @@ static int radius_get_vp(REQUEST *request, const char *name, VALUE_PAIR **vp_p)
        } else if (strncmp(vp_name, "proxy-reply:", 12) == 0) {
                vp_name += 12;
                if (request->proxy_reply) vps = myrequest->proxy_reply->vps;
+#endif
 
        } else if (strncmp(vp_name, "config:", 7) == 0) {
                vp_name += 7;
@@ -249,6 +243,42 @@ static int radius_get_vp(REQUEST *request, const char *name, VALUE_PAIR **vp_p)
                vp_name += 8;
                vps = myrequest->config_items;
 
+#ifdef WITH_COA
+       } else if (strncmp(vp_name, "coa:", 4) == 0) {
+               vp_name += 4;
+
+               if (myrequest->coa &&
+                   (myrequest->coa->proxy->code == PW_COA_REQUEST)) {
+                       vps = myrequest->coa->proxy->vps;
+               }
+
+       } else if (strncmp(vp_name, "coa-reply:", 10) == 0) {
+               vp_name += 10;
+
+               if (myrequest->coa && /* match reply with request */
+                   (myrequest->coa->proxy->code == PW_COA_REQUEST) &&
+                   (myrequest->coa->proxy_reply)) {
+                       vps = myrequest->coa->proxy_reply->vps;
+               }
+
+       } else if (strncmp(vp_name, "disconnect:", 11) == 0) {
+               vp_name += 11;
+
+               if (myrequest->coa &&
+                   (myrequest->coa->proxy->code == PW_DISCONNECT_REQUEST)) {
+                       vps = myrequest->coa->proxy->vps;
+               }
+
+       } else if (strncmp(vp_name, "disconnect-reply:", 17) == 0) {
+               vp_name += 17;
+
+               if (myrequest->coa && /* match reply with request */
+                   (myrequest->coa->proxy->code == PW_DISCONNECT_REQUEST) &&
+                   (myrequest->coa->proxy_reply)) {
+                       vps = myrequest->coa->proxy_reply->vps;
+               }
+#endif
+
        } else {
                vps = myrequest->packet->vps;
        }
@@ -259,7 +289,7 @@ static int radius_get_vp(REQUEST *request, const char *name, VALUE_PAIR **vp_p)
        /*
         *      May not may not be found, but it *is* a known name.
         */
-       *vp_p = pairfind(vps, da->attr);
+       *vp_p = pairfind(vps, da->attr, da->vendor);
        return TRUE;
 }
 
@@ -273,7 +303,7 @@ static int radius_do_cmp(REQUEST *request, int *presult,
                         int cflags, int modreturn)
 {
        int result;
-       int lint, rint;
+       uint32_t lint, rint;
        VALUE_PAIR *vp = NULL;
 #ifdef HAVE_REGEX_H
        char buffer[1024];
@@ -325,17 +355,19 @@ static int radius_do_cmp(REQUEST *request, int *presult,
                                 *      If so, try looking for it.
                                 */
                                da = dict_attrbyname(pleft);
-                               if (da && radius_find_compare(da->attr)) {
+                               if (da && (da->vendor == 0) && radius_find_compare(da->attr)) {
                                        VALUE_PAIR *check = pairmake(pleft, pright, token);
                                        *presult = (radius_callback_compare(request, NULL, check, NULL, NULL) == 0);
+                                       RDEBUG3("  Callback returns %d",
+                                               *presult);
                                        pairfree(&check);
-                                       if (*presult)  return TRUE;
-                                       return FALSE;
+                                       return TRUE;
                                }
                                
-                               DEBUG2("    (Attribute %s was not found)",
+                               RDEBUG2("    (Attribute %s was not found)",
                                       pleft);
-                               return FALSE;
+                               *presult = 0;
+                               return TRUE;
                        }
 
 #ifdef HAVE_REGEX_H
@@ -353,13 +385,14 @@ static int radius_do_cmp(REQUEST *request, int *presult,
 
                        memcpy(&myvp, vp, sizeof(myvp));
                        if (!pairparsevalue(&myvp, pright)) {
-                               DEBUG2("Failed parsing \"%s\": %s",
-                                      pright, librad_errstr);
+                               RDEBUG2("Failed parsing \"%s\": %s",
+                                      pright, fr_strerror());
                                return FALSE;
                        }
 
                        myvp.operator = token;
                        *presult = paircmp(&myvp, vp);
+                       RDEBUG3("  paircmp -> %d", *presult);
                        return TRUE;
                } /* else it's not a VP in a list */
        }
@@ -373,15 +406,15 @@ static int radius_do_cmp(REQUEST *request, int *presult,
        case T_OP_LE:
        case T_OP_LT:
                if (!all_digits(pright)) {
-                       DEBUG2("    (Right field is not a number at: %s)", pright);
+                       RDEBUG2("    (Right field is not a number at: %s)", pright);
                        return FALSE;
                }
-               rint = atoi(pright);
+               rint = strtoul(pright, NULL, 0);
                if (!all_digits(pleft)) {
-                       DEBUG2("    (Left field is not a number at: %s)", pleft);
+                       RDEBUG2("    (Left field is not a number at: %s)", pleft);
                        return FALSE;
                }
-               lint = atoi(pleft);
+               lint = strtoul(pleft, NULL, 0);
                break;
                
        default:
@@ -395,7 +428,7 @@ static int radius_do_cmp(REQUEST *request, int *presult,
                 *      Check for truth or falsehood.
                 */
                if (all_digits(pleft)) {
-                       lint = atoi(pleft);
+                       lint = strtoul(pleft, NULL, 0);
                        result = (lint != 0);
                        
                } else {
@@ -437,7 +470,17 @@ static int radius_do_cmp(REQUEST *request, int *presult,
                /*
                 *      Include substring matches.
                 */
-               regcomp(&reg, pright, cflags);
+               compare = regcomp(&reg, pright, cflags);
+               if (compare != 0) {
+                       if (debug_flag) {
+                               char errbuf[128];
+
+                               regerror(compare, &reg, errbuf, sizeof(errbuf));
+                               DEBUG("ERROR: Failed compiling regular expression: %s", errbuf);
+                       }
+                       return FALSE;
+               }
+
                compare = regexec(&reg, pleft,
                                  REQUEST_MAX_REGEX + 1,
                                  rxmatch, 0);
@@ -451,6 +494,7 @@ static int radius_do_cmp(REQUEST *request, int *presult,
 
                        free(request_data_get(request, request,
                                              REQUEST_DATA_REGEX | i));
+
                        /*
                         *      No %{i}, skip it.
                         *      We MAY have %{2} without %{1}.
@@ -458,21 +502,13 @@ static int radius_do_cmp(REQUEST *request, int *presult,
                        if (rxmatch[i].rm_so == -1) continue;
                        
                        /*
-                        *      Copy substring into buffer.
+                        *      Copy substring into allocated buffer
                         */
-                       memcpy(buffer, pleft + rxmatch[i].rm_so,
+                       r = rad_malloc(rxmatch[i].rm_eo -rxmatch[i].rm_so + 1);
+                       memcpy(r, pleft + rxmatch[i].rm_so,
                               rxmatch[i].rm_eo - rxmatch[i].rm_so);
-                       buffer[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...
-                        */
-                       r = strdup(buffer);
+                       r[rxmatch[i].rm_eo - rxmatch[i].rm_so] = '\0';
+
                        request_data_add(request, request,
                                         REQUEST_DATA_REGEX | i,
                                         r, free);
@@ -489,7 +525,17 @@ static int radius_do_cmp(REQUEST *request, int *presult,
                /*
                 *      Include substring matches.
                 */
-               regcomp(&reg, pright, cflags);
+               compare = regcomp(&reg, pright, cflags);
+               if (compare != 0) {
+                       if (debug_flag) {
+                               char errbuf[128];
+
+                               regerror(compare, &reg, errbuf, sizeof(errbuf));
+                               DEBUG("ERROR: Failed compiling regular expression: %s", errbuf);
+                       }
+                       return FALSE;
+               }
+
                compare = regexec(&reg, pleft,
                                  REQUEST_MAX_REGEX + 1,
                                  rxmatch, 0);
@@ -501,7 +547,7 @@ static int radius_do_cmp(REQUEST *request, int *presult,
 #endif
                
        default:
-               DEBUG4(">>> NOT IMPLEMENTED %d", token);
+               RDEBUG4(">>> NOT IMPLEMENTED %d", token);
                result = FALSE;
                break;
        }
@@ -510,6 +556,7 @@ static int radius_do_cmp(REQUEST *request, int *presult,
        return TRUE;
 }
 
+
 int radius_evaluate_condition(REQUEST *request, int modreturn, int depth,
                              const char **ptr, int evaluate_it, int *presult)
 {
@@ -517,7 +564,7 @@ int radius_evaluate_condition(REQUEST *request, int modreturn, int depth,
        int result = TRUE;
        int invert = FALSE;
        int evaluate_next_condition = evaluate_it;
-       const char *p = *ptr;
+       const char *p;
        const char *q, *start;
        FR_TOKEN token, lt, rt;
        char left[1024], right[1024], comp[4];
@@ -530,26 +577,41 @@ int radius_evaluate_condition(REQUEST *request, int modreturn, int depth,
                return FALSE;
        }
 
+       /*
+        *      Horrible parser.
+        */
+       p =  *ptr;
        while (*p) {
                while ((*p == ' ') || (*p == '\t')) p++;
 
-               if (*p == '!') {
-                       DEBUG4(">>> INVERT");
-                       invert = TRUE;
+               /*
+                *      ! EXPR
+                */
+               if (!found_condition && (*p == '!')) {
+                       /*
+                        *      Don't change the results if we're not
+                        *      evaluating the condition.
+                        */
+                       if (evaluate_next_condition) {
+                               RDEBUG4(">>> INVERT");
+                               invert = TRUE;
+                       }
                        p++;
+
+                       while ((*p == ' ') || (*p == '\t')) p++;
                }
 
                /*
-                *      It's a subcondition.
+                *      ( EXPR ) 
                 */
-               if (*p == '(') {
+               if (!found_condition && (*p == '(')) {
                        const char *end = p + 1;
 
                        /*
                         *      Evaluate the condition, bailing out on
                         *      parse error.
                         */
-                       DEBUG4(">>> CALLING EVALUATE %s", end);
+                       RDEBUG4(">>> RECURSING WITH ... %s", end);
                        if (!radius_evaluate_condition(request, modreturn,
                                                       depth + 1, &end,
                                                       evaluate_next_condition,
@@ -557,14 +619,14 @@ int radius_evaluate_condition(REQUEST *request, int modreturn, int depth,
                                return FALSE;
                        }
 
-                       if (invert && evaluate_next_condition) {
-                               DEBUG2("%.*s Converting !%s -> %s",
-                                      depth, filler,
-                                      (result != FALSE) ? "TRUE" : "FALSE",
-                                      (result == FALSE) ? "TRUE" : "FALSE");
-
-                                      
-                               result = (result == FALSE);
+                       if (invert) {
+                               if (evaluate_next_condition) {
+                                       RDEBUG2("%.*s Converting !%s -> %s",
+                                               depth, filler,
+                                               (result != FALSE) ? "TRUE" : "FALSE",
+                                               (result == FALSE) ? "TRUE" : "FALSE");
+                                       result = (result == FALSE);
+                               }
                                invert = FALSE;
                        }
 
@@ -573,76 +635,71 @@ int radius_evaluate_condition(REQUEST *request, int modreturn, int depth,
                         *      condition
                         */
                        p = end;
-                       DEBUG4(">>> EVALUATE RETURNED ::%s::", end);
-                       
-                       if (!((*p == ')') || (*p == '!') ||
-                             ((p[0] == '&') && (p[1] == '&')) ||
-                             ((p[0] == '|') && (p[1] == '|')))) {
+                       RDEBUG4(">>> AFTER RECURSION ... %s", end);
+
+                       while ((*p == ' ') || (*p == '\t')) p++;
 
-                               radlog(L_ERR, "Parse error in condition at: %s", p);
+                       if (!*p) {
+                               radlog(L_ERR, "No closing brace");
                                return FALSE;
                        }
-                       if (*p == ')') p++;     /* skip it */
+
+                       if (*p == ')') p++; /* eat closing brace */
                        found_condition = TRUE;
-                       
+
                        while ((*p == ' ') || (*p == '\t')) p++;
+               }
 
+               /*
+                *      At EOL or closing brace, update && return.
+                */
+               if (found_condition && (!*p || (*p == ')'))) break;
+
+               /*
+                *      Now it's either:
+                *
+                *      WORD
+                *      WORD1 op WORD2
+                *      && EXPR
+                *      || EXPR
+                */
+               if (found_condition) {
                        /*
-                        *      EOL.  It's OK.
+                        *      (A && B) means "evaluate B
+                        *      only if A was true"
                         */
-                       if (!*p) {
-                               DEBUG4(">>> AT EOL");
-                               *ptr = p;
-                               *presult = result;
-                               return TRUE;
-                               
-                               /*
-                                *      (A && B) means "evaluate B
-                                *      only if A was true"
-                                */
-                       } else if ((p[0] == '&') && (p[1] == '&')) {
-                               if (result == TRUE) {
-                                       evaluate_next_condition = evaluate_it;
-                               } else {
-                                       evaluate_next_condition = FALSE;
-                               }
-                               p += 2;
-                               
-                               /*
-                                *      (A || B) means "evaluate B
-                                *      only if A was false"
-                                */
-                       } else if ((p[0] == '|') && (p[1] == '|')) {
-                               if (result == FALSE) {
-                                       evaluate_next_condition = evaluate_it;
-                               } else {
-                                       evaluate_next_condition = FALSE;
-                               }
+                       if ((p[0] == '&') && (p[1] == '&')) {
+                               if (!result) evaluate_next_condition = FALSE;
                                p += 2;
+                               found_condition = FALSE;
+                               continue; /* go back to the start */
+                       }
 
-                       } else if (*p == ')') {
-                               DEBUG4(">>> CLOSING BRACE");
-                               *ptr = p;
-                               *presult = result;
-                               return TRUE;
-
-                       } else {
-                               /*
-                                *      Parse error
-                                */
-                               radlog(L_ERR, "Unexpected trailing text at: %s", p);
-                               return FALSE;
+                       /*
+                        *      (A || B) means "evaluate B
+                        *      only if A was false"
+                        */
+                       if ((p[0] == '|') && (p[1] == '|')) {
+                               if (result) evaluate_next_condition = FALSE;
+                               p += 2;
+                               found_condition = FALSE;
+                               continue;
                        }
-               } /* else it wasn't an opening brace */
 
-               while ((*p == ' ') || (*p == '\t')) p++;
+                       /*
+                        *      It must be:
+                        *
+                        *      WORD
+                        *      WORD1 op WORD2
+                        */
+               }
 
-               /*
-                *      More conditions, keep going.
-                */
-               if ((*p == '(') || (p[0] == '!')) continue;
+               if (found_condition) {
+                       radlog(L_ERR, "Consecutive conditions at %s", p);
+                       return FALSE;
+               }
 
-               DEBUG4(">>> LOOKING AT %s", p);
+               RDEBUG4(">>> LOOKING AT %s", p);
                start = p;
 
                /*
@@ -654,7 +711,7 @@ int radius_evaluate_condition(REQUEST *request, int modreturn, int depth,
                }
 
                /*
-                *      Look for word == value
+                *      Look for WORD1 op WORD2
                 */
                lt = gettoken(&p, left, sizeof(left));
                if ((lt != T_BARE_WORD) &&
@@ -677,23 +734,34 @@ int radius_evaluate_condition(REQUEST *request, int modreturn, int depth,
                }
 
                /*
-                *      Peek ahead.  Maybe it's just a check for
-                *      existence.  If so, there shouldn't be anything
-                *      else.
+                *      Peek ahead, to see if it's:
+                *
+                *      WORD
+                *
+                *      or something else, such as
+                *
+                *      WORD1 op WORD2
+                *      WORD )
+                *      WORD && EXPR
+                *      WORD || EXPR
                 */
                q = p;
                while ((*q == ' ') || (*q == '\t')) q++;
 
                /*
-                *      End of condition, 
+                *      If the next thing is:
+                *
+                *      EOL
+                *      end of condition
+                *      &&
+                *      ||
+                *
+                *      Then WORD is just a test for existence.
+                *      Remember that and skip ahead.
                 */
                if (!*q || (*q == ')') ||
-                   ((*q == '!') && (q[1] != '=') && (q[1] != '~')) ||
                    ((q[0] == '&') && (q[1] == '&')) ||
                    ((q[0] == '|') && (q[1] == '|'))) {
-                       /*
-                        *      Simplify the code.
-                        */
                        token = T_OP_CMP_TRUE;
                        rt = T_OP_INVALID;
                        pright = NULL;
@@ -701,12 +769,13 @@ int radius_evaluate_condition(REQUEST *request, int modreturn, int depth,
                }
 
                /*
-                *      Else it's a full "foo == bar" thingy.
+                *      Otherwise, it's:
+                *
+                *      WORD1 op WORD2
                 */
                token = gettoken(&p, comp, sizeof(comp));
                if ((token < T_OP_NE) || (token > T_OP_CMP_EQ) ||
-                   (token == T_OP_CMP_TRUE) ||
-                   (token == T_OP_CMP_FALSE)) {
+                   (token == T_OP_CMP_TRUE)) {
                        radlog(L_ERR, "Expected comparison at: %s", comp);
                        return FALSE;
                }
@@ -753,7 +822,7 @@ int radius_evaluate_condition(REQUEST *request, int modreturn, int depth,
                        }
                }
                
-               DEBUG4(">>> %d:%s %d %d:%s",
+               RDEBUG4(">>> %d:%s %d %d:%s",
                       lt, pleft, token, rt, pright);
                
        do_cmp:
@@ -765,58 +834,45 @@ int radius_evaluate_condition(REQUEST *request, int modreturn, int depth,
                                           rt, pright, cflags, modreturn)) {
                                return FALSE;
                        }
+                       RDEBUG4(">>> Comparison returned %d", result);
+
+                       if (invert) {
+                               RDEBUG4(">>> INVERTING result");
+                               result = (result == FALSE);
+                       }
 
-                       DEBUG2("%.*s Evaluating %s(%.*s) -> %s",
+                       RDEBUG2("%.*s Evaluating %s(%.*s) -> %s",
                               depth, filler,
                               invert ? "!" : "", p - start, start,
                               (result != FALSE) ? "TRUE" : "FALSE");
 
-                       DEBUG4(">>> GOT result %d", result);
+                       invert = FALSE;
+                       RDEBUG4(">>> GOT result %d", result);
 
                        /*
                         *      Not evaluating it.  We may be just
                         *      parsing it.
                         */
                } else if (request) {
-                       DEBUG2("%.*s Skipping %s(%.*s)",
+                       RDEBUG2("%.*s Skipping %s(%.*s)",
                               depth, filler,
                               invert ? "!" : "", p - start, start);
                }
 
-               if (invert) {
-                       DEBUG4(">>> INVERTING result");
-                       result = (result == FALSE);
-                       invert = FALSE;
-               }
-
-               /*
-                *      Don't evaluate it.
-                */
-               DEBUG4(">>> EVALUATE %d ::%s::",
-                       evaluate_next_condition, p);
-
-               while ((*p == ' ') || (*p == '\t')) p++;
-
-               /*
-                *      Closing brace or EOL, return.
-                */
-               if (!*p || (*p == ')') ||
-                   ((*p == '!') && (p[1] != '=') && (p[1] != '~')) ||
-                   ((p[0] == '&') && (p[1] == '&')) ||
-                   ((p[0] == '|') && (p[1] == '|'))) {
-                       DEBUG4(">>> AT EOL2a");
-                       *ptr = p;
-                       if (evaluate_next_condition) *presult = result;
-                       return TRUE;
-               }
+               found_condition = TRUE;
        } /* loop over the input condition */
 
-       DEBUG4(">>> AT EOL2b");
+       if (!found_condition) {
+               radlog(L_ERR, "Syntax error.  Expected condition at %s", p);
+               return FALSE;
+       }
+
+       RDEBUG4(">>> AT EOL -> %d", result);
        *ptr = p;
-       if (evaluate_next_condition) *presult = result;
+       if (evaluate_it) *presult = result;
        return TRUE;
 }
-
+#endif
 
 static void fix_up(REQUEST *request)
 {
@@ -826,6 +882,8 @@ static void fix_up(REQUEST *request)
        request->password = NULL;
        
        for (vp = request->packet->vps; vp != NULL; vp = vp->next) {
+               if (vp->vendor != 0) continue;
+
                if ((vp->attribute == PW_USER_NAME) &&
                    !request->username) {
                        request->username = vp;
@@ -836,6 +894,8 @@ static void fix_up(REQUEST *request)
                } else if (vp->attribute == PW_USER_PASSWORD) {
                        request->password = vp;
                }
+
+               if (request->username && request->password) break;
        }
 }
 
@@ -904,7 +964,7 @@ void radius_pairmove(REQUEST *request, VALUE_PAIR **to, VALUE_PAIR *from)
        edited = rad_malloc(sizeof(*edited) * to_count);
        memset(edited, 0, sizeof(*edited) * to_count);
 
-       DEBUG4("::: FROM %d TO %d MAX %d", from_count, to_count, count);
+       RDEBUG4("::: FROM %d TO %d MAX %d", from_count, to_count, count);
 
        /*
         *      Now that we have the lists initialized, start working
@@ -913,7 +973,7 @@ void radius_pairmove(REQUEST *request, VALUE_PAIR **to, VALUE_PAIR *from)
        for (i = 0; i < from_count; i++) {
                int found;
 
-               DEBUG4("::: Examining %s", from_list[i]->name);
+               RDEBUG4("::: Examining %s", from_list[i]->name);
 
                /*
                 *      Attribute should be appended, OR the "to" list
@@ -924,12 +984,13 @@ void radius_pairmove(REQUEST *request, VALUE_PAIR **to, VALUE_PAIR *from)
 
                found = FALSE;
                for (j = 0; j < to_count; j++) {
-                       if (edited[j]) continue;
+                       if (edited[j] || !to_list[j] || !from_list[i]) continue;
 
                        /*
                         *      Attributes aren't the same, skip them.
                         */
-                       if (from_list[i]->attribute != to_list[j]->attribute) {
+                       if ((from_list[i]->attribute != to_list[j]->attribute) ||
+                           (from_list[i]->vendor != to_list[j]->vendor)) {
                                continue;
                        }
 
@@ -946,7 +1007,7 @@ void radius_pairmove(REQUEST *request, VALUE_PAIR **to, VALUE_PAIR *from)
                         *      the one in the "from" list.
                         */
                        if (from_list[i]->operator == T_OP_SET) {
-                               DEBUG4("::: OVERWRITING %s FROM %d TO %d",
+                               RDEBUG4("::: OVERWRITING %s FROM %d TO %d",
                                       to_list[j]->name, i, j);
                                pairfree(&to_list[j]);
                                to_list[j] = from_list[i];
@@ -966,6 +1027,14 @@ void radius_pairmove(REQUEST *request, VALUE_PAIR **to, VALUE_PAIR *from)
                        }
 
                        /*
+                        *      Delete every attribute, independent
+                        *      of its value.
+                        */
+                       if (from_list[i]->operator == T_OP_CMP_FALSE) {
+                               goto delete;
+                       }
+
+                       /*
                         *      Delete all matching attributes from
                         *      "to"
                         */
@@ -1003,7 +1072,7 @@ void radius_pairmove(REQUEST *request, VALUE_PAIR **to, VALUE_PAIR *from)
                                case T_OP_SUB:
                                        if (rcode == 0) {
                                        delete:
-                                               DEBUG4("::: DELETING %s FROM %d TO %d",
+                                               RDEBUG4("::: DELETING %s FROM %d TO %d",
                                                       from_list[i]->name, i, j);
                                                pairfree(&to_list[j]);
                                                to_list[j] = NULL;
@@ -1016,7 +1085,7 @@ void radius_pairmove(REQUEST *request, VALUE_PAIR **to, VALUE_PAIR *from)
                                         */
                                case T_OP_LE:
                                        if (rcode > 0) {
-                                               DEBUG4("::: REPLACING %s FROM %d TO %d",
+                                               RDEBUG4("::: REPLACING %s FROM %d TO %d",
                                                       from_list[i]->name, i, j);
                                                pairfree(&to_list[j]);
                                                to_list[j] = from_list[i];
@@ -1027,7 +1096,7 @@ void radius_pairmove(REQUEST *request, VALUE_PAIR **to, VALUE_PAIR *from)
 
                                case T_OP_GE:
                                        if (rcode < 0) {
-                                               DEBUG4("::: REPLACING %s FROM %d TO %d",
+                                               RDEBUG4("::: REPLACING %s FROM %d TO %d",
                                                       from_list[i]->name, i, j);
                                                pairfree(&to_list[j]);
                                                to_list[j] = from_list[i];
@@ -1055,7 +1124,7 @@ void radius_pairmove(REQUEST *request, VALUE_PAIR **to, VALUE_PAIR *from)
                            (from_list[i]->operator == T_OP_GE) ||
                            (from_list[i]->operator == T_OP_SET)) {
                        append:
-                               DEBUG4("::: APPENDING %s FROM %d TO %d",
+                               RDEBUG4("::: APPENDING %s FROM %d TO %d",
                                       from_list[i]->name, i, tailto);
                                to_list[tailto++] = from_list[i];
                                from_list[i] = NULL;
@@ -1073,7 +1142,7 @@ void radius_pairmove(REQUEST *request, VALUE_PAIR **to, VALUE_PAIR *from)
        }
        free(from_list);
 
-       DEBUG4("::: TO in %d out %d", to_count, tailto);
+       RDEBUG4("::: TO in %d out %d", to_count, tailto);
 
        /*
         *      Re-chain the "to" list.
@@ -1084,7 +1153,7 @@ void radius_pairmove(REQUEST *request, VALUE_PAIR **to, VALUE_PAIR *from)
        for (i = 0; i < tailto; i++) {
                if (!to_list[i]) continue;
                
-               DEBUG4("::: to[%d] = %s", i, to_list[i]->name);
+               RDEBUG4("::: to[%d] = %s", i, to_list[i]->name);
 
                /*
                 *      Mash the operator to a simple '='.  The
@@ -1099,6 +1168,9 @@ void radius_pairmove(REQUEST *request, VALUE_PAIR **to, VALUE_PAIR *from)
                last = &(*last)->next;
        }
 
+       rad_assert(request != NULL);
+       rad_assert(request->packet != NULL);
+
        /*
         *      Fix dumb cache issues
         */
@@ -1113,6 +1185,7 @@ void radius_pairmove(REQUEST *request, VALUE_PAIR **to, VALUE_PAIR *from)
 }
 
 
+#ifdef WITH_UNLANG
 /*
  *     Copied shamelessly from conffile.c, to simplify the API for
  *     now...
@@ -1148,7 +1221,7 @@ int radius_update_attrlist(REQUEST *request, CONF_SECTION *cs,
        CONF_ITEM *ci;
        VALUE_PAIR *newlist, *vp;
        VALUE_PAIR **output_vps = NULL;
-       REQUEST *request_vps = request;
+       REQUEST *myrequest = request;
 
        if (!request || !cs) return RLM_MODULE_INVALID;
 
@@ -1159,27 +1232,67 @@ int radius_update_attrlist(REQUEST *request, CONF_SECTION *cs,
        if (strncmp(name, "outer.", 6) == 0) {
                if (!request->parent) return RLM_MODULE_NOOP;
 
-               request_vps = request->parent;
+               myrequest = request->parent;
                name += 6;
        }
 
        if (strcmp(name, "request") == 0) {
-               output_vps = &request_vps->packet->vps;
+               output_vps = &myrequest->packet->vps;
 
        } else if (strcmp(name, "reply") == 0) {
-               output_vps = &request_vps->reply->vps;
+               output_vps = &myrequest->reply->vps;
 
+#ifdef WITH_PROXY
        } else if (strcmp(name, "proxy-request") == 0) {
-               if (request->proxy) output_vps = &request_vps->proxy->vps;
+               if (request->proxy) output_vps = &myrequest->proxy->vps;
 
        } else if (strcmp(name, "proxy-reply") == 0) {
                if (request->proxy_reply) output_vps = &request->proxy_reply->vps;
+#endif
 
        } else if (strcmp(name, "config") == 0) {
-               output_vps = &request_vps->config_items;
+               output_vps = &myrequest->config_items;
 
        } else if (strcmp(name, "control") == 0) {
-               output_vps = &request_vps->config_items;
+               output_vps = &myrequest->config_items;
+
+#ifdef WITH_COA
+       } else if (strcmp(name, "coa") == 0) {
+               if (!myrequest->coa) {
+                       request_alloc_coa(myrequest);
+                       myrequest->coa->proxy->code = PW_COA_REQUEST;
+               }
+                 
+               if (myrequest->coa &&
+                   (myrequest->coa->proxy->code == PW_COA_REQUEST)) {
+                       output_vps = &myrequest->coa->proxy->vps;
+               }
+
+       } else if (strcmp(name, "coa-reply") == 0) {
+               if (myrequest->coa && /* match reply with request */
+                   (myrequest->coa->proxy->code == PW_COA_REQUEST) &&
+                    (myrequest->coa->proxy_reply)) {
+                     output_vps = &myrequest->coa->proxy_reply->vps;
+               }
+
+       } else if (strcmp(name, "disconnect") == 0) {
+               if (!myrequest->coa) {
+                       request_alloc_coa(myrequest);
+                       if (myrequest->coa) myrequest->coa->proxy->code = PW_DISCONNECT_REQUEST;
+               }
+
+               if (myrequest->coa &&
+                   (myrequest->coa->proxy->code == PW_DISCONNECT_REQUEST)) {
+                       output_vps = &myrequest->coa->proxy->vps;
+               }
+
+       } else if (strcmp(name, "disconnect-reply") == 0) {
+               if (myrequest->coa && /* match reply with request */
+                   (myrequest->coa->proxy->code == PW_DISCONNECT_REQUEST) &&
+                   (myrequest->coa->proxy_reply)) {
+                       output_vps = &myrequest->coa->proxy_reply->vps;
+               }
+#endif
 
        } else {
                return RLM_MODULE_INVALID;
@@ -1189,7 +1302,7 @@ int radius_update_attrlist(REQUEST *request, CONF_SECTION *cs,
 
        newlist = paircopy(input_vps);
        if (!newlist) {
-               DEBUG2("Out of memory");
+               RDEBUG2("Out of memory");
                return RLM_MODULE_FAIL;
        }
 
@@ -1209,6 +1322,13 @@ int radius_update_attrlist(REQUEST *request, CONF_SECTION *cs,
 
                cp = cf_itemtopair(ci);
 
+#ifndef NDEBUG
+               if (debug_flag && (vp->vendor == 0) &&
+                   radius_find_compare(vp->attribute)) {
+                       DEBUG("WARNING: You are modifying the value of virtual attribute %s.  This is not supported.", vp->name);
+               }
+#endif
+
                /*
                 *      The VP && CF lists should be in sync.  If they're
                 *      not, panic.
@@ -1225,8 +1345,8 @@ int radius_update_attrlist(REQUEST *request, CONF_SECTION *cs,
                        }
 
                        if (!pairparsevalue(vp, value)) {
-                               DEBUG2("ERROR: Failed parsing value \"%s\" for attribute %s: %s",
-                                      value, vp->name, librad_errstr);
+                               RDEBUG2("ERROR: Failed parsing value \"%s\" for attribute %s: %s",
+                                      value, vp->name, fr_strerror());
                                pairfree(&newlist);
                                return RLM_MODULE_FAIL;
                        }
@@ -1239,3 +1359,4 @@ int radius_update_attrlist(REQUEST *request, CONF_SECTION *cs,
 
        return RLM_MODULE_UPDATED;
 }
+#endif