2
0
mirror of https://github.com/vdukhovni/postfix synced 2025-09-01 06:35:27 +00:00

postfix-3.2-20170122

This commit is contained in:
Wietse Venema
2017-01-22 00:00:00 -05:00
committed by Viktor Dukhovni
parent 7a51b3ef05
commit e69c67e916
23 changed files with 836 additions and 758 deletions

View File

@@ -22845,3 +22845,21 @@ Apologies for any names omitted.
compatibility, for check_sender_access and check_recipient_access. compatibility, for check_sender_access and check_recipient_access.
This now uses 'user@' lookup support in the mail_addr_find() This now uses 'user@' lookup support in the mail_addr_find()
engine. File: global/mail_addr_find.*, smtpd/smtpd_check.c. engine. File: global/mail_addr_find.*, smtpd/smtpd_check.c.
20170122
Cleanup: separated the database query form from the address
form that is input to mail_addr_find_() or mail_addr_map*(),
in attempt to make code more obviously correct. Files:
global/mail_addr_find.c, global/mail_addr_map.c.
Abandoned an experiment that used internal-form queries for
all maps, because it would be very difficult to test. The
tests inputs would have to compensate for multiple levels
of unquoting by postmap, C compilers, or shell interpreters.
Cleanup: moved the backwards-compatibility lookup strategy
(try the external address form first, then the internal
address form if it is different) inside the loop that
iterates over full and partial address forms. File:
global/mail_addr_find.c.

View File

@@ -8,15 +8,11 @@ Wish list:
Enable external-form lookups for smtpd access maps. Enable external-form lookups for smtpd access maps.
Enable caching in quote_822_local. Convert postalias(1) to store external-form keys, and convert
aliases(5) to perform external-first lookup with fallback to
Quoting: in smtp_map11, don't convert to external. internal form, to make it consistent with the rest of Postfix.
In several years we may remove the internal-form fallbacks
Make the address substring generation ("strategy") suitable with a compatibility_level safety net.
for dict-based applications such as access(5) maps or local
aliases. Either that, or do some serious surgery on the
dict-based mechanisms. For access(5) maps this requires a
localpart[+ext]@ query that comes after the domain queries.
In the bounce daemon, set util_utf8_enable if returning an In the bounce daemon, set util_utf8_enable if returning an
SMTPUTF8 message. SMTPUTF8 message.
@@ -37,18 +33,6 @@ Wish list:
RHS. This will not preserve trailing comments in lines that RHS. This will not preserve trailing comments in lines that
are modified with "postconf -e" and the like. are modified with "postconf -e" and the like.
The cleanup daemon searches canonical_maps and virtual_alias_maps
with quoted address forms. The address local part should
be in unquoted form before it is split into name and
extension. Note, however, that although quoting is done
over the entire localpart, unquoting is not simply a matter
of removing the outer quotes. The fix will require careful
consideration of the responsibilities of mail_addr_map(),
mail_addr_find(), and mail_addr_crunch(), and making sure
that the callers can handle quoted results. For example,
sender_bcc_maps and recipient_bcc_maps invoke mail_addr_find()
with unquoted forms and expects an unquoted result, and so on.
Maintainability: replace lengthy libmilter-API argument lists Maintainability: replace lengthy libmilter-API argument lists
with named parameters, as with the libtls API. with named parameters, as with the libtls API.

View File

@@ -46,7 +46,8 @@ POSTMAP(1) POSTMAP(1)
When the <i>key</i> specifies email address information, the localpart should When the <i>key</i> specifies email address information, the localpart should
be enclosed with double quotes if required by <a href="http://tools.ietf.org/html/rfc5322">RFC 5322</a>. For example, an be enclosed with double quotes if required by <a href="http://tools.ietf.org/html/rfc5322">RFC 5322</a>. For example, an
address localpart that contains address localpart that contains ";", or a localpart that starts or ends
with ".".
By default the lookup key is mapped to lowercase to make the lookups By default the lookup key is mapped to lowercase to make the lookups
case insensitive; as of Postfix 2.3 this case folding happens only with case insensitive; as of Postfix 2.3 this case folding happens only with
@@ -59,7 +60,7 @@ POSTMAP(1) POSTMAP(1)
<b>COMMAND-LINE ARGUMENTS</b> <b>COMMAND-LINE ARGUMENTS</b>
<b>-b</b> Enable message body query mode. When reading lookup keys from <b>-b</b> Enable message body query mode. When reading lookup keys from
standard input with "<b>-q -</b>", process the input as if it is an standard input with "<b>-q -</b>", process the input as if it is an
email message in <a href="http://tools.ietf.org/html/rfc2822">RFC 2822</a> format. Each line of body content email message in <a href="http://tools.ietf.org/html/rfc5322">RFC 5322</a> format. Each line of body content
becomes one lookup key. becomes one lookup key.
By default, the <b>-b</b> option starts generating lookup keys at the By default, the <b>-b</b> option starts generating lookup keys at the
@@ -96,7 +97,7 @@ POSTMAP(1) POSTMAP(1)
<b>-h</b> Enable message header query mode. When reading lookup keys from <b>-h</b> Enable message header query mode. When reading lookup keys from
standard input with "<b>-q -</b>", process the input as if it is an standard input with "<b>-q -</b>", process the input as if it is an
email message in <a href="http://tools.ietf.org/html/rfc2822">RFC 2822</a> format. Each logical header line email message in <a href="http://tools.ietf.org/html/rfc5322">RFC 5322</a> format. Each logical header line
becomes one lookup key. A multi-line header becomes one lookup becomes one lookup key. A multi-line header becomes one lookup
key with one or more embedded newline characters. key with one or more embedded newline characters.

View File

@@ -56,7 +56,7 @@ keys is supported as of Postfix 3.2.
When the \fIkey\fR specifies email address information, the When the \fIkey\fR specifies email address information, the
localpart should be enclosed with double quotes if required localpart should be enclosed with double quotes if required
by RFC 5322. For example, an address localpart that contains by RFC 5322. For example, an address localpart that contains
';' or that ends on '.'. ";", or a localpart that starts or ends with ".".
By default the lookup key is mapped to lowercase to make By default the lookup key is mapped to lowercase to make
the lookups case insensitive; as of Postfix 2.3 this case the lookups case insensitive; as of Postfix 2.3 this case
@@ -74,7 +74,7 @@ information with $\fInumber\fR substitutions.
.IP \fB\-b\fR .IP \fB\-b\fR
Enable message body query mode. When reading lookup keys Enable message body query mode. When reading lookup keys
from standard input with "\fB\-q \-\fR", process the input from standard input with "\fB\-q \-\fR", process the input
as if it is an email message in RFC 2822 format. Each line as if it is an email message in RFC 5322 format. Each line
of body content becomes one lookup key. of body content becomes one lookup key.
.sp .sp
By default, the \fB\-b\fR option starts generating lookup By default, the \fB\-b\fR option starts generating lookup
@@ -111,7 +111,7 @@ is controlled by appending a flag to a pattern.
.IP \fB\-h\fR .IP \fB\-h\fR
Enable message header query mode. When reading lookup keys Enable message header query mode. When reading lookup keys
from standard input with "\fB\-q \-\fR", process the input from standard input with "\fB\-q \-\fR", process the input
as if it is an email message in RFC 2822 format. Each as if it is an email message in RFC 5322 format. Each
logical header line becomes one lookup key. A multi\-line logical header line becomes one lookup key. A multi\-line
header becomes one lookup key with one or more embedded header becomes one lookup key with one or more embedded
newline characters. newline characters.

View File

