Don't check for appending attributes if the attribute was
[freeradius.git] / src / main / evaluate.c
1 /*
2  * evaluate.c   Evaluate complex conditions
3  *
4  * Version:     $Id$
5  *
6  *   This program is free software; you can redistribute it and/or modify
7  *   it under the terms of the GNU General Public License as published by
8  *   the Free Software Foundation; either version 2 of the License, or
9  *   (at your option) any later version.
10  *
11  *   This program is distributed in the hope that it will be useful,
12  *   but WITHOUT ANY WARRANTY; without even the implied warranty of
13  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  *   GNU General Public License for more details.
15  *
16  *   You should have received a copy of the GNU General Public License
17  *   along with this program; if not, write to the Free Software
18  *   Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
19  *
20  * Copyright 2007  The FreeRADIUS server project
21  * Copyright 2007  Alan DeKok <aland@deployingradius.com>
22  */
23
24 #include <freeradius-devel/ident.h>
25 RCSID("$Id$")
26
27 #include <freeradius-devel/radiusd.h>
28 #include <freeradius-devel/modules.h>
29 #include <freeradius-devel/rad_assert.h>
30
31 #include <ctype.h>
32
33 #ifdef HAVE_REGEX_H
34 #include <regex.h>
35
36 /*
37  *  For POSIX Regular expressions.
38  *  (0) Means no extended regular expressions.
39  *  REG_EXTENDED means use extended regular expressions.
40  */
41 #ifndef REG_EXTENDED
42 #define REG_EXTENDED (0)
43 #endif
44
45 #ifndef REG_NOSUB
46 #define REG_NOSUB (0)
47 #endif
48 #endif
49
50
51 static int all_digits(const char *string)
52 {
53         const char *p = string;
54
55         if (*p == '-') p++;
56
57         while (isdigit((int) *p)) p++;
58
59         return (*p == '\0');
60 }
61
62 #ifndef NDEBUG
63 #ifndef DEBUG4
64 #define DEBUG4  if (debug_flag > 4)log_debug
65 #endif
66 #else
67 #define DEBUG4 if (0) log_debug
68 #endif
69
70 static const char *filler = "????????????????????????????????????????????????????????????????";
71
72 static const char *expand_string(char *buffer, size_t sizeof_buffer,
73                                  REQUEST *request,
74                                  LRAD_TOKEN value_type, const char *value)
75 {
76         int result;
77         char *p;
78
79         switch (value_type) {
80         default:
81         case T_BARE_WORD:
82         case T_SINGLE_QUOTED_STRING:
83                 return value;
84
85         case T_BACK_QUOTED_STRING:
86                 result = radius_exec_program(value, request, 1,
87                                              buffer, sizeof_buffer, NULL,
88                                              NULL, 0);
89                 if (result != 0) {
90                         return NULL;
91                 }
92
93                 /*
94                  *      The result should be ASCII.
95                  */
96                 for (p = buffer; *p != '\0'; p++) {
97                         if (*p < ' ' ) {
98                                 *p = '\0';
99                                 return buffer;
100                         }
101                 }
102                 return buffer;
103
104
105         case T_DOUBLE_QUOTED_STRING:
106                 if (!strchr(value, '%')) return value;
107
108                 radius_xlat(buffer, sizeof_buffer, value, request, NULL);
109                 return buffer;
110         }
111
112         return NULL;
113 }
114
115 #ifdef HAVE_REGEX_H
116 static LRAD_TOKEN getregex(char **ptr, char *buffer, size_t buflen, int *pcflags)
117 {
118         char *p = *ptr;
119         char *q = buffer;
120
121         if (*p != '/') return T_OP_INVALID;
122
123         *pcflags = REG_EXTENDED;
124
125         p++;
126         while (*p) {
127                 if (buflen <= 1) break;
128
129                 if (*p == '/') {
130                         p++;
131
132                         /*
133                          *      Check for case insensitivity
134                          */
135                         if (*p == 'i') {
136                                 p++;
137                                 *pcflags |= REG_ICASE;
138                         }
139
140                         break;
141                 }
142
143                 if (*p == '\\') {
144                         int x;
145                         
146                         switch (p[1]) {
147                         case 'r':
148                                 *q++ = '\r';
149                                 break;
150                         case 'n':
151                                 *q++ = '\n';
152                                 break;
153                         case 't':
154                                 *q++ = '\t';
155                                 break;
156                         case '"':
157                                 *q++ = '"';
158                                 break;
159                         case '\'':
160                                 *q++ = '\'';
161                                 break;
162                         case '`':
163                                 *q++ = '`';
164                                 break;
165                                 
166                                 /*
167                                  *      FIXME: add 'x' and 'u'
168                                  */
169
170                         default:
171                                 if ((p[1] >= '0') && (p[1] <= '9') &&
172                                     (sscanf(p, "%3o", &x) == 1)) {
173                                         *q++ = x;
174                                         p += 2;
175                                 } else {
176                                         *q++ = p[1];
177                                 }
178                                 break;
179                         }
180                         p += 2;
181                         buflen--;
182                         continue;
183                 }
184
185                 *(q++) = *(p++);
186                 buflen--;
187         }
188         *q = '\0';
189         *ptr = p;
190
191         return T_BARE_WORD;
192 }
193 #endif
194
195 int radius_evaluate_condition(REQUEST *request, int depth,
196                               const char **ptr, int evaluate_it, int *presult)
197 {
198         int found_condition = FALSE;
199         int result = TRUE;
200         int invert = FALSE;
201         int evaluate_next_condition = evaluate_it;
202         const char *p = *ptr;
203         const char *q, *start;
204         LRAD_TOKEN token, lt, rt;
205         char left[1024], right[1024], comp[4];
206         const char *pleft, *pright;
207         char  xleft[1024], xright[1024];
208         int lint, rint;
209 #ifdef HAVE_REGEX_H
210         int cflags = 0;
211 #endif
212
213         if (!ptr || !*ptr || (depth >= 64)) {
214                 radlog(L_ERR, "Internal sanity check failed in evaluate condition");
215                 return FALSE;
216         }
217
218         while (*p) {
219                 while ((*p == ' ') || (*p == '\t')) p++;
220
221                 if (*p == '!') {
222                         DEBUG4(">>> INVERT");
223                         invert = TRUE;
224                         p++;
225                 }
226
227                 /*
228                  *      It's a subcondition.
229                  */
230                 if (*p == '(') {
231                         const char *end = p + 1;
232
233                         /*
234                          *      Evaluate the condition, bailing out on
235                          *      parse error.
236                          */
237                         DEBUG4(">>> CALLING EVALUATE %s", end);
238                         if (!radius_evaluate_condition(request, depth + 1,
239                                                        &end,
240                                                        evaluate_next_condition,
241                                                        &result)) {
242                                 return FALSE;
243                         }
244
245                         if (invert && evaluate_next_condition) {
246                                 DEBUG2("%.*s Converting !%s -> %s",
247                                        depth, filler,
248                                        (result != FALSE) ? "TRUE" : "FALSE",
249                                        (result == FALSE) ? "TRUE" : "FALSE");
250
251                                        
252                                 result = (result == FALSE);
253                                 invert = FALSE;
254                         }
255
256                         /*
257                          *      Start from the end of the previous
258                          *      condition
259                          */
260                         p = end;
261                         DEBUG4(">>> EVALUATE RETURNED ::%s::", end);
262                         
263                         if (!((*p == ')') || (*p == '!') ||
264                               ((p[0] == '&') && (p[1] == '&')) ||
265                               ((p[0] == '|') && (p[1] == '|')))) {
266
267                                 radlog(L_ERR, "Parse error in condition at: %s", p);
268                                 return FALSE;
269                         }
270                         if (*p == ')') p++;     /* skip it */
271                         found_condition = TRUE;
272                         
273                         while ((*p == ' ') || (*p == '\t')) p++;
274
275                         /*
276                          *      EOL.  It's OK.
277                          */
278                         if (!*p) {
279                                 DEBUG4(">>> AT EOL");
280                                 *ptr = p;
281                                 *presult = result;
282                                 return TRUE;
283                                 
284                                 /*
285                                  *      (A && B) means "evaluate B
286                                  *      only if A was true"
287                                  */
288                         } else if ((p[0] == '&') && (p[1] == '&')) {
289                                 if (result == TRUE) {
290                                         evaluate_next_condition = evaluate_it;
291                                 } else {
292                                         evaluate_next_condition = FALSE;
293                                 }
294                                 p += 2;
295                                 
296                                 /*
297                                  *      (A || B) means "evaluate B
298                                  *      only if A was false"
299                                  */
300                         } else if ((p[0] == '|') && (p[1] == '|')) {
301                                 if (result == FALSE) {
302                                         evaluate_next_condition = evaluate_it;
303                                 } else {
304                                         evaluate_next_condition = FALSE;
305                                 }
306                                 p += 2;
307
308                         } else if (*p == ')') {
309                                 DEBUG4(">>> CLOSING BRACE");
310                                 *ptr = p;
311                                 *presult = result;
312                                 return TRUE;
313
314                         } else {
315                                 /*
316                                  *      Parse error
317                                  */
318                                 radlog(L_ERR, "Unexpected trailing text at: %s", p);
319                                 return FALSE;
320                         }
321                 } /* else it wasn't an opening brace */
322
323                 while ((*p == ' ') || (*p == '\t')) p++;
324
325                 /*
326                  *      More conditions, keep going.
327                  */
328                 if ((*p == '(') || (p[0] == '!')) continue;
329
330                 DEBUG4(">>> LOOKING AT %s", p);
331                 start = p;
332
333                 /*
334                  *      Look for common errors.
335                  */
336                 if ((p[0] == '%') && (p[1] == '{')) {
337                         radlog(L_ERR, "Bare %%{...} is invalid in condition at: %s", p);
338                         return FALSE;
339                 }
340
341                 /*
342                  *      Look for word == value
343                  */
344                 lt = gettoken(&p, left, sizeof(left));
345                 if ((lt != T_BARE_WORD) &&
346                     (lt != T_DOUBLE_QUOTED_STRING) &&
347                     (lt != T_SINGLE_QUOTED_STRING) &&
348                     (lt != T_BACK_QUOTED_STRING)) {
349                         radlog(L_ERR, "Expected string or numbers at: %s", p);
350                         return FALSE;
351                 }
352
353                 pleft = left;
354                 if (evaluate_next_condition) {
355                         pleft = expand_string(xleft, sizeof(xleft), request,
356                                               lt, left);
357                         if (!pleft) {
358                                 radlog(L_ERR, "Failed expanding string at: %s",
359                                        left);
360                                 return FALSE;
361                         }
362                 }
363
364                 /*
365                  *      Peek ahead.  Maybe it's just a check for
366                  *      existence.  If so, there shouldn't be anything
367                  *      else.
368                  */
369                 q = p;
370                 while ((*q == ' ') || (*q == '\t')) q++;
371
372                 /*
373                  *      End of condition, 
374                  */
375                 if (!*q || (*q == ')') || ((*q == '!') && (q[1] != '=')) ||
376                     ((q[0] == '&') && (q[1] == '&')) ||
377                     ((q[0] == '|') && (q[1] == '|'))) {
378                         /*
379                          *      Check for truth or falsehood.
380                          */
381                         if (all_digits(pleft)) {
382                                 lint = atoi(pleft);
383                                 result = (lint != 0);
384                         } else {
385                                 result = (*pleft != '\0');
386                         }
387
388                         if (invert) {
389                                 DEBUG4(">>> INVERTING result");
390                                 result = (result == FALSE);
391                                 invert = FALSE;
392                         }
393
394                         if (evaluate_next_condition) {
395                                 DEBUG2("%.*s Evaluating %s\"%s\" -> %s",
396                                        depth, filler,
397                                        invert ? "!" : "", pleft,
398                                        (result != FALSE) ? "TRUE" : "FALSE");
399
400                         } else if (request) {
401                                 DEBUG2("%.*s Skipping %s\"%s\"",
402                                        depth, filler,
403                                        invert ? "!" : "", pleft);
404                         }
405
406                         DEBUG4(">>> I%d %d:%s", invert,
407                                lt, left);
408                         goto end_of_condition;
409                 }
410
411                 /*
412                  *      Else it's a full "foo == bar" thingy.
413                  */
414                 token = gettoken(&p, comp, sizeof(comp));
415                 if ((token < T_OP_NE) || (token > T_OP_CMP_EQ) ||
416                     (token == T_OP_CMP_TRUE) ||
417                     (token == T_OP_CMP_FALSE)) {
418                         radlog(L_ERR, "Expected comparison at: %s", comp);
419                         return FALSE;
420                 }
421                 
422                 /*
423                  *      Look for common errors.
424                  */
425                 if ((p[0] == '%') && (p[1] == '{')) {
426                         radlog(L_ERR, "Bare %%{...} is invalid in condition at: %s", p);
427                         return FALSE;
428                 }
429                 
430                 /*
431                  *      Validate strings.
432                  */
433 #ifdef HAVE_REGEX_H
434                 if ((token == T_OP_REG_EQ) ||
435                     (token == T_OP_REG_NE)) {
436                         rt = getregex(&p, right, sizeof(right), &cflags);
437                 } else
438 #endif
439                         rt = gettoken(&p, right, sizeof(right));
440
441                 if ((rt != T_BARE_WORD) &&
442                     (rt != T_DOUBLE_QUOTED_STRING) &&
443                     (rt != T_SINGLE_QUOTED_STRING) &&
444                     (rt != T_BACK_QUOTED_STRING)) {
445                         radlog(L_ERR, "Expected string or numbers at: %s", p);
446                         return FALSE;
447                 }
448                 
449                 pright = right;
450                 if (evaluate_next_condition) {
451                         pright = expand_string(xright, sizeof(xright), request,
452                                                rt, right);
453                         if (!pright) {
454                                 radlog(L_ERR, "Failed expanding string at: %s",
455                                        right);
456                                 return FALSE;
457                         }
458                 }
459                 
460                 DEBUG4(">>> %d:%s %d %d:%s",
461                        lt, pleft, token, rt, pright);
462                 
463                 if (evaluate_next_condition) {
464                         switch (token) {
465                         case T_OP_GT:
466                         case T_OP_LE:
467                         case T_OP_LT:
468                                 if (!all_digits(pleft)) {
469                                         radlog(L_ERR, "Left field is not a number at: %s", pleft);
470                                         return FALSE;
471                                 }
472                                 if (!all_digits(pright)) {
473                                         radlog(L_ERR, "Right field is not a number at: %s", pright);
474                                         return FALSE;
475                                 }
476                                 lint = atoi(pleft);
477                                 rint = atoi(pright);
478                                 break;
479                                 
480                         default:
481                                 break;
482                         }
483
484                         switch (token) {
485                         case T_OP_CMP_EQ:
486                                 result = (strcmp(pleft, pright) == 0);
487                                 break;
488
489                         case T_OP_NE:
490                                 result = (strcmp(pleft, pright) != 0);
491                                 break;
492
493                         case T_OP_GE:
494                                 result = (lint >= rint);
495                                 break;
496
497                         case T_OP_GT:
498                                 result = (lint > rint);
499                                 break;
500
501                         case T_OP_LE:
502                                 result = (lint <= rint);
503                                 break;
504
505                         case T_OP_LT:
506                                 result = (lint < rint);
507                                 break;
508
509 #ifdef HAVE_REGEX_H
510                         case T_OP_REG_EQ: {
511                                 int i, compare;
512                                 regex_t reg;
513                                 regmatch_t rxmatch[REQUEST_MAX_REGEX + 1];
514                                 
515                                 /*
516                                  *      Include substring matches.
517                                  */
518                                 regcomp(&reg, pright, cflags);
519                                 compare = regexec(&reg, pleft,
520                                                   REQUEST_MAX_REGEX + 1,
521                                                   rxmatch, 0);
522                                 regfree(&reg);
523                                 
524                                 /*
525                                  *      Add %{0}, %{1}, etc.
526                                  */
527                                 for (i = 0; i <= REQUEST_MAX_REGEX; i++) {
528                                         char *r;
529                                         char buffer[1024];
530                                         
531                                         /*
532                                          *      Didn't match: delete old
533                                          *      match, if it existed.
534                                          */
535                                         if ((compare != 0) ||
536                                             (rxmatch[i].rm_so == -1)) {
537                                                 r = request_data_get(request,
538                                                                      request,
539                                                                      REQUEST_DATA_REGEX | i);
540                                                 if (r) {
541                                                         free(r);
542                                                         continue;
543                                                 }
544                                                 
545                                                 /*
546                                                  *      No previous match
547                                                  *      to delete, stop.
548                                                  */
549                                                 break;
550                                         }
551                                         
552                                         /*
553                                          *      Copy substring into buffer.
554                                          */
555                                         memcpy(buffer, pleft + rxmatch[i].rm_so,
556                                                rxmatch[i].rm_eo - rxmatch[i].rm_so);
557                                         buffer[rxmatch[i].rm_eo - rxmatch[i].rm_so] = '\0';
558                                         
559                                         /*
560                                          *      Copy substring, and add it to
561                                          *      the request.
562                                          *
563                                          *      Note that we don't check
564                                          *      for out of memory, which is
565                                          *      the only error we can get...
566                                          */
567                                         r = strdup(buffer);
568                                         request_data_add(request, request,
569                                                          REQUEST_DATA_REGEX | i,
570                                                          r, free);
571                                 }
572                                 result = (compare == 0);
573                         }
574                                 break;
575
576                         case T_OP_REG_NE: {
577                                 int compare;
578                                 regex_t reg;
579                                 regmatch_t rxmatch[REQUEST_MAX_REGEX + 1];
580                                 
581                                 /*
582                                  *      Include substring matches.
583                                  */
584                                 regcomp(&reg, pright, cflags);
585                                 compare = regexec(&reg, pleft,
586                                                   REQUEST_MAX_REGEX + 1,
587                                                   rxmatch, 0);
588                                 regfree(&reg);
589                                 
590                                 result = (compare != 0);
591                         }
592                                 break;
593 #endif
594
595                         default:
596                                 DEBUG4(">>> NOT IMPLEMENTED %d", token);
597                                 break;
598                         }
599
600                         DEBUG2("%.*s Evaluating %s(%.*s) -> %s",
601                                depth, filler,
602                                invert ? "!" : "", p - start, start,
603                                (result != FALSE) ? "TRUE" : "FALSE");
604
605                         DEBUG4(">>> GOT result %d", result);
606
607                         /*
608                          *      Not evaluating it.  We may be just
609                          *      parsing it.
610                          */
611                 } else if (request) {
612                         DEBUG2("%.*s Skipping %s(\"%s\" %s \"%s\")",
613                                depth, filler,
614                                invert ? "!" : "", pleft, comp, pright);
615                 }
616
617                 end_of_condition:
618                 if (invert) {
619                         DEBUG4(">>> INVERTING result");
620                         result = (result == FALSE);
621                         invert = FALSE;
622                 }
623
624                 /*
625                  *      Don't evaluate it.
626                  */
627                 DEBUG4(">>> EVALUATE %d ::%s::",
628                         evaluate_next_condition, p);
629
630                 while ((*p == ' ') || (*p == '\t')) p++;
631
632                 /*
633                  *      Closing brace or EOL, return.
634                  */
635                 if (!*p || (*p == ')') || ((*p == '!') && (p[1] != '=')) ||
636                     ((p[0] == '&') && (p[1] == '&')) ||
637                     ((p[0] == '|') && (p[1] == '|'))) {
638                         DEBUG4(">>> AT EOL2a");
639                         *ptr = p;
640                         if (evaluate_next_condition) *presult = result;
641                         return TRUE;
642                 }
643         } /* loop over the input condition */
644
645         DEBUG4(">>> AT EOL2b");
646         *ptr = p;
647         if (evaluate_next_condition) *presult = result;
648         return TRUE;
649 }
650
651 /*
652  *      The pairmove() function in src/lib/valuepair.c does all sorts of
653  *      extra magic that we don't want here.
654  *
655  *      FIXME: integrate this with the code calling it, so that we
656  *      only paircopy() those attributes that we're really going to
657  *      use.
658  */
659 static void my_pairmove(REQUEST *request, VALUE_PAIR **to, VALUE_PAIR *from)
660 {
661         int i, j, count, from_count, to_count, tailto;
662         VALUE_PAIR *vp, *next, **last;
663         VALUE_PAIR **from_list, **to_list;
664         int *edited;
665
666         /*
667          *      Set up arrays for editing, to remove some of the
668          *      O(N^2) dependencies.  This also makes it easier to
669          *      insert and remove attributes.
670          *
671          *      It also means that the operators apply ONLY to the
672          *      attributes in the original list.  With the previous
673          *      implementation of pairmove(), adding two attributes
674          *      via "+=" and then "=" would mean that the second one
675          *      wasn't added, because of the existence of the first
676          *      one in the "to" list.  This implementation doesn't
677          *      have that bug.
678          *
679          *      Also, the previous implementation did NOT implement
680          *      "-=" correctly.  If two of the same attributes existed
681          *      in the "to" list, and you tried to subtract something
682          *      matching the *second* value, then the pairdelete()
683          *      function was called, and the *all* attributes of that
684          *      number were deleted.  With this implementation, only
685          *      the matching attributes are deleted.
686          */
687         count = 0;
688         for (vp = from; vp != NULL; vp = vp->next) count++;
689         from_list = rad_malloc(sizeof(*from_list) * count);
690
691         for (vp = *to; vp != NULL; vp = vp->next) count++;
692         to_list = rad_malloc(sizeof(*to_list) * count);
693
694         /*
695          *      Move the lists to the arrays, and break the list
696          *      chains.
697          */
698         from_count = 0;
699         for (vp = from; vp != NULL; vp = next) {
700                 next = vp->next;
701                 from_list[from_count++] = vp;
702                 vp->next = NULL;
703         }
704
705         to_count = 0;
706         for (vp = *to; vp != NULL; vp = next) {
707                 next = vp->next;
708                 to_list[to_count++] = vp;
709                 vp->next = NULL;
710         }
711         tailto = to_count;
712         edited = rad_malloc(sizeof(*edited) * to_count);
713         memset(edited, 0, sizeof(*edited) * to_count);
714
715         DEBUG4("::: FROM %d TO %d MAX %d", from_count, to_count, count);
716
717         /*
718          *      Now that we have the lists initialized, start working
719          *      over them.
720          */
721         for (i = 0; i < from_count; i++) {
722                 int found;
723
724                 DEBUG4("::: Examining %s", from_list[i]->name);
725
726                 /*
727                  *      Attribute should be appended, OR the "to" list
728                  *      is empty, and we're supposed to replace or
729                  *      "add if not existing".
730                  */
731                 if (from_list[i]->operator == T_OP_ADD) goto append;
732
733                 found = FALSE;
734                 for (j = 0; j < to_count; j++) {
735                         if (edited[j]) continue;
736
737                         /*
738                          *      Attributes aren't the same, skip them.
739                          */
740                         if (from_list[i]->attribute != to_list[j]->attribute) {
741                                 continue;
742                         }
743
744                         /*
745                          *      We don't use a "switch" statement here
746                          *      because we want to break out of the
747                          *      "for" loop over 'j' in most cases.
748                          */
749
750                         /*
751                          *      Over-write the FIRST instance of the
752                          *      matching attribute name.  We free the
753                          *      one in the "to" list, and move over
754                          *      the one in the "from" list.
755                          */
756                         if (from_list[i]->operator == T_OP_SET) {
757                                 DEBUG4("::: OVERWRITING %s FROM %d TO %d",
758                                        to_list[j]->name, i, j);
759                                 pairfree(&to_list[j]);
760                                 to_list[j] = from_list[i];
761                                 from_list[i] = NULL;
762                                 edited[j] = TRUE;
763                                 break;
764                         }
765
766                         /*
767                          *      Add the attribute only if it does not
768                          *      exist... but it exists, so we stop
769                          *      looking.
770                          */
771                         if (from_list[i]->operator == T_OP_EQ) {
772                                 found = TRUE;
773                                 break;
774                         }
775
776                         /*
777                          *      Delete all matching attributes from
778                          *      "to"
779                          */
780                         if ((from_list[i]->operator == T_OP_SUB) ||
781                             (from_list[i]->operator == T_OP_LE) ||
782                             (from_list[i]->operator == T_OP_GE)) {
783                                 int rcode;
784                                 int old_op = from_list[i]->operator;
785
786                                 /*
787                                  *      Check for equality.
788                                  */
789                                 from_list[i]->operator = T_OP_CMP_EQ;
790
791                                 /*
792                                  *      If equal, delete the one in
793                                  *      the "to" list.
794                                  */
795                                 rcode = radius_compare_vps(NULL, from_list[i],
796                                                            to_list[j]);
797                                 /*
798                                  *      We may want to do more
799                                  *      subtractions, so we re-set the
800                                  *      operator back to it's original
801                                  *      value.
802                                  */
803                                 from_list[i]->operator = old_op;
804
805                                 switch (old_op) {
806                                 case T_OP_SUB:
807                                         if (rcode == 0) {
808                                                 DEBUG4("::: DELETING %s FROM %d TO %d",
809                                                        from_list[i]->name, i, j);
810                                                 pairfree(&to_list[j]);
811                                                 to_list[j] = NULL;
812                                         }
813                                         break;
814
815                                         /*
816                                          *      Enforce <=.  If it's
817                                          *      >, replace it.
818                                          */
819                                 case T_OP_LE:
820                                         if (rcode > 0) {
821                                                 DEBUG4("::: REPLACING %s FROM %d TO %d",
822                                                        from_list[i]->name, i, j);
823                                                 pairfree(&to_list[j]);
824                                                 to_list[j] = from_list[i];
825                                                 from_list[i] = NULL;
826                                                 edited[j] = TRUE;
827                                         }
828                                         break;
829
830                                 case T_OP_GE:
831                                         if (rcode < 0) {
832                                                 DEBUG4("::: REPLACING %s FROM %d TO %d",
833                                                        from_list[i]->name, i, j);
834                                                 pairfree(&to_list[j]);
835                                                 to_list[j] = from_list[i];
836                                                 from_list[i] = NULL;
837                                                 edited[j] = TRUE;
838                                         }
839                                         break;
840                                 }
841
842                                 continue;
843                         }
844
845                         rad_assert(0 == 1); /* panic! */
846                 }
847
848                 /*
849                  *      We were asked to add it if it didn't exist,
850                  *      and it doesn't exist.  Move it over to the
851                  *      tail of the "to" list, UNLESS it was already
852                  *      moved by another operator.
853                  */
854                 if (!found && from_list[i]) {
855                         if ((from_list[i]->operator == T_OP_EQ) ||
856                             (from_list[i]->operator == T_OP_LE) ||
857                             (from_list[i]->operator == T_OP_GE) ||
858                             (from_list[i]->operator == T_OP_SET)) {
859                         append:
860                                 DEBUG4("::: APPENDING %s FROM %d TO %d",
861                                        from_list[i]->name, i, tailto);
862                                 to_list[tailto++] = from_list[i];
863                                 from_list[i] = NULL;
864                         }
865                 }
866         }
867
868         /*
869          *      Delete attributes in the "from" list.
870          */
871         for (i = 0; i < from_count; i++) {
872                 if (!from_list[i]) continue;
873
874                 pairfree(&from_list[i]);
875         }
876         free(from_list);
877
878         DEBUG4("::: TO in %d out %d", to_count, tailto);
879
880         /*
881          *      Re-chain the "to" list.
882          */
883         *to = NULL;
884         last = to;
885         for (i = 0; i < tailto; i++) {
886                 if (!to_list[i]) continue;
887                 
888                 DEBUG4("::: to[%d] = %s", i, to_list[i]->name);
889
890                 *last = to_list[i];
891                 last = &(*last)->next;
892
893                 /*
894                  *      Fix dumb cache issues
895                  */
896                 if ((i >= to_count) || edited[i]) {
897                         if (to_list[i]->attribute == PW_USER_NAME) {
898                                 request->username = to_list[i];
899                                 
900                         } else if (to_list[i]->attribute == PW_USER_PASSWORD) {
901                                 request->password = to_list[i];
902                         }
903                 }
904         }
905         free(to_list);
906 }
907
908
909 /*
910  *     Copied shamelessly from conffile.c, to simplify the API for
911  *     now...
912  */
913 typedef enum conf_type {
914         CONF_ITEM_INVALID = 0,
915         CONF_ITEM_PAIR,
916         CONF_ITEM_SECTION,
917         CONF_ITEM_DATA
918 } CONF_ITEM_TYPE;
919
920 struct conf_item {
921         struct conf_item *next;
922         struct conf_part *parent;
923         int lineno;
924         const char *filename;
925         CONF_ITEM_TYPE type;
926 };
927 struct conf_pair {
928         CONF_ITEM item;
929         char *attr;
930         char *value;
931         LRAD_TOKEN operator;
932         LRAD_TOKEN value_type;
933 };
934
935 /*
936  *      Add attributes to a list.
937  */
938 int radius_update_attrlist(REQUEST *request, CONF_SECTION *cs,
939                            VALUE_PAIR *input_vps, const char *name)
940 {
941         CONF_ITEM *ci;
942         VALUE_PAIR *newlist, *vp;
943         VALUE_PAIR **output_vps = NULL;
944
945         if (!request || !cs) return RLM_MODULE_INVALID;
946
947         if (strcmp(name, "request") == 0) {
948                 output_vps = &request->packet->vps;
949
950         } else if (strcmp(name, "reply") == 0) {
951                 output_vps = &request->reply->vps;
952
953         } else if (strcmp(name, "proxy-request") == 0) {
954                 if (request->proxy) output_vps = &request->proxy->vps;
955
956         } else if (strcmp(name, "proxy-reply") == 0) {
957                 if (request->proxy_reply) output_vps = &request->proxy_reply->vps;
958
959         } else if (strcmp(name, "config") == 0) {
960                 output_vps = &request->config_items;
961
962         } else if (strcmp(name, "control") == 0) {
963                 output_vps = &request->config_items;
964
965         } else {
966                 return RLM_MODULE_INVALID;
967         }
968
969         if (!output_vps) return RLM_MODULE_NOOP; /* didn't update the list */
970
971         newlist = paircopy(input_vps);
972         if (!newlist) {
973                 DEBUG2("Out of memory");
974                 return RLM_MODULE_FAIL;
975         }
976
977         vp = newlist;
978         for (ci=cf_item_find_next(cs, NULL);
979              ci != NULL;
980              ci=cf_item_find_next(cs, ci)) {
981                 CONF_PAIR *cp;
982
983                 /*
984                  *      This should never happen.
985                  */
986                 if (cf_item_is_section(ci)) {
987                         pairfree(&newlist);
988                         return RLM_MODULE_INVALID;
989                 }
990
991                 cp = cf_itemtopair(ci);
992
993                 /*
994                  *      The VP && CF lists should be in sync.  If they're
995                  *      not, panic.
996                  */
997                 if (vp->flags.do_xlat) {
998                         const char *value;
999                         char buffer[2048];
1000
1001                         value = expand_string(buffer, sizeof(buffer), request,
1002                                               cp->value_type, cp->value);
1003                         if (!value) {
1004                                 pairfree(&newlist);
1005                                 return RLM_MODULE_INVALID;
1006                         }
1007
1008                         if (!pairparsevalue(vp, value)) {
1009                                 DEBUG2("ERROR: Failed parsing value \"%s\" for attribute %s: %s",
1010                                        value, vp->name, librad_errstr);
1011                                 pairfree(&newlist);
1012                                 return RLM_MODULE_FAIL;
1013                         }
1014                         vp->flags.do_xlat = 0;
1015                 }
1016                 vp = vp->next;
1017         }
1018
1019         my_pairmove(request, output_vps, newlist);
1020
1021         return RLM_MODULE_UPDATED;
1022 }
1023
1024