From: Wietse Venema Date: Sun, 22 Apr 2007 05:00:00 +0000 (-0500) Subject: postfix-2.5-20070422 X-Git-Tag: v2.5.0-RC1~47 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=7f178c7b2479303f5cda8d1868793d051ad6aeb1;p=thirdparty%2Fpostfix.git postfix-2.5-20070422 --- diff --git a/postfix/.indent.pro b/postfix/.indent.pro index ea48c48d4..fc69659c2 100644 --- a/postfix/.indent.pro +++ b/postfix/.indent.pro @@ -46,6 +46,7 @@ -TCRYPTO_EX_DATA -TCTABLE -TCTABLE_ENTRY +-TDELIVERED_HDR_INFO -TDELIVER_ATTR -TDELIVER_REQUEST -TDELTA_TIME diff --git a/postfix/HISTORY b/postfix/HISTORY index 37f27edab..e2494e371 100644 --- a/postfix/HISTORY +++ b/postfix/HISTORY @@ -13400,8 +13400,8 @@ Apologies for any names omitted. 20070331 Bugfix (introduced Postfix 2.3): segfault with HOLD action - in header/body_checks on 64-bit platforms. File: - cleanup/cleanup_api.c. + in access/header_checks/body_checks on 64-bit platforms. + File: cleanup/cleanup_api.c. 20070402 @@ -13416,14 +13416,58 @@ Apologies for any names omitted. broke with "fcntl F_DUPFD: Invalid argument" on 64-bit Solaris. Files: master/multi_server.c, *qmgr/qmgr_transport.c. +20070405 + + Feature: BCC access/policy action, to demonstrate that this + is not a good feature. The action's behavior is non-intuitive + and requires too much documentation to explain. It's + therefore snapshot only. File: smtpd/smtpd_check.c. + +20070414 + + Cleanup: expire cached results from addres rewriting, address + resolution, and from transport map lookups. Results expire + after 30 seconds; short enough that it doesn't freak out + people who run the same test repeatedly, and long enough + that it doesn't upset other people with continuous streams + of "*" transport map lookups. Files: global/rewrite_clnt.c, + global/resolve_clnt.c, trivial-rewrite/transport.c. + +20070421 + + Cleanup: on (Linux) platforms that cripple signal handlers + with deadlock, "postfix stop" now forcefully stops all the + processes in the master's process group, not just the master + process alone. File: conf/postfix-script. + +20070422 + + Cleanup: the "Delivered-To:" loop detection implementation + was moved from the local(8) delivery agent to the library, + where it can also be used by other delivery agents. Files: + global/delivered_hdr.[hc]. + + Safety: the "Delivered-To:" loop detection implementation + keeps state for no more than 1000 "Delivered-To:" headers. + + Feature: $domain command-line macro support, to get access + to the recipient address domain portion. Based on code by + Koen Vermeer. File: pipe/pipe.c. + + Cleanup: suport for "Delivered-To:" loop detection in the + pipe(8) delivery agent. This follows a general principle: + if a program creates the "Delivered-To:" header, then it + is also responsible for "Delivered-To:" loop detection. + File pipe/pipe.c. + Wish list: Remove defer(8) and trace(8) references and man pages. These are services not program names. Bind all deliveries to the same local delivery process, - making Postfix perform as poorly as monolithic mailers, - but giving a possibility to eliminate duplicate deliveries. + making Postfix perform as poorly as monolithic mailers, but + giving a possibility to eliminate duplicate deliveries. Maybe declare loop when resolve_local(mxhost) is true? diff --git a/postfix/README_FILES/SMTPD_POLICY_README b/postfix/README_FILES/SMTPD_POLICY_README index 68e04db0c..bbcb28ef4 100644 --- a/postfix/README_FILES/SMTPD_POLICY_README +++ b/postfix/README_FILES/SMTPD_POLICY_README @@ -43,6 +43,7 @@ terminated by an empty line. Here is an example of all the attributes that the Postfix SMTP server sends in a delegated SMTPD access policy request: + PPoossttffiixx vveerrssiioonn 22..11 aanndd llaatteerr:: request=smtpd_access_policy protocol_state=RCPT protocol_name=SMTP diff --git a/postfix/RELEASE_NOTES b/postfix/RELEASE_NOTES index 2ae10b550..78b49996c 100644 --- a/postfix/RELEASE_NOTES +++ b/postfix/RELEASE_NOTES @@ -16,3 +16,12 @@ Incompatibility with Postfix 2.3 and earlier If you upgrade from Postfix 2.3 or earlier, read RELEASE_NOTES-2.4 before proceeding. + +Incompatibility with Postfix snapshot 20070422 +============================================== + +When the pipe(8) delivery agent is configured to create the optional +Delivered-To: header, it first checks if that same header is already +present. If so, the mail is returned as undeliverable. This test +should have been included with Postfix 2.0 when Delivered-To: support +was added to the pipe(8) delivery agent. diff --git a/postfix/conf/access b/postfix/conf/access index 6026a80f4..9d396594e 100644 --- a/postfix/conf/access +++ b/postfix/conf/access @@ -219,6 +219,17 @@ # Apply the named UCE restriction(s) (permit, reject, # reject_unauth_destination, and so on). # +# BCC user@domain +# Send one copy of the message to the specified +# recipient. +# +# If multiple BCC actions are specified within the +# same SMTP MAIL transaction, only the last action +# will be used. +# +# This feature is not part of the stable Postfix +# release. +# # DISCARD optional text... # Claim successful delivery and silently discard the # message. Log the optional text if specified, oth- diff --git a/postfix/conf/master.cf b/postfix/conf/master.cf index 29ea1a043..1bdc00c1b 100644 --- a/postfix/conf/master.cf +++ b/postfix/conf/master.cf @@ -2,6 +2,8 @@ # Postfix master process configuration file. For details on the format # of the file, see the master(5) manual page (command: "man 5 master"). # +# Do not forget to execute "postfix reload" after editing this file. +# # ========================================================================== # service type private unpriv chroot wakeup maxproc command + args # (yes) (yes) (yes) (never) (100) diff --git a/postfix/conf/postfix-script b/postfix/conf/postfix-script index 33b408cf4..e5a516918 100644 --- a/postfix/conf/postfix-script +++ b/postfix/conf/postfix-script @@ -136,7 +136,8 @@ stop) sleep 1 done $WARN stopping the Postfix mail system with force - kill -9 `sed 1q pid/master.pid` + pid=`awk '{ print $1; exit 0 } END { exit 1 }' pid/master.pid` && + kill -9 -$pid ;; abort) diff --git a/postfix/html/SMTPD_POLICY_README.html b/postfix/html/SMTPD_POLICY_README.html index 9962eae6f..05ff6ff5d 100644 --- a/postfix/html/SMTPD_POLICY_README.html +++ b/postfix/html/SMTPD_POLICY_README.html @@ -74,6 +74,7 @@ server sends in a delegated SMTPD access policy request:

+Postfix version 2.1 and later:
 request=smtpd_access_policy
 protocol_state=RCPT
 protocol_name=SMTP
diff --git a/postfix/html/access.5.html b/postfix/html/access.5.html
index f73bc3bef..1eb182bbc 100644
--- a/postfix/html/access.5.html
+++ b/postfix/html/access.5.html
@@ -225,6 +225,17 @@ ACCESS(5)                                                            ACCESS(5)
               Apply the named UCE restriction(s) (permit, reject,
               reject_unauth_destination, and so on).
 
+       BCC user@domain
+              Send  one  copy  of  the  message  to the specified
+              recipient.
+
+              If multiple BCC actions are  specified  within  the
+              same  SMTP  MAIL  transaction, only the last action
+              will be used.
+
+              This feature is not  part  of  the  stable  Postfix
+              release.
+
        DISCARD optional text...
               Claim  successful delivery and silently discard the
               message.  Log the optional text if specified,  oth-
diff --git a/postfix/html/pcre_table.5.html b/postfix/html/pcre_table.5.html
index 7a68ce8a5..a80d3108e 100644
--- a/postfix/html/pcre_table.5.html
+++ b/postfix/html/pcre_table.5.html
@@ -50,8 +50,8 @@ PCRE_TABLE(5)                                                    PCRE_TABLE(5)
        if /pattern/flags
 
        endif  Match the input string against the patterns between
-              if and endif, if and only if the input string  also
-              matches pattern. The if..endif can nest.
+              if and endif, if and only if that same input string
+              also matches pattern. The if..endif can nest.
 
               Note:  do not prepend whitespace to patterns inside
               if..endif.
@@ -61,8 +61,8 @@ PCRE_TABLE(5)                                                    PCRE_TABLE(5)
        if !/pattern/flags
 
        endif  Match the input string against the patterns between
-              if and endif, if and only if the input string  does
-              not match pattern. The if..endif can nest.
+              if and endif, if and only if that same input string
+              does not match pattern. The if..endif can nest.
 
               Note:  do not prepend whitespace to patterns inside
               if..endif.
diff --git a/postfix/html/pipe.8.html b/postfix/html/pipe.8.html
index a6abe8188..cf1210a0c 100644
--- a/postfix/html/pipe.8.html
+++ b/postfix/html/pipe.8.html
@@ -30,9 +30,11 @@ PIPE(8)                                                                PIPE(8)
        trace(8) daemon as appropriate.
 
 SINGLE-RECIPIENT DELIVERY
-       Some external commands cannot handle more than one recipi-
-       ent per delivery request. Examples of such transports  are
-       pagers or fax machines.
+       Some  destinations  cannot  handle more than one recipient
+       per delivery request. Examples are pagers or fax machines.
+       In  addition, multi-recipient delivery is undesirable when
+       prepending  a  Delivered-to:  or  X-Original-To:   message
+       header.
 
        To  prevent  Postfix  from sending multiple recipients per
        delivery request, specify
@@ -86,89 +88,99 @@ PIPE(8)                                                                PIPE(8)
               D      Prepend  a "Delivered-To: recipient" message
                      header with the envelope recipient  address.
                      Note: for this to work, the transport_desti-
-                     nation_recipient_limit must be 1.
+                     nation_recipient_limit must be 1  (see  SIN-
+                     GLE-RECIPIENT DELIVERY above for details).
+
+                     This   code  also  enforces  loop  detection
+                     (Postfix  2.5  and  later).   If  a  message
+                     already contains a Delivered-To: header with
+                     the same recipient address, then the message
+                     is returned as undeliverable.
 
                      This feature is available as of Postfix 2.0.
 
-              F      Prepend  a "From sender time_stamp" envelope
-                     header to  the  message  content.   This  is
+              F      Prepend a "From sender time_stamp"  envelope
+                     header  to  the  message  content.   This is
                      expected by, for example, UUCP software.
 
-              O      Prepend  an  "X-Original-To: recipient" mes-
-                     sage header with the  recipient  address  as
-                     given  to  Postfix.  Note: for this to work,
+              O      Prepend an "X-Original-To:  recipient"  mes-
+                     sage  header  with  the recipient address as
+                     given to Postfix. Note: for  this  to  work,
                      the    transport_destination_recipient_limit
