2
0
mirror of https://github.com/vdukhovni/postfix synced 2025-08-30 21:55:20 +00:00

postfix-3.4-20181229

This commit is contained in:
Wietse Venema
2018-12-29 00:00:00 -05:00
committed by Viktor Dukhovni
parent b15570215e
commit 7c27c10eb2
25 changed files with 227 additions and 462 deletions

View File

@@ -23917,11 +23917,14 @@ Apologies for any names omitted.
message to the postscreen_pre_queue_limit. Problem reported message to the postscreen_pre_queue_limit. Problem reported
by Michael Orlitzky. File: proto/POSTSCREEN_README.html. by Michael Orlitzky. File: proto/POSTSCREEN_README.html.
20181228 20181229
Explicit maps_file_find() and dict_file_lookup() methods
that decode base64 content. Decoding content is not built
into the dict->lookup() method, because that would complicate
the implementation of map nesting (inline, thash), map
composition (pipemap, unionmap), and map proxying. For
consistency, decoding base64 file content is also not built
into the maps_find() method. Files: util/dict.h.
util/dict_file.c, global/maps.[hc], postmap/postmap.c.
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.

View File

@@ -1,8 +1,7 @@
Wish list: Wish list:
Add sequence support to dictionary wrappers, and remove With DICT_FLAG_RHS_IS_FILE, should dict_update() open a
ad-hoc decoding from the postmap command. What about invalid file? base64-encode the value?
base64? Warn and skip?
In smtpd(8) and postscreen(8), set the ehlo_discard_mask In smtpd(8) and postscreen(8), set the ehlo_discard_mask
to ~0 so that STARTTLS, BDAT, DSN, etc. work only for clients to ~0 so that STARTTLS, BDAT, DSN, etc. work only for clients

View File

@@ -20,7 +20,7 @@
* Patches change both the patchlevel and the release date. Snapshots have no * Patches change both the patchlevel and the release date. Snapshots have no
* patchlevel; they change the release date only. * patchlevel; they change the release date only.
*/ */
#define MAIL_RELEASE_DATE "20181228" #define MAIL_RELEASE_DATE "20181229"
#define MAIL_VERSION_NUMBER "3.4" #define MAIL_VERSION_NUMBER "3.4"
#ifdef SNAPSHOT #ifdef SNAPSHOT

View File

@@ -16,6 +16,11 @@
/* const char *key; /* const char *key;
/* int flags; /* int flags;
/* /*
/* const char *maps_file_find(maps, key, flags)
/* MAPS *maps;
/* const char *key;
/* int flags;
/*
/* MAPS *maps_free(maps) /* MAPS *maps_free(maps)
/* MAPS *maps; /* MAPS *maps;
/* DESCRIPTION /* DESCRIPTION
@@ -39,6 +44,10 @@
/* for example, DICT_FLAG_FIXED | DICT_FLAG_PATTERN selects /* for example, DICT_FLAG_FIXED | DICT_FLAG_PATTERN selects
/* dictionaries that have fixed keys or pattern keys. /* dictionaries that have fixed keys or pattern keys.
/* /*
/* maps_file_find() implements maps_find() but also decodes
/* the base64 lookup result. This requires that the maps are
/* opened with DICT_FLAG_SRC_RHS_IS_FILE.
/*
/* maps_free() releases storage claimed by maps_create() /* maps_free() releases storage claimed by maps_create()
/* and conveniently returns a null pointer. /* and conveniently returns a null pointer.
/* /*
@@ -189,15 +198,81 @@ const char *maps_find(MAPS *maps, const char *name, int flags)
maps->title, name); maps->title, name);
msg_warn("%s should return NO RESULT in case of NOT FOUND", msg_warn("%s should return NO RESULT in case of NOT FOUND",
maps->title); maps->title);
maps->error = DICT_ERR_RETRY; maps->error = DICT_ERR_CONFIG;
return (0); return (0);
} }
if (msg_verbose) if (msg_verbose)
msg_info("%s: %s: %s: %s = %s", myname, maps->title, msg_info("%s: %s: %s: %s = %.100s%s", myname, maps->title,
*map_name, name, expansion); *map_name, name, expansion,
strlen(expansion) > 100 ? "..." : "");
return (expansion); return (expansion);
} else if ((maps->error = dict->error) != 0) { } else if ((maps->error = dict->error) != 0) {
msg_warn("%s:%s lookup error for \"%.100s\"", msg_warn("%s:%s lookup error for \"%s\"",
dict->type, dict->name, name);
break;
}
}
if (msg_verbose)
msg_info("%s: %s: %s: %s", myname, maps->title, name, maps->error ?
"search aborted" : "not found");
return (0);
}
/* maps_file_find - search a list of dictionaries and base64 decode */
const char *maps_file_find(MAPS *maps, const char *name, int flags)
{
const char *myname = "maps_file_find";
char **map_name;
const char *expansion;
DICT *dict;
VSTRING *unb64;
char *err;
/*
* In case of return without map lookup (empty name or no maps).
*/
maps->error = 0;
/*
* Temp. workaround, for buggy callers that pass zero-length keys when
* given partial addresses.
*/
if (*name == 0)
return (0);
for (map_name = maps->argv->argv; *map_name; map_name++) {
if ((dict = dict_handle(*map_name)) == 0)
msg_panic("%s: dictionary not found: %s", myname, *map_name);
if ((dict->flags & DICT_FLAG_SRC_RHS_IS_FILE) == 0)
msg_panic("%s: %s: opened without DICT_FLAG_SRC_RHS_IS_FILE",
myname, maps->title);
if (flags != 0 && (dict->flags & flags) == 0)
continue;
if ((expansion = dict_get(dict, name)) != 0) {
if (*expansion == 0) {
msg_warn("%s lookup of %s returns an empty string result",
maps->title, name);
msg_warn("%s should return NO RESULT in case of NOT FOUND",
maps->title);
maps->error = DICT_ERR_CONFIG;
return (0);
}
if (msg_verbose)
msg_info("%s: %s: %s: %s = %.100s%s", myname, maps->title,
*map_name, name, expansion,
strlen(expansion) > 100 ? "..." : "");
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);
myfree(err);
maps->error = DICT_ERR_CONFIG;
return (0);
}
return (vstring_str(unb64));
} else if ((maps->error = dict->error) != 0) {
msg_warn("%s:%s lookup error for \"%s\"",
dict->type, dict->name, name); dict->type, dict->name, name);
break; break;
} }

