mirror of
https://github.com/vdukhovni/postfix
synced 2025-08-22 18:07:41 +00:00
postfix-3.11-20250709
This commit is contained in:
parent
646ffe205b
commit
fcc88c9a9c
@ -29309,3 +29309,39 @@ Apologies for any names omitted.
|
||||
util/dict_regexp.c, util/dict_sdbm.c, util/dict_sockmap.c,
|
||||
util/dict_static.c, util/dict_surrogate.c, util/dict_tcp.c,
|
||||
util/dict_thash.c, util/dict_union.c, util/dict_unix.c.
|
||||
|
||||
20250626
|
||||
|
||||
Cleanup: removed explicit dictionary life-cycle management
|
||||
complexity from dict_xxx_open() and maps_create(), and
|
||||
centralized it under the generic dict_open() API. Files:
|
||||
global/maps.c, util/dict.c, util/dict.h, util/dict_alloc.c,
|
||||
util/dict_debug.c, util/dict_inline.c, util/dict_open.c,
|
||||
util/dict_pipe.c, util/dict_test.c, util/dict_thash.c,
|
||||
util/dict_union.c.
|
||||
|
||||
20250627
|
||||
|
||||
Temporary workaround to avoid a potential breaking change:
|
||||
allow the proxymap server to continue registering a dictionary
|
||||
under a legacy name, in addition to the preferred name that
|
||||
it is registered under by dict_open(). Files: util/dict.[hc],
|
||||
proxymap/proxymap.c.
|
||||
|
||||
Cleanup: some unused test binaries failed to build. Files:
|
||||
global/own_inet_addr.c, global/data_redirect.c,
|
||||
global/mynetworks.c.
|
||||
|
||||
20250608
|
||||
|
||||
Cosmetic changes with better identifier names. Files:
|
||||
util/dict.[hc], util/dict_alloc.c, util/dict_pipe.c,
|
||||
util/dict_union.c
|
||||
|
||||
Cleanup: simplified the match_list parser, and added a unit
|
||||
test for !maptype:mapname. Files: util/match_list.c,
|
||||
global/namaddr_list.in, global/namaddr_list.ref.
|
||||
|
||||
Cleanup: simplified the rule parser in global/server_acl.c.
|
||||
|
||||
Unbroke dict_debug Valgrind checks. File: util/dict_debug_test.sh.
|
||||
|
@ -177,3 +177,5 @@ proto proto COMPATIBILITY_README html
|
||||
smtp smtp c tlsproxy tlsproxy c proto postconf proto
|
||||
rhansen rhansen org Files proto DATABASE_README html
|
||||
postconf Makefile in postconf postconf c
|
||||
dict_open Files util dict hc proxymap proxymap c
|
||||
proxymap proxymap c
|
||||
|
@ -1864,3 +1864,4 @@ DGST
|
||||
DIGEST
|
||||
OSSL
|
||||
ossl
|
||||
deduplicates
|
||||
|
@ -227,7 +227,7 @@ int main(int argc, char **argv)
|
||||
vstream_fflush(VSTREAM_OUT);
|
||||
continue;
|
||||
}
|
||||
target = mystrtokq(&bufp, " \t");
|
||||
target = mystrtokq(&bufp, " \t", CHARS_BRACE);
|
||||
junk = mystrtok(&bufp, " \t");
|
||||
if (strcmp(cmd, "file") == 0 && target && !junk) {
|
||||
data_redirect_file(result, target);
|
||||
|
@ -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 "20250624"
|
||||
#define MAIL_RELEASE_DATE "20250709"
|
||||
#define MAIL_VERSION_NUMBER "3.11"
|
||||
|
||||
#ifdef SNAPSHOT
|
||||
|
@ -132,7 +132,6 @@ MAPS *maps_create(const char *title, const char *map_names, int dict_flags)
|
||||
static char parens[] = CHARS_BRACE;
|
||||
MAPS *maps;
|
||||
char *map_type_name;
|
||||
VSTRING *map_type_name_flags;
|
||||
DICT *dict;
|
||||
|
||||
/*
|
||||
@ -149,23 +148,17 @@ MAPS *maps_create(const char *title, const char *map_names, int dict_flags)
|
||||
*/
|
||||
if (*map_names) {
|
||||
bufp = temp = mystrdup(map_names);
|
||||
map_type_name_flags = vstring_alloc(10);
|
||||
|
||||
#define OPEN_FLAGS O_RDONLY
|
||||
|
||||
while ((map_type_name = mystrtokq(&bufp, sep, parens)) != 0) {
|
||||
dict_make_registered_name(map_type_name_flags, map_type_name,
|
||||
OPEN_FLAGS, dict_flags);
|
||||
if ((dict = dict_handle(vstring_str(map_type_name_flags))) == 0)
|
||||
dict = dict_open(map_type_name, OPEN_FLAGS, dict_flags);
|
||||
if ((dict->flags & dict_flags) != dict_flags)
|
||||
msg_panic("%s: map %s has flags 0%o, want flags 0%o",
|
||||
myname, map_type_name, dict->flags, dict_flags);
|
||||
dict_register(vstring_str(map_type_name_flags), dict);
|
||||
argv_add(maps->argv, vstring_str(map_type_name_flags), ARGV_END);
|
||||
argv_add(maps->argv, dict->reg_name, ARGV_END);
|
||||
}
|
||||
myfree(temp);
|
||||
vstring_free(map_type_name_flags);
|
||||
}
|
||||
return (maps);
|
||||
}
|
||||
@ -299,12 +292,16 @@ const char *maps_file_find(MAPS *maps, const char *name, int flags)
|
||||
|
||||
MAPS *maps_free(MAPS *maps)
|
||||
{
|
||||
const char *myname = "maps_free";
|
||||
DICT *dict;
|
||||
char **map_name;
|
||||
|
||||
for (map_name = maps->argv->argv; *map_name; map_name++) {
|
||||
if (msg_verbose)
|
||||
msg_info("maps_free: %s", *map_name);
|
||||
dict_unregister(*map_name);
|
||||
if ((dict = dict_handle(*map_name)) == 0)
|
||||
msg_panic("%s: dictionary not found: %s", myname, *map_name);
|
||||
dict_close(dict);
|
||||
}
|
||||
myfree(maps->title);
|
||||
argv_free(maps->argv);
|
||||
|
@ -315,12 +315,9 @@ const char *mynetworks_host(void)
|
||||
#ifdef TEST
|
||||
#include <inet_proto.h>
|
||||
|
||||
char *var_inet_interfaces;
|
||||
char *var_mynetworks_style;
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
INET_PROTO_INFO *proto_info;
|
||||
const INET_PROTO_INFO *proto_info;
|
||||
|
||||
if (argc != 4)
|
||||
msg_fatal("usage: %s protocols mask_style interface_list (e.g. \"all subnet all\")",
|
||||
|
@ -50,3 +50,9 @@ ${SHLIB_ENV} ${VALGRIND} ./namadr_list /tmp/junk foo 1.2.3.4
|
||||
${SHLIB_ENV} ${VALGRIND} ./namadr_list /tmp/junk bar 1.2.3.4
|
||||
${SHLIB_ENV} ${VALGRIND} ./namadr_list /tmp/junk baz 1.2.3.4
|
||||
${SHLIB_ENV} ${VALGRIND} ./namadr_list /tmp/junk fool 1.2.3.4
|
||||
echo 'foo !inline:{
|
||||
{ bar = yes}
|
||||
} baz' >junk; mv junk /tmp
|
||||
${SHLIB_ENV} ${VALGRIND} ./namadr_list /tmp/junk foo 1.2.3.4
|
||||
${SHLIB_ENV} ${VALGRIND} ./namadr_list /tmp/junk bar 1.2.3.4
|
||||
${SHLIB_ENV} ${VALGRIND} ./namadr_list /tmp/junk baz 1.2.3.4
|
||||
|
@ -59,3 +59,6 @@ foo/1.2.3.4: YES
|
||||
bar/1.2.3.4: YES
|
||||
baz/1.2.3.4: YES
|
||||
fool/1.2.3.4: NO
|
||||
foo/1.2.3.4: YES
|
||||
bar/1.2.3.4: NO
|
||||
baz/1.2.3.4: YES
|
||||
|
@ -294,11 +294,9 @@ static void inet_addr_list_print(INET_ADDR_LIST *list)
|
||||
}
|
||||
}
|
||||
|
||||
char *var_inet_interfaces;
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
INET_PROTO_INFO *proto_info;
|
||||
const INET_PROTO_INFO *proto_info;
|
||||
INET_ADDR_LIST *list;
|
||||
|
||||
if (argc != 3)
|
||||
|
@ -64,6 +64,9 @@
|
||||
/* Google, Inc.
|
||||
/* 111 8th Avenue
|
||||
/* New York, NY 10011, USA
|
||||
/*
|
||||
/* Wietse Venema
|
||||
/* porcupine.org
|
||||
/*--*/
|
||||
|
||||
/* System library. */
|
||||
@ -141,10 +144,9 @@ SERVER_ACL *server_acl_parse(const char *extern_acl, const char *origin)
|
||||
argv_add(intern_acl, SERVER_ACL_NAME_DUNNO, (char *) 0);
|
||||
break;
|
||||
} else {
|
||||
if (dict_handle(acl) == 0)
|
||||
dict_register(acl, dict_open(acl, O_RDONLY, DICT_FLAG_LOCK
|
||||
acl = dict_open(acl, O_RDONLY, DICT_FLAG_LOCK
|
||||
| DICT_FLAG_FOLD_FIX
|
||||
| DICT_FLAG_UTF8_REQUEST));
|
||||
| DICT_FLAG_UTF8_REQUEST)->reg_name;
|
||||
}
|
||||
}
|
||||
argv_add(intern_acl, acl, (char *) 0);
|
||||
@ -212,8 +214,9 @@ int server_acl_eval(const char *client_addr, SERVER_ACL * intern_acl,
|
||||
if (ret != SERVER_ACL_ACT_DUNNO)
|
||||
return (ret);
|
||||
} else if (dict->error != 0) {
|
||||
msg_warn("%s: %s: table lookup error -- ignoring the remainder "
|
||||
"of this access list", origin, acl);
|
||||
msg_warn("%s: %s:%s: table lookup error -- ignoring the "
|
||||
"remainder of this access list", origin, dict->type,
|
||||
dict->name);
|
||||
return (SERVER_ACL_ACT_ERROR);
|
||||
}
|
||||
} else if (STREQ(acl, SERVER_ACL_NAME_DUNNO)) {
|
||||
|
@ -843,6 +843,14 @@ int main(int argc, char **argv)
|
||||
*/
|
||||
MAIL_VERSION_STAMP_ALLOCATE;
|
||||
|
||||
/*
|
||||
* Workaround for programs that make explicit dict_register() calls with
|
||||
* a table that is already registered under a different name. This is
|
||||
* safe only in programs that do not unregister or close a table that is
|
||||
* registered with multiple names.
|
||||
*/
|
||||
dict_allow_multiple_dict_register_names = 1;
|
||||
|
||||
/*
|
||||
* XXX When invoked with the master.cf service name "proxywrite", the
|
||||
* proxymap daemon will allow update requests. To update a table that is
|
||||
|
@ -58,6 +58,8 @@ lib_update: $(LIB_DIR)/$(LIB) $(HDRS) $(MOCK_OBJ)
|
||||
cmp -s $$i $(LIB_DIR)/$$i 2>/dev/null || cp $$i $(LIB_DIR); \
|
||||
done
|
||||
|
||||
tests:
|
||||
|
||||
clean:
|
||||
rm -f $(LIB_SO) *.o
|
||||
|
||||
|
@ -659,13 +659,13 @@ tests: all valid_hostname_test mac_expand_test dict_test unescape_test \
|
||||
binhash_test argv_test inet_prefix_top_test printable_test \
|
||||
valid_utf8_string_test readlline_test quote_for_json_test \
|
||||
normalize_ws_test valid_uri_scheme_test clean_ascii_cntrl_space_test \
|
||||
test_normalize_v4mapped_addr test_ossl_digest test_dict_pipe
|
||||
test_normalize_v4mapped_addr test_ossl_digest test_dict_pipe \
|
||||
test_dict_union
|
||||
|
||||
dict_tests: all dict_test \
|
||||
dict_pcre_tests dict_cidr_test dict_thash_test dict_static_test \
|
||||
dict_inline_test dict_utf8_test dict_regexp_test \
|
||||
dict_regexp_file_test dict_cidr_file_test \
|
||||
dict_regexp_file_test dict_cidr_file_test dict_seq_test \
|
||||
dict_static_file_test dict_random_test dict_random_file_test \
|
||||
dict_inline_file_test dict_stream_test dict_inline_regexp_test \
|
||||
dict_inline_cidr_test dict_debug_test
|
||||
@ -811,7 +811,7 @@ split_qnameval_test: split_qnameval update
|
||||
|
||||
dict_seq_test: dict_open testdb dict_seq.in dict_seq.ref
|
||||
rm -f testdb.db testdb.dir testdb.pag
|
||||
$(SHLIB_ENV) ${VALGRIND} ./dict_open hash:testdb create sync < dict_seq.in 2>&1 | sed 's/uid=[0-9][0-9][0-9]*/uid=USER/' > dict_seq.tmp
|
||||
${HTABLE_FIX} $(SHLIB_ENV) ${VALGRIND} ./dict_open internal:testdb create sync_update < dict_seq.in 2>&1 | sed 's/uid=[0-9][0-9][0-9]*/uid=USER/' > dict_seq.tmp
|
||||
diff dict_seq.ref dict_seq.tmp
|
||||
rm -f testdb.db testdb.dir testdb.pag dict_seq.tmp
|
||||
|
||||
|
@ -75,6 +75,15 @@
|
||||
/* const char *type_name,
|
||||
/* int open_flags,
|
||||
/* int dict_flags)
|
||||
/*
|
||||
/* char *dict_make_registered_name4(
|
||||
/* VSTRING *out,
|
||||
/* const char *type,
|
||||
/* const char *name,
|
||||
/* int open_flags,
|
||||
/* int dict_flags)
|
||||
/*
|
||||
/* int dict_allow_multiple_dict_register_names;
|
||||
/* DESCRIPTION
|
||||
/* This module maintains a collection of name-value dictionaries.
|
||||
/* Each dictionary has its own name and has its own methods to read
|
||||
@ -173,11 +182,19 @@
|
||||
/* dict_flags_mask() returns the bitmask for the specified
|
||||
/* comma/space-separated dictionary flag names.
|
||||
/*
|
||||
/* dict_make_registered_name() formats a dictionary type:name and
|
||||
/* (initial) flag values for use in dict_register() calls.
|
||||
/* dict_make_registered_name*() format a dictionary type, name,
|
||||
/* and (initial) flag values for use in dict_register() calls.
|
||||
/* This encourages consistent sharing of dictionary instances that
|
||||
/* have the exact same type:name and (initial) flags. The result
|
||||
/* value is the string value of the \fIout\fR VSTRING buffer.
|
||||
/*
|
||||
/* dict_allow_multiple_dict_register_names enables a temporary
|
||||
/* workaround for programs that make explicit dict_register()
|
||||
/* calls with a table that is already registered under a different
|
||||
/* name. Setting this to non-zero allows a dictionary to be
|
||||
/* registered under multiple names. This workaround is safe only
|
||||
/* in programs that do not unregister or close a table that is
|
||||
/* registered with multiple names.
|
||||
/* TRUST AND PROVENANCE
|
||||
/* .ad
|
||||
/* .fi
|
||||
@ -322,8 +339,24 @@ typedef struct {
|
||||
dict = node->dict; \
|
||||
} while (0)
|
||||
|
||||
/*
|
||||
* Workaround for programs that make explicit dict_register() calls with
|
||||
* tables that are already registered under a different name. This is safe
|
||||
* only in programs that do not unregister or close a table that is
|
||||
* registered with multiple names.
|
||||
*/
|
||||
int dict_allow_multiple_dict_register_names = 0;
|
||||
|
||||
#define STR(x) vstring_str(x)
|
||||
|
||||
/* dict_register_close - trigger dictionary cleanup */
|
||||
|
||||
static void dict_register_close(DICT *dict)
|
||||
{
|
||||
/* This will eventually call dict->saved_lose(). */
|
||||
dict_unregister(dict->reg_name);
|
||||
}
|
||||
|
||||
/* dict_register - make association with dictionary */
|
||||
|
||||
void dict_register(const char *dict_name, DICT *dict_info)
|
||||
@ -331,6 +364,15 @@ void dict_register(const char *dict_name, DICT *dict_info)
|
||||
const char *myname = "dict_register";
|
||||
DICT_NODE *node;
|
||||
|
||||
/*
|
||||
* Enforce referential integrity.
|
||||
*/
|
||||
if (dict_allow_multiple_dict_register_names == 0
|
||||
&& dict_info->reg_name && strcmp(dict_name, dict_info->reg_name) != 0)
|
||||
msg_panic("%s: '%s:%s' is already registered under '%s' and cannot "
|
||||
"also be registered under '%s'", myname, dict_info->type,
|
||||
dict_info->name, dict_info->reg_name, dict_name);
|
||||
|
||||
if (dict_table == 0)
|
||||
dict_table = htable_create(0);
|
||||
if ((node = dict_node(dict_name)) == 0) {
|
||||
@ -338,6 +380,9 @@ void dict_register(const char *dict_name, DICT *dict_info)
|
||||
node->dict = dict_info;
|
||||
node->refcount = 0;
|
||||
htable_enter(dict_table, dict_name, (void *) node);
|
||||
dict_info->reg_name = mystrdup(dict_name);
|
||||
dict_info->saved_close = dict_info->close;
|
||||
dict_info->close = dict_register_close;
|
||||
} else if (dict_info != node->dict)
|
||||
msg_fatal("%s: dictionary name exists: %s", myname, dict_name);
|
||||
node->refcount++;
|
||||
@ -361,7 +406,9 @@ static void dict_node_free(void *ptr)
|
||||
DICT_NODE *node = (DICT_NODE *) ptr;
|
||||
DICT *dict = node->dict;
|
||||
|
||||
if (dict->close)
|
||||
if (dict->saved_close) /* managed by dict_register() */
|
||||
dict->saved_close(dict);
|
||||
else
|
||||
dict->close(dict);
|
||||
myfree((void *) node);
|
||||
}
|
||||
@ -644,9 +691,9 @@ static const NAME_MASK dict_mask[] = {
|
||||
"fixed", DICT_FLAG_FIXED, /* fixed key map */
|
||||
"pattern", DICT_FLAG_PATTERN, /* keys are patterns */
|
||||
"lock", DICT_FLAG_LOCK, /* lock before access */
|
||||
"replace", DICT_FLAG_DUP_REPLACE, /* if file, replace dups */
|
||||
"dup_replace", DICT_FLAG_DUP_REPLACE, /* if file, replace dups */
|
||||
"sync_update", DICT_FLAG_SYNC_UPDATE, /* if file, sync updates */
|
||||
/*"debug", DICT_FLAG_DEBUG, /* log access */
|
||||
/* "debug", DICT_FLAG_DEBUG, /* log access */
|
||||
"no_regsub", DICT_FLAG_NO_REGSUB, /* disallow regexp substitution */
|
||||
"no_proxy", DICT_FLAG_NO_PROXY, /* disallow proxy mapping */
|
||||
"no_unauth", DICT_FLAG_NO_UNAUTH, /* disallow unauthenticated data */
|
||||
@ -690,3 +737,14 @@ char *dict_make_registered_name(VSTRING *out, const char *type_name,
|
||||
type_name, open_flags,
|
||||
dict_flags_str(dict_flags))));
|
||||
}
|
||||
|
||||
/* dict_make_registered_name4 - format registry name for consistent sharing */
|
||||
|
||||
char *dict_make_registered_name4(VSTRING *out, const char *type,
|
||||
const char *name,
|
||||
int open_flags, int dict_flags)
|
||||
{
|
||||
return (STR(vstring_sprintf(out, "%s:%s(%o,%s)",
|
||||
type, name, open_flags,
|
||||
dict_flags_str(dict_flags))));
|
||||
}
|
||||
|
@ -97,6 +97,8 @@ typedef struct DICT {
|
||||
struct DICT_UTF8_BACKUP *utf8_backup; /* see below */
|
||||
struct VSTRING *file_buf; /* dict_file_to_buf() */
|
||||
struct VSTRING *file_b64; /* dict_file_to_b64() */
|
||||
char *reg_name; /* managed by dict_register() */
|
||||
void (*saved_close) (struct DICT *); /* managed by dict_register() */
|
||||
} DICT;
|
||||
|
||||
extern DICT *dict_alloc(const char *, const char *, ssize_t);
|
||||
@ -282,6 +284,15 @@ extern DICT *PRINTFLIKE(5, 6) dict_surrogate(const char *, const char *, int, in
|
||||
* type, name, open_flags, and (initial) dict_flags.
|
||||
*/
|
||||
extern char *dict_make_registered_name(VSTRING *, const char *, int, int);
|
||||
extern char *dict_make_registered_name4(VSTRING *, const char *, const char *, int, int);
|
||||
|
||||
/*
|
||||
* Workaround for programs that make explicit dict_register() calls with a
|
||||
* table that is already registered under a different name. This is safe
|
||||
* only in programs that do not unregister or close a table that is
|
||||
* registered with multiple names.
|
||||
*/
|
||||
extern int dict_allow_multiple_dict_register_names;
|
||||
|
||||
/*
|
||||
* This name is reserved for matchlist error handling.
|
||||
|
@ -141,6 +141,10 @@ DICT *dict_alloc(const char *dict_type, const char *dict_name, ssize_t size)
|
||||
{
|
||||
DICT *dict = (DICT *) mymalloc(size);
|
||||
|
||||
if (msg_verbose > 2)
|
||||
msg_info("dict_alloc(\"%s\", \"%s\", %ld)",
|
||||
dict_type, dict_name, (long) size);
|
||||
|
||||
dict->type = mystrdup(dict_type);
|
||||
dict->name = mystrdup(dict_name);
|
||||
dict->flags = DICT_FLAG_FIXED;
|
||||
@ -162,6 +166,8 @@ DICT *dict_alloc(const char *dict_type, const char *dict_name, ssize_t size)
|
||||
dict->utf8_backup = 0;
|
||||
dict->file_buf = 0;
|
||||
dict->file_b64 = 0;
|
||||
dict->reg_name = 0;
|
||||
dict->saved_close = 0;
|
||||
return dict;
|
||||
}
|
||||
|
||||
@ -169,6 +175,11 @@ DICT *dict_alloc(const char *dict_type, const char *dict_name, ssize_t size)
|
||||
|
||||
void dict_free(DICT *dict)
|
||||
{
|
||||
if (msg_verbose > 2)
|
||||
msg_info("dict_free type=\"%s\" name=\"%s\" reg_name=\"%s\")",
|
||||
dict->type, dict->name, dict->reg_name ?
|
||||
dict->reg_name : "(null)");
|
||||
|
||||
myfree(dict->type);
|
||||
myfree(dict->name);
|
||||
if (dict->jbuf)
|
||||
@ -179,6 +190,8 @@ void dict_free(DICT *dict)
|
||||
vstring_free(dict->file_buf);
|
||||
if (dict->file_b64)
|
||||
vstring_free(dict->file_b64);
|
||||
if (dict->reg_name)
|
||||
myfree(dict->reg_name);
|
||||
myfree((void *) dict);
|
||||
}
|
||||
|
||||
|
@ -128,8 +128,9 @@ static int dict_debug_sequence(DICT *dict, int function,
|
||||
|
||||
static void dict_debug_close(DICT *dict)
|
||||
{
|
||||
/* TODO(wietse) use the annotated name from dict_make_registered_name(). */
|
||||
dict_unregister(dict->name);
|
||||
DICT_DEBUG *dict_debug = (DICT_DEBUG *) dict;
|
||||
|
||||
dict_close(dict_debug->real_dict);
|
||||
dict_free(dict);
|
||||
}
|
||||
|
||||
@ -143,21 +144,11 @@ DICT *dict_debug_open(const char *name, int open_flags, int dict_flags)
|
||||
msg_info("%s: %s", myname, name);
|
||||
|
||||
/*
|
||||
* Reuse a previously registered table if present. This prevents a config
|
||||
* containing both debug:foo:bar and foo:bar from creating two DICT
|
||||
* objects for foo:bar.
|
||||
*
|
||||
* TODO(wietse) use the annotated name from dict_make_registered_name().
|
||||
*/
|
||||
DICT *real_dict = dict_handle(name);
|
||||
|
||||
if (real_dict == 0)
|
||||
real_dict = dict_open(name, open_flags, dict_flags);
|
||||
dict_register(name, real_dict);
|
||||
|
||||
/*
|
||||
* Encapsulate the real dictionary.
|
||||
* dict_open() will reuse a previously registered table if present. This
|
||||
* prevents a config containing both debug:foo:bar and foo:bar from
|
||||
* creating two DICT objects for foo:bar.
|
||||
*/
|
||||
DICT *real_dict = dict_open(name, open_flags, dict_flags);
|
||||
DICT_DEBUG *dict_debug = (DICT_DEBUG *) dict_alloc(DICT_TYPE_DEBUG, name,
|
||||
sizeof(*dict_debug));
|
||||
|
||||
|
@ -1,19 +1,19 @@
|
||||
+ ./dict_open debug: read
|
||||
>>> dict_open 'debug:' read
|
||||
./dict_open: fatal: open dictionary: expecting "type:name" form instead of "debug:"
|
||||
+ ./dict_open debug:missing_colon_and_name read
|
||||
>>> dict_open 'debug:missing_colon_and_name' read
|
||||
./dict_open: fatal: open dictionary: expecting "type:name" form instead of "missing_colon_and_name"
|
||||
+ ./dict_open 'debug:static:{space in name}' read
|
||||
>>> dict_open 'debug:static:{space in name}' read
|
||||
owner=trusted (uid=2147483647)
|
||||
> get k
|
||||
./dict_open: static:{space in name} lookup: "k" = "space in name"
|
||||
k=space in name
|
||||
+ ./dict_open debug:debug:static:value read
|
||||
>>> dict_open 'debug:debug:static:value' read
|
||||
owner=trusted (uid=2147483647)
|
||||
> get k
|
||||
./dict_open: static:value lookup: "k" = "value"
|
||||
./dict_open: debug:static:value lookup: "k" = "value"
|
||||
k=value
|
||||
+ ./dict_open debug:internal:name write
|
||||
>>> dict_open 'debug:internal:name' write
|
||||
owner=trusted (uid=2147483647)
|
||||
> get k
|
||||
./dict_open: internal:name lookup: "k" = "not_found"
|
||||
@ -41,7 +41,7 @@ k: not found
|
||||
> first
|
||||
./dict_open: internal:name sequence: found EOF
|
||||
not found
|
||||
+ ./dict_open 'debug:fail:{oh no}' read
|
||||
>>> dict_open 'debug:fail:{oh no}' read
|
||||
owner=trusted (uid=2147483647)
|
||||
> get k
|
||||
./dict_open: fail:{oh no} lookup: "k" = "error"
|
||||
|
14
postfix/src/util/dict_debug_test.sh
Normal file → Executable file
14
postfix/src/util/dict_debug_test.sh
Normal file → Executable file
@ -1,13 +1,23 @@
|
||||
#!/bin/sh
|
||||
set -ex
|
||||
set -e
|
||||
|
||||
echo ">>> dict_open 'debug:' read"
|
||||
! ${VALGRIND} ./dict_open 'debug:' read </dev/null || exit 1
|
||||
|
||||
echo ">>> dict_open 'debug:missing_colon_and_name' read"
|
||||
! ${VALGRIND} ./dict_open 'debug:missing_colon_and_name' read </dev/null || exit 1
|
||||
|
||||
echo ">>> dict_open 'debug:static:{space in name}' read"
|
||||
${VALGRIND} ./dict_open 'debug:static:{space in name}' read <<EOF
|
||||
get k
|
||||
EOF
|
||||
|
||||
echo ">>> dict_open 'debug:debug:static:value' read"
|
||||
${VALGRIND} ./dict_open 'debug:debug:static:value' read <<EOF
|
||||
get k
|
||||
EOF
|
||||
|
||||
echo ">>> dict_open 'debug:internal:name' write"
|
||||
${VALGRIND} ./dict_open 'debug:internal:name' write <<EOF
|
||||
get k
|
||||
put k=v
|
||||
@ -19,6 +29,8 @@ get k
|
||||
del k
|
||||
first
|
||||
EOF
|
||||
|
||||
echo ">>> dict_open 'debug:fail:{oh no}' read"
|
||||
${VALGRIND} ./dict_open 'debug:fail:{oh no}' read <<EOF
|
||||
get k
|
||||
first
|
||||
|
@ -110,7 +110,7 @@ DICT *dict_inline_open(const char *name, int open_flags, int dict_flags)
|
||||
/*
|
||||
* Reuse the "internal" dictionary type.
|
||||
*/
|
||||
dict = dict_open3(DICT_TYPE_HT, name, open_flags, dict_flags);
|
||||
dict = dict_ht_open(name, open_flags, dict_flags);
|
||||
dict_type_override(dict, DICT_TYPE_INLINE);
|
||||
while ((nameval = mystrtokq(&cp, CHARS_COMMA_SP, CHARS_BRACE)) != 0) {
|
||||
if (nameval[0] == CHARS_BRACE[0])
|
||||
|
@ -82,7 +82,17 @@
|
||||
/* const char *type;
|
||||
/* DESCRIPTION
|
||||
/* This module implements a low-level interface to multiple
|
||||
/* physical dictionary types.
|
||||
/* dictionary types.
|
||||
/*
|
||||
/* In addition to providing a mapping from type names to
|
||||
/* implementations, this module deduplicates requests to open a
|
||||
/* dictionary with the same fingerprint (type, name, and initial
|
||||
/* flags), and manages the dictionary life cycle using reference
|
||||
/* counts maintained with dict_(un)register().
|
||||
/*
|
||||
/* The fingerprint, generated with dict_make_registered_name()
|
||||
/* and available as DICT.reg_name, may be used in dict_handle()
|
||||
/* calls.
|
||||
/*
|
||||
/* dict_open() takes a type:name pair that specifies a dictionary type
|
||||
/* and dictionary name, opens the dictionary, and returns a dictionary
|
||||
@ -164,8 +174,6 @@
|
||||
/* Enable preliminary code for bulk-mode database updates.
|
||||
/* The caller must create an exception handler with dict_jmp_alloc()
|
||||
/* and must trap exceptions from the database client with dict_setjmp().
|
||||
/* .IP DICT_FLAG_DEBUG
|
||||
/* Enable additional logging.
|
||||
/* .IP DICT_FLAG_UTF8_REQUEST
|
||||
/* With util_utf8_enable != 0, require that lookup/update/delete
|
||||
/* keys and values are valid UTF-8. Skip a lookup/update/delete
|
||||
@ -281,7 +289,11 @@
|
||||
/*
|
||||
/* dict_type_override() changes the symbolic dictionary type.
|
||||
/* This is used by dictionaries whose internals are based on
|
||||
/* some other dictionary type.
|
||||
/* some other dictionary type. dict_type_override() requires that
|
||||
/* the dictionary is not already registered with dict_register(),
|
||||
/* i.e., it must be the result from dict_xxx_open(), not from
|
||||
/* dict_open(). If needed in the future, this limitation may
|
||||
/* be lifted.
|
||||
/* DIAGNOSTICS
|
||||
/* Fatal error: open error, unsupported dictionary type, attempt to
|
||||
/* update non-writable dictionary.
|
||||
@ -481,19 +493,36 @@ DICT *dict_open3(const char *dict_type, const char *dict_name,
|
||||
{
|
||||
const char *myname = "dict_open";
|
||||
const DICT_OPEN_INFO *dp;
|
||||
VSTRING *reg_name = vstring_alloc(100);
|
||||
DICT *dict;
|
||||
|
||||
#define DICT_OPEN3_RETURN(d) do { \
|
||||
DICT *_d = (d); \
|
||||
dict_register(vstring_str(reg_name), _d); \
|
||||
vstring_free(reg_name); \
|
||||
return (_d); \
|
||||
} while (0)
|
||||
|
||||
/*
|
||||
* If the dictionary is already open, simply increase the reference count
|
||||
* to update an existing life cycle.
|
||||
*/
|
||||
dict_make_registered_name4(reg_name, dict_type, dict_name,
|
||||
open_flags, dict_flags);
|
||||
if ((dict = dict_handle(vstring_str(reg_name))) != 0)
|
||||
DICT_OPEN3_RETURN(dict);
|
||||
|
||||
if (*dict_type == 0 || *dict_name == 0)
|
||||
msg_fatal("open dictionary: expecting \"type:name\" form instead of \"%s:%s\"",
|
||||
dict_type, dict_name);
|
||||
if (NEED_DICT_OPEN_INIT())
|
||||
dict_open_init();
|
||||
if ((dp = dict_open_lookup(dict_type)) == 0)
|
||||
return (dict_surrogate(dict_type, dict_name, open_flags, dict_flags,
|
||||
"unsupported dictionary type: %s", dict_type));
|
||||
DICT_OPEN3_RETURN(dict_surrogate(dict_type, dict_name, open_flags,
|
||||
dict_flags, "unsupported dictionary type: %s", dict_type));
|
||||
if ((dict = dp->dict_fn(dict_name, open_flags, dict_flags)) == 0)
|
||||
return (dict_surrogate(dict_type, dict_name, open_flags, dict_flags,
|
||||
"cannot open %s:%s: %m", dict_type, dict_name));
|
||||
DICT_OPEN3_RETURN(dict_surrogate(dict_type, dict_name, open_flags,
|
||||
dict_flags, "cannot open %s:%s: %m", dict_type, dict_name));
|
||||
if (msg_verbose)
|
||||
msg_info("%s: %s:%s", myname, dict_type, dict_name);
|
||||
/* XXX The choice between wait-for-lock or no-wait is hard-coded. */
|
||||
@ -515,7 +544,8 @@ DICT *dict_open3(const char *dict_type, const char *dict_name,
|
||||
if ((dict->flags & DICT_FLAG_UTF8_ACTIVE) == 0
|
||||
&& DICT_NEED_UTF8_ACTIVATION(util_utf8_enable, dict_flags))
|
||||
dict = dict_utf8_activate(dict);
|
||||
return (dict);
|
||||
/* Register the result. */
|
||||
DICT_OPEN3_RETURN(dict);
|
||||
}
|
||||
|
||||
/* dict_open_register - register dictionary type */
|
||||
@ -601,6 +631,16 @@ DICT_MAPNAMES_EXTEND_FN dict_mapnames_extend(DICT_MAPNAMES_EXTEND_FN new_cb)
|
||||
|
||||
void dict_type_override(DICT *dict, const char *type)
|
||||
{
|
||||
|
||||
/*
|
||||
* To lift this limitation, compute a new reg_name, and implement a move
|
||||
* (copy+delete) operation from the old reg_name to the new one. Also
|
||||
* handle the case that the new destination name is already in use. The
|
||||
* above should be encapsulated in code adjacent to dict_register().
|
||||
*/
|
||||
if (dict->reg_name)
|
||||
msg_panic("%s: %s:%s is already registered",
|
||||
__func__, dict->type, dict->name);
|
||||
myfree(dict->type);
|
||||
dict->type = mystrdup(type);
|
||||
}
|
||||
|
@ -72,13 +72,13 @@ static const char *dict_pipe_lookup(DICT *dict, const char *query)
|
||||
DICT_PIPE *dict_pipe = (DICT_PIPE *) dict;
|
||||
DICT *map;
|
||||
char **cpp;
|
||||
char *dict_type_name;
|
||||
char *reg_name;
|
||||
const char *result = 0;
|
||||
|
||||
vstring_strcpy(dict_pipe->qr_buf, query);
|
||||
for (cpp = dict_pipe->map_pipe->argv; (dict_type_name = *cpp) != 0; cpp++) {
|
||||
if ((map = dict_handle(dict_type_name)) == 0)
|
||||
msg_panic("%s: dictionary \"%s\" not found", myname, dict_type_name);
|
||||
for (cpp = dict_pipe->map_pipe->argv; (reg_name = *cpp) != 0; cpp++) {
|
||||
if ((map = dict_handle(reg_name)) == 0)
|
||||
msg_panic("%s: dictionary \"%s\" not found", myname, reg_name);
|
||||
if ((result = dict_get(map, STR(dict_pipe->qr_buf))) == 0)
|
||||
DICT_ERR_VAL_RETURN(dict, map->error, result);
|
||||
vstring_strcpy(dict_pipe->qr_buf, result);
|
||||
@ -90,12 +90,17 @@ static const char *dict_pipe_lookup(DICT *dict, const char *query)
|
||||
|
||||
static void dict_pipe_close(DICT *dict)
|
||||
{
|
||||
static const char myname[] = "dict_pipe_close";
|
||||
DICT_PIPE *dict_pipe = (DICT_PIPE *) dict;
|
||||
DICT *map;
|
||||
char **cpp;
|
||||
char *dict_type_name;
|
||||
char *reg_name;
|
||||
|
||||
for (cpp = dict_pipe->map_pipe->argv; (dict_type_name = *cpp) != 0; cpp++)
|
||||
dict_unregister(dict_type_name);
|
||||
for (cpp = dict_pipe->map_pipe->argv; (reg_name = *cpp) != 0; cpp++) {
|
||||
if ((map = dict_handle(reg_name)) == 0)
|
||||
msg_panic("%s: dictionary \"%s\" not found", myname, reg_name);
|
||||
dict_close(map);
|
||||
}
|
||||
argv_free(dict_pipe->map_pipe);
|
||||
vstring_free(dict_pipe->qr_buf);
|
||||
dict_free(dict);
|
||||
@ -115,8 +120,6 @@ DICT *dict_pipe_open(const char *name, int open_flags, int dict_flags)
|
||||
int match_flags = 0;
|
||||
struct DICT_OWNER aggr_owner;
|
||||
size_t len;
|
||||
VSTRING *reg_name_buf = vstring_alloc(100);
|
||||
char *reg_name;
|
||||
|
||||
/*
|
||||
* Clarity first. Let the optimizer worry about redundant code.
|
||||
@ -126,8 +129,6 @@ DICT *dict_pipe_open(const char *name, int open_flags, int dict_flags)
|
||||
myfree(saved_name); \
|
||||
if (argv != 0) \
|
||||
argv_free(argv); \
|
||||
if (reg_name_buf != 0) \
|
||||
vstring_free(reg_name_buf); \
|
||||
return (x); \
|
||||
} while (0)
|
||||
|
||||
@ -176,12 +177,8 @@ DICT *dict_pipe_open(const char *name, int open_flags, int dict_flags)
|
||||
for (cpp = argv->argv; (dict_type_name = *cpp) != 0; cpp++) {
|
||||
if (msg_verbose)
|
||||
msg_info("%s: %s", myname, dict_type_name);
|
||||
reg_name = dict_make_registered_name(reg_name_buf, dict_type_name,
|
||||
open_flags, dict_flags);
|
||||
if ((dict = dict_handle(reg_name)) == 0)
|
||||
dict = dict_open(dict_type_name, open_flags, dict_flags);
|
||||
dict_register(reg_name, dict);
|
||||
argv_replace_one(argv, cpp - argv->argv, reg_name);
|
||||
argv_replace_one(argv, cpp - argv->argv, dict->reg_name);
|
||||
DICT_OWNER_AGGREGATE_UPDATE(aggr_owner, dict->owner);
|
||||
if (cpp == argv->argv)
|
||||
match_flags = dict->flags & (DICT_FLAG_FIXED | DICT_FLAG_PATTERN);
|
||||
@ -196,8 +193,7 @@ DICT *dict_pipe_open(const char *name, int open_flags, int dict_flags)
|
||||
dict_pipe->dict.close = dict_pipe_close;
|
||||
dict_pipe->dict.flags = dict_flags | match_flags;
|
||||
dict_pipe->dict.owner = aggr_owner;
|
||||
dict_pipe->qr_buf = reg_name_buf;
|
||||
reg_name_buf = 0;
|
||||
dict_pipe->qr_buf = vstring_alloc(100);
|
||||
dict_pipe->map_pipe = argv;
|
||||
argv = 0;
|
||||
DICT_PIPE_RETURN(&dict_pipe->dict);
|
||||
|
@ -1,22 +1,18 @@
|
||||
owner=trusted (uid=USER)
|
||||
> put 5 5
|
||||
5=5
|
||||
> put 3 3
|
||||
3=3
|
||||
> put 4 4
|
||||
4=4
|
||||
> put 1 1
|
||||
1=1
|
||||
> put 2 2
|
||||
2=2
|
||||
> first
|
||||
4=4
|
||||
> next
|
||||
2=2
|
||||
> next
|
||||
3=3
|
||||
> next
|
||||
1=1
|
||||
> next
|
||||
4=4
|
||||
> next
|
||||
5=5
|
||||
> next
|
||||
3=3
|
||||
> next
|
||||
1=1
|
||||
> next
|
||||
not found
|
||||
|
@ -86,7 +86,6 @@ void dict_test(int argc, char **argv)
|
||||
dict_allow_surrogate = 1;
|
||||
util_utf8_enable = 1;
|
||||
dict = dict_open(dict_name, open_flags, dict_flags);
|
||||
dict_register(dict_name, dict);
|
||||
vstream_printf("owner=%s (uid=%ld)\n",
|
||||
dict->owner.status == DICT_OWNER_TRUSTED ? "trusted" :
|
||||
dict->owner.status == DICT_OWNER_UNTRUSTED ? "untrusted" :
|
||||
|
@ -110,7 +110,7 @@ DICT *dict_thash_open(const char *path, int open_flags, int dict_flags)
|
||||
/*
|
||||
* Reuse the "internal" dictionary type.
|
||||
*/
|
||||
dict = dict_open3(DICT_TYPE_HT, path, open_flags, dict_flags);
|
||||
dict = dict_ht_open(path, open_flags, dict_flags);
|
||||
dict_type_override(dict, DICT_TYPE_THASH);
|
||||
|
||||
/*
|
||||
|
@ -76,16 +76,16 @@ static const char *dict_union_lookup(DICT *dict, const char *query)
|
||||
DICT_UNION *dict_union = (DICT_UNION *) dict;
|
||||
DICT *map;
|
||||
char **cpp;
|
||||
char *dict_type_name;
|
||||
char *reg_name;
|
||||
const char *result = 0;
|
||||
|
||||
/*
|
||||
* After Roel van Meer, postfix-users mailing list, Sept 2014.
|
||||
*/
|
||||
VSTRING_RESET(dict_union->re_buf);
|
||||
for (cpp = dict_union->map_union->argv; (dict_type_name = *cpp) != 0; cpp++) {
|
||||
if ((map = dict_handle(dict_type_name)) == 0)
|
||||
msg_panic("%s: dictionary \"%s\" not found", myname, dict_type_name);
|
||||
for (cpp = dict_union->map_union->argv; (reg_name = *cpp) != 0; cpp++) {
|
||||
if ((map = dict_handle(reg_name)) == 0)
|
||||
msg_panic("%s: dictionary \"%s\" not found", myname, reg_name);
|
||||
if ((result = dict_get(map, query)) != 0) {
|
||||
if (VSTRING_LEN(dict_union->re_buf) > 0)
|
||||
VSTRING_ADDCH(dict_union->re_buf, ',');
|
||||
@ -103,12 +103,17 @@ static const char *dict_union_lookup(DICT *dict, const char *query)
|
||||
|
||||
static void dict_union_close(DICT *dict)
|
||||
{
|
||||
static const char myname[] = "dict_union_close";
|
||||
DICT_UNION *dict_union = (DICT_UNION *) dict;
|
||||
DICT *map;
|
||||
char **cpp;
|
||||
char *dict_type_name;
|
||||
char *reg_name;
|
||||
|
||||
for (cpp = dict_union->map_union->argv; (dict_type_name = *cpp) != 0; cpp++)
|
||||
dict_unregister(dict_type_name);
|
||||
for (cpp = dict_union->map_union->argv; (reg_name = *cpp) != 0; cpp++) {
|
||||
if ((map = dict_handle(reg_name)) == 0)
|
||||
msg_panic("%s: dictionary \"%s\" not found", myname, reg_name);
|
||||
dict_close(map);
|
||||
}
|
||||
argv_free(dict_union->map_union);
|
||||
vstring_free(dict_union->re_buf);
|
||||
dict_free(dict);
|
||||
@ -128,8 +133,6 @@ DICT *dict_union_open(const char *name, int open_flags, int dict_flags)
|
||||
int match_flags = 0;
|
||||
struct DICT_OWNER aggr_owner;
|
||||
size_t len;
|
||||
VSTRING *reg_name_buf = vstring_alloc(100);
|
||||
char *reg_name;
|
||||
|
||||
/*
|
||||
* Clarity first. Let the optimizer worry about redundant code.
|
||||
@ -139,8 +142,6 @@ DICT *dict_union_open(const char *name, int open_flags, int dict_flags)
|
||||
myfree(saved_name); \
|
||||
if (argv != 0) \
|
||||
argv_free(argv); \
|
||||
if (reg_name_buf != 0) \
|
||||
vstring_free(reg_name_buf); \
|
||||
return (x); \
|
||||
} while (0)
|
||||
|
||||
@ -189,12 +190,8 @@ DICT *dict_union_open(const char *name, int open_flags, int dict_flags)
|
||||
for (cpp = argv->argv; (dict_type_name = *cpp) != 0; cpp++) {
|
||||
if (msg_verbose)
|
||||
msg_info("%s: %s", myname, dict_type_name);
|
||||
reg_name = dict_make_registered_name(reg_name_buf, dict_type_name,
|
||||
open_flags, dict_flags);
|
||||
if ((dict = dict_handle(reg_name)) == 0)
|
||||
dict = dict_open(dict_type_name, open_flags, dict_flags);
|
||||
dict_register(reg_name, dict);
|
||||
argv_replace_one(argv, cpp - argv->argv, reg_name);
|
||||
argv_replace_one(argv, cpp - argv->argv, dict->reg_name);
|
||||
DICT_OWNER_AGGREGATE_UPDATE(aggr_owner, dict->owner);
|
||||
if (cpp == argv->argv)
|
||||
match_flags = dict->flags & (DICT_FLAG_FIXED | DICT_FLAG_PATTERN);
|
||||
@ -209,8 +206,7 @@ DICT *dict_union_open(const char *name, int open_flags, int dict_flags)
|
||||
dict_union->dict.close = dict_union_close;
|
||||
dict_union->dict.flags = dict_flags | match_flags;
|
||||
dict_union->dict.owner = aggr_owner;
|
||||
dict_union->re_buf = reg_name_buf;
|
||||
reg_name_buf = 0;
|
||||
dict_union->re_buf = vstring_alloc(100);
|
||||
dict_union->map_union = argv;
|
||||
argv = 0;
|
||||
DICT_UNION_RETURN(&dict_union->dict);
|
||||
|
@ -1,6 +1,6 @@
|
||||
owner=trusted (uid=2147483647)
|
||||
> flags
|
||||
dict flags fixed|lock|replace|utf8_request|utf8_active
|
||||
dict flags fixed|lock|dup_replace|utf8_request|utf8_active
|
||||
> verbose
|
||||
> get foo
|
||||
foo: not found
|
||||
|
@ -124,7 +124,6 @@ static ARGV *match_list_parse(MATCH_LIST *match_list, ARGV *pat_list,
|
||||
char *bp = string;
|
||||
char *start;
|
||||
char *item;
|
||||
char *map_type_name_flags;
|
||||
int match;
|
||||
|
||||
/*
|
||||
@ -170,12 +169,8 @@ static ARGV *match_list_parse(MATCH_LIST *match_list, ARGV *pat_list,
|
||||
msg_fatal("%s: read file %s: %m", myname, item);
|
||||
}
|
||||
} else if (MATCH_DICTIONARY(item)) { /* type:table */
|
||||
vstring_sprintf(buf, "%s%s(%o,%s)", match ? "" : "!",
|
||||
item, OPEN_FLAGS, dict_flags_str(DICT_FLAGS));
|
||||
map_type_name_flags = STR(buf) + (match == 0);
|
||||
if (dict_handle(map_type_name_flags) == 0)
|
||||
dict_register(map_type_name_flags,
|
||||
dict_open(item, OPEN_FLAGS, DICT_FLAGS));
|
||||
vstring_sprintf(buf, "%s%s", match ? "" : "!",
|
||||
dict_open(item, OPEN_FLAGS, DICT_FLAGS)->reg_name);
|
||||
argv_add(pat_list, STR(buf), (char *) 0);
|
||||
} else { /* other pattern */
|
||||
casefold(match_list->fold_buf, match ?
|
||||
|
Loading…
x
Reference in New Issue
Block a user