-                     must be 1.
+                     must be  1  (see  SINGLE-RECIPIENT  DELIVERY
+                     above for details).
 
                      This feature is available as of Postfix 2.0.
 
               R      Prepend a Return-Path: message  header  with
                      the envelope sender address.
 
-              h      Fold the command-line $recipient domain name
-                     and $nexthop host name to lower case.   This
-                     is recommended for delivery via UUCP.
+              h      Fold  the  command-line  $recipient  address
+                     domain part (text to the right of the right-
+                     most  @  character)  to lower case; fold the
+                     entire  command-line  $domain  and  $nexthop
+                     host  or  domain  information to lower case.
+                     This is recommended for delivery via UUCP.
 
-              q      Quote  white space and other special charac-
+              q      Quote white space and other special  charac-
                      ters in the command-line $sender and $recip-
                      ient address localparts (text to the left of
                      the right-most @ character), according to an
-                     8-bit  transparent version of RFC 822.  This
-                     is recommended  for  delivery  via  UUCP  or
+                     8-bit transparent version of RFC 822.   This
+                     is  recommended  for  delivery  via  UUCP or
                      BSMTP.
 
-                     The  result  is  compatible with the address
-                     parsing of command-line  recipients  by  the
+                     The result is compatible  with  the  address
+                     parsing  of  command-line  recipients by the
                      Postfix sendmail(1) mail submission command.
 
-                     The q flag affects  only  entire  addresses,
+                     The  q  flag  affects only entire addresses,
                      not the partial address information from the
-                     $user, $extension or  $mailbox  command-line
+                     $user,  $extension  or $mailbox command-line
                      macros.
 
               u      Fold  the  command-line  $recipient  address
-                     localpart (text to the left  of  the  right-
-                     most  @  character)  to lower case.  This is
+                     localpart  (text  to  the left of the right-
+                     most @ character) to lower  case.   This  is
                      recommended for delivery via UUCP.
 
               .      Prepend "." to lines starting with ".". This
                      is needed by, for example, BSMTP software.
 
-              >      Prepend  ">" to lines starting with "From ".
+              >      Prepend ">" to lines starting with "From  ".
                      This is expected by, for example, UUCP soft-
                      ware.
 
        null_sender=replacement (default: MAILER-DAEMON)
               Replace the null sender address (typically used for
-              delivery status notifications) with  the  specified
+              delivery  status  notifications) with the specified
               text when expanding the $sender command-line macro,
               and when generating a From_ or Return-Path: message
               header.
 
-              If  the null sender replacement text is a non-empty
-              string then it  is  affected  by  the  q  flag  for
+              If the null sender replacement text is a  non-empty
+              string  then  it  is  affected  by  the  q flag for
               address quoting in command-line arguments.
 
               The null sender replacement text may be empty; this
-              form is recommended for content filters  that  feed
+              form  is  recommended for content filters that feed
               mail back into Postfix. The empty sender address is
-              not affected by the q flag for address  quoting  in
+              not  affected  by the q flag for address quoting in
               command-line arguments.
 
               Caution: a null sender address is easily mis-parsed
-              by naive software. For example,  when  the  pipe(8)
+              by  naive  software.  For example, when the pipe(8)
               daemon executes a command such as:
 
                   command -f$sender -- $recipient (bad)
 
               the command will mis-parse the -f option value when
-              the sender address is a null string.   For  correct
+              the  sender  address is a null string.  For correct
               parsing, specify $sender as an argument by itself:
 
                   command -f $sender -- $recipient (good)
 
-              This  feature  is  available  with  Postfix 2.3 and
-              later.
+              This feature is available as of Postfix 2.3.
 
        size=size_limit (optional)
               Messages greater in size than this limit (in bytes)
@@ -224,176 +236,187 @@ PIPE(8)                                                                PIPE(8)
 
                      This is available in Postfix 2.2 and  later.
 
+              ${domain}
+                     This  macro expands to the domain portion of
+                     the recipient address.  For example, with an
+                     address   user+foo@domain   the   domain  is
+                     domain.
+
+                     This information is modified by the  h  flag
+                     for case folding.
+
+                     This  is available in Postfix 2.5 and later.
+
               ${extension}
-                     This  macro expands to the extension part of
-                     a recipient address.  For example,  with  an
+                     This macro expands to the extension part  of
+                     a  recipient  address.  For example, with an
                      address  user+foo@domain  the  extension  is
                      foo.
 
-                     A  command-line   argument   that   contains
-                     ${extension}  expands  into as many command-
+                     A   command-line   argument   that  contains
+                     ${extension} expands into as  many  command-
                      line arguments as there are recipients.
 
-                     This information is modified by the  u  flag
+                     This  information  is modified by the u flag
                      for case folding.
 
               ${mailbox}
-                     This  macro  expands  to  the complete local
-                     part of a recipient address.   For  example,
-                     with  an address user+foo@domain the mailbox
+                     This macro expands  to  the  complete  local
+                     part  of  a recipient address.  For example,
+                     with an address user+foo@domain the  mailbox
                      is user+foo.
 
-                     A  command-line   argument   that   contains
-                     ${mailbox}  expands  to as many command-line
+                     A   command-line   argument   that  contains
+                     ${mailbox} expands to as  many  command-line
                      arguments as there are recipients.
 
-                     This information is modified by the  u  flag
+                     This  information  is modified by the u flag
                      for case folding.
 
               ${nexthop}
                      This macro expands to the next-hop hostname.
 
-                     This information is modified by the  h  flag
+                     This  information  is modified by the h flag
                      for case folding.
 
               ${recipient}
                      This macro expands to the complete recipient
                      address.
 
-                     A  command-line   argument   that   contains
+                     A   command-line   argument   that  contains
                      ${recipient} expands to as many command-line
                      arguments as there are recipients.
 
-                     This information  is  modified  by  the  hqu
+                     This  information  is  modified  by  the hqu
                      flags for quoting and case folding.
 
               ${sasl_method}
-                     This  macro  expands to the SASL authentica-
-                     tion mechanism used during the reception  of
-                     the  message.  An  empty string is passed if
-                     the message has been received  without  SASL
+                     This macro expands to the  SASL  authentica-
+                     tion  mechanism used during the reception of
+                     the message. An empty string  is  passed  if
+                     the  message  has been received without SASL
                      authentication.
 
-                     This  is available in Postfix 2.2 and later.
+                     This is available in Postfix 2.2 and  later.
 
               ${sasl_sender}
-                     This macro expands to the SASL  sender  name
-                     (i.e.  the  original  submitter  as  per RFC
-                     2554) used during the reception of the  mes-
+                     This  macro  expands to the SASL sender name
+                     (i.e. the  original  submitter  as  per  RFC
+                     2554)  used during the reception of the mes-
                      sage.
 
-                     This  is available in Postfix 2.2 and later.
+                     This is available in Postfix 2.2 and  later.
 
               ${sasl_username}
-                     This macro expands to  the  SASL  user  name
+                     This  macro  expands  to  the SASL user name
                      used during the reception of the message. An
-                     empty string is passed if  the  message  has
+                     empty  string  is  passed if the message has
                      been received without SASL authentication.
 
-                     This  is available in Postfix 2.2 and later.
+                     This is available in Postfix 2.2 and  later.
 
               ${sender}
-                     This macro expands to  the  envelope  sender
+                     This  macro  expands  to the envelope sender
                      address. By default, the null sender address
-                     expands  to  MAILER-DAEMON;  this   can   be
-                     changed  with  the null_sender attribute, as
+                     expands   to   MAILER-DAEMON;  this  can  be
+                     changed with the null_sender  attribute,  as
                      described above.
 
-                     This information is modified by the  q  flag
+                     This  information  is modified by the q flag
                      for quoting.
 
               ${size}
-                     This  macro expands to Postfix's idea of the
-                     message size, which is an  approximation  of
+                     This macro expands to Postfix's idea of  the
+                     message  size,  which is an approximation of
                      the size of the message as delivered.
 
               ${user}
                      This macro expands to the username part of a
-                     recipient address.   For  example,  with  an
+                     recipient  address.   For  example,  with an
                      address user+foo@domain the username part is
                      user.
 
-                     A  command-line   argument   that   contains
-                     ${user}  expands  into  as many command-line
+                     A   command-line   argument   that  contains
+                     ${user} expands into  as  many  command-line
                      arguments as there are recipients.
 
-                     This information is modified by the  u  flag
+                     This  information  is modified by the u flag
                      for case folding.
 
 STANDARDS
        RFC 3463 (Enhanced status codes)
 
 DIAGNOSTICS
-       Command  exit status codes are expected to follow the con-
-       ventions defined in <sysexits.h>.   Exit  status  0  means
+       Command exit status codes are expected to follow the  con-
+       ventions  defined  in  <sysexits.h>.   Exit status 0 means
        normal successful completion.
 
-       Postfix  version  2.3  and  later  support  RFC 3463-style
-       enhanced status codes.  If a  command  terminates  with  a
-       non-zero  exit  status, and the command output begins with
+       Postfix version  2.3  and  later  support  RFC  3463-style
+       enhanced  status  codes.   If  a command terminates with a
+       non-zero exit status, and the command output  begins  with
        an enhanced status code, this status code takes precedence
        over the non-zero exit status.
 
-       Problems  and transactions are logged to syslogd(8).  Cor-
-       rupted message files are marked so that the queue  manager
+       Problems and transactions are logged to syslogd(8).   Cor-
+       rupted  message files are marked so that the queue manager
        can move them to the corrupt queue for further inspection.
 
 SECURITY
-       This program needs a dual personality  1)  to  access  the
-       private  Postfix  queue and IPC mechanisms, and 2) to exe-
+       This  program  needs  a  dual personality 1) to access the
+       private Postfix queue and IPC mechanisms, and 2)  to  exe-
        cute external commands as the specified user. It is there-
        fore security sensitive.
 
 CONFIGURATION PARAMETERS
-       Changes  to main.cf are picked up automatically as pipe(8)
-       processes run for only a limited amount of time.  Use  the
+       Changes to main.cf are picked up automatically as  pipe(8)
+       processes  run  for only a limited amount of time. Use the
        command "postfix reload" to speed up a change.
 
-       The  text  below  provides  only  a parameter summary. See
+       The text below provides  only  a  parameter  summary.  See
        postconf(5) for more details including examples.
 
 RESOURCE AND RATE CONTROLS
-       In the text below, transport is the first field in a  mas-
+       In  the text below, transport is the first field in a mas-
        ter.cf entry.
 
        transport_destination_concurrency_limit ($default_destina-
        tion_concurrency_limit)
               Limit the number of parallel deliveries to the same
-              destination, for delivery via the named  transport.
+              destination,  for delivery via the named transport.
               The limit is enforced by the Postfix queue manager.
 
        transport_destination_recipient_limit   ($default_destina-
        tion_recipient_limit)
