2
0
mirror of https://github.com/vdukhovni/postfix synced 2025-08-30 05:38:06 +00:00

snapshot-20010709

This commit is contained in:
Wietse Venema 2001-07-09 00:00:00 -05:00 committed by Viktor Dukhovni
parent 7e8044da83
commit a94bf41134
44 changed files with 987 additions and 116 deletions

1
postfix/.indent.pro vendored
View File

@ -1,3 +1,4 @@
-TABOUNCE
-TALIAS_TOKEN
-TARGV
-TBH_TABLE

View File

@ -25,7 +25,7 @@ lmtp support yes (client)
m4 config no
mail to command yes (configurable for .forward, aliases, :include:)
mail to file yes (configurable for .forward, aliases, :include:)
maildir yes (with procmail)
maildir yes
mailertable yes (it's called transport)
mailq yes
majordomo yes (edit approve script to delete /delivered-to/i)
@ -38,6 +38,7 @@ nis tables yes
nis+ tables not yet
pipeline option yes (server and client)
pop/imap yes (with third-party daemons that use /var[/spool]/mail)
qmqp server yes (with verp support)
rbl support yes
return-receipt: not yet
sasl support yes (compile time option)
@ -55,5 +56,6 @@ user+extension yes (also: .forward+extension)
user-extension yes (also: .forward-extension)
user.lock yes (runtime configurable)
uucp support yes (sends user@domain recipients)
verp support yes (delimiters are configurable)
virtual domains yes
year 2000 safe yes

View File

@ -5292,13 +5292,12 @@ Apologies for any names omitted.
Cleanup: the virtual delivery agent was poorly integrated
so that the SMTP server and queue manager did not reject
mail for unknown users. Files: smtpd/smtpd_check.c,
*qmgr/qmgr_message.c.
mail for unknown users. Files: smtpd/smtpd_check.c.
20010705
Feature: QMQP server for compatibility with the ezmlm list
manager. Files: util/netstring.[hc], qmqpd/qmqpd*.c.
Feature: QMQP server, compatible with qmail and the ezmlm
list manager. Files: util/netstring.[hc], qmqpd/qmqpd*.c.
20010706
@ -5309,3 +5308,17 @@ Apologies for any names omitted.
Bugfix: with disable_dns=yes, the SMTP client treated all
host lookup errors as permanent. File: smtp/smtp_addr.c.
20010709
Feature: VERP support, based on a patch by Peng Yong, and
with the missing parts filled in so that the Postfix bounce
daemon can send one VERP bounce per undeliverable recipient.
Files: , sendmail/sendmail.c, smtpd/smtpd.c, qmgr/qmgr_deliver.c,
bounce/bounce_notify_verp.c, qmqpd/qmqpd.c, plus a couple
support routines in the global library.
Cleanup: with recipient_delimiter=+ (or any character other
than -) Postfix will now recognize address extensions even
with owner-foo+extension addresses. This is necessary to
make VERP work for mailing lists.

View File

@ -145,7 +145,7 @@ expect to run more than 1000 delivery processes, you may need to
override the definition of the FD_SETSIZE macro to make select()
work correctly:
% make makefiles CCARGS=-FD_SETSIZE=2048
% make makefiles CCARGS=-DFD_SETSIZE=2048
In any case, if the command

View File

@ -6,8 +6,8 @@ that Postfix can be used as a backend for the Ezmlm-idx mailing
list manager. This support includes qmqp-source and qmqp-sink
programs for protocol stress testing.
Turning on the QMQP service
===========================
Turning on the Postfix QMQP service
===================================
To enable QMQP server support on an existing Postfix system you
have to add the following line to /etc/postfix/master.cf:
@ -15,8 +15,8 @@ have to add the following line to /etc/postfix/master.cf:
628 inet n - n - - qmqpd
QMQP server access control
==========================
Postfix QMQP server access control
==================================
By default, the QMQP server does not accept mail from any client.
This is because the QMQP server relays mail to any destination
@ -37,3 +37,9 @@ instead.
Patterns are separated by whitespace and/or commas. In order to
reverse the result, precede a non-file name pattern with an
exclamation point (!).
Setting up Ezmlm-idx to use Postfix QMQP support
================================================
You need to list the Postfix IP address in a suitable configuration
file. See the ezmlm-idx documentation for details.

View File

@ -1,17 +1,33 @@
Incompatible changes with snapshot-20010707
Incompatible changes with snapshot-20010709
===========================================
The SMTP client by default breaks lines > 2048 characters, in order
to avoid problems with mail delivery to fragile SMTP server software.
To get the old behavior, specify "smtp_break_lines = no" in the
Postfix main.cf file.
This release introduces a new queue file record type that is used
only for messages that actually use VERP (variable envelope return
path) support. With this sole exception, the queue file format is
entirely backwards compatible with previous Postfix releases.
Major changes with snapshot-20010707
The SMTP client now by default breaks lines > 2048 characters, to
avoid mail delivery problems with fragile SMTP server software.
To get the old behavior back, specify "smtp_break_lines = no" in
the Postfix main.cf file.
With recipient_delimiter=+ (or any character other than -) Postfix
will now recognize address extensions even with owner-foo+extension
addresses. This change was necessary to make VERP useful for mailing
list bounce processing.
Major changes with snapshot-20010709
====================================
QMQP server support, so that Postfix can be used as a backend mailer
for the Ezmlm-idx mailing list manager. The service is disabled by
default. To enable, follow instructions in the README_QMQP file.
for the ezmlm-idx mailing list manager. You still need qmail to
drive ezmlm and to process mailing list bounces. The QMQP service
is disabled by default. To enable, follow the instructions in the
QMQP_README file.
VERP (variable envelope return path) support. This is enabled by
default. See the VERP_README file for instructions. These instructions
need more examples for how to process bounces automatically.
You can now reject unknown virtual(8) recipients at the SMTP port
by specifying a "domain.name whatever" entry in the tables specified

96
postfix/VERP_README Normal file
View File

@ -0,0 +1,96 @@
Postfix VERP support
====================
Postfix supports variable envelope return path addresses, which
means that each recipient receives a customized copy of the message,
with the recipient address encoded in the envelope sender address.
This concept was popularized by the qmail MTA and by the ezmlm
mailing list manager.
When VERP style delivery is requested, Postfix delivers mail with
sender address prefix@origin for a recipient user@domain, with a
sender address that encodes the recipient as follows:
prefix+user=domain@origin
so that undeliverable mail reveals what address was undeliverable.
The + and = are the default VERP delimiters. You can specify non-
default delimiters in main.cf with the default_verp_delimiters
configuration parameter (default value: +=). Specify two characters;
the first delimiter should match the $recipient_delimiter setting.
Using VERP with majordomo etc. mailing lists
============================================
In order to make VERP useful with majordomo etc. mailing lists,
you would configure the list manager to submit mail as:
sendmail -V -f owner-listname other-arguments...
This text assumes that you have set up an owner-listname alias that
routes undeliverable mail to a real person:
/etc/aliases:
owner-listname: yourname+listname
In order to process bounces we are going to make extensive use of
address extension tricks.
You need to tell Postfix that + is the separator between an address
and its optional address extension, that address extensions are
appended to .forward file names, and that address extensions are
to be discarded when doing alias expansions:
/etc/postfix/main.cf:
recipient_delimiter = +
forward_path = $home/.forward${recipient_delimiter}${extension},$home/.forward
propagate_unmatched_extensions = canonical, virtual
(the last two parameter settings are default settings).
You need to set up a file named .forward+listname with the commands
that process all the mail that is sent to the owner-listname address:
~/.forward+listname:
"|/some/where/command ..."
With this set up, undeliverable mail for user@domain will be returned
to the following address:
owner-listname+user=domain@your.domain
which is processed by the command in your .forward+listname file.
It is left as an exercise for the reader to parse the To: header
line and to pull out the user=domain part from the recipient address.
VERP support in the Postfix SMTP server
=======================================
The Postfix SMTP server has a new command XVERP to enable VERP
style delivery. The syntax allows two forms:
MAIL FROM:<sender@domain> XVERP
MAIL FROM:<sender@domain> XVERP=xy
where x and y are the VERP delimiters. When no VERP delimiters
are specified, Postfix uses the two characters specified with the
default_verp_delimiters configuration parameter.
VERP support in the Postfix sendmail command
============================================
The Postfix sendmail command has a -V flag to request VERP style
delivery. It is not possible to override the default VERP delimiters.
VERP support in the Postfix QMQP server
=======================================
When the Postfix QMQP server receives mail with a an envelope sender
address of the form:
prefix-@origin-@[]
Postfix generates VERP sender addresses using prefix@domain as the
original sender address, and using "-=" as the VERP delimiters.

View File

@ -94,33 +94,38 @@ SENDMAIL(1) SENDMAIL(1)
<b>-U</b> (ignored)
Initial user submission.
<b>-bd</b> Go into daemon mode. This mode of operation is
<b>-V</b> Variable Envelope Return Path. Given an envelope
sender address <i>prefix</i>-@<i>origin</i>, each recipient
<i>user@domain</i> receives mail with a personalized enve-
lope sender address <i>prefix</i><b>-</b><i>user=domain</i>@<i>origin</i>.
<b>-bd</b> Go into daemon mode. This mode of operation is
implemented by executing the <b>postfix</b> <b>start</b> command.
<b>-bi</b> Initialize alias database. See the <b>newaliases</b> com-
<b>-bi</b> Initialize alias database. See the <b>newaliases</b> com-
mand above.
<b>-bm</b> Read mail from standard input and arrange for
<b>-bm</b> Read mail from standard input and arrange for
delivery. This is the default mode of operation.
<b>-bp</b> List the mail queue. See the <b>mailq</b> command above.
<b>-bs</b> Stand-alone SMTP server mode. Read SMTP commands
from standard input, and write responses to stan-
<b>-bs</b> Stand-alone SMTP server mode. Read SMTP commands
from standard input, and write responses to stan-
dard output. This mode of operation is implemented
by running the <a href="smtpd.8.html"><b>smtpd</b>(8)</a> daemon.
<b>-f</b> <i>sender</i>
Set the envelope sender address. This is the
address where delivery problems are sent to, unless
the message contains an <b>Errors-To:</b> message header.
the message contains an <b>Errors-To:</b> message header.
<b>-h</b> <i>hop_count</i> (ignored)
Hop count limit. Use the <b>hopcount</b><i>_</i><b>limit</b> configura-
Hop count limit. Use the <b>hopcount</b><i>_</i><b>limit</b> configura-
tion parameter instead.
<b>-i</b> When reading a message from standard input, don't
treat a line with only a <b>.</b> character as the end of
<b>-i</b> When reading a message from standard input, don't
treat a line with only a <b>.</b> character as the end of
input.
<b>-m</b> (ignored)
@ -130,68 +135,68 @@ SENDMAIL(1) SENDMAIL(1)
Backwards compatibility.
<b>-oA</b><i>alias_database</i>
Non-default alias database. Specify <i>pathname</i> or
Non-default alias database. Specify <i>pathname</i> or
<i>type</i>:<i>pathname</i>. See <a href="postalias.1.html"><b>postalias</b>(1)</a> for details.
<b>-o7</b> (ignored)
<b>-o8</b> (ignored)
The message body type. Currently, Postfix imple-
The message body type. Currently, Postfix imple-
ments <b>just-send-eight</b>.
<b>-oi</b> When reading a message from standard input, don't
treat a line with only a <b>.</b> character as the end of
<b>-oi</b> When reading a message from standard input, don't
treat a line with only a <b>.</b> character as the end of
input.
<b>-om</b> (ignored)
The sender is never eliminated from alias etc.
The sender is never eliminated from alias etc.
expansions.
<b>-o</b> <i>x</i> <i>value</i> (ignored)
Set option <i>x</i> to <i>value</i>. Use the equivalent configu-
Set option <i>x</i> to <i>value</i>. Use the equivalent configu-
ration parameter in <b>main.cf</b> instead.
<b>-r</b> <i>sender</i>
Set the envelope sender address. This is the
address where delivery problems are sent to, unless
the message contains an <b>Errors-To:</b> message header.
the message contains an <b>Errors-To:</b> message header.
<b>-q</b> Attempt to deliver all queued mail. This is imple-
<b>-q</b> Attempt to deliver all queued mail. This is imple-
mented by kicking the <a href="qmgr.8.html"><b>qmgr</b>(8)</a> daemon.
<b>-q</b><i>interval</i> (ignored)
The interval between queue runs. Use the
The interval between queue runs. Use the
<b>queue</b><i>_</i><b>run</b><i>_</i><b>delay</b> configuration parameter instead.
<b>-qR</b><i>site</i>
Schedule immediate delivery of all mail that is
queued for the named <i>site</i>. Depending on the desti-
nation, this uses "fast flush" service, or it has
the same effect as <b>sendmail</b> <b>-q</b>. This is imple-
Schedule immediate delivery of all mail that is
queued for the named <i>site</i>. Depending on the desti-
nation, this uses "fast flush" service, or it has
the same effect as <b>sendmail</b> <b>-q</b>. This is imple-
mented by connecting to the local SMTP server. See
<a href="smtpd.8.html"><b>smtpd</b>(8)</a> for more information about the "fast
flush" service.
<b>-qS</b><i>site</i>
This command is not implemented. Use the slower
This command is not implemented. Use the slower
<b>sendmail</b> <b>-q</b> command instead.
<b>-t</b> Extract recipients from message headers. This
requires that no recipients be specified on the
<b>-t</b> Extract recipients from message headers. This
requires that no recipients be specified on the
command line.
<b>-v</b> Enable verbose logging for debugging purposes. Mul-
tiple <b>-v</b> options make the software increasingly
tiple <b>-v</b> options make the software increasingly
verbose.
<b>SECURITY</b>
By design, this program is not set-user (or group) id.
However, it must handle data from untrusted users or
untrusted machines. Thus, the usual precautions need to
By design, this program is not set-user (or group) id.
However, it must handle data from untrusted users or
untrusted machines. Thus, the usual precautions need to
be taken against malicious inputs.
<b>DIAGNOSTICS</b>
Problems are logged to <b>syslogd</b>(8) and to the standard
Problems are logged to <b>syslogd</b>(8) and to the standard
error stream.
<b>ENVIRONMENT</b>
@ -203,7 +208,7 @@ SENDMAIL(1) SENDMAIL(1)
<b>MAIL</b><i>_</i><b>DEBUG</b>
Enable debugging with an external command, as spec-
ified with the <b>debugger</b><i>_</i><b>command</b> configuration
ified with the <b>debugger</b><i>_</i><b>command</b> configuration
parameter.
<b>FILES</b>
@ -211,13 +216,13 @@ SENDMAIL(1) SENDMAIL(1)
/etc/postfix, configuration files
<b>CONFIGURATION</b> <b>PARAMETERS</b>
See the Postfix <b>main.cf</b> file for syntax details and for
default values. Use the <b>postfix</b> <b>reload</b> command after a
See the Postfix <b>main.cf</b> file for syntax details and for
default values. Use the <b>postfix</b> <b>reload</b> command after a
configuration change.
<b>alias</b><i>_</i><b>database</b>
Default alias database(s) for <b>newaliases</b>. The
default value for this parameter is system-spe-
Default alias database(s) for <b>newaliases</b>. The
default value for this parameter is system-spe-
cific.
<b>bounce</b><i>_</i><b>size</b><i>_</i><b>limit</b>
@ -233,55 +238,55 @@ SENDMAIL(1) SENDMAIL(1)
initialized.
<b>debug</b><i>_</i><b>peer</b><i>_</i><b>level</b>
Increment in verbose logging level when a remote
Increment in verbose logging level when a remote
host matches a pattern in the <b>debug</b><i>_</i><b>peer</b><i>_</i><b>list</b>
parameter.
<b>debug</b><i>_</i><b>peer</b><i>_</i><b>list</b>
List of domain or network patterns. When a remote
host matches a pattern, increase the verbose log-
ging level by the amount specified in the
List of domain or network patterns. When a remote
host matches a pattern, increase the verbose log-
ging level by the amount specified in the
<b>debug</b><i>_</i><b>peer</b><i>_</i><b>level</b> parameter.
<b>fast</b><i>_</i><b>flush</b><i>_</i><b>domains</b>
List of domains that will receive "fast flush" ser-
vice (default: all domains that this system is
willing to relay mail to). This greatly improves
the performance of the SMTP <b>ETRN</b> request, and of
the <b>sendmail</b> <b>-qR</b> command. For domains not in the
vice (default: all domains that this system is
willing to relay mail to). This greatly improves
the performance of the SMTP <b>ETRN</b> request, and of
the <b>sendmail</b> <b>-qR</b> command. For domains not in the
list, Postfix simply attempts to deliver all queued
mail.
<b>fork</b><i>_</i><b>attempts</b>
Number of attempts to <b>fork</b>() a process before giv-
Number of attempts to <b>fork</b>() a process before giv-
ing up.
<b>fork</b><i>_</i><b>delay</b>
Delay in seconds between successive <b>fork</b>()
Delay in seconds between successive <b>fork</b>()
attempts.
<b>hopcount</b><i>_</i><b>limit</b>
Limit the number of <b>Received:</b> message headers.
<b>mail</b><i>_</i><b>owner</b>
The owner of the mail queue and of most Postfix
The owner of the mail queue and of most Postfix
processes.
<b>command</b><i>_</i><b>directory</b>
Directory with Postfix support commands (default:
Directory with Postfix support commands (default:
<b>$program</b><i>_</i><b>directory</b>).
<b>daemon</b><i>_</i><b>directory</b>
Directory with Postfix daemon programs (default:
Directory with Postfix daemon programs (default:
<b>$program</b><i>_</i><b>directory</b>).
<b>queue</b><i>_</i><b>directory</b>
Top-level directory of the Postfix queue. This is
Top-level directory of the Postfix queue. This is
also the root directory of Postfix daemons that run
chrooted.
<b>queue</b><i>_</i><b>run</b><i>_</i><b>delay</b>
The time between successive scans of the deferred
The time between successive scans of the deferred
queue.
<b>SEE</b> <b>ALSO</b>
@ -297,7 +302,7 @@ SENDMAIL(1) SENDMAIL(1)
syslogd(8) system logging
<b>LICENSE</b>
The Secure Mailer license must be distributed with this
The Secure Mailer license must be distributed with this
software.
<b>AUTHOR(S)</b>

View File

@ -35,8 +35,8 @@ SMTPD(8) SMTPD(8)
<b>STANDARDS</b>
<a href="http://www.faqs.org/rfcs/rfc821.html">RFC 821</a> (SMTP protocol)
<a href="http://www.faqs.org/rfcs/rfc1123.html">RFC 1123</a> (Host requirements)
<a href="http://www.faqs.org/rfcs/rfc1651.html">RFC 1651</a> (SMTP service extensions)
<a href="http://www.faqs.org/rfcs/rfc1652.html">RFC 1652</a> (8bit-MIME transport)
<a href="http://www.faqs.org/rfcs/rfc1869.html">RFC 1869</a> (SMTP service extensions)
<a href="http://www.faqs.org/rfcs/rfc1854.html">RFC 1854</a> (SMTP Pipelining)
<a href="http://www.faqs.org/rfcs/rfc1870.html">RFC 1870</a> (Message Size Declaration)
<a href="http://www.faqs.org/rfcs/rfc1985.html">RFC 1985</a> (ETRN command)

View File

@ -82,6 +82,11 @@ Log mailer traffic. Use the \fBdebug_peer_list\fR and
\fBdebug_peer_level\fR configuration parameters instead.
.IP "\fB-U\fR (ignored)"
Initial user submission.
.IP \fB-V\fR
Variable Envelope Return Path. Given an envelope sender address
\fIprefix\fR-@\fIorigin\fR, each recipient \fIuser@domain\fR
receives mail with a personalized envelope sender address
\fIprefix\fB-\fIuser=domain\fR@\fIorigin\fR.
.IP \fB-bd\fR
Go into daemon mode. This mode of operation is implemented by
executing the \fBpostfix start\fR command.

View File

@ -42,8 +42,8 @@ run chrooted at fixed low privilege.
.nf
RFC 821 (SMTP protocol)
RFC 1123 (Host requirements)
RFC 1651 (SMTP service extensions)
RFC 1652 (8bit-MIME transport)
RFC 1869 (SMTP service extensions)
RFC 1854 (SMTP Pipelining)
RFC 1870 (Message Size Declaration)
RFC 1985 (ETRN command)

View File

@ -1,8 +1,8 @@
SHELL = /bin/sh
SRCS = bounce.c bounce_append_service.c bounce_notify_service.c \
bounce_cleanup.c bounce_notify_util.c
bounce_cleanup.c bounce_notify_util.c bounce_notify_verp.c
OBJS = bounce.o bounce_append_service.o bounce_notify_service.o \
bounce_cleanup.o bounce_notify_util.o
bounce_cleanup.o bounce_notify_util.o bounce_notify_verp.o
HDRS =
TESTSRC =
WARN = -W -Wformat -Wimplicit -Wmissing-prototypes \
@ -69,6 +69,7 @@ bounce.o: ../../include/mail_queue.h
bounce.o: ../../include/mail_params.h
bounce.o: ../../include/mail_conf.h
bounce.o: ../../include/bounce.h
bounce.o: ../../include/mail_addr.h
bounce.o: ../../include/mail_server.h
bounce.o: bounce_service.h
bounce.o: ../../include/bounce_log.h
@ -133,3 +134,18 @@ bounce_notify_util.o: ../../include/name_mask.h
bounce_notify_util.o: ../../include/bounce_log.h
bounce_notify_util.o: ../../include/mail_date.h
bounce_notify_util.o: bounce_service.h
bounce_notify_verp.o: bounce_notify_verp.c
bounce_notify_verp.o: ../../include/sys_defs.h
bounce_notify_verp.o: ../../include/msg.h
bounce_notify_verp.o: ../../include/vstream.h
bounce_notify_verp.o: ../../include/vbuf.h
bounce_notify_verp.o: ../../include/name_mask.h
bounce_notify_verp.o: ../../include/mail_params.h
bounce_notify_verp.o: ../../include/mail_queue.h
bounce_notify_verp.o: ../../include/vstring.h
bounce_notify_verp.o: ../../include/post_mail.h
bounce_notify_verp.o: ../../include/cleanup_user.h
bounce_notify_verp.o: ../../include/mail_addr.h
bounce_notify_verp.o: ../../include/mail_error.h
bounce_notify_verp.o: bounce_service.h
bounce_notify_verp.o: ../../include/bounce_log.h

View File

@ -80,6 +80,11 @@
/* System library. */
#include <sys_defs.h>
#include <string.h>
#ifdef STRCASECMP_IN_STRINGS_H
#include <strings.h>
#endif
/* Utility library. */
@ -95,6 +100,7 @@
#include <mail_params.h>
#include <mail_conf.h>
#include <bounce.h>
#include <mail_addr.h>
/* Single-threaded server skeleton. */
@ -122,6 +128,7 @@ static VSTRING *queue_id;
static VSTRING *queue_name;
static VSTRING *recipient;
static VSTRING *sender;
static VSTRING *verp_delims;
static VSTRING *why;
#define STR vstring_str
@ -199,7 +206,62 @@ static int bounce_notify_proto(char *service_name, VSTREAM *client, int flush)
* Execute the request.
*/
return (bounce_notify_service(service_name, STR(queue_name),
STR(queue_id), STR(sender), flush));
STR(queue_id), STR(sender), flush));
}
/* bounce_verp_proto - bounce_notify server protocol, VERP style */
static int bounce_verp_proto(char *service_name, VSTREAM *client, int flush)
{
char *myname="bounce_verp_proto";
int flags;
/*
* Read and validate the client request.
*/
if (mail_command_read(client, "%d %s %s %s %s",
&flags, queue_name, queue_id,
sender, verp_delims) != 5) {
msg_warn("malformed request");
return (-1);
}
if (mail_queue_name_ok(STR(queue_name)) == 0) {
msg_warn("malformed queue name: %s", printable(STR(queue_name), '?'));
return (-1);
}
if (mail_queue_id_ok(STR(queue_id)) == 0) {
msg_warn("malformed queue id: %s", printable(STR(queue_id), '?'));
return (-1);
}
if (strlen(STR(verp_delims)) != 2) {
msg_warn("malformed verp delimiter string: %s",
printable(STR(verp_delims), '?'));
return (-1);
}
if (msg_verbose)
msg_info("%s: service=%s queue=%s id=%s sender=%s delim=%s",
myname, service_name, STR(queue_name), STR(queue_id),
STR(sender), STR(verp_delims));
/*
* On request by the client, set up a trap to delete the log file in case
* of errors.
*/
if (flags & BOUNCE_FLAG_CLEAN)
bounce_cleanup_register(service_name, STR(queue_id));
/*
* Execute the request. Fall back to traditional notification if a bounce
* was returned as undeliverable, because we don't want to VERPify those.
*/
if (!*STR(sender) || !strcasecmp(STR(sender), mail_addr_double_bounce())) {
msg_warn("request to send VERP-style notification of bounced mail");
return (bounce_notify_service(service_name, STR(queue_name),
STR(queue_id), STR(sender), flush));
} else
return (bounce_notify_verp(service_name, STR(queue_name),
STR(queue_id), STR(sender),
STR(verp_delims), flush));
}
/* bounce_service - parse bounce command type and delegate */
@ -228,6 +290,8 @@ static void bounce_service(VSTREAM *client, char *service_name, char **argv)
if (mail_scan(client, "%d", &command) != 1) {
msg_warn("malformed request");
status = -1;
} else if (command == BOUNCE_CMD_VERP) {
status = bounce_verp_proto(service_name, client, REALLY_BOUNCE);
} else if (command == BOUNCE_CMD_FLUSH) {
status = bounce_notify_proto(service_name, client, REALLY_BOUNCE);
} else if (command == BOUNCE_CMD_WARN) {
@ -271,6 +335,7 @@ static void post_jail_init(char *unused_name, char **unused_argv)
queue_name = vstring_alloc(10);
recipient = vstring_alloc(10);
sender = vstring_alloc(10);
verp_delims = vstring_alloc(10);
why = vstring_alloc(10);
}

