]> git.ipfire.org Git - thirdparty/postfix.git/commitdiff
postfix-3.4-20181229-nonprod
authorWietse Venema <wietse@porcupine.org>
Sat, 29 Dec 2018 05:00:00 +0000 (00:00 -0500)
committerViktor Dukhovni <postfix-users@dukhovni.org>
Sun, 30 Dec 2018 01:24:47 +0000 (20:24 -0500)
25 files changed:
postfix/HISTORY
postfix/src/global/mail_version.h
postfix/src/global/maps.c
postfix/src/global/maps.h
postfix/src/global/mkmap_open.c
postfix/src/postmap/file_test.ref
postfix/src/postmap/postmap.c
postfix/src/smtpd/smtpd_sasl_proto.c
postfix/src/tls/tls_misc.c
postfix/src/util/Makefile.in
postfix/src/util/dict.c
postfix/src/util/dict.h
postfix/src/util/dict_alloc.c
postfix/src/util/dict_cidr_file.ref
postfix/src/util/dict_file.c
postfix/src/util/dict_inline_file.ref
postfix/src/util/dict_open.c
postfix/src/util/dict_pcre_file.ref
postfix/src/util/dict_pipe.c
postfix/src/util/dict_random_file.ref
postfix/src/util/dict_regexp_file.ref
postfix/src/util/dict_static_file.ref
postfix/src/util/dict_union.c
postfix/src/util/dict_utf8.c
postfix/src/util/dict_wrapper.c [deleted file]

index c2ee393455f85736a81ebe7572c53478cb006a1a..acb73f5f4a85167649c318907185c7944de37114 100644 (file)
@@ -23922,11 +23922,13 @@ Apologies for any names omitted.
        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.
+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.
index 882c9c4717f311289d8fa80c58f4493bac484423..012f65a779a9f053b8b0915e2bbdc555c6e26055 100644 (file)
@@ -20,7 +20,7 @@
   * Patches change both the patchlevel and the release date. Snapshots have no
   * patchlevel; they change the release date only.
   */
-#define MAIL_RELEASE_DATE      "20181228"
+#define MAIL_RELEASE_DATE      "20181229"
 #define MAIL_VERSION_NUMBER    "3.4"
 
 #ifdef SNAPSHOT
index 8a1dc9e881828fa1828cb94cbe5e810b94e1cb53..7c84e9aa01a145396e430cda09ebab8f334e621a 100644 (file)
 /*     const char *key;
 /*     int     flags;
 /*
+/*     const char *maps_file_find(maps, key, flags)
+/*     MAPS    *maps;
+/*     const char *key;
+/*     int     flags;
+/*
 /*     MAPS    *maps_free(maps)
 /*     MAPS    *maps;
 /* DESCRIPTION
 /*     for example, DICT_FLAG_FIXED | DICT_FLAG_PATTERN selects
 /*     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()
 /*     and conveniently returns a null pointer.
 /*
@@ -189,15 +198,81 @@ const char *maps_find(MAPS *maps, const char *name, int flags)
                         maps->title, name);
                msg_warn("%s should return NO RESULT in case of NOT FOUND",
                         maps->title);
-               maps->error = DICT_ERR_RETRY;
+               maps->error = DICT_ERR_CONFIG;
                return (0);
            }
            if (msg_verbose)
-               msg_info("%s: %s: %s: %s = %s", myname, maps->title,
-                        *map_name, name, expansion);
+               msg_info("%s: %s: %s: %s = %.100s%s", myname, maps->title,
+                        *map_name, name, expansion,
+                        strlen(expansion) > 100 ? "..." : "");
            return (expansion);
        } 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);
            break;
        }
index ccf2c6ac2b0a1d43076985e6f97ba9969997f3d9..04ee6dc933e69d0368b9a7ddbece733561ec7d32 100644 (file)
@@ -27,6 +27,7 @@ typedef struct MAPS {
 
 extern MAPS *maps_create(const char *, 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 *);
 
 /* LICENSE
index 32dfc9ea6b8144b26bb3d1a91b9109d27689fa12..9d15eec30a27f0557c590458e1cddc165ad45e01 100644 (file)
@@ -301,12 +301,7 @@ MKMAP  *mkmap_open(const char *type, const char *path,
      */
     if ((mkmap->dict->flags & DICT_FLAG_UTF8_ACTIVE) == 0
        && DICT_NEED_UTF8_ACTIVATION(util_utf8_enable, dict_flags))