-              Limit the number of recipients per  message  deliv-
-              ery,  for  delivery  via  the named transport.  The
+              Limit  the  number of recipients per message deliv-
+              ery, for delivery via  the  named  transport.   The
               limit is enforced by the Postfix queue manager.
 
        transport_time_limit ($command_time_limit)
-              Limit the time for delivery  to  external  command,
+              Limit  the  time  for delivery to external command,
               for delivery via the named transport.  The limit is
               enforced by the pipe delivery agent.
 
-              Postfix 2.4 and later support a suffix that  speci-
-              fies  the  time  unit:  s (seconds), m (minutes), h
+              Postfix  2.4 and later support a suffix that speci-
+              fies the time unit: s  (seconds),  m  (minutes),  h
               (hours), d (days), w (weeks). The default time unit
               is seconds.
 
 MISCELLANEOUS CONTROLS
        config_directory (see 'postconf -d' output)
-              The  default  location  of  the Postfix main.cf and
+              The default location of  the  Postfix  main.cf  and
               master.cf configuration files.
 
        daemon_timeout (18000s)
-              How much time a Postfix daemon process may take  to
-              handle  a  request  before  it  is  terminated by a
+              How  much time a Postfix daemon process may take to
+              handle a request  before  it  is  terminated  by  a
               built-in watchdog timer.
 
        delay_logging_resolution_limit (2)
-              The maximal number  of  digits  after  the  decimal
+              The  maximal  number  of  digits  after the decimal
               point when logging sub-second delay values.
 
        export_environment (see 'postconf -d' output)
-              The  list  of  environment variables that a Postfix
+              The list of environment variables  that  a  Postfix
               process will export to non-Postfix processes.
 
        ipc_timeout (3600s)
@@ -405,25 +428,25 @@ PIPE(8)                                                                PIPE(8)
               and most Postfix daemon processes.
 
        max_idle (100s)
-              The maximum amount of time  that  an  idle  Postfix
-              daemon  process  waits  for  an incoming connection
+              The  maximum  amount  of  time that an idle Postfix
+              daemon process waits  for  an  incoming  connection
               before terminating voluntarily.
 
        max_use (100)
-              The maximal number of incoming connections  that  a
-              Postfix  daemon  process will service before termi-
+              The  maximal  number of incoming connections that a
+              Postfix daemon process will service  before  termi-
               nating voluntarily.
 
        process_id (read-only)
-              The process ID  of  a  Postfix  command  or  daemon
+              The  process  ID  of  a  Postfix  command or daemon
               process.
 
        process_name (read-only)
-              The  process  name  of  a Postfix command or daemon
+              The process name of a  Postfix  command  or  daemon
               process.
 
        queue_directory (see 'postconf -d' output)
-              The location of the Postfix top-level queue  direc-
+              The  location of the Postfix top-level queue direc-
               tory.
 
        recipient_delimiter (empty)
@@ -434,8 +457,8 @@ PIPE(8)                                                                PIPE(8)
               The syslog facility of Postfix logging.
 
        syslog_name (postfix)
-              The mail system  name  that  is  prepended  to  the
-              process  name  in  syslog  records, so that "smtpd"
+              The  mail  system  name  that  is  prepended to the
+              process name in syslog  records,  so  that  "smtpd"
               becomes, for example, "postfix/smtpd".
 
 SEE ALSO
@@ -447,7 +470,7 @@ PIPE(8)                                                                PIPE(8)
        syslogd(8), system logging
 
 LICENSE
-       The Secure Mailer license must be  distributed  with  this
+       The  Secure  Mailer  license must be distributed with this
        software.
 
 AUTHOR(S)
diff --git a/postfix/html/regexp_table.5.html b/postfix/html/regexp_table.5.html
index ac0646f7a..5ec01696f 100644
--- a/postfix/html/regexp_table.5.html
+++ b/postfix/html/regexp_table.5.html
@@ -62,77 +62,76 @@ REGEXP_TABLE(5)                                                REGEXP_TABLE(5)
 
        endif  Match the input string against the patterns between
               if and endif, if and only if that same input string
-              does  not  match  pattern.  The if..endif can nest.
-              matches pattern. The if..endif can nest.
+              does not match pattern. The if..endif can nest.
 
-              Note: do not prepend whitespace to patterns  inside
+              Note:  do not prepend whitespace to patterns inside
               if..endif.
 
               This feature is available in Postfix 2.1 and later.
 
        blank lines and comments
