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