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