View File

@ -199,7 +199,7 @@ BOUNCE_INFO *bounce_mail_init(const char *service, const char *queue_name,
*/
if ((bounce_info->log_handle = bounce_log_open(bounce_info->service,
bounce_info->queue_id,
O_RDONLY, 0)) == 0
O_RDWR, 0)) == 0
&& errno != ENOENT)
msg_fatal("open %s %s: %m", bounce_info->service,
bounce_info->queue_id);

View File

@ -0,0 +1,210 @@
/*++
/* NAME
/* bounce_notify_verp 3
/* SUMMARY
/* send non-delivery report to sender, server side
/* SYNOPSIS
/* #include "bounce_service.h"
/*
/* int bounce_notify_verp(service, queue_name, queue_id, sender,
/* verp_delims, flush)
/* char *queue_name;
/* char *queue_id;
/* char *sender;
/* char *verp_delims;
/* int flush;
/* DESCRIPTION
/* This module implements the server side of the bounce_notify()
/* (send bounce message) request. If flush is zero, the logfile
/* is not removed, and a warning is sent instead of a bounce.
/* The bounce recipient address is encoded in VERP format.
/* This routine must be used for single bounces only.
/*
/* When a message bounces, a full copy is sent to the originator,
/* and an optional copy of the diagnostics with message headers is
/* sent to the postmaster. The result is non-zero when the operation
/* should be tried again.
/*
/* When a bounce is sent, the sender address is the empty
/* address.
/* DIAGNOSTICS
/* Fatal error: error opening existing file. Warnings: corrupt
/* message file. A corrupt message is saved to the "corrupt"
/* queue for further inspection.
/* SEE ALSO
/* bounce(3) basic bounce service client interface
/* LICENSE
/* .ad
/* .fi
/* The Secure Mailer license must be distributed with this software.
/* AUTHOR(S)
/* Wietse Venema
/* IBM T.J. Watson Research
/* P.O. Box 704
/* Yorktown Heights, NY 10598, USA
/*--*/
/* System library. */
#include <sys_defs.h>
#include <fcntl.h>
#include <errno.h>
#include <string.h>
#include <ctype.h>
#ifdef STRCASECMP_IN_STRINGS_H
#include <strings.h>
#endif
/* Utility library. */
#include <msg.h>
#include <vstream.h>
#include <name_mask.h>
/* Global library. */
#include <mail_params.h>
#include <mail_queue.h>
#include <post_mail.h>
#include <mail_addr.h>
#include <mail_error.h>
#include <verp_sender.h>
/* Application-specific. */
#include "bounce_service.h"
#define STR vstring_str
/* bounce_notify_verp - send a bounce */
int bounce_notify_verp(char *service, char *queue_name,
char *queue_id, char *recipient,
char *verp_delims, int flush)
{
char *myname = "bounce_notify_verp";
BOUNCE_INFO *bounce_info;
int bounce_status = 0;
int postmaster_status;
VSTREAM *bounce;
int notify_mask = name_mask(VAR_NOTIFY_CLASSES, mail_error_masks,
var_notify_classes);
char *postmaster;
VSTRING *verp_buf = vstring_alloc(100);
/*
* Sanity checks. We must be called only for undeliverable non-bounce
* messages.
*/
if (*recipient == 0)
msg_panic("%s: attempt to bounce a single bounce", myname);
if (strcasecmp(recipient, mail_addr_double_bounce()) == 0)
msg_panic("%s: attempt to bounce a double bounce", myname);
/*
* Initialize. Open queue file, bounce log, etc.
*/
bounce_info = bounce_mail_init(service, queue_name, queue_id, flush);
#define NULL_SENDER MAIL_ADDR_EMPTY /* special address */
#define NULL_CLEANUP_FLAGS 0
#define BOUNCE_HEADERS 1
#define BOUNCE_ALL 0
/*
* A non-bounce message was returned. Send a single bounce, one per
* recipient.
*/
while (bounce_log_read(bounce_info->log_handle) != 0) {
/*
* Notify the originator.
*/
verp_sender(verp_buf, verp_delims, recipient,
bounce_info->log_handle->recipient);
if ((bounce = post_mail_fopen_nowait(NULL_SENDER, STR(verp_buf),
NULL_CLEANUP_FLAGS)) != 0) {
/*
* Send the bounce message header, some boilerplate text that
* pretends that we are a polite mail system, the text with
* reason for the bounce, and a copy of the original message.
*/
if (bounce_header(bounce, bounce_info, STR(verp_buf)) == 0
&& bounce_boilerplate(bounce, bounce_info) == 0
&& bounce_recipient_log(bounce, bounce_info) == 0
&& bounce_header_dsn(bounce, bounce_info) == 0
&& bounce_recipient_dsn(bounce, bounce_info) == 0)
bounce_original(bounce, bounce_info, flush ?
BOUNCE_ALL : BOUNCE_HEADERS);
bounce_status = post_mail_fclose(bounce);
} else
bounce_status = 1;
/*
* Stop at the first sign of trouble, instead of making the problem
* worse.
*/
if (bounce_status != 0)
break;
/*
* Mark this recipient as done.
*/
bounce_log_delrcpt(bounce_info->log_handle);
/*
* Optionally, send a postmaster notice.
*
* This postmaster notice is not critical, so if it fails don't
* retransmit the bounce that we just generated, just log a warning.
*/
#define WANT_IF_BOUNCE (flush == 1 && (notify_mask & MAIL_ERROR_BOUNCE))
#define WANT_IF_DELAY (flush == 0 && (notify_mask & MAIL_ERROR_DELAY))
if (WANT_IF_BOUNCE || WANT_IF_DELAY) {
/*
* Send the text with reason for the bounce, and the headers of
* the original message. Don't bother sending the boiler-plate
* text. This postmaster notice is not critical, so if it fails
* don't retransmit the bounce that we just generated, just log a
* warning.
*/
postmaster = flush ? var_bounce_rcpt : var_delay_rcpt;
if ((bounce = post_mail_fopen_nowait(mail_addr_double_bounce(),
postmaster,
NULL_CLEANUP_FLAGS)) != 0) {
if (bounce_header(bounce, bounce_info, postmaster) == 0
&& bounce_recipient_log(bounce, bounce_info) == 0
&& bounce_header_dsn(bounce, bounce_info) == 0
&& bounce_recipient_dsn(bounce, bounce_info) == 0)
bounce_original(bounce, bounce_info, BOUNCE_HEADERS);
postmaster_status = post_mail_fclose(bounce);
} else
postmaster_status = 1;
if (postmaster_status)
msg_warn("postmaster notice failed while bouncing to %s",
recipient);
}
}
/*
* Examine the completion status. Delete the bounce log file only when
* the bounce was posted successfully, and only if we are bouncing for
* real, not just warning.
*/
if (flush != 0 && bounce_status == 0 && mail_queue_remove(service, queue_id)
&& errno != ENOENT)
msg_fatal("remove %s %s: %m", service, queue_id);
/*
* Cleanup.
*/
bounce_mail_free(bounce_info);
vstring_free(verp_buf);
return (bounce_status);
}

