1 /* $NetBSD: bounce_notify_util.c,v 1.1.1.1 2009/06/23 10:08:42 tron Exp $ */ 2 3 /*++ 4 /* NAME 5 /* bounce_notify_util 3 6 /* SUMMARY 7 /* send non-delivery report to sender, server side 8 /* SYNOPSIS 9 /* #include "bounce_service.h" 10 /* 11 /* typedef struct { 12 /* .in +4 13 /* /* All private members... */ 14 /* .in -4 15 /* } BOUNCE_INFO; 16 /* 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; 25 /* 26 /* BOUNCE_INFO *bounce_mail_one_init(queue_name, queue_id, encoding, 27 /* dsn_envid, dsn_notify, rcpt_buf, 28 /* dsn_buf, template) 29 /* const char *queue_name; 30 /* const char *queue_id; 31 /* const char *encoding; 32 /* int dsn_notify; 33 /* const char *dsn_envid; 34 /* RCPT_BUF *rcpt_buf; 35 /* DSN_BUF *dsn_buf; 36 /* const BOUNCE_TEMPLATE *template; 37 /* 38 /* void bounce_mail_free(bounce_info) 39 /* BOUNCE_INFO *bounce_info; 40 /* 41 /* int bounce_header(fp, bounce_info, recipient, postmaster_copy) 42 /* VSTREAM *fp; 43 /* BOUNCE_INFO *bounce_info; 44 /* const char *recipient; 45 /* int postmaster_copy; 46 /* 47 /* int bounce_boilerplate(fp, bounce_info) 48 /* VSTREAM *fp; 49 /* BOUNCE_INFO *bounce_info; 50 /* 51 /* int bounce_recipient_log(fp, bounce_info) 52 /* VSTREAM *fp; 53 /* BOUNCE_INFO *bounce_info; 54 /* 55 /* int bounce_diagnostic_log(fp, bounce_info, notify_filter) 56 /* VSTREAM *fp; 57 /* BOUNCE_INFO *bounce_info; 58 /* int notify_filter; 59 /* 60 /* int bounce_header_dsn(fp, bounce_info) 61 /* VSTREAM *fp; 62 /* BOUNCE_INFO *bounce_info; 63 /* 64 /* int bounce_recipient_dsn(fp, bounce_info) 65 /* VSTREAM *fp; 66 /* BOUNCE_INFO *bounce_info; 67 /* 68 /* int bounce_diagnostic_dsn(fp, bounce_info, notify_filter) 69 /* VSTREAM *fp; 70 /* BOUNCE_INFO *bounce_info; 71 /* int notify_filter; 72 /* 73 /* int bounce_original(fp, bounce_info, headers_only) 74 /* VSTREAM *fp; 75 /* BOUNCE_INFO *bounce_info; 76 /* int headers_only; 77 /* 78 /* void bounce_delrcpt(bounce_info) 79 /* BOUNCE_INFO *bounce_info; 80 /* 81 /* void bounce_delrcpt_one(bounce_info) 82 /* BOUNCE_INFO *bounce_info; 83 /* DESCRIPTION 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). 87 /* 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. 92 /* 93 /* bounce_mail_one_init() provides the same function for only 94 /* one recipient that is not read from bounce logfile. 95 /* 96 /* bounce_mail_free() releases memory allocated by bounce_mail_init() 97 /* and closes any files opened by bounce_mail_init(). 98 /* 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. 103 /* 104 /* bounce_boilerplate() produces the standard "sorry" text that 105 /* creates the illusion that mail systems are civilized. 106 /* 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. 110 /* 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. 118 /* 119 /* bounce_header_dsn() starts a message/delivery-status message 120 /* segment and sends the machine-readable information that identifies 121 /* the reporting MTA. 122 /* 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. 126 /* 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. 134 /* 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). 138 /* 139 /* bounce_delrcpt() deletes recipients in the logfile from the original 140 /* queue file. 141 /* 142 /* bounce_delrcpt_one() deletes one recipient from the original 143 /* queue file. 144 /* DIAGNOSTICS 145 /* Fatal error: error opening existing file. 146 /* BUGS 147 /* SEE ALSO 148 /* bounce(3) basic bounce service client interface 149 /* LICENSE 150 /* .ad 151 /* .fi 152 /* The Secure Mailer license must be distributed with this software. 153 /* AUTHOR(S) 154 /* Wietse Venema 155 /* IBM T.J. Watson Research 156 /* P.O. Box 704 157 /* Yorktown Heights, NY 10598, USA 158 /*--*/ 159 160 /* System library. */ 161 162 #include <sys_defs.h> 163 #include <sys/stat.h> 164 #include <stdlib.h> 165 #include <stdio.h> /* sscanf() */ 166 #include <unistd.h> 167 #include <errno.h> 168 #include <string.h> 169 #include <ctype.h> 170 171 #ifdef STRCASECMP_IN_STRINGS_H 172 #include <strings.h> 173 #endif 174 175 /* Utility library. */ 176 177 #include <msg.h> 178 #include <mymalloc.h> 179 #include <events.h> 180 #include <vstring.h> 181 #include <vstream.h> 182 #include <line_wrap.h> 183 #include <stringops.h> 184 #include <myflock.h> 185 186 /* Global library. */ 187 188 #include <mail_queue.h> 189 #include <quote_822_local.h> 190 #include <mail_params.h> 191 #include <is_header.h> 192 #include <record.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> 200 #include <lex_822.h> 201 #include <deliver_completed.h> 202 #include <dsn_mask.h> 203 204 /* Application-specific. */ 205 206 #include "bounce_service.h" 207 208 #define STR vstring_str 209 210 /* bounce_mail_alloc - initialize */ 211 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, 217 RCPT_BUF *rcpt_buf, 218 DSN_BUF *dsn_buf, 219 BOUNCE_TEMPLATE *template, 220 BOUNCE_LOG *log_handle) 221 { 222 BOUNCE_INFO *bounce_info; 223 int rec_type; 224 225 /* 226 * Bundle up a bunch of parameters and initialize information that will 227 * be discovered on the fly. 228 */ 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"; 237 } else { 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; 242 } 243 if (dsn_envid && *dsn_envid) 244 bounce_info->dsn_envid = dsn_envid; 245 else 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; 256 257 /* 258 * RFC 1894: diagnostic-type is an RFC 822 atom. We use X-$mail_name and 259 * must ensure it is valid. 260 */ 261 bounce_info->mail_name = mystrdup(var_mail_name); 262 translit(bounce_info->mail_name, " \t\r\n()<>@,;:\\\".[]", 263 "-----------------"); 264 265 /* 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. 269 */ 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)); 273 274 /* 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 280 * backoff. 281 */ 282 if ((bounce_info->orig_fp = mail_queue_open(queue_name, queue_id, 283 O_RDWR, 0)) == 0 284 && errno != ENOENT) 285 msg_fatal("open %s %s: %m", service, queue_id); 286 287 /* 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). 291 * 292 * Lock the file for shared use, so that queue manager leaves it alone after 293 * restarting. 294 */ 295 #define DELIVER_LOCK_MODE (MYFLOCK_OP_SHARED | MYFLOCK_OP_NOWAIT) 296 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) { 304 305 /* 306 * Postfix version dependent: data offset in SIZE record. 307 */ 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; 317 } 318 319 /* 320 * Information for the Arrival-Date: attribute. 321 */ 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; 326 } 327 328 /* 329 * Information for the X-Postfix-Sender: attribute. 330 */ 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); 336 } 337 338 /* 339 * Backwards compatibility: no data offset in SIZE record. 340 */ 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); 347 break; 348 } 349 if (bounce_info->orig_offs > 0 350 && bounce_info->arrival_time > 0 351 && VSTRING_LEN(bounce_info->sender) > 0) 352 break; 353 } 354 } 355 return (bounce_info); 356 } 357 358 /* bounce_mail_init - initialize */ 359 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) 366 { 367 BOUNCE_INFO *bounce_info; 368 BOUNCE_LOG *log_handle; 369 RCPT_BUF *rcpt_buf; 370 DSN_BUF *dsn_buf; 371 372 /* 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. 379 */ 380 if ((log_handle = bounce_log_open(service, queue_id, O_RDONLY, 0)) == 0) { 381 if (errno != ENOENT) 382 msg_fatal("open %s %s: %m", service, queue_id); 383 rcpt_buf = 0; 384 dsn_buf = 0; 385 } else { 386 rcpt_buf = rcpb_create(); 387 dsn_buf = dsb_create(); 388 } 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); 393 } 394 395 /* bounce_mail_one_init - initialize */ 396 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, 401 RCPT_BUF *rcpt_buf, 402 DSN_BUF *dsn_buf, 403 BOUNCE_TEMPLATE *template) 404 { 405 BOUNCE_INFO *bounce_info; 406 407 /* 408 * Initialize the bounce_info structure for just one recipient. 409 */ 410 bounce_info = bounce_mail_alloc("none", queue_name, queue_id, encoding, 411 dsn_envid, rcpt_buf, dsn_buf, template, 412 (BOUNCE_LOG *) 0); 413 return (bounce_info); 414 } 415 416 /* bounce_mail_free - undo bounce_mail_init */ 417 418 void bounce_mail_free(BOUNCE_INFO *bounce_info) 419 { 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); 426 } 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); 436 } 437 438 /* bounce_header - generate bounce message header */ 439 440 int bounce_header(VSTREAM *bounce, BOUNCE_INFO *bounce_info, 441 const char *dest, int postmaster_copy) 442 { 443 BOUNCE_TEMPLATE *template = bounce_info->template; 444 445 /* 446 * Print a minimal bounce header. The cleanup service will add other 447 * headers and will make all addresses fully qualified. 448 */ 449 #define STREQ(a, b) (strcasecmp((a), (b)) == 0) 450 451 /* 452 * Generic headers. 453 */ 454 bounce_template_headers(post_mail_fprintf, bounce, template, 455 STR(quote_822_local(bounce_info->buf, dest)), 456 postmaster_copy); 457 458 /* 459 * Auto-Submitted header, as per RFC 3834. 460 */ 461 post_mail_fprintf(bounce, "Auto-Submitted: %s", postmaster_copy ? 462 "auto-generated" : "auto-replied"); 463 464 /* 465 * MIME header. Use 8bit encoding when either the bounced message or the 466 * template requires it. 467 */ 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."); 479 480 /* 481 * MIME header. 482 */ 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, ""); 489 490 return (vstream_ferror(bounce)); 491 } 492 493 /* bounce_boilerplate - generate boiler-plate text */ 494 495 int bounce_boilerplate(VSTREAM *bounce, BOUNCE_INFO *bounce_info) 496 { 497 498 /* 499 * Print the boiler-plate text. 500 */ 501 bounce_template_expand(post_mail_fputs, bounce, bounce_info->template); 502 return (vstream_ferror(bounce)); 503 } 504 505 /* bounce_print - line_wrap callback */ 506 507 static void bounce_print(const char *str, int len, int indent, char *context) 508 { 509 VSTREAM *bounce = (VSTREAM *) context; 510 511 post_mail_fprintf(bounce, "%*s%.*s", indent, "", len, str); 512 } 513 514 /* bounce_print_wrap - print and wrap a line */ 515 516 static void bounce_print_wrap(VSTREAM *bounce, BOUNCE_INFO *bounce_info, 517 const char *format,...) 518 { 519 va_list ap; 520 521 #define LENGTH 79 522 #define INDENT 4 523 524 va_start(ap, format); 525 vstring_vsprintf(bounce_info->buf, format, ap); 526 va_end(ap); 527 line_wrap(STR(bounce_info->buf), LENGTH, INDENT, 528 bounce_print, (char *) bounce); 529 } 530 531 /* bounce_recipient_log - send one bounce log report entry */ 532 533 int bounce_recipient_log(VSTREAM *bounce, BOUNCE_INFO *bounce_info) 534 { 535 RECIPIENT *rcpt = &bounce_info->rcpt_buf->rcpt; 536 DSN *dsn = &bounce_info->dsn_buf->dsn; 537 538 /* 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 542 * program. 543 */ 544 #define NON_NULL_EMPTY(s) ((s) && *(s)) 545 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); 550 } else { 551 bounce_print_wrap(bounce, bounce_info, "<%s>: %s", 552 rcpt->address, dsn->reason); 553 } 554 return (vstream_ferror(bounce)); 555 } 556 557 /* bounce_diagnostic_log - send bounce log report */ 558 559 int bounce_diagnostic_log(VSTREAM *bounce, BOUNCE_INFO *bounce_info, 560 int notify_filter) 561 { 562 RECIPIENT *rcpt = &bounce_info->rcpt_buf->rcpt; 563 int count = 0; 564 565 /* 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. 569 * 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 572 * canceled. 573 */ 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 */ 580 } 581 } else { 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)) { 586 count++; 587 if (bounce_recipient_log(bounce, bounce_info) != 0) 588 break; 589 } 590 } 591 } 592 return (vstream_ferror(bounce) ? -1 : count); 593 } 594 595 /* bounce_header_dsn - send per-MTA bounce DSN records */ 596 597 int bounce_header_dsn(VSTREAM *bounce, BOUNCE_INFO *bounce_info) 598 { 599 600 /* 601 * MIME header. 602 */ 603 post_mail_fputs(bounce, ""); 604 post_mail_fprintf(bounce, "--%s", bounce_info->mime_boundary); 605 post_mail_fprintf(bounce, "Content-Description: %s", 606 "Delivery report"); 607 post_mail_fprintf(bounce, "Content-Type: %s", "message/delivery-status"); 608 609 /* 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. 614 */ 615 post_mail_fputs(bounce, ""); 616 post_mail_fprintf(bounce, "Reporting-MTA: dns; %s", var_myhostname); 617 #if 0 618 post_mail_fprintf(bounce, "Received-From-MTA: dns; %s", "whatever"); 619 #endif 620 if (NON_NULL_EMPTY(bounce_info->dsn_envid)) { 621 post_mail_fprintf(bounce, "Original-Envelope-Id: %s", 622 bounce_info->dsn_envid); 623 } 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)); 633 } 634 635 /* bounce_recipient_dsn - send per-recipient DSN records */ 636 637 int bounce_recipient_dsn(VSTREAM *bounce, BOUNCE_INFO *bounce_info) 638 { 639 RECIPIENT *rcpt = &bounce_info->rcpt_buf->rcpt; 640 DSN *dsn = &bounce_info->dsn_buf->dsn; 641 642 post_mail_fputs(bounce, ""); 643 post_mail_fprintf(bounce, "Final-Recipient: rfc822; %s", rcpt->address); 644 645 /* 646 * XXX DSN 647 * 648 * RFC 3464 section 6.3.d: "If no ORCPT parameter was provided for this 649 * recipient, the Original-Recipient field MUST NOT appear." 650 * 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. 655 * 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. 660 * 661 * Postfix always reports an Original-Recipient field, because it is more 662 * more useful and more consistent. 663 */ 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", 668 rcpt->orig_addr); 669 } 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); 680 else 681 bounce_print_wrap(bounce, bounce_info, "Diagnostic-Code: X-%s; %s", 682 bounce_info->mail_name, dsn->reason); 683 #if 0 684 if (dsn->time > 0) 685 post_mail_fprintf(bounce, "Last-Attempt-Date: %s", 686 mail_date(dsn->time)); 687 #endif 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)); 692 } 693 694 /* bounce_diagnostic_dsn - send bounce log report, machine readable form */ 695 696 int bounce_diagnostic_dsn(VSTREAM *bounce, BOUNCE_INFO *bounce_info, 697 int notify_filter) 698 { 699 RECIPIENT *rcpt = &bounce_info->rcpt_buf->rcpt; 700 int count = 0; 701 702 /* 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. 706 * 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 709 * canceled. 710 */ 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 */ 715 } else { 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)) { 720 count++; 721 if (bounce_recipient_dsn(bounce, bounce_info) != 0) 722 break; 723 } 724 } 725 } 726 return (vstream_ferror(bounce) ? -1 : count); 727 } 728 729 /* bounce_original - send a copy of the original to the victim */ 730 731 int bounce_original(VSTREAM *bounce, BOUNCE_INFO *bounce_info, 732 int headers_only) 733 { 734 int status = 0; 735 int rec_type = 0; 736 737 /* 738 * When truncating a large message, don't damage the MIME structure: send 739 * the message headers only. 740 */ 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; 746 747 /* 748 * MIME headers. 749 */ 750 #define IS_UNDELIVERED_TEMPLATE(template) \ 751 (IS_FAILURE_TEMPLATE(template) || IS_DELAY_TEMPLATE(template)) 752 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) ? 757 "Undelivered " : "", 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, ""); 767 768 /* 769 * Send place holder if original is unavailable. 770 */ 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)); 775 } 776 777 /* 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. 784 */ 785 post_mail_fprintf(bounce, "Return-Path: <%s>", STR(bounce_info->sender)); 786 787 /* 788 * Copy the original message contents. We're doing raw record output here 789 * so that we don't throw away binary transparency yet. 790 */ 791 #define IS_HEADER(s) (IS_SPACE_TAB(*(s)) || is_header(s)) 792 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) 795 break; 796 if (headers_only == DSN_RET_HDRS 797 && !IS_HEADER(vstring_str(bounce_info->buf))) 798 break; 799 status = (REC_PUT_BUF(bounce, rec_type, bounce_info->buf) != rec_type); 800 } 801 802 /* 803 * Final MIME headers. These require -- at the end of the boundary 804 * string. 805 * 806 * XXX This should be a separate bounce_terminate() entry so we can be 807 * assured that the terminator will always be sent. 808 */ 809 post_mail_fputs(bounce, ""); 810 post_mail_fprintf(bounce, "--%s--", bounce_info->mime_boundary); 811 812 return (status); 813 } 814 815 /* bounce_delrcpt - delete recipients from original queue file */ 816 817 void bounce_delrcpt(BOUNCE_INFO *bounce_info) 818 { 819 RECIPIENT *rcpt = &bounce_info->rcpt_buf->rcpt; 820 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); 828 } 829 830 /* bounce_delrcpt_one - delete one recipient from original queue file */ 831 832 void bounce_delrcpt_one(BOUNCE_INFO *bounce_info) 833 { 834 RECIPIENT *rcpt = &bounce_info->rcpt_buf->rcpt; 835 836 if (bounce_info->orig_fp != 0 && rcpt->offset > 0) 837 deliver_completed(bounce_info->orig_fp, rcpt->offset); 838 } 839