@@ -165,7 +165,8 @@ off_t cleanup_addr_sender(CLEANUP_STATE *state, const char *buf)
if ((state->flags & CLEANUP_FLAG_BCC_OK) if ((state->flags & CLEANUP_FLAG_BCC_OK)
&& *STR(clean_addr) && *STR(clean_addr)
&& cleanup_send_bcc_maps) { && cleanup_send_bcc_maps) {
if ((bcc = mail_addr_find(cleanup_send_bcc_maps, STR(clean_addr), if ((bcc = mail_addr_find_to_internal(cleanup_send_bcc_maps,
STR(clean_addr),
IGNORE_EXTENSION)) != 0) { IGNORE_EXTENSION)) != 0) {
cleanup_addr_bcc(state, bcc); cleanup_addr_bcc(state, bcc);
} else if (cleanup_send_bcc_maps->error) { } else if (cleanup_send_bcc_maps->error) {
@@ -228,7 +229,8 @@ void cleanup_addr_recipient(CLEANUP_STATE *state, const char *buf)
if ((state->flags & CLEANUP_FLAG_BCC_OK) if ((state->flags & CLEANUP_FLAG_BCC_OK)
&& *STR(clean_addr) && *STR(clean_addr)
&& cleanup_rcpt_bcc_maps) { && cleanup_rcpt_bcc_maps) {
if ((bcc = mail_addr_find(cleanup_rcpt_bcc_maps, STR(clean_addr), if ((bcc = mail_addr_find_to_internal(cleanup_rcpt_bcc_maps,
STR(clean_addr),
IGNORE_EXTENSION)) != 0) { IGNORE_EXTENSION)) != 0) {
cleanup_addr_bcc(state, bcc); cleanup_addr_bcc(state, bcc);
} else if (cleanup_rcpt_bcc_maps->error) { } else if (cleanup_rcpt_bcc_maps->error) {

View File

@@ -105,6 +105,7 @@ int cleanup_map11_external(CLEANUP_STATE *state, VSTRING *addr,
*/ */
for (count = 0; count < MAX_RECURSION; count++) { for (count = 0; count < MAX_RECURSION; count++) {
if ((new_addr = mail_addr_map_opt(maps, STR(addr), propagate, if ((new_addr = mail_addr_map_opt(maps, STR(addr), propagate,
MAIL_ADDR_FORM_EXTERNAL,
MAIL_ADDR_FORM_EXTERNAL, MAIL_ADDR_FORM_EXTERNAL,
MAIL_ADDR_FORM_EXTERNAL)) != 0) { MAIL_ADDR_FORM_EXTERNAL)) != 0) {
if (new_addr->argc > 1) if (new_addr->argc > 1)

View File

@@ -118,7 +118,7 @@ TESTPROG= domain_list dot_lockfile mail_addr_crunch mail_addr_find \
valid_mailhost_addr own_inet_addr header_body_checks \ valid_mailhost_addr own_inet_addr header_body_checks \
data_redirect addr_match_list safe_ultostr verify_sender_addr \ data_redirect addr_match_list safe_ultostr verify_sender_addr \
mail_version mail_dict server_acl uxtext mail_parm_split \ mail_version mail_dict server_acl uxtext mail_parm_split \
fold_addr smtp_reply_footer mail_addr_map_tester fold_addr smtp_reply_footer mail_addr_map
LIBS = ../../lib/lib$(LIB_PREFIX)util$(LIB_SUFFIX) LIBS = ../../lib/lib$(LIB_PREFIX)util$(LIB_SUFFIX)
LIB_DIR = ../../lib LIB_DIR = ../../lib
@@ -249,8 +249,10 @@ off_cvt: $(LIB) $(LIBS)
$(CC) $(CFLAGS) -DTEST -o $@ $@.c $(LIB) $(LIBS) $(SYSLIBS) $(CC) $(CFLAGS) -DTEST -o $@ $@.c $(LIB) $(LIBS) $(SYSLIBS)
mv junk $@.o mv junk $@.o
mail_addr_map_tester: mail_addr_map_tester.c $(LIB) $(LIBS) mail_addr_map: mail_addr_map.c $(LIB) $(LIBS)
mv $@.o junk
$(CC) $(CFLAGS) -DTEST -o $@ $@.c $(LIB) $(LIBS) $(SYSLIBS) $(CC) $(CFLAGS) -DTEST -o $@ $@.c $(LIB) $(LIBS) $(SYSLIBS)
mv junk $@.o
mail_addr_find: $(LIB) $(LIBS) mail_addr_find: $(LIB) $(LIBS)
mv $@.o junk mv $@.o junk
@@ -683,9 +685,9 @@ mail_addr_find_test: update mail_addr_find mail_addr_find.in mail_addr_find.ref
diff mail_addr_find.ref mail_addr_find.tmp diff mail_addr_find.ref mail_addr_find.tmp
rm -f mail_addr_find.tmp rm -f mail_addr_find.tmp
mail_addr_map_test: update mail_addr_map_tester mail_addr_map.ref mail_addr_map_test: update mail_addr_map mail_addr_map.ref
-$(SHLIB_ENV) $(VALGRIND) ./mail_addr_map_tester pass_tests -$(SHLIB_ENV) $(VALGRIND) ./mail_addr_map pass_tests
-$(SHLIB_ENV) $(VALGRIND) ./mail_addr_map_tester fail_tests >mail_addr_map.tmp 2>&1 -$(SHLIB_ENV) $(VALGRIND) ./mail_addr_map fail_tests >mail_addr_map.tmp 2>&1
diff mail_addr_map.ref mail_addr_map.tmp diff mail_addr_map.ref mail_addr_map.tmp
rm -f mail_addr_map.tmp rm -f mail_addr_map.tmp
@@ -1490,6 +1492,7 @@ mail_addr_find.o: ../../include/dict.h
mail_addr_find.o: ../../include/msg.h mail_addr_find.o: ../../include/msg.h
mail_addr_find.o: ../../include/myflock.h mail_addr_find.o: ../../include/myflock.h
mail_addr_find.o: ../../include/mymalloc.h mail_addr_find.o: ../../include/mymalloc.h
mail_addr_find.o: ../../include/name_mask.h
mail_addr_find.o: ../../include/stringops.h mail_addr_find.o: ../../include/stringops.h
mail_addr_find.o: ../../include/sys_defs.h mail_addr_find.o: ../../include/sys_defs.h
mail_addr_find.o: ../../include/vbuf.h mail_addr_find.o: ../../include/vbuf.h
@@ -1526,23 +1529,6 @@ mail_addr_map.o: mail_addr_map.h
mail_addr_map.o: maps.h mail_addr_map.o: maps.h
mail_addr_map.o: quote_822_local.h mail_addr_map.o: quote_822_local.h
mail_addr_map.o: quote_flags.h mail_addr_map.o: quote_flags.h
mail_addr_map_tester.o: ../../include/argv.h
mail_addr_map_tester.o: ../../include/check_arg.h
mail_addr_map_tester.o: ../../include/dict.h
mail_addr_map_tester.o: ../../include/msg.h
mail_addr_map_tester.o: ../../include/myflock.h
mail_addr_map_tester.o: ../../include/mymalloc.h
mail_addr_map_tester.o: ../../include/sys_defs.h
mail_addr_map_tester.o: ../../include/vbuf.h
mail_addr_map_tester.o: ../../include/vstream.h
mail_addr_map_tester.o: ../../include/vstring.h
mail_addr_map_tester.o: canon_addr.h
mail_addr_map_tester.o: mail_addr_form.h
mail_addr_map_tester.o: mail_addr_map.h
mail_addr_map_tester.o: mail_addr_map_tester.c
mail_addr_map_tester.o: mail_conf.h
mail_addr_map_tester.o: mail_params.h
mail_addr_map_tester.o: maps.h
mail_command_client.o: ../../include/attr.h mail_command_client.o: ../../include/attr.h
mail_command_client.o: ../../include/check_arg.h mail_command_client.o: ../../include/check_arg.h
mail_command_client.o: ../../include/htable.h mail_command_client.o: ../../include/htable.h

View File

@@ -11,12 +11,13 @@
/* const char *address; /* const char *address;
/* char **extension; /* char **extension;
/* /*
/* const char *mail_addr_find_opt(maps, address, extension, /* const char *mail_addr_find_opt(maps, address, extension, in_form,
/* in_form, out_form, strategy) /* query_form, out_form, strategy)
/* MAPS *maps; /* MAPS *maps;
/* const char *address; /* const char *address;
/* char **extension; /* char **extension;
/* int in_form; /* int in_form;
/* int in_form;
/* int out_form; /* int out_form;
/* int strategy; /* int strategy;
/* LEGACY SUPPORT /* LEGACY SUPPORT
@@ -25,6 +26,11 @@
/* const char *address; /* const char *address;
/* char **extension; /* char **extension;
/* /*
/* const char *mail_addr_find_to_internal(maps, address, extension)
/* MAPS *maps;
/* const char *address;
/* char **extension;
/*
/* const char *mail_addr_find_strategy(maps, address, extension) /* const char *mail_addr_find_strategy(maps, address, extension)
/* MAPS *maps; /* MAPS *maps;
/* const char *address; /* const char *address;
@@ -37,33 +43,37 @@
/* preferences when it opens the maps. /* preferences when it opens the maps.
/* The result is overwritten upon each call. /* The result is overwritten upon each call.
/* /*
/* The table key and value are expected to be in external /* In the lookup table, the key is expected to be in external
/* (quoted) form. Override these assumptions with the in_form /* form (as produced with the postmap command) and the value is
/* expected to be in external (quoted) form if it is an email
/* address. Override these assumptions with the query_form
/* and out_form arguments. /* and out_form arguments.
/* /*
/* With mail_addr_find_int_to_ext(), the specified address is in /* With mail_addr_find_int_to_ext(), the specified address is in
/* internal (unquoted) form. The result is in the form found /* internal (unquoted) form, the query is made in external (quoted)
/* in the table (it is not necessarily an email address). This /* form, and the result is in the form found in the table (it is
/* version minimizes internal/external (unquoted/quoted) /* not necessarily an email address). This version minimizes
/* conversions of the query, extension, or result. /* internal/external (unquoted/quoted) conversions of the input,
/* query, extension, or result.
/* /*
/* mail_addr_find_opt() gives more control, at the cost of /* mail_addr_find_opt() gives more control, at the cost of
/* additional conversions between internal and external forms. /* additional conversions between internal and external forms.
/* In particular, the output conversion to internal form assumes /* In particular, output conversion to internal form assumes
/* that the lookup result is an email address. /* that the lookup result is an email address.
/* /*
/* mail_addr_find() is used by legacy code that historically /* mail_addr_find() is used by legacy code that historically searched
/* searched with internal-form keys. The lookup strategy is /* with internal-form queries. The input is in internal form. It
/* to first look up with (in_form, out_form) of (INTERNAL, /* searches with external-form queries first, and falls back to
/* NOCONV), which converts the key to external form. If no /* internal-form queries if no result was found and the external
/* result is found, and the internal and external key forms /* and internal forms differ. The result is external form (i.e. no
/* differ, there is a backwards-compatibility lookup with /* conversion).
/* (in_form, out_form) of (NOCONV, NOCONV).
/* /*
/* mail_addr_find_strategy() overrides the default search /* mail_addr_find_to_internal() is like mail_addr_find() but assumes
/* strategy for full and partial addresses. /* that the lookup result is one external-form email address,
/* and converts it to internal form.
/* /*
/* An address that is in the form \fIuser\fR matches itself. /* mail_addr_find_strategy() is like mail_addr_find() but overrides
/* the default search strategy for full and partial addresses.
/* /*
/* Arguments: /* Arguments:
/* .IP maps /* .IP maps
@@ -77,48 +87,54 @@
/* The copy includes the recipient address delimiter. /* The copy includes the recipient address delimiter.
/* The copy is in internal (unquoted) form. /* The copy is in internal (unquoted) form.
/* The caller is expected to pass the copy to myfree(). /* The caller is expected to pass the copy to myfree().
/* .IP query_form
/* The address form to use for database queries: one of
/* MAIL_ADDR_FORM_INTERNAL (unquoted form), MAIL_ADDR_FORM_EXTERNAL
/* (quoted form), or MAIL_ADDR_FORM_EXTERNAL_FIRST (external form,
/* then internal form if the external and internal forms differ).
/* .IP in_form /* .IP in_form
/* .IP out_form /* .IP out_form
/* Input and output address forms, either MAIL_ADDR_FORM_INTERNAL /* Input and output address forms, one of MAIL_ADDR_FORM_INTERNAL
/* (unquoted form), MAIL_ADDR_FORM_EXTERNAL (quoted form), or /* (unquoted form), or MAIL_ADDR_FORM_EXTERNAL (quoted form).
/* MAIL_ADDR_FORM_NOCONV (don't convert between unquoted and
/* quoted form).
/* .IP strategy /* .IP strategy
/* The lookup strategy for full and partial addresses, specified /* The lookup strategy for full and partial addresses, specified
/* as the binary OR of one or more of the following. These /* as the binary OR of one or more of the following. These lookups
/* lookups are implemented in the order as listed below. /* are implemented in the order as listed below.
/* .RS /* .RS
/* .IP MAIL_ADDR_FIND_FULL /* .IP MAF_STRATEGY_DEFAULT
/* A convenience alias for (MAF_STRATEGY_FULL | MAF_STRATEGY_NOEXT |
/* MAF_STRATEGY_LOCALPART_IF_LOCAL | MAF_STRATEGY_AT_DOMAIN).
/* .IP MAF_STRATEGY_FULL
/* Look up the full email address. /* Look up the full email address.
/* .IP MAIL_ADDR_FIND_NOEXT /* .IP MAF_STRATEGY_NOEXT
/* If no match was found, and the address has an extension, /* If no match was found, and the address has a localpart extension,
/* look up the address after removing the address extension. /* look up the address after removing the extension.
/* .IP MAIL_ADDR_FIND_LOCALPART_IF_LOCAL /* .IP MAF_STRATEGY_LOCALPART_IF_LOCAL
/* If no match was found, and the domain matches myorigin, /* If no match was found, and the domain matches myorigin,
/* mydestination, or any proxy_interfaces IP address, look up /* mydestination, or any inet_interfaces or proxy_interfaces IP
/* the localpart. If no match was found, and the address has /* address, look up the localpart. If no match was found, and the
/* an extension, repeat the same query after removing the /* address has a localpart extension, repeat the same query after
/* address extension unless MAIL_ADDR_FIND_NOEXT is specified. /* removing the extension unless MAF_STRATEGY_NOEXT is specified.
/* .IP MAIL_ADDR_FIND_LOCALPART_AT_IF_LOCAL /* .IP MAF_STRATEGY_LOCALPART_AT_IF_LOCAL
/* As above, but using the localpart@ instead. /* As above, but using the localpart@ instead.
/* .IP MAIL_ADDR_FIND_ATDOMAIN /* .IP MAF_STRATEGY_AT_DOMAIN
/* If no match was found, look up the @domain without localpart. /* If no match was found, look up the @domain without localpart.
/* .IP MAIL_ADDR_FIND_DOMAIN /* .IP MAF_STRATEGY_DOMAIN
/* If no match was found, look up the domain without localpart. /* If no match was found, look up the domain without localpart.
/* .IP MAIL_ADDR_FIND_PMS /* .IP MAF_STRATEGY_PMS
/* When used with MAIL_ADDR_FIND_DOMAIN, also matches subdomains. /* When used with MAF_STRATEGY_DOMAIN, also matches subdomains.
/* .IP MAIL_ADDR_FIND_PMDS /* .IP MAF_STRATEGY_PMDS
/* When used with MAIL_ADDR_FIND_DOMAIN, also matches dot-subdomains. /* When used with MAF_STRATEGY_DOMAIN, also matches dot-subdomains.
/* .IP MAIL_ADDR_FIND_LOCALPART_AT /* .IP MAF_STRATEGY_LOCALPART_AT
/* If no match was found, look up the localpart@, regardless /* If no match was found, look up the localpart@, regardless of
/* of the domain content. /* the domain content.
/* .RE /* .RE
/* DIAGNOSTICS /* DIAGNOSTICS
/* The maps->error value is non-zero when the lookup /* The maps->error value is non-zero when the lookup should be
/* should be tried again. /* tried again.
/* SEE ALSO /* SEE ALSO
/* maps(3), multi-dictionary search /* maps(3), multi-dictionary search resolve_local(3), recognize
/* resolve_local(3), recognize local system /* local system
/* LICENSE /* LICENSE
/* .ad /* .ad
/* .fi /* .fi
@@ -156,27 +172,22 @@
#define STR vstring_str #define STR vstring_str
#ifdef TEST
static const NAME_MASK strategy_table[] = { static const NAME_MASK strategy_table[] = {
"full", MAIL_ADDR_FIND_FULL, "full", MAF_STRATEGY_FULL,
"noext", MAIL_ADDR_FIND_NOEXT, "noext", MAF_STRATEGY_NOEXT,
"localpart_if_local", MAIL_ADDR_FIND_LOCALPART_IF_LOCAL, "localpart_if_local", MAF_STRATEGY_LOCALPART_IF_LOCAL,
"localpart_at_if_local", MAIL_ADDR_FIND_LOCALPART_AT_IF_LOCAL, "localpart_at_if_local", MAF_STRATEGY_LOCALPART_AT_IF_LOCAL,
"atdomain", MAIL_ADDR_FIND_ATDOMAIN, "atdomain", MAF_STRATEGY_AT_DOMAIN,
"domain", MAIL_ADDR_FIND_DOMAIN, "domain", MAF_STRATEGY_DOMAIN,
"pms", MAIL_ADDR_FIND_PMS, "pms", MAF_STRATEGY_PMS,
"pmds", MAIL_ADDR_FIND_PMDS, "pmds", MAF_STRATEGY_PMDS,
"localpartat", MAIL_ADDR_FIND_LOCALPART_AT, "localpart_at", MAF_STRATEGY_LOCALPART_AT,
"default", MAIL_ADDR_FIND_DEFAULT, "default", MAF_STRATEGY_DEFAULT,
0, -1, 0, -1,
}; };
/*
* Specify what keys are partial or full, to avoid matching partial
* addresses with regular expressions.
*/
#define FULL 0
#define PARTIAL DICT_FLAG_FIXED
/* strategy_from_string - symbolic strategy flags to internal form */ /* strategy_from_string - symbolic strategy flags to internal form */
static int strategy_from_string(const char *strategy_string) static int strategy_from_string(const char *strategy_string)
@@ -199,29 +210,63 @@ static const char *strategy_to_string(VSTRING *res_buf, int strategy_mask)
NAME_MASK_WARN | NAME_MASK_PIPE)); NAME_MASK_WARN | NAME_MASK_PIPE));
} }
/* find_addr - helper to search map with external-form address */ #endif
/*
* Specify what keys are partial or full, to avoid matching partial
* addresses with regular expressions.
*/
#define FULL 0
#define PARTIAL DICT_FLAG_FIXED
/* find_addr - helper to search maps with the right query form */
static const char *find_addr(MAPS *path, const char *address, int flags, static const char *find_addr(MAPS *path, const char *address, int flags,
int with_domain, int find_form, VSTRING *ext_addr_buf) int with_domain, int query_form, VSTRING *ext_addr_buf)
{ {
const char *result;
#define SANS_DOMAIN 0 #define SANS_DOMAIN 0
#define WITH_DOMAIN 1 #define WITH_DOMAIN 1
if (find_form == MAIL_ADDR_FORM_INTERNAL) { switch (query_form) {
/*
* Query with external-form (quoted) address.
*/
case MAIL_ADDR_FORM_EXTERNAL:
case MAIL_ADDR_FORM_EXTERNAL_FIRST:
quote_822_local_flags(ext_addr_buf, address, quote_822_local_flags(ext_addr_buf, address,
with_domain ? QUOTE_FLAG_DEFAULT : with_domain ? QUOTE_FLAG_DEFAULT :
QUOTE_FLAG_DEFAULT | QUOTE_FLAG_BARE_LOCALPART); QUOTE_FLAG_DEFAULT | QUOTE_FLAG_BARE_LOCALPART);
address = STR(ext_addr_buf); result = maps_find(path, STR(ext_addr_buf), flags);
if (result != 0 || path->error != 0
|| query_form == MAIL_ADDR_FORM_EXTERNAL
|| strcmp(address, STR(ext_addr_buf)) == 0)
break;
/* FALLTHROUGH */
/*
* Query with internal-form (unquoted) address.
*/
case MAIL_ADDR_FORM_INTERNAL:
result = maps_find(path, address, flags);
break;
/*
* Can't happen.
*/
default:
msg_panic("mail_addr_find: bad query_form: %d", query_form);
} }
return (maps_find(path, address, flags)); return (result);
} }
/* find_local - search on localpart info */ /* find_local - search on localpart info */
static const char *find_local(MAPS *path, char *ratsign, int rats_offs, static const char *find_local(MAPS *path, char *ratsign, int rats_offs,
char *int_full_key, char *int_bare_key, char *int_full_key, char *int_bare_key,
int find_form, char **extp, char **saved_ext, int query_form, char **extp, char **saved_ext,
VSTRING *ext_addr_buf) VSTRING *ext_addr_buf)
{ {
const char *myname = "mail_addr_find"; const char *myname = "mail_addr_find";
@@ -230,15 +275,15 @@ static const char *find_local(MAPS *path, char *ratsign, int rats_offs,
int saved_ch; int saved_ch;
/* /*
* This was ripped from the middle of a function so it can be reused, * This code was ripped from the middle of a function so that it can be
* that's why the interface makes no sense. * reused multiple times, that's why the interface makes little sense.
*/ */
with_domain = rats_offs ? WITH_DOMAIN : SANS_DOMAIN; with_domain = rats_offs ? WITH_DOMAIN : SANS_DOMAIN;
saved_ch = *(unsigned char *) (ratsign + rats_offs); saved_ch = *(unsigned char *) (ratsign + rats_offs);
*(ratsign + rats_offs) = 0; *(ratsign + rats_offs) = 0;
result = find_addr(path, int_full_key, PARTIAL, with_domain, result = find_addr(path, int_full_key, PARTIAL, with_domain,
find_form, ext_addr_buf); query_form, ext_addr_buf);
*(ratsign + rats_offs) = saved_ch; *(ratsign + rats_offs) = saved_ch;
if (result == 0 && path->error == 0 && int_bare_key != 0) { if (result == 0 && path->error == 0 && int_bare_key != 0) {
if ((ratsign = strrchr(int_bare_key, '@')) == 0) if ((ratsign = strrchr(int_bare_key, '@')) == 0)
@@ -246,7 +291,7 @@ static const char *find_local(MAPS *path, char *ratsign, int rats_offs,
saved_ch = *(unsigned char *) (ratsign + rats_offs); saved_ch = *(unsigned char *) (ratsign + rats_offs);
*(ratsign + rats_offs) = 0; *(ratsign + rats_offs) = 0;
if ((result = find_addr(path, int_bare_key, PARTIAL, with_domain, if ((result = find_addr(path, int_bare_key, PARTIAL, with_domain,
find_form, ext_addr_buf)) != 0 query_form, ext_addr_buf)) != 0
&& extp != 0) { && extp != 0) {
*extp = *saved_ext; *extp = *saved_ext;
*saved_ext = 0; *saved_ext = 0;
@@ -259,11 +304,11 @@ static const char *find_local(MAPS *path, char *ratsign, int rats_offs,
/* mail_addr_find_opt - map a canonical address */ /* mail_addr_find_opt - map a canonical address */
const char *mail_addr_find_opt(MAPS *path, const char *address, char **extp, const char *mail_addr_find_opt(MAPS *path, const char *address, char **extp,
int in_form, int out_form, int strategy) int in_form, int query_form,
int out_form, int strategy)
{ {
const char *myname = "mail_addr_find"; const char *myname = "mail_addr_find";
VSTRING *ext_addr_buf = 0; VSTRING *ext_addr_buf = 0;
int find_form;
VSTRING *int_addr_buf = 0; VSTRING *int_addr_buf = 0;
const char *int_addr; const char *int_addr;
static VSTRING *int_result = 0; static VSTRING *int_result = 0;
@@ -275,27 +320,27 @@ const char *mail_addr_find_opt(MAPS *path, const char *address, char **extp,
int rc = 0; int rc = 0;
/* /*
* Optionally convert input from external form. * Optionally convert the address from external form.
*/ */
if (in_form == MAIL_ADDR_FORM_EXTERNAL) { if (in_form == MAIL_ADDR_FORM_EXTERNAL) {
int_addr_buf = vstring_alloc(100); int_addr_buf = vstring_alloc(100);
unquote_822_local(int_addr_buf, address); unquote_822_local(int_addr_buf, address);
int_addr = STR(int_addr_buf); int_addr = STR(int_addr_buf);
find_form = MAIL_ADDR_FORM_INTERNAL;
} else { } else {
int_addr = address; int_addr = address;
find_form = in_form;
} }
if (find_form == MAIL_ADDR_FORM_INTERNAL) if (query_form == MAIL_ADDR_FORM_EXTERNAL_FIRST
|| query_form == MAIL_ADDR_FORM_EXTERNAL)
ext_addr_buf = vstring_alloc(100); ext_addr_buf = vstring_alloc(100);
/* /*
* Initialize. * Initialize.
*/ */
int_full_key = mystrdup(int_addr); int_full_key = mystrdup(int_addr);
if (*var_rcpt_delim == 0 || (strategy & MAIL_ADDR_FIND_NOEXT) == 0) { if (*var_rcpt_delim == 0 || (strategy & MAF_STRATEGY_NOEXT) == 0) {
int_bare_key = saved_ext = 0; int_bare_key = saved_ext = 0;
} else { } else {
/* XXX This could be done after user+foo@domain fails. */
int_bare_key = int_bare_key =
strip_addr_internal(int_full_key, &saved_ext, var_rcpt_delim); strip_addr_internal(int_full_key, &saved_ext, var_rcpt_delim);
} }
@@ -303,9 +348,9 @@ const char *mail_addr_find_opt(MAPS *path, const char *address, char **extp,
/* /*
* Try user+foo@domain and user@domain. * Try user+foo@domain and user@domain.
*/ */
if ((strategy & MAIL_ADDR_FIND_FULL) != 0) { if ((strategy & MAF_STRATEGY_FULL) != 0) {
result = find_addr(path, int_full_key, FULL, WITH_DOMAIN, result = find_addr(path, int_full_key, FULL, WITH_DOMAIN,
find_form, ext_addr_buf); query_form, ext_addr_buf);
} else { } else {
result = 0; result = 0;
path->error = 0; path->error = 0;
@@ -313,31 +358,31 @@ const char *mail_addr_find_opt(MAPS *path, const char *address, char **extp,
if (result == 0 && path->error == 0 && int_bare_key != 0 if (result == 0 && path->error == 0 && int_bare_key != 0
&& (result = find_addr(path, int_bare_key, PARTIAL, WITH_DOMAIN, && (result = find_addr(path, int_bare_key, PARTIAL, WITH_DOMAIN,
find_form, ext_addr_buf)) != 0 query_form, ext_addr_buf)) != 0
&& extp != 0) { && extp != 0) {
*extp = saved_ext; *extp = saved_ext;
saved_ext = 0; saved_ext = 0;
} }
/* /*
* Try user+foo, if the domain matches user+foo@$myorigin, * Try user+foo if the domain matches user+foo@$myorigin,
* user+foo@$mydestination or user+foo@[${proxy,inet}_interfaces]. Then * user+foo@$mydestination or user+foo@[${proxy,inet}_interfaces]. Then
* try with +foo stripped off. * try with +foo stripped off.
*/ */
if (result == 0 && path->error == 0 if (result == 0 && path->error == 0
&& (ratsign = strrchr(int_full_key, '@')) != 0 && (ratsign = strrchr(int_full_key, '@')) != 0
&& (strategy & (MAIL_ADDR_FIND_LOCALPART_IF_LOCAL && (strategy & (MAF_STRATEGY_LOCALPART_IF_LOCAL
| MAIL_ADDR_FIND_LOCALPART_AT_IF_LOCAL)) != 0) { | MAF_STRATEGY_LOCALPART_AT_IF_LOCAL)) != 0) {
if (strcasecmp_utf8(ratsign + 1, var_myorigin) == 0 if (strcasecmp_utf8(ratsign + 1, var_myorigin) == 0
|| (rc = resolve_local(ratsign + 1)) > 0) { || (rc = resolve_local(ratsign + 1)) > 0) {
if ((strategy & MAIL_ADDR_FIND_LOCALPART_IF_LOCAL) != 0) if ((strategy & MAF_STRATEGY_LOCALPART_IF_LOCAL) != 0)
result = find_local(path, ratsign, 0, int_full_key, result = find_local(path, ratsign, 0, int_full_key,
int_bare_key, find_form, extp, &saved_ext, int_bare_key, query_form, extp, &saved_ext,
ext_addr_buf); ext_addr_buf);
if (result == 0 && path->error == 0 if (result == 0 && path->error == 0
&& (strategy & MAIL_ADDR_FIND_LOCALPART_AT_IF_LOCAL) != 0) && (strategy & MAF_STRATEGY_LOCALPART_AT_IF_LOCAL) != 0)
result = find_local(path, ratsign, 1, int_full_key, result = find_local(path, ratsign, 1, int_full_key,
int_bare_key, find_form, extp, &saved_ext, int_bare_key, query_form, extp, &saved_ext,
ext_addr_buf); ext_addr_buf);
} else if (rc < 0) } else if (rc < 0)
path->error = rc; path->error = rc;
@@ -347,40 +392,40 @@ const char *mail_addr_find_opt(MAPS *path, const char *address, char **extp,
* Try @domain. * Try @domain.
*/ */
if (result == 0 && path->error == 0 && ratsign != 0 if (result == 0 && path->error == 0 && ratsign != 0
&& (strategy & MAIL_ADDR_FIND_ATDOMAIN) != 0) && (strategy & MAF_STRATEGY_AT_DOMAIN) != 0)
result = maps_find(path, ratsign, PARTIAL); result = maps_find(path, ratsign, PARTIAL);
/* /*
* Try domain (optionally, subdomains). * Try domain (optionally, subdomains).
*/ */
if (result == 0 && path->error == 0 && ratsign != 0 if (result == 0 && path->error == 0 && ratsign != 0
&& (strategy & MAIL_ADDR_FIND_DOMAIN) != 0) { && (strategy & MAF_STRATEGY_DOMAIN) != 0) {
const char *name; const char *name;
const char *next; const char *next;
for (name = ratsign + 1; *name != 0; name = next) { for (name = ratsign + 1; *name != 0; name = next) {
if ((result = maps_find(path, name, PARTIAL)) != 0 if ((result = maps_find(path, name, PARTIAL)) != 0
|| path->error != 0 || path->error != 0
|| (strategy & (MAIL_ADDR_FIND_PMS | MAIL_ADDR_FIND_PMDS)) == 0 || (strategy & (MAF_STRATEGY_PMS | MAF_STRATEGY_PMDS)) == 0
|| (next = strchr(name + 1, '.')) == 0) || (next = strchr(name + 1, '.')) == 0)
break; break;
if ((strategy & MAIL_ADDR_FIND_PMDS) == 0) if ((strategy & MAF_STRATEGY_PMDS) == 0)
next++; next++;
} }
} }
/* /*
* Try localpart@ even if not local. * Try localpart@ even if the domain is not local.
*/ */
if ((strategy & MAIL_ADDR_FIND_LOCALPART_AT) != 0 \ if ((strategy & MAF_STRATEGY_LOCALPART_AT) != 0 \
&&result == 0 && path->error == 0) &&result == 0 && path->error == 0)
result = find_local(path, ratsign, 1, int_full_key, result = find_local(path, ratsign, 1, int_full_key,
int_bare_key, find_form, extp, &saved_ext, int_bare_key, query_form, extp, &saved_ext,
ext_addr_buf); ext_addr_buf);
/* /*
* Optionally convert the result to internal form. The lookup result is * Optionally convert the result to internal form. The lookup result is
* supposed to be in external form. * supposed to be one external-form email address.
*/ */
if (result != 0 && out_form == MAIL_ADDR_FORM_INTERNAL) { if (result != 0 && out_form == MAIL_ADDR_FORM_INTERNAL) {
if (int_result == 0) if (int_result == 0)
@@ -409,40 +454,6 @@ const char *mail_addr_find_opt(MAPS *path, const char *address, char **extp,
return (result); return (result);
} }
/* mail_addr_find_strategy - map a canonical address */
const char *mail_addr_find_strategy(MAPS *path, const char *address,
char **extp, int strategy)
{
static VSTRING *ext_addr_buf;
const char *result;
/*
* The future: look up with MAIL_ADDR_FORM_INTERNAL, which converts keys
* to external form.
*/
if ((result = mail_addr_find_opt(path, address, extp,
MAIL_ADDR_FORM_INTERNAL,
MAIL_ADDR_FORM_NOCONV,
strategy)) != 0
|| path->error != 0)
return (result);
/*
* The past: look up with MAIL_ADDR_FORM_NOCONV, which leaves keys in
* internal form.
*/
if (ext_addr_buf == 0)
ext_addr_buf = vstring_alloc(100);
quote_822_local(ext_addr_buf, address);
if (strcmp(STR(ext_addr_buf), address) != 0)
result = mail_addr_find_opt(path, address, extp,
MAIL_ADDR_FORM_NOCONV,
MAIL_ADDR_FORM_NOCONV,
strategy);
return (result);
}
#ifdef TEST #ifdef TEST
/* /*
@@ -455,23 +466,25 @@ const char *mail_addr_find_strategy(MAPS *path, const char *address,
static NORETURN usage(const char *progname) static NORETURN usage(const char *progname)
{ {
msg_fatal("usage: %s [-v] database", progname); msg_fatal("usage: %s [-v]", progname);
} }
int main(int argc, char **argv) int main(int argc, char **argv)
{ {
VSTRING *buffer = vstring_alloc(100); VSTRING *buffer = vstring_alloc(100);
char *bp; char *bp;
MAPS *path; MAPS *path = 0;
const char *result; const char *result;
char *extent; char *extent;
char *in_field; char *in_field;
char *query_field;
char *out_field; char *out_field;
char *strategy_field; char *strategy_field;
char *key_field; char *key_field;
char *expect_res; char *expect_res;
char *expect_ext; char *expect_ext;
int in_form; int in_form;
int query_form;
int out_form; int out_form;
int strategy_flags; int strategy_flags;
int ch; int ch;
@@ -489,7 +502,7 @@ int main(int argc, char **argv)
usage(argv[0]); usage(argv[0]);
} }
} }
if (argc != optind + 1) if (argc != optind)
usage(argv[0]); usage(argv[0]);
/* /*
@@ -502,26 +515,38 @@ int main(int argc, char **argv)
UPDATE(var_mydomain, "localdomain"); UPDATE(var_mydomain, "localdomain");
UPDATE(var_myorigin, "localdomain"); UPDATE(var_myorigin, "localdomain");
UPDATE(var_mydest, "localhost.localdomain"); UPDATE(var_mydest, "localhost.localdomain");
path = maps_create(argv[0], argv[optind], DICT_FLAG_LOCK
| DICT_FLAG_FOLD_FIX | DICT_FLAG_UTF8_REQUEST);
while (vstring_fgets_nonl(buffer, VSTREAM_IN)) { while (vstring_fgets_nonl(buffer, VSTREAM_IN)) {
bp = STR(buffer); bp = STR(buffer);
if (msg_verbose)
msg_info("> %s", bp);
/*
* Quick and dirty.
*/
if (path == 0) {
path = maps_create(argv[0], bp, DICT_FLAG_LOCK
| DICT_FLAG_FOLD_FIX | DICT_FLAG_UTF8_REQUEST);
vstream_printf("%s\n", bp);
continue;
}
/* /*
* Parse the input and expectations. * Parse the input and expectations.
*/ */
/* internal, external, noconv, or compat. */ /* internal, external. */
if ((in_field = mystrtok(&bp, ":")) == 0) if ((in_field = mystrtok(&bp, ":")) == 0)
msg_fatal("no input form"); msg_fatal("no input form");
if ((in_form = mail_addr_form_from_string(in_field)) < 0 if ((in_form = mail_addr_form_from_string(in_field)) < 0)
&& strcmp(in_field, "compat") != 0)
msg_fatal("bad input form: '%s'", in_field); msg_fatal("bad input form: '%s'", in_field);
if ((query_field = mystrtok(&bp, ":")) == 0)
msg_fatal("no query form");
/* internal, external, external-first. */
if ((query_form = mail_addr_form_from_string(query_field)) < 0)
msg_fatal("bad query form: '%s'", query_field);
if ((out_field = mystrtok(&bp, ":")) == 0) if ((out_field = mystrtok(&bp, ":")) == 0)
msg_fatal("no output form"); msg_fatal("no output form");
/* internal, external, noconv, or compat, depending on in_form. */ /* internal, external. */
if (((out_form = mail_addr_form_from_string(out_field)) < 0 if ((out_form = mail_addr_form_from_string(out_field)) < 0)
&& strcmp(out_field, "compat") != 0)
|| ((in_form >= 0) != (out_form >= 0)))
msg_fatal("bad output form: '%s'", out_field); msg_fatal("bad output form: '%s'", out_field);
if ((strategy_field = mystrtok(&bp, ":")) == 0) if ((strategy_field = mystrtok(&bp, ":")) == 0)
msg_fatal("no strategy field"); msg_fatal("no strategy field");
@@ -538,18 +563,11 @@ int main(int argc, char **argv)
* Lookups. * Lookups.
*/ */
extent = 0; extent = 0;
if (in_form >= 0 && out_form >= 0) {
/* It's the future. */
result = mail_addr_find_opt(path, key_field, &extent, result = mail_addr_find_opt(path, key_field, &extent,
in_form, out_form, in_form, query_form, out_form,
strategy_flags); strategy_flags);
} else { vstream_printf("%s:%s -%s-> %s:%s (%s)\n",
/* Backwards compatibility. */ in_field, key_field, query_field, out_field, result ? result :
result = mail_addr_find_strategy(path, key_field, &extent,
strategy_flags);
}
vstream_printf("%s:%s -> %s:%s (%s)\n",
in_field, key_field, out_field, result ? result :
path->error ? "(try again)" : path->error ? "(try again)" :
"(not found)", extent ? extent : "null extension"); "(not found)", extent ? extent : "null extension");
vstream_fflush(VSTREAM_OUT); vstream_fflush(VSTREAM_OUT);

View File

@@ -1,5 +1,5 @@
#ifndef _MAIL_ADDR_FIND_H_INCLUDED_ #ifndef _MAF_STRATEGY_H_INCLUDED_
#define _MAIL_ADDR_FIND_H_INCLUDED_ #define _MAF_STRATEGY_H_INCLUDED_
/*++ /*++
/* NAME /* NAME
@@ -21,37 +21,47 @@
* External interface. * External interface.
*/ */
extern const char *mail_addr_find_opt(MAPS *, const char *, char **, extern const char *mail_addr_find_opt(MAPS *, const char *, char **,
int, int, int); int, int, int, int);
#define MAIL_ADDR_FIND_FULL (1<<0) /* localpart+ext@domain */ #define MAF_STRATEGY_FULL (1<<0) /* localpart+ext@domain */
#define MAIL_ADDR_FIND_NOEXT (1<<1) /* localpart@domain */ #define MAF_STRATEGY_NOEXT (1<<1) /* localpart@domain */
#define MAIL_ADDR_FIND_LOCALPART_IF_LOCAL \ #define MAF_STRATEGY_LOCALPART_IF_LOCAL \
(1<<2) /* localpart (maybe localpart+ext) */ (1<<2) /* localpart (maybe localpart+ext) */
#define MAIL_ADDR_FIND_LOCALPART_AT_IF_LOCAL \ #define MAF_STRATEGY_LOCALPART_AT_IF_LOCAL \
(1<<3) /* ditto, with @ at end */ (1<<3) /* ditto, with @ at end */
#define MAIL_ADDR_FIND_ATDOMAIN (1<<4) /* @domain */ #define MAF_STRATEGY_AT_DOMAIN (1<<4) /* @domain */
#define MAIL_ADDR_FIND_DOMAIN (1<<5) /* domain */ #define MAF_STRATEGY_DOMAIN (1<<5) /* domain */
#define MAIL_ADDR_FIND_PMS (1<<6) /* parent matches subdomain */ #define MAF_STRATEGY_PMS (1<<6) /* parent matches subdomain */
#define MAIL_ADDR_FIND_PMDS (1<<7) /* parent matches dot-subdomain */ #define MAF_STRATEGY_PMDS (1<<7) /* parent matches dot-subdomain */
#define MAIL_ADDR_FIND_LOCALPART_AT \ #define MAF_STRATEGY_LOCALPART_AT \
(1<<8) /* localpart@ (maybe localpart+ext@) */ (1<<8) /* localpart@ (maybe localpart+ext@) */
#define MAIL_ADDR_FIND_DEFAULT (MAIL_ADDR_FIND_FULL | MAIL_ADDR_FIND_NOEXT \ #define MAF_STRATEGY_DEFAULT (MAF_STRATEGY_FULL | MAF_STRATEGY_NOEXT \
| MAIL_ADDR_FIND_LOCALPART_IF_LOCAL \ | MAF_STRATEGY_LOCALPART_IF_LOCAL \
| MAIL_ADDR_FIND_ATDOMAIN) | MAF_STRATEGY_AT_DOMAIN)
/* The least-overhead form. */ /* The least-overhead form. */
#define mail_addr_find_int_to_ext(maps, address, extension) \ #define mail_addr_find_int_to_ext(maps, address, extension) \
mail_addr_find_opt((maps), (address), (extension), \ mail_addr_find_opt((maps), (address), (extension), \
MAIL_ADDR_FORM_INTERNAL, MAIL_ADDR_FORM_EXTERNAL, \ MAIL_ADDR_FORM_INTERNAL, MAIL_ADDR_FORM_EXTERNAL, \
MAIL_ADDR_FIND_DEFAULT) MAIL_ADDR_FORM_EXTERNAL, MAF_STRATEGY_DEFAULT)
/* The legacy form. */ /* The legacy forms. */
extern const char *mail_addr_find_strategy(MAPS *, const char *, char **, int); #define MAF_FORM_LEGACY \
MAIL_ADDR_FORM_INTERNAL, MAIL_ADDR_FORM_EXTERNAL_FIRST, \
MAIL_ADDR_FORM_EXTERNAL
#define mail_addr_find_strategy(maps, address, extension, strategy) \
mail_addr_find_opt((maps), (address), (extension), \
MAF_FORM_LEGACY, (strategy))
#define mail_addr_find(maps, address, extension) \ #define mail_addr_find(maps, address, extension) \
mail_addr_find_strategy((maps), (address), (extension), \ mail_addr_find_strategy((maps), (address), (extension), \
MAIL_ADDR_FIND_DEFAULT) MAF_STRATEGY_DEFAULT)
#define mail_addr_find_to_internal(maps, address, extension) \
mail_addr_find_opt((maps), (address), (extension), \
MAF_FORM_LEGACY, MAF_STRATEGY_DEFAULT)
/* LICENSE /* LICENSE
/* .ad /* .ad

View File

@@ -4,78 +4,94 @@
# The last fields are optional. # The last fields are optional.
echo ==== no search string extension echo ==== no search string extension
$VALGRIND ./mail_addr_find 'inline:{plain1@1.example=plain2@2.example,{"aa bb"@cc.example="dd ee"@dd.example}}' <<'EOF' $VALGRIND ./mail_addr_find <<'EOF'
internal:external:default:plain1@1.example:plain2@2.example inline:{plain1@1.example=plain2@2.example,{"aa bb"@cc.example="dd ee"@dd.example}}
internal:external:default:aa bb@cc.example:"dd ee"@dd.example internal:external:external:default:plain1@1.example:plain2@2.example
external:external:default:"aa bb"@cc.example:"dd ee"@dd.example internal:external:external:default:aa bb@cc.example:"dd ee"@dd.example
external:internal:default:"aa bb"@cc.example:dd ee@dd.example external:external:external:default:"aa bb"@cc.example:"dd ee"@dd.example
noconv:noconv:default:plain1@1.example:plain2@2.example external:external:internal:default:"aa bb"@cc.example:dd ee@dd.example
noconv:noconv:default:aa bb@cc.example internal:internal:external:default:plain1@1.example:plain2@2.example
noconv:noconv:default:"aa bb"@cc.example:"dd ee"@dd.example internal:internal:external:default:aa bb@cc.example
internal:internal:external:default:"aa bb"@cc.example:"dd ee"@dd.example
EOF EOF
echo ==== with search string extension echo ==== with search string extension
$VALGRIND ./mail_addr_find 'inline:{plain1@1.example=plain2@2.example,{"aa bb"@cc.example="dd ee"@dd.example}}' <<'EOF' $VALGRIND ./mail_addr_find <<'EOF'
internal:external:default:plain1+ext@1.example:plain2@2.example:+ext inline:{plain1@1.example=plain2@2.example,{"aa bb"@cc.example="dd ee"@dd.example}}
internal:external:default:aa bb+ax bx@cc.example:"dd ee"@dd.example:+ax bx internal:external:external:default:plain1+ext@1.example:plain2@2.example:+ext
external:external:default:"aa bb+ax bx"@cc.example:"dd ee"@dd.example:+ax bx internal:external:external:default:aa bb+ax bx@cc.example:"dd ee"@dd.example:+ax bx
external:internal:default:"aa bb+ax bx"@cc.example:dd ee@dd.example:+ax bx external:external:external:default:"aa bb+ax bx"@cc.example:"dd ee"@dd.example:+ax bx
noconv:noconv:default:plain1+ext@1.example:plain2@2.example:+ext external:external:internal:default:"aa bb+ax bx"@cc.example:dd ee@dd.example:+ax bx
noconv:noconv:default:"aa bb+ax bx"@cc.example internal:internal:external:default:plain1+ext@1.example:plain2@2.example:+ext
noconv:noconv:default:"aa bb"+ax bx@cc.example:"dd ee"@dd.example:+ax bx internal:internal:external:default:"aa bb+ax bx"@cc.example
internal:internal:external:default:"aa bb"+ax bx@cc.example:"dd ee"@dd.example:+ax bx
EOF EOF
echo ==== at in localpart echo ==== at in localpart
$VALGRIND ./mail_addr_find 'inline:{"a@b"=foo@example,"a.b."=bar@example}' <<'EOF' $VALGRIND ./mail_addr_find <<'EOF'
external:external:default:"a@b"@localhost.localdomain:foo@example inline:{"a@b"=foo@example,"a.b."=bar@example}
external:external:default:"a@b+ext"@localhost.localdomain:foo@example:+ext external:external:external:default:"a@b"@localhost.localdomain:foo@example
external:external:default:"a.b."@localhost.localdomain:bar@example external:external:external:default:"a@b+ext"@localhost.localdomain:foo@example:+ext
external:external:external:default:"a.b."@localhost.localdomain:bar@example
EOF EOF
echo ==== legacy support echo ==== legacy support
$VALGRIND ./mail_addr_find 'inline:{"a@b"=extern-1@example,a@b=intern-1@example,a.b.=intern-2@example}' <<'EOF' $VALGRIND ./mail_addr_find <<'EOF'
compat:compat:default:a@b@localhost.localdomain:extern-1@example inline:{"a@b"=extern-1@example,a@b=intern-1@example,a.b.=intern-2@example}
compat:compat:default:a.b.@localhost.localdomain:intern-2@example internal:external-first:external:default:a@b@localhost.localdomain:extern-1@example
internal:external-first:external:default:a.b.@localhost.localdomain:intern-2@example
EOF EOF
echo ==== atdomain test echo ==== at_domain test
$VALGRIND ./mail_addr_find 'inline:{plain1@1.example=plain2@2.example,@3.example=plain4@4.example,plain5@3.example=plain6@6.example}' <<'EOF' $VALGRIND ./mail_addr_find <<'EOF'
external:external:default:plain1+ext@1.example:plain2@2.example:+ext inline:{plain1@1.example=plain2@2.example,@3.example=plain4@4.example,plain5@3.example=plain6@6.example}
external:external:default:plain2@2.example: external:external:external:default:plain1+ext@1.example:plain2@2.example:+ext
external:external:default:plain3@3.example:plain4@4.example external:external:external:default:plain2@2.example:
external:external:default:plain5@3.example:plain6@6.example external:external:external:default:plain3@3.example:plain4@4.example
external:external:external:default:plain5@3.example:plain6@6.example
EOF EOF
echo ==== domain test echo ==== domain test
$VALGRIND ./mail_addr_find 'inline:{plain1@1.example=plain2@2.example,3.example=plain4@4.example,plain5@3.example=plain6@6.example}' <<'EOF' $VALGRIND ./mail_addr_find <<'EOF'
external:external:full|noext|domain:plain1+ext@1.example:plain2@2.example:+ext inline:{plain1@1.example=plain2@2.example,3.example=plain4@4.example,plain5@3.example=plain6@6.example}
external:external:full|noext|domain:plain2@2.example: external:external:external:full|noext|domain:plain1+ext@1.example:plain2@2.example:+ext
external:external:full|noext|domain:plain3@3.example:plain4@4.example external:external:external:full|noext|domain:plain2@2.example:
external:external:full|noext|domain:plain5@3.example:plain6@6.example external:external:external:full|noext|domain:plain3@3.example:plain4@4.example
external:external:external:full|noext|domain:plain5@3.example:plain6@6.example
EOF EOF
echo ==== atdomain for local domain echo ==== at_domain for local domain
$VALGRIND ./mail_addr_find 'inline:{ab=foo@example,@localhost.localdomain=@bar.example}' <<'EOF' $VALGRIND ./mail_addr_find <<'EOF'
external:external:default:ab@localhost.localdomain:foo@example: inline:{ab=foo@example,@localhost.localdomain=@bar.example}
external:external:default:cd@localhost.localdomain:@bar.example external:external:external:default:ab@localhost.localdomain:foo@example:
external:external:external:default:cd@localhost.localdomain:@bar.example
EOF EOF
echo ==== localat and domain test echo ==== localpart_at_if_local and domain test
$VALGRIND ./mail_addr_find 'inline:{ab@=foo@example,localhost.localdomain=@bar.example}' <<'EOF' $VALGRIND ./mail_addr_find <<'EOF'
internal:external:localpart_at_if_local|domain:ab@localhost.localdomain:foo@example: inline:{ab@=foo@example,localhost.localdomain=@bar.example}
internal:external:localpart_at_if_local|noext|domain:ab+ext@localhost.localdomain:foo@example:+ext internal:external:external:localpart_at_if_local|domain:ab@localhost.localdomain:foo@example:
internal:external:localpart_at_if_local|domain:cd@localhost.localdomain:@bar.example internal:external:external:localpart_at_if_local|noext|domain:ab+ext@localhost.localdomain:foo@example:+ext
internal:external:external:localpart_at_if_local|domain:cd@localhost.localdomain:@bar.example
EOF
echo ==== localpart_at has less precedence than domain test
$VALGRIND ./mail_addr_find <<'EOF'
inline:{ab@=foo@example,localhost.localdomain=@bar.example}
external:external:external:localpart_at|domain:ab@localhost.localdomain:@bar.example:
external:external:external:localpart_at|domain:ab@foo:foo@example
EOF EOF
echo ==== domain and subdomain test echo ==== domain and subdomain test
$VALGRIND ./mail_addr_find 'inline:{example=example-result,.example=dot-example-result}' <<'EOF' $VALGRIND ./mail_addr_find <<'EOF'
external:external:domain:plain1+ext@1.example inline:{example=example-result,.example=dot-example-result}
external:external:domain:foo@sub.example external:external:external:domain:plain1+ext@1.example
external:external:domain:foo@example:example-result external:external:external:domain:foo@sub.example
external:external:domain|pms:foo@example:example-result external:external:external:domain:foo@example:example-result
external:external:domain|pms:foo@sub.example:example-result external:external:external:domain|pms:foo@example:example-result
external:external:domain|pms:foo@sub.sub.example:example-result external:external:external:domain|pms:foo@sub.example:example-result
external:external:domain|pmds:foo@example:example-result external:external:external:domain|pms:foo@sub.sub.example:example-result
external:external:domain|pmds:foo@sub.example:dot-example-result external:external:external:domain|pmds:foo@example:example-result
external:external:domain|pmds:foo@sub.sub.example:dot-example-result external:external:external:domain|pmds:foo@sub.example:dot-example-result
external:external:external:domain|pmds:foo@sub.sub.example:dot-example-result
EOF EOF

View File

@@ -1,50 +1,63 @@
==== no search string extension ==== no search string extension
internal:plain1@1.example -> external:plain2@2.example (null extension) inline:{plain1@1.example=plain2@2.example,{"aa bb"@cc.example="dd ee"@dd.example}}
internal:aa bb@cc.example -> external:"dd ee"@dd.example (null extension) internal:plain1@1.example -external-> external:plain2@2.example (null extension)
external:"aa bb"@cc.example -> external:"dd ee"@dd.example (null extension) internal:aa bb@cc.example -external-> external:"dd ee"@dd.example (null extension)
external:"aa bb"@cc.example -> internal:dd ee@dd.example (null extension) external:"aa bb"@cc.example -external-> external:"dd ee"@dd.example (null extension)
noconv:plain1@1.example -> noconv:plain2@2.example (null extension) external:"aa bb"@cc.example -external-> internal:dd ee@dd.example (null extension)
noconv:aa bb@cc.example -> noconv:(not found) (null extension) internal:plain1@1.example -internal-> external:plain2@2.example (null extension)
noconv:"aa bb"@cc.example -> noconv:"dd ee"@dd.example (null extension) internal:aa bb@cc.example -internal-> external:(not found) (null extension)
internal:"aa bb"@cc.example -internal-> external:"dd ee"@dd.example (null extension)
==== with search string extension ==== with search string extension
internal:plain1+ext@1.example -> external:plain2@2.example (+ext) inline:{plain1@1.example=plain2@2.example,{"aa bb"@cc.example="dd ee"@dd.example}}
internal:aa bb+ax bx@cc.example -> external:"dd ee"@dd.example (+ax bx) internal:plain1+ext@1.example -external-> external:plain2@2.example (+ext)
external:"aa bb+ax bx"@cc.example -> external:"dd ee"@dd.example (+ax bx) internal:aa bb+ax bx@cc.example -external-> external:"dd ee"@dd.example (+ax bx)
external:"aa bb+ax bx"@cc.example -> internal:dd ee@dd.example (+ax bx) external:"aa bb+ax bx"@cc.example -external-> external:"dd ee"@dd.example (+ax bx)
noconv:plain1+ext@1.example -> noconv:plain2@2.example (+ext) external:"aa bb+ax bx"@cc.example -external-> internal:dd ee@dd.example (+ax bx)
noconv:"aa bb+ax bx"@cc.example -> noconv:(not found) (null extension) internal:plain1+ext@1.example -internal-> external:plain2@2.example (+ext)
noconv:"aa bb"+ax bx@cc.example -> noconv:"dd ee"@dd.example (+ax bx) internal:"aa bb+ax bx"@cc.example -internal-> external:(not found) (null extension)
internal:"aa bb"+ax bx@cc.example -internal-> external:"dd ee"@dd.example (+ax bx)
==== at in localpart ==== at in localpart
external:"a@b"@localhost.localdomain -> external:foo@example (null extension) inline:{"a@b"=foo@example,"a.b."=bar@example}
external:"a@b+ext"@localhost.localdomain -> external:foo@example (+ext) external:"a@b"@localhost.localdomain -external-> external:foo@example (null extension)
external:"a.b."@localhost.localdomain -> external:bar@example (null extension) external:"a@b+ext"@localhost.localdomain -external-> external:foo@example (+ext)
external:"a.b."@localhost.localdomain -external-> external:bar@example (null extension)
==== legacy support ==== legacy support
compat:a@b@localhost.localdomain -> compat:extern-1@example (null extension) inline:{"a@b"=extern-1@example,a@b=intern-1@example,a.b.=intern-2@example}
compat:a.b.@localhost.localdomain -> compat:intern-2@example (null extension) internal:a@b@localhost.localdomain -external-first-> external:extern-1@example (null extension)
==== atdomain test internal:a.b.@localhost.localdomain -external-first-> external:intern-2@example (null extension)
external:plain1+ext@1.example -> external:plain2@2.example (+ext) ==== at_domain test
external:plain2@2.example -> external:(not found) (null extension) inline:{plain1@1.example=plain2@2.example,@3.example=plain4@4.example,plain5@3.example=plain6@6.example}
external:plain3@3.example -> external:plain4@4.example (null extension) external:plain1+ext@1.example -external-> external:plain2@2.example (+ext)
external:plain5@3.example -> external:plain6@6.example (null extension) external:plain2@2.example -external-> external:(not found) (null extension)
external:plain3@3.example -external-> external:plain4@4.example (null extension)
external:plain5@3.example -external-> external:plain6@6.example (null extension)
==== domain test ==== domain test
external:plain1+ext@1.example -> external:plain2@2.example (+ext) inline:{plain1@1.example=plain2@2.example,3.example=plain4@4.example,plain5@3.example=plain6@6.example}
external:plain2@2.example -> external:(not found) (null extension) external:plain1+ext@1.example -external-> external:plain2@2.example (+ext)
external:plain3@3.example -> external:plain4@4.example (null extension) external:plain2@2.example -external-> external:(not found) (null extension)
external:plain5@3.example -> external:plain6@6.example (null extension) external:plain3@3.example -external-> external:plain4@4.example (null extension)
==== atdomain for local domain external:plain5@3.example -external-> external:plain6@6.example (null extension)
external:ab@localhost.localdomain -> external:foo@example (null extension) ==== at_domain for local domain
external:cd@localhost.localdomain -> external:@bar.example (null extension) inline:{ab=foo@example,@localhost.localdomain=@bar.example}
==== localat and domain test external:ab@localhost.localdomain -external-> external:foo@example (null extension)
internal:ab@localhost.localdomain -> external:foo@example (null extension) external:cd@localhost.localdomain -external-> external:@bar.example (null extension)
internal:ab+ext@localhost.localdomain -> external:foo@example (+ext) ==== localpart_at_if_local and domain test
internal:cd@localhost.localdomain -> external:@bar.example (null extension) inline:{ab@=foo@example,localhost.localdomain=@bar.example}
internal:ab@localhost.localdomain -external-> external:foo@example (null extension)
internal:ab+ext@localhost.localdomain -external-> external:foo@example (+ext)
internal:cd@localhost.localdomain -external-> external:@bar.example (null extension)
==== localpart_at has less precedence than domain test
inline:{ab@=foo@example,localhost.localdomain=@bar.example}
external:ab@localhost.localdomain -external-> external:@bar.example (null extension)
external:ab@foo -external-> external:foo@example (null extension)
==== domain and subdomain test ==== domain and subdomain test
external:plain1+ext@1.example -> external:(not found) (null extension) inline:{example=example-result,.example=dot-example-result}
external:foo@sub.example -> external:(not found) (null extension) external:plain1+ext@1.example -external-> external:(not found) (null extension)
external:foo@example -> external:example-result (null extension) external:foo@sub.example -external-> external:(not found) (null extension)
external:foo@example -> external:example-result (null extension) external:foo@example -external-> external:example-result (null extension)
external:foo@sub.example -> external:example-result (null extension) external:foo@example -external-> external:example-result (null extension)
external:foo@sub.sub.example -> external:example-result (null extension) external:foo@sub.example -external-> external:example-result (null extension)
external:foo@example -> external:example-result (null extension) external:foo@sub.sub.example -external-> external:example-result (null extension)
external:foo@sub.example -> external:dot-example-result (null extension) external:foo@example -external-> external:example-result (null extension)
external:foo@sub.sub.example -> external:dot-example-result (null extension) external:foo@sub.example -external-> external:dot-example-result (null extension)
external:foo@sub.sub.example -external-> external:dot-example-result (null extension)

View File

@@ -11,9 +11,9 @@
/* const char *mail_addr_form_to_string(int addr_form) /* const char *mail_addr_form_to_string(int addr_form)
/* DESCRIPTION /* DESCRIPTION
/* mail_addr_form_from_string() converts a symbolic mail address /* mail_addr_form_from_string() converts a symbolic mail address
/* form name ("internal", "external", "noconv") into the /* form name ("internal", "external", "internal-first") into the
/* corresponding internal code. The result is -1 if an /* corresponding internal code. The result is -1 if an unrecognized
/* unrecognized name was specified. /* name was specified.
/* /*
/* mail_addr_form_to_string() converts from internal code /* mail_addr_form_to_string() converts from internal code
/* to the corresponding symbolic name. The result is null if /* to the corresponding symbolic name. The result is null if
@@ -47,7 +47,7 @@
static const NAME_CODE addr_form_table[] = { static const NAME_CODE addr_form_table[] = {
"external", MAIL_ADDR_FORM_EXTERNAL, "external", MAIL_ADDR_FORM_EXTERNAL,
"internal", MAIL_ADDR_FORM_INTERNAL, "internal", MAIL_ADDR_FORM_INTERNAL,
"noconv", MAIL_ADDR_FORM_NOCONV, "external-first", MAIL_ADDR_FORM_EXTERNAL_FIRST,
0, -1, 0, -1,
}; };

View File

@@ -12,12 +12,11 @@
/* .nf /* .nf
/* /*
* External interface. The MAIL_ADDR_FORM_NOCONV is for legacy code that * External interface.
* hasn't yet been converted to external-form address lookups.
*/ */
#define MAIL_ADDR_FORM_NOCONV 0 /* do not convert */
#define MAIL_ADDR_FORM_INTERNAL 1 /* unquoted form */ #define MAIL_ADDR_FORM_INTERNAL 1 /* unquoted form */
#define MAIL_ADDR_FORM_EXTERNAL 2 /* quoted form */ #define MAIL_ADDR_FORM_EXTERNAL 2 /* quoted form */
#define MAIL_ADDR_FORM_EXTERNAL_FIRST 3 /* quoted form, then unquoted */
extern int mail_addr_form_from_string(const char *); extern int mail_addr_form_from_string(const char *);
extern const char *mail_addr_form_to_string(int); extern const char *mail_addr_form_to_string(int);

View File

@@ -11,11 +11,14 @@
/* const char *address; /* const char *address;
/* int propagate; /* int propagate;
/* /*
/* ARGV *mail_addr_map_opt(path, address, propagate, in_form, out_form) /* ARGV *mail_addr_map_opt(path, address, propagate, in_form,
/* query_form, out_form)
/* MAPS *path; /* MAPS *path;
/* const char *address; /* const char *address;
/* int propagate; /* int propagate;
/* int how; /* int in_form;
/* int query_form;
/* int out_form;
/* DESCRIPTION /* DESCRIPTION
/* mail_addr_map_*() returns the translation for the named address, /* mail_addr_map_*() returns the translation for the named address,
/* or a null pointer if none is found. /* or a null pointer if none is found.
@@ -35,18 +38,17 @@
/* form \fI@otherdomain\fR, the result is the original user in /* form \fI@otherdomain\fR, the result is the original user in
/* \fIotherdomain\fR. /* \fIotherdomain\fR.
/* /*
/* mail_addr_map_opt() gives additional control over whether the
/* input is in internal (unquoted) or external (quoted) form.
/* to internal form and invokes mail_addr_map_int_to_ext().
/* This may introduce additional unqoute822_local() and
/* quote_833_local() calls.
/*
/* Arguments: /* Arguments:
/* .IP path /* .IP path
/* Dictionary search path (see maps(3)). /* Dictionary search path (see maps(3)).
/* .IP address /* .IP address
/* The address to be looked up in external (quoted) form, or /* The address to be looked up in external (quoted) form, or
/* in the form specified with the in_form argument. /* in the form specified with the in_form argument.
/* .IP query_form
/* Database query address forms, either MAIL_ADDR_FORM_INTERNAL
/* (unquoted form), MAIL_ADDR_FORM_EXTERNAL_FIRST (external, then
/* internal if the forms differ) or MAIL_ADDR_FORM_EXTERNAL
/* (quoted form).
/* .IP in_form /* .IP in_form
/* .IP out_form /* .IP out_form
/* Input and output address forms, either MAIL_ADDR_FORM_INTERNAL /* Input and output address forms, either MAIL_ADDR_FORM_INTERNAL
@@ -98,7 +100,7 @@
/* mail_addr_map - map a canonical address */ /* mail_addr_map - map a canonical address */
ARGV *mail_addr_map_opt(MAPS *path, const char *address, int propagate, ARGV *mail_addr_map_opt(MAPS *path, const char *address, int propagate,
int in_form, int out_form) int in_form, int query_form, int out_form)
{ {
VSTRING *buffer = 0; VSTRING *buffer = 0;
const char *myname = "mail_addr_map"; const char *myname = "mail_addr_map";
@@ -111,16 +113,9 @@ ARGV *mail_addr_map_opt(MAPS *path, const char *address, int propagate,
VSTRING *ext_address = 0; VSTRING *ext_address = 0;
const char *int_addr; const char *int_addr;
/* Crutch until we can retire MAIL_ADDR_FORM_NOCONV. */
int mid_form = (out_form == MAIL_ADDR_FORM_NOCONV ?
MAIL_ADDR_FORM_NOCONV : MAIL_ADDR_FORM_EXTERNAL);
/* /*
* Optionally convert input from external form. We prefer internal-form * Optionally convert input from external form. We prefer internal-form
* input to avoid an unnecessary input conversion in * input to avoid unnecessary input conversion in mail_addr_find_opt().
* mail_addr_find_opt(). But the consequence is that we have to convert
* the internal-form input's localpart to external form when mapping
* @domain -> @domain.
*/ */
if (in_form == MAIL_ADDR_FORM_EXTERNAL) { if (in_form == MAIL_ADDR_FORM_EXTERNAL) {
int_address = vstring_alloc(100); int_address = vstring_alloc(100);
@@ -134,16 +129,16 @@ ARGV *mail_addr_map_opt(MAPS *path, const char *address, int propagate,
/* /*
* Look up the full address; if no match is found, look up the address * Look up the full address; if no match is found, look up the address
* with the extension stripped off, and remember the unmatched extension. * with the extension stripped off, and remember the unmatched extension.
* We explicitly call the mail_addr_find_opt() variant that does not
* convert the lookup result.
*/ */
if ((string = mail_addr_find_opt(path, int_addr, &extension, if ((string = mail_addr_find_opt(path, int_addr, &extension,
in_form, mid_form, in_form, query_form,
MAIL_ADDR_FIND_DEFAULT)) != 0) { MAIL_ADDR_FORM_EXTERNAL,
MAF_STRATEGY_DEFAULT)) != 0) {
/* /*
* Prepend the original user to @otherdomain, but do not propagate * Prepend the original user to @otherdomain, but do not propagate
* the unmatched address extension. * the unmatched address extension. Convert the address to external
* form just like the mail_addr_find_opt() output.
*/ */
if (*string == '@') { if (*string == '@') {
buffer = vstring_alloc(100); buffer = vstring_alloc(100);
@@ -153,9 +148,9 @@ ARGV *mail_addr_map_opt(MAPS *path, const char *address, int propagate,
vstring_strcpy(buffer, int_addr); vstring_strcpy(buffer, int_addr);
if (extension) if (extension)
vstring_truncate(buffer, LEN(buffer) - strlen(extension)); vstring_truncate(buffer, LEN(buffer) - strlen(extension));
ext_address = vstring_alloc(100); vstring_strcat(buffer, string);
ext_address = vstring_alloc(2 * LEN(buffer));
quote_822_local(ext_address, STR(buffer)); quote_822_local(ext_address, STR(buffer));
vstring_strcat(ext_address, string);
string = STR(ext_address); string = STR(ext_address);
} }
@@ -164,7 +159,7 @@ ARGV *mail_addr_map_opt(MAPS *path, const char *address, int propagate,
* each address found. * each address found.
*/ */
argv = mail_addr_crunch_opt(string, propagate ? extension : 0, argv = mail_addr_crunch_opt(string, propagate ? extension : 0,
mid_form, out_form); MAIL_ADDR_FORM_EXTERNAL, out_form);
if (buffer) if (buffer)
vstring_free(buffer); vstring_free(buffer);
if (ext_address) if (ext_address)
@@ -199,3 +194,334 @@ ARGV *mail_addr_map_opt(MAPS *path, const char *address, int propagate,
return (argv); return (argv);
} }
#ifdef TEST
/*
* SYNOPSIS
* mail_addr_map pass_tests | fail_tests
* DESCRIPTION
* mail_addr_map performs the specified set of built-in
* unit tests. With 'pass_tests', all tests must pass, and
* with 'fail_tests' all tests must fail.
* DIAGNOSTICS
* When a unit test fails, the program prints details of the
* failed test.
*
* The program terminates with a non-zero exit status when at
* least one test does not pass with 'pass_tests', or when at
* least one test does not fail with 'fail_tests'.
*/
/* System library. */
#include <sys_defs.h>
#include <ctype.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
/* Utility library. */
#include <argv.h>
#include <msg.h>
#include <mymalloc.h>
#include <vstring.h>
/* Global library. */
#include <canon_addr.h>
#include <mail_addr_map.h>
#include <mail_conf.h> /* XXX eliminate main.cf dependency */
#include <mail_params.h>
/* Application-specific. */
#define STR vstring_str
typedef struct {
const char *testname;
const char *database;
int propagate;
const char *delimiter;
int in_form;
int query_form;
int out_form;
const char *address;
const char *expect_argv[2];
int expect_argc;
} MAIL_ADDR_MAP_TEST;
#define DONT_PROPAGATE_UNMATCHED_EXTENSION 0
#define DO_PROPAGATE_UNMATCHED_EXTENSION 1
#define NO_RECIPIENT_DELIMITER ""
#define PLUS_RECIPIENT_DELIMITER "+"
/*
* All these tests must pass, so that we know that mail_addr_map_opt() works
* as intended. mail_addr_map() has always been used for maps that expect
* external-form queries, so there are no tests here for internal-form
* queries.
*/
static MAIL_ADDR_MAP_TEST pass_tests[] = {
{
"1 external -external-> external, no extension",
"inline:{ aa@example.com=bb@example.com }",
DO_PROPAGATE_UNMATCHED_EXTENSION, PLUS_RECIPIENT_DELIMITER,
MAIL_ADDR_FORM_EXTERNAL, MAIL_ADDR_FORM_EXTERNAL,
MAIL_ADDR_FORM_EXTERNAL,
"aa@example.com",
{"bb@example.com"}, 1,
},
{
"2 external -external-> external, extension, propagation",
"inline:{ aa@example.com=bb@example.com }",
DO_PROPAGATE_UNMATCHED_EXTENSION, PLUS_RECIPIENT_DELIMITER,
MAIL_ADDR_FORM_EXTERNAL, MAIL_ADDR_FORM_EXTERNAL,
MAIL_ADDR_FORM_EXTERNAL,
"aa+ext@example.com",
{"bb+ext@example.com"}, 1,
},
{
"3 external -external-> external, extension, no propagation, no match",
"inline:{ aa@example.com=bb@example.com }",
DONT_PROPAGATE_UNMATCHED_EXTENSION, NO_RECIPIENT_DELIMITER,
MAIL_ADDR_FORM_EXTERNAL, MAIL_ADDR_FORM_EXTERNAL,
MAIL_ADDR_FORM_EXTERNAL,
"aa+ext@example.com",
{0}, 0,
},
{
"4 external -external-> external, extension, full match",
"inline:{{cc+ext@example.com=dd@example.com,ee@example.com}}",
DO_PROPAGATE_UNMATCHED_EXTENSION, PLUS_RECIPIENT_DELIMITER,
MAIL_ADDR_FORM_EXTERNAL, MAIL_ADDR_FORM_EXTERNAL,
MAIL_ADDR_FORM_EXTERNAL,
"cc+ext@example.com",
{"dd@example.com", "ee@example.com"}, 2,
},
{
"5 external -external-> external, no extension, quoted",
"inline:{ {\"a a\"@example.com=\"b b\"@example.com} }",
DO_PROPAGATE_UNMATCHED_EXTENSION, PLUS_RECIPIENT_DELIMITER,
MAIL_ADDR_FORM_EXTERNAL, MAIL_ADDR_FORM_EXTERNAL,
MAIL_ADDR_FORM_EXTERNAL,
"\"a a\"@example.com",
{"\"b b\"@example.com"}, 1,
},
{
"6 external -external-> external, extension, propagation, quoted",
"inline:{ {\"a a\"@example.com=\"b b\"@example.com} }",
DO_PROPAGATE_UNMATCHED_EXTENSION, PLUS_RECIPIENT_DELIMITER,
MAIL_ADDR_FORM_EXTERNAL, MAIL_ADDR_FORM_EXTERNAL,
MAIL_ADDR_FORM_EXTERNAL,
"\"a a+ext\"@example.com",
{"\"b b+ext\"@example.com"}, 1,
},
{
"7 internal -external-> internal, no extension, propagation, embedded space",
"inline:{ {\"a a\"@example.com=\"b b\"@example.com} }",
DO_PROPAGATE_UNMATCHED_EXTENSION, PLUS_RECIPIENT_DELIMITER,
MAIL_ADDR_FORM_INTERNAL, MAIL_ADDR_FORM_EXTERNAL,
MAIL_ADDR_FORM_INTERNAL,
"a a@example.com",
{"b b@example.com"}, 1,
},
{
"8 internal -external-> internal, extension, propagation, embedded space",
"inline:{ {\"a a\"@example.com=\"b b\"@example.com} }",
DO_PROPAGATE_UNMATCHED_EXTENSION, PLUS_RECIPIENT_DELIMITER,
MAIL_ADDR_FORM_INTERNAL, MAIL_ADDR_FORM_EXTERNAL,
MAIL_ADDR_FORM_INTERNAL,
"a a+ext@example.com",
{"b b+ext@example.com"}, 1,
},
{
"9 internal -external-> internal, no extension, propagation, embedded space",
"inline:{ {a_a@example.com=\"b b\"@example.com} }",
DO_PROPAGATE_UNMATCHED_EXTENSION, PLUS_RECIPIENT_DELIMITER,
MAIL_ADDR_FORM_INTERNAL, MAIL_ADDR_FORM_EXTERNAL,
MAIL_ADDR_FORM_INTERNAL,
"a_a@example.com",
{"b b@example.com"}, 1,
},
{
"10 internal -external-> internal, extension, propagation, embedded space",
"inline:{ {a_a@example.com=\"b b\"@example.com} }",
DO_PROPAGATE_UNMATCHED_EXTENSION, PLUS_RECIPIENT_DELIMITER,
MAIL_ADDR_FORM_INTERNAL, MAIL_ADDR_FORM_EXTERNAL,
MAIL_ADDR_FORM_INTERNAL,
"a_a+ext@example.com",
{"b b+ext@example.com"}, 1,
},
{
"11 internal -external-> internal, no extension, @domain",
"inline:{ {@example.com=@example.net} }",
DO_PROPAGATE_UNMATCHED_EXTENSION, PLUS_RECIPIENT_DELIMITER,
MAIL_ADDR_FORM_INTERNAL, MAIL_ADDR_FORM_EXTERNAL,
MAIL_ADDR_FORM_EXTERNAL,
"a@a@example.com",
{"\"a@a\"@example.net"}, 1,
},
0,
};
/*
* All these tests must fail, so that we know that the tests work.
*/
static MAIL_ADDR_MAP_TEST fail_tests[] = {
{
"selftest 1 external -external-> external, no extension, quoted",
"inline:{ {\"a a\"@example.com=\"b b\"@example.com} }",
DO_PROPAGATE_UNMATCHED_EXTENSION, PLUS_RECIPIENT_DELIMITER,
MAIL_ADDR_FORM_EXTERNAL, MAIL_ADDR_FORM_EXTERNAL,
MAIL_ADDR_FORM_EXTERNAL,
"\"a a\"@example.com",
{"\"bXb\"@example.com"}, 1,
},
{
"selftest 2 external -external-> external, no extension, quoted",
"inline:{ {\"a a\"@example.com=\"b b\"@example.com} }",
DO_PROPAGATE_UNMATCHED_EXTENSION, PLUS_RECIPIENT_DELIMITER,
MAIL_ADDR_FORM_EXTERNAL, MAIL_ADDR_FORM_EXTERNAL,
MAIL_ADDR_FORM_EXTERNAL,
"\"aXa\"@example.com",
{"\"b b\"@example.com"}, 1,
},
{
"selftest 3 external -external-> external, no extension, quoted",
"inline:{ {\"a a\"@example.com=\"b b\"@example.com} }",
DO_PROPAGATE_UNMATCHED_EXTENSION, PLUS_RECIPIENT_DELIMITER,
MAIL_ADDR_FORM_EXTERNAL, MAIL_ADDR_FORM_EXTERNAL,
MAIL_ADDR_FORM_EXTERNAL,
"\"a a\"@example.com",
{0}, 0,
},
0,
};
/* canon_addr_external - surrogate to avoid trivial-rewrite dependency */
VSTRING *canon_addr_external(VSTRING *result, const char *addr)
{
return (vstring_strcpy(result, addr));
}
static int compare(const char *testname,
const char **expect_argv, int expect_argc,
char **result_argv, int result_argc)
{
int n;
int err = 0;
if (expect_argc != 0 && result_argc != 0) {
for (n = 0; n < expect_argc && n < result_argc; n++) {
if (strcmp(expect_argv[n], result_argv[n]) != 0) {
msg_warn("fail test %s: expect[%d]='%s', result[%d]='%s'",
testname, n, expect_argv[n], n, result_argv[n]);
err = 1;
}
}
}
if (expect_argc != result_argc) {
msg_warn("fail test %s: expects %d results but there were %d",
testname, expect_argc, result_argc);
for (n = expect_argc; n < result_argc; n++)
msg_info("no expect to match result[%d]='%s'", n, result_argv[n]);
for (n = result_argc; n < expect_argc; n++)
msg_info("no result to match expect[%d]='%s'", n, expect_argv[n]);
err = 1;
}
return (err);
}
static char *progname;
static NORETURN usage(void)
{
msg_fatal("usage: %s pass_test | fail_test", progname);
}
int main(int argc, char **argv)
{
MAIL_ADDR_MAP_TEST *test;
MAIL_ADDR_MAP_TEST *tests;
int errs = 0;
#define UPDATE(dst, src) { if (dst) myfree(dst); dst = mystrdup(src); }
/*
* Parse JCL.
*/
progname = argv[0];
if (argc != 2) {
usage();
} else if (strcmp(argv[1], "pass_tests") == 0) {
tests = pass_tests;
} else if (strcmp(argv[1], "fail_tests") == 0) {
tests = fail_tests;
} else {
usage();
}
/*
* Initialize.
*/
mail_conf_read(); /* XXX eliminate */
/*
* A read-eval-print loop, because specifying C strings with quotes and
* backslashes is painful.
*/
for (test = tests; test->testname; test++) {
ARGV *result;
int fail = 0;
if (mail_addr_form_to_string(test->in_form) == 0) {
msg_warn("test %s: bad in_form field: %d",
test->testname, test->in_form);
fail = 1;
continue;
}
if (mail_addr_form_to_string(test->query_form) == 0) {
msg_warn("test %s: bad query_form field: %d",
test->testname, test->query_form);
fail = 1;
continue;
}
if (mail_addr_form_to_string(test->out_form) == 0) {
msg_warn("test %s: bad out_form field: %d",
test->testname, test->out_form);
fail = 1;
continue;
}
MAPS *maps = maps_create("test", test->database, DICT_FLAG_LOCK
| DICT_FLAG_FOLD_FIX | DICT_FLAG_UTF8_REQUEST);
UPDATE(var_rcpt_delim, test->delimiter);
result = mail_addr_map_opt(maps, test->address, test->propagate,
test->in_form, test->query_form, test->out_form);
if (compare(test->testname, test->expect_argv, test->expect_argc,
result ? result->argv : 0, result ? result->argc : 0) != 0) {
msg_info("database = %s", test->database);
msg_info("propagate = %d", test->propagate);
msg_info("delimiter = '%s'", var_rcpt_delim);
msg_info("in_form = %s", mail_addr_form_to_string(test->in_form));
msg_info("query_form = %s", mail_addr_form_to_string(test->query_form));
msg_info("out_form = %s", mail_addr_form_to_string(test->out_form));
msg_info("address = %s", test->address);
fail = 1;
}
maps_free(maps);
if (result)
argv_free(result);
/*
* It is an error if a test does not pass or fail as intended.
*/
errs += (tests == pass_tests ? fail : !fail);
}
return (errs != 0);
}
#endif

View File

@@ -25,12 +25,13 @@
/* /*
* External interface. * External interface.
*/ */
extern ARGV *mail_addr_map_opt(MAPS *, const char *, int, int, int); extern ARGV *mail_addr_map_opt(MAPS *, const char *, int, int, int, int);
/* The least-overhead form. */ /* The least-overhead form. */
#define mail_addr_map_internal(path, address, propagate) \ #define mail_addr_map_internal(path, address, propagate) \
mail_addr_map_opt((path), (address), (propagate), \ mail_addr_map_opt((path), (address), (propagate), \
MAIL_ADDR_FORM_INTERNAL, MAIL_ADDR_FORM_INTERNAL) MAIL_ADDR_FORM_INTERNAL, MAIL_ADDR_FORM_EXTERNAL, \
MAIL_ADDR_FORM_INTERNAL)
/* LICENSE /* LICENSE
/* .ad /* .ad

View File

@@ -1,23 +1,26 @@
unknown: warning: fail test selftest 1 external to external, no extension, quoted: expect[0]='"bXb"@example.com', result[0]='"b b"@example.com' unknown: warning: fail test selftest 1 external -external-> external, no extension, quoted: expect[0]='"bXb"@example.com', result[0]='"b b"@example.com'
unknown: database = inline:{ {"a a"@example.com="b b"@example.com} } unknown: database = inline:{ {"a a"@example.com="b b"@example.com} }
unknown: propagate = 1 unknown: propagate = 1
unknown: delimiter = '+' unknown: delimiter = '+'
unknown: in_form = external unknown: in_form = external
unknown: query_form = external
unknown: out_form = external unknown: out_form = external
unknown: address = "a a"@example.com unknown: address = "a a"@example.com
unknown: warning: fail test selftest 2 external to external, no extension, quoted: expects 1 results but there were 0 unknown: warning: fail test selftest 2 external -external-> external, no extension, quoted: expects 1 results but there were 0
unknown: no result to match expect[0]='"b b"@example.com' unknown: no result to match expect[0]='"b b"@example.com'
unknown: database = inline:{ {"a a"@example.com="b b"@example.com} } unknown: database = inline:{ {"a a"@example.com="b b"@example.com} }
unknown: propagate = 1 unknown: propagate = 1
unknown: delimiter = '+' unknown: delimiter = '+'
unknown: in_form = external unknown: in_form = external
unknown: query_form = external
unknown: out_form = external unknown: out_form = external
unknown: address = "aXa"@example.com unknown: address = "aXa"@example.com
unknown: warning: fail test selftest 3 external to external, no extension, quoted: expects 0 results but there were 1 unknown: warning: fail test selftest 3 external -external-> external, no extension, quoted: expects 0 results but there were 1
unknown: no expect to match result[0]='"b b"@example.com' unknown: no expect to match result[0]='"b b"@example.com'
unknown: database = inline:{ {"a a"@example.com="b b"@example.com} } unknown: database = inline:{ {"a a"@example.com="b b"@example.com} }
unknown: propagate = 1 unknown: propagate = 1
unknown: delimiter = '+' unknown: delimiter = '+'
unknown: in_form = external unknown: in_form = external
unknown: query_form = external
unknown: out_form = external unknown: out_form = external
unknown: address = "a a"@example.com unknown: address = "a a"@example.com

View File

@@ -1,309 +0,0 @@
/*++
/* NAME
/* mail_addr_map_tester 1
/* SUMMARY
/* mail_addr_map test program
/* SYNOPSIS
/* mail_addr_map pass_tests | fail_tests
/* DESCRIPTION
/* mail_addr_map performs the specified set of built-in
/* unit tests. With 'pass_tests', all tests must pass, and
/* with 'fail_tests' all tests must fail.
/* DIAGNOSTICS
/* When a unit test fails, the program prints details of the
/* failed test.
/*
/* The program terminates with a non-zero exit status when at
/* least one test does not pass with 'pass_tests', or when at
/* least one test does not fail with 'fail_tests'.
/* SEE ALSO
/* mail_addr_map(3), generic address mapping
/* 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 <ctype.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
/* Utility library. */
#include <argv.h>
#include <msg.h>
#include <mymalloc.h>
#include <vstring.h>
/* Global library. */
#include <canon_addr.h>
#include <mail_addr_map.h>
#include <mail_conf.h> /* XXX eliminate main.cf dependency */
#include <mail_params.h>
/* Application-specific. */
#define STR vstring_str
typedef struct {
const char *testname;
const char *database;
int propagate;
const char *delimiter;
int in_form;
int out_form;
const char *address;
const char *expect_argv[2];
int expect_argc;
} MAIL_ADDR_MAP_TEST;
#define DONT_PROPAGATE_UNMATCHED_EXTENSION 0
#define DO_PROPAGATE_UNMATCHED_EXTENSION 1
#define NO_RECIPIENT_DELIMITER ""
#define PLUS_RECIPIENT_DELIMITER "+"
/*
* All these tests must pass, so that we know that mail_addr_map_opt() works
* as intended.
*/
static MAIL_ADDR_MAP_TEST pass_tests[] = {
{
"1 external to external, no extension",
"inline:{ aa@example.com=bb@example.com }",
DO_PROPAGATE_UNMATCHED_EXTENSION, PLUS_RECIPIENT_DELIMITER,
MAIL_ADDR_FORM_EXTERNAL, MAIL_ADDR_FORM_EXTERNAL,
"aa@example.com",
{"bb@example.com"}, 1,
},
{
"2 external to external, extension, propagation",
"inline:{ aa@example.com=bb@example.com }",
DO_PROPAGATE_UNMATCHED_EXTENSION, PLUS_RECIPIENT_DELIMITER,
MAIL_ADDR_FORM_EXTERNAL, MAIL_ADDR_FORM_EXTERNAL,
"aa+ext@example.com",
{"bb+ext@example.com"}, 1,
},
{
"3 external to external, extension, no propagation, no match",
"inline:{ aa@example.com=bb@example.com }",
DONT_PROPAGATE_UNMATCHED_EXTENSION, NO_RECIPIENT_DELIMITER,
MAIL_ADDR_FORM_EXTERNAL, MAIL_ADDR_FORM_EXTERNAL,
"aa+ext@example.com",
{0}, 0,
},
{
"4 external to external, extension, full match",
"inline:{{cc+ext@example.com=dd@example.com,ee@example.com}}",
DO_PROPAGATE_UNMATCHED_EXTENSION, PLUS_RECIPIENT_DELIMITER,
MAIL_ADDR_FORM_EXTERNAL, MAIL_ADDR_FORM_EXTERNAL,
"cc+ext@example.com",
{"dd@example.com", "ee@example.com"}, 2,
},
{
"5 external to external, no extension, quoted",
"inline:{ {\"a a\"@example.com=\"b b\"@example.com} }",
DO_PROPAGATE_UNMATCHED_EXTENSION, PLUS_RECIPIENT_DELIMITER,
MAIL_ADDR_FORM_EXTERNAL, MAIL_ADDR_FORM_EXTERNAL,
"\"a a\"@example.com",
{"\"b b\"@example.com"}, 1,
},
{
"6 external to external, extension, propagation, quoted",
"inline:{ {\"a a\"@example.com=\"b b\"@example.com} }",
DO_PROPAGATE_UNMATCHED_EXTENSION, PLUS_RECIPIENT_DELIMITER,
MAIL_ADDR_FORM_EXTERNAL, MAIL_ADDR_FORM_EXTERNAL,
"\"a a+ext\"@example.com",
{"\"b b+ext\"@example.com"}, 1,
},
{
"7 internal to internal, no extension, propagation, embedded space",
"inline:{ {\"a a\"@example.com=\"b b\"@example.com} }",
DO_PROPAGATE_UNMATCHED_EXTENSION, PLUS_RECIPIENT_DELIMITER,
MAIL_ADDR_FORM_INTERNAL, MAIL_ADDR_FORM_INTERNAL,
"a a@example.com",
{"b b@example.com"}, 1,
},
{
"8 internal to internal, extension, propagation, embedded space",
"inline:{ {\"a a\"@example.com=\"b b\"@example.com} }",
DO_PROPAGATE_UNMATCHED_EXTENSION, PLUS_RECIPIENT_DELIMITER,
MAIL_ADDR_FORM_INTERNAL, MAIL_ADDR_FORM_INTERNAL,
"a a+ext@example.com",
{"b b+ext@example.com"}, 1,
},
{
"9 noconv to noconv, no extension, propagation, embedded space",
"inline:{ {a_a@example.com=\"b b\"@example.com} }",
DO_PROPAGATE_UNMATCHED_EXTENSION, PLUS_RECIPIENT_DELIMITER,
MAIL_ADDR_FORM_INTERNAL, MAIL_ADDR_FORM_INTERNAL,
"a_a@example.com",
{"b b@example.com"}, 1,
},
{
"10 noconv to noconv, extension, propagation, embedded space",
"inline:{ {a_a@example.com=\"b b\"@example.com} }",
DO_PROPAGATE_UNMATCHED_EXTENSION, PLUS_RECIPIENT_DELIMITER,
MAIL_ADDR_FORM_INTERNAL, MAIL_ADDR_FORM_INTERNAL,
"a_a+ext@example.com",
{"b b+ext@example.com"}, 1,
},
0,
};
/*
* All these tests must fail, so that we know that the tests work.
*/
static MAIL_ADDR_MAP_TEST fail_tests[] = {
{
"selftest 1 external to external, no extension, quoted",
"inline:{ {\"a a\"@example.com=\"b b\"@example.com} }",
DO_PROPAGATE_UNMATCHED_EXTENSION, PLUS_RECIPIENT_DELIMITER,
MAIL_ADDR_FORM_EXTERNAL, MAIL_ADDR_FORM_EXTERNAL,
"\"a a\"@example.com",
{"\"bXb\"@example.com"}, 1,
},
{
"selftest 2 external to external, no extension, quoted",
"inline:{ {\"a a\"@example.com=\"b b\"@example.com} }",
DO_PROPAGATE_UNMATCHED_EXTENSION, PLUS_RECIPIENT_DELIMITER,
MAIL_ADDR_FORM_EXTERNAL, MAIL_ADDR_FORM_EXTERNAL,
"\"aXa\"@example.com",
{"\"b b\"@example.com"}, 1,
},
{
"selftest 3 external to external, no extension, quoted",
"inline:{ {\"a a\"@example.com=\"b b\"@example.com} }",
DO_PROPAGATE_UNMATCHED_EXTENSION, PLUS_RECIPIENT_DELIMITER,
MAIL_ADDR_FORM_EXTERNAL, MAIL_ADDR_FORM_EXTERNAL,
"\"a a\"@example.com",
{0}, 0,
},
0,
};
/* canon_addr_external - surrogate to avoid trivial-rewrite dependency */
VSTRING *canon_addr_external(VSTRING *result, const char *addr)
{
return (vstring_strcpy(result, addr));
}
static int compare(const char *testname,
const char **expect_argv, int expect_argc,
char **result_argv, int result_argc)
{
int n;
int err = 0;
if (expect_argc != 0 && result_argc != 0) {
for (n = 0; n < expect_argc && n < result_argc; n++) {
if (strcmp(expect_argv[n], result_argv[n]) != 0) {
msg_warn("fail test %s: expect[%d]='%s', result[%d]='%s'",
testname, n, expect_argv[n], n, result_argv[n]);
err = 1;
}
}
}
if (expect_argc != result_argc) {
msg_warn("fail test %s: expects %d results but there were %d",
testname, expect_argc, result_argc);
for (n = expect_argc; n < result_argc; n++)
msg_info("no expect to match result[%d]='%s'", n, result_argv[n]);
for (n = result_argc; n < expect_argc; n++)
msg_info("no result to match expect[%d]='%s'", n, expect_argv[n]);
err = 1;
}
return (err);
}
static char *progname;
static NORETURN usage(void)
{
msg_fatal("usage: %s pass_test | fail_test", progname);
}
int main(int argc, char **argv)
{
MAIL_ADDR_MAP_TEST *test;
MAIL_ADDR_MAP_TEST *tests;
int errs = 0;
#define UPDATE(dst, src) { if (dst) myfree(dst); dst = mystrdup(src); }
/*
* Parse JCL.
*/
progname = argv[0];
if (argc != 2) {
usage();
} else if (strcmp(argv[1], "pass_tests") == 0) {
tests = pass_tests;
} else if (strcmp(argv[1], "fail_tests") == 0) {
tests = fail_tests;
} else {
usage();
}
/*
* Initialize.
*/
mail_conf_read(); /* XXX eliminate */
/*
* A read-eval-print loop, because specifying C strings with quotes and
* backslashes is painful.
*/
for (test = tests; test->testname; test++) {
ARGV *result;
int fail = 0;
if (mail_addr_form_to_string(test->in_form) == 0) {
msg_warn("test %s: bad in_form field: %d",
test->testname, test->in_form);
fail = 1;
continue;
}
if (mail_addr_form_to_string(test->out_form) == 0) {
msg_warn("test %s: bad out_form field: %d",
test->testname, test->out_form);
fail = 1;
continue;
}
MAPS *maps = maps_create("test", test->database, DICT_FLAG_LOCK
| DICT_FLAG_FOLD_FIX | DICT_FLAG_UTF8_REQUEST);
UPDATE(var_rcpt_delim, test->delimiter);
result = mail_addr_map_opt(maps, test->address, test->propagate,
test->in_form, test->out_form);
if (compare(test->testname, test->expect_argv, test->expect_argc,
result ? result->argv : 0, result ? result->argc : 0) != 0) {
msg_info("database = %s", test->database);
msg_info("propagate = %d", test->propagate);
msg_info("delimiter = '%s'", var_rcpt_delim);
msg_info("in_form = %s", mail_addr_form_to_string(test->in_form));
msg_info("out_form = %s", mail_addr_form_to_string(test->out_form));
msg_info("address = %s", test->address);
fail = 1;
}
maps_free(maps);
if (result)
argv_free(result);
/*
* It is an error if a test does not pass or fail as intended.
*/
errs += (tests == pass_tests ? fail : !fail);
}
return (errs != 0);
}

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 "20170117" #define MAIL_RELEASE_DATE "20170122"
#define MAIL_VERSION_NUMBER "3.2" #define MAIL_VERSION_NUMBER "3.2"
#ifdef SNAPSHOT #ifdef SNAPSHOT

View File

@@ -48,7 +48,7 @@
/* When the \fIkey\fR specifies email address information, the /* When the \fIkey\fR specifies email address information, the
/* localpart should be enclosed with double quotes if required /* localpart should be enclosed with double quotes if required
/* by RFC 5322. For example, an address localpart that contains /* by RFC 5322. For example, an address localpart that contains
/* ';' or that ends on '.'. /* ";", or a localpart that starts or ends with ".".
/* /*
/* By default the lookup key is mapped to lowercase to make /* By default the lookup key is mapped to lowercase to make
/* the lookups case insensitive; as of Postfix 2.3 this case /* the lookups case insensitive; as of Postfix 2.3 this case
@@ -64,7 +64,7 @@
/* .IP \fB-b\fR /* .IP \fB-b\fR
/* Enable message body query mode. When reading lookup keys /* Enable message body query mode. When reading lookup keys
/* from standard input with "\fB-q -\fR", process the input /* from standard input with "\fB-q -\fR", process the input
/* as if it is an email message in RFC 2822 format. Each line /* as if it is an email message in RFC 5322 format. Each line
/* of body content becomes one lookup key. /* of body content becomes one lookup key.
/* .sp /* .sp
/* By default, the \fB-b\fR option starts generating lookup /* By default, the \fB-b\fR option starts generating lookup
@@ -101,7 +101,7 @@
/* .IP \fB-h\fR /* .IP \fB-h\fR
/* Enable message header query mode. When reading lookup keys /* Enable message header query mode. When reading lookup keys
/* from standard input with "\fB-q -\fR", process the input /* from standard input with "\fB-q -\fR", process the input
/* as if it is an email message in RFC 2822 format. Each /* as if it is an email message in RFC 5322 format. Each
/* logical header line becomes one lookup key. A multi-line /* logical header line becomes one lookup key. A multi-line
/* header becomes one lookup key with one or more embedded /* header becomes one lookup key with one or more embedded
/* newline characters. /* newline characters.

View File

@@ -81,7 +81,9 @@ int smtp_map11_external(VSTRING *addr, MAPS *maps, int propagate)
const char *result; const char *result;
if ((new_addr = mail_addr_map_opt(maps, STR(addr), propagate, if ((new_addr = mail_addr_map_opt(maps, STR(addr), propagate,
MAIL_ADDR_FORM_EXTERNAL, MAIL_ADDR_FORM_EXTERNAL)) != 0) { MAIL_ADDR_FORM_EXTERNAL,
MAIL_ADDR_FORM_EXTERNAL,
MAIL_ADDR_FORM_EXTERNAL)) != 0) {
if (new_addr->argc > 1) if (new_addr->argc > 1)
msg_warn("multi-valued %s result for %s", maps->title, STR(addr)); msg_warn("multi-valued %s result for %s", maps->title, STR(addr));
result = new_addr->argv[0]; result = new_addr->argv[0];

View File

@@ -114,6 +114,8 @@ smtpd_addr_valid_test: smtpd_check smtpd_addr_valid.in smtpd_addr_valid.ref
# This requires that the DNS server can query porcupine.org. # This requires that the DNS server can query porcupine.org.
ADDRINFO_FIX = sed 's/No address associated with hostname/hostname nor servname provided, or not known/'
smtpd_exp_test: smtpd_check smtpd_exp.in smtpd_exp.ref smtpd_exp_test: smtpd_check smtpd_exp.in smtpd_exp.ref
$(SHLIB_ENV) $(VALGRIND) ../postmap/postmap hash:smtpd_check_access $(SHLIB_ENV) $(VALGRIND) ../postmap/postmap hash:smtpd_check_access
$(SHLIB_ENV) $(VALGRIND) ./smtpd_check <smtpd_exp.in >smtpd_exp.tmp 2>&1 $(SHLIB_ENV) $(VALGRIND) ./smtpd_check <smtpd_exp.in >smtpd_exp.tmp 2>&1
@@ -122,13 +124,13 @@ smtpd_exp_test: smtpd_check smtpd_exp.in smtpd_exp.ref
smtpd_server_test: smtpd_check smtpd_server.in smtpd_server.ref smtpd_server_test: smtpd_check smtpd_server.in smtpd_server.ref
$(SHLIB_ENV) $(VALGRIND) ./smtpd_check <smtpd_server.in >smtpd_server.tmp 2>&1 $(SHLIB_ENV) $(VALGRIND) ./smtpd_check <smtpd_server.in >smtpd_server.tmp 2>&1
diff smtpd_server.ref smtpd_server.tmp $(ADDRINFO_FIX) smtpd_server.tmp | diff smtpd_server.ref -
rm -f smtpd_server.tmp smtpd_check_access.* rm -f smtpd_server.tmp smtpd_check_access.*
smtpd_nullmx_test: smtpd_check smtpd_nullmx.in smtpd_nullmx.ref smtpd_nullmx_test: smtpd_check smtpd_nullmx.in smtpd_nullmx.ref
$(SHLIB_ENV) $(VALGRIND) ../postmap/postmap hash:smtpd_check_access $(SHLIB_ENV) $(VALGRIND) ../postmap/postmap hash:smtpd_check_access
$(SHLIB_ENV) $(VALGRIND) ./smtpd_check <smtpd_nullmx.in >smtpd_nullmx.tmp 2>&1 $(SHLIB_ENV) $(VALGRIND) ./smtpd_check <smtpd_nullmx.in >smtpd_nullmx.tmp 2>&1
diff smtpd_nullmx.ref smtpd_nullmx.tmp $(ADDRINFO_FIX) smtpd_nullmx.tmp | diff smtpd_nullmx.ref -
rm -f smtpd_nullmx.tmp smtpd_check_access.* rm -f smtpd_nullmx.tmp smtpd_check_access.*
smtpd_dns_filter_test: smtpd_check smtpd_dns_filter.in smtpd_dns_filter.ref \ smtpd_dns_filter_test: smtpd_check smtpd_dns_filter.in smtpd_dns_filter.ref \

View File

@@ -653,6 +653,8 @@ static ARGV *smtpd_check_parse(int flags, const char *checks)
return (argv); return (argv);
} }
#ifndef TEST
/* has_required - make sure required restriction is present */ /* has_required - make sure required restriction is present */
static int has_required(ARGV *restrictions, const char **required) static int has_required(ARGV *restrictions, const char **required)
@@ -711,6 +713,8 @@ static void fail_required(const char *name, const char **required)
name, STR(example)); name, STR(example));
} }
#endif
/* smtpd_check_init - initialize once during process lifetime */ /* smtpd_check_init - initialize once during process lifetime */
void smtpd_check_init(void) void smtpd_check_init(void)
@@ -719,6 +723,7 @@ void smtpd_check_init(void)
const char *name; const char *name;
const char *value; const char *value;
char *cp; char *cp;
#ifndef TEST
static const char *rcpt_required[] = { static const char *rcpt_required[] = {
REJECT_UNAUTH_DEST, REJECT_UNAUTH_DEST,
DEFER_UNAUTH_DEST, DEFER_UNAUTH_DEST,
@@ -728,6 +733,7 @@ void smtpd_check_init(void)
CHECK_RELAY_DOMAINS, CHECK_RELAY_DOMAINS,
0, 0,
}; };
#endif
static NAME_CODE tempfail_actions[] = { static NAME_CODE tempfail_actions[] = {
DEFER_ALL, DEFER_ALL_ACT, DEFER_ALL, DEFER_ALL_ACT,
DEFER_IF_PERMIT, DEFER_IF_PERMIT_ACT, DEFER_IF_PERMIT, DEFER_IF_PERMIT_ACT,
@@ -3205,9 +3211,9 @@ static int check_mail_access(SMTPD_STATE *state, const char *table,
* Look up user+foo@domain if the address has an extension, user@domain * Look up user+foo@domain if the address has an extension, user@domain
* otherwise. * otherwise.
*/ */
#define LOOKUP_STRATEGY (MAIL_ADDR_FIND_FULL | MAIL_ADDR_FIND_NOEXT \ #define LOOKUP_STRATEGY (MAF_STRATEGY_FULL | MAF_STRATEGY_NOEXT \
| MAIL_ADDR_FIND_DOMAIN | MAIL_ADDR_FIND_PMS \ | MAF_STRATEGY_DOMAIN | MAF_STRATEGY_PMS \
| MAIL_ADDR_FIND_LOCALPART_AT) | MAF_STRATEGY_LOCALPART_AT)
if ((maps = (MAPS *) htable_find(map_command_table, table)) == 0) { if ((maps = (MAPS *) htable_find(map_command_table, table)) == 0) {
msg_warn("%s: unexpected dictionary: %s", myname, table); msg_warn("%s: unexpected dictionary: %s", myname, table);
@@ -5893,7 +5899,6 @@ int main(int argc, char **argv)
char *bp; char *bp;
char *resp; char *resp;
char *addr; char *addr;
INET_PROTO_INFO *proto_info;
/* /*
* Initialization. Use dummies for client information. * Initialization. Use dummies for client information.
@@ -5905,7 +5910,7 @@ int main(int argc, char **argv)
int_init(); int_init();
smtpd_check_init(); smtpd_check_init();
smtpd_expand_init(); smtpd_expand_init();
proto_info = inet_proto_init(argv[0], INET_PROTO_NAME_IPV4); (void) inet_proto_init(argv[0], INET_PROTO_NAME_IPV4);
smtpd_state_init(&state, VSTREAM_IN, "smtpd"); smtpd_state_init(&state, VSTREAM_IN, "smtpd");
state.queue_id = "<queue id>"; state.queue_id = "<queue id>";

View File

@@ -261,9 +261,9 @@ int transport_lookup(TRANSPORT_INFO *tp, const char *addr,
* internal form. * internal form.
*/ */
#define LOOKUP_STRATEGY \ #define LOOKUP_STRATEGY \
(MAIL_ADDR_FIND_FULL | MAIL_ADDR_FIND_NOEXT | MAIL_ADDR_FIND_DOMAIN | \ (MAF_STRATEGY_FULL | MAF_STRATEGY_NOEXT | MAF_STRATEGY_DOMAIN | \
(transport_match_parent_style == MATCH_FLAG_PARENT ? \ (transport_match_parent_style == MATCH_FLAG_PARENT ? \
MAIL_ADDR_FIND_PMS : MAIL_ADDR_FIND_PMDS)) MAF_STRATEGY_PMS : MAF_STRATEGY_PMDS))
if ((ratsign = strrchr(addr, '@')) == 0 || ratsign[1] == 0) if ((ratsign = strrchr(addr, '@')) == 0 || ratsign[1] == 0)
msg_panic("transport_lookup: bad address: \"%s\"", addr); msg_panic("transport_lookup: bad address: \"%s\"", addr);
@@ -427,7 +427,7 @@ int main(int argc, char **argv)
vstring_free(nexthop); vstring_free(nexthop);
vstring_free(channel); vstring_free(channel);
vstring_free(buffer); vstring_free(buffer);
exit(0); exit(errs != 0);
} }
#endif #endif