]> git.ipfire.org Git - thirdparty/postfix.git/commitdiff
postfix-1.1.11-20020803
authorWietse Venema <wietse@porcupine.org>
Sat, 3 Aug 2002 05:00:00 +0000 (00:00 -0500)
committerViktor Dukhovni <viktor@dukhovni.org>
Tue, 5 Feb 2013 06:28:07 +0000 (06:28 +0000)
43 files changed:
postfix/HISTORY
postfix/README_FILES/DEBUG_README
postfix/RELEASE_NOTES
postfix/conf/main.cf
postfix/conf/postfix-files
postfix/conf/sample-smtpd.cf
postfix/html/local.8.html
postfix/html/postsuper.1.html
postfix/html/sendmail.1.html
postfix/html/smtpd.8.html
postfix/html/uce.html
postfix/makedefs
postfix/man/man1/postsuper.1
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_one_service.c [new file with mode: 0644]
postfix/src/bounce/bounce_service.h
postfix/src/cleanup/cleanup_message.c
postfix/src/global/bounce.c
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_queue.h
postfix/src/global/mail_version.h
postfix/src/local/Makefile.in
postfix/src/local/alias.c
postfix/src/local/local.c
postfix/src/local/local.h
postfix/src/local/recipient.c
postfix/src/postsuper/postsuper.c
postfix/src/sendmail/sendmail.c
postfix/src/showq/showq.c
postfix/src/smtp/smtp_proto.c
postfix/src/smtpd/smtpd.c
postfix/src/smtpd/smtpd_check.c
postfix/src/smtpd/smtpd_sasl_glue.c
postfix/src/util/dict_ldap.c
postfix/src/util/sys_defs.h
postfix/src/virtual/Makefile.in

index 86c5a47327d34d84a71040e0f98cd985aff18f05..47fd8211303b974936c23d89a7d0f0991681c016 100644 (file)
@@ -6723,6 +6723,46 @@ Apologies for any names omitted.
        Bugfix: garbage in verbose "flush" server logging. Victor
        Duchovni.  File: flush/flush.c.
 
+       Incompatibility: smtpd_sasl_local_domain now defaults to
+       the null string. File: smtpd/smtpd.c, smtpd/smtpd_sasl_glue.c.
+
+20020726
+
+       Documentation: added GDB debugging instructions for sites
+       that do not have X installed on the Postfix machine. Henrik
+       Larsson, spambox.dk.
+
+20020729
+
+       Weird: installed RedHat 3.03 inside VMware, and no change
+       was needed to build Postfix, except to recognize the Linux
+       version.
+
+       Bugfix: some mailers will announce ESMTP features in their
+       HELO (not EHLO) response. Postfix did not ignore them.
+       File: smtp/smtp_proto.c.
+
+20020731
+
+       Cleanup: permit_naked_ip_address is unsafe and will go
+       away.  Postfix logs a warning. File: smtpd/smtpd_check.c.
+
+20020801
+
+       Cleanup: the warning message for matched header/body
+       content was misleading. File: cleanup/cleanup_message.c.
+
+       Safety: moved the "postsuper -r ALL" operation after the
+       "postsuper -s" check that makes queue file names match
+       inode numbers. This avoids loss of mail in the unlikely
+       case that someone runs "postsuper -sr ALL" on a queue that
+       was copied from another place.
+
+       Feature: "postsuper -h" to put mail "on hold" and "postsuper
+       -H" to release mail that was placed "on hold". This involves
+       a new queue, which is appropriately named "hold".  Files:
+       postsuper/postsuper.c, showq/showq.c.
+
 Open problems:
 
        Medium: should permit_mx_backup defer delivery if DNS
index 43b2b889ecac9837f96552919639e4a66a6ee601..8d9a43d829a0db1cc01b283042716eb826105bc3 100644 (file)
@@ -97,16 +97,24 @@ that it invokes the debugger of your choice, for example:
          PATH=/usr/bin:/usr/X11R6/bin
          xxgdb $daemon_directory/$process_name $process_id & sleep 5
 
-If you use xxgdb, be sure that gdb is in the command search path.
+If you do not have X on the Postfix machine, then xxgdb isn't going
+to work. Instead, you can try to run gdb in non-interactive mode:
 
-Export XAUTHORITY so that X access control works, for example:
+    debugger_command =
+       PATH=/bin:/usr/bin:/usr/local/bin; export PATH; (echo cont;
+       echo where) | gdb $daemon_directory/$process_name $process_id 2>&1
+       >$config_directory/$process_name.$process_id.log & sleep 5
+
+If you use xxgdb, be sure that gdb is in the command search path,
+and export XAUTHORITY so that X access control works, for example:
 
     % setenv XAUTHORITY ~/.Xauthority
 
 Stop and start the Postfix system. 
 
 Whenever the suspect daemon process is started, a debugger window
-pops up and you can watch in detail what happens.
+pops up and you can watch in detail what happens (when using xxgdb)
+or a file is created (if using gdb in non-interactive mode).
 
 7 - Unreasonable behavior
 =========================
index ef0baea09b2537ae94e019526d2e9b4fe7649842..6b31fbfe705be31cbdcc450ca95b9eeac28edcae 100644 (file)
@@ -12,6 +12,12 @@ snapshot release).  Patches change the patchlevel and the release
 date. Snapshots change only the release date, unless they include
 the same bugfixes as a patch release.
 
+Incompatible changes with Postfix snapshot 1.1.11-200208XX
+==========================================================
+
+In mailq output, the queue ID is followed by the ! character when
+the message is in the "hold" queue.
+
 Incompatible changes with Postfix snapshot 1.1.11-20020717
 ==========================================================
 
index 29416c489092b0ba3c64521ce0d2d89a24b62544..579916cfa0f950b0ebcd8472b608ab73741ebe45 100644 (file)
@@ -509,9 +509,15 @@ debug_peer_level = 2
 # set up your XAUTHORITY environment variable before starting Postfix.
 #
 debugger_command =
-        PATH=/usr/bin:/usr/X11R6/bin
+        PATH=/bin:/usr/bin:/usr/local/bin:/usr/X11R6/bin
         xxgdb $daemon_directory/$process_name $process_id & sleep 5
 
+# If you don't have X installed on the Postfix machine, try:
+# debugger_command =
+#      PATH=/bin:/usr/bin:/usr/local/bin; export PATH; (echo cont;
+#      echo where) | gdb $daemon_directory/$process_name $process_id 2>&1
+#      >$config_directory/$process_name.$process_id.log & sleep 5
+
 # INSTALL-TIME CONFIGURATION INFORMATION
 #
 # The following parameters are used when installing a new Postfix version.
index 4bc0d72fe2b7bee5b971a544b7109d436adca336..3616c652ddbce518402a8cdf942c9890e11e0f27 100644 (file)
@@ -47,9 +47,9 @@ $queue_directory/corrupt:d:$mail_owner:-:700:ucr
 $queue_directory/defer:d:$mail_owner:-:700:ucr
 $queue_directory/deferred:d:$mail_owner:-:700:ucr
 $queue_directory/flush:d:$mail_owner:-:700:ucr
+$queue_directory/hold:d:$mail_owner:-:700:ucr
 $queue_directory/incoming:d:$mail_owner:-:700:ucr
 $queue_directory/private:d:$mail_owner:-:700:uc
-$queue_directory/saved:d:$mail_owner:-:700:ucr
 $queue_directory/maildrop:d:$mail_owner:$setgid_group:730:uc
 $queue_directory/public:d:$mail_owner:$setgid_group:710:uc
 $queue_directory/pid:d:root:-:755:uc
index 87273646e87e64a7d13d2e41afa87669000f4f7b..671b3aa74c9cd0410070d48c479fc963b859d36e 100644 (file)
@@ -444,6 +444,13 @@ relay_domains = $mydestination
 #
 access_map_reject_code = 550
 
+# The defer_code parameter specifies the SMTP server response code
+# when an SMTP client request is rejected by the "defer" restriction.
+#
+# Do not change this unless you have a complete understanding of RFC 821.
+#
+defer_code = 450
+
 # The invalid_hostname_reject_code parameter specifies the SMTP server
 # response when a client violates the reject_invalid_hostname anti-UCE
 # restriction.
index d08a2ffa5309b3a0b6d6fcd6d47a215971e83108..801c4f09c01d78df49bb63dd214bf9b7c66fd092 100644 (file)
@@ -1,5 +1,4 @@
 <html> <head> </head> <body> <pre>
-
 LOCAL(8)                                                 LOCAL(8)
 
 <b>NAME</b>
@@ -440,6 +439,5 @@ LOCAL(8)                                                 LOCAL(8)
        P.O. Box 704
        Yorktown Heights, NY 10598, USA
 
-                                                                1
-
+                                                         LOCAL(8)
 </pre> </body> </html>
index 44642dbb6b1bcda32dde7107458eb5cd004f756a..7b08e7effe966a325621453e39d5187433703f73 100644 (file)
@@ -5,8 +5,8 @@ POSTSUPER(1)                                         POSTSUPER(1)
        postsuper - Postfix superintendent
 
 <b>SYNOPSIS</b>
-       <b>postsuper</b> [<b>-psv</b>] [<b>-d</b> <i>queue_id</i>] [<b>-r</b> <i>queue_id</i>] [<i>directory</i>
-       <i>...</i>]
+       <b>postsuper</b> [<b>-psv</b>] [<b>-d</b> <i>queue_id</i>] [<b>-h</b> <i>queue_id</i>] [<b>-H</b> <i>queue_id</i>]
+       [<b>-r</b> <i>queue_id</i>] [<i>directory</i> <i>...</i>]
 
 <b>DESCRIPTION</b>
        The <b>postsuper</b> command does maintenance jobs on the Postfix
@@ -22,9 +22,15 @@ POSTSUPER(1)                                         POSTSUPER(1)
 
        <b>-d</b> <i>queue_id</i>
               Delete one message with the named queue ID from the
-              named  mail queue(s) (default: <b>incoming</b>, <b>active</b> and
-              <b>deferred</b>).  If a <i>queue_id</i> of <b>-</b>  is  specified,  the
-              program reads queue IDs from standard input.
+              named   mail  queue(s)  (default:  <b>hold</b>,  <b>incoming</b>,
+              <b>active</b> and <b>deferred</b>).  If a <i>queue_id</i> of <b>-</b> is speci-
+              fied,  the  program  reads  queue IDs from standard
+              input. For example, to delete all mail from  or  to
+              <b>user@example.com</b>:
+
+              mailq | awk  'BEGIN { RS = "" } \
+                  / user@example\.com$/ { print $1 } \
+              ' | postsuper -d -
 
               Specify <b>-d</b> <b>ALL</b> to remove all messages; for example,
               specify <b>-d</b> <b>ALL</b>  <b>deferred</b>  to  delete  mail  in  the
@@ -55,16 +61,42 @@ POSTSUPER(1)                                         POSTSUPER(1)
                      of  the  old  message  that  it  should have
                      deleted.
 
+       <b>-h</b> <i>queue_id</i>
+              Put mail "on hold" so that no attempt  is  made  to
+              deliver  it.  Move one message with the named queue
+              ID from the named mail queue(s) (default: <b>incoming</b>,
+              <b>active</b>  and  <b>deferred</b>)  to  the  <b>hold</b>  queue.  If a
+              <i>queue_id</i> of <b>-</b> is specified, the program reads queue
+              IDs from standard input.
+
+              Specify  <b>-h</b>  <b>ALL</b> to hold all messages; for example,
+              specify  <b>-h</b>  <b>ALL</b>  <b>deferred</b>  to  hold  mail  in  the
+              <b>deferred</b>  queue.  As a safety measure, the word <b>ALL</b>
+              must be specified in upper case.
+
+              Note: mail that is put "on hold" will not expire.
+
+       <b>-H</b> <i>queue_id</i>
+              Release mail that was put "on hold".  Move one mes-
+              sage  with  the  named queue ID from the named mail
+              queue(s) (default: <b>hold</b>) to the <b>deferred</b> queue.  If
+              a  <i>queue_id</i>  of  <b>-</b>  is specified, the program reads
+              queue IDs from standard input.
+
+              Specify <b>-H</b> <b>ALL</b> to release  all  mail  that  is  "on
+              hold".   As  a safety measure, the word <b>ALL</b> must be
+              specified in upper case.
+
        <b>-p</b>     Purge old temporary files that are left over  after
               system or software crashes.
 
        <b>-r</b> <i>queue_id</i>
               Requeue  the  message  with the named queue ID from
-              the named mail queue(s) (default: <b>incoming</b>,  <b>active</b>
-              and <b>deferred</b>).  To requeue multiple messages, spec-
-              ify multiple  <b>-r</b>  command-line  options.   Alterna-
-              tively,  if  a <i>queue_id</i> of <b>-</b> is specified, the pro-
-              gram reads queue IDs from standard input.
+              the named mail queue(s) (default:  <b>hold</b>,  <b>incoming</b>,
+              <b>active</b>  and  <b>deferred</b>).   To  requeue multiple mes-
+              sages, specify multiple  <b>-r</b>  command-line  options.
+              Alternatively, if a <i>queue_id</i> of <b>-</b> is specified, the
+              program reads queue IDs from standard input.
 
               Specify <b>-r</b> <b>ALL</b> to requeue all messages. As a safety
               measure,  the  word  <b>ALL</b> must be specified in upper
@@ -113,19 +145,23 @@ POSTSUPER(1)                                         POSTSUPER(1)
        report is written to the standard error stream and to <b>sys-</b>
        <b>logd</b>.
 
+<b>BUGS</b>
+       Mail  that  is  not sanitized by Postfix (i.e. mail in the
+       <b>maildrop</b> queue) cannot be placed "on hold".
+
 <b>CONFIGURATION</b> <b>PARAMETERS</b>
-       See  the  Postfix  <b>main.cf</b> file for syntax details and for
+       See the Postfix <b>main.cf</b> file for syntax  details  and  for
        default values.
 
        <b>hash</b><i>_</i><b>queue</b><i>_</i><b>depth</b>
               Number of subdirectory levels for hashed queues.
 
        <b>hash</b><i>_</i><b>queue</b><i>_</i><b>names</b>
