diff --git a/postfix/HISTORY b/postfix/HISTORY
index 0e672df4f..a6d71241d 100644
--- a/postfix/HISTORY
+++ b/postfix/HISTORY
@@ -22845,3 +22845,21 @@ Apologies for any names omitted.
compatibility, for check_sender_access and check_recipient_access.
This now uses 'user@' lookup support in the mail_addr_find()
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.
diff --git a/postfix/WISHLIST b/postfix/WISHLIST
index 167140d49..f015bdd79 100644
--- a/postfix/WISHLIST
+++ b/postfix/WISHLIST
@@ -8,15 +8,11 @@ Wish list:
Enable external-form lookups for smtpd access maps.
- Enable caching in quote_822_local.
-
- Quoting: in smtp_map11, don't convert to external.
-
- Make the address substring generation ("strategy") suitable
- 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.
+ Convert postalias(1) to store external-form keys, and convert
+ aliases(5) to perform external-first lookup with fallback to
+ internal form, to make it consistent with the rest of Postfix.
+ In several years we may remove the internal-form fallbacks
+ with a compatibility_level safety net.
In the bounce daemon, set util_utf8_enable if returning an
SMTPUTF8 message.
@@ -37,18 +33,6 @@ Wish list:
RHS. This will not preserve trailing comments in lines that
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
with named parameters, as with the libtls API.
diff --git a/postfix/html/postmap.1.html b/postfix/html/postmap.1.html
index 82a720255..2d3361348 100644
--- a/postfix/html/postmap.1.html
+++ b/postfix/html/postmap.1.html
@@ -46,127 +46,128 @@ POSTMAP(1) POSTMAP(1)
When the key specifies email address information, the localpart should
be enclosed with double quotes if required by RFC 5322. 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
tables whose lookup keys are fixed-case strings such as btree:, dbm: or
hash:. With earlier versions, the lookup key is folded even with tables
- where a lookup field can match both upper and lower case text, such as
- regexp: and pcre:. This resulted in loss of information with $number
+ where a lookup field can match both upper and lower case text, such as
+ regexp: and pcre:. This resulted in loss of information with $number
substitutions.
COMMAND-LINE ARGUMENTS
- -b Enable message body query mode. When reading lookup keys from
- standard input with "-q -", process the input as if it is an
- email message in RFC 2822 format. Each line of body content
+ -b Enable message body query mode. When reading lookup keys from
+ standard input with "-q -", process the input as if it is an
+ email message in RFC 5322 format. Each line of body content
becomes one lookup key.
- By default, the -b option starts generating lookup keys at the
- first non-header line, and stops when the end of the message is
- reached. To simulate body_checks(5) processing, enable MIME
- parsing with -m. With this, the -b option generates no
- body-style lookup keys for attachment MIME headers and for
+ By default, the -b option starts generating lookup keys at the
+ first non-header line, and stops when the end of the message is
+ reached. To simulate body_checks(5) processing, enable MIME
+ parsing with -m. With this, the -b option generates no
+ body-style lookup keys for attachment MIME headers and for
attached message/* headers.
- NOTE: with "smtputf8_enable = yes", the -b option option dis-
- ables UTF-8 syntax checks on query keys and lookup results.
+ NOTE: with "smtputf8_enable = yes", the -b option option dis-
+ ables UTF-8 syntax checks on query keys and lookup results.
Specify the -U option to force UTF-8 syntax checks anyway.
This feature is available in Postfix version 2.6 and later.
-c config_dir
- Read the main.cf configuration file in the named directory
+ Read the main.cf configuration file in the named directory
instead of the default configuration directory.
- -d key Search the specified maps for key and remove one entry per map.
- The exit status is zero when the requested information was
+ -d key Search the specified maps for key and remove one entry per map.
+ The exit status is zero when the requested information was
found.
- If a key value of - is specified, the program reads key values
- from the standard input stream. The exit status is zero when at
+ If a key value of - is specified, the program reads key values
+ from the standard input stream. The exit status is zero when at
least one of the requested keys was found.
- -f Do not fold the lookup key to lower case while creating or
+ -f Do not fold the lookup key to lower case while creating or
querying a table.
- With Postfix version 2.3 and later, this option has no effect
+ With Postfix version 2.3 and later, this option has no effect
for regular expression tables. There, case folding is controlled
by appending a flag to a pattern.
- -h Enable message header query mode. When reading lookup keys from
- standard input with "-q -", process the input as if it is an
- email message in RFC 2822 format. Each logical header line
- becomes one lookup key. A multi-line header becomes one lookup
+ -h Enable message header query mode. When reading lookup keys from
+ standard input with "-q -", process the input as if it is an
+ email message in RFC 5322 format. Each logical header line
+ becomes one lookup key. A multi-line header becomes one lookup
key with one or more embedded newline characters.
- By default, the -h option generates lookup keys until the first
- non-header line is reached. To simulate header_checks(5) pro-
- cessing, enable MIME parsing with -m. With this, the -h option
- also generates header-style lookup keys for attachment MIME
+ By default, the -h option generates lookup keys until the first
+ non-header line is reached. To simulate header_checks(5) pro-
+ cessing, enable MIME parsing with -m. With this, the -h option
+ also generates header-style lookup keys for attachment MIME
headers and for attached message/* headers.
- NOTE: with "smtputf8_enable = yes", the -b option option dis-
- ables UTF-8 syntax checks on query keys and lookup results.
+ NOTE: with "smtputf8_enable = yes", the -b option option dis-
+ ables UTF-8 syntax checks on query keys and lookup results.
Specify the -U option to force UTF-8 syntax checks anyway.
This feature is available in Postfix version 2.6 and later.
- -i Incremental mode. Read entries from standard input and do not
- truncate an existing database. By default, postmap(1) creates a
+ -i Incremental mode. Read entries from standard input and do not
+ truncate an existing database. By default, postmap(1) creates a
new database from the entries in file_name.
-m Enable MIME parsing with "-b" and "-h".
This feature is available in Postfix version 2.6 and later.
- -N Include the terminating null character that terminates lookup
- keys and values. By default, postmap(1) does whatever is the
+ -N Include the terminating null character that terminates lookup
+ keys and values. By default, postmap(1) does whatever is the
default for the host operating system.
- -n Don't include the terminating null character that terminates
- lookup keys and values. By default, postmap(1) does whatever is
+ -n Don't include the terminating null character that terminates
+ lookup keys and values. By default, postmap(1) does whatever is
the default for the host operating system.
- -o Do not release root privileges when processing a non-root input
- file. By default, postmap(1) drops root privileges and runs as
+ -o Do not release root privileges when processing a non-root input
+ file. By default, postmap(1) drops root privileges and runs as
the source file owner instead.
- -p Do not inherit the file access permissions from the input file
- when creating a new file. Instead, create a new file with
+ -p Do not inherit the file access permissions from the input file
+ when creating a new file. Instead, create a new file with
default access permissions (mode 0644).
- -q key Search the specified maps for key and write the first value
- found to the standard output stream. The exit status is zero
+ -q key Search the specified maps for key and write the first value
+ found to the standard output stream. The exit status is zero
when the requested information was found.
- If a key value of - is specified, the program reads key values
- from the standard input stream and writes one line of key value
+ If a key value of - is specified, the program reads key values
+ from the standard input stream and writes one line of key value
output for each key that was found. The exit status is zero when
at least one of the requested keys was found.
- -r When updating a table, do not complain about attempts to update
+ -r When updating a table, do not complain about attempts to update
existing entries, and make those updates anyway.
- -s Retrieve all database elements, and write one line of key value
- output for each element. The elements are printed in database
- order, which is not necessarily the same as the original input
+ -s Retrieve all database elements, and write one line of key value
+ output for each element. The elements are printed in database
+ order, which is not necessarily the same as the original input
order.
- This feature is available in Postfix version 2.2 and later, and
+ This feature is available in Postfix version 2.2 and later, and
is not available for all database types.
- -u Disable UTF-8 support. UTF-8 support is enabled by default when
- "smtputf8_enable = yes". It requires that keys and values are
+ -u Disable UTF-8 support. UTF-8 support is enabled by default when
+ "smtputf8_enable = yes". It requires that keys and values are
valid UTF-8 strings.
-U With "smtputf8_enable = yes", force UTF-8 syntax checks with the
-b and -h options.
- -v Enable verbose logging for debugging purposes. Multiple -v
+ -v Enable verbose logging for debugging purposes. Multiple -v
options make the software increasingly verbose.
- -w When updating a table, do not complain about attempts to update
+ -w When updating a table, do not complain about attempts to update
existing entries, and ignore those attempts.
Arguments:
@@ -178,32 +179,32 @@ POSTMAP(1) POSTMAP(1)
The postmap(1) command can query any supported file type, but it
can create only the following file types:
- btree The output file is a btree file, named file_name.db.
- This is available on systems with support for db data-
+ btree The output file is a btree file, named file_name.db.
+ This is available on systems with support for db data-
bases.
- cdb The output consists of one file, named file_name.cdb.
- This is available on systems with support for cdb data-
+ cdb The output consists of one file, named file_name.cdb.
+ This is available on systems with support for cdb data-
bases.
dbm The output consists of two files, named file_name.pag and
file_name.dir. This is available on systems with support
for dbm databases.
- hash The output file is a hashed file, named file_name.db.
- This is available on systems with support for db data-
+ hash The output file is a hashed file, named file_name.db.
+ This is available on systems with support for db data-
bases.
- fail A table that reliably fails all requests. The lookup ta-
- ble name is used for logging only. This table exists to
+ fail A table that reliably fails all requests. The lookup ta-
+ ble name is used for logging only. This table exists to
simplify Postfix error tests.
sdbm The output consists of two files, named file_name.pag and
file_name.dir. This is available on systems with support
for sdbm databases.
- When no file_type is specified, the software uses the database
- type specified via the default_database_type configuration
+ When no file_type is specified, the software uses the database
+ type specified via the default_database_type configuration
parameter.
file_name
@@ -212,11 +213,11 @@ POSTMAP(1) POSTMAP(1)
DIAGNOSTICS
Problems are logged to the standard error stream and to syslogd(8). No
- output means that no problems were detected. Duplicate entries are
+ output means that no problems were detected. Duplicate entries are
skipped and are flagged with a warning.
postmap(1) terminates with zero exit status in case of success (includ-
- ing successful "postmap -q" lookup) and terminates with non-zero exit
+ ing successful "postmap -q" lookup) and terminates with non-zero exit
status in case of failure.
ENVIRONMENT
@@ -227,12 +228,12 @@ POSTMAP(1) POSTMAP(1)
Enable verbose logging for debugging purposes.
CONFIGURATION PARAMETERS
- The following main.cf parameters are especially relevant to this pro-
- gram. The text below provides only a parameter summary. See post-
+ The following main.cf parameters are especially relevant to this pro-
+ gram. The text below provides only a parameter summary. See post-
conf(5) for more details including examples.
berkeley_db_create_buffer_size (16777216)
- The per-table I/O buffer size for programs that create Berkeley
+ The per-table I/O buffer size for programs that create Berkeley
DB hash or btree tables.
berkeley_db_read_buffer_size (131072)
@@ -240,7 +241,7 @@ POSTMAP(1) POSTMAP(1)
hash or btree tables.
config_directory (see 'postconf -d' output)
- The default location of the Postfix main.cf and master.cf con-
+ The default location of the Postfix main.cf and master.cf con-
figuration files.
default_database_type (see 'postconf -d' output)
@@ -248,14 +249,14 @@ POSTMAP(1) POSTMAP(1)
and postmap(1) commands.
smtputf8_enable (yes)
- Enable preliminary SMTPUTF8 support for the protocols described
+ Enable preliminary SMTPUTF8 support for the protocols described
in RFC 6531..6533.
syslog_facility (mail)
The syslog facility of Postfix logging.
syslog_name (see 'postconf -d' output)
- A prefix that is prepended to the process name in syslog
+ A prefix that is prepended to the process name in syslog
records, so that, for example, "smtpd" becomes "prefix/smtpd".
SEE ALSO
diff --git a/postfix/man/man1/postmap.1 b/postfix/man/man1/postmap.1
index f0f8e2bee..862438ed2 100644
--- a/postfix/man/man1/postmap.1
+++ b/postfix/man/man1/postmap.1
@@ -56,7 +56,7 @@ keys is supported as of Postfix 3.2.
When the \fIkey\fR specifies email address information, the
localpart should be enclosed with double quotes if required
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
the lookups case insensitive; as of Postfix 2.3 this case
@@ -74,7 +74,7 @@ information with $\fInumber\fR substitutions.
.IP \fB\-b\fR
Enable message body query mode. When reading lookup keys
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.
.sp
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
Enable message header query mode. When reading lookup keys
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
header becomes one lookup key with one or more embedded
newline characters.
diff --git a/postfix/src/cleanup/cleanup_addr.c b/postfix/src/cleanup/cleanup_addr.c
index b6396ada8..73b72edc4 100644
--- a/postfix/src/cleanup/cleanup_addr.c
+++ b/postfix/src/cleanup/cleanup_addr.c
@@ -165,8 +165,9 @@ off_t cleanup_addr_sender(CLEANUP_STATE *state, const char *buf)
if ((state->flags & CLEANUP_FLAG_BCC_OK)
&& *STR(clean_addr)
&& cleanup_send_bcc_maps) {
- if ((bcc = mail_addr_find(cleanup_send_bcc_maps, STR(clean_addr),
- IGNORE_EXTENSION)) != 0) {
+ if ((bcc = mail_addr_find_to_internal(cleanup_send_bcc_maps,
+ STR(clean_addr),
+ IGNORE_EXTENSION)) != 0) {
cleanup_addr_bcc(state, bcc);
} else if (cleanup_send_bcc_maps->error) {
msg_warn("%s: %s map lookup problem -- "
@@ -228,8 +229,9 @@ void cleanup_addr_recipient(CLEANUP_STATE *state, const char *buf)
if ((state->flags & CLEANUP_FLAG_BCC_OK)
&& *STR(clean_addr)
&& cleanup_rcpt_bcc_maps) {
- if ((bcc = mail_addr_find(cleanup_rcpt_bcc_maps, STR(clean_addr),
- IGNORE_EXTENSION)) != 0) {
+ if ((bcc = mail_addr_find_to_internal(cleanup_rcpt_bcc_maps,
+ STR(clean_addr),
+ IGNORE_EXTENSION)) != 0) {
cleanup_addr_bcc(state, bcc);
} else if (cleanup_rcpt_bcc_maps->error) {
msg_warn("%s: %s map lookup problem -- "
diff --git a/postfix/src/cleanup/cleanup_map11.c b/postfix/src/cleanup/cleanup_map11.c
index 941e156f2..fd9cddcf1 100644
--- a/postfix/src/cleanup/cleanup_map11.c
+++ b/postfix/src/cleanup/cleanup_map11.c
@@ -105,6 +105,7 @@ int cleanup_map11_external(CLEANUP_STATE *state, VSTRING *addr,
*/
for (count = 0; count < MAX_RECURSION; count++) {
if ((new_addr = mail_addr_map_opt(maps, STR(addr), propagate,
+ MAIL_ADDR_FORM_EXTERNAL,
MAIL_ADDR_FORM_EXTERNAL,
MAIL_ADDR_FORM_EXTERNAL)) != 0) {
if (new_addr->argc > 1)
diff --git a/postfix/src/global/Makefile.in b/postfix/src/global/Makefile.in
index 4bafabeb4..722517dbc 100644
--- a/postfix/src/global/Makefile.in
+++ b/postfix/src/global/Makefile.in
@@ -118,7 +118,7 @@ TESTPROG= domain_list dot_lockfile mail_addr_crunch mail_addr_find \
valid_mailhost_addr own_inet_addr header_body_checks \
data_redirect addr_match_list safe_ultostr verify_sender_addr \
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)
LIB_DIR = ../../lib
@@ -249,8 +249,10 @@ off_cvt: $(LIB) $(LIBS)
$(CC) $(CFLAGS) -DTEST -o $@ $@.c $(LIB) $(LIBS) $(SYSLIBS)
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)
+ mv junk $@.o
mail_addr_find: $(LIB) $(LIBS)
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
rm -f mail_addr_find.tmp
-mail_addr_map_test: update mail_addr_map_tester mail_addr_map.ref
- -$(SHLIB_ENV) $(VALGRIND) ./mail_addr_map_tester pass_tests
- -$(SHLIB_ENV) $(VALGRIND) ./mail_addr_map_tester fail_tests >mail_addr_map.tmp 2>&1
+mail_addr_map_test: update mail_addr_map mail_addr_map.ref
+ -$(SHLIB_ENV) $(VALGRIND) ./mail_addr_map pass_tests
+ -$(SHLIB_ENV) $(VALGRIND) ./mail_addr_map fail_tests >mail_addr_map.tmp 2>&1
diff mail_addr_map.ref 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/myflock.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/sys_defs.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: quote_822_local.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/check_arg.h
mail_command_client.o: ../../include/htable.h
diff --git a/postfix/src/global/mail_addr_find.c b/postfix/src/global/mail_addr_find.c
index 56907acc7..5934db5c1 100644
--- a/postfix/src/global/mail_addr_find.c
+++ b/postfix/src/global/mail_addr_find.c
@@ -11,12 +11,13 @@
/* const char *address;
/* char **extension;
/*
-/* const char *mail_addr_find_opt(maps, address, extension,
-/* in_form, out_form, strategy)
+/* const char *mail_addr_find_opt(maps, address, extension, in_form,
+/* query_form, out_form, strategy)
/* MAPS *maps;
/* const char *address;
/* char **extension;
/* int in_form;
+/* int in_form;
/* int out_form;
/* int strategy;
/* LEGACY SUPPORT
@@ -25,6 +26,11 @@
/* const char *address;
/* 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)
/* MAPS *maps;
/* const char *address;
@@ -37,33 +43,37 @@
/* preferences when it opens the maps.
/* The result is overwritten upon each call.
/*
-/* The table key and value are expected to be in external
-/* (quoted) form. Override these assumptions with the in_form
+/* In the lookup table, the key is expected to be in external
+/* 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.
/*
/* With mail_addr_find_int_to_ext(), the specified address is in
-/* internal (unquoted) form. The result is in the form found
-/* in the table (it is not necessarily an email address). This
-/* version minimizes internal/external (unquoted/quoted)
-/* conversions of the query, extension, or result.
+/* internal (unquoted) form, the query is made in external (quoted)
+/* form, and the result is in the form found in the table (it is
+/* not necessarily an email address). This version minimizes
+/* internal/external (unquoted/quoted) conversions of the input,
+/* query, extension, or result.
/*
/* mail_addr_find_opt() gives more control, at the cost of
/* 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.
/*
-/* mail_addr_find() is used by legacy code that historically
-/* searched with internal-form keys. The lookup strategy is
-/* to first look up with (in_form, out_form) of (INTERNAL,
-/* NOCONV), which converts the key to external form. If no
-/* result is found, and the internal and external key forms
-/* differ, there is a backwards-compatibility lookup with
-/* (in_form, out_form) of (NOCONV, NOCONV).
+/* mail_addr_find() is used by legacy code that historically searched
+/* with internal-form queries. The input is in internal form. It
+/* searches with external-form queries first, and falls back to
+/* internal-form queries if no result was found and the external
+/* and internal forms differ. The result is external form (i.e. no
+/* conversion).
/*
-/* mail_addr_find_strategy() overrides the default search
-/* strategy for full and partial addresses.
+/* mail_addr_find_to_internal() is like mail_addr_find() but assumes
+/* 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:
/* .IP maps
@@ -77,48 +87,54 @@
/* The copy includes the recipient address delimiter.
/* The copy is in internal (unquoted) form.
/* 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 out_form
-/* Input and output address forms, either MAIL_ADDR_FORM_INTERNAL
-/* (unquoted form), MAIL_ADDR_FORM_EXTERNAL (quoted form), or
-/* MAIL_ADDR_FORM_NOCONV (don't convert between unquoted and
-/* quoted form).
+/* Input and output address forms, one of MAIL_ADDR_FORM_INTERNAL
+/* (unquoted form), or MAIL_ADDR_FORM_EXTERNAL (quoted form).
/* .IP strategy
/* The lookup strategy for full and partial addresses, specified
-/* as the binary OR of one or more of the following. These
-/* lookups are implemented in the order as listed below.
+/* as the binary OR of one or more of the following. These lookups
+/* are implemented in the order as listed below.
/* .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.
-/* .IP MAIL_ADDR_FIND_NOEXT
-/* If no match was found, and the address has an extension,
-/* look up the address after removing the address extension.
-/* .IP MAIL_ADDR_FIND_LOCALPART_IF_LOCAL
+/* .IP MAF_STRATEGY_NOEXT
+/* If no match was found, and the address has a localpart extension,
+/* look up the address after removing the extension.
+/* .IP MAF_STRATEGY_LOCALPART_IF_LOCAL
/* If no match was found, and the domain matches myorigin,
-/* mydestination, or any proxy_interfaces IP address, look up
-/* the localpart. If no match was found, and the address has
-/* an extension, repeat the same query after removing the
-/* address extension unless MAIL_ADDR_FIND_NOEXT is specified.
-/* .IP MAIL_ADDR_FIND_LOCALPART_AT_IF_LOCAL
+/* mydestination, or any inet_interfaces or proxy_interfaces IP
+/* address, look up the localpart. If no match was found, and the
+/* address has a localpart extension, repeat the same query after
+/* removing the extension unless MAF_STRATEGY_NOEXT is specified.
+/* .IP MAF_STRATEGY_LOCALPART_AT_IF_LOCAL
/* 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.
-/* .IP MAIL_ADDR_FIND_DOMAIN
+/* .IP MAF_STRATEGY_DOMAIN
/* If no match was found, look up the domain without localpart.
-/* .IP MAIL_ADDR_FIND_PMS
-/* When used with MAIL_ADDR_FIND_DOMAIN, also matches subdomains.
-/* .IP MAIL_ADDR_FIND_PMDS
-/* When used with MAIL_ADDR_FIND_DOMAIN, also matches dot-subdomains.
-/* .IP MAIL_ADDR_FIND_LOCALPART_AT
-/* If no match was found, look up the localpart@, regardless
-/* of the domain content.
+/* .IP MAF_STRATEGY_PMS
+/* When used with MAF_STRATEGY_DOMAIN, also matches subdomains.
+/* .IP MAF_STRATEGY_PMDS
+/* When used with MAF_STRATEGY_DOMAIN, also matches dot-subdomains.
+/* .IP MAF_STRATEGY_LOCALPART_AT
+/* If no match was found, look up the localpart@, regardless of
+/* the domain content.
/* .RE
/* DIAGNOSTICS
-/* The maps->error value is non-zero when the lookup
-/* should be tried again.
+/* The maps->error value is non-zero when the lookup should be
+/* tried again.
/* SEE ALSO
-/* maps(3), multi-dictionary search
-/* resolve_local(3), recognize local system
+/* maps(3), multi-dictionary search resolve_local(3), recognize
+/* local system
/* LICENSE
/* .ad
/* .fi
@@ -156,27 +172,22 @@
#define STR vstring_str
+#ifdef TEST
+
static const NAME_MASK strategy_table[] = {
- "full", MAIL_ADDR_FIND_FULL,
- "noext", MAIL_ADDR_FIND_NOEXT,
- "localpart_if_local", MAIL_ADDR_FIND_LOCALPART_IF_LOCAL,
- "localpart_at_if_local", MAIL_ADDR_FIND_LOCALPART_AT_IF_LOCAL,
- "atdomain", MAIL_ADDR_FIND_ATDOMAIN,
- "domain", MAIL_ADDR_FIND_DOMAIN,
- "pms", MAIL_ADDR_FIND_PMS,
- "pmds", MAIL_ADDR_FIND_PMDS,
- "localpartat", MAIL_ADDR_FIND_LOCALPART_AT,
- "default", MAIL_ADDR_FIND_DEFAULT,
+ "full", MAF_STRATEGY_FULL,
+ "noext", MAF_STRATEGY_NOEXT,
+ "localpart_if_local", MAF_STRATEGY_LOCALPART_IF_LOCAL,
+ "localpart_at_if_local", MAF_STRATEGY_LOCALPART_AT_IF_LOCAL,
+ "atdomain", MAF_STRATEGY_AT_DOMAIN,
+ "domain", MAF_STRATEGY_DOMAIN,
+ "pms", MAF_STRATEGY_PMS,
+ "pmds", MAF_STRATEGY_PMDS,
+ "localpart_at", MAF_STRATEGY_LOCALPART_AT,
+ "default", MAF_STRATEGY_DEFAULT,
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 */
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));
}
-/* 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,
- 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 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,
with_domain ? QUOTE_FLAG_DEFAULT :
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 */
static const char *find_local(MAPS *path, char *ratsign, int rats_offs,
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)
{
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;
/*
- * This was ripped from the middle of a function so it can be reused,
- * that's why the interface makes no sense.
+ * This code was ripped from the middle of a function so that it can be
+ * reused multiple times, that's why the interface makes little sense.
*/
with_domain = rats_offs ? WITH_DOMAIN : SANS_DOMAIN;
saved_ch = *(unsigned char *) (ratsign + rats_offs);
*(ratsign + rats_offs) = 0;
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;
if (result == 0 && path->error == 0 && 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);
*(ratsign + rats_offs) = 0;
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 = *saved_ext;
*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 */
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";
VSTRING *ext_addr_buf = 0;
- int find_form;
VSTRING *int_addr_buf = 0;
const char *int_addr;
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;
/*
- * Optionally convert input from external form.
+ * Optionally convert the address from external form.
*/
if (in_form == MAIL_ADDR_FORM_EXTERNAL) {
int_addr_buf = vstring_alloc(100);
unquote_822_local(int_addr_buf, address);
int_addr = STR(int_addr_buf);
- find_form = MAIL_ADDR_FORM_INTERNAL;
} else {
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);
/*
* Initialize.
*/
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;
} else {
+ /* XXX This could be done after user+foo@domain fails. */
int_bare_key =
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.
*/
- if ((strategy & MAIL_ADDR_FIND_FULL) != 0) {
+ if ((strategy & MAF_STRATEGY_FULL) != 0) {
result = find_addr(path, int_full_key, FULL, WITH_DOMAIN,
- find_form, ext_addr_buf);
+ query_form, ext_addr_buf);
} else {
result = 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
&& (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 = saved_ext;
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
* try with +foo stripped off.
*/
if (result == 0 && path->error == 0
&& (ratsign = strrchr(int_full_key, '@')) != 0
- && (strategy & (MAIL_ADDR_FIND_LOCALPART_IF_LOCAL
- | MAIL_ADDR_FIND_LOCALPART_AT_IF_LOCAL)) != 0) {
+ && (strategy & (MAF_STRATEGY_LOCALPART_IF_LOCAL
+ | MAF_STRATEGY_LOCALPART_AT_IF_LOCAL)) != 0) {
if (strcasecmp_utf8(ratsign + 1, var_myorigin) == 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,
- int_bare_key, find_form, extp, &saved_ext,
+ int_bare_key, query_form, extp, &saved_ext,
ext_addr_buf);
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,
- int_bare_key, find_form, extp, &saved_ext,
+ int_bare_key, query_form, extp, &saved_ext,
ext_addr_buf);
} else if (rc < 0)
path->error = rc;
@@ -347,40 +392,40 @@ const char *mail_addr_find_opt(MAPS *path, const char *address, char **extp,
* Try @domain.
*/
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);
/*
* Try domain (optionally, subdomains).
*/
if (result == 0 && path->error == 0 && ratsign != 0
- && (strategy & MAIL_ADDR_FIND_DOMAIN) != 0) {
+ && (strategy & MAF_STRATEGY_DOMAIN) != 0) {
const char *name;
const char *next;
for (name = ratsign + 1; *name != 0; name = next) {
if ((result = maps_find(path, name, PARTIAL)) != 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)
break;
- if ((strategy & MAIL_ADDR_FIND_PMDS) == 0)
+ if ((strategy & MAF_STRATEGY_PMDS) == 0)
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 = 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);
/*
* 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 (int_result == 0)
@@ -409,40 +454,6 @@ const char *mail_addr_find_opt(MAPS *path, const char *address, char **extp,
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
/*
@@ -455,23 +466,25 @@ const char *mail_addr_find_strategy(MAPS *path, const char *address,
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)
{
VSTRING *buffer = vstring_alloc(100);
char *bp;
- MAPS *path;
+ MAPS *path = 0;
const char *result;
char *extent;
char *in_field;
+ char *query_field;
char *out_field;
char *strategy_field;
char *key_field;
char *expect_res;
char *expect_ext;
int in_form;
+ int query_form;
int out_form;
int strategy_flags;
int ch;
@@ -489,7 +502,7 @@ int main(int argc, char **argv)
usage(argv[0]);
}
}
- if (argc != optind + 1)
+ if (argc != optind)
usage(argv[0]);
/*
@@ -502,26 +515,38 @@ int main(int argc, char **argv)
UPDATE(var_mydomain, "localdomain");
UPDATE(var_myorigin, "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)) {
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.
*/
- /* internal, external, noconv, or compat. */
+ /* internal, external. */
if ((in_field = mystrtok(&bp, ":")) == 0)
msg_fatal("no input form");
- if ((in_form = mail_addr_form_from_string(in_field)) < 0
- && strcmp(in_field, "compat") != 0)
+ if ((in_form = mail_addr_form_from_string(in_field)) < 0)
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)
msg_fatal("no output form");
- /* internal, external, noconv, or compat, depending on in_form. */
- if (((out_form = mail_addr_form_from_string(out_field)) < 0
- && strcmp(out_field, "compat") != 0)
- || ((in_form >= 0) != (out_form >= 0)))
+ /* internal, external. */
+ if ((out_form = mail_addr_form_from_string(out_field)) < 0)
msg_fatal("bad output form: '%s'", out_field);
if ((strategy_field = mystrtok(&bp, ":")) == 0)
msg_fatal("no strategy field");
@@ -538,18 +563,11 @@ int main(int argc, char **argv)
* Lookups.
*/
extent = 0;
- if (in_form >= 0 && out_form >= 0) {
- /* It's the future. */
- result = mail_addr_find_opt(path, key_field, &extent,
- in_form, out_form,
- strategy_flags);
- } else {
- /* Backwards compatibility. */
- 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 :
+ result = mail_addr_find_opt(path, key_field, &extent,
+ in_form, query_form, out_form,
+ strategy_flags);
+ vstream_printf("%s:%s -%s-> %s:%s (%s)\n",
+ in_field, key_field, query_field, out_field, result ? result :
path->error ? "(try again)" :
"(not found)", extent ? extent : "null extension");
vstream_fflush(VSTREAM_OUT);
diff --git a/postfix/src/global/mail_addr_find.h b/postfix/src/global/mail_addr_find.h
index 9c6546d9f..9afccf594 100644
--- a/postfix/src/global/mail_addr_find.h
+++ b/postfix/src/global/mail_addr_find.h
@@ -1,5 +1,5 @@
-#ifndef _MAIL_ADDR_FIND_H_INCLUDED_
-#define _MAIL_ADDR_FIND_H_INCLUDED_
+#ifndef _MAF_STRATEGY_H_INCLUDED_
+#define _MAF_STRATEGY_H_INCLUDED_
/*++
/* NAME
@@ -20,38 +20,48 @@
/*
* External interface.
*/
-extern const char *mail_addr_find_opt(MAPS *, const char *, char **,
- int, int, int);
+extern const char *mail_addr_find_opt(MAPS *, const char *, char **,
+ int, int, int, int);
-#define MAIL_ADDR_FIND_FULL (1<<0) /* localpart+ext@domain */
-#define MAIL_ADDR_FIND_NOEXT (1<<1) /* localpart@domain */
-#define MAIL_ADDR_FIND_LOCALPART_IF_LOCAL \
+#define MAF_STRATEGY_FULL (1<<0) /* localpart+ext@domain */
+#define MAF_STRATEGY_NOEXT (1<<1) /* localpart@domain */
+#define MAF_STRATEGY_LOCALPART_IF_LOCAL \
(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 */
-#define MAIL_ADDR_FIND_ATDOMAIN (1<<4) /* @domain */
-#define MAIL_ADDR_FIND_DOMAIN (1<<5) /* domain */
-#define MAIL_ADDR_FIND_PMS (1<<6) /* parent matches subdomain */
-#define MAIL_ADDR_FIND_PMDS (1<<7) /* parent matches dot-subdomain */
-#define MAIL_ADDR_FIND_LOCALPART_AT \
+#define MAF_STRATEGY_AT_DOMAIN (1<<4) /* @domain */
+#define MAF_STRATEGY_DOMAIN (1<<5) /* domain */
+#define MAF_STRATEGY_PMS (1<<6) /* parent matches subdomain */
+#define MAF_STRATEGY_PMDS (1<<7) /* parent matches dot-subdomain */
+#define MAF_STRATEGY_LOCALPART_AT \
(1<<8) /* localpart@ (maybe localpart+ext@) */
-#define MAIL_ADDR_FIND_DEFAULT (MAIL_ADDR_FIND_FULL | MAIL_ADDR_FIND_NOEXT \
- | MAIL_ADDR_FIND_LOCALPART_IF_LOCAL \
- | MAIL_ADDR_FIND_ATDOMAIN)
+#define MAF_STRATEGY_DEFAULT (MAF_STRATEGY_FULL | MAF_STRATEGY_NOEXT \
+ | MAF_STRATEGY_LOCALPART_IF_LOCAL \
+ | MAF_STRATEGY_AT_DOMAIN)
/* The least-overhead form. */
#define mail_addr_find_int_to_ext(maps, address, extension) \
mail_addr_find_opt((maps), (address), (extension), \
MAIL_ADDR_FORM_INTERNAL, MAIL_ADDR_FORM_EXTERNAL, \
- MAIL_ADDR_FIND_DEFAULT)
+ MAIL_ADDR_FORM_EXTERNAL, MAF_STRATEGY_DEFAULT)
- /* The legacy form. */
-extern const char *mail_addr_find_strategy(MAPS *, const char *, char **, int);
+ /* The legacy forms. */
+#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) \
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
/* .ad
diff --git a/postfix/src/global/mail_addr_find.in b/postfix/src/global/mail_addr_find.in
index 36c1cfc5d..b8278594f 100644
--- a/postfix/src/global/mail_addr_find.in
+++ b/postfix/src/global/mail_addr_find.in
@@ -4,78 +4,94 @@
# The last fields are optional.
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'
-internal:external:default:plain1@1.example:plain2@2.example
-internal:external:default:aa bb@cc.example:"dd ee"@dd.example
-external:external:default:"aa bb"@cc.example:"dd ee"@dd.example
-external:internal:default:"aa bb"@cc.example:dd ee@dd.example
-noconv:noconv:default:plain1@1.example:plain2@2.example
-noconv:noconv:default:aa bb@cc.example
-noconv:noconv:default:"aa bb"@cc.example:"dd ee"@dd.example
+$VALGRIND ./mail_addr_find <<'EOF'
+inline:{plain1@1.example=plain2@2.example,{"aa bb"@cc.example="dd ee"@dd.example}}
+internal:external:external:default:plain1@1.example:plain2@2.example
+internal:external:external:default:aa bb@cc.example:"dd ee"@dd.example
+external:external:external:default:"aa bb"@cc.example:"dd ee"@dd.example
+external:external:internal:default:"aa bb"@cc.example:dd ee@dd.example
+internal:internal:external:default:plain1@1.example:plain2@2.example
+internal:internal:external:default:aa bb@cc.example
+internal:internal:external:default:"aa bb"@cc.example:"dd ee"@dd.example
EOF
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'
-internal:external:default:plain1+ext@1.example:plain2@2.example:+ext
-internal:external:default:aa bb+ax bx@cc.example:"dd ee"@dd.example:+ax bx
-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
-noconv:noconv:default:plain1+ext@1.example:plain2@2.example:+ext
-noconv:noconv:default:"aa bb+ax bx"@cc.example
-noconv:noconv:default:"aa bb"+ax bx@cc.example:"dd ee"@dd.example:+ax bx
+$VALGRIND ./mail_addr_find <<'EOF'
+inline:{plain1@1.example=plain2@2.example,{"aa bb"@cc.example="dd ee"@dd.example}}
+internal:external:external:default:plain1+ext@1.example:plain2@2.example:+ext
+internal:external:external: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
+external:external:internal:default:"aa bb+ax bx"@cc.example:dd ee@dd.example:+ax bx
+internal:internal:external:default:plain1+ext@1.example:plain2@2.example:+ext
+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
echo ==== at in localpart
-$VALGRIND ./mail_addr_find 'inline:{"a@b"=foo@example,"a.b."=bar@example}' <<'EOF'
-external:external:default:"a@b"@localhost.localdomain:foo@example
-external:external:default:"a@b+ext"@localhost.localdomain:foo@example:+ext
-external:external:default:"a.b."@localhost.localdomain:bar@example
+$VALGRIND ./mail_addr_find <<'EOF'
+inline:{"a@b"=foo@example,"a.b."=bar@example}
+external:external:external:default:"a@b"@localhost.localdomain:foo@example
+external:external:external:default:"a@b+ext"@localhost.localdomain:foo@example:+ext
+external:external:external:default:"a.b."@localhost.localdomain:bar@example
EOF
echo ==== legacy support
-$VALGRIND ./mail_addr_find 'inline:{"a@b"=extern-1@example,a@b=intern-1@example,a.b.=intern-2@example}' <<'EOF'
-compat:compat:default:a@b@localhost.localdomain:extern-1@example
-compat:compat:default:a.b.@localhost.localdomain:intern-2@example
+$VALGRIND ./mail_addr_find <<'EOF'
+inline:{"a@b"=extern-1@example,a@b=intern-1@example,a.b.=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
-echo ==== atdomain test
-$VALGRIND ./mail_addr_find 'inline:{plain1@1.example=plain2@2.example,@3.example=plain4@4.example,plain5@3.example=plain6@6.example}' <<'EOF'
-external:external:default:plain1+ext@1.example:plain2@2.example:+ext
-external:external:default:plain2@2.example:
-external:external:default:plain3@3.example:plain4@4.example
-external:external:default:plain5@3.example:plain6@6.example
+echo ==== at_domain test
+$VALGRIND ./mail_addr_find <<'EOF'
+inline:{plain1@1.example=plain2@2.example,@3.example=plain4@4.example,plain5@3.example=plain6@6.example}
+external:external:external:default:plain1+ext@1.example:plain2@2.example:+ext
+external:external:external:default:plain2@2.example:
+external:external:external:default:plain3@3.example:plain4@4.example
+external:external:external:default:plain5@3.example:plain6@6.example
EOF
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'
-external:external:full|noext|domain:plain1+ext@1.example:plain2@2.example:+ext
-external:external:full|noext|domain:plain2@2.example:
-external:external:full|noext|domain:plain3@3.example:plain4@4.example
-external:external:full|noext|domain:plain5@3.example:plain6@6.example
+$VALGRIND ./mail_addr_find <<'EOF'
+inline:{plain1@1.example=plain2@2.example,3.example=plain4@4.example,plain5@3.example=plain6@6.example}
+external:external:external:full|noext|domain:plain1+ext@1.example:plain2@2.example:+ext
+external:external:external:full|noext|domain:plain2@2.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
-echo ==== atdomain for local domain
-$VALGRIND ./mail_addr_find 'inline:{ab=foo@example,@localhost.localdomain=@bar.example}' <<'EOF'
-external:external:default:ab@localhost.localdomain:foo@example:
-external:external:default:cd@localhost.localdomain:@bar.example
+echo ==== at_domain for local domain
+$VALGRIND ./mail_addr_find <<'EOF'
+inline:{ab=foo@example,@localhost.localdomain=@bar.example}
+external:external:external:default:ab@localhost.localdomain:foo@example:
+external:external:external:default:cd@localhost.localdomain:@bar.example
EOF
-echo ==== localat and domain test
-$VALGRIND ./mail_addr_find 'inline:{ab@=foo@example,localhost.localdomain=@bar.example}' <<'EOF'
-internal:external:localpart_at_if_local|domain:ab@localhost.localdomain:foo@example:
-internal:external:localpart_at_if_local|noext|domain:ab+ext@localhost.localdomain:foo@example:+ext
-internal:external:localpart_at_if_local|domain:cd@localhost.localdomain:@bar.example
+echo ==== localpart_at_if_local and domain test
+$VALGRIND ./mail_addr_find <<'EOF'
+inline:{ab@=foo@example,localhost.localdomain=@bar.example}
+internal:external:external:localpart_at_if_local|domain:ab@localhost.localdomain:foo@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
echo ==== domain and subdomain test
-$VALGRIND ./mail_addr_find 'inline:{example=example-result,.example=dot-example-result}' <<'EOF'
-external:external:domain:plain1+ext@1.example
-external:external:domain:foo@sub.example
-external:external:domain:foo@example:example-result
-external:external:domain|pms:foo@example:example-result
-external:external:domain|pms:foo@sub.example:example-result
-external:external:domain|pms:foo@sub.sub.example:example-result
-external:external:domain|pmds:foo@example:example-result
-external:external:domain|pmds:foo@sub.example:dot-example-result
-external:external:domain|pmds:foo@sub.sub.example:dot-example-result
+$VALGRIND ./mail_addr_find <<'EOF'
+inline:{example=example-result,.example=dot-example-result}
+external:external:external:domain:plain1+ext@1.example
+external:external:external:domain:foo@sub.example
+external:external:external:domain:foo@example:example-result
+external:external:external:domain|pms:foo@example:example-result
+external:external:external:domain|pms:foo@sub.example:example-result
+external:external:external:domain|pms:foo@sub.sub.example:example-result
+external:external:external:domain|pmds:foo@example: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
diff --git a/postfix/src/global/mail_addr_find.ref b/postfix/src/global/mail_addr_find.ref
index 102b807d2..6833eecc5 100644
--- a/postfix/src/global/mail_addr_find.ref
+++ b/postfix/src/global/mail_addr_find.ref
@@ -1,50 +1,63 @@
==== no search string extension
-internal:plain1@1.example -> external:plain2@2.example (null extension)
-internal:aa bb@cc.example -> external:"dd ee"@dd.example (null extension)
-external:"aa bb"@cc.example -> external:"dd ee"@dd.example (null extension)
-external:"aa bb"@cc.example -> internal:dd ee@dd.example (null extension)
-noconv:plain1@1.example -> noconv:plain2@2.example (null extension)
-noconv:aa bb@cc.example -> noconv:(not found) (null extension)
-noconv:"aa bb"@cc.example -> noconv:"dd ee"@dd.example (null extension)
+inline:{plain1@1.example=plain2@2.example,{"aa bb"@cc.example="dd ee"@dd.example}}
+internal:plain1@1.example -external-> external:plain2@2.example (null extension)
+internal:aa bb@cc.example -external-> external:"dd ee"@dd.example (null extension)
+external:"aa bb"@cc.example -external-> external:"dd ee"@dd.example (null extension)
+external:"aa bb"@cc.example -external-> internal:dd ee@dd.example (null extension)
+internal:plain1@1.example -internal-> external:plain2@2.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
-internal:plain1+ext@1.example -> external:plain2@2.example (+ext)
-internal:aa bb+ax bx@cc.example -> external:"dd ee"@dd.example (+ax bx)
-external:"aa bb+ax bx"@cc.example -> external:"dd ee"@dd.example (+ax bx)
-external:"aa bb+ax bx"@cc.example -> internal:dd ee@dd.example (+ax bx)
-noconv:plain1+ext@1.example -> noconv:plain2@2.example (+ext)
-noconv:"aa bb+ax bx"@cc.example -> noconv:(not found) (null extension)
-noconv:"aa bb"+ax bx@cc.example -> noconv:"dd ee"@dd.example (+ax bx)
+inline:{plain1@1.example=plain2@2.example,{"aa bb"@cc.example="dd ee"@dd.example}}
+internal:plain1+ext@1.example -external-> external:plain2@2.example (+ext)
+internal:aa bb+ax bx@cc.example -external-> external:"dd ee"@dd.example (+ax bx)
+external:"aa bb+ax bx"@cc.example -external-> external:"dd ee"@dd.example (+ax bx)
+external:"aa bb+ax bx"@cc.example -external-> internal:dd ee@dd.example (+ax bx)
+internal:plain1+ext@1.example -internal-> external:plain2@2.example (+ext)
+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
-external:"a@b"@localhost.localdomain -> external:foo@example (null extension)
-external:"a@b+ext"@localhost.localdomain -> external:foo@example (+ext)
-external:"a.b."@localhost.localdomain -> external:bar@example (null extension)
+inline:{"a@b"=foo@example,"a.b."=bar@example}
+external:"a@b"@localhost.localdomain -external-> external:foo@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
-compat:a@b@localhost.localdomain -> compat:extern-1@example (null extension)
-compat:a.b.@localhost.localdomain -> compat:intern-2@example (null extension)
-==== atdomain test
-external:plain1+ext@1.example -> external:plain2@2.example (+ext)
-external:plain2@2.example -> external:(not found) (null extension)
-external:plain3@3.example -> external:plain4@4.example (null extension)
-external:plain5@3.example -> external:plain6@6.example (null extension)
+inline:{"a@b"=extern-1@example,a@b=intern-1@example,a.b.=intern-2@example}
+internal:a@b@localhost.localdomain -external-first-> external:extern-1@example (null extension)
+internal:a.b.@localhost.localdomain -external-first-> external:intern-2@example (null extension)
+==== at_domain test
+inline:{plain1@1.example=plain2@2.example,@3.example=plain4@4.example,plain5@3.example=plain6@6.example}
+external:plain1+ext@1.example -external-> external:plain2@2.example (+ext)
+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
-external:plain1+ext@1.example -> external:plain2@2.example (+ext)
-external:plain2@2.example -> external:(not found) (null extension)
-external:plain3@3.example -> external:plain4@4.example (null extension)
-external:plain5@3.example -> external:plain6@6.example (null extension)
-==== atdomain for local domain
-external:ab@localhost.localdomain -> external:foo@example (null extension)
-external:cd@localhost.localdomain -> external:@bar.example (null extension)
-==== localat and domain test
-internal:ab@localhost.localdomain -> external:foo@example (null extension)
-internal:ab+ext@localhost.localdomain -> external:foo@example (+ext)
-internal:cd@localhost.localdomain -> external:@bar.example (null extension)
+inline:{plain1@1.example=plain2@2.example,3.example=plain4@4.example,plain5@3.example=plain6@6.example}
+external:plain1+ext@1.example -external-> external:plain2@2.example (+ext)
+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)
+==== at_domain for local domain
+inline:{ab=foo@example,@localhost.localdomain=@bar.example}
+external:ab@localhost.localdomain -external-> external:foo@example (null extension)
+external:cd@localhost.localdomain -external-> external:@bar.example (null extension)
+==== localpart_at_if_local and domain test
+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
-external:plain1+ext@1.example -> external:(not found) (null extension)
-external:foo@sub.example -> external:(not found) (null extension)
-external:foo@example -> external:example-result (null extension)
-external:foo@example -> external:example-result (null extension)
-external:foo@sub.example -> external:example-result (null extension)
-external:foo@sub.sub.example -> external:example-result (null extension)
-external:foo@example -> external:example-result (null extension)
-external:foo@sub.example -> external:dot-example-result (null extension)
-external:foo@sub.sub.example -> external:dot-example-result (null extension)
+inline:{example=example-result,.example=dot-example-result}
+external:plain1+ext@1.example -external-> external:(not found) (null extension)
+external:foo@sub.example -external-> external:(not found) (null extension)
+external:foo@example -external-> external:example-result (null extension)
+external:foo@example -external-> external:example-result (null extension)
+external:foo@sub.example -external-> external:example-result (null extension)
+external:foo@sub.sub.example -external-> external:example-result (null extension)
+external:foo@example -external-> external: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)
diff --git a/postfix/src/global/mail_addr_form.c b/postfix/src/global/mail_addr_form.c
index 8a1e94261..ba81ecdef 100644
--- a/postfix/src/global/mail_addr_form.c
+++ b/postfix/src/global/mail_addr_form.c
@@ -11,9 +11,9 @@
/* const char *mail_addr_form_to_string(int addr_form)
/* DESCRIPTION
/* mail_addr_form_from_string() converts a symbolic mail address
-/* form name ("internal", "external", "noconv") into the
-/* corresponding internal code. The result is -1 if an
-/* unrecognized name was specified.
+/* form name ("internal", "external", "internal-first") into the
+/* corresponding internal code. The result is -1 if an unrecognized
+/* name was specified.
/*
/* mail_addr_form_to_string() converts from internal code
/* to the corresponding symbolic name. The result is null if
@@ -47,7 +47,7 @@
static const NAME_CODE addr_form_table[] = {
"external", MAIL_ADDR_FORM_EXTERNAL,
"internal", MAIL_ADDR_FORM_INTERNAL,
- "noconv", MAIL_ADDR_FORM_NOCONV,
+ "external-first", MAIL_ADDR_FORM_EXTERNAL_FIRST,
0, -1,
};
diff --git a/postfix/src/global/mail_addr_form.h b/postfix/src/global/mail_addr_form.h
index f263ac511..a547e7877 100644
--- a/postfix/src/global/mail_addr_form.h
+++ b/postfix/src/global/mail_addr_form.h
@@ -12,12 +12,11 @@
/* .nf
/*
- * External interface. The MAIL_ADDR_FORM_NOCONV is for legacy code that
- * hasn't yet been converted to external-form address lookups.
+ * External interface.
*/
-#define MAIL_ADDR_FORM_NOCONV 0 /* do not convert */
#define MAIL_ADDR_FORM_INTERNAL 1 /* unquoted 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 const char *mail_addr_form_to_string(int);
diff --git a/postfix/src/global/mail_addr_map.c b/postfix/src/global/mail_addr_map.c
index 40b4b9bdc..7caa92466 100644
--- a/postfix/src/global/mail_addr_map.c
+++ b/postfix/src/global/mail_addr_map.c
@@ -11,11 +11,14 @@
/* const char *address;
/* 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;
/* const char *address;
/* int propagate;
-/* int how;
+/* int in_form;
+/* int query_form;
+/* int out_form;
/* DESCRIPTION
/* mail_addr_map_*() returns the translation for the named address,
/* or a null pointer if none is found.
@@ -35,18 +38,17 @@
/* form \fI@otherdomain\fR, the result is the original user in
/* \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:
/* .IP path
/* Dictionary search path (see maps(3)).
/* .IP address
/* The address to be looked up in external (quoted) form, or
/* 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 out_form
/* Input and output address forms, either MAIL_ADDR_FORM_INTERNAL
@@ -98,7 +100,7 @@
/* mail_addr_map - map a canonical address */
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;
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;
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
- * input to avoid an unnecessary input conversion in
- * 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.
+ * input to avoid unnecessary input conversion in mail_addr_find_opt().
*/
if (in_form == MAIL_ADDR_FORM_EXTERNAL) {
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
* 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,
- in_form, mid_form,
- MAIL_ADDR_FIND_DEFAULT)) != 0) {
+ in_form, query_form,
+ MAIL_ADDR_FORM_EXTERNAL,
+ MAF_STRATEGY_DEFAULT)) != 0) {
/*
* 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 == '@') {
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);
if (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));
- vstring_strcat(ext_address, string);
string = STR(ext_address);
}
@@ -164,7 +159,7 @@ ARGV *mail_addr_map_opt(MAPS *path, const char *address, int propagate,
* each address found.
*/
argv = mail_addr_crunch_opt(string, propagate ? extension : 0,
- mid_form, out_form);
+ MAIL_ADDR_FORM_EXTERNAL, out_form);
if (buffer)
vstring_free(buffer);
if (ext_address)
@@ -199,3 +194,334 @@ ARGV *mail_addr_map_opt(MAPS *path, const char *address, int propagate,
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
+#include
+#include
+#include
+#include
+
+/* Utility library. */
+
+#include
+#include
+#include
+#include
+
+/* Global library. */
+
+#include
+#include
+#include /* XXX eliminate main.cf dependency */
+#include
+
+/* 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
diff --git a/postfix/src/global/mail_addr_map.h b/postfix/src/global/mail_addr_map.h
index 945417b86..46bbebf97 100644
--- a/postfix/src/global/mail_addr_map.h
+++ b/postfix/src/global/mail_addr_map.h
@@ -25,12 +25,13 @@
/*
* 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. */
#define mail_addr_map_internal(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
/* .ad
diff --git a/postfix/src/global/mail_addr_map.ref b/postfix/src/global/mail_addr_map.ref
index c4b3f0535..28647b136 100644
--- a/postfix/src/global/mail_addr_map.ref
+++ b/postfix/src/global/mail_addr_map.ref
@@ -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: propagate = 1
unknown: delimiter = '+'
unknown: in_form = external
+unknown: query_form = external
unknown: out_form = external
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: database = inline:{ {"a a"@example.com="b b"@example.com} }
unknown: propagate = 1
unknown: delimiter = '+'
unknown: in_form = external
+unknown: query_form = external
unknown: out_form = external
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: database = inline:{ {"a a"@example.com="b b"@example.com} }
unknown: propagate = 1
unknown: delimiter = '+'
unknown: in_form = external
+unknown: query_form = external
unknown: out_form = external
unknown: address = "a a"@example.com
diff --git a/postfix/src/global/mail_addr_map_tester.c b/postfix/src/global/mail_addr_map_tester.c
deleted file mode 100644
index aae1a3bf5..000000000
--- a/postfix/src/global/mail_addr_map_tester.c
+++ /dev/null
@@ -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
-#include
-#include
-#include
-#include
-
-/* Utility library. */
-
-#include
-#include
-#include
-#include
-
-/* Global library. */
-
-#include
-#include
-#include /* XXX eliminate main.cf dependency */
-#include
-
-/* 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);
-}
diff --git a/postfix/src/global/mail_version.h b/postfix/src/global/mail_version.h
index 27b9895a9..6b81b8527 100644
--- a/postfix/src/global/mail_version.h
+++ b/postfix/src/global/mail_version.h
@@ -20,7 +20,7 @@
* Patches change both the patchlevel and the release date. Snapshots have no
* patchlevel; they change the release date only.
*/
-#define MAIL_RELEASE_DATE "20170117"
+#define MAIL_RELEASE_DATE "20170122"
#define MAIL_VERSION_NUMBER "3.2"
#ifdef SNAPSHOT
diff --git a/postfix/src/postmap/postmap.c b/postfix/src/postmap/postmap.c
index 453531acb..2cb433365 100644
--- a/postfix/src/postmap/postmap.c
+++ b/postfix/src/postmap/postmap.c
@@ -48,7 +48,7 @@
/* When the \fIkey\fR specifies email address information, the
/* localpart should be enclosed with double quotes if required
/* 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
/* the lookups case insensitive; as of Postfix 2.3 this case
@@ -64,7 +64,7 @@
/* .IP \fB-b\fR
/* Enable message body query mode. When reading lookup keys
/* 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.
/* .sp
/* By default, the \fB-b\fR option starts generating lookup
@@ -101,7 +101,7 @@
/* .IP \fB-h\fR
/* Enable message header query mode. When reading lookup keys
/* 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
/* header becomes one lookup key with one or more embedded
/* newline characters.
diff --git a/postfix/src/smtp/smtp_map11.c b/postfix/src/smtp/smtp_map11.c
index 71739b61e..d2088c30b 100644
--- a/postfix/src/smtp/smtp_map11.c
+++ b/postfix/src/smtp/smtp_map11.c
@@ -81,7 +81,9 @@ int smtp_map11_external(VSTRING *addr, MAPS *maps, int propagate)
const char *result;
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)
msg_warn("multi-valued %s result for %s", maps->title, STR(addr));
result = new_addr->argv[0];
diff --git a/postfix/src/smtpd/Makefile.in b/postfix/src/smtpd/Makefile.in
index 20bb96feb..f9811960b 100644
--- a/postfix/src/smtpd/Makefile.in
+++ b/postfix/src/smtpd/Makefile.in
@@ -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.
+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
$(SHLIB_ENV) $(VALGRIND) ../postmap/postmap hash:smtpd_check_access
$(SHLIB_ENV) $(VALGRIND) ./smtpd_check 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
$(SHLIB_ENV) $(VALGRIND) ./smtpd_check 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.*
smtpd_nullmx_test: smtpd_check smtpd_nullmx.in smtpd_nullmx.ref
$(SHLIB_ENV) $(VALGRIND) ../postmap/postmap hash:smtpd_check_access
$(SHLIB_ENV) $(VALGRIND) ./smtpd_check 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.*
smtpd_dns_filter_test: smtpd_check smtpd_dns_filter.in smtpd_dns_filter.ref \
diff --git a/postfix/src/smtpd/smtpd_check.c b/postfix/src/smtpd/smtpd_check.c
index c627d4fe1..9924afa7e 100644
--- a/postfix/src/smtpd/smtpd_check.c
+++ b/postfix/src/smtpd/smtpd_check.c
@@ -653,6 +653,8 @@ static ARGV *smtpd_check_parse(int flags, const char *checks)
return (argv);
}
+#ifndef TEST
+
/* has_required - make sure required restriction is present */
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));
}
+#endif
+
/* smtpd_check_init - initialize once during process lifetime */
void smtpd_check_init(void)
@@ -719,6 +723,7 @@ void smtpd_check_init(void)
const char *name;
const char *value;
char *cp;
+#ifndef TEST
static const char *rcpt_required[] = {
REJECT_UNAUTH_DEST,
DEFER_UNAUTH_DEST,
@@ -728,6 +733,7 @@ void smtpd_check_init(void)
CHECK_RELAY_DOMAINS,
0,
};
+#endif
static NAME_CODE tempfail_actions[] = {
DEFER_ALL, DEFER_ALL_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
* otherwise.
*/
-#define LOOKUP_STRATEGY (MAIL_ADDR_FIND_FULL | MAIL_ADDR_FIND_NOEXT \
- | MAIL_ADDR_FIND_DOMAIN | MAIL_ADDR_FIND_PMS \
- | MAIL_ADDR_FIND_LOCALPART_AT)
+#define LOOKUP_STRATEGY (MAF_STRATEGY_FULL | MAF_STRATEGY_NOEXT \
+ | MAF_STRATEGY_DOMAIN | MAF_STRATEGY_PMS \
+ | MAF_STRATEGY_LOCALPART_AT)
if ((maps = (MAPS *) htable_find(map_command_table, table)) == 0) {
msg_warn("%s: unexpected dictionary: %s", myname, table);
@@ -5893,7 +5899,6 @@ int main(int argc, char **argv)
char *bp;
char *resp;
char *addr;
- INET_PROTO_INFO *proto_info;
/*
* Initialization. Use dummies for client information.
@@ -5905,7 +5910,7 @@ int main(int argc, char **argv)
int_init();
smtpd_check_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");
state.queue_id = "";
diff --git a/postfix/src/trivial-rewrite/transport.c b/postfix/src/trivial-rewrite/transport.c
index d4d5dbbc5..4fa9082f1 100644
--- a/postfix/src/trivial-rewrite/transport.c
+++ b/postfix/src/trivial-rewrite/transport.c
@@ -261,9 +261,9 @@ int transport_lookup(TRANSPORT_INFO *tp, const char *addr,
* internal form.
*/
#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 ? \
- MAIL_ADDR_FIND_PMS : MAIL_ADDR_FIND_PMDS))
+ MAF_STRATEGY_PMS : MAF_STRATEGY_PMDS))
if ((ratsign = strrchr(addr, '@')) == 0 || ratsign[1] == 0)
msg_panic("transport_lookup: bad address: \"%s\"", addr);
@@ -427,7 +427,7 @@ int main(int argc, char **argv)
vstring_free(nexthop);
vstring_free(channel);
vstring_free(buffer);
- exit(0);
+ exit(errs != 0);
}
#endif