]> git.ipfire.org Git - thirdparty/freeradius-server.git/commitdiff
add fr_pair_delete_by_da_nested()
authorAlan T. DeKok <aland@freeradius.org>
Wed, 13 Sep 2023 16:49:16 +0000 (12:49 -0400)
committerAlan T. DeKok <aland@freeradius.org>
Wed, 13 Sep 2023 16:49:16 +0000 (12:49 -0400)
and use it in server macros.  This change allows the uses of
pair_delete_request() etc. to work properly with nested attributes

src/lib/server/pair.h
src/lib/util/pair.c
src/lib/util/pair.h
src/lib/util/pair_tests.c

index 0a9a608ae8b574439458ac723bb4c4719810504a..ce7a792dde8614223ebd161d0ef6f4b773ef23d5 100644 (file)
@@ -157,8 +157,8 @@ RCSIDH(server_pair_h, "$Id$")
  */
 #define pair_delete(_list, _pair_or_da) \
        _Generic((_pair_or_da), \
-                fr_dict_attr_t const *         : fr_pair_delete_by_da(_list, UNCONST(fr_dict_attr_t *, _pair_or_da)),  \
-                fr_dict_attr_t *               : fr_pair_delete_by_da(_list, UNCONST(fr_dict_attr_t *, _pair_or_da)),  \
+                fr_dict_attr_t const *         : fr_pair_delete_by_da_nested(_list, UNCONST(fr_dict_attr_t *, _pair_or_da)),   \
+                fr_dict_attr_t *               : fr_pair_delete_by_da_nested(_list, UNCONST(fr_dict_attr_t *, _pair_or_da)),   \
                 fr_pair_t *                    : fr_pair_delete(_list, UNCONST(fr_pair_t *, _pair_or_da))              \
        )
 
index c84a077dea883a7d32dbe0dc0e7985588c71f0da..694c4ef62aa64f934b6d48d73e6bf29b635faf99 100644 (file)
@@ -1590,6 +1590,93 @@ int fr_pair_delete_by_da(fr_pair_list_t *list, fr_dict_attr_t const *da)
        return cnt;
 }
 
+/** Delete matching pairs from the specified list
+ *
+ * @param[in,out] list to search for attributes in or delete attributes from.
+ * @param[in] da       to match.
+ * @return
+ *     - >0 the number of pairs deleted.
+ *     - 0 if no pairs were deleted.
+ */
+int fr_pair_delete_by_da_nested(fr_pair_list_t *list, fr_dict_attr_t const *da)
+{
+       int                     cnt = 0;
+       fr_pair_t               *vp;
+       fr_dict_attr_t const    **find;         /* DA currently being looked for */
+       fr_pair_list_t          *cur_list;      /* Current list being searched */
+       fr_da_stack_t           da_stack;
+
+       if (da->depth <= 1) return fr_pair_delete_by_da(list, da);
+
+       if (fr_pair_list_empty(list)) return 0;
+
+       /*
+        *      Similar to fr_pair_find_by_da_nested()
+        */
+       fr_proto_da_stack_build(&da_stack, da);
+       cur_list = list;
+       find = &da_stack.da[0];
+       vp = NULL;
+
+       /*
+        *      Loop over the list at each level until we find a matching da.
+        */
+       while (true) {
+               fr_pair_t       *next;
+
+               fr_assert((*find)->depth <= da->depth);
+
+               /*
+                *      Find a vp which matches a given da.  If found,
+                *      recurse into the child list to find the child
+                *      attribute.
+                *
+                */
+               next = fr_pair_find_by_da(cur_list, vp, *find);
+               if (next) {
+                       /*
+                        *      We've found a match for the requested
+                        *      da - delete it
+                        */
+                       if ((*find) == da) {
+                               do {
+                                       fr_pair_delete(cur_list, next);
+                                       cnt++;
+                               } while ((next = fr_pair_find_by_da(cur_list, vp, *find)) != NULL);
+
+                               return cnt;
+                       }
+
+                       /*
+                        *      Prepare to search the next level.
+                        */
+                       cur_list = &next->vp_group;
+                       find++;
+                       vp = NULL;
+                       continue;
+               }
+
+               /*
+                *      We hit the end of the top-level list.  Therefore we found nothing.
+                */
+               if (cur_list == list) break;
+
+               /*
+                *      We hit the end of *A* list.  Go to the parent
+                *      VP, and then find its list.
+                *
+                *      We still then have to go to the next attribute
+                *      in the parent list, as we've checked all of the
+                *      children of this VP.
+                */
+               find--;
+               vp = fr_pair_list_parent(cur_list);
+               cur_list = fr_pair_parent_list(vp);
+       }
+
+       return fr_pair_delete_by_da(list, da);
+}
+
 /** Delete matching pairs from the specified list
  *
  * @param[in] list     to delete attributes from.
index 6658354325f8b6a068eb421ff783cae157b5e522..16b644fb5e0bb2b5c63b2160092a77f381d2cc7d 100644 (file)
@@ -522,6 +522,8 @@ static inline CC_HINT(nonnull(3,4)) int fr_pair_find_or_append_by_da(TALLOC_CTX
 
 int            fr_pair_delete_by_da(fr_pair_list_t *head, fr_dict_attr_t const *da) CC_HINT(nonnull);
 
+int            fr_pair_delete_by_da_nested(fr_pair_list_t *list, fr_dict_attr_t const *da) CC_HINT(nonnull);
+
 fr_pair_t      *fr_pair_delete(fr_pair_list_t *list, fr_pair_t *vp) CC_HINT(nonnull);
 
 /* functions for FR_TYPE_STRUCTURAL */
