change call to expand() to be more rational (and consistent!)
[unix-history] / usr / src / usr.sbin / sendmail / src / savemail.c
index 777348a..5cfa6e8 100644 (file)
@@ -7,11 +7,11 @@
  */
 
 #ifndef lint
  */
 
 #ifndef lint
-static char sccsid[] = "@(#)savemail.c 8.2 (Berkeley) %G%";
+static char sccsid[] = "@(#)savemail.c 8.56 (Berkeley) %G%";
 #endif /* not lint */
 
 #endif /* not lint */
 
-# include <pwd.h>
 # include "sendmail.h"
 # include "sendmail.h"
+# include <pwd.h>
 
 /*
 **  SAVEMAIL -- Save mail on error
 
 /*
 **  SAVEMAIL -- Save mail on error
@@ -23,6 +23,8 @@ static char sccsid[] = "@(#)savemail.c        8.2 (Berkeley) %G%";
 **
 **     Parameters:
 **             e -- the envelope containing the message in error.
 **
 **     Parameters:
 **             e -- the envelope containing the message in error.
+**             sendbody -- if TRUE, also send back the body of the
+**                     message; otherwise just send the header.
 **
 **     Returns:
 **             none
 **
 **     Returns:
 **             none
@@ -43,24 +45,32 @@ static char sccsid[] = "@(#)savemail.c      8.2 (Berkeley) %G%";
 # define ESM_PANIC     6       /* leave the locked queue/transcript files */
 # define ESM_DONE      7       /* the message is successfully delivered */
 
 # define ESM_PANIC     6       /* leave the locked queue/transcript files */
 # define ESM_DONE      7       /* the message is successfully delivered */
 
+# ifndef _PATH_VARTMP
+#  define _PATH_VARTMP "/usr/tmp/"
+# endif
+
 
 
-savemail(e)
+savemail(e, sendbody)
        register ENVELOPE *e;
        register ENVELOPE *e;
+       bool sendbody;
 {
        register struct passwd *pw;
        register FILE *fp;
        int state;
        auto ADDRESS *q = NULL;
 {
        register struct passwd *pw;
        register FILE *fp;
        int state;
        auto ADDRESS *q = NULL;
+       register char *p;
+       MCI mcibuf;
        char buf[MAXLINE+1];
        extern struct passwd *getpwnam();
        char buf[MAXLINE+1];
        extern struct passwd *getpwnam();
-       register char *p;
        extern char *ttypath();
        typedef int (*fnptr)();
        extern char *ttypath();
        typedef int (*fnptr)();
+       extern bool writable();
 
        if (tTd(6, 1))
        {
 
        if (tTd(6, 1))
        {
-               printf("\nsavemail, errormode = %c, id = %s\n  e_from=",
-                       e->e_errormode, e->e_id == NULL ? "NONE" : e->e_id);
+               printf("\nsavemail, errormode = %c, id = %s, ExitStat = %d\n  e_from=",
+                       e->e_errormode, e->e_id == NULL ? "NONE" : e->e_id,
+                       ExitStat);
                printaddr(&e->e_from, FALSE);
        }
 
                printaddr(&e->e_from, FALSE);
        }
 
@@ -70,8 +80,6 @@ savemail(e)
                return;
        }
 
                return;
        }
 
-       e->e_flags &= ~EF_FATALERRS;
-
        /*
        **  In the unhappy event we don't know who to return the mail
        **  to, make someone up.
        /*
        **  In the unhappy event we don't know who to return the mail
        **  to, make someone up.
@@ -155,7 +163,7 @@ savemail(e)
                switch (state)
                {
                  case ESM_QUIET:
                switch (state)
                {
                  case ESM_QUIET:
-                       if (e->e_from.q_mailer == LocalMailer)
+                       if (bitnset(M_LOCALMAILER, e->e_from.q_mailer->m_flags))
                                state = ESM_DEADLETTER;
                        else
                                state = ESM_MAIL;
                                state = ESM_DEADLETTER;
                        else
                                state = ESM_MAIL;
@@ -175,7 +183,7 @@ savemail(e)
                                break;
                        }
 
                                break;
                        }
 
-                       expand("\201n", buf, &buf[sizeof buf - 1], e);
+                       expand("\201n", buf, sizeof buf, e);
                        printf("\r\nMessage from %s...\r\n", buf);
                        printf("Errors occurred while sending mail.\r\n");
                        if (e->e_xfp != NULL)
                        printf("\r\nMessage from %s...\r\n", buf);
                        printf("Errors occurred while sending mail.\r\n");
                        if (e->e_xfp != NULL)
@@ -212,33 +220,48 @@ savemail(e)
                        **      joe@x, which gives a response, etc.  Also force
                        **      the mail to be delivered even if a version of
                        **      it has already been sent to the sender.
                        **      joe@x, which gives a response, etc.  Also force
                        **      the mail to be delivered even if a version of
                        **      it has already been sent to the sender.
+                       **
+                       **  If this is a configuration or local software
+                       **      error, send to the local postmaster as well,
+                       **      since the originator can't do anything
+                       **      about it anyway.  Note that this is a full
+                       **      copy of the message (intentionally) so that
+                       **      the Postmaster can forward things along.
                        */
 
                        */
 
