BSD 4_4_Lite1 release
[unix-history] / usr / src / usr.sbin / sendmail / src / savemail.c
index f998541..6467def 100644 (file)
@@ -1,17 +1,43 @@
 /*
  * Copyright (c) 1983 Eric P. Allman
 /*
  * Copyright (c) 1983 Eric P. Allman
- * Copyright (c) 1988 Regents of the University of California.
- * All rights reserved.
+ * Copyright (c) 1988, 1993
+ *     The Regents of the University of California.  All rights reserved.
  *
  *
- * %sccs.include.redist.c%
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *     This product includes software developed by the University of
+ *     California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
  */
 
 #ifndef lint
  */
 
 #ifndef lint
-static char sccsid[] = "@(#)savemail.c 6.20 (Berkeley) %G%";
+static char sccsid[] = "@(#)savemail.c 8.28 (Berkeley) 3/11/94";
 #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
@@ -43,6 +69,10 @@ static char sccsid[] = "@(#)savemail.c       6.20 (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)
        register ENVELOPE *e;
 
 savemail(e)
        register ENVELOPE *e;
@@ -50,31 +80,41 @@ savemail(e)
        register struct passwd *pw;
        register FILE *fp;
        int state;
        register struct passwd *pw;
        register FILE *fp;
        int state;