index 20e652b96ae7247cf8f88cf90c9227c640b55b32..396e056f7e6ceabde3732fbc2e445a29a7e61cce 100644 (file)
@@ -156,6 +156,33 @@ static void test_fr_pair_afrom_da_nested(void)
        talloc_free(parent);    /* not vp! */
 }
 
+static void test_fr_pair_delete_by_da_nested(void)
+{
+       int             count;
+       fr_pair_t       *vp, *parent = NULL;
+       fr_pair_list_t  local_pairs;
+
+       fr_pair_list_init(&local_pairs);
+
+       TEST_CASE("Allocation using fr_pair_afrom_da_nested");
+       TEST_CHECK((vp = fr_pair_afrom_da_nested(autofree, &local_pairs, fr_dict_attr_test_tlv_string)) != NULL);
+
+       TEST_CHECK(vp && vp->da == fr_dict_attr_test_tlv_string);
+       TEST_MSG("Expected attr(%s) == vp->da->attr(%s)", fr_dict_attr_test_tlv_string->name, vp->da->name);
+
+       TEST_CASE("Deleted nested pair");
+       TEST_CHECK((count = fr_pair_delete_by_da_nested(&local_pairs, fr_dict_attr_test_tlv_string)) == 1);
+
+       TEST_CASE("Top list still has the tlv attribute");
+       parent = fr_pair_find_by_da(&local_pairs, NULL, fr_dict_attr_test_tlv);
+       TEST_ASSERT(parent != NULL);
+
+       TEST_CASE("Parent list does not have the tlv child attribute");
+       TEST_CHECK(fr_pair_find_by_da(&parent->vp_group, NULL, fr_dict_attr_test_tlv_string) == NULL);
+
+       fr_pair_list_free(&local_pairs);
+}
+
 static void test_fr_pair_copy(void)
 {
        fr_pair_t *vp, *copy;
@@ -1405,6 +1432,7 @@ TEST_LIST = {
        { "fr_pair_update_by_da_parent",          test_fr_pair_update_by_da_parent },
        { "fr_pair_delete",                       test_fr_pair_delete },
        { "fr_pair_delete_by_da",                 test_fr_pair_delete_by_da },
+       { "fr_pair_delete_by_da_nested",          test_fr_pair_delete_by_da_nested },
 
        /* Compare */
        { "fr_pair_cmp",                          test_fr_pair_cmp },