NetBSD/gnu/dist/postfix/util/dict_open.c

388 lines
10 KiB
C

/*++
/* NAME
/* dict_open 3
/* SUMMARY
/* low-level dictionary interface
/* SYNOPSIS
/* #include <dict.h>
/*
/* DICT *dict_open(dict_spec, open_flags, dict_flags)
/* const char *dict_spec;
/* int open_flags;
/* int dict_flags;
/*
/* DICT *dict_open3(dict_type, dict_name, open_flags, dict_flags)
/* const char *dict_type;
/* const char *dict_name;
/* int open_flags;
/* int dict_flags;
/*
/* void dict_put(dict, key, value)
/* DICT *dict;
/* const char *key;
/* const char *value;
/*
/* char *dict_get(dict, key)
/* DICT *dict;
/* const char *key;
/*
/* char *dict_del(dict, key)
/* DICT *dict;
/* const char *key;
/*
/* void dict_seq(dict, func, key, value)
/* DICT *dict;
/* int func;
/* const char **key;
/* const char **value;
/*
/* void dict_close(dict)
/* DICT *dict;
/*
/* dict_open_register(type, open)
/* char *type;
/* DICT *(*open) (const char *, int, int);
/* DESCRIPTION
/* This module implements a low-level interface to multiple
/* physical dictionary types.
/*
/* dict_open() takes a type:name pair that specifies a dictionary type
/* and dictionary name, opens the dictionary, and returns a dictionary
/* handle. The \fIopen_flags\fR arguments are as in open(2). The
/* \fIdict_flags\fR are the bit-wise OR of zero or more of the following:
/* .IP DICT_FLAG_DUP_WARN
/* Warn about duplicate keys, if the underlying database does not
/* support duplicate keys. The default is to terminate with a fatal
/* error.
/* .IP DICT_FLAG_DUP_IGNORE
/* Ignore duplicate keys if the underlying database does not
/* support duplicate keys. The default is to terminate with a fatal
/* error.
/* .IP DICT_FLAG_DUP_REPLACE
/* Replace duplicate keys if the underlying database supports such
/* an operation. The default is to terminate with a fatal error.
/* .IP DICT_FLAG_TRY0NULL
/* With maps where this is appropriate, append no null byte to
/* keys and values.
/* When neither DICT_FLAG_TRY0NULL nor DICT_FLAG_TRY1NULL are
/* specified, the software guesses what format to use for reading;
/* and in the absence of definite information, a system-dependent
/* default is chosen for writing.
/* .IP DICT_FLAG_TRY1NULL
/* With maps where this is appropriate, append one null byte to
/* keys and values.
/* When neither DICT_FLAG_TRY0NULL nor DICT_FLAG_TRY1NULL are
/* specified, the software guesses what format to use for reading;
/* and in the absence of definite information, a system-dependent
/* default is chosen for writing.
/* .IP DICT_FLAG_LOCK
/* With maps where this is appropriate, acquire an exclusive lock
/* before writing, and acquire a shared lock before reading.
/* .PP
/* The dictionary types are as follows:
/* .IP environ
/* The process environment array. The \fIdict_name\fR argument is ignored.
/* .IP dbm
/* DBM file.
/* .IP hash
/* Berkeley DB file in hash format.
/* .IP btree
/* Berkeley DB file in btree format.
/* .IP nis
/* NIS map. Only read access is supported.
/* .IP nisplus
/* NIS+ map. Only read access is supported.
/* .IP netinfo
/* NetInfo table. Only read access is supported.
/* .IP ldap
/* LDAP ("light-weight" directory access protocol) database access.
/* .IP pcre
/* PERL-compatible regular expressions.
/* .IP regexp
/* POSIX-compatible regular expressions.
/* .PP
/* dict_open3() takes separate arguments for dictionary type and
/* name, but otherwise performs the same functions as dict_open().
/*
/* dict_get() retrieves the value stored in the named dictionary
/* under the given key. A null pointer means the value was not found.
/*
/* dict_put() stores the specified key and value into the named
/* dictionary.
/*
/* dict_del() removes a dictionary entry, and returns non-zero
/* in case of problems.
/*
/* dict_seq() iterates over all members in the named dictionary.
/* func is define DICT_SEQ_FUN_FIRST (select first member) or
/* DICT_SEQ_FUN_NEXT (select next member). A null result means
/* there is more.
/*
/* dict_close() closes the specified dictionary and cleans up the
/* associated data structures.
/*
/* dict_open_register() adds support for a new dictionary type.
/* DIAGNOSTICS
/* Fatal error: open error, unsupported dictionary type, attempt to
/* update non-writable dictionary.
/* 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 <sys_defs.h>
#include <string.h>
#ifdef STRCASECMP_IN_STRINGS_H
#include <strings.h>
#endif
/* Utility library. */
#include <argv.h>
#include <mymalloc.h>
#include <msg.h>
#include <dict.h>
#include <dict_env.h>
#include <dict_unix.h>
#include <dict_dbm.h>
#include <dict_db.h>
#include <dict_nis.h>
#include <dict_nisplus.h>
#include <dict_ni.h>
#include <dict_ldap.h>
#include <dict_mysql.h>
#include <dict_pcre.h>
#include <dict_regexp.h>
#include <stringops.h>
#include <split_at.h>
#include <htable.h>
/*
* lookup table for available map types.
*/
typedef struct {
const char *type;
struct DICT *(*open) (const char *, int, int);
} DICT_OPEN_INFO;
static DICT_OPEN_INFO dict_open_info[] = {
"environ", dict_env_open,
"unix", dict_unix_open,
#ifdef HAS_DBM
"dbm", dict_dbm_open,
#endif
#ifdef HAS_DB
"hash", dict_hash_open,
"btree", dict_btree_open,
#endif
#ifdef HAS_NIS
"nis", dict_nis_open,
#endif
#ifdef HAS_NISPLUS
"nisplus", dict_nisplus_open,
#endif
#ifdef HAS_NETINFO
"netinfo", dict_ni_open,
#endif
#ifdef HAS_LDAP
"ldap", dict_ldap_open,
#endif
#ifdef HAS_MYSQL
"mysql", dict_mysql_open,
#endif
#ifdef HAS_PCRE
"pcre", dict_pcre_open,
#endif
#ifdef HAS_POSIX_REGEXP
"regexp", dict_regexp_open,
#endif
0,
};
static HTABLE *dict_open_hash;
/* dict_open_init - one-off initialization */
static void dict_open_init(void)
{
const char *myname = "dict_open_init";
DICT_OPEN_INFO *dp;
if (dict_open_hash != 0)
msg_panic("%s: multiple initialization", myname);
dict_open_hash = htable_create(10);
for (dp = dict_open_info; dp->type; dp++)
htable_enter(dict_open_hash, dp->type, (char *) dp);
}
/* dict_open - open dictionary */
DICT *dict_open(const char *dict_spec, int open_flags, int dict_flags)
{
char *saved_dict_spec = mystrdup(dict_spec);
char *dict_name;
DICT *dict;
if ((dict_name = split_at(saved_dict_spec, ':')) == 0)
msg_fatal("open dictionary: need \"type:name\" form: %s", dict_spec);
dict = dict_open3(saved_dict_spec, dict_name, open_flags, dict_flags);
myfree(saved_dict_spec);
return (dict);
}
/* dict_open3 - open dictionary */
DICT *dict_open3(const char *dict_type, const char *dict_name,
int open_flags, int dict_flags)
{
const char *myname = "dict_open";
DICT_OPEN_INFO *dp;
DICT *dict;
if (dict_open_hash == 0)
dict_open_init();
if ((dp = (DICT_OPEN_INFO *) htable_find(dict_open_hash, dict_type)) == 0)
msg_fatal("unsupported dictionary type: %s", dict_type);
if ((dict = dp->open(dict_name, open_flags, dict_flags)) == 0)
msg_fatal("opening %s:%s %m", dict_type, dict_name);
if (msg_verbose)
msg_info("%s: %s:%s", myname, dict_type, dict_name);
return (dict);
}
/* dict_open_register - register dictionary type */
void dict_open_register(const char *type,
DICT *(*open) (const char *, int, int))
{
const char *myname = "dict_open_register";
DICT_OPEN_INFO *dp;
if (dict_open_hash == 0)
dict_open_init();
if (htable_find(dict_open_hash, type))
msg_panic("%s: dictionary type exists: %s", myname, type);
dp = (DICT_OPEN_INFO *) mymalloc(sizeof(*dp));
dp->type = mystrdup(type);
dp->open = open;
htable_enter(dict_open_hash, dp->type, (char *) dp);
}
/* dict_mapnames - return an ARGV of available map_names */
ARGV *dict_mapnames()
{
HTABLE_INFO **ht_info;
HTABLE_INFO **ht;
DICT_OPEN_INFO *dp;
ARGV *mapnames;
if (dict_open_hash == 0)
dict_open_init();
mapnames = argv_alloc(dict_open_hash->used + 1);
for (ht_info = ht = htable_list(dict_open_hash); *ht; ht++) {
dp = (DICT_OPEN_INFO *) ht[0]->value;
argv_add(mapnames, dp->type, ARGV_END);
}
myfree((char *) ht_info);
argv_terminate(mapnames);
return mapnames;
}
#ifdef TEST
/*
* Proof-of-concept test program. Create, update or read a database. When
* the input is a name=value pair, the database is updated, otherwise the
* program assumes that the input specifies a lookup key and prints the
* corresponding value.
*/
/* System library. */
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
/* Utility library. */
#include "vstring.h"
#include "vstream.h"
#include "msg_vstream.h"
#include "vstring_vstream.h"
static NORETURN usage(char *myname)
{
msg_fatal("usage: %s type:file read|write|create", myname);
}
main(int argc, char **argv)
{
VSTRING *keybuf = vstring_alloc(1);
DICT *dict;
char *dict_name;
int open_flags;
char *key;
const char *value;
int ch;
msg_vstream_init(argv[0], VSTREAM_ERR);
while ((ch = GETOPT(argc, argv, "v")) > 0) {
switch (ch) {
default:
usage(argv[0]);
case 'v':
msg_verbose++;
break;
}
}
optind = OPTIND;
if (argc - optind != 2)
usage(argv[0]);
if (strcasecmp(argv[optind + 1], "create") == 0)
open_flags = O_CREAT | O_RDWR | O_TRUNC;
else if (strcasecmp(argv[optind + 1], "write") == 0)
open_flags = O_RDWR;
else if (strcasecmp(argv[optind + 1], "read") == 0)
open_flags = O_RDONLY;
else
msg_fatal("unknown access mode: %s", argv[2]);
dict_name = argv[optind];
dict = dict_open(dict_name, open_flags, DICT_FLAG_LOCK);
dict_register(dict_name, dict);
while (vstring_fgets_nonl(keybuf, VSTREAM_IN)) {
if (dict_changed()) {
msg_warn("dictionary has changed -- exiting");
exit(0);
}
if ((key = strtok(vstring_str(keybuf), " =")) == 0)
continue;
if ((value = strtok((char *) 0, " =")) == 0) {
if ((value = dict_get(dict, key)) == 0) {
vstream_printf("not found\n");
} else {
vstream_printf("%s\n", value);
}
} else {
dict_put(dict, key, value);
}
vstream_fflush(VSTREAM_OUT);
}
vstring_free(keybuf);
dict_close(dict);
}
#endif