-       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);
+       mkmap->dict = dict_utf8_activate(mkmap->dict);
 
     /*
      * Resume signal delivery if multi-writer safe.
index 1e808abbe5e48787f2d5de4fb8fd55dea35d0899..bc4815d53c2da49254af29aabcb42844f454d6f4 100644 (file)
@@ -11,4 +11,4 @@ file-2        this-is-file2
 this-is-file1
 this-is-file2
 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
index 2077335734949ef73b34a9ce22db09f4ae61ae81..9f368abb552607c3889b6637d3fcc6af408d1789 100644 (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 ?
                       dict_open3(maps[n], map_name, 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) {
                        msg_warn("table %s:%s: key %s: empty string result is not allowed",
                               dicts[n]->type, dicts[n]->name, STR(keybuf));
                        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;
-
-                       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);
                    found = 1;
                    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",
                              dicts[n]->type, dicts[n]->name);
+               }
            }
        }
     } else {
@@ -752,31 +748,27 @@ static int postmap_query(const char *map_type, const char *map_name,
     const char *value;
 
     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) {
            msg_warn("table %s:%s: key %s: empty string result is not allowed",
                     map_type, map_name, key);
            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;
-
-           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);
     }
-    if (dict->error)
-       msg_fatal("table %s:%s: query error: %m", dict->type, dict->name);
+    switch (dict->error) {
+    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);
     dict_close(dict);
     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",
                     map_type, map_name);
        }
-#if 1
        if (dict_flags & DICT_FLAG_SRC_RHS_IS_FILE) {
            VSTRING *unb64;
            char   *err;
@@ -900,7 +891,6 @@ static void postmap_seq(const char *map_type, const char *map_name,
            }
            value = STR(unb64);
        }
-#endif
        vstream_printf("%s      %s\n", key, value);
     }
     if (dict->error)
index 6706fc2b991fc030a53c7dc38015fb1f566eebf8..476752df702baa83689ff9f656e05e1ef9999c58 100644 (file)
@@ -7,7 +7,7 @@
 /*     #include "smtpd.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;
 /*     int     argc;
 /*     SMTPD_TOKEN *argv;
index b89313be6c6eb1216264d669821e3651251338ab..a2ad6aea59175a279aaa7eb1c1b9aa8305263681 100644 (file)
@@ -801,7 +801,8 @@ static int server_sni_callback(SSL *ssl, int *alert, void *arg)
        return SSL_TLSEXT_ERR_NOACK;
 
     do {
-       pem = maps_find(tls_server_sni_maps, sni, DICT_FLAG_SRC_RHS_IS_FILE);
+       /* Don't silently skip maps opened with the wrong flags. */
+       pem = maps_file_find(tls_server_sni_maps, sni, 0);
     } while (!pem
             && !tls_server_sni_maps->error
             && (sni = strchr(sni + 1, '.')) != 0);
index a1bd8bcba2883371159a63f8a3fcbec6318d3d14..f0715959c07a11a75e28b624cfd34f4f39fbbada 100644 (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 \
        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 \
-       dict_wrapper.c
+       split_qnameval.c argv_attr_print.c argv_attr_scan.c dict_file.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 \
@@ -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 \
        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 \
-       dict_wrapper.o
+       split_qnameval.o argv_attr_print.o argv_attr_scan.o dict_file.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.
@@ -1342,6 +1340,7 @@ dict_ht.o: sys_defs.h
 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
@@ -1648,18 +1647,6 @@ dict_utf8.o: sys_defs.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
@@ -1917,6 +1904,8 @@ load_file.o: vbuf.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
index 84e413eb028537292f1620550e8e04a1f340c064..5d53860f17d57e3f1fa52b47389dad0f8cc667aa 100644 (file)
@@ -640,7 +640,6 @@ static const NAME_MASK dict_mask[] = {
     "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,
 };
 
index 8c03477e3b52f2cc4d90583c6300ebfa6ac7d0ea..5cba861515f950855117788db6ab29db335330fb 100644 (file)
@@ -93,7 +93,7 @@ typedef struct DICT {
     DICT_OWNER owner;                  /* provenance */
     int     error;                     /* last operation only */
     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_b64;          /* dict_file_to_b64() */
 } 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_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)
 
