+
+ if (retval==TR_FILTER_MATCH)
+ break;
+
+ }
+
+ if (retval==TR_FILTER_MATCH) {
+ /* Matched line ii. Grab its action and constraints. */
+ *out_action = this_fline->action;
+ if (constraints!=NULL) {
+ /* if either constraint is missing, these are no-ops */
+ tr_constraint_add_to_set(constraints, this_fline->realm_cons);
+ tr_constraint_add_to_set(constraints, this_fline->domain_cons);
+ }
+ }
+
+ return retval;
+}
+
+void tr_fspec_free(TR_FSPEC *fspec)
+{
+ talloc_free(fspec);
+}
+
+/**
+ * Helper for tr_fspec_destructor - calls tr_free_name on its first argument
+ *
+ * @param item void pointer to a TR_NAME
+ * @param cookie ignored
+ */
+static void fspec_destruct_helper(void *item, void *cookie)
+{
+ TR_NAME *name = (TR_NAME *) item;
+ tr_free_name(name);
+}
+static int tr_fspec_destructor(void *obj)
+{
+ TR_FSPEC *fspec = talloc_get_type_abort(obj, TR_FSPEC);
+
+ if (fspec->field != NULL)
+ tr_free_name(fspec->field);
+
+ if (fspec->match)
+ tr_list_foreach(fspec->match, fspec_destruct_helper, NULL);
+
+ return 0;
+}
+
+TR_FSPEC *tr_fspec_new(TALLOC_CTX *mem_ctx)
+{
+ TR_FSPEC *fspec = talloc(mem_ctx, TR_FSPEC);
+
+ if (fspec != NULL) {
+ fspec->field = NULL;
+ fspec->match = tr_list_new(fspec);
+ if (fspec->match == NULL) {
+ talloc_free(fspec);
+ return NULL;
+ }
+ talloc_set_destructor((void *)fspec, tr_fspec_destructor);
+ }
+ return fspec;
+}
+
+/* Helper function and cookie structure for finding a match. The helper is called
+ * for every item in the match list, even after a match is found. If a match is found,
+ * match should be pointed to the matching item. If this is not NULL, do not change it
+ * because a match has already been found. */
+struct fspec_match_cookie { TR_NAME *name; TR_NAME *match;};
+static void fspec_match_helper(void *item, void *data)
+{
+ TR_NAME *this_name = (TR_NAME *) item;
+ struct fspec_match_cookie *cookie = (struct fspec_match_cookie *) data;
+ if (cookie->match == NULL) {
+ if (tr_name_prefix_wildcard_match(cookie->name, this_name))
+ cookie->match = this_name;
+ }
+}
+/* returns 1 if the spec matches */
+int tr_fspec_matches(TR_FSPEC *fspec, TR_FILTER_TYPE ftype, TR_FILTER_TARGET *target)
+{
+ struct tr_filter_field_entry *field=NULL;
+ struct fspec_match_cookie cookie = {0};
+
+ if (fspec==NULL)
+ return 0;
+
+ /* Look up how to handle the requested field */
+ field = tr_filter_field_entry(ftype, fspec->field);
+ if (field==NULL) {
+ tr_err("tr_fspec_matches: No entry to handle field %.*s for %*s filter.",
+ fspec->field->len, fspec->field->buf,
+ tr_filter_type_to_string(ftype));
+ return 0;
+ }
+
+ cookie.name = field->get(target);
+ if (cookie.name==NULL)
+ return 0; /* if there's no value, there's no match */
+
+ cookie.match = NULL;
+ tr_list_foreach(fspec->match,
+ fspec_match_helper,
+ &cookie);
+ if (cookie.match) {
+ tr_debug("tr_fspec_matches: Field %.*s value \"%.*s\" matches \"%.*s\" for %s filter.",
+ fspec->field->len, fspec->field->buf,
+ cookie.name->len, cookie.name->buf,
+ cookie.match->len, cookie.match->buf,
+ tr_filter_type_to_string(ftype));
+ } else {
+ tr_debug("tr_fspec_matches: Field %.*s value \"%.*s\" does not match for %s filter.",
+ fspec->field->len, fspec->field->buf,
+ cookie.name->len, cookie.name->buf,
+ tr_filter_type_to_string(ftype));
+ }
+ return (cookie.match != NULL);
+}
+
+void tr_fline_free(TR_FLINE *fline)
+{
+ talloc_free(fline);
+}
+
+TR_FLINE *tr_fline_new(TALLOC_CTX *mem_ctx)
+{
+ TR_FLINE *fl = talloc(mem_ctx, TR_FLINE);
+
+ if (fl != NULL) {
+ fl->action = TR_FILTER_ACTION_UNKNOWN;
+ fl->realm_cons = NULL;
+ fl->domain_cons = NULL;
+ fl->specs = tr_list_new(fl);
+ if (fl->specs == NULL) {
+ talloc_free(fl);
+ return NULL;
+ }