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