-              The names of queues that are organized into  multi-
+              The  names of queues that are organized into multi-
               ple levels of subdirectories.
 
 <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 8b0033dc653deff286b8dd3391e7954294c22564..b273b0e31a79867ed7d028388dfe63135f409b83 100644 (file)
@@ -34,7 +34,15 @@ SENDMAIL(1)                                           SENDMAIL(1)
               mail could not be delivered upon the last  attempt,
               the reason for failure is shown. This mode of oper-
               ation is implemented by executing the  <a href="postqueue.1.html"><b>postqueue</b>(1)</a>
-              command.
+              command.  The  queue  ID  string  is followed by an
+              optional status character:
+
+              <b>*</b>      The message is in the <b>active</b> queue, i.e. the
+                     message is selected for delivery.
+
+              <b>!</b>      The  message  is  in the <b>hold</b> queue, i.e. no
+                     further delivery attempt will be made  until
+                     the mail is taken off hold.
 
        <b>newaliases</b>
               Initialize the alias database.  If no input file is
@@ -313,6 +321,7 @@ SENDMAIL(1)                                           SENDMAIL(1)
 
 <b>SEE</b> <b>ALSO</b>
        <a href="pickup.8.html">pickup(8)</a> mail pickup daemon
+       <a href="postsuper.1.html">postsuper(1)</a> queue maintenance
        <a href="postalias.1.html">postalias(1)</a> maintain alias database
        <a href="postdrop.1.html">postdrop(1)</a> mail posting utility
        <a href="postfix.1.html">postfix(1)</a> mail system control
index 1676d4dec9e3531f25eacda9ff009e3b670fd522..c7f2a0e69923a1ee3eed298452d88c25755ff7d4 100644 (file)
@@ -290,33 +290,37 @@ SMTPD(8)                                                 SMTPD(8)
               Server response when a client  violates  an  access
               database restriction.
 
+       <b>defer</b><i>_</i><b>code</b>
+              Server  response  when a client request is rejected
+              by the <b>defer</b> restriction.
+
        <b>invalid</b><i>_</i><b>hostname</b><i>_</i><b>reject</b><i>_</i><b>code</b>
-              Server   response   when   a  client  violates  the
+              Server  response  when  a   client   violates   the
               <b>reject</b><i>_</i><b>invalid</b><i>_</i><b>hostname</b> restriction.
 
        <b>maps</b><i>_</i><b>rbl</b><i>_</i><b>reject</b><i>_</i><b>code</b>
-              Server  response  when  a   client   violates   the
+              Server   response   when   a  client  violates  the
               <b>maps</b><i>_</i><b>rbl</b><i>_</i><b>domains</b> restriction.
 
        <b>reject</b><i>_</i><b>code</b>
-              Response  code  when  the  client  matches a <b>reject</b>
+              Response code when  the  client  matches  a  <b>reject</b>
               restriction.
 
        <b>relay</b><i>_</i><b>domains</b><i>_</i><b>reject</b><i>_</i><b>code</b>
-              Server response when a client attempts  to  violate
+              Server  response  when a client attempts to violate
               the mail relay policy.
 
        <b>unknown</b><i>_</i><b>address</b><i>_</i><b>reject</b><i>_</i><b>code</b>
-              Server   response   when   a  client  violates  the
+              Server  response  when  a   client   violates   the
               <b>reject</b><i>_</i><b>unknown</b><i>_</i><b>address</b> restriction.
 
        <b>unknown</b><i>_</i><b>client</b><i>_</i><b>reject</b><i>_</i><b>code</b>
-              Server response when a client  without  address  to
-              name  mapping  violates  the <b>reject</b><i>_</i><b>unknown</b><i>_</i><b>clients</b>
+              Server  response  when  a client without address to
+              name mapping  violates  the  <b>reject</b><i>_</i><b>unknown</b><i>_</i><b>clients</b>
               restriction.
 
        <b>unknown</b><i>_</i><b>hostname</b><i>_</i><b>reject</b><i>_</i><b>code</b>
-              Server  response  when  a   client   violates   the
+              Server   response   when   a  client  violates  the
               <b>reject</b><i>_</i><b>unknown</b><i>_</i><b>hostname</b> restriction.
 
 <b>SEE</b> <b>ALSO</b>
@@ -325,7 +329,7 @@ SMTPD(8)                                                 SMTPD(8)
        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 883df41ed962274b38eace08db6a81fd9172f64d..e04e447811a1dc8a07aab71916218345cd0e08c8 100644 (file)
@@ -342,6 +342,8 @@ rejected requests (default: <b>554</b>).
 
 <dt> <b><a href="#permit">permit</a></b>
 
+<dt> <b><a href="#defer">defer</a></b>
+
 <dt> <b><a href="#reject">reject</a></b>
 
 <dt> <b><a href="#warn_if_reject">warn_if_reject</a></b>
@@ -513,6 +515,8 @@ the response code for <b>REJECT</b> results (default:  <b>554</b>).
 
 <dt> <b><a href="#permit">permit</a></b>
 
+<dt> <b><a href="#defer">defer</a></b>
+
 <dt> <b><a href="#reject">reject</a></b>
 
 <dt> <b><a href="#warn_if_reject">warn_if_reject</a></b>
@@ -692,6 +696,8 @@ client login name doesn't own the MAIL FROM address according to
 
 <dt> <b><a href="#permit">permit</a></b>
 
+<dt> <b><a href="#defer">defer</a></b>
+
 <dt> <b><a href="#reject">reject</a></b>
 
 <dt> <b><a href="#warn_if_reject">warn_if_reject</a></b>
@@ -966,6 +972,8 @@ response code to rejected requests (default:  <b>504</b>).
 
 <dt> <b><a href="#permit">permit</a></b>
 
+<dt> <b><a href="#defer">defer</a></b>
+
 <dt> <b><a href="#reject">reject</a></b>
 
 <dt> <b><a href="#warn_if_reject">warn_if_reject</a></b>
@@ -1066,6 +1074,8 @@ the result code for rejected requests (default: <b>554</b>).
 
 <dt> <b><a href="#permit">permit</a></b>
 
+<dt> <b><a href="#defer">defer</a></b>
+
 <dt> <b><a href="#reject">reject</a></b>
 
 <dt> <b><a href="#warn_if_reject">warn_if_reject</a></b>
@@ -1102,6 +1112,14 @@ policy explicit.
 
 <p>
 
+<a name="defer">
+
+<dt> <b>defer</b> <dd> Defer the request. The client is told to
+try again later. This restriction is useful at the end of a
+restriction list, to make the default policy explicit.
+
+<p>
+
 <a name="reject">
 
 <dt> <b>reject</b> <dd> Reject the request. This restriction
index 19b77ed74391aa34f65b94375d88a11dce7da280..f8f5300fc0bb43553309c2af3f6ab258f9491691 100644 (file)
@@ -170,6 +170,10 @@ case "$SYSTEM.$RELEASE" in
                        ;;
                *)      echo "Unknown AIX version: `uname -v`." 1>&2; exit 1;;
                esac;;
+               # Tested with RedHat 3.03 on 20020729.
+    Linux.1*)  SYSTYPE=LINUX1
+               SYSLIBS="-ldb"
+               ;;
     Linux.2*)  SYSTYPE=LINUX2
                # Postfix no longer needs DB 1.85 compatibility
                if [ -f /usr/include/db.h ]
index 229d42f3264e917f91e2932cef4d8f7cdf05e314..fe4d085a29589bf08d42d7e5d2e1f1b9a94e4b27 100644 (file)
@@ -10,6 +10,7 @@ Postfix superintendent
 .nf
 .fi
 \fBpostsuper\fR [\fB-psv\fR] [\fB-d \fIqueue_id\fR]
+[\fB-h \fIqueue_id\fR] [\fB-H \fIqueue_id\fR]
 [\fB-r \fIqueue_id\fR] [\fIdirectory ...\fR]
 .SH DESCRIPTION
 .ad
@@ -26,10 +27,17 @@ directories - this includes the \fBincoming\fR, \fBactive\fR and
 Options:
 .IP "\fB-d \fIqueue_id\fR"
 Delete one message with the named queue ID from the named
-mail queue(s) (default: \fBincoming\fR, \fBactive\fR and
+mail queue(s) (default: \fBhold\fR, \fBincoming\fR, \fBactive\fR and
 \fBdeferred\fR).
 If a \fIqueue_id\fR of \fB-\fR is specified, the program reads
-queue IDs from standard input.
+queue IDs from standard input. For example, to delete all mail
+from or to \fBuser@example.com\fR:
+.sp
+mailq | awk  \'BEGIN { RS = "" } \e
+.ti +4
+/ user@example\e.com$/ { print $1 } \e
+.br
+\' | postsuper -d -
 .sp
 Specify \fB-d ALL\fR to remove all messages; for example, specify
 \fB-d ALL deferred\fR to delete mail in the \fBdeferred\fR queue.
@@ -59,12 +67,36 @@ can distinguish within a second).
 \fBpostsuper\fR deletes the new message, instead of the old
 message that it should have deleted.
 .RE
+.IP "\fB-h \fIqueue_id\fR"
+Put mail "on hold" so that no attempt is made to deliver it.
+Move one message with the named queue ID from the named
+mail queue(s) (default: \fBincoming\fR, \fBactive\fR and
+\fBdeferred\fR) to the \fBhold\fR queue.
+If a \fIqueue_id\fR of \fB-\fR is specified, the program reads
+queue IDs from standard input.
+.sp
+Specify \fB-h ALL\fR to hold all messages; for example, specify
+\fB-h ALL deferred\fR to hold mail in the \fBdeferred\fR queue.
+As a safety measure, the word \fBALL\fR must be specified in upper
+case.
+.sp
+Note: mail that is put "on hold" will not expire.
+.IP "\fB-H \fIqueue_id\fR"
+Release mail that was put "on hold".
+Move one message with the named queue ID from the named
+mail queue(s) (default: \fBhold\fR) to the \fBdeferred\fR queue.
+If a \fIqueue_id\fR of \fB-\fR is specified, the program reads
+queue IDs from standard input.
+.sp
+Specify \fB-H ALL\fR to release all mail that is "on hold".
+As a safety measure, the word \fBALL\fR must be specified in upper
+case.
 .IP \fB-p\fR
 Purge old temporary files that are left over after system or
 software crashes.
 .IP "\fB-r \fIqueue_id\fR"
 Requeue the message with the named queue ID from the named
-mail queue(s) (default: \fBincoming\fR, \fBactive\fR and
+mail queue(s) (default: \fBhold\fR, \fBincoming\fR, \fBactive\fR and
 \fBdeferred\fR).
 To requeue multiple messages, specify multiple \fB-r\fR
 command-line options.
@@ -113,6 +145,11 @@ Problems are reported to the standard error stream and to
 the number of messages requeued with \fB-r\fR, and the number of
 messages whose queue file name was fixed with \fB-s\fR. The report
 is written to the standard error stream and to \fBsyslogd\fR.
+.SH BUGS
+.ad
+.fi
+Mail that is not sanitized by Postfix (i.e. mail in the \fBmaildrop\fR
+queue) cannot be placed "on hold".
 .SH CONFIGURATION PARAMETERS
 .na
 .nf
index 5ada1f4896f9706e4f110211d58974d53de8f5f0..2aa43d9a84d4dcd8c5d42679f1ede52997d9ff89 100644 (file)
@@ -36,7 +36,16 @@ List the mail queue. Each entry shows the queue file ID, message
 size, arrival time, sender, and the recipients that still need to
 be delivered.  If mail could not be delivered upon the last attempt,
 the reason for failure is shown. This mode of operation is implemented
-by executing the \fBpostqueue\fR(1) command.
+by executing the \fBpostqueue\fR(1) command. The queue ID string
+is followed by an optional status character:
+.RS
+.IP \fB*\fR
+The message is in the \fBactive\fR queue, i.e. the message is
+selected for delivery.
+.IP \fB!\fR
+The message is in the \fBhold\fR queue, i.e. no further delivery
+attempt will be made until the mail is taken off hold.
+.RE
 .IP \fBnewaliases\fR
 Initialize the alias database.  If no input file is specified (with
 the \fB-oA\fR option, see below), the program processes the file(s)
@@ -262,6 +271,7 @@ The characters that Postfix accepts as VERP delimiter characters.
 .na
 .nf
 pickup(8) mail pickup daemon
+postsuper(1) queue maintenance
 postalias(1) maintain alias database
 postdrop(1) mail posting utility
 postfix(1) mail system control
index c4e690633307201bd58c8478e85d4ca7639ba8d0..be3c719c32df9daa7018f37e00a15957f28f9465 100644 (file)
@@ -238,6 +238,9 @@ mail from or to.
 .fi
 .IP \fBaccess_map_reject_code\fR
 Server response when a client violates an access database restriction.
+.IP \fBdefer_code\fR
+Server response when a client request is rejected by the \fBdefer\fR
+restriction.
 .IP \fBinvalid_hostname_reject_code\fR
 Server response when a client violates the \fBreject_invalid_hostname\fR
 restriction.
index 2769ab7b5d8ae8c656a5eb41c191e48fa0a34d3b..8eed9c27cdc87622c9eb515a55ec89006c36db62 100644 (file)
@@ -1,8 +1,10 @@
 SHELL  = /bin/sh
 SRCS   = bounce.c bounce_append_service.c bounce_notify_service.c \
-       bounce_cleanup.c bounce_notify_util.c bounce_notify_verp.c
+       bounce_cleanup.c bounce_notify_util.c bounce_notify_verp.c \
+       bounce_one_service.c
 OBJS   = bounce.o bounce_append_service.o bounce_notify_service.o \
-       bounce_cleanup.o bounce_notify_util.o bounce_notify_verp.o
+       bounce_cleanup.o bounce_notify_util.o bounce_notify_verp.o \
+       bounce_one_service.o
 HDRS   = 
 TESTSRC        = 
 WARN   = -W -Wformat -Wimplicit -Wmissing-prototypes \
@@ -159,3 +161,18 @@ bounce_notify_verp.o: ../../include/mail_error.h
 bounce_notify_verp.o: ../../include/verp_sender.h
 bounce_notify_verp.o: bounce_service.h
 bounce_notify_verp.o: ../../include/bounce_log.h
+bounce_one_service.o: bounce_one_service.c
+bounce_one_service.o: ../../include/sys_defs.h
+bounce_one_service.o: ../../include/msg.h
+bounce_one_service.o: ../../include/vstream.h
+bounce_one_service.o: ../../include/vbuf.h
+bounce_one_service.o: ../../include/name_mask.h
+bounce_one_service.o: ../../include/mail_params.h
+bounce_one_service.o: ../../include/mail_queue.h
+bounce_one_service.o: ../../include/vstring.h
+bounce_one_service.o: ../../include/post_mail.h
+bounce_one_service.o: ../../include/cleanup_user.h
+bounce_one_service.o: ../../include/mail_addr.h
+bounce_one_service.o: ../../include/mail_error.h
+bounce_one_service.o: bounce_service.h
+bounce_one_service.o: ../../include/bounce_log.h
index 00c40cd2d14c5117e092b33d970ff6dca11965a1..f176e222fb922c841ba09535626e60533903caa7 100644 (file)
@@ -283,6 +283,52 @@ static int bounce_verp_proto(char *service_name, VSTREAM *client, int flush)
                                   STR(sender), STR(verp_delims), flush));
 }
 
