-TBINATTR_INFO
-TBINHASH
-TBINHASH_INFO
+-TBOUNCE_INFO
+-TBOUNCE_LOG
-TBOUNCE_STAT
-TCLEANUP_STATE
-TCLIENT_LIST
Portability: MacOSX hints and tips by Joe Block, University
of Central Florida School of Optics/CREOL
- The MacOSX gcc compiler does not understand the new
- printf_like/scanf_like attributes. File: util/sys_defs.h.
+ Portability: The MacOSX gcc compiler does not understand
+ the new printf_like/scanf_like attributes. File: util/sys_defs.h.
20000922
still logged with the default syslog facility, as are errors
while processing the main.cf file (surprise). Based on
code by Andrew McNamara.
+
+20000923
+
+ Cleanup: new bounce logfile API so that Postfix can change
+ to an extensible bounce logfile format with per-recipient
+ sender addresses (needed for VERP and for reporting local
+ list delivery problems to the list owner) and other
+ attributes. File: global/bounce_log.[hc].
+
+ Cleanup: replaced the ad-hoc logfile parsing code in showq
+ by something that uses the generic bounce logfile API.
+
+20000924
+
+ Feature: Postfix bounced mail and delayed mail notifications
+ now have the standard RFC 1894 form (DSN). The bounce
+ service now uses the's generic bounce logfile API. File:
+ bounce/bounce_notify_service.c, bounce/bounce_notify_util.c.
+
+ Cleanup: deleted the per-recipient bounce protocol. Future
+ bounce logfiles will support per-recipient bounce addresses.
+ Files: global/bounce.c, bounce/bounce_recip_service.
+Incompatible changes with snapshot-20000924
+===========================================
+
+The postmaster address in the "sorry" text at the top of bounced
+mail is now just postmaster, not postmaster@sending.machine. The
+idea is to refer users to their own postmaster.
+
+Major changes with snapshot-20000924
+====================================
+
+DSN formatted bounced/delayed mail notifications, finally. The
+human-readable text still exists, so that users will not have to
+be unnecessarily confused by all the ugliness of RFC 1894.
+
Major changes with snapshot-20000923
====================================
and of the corresponding message. When the bounce
is posted successfully, the log file is deleted.
- <b>o</b> Post a bounce message without accessing a per-mes-
- sage log file.
-
- The software does a best effort to notify the sender that
- there was a problem. A notification is sent even when the
+ The software does a best effort to notify the sender that
+ there was a problem. A notification is sent even when the
log file or original message cannot be read.
- Optionally, a client can request that the per-message log
- file be deleted when the requested operation fails. This
+ Optionally, a client can request that the per-message log
+ file be deleted when the requested operation fails. This
is used by clients that cannot retry transactions by them-
- selves, and that depend on retry logic in their own
+ selves, and that depend on retry logic in their own
client.
<b>STANDARDS</b>
- RFC 822 (ARPA Internet Text Messages)
+ <a href="http://www.faqs.org/rfcs/rfc822.html">RFC 822</a> (ARPA Internet Text Messages)
+ <a href="http://www.faqs.org/rfcs/rfc1894.html">RFC 1894</a> (Delivery Status Notifications)
<b>DIAGNOSTICS</b>
Problems and transactions are logged to <b>syslogd</b>(8).
<b>BUGS</b>
- The log files use an ad-hoc, unstructured format. This
- will have to change in order to easily support standard
+ The log files use an ad-hoc, unstructured format. This
+ will have to change in order to easily support standard
delivery status notifications.
<b>CONFIGURATION</b> <b>PARAMETERS</b>
- The following <b>main.cf</b> parameters are especially relevant
- to this program. See the Postfix <b>main.cf</b> file for syntax
- details and for default values. Use the <b>postfix</b> <b>reload</b>
+ The following <b>main.cf</b> parameters are especially relevant
+ to this program. See the Postfix <b>main.cf</b> file for syntax
+ details and for default values. Use the <b>postfix</b> <b>reload</b>
command after a configuration change.
+ <b>bounce</b><i>_</i><b>notice</b><i>_</i><b>recipient</b>
+ The recipient of single bounce postmaster notices.
BOUNCE(8) BOUNCE(8)
- <b>bounce</b><i>_</i><b>notice</b><i>_</i><b>recipient</b>
- The recipient of single bounce postmaster notices.
-
<b>2bounce</b><i>_</i><b>notice</b><i>_</i><b>recipient</b>
- The recipient of double bounce postmaster notices.
+ The recipient of double bounce postmaster notices.
<b>delay</b><i>_</i><b>notice</b><i>_</i><b>recipient</b>
The recipient of "delayed mail" postmaster notices.
<b>bounce</b><i>_</i><b>size</b><i>_</i><b>limit</b>
- Limit the amount of original message context that
+ Limit the amount of original message context that
is sent in a non-delivery notification.
<b>mail</b><i>_</i><b>name</b>
- Use this mail system name in the introductory text
+ Use this mail system name in the introductory text
at the start of a bounce message.
<b>notify</b><i>_</i><b>classes</b>
- Notify the postmaster of bounced mail when this
- parameter includes the <b>bounce</b> class. For privacy
+ Notify the postmaster of bounced mail when this
+ parameter includes the <b>bounce</b> class. For privacy
reasons, the message body is not included.
<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>
+
+
+
Post a bounce message, with a copy of a log file and of the
corresponding message. When the bounce is posted successfully,
the log file is deleted.
-.IP \(bu
-Post a bounce message without accessing a per-message log file.
.PP
The software does a best effort to notify the sender that there
was a problem. A notification is sent even when the log file
.na
.nf
RFC 822 (ARPA Internet Text Messages)
+RFC 1894 (Delivery Status Notifications)
.SH DIAGNOSTICS
.ad
.fi
SHELL = /bin/sh
SRCS = bounce.c bounce_append_service.c bounce_notify_service.c \
- bounce_recip_service.c bounce_cleanup.c
+ bounce_cleanup.c bounce_notify_util.c
OBJS = bounce.o bounce_append_service.o bounce_notify_service.o \
- bounce_recip_service.o bounce_cleanup.o
+ bounce_cleanup.o bounce_notify_util.o
HDRS =
TESTSRC =
WARN = -W -Wformat -Wimplicit -Wmissing-prototypes \
bounce.o: ../../include/bounce.h
bounce.o: ../../include/mail_server.h
bounce.o: bounce_service.h
+bounce.o: ../../include/bounce_log.h
bounce_append_service.o: bounce_append_service.c
bounce_append_service.o: ../../include/sys_defs.h
bounce_append_service.o: ../../include/msg.h
bounce_append_service.o: ../../include/quote_822_local.h
bounce_append_service.o: ../../include/deliver_flock.h
bounce_append_service.o: bounce_service.h
+bounce_append_service.o: ../../include/bounce_log.h
bounce_cleanup.o: bounce_cleanup.c
bounce_cleanup.o: ../../include/sys_defs.h
bounce_cleanup.o: ../../include/msg.h
bounce_cleanup.o: ../../include/mail_queue.h
bounce_cleanup.o: ../../include/vstream.h
bounce_cleanup.o: bounce_service.h
+bounce_cleanup.o: ../../include/bounce_log.h
bounce_notify_service.o: bounce_notify_service.c
bounce_notify_service.o: ../../include/sys_defs.h
bounce_notify_service.o: ../../include/msg.h
-bounce_notify_service.o: ../../include/vstring.h
-bounce_notify_service.o: ../../include/vbuf.h
bounce_notify_service.o: ../../include/vstream.h
-bounce_notify_service.o: ../../include/vstring_vstream.h
-bounce_notify_service.o: ../../include/mymalloc.h
-bounce_notify_service.o: ../../include/stringops.h
-bounce_notify_service.o: ../../include/events.h
-bounce_notify_service.o: ../../include/line_wrap.h
+bounce_notify_service.o: ../../include/vbuf.h
bounce_notify_service.o: ../../include/name_mask.h
-bounce_notify_service.o: ../../include/mail_queue.h
-bounce_notify_service.o: ../../include/mail_proto.h
-bounce_notify_service.o: ../../include/iostuff.h
-bounce_notify_service.o: ../../include/quote_822_local.h
bounce_notify_service.o: ../../include/mail_params.h
-bounce_notify_service.o: ../../include/canon_addr.h
-bounce_notify_service.o: ../../include/is_header.h
-bounce_notify_service.o: ../../include/record.h
-bounce_notify_service.o: ../../include/rec_type.h
-bounce_notify_service.o: ../../include/mail_conf.h
bounce_notify_service.o: ../../include/post_mail.h
bounce_notify_service.o: ../../include/cleanup_user.h
bounce_notify_service.o: ../../include/mail_addr.h
-bounce_notify_service.o: ../../include/mark_corrupt.h
bounce_notify_service.o: ../../include/mail_error.h
bounce_notify_service.o: bounce_service.h
-bounce_recip_service.o: bounce_recip_service.c
-bounce_recip_service.o: ../../include/sys_defs.h
-bounce_recip_service.o: ../../include/msg.h
-bounce_recip_service.o: ../../include/vstring.h
-bounce_recip_service.o: ../../include/vbuf.h
-bounce_recip_service.o: ../../include/vstream.h
-bounce_recip_service.o: ../../include/vstring_vstream.h
-bounce_recip_service.o: ../../include/mymalloc.h
-bounce_recip_service.o: ../../include/stringops.h
-bounce_recip_service.o: ../../include/events.h
-bounce_recip_service.o: ../../include/line_wrap.h
-bounce_recip_service.o: ../../include/name_mask.h
-bounce_recip_service.o: ../../include/mail_queue.h
-bounce_recip_service.o: ../../include/mail_proto.h
-bounce_recip_service.o: ../../include/iostuff.h
-bounce_recip_service.o: ../../include/quote_822_local.h
-bounce_recip_service.o: ../../include/mail_params.h
-bounce_recip_service.o: ../../include/canon_addr.h
-bounce_recip_service.o: ../../include/is_header.h
-bounce_recip_service.o: ../../include/record.h
-bounce_recip_service.o: ../../include/rec_type.h
-bounce_recip_service.o: ../../include/mail_conf.h
-bounce_recip_service.o: ../../include/post_mail.h
-bounce_recip_service.o: ../../include/cleanup_user.h
-bounce_recip_service.o: ../../include/mail_addr.h
-bounce_recip_service.o: ../../include/mark_corrupt.h
-bounce_recip_service.o: ../../include/mail_error.h
-bounce_recip_service.o: bounce_service.h
+bounce_notify_service.o: ../../include/vstring.h
+bounce_notify_service.o: ../../include/bounce_log.h
+bounce_notify_util.o: bounce_notify_util.c
+bounce_notify_util.o: ../../include/sys_defs.h
+bounce_notify_util.o: ../../include/msg.h
+bounce_notify_util.o: ../../include/mymalloc.h
+bounce_notify_util.o: ../../include/events.h
+bounce_notify_util.o: ../../include/vstring.h
+bounce_notify_util.o: ../../include/vbuf.h
+bounce_notify_util.o: ../../include/vstream.h
+bounce_notify_util.o: ../../include/line_wrap.h
+bounce_notify_util.o: ../../include/mail_queue.h
+bounce_notify_util.o: ../../include/quote_822_local.h
+bounce_notify_util.o: ../../include/mail_params.h
+bounce_notify_util.o: ../../include/is_header.h
+bounce_notify_util.o: ../../include/record.h
+bounce_notify_util.o: ../../include/rec_type.h
+bounce_notify_util.o: ../../include/post_mail.h
+bounce_notify_util.o: ../../include/cleanup_user.h
+bounce_notify_util.o: ../../include/mail_addr.h
+bounce_notify_util.o: ../../include/mail_error.h
+bounce_notify_util.o: ../../include/name_mask.h
+bounce_notify_util.o: ../../include/bounce_log.h
+bounce_notify_util.o: ../../include/mail_date.h
+bounce_notify_util.o: bounce_service.h
/* Post a bounce message, with a copy of a log file and of the
/* corresponding message. When the bounce is posted successfully,
/* the log file is deleted.
-/* .IP \(bu
-/* Post a bounce message without accessing a per-message log file.
/* .PP
/* The software does a best effort to notify the sender that there
/* was a problem. A notification is sent even when the log file
/* themselves, and that depend on retry logic in their own client.
/* STANDARDS
/* RFC 822 (ARPA Internet Text Messages)
+/* RFC 1894 (Delivery Status Notifications)
/* DIAGNOSTICS
/* Problems and transactions are logged to \fBsyslogd\fR(8).
/* BUGS
STR(queue_id), STR(sender), flush));
}
-/* bounce_recip_proto - bounce_recip server protocol */
-
-static int bounce_recip_proto(char *service_name, VSTREAM *client, int flush)
-{
- int flags;
-
- /*
- * Read and validate the client request.
- */
- if (mail_command_read(client, "%d %s %s %s %s %s",
- &flags, queue_name, queue_id,
- sender, recipient, why) != 6) {
- msg_warn("malformed request");
- return (-1);
- }
- if (mail_queue_name_ok(STR(queue_name)) == 0) {
- msg_warn("malformed queue name: %s", printable(STR(queue_name), '?'));
- return (-1);
- }
- if (mail_queue_id_ok(STR(queue_id)) == 0) {
- msg_warn("malformed queue id: %s", printable(STR(queue_id), '?'));
- return (-1);
- }
- if (msg_verbose)
- msg_info("bounce_recip_proto: service=%s queue=%s id=%s sender=%s recip=%s why=%s",
- service_name, STR(queue_name), STR(queue_id),
- STR(sender), STR(recipient), STR(why));
-
- /*
- * Execute the request.
- */
- return (bounce_recip_service(service_name, STR(queue_name), STR(queue_id),
- STR(sender), STR(recipient), STR(why), flush));
-}
-
/* 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_RECIP) {
- status = bounce_recip_proto(service_name, client, REALLY_BOUNCE);
} else {
msg_warn("unknown command: %d", command);
status = -1;
/* Utility library. */
#include <msg.h>
-#include <vstring.h>
#include <vstream.h>
-#include <vstring_vstream.h>
-#include <mymalloc.h>
-#include <stringops.h>
-#include <events.h>
-#include <line_wrap.h>
#include <name_mask.h>
/* Global library. */
-#include <mail_queue.h>
-#include <mail_proto.h>
-#include <quote_822_local.h>
#include <mail_params.h>
-#include <canon_addr.h>
-#include <is_header.h>
-#include <record.h>
-#include <rec_type.h>
-#include <mail_conf.h>
+#include <mail_queue.h>
#include <post_mail.h>
#include <mail_addr.h>
-#include <mark_corrupt.h>
#include <mail_error.h>
/* Application-specific. */
#define STR vstring_str
-/* bounce_header - generate bounce message header */
-
-static int bounce_header(VSTREAM *bounce, VSTRING *buf, const char *dest,
- const char *boundary, int flush)
-{
-
- /*
- * Print a minimal bounce header. The cleanup service will add other
- * headers and will make all addresses fully qualified.
- */
-#define STREQ(a, b) (strcasecmp((a), (b)) == 0)
-
- post_mail_fprintf(bounce, "From: %s (Mail Delivery System)",
- MAIL_ADDR_MAIL_DAEMON);
-
- if (flush) {
- post_mail_fputs(bounce, dest == var_bounce_rcpt
- || dest == var_2bounce_rcpt || dest == var_delay_rcpt ?
- "Subject: Postmaster Copy: Undelivered Mail" :
- "Subject: Undelivered Mail Returned to Sender");
- } else {
- post_mail_fputs(bounce, dest == var_bounce_rcpt
- || dest == var_2bounce_rcpt || dest == var_delay_rcpt ?
- "Subject: Postmaster Warning: Delayed Mail" :
- "Subject: Delayed Mail (still being retried)");
- }
- post_mail_fprintf(bounce, "To: %s", STR(quote_822_local(buf, dest)));
-
- /*
- * MIME header.
- */
- post_mail_fprintf(bounce, "MIME-Version: 1.0");
-#ifdef DSN
- post_mail_fprintf(bounce, "Content-Type: %s; report-type=%s;",
- "multipart/report", "delivery-status");
-#else
- post_mail_fprintf(bounce, "Content-Type: multipart/mixed;");
-#endif
- post_mail_fprintf(bounce, "\tboundary=\"%s\"", boundary);
- post_mail_fputs(bounce, "");
- post_mail_fputs(bounce, "This is a MIME-encapsulated message.");
-
- /*
- * More MIME header.
- */
- post_mail_fputs(bounce, "");
- post_mail_fprintf(bounce, "--%s", boundary);
- post_mail_fprintf(bounce, "Content-Description: %s", "Notification");
- post_mail_fprintf(bounce, "Content-Type: %s", "text/plain");
- post_mail_fputs(bounce, "");
-
- return (vstream_ferror(bounce));
-}
-
-/* bounce_boilerplate - generate boiler-plate text */
-
-static int bounce_boilerplate(VSTREAM *bounce, VSTRING *buf,
- const char *boundary, int flush)
-{
-
- /*
- * Print the message body with the problem report. XXX For now, we use a
- * fixed bounce template. We could use a site-specific parametrized
- * template with ${name} macros and we could do wonderful things such as
- * word wrapping to make the text look nicer. No matter how hard we would
- * try, receiving bounced mail will always suck.
- */
- post_mail_fprintf(bounce, "This is the %s program at host %s.",
- var_mail_name, var_myhostname);
- post_mail_fputs(bounce, "");
- if (flush) {
- post_mail_fputs(bounce,
- "I'm sorry to have to inform you that the message returned");
- post_mail_fputs(bounce,
- "below could not be delivered to one or more destinations.");
- } else {
- post_mail_fputs(bounce,
- "####################################################################");
- post_mail_fputs(bounce,
- "# THIS IS A WARNING ONLY. YOU DO NOT NEED TO RESEND YOUR MESSAGE. #");
- post_mail_fputs(bounce,
- "####################################################################");
- post_mail_fputs(bounce, "");
- post_mail_fprintf(bounce,
- "Your message could not be delivered for %d hours.",
- var_delay_warn_time);
- post_mail_fprintf(bounce,
- "It will be retried until it is %d days old.",
- var_max_queue_time);
- }
-
- post_mail_fputs(bounce, "");
- post_mail_fprintf(bounce,
- "For further assistance, please contact <%s>",
- STR(canon_addr_external(buf, MAIL_ADDR_POSTMASTER)));
- if (flush) {
- post_mail_fputs(bounce, "");
- post_mail_fprintf(bounce,
- "If you do so, please include this problem report. You can");
- post_mail_fprintf(bounce,
- "delete your own text from the message returned below.");
- }
- post_mail_fputs(bounce, "");
- post_mail_fprintf(bounce, "\t\t\tThe %s program", var_mail_name);
- post_mail_fputs(bounce, "");
- return (vstream_ferror(bounce));
-}
-
-/* bounce_print - line_wrap callback */
-
-static void bounce_print(const char *str, int len, int indent, char *context)
-{
- VSTREAM *bounce = (VSTREAM *) context;
-
- post_mail_fprintf(bounce, "%*s%.*s", indent, "", len, str);
-}
-
-/* bounce_diagnostics - send bounce log report */
-
-static int bounce_diagnostics(char *service, VSTREAM *bounce, VSTRING *buf,
- char *queue_id, const char *boundary)
-{
- VSTREAM *log;
-
- /*
- * MIME header.
- */
-#ifdef DSN
- post_mail_fputs(bounce, "");
- post_mail_fprintf(bounce, "--%s", boundary);
- post_mail_fprintf(bounce, "Content-Description: %s", "Delivery error report");
- post_mail_fprintf(bounce, "Content-Type: %s", "message/delivery-status");
- post_mail_fputs(bounce, "");
-#endif
-
- /*
- * 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 = mail_queue_open(service, queue_id, O_RDONLY, 0)) == 0) {
- if (errno != ENOENT)
- msg_fatal("open %s %s: %m", service, queue_id);
- post_mail_fputs(bounce, "\t--- Delivery error report unavailable ---");
- post_mail_fputs(bounce, "");
- }
-
- /*
- * Append a copy of the delivery error log. Again, we're doing a best
- * effort, so there is no point raising a fatal run-time error in case of
- * a logfile read error. Wrap long lines, filter non-printable
- * characters, and prepend one blank, so this data can safely be piped
- * into other programs.
- */
- else {
-
-#define LENGTH 79
-#define INDENT 4
- while (vstream_ferror(bounce) == 0 && vstring_fgets_nonl(buf, log)) {
- printable(STR(buf), '_');
- line_wrap(STR(buf), LENGTH, INDENT, bounce_print, (char *) bounce);
- if (vstream_ferror(bounce) != 0)
- break;
- }
- if (vstream_fclose(log))
- msg_warn("read bounce log %s: %m", queue_id);
- }
- return (vstream_ferror(bounce));
-}
-
-/* bounce_original - send a copy of the original to the victim */
-
-static int bounce_original(char *service, VSTREAM *bounce, VSTRING *buf,
- char *queue_name, char *queue_id,
- const char *boundary, int headers_only)
-{
- int status = 0;
- VSTREAM *src;
- int rec_type;
- int bounce_length;
-
- /*
- * MIME headers.
- */
- post_mail_fputs(bounce, "");
- post_mail_fprintf(bounce, "--%s", boundary);
- post_mail_fprintf(bounce, "Content-Description: %s", "Undelivered Message");
- post_mail_fprintf(bounce, "Content-Type: %s", headers_only ?
- "text/rfc822-headers" : "message/rfc822");
- post_mail_fputs(bounce, "");
-
- /*
- * 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
- * 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 ((src = mail_queue_open(queue_name, queue_id, O_RDONLY, 0)) == 0) {
- if (errno != ENOENT)
- msg_fatal("open %s %s: %m", service, queue_id);
- post_mail_fputs(bounce, "\t--- Undelivered message unavailable ---");
- post_mail_fputs(bounce, "");
- return (vstream_ferror(bounce));
- }
-
- /*
- * Skip over the original message envelope records. If the envelope is
- * corrupted just send whatever we can (remember this is a best effort,
- * it does not have to be perfect).
- */
- while ((rec_type = rec_get(src, buf, 0)) > 0)
- if (rec_type == REC_TYPE_MESG)
- break;
-
- /*
- * Copy the original message contents. Limit the amount of bounced text
- * so there is a better chance of the bounce making it back. We're doing
- * raw record output here so that we don't throw away binary transparency
- * yet.
- */
-#define IS_HEADER(s) (ISSPACE(*(s)) || is_header(s))
-
- bounce_length = 0;
- while (status == 0 && (rec_type = rec_get(src, buf, 0)) > 0) {
- if (rec_type != REC_TYPE_NORM && rec_type != REC_TYPE_CONT)
- break;
- if (headers_only && !IS_HEADER(vstring_str(buf)))
- break;
- if (var_bounce_limit == 0 || bounce_length < var_bounce_limit) {
- bounce_length += VSTRING_LEN(buf);
- status = (REC_PUT_BUF(bounce, rec_type, buf) != rec_type);
- }
- }
- post_mail_fputs(bounce, "");
- post_mail_fprintf(bounce, "--%s--", boundary);
- if (headers_only == 0 && rec_type != REC_TYPE_XTRA)
- status |= mark_corrupt(src);
- if (vstream_fclose(src))
- msg_warn("read message file %s %s: %m", queue_name, queue_id);
- return (status);
-}
-
/* bounce_notify_service - send a bounce */
int bounce_notify_service(char *service, char *queue_name,
char *queue_id, char *recipient, int flush)
{
- VSTRING *buf = vstring_alloc(100);
+ BOUNCE_INFO *bounce_info;
int bounce_status = 1;
int postmaster_status = 1;
VSTREAM *bounce;
int notify_mask = name_mask(mail_error_masks, var_notify_classes);
- VSTRING *boundary = vstring_alloc(100);
char *postmaster;
/*
- * Unique string for multi-part message boundaries.
+ * Initialize. Open queue file, bounce log, etc.
*/
- vstring_sprintf(boundary, "%s.%ld/%s",
- queue_id, (long) event_time(), var_myhostname);
+ bounce_info = bounce_mail_init(service, queue_name, queue_id, flush);
#define NULL_SENDER MAIL_ADDR_EMPTY /* special address */
#define NULL_CLEANUP_FLAGS 0
*/
if (strcasecmp(recipient, mail_addr_double_bounce()) == 0) {
msg_warn("%s: undeliverable postmaster notification discarded",
- queue_id);
+ queue_id);
bounce_status = 0;
}
* reason for the bounce, and the headers of the original
* message. Don't bother sending the boiler-plate text.
*/
- if (!bounce_header(bounce, buf, postmaster,
- STR(boundary), flush)
- && bounce_diagnostics(service, bounce, buf, queue_id,
- STR(boundary)) == 0)
- bounce_original(service, bounce, buf, queue_name, queue_id,
- STR(boundary),
- flush ? BOUNCE_ALL : BOUNCE_HEADERS);
+ if (!bounce_header(bounce, bounce_info, postmaster)
+ && bounce_diagnostic_log(bounce, bounce_info) == 0
+ && bounce_header_dsn(bounce, bounce_info) == 0
+ && bounce_diagnostic_dsn(bounce, bounce_info) == 0)
+ bounce_original(bounce, bounce_info, flush ?
+ BOUNCE_ALL : BOUNCE_HEADERS);
bounce_status = post_mail_fclose(bounce);
}
}
*/
else {
if ((bounce = post_mail_fopen_nowait(NULL_SENDER, recipient,
- NULL_CLEANUP_FLAGS,
- "BOUNCE")) != 0) {
+ NULL_CLEANUP_FLAGS,
+ "BOUNCE")) != 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, buf, recipient, STR(boundary), flush) == 0
- && bounce_boilerplate(bounce, buf, STR(boundary), flush) == 0
- && bounce_diagnostics(service, bounce, buf, queue_id,
- STR(boundary)) == 0)
- bounce_original(service, bounce, buf, queue_name, queue_id,
- STR(boundary),
- flush ? BOUNCE_ALL : BOUNCE_HEADERS);
+ if (bounce_header(bounce, bounce_info, recipient) == 0
+ && bounce_boilerplate(bounce, bounce_info) == 0
+ && bounce_diagnostic_log(bounce, bounce_info) == 0
+ && bounce_header_dsn(bounce, bounce_info) == 0
+ && bounce_diagnostic_dsn(bounce, bounce_info) == 0)
+ bounce_original(bounce, bounce_info, flush ?
+ BOUNCE_ALL : BOUNCE_HEADERS);
bounce_status = post_mail_fclose(bounce);
}
postmaster,
NULL_CLEANUP_FLAGS,
"BOUNCE")) != 0) {
- if (!bounce_header(bounce, buf, postmaster,
- STR(boundary), flush)
- && bounce_diagnostics(service, bounce, buf,
- queue_id, STR(boundary)) == 0)
- bounce_original(service, bounce, buf, queue_name, queue_id,
- STR(boundary), BOUNCE_HEADERS);
+ if (bounce_header(bounce, bounce_info, postmaster) == 0
+ && bounce_diagnostic_log(bounce, bounce_info) == 0
+ && bounce_header_dsn(bounce, bounce_info) == 0
+ && bounce_diagnostic_dsn(bounce, bounce_info) == 0)
+ bounce_original(bounce, bounce_info, BOUNCE_HEADERS);
postmaster_status = post_mail_fclose(bounce);
}
if (postmaster_status)
/*
* Cleanup.
*/
- vstring_free(buf);
- vstring_free(boundary);
+ bounce_mail_free(bounce_info);
return (bounce_status);
}
--- /dev/null
+/*++
+/* NAME
+/* bounce_notify_util 3
+/* SUMMARY
+/* send non-delivery report to sender, server side
+/* SYNOPSIS
+/* #include "bounce_service.h"
+/*
+/* typedef struct {
+/* .in +4
+/* /* All private members... */
+/* .in -4
+/* } BOUNCE_INFO;
+/*
+/* BOUNCE_INFO *bounce_mail_init(service, queue_name, queue_id, flush)
+/* const char *service;
+/* const char *queue_name;
+/* const char *queue_id;
+/* int flush;
+/*
+/* void bounce_mail_free(bounce_info)
+/* BOUNCE_INFO *bounce_info;
+/*
+/* int bounce_header(fp, bounce_info, recipient)
+/* VSTREAM *fp;
+/* BOUNCE_INFO *bounce_info;
+/* const char *recipient;
+/*
+/* int bounce_boilerplate(fp, bounce_info)
+/* VSTREAM *fp;
+/* BOUNCE_INFO *bounce_info;
+/*
+/* int bounce_recipient_log(fp, bounce_info)
+/* VSTREAM *fp;
+/* BOUNCE_INFO *bounce_info;
+/*
+/* int bounce_diagnostic_log(fp, bounce_info)
+/* VSTREAM *fp;
+/* BOUNCE_INFO *bounce_info;
+/*
+/* int bounce_header_dsn(fp, bounce_info)
+/* VSTREAM *fp;
+/* BOUNCE_INFO *bounce_info;
+/*
+/* int bounce_recipient_dsn(fp, bounce_info)
+/* VSTREAM *fp;
+/* BOUNCE_INFO *bounce_info;
+/*
+/* int bounce_diagnostic_dsn(fp, bounce_info)
+/* VSTREAM *fp;
+/* BOUNCE_INFO *bounce_info;
+/*
+/* int bounce_original(fp, bounce_info, headers_only)
+/* VSTREAM *fp;
+/* BOUNCE_INFO *bounce_info;
+/* int headers_only;
+/* DESCRIPTION
+/* This module implements the grunt work of sending a non-delivery
+/* notification. A bounce is sent in a form that satisfies RFC 1894
+/* (delivery status notifications).
+/*
+/* bounce_mail_init() bundles up its argument and attempts to
+/* open the corresponding logfile and message file. A BOUNCE_INFO
+/* structure contains all the necessary information about an
+/* undeliverable message.
+/*
+/* bounce_mail_free() releases memory allocated by bounce_mail_init()
+/* and closes any files opened by bounce_mail_init().
+/*
+/* bounce_header() produces a standard mail header with the specified
+/* recipient and starts a text/plain message segment for the
+/* human-readable problem description.
+/*
+/* bounce_boilerplate() produces the standard "sorry" text that
+/* creates the illusion that mail systems are civilized.
+/*
+/* bounce_recipient_log() sends a human-readable representation of
+/* logfile information for one recipient, with the recipient address
+/* and with the text why the recipient was undeliverable.
+/*
+/* bounce_diagnostic_log() sends a human-readable representation of
+/* logfile information for all undeliverable recipients. This routine
+/* will become obsolete when individual recipients of the same message
+/* can have different sender addresses to bounce to.
+/*
+/* bounce_header_dsn() starts a message/delivery-status message
+/* segment and sends the machine-readable information that identifies
+/* the reporting MTA.
+/*
+/* bounce_recipient_dsn() sends a machine-readable representation of
+/* logfile information for one recipient, with the recipient address
+/* and with the text why the recipient was undeliverable.
+/*
+/* bounce_diagnostic_dsn() sends a machine-readable representation of
+/* logfile information for all undeliverable recipients. This routine
+/* will become obsolete when individual recipients of the same message
+/* can have different sender addresses to bounce to.
+/*
+/* bounce_original() starts a message/rfc822 or headers/rfc822
+/* message segment and sends the original message, either full or
+/* message headers only.
+/* 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 <stdlib.h>
+#include <unistd.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 <mymalloc.h>
+#include <events.h>
+#include <vstring.h>
+#include <vstream.h>
+#include <line_wrap.h>
+
+/* Global library. */
+
+#include <mail_queue.h>
+#include <quote_822_local.h>
+#include <mail_params.h>
+#include <is_header.h>
+#include <record.h>
+#include <rec_type.h>
+#include <post_mail.h>
+#include <mail_addr.h>
+#include <mail_error.h>
+#include <bounce_log.h>
+#include <mail_date.h>
+
+/* Application-specific. */
+
+#include "bounce_service.h"
+
+#define STR vstring_str
+
+/* bounce_mail_init - initialize */
+
+BOUNCE_INFO *bounce_mail_init(const char *service, const char *queue_name,
+ const char *queue_id, int flush)
+{
+ BOUNCE_INFO *bounce_info;
+ int rec_type;
+
+ /*
+ * Bundle up a bunch of parameters and initialize information. that will
+ * be discovered on the fly.
+ */
+ bounce_info = (BOUNCE_INFO *) mymalloc(sizeof(*bounce_info));
+ bounce_info->service = service;
+ bounce_info->queue_name = queue_name;
+ bounce_info->queue_id = queue_id;
+ bounce_info->flush = flush;
+ bounce_info->buf = vstring_alloc(100);
+ bounce_info->arrival_time = 0;
+ bounce_info->orig_offs = 0;
+
+ /*
+ * Compute a supposedly unique boundary string. This assumes that a queue
+ * ID and a hostname contain acceptable characters for a boundary string,
+ * but the assumption is not verified.
+ */
+ vstring_sprintf(bounce_info->buf, "%s.%lu/%s",
+ 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_RDONLY, 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
+ * 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->orig_fp = mail_queue_open(queue_name, queue_id,
+ O_RDONLY, 0)) == 0
+ && errno != ENOENT)
+ msg_fatal("open %s %s: %m", service, queue_id);
+
+ /*
+ * Skip over the original message envelope records. If the envelope is
+ * corrupted just send whatever we can (remember this is a best effort,
+ * it does not have to be perfect).
+ */
+ while ((rec_type = rec_get(bounce_info->orig_fp,
+ bounce_info->buf, 0)) > 0) {
+ if (rec_type == REC_TYPE_TIME && bounce_info->arrival_time == 0) {
+ if ((bounce_info->arrival_time = atol(STR(bounce_info->buf))) < 0)
+ bounce_info->arrival_time = 0;
+ } else if (rec_type == REC_TYPE_MESG) {
+ bounce_info->orig_offs = vstream_ftell(bounce_info->orig_fp);
+ break;
+ }
+ }
+ return (bounce_info);
+}
+
+/* bounce_mail_free - undo bounce_mail_init */
+
+void bounce_mail_free(BOUNCE_INFO *bounce_info)
+{
+ if (bounce_info->log_handle && bounce_log_close(bounce_info->log_handle))
+ msg_warn("%s: read bounce log %s: %m",
+ bounce_info->queue_id, bounce_info->queue_id);
+ if (bounce_info->orig_fp && vstream_fclose(bounce_info->orig_fp))
+ msg_warn("%s: read message file %s %s: %m",
+ bounce_info->queue_id, bounce_info->queue_name,
+ bounce_info->queue_id);
+ vstring_free(bounce_info->buf);
+ myfree((char *) bounce_info->mime_boundary);
+ myfree((char *) bounce_info);
+}
+
+/* bounce_header - generate bounce message header */
+
+int bounce_header(VSTREAM *bounce, BOUNCE_INFO *bounce_info,
+ const char *dest)
+{
+
+ /*
+ * Print a minimal bounce header. The cleanup service will add other
+ * headers and will make all addresses fully qualified.
+ */
+#define STREQ(a, b) (strcasecmp((a), (b)) == 0)
+
+ post_mail_fprintf(bounce, "From: %s (Mail Delivery System)",
+ MAIL_ADDR_MAIL_DAEMON);
+
+ /*
+ * Non-delivery subject line.
+ */
+ if (bounce_info->flush) {
+ post_mail_fputs(bounce, dest == var_bounce_rcpt
+ || dest == var_2bounce_rcpt || dest == var_delay_rcpt ?
+ "Subject: Postmaster Copy: Undelivered Mail" :
+ "Subject: Undelivered Mail Returned to Sender");
+ }
+
+ /*
+ * Delayed mail subject line.
+ */
+ else {
+ post_mail_fputs(bounce, dest == var_bounce_rcpt
+ || dest == var_2bounce_rcpt || dest == var_delay_rcpt ?
+ "Subject: Postmaster Warning: Delayed Mail" :
+ "Subject: Delayed Mail (still being retried)");
+ }
+ post_mail_fprintf(bounce, "To: %s",
+ STR(quote_822_local(bounce_info->buf, dest)));
+
+ /*
+ * MIME header.
+ */
+ post_mail_fprintf(bounce, "MIME-Version: 1.0");
+ post_mail_fprintf(bounce, "Content-Type: %s; report-type=%s;",
+ "multipart/report", "delivery-status");
+ post_mail_fprintf(bounce, "\tboundary=\"%s\"", bounce_info->mime_boundary);
+ post_mail_fputs(bounce, "");
+ post_mail_fputs(bounce, "This is a MIME-encapsulated message.");
+
+ /*
+ * MIME header.
+ */
+ post_mail_fputs(bounce, "");
+ post_mail_fprintf(bounce, "--%s", bounce_info->mime_boundary);
+ post_mail_fprintf(bounce, "Content-Description: %s", "Notification");
+ post_mail_fprintf(bounce, "Content-Type: %s", "text/plain");
+ post_mail_fputs(bounce, "");
+
+ return (vstream_ferror(bounce));
+}
+
+/* bounce_boilerplate - generate boiler-plate text */
+
+int bounce_boilerplate(VSTREAM *bounce, BOUNCE_INFO *bounce_info)
+{
+
+ /*
+ * Print the message body with the problem report. XXX For now, we use a
+ * fixed bounce template. We could use a site-specific parametrized
+ * template with ${name} macros and we could do wonderful things such as
+ * word wrapping to make the text look nicer. No matter how hard we would
+ * try, receiving bounced mail will always suck.
+ */
+ post_mail_fprintf(bounce, "This is the %s program at host %s.",
+ var_mail_name, var_myhostname);
+ post_mail_fputs(bounce, "");
+ if (bounce_info->flush) {
+ post_mail_fputs(bounce,
+ "I'm sorry to have to inform you that the message returned");
+ post_mail_fputs(bounce,
+ "below could not be delivered to one or more destinations.");
+ } else {
+ post_mail_fputs(bounce,
+ "####################################################################");
+ post_mail_fputs(bounce,
+ "# THIS IS A WARNING ONLY. YOU DO NOT NEED TO RESEND YOUR MESSAGE. #");
+ post_mail_fputs(bounce,
+ "####################################################################");
+ post_mail_fputs(bounce, "");
+ post_mail_fprintf(bounce,
+ "Your message could not be delivered for %d hours.",
+ var_delay_warn_time);
+ post_mail_fprintf(bounce,
+ "It will be retried until it is %d days old.",
+ var_max_queue_time);
+ }
+
+ post_mail_fputs(bounce, "");
+ post_mail_fprintf(bounce,
+ "For further assistance, please send mail to <%s>",
+ MAIL_ADDR_POSTMASTER);
+ if (bounce_info->flush) {
+ post_mail_fputs(bounce, "");
+ post_mail_fprintf(bounce,
+ "If you do so, please include this problem report. You can");
+ post_mail_fprintf(bounce,
+ "delete your own text from the message returned below.");
+ }
+ post_mail_fputs(bounce, "");
+ post_mail_fprintf(bounce, "\t\t\tThe %s program", var_mail_name);
+ return (vstream_ferror(bounce));
+}
+
+/* bounce_print - line_wrap callback */
+
+static void bounce_print(const char *str, int len, int indent, char *context)
+{
+ VSTREAM *bounce = (VSTREAM *) context;
+
+ post_mail_fprintf(bounce, "%*s%.*s", indent, "", len, str);
+}
+
+/* bounce_print_wrap - print and wrap a line */
+
+static void bounce_print_wrap(VSTREAM *bounce, BOUNCE_INFO *bounce_info,
+ const char *format,...)
+{
+ va_list ap;
+
+#define LENGTH 79
+#define INDENT 4
+
+ va_start(ap, format);
+ vstring_vsprintf(bounce_info->buf, format, ap);
+ va_end(ap);
+ line_wrap(STR(bounce_info->buf), LENGTH, INDENT,
+ bounce_print, (char *) bounce);
+}
+
+/* bounce_recipient_log - send one bounce log report entry */
+
+int bounce_recipient_log(VSTREAM *bounce, BOUNCE_INFO *bounce_info)
+{
+
+ /*
+ * Mask control and non-ASCII characters (done in bounce_log_read()),
+ * wrap long lines and prepend one blank, so this data can safely be
+ * piped into other programs. Sort of like TCP Wrapper's safe_finger
+ * program.
+ */
+ post_mail_fputs(bounce, "");
+ bounce_print_wrap(bounce, bounce_info, "<%s>: %s",
+ bounce_info->log_handle->recipient, bounce_info->log_handle->text);
+ return (vstream_ferror(bounce));
+}
+
+/* bounce_diagnostic_log - send bounce log report */
+
+int bounce_diagnostic_log(VSTREAM *bounce, BOUNCE_INFO *bounce_info)
+{
+
+ /*
+ * Append a copy of the delivery error log. We're doing a best effort, so
+ * there is no point raising a fatal run-time error in case of a logfile
+ * read error.
+ */
+ if (bounce_info->log_handle == 0
+ || bounce_log_rewind(bounce_info->log_handle)) {
+ post_mail_fputs(bounce, "\t--- Delivery error report unavailable ---");
+ } else {
+ while (bounce_log_read(bounce_info->log_handle) != 0)
+ if (bounce_recipient_log(bounce, bounce_info) != 0)
+ break;
+ }
+ return (vstream_ferror(bounce));
+}
+
+/* bounce_header_dsn - send per-MTA bounce DSN records */
+
+int bounce_header_dsn(VSTREAM *bounce, BOUNCE_INFO *bounce_info)
+{
+
+ /*
+ * MIME header.
+ */
+ post_mail_fputs(bounce, "");
+ post_mail_fprintf(bounce, "--%s", bounce_info->mime_boundary);
+ post_mail_fprintf(bounce, "Content-Description: %s",
+ "Delivery error report");
+ post_mail_fprintf(bounce, "Content-Type: %s", "message/delivery-status");
+
+ /*
+ * According to RFC 1894: The body of a message/delivery-status consists
+ * of one or more "fields" formatted according to the ABNF of RFC 822
+ * header "fields" (see [6]). The per-message fields appear first,
+ * followed by a blank line.
+ */
+ post_mail_fputs(bounce, "");
+ post_mail_fprintf(bounce, "Reporting-MTA: dns; %s", var_myhostname);
+#if 0
+ post_mail_fprintf(bounce, "Received-From-MTA: dns; %s", "whatever");
+#endif
+ if (bounce_info->arrival_time > 0)
+ post_mail_fprintf(bounce, "Arrival-Date: %s",
+ mail_date(bounce_info->arrival_time));
+ return (vstream_ferror(bounce));
+}
+
+/* bounce_recipient_dsn - send per-recipient DSN records */
+
+int bounce_recipient_dsn(VSTREAM *bounce, BOUNCE_INFO *bounce_info)
+{
+ post_mail_fputs(bounce, "");
+#if 0
+ post_mail_fprintf(bounce, "Original-Recipient: rfc822; %s", "whatever");
+#endif
+ post_mail_fprintf(bounce, "Final-Recipient: rfc822; %s",
+ bounce_info->log_handle->recipient);
+ post_mail_fprintf(bounce, "Action: %s", bounce_info->flush ?
+ "failed" : "delayed");
+ post_mail_fprintf(bounce, "Status: %s", bounce_info->log_handle->status);
+ bounce_print_wrap(bounce, bounce_info, "Diagnostic-Code: X-Postfix; %s",
+ bounce_info->log_handle->text);
+#if 0
+ post_mail_fprintf(bounce, "Last-Attempt-Date: %s",
+ bounce_info->log_handle->log_time);
+#endif
+ if (bounce_info->flush == 0)
+ post_mail_fprintf(bounce, "Will-Retry-Until: %s",
+ mail_date(bounce_info->arrival_time + 86400 * var_max_queue_time));
+ return (vstream_ferror(bounce));
+}
+
+/* bounce_diagnostic_dsn - send bounce log report, machine readable form */
+
+int bounce_diagnostic_dsn(VSTREAM *bounce, BOUNCE_INFO *bounce_info)
+{
+
+ /*
+ * Append a copy of the delivery error log. We're doing a best effort, so
+ * there is no point raising a fatal run-time error in case of a logfile
+ * read error.
+ */
+ if (bounce_info->log_handle != 0
+ && bounce_log_rewind(bounce_info->log_handle) == 0) {
+ while (bounce_log_read(bounce_info->log_handle) != 0)
+ if (bounce_recipient_dsn(bounce, bounce_info) != 0)
+ break;
+ }
+ return (vstream_ferror(bounce));
+}
+
+/* bounce_original - send a copy of the original to the victim */
+
+int bounce_original(VSTREAM *bounce, BOUNCE_INFO *bounce_info,
+ int headers_only)
+{
+ int status = 0;
+ int rec_type = 0;
+ int bounce_length;
+
+ /*
+ * MIME headers.
+ */
+ post_mail_fputs(bounce, "");
+ post_mail_fprintf(bounce, "--%s", bounce_info->mime_boundary);
+ post_mail_fprintf(bounce, "Content-Description: %s", headers_only ?
+ "Undelivered Message Headers" : "Undelivered Message");
+ post_mail_fprintf(bounce, "Content-Type: %s", headers_only ?
+ "text/rfc822-headers" : "message/rfc822");
+ post_mail_fputs(bounce, "");
+
+ /*
+ * Send place holder if original is unavailable.
+ */
+ if (bounce_info->orig_offs == 0 || vstream_fseek(bounce_info->orig_fp,
+ bounce_info->orig_offs, SEEK_SET) < 0) {
+ post_mail_fputs(bounce, "\t--- Undelivered message unavailable ---");
+ return (vstream_ferror(bounce));
+ }
+
+ /*
+ * Copy the original message contents. Limit the amount of bounced text
+ * so there is a better chance of the bounce making it back. We're doing
+ * raw record output here so that we don't throw away binary transparency
+ * yet.
+ */
+#define IS_HEADER(s) (ISSPACE(*(s)) || is_header(s))
+
+ bounce_length = 0;
+ while (status == 0 && (rec_type = rec_get(bounce_info->orig_fp, bounce_info->buf, 0)) > 0) {
+ if (rec_type != REC_TYPE_NORM && rec_type != REC_TYPE_CONT)
+ break;
+ if (headers_only && !IS_HEADER(vstring_str(bounce_info->buf)))
+ break;
+ if (var_bounce_limit == 0 || bounce_length < var_bounce_limit) {
+ bounce_length += VSTRING_LEN(bounce_info->buf) + 2;
+ status = (REC_PUT_BUF(bounce, rec_type, bounce_info->buf) != rec_type);
+ }
+ }
+
+ /*
+ * Final MIME headers. These require -- at the end of the boundary
+ * string.
+ */
+ post_mail_fputs(bounce, "");
+ post_mail_fprintf(bounce, "--%s--", bounce_info->mime_boundary);
+
+ return (status);
+}
+++ /dev/null
-/*++
-/* NAME
-/* bounce_recip_service 3
-/* SUMMARY
-/* send non-delivery report to sender, server side
-/* SYNOPSIS
-/* #include "bounce_service.h"
-/*
-/* int bounce_recip_service(queue_name, queue_id, sender,
-/* bounced_addr, why, flush)
-/* char *queue_name;
-/* char *queue_id;
-/* char *sender;
-/* char *recipient;
-/* char *why;
-/* int flush;
-/* DESCRIPTION
-/* This module implements the server side of the bounce_recip()
-/* (send bounce message) request. If flush is zero, a warning is
-/* sent instead of a bounce.
-/*
-/* 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 <vstring.h>
-#include <vstream.h>
-#include <vstring_vstream.h>
-#include <mymalloc.h>
-#include <stringops.h>
-#include <events.h>
-#include <line_wrap.h>
-#include <name_mask.h>
-
-/* Global library. */
-
-#include <mail_queue.h>
-#include <mail_proto.h>
-#include <quote_822_local.h>
-#include <mail_params.h>
-#include <canon_addr.h>
-#include <is_header.h>
-#include <record.h>
-#include <rec_type.h>
-#include <mail_conf.h>
-#include <post_mail.h>
-#include <mail_addr.h>
-#include <mark_corrupt.h>
-#include <mail_error.h>
-
-/* Application-specific. */
-
-#include "bounce_service.h"
-
-#define STR vstring_str
-
-/* bounce_header - generate bounce message header */
-
-static int bounce_header(VSTREAM *bounce, VSTRING *buf, const char *dest,
- const char *boundary, int flush)
-{
-
- /*
- * Print a minimal bounce header. The cleanup service will add other
- * headers and will make all addresses fully qualified.
- */
-#define STREQ(a, b) (strcasecmp((a), (b)) == 0)
-
- post_mail_fprintf(bounce, "From: %s (Mail Delivery System)",
- MAIL_ADDR_MAIL_DAEMON);
-
- if (flush) {
- post_mail_fputs(bounce, dest == var_bounce_rcpt
- || dest == var_2bounce_rcpt || dest == var_delay_rcpt ?
- "Subject: Postmaster Copy: Undelivered Mail" :
- "Subject: Undelivered Mail Returned to Sender");
- } else {
- post_mail_fputs(bounce, dest == var_bounce_rcpt
- || dest == var_2bounce_rcpt || dest == var_delay_rcpt ?
- "Subject: Postmaster Warning: Delayed Mail" :
- "Subject: Delayed Mail (still being retried)");
- }
- post_mail_fprintf(bounce, "To: %s", STR(quote_822_local(buf, dest)));
-
- /*
- * MIME header.
- */
- post_mail_fprintf(bounce, "MIME-Version: 1.0");
-#ifdef DSN
- post_mail_fprintf(bounce, "Content-Type: %s; report-type=%s;",
- "multipart/report", "delivery-status");
-#else
- post_mail_fprintf(bounce, "Content-Type: multipart/mixed;");
-#endif
- post_mail_fprintf(bounce, "\tboundary=\"%s\"", boundary);
- post_mail_fputs(bounce, "");
- post_mail_fputs(bounce, "This is a MIME-encapsulated message.");
-
- /*
- * More MIME header.
- */
- post_mail_fputs(bounce, "");
- post_mail_fprintf(bounce, "--%s", boundary);
- post_mail_fprintf(bounce, "Content-Description: %s", "Notification");
- post_mail_fprintf(bounce, "Content-Type: %s", "text/plain");
- post_mail_fputs(bounce, "");
-
- return (vstream_ferror(bounce));
-}
-
-/* bounce_boilerplate - generate boiler-plate text */
-
-static int bounce_boilerplate(VSTREAM *bounce, VSTRING *buf,
- const char *boundary, int flush)
-{
-
- /*
- * Print the message body with the problem report. XXX For now, we use a
- * fixed bounce template. We could use a site-specific parametrized
- * template with ${name} macros and we could do wonderful things such as
- * word wrapping to make the text look nicer. No matter how hard we would
- * try, receiving bounced mail will always suck.
- */
- post_mail_fprintf(bounce, "This is the %s program at host %s.",
- var_mail_name, var_myhostname);
- post_mail_fputs(bounce, "");
- if (flush) {
- post_mail_fputs(bounce,
- "I'm sorry to have to inform you that the message returned");
- post_mail_fputs(bounce,
- "below could not be delivered to one or more destinations.");
- } else {
- post_mail_fputs(bounce,
- "####################################################################");
- post_mail_fputs(bounce,
- "# THIS IS A WARNING ONLY. YOU DO NOT NEED TO RESEND YOUR MESSAGE. #");
- post_mail_fputs(bounce,
- "####################################################################");
- post_mail_fputs(bounce, "");
- post_mail_fprintf(bounce,
- "Your message could not be delivered for %d hours.",
- var_delay_warn_time);
- post_mail_fprintf(bounce,
- "It will be retried until it is %d days old.",
- var_max_queue_time);
- }
-
- post_mail_fputs(bounce, "");
- post_mail_fprintf(bounce,
- "For further assistance, please contact <%s>",
- STR(canon_addr_external(buf, MAIL_ADDR_POSTMASTER)));
- if (flush) {
- post_mail_fputs(bounce, "");
- post_mail_fprintf(bounce,
- "If you do so, please include this problem report. You can");
- post_mail_fprintf(bounce,
- "delete your own text from the message returned below.");
- }
- post_mail_fputs(bounce, "");
- post_mail_fprintf(bounce, "\t\t\tThe %s program", var_mail_name);
- post_mail_fputs(bounce, "");
- return (vstream_ferror(bounce));
-}
-
-/* bounce_print - line_wrap callback */
-
-static void bounce_print(const char *str, int len, int indent, char *context)
-{
- VSTREAM *bounce = (VSTREAM *) context;
-
- post_mail_fprintf(bounce, "%*s%.*s", indent, "", len, str);
-}
-
-/* bounce_diagnostics - send bounce log report */
-
-static int bounce_diagnostics(VSTREAM *bounce, const char *boundary,
- const char *unused_recipient, char *why)
-{
-
- /*
- * MIME header.
- */
-#ifdef DSN
- post_mail_fputs(bounce, "");
- post_mail_fprintf(bounce, "--%s", boundary);
- post_mail_fprintf(bounce, "Content-Description: %s", "Delivery error report");
- post_mail_fprintf(bounce, "Content-Type: %s", "message/delivery-status");
- post_mail_fputs(bounce, "");
-#endif
-
- /*
- * Append a copy of the delivery error log. Again, we're doing a best
- * effort, so there is no point raising a fatal run-time error in case of
- * a logfile read error. Wrap long lines, filter non-printable
- * characters, and prepend one blank, so this data can safely be piped
- * into other programs.
- *
- * XXX recipient may be empty.
- */
-
-#define LENGTH 79
-#define INDENT 4
- printable(why, '_');
- line_wrap(why, LENGTH, INDENT, bounce_print, (char *) bounce);
- return (vstream_ferror(bounce));
-}
-
-/* bounce_original - send a copy of the original to the victim */
-
-static int bounce_original(char *service, VSTREAM *bounce, VSTRING *buf,
- char *queue_name, char *queue_id,
- const char *boundary, int headers_only)
-{
- int status = 0;
- VSTREAM *src;
- int rec_type;
- int bounce_length;
-
- /*
- * MIME headers.
- */
- post_mail_fputs(bounce, "");
- post_mail_fprintf(bounce, "--%s", boundary);
- post_mail_fprintf(bounce, "Content-Description: %s", "Undelivered Message");
- post_mail_fprintf(bounce, "Content-Type: %s", headers_only ?
- "text/rfc822-headers" : "message/rfc822");
- post_mail_fputs(bounce, "");
-
- /*
- * 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
- * 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 ((src = mail_queue_open(queue_name, queue_id, O_RDONLY, 0)) == 0) {
- if (errno != ENOENT)
- msg_fatal("open %s %s: %m", service, queue_id);
- post_mail_fputs(bounce, "\t--- Undelivered message unavailable ---");
- post_mail_fputs(bounce, "");
- return (vstream_ferror(bounce));
- }
-
- /*
- * Skip over the original message envelope records. If the envelope is
- * corrupted just send whatever we can (remember this is a best effort,
- * it does not have to be perfect).
- */
- while ((rec_type = rec_get(src, buf, 0)) > 0)
- if (rec_type == REC_TYPE_MESG)
- break;
-
- /*
- * Copy the original message contents. Limit the amount of bounced text
- * so there is a better chance of the bounce making it back. We're doing
- * raw record output here so that we don't throw away binary transparency
- * yet.
- */
-#define IS_HEADER(s) (ISSPACE(*(s)) || is_header(s))
-
- bounce_length = 0;
- while (status == 0 && (rec_type = rec_get(src, buf, 0)) > 0) {
- if (rec_type != REC_TYPE_NORM && rec_type != REC_TYPE_CONT)
- break;
- if (headers_only && !IS_HEADER(vstring_str(buf)))
- break;
- if (var_bounce_limit == 0 || bounce_length < var_bounce_limit) {
- bounce_length += VSTRING_LEN(buf);
- status = (REC_PUT_BUF(bounce, rec_type, buf) != rec_type);
- }
- }
- post_mail_fputs(bounce, "");
- post_mail_fprintf(bounce, "--%s--", boundary);
- if (headers_only == 0 && rec_type != REC_TYPE_XTRA)
- status |= mark_corrupt(src);
- if (vstream_fclose(src))
- msg_warn("read message file %s %s: %m", queue_name, queue_id);
- return (status);
-}
-
-/* bounce_recip_service - send a bounce */
-
-int bounce_recip_service(char *service, char *queue_name, char *queue_id,
- char *recipient, char *bounced_addr,
- char *why, int flush)
-{
- VSTRING *buf = vstring_alloc(100);
- int bounce_status = 1;
- int postmaster_status = 1;
- VSTREAM *bounce;
- int notify_mask = name_mask(mail_error_masks, var_notify_classes);
- VSTRING *boundary = vstring_alloc(100);
- char *postmaster;
-
- /*
- * Unique string for multi-part message boundaries.
- */
- vstring_sprintf(boundary, "%s.%ld/%s",
- queue_id, (long) event_time(), var_myhostname);
-
-#define NULL_SENDER MAIL_ADDR_EMPTY /* special address */
-#define NULL_CLEANUP_FLAGS 0
-#define BOUNCE_HEADERS 1
-#define BOUNCE_ALL 0
-
- /*
- * The choice of sender address depends on the recipient 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 recipient 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(recipient, 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 (flush == 1 && (notify_mask & ANY_BOUNCE) == 0)
-#define SKIP_IF_DELAY (flush == 0 && (notify_mask & MAIL_ERROR_DELAY) == 0)
-
- else if (*recipient == 0) {
- if (SKIP_IF_BOUNCE || SKIP_IF_DELAY) {
- bounce_status = 0;
- } else {
- postmaster = flush ? var_2bounce_rcpt : var_delay_rcpt;
- if ((bounce = post_mail_fopen_nowait(mail_addr_double_bounce(),
- postmaster,
- NULL_CLEANUP_FLAGS,
- "BOUNCE")) != 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, buf, postmaster,
- STR(boundary), flush)
- && bounce_diagnostics(bounce, STR(boundary),
- bounced_addr, why) == 0)
- bounce_original(service, bounce, buf, queue_name, queue_id,
- STR(boundary),
- flush ? BOUNCE_ALL : BOUNCE_HEADERS);
- bounce_status = post_mail_fclose(bounce);
- }
- }
- }
-
- /*
- * Non-bounce failed. Send a single bounce.
- */
- else {
- if ((bounce = post_mail_fopen_nowait(NULL_SENDER, recipient,
- NULL_CLEANUP_FLAGS,
- "BOUNCE")) != 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, buf, recipient, STR(boundary), flush) == 0
- && bounce_boilerplate(bounce, buf, STR(boundary), flush) == 0
- && bounce_diagnostics(bounce, STR(boundary),
- bounced_addr, why) == 0)
- bounce_original(service, bounce, buf, queue_name, queue_id,
- STR(boundary),
- flush ? BOUNCE_ALL : BOUNCE_HEADERS);
- 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 (flush == 1 && (notify_mask & MAIL_ERROR_BOUNCE))
-#define WANT_IF_DELAY (flush == 0 && (notify_mask & MAIL_ERROR_DELAY))
-
- if (bounce_status == 0 && (WANT_IF_BOUNCE || WANT_IF_DELAY)
- && strcasecmp(recipient, 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.
- */
- postmaster = flush ? var_bounce_rcpt : var_delay_rcpt;
- if ((bounce = post_mail_fopen_nowait(mail_addr_double_bounce(),
- postmaster,
- NULL_CLEANUP_FLAGS,
- "BOUNCE")) != 0) {
- if (!bounce_header(bounce, buf, postmaster,
- STR(boundary), flush)
- && bounce_diagnostics(bounce, STR(boundary),
- bounced_addr, why) == 0)
- bounce_original(service, bounce, buf, queue_name, queue_id,
- STR(boundary), BOUNCE_HEADERS);
- postmaster_status = post_mail_fclose(bounce);
- }
- if (postmaster_status)
- msg_warn("postmaster notice failed while bouncing to %s",
- recipient);
- }
- }
-
- /*
- * Cleanup.
- */
- vstring_free(buf);
- vstring_free(boundary);
-
- return (bounce_status);
-}
*/
#include <vstring.h>
+ /*
+ * Global library.
+ */
+#include <bounce_log.h>
+
/*
* bounce_append_service.c
*/
*/
extern int bounce_notify_service(char *, char *, char *, char *, int);
- /*
- * bounce_recip_service.c
- */
-extern int bounce_recip_service(char *, char *, char *, char *, char *, char *, int);
-
/*
* bounce_cleanup.c
*/
#define bounce_cleanup_registered() (bounce_cleanup_path != 0)
+ /*
+ * bounce_notify_util.c
+ */
+typedef struct {
+ const char *service; /* bounce or defer */
+ const char *queue_name; /* incoming, etc. */
+ const char *queue_id; /* base name */
+ const char *mime_boundary; /* for MIME */
+ int flush; /* 0=defer, other=bounce */
+ VSTRING *buf; /* scratch pad */
+ VSTREAM *orig_fp; /* open queue file */
+ long orig_offs; /* start of content */
+ time_t arrival_time; /* time of arrival */
+ BOUNCE_LOG *log_handle; /* open logfile */
+} BOUNCE_INFO;
+
+extern BOUNCE_INFO *bounce_mail_init(const char *, const char *, const char *, int);
+extern void bounce_mail_free(BOUNCE_INFO *);
+extern int bounce_header(VSTREAM *, BOUNCE_INFO *, const char *);
+extern int bounce_boilerplate(VSTREAM *, BOUNCE_INFO *);
+extern int bounce_recipient_log(VSTREAM *, BOUNCE_INFO *);
+extern int bounce_diagnostic_log(VSTREAM *, BOUNCE_INFO *);
+extern int bounce_header_dsn(VSTREAM *, BOUNCE_INFO *);
+extern int bounce_recipient_dsn(VSTREAM *, BOUNCE_INFO *);
+extern int bounce_diagnostic_dsn(VSTREAM *, BOUNCE_INFO *);
+extern int bounce_original(VSTREAM *, BOUNCE_INFO *, int);
+
/* LICENSE
/* .ad
/* .fi
if (state->errs & CLEANUP_STAT_LETHAL) {
if (CAN_BOUNCE()) {
- if (bounce_recip(BOUNCE_FLAG_CLEAN,
- MAIL_QUEUE_INCOMING, state->queue_id,
- state->sender, state->recip ?
- state->recip : "unknown", "cleanup", state->time,
- "Message processing aborted: %s",
- cleanup_strerror(state->errs)) == 0) {
+ if (bounce_append(BOUNCE_FLAG_CLEAN, state->queue_id,
+ state->recip ? state->recip : "unknown",
+ "cleanup", state->time,
+ "Message processing aborted: %s",
+ cleanup_strerror(state->errs)) == 0
+ && bounce_flush(BOUNCE_FLAG_CLEAN, MAIL_QUEUE_INCOMING,
+ state->queue_id, state->sender) == 0) {
state->errs = 0;
} else {
msg_warn("%s: bounce message failure", state->queue_id);
record.c remove.c resolve_clnt.c resolve_local.c rewrite_clnt.c \
sent.c smtp_stream.c split_addr.c string_list.c sys_exits.c \
timed_ipc.c tok822_find.c tok822_node.c tok822_parse.c \
- tok822_resolve.c tok822_rewrite.c tok822_tree.c
+ tok822_resolve.c tok822_rewrite.c tok822_tree.c xtext.c bounce_log.c
OBJS = been_here.o bounce.o canon_addr.o cleanup_strerror.o clnt_stream.o \
debug_peer.o debug_process.o defer.o deliver_completed.o \
deliver_flock.o deliver_pass.o deliver_request.o domain_list.o \
record.o remove.o resolve_clnt.o resolve_local.o rewrite_clnt.o \
sent.o smtp_stream.o split_addr.o string_list.o sys_exits.o \
timed_ipc.o tok822_find.o tok822_node.o tok822_parse.o \
- tok822_resolve.o tok822_rewrite.o tok822_tree.o
+ tok822_resolve.o tok822_rewrite.o tok822_tree.o xtext.o bounce_log.o
HDRS = been_here.h bounce.h canon_addr.h cleanup_user.h clnt_stream.h \
config.h debug_peer.h debug_process.h defer.h deliver_completed.h \
deliver_flock.h deliver_pass.h deliver_request.h domain_list.h \
quote_821_local.h quote_822_local.h rec_streamlf.h rec_type.h \
recipient_list.h record.h resolve_clnt.h resolve_local.h \
rewrite_clnt.h sent.h smtp_stream.h split_addr.h string_list.h \
- sys_exits.h timed_ipc.h tok822.h
+ sys_exits.h timed_ipc.h tok822.h xtext.h bounce_log.h
TESTSRC = rec2stream.c stream2rec.c recdump.c
WARN = -W -Wformat -Wimplicit -Wmissing-prototypes \
-Wparentheses -Wstrict-prototypes -Wswitch -Wuninitialized \
bounce.o: ../../include/iostuff.h
bounce.o: defer.h
bounce.o: bounce.h
+bounce_log.o: bounce_log.c
+bounce_log.o: ../../include/sys_defs.h
+bounce_log.o: ../../include/mymalloc.h
+bounce_log.o: ../../include/vstream.h
+bounce_log.o: ../../include/vbuf.h
+bounce_log.o: ../../include/vstring.h
+bounce_log.o: ../../include/vstring_vstream.h
+bounce_log.o: mail_queue.h
+bounce_log.o: bounce_log.h
canon_addr.o: canon_addr.c
canon_addr.o: ../../include/sys_defs.h
canon_addr.o: ../../include/vstring.h
tok822_tree.o: ../../include/vbuf.h
tok822_tree.o: tok822.h
tok822_tree.o: resolve_clnt.h
+xtext.o: xtext.c
+xtext.o: ../../include/sys_defs.h
+xtext.o: ../../include/vstream.h
+xtext.o: ../../include/vbuf.h
+xtext.o: ../../include/vstring.h
+xtext.o: xtext.h
/* const char *queue;
/* const char *id;
/* const char *sender;
-/*
-/* int bounce_recip(flags, queue, id, sender, recipient, relay,
-/* entry, format, ...)
-/* int flags;
-/* const char *queue;
-/* const char *id;
-/* const char *sender;
-/* const char *recipient;
-/* const char *relay;
-/* time_t entry;
-/* const char *format;
-/*
-/* int vbounce_recip(flags, queue, id, sender, recipient, relay,
-/* entry, format, ap)
-/* int flags;
-/* const char *queue;
-/* const char *id;
-/* 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_recip() and vbounce_recipient() send one bounce
-/* message immediately, without accessing a per-message bounce file.
-/*
/* Arguments:
/* .IP flags
/* The bitwise OR of zero or more of the following (specify
return (-1);
}
}
-
-/* bounce_recip - send bounce for one recipient */
-
-int bounce_recip(int flags, const char *queue, const char *id,
- 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_recip(flags, queue, id, sender, recipient, relay,
- entry, fmt, ap);
- va_end(ap);
- return (status);
-}
-
-/* vbounce_recip - send bounce for one recipient */
-
-int vbounce_recip(int flags, const char *queue, const char *id,
- 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 pretending that we can't bounce, don't create a defer log
- * file when we wouldn't keep the bounce log file. That's a lot of
- * negatives in one sentence.
- */
- if (var_soft_bounce && (flags & BOUNCE_FLAG_CLEAN))
- return (-1);
-
- delay = time((time_t *) 0) - entry;
- why = vstring_alloc(100);
- vstring_vsprintf(why, fmt, ap);
- if (mail_command_write(MAIL_CLASS_PRIVATE, var_soft_bounce ?
- MAIL_SERVICE_DEFER : MAIL_SERVICE_BOUNCE,
- "%d %d %s %s %s %s %s", BOUNCE_CMD_RECIP,
- flags, queue, id, sender, recipient,
- vstring_str(why)) == 0) {
- msg_info("%s: to=<%s>, relay=%s, delay=%d, status=%s (%s)",
- id, recipient, relay, delay, var_soft_bounce ? "deferred" :
- "bounced", vstring_str(why));
- status = (var_soft_bounce ? -1 : 0);
- } else if ((flags & BOUNCE_FLAG_CLEAN) == 0) {
- status = defer_append(flags, id, recipient, "bounce", delay,
- "bounce failed");
- } else {
- status = -1;
- }
- vstring_free(why);
- return (status);
-}
#define BOUNCE_CMD_FLUSH 1 /* send log */
#define BOUNCE_CMD_WARN 2 /* send warning bounce, don't delete
* log */
-#define BOUNCE_CMD_RECIP 3 /* immediate bounce, no logfile */
/*
* Flags.
--- /dev/null
+/*++
+/* NAME
+/* bounce_log 3
+/* SUMMARY
+/* bounce file API
+/* SYNOPSIS
+/* #include <bounce_log.h>
+/*
+/* typedef struct {
+/* .in +4
+/* /* public members... */
+/* const char *recipient;
+/* const char *status;
+/* const char *text;
+/* .in -4
+/* } BOUNCE_LOG;
+/*
+/* BOUNCE_LOG *bounce_log_open(queue, id, flags, mode)
+/* const char *queue;
+/* const char *id;
+/* int flags;
+/* int mode;
+/*
+/* BOUNCE_LOG *bounce_log_read(bp)
+/* BOUNCE_LOG *bp;
+/*
+/* void bounce_log_rewind(bp)
+/* BOUNCE_LOG *bp;
+/*
+/* void bounce_log_close(bp)
+/* BOUNCE_LOG *bp;
+/* DESCRIPTION
+/* This module implements a bounce/defer logfile API. Information
+/* is sanitized for control and non-ASCII characters. Currently,
+/* only the reading end is implemented.
+/*
+/* bounce_log_open() opens the named bounce or defer logfile
+/* and returns a handle that must be used for further access.
+/* The result is a null pointer if the file cannot be opened.
+/* The caller is expected to inspect the errno code and deal
+/* with the problem.
+/*
+/* bounce_log_read() reads the next record from the bounce or defer
+/* logfile (skipping over and warning about malformed data)
+/* and breaks out the recipient address, the recipient status
+/* and the text that explains why the recipient was undeliverable.
+/* bounce_log_read() returns a null pointer when no recipient was read,
+/* otherwise it returns its argument.
+/*
+/* bounce_log_rewind() is a helper that seeks to the first recipient
+/* in an open bounce or defer logfile (skipping over recipients that
+/* are marked as done). The result is 0 in case of success, -1 in case
+/* of problems.
+/*
+/* 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.
+/*
+/* Arguments:
+/* .IP queue
+/* The bounce or defer queue name.
+/* .IP id
+/* The message queue id of bounce or defer logfile. This
+/* file has the same name as the original message file.
+/* .IP flags
+/* File open flags, as with open(2).
+/* .IP more
+/* File permissions, as with open(2).
+/* .PP
+/* Results:
+/* .IP recipient
+/* The final recipient address.
+/* .IP text
+/* The text that explains why the recipient was undeliverable.
+/* .IP status
+/* String with DSN compatible status code (digit.digit.digit).
+/* .PP
+/* Other fields will be added as the code evolves.
+/* LICENSE
+/* .ad
+/* .fi
+/* The Secure Mailer license must be distributed with this software.
+/* AUTHOR(S)
+/* Wietse Venema
+/* IBM T.J. Watson Research
+/* P.O. Box 704
+/* Yorktown Heights, NY 10598, USA
+/*--*/
+
+/* System library. */
+
+#include <sys_defs.h>
+#include <string.h>
+#include <ctype.h>
+
+/* Utility library. */
+
+#include <msg.h>
+#include <mymalloc.h>
+#include <vstream.h>
+#include <vstring.h>
+#include <vstring_vstream.h>
+#include <stringops.h>
+
+/* Global library. */
+
+#include <mail_queue.h>
+#include <bounce_log.h>
+
+/* Application-specific. */
+
+#define STR(x) vstring_str(x)
+
+/* 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)
+
+ /*
+ * TODO: peek at the first byte to see if this is an old-style log
+ * (<recipient>: text) or a new-style extensible log with multiple
+ * attributes per recipient.
+ */
+ 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";
+ return (bp);
+ }
+}
+
+/* bounce_log_read - read one record from bounce log file */
+
+BOUNCE_LOG *bounce_log_read(BOUNCE_LOG *bp)
+{
+ char *recipient;
+ char *text;
+ char *cp;
+
+ while (vstring_get_nonl(bp->buf, bp->fp) != VSTREAM_EOF) {
+
+ if (STR(bp->buf)[0] == 0)
+ continue;
+
+ /*
+ * Sanitize.
+ */
+ cp = printable(STR(bp->buf), '?');
+
+ /*
+ * Find the recipient address.
+ */
+ if (*cp != '<') {
+ msg_warn("%s: malformed record: %.30s...",
+ VSTREAM_PATH(bp->fp), cp);
+ continue;
+ }
+ recipient = cp + 1;
+ if ((cp = strstr(recipient, ">: ")) == 0) {
+ msg_warn("%s: malformed record: %.30s...",
+ VSTREAM_PATH(bp->fp), cp);
+ continue;
+ }
+ *cp = 0;
+ bp->recipient = *recipient ? recipient : "(MAILER-DAEMON)";
+
+ /*
+ * Find the text that explains why mail was not deliverable.
+ */
+ text = cp + 2;
+ while (*text && ISSPACE(*text))
+ text++;
+ bp->text = text;
+
+ return (bp);
+ }
+ return (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);
+ myfree((char *) bp);
+ return (ret);
+}
--- /dev/null
+#ifndef _BOUNCE_LOG_H_INCLUDED_
+#define _BOUNCE_LOG_H_INCLUDED_
+
+/*++
+/* NAME
+/* bounce_log 3h
+/* SUMMARY
+/* bounce file reader
+/* SYNOPSIS
+/* #include <bounce_log.h>
+/* DESCRIPTION
+/* .nf
+
+ /*
+ * Utility library.
+ */
+#include <vstream.h>
+#include <vstring.h>
+
+ /*
+ * External interface.
+ */
+typedef struct {
+ /* Private. */
+ VSTREAM *fp; /* open file */
+ VSTRING *buf; /* I/O buffer */
+ /* Public. */
+ const char *recipient; /* final recipient */
+ const char *status; /* recipient status */
+ const char *text; /* why undeliverable */
+} BOUNCE_LOG;
+
+extern BOUNCE_LOG *bounce_log_open(const char *, const char *, int, int);
+extern BOUNCE_LOG *bounce_log_read(BOUNCE_LOG *);
+extern int bounce_log_close(BOUNCE_LOG *);
+
+#define bounce_log_rewind(bp) vstream_fseek((bp)->fp, 0L, SEEK_SET)
+
+/* LICENSE
+/* .ad
+/* .fi
+/* The Secure Mailer license must be distributed with this software.
+/* AUTHOR(S)
+/* Wietse Venema
+/* IBM T.J. Watson Research
+/* P.O. Box 704
+/* Yorktown Heights, NY 10598, USA
+/*--*/
+
+#endif
* Version of this program.
*/
#define VAR_MAIL_VERSION "mail_version"
-#define DEF_MAIL_VERSION "Snapshot-20000923"
+#define DEF_MAIL_VERSION "Snapshot-20000924"
extern char *var_mail_version;
/* LICENSE
--- /dev/null
+/*++
+/* NAME
+/* xtext 3
+/* SUMMARY
+/* translate characters according to RFC 1894
+/* SYNOPSIS
+/* #include <xtext.h>
+/*
+/* VSTRING *xtext(result, original)
+/* VSTRING *result;
+/* const char *original;
+/* DESCRIPTION
+/* xtext() takes a null-terminated string, and produces a translation
+/* according to RFC 1894 (DSN).
+/* BUGS
+/* Cannot replace null characters.
+/*
+/* Does not insert CR LF SPACE to limit output line length.
+/* SEE ALSO
+/* RFC 1894, Delivery Status Notifications
+/* 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 <vstream.h>
+
+/* Utility library. */
+
+#include <vstring.h>
+
+/* Global library. */
+
+#include <xtext.h>
+
+/* xtext - translate text according to RFC 1894 */
+
+VSTRING *xtext(VSTRING *result, const char *original)
+{
+ const char *cp;
+ int ch;
+
+ /*
+ * Preliminary implementation. ASCII specific!!
+ */
+ VSTRING_RESET(result);
+ for (cp = original; (ch = *(unsigned char *) cp) != 0; cp++) {
+ if (ch == '+' || ch == '\\' || ch == '(' || ch < 33 || ch > 126)
+ vstring_sprintf_append(result, "+%02X", ch);
+ else
+ VSTRING_ADDCH(result, ch);
+ }
+ VSTRING_TERMINATE(result);
+
+ return (result);
+}
+
+#ifdef TEST
+
+#define STR(x) vstring_str(x)
+
+#include <vstream.h>
+
+int main(int unused_argc, char **unused_argv)
+{
+ VSTRING *ibuf = vstring_alloc(100);
+ VSTRING *obuf = vstring_alloc(100);
+
+ while (vstring_fgets(ibuf, VSTREAM_IN)) {
+ vstream_fputs(STR(xtext(obuf, STR(ibuf))));
+ vstream_fflush(VSTREAM_OUT);
+ }
+ vstring_free(ibuf);
+ vstring_free(obuf);
+ return (0);
+}
+
+#endif
--- /dev/null
+/*++
+/* NAME
+/* xtext 3h
+/* SUMMARY
+/* translate characters according to RFC 1894
+/* SYNOPSIS
+/* #include <xtext.h>
+/* DESCRIPTION
+/* .nf
+
+ /*
+ * Utility library.
+ */
+#include <vstring.h>
+
+ /*
+ * External interface.
+ */
+extern VSTRING *xtext(VSTRING *, const char *);
+
+/* LICENSE
+/* .ad
+/* .fi
+/* The Secure Mailer license must be distributed with this software.
+/* AUTHOR(S)
+/* Wietse Venema
+/* IBM T.J. Watson Research
+/* P.O. Box 704
+/* Yorktown Heights, NY 10598, USA
+/*--*/
showq.o: ../../include/vstring_vstream.h
showq.o: ../../include/stringops.h
showq.o: ../../include/mymalloc.h
+showq.o: ../../include/htable.h
showq.o: ../../include/mail_queue.h
showq.o: ../../include/mail_open_ok.h
showq.o: ../../include/mail_proto.h
showq.o: ../../include/mail_conf.h
showq.o: ../../include/record.h
showq.o: ../../include/rec_type.h
-showq.o: ../../include/htable.h
+showq.o: ../../include/bounce_log.h
showq.o: ../../include/mail_server.h
#include <vstring_vstream.h>
#include <stringops.h>
#include <mymalloc.h>
+#include <htable.h>
/* Global library. */
#include <mail_conf.h>
#include <record.h>
#include <rec_type.h>
-#include <htable.h>
+#include <bounce_log.h>
/* Single-threaded server skeleton. */
#define STRING_FORMAT "%-10s %8s %-20s %s\n"
#define DATA_FORMAT "%-10s%c%8ld %20.20s %s\n"
-static void showq_reasons(VSTREAM *, VSTREAM *, HTABLE *);
+static void showq_reasons(VSTREAM *, BOUNCE_LOG *, HTABLE *);
static void showq_report(VSTREAM *client, char *queue, char *id,
VSTREAM *qfile, long size)
time_t arrival_time = 0;
char *start;
long msg_size = 0;
- VSTREAM *logfile;
+ BOUNCE_LOG *logfile;
HTABLE *dup_filter = 0;
char status = (strcmp(queue, MAIL_QUEUE_ACTIVE) == 0 ? '*' : ' ');
*/
if (rec_type == REC_TYPE_FROM
&& dup_filter == 0
- && (logfile = mail_queue_open(MAIL_QUEUE_DEFER, id,
- O_RDONLY, 0)) != 0) {
+ && (logfile = bounce_log_open(MAIL_QUEUE_DEFER, id, O_RDONLY, 0)) != 0) {
dup_filter = htable_create(var_dup_filter_limit);
showq_reasons(client, logfile, dup_filter);
- if (vstream_fclose(logfile))
+ if (bounce_log_close(logfile))
msg_warn("close %s %s: %m", MAIL_QUEUE_DEFER, id);
}
}
/* showq_reasons - show deferral reasons */
-static void showq_reasons(VSTREAM *client, VSTREAM *logfile, HTABLE *dup_filter)
+static void showq_reasons(VSTREAM *client, BOUNCE_LOG *bp, HTABLE *dup_filter)
{
- VSTRING *buf = vstring_alloc(100);
- char *recipient;
- char *reason;
char *saved_reason = 0;
- char *cp;
-
- /*
- * XXX Kluge alert. The defer log is an unstructured file. This has the
- * advantage that information is directly suitable for human consumption,
- * and that a process may crash while updating the file - the result will
- * still be usable. The downside of using an unstructured file is that it
- * is hard to process such information mechanically, like we do here. In
- * the end this will have to be a structured file anyway so we can do
- * DSN.
- */
-#define STR vstring_str
-
- while (vstring_get_nonl(buf, logfile) != VSTREAM_EOF) {
-
- /*
- * Do this now so the string won't be reallocated.
- */
- VSTRING_ADDCH(buf, ')');
- VSTRING_TERMINATE(buf);
+ int padding;
- cp = printable(STR(buf), '?');
- if (cp[1] == 0)
- continue;
-
- /*
- * Find the recipient address.
- */
- if (*cp != '<') {
- msg_warn("%s: bad defer record: %.30s...",
- VSTREAM_PATH(logfile), cp);
- continue;
- }
- recipient = cp + 1;
- if ((cp = strstr(recipient, ">:")) == 0) {
- msg_warn("%s: bad defer record: %.30s...",
- VSTREAM_PATH(logfile), cp);
- continue;
- }
- *cp = 0;
+ while (bounce_log_read(bp) != 0) {
/*
* Update the duplicate filter.
*/
- if (*recipient == 0) /* can't happen? */
- recipient = "(MAILER-DAEMON)";
if (var_dup_filter_limit == 0
|| dup_filter->used < var_dup_filter_limit)
- if (htable_locate(dup_filter, recipient) == 0)
- htable_enter(dup_filter, recipient, (char *) 0);
-
- /*
- * Find the reason for deferral. Put parentheses around it.
- */
- reason = cp + 2;
- while (*reason && ISSPACE(*reason))
- reason++;
- reason -= 1;
- *reason = '(';
+ if (htable_locate(dup_filter, bp->recipient) == 0)
+ htable_enter(dup_filter, bp->recipient, (char *) 0);
/*
* Don't print the reason when the previous recipient had the same
* problem.
*/
- if (saved_reason == 0 || strcmp(saved_reason, reason) != 0) {
+ if (saved_reason == 0 || strcmp(saved_reason, bp->text) != 0) {
if (saved_reason)
myfree(saved_reason);
- saved_reason = mystrdup(reason);
- vstream_fprintf(client, "%78s\n", reason);
+ saved_reason = mystrdup(bp->text);
+ if ((padding = 76 - strlen(saved_reason)) < 0)
+ padding = 0;
+ vstream_fprintf(client, "%*s(%s)\n", padding, "", saved_reason);
}
- vstream_fprintf(client, STRING_FORMAT, "", "", "",
- printable(recipient, '?'));
+ vstream_fprintf(client, STRING_FORMAT, "", "", "", bp->recipient);
}
if (saved_reason)
myfree(saved_reason);
- vstring_free(buf);
}
#if __GNUC__ == 2 && __GNUC_MINOR__ >= 7
#define SCANFLIKE(x,y) __attribute__ ((format (scanf, (x), (y))))
#else
-#define SCANFLIKE
+#define SCANFLIKE(x,y)
#endif
#endif