From: Wietse Venema Date: Sun, 24 Sep 2000 00:00:00 +0000 (+0000) Subject: snapshot-20000924 X-Git-Tag: v20010228~37 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=9bdf587a11667a0f68097470600d39792e8e54ff;p=thirdparty%2Fpostfix.git snapshot-20000924 --- diff --git a/postfix/.indent.pro b/postfix/.indent.pro index 005b2559e..ca739a89c 100644 --- a/postfix/.indent.pro +++ b/postfix/.indent.pro @@ -5,6 +5,8 @@ -TBINATTR_INFO -TBINHASH -TBINHASH_INFO +-TBOUNCE_INFO +-TBOUNCE_LOG -TBOUNCE_STAT -TCLEANUP_STATE -TCLIENT_LIST diff --git a/postfix/HISTORY b/postfix/HISTORY index e328ae595..c8c9a9596 100644 --- a/postfix/HISTORY +++ b/postfix/HISTORY @@ -4256,8 +4256,8 @@ Apologies for any names omitted. 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 @@ -4270,3 +4270,25 @@ Apologies for any names omitted. 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. diff --git a/postfix/RELEASE_NOTES b/postfix/RELEASE_NOTES index 746938cfe..5c88e92a5 100644 --- a/postfix/RELEASE_NOTES +++ b/postfix/RELEASE_NOTES @@ -1,3 +1,17 @@ +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 ==================================== diff --git a/postfix/html/bounce.8.html b/postfix/html/bounce.8.html index a4d8be3c0..63e760535 100644 --- a/postfix/html/bounce.8.html +++ b/postfix/html/bounce.8.html @@ -28,36 +28,36 @@ BOUNCE(8) BOUNCE(8) and of the corresponding message. When the bounce is posted successfully, the log file is deleted. - o 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. STANDARDS - RFC 822 (ARPA Internet Text Messages) + RFC 822 (ARPA Internet Text Messages) + RFC 1894 (Delivery Status Notifications) DIAGNOSTICS Problems and transactions are logged to syslogd(8). BUGS - 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. CONFIGURATION PARAMETERS - The following main.cf parameters are especially relevant - to this program. See the Postfix main.cf file for syntax - details and for default values. Use the postfix reload + The following main.cf parameters are especially relevant + to this program. See the Postfix main.cf file for syntax + details and for default values. Use the postfix reload command after a configuration change. + bounce_notice_recipient + The recipient of single bounce postmaster notices. @@ -71,26 +71,23 @@ BOUNCE(8) BOUNCE(8) BOUNCE(8) BOUNCE(8) - bounce_notice_recipient - The recipient of single bounce postmaster notices. - 2bounce_notice_recipient - The recipient of double bounce postmaster notices. + The recipient of double bounce postmaster notices. delay_notice_recipient The recipient of "delayed mail" postmaster notices. bounce_size_limit - Limit the amount of original message context that + Limit the amount of original message context that is sent in a non-delivery notification. mail_name - 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. notify_classes - Notify the postmaster of bounced mail when this - parameter includes the bounce class. For privacy + Notify the postmaster of bounced mail when this + parameter includes the bounce class. For privacy reasons, the message body is not included. SEE ALSO @@ -99,7 +96,7 @@ BOUNCE(8) BOUNCE(8) syslogd(8) system logging LICENSE - The Secure Mailer license must be distributed with this + The Secure Mailer license must be distributed with this software. AUTHOR(S) @@ -124,6 +121,9 @@ BOUNCE(8) BOUNCE(8) + + + diff --git a/postfix/man/man8/bounce.8 b/postfix/man/man8/bounce.8 index 59c98af64..02dc4cee6 100644 --- a/postfix/man/man8/bounce.8 +++ b/postfix/man/man8/bounce.8 @@ -27,8 +27,6 @@ Append a recipient status record to a per-message log file. 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 @@ -42,6 +40,7 @@ themselves, and that depend on retry logic in their own client. .na .nf RFC 822 (ARPA Internet Text Messages) +RFC 1894 (Delivery Status Notifications) .SH DIAGNOSTICS .ad .fi diff --git a/postfix/src/bounce/Makefile.in b/postfix/src/bounce/Makefile.in index 1b0943545..97bb71431 100644 --- a/postfix/src/bounce/Makefile.in +++ b/postfix/src/bounce/Makefile.in @@ -1,8 +1,8 @@ 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 \ @@ -71,6 +71,7 @@ bounce.o: ../../include/mail_conf.h 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 @@ -82,6 +83,7 @@ bounce_append_service.o: ../../include/mail_queue.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 @@ -91,59 +93,41 @@ bounce_cleanup.o: ../../include/vbuf.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 diff --git a/postfix/src/bounce/bounce.c b/postfix/src/bounce/bounce.c index 616527fbd..8ee090d55 100644 --- a/postfix/src/bounce/bounce.c +++ b/postfix/src/bounce/bounce.c @@ -21,8 +21,6 @@ /* 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 @@ -34,6 +32,7 @@ /* 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 @@ -203,41 +202,6 @@ static int bounce_notify_proto(char *service_name, VSTREAM *client, int flush) 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) @@ -270,8 +234,6 @@ 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; diff --git a/postfix/src/bounce/bounce_notify_service.c b/postfix/src/bounce/bounce_notify_service.c index 78bcfa1a9..4f668841d 100644 --- a/postfix/src/bounce/bounce_notify_service.c +++ b/postfix/src/bounce/bounce_notify_service.c @@ -58,29 +58,15 @@ /* Utility library. */ #include -#include #include -#include -#include -#include -#include -#include #include /* Global library. */ -#include -#include -#include #include -#include -#include -#include -#include -#include +#include #include #include -#include #include /* Application-specific. */ @@ -89,271 +75,22 @@ #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 @@ -382,7 +119,7 @@ int bounce_notify_service(char *service, char *queue_name, */ if (strcasecmp(recipient, mail_addr_double_bounce()) == 0) { msg_warn("%s: undeliverable postmaster notification discarded", - queue_id); + queue_id); bounce_status = 0; } @@ -409,13 +146,12 @@ int bounce_notify_service(char *service, char *queue_name, * 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); } } @@ -426,21 +162,21 @@ int bounce_notify_service(char *service, char *queue_name, */ 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); } @@ -468,12 +204,11 @@ int bounce_notify_service(char *service, char *queue_name, 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) @@ -494,8 +229,7 @@ int bounce_notify_service(char *service, char *queue_name, /* * Cleanup. */ - vstring_free(buf); - vstring_free(boundary); + bounce_mail_free(bounce_info); return (bounce_status); } diff --git a/postfix/src/bounce/bounce_notify_util.c b/postfix/src/bounce/bounce_notify_util.c new file mode 100644 index 000000000..921dc2802 --- /dev/null +++ b/postfix/src/bounce/bounce_notify_util.c @@ -0,0 +1,561 @@ +/*++ +/* 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 +#include +#include +#include +#include +#include + +#ifdef STRCASECMP_IN_STRINGS_H +#include +#endif + +/* Utility library. */ + +#include +#include +#include +#include +#include +#include + +/* Global library. */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* 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); +} diff --git a/postfix/src/bounce/bounce_recip_service.c b/postfix/src/bounce/bounce_recip_service.c deleted file mode 100644 index 2ff9cc0c5..000000000 --- a/postfix/src/bounce/bounce_recip_service.c +++ /dev/null @@ -1,474 +0,0 @@ -/*++ -/* 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 -#include -#include -#include -#include - -#ifdef STRCASECMP_IN_STRINGS_H -#include -#endif - -/* Utility library. */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -/* Global library. */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -/* 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); -} diff --git a/postfix/src/bounce/bounce_service.h b/postfix/src/bounce/bounce_service.h index c29889707..369a3da0d 100644 --- a/postfix/src/bounce/bounce_service.h +++ b/postfix/src/bounce/bounce_service.h @@ -13,6 +13,11 @@ */ #include + /* + * Global library. + */ +#include + /* * bounce_append_service.c */ @@ -23,11 +28,6 @@ extern int bounce_append_service(char *, char *, char *, char *); */ 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 */ @@ -38,6 +38,33 @@ extern void bounce_cleanup_unregister(void); #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 diff --git a/postfix/src/cleanup/cleanup_api.c b/postfix/src/cleanup/cleanup_api.c index 0d530da9b..3d1e4d9cc 100644 --- a/postfix/src/cleanup/cleanup_api.c +++ b/postfix/src/cleanup/cleanup_api.c @@ -216,12 +216,13 @@ int cleanup_close(CLEANUP_STATE *state) 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); diff --git a/postfix/src/global/Makefile.in b/postfix/src/global/Makefile.in index 9d522c7c5..3168c858e 100644 --- a/postfix/src/global/Makefile.in +++ b/postfix/src/global/Makefile.in @@ -17,7 +17,7 @@ SRCS = been_here.c bounce.c canon_addr.c cleanup_strerror.c clnt_stream.c \ 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 \ @@ -36,7 +36,7 @@ OBJS = been_here.o bounce.o canon_addr.o cleanup_strerror.o clnt_stream.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 \ @@ -51,7 +51,7 @@ HDRS = been_here.h bounce.h canon_addr.h cleanup_user.h clnt_stream.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 \ @@ -255,6 +255,15 @@ bounce.o: ../../include/vstream.h 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 @@ -950,3 +959,9 @@ tok822_tree.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 diff --git a/postfix/src/global/bounce.c b/postfix/src/global/bounce.c index 122652d69..75b07298d 100644 --- a/postfix/src/global/bounce.c +++ b/postfix/src/global/bounce.c @@ -28,29 +28,6 @@ /* 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 @@ -65,9 +42,6 @@ /* 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 @@ -209,61 +183,3 @@ int bounce_flush(int flags, const char *queue, const char *id, 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); -} diff --git a/postfix/src/global/bounce.h b/postfix/src/global/bounce.h index fd3623e98..43ebc6040 100644 --- a/postfix/src/global/bounce.h +++ b/postfix/src/global/bounce.h @@ -42,7 +42,6 @@ extern int vbounce_recip(int, const char *, const char *, const char *, #define BOUNCE_CMD_FLUSH 1 /* send log */ #define BOUNCE_CMD_WARN 2 /* send warning bounce, don't delete * log */ -#define BOUNCE_CMD_RECIP 3 /* immediate bounce, no logfile */ /* * Flags. diff --git a/postfix/src/global/bounce_log.c b/postfix/src/global/bounce_log.c new file mode 100644 index 000000000..1bbcd23e0 --- /dev/null +++ b/postfix/src/global/bounce_log.c @@ -0,0 +1,198 @@ +/*++ +/* NAME +/* bounce_log 3 +/* SUMMARY +/* bounce file API +/* SYNOPSIS +/* #include +/* +/* 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 +#include +#include + +/* Utility library. */ + +#include +#include +#include +#include +#include +#include + +/* Global library. */ + +#include +#include + +/* 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 + * (: 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); +} diff --git a/postfix/src/global/bounce_log.h b/postfix/src/global/bounce_log.h new file mode 100644 index 000000000..7b949e951 --- /dev/null +++ b/postfix/src/global/bounce_log.h @@ -0,0 +1,50 @@ +#ifndef _BOUNCE_LOG_H_INCLUDED_ +#define _BOUNCE_LOG_H_INCLUDED_ + +/*++ +/* NAME +/* bounce_log 3h +/* SUMMARY +/* bounce file reader +/* SYNOPSIS +/* #include +/* DESCRIPTION +/* .nf + + /* + * Utility library. + */ +#include +#include + + /* + * 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 diff --git a/postfix/src/global/mail_version.h b/postfix/src/global/mail_version.h index 3fe444180..c6cebe89f 100644 --- a/postfix/src/global/mail_version.h +++ b/postfix/src/global/mail_version.h @@ -15,7 +15,7 @@ * 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 diff --git a/postfix/src/global/xtext.c b/postfix/src/global/xtext.c new file mode 100644 index 000000000..32a51e499 --- /dev/null +++ b/postfix/src/global/xtext.c @@ -0,0 +1,87 @@ +/*++ +/* NAME +/* xtext 3 +/* SUMMARY +/* translate characters according to RFC 1894 +/* SYNOPSIS +/* #include +/* +/* 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 + +/* Utility library. */ + +#include + +/* Global library. */ + +#include + +/* 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 + +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 diff --git a/postfix/src/global/xtext.h b/postfix/src/global/xtext.h new file mode 100644 index 000000000..3c02057e7 --- /dev/null +++ b/postfix/src/global/xtext.h @@ -0,0 +1,30 @@ +/*++ +/* NAME +/* xtext 3h +/* SUMMARY +/* translate characters according to RFC 1894 +/* SYNOPSIS +/* #include +/* DESCRIPTION +/* .nf + + /* + * Utility library. + */ +#include + + /* + * 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 +/*--*/ diff --git a/postfix/src/showq/Makefile.in b/postfix/src/showq/Makefile.in index cfcaf2daf..3418afb2b 100644 --- a/postfix/src/showq/Makefile.in +++ b/postfix/src/showq/Makefile.in @@ -63,6 +63,7 @@ showq.o: ../../include/vstream.h 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 @@ -73,5 +74,5 @@ showq.o: ../../include/mail_scan_dir.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 diff --git a/postfix/src/showq/showq.c b/postfix/src/showq/showq.c index d43bccab8..06fdb1c25 100644 --- a/postfix/src/showq/showq.c +++ b/postfix/src/showq/showq.c @@ -67,6 +67,7 @@ #include #include #include +#include /* Global library. */ @@ -79,7 +80,7 @@ #include #include #include -#include +#include /* Single-threaded server skeleton. */ @@ -92,7 +93,7 @@ int var_dup_filter_limit; #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) @@ -102,7 +103,7 @@ static void showq_report(VSTREAM *client, char *queue, char *id, 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 ? '*' : ' '); @@ -150,11 +151,10 @@ static void showq_report(VSTREAM *client, char *queue, char *id, */ 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); } } @@ -165,88 +165,37 @@ static void showq_report(VSTREAM *client, char *queue, char *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); } diff --git a/postfix/src/util/sys_defs.h b/postfix/src/util/sys_defs.h index ed7b21722..4221e3807 100644 --- a/postfix/src/util/sys_defs.h +++ b/postfix/src/util/sys_defs.h @@ -837,7 +837,7 @@ typedef int pid_t; #if __GNUC__ == 2 && __GNUC_MINOR__ >= 7 #define SCANFLIKE(x,y) __attribute__ ((format (scanf, (x), (y)))) #else -#define SCANFLIKE +#define SCANFLIKE(x,y) #endif #endif