/*++ /* NAME /* maps 3 /* SUMMARY /* multi-dictionary search /* SYNOPSIS /* #include /* /* MAPS *maps_create(title, map_names, flags) /* const char *title; /* const char *map_names; /* int flags; /* /* const char *maps_find(maps, key, flags) /* MAPS *maps; /* const char *key; /* int flags; /* /* const char *maps_file_find(maps, key, flags) /* MAPS *maps; /* const char *key; /* int flags; /* /* MAPS *maps_free(maps) /* MAPS *maps; /* DESCRIPTION /* This module implements multi-dictionary searches. it goes /* through the high-level dictionary interface and does file /* locking. Dictionaries are opened read-only, and in-memory /* dictionary instances are shared. /* /* maps_create() takes list of type:name pairs and opens the /* named dictionaries. /* The result is a handle that must be specified along with all /* other maps_xxx() operations. /* See dict_open(3) for a description of flags. /* This includes the flags that specify preferences for search /* string case folding. /* /* maps_find() searches the specified list of dictionaries /* in the specified order for the named key. The result is in /* memory that is overwritten upon each call. /* The flags argument is either 0 or specifies a filter: /* for example, DICT_FLAG_FIXED | DICT_FLAG_PATTERN selects /* dictionaries that have fixed keys or pattern keys. /* /* maps_file_find() implements maps_find() but also decodes /* the base64 lookup result. This requires that the maps are /* opened with DICT_FLAG_SRC_RHS_IS_FILE. /* /* maps_free() releases storage claimed by maps_create() /* and conveniently returns a null pointer. /* /* Arguments: /* .IP title /* String used for diagnostics. Typically one specifies the /* type of information stored in the lookup tables. /* .IP map_names /* Null-terminated string with type:name dictionary specifications, /* separated by whitespace or commas. /* .IP flags /* With maps_create(), flags that are passed to dict_open(). /* With maps_find(), flags that control searching behavior /* as documented above. /* .IP maps /* A result from maps_create(). /* .IP key /* Null-terminated string with a lookup key. Table lookup is case /* sensitive. /* DIAGNOSTICS /* Panic: inappropriate use; fatal errors: out of memory, unable /* to open database. Warnings: null string lookup result. /* /* maps_find() returns a null pointer when the requested /* information was not found, and logs a warning when the /* lookup failed due to error. The maps->error value indicates /* if the last lookup failed due to error. /* BUGS /* The dictionary name space is flat, so dictionary names allocated /* by maps_create() may collide with dictionary names allocated by /* other methods. /* /* This functionality could be implemented by allowing the user to /* specify dictionary search paths to dict_lookup() or dict_eval(). /* However, that would either require that the dict(3) module adopts /* someone else's list notation syntax, or that the dict(3) module /* imposes syntax restrictions onto other software, neither of which /* is desirable. /* LICENSE /* .ad /* .fi /* The Secure Mailer license must be distributed with this software. /* AUTHOR(S) /* Wietse Venema /* IBM T.J. Watson Research /* P.O. Box 704 /* Yorktown Heights, NY 10598, USA /*--*/ /* System library. */ #include #include /* Utility library. */ #include #include #include #include #include #include /* Global library. */ #include "mail_conf.h" #include "maps.h" /* maps_create - initialize */ MAPS *maps_create(const char *title, const char *map_names, int dict_flags) { const char *myname = "maps_create"; char *temp; char *bufp; static char sep[] = CHARS_COMMA_SP; static char parens[] = CHARS_BRACE; MAPS *maps; char *map_type_name; VSTRING *map_type_name_flags; DICT *dict; /* * Initialize. */ maps = (MAPS *) mymalloc(sizeof(*maps)); maps->title = mystrdup(title); maps->argv = argv_alloc(2); maps->error = 0; /* * For each specified type:name pair, either register a new dictionary, * or increment the reference count of an existing one. */ if (*map_names) { bufp = temp = mystrdup(map_names); map_type_name_flags = vstring_alloc(10); #define OPEN_FLAGS O_RDONLY while ((map_type_name = mystrtokq(&bufp, sep, parens)) != 0) { vstring_sprintf(map_type_name_flags, "%s(%o,%s)", map_type_name, OPEN_FLAGS, dict_flags_str(dict_flags)); if ((dict = dict_handle(vstring_str(map_type_name_flags))) == 0) dict = dict_open(map_type_name, OPEN_FLAGS, dict_flags); if ((dict->flags & dict_flags) != dict_flags) msg_panic("%s: map %s has flags 0%o, want flags 0%o", myname, map_type_name, dict->flags, dict_flags); dict_register(vstring_str(map_type_name_flags), dict); argv_add(maps->argv, vstring_str(map_type_name_flags), ARGV_END); } myfree(temp); vstring_free(map_type_name_flags); } return (maps); } /* maps_find - search a list of dictionaries */ const char *maps_find(MAPS *maps, const char *name, int flags) { const char *myname = "maps_find"; char **map_name; const char *expansion; DICT *dict; /* * In case of return without map lookup (empty name or no maps). */ maps->error = 0; /* * Temp. workaround, for buggy callers that pass zero-length keys when * given partial addresses. */ if (*name == 0) return (0); for (map_name = maps->argv->argv; *map_name; map_name++) { if ((dict = dict_handle(*map_name)) == 0) msg_panic("%s: dictionary not found: %s", myname, *map_name); if (flags != 0 && (dict->flags & flags) == 0) continue; if ((expansion = dict_get(dict, name)) != 0) { if (*expansion == 0) { msg_warn("%s lookup of %s returns an empty string result", maps->title, name); msg_warn("%s should return NO RESULT in case of NOT FOUND", maps->title); maps->error = DICT_ERR_CONFIG; return (0); } if (msg_verbose) msg_info("%s: %s: %s: %s = %.100s%s", myname, maps->title, *map_name, name, expansion, strlen(expansion) > 100 ? "..." : ""); return (expansion); } else if ((maps->error = dict->error) != 0) { msg_warn("%s:%s lookup error for \"%s\"", dict->type, dict->name, name); break; } } if (msg_verbose) msg_info("%s: %s: %s: %s", myname, maps->title, name, maps->error ? "search aborted" : "not found"); return (0); } /* maps_file_find - search a list of dictionaries and base64 decode */ const char *maps_file_find(MAPS *maps, const char *name, int flags) { const char *myname = "maps_file_find"; char **map_name; const char *expansion; DICT *dict; VSTRING *unb64; char *err; /* * In case of return without map lookup (empty name or no maps). */ maps->error = 0; /* * Temp. workaround, for buggy callers that pass zero-length keys when * given partial addresses. */ if (*name == 0) return (0); for (map_name = maps->argv->argv; *map_name; map_name++) { if ((dict = dict_handle(*map_name)) == 0) msg_panic("%s: dictionary not found: %s", myname, *map_name); if ((dict->flags & DICT_FLAG_SRC_RHS_IS_FILE) == 0) msg_panic("%s: %s: opened without DICT_FLAG_SRC_RHS_IS_FILE", myname, maps->title); if (flags != 0 && (dict->flags & flags) == 0) continue; if ((expansion = dict_get(dict, name)) != 0) { if (*expansion == 0) { msg_warn("%s lookup of %s returns an empty string result", maps->title, name); msg_warn("%s should return NO RESULT in case of NOT FOUND", maps->title); maps->error = DICT_ERR_CONFIG; return (0); } if (msg_verbose) msg_info("%s: %s: %s: %s = %.100s%s", myname, maps->title, *map_name, name, expansion, strlen(expansion) > 100 ? "..." : ""); if ((unb64 = dict_file_from_b64(dict, expansion)) == 0) { err = dict_file_get_error(dict); msg_warn("table %s:%s: key %s: %s", dict->type, dict->name, name, err); myfree(err); maps->error = DICT_ERR_CONFIG; return (0); } return (vstring_str(unb64)); } else if ((maps->error = dict->error) != 0) { msg_warn("%s:%s lookup error for \"%s\"", dict->type, dict->name, name); break; } } if (msg_verbose) msg_info("%s: %s: %s: %s", myname, maps->title, name, maps->error ? "search aborted" : "not found"); return (0); } /* maps_free - release storage */ MAPS *maps_free(MAPS *maps) { char **map_name; for (map_name = maps->argv->argv; *map_name; map_name++) { if (msg_verbose) msg_info("maps_free: %s", *map_name); dict_unregister(*map_name); } myfree(maps->title); argv_free(maps->argv); myfree((void *) maps); return (0); } #ifdef TEST #include #include #include int main(int argc, char **argv) { VSTRING *buf = vstring_alloc(100); MAPS *maps; const char *result; if (argc != 2) msg_fatal("usage: %s maps", argv[0]); msg_verbose = 2; maps = maps_create("whatever", argv[1], DICT_FLAG_LOCK); while (vstring_fgets_nonl(buf, VSTREAM_IN)) { maps->error = 99; vstream_printf("\"%s\": ", vstring_str(buf)); if ((result = maps_find(maps, vstring_str(buf), 0)) != 0) { vstream_printf("%s\n", result); } else if (maps->error != 0) { vstream_printf("lookup error\n"); } else { vstream_printf("not found\n"); } vstream_fflush(VSTREAM_OUT); } maps_free(maps); vstring_free(buf); return (0); } #endif