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