xref: /netbsd-src/external/ibm-public/postfix/dist/src/global/maps.c (revision c48c605c14fd8622b523d1d6a3f0c0bad133ea89)
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