2 * Copyright (c) 1998-2000 Sendmail, Inc. and its suppliers.
4 * Copyright (c) 1983, 1995-1997 Eric P. Allman. All rights reserved.
5 * Copyright (c) 1988, 1993
6 * The Regents of the University of California. All rights reserved.
8 * By using this file, you agree to the terms and conditions set
9 * forth in the LICENSE file which can be found at the top level of
10 * the sendmail distribution.
15 static char id[] = "@(#)$Id: savemail.c,v 8.212.4.5 2000/08/22 22:46:00 gshapiro Exp $";
21 static void errbody __P((MCI *, ENVELOPE *, char *));
22 static bool pruneroute __P((char *));
25 ** SAVEMAIL -- Save mail on error
27 ** If mailing back errors, mail it back to the originator
28 ** together with an error message; otherwise, just put it in
29 ** dead.letter in the user's home directory (if he exists on
33 ** e -- the envelope containing the message in error.
34 ** sendbody -- if TRUE, also send back the body of the
35 ** message; otherwise just send the header.
41 ** Saves the letter, by writing or mailing it back to the
42 ** sender, or by putting it in dead.letter in her home
46 /* defines for state machine */
47 #define ESM_REPORT 0 /* report to sender's terminal */
48 #define ESM_MAIL 1 /* mail back to sender */
49 #define ESM_QUIET 2 /* mail has already been returned */
50 #define ESM_DEADLETTER 3 /* save in ~/dead.letter */
51 #define ESM_POSTMASTER 4 /* return to postmaster */
52 #define ESM_DEADLETTERDROP 5 /* save in DeadLetterDrop */
53 #define ESM_PANIC 6 /* call loseqfile() */
54 #define ESM_DONE 7 /* message is successfully delivered */
62 register struct passwd *pw;
65 auto ADDRESS *q = NULL;
70 char buf[MAXLINE + 1];
74 dprintf("\nsavemail, errormode = %c, id = %s, ExitStat = %d\n e_from=",
75 e->e_errormode, e->e_id == NULL ? "NONE" : e->e_id,
77 printaddr(&e->e_from, FALSE);
82 /* can't return a message with no id */
87 ** In the unhappy event we don't know who to return the mail
88 ** to, make someone up.
91 if (e->e_from.q_paddr == NULL)
93 e->e_sender = "Postmaster";
94 if (parseaddr(e->e_sender, &e->e_from,
95 RF_COPYPARSE|RF_SENDERADDR, '\0', NULL, e) == NULL)
97 syserr("553 5.3.5 Cannot parse Postmaster!");
98 finis(TRUE, EX_SOFTWARE);
104 ** Basic state machine.
106 ** This machine runs through the following states:
108 ** ESM_QUIET Errors have already been printed iff the
110 ** ESM_REPORT Report directly to the sender's terminal.
111 ** ESM_MAIL Mail response to the sender.
112 ** ESM_DEADLETTER Save response in ~/dead.letter.
113 ** ESM_POSTMASTER Mail response to the postmaster.
114 ** ESM_DEADLETTERDROP
115 ** If DeadLetterDrop set, save it there.
116 ** ESM_PANIC Save response anywhere possible.
119 /* determine starting state */
120 switch (e->e_errormode)
137 /* no need to return anything at all */
141 syserr("554 5.3.0 savemail: bogus errormode x%x\n",
147 /* if this is already an error response, send to postmaster */
148 if (bitset(EF_RESPONSE, e->e_flags))
150 if (e->e_parent != NULL &&
151 bitset(EF_RESPONSE, e->e_parent->e_flags))
153 /* got an error sending a response -- can it */
156 state = ESM_POSTMASTER;
159 while (state != ESM_DONE)
162 dprintf(" state %d\n", state);
167 if (bitnset(M_LOCALMAILER, e->e_from.q_mailer->m_flags))
168 state = ESM_DEADLETTER;
176 ** If the user is still logged in on the same terminal,
177 ** then write the error messages back to hir (sic).
181 if (p == NULL || freopen(p, "w", stdout) == NULL)
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)
192 (void) bfrewind(e->e_xfp);
193 printf("Transcript follows:\r\n");
194 while (fgets(buf, sizeof buf, e->e_xfp) != NULL &&
196 (void) fputs(buf, stdout);
200 syserr("Cannot open %s", queuename(e, 'x'));
201 printf("Transcript of session is unavailable.\r\n");
203 printf("Original message will be saved in dead.letter.\r\n");
204 state = ESM_DEADLETTER;
209 ** If mailing back, do it.
210 ** Throw away all further output. Don't alias,
211 ** since this could cause loops, e.g., if joe
212 ** mails to joe@x, and for some reason the network
213 ** for @x is down, then the response gets sent to
214 ** joe@x, which gives a response, etc. Also force
215 ** the mail to be delivered even if a version of
216 ** it has already been sent to the sender.
218 ** If this is a configuration or local software
219 ** error, send to the local postmaster as well,
220 ** since the originator can't do anything
221 ** about it anyway. Note that this is a full
222 ** copy of the message (intentionally) so that
223 ** the Postmaster can forward things along.
226 if (ExitStat == EX_CONFIG || ExitStat == EX_SOFTWARE)
228 (void) sendtolist("postmaster",
229 NULLADDR, &e->e_errorqueue, 0, e);
231 if (!emptyaddr(&e->e_from))
233 char from[TOBUFSIZE];
235 if (strlen(e->e_from.q_paddr) >= sizeof from)
237 state = ESM_POSTMASTER;
240 (void) strlcpy(from, e->e_from.q_paddr,
243 if (!DontPruneRoutes && pruneroute(from))
247 for (a = e->e_errorqueue; a != NULL;
250 if (sameaddr(a, &e->e_from))
251 a->q_state = QS_DUPLICATE;
254 (void) sendtolist(from, NULLADDR,
255 &e->e_errorqueue, 0, e);
259 ** Deliver a non-delivery report to the
260 ** Postmaster-designate (not necessarily
261 ** Postmaster). This does not include the
262 ** body of the message, for privacy reasons.
263 ** You really shouldn't need this.
266 e->e_flags |= EF_PM_NOTIFY;
268 /* check to see if there are any good addresses */
269 for (q = e->e_errorqueue; q != NULL; q = q->q_next)
271 if (QS_IS_SENDABLE(q->q_state))
276 /* this is an error-error */
277 state = ESM_POSTMASTER;
280 if (returntosender(e->e_message, e->e_errorqueue,
281 sendbody ? RTSF_SEND_BODY
289 /* didn't work -- return to postmaster */
290 state = ESM_POSTMASTER;
295 ** Similar to previous case, but to system postmaster.
299 expand(DoubleBounceAddr, buf, sizeof buf, e);
300 if (sendtolist(buf, NULLADDR, &q, 0, e) <= 0)
302 syserr("553 5.3.0 cannot parse %s!", buf);
303 ExitStat = EX_SOFTWARE;
304 state = ESM_DEADLETTERDROP;
307 flags = RTSF_PM_BOUNCE;
309 flags |= RTSF_SEND_BODY;
310 if (returntosender(e->e_message, q, flags, e) == 0)
316 /* didn't work -- last resort */
317 state = ESM_DEADLETTERDROP;
322 ** Save the message in dead.letter.
323 ** If we weren't mailing back, and the user is
324 ** local, we should save the message in
325 ** ~/dead.letter so that the poor person doesn't
326 ** have to type it over again -- and we all know
327 ** what poor typists UNIX users are.
331 if (bitnset(M_HASPWENT, e->e_from.q_mailer->m_flags))
333 if (e->e_from.q_home != NULL)
334 p = e->e_from.q_home;
335 else if ((pw = sm_getpwnam(e->e_from.q_user)) != NULL &&
339 if (p == NULL || e->e_dfp == NULL)
341 /* no local directory or no data file */
346 /* we have a home directory; write dead.letter */
349 /* get the sender for the UnixFromLine */
350 p = macvalue('g', e);
351 define('g', e->e_sender, e);
353 expand("\201z/dead.letter", buf, sizeof buf, e);
354 sff = SFF_CREAT|SFF_REGONLY|SFF_RUNASREALUID;
358 if (writable(buf, NULL, sff) &&
359 mailfile(buf, FileMailer, NULL, sff, e) == EX_OK)
361 int oldverb = Verbose;
363 if (OpMode != MD_DAEMON && OpMode != MD_SMTP)
366 message("Saved message in %s", buf);
376 case ESM_DEADLETTERDROP:
378 ** Log the mail in DeadLetterDrop file.
387 if ((SafeFileEnv != NULL && SafeFileEnv[0] != '\0') ||
388 DeadLetterDrop == NULL ||
389 DeadLetterDrop[0] == '\0')
395 sff = SFF_CREAT|SFF_REGONLY|SFF_ROOTOK|SFF_OPENASROOT|SFF_MUSTOWN;
396 if (!writable(DeadLetterDrop, NULL, sff) ||
397 (fp = safefopen(DeadLetterDrop, O_WRONLY|O_APPEND,
398 FileMode, sff)) == NULL)
404 memset(&mcibuf, '\0', sizeof mcibuf);
406 mcibuf.mci_mailer = FileMailer;
407 if (bitnset(M_7BITS, FileMailer->m_flags))
408 mcibuf.mci_flags |= MCIF_7BIT;
410 /* get the sender for the UnixFromLine */
411 p = macvalue('g', e);
412 define('g', e->e_sender, e);
414 putfromline(&mcibuf, e);
415 (*e->e_puthdr)(&mcibuf, e->e_header, e, M87F_OUTER);
416 (*e->e_putbody)(&mcibuf, e, NULL);
417 putline("\n", &mcibuf);
424 int oldverb = Verbose;
426 if (OpMode != MD_DAEMON && OpMode != MD_SMTP)
429 message("Saved message in %s",
433 sm_syslog(LOG_NOTICE, e->e_id,
434 "Saved message in %s",
442 syserr("554 5.3.5 savemail: unknown state %d", state);
447 /* leave the locked queue & transcript files around */
448 loseqfile(e, "savemail panic");
450 syserr("!554 savemail: cannot save rejected email anywhere");
455 ** RETURNTOSENDER -- return a message to the sender with an error.
458 ** msg -- the explanatory message.
459 ** returnq -- the queue of people to send the message to.
460 ** flags -- flags tweaking the operation:
461 ** RTSF_SENDBODY -- include body of message (otherwise
462 ** just send the header).
463 ** RTSF_PMBOUNCE -- this is a postmaster bounce.
464 ** e -- the current envelope.
467 ** zero -- if everything went ok.
468 ** else -- some error.
471 ** Returns the current message to the sender via
475 #define MAXRETURNS 6 /* max depth of returning messages */
476 #define ERRORFUDGE 100 /* nominal size of error message text */
479 returntosender(msg, returnq, flags, e)
483 register ENVELOPE *e;
485 register ENVELOPE *ee;
486 ENVELOPE *oldcur = CurEnv;
487 ENVELOPE errenvelope;
488 static int returndepth = 0;
491 char buf[MAXNAME + 1];
497 msg = "Unable to deliver mail";
501 dprintf("\n*** Return To Sender: msg=\"%s\", depth=%d, e=%lx, returnq=",
502 msg, returndepth, (u_long) e);
503 printaddr(returnq, TRUE);
507 printaddr(e->e_sendqueue, TRUE);
511 if (++returndepth >= MAXRETURNS)
513 if (returndepth != MAXRETURNS)
514 syserr("554 5.3.0 returntosender: infinite recursion on %s",
516 /* don't "unrecurse" and fake a clean exit */
521 define('g', e->e_sender, e);
522 define('u', NULL, e);
524 /* initialize error envelope */
525 ee = newenvelope(&errenvelope, e);
526 define('a', "\201b", ee);
528 define('s', "localhost", ee);
529 define('_', "localhost", ee);
531 define(macid("{auth_type}", NULL), "", ee);
532 define(macid("{auth_authen}", NULL), "", ee);
533 define(macid("{auth_author}", NULL), "", ee);
534 define(macid("{auth_ssf}", NULL), "", ee);
537 define(macid("{cert_issuer}", NULL), "", ee);
538 define(macid("{cert_subject}", NULL), "", ee);
539 define(macid("{cipher_bits}", NULL), "", ee);
540 define(macid("{cipher}", NULL), "", ee);
541 define(macid("{tls_version}", NULL), "", ee);
542 define(macid("{verify}", NULL), "", ee);
544 define(macid("{alg_bits}", NULL), "", ee);
545 define(macid("{cn_issuer}", NULL), "", ee);
546 define(macid("{cn_subject}", NULL), "", ee);
547 # endif /* _FFR_TLS_1 */
548 #endif /* STARTTLS */
550 ee->e_puthdr = putheader;
551 ee->e_putbody = errbody;
552 ee->e_flags |= EF_RESPONSE|EF_METOO;
553 if (!bitset(EF_OLDSTYLE, e->e_flags))
554 ee->e_flags &= ~EF_OLDSTYLE;
555 if (bitset(EF_DONT_MIME, e->e_flags))
557 ee->e_flags |= EF_DONT_MIME;
560 ** If we can't convert to MIME and we don't pass
561 ** 8-bit, we can't send the body.
564 if (bitset(EF_HAS8BIT, e->e_flags) &&
565 !bitset(MM_PASS8BIT, MimeMode))
566 flags &= ~RTSF_SEND_BODY;
569 ee->e_sendqueue = returnq;
570 ee->e_msgsize = ERRORFUDGE;
571 if (bitset(RTSF_SEND_BODY, flags) &&
572 !bitset(PRIV_NOBODYRETN, PrivacyFlags))
573 ee->e_msgsize += e->e_msgsize;
575 ee->e_flags |= EF_NO_BODY_RETN;
579 _res.retry = TimeOuts.res_retry[RES_TO_FIRST];
580 _res.retrans = TimeOuts.res_retrans[RES_TO_FIRST];
581 #endif /* NAMED_BIND */
582 for (q = returnq; q != NULL; q = q->q_next)
584 if (QS_IS_BADADDR(q->q_state))
587 q->q_flags &= ~(QHASNOTIFY|Q_PINGFLAGS);
588 q->q_flags |= QPINGONFAILURE;
590 if (!QS_IS_DEAD(q->q_state))
593 if (q->q_alias == NULL)
594 addheader("To", q->q_paddr, 0, &ee->e_header);
599 if (bitset(EF_RESPONSE, e->e_flags))
600 p = "return to sender";
601 else if (bitset(EF_WARNING, e->e_flags))
603 else if (bitset(RTSF_PM_BOUNCE, flags))
604 p = "postmaster notify";
607 sm_syslog(LOG_INFO, e->e_id,
609 ee->e_id, p, shortenstring(msg, MAXSHORTSTR));
614 addheader("MIME-Version", "1.0", 0, &ee->e_header);
616 (void) snprintf(buf, sizeof buf, "%s.%ld/%.100s",
617 ee->e_id, curtime(), MyHostName);
618 ee->e_msgboundary = newstr(buf);
619 (void) snprintf(buf, sizeof buf,
621 "multipart/report; report-type=delivery-status;\n\tboundary=\"%s\"",
623 "multipart/mixed; boundary=\"%s\"",
626 addheader("Content-Type", buf, 0, &ee->e_header);
628 p = hvalue("Content-Transfer-Encoding", e->e_header);
629 if (p != NULL && strcasecmp(p, "binary") != 0)
631 if (p == NULL && bitset(EF_HAS8BIT, e->e_flags))
634 addheader("Content-Transfer-Encoding",
635 p, 0, &ee->e_header);
637 if (strncmp(msg, "Warning:", 8) == 0)
639 addheader("Subject", msg, 0, &ee->e_header);
640 p = "warning-timeout";
642 else if (strncmp(msg, "Postmaster warning:", 19) == 0)
644 addheader("Subject", msg, 0, &ee->e_header);
645 p = "postmaster-warning";
647 else if (strcmp(msg, "Return receipt") == 0)
649 addheader("Subject", msg, 0, &ee->e_header);
650 p = "return-receipt";
652 else if (bitset(RTSF_PM_BOUNCE, flags))
654 snprintf(buf, sizeof buf,
655 "Postmaster notify: see transcript for details");
656 addheader("Subject", buf, 0, &ee->e_header);
657 p = "postmaster-notification";
661 snprintf(buf, sizeof buf,
662 "Returned mail: see transcript for details");
663 addheader("Subject", buf, 0, &ee->e_header);
666 (void) snprintf(buf, sizeof buf, "auto-generated (%s)", p);
667 addheader("Auto-Submitted", buf, 0, &ee->e_header);
669 /* fake up an address header for the from person */
670 expand("\201n", buf, sizeof buf, e);
671 if (parseaddr(buf, &ee->e_from,
672 RF_COPYALL|RF_SENDERADDR, '\0', NULL, e) == NULL)
674 syserr("553 5.3.5 Can't parse myself!");
675 ExitStat = EX_SOFTWARE;
679 ee->e_from.q_flags &= ~(QHASNOTIFY|Q_PINGFLAGS);
680 ee->e_from.q_flags |= QPINGONFAILURE;
681 ee->e_sender = ee->e_from.q_paddr;
683 /* push state into submessage */
685 define('f', "\201n", ee);
686 define('x', "Mail Delivery Subsystem", ee);
689 /* mark statistics */
690 markstats(ee, NULLADDR, FALSE);
692 /* actually deliver the error message */
693 sendall(ee, SM_DELIVER);
696 dropenvelope(ee, TRUE);
700 /* check for delivery errors */
701 if (ee->e_parent == NULL ||
702 !bitset(EF_RESPONSE, ee->e_parent->e_flags))
704 for (q = ee->e_sendqueue; q != NULL; q = q->q_next)
706 if (QS_IS_ATTEMPTED(q->q_state))
712 ** ERRBODY -- output the body of an error message.
714 ** Typically this is a copy of the transcript plus a copy of the
715 ** original offending message.
718 ** mci -- the mailer connection information.
719 ** e -- the envelope we are working in.
720 ** separator -- any possible MIME separator.
726 ** Outputs the body of an error message.
731 errbody(mci, e, separator)
733 register ENVELOPE *e;
740 register FILE *xfile;
742 register ADDRESS *q = NULL;
745 if (bitset(MCIF_INHEADER, mci->mci_flags))
748 mci->mci_flags &= ~MCIF_INHEADER;
750 if (e->e_parent == NULL)
752 syserr("errbody: null parent");
753 putline(" ----- Original message lost -----\n", mci);
758 ** Output MIME header.
761 if (e->e_msgboundary != NULL)
763 putline("This is a MIME-encapsulated message", mci);
765 (void) snprintf(buf, sizeof buf, "--%s", e->e_msgboundary);
771 ** Output introductory information.
775 p = hvalue("subject", e->e_header);
776 if (p != NULL && strncmp(p, "Postmaster ", 11) == 0)
780 for (q = e->e_parent->e_sendqueue; q != NULL; q = q->q_next)
782 if (QS_IS_BADADDR(q->q_state))
786 if (!pm_notify && q == NULL &&
787 !bitset(EF_FATALERRS|EF_SENDRECEIPT, e->e_parent->e_flags))
789 putline(" **********************************************",
791 putline(" ** THIS IS A WARNING MESSAGE ONLY **",
793 putline(" ** YOU DO NOT NEED TO RESEND YOUR MESSAGE **",
795 putline(" **********************************************",
799 snprintf(buf, sizeof buf, "The original message was received at %s",
800 arpadate(ctime(&e->e_parent->e_ctime)));
802 expand("from \201_", buf, sizeof buf, e->e_parent);
805 /* include id in postmaster copies */
806 if (pm_notify && e->e_parent->e_id != NULL)
808 snprintf(buf, sizeof buf, "with id %s", e->e_parent->e_id);
814 ** Output error message header (if specified and available).
817 if (ErrMsgFile != NULL &&
818 !bitset(EF_SENDRECEIPT, e->e_parent->e_flags))
820 if (*ErrMsgFile == '/')
822 long sff = SFF_ROOTOK|SFF_REGONLY;
824 if (DontLockReadFiles)
826 if (!bitnset(DBS_ERRORHEADERINUNSAFEDIRPATH,
828 sff |= SFF_SAFEDIRPATH;
829 xfile = safefopen(ErrMsgFile, O_RDONLY, 0444, sff);
832 while (fgets(buf, sizeof buf, xfile) != NULL)
834 translate_dollars(buf);
835 expand(buf, buf, sizeof buf, e);
838 (void) fclose(xfile);
844 expand(ErrMsgFile, buf, sizeof buf, e);
851 ** Output message introduction
855 for (q = e->e_parent->e_sendqueue; q != NULL; q = q->q_next)
857 if (!QS_IS_BADADDR(q->q_state) ||
858 !bitset(QPINGONFAILURE, q->q_flags))
863 putline(" ----- The following addresses had permanent fatal errors -----",
868 snprintf(buf, sizeof buf, "%s",
869 shortenstring(q->q_paddr, MAXSHORTSTR));
871 if (q->q_rstatus != NULL)
873 snprintf(buf, sizeof buf, " (reason: %s)",
874 shortenstring(exitstat(q->q_rstatus),
878 if (q->q_alias != NULL)
880 snprintf(buf, sizeof buf, " (expanded from: %s)",
881 shortenstring(q->q_alias->q_paddr,
890 for (q = e->e_parent->e_sendqueue; q != NULL; q = q->q_next)
892 if (QS_IS_BADADDR(q->q_state) ||
893 !bitset(QPRIMARY, q->q_flags) ||
894 !bitset(QDELAYED, q->q_flags))
899 putline(" ----- The following addresses had transient non-fatal errors -----",
904 snprintf(buf, sizeof buf, "%s",
905 shortenstring(q->q_paddr, MAXSHORTSTR));
907 if (q->q_alias != NULL)
909 snprintf(buf, sizeof buf, " (expanded from: %s)",
910 shortenstring(q->q_alias->q_paddr,
919 for (q = e->e_parent->e_sendqueue; q != NULL; q = q->q_next)
921 if (QS_IS_BADADDR(q->q_state) ||
922 !bitset(QPRIMARY, q->q_flags) ||
923 bitset(QDELAYED, q->q_flags))
925 else if (!bitset(QPINGONSUCCESS, q->q_flags))
927 else if (bitset(QRELAYED, q->q_flags))
928 p = "relayed to non-DSN-aware mailer";
929 else if (bitset(QDELIVERED, q->q_flags))
931 if (bitset(QEXPANDED, q->q_flags))
932 p = "successfully delivered to mailing list";
934 p = "successfully delivered to mailbox";
936 else if (bitset(QEXPANDED, q->q_flags))
937 p = "expanded by alias";
943 putline(" ----- The following addresses had successful delivery notifications -----",
948 snprintf(buf, sizeof buf, "%s (%s)",
949 shortenstring(q->q_paddr, MAXSHORTSTR), p);
951 if (q->q_alias != NULL)
953 snprintf(buf, sizeof buf, " (expanded from: %s)",
954 shortenstring(q->q_alias->q_paddr,
963 ** Output transcript of errors
966 (void) fflush(stdout);
967 if (e->e_parent->e_xfp == NULL)
969 putline(" ----- Transcript of session is unavailable -----\n",
975 (void) bfrewind(e->e_parent->e_xfp);
976 if (e->e_xfp != NULL)
977 (void) fflush(e->e_xfp);
978 while (fgets(buf, sizeof buf, e->e_parent->e_xfp) != NULL)
981 putline(" ----- Transcript of session follows -----\n",
991 ** Output machine-readable version.
994 if (e->e_msgboundary != NULL)
997 (void) snprintf(buf, sizeof buf, "--%s", e->e_msgboundary);
999 putline("Content-Type: message/delivery-status", mci);
1003 ** Output per-message information.
1006 /* original envelope id from MAIL FROM: line */
1007 if (e->e_parent->e_envid != NULL)
1009 (void) snprintf(buf, sizeof buf,
1010 "Original-Envelope-Id: %.800s",
1011 xuntextify(e->e_parent->e_envid));
1015 /* Reporting-MTA: is us (required) */
1016 (void) snprintf(buf, sizeof buf, "Reporting-MTA: dns; %.800s", MyHostName);
1019 /* DSN-Gateway: not relevant since we are not translating */
1021 /* Received-From-MTA: shows where we got this message from */
1022 if (RealHostName != NULL)
1024 /* XXX use $s for type? */
1025 if (e->e_parent->e_from.q_mailer == NULL ||
1026 (p = e->e_parent->e_from.q_mailer->m_mtatype) == NULL)
1028 (void) snprintf(buf, sizeof buf,
1029 "Received-From-MTA: %s; %.800s",
1034 /* Arrival-Date: -- when it arrived here */
1035 (void) snprintf(buf, sizeof buf, "Arrival-Date: %s",
1036 arpadate(ctime(&e->e_parent->e_ctime)));
1040 ** Output per-address information.
1043 for (q = e->e_parent->e_sendqueue; q != NULL; q = q->q_next)
1045 register ADDRESS *r;
1048 if (QS_IS_BADADDR(q->q_state))
1050 else if (!bitset(QPRIMARY, q->q_flags))
1052 else if (bitset(QDELIVERED, q->q_flags))
1054 if (bitset(QEXPANDED, q->q_flags))
1055 action = "delivered (to mailing list)";
1057 action = "delivered (to mailbox)";
1059 else if (bitset(QRELAYED, q->q_flags))
1060 action = "relayed (to non-DSN-aware mailer)";
1061 else if (bitset(QEXPANDED, q->q_flags))
1062 action = "expanded (to multi-recipient alias)";
1063 else if (bitset(QDELAYED, q->q_flags))
1070 /* Original-Recipient: -- passed from on high */
1071 if (q->q_orcpt != NULL)
1073 (void) snprintf(buf, sizeof buf,
1074 "Original-Recipient: %.800s",
1079 /* Final-Recipient: -- the name from the RCPT command */
1080 p = e->e_parent->e_from.q_mailer->m_addrtype;
1083 for (r = q; r->q_alias != NULL; r = r->q_alias)
1085 if (strcasecmp(p, "rfc822") != 0)
1087 (void) snprintf(buf, sizeof buf,
1088 "Final-Recipient: %s; %.800s",
1089 r->q_mailer->m_addrtype,
1092 else if (strchr(r->q_user, '@') != NULL)
1094 (void) snprintf(buf, sizeof buf,
1095 "Final-Recipient: %s; %.800s",
1098 else if (strchr(r->q_paddr, '@') != NULL)
1104 /* strip brackets from address */
1108 b = qp[strlen(qp) - 1] == '>';
1110 qp[strlen(qp) - 1] = '\0';
1113 (void) snprintf(buf, sizeof buf,
1114 "Final-Recipient: %s; %.800s",
1118 qp[strlen(qp)] = '>';
1122 (void) snprintf(buf, sizeof buf,
1123 "Final-Recipient: %s; %.700s@%.100s",
1124 p, r->q_user, MyHostName);
1128 /* X-Actual-Recipient: -- the real problem address */
1129 if (r != q && q->q_user[0] != '\0')
1131 if (q->q_mailer != NULL &&
1132 q->q_mailer->m_addrtype != NULL)
1133 p = q->q_mailer->m_addrtype;
1137 if (strcasecmp(p, "rfc822") == 0 &&
1138 strchr(q->q_user, '@') == NULL)
1140 (void) snprintf(buf, sizeof buf,
1141 "X-Actual-Recipient: %s; %.700s@%.100s",
1147 (void) snprintf(buf, sizeof buf,
1148 "X-Actual-Recipient: %s; %.800s",
1154 /* Action: -- what happened? */
1155 snprintf(buf, sizeof buf, "Action: %s", action);
1158 /* Status: -- what _really_ happened? */
1159 if (q->q_status != NULL)
1161 else if (QS_IS_BADADDR(q->q_state))
1163 else if (QS_IS_QUEUEUP(q->q_state))
1167 snprintf(buf, sizeof buf, "Status: %s", p);
1170 /* Remote-MTA: -- who was I talking to? */
1171 if (q->q_statmta != NULL)
1173 if (q->q_mailer == NULL ||
1174 (p = q->q_mailer->m_mtatype) == NULL)
1176 (void) snprintf(buf, sizeof buf,
1177 "Remote-MTA: %s; %.800s",
1179 p = &buf[strlen(buf) - 1];
1185 /* Diagnostic-Code: -- actual result from other end */
1186 if (q->q_rstatus != NULL)
1188 p = q->q_mailer->m_diagtype;
1191 (void) snprintf(buf, sizeof buf,
1192 "Diagnostic-Code: %s; %.800s",
1197 /* Last-Attempt-Date: -- fine granularity */
1198 if (q->q_statdate == (time_t) 0L)
1199 q->q_statdate = curtime();
1200 (void) snprintf(buf, sizeof buf,
1201 "Last-Attempt-Date: %s",
1202 arpadate(ctime(&q->q_statdate)));
1205 /* Will-Retry-Until: -- for delayed messages only */
1206 if (QS_IS_QUEUEUP(q->q_state))
1210 xdate = e->e_parent->e_ctime +
1211 TimeOuts.to_q_return[e->e_parent->e_timeoutclass];
1212 snprintf(buf, sizeof buf,
1213 "Will-Retry-Until: %s",
1214 arpadate(ctime(&xdate)));
1222 ** Output text of original message
1226 if (bitset(EF_HAS_DF, e->e_parent->e_flags))
1228 sendbody = !bitset(EF_NO_BODY_RETN, e->e_parent->e_flags) &&
1229 !bitset(EF_NO_BODY_RETN, e->e_flags);
1231 if (e->e_msgboundary == NULL)
1234 putline(" ----- Original message follows -----\n", mci);
1236 putline(" ----- Message header follows -----\n", mci);
1240 (void) snprintf(buf, sizeof buf, "--%s",
1244 (void) snprintf(buf, sizeof buf, "Content-Type: %s",
1245 sendbody ? "message/rfc822"
1246 : "text/rfc822-headers");
1249 p = hvalue("Content-Transfer-Encoding",
1250 e->e_parent->e_header);
1251 if (p != NULL && strcasecmp(p, "binary") != 0)
1254 bitset(EF_HAS8BIT, e->e_parent->e_flags))
1258 (void) snprintf(buf, sizeof buf,
1259 "Content-Transfer-Encoding: %s",
1266 putheader(mci, e->e_parent->e_header, e->e_parent, M87F_OUTER);
1269 putbody(mci, e->e_parent, e->e_msgboundary);
1270 else if (e->e_msgboundary == NULL)
1273 putline(" ----- Message body suppressed -----", mci);
1276 else if (e->e_msgboundary == NULL)
1278 putline(" ----- No message was collected -----\n", mci);
1281 if (e->e_msgboundary != NULL)
1284 (void) snprintf(buf, sizeof buf, "--%s--", e->e_msgboundary);
1288 (void) fflush(mci->mci_out);
1295 syserr("errbody: I/O error");
1298 ** SMTPTODSN -- convert SMTP to DSN status code
1301 ** smtpstat -- the smtp status code (e.g., 550).
1304 ** The DSN version of the status code.
1316 case 450: /* Req mail action not taken: mailbox unavailable */
1319 case 451: /* Req action aborted: local error in processing */
1322 case 452: /* Req action not taken: insufficient sys storage */
1325 case 500: /* Syntax error, command unrecognized */
1328 case 501: /* Syntax error in parameters or arguments */
1331 case 502: /* Command not implemented */
1334 case 503: /* Bad sequence of commands */
1337 case 504: /* Command parameter not implemented */
1340 case 550: /* Req mail action not taken: mailbox unavailable */
1343 case 551: /* User not local; please try <...> */
1346 case 552: /* Req mail action aborted: exceeded storage alloc */
1349 case 553: /* Req action not taken: mailbox name not allowed */
1352 case 554: /* Transaction failed */
1356 if ((smtpstat / 100) == 2)
1358 if ((smtpstat / 100) == 4)
1363 ** XTEXTIFY -- take regular text and turn it into DSN-style xtext
1366 ** t -- the text to convert.
1367 ** taboo -- additional characters that must be encoded.
1370 ** The xtext-ified version of the same string.
1381 static char *bp = NULL;
1382 static int bplen = 0;
1387 /* figure out how long this xtext will have to be */
1389 for (p = t; *p != '\0'; p++)
1391 register int c = (*p & 0xff);
1393 /* ASCII dependence here -- this is the way the spec words it */
1394 if (c < '!' || c > '~' || c == '+' || c == '\\' || c == '(' ||
1395 strchr(taboo, c) != NULL)
1401 l += nbogus * 2 + 1;
1403 /* now allocate space if necessary for the new string */
1412 /* ok, copy the text with byte expansion */
1413 for (p = bp; *t != '\0'; )
1415 register int c = (*t++ & 0xff);
1417 /* ASCII dependence here -- this is the way the spec words it */
1418 if (c < '!' || c > '~' || c == '+' || c == '\\' || c == '(' ||
1419 strchr(taboo, c) != NULL)
1422 *p++ = "0123456789ABCDEF"[c >> 4];
1423 *p++ = "0123456789ABCDEF"[c & 0xf];
1432 ** XUNTEXTIFY -- take xtext and turn it into plain text
1435 ** t -- the xtextified text.
1438 ** The decoded text. No attempt is made to deal with
1439 ** null strings in the resulting text.
1448 static char *bp = NULL;
1449 static int bplen = 0;
1451 /* heuristic -- if no plus sign, just return the input */
1452 if (strchr(t, '+') == NULL)
1455 /* xtext is always longer than decoded text */
1465 /* ok, copy the text with byte compression */
1466 for (p = bp; *t != '\0'; t++)
1468 register int c = *t & 0xff;
1477 if (!isascii(c) || !isxdigit(c))
1479 /* error -- first digit is not hex */
1480 usrerr("bogus xtext: +%c", c);
1486 else if (isupper(c))
1493 if (!isascii(c) || !isxdigit(c))
1495 /* error -- second digit is not hex */
1496 usrerr("bogus xtext: +%x%c", *p >> 4, c);
1502 else if (isupper(c))
1512 ** XTEXTOK -- check if a string is legal xtext
1514 ** Xtext is used in Delivery Status Notifications. The spec was
1515 ** taken from RFC 1891, ``SMTP Service Extension for Delivery
1516 ** Status Notifications''.
1519 ** s -- the string to check.
1522 ** TRUE -- if 's' is legal xtext.
1523 ** FALSE -- if it has any illegal characters in it.
1532 while ((c = *s++) != '\0')
1537 if (!isascii(c) || !isxdigit(c))
1540 if (!isascii(c) || !isxdigit(c))
1543 else if (c < '!' || c > '~' || c == '=')
1549 ** PRUNEROUTE -- prune an RFC-822 source route
1551 ** Trims down a source route to the last internet-registered hop.
1552 ** This is encouraged by RFC 1123 section 5.3.3.
1555 ** addr -- the address
1558 ** TRUE -- address was modified
1559 ** FALSE -- address could not be pruned
1562 ** modifies addr in-place
1570 char *start, *at, *comma;
1574 char hostbuf[BUFSIZ];
1575 char *mxhosts[MAXMXHOSTS + 1];
1577 /* check to see if this is really a route-addr */
1578 if (*addr != '<' || addr[1] != '@' || addr[strlen(addr) - 1] != '>')
1580 start = strchr(addr, ':');
1581 at = strrchr(addr, '@');
1582 if (start == NULL || at == NULL || at < start)
1585 /* slice off the angle brackets */
1587 if (i >= (SIZE_T) sizeof hostbuf)
1589 (void) strlcpy(hostbuf, at + 1, sizeof hostbuf);
1590 hostbuf[i - 1] = '\0';
1594 if (getmxrr(hostbuf, mxhosts, NULL, FALSE, &rcode) > 0)
1596 (void) strlcpy(addr + 1, start + 1, strlen(addr) - 1);
1601 comma = strrchr(addr, ',');
1602 if (comma != NULL && comma[1] == '@' &&
1603 strlen(comma + 2) < (SIZE_T) sizeof hostbuf)
1604 (void) strlcpy(hostbuf, comma + 2, sizeof hostbuf);
1610 #endif /* NAMED_BIND */