Documentation: in smtpd.c, the comment that justifies the
454 reply for "TLS unavailable" cited the wrong RFC.
+
+20130404
+
+ Human factors: warning when a main.cf parameter has multiple
+ entries with different values. File: util/dict.c.
+
+20130405
+
+ Feature: the recipient_delimiter parameter can now specify
+ a set of characters. A user name is now separated from its
+ address extension by the first character that matches the
+ recipient_delimiter set. Files: proto/postconf.proto,
+ src/global/mail_addr_find.c, src/global/mail_params.c,
+ src/global/split_addr.c, src/global/split_addr.h,
+ src/global/strip_addr.c, src/global/strip_addr.h,
+ src/global/strip_addr.ref, src/local/bounce_workaround.c,
+ src/local/local.c, src/local/local_expand.c, src/local/recipient.c,
+ src/local/resolve.c, src/oqmgr/qmgr_message.c, src/pipe/pipe.c,
+ src/qmgr/qmgr_message.c, src/smtpd/smtpd.c,
+ src/smtpd/smtpd_check.c, src/trivial-rewrite/transport.c,
+ src/trivial-rewrite/trivial-rewrite.c.
If you upgrade from Postfix 2.9 or earlier, read RELEASE_NOTES-2.10
before proceeding.
+Major changes with snapshot 20130405
+====================================
+
+The recipient_delimiter parameter can now specify a set of characters.
+A user name is now separated from its address extension by the first
+character that matches the recipient_delimiter set.
+
+For example, specify "recipient_delimiter = +-" to support both the
+Postfix-style "+" and the qmail-style "-" extension delimiter.
+
+As before, this implementation recognizes one delimiter character
+per email address, and one address extension per email address.
+
Major changes with snapshot 20130319
====================================
tory.
<b><a href="postconf.5.html#recipient_delimiter">recipient_delimiter</a> (empty)</b>
- The separator between user names and address exten-
- sions (user+foo).
+ The set of characters that can separate a user name
+ from its address extension (user+foo).
<b><a href="postconf.5.html#require_home_directory">require_home_directory</a> (no)</b>
Require that a <a href="local.8.html"><b>local</b>(8)</a> recipient's home directory
tory.
<b><a href="postconf.5.html#recipient_delimiter">recipient_delimiter</a> (empty)</b>
- The separator between user names and address exten-
- sions (user+foo).
+ The set of characters that can separate a user name
+ from its address extension (user+foo).
<b><a href="postconf.5.html#syslog_facility">syslog_facility</a> (mail)</b>
The syslog facility of Postfix logging.
<dt><b>$<a href="postconf.5.html#recipient_delimiter">recipient_delimiter</a></b></dt>
-<dd>The system-wide recipient address extension delimiter. </dd>
+<dd>The address extension delimiter that was found in the recipient
+address (Postfix 2.11 and later), or the system-wide recipient
+address extension delimiter (Postfix 2.10 and earlier). </dd>
<dt><b>${name?value}</b></dt>
<dt><b>$<a href="postconf.5.html#recipient_delimiter">recipient_delimiter</a></b></dt>
-<dd>The system-wide recipient address extension delimiter. </dd>
+<dd>The address extension delimiter that was found in the recipient
+address (Postfix 2.11 and later), or the system-wide recipient
+address extension delimiter (Postfix 2.10 and earlier). </dd>
<dt><b>${name?value}</b></dt>
<dt><b>$<a href="postconf.5.html#recipient_delimiter">recipient_delimiter</a></b></dt>
-<dd>The system-wide recipient address extension delimiter. </dd>
+<dd>The address extension delimiter that was found in the recipient
+address (Postfix 2.11 and later), or the system-wide recipient
+address extension delimiter (Postfix 2.10 and earlier). </dd>
<dt><b>$shell</b></dt>
<DT><b><a name="recipient_delimiter">recipient_delimiter</a>
(default: empty)</b></DT><DD>
-<p>
-The separator between user names and address extensions (user+foo).
-See <a href="canonical.5.html">canonical(5)</a>, <a href="local.8.html">local(8)</a>, <a href="relocated.5.html">relocated(5)</a> and <a href="virtual.5.html">virtual(5)</a> for the
-effects this has on aliases, canonical, virtual, relocated and
-on .forward file lookups. Basically, the software tries user+foo
-and .forward+foo before trying user and .forward.
+<p> The set of characters that can separate a user name from its
+address extension (user+foo). See <a href="canonical.5.html">canonical(5)</a>, <a href="local.8.html">local(8)</a>, <a href="relocated.5.html">relocated(5)</a>
+and <a href="virtual.5.html">virtual(5)</a> for the effects this has on aliases, canonical,
+virtual, and relocated lookups. Basically, the software tries
+user+foo and .forward+foo before trying user and .forward. </p>
+
+<p> This implementation recognizes one delimiter character per email
+address, and one address extension per email address. </p>
+
+<p> When the <a href="postconf.5.html#recipient_delimiter">recipient_delimiter</a> set contains multiple characters
+(Postfix 2.11 and later), a user name is separated from its address
+extension by the first character that matches the <a href="postconf.5.html#recipient_delimiter">recipient_delimiter</a>
+set. </p>
+
+<p> When used in <a href="postconf.5.html#forward_path">forward_path</a>, ${<a href="postconf.5.html#recipient_delimiter">recipient_delimiter</a>} is replaced
+with the recipient delimiter that was found in the recipient email
+address (Postfix 2.11 and later), or it is replaced with the <a href="postconf.5.html">main.cf</a>
+<a href="postconf.5.html#recipient_delimiter">recipient_delimiter</a> parameter value (Postfix 2.10 and earlier).
</p>
+<p> The <a href="postconf.5.html#recipient_delimiter">recipient_delimiter</a> is not applied to the mailer-daemon
+address, the postmaster address, or the double-bounce address. With
+the default "<a href="postconf.5.html#owner_request_special">owner_request_special</a> = yes" setting, the <a href="postconf.5.html#recipient_delimiter">recipient_delimiter</a>
+is also not applied to addresses with the special "owner-" prefix
+or the special "-request" suffix. </p>
+
<p>
-Example:
+Examples:
</p>
<pre>
+# Handle Postfix-style extensions.
<a href="postconf.5.html#recipient_delimiter">recipient_delimiter</a> = +
</pre>
+<pre>
+# Handle both Postfix and qmail extensions (Postfix 2.11 and later).
+recipient_delimiters = +-
+</pre>
+
+<pre>
+# Use .forward for mail without address extension, and for mail with
+# an unrecognized address extension.
+<a href="postconf.5.html#forward_path">forward_path</a> = $home/.forward${<a href="postconf.5.html#recipient_delimiter">recipient_delimiter</a>}${extension},
+ $home/.forward,
+</pre>
+
</DD>
tory.
<b><a href="postconf.5.html#recipient_delimiter">recipient_delimiter</a> (empty)</b>
- The separator between user names and address exten-
- sions (user+foo).
+ The set of characters that can separate a user name
+ from its address extension (user+foo).
<b><a href="postconf.5.html#smtpd_banner">smtpd_banner</a> ($<a href="postconf.5.html#myhostname">myhostname</a> ESMTP $<a href="postconf.5.html#mail_name">mail_name</a>)</b>
The text that follows the 220 status code in the
information.
<b><a href="postconf.5.html#recipient_delimiter">recipient_delimiter</a> (empty)</b>
- The separator between user names and address exten-
- sions (user+foo).
+ The set of characters that can separate a user name
+ from its address extension (user+foo).
<b><a href="postconf.5.html#swap_bangpath">swap_bangpath</a> (yes)</b>
Enable the rewriting of "site!user" into
The entire recipient localpart.
.br
.IP "\fB$recipient_delimiter\fR"
-The system-wide recipient address extension delimiter.
+The address extension delimiter that was found in the recipient
+address (Postfix 2.11 and later), or the system-wide recipient
+address extension delimiter (Postfix 2.10 and earlier).
.br
.IP "\fB${name?value}\fR"
Expands to \fIvalue\fR when \fI$name\fR is non-empty.
The entire recipient localpart.
.br
.IP "\fB$recipient_delimiter\fR"
-The system-wide recipient address extension delimiter.
+The address extension delimiter that was found in the recipient
+address (Postfix 2.11 and later), or the system-wide recipient
+address extension delimiter (Postfix 2.10 and earlier).
.br
.IP "\fB${name?value}\fR"
Expands to \fIvalue\fR when \fI$name\fR is non-empty.
The full recipient address.
.br
.IP "\fB$recipient_delimiter\fR"
-The system-wide recipient address extension delimiter.
+The address extension delimiter that was found in the recipient
+address (Postfix 2.11 and later), or the system-wide recipient
+address extension delimiter (Postfix 2.10 and earlier).
.br
.IP "\fB$shell\fR"
The recipient's login shell.
.ad
.ft R
.SH recipient_delimiter (default: empty)
-The separator between user names and address extensions (user+foo).
-See \fBcanonical\fR(5), \fBlocal\fR(8), \fBrelocated\fR(5) and \fBvirtual\fR(5) for the
-effects this has on aliases, canonical, virtual, relocated and
-on .forward file lookups. Basically, the software tries user+foo
-and .forward+foo before trying user and .forward.
+The set of characters that can separate a user name from its
+address extension (user+foo). See \fBcanonical\fR(5), \fBlocal\fR(8), \fBrelocated\fR(5)
+and \fBvirtual\fR(5) for the effects this has on aliases, canonical,
+virtual, and relocated lookups. Basically, the software tries
+user+foo and .forward+foo before trying user and .forward.
+.PP
+This implementation recognizes one delimiter character per email
+address, and one address extension per email address.
+.PP
+When the recipient_delimiter set contains multiple characters
+(Postfix 2.11 and later), a user name is separated from its address
+extension by the first character that matches the recipient_delimiter
+set.
+.PP
+When used in forward_path, ${recipient_delimiter} is replaced
+with the recipient delimiter that was found in the recipient email
+address (Postfix 2.11 and later), or it is replaced with the main.cf
+recipient_delimiter parameter value (Postfix 2.10 and earlier).
+.PP
+The recipient_delimiter is not applied to the mailer-daemon
+address, the postmaster address, or the double-bounce address. With
+the default "owner_request_special = yes" setting, the recipient_delimiter
+is also not applied to addresses with the special "owner-" prefix
+or the special "-request" suffix.
.PP
-Example:
+Examples:
.PP
.nf
.na
.ft C
+# Handle Postfix-style extensions.
recipient_delimiter = +
.fi
.ad
.ft R
+.PP
+.nf
+.na
+.ft C
+# Handle both Postfix and qmail extensions (Postfix 2.11 and later).
+recipient_delimiters = +-
+.fi
+.ad
+.ft R
+.PP
+.nf
+.na
+.ft C
+# Use .forward for mail without address extension, and for mail with
+# an unrecognized address extension.
+forward_path = $home/.forward${recipient_delimiter}${extension},
+ $home/.forward,
+.fi
+.ad
+.ft R
.SH reject_code (default: 554)
The numerical Postfix SMTP server response code when a remote SMTP
client request is rejected by the "reject" restriction.
.IP "\fBqueue_directory (see 'postconf -d' output)\fR"
The location of the Postfix top-level queue directory.
.IP "\fBrecipient_delimiter (empty)\fR"
-The separator between user names and address extensions (user+foo).
+The set of characters that can separate a user name from its
+address extension (user+foo).
.IP "\fBrequire_home_directory (no)\fR"
Require that a \fBlocal\fR(8) recipient's home directory exists
before mail delivery is attempted.
.IP "\fBqueue_directory (see 'postconf -d' output)\fR"
The location of the Postfix top-level queue directory.
.IP "\fBrecipient_delimiter (empty)\fR"
-The separator between user names and address extensions (user+foo).
+The set of characters that can separate a user name from its
+address extension (user+foo).
.IP "\fBsyslog_facility (mail)\fR"
The syslog facility of Postfix logging.
.IP "\fBsyslog_name (see 'postconf -d' output)\fR"
.IP "\fBqueue_directory (see 'postconf -d' output)\fR"
The location of the Postfix top-level queue directory.
.IP "\fBrecipient_delimiter (empty)\fR"
-The separator between user names and address extensions (user+foo).
+The set of characters that can separate a user name from its
+address extension (user+foo).
.IP "\fBsmtpd_banner ($myhostname ESMTP $mail_name)\fR"
The text that follows the 220 status code in the SMTP greeting
banner.
With locally submitted mail, append the string ".$mydomain" to
addresses that have no ".domain" information.
.IP "\fBrecipient_delimiter (empty)\fR"
-The separator between user names and address extensions (user+foo).
+The set of characters that can separate a user name from its
+address extension (user+foo).
.IP "\fBswap_bangpath (yes)\fR"
Enable the rewriting of "site!user" into "user@site".
.PP
<dt><b>$recipient_delimiter</b></dt>
-<dd>The system-wide recipient address extension delimiter. </dd>
+<dd>The address extension delimiter that was found in the recipient
+address (Postfix 2.11 and later), or the system-wide recipient
+address extension delimiter (Postfix 2.10 and earlier). </dd>
<dt><b>${name?value}</b></dt>
<dt><b>$recipient_delimiter</b></dt>
-<dd>The system-wide recipient address extension delimiter. </dd>
+<dd>The address extension delimiter that was found in the recipient
+address (Postfix 2.11 and later), or the system-wide recipient
+address extension delimiter (Postfix 2.10 and earlier). </dd>
<dt><b>${name?value}</b></dt>
<dt><b>$recipient_delimiter</b></dt>
-<dd>The system-wide recipient address extension delimiter. </dd>
+<dd>The address extension delimiter that was found in the recipient
+address (Postfix 2.11 and later), or the system-wide recipient
+address extension delimiter (Postfix 2.10 and earlier). </dd>
<dt><b>$shell</b></dt>
recipient_canonical_maps = hash:/etc/postfix/recipient_canonical
</pre>
-%PARAM recipient_delimiter
+%PARAM recipient_delimiter
-<p>
-The separator between user names and address extensions (user+foo).
-See canonical(5), local(8), relocated(5) and virtual(5) for the
-effects this has on aliases, canonical, virtual, relocated and
-on .forward file lookups. Basically, the software tries user+foo
-and .forward+foo before trying user and .forward.
+<p> The set of characters that can separate a user name from its
+address extension (user+foo). See canonical(5), local(8), relocated(5)
+and virtual(5) for the effects this has on aliases, canonical,
+virtual, and relocated lookups. Basically, the software tries
+user+foo and .forward+foo before trying user and .forward. </p>
+
+<p> This implementation recognizes one delimiter character per email
+address, and one address extension per email address. </p>
+
+<p> When the recipient_delimiter set contains multiple characters
+(Postfix 2.11 and later), a user name is separated from its address
+extension by the first character that matches the recipient_delimiter
+set. </p>
+
+<p> When used in forward_path, ${recipient_delimiter} is replaced
+with the recipient delimiter that was found in the recipient email
+address (Postfix 2.11 and later), or it is replaced with the main.cf
+recipient_delimiter parameter value (Postfix 2.10 and earlier).
</p>
+<p> The recipient_delimiter is not applied to the mailer-daemon
+address, the postmaster address, or the double-bounce address. With
+the default "owner_request_special = yes" setting, the recipient_delimiter
+is also not applied to addresses with the special "owner-" prefix
+or the special "-request" suffix. </p>
+
<p>
-Example:
+Examples:
</p>
-
+
<pre>
+# Handle Postfix-style extensions.
recipient_delimiter = +
</pre>
+<pre>
+# Handle both Postfix and qmail extensions (Postfix 2.11 and later).
+recipient_delimiters = +-
+</pre>
+
+<pre>
+# Use .forward for mail without address extension, and for mail with
+# an unrecognized address extension.
+forward_path = $home/.forward${recipient_delimiter}${extension},
+ $home/.forward,
+</pre>
+
%PARAM reject_code 554
<p>
if (*var_rcpt_delim == 0) {
bare_key = saved_ext = 0;
} else {
- bare_key = strip_addr(full_key, &saved_ext, *var_rcpt_delim);
+ bare_key = strip_addr(full_key, &saved_ext, var_rcpt_delim);
}
/*
VAR_MAIL_VERSION, DEF_MAIL_VERSION, &var_mail_version, 1, 0,
VAR_DB_TYPE, DEF_DB_TYPE, &var_db_type, 1, 0,
VAR_HASH_QUEUE_NAMES, DEF_HASH_QUEUE_NAMES, &var_hash_queue_names, 1, 0,
- VAR_RCPT_DELIM, DEF_RCPT_DELIM, &var_rcpt_delim, 0, 1,
+ VAR_RCPT_DELIM, DEF_RCPT_DELIM, &var_rcpt_delim, 0, 0,
VAR_RELAY_DOMAINS, DEF_RELAY_DOMAINS, &var_relay_domains, 0, 0,
VAR_FFLUSH_DOMAINS, DEF_FFLUSH_DOMAINS, &var_fflush_domains, 0, 0,
VAR_EXPORT_ENVIRON, DEF_EXPORT_ENVIRON, &var_export_environ, 0, 0,
* Patches change both the patchlevel and the release date. Snapshots have no
* patchlevel; they change the release date only.
*/
-#define MAIL_RELEASE_DATE "20130403"
+#define MAIL_RELEASE_DATE "20130405"
#define MAIL_VERSION_NUMBER "2.11"
#ifdef SNAPSHOT
/* SYNOPSIS
/* #include <split_addr.h>
/*
-/* char *split_addr(localpart, delimiter)
+/* char *split_addr(localpart, delimiter_set)
/* char *localpart;
-/* int delimiter;
+/* const char *delimiter_set;
/* DESCRIPTION
/* split_addr() null-terminates \fIlocalpart\fR at the first
-/* occurrence of the \fIdelimiter\fR character found, and
+/* occurrence of the \fIdelimiter\fR character(s) found, and
/* returns a pointer to the remainder.
/*
/* Reserved addresses are not split: postmaster, mailer-daemon,
/* split_addr - split address with extreme prejudice */
-char *split_addr(char *localpart, int delimiter)
+char *split_addr(char *localpart, const char *delimiter_set)
{
int len;
/*
* Backwards compatibility: don't split owner-foo or foo-request.
*/
- if (delimiter == '-' && var_ownreq_special != 0) {
+ if (strchr(delimiter_set, '-') != 0 && var_ownreq_special != 0) {
if (strncasecmp(localpart, "owner-", 6) == 0)
return (0);
if ((len = strlen(localpart) - 8) > 0
* Safe to split this address. Do not split the address if the result
* would have a null localpart.
*/
- return (delimiter == *localpart ? 0 : split_at(localpart, delimiter));
+ if ((len = strcspn(localpart, delimiter_set)) == 0 || localpart[len] == 0) {
+ return (0);
+ } else {
+ localpart[len] = 0;
+ return (localpart + len + 1);
+ }
}
/* External interface. */
-extern char *split_addr(char *, int);
+extern char *split_addr(char *, const char *);
/* LICENSE
/* .ad
/* SYNOPSIS
/* #include <strip_addr.h>
/*
-/* char *strip_addr(address, extension, delimiter)
+/* char *strip_addr(address, extension, delimiter_set)
/* const char *address;
/* char **extension;
-/* int delimiter;
+/* const char *delimiter_set;
/* DESCRIPTION
/* strip_addr() takes an address and either returns a null
/* pointer when the address contains no address extension,
/* that had to be chopped off.
/* The copy includes the recipient address delimiter.
/* The caller is expected to pass the copy to myfree().
-/* .IP delimiter
-/* Recipient address delimiter.
+/* .IP delimiter_set
+/* Set of recipient address delimiter characters.
/* SEE ALSO
/* split_addr(3) strip extension from localpart
/* LICENSE
/* strip_addr - strip extension from address */
-char *strip_addr(const char *full, char **extension, int delimiter)
+char *strip_addr(const char *full, char **extension, const char *delimiter_set)
{
char *ratsign;
char *extent;
/*
* A quick test to eliminate inputs without delimiter anywhere.
*/
- if (delimiter == 0 || strchr(full, delimiter) == 0) {
+ if (*delimiter_set == 0 || full[strcspn(full, delimiter_set)] == 0) {
stripped = saved_ext = 0;
} else {
stripped = mystrdup(full);
if ((ratsign = strrchr(stripped, '@')) != 0)
*ratsign = 0;
- if ((extent = split_addr(stripped, delimiter)) != 0) {
+ if ((extent = split_addr(stripped, delimiter_set)) != 0) {
extent -= 1;
if (extension) {
- *extent = delimiter;
+ *extent = full[strlen(stripped)];
saved_ext = mystrdup(extent);
*extent = 0;
} else
{
char *extension;
char *stripped;
- int delim = '-';
+ char* delim = "+-";
+
+#define NO_DELIM ""
/*
* Incredible. This function takes only three arguments, and the tests
* already take more lines of code than the code being tested.
*/
- stripped = strip_addr("foo", (char **) 0, 0);
+ stripped = strip_addr("foo", (char **) 0, NO_DELIM);
if (stripped != 0)
msg_panic("strip_addr botch 1");
- stripped = strip_addr("foo", &extension, 0);
+ stripped = strip_addr("foo", &extension, NO_DELIM);
if (stripped != 0)
msg_panic("strip_addr botch 2");
if (extension != 0)
if (extension != 0)
msg_panic("strip_addr botch 6");
- stripped = strip_addr("foo@bar", (char **) 0, 0);
+ stripped = strip_addr("foo@bar", (char **) 0, NO_DELIM);
if (stripped != 0)
msg_panic("strip_addr botch 7");
- stripped = strip_addr("foo@bar", &extension, 0);
+ stripped = strip_addr("foo@bar", &extension, NO_DELIM);
if (stripped != 0)
msg_panic("strip_addr botch 8");
if (extension != 0)
if (extension != 0)
msg_panic("strip_addr botch 12");
- stripped = strip_addr("foo-ext", (char **) 0, 0);
+ stripped = strip_addr("foo-ext", (char **) 0, NO_DELIM);
if (stripped != 0)
msg_panic("strip_addr botch 13");
- stripped = strip_addr("foo-ext", &extension, 0);
+ stripped = strip_addr("foo-ext", &extension, NO_DELIM);
if (stripped != 0)
msg_panic("strip_addr botch 14");
if (extension != 0)
myfree(stripped);
myfree(extension);
- stripped = strip_addr("foo-ext@bar", (char **) 0, 0);
+ stripped = strip_addr("foo-ext@bar", (char **) 0, NO_DELIM);
if (stripped != 0)
msg_panic("strip_addr botch 19");
- stripped = strip_addr("foo-ext@bar", &extension, 0);
+ stripped = strip_addr("foo-ext@bar", &extension, NO_DELIM);
if (stripped != 0)
msg_panic("strip_addr botch 20");
if (extension != 0)
myfree(stripped);
myfree(extension);
+ stripped = strip_addr("foo+ext@bar", &extension, delim);
+ if (stripped == 0)
+ msg_panic("strip_addr botch 25");
+ if (extension == 0)
+ msg_panic("strip_addr botch 26");
+ msg_info("wanted: foo+ext@bar -> %s %s", "foo@bar", "+ext");
+ msg_info("strip_addr foo+ext@bar -> %s %s", stripped, extension);
+ myfree(stripped);
+ myfree(extension);
+
return (0);
}
/* External interface. */
-extern char *strip_addr(const char *, char **, int);
+extern char *strip_addr(const char *, char **, const char *);
/* LICENSE
/* .ad
unknown: strip_addr foo-ext@bar -> foo@bar
unknown: wanted: foo-ext@bar -> foo@bar -ext
unknown: strip_addr foo-ext@bar -> foo@bar -ext
+unknown: wanted: foo+ext@bar -> foo@bar +ext
+unknown: strip_addr foo+ext@bar -> foo@bar +ext
if (alias_maps->error == 0 && owner_expansion == 0
&& (stripped_recipient = strip_addr(state.msg_attr.rcpt.address,
(char **) 0,
- *var_rcpt_delim)) != 0) {
+ var_rcpt_delim)) != 0) {
myfree(owner_alias);
FIND_OWNER(owner_alias, owner_expansion, stripped_recipient);
myfree(stripped_recipient);
/* .IP "\fBqueue_directory (see 'postconf -d' output)\fR"
/* The location of the Postfix top-level queue directory.
/* .IP "\fBrecipient_delimiter (empty)\fR"
-/* The separator between user names and address extensions (user+foo).
+/* The set of characters that can separate a user name from its
+/* address extension (user+foo).
/* .IP "\fBrequire_home_directory (no)\fR"
/* Require that a \fBlocal\fR(8) recipient's home directory exists
/* before mail delivery is attempted.
static const char *local_expand_lookup(const char *name, int mode, char *ptr)
{
LOCAL_EXP *local = (LOCAL_EXP *) ptr;
+ static char rcpt_delim[2];
#define STREQ(x,y) (*(x) == *(y) && strcmp((x), (y)) == 0)
local->status |= LOCAL_EXP_EXTENSION_MATCHED;
return (local->state->msg_attr.extension);
} else if (STREQ(name, "recipient_delimiter")) {
- return (*var_rcpt_delim ? var_rcpt_delim : 0);
+ rcpt_delim[0] =
+ local->state->msg_attr.local[strlen(local->state->msg_attr.user)];
+ rcpt_delim[1] = 0;
+ return (rcpt_delim[0] ? rcpt_delim : 0);
#if 0
} else if (STREQ(name, "client_hostname")) {
return (local->state->msg_attr.request->client_name);
state.msg_attr.user = mystrdup(state.msg_attr.local);
if (*var_rcpt_delim) {
state.msg_attr.extension =
- split_addr(state.msg_attr.user, *var_rcpt_delim);
+ split_addr(state.msg_attr.user, var_rcpt_delim);
if (state.msg_attr.extension && strchr(state.msg_attr.extension, '/')) {
msg_warn("%s: address with illegal extension: %s",
state.msg_attr.queue_id, state.msg_attr.local);
int status;
ssize_t ext_len;
char *ratsign;
+ int rcpt_delim;
/*
* Make verbose logging easier to understand.
* Splice in the optional unmatched address extension.
*/
if (state.msg_attr.unmatched) {
+ rcpt_delim = state.msg_attr.local[strlen(state.msg_attr.user)];
if ((ratsign = strrchr(STR(reply.recipient), '@')) == 0) {
- VSTRING_ADDCH(reply.recipient, *var_rcpt_delim);
+ VSTRING_ADDCH(reply.recipient, rcpt_delim);
vstring_strcat(reply.recipient, state.msg_attr.unmatched);
} else {
ext_len = strlen(state.msg_attr.unmatched);
if ((ratsign = strrchr(STR(reply.recipient), '@')) == 0)
msg_panic("%s: recipient @ botch", myname);
memmove(ratsign + ext_len + 1, ratsign, strlen(ratsign) + 1);
- *ratsign = *var_rcpt_delim;
+ *ratsign = rcpt_delim;
memcpy(ratsign + 1, state.msg_attr.unmatched, ext_len);
VSTRING_SKIP(reply.recipient);
}
: strlen(STR(reply.recipient)));
vstring_strncpy(queue_name, STR(reply.recipient), len);
/* Remove the address extension from the recipient localpart. */
- if (*var_rcpt_delim && split_addr(STR(queue_name), *var_rcpt_delim))
+ if (*var_rcpt_delim && split_addr(STR(queue_name), var_rcpt_delim))
vstring_truncate(queue_name, strlen(STR(queue_name)));
/* Assume the recipient domain is equivalent to nexthop. */
vstring_sprintf_append(queue_name, "@%s", STR(reply.nexthop));
/* .IP "\fBqueue_directory (see 'postconf -d' output)\fR"
/* The location of the Postfix top-level queue directory.
/* .IP "\fBrecipient_delimiter (empty)\fR"
-/* The separator between user names and address extensions (user+foo).
+/* The set of characters that can separate a user name from its
+/* address extension (user+foo).
/* .IP "\fBsyslog_facility (mail)\fR"
/* The syslog facility of Postfix logging.
/* .IP "\fBsyslog_name (see 'postconf -d' output)\fR"
msg_warn("no @ in recipient address: %s",
rcpt_list->info[i].address);
if (*var_rcpt_delim)
- split_addr(STR(buf), *var_rcpt_delim);
+ split_addr(STR(buf), var_rcpt_delim);
if (*STR(buf) == 0)
continue;
dict_update(PIPE_DICT_TABLE, PIPE_DICT_USER, STR(buf));
msg_warn("no @ in recipient address: %s",
rcpt_list->info[i].address);
if (*var_rcpt_delim == 0
- || (ext = split_addr(STR(buf), *var_rcpt_delim)) == 0)
+ || (ext = split_addr(STR(buf), var_rcpt_delim)) == 0)
ext = ""; /* insert null arg */
dict_update(PIPE_DICT_TABLE, PIPE_DICT_EXTENSION, ext);
}
: strlen(STR(reply.recipient)));
vstring_strncpy(queue_name, STR(reply.recipient), len);
/* Remove the address extension from the recipient localpart. */
- if (*var_rcpt_delim && split_addr(STR(queue_name), *var_rcpt_delim))
+ if (*var_rcpt_delim && split_addr(STR(queue_name), var_rcpt_delim))
vstring_truncate(queue_name, strlen(STR(queue_name)));
/* Assume the recipient domain is equivalent to nexthop. */
vstring_sprintf_append(queue_name, "@%s", STR(reply.nexthop));
/* .IP "\fBqueue_directory (see 'postconf -d' output)\fR"
/* The location of the Postfix top-level queue directory.
/* .IP "\fBrecipient_delimiter (empty)\fR"
-/* The separator between user names and address extensions (user+foo).
+/* The set of characters that can separate a user name from its
+/* address extension (user+foo).
/* .IP "\fBsmtpd_banner ($myhostname ESMTP $mail_name)\fR"
/* The text that follows the 220 status code in the SMTP greeting
/* banner.
if (*var_rcpt_delim == 0) {
bare_addr = 0;
} else {
- bare_addr = strip_addr(addr, (char **) 0, *var_rcpt_delim);
+ bare_addr = strip_addr(addr, (char **) 0, var_rcpt_delim);
}
#define CHECK_MAIL_ACCESS_RETURN(x) \
* partial lookup keys with regular expressions.
*/
if ((stripped_addr = strip_addr(addr, DISCARD_EXTENSION,
- *var_rcpt_delim)) != 0) {
+ var_rcpt_delim)) != 0) {
found = find_transport_entry(tp, stripped_addr, rcpt_domain, PARTIAL,
channel, nexthop);
/* With locally submitted mail, append the string ".$mydomain" to
/* addresses that have no ".domain" information.
/* .IP "\fBrecipient_delimiter (empty)\fR"
-/* The separator between user names and address extensions (user+foo).
+/* The set of characters that can separate a user name from its
+/* address extension (user+foo).
/* .IP "\fBswap_bangpath (yes)\fR"
/* Enable the rewriting of "site!user" into "user@site".
/* .PP
VSTRING *buf;
char *member;
char *val;
+ const char *old;
int old_lineno;
int lineno;
const char *err;
err, STR(buf));
if (msg_verbose > 1)
msg_info("%s: %s = %s", myname, member, val);
+ if ((old = dict->lookup(dict, member)) != 0
+ && strcmp(old, val) != 0)
+ msg_warn("%s, line %d: overriding earlier entry: %s=%s",
+ VSTREAM_PATH(fp), lineno, member, old);
if (dict->update(dict, member, val) != 0)
msg_fatal("%s, line %d: unable to update %s:%s",
VSTREAM_PATH(fp), lineno, dict->type, dict->name);