View File

@@ -27,6 +27,7 @@ typedef struct MAPS {
extern MAPS *maps_create(const char *, const char *, int); extern MAPS *maps_create(const char *, const char *, int);
extern const char *maps_find(MAPS *, const char *, int); extern const char *maps_find(MAPS *, const char *, int);
extern const char *maps_file_find(MAPS *, const char *, int);
extern MAPS *maps_free(MAPS *); extern MAPS *maps_free(MAPS *);
/* LICENSE /* LICENSE

View File

@@ -301,12 +301,7 @@ MKMAP *mkmap_open(const char *type, const char *path,
*/ */
if ((mkmap->dict->flags & DICT_FLAG_UTF8_ACTIVE) == 0 if ((mkmap->dict->flags & DICT_FLAG_UTF8_ACTIVE) == 0
&& DICT_NEED_UTF8_ACTIVATION(util_utf8_enable, dict_flags)) && DICT_NEED_UTF8_ACTIVATION(util_utf8_enable, dict_flags))
dict_utf8_wrapper_activate(mkmap->dict); mkmap->dict = dict_utf8_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. * Resume signal delivery if multi-writer safe.

View File

@@ -11,4 +11,4 @@ file-2 this-is-file2
this-is-file1 this-is-file1
this-is-file2 this-is-file2
postmap: warning: 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 postmap: fatal: table hash:file_test_map.db: query error

View File

@@ -651,34 +651,30 @@ static int postmap_queries(VSTREAM *in, char **maps, const int map_count,
dicts[n] = ((map_name = split_at(maps[n], ':')) != 0 ? dicts[n] = ((map_name = split_at(maps[n], ':')) != 0 ?
dict_open3(maps[n], map_name, O_RDONLY, dict_flags) : dict_open3(maps[n], map_name, O_RDONLY, dict_flags) :
dict_open3(var_db_type, maps[n], O_RDONLY, dict_flags)); dict_open3(var_db_type, maps[n], O_RDONLY, dict_flags));
if ((value = dict_get(dicts[n], STR(keybuf))) != 0) { value = ((dict_flags & DICT_FLAG_SRC_RHS_IS_FILE) ?
dict_file_lookup : dicts[n]->lookup)
(dicts[n], STR(keybuf));
if (value != 0) {
if (*value == 0) { if (*value == 0) {
msg_warn("table %s:%s: key %s: empty string result is not allowed", msg_warn("table %s:%s: key %s: empty string result is not allowed",
dicts[n]->type, dicts[n]->name, STR(keybuf)); dicts[n]->type, dicts[n]->name, STR(keybuf));
msg_warn("table %s:%s should return NO RESULT in case of NOT FOUND", msg_warn("table %s:%s should return NO RESULT in case of NOT FOUND",
dicts[n]->type, dicts[n]->name); dicts[n]->type, dicts[n]->name);
} }
#if 0
if (dict_flags & DICT_FLAG_SRC_RHS_IS_FILE) {
VSTRING *unb64;
char *err;
if ((unb64 = dict_file_from_b64(dicts[n], value)) == 0) {
err = dict_file_get_error(dicts[n]);
msg_fatal("table %s:%s: key %s: %s",
dicts[n]->type, dicts[n]->name,
STR(keybuf), err);
}
value = STR(unb64);
}
#endif
vstream_printf("%s %s\n", STR(keybuf), value); vstream_printf("%s %s\n", STR(keybuf), value);
found = 1; found = 1;
break; break;
} }
if (dicts[n]->error) switch (dicts[n]->error) {
case 0:
break;
case DICT_ERR_CONFIG:
msg_fatal("table %s:%s: query error",
dicts[n]->type, dicts[n]->name);
default:
msg_fatal("table %s:%s: query error: %m", msg_fatal("table %s:%s: query error: %m",
dicts[n]->type, dicts[n]->name); dicts[n]->type, dicts[n]->name);
}
} }
} }
} else { } else {
@@ -752,31 +748,27 @@ static int postmap_query(const char *map_type, const char *map_name,
const char *value; const char *value;
dict = dict_open3(map_type, map_name, O_RDONLY, dict_flags); dict = dict_open3(map_type, map_name, O_RDONLY, dict_flags);
if ((value = dict_get(dict, key)) != 0) { value = ((dict_flags & DICT_FLAG_SRC_RHS_IS_FILE) ?
dict_file_lookup : dict->lookup) (dict, key);
if (value != 0) {
if (*value == 0) { if (*value == 0) {
msg_warn("table %s:%s: key %s: empty string result is not allowed", msg_warn("table %s:%s: key %s: empty string result is not allowed",
map_type, map_name, key); map_type, map_name, key);
msg_warn("table %s:%s should return NO RESULT in case of NOT FOUND", msg_warn("table %s:%s should return NO RESULT in case of NOT FOUND",
map_type, map_name); map_type, map_name);
} }
#if 0
if (dict_flags & DICT_FLAG_SRC_RHS_IS_FILE) {
VSTRING *unb64;
char *err;
if ((unb64 = dict_file_from_b64(dict, value)) == 0) {
err = dict_file_get_error(dict);
msg_fatal("table %s:%s: key %s: %s",
dict->type, dict->name,
key, err);
}
value = STR(unb64);
}
#endif
vstream_printf("%s\n", value); vstream_printf("%s\n", value);
} }
if (dict->error) switch (dict->error) {
msg_fatal("table %s:%s: query error: %m", dict->type, dict->name); case 0:
break;
case DICT_ERR_CONFIG:
msg_fatal("table %s:%s: query error",
dict->type, dict->name);
default:
msg_fatal("table %s:%s: query error: %m",
dict->type, dict->name);
}
vstream_fflush(VSTREAM_OUT); vstream_fflush(VSTREAM_OUT);
dict_close(dict); dict_close(dict);
return (value != 0); return (value != 0);
@@ -885,7 +877,6 @@ static void postmap_seq(const char *map_type, const char *map_name,
msg_warn("table %s:%s should return NO RESULT in case of NOT FOUND", msg_warn("table %s:%s should return NO RESULT in case of NOT FOUND",
map_type, map_name); map_type, map_name);
} }
#if 1
if (dict_flags & DICT_FLAG_SRC_RHS_IS_FILE) { if (dict_flags & DICT_FLAG_SRC_RHS_IS_FILE) {
VSTRING *unb64; VSTRING *unb64;
char *err; char *err;
@@ -900,7 +891,6 @@ static void postmap_seq(const char *map_type, const char *map_name,
} }
value = STR(unb64); value = STR(unb64);
} }
#endif
vstream_printf("%s %s\n", key, value); vstream_printf("%s %s\n", key, value);
} }
if (dict->error) if (dict->error)