-       auto ADDRESS *q;
+       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\n", ErrorMode);
+       {
+               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);
+       }
 
 
-       if (bitset(EF_RESPONSE, e->e_flags))
+       if (e->e_id == NULL)
+       {
+               /* can't return a message with no id */
                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.
        */
 
-       if (CurEnv->e_returnto == NULL)
+       if (e->e_from.q_paddr == NULL)
        {
        {
-               CurEnv->e_returnto = parse("root", (ADDRESS *) NULL, 0);
-               if (CurEnv->e_returnto == NULL)
+               e->e_sender = "Postmaster";
+               if (parseaddr(e->e_sender, &e->e_from,
+                             RF_COPYPARSE|RF_SENDERADDR, '\0', NULL, e) == NULL)
                {
                {
-                       syserr("553 Cannot parse root!");
+                       syserr("553 Cannot parse Postmaster!");
                        ExitStat = EX_SOFTWARE;
                        finis();
                }
                        ExitStat = EX_SOFTWARE;
                        finis();
                }
@@ -96,7 +136,7 @@ savemail(e)
        */
 
        /* determine starting state */
        */
 
        /* determine starting state */
-       switch (ErrorMode)
+       switch (e->e_errormode)
        {
          case EM_WRITE:
                state = ESM_REPORT;
        {
          case EM_WRITE:
                state = ESM_REPORT;
@@ -122,11 +162,23 @@ savemail(e)
                return;
 
          default:
                return;
 
          default:
-               syserr("554 savemail: ErrorMode x%x\n");
+               syserr("554 savemail: bogus errormode x%x\n", e->e_errormode);
                state = ESM_MAIL;
                break;
        }
 
                state = ESM_MAIL;
                break;
        }
 
+       /* if this is already an error response, send to postmaster */
+       if (bitset(EF_RESPONSE, e->e_flags))
+       {
+               if (e->e_parent != NULL &&
+                   bitset(EF_RESPONSE, e->e_parent->e_flags))
+               {
+                       /* got an error sending a response -- can it */
+                       return;
+               }
+               state = ESM_POSTMASTER;
+       }
+
        while (state != ESM_DONE)
        {
                if (tTd(6, 5))
        while (state != ESM_DONE)
        {
                if (tTd(6, 5))
@@ -176,14 +228,13 @@ savemail(e)
                                while (fgets(buf, sizeof buf, fp) != NULL &&
                                       !ferror(stdout))
                                        fputs(buf, stdout);
                                while (fgets(buf, sizeof buf, fp) != NULL &&
                                       !ferror(stdout))
                                        fputs(buf, stdout);
-                               (void) fclose(fp);
+                               (void) xfclose(fp, "savemail transcript", e->e_id);
                        }
                        printf("Original message will be saved in dead.letter.\r\n");
                        state = ESM_DEADLETTER;
                        break;
 
                  case ESM_MAIL:
                        }
                        printf("Original message will be saved in dead.letter.\r\n");
                        state = ESM_DEADLETTER;
                        break;
 
                  case ESM_MAIL:
-                 case ESM_POSTMASTER:
                        /*
                        **  If mailing back, do it.
                        **      Throw away all further output.  Don't alias,
                        /*
                        **  If mailing back, do it.
                        **      Throw away all further output.  Don't alias,
@@ -194,61 +245,78 @@ savemail(e)
                        **      the mail to be delivered even if a version of
                        **      it has already been sent to the sender.
                        **
                        **      the mail to be delivered even if a version of
                        **      it has already been sent to the sender.
                        **
-                       **      Clever technique for computing rpath from
-                       **      Eric Wassenaar <e07@nikhef.nl>.
+                       **  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 (state == ESM_MAIL)
+                       if (ExitStat == EX_CONFIG || ExitStat == EX_SOFTWARE)
                        {
                        {
-                               char *rpath;
+                               (void) sendtolist("postmaster",
+                                         NULLADDR, &e->e_errorqueue, e);
+                       }
+                       if (strcmp(e->e_from.q_paddr, "<>") != 0)
+                       {
+                               (void) sendtolist(e->e_from.q_paddr,
+                                         NULLADDR, &e->e_errorqueue, e);
+                       }
 
 
-                               if (e->e_returnpath != e->e_sender)
-                                       rpath = e->e_returnpath;
-                               else
-                                       rpath = e->e_from.q_paddr;
-                               if (strcmp(rpath, "<>") != 0)
-                                       (void) sendtolist(rpath,
-                                                 (ADDRESS *) NULL,
-                                                 &e->e_errorqueue, 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;
-                               if (q == NULL)
-                               {
-                                       /* this is an error-error */
-                                       state = ESM_USRTMP;
+                       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;
                                        break;
-                               }
+                       if (q == NULL)
+                       {
+                               /* this is an error-error */
+                               state = ESM_POSTMASTER;
+                               break;
                        }
                        }
-                       else
+                       if (returntosender(e->e_message, e->e_errorqueue,
+                                          (e->e_class >= 0), e) == 0)
                        {
                        {
-                               if (parseaddr("postmaster", q, 0, '\0', NULL, e) == NULL)
-                               {
-                                       syserr("553 cannot parse postmaster!");
-                                       ExitStat = EX_SOFTWARE;
-                                       state = ESM_USRTMP;
-                                       break;
-                               }
+                               state = ESM_DONE;
+                               break;
+                       }
+
+                       /* didn't work -- return to postmaster */
+                       state = ESM_POSTMASTER;
+                       break;
+
+                 case ESM_POSTMASTER:
+                       /*
+                       **  Similar to previous case, but to system postmaster.
+                       */
+
+                       q = NULL;
+                       if (sendtolist("postmaster", NULL, &q, e) <= 0)
+                       {
+                               syserr("553 cannot parse postmaster!");
+                               ExitStat = EX_SOFTWARE;
+                               state = ESM_USRTMP;
+                               break;
                        }
                        }
-                       if (returntosender(e->e_message != NULL ? e->e_message :
-                                          "Unable to deliver mail",
+                       if (returntosender(e->e_message,
                                           q, (e->e_class >= 0), e) == 0)
                        {
                                state = ESM_DONE;
                                break;
                        }
 
                                           q, (e->e_class >= 0), e) == 0)
                        {
                                state = ESM_DONE;
                                break;
                        }
 
-                       state = state == ESM_MAIL ? ESM_POSTMASTER : ESM_USRTMP;
+                       /* didn't work -- last resort */
+                       state = ESM_USRTMP;
                        break;
 
                  case ESM_DEADLETTER:
                        break;
 
                  case ESM_DEADLETTER:
@@ -271,13 +339,12 @@ savemail(e)
                        }
                        if (p == NULL)
                        {
                        }
                        if (p == NULL)
                        {
-                               syserr("554 Can't return mail to %s", e->e_from.q_paddr);
+                               /* no local directory */
                                state = ESM_MAIL;
                                break;
                        }
                        if (e->e_dfp != NULL)
                        {
                                state = ESM_MAIL;
                                break;
                        }
                        if (e->e_dfp != NULL)
                        {
-                               auto ADDRESS *q;
                                bool oldverb = Verbose;
 
                                /* we have a home directory; open dead.letter */
                                bool oldverb = Verbose;
 
                                /* we have a home directory; open dead.letter */
@@ -289,7 +356,9 @@ savemail(e)
                                e->e_to = buf;
                                q = NULL;
                                (void) sendtolist(buf, &e->e_from, &q, e);
                                e->e_to = buf;
                                q = NULL;
                                (void) sendtolist(buf, &e->e_from, &q, e);
-                               if (deliver(e, q) == 0)
+                               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;
@@ -312,21 +381,34 @@ savemail(e)
                                break;
                        }
 
                                break;
                        }
 