View File

@ -28,6 +28,11 @@ extern int bounce_append_service(char *, char *, char *, char *);
*/
extern int bounce_notify_service(char *, char *, char *, char *, int);
/*
* bounce_notify_verp.c
*/
extern int bounce_notify_verp(char *, char *, char *, char *, char *, int);
/*
* bounce_cleanup.c
*/
@ -51,7 +56,7 @@ typedef struct {
VSTREAM *orig_fp; /* open queue file */
long orig_offs; /* start of content */
time_t arrival_time; /* time of arrival */
BOUNCE_LOG *log_handle; /* open logfile */
BOUNCE_LOG *log_handle; /* open logfile */
} BOUNCE_INFO;
extern BOUNCE_INFO *bounce_mail_init(const char *, const char *, const char *, int);

View File

@ -179,6 +179,20 @@ static void cleanup_envelope_process(CLEANUP_STATE *state, int type, char *buf,
state->errs |= CLEANUP_STAT_BAD;
return;
}
} else if (type == REC_TYPE_VERP) {
if (state->sender == 0 || *state->sender == 0) {
state->errs |= CLEANUP_STAT_BAD;
return;
}
if (len == 0) {
buf = var_verp_delim;
len = strlen(buf);
}
if (len == 2) {
cleanup_out(state, type, buf, len);
} else {
state->errs |= CLEANUP_STAT_BAD;
}
} else {
cleanup_out(state, type, buf, len);
}