View File

@@ -7,7 +7,7 @@
/* #include "smtpd.h" /* #include "smtpd.h"
/* #include "smtpd_sasl_proto.h" /* #include "smtpd_sasl_proto.h"
/* /*
/* void smtpd_sasl_auth_cmd(state, argc, argv) /* int smtpd_sasl_auth_cmd(state, argc, argv)
/* SMTPD_STATE *state; /* SMTPD_STATE *state;
/* int argc; /* int argc;
/* SMTPD_TOKEN *argv; /* SMTPD_TOKEN *argv;

View File

@@ -40,8 +40,7 @@ SRCS = alldig.c allprint.c argv.c argv_split.c attr_clnt.c attr_print0.c \
poll_fd.c timecmp.c slmdb.c dict_pipe.c dict_random.c \ 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 \ 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 \ 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 \ 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_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 \ attr_scan_plain.o auto_clnt.o base64_code.o basename.o binhash.o \
@@ -83,8 +82,7 @@ OBJS = alldig.o allprint.o argv.o argv_split.o attr_clnt.o attr_print0.o \
poll_fd.o timecmp.o $(NON_PLUGIN_MAP_OBJ) dict_pipe.o dict_random.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 \ 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 \ 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. # 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), # When hard-linking these, makedefs sets NON_PLUGIN_MAP_OBJ=$(MAP_OBJ),
# otherwise it sets the PLUGIN_* macros. # otherwise it sets the PLUGIN_* macros.
@@ -1342,6 +1340,7 @@ dict_ht.o: sys_defs.h
dict_ht.o: vbuf.h dict_ht.o: vbuf.h
dict_ht.o: vstream.h dict_ht.o: vstream.h
dict_ht.o: vstring.h dict_ht.o: vstring.h
dict_inline.cdict_static.o: dict_inline.cdict_static.c
dict_inline.o: argv.h dict_inline.o: argv.h
dict_inline.o: check_arg.h dict_inline.o: check_arg.h
dict_inline.o: dict.h dict_inline.o: dict.h
@@ -1648,18 +1647,6 @@ dict_utf8.o: sys_defs.h
dict_utf8.o: vbuf.h dict_utf8.o: vbuf.h
dict_utf8.o: vstream.h dict_utf8.o: vstream.h
dict_utf8.o: vstring.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: check_arg.h
dir_forest.o: dir_forest.c dir_forest.o: dir_forest.c
dir_forest.o: dir_forest.h dir_forest.o: dir_forest.h
@@ -1917,6 +1904,8 @@ load_file.o: vbuf.h
load_file.o: vstream.h load_file.o: vstream.h
load_file.o: warn_stat.h load_file.o: warn_stat.h
load_lib.o: load_lib.c load_lib.o: load_lib.c
load_lib.o: load_lib.h
load_lib.o: msg.h
load_lib.o: sys_defs.h load_lib.o: sys_defs.h
lowercase.o: check_arg.h lowercase.o: check_arg.h
lowercase.o: lowercase.c lowercase.o: lowercase.c

View File

@@ -640,7 +640,6 @@ static const NAME_MASK dict_mask[] = {
"utf8_request", DICT_FLAG_UTF8_REQUEST, /* request UTF-8 activation */ "utf8_request", DICT_FLAG_UTF8_REQUEST, /* request UTF-8 activation */
"utf8_active", DICT_FLAG_UTF8_ACTIVE, /* UTF-8 is activated */ "utf8_active", DICT_FLAG_UTF8_ACTIVE, /* UTF-8 is activated */
"src_rhs_is_file", DICT_FLAG_SRC_RHS_IS_FILE, /* value from file */ "src_rhs_is_file", DICT_FLAG_SRC_RHS_IS_FILE, /* value from file */
"unb64_active", DICT_FLAG_UNB64_ACTIVE, /* base64 decode activated */
0, 0,
}; };

