1 /*++
2 /* NAME
3 /* maps 3
4 /* SUMMARY
5 /* multi-dictionary search
6 /* SYNOPSIS
7 /* #include <maps.h>
8 /*
9 /* MAPS *maps_create(title, map_names, flags)
10 /* const char *title;
11 /* const char *map_names;
12 /* int flags;
13 /*
14 /* const char *maps_find(maps, key, flags)
15 /* MAPS *maps;
16 /* const char *key;
17 /* int flags;
18 /*
19 /* const char *maps_file_find(maps, key, flags)
20 /* MAPS *maps;
21 /* const char *key;
22 /* int flags;
23 /*
24 /* MAPS *maps_free(maps)
25 /* MAPS *maps;
26 /* DESCRIPTION
27 /* This module implements multi-dictionary searches. it goes
28 /* through the high-level dictionary interface and does file
29 /* locking. Dictionaries are opened read-only, and in-memory
30 /* dictionary instances are shared.
31 /*
32 /* maps_create() takes list of type:name pairs and opens the
33 /* named dictionaries.
34 /* The result is a handle that must be specified along with all
35 /* other maps_xxx() operations.
36 /* See dict_open(3) for a description of flags.
37 /* This includes the flags that specify preferences for search
38 /* string case folding.
39 /*
40 /* maps_find() searches the specified list of dictionaries
41 /* in the specified order for the named key. The result is in
42 /* memory that is overwritten upon each call.
43 /* The flags argument is either 0 or specifies a filter:
44 /* for example, DICT_FLAG_FIXED | DICT_FLAG_PATTERN selects
45 /* dictionaries that have fixed keys or pattern keys.
46 /*
47 /* maps_file_find() implements maps_find() but also decodes
48 /* the base64 lookup result. This requires that the maps are
49 /* opened with DICT_FLAG_SRC_RHS_IS_FILE.
50 /*
51 /* maps_free() releases storage claimed by maps_create()
52 /* and conveniently returns a null pointer.
53 /*
54 /* Arguments:
55 /* .IP title
56 /* String used for diagnostics. Typically one specifies the
57 /* type of information stored in the lookup tables.
58 /* .IP map_names
59 /* Null-terminated string with type:name dictionary specifications,
60 /* separated by whitespace or commas.
61 /* .IP flags
62 /* With maps_create(), flags that are passed to dict_open().
63 /* With maps_find(), flags that control searching behavior
64 /* as documented above.
65 /* .IP maps
66 /* A result from maps_create().
67 /* .IP key
68 /* Null-terminated string with a lookup key. Table lookup is case
69 /* sensitive.
70 /* DIAGNOSTICS
71 /* Panic: inappropriate use; fatal errors: out of memory, unable
72 /* to open database. Warnings: null string lookup result.
73 /*
74 /* maps_find() returns a null pointer when the requested
75 /* information was not found, and logs a warning when the
76 /* lookup failed due to error. The maps->error value indicates
77 /* if the last lookup failed due to error.
78 /* BUGS
79 /* The dictionary name space is flat, so dictionary names allocated
80 /* by maps_create() may collide with dictionary names allocated by
81 /* other methods.
82 /*
83 /* This functionality could be implemented by allowing the user to
84 /* specify dictionary search paths to dict_lookup() or dict_eval().
85 /* However, that would either require that the dict(3) module adopts
86 /* someone else's list notation syntax, or that the dict(3) module
87 /* imposes syntax restrictions onto other software, neither of which
88 /* is desirable.
89 /* LICENSE
90 /* .ad
91 /* .fi
92 /* The Secure Mailer license must be distributed with this software.
93 /* AUTHOR(S)
94 /* Wietse Venema
95 /* IBM T.J. Watson Research
96 /* P.O. Box 704
97 /* Yorktown Heights, NY 10598, USA
98 /*--*/
99
100 /* System library. */
101
102 #include <sys_defs.h>
103 #include <string.h>
104
105 /* Utility library. */
106
107 #include <argv.h>
108 #include <mymalloc.h>
109 #include <msg.h>
110 #include <dict.h>
111 #include <stringops.h>
112 #include <split_at.h>
113
114 /* Global library. */
115
116 #include "mail_conf.h"
117 #include "maps.h"
118
119 /* maps_create - initialize */
120
maps_create(const char * title,const char * map_names,int dict_flags)121 MAPS *maps_create(const char *title, const char *map_names, int dict_flags)
122 {
123 const char *myname = "maps_create";
124 char *temp;
125 char *bufp;
126 static char sep[] = CHARS_COMMA_SP;
127 static char parens[] = CHARS_BRACE;
128 MAPS *maps;
129 char *map_type_name;
130 VSTRING *map_type_name_flags;
131 DICT *dict;
132
133 /*
134 * Initialize.
135 */
136 maps = (MAPS *) mymalloc(sizeof(*maps));
137 maps->title = mystrdup(title);
138 maps->argv = argv_alloc(2);
139 maps->error = 0;
140
141 /*
142 * For each specified type:name pair, either register a new dictionary,
143 * or increment the reference count of an existing one.
144 */
145 if (*map_names) {
146 bufp = temp = mystrdup(map_names);
147 map_type_name_flags = vstring_alloc(10);
148
149 #define OPEN_FLAGS O_RDONLY
150
151 while ((map_type_name = mystrtokq(&bufp, sep, parens)) != 0) {
152 vstring_sprintf(map_type_name_flags, "%s(%o,%s)",
153 map_type_name, OPEN_FLAGS,
154 dict_flags_str(dict_flags));
155 if ((dict = dict_handle(vstring_str(map_type_name_flags))) == 0)
156 dict = dict_open(map_type_name, OPEN_FLAGS, dict_flags);
157 if ((dict->flags & dict_flags) != dict_flags)
158 msg_panic("%s: map %s has flags 0%o, want flags 0%o",
159 myname, map_type_name, dict->flags, dict_flags);
160 dict_register(vstring_str(map_type_name_flags), dict);
161 argv_add(maps->argv, vstring_str(map_type_name_flags), ARGV_END);
162 }
163 myfree(temp);
164 vstring_free(map_type_name_flags);
165 }
166 return (maps);
167 }
168
169 /* maps_find - search a list of dictionaries */
170
maps_find(MAPS * maps,const char * name,int flags)171 const char *maps_find(MAPS *maps, const char *name, int flags)
172 {
173 const char *myname = "maps_find";
174 char **map_name;
175 const char *expansion;
176 DICT *dict;
177
178 /*
179 * In case of return without map lookup (empty name or no maps).
180 */
181 maps->error = 0;
182
183 /*
184 * Temp. workaround, for buggy callers that pass zero-length keys when
185 * given partial addresses.
186 */
187 if (*name == 0)
188 return (0);
189
190 for (map_name = maps->argv->argv; *map_name; map_name++) {
191 if ((dict = dict_handle(*map_name)) == 0)
192 msg_panic("%s: dictionary not found: %s", myname, *map_name);
193 if (flags != 0 && (dict->flags & flags) == 0)
194 continue;
195 if ((expansion = dict_get(dict, name)) != 0) {
196 if (*expansion == 0) {
197 msg_warn("%s lookup of %s returns an empty string result",
198 maps->title, name);
199 msg_warn("%s should return NO RESULT in case of NOT FOUND",
200 maps->title);
201 maps->error = DICT_ERR_CONFIG;
202 return (0);
203 }
204 if (msg_verbose)
205 msg_info("%s: %s: %s: %s = %.100s%s", myname, maps->title,
206 *map_name, name, expansion,
207 strlen(expansion) > 100 ? "..." : "");
208 return (expansion);
209 } else if ((maps->error = dict->error) != 0) {
210 msg_warn("%s:%s lookup error for \"%s\"",
211 dict->type, dict->name, name);
212 break;
213 }
214 }
215 if (msg_verbose)
216 msg_info("%s: %s: %s: %s", myname, maps->title, name, maps->error ?
217 "search aborted" : "not found");
218 return (0);
219 }
220
221 /* maps_file_find - search a list of dictionaries and base64 decode */
222
maps_file_find(MAPS * maps,const char * name,int flags)223 const char *maps_file_find(MAPS *maps, const char *name, int flags)
224 {
225 const char *myname = "maps_file_find";
226 char **map_name;
227 const char *expansion;
228 DICT *dict;
229 VSTRING *unb64;
230 char *err;
231
232 /*
233 * In case of return without map lookup (empty name or no maps).
234 */
235 maps->error = 0;
236
237 /*
238 * Temp. workaround, for buggy callers that pass zero-length keys when
239 * given partial addresses.
240 */
241 if (*name == 0)
242 return (0);
243
244 for (map_name = maps->argv->argv; *map_name; map_name++) {
245 if ((dict = dict_handle(*map_name)) == 0)
246 msg_panic("%s: dictionary not found: %s", myname, *map_name);
247 if ((dict->flags & DICT_FLAG_SRC_RHS_IS_FILE) == 0)
248 msg_panic("%s: %s: opened without DICT_FLAG_SRC_RHS_IS_FILE",
249 myname, maps->title);
250 if (flags != 0 && (dict->flags & flags) == 0)
251 continue;
252 if ((expansion = dict_get(dict, name)) != 0) {
253 if (*expansion == 0) {
254 msg_warn("%s lookup of %s returns an empty string result",
255 maps->title, name);
256 msg_warn("%s should return NO RESULT in case of NOT FOUND",
257 maps->title);
258 maps->error = DICT_ERR_CONFIG;
259 return (0);
260 }
261 if (msg_verbose)
262 msg_info("%s: %s: %s: %s = %.100s%s", myname, maps->title,
263 *map_name, name, expansion,
264 strlen(expansion) > 100 ? "..." : "");
265 if ((unb64 = dict_file_from_b64(dict, expansion)) == 0) {
266 err = dict_file_get_error(dict);
267 msg_warn("table %s:%s: key %s: %s",
268 dict->type, dict->name, name, err);
269 myfree(err);
270 maps->error = DICT_ERR_CONFIG;
271 return (0);
272 }
273 return (vstring_str(unb64));
274 } else if ((maps->error = dict->error) != 0) {
275 msg_warn("%s:%s lookup error for \"%s\"",
276 dict->type, dict->name, name);
277 break;
278 }
279 }
280 if (msg_verbose)
281 msg_info("%s: %s: %s: %s", myname, maps->title, name, maps->error ?
282 "search aborted" : "not found");
283 return (0);
284 }
285
286 /* maps_free - release storage */
287
maps_free(MAPS * maps)288 MAPS *maps_free(MAPS *maps)
289 {
290 char **map_name;
291
292 for (map_name = maps->argv->argv; *map_name; map_name++) {
293 if (msg_verbose)
294 msg_info("maps_free: %s", *map_name);
295 dict_unregister(*map_name);
296 }
297 myfree(maps->title);
298 argv_free(maps->argv);
299 myfree((void *) maps);
300 return (0);
301 }
302
303 #ifdef TEST
304
305 #include <vstring.h>
306 #include <vstream.h>
307 #include <vstring_vstream.h>
308
main(int argc,char ** argv)309 int main(int argc, char **argv)
310 {
311 VSTRING *buf = vstring_alloc(100);
312 MAPS *maps;
313 const char *result;
314
315 if (argc != 2)
316 msg_fatal("usage: %s maps", argv[0]);
317 msg_verbose = 2;
318 maps = maps_create("whatever", argv[1], DICT_FLAG_LOCK);
319
320 while (vstring_fgets_nonl(buf, VSTREAM_IN)) {
321 maps->error = 99;
322 vstream_printf("\"%s\": ", vstring_str(buf));
323 if ((result = maps_find(maps, vstring_str(buf), 0)) != 0) {
324 vstream_printf("%s\n", result);
325 } else if (maps->error != 0) {
326 vstream_printf("lookup error\n");
327 } else {
328 vstream_printf("not found\n");
329 }
330 vstream_fflush(VSTREAM_OUT);
331 }
332 maps_free(maps);
333 vstring_free(buf);
334 return (0);
335 }
336
337 #endif
338