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/* MAPS *maps_free(maps) 20/* MAPS *maps; 21/* DESCRIPTION 22/* This module implements multi-dictionary searches. it goes 23/* through the high-level dictionary interface and does file 24/* locking. Dictionaries are opened read-only, and in-memory 25/* dictionary instances are shared. 26/* 27/* maps_create() takes list of type:name pairs and opens the 28/* named dictionaries. 29/* The result is a handle that must be specified along with all 30/* other maps_xxx() operations. 31/* See dict_open(3) for a description of flags. 32/* This includes the flags that specify preferences for search 33/* string case folding. 34/* 35/* maps_find() searches the specified list of dictionaries 36/* in the specified order for the named key. The result is in 37/* memory that is overwritten upon each call. 38/* The flags argument is either 0 or specifies a filter: 39/* for example, DICT_FLAG_FIXED | DICT_FLAG_PATTERN selects 40/* dictionaries that have fixed keys or pattern keys. 41/* 42/* maps_free() releases storage claimed by maps_create() 43/* and conveniently returns a null pointer. 44/* 45/* Arguments: 46/* .IP title 47/* String used for diagnostics. Typically one specifies the 48/* type of information stored in the lookup tables. 49/* .IP map_names 50/* Null-terminated string with type:name dictionary specifications, 51/* separated by whitespace or commas. 52/* .IP flags 53/* With maps_create(), flags that are passed to dict_open(). 54/* With maps_find(), flags that control searching behavior 55/* as documented above. 56/* .IP maps 57/* A result from maps_create(). 58/* .IP key 59/* Null-terminated string with a lookup key. Table lookup is case 60/* sensitive. 61/* DIAGNOSTICS 62/* Panic: inappropriate use; fatal errors: out of memory, unable 63/* to open database. Warnings: null string lookup result. 64/* 65/* maps_find() returns a null pointer when the requested 66/* information was not found, and logs a warning when the 67/* lookup failed due to error. The maps->error value indicates 68/* if the last lookup failed due to error. 69/* BUGS 70/* The dictionary name space is flat, so dictionary names allocated 71/* by maps_create() may collide with dictionary names allocated by 72/* other methods. 73/* 74/* This functionality could be implemented by allowing the user to 75/* specify dictionary search paths to dict_lookup() or dict_eval(). 76/* However, that would either require that the dict(3) module adopts 77/* someone else's list notation syntax, or that the dict(3) module 78/* imposes syntax restrictions onto other software, neither of which 79/* is desirable. 80/* LICENSE 81/* .ad 82/* .fi 83/* The Secure Mailer license must be distributed with this software. 84/* AUTHOR(S) 85/* Wietse Venema 86/* IBM T.J. Watson Research 87/* P.O. Box 704 88/* Yorktown Heights, NY 10598, USA 89/*--*/ 90 91/* System library. */ 92 93#include <sys_defs.h> 94#include <string.h> 95 96/* Utility library. */ 97 98#include <argv.h> 99#include <mymalloc.h> 100#include <msg.h> 101#include <dict.h> 102#include <stringops.h> 103#include <split_at.h> 104 105/* Global library. */ 106 107#include "mail_conf.h" 108#include "maps.h" 109 110/* maps_create - initialize */ 111 112MAPS *maps_create(const char *title, const char *map_names, int dict_flags) 113{ 114 const char *myname = "maps_create"; 115 char *temp; 116 char *bufp; 117 static char sep[] = " \t,\r\n"; 118 MAPS *maps; 119 char *map_type_name; 120 VSTRING *map_type_name_flags; 121 DICT *dict; 122 123 /* 124 * Initialize. 125 */ 126 maps = (MAPS *) mymalloc(sizeof(*maps)); 127 maps->title = mystrdup(title); 128 maps->argv = argv_alloc(2); 129 maps->error = 0; 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 maps->error = 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 maps->error = 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 ((maps->error = dict->error) != 0) { 199 msg_warn("%s:%s lookup error for \"%.100s\"", 200 dict->type, dict->name, name); 201 break; 202 } 203 } 204 if (msg_verbose) 205 msg_info("%s: %s: %s: %s", myname, maps->title, name, maps->error ? 206 "search aborted" : "not found"); 207 return (0); 208} 209 210/* maps_free - release storage */ 211 212MAPS *maps_free(MAPS *maps) 213{ 214 char **map_name; 215 216 for (map_name = maps->argv->argv; *map_name; map_name++) { 217 if (msg_verbose) 218 msg_info("maps_free: %s", *map_name); 219 dict_unregister(*map_name); 220 } 221 myfree(maps->title); 222 argv_free(maps->argv); 223 myfree((char *) maps); 224 return (0); 225} 226 227#ifdef TEST 228 229#include <vstring.h> 230#include <vstream.h> 231#include <vstring_vstream.h> 232 233int main(int argc, char **argv) 234{ 235 VSTRING *buf = vstring_alloc(100); 236 MAPS *maps; 237 const char *result; 238 239 if (argc != 2) 240 msg_fatal("usage: %s maps", argv[0]); 241 msg_verbose = 2; 242 maps = maps_create("whatever", argv[1], DICT_FLAG_LOCK); 243 244 while (vstring_fgets_nonl(buf, VSTREAM_IN)) { 245 maps->error = 99; 246 vstream_printf("\"%s\": ", vstring_str(buf)); 247 if ((result = maps_find(maps, vstring_str(buf), 0)) != 0) { 248 vstream_printf("%s\n", result); 249 } else if (maps->error != 0) { 250 vstream_printf("lookup error\n"); 251 } else { 252 vstream_printf("not found\n"); 253 } 254 vstream_fflush(VSTREAM_OUT); 255 } 256 maps_free(maps); 257 vstring_free(buf); 258 return (0); 259} 260 261#endif 262