1 /* $NetBSD: dict_union.c,v 1.2 2017/02/14 01:16:49 christos Exp $ */ 2 3 /*++ 4 /* NAME 5 /* dict_union 3 6 /* SUMMARY 7 /* dictionary manager interface for union of tables 8 /* SYNOPSIS 9 /* #include <dict_union.h> 10 /* 11 /* DICT *dict_union_open(name, open_flags, dict_flags) 12 /* const char *name; 13 /* int open_flags; 14 /* int dict_flags; 15 /* DESCRIPTION 16 /* dict_union_open() opens a sequence of one or more tables. 17 /* Example: "\fBunionmap:{\fItype_1:name_1, ..., type_n:name_n\fR}". 18 /* 19 /* Each "unionmap:" query is given to each table in the specified 20 /* order. All found results are concatenated, separated by 21 /* comma. The unionmap table produces no result when all 22 /* lookup tables return no result. 23 /* 24 /* The first and last characters of a "unionmap:" table name 25 /* must be '{' and '}'. Within these, individual maps are 26 /* separated with comma or whitespace. 27 /* 28 /* The open_flags and dict_flags arguments are passed on to 29 /* the underlying dictionaries. 30 /* SEE ALSO 31 /* dict(3) generic dictionary manager 32 /* LICENSE 33 /* .ad 34 /* .fi 35 /* The Secure Mailer license must be distributed with this software. 36 /* AUTHOR(S) 37 /* Wietse Venema 38 /* IBM T.J. Watson Research 39 /* P.O. Box 704 40 /* Yorktown Heights, NY 10598, USA 41 /*--*/ 42 43 /* System library. */ 44 45 #include <sys_defs.h> 46 #include <string.h> 47 48 /* Utility library. */ 49 50 #include <msg.h> 51 #include <mymalloc.h> 52 #include <htable.h> 53 #include <dict.h> 54 #include <dict_union.h> 55 #include <stringops.h> 56 #include <vstring.h> 57 58 /* Application-specific. */ 59 60 typedef struct { 61 DICT dict; /* generic members */ 62 ARGV *map_union; /* pipelined tables */ 63 VSTRING *re_buf; /* reply buffer */ 64 } DICT_UNION; 65 66 #define STR(x) vstring_str(x) 67 68 /* dict_union_lookup - search a bunch of tables and combine the results */ 69 70 static const char *dict_union_lookup(DICT *dict, const char *query) 71 { 72 static const char myname[] = "dict_union_lookup"; 73 DICT_UNION *dict_union = (DICT_UNION *) dict; 74 DICT *map; 75 char **cpp; 76 char *dict_type_name; 77 const char *result = 0; 78 79 /* 80 * After Roel van Meer, postfix-users mailing list, Sept 2014. 81 */ 82 VSTRING_RESET(dict_union->re_buf); 83 for (cpp = dict_union->map_union->argv; (dict_type_name = *cpp) != 0; cpp++) { 84 if ((map = dict_handle(dict_type_name)) == 0) 85 msg_panic("%s: dictionary \"%s\" not found", myname, dict_type_name); 86 if ((result = dict_get(map, query)) != 0) { 87 if (VSTRING_LEN(dict_union->re_buf) > 0) 88 VSTRING_ADDCH(dict_union->re_buf, ','); 89 vstring_strcat(dict_union->re_buf, result); 90 } else if (map->error != 0) { 91 DICT_ERR_VAL_RETURN(dict, map->error, 0); 92 } 93 } 94 DICT_ERR_VAL_RETURN(dict, DICT_ERR_NONE, 95 VSTRING_LEN(dict_union->re_buf) > 0 ? 96 STR(dict_union->re_buf) : 0); 97 } 98 99 /* dict_union_close - disassociate from a bunch of tables */ 100 101 static void dict_union_close(DICT *dict) 102 { 103 DICT_UNION *dict_union = (DICT_UNION *) dict; 104 char **cpp; 105 char *dict_type_name; 106 107 for (cpp = dict_union->map_union->argv; (dict_type_name = *cpp) != 0; cpp++) 108 dict_unregister(dict_type_name); 109 argv_free(dict_union->map_union); 110 vstring_free(dict_union->re_buf); 111 dict_free(dict); 112 } 113 114 /* dict_union_open - open a bunch of tables */ 115 116 DICT *dict_union_open(const char *name, int open_flags, int dict_flags) 117 { 118 static const char myname[] = "dict_union_open"; 119 DICT_UNION *dict_union; 120 char *saved_name = 0; 121 char *dict_type_name; 122 ARGV *argv = 0; 123 char **cpp; 124 DICT *dict; 125 int match_flags = 0; 126 struct DICT_OWNER aggr_owner; 127 size_t len; 128 129 /* 130 * Clarity first. Let the optimizer worry about redundant code. 131 */ 132 #define DICT_UNION_RETURN(x) do { \ 133 if (saved_name != 0) \ 134 myfree(saved_name); \ 135 if (argv != 0) \ 136 argv_free(argv); \ 137 return (x); \ 138 } while (0) 139 140 /* 141 * Sanity checks. 142 */ 143 if (open_flags != O_RDONLY) 144 DICT_UNION_RETURN(dict_surrogate(DICT_TYPE_UNION, name, 145 open_flags, dict_flags, 146 "%s:%s map requires O_RDONLY access mode", 147 DICT_TYPE_UNION, name)); 148 149 /* 150 * Split the table name into its constituent parts. 151 */ 152 if ((len = balpar(name, CHARS_BRACE)) == 0 || name[len] != 0 153 || *(saved_name = mystrndup(name + 1, len - 2)) == 0 154 || ((argv = argv_splitq(saved_name, CHARS_COMMA_SP, CHARS_BRACE)), 155 (argv->argc == 0))) 156 DICT_UNION_RETURN(dict_surrogate(DICT_TYPE_UNION, name, 157 open_flags, dict_flags, 158 "bad syntax: \"%s:%s\"; " 159 "need \"%s:{type:name...}\"", 160 DICT_TYPE_UNION, name, 161 DICT_TYPE_UNION)); 162 163 /* 164 * The least-trusted table in the set determines the over-all trust 165 * level. The first table determines the pattern-matching flags. 166 */ 167 DICT_OWNER_AGGREGATE_INIT(aggr_owner); 168 for (cpp = argv->argv; (dict_type_name = *cpp) != 0; cpp++) { 169 if (msg_verbose) 170 msg_info("%s: %s", myname, dict_type_name); 171 if (strchr(dict_type_name, ':') == 0) 172 DICT_UNION_RETURN(dict_surrogate(DICT_TYPE_UNION, name, 173 open_flags, dict_flags, 174 "bad syntax: \"%s:%s\"; " 175 "need \"%s:{type:name...}\"", 176 DICT_TYPE_UNION, name, 177 DICT_TYPE_UNION)); 178 if ((dict = dict_handle(dict_type_name)) == 0) 179 dict = dict_open(dict_type_name, open_flags, dict_flags); 180 dict_register(dict_type_name, dict); 181 DICT_OWNER_AGGREGATE_UPDATE(aggr_owner, dict->owner); 182 if (cpp == argv->argv) 183 match_flags = dict->flags & (DICT_FLAG_FIXED | DICT_FLAG_PATTERN); 184 } 185 186 /* 187 * Bundle up the result. 188 */ 189 dict_union = 190 (DICT_UNION *) dict_alloc(DICT_TYPE_UNION, name, sizeof(*dict_union)); 191 dict_union->dict.lookup = dict_union_lookup; 192 dict_union->dict.close = dict_union_close; 193 dict_union->dict.flags = dict_flags | match_flags; 194 dict_union->dict.owner = aggr_owner; 195 dict_union->re_buf = vstring_alloc(100); 196 dict_union->map_union = argv; 197 argv = 0; 198 DICT_UNION_RETURN(DICT_DEBUG (&dict_union->dict)); 199 } 200