5 /* bounce_notify_util 3
7 /* send non-delivery report to sender, server side
9 /* #include "bounce_service.h"
13 /* /* All private members... */
17 /* BOUNCE_INFO *bounce_mail_init(service, queue_name, queue_id,
18 /* encoding, dsn_envid, template)
19 /* const char *service;
20 /* const char *queue_name;
21 /* const char *queue_id;
22 /* const char *encoding;
23 /* const char *dsn_envid;
24 /* const BOUNCE_TEMPLATE *template;
26 /* BOUNCE_INFO *bounce_mail_one_init(queue_name, queue_id, encoding,
27 /* dsn_envid, dsn_notify, rcpt_buf,
29 /* const char *queue_name;
30 /* const char *queue_id;
31 /* const char *encoding;
33 /* const char *dsn_envid;
34 /* RCPT_BUF *rcpt_buf;
36 /* const BOUNCE_TEMPLATE *template;
38 /* void bounce_mail_free(bounce_info)
39 /* BOUNCE_INFO *bounce_info;
41 /* int bounce_header(fp, bounce_info, recipient, postmaster_copy)
43 /* BOUNCE_INFO *bounce_info;
44 /* const char *recipient;
45 /* int postmaster_copy;
47 /* int bounce_boilerplate(fp, bounce_info)
49 /* BOUNCE_INFO *bounce_info;
51 /* int bounce_recipient_log(fp, bounce_info)
53 /* BOUNCE_INFO *bounce_info;
55 /* int bounce_diagnostic_log(fp, bounce_info, notify_filter)
57 /* BOUNCE_INFO *bounce_info;
60 /* int bounce_header_dsn(fp, bounce_info)
62 /* BOUNCE_INFO *bounce_info;
64 /* int bounce_recipient_dsn(fp, bounce_info)
66 /* BOUNCE_INFO *bounce_info;
68 /* int bounce_diagnostic_dsn(fp, bounce_info, notify_filter)
70 /* BOUNCE_INFO *bounce_info;
73 /* int bounce_original(fp, bounce_info, headers_only)
75 /* BOUNCE_INFO *bounce_info;
78 /* void bounce_delrcpt(bounce_info)
79 /* BOUNCE_INFO *bounce_info;
81 /* void bounce_delrcpt_one(bounce_info)
82 /* BOUNCE_INFO *bounce_info;
84 /* This module implements the grunt work of sending a non-delivery
85 /* notification. A bounce is sent in a form that satisfies RFC 1894
86 /* (delivery status notifications).
88 /* bounce_mail_init() bundles up its argument and attempts to
89 /* open the corresponding logfile and message file. A BOUNCE_INFO
90 /* structure contains all the necessary information about an
91 /* undeliverable message.
93 /* bounce_mail_one_init() provides the same function for only
94 /* one recipient that is not read from bounce logfile.
96 /* bounce_mail_free() releases memory allocated by bounce_mail_init()
97 /* and closes any files opened by bounce_mail_init().
99 /* bounce_header() produces a standard mail header with the specified
100 /* recipient and starts a text/plain message segment for the
101 /* human-readable problem description. postmaster_copy is either
102 /* POSTMASTER_COPY or NO_POSTMASTER_COPY.
104 /* bounce_boilerplate() produces the standard "sorry" text that
105 /* creates the illusion that mail systems are civilized.
107 /* bounce_recipient_log() sends a human-readable representation of
108 /* logfile information for one recipient, with the recipient address
109 /* and with the text why the recipient was undeliverable.
111 /* bounce_diagnostic_log() sends a human-readable representation of
112 /* logfile information for all undeliverable recipients. The
113 /* notify_filter specifies what recipient status records should be
114 /* reported: DSN_NOTIFY_SUCCESS, DSN_NOTIFY_FAILURE, DSN_NOTIFY_DELAY.
115 /* In the absence of DSN NOTIFY information all records are reported.
116 /* The result value is -1 in case of error, the number of reported
117 /* recipients in case of success.
119 /* bounce_header_dsn() starts a message/delivery-status message
120 /* segment and sends the machine-readable information that identifies
121 /* the reporting MTA.
123 /* bounce_recipient_dsn() sends a machine-readable representation of
124 /* logfile information for one recipient, with the recipient address
125 /* and with the text why the recipient was undeliverable.
127 /* bounce_diagnostic_dsn() sends a machine-readable representation of
128 /* logfile information for all undeliverable recipients. The
129 /* notify_filter specifies what recipient status records should be
130 /* reported: DSN_NOTIFY_SUCCESS, DSN_NOTIFY_FAILURE, DSN_NOTIFY_DELAY.
131 /* In the absence of DSN NOTIFY information all records are reported.
132 /* The result value is -1 in case of error, the number of reported
133 /* recipients in case of success.
135 /* bounce_original() starts a message/rfc822 or text/rfc822-headers
136 /* message segment and sends the original message, either full
137 /* (DSN_RET_FULL) or message headers only (DSN_RET_HDRS).
139 /* bounce_delrcpt() deletes recipients in the logfile from the original
142 /* bounce_delrcpt_one() deletes one recipient from the original
145 /* Fatal error: error opening existing file.
148 /* bounce(3) basic bounce service client interface
152 /* The Secure Mailer license must be distributed with this software.
155 /* IBM T.J. Watson Research
157 /* Yorktown Heights, NY 10598, USA
160 /* System library. */
162 #include <sys_defs.h>
163 #include <sys/stat.h>
165 #include <stdio.h> /* sscanf() */
171 #ifdef STRCASECMP_IN_STRINGS_H
175 /* Utility library. */
178 #include <mymalloc.h>
182 #include <line_wrap.h>
183 #include <stringops.h>
186 /* Global library. */
188 #include <mail_queue.h>
189 #include <quote_822_local.h>
190 #include <mail_params.h>
191 #include <is_header.h>
193 #include <rec_type.h>
194 #include <post_mail.h>
195 #include <mail_addr.h>
196 #include <mail_error.h>
197 #include <bounce_log.h>
198 #include <mail_date.h>
199 #include <mail_proto.h>
201 #include <deliver_completed.h>
202 #include <dsn_mask.h>
204 /* Application-specific. */
206 #include "bounce_service.h"
208 #define STR vstring_str
210 /* bounce_mail_alloc - initialize */
212 static BOUNCE_INFO
*bounce_mail_alloc(const char *service
,
213 const char *queue_name
,
214 const char *queue_id
,
215 const char *encoding
,
216 const char *dsn_envid
,
219 BOUNCE_TEMPLATE
*template,
220 BOUNCE_LOG
*log_handle
)
222 BOUNCE_INFO
*bounce_info
;
226 * Bundle up a bunch of parameters and initialize information that will
227 * be discovered on the fly.
229 bounce_info
= (BOUNCE_INFO
*) mymalloc(sizeof(*bounce_info
));
230 bounce_info
->service
= service
;
231 bounce_info
->queue_name
= queue_name
;
232 bounce_info
->queue_id
= queue_id
;
233 if (strcmp(encoding
, MAIL_ATTR_ENC_8BIT
) == 0) {
234 bounce_info
->mime_encoding
= "8bit";
235 } else if (strcmp(encoding
, MAIL_ATTR_ENC_7BIT
) == 0) {
236 bounce_info
->mime_encoding
= "7bit";
238 if (strcmp(encoding
, MAIL_ATTR_ENC_NONE
) != 0)
239 msg_warn("%s: unknown encoding: %.200s",
240 bounce_info
->queue_id
, encoding
);
241 bounce_info
->mime_encoding
= 0;
243 if (dsn_envid
&& *dsn_envid
)
244 bounce_info
->dsn_envid
= dsn_envid
;
246 bounce_info
->dsn_envid
= 0;
247 bounce_info
->template = template;
248 bounce_info
->buf
= vstring_alloc(100);
249 bounce_info
->sender
= vstring_alloc(100);
250 bounce_info
->arrival_time
= 0;
251 bounce_info
->orig_offs
= 0;
252 bounce_info
->message_size
= 0;
253 bounce_info
->rcpt_buf
= rcpt_buf
;
254 bounce_info
->dsn_buf
= dsn_buf
;
255 bounce_info
->log_handle
= log_handle
;
258 * RFC 1894: diagnostic-type is an RFC 822 atom. We use X-$mail_name and
259 * must ensure it is valid.
261 bounce_info
->mail_name
= mystrdup(var_mail_name
);
262 translit(bounce_info
->mail_name
, " \t\r\n()<>@,;:\\\".[]",
263 "-----------------");
266 * Compute a supposedly unique boundary string. This assumes that a queue
267 * ID and a hostname contain acceptable characters for a boundary string,
268 * but the assumption is not verified.
270 vstring_sprintf(bounce_info
->buf
, "%s.%lu/%s",
271 queue_id
, (unsigned long) event_time(), var_myhostname
);
272 bounce_info
->mime_boundary
= mystrdup(STR(bounce_info
->buf
));
275 * If the original message cannot be found, do not raise a run-time
276 * error. There is nothing we can do about the error, and all we are
277 * doing is to inform the sender of a delivery problem. Bouncing a
278 * message does not have to be a perfect job. But if the system IS
279 * running out of resources, raise a fatal run-time error and force a
282 if ((bounce_info
->orig_fp
= mail_queue_open(queue_name
, queue_id
,
285 msg_fatal("open %s %s: %m", service
, queue_id
);
288 * Get time/size/sender information from the original message envelope
289 * records. If the envelope is corrupted just send whatever we can
290 * (remember this is a best effort, it does not have to be perfect).
292 * Lock the file for shared use, so that queue manager leaves it alone after
295 #define DELIVER_LOCK_MODE (MYFLOCK_OP_SHARED | MYFLOCK_OP_NOWAIT)
297 if (bounce_info
->orig_fp
!= 0) {
298 if (myflock(vstream_fileno(bounce_info
->orig_fp
), INTERNAL_LOCK
,
299 DELIVER_LOCK_MODE
) < 0)
300 msg_fatal("cannot get shared lock on %s: %m",
301 VSTREAM_PATH(bounce_info
->orig_fp
));
302 while ((rec_type
= rec_get(bounce_info
->orig_fp
,
303 bounce_info
->buf
, 0)) > 0) {
306 * Postfix version dependent: data offset in SIZE record.
308 if (rec_type
== REC_TYPE_SIZE
) {
309 if (bounce_info
->message_size
== 0)
310 sscanf(STR(bounce_info
->buf
), "%ld %ld",
311 &bounce_info
->message_size
,
312 &bounce_info
->orig_offs
);
313 if (bounce_info
->message_size
< 0)
314 bounce_info
->message_size
= 0;
315 if (bounce_info
->orig_offs
< 0)
316 bounce_info
->orig_offs
= 0;
320 * Information for the Arrival-Date: attribute.
322 else if (rec_type
== REC_TYPE_TIME
) {
323 if (bounce_info
->arrival_time
== 0
324 && (bounce_info
->arrival_time
= atol(STR(bounce_info
->buf
))) < 0)
325 bounce_info
->arrival_time
= 0;
329 * Information for the X-Postfix-Sender: attribute.
331 else if (rec_type
== REC_TYPE_FROM
) {
332 quote_822_local_flags(bounce_info
->sender
,
333 VSTRING_LEN(bounce_info
->buf
) ?
334 STR(bounce_info
->buf
) :
335 mail_addr_mail_daemon(), 0);
339 * Backwards compatibility: no data offset in SIZE record.
341 else if (rec_type
== REC_TYPE_MESG
) {
342 /* XXX Future: sender+recipient after message content. */
343 if (VSTRING_LEN(bounce_info
->sender
) == 0)
344 msg_warn("%s: no sender before message content record",
345 bounce_info
->queue_id
);
346 bounce_info
->orig_offs
= vstream_ftell(bounce_info
->orig_fp
);
349 if (bounce_info
->orig_offs
> 0
350 && bounce_info
->arrival_time
> 0
351 && VSTRING_LEN(bounce_info
->sender
) > 0)
355 return (bounce_info
);
358 /* bounce_mail_init - initialize */
360 BOUNCE_INFO
*bounce_mail_init(const char *service
,
361 const char *queue_name
,
362 const char *queue_id
,
363 const char *encoding
,
364 const char *dsn_envid
,
365 BOUNCE_TEMPLATE
*template)
367 BOUNCE_INFO
*bounce_info
;
368 BOUNCE_LOG
*log_handle
;
373 * Initialize the bounce_info structure. If the bounce log cannot be
374 * found, do not raise a fatal run-time error. There is nothing we can do
375 * about the error, and all we are doing is to inform the sender of a
376 * delivery problem, Bouncing a message does not have to be a perfect
377 * job. But if the system IS running out of resources, raise a fatal
378 * run-time error and force a backoff.
380 if ((log_handle
= bounce_log_open(service
, queue_id
, O_RDONLY
, 0)) == 0) {
382 msg_fatal("open %s %s: %m", service
, queue_id
);
386 rcpt_buf
= rcpb_create();
387 dsn_buf
= dsb_create();
389 bounce_info
= bounce_mail_alloc(service
, queue_name
, queue_id
, encoding
,
390 dsn_envid
, rcpt_buf
, dsn_buf
,
391 template, log_handle
);
392 return (bounce_info
);
395 /* bounce_mail_one_init - initialize */
397 BOUNCE_INFO
*bounce_mail_one_init(const char *queue_name
,
398 const char *queue_id
,
399 const char *encoding
,
400 const char *dsn_envid
,
403 BOUNCE_TEMPLATE
*template)
405 BOUNCE_INFO
*bounce_info
;
408 * Initialize the bounce_info structure for just one recipient.
410 bounce_info
= bounce_mail_alloc("none", queue_name
, queue_id
, encoding
,
411 dsn_envid
, rcpt_buf
, dsn_buf
, template,
413 return (bounce_info
);
416 /* bounce_mail_free - undo bounce_mail_init */
418 void bounce_mail_free(BOUNCE_INFO
*bounce_info
)
420 if (bounce_info
->log_handle
) {
421 if (bounce_log_close(bounce_info
->log_handle
))
422 msg_warn("%s: read bounce log %s: %m",
423 bounce_info
->queue_id
, bounce_info
->queue_id
);
424 rcpb_free(bounce_info
->rcpt_buf
);
425 dsb_free(bounce_info
->dsn_buf
);
427 if (bounce_info
->orig_fp
&& vstream_fclose(bounce_info
->orig_fp
))
428 msg_warn("%s: read message file %s %s: %m",
429 bounce_info
->queue_id
, bounce_info
->queue_name
,
430 bounce_info
->queue_id
);
431 vstring_free(bounce_info
->buf
);
432 vstring_free(bounce_info
->sender
);
433 myfree(bounce_info
->mail_name
);
434 myfree((char *) bounce_info
->mime_boundary
);
435 myfree((char *) bounce_info
);
438 /* bounce_header - generate bounce message header */
440 int bounce_header(VSTREAM
*bounce
, BOUNCE_INFO
*bounce_info
,
441 const char *dest
, int postmaster_copy
)
443 BOUNCE_TEMPLATE
*template = bounce_info
->template;
446 * Print a minimal bounce header. The cleanup service will add other
447 * headers and will make all addresses fully qualified.
449 #define STREQ(a, b) (strcasecmp((a), (b)) == 0)
454 bounce_template_headers(post_mail_fprintf
, bounce
, template,
455 STR(quote_822_local(bounce_info
->buf
, dest
)),
459 * Auto-Submitted header, as per RFC 3834.
461 post_mail_fprintf(bounce
, "Auto-Submitted: %s", postmaster_copy
?
462 "auto-generated" : "auto-replied");
465 * MIME header. Use 8bit encoding when either the bounced message or the
466 * template requires it.
468 post_mail_fprintf(bounce
, "MIME-Version: 1.0");
469 post_mail_fprintf(bounce
, "Content-Type: %s; report-type=%s;",
470 "multipart/report", "delivery-status");
471 post_mail_fprintf(bounce
, "\tboundary=\"%s\"", bounce_info
->mime_boundary
);
472 if (bounce_info
->mime_encoding
)
473 post_mail_fprintf(bounce
, "Content-Transfer-Encoding: %s",
474 STREQ(bounce_info
->mime_encoding
, MAIL_ATTR_ENC_7BIT
) ?
475 bounce_template_encoding(template) :
476 bounce_info
->mime_encoding
);
477 post_mail_fputs(bounce
, "");
478 post_mail_fputs(bounce
, "This is a MIME-encapsulated message.");
483 post_mail_fputs(bounce
, "");
484 post_mail_fprintf(bounce
, "--%s", bounce_info
->mime_boundary
);
485 post_mail_fprintf(bounce
, "Content-Description: %s", "Notification");
486 post_mail_fprintf(bounce
, "Content-Type: %s; charset=%s",
487 "text/plain", bounce_template_charset(template));
488 post_mail_fputs(bounce
, "");
490 return (vstream_ferror(bounce
));
493 /* bounce_boilerplate - generate boiler-plate text */
495 int bounce_boilerplate(VSTREAM
*bounce
, BOUNCE_INFO
*bounce_info
)
499 * Print the boiler-plate text.
501 bounce_template_expand(post_mail_fputs
, bounce
, bounce_info
->template);
502 return (vstream_ferror(bounce
));
505 /* bounce_print - line_wrap callback */
507 static void bounce_print(const char *str
, int len
, int indent
, char *context
)
509 VSTREAM
*bounce
= (VSTREAM
*) context
;
511 post_mail_fprintf(bounce
, "%*s%.*s", indent
, "", len
, str
);
514 /* bounce_print_wrap - print and wrap a line */
516 static void bounce_print_wrap(VSTREAM
*bounce
, BOUNCE_INFO
*bounce_info
,
517 const char *format
,...)
524 va_start(ap
, format
);
525 vstring_vsprintf(bounce_info
->buf
, format
, ap
);
527 line_wrap(STR(bounce_info
->buf
), LENGTH
, INDENT
,
528 bounce_print
, (char *) bounce
);
531 /* bounce_recipient_log - send one bounce log report entry */
533 int bounce_recipient_log(VSTREAM
*bounce
, BOUNCE_INFO
*bounce_info
)
535 RECIPIENT
*rcpt
= &bounce_info
->rcpt_buf
->rcpt
;
536 DSN
*dsn
= &bounce_info
->dsn_buf
->dsn
;
539 * Mask control and non-ASCII characters (done in bounce_log_read()),
540 * wrap long lines and prepend one blank, so this data can safely be
541 * piped into other programs. Sort of like TCP Wrapper's safe_finger
544 #define NON_NULL_EMPTY(s) ((s) && *(s))
546 post_mail_fputs(bounce
, "");
547 if (NON_NULL_EMPTY(rcpt
->orig_addr
)) {
548 bounce_print_wrap(bounce
, bounce_info
, "<%s> (expanded from <%s>): %s",
549 rcpt
->address
, rcpt
->orig_addr
, dsn
->reason
);
551 bounce_print_wrap(bounce
, bounce_info
, "<%s>: %s",
552 rcpt
->address
, dsn
->reason
);
554 return (vstream_ferror(bounce
));
557 /* bounce_diagnostic_log - send bounce log report */
559 int bounce_diagnostic_log(VSTREAM
*bounce
, BOUNCE_INFO
*bounce_info
,
562 RECIPIENT
*rcpt
= &bounce_info
->rcpt_buf
->rcpt
;
566 * Append a human-readable copy of the delivery error log. We're doing a
567 * best effort, so there is no point raising a fatal run-time error in
568 * case of a logfile read error.
570 * XXX DSN If the logfile with failed recipients is unavailable, pretend
571 * that we found something anyway, so that this notification will not be
574 if (bounce_info
->log_handle
== 0
575 || bounce_log_rewind(bounce_info
->log_handle
)) {
576 if (IS_FAILURE_TEMPLATE(bounce_info
->template)) {
577 post_mail_fputs(bounce
, "");
578 post_mail_fputs(bounce
, "\t--- Delivery report unavailable ---");
579 count
= 1; /* XXX don't abort */
582 while (bounce_log_read(bounce_info
->log_handle
, bounce_info
->rcpt_buf
,
583 bounce_info
->dsn_buf
) != 0) {
584 if (rcpt
->dsn_notify
== 0 /* compat */
585 || (rcpt
->dsn_notify
& notify_filter
)) {
587 if (bounce_recipient_log(bounce
, bounce_info
) != 0)
592 return (vstream_ferror(bounce
) ? -1 : count
);
595 /* bounce_header_dsn - send per-MTA bounce DSN records */
597 int bounce_header_dsn(VSTREAM
*bounce
, BOUNCE_INFO
*bounce_info
)
603 post_mail_fputs(bounce
, "");
604 post_mail_fprintf(bounce
, "--%s", bounce_info
->mime_boundary
);
605 post_mail_fprintf(bounce
, "Content-Description: %s",
607 post_mail_fprintf(bounce
, "Content-Type: %s", "message/delivery-status");
610 * According to RFC 1894: The body of a message/delivery-status consists
611 * of one or more "fields" formatted according to the ABNF of RFC 822
612 * header "fields" (see [6]). The per-message fields appear first,
613 * followed by a blank line.
615 post_mail_fputs(bounce
, "");
616 post_mail_fprintf(bounce
, "Reporting-MTA: dns; %s", var_myhostname
);
618 post_mail_fprintf(bounce
, "Received-From-MTA: dns; %s", "whatever");
620 if (NON_NULL_EMPTY(bounce_info
->dsn_envid
)) {
621 post_mail_fprintf(bounce
, "Original-Envelope-Id: %s",
622 bounce_info
->dsn_envid
);
624 post_mail_fprintf(bounce
, "X-%s-Queue-ID: %s",
625 bounce_info
->mail_name
, bounce_info
->queue_id
);
626 if (VSTRING_LEN(bounce_info
->sender
) > 0)
627 post_mail_fprintf(bounce
, "X-%s-Sender: rfc822; %s",
628 bounce_info
->mail_name
, STR(bounce_info
->sender
));
629 if (bounce_info
->arrival_time
> 0)
630 post_mail_fprintf(bounce
, "Arrival-Date: %s",
631 mail_date(bounce_info
->arrival_time
));
632 return (vstream_ferror(bounce
));
635 /* bounce_recipient_dsn - send per-recipient DSN records */
637 int bounce_recipient_dsn(VSTREAM
*bounce
, BOUNCE_INFO
*bounce_info
)
639 RECIPIENT
*rcpt
= &bounce_info
->rcpt_buf
->rcpt
;
640 DSN
*dsn
= &bounce_info
->dsn_buf
->dsn
;
642 post_mail_fputs(bounce
, "");
643 post_mail_fprintf(bounce
, "Final-Recipient: rfc822; %s", rcpt
->address
);
648 * RFC 3464 section 6.3.d: "If no ORCPT parameter was provided for this
649 * recipient, the Original-Recipient field MUST NOT appear."
651 * This is inconsistent with section 5.2.1.d: "If no ORCPT parameter was
652 * present in the RCPT command when the message was received, an ORCPT
653 * parameter MAY be added to the RCPT command when the message is
654 * relayed.". Postfix adds an ORCPT parameter under these conditions.
656 * Therefore, all down-stream MTAs will send DSNs with Original-Recipient
657 * field ontaining this same ORCPT value. When a down-stream MTA can use
658 * that information in their DSNs, it makes no sense that an up-stream
659 * MTA can't use that same information in its own DSNs.
661 * Postfix always reports an Original-Recipient field, because it is more
662 * more useful and more consistent.
664 if (NON_NULL_EMPTY(rcpt
->dsn_orcpt
)) {
665 post_mail_fprintf(bounce
, "Original-Recipient: %s", rcpt
->dsn_orcpt
);
666 } else if (NON_NULL_EMPTY(rcpt
->orig_addr
)) {
667 post_mail_fprintf(bounce
, "Original-Recipient: rfc822; %s",
670 post_mail_fprintf(bounce
, "Action: %s",
671 IS_FAILURE_TEMPLATE(bounce_info
->template) ?
672 "failed" : dsn
->action
);
673 post_mail_fprintf(bounce
, "Status: %s", dsn
->status
);
674 if (NON_NULL_EMPTY(dsn
->mtype
) && NON_NULL_EMPTY(dsn
->mname
))
675 bounce_print_wrap(bounce
, bounce_info
, "Remote-MTA: %s; %s",
676 dsn
->mtype
, dsn
->mname
);
677 if (NON_NULL_EMPTY(dsn
->dtype
) && NON_NULL_EMPTY(dsn
->dtext
))
678 bounce_print_wrap(bounce
, bounce_info
, "Diagnostic-Code: %s; %s",
679 dsn
->dtype
, dsn
->dtext
);
681 bounce_print_wrap(bounce
, bounce_info
, "Diagnostic-Code: X-%s; %s",
682 bounce_info
->mail_name
, dsn
->reason
);
685 post_mail_fprintf(bounce
, "Last-Attempt-Date: %s",
686 mail_date(dsn
->time
));
688 if (IS_DELAY_TEMPLATE(bounce_info
->template))
689 post_mail_fprintf(bounce
, "Will-Retry-Until: %s",
690 mail_date(bounce_info
->arrival_time
+ var_max_queue_time
));
691 return (vstream_ferror(bounce
));
694 /* bounce_diagnostic_dsn - send bounce log report, machine readable form */
696 int bounce_diagnostic_dsn(VSTREAM
*bounce
, BOUNCE_INFO
*bounce_info
,
699 RECIPIENT
*rcpt
= &bounce_info
->rcpt_buf
->rcpt
;
703 * Append a machine-readable copy of the delivery error log. We're doing
704 * a best effort, so there is no point raising a fatal run-time error in
705 * case of a logfile read error.
707 * XXX DSN If the logfile with failed recipients is unavailable, pretend
708 * that we found something anyway, so that this notification will not be
711 if (bounce_info
->log_handle
== 0
712 || bounce_log_rewind(bounce_info
->log_handle
)) {
713 if (IS_FAILURE_TEMPLATE(bounce_info
->template))
714 count
= 1; /* XXX don't abort */
716 while (bounce_log_read(bounce_info
->log_handle
, bounce_info
->rcpt_buf
,
717 bounce_info
->dsn_buf
) != 0) {
718 if (rcpt
->dsn_notify
== 0 /* compat */
719 || (rcpt
->dsn_notify
& notify_filter
)) {
721 if (bounce_recipient_dsn(bounce
, bounce_info
) != 0)
726 return (vstream_ferror(bounce
) ? -1 : count
);
729 /* bounce_original - send a copy of the original to the victim */
731 int bounce_original(VSTREAM
*bounce
, BOUNCE_INFO
*bounce_info
,
738 * When truncating a large message, don't damage the MIME structure: send
739 * the message headers only.
741 if (var_bounce_limit
> 0
742 && bounce_info
->orig_fp
743 && (bounce_info
->message_size
<= 0
744 || bounce_info
->message_size
> var_bounce_limit
))
745 headers_only
= DSN_RET_HDRS
;
750 #define IS_UNDELIVERED_TEMPLATE(template) \
751 (IS_FAILURE_TEMPLATE(template) || IS_DELAY_TEMPLATE(template))
753 post_mail_fputs(bounce
, "");
754 post_mail_fprintf(bounce
, "--%s", bounce_info
->mime_boundary
);
755 post_mail_fprintf(bounce
, "Content-Description: %s%s",
756 IS_UNDELIVERED_TEMPLATE(bounce_info
->template) ?
758 headers_only
== DSN_RET_HDRS
?
759 "Message Headers" : "Message");
760 post_mail_fprintf(bounce
, "Content-Type: %s",
761 headers_only
== DSN_RET_HDRS
?
762 "text/rfc822-headers" : "message/rfc822");
763 if (bounce_info
->mime_encoding
)
764 post_mail_fprintf(bounce
, "Content-Transfer-Encoding: %s",
765 bounce_info
->mime_encoding
);
766 post_mail_fputs(bounce
, "");
769 * Send place holder if original is unavailable.
771 if (bounce_info
->orig_offs
== 0 || vstream_fseek(bounce_info
->orig_fp
,
772 bounce_info
->orig_offs
, SEEK_SET
) < 0) {
773 post_mail_fputs(bounce
, "\t--- Undelivered message unavailable ---");
774 return (vstream_ferror(bounce
));
778 * XXX The cleanup server removes Return-Path: headers. This should be
779 * done only with mail that enters via a non-SMTP channel, but changing
780 * this now could break other software. Removing Return-Path: could break
781 * digital signatures, though this is unlikely. In any case,
782 * header_checks are more effective when the Return-Path: header is
783 * present, so we prepend one to the bounce message.
785 post_mail_fprintf(bounce
, "Return-Path: <%s>", STR(bounce_info
->sender
));
788 * Copy the original message contents. We're doing raw record output here
789 * so that we don't throw away binary transparency yet.
791 #define IS_HEADER(s) (IS_SPACE_TAB(*(s)) || is_header(s))
793 while (status
== 0 && (rec_type
= rec_get(bounce_info
->orig_fp
, bounce_info
->buf
, 0)) > 0) {
794 if (rec_type
!= REC_TYPE_NORM
&& rec_type
!= REC_TYPE_CONT
)
796 if (headers_only
== DSN_RET_HDRS
797 && !IS_HEADER(vstring_str(bounce_info
->buf
)))
799 status
= (REC_PUT_BUF(bounce
, rec_type
, bounce_info
->buf
) != rec_type
);
803 * Final MIME headers. These require -- at the end of the boundary
806 * XXX This should be a separate bounce_terminate() entry so we can be
807 * assured that the terminator will always be sent.
809 post_mail_fputs(bounce
, "");
810 post_mail_fprintf(bounce
, "--%s--", bounce_info
->mime_boundary
);
815 /* bounce_delrcpt - delete recipients from original queue file */
817 void bounce_delrcpt(BOUNCE_INFO
*bounce_info
)
819 RECIPIENT
*rcpt
= &bounce_info
->rcpt_buf
->rcpt
;
821 if (bounce_info
->orig_fp
!= 0
822 && bounce_info
->log_handle
!= 0
823 && bounce_log_rewind(bounce_info
->log_handle
) == 0)
824 while (bounce_log_read(bounce_info
->log_handle
, bounce_info
->rcpt_buf
,
825 bounce_info
->dsn_buf
) != 0)
826 if (rcpt
->offset
> 0)
827 deliver_completed(bounce_info
->orig_fp
, rcpt
->offset
);
830 /* bounce_delrcpt_one - delete one recipient from original queue file */
832 void bounce_delrcpt_one(BOUNCE_INFO
*bounce_info
)
834 RECIPIENT
*rcpt
= &bounce_info
->rcpt_buf
->rcpt
;
836 if (bounce_info
->orig_fp
!= 0 && rcpt
->offset
> 0)
837 deliver_completed(bounce_info
->orig_fp
, rcpt
->offset
);