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

postfix-3.2-20170122

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

View File

@@ -22845,3 +22845,21 @@ Apologies for any names omitted.
compatibility, for check_sender_access and check_recipient_access.
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.

View File

@@ -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.

View File

@@ -46,127 +46,128 @@ POSTMAP(1) POSTMAP(1)
When the <i>key</i> specifies email address information, the localpart should
be enclosed with double quotes if required by <a href="http://tools.ietf.org/html/rfc5322">RFC 5322</a>. For example, an
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 <a href="DATABASE_README.html#types">btree</a>:, <a href="DATABASE_README.html#types">dbm</a>: or
<a href="DATABASE_README.html#types">hash</a>:. 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
<a href="regexp_table.5.html">regexp</a>: and <a href="pcre_table.5.html">pcre</a>:. This resulted in loss of information with $<i>number</i>
where a lookup field can match both upper and lower case text, such as
<a href="regexp_table.5.html">regexp</a>: and <a href="pcre_table.5.html">pcre</a>:. This resulted in loss of information with $<i>number</i>
substitutions.
<b>COMMAND-LINE ARGUMENTS</b>
<b>-b</b> Enable message body query mode. When reading lookup keys from
standard input with "<b>-q -</b>", process the input as if it is an
email message in <a href="http://tools.ietf.org/html/rfc2822">RFC 2822</a> format. Each line of body content
<b>-b</b> Enable message body query mode. When reading lookup keys from
standard input with "<b>-q -</b>", process the input as if it is an
email message in <a href="http://tools.ietf.org/html/rfc5322">RFC 5322</a> format. Each line of body content
becomes one lookup key.
By default, the <b>-b</b> option starts generating lookup keys at the
first non-header line, and stops when the end of the message is
reached. To simulate <a href="header_checks.5.html"><b>body_checks</b>(5)</a> processing, enable MIME
parsing with <b>-m</b>. With this, the <b>-b</b> option generates no
body-style lookup keys for attachment MIME headers and for
By default, the <b>-b</b> option starts generating lookup keys at the
first non-header line, and stops when the end of the message is
reached. To simulate <a href="header_checks.5.html"><b>body_checks</b>(5)</a> processing, enable MIME
parsing with <b>-m</b>. With this, the <b>-b</b> option generates no
body-style lookup keys for attachment MIME headers and for
attached message/* headers.
NOTE: with "<a href="postconf.5.html#smtputf8_enable">smtputf8_enable</a> = yes", the <b>-b</b> option option dis-
ables UTF-8 syntax checks on query keys and lookup results.
NOTE: with "<a href="postconf.5.html#smtputf8_enable">smtputf8_enable</a> = yes", the <b>-b</b> option option dis-
ables UTF-8 syntax checks on query keys and lookup results.
Specify the <b>-U</b> option to force UTF-8 syntax checks anyway.
This feature is available in Postfix version 2.6 and later.
<b>-c</b> <i>config</i><b>_</b><i>dir</i>
Read the <a href="postconf.5.html"><b>main.cf</b></a> configuration file in the named directory
Read the <a href="postconf.5.html"><b>main.cf</b></a> configuration file in the named directory
instead of the default configuration directory.
<b>-d</b> <i>key</i> Search the specified maps for <i>key</i> and remove one entry per map.
The exit status is zero when the requested information was
<b>-d</b> <i>key</i> Search the specified maps for <i>key</i> and remove one entry per map.
The exit status is zero when the requested information was
found.
If a key value of <b>-</b> is specified, the program reads key values
from the standard input stream. The exit status is zero when at
If a key value of <b>-</b> 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.
<b>-f</b> Do not fold the lookup key to lower case while creating or
<b>-f</b> 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.
<b>-h</b> Enable message header query mode. When reading lookup keys from
standard input with "<b>-q -</b>", process the input as if it is an
email message in <a href="http://tools.ietf.org/html/rfc2822">RFC 2822</a> format. Each logical header line
becomes one lookup key. A multi-line header becomes one lookup
<b>-h</b> Enable message header query mode. When reading lookup keys from
standard input with "<b>-q -</b>", process the input as if it is an
email message in <a href="http://tools.ietf.org/html/rfc5322">RFC 5322</a> format. Each logical header line
becomes one lookup key. A multi-line header becomes one lookup
key with one or more embedded newline characters.
By default, the <b>-h</b> option generates lookup keys until the first
non-header line is reached. To simulate <a href="header_checks.5.html"><b>header_checks</b>(5)</a> pro-
cessing, enable MIME parsing with <b>-m</b>. With this, the <b>-h</b> option
also generates header-style lookup keys for attachment MIME
By default, the <b>-h</b> option generates lookup keys until the first
non-header line is reached. To simulate <a href="header_checks.5.html"><b>header_checks</b>(5)</a> pro-
cessing, enable MIME parsing with <b>-m</b>. With this, the <b>-h</b> option
also generates header-style lookup keys for attachment MIME
headers and for attached message/* headers.
NOTE: with "<a href="postconf.5.html#smtputf8_enable">smtputf8_enable</a> = yes", the <b>-b</b> option option dis-
ables UTF-8 syntax checks on query keys and lookup results.
NOTE: with "<a href="postconf.5.html#smtputf8_enable">smtputf8_enable</a> = yes", the <b>-b</b> option option dis-
ables UTF-8 syntax checks on query keys and lookup results.
Specify the <b>-U</b> option to force UTF-8 syntax checks anyway.
This feature is available in Postfix version 2.6 and later.
<b>-i</b> Incremental mode. Read entries from standard input and do not
truncate an existing database. By default, <a href="postmap.1.html"><b>postmap</b>(1)</a> creates a
<b>-i</b> Incremental mode. Read entries from standard input and do not
truncate an existing database. By default, <a href="postmap.1.html"><b>postmap</b>(1)</a> creates a
new database from the entries in <b>file_name</b>.
<b>-m</b> Enable MIME parsing with "<b>-b</b>" and "<b>-h</b>".
This feature is available in Postfix version 2.6 and later.
<b>-N</b> Include the terminating null character that terminates lookup
keys and values. By default, <a href="postmap.1.html"><b>postmap</b>(1)</a> does whatever is the
<b>-N</b> Include the terminating null character that terminates lookup
keys and values. By default, <a href="postmap.1.html"><b>postmap</b>(1)</a> does whatever is the
default for the host operating system.
<b>-n</b> Don't include the terminating null character that terminates
lookup keys and values. By default, <a href="postmap.1.html"><b>postmap</b>(1)</a> does whatever is
<b>-n</b> Don't include the terminating null character that terminates
lookup keys and values. By default, <a href="postmap.1.html"><b>postmap</b>(1)</a> does whatever is
the default for the host operating system.
<b>-o</b> Do not release root privileges when processing a non-root input
file. By default, <a href="postmap.1.html"><b>postmap</b>(1)</a> drops root privileges and runs as
<b>-o</b> Do not release root privileges when processing a non-root input
file. By default, <a href="postmap.1.html"><b>postmap</b>(1)</a> drops root privileges and runs as
the source file owner instead.
<b>-p</b> Do not inherit the file access permissions from the input file
when creating a new file. Instead, create a new file with
<b>-p</b> 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).
<b>-q</b> <i>key</i> Search the specified maps for <i>key</i> and write the first value
found to the standard output stream. The exit status is zero
<b>-q</b> <i>key</i> Search the specified maps for <i>key</i> 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 <b>-</b> is specified, the program reads key values
from the standard input stream and writes one line of <i>key value</i>
If a key value of <b>-</b> is specified, the program reads key values
from the standard input stream and writes one line of <i>key value</i>
output for each key that was found. The exit status is zero when
at least one of the requested keys was found.
<b>-r</b> When updating a table, do not complain about attempts to update
<b>-r</b> When updating a table, do not complain about attempts to update
existing entries, and make those updates anyway.
<b>-s</b> Retrieve all database elements, and write one line of <i>key value</i>
output for each element. The elements are printed in database
order, which is not necessarily the same as the original input
<b>-s</b> Retrieve all database elements, and write one line of <i>key value</i>
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.
<b>-u</b> Disable UTF-8 support. UTF-8 support is enabled by default when
"<a href="postconf.5.html#smtputf8_enable">smtputf8_enable</a> = yes". It requires that keys and values are
<b>-u</b> Disable UTF-8 support. UTF-8 support is enabled by default when
"<a href="postconf.5.html#smtputf8_enable">smtputf8_enable</a> = yes". It requires that keys and values are
valid UTF-8 strings.
<b>-U</b> With "<a href="postconf.5.html#smtputf8_enable">smtputf8_enable</a> = yes", force UTF-8 syntax checks with the
<b>-b</b> and <b>-h</b> options.
<b>-v</b> Enable verbose logging for debugging purposes. Multiple <b>-v</b>
<b>-v</b> Enable verbose logging for debugging purposes. Multiple <b>-v</b>
options make the software increasingly verbose.
<b>-w</b> When updating a table, do not complain about attempts to update
<b>-w</b> 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 <a href="postmap.1.html"><b>postmap</b>(1)</a> command can query any supported file type, but it
can create only the following file types:
<b>btree</b> The output file is a btree file, named <i>file</i><b>_</b><i>name</i><b>.db</b>.
This is available on systems with support for <b>db</b> data-
<b>btree</b> The output file is a btree file, named <i>file</i><b>_</b><i>name</i><b>.db</b>.
This is available on systems with support for <b>db</b> data-
bases.
<b>cdb</b> The output consists of one file, named <i>file</i><b>_</b><i>name</i><b>.cdb</b>.
This is available on systems with support for <b>cdb</b> data-
<b>cdb</b> The output consists of one file, named <i>file</i><b>_</b><i>name</i><b>.cdb</b>.
This is available on systems with support for <b>cdb</b> data-
bases.
<b>dbm</b> The output consists of two files, named <i>file</i><b>_</b><i>name</i><b>.pag</b> and
<i>file</i><b>_</b><i>name</i><b>.dir</b>. This is available on systems with support
for <b>dbm</b> databases.
<b>hash</b> The output file is a hashed file, named <i>file</i><b>_</b><i>name</i><b>.db</b>.
This is available on systems with support for <b>db</b> data-
<b>hash</b> The output file is a hashed file, named <i>file</i><b>_</b><i>name</i><b>.db</b>.
This is available on systems with support for <b>db</b> data-
bases.
<b>fail</b> A table that reliably fails all requests. The lookup ta-
ble name is used for logging only. This table exists to
<b>fail</b> 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.
<b>sdbm</b> The output consists of two files, named <i>file</i><b>_</b><i>name</i><b>.pag</b> and
<i>file</i><b>_</b><i>name</i><b>.dir</b>. This is available on systems with support
for <b>sdbm</b> databases.
When no <i>file</i><b>_</b><i>type</i> is specified, the software uses the database
type specified via the <b><a href="postconf.5.html#default_database_type">default_database_type</a></b> configuration
When no <i>file</i><b>_</b><i>type</i> is specified, the software uses the database
type specified via the <b><a href="postconf.5.html#default_database_type">default_database_type</a></b> configuration
parameter.
<i>file</i><b>_</b><i>name</i>
@@ -212,11 +213,11 @@ POSTMAP(1) POSTMAP(1)
<b>DIAGNOSTICS</b>
Problems are logged to the standard error stream and to <b>syslogd</b>(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.
<a href="postmap.1.html"><b>postmap</b>(1)</a> terminates with zero exit status in case of success (includ-
ing successful "<b>postmap -q</b>" lookup) and terminates with non-zero exit
ing successful "<b>postmap -q</b>" lookup) and terminates with non-zero exit
status in case of failure.
<b>ENVIRONMENT</b>
@@ -227,12 +228,12 @@ POSTMAP(1) POSTMAP(1)
Enable verbose logging for debugging purposes.
<b>CONFIGURATION PARAMETERS</b>
The following <a href="postconf.5.html"><b>main.cf</b></a> parameters are especially relevant to this pro-
gram. The text below provides only a parameter summary. See <a href="postconf.5.html"><b>post-</b></a>
The following <a href="postconf.5.html"><b>main.cf</b></a> parameters are especially relevant to this pro-
gram. The text below provides only a parameter summary. See <a href="postconf.5.html"><b>post-</b></a>
<a href="postconf.5.html"><b>conf</b>(5)</a> for more details including examples.
<b><a href="postconf.5.html#berkeley_db_create_buffer_size">berkeley_db_create_buffer_size</a> (16777216)</b>
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.
<b><a href="postconf.5.html#berkeley_db_read_buffer_size">berkeley_db_read_buffer_size</a> (131072)</b>
@@ -240,7 +241,7 @@ POSTMAP(1) POSTMAP(1)
hash or btree tables.
<b><a href="postconf.5.html#config_directory">config_directory</a> (see 'postconf -d' output)</b>
The default location of the Postfix <a href="postconf.5.html">main.cf</a> and <a href="master.5.html">master.cf</a> con-
The default location of the Postfix <a href="postconf.5.html">main.cf</a> and <a href="master.5.html">master.cf</a> con-
figuration files.
<b><a href="postconf.5.html#default_database_type">default_database_type</a> (see 'postconf -d' output)</b>
@@ -248,14 +249,14 @@ POSTMAP(1) POSTMAP(1)
and <a href="postmap.1.html"><b>postmap</b>(1)</a> commands.
<b><a href="postconf.5.html#smtputf8_enable">smtputf8_enable</a> (yes)</b>
Enable preliminary SMTPUTF8 support for the protocols described
Enable preliminary SMTPUTF8 support for the protocols described
in <a href="http://tools.ietf.org/html/rfc6531">RFC 6531</a>..6533.
<b><a href="postconf.5.html#syslog_facility">syslog_facility</a> (mail)</b>
The syslog facility of Postfix logging.
<b><a href="postconf.5.html#syslog_name">syslog_name</a> (see 'postconf -d' output)</b>
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".
<b>SEE ALSO</b>

View File

@@ -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.

View File

@@ -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 -- "

View File

@@ -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)

View File

@@ -118,7 +118,7 @@ TESTPROG= domain_list dot_lockfile mail_addr_crunch mail_addr_find \
valid_mailhost_addr own_inet_addr header_body_checks \
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

View File

@@ -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);

View File

@@ -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

View File

@@ -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

View File

@@ -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)

View File

@@ -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,
};

View File

@@ -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);

View File

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

View File

@@ -25,12 +25,13 @@
/*
* External interface.
*/
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

