1/*	$NetBSD: dict.c,v 1.1.1.4 2012/02/17 08:36:21 tron Exp $	*/
2
3/*++
4/* NAME
5/*	dict 3
6/* SUMMARY
7/*	dictionary manager
8/* SYNOPSIS
9/*	#include <dict.h>
10/*
11/*	extern int dict_unknown_allowed;
12/*	extern int dict_errno;
13/*
14/*	void	dict_register(dict_name, dict_info)
15/*	const char *dict_name;
16/*	DICT	*dict_info;
17/*
18/*	DICT	*dict_handle(dict_name)
19/*	const char *dict_name;
20/*
21/*	void	dict_unregister(dict_name)
22/*	const char *dict_name;
23/*
24/*	void	dict_update(dict_name, member, value)
25/*	const char *dict_name;
26/*	const char *member;
27/*	const char *value;
28/*
29/*	const char *dict_lookup(dict_name, member)
30/*	const char *dict_name;
31/*	const char *member;
32/*
33/*	int	dict_delete(dict_name, member)
34/*	const char *dict_name;
35/*	const char *member;
36/*
37/*	int	dict_sequence(dict_name, func, member, value)
38/*	const char *dict_name;
39/*	int	func;
40/*	const char **member;
41/*	const char **value;
42/*
43/*	const char *dict_eval(dict_name, string, int recursive)
44/*	const char *dict_name;
45/*	const char *string;
46/*	int	recursive;
47/*
48/*	int	dict_walk(action, context)
49/*	void	(*action)(dict_name, dict_handle, context)
50/*	char	*context;
51/*
52/*	const char *dict_changed_name()
53/* AUXILIARY FUNCTIONS
54/*	void	dict_load_file(dict_name, path)
55/*	const char *dict_name;
56/*	const char *path;
57/*
58/*	void	dict_load_fp(dict_name, fp)
59/*	const char *dict_name;
60/*	VSTREAM	*fp;
61/*
62/*	const char *dict_flags_str(dict_flags)
63/*	int	dict_flags;
64/* DESCRIPTION
65/*	This module maintains a collection of name-value dictionaries.
66/*	Each dictionary has its own name and has its own methods to read
67/*	or update members. Examples of dictionaries that can be accessed
68/*	in this manner are the global UNIX-style process environment,
69/*	hash tables, NIS maps, DBM files, and so on. Dictionary values
70/*	are not limited to strings but can be arbitrary objects as long
71/*	as they can be represented by character pointers.
72/* FEATURES
73/* .fi
74/* .ad
75/*	Notable features of this module are:
76/* .IP "macro expansion (string-valued dictionaries only)"
77/*	Macros of the form $\fIname\fR can be expanded to the current
78/*	value of \fIname\fR. The forms $(\fIname\fR) and ${\fIname\fR} are
79/*	also supported.
80/* .IP "unknown names"
81/*	References to unknown dictionary or dictionary member names either
82/*	default to an empty dictionary or null pointer value, or cause a
83/*	run-time error. The behavior is controlled by the global
84/*	\fIdict_unknown_allowed\fR boolean variable.
85/* .PP
86/*	dict_register() adds a new dictionary, including access methods,
87/*	to the list of known dictionaries, or increments the reference
88/*	count for an existing (name, dictionary) pair.  Otherwise, it is
89/*	an error to pass an existing name (this would cause a memory leak).
90/*
91/*	dict_handle() returns the generic dictionary handle of the
92/*	named dictionary, or a null pointer when the named dictionary
93/*	is not found.
94/*
95/*	dict_unregister() decrements the reference count of the named
96/*	dictionary. When the reference count reaches zero, dict_unregister()
97/*	breaks the (name, dictionary) association and executes the
98/*	dictionary's optional \fIremove\fR method.
99/*
100/*	dict_update() updates the value of the named dictionary member.
101/*	The dictionary member and the named dictionary are instantiated
102/*	on the fly.
103/*
104/*	dict_lookup() returns the value of the named member (i.e. without
105/*	expanding macros in the member value).  The \fIdict_name\fR argument
106/*	specifies the dictionary to search. The result is a null pointer
107/*	when no value is found, otherwise the result is owned by the
108/*	underlying dictionary method. Make a copy if the result is to be
109/*	modified, or if the result is to survive multiple dict_lookup() calls.
110/*
111/*	dict_delete() removes the named member from the named dictionary.
112/*	The result value is zero when the member was found.
113/*
114/*	dict_sequence() steps through the named dictionary and returns
115/*	keys and values in some implementation-defined order. The func
116/*	argument is DICT_SEQ_FUN_FIRST to set the cursor to the first
117/*	entry or DICT_SEQ_FUN_NEXT to select the next entry. The result
118/*	is owned by the underlying dictionary method. Make a copy if the
119/*	result is to be modified, or if the result is to survive multiple
120/*	dict_sequence() calls. The result value is zero when a member
121/*	was found.
122/*
123/*	dict_eval() expands macro references in the specified string.
124/*	The result is owned by the dictionary manager. Make a copy if the
125/*	result is to survive multiple dict_eval() calls. When the
126/*	\fIrecursive\fR argument is non-zero, macro references in macro
127/*	lookup results are expanded recursively.
128/*
129/*	dict_walk() iterates over all registered dictionaries in some
130/*	arbitrary order, and invokes the specified action routine with
131/*	as arguments:
132/* .IP "const char *dict_name"
133/*	Dictionary name.
134/* .IP "DICT *dict_handle"
135/*	Generic dictionary handle.
136/* .IP "char *context"
137/*	Application context from the caller.
138/* .PP
139/*	dict_changed_name() returns non-zero when any dictionary needs to
140/*	be re-opened because it has changed or because it was unlinked.
141/*	A non-zero result is the name of a changed dictionary.
142/*
143/*	dict_load_file() reads name-value entries from the named file.
144/*	Lines that begin with whitespace are concatenated to the preceding
145/*	line (the newline is deleted).
146/*	Each entry is stored in the dictionary named by \fIdict_name\fR.
147/*
148/*	dict_load_fp() reads name-value entries from an open stream.
149/*	It has the same semantics as the dict_load_file() function.
150/*
151/*	dict_flags_str() returns a printable representation of the
152/*	specified dictionary flags. The result is overwritten upon
153/*	each call.
154/* SEE ALSO
155/*	htable(3)
156/* BUGS
157/* DIAGNOSTICS
158/*	Fatal errors: out of memory, reference to unknown name,
159/*	malformed macro name.
160/*
161/*	The lookup routines may set the \fIdict_errno\fR variable when
162/*	they were unable to find the requested result. The lookup
163/*	routines must reset \fIdict_errno\fR before each lookup operation.
164/*	\fIdict_errno\fR can have the following values:
165/* .IP DICT_ERR_RETRY
166/*	The dictionary was temporarily unavailable. This can happen
167/*	with network-based services.
168/* LICENSE
169/* .ad
170/* .fi
171/*	The Secure Mailer license must be distributed with this software.
172/* AUTHOR(S)
173/*	Wietse Venema
174/*	IBM T.J. Watson Research
175/*	P.O. Box 704
176/*	Yorktown Heights, NY 10598, USA
177/*--*/
178
179/* System libraries. */
180
181#include "sys_defs.h"
182#include <sys/stat.h>
183#include <fcntl.h>
184#include <ctype.h>
185#include <string.h>
186#include <time.h>
187
188/* Utility library. */
189
190#include "msg.h"
191#include "htable.h"
192#include "mymalloc.h"
193#include "vstream.h"
194#include "vstring.h"
195#include "readlline.h"
196#include "mac_expand.h"
197#include "stringops.h"
198#include "iostuff.h"
199#include "name_mask.h"
200#include "dict.h"
201#include "dict_ht.h"
202
203 /*
204  * By default, use a sane default for an unknown name.
205  */
206int     dict_unknown_allowed = 1;
207int     dict_errno = 0;
208
209static HTABLE *dict_table;
210
211 /*
212  * Each (name, dictionary) instance has a reference count. The count is part
213  * of the name, not the dictionary. The same dictionary may be registered
214  * under multiple names. The structure below keeps track of instances and
215  * reference counts.
216  */
217typedef struct {
218    DICT   *dict;
219    int     refcount;
220} DICT_NODE;
221
222#define dict_node(dict) \
223	(dict_table ? (DICT_NODE *) htable_find(dict_table, dict) : 0)
224
225#define STR(x)	vstring_str(x)
226
227/* dict_register - make association with dictionary */
228
229void    dict_register(const char *dict_name, DICT *dict_info)
230{
231    const char *myname = "dict_register";
232    DICT_NODE *node;
233
234    if (dict_table == 0)
235	dict_table = htable_create(0);
236    if ((node = dict_node(dict_name)) == 0) {
237	node = (DICT_NODE *) mymalloc(sizeof(*node));
238	node->dict = dict_info;
239	node->refcount = 0;
240	htable_enter(dict_table, dict_name, (char *) node);
241    } else if (dict_info != node->dict)
242	msg_fatal("%s: dictionary name exists: %s", myname, dict_name);
243    node->refcount++;
244    if (msg_verbose > 1)
245	msg_info("%s: %s %d", myname, dict_name, node->refcount);
246}
247
248/* dict_handle - locate generic dictionary handle */
249
250DICT   *dict_handle(const char *dict_name)
251{
252    DICT_NODE *node;
253
254    return ((node = dict_node(dict_name)) != 0 ? node->dict : 0);
255}
256
257/* dict_node_free - dict_unregister() callback */
258
259static void dict_node_free(char *ptr)
260{
261    DICT_NODE *node = (DICT_NODE *) ptr;
262    DICT   *dict = node->dict;
263
264    if (dict->close)
265	dict->close(dict);
266    myfree((char *) node);
267}
268
269/* dict_unregister - break association with named dictionary */
270
271void    dict_unregister(const char *dict_name)
272{
273    const char *myname = "dict_unregister";
274    DICT_NODE *node;
275
276    if ((node = dict_node(dict_name)) == 0)
277	msg_panic("non-existing dictionary: %s", dict_name);
278    if (msg_verbose > 1)
279	msg_info("%s: %s %d", myname, dict_name, node->refcount);
280    if (--(node->refcount) == 0)
281	htable_delete(dict_table, dict_name, dict_node_free);
282}
283
284/* dict_update - replace or add dictionary entry */
285
286void    dict_update(const char *dict_name, const char *member, const char *value)
287{
288    const char *myname = "dict_update";
289    DICT_NODE *node;
290    DICT   *dict;
291
292    if ((node = dict_node(dict_name)) == 0) {
293	if (dict_unknown_allowed == 0)
294	    msg_fatal("%s: unknown dictionary: %s", myname, dict_name);
295	dict = dict_ht_open(dict_name, O_CREAT | O_RDWR, 0);
296	dict_register(dict_name, dict);
297    } else
298	dict = node->dict;
299    if (msg_verbose > 1)
300	msg_info("%s: %s = %s", myname, member, value);
301    dict->update(dict, member, value);
302}
303
304/* dict_lookup - look up dictionary entry */
305
306const char *dict_lookup(const char *dict_name, const char *member)
307{
308    const char *myname = "dict_lookup";
309    DICT_NODE *node;
310    DICT   *dict;
311    const char *ret = 0;
312
313    if ((node = dict_node(dict_name)) == 0) {
314	if (dict_unknown_allowed == 0)
315	    msg_fatal("%s: unknown dictionary: %s", myname, dict_name);
316    } else {
317	dict = node->dict;
318	ret = dict->lookup(dict, member);
319	if (ret == 0 && dict_unknown_allowed == 0)
320	    msg_fatal("dictionary %s: unknown member: %s", dict_name, member);
321    }
322    if (msg_verbose > 1)
323	msg_info("%s: %s = %s", myname, member, ret ? ret : "(notfound)");
324    return (ret);
325}
326
327/* dict_delete - delete dictionary entry */
328
329int     dict_delete(const char *dict_name, const char *member)
330{
331    const char *myname = "dict_delete";
332    DICT_NODE *node;
333    DICT   *dict;
334    int     result;
335
336    if ((node = dict_node(dict_name)) == 0) {
337	if (dict_unknown_allowed == 0)
338	    msg_fatal("%s: unknown dictionary: %s", myname, dict_name);
339	dict = dict_ht_open(dict_name, O_CREAT | O_RDWR, 0);
340	dict_register(dict_name, dict);
341    } else
342	dict = node->dict;
343    if (msg_verbose > 1)
344	msg_info("%s: delete %s", myname, member);
345    if ((result = dict->delete(dict, member)) != 0 && dict_unknown_allowed == 0)
346	msg_fatal("%s: dictionary %s: unknown member: %s",
347		  myname, dict_name, member);
348    return (result);
349}
350
351/* dict_sequence - traverse dictionary */
352
353int     dict_sequence(const char *dict_name, const int func,
354		              const char **member, const char **value)
355{
356    const char *myname = "dict_sequence";
357    DICT_NODE *node;
358    DICT   *dict;
359
360    if ((node = dict_node(dict_name)) == 0) {
361	if (dict_unknown_allowed == 0)
362	    msg_fatal("%s: unknown dictionary: %s", myname, dict_name);
363	dict = dict_ht_open(dict_name, O_CREAT | O_RDWR, 0);
364	dict_register(dict_name, dict);
365    } else
366	dict = node->dict;
367    if (msg_verbose > 1)
368	msg_info("%s: sequence func %d", myname, func);
369    return (dict->sequence(dict, func, member, value));
370}
371
372/* dict_load_file - read entries from text file */
373
374void    dict_load_file(const char *dict_name, const char *path)
375{
376    VSTREAM *fp;
377    struct stat st;
378    time_t  before;
379    time_t  after;
380
381    /*
382     * Read the file again if it is hot. This may result in reading a partial
383     * parameter name when a file changes in the middle of a read.
384     */
385    for (before = time((time_t *) 0); /* see below */ ; before = after) {
386	if ((fp = vstream_fopen(path, O_RDONLY, 0)) == 0)
387	    msg_fatal("open %s: %m", path);
388	dict_load_fp(dict_name, fp);
389	if (fstat(vstream_fileno(fp), &st) < 0)
390	    msg_fatal("fstat %s: %m", path);
391	if (vstream_ferror(fp) || vstream_fclose(fp))
392	    msg_fatal("read %s: %m", path);
393	after = time((time_t *) 0);
394	if (st.st_mtime < before - 1 || st.st_mtime > after)
395	    break;
396	if (msg_verbose > 1)
397	    msg_info("pausing to let %s cool down", path);
398	doze(300000);
399    }
400}
401
402/* dict_load_fp - read entries from open stream */
403
404void    dict_load_fp(const char *dict_name, VSTREAM *fp)
405{
406    VSTRING *buf;
407    char   *member;
408    char   *val;
409    int     lineno;
410    const char *err;
411
412    buf = vstring_alloc(100);
413    lineno = 0;
414
415    while (readlline(buf, fp, &lineno)) {
416	if ((err = split_nameval(STR(buf), &member, &val)) != 0)
417	    msg_fatal("%s, line %d: %s: \"%s\"",
418		      VSTREAM_PATH(fp), lineno, err, STR(buf));
419	dict_update(dict_name, member, val);
420    }
421    vstring_free(buf);
422}
423
424/* dict_eval_lookup - macro parser call-back routine */
425
426static const char *dict_eval_lookup(const char *key, int unused_type,
427				            char *dict_name)
428{
429    const char *pp;
430
431    /*
432     * XXX how would one recover?
433     */
434    if ((pp = dict_lookup(dict_name, key)) == 0 && dict_errno != 0)
435	msg_fatal("dictionary %s: lookup %s: temporary error", dict_name, key);
436
437    return (pp);
438}
439
440/* dict_eval - expand embedded dictionary references */
441
442const char *dict_eval(const char *dict_name, const char *value, int recursive)
443{
444    const char *myname = "dict_eval";
445    static VSTRING *buf;
446    int     status;
447
448    /*
449     * Initialize.
450     */
451    if (buf == 0)
452	buf = vstring_alloc(10);
453
454    /*
455     * Expand macros, possibly recursively.
456     */
457#define DONT_FILTER (char *) 0
458
459    status = mac_expand(buf, value,
460			recursive ? MAC_EXP_FLAG_RECURSE : MAC_EXP_FLAG_NONE,
461			DONT_FILTER, dict_eval_lookup, (char *) dict_name);
462    if (status & MAC_PARSE_ERROR)
463	msg_fatal("dictionary %s: macro processing error", dict_name);
464    if (msg_verbose > 1) {
465	if (strcmp(value, STR(buf)) != 0)
466	    msg_info("%s: expand %s -> %s", myname, value, STR(buf));
467	else
468	    msg_info("%s: const  %s", myname, value);
469    }
470    return (STR(buf));
471}
472
473/* dict_walk - iterate over all dictionaries in arbitrary order */
474
475void    dict_walk(DICT_WALK_ACTION action, char *ptr)
476{
477    HTABLE_INFO **ht_info_list;
478    HTABLE_INFO **ht;
479    HTABLE_INFO *h;
480
481    ht_info_list = htable_list(dict_table);
482    for (ht = ht_info_list; (h = *ht) != 0; ht++)
483	action(h->key, (DICT *) h->value, ptr);
484    myfree((char *) ht_info_list);
485}
486
487/* dict_changed_name - see if any dictionary has changed */
488
489const char *dict_changed_name(void)
490{
491    const char *myname = "dict_changed_name";
492    struct stat st;
493    HTABLE_INFO **ht_info_list;
494    HTABLE_INFO **ht;
495    HTABLE_INFO *h;
496    const char *status;
497    DICT   *dict;
498
499    ht_info_list = htable_list(dict_table);
500    for (status = 0, ht = ht_info_list; status == 0 && (h = *ht) != 0; ht++) {
501	dict = ((DICT_NODE *) h->value)->dict;
502	if (dict->stat_fd < 0)			/* not file-based */
503	    continue;
504	if (dict->mtime == 0)			/* not bloody likely */
505	    msg_warn("%s: table %s: null time stamp", myname, h->key);
506	if (fstat(dict->stat_fd, &st) < 0)
507	    msg_fatal("%s: fstat: %m", myname);
508	if (st.st_mtime != dict->mtime || st.st_nlink == 0)
509	    status = h->key;
510    }
511    myfree((char *) ht_info_list);
512    return (status);
513}
514
515/* dict_changed - backwards compatibility */
516
517int     dict_changed(void)
518{
519    return (dict_changed_name() != 0);
520}
521
522 /*
523  * Mapping between flag names and flag values.
524  */
525static const NAME_MASK dict_mask[] = {
526    "warn_dup", (1 << 0),		/* if file, warn about dups */
527    "ignore_dup", (1 << 1),		/* if file, ignore dups */
528    "try0null", (1 << 2),		/* do not append 0 to key/value */
529    "try1null", (1 << 3),		/* append 0 to key/value */
530    "fixed", (1 << 4),			/* fixed key map */
531    "pattern", (1 << 5),		/* keys are patterns */
532    "lock", (1 << 6),			/* lock before access */
533    "replace", (1 << 7),		/* if file, replace dups */
534    "sync_update", (1 << 8),		/* if file, sync updates */
535    "debug", (1 << 9),			/* log access */
536    "no_regsub", (1 << 11),		/* disallow regexp substitution */
537    "no_proxy", (1 << 12),		/* disallow proxy mapping */
538    "no_unauth", (1 << 13),		/* disallow unauthenticated data */
539    "fold_fix", (1 << 14),		/* case-fold with fixed-case key map */
540    "fold_mul", (1 << 15),		/* case-fold with multi-case key map */
541    "open_lock", (1 << 16),		/* permanent lock upon open */
542    0,
543};
544
545/* dict_flags_str - convert mask to string for debugging purposes */
546
547const char *dict_flags_str(int dict_flags)
548{
549    static VSTRING *buf = 0;
550
551    if (buf == 0)
552	buf = vstring_alloc(1);
553
554    return (str_name_mask_opt(buf, "dictionary flags", dict_mask, dict_flags,
555			      NAME_MASK_RETURN | NAME_MASK_PIPE));
556}
557