-                       fp = dfopen("/usr/tmp/dead.letter", "a");
+                       strcpy(buf, _PATH_VARTMP);
+                       strcat(buf, "dead.letter");
+                       if (!writable(buf, NULLADDR, SFF_NOSLINK))
+                       {
+                               state = ESM_PANIC;
+                               break;
+                       }
+                       fp = dfopen(buf, O_WRONLY|O_CREAT|O_APPEND, FileMode);
                        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);
-                       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);
+                       putline("\n", &mcibuf);
+                       (*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) fclose(fp);
+                       (void) xfclose(fp, "savemail", "/usr/tmp/dead.letter");
                        break;
 
                  default:
                        break;
 
                  default:
@@ -336,8 +418,7 @@ 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);
+                       syserr("!554 savemail: cannot save rejected email anywhere");
                }
        }
 }
                }
        }
 }
@@ -374,16 +455,21 @@ returntosender(msg, returnq, sendbody, e)
        char buf[MAXNAME];
        extern putheader(), errbody();
        register ENVELOPE *ee;
        char buf[MAXNAME];
        extern putheader(), errbody();
        register ENVELOPE *ee;
-       extern ENVELOPE *newenvelope();
+       ENVELOPE *oldcur = CurEnv;
        ENVELOPE errenvelope;
        static int returndepth;
        register ADDRESS *q;
 
        ENVELOPE errenvelope;
        static int returndepth;
        register ADDRESS *q;
 
+       if (returnq == NULL)
+               return (-1);
+
+       if (msg == NULL)
+               msg = "Unable to deliver mail";
+
        if (tTd(6, 1))
        {
        if (tTd(6, 1))
        {
-               printf("Return To Sender: msg=\"%s\", depth=%d, e=%x,\n",
+               printf("Return To Sender: msg=\"%s\", depth=%d, e=%x, returnq=",
                       msg, returndepth, e);
                       msg, returndepth, e);
-               printf("\treturnq=");
                printaddr(returnq, TRUE);
        }
 
                printaddr(returnq, TRUE);
        }
 
@@ -397,30 +483,36 @@ returntosender(msg, returnq, sendbody, e)
        }
 
        SendBody = sendbody;
        }
 
        SendBody = sendbody;
-       define('g', e->e_sender, e);
-       define('<', e->e_returnpath, e);
+       define('g', e->e_from.q_paddr, e);
+       define('u', NULL, e);
        ee = newenvelope(&errenvelope, e);
        define('a', "\201b", ee);
        ee = newenvelope(&errenvelope, e);
        define('a', "\201b", ee);
+       define('r', "internal", ee);
+       define('s', "localhost", ee);
+       define('_', "localhost", ee);
        ee->e_puthdr = putheader;
        ee->e_putbody = errbody;
        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 (!NoReturn)
+               ee->e_msgsize += e->e_msgsize;
+       initsys(ee);
        for (q = returnq; q != NULL; q = q->q_next)
        {
        for (q = returnq; q != NULL; q = q->q_next)
        {
-               if (bitset(QDONTSEND, q->q_flags))
+               if (bitset(QBADADDR, q->q_flags))
                        continue;
 
                        continue;
 
-               ee->e_nrcpts++;
+               if (!bitset(QDONTSEND, q->q_flags))
+                       ee->e_nrcpts++;
 
                if (!DontPruneRoutes && pruneroute(q->q_paddr))
 
                if (!DontPruneRoutes && pruneroute(q->q_paddr))
-                       parseaddr(q->q_paddr, q, 0, '\0', NULL, e);
+                       parseaddr(q->q_paddr, q, RF_COPYPARSE, '\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);
        }
 
 # ifdef LOG
        }
 
 # ifdef LOG
@@ -430,36 +522,44 @@ returntosender(msg, returnq, sendbody, e)
 # endif
 
        (void) sprintf(buf, "Returned mail: %s", msg);
 # endif
 
        (void) sprintf(buf, "Returned mail: %s", msg);
-       addheader("subject", buf, ee);
+       addheader("Subject", buf, ee);
+       if (SendMIMEErrors)
+       {
+               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);
+       }
 
        /* fake up an address header for the from person */
        expand("\201n", buf, &buf[sizeof buf - 1], e);
 
        /* fake up an address header for the from person */
        expand("\201n", buf, &buf[sizeof buf - 1], e);
