diff options
Diffstat (limited to '')
-rw-r--r-- | src/util/dict_union.c | 202 |
1 files changed, 202 insertions, 0 deletions
diff --git a/src/util/dict_union.c b/src/util/dict_union.c new file mode 100644 index 0000000..80df03b --- /dev/null +++ b/src/util/dict_union.c @@ -0,0 +1,202 @@ +/*++ +/* NAME +/* dict_union 3 +/* SUMMARY +/* dictionary manager interface for union of tables +/* SYNOPSIS +/* #include <dict_union.h> +/* +/* DICT *dict_union_open(name, open_flags, dict_flags) +/* const char *name; +/* int open_flags; +/* int dict_flags; +/* DESCRIPTION +/* dict_union_open() opens a sequence of one or more tables. +/* Example: "\fBunionmap:{\fItype_1:name_1, ..., type_n:name_n\fR}". +/* +/* Each "unionmap:" query is given to each table in the specified +/* order. All found results are concatenated, separated by +/* comma. The unionmap table produces no result when all +/* lookup tables return no result. +/* +/* The first and last characters of a "unionmap:" table name +/* must be '{' and '}'. Within these, individual maps are +/* separated with comma or whitespace. +/* +/* The open_flags and dict_flags arguments are passed on to +/* the underlying dictionaries. +/* SEE ALSO +/* dict(3) generic dictionary manager +/* LICENSE +/* .ad +/* .fi +/* The Secure Mailer license must be distributed with this software. +/* AUTHOR(S) +/* Wietse Venema +/* IBM T.J. Watson Research +/* P.O. Box 704 +/* Yorktown Heights, NY 10598, USA +/* +/* Wietse Venema +/* Google, Inc. +/* 111 8th Avenue +/* New York, NY 10011, USA +/*--*/ + +/* System library. */ + +#include <sys_defs.h> +#include <string.h> + +/* Utility library. */ + +#include <msg.h> +#include <mymalloc.h> +#include <htable.h> +#include <dict.h> +#include <dict_union.h> +#include <stringops.h> +#include <vstring.h> + +/* Application-specific. */ + +typedef struct { + DICT dict; /* generic members */ + ARGV *map_union; /* pipelined tables */ + VSTRING *re_buf; /* reply buffer */ +} DICT_UNION; + +#define STR(x) vstring_str(x) + +/* dict_union_lookup - search a bunch of tables and combine the results */ + +static const char *dict_union_lookup(DICT *dict, const char *query) +{ + static const char myname[] = "dict_union_lookup"; + DICT_UNION *dict_union = (DICT_UNION *) dict; + DICT *map; + char **cpp; + char *dict_type_name; + const char *result = 0; + + /* + * After Roel van Meer, postfix-users mailing list, Sept 2014. + */ + VSTRING_RESET(dict_union->re_buf); + for (cpp = dict_union->map_union->argv; (dict_type_name = *cpp) != 0; cpp++) { + if ((map = dict_handle(dict_type_name)) == 0) + msg_panic("%s: dictionary \"%s\" not found", myname, dict_type_name); + if ((result = dict_get(map, query)) != 0) { + if (VSTRING_LEN(dict_union->re_buf) > 0) + VSTRING_ADDCH(dict_union->re_buf, ','); + vstring_strcat(dict_union->re_buf, result); + } else if (map->error != 0) { + DICT_ERR_VAL_RETURN(dict, map->error, 0); + } + } + DICT_ERR_VAL_RETURN(dict, DICT_ERR_NONE, + VSTRING_LEN(dict_union->re_buf) > 0 ? + STR(dict_union->re_buf) : 0); +} + +/* dict_union_close - disassociate from a bunch of tables */ + +static void dict_union_close(DICT *dict) +{ + DICT_UNION *dict_union = (DICT_UNION *) dict; + char **cpp; + char *dict_type_name; + + for (cpp = dict_union->map_union->argv; (dict_type_name = *cpp) != 0; cpp++) + dict_unregister(dict_type_name); + argv_free(dict_union->map_union); + vstring_free(dict_union->re_buf); + dict_free(dict); +} + +/* dict_union_open - open a bunch of tables */ + +DICT *dict_union_open(const char *name, int open_flags, int dict_flags) +{ + static const char myname[] = "dict_union_open"; + DICT_UNION *dict_union; + char *saved_name = 0; + char *dict_type_name; + ARGV *argv = 0; + char **cpp; + DICT *dict; + int match_flags = 0; + struct DICT_OWNER aggr_owner; + size_t len; + + /* + * Clarity first. Let the optimizer worry about redundant code. + */ +#define DICT_UNION_RETURN(x) do { \ + if (saved_name != 0) \ + myfree(saved_name); \ + if (argv != 0) \ + argv_free(argv); \ + return (x); \ + } while (0) + + /* + * Sanity checks. + */ + if (open_flags != O_RDONLY) + DICT_UNION_RETURN(dict_surrogate(DICT_TYPE_UNION, name, + open_flags, dict_flags, + "%s:%s map requires O_RDONLY access mode", + DICT_TYPE_UNION, name)); + + /* + * Split the table name into its constituent parts. + */ + if ((len = balpar(name, CHARS_BRACE)) == 0 || name[len] != 0 + || *(saved_name = mystrndup(name + 1, len - 2)) == 0 + || ((argv = argv_splitq(saved_name, CHARS_COMMA_SP, CHARS_BRACE)), + (argv->argc == 0))) + DICT_UNION_RETURN(dict_surrogate(DICT_TYPE_UNION, name, + open_flags, dict_flags, + "bad syntax: \"%s:%s\"; " + "need \"%s:{type:name...}\"", + DICT_TYPE_UNION, name, + DICT_TYPE_UNION)); + + /* + * The least-trusted table in the set determines the over-all trust + * level. The first table determines the pattern-matching flags. + */ + DICT_OWNER_AGGREGATE_INIT(aggr_owner); + for (cpp = argv->argv; (dict_type_name = *cpp) != 0; cpp++) { + if (msg_verbose) + msg_info("%s: %s", myname, dict_type_name); + if (strchr(dict_type_name, ':') == 0) + DICT_UNION_RETURN(dict_surrogate(DICT_TYPE_UNION, name, + open_flags, dict_flags, + "bad syntax: \"%s:%s\"; " + "need \"%s:{type:name...}\"", + DICT_TYPE_UNION, name, + DICT_TYPE_UNION)); + if ((dict = dict_handle(dict_type_name)) == 0) + dict = dict_open(dict_type_name, open_flags, dict_flags); + dict_register(dict_type_name, dict); + DICT_OWNER_AGGREGATE_UPDATE(aggr_owner, dict->owner); + if (cpp == argv->argv) + match_flags = dict->flags & (DICT_FLAG_FIXED | DICT_FLAG_PATTERN); + } + + /* + * Bundle up the result. + */ + dict_union = + (DICT_UNION *) dict_alloc(DICT_TYPE_UNION, name, sizeof(*dict_union)); + dict_union->dict.lookup = dict_union_lookup; + dict_union->dict.close = dict_union_close; + dict_union->dict.flags = dict_flags | match_flags; + dict_union->dict.owner = aggr_owner; + dict_union->re_buf = vstring_alloc(100); + dict_union->map_union = argv; + argv = 0; + DICT_UNION_RETURN(DICT_DEBUG (&dict_union->dict)); +} |