View File

@@ -1,23 +1,26 @@
unknown: warning: fail test selftest 1 external to external, no extension, quoted: expect[0]='"bXb"@example.com', result[0]='"b b"@example.com'
unknown: warning: fail test selftest 1 external -external-> external, no extension, quoted: expect[0]='"bXb"@example.com', result[0]='"b b"@example.com'
unknown: database = inline:{ {"a a"@example.com="b b"@example.com} }
unknown: 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

View File

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

View File

@@ -20,7 +20,7 @@
* Patches change both the patchlevel and the release date. Snapshots have no
* 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

View File

@@ -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.

View File

@@ -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];

View File

@@ -114,6 +114,8 @@ smtpd_addr_valid_test: smtpd_check smtpd_addr_valid.in smtpd_addr_valid.ref
# This requires that the DNS server can query porcupine.org.
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.in >smtpd_exp.tmp 2>&1
@@ -122,13 +124,13 @@ smtpd_exp_test: smtpd_check smtpd_exp.in smtpd_exp.ref
smtpd_server_test: smtpd_check smtpd_server.in smtpd_server.ref
$(SHLIB_ENV) $(VALGRIND) ./smtpd_check <smtpd_server.in >smtpd_server.tmp 2>&1
diff smtpd_server.ref smtpd_server.tmp
$(ADDRINFO_FIX) smtpd_server.tmp | diff smtpd_server.ref -
rm -f smtpd_server.tmp smtpd_check_access.*
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.in >smtpd_nullmx.tmp 2>&1
diff smtpd_nullmx.ref smtpd_nullmx.tmp
$(ADDRINFO_FIX) smtpd_nullmx.tmp | diff smtpd_nullmx.ref -
rm -f smtpd_nullmx.tmp smtpd_check_access.*
smtpd_dns_filter_test: smtpd_check smtpd_dns_filter.in smtpd_dns_filter.ref \

View File

@@ -653,6 +653,8 @@ static ARGV *smtpd_check_parse(int flags, const char *checks)
return (argv);
}
#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 = "<queue id>";

View File

@@ -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