-                       if (strcmp(e->e_from.q_paddr, "<>") != 0)
+                       if (ExitStat == EX_CONFIG || ExitStat == EX_SOFTWARE)
+                       {
+                               (void) sendtolist("postmaster",
+                                         NULLADDR, &e->e_errorqueue, 0, e);
+                       }
+                       if (!emptyaddr(&e->e_from))
+                       {
                                (void) sendtolist(e->e_from.q_paddr,
                                (void) sendtolist(e->e_from.q_paddr,
-                                         (ADDRESS *) NULL,
-                                         &e->e_errorqueue, e);
+                                         NULLADDR, &e->e_errorqueue, 0, e);
+                       }
 
 
-                       /* deliver a cc: to the postmaster if desired */
-                       if (PostMasterCopy != NULL)
-                       {
-                               auto ADDRESS *rlist = NULL;
+                       /*
+                       **  Deliver a non-delivery report to the
+                       **  Postmaster-designate (not necessarily
+                       **  Postmaster).  This does not include the
+                       **  body of the message, for privacy reasons.
+                       **  You really shouldn't need this.
+                       */
 
 
-                               (void) sendtolist(PostMasterCopy,
-                                                 (ADDRESS *) NULL,
-                                                 &rlist, e);
-                               (void) returntosender(e->e_message,
-                                                     rlist, FALSE, e);
-                       }
-                       q = e->e_errorqueue;
+                       e->e_flags |= EF_PM_NOTIFY;
+
+                       /* check to see if there are any good addresses */
+                       for (q = e->e_errorqueue; q != NULL; q = q->q_next)
+                               if (!bitset(QBADADDR|QDONTSEND, q->q_flags))
+                                       break;
                        if (q == NULL)
                        {
                                /* this is an error-error */
                                state = ESM_POSTMASTER;
                                break;
                        }
                        if (q == NULL)
                        {
                                /* this is an error-error */
                                state = ESM_POSTMASTER;
                                break;
                        }
-                       if (returntosender(e->e_message,
-                                          q, (e->e_class >= 0), e) == 0)
+                       if (returntosender(e->e_message, e->e_errorqueue,
+                                          sendbody, e) == 0)
                        {
                                state = ESM_DONE;
                                break;
                        {
                                state = ESM_DONE;
                                break;
@@ -254,15 +277,14 @@ savemail(e)
                        */
 
                        q = NULL;
                        */
 
                        q = NULL;
-                       if (sendtolist("postmaster", NULL, &q, e) <= 0)
+                       if (sendtolist("postmaster", NULL, &q, 0, e) <= 0)
                        {
                                syserr("553 cannot parse postmaster!");
                                ExitStat = EX_SOFTWARE;
                                state = ESM_USRTMP;
                                break;
                        }
                        {
                                syserr("553 cannot parse postmaster!");
                                ExitStat = EX_SOFTWARE;
                                state = ESM_USRTMP;
                                break;
                        }
-                       if (returntosender(e->e_message,
-                                          q, (e->e_class >= 0), e) == 0)
+                       if (returntosender(e->e_message, q, sendbody, e) == 0)
                        {
                                state = ESM_DONE;
                                break;
                        {
                                state = ESM_DONE;
                                break;
@@ -283,7 +305,7 @@ savemail(e)
                        */
 
                        p = NULL;
                        */
 
                        p = NULL;
-                       if (e->e_from.q_mailer == LocalMailer)
+                       if (bitnset(M_HASPWENT, e->e_from.q_mailer->m_flags))
                        {
                                if (e->e_from.q_home != NULL)
                                        p = e->e_from.q_home;
                        {
                                if (e->e_from.q_home != NULL)
                                        p = e->e_from.q_home;
@@ -302,14 +324,16 @@ savemail(e)
 
                                /* we have a home directory; open dead.letter */
                                define('z', p, e);
 
                                /* we have a home directory; open dead.letter */
                                define('z', p, e);
-                               expand("\201z/dead.letter", buf, &buf[sizeof buf - 1], e);
+                               expand("\201z/dead.letter", buf, sizeof buf, e);
                                Verbose = TRUE;
                                message("Saving message in %s", buf);
                                Verbose = oldverb;
                                e->e_to = buf;
                                q = NULL;
                                Verbose = TRUE;
                                message("Saving message in %s", buf);
                                Verbose = oldverb;
                                e->e_to = buf;
                                q = NULL;
-                               (void) sendtolist(buf, &e->e_from, &q, e);
-                               if (deliver(e, q) == 0)
+                               (void) sendtolist(buf, &e->e_from, &q, 0, e);
+                               if (q != NULL &&
+                                   !bitset(QBADADDR, q->q_flags) &&
+                                   deliver(e, q) == 0)
                                        state = ESM_DONE;
                                else
                                        state = ESM_MAIL;
                                        state = ESM_DONE;
                                else
                                        state = ESM_MAIL;
@@ -332,22 +356,40 @@ savemail(e)
                                break;
                        }
 
                                break;
                        }
 
-                       fp = dfopen("/usr/tmp/dead.letter",
-                                   O_WRONLY|O_CREAT|O_APPEND, FileMode);
+                       if (SafeFileEnv != NULL && SafeFileEnv[0] != '\0')
+                       {
+                               state = ESM_PANIC;
+                               break;
+                       }
+
+                       strcpy(buf, _PATH_VARTMP);
+                       strcat(buf, "dead.letter");
+                       if (!writable(buf, NULLADDR, SFF_NOSLINK|SFF_CREAT))
+                       {
+                               state = ESM_PANIC;
+                               break;
+                       }
+                       fp = safefopen(buf, O_WRONLY|O_CREAT|O_APPEND,
+                                       FileMode, SFF_NOSLINK|SFF_REGONLY);
                        if (fp == NULL)
                        {
                                state = ESM_PANIC;
                                break;
                        }
 
                        if (fp == NULL)
                        {
                                state = ESM_PANIC;
                                break;
                        }
 
-                       putfromline(fp, FileMailer, e);
-                       (*e->e_puthdr)(fp, FileMailer, e);
-                       putline("\n", fp, FileMailer);
-                       (*e->e_putbody)(fp, FileMailer, e, NULL);
-                       putline("\n", fp, FileMailer);
+                       bzero(&mcibuf, sizeof mcibuf);
+                       mcibuf.mci_out = fp;
+                       mcibuf.mci_mailer = FileMailer;
+                       if (bitnset(M_7BITS, FileMailer->m_flags))
+                               mcibuf.mci_flags |= MCIF_7BIT;
+
+                       putfromline(&mcibuf, e);
+                       (*e->e_puthdr)(&mcibuf, e->e_header, e);
+                       (*e->e_putbody)(&mcibuf, e, NULL);
+                       putline("\n", &mcibuf);
                        (void) fflush(fp);
                        state = ferror(fp) ? ESM_PANIC : ESM_DONE;
                        (void) fflush(fp);
                        state = ferror(fp) ? ESM_PANIC : ESM_DONE;
-                       (void) xfclose(fp, "savemail", "/usr/tmp/dead.letter");
+                       (void) xfclose(fp, "savemail", buf);
                        break;
 
                  default:
                        break;
 
                  default:
@@ -357,8 +399,8 @@ savemail(e)
 
                  case ESM_PANIC:
                        /* leave the locked queue & transcript files around */
 
                  case ESM_PANIC:
                        /* leave the locked queue & transcript files around */
-                       syserr("554 savemail: cannot save rejected email anywhere");
-                       exit(EX_SOFTWARE);
+                       loseqfile(e, "savemail panic");
+                       syserr("!554 savemail: cannot save rejected email anywhere");
                }
        }
 }
                }
        }
 }