-              Empty lines and whitespace-only lines are  ignored,
-              as  are  lines whose first non-whitespace character
+              Empty  lines and whitespace-only lines are ignored,
+              as are lines whose first  non-whitespace  character
               is a `#'.
 
        multi-line text
-              A logical line starts with non-whitespace  text.  A
-              line  that starts with whitespace continues a logi-
+              A  logical  line starts with non-whitespace text. A
+              line that starts with whitespace continues a  logi-
               cal line.
 
-       Each pattern is a POSIX regular expression enclosed  by  a
+       Each  pattern  is a POSIX regular expression enclosed by a
        pair of delimiters. The regular expression syntax is docu-
-       mented in  re_format(7)  with  4.4BSD,  in  regex(5)  with
+       mented  in  re_format(7)  with  4.4BSD,  in  regex(5) with
        Solaris, and in regex(7) with Linux. Other systems may use
        other document names.
 
-       The expression delimiter  can  be  any  character,  except
+       The  expression  delimiter  can  be  any character, except
        whitespace or characters that have special meaning (tradi-
-       tionally the forward slash is used). The  regular  expres-
+       tionally  the  forward slash is used). The regular expres-
        sion can contain whitespace.
 
        By default, matching is case-insensitive, and newlines are
-       not treated as special characters. The  behavior  is  con-
-       trolled  by  flags,  which are toggled by appending one or
+       not  treated  as  special characters. The behavior is con-
+       trolled by flags, which are toggled by  appending  one  or
        more of the following characters after the pattern:
 
        i (default: on)
-              Toggles the  case  sensitivity  flag.  By  default,
+              Toggles  the  case  sensitivity  flag.  By default,
               matching is case insensitive.
 
        x (default: on)
-              Toggles  the  extended  expression  syntax flag. By
-              default, support for extended expression syntax  is
+              Toggles the extended  expression  syntax  flag.  By
+              default,  support for extended expression syntax is
               enabled.
 
        m (default: off)
-              Toggle  the multi-line mode flag. When this flag is
-              on, the ^ and $  metacharacters  match  immediately
-              after  and  immediately before a newline character,
-              respectively, in addition to matching at the  start
+              Toggle the multi-line mode flag. When this flag  is
+              on,  the  ^  and $ metacharacters match immediately
+              after and immediately before a  newline  character,
+              respectively,  in addition to matching at the start
               and end of the input string.
 
 TABLE SEARCH ORDER
-       Patterns  are applied in the order as specified in the ta-
-       ble, until a pattern  is  found  that  matches  the  input
+       Patterns are applied in the order as specified in the  ta-
+       ble,  until  a  pattern  is  found  that matches the input
        string.
 
-       Each  pattern  is  applied  to  the  entire  input string.
-       Depending on the application, that  string  is  an  entire
+       Each pattern  is  applied  to  the  entire  input  string.
+       Depending  on  the  application,  that string is an entire
        client hostname, an entire client IP address, or an entire
-       mail address.  Thus, no parent domain  or  parent  network
-       search  is  done,  and  user@domain mail addresses are not
-       broken up into their user and  domain  constituent  parts,
+       mail  address.   Thus,  no parent domain or parent network
+       search is done, and user@domain  mail  addresses  are  not
+       broken  up  into  their user and domain constituent parts,
        nor is user+foo broken up into user and foo.
 
 TEXT SUBSTITUTION
-       Substitution  of  substrings  from  the matched expression
-       into the result string is possible  using  $1,  $2,  etc.;
+       Substitution of substrings  from  the  matched  expression
+       into  the  result  string  is possible using $1, $2, etc.;
        specify $$ to produce a $ character as output.  The macros
-       in the result string may need to be  written  as  ${n}  or
+       in  the  result  string  may need to be written as ${n} or
        $(n) if they aren't followed by whitespace.
 
-       Note:  since negated patterns (those preceded by !) return
+       Note: since negated patterns (those preceded by !)  return
        a result when the expression does not match, substitutions
        are not available for negated patterns.
 
diff --git a/postfix/man/man5/access.5 b/postfix/man/man5/access.5
index 63485b9f5..e96f4ad3b 100644
--- a/postfix/man/man5/access.5
+++ b/postfix/man/man5/access.5
@@ -216,6 +216,13 @@ This feature is available in Postfix 2.1 and later.
 .IP \fIrestriction...\fR
 Apply the named UCE restriction(s) (\fBpermit\fR, \fBreject\fR,
 \fBreject_unauth_destination\fR, and so on).
+.IP "\fBBCC \fIuser@domain\fR"
+Send one copy of the message to the specified recipient.
+.sp
+If multiple BCC actions are specified within the same SMTP
+MAIL transaction, only the last action will be used.
+.sp
+This feature is not part of the stable Postfix release.
 .IP "\fBDISCARD \fIoptional text...\fR
 Claim successful delivery and silently discard the message.
 Log the optional text if specified, otherwise log a generic
diff --git a/postfix/man/man5/pcre_table.5 b/postfix/man/man5/pcre_table.5
index 8cb6abf5d..c3c008fb1 100644
--- a/postfix/man/man5/pcre_table.5
+++ b/postfix/man/man5/pcre_table.5
@@ -51,7 +51,7 @@ the corresponding \fIresult\fR value.
 .IP "\fBif /\fIpattern\fB/\fIflags\fR"
 .IP "\fBendif\fR"
 Match the input string against the patterns between \fBif\fR
-and \fBendif\fR, if and only if the input string also matches
+and \fBendif\fR, if and only if that same input string also matches
 \fIpattern\fR. The \fBif\fR..\fBendif\fR can nest.
 .sp
 Note: do not prepend whitespace to patterns inside
@@ -61,7 +61,7 @@ This feature is available in Postfix 2.1 and later.
 .IP "\fBif !/\fIpattern\fB/\fIflags\fR"
 .IP "\fBendif\fR"
 Match the input string against the patterns between \fBif\fR
-and \fBendif\fR, if and only if the input string does \fBnot\fR
+and \fBendif\fR, if and only if that same input string does \fBnot\fR
 match \fIpattern\fR. The \fBif\fR..\fBendif\fR can nest.
 .sp
 Note: do not prepend whitespace to patterns inside
diff --git a/postfix/man/man5/regexp_table.5 b/postfix/man/man5/regexp_table.5
index 5113c1a1f..4b4d98231 100644
--- a/postfix/man/man5/regexp_table.5
+++ b/postfix/man/man5/regexp_table.5
@@ -63,7 +63,6 @@ This feature is available in Postfix 2.1 and later.
 Match the input string against the patterns between \fBif\fR
 and \fBendif\fR, if and only if that same input string does
 \fBnot\fR match \fIpattern\fR. The \fBif\fR..\fBendif\fR can nest.
-matches \fIpattern\fR. The \fBif\fR..\fBendif\fR can nest.
 .sp
 Note: do not prepend whitespace to patterns inside
 \fBif\fR..\fBendif\fR.
diff --git a/postfix/man/man8/pipe.8 b/postfix/man/man8/pipe.8
index 4c27e7bb0..7b5a2c675 100644
--- a/postfix/man/man8/pipe.8
+++ b/postfix/man/man8/pipe.8
@@ -31,9 +31,11 @@ appropriate.
 .nf
 .ad
 .fi
-Some external commands cannot handle more than one recipient
-per delivery request. Examples of such transports are pagers
-or fax machines.
+Some destinations cannot handle more than one recipient per
+delivery request. Examples are pagers or fax machines.
+In addition, multi-recipient delivery is undesirable when
+prepending a \fBDelivered-to:\fR or \fBX-Original-To:\fR
+message header.
 
 To prevent Postfix from sending multiple recipients per delivery
 request, specify
@@ -84,7 +86,13 @@ when preceded by a blank line.
 .IP \fBD\fR
 Prepend a "\fBDelivered-To: \fIrecipient\fR" message header with the
 envelope recipient address. Note: for this to work, the
-\fItransport\fB_destination_recipient_limit\fR must be 1.
+\fItransport\fB_destination_recipient_limit\fR must be 1
+(see SINGLE-RECIPIENT DELIVERY above for details).
+.sp
+This code also enforces loop detection (Postfix 2.5 and later).
+If a message already contains a \fBDelivered-To:\fR header
+with the same recipient address, then the message is
+returned as undeliverable.
 .sp
 This feature is available as of Postfix 2.0.
 .IP \fBF\fR
@@ -94,15 +102,18 @@ This is expected by, for example, \fBUUCP\fR software.
 .IP \fBO\fR
 Prepend an "\fBX-Original-To: \fIrecipient\fR" message header
 with the recipient address as given to Postfix. Note: for this to
-work, the \fItransport\fB_destination_recipient_limit\fR must be 1.
+work, the \fItransport\fB_destination_recipient_limit\fR must be 1
+(see SINGLE-RECIPIENT DELIVERY above for details).
 .sp
 This feature is available as of Postfix 2.0.
 .IP \fBR\fR
 Prepend a \fBReturn-Path:\fR message header with the envelope sender
 address.
 .IP \fBh\fR
-Fold the command-line \fB$recipient\fR domain name and \fB$nexthop\fR
-host name to lower case.
+Fold the command-line \fB$recipient\fR address domain part
+(text to the right of the right-most \fB@\fR character) to
+lower case; fold the entire command-line \fB$domain\fR and
+\fB$nexthop\fR host or domain information to lower case.
 This is recommended for delivery via \fBUUCP\fR.
 .IP \fBq\fR
 Quote white space and other special characters in the command-line
@@ -159,7 +170,7 @@ specify \fB$sender\fR as an argument by itself:
     command -f $sender -- $recipient (\fIgood\fR)
 .fi
 .IP
-This feature is available with Postfix 2.3 and later.
+This feature is available as of Postfix 2.3.
 .IP "\fBsize\fR=\fIsize_limit\fR (optional)"
 Messages greater in size than this limit (in bytes) will
 be returned to the sender as undeliverable.
@@ -201,6 +212,14 @@ This is available in Postfix 2.2 and later.
 This macro expands to the remote client protocol.
 .sp
 This is available in Postfix 2.2 and later.
+.IP \fB${\fBdomain\fR}\fR
+This macro expands to the domain portion of the recipient
+address.  For example, with an address \fIuser+foo@domain\fR
+the domain is \fIdomain\fR.
+.sp
+This information is modified by the \fBh\fR flag for case folding.
+.sp
+This is available in Postfix 2.5 and later.
 .IP \fB${\fBextension\fR}\fR
 This macro expands to the extension part of a recipient address.
 For example, with an address \fIuser+foo@domain\fR the extension is
diff --git a/postfix/proto/SMTPD_POLICY_README.html b/postfix/proto/SMTPD_POLICY_README.html
index b0fc39ee3..1854a6f00 100644
--- a/postfix/proto/SMTPD_POLICY_README.html
+++ b/postfix/proto/SMTPD_POLICY_README.html
@@ -74,6 +74,7 @@ server sends in a delegated SMTPD access policy request: 

+Postfix version 2.1 and later:
 request=smtpd_access_policy
 protocol_state=RCPT
 protocol_name=SMTP
diff --git a/postfix/proto/access b/postfix/proto/access
index 6ebee10ad..665cc9e8f 100644
--- a/postfix/proto/access
+++ b/postfix/proto/access
@@ -194,6 +194,13 @@
 # .IP \fIrestriction...\fR
 #	Apply the named UCE restriction(s) (\fBpermit\fR, \fBreject\fR,
 #	\fBreject_unauth_destination\fR, and so on).
+# .IP "\fBBCC \fIuser@domain\fR"
+#	Send one copy of the message to the specified recipient.
+# .sp
+#	If multiple BCC actions are specified within the same SMTP
+#	MAIL transaction, only the last action will be used.
+# .sp
+#	This feature is not part of the stable Postfix release.
 # \" .IP "\fBDELAY \fItime\fR"
 # \"	Place the message into the deferred queue, and delay the
 # \"	initial delivery attempt by \fItime\fR. The time value may
diff --git a/postfix/proto/pcre_table b/postfix/proto/pcre_table
index c0ab8f3eb..679e67ef7 100644
--- a/postfix/proto/pcre_table
+++ b/postfix/proto/pcre_table
@@ -41,7 +41,7 @@
 # .IP "\fBif /\fIpattern\fB/\fIflags\fR"
 # .IP "\fBendif\fR"
 #	Match the input string against the patterns between \fBif\fR
-#	and \fBendif\fR, if and only if the input string also matches
+#	and \fBendif\fR, if and only if that same input string also matches
 #	\fIpattern\fR. The \fBif\fR..\fBendif\fR can nest.
 # .sp
 #	Note: do not prepend whitespace to patterns inside
@@ -51,7 +51,7 @@
 # .IP "\fBif !/\fIpattern\fB/\fIflags\fR"
 # .IP "\fBendif\fR"
 #	Match the input string against the patterns between \fBif\fR
-#	and \fBendif\fR, if and only if the input string does \fBnot\fR
+#	and \fBendif\fR, if and only if that same input string does \fBnot\fR
 #	match \fIpattern\fR. The \fBif\fR..\fBendif\fR can nest.
 # .sp
 #	Note: do not prepend whitespace to patterns inside
diff --git a/postfix/proto/regexp_table b/postfix/proto/regexp_table
index 3a15f46a9..6e225b33c 100644
--- a/postfix/proto/regexp_table
+++ b/postfix/proto/regexp_table
@@ -53,7 +53,6 @@
 #       Match the input string against the patterns between \fBif\fR
 #	and \fBendif\fR, if and only if that same input string does
 #	\fBnot\fR match \fIpattern\fR. The \fBif\fR..\fBendif\fR can nest.
-#	matches \fIpattern\fR. The \fBif\fR..\fBendif\fR can nest.
 # .sp
 #       Note: do not prepend whitespace to patterns inside
 #	\fBif\fR..\fBendif\fR.
diff --git a/postfix/src/global/Makefile.in b/postfix/src/global/Makefile.in
index b6c602f0b..1e2aa89a2 100644
--- a/postfix/src/global/Makefile.in
+++ b/postfix/src/global/Makefile.in
@@ -27,7 +27,7 @@ SRCS	= abounce.c anvil_clnt.c been_here.c bounce.c bounce_log.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 trace.c \
 	user_acl.c valid_mailhost_addr.c verify.c verify_clnt.c \
-	verp_sender.c wildcard_inet_addr.c xtext.c
+	verp_sender.c wildcard_inet_addr.c xtext.c delivered_hdr.c
 OBJS	= abounce.o anvil_clnt.o been_here.o bounce.o bounce_log.o \
 	canon_addr.o cfg_parser.o cleanup_strerror.o cleanup_strflags.o \
 	clnt_stream.o conv_time.o db_common.o debug_peer.o debug_process.o \
@@ -56,7 +56,7 @@ OBJS	= abounce.o anvil_clnt.o been_here.o bounce.o bounce_log.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 trace.o \
 	user_acl.o valid_mailhost_addr.o verify.o verify_clnt.o \
-	verp_sender.o wildcard_inet_addr.o xtext.o
+	verp_sender.o wildcard_inet_addr.o xtext.o delivered_hdr.o
 HDRS	= abounce.h anvil_clnt.h been_here.h bounce.h bounce_log.h \
 	canon_addr.h cfg_parser.h cleanup_user.h clnt_stream.h config.h \
 	conv_time.h db_common.h debug_peer.h debug_process.h defer.h \
@@ -79,7 +79,7 @@ HDRS	= abounce.h anvil_clnt.h been_here.h bounce.h bounce_log.h \
 	rewrite_clnt.h scache.h sent.h smtp_stream.h split_addr.h \
 	string_list.h strip_addr.h sys_exits.h timed_ipc.h tok822.h \
 	trace.h user_acl.h valid_mailhost_addr.h verify.h verify_clnt.h \
-	verp_sender.h wildcard_inet_addr.h xtext.h
+	verp_sender.h wildcard_inet_addr.h xtext.h delivered_hdr.h
 TESTSRC	= rec2stream.c stream2rec.c recdump.c
 DEFS	= -I. -I$(INC_DIR) -D$(SYSTYPE)
 CFLAGS	= $(DEBUG) $(OPT) $(DEFS)
@@ -688,6 +688,23 @@ deliver_request.o: mail_queue.h
 deliver_request.o: msg_stats.h
 deliver_request.o: rcpt_buf.h
 deliver_request.o: recipient_list.h
+delivered_hdr.o: ../../include/htable.h
+delivered_hdr.o: ../../include/msg.h
+delivered_hdr.o: ../../include/mymalloc.h
+delivered_hdr.o: ../../include/stringops.h
+delivered_hdr.o: ../../include/sys_defs.h
+delivered_hdr.o: ../../include/vbuf.h
+delivered_hdr.o: ../../include/vstream.h
+delivered_hdr.o: ../../include/vstring.h
+delivered_hdr.o: ../../include/vstring_vstream.h
+delivered_hdr.o: delivered_hdr.c
+delivered_hdr.o: delivered_hdr.h
+delivered_hdr.o: header_opts.h
+delivered_hdr.o: is_header.h
+delivered_hdr.o: quote_822_local.h
+delivered_hdr.o: quote_flags.h
+delivered_hdr.o: rec_type.h
+delivered_hdr.o: record.h
 dict_ldap.o: ../../include/argv.h
 dict_ldap.o: ../../include/binhash.h
 dict_ldap.o: ../../include/dict.h
diff --git a/postfix/src/global/delivered_hdr.c b/postfix/src/global/delivered_hdr.c
new file mode 100644
index 000000000..f3dc2c4d5
--- /dev/null
+++ b/postfix/src/global/delivered_hdr.c
@@ -0,0 +1,167 @@
+/*++
+/* NAME
+/*	delivered_hdr 3
+/* SUMMARY
+/*	process Delivered-To: headers
+/* SYNOPSIS
+/*	#include 
+/*
+/*	DELIVERED_HDR_INFO *delivered_hdr_init(stream, offset)
+/*	VSTREAM	*stream;
+/*	off_t	offset;
+/*
+/*	int	delivered_hdr_find(info, address)
+/*	DELIVERED_HDR_INFO *info;
+/*	const char *address;
+/*
+/*	void	delivered_hdr_free(info)
+/*	DELIVERED_HDR_INFO *info;
+/* DESCRIPTION
+/*	This module processes addresses in Delivered-To: headers.
+/*	These headers are added by some mail delivery systems, for the
+/*	purpose of breaking mail forwarding loops. N.B. This solves
+/*	a different problem than the Received: hop count limit. Hop
+/*	counts are used to limit the impact of mail routing problems.
+/*
+/*	delivered_hdr_init() extracts Delivered-To: header addresses
+/*	from the specified message, and returns a table with the
+/*	result. The file seek pointer is changed.
+/*
+/*	delivered_hdr_find() looks up the address in the lookup table,
+/*	and returns non-zero when the address was found. The
+/*	address argument must be in internalized form.
+/*
+/*	delivered_hdr_free() releases storage that was allocated by
+/*	delivered_hdr_init().
+/*
+/*	Arguments:
+/* .IP stream
+/*	The open queue file.
+/* .IP offset
+/*	Offset of the first message content record.
+/* .IP info
+/*	Extracted Delivered-To: addresses information.
+/* .IP address
+/*	A recipient address, internal form.
+/* DIAGNOSTICS
+/*	Fatal errors: out of memory.
+/* SEE ALSO
+/*	mail_copy(3), producer of Delivered-To: and other headers.
+/* 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 
+#include 
+#include 
+#include 
+
+/* Utility library. */
+
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+
+/* Global library. */
+
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+
+ /*
+  * Application-specific.
+  */
+struct DELIVERED_HDR_INFO {
+    VSTRING *buf;
+    HTABLE *table;
+};
+
+#define STR(x) vstring_str(x)
+
+/* delivered_hdr_init - extract delivered-to information from the message */
+
+DELIVERED_HDR_INFO *delivered_hdr_init(VSTREAM *fp, off_t offset)
+{
+    char   *cp;
+    DELIVERED_HDR_INFO *info;
+    HEADER_OPTS *hdr;
+
+    info = (DELIVERED_HDR_INFO *) mymalloc(sizeof(*info));
+    info->buf = vstring_alloc(10);
+    info->table = htable_create(0);
+
+    if (vstream_fseek(fp, offset, SEEK_SET) < 0)
+	msg_fatal("seek queue file %s: %m", VSTREAM_PATH(fp));
+
+    /*
+     * XXX Assume that mail_copy() produces delivered-to headers that fit in
+     * a REC_TYPE_NORM record. Lowercase the delivered-to addresses for
+     * consistency.
+     * 
+     * XXX Don't get bogged down by gazillions of delivered-to headers.
+     */
+#define DELIVERED_HDR_LIMIT	1000
+
+    while (rec_get(fp, info->buf, 0) == REC_TYPE_NORM
+	   && info->table->used < DELIVERED_HDR_LIMIT) {
+	if (is_header(STR(info->buf))) {
+	    if ((hdr = header_opts_find(STR(info->buf))) != 0
+		&& hdr->type == HDR_DELIVERED_TO) {
+		cp = STR(info->buf) + strlen(hdr->name) + 1;
+		while (ISSPACE(*cp))
+		    cp++;
+		lowercase(cp);
+		if (msg_verbose)
+		    msg_info("delivered_hdr_init: %s", cp);
+		htable_enter(info->table, cp, (char *) 0);
+	    }
+	} else if (ISSPACE(STR(info->buf)[0])) {
+	    continue;
+	} else {
+	    break;
+	}
+    }
+    return (info);
+}
+
+/* delivered_hdr_find - look up recipient in delivered table */
+
+int     delivered_hdr_find(DELIVERED_HDR_INFO *info, const char *address)
+{
+    HTABLE_INFO *ht;
+
+    /*
+     * mail_copy() uses quote_822_local() when writing the Delivered-To:
+     * header. We must therefore apply the same transformation when looking
+     * up the recipient. Lowercase the delivered-to address for consistency.
+     */
+    quote_822_local(info->buf, address);
+    lowercase(STR(info->buf));
+    ht = htable_locate(info->table, STR(info->buf));
+    return (ht != 0);
+}
+
+/* delivered_hdr_free - destructor */
+
+void    delivered_hdr_free(DELIVERED_HDR_INFO *info)
+{
+    vstring_free(info->buf);
+    htable_free(info->table, (void (*) (char *)) 0);
+    myfree((char *) info);
+}
diff --git a/postfix/src/global/delivered_hdr.h b/postfix/src/global/delivered_hdr.h
new file mode 100644
index 000000000..4a7ca25b7
--- /dev/null
+++ b/postfix/src/global/delivered_hdr.h
@@ -0,0 +1,38 @@
+#ifndef _DELIVERED_HDR_H_INCLUDED_
+#define _DELIVERED_HDR_H_INCLUDED_
+
+/*++
+/* NAME
+/*	delivered_hdr 3h
+/* SUMMARY
+/*	process Delivered-To: headers
+/* SYNOPSIS
+/*	#include 
+/* DESCRIPTION
+/* .nf
+
+ /*
+  * Utility library.
+  */
+#include 
+
+ /*
+  * External interface.
+  */
+typedef struct DELIVERED_HDR_INFO DELIVERED_HDR_INFO;
+extern DELIVERED_HDR_INFO *delivered_hdr_init(VSTREAM *, off_t);
+extern int delivered_hdr_find(DELIVERED_HDR_INFO *, const char *);
+extern void delivered_hdr_free(DELIVERED_HDR_INFO *);
+
+/* 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
diff --git a/postfix/src/global/mail_version.h b/postfix/src/global/mail_version.h
index da7a1f3d3..b5f4ec0d8 100644
--- a/postfix/src/global/mail_version.h
+++ b/postfix/src/global/mail_version.h
@@ -20,7 +20,7 @@
   * Patches change both the patchlevel and the release date. Snapshots have no
   * patchlevel; they change the release date only.
   */
-#define MAIL_RELEASE_DATE	"20070402"
+#define MAIL_RELEASE_DATE	"20070422"
 #define MAIL_VERSION_NUMBER	"2.5"
 
 #ifdef SNAPSHOT
diff --git a/postfix/src/global/resolve_clnt.c b/postfix/src/global/resolve_clnt.c
index b96f0a736..4668deadd 100644
--- a/postfix/src/global/resolve_clnt.c
+++ b/postfix/src/global/resolve_clnt.c
@@ -141,6 +141,7 @@
   */
 extern CLNT_STREAM *rewrite_clnt_stream;
 
+static time_t last_expire;
 static VSTRING *last_class;
 static VSTRING *last_sender;
 static VSTRING *last_addr;
@@ -190,7 +191,8 @@ void    resolve_clnt(const char *class, const char *sender,
      */
 #define IFSET(flag, text) ((reply->flags & (flag)) ? (text) : "")
 
-    if (*addr && strcmp(addr, STR(last_addr)) == 0
+    if (event_time() < last_expire
+	&& *addr && strcmp(addr, STR(last_addr)) == 0
 	&& strcmp(class, STR(last_class)) == 0
 	&& strcmp(sender, STR(last_sender)) == 0) {
 	vstring_strcpy(reply->transport, STR(last_reply.transport));
@@ -282,6 +284,7 @@ void    resolve_clnt(const char *class, const char *sender,
     vstring_strcpy(last_reply.nexthop, STR(reply->nexthop));
     vstring_strcpy(last_reply.recipient, STR(reply->recipient));
     last_reply.flags = reply->flags;
+    last_expire = event_time() + 30;		/* XXX make configurable */
 }
 
 /* resolve_clnt_free - destroy reply */
diff --git a/postfix/src/global/rewrite_clnt.c b/postfix/src/global/rewrite_clnt.c
index 8bb3f0a52..6d110bf59 100644
--- a/postfix/src/global/rewrite_clnt.c
+++ b/postfix/src/global/rewrite_clnt.c
@@ -72,6 +72,7 @@
   */
 CLNT_STREAM *rewrite_clnt_stream = 0;
 
+static time_t last_expire;
 static VSTRING *last_rule;
 static VSTRING *last_addr;
 static VSTRING *last_result;
@@ -107,7 +108,8 @@ VSTRING *rewrite_clnt(const char *rule, const char *addr, VSTRING *result)
     /*
      * Peek at the cache.
      */
-    if (strcmp(addr, STR(last_addr)) == 0
+    if (event_time() < last_expire
+	&& strcmp(addr, STR(last_addr)) == 0
 	&& strcmp(rule, STR(last_rule)) == 0) {
 	vstring_strcpy(result, STR(last_result));
 	if (msg_verbose)
@@ -163,6 +165,7 @@ VSTRING *rewrite_clnt(const char *rule, const char *addr, VSTRING *result)
     vstring_strcpy(last_rule, rule);
     vstring_strcpy(last_addr, addr);
     vstring_strcpy(last_result, STR(result));
+    last_expire = event_time() + 30;		/* XXX make configurable */
 
     return (result);
 }
diff --git a/postfix/src/local/Makefile.in b/postfix/src/local/Makefile.in
index f22a5646e..10962b5cc 100644
--- a/postfix/src/local/Makefile.in
+++ b/postfix/src/local/Makefile.in
@@ -1,9 +1,9 @@
 SHELL	= /bin/sh
-SRCS	= alias.c command.c delivered.c dotforward.c file.c forward.c \
+SRCS	= alias.c command.c dotforward.c file.c forward.c \
 	include.c indirect.c local.c mailbox.c recipient.c resolve.c token.c \
 	deliver_attr.c maildir.c biff_notify.c unknown.c \
 	local_expand.c
-OBJS	= alias.o command.o delivered.o dotforward.o file.o forward.o \
+OBJS	= alias.o command.o dotforward.o file.o forward.o \
 	include.o indirect.o local.o mailbox.o recipient.o resolve.o token.o \
 	deliver_attr.o maildir.o biff_notify.o unknown.o \
 	local_expand.o
@@ -71,6 +71,7 @@ alias.o: ../../include/bounce.h
 alias.o: ../../include/canon_addr.h
 alias.o: ../../include/defer.h
 alias.o: ../../include/deliver_request.h
+alias.o: ../../include/delivered_hdr.h
 alias.o: ../../include/dict.h
 alias.o: ../../include/dsn.h
 alias.o: ../../include/dsn_buf.h
@@ -105,6 +106,7 @@ command.o: ../../include/been_here.h
 command.o: ../../include/bounce.h
 command.o: ../../include/defer.h
 command.o: ../../include/deliver_request.h
+command.o: ../../include/delivered_hdr.h
 command.o: ../../include/dict.h
 command.o: ../../include/dsn.h
 command.o: ../../include/dsn_buf.h
@@ -132,6 +134,7 @@ deliver_attr.o: ../../include/argv.h
 deliver_attr.o: ../../include/attr.h
 deliver_attr.o: ../../include/been_here.h
 deliver_attr.o: ../../include/deliver_request.h
+deliver_attr.o: ../../include/delivered_hdr.h
 deliver_attr.o: ../../include/dict.h
 deliver_attr.o: ../../include/dsn.h
 deliver_attr.o: ../../include/dsn_buf.h
@@ -149,40 +152,12 @@ deliver_attr.o: ../../include/vstream.h
 deliver_attr.o: ../../include/vstring.h
 deliver_attr.o: deliver_attr.c
 deliver_attr.o: local.h
-delivered.o: ../../include/argv.h
-delivered.o: ../../include/attr.h
-delivered.o: ../../include/been_here.h
-delivered.o: ../../include/deliver_request.h
-delivered.o: ../../include/dict.h
-delivered.o: ../../include/dsn.h
-delivered.o: ../../include/dsn_buf.h
-delivered.o: ../../include/header_opts.h
-delivered.o: ../../include/htable.h
-delivered.o: ../../include/is_header.h
-delivered.o: ../../include/maps.h
-delivered.o: ../../include/mbox_conf.h
-delivered.o: ../../include/msg.h
-delivered.o: ../../include/msg_stats.h
-delivered.o: ../../include/quote_822_local.h
-delivered.o: ../../include/quote_flags.h
-delivered.o: ../../include/rec_type.h
-delivered.o: ../../include/recipient_list.h
-delivered.o: ../../include/record.h
-delivered.o: ../../include/resolve_clnt.h
-delivered.o: ../../include/stringops.h
-delivered.o: ../../include/sys_defs.h
-delivered.o: ../../include/tok822.h
-delivered.o: ../../include/vbuf.h
-delivered.o: ../../include/vstream.h
-delivered.o: ../../include/vstring.h
-delivered.o: ../../include/vstring_vstream.h
-delivered.o: delivered.c
-delivered.o: local.h
 dotforward.o: ../../include/argv.h
 dotforward.o: ../../include/attr.h
 dotforward.o: ../../include/been_here.h
 dotforward.o: ../../include/bounce.h
 dotforward.o: ../../include/deliver_request.h
+dotforward.o: ../../include/delivered_hdr.h
 dotforward.o: ../../include/dict.h
 dotforward.o: ../../include/dsn.h
 dotforward.o: ../../include/dsn_buf.h
@@ -221,6 +196,7 @@ file.o: ../../include/bounce.h
 file.o: ../../include/defer.h
 file.o: ../../include/deliver_flock.h
 file.o: ../../include/deliver_request.h
+file.o: ../../include/delivered_hdr.h
 file.o: ../../include/dict.h
 file.o: ../../include/dsn.h
 file.o: ../../include/dsn_buf.h
@@ -252,6 +228,7 @@ forward.o: ../../include/been_here.h
 forward.o: ../../include/bounce.h
 forward.o: ../../include/cleanup_user.h
 forward.o: ../../include/deliver_request.h
+forward.o: ../../include/delivered_hdr.h
 forward.o: ../../include/dict.h
 forward.o: ../../include/dsn.h
 forward.o: ../../include/dsn_buf.h
@@ -287,6 +264,7 @@ include.o: ../../include/been_here.h
 include.o: ../../include/bounce.h
 include.o: ../../include/defer.h
 include.o: ../../include/deliver_request.h
+include.o: ../../include/delivered_hdr.h
 include.o: ../../include/dict.h
 include.o: ../../include/dsn.h
 include.o: ../../include/dsn_buf.h
@@ -318,6 +296,7 @@ indirect.o: ../../include/been_here.h
 indirect.o: ../../include/bounce.h
 indirect.o: ../../include/defer.h
 indirect.o: ../../include/deliver_request.h
+indirect.o: ../../include/delivered_hdr.h
 indirect.o: ../../include/dict.h
 indirect.o: ../../include/dsn.h
 indirect.o: ../../include/dsn_buf.h
@@ -342,6 +321,7 @@ local.o: ../../include/attr.h
 local.o: ../../include/been_here.h
 local.o: ../../include/deliver_completed.h
 local.o: ../../include/deliver_request.h
+local.o: ../../include/delivered_hdr.h
 local.o: ../../include/dict.h
 local.o: ../../include/dsn.h
 local.o: ../../include/dsn_buf.h
@@ -374,6 +354,7 @@ local_expand.o: ../../include/argv.h
 local_expand.o: ../../include/attr.h
 local_expand.o: ../../include/been_here.h
 local_expand.o: ../../include/deliver_request.h
+local_expand.o: ../../include/delivered_hdr.h
 local_expand.o: ../../include/dict.h
 local_expand.o: ../../include/dsn.h
 local_expand.o: ../../include/dsn_buf.h
@@ -400,6 +381,7 @@ mailbox.o: ../../include/bounce.h
 mailbox.o: ../../include/defer.h
 mailbox.o: ../../include/deliver_pass.h
 mailbox.o: ../../include/deliver_request.h
+mailbox.o: ../../include/delivered_hdr.h
 mailbox.o: ../../include/dict.h
 mailbox.o: ../../include/dsn.h
 mailbox.o: ../../include/dsn_buf.h
@@ -436,6 +418,7 @@ maildir.o: ../../include/been_here.h
 maildir.o: ../../include/bounce.h
 maildir.o: ../../include/defer.h
 maildir.o: ../../include/deliver_request.h
+maildir.o: ../../include/delivered_hdr.h
 maildir.o: ../../include/dict.h
 maildir.o: ../../include/dsn.h
 maildir.o: ../../include/dsn_buf.h
@@ -472,6 +455,7 @@ recipient.o: ../../include/bounce.h
 recipient.o: ../../include/canon_addr.h
 recipient.o: ../../include/defer.h
 recipient.o: ../../include/deliver_request.h
+recipient.o: ../../include/delivered_hdr.h
 recipient.o: ../../include/dict.h
 recipient.o: ../../include/dsn.h
 recipient.o: ../../include/dsn_buf.h
@@ -504,6 +488,7 @@ resolve.o: ../../include/been_here.h
 resolve.o: ../../include/bounce.h
 resolve.o: ../../include/defer.h
 resolve.o: ../../include/deliver_request.h
+resolve.o: ../../include/delivered_hdr.h
 resolve.o: ../../include/dict.h
 resolve.o: ../../include/dsn.h
 resolve.o: ../../include/dsn_buf.h
@@ -531,6 +516,7 @@ token.o: ../../include/been_here.h
 token.o: ../../include/bounce.h
 token.o: ../../include/defer.h
 token.o: ../../include/deliver_request.h
+token.o: ../../include/delivered_hdr.h
 token.o: ../../include/dict.h
 token.o: ../../include/dsn.h
 token.o: ../../include/dsn_buf.h
@@ -559,6 +545,7 @@ unknown.o: ../../include/been_here.h
 unknown.o: ../../include/bounce.h
 unknown.o: ../../include/deliver_pass.h
 unknown.o: ../../include/deliver_request.h
+unknown.o: ../../include/delivered_hdr.h
 unknown.o: ../../include/dict.h
 unknown.o: ../../include/dsn.h
 unknown.o: ../../include/dsn_buf.h
diff --git a/postfix/src/local/delivered.c b/postfix/src/local/delivered.c
deleted file mode 100644
index 20156ce5b..000000000
--- a/postfix/src/local/delivered.c
+++ /dev/null
@@ -1,142 +0,0 @@
-/*++
-/* NAME
-/*	delivered 3
-/* SUMMARY
-/*	process Delivered-To: headers
-/* SYNOPSIS
-/*	#include "local.h"
-/*
-/*	HTABLE	*delivered_init(attr)
-/*	DELIVER_ATTR attr;
-/*
-/*	int	delivered_find(table, address)
-/*	HTABLE	*table;
-/*	const char *address;
-/*
-/*	void	delivered_free(table)
-/*	HTABLE	*table;
-/* DESCRIPTION
-/*	This module processes addresses in Delivered-To: headers.
-/*	These headers are added by some mail delivery systems, for the
-/*	purpose of breaking mail forwarding loops. N.B. This solves
-/*	a different problem than the Received: hop count limit. Hop
-/*	counts are used to limit the impact of mail routing problems.
-/*
-/*	delivered_init() extracts Delivered-To: header addresses
-/*	from the specified message, and returns a table with the
-/*	result.
-/*
-/*	delivered_find() looks up the address in the lookup table,
-/*	and returns non-zero when the address was found. The
-/*	address argument must be in internalized form.
-/*
-/*	delivered_free() releases storage that was allocated by
-/*	delivered_init().
-/*
-/*	Arguments:
-/* .IP state
-/*	The attributes that specify the message, recipient and more.
-/* .IP table
-/*	A table with extracted Delivered-To: addresses.
-/* .IP address
-/*	A recipient address, internal form.
-/* DIAGNOSTICS
-/*	Fatal errors: out of memory.
-/* SEE ALSO
-/* 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 
-#include 
-#include 
-#include 
-
-/* Utility library. */
-
-#include 
-#include 
-#include 
-#include 
-#include 
-#include 
-
-/* Global library. */
-
-#include 
-#include 
-#include 
-#include 
-#include 
-
-/* Application-specific. */
-
-#include "local.h"
-
-static VSTRING *buf;
-
-/* delivered_init - extract delivered-to information from the message */
-
-HTABLE *delivered_init(DELIVER_ATTR attr)
-{
-    char   *cp;
-    HTABLE *table = htable_create(0);
-    HEADER_OPTS *hdr;
-
-    if (buf == 0)
-	buf = vstring_alloc(10);
-
-    if (vstream_fseek(attr.fp, attr.offset, SEEK_SET) < 0)
-	msg_fatal("seek queue file %s: %m", VSTREAM_PATH(attr.fp));
-
-    /*
-     * XXX Assume that normal mail systems produce headers that fit in a
-     * REC_TYPE_NORM record. Lowercase the delivered-to addresses for
-     * consistency.
-     */
-    while (rec_get(attr.fp, buf, 0) == REC_TYPE_NORM) {
-	if (is_header(STR(buf))) {
-	    if ((hdr = header_opts_find(STR(buf))) != 0
-		&& hdr->type == HDR_DELIVERED_TO) {
-		cp = STR(buf) + strlen(hdr->name) + 1;
-		while (ISSPACE(*cp))
-		    cp++;
-		lowercase(cp);
-		if (msg_verbose)
-		    msg_info("delivered_init: %s", cp);
-		htable_enter(table, cp, (char *) 0);
-	    }
-	} else if (ISSPACE(STR(buf)[0])) {
-	    continue;
-	} else {
-	    break;
-	}
-    }
-    return (table);
-}
-
-/* delivered_find - look up recipient in delivered table */
-
-int     delivered_find(HTABLE *table, const char *address)
-{
-    HTABLE_INFO *ht;
-
-    /*
-     * mail_copy() uses quote_822_local() when writing the Delivered-To:
-     * header. We must therefore apply the same transformation when looking
-     * up the recipient. Lowercase the delivered-to address for consistency.
-     */
-    quote_822_local(buf, address);
-    lowercase(STR(buf));
-    ht = htable_locate(table, STR(buf));
-    return (ht != 0);
-}
diff --git a/postfix/src/local/local.c b/postfix/src/local/local.c
index 557be6f4d..f46ed71b3 100644
--- a/postfix/src/local/local.c
+++ b/postfix/src/local/local.c
@@ -693,7 +693,7 @@ static int local_deliver(DELIVER_REQUEST *rqst, char *service)
     state.msg_attr.request = rqst;
     RESET_OWNER_ATTR(state.msg_attr, state.level);
     RESET_USER_ATTR(usr_attr, state.level);
-    state.loop_info = delivered_init(state.msg_attr);	/* delivered-to */
+    state.loop_info = delivered_hdr_init(rqst->fp, rqst->data_offset);
     state.request = rqst;
 
     /*
@@ -717,7 +717,7 @@ static int local_deliver(DELIVER_REQUEST *rqst, char *service)
     /*
      * Clean up.
      */
-    delivered_free(state.loop_info);
+    delivered_hdr_free(state.loop_info);
     deliver_attr_free(&state.msg_attr);
 
     return (msg_stat);
diff --git a/postfix/src/local/local.h b/postfix/src/local/local.h
index 67c3a9403..d63c99a65 100644
--- a/postfix/src/local/local.h
+++ b/postfix/src/local/local.h
@@ -25,6 +25,7 @@
 #include 
 #include 
 #include 
+#include 
 
  /*
   * User attributes: these control the privileges for delivery to external
@@ -107,7 +108,7 @@ typedef struct LOCAL_STATE {
     int     level;			/* nesting level, for logging */
     DELIVER_ATTR msg_attr;		/* message attributes */
     BH_TABLE *dup_filter;		/* internal duplicate filter */
-    HTABLE *loop_info;			/* external loop filter */
+    DELIVERED_HDR_INFO *loop_info;	/* external loop filter */
     DELIVER_REQUEST *request;		/* as from queue manager */
 } LOCAL_STATE;
 
@@ -203,14 +204,6 @@ extern int local_mbox_lock_mask;
 
 extern int local_deliver_hdr_mask;
 
- /*
-  * delivered.c
-  */
-extern HTABLE *delivered_init(DELIVER_ATTR);
-extern int delivered_find(HTABLE *, const char *);
-
-#define delivered_free(t) htable_free((t), (void (*) (char *)) 0)
-
  /*
   * forward.c
   */
diff --git a/postfix/src/local/recipient.c b/postfix/src/local/recipient.c
index e3bab10d0..e279eef0e 100644
--- a/postfix/src/local/recipient.c
+++ b/postfix/src/local/recipient.c
@@ -229,7 +229,7 @@ int     deliver_recipient(LOCAL_STATE state, USER_ATTR usr_attr)
      * need for VERP specific bouncing code, at the cost of complicating the
      * normal bounce sending procedure, but would simplify the code below.
      */
-    if (delivered_find(state.loop_info, state.msg_attr.rcpt.address)) {
+    if (delivered_hdr_find(state.loop_info, state.msg_attr.rcpt.address)) {
 	VSTRING *canon_owner = 0;
 
 	if (var_ownreq_special) {
diff --git a/postfix/src/pipe/Makefile.in b/postfix/src/pipe/Makefile.in
index c661d7420..b9f534fbd 100644
--- a/postfix/src/pipe/Makefile.in
+++ b/postfix/src/pipe/Makefile.in
@@ -64,6 +64,7 @@ pipe.o: ../../include/canon_addr.h
 pipe.o: ../../include/defer.h
 pipe.o: ../../include/deliver_completed.h
 pipe.o: ../../include/deliver_request.h
+pipe.o: ../../include/delivered_hdr.h
 pipe.o: ../../include/dict.h
 pipe.o: ../../include/dsn.h
 pipe.o: ../../include/dsn_buf.h
diff --git a/postfix/src/pipe/pipe.c b/postfix/src/pipe/pipe.c
index d56584ca7..ec163e15d 100644
--- a/postfix/src/pipe/pipe.c
+++ b/postfix/src/pipe/pipe.c
@@ -23,9 +23,11 @@
 /* SINGLE-RECIPIENT DELIVERY
 /* .ad
 /* .fi
-/*	Some external commands cannot handle more than one recipient
-/*	per delivery request. Examples of such transports are pagers
-/*	or fax machines.
+/*	Some destinations cannot handle more than one recipient per
+/*	delivery request. Examples are pagers or fax machines.
+/*	In addition, multi-recipient delivery is undesirable when
+/*	prepending a \fBDelivered-to:\fR or \fBX-Original-To:\fR
+/*	message header.
 /*
 /*	To prevent Postfix from sending multiple recipients per delivery
 /*	request, specify
@@ -74,7 +76,13 @@
 /* .IP \fBD\fR
 /*	Prepend a "\fBDelivered-To: \fIrecipient\fR" message header with the
 /*	envelope recipient address. Note: for this to work, the
-/*	\fItransport\fB_destination_recipient_limit\fR must be 1.
+/*	\fItransport\fB_destination_recipient_limit\fR must be 1
+/*	(see SINGLE-RECIPIENT DELIVERY above for details).
+/* .sp
+/*	This code also enforces loop detection (Postfix 2.5 and later).
+/*	If a message already contains a \fBDelivered-To:\fR header
+/*	with the same recipient address, then the message is
+/*	returned as undeliverable.
 /* .sp
 /*	This feature is available as of Postfix 2.0.
 /* .IP \fBF\fR
@@ -84,15 +92,18 @@
 /* .IP \fBO\fR
 /*	Prepend an "\fBX-Original-To: \fIrecipient\fR" message header
 /*	with the recipient address as given to Postfix. Note: for this to
-/*	work, the \fItransport\fB_destination_recipient_limit\fR must be 1.
+/*	work, the \fItransport\fB_destination_recipient_limit\fR must be 1
+/*	(see SINGLE-RECIPIENT DELIVERY above for details).
 /* .sp
 /*	This feature is available as of Postfix 2.0.
 /* .IP \fBR\fR
 /*	Prepend a \fBReturn-Path:\fR message header with the envelope sender
 /*	address.
 /* .IP \fBh\fR
-/*	Fold the command-line \fB$recipient\fR domain name and \fB$nexthop\fR
-/*	host name to lower case.
+/*	Fold the command-line \fB$recipient\fR address domain part
+/*	(text to the right of the right-most \fB@\fR character) to
+/*	lower case; fold the entire command-line \fB$domain\fR and
+/*	\fB$nexthop\fR host or domain information to lower case.
 /*	This is recommended for delivery via \fBUUCP\fR.
 /* .IP \fBq\fR
 /*	Quote white space and other special characters in the command-line
@@ -149,7 +160,7 @@
 /*	    command -f $sender -- $recipient (\fIgood\fR)
 /* .fi
 /* .IP
-/*	This feature is available with Postfix 2.3 and later.
+/*	This feature is available as of Postfix 2.3.
 /* .IP "\fBsize\fR=\fIsize_limit\fR (optional)"
 /*	Messages greater in size than this limit (in bytes) will
 /*	be returned to the sender as undeliverable.
@@ -191,6 +202,14 @@
 /*	This macro expands to the remote client protocol.
 /* .sp
 /*	This is available in Postfix 2.2 and later.
+/* .IP \fB${\fBdomain\fR}\fR
+/*	This macro expands to the domain portion of the recipient
+/*	address.  For example, with an address \fIuser+foo@domain\fR
+/*	the domain is \fIdomain\fR.
+/* .sp
+/*	This information is modified by the \fBh\fR flag for case folding.
+/* .sp
+/*	This is available in Postfix 2.5 and later.
 /* .IP \fB${\fBextension\fR}\fR
 /*	This macro expands to the extension part of a recipient address.
 /*	For example, with an address \fIuser+foo@domain\fR the extension is
@@ -420,6 +439,7 @@
 #include 
 #include 
 #include 
+#include 
 
 /* Single server skeleton. */
 
@@ -441,6 +461,7 @@
 #define PIPE_DICT_USER		"user"	/* key */
 #define PIPE_DICT_EXTENSION	"extension"	/* key */
 #define PIPE_DICT_MAILBOX	"mailbox"	/* key */
+#define PIPE_DICT_DOMAIN	"domain"/* key */
 #define PIPE_DICT_SIZE		"size"	/* key */
 #define PIPE_DICT_CLIENT_ADDR	"client_address"	/* key */
 #define PIPE_DICT_CLIENT_NAME	"client_hostname"	/* key */
@@ -458,6 +479,7 @@
 #define PIPE_FLAG_USER		(1<<1)
 #define PIPE_FLAG_EXTENSION	(1<<2)
 #define PIPE_FLAG_MAILBOX	(1<<3)
+#define PIPE_FLAG_DOMAIN	(1<<4)
 
  /*
   * Additional flags. These are colocated with mail_copy() flags. Allow some
@@ -531,6 +553,7 @@ static int parse_callback(int type, VSTRING *buf, char *context)
 	PIPE_DICT_USER, PIPE_FLAG_USER,
 	PIPE_DICT_EXTENSION, PIPE_FLAG_EXTENSION,
 	PIPE_DICT_MAILBOX, PIPE_FLAG_MAILBOX,
+	PIPE_DICT_DOMAIN, PIPE_FLAG_DOMAIN,
 	PIPE_DICT_SIZE, 0,
 	PIPE_DICT_CLIENT_ADDR, 0,
 	PIPE_DICT_CLIENT_NAME, 0,
@@ -609,6 +632,7 @@ static ARGV *expand_argv(const char *service, char **argv,
     PIPE_STATE state;
     int     i;
     char   *ext;
+    char   *dom;
 
     /*
      * This appears to be simple operation (replace $name by its expansion).
@@ -700,6 +724,22 @@ static ARGV *expand_argv(const char *service, char **argv,
 		    dict_update(PIPE_DICT_TABLE, PIPE_DICT_MAILBOX, STR(buf));
 		}
 
+		/*
+		 * This argument contains $domain. Extract the domain name:
+		 * anything to the right of the rightmost @.
+		 */
+		if (state.expand_flag & PIPE_FLAG_DOMAIN) {
+		    morph_recipient(buf, rcpt_list->info[i].address,
+				    flags & PIPE_OPT_FOLD_FLAGS);
+		    dom = split_at_right(STR(buf), '@');
+		    if (dom == 0) {
+			msg_warn("no @ in recipient address: %s",
+				 rcpt_list->info[i].address);
+			dom = "";		/* insert null arg */
+		    }
+		    dict_update(PIPE_DICT_TABLE, PIPE_DICT_DOMAIN, dom);
+		}
+
 		/*
 		 * Done.
 		 */
@@ -1073,6 +1113,32 @@ static int deliver_message(DELIVER_REQUEST *request, char *service, char **argv)
 	return (deliver_status);
     }
 
+    /*
+     * Report mail delivery loops. By definition, this requires
+     * single-recipient delivery. Don't silently lose recipients.
+     */
+    if (attr.flags & MAIL_COPY_DELIVERED) {
+	DELIVERED_HDR_INFO *info;
+	RECIPIENT *rcpt;
+	int     loop_found;
+
+	if (request->rcpt_list.len > 1)
+	    msg_panic("%s: delivered-to enabled with multi-recipient request",
+		      myname);
+	info = delivered_hdr_init(request->fp, request->data_offset);
+	rcpt = request->rcpt_list.info;
+	loop_found = delivered_hdr_find(info, rcpt->address);
+	delivered_hdr_free(info);
+	if (loop_found) {
+	    dsb_simple(why, "5.4.6", "mail forwarding loop for %s",
+		       rcpt->address);
+	    deliver_status = eval_command_status(PIPE_STAT_BOUNCE, service,
+						 request, request->fp, why);
+	    DELIVER_MSG_CLEANUP();
+	    return (deliver_status);
+	}
+    }
+
     /*
      * Deliver. Set the nexthop and sender variables, and expand the command
      * argument vector. Recipients will be expanded on the fly. XXX Rewrite
diff --git a/postfix/src/smtpd/smtpd.c b/postfix/src/smtpd/smtpd.c
index 601a1f538..70522679f 100644
--- a/postfix/src/smtpd/smtpd.c
+++ b/postfix/src/smtpd/smtpd.c
@@ -2131,6 +2131,10 @@ static void mail_reset(SMTPD_STATE *state)
 	myfree(state->saved_redirect);
 	state->saved_redirect = 0;
     }
+    if (state->saved_bcc) {
+	myfree(state->saved_bcc);
+	state->saved_bcc = 0;
+    }
     state->saved_flags = 0;
 #ifdef DELAY_ACTION
     state->saved_delay = 0;
@@ -2712,6 +2716,12 @@ static int data_cmd(SMTPD_STATE *state, int argc, SMTPD_TOKEN *unused_argv)
 	    if (state->saved_redirect)
 		rec_fprintf(state->cleanup, REC_TYPE_RDR, "%s",
 			    state->saved_redirect);
+	    if (state->saved_bcc) {
+		rec_fprintf(state->cleanup, REC_TYPE_RCPT, "%s",
+			    state->saved_bcc);
+		rec_fprintf(state->cleanup, REC_TYPE_ATTR, "%s=%d",
+			    MAIL_ATTR_DSN_NOTIFY, DSN_NOTIFY_NEVER);
+	    }
 	    if (state->saved_flags)
 		rec_fprintf(state->cleanup, REC_TYPE_FLGS, "%d",
 			    state->saved_flags);
diff --git a/postfix/src/smtpd/smtpd.h b/postfix/src/smtpd/smtpd.h
index 44478605c..d44978050 100644
--- a/postfix/src/smtpd/smtpd.h
+++ b/postfix/src/smtpd/smtpd.h
@@ -133,6 +133,7 @@ typedef struct SMTPD_STATE {
     int     discard;			/* discard message */
     char   *saved_filter;		/* postponed filter action */
     char   *saved_redirect;		/* postponed redirect action */
+    char   *saved_bcc;			/* postponed bcc action */
     int     saved_flags;		/* postponed hold/discard */
 #ifdef DELAY_ACTION
     int     saved_delay;		/* postponed deferred delay */
diff --git a/postfix/src/smtpd/smtpd_check.c b/postfix/src/smtpd/smtpd_check.c
index dde160828..6a3e14351 100644
--- a/postfix/src/smtpd/smtpd_check.c
+++ b/postfix/src/smtpd/smtpd_check.c
@@ -2099,6 +2099,32 @@ static int check_table_result(SMTPD_STATE *state, const char *table,
 	}
     }
 
+    /*
+     * BCC means deliver to designated recipient. But we may still
+     * change our mind, and reject/discard the message for other reasons.
+     */
+#ifdef SNAPSHOT
+    if (STREQUAL(value, "BCC", cmd_len)) {
+#ifndef TEST
+	if (can_delegate_action(state, table, "BCC", reply_class) == 0)
+	    return (SMTPD_CHECK_DUNNO);
+#endif
+	if (strchr(cmd_text, '@') == 0) {
+	    msg_warn("access table %s entry \"%s\" requires user@domain target",
+		     table, datum);
+	    return (SMTPD_CHECK_DUNNO);
+	} else {
+	    vstring_sprintf(error_text, "<%s>: %s triggers BCC %s",
+			    reply_name, reply_class, cmd_text);
+	    log_whatsup(state, "bcc", STR(error_text));
+#ifndef TEST
+	    UPDATE_STRING(state->saved_bcc, cmd_text);
+#endif
+	    return (SMTPD_CHECK_DUNNO);
+	}
+    }
+#endif
+
     /*
      * DEFER_IF_PERMIT changes "permit" into "maybe". Use optional text or
      * generate a generic error response.
diff --git a/postfix/src/smtpd/smtpd_state.c b/postfix/src/smtpd/smtpd_state.c
index 1f8092dbf..a104a1174 100644
--- a/postfix/src/smtpd/smtpd_state.c
+++ b/postfix/src/smtpd/smtpd_state.c
@@ -123,6 +123,7 @@ void    smtpd_state_init(SMTPD_STATE *state, VSTREAM *stream,
     state->proxy_xforward_features = 0;
     state->saved_filter = 0;
     state->saved_redirect = 0;
+    state->saved_bcc = 0;
     state->saved_flags = 0;
 #ifdef DELAY_ACTION
     state->saved_delay = 0;
diff --git a/postfix/src/trivial-rewrite/Makefile.in b/postfix/src/trivial-rewrite/Makefile.in
index 6b3ae51a3..6f4e24e8f 100644
--- a/postfix/src/trivial-rewrite/Makefile.in
+++ b/postfix/src/trivial-rewrite/Makefile.in
@@ -119,6 +119,7 @@ rewrite.o: trivial-rewrite.h
 transport.o: ../../include/argv.h
 transport.o: ../../include/attr.h
 transport.o: ../../include/dict.h
+transport.o: ../../include/events.h
 transport.o: ../../include/iostuff.h
 transport.o: ../../include/mail_params.h
 transport.o: ../../include/mail_proto.h
diff --git a/postfix/src/trivial-rewrite/transport.c b/postfix/src/trivial-rewrite/transport.c
index 890517746..7750a372b 100644
--- a/postfix/src/trivial-rewrite/transport.c
+++ b/postfix/src/trivial-rewrite/transport.c
@@ -69,6 +69,7 @@
 #include 
 #include 
 #include 
+#include 
 
 /* Global library. */
 
@@ -99,8 +100,10 @@ TRANSPORT_INFO *transport_pre_init(const char *transport_maps_name,
     tp->transport_path = maps_create(transport_maps_name, transport_maps,
 				     DICT_FLAG_LOCK | DICT_FLAG_FOLD_FIX
 				     | DICT_FLAG_NO_REGSUB);
-    tp->wildcard_channel = tp->wildcard_nexthop = 0;
+    tp->wildcard_channel = vstring_alloc(10);
+    tp->wildcard_nexthop = vstring_alloc(10);
     tp->transport_errno = 0;
+    tp->expire = 0;
     return (tp);
 }
 
@@ -204,8 +207,6 @@ static int find_transport_entry(TRANSPORT_INFO *tp, const char *key,
 
 static void transport_wildcard_init(TRANSPORT_INFO *tp)
 {
-    VSTRING *channel = vstring_alloc(10);
-    VSTRING *nexthop = vstring_alloc(10);
 
     /*
      * Technically, the wildcard lookup pattern is redundant. A static map
@@ -221,22 +222,20 @@ static void transport_wildcard_init(TRANSPORT_INFO *tp)
 #define FULL		0
 #define PARTIAL		DICT_FLAG_FIXED
 
-    if (find_transport_entry(tp, WILDCARD, "", FULL, channel, nexthop)) {
+    if (find_transport_entry(tp, WILDCARD, "", FULL,
+			     tp->wildcard_channel,
+			     tp->wildcard_nexthop)) {
 	tp->transport_errno = 0;
-	if (tp->wildcard_channel)
-	    vstring_free(tp->wildcard_channel);
-	tp->wildcard_channel = channel;
-	if (tp->wildcard_nexthop)
-	    vstring_free(tp->wildcard_nexthop);
-	tp->wildcard_nexthop = nexthop;
 	if (msg_verbose)
 	    msg_info("wildcard_{chan:hop}={%s:%s}",
-		     vstring_str(channel), vstring_str(nexthop));
+		     vstring_str(tp->wildcard_channel),
+		     vstring_str(tp->wildcard_nexthop));
     } else {
 	tp->transport_errno = dict_errno;
-	vstring_free(channel);
-	vstring_free(nexthop);
+	VSTRING_RESET(tp->wildcard_channel);
+	VSTRING_RESET(tp->wildcard_nexthop);
     }
+    tp->expire = event_time() + 30;		/* XXX make configurable */
 }
 
 /* transport_lookup - map a transport domain */
@@ -321,12 +320,12 @@ int     transport_lookup(TRANSPORT_INFO *tp, const char *addr,
     /*
      * Fall back to the wild-card entry.
      */
-    if (tp->transport_errno)
+    if (tp->transport_errno || event_time() > tp->expire)
 	transport_wildcard_init(tp);
     if (tp->transport_errno) {
 	dict_errno = tp->transport_errno;
 	return (NOTFOUND);
-    } else if (tp->wildcard_channel) {
+    } else if (tp->wildcard_channel && VSTRING_LEN(tp->wildcard_channel)) {
 	update_entry(STR(tp->wildcard_channel), STR(tp->wildcard_nexthop),
 		     rcpt_domain, channel, nexthop);
 	return (FOUND);
diff --git a/postfix/src/trivial-rewrite/transport.h b/postfix/src/trivial-rewrite/transport.h
index 27912d821..0660f97b5 100644
--- a/postfix/src/trivial-rewrite/transport.h
+++ b/postfix/src/trivial-rewrite/transport.h
@@ -8,6 +8,11 @@
 /* DESCRIPTION
 /* .nf
 
+ /*
+  * System library.
+  */
+#include 
+
  /*
   * Utility library.
   */
@@ -26,6 +31,7 @@ typedef struct TRANSPORT_INFO {
     VSTRING *wildcard_channel;
     VSTRING *wildcard_nexthop;
     int     transport_errno;
+    time_t  expire;
 } TRANSPORT_INFO;
 
 extern TRANSPORT_INFO *transport_pre_init(const char *, const char *);