2 * evaluate.c Evaluate complex conditions
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.
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.
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
20 * Copyright 2007 The FreeRADIUS server project
21 * Copyright 2007 Alan DeKok <aland@deployingradius.com>
26 #include <freeradius-devel/radiusd.h>
27 #include <freeradius-devel/modules.h>
28 #include <freeradius-devel/parser.h>
29 #include <freeradius-devel/rad_assert.h>
35 #ifdef WITH_EVAL_DEBUG
36 #define EVAL_DEBUG(fmt, ...) printf("EVAL: ");printf(fmt, ## __VA_ARGS__);printf("\n");fflush(stdout)
38 static FR_NAME_NUMBER const template_names[] = {
39 { "literal", VPT_TYPE_LITERAL },
40 { "xlat", VPT_TYPE_XLAT },
41 { "attr", VPT_TYPE_ATTR },
42 { "list", VPT_TYPE_LIST },
43 { "regex", VPT_TYPE_REGEX },
44 { "exec", VPT_TYPE_EXEC },
45 { "data", VPT_TYPE_DATA },
46 { "xlat", VPT_TYPE_XLAT_STRUCT },
47 { "regex", VPT_TYPE_REGEX_STRUCT },
51 #define EVAL_DEBUG(...)
54 FR_NAME_NUMBER const modreturn_table[] = {
55 { "reject", RLM_MODULE_REJECT },
56 { "fail", RLM_MODULE_FAIL },
57 { "ok", RLM_MODULE_OK },
58 { "handled", RLM_MODULE_HANDLED },
59 { "invalid", RLM_MODULE_INVALID },
60 { "userlock", RLM_MODULE_USERLOCK },
61 { "notfound", RLM_MODULE_NOTFOUND },
62 { "noop", RLM_MODULE_NOOP },
63 { "updated", RLM_MODULE_UPDATED },
68 static int all_digits(char const *string)
70 char const *p = string;
72 rad_assert(p != NULL);
76 while (isdigit((int) *p)) p++;
81 /** Expand the RHS of a template
83 * @note Length of expanded string can be found with talloc_array_length(*out) - 1
85 * @param out where to write a pointer to the newly allocated buffer.
86 * @param request Current request.
87 * @param vpt to evaluate.
88 * @return -1 on error, else 0.
90 static int radius_expand_tmpl(char **out, REQUEST *request, value_pair_tmpl_t const *vpt)
96 rad_assert(vpt->type != VPT_TYPE_LIST);
99 case VPT_TYPE_LITERAL:
100 EVAL_DEBUG("TMPL LITERAL");
101 *out = talloc_typed_strdup(request, vpt->name);
105 EVAL_DEBUG("TMPL EXEC");
106 *out = talloc_array(request, char, 1024);
107 if (radius_exec_program(request, vpt->name, true, false, *out, 1024, EXEC_TIMEOUT, NULL, NULL) != 0) {
114 EVAL_DEBUG("TMPL REGEX");
115 /* Error in expansion, this is distinct from zero length expansion */
116 if (radius_axlat(out, request, vpt->name, NULL, NULL) < 0) {
123 EVAL_DEBUG("TMPL XLAT");
124 /* Error in expansion, this is distinct from zero length expansion */
125 if (radius_axlat(out, request, vpt->name, NULL, NULL) < 0) {
131 case VPT_TYPE_XLAT_STRUCT:
132 EVAL_DEBUG("TMPL XLAT_STRUCT");
133 /* Error in expansion, this is distinct from zero length expansion */
134 if (radius_axlat_struct(out, request, vpt->vpt_xlat, NULL, NULL) < 0) {
138 RDEBUG2("EXPAND %s", vpt->name); /* xlat_struct doesn't do this */
139 RDEBUG2(" --> %s", *out);
143 EVAL_DEBUG("TMPL ATTR");
144 ret = radius_tmpl_get_vp(&vp, request, vpt);
145 if (ret < 0) return ret;
147 *out = vp_aprint_value(request, vp);
154 case VPT_TYPE_REGEX_STRUCT:
162 EVAL_DEBUG("Expand tmpl --> %s", *out);
166 /** Evaluate a template
168 * @param[in] request the REQUEST
169 * @param[in] modreturn the previous module return code
170 * @param[in] depth of the recursion (only used for debugging)
171 * @param[in] vpt the template to evaluate
172 * @return -1 on error, 0 for "no match", 1 for "match".
174 int radius_evaluate_tmpl(REQUEST *request, int modreturn, UNUSED int depth,
175 value_pair_tmpl_t const *vpt)
182 case VPT_TYPE_LITERAL:
183 modcode = fr_str2int(modreturn_table, vpt->name, RLM_MODULE_UNKNOWN);
184 if (modcode != RLM_MODULE_UNKNOWN) {
185 rcode = (modcode == modreturn);
190 * Else it's a literal string. Empty string is
191 * false, non-empty string is true.
193 * @todo: Maybe also check for digits?
195 * The VPT *doesn't* have a "bare word" type,
196 * which arguably it should.
198 rcode = (vpt->name != '\0');
203 if (radius_tmpl_get_vp(NULL, request, vpt) < 0) {
211 * FIXME: expand the strings
212 * if not empty, return!
214 case VPT_TYPE_XLAT_STRUCT:
217 if (!*vpt->name) return false;
218 rcode = radius_expand_tmpl(&buffer, request, vpt);
220 EVAL_DEBUG("FAIL %d", __LINE__);
223 rcode = (buffer && (*buffer != '\0'));
228 * Can't have a bare ... (/foo/) ...
231 case VPT_TYPE_REGEX_STRUCT:
232 EVAL_DEBUG("FAIL %d", __LINE__);
245 static int do_regex(REQUEST *request, value_pair_map_t const *map)
247 int compare, rcode, ret;
250 regmatch_t rxmatch[REQUEST_MAX_REGEX + 1];
253 * Expand and then compile it.
255 switch (map->src->type) {
257 rcode = radius_expand_tmpl(&rhs, request, map->src);
259 EVAL_DEBUG("FAIL %d", __LINE__);
262 rad_assert(rhs != NULL);
264 compare = regcomp(®, rhs, REG_EXTENDED | (map->src->vpt_iflag ? REG_ICASE : 0));
269 regerror(compare, ®, errbuf, sizeof(errbuf));
270 ERROR("Failed compiling regular expression: %s", errbuf);
272 EVAL_DEBUG("FAIL %d", __LINE__);
279 case VPT_TYPE_REGEX_STRUCT:
280 preg = map->src->vpt_preg;
288 rcode = radius_expand_tmpl(&lhs, request, map->dst);
290 EVAL_DEBUG("FAIL %d", __LINE__);
294 rad_assert(lhs != NULL);
297 * regexec doesn't initialise unused elements
299 memset(&rxmatch, 0, sizeof(rxmatch));
300 compare = regexec(preg, lhs, REQUEST_MAX_REGEX + 1, rxmatch, 0);
301 rad_regcapture(request, compare, lhs, rxmatch);
302 ret = (compare == 0);
306 * regcomp allocs extra memory for the expression, so if the
307 * result wasn't cached we need to free it here.
309 if (preg == ®) regfree(®);
315 * Expand a template to a string, parse it as type of "cast", and
316 * create a VP from the data.
318 static VALUE_PAIR *get_cast_vp(REQUEST *request, value_pair_tmpl_t const *vpt, DICT_ATTR const *cast)
324 vp = pairalloc(request, cast);
325 if (!vp) return NULL;
327 if (vpt->type == VPT_TYPE_DATA) {
328 rad_assert(vp->da->type == vpt->vpt_da->type);
329 pairdatacpy(vp, vpt->vpt_da, vpt->vpt_value, vpt->vpt_length);
333 rcode = radius_expand_tmpl(&str, request, vpt);
339 if (pairparsevalue(vp, str, 0) < 0) {
349 * Copy data from src to dst, where the attributes are of
352 static int do_cast_copy(VALUE_PAIR *dst, VALUE_PAIR const *src)
354 rad_assert(dst->da->type != src->da->type);
356 if (dst->da->type == PW_TYPE_STRING) {
357 dst->vp_strvalue = vp_aprint_value(dst, src);
358 dst->length = strlen(dst->vp_strvalue);
362 if (dst->da->type == PW_TYPE_OCTETS) {
363 if (src->da->type == PW_TYPE_STRING) {
364 pairmemcpy(dst, src->vp_octets, src->length); /* Copy embedded NULLs */
366 pairmemcpy(dst, (uint8_t const *) &src->data, src->length);
371 if (src->da->type == PW_TYPE_STRING) {
372 return pairparsevalue(dst, src->vp_strvalue, 0);
375 if ((src->da->type == PW_TYPE_INTEGER64) &&
376 (dst->da->type == PW_TYPE_ETHERNET)) {
380 i = htonll(src->vp_integer64);
381 memcpy(array, &i, 8);
384 * For OUIs in the DB.
386 if ((array[0] != 0) || (array[1] != 0)) return -1;
388 memcpy(&dst->vp_ether, &array[2], 6);
394 * The attribute we've found has to have a size which is
395 * compatible with the type of the destination cast.
397 if ((src->length < dict_attr_sizes[dst->da->type][0]) ||
398 (src->length > dict_attr_sizes[dst->da->type][1])) {
399 EVAL_DEBUG("Casted attribute is wrong size (%u)", (unsigned int) src->length);
403 if (src->da->type == PW_TYPE_OCTETS) {
404 switch (dst->da->type) {
405 case PW_TYPE_INTEGER64:
406 dst->vp_integer = ntohll(*(uint64_t const *) src->vp_octets);
410 case PW_TYPE_INTEGER:
413 dst->vp_integer = ntohl(*(uint32_t const *) src->vp_octets);
417 dst->vp_integer = ntohs(*(uint16_t const *) src->vp_octets);
421 dst->vp_integer = src->vp_octets[0];
425 memcpy(&dst->data, src->vp_octets, src->length);
429 dst->length = src->length;
434 * Convert host order to network byte order.
436 if ((dst->da->type == PW_TYPE_IPV4_ADDR) &&
437 ((src->da->type == PW_TYPE_INTEGER) ||
438 (src->da->type == PW_TYPE_DATE) ||
439 (src->da->type == PW_TYPE_SIGNED))) {
440 dst->vp_ipaddr = htonl(src->vp_integer);
442 } else if ((src->da->type == PW_TYPE_IPV4_ADDR) &&
443 ((dst->da->type == PW_TYPE_INTEGER) ||
444 (dst->da->type == PW_TYPE_DATE) ||
445 (dst->da->type == PW_TYPE_SIGNED))) {
446 dst->vp_integer = htonl(src->vp_ipaddr);
448 } else { /* they're of the same byte order */
449 memcpy(&dst->data, &src->data, src->length);
452 dst->length = src->length;
460 * @param[in] request the REQUEST
461 * @param[in] modreturn the previous module return code
462 * @param[in] depth of the recursion (only used for debugging)
463 * @param[in] c the condition to evaluate
464 * @return -1 on error, 0 for "no match", 1 for "match".
466 int radius_evaluate_map(REQUEST *request, UNUSED int modreturn, UNUSED int depth,
471 value_pair_map_t *map;
473 rad_assert(c->type == COND_TYPE_MAP);
476 rad_assert(map->dst->type != VPT_TYPE_UNKNOWN);
477 rad_assert(map->src->type != VPT_TYPE_UNKNOWN);
478 rad_assert(map->dst->type != VPT_TYPE_LIST);
479 rad_assert(map->src->type != VPT_TYPE_LIST);
480 rad_assert(map->dst->type != VPT_TYPE_REGEX);
481 rad_assert(map->dst->type != VPT_TYPE_REGEX_STRUCT);
483 EVAL_DEBUG("Map %s ? %s",
484 fr_int2str(template_names, map->dst->type, "???"),
485 fr_int2str(template_names, map->src->type, "???"));
490 if ((map->src->type == VPT_TYPE_REGEX) ||
491 (map->src->type == VPT_TYPE_REGEX_STRUCT)) {
492 rad_assert(map->op == T_OP_REG_EQ);
494 rad_assert(!((map->op == T_OP_REG_EQ) || (map->op == T_OP_REG_NE)));
498 * They're both attributes. Do attribute-specific work.
500 * LHS is DST. RHS is SRC <sigh>
502 if (!c->cast && (map->src->type == VPT_TYPE_ATTR) && (map->dst->type == VPT_TYPE_ATTR)) {
503 VALUE_PAIR *lhs_vp, *rhs_vp;
505 EVAL_DEBUG("ATTR to ATTR");
506 if ((radius_tmpl_get_vp(&lhs_vp, request, map->dst) < 0) ||
507 (radius_tmpl_get_vp(&rhs_vp, request, map->src) < 0)) return false;
509 return paircmp_op(lhs_vp, map->op, rhs_vp);
513 * LHS is a cast. Do type-specific comparisons, as if
514 * the LHS was a real attribute.
517 VALUE_PAIR *lhs_vp, *rhs_vp;
520 * Try to copy data from the VP which is being
521 * casted, instead of printing it to a string and
522 * then re-parsing it.
524 if (map->dst->type == VPT_TYPE_ATTR) {
527 if (radius_tmpl_get_vp(&cast_vp, request, map->dst) < 0) return false;
529 lhs_vp = pairalloc(request, c->cast);
530 if (!lhs_vp) return false;
533 * In a separate function for clarity
535 if (do_cast_copy(lhs_vp, cast_vp) < 0) {
541 lhs_vp = get_cast_vp(request, map->dst, c->cast);
543 if (!lhs_vp) return false;
546 * Get either a real VP, or parse the RHS into a
547 * VP, and return that.
549 if (map->src->type == VPT_TYPE_ATTR) {
550 if (radius_tmpl_get_vp(&rhs_vp, request, map->src) < 0) return false;
552 rhs_vp = get_cast_vp(request, map->src, c->cast);
555 if (!rhs_vp) return false;
557 EVAL_DEBUG("CAST to %s",
558 fr_int2str(dict_attr_types,
559 c->cast->type, "?Unknown?"));
561 rcode = paircmp_op(lhs_vp, map->op, rhs_vp);
563 if (map->src->type != VPT_TYPE_ATTR) {
570 * Might be a virtual comparison
572 if ((map->dst->type == VPT_TYPE_ATTR) &&
573 (map->src->type != VPT_TYPE_REGEX) &&
574 (map->src->type != VPT_TYPE_REGEX_STRUCT) &&
575 (c->pass2_fixup == PASS2_PAIRCOMPARE)) {
579 EVAL_DEBUG("virtual ATTR to DATA");
581 lhs_vp = get_cast_vp(request, map->src, map->dst->vpt_da);
582 if (!lhs_vp) return false;
585 * paircompare requires the operator be set for the
588 lhs_vp->op = map->op;
589 ret = paircompare(request, request->packet->vps, lhs_vp, NULL);
596 rad_assert(c->pass2_fixup != PASS2_PAIRCOMPARE);
599 * RHS has been pre-parsed into binary data. Go check
602 if ((map->dst->type == VPT_TYPE_ATTR) &&
603 (map->src->type == VPT_TYPE_DATA)) {
604 VALUE_PAIR *lhs_vp, *rhs_vp;
606 EVAL_DEBUG("ATTR to DATA");
608 if (radius_tmpl_get_vp(&lhs_vp, request, map->dst) < 0) return false;
610 rhs_vp = get_cast_vp(request, map->src, map->dst->vpt_da);
611 if (!rhs_vp) return false;
613 #ifdef WITH_EVAL_DEBUG
618 rcode = paircmp_op(lhs_vp, map->op, rhs_vp);
623 rad_assert(map->src->type != VPT_TYPE_DATA);
624 rad_assert(map->dst->type != VPT_TYPE_DATA);
628 * Parse regular expressions.
630 if ((map->src->type == VPT_TYPE_REGEX) ||
631 (map->src->type == VPT_TYPE_REGEX_STRUCT)) {
632 return do_regex(request, map);
637 * The RHS now needs to be expanded into a string.
639 rcode = radius_expand_tmpl(&rhs, request, map->src);
641 EVAL_DEBUG("FAIL %d", __LINE__);
644 rad_assert(rhs != NULL);
649 * Parse the RHS to be the same DA as the LHS. do
650 * comparisons. So long as it's not a regex, which does
651 * string comparisons.
653 * The LHS may be a virtual attribute, too.
655 if (map->dst->type == VPT_TYPE_ATTR) {
656 VALUE_PAIR *lhs_vp, *rhs_vp;
658 EVAL_DEBUG("ATTR to non-REGEX");
661 * No LHS means no match
663 if (radius_tmpl_get_vp(&lhs_vp, request, map->dst) < 0) {
665 * Not a real attr: might be a dynamic comparison.
667 if ((map->dst->type == VPT_TYPE_ATTR) &&
668 (map->dst->vpt_da->vendor == 0) &&
669 radius_find_compare(map->dst->vpt_da)) {
670 rhs_vp = pairalloc(request, map->dst->vpt_da);
671 rad_assert(rhs_vp != NULL);
672 if (pairparsevalue(rhs_vp, rhs, 0) < 0) {
674 EVAL_DEBUG("FAIL %d", __LINE__);
679 rcode = (radius_callback_compare(request, NULL, rhs_vp, NULL, NULL) == 0);
690 rhs_vp = pairalloc(request, map->dst->vpt_da);
691 rad_assert(rhs_vp != NULL);
692 if (pairparsevalue(rhs_vp, rhs, 0) < 0) {
695 EVAL_DEBUG("FAIL %d", __LINE__);
699 rcode = paircmp_op(lhs_vp, map->op, rhs_vp);
706 * The LHS is a string. Expand it.
708 rcode = radius_expand_tmpl(&lhs, request, map->dst);
710 EVAL_DEBUG("FAIL %d", __LINE__);
713 rad_assert(lhs != NULL);
715 EVAL_DEBUG("LHS is %s", lhs);
718 * Loop over the string, doing comparisons
720 if (all_digits(lhs) && all_digits(rhs)) {
723 lint = strtoul(lhs, NULL, 0);
724 rint = strtoul(rhs, NULL, 0);
730 return (lint == rint);
733 return (lint != rint);
736 return (lint < rint);
739 return (lint > rint);
742 return (lint <= rint);
745 return (lint >= rint);
752 rad_assert(lhs != NULL);
753 rad_assert(rhs != NULL);
755 rcode = strcmp(lhs, rhs);
783 EVAL_DEBUG("FAIL %d", __LINE__);
788 /** Evaluate a fr_cond_t;
790 * @param[in] request the REQUEST
791 * @param[in] modreturn the previous module return code
792 * @param[in] depth of the recursion (only used for debugging)
793 * @param[in] c the condition to evaluate
794 * @return -1 on error, 0 for "no match", 1 for "match".
796 int radius_evaluate_cond(REQUEST *request, int modreturn, int depth,
800 #ifdef WITH_EVAL_DEBUG
803 fr_cond_sprint(buffer, sizeof(buffer), c);
804 EVAL_DEBUG("%s", buffer);
809 case COND_TYPE_EXISTS:
810 rcode = radius_evaluate_tmpl(request, modreturn, depth, c->data.vpt);
814 rcode = radius_evaluate_map(request, modreturn, depth, c);
817 case COND_TYPE_CHILD:
818 rcode = radius_evaluate_cond(request, modreturn, depth + 1, c->data.child);
825 case COND_TYPE_FALSE:
829 EVAL_DEBUG("FAIL %d", __LINE__);
833 if (rcode < 0) return rcode;
835 if (c->negate) rcode = !rcode;
840 * FALSE && ... = FALSE
842 if (!rcode && (c->next_op == COND_AND)) return false;
847 if (rcode && (c->next_op == COND_OR)) return true;
853 EVAL_DEBUG("FAIL %d", __LINE__);
861 * The pairmove() function in src/lib/valuepair.c does all sorts of
862 * extra magic that we don't want here.
864 * FIXME: integrate this with the code calling it, so that we
865 * only paircopy() those attributes that we're really going to
868 void radius_pairmove(REQUEST *request, VALUE_PAIR **to, VALUE_PAIR *from, bool do_xlat)
870 int i, j, count, from_count, to_count, tailto;
872 VALUE_PAIR *vp, *next, **last;
873 VALUE_PAIR **from_list, **to_list;
875 REQUEST *fixup = NULL;
877 if (!request) return;
880 * Set up arrays for editing, to remove some of the
881 * O(N^2) dependencies. This also makes it easier to
882 * insert and remove attributes.
884 * It also means that the operators apply ONLY to the
885 * attributes in the original list. With the previous
886 * implementation of pairmove(), adding two attributes
887 * via "+=" and then "=" would mean that the second one
888 * wasn't added, because of the existence of the first
889 * one in the "to" list. This implementation doesn't
892 * Also, the previous implementation did NOT implement
893 * "-=" correctly. If two of the same attributes existed
894 * in the "to" list, and you tried to subtract something
895 * matching the *second* value, then the pairdelete()
896 * function was called, and the *all* attributes of that
897 * number were deleted. With this implementation, only
898 * the matching attributes are deleted.
901 for (vp = fr_cursor_init(&cursor, &from); vp; vp = fr_cursor_next(&cursor)) count++;
902 from_list = rad_malloc(sizeof(*from_list) * count);
904 for (vp = fr_cursor_init(&cursor, to); vp; vp = fr_cursor_next(&cursor)) count++;
905 to_list = rad_malloc(sizeof(*to_list) * count);
908 * Move the lists to the arrays, and break the list
912 for (vp = from; vp != NULL; vp = next) {
914 from_list[from_count++] = vp;
919 for (vp = *to; vp != NULL; vp = next) {
921 to_list[to_count++] = vp;
925 edited = rad_malloc(sizeof(*edited) * to_count);
926 memset(edited, 0, sizeof(*edited) * to_count);
928 RDEBUG4("::: FROM %d TO %d MAX %d", from_count, to_count, count);
931 * Now that we have the lists initialized, start working
934 for (i = 0; i < from_count; i++) {
937 RDEBUG4("::: Examining %s", from_list[i]->da->name);
939 if (do_xlat) radius_xlat_do(request, from_list[i]);
942 * Attribute should be appended, OR the "to" list
943 * is empty, and we're supposed to replace or
944 * "add if not existing".
946 if (from_list[i]->op == T_OP_ADD) goto append;
949 for (j = 0; j < to_count; j++) {
950 if (edited[j] || !to_list[j] || !from_list[i]) continue;
953 * Attributes aren't the same, skip them.
955 if (from_list[i]->da != to_list[j]->da) {
960 * We don't use a "switch" statement here
961 * because we want to break out of the
962 * "for" loop over 'j' in most cases.
966 * Over-write the FIRST instance of the
967 * matching attribute name. We free the
968 * one in the "to" list, and move over
969 * the one in the "from" list.
971 if (from_list[i]->op == T_OP_SET) {
972 RDEBUG4("::: OVERWRITING %s FROM %d TO %d",
973 to_list[j]->da->name, i, j);
974 pairfree(&to_list[j]);
975 to_list[j] = from_list[i];
982 * Add the attribute only if it does not
983 * exist... but it exists, so we stop
986 if (from_list[i]->op == T_OP_EQ) {
992 * Delete every attribute, independent
995 if (from_list[i]->op == T_OP_CMP_FALSE) {
1000 * Delete all matching attributes from
1003 if ((from_list[i]->op == T_OP_SUB) ||
1004 (from_list[i]->op == T_OP_CMP_EQ) ||
1005 (from_list[i]->op == T_OP_LE) ||
1006 (from_list[i]->op == T_OP_GE)) {
1008 int old_op = from_list[i]->op;
1011 * Check for equality.
1013 from_list[i]->op = T_OP_CMP_EQ;
1016 * If equal, delete the one in
1019 rcode = radius_compare_vps(NULL, from_list[i],
1022 * We may want to do more
1023 * subtractions, so we re-set the
1024 * operator back to it's original
1027 from_list[i]->op = old_op;
1031 if (rcode != 0) goto delete;
1037 RDEBUG4("::: DELETING %s FROM %d TO %d",
1038 from_list[i]->da->name, i, j);
1039 pairfree(&to_list[j]);
1045 * Enforce <=. If it's
1050 RDEBUG4("::: REPLACING %s FROM %d TO %d",
1051 from_list[i]->da->name, i, j);
1052 pairfree(&to_list[j]);
1053 to_list[j] = from_list[i];
1054 from_list[i] = NULL;
1061 RDEBUG4("::: REPLACING %s FROM %d TO %d",
1062 from_list[i]->da->name, i, j);
1063 pairfree(&to_list[j]);
1064 to_list[j] = from_list[i];
1065 from_list[i] = NULL;
1074 rad_assert(0 == 1); /* panic! */
1078 * We were asked to add it if it didn't exist,
1079 * and it doesn't exist. Move it over to the
1080 * tail of the "to" list, UNLESS it was already
1081 * moved by another operator.
1083 if (!found && from_list[i]) {
1084 if ((from_list[i]->op == T_OP_EQ) ||
1085 (from_list[i]->op == T_OP_LE) ||
1086 (from_list[i]->op == T_OP_GE) ||
1087 (from_list[i]->op == T_OP_SET)) {
1089 RDEBUG4("::: APPENDING %s FROM %d TO %d",
1090 from_list[i]->da->name, i, tailto);
1091 to_list[tailto++] = from_list[i];
1092 from_list[i] = NULL;
1098 * Delete attributes in the "from" list.
1100 for (i = 0; i < from_count; i++) {
1101 if (!from_list[i]) continue;
1103 pairfree(&from_list[i]);
1107 RDEBUG4("::: TO in %d out %d", to_count, tailto);
1110 * Re-chain the "to" list.
1115 if (to == &request->packet->vps) {
1117 } else if (request->parent && (to == &request->parent->packet->vps)) {
1118 fixup = request->parent;
1121 fixup->username = NULL;
1122 fixup->password = NULL;
1125 for (i = 0; i < tailto; i++) {
1126 if (!to_list[i]) continue;
1129 RDEBUG4("::: to[%d] = %s", i, vp->da->name);
1132 * Mash the operator to a simple '='. The
1133 * operators in the "to" list aren't used for
1134 * anything. BUT they're used in the "detail"
1135 * file and debug output, where we don't want to
1136 * see the operators.
1141 * Fix dumb cache issues
1143 if (fixup && !vp->da->vendor) {
1144 if ((vp->da->attr == PW_USER_NAME) &&
1146 fixup->username = vp;
1148 } else if (vp->da->attr == PW_STRIPPED_USER_NAME) {
1149 fixup->username = vp;
1151 } else if (vp->da->attr == PW_USER_PASSWORD) {
1152 fixup->password = vp;
1157 last = &(*last)->next;
1160 rad_assert(request->packet != NULL);