View File

@@ -93,7 +93,7 @@ typedef struct DICT {
DICT_OWNER owner; /* provenance */ DICT_OWNER owner; /* provenance */
int error; /* last operation only */ int error; /* last operation only */
DICT_JMP_BUF *jbuf; /* exception handling */ DICT_JMP_BUF *jbuf; /* exception handling */
struct DICT_WRAPPER *wrapper; /* see below */ struct DICT_UTF8_BACKUP *utf8_backup; /* see below */
struct VSTRING *file_buf; /* dict_file_to_buf() */ struct VSTRING *file_buf; /* dict_file_to_buf() */
struct VSTRING *file_b64; /* dict_file_to_b64() */ struct VSTRING *file_b64; /* dict_file_to_b64() */
} DICT; } DICT;
@@ -133,7 +133,6 @@ extern DICT *dict_debug(DICT *);
#define DICT_FLAG_UTF8_ACTIVE (1<<20) /* UTF-8 proxy layer is present */ #define DICT_FLAG_UTF8_ACTIVE (1<<20) /* UTF-8 proxy layer is present */
#define DICT_FLAG_SRC_RHS_IS_FILE \ #define DICT_FLAG_SRC_RHS_IS_FILE \
(1<<21) /* Map source RHS is a 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) #define DICT_FLAG_UTF8_MASK (DICT_FLAG_UTF8_REQUEST)
@@ -252,29 +251,15 @@ extern int dict_flags_mask(const char *);
extern void dict_type_override(DICT *, const char *); extern void dict_type_override(DICT *, const char *);
/* /*
* Wrappers for DICT methods. Usage: create an "trivial" wrapper object with * Check and convert UTF-8 keys and values.
* 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_WRAPPER { typedef struct DICT_UTF8_BACKUP {
const char *name; /* for literal constant */ const char *(*lookup) (struct DICT *, const char *);
const char *(*lookup) (struct DICT_WRAPPER *, DICT *, const char *); int (*update) (struct DICT *, const char *, const char *);
int (*update) (struct DICT_WRAPPER *, DICT *, const char *, const char *); int (*delete) (struct DICT *, const char *);
int (*delete) (struct DICT_WRAPPER *, DICT *, const char *); } DICT_UTF8_BACKUP;
struct DICT_WRAPPER *next;
} DICT_WRAPPER;
extern void dict_wrapper_prepend(DICT *, DICT_WRAPPER *); extern DICT *dict_utf8_activate(DICT *);
extern DICT_WRAPPER *dict_wrapper_alloc(ssize_t);
extern void dict_wrapper_free(DICT_WRAPPER *);
/*
* 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. * Driver for interactive or scripted tests.
@@ -327,6 +312,7 @@ extern struct VSTRING *dict_file_to_b64(DICT *, const char *);
extern struct VSTRING *dict_file_from_b64(DICT *, const char *); extern struct VSTRING *dict_file_from_b64(DICT *, const char *);
extern char *dict_file_get_error(DICT *); extern char *dict_file_get_error(DICT *);
extern void dict_file_purge_buffers(DICT *); extern void dict_file_purge_buffers(DICT *);
extern const char *dict_file_lookup(DICT *dict, const char *);
/* LICENSE /* LICENSE
/* .ad /* .ad

View File

@@ -159,7 +159,7 @@ DICT *dict_alloc(const char *dict_type, const char *dict_name, ssize_t size)
dict->owner.uid = INT_MAX; dict->owner.uid = INT_MAX;
dict->error = DICT_ERR_NONE; dict->error = DICT_ERR_NONE;
dict->jbuf = 0; dict->jbuf = 0;
dict->wrapper = 0; dict->utf8_backup = 0;
dict->file_buf = 0; dict->file_buf = 0;
dict->file_b64 = 0; dict->file_b64 = 0;
return dict; return dict;
@@ -173,8 +173,8 @@ void dict_free(DICT *dict)
myfree(dict->name); myfree(dict->name);
if (dict->jbuf) if (dict->jbuf)
myfree((void *) dict->jbuf); myfree((void *) dict->jbuf);
if (dict->wrapper) if (dict->utf8_backup)
dict_wrapper_free(dict->wrapper); myfree((void *) dict->utf8_backup);
if (dict->file_buf) if (dict->file_buf)
vstring_free(dict->file_buf); vstring_free(dict->file_buf);
if (dict->file_b64) if (dict->file_b64)

View File

@@ -1,10 +1,8 @@
./dict_open: warning: cidr map dict_cidr_file.map, line 3: open dict_cidr_file3: No such file or directory: skipping this rule ./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) owner=untrusted (uid=USER)
> get 1.1.1.1 > get 1.1.1.1
1.1.1.1=this-is-file1 1.1.1.1=dGhpcy1pcy1maWxlMQo=
> get 2.2.2.2 > get 2.2.2.2
2.2.2.2=this-is-file2 2.2.2.2=dGhpcy1pcy1maWxlMgo=
> get 3.3.3.3 > get 3.3.3.3
3.3.3.3: not found 3.3.3.3: not found

View File

@@ -24,7 +24,7 @@
/* void dict_file_purge_buffers( /* void dict_file_purge_buffers(
/* DICT *dict) /* DICT *dict)
/* /*
/* void dict_file_wrapper_activate( /* const char *dict_file_lookup(
/* DICT *dict) /* DICT *dict)
/* DESCRIPTION /* DESCRIPTION
/* dict_file_to_buf() reads the content of the specified files, /* dict_file_to_buf() reads the content of the specified files,
@@ -48,10 +48,17 @@
/* it returns a desciption of the problem. Storage is owned /* it returns a desciption of the problem. Storage is owned
/* by the caller. /* by the caller.
/* /*
/* dict_file_wrapper_activate() activates a wrapper that /* dict_file_lookup() wraps the dictionary lookup method and
/* automatically base64-decodes lookup results. /* decodes the base64 lookup result. The dictionary must be
/* opened with DICT_FLAG_SRC_RHS_IS_FILE. Sets dict->error to
/* DICT_ERR_CONFIG if the content is invalid. Decoding is not
/* built into the dict->lookup() method, because that would
/* complicate the implementation of map nesting (inline, thash),
/* map composition (pipemap, unionmap), and map proxying.
/* DIAGNOSTICS /* DIAGNOSTICS
/* In case of error the result value is a null pointer, and /* Panic: interface violation.
/*
/* In case of error the VSTRING result value is a null pointer, and
/* an error description can be retrieved with dict_file_get_error(). /* an error description can be retrieved with dict_file_get_error().
/* The storage is owned by the caller. /* The storage is owned by the caller.
/* LICENSE /* LICENSE
@@ -199,56 +206,28 @@ void dict_file_purge_buffers(DICT *dict)
} }
} }
/* dict_file_wrapper_lookup - wrap the lookup method */ /* dict_file_lookup - look up and decode dictionary entry */
static const char *dict_file_wrapper_lookup(DICT_WRAPPER *wrapper, const char *dict_file_lookup(DICT *dict, const char *key)
DICT *dict, const char *key)
{ {
DICT_WRAPPER *next_wrapper; const char myname[] = "dict_file_lookup";
const char *res; const char *res;
VSTRING *unb64; VSTRING *unb64;
char *err; char *err;
next_wrapper = wrapper->next; if ((dict->flags & DICT_FLAG_SRC_RHS_IS_FILE) == 0)
if ((res = next_wrapper->lookup(next_wrapper, dict, key)) != 0) { msg_panic("%s: dictionary opened without DICT_FLAG_SRC_RHS_IS_FILE",
if ((unb64 = dict_file_from_b64(dict, res)) == 0) { myname);
err = dict_file_get_error(dict); if ((res = dict->lookup(dict, key)) == 0)
msg_warn("table %s:%s: key %s: %s", return (0);
dict->type, dict->name, key, err); if ((unb64 = dict_file_from_b64(dict, res)) == 0) {
myfree(err); err = dict_file_get_error(dict);
dict->error = DICT_ERR_CONFIG; msg_warn("table %s:%s: key %s: %s",
res = 0; dict->type, dict->name,
} else { key, err);
res = vstring_str(unb64); myfree(err);
} dict->error = DICT_ERR_CONFIG;
return (0);
} }
return (res); return STR(unb64);
}
/* 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;
} }

View File

@@ -5,10 +5,8 @@ owner=trusted (uid=2147483647)
foo: error foo: error
owner=trusted (uid=2147483647) owner=trusted (uid=2147483647)
> get file1 > get file1
file1=this-is-file1 file1=dGhpcy1pcy1maWxlMQo=
> get file2 > get file2
file2=this-is-file2 file2=dGhpcy1pcy1maWxlMgo=
> get file3 > get file3
file3: not found file3: not found

View File

@@ -479,16 +479,10 @@ DICT *dict_open3(const char *dict_type, const char *dict_name,
msg_fatal("%s:%s: unable to get exclusive lock: %m", msg_fatal("%s:%s: unable to get exclusive lock: %m",
dict_type, dict_name); dict_type, dict_name);
} }
/* Insert wrapper for UTF-8 syntax checks and casefolding. */ /* Last step: insert proxy for UTF-8 syntax checks and casefolding. */
if ((dict->flags & DICT_FLAG_UTF8_ACTIVE) == 0 if ((dict->flags & DICT_FLAG_UTF8_ACTIVE) == 0
&& DICT_NEED_UTF8_ACTIVATION(util_utf8_enable, dict_flags)) && DICT_NEED_UTF8_ACTIVATION(util_utf8_enable, dict_flags))
dict_utf8_wrapper_activate(dict); dict = dict_utf8_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); return (dict);
} }

