]> git.ipfire.org Git - thirdparty/postfix.git/commitdiff
snapshot-20010709
authorWietse Venema <wietse@porcupine.org>
Mon, 9 Jul 2001 05:00:00 +0000 (00:00 -0500)
committerViktor Dukhovni <viktor@dukhovni.org>
Tue, 5 Feb 2013 06:27:21 +0000 (06:27 +0000)
44 files changed:
postfix/.indent.pro
postfix/COMPATIBILITY
postfix/HISTORY
postfix/INSTALL
postfix/QMQP_README [moved from postfix/README_QMQP with 78% similarity]
postfix/RELEASE_NOTES
postfix/VERP_README [new file with mode: 0644]
postfix/html/sendmail.1.html
postfix/html/smtpd.8.html
postfix/man/man1/sendmail.1
postfix/man/man8/smtpd.8
postfix/src/bounce/Makefile.in
postfix/src/bounce/bounce.c
postfix/src/bounce/bounce_notify_util.c
postfix/src/bounce/bounce_notify_verp.c [new file with mode: 0644]
postfix/src/bounce/bounce_service.h
postfix/src/cleanup/cleanup_envelope.c
postfix/src/cleanup/cleanup_init.c
postfix/src/global/Makefile.in
postfix/src/global/abounce.c
postfix/src/global/abounce.h
postfix/src/global/bounce.h
postfix/src/global/bounce_log.c
postfix/src/global/bounce_log.h
postfix/src/global/mail_params.h
postfix/src/global/mail_version.h
postfix/src/global/rec_type.c
postfix/src/global/rec_type.h
postfix/src/global/split_addr.c
postfix/src/global/verp_sender.c [new file with mode: 0644]
postfix/src/global/verp_sender.h [new file with mode: 0644]
postfix/src/nqmgr/qmgr.h
postfix/src/nqmgr/qmgr_active.c
postfix/src/nqmgr/qmgr_deliver.c
postfix/src/nqmgr/qmgr_message.c
postfix/src/qmgr/Makefile.in
postfix/src/qmgr/qmgr.h
postfix/src/qmgr/qmgr_active.c
postfix/src/qmgr/qmgr_deliver.c
postfix/src/qmgr/qmgr_message.c
postfix/src/qmqpd/qmqpd.c
postfix/src/sendmail/sendmail.c
postfix/src/smtpd/smtpd.c
postfix/src/smtpstone/qmqp-sink.c

index 3dcb109b1040775ca3effcb70ba40b414f1a32b9..23f1f46ce679ad352585cb96b4f2949c11ff2e68 100644 (file)
@@ -1,3 +1,4 @@
+-TABOUNCE
 -TALIAS_TOKEN
 -TARGV
 -TBH_TABLE