-       ee->e_sender = newstr(buf);
-       if (ConfigLevel >= 4)
-               ee->e_returnpath = "<>";
-       else
-               ee->e_returnpath = ee->e_sender;
-       if (parseaddr(buf, &ee->e_from, -1, '\0', NULL, e) == NULL)
+       if (parseaddr(buf, &ee->e_from, RF_COPYALL|RF_SENDERADDR, '\0', NULL, e) == NULL)
        {
                syserr("553 Can't parse myself!");
                ExitStat = EX_SOFTWARE;
                returndepth--;
                return (-1);
        }
        {
                syserr("553 Can't parse myself!");
                ExitStat = EX_SOFTWARE;
                returndepth--;
                return (-1);
        }
-       loweraddr(&ee->e_from);
+       ee->e_sender = ee->e_from.q_paddr;
 
        /* push state into submessage */
        CurEnv = ee;
        define('f', "\201n", ee);
        define('x', "Mail Delivery Subsystem", ee);
 
        /* push state into submessage */
        CurEnv = ee;
        define('f', "\201n", ee);
        define('x', "Mail Delivery Subsystem", ee);
-       eatheader(ee, FALSE);
+       eatheader(ee, TRUE);
+
+       /* mark statistics */
+       markstats(ee, NULLADDR);
 
        /* actually deliver the error message */
        sendall(ee, SM_DEFAULT);
 
        /* restore state */
        dropenvelope(ee);
 
        /* actually deliver the error message */
        sendall(ee, SM_DEFAULT);
 
        /* restore state */
        dropenvelope(ee);
-       CurEnv = CurEnv->e_parent;
+       CurEnv = oldcur;
        returndepth--;
 
        /* should check for delivery errors here */
        returndepth--;
 
        /* should check for delivery errors here */
@@ -472,8 +572,7 @@ 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.
 **
 **     Returns:
 **             e -- the envelope we are working in.
 **
 **     Returns:
@@ -483,14 +582,62 @@ 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)
+       register MCI *mci;
        register ENVELOPE *e;
 {
        register FILE *xfile;
        register ENVELOPE *e;
 {
        register FILE *xfile;
-       char buf[MAXLINE];
        char *p;
        char *p;
+       register ADDRESS *q;
+       bool printheader;
+       char buf[MAXLINE];
+
+       if (e->e_parent == NULL)
+       {
+               syserr("errbody: null parent");
+               putline("   ----- Original message lost -----\n", mci);
+               return;
+       }
+
+       /*
+       **  Output MIME header.
+       */
+
+       if (e->e_msgboundary != NULL)
+       {
+               putline("This is a MIME-encapsulated message", mci);
+               putline("", mci);
+               (void) sprintf(buf, "--%s", e->e_msgboundary);
+               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, &buf[sizeof buf - 1], e->e_parent);
+       putline(buf, mci);
+       putline("", mci);
 
        /*
        **  Output error message header (if specified and available).
 
        /*
        **  Output error message header (if specified and available).
@@ -506,20 +653,53 @@ errbody(fp, m, e)
                                while (fgets(buf, sizeof buf, xfile) != NULL)
                                {
                                        expand(buf, buf, &buf[sizeof buf - 1], e);
                                while (fgets(buf, sizeof buf, xfile) != NULL)
                                {
                                        expand(buf, buf, &buf[sizeof buf - 1], e);
-                                       putline(buf, fp, m);
+                                       putline(buf, mci);
                                }
                                (void) fclose(xfile);
                                }
                                (void) fclose(xfile);
-                               fprintf(fp, "\n");
+                               putline("\n", mci);
                        }
                }
                else
                {
                        expand(ErrMsgFile, buf, &buf[sizeof buf - 1], e);
                        }
                }
                else
                {
                        expand(ErrMsgFile, buf, &buf[sizeof buf - 1], e);
-                       putline(buf, fp, m);
-                       fprintf(fp, "\n");
+                       putline(buf, mci);
+                       putline("", mci);
                }
        }
 
                }
        }
 
+       /*
+       **  Output message introduction
+       */
+
+       printheader = TRUE;
+       for (q = e->e_parent->e_sendqueue; q != NULL; q = q->q_next)
+       {
+               if (bitset(QBADADDR|QREPORT, q->q_flags))
+               {
+                       if (printheader)
+                       {
+                               putline("   ----- The following addresses had delivery problems -----",
+                                       mci);
+                               printheader = FALSE;
+                       }
+                       strcpy(buf, q->q_paddr);
+                       if (bitset(QBADADDR, q->q_flags))
+                               strcat(buf, "  (unrecoverable error)");
+                       else
+                               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)
+               putline("\n", mci);
+
        /*
        **  Output transcript of errors
        */
        /*
        **  Output transcript of errors
        */
@@ -529,16 +709,16 @@ 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);
-               fprintf(fp, "  ----- Transcript of session is unavailable -----\n");
+               putline("   ----- Transcript of session is unavailable -----\n", mci);
        }
        else
        {
        }
        else
        {
-               fprintf(fp, "   ----- Transcript of session follows -----\n");
+               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);
-               (void) fclose(xfile);
+                       putline(buf, mci);
+               (void) xfclose(xfile, "errbody xscript", p);
        }
        errno = 0;
 
        }
        errno = 0;
 