View File

@@ -3,15 +3,10 @@
./dict_open: warning: pcre map dict_pcre_file.map, line 6: empty pathname list: >>,<<': skipping this rule ./dict_open: warning: pcre map dict_pcre_file.map, line 6: empty pathname list: >>,<<': skipping this rule
owner=untrusted (uid=USER) owner=untrusted (uid=USER)
> get file1 > get file1
file1=this-is-file1 file1=dGhpcy1pcy1maWxlMQo=
> get file2 > get file2
file2=this-is-file2 file2=dGhpcy1pcy1maWxlMgo=
> get file3 > get file3
file3: not found file3: not found
> get files12 > get files12
files12=this-is-file1 files12=dGhpcy1pcy1maWxlMQoKdGhpcy1pcy1maWxlMgo=
this-is-file2

View File

@@ -166,8 +166,7 @@ DICT *dict_pipe_open(const char *name, int open_flags, int dict_flags)
DICT_TYPE_PIPE, name, DICT_TYPE_PIPE, name,
DICT_TYPE_PIPE)); DICT_TYPE_PIPE));
if ((dict = dict_handle(dict_type_name)) == 0) if ((dict = dict_handle(dict_type_name)) == 0)
dict = dict_open(dict_type_name, open_flags, dict = dict_open(dict_type_name, open_flags, dict_flags);
dict_flags & ~DICT_FLAG_SRC_RHS_IS_FILE);
dict_register(dict_type_name, dict); dict_register(dict_type_name, dict);
DICT_OWNER_AGGREGATE_UPDATE(aggr_owner, dict->owner); DICT_OWNER_AGGREGATE_UPDATE(aggr_owner, dict->owner);
if (cpp == argv->argv) if (cpp == argv->argv)

