1 /* 2 * Copyright (c) 1983, 1995 Eric P. Allman 3 * Copyright (c) 1988, 1993 4 * The Regents of the University of California. All rights reserved. 5 * 6 * %sccs.include.redist.c% 7 */ 8 9 #ifndef lint 10 static char sccsid[] = "@(#)savemail.c 8.71 (Berkeley) 05/28/95"; 11 #endif /* not lint */ 12 13 # include "sendmail.h" 14 15 /* 16 ** SAVEMAIL -- Save mail on error 17 ** 18 ** If mailing back errors, mail it back to the originator 19 ** together with an error message; otherwise, just put it in 20 ** dead.letter in the user's home directory (if he exists on 21 ** this machine). 22 ** 23 ** Parameters: 24 ** e -- the envelope containing the message in error. 25 ** sendbody -- if TRUE, also send back the body of the 26 ** message; otherwise just send the header. 27 ** 28 ** Returns: 29 ** none 30 ** 31 ** Side Effects: 32 ** Saves the letter, by writing or mailing it back to the 33 ** sender, or by putting it in dead.letter in her home 34 ** directory. 35 */ 36 37 /* defines for state machine */ 38 # define ESM_REPORT 0 /* report to sender's terminal */ 39 # define ESM_MAIL 1 /* mail back to sender */ 40 # define ESM_QUIET 2 /* messages have already been returned */ 41 # define ESM_DEADLETTER 3 /* save in ~/dead.letter */ 42 # define ESM_POSTMASTER 4 /* return to postmaster */ 43 # define ESM_USRTMP 5 /* save in /usr/tmp/dead.letter */ 44 # define ESM_PANIC 6 /* leave the locked queue/transcript files */ 45 # define ESM_DONE 7 /* the message is successfully delivered */ 46 47 # ifndef _PATH_VARTMP 48 # define _PATH_VARTMP "/usr/tmp/" 49 # endif 50 51 52 void 53 savemail(e, sendbody) 54 register ENVELOPE *e; 55 bool sendbody; 56 { 57 register struct passwd *pw; 58 register FILE *fp; 59 int state; 60 auto ADDRESS *q = NULL; 61 register char *p; 62 MCI mcibuf; 63 int sfflags; 64 char buf[MAXLINE+1]; 65 extern char *ttypath(); 66 typedef int (*fnptr)(); 67 extern bool writable(); 68 69 if (tTd(6, 1)) 70 { 71 printf("\nsavemail, errormode = %c, id = %s, ExitStat = %d\n e_from=", 72 e->e_errormode, e->e_id == NULL ? "NONE" : e->e_id, 73 ExitStat); 74 printaddr(&e->e_from, FALSE); 75 } 76 77 if (e->e_id == NULL) 78 { 79 /* can't return a message with no id */ 80 return; 81 } 82 83 /* 84 ** In the unhappy event we don't know who to return the mail 85 ** to, make someone up. 86 */ 87 88 if (e->e_from.q_paddr == NULL) 89 { 90 e->e_sender = "Postmaster"; 91 if (parseaddr(e->e_sender, &e->e_from, 92 RF_COPYPARSE|RF_SENDERADDR, '\0', NULL, e) == NULL) 93 { 94 syserr("553 Cannot parse Postmaster!"); 95 ExitStat = EX_SOFTWARE; 96 finis(); 97 } 98 } 99 e->e_to = NULL; 100 101 /* 102 ** Basic state machine. 103 ** 104 ** This machine runs through the following states: 105 ** 106 ** ESM_QUIET Errors have already been printed iff the 107 ** sender is local. 108 ** ESM_REPORT Report directly to the sender's terminal. 109 ** ESM_MAIL Mail response to the sender. 110 ** ESM_DEADLETTER Save response in ~/dead.letter. 111 ** ESM_POSTMASTER Mail response to the postmaster. 112 ** ESM_PANIC Save response anywhere possible. 113 */ 114 115 /* determine starting state */ 116 switch (e->e_errormode) 117 { 118 case EM_WRITE: 119 state = ESM_REPORT; 120 break; 121 122 case EM_BERKNET: 123 /* mail back, but return o.k. exit status */ 124 ExitStat = EX_OK; 125 126 /* fall through.... */ 127 128 case EM_MAIL: 129 state = ESM_MAIL; 130 break; 131 132 case EM_PRINT: 133 case '\0': 134 state = ESM_QUIET; 135 break; 136 137 case EM_QUIET: 138 /* no need to return anything at all */ 139 return; 140 141 default: 142 syserr("554 savemail: bogus errormode x%x\n", e->e_errormode); 143 state = ESM_MAIL; 144 break; 145 } 146 147 /* if this is already an error response, send to postmaster */ 148 if (bitset(EF_RESPONSE, e->e_flags)) 149 { 150 if (e->e_parent != NULL && 151 bitset(EF_RESPONSE, e->e_parent->e_flags)) 152 { 153 /* got an error sending a response -- can it */ 154 return; 155 } 156 state = ESM_POSTMASTER; 157 } 158 159 while (state != ESM_DONE) 160 { 161 if (tTd(6, 5)) 162 printf(" state %d\n", state); 163 164 switch (state) 165 { 166 case ESM_QUIET: 167 if (bitnset(M_LOCALMAILER, e->e_from.q_mailer->m_flags)) 168 state = ESM_DEADLETTER; 169 else 170 state = ESM_MAIL; 171 break; 172 173 case ESM_REPORT: 174 175 /* 176 ** If the user is still logged in on the same terminal, 177 ** then write the error messages back to hir (sic). 178 */ 179 180 p = ttypath(); 181 if (p == NULL || freopen(p, "w", stdout) == NULL) 182 { 183 state = ESM_MAIL; 184 break; 185 } 186 187 expand("\201n", buf, sizeof buf, e); 188 printf("\r\nMessage from %s...\r\n", buf); 189 printf("Errors occurred while sending mail.\r\n"); 190 if (e->e_xfp != NULL) 191 { 192 (void) fflush(e->e_xfp); 193 fp = fopen(queuename(e, 'x'), "r"); 194 } 195 else 196 fp = NULL; 197 if (fp == NULL) 198 { 199 syserr("Cannot open %s", queuename(e, 'x')); 200 printf("Transcript of session is unavailable.\r\n"); 201 } 202 else 203 { 204 printf("Transcript follows:\r\n"); 205 while (fgets(buf, sizeof buf, fp) != NULL && 206 !ferror(stdout)) 207 fputs(buf, stdout); 208 (void) xfclose(fp, "savemail transcript", e->e_id); 209 } 210 printf("Original message will be saved in dead.letter.\r\n"); 211 state = ESM_DEADLETTER; 212 break; 213 214 case ESM_MAIL: 215 /* 216 ** If mailing back, do it. 217 ** Throw away all further output. Don't alias, 218 ** since this could cause loops, e.g., if joe 219 ** mails to joe@x, and for some reason the network 220 ** for @x is down, then the response gets sent to 221 ** joe@x, which gives a response, etc. Also force 222 ** the mail to be delivered even if a version of 223 ** it has already been sent to the sender. 224 ** 225 ** If this is a configuration or local software 226 ** error, send to the local postmaster as well, 227 ** since the originator can't do anything 228 ** about it anyway. Note that this is a full 229 ** copy of the message (intentionally) so that 230 ** the Postmaster can forward things along. 231 */ 232 233 if (ExitStat == EX_CONFIG || ExitStat == EX_SOFTWARE) 234 { 235 (void) sendtolist("postmaster", 236 NULLADDR, &e->e_errorqueue, 0, e); 237 } 238 if (!emptyaddr(&e->e_from)) 239 { 240 (void) sendtolist(e->e_from.q_paddr, 241 NULLADDR, &e->e_errorqueue, 0, e); 242 } 243 244 /* 245 ** Deliver a non-delivery report to the 246 ** Postmaster-designate (not necessarily 247 ** Postmaster). This does not include the 248 ** body of the message, for privacy reasons. 249 ** You really shouldn't need this. 250 */ 251 252 e->e_flags |= EF_PM_NOTIFY; 253 254 /* check to see if there are any good addresses */ 255 for (q = e->e_errorqueue; q != NULL; q = q->q_next) 256 if (!bitset(QBADADDR|QDONTSEND, q->q_flags)) 257 break; 258 if (q == NULL) 259 { 260 /* this is an error-error */ 261 state = ESM_POSTMASTER; 262 break; 263 } 264 if (returntosender(e->e_message, e->e_errorqueue, 265 sendbody, e) == 0) 266 { 267 state = ESM_DONE; 268 break; 269 } 270 271 /* didn't work -- return to postmaster */ 272 state = ESM_POSTMASTER; 273 break; 274 275 case ESM_POSTMASTER: 276 /* 277 ** Similar to previous case, but to system postmaster. 278 */ 279 280 q = NULL; 281 if (sendtolist("postmaster", NULL, &q, 0, e) <= 0) 282 { 283 syserr("553 cannot parse postmaster!"); 284 ExitStat = EX_SOFTWARE; 285 state = ESM_USRTMP; 286 break; 287 } 288 if (returntosender(e->e_message, q, sendbody, e) == 0) 289 { 290 state = ESM_DONE; 291 break; 292 } 293 294 /* didn't work -- last resort */ 295 state = ESM_USRTMP; 296 break; 297 298 case ESM_DEADLETTER: 299 /* 300 ** Save the message in dead.letter. 301 ** If we weren't mailing back, and the user is 302 ** local, we should save the message in 303 ** ~/dead.letter so that the poor person doesn't 304 ** have to type it over again -- and we all know 305 ** what poor typists UNIX users are. 306 */ 307 308 p = NULL; 309 if (bitnset(M_HASPWENT, e->e_from.q_mailer->m_flags)) 310 { 311 if (e->e_from.q_home != NULL) 312 p = e->e_from.q_home; 313 else if ((pw = sm_getpwnam(e->e_from.q_user)) != NULL) 314 p = pw->pw_dir; 315 } 316 if (p == NULL || e->e_dfp == NULL) 317 { 318 /* no local directory or no data file */ 319 state = ESM_MAIL; 320 break; 321 } 322 323 /* we have a home directory; open dead.letter */ 324 define('z', p, e); 325 expand("\201z/dead.letter", buf, sizeof buf, e); 326 sfflags = SFF_NOSLINK|SFF_CREAT|SFF_REGONLY|SFF_RUNASREALUID; 327 e->e_to = buf; 328 goto writefile; 329 330 case ESM_USRTMP: 331 /* 332 ** Log the mail in /usr/tmp/dead.letter. 333 */ 334 335 if (e->e_class < 0) 336 { 337 state = ESM_DONE; 338 break; 339 } 340 341 if (SafeFileEnv != NULL && SafeFileEnv[0] != '\0') 342 { 343 state = ESM_PANIC; 344 break; 345 } 346 347 strcpy(buf, _PATH_VARTMP); 348 strcat(buf, "dead.letter"); 349 sfflags = SFF_NOSLINK|SFF_CREAT|SFF_REGONLY; 350 351 writefile: 352 if (!writable(buf, q, sfflags) || 353 (fp = safefopen(buf, O_WRONLY|O_CREAT|O_APPEND, 354 FileMode, sfflags)) == NULL) 355 { 356 if (state == ESM_USRTMP) 357 state = ESM_PANIC; 358 else 359 state = ESM_MAIL; 360 break; 361 } 362 363 bzero(&mcibuf, sizeof mcibuf); 364 mcibuf.mci_out = fp; 365 mcibuf.mci_mailer = FileMailer; 366 if (bitnset(M_7BITS, FileMailer->m_flags)) 367 mcibuf.mci_flags |= MCIF_7BIT; 368 369 putfromline(&mcibuf, e); 370 (*e->e_puthdr)(&mcibuf, e->e_header, e); 371 (*e->e_putbody)(&mcibuf, e, NULL); 372 putline("\n", &mcibuf); 373 (void) fflush(fp); 374 if (!ferror(fp)) 375 { 376 bool oldverb = Verbose; 377 378 Verbose = TRUE; 379 message("Saved message in %s", buf); 380 Verbose = oldverb; 381 state = ESM_DONE; 382 } 383 else if (state == ESM_USRTMP) 384 state = ESM_PANIC; 385 else 386 state = ESM_MAIL; 387 (void) xfclose(fp, "savemail", buf); 388 break; 389 390 default: 391 syserr("554 savemail: unknown state %d", state); 392 393 /* fall through ... */ 394 395 case ESM_PANIC: 396 /* leave the locked queue & transcript files around */ 397 loseqfile(e, "savemail panic"); 398 syserr("!554 savemail: cannot save rejected email anywhere"); 399 } 400 } 401 } 402 /* 403 ** RETURNTOSENDER -- return a message to the sender with an error. 404 ** 405 ** Parameters: 406 ** msg -- the explanatory message. 407 ** returnq -- the queue of people to send the message to. 408 ** sendbody -- if TRUE, also send back the body of the 409 ** message; otherwise just send the header. 410 ** e -- the current envelope. 411 ** 412 ** Returns: 413 ** zero -- if everything went ok. 414 ** else -- some error. 415 ** 416 ** Side Effects: 417 ** Returns the current message to the sender via 418 ** mail. 419 */ 420 421 #define MAXRETURNS 6 /* max depth of returning messages */ 422 #define ERRORFUDGE 100 /* nominal size of error message text */ 423 424 int 425 returntosender(msg, returnq, sendbody, e) 426 char *msg; 427 ADDRESS *returnq; 428 bool sendbody; 429 register ENVELOPE *e; 430 { 431 register ENVELOPE *ee; 432 ENVELOPE *oldcur = CurEnv; 433 ENVELOPE errenvelope; 434 static int returndepth; 435 register ADDRESS *q; 436 char *p; 437 char buf[MAXNAME + 1]; 438 extern void errbody __P((MCI *, ENVELOPE *, char *)); 439 440 if (returnq == NULL) 441 return (-1); 442 443 if (msg == NULL) 444 msg = "Unable to deliver mail"; 445 446 if (tTd(6, 1)) 447 { 448 printf("\n*** Return To Sender: msg=\"%s\", depth=%d, e=%x, returnq=", 449 msg, returndepth, e); 450 printaddr(returnq, TRUE); 451 if (tTd(6, 20)) 452 { 453 printf("Sendq="); 454 printaddr(e->e_sendqueue, TRUE); 455 } 456 } 457 458 if (++returndepth >= MAXRETURNS) 459 { 460 if (returndepth != MAXRETURNS) 461 syserr("554 returntosender: infinite recursion on %s", returnq->q_paddr); 462 /* don't "unrecurse" and fake a clean exit */ 463 /* returndepth--; */ 464 return (0); 465 } 466 467 define('g', e->e_from.q_paddr, e); 468 define('u', NULL, e); 469 470 /* initialize error envelope */ 471 ee = newenvelope(&errenvelope, e); 472 define('a', "\201b", ee); 473 define('r', "internal", ee); 474 define('s', "localhost", ee); 475 define('_', "localhost", ee); 476 ee->e_puthdr = putheader; 477 ee->e_putbody = errbody; 478 ee->e_flags |= EF_RESPONSE|EF_METOO; 479 if (!bitset(EF_OLDSTYLE, e->e_flags)) 480 ee->e_flags &= ~EF_OLDSTYLE; 481 ee->e_sendqueue = returnq; 482 ee->e_msgsize = ERRORFUDGE; 483 if (sendbody) 484 ee->e_msgsize += e->e_msgsize; 485 else 486 ee->e_flags |= EF_NO_BODY_RETN; 487 initsys(ee); 488 for (q = returnq; q != NULL; q = q->q_next) 489 { 490 if (bitset(QBADADDR, q->q_flags)) 491 continue; 492 493 if (!DontPruneRoutes && pruneroute(q->q_paddr)) 494 { 495 register ADDRESS *p; 496 497 parseaddr(q->q_paddr, q, RF_COPYPARSE, '\0', NULL, e); 498 for (p = returnq; p != NULL; p = p->q_next) 499 { 500 if (p != q && sameaddr(p, q)) 501 q->q_flags |= QDONTSEND; 502 } 503 } 504 505 if (!bitset(QDONTSEND, q->q_flags)) 506 ee->e_nrcpts++; 507 508 if (q->q_alias == NULL) 509 addheader("To", q->q_paddr, &ee->e_header); 510 } 511 512 # ifdef LOG 513 if (LogLevel > 5) 514 syslog(LOG_INFO, "%s: %s: return to sender: %s", 515 e->e_id, ee->e_id, msg); 516 # endif 517 518 if (SendMIMEErrors) 519 { 520 addheader("MIME-Version", "1.0", &ee->e_header); 521 (void) sprintf(buf, "%s.%ld/%s", 522 ee->e_id, curtime(), MyHostName); 523 ee->e_msgboundary = newstr(buf); 524 (void) sprintf(buf, 525 #if DSN 526 "multipart/report; report-type=X-delivery-status-3 (Draft of May 5, 1995);\n\tboundary=\"%s\"", 527 #else 528 "multipart/mixed; boundary=\"%s\"", 529 #endif 530 ee->e_msgboundary); 531 addheader("Content-Type", buf, &ee->e_header); 532 } 533 if (strncmp(msg, "Warning:", 8) == 0) 534 { 535 addheader("Subject", msg, &ee->e_header); 536 p = "warning-timeout"; 537 } 538 else if (strcmp(msg, "Return receipt") == 0) 539 { 540 addheader("Subject", msg, &ee->e_header); 541 p = "return-receipt"; 542 } 543 else 544 { 545 sprintf(buf, "Returned mail: %.*s", sizeof buf - 20, msg); 546 addheader("Subject", buf, &ee->e_header); 547 p = "failure"; 548 } 549 (void) sprintf(buf, "auto-generated (%s)", p); 550 addheader("Auto-Submitted", buf, &ee->e_header); 551 552 /* fake up an address header for the from person */ 553 expand("\201n", buf, sizeof buf, e); 554 if (parseaddr(buf, &ee->e_from, RF_COPYALL|RF_SENDERADDR, '\0', NULL, e) == NULL) 555 { 556 syserr("553 Can't parse myself!"); 557 ExitStat = EX_SOFTWARE; 558 returndepth--; 559 return (-1); 560 } 561 ee->e_sender = ee->e_from.q_paddr; 562 563 /* push state into submessage */ 564 CurEnv = ee; 565 define('f', "\201n", ee); 566 define('x', "Mail Delivery Subsystem", ee); 567 eatheader(ee, TRUE); 568 569 /* mark statistics */ 570 markstats(ee, NULLADDR); 571 572 /* actually deliver the error message */ 573 sendall(ee, SM_DEFAULT); 574 575 /* restore state */ 576 dropenvelope(ee); 577 CurEnv = oldcur; 578 returndepth--; 579 580 /* should check for delivery errors here */ 581 return (0); 582 } 583 /* 584 ** ERRBODY -- output the body of an error message. 585 ** 586 ** Typically this is a copy of the transcript plus a copy of the 587 ** original offending message. 588 ** 589 ** Parameters: 590 ** mci -- the mailer connection information. 591 ** e -- the envelope we are working in. 592 ** separator -- any possible MIME separator. 593 ** flags -- to modify the behaviour. 594 ** 595 ** Returns: 596 ** none 597 ** 598 ** Side Effects: 599 ** Outputs the body of an error message. 600 */ 601 602 void 603 errbody(mci, e, separator) 604 register MCI *mci; 605 register ENVELOPE *e; 606 char *separator; 607 { 608 register FILE *xfile; 609 char *p; 610 register ADDRESS *q; 611 bool printheader; 612 bool sendbody; 613 char buf[MAXLINE]; 614 extern char *xtextify(); 615 616 if (bitset(MCIF_INHEADER, mci->mci_flags)) 617 { 618 putline("", mci); 619 mci->mci_flags &= ~MCIF_INHEADER; 620 } 621 if (e->e_parent == NULL) 622 { 623 syserr("errbody: null parent"); 624 putline(" ----- Original message lost -----\n", mci); 625 return; 626 } 627 628 /* 629 ** Output MIME header. 630 */ 631 632 if (e->e_msgboundary != NULL) 633 { 634 putline("This is a MIME-encapsulated message", mci); 635 putline("", mci); 636 (void) sprintf(buf, "--%s", e->e_msgboundary); 637 putline(buf, mci); 638 putline("", mci); 639 } 640 641 /* 642 ** Output introductory information. 643 */ 644 645 for (q = e->e_parent->e_sendqueue; q != NULL; q = q->q_next) 646 if (bitset(QBADADDR, q->q_flags)) 647 break; 648 if (q == NULL && 649 !bitset(EF_FATALERRS|EF_SENDRECEIPT, e->e_parent->e_flags)) 650 { 651 putline(" **********************************************", 652 mci); 653 putline(" ** THIS IS A WARNING MESSAGE ONLY **", 654 mci); 655 putline(" ** YOU DO NOT NEED TO RESEND YOUR MESSAGE **", 656 mci); 657 putline(" **********************************************", 658 mci); 659 putline("", mci); 660 } 661 sprintf(buf, "The original message was received at %s", 662 arpadate(ctime(&e->e_parent->e_ctime))); 663 putline(buf, mci); 664 expand("from \201_", buf, sizeof buf, e->e_parent); 665 putline(buf, mci); 666 putline("", mci); 667 668 /* 669 ** Output error message header (if specified and available). 670 */ 671 672 if (ErrMsgFile != NULL && !bitset(EF_SENDRECEIPT, e->e_parent->e_flags)) 673 { 674 if (*ErrMsgFile == '/') 675 { 676 xfile = fopen(ErrMsgFile, "r"); 677 if (xfile != NULL) 678 { 679 while (fgets(buf, sizeof buf, xfile) != NULL) 680 { 681 expand(buf, buf, sizeof buf, e); 682 putline(buf, mci); 683 } 684 (void) fclose(xfile); 685 putline("\n", mci); 686 } 687 } 688 else 689 { 690 expand(ErrMsgFile, buf, sizeof buf, e); 691 putline(buf, mci); 692 putline("", mci); 693 } 694 } 695 696 /* 697 ** Output message introduction 698 */ 699 700 printheader = TRUE; 701 for (q = e->e_parent->e_sendqueue; q != NULL; q = q->q_next) 702 { 703 if (bitset(QBADADDR, q->q_flags)) 704 { 705 if (!bitset(QPINGONFAILURE, q->q_flags)) 706 continue; 707 p = "unrecoverable error"; 708 } 709 else if (!bitset(QPRIMARY, q->q_flags)) 710 continue; 711 else if (bitset(QRELAYED, q->q_flags)) 712 p = "relayed to non-DSN-aware mailer"; 713 else if (bitset(QDELIVERED, q->q_flags)) 714 { 715 if (bitset(QEXPANDED, q->q_flags)) 716 p = "successfully delivered to mailing list"; 717 else 718 p = "successfully delivered to mailbox"; 719 } 720 else if (bitset(QEXPANDED, q->q_flags)) 721 p = "expanded by alias"; 722 else if (bitset(QDELAYED, q->q_flags)) 723 p = "transient failure"; 724 else 725 continue; 726 727 if (printheader) 728 { 729 putline(" ----- The following addresses have delivery notifications -----", 730 mci); 731 printheader = FALSE; 732 } 733 734 sprintf(buf, "%s (%s)", q->q_paddr, p); 735 putline(buf, mci); 736 if (q->q_alias != NULL) 737 { 738 strcpy(buf, " (expanded from: "); 739 strcat(buf, q->q_alias->q_paddr); 740 strcat(buf, ")"); 741 putline(buf, mci); 742 } 743 } 744 if (!printheader) 745 putline("\n", mci); 746 747 /* 748 ** Output transcript of errors 749 */ 750 751 (void) fflush(stdout); 752 p = queuename(e->e_parent, 'x'); 753 if ((xfile = fopen(p, "r")) == NULL) 754 { 755 syserr("Cannot open %s", p); 756 putline(" ----- Transcript of session is unavailable -----\n", mci); 757 } 758 else 759 { 760 printheader = TRUE; 761 if (e->e_xfp != NULL) 762 (void) fflush(e->e_xfp); 763 while (fgets(buf, sizeof buf, xfile) != NULL) 764 { 765 if (printheader) 766 putline(" ----- Transcript of session follows -----\n", mci); 767 printheader = FALSE; 768 putline(buf, mci); 769 } 770 (void) xfclose(xfile, "errbody xscript", p); 771 } 772 errno = 0; 773 774 #if DSN 775 /* 776 ** Output machine-readable version. 777 */ 778 779 if (e->e_msgboundary != NULL) 780 { 781 putline("", mci); 782 (void) sprintf(buf, "--%s", e->e_msgboundary); 783 putline(buf, mci); 784 putline("Content-Type: message/X-delivery-status-04a (Draft of April 4, 1995)", mci); 785 putline("", mci); 786 787 /* 788 ** Output per-message information. 789 */ 790 791 /* original envelope id from MAIL FROM: line */ 792 if (e->e_parent->e_envid != NULL) 793 { 794 (void) sprintf(buf, "Original-Envelope-Id: %s", 795 xtextify(e->e_parent->e_envid)); 796 putline(buf, mci); 797 } 798 799 /* Reporting-MTA: is us (required) */ 800 (void) sprintf(buf, "Reporting-MTA: dns; %s", 801 xtextify(MyHostName)); 802 putline(buf, mci); 803 804 /* DSN-Gateway: not relevant since we are not translating */ 805 806 /* Received-From-MTA: shows where we got this message from */ 807 if (RealHostName != NULL) 808 { 809 /* XXX use $s for type? */ 810 p = e->e_parent->e_from.q_mailer->m_mtatype; 811 if (p == NULL) 812 p = "dns"; 813 (void) sprintf(buf, "Received-From-MTA: %s; %s", 814 p, xtextify(RealHostName)); 815 putline(buf, mci); 816 } 817 818 /* Arrival-Date: -- when it arrived here */ 819 (void) sprintf(buf, "Arrival-Date: %s", 820 arpadate(ctime(&e->e_parent->e_ctime))); 821 putline(buf, mci); 822 823 /* 824 ** Output per-address information. 825 */ 826 827 for (q = e->e_parent->e_sendqueue; q != NULL; q = q->q_next) 828 { 829 register ADDRESS *r; 830 char *action; 831 832 if (bitset(QBADADDR, q->q_flags)) 833 action = "failed"; 834 else if (!bitset(QPRIMARY, q->q_flags)) 835 continue; 836 else if (bitset(QDELIVERED, q->q_flags)) 837 { 838 if (bitset(QEXPANDED, q->q_flags)) 839 action = "delivered (to mailing list)"; 840 else 841 action = "delivered (to mailbox)"; 842 } 843 else if (bitset(QRELAYED, q->q_flags)) 844 action = "relayed (to non-DSN-aware mailer)"; 845 else if (bitset(QEXPANDED, q->q_flags)) 846 action = "expanded (to multi-recipient alias)"; 847 else if (bitset(QDELAYED, q->q_flags)) 848 action = "delayed"; 849 else 850 continue; 851 852 putline("", mci); 853 854 /* Original-Recipient: -- passed from on high */ 855 if (q->q_orcpt != NULL) 856 { 857 (void) sprintf(buf, "Original-Recipient: %s", 858 xtextify(q->q_orcpt)); 859 putline(buf, mci); 860 } 861 862 /* Final-Recipient: -- the name from the RCPT command */ 863 p = e->e_parent->e_from.q_mailer->m_addrtype; 864 if (p == NULL) 865 p = "rfc822"; 866 for (r = q; r->q_alias != NULL; r = r->q_alias) 867 continue; 868 if (strchr(r->q_user, '@') == NULL) 869 { 870 (void) sprintf(buf, "Final-Recipient: %s; %s@", 871 p, xtextify(r->q_user)); 872 strcat(buf, xtextify(MyHostName)); 873 } 874 else 875 { 876 (void) sprintf(buf, "Final-Recipient: %s; %s", 877 p, xtextify(r->q_user)); 878 } 879 putline(buf, mci); 880 881 /* X-Actual-Recipient: -- the real problem address */ 882 if (r != q) 883 { 884 if (strchr(q->q_user, '@') == NULL) 885 { 886 (void) sprintf(buf, "X-Actual-Recipient: %s; %s@", 887 p, xtextify(q->q_user)); 888 strcat(buf, xtextify(MyHostName)); 889 } 890 else 891 { 892 (void) sprintf(buf, "X-Actual-Recipient: %s; %s", 893 p, xtextify(q->q_user)); 894 } 895 putline(buf, mci); 896 } 897 898 /* Action: -- what happened? */ 899 sprintf(buf, "Action: %s", action); 900 putline(buf, mci); 901 902 /* Status: -- what _really_ happened? */ 903 strcpy(buf, "Status: "); 904 if (q->q_status != NULL) 905 strcat(buf, q->q_status); 906 else if (bitset(QBADADDR, q->q_flags)) 907 strcat(buf, "5.0.0"); 908 else if (bitset(QQUEUEUP, q->q_flags)) 909 strcat(buf, "4.0.0"); 910 else 911 strcat(buf, "2.0.0"); 912 putline(buf, mci); 913 914 /* Remote-MTA: -- who was I talking to? */ 915 p = q->q_mailer->m_mtatype; 916 if (p == NULL) 917 p = "dns"; 918 (void) sprintf(buf, "Remote-MTA: %s; ", p); 919 if (q->q_statmta != NULL) 920 p = q->q_statmta; 921 else if (q->q_host != NULL && q->q_host[0] != '\0') 922 p = q->q_host; 923 else 924 p = NULL; 925 if (p != NULL) 926 { 927 strcat(buf, p); 928 p = &buf[strlen(buf) - 1]; 929 if (*p == '.') 930 *p = '\0'; 931 putline(buf, mci); 932 } 933 934 /* Diagnostic-Code: -- actual result from other end */ 935 if (q->q_rstatus != NULL) 936 { 937 p = q->q_mailer->m_diagtype; 938 if (p == NULL) 939 p = "smtp"; 940 (void) sprintf(buf, "Diagnostic-Code: %s; %s", 941 p, xtextify(q->q_rstatus)); 942 putline(buf, mci); 943 } 944 945 /* Last-Attempt-Date: -- fine granularity */ 946 if (q->q_statdate == (time_t) 0L) 947 q->q_statdate = curtime(); 948 (void) sprintf(buf, "Last-Attempt-Date: %s", 949 arpadate(ctime(&q->q_statdate))); 950 putline(buf, mci); 951 952 /* Will-Retry-Until: -- for delayed messages only */ 953 if (bitset(QQUEUEUP, q->q_flags) && 954 !bitset(QBADADDR, q->q_flags)) 955 { 956 time_t xdate; 957 958 xdate = e->e_ctime + TimeOuts.to_q_return[e->e_timeoutclass]; 959 sprintf(buf, "Will-Retry-Until: %s", 960 arpadate(ctime(&xdate))); 961 putline(buf, mci); 962 } 963 } 964 } 965 #endif 966 967 /* 968 ** Output text of original message 969 */ 970 971 putline("", mci); 972 if (bitset(EF_HAS_DF, e->e_parent->e_flags)) 973 { 974 sendbody = !bitset(EF_NO_BODY_RETN, e->e_parent->e_flags) && 975 !bitset(EF_NO_BODY_RETN, e->e_flags); 976 977 if (e->e_msgboundary == NULL) 978 { 979 if (sendbody) 980 putline(" ----- Original message follows -----\n", mci); 981 else 982 putline(" ----- Message header follows -----\n", mci); 983 (void) fflush(mci->mci_out); 984 } 985 else 986 { 987 (void) sprintf(buf, "--%s", e->e_msgboundary); 988 putline(buf, mci); 989 (void) sprintf(buf, "Content-Type: %s", 990 sendbody ? "message/rfc822" 991 : "text/rfc822-headers"); 992 putline(buf, mci); 993 } 994 putline("", mci); 995 putheader(mci, e->e_parent->e_header, e->e_parent); 996 if (sendbody) 997 putbody(mci, e->e_parent, e->e_msgboundary); 998 else if (e->e_msgboundary == NULL) 999 { 1000 putline("", mci); 1001 putline(" ----- Message body suppressed -----", mci); 1002 } 1003 } 1004 else if (e->e_msgboundary == NULL) 1005 { 1006 putline(" ----- No message was collected -----\n", mci); 1007 } 1008 1009 if (e->e_msgboundary != NULL) 1010 { 1011 putline("", mci); 1012 (void) sprintf(buf, "--%s--", e->e_msgboundary); 1013 putline(buf, mci); 1014 } 1015 putline("", mci); 1016 1017 /* 1018 ** Cleanup and exit 1019 */ 1020 1021 if (errno != 0) 1022 syserr("errbody: I/O error"); 1023 } 1024 /* 1025 ** SMTPTODSN -- convert SMTP to DSN status code 1026 ** 1027 ** Parameters: 1028 ** smtpstat -- the smtp status code (e.g., 550). 1029 ** 1030 ** Returns: 1031 ** The DSN version of the status code. 1032 */ 1033 1034 char * 1035 smtptodsn(smtpstat) 1036 int smtpstat; 1037 { 1038 if (smtpstat < 0) 1039 return "4.4.2"; 1040 1041 switch (smtpstat) 1042 { 1043 case 450: /* Req mail action not taken: mailbox unavailable */ 1044 return "4.2.0"; 1045 1046 case 451: /* Req action aborted: local error in processing */ 1047 return "4.3.0"; 1048 1049 case 452: /* Req action not taken: insufficient sys storage */ 1050 return "4.3.1"; 1051 1052 case 500: /* Syntax error, command unrecognized */ 1053 return "5.5.2"; 1054 1055 case 501: /* Syntax error in parameters or arguments */ 1056 return "5.5.4"; 1057 1058 case 502: /* Command not implemented */ 1059 return "5.5.1"; 1060 1061 case 503: /* Bad sequence of commands */ 1062 return "5.5.1"; 1063 1064 case 504: /* Command parameter not implemented */ 1065 return "5.5.4"; 1066 1067 case 550: /* Req mail action not taken: mailbox unavailable */ 1068 return "5.2.0"; 1069 1070 case 551: /* User not local; please try <...> */ 1071 return "5.1.6"; 1072 1073 case 552: /* Req mail action aborted: exceeded storage alloc */ 1074 return "5.2.2"; 1075 1076 case 553: /* Req action not taken: mailbox name not allowed */ 1077 return "5.1.3"; 1078 1079 case 554: /* Transaction failed */ 1080 return "5.0.0"; 1081 } 1082 1083 if ((smtpstat / 100) == 2) 1084 return "2.0.0"; 1085 if ((smtpstat / 100) == 4) 1086 return "4.0.0"; 1087 return "5.0.0"; 1088 } 1089 /* 1090 ** XTEXTIFY -- take regular text and turn it into DSN-style xtext 1091 ** 1092 ** Parameters: 1093 ** t -- the text to convert. 1094 ** 1095 ** Returns: 1096 ** The xtext-ified version of the same string. 1097 */ 1098 1099 char * 1100 xtextify(t) 1101 register char *t; 1102 { 1103 register char *p; 1104 int l; 1105 int nbogus; 1106 static char *bp = NULL; 1107 static int bplen = 0; 1108 1109 /* figure out how long this xtext will have to be */ 1110 nbogus = l = 0; 1111 for (p = t; *p != '\0'; p++) 1112 { 1113 register int c = (*p & 0xff); 1114 1115 /* ASCII dependence here -- this is the way the spec words it */ 1116 if (c < '!' || c > '~' || c == '+' || c == '\\' || c == '(') 1117 nbogus++; 1118 l++; 1119 } 1120 if (nbogus == 0) 1121 return t; 1122 l += nbogus * 2 + 1; 1123 1124 /* now allocate space if necessary for the new string */ 1125 if (l > bplen) 1126 { 1127 if (bp != NULL) 1128 free(bp); 1129 bp = xalloc(l); 1130 bplen = l; 1131 } 1132 1133 /* ok, copy the text with byte expansion */ 1134 for (p = bp; *t != '\0'; ) 1135 { 1136 register int c = (*t++ & 0xff); 1137 1138 /* ASCII dependence here -- this is the way the spec words it */ 1139 if (c < '!' || c > '~' || c == '+' || c == '\\' || c == '(') 1140 { 1141 *p++ = '+'; 1142 *p++ = "0123456789abcdef"[c >> 4]; 1143 *p++ = "0123456789abcdef"[c & 0xf]; 1144 } 1145 else 1146 *p++ = c; 1147 } 1148 *p = '\0'; 1149 return bp; 1150 } 1151 /* 1152 ** XTEXTOK -- check if a string is legal xtext 1153 ** 1154 ** Xtext is used in Delivery Status Notifications. The spec was 1155 ** taken from draft-ietf-notary-mime-delivery-04.txt. 1156 ** 1157 ** Parameters: 1158 ** s -- the string to check. 1159 ** 1160 ** Returns: 1161 ** TRUE -- if 's' is legal xtext. 1162 ** FALSE -- if it has any illegal characters in it. 1163 */ 1164 1165 bool 1166 xtextok(s) 1167 char *s; 1168 { 1169 int c; 1170 1171 while ((c = *s++) != '\0') 1172 { 1173 if (c == '+') 1174 { 1175 c = *s++; 1176 if (!isascii(c) || !isxdigit(c)) 1177 return FALSE; 1178 c = *s++; 1179 if (!isascii(c) || !isxdigit(c)) 1180 return FALSE; 1181 } 1182 else if (c < '!' || c > '~' || c == '\\' || c == '(') 1183 return FALSE; 1184 } 1185 return TRUE; 1186 } 1187 /* 1188 ** PRUNEROUTE -- prune an RFC-822 source route 1189 ** 1190 ** Trims down a source route to the last internet-registered hop. 1191 ** This is encouraged by RFC 1123 section 5.3.3. 1192 ** 1193 ** Parameters: 1194 ** addr -- the address 1195 ** 1196 ** Returns: 1197 ** TRUE -- address was modified 1198 ** FALSE -- address could not be pruned 1199 ** 1200 ** Side Effects: 1201 ** modifies addr in-place 1202 */ 1203 1204 bool 1205 pruneroute(addr) 1206 char *addr; 1207 { 1208 #if NAMED_BIND 1209 char *start, *at, *comma; 1210 char c; 1211 int rcode; 1212 char hostbuf[BUFSIZ]; 1213 char *mxhosts[MAXMXHOSTS + 1]; 1214 1215 /* check to see if this is really a route-addr */ 1216 if (*addr != '<' || addr[1] != '@' || addr[strlen(addr) - 1] != '>') 1217 return FALSE; 1218 start = strchr(addr, ':'); 1219 at = strrchr(addr, '@'); 1220 if (start == NULL || at == NULL || at < start) 1221 return FALSE; 1222 1223 /* slice off the angle brackets */ 1224 strcpy(hostbuf, at + 1); 1225 hostbuf[strlen(hostbuf) - 1] = '\0'; 1226 1227 while (start) 1228 { 1229 if (getmxrr(hostbuf, mxhosts, FALSE, &rcode) > 0) 1230 { 1231 strcpy(addr + 1, start + 1); 1232 return TRUE; 1233 } 1234 c = *start; 1235 *start = '\0'; 1236 comma = strrchr(addr, ','); 1237 if (comma && comma[1] == '@') 1238 strcpy(hostbuf, comma + 2); 1239 else 1240 comma = 0; 1241 *start = c; 1242 start = comma; 1243 } 1244 #endif 1245 return FALSE; 1246 } 1247