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