@@ -252,29 +251,15 @@ extern int dict_flags_mask(const char *);
 extern void dict_type_override(DICT *, const char *);
 
  /*
-  * 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).
+  * Check and convert UTF-8 keys and values.
   */
-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 *);
+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;
 
- /*
-  * Things that build on DICT_WRAPPER.
-  */
-extern void dict_utf8_wrapper_activate(DICT *);
-extern void dict_file_wrapper_activate(DICT *);
+extern DICT *dict_utf8_activate(DICT *);
 
  /*
   * 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 char *dict_file_get_error(DICT *);
 extern void dict_file_purge_buffers(DICT *);
+extern const char *dict_file_lookup(DICT *dict, const char *);
 
 /* LICENSE
 /* .ad
index 384defbf69446ef25732dc47e602c5ebd13ad1b2..3285a38e35758ecdcf036d83db5bb5be98616bd1 100644 (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->error = DICT_ERR_NONE;
     dict->jbuf = 0;
-    dict->wrapper = 0;
+    dict->utf8_backup = 0;
     dict->file_buf = 0;
     dict->file_b64 = 0;
     return dict;
@@ -173,8 +173,8 @@ void    dict_free(DICT *dict)
     myfree(dict->name);
     if (dict->jbuf)
        myfree((void *) dict->jbuf);
-    if (dict->wrapper)
-       dict_wrapper_free(dict->wrapper);
+    if (dict->utf8_backup)
+       myfree((void *) dict->utf8_backup);
     if (dict->file_buf)
        vstring_free(dict->file_buf);
     if (dict->file_b64)
index 537805e240ce353b257a3c569084de48ed86fde9..3e9e79205c7aa56f1e73a733ce7c84a67b4f695d 100644 (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
 owner=untrusted (uid=USER)
 > get 1.1.1.1
-1.1.1.1=this-is-file1
-
+1.1.1.1=dGhpcy1pcy1maWxlMQo=
 > get 2.2.2.2
-2.2.2.2=this-is-file2
-
+2.2.2.2=dGhpcy1pcy1maWxlMgo=
 > get 3.3.3.3
 3.3.3.3: not found
index ebfbb3f16109e9b0646c62fe55bd085923ba05b5..ca4924c850801394537889b662b45f38b0e554c5 100644 (file)
@@ -24,7 +24,7 @@
 /*     void    dict_file_purge_buffers(
 /*     DICT    *dict)
 /*
-/*     void    dict_file_wrapper_activate(
+/*     const char *dict_file_lookup(
 /*     DICT    *dict)
 /* DESCRIPTION
 /*     dict_file_to_buf() reads the content of the specified files,
 /*     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.
+/*     dict_file_lookup() wraps the dictionary lookup method and
+/*     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
-/*     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().
 /*     The storage is owned by the caller.
 /* 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,
-                                               DICT *dict, const char *key)
+const char *dict_file_lookup(DICT *dict, const char *key)
 {
-    DICT_WRAPPER *next_wrapper;
+    const char myname[] = "dict_file_lookup";
     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);
-       }
+    if ((dict->flags & DICT_FLAG_SRC_RHS_IS_FILE) == 0)
+       msg_panic("%s: dictionary opened without DICT_FLAG_SRC_RHS_IS_FILE",
+                 myname);
+    if ((res = dict->lookup(dict, key)) == 0)
+       return (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;
+       return (0);
     }
-    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;
+    return STR(unb64);
 }
index 2aed9bd57a18ef14c8d67395f30a73c84c9c7796..9d49e95f42010cca2a00062f3487c3c8ee0e3e0c 100644 (file)
@@ -5,10 +5,8 @@ owner=trusted (uid=2147483647)
 foo: error
 owner=trusted (uid=2147483647)
 > get file1
-file1=this-is-file1
-
+file1=dGhpcy1pcy1maWxlMQo=
 > get file2
-file2=this-is-file2
-
+file2=dGhpcy1pcy1maWxlMgo=
 > get file3
 file3: not found
index ad95f1255381b7d3d93614bcdda0f1170d232b9e..ca8df67eea5abd2ce6a5c1892ec56f0efec23501 100644 (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",
                      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
        && DICT_NEED_UTF8_ACTIVATION(util_utf8_enable, dict_flags))
-       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);
-
+       dict = dict_utf8_activate(dict);
     return (dict);
 }
 
index 7021c337049e1fcaa76cf5346470e545f2a5f614..727306d91c37a56f0d01ceaf8cbc31e5a29d7a71 100644 (file)
@@ -3,15 +3,10 @@
 ./dict_open: warning: pcre map dict_pcre_file.map, line 6: empty pathname list: >>,<<': skipping this rule
 owner=untrusted (uid=USER)
 > get file1
-file1=this-is-file1
-
+file1=dGhpcy1pcy1maWxlMQo=
 > get file2
-file2=this-is-file2
-
+file2=dGhpcy1pcy1maWxlMgo=
 > get file3
 file3: not found
 > get files12
-files12=this-is-file1
-
-this-is-file2
-
+files12=dGhpcy1pcy1maWxlMQoKdGhpcy1pcy1maWxlMgo=
index 9940c4be660d83e0d073269ba774d54755473c81..8ce0faad728d5f973fc2718bbe2917b6670997a5 100644 (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));
        if ((dict = dict_handle(dict_type_name)) == 0)
-           dict = dict_open(dict_type_name, open_flags,
-                            dict_flags & ~DICT_FLAG_SRC_RHS_IS_FILE);
+           dict = dict_open(dict_type_name, open_flags, dict_flags);
        dict_register(dict_type_name, dict);
        DICT_OWNER_AGGREGATE_UPDATE(aggr_owner, dict->owner);
        if (cpp == argv->argv)
index 7622c1f9cc70ed9e13ddba2412944447dc98f7ae..cb0187379fa017c699dae857d58c77acdf77505f 100644 (file)
@@ -5,8 +5,6 @@ owner=trusted (uid=2147483647)
 foo: error
 owner=trusted (uid=0)
 > get foo
-foo=this-is-file1
-
+foo=dGhpcy1pcy1maWxlMQo=
 > get bar
-bar=this-is-file1
-
+bar=dGhpcy1pcy1maWxlMQo=
index 2a2ba7f52282cdc76dc490132aeb87a4d60438cb..2218143623fae5def96bd69c8d5bf51fe9733083 100644 (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
 owner=untrusted (uid=USER)
 > get file1
-file1=this-is-file1
-
+file1=dGhpcy1pcy1maWxlMQo=
 > get file2
-file2=this-is-file2
-
+file2=dGhpcy1pcy1maWxlMgo=
 > get file3
 file3: not found
index 47048a29780ff46184253ad88fc33e00fd38fa46..259f9fb67a05918dc27e6ad41a051b7503795a29 100644 (file)
@@ -5,8 +5,6 @@ owner=trusted (uid=2147483647)
 foo: error
 owner=trusted (uid=2147483647)
 > get file1
-file1=this-is-file1
-
+file1=dGhpcy1pcy1maWxlMQo=
 > get file2
-file2=this-is-file1
-
+file2=dGhpcy1pcy1maWxlMQo=
index 5c3b08af8679be38b838dd85cad1981425aeaae0..80df03b61717d7ea943ef5768de361789d63e7c7 100644 (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));
        if ((dict = dict_handle(dict_type_name)) == 0)
-           dict = dict_open(dict_type_name, open_flags,
-                            dict_flags & ~DICT_FLAG_SRC_RHS_IS_FILE);
+           dict = dict_open(dict_type_name, open_flags, dict_flags);
        dict_register(dict_type_name, dict);
        DICT_OWNER_AGGREGATE_UPDATE(aggr_owner, dict->owner);
        if (cpp == argv->argv)
index e7dc4757856f9c2c478f3abe8d2c58fdc5e66666..f1fc65a5920ccec21a47a04ac7f465093a19b57f 100644 (file)
@@ -6,13 +6,13 @@
 /* SYNOPSIS
 /*     #include <dict.h>
 /*
-/*     void    dict_utf8_wrapper_activate(
+/*     DICT    *dict_utf8_activate(
 /*     DICT    *dict)
 /* 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
 /*     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
 /*     robustness (it avoids the need for new error-handling code
@@ -22,6 +22,8 @@
 /*     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
@@ -131,10 +133,9 @@ static int dict_utf8_check(const char *string, CONST_CHAR_STAR *err)
 
 /* dict_utf8_lookup - UTF-8 lookup method wrapper */
 