@@ -548,31 +728,42 @@ errbody(fp, m, e)
 
        if (NoReturn)
                SendBody = FALSE;
 
        if (NoReturn)
                SendBody = FALSE;
-       if (e->e_parent->e_dfp != NULL)
+       putline("", mci);
+       if (e->e_parent->e_df != NULL)
        {
                if (SendBody)
        {
                if (SendBody)
-               {
-                       putline("\n", fp, m);
-                       putline("   ----- Unsent message follows -----\n", fp, m);
-                       (void) fflush(fp);
-                       putheader(fp, m, e->e_parent);
-                       putline("\n", fp, m);
-                       putbody(fp, m, e->e_parent);
-               }
+                       putline("   ----- Original message follows -----\n", mci);
                else
                else
+                       putline("   ----- Message header follows -----\n", mci);
+               (void) fflush(mci->mci_out);
+
+               if (e->e_msgboundary != NULL)
                {
                {
-                       putline("\n", fp, m);
-                       putline("  ----- Message header follows -----\n", fp, m);
-                       (void) fflush(fp);
-                       putheader(fp, m, e->e_parent);
+                       putline("", mci);
+                       (void) sprintf(buf, "--%s", e->e_msgboundary);
+                       putline(buf, mci);
+                       putline("Content-Type: message/rfc822", mci);
+                       putline("", mci);
                }
                }
+               putheader(mci, e->e_parent);
+               putline("", mci);
+               if (SendBody)
+                       putbody(mci, e->e_parent, e->e_msgboundary);
+               else
+                       putline("   ----- Message body suppressed -----", mci);
        }
        else
        {
        }
        else
        {
-               putline("\n", fp, m);
-               putline("  ----- No message was collected -----\n", fp, m);
-               putline("\n", fp, m);
+               putline("  ----- No message was collected -----\n", mci);
+       }
+
+       if (e->e_msgboundary != NULL)
+       {
+               putline("", mci);
+               (void) sprintf(buf, "--%s--", e->e_msgboundary);
+               putline(buf, mci);
        }
        }
+       putline("", mci);
 
        /*
        **  Cleanup and exit
 
        /*
        **  Cleanup and exit
@@ -601,7 +792,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;
@@ -622,7 +813,7 @@ pruneroute(addr)
 
        while (start)
        {
 
        while (start)
        {
-               if (getmxrr(hostbuf, mxhosts, "", &rcode) > 0)
+               if (getmxrr(hostbuf, mxhosts, FALSE, &rcode) > 0)
                {
                        strcpy(addr + 1, start + 1);
                        return TRUE;
                {
                        strcpy(addr + 1, start + 1);
                        return TRUE;