index bb754a363b156510168de80914fa9d55fde07fb2..76803407afd4208a801926bc66ff437973fd7b91 100644 (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
index 17a0dfc47a31a8aeb7cfe82f2e80f228cea759cb..1b82ef459abae0a7200267dc8ac65f56473d1c2e 100644 (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.
index da6526c744c78d75bcc2c3a7555e3301b0058bd5..6e74ebcbf16cd8533c633ce22b309120b733d5e6 100644 (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
 
similarity index 78%
rename from postfix/README_QMQP
rename to postfix/QMQP_README
index 666f79e418fae2fd978ca08b4335ce4a78119a70..8f9ed077617ece92fe3b9fb42e073ee52137e2af 100644 (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.
index 71b51f4ac37f1815c0439e23d88dbc8d53ee2f04..48abe2be6fc54f629629859d4677fbca6edb1d5c 100644 (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
diff --git a/postfix/VERP_README b/postfix/VERP_README
new file mode 100644 (file)
index 0000000..d8c9e2f
--- /dev/null
@@ -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.
index 1d414d2df19d1f1a6ff8e903381339c7924a56e0..1c6848aebc94798b85c8e8397b62eed77dc27b85 100644 (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>
index 2e6c95d2137ec24c3d6dabfc19cfea94fe26c5ae..23ec5069faac9b81f5ea66384ec6186167df179e 100644 (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)
index b7dade23f183bb15a713b2fc395023f6b0368b2f..a3a01dda58b189658a197391e000df9c045bbe46 100644 (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.
index 9c3b9236b4aa15696fccf410f1c49ce2b381c499..2a979fee3694a1e14c292cc6a8cd05d8c18dee8c 100644 (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)
index 4172afd76ce0eeaa1a215b9cd3973a1d797b1cf7..cad8498f0fc75a7fc3558f18068eeef0805b4817 100644 (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
index eeedb7b141b09a67c21715606f6f975027457602..75a62032eebae4881c25cae941ff60dff4234f85 100644 (file)
 /* System library. */
 
 #include <sys_defs.h>
+#include <string.h>
+
+#ifdef STRCASECMP_IN_STRINGS_H
+#include <strings.h>
+#endif
 
 /* Utility library. */
 
 #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);
 }
 
index cc5e15f6bf06afd3d8f59e4f370aceac876e2fe6..efa68ce4988077f933288d4566a0e77af6ebe960 100644 (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);
diff --git a/postfix/src/bounce/bounce_notify_verp.c b/postfix/src/bounce/bounce_notify_verp.c
new file mode 100644 (file)
index 0000000..f1880ba
--- /dev/null
@@ -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);
+}
index 369a3da0d0898fe0421c62bb4363eb83df32e9a0..858609b7970aaa2ad245572f1c605d4c8f5babef 100644 (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);
index 86758c146ad316789a400abbef9d698f346ce39b..be3f727b64c343cdfd61719ac61e96dcafc032bc 100644 (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);
     }
index 0cb5812c5bca85781931d5018fc3c17dfb00824b..29336362c00f073ceb0f5e24325cd2a4283731dc 100644 (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,
 };
 
index b14c251a302aacfa883dabd9ac15b822c87aed2a..da8e25a62a86908fcedefc58082e170a350b0d09 100644 (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
index bfda481d51dba774d2df1cb8ddff6fe9f8a37d36..6f23020812e59d55b58fae3e27e5f96ea4db8ab0 100644 (file)
 /*     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;
 /*     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;
 /*     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,
index 8932248751cc4715ad78ed8e86260cd8c256e723..69542383a5b75fd1fce459b6dccb2d4f106a41e1 100644 (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
index 43ebc6040bbd5b00a26410d97e2f0545e5b1ca48..018b91a4fe6a444452f98d2af1587c96a2df4174 100644 (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.
index 566ad48783b0653157d9a65f205af62ac6cb4cf5..f0f841532077792ebaa7ad501edbf07507cbd007 100644 (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)
index 7b949e9517593fc1d2b9ee1281ce4c1752d4c820..0f91c6f8d68de142c191a161cdcd5ae5f0cba2c8 100644 (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
index 1cfaf8755a8123c9d8bc2b342ecef951c51dcf95..c1c5b521bf844b8e81cc38e661d01850396230d4 100644 (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
index acd597ae939201be2d519a648dbc6c07fd7c7c81..7b5aa94c586a28dffd22c078677f0814885de353 100644 (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
index c86a461aa1464b2c8a51010ce715b3e75e554add..3b8f46f059f19240c14f96ea0b2f0404e3a7cd8f 100644 (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,
 };
index 4412ee37da4593c91bbe2e286f722ab4c18bdea7..1a89d8c959fbb39906003ce521b668bc798d5a9e 100644 (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"
index d9db5777bd66d169003487ff04b1fbf98329f83c..3b1fbb3b3e95fe90eca8d7b676cdc2602b770d0b 100644 (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
diff --git a/postfix/src/global/verp_sender.c b/postfix/src/global/verp_sender.c
new file mode 100644 (file)
index 0000000..8e88eb4
--- /dev/null
@@ -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);
+}
diff --git a/postfix/src/global/verp_sender.h b/postfix/src/global/verp_sender.h
new file mode 100644 (file)
index 0000000..11beb67
--- /dev/null
@@ -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
index e32c64bf4f15973b29d19dd436318615263a581b..2afbf440b7f3b88907373fbe727cb2d784e4906d 100644 (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 */
index 404c4eda986d0606ea6febceafb868a7e3711ed9..847f42713646e552d91e97aeafafd90d19a6766d 100644 (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) {
index 8a06746ce63fe2d4b8fa5e085ef13350252de3f3..d8e513122669821e5b516c4f2896d2978fe31d38 100644 (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");
index 5d56eba039ac5a586c0054902209ff125ba2d1bf..1fcbb6d27b46a7b1583f12b0ac5013e0ab0cddcb 100644 (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)
index c37f1d78fd0c6bf3e97d411009215d7ef6b6aeec..db38855670f24f8e4b3b425b3bcaa9aa46d9a3ab 100644 (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
index 45ceac8e99bcd50f03911fde770985d075d7fce9..db55af124b3a3679a52b60b7a798e98b198df757 100644 (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 */
index 404c4eda986d0606ea6febceafb868a7e3711ed9..847f42713646e552d91e97aeafafd90d19a6766d 100644 (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) {
index 1a605dd1f052914f228402b7dc400a91268a85f8..3614690cae0405580cb462f70ab500780db9fcb6 100644 (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");
index e696fb4a768133a6d35d0b59a7bcdcea3bfeaf3b..db88b63fc892ab004f2977d8a4f6063609378e0b 100644 (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)
index 281e472c21caba5e6cc5dddafa846aa8e45fab29..5117d399cd02e9eaada382322ac73f0d37366670 100644 (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));
 }
 
index a3daf6422a761d0ee2ee677c2484ae00fec2f67d..a11837ef48b8079196e29579a3ca95d8cc8222f1 100644 (file)
 /*     \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:
index 5353a8e6099ea8fdb818eb70686e93278f706c16..3bc59cb3ddaa3df93e32e796dfc833b9333b3bcd 100644 (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);
index 53b39584cd35a8bb973a52298ca815f749ed44da..1c696b8991b569377593409e69271a44befeb61e 100644 (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.