-static const char *dict_utf8_lookup(DICT_WRAPPER *wrapper, DICT *dict,
-                                           const char *key)
+static const char *dict_utf8_lookup(DICT *dict, const char *key)
 {
-    DICT_WRAPPER *next_wrapper;
+    DICT_UTF8_BACKUP *backup;
     const char *utf8_err;
     const char *fold_res;
     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);
     dict->flags &= ~DICT_FLAG_FOLD_ANY;
-    next_wrapper = wrapper->next;
-    value = next_wrapper->lookup(next_wrapper, dict, fold_res);
+    backup = dict->utf8_backup;
+    value = backup->lookup(dict, fold_res);
     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 */
 
-static int dict_utf8_update(DICT_WRAPPER *wrapper, DICT *dict,
-                                   const char *key, const char *value)
+static int dict_utf8_update(DICT *dict, const char *key, const char *value)
 {
-    DICT_WRAPPER *next_wrapper;
+    DICT_UTF8_BACKUP *backup;
     const char *utf8_err;
     const char *fold_res;
     int     saved_flags;
@@ -209,8 +209,8 @@ static int dict_utf8_update(DICT_WRAPPER *wrapper, DICT *dict,
     else {
        saved_flags = (dict->flags & DICT_FLAG_FOLD_ANY);
        dict->flags &= ~DICT_FLAG_FOLD_ANY;
-       next_wrapper = wrapper->next;
-       status = next_wrapper->update(next_wrapper, dict, fold_res, value);
+       backup = dict->utf8_backup;
+       status = backup->update(dict, fold_res, value);
        dict->flags |= saved_flags;
        return (status);
     }
@@ -218,9 +218,9 @@ static int dict_utf8_update(DICT_WRAPPER *wrapper, DICT *dict,
 
 /* 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 *fold_res;
     int     saved_flags;
@@ -242,19 +242,19 @@ static int dict_utf8_delete(DICT_WRAPPER *wrapper, DICT *dict, const char *key)
     else {
        saved_flags = (dict->flags & DICT_FLAG_FOLD_ANY);
        dict->flags &= ~DICT_FLAG_FOLD_ANY;
-       next_wrapper = wrapper->next;
-       status = next_wrapper->delete(next_wrapper, dict, fold_res);
+       backup = dict->utf8_backup;
+       status = backup->delete(dict, fold_res);
        dict->flags |= saved_flags;
        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";
-    DICT_WRAPPER *wrapper;
+    const char myname[] = "dict_utf8_activate";
+    DICT_UTF8_BACKUP *backup;
 
     /*
      * Sanity check.
@@ -264,22 +264,37 @@ void    dict_utf8_wrapper_activate(DICT *dict)
     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))
+    if ((dict->flags & DICT_FLAG_UTF8_ACTIVE) || dict->utf8_backup != 0)
        msg_panic("%s: %s:%s Unicode support is already activated",
                  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));
-    wrapper->name = "utf8";
-    wrapper->lookup = dict_utf8_lookup;
-    wrapper->update = dict_utf8_update;
-    wrapper->delete = dict_utf8_delete;
-    dict_wrapper_prepend(dict, wrapper);
+    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;
 
     /*
      * Leave our mark. See sanity check above.
      */
     dict->flags |= DICT_FLAG_UTF8_ACTIVE;
+
+    return (dict);
 }
diff --git a/postfix/src/util/dict_wrapper.c b/postfix/src/util/dict_wrapper.c
deleted file mode 100644 (file)
index 3a54c6b..0000000
+++ /dev/null
@@ -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);
-}