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.
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.
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.
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:
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>
<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>
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>
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>
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>
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
.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
.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.
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 -- "
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 -- "
*/
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)
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
$(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
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
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
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
/* 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
/* 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;
/* 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
/* 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
#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)
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";
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)
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;
/* 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;
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);
}
/*
* 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;
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;
* 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)
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
/*
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;
usage(argv[0]);
}
}
- if (argc != optind + 1)
+ if (argc != optind)
usage(argv[0]);
/*
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");
* 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);
-#ifndef _MAIL_ADDR_FIND_H_INCLUDED_
-#define _MAIL_ADDR_FIND_H_INCLUDED_
+#ifndef _MAF_STRATEGY_H_INCLUDED_
+#define _MAF_STRATEGY_H_INCLUDED_
/*++
/* NAME
/*
* 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
# 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
==== 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)
/* 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
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,
};
/* .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);
/* 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.
/* 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
/* 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";
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);
/*
* 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);
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);
}
* 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)
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
/*
* 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
-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
+++ /dev/null
-/*++
-/* 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);
-}
* 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
/* 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
/* .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
/* .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.
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];
# 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
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 \
return (argv);
}
+#ifndef TEST
+
/* has_required - make sure required restriction is present */
static int has_required(ARGV *restrictions, const char **required)
name, STR(example));
}
+#endif
+
/* smtpd_check_init - initialize once during process lifetime */
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,
CHECK_RELAY_DOMAINS,
0,
};
+#endif
static NAME_CODE tempfail_actions[] = {
DEFER_ALL, DEFER_ALL_ACT,
DEFER_IF_PERMIT, DEFER_IF_PERMIT_ACT,
* 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);
char *bp;
char *resp;
char *addr;
- INET_PROTO_INFO *proto_info;
/*
* Initialization. Use dummies for client information.
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>";
* 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);
vstring_free(nexthop);
vstring_free(channel);
vstring_free(buffer);
- exit(0);
+ exit(errs != 0);
}
#endif