+/* bounce_one_proto - bounce_one server protocol */
+
+static int bounce_one_proto(char *service_name, VSTREAM *client)
+{
+    int     unused_flags;
+
+    /*
+     * Read and validate the client request.
+     */
+    if (mail_command_server(client,
+                           ATTR_TYPE_NUM, MAIL_ATTR_FLAGS, &unused_flags,
+                           ATTR_TYPE_STR, MAIL_ATTR_QUEUE, queue_name,
+                           ATTR_TYPE_STR, MAIL_ATTR_QUEUEID, queue_id,
+                           ATTR_TYPE_STR, MAIL_ATTR_ENCODING, encoding,
+                           ATTR_TYPE_STR, MAIL_ATTR_SENDER, sender,
+                           ATTR_TYPE_STR, MAIL_ATTR_RECIP, recipient,
+                           ATTR_TYPE_STR, MAIL_ATTR_WHY, why,
+                           ATTR_TYPE_END) != 7) {
+       msg_warn("malformed request");
+       return (-1);
+    }
+    if (strcmp(service_name, MAIL_SERVICE_BOUNCE) != 0) {
+       msg_warn("wrong service name \"%s\" for one-recipient bouncing",
+                service_name);
+       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 (msg_verbose)
+       msg_info("bounce_one_proto: queue=%s id=%s encoding=%s sender=%s recipient=%s why=%s",
+                STR(queue_name), STR(queue_id), STR(encoding),
+                STR(sender), STR(recipient), STR(why));
+
+    /*
+     * Execute the request.
+     */
+    return (bounce_one_service(STR(queue_name), STR(queue_id), STR(encoding),
+                              STR(sender), STR(recipient), STR(why)));
+}
+
 /* bounce_service - parse bounce command type and delegate */
 
 static void bounce_service(VSTREAM *client, char *service_name, char **argv)
@@ -318,6 +364,8 @@ static void bounce_service(VSTREAM *client, char *service_name, char **argv)
        status = bounce_notify_proto(service_name, client, JUST_WARN);
     } else if (command == BOUNCE_CMD_APPEND) {
        status = bounce_append_proto(service_name, client);
+    } else if (command == BOUNCE_CMD_ONE) {
+       status = bounce_one_proto(service_name, client);
     } else {
        msg_warn("unknown command: %d", command);
        status = -1;
index 1c6485630271cabe529a31fff69691cc71a16114..b8970025b02284e81bc4e6d0850b8958bdf105a6 100644 (file)
 /*     const char *encoding;
 /*     int     flush;
 /*
+/*     BOUNCE_INFO *bounce_mail_one_init(queue_name, queue_id,
+/*                                     encoding, orig_recipient, why)
+/*     const char *queue_name;
+/*     const char *queue_id;
+/*     const char *encoding;
+/*     const char *orig_recipient;
+/*     const char *why;
+/*
 /*     void    bounce_mail_free(bounce_info)
 /*     BOUNCE_INFO *bounce_info;
 /*
@@ -66,6 +74,9 @@
 /*     structure contains all the necessary information about an
 /*     undeliverable message.
 /*
+/*     bounce_mail_one_init() provides the same function for only
+/*     one recipient that is not read from bounce logfile.
+/*
 /*     bounce_mail_free() releases memory allocated by bounce_mail_init()
 /*     and closes any files opened by bounce_mail_init().
 /*
 
 #define STR vstring_str
 
-/* bounce_mail_init - initialize */
+/* bounce_mail_alloc - initialize */
 
-BOUNCE_INFO *bounce_mail_init(const char *service, const char *queue_name,
-                                     const char *queue_id,
-                                     const char *encoding, int flush)
+static BOUNCE_INFO *bounce_mail_alloc(const char *service,
+                                                    const char *queue_name,
+                                                    const char *queue_id,
+                                                    const char *encoding,
+                                                    int flush,
+                                                    BOUNCE_LOG *log_handle)
 {
     BOUNCE_INFO *bounce_info;
     int     rec_type;
@@ -194,6 +208,7 @@ BOUNCE_INFO *bounce_mail_init(const char *service, const char *queue_name,
     bounce_info->buf = vstring_alloc(100);
     bounce_info->arrival_time = 0;
     bounce_info->orig_offs = 0;
+    bounce_info->log_handle = log_handle;
 
     /*
      * Compute a supposedly unique boundary string. This assumes that a queue
@@ -204,21 +219,6 @@ BOUNCE_INFO *bounce_mail_init(const char *service, const char *queue_name,
                    queue_id, (unsigned long) event_time(), var_myhostname);
     bounce_info->mime_boundary = mystrdup(STR(bounce_info->buf));
 
-    /*
-     * If the bounce log cannot be found, do not raise a fatal run-time
-     * error. There is nothing we can do about the error, and all we are
-     * doing is to inform the sender of a delivery problem, Bouncing a
-     * message does not have to be a perfect job. But if the system IS
-     * running out of resources, raise a fatal run-time error and force a
-     * backoff.
-     */
-    if ((bounce_info->log_handle = bounce_log_open(bounce_info->service,
-                                                  bounce_info->queue_id,
-                                                  O_RDWR, 0)) == 0
-       && errno != ENOENT)
-       msg_fatal("open %s %s: %m", bounce_info->service,
-                 bounce_info->queue_id);
-
     /*
      * If the original message cannot be found, do not raise a run-time
      * error. There is nothing we can do about the error, and all we are
@@ -252,6 +252,56 @@ BOUNCE_INFO *bounce_mail_init(const char *service, const char *queue_name,
     return (bounce_info);
 }
 
+/* bounce_mail_init - initialize */
+
+BOUNCE_INFO *bounce_mail_init(const char *service,
+                                     const char *queue_name,
+                                     const char *queue_id,
+                                     const char *encoding,
+                                     int flush)
+{
+    BOUNCE_INFO *bounce_info;
+    BOUNCE_LOG *log_handle;
+
+    /*
+     * Initialize the bounce_info structure. If the bounce log cannot be
+     * found, do not raise a fatal run-time error. There is nothing we can do
+     * about the error, and all we are doing is to inform the sender of a
+     * delivery problem, Bouncing a message does not have to be a perfect
+     * job. But if the system IS running out of resources, raise a fatal
+     * run-time error and force a backoff.
+     */
+    if ((log_handle = bounce_log_open(service, queue_id, O_RDWR, 0)) == 0
+       && errno != ENOENT)
+       msg_fatal("open %s %s: %m", service, queue_id);
+    bounce_info = bounce_mail_alloc(service, queue_name, queue_id,
+                                   encoding, flush, log_handle);
+    return (bounce_info);
+}
+
+/* bounce_mail_one_init - initialize */
+
+BOUNCE_INFO *bounce_mail_one_init(const char *queue_name,
+                                         const char *queue_id,
+                                         const char *encoding,
+                                         const char *orig_recipient,
+                                         const char *why)
+{
+    BOUNCE_INFO *bounce_info;
+    BOUNCE_LOG *log_handle;
+
+    /*
+     * Initialize the bounce_info structure. Forge a logfile record for just
+     * one recipient.
+     */
+#define REALLY_BOUNCE  1
+
+    log_handle = bounce_log_forge(orig_recipient, "5.0.0", why);
+    bounce_info = bounce_mail_alloc("none", queue_name, queue_id,
+                                   encoding, REALLY_BOUNCE, log_handle);
+    return (bounce_info);
+}
+
 /* bounce_mail_free - undo bounce_mail_init */
 
 void    bounce_mail_free(BOUNCE_INFO *bounce_info)
diff --git a/postfix/src/bounce/bounce_one_service.c b/postfix/src/bounce/bounce_one_service.c
new file mode 100644 (file)
index 0000000..a654b83
--- /dev/null
@@ -0,0 +1,221 @@
+/*++
+/* NAME
+/*     bounce_one_service 3
+/* SUMMARY
+/*     send non-delivery report to sender, server side
+/* SYNOPSIS
+/*     #include "bounce_service.h"
+/*
+/*     int     bounce_one_service(queue_name, queue_id, encoding,
+/*                                     orig_sender, orig_recipient, why)
+/*     char    *queue_name;
+/*     char    *queue_id;
+/*     char    *encoding;
+/*     char    *orig_sender;
+/*     char    *orig_recipient;
+/*     char    *why;
+/* DESCRIPTION
+/*     This module implements the server side of the bounce_one()
+/*     (send bounce message for one recipient) request.
+/*
+/*     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.  When a bounce bounces, an optional double bounce
+/*     with the entire undeliverable mail is sent to the postmaster,
+/*     with as sender address the double bounce address.
+/* DIAGNOSTICS
+/*     Fatal error: error opening existing file. Warnings: corrupt
+/*     message file. A corrupt message is saved to the "corrupt"
+/*     queue for further inspection.
+/* BUGS
+/* 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 <post_mail.h>
+#include <mail_addr.h>
+#include <mail_error.h>
+
+/* Application-specific. */
+
+#include "bounce_service.h"
+
+#define STR vstring_str
+
+/* bounce_one_service - send a bounce for one recipient */
+
+int     bounce_one_service(char *queue_name, char *queue_id, char *encoding,
+                                  char *orig_sender, char *orig_recipient,
+                                  char *why)
+{
+    BOUNCE_INFO *bounce_info;
+    int     bounce_status = 1;
+    int     postmaster_status = 1;
+    VSTREAM *bounce;
+    int     notify_mask = name_mask(VAR_NOTIFY_CLASSES, mail_error_masks,
+                                   var_notify_classes);
+
+    /*
+     * Initialize. Open queue file, bounce log, etc.
+     */
+    bounce_info = bounce_mail_one_init(queue_name, queue_id,
+                                      encoding, orig_recipient, why);
+
+#define NULL_SENDER            MAIL_ADDR_EMPTY /* special address */
+#define NULL_CLEANUP_FLAGS     0
+#define BOUNCE_HEADERS         1
+#define BOUNCE_ALL             0
+
+    /*
+     * The choice of bounce sender address depends on the original sender
+     * address. For a single bounce (a non-delivery notification to the
+     * message originator), the sender address is the empty string. For a
+     * double bounce (typically a failed single bounce, or a postmaster
+     * notification that was produced by any of the mail processes) the
+     * sender address is defined by the var_double_bounce_sender
+     * configuration variable. When a double bounce cannot be delivered, the
+     * queue manager blackholes the resulting triple bounce message.
+     */
+
+    /*
+     * Double bounce failed. Never send a triple bounce.
+     * 
+     * However, this does not prevent double bounces from bouncing on other
+     * systems. In order to cope with this, either the queue manager must
+     * recognize the double-bounce original sender address and discard mail,
+     * or every delivery agent must recognize the double-bounce sender
+     * address and substitute something else so mail does not come back at
+     * us.
+     */
+    if (strcasecmp(orig_sender, mail_addr_double_bounce()) == 0) {
+       msg_warn("%s: undeliverable postmaster notification discarded",
+                queue_id);
+       bounce_status = 0;
+    }
+
+    /*
+     * Single bounce failed. Optionally send a double bounce to postmaster.
+     */
+#define ANY_BOUNCE (MAIL_ERROR_2BOUNCE | MAIL_ERROR_BOUNCE)
+#define SKIP_IF_BOUNCE ((notify_mask & ANY_BOUNCE) == 0)
+
+    else if (*orig_sender == 0) {
+       if (SKIP_IF_BOUNCE) {
+           bounce_status = 0;
+       } else {
+           if ((bounce = post_mail_fopen_nowait(mail_addr_double_bounce(),
+                                                var_2bounce_rcpt,
+                                                NULL_CLEANUP_FLAGS)) != 0) {
+
+               /*
+                * Double bounce to Postmaster. This is the last opportunity
+                * for this message to be delivered. Send the text with
+                * reason for the bounce, and the headers of the original
+                * message. Don't bother sending the boiler-plate text.
+                */
+               if (!bounce_header(bounce, bounce_info, var_2bounce_rcpt)
+                   && 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_ALL);
+               bounce_status = post_mail_fclose(bounce);
+           }
+       }
+    }
+
+    /*
+     * Non-bounce failed. Send a single bounce.
+     */
+    else {
+       if ((bounce = post_mail_fopen_nowait(NULL_SENDER, orig_sender,
+                                            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, orig_sender) == 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, BOUNCE_ALL);
+           bounce_status = post_mail_fclose(bounce);
+       }
+
+       /*
+        * 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 ((notify_mask & MAIL_ERROR_BOUNCE))
+
+       if (bounce_status == 0 && (WANT_IF_BOUNCE)
+           && strcasecmp(orig_sender, mail_addr_double_bounce()) != 0) {
+
+           /*
+            * 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.
+            */
+           if ((bounce = post_mail_fopen_nowait(mail_addr_double_bounce(),
+                                                var_bounce_rcpt,
+                                                NULL_CLEANUP_FLAGS)) != 0) {
+               if (bounce_header(bounce, bounce_info, var_bounce_rcpt) == 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);
+           }
+           if (postmaster_status)
+               msg_warn("postmaster notice failed while bouncing to %s",
+                        orig_sender);
+       }
+    }
+
+    /*
+     * Cleanup.
+     */
+    bounce_mail_free(bounce_info);
+
+    return (bounce_status);
+}
index 889e8e9d635a0c9982f9a6408fe2ab85a0494cf4..22636e8c7bc8fa5dd9790fa3a3c86157ff6d8e96 100644 (file)
@@ -33,6 +33,11 @@ extern int bounce_notify_service(char *, char *, char *, char *, char *, int);
   */
 extern int bounce_notify_verp(char *, char *, char *, char *, char *, char *, int);
 
+ /*
+  * bounce_one_service.c
+  */
+extern int bounce_one_service(char *, char *, char *, char *, char *, char *);
+
  /*
   * bounce_cleanup.c
   */
@@ -61,6 +66,7 @@ typedef struct {
 } BOUNCE_INFO;
 
 extern BOUNCE_INFO *bounce_mail_init(const char *, const char *, const char *, const char *, int);
+extern BOUNCE_INFO *bounce_mail_one_init(const char *, const char *, const char *, const char *, const char *);
 extern void bounce_mail_free(BOUNCE_INFO *);
 extern int bounce_header(VSTREAM *, BOUNCE_INFO *, const char *);
 extern int bounce_boilerplate(VSTREAM *, BOUNCE_INFO *);
index 752e8479e1b1df899444ef46d7235fa5fa793b34..e93eb91c9f8c82f3eab095ac8300075a20f40988 100644 (file)
@@ -293,8 +293,7 @@ static int cleanup_act(CLEANUP_STATE *state, char *context, const char *buf,
        msg_info("%s: warning: %s %.200s; from=<%s> to=<%s>: %s",
                 state->queue_id, context, buf, state->sender,
                 state->recip ? state->recip : "unknown",
-                *optional_text ? optional_text :
-                cleanup_strerror(CLEANUP_STAT_CONT));
+                *optional_text ? optional_text : "content matched");
        return (CLEANUP_ACT_KEEP);
     }
     if (STREQUAL(value, "FILTER", command_len)) {
index 05648cbc30347de918ac50ae0423269169622fa6..14b74f84b96ae4dac4b079868878bf953daa0795 100644 (file)
 /*     const char *id;
 /*     const char *encoding;
 /*     const char *sender;
+/*
+/*     int     bounce_one(flags, queue, id, encoding, sender,
+/*                             recipient, relay, entry, format, ...)
+/*     int     flags;
+/*     const char *queue;
+/*     const char *id;
+/*     const char *encoding;
+/*     const char *sender;
+/*     const char *recipient;
+/*     const char *relay;
+/*     time_t  entry;
+/*     const char *format;
+/*
+/*     int     vbounce_one(flags, queue, id, encoding, sender,
+/*                             recipient, relay, entry, format, ap)
+/*     int     flags;
+/*     const char *queue;
+/*     const char *id;
+/*     const char *encoding;
+/*     const char *sender;
+/*     const char *recipient;
+/*     const char *relay;
+/*     time_t  entry;
+/*     const char *format;
+/*     va_list ap;
 /* DESCRIPTION
 /*     This module implements the client interface to the message
 /*     bounce service, which maintains a per-message log of status
 /*     the specified sender, including the bounce log that was
 /*     built with bounce_append().
 /*
+/*     bounce_one() bounces one recipient and immediately sends a 
+/*     notification to the sender. This procedure does not append 
+/*     the recipient and reason to the per-message bounce log, and 
+/*     should be used when a delivery agent changes the error 
+/*     return address in a manner that depends on the recipient
+/*     address.
+/*
+/*     vbounce_one() implements an alternative interface.
+/*
 /*     Arguments:
 /* .IP flags
 /*     The bitwise OR of zero or more of the following (specify
@@ -159,7 +193,7 @@ int     vbounce_append(int flags, const char *id, const char *recipient,
                 vstring_str(why));
        status = (var_soft_bounce ? -1 : 0);
     } else if ((flags & BOUNCE_FLAG_CLEAN) == 0) {
-       status = defer_append(flags, id, recipient, "bounce", delay,
+       status = defer_append(flags, id, recipient, "bounce", entry,
                              "bounce failed");
     } else {
        status = -1;
@@ -196,3 +230,64 @@ int     bounce_flush(int flags, const char *queue, const char *id,
        return (-1);
     }
 }
+
+/* bounce_one - send notice for one recipient */
+
+int     bounce_one(int flags, const char *queue, const char *id,
+                          const char *encoding, const char *sender,
+                          const char *recipient, const char *relay,
+                          time_t entry, const char *fmt,...)
+{
+    va_list ap;
+    int     status;
+
+    va_start(ap, fmt);
+    status = vbounce_one(flags, queue, id, encoding, sender,
+                        recipient, relay, entry, fmt, ap);
+    va_end(ap);
+    return (status);
+}
+
+/* vbounce_one - send notice for one recipient */
+
+int     vbounce_one(int flags, const char *queue, const char *id,
+                           const char *encoding, const char *sender,
+                           const char *recipient, const char *relay,
+                           time_t entry, const char *fmt, va_list ap)
+{
+    VSTRING *why;
+    int     status;
+    int     delay;
+
+    /*
+     * When we're not bouncing, then use the standard logfile based
+     * procedure.
+     */
+    if (var_soft_bounce)
+       return (vbounce_append(flags, id, recipient, relay, entry, fmt, ap));
+
+    why = vstring_alloc(100);
+    delay = time((time_t *) 0) - entry;
+    vstring_vsprintf(why, fmt, ap);
+    if (mail_command_client(MAIL_CLASS_PRIVATE, var_bounce_service,
+                           ATTR_TYPE_NUM, MAIL_ATTR_NREQ, BOUNCE_CMD_ONE,
+                           ATTR_TYPE_NUM, MAIL_ATTR_FLAGS, flags,
+                           ATTR_TYPE_STR, MAIL_ATTR_QUEUE, queue,
+                           ATTR_TYPE_STR, MAIL_ATTR_QUEUEID, id,
+                           ATTR_TYPE_STR, MAIL_ATTR_ENCODING, encoding,
+                           ATTR_TYPE_STR, MAIL_ATTR_SENDER, sender,
+                           ATTR_TYPE_STR, MAIL_ATTR_RECIP, recipient,
+                           ATTR_TYPE_STR, MAIL_ATTR_WHY, vstring_str(why),
+                           ATTR_TYPE_END) == 0) {
+       msg_info("%s: to=<%s>, relay=%s, delay=%d, status=bounced (%s)",
+                id, recipient, relay, delay, vstring_str(why));
+       status = 0;
+    } else if ((flags & BOUNCE_FLAG_CLEAN) == 0) {
+       status = defer_append(flags, id, recipient, "bounce", entry,
+                             "bounce failed");
+    } else {
+       status = -1;
+    }
+    vstring_free(why);
+    return (status);
+}
index 11b91eaf5b7d8b0beae004a1d14206dc10ed2469..301ba72d16d70e3f35a8099d7dff971f96222747 100644 (file)
@@ -26,14 +26,13 @@ extern int PRINTFLIKE(6, 7) bounce_append(int, const char *, const char *,
 extern int vbounce_append(int, const char *, const char *, const char *,
                                  time_t, const char *, va_list);
 extern int bounce_flush(int, const char *, const char *, const char *, const char *);
-
-extern int PRINTFLIKE(8, 9) bounce_recip(int, const char *, const char *,
-                                                const char *, const char *,
-                                                const char *, time_t,
-                                                const char *,...);
-extern int vbounce_recip(int, const char *, const char *, const char *,
-                                const char *, const char *, time_t,
-                                const char *, va_list);
+extern int PRINTFLIKE(9, 10) bounce_one(int, const char *, const char *,
+                                               const char *, const char *,
+                                               const char *, const char *,
+                                               time_t, const char *,...);
+extern int vbounce_one(int, const char *, const char *, const char *,
+                              const char *, const char *, const char *,
+                              time_t, const char *, va_list);
 
  /*
   * Bounce/defer protocol commands.
@@ -42,6 +41,7 @@ extern int vbounce_recip(int, const char *, const char *, const char *,
 #define BOUNCE_CMD_FLUSH       1       /* send log */
 #define BOUNCE_CMD_WARN                2       /* send warning, don't delete log */
 #define BOUNCE_CMD_VERP                3       /* send log, verp style */
+#define BOUNCE_CMD_ONE         4       /* send one recipient notice */
 
  /*
   * Flags.
index f0f841532077792ebaa7ad501edbf07507cbd007..a063ede95ab78bf9532db85312c4b23ec0c8ba28 100644 (file)
 /*     void    bounce_log_rewind(bp)
 /*     BOUNCE_LOG *bp;
 /*
+/*     BOUNCE_LOG *bounce_log_forge(recipient, dsn, why)
+/*     const char *recipient;
+/*     const char *status;
+/*     const char *why;
+/*
 /*     void    bounce_log_close(bp)
 /*     BOUNCE_LOG *bp;
 /* DESCRIPTION
 /*     are marked as done). The result is 0 in case of success, -1 in case
 /*     of problems.
 /*
+/*     bounce_log_forge() forges one recipient status record
+/*     without actually accessing a logfile. 
+/*     The result cannot be used for any logfile access operation
+/*     and must be disposed of by passing it to bounce_log_close().
+/*
 /*     bounce_log_close() closes an open bounce or defer logfile and
 /*     releases memory for the specified handle. The result is non-zero
 /*     in case of I/O errors.
 
 #define STR(x) vstring_str(x)
 
+/* bounce_log_init - initialize structure */
+
+static BOUNCE_LOG *bounce_log_init(VSTREAM *fp,
+                                          VSTRING *buf,
+                                          const char *recipient,
+                                          const char *status,
+                                          const char *text,
+                                          long offset)
+{
+    BOUNCE_LOG *bp;
+
+    bp = (BOUNCE_LOG *) mymalloc(sizeof(*bp));
+    bp->fp = fp;
+    bp->buf = buf;
+    bp->recipient = recipient;
+    bp->status = status;
+    bp->text = text;
+    bp->offset = offset;
+    return (bp);
+}
+
 /* bounce_log_open - open bounce read stream */
 
 BOUNCE_LOG *bounce_log_open(const char *queue_name, const char *queue_id,
                                    int flags, int mode)
 {
-    BOUNCE_LOG *bp;
     VSTREAM *fp;
 
 #define STREQ(x,y)     (strcmp((x),(y)) == 0)
@@ -135,12 +165,13 @@ BOUNCE_LOG *bounce_log_open(const char *queue_name, const char *queue_id,
     if ((fp = mail_queue_open(queue_name, queue_id, flags, mode)) == 0) {
        return (0);
     } else {
-       bp = (BOUNCE_LOG *) mymalloc(sizeof(*bp));
-       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);
+       return (bounce_log_init(fp,             /* stream */
+                               vstring_alloc(100),     /* buffer */
+                               (const char *) 0,       /* recipient */
+                               STREQ(queue_name, MAIL_QUEUE_DEFER) ?
+                               "4.0.0" : "5.0.0",      /* status */
+                               (const char *) 0,       /* text */
+                               0));            /* offset */
     }
 }
 
@@ -214,14 +245,27 @@ BOUNCE_LOG *bounce_log_delrcpt(BOUNCE_LOG *bp)
     return (bp);
 }
 
+/* bounce_log_forge - forge one recipient status record */
+
+BOUNCE_LOG *bounce_log_forge(const char *recipient, const char *status,
+                                    const char *text)
+{
+    return (bounce_log_init((VSTREAM *) 0, (VSTRING *) 0,
+                           recipient, status, text, 0));
+}
+
 /* bounce_log_close - close bounce reader stream */
 
 int     bounce_log_close(BOUNCE_LOG *bp)
 {
     int     ret;
 
-    ret = vstream_fclose(bp->fp);
-    vstring_free(bp->buf);
+    if (bp->fp)
+       ret = vstream_fclose(bp->fp);
+    else
+       ret = 0;
+    if (bp->buf)
+       vstring_free(bp->buf);
     myfree((char *) bp);
     return (ret);
 }
index 0f91c6f8d68de142c191a161cdcd5ae5f0cba2c8..a478b7486ba96caf13976e270b4d847575cfec69 100644 (file)
@@ -34,6 +34,7 @@ typedef struct {
 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 BOUNCE_LOG *bounce_log_forge(const char *, const char *, const char *);
 extern int bounce_log_close(BOUNCE_LOG *);
 
 #define bounce_log_rewind(bp) vstream_fseek((bp)->fp, 0L, SEEK_SET)
index 3c23470614e4c6b0675aabc02818663f449274d0..7c6cce5591a3407cef076a8a65cb3a213397a825 100644 (file)
@@ -1131,6 +1131,11 @@ extern bool var_allow_untrust_route;
 #define DEF_REJECT_CODE                554
 extern int var_reject_code;
 
+#define DEFER_ALL              "defer"
+#define VAR_DEFER_CODE         "defer_code"
+#define DEF_DEFER_CODE         450
+extern int var_defer_code;
+
 #define REJECT_UNKNOWN_CLIENT  "reject_unknown_client"
 #define VAR_UNK_CLIENT_CODE    "unknown_client_reject_code"
 #define DEF_UNK_CLIENT_CODE    450
index 9486eb8ffefb5a497574e4f6f03a034b6655c44a..f18fb4516483e81f2126e00edbe6c361a58e2098 100644 (file)
@@ -21,6 +21,7 @@
   * Mail queue names.
   */
 #define MAIL_QUEUE_MAILDROP    "maildrop"
+#define MAIL_QUEUE_HOLD                "hold"
 #define MAIL_QUEUE_INCOMING    "incoming"
 #define MAIL_QUEUE_ACTIVE      "active"
 #define MAIL_QUEUE_DEFERRED    "deferred"
index 554c1a950785c8ba7e03b374ad205cf074267df6..36fa490b7ef9eeb802d327828add4bfa4c3e4567 100644 (file)
@@ -20,7 +20,7 @@
   * Patches change the patchlevel and the release date. Snapshots change the
   * release date only, unless they include the same bugfix as a patch release.
   */
-#define MAIL_RELEASE_DATE      "20020719"
+#define MAIL_RELEASE_DATE      "20020803"
 
 #define VAR_MAIL_VERSION       "mail_version"
 #define DEF_MAIL_VERSION       "1.1.11-" MAIL_RELEASE_DATE
index b07e17fb8c07dd3f5526bc5a41e0b7a986a77d41..e38a38a35777844426c5195ea0963a8a327fb395 100644 (file)
@@ -113,6 +113,8 @@ command.o: ../../include/resolve_clnt.h
 command.o: ../../include/deliver_request.h
 command.o: ../../include/recipient_list.h
 command.o: ../../include/mbox_conf.h
+command.o: ../../include/maps.h
+command.o: ../../include/dict.h
 deliver_attr.o: deliver_attr.c
 deliver_attr.o: ../../include/sys_defs.h
 deliver_attr.o: ../../include/msg.h
@@ -128,6 +130,8 @@ deliver_attr.o: ../../include/deliver_request.h
 deliver_attr.o: ../../include/recipient_list.h
 deliver_attr.o: ../../include/mbox_conf.h
 deliver_attr.o: ../../include/argv.h
+deliver_attr.o: ../../include/maps.h
+deliver_attr.o: ../../include/dict.h
 delivered.o: delivered.c
 delivered.o: ../../include/sys_defs.h
 delivered.o: ../../include/msg.h
@@ -151,6 +155,8 @@ delivered.o: ../../include/deliver_request.h
 delivered.o: ../../include/recipient_list.h
 delivered.o: ../../include/mbox_conf.h
 delivered.o: ../../include/argv.h
+delivered.o: ../../include/maps.h
+delivered.o: ../../include/dict.h
 dotforward.o: dotforward.c
 dotforward.o: ../../include/sys_defs.h
 dotforward.o: ../../include/msg.h
@@ -178,6 +184,8 @@ dotforward.o: ../../include/deliver_request.h
 dotforward.o: ../../include/recipient_list.h
 dotforward.o: ../../include/mbox_conf.h
 dotforward.o: ../../include/argv.h
+dotforward.o: ../../include/maps.h
+dotforward.o: ../../include/dict.h
 file.o: file.c
 file.o: ../../include/sys_defs.h
 file.o: ../../include/msg.h
@@ -203,6 +211,8 @@ file.o: ../../include/tok822.h
 file.o: ../../include/resolve_clnt.h
 file.o: ../../include/deliver_request.h
 file.o: ../../include/recipient_list.h
+file.o: ../../include/maps.h
+file.o: ../../include/dict.h
 forward.o: forward.c
 forward.o: ../../include/sys_defs.h
 forward.o: ../../include/msg.h
@@ -231,6 +241,8 @@ forward.o: ../../include/resolve_clnt.h
 forward.o: ../../include/deliver_request.h
 forward.o: ../../include/recipient_list.h
 forward.o: ../../include/mbox_conf.h
+forward.o: ../../include/maps.h
+forward.o: ../../include/dict.h
 include.o: include.c
 include.o: ../../include/sys_defs.h
 include.o: ../../include/msg.h
@@ -255,6 +267,8 @@ include.o: ../../include/deliver_request.h
 include.o: ../../include/recipient_list.h
 include.o: ../../include/mbox_conf.h
 include.o: ../../include/argv.h
+include.o: ../../include/maps.h
+include.o: ../../include/dict.h
 indirect.o: indirect.c
 indirect.o: ../../include/sys_defs.h
 indirect.o: ../../include/msg.h
@@ -273,6 +287,8 @@ indirect.o: ../../include/deliver_request.h
 indirect.o: ../../include/recipient_list.h
 indirect.o: ../../include/mbox_conf.h
 indirect.o: ../../include/argv.h
+indirect.o: ../../include/maps.h
+indirect.o: ../../include/dict.h
 local.o: local.c
 local.o: ../../include/sys_defs.h
 local.o: ../../include/msg.h
@@ -294,6 +310,7 @@ local.o: ../../include/mail_addr.h
 local.o: ../../include/mail_conf.h
 local.o: ../../include/been_here.h
 local.o: ../../include/ext_prop.h
+local.o: ../../include/maps.h
 local.o: ../../include/mail_server.h
 local.o: local.h
 local.o: ../../include/tok822.h
@@ -316,6 +333,8 @@ local_expand.o: ../../include/deliver_request.h
 local_expand.o: ../../include/recipient_list.h
 local_expand.o: ../../include/mbox_conf.h
 local_expand.o: ../../include/argv.h
+local_expand.o: ../../include/maps.h
+local_expand.o: ../../include/dict.h
 mailbox.o: mailbox.c
 mailbox.o: ../../include/sys_defs.h
 mailbox.o: ../../include/msg.h
@@ -375,6 +394,8 @@ maildir.o: ../../include/deliver_request.h
 maildir.o: ../../include/recipient_list.h
 maildir.o: ../../include/mbox_conf.h
 maildir.o: ../../include/argv.h
+maildir.o: ../../include/maps.h
+maildir.o: ../../include/dict.h
 recipient.o: recipient.c
 recipient.o: ../../include/sys_defs.h
 recipient.o: ../../include/msg.h
@@ -401,6 +422,7 @@ recipient.o: ../../include/resolve_clnt.h
 recipient.o: ../../include/deliver_request.h
 recipient.o: ../../include/recipient_list.h
 recipient.o: ../../include/mbox_conf.h
+recipient.o: ../../include/maps.h
 resolve.o: resolve.c
 resolve.o: ../../include/sys_defs.h
 resolve.o: ../../include/msg.h
@@ -421,6 +443,8 @@ resolve.o: ../../include/deliver_request.h
 resolve.o: ../../include/recipient_list.h
 resolve.o: ../../include/mbox_conf.h
 resolve.o: ../../include/argv.h
+resolve.o: ../../include/maps.h
+resolve.o: ../../include/dict.h
 token.o: token.c
 token.o: ../../include/sys_defs.h
 token.o: ../../include/msg.h
@@ -442,6 +466,8 @@ token.o: ../../include/deliver_request.h
 token.o: ../../include/recipient_list.h
 token.o: ../../include/mbox_conf.h
 token.o: ../../include/argv.h
+token.o: ../../include/maps.h
+token.o: ../../include/dict.h
 unknown.o: unknown.c
 unknown.o: ../../include/sys_defs.h
 unknown.o: ../../include/msg.h
@@ -464,3 +490,5 @@ unknown.o: ../../include/deliver_request.h
 unknown.o: ../../include/recipient_list.h
 unknown.o: ../../include/mbox_conf.h
 unknown.o: ../../include/argv.h
+unknown.o: ../../include/maps.h
+unknown.o: ../../include/dict.h
index af0fcf355a5e98111d2e015e9b3d64dc7350ccfd..717f9f57b4233e47e18642fe15055f7adfa4fbc8 100644 (file)
@@ -129,7 +129,6 @@ int     deliver_alias(LOCAL_STATE state, USER_ATTR usr_attr,
     const char *alias_result;
     char   *expansion;
     char   *owner;
-    static MAPS *maps;
     char  **cpp;
     uid_t   alias_uid;
     struct mypasswd *alias_pwd;
@@ -145,12 +144,6 @@ int     deliver_alias(LOCAL_STATE state, USER_ATTR usr_attr,
     if (msg_verbose)
        MSG_LOG_STATE(myname, state);
 
-    /*
-     * Do this only once.
-     */
-    if (maps == 0)
-       maps = maps_create("aliases", var_alias_maps, DICT_FLAG_LOCK);
-
     /*
      * DUPLICATE/LOOP ELIMINATION
      * 
@@ -195,7 +188,7 @@ int     deliver_alias(LOCAL_STATE state, USER_ATTR usr_attr,
      * 
      * Don't match aliases that are based on regexps.
      */
-    for (cpp = maps->argv->argv; *cpp; cpp++) {
+    for (cpp = alias_maps->argv->argv; *cpp; cpp++) {
        if ((dict = dict_handle(*cpp)) == 0)
            msg_panic("%s: dictionary not found: %s", myname, *cpp);
        if ((dict->flags & DICT_FLAG_FIXED) == 0) {
@@ -252,7 +245,7 @@ int     deliver_alias(LOCAL_STATE state, USER_ATTR usr_attr,
 
            expansion = mystrdup(alias_result);
            if (OWNER_ASSIGN(owner) != 0
-           && (owner_rhs = maps_find(maps, owner, DICT_FLAG_FIXED)) != 0) {
+           && (owner_rhs = maps_find(alias_maps, owner, DICT_FLAG_FIXED)) != 0) {
                canon_owner = canon_addr_internal(vstring_alloc(10),
                                     var_exp_own_alias ? owner_rhs : owner);
                SET_OWNER_ATTR(state.msg_attr, STR(canon_owner), state.level);
index dc13ca166777bb564b224bfb2ffadb77f74b719a..fb00a3b75e3024b4c76cc89eeec775545ab08ac9 100644 (file)
 #include <been_here.h>
 #include <mail_params.h>
 #include <ext_prop.h>
+#include <maps.h>
 
 /* Single server skeleton. */
 
@@ -478,6 +479,7 @@ int     local_file_deliver_mask;
 int     local_ext_prop_mask;
 int     local_deliver_hdr_mask;
 int     local_mbox_lock_mask;
+MAPS   *alias_maps;
 
 /* local_deliver - deliver message with extreme prejudice */
 
@@ -661,6 +663,7 @@ static void pre_init(char *unused_name, char **unused_argv)
                      VAR_MAILBOX_LIMIT, VAR_MESSAGE_LIMIT);
        set_file_limit(var_mailbox_limit);
     }
+    alias_maps = maps_create("aliases", var_alias_maps, DICT_FLAG_LOCK);
 }
 
 /* main - pass control to the single-threaded skeleton */
index 302ea54197333c2c85c65a08fac881fdf2ef5f58..75fa5b0b340f64a96668d4adf2499f4d4fe695ba 100644 (file)
@@ -22,6 +22,7 @@
 #include <tok822.h>
 #include <deliver_request.h>
 #include <mbox_conf.h>
+#include <maps.h>
 
  /*
   * User attributes: these control the privileges for delivery to external
@@ -210,6 +211,12 @@ int     local_expand(VSTRING *, const char *, LOCAL_STATE *, USER_ATTR *, const
 
 #define LOCAL_EXP_EXTENSION_MATCHED    (1<<MAC_PARSE_USER)
 
+ /*
+  * alias.c
+  */
+extern MAPS *alias_maps;
+#define ALIAS_DICT_FLAGS       DICT_FLAG_FIXED
+
 /* LICENSE
 /* .ad
 /* .fi
index 737382295ad437a853a89d8984b8aec0147bcf2b..227910d6d1b4ca7bb4a7446f85e6a3a4fd629635 100644 (file)
 #include <split_addr.h>
 #include <ext_prop.h>
 #include <mypwd.h>
+#include <canon_addr.h>
 
 /* Application-specific. */
 
 #include "local.h"
 
+#define STR(x) vstring_str(x)
+
 /* deliver_switch - branch on recipient type */
 
 static int deliver_switch(LOCAL_STATE state, USER_ATTR usr_attr)
@@ -211,14 +214,6 @@ int     deliver_recipient(LOCAL_STATE state, USER_ATTR usr_attr)
                  state.level, state.msg_attr.recipient))
        return (0);
 
-    /*
-     * With each level of recursion, detect and break external message
-     * forwarding loops.
-     */
-    if (delivered_find(state.loop_info, state.msg_attr.recipient))
-       return (bounce_append(BOUNCE_FLAG_KEEP, BOUNCE_ATTR(state.msg_attr),
-                 "mail forwarding loop for %s", state.msg_attr.recipient));
-
     /*
      * Set up the recipient-specific attributes. If this is forwarded mail,
      * leave the delivered attribute alone, so that the forwarded message
@@ -254,6 +249,42 @@ int     deliver_recipient(LOCAL_STATE state, USER_ATTR usr_attr)
        return (bounce_append(BOUNCE_FLAG_KEEP, BOUNCE_ATTR(state.msg_attr),
                          "null username in %s", state.msg_attr.recipient));
 
+    /*
+     * With each level of recursion, detect and break external message
+     * forwarding loops. If the looping recipient address has an owner-
+     * alias, then assume the error report should be sent there instead.
+     * 
+     * XXX A delivery agent cannot change the envelope sender address for
+     * bouncing, that would break multi-recipient mail. The fix would be to
+     * change the delivery agent to bounce service protocol. The bounce
+     * daemon would have to record in the bounce logfile for each bounced
+     * recipient the sender address that the recipient would have to be
+     * bounced to. This could simplify VERP implementation, at the cost of
+     * greatly complicating the normal bounce sending procedure.
+     */
+    if (delivered_find(state.loop_info, state.msg_attr.delivered)) {
+       VSTRING *canon_owner = 0;
+
+       if (var_ownreq_special) {
+           char   *owner;
+           const char *owner_rhs;
+
+           owner = concatenate("owner-", state.msg_attr.user, (char *) 0);
+           owner_rhs = maps_find(alias_maps, owner, ALIAS_DICT_FLAGS);
+           if (owner_rhs != 0) {
+               canon_owner = canon_addr_internal(vstring_alloc(10),
+                                    var_exp_own_alias ? owner_rhs : owner);
+               SET_OWNER_ATTR(state.msg_attr, STR(canon_owner), state.level);
+           }
+           myfree(owner);
+       }
+       rcpt_stat = bounce_append(BOUNCE_FLAG_KEEP, BOUNCE_ATTR(state.msg_attr),
+                  "mail forwarding loop for %s", state.msg_attr.recipient);
+       if (canon_owner)
+           vstring_free(canon_owner);
+       return (rcpt_stat);
+    }
+
     /*
      * Run the recipient through the delivery switch.
      */
index 3b62ab972257991e2aa617c89a15f866f11a2603..ed07f3a9818a49f5f56c586a4163ed627f95e84b 100644 (file)
@@ -6,6 +6,7 @@
 /* SYNOPSIS
 /* .fi
 /*     \fBpostsuper\fR [\fB-psv\fR] [\fB-d \fIqueue_id\fR]
+/*             [\fB-h \fIqueue_id\fR] [\fB-H \fIqueue_id\fR]
 /*             [\fB-r \fIqueue_id\fR] [\fIdirectory ...\fR]
 /* DESCRIPTION
 /*     The \fBpostsuper\fR command does maintenance jobs on the Postfix
 /*
 /*     By default, \fBpostsuper\fR performs the operations requested with the
 /*     \fB-s\fR and \fB-p\fR command-line options on all Postfix queue
-/*      directories - this includes the \fBincoming\fR, \fBactive\fR and
+/*     directories - this includes the \fBincoming\fR, \fBactive\fR and
 /*     \fBdeferred\fR directories with mail files and the \fBbounce\fR,
 /*     \fBdefer\fR and \fBflush\fR directories with log files.
 /*
 /*     Options:
 /* .IP "\fB-d \fIqueue_id\fR"
-/*      Delete one message with the named queue ID from the named
-/*     mail queue(s) (default: \fBincoming\fR, \fBactive\fR and
-/*      \fBdeferred\fR).
+/*     Delete one message with the named queue ID from the named
+/*     mail queue(s) (default: \fBhold\fR, \fBincoming\fR, \fBactive\fR and
+/*     \fBdeferred\fR).
 /*     If a \fIqueue_id\fR of \fB-\fR is specified, the program reads
-/*     queue IDs from standard input.
+/*     queue IDs from standard input. For example, to delete all mail
+/*     from or to \fBuser@example.com\fR:
+/* .sp
+/*     mailq | awk  \'BEGIN { RS = "" } \e
+/* .ti +4
+/*     / user@example\e.com$/ { print $1 } \e
+/* .br
+/*     \' | postsuper -d -
 /* .sp
 /*     Specify \fB-d ALL\fR to remove all messages; for example, specify
 /*     \fB-d ALL deferred\fR to delete mail in the \fBdeferred\fR queue.
 /*     \fBpostsuper\fR deletes the new message, instead of the old
 /*     message that it should have deleted.
 /* .RE
+/* .IP "\fB-h \fIqueue_id\fR"
+/*     Put mail "on hold" so that no attempt is made to deliver it.
+/*     Move one message with the named queue ID from the named
+/*     mail queue(s) (default: \fBincoming\fR, \fBactive\fR and
+/*     \fBdeferred\fR) to the \fBhold\fR queue.
+/*     If a \fIqueue_id\fR of \fB-\fR is specified, the program reads
+/*     queue IDs from standard input.
+/* .sp
+/*     Specify \fB-h ALL\fR to hold all messages; for example, specify
+/*     \fB-h ALL deferred\fR to hold mail in the \fBdeferred\fR queue.
+/*     As a safety measure, the word \fBALL\fR must be specified in upper
+/*     case.
+/* .sp
+/*     Note: mail that is put "on hold" will not expire.
+/* .IP "\fB-H \fIqueue_id\fR"
+/*     Release mail that was put "on hold".
+/*     Move one message with the named queue ID from the named
+/*     mail queue(s) (default: \fBhold\fR) to the \fBdeferred\fR queue.
+/*     If a \fIqueue_id\fR of \fB-\fR is specified, the program reads
+/*     queue IDs from standard input.
+/* .sp
+/*     Specify \fB-H ALL\fR to release all mail that is "on hold".
+/*     As a safety measure, the word \fBALL\fR must be specified in upper
+/*     case.
 /* .IP \fB-p\fR
 /*     Purge old temporary files that are left over after system or
 /*     software crashes.
 /* .IP "\fB-r \fIqueue_id\fR"
-/*      Requeue the message with the named queue ID from the named
-/*     mail queue(s) (default: \fBincoming\fR, \fBactive\fR and
-/*      \fBdeferred\fR).
-/*      To requeue multiple messages, specify multiple \fB-r\fR
+/*     Requeue the message with the named queue ID from the named
+/*     mail queue(s) (default: \fBhold\fR, \fBincoming\fR, \fBactive\fR and
+/*     \fBdeferred\fR).
+/*     To requeue multiple messages, specify multiple \fB-r\fR
 /*     command-line options.
-/*      Alternatively, if a \fIqueue_id\fR of \fB-\fR is specified,
+/*     Alternatively, if a \fIqueue_id\fR of \fB-\fR is specified,
 /*     the program reads queue IDs from standard input.
 /* .sp
 /*     Specify \fB-r ALL\fR to requeue all messages. As a safety
 /* .sp
 /*     A requeued message is moved to the \fBmaildrop\fR queue, from
 /*     where it is copied by the pickup daemon to a new file whose name
-/*      is guaranteed to match the new queue file inode number. The
-/*      new queue file is subjected again to mail address rewriting and
+/*     is guaranteed to match the new queue file inode number. The
+/*     new queue file is subjected again to mail address rewriting and
 /*     substitution. This is useful when rewriting rules or virtual
 /*     mappings have changed.
 /* .sp
 /*     the number of messages requeued with \fB-r\fR, and the number of
 /*     messages whose queue file name was fixed with \fB-s\fR. The report
 /*     is written to the standard error stream and to \fBsyslogd\fR.
+/* BUGS
+/*     Mail that is not sanitized by Postfix (i.e. mail in the \fBmaildrop\fR
+/*     queue) cannot be placed "on hold".
 /* CONFIGURATION PARAMETERS
 /* .ad
 /* .fi
 #define ACTION_DELETE_ALL (1<<3)       /* delete all queue file(s) */
 #define ACTION_REQUEUE_ONE (1<<4)      /* requeue named queue file(s) */
 #define ACTION_REQUEUE_ALL (1<<5)      /* requeue all queue file(s) */
+#define ACTION_HOLD_ONE        (1<<6)          /* put named queue file(s) on hold */
+#define ACTION_HOLD_ALL        (1<<7)          /* put all messages on hold */
+#define ACTION_RELEASE_ONE (1<<8)      /* release named queue file(s) */
+#define ACTION_RELEASE_ALL (1<<9)      /* release all "on hold" mail */
 
 #define ACTION_DEFAULT (ACTION_STRUCT | ACTION_PURGE)
 
+ /*
+  * Actions that operate on individually named queue files. These must never
+  * be done when queue file names are changed to match their inode number.
+  */
+#define ACTIONS_BY_QUEUE_ID    (ACTION_DELETE_ONE | ACTION_REQUEUE_ONE \
+                               | ACTION_HOLD_ONE | ACTION_RELEASE_ONE)
+
+ /*
+  * Mass rename operations that are postponed to a second pass after queue
+  * file names are changed to match their inode number.
+  */
+#define ACTIONS_AFTER_INUM_FIX (ACTION_REQUEUE_ALL | ACTION_HOLD_ALL \
+                               | ACTION_RELEASE_ALL)
+
  /*
   * Information about queue directories and what we expect to do there. If a
   * file has unexpected owner permissions and is older than some threshold,
@@ -195,6 +248,7 @@ static struct queue_info queue_info[] = {
     MAIL_QUEUE_INCOMING, MAIL_QUEUE_STAT_READY, RECURSE,
     MAIL_QUEUE_ACTIVE, MAIL_QUEUE_STAT_READY, RECURSE,
     MAIL_QUEUE_DEFERRED, MAIL_QUEUE_STAT_READY, RECURSE,
+    MAIL_QUEUE_HOLD, MAIL_QUEUE_STAT_READY, RECURSE,
     MAIL_QUEUE_DEFER, 0600, RECURSE,
     MAIL_QUEUE_BOUNCE, 0600, RECURSE,
     MAIL_QUEUE_FLUSH, 0600, RECURSE,
@@ -224,6 +278,8 @@ const char *log_queue_names[] = {
   * multiple results from a function.
   */
 static int message_requeued = 0;       /* requeued messages */
+static int message_held = 0;           /* messages put on hold */
+static int message_released = 0;       /* messages released from hold */
 static int message_deleted = 0;                /* deleted messages */
 static int inode_fixed = 0;            /* queue id matched to inode number */
 static int inode_mismatch = 0;         /* queue id inode mismatch */
@@ -391,6 +447,97 @@ static int requeue_one(const char **queue_names, const char *queue_id)
     return (found);
 }
 
+/* hold_one - put "on hold" one message instance */
+
+static int hold_one(const char **queue_names, const char *queue_id)
+{
+    struct stat st;
+    const char **msg_qpp;
+    const char *old_path;
+    VSTRING *new_path_buf;
+    int     found;
+    int     tries;
+
+    /*
+     * Sanity check. No early returns beyond this point.
+     */
+    if (!mail_queue_id_ok(queue_id)) {
+       msg_warn("invalid mail queue id: %s", queue_id);
+       return (0);
+    }
+    new_path_buf = vstring_alloc(100);
+
+    /*
+     * Skip meta file directories. Like the mass requeue operation, we not
+     * delete defer or bounce logfiles, to avoid losing a race where the
+     * queue manager decides to bounce mail after all recipients have been
+     * tried.
+     * 
+     * XXX We must not put maildrop mail on hold because that would mix already
+     * sanitized mail with mail that still needs to be sanitized.
+     */
+    for (found = 0, tries = 0; found == 0 && tries < 2; tries++) {
+       for (msg_qpp = queue_names; *msg_qpp != 0; msg_qpp++) {
+           if (strcmp(*msg_qpp, MAIL_QUEUE_MAILDROP) == 0)
+               continue;
+           if (strcmp(*msg_qpp, MAIL_QUEUE_HOLD) == 0)
+               continue;
+           if (!MESSAGE_QUEUE(find_queue_info(*msg_qpp)))
+               continue;
+           if (mail_open_ok(*msg_qpp, queue_id, &st, &old_path) != MAIL_OPEN_YES)
+               continue;
+           (void) mail_queue_path(new_path_buf, MAIL_QUEUE_HOLD, queue_id);
+           if (postrename(old_path, STR(new_path_buf)) == 0) {
+               msg_info("%s: placed on hold", queue_id);
+               found = 1;
+               break;
+           }                                   /* else: maybe lost a race */
+       }
+    }
+    vstring_free(new_path_buf);
+    return (found);
+}
+
+/* release_one - release one message instance that was placed "on hold" */
+
+static int release_one(const char **queue_names, const char *queue_id)
+{
+    struct stat st;
+    const char **msg_qpp;
+    const char *old_path;
+    VSTRING *new_path_buf;
+    int     found;
+
+    /*
+     * Sanity check. No early returns beyond this point.
+     */
+    if (!mail_queue_id_ok(queue_id)) {
+       msg_warn("invalid mail queue id: %s", queue_id);
+       return (0);
+    }
+    new_path_buf = vstring_alloc(100);
+
+    /*
+     * Skip inapplicable directories. This can happen when -H is combined
+     * with other operations.
+     */
+    found = 0;
+    for (msg_qpp = queue_names; *msg_qpp != 0; msg_qpp++) {
+       if (strcmp(*msg_qpp, MAIL_QUEUE_HOLD) != 0)
+           continue;
+       if (mail_open_ok(*msg_qpp, queue_id, &st, &old_path) != MAIL_OPEN_YES)
+           continue;
+       (void) mail_queue_path(new_path_buf, MAIL_QUEUE_DEFERRED, queue_id);
+       if (postrename(old_path, STR(new_path_buf)) == 0) {
+           msg_info("%s: released from hold", queue_id);
+           found = 1;
+           break;
+       }
+    }
+    vstring_free(new_path_buf);
+    return (found);
+}
+
 /* operate_stream - operate on queue IDs given on stream */
 
 static int operate_stream(VSTREAM *fp,
@@ -547,8 +694,8 @@ static void super(const char **queues, int action)
                continue;
 
            /*
-            * Remove alien directories. If maildrop is world writable, then
-            * we cannot abort just because we cannot remove someone's
+            * Remove alien directories. If maildrop is compromised, then we
+            * cannot abort just because we cannot remove someone's
             * directory.
             */
            if (S_ISDIR(st.st_mode)) {
@@ -565,11 +712,9 @@ static void super(const char **queues, int action)
 
            /*
             * Mass deletion. We count the deletion of mail that this system
-            * has taken responsibility for. XXX This option does not use the
-            * mail_queue(3) API, so that it can be run on a queue that does
-            * not have files in the "right" place. It would be terribly
-            * inefficient to first have to rename files into place before
-            * deleting them.
+            * has taken responsibility for. XXX This option does not use
+            * mail_queue_remove(), so that it can avoid having to first move
+            * queue files to the "right" subdirectory level.
             */
            if (action & ACTION_DELETE_ALL) {
                if (postremove(STR(actual_path)) == 0)
@@ -594,22 +739,16 @@ static void super(const char **queues, int action)
                continue;
            }
 
-           /*
-            * Skip temporary message files that aren't old enough.
-            */
-           if (MESSAGE_QUEUE(qp) && !READY_MESSAGE(st))
-               /* No further work on this object is possible. */
-               continue;
-
            /*
             * Fix queueid#FIX names that were left from a previous pass over
             * the queue where message queue file names were matched to their
-            * inode number. This requires that the file is already in the
-            * proper directory so that we only have to strip the suffix.
-            * Make sure that the name minus suffix is well formed and that
-            * the name matches the file inode number.
+            * inode number. We strip the suffix and move the file into the
+            * proper subdirectory level. Make sure that the name minus
+            * suffix is well formed and that the name matches the file inode
+            * number.
             */
-           if (strcmp(path + (strlen(path) - SUFFIX_LEN), SUFFIX) == 0) {
+           if ((action & ACTION_STRUCT)
+               && strcmp(path + (strlen(path) - SUFFIX_LEN), SUFFIX) == 0) {
                path[strlen(path) - SUFFIX_LEN] = 0;    /* XXX */
                if (!mail_queue_id_ok(path)) {
                    msg_warn("bogus file name: %s", STR(actual_path));
@@ -625,8 +764,7 @@ static void super(const char **queues, int action)
                        continue;
                    }
                }
-               vstring_strncpy(wanted_path, STR(actual_path),
-                               strlen(STR(actual_path)) - SUFFIX_LEN);
+               (void) mail_queue_path(wanted_path, queue_name, path);
                if (postrename(STR(actual_path), STR(wanted_path)) < 0) {
                    /* No further work on this object is possible. */
                    continue;
@@ -647,25 +785,6 @@ static void super(const char **queues, int action)
                continue;
            }
 
-           /*
-            * Mass requeuing. The pickup daemon will copy requeued mail to a
-            * new queue file, so that address rewriting is applied again.
-            * XXX This option does not use the mail_queue(3) API, so that it
-            * can be run on a queue that does not have the files in the
-            * "right" place. It would be terribly inefficient to first have
-            * to rename files into place before requeuing them. Like the
-            * requeue_one() routine, this code does not touch logfiles.
-            */
-           if ((action & ACTION_REQUEUE_ALL)
-               && MESSAGE_QUEUE(qp)
-               && strcmp(queue_name, MAIL_QUEUE_MAILDROP) != 0) {
-               (void) mail_queue_path(wanted_path, MAIL_QUEUE_MAILDROP, path);
-               if (postrename(STR(actual_path), STR(wanted_path)) == 0)
-                   message_requeued++;
-               /* At this point, path and actual_path are invalidated. */
-               continue;
-           }
-
            /*
             * See if the file name matches the file inode number. Skip meta
             * file directories. This option requires that meta files be put
@@ -675,11 +794,9 @@ static void super(const char **queues, int action)
             * upon the second pass the #FIX is stripped off the name. Of
             * course we have to be prepared that the program is interrupted
             * before it completes, so any left-over newqueueid#FIX files
-            * have to be handled properly. XXX This option does not use the
-            * mail_queue(3) API for message queue files, so that it can be
-            * run on a queue that does not have message queue files in the
-            * "right" place. It would be terribly inefficient to first have
-            * to rename files into place before fixing the file names.
+            * have to be handled properly. XXX This option cannot use
+            * mail_queue_rename(), because the queue file name violates
+            * normal queue file syntax.
             */
            if ((action & ACTION_STRUCT) != 0 && MESSAGE_QUEUE(qp)) {
                if (sscanf(path + 5, "%lx", &inum) != 1) {
@@ -688,16 +805,73 @@ static void super(const char **queues, int action)
                }
                if (inum != (unsigned long) st.st_ino) {
                    inode_mismatch++;           /* before we fix */
+                   action &= ~ACTIONS_AFTER_INUM_FIX;
                    fix_queue_id(STR(actual_path), queue_name, path, st.st_ino);
                    /* At this point, path and actual_path are invalidated. */
                    continue;
                }
            }
 
+           /*
+            * Mass requeuing. The pickup daemon will copy requeued mail to a
+            * new queue file, so that address rewriting is applied again.
+            * XXX This option does not use mail_queue_rename(), so that it
+            * can avoid having to first move queue files to the "right"
+            * subdirectory level. Like the requeue_one() routine, this code
+            * does not touch logfiles.
+            */
+           if ((action & ACTION_REQUEUE_ALL)
+               && MESSAGE_QUEUE(qp)
+               && strcmp(queue_name, MAIL_QUEUE_MAILDROP) != 0) {
+               (void) mail_queue_path(wanted_path, MAIL_QUEUE_MAILDROP, path);
+               if (postrename(STR(actual_path), STR(wanted_path)) == 0)
+                   message_requeued++;
+               /* At this point, path and actual_path are invalidated. */
+               continue;
+           }
+
+           /*
+            * Mass renaming to the "on hold" queue. XXX This option does not
+            * use mail_queue_rename(), so that it can avoid having to first
+            * move queue files to the "right" subdirectory level. Like the
+            * hold_one() routine, this code does not touch logfiles, and
+            * must not touch files in the maildrop queue, because maildrop
+            * files contain data that has not yet been sanitized and therefore
+            * must not be mixed with already sanitized mail.
+            */
+           if ((action & ACTION_HOLD_ALL)
+               && MESSAGE_QUEUE(qp)
+               && strcmp(queue_name, MAIL_QUEUE_MAILDROP) != 0
+               && strcmp(queue_name, MAIL_QUEUE_HOLD) != 0) {
+               (void) mail_queue_path(wanted_path, MAIL_QUEUE_HOLD, path);
+               if (postrename(STR(actual_path), STR(wanted_path)) == 0)
+                   message_held++;
+               /* At this point, path and actual_path are invalidated. */
+               continue;
+           }
+
+           /*
+            * Mass release from the "on hold" queue. XXX This option does
+            * not use mail_queue_rename(), so that it can avoid having to
+            * first move queue files to the "right" subdirectory level. Like
+            * the release_one() routine, this code must not touch logfiles.
+            */
+           if ((action & ACTION_RELEASE_ALL)
+               && strcmp(queue_name, MAIL_QUEUE_HOLD) == 0) {
+               (void) mail_queue_path(wanted_path, MAIL_QUEUE_DEFERRED, path);
+               if (postrename(STR(actual_path), STR(wanted_path)) == 0)
+                   message_released++;
+               /* At this point, path and actual_path are invalidated. */
+               continue;
+           }
+
            /*
             * See if this file sits in the right place in the file system
             * hierarchy. Its place may be wrong after a change to the
-            * hash_queue_{names,depth} parameter settings.
+            * hash_queue_{names,depth} parameter settings. This requires
+            * that the bounce/defer logfiles be at the right subdirectory
+            * level first, otherwise we would fail to properly rename
+            * bounce/defer logfiles.
             */
            if (action & ACTION_STRUCT) {
                (void) mail_queue_path(wanted_path, queue_name, path);
@@ -745,10 +919,16 @@ int     main(int argc, char **argv)
     int     c;
     ARGV   *requeue_names = 0;
     ARGV   *delete_names = 0;
+    ARGV   *hold_names = 0;
+    ARGV   *release_names = 0;
     char  **cpp;
 
     /*
-     * Defaults.
+     * Defaults. The structural checks must fix the directory levels of "log
+     * file" directories (bounce, defer) before doing structural checks on
+     * the "message file" directories, so that we can find the logfiles in
+     * the right place when message files need to be renamed to match their
+     * inode number.
      */
     static char *default_queues[] = {
        MAIL_QUEUE_DEFER,               /* before message directories */
@@ -757,9 +937,20 @@ int     main(int argc, char **argv)
        MAIL_QUEUE_INCOMING,
        MAIL_QUEUE_ACTIVE,
        MAIL_QUEUE_DEFERRED,
+       MAIL_QUEUE_HOLD,
        MAIL_QUEUE_FLUSH,
        0,
     };
+    static char *default_hold_queues[] = {
+       MAIL_QUEUE_INCOMING,
+       MAIL_QUEUE_ACTIVE,
+       MAIL_QUEUE_DEFERRED,
+       0,
+    };
+    static char *default_release_queues[] = {
+       MAIL_QUEUE_HOLD,
+       0,
+    };
 
     /*
      * Be consistent with file permissions.
@@ -827,11 +1018,14 @@ int     main(int argc, char **argv)
     /*
      * Parse JCL.
      */
-    while ((c = GETOPT(argc, argv, "d:pr:sv")) > 0) {
+    while ((c = GETOPT(argc, argv, "d:h:H:pr:sv")) > 0) {
        switch (c) {
        default:
-           msg_fatal("usage: %s [-d queue_id (delete)] [-p (purge temporary files)] [-r queue_id (requeue)] [-s (structure fix)]",
-                     argv[0]);
+           msg_fatal("usage: %s [-d queue_id (delete)] "
+                     "[-h queue_id (hold)] [-H queue_id (un-hold)] "
+                     "[-p (purge temporary files)] [-r queue_id (requeue)] "
+                     "[-s (structure fix)] [-v (verbose)] "
+                     "[queue...]", argv[0]);
        case 'd':
            if (delete_names == 0)
                delete_names = argv_alloc(1);
@@ -839,6 +1033,20 @@ int     main(int argc, char **argv)
            action |= (strcmp(optarg, "ALL") == 0 ?
                       ACTION_DELETE_ALL : ACTION_DELETE_ONE);
            break;
+       case 'h':
+           if (hold_names == 0)
+               hold_names = argv_alloc(1);
+           argv_add(hold_names, optarg, (char *) 0);
+           action |= (strcmp(optarg, "ALL") == 0 ?
+                      ACTION_HOLD_ALL : ACTION_HOLD_ONE);
+           break;
+       case 'H':
+           if (release_names == 0)
+               release_names = argv_alloc(1);
+           argv_add(release_names, optarg, (char *) 0);
+           action |= (strcmp(optarg, "ALL") == 0 ?
+                      ACTION_RELEASE_ALL : ACTION_RELEASE_ONE);
+           break;
        case 'p':
            action |= ACTION_PURGE;
            break;
@@ -858,20 +1066,46 @@ int     main(int argc, char **argv)
        }
     }
 
+    /*
+     * Sanity checks.
+     */
+    if ((action & ACTION_DELETE_ALL) && (action & ACTION_DELETE_ONE)) {
+       msg_warn("option \"-d ALL\" will ignore other command line queue IDs");
+       action &= ~ACTION_DELETE_ONE;
+    }
+    if ((action & ACTION_REQUEUE_ALL) && (action & ACTION_REQUEUE_ONE)) {
+       msg_warn("option \"-r ALL\" will ignore other command line queue IDs");
+       action &= ~ACTION_REQUEUE_ONE;
+    }
+    if ((action & ACTION_HOLD_ALL) && (action & ACTION_HOLD_ONE)) {
+       msg_warn("option \"-h ALL\" will ignore other command line queue IDs");
+       action &= ~ACTION_HOLD_ONE;
+    }
+    if ((action & ACTION_RELEASE_ALL) && (action & ACTION_RELEASE_ONE)) {
+       msg_warn("option \"-H ALL\" will ignore other command line queue IDs");
+       action &= ~ACTION_RELEASE_ONE;
+    }
+
     /*
      * Execute the explicitly specified (or default) action, on the
      * explicitly specified (or default) queues.
      * 
      * XXX Work around gcc const brain damage.
+     * 
+     * XXX The file name/inode number fix should always run over all message
+     * file directories, and should always be preceded by a subdirectory
+     * level check of the bounce and defer logfile directories.
      */
     if (action == 0)
        action = ACTION_DEFAULT;
-    if (argv[optind] == 0)
-       queues = (const char **) default_queues;
-    else
+    if (argv[optind] != 0)
        queues = (const char **) argv + optind;
-
-#define ACTIONS_BY_QUEUE_ID (ACTION_DELETE_ONE | ACTION_REQUEUE_ONE)
+    else if (action == ACTION_HOLD_ALL)
+       queues = (const char **) default_hold_queues;
+    else if (action == ACTION_RELEASE_ALL)
+       queues = (const char **) default_release_queues;
+    else
+       queues = (const char **) default_queues;
 
     /*
      * Basic queue maintenance, as well as mass deletion, mass requeuing, and
@@ -879,15 +1113,27 @@ int     main(int argc, char **argv)
      * right place before the file-by-name operations are done.
      */
     if (action & ~ACTIONS_BY_QUEUE_ID)
-       super(queues, action & ~ACTIONS_BY_QUEUE_ID);
+       super(queues, action);
 
     /*
      * If any file names needed changing to match the message file inode
      * number, those files were named newqeueid#FIX. We need a second pass to
-     * strip the suffix from the new queue ID.
+     * strip the suffix from the new queue ID, and to complete any requested
+     * operations that had to be skipped in the first pass.
      */
     if (inode_mismatch > 0)
-       super(queues, 0);
+       super(queues, action);
+
+    /*
+     * Don't do actions by queue file name if any queue files changed name
+     * because they did not match the queue file inode number. We could be
+     * acting on the wrong queue file and lose mail.
+     */
+    if ((action & ACTIONS_BY_QUEUE_ID)
+       && (inode_mismatch > 0 || inode_fixed > 0)) {
+       msg_error("QUEUE FILE NAMES WERE CHANGED TO MATCH INODE NUMBERS");
+       msg_fatal("CHECK YOUR QUEUE IDS AND RE-ISSUE THE COMMAND");
+    }
 
     /*
      * Delete queue files by name. This must not be done when queue file
@@ -895,11 +1141,9 @@ int     main(int argc, char **argv)
      * because we could be deleting the wrong message.
      */
     if (action & ACTION_DELETE_ONE) {
-       if (inode_mismatch > 0 || inode_fixed > 0) {
-           msg_error("QUEUE FILE NAMES WERE CHANGED TO MATCH INODE NUMBERS");
-           msg_fatal("CHECK YOUR QUEUE IDS AND RE-ISSUE THE COMMAND");
-       }
        argv_terminate(delete_names);
+       queues = (const char **)
+           (argv[optind] ? argv + optind : default_queues);
        for (cpp = delete_names->argv; *cpp; cpp++) {
            if (strcmp(*cpp, "ALL") == 0)
                continue;
@@ -917,11 +1161,9 @@ int     main(int argc, char **argv)
      * because we could be requeuing the wrong message.
      */
     if (action & ACTION_REQUEUE_ONE) {
-       if (inode_mismatch > 0 || inode_fixed > 0) {
-           msg_error("QUEUE FILE NAMES WERE CHANGED TO MATCH INODE NUMBERS");
-           msg_fatal("CHECK YOUR QUEUE IDS AND RE-ISSUE THE COMMAND");
-       }
        argv_terminate(requeue_names);
+       queues = (const char **)
+           (argv[optind] ? argv + optind : default_queues);
        for (cpp = requeue_names->argv; *cpp; cpp++) {
            if (strcmp(*cpp, "ALL") == 0)
                continue;
@@ -933,6 +1175,46 @@ int     main(int argc, char **argv)
        }
     }
 
+    /*
+     * Put on hold queue files by name. This must not be done when queue file
+     * names have changed names as a result of inode number mismatches,
+     * because we could put on hold the wrong message.
+     */
+    if (action & ACTION_HOLD_ONE) {
+       argv_terminate(hold_names);
+       queues = (const char **)
+           (argv[optind] ? argv + optind : default_hold_queues);
+       for (cpp = hold_names->argv; *cpp; cpp++) {
+           if (strcmp(*cpp, "ALL") == 0)
+               continue;
+           if (strcmp(*cpp, "-") == 0)
+               message_held +=
+                   operate_stream(VSTREAM_IN, hold_one, queues);
+           else
+               message_held += hold_one(queues, *cpp);
+       }
+    }
+
+    /*
+     * Take "off hold" queue files by name. This must not be done when queue
+     * file names have changed names as a result of inode number mismatches,
+     * because we could take off hold the wrong message.
+     */
+    if (action & ACTION_RELEASE_ONE) {
+       argv_terminate(release_names);
+       queues = (const char **)
+           (argv[optind] ? argv + optind : default_release_queues);
+       for (cpp = release_names->argv; *cpp; cpp++) {
+           if (strcmp(*cpp, "ALL") == 0)
+               continue;
+           if (strcmp(*cpp, "-") == 0)
+               message_released +=
+                   operate_stream(VSTREAM_IN, release_one, queues);
+           else
+               message_released += release_one(queues, *cpp);
+       }
+    }
+
     /*
      * Report.
      */
@@ -942,6 +1224,12 @@ int     main(int argc, char **argv)
     if (message_deleted > 0)
        msg_info("Deleted: %d message%s", message_deleted,
                 message_deleted > 1 ? "s" : "");
+    if (message_held > 0)
+       msg_info("Placed on hold: %d message%s",
+                message_held, message_held > 1 ? "s" : "");
+    if (message_released > 0)
+       msg_info("Released from hold: %d message%s",
+                message_released, message_released > 1 ? "s" : "");
     if (inode_fixed > 0)
        msg_info("Renamed to match inode number: %d message%s", inode_fixed,
                 inode_fixed > 1 ? "s" : "");
@@ -955,6 +1243,10 @@ int     main(int argc, char **argv)
        argv_free(requeue_names);
     if (delete_names)
        argv_free(delete_names);
+    if (hold_names)
+       argv_free(hold_names);
+    if (release_names)
+       argv_free(release_names);
 
     exit(0);
 }
index 899e45cba0ce56e5fb27699f3d610d150d903d8b..679bd5ddbf184bf5ce0d6ed0f10603d3af4bbbad 100644 (file)
 /*     size, arrival time, sender, and the recipients that still need to
 /*     be delivered.  If mail could not be delivered upon the last attempt,
 /*     the reason for failure is shown. This mode of operation is implemented
-/*     by executing the \fBpostqueue\fR(1) command.
+/*     by executing the \fBpostqueue\fR(1) command. The queue ID string
+/*     is followed by an optional status character:
+/* .RS
+/* .IP \fB*\fR
+/*     The message is in the \fBactive\fR queue, i.e. the message is 
+/*     selected for delivery.
+/* .IP \fB!\fR
+/*     The message is in the \fBhold\fR queue, i.e. no further delivery
+/*     attempt will be made until the mail is taken off hold.
+/* .RE
 /* .IP \fBnewaliases\fR
 /*     Initialize the alias database.  If no input file is specified (with
 /*     the \fB-oA\fR option, see below), the program processes the file(s)
 /*     The characters that Postfix accepts as VERP delimiter characters.
 /* SEE ALSO
 /*     pickup(8) mail pickup daemon
+/*     postsuper(1) queue maintenance
 /*     postalias(1) maintain alias database
 /*     postdrop(1) mail posting utility
 /*     postfix(1) mail system control
index c8c70855b2a16f4171eed20f08a3b91c4a8fb4f6..8ccd4f31c8467c1b39d70290596387c241e0f420 100644 (file)
@@ -114,7 +114,8 @@ static void showq_report(VSTREAM *client, char *queue, char *id,
     long    msg_size = 0;
     BOUNCE_LOG *logfile;
     HTABLE *dup_filter = 0;
-    char    status = (strcmp(queue, MAIL_QUEUE_ACTIVE) == 0 ? '*' : ' ');
+    char    status = (strcmp(queue, MAIL_QUEUE_ACTIVE) == 0 ? '*' :
+                     strcmp(queue, MAIL_QUEUE_HOLD) == 0 ? '!' : ' ');
     long    offset;
 
     /*
@@ -246,6 +247,7 @@ static void showq_service(VSTREAM *client, char *unused_service, char **argv)
        MAIL_QUEUE_INCOMING, mail_scan_dir_next,
        MAIL_QUEUE_ACTIVE, mail_scan_dir_next,
        MAIL_QUEUE_DEFERRED, mail_scan_dir_next,
+       MAIL_QUEUE_HOLD, mail_scan_dir_next,
        0,
     };
 
index 5cbf38da7c888888b17754dc9c45550017cadacd..c402bc4adbbd66a1acdebb6a24482503efc79325 100644 (file)
@@ -230,6 +230,7 @@ int     smtp_helo(SMTP_STATE *state)
                                   "host %s refused to talk to me: %s",
                                   session->namaddr,
                                   translit(resp->str, "\n", " ")));
+       return (0);
     }
 
     /*
index cf2adfb46ddea5052388aaea58562f0dec0e8247..91e7b6dee9455ea43d4244c30a06803fbe99f928 100644 (file)
 /* .fi
 /* .IP \fBaccess_map_reject_code\fR
 /*     Server response when a client violates an access database restriction.
+/* .IP \fBdefer_code\fR
+/*     Server response when a client request is rejected by the \fBdefer\fR
+/*     restriction.
 /* .IP \fBinvalid_hostname_reject_code\fR
 /*     Server response when a client violates the \fBreject_invalid_hostname\fR
 /*     restriction.
@@ -358,6 +361,7 @@ int     var_access_map_code;
 char   *var_maps_rbl_domains;
 int     var_helo_required;
 int     var_reject_code;
+int     var_defer_code;
 int     var_smtpd_err_sleep;
 int     var_non_fqdn_code;
 char   *var_always_bcc;
@@ -1577,6 +1581,7 @@ int     main(int argc, char **argv)
        VAR_MAPS_RBL_CODE, DEF_MAPS_RBL_CODE, &var_maps_rbl_code, 0, 0,
        VAR_ACCESS_MAP_CODE, DEF_ACCESS_MAP_CODE, &var_access_map_code, 0, 0,
        VAR_REJECT_CODE, DEF_REJECT_CODE, &var_reject_code, 0, 0,
+       VAR_DEFER_CODE, DEF_DEFER_CODE, &var_defer_code, 0, 0,
        VAR_NON_FQDN_CODE, DEF_NON_FQDN_CODE, &var_non_fqdn_code, 0, 0,
        VAR_SMTPD_JUNK_CMD, DEF_SMTPD_JUNK_CMD, &var_smtpd_junk_cmd_limit, 1, 0,
        VAR_SMTPD_HIST_THRSH, DEF_SMTPD_HIST_THRSH, &var_smtpd_hist_thrsh, 1, 0,
@@ -1617,7 +1622,7 @@ int     main(int argc, char **argv)
        VAR_ALIAS_MAPS, DEF_ALIAS_MAPS, &var_alias_maps, 0, 0,
        VAR_LOCAL_RCPT_MAPS, DEF_LOCAL_RCPT_MAPS, &var_local_rcpt_maps, 0, 0,
        VAR_SMTPD_SASL_OPTS, DEF_SMTPD_SASL_OPTS, &var_smtpd_sasl_opts, 0, 0,
-       VAR_SMTPD_SASL_REALM, DEF_SMTPD_SASL_REALM, &var_smtpd_sasl_realm, 1, 0,
+       VAR_SMTPD_SASL_REALM, DEF_SMTPD_SASL_REALM, &var_smtpd_sasl_realm, 0, 0,
        VAR_FILTER_XPORT, DEF_FILTER_XPORT, &var_filter_xport, 0, 0,
        VAR_PERM_MX_NETWORKS, DEF_PERM_MX_NETWORKS, &var_perm_mx_networks, 0, 0,
        VAR_SMTPD_SND_AUTH_MAPS, DEF_SMTPD_SND_AUTH_MAPS, &var_smtpd_snd_auth_maps, 0, 0,
index 27ed4fb7658f8ccec2f37aadb6150761b1006165..abde395c886a58fd102e65e1633a00ea0a940038 100644 (file)
@@ -53,8 +53,9 @@
 /*     Restrictions that can appear in some or all restriction
 /*     lists:
 /* .IP reject
+/* .IP defer
 /* .IP permit
-/*     Reject or permit the request unconditionally. This is to be used
+/*     Reject, defer or permit the request unconditionally. This is to be used
 /*     at the end of a restriction list in order to make the default
 /*     action explicit.
 /* .IP reject_unknown_client
@@ -516,6 +517,7 @@ void    smtpd_check_init(void)
        CHECK_RELAY_DOMAINS,
        REJECT_UNAUTH_DEST,
        REJECT_ALL,
+       DEFER_ALL,
        0,
     };
 
@@ -2057,6 +2059,13 @@ static int generic_checks(SMTPD_STATE *state, ARGV *restrictions,
            if (cpp[1] != 0 && state->warn_if_reject == 0)
                msg_warn("restriction `%s' after `%s' is ignored",
                         cpp[1], PERMIT_ALL);
+       } else if (strcasecmp(name, DEFER_ALL) == 0) {
+           status = smtpd_check_reject(state, MAIL_ERROR_POLICY,
+                                     "%d <%s>: %s rejected: Try again later",
+                                 var_defer_code, reply_name, reply_class);
+           if (cpp[1] != 0 && state->warn_if_reject == 0)
+               msg_warn("restriction `%s' after `%s' is ignored",
+                        cpp[1], DEFER_ALL);
        } else if (strcasecmp(name, REJECT_ALL) == 0) {
            status = smtpd_check_reject(state, MAIL_ERROR_POLICY,
                                      "%d <%s>: %s rejected: Access denied",
@@ -2110,6 +2119,8 @@ static int generic_checks(SMTPD_STATE *state, ARGV *restrictions,
                                         state->helo_name, SMTPD_NAME_HELO);
            }
        } else if (strcasecmp(name, PERMIT_NAKED_IP_ADDR) == 0) {
+           msg_warn("restriction %s is deprecated. Use %s instead",
+                       PERMIT_NAKED_IP_ADDR, PERMIT_MYNETWORKS);
            if (state->helo_name) {
                if (state->helo_name[strspn(state->helo_name, "0123456789.")] == 0
                && (status = reject_invalid_hostaddr(state, state->helo_name,
index a112245e9aa9b5719de554a5e157c8ef5bb419d4..c05b7d6721e7ae6b1496e35741267b9735ea1d14 100644 (file)
@@ -255,6 +255,7 @@ void    smtpd_sasl_connect(SMTPD_STATE *state)
      */
 #define NO_SECURITY_LAYERS     (0)
 #define NO_SESSION_CALLBACKS   ((sasl_callback_t *) 0)
+#define NO_AUTH_REALM          ((char *) 0)
 
 #if SASL_VERSION_MAJOR >= 2 && defined(USE_SASL_IP_AUTH)
 
@@ -273,7 +274,8 @@ void    smtpd_sasl_connect(SMTPD_STATE *state)
     client_address = 0;
 #endif
 
-    if (SASL_SERVER_NEW("smtp", var_myhostname, var_smtpd_sasl_realm,
+    if (SASL_SERVER_NEW("smtp", var_myhostname, *var_smtpd_sasl_realm ?
+                       var_smtpd_sasl_realm : NO_AUTH_REALM,
                        server_address, client_address,
                        NO_SESSION_CALLBACKS, NO_SECURITY_LAYERS,
                        &state->sasl_conn) != SASL_OK)
index 970738569fb5ca039a95e6decfedc252a53a2cba..eab1c20f33fb956d0746b971f0b8d1ae01509dbe 100644 (file)
 
 #ifdef HAS_LDAP
 
+ /*
+  * Older APIs have weird memory freeing behavior.
+  */
+#if !defined(LDAP_API_VERSION) || (LDAP_API_VERSION < 2000)
+#error "Your LDAP version is too old"
+#endif
+
 #include <sys/time.h>
 #include <stdio.h>
 #include <signal.h>
index 37a4d8acf9afa106ed91923b4f99f17b789c087b..fd837dd259739dc6648e83c1754a36c772034015 100644 (file)
@@ -502,6 +502,34 @@ extern int initgroups(const char *, int);
 #define NATIVE_NEWALIAS_PATH "/usr/bin/newaliases"
 #define NATIVE_COMMAND_DIR "/usr/sbin"
 #define NATIVE_DAEMON_DIR "/usr/libexec/postfix"
+#endif
+
+#ifdef LINUX1
+#define SUPPORTED
+#include <sys/types.h>
+#define USE_PATHS_H
+#define HAS_FLOCK_LOCK
+#define HAS_FCNTL_LOCK
+#define INTERNAL_LOCK  MYFLOCK_STYLE_FLOCK
+#define DEF_MAILBOX_LOCK "flock, dotlock"      /* unverified */
+#define HAS_FSYNC
+#define HAS_DB
+#define DEF_DB_TYPE    "hash"
+#define ALIAS_DB_MAP   "hash:/etc/aliases"
+#define HAS_NIS
+#define GETTIMEOFDAY(t)        gettimeofday(t,(struct timezone *) 0)
+#define ROOT_PATH      "/bin:/usr/bin:/sbin:/usr/sbin"
+#define FIONREAD_IN_TERMIOS_H          /* maybe unnecessary */
+#define USE_STATFS
+#define STATFS_IN_SYS_VFS_H
+#define UNIX_DOMAIN_CONNECT_BLOCKS_FOR_ACCEPT  /* unverified */
+#define PREPEND_PLUS_TO_OPTSTRING
+#define HAS_POSIX_REGEXP
+#define NATIVE_SENDMAIL_PATH "/usr/sbin/sendmail"
+#define NATIVE_MAILQ_PATH "/usr/bin/mailq"
+#define NATIVE_NEWALIAS_PATH "/usr/bin/newaliases"
+#define NATIVE_COMMAND_DIR "/usr/sbin"
+#define NATIVE_DAEMON_DIR "/usr/libexec/postfix"
 #endif
 
  /*
index 1bec4727685078d3c16ef2943543e18550587ac1..c2f9a2ae4f8802337c4bbaf9c10303c721bd4400 100644 (file)
@@ -6,7 +6,7 @@ TESTSRC =
 WARN   = -W -Wformat -Wimplicit -Wmissing-prototypes \
        -Wparentheses -Wstrict-prototypes -Wswitch -Wuninitialized \
        -Wunused
-DEFS   = -I. -I$(INC_DIR) -D$(SYSTYPE) -I..
+DEFS   = -I. -I$(INC_DIR) -D$(SYSTYPE)
 CFLAGS = $(DEBUG) $(OPT) $(DEFS)
 PROG   = virtual
 TESTPROG=