View File

@ -106,6 +106,7 @@ char *var_prop_extension; /* propagate unmatched extension */
char *var_always_bcc; /* big brother */
int var_extra_rcpt_limit; /* recipient extract limit */
char *var_rcpt_witheld; /* recipients not disclosed */
char *var_verp_delim; /* default VERP delimiters */
CONFIG_INT_TABLE cleanup_int_table[] = {
VAR_HOPCOUNT_LIMIT, DEF_HOPCOUNT_LIMIT, &var_hopcount_limit, 1, 0,
@ -133,6 +134,7 @@ CONFIG_STR_TABLE cleanup_str_table[] = {
VAR_PROP_EXTENSION, DEF_PROP_EXTENSION, &var_prop_extension, 0, 0,
VAR_ALWAYS_BCC, DEF_ALWAYS_BCC, &var_always_bcc, 0, 0,
VAR_RCPT_WITHELD, DEF_RCPT_WITHELD, &var_rcpt_witheld, 1, 0,
VAR_VERP_DELIM, DEF_VERP_DELIM, &var_verp_delim, 2, 2,
0,
};

View File

@ -18,7 +18,8 @@ SRCS = been_here.c bounce.c canon_addr.c cleanup_strerror.c clnt_stream.c \
sent.c smtp_stream.c split_addr.c string_list.c sys_exits.c \
timed_ipc.c tok822_find.c tok822_node.c tok822_parse.c \
tok822_resolve.c tok822_rewrite.c tok822_tree.c xtext.c bounce_log.c \
flush_clnt.c mail_conf_time.c mbox_conf.c mbox_open.c abounce.c
flush_clnt.c mail_conf_time.c mbox_conf.c mbox_open.c abounce.c \
verp_sender.c
OBJS = been_here.o bounce.o canon_addr.o cleanup_strerror.o clnt_stream.o \
debug_peer.o debug_process.o defer.o deliver_completed.o \
deliver_flock.o deliver_pass.o deliver_request.o domain_list.o \
@ -38,7 +39,8 @@ OBJS = been_here.o bounce.o canon_addr.o cleanup_strerror.o clnt_stream.o \
sent.o smtp_stream.o split_addr.o string_list.o sys_exits.o \
timed_ipc.o tok822_find.o tok822_node.o tok822_parse.o \
tok822_resolve.o tok822_rewrite.o tok822_tree.o xtext.o bounce_log.o \
flush_clnt.o mail_conf_time.o mbox_conf.o mbox_open.o abounce.o
flush_clnt.o mail_conf_time.o mbox_conf.o mbox_open.o abounce.o \
verp_sender.o
HDRS = been_here.h bounce.h canon_addr.h cleanup_user.h clnt_stream.h \
config.h debug_peer.h debug_process.h defer.h deliver_completed.h \
deliver_flock.h deliver_pass.h deliver_request.h domain_list.h \
@ -54,7 +56,7 @@ HDRS = been_here.h bounce.h canon_addr.h cleanup_user.h clnt_stream.h \
recipient_list.h record.h resolve_clnt.h resolve_local.h \
rewrite_clnt.h sent.h smtp_stream.h split_addr.h string_list.h \
sys_exits.h timed_ipc.h tok822.h xtext.h bounce_log.h flush_clnt.h \
mbox_conf.h mbox_open.h abounce.h qmqp_proto.h
mbox_conf.h mbox_open.h abounce.h qmqp_proto.h verp_sender.h
TESTSRC = rec2stream.c stream2rec.c recdump.c
WARN = -W -Wformat -Wimplicit -Wmissing-prototypes \
-Wparentheses -Wstrict-prototypes -Wswitch -Wuninitialized \
@ -1036,6 +1038,11 @@ tok822_tree.o: ../../include/vstring.h
tok822_tree.o: ../../include/vbuf.h
tok822_tree.o: tok822.h
tok822_tree.o: resolve_clnt.h
verp_sender.o: verp_sender.c
verp_sender.o: ../../include/sys_defs.h
verp_sender.o: ../../include/vstring.h
verp_sender.o: ../../include/vbuf.h
verp_sender.o: verp_sender.h
xtext.o: xtext.c
xtext.o: ../../include/sys_defs.h
xtext.o: ../../include/vstream.h

View File

@ -14,6 +14,15 @@
/* void (*callback)(int status, char *context);
/* char *context;
/*
/* void abounce_flush_verp(flags, queue, id, sender, verp, callback, context)
/* int flags;
/* const char *queue;
/* const char *id;
/* const char *sender;
/* const char *verp;
/* void (*callback)(int status, char *context);
/* char *context;
/*
/* void adefer_flush(flags, queue, id, sender, callback, context)
/* int flags;
/* const char *queue;
@ -22,6 +31,15 @@
/* void (*callback)(int status, char *context);
/* char *context;
/*
/* void adefer_flush_verp(flags, queue, id, sender, verp, callback, context)
/* int flags;
/* const char *queue;
/* const char *id;
/* const char *sender;
/* const char *verp;
/* void (*callback)(int status, char *context);
/* char *context;
/*
/* void adefer_warn(flags, queue, id, sender, callback, context)
/* int flags;
/* const char *queue;
@ -38,10 +56,16 @@
/* the specified sender, including the bounce log that was
/* built with bounce_append().
/*
/* abounce_flush_verp() is like abounce_flush() but sends
/* one VERP style notification per undeliverable recipient.
/*
/* adefer_flush() bounces the specified message to
/* the specified sender, including the defer log that was
/* built with defer_append().
/*
/* adefer_flush_verp() is like adefer_flush() but sends
/* one VERP style notification per undeliverable recipient.
/*
/* adefer_warn() sends a "mail is delayed" notification to
/* the specified sender, including the defer log that was
/* built with defer_append().
@ -64,6 +88,8 @@
/* file has the same name as the original message file.
/* .IP sender
/* The sender envelope address.
/* .IP verp
/* VERP delimiter characters.
/* .IP callback
/* Name of a routine that receives the notification status as
/* documented for bounce_flush() or defer_flush().
@ -148,6 +174,60 @@ static void abounce_event(int unused_event, char *context)
abounce_done(ap, mail_scan(ap->fp, "%d", &status) == 1 ? status : -1);
}
/* abounce_request_verp - suspend pseudo thread until server reply event */
static void abounce_request_verp(const char *class, const char *service,
int command, int flags,
const char *queue, const char *id,
const char *sender, const char *verp,
ABOUNCE_FN callback,
char *context)
{
ABOUNCE *ap;
/*
* Save pseudo thread state. Connect to the server. Send the request and
* suspend the pseudo thread until the server replies (or dies).
*/
ap = (ABOUNCE *) mymalloc(sizeof(*ap));
ap->command = command;
ap->flags = flags;
ap->id = mystrdup(id);
ap->callback = callback;
ap->context = context;
ap->fp = mail_connect_wait(class, service);
if (mail_print(ap->fp, "%d %d %s %s %s %s %s", command,
flags, queue, id, sender, verp, MAIL_EOF) == 0
&& vstream_fflush(ap->fp) == 0) {
event_enable_read(vstream_fileno(ap->fp), abounce_event, (char *) ap);
} else {
abounce_done(ap, -1);
}
}
/* abounce_flush_verp - asynchronous bounce flush */
void abounce_flush_verp(int flags, const char *queue, const char *id,
const char *sender, const char *verp,
ABOUNCE_FN callback, char *context)
{
abounce_request_verp(MAIL_CLASS_PRIVATE, MAIL_SERVICE_BOUNCE,
BOUNCE_CMD_VERP, flags, queue, id, sender, verp,
callback, context);
}
/* adefer_flush_verp - asynchronous defer flush */
void adefer_flush_verp(int flags, const char *queue, const char *id,
const char *sender, const char *verp,
ABOUNCE_FN callback, char *context)
{
abounce_request_verp(MAIL_CLASS_PRIVATE, MAIL_SERVICE_DEFER,
BOUNCE_CMD_VERP, flags, queue, id, sender, verp,
callback, context);
}
/* abounce_request - suspend pseudo thread until server reply event */
static void abounce_request(const char *class, const char *service,

View File

@ -25,6 +25,9 @@ extern void abounce_flush(int, const char *, const char *, const char *, ABOUNCE
extern void adefer_flush(int, const char *, const char *, const char *, ABOUNCE_FN, char *);
extern void adefer_warn(int, const char *, const char *, const char *, ABOUNCE_FN, char *);
extern void abounce_flush_verp(int, const char *, const char *, const char *, const char *, ABOUNCE_FN, char *);
extern void adefer_flush_verp(int, const char *, const char *, const char *, const char *, ABOUNCE_FN, char *);
/* LICENSE
/* .ad
/* .fi

View File

@ -40,8 +40,8 @@ extern int vbounce_recip(int, const char *, const char *, const char *,
*/
#define BOUNCE_CMD_APPEND 0 /* append log */
#define BOUNCE_CMD_FLUSH 1 /* send log */
#define BOUNCE_CMD_WARN 2 /* send warning bounce, don't delete
* log */
#define BOUNCE_CMD_WARN 2 /* send warning, don't delete log */
#define BOUNCE_CMD_VERP 3 /* send log, verp style */
/*
* Flags.

View File

@ -24,6 +24,9 @@
/* BOUNCE_LOG *bounce_log_read(bp)
/* BOUNCE_LOG *bp;
/*
/* BOUNCE_LOG *bounce_log_delrcpt(bp)
/* BOUNCE_LOG *bp;
/*
/* void bounce_log_rewind(bp)
/* BOUNCE_LOG *bp;
/*
@ -31,8 +34,7 @@
/* BOUNCE_LOG *bp;
/* DESCRIPTION
/* This module implements a bounce/defer logfile API. Information
/* is sanitized for control and non-ASCII characters. Currently,
/* only the reading end is implemented.
/* is sanitized for control and non-ASCII characters.
/*
/* bounce_log_open() opens the named bounce or defer logfile
/* and returns a handle that must be used for further access.
@ -47,6 +49,9 @@
/* bounce_log_read() returns a null pointer when no recipient was read,
/* otherwise it returns its argument.
/*
/* bounce_log_delrcpt() marks the last accessed recipient record as
/* "deleted". This requires that the logfile is opened for update.
/*
/* bounce_log_rewind() is a helper that seeks to the first recipient
/* in an open bounce or defer logfile (skipping over recipients that
/* are marked as done). The result is 0 in case of success, -1 in case
@ -92,6 +97,7 @@
#include <sys_defs.h>
#include <string.h>
#include <ctype.h>
#include <unistd.h>
/* Utility library. */
@ -133,6 +139,7 @@ BOUNCE_LOG *bounce_log_open(const char *queue_name, const char *queue_id,
bp->fp = fp;
bp->buf = vstring_alloc(100);
bp->status = STREQ(queue_name, MAIL_QUEUE_DEFER) ? "4.0.0" : "5.0.0";
bp->offset = 0;
return (bp);
}
}
@ -145,7 +152,8 @@ BOUNCE_LOG *bounce_log_read(BOUNCE_LOG *bp)
char *text;
char *cp;
while (vstring_get_nonl(bp->buf, bp->fp) != VSTREAM_EOF) {
while ((bp->offset = vstream_ftell(bp->fp)),
(vstring_get_nonl(bp->buf, bp->fp) != VSTREAM_EOF)) {
if (STR(bp->buf)[0] == 0)
continue;
@ -155,6 +163,12 @@ BOUNCE_LOG *bounce_log_read(BOUNCE_LOG *bp)
*/
cp = printable(STR(bp->buf), '?');
/*
* Skip over deleted recipients.
*/
if (*cp == BOUNCE_LOG_STAT_DELETED)
continue;
/*
* Find the recipient address.
*/
@ -185,6 +199,21 @@ BOUNCE_LOG *bounce_log_read(BOUNCE_LOG *bp)
return (0);
}
/* bounce_log_delrcpt - mark recipient record as deleted */
BOUNCE_LOG *bounce_log_delrcpt(BOUNCE_LOG *bp)
{
long current_offset;
current_offset = vstream_ftell(bp->fp);
if (vstream_fseek(bp->fp, bp->offset, SEEK_SET) < 0)
msg_fatal("bounce logfile %s seek error: %m", VSTREAM_PATH(bp->fp));
VSTREAM_PUTC(BOUNCE_LOG_STAT_DELETED, bp->fp);
if (vstream_fseek(bp->fp, current_offset, SEEK_SET) < 0)
msg_fatal("bounce logfile %s seek error: %m", VSTREAM_PATH(bp->fp));
return (bp);
}
/* bounce_log_close - close bounce reader stream */
int bounce_log_close(BOUNCE_LOG *bp)

View File

@ -28,14 +28,18 @@ typedef struct {
const char *recipient; /* final recipient */
const char *status; /* recipient status */
const char *text; /* why undeliverable */
long offset; /* start of current record */
} BOUNCE_LOG;
extern BOUNCE_LOG *bounce_log_open(const char *, const char *, int, int);
extern BOUNCE_LOG *bounce_log_read(BOUNCE_LOG *);
extern BOUNCE_LOG *bounce_log_delrcpt(BOUNCE_LOG *);
extern int bounce_log_close(BOUNCE_LOG *);
#define bounce_log_rewind(bp) vstream_fseek((bp)->fp, 0L, SEEK_SET)
#define BOUNCE_LOG_STAT_DELETED 'D' /* deleted record */
/* LICENSE
/* .ad
/* .fi

View File

@ -1271,6 +1271,14 @@ extern int var_qmqpd_timeout;
#define DEF_QMTPD_ERR_SLEEP "5s"
extern int var_qmqpd_err_sleep;
/*
* VERP, more DJB intellectual cross-pollination. However, we prefer + as
* the default recipient delimiter.
*/
#define VAR_VERP_DELIM "default_verp_delimiters"
#define DEF_VERP_DELIM "+="
extern char *var_verp_delim;
/* LICENSE
/* .ad
/* .fi

View File

@ -15,7 +15,7 @@
* Version of this program.
*/
#define VAR_MAIL_VERSION "mail_version"
#define DEF_MAIL_VERSION "Snapshot-20010707"
#define DEF_MAIL_VERSION "Snapshot-20010709"
extern char *var_mail_version;
/* LICENSE

View File

@ -56,6 +56,7 @@ REC_TYPE_NAME rec_type_names[] = {
REC_TYPE_RRTO, "return_receipt",
REC_TYPE_ERTO, "errors_to",
REC_TYPE_PRIO, "priority",
REC_TYPE_VERP, "verp_delimiters",
REC_TYPE_END, "message_end",
0, 0,
};

View File

@ -45,6 +45,7 @@
#define REC_TYPE_RRTO 'r' /* return-receipt, from headers */
#define REC_TYPE_ERTO 'e' /* errors-to, from headers */
#define REC_TYPE_PRIO 'P' /* priority */
#define REC_TYPE_VERP 'V' /* VERP delimiters */
#define REC_TYPE_END 'E' /* terminator, required */
@ -53,7 +54,7 @@
* record groups. The first member in each set is the record type that
* indicates the end of that record group.
*/
#define REC_TYPE_ENVELOPE "MCTFILSDRW"
#define REC_TYPE_ENVELOPE "MCTFILSDRWV"
#define REC_TYPE_CONTENT "XLN"
#define REC_TYPE_EXTRACT "EDRPre"
#define REC_TYPE_NOEXTRACT "E"

View File

@ -67,7 +67,7 @@ char *split_addr(char *localpart, int delimiter)
/*
* Backwards compatibility: don't split owner-foo or foo-request.
*/
if (var_ownreq_special != 0) {
if (delimiter == '-' && var_ownreq_special != 0) {
if (strncasecmp(localpart, "owner-", 6) == 0)
return (0);
if ((len = strlen(localpart) - 8) > 0

View File

@ -0,0 +1,83 @@
/*++
/* NAME
/* verp_sender 3
/* SUMMARY
/* quote local part of mailbox
/* SYNOPSIS
/* #include <verp_sender.h>
/*
/* VSTRING *verp_sender(dst, delims, sender, recipient)
/* VSTRING *dst;
/* const char *delims;
/* const char *sender;
/* const char *recipient;
/* DESCRIPTION
/* verp_sender() encodes the recipient address in the sender
/* address, using the specified delimiters. For example,
/* with delims +=, sender \fIprefix@origin\fR, and
/* recipient \fIuser@domain\fR the result is
/* \fIprefix+user=domain@origin\fR.
/*
/* Arguments:
/* .IP dst
/* The result. The buffer is null terminated.
/* .IP delims
/* VERP formatting characters.
/* .IP sender
/* Sender envelope address.
/* .IP recipient
/* Recipient envelope address.
/* LICENSE
/* .ad
/* .fi
/* The Secure Mailer license must be distributed with this software.
/* AUTHOR(S)
/* Wietse Venema
/* IBM T.J. Watson Research
/* P.O. Box 704
/* Yorktown Heights, NY 10598, USA
/*--*/
/* System library. */
#include <sys_defs.h>
#include <string.h>
/* Utility library. */
#include <vstring.h>
/* Global library. */
#include <verp_sender.h>
/* verp_sender - encode recipient into envelope sender address */
VSTRING *verp_sender(VSTRING *buf, const char *delimiters,
const char *sender, const char *recipient)
{
int send_local_len;
int rcpt_local_len;
const char *cp;
/*
* Change prefix@origin into prefix+user=domain@origin.
*/
send_local_len = ((cp = strrchr(sender, '@')) ?
cp - sender : strlen(sender));
rcpt_local_len = ((cp = strrchr(recipient, '@')) ?
cp - recipient : strlen(recipient));
vstring_strncpy(buf, sender, send_local_len);
VSTRING_ADDCH(buf, delimiters[0] & 0xff);
vstring_strncat(buf, recipient, rcpt_local_len);
if (recipient[rcpt_local_len] && recipient[rcpt_local_len + 1]) {
VSTRING_ADDCH(buf, delimiters[1] & 0xff);
vstring_strcat(buf, recipient + rcpt_local_len + 1);
}
if (sender[send_local_len] && sender[send_local_len + 1]) {
VSTRING_ADDCH(buf, '@');
vstring_strcat(buf, sender + send_local_len + 1);
}
VSTRING_TERMINATE(buf);
return (buf);
}

View File

@ -0,0 +1,35 @@
#ifndef _VERP_SENDER_H_INCLUDED_
#define _VERP_SENDER_H_INCLUDED_
/*++
/* NAME
/* verp_sender 3h
/* SUMMARY
/* encode recipient into sender, VERP style
/* SYNOPSIS
/* #include "verp_sender.h"
/* DESCRIPTION
/* .nf
/*
* Utility library.
*/
#include <vstring.h>
/*
* External interface.
*/
extern VSTRING *verp_sender(VSTRING *, const char *, const char *, const char *);
/* LICENSE
/* .ad
/* .fi
/* The Secure Mailer license must be distributed with this software.
/* AUTHOR(S)
/* Wietse Venema
/* IBM T.J. Watson Research
/* P.O. Box 704
/* Yorktown Heights, NY 10598, USA
/*--*/
#endif

View File

@ -267,6 +267,7 @@ struct QMGR_MESSAGE {
char *queue_name; /* queue name */
char *queue_id; /* queue file */
char *sender; /* complete address */
char *verp_delims; /* VERP delimiters */
char *errors_to; /* error report address */
char *return_receipt; /* confirm receipt address */
char *filter_xport; /* filtering transport */

View File

@ -275,12 +275,21 @@ void qmgr_active_done(QMGR_MESSAGE *message)
} else {
if (msg_verbose)
msg_info("%s: bounce %s", myname, message->queue_id);
abounce_flush(BOUNCE_FLAG_KEEP,
message->queue_name,
message->queue_id,
message->errors_to,
qmgr_active_done_2_bounce_flush,
(char *) message);
if (message->verp_delims == 0)
abounce_flush(BOUNCE_FLAG_KEEP,
message->queue_name,
message->queue_id,
message->errors_to,
qmgr_active_done_2_bounce_flush,
(char *) message);
else
abounce_flush_verp(BOUNCE_FLAG_KEEP,
message->queue_name,
message->queue_id,
message->errors_to,
message->verp_delims,
qmgr_active_done_2_bounce_flush,
(char *) message);
return;
}
}
@ -353,12 +362,21 @@ static void qmgr_active_done_2_generic(QMGR_MESSAGE *message)
if (event_time() > message->arrival_time + var_max_queue_time) {
if (msg_verbose)
msg_info("%s: too old, bouncing %s", myname, message->queue_id);
adefer_flush(BOUNCE_FLAG_KEEP,
message->queue_name,
message->queue_id,
message->errors_to,
qmgr_active_done_3_defer_flush,
(char *) message);
if (message->verp_delims == 0)
adefer_flush(BOUNCE_FLAG_KEEP,
message->queue_name,
message->queue_id,
message->errors_to,
qmgr_active_done_3_defer_flush,
(char *) message);
else
adefer_flush_verp(BOUNCE_FLAG_KEEP,
message->queue_name,
message->queue_id,
message->errors_to,
message->verp_delims,
qmgr_active_done_3_defer_flush,
(char *) message);
return;
} else if (message->warn_time > 0
&& event_time() > message->warn_time) {

View File

@ -68,6 +68,7 @@
#include <recipient_list.h>
#include <mail_params.h>
#include <deliver_request.h>
#include <verp_sender.h>
/* Application-specific. */
@ -124,6 +125,22 @@ static int qmgr_deliver_send_request(QMGR_ENTRY *entry, VSTREAM *stream)
QMGR_RCPT *recipient;
QMGR_MESSAGE *message = entry->message;
char *cp;
VSTRING *sender_buf = 0;
char *sender;
/*
* If variable envelope return path is requested, change prefix+@origin
* into prefix+user=domain@origin. Note that with VERP there is only one
* recipient per delivery.
*/
if (message->verp_delims == 0) {
sender = message->sender;
} else {
sender_buf = vstring_alloc(100);
verp_sender(sender_buf, message->verp_delims,
message->sender, list.info->address);
sender = vstring_str(sender_buf);
}
/*
* With mail transports that accept only one recipient per delivery, the
@ -136,9 +153,11 @@ static int qmgr_deliver_send_request(QMGR_ENTRY *entry, VSTREAM *stream)
message->queue_name, message->queue_id,
message->data_offset, message->data_size,
(cp = strrchr(entry->queue->name, '@')) != 0 && cp[1] ? cp + 1 :
entry->queue->name, message->sender,
entry->queue->name, sender,
message->errors_to, message->return_receipt,
message->arrival_time);
if (sender_buf != 0)
vstring_free(sender_buf);
for (recipient = list.info; recipient < list.info + list.len; recipient++)
mail_print(stream, "%ld %s", recipient->offset, recipient->address);
mail_print(stream, "%s", "0");

View File

@ -161,6 +161,7 @@ static QMGR_MESSAGE *qmgr_message_create(const char *queue_name,
message->warn_offset = 0;
message->warn_time = 0;
message->rcpt_offset = 0;
message->verp_delims = 0;
message->unread_offset = 0;
qmgr_rcpt_list_init(&message->rcpt_list);
message->rcpt_count = 0;
@ -423,6 +424,14 @@ static int qmgr_message_read(QMGR_MESSAGE *message)
message->warn_offset = curr_offset;
message->warn_time = atol(start);
}
} else if (rec_type == REC_TYPE_VERP) {
if (strlen(start) != 2) {
msg_warn("%s: bad VERP record length: \"%s\"",
message->queue_id, start);
} else {
message->single_rcpt = 1;
message->verp_delims = mystrdup(start);
}
}
} while (rec_type > 0 && rec_type != REC_TYPE_END);
@ -902,6 +911,8 @@ void qmgr_message_free(QMGR_MESSAGE *message)
myfree(message->queue_name);
if (message->sender)
myfree(message->sender);
if (message->verp_delims)
myfree(message->verp_delims);
if (message->errors_to)
myfree(message->errors_to);
if (message->return_receipt)

View File

@ -136,6 +136,7 @@ qmgr_deliver.o: ../../include/mail_proto.h
qmgr_deliver.o: ../../include/recipient_list.h
qmgr_deliver.o: ../../include/mail_params.h
qmgr_deliver.o: ../../include/deliver_request.h
qmgr_deliver.o: ../../include/verp_sender.h
qmgr_deliver.o: qmgr.h
qmgr_deliver.o: ../../include/scan_dir.h
qmgr_deliver.o: ../../include/maps.h

View File

@ -227,6 +227,7 @@ struct QMGR_MESSAGE {
char *queue_name; /* queue name */
char *queue_id; /* queue file */
char *sender; /* complete address */
char *verp_delims; /* VERP delimiters */
char *errors_to; /* error report address */
char *return_receipt; /* confirm receipt address */
char *filter_xport; /* filtering transport */

View File

@ -275,12 +275,21 @@ void qmgr_active_done(QMGR_MESSAGE *message)
} else {
if (msg_verbose)
msg_info("%s: bounce %s", myname, message->queue_id);
abounce_flush(BOUNCE_FLAG_KEEP,
message->queue_name,
message->queue_id,
message->errors_to,
qmgr_active_done_2_bounce_flush,
(char *) message);
if (message->verp_delims == 0)
abounce_flush(BOUNCE_FLAG_KEEP,
message->queue_name,
message->queue_id,
message->errors_to,
qmgr_active_done_2_bounce_flush,
(char *) message);
else
abounce_flush_verp(BOUNCE_FLAG_KEEP,
message->queue_name,
message->queue_id,
message->errors_to,
message->verp_delims,
qmgr_active_done_2_bounce_flush,
(char *) message);
return;
}
}
@ -353,12 +362,21 @@ static void qmgr_active_done_2_generic(QMGR_MESSAGE *message)
if (event_time() > message->arrival_time + var_max_queue_time) {
if (msg_verbose)
msg_info("%s: too old, bouncing %s", myname, message->queue_id);
adefer_flush(BOUNCE_FLAG_KEEP,
message->queue_name,
message->queue_id,
message->errors_to,
qmgr_active_done_3_defer_flush,
(char *) message);
if (message->verp_delims == 0)
adefer_flush(BOUNCE_FLAG_KEEP,
message->queue_name,
message->queue_id,
message->errors_to,
qmgr_active_done_3_defer_flush,
(char *) message);
else
adefer_flush_verp(BOUNCE_FLAG_KEEP,
message->queue_name,
message->queue_id,
message->errors_to,
message->verp_delims,
qmgr_active_done_3_defer_flush,
(char *) message);
return;
} else if (message->warn_time > 0
&& event_time() > message->warn_time) {

View File

@ -63,6 +63,7 @@
#include <recipient_list.h>
#include <mail_params.h>
#include <deliver_request.h>
#include <verp_sender.h>
/* Application-specific. */
@ -119,6 +120,22 @@ static int qmgr_deliver_send_request(QMGR_ENTRY *entry, VSTREAM *stream)
QMGR_RCPT *recipient;
QMGR_MESSAGE *message = entry->message;
char *cp;
VSTRING *sender_buf = 0;
char *sender;
/*
* If variable envelope return path is requested, change prefix+@origin
* into prefix+user=domain@origin. Note that with VERP there is only one
* recipient per delivery.
*/
if (message->verp_delims == 0) {
sender = message->sender;
} else {
sender_buf = vstring_alloc(100);
verp_sender(sender_buf, message->verp_delims,
message->sender, list.info->address);
sender = vstring_str(sender_buf);
}
/*
* With mail transports that accept only one recipient per delivery, the
@ -131,9 +148,11 @@ static int qmgr_deliver_send_request(QMGR_ENTRY *entry, VSTREAM *stream)
message->queue_name, message->queue_id,
message->data_offset, message->data_size,
(cp = strrchr(entry->queue->name, '@')) != 0 && cp[1] ? cp + 1 :
entry->queue->name, message->sender,
entry->queue->name, sender,
message->errors_to, message->return_receipt,
message->arrival_time);
if (sender_buf != 0)
vstring_free(sender_buf);
for (recipient = list.info; recipient < list.info + list.len; recipient++)
mail_print(stream, "%ld %s", recipient->offset, recipient->address);
mail_print(stream, "%s", "0");

View File

@ -151,6 +151,7 @@ static QMGR_MESSAGE *qmgr_message_create(const char *queue_name,
message->warn_offset = 0;
message->warn_time = 0;
message->rcpt_offset = 0;
message->verp_delims = 0;
qmgr_rcpt_list_init(&message->rcpt_list);
return (message);
}
@ -303,6 +304,14 @@ static int qmgr_message_read(QMGR_MESSAGE *message)
message->warn_offset = curr_offset;
message->warn_time = atol(start);
}
} else if (rec_type == REC_TYPE_VERP) {
if (strlen(start) != 2) {
msg_warn("%s: bad VERP record length: \"%s\"",
message->queue_id, start);
} else {
message->single_rcpt = 1;
message->verp_delims = mystrdup(start);
}
}
} while (rec_type > 0 && rec_type != REC_TYPE_END);
@ -737,6 +746,8 @@ void qmgr_message_free(QMGR_MESSAGE *message)
myfree(message->queue_name);
if (message->sender)
myfree(message->sender);
if (message->verp_delims)
myfree(message->verp_delims);
if (message->errors_to)
myfree(message->errors_to);
if (message->return_receipt)

View File

@ -210,11 +210,34 @@ static void qmqpd_read_content(QMQPD_STATE *state)
static void qmqpd_copy_sender(QMQPD_STATE *state)
{
char *end_prefix;
char *end_origin;
int verp_requested;
/*
* If the sender address looks like prefix-@origin-@[], then request
* variable envelope return path delivery, with an envelope sender
* address of prefix@origin, and with VERP delimiters of - and =. This
* way, the recipients will see envelope sender addresses that look like:
* prefix-user=domain@origin.
*/
state->where = "receiving sender address";
netstring_get(state->client, state->buf, var_line_limit);
verp_requested = ((end_prefix = strstr(STR(state->buf), "-@")) != 0
&& (end_origin = strstr(end_prefix + 2, "-@")) != 0
&& strncmp(end_origin + 2, "[]", 2) == 0
&& vstring_end(state->buf) == end_origin + 4);
if (verp_requested) {
memcpy(end_prefix, end_prefix + 1, end_origin - end_prefix - 1);
vstring_truncate(state->buf, end_origin - STR(state->buf) - 1);
}
if (state->err == CLEANUP_STAT_OK
&& REC_PUT_BUF(state->cleanup, REC_TYPE_FROM, state->buf) < 0)
state->err = CLEANUP_STAT_WRITE;
if (verp_requested)
if (state->err == CLEANUP_STAT_OK
&& rec_put(state->cleanup, REC_TYPE_VERP, "-=", 2) < 0)
state->err = CLEANUP_STAT_WRITE;
state->sender = mystrndup(STR(state->buf), LEN(state->buf));
}

View File

@ -76,6 +76,11 @@
/* \fBdebug_peer_level\fR configuration parameters instead.
/* .IP "\fB-U\fR (ignored)"
/* Initial user submission.
/* .IP \fB-V\fR
/* Variable Envelope Return Path. Given an envelope sender address
/* \fIprefix\fR-@\fIorigin\fR, each recipient \fIuser@domain\fR
/* receives mail with a personalized envelope sender address
/* \fIprefix\fB-\fIuser=domain\fR@\fIorigin\fR.
/* .IP \fB-bd\fR
/* Go into daemon mode. This mode of operation is implemented by
/* executing the \fBpostfix start\fR command.
@ -319,6 +324,11 @@ static void sendmail_cleanup(void);
#define SM_FLAG_DEFAULT (SM_FLAG_AEOF)
/*
* VERP support.
*/
char *verp_delims;
/*
* Silly little macros (SLMs).
*/
@ -414,6 +424,10 @@ static void enqueue(const int flags, const char *sender, const char *full_name,
if (full_name || (full_name = fullname()) != 0)
rec_fputs(dst, REC_TYPE_FULL, full_name);
rec_fputs(dst, REC_TYPE_FROM, saved_sender);
if (verp_delims && *saved_sender == 0)
msg_fatal("-V option requires non-null sender address");
if (verp_delims)
rec_fputs(dst, REC_TYPE_VERP, verp_delims);
if (recipients) {
for (cpp = recipients; *cpp != 0; cpp++) {
tree = tok822_parse(*cpp);
@ -794,7 +808,7 @@ int main(int argc, char **argv)
optind++;
continue;
}
if ((c = GETOPT(argc, argv, "B:C:F:GIN:R:UX:b:ce:f:h:imno:p:r:q:tvx")) <= 0)
if ((c = GETOPT(argc, argv, "B:C:F:GIN:R:UVX:b:ce:f:h:imno:p:r:q:tvx")) <= 0)
break;
switch (c) {
default:
@ -817,6 +831,9 @@ int main(int argc, char **argv)
break;
case 'R': /* DSN */
break;
case 'V': /* VERP */
verp_delims = "";
break;
case 'b':
switch (*optarg) {
default:

View File

@ -32,8 +32,8 @@
/* STANDARDS
/* RFC 821 (SMTP protocol)
/* RFC 1123 (Host requirements)
/* RFC 1651 (SMTP service extensions)
/* RFC 1652 (8bit-MIME transport)
/* RFC 1869 (SMTP service extensions)
/* RFC 1854 (SMTP Pipelining)
/* RFC 1870 (Message Size Declaration)
/* RFC 1985 (ETRN command)
@ -363,6 +363,12 @@ char *smtpd_path;
#define STR(x) vstring_str(x)
#define LEN(x) VSTRING_LEN(x)
/*
* VERP command name.
*/
#define VERP_CMD "XVERP"
#define VERP_CMD_LEN 5
/*
* Forward declarations.
*/
@ -459,6 +465,7 @@ static int ehlo_cmd(SMTPD_STATE *state, int argc, SMTPD_TOKEN *argv)
smtpd_chat_reply(state, "250-AUTH=%s", state->sasl_mechanism_list);
}
#endif
smtpd_chat_reply(state, "250-%s", VERP_CMD);
smtpd_chat_reply(state, "250 8BITMIME");
return (0);
}
@ -626,6 +633,7 @@ static int mail_cmd(SMTPD_STATE *state, int argc, SMTPD_TOKEN *argv)
char *err;
int narg;
char *arg;
char *verp_delims = 0;
state->msg_size = 0;
@ -680,12 +688,27 @@ static int mail_cmd(SMTPD_STATE *state, int argc, SMTPD_TOKEN *argv)
return (-1);
}
#endif
} else if (strcasecmp(arg, VERP_CMD) == 0) {
verp_delims = "";
} else if (strncasecmp(arg, VERP_CMD, VERP_CMD_LEN) == 0
&& arg[VERP_CMD_LEN] == '=') {
verp_delims = arg + VERP_CMD_LEN + 1;
if (strlen(verp_delims) != 2) {
state->error_mask |= MAIL_ERROR_PROTOCOL;
smtpd_chat_reply(state, "501 Bad %s parameter: %s",
VERP_CMD, arg);
return (-1);
}
} else {
state->error_mask |= MAIL_ERROR_PROTOCOL;
smtpd_chat_reply(state, "555 Unsupported option: %s", arg);
return (-1);
}
}
if (verp_delims && argv[2].strval[0] == 0) {
smtpd_chat_reply(state, "503 Error: XVERP requires non-null sender");
return (-1);
}
state->time = time((time_t *) 0);
if (SMTPD_STAND_ALONE(state) == 0
&& var_smtpd_delay_reject == 0
@ -718,6 +741,8 @@ static int mail_cmd(SMTPD_STATE *state, int argc, SMTPD_TOKEN *argv)
if (*var_filter_xport)
rec_fprintf(state->cleanup, REC_TYPE_FILT, "%s", var_filter_xport);
rec_fputs(state->cleanup, REC_TYPE_FROM, argv[2].strval);
if (verp_delims)
rec_fputs(state->cleanup, REC_TYPE_VERP, verp_delims);
state->sender = mystrdup(argv[2].strval);
smtpd_chat_reply(state, "250 Ok");
return (0);

View File

@ -8,7 +8,7 @@
/* \fBqmqp-sink\fR [\fB-cv\fR] [\fB-x \fItime\fR]
/* [\fBinet:\fR][\fIhost\fR]:\fIport\fR \fIbacklog\fR
/*
/* \fBqmqp-sink\fR [\fB-cv\fR]
/* \fBqmqp-sink\fR [\fB-cv\fR] [\fB-x \fItime\fR]
/* \fBunix:\fR\fIpathname\fR \fIbacklog\fR
/* DESCRIPTION
/* \fIqmqp-sink\fR listens on the named host (or address) and port.