7 /* low-level dictionary interface
11 /* DICT *dict_open(dict_spec, open_flags, dict_flags)
12 /* const char *dict_spec;
16 /* DICT *dict_open3(dict_type, dict_name, open_flags, dict_flags)
17 /* const char *dict_type;
18 /* const char *dict_name;
22 /* void dict_put(dict, key, value)
27 /* const char *dict_get(dict, key)
31 /* int dict_del(dict, key)
35 /* void dict_seq(dict, func, key, value)
39 /* const char **value;
41 /* void dict_close(dict)
44 /* dict_open_register(type, open)
46 /* DICT *(*open) (const char *, int, int);
48 /* ARGV *dict_mapnames()
50 /* This module implements a low-level interface to multiple
51 /* physical dictionary types.
53 /* dict_open() takes a type:name pair that specifies a dictionary type
54 /* and dictionary name, opens the dictionary, and returns a dictionary
55 /* handle. The \fIopen_flags\fR arguments are as in open(2). The
56 /* \fIdict_flags\fR are the bit-wise OR of zero or more of the following:
57 /* .IP DICT_FLAG_DUP_WARN
58 /* Warn about duplicate keys, if the underlying database does not
59 /* support duplicate keys. The default is to terminate with a fatal
61 /* .IP DICT_FLAG_DUP_IGNORE
62 /* Ignore duplicate keys if the underlying database does not
63 /* support duplicate keys. The default is to terminate with a fatal
65 /* .IP DICT_FLAG_DUP_REPLACE
66 /* Replace duplicate keys if the underlying database supports such
67 /* an operation. The default is to terminate with a fatal error.
68 /* .IP DICT_FLAG_TRY0NULL
69 /* With maps where this is appropriate, append no null byte to
71 /* When neither DICT_FLAG_TRY0NULL nor DICT_FLAG_TRY1NULL are
72 /* specified, the software guesses what format to use for reading;
73 /* and in the absence of definite information, a system-dependent
74 /* default is chosen for writing.
75 /* .IP DICT_FLAG_TRY1NULL
76 /* With maps where this is appropriate, append one null byte to
78 /* When neither DICT_FLAG_TRY0NULL nor DICT_FLAG_TRY1NULL are
79 /* specified, the software guesses what format to use for reading;
80 /* and in the absence of definite information, a system-dependent
81 /* default is chosen for writing.
83 /* With maps where this is appropriate, acquire an exclusive lock
84 /* before writing, and acquire a shared lock before reading.
85 /* .IP DICT_FLAG_FOLD_FIX
86 /* With databases whose lookup fields are fixed-case strings,
87 /* fold the search key to lower case before accessing the
88 /* database. This includes hash:, cdb:, dbm:. nis:, ldap:,
90 /* .IP DICT_FLAG_FOLD_MUL
91 /* With databases where one lookup field can match both upper
92 /* and lower case, fold the search key to lower case before
93 /* accessing the database. This includes regexp: and pcre:
94 /* .IP DICT_FLAG_FOLD_ANY
95 /* Short-hand for (DICT_FLAG_FOLD_FIX | DICT_FLAG_FOLD_MUL).
96 /* .IP DICT_FLAG_SYNC_UPDATE
97 /* With file-based maps, flush I/O buffers to file after each update.
98 /* Thus feature is not supported with some file-based dictionaries.
99 /* .IP DICT_FLAG_NO_REGSUB
100 /* Disallow regular expression substitution from left-hand side data
101 /* into the right-hand side.
102 /* .IP DICT_FLAG_NO_PROXY
103 /* Disallow access through the \fBproxymap\fR service.
104 /* .IP DICT_FLAG_NO_UNAUTH
105 /* Disallow network lookup mechanisms that lack any form of
106 /* authentication (example: tcp_table; even NIS can be secured
107 /* to some extent by requiring that the server binds to a
109 /* .IP DICT_FLAG_PARANOID
110 /* A combination of all the paranoia flags: DICT_FLAG_NO_REGSUB,
111 /* DICT_FLAG_NO_PROXY and DICT_FLAG_NO_UNAUTH.
113 /* Specify DICT_FLAG_NONE for no special processing.
115 /* The dictionary types are as follows:
117 /* The process environment array. The \fIdict_name\fR argument is ignored.
121 /* Berkeley DB file in hash format.
123 /* Berkeley DB file in btree format.
125 /* NIS map. Only read access is supported.
127 /* NIS+ map. Only read access is supported.
129 /* NetInfo table. Only read access is supported.
131 /* LDAP ("light-weight" directory access protocol) database access.
133 /* PERL-compatible regular expressions.
135 /* POSIX-compatible regular expressions.
137 /* dict_open3() takes separate arguments for dictionary type and
138 /* name, but otherwise performs the same functions as dict_open().
140 /* dict_get() retrieves the value stored in the named dictionary
141 /* under the given key. A null pointer means the value was not found.
142 /* As with dict_lookup(), the result is owned by the lookup table
143 /* implementation. Make a copy if the result is to be modified,
144 /* or if the result is to survive multiple table lookups.
146 /* dict_put() stores the specified key and value into the named
149 /* dict_del() removes a dictionary entry, and returns non-zero
150 /* in case of success.
152 /* dict_seq() iterates over all members in the named dictionary.
153 /* func is define DICT_SEQ_FUN_FIRST (select first member) or
154 /* DICT_SEQ_FUN_NEXT (select next member). A null result means
157 /* dict_close() closes the specified dictionary and cleans up the
158 /* associated data structures.
160 /* dict_open_register() adds support for a new dictionary type.
162 /* dict_mapnames() returns a sorted list with the names of all available
165 /* Fatal error: open error, unsupported dictionary type, attempt to
166 /* update non-writable dictionary.
170 /* The Secure Mailer license must be distributed with this software.
173 /* IBM T.J. Watson Research
175 /* Yorktown Heights, NY 10598, USA
178 /* System library. */
180 #include <sys_defs.h>
184 #ifdef STRCASECMP_IN_STRINGS_H
188 /* Utility library. */
191 #include <mymalloc.h>
194 #include <dict_cdb.h>
195 #include <dict_env.h>
196 #include <dict_unix.h>
197 #include <dict_tcp.h>
198 #include <dict_sdbm.h>
199 #include <dict_dbm.h>
201 #include <dict_nis.h>
202 #include <dict_nisplus.h>
204 #include <dict_pcre.h>
205 #include <dict_regexp.h>
206 #include <dict_static.h>
207 #include <dict_cidr.h>
208 #include <stringops.h>
209 #include <split_at.h>
213 * lookup table for available map types.
217 struct DICT
*(*open
) (const char *, int, int);
220 static const DICT_OPEN_INFO dict_open_info
[] = {
222 DICT_TYPE_CDB
, dict_cdb_open
,
224 DICT_TYPE_ENVIRON
, dict_env_open
,
225 DICT_TYPE_UNIX
, dict_unix_open
,
227 DICT_TYPE_TCP
, dict_tcp_open
,
230 DICT_TYPE_SDBM
, dict_sdbm_open
,
233 DICT_TYPE_DBM
, dict_dbm_open
,
236 DICT_TYPE_HASH
, dict_hash_open
,
237 DICT_TYPE_BTREE
, dict_btree_open
,
240 DICT_TYPE_NIS
, dict_nis_open
,
243 DICT_TYPE_NISPLUS
, dict_nisplus_open
,
246 DICT_TYPE_NETINFO
, dict_ni_open
,
249 DICT_TYPE_PCRE
, dict_pcre_open
,
251 #ifdef HAS_POSIX_REGEXP
252 DICT_TYPE_REGEXP
, dict_regexp_open
,
254 DICT_TYPE_STATIC
, dict_static_open
,
255 DICT_TYPE_CIDR
, dict_cidr_open
,
259 static HTABLE
*dict_open_hash
;
261 /* dict_open_init - one-off initialization */
263 static void dict_open_init(void)
265 const char *myname
= "dict_open_init";
266 const DICT_OPEN_INFO
*dp
;
268 if (dict_open_hash
!= 0)
269 msg_panic("%s: multiple initialization", myname
);
270 dict_open_hash
= htable_create(10);
272 for (dp
= dict_open_info
; dp
->type
; dp
++)
273 htable_enter(dict_open_hash
, dp
->type
, (char *) dp
);
276 /* dict_open - open dictionary */
278 DICT
*dict_open(const char *dict_spec
, int open_flags
, int dict_flags
)
280 char *saved_dict_spec
= mystrdup(dict_spec
);
284 if ((dict_name
= split_at(saved_dict_spec
, ':')) == 0)
285 msg_fatal("open dictionary: expecting \"type:name\" form instead of \"%s\"",
288 dict
= dict_open3(saved_dict_spec
, dict_name
, open_flags
, dict_flags
);
289 myfree(saved_dict_spec
);
294 /* dict_open3 - open dictionary */
296 DICT
*dict_open3(const char *dict_type
, const char *dict_name
,
297 int open_flags
, int dict_flags
)
299 const char *myname
= "dict_open";
303 if (*dict_type
== 0 || *dict_name
== 0)
304 msg_fatal("open dictionary: expecting \"type:name\" form instead of \"%s:%s\"",
305 dict_type
, dict_name
);
306 if (dict_open_hash
== 0)
308 if ((dp
= (DICT_OPEN_INFO
*) htable_find(dict_open_hash
, dict_type
)) == 0)
309 msg_fatal("unsupported dictionary type: %s", dict_type
);
310 if ((dict
= dp
->open(dict_name
, open_flags
, dict_flags
)) == 0)
311 msg_fatal("opening %s:%s %m", dict_type
, dict_name
);
313 msg_info("%s: %s:%s", myname
, dict_type
, dict_name
);
317 /* dict_open_register - register dictionary type */
319 void dict_open_register(const char *type
,
320 DICT
*(*open
) (const char *, int, int))
322 const char *myname
= "dict_open_register";
325 if (dict_open_hash
== 0)
327 if (htable_find(dict_open_hash
, type
))
328 msg_panic("%s: dictionary type exists: %s", myname
, type
);
329 dp
= (DICT_OPEN_INFO
*) mymalloc(sizeof(*dp
));
330 dp
->type
= mystrdup(type
);
332 htable_enter(dict_open_hash
, dp
->type
, (char *) dp
);
335 /* dict_sort_alpha_cpp - qsort() callback */
337 static int dict_sort_alpha_cpp(const void *a
, const void *b
)
339 return (strcmp(((char **) a
)[0], ((char **) b
)[0]));
342 /* dict_mapnames - return an ARGV of available map_names */
344 ARGV
*dict_mapnames()
346 HTABLE_INFO
**ht_info
;
351 if (dict_open_hash
== 0)
353 mapnames
= argv_alloc(dict_open_hash
->used
+ 1);
354 for (ht_info
= ht
= htable_list(dict_open_hash
); *ht
; ht
++) {
355 dp
= (DICT_OPEN_INFO
*) ht
[0]->value
;
356 argv_add(mapnames
, dp
->type
, ARGV_END
);
358 qsort((void *) mapnames
->argv
, mapnames
->argc
, sizeof(mapnames
->argv
[0]),
359 dict_sort_alpha_cpp
);
360 myfree((char *) ht_info
);
361 argv_terminate(mapnames
);
368 * Proof-of-concept test program. Create, update or read a database. When
369 * the input is a name=value pair, the database is updated, otherwise the
370 * program assumes that the input specifies a lookup key and prints the
371 * corresponding value.
374 /* System library. */
381 /* Utility library. */
385 #include "msg_vstream.h"
386 #include "vstring_vstream.h"
388 static NORETURN
usage(char *myname
)
390 msg_fatal("usage: %s type:file read|write|create [fold]", myname
);
393 int main(int argc
, char **argv
)
395 VSTRING
*keybuf
= vstring_alloc(1);
396 VSTRING
*inbuf
= vstring_alloc(1);
405 int dict_flags
= DICT_FLAG_LOCK
| DICT_FLAG_DUP_REPLACE
;
407 signal(SIGPIPE
, SIG_IGN
);
409 msg_vstream_init(argv
[0], VSTREAM_ERR
);
410 while ((ch
= GETOPT(argc
, argv
, "v")) > 0) {
420 if (argc
- optind
< 2 || argc
- optind
> 3)
422 if (strcasecmp(argv
[optind
+ 1], "create") == 0)
423 open_flags
= O_CREAT
| O_RDWR
| O_TRUNC
;
424 else if (strcasecmp(argv
[optind
+ 1], "write") == 0)
426 else if (strcasecmp(argv
[optind
+ 1], "read") == 0)
427 open_flags
= O_RDONLY
;
429 msg_fatal("unknown access mode: %s", argv
[2]);
430 if (argv
[optind
+ 2] && strcasecmp(argv
[optind
+ 2], "fold") == 0)
431 dict_flags
|= DICT_FLAG_FOLD_ANY
;
432 dict_name
= argv
[optind
];
433 dict
= dict_open(dict_name
, open_flags
, dict_flags
);
434 dict_register(dict_name
, dict
);
435 while (vstring_fgets_nonl(inbuf
, VSTREAM_IN
)) {
436 bufp
= vstring_str(inbuf
);
438 vstream_printf("> %s\n", bufp
);
439 vstream_fflush(VSTREAM_OUT
);
443 if ((cmd
= mystrtok(&bufp
, " ")) == 0) {
444 vstream_printf("usage: del key|get key|put key=value|first|next\n");
445 vstream_fflush(VSTREAM_OUT
);
448 if (dict_changed_name())
449 msg_warn("dictionary has changed");
450 key
= *bufp
? vstring_str(unescape(keybuf
, mystrtok(&bufp
, " ="))) : 0;
451 value
= mystrtok(&bufp
, " =");
452 if (strcmp(cmd
, "del") == 0 && key
&& !value
) {
453 if (dict_del(dict
, key
))
454 vstream_printf("%s: not found\n", key
);
456 vstream_printf("%s: deleted\n", key
);
457 } else if (strcmp(cmd
, "get") == 0 && key
&& !value
) {
458 if ((value
= dict_get(dict
, key
)) == 0) {
459 vstream_printf("%s: %s\n", key
,
460 dict_errno
== DICT_ERR_RETRY
?
461 "soft error" : "not found");
463 vstream_printf("%s=%s\n", key
, value
);
465 } else if (strcmp(cmd
, "put") == 0 && key
&& value
) {
466 dict_put(dict
, key
, value
);
467 vstream_printf("%s=%s\n", key
, value
);
468 } else if (strcmp(cmd
, "first") == 0 && !key
&& !value
) {
469 if (dict_seq(dict
, DICT_SEQ_FUN_FIRST
, &key
, &value
) == 0)
470 vstream_printf("%s=%s\n", key
, value
);
472 vstream_printf("%s\n",
473 dict_errno
== DICT_ERR_RETRY
?
474 "soft error" : "not found");
475 } else if (strcmp(cmd
, "next") == 0 && !key
&& !value
) {
476 if (dict_seq(dict
, DICT_SEQ_FUN_NEXT
, &key
, &value
) == 0)
477 vstream_printf("%s=%s\n", key
, value
);
479 vstream_printf("%s\n",
480 dict_errno
== DICT_ERR_RETRY
?
481 "soft error" : "not found");
483 vstream_printf("usage: del key|get key|put key=value|first|next\n");
485 vstream_fflush(VSTREAM_OUT
);
487 vstring_free(keybuf
);