View File

@@ -5,8 +5,6 @@ owner=trusted (uid=2147483647)
foo: error foo: error
owner=trusted (uid=0) owner=trusted (uid=0)
> get foo > get foo
foo=this-is-file1 foo=dGhpcy1pcy1maWxlMQo=
> get bar > get bar
bar=this-is-file1 bar=dGhpcy1pcy1maWxlMQo=

View File

@@ -1,10 +1,8 @@
./dict_open: warning: regexp map dict_regexp_file.map, line 3: open dict_regexp_file3: No such file or directory: skipping this rule ./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) owner=untrusted (uid=USER)
> get file1 > get file1
file1=this-is-file1 file1=dGhpcy1pcy1maWxlMQo=
> get file2 > get file2
file2=this-is-file2 file2=dGhpcy1pcy1maWxlMgo=
> get file3 > get file3
file3: not found file3: not found

View File

@@ -5,8 +5,6 @@ owner=trusted (uid=2147483647)
foo: error foo: error
owner=trusted (uid=2147483647) owner=trusted (uid=2147483647)
> get file1 > get file1
file1=this-is-file1 file1=dGhpcy1pcy1maWxlMQo=
> get file2 > get file2
file2=this-is-file1 file2=dGhpcy1pcy1maWxlMQo=

View File

