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