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
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
=========================
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
==========================================================
# 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.
$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
#
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.
<html> <head> </head> <body> <pre>
-
LOCAL(8) LOCAL(8)
<b>NAME</b>
P.O. Box 704
Yorktown Heights, NY 10598, USA
- 1
-
+ LOCAL(8)
</pre> </body> </html>
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
<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
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
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>
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
<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
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>
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>
<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>
<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>
<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>
<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>
<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>
<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
;;
*) 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 ]
.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
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.
\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.
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
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)
.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
.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.
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 \
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
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)
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;
/* 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;
/*
/* 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;
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
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
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)
--- /dev/null
+/*++
+/* 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);
+}
*/
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
*/
} 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 *);
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)) {
/* 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
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;
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);
+}
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.
#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.
/* 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)
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 */
}
}
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);
}
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)
#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
* 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"
* 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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
const char *alias_result;
char *expansion;
char *owner;
- static MAPS *maps;
char **cpp;
uid_t alias_uid;
struct mypasswd *alias_pwd;
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
*
*
* 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) {
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);
#include <been_here.h>
#include <mail_params.h>
#include <ext_prop.h>
+#include <maps.h>
/* Single server skeleton. */
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 */
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 */
#include <tok822.h>
#include <deliver_request.h>
#include <mbox_conf.h>
+#include <maps.h>
/*
* User attributes: these control the privileges for delivery to external
#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
#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)
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
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.
*/
/* 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,
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,
* 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 */
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,
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)) {
/*
* 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)
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));
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;
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
* 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) {
}
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);
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 */
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.
/*
* 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);
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;
}
}
+ /*
+ * 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
* 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
* 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;
* 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;
}
}
+ /*
+ * 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.
*/
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" : "");
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);
}
/* 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
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;
/*
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,
};
"host %s refused to talk to me: %s",
session->namaddr,
translit(resp->str, "\n", " ")));
+ return (0);
}
/*
/* .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.
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;
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,
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,
/* 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
CHECK_RELAY_DOMAINS,
REJECT_UNAUTH_DEST,
REJECT_ALL,
+ DEFER_ALL,
0,
};
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",
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,
*/
#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)
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)
#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>
#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
/*
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=