xref: /netbsd-src/external/ibm-public/postfix/dist/src/global/maps.c (revision 33881f779a77dce6440bdc44610d94de75bebefe)
1 /*	$NetBSD: maps.c,v 1.3 2020/03/18 19:05:16 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 
102 /* System library. */
103 
104 #include <sys_defs.h>
105 #include <string.h>
106 
107 /* Utility library. */
108 
109 #include <argv.h>
110 #include <mymalloc.h>
111 #include <msg.h>
112 #include <dict.h>
113 #include <stringops.h>
114 #include <split_at.h>
115 
116 /* Global library. */
117 
118 #include "mail_conf.h"
119 #include "maps.h"
120 
121 /* maps_create - initialize */
122 
123 MAPS   *maps_create(const char *title, const char *map_names, int dict_flags)
124 {
125     const char *myname = "maps_create";
126     char   *temp;
127     char   *bufp;
128     static char sep[] = CHARS_COMMA_SP;
129     static char parens[] = CHARS_BRACE;
130     MAPS   *maps;
131     char   *map_type_name;
132     VSTRING *map_type_name_flags;
133     DICT   *dict;
134 
135     /*
136      * Initialize.
137      */
138     maps = (MAPS *) mymalloc(sizeof(*maps));
139     maps->title = mystrdup(title);
140     maps->argv = argv_alloc(2);
141     maps->error = 0;
142 
143     /*
144      * For each specified type:name pair, either register a new dictionary,
145      * or increment the reference count of an existing one.
146      */
147     if (*map_names) {
148 	bufp = temp = mystrdup(map_names);
149 	map_type_name_flags = vstring_alloc(10);
150 
151 #define OPEN_FLAGS	O_RDONLY
152 
153 	while ((map_type_name = mystrtokq(&bufp, sep, parens)) != 0) {
154 	    vstring_sprintf(map_type_name_flags, "%s(%o,%s)",
155 			    map_type_name, OPEN_FLAGS,
156 			    dict_flags_str(dict_flags));
157 	    if ((dict = dict_handle(vstring_str(map_type_name_flags))) == 0)
158 		dict = dict_open(map_type_name, OPEN_FLAGS, dict_flags);
159 	    if ((dict->flags & dict_flags) != dict_flags)
160 		msg_panic("%s: map %s has flags 0%o, want flags 0%o",
161 			  myname, map_type_name, dict->flags, dict_flags);
162 	    dict_register(vstring_str(map_type_name_flags), dict);
163 	    argv_add(maps->argv, vstring_str(map_type_name_flags), ARGV_END);
164 	}
165 	myfree(temp);
166 	vstring_free(map_type_name_flags);
167     }
168     return (maps);
169 }
170 
171 /* maps_find - search a list of dictionaries */
172 
173 const char *maps_find(MAPS *maps, const char *name, int flags)
174 {
175     const char *myname = "maps_find";
176     char  **map_name;
177     const char *expansion;
178     DICT   *dict;
179 
180     /*
181      * In case of return without map lookup (empty name or no maps).
182      */
183     maps->error = 0;
184 
185     /*
186      * Temp. workaround, for buggy callers that pass zero-length keys when
187      * given partial addresses.
188      */
189     if (*name == 0)
190 	return (0);
191 
192     for (map_name = maps->argv->argv; *map_name; map_name++) {
193 	if ((dict = dict_handle(*map_name)) == 0)
194 	    msg_panic("%s: dictionary not found: %s", myname, *map_name);
195 	if (flags != 0 && (dict->flags & flags) == 0)
196 	    continue;
197 	if ((expansion = dict_get(dict, name)) != 0) {
198 	    if (*expansion == 0) {
199 		msg_warn("%s lookup of %s returns an empty string result",
200 			 maps->title, name);
201 		msg_warn("%s should return NO RESULT in case of NOT FOUND",
202 			 maps->title);
203 		maps->error = DICT_ERR_CONFIG;
204 		return (0);
205 	    }
206 	    if (msg_verbose)
207 		msg_info("%s: %s: %s: %s = %.100s%s", myname, maps->title,
208 			 *map_name, name, expansion,
209 			 strlen(expansion) > 100 ? "..." : "");
210 	    return (expansion);
211 	} else if ((maps->error = dict->error) != 0) {
212 	    msg_warn("%s:%s lookup error for \"%s\"",
213 		     dict->type, dict->name, name);
214 	    break;
215 	}
216     }
217     if (msg_verbose)
218 	msg_info("%s: %s: %s: %s", myname, maps->title, name, maps->error ?
219 		 "search aborted" : "not found");
220     return (0);
221 }
222 
223 /* maps_file_find - search a list of dictionaries and base64 decode */
224 
225 const char *maps_file_find(MAPS *maps, const char *name, int flags)
226 {
227     const char *myname = "maps_file_find";
228     char  **map_name;
229     const char *expansion;
230     DICT   *dict;
231     VSTRING *unb64;
232     char   *err;
233 
234     /*
235      * In case of return without map lookup (empty name or no maps).
236      */
237     maps->error = 0;
238 
239     /*
240      * Temp. workaround, for buggy callers that pass zero-length keys when
241      * given partial addresses.
242      */
243     if (*name == 0)
244 	return (0);
245 
246     for (map_name = maps->argv->argv; *map_name; map_name++) {
247 	if ((dict = dict_handle(*map_name)) == 0)
248 	    msg_panic("%s: dictionary not found: %s", myname, *map_name);
249 	if ((dict->flags & DICT_FLAG_SRC_RHS_IS_FILE) == 0)
250 	    msg_panic("%s: %s: opened without DICT_FLAG_SRC_RHS_IS_FILE",
251 		      myname, maps->title);
252 	if (flags != 0 && (dict->flags & flags) == 0)
253 	    continue;
254 	if ((expansion = dict_get(dict, name)) != 0) {
255 	    if (*expansion == 0) {
256 		msg_warn("%s lookup of %s returns an empty string result",
257 			 maps->title, name);
258 		msg_warn("%s should return NO RESULT in case of NOT FOUND",
259 			 maps->title);
260 		maps->error = DICT_ERR_CONFIG;
261 		return (0);
262 	    }
263 	    if (msg_verbose)
264 		msg_info("%s: %s: %s: %s = %.100s%s", myname, maps->title,
265 			 *map_name, name, expansion,
266 			 strlen(expansion) > 100 ? "..." : "");
267 	    if ((unb64 = dict_file_from_b64(dict, expansion)) == 0) {
268 		err = dict_file_get_error(dict);
269 		msg_warn("table %s:%s: key %s: %s",
270 			 dict->type, dict->name, name, err);
271 		myfree(err);
272 		maps->error = DICT_ERR_CONFIG;
273 		return (0);
274 	    }
275 	    return (vstring_str(unb64));
276 	} else if ((maps->error = dict->error) != 0) {
277 	    msg_warn("%s:%s lookup error for \"%s\"",
278 		     dict->type, dict->name, name);
279 	    break;
280 	}
281     }
282     if (msg_verbose)
283 	msg_info("%s: %s: %s: %s", myname, maps->title, name, maps->error ?
284 		 "search aborted" : "not found");
285     return (0);
286 }
287 
288 /* maps_free - release storage */
289 
290 MAPS   *maps_free(MAPS *maps)
291 {
292     char  **map_name;
293 
294     for (map_name = maps->argv->argv; *map_name; map_name++) {
295 	if (msg_verbose)
296 	    msg_info("maps_free: %s", *map_name);
297 	dict_unregister(*map_name);
298     }
299     myfree(maps->title);
300     argv_free(maps->argv);
301     myfree((void *) maps);
302     return (0);
303 }
304 
305 #ifdef TEST
306 
307 #include <vstring.h>
308 #include <vstream.h>
309 #include <vstring_vstream.h>
310 
311 int     main(int argc, char **argv)
312 {
313     VSTRING *buf = vstring_alloc(100);
314     MAPS   *maps;
315     const char *result;
316 
317     if (argc != 2)
318 	msg_fatal("usage: %s maps", argv[0]);
319     msg_verbose = 2;
320     maps = maps_create("whatever", argv[1], DICT_FLAG_LOCK);
321 
322     while (vstring_fgets_nonl(buf, VSTREAM_IN)) {
323 	maps->error = 99;
324 	vstream_printf("\"%s\": ", vstring_str(buf));
325 	if ((result = maps_find(maps, vstring_str(buf), 0)) != 0) {
326 	    vstream_printf("%s\n", result);
327 	} else if (maps->error != 0) {
328 	    vstream_printf("lookup error\n");
329 	} else {
330 	    vstream_printf("not found\n");
331 	}
332 	vstream_fflush(VSTREAM_OUT);
333     }
334     maps_free(maps);
335     vstring_free(buf);
336     return (0);
337 }
338 
339 #endif
340