-TDICT_DEBUG
-TDICT_ENV
-TDICT_FAIL
+-TDICT_FINAL_WRAPPER
-TDICT_HT
-TDICT_INLINE
-TDICT_LDAP
-TDICT_THASH
-TDICT_UNION
-TDICT_UNIX
--TDICT_UTF8_BACKUP
+-TDICT_WRAPPER
-TDNS_FIXED
-TDNS_REPLY
-TDNS_RR
postscreen_post_queue_limit, and attributed the wrong reject
message to the postscreen_pre_queue_limit. Problem reported
by Michael Orlitzky. File: proto/POSTSCREEN_README.html.
+
+ Compatibility: removed support for OpenSSL 1.0.1 and earlier.
+
+ Feature: TLS support for client-side and server-side SNI
+ in the Postfix SMTP server, SMTP client, and tlsproxy.
+
+20181228
+
+ Cleanup: generic wrapper infrastructure for Postfix maps
+ (dictionaries) that is the basis for UTF8 checks and for
+ base64 decoding of lookup results. Files: global/mkmap_open.c,
+ postmap/postmap.c, util/dict.c, util/dict.h, util/dict_alloc.c,
+ util/dict_file.c, util/dict_open.c, util/dict_pipe.c,
+ util/dict_union.c, util/dict_utf8.c, util/dict_wrapper.c.
* Patches change both the patchlevel and the release date. Snapshots have no
* patchlevel; they change the release date only.
*/
-#define MAIL_RELEASE_DATE "20181226"
+#define MAIL_RELEASE_DATE "20181228"
#define MAIL_VERSION_NUMBER "3.4"
#ifdef SNAPSHOT
char **map_name;
const char *expansion;
DICT *dict;
- int rhs_is_file;
-
- /*
- * For now, handled at this layer, rather rather than implicitly in
- * dict_get().
- */
- rhs_is_file = flags & DICT_FLAG_SRC_RHS_IS_FILE;
- flags &= ~DICT_FLAG_SRC_RHS_IS_FILE;
/*
* In case of return without map lookup (empty name or no maps).
maps->error = DICT_ERR_RETRY;
return (0);
}
- /* Log raw value, prior to base64 decoding */
if (msg_verbose)
msg_info("%s: %s: %s: %s = %s", myname, maps->title,
*map_name, name, expansion);
- if (rhs_is_file) {
- VSTRING *unb64;
- char *err;
-
- if ((unb64 = dict_file_from_b64(dict, expansion)) == 0) {
- err = dict_file_get_error(dict);
- msg_warn("table %s:%s: key %s: %s",
- dict->type, dict->name, name, err);
- maps->error = DICT_ERR_RETRY;
- return (0);
- }
- expansion = vstring_str(unb64);
- }
return (expansion);
} else if ((maps->error = dict->error) != 0) {
msg_warn("%s:%s lookup error for \"%.100s\"",
*/
if ((mkmap->dict->flags & DICT_FLAG_UTF8_ACTIVE) == 0
&& DICT_NEED_UTF8_ACTIVATION(util_utf8_enable, dict_flags))
- mkmap->dict = dict_utf8_activate(mkmap->dict);
+ dict_utf8_wrapper_activate(mkmap->dict);
+
+ /* Insert wrapper for base64 decoding file content. */
+ if ((mkmap->dict->flags & DICT_FLAG_UNB64_ACTIVE) == 0
+ && (mkmap->dict->flags & DICT_FLAG_SRC_RHS_IS_FILE) != 0)
+ dict_file_wrapper_activate(mkmap->dict);
/*
* Resume signal delivery if multi-writer safe.
file-2 this-is-file2
this-is-file1
this-is-file2
-postmap: fatal: table hash:file_test_map.db: key entry-4: malformed BASE64 value: postmap-entry-4
+postmap: warning: table hash:file_test_map.db: key entry-4: malformed BASE64 value: postmap-entry-4
+postmap: fatal: table hash:file_test_map.db: query error: No such file or directory
msg_warn("table %s:%s should return NO RESULT in case of NOT FOUND",
dicts[n]->type, dicts[n]->name);
}
+#if 0
if (dict_flags & DICT_FLAG_SRC_RHS_IS_FILE) {
VSTRING *unb64;
char *err;
}
value = STR(unb64);
}
+#endif
vstream_printf("%s %s\n", STR(keybuf), value);
found = 1;
break;
msg_warn("table %s:%s should return NO RESULT in case of NOT FOUND",
map_type, map_name);
}
+#if 0
if (dict_flags & DICT_FLAG_SRC_RHS_IS_FILE) {
VSTRING *unb64;
char *err;
}
value = STR(unb64);
}
+#endif
vstream_printf("%s\n", value);
}
if (dict->error)
msg_warn("table %s:%s should return NO RESULT in case of NOT FOUND",
map_type, map_name);
}
+#if 1
if (dict_flags & DICT_FLAG_SRC_RHS_IS_FILE) {
VSTRING *unb64;
char *err;
}
value = STR(unb64);
}
+#endif
vstream_printf("%s %s\n", key, value);
}
if (dict->error)
/* use_chain - load cert, key and chain into ctx or ssl */
-#if OPENSSL_VERSION_NUMBER >= 0x1010000fUL
+#if OPENSSL_VERSION_NUMBER >= 0x1010100fUL
static int use_chain(pem_load_state *st)
{
int ret;
#else
-/* Legacy OpenSSL 1.0.2 interface */
+/* Legacy OpenSSL 1.0.2 and 1.1.0 interface */
static int use_chain(pem_load_state *st)
{
int ret = 1;
poll_fd.c timecmp.c slmdb.c dict_pipe.c dict_random.c \
valid_utf8_hostname.c midna_domain.c argv_splitq.c balpar.c dict_union.c \
extpar.c dict_inline.c casefold.c dict_utf8.c strcasecmp_utf8.c \
- split_qnameval.c argv_attr_print.c argv_attr_scan.c dict_file.c
+ split_qnameval.c argv_attr_print.c argv_attr_scan.c dict_file.c \
+ dict_wrapper.c
OBJS = alldig.o allprint.o argv.o argv_split.o attr_clnt.o attr_print0.o \
attr_print64.o attr_print_plain.o attr_scan0.o attr_scan64.o \
attr_scan_plain.o auto_clnt.o base64_code.o basename.o binhash.o \
poll_fd.o timecmp.o $(NON_PLUGIN_MAP_OBJ) dict_pipe.o dict_random.o \
valid_utf8_hostname.o midna_domain.o argv_splitq.o balpar.o dict_union.o \
extpar.o dict_inline.o casefold.o dict_utf8.o strcasecmp_utf8.o \
- split_qnameval.o argv_attr_print.o argv_attr_scan.o dict_file.o
+ split_qnameval.o argv_attr_print.o argv_attr_scan.o dict_file.o \
+ dict_wrapper.o
# MAP_OBJ is for maps that may be dynamically loaded with dynamicmaps.cf.
# When hard-linking these, makedefs sets NON_PLUGIN_MAP_OBJ=$(MAP_OBJ),
# otherwise it sets the PLUGIN_* macros.
dict_ht.o: vbuf.h
dict_ht.o: vstream.h
dict_ht.o: vstring.h
-dict_inline.cdict_static.o: dict_inline.cdict_static.c
dict_inline.o: argv.h
dict_inline.o: check_arg.h
dict_inline.o: dict.h
dict_utf8.o: vbuf.h
dict_utf8.o: vstream.h
dict_utf8.o: vstring.h
+dict_wrapper.o: argv.h
+dict_wrapper.o: check_arg.h
+dict_wrapper.o: dict.h
+dict_wrapper.o: dict_wrapper.c
+dict_wrapper.o: msg.h
+dict_wrapper.o: myflock.h
+dict_wrapper.o: mymalloc.h
+dict_wrapper.o: stringops.h
+dict_wrapper.o: sys_defs.h
+dict_wrapper.o: vbuf.h
+dict_wrapper.o: vstream.h
+dict_wrapper.o: vstring.h
dir_forest.o: check_arg.h
dir_forest.o: dir_forest.c
dir_forest.o: dir_forest.h
load_file.o: vstream.h
load_file.o: warn_stat.h
load_lib.o: load_lib.c
-load_lib.o: load_lib.h
-load_lib.o: msg.h
load_lib.o: sys_defs.h
lowercase.o: check_arg.h
lowercase.o: lowercase.c
"utf8_request", DICT_FLAG_UTF8_REQUEST, /* request UTF-8 activation */
"utf8_active", DICT_FLAG_UTF8_ACTIVE, /* UTF-8 is activated */
"src_rhs_is_file", DICT_FLAG_SRC_RHS_IS_FILE, /* value from file */
+ "unb64_active", DICT_FLAG_UNB64_ACTIVE, /* base64 decode activated */
0,
};
DICT_OWNER owner; /* provenance */
int error; /* last operation only */
DICT_JMP_BUF *jbuf; /* exception handling */
- struct DICT_UTF8_BACKUP *utf8_backup; /* see below */
+ struct DICT_WRAPPER *wrapper; /* see below */
struct VSTRING *file_buf; /* dict_file_to_buf() */
struct VSTRING *file_b64; /* dict_file_to_b64() */
} DICT;
#define DICT_FLAG_UTF8_ACTIVE (1<<20) /* UTF-8 proxy layer is present */
#define DICT_FLAG_SRC_RHS_IS_FILE \
(1<<21) /* Map source RHS is a file */
+#define DICT_FLAG_UNB64_ACTIVE (1<<22) /* File decode proxy layer is present */
#define DICT_FLAG_UTF8_MASK (DICT_FLAG_UTF8_REQUEST)
extern void dict_type_override(DICT *, const char *);
/*
- * Check and convert UTF-8 keys and values.
+ * Wrappers for DICT methods. Usage: create an "trivial" wrapper object with
+ * dict_wrapper_alloc(), then for each method that requires special
+ * processing, specify a pointer to function that calls the 'next' wrapper's
+ * method of the same type, with the 'next' wrapper as the first argument
+ * (the 'self' pointer).
*/
-typedef struct DICT_UTF8_BACKUP {
- const char *(*lookup) (struct DICT *, const char *);
- int (*update) (struct DICT *, const char *, const char *);
- int (*delete) (struct DICT *, const char *);
-} DICT_UTF8_BACKUP;
+typedef struct DICT_WRAPPER {
+ const char *name; /* for literal constant */
+ const char *(*lookup) (struct DICT_WRAPPER *, DICT *, const char *);
+ int (*update) (struct DICT_WRAPPER *, DICT *, const char *, const char *);
+ int (*delete) (struct DICT_WRAPPER *, DICT *, const char *);
+ struct DICT_WRAPPER *next;
+} DICT_WRAPPER;
+
+extern void dict_wrapper_prepend(DICT *, DICT_WRAPPER *);
+extern DICT_WRAPPER *dict_wrapper_alloc(ssize_t);
+extern void dict_wrapper_free(DICT_WRAPPER *);
-extern DICT *dict_utf8_activate(DICT *);
+ /*
+ * Things that build on DICT_WRAPPER.
+ */
+extern void dict_utf8_wrapper_activate(DICT *);
+extern void dict_file_wrapper_activate(DICT *);
/*
* Driver for interactive or scripted tests.
dict->owner.uid = INT_MAX;
dict->error = DICT_ERR_NONE;
dict->jbuf = 0;
- dict->utf8_backup = 0;
+ dict->wrapper = 0;
dict->file_buf = 0;
dict->file_b64 = 0;
return dict;
myfree(dict->name);
if (dict->jbuf)
myfree((void *) dict->jbuf);
- if (dict->utf8_backup)
- myfree((void *) dict->utf8_backup);
+ if (dict->wrapper)
+ dict_wrapper_free(dict->wrapper);
if (dict->file_buf)
vstring_free(dict->file_buf);
if (dict->file_b64)
./dict_open: warning: cidr map dict_cidr_file.map, line 3: open dict_cidr_file3: No such file or directory: skipping this rule
owner=untrusted (uid=USER)
> get 1.1.1.1
-1.1.1.1=dGhpcy1pcy1maWxlMQo=
+1.1.1.1=this-is-file1
+
> get 2.2.2.2
-2.2.2.2=dGhpcy1pcy1maWxlMgo=
+2.2.2.2=this-is-file2
+
> get 3.3.3.3
3.3.3.3: not found
/*
/* void dict_file_purge_buffers(
/* DICT *dict)
+/*
+/* void dict_file_wrapper_activate(
+/* DICT *dict)
/* DESCRIPTION
/* dict_file_to_buf() reads the content of the specified files,
/* with names separated by CHARS_COMMA_SP, while inserting a
/* dict_file_get_error() should be called only after error;
/* it returns a desciption of the problem. Storage is owned
/* by the caller.
+/*
+/* dict_file_wrapper_activate() activates a wrapper that
+/* automatically base64-decodes lookup results.
/* DIAGNOSTICS
/* In case of error the result value is a null pointer, and
/* an error description can be retrieved with dict_file_get_error().
dict->file_b64 = 0;
}
}
+
+/* dict_file_wrapper_lookup - wrap the lookup method */
+
+static const char *dict_file_wrapper_lookup(DICT_WRAPPER *wrapper,
+ DICT *dict, const char *key)
+{
+ DICT_WRAPPER *next_wrapper;
+ const char *res;
+ VSTRING *unb64;
+ char *err;
+
+ next_wrapper = wrapper->next;
+ if ((res = next_wrapper->lookup(next_wrapper, dict, key)) != 0) {
+ if ((unb64 = dict_file_from_b64(dict, res)) == 0) {
+ err = dict_file_get_error(dict);
+ msg_warn("table %s:%s: key %s: %s",
+ dict->type, dict->name, key, err);
+ myfree(err);
+ dict->error = DICT_ERR_CONFIG;
+ res = 0;
+ } else {
+ res = vstring_str(unb64);
+ }
+ }
+ return (res);
+}
+
+/* dict_file_wrapper_activate - wrap the lookup method */
+
+void dict_file_wrapper_activate(DICT *dict)
+{
+ const char myname[] = "dict_file_wrapper_activate";
+ DICT_WRAPPER *wrapper;
+
+ /*
+ * Sanity check.
+ */
+ if ((dict->flags & DICT_FLAG_UNB64_ACTIVE))
+ msg_panic("%s: %s:%s Base64 decoding support is already activated",
+ myname, dict->type, dict->name);
+
+ /*
+ * Interpose on the lookup method.
+ */
+ wrapper = dict_wrapper_alloc(sizeof(*wrapper));
+ wrapper->name = "file";
+ wrapper->lookup = dict_file_wrapper_lookup;
+ dict_wrapper_prepend(dict, wrapper);
+
+ /*
+ * Leave our mark. See sanity check above.
+ */
+ dict->flags |= DICT_FLAG_UNB64_ACTIVE;
+}
foo: error
owner=trusted (uid=2147483647)
> get file1
-file1=dGhpcy1pcy1maWxlMQo=
+file1=this-is-file1
+
> get file2
-file2=dGhpcy1pcy1maWxlMgo=
+file2=this-is-file2
+
> get file3
file3: not found
msg_fatal("%s:%s: unable to get exclusive lock: %m",
dict_type, dict_name);
}
- /* Last step: insert proxy for UTF-8 syntax checks and casefolding. */
+ /* Insert wrapper for UTF-8 syntax checks and casefolding. */
if ((dict->flags & DICT_FLAG_UTF8_ACTIVE) == 0
&& DICT_NEED_UTF8_ACTIVATION(util_utf8_enable, dict_flags))
- dict = dict_utf8_activate(dict);
+ dict_utf8_wrapper_activate(dict);
+
+ /* Insert wrapper for base64 decoding file content. */
+ if ((dict->flags & DICT_FLAG_UNB64_ACTIVE) == 0
+ && dict->flags & DICT_FLAG_SRC_RHS_IS_FILE)
+ dict_file_wrapper_activate(dict);
+
return (dict);
}
./dict_open: warning: pcre map dict_pcre_file.map, line 6: empty pathname list: >>,<<': skipping this rule
owner=untrusted (uid=USER)
> get file1
-file1=dGhpcy1pcy1maWxlMQo=
+file1=this-is-file1
+
> get file2
-file2=dGhpcy1pcy1maWxlMgo=
+file2=this-is-file2
+
> get file3
file3: not found
> get files12
-files12=dGhpcy1pcy1maWxlMQoKdGhpcy1pcy1maWxlMgo=
+files12=this-is-file1
+
+this-is-file2
+
DICT_TYPE_PIPE, name,
DICT_TYPE_PIPE));
if ((dict = dict_handle(dict_type_name)) == 0)
- dict = dict_open(dict_type_name, open_flags, dict_flags);
+ dict = dict_open(dict_type_name, open_flags,
+ dict_flags & ~DICT_FLAG_SRC_RHS_IS_FILE);
dict_register(dict_type_name, dict);
DICT_OWNER_AGGREGATE_UPDATE(aggr_owner, dict->owner);
if (cpp == argv->argv)
foo: error
owner=trusted (uid=0)
> get foo
-foo=dGhpcy1pcy1maWxlMQo=
+foo=this-is-file1
+
> get bar
-bar=dGhpcy1pcy1maWxlMQo=
+bar=this-is-file1
+
./dict_open: warning: regexp map dict_regexp_file.map, line 3: open dict_regexp_file3: No such file or directory: skipping this rule
owner=untrusted (uid=USER)
> get file1
-file1=dGhpcy1pcy1maWxlMQo=
+file1=this-is-file1
+
> get file2
-file2=dGhpcy1pcy1maWxlMgo=
+file2=this-is-file2
+
> get file3
file3: not found
foo: error
owner=trusted (uid=2147483647)
> get file1
-file1=dGhpcy1pcy1maWxlMQo=
+file1=this-is-file1
+
> get file2
-file2=dGhpcy1pcy1maWxlMQo=
+file2=this-is-file1
+
DICT_TYPE_UNION, name,
DICT_TYPE_UNION));
if ((dict = dict_handle(dict_type_name)) == 0)
- dict = dict_open(dict_type_name, open_flags, dict_flags);
+ dict = dict_open(dict_type_name, open_flags,
+ dict_flags & ~DICT_FLAG_SRC_RHS_IS_FILE);
dict_register(dict_type_name, dict);
DICT_OWNER_AGGREGATE_UPDATE(aggr_owner, dict->owner);
if (cpp == argv->argv)
/* SYNOPSIS
/* #include <dict.h>
/*
-/* DICT *dict_utf8_activate(
+/* void dict_utf8_wrapper_activate(
/* DICT *dict)
/* DESCRIPTION
-/* dict_utf8_activate() wraps a dictionary's lookup/update/delete
+/* dict_utf8_wrapper_activate() wraps a dictionary's lookup/update/delete
/* methods with code that enforces UTF-8 checks on keys and
/* values, and that logs a warning when incorrect UTF-8 is
-/* encountered. The original dictionary handle becomes invalid.
+/* encountered.
/*
/* The wrapper code enforces a policy that maximizes application
/* robustness (it avoids the need for new error-handling code
/* skipped while reporting a non-error status, and lookup
/* results that contain a non-UTF-8 value are blocked while
/* reporting a configuration error.
-/* BUGS
-/* dict_utf8_activate() does not nest.
/* LICENSE
/* .ad
/* .fi
/* dict_utf8_lookup - UTF-8 lookup method wrapper */
-static const char *dict_utf8_lookup(DICT *dict, const char *key)
+static const char *dict_utf8_lookup(DICT_WRAPPER *wrapper, DICT *dict,
+ const char *key)
{
- DICT_UTF8_BACKUP *backup;
+ DICT_WRAPPER *next_wrapper;
const char *utf8_err;
const char *fold_res;
const char *value;
*/
saved_flags = (dict->flags & DICT_FLAG_FOLD_ANY);
dict->flags &= ~DICT_FLAG_FOLD_ANY;
- backup = dict->utf8_backup;
- value = backup->lookup(dict, fold_res);
+ next_wrapper = wrapper->next;
+ value = next_wrapper->lookup(next_wrapper, dict, fold_res);
dict->flags |= saved_flags;
/*
/* dict_utf8_update - UTF-8 update method wrapper */
-static int dict_utf8_update(DICT *dict, const char *key, const char *value)
+static int dict_utf8_update(DICT_WRAPPER *wrapper, DICT *dict,
+ const char *key, const char *value)
{
- DICT_UTF8_BACKUP *backup;
+ DICT_WRAPPER *next_wrapper;
const char *utf8_err;
const char *fold_res;
int saved_flags;
else {
saved_flags = (dict->flags & DICT_FLAG_FOLD_ANY);
dict->flags &= ~DICT_FLAG_FOLD_ANY;
- backup = dict->utf8_backup;
- status = backup->update(dict, fold_res, value);
+ next_wrapper = wrapper->next;
+ status = next_wrapper->update(next_wrapper, dict, fold_res, value);
dict->flags |= saved_flags;
return (status);
}
/* dict_utf8_delete - UTF-8 delete method wrapper */
-static int dict_utf8_delete(DICT *dict, const char *key)
+static int dict_utf8_delete(DICT_WRAPPER *wrapper, DICT *dict, const char *key)
{
- DICT_UTF8_BACKUP *backup;
+ DICT_WRAPPER *next_wrapper;
const char *utf8_err;
const char *fold_res;
int saved_flags;
else {
saved_flags = (dict->flags & DICT_FLAG_FOLD_ANY);
dict->flags &= ~DICT_FLAG_FOLD_ANY;
- backup = dict->utf8_backup;
- status = backup->delete(dict, fold_res);
+ next_wrapper = wrapper->next;
+ status = next_wrapper->delete(next_wrapper, dict, fold_res);
dict->flags |= saved_flags;
return (status);
}
}
-/* dict_utf8_activate - wrap a legacy dict object for UTF-8 processing */
+/* dict_utf8_wrapper_activate - wrap legacy dict object for UTF-8 processing */
-DICT *dict_utf8_activate(DICT *dict)
+void dict_utf8_wrapper_activate(DICT *dict)
{
- const char myname[] = "dict_utf8_activate";
- DICT_UTF8_BACKUP *backup;
+ const char myname[] = "dict_utf8_wrapper_activate";
+ DICT_WRAPPER *wrapper;
/*
* Sanity check.
if ((dict->flags & DICT_FLAG_UTF8_REQUEST) == 0)
msg_panic("%s: %s:%s does not request Unicode support",
myname, dict->type, dict->name);
- if ((dict->flags & DICT_FLAG_UTF8_ACTIVE) || dict->utf8_backup != 0)
+ if ((dict->flags & DICT_FLAG_UTF8_ACTIVE))
msg_panic("%s: %s:%s Unicode support is already activated",
myname, dict->type, dict->name);
/*
- * Unlike dict_debug(3) we do not put a proxy dict object in front of the
- * encapsulated object, because then we would have to bidirectionally
- * propagate changes in the data members (errors, flags, jbuf, and so on)
- * between proxy object and encapsulated object.
- *
- * Instead we attach ourselves behind the encapsulated dict object, and
- * redirect some function pointers to ourselves.
+ * Interpose on the lookup/update/delete methods.
*/
- backup = dict->utf8_backup = (DICT_UTF8_BACKUP *) mymalloc(sizeof(*backup));
-
- /*
- * Interpose on the lookup/update/delete methods. It is a conscious
- * decision not to tinker with the iterator or destructor.
- */
- backup->lookup = dict->lookup;
- backup->update = dict->update;
- backup->delete = dict->delete;
-
- dict->lookup = dict_utf8_lookup;
- dict->update = dict_utf8_update;
- dict->delete = dict_utf8_delete;
+ wrapper = dict_wrapper_alloc(sizeof(*wrapper));
+ wrapper->name = "utf8";
+ wrapper->lookup = dict_utf8_lookup;
+ wrapper->update = dict_utf8_update;
+ wrapper->delete = dict_utf8_delete;
+ dict_wrapper_prepend(dict, wrapper);
/*
* Leave our mark. See sanity check above.
*/
dict->flags |= DICT_FLAG_UTF8_ACTIVE;
-
- return (dict);
}
--- /dev/null
+/*++
+/* NAME
+/* dict_wrapper 3
+/* SUMMARY
+/* dictionary method wrappers
+/* SYNOPSIS
+/* #include <dict.h>
+/*
+/* void dict_wrapper_prepend(
+/* DICT *dict,
+/* DICT_WRAPPER *wrapper)
+/*
+/* DICT_WRAPPER *dict_wrapper_alloc(
+/* ssize_t size)
+/*
+/* void dict_wrapper_free(
+/* DICT_WRAPPER *wrapper)
+/* DESCRIPTION
+/* dict_wrapper_prepend() prepends the specified dictionary
+/* lookup/update/delete wrappers to a chain that is evaluated
+/* in reverse order. dict_wrapper_prepend() takes ownership
+/* of the wrapper.
+/*
+/* dict_wrapper_alloc() allocates memory for a dictionary
+/* wrapper object and initializes all wrapper methods to
+/* empty (no override).
+/*
+/* dict_wrapper_free() destroys a chain of dictionary wrappers.
+/* LICENSE
+/* .ad
+/* .fi
+/* The Secure Mailer license must be distributed with this software.
+/* AUTHOR(S)
+/* Wietse Venema
+/* Google, Inc.
+/* 111 8th Avenue
+/* New York, NY 10011, USA
+/*--*/
+
+ /*
+ * System library.
+ */
+#include <sys_defs.h>
+#include <string.h>
+
+ /*
+ * Utility library.
+ */
+#include <msg.h>
+#include <stringops.h>
+#include <dict.h>
+#include <mymalloc.h>
+#include <msg.h>
+
+ /*
+ * The final DICT_WRAPPER is installed first, and also contains the original
+ * DICT's methods.
+ */
+typedef struct {
+ DICT_WRAPPER wrapper; /* parent class, must be first */
+ const char *(*saved_lookup) (DICT *, const char *);
+ int (*saved_update) (DICT *, const char *, const char *);
+ int (*saved_delete) (DICT *, const char *);
+} DICT_FINAL_WRAPPER;
+
+ /*
+ * Functions that override DICT methods, and that call into the head of
+ * the dict wrapper chain.
+ */
+
+/* dict_wrapper_lookup - DICT method to call into wrapper chain head */
+
+static const char *dict_wrapper_lookup(DICT *dict, const char *key)
+{
+ DICT_WRAPPER *head_wrapper = dict->wrapper;
+
+ return (head_wrapper->lookup(head_wrapper, dict, key));
+}
+
+/* dict_wrapper_update - DICT method to call into wrapper chain head */
+
+static int dict_wrapper_update(DICT *dict, const char *key, const char *value)
+{
+ DICT_WRAPPER *head_wrapper = dict->wrapper;
+
+ return (head_wrapper->update(head_wrapper, dict, key, value));
+}
+
+/* dict_wrapper_delete - DICT method to call into wrapper chain head */
+
+static int dict_wrapper_delete(DICT *dict, const char *key)
+{
+ DICT_WRAPPER *head_wrapper = dict->wrapper;
+
+ return (head_wrapper->delete(head_wrapper, dict, key));
+}
+
+ /*
+ * Empty methods for wrappers that override only some methods. These ensure
+ * that the next wrapper's methods are called with the right 'self' pointer.
+ */
+
+/* empty_wrapper_lookup - wrapper method to call into next wrapper */
+
+static const char *empty_wrapper_lookup(DICT_WRAPPER *wrapper, DICT *dict,
+ const char *key)
+{
+ DICT_WRAPPER *next_wrapper = wrapper->next;
+
+ return (next_wrapper->lookup(next_wrapper, dict, key));
+}
+
+/* empty_wrapper_update - wrapper method to call into next wrapper */
+
+static int empty_wrapper_update(DICT_WRAPPER *wrapper, DICT *dict,
+ const char *key, const char *value)
+{
+ DICT_WRAPPER *next_wrapper = wrapper->next;
+
+ return (next_wrapper->update(next_wrapper, dict, key, value));
+}
+
+/* empty_wrapper_delete - wrapper method to call into next wrapper */
+
+static int empty_wrapper_delete(DICT_WRAPPER *wrapper, DICT *dict,
+ const char *key)
+{
+ DICT_WRAPPER *next_wrapper = wrapper->next;
+
+ return (next_wrapper->delete(next_wrapper, dict, key));
+}
+
+ /*
+ * Wrapper methods for the final dict wrapper in the chain. These call into
+ * the saved DICT methods.
+ */
+
+/* final_wrapper_lookup - wrapper method to call saved DICT method */
+
+static const char *final_wrapper_lookup(DICT_WRAPPER *wrapper, DICT *dict,
+ const char *key)
+{
+ DICT_FINAL_WRAPPER *final_wrapper = (DICT_FINAL_WRAPPER *) wrapper;
+
+ return (final_wrapper->saved_lookup(dict, key));
+}
+
+/* final_wrapper_update - wrapper method to call saved DICT method */
+
+static int final_wrapper_update(DICT_WRAPPER *wrapper, DICT *dict,
+ const char *key, const char *value)
+{
+ DICT_FINAL_WRAPPER *final_wrapper = (DICT_FINAL_WRAPPER *) wrapper;
+
+ return (final_wrapper->saved_update(dict, key, value));
+}
+
+/* final_wrapper_delete - wrapper method to call saved DICT method */
+
+static int final_wrapper_delete(DICT_WRAPPER *wrapper, DICT *dict,
+ const char *key)
+{
+ DICT_FINAL_WRAPPER *final_wrapper = (DICT_FINAL_WRAPPER *) wrapper;
+
+ return (final_wrapper->saved_delete(dict, key));
+}
+
+ /*
+ * Finally, the functions that build the wrapper chain.
+ */
+
+/* dict_wrapper_activate - wrap a DICT object for additional processing */
+
+static void dict_wrapper_activate(DICT *dict)
+{
+ const char myname[] = "dict_wrapper_activate";
+ DICT_FINAL_WRAPPER *final_wrapper;
+
+ /*
+ * Sanity check.
+ */
+ if (dict->wrapper != 0)
+ msg_panic("%s: %s:%s wrapper support is already activated",
+ myname, dict->type, dict->name);
+
+ /*
+ * Install the final wrapper object that calls the original DICT's
+ * methods, and redirect DICT's method calls to ourselves. All other
+ * dictionary wrappers will be prepended to a chain that ends in the
+ * final wrapper object.
+ */
+ final_wrapper = (DICT_FINAL_WRAPPER *) mymalloc(sizeof(*final_wrapper));
+ final_wrapper->wrapper.name = "final";
+ final_wrapper->wrapper.lookup = final_wrapper_lookup;
+ final_wrapper->wrapper.update = final_wrapper_update;
+ final_wrapper->wrapper.delete = final_wrapper_delete;
+ final_wrapper->wrapper.next = 0;
+ dict->wrapper = &final_wrapper->wrapper;
+
+ /*
+ * Interpose on the DICT's lookup/update/delete methods.
+ */
+ final_wrapper->saved_lookup = dict->lookup;
+ final_wrapper->saved_update = dict->update;
+ final_wrapper->saved_delete = dict->delete;
+
+ dict->lookup = dict_wrapper_lookup;
+ dict->update = dict_wrapper_update;
+ dict->delete = dict_wrapper_delete;
+}
+
+/* dict_wrapper_alloc - allocate and initialize dictionary wrapper */
+
+DICT_WRAPPER *dict_wrapper_alloc(ssize_t size)
+{
+ DICT_WRAPPER *wrapper;
+
+ wrapper = (DICT_WRAPPER *) mymalloc(size);
+ wrapper->lookup = empty_wrapper_lookup;
+ wrapper->update = empty_wrapper_update;
+ wrapper->delete = empty_wrapper_delete;
+ return (wrapper);
+}
+
+/* dict_wrapper_prepend - prepend dict method overrides */
+
+void dict_wrapper_prepend(DICT *dict, DICT_WRAPPER *wrapper)
+{
+ if (dict->wrapper == 0)
+ dict_wrapper_activate(dict);
+
+ wrapper->next = dict->wrapper;
+ dict->wrapper = wrapper;
+}
+
+/* dict_wrapper_free - wrapper destructor */
+
+void dict_wrapper_free(DICT_WRAPPER *wrapper)
+{
+ if (wrapper->next)
+ dict_wrapper_free(wrapper->next);
+ myfree((void *) wrapper);
+}