11 /* extern int dict_unknown_allowed;
12 /* extern int dict_errno;
14 /* void dict_register(dict_name, dict_info)
15 /* const char *dict_name;
18 /* DICT *dict_handle(dict_name)
19 /* const char *dict_name;
21 /* void dict_unregister(dict_name)
22 /* const char *dict_name;
24 /* void dict_update(dict_name, member, value)
25 /* const char *dict_name;
26 /* const char *member;
29 /* const char *dict_lookup(dict_name, member)
30 /* const char *dict_name;
31 /* const char *member;
33 /* int dict_delete(dict_name, member)
34 /* const char *dict_name;
35 /* const char *member;
37 /* int dict_sequence(dict_name, func, member, value)
38 /* const char *dict_name;
40 /* const char **member;
41 /* const char **value;
43 /* const char *dict_eval(dict_name, string, int recursive)
44 /* const char *dict_name;
45 /* const char *string;
48 /* int dict_walk(action, context)
49 /* void (*action)(dict_name, dict_handle, context)
52 /* const char *dict_changed_name()
53 /* AUXILIARY FUNCTIONS
54 /* void dict_load_file(dict_name, path)
55 /* const char *dict_name;
58 /* void dict_load_fp(dict_name, fp)
59 /* const char *dict_name;
62 /* const char *dict_flags_str(dict_flags)
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.
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
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.
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).
91 /* dict_handle() returns the generic dictionary handle of the
92 /* named dictionary, or a null pointer when the named dictionary
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.
100 /* dict_update() updates the value of the named dictionary member.
101 /* The dictionary member and the named dictionary are instantiated
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.
111 /* dict_delete() removes the named member from the named dictionary.
112 /* The result is non-zero when the member does not exist.
114 /* dict_sequence() steps throuh 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.
122 /* dict_eval() expands macro references in the specified string.
123 /* The result is owned by the dictionary manager. Make a copy if the
124 /* result is to survive multiple dict_eval() calls. When the
125 /* \fIrecursive\fR argument is non-zero, macro references in macro
126 /* lookup results are expanded recursively.
128 /* dict_walk() iterates over all registered dictionaries in some
129 /* arbitrary order, and invokes the specified action routine with
131 /* .IP "const char *dict_name"
133 /* .IP "DICT *dict_handle"
134 /* Generic dictionary handle.
135 /* .IP "char *context"
136 /* Application context from the caller.
138 /* dict_changed_name() returns non-zero when any dictionary needs to
139 /* be re-opened because it has changed or because it was unlinked.
140 /* A non-zero result is the name of a changed dictionary.
142 /* dict_load_file() reads name-value entries from the named file.
143 /* Lines that begin with whitespace are concatenated to the preceding
144 /* line (the newline is deleted).
145 /* Each entry is stored in the dictionary named by \fIdict_name\fR.
147 /* dict_load_fp() reads name-value entries from an open stream.
148 /* It has the same semantics as the dict_load_file() function.
150 /* dict_flags_str() returns a printable representation of the
151 /* specified dictionary flags. The result is overwritten upon
157 /* Fatal errors: out of memory, reference to unknown name,
158 /* malformed macro name.
160 /* The lookup routines may set the \fIdict_errno\fR variable when
161 /* they were unable to find the requested result. The lookup
162 /* routines must reset \fIdict_errno\fR before each lookup operation.
163 /* \fIdict_errno\fR can have the following values:
164 /* .IP DICT_ERR_RETRY
165 /* The dictionary was temporarily unavailable. This can happen
166 /* with network-based services.
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 libraries. */
180 #include "sys_defs.h"
181 #include <sys/stat.h>
187 /* Utility library. */
191 #include "mymalloc.h"
194 #include "readlline.h"
195 #include "mac_expand.h"
196 #include "stringops.h"
198 #include "name_mask.h"
203 * By default, use a sane default for an unknown name.
205 int dict_unknown_allowed
= 1;
208 static HTABLE
*dict_table
;
211 * Each (name, dictionary) instance has a reference count. The count is part
212 * of the name, not the dictionary. The same dictionary may be registered
213 * under multiple names. The structure below keeps track of instances and
221 #define dict_node(dict) \
222 (dict_table ? (DICT_NODE *) htable_find(dict_table, dict) : 0)
224 #define STR(x) vstring_str(x)
226 /* dict_register - make association with dictionary */
228 void dict_register(const char *dict_name
, DICT
*dict_info
)
230 const char *myname
= "dict_register";
234 dict_table
= htable_create(0);
235 if ((node
= dict_node(dict_name
)) == 0) {
236 node
= (DICT_NODE
*) mymalloc(sizeof(*node
));
237 node
->dict
= dict_info
;
239 htable_enter(dict_table
, dict_name
, (char *) node
);
240 } else if (dict_info
!= node
->dict
)
241 msg_fatal("%s: dictionary name exists: %s", myname
, dict_name
);
244 msg_info("%s: %s %d", myname
, dict_name
, node
->refcount
);
247 /* dict_handle - locate generic dictionary handle */
249 DICT
*dict_handle(const char *dict_name
)
253 return ((node
= dict_node(dict_name
)) != 0 ? node
->dict
: 0);
256 /* dict_node_free - dict_unregister() callback */
258 static void dict_node_free(char *ptr
)
260 DICT_NODE
*node
= (DICT_NODE
*) ptr
;
261 DICT
*dict
= node
->dict
;
265 myfree((char *) node
);
268 /* dict_unregister - break association with named dictionary */
270 void dict_unregister(const char *dict_name
)
272 const char *myname
= "dict_unregister";
275 if ((node
= dict_node(dict_name
)) == 0)
276 msg_panic("non-existing dictionary: %s", dict_name
);
278 msg_info("%s: %s %d", myname
, dict_name
, node
->refcount
);
279 if (--(node
->refcount
) == 0)
280 htable_delete(dict_table
, dict_name
, dict_node_free
);
283 /* dict_update - replace or add dictionary entry */
285 void dict_update(const char *dict_name
, const char *member
, const char *value
)
287 const char *myname
= "dict_update";
291 if ((node
= dict_node(dict_name
)) == 0) {
292 if (dict_unknown_allowed
== 0)
293 msg_fatal("%s: unknown dictionary: %s", myname
, dict_name
);
294 dict
= dict_ht_open(dict_name
, htable_create(0), myfree
);
295 dict_register(dict_name
, dict
);
299 msg_info("%s: %s = %s", myname
, member
, value
);
300 dict
->update(dict
, member
, value
);
303 /* dict_lookup - look up dictionary entry */
305 const char *dict_lookup(const char *dict_name
, const char *member
)
307 const char *myname
= "dict_lookup";
312 if ((node
= dict_node(dict_name
)) == 0) {
313 if (dict_unknown_allowed
== 0)
314 msg_fatal("%s: unknown dictionary: %s", myname
, dict_name
);
317 ret
= dict
->lookup(dict
, member
);
318 if (ret
== 0 && dict_unknown_allowed
== 0)
319 msg_fatal("dictionary %s: unknown member: %s", dict_name
, member
);
322 msg_info("%s: %s = %s", myname
, member
, ret
? ret
: "(notfound)");
326 /* dict_delete - delete dictionary entry */
328 int dict_delete(const char *dict_name
, const char *member
)
330 const char *myname
= "dict_delete";
335 if ((node
= dict_node(dict_name
)) == 0) {
336 if (dict_unknown_allowed
== 0)
337 msg_fatal("%s: unknown dictionary: %s", myname
, dict_name
);
338 dict
= dict_ht_open(dict_name
, htable_create(0), myfree
);
339 dict_register(dict_name
, dict
);
343 msg_info("%s: delete %s", myname
, member
);
344 if ((result
= dict
->delete(dict
, member
)) != 0 && dict_unknown_allowed
== 0)
345 msg_fatal("%s: dictionary %s: unknown member: %s",
346 myname
, dict_name
, member
);
350 /* dict_sequence - traverse dictionary */
352 int dict_sequence(const char *dict_name
, const int func
,
353 const char **member
, const char **value
)
355 const char *myname
= "dict_sequence";
359 if ((node
= dict_node(dict_name
)) == 0) {
360 if (dict_unknown_allowed
== 0)
361 msg_fatal("%s: unknown dictionary: %s", myname
, dict_name
);
362 dict
= dict_ht_open(dict_name
, htable_create(0), myfree
);
363 dict_register(dict_name
, dict
);
367 msg_info("%s: sequence func %d", myname
, func
);
368 return (dict
->sequence(dict
, func
, member
, value
));
371 /* dict_load_file - read entries from text file */
373 void dict_load_file(const char *dict_name
, const char *path
)
381 * Read the file again if it is hot. This may result in reading a partial
382 * parameter name when a file changes in the middle of a read.
384 for (before
= time((time_t *) 0); /* see below */ ; before
= after
) {
385 if ((fp
= vstream_fopen(path
, O_RDONLY
, 0)) == 0)
386 msg_fatal("open %s: %m", path
);
387 dict_load_fp(dict_name
, fp
);
388 if (fstat(vstream_fileno(fp
), &st
) < 0)
389 msg_fatal("fstat %s: %m", path
);
390 if (vstream_ferror(fp
) || vstream_fclose(fp
))
391 msg_fatal("read %s: %m", path
);
392 after
= time((time_t *) 0);
393 if (st
.st_mtime
< before
- 1 || st
.st_mtime
> after
)
396 msg_info("pausing to let %s cool down", path
);
401 /* dict_load_fp - read entries from open stream */
403 void dict_load_fp(const char *dict_name
, VSTREAM
*fp
)
411 buf
= vstring_alloc(100);
414 while (readlline(buf
, fp
, &lineno
)) {
415 if ((err
= split_nameval(STR(buf
), &member
, &val
)) != 0)
416 msg_fatal("%s, line %d: %s: \"%s\"",
417 VSTREAM_PATH(fp
), lineno
, err
, STR(buf
));
418 dict_update(dict_name
, member
, val
);
423 /* dict_eval_lookup - macro parser call-back routine */
425 static const char *dict_eval_lookup(const char *key
, int unused_type
,
431 * XXX how would one recover?
433 if ((pp
= dict_lookup(dict_name
, key
)) == 0 && dict_errno
!= 0)
434 msg_fatal("dictionary %s: lookup %s: temporary error", dict_name
, key
);
439 /* dict_eval - expand embedded dictionary references */
441 const char *dict_eval(const char *dict_name
, const char *value
, int recursive
)
443 const char *myname
= "dict_eval";
451 buf
= vstring_alloc(10);
454 * Expand macros, possibly recursively.
456 #define DONT_FILTER (char *) 0
458 status
= mac_expand(buf
, value
,
459 recursive
? MAC_EXP_FLAG_RECURSE
: MAC_EXP_FLAG_NONE
,
460 DONT_FILTER
, dict_eval_lookup
, (char *) dict_name
);
461 if (status
& MAC_PARSE_ERROR
)
462 msg_fatal("dictionary %s: macro processing error", dict_name
);
464 if (strcmp(value
, STR(buf
)) != 0)
465 msg_info("%s: expand %s -> %s", myname
, value
, STR(buf
));
467 msg_info("%s: const %s", myname
, value
);
472 /* dict_walk - iterate over all dictionaries in arbitrary order */
474 void dict_walk(DICT_WALK_ACTION action
, char *ptr
)
476 HTABLE_INFO
**ht_info_list
;
480 ht_info_list
= htable_list(dict_table
);
481 for (ht
= ht_info_list
; (h
= *ht
) != 0; ht
++)
482 action(h
->key
, (DICT
*) h
->value
, ptr
);
483 myfree((char *) ht_info_list
);
486 /* dict_changed_name - see if any dictionary has changed */
488 const char *dict_changed_name(void)
490 const char *myname
= "dict_changed_name";
492 HTABLE_INFO
**ht_info_list
;
498 ht_info_list
= htable_list(dict_table
);
499 for (status
= 0, ht
= ht_info_list
; status
== 0 && (h
= *ht
) != 0; ht
++) {
500 dict
= ((DICT_NODE
*) h
->value
)->dict
;
501 if (dict
->stat_fd
< 0) /* not file-based */
503 if (dict
->mtime
== 0) /* not bloody likely */
504 msg_warn("%s: table %s: null time stamp", myname
, h
->key
);
505 if (fstat(dict
->stat_fd
, &st
) < 0)
506 msg_fatal("%s: fstat: %m", myname
);
507 if (st
.st_mtime
!= dict
->mtime
|| st
.st_nlink
== 0)
510 myfree((char *) ht_info_list
);
514 /* dict_changed - backwards compatibility */
516 int dict_changed(void)
518 return (dict_changed_name() != 0);
522 * Mapping between flag names and flag values.
524 static const NAME_MASK dict_mask
[] = {
525 "warn_dup", (1 << 0), /* if file, warn about dups */
526 "ignore_dup", (1 << 1), /* if file, ignore dups */
527 "try0null", (1 << 2), /* do not append 0 to key/value */
528 "try1null", (1 << 3), /* append 0 to key/value */
529 "fixed", (1 << 4), /* fixed key map */
530 "pattern", (1 << 5), /* keys are patterns */
531 "lock", (1 << 6), /* lock before access */
532 "replace", (1 << 7), /* if file, replace dups */
533 "sync_update", (1 << 8), /* if file, sync updates */
534 "debug", (1 << 9), /* log access */
535 "no_regsub", (1 << 11), /* disallow regexp substitution */
536 "no_proxy", (1 << 12), /* disallow proxy mapping */
537 "no_unauth", (1 << 13), /* disallow unauthenticated data */
538 "fold_fix", (1 << 14), /* case-fold with fixed-case key map */
539 "fold_mul", (1 << 15), /* case-fold with multi-case key map */
542 /* dict_flags_str - convert mask to string for debugging purposes */
544 const char *dict_flags_str(int dict_flags
)
546 static VSTRING
*buf
= 0;
549 buf
= vstring_alloc(1);
551 return (str_name_mask_opt(buf
, "dictionary flags", dict_mask
, dict_flags
,
552 NAME_MASK_RETURN
| NAME_MASK_PIPE
));