@@ -392,13 +434,14 @@ returntosender(msg, returnq, sendbody, e)
        bool sendbody;
        register ENVELOPE *e;
 {
        bool sendbody;
        register ENVELOPE *e;
 {
-       char buf[MAXNAME];
+       char buf[MAXNAME + 1];
        extern putheader(), errbody();
        register ENVELOPE *ee;
        ENVELOPE *oldcur = CurEnv;
        ENVELOPE errenvelope;
        static int returndepth;
        register ADDRESS *q;
        extern putheader(), errbody();
        register ENVELOPE *ee;
        ENVELOPE *oldcur = CurEnv;
        ENVELOPE errenvelope;
        static int returndepth;
        register ADDRESS *q;
+       char *p;
 
        if (returnq == NULL)
                return (-1);
 
        if (returnq == NULL)
                return (-1);
@@ -408,9 +451,14 @@ returntosender(msg, returnq, sendbody, e)
 
        if (tTd(6, 1))
        {
 
        if (tTd(6, 1))
        {
-               printf("Return To Sender: msg=\"%s\", depth=%d, e=%x, returnq=",
+               printf("\n*** Return To Sender: msg=\"%s\", depth=%d, e=%x, returnq=",
                       msg, returndepth, e);
                printaddr(returnq, TRUE);
                       msg, returndepth, e);
                printaddr(returnq, TRUE);
+               if (tTd(6, 20))
+               {
+                       printf("Sendq=");
+                       printaddr(e->e_sendqueue, TRUE);
+               }
        }
 
        if (++returndepth >= MAXRETURNS)
        }
 
        if (++returndepth >= MAXRETURNS)
@@ -424,6 +472,9 @@ returntosender(msg, returnq, sendbody, e)
 
        SendBody = sendbody;
        define('g', e->e_from.q_paddr, e);
 
        SendBody = sendbody;
        define('g', e->e_from.q_paddr, e);
+       define('u', NULL, e);
+
+       /* initialize error envelope */
        ee = newenvelope(&errenvelope, e);
        define('a', "\201b", ee);
        define('r', "internal", ee);
        ee = newenvelope(&errenvelope, e);
        define('a', "\201b", ee);
        define('r', "internal", ee);
@@ -431,49 +482,54 @@ returntosender(msg, returnq, sendbody, e)
        define('_', "localhost", ee);
        ee->e_puthdr = putheader;
        ee->e_putbody = errbody;
        define('_', "localhost", ee);
        ee->e_puthdr = putheader;
        ee->e_putbody = errbody;
-       ee->e_flags |= EF_RESPONSE;
+       ee->e_flags |= EF_RESPONSE|EF_METOO;
        if (!bitset(EF_OLDSTYLE, e->e_flags))
                ee->e_flags &= ~EF_OLDSTYLE;
        ee->e_sendqueue = returnq;
        if (!bitset(EF_OLDSTYLE, e->e_flags))
                ee->e_flags &= ~EF_OLDSTYLE;
        ee->e_sendqueue = returnq;
-       ee->e_msgsize = e->e_msgsize + ERRORFUDGE;
-       openxscript(ee);
+       ee->e_msgsize = ERRORFUDGE;
+       if (!bitset(EF_NORETURN, e->e_flags))
+               ee->e_msgsize += e->e_msgsize;
+       initsys(ee);
        for (q = returnq; q != NULL; q = q->q_next)
        {
                if (bitset(QBADADDR, q->q_flags))
                        continue;
 
        for (q = returnq; q != NULL; q = q->q_next)
        {
                if (bitset(QBADADDR, q->q_flags))
                        continue;
 
+               if (!DontPruneRoutes && pruneroute(q->q_paddr))
+               {
+                       register ADDRESS *p;
+
+                       parseaddr(q->q_paddr, q, RF_COPYPARSE, '\0', NULL, e);
+                       for (p = returnq; p != NULL; p = p->q_next)
+                       {
+                               if (p != q && sameaddr(p, q))
+                                       q->q_flags |= QDONTSEND;
+                       }
+               }
+
                if (!bitset(QDONTSEND, q->q_flags))
                        ee->e_nrcpts++;
 
                if (!bitset(QDONTSEND, q->q_flags))
                        ee->e_nrcpts++;
 
-               if (!DontPruneRoutes && pruneroute(q->q_paddr))
-                       parseaddr(q->q_paddr, q, 0, '\0', NULL, e);
-
                if (q->q_alias == NULL)
                if (q->q_alias == NULL)
-                       addheader("To", q->q_paddr, ee);
+                       addheader("To", q->q_paddr, &ee->e_header);
        }
 
 # ifdef LOG
        if (LogLevel > 5)
        }
 
 # ifdef LOG
        if (LogLevel > 5)
-               syslog(LOG_INFO, "%s: %s: return to sender: %s",
+               syslog(LOG_INFO, "%s: %s: returntosender: %s",
                        e->e_id, ee->e_id, msg);
 # endif
 
                        e->e_id, ee->e_id, msg);
 # endif
 
-       (void) sprintf(buf, "Returned mail: %s", msg);
-       addheader("Subject", buf, ee);
-       if (SendMIMEErrors)
+       if (strncasecmp(msg, "warning:", 8) != 0)
        {
        {
-               addheader("MIME-Version", "1.0", ee);
-               (void) sprintf(buf, "%s.%ld/%s",
-                       ee->e_id, curtime(), MyHostName);
-               ee->e_msgboundary = newstr(buf);
-               (void) sprintf(buf, "multipart/mixed; boundary=\"%s\"",
-                                       ee->e_msgboundary);
-               addheader("Content-Type", buf, ee);
+               (void) sprintf(buf, "Returned mail: %s", msg);
+               msg = buf;
        }
        }
+       addheader("Subject", msg, ee);
 
        /* fake up an address header for the from person */
 
        /* fake up an address header for the from person */
-       expand("\201n", buf, &buf[sizeof buf - 1], e);
-       if (parseaddr(buf, &ee->e_from, 1, '\0', NULL, e) == NULL)
+       expand("\201n", buf, sizeof buf, e);
+       if (parseaddr(buf, &ee->e_from, RF_COPYALL|RF_SENDERADDR, '\0', NULL, e) == NULL)
        {
                syserr("553 Can't parse myself!");
                ExitStat = EX_SOFTWARE;
        {
                syserr("553 Can't parse myself!");
                ExitStat = EX_SOFTWARE;
@@ -489,7 +545,7 @@ returntosender(msg, returnq, sendbody, e)
        eatheader(ee, TRUE);
 
        /* mark statistics */
        eatheader(ee, TRUE);
 
        /* mark statistics */
-       markstats(ee, (ADDRESS *) NULL);
+       markstats(ee, NULLADDR);
 
        /* actually deliver the error message */
        sendall(ee, SM_DEFAULT);
 
        /* actually deliver the error message */
        sendall(ee, SM_DEFAULT);
@@ -509,9 +565,10 @@ returntosender(msg, returnq, sendbody, e)
 **     original offending message.
 **
 **     Parameters:
 **     original offending message.
 **
 **     Parameters:
-**             fp -- the output file.
-**             m -- the mailer to output to.
+**             mci -- the mailer connection information.
 **             e -- the envelope we are working in.
 **             e -- the envelope we are working in.
+**             separator -- any possible MIME separator.
+**             flags -- to modify the behaviour.
 **
 **     Returns:
 **             none
 **
 **     Returns:
 **             none
@@ -520,21 +577,27 @@ returntosender(msg, returnq, sendbody, e)
 **             Outputs the body of an error message.
 */
 
 **             Outputs the body of an error message.
 */
 
-errbody(fp, m, e)
-       register FILE *fp;
-       register struct mailer *m;
+errbody(mci, e, separator)
+       register MCI *mci;
        register ENVELOPE *e;
        register ENVELOPE *e;
+       char *separator;
 {
        register FILE *xfile;
        char *p;
        register ADDRESS *q;
        bool printheader;
        char buf[MAXLINE];
 {
        register FILE *xfile;
        char *p;
        register ADDRESS *q;
        bool printheader;
        char buf[MAXLINE];
+       extern char *xtextify();
 
 
+       if (bitset(MCIF_INHEADER, mci->mci_flags))
+       {
+               putline("", mci);
+               mci->mci_flags &= ~MCIF_INHEADER;
+       }
        if (e->e_parent == NULL)
        {
                syserr("errbody: null parent");
        if (e->e_parent == NULL)
        {
                syserr("errbody: null parent");
-               putline("   ----- Original message lost -----\n", fp, m);
+               putline("   ----- Original message lost -----\n", mci);
                return;
        }
 
                return;
        }
 
@@ -544,18 +607,45 @@ errbody(fp, m, e)
 
        if (e->e_msgboundary != NULL)
        {
 
        if (e->e_msgboundary != NULL)
        {
-               putline("This is a MIME-encapsulated message", fp, m);
-               putline("", fp, m);
+               putline("This is a MIME-encapsulated message", mci);
+               putline("", mci);
                (void) sprintf(buf, "--%s", e->e_msgboundary);
                (void) sprintf(buf, "--%s", e->e_msgboundary);
-               putline(buf, fp, m);
-               putline("", fp, m);
+               putline(buf, mci);
+               putline("", mci);
+       }
+
+       /*
+       **  Output introductory information.
+       */
+
+       for (q = e->e_parent->e_sendqueue; q != NULL; q = q->q_next)
+               if (bitset(QBADADDR, q->q_flags))
+                       break;
+       if (q == NULL &&
+           !bitset(EF_FATALERRS|EF_SENDRECEIPT, e->e_parent->e_flags))
+       {
+               putline("    **********************************************",
+                       mci);
+               putline("    **      THIS IS A WARNING MESSAGE ONLY      **",
+                       mci);
+               putline("    **  YOU DO NOT NEED TO RESEND YOUR MESSAGE  **",
+                       mci);
+               putline("    **********************************************",
+                       mci);
+               putline("", mci);
        }
        }
+       sprintf(buf, "The original message was received at %s",
+               arpadate(ctime(&e->e_parent->e_ctime)));
+       putline(buf, mci);
+       expand("from \201_", buf, sizeof buf, e->e_parent);
+       putline(buf, mci);
+       putline("", mci);
 
        /*
        **  Output error message header (if specified and available).
        */
 
 
        /*
        **  Output error message header (if specified and available).
        */
 
-       if (ErrMsgFile != NULL)
+       if (ErrMsgFile != NULL && !bitset(EF_SENDRECEIPT, e->e_parent->e_flags))
        {
                if (*ErrMsgFile == '/')
                {
        {
                if (*ErrMsgFile == '/')
                {
@@ -564,18 +654,18 @@ errbody(fp, m, e)
                        {
                                while (fgets(buf, sizeof buf, xfile) != NULL)
                                {
                        {
                                while (fgets(buf, sizeof buf, xfile) != NULL)
                                {
-                                       expand(buf, buf, &buf[sizeof buf - 1], e);
-                                       putline(buf, fp, m);
+                                       expand(buf, buf, sizeof buf, e);
+                                       putline(buf, mci);
                                }
                                (void) fclose(xfile);
                                }
                                (void) fclose(xfile);
-                               putline("\n", fp, m);
+                               putline("\n", mci);
                        }
                }
                else
                {
                        }
                }
                else
                {
-                       expand(ErrMsgFile, buf, &buf[sizeof buf - 1], e);
-                       putline(buf, fp, m);
-                       putline("", fp, m);
+                       expand(ErrMsgFile, buf, sizeof buf, e);
+                       putline(buf, mci);
+                       putline("", mci);
                }
        }
 
                }
        }
 
@@ -586,22 +676,35 @@ errbody(fp, m, e)
        printheader = TRUE;
        for (q = e->e_parent->e_sendqueue; q != NULL; q = q->q_next)
        {
        printheader = TRUE;
        for (q = e->e_parent->e_sendqueue; q != NULL; q = q->q_next)
        {
-               if (bitset(QBADADDR, q->q_flags))
+               if (bitset(QBADADDR|QREPORT|QRELAYED, q->q_flags))
                {
                        if (printheader)
                        {
                {
                        if (printheader)
                        {
-                               putline("   ----- The following addresses failed -----",
-                                       fp, m);
+                               putline("   ----- The following addresses have delivery notifications -----",
+                                       mci);
                                printheader = FALSE;
                        }
                                printheader = FALSE;
                        }
-                       if (q->q_alias != NULL)
-                               putline(q->q_alias->q_paddr, fp, m);
+                       strcpy(buf, q->q_paddr);
+                       if (bitset(QBADADDR, q->q_flags))
+                               strcat(buf, "  (unrecoverable error)");
+                       else if (bitset(QRELAYED, q->q_flags))
+                               strcat(buf, "  (relayed to non-DSN-aware mailer)");
+                       else if (bitset(QSENT, q->q_flags))
+                               strcat(buf, "  (successfully delivered)");
                        else
                        else
-                               putline(q->q_paddr, fp, m);
+                               strcat(buf, "  (transient failure)");
+                       putline(buf, mci);
+                       if (q->q_alias != NULL)
+                       {
+                               strcpy(buf, "    (expanded from: ");
+                               strcat(buf, q->q_alias->q_paddr);
+                               strcat(buf, ")");
+                               putline(buf, mci);
+                       }
                }
        }
        if (!printheader)
                }
        }
        if (!printheader)
-               putline("\n", fp, m);
+               putline("\n", mci);
 
        /*
        **  Output transcript of errors
 
        /*
        **  Output transcript of errors
@@ -612,61 +715,226 @@ errbody(fp, m, e)
        if ((xfile = fopen(p, "r")) == NULL)
        {
                syserr("Cannot open %s", p);
        if ((xfile = fopen(p, "r")) == NULL)
        {
                syserr("Cannot open %s", p);
-               putline("   ----- Transcript of session is unavailable -----\n", fp, m);
+               putline("   ----- Transcript of session is unavailable -----\n", mci);
        }
        else
        {
        }
        else
        {
-               putline("   ----- Transcript of session follows -----\n", fp, m);
+               putline("   ----- Transcript of session follows -----\n", mci);
                if (e->e_xfp != NULL)
                        (void) fflush(e->e_xfp);
                while (fgets(buf, sizeof buf, xfile) != NULL)
                if (e->e_xfp != NULL)
                        (void) fflush(e->e_xfp);
                while (fgets(buf, sizeof buf, xfile) != NULL)
-                       putline(buf, fp, m);
+                       putline(buf, mci);
                (void) xfclose(xfile, "errbody xscript", p);
        }
        errno = 0;
 
                (void) xfclose(xfile, "errbody xscript", p);
        }
        errno = 0;
 
+#ifdef DSN
+       /*
+       **  Output machine-readable version.
+       */
+
+       if (e->e_msgboundary != NULL)
+       {
+               putline("", mci);
+               (void) sprintf(buf, "--%s", e->e_msgboundary);
+               putline(buf, mci);
+               putline("Content-Type: message/X-delivery-status-2 (Draft of 20 January 1995)", mci);
+               putline("", mci);
+
+               /*
+               **  Output per-message information.
+               */
+
+               /* original envelope id from MAIL FROM: line */
+               if (e->e_parent->e_envid != NULL)
+               {
+                       (void) sprintf(buf, "Original-Envelope-Id: %s",
+                               xtextify(e->e_parent->e_envid));
+                       putline(buf, mci);
+               }
+
+               /* Reporting-MTA: is us (required) */
+               p = e->e_parent->e_from.q_mailer->m_mtatype;
+               if (p == NULL)
+                       p = "dns";
+               (void) sprintf(buf, "Reporting-MTA: %s; %s", p, MyHostName);
+               putline(buf, mci);
+
+               /* Received-From-MTA: shows where we got this message from */
+               if (RealHostName != NULL)
+               {
+                       /* XXX use $s for type? */
+                       p = e->e_parent->e_from.q_mailer->m_mtatype;
+                       if (p == NULL)
+                               p = "dns";
+                       (void) sprintf(buf, "Received-From-MTA: %s; %s",
+                               p, RealHostName);
+                       putline(buf, mci);
+               }
+
+               /* Arrival-Date: -- when it arrived here */
+               (void) sprintf(buf, "Arrival-Date: %s",
+                       arpadate(ctime(&e->e_parent->e_ctime)));
+               putline(buf, mci);
+
+               /*
+               **  Output per-address information.
+               */
+
+               for (q = e->e_parent->e_sendqueue; q != NULL; q = q->q_next)
+               {
+                       register ADDRESS *r;
+
+                       if (!bitset(QBADADDR|QREPORT|QRELAYED, q->q_flags))
+                               continue;
+                       putline("", mci);
+
+                       /* Original-Recipient: -- passed from on high */
+                       if (q->q_orcpt != NULL)
+                       {
+                               (void) sprintf(buf, "Original-Recipient: %s",
+                                       xtextify(q->q_orcpt));
+                               putline(buf, mci);
+                       }
+
+                       /* Final-Recipient: -- the name from the RCPT command */
+                       p = e->e_parent->e_from.q_mailer->m_addrtype;
+                       if (p == NULL)
+                               p = "rfc822";
+                       for (r = q; r->q_alias != NULL; r = r->q_alias)
+                               continue;
+                       if (strchr(r->q_user, '@') == NULL)
+                               (void) sprintf(buf, "Final-Recipient: %s; %s@%s",
+                                       p, xtextify(r->q_user), MyHostName);
+                       else
+                               (void) sprintf(buf, "Final-Recipient: %s; %s",
+                                       p, xtextify(r->q_user));
+                       putline(buf, mci);
+
+                       /* Action: -- what happened? */
+                       if (bitset(QBADADDR, q->q_flags))
+                               putline("Action: failure", mci);
+                       else if (bitset(QQUEUEUP, q->q_flags))
+                               putline("Action: delayed", mci);
+                       else if (bitset(QRELAYED, q->q_flags))
+                               putline("Action: relayed", mci);
+                       else
+                               putline("Action: delivered", mci);
+
+                       /* Status: -- what _really_ happened? */
+                       strcpy(buf, "Status: ");
+                       if (q->q_status != NULL)
+                               strcat(buf, q->q_status);
+                       else if (bitset(QBADADDR, q->q_flags))
+                               strcat(buf, "5.0.0");
+                       else if (bitset(QQUEUEUP, q->q_flags))
+                               strcat(buf, "4.0.0");
+                       else if (bitset(QRELAYED, q->q_flags))
+                               strcat(buf, "6.0.1");
+                       else
+                               strcat(buf, "2.0.0");
+                       putline(buf, mci);
+
+                       /* Remote-MTA: -- who was I talking to? */
+                       p = q->q_mailer->m_mtatype;
+                       if (p == NULL)
+                               p = "dns";
+                       (void) sprintf(buf, "Remote-MTA: %s; ", p);
+                       if (q->q_statmta != NULL)
+                               p = q->q_statmta;
+                       else if (q->q_host != NULL)
+                               p = q->q_host;
+                       else
+                               p = NULL;
+                       if (p != NULL)
+                       {
+                               strcat(buf, p);
+                               p = &buf[strlen(buf) - 1];
+                               if (*p == '.')
+                                       *p = '\0';
+                               putline(buf, mci);
+                       }
+
+                       /* Diagnostic-Code: -- actual result from other end */
+                       if (q->q_rstatus != NULL)
+                       {
+                               p = q->q_mailer->m_diagtype;
+                               if (p == NULL)
+                                       p = "smtp";
+                               (void) sprintf(buf, "Diagnostic-Code: %s; %s",
+                                       p, q->q_rstatus);
+                               putline(buf, mci);
+                       }
+
+                       /* Last-Attempt-Date: -- fine granularity */
+                       if (q->q_statdate == (time_t) 0L)
+                               q->q_statdate = curtime();
+                       (void) sprintf(buf, "Last-Attempt-Date: %s",
+                               arpadate(ctime(&q->q_statdate)));
+                       putline(buf, mci);
+
+                       /* Expiry-Date: -- for delayed messages only */
+                       if (bitset(QQUEUEUP, q->q_flags) &&
+                           !bitset(QBADADDR, q->q_flags))
+                       {
+                               time_t xdate;
+
+                               xdate = e->e_ctime + TimeOuts.to_q_return[e->e_timeoutclass];
+                               sprintf(buf, "Expiry-Date: %s",
+                                       arpadate(ctime(&xdate)));
+                               putline(buf, mci);
+                       }
+               }
+       }
+#endif
+
        /*
        **  Output text of original message
        */
 
        /*
        **  Output text of original message
        */
 
-       if (NoReturn)
+       if (bitset(EF_NORETURN, e->e_parent->e_flags))
                SendBody = FALSE;
                SendBody = FALSE;
-       putline("", fp, m);
+       putline("", mci);
        if (e->e_parent->e_df != NULL)
        {
        if (e->e_parent->e_df != NULL)
        {
-               if (SendBody)
-                       putline("   ----- Unsent message follows -----\n", fp, m);
+               if (e->e_msgboundary == NULL)
+               {
+                       if (SendBody)
+                               putline("   ----- Original message follows -----\n", mci);
+                       else
+                               putline("   ----- Message header follows -----\n", mci);
+                       (void) fflush(mci->mci_out);
+               }
                else
                else
-                       putline("   ----- Message header follows -----\n", fp, m);
-               (void) fflush(fp);
-
-               if (e->e_msgboundary != NULL)
                {
                {
-                       putline("", fp, m);
                        (void) sprintf(buf, "--%s", e->e_msgboundary);
                        (void) sprintf(buf, "--%s", e->e_msgboundary);
-                       putline(buf, fp, m);
-                       putline("Content-Type: message/rfc822", fp, m);
-                       putline("", fp, m);
+                       putline(buf, mci);
+                       (void) sprintf(buf, "Content-Type: message/rfc822%s",
+                               mci, SendBody ? "" : "-headers");
+                       putline(buf, mci);
                }
                }
-               putheader(fp, m, e->e_parent);
-               putline("", fp, m);
+               putline("", mci);
+               putheader(mci, e->e_parent->e_header, e->e_parent);
                if (SendBody)
                if (SendBody)
-                       putbody(fp, m, e->e_parent, e->e_msgboundary);
-               else
-                       putline("   ----- Message body suppressed -----", fp, m);
+                       putbody(mci, e->e_parent, e->e_msgboundary);
+               else if (e->e_msgboundary == NULL)
+               {
+                       putline("", mci);
+                       putline("   ----- Message body suppressed -----", mci);
+               }
        }
        }
-       else
+       else if (e->e_msgboundary == NULL)
        {
        {
-               putline("  ----- No message was collected -----\n", fp, m);
+               putline("  ----- No message was collected -----\n", mci);
        }
 
        if (e->e_msgboundary != NULL)
        {
        }
 
        if (e->e_msgboundary != NULL)
        {
-               putline("", fp, m);
+               putline("", mci);
                (void) sprintf(buf, "--%s--", e->e_msgboundary);
                (void) sprintf(buf, "--%s--", e->e_msgboundary);
-               putline(buf, fp, m);
+               putline(buf, mci);
        }
        }
-       putline("", fp, m);
+       putline("", mci);
 
        /*
        **  Cleanup and exit
 
        /*
        **  Cleanup and exit
@@ -676,6 +944,130 @@ errbody(fp, m, e)
                syserr("errbody: I/O error");
 }
 \f/*
                syserr("errbody: I/O error");
 }
 \f/*
+**  SMTPTODSN -- convert SMTP to DSN status code
+**
+**     Parameters:
+**             smtpstat -- the smtp status code (e.g., 550).
+**
+**     Returns:
+**             The DSN version of the status code.
+*/
+
+char *
+smtptodsn(smtpstat)
+       int smtpstat;
+{
+       switch (smtpstat)
+       {
+         case 450:     /* Req mail action not taken: mailbox unavailable */
+               return "4.2.0";
+
+         case 451:     /* Req action aborted: local error in processing */
+               return "4.3.0";
+
+         case 452:     /* Req action not taken: insufficient sys storage */
+               return "4.3.1";
+
+         case 500:     /* Syntax error, command unrecognized */
+               return "5.5.2";
+
+         case 501:     /* Syntax error in parameters or arguments */
+               return "5.5.4";
+
+         case 502:     /* Command not implemented */
+               return "5.5.1";
+
+         case 503:     /* Bad sequence of commands */
+               return "5.5.1";
+
+         case 504:     /* Command parameter not implemented */
+               return "5.5.4";
+
+         case 550:     /* Req mail action not taken: mailbox unavailable */
+               return "5.2.0";
+
+         case 551:     /* User not local; please try <...> */
+               return "5.1.6";
+
+         case 552:     /* Req mail action aborted: exceeded storage alloc */
+               return "5.2.2";
+
+         case 553:     /* Req action not taken: mailbox name not allowed */
+               return "5.1.3";
+
+         case 554:     /* Transaction failed */
+               return "5.0.0";
+       }
+
+       if ((smtpstat / 100) == 2)
+               return "2.0.0";
+       if ((smtpstat / 100) == 4)
+               return "4.0.0";
+       return "5.0.0";
+}
+\f/*
+**  XTEXTIFY -- take regular text and turn it into DSN-style xtext
+**
+**     Parameters:
+**             t -- the text to convert.
+**
+**     Returns:
+**             The xtext-ified version of the same string.
+*/
+
+char *
+xtextify(t)
+       register char *t;
+{
+       register char *p;
+       int l;
+       int nbogus;
+       static char *bp = NULL;
+       static int bplen = 0;
+
+       /* figure out how long this xtext will have to be */
+       nbogus = l = 0;
+       for (p = t; *p != '\0'; p++)
+       {
+               register int c = (*p & 0xff);
+
+               /* ASCII dependence here -- this is the way the spec words it */
+               if (c < '!' || c > '~' || c == '+' || c == '\\' || c == '(')
+                       nbogus++;
+               l++;
+       }
+       if (nbogus == 0)
+               return t;
+       l += nbogus * 2 + 1;
+
+       /* now allocate space if necessary for the new string */
+       if (l > bplen)
+       {
+               if (bp != NULL)
+                       free(bp);
+               bp = xalloc(l);
+               bplen = l;
+       }
+
+       /* ok, copy the text with byte expansion */
+       for (p = bp; *t != '\0'; )
+       {
+               register int c = (*t++ & 0xff);
+
+               /* ASCII dependence here -- this is the way the spec words it */
+               if (c < '!' || c > '~' || c == '+' || c == '\\' || c == '(')
+               {
+                       *p++ = '+';
+                       *p++ = "0123456789abcdef"[c >> 4];
+                       *p++ = "0123456789abcdef"[c & 0xf];
+               }
+               else
+                       *p++ = c;
+       }
+       *p = '\0';
+       return bp;
+}
+\f/*
 **  PRUNEROUTE -- prune an RFC-822 source route
 ** 
 **     Trims down a source route to the last internet-registered hop.
 **  PRUNEROUTE -- prune an RFC-822 source route
 ** 
 **     Trims down a source route to the last internet-registered hop.
@@ -695,7 +1087,7 @@ errbody(fp, m, e)
 pruneroute(addr)
        char *addr;
 {
 pruneroute(addr)
        char *addr;
 {
-#ifdef NAMED_BIND
+#if NAMED_BIND
        char *start, *at, *comma;
        char c;
        int rcode;
        char *start, *at, *comma;
        char c;
        int rcode;