1/*++ 2/* NAME 3/* bounce_notify_util 3 4/* SUMMARY 5/* send non-delivery report to sender, server side 6/* SYNOPSIS 7/* #include "bounce_service.h" 8/* 9/* typedef struct { 10/* .in +4 11/* /* All private members... */ 12/* .in -4 13/* } BOUNCE_INFO; 14/* 15/* BOUNCE_INFO *bounce_mail_init(service, queue_name, queue_id, 16/* encoding, dsn_envid, template) 17/* const char *service; 18/* const char *queue_name; 19/* const char *queue_id; 20/* const char *encoding; 21/* const char *dsn_envid; 22/* const BOUNCE_TEMPLATE *template; 23/* 24/* BOUNCE_INFO *bounce_mail_one_init(queue_name, queue_id, encoding, 25/* dsn_envid, dsn_notify, rcpt_buf, 26/* dsn_buf, template) 27/* const char *queue_name; 28/* const char *queue_id; 29/* const char *encoding; 30/* int dsn_notify; 31/* const char *dsn_envid; 32/* RCPT_BUF *rcpt_buf; 33/* DSN_BUF *dsn_buf; 34/* const BOUNCE_TEMPLATE *template; 35/* 36/* void bounce_mail_free(bounce_info) 37/* BOUNCE_INFO *bounce_info; 38/* 39/* int bounce_header(fp, bounce_info, recipient, postmaster_copy) 40/* VSTREAM *fp; 41/* BOUNCE_INFO *bounce_info; 42/* const char *recipient; 43/* int postmaster_copy; 44/* 45/* int bounce_boilerplate(fp, bounce_info) 46/* VSTREAM *fp; 47/* BOUNCE_INFO *bounce_info; 48/* 49/* int bounce_recipient_log(fp, bounce_info) 50/* VSTREAM *fp; 51/* BOUNCE_INFO *bounce_info; 52/* 53/* int bounce_diagnostic_log(fp, bounce_info, notify_filter) 54/* VSTREAM *fp; 55/* BOUNCE_INFO *bounce_info; 56/* int notify_filter; 57/* 58/* int bounce_header_dsn(fp, bounce_info) 59/* VSTREAM *fp; 60/* BOUNCE_INFO *bounce_info; 61/* 62/* int bounce_recipient_dsn(fp, bounce_info) 63/* VSTREAM *fp; 64/* BOUNCE_INFO *bounce_info; 65/* 66/* int bounce_diagnostic_dsn(fp, bounce_info, notify_filter) 67/* VSTREAM *fp; 68/* BOUNCE_INFO *bounce_info; 69/* int notify_filter; 70/* 71/* int bounce_original(fp, bounce_info, headers_only) 72/* VSTREAM *fp; 73/* BOUNCE_INFO *bounce_info; 74/* int headers_only; 75/* 76/* void bounce_delrcpt(bounce_info) 77/* BOUNCE_INFO *bounce_info; 78/* 79/* void bounce_delrcpt_one(bounce_info) 80/* BOUNCE_INFO *bounce_info; 81/* DESCRIPTION 82/* This module implements the grunt work of sending a non-delivery 83/* notification. A bounce is sent in a form that satisfies RFC 1894 84/* (delivery status notifications). 85/* 86/* bounce_mail_init() bundles up its argument and attempts to 87/* open the corresponding logfile and message file. A BOUNCE_INFO 88/* structure contains all the necessary information about an 89/* undeliverable message. 90/* 91/* bounce_mail_one_init() provides the same function for only 92/* one recipient that is not read from bounce logfile. 93/* 94/* bounce_mail_free() releases memory allocated by bounce_mail_init() 95/* and closes any files opened by bounce_mail_init(). 96/* 97/* bounce_header() produces a standard mail header with the specified 98/* recipient and starts a text/plain message segment for the 99/* human-readable problem description. postmaster_copy is either 100/* POSTMASTER_COPY or NO_POSTMASTER_COPY. 101/* 102/* bounce_boilerplate() produces the standard "sorry" text that 103/* creates the illusion that mail systems are civilized. 104/* 105/* bounce_recipient_log() sends a human-readable representation of 106/* logfile information for one recipient, with the recipient address 107/* and with the text why the recipient was undeliverable. 108/* 109/* bounce_diagnostic_log() sends a human-readable representation of 110/* logfile information for all undeliverable recipients. The 111/* notify_filter specifies what recipient status records should be 112/* reported: DSN_NOTIFY_SUCCESS, DSN_NOTIFY_FAILURE, DSN_NOTIFY_DELAY. 113/* In the absence of DSN NOTIFY information all records are reported. 114/* The result value is -1 in case of error, the number of reported 115/* recipients in case of success. 116/* 117/* bounce_header_dsn() starts a message/delivery-status message 118/* segment and sends the machine-readable information that identifies 119/* the reporting MTA. 120/* 121/* bounce_recipient_dsn() sends a machine-readable representation of 122/* logfile information for one recipient, with the recipient address 123/* and with the text why the recipient was undeliverable. 124/* 125/* bounce_diagnostic_dsn() sends a machine-readable representation of 126/* logfile information for all undeliverable recipients. The 127/* notify_filter specifies what recipient status records should be 128/* reported: DSN_NOTIFY_SUCCESS, DSN_NOTIFY_FAILURE, DSN_NOTIFY_DELAY. 129/* In the absence of DSN NOTIFY information all records are reported. 130/* The result value is -1 in case of error, the number of reported 131/* recipients in case of success. 132/* 133/* bounce_original() starts a message/rfc822 or text/rfc822-headers 134/* message segment and sends the original message, either full 135/* (DSN_RET_FULL) or message headers only (DSN_RET_HDRS). 136/* 137/* bounce_delrcpt() deletes recipients in the logfile from the original 138/* queue file. 139/* 140/* bounce_delrcpt_one() deletes one recipient from the original 141/* queue file. 142/* DIAGNOSTICS 143/* Fatal error: error opening existing file. 144/* BUGS 145/* SEE ALSO 146/* bounce(3) basic bounce service client interface 147/* LICENSE 148/* .ad 149/* .fi 150/* The Secure Mailer license must be distributed with this software. 151/* AUTHOR(S) 152/* Wietse Venema 153/* IBM T.J. Watson Research 154/* P.O. Box 704 155/* Yorktown Heights, NY 10598, USA 156/*--*/ 157 158/* System library. */ 159 160#include <sys_defs.h> 161#include <sys/stat.h> 162#include <stdlib.h> 163#include <stdio.h> /* sscanf() */ 164#include <unistd.h> 165#include <errno.h> 166#include <string.h> 167#include <ctype.h> 168 169#ifdef STRCASECMP_IN_STRINGS_H 170#include <strings.h> 171#endif 172 173/* Utility library. */ 174 175#include <msg.h> 176#include <mymalloc.h> 177#include <events.h> 178#include <vstring.h> 179#include <vstream.h> 180#include <line_wrap.h> 181#include <stringops.h> 182#include <myflock.h> 183 184/* Global library. */ 185 186#include <mail_queue.h> 187#include <quote_822_local.h> 188#include <mail_params.h> 189#include <is_header.h> 190#include <record.h> 191#include <rec_type.h> 192#include <post_mail.h> 193#include <mail_addr.h> 194#include <mail_error.h> 195#include <bounce_log.h> 196#include <mail_date.h> 197#include <mail_proto.h> 198#include <lex_822.h> 199#include <deliver_completed.h> 200#include <dsn_mask.h> 201 202/* Application-specific. */ 203 204#include "bounce_service.h" 205 206#define STR vstring_str 207 208/* bounce_mail_alloc - initialize */ 209 210static BOUNCE_INFO *bounce_mail_alloc(const char *service, 211 const char *queue_name, 212 const char *queue_id, 213 const char *encoding, 214 const char *dsn_envid, 215 RCPT_BUF *rcpt_buf, 216 DSN_BUF *dsn_buf, 217 BOUNCE_TEMPLATE *template, 218 BOUNCE_LOG *log_handle) 219{ 220 BOUNCE_INFO *bounce_info; 221 int rec_type; 222 223 /* 224 * Bundle up a bunch of parameters and initialize information that will 225 * be discovered on the fly. 226 */ 227 bounce_info = (BOUNCE_INFO *) mymalloc(sizeof(*bounce_info)); 228 bounce_info->service = service; 229 bounce_info->queue_name = queue_name; 230 bounce_info->queue_id = queue_id; 231 if (strcmp(encoding, MAIL_ATTR_ENC_8BIT) == 0) { 232 bounce_info->mime_encoding = "8bit"; 233 } else if (strcmp(encoding, MAIL_ATTR_ENC_7BIT) == 0) { 234 bounce_info->mime_encoding = "7bit"; 235 } else { 236 if (strcmp(encoding, MAIL_ATTR_ENC_NONE) != 0) 237 msg_warn("%s: unknown encoding: %.200s", 238 bounce_info->queue_id, encoding); 239 bounce_info->mime_encoding = 0; 240 } 241 if (dsn_envid && *dsn_envid) 242 bounce_info->dsn_envid = dsn_envid; 243 else 244 bounce_info->dsn_envid = 0; 245 bounce_info->template = template; 246 bounce_info->buf = vstring_alloc(100); 247 bounce_info->sender = vstring_alloc(100); 248 bounce_info->arrival_time = 0; 249 bounce_info->orig_offs = 0; 250 bounce_info->message_size = 0; 251 bounce_info->rcpt_buf = rcpt_buf; 252 bounce_info->dsn_buf = dsn_buf; 253 bounce_info->log_handle = log_handle; 254 255 /* 256 * RFC 1894: diagnostic-type is an RFC 822 atom. We use X-$mail_name and 257 * must ensure it is valid. 258 */ 259 bounce_info->mail_name = mystrdup(var_mail_name); 260 translit(bounce_info->mail_name, " \t\r\n()<>@,;:\\\".[]", 261 "-----------------"); 262 263 /* 264 * Compute a supposedly unique boundary string. This assumes that a queue 265 * ID and a hostname contain acceptable characters for a boundary string, 266 * but the assumption is not verified. 267 */ 268 vstring_sprintf(bounce_info->buf, "%s.%lu/%s", 269 queue_id, (unsigned long) event_time(), var_myhostname); 270 bounce_info->mime_boundary = mystrdup(STR(bounce_info->buf)); 271 272 /* 273 * If the original message cannot be found, do not raise a run-time 274 * error. There is nothing we can do about the error, and all we are 275 * doing is to inform the sender of a delivery problem. Bouncing a 276 * message does not have to be a perfect job. But if the system IS 277 * running out of resources, raise a fatal run-time error and force a 278 * backoff. 279 */ 280 if ((bounce_info->orig_fp = mail_queue_open(queue_name, queue_id, 281 O_RDWR, 0)) == 0 282 && errno != ENOENT) 283 msg_fatal("open %s %s: %m", service, queue_id); 284 285 /* 286 * Get time/size/sender information from the original message envelope 287 * records. If the envelope is corrupted just send whatever we can 288 * (remember this is a best effort, it does not have to be perfect). 289 * 290 * Lock the file for shared use, so that queue manager leaves it alone after 291 * restarting. 292 */ 293#define DELIVER_LOCK_MODE (MYFLOCK_OP_SHARED | MYFLOCK_OP_NOWAIT) 294 295 if (bounce_info->orig_fp != 0) { 296 if (myflock(vstream_fileno(bounce_info->orig_fp), INTERNAL_LOCK, 297 DELIVER_LOCK_MODE) < 0) 298 msg_fatal("cannot get shared lock on %s: %m", 299 VSTREAM_PATH(bounce_info->orig_fp)); 300 while ((rec_type = rec_get(bounce_info->orig_fp, 301 bounce_info->buf, 0)) > 0) { 302 303 /* 304 * Postfix version dependent: data offset in SIZE record. 305 */ 306 if (rec_type == REC_TYPE_SIZE) { 307 if (bounce_info->message_size == 0) 308 sscanf(STR(bounce_info->buf), "%ld %ld", 309 &bounce_info->message_size, 310 &bounce_info->orig_offs); 311 if (bounce_info->message_size < 0) 312 bounce_info->message_size = 0; 313 if (bounce_info->orig_offs < 0) 314 bounce_info->orig_offs = 0; 315 } 316 317 /* 318 * Information for the Arrival-Date: attribute. 319 */ 320 else if (rec_type == REC_TYPE_TIME) { 321 if (bounce_info->arrival_time == 0 322 && (bounce_info->arrival_time = atol(STR(bounce_info->buf))) < 0) 323 bounce_info->arrival_time = 0; 324 } 325 326 /* 327 * Information for the X-Postfix-Sender: attribute. 328 */ 329 else if (rec_type == REC_TYPE_FROM) { 330 quote_822_local_flags(bounce_info->sender, 331 VSTRING_LEN(bounce_info->buf) ? 332 STR(bounce_info->buf) : 333 mail_addr_mail_daemon(), 0); 334 } 335 336 /* 337 * Backwards compatibility: no data offset in SIZE record. 338 */ 339 else if (rec_type == REC_TYPE_MESG) { 340 /* XXX Future: sender+recipient after message content. */ 341 if (VSTRING_LEN(bounce_info->sender) == 0) 342 msg_warn("%s: no sender before message content record", 343 bounce_info->queue_id); 344 bounce_info->orig_offs = vstream_ftell(bounce_info->orig_fp); 345 break; 346 } 347 if (bounce_info->orig_offs > 0 348 && bounce_info->arrival_time > 0 349 && VSTRING_LEN(bounce_info->sender) > 0) 350 break; 351 } 352 } 353 return (bounce_info); 354} 355 356/* bounce_mail_init - initialize */ 357 358BOUNCE_INFO *bounce_mail_init(const char *service, 359 const char *queue_name, 360 const char *queue_id, 361 const char *encoding, 362 const char *dsn_envid, 363 BOUNCE_TEMPLATE *template) 364{ 365 BOUNCE_INFO *bounce_info; 366 BOUNCE_LOG *log_handle; 367 RCPT_BUF *rcpt_buf; 368 DSN_BUF *dsn_buf; 369 370 /* 371 * Initialize the bounce_info structure. If the bounce log cannot be 372 * found, do not raise a fatal run-time error. There is nothing we can do 373 * about the error, and all we are doing is to inform the sender of a 374 * delivery problem, Bouncing a message does not have to be a perfect 375 * job. But if the system IS running out of resources, raise a fatal 376 * run-time error and force a backoff. 377 */ 378 if ((log_handle = bounce_log_open(service, queue_id, O_RDONLY, 0)) == 0) { 379 if (errno != ENOENT) 380 msg_fatal("open %s %s: %m", service, queue_id); 381 rcpt_buf = 0; 382 dsn_buf = 0; 383 } else { 384 rcpt_buf = rcpb_create(); 385 dsn_buf = dsb_create(); 386 } 387 bounce_info = bounce_mail_alloc(service, queue_name, queue_id, encoding, 388 dsn_envid, rcpt_buf, dsn_buf, 389 template, log_handle); 390 return (bounce_info); 391} 392 393/* bounce_mail_one_init - initialize */ 394 395BOUNCE_INFO *bounce_mail_one_init(const char *queue_name, 396 const char *queue_id, 397 const char *encoding, 398 const char *dsn_envid, 399 RCPT_BUF *rcpt_buf, 400 DSN_BUF *dsn_buf, 401 BOUNCE_TEMPLATE *template) 402{ 403 BOUNCE_INFO *bounce_info; 404 405 /* 406 * Initialize the bounce_info structure for just one recipient. 407 */ 408 bounce_info = bounce_mail_alloc("none", queue_name, queue_id, encoding, 409 dsn_envid, rcpt_buf, dsn_buf, template, 410 (BOUNCE_LOG *) 0); 411 return (bounce_info); 412} 413 414/* bounce_mail_free - undo bounce_mail_init */ 415 416void bounce_mail_free(BOUNCE_INFO *bounce_info) 417{ 418 if (bounce_info->log_handle) { 419 if (bounce_log_close(bounce_info->log_handle)) 420 msg_warn("%s: read bounce log %s: %m", 421 bounce_info->queue_id, bounce_info->queue_id); 422 rcpb_free(bounce_info->rcpt_buf); 423 dsb_free(bounce_info->dsn_buf); 424 } 425 if (bounce_info->orig_fp && vstream_fclose(bounce_info->orig_fp)) 426 msg_warn("%s: read message file %s %s: %m", 427 bounce_info->queue_id, bounce_info->queue_name, 428 bounce_info->queue_id); 429 vstring_free(bounce_info->buf); 430 vstring_free(bounce_info->sender); 431 myfree(bounce_info->mail_name); 432 myfree((char *) bounce_info->mime_boundary); 433 myfree((char *) bounce_info); 434} 435 436/* bounce_header - generate bounce message header */ 437 438int bounce_header(VSTREAM *bounce, BOUNCE_INFO *bounce_info, 439 const char *dest, int postmaster_copy) 440{ 441 BOUNCE_TEMPLATE *template = bounce_info->template; 442 443 /* 444 * Print a minimal bounce header. The cleanup service will add other 445 * headers and will make all addresses fully qualified. 446 */ 447#define STREQ(a, b) (strcasecmp((a), (b)) == 0) 448 449 /* 450 * Generic headers. 451 */ 452 bounce_template_headers(post_mail_fprintf, bounce, template, 453 STR(quote_822_local(bounce_info->buf, dest)), 454 postmaster_copy); 455 456 /* 457 * Auto-Submitted header, as per RFC 3834. 458 */ 459 post_mail_fprintf(bounce, "Auto-Submitted: %s", postmaster_copy ? 460 "auto-generated" : "auto-replied"); 461 462 /* 463 * MIME header. Use 8bit encoding when either the bounced message or the 464 * template requires it. 465 */ 466 post_mail_fprintf(bounce, "MIME-Version: 1.0"); 467 post_mail_fprintf(bounce, "Content-Type: %s; report-type=%s;", 468 "multipart/report", "delivery-status"); 469 post_mail_fprintf(bounce, "\tboundary=\"%s\"", bounce_info->mime_boundary); 470 if (bounce_info->mime_encoding) 471 post_mail_fprintf(bounce, "Content-Transfer-Encoding: %s", 472 STREQ(bounce_info->mime_encoding, MAIL_ATTR_ENC_7BIT) ? 473 bounce_template_encoding(template) : 474 bounce_info->mime_encoding); 475 post_mail_fputs(bounce, ""); 476 post_mail_fputs(bounce, "This is a MIME-encapsulated message."); 477 478 /* 479 * MIME header. 480 */ 481 post_mail_fputs(bounce, ""); 482 post_mail_fprintf(bounce, "--%s", bounce_info->mime_boundary); 483 post_mail_fprintf(bounce, "Content-Description: %s", "Notification"); 484 post_mail_fprintf(bounce, "Content-Type: %s; charset=%s", 485 "text/plain", bounce_template_charset(template)); 486 post_mail_fputs(bounce, ""); 487 488 return (vstream_ferror(bounce)); 489} 490 491/* bounce_boilerplate - generate boiler-plate text */ 492 493int bounce_boilerplate(VSTREAM *bounce, BOUNCE_INFO *bounce_info) 494{ 495 496 /* 497 * Print the boiler-plate text. 498 */ 499 bounce_template_expand(post_mail_fputs, bounce, bounce_info->template); 500 return (vstream_ferror(bounce)); 501} 502 503/* bounce_print - line_wrap callback */ 504 505static void bounce_print(const char *str, int len, int indent, char *context) 506{ 507 VSTREAM *bounce = (VSTREAM *) context; 508 509 post_mail_fprintf(bounce, "%*s%.*s", indent, "", len, str); 510} 511 512/* bounce_print_wrap - print and wrap a line */ 513 514static void bounce_print_wrap(VSTREAM *bounce, BOUNCE_INFO *bounce_info, 515 const char *format,...) 516{ 517 va_list ap; 518 519#define LENGTH 79 520#define INDENT 4 521 522 va_start(ap, format); 523 vstring_vsprintf(bounce_info->buf, format, ap); 524 va_end(ap); 525 line_wrap(STR(bounce_info->buf), LENGTH, INDENT, 526 bounce_print, (char *) bounce); 527} 528 529/* bounce_recipient_log - send one bounce log report entry */ 530 531int bounce_recipient_log(VSTREAM *bounce, BOUNCE_INFO *bounce_info) 532{ 533 RECIPIENT *rcpt = &bounce_info->rcpt_buf->rcpt; 534 DSN *dsn = &bounce_info->dsn_buf->dsn; 535 536 /* 537 * Mask control and non-ASCII characters (done in bounce_log_read()), 538 * wrap long lines and prepend one blank, so this data can safely be 539 * piped into other programs. Sort of like TCP Wrapper's safe_finger 540 * program. 541 */ 542#define NON_NULL_EMPTY(s) ((s) && *(s)) 543 544 post_mail_fputs(bounce, ""); 545 if (NON_NULL_EMPTY(rcpt->orig_addr)) { 546 bounce_print_wrap(bounce, bounce_info, "<%s> (expanded from <%s>): %s", 547 rcpt->address, rcpt->orig_addr, dsn->reason); 548 } else { 549 bounce_print_wrap(bounce, bounce_info, "<%s>: %s", 550 rcpt->address, dsn->reason); 551 } 552 return (vstream_ferror(bounce)); 553} 554 555/* bounce_diagnostic_log - send bounce log report */ 556 557int bounce_diagnostic_log(VSTREAM *bounce, BOUNCE_INFO *bounce_info, 558 int notify_filter) 559{ 560 RECIPIENT *rcpt = &bounce_info->rcpt_buf->rcpt; 561 int count = 0; 562 563 /* 564 * Append a human-readable copy of the delivery error log. We're doing a 565 * best effort, so there is no point raising a fatal run-time error in 566 * case of a logfile read error. 567 * 568 * XXX DSN If the logfile with failed recipients is unavailable, pretend 569 * that we found something anyway, so that this notification will not be 570 * canceled. 571 */ 572 if (bounce_info->log_handle == 0 573 || bounce_log_rewind(bounce_info->log_handle)) { 574 if (IS_FAILURE_TEMPLATE(bounce_info->template)) { 575 post_mail_fputs(bounce, ""); 576 post_mail_fputs(bounce, "\t--- Delivery report unavailable ---"); 577 count = 1; /* XXX don't abort */ 578 } 579 } else { 580 while (bounce_log_read(bounce_info->log_handle, bounce_info->rcpt_buf, 581 bounce_info->dsn_buf) != 0) { 582 if (rcpt->dsn_notify == 0 /* compat */ 583 || (rcpt->dsn_notify & notify_filter)) { 584 count++; 585 if (bounce_recipient_log(bounce, bounce_info) != 0) 586 break; 587 } 588 } 589 } 590 return (vstream_ferror(bounce) ? -1 : count); 591} 592 593/* bounce_header_dsn - send per-MTA bounce DSN records */ 594 595int bounce_header_dsn(VSTREAM *bounce, BOUNCE_INFO *bounce_info) 596{ 597 598 /* 599 * MIME header. 600 */ 601 post_mail_fputs(bounce, ""); 602 post_mail_fprintf(bounce, "--%s", bounce_info->mime_boundary); 603 post_mail_fprintf(bounce, "Content-Description: %s", 604 "Delivery report"); 605 post_mail_fprintf(bounce, "Content-Type: %s", "message/delivery-status"); 606 607 /* 608 * According to RFC 1894: The body of a message/delivery-status consists 609 * of one or more "fields" formatted according to the ABNF of RFC 822 610 * header "fields" (see [6]). The per-message fields appear first, 611 * followed by a blank line. 612 */ 613 post_mail_fputs(bounce, ""); 614 post_mail_fprintf(bounce, "Reporting-MTA: dns; %s", var_myhostname); 615#if 0 616 post_mail_fprintf(bounce, "Received-From-MTA: dns; %s", "whatever"); 617#endif 618 if (NON_NULL_EMPTY(bounce_info->dsn_envid)) { 619 post_mail_fprintf(bounce, "Original-Envelope-Id: %s", 620 bounce_info->dsn_envid); 621 } 622 post_mail_fprintf(bounce, "X-%s-Queue-ID: %s", 623 bounce_info->mail_name, bounce_info->queue_id); 624 if (VSTRING_LEN(bounce_info->sender) > 0) 625 post_mail_fprintf(bounce, "X-%s-Sender: rfc822; %s", 626 bounce_info->mail_name, STR(bounce_info->sender)); 627 if (bounce_info->arrival_time > 0) 628 post_mail_fprintf(bounce, "Arrival-Date: %s", 629 mail_date(bounce_info->arrival_time)); 630 return (vstream_ferror(bounce)); 631} 632 633/* bounce_recipient_dsn - send per-recipient DSN records */ 634 635int bounce_recipient_dsn(VSTREAM *bounce, BOUNCE_INFO *bounce_info) 636{ 637 RECIPIENT *rcpt = &bounce_info->rcpt_buf->rcpt; 638 DSN *dsn = &bounce_info->dsn_buf->dsn; 639 640 post_mail_fputs(bounce, ""); 641 post_mail_fprintf(bounce, "Final-Recipient: rfc822; %s", rcpt->address); 642 643 /* 644 * XXX DSN 645 * 646 * RFC 3464 section 6.3.d: "If no ORCPT parameter was provided for this 647 * recipient, the Original-Recipient field MUST NOT appear." 648 * 649 * This is inconsistent with section 5.2.1.d: "If no ORCPT parameter was 650 * present in the RCPT command when the message was received, an ORCPT 651 * parameter MAY be added to the RCPT command when the message is 652 * relayed.". Postfix adds an ORCPT parameter under these conditions. 653 * 654 * Therefore, all down-stream MTAs will send DSNs with Original-Recipient 655 * field ontaining this same ORCPT value. When a down-stream MTA can use 656 * that information in their DSNs, it makes no sense that an up-stream 657 * MTA can't use that same information in its own DSNs. 658 * 659 * Postfix always reports an Original-Recipient field, because it is more 660 * more useful and more consistent. 661 */ 662 if (NON_NULL_EMPTY(rcpt->dsn_orcpt)) { 663 post_mail_fprintf(bounce, "Original-Recipient: %s", rcpt->dsn_orcpt); 664 } else if (NON_NULL_EMPTY(rcpt->orig_addr)) { 665 post_mail_fprintf(bounce, "Original-Recipient: rfc822; %s", 666 rcpt->orig_addr); 667 } 668 post_mail_fprintf(bounce, "Action: %s", 669 IS_FAILURE_TEMPLATE(bounce_info->template) ? 670 "failed" : dsn->action); 671 post_mail_fprintf(bounce, "Status: %s", dsn->status); 672 if (NON_NULL_EMPTY(dsn->mtype) && NON_NULL_EMPTY(dsn->mname)) 673 bounce_print_wrap(bounce, bounce_info, "Remote-MTA: %s; %s", 674 dsn->mtype, dsn->mname); 675 if (NON_NULL_EMPTY(dsn->dtype) && NON_NULL_EMPTY(dsn->dtext)) 676 bounce_print_wrap(bounce, bounce_info, "Diagnostic-Code: %s; %s", 677 dsn->dtype, dsn->dtext); 678 else 679 bounce_print_wrap(bounce, bounce_info, "Diagnostic-Code: X-%s; %s", 680 bounce_info->mail_name, dsn->reason); 681#if 0 682 if (dsn->time > 0) 683 post_mail_fprintf(bounce, "Last-Attempt-Date: %s", 684 mail_date(dsn->time)); 685#endif 686 if (IS_DELAY_TEMPLATE(bounce_info->template)) 687 post_mail_fprintf(bounce, "Will-Retry-Until: %s", 688 mail_date(bounce_info->arrival_time + var_max_queue_time)); 689 return (vstream_ferror(bounce)); 690} 691 692/* bounce_diagnostic_dsn - send bounce log report, machine readable form */ 693 694int bounce_diagnostic_dsn(VSTREAM *bounce, BOUNCE_INFO *bounce_info, 695 int notify_filter) 696{ 697 RECIPIENT *rcpt = &bounce_info->rcpt_buf->rcpt; 698 int count = 0; 699 700 /* 701 * Append a machine-readable copy of the delivery error log. We're doing 702 * a best effort, so there is no point raising a fatal run-time error in 703 * case of a logfile read error. 704 * 705 * XXX DSN If the logfile with failed recipients is unavailable, pretend 706 * that we found something anyway, so that this notification will not be 707 * canceled. 708 */ 709 if (bounce_info->log_handle == 0 710 || bounce_log_rewind(bounce_info->log_handle)) { 711 if (IS_FAILURE_TEMPLATE(bounce_info->template)) 712 count = 1; /* XXX don't abort */ 713 } else { 714 while (bounce_log_read(bounce_info->log_handle, bounce_info->rcpt_buf, 715 bounce_info->dsn_buf) != 0) { 716 if (rcpt->dsn_notify == 0 /* compat */ 717 || (rcpt->dsn_notify & notify_filter)) { 718 count++; 719 if (bounce_recipient_dsn(bounce, bounce_info) != 0) 720 break; 721 } 722 } 723 } 724 return (vstream_ferror(bounce) ? -1 : count); 725} 726 727/* bounce_original - send a copy of the original to the victim */ 728 729int bounce_original(VSTREAM *bounce, BOUNCE_INFO *bounce_info, 730 int headers_only) 731{ 732 int status = 0; 733 int rec_type = 0; 734 735 /* 736 * When truncating a large message, don't damage the MIME structure: send 737 * the message headers only. 738 */ 739 if (var_bounce_limit > 0 740 && bounce_info->orig_fp 741 && (bounce_info->message_size <= 0 742 || bounce_info->message_size > var_bounce_limit)) 743 headers_only = DSN_RET_HDRS; 744 745 /* 746 * MIME headers. 747 */ 748#define IS_UNDELIVERED_TEMPLATE(template) \ 749 (IS_FAILURE_TEMPLATE(template) || IS_DELAY_TEMPLATE(template)) 750 751 post_mail_fputs(bounce, ""); 752 post_mail_fprintf(bounce, "--%s", bounce_info->mime_boundary); 753 post_mail_fprintf(bounce, "Content-Description: %s%s", 754 IS_UNDELIVERED_TEMPLATE(bounce_info->template) ? 755 "Undelivered " : "", 756 headers_only == DSN_RET_HDRS ? 757 "Message Headers" : "Message"); 758 post_mail_fprintf(bounce, "Content-Type: %s", 759 headers_only == DSN_RET_HDRS ? 760 "text/rfc822-headers" : "message/rfc822"); 761 if (bounce_info->mime_encoding) 762 post_mail_fprintf(bounce, "Content-Transfer-Encoding: %s", 763 bounce_info->mime_encoding); 764 post_mail_fputs(bounce, ""); 765 766 /* 767 * Send place holder if original is unavailable. 768 */ 769 if (bounce_info->orig_offs == 0 || vstream_fseek(bounce_info->orig_fp, 770 bounce_info->orig_offs, SEEK_SET) < 0) { 771 post_mail_fputs(bounce, "\t--- Undelivered message unavailable ---"); 772 return (vstream_ferror(bounce)); 773 } 774 775 /* 776 * XXX The cleanup server removes Return-Path: headers. This should be 777 * done only with mail that enters via a non-SMTP channel, but changing 778 * this now could break other software. Removing Return-Path: could break 779 * digital signatures, though this is unlikely. In any case, 780 * header_checks are more effective when the Return-Path: header is 781 * present, so we prepend one to the bounce message. 782 */ 783 post_mail_fprintf(bounce, "Return-Path: <%s>", STR(bounce_info->sender)); 784 785 /* 786 * Copy the original message contents. We're doing raw record output here 787 * so that we don't throw away binary transparency yet. 788 */ 789#define IS_HEADER(s) (IS_SPACE_TAB(*(s)) || is_header(s)) 790 791 while (status == 0 && (rec_type = rec_get(bounce_info->orig_fp, bounce_info->buf, 0)) > 0) { 792 if (rec_type != REC_TYPE_NORM && rec_type != REC_TYPE_CONT) 793 break; 794 if (headers_only == DSN_RET_HDRS 795 && !IS_HEADER(vstring_str(bounce_info->buf))) 796 break; 797 status = (REC_PUT_BUF(bounce, rec_type, bounce_info->buf) != rec_type); 798 } 799 800 /* 801 * Final MIME headers. These require -- at the end of the boundary 802 * string. 803 * 804 * XXX This should be a separate bounce_terminate() entry so we can be 805 * assured that the terminator will always be sent. 806 */ 807 post_mail_fputs(bounce, ""); 808 post_mail_fprintf(bounce, "--%s--", bounce_info->mime_boundary); 809 810 return (status); 811} 812 813/* bounce_delrcpt - delete recipients from original queue file */ 814 815void bounce_delrcpt(BOUNCE_INFO *bounce_info) 816{ 817 RECIPIENT *rcpt = &bounce_info->rcpt_buf->rcpt; 818 819 if (bounce_info->orig_fp != 0 820 && bounce_info->log_handle != 0 821 && bounce_log_rewind(bounce_info->log_handle) == 0) 822 while (bounce_log_read(bounce_info->log_handle, bounce_info->rcpt_buf, 823 bounce_info->dsn_buf) != 0) 824 if (rcpt->offset > 0) 825 deliver_completed(bounce_info->orig_fp, rcpt->offset); 826} 827 828/* bounce_delrcpt_one - delete one recipient from original queue file */ 829 830void bounce_delrcpt_one(BOUNCE_INFO *bounce_info) 831{ 832 RECIPIENT *rcpt = &bounce_info->rcpt_buf->rcpt; 833 834 if (bounce_info->orig_fp != 0 && rcpt->offset > 0) 835 deliver_completed(bounce_info->orig_fp, rcpt->offset); 836} 837