free stuff
[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(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
665         /*
666          *      Set up arrays for editing, to remove some of the
667          *      O(N^2) dependencies.  This also makes it easier to
668          *      insert and remove attributes.
669          *
670          *      It also means that the operators apply ONLY to the
671          *      attributes in the original list.  With the previous
672          *      implementation of pairmove(), adding two attributes
673          *      via "+=" and then "=" would mean that the second one
674          *      wasn't added, because of the existence of the first
675          *      one in the "to" list.  This implementation doesn't
676          *      have that bug.
677          *
678          *      Also, the previous implementation did NOT implement
679          *      "-=" correctly.  If two of the same attributes existed
680          *      in the "to" list, and you tried to subtract something
681          *      matching the *second* value, then the pairdelete()
682          *      function was called, and the *all* attributes of that
683          *      number were deleted.  With this implementation, only
684          *      the matching attributes are deleted.
685          */
686         count = 0;
687         for (vp = *from; vp != NULL; vp = vp->next) count++;
688         from_list = rad_malloc(sizeof(*from_list) * count);
689
690         for (vp = *to; vp != NULL; vp = vp->next) count++;
691         to_list = rad_malloc(sizeof(*to_list) * count);
692
693         /*
694          *      Move the lists to the arrays, and break the list
695          *      chains.
696          */
697         from_count = 0;
698         for (vp = *from; vp != NULL; vp = next) {
699                 next = vp->next;
700                 from_list[from_count++] = vp;
701                 vp->next = NULL;
702         }
703
704         to_count = 0;
705         for (vp = *to; vp != NULL; vp = next) {
706                 next = vp->next;
707                 to_list[to_count++] = vp;
708                 vp->next = NULL;
709         }
710         tailto = to_count;
711
712         DEBUG4("FROM %d TO %d MAX %d", from_count, to_count, count);
713
714         /*
715          *      Now that we have the lists initialized, start working
716          *      over them.
717          */
718         for (i = 0; i < from_count; i++) {
719                 int found;
720
721                 /*
722                  *      Attribute should be appended, OR the "to" list
723                  *      is empty, and we're supposed to replace or
724                  *      "add if not existing".
725                  */
726                 if ((from_list[i]->operator == T_OP_ADD) ||
727                     ((to_count == 0) &&
728                      ((from_list[i]->operator == T_OP_SET) ||
729                       (from_list[i]->operator == T_OP_EQ)))) {
730                         DEBUG4("APPENDING %s FROM %d TO %d",
731                                from_list[i]->name, i, tailto);
732                         to_list[tailto++] = from_list[i];
733                         from_list[i] = NULL;
734                         continue;
735                 }
736
737                 found = FALSE;
738                 for (j = 0; j < to_count; j++) {
739                         /*
740                          *      Attributes aren't the same, skip them.
741                          */
742                         if (from_list[i]->attribute != to_list[j]->attribute) {
743                                 continue;
744                         }
745
746                         /*
747                          *      We don't use a "switch" statement here
748                          *      because we want to break out of the
749                          *      "for" loop over 'j' in most cases.
750                          */
751
752                         /*
753                          *      Over-write the FIRST instance of the
754                          *      matching attribute name.  We free the
755                          *      one in the "to" list, and move over
756                          *      the one in the "from" list.
757                          */
758                         if (from_list[i]->operator == T_OP_SET) {
759                                 DEBUG4("OVERWRITING %s FROM %d TO %d",
760                                        to_list[j]->name, i, j);
761                                 pairfree(&to_list[j]);
762                                 to_list[j] = from_list[i];
763                                 from_list[i] = NULL;
764                                 break;
765                         }
766
767                         /*
768                          *      Add the attribute only if it does not
769                          *      exist... but it exists, so we stop
770                          *      looking.
771                          */
772                         if (from_list[i]->operator == T_OP_EQ) {
773                                 found = TRUE;
774                                 break;
775                         }
776
777                         /*
778                          *      Delete all matching attributes from
779                          *      "to"
780                          */
781                         if (from_list[i]->operator == T_OP_SUB) {
782                                 /*
783                                  *      Check for equality.
784                                  */
785                                 from_list[i]->operator = T_OP_CMP_EQ;
786
787                                 /*
788                                  *      If equal, delete the one in
789                                  *      the "to" list.
790                                  */
791                                 if (radius_compare_vps(NULL, from_list[i],
792                                                        to_list[j]) == 0) {
793
794                                         DEBUG4("DELETING %s FROM %d TO %d",
795                                                from_list[i]->name, i, j);
796                                         pairfree(&to_list[j]);
797                                         to_list[j] = NULL;
798                                 }
799
800                                 /*
801                                  *      We may want to do more
802                                  *      subtractions, so we re-set the
803                                  *      operator back to it's original
804                                  *      value.
805                                  */
806                                 from_list[i]->operator = T_OP_SUB;
807                                 continue;
808                         }
809
810                         rad_assert(0 == 1); /* panic! */
811                 }
812
813                 /*
814                  *      We were asked to add it if it didn't exist,
815                  *      and it doesn't exist.  Move it over to the tail
816                  *      of the "to" list.
817                  */
818                 if ((from_list[i]->operator == T_OP_EQ) && !found) {
819                         to_list[tailto++] = from_list[i];
820                         from_list[i] = NULL;
821                 }
822         }
823
824         /*
825          *      Re-chain the "from" list.
826          */
827         *from = NULL;
828         last = from;
829         for (i = 0; i < from_count; i++) {
830                 if (!from_list[i]) continue;
831
832                 *last = from_list[i];
833                 last = &(*last)->next;
834         }
835         free(from_list);
836
837         DEBUG4("TO %d %d", to_count, tailto);
838
839         /*
840          *      Re-chain the "to" list.
841          */
842         *to = NULL;
843         last = to;
844         for (i = 0; i < tailto; i++) {
845                 if (!to_list[i]) continue;
846                 
847                 DEBUG4("to[%d] = %s", i, to_list[i]->name);
848
849                 *last = to_list[i];
850                 last = &(*last)->next;
851         }
852         free(to_list);
853 }
854
855
856 /*
857  *     Copied shamelessly from conffile.c, to simplify the API for
858  *     now...
859  */
860 typedef enum conf_type {
861         CONF_ITEM_INVALID = 0,
862         CONF_ITEM_PAIR,
863         CONF_ITEM_SECTION,
864         CONF_ITEM_DATA
865 } CONF_ITEM_TYPE;
866
867 struct conf_item {
868         struct conf_item *next;
869         struct conf_part *parent;
870         int lineno;
871         CONF_ITEM_TYPE type;
872 };
873 struct conf_pair {
874         CONF_ITEM item;
875         char *attr;
876         char *value;
877         LRAD_TOKEN operator;
878         LRAD_TOKEN value_type;
879 };
880
881 /*
882  *      Add attributes to a list.
883  */
884 int radius_update_attrlist(REQUEST *request, CONF_SECTION *cs,
885                            VALUE_PAIR *input_vps, const char *name)
886 {
887         CONF_ITEM *ci;
888         VALUE_PAIR *newlist, *vp;
889         VALUE_PAIR **output_vps = NULL;
890
891         if (!request || !cs) return RLM_MODULE_INVALID;
892
893         if (strcmp(name, "request") == 0) {
894                 output_vps = &request->packet->vps;
895
896         } else if (strcmp(name, "reply") == 0) {
897                 output_vps = &request->reply->vps;
898
899         } else if (strcmp(name, "proxy-request") == 0) {
900                 if (request->proxy) output_vps = &request->proxy->vps;
901
902         } else if (strcmp(name, "proxy-reply") == 0) {
903                 if (request->proxy_reply) output_vps = &request->proxy_reply->vps;
904
905         } else if (strcmp(name, "config") == 0) {
906                 output_vps = &request->config_items;
907
908         } else if (strcmp(name, "control") == 0) {
909                 output_vps = &request->config_items;
910
911         } else {
912                 return RLM_MODULE_INVALID;
913         }
914
915         if (!output_vps) return RLM_MODULE_NOOP; /* didn't update the list */
916
917         newlist = paircopy(input_vps);
918         if (!newlist) {
919                 DEBUG2("Out of memory");
920                 return RLM_MODULE_FAIL;
921         }
922
923         vp = newlist;
924         for (ci=cf_item_find_next(cs, NULL);
925              ci != NULL;
926              ci=cf_item_find_next(cs, ci)) {
927                 const char *value;
928                 CONF_PAIR *cp;
929                 char buffer[2048];
930
931                 /*
932                  *      This should never happen.
933                  */
934                 if (cf_item_is_section(ci)) {
935                         pairfree(&newlist);
936                         return RLM_MODULE_INVALID;
937                 }
938
939                 cp = cf_itemtopair(ci);
940
941                 value = expand_string(buffer, sizeof(buffer), request,
942                                       cp->value_type, cp->value);
943                 if (!value) {
944                         pairfree(&newlist);
945                         return RLM_MODULE_INVALID;
946                 }
947
948                 /*
949                  *      The VP && CF lists should be in sync.  If they're
950                  *      not, panic.
951                  */
952                 if (vp->flags.do_xlat && !pairparsevalue(vp, value)) {
953                         DEBUG2("ERROR: Failed parsing value \"%s\" for attribute %s: %s",
954                                value, vp->name, librad_errstr);
955                         pairfree(&newlist);
956                         return RLM_MODULE_FAIL;
957                 }
958                 vp->flags.do_xlat = 0;
959                 vp = vp->next;
960         }
961
962         my_pairmove(output_vps, &newlist);
963         pairfree(&newlist);
964
965         return RLM_MODULE_UPDATED;
966 }
967
968