*/
RCSID("$Id$")
-#include <freeradius-devel/server/cond_eval.h>
#include <freeradius-devel/server/cond.h>
+#include <freeradius-devel/server/cond_eval.h>
#include <freeradius-devel/server/module.h>
#include <freeradius-devel/server/paircmp.h>
#include <freeradius-devel/server/regex.h>
+#include <freeradius-devel/server/tmpl_dcursor.h>
#include <freeradius-devel/util/debug.h>
#include <freeradius-devel/util/print.h>
int rcode;
fr_pair_t *vp;
fr_dcursor_t cursor;
- tmpl_pair_cursor_ctx_t cc;
+ tmpl_dcursor_ctx_t cc;
fr_value_box_t *rhs, rhs_cast;
fr_dict_attr_t const *da = NULL;
rhs = NULL; /* shut up clang scan */
fr_value_box_init_null(&rhs_cast);
- for (vp = tmpl_pair_cursor_init(&rcode, request, &cc, &cursor, request, map->rhs);
+ for (vp = tmpl_dcursor_init(&rcode, request, &cc, &cursor, request, map->rhs);
vp;
vp = fr_dcursor_next(&cursor)) {
if (cond_realize_attr(request, &rhs, &rhs_cast, map->rhs, vp, da) < 0) {
if (rcode != 0) break;
}
- tmpl_pair_cursor_clear(&cc);
+ tmpl_dursor_clear(&cc);
return (rcode == 1);
}
fr_pair_t *virt, *vp;
fr_value_box_t *rhs, rhs_cast;
fr_dcursor_t cursor;
- tmpl_pair_cursor_ctx_t cc;
+ tmpl_dcursor_ctx_t cc;
fr_assert(tmpl_is_attr(map->lhs));
fr_assert(tmpl_is_attr(map->rhs));
rhs = NULL; /* shut up clang scan */
fr_value_box_clear(&rhs_cast);
- for (vp = tmpl_pair_cursor_init(&rcode, request, &cc, &cursor, request, map->rhs);
+ for (vp = tmpl_dcursor_init(&rcode, request, &cc, &cursor, request, map->rhs);
vp;
vp = fr_dcursor_next(&cursor)) {
if (cond_realize_attr(request, &rhs, &rhs_cast, map->rhs, vp, NULL) < 0) {
if (rcode != 0) break;
}
- tmpl_pair_cursor_clear(&cc);
+ tmpl_dursor_clear(&cc);
return (rcode == 1);
}
{
fr_pair_t *vp;
fr_dcursor_t cursor;
- tmpl_pair_cursor_ctx_t cc;
+ tmpl_dcursor_ctx_t cc;
fr_assert(!lhs);
- for (vp = tmpl_pair_cursor_init(&rcode, request, &cc, &cursor, request, map->lhs);
+ for (vp = tmpl_dcursor_init(&rcode, request, &cc, &cursor, request, map->lhs);
vp;
vp = fr_dcursor_next(&cursor)) {
fr_value_box_t lhs_cast;
continue;
}
- tmpl_pair_cursor_clear(&cc);
+ tmpl_dursor_clear(&cc);
}
break;
snmp.c \
state.c \
stats.c \
+ tmpl_dcursor.c \
tmpl_eval.c \
tmpl_tokenize.c \
trigger.c \
RCSID("$Id$")
+#include <freeradius-devel/server/cond.h>
#include <freeradius-devel/server/exec.h>
#include <freeradius-devel/server/exec_legacy.h>
#include <freeradius-devel/server/map.h>
#include <freeradius-devel/server/paircmp.h>
-#include <freeradius-devel/server/cond.h>
+#include <freeradius-devel/server/tmpl_dcursor.h>
#include <freeradius-devel/util/debug.h>
#include <freeradius-devel/util/base16.h>
tmpl_request_ref_t request_ref;
tmpl_pair_list_t list_ref;
- tmpl_pair_cursor_ctx_t cc = {};
+ tmpl_dcursor_ctx_t cc = {};
fr_pair_list_init(&src_list);
MAP_VERIFY(map);
* the dst_list and vp pointing to the attribute or the VP
* being NULL (no attribute at that index).
*/
- dst = tmpl_pair_cursor_init(NULL, tmp_ctx, &cc, &dst_list, request, map->lhs);
+ dst = tmpl_dcursor_init(NULL, tmp_ctx, &cc, &dst_list, request, map->lhs);
/*
* The destination is an attribute
*/
* Find out what we need to build and build it
*/
if ((tmpl_extents_find(tmp_ctx, &leaf, &interior, request, map->lhs) < 0) ||
- (tmpl_extents_build_to_leaf(&leaf, &interior, map->lhs) < 0)) {
+ (tmpl_extents_build_to_leaf_parent(&leaf, &interior, map->lhs) < 0)) {
fr_dlist_talloc_free(&leaf);
fr_dlist_talloc_free(&interior);
rcode = -1;
* Find out what we need to build and build it
*/
if ((tmpl_extents_find(tmp_ctx, &leaf, &interior, request, map->lhs) < 0) ||
- (tmpl_extents_build_to_leaf(&leaf, &interior, map->lhs) < 0)) {
+ (tmpl_extents_build_to_leaf_parent(&leaf, &interior, map->lhs) < 0)) {
op_set_error:
fr_dlist_talloc_free(&leaf);
fr_dlist_talloc_free(&interior);
* Find out what we need to build and build it
*/
if ((tmpl_extents_find(tmp_ctx, &leaf, &interior, request, map->lhs) < 0) ||
- (tmpl_extents_build_to_leaf(&leaf, &interior, map->lhs) < 0)) {
+ (tmpl_extents_build_to_leaf_parent(&leaf, &interior, map->lhs) < 0)) {
fr_dlist_talloc_free(&leaf);
fr_dlist_talloc_free(&interior);
rcode = -1;
fr_assert(fr_pair_list_empty(&src_list));
finish:
- tmpl_pair_cursor_clear(&cc);
+ tmpl_dursor_clear(&cc);
talloc_free(tmp_ctx);
return rcode;
}
#include <freeradius-devel/server/exec.h>
#include <freeradius-devel/server/map.h>
#include <freeradius-devel/server/paircmp.h>
-#include <freeradius-devel/util/debug.h>
+#include <freeradius-devel/server/tmpl_dcursor.h>
+#include <freeradius-devel/util/debug.h>
#include <freeradius-devel/util/misc.h>
#include <freeradius-devel/util/pair_legacy.h>
case TMPL_TYPE_ATTR:
{
fr_dcursor_t from;
- tmpl_pair_cursor_ctx_t cc_attr;
+ tmpl_dcursor_ctx_t cc_attr;
fr_pair_t *vp;
fr_value_box_t *n_vb;
int err;
* Check we have pairs to copy *before*
* doing any expensive allocations.
*/
- vp = tmpl_pair_cursor_init(&err, request, &cc_attr, &from, request, mutated->rhs);
+ vp = tmpl_dcursor_init(&err, request, &cc_attr, &from, request, mutated->rhs);
if (!vp) switch (err) {
default:
break;
* we should delete all LHS attributes.
*/
if (mutated->op == T_OP_SET) n = list_mod_delete_afrom_map(ctx, original, mutated);
- tmpl_pair_cursor_clear(&cc_attr);
+ tmpl_dursor_clear(&cc_attr);
goto finish;
case -2: /* No matching list */
case -3: /* No request context */
case -4: /* memory allocation error */
RPEDEBUG("Failed resolving attribute source");
- tmpl_pair_cursor_clear(&cc_attr);
+ tmpl_dursor_clear(&cc_attr);
goto error;
}
n = list_mod_generic_afrom_map(ctx, original, mutated);
if (!n) {
- tmpl_pair_cursor_clear(&cc_attr);
+ tmpl_dursor_clear(&cc_attr);
goto error;
}
attr_error:
fr_dcursor_head(&values);
fr_dcursor_free_list(&values);
- tmpl_pair_cursor_clear(&cc_attr);
+ tmpl_dursor_clear(&cc_attr);
goto error;
}
fr_dcursor_append(&values, n_vb);
} while ((vp = fr_dcursor_next(&from)));
- tmpl_pair_cursor_clear(&cc_attr);
+ tmpl_dursor_clear(&cc_attr);
}
break;
TALLOC_CTX *parent;
fr_dcursor_t list;
- tmpl_pair_cursor_ctx_t cc;
+ tmpl_dcursor_ctx_t cc;
memset(&cc, 0, sizeof(cc));
* the list and vp pointing to the attribute or the VP
* being NULL (no attribute at that index).
*/
- found = tmpl_pair_cursor_init(NULL, request, &cc, &list, request, mod->lhs);
+ found = tmpl_dcursor_init(NULL, request, &cc, &list, request, mod->lhs);
/*
* The destination is an attribute
}
finish:
- tmpl_pair_cursor_clear(&cc);
+ tmpl_dursor_clear(&cc);
return rcode;
}
* functions which can be used to iterate over only the #fr_pair_t that match a
* tmpl_t in a given list.
*
- * @see tmpl_pair_cursor_init
+ * @see tmpl_dcursor_init
* @see tmpl_cursor_next
*
* Or for simplicity, there are functions which wrap the cursor functions, to copy or
///< they ensure the correct parsing rules are applied.
};
-typedef struct tmpl_cursor_ctx_s tmpl_pair_cursor_ctx_t;
-typedef struct tmpl_cursor_nested_s tmpl_cursor_nested_t;
-
-typedef fr_pair_t *(*tmpl_cursor_eval_t)(fr_dlist_head_t *list_head, fr_pair_t *current, tmpl_cursor_nested_t *ns);
-
-/** State for traversing an attribute reference
- *
- */
-struct tmpl_cursor_nested_s {
- fr_dlist_t entry; //!< Entry in the dlist.
- tmpl_attr_t const *ar; //!< Attribute reference this state
- ///< entry is associated with. Mainly for debugging.
- tmpl_cursor_eval_t func; //!< Function used to evaluate this attribute reference.
- TALLOC_CTX *list_ctx; //!< Track where we should be allocating attributes.
-
- union {
- struct {
- fr_dcursor_t cursor; //!< Group traversal is much easier
- ///< but we still need to keep track
- ///< where we are in the list in case
- ///< we're doing counts.
- } group;
-
- struct {
- fr_pair_list_t *list_head; //!< Head of the list we're currently
- ///< iterating over.
- } leaf;
- };
-};
-
-/** Maintains state between cursor calls
- *
- */
-struct tmpl_cursor_ctx_s {
- TALLOC_CTX *ctx; //!< Temporary allocations go here.
- TALLOC_CTX *pool; //!< Temporary pool.
- tmpl_t const *vpt; //!< tmpl we're evaluating.
-
- request_t *request; //!< Result of following the request references.
- fr_pair_list_t *list; //!< List within the request.
-
- tmpl_cursor_nested_t leaf; //!< Pre-allocated leaf state. We always need
- ///< one of these so it doesn't make sense to
- ///< allocate it later.
-
- fr_dlist_head_t nested; //!< Nested state. These are allocated when we
- ///< need to maintain state between multiple
- ///< cursor calls for a particular attribute
- ///< reference.
-};
-
/** Describes the current extents of a pair tree in relation to the tree described by a tmpl_t
*
*/
@code{.c}
static tmpl_t list = tmpl_init_initialiser_list(CURRENT_REQUEST, PAIR_LIST_REQUEST);
fr_dcursor_t cursor;
- tmpl_pair_cursor_ctx_t cc,
+ tmpl_dcursor_ctx_t cc,
fr_pair_t *vp;
// Iterate over all pairs in the request list
- for (vp = tmpl_pair_cursor_init(NULL, &cursor, request, &list);
+ for (vp = tmpl_dcursor_init(NULL, &cursor, request, &list);
vp;
vp = tmpl_cursor_next(&cursor, &list)) {
// Do something
}
- tmpl_pair_cursor_clear(&cc);
+ tmpl_dursor_clear(&cc);
@endcode
*
* @param _request to locate the list in.
* @param _list to set as the target for the template.
- * @see tmpl_pair_cursor_init
+ * @see tmpl_dcursor_init
* @see tmpl_cursor_next
*/
#define tmpl_init_initialiser_list(_request, _list)\
fr_type_t dst_type)
CC_HINT(nonnull (2, 3, 4));
-fr_pair_t *tmpl_pair_cursor_init(int *err, TALLOC_CTX *ctx, tmpl_pair_cursor_ctx_t *cc,
- fr_dcursor_t *cursor, request_t *request,
- tmpl_t const *vpt);
-
-void tmpl_pair_cursor_clear(tmpl_pair_cursor_ctx_t *cc);
-
int tmpl_copy_pairs(TALLOC_CTX *ctx, fr_pair_list_t *out,
request_t *request, tmpl_t const *vpt);
fr_dlist_head_t *leaf, fr_dlist_head_t *interior,
request_t *request, tmpl_t const *vpt);
-int tmpl_extents_build_to_leaf(fr_dlist_head_t *leaf, fr_dlist_head_t *interior,
+int tmpl_extents_build_to_leaf_parent(fr_dlist_head_t *leaf, fr_dlist_head_t *interior,
tmpl_t const *vpt);
void tmpl_extents_debug(fr_dlist_head_t *head);
--- /dev/null
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+/**
+ * $Id$
+ *
+ * @brief #fr_pair_t template functions
+ * @file src/lib/server/tmpl_cursor.c
+ *
+ * @ingroup AVP
+ *
+ * @copyright 2020-2021 Arran Cudbard-Bell <a.cudbardb@freeradius.org>
+ */
+RCSID("$Id$")
+
+#include <freeradius-devel/server/exec.h>
+#include <freeradius-devel/server/exec_legacy.h>
+#include <freeradius-devel/server/tmpl.h>
+#include <freeradius-devel/server/tmpl_dcursor.h>
+#include <freeradius-devel/util/dlist.h>
+#include <freeradius-devel/util/proto.h>
+#include <freeradius-devel/util/value.h>
+#include <freeradius-devel/util/edit.h>
+
+static inline CC_HINT(always_inline)
+void _tmpl_cursor_pool_init(tmpl_dcursor_ctx_t *cc)
+{
+ if (!cc->pool) MEM(cc->pool = talloc_pool(cc->ctx, sizeof(tmpl_dcursor_nested_t) * 5));
+}
+
+/** Traverse a group attribute
+ *
+ * Here we just look for a particular group attribute in the context of its parent
+ *
+ * @param[in] list_head The head of the pair_list being evaluated.
+ * @param[in] current The pair to evaluate.
+ * @param[in] ns Tracks tree position between cursor calls.
+ * @return
+ * - the next matching attribute
+ * - NULL if none found
+ */
+static fr_pair_t *_tmpl_cursor_child_eval(UNUSED fr_dlist_head_t *list_head, UNUSED fr_pair_t *current, tmpl_dcursor_nested_t *ns)
+{
+ fr_pair_t *vp;
+
+ for (vp = fr_dcursor_current(&ns->group.cursor);
+ vp;
+ vp = fr_dcursor_next(&ns->group.cursor)) {
+ if (fr_dict_attr_cmp(ns->ar->ar_da, vp->da) == 0) {
+ fr_dcursor_next(&ns->group.cursor); /* Advance to correct position for next call */
+ return vp;
+ }
+ }
+
+ return NULL;
+}
+
+/** Initialise the evaluation context for traversing a group attribute
+ *
+ */
+static inline CC_HINT(always_inline)
+void _tmpl_cursor_child_init(TALLOC_CTX *list_ctx, fr_pair_list_t *list, tmpl_attr_t const *ar, tmpl_dcursor_ctx_t *cc)
+{
+ tmpl_dcursor_nested_t *ns;
+
+ _tmpl_cursor_pool_init(cc);
+ MEM(ns = talloc(cc->pool, tmpl_dcursor_nested_t));
+ *ns = (tmpl_dcursor_nested_t){
+ .ar = ar,
+ .func = _tmpl_cursor_child_eval,
+ .list_ctx = list_ctx
+ };
+ fr_pair_dcursor_init(&ns->group.cursor, list);
+ fr_dlist_insert_tail(&cc->nested, ns);
+}
+
+/** Find a leaf attribute
+ *
+ * @param[in] list_head The head of the pair_list being evaluated.
+ * @param[in] curr The current attribute to start searching from.
+ * @param[in] ns Tracks tree position between cursor calls.
+ * @return
+ * - the next matching attribute
+ * - NULL if none found
+ */
+static fr_pair_t *_tmpl_cursor_leaf_eval(fr_dlist_head_t *list_head, fr_pair_t *curr, tmpl_dcursor_nested_t *ns)
+{
+ fr_pair_t *vp = curr;
+
+ while (vp) {
+ if (fr_dict_attr_cmp(ns->ar->ar_da, vp->da) == 0) return vp;
+ vp = fr_dlist_next(list_head, vp);
+ }
+
+ return NULL;
+}
+
+/** Initialise the evaluation context for finding a leaf attribute
+ *
+ */
+static inline CC_HINT(always_inline)
+void _tmpl_cursor_leaf_init(TALLOC_CTX *list_ctx, fr_pair_list_t *list, tmpl_attr_t const *ar, tmpl_dcursor_ctx_t *cc)
+{
+ tmpl_dcursor_nested_t *ns = &cc->leaf;
+
+ *ns = (tmpl_dcursor_nested_t){
+ .ar = ar,
+ .func = _tmpl_cursor_leaf_eval,
+ .list_ctx = list_ctx
+ };
+ ns->leaf.list_head = list;
+ fr_dlist_insert_tail(&cc->nested, ns);
+}
+
+/** Stub list eval function until we can remove lists
+ *
+ */
+static fr_pair_t *_tmpl_cursor_list_eval(UNUSED fr_dlist_head_t *list_head, fr_pair_t *curr, UNUSED tmpl_dcursor_nested_t *ns)
+{
+ return curr;
+}
+
+static inline CC_HINT(always_inline)
+void _tmpl_cursor_list_init(TALLOC_CTX *list_ctx, fr_pair_list_t *list, tmpl_attr_t const *ar, tmpl_dcursor_ctx_t *cc)
+{
+ tmpl_dcursor_nested_t *ns;
+
+ ns = &cc->leaf;
+ *ns = (tmpl_dcursor_nested_t){
+ .ar = ar,
+ .func = _tmpl_cursor_list_eval,
+ .list_ctx = list_ctx
+ };
+ ns->leaf.list_head = list;
+ fr_dlist_insert_tail(&cc->nested, ns);
+}
+
+static inline CC_HINT(always_inline) void _tmpl_cursor_common_pop(tmpl_dcursor_ctx_t *cc)
+{
+ tmpl_dcursor_nested_t *ns = fr_dlist_pop_tail(&cc->nested);
+
+ if (ns != &cc->leaf) talloc_free(ns);
+}
+
+/** Evaluates, then, sometimes, pops evaluation contexts from the tmpl stack
+ *
+ * To pop or not to pop is determined by whether evaluating the context again
+ * would/should/could produce another fr_pair_t.
+ *
+ * @param[in] list_head The head of the pair_list being evaluated.
+ * @param[in] curr The pair to evaluate.
+ * @param[in] cc Tracks state between cursor calls.
+ * @return the vp evaluated.
+ */
+static inline CC_HINT(always_inline)
+fr_pair_t *_tmpl_cursor_eval(fr_dlist_head_t *list_head, fr_pair_t *curr, tmpl_dcursor_ctx_t *cc)
+{
+ tmpl_attr_t const *ar;
+ tmpl_dcursor_nested_t *ns;
+ fr_pair_t *iter = curr, *vp;
+
+ ns = fr_dlist_tail(&cc->nested);
+ ar = ns->ar;
+
+ if (ar) switch (ar->ar_num) {
+ /*
+ * Get the first instance
+ */
+ case NUM_ANY:
+ vp = ns->func(list_head, curr, ns);
+ _tmpl_cursor_common_pop(cc);
+ break;
+
+ /*
+ * Get all instances
+ */
+ case NUM_ALL:
+ case NUM_COUNT:
+ all_inst:
+ vp = ns->func(list_head, curr, ns);
+ if (!vp) _tmpl_cursor_common_pop(cc); /* pop only when we're done */
+ break;
+
+ /*
+ * Get the last instance
+ */
+ case NUM_LAST:
+ vp = NULL;
+ while ((iter = ns->func(list_head, iter, ns))) {
+ vp = iter;
+
+ if (!fr_dlist_next(list_head, vp)) break;
+
+ iter = fr_dlist_next(list_head,vp);
+ }
+ _tmpl_cursor_common_pop(cc);
+ break;
+
+ /*
+ * Get the n'th instance
+ */
+ default:
+ {
+ int16_t i = 0;
+
+ for (;;) {
+ vp = ns->func(list_head, iter, ns);
+ if (!vp) break; /* Prev and next at the correct points */
+
+ if (++i > ar->num) break;
+
+ iter = fr_dlist_next(list_head, vp);
+ };
+ _tmpl_cursor_common_pop(cc);
+ }
+ break;
+ } else goto all_inst; /* Used for TMPL_TYPE_LIST */
+
+ return vp;
+}
+
+static inline CC_HINT(always_inline)
+void _tmpl_cursor_pair_init(TALLOC_CTX *list_ctx, fr_pair_list_t *list, tmpl_attr_t const *ar, tmpl_dcursor_ctx_t *cc)
+{
+ if (fr_dlist_next(&cc->vpt->data.attribute.ar, ar)) switch (ar->ar_da->type) {
+ case FR_TYPE_STRUCTURAL:
+ _tmpl_cursor_child_init(list_ctx, list, ar, cc);
+ break;
+
+ default:
+ leaf:
+ _tmpl_cursor_leaf_init(list_ctx, list, ar, cc);
+ break;
+ } else goto leaf;
+}
+
+static void *_tmpl_cursor_next(fr_dlist_head_t *list, void *curr, void *uctx)
+{
+ tmpl_dcursor_ctx_t *cc = uctx;
+ tmpl_t const *vpt = cc->vpt;
+
+ fr_pair_t *vp;
+ fr_pair_list_t *list_head;
+
+ switch (vpt->type) {
+ case TMPL_TYPE_ATTR:
+ {
+ tmpl_attr_t const *ar = NULL;
+ tmpl_dcursor_nested_t *ns = NULL;
+
+ /*
+ * - Continue until there are no evaluation contexts
+ * - Push a evaluation context if evaluating the head of the
+ * stack yields a VP and we're not at the deepest attribute
+ * reference.
+ * - Return if we have a VP and there are no more attribute
+ * references to push, i.e. we're at the deepest attribute
+ * reference.
+ */
+ while ((ns = fr_dlist_tail(&cc->nested))) {
+ ar = ns->ar;
+ vp = _tmpl_cursor_eval(list, curr, cc);
+ if (!vp) continue;
+
+ ar = fr_dlist_next(&vpt->data.attribute.ar, ar);
+ if (ar) {
+ list_head = &vp->vp_group;
+ _tmpl_cursor_pair_init(vp, list_head, ar, cc);
+ curr = fr_pair_list_head(list_head);
+ list = UNCONST(fr_dlist_head_t *, fr_pair_list_order(list_head));
+ continue;
+ }
+
+ return vp;
+ }
+
+ null_result:
+ return NULL;
+ }
+
+ /*
+ * Hacks for evaluating lists
+ * Hopefully this tmpl type goes away soon...
+ */
+ case TMPL_TYPE_LIST:
+ if (!fr_dlist_tail(&cc->nested)) goto null_result; /* end of list */
+
+ vp = _tmpl_cursor_eval(list, curr, cc);
+ if (!vp) goto null_result;
+
+ return vp;
+
+ default:
+ fr_assert(0);
+ }
+
+ return NULL;
+}
+
+/** Initialise a #fr_dcursor_t to the #fr_pair_t specified by a #tmpl_t
+ *
+ * This makes iterating over the one or more #fr_pair_t specified by a #tmpl_t
+ * significantly easier.
+ *
+ * @param[out] err May be NULL if no error code is required.
+ * Will be set to:
+ * - 0 on success.
+ * - -1 if no matching #fr_pair_t could be found.
+ * - -2 if list could not be found (doesn't exist in current #request_t).
+ * - -3 if context could not be found (no parent #request_t available).
+ * @param[in] ctx to make temporary allocations under.
+ * @param[in] cc to initialise. Tracks evaluation state.
+ * Must be explicitly cleared with tmpl_cursor_state_clear
+ * otherwise we will leak memory.
+ * @param[in] cursor to store iterator position.
+ * @param[in] request The current #request_t.
+ * @param[in] vpt specifying the #fr_pair_t type or list to iterate over.
+ * @return
+ * - First #fr_pair_t specified by the #tmpl_t.
+ * - NULL if no matching #fr_pair_t found, and NULL on error.
+ *
+ * @see tmpl_cursor_next
+ */
+fr_pair_t *tmpl_dcursor_init(int *err, TALLOC_CTX *ctx, tmpl_dcursor_ctx_t *cc,
+ fr_dcursor_t *cursor, request_t *request, tmpl_t const *vpt)
+{
+ fr_pair_t *vp = NULL;
+ fr_pair_list_t *list_head;
+ tmpl_request_t *rr = NULL;
+ TALLOC_CTX *list_ctx;
+
+ TMPL_VERIFY(vpt);
+
+ fr_assert(tmpl_is_attr(vpt) || tmpl_is_list(vpt));
+
+ if (err) *err = 0;
+
+ /*
+ * Navigate to the correct request context
+ */
+ while ((rr = fr_dlist_next(&vpt->data.attribute.rr, rr))) {
+ if (tmpl_request_ptr(&request, rr->request) < 0) {
+ if (err) {
+ *err = -3;
+ fr_strerror_printf("Request context \"%s\" not available",
+ fr_table_str_by_value(tmpl_request_ref_table, rr->request, "<INVALID>"));
+ }
+ error:
+ memset(cc, 0, sizeof(*cc)); /* so tmpl_dursor_clear doesn't explode */
+ return NULL;
+ }
+ }
+
+ /*
+ * Get the right list in the specified context
+ */
+ list_head = tmpl_list_head(request, tmpl_list(vpt));
+ if (!list_head) {
+ if (err) {
+ *err = -2;
+ fr_strerror_printf("List \"%s\" not available in this context",
+ fr_table_str_by_value(pair_list_table, tmpl_list(vpt), "<INVALID>"));
+ }
+ goto error;
+ }
+ list_ctx = tmpl_list_ctx(request, tmpl_list(vpt));
+
+ /*
+ * Initialise the temporary cursor context
+ */
+ *cc = (tmpl_dcursor_ctx_t){
+ .vpt = vpt,
+ .ctx = ctx,
+ .request = request,
+ .list = list_head
+ };
+ fr_dlist_init(&cc->nested, tmpl_dcursor_nested_t, entry);
+
+ /*
+ * Prime the stack!
+ */
+ switch (vpt->type) {
+ case TMPL_TYPE_ATTR:
+ _tmpl_cursor_pair_init(list_ctx, cc->list, fr_dlist_head(&vpt->data.attribute.ar), cc);
+ break;
+
+ case TMPL_TYPE_LIST:
+ _tmpl_cursor_list_init(list_ctx, cc->list, fr_dlist_head(&vpt->data.attribute.ar), cc);
+ break;
+
+ default:
+ fr_assert(0);
+ break;
+ }
+
+ /*
+ * Get the first entry from the tmpl
+ */
+ vp = fr_pair_dcursor_iter_init(cursor, list_head, _tmpl_cursor_next, cc);
+ if (!vp) {
+ if (err) {
+ *err = -1;
+ if (tmpl_is_list(vpt)) {
+ fr_strerror_printf("List \"%s\" is empty", vpt->name);
+ } else {
+ fr_strerror_printf("No matching \"%s\" pairs found", tmpl_da(vpt)->name);
+ }
+ }
+ return NULL;
+ }
+
+ return vp;
+}
+
+/** Clear any temporary state allocations
+ *
+ */
+void tmpl_dursor_clear(tmpl_dcursor_ctx_t *cc)
+{
+ if (!fr_dlist_num_elements(&cc->nested)) return;/* Help simplify dealing with unused cursor ctxs */
+
+ fr_dlist_remove(&cc->nested, &cc->leaf); /* Noop if leaf isn't inserted */
+ fr_dlist_talloc_free(&cc->nested);
+
+ /*
+ * Always free the pool because it's allocated when
+ * any nested ctxs are used.
+ */
+ TALLOC_FREE(cc->pool);
+}
+
+
+#define EXTENT_ADD(_out, _ar, _list_ctx, _list) \
+ do { \
+ tmpl_attr_extent_t *_extent; \
+ MEM(_extent = talloc(ctx, tmpl_attr_extent_t)); \
+ *_extent = (tmpl_attr_extent_t){ \
+ .ar = _ar, \
+ .list_ctx = _list_ctx, \
+ .list = _list \
+ }; \
+ fr_dlist_insert_tail(_out, _extent); \
+ } while (0)
+
+/** Determines points where the reference list extends beyond the current pair tree
+ *
+ * If a particular branch in the VP hierarchy is incomplete, i.e. the chain of attribute
+ * refers to nodes deeper than the nodes currently in the tree, then we return the
+ * deepest point node in the tree which matched, and the ar that we failed to evaluate.
+ *
+ * If the reference list resolves to one or more structural pairs, return those as well.
+ *
+ * This function can be used for a number of different operations, but it's most useful
+ * for determining insertion points for new attributes, or determining which attributes
+ * need to be updated.
+ *
+ * @param[in] ctx to allocate. It's recommended to pass a pool with space
+ * for at least five extent structures.
+ * @param[out] existing List of extents we discovered by evaluating all
+ * attribute references. May be NULL.
+ * @param[out] to_build List of extents that need building out, i.e. references
+ * extend beyond pairs. May be NULL.
+ * @param[in] request The current #request_t.
+ * @param[in] vpt specifying the #fr_pair_t type to retrieve or create.
+ * Must be #TMPL_TYPE_ATTR.
+ * @return
+ * - 0 on success a pair was found.
+ * - -2 if list could not be found (doesn't exist in current #request_t).
+ * - -3 if context could not be found (no parent #request_t available).
+ */
+int tmpl_extents_find(TALLOC_CTX *ctx,
+ fr_dlist_head_t *existing, fr_dlist_head_t *to_build,
+ request_t *request, tmpl_t const *vpt)
+{
+ fr_pair_t *curr = NULL;
+ fr_pair_list_t *list_head;
+
+ TALLOC_CTX *list_ctx = NULL;
+
+ tmpl_dcursor_ctx_t cc;
+ tmpl_dcursor_nested_t *ns = NULL;
+
+ tmpl_request_t *rr = NULL;
+ tmpl_attr_t const *ar = NULL;
+
+ TMPL_VERIFY(vpt);
+
+ fr_assert(tmpl_is_attr(vpt) || tmpl_is_list(vpt));
+
+ /*
+ * Navigate to the correct request context
+ */
+ while ((rr = fr_dlist_next(&vpt->data.attribute.rr, rr))) {
+ if (tmpl_request_ptr(&request, rr->request) < 0) {
+ fr_strerror_printf("Request context \"%s\" not available",
+ fr_table_str_by_value(tmpl_request_ref_table, rr->request, "<INVALID>"));
+ return -3;
+ }
+ }
+
+ /*
+ * Get the right list in the specified context
+ */
+ list_head = tmpl_list_head(request, tmpl_list(vpt));
+ if (!list_head) {
+ fr_strerror_printf("List \"%s\" not available in this context",
+ fr_table_str_by_value(pair_list_table, tmpl_list(vpt), "<INVALID>"));
+ return -2;
+ }
+ list_ctx = tmpl_list_ctx(request, tmpl_list(vpt));
+
+ /*
+ * If it's a list, just return the list head
+ */
+ if (vpt->type == TMPL_TYPE_LIST) {
+ do_list:
+ if (existing) EXTENT_ADD(existing, NULL, list_ctx, list_head);
+ return 0;
+ }
+
+ /*
+ * If it's a leaf skip all the expensive
+ * initialisation and just return the list
+ * it's part of.
+ *
+ * This is only needed because lists are
+ * treated specially. Once lists are groups
+ * this can be removed.
+ */
+ ar = fr_dlist_head(&vpt->data.attribute.ar);
+ switch (ar->ar_da->type) {
+ case FR_TYPE_STRUCTURAL:
+ break;
+
+ default:
+ goto do_list;
+ }
+
+ /*
+ * Initialise the temporary cursor context
+ */
+ cc = (tmpl_dcursor_ctx_t){
+ .vpt = vpt,
+ .ctx = ctx,
+ .request = request,
+ .list = list_head
+ };
+ fr_dlist_init(&cc.nested, tmpl_dcursor_nested_t, entry);
+
+ /*
+ * Prime the stack!
+ */
+ _tmpl_cursor_pair_init(list_ctx, cc.list, fr_dlist_head(&vpt->data.attribute.ar), &cc);
+
+ /*
+ * - Continue until there are no evaluation contexts
+ * - Push a evaluation context if evaluating the head of the
+ * stack yields a VP and we're not at the deepest attribute
+ * reference.
+ * - Return if we have a VP and there are no more attribute
+ * references to push, i.e. we're at the deepest attribute
+ * reference.
+ */
+ curr = fr_pair_list_head(list_head);
+ while ((ns = fr_dlist_tail(&cc.nested))) {
+ tmpl_attr_t const *n_ar;
+
+ list_ctx = ns->list_ctx;
+ ar = ns->ar;
+ curr = _tmpl_cursor_eval(&list_head->order, curr, &cc);
+ if (!curr) {
+ /*
+ * References extend beyond current
+ * pair tree.
+ */
+ if (!ar->resolve_only && to_build) EXTENT_ADD(to_build, ar, list_ctx, list_head);
+ continue; /* Rely on _tmpl_cursor_eval popping the stack */
+ }
+
+ /*
+ * Evaluate the next reference
+ */
+ n_ar = fr_dlist_next(&vpt->data.attribute.ar, ar);
+ if (n_ar) {
+ ar = n_ar;
+ list_head = &curr->vp_group;
+ list_ctx = curr; /* Allocations are under the group */
+ _tmpl_cursor_pair_init(list_ctx, list_head, ar, &cc);
+ curr = fr_pair_list_head(list_head);
+ continue;
+ }
+
+ /*
+ * Only reached when we can't find an exiting
+ * part of the pair_root to keep walking.
+ *
+ * VP tree may extend beyond the reference.
+ * If the reference was structural, record this
+ * as an extent.
+ */
+ if (existing) switch (ar->da->type) {
+ case FR_TYPE_STRUCTURAL:
+ EXTENT_ADD(existing, NULL, curr, list_head);
+ break;
+
+ default:
+ break;
+ }
+ }
+
+ return 0;
+}
+
+/** Allocate interior pairs
+ *
+ * Builds out the pair tree to the point where leaf attributes can be added
+ *
+ * @param[out] existing List to add built out attributes to.
+ * @param[in] to_build List to remove attributes from.
+ * @param[in] vpt We are evaluating.
+ * @return
+ * - 0 on success.
+ * - -1 on failure.
+ */
+int tmpl_extents_build_to_leaf_parent(fr_dlist_head_t *existing, fr_dlist_head_t *to_build, tmpl_t const *vpt)
+{
+ tmpl_attr_extent_t *extent = NULL;
+
+ while ((extent = fr_dlist_head(to_build))) {
+ fr_pair_list_t *list;
+ TALLOC_CTX *list_ctx;
+ fr_pair_t *vp;
+ tmpl_attr_t const *ar;
+
+ fr_assert(extent->ar); /* Interior extents MUST contain an ar */
+
+ /*
+ * Try and allocate VPs for the
+ * rest of the attribute references.
+ */
+ for (ar = extent->ar, list = extent->list, list_ctx = extent->list_ctx;
+ ar;
+ ar = fr_dlist_next(&vpt->data.attribute.ar, ar)) {
+ switch (ar->type) {
+ case TMPL_ATTR_TYPE_NORMAL:
+ case TMPL_ATTR_TYPE_UNKNOWN:
+ /*
+ * Don't build leaf attributes
+ */
+ if (!fr_type_is_structural(ar->ar_da->type)) continue;
+
+ MEM(vp = fr_pair_afrom_da(list_ctx, ar->ar_da)); /* Copies unknowns */
+ fr_pair_append(list, vp);
+ list = &vp->vp_group;
+ list_ctx = vp; /* New allocations occur under the VP */
+ break;
+
+ default:
+ fr_assert_fail("references of this type should have been resolved");
+ return -1;
+ }
+ }
+
+ fr_dlist_remove(to_build, extent); /* Do this *before* zeroing the dlist headers */
+ *extent = (tmpl_attr_extent_t){
+ .list = list,
+ .list_ctx = list_ctx
+ };
+ fr_dlist_insert_tail(existing, extent); /* move between in and out */
+ }
+
+ return 0;
+}
+
+void tmpl_extents_debug(fr_dlist_head_t *head)
+{
+ tmpl_attr_extent_t const *extent = NULL;
+ fr_pair_t *vp = NULL;
+
+ for (extent = fr_dlist_head(head);
+ extent;
+ extent = fr_dlist_next(head, extent)) {
+ tmpl_attr_t const *ar = extent->ar;
+ char const *ctx_name;
+
+ if (ar) {
+ FR_FAULT_LOG("extent-interior-attr");
+ tmpl_attr_ref_debug(extent->ar, 0);
+ } else {
+ FR_FAULT_LOG("extent-leaf");
+ }
+
+ ctx_name = talloc_get_name(extent->list_ctx);
+ if (strcmp(ctx_name, "fr_pair_t") == 0) {
+ FR_FAULT_LOG("list_ctx : %p (%s, %s)", extent->list_ctx, ctx_name,
+ ((fr_pair_t *)extent->list_ctx)->da->name);
+ } else {
+ FR_FAULT_LOG("list_ctx : %p (%s)", extent->list_ctx, ctx_name);
+ }
+ FR_FAULT_LOG("list : %p", extent->list);
+ if (fr_pair_list_empty(extent->list)) {
+ FR_FAULT_LOG("list (first) : none (%p)", extent->list);
+ } else {
+ vp = fr_pair_list_head(extent->list);
+ FR_FAULT_LOG("list (first) : %s (%p)", vp->da->name, extent->list);
+ }
+ }
+
+}
--- /dev/null
+#pragma once
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+
+#include <freeradius-devel/server/tmpl.h>
+#include <freeradius-devel/util/dcursor.h>
+
+RCSIDH(tmpl_dcursor_t, "$Id$")
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef struct tmpl_dcursor_ctx_s tmpl_dcursor_ctx_t;
+typedef struct tmpl_dcursor_nested_s tmpl_dcursor_nested_t;
+
+/** Evaluation function for iterating over a given type of attribute
+ *
+ * Currently attributes are divided into structural and leaf attributes,
+ * so we need an evaluation function for each of those.
+ *
+ * @param[in] list_head to evaluate.
+ * @param[in] current position in the list.
+ * @param[in] ns Nested state.
+ */
+typedef fr_pair_t *(*tmpl_dursor_eval_t)(fr_dlist_head_t *list_head, fr_pair_t *current, tmpl_dcursor_nested_t *ns);
+
+/** State for traversing an attribute reference
+ *
+ */
+struct tmpl_dcursor_nested_s {
+ fr_dlist_t entry; //!< Entry in the dlist that forms the evaluation stack.
+ tmpl_attr_t const *ar; //!< Attribute reference this state
+ ///< entry is associated with. Mainly for debugging.
+ tmpl_dursor_eval_t func; //!< Function used to evaluate this attribute reference.
+ TALLOC_CTX *list_ctx; //!< Track where we should be allocating attributes.
+
+ bool seen; //!< Whether we've seen an attribute at this level of
+ ///< evaluation already. This is mainly used where
+ ///< the build cursor is used.
+
+ union {
+ struct {
+ fr_dcursor_t cursor; //!< Group traversal is much easier
+ ///< but we still need to keep track
+ ///< where we are in the list in case
+ ///< we're doing counts.
+ } group;
+
+ struct {
+ fr_pair_list_t *list_head; //!< Head of the list we're currently
+ ///< iterating over.
+ } leaf;
+ };
+};
+
+/** Maintains state between cursor calls
+ *
+ */
+struct tmpl_dcursor_ctx_s {
+ TALLOC_CTX *ctx; //!< Temporary allocations go here.
+ TALLOC_CTX *pool; //!< Temporary pool.
+ tmpl_t const *vpt; //!< tmpl we're evaluating.
+
+ request_t *request; //!< Result of following the request references.
+
+ fr_pair_list_t *list; //!< List within the request.
+
+ fr_dlist_head_t nested; //!< Nested state. These are allocated when we
+ ///< need to maintain state between multiple
+ ///< cursor calls for a particular attribute
+ ///< reference.
+ ///< This forms a stack of tmpl_dcursor_nested_t
+ ///< and tracks where we are in evaluation at
+ ///< all levels.
+
+ tmpl_dcursor_nested_t leaf; //!< Pre-allocated leaf state. We always need
+ ///< one of these so it doesn't make sense to
+ ///< allocate it later.
+};
+
+fr_pair_t *tmpl_dcursor_init(int *err, TALLOC_CTX *ctx, tmpl_dcursor_ctx_t *cc,
+ fr_dcursor_t *cursor, request_t *request,
+ tmpl_t const *vpt);
+
+void tmpl_dursor_clear(tmpl_dcursor_ctx_t *cc);
#include <freeradius-devel/server/exec.h>
#include <freeradius-devel/server/exec_legacy.h>
#include <freeradius-devel/server/tmpl.h>
+#include <freeradius-devel/server/tmpl_dcursor.h>
#include <freeradius-devel/util/dlist.h>
#include <freeradius-devel/util/proto.h>
#include <freeradius-devel/util/value.h>
+#include <freeradius-devel/util/edit.h>
/** Resolve attribute #pair_list_t value to an attribute list.
*
* name couldn't be resolved.
* @return a pointer to the HEAD of a list in the #request_t.
*
- * @see tmpl_pair_cursor_init
+ * @see tmpl_dcursor_init
*/
fr_pair_list_t *tmpl_list_head(request_t *request, tmpl_pair_list_t list)
{
return from_cast.vb_length;
}
-static inline CC_HINT(always_inline)
-void _tmpl_cursor_pool_init(tmpl_pair_cursor_ctx_t *cc)
-{
- if (!cc->pool) MEM(cc->pool = talloc_pool(cc->ctx, sizeof(tmpl_cursor_nested_t) * 5));
-}
-
-/** Traverse a group attribute
- *
- * Here we just look for a particular group attribute in the context of its parent
- *
- * @param[in] list_head The head of the pair_list being evaluated.
- * @param[in] current The pair to evaluate.
- * @param[in] ns Tracks tree position between cursor calls.
- * @return
- * - the next matching attribute
- * - NULL if none found
- */
-static fr_pair_t *_tmpl_cursor_child_eval(UNUSED fr_dlist_head_t *list_head, UNUSED fr_pair_t *current, tmpl_cursor_nested_t *ns)
-{
- fr_pair_t *vp;
-
- for (vp = fr_dcursor_current(&ns->group.cursor);
- vp;
- vp = fr_dcursor_next(&ns->group.cursor)) {
- if (fr_dict_attr_cmp(ns->ar->ar_da, vp->da) == 0) {
- fr_dcursor_next(&ns->group.cursor); /* Advance to correct position for next call */
- return vp;
- }
- }
-
- return NULL;
-}
-
-/** Initialise the evaluation context for traversing a group attribute
- *
- */
-static inline CC_HINT(always_inline)
-void _tmpl_cursor_child_init(TALLOC_CTX *list_ctx, fr_pair_list_t *list, tmpl_attr_t const *ar, tmpl_pair_cursor_ctx_t *cc)
-{
- tmpl_cursor_nested_t *ns;
-
- _tmpl_cursor_pool_init(cc);
- MEM(ns = talloc(cc->pool, tmpl_cursor_nested_t));
- *ns = (tmpl_cursor_nested_t){
- .ar = ar,
- .func = _tmpl_cursor_child_eval,
- .list_ctx = list_ctx
- };
- fr_pair_dcursor_init(&ns->group.cursor, list);
- fr_dlist_insert_tail(&cc->nested, ns);
-}
-
-/** Find a leaf attribute
- *
- * @param[in] list_head The head of the pair_list being evaluated.
- * @param[in] curr The current attribute to start searching from.
- * @param[in] ns Tracks tree position between cursor calls.
- * @return
- * - the next matching attribute
- * - NULL if none found
- */
-static fr_pair_t *_tmpl_cursor_leaf_eval(fr_dlist_head_t *list_head, fr_pair_t *curr, tmpl_cursor_nested_t *ns)
-{
- fr_pair_t *vp = curr;
-
- while (vp) {
- if (fr_dict_attr_cmp(ns->ar->ar_da, vp->da) == 0) return vp;
- vp = fr_dlist_next(list_head, vp);
- }
-
- return NULL;
-}
-
-/** Initialise the evaluation context for finding a leaf attribute
- *
- */
-static inline CC_HINT(always_inline)
-void _tmpl_cursor_leaf_init(TALLOC_CTX *list_ctx, fr_pair_list_t *list, tmpl_attr_t const *ar, tmpl_pair_cursor_ctx_t *cc)
-{
- tmpl_cursor_nested_t *ns = &cc->leaf;
-
- *ns = (tmpl_cursor_nested_t){
- .ar = ar,
- .func = _tmpl_cursor_leaf_eval,
- .list_ctx = list_ctx
- };
- ns->leaf.list_head = list;
- fr_dlist_insert_tail(&cc->nested, ns);
-}
-
-/** Stub list eval function until we can remove lists
- *
- */
-static fr_pair_t *_tmpl_cursor_list_eval(UNUSED fr_dlist_head_t *list_head, fr_pair_t *curr, UNUSED tmpl_cursor_nested_t *ns)
-{
- return curr;
-}
-
-static inline CC_HINT(always_inline)
-void _tmpl_cursor_list_init(TALLOC_CTX *list_ctx, fr_pair_list_t *list, tmpl_attr_t const *ar, tmpl_pair_cursor_ctx_t *cc)
-{
- tmpl_cursor_nested_t *ns;
-
- ns = &cc->leaf;
- *ns = (tmpl_cursor_nested_t){
- .ar = ar,
- .func = _tmpl_cursor_list_eval,
- .list_ctx = list_ctx
- };
- ns->leaf.list_head = list;
- fr_dlist_insert_tail(&cc->nested, ns);
-}
-
-static inline CC_HINT(always_inline) void _tmpl_cursor_eval_pop(tmpl_pair_cursor_ctx_t *cc)
-{
- tmpl_cursor_nested_t *ns = fr_dlist_pop_tail(&cc->nested);
-
- if (ns != &cc->leaf) talloc_free(ns);
-}
-
-/** Evaluates, then, sometimes, pops evaluation contexts from the tmpl stack
- *
- * To pop or not to pop is determined by whether evaluating the context again
- * would/should/could produce another fr_pair_t.
- *
- * @param[in] list_head The head of the pair_list being evaluated.
- * @param[in] curr The pair to evaluate.
- * @param[in] cc Tracks state between cursor calls.
- * @return the vp evaluated.
- */
-static inline CC_HINT(always_inline)
-fr_pair_t *_tmpl_cursor_eval(fr_dlist_head_t *list_head, fr_pair_t *curr, tmpl_pair_cursor_ctx_t *cc)
-{
- tmpl_attr_t const *ar;
- tmpl_cursor_nested_t *ns;
- fr_pair_t *iter = curr, *vp;
-
- ns = fr_dlist_tail(&cc->nested);
- ar = ns->ar;
-
- if (ar) switch (ar->ar_num) {
- /*
- * Get the first instance
- */
- case NUM_ANY:
- vp = ns->func(list_head, curr, ns);
- _tmpl_cursor_eval_pop(cc);
- break;
-
- /*
- * Get all instances
- */
- case NUM_ALL:
- case NUM_COUNT:
- all_inst:
- vp = ns->func(list_head, curr, ns);
- if (!vp) _tmpl_cursor_eval_pop(cc); /* pop only when we're done */
- break;
-
- /*
- * Get the last instance
- */
- case NUM_LAST:
- vp = NULL;
- while ((iter = ns->func(list_head, iter, ns))) {
- vp = iter;
-
- if (!fr_dlist_next(list_head, vp)) break;
-
- iter = fr_dlist_next(list_head,vp);
- }
- _tmpl_cursor_eval_pop(cc);
- break;
-
- /*
- * Get the n'th instance
- */
- default:
- {
- int16_t i = 0;
-
- for (;;) {
- vp = ns->func(list_head, iter, ns);
- if (!vp) break; /* Prev and next at the correct points */
-
- if (++i > ar->num) break;
-
- iter = fr_dlist_next(list_head, vp);
- };
- _tmpl_cursor_eval_pop(cc);
- }
- break;
- } else goto all_inst; /* Used for TMPL_TYPE_LIST */
-
- return vp;
-}
-
-static inline CC_HINT(always_inline)
-void _tmpl_pair_cursor_init(TALLOC_CTX *list_ctx, fr_pair_list_t *list, tmpl_attr_t const *ar, tmpl_pair_cursor_ctx_t *cc)
-{
- if (fr_dlist_next(&cc->vpt->data.attribute.ar, ar)) switch (ar->ar_da->type) {
- case FR_TYPE_STRUCTURAL:
- _tmpl_cursor_child_init(list_ctx, list, ar, cc);
- break;
-
- default:
- leaf:
- _tmpl_cursor_leaf_init(list_ctx, list, ar, cc);
- break;
- } else goto leaf;
-}
-
-static void *_tmpl_cursor_next(fr_dlist_head_t *list, void *curr, void *uctx)
-{
- tmpl_pair_cursor_ctx_t *cc = uctx;
- tmpl_t const *vpt = cc->vpt;
-
- fr_pair_t *vp;
- fr_pair_list_t *list_head;
-
- switch (vpt->type) {
- case TMPL_TYPE_ATTR:
- {
- tmpl_attr_t const *ar = NULL;
- tmpl_cursor_nested_t *ns = NULL;
-
- /*
- * - Continue until there are no evaluation contexts
- * - Push a evaluation context if evaluating the head of the
- * stack yields a VP and we're not at the deepest attribute
- * reference.
- * - Return if we have a VP and there are no more attribute
- * references to push, i.e. we're at the deepest attribute
- * reference.
- */
- while ((ns = fr_dlist_tail(&cc->nested))) {
- ar = ns->ar;
- vp = _tmpl_cursor_eval(list, curr, cc);
- if (!vp) continue;
-
- ar = fr_dlist_next(&vpt->data.attribute.ar, ar);
- if (ar) {
- list_head = &vp->vp_group;
- _tmpl_pair_cursor_init(vp, list_head, ar, cc);
- curr = fr_pair_list_head(list_head);
- list = UNCONST(fr_dlist_head_t *, fr_pair_list_order(list_head));
- continue;
- }
-
- return vp;
- }
-
- null_result:
- return NULL;
- }
-
- /*
- * Hacks for evaluating lists
- * Hopefully this tmpl type goes away soon...
- */
- case TMPL_TYPE_LIST:
- if (!fr_dlist_tail(&cc->nested)) goto null_result; /* end of list */
-
- vp = _tmpl_cursor_eval(list, curr, cc);
- if (!vp) goto null_result;
-
- return vp;
-
- default:
- fr_assert(0);
- }
-
- return NULL;
-}
-
-/** Initialise a #fr_dcursor_t to the #fr_pair_t specified by a #tmpl_t
- *
- * This makes iterating over the one or more #fr_pair_t specified by a #tmpl_t
- * significantly easier.
- *
- * @param[out] err May be NULL if no error code is required.
- * Will be set to:
- * - 0 on success.
- * - -1 if no matching #fr_pair_t could be found.
- * - -2 if list could not be found (doesn't exist in current #request_t).
- * - -3 if context could not be found (no parent #request_t available).
- * @param[in] ctx to make temporary allocations under.
- * @param[in] cc to initialise. Tracks evaluation state.
- * Must be explicitly cleared with tmpl_cursor_state_clear
- * otherwise we will leak memory.
- * @param[in] cursor to store iterator position.
- * @param[in] request The current #request_t.
- * @param[in] vpt specifying the #fr_pair_t type or list to iterate over.
- * @return
- * - First #fr_pair_t specified by the #tmpl_t.
- * - NULL if no matching #fr_pair_t found, and NULL on error.
- *
- * @see tmpl_cursor_next
- */
-fr_pair_t *tmpl_pair_cursor_init(int *err, TALLOC_CTX *ctx, tmpl_pair_cursor_ctx_t *cc,
- fr_dcursor_t *cursor, request_t *request, tmpl_t const *vpt)
-{
- fr_pair_t *vp = NULL;
- fr_pair_list_t *list_head = NULL;
- tmpl_request_t *rr = NULL;
- TALLOC_CTX *list_ctx = NULL;
-
- TMPL_VERIFY(vpt);
-
- fr_assert(tmpl_is_attr(vpt) || tmpl_is_list(vpt));
-
- if (err) *err = 0;
-
- /*
- * Navigate to the correct request context
- */
- while ((rr = fr_dlist_next(&vpt->data.attribute.rr, rr))) {
- if (tmpl_request_ptr(&request, rr->request) < 0) {
- if (err) {
- *err = -3;
- fr_strerror_printf("Request context \"%s\" not available",
- fr_table_str_by_value(tmpl_request_ref_table, rr->request, "<INVALID>"));
- }
- error:
- memset(cc, 0, sizeof(*cc)); /* so tmpl_pair_cursor_clear doesn't explode */
- return NULL;
- }
- }
-
- /*
- * If the rules say "return the list as an attribute",
- * then check for that. But if the first da isn't a
- * list, then default to whatever list ref is given.
- *
- * @todo - for the future, have the tokenizer (or
- * whatever) always add the list head to the list of
- * attribute references.
- */
- if (vpt->rules.list_as_attr) {
- tmpl_attr_t const *ar;
-
- ar = fr_dlist_head(&vpt->data.attribute.ar);
- fr_assert(ar != NULL);
-
- if ((ar->ar_da == request_attr_request) ||
- (ar->ar_da == request_attr_reply) ||
- (ar->ar_da == request_attr_control) ||
- (ar->ar_da == request_attr_state)) {
- list_head = &request->pair_root->vp_group;
- list_ctx = request->pair_root;
- }
- }
-
- if (!list_head) {
- /*
- * Get the right list in the specified context
- */
- list_head = tmpl_list_head(request, tmpl_list(vpt));
- if (!list_head) {
- if (err) {
- *err = -2;
- fr_strerror_printf("List \"%s\" not available in this context",
- fr_table_str_by_value(pair_list_table, tmpl_list(vpt), "<INVALID>"));
- }
- goto error;
- }
- list_ctx = tmpl_list_ctx(request, tmpl_list(vpt));
- }
- fr_assert(list_ctx != NULL);
-
- /*
- * Initialise the temporary cursor context
- */
- *cc = (tmpl_pair_cursor_ctx_t){
- .vpt = vpt,
- .ctx = ctx,
- .request = request,
- .list = list_head
- };
- fr_dlist_init(&cc->nested, tmpl_cursor_nested_t, entry);
-
- /*
- * Prime the stack!
- */
- switch (vpt->type) {
- case TMPL_TYPE_ATTR:
- _tmpl_pair_cursor_init(list_ctx, cc->list, fr_dlist_head(&vpt->data.attribute.ar), cc);
- break;
-
- case TMPL_TYPE_LIST:
- _tmpl_cursor_list_init(list_ctx, cc->list, fr_dlist_head(&vpt->data.attribute.ar), cc);
- break;
-
- default:
- fr_assert(0);
- break;
- }
-
- /*
- * Get the first entry from the tmpl
- */
- vp = fr_pair_dcursor_iter_init(cursor, list_head, _tmpl_cursor_next, cc);
- if (!vp) {
- if (err) {
- *err = -1;
- if (tmpl_is_list(vpt)) {
- fr_strerror_printf("List \"%s\" is empty", vpt->name);
- } else {
- fr_strerror_printf("No matching \"%s\" pairs found", tmpl_da(vpt)->name);
- }
- }
- return NULL;
- }
-
- return vp;
-}
-
-/** Clear any temporary state allocations
- *
- */
-void tmpl_pair_cursor_clear(tmpl_pair_cursor_ctx_t *cc)
-{
- if (!fr_dlist_num_elements(&cc->nested)) return;/* Help simplify dealing with unused cursor ctxs */
-
- fr_dlist_remove(&cc->nested, &cc->leaf); /* Noop if leaf isn't inserted */
- fr_dlist_talloc_free(&cc->nested);
-
- /*
- * Always free the pool because it's allocated when
- * any nested ctxs are used.
- */
- TALLOC_FREE(cc->pool);
-}
-
/** Copy pairs matching a #tmpl_t in the current #request_t
*
* @param ctx to allocate new #fr_pair_t in.
{
fr_pair_t *vp;
fr_dcursor_t from;
- tmpl_pair_cursor_ctx_t cc;
+ tmpl_dcursor_ctx_t cc;
TMPL_VERIFY(vpt);
fr_assert(tmpl_is_attr(vpt) || tmpl_is_list(vpt));
- for (vp = tmpl_pair_cursor_init(&err, NULL, &cc, &from, request, vpt);
+ for (vp = tmpl_dcursor_init(&err, NULL, &cc, &from, request, vpt);
vp;
vp = fr_dcursor_next(&from)) {
vp = fr_pair_copy(ctx, vp);
}
fr_pair_append(out, vp);
}
- tmpl_pair_cursor_clear(&cc);
+ tmpl_dursor_clear(&cc);
return err;
}
{
fr_pair_t *vp;
fr_dcursor_t from;
- tmpl_pair_cursor_ctx_t cc;
+ tmpl_dcursor_ctx_t cc;
TMPL_VERIFY(vpt);
fr_pair_list_free(out);
- for (vp = tmpl_pair_cursor_init(&err, NULL, &cc, &from, request, vpt);
+ for (vp = tmpl_dcursor_init(&err, NULL, &cc, &from, request, vpt);
vp;
vp = fr_dcursor_next(&from)) {
switch (vp->da->type) {
}
}
done:
- tmpl_pair_cursor_clear(&cc);
+ tmpl_dursor_clear(&cc);
return err;
}
int tmpl_find_vp(fr_pair_t **out, request_t *request, tmpl_t const *vpt)
{
fr_dcursor_t cursor;
- tmpl_pair_cursor_ctx_t cc;
+ tmpl_dcursor_ctx_t cc;
fr_pair_t *vp;
int err;
TMPL_VERIFY(vpt);
- vp = tmpl_pair_cursor_init(&err, request, &cc, &cursor, request, vpt);
- tmpl_pair_cursor_clear(&cc);
+ vp = tmpl_dcursor_init(&err, request, &cc, &cursor, request, vpt);
+ tmpl_dursor_clear(&cc);
if (out) *out = vp;
int tmpl_find_or_add_vp(fr_pair_t **out, request_t *request, tmpl_t const *vpt)
{
fr_dcursor_t cursor;
- tmpl_pair_cursor_ctx_t cc;
+ tmpl_dcursor_ctx_t cc;
fr_pair_t *vp;
int err;
*out = NULL;
- vp = tmpl_pair_cursor_init(&err, NULL, &cc, &cursor, request, vpt);
- tmpl_pair_cursor_clear(&cc);
+ vp = tmpl_dcursor_init(&err, NULL, &cc, &cursor, request, vpt);
+ tmpl_dursor_clear(&cc);
switch (err) {
case 0:
return err;
}
}
-
-/** Determines points where the reference list extends beyond the current pair tree
- *
- * If a particular branch in the VP hierarchy is incomplete, i.e. the chain of attribute
- * refers to nodes deeper than the nodes currently in the tree, then we return the
- * deepest point node in the tree which matched, and the ar that we failed to evaluate.
- *
- * If the reference list resolves to one or more structural pairs, return those as well.
- *
- * This function can be used for a number of different operations, but it's most useful
- * for determining insertion points for new attributes, or determining which attributes
- * need to be updated.
- *
- * @param[in] ctx to allocate. It's recommended to pass a pool with space
- * for at least five extent structures.
- * @param[out] leaf List of extents we discovered by evaluating all
- * attribute references. May be NULL.
- * @param[out] interior List of extents that need building out, i.e. references
- * extend beyond pairs. May be NULL.
- * @param[in] request The current #request_t.
- * @param[in] vpt specifying the #fr_pair_t type to retrieve or create.
- * Must be #TMPL_TYPE_ATTR.
- * @return
- * - 1 on success a pair was created.
- * - 0 on success a pair was found.
- * - -1 if a new #fr_pair_t couldn't be found or created.
- * - -2 if list could not be found (doesn't exist in current #request_t).
- * - -3 if context could not be found (no parent #request_t available).
- */
-int tmpl_extents_find(TALLOC_CTX *ctx,
- fr_dlist_head_t *leaf, fr_dlist_head_t *interior,
- request_t *request, tmpl_t const *vpt)
-{
- fr_pair_t *curr = NULL;
- fr_pair_list_t *list_head;
-
- TALLOC_CTX *list_ctx = NULL;
-
- tmpl_pair_cursor_ctx_t cc;
- tmpl_cursor_nested_t *ns = NULL;
-
- tmpl_request_t *rr = NULL;
- tmpl_attr_t const *ar = NULL;
-
- TMPL_VERIFY(vpt);
-
- fr_assert(tmpl_is_attr(vpt) || tmpl_is_list(vpt));
-
-#define EXTENT_ADD(_out, _ar, _list_ctx, _list) \
- do { \
- tmpl_attr_extent_t *_extent; \
- MEM(_extent = talloc(ctx, tmpl_attr_extent_t)); \
- *_extent = (tmpl_attr_extent_t){ \
- .ar = _ar, \
- .list_ctx = _list_ctx, \
- .list = _list \
- }; \
- fr_dlist_insert_tail(_out, _extent); \
- } while (0)
-
- /*
- * Navigate to the correct request context
- */
- while ((rr = fr_dlist_next(&vpt->data.attribute.rr, rr))) {
- if (tmpl_request_ptr(&request, rr->request) < 0) {
- fr_strerror_printf("Request context \"%s\" not available",
- fr_table_str_by_value(tmpl_request_ref_table, rr->request, "<INVALID>"));
- return -3;
- }
- }
-
- /*
- * Get the right list in the specified context
- */
- list_head = tmpl_list_head(request, tmpl_list(vpt));
- if (!list_head) {
- fr_strerror_printf("List \"%s\" not available in this context",
- fr_table_str_by_value(pair_list_table, tmpl_list(vpt), "<INVALID>"));
- return -2;
- }
- list_ctx = tmpl_list_ctx(request, tmpl_list(vpt));
-
- /*
- * If it's a list, just return the list head
- */
- if (vpt->type == TMPL_TYPE_LIST) {
- do_list:
- if (leaf) EXTENT_ADD(leaf, NULL, list_ctx, list_head);
- return 0;
- }
-
- /*
- * If it's a leaf skip all the expensive
- * initialisation and just return the list
- * it's part of.
- *
- * This is only needed because lists are
- * treated specially. Once lists are groups
- * this can be removed.
- */
- ar = fr_dlist_head(&vpt->data.attribute.ar);
- switch (ar->ar_da->type) {
- case FR_TYPE_STRUCTURAL:
- break;
-
- default:
- goto do_list;
- }
-
- /*
- * Initialise the temporary cursor context
- */
- cc = (tmpl_pair_cursor_ctx_t){
- .vpt = vpt,
- .ctx = ctx,
- .request = request,
- .list = list_head
- };
- fr_dlist_init(&cc.nested, tmpl_cursor_nested_t, entry);
-
- /*
- * Prime the stack!
- */
- _tmpl_pair_cursor_init(list_ctx, cc.list, fr_dlist_head(&vpt->data.attribute.ar), &cc);
-
- /*
- * - Continue until there are no evaluation contexts
- * - Push a evaluation context if evaluating the head of the
- * stack yields a VP and we're not at the deepest attribute
- * reference.
- * - Return if we have a VP and there are no more attribute
- * references to push, i.e. we're at the deepest attribute
- * reference.
- */
- curr = fr_pair_list_head(list_head);
- while ((ns = fr_dlist_tail(&cc.nested))) {
- tmpl_attr_t const *n_ar;
-
- list_ctx = ns->list_ctx;
- ar = ns->ar;
- curr = _tmpl_cursor_eval(&list_head->order, curr, &cc);
- if (!curr) {
- /*
- * References extend beyond current
- * pair tree.
- */
- if (!ar->resolve_only && interior) EXTENT_ADD(interior, ar, list_ctx, list_head);
- continue; /* Rely on _tmpl_cursor_eval popping the stack */
- }
-
- /*
- * Evaluate the next reference
- */
- n_ar = fr_dlist_next(&vpt->data.attribute.ar, ar);
- if (n_ar) {
- ar = n_ar;
- list_head = &curr->vp_group;
- list_ctx = curr; /* Allocations are under the group */
- _tmpl_pair_cursor_init(list_ctx, list_head, ar, &cc);
- curr = fr_pair_list_head(list_head);
- continue;
- }
-
- /*
- * VP tree may extend beyond
- * the reference. If the reference
- * was structural, record this as
- * an extent.
- */
- switch (ar->da->type) {
- case FR_TYPE_STRUCTURAL:
- if (leaf) EXTENT_ADD(leaf, NULL, curr, list_head);
- continue;
-
- default:
- break;
- }
- }
-
- return 0;
-}
-
-/** Allocate interior pairs
- *
- * Builds out the pair tree to the point where leaf attributes can be added
- *
- * @param[out] leaf List to add built out attributes to.
- * @param[in] interior List to remove attributes from.
- * @param[in] vpt We are evaluating.
- * @return
- * - 0 on success.
- * - -1 on failure.
- */
-int tmpl_extents_build_to_leaf(fr_dlist_head_t *leaf, fr_dlist_head_t *interior, tmpl_t const *vpt)
-{
- tmpl_attr_extent_t *extent = NULL;
-
- while ((extent = fr_dlist_head(interior))) {
- fr_pair_t *vp;
- fr_pair_list_t *list;
- tmpl_attr_t const *ar;
- TALLOC_CTX *list_ctx = extent->list_ctx;
-
- fr_assert(extent->ar); /* Interior extents MUST contain an ar */
-
- /*
- * Try and allocate VPs for the
- * rest of the attribute references.
- */
- for (ar = extent->ar, list = extent->list;
- ar;
- ar = fr_dlist_next(&vpt->data.attribute.ar, ar)) {
- switch (ar->type) {
- case TMPL_ATTR_TYPE_NORMAL:
- case TMPL_ATTR_TYPE_UNKNOWN:
- /*
- * Don't build leaf attributes
- */
- switch (ar->ar_da->type) {
- case FR_TYPE_STRUCTURAL:
- break;
-
- default:
- continue;
- }
-
- MEM(vp = fr_pair_afrom_da(list_ctx, ar->ar_da)); /* Copies unknowns */
- fr_pair_append(list, vp);
- list = &vp->vp_group;
- list_ctx = vp; /* New allocations occur under the VP */
- break;
-
- default:
- fr_assert_fail("references of this type should have been resolved");
- return -1;
- }
- }
-
- fr_dlist_remove(interior, extent); /* Do this *before* zeroing the dlist headers */
- *extent = (tmpl_attr_extent_t){
- .list = list,
- .list_ctx = list_ctx
- };
- fr_dlist_insert_tail(leaf, extent); /* move between in and out */
- }
-
- return 0;
-}
-
-void tmpl_extents_debug(fr_dlist_head_t *head)
-{
- tmpl_attr_extent_t const *extent = NULL;
- fr_pair_t *vp = NULL;
-
- for (extent = fr_dlist_head(head);
- extent;
- extent = fr_dlist_next(head, extent)) {
- tmpl_attr_t const *ar = extent->ar;
- char const *ctx_name;
-
- if (ar) {
- FR_FAULT_LOG("extent-interior-attr");
- tmpl_attr_ref_debug(extent->ar, 0);
- } else {
- FR_FAULT_LOG("extent-leaf");
- }
-
- ctx_name = talloc_get_name(extent->list_ctx);
- if (strcmp(ctx_name, "fr_pair_t") == 0) {
- FR_FAULT_LOG("list_ctx : %p (%s, %s)", extent->list_ctx, ctx_name,
- ((fr_pair_t *)extent->list_ctx)->da->name);
- } else {
- FR_FAULT_LOG("list_ctx : %p (%s)", extent->list_ctx, ctx_name);
- }
- FR_FAULT_LOG("list : %p", extent->list);
- if (fr_pair_list_empty(extent->list)) {
- FR_FAULT_LOG("list (first) : none (%p)", extent->list);
- } else {
- vp = fr_pair_list_head(extent->list);
- FR_FAULT_LOG("list (first) : %s (%p)", vp->da->name, extent->list);
- }
- }
-
-}
* Find out what we need to build and build it
*/
if ((tmpl_extents_find(state, &leaf, &interior, request, gext->dst) < 0) ||
- (tmpl_extents_build_to_leaf(&leaf, &interior, gext->dst) < 0)) {
+ (tmpl_extents_build_to_leaf_parent(&leaf, &interior, gext->dst) < 0)) {
RPDEBUG("Discarding subrequest attributes - Failed allocating groups");
fr_dlist_talloc_free(&leaf);
fr_dlist_talloc_free(&interior);
*/
#include <freeradius-devel/server/base.h>
+#include <freeradius-devel/server/tmpl_dcursor.h>
#include <freeradius-devel/unlang/xlat_priv.h>
#include <freeradius-devel/io/test_point.h>
{
fr_pair_t *vp;
fr_dcursor_t cursor;
- tmpl_pair_cursor_ctx_t cc;
+ tmpl_dcursor_ctx_t cc;
tmpl_t *vpt;
fr_value_box_t *attr = fr_dlist_head(in);
char const *fmt;
RIDEBUG("Attributes matching \"%s\"", fmt);
RINDENT();
- for (vp = tmpl_pair_cursor_init(NULL, NULL, &cc, &cursor, request, vpt);
+ for (vp = tmpl_dcursor_init(NULL, NULL, &cc, &cursor, request, vpt);
vp;
vp = fr_dcursor_next(&cursor)) {
fr_dict_vendor_t const *vendor;
talloc_free(dst);
}
}
- tmpl_pair_cursor_clear(&cc);
+ tmpl_dursor_clear(&cc);
REXDENT();
talloc_free(vpt);
{
tmpl_t *vpt = NULL;
fr_dcursor_t cursor;
- tmpl_pair_cursor_ctx_t cc;
+ tmpl_dcursor_ctx_t cc;
fr_value_box_t *vb;
fr_value_box_t *in_head = fr_dlist_head(in);
return XLAT_ACTION_FAIL;
}
- for (vp = tmpl_pair_cursor_init(NULL, NULL, &cc, &cursor, request, vpt);
+ for (vp = tmpl_dcursor_init(NULL, NULL, &cc, &cursor, request, vpt);
vp;
vp = fr_dcursor_next(&cursor)) {
fr_token_t op = vp->op;
fr_value_box_bstrdup_buffer_shallow(NULL, vb, NULL, buff, false);
fr_dcursor_append(out, vb);
}
- tmpl_pair_cursor_clear(&cc);
+ tmpl_dursor_clear(&cc);
talloc_free(vpt);
return XLAT_ACTION_DONE;
tmpl_t *vpt;
fr_pair_t *vp;
fr_dcursor_t cursor;
- tmpl_pair_cursor_ctx_t cc;
+ tmpl_dcursor_ctx_t cc;
bool tainted = false;
fr_value_box_t *encoded;
/*
* Loop over the attributes, encoding them.
*/
- for (vp = tmpl_pair_cursor_init(NULL, NULL, &cc, &cursor, request, vpt);
+ for (vp = tmpl_dcursor_init(NULL, NULL, &cc, &cursor, request, vpt);
vp != NULL;
vp = fr_dcursor_next(&cursor)) {
if (vp->da->flags.internal) continue;
len = tp_encode->func(&FR_DBUFF_TMP(p, end), &cursor, encode_ctx);
if (len < 0) {
RPEDEBUG("Protocol encoding failed");
- tmpl_pair_cursor_clear(&cc);
+ tmpl_dursor_clear(&cc);
talloc_free(vpt);
return XLAT_ACTION_FAIL;
}
p += len;
}
- tmpl_pair_cursor_clear(&cc);
+ tmpl_dursor_clear(&cc);
talloc_free(vpt);
/*
RCSID("$Id$")
#include <freeradius-devel/server/base.h>
-#include <freeradius-devel/util/debug.h>
+#include <freeradius-devel/server/tmpl_dcursor.h>
#include <freeradius-devel/unlang/xlat_priv.h>
+#include <freeradius-devel/util/debug.h>
#include <freeradius-devel/unlang/unlang_priv.h> /* Remove when everything uses new xlat API */
fr_value_box_t *value;
fr_dcursor_t cursor;
- tmpl_pair_cursor_ctx_t cc;
+ tmpl_dcursor_ctx_t cc;
xlat_action_t ret = XLAT_ACTION_DONE;
* This allows users to manipulate virtual attributes as if
* they were real ones.
*/
- vp = tmpl_pair_cursor_init(NULL, NULL, &cc, &cursor, request, vpt);
+ vp = tmpl_dcursor_init(NULL, NULL, &cc, &cursor, request, vpt);
/*
* We didn't find the VP in a list, check to see if it's
default:
/*
* The cursor was set to the correct
- * position above by tmpl_pair_cursor_init.
+ * position above by tmpl_dcursor_init.
*/
vp = fr_dcursor_current(&cursor); /* NULLness checked above */
value = fr_value_box_alloc(ctx, vp->data.type, vp->da, vp->data.tainted);
}
done:
- tmpl_pair_cursor_clear(&cc);
+ tmpl_dursor_clear(&cc);
return ret;
}
* inserting pairs in a list, then we find the lowest existing pair,
* and add pairs there.
*
- * The functions tmpl_extents_find() and tmpl_extents_build_to_leaf()
+ * The functions tmpl_extents_find() and tmpl_extents_build_to_leaf_parent()
* should help us figure out where the VPs exist or not.
*
* The overall "update" algorithm is now:
USES_APPLE_DEPRECATED_API
#include <freeradius-devel/server/base.h>
-
#include <freeradius-devel/server/module.h>
+#include <freeradius-devel/server/tmpl_dcursor.h>
#include <freeradius-devel/util/debug.h>
#include <ctype.h>
ssize_t slen;
fr_pair_t *vp;
fr_dcursor_t cursor;
- tmpl_pair_cursor_ctx_t cc;
+ tmpl_dcursor_ctx_t cc;
slen = tmpl_afrom_attr_substr(request, NULL, &vpt,
&FR_SBUFF_IN(p, strlen(p)),
}
x = 0;
- for (i = 0, vp = tmpl_pair_cursor_init(&err, NULL, &cc, &cursor, request, vpt);
+ for (i = 0, vp = tmpl_dcursor_init(&err, NULL, &cc, &cursor, request, vpt);
(i < max) && (vp != NULL);
i++, vp = fr_dcursor_next(&cursor)) {
int64_t y;
RPEDEBUG("Failed converting &%.*s to an integer value", (int) vpt->len,
vpt->name);
error:
- tmpl_pair_cursor_clear(&cc);
+ tmpl_dursor_clear(&cc);
return false;
}
if (value.vb_uint64 > INT64_MAX) {
x += y;
} /* loop over all found VPs */
- tmpl_pair_cursor_clear(&cc);
+ tmpl_dursor_clear(&cc);
if (err != 0) {
RWDEBUG("Can't find &%.*s. Using 0 as operand value", (int)vpt->len, vpt->name);
#include <freeradius-devel/server/base.h>
#include <freeradius-devel/server/exfile.h>
#include <freeradius-devel/server/module.h>
+#include <freeradius-devel/server/tmpl_dcursor.h>
#include <freeradius-devel/util/debug.h>
#include <freeradius-devel/util/perm.h>
{
#define VECTOR_INCREMENT 20
fr_dcursor_t cursor;
- tmpl_pair_cursor_ctx_t cc;
+ tmpl_dcursor_ctx_t cc;
fr_pair_t *vp;
int alloced = VECTOR_INCREMENT, i;
MEM(vector = talloc_array(request, struct iovec, alloced));
- for (vp = tmpl_pair_cursor_init(NULL, NULL, &cc, &cursor, request, vpt_p), i = 0;
+ for (vp = tmpl_dcursor_init(NULL, NULL, &cc, &cursor, request, vpt_p), i = 0;
vp;
vp = fr_dcursor_next(&cursor), i++) {
/* need extra for line terminator */
vector[i].iov_len = inst->delimiter_len;
}
}
- tmpl_pair_cursor_clear(&cc);
+ tmpl_dursor_clear(&cc);
vector_p = vector;
vector_len = i;
}
*/
RCSID("$Id$")
+#include <freeradius-devel/curl/base.h>
#include <freeradius-devel/server/base.h>
+#include <freeradius-devel/server/cf_priv.h>
#include <freeradius-devel/server/module.h>
-#include <freeradius-devel/curl/base.h>
+#include <freeradius-devel/server/tmpl_dcursor.h>
#include <freeradius-devel/util/talloc.h>
-#include <freeradius-devel/server/cf_priv.h>
static fr_dict_t const *dict_radius; /*dictionary for radius protocol*/
static fr_dict_t const *dict_freeradius;
{
request_t *request = ((fr_mail_ctx_t *)uctx)->request;
fr_pair_t *vp;
- tmpl_pair_cursor_ctx_t cc;
+ tmpl_dcursor_ctx_t cc;
int count = 0;
/* Iterate over the VP and add the string value to the curl_slist */
- vp = tmpl_pair_cursor_init(NULL, NULL, &cc, &uctx->cursor, request, tmpl);
+ vp = tmpl_dcursor_init(NULL, NULL, &cc, &uctx->cursor, request, tmpl);
while (vp) {
count += 1;
*out = curl_slist_append(*out, vp->vp_strvalue);
vp = fr_dcursor_next(&uctx->cursor);
}
/* Return the number of elements that were found */
- tmpl_pair_cursor_clear(&cc);
+ tmpl_dursor_clear(&cc);
return count;
}
static ssize_t tmpl_attr_to_sbuff (fr_mail_ctx_t *uctx, fr_sbuff_t *out, tmpl_t const *vpt, char const *delimeter)
{
fr_pair_t *vp;
- tmpl_pair_cursor_ctx_t cc;
+ tmpl_dcursor_ctx_t cc;
ssize_t copied = 0;
/* Loop through the elements to be added to the sbuff */
- vp = tmpl_pair_cursor_init(NULL, NULL, &cc, &uctx->cursor, uctx->request, vpt);
+ vp = tmpl_dcursor_init(NULL, NULL, &cc, &uctx->cursor, uctx->request, vpt);
while (vp) {
copied += fr_sbuff_in_bstrncpy(out, vp->vp_strvalue, vp->vp_length);
vp = fr_dcursor_next(&uctx->cursor);
copied += fr_sbuff_in_strcpy(out, delimeter);
}
}
- tmpl_pair_cursor_clear(&cc);
+ tmpl_dursor_clear(&cc);
return copied;
}
{
fr_pair_t *vp;
request_t *request = uctx->request;
- tmpl_pair_cursor_ctx_t cc;
+ tmpl_dcursor_ctx_t cc;
int attachments_set = 0;
/* Check for any file attachments */
- for( vp = tmpl_pair_cursor_init(NULL, NULL, &cc, &uctx->cursor, request, tmpl);
+ for( vp = tmpl_dcursor_init(NULL, NULL, &cc, &uctx->cursor, request, tmpl);
vp;
vp = fr_dcursor_next(&uctx->cursor)){
if(vp->vp_tainted) {
}
attachments_set += str_to_attachments(uctx, mime, vp->vp_strvalue, vp->vp_length, path_buffer, m);
}
- tmpl_pair_cursor_clear(&cc);
+ tmpl_dursor_clear(&cc);
return attachments_set;
}