@@ -179,8 +179,7 @@ DICT *dict_union_open(const char *name, int open_flags, int dict_flags)
DICT_TYPE_UNION, name, DICT_TYPE_UNION, name,
DICT_TYPE_UNION)); DICT_TYPE_UNION));
if ((dict = dict_handle(dict_type_name)) == 0) if ((dict = dict_handle(dict_type_name)) == 0)
dict = dict_open(dict_type_name, open_flags, dict = dict_open(dict_type_name, open_flags, dict_flags);
dict_flags & ~DICT_FLAG_SRC_RHS_IS_FILE);
dict_register(dict_type_name, dict); dict_register(dict_type_name, dict);
DICT_OWNER_AGGREGATE_UPDATE(aggr_owner, dict->owner); DICT_OWNER_AGGREGATE_UPDATE(aggr_owner, dict->owner);
if (cpp == argv->argv) if (cpp == argv->argv)

View File

@@ -6,13 +6,13 @@
/* SYNOPSIS /* SYNOPSIS
/* #include <dict.h> /* #include <dict.h>
/* /*
/* void dict_utf8_wrapper_activate( /* DICT *dict_utf8_activate(
/* DICT *dict) /* DICT *dict)
/* DESCRIPTION /* DESCRIPTION
/* dict_utf8_wrapper_activate() wraps a dictionary's lookup/update/delete /* dict_utf8_activate() wraps a dictionary's lookup/update/delete
/* methods with code that enforces UTF-8 checks on keys and /* methods with code that enforces UTF-8 checks on keys and
/* values, and that logs a warning when incorrect UTF-8 is /* values, and that logs a warning when incorrect UTF-8 is
/* encountered. /* encountered. The original dictionary handle becomes invalid.
/* /*
/* The wrapper code enforces a policy that maximizes application /* The wrapper code enforces a policy that maximizes application
/* robustness (it avoids the need for new error-handling code /* robustness (it avoids the need for new error-handling code
@@ -22,6 +22,8 @@
/* skipped while reporting a non-error status, and lookup /* skipped while reporting a non-error status, and lookup
/* results that contain a non-UTF-8 value are blocked while /* results that contain a non-UTF-8 value are blocked while
/* reporting a configuration error. /* reporting a configuration error.
/* BUGS
/* dict_utf8_activate() does not nest.
/* LICENSE /* LICENSE
/* .ad /* .ad
/* .fi /* .fi
@@ -131,10 +133,9 @@ static int dict_utf8_check(const char *string, CONST_CHAR_STAR *err)
/* dict_utf8_lookup - UTF-8 lookup method wrapper */ /* dict_utf8_lookup - UTF-8 lookup method wrapper */
static const char *dict_utf8_lookup(DICT_WRAPPER *wrapper, DICT *dict, static const char *dict_utf8_lookup(DICT *dict, const char *key)
const char *key)
{ {
DICT_WRAPPER *next_wrapper; DICT_UTF8_BACKUP *backup;
const char *utf8_err; const char *utf8_err;
const char *fold_res; const char *fold_res;
const char *value; const char *value;
@@ -155,8 +156,8 @@ static const char *dict_utf8_lookup(DICT_WRAPPER *wrapper, DICT *dict,
*/ */
saved_flags = (dict->flags & DICT_FLAG_FOLD_ANY); saved_flags = (dict->flags & DICT_FLAG_FOLD_ANY);
dict->flags &= ~DICT_FLAG_FOLD_ANY; dict->flags &= ~DICT_FLAG_FOLD_ANY;
next_wrapper = wrapper->next; backup = dict->utf8_backup;
value = next_wrapper->lookup(next_wrapper, dict, fold_res); value = backup->lookup(dict, fold_res);
dict->flags |= saved_flags; dict->flags |= saved_flags;
/* /*
@@ -174,10 +175,9 @@ static const char *dict_utf8_lookup(DICT_WRAPPER *wrapper, DICT *dict,
/* dict_utf8_update - UTF-8 update method wrapper */ /* dict_utf8_update - UTF-8 update method wrapper */
static int dict_utf8_update(DICT_WRAPPER *wrapper, DICT *dict, static int dict_utf8_update(DICT *dict, const char *key, const char *value)
const char *key, const char *value)
{ {
DICT_WRAPPER *next_wrapper; DICT_UTF8_BACKUP *backup;
const char *utf8_err; const char *utf8_err;
const char *fold_res; const char *fold_res;
int saved_flags; int saved_flags;
@@ -209,8 +209,8 @@ static int dict_utf8_update(DICT_WRAPPER *wrapper, DICT *dict,
else { else {
saved_flags = (dict->flags & DICT_FLAG_FOLD_ANY); saved_flags = (dict->flags & DICT_FLAG_FOLD_ANY);
dict->flags &= ~DICT_FLAG_FOLD_ANY; dict->flags &= ~DICT_FLAG_FOLD_ANY;
next_wrapper = wrapper->next; backup = dict->utf8_backup;
status = next_wrapper->update(next_wrapper, dict, fold_res, value); status = backup->update(dict, fold_res, value);
dict->flags |= saved_flags; dict->flags |= saved_flags;
return (status); return (status);
} }
@@ -218,9 +218,9 @@ static int dict_utf8_update(DICT_WRAPPER *wrapper, DICT *dict,
/* dict_utf8_delete - UTF-8 delete method wrapper */ /* dict_utf8_delete - UTF-8 delete method wrapper */
static int dict_utf8_delete(DICT_WRAPPER *wrapper, DICT *dict, const char *key) static int dict_utf8_delete(DICT *dict, const char *key)
{ {
DICT_WRAPPER *next_wrapper; DICT_UTF8_BACKUP *backup;
const char *utf8_err; const char *utf8_err;
const char *fold_res; const char *fold_res;
int saved_flags; int saved_flags;
@@ -242,19 +242,19 @@ static int dict_utf8_delete(DICT_WRAPPER *wrapper, DICT *dict, const char *key)
else { else {
saved_flags = (dict->flags & DICT_FLAG_FOLD_ANY); saved_flags = (dict->flags & DICT_FLAG_FOLD_ANY);
dict->flags &= ~DICT_FLAG_FOLD_ANY; dict->flags &= ~DICT_FLAG_FOLD_ANY;
next_wrapper = wrapper->next; backup = dict->utf8_backup;
status = next_wrapper->delete(next_wrapper, dict, fold_res); status = backup->delete(dict, fold_res);
dict->flags |= saved_flags; dict->flags |= saved_flags;
return (status); return (status);
} }
} }
/* dict_utf8_wrapper_activate - wrap legacy dict object for UTF-8 processing */ /* dict_utf8_activate - wrap a legacy dict object for UTF-8 processing */
void dict_utf8_wrapper_activate(DICT *dict) DICT *dict_utf8_activate(DICT *dict)
{ {
const char myname[] = "dict_utf8_wrapper_activate"; const char myname[] = "dict_utf8_activate";
DICT_WRAPPER *wrapper; DICT_UTF8_BACKUP *backup;
/* /*
* Sanity check. * Sanity check.
@@ -264,22 +264,37 @@ void dict_utf8_wrapper_activate(DICT *dict)
if ((dict->flags & DICT_FLAG_UTF8_REQUEST) == 0) if ((dict->flags & DICT_FLAG_UTF8_REQUEST) == 0)
msg_panic("%s: %s:%s does not request Unicode support", msg_panic("%s: %s:%s does not request Unicode support",
myname, dict->type, dict->name); myname, dict->type, dict->name);
if ((dict->flags & DICT_FLAG_UTF8_ACTIVE)) if ((dict->flags & DICT_FLAG_UTF8_ACTIVE) || dict->utf8_backup != 0)
msg_panic("%s: %s:%s Unicode support is already activated", msg_panic("%s: %s:%s Unicode support is already activated",
myname, dict->type, dict->name); myname, dict->type, dict->name);
/* /*
* Interpose on the lookup/update/delete methods. * 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.
*/ */
wrapper = dict_wrapper_alloc(sizeof(*wrapper)); backup = dict->utf8_backup = (DICT_UTF8_BACKUP *) mymalloc(sizeof(*backup));
wrapper->name = "utf8";
wrapper->lookup = dict_utf8_lookup; /*
wrapper->update = dict_utf8_update; * Interpose on the lookup/update/delete methods. It is a conscious
wrapper->delete = dict_utf8_delete; * decision not to tinker with the iterator or destructor.
dict_wrapper_prepend(dict, wrapper); */
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;
/* /*
* Leave our mark. See sanity check above. * Leave our mark. See sanity check above.
*/ */
dict->flags |= DICT_FLAG_UTF8_ACTIVE; dict->flags |= DICT_FLAG_UTF8_ACTIVE;
return (dict);
} }

View File

@@ -1,243 +0,0 @@
/*++
/* 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);
}