1 /* $NetBSD: dict_union.c,v 1.3 2020/03/18 19:05:21 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 /* Wietse Venema
43 /* Google, Inc.
44 /* 111 8th Avenue
45 /* New York, NY 10011, USA
46 /*--*/
47
48 /* System library. */
49
50 #include <sys_defs.h>
51 #include <string.h>
52
53 /* Utility library. */
54
55 #include <msg.h>
56 #include <mymalloc.h>
57 #include <htable.h>
58 #include <dict.h>
59 #include <dict_union.h>
60 #include <stringops.h>
61 #include <vstring.h>
62
63 /* Application-specific. */
64
65 typedef struct {
66 DICT dict; /* generic members */
67 ARGV *map_union; /* pipelined tables */
68 VSTRING *re_buf; /* reply buffer */
69 } DICT_UNION;
70
71 #define STR(x) vstring_str(x)
72
73 /* dict_union_lookup - search a bunch of tables and combine the results */
74
dict_union_lookup(DICT * dict,const char * query)75 static const char *dict_union_lookup(DICT *dict, const char *query)
76 {
77 static const char myname[] = "dict_union_lookup";
78 DICT_UNION *dict_union = (DICT_UNION *) dict;
79 DICT *map;
80 char **cpp;
81 char *dict_type_name;
82 const char *result = 0;
83
84 /*
85 * After Roel van Meer, postfix-users mailing list, Sept 2014.
86 */
87 VSTRING_RESET(dict_union->re_buf);
88 for (cpp = dict_union->map_union->argv; (dict_type_name = *cpp) != 0; cpp++) {
89 if ((map = dict_handle(dict_type_name)) == 0)
90 msg_panic("%s: dictionary \"%s\" not found", myname, dict_type_name);
91 if ((result = dict_get(map, query)) != 0) {
92 if (VSTRING_LEN(dict_union->re_buf) > 0)
93 VSTRING_ADDCH(dict_union->re_buf, ',');
94 vstring_strcat(dict_union->re_buf, result);
95 } else if (map->error != 0) {
96 DICT_ERR_VAL_RETURN(dict, map->error, 0);
97 }
98 }
99 DICT_ERR_VAL_RETURN(dict, DICT_ERR_NONE,
100 VSTRING_LEN(dict_union->re_buf) > 0 ?
101 STR(dict_union->re_buf) : 0);
102 }
103
104 /* dict_union_close - disassociate from a bunch of tables */
105
dict_union_close(DICT * dict)106 static void dict_union_close(DICT *dict)
107 {
108 DICT_UNION *dict_union = (DICT_UNION *) dict;
109 char **cpp;
110 char *dict_type_name;
111
112 for (cpp = dict_union->map_union->argv; (dict_type_name = *cpp) != 0; cpp++)
113 dict_unregister(dict_type_name);
114 argv_free(dict_union->map_union);
115 vstring_free(dict_union->re_buf);
116 dict_free(dict);
117 }
118
119 /* dict_union_open - open a bunch of tables */
120
dict_union_open(const char * name,int open_flags,int dict_flags)121 DICT *dict_union_open(const char *name, int open_flags, int dict_flags)
122 {
123 static const char myname[] = "dict_union_open";
124 DICT_UNION *dict_union;
125 char *saved_name = 0;
126 char *dict_type_name;
127 ARGV *argv = 0;
128 char **cpp;
129 DICT *dict;
130 int match_flags = 0;
131 struct DICT_OWNER aggr_owner;
132 size_t len;
133
134 /*
135 * Clarity first. Let the optimizer worry about redundant code.
136 */
137 #define DICT_UNION_RETURN(x) do { \
138 if (saved_name != 0) \
139 myfree(saved_name); \
140 if (argv != 0) \
141 argv_free(argv); \
142 return (x); \
143 } while (0)
144
145 /*
146 * Sanity checks.
147 */
148 if (open_flags != O_RDONLY)
149 DICT_UNION_RETURN(dict_surrogate(DICT_TYPE_UNION, name,
150 open_flags, dict_flags,
151 "%s:%s map requires O_RDONLY access mode",
152 DICT_TYPE_UNION, name));
153
154 /*
155 * Split the table name into its constituent parts.
156 */
157 if ((len = balpar(name, CHARS_BRACE)) == 0 || name[len] != 0
158 || *(saved_name = mystrndup(name + 1, len - 2)) == 0
159 || ((argv = argv_splitq(saved_name, CHARS_COMMA_SP, CHARS_BRACE)),
160 (argv->argc == 0)))
161 DICT_UNION_RETURN(dict_surrogate(DICT_TYPE_UNION, name,
162 open_flags, dict_flags,
163 "bad syntax: \"%s:%s\"; "
164 "need \"%s:{type:name...}\"",
165 DICT_TYPE_UNION, name,
166 DICT_TYPE_UNION));
167
168 /*
169 * The least-trusted table in the set determines the over-all trust
170 * level. The first table determines the pattern-matching flags.
171 */
172 DICT_OWNER_AGGREGATE_INIT(aggr_owner);
173 for (cpp = argv->argv; (dict_type_name = *cpp) != 0; cpp++) {
174 if (msg_verbose)
175 msg_info("%s: %s", myname, dict_type_name);
176 if (strchr(dict_type_name, ':') == 0)
177 DICT_UNION_RETURN(dict_surrogate(DICT_TYPE_UNION, name,
178 open_flags, dict_flags,
179 "bad syntax: \"%s:%s\"; "
180 "need \"%s:{type:name...}\"",
181 DICT_TYPE_UNION, name,
182 DICT_TYPE_UNION));
183 if ((dict = dict_handle(dict_type_name)) == 0)
184 dict = dict_open(dict_type_name, open_flags, dict_flags);
185 dict_register(dict_type_name, dict);
186 DICT_OWNER_AGGREGATE_UPDATE(aggr_owner, dict->owner);
187 if (cpp == argv->argv)
188 match_flags = dict->flags & (DICT_FLAG_FIXED | DICT_FLAG_PATTERN);
189 }
190
191 /*
192 * Bundle up the result.
193 */
194 dict_union =
195 (DICT_UNION *) dict_alloc(DICT_TYPE_UNION, name, sizeof(*dict_union));
196 dict_union->dict.lookup = dict_union_lookup;
197 dict_union->dict.close = dict_union_close;
198 dict_union->dict.flags = dict_flags | match_flags;
199 dict_union->dict.owner = aggr_owner;
200 dict_union->re_buf = vstring_alloc(100);
201 dict_union->map_union = argv;
202 argv = 0;
203 DICT_UNION_RETURN(DICT_DEBUG (&dict_union->dict));
204 }
205