BSD 4_4 release
[unix-history] / usr / src / usr.sbin / sendmail / src / savemail.c
index fd3c603..5a35734 100644 (file)
@@ -1,18 +1,54 @@
+/*
+ * Copyright (c) 1983 Eric P. Allman
+ * Copyright (c) 1988, 1993
+ *     The Regents of the University of California.  All rights reserved.
+ *
+ * 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
+static char sccsid[] = "@(#)savemail.c 8.1 (Berkeley) 6/7/93";
+#endif /* not lint */
+
 # include <pwd.h>
 # include "sendmail.h"
 
 # include <pwd.h>
 # include "sendmail.h"
 
-SCCSID(@(#)savemail.c  3.39            %G%);
-
 /*
 **  SAVEMAIL -- Save mail on error
 **
 /*
 **  SAVEMAIL -- Save mail on error
 **
-**     If the MailBack flag is set, mail it back to the originator
+**     If mailing back errors, mail it back to the originator
 **     together with an error message; otherwise, just put it in
 **     dead.letter in the user's home directory (if he exists on
 **     this machine).
 **
 **     Parameters:
 **     together with an error message; otherwise, just put it in
 **     dead.letter in the user's home directory (if he exists on
 **     this machine).
 **
 **     Parameters:
-**             none
+**             e -- the envelope containing the message in error.
 **
 **     Returns:
 **             none
 **
 **     Returns:
 **             none
@@ -23,175 +59,344 @@ SCCSID(@(#)savemail.c     3.39            %G%);
 **             directory.
 */
 
 **             directory.
 */
 
-savemail()
+/* defines for state machine */
+# define ESM_REPORT    0       /* report to sender's terminal */
+# define ESM_MAIL      1       /* mail back to sender */
+# define ESM_QUIET     2       /* messages have already been returned */
+# define ESM_DEADLETTER        3       /* save in ~/dead.letter */
+# define ESM_POSTMASTER        4       /* return to postmaster */
+# define ESM_USRTMP    5       /* save in /usr/tmp/dead.letter */
+# define ESM_PANIC     6       /* leave the locked queue/transcript files */
+# define ESM_DONE      7       /* the message is successfully delivered */
+
+
+savemail(e)
+       register ENVELOPE *e;
 {
        register struct passwd *pw;
 {
        register struct passwd *pw;
-       register FILE *xfile;
+       register FILE *fp;
+       int state;
+       auto ADDRESS *q = NULL;
        char buf[MAXLINE+1];
        extern struct passwd *getpwnam();
        register char *p;
        extern char *ttypath();
        char buf[MAXLINE+1];
        extern struct passwd *getpwnam();
        register char *p;
        extern char *ttypath();
-       static int exclusive;
        typedef int (*fnptr)();
        typedef int (*fnptr)();
-       extern ENVELOPE *newenvelope();
 
 
-# ifdef DEBUG
-       if (Debug)
-               printf("\nsavemail: exclusive %d\n", exclusive);
-# endif DEBUG
+       if (tTd(6, 1))
+       {
+               printf("\nsavemail, errormode = %c, id = %s\n  e_from=",
+                       e->e_errormode, e->e_id == NULL ? "NONE" : e->e_id);
+               printaddr(&e->e_from, FALSE);
+       }
 
 
-       if (exclusive++)
-               return;
-       if (CurEnv->e_class <= PRI_JUNK)
+       if (e->e_id == NULL)
        {
        {
-               message(Arpa_Info, "Dumping junk mail");
+               /* can't return a message with no id */
                return;
        }
                return;
        }
-       ForceMail = TRUE;
-       FatalErrors = FALSE;
+
+       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, 0, '\0', NULL, e) == NULL)
                {
                {
-                       syserr("Cannot parse root!");
+                       syserr("553 Cannot parse Postmaster!");
                        ExitStat = EX_SOFTWARE;
                        finis();
                }
        }
                        ExitStat = EX_SOFTWARE;
                        finis();
                }
        }
-       CurEnv->e_to = NULL;
+       e->e_to = NULL;
 
        /*
 
        /*
-       **  If called from Eric Schmidt's network, do special mailback.
-       **      Fundamentally, this is the mailback case except that
-       **      it returns an OK exit status (assuming the return
-       **      worked).
-       **  Also, if the from address is not local, mail it back.
+       **  Basic state machine.
+       **
+       **      This machine runs through the following states:
+       **
+       **      ESM_QUIET       Errors have already been printed iff the
+       **                      sender is local.
+       **      ESM_REPORT      Report directly to the sender's terminal.
+       **      ESM_MAIL        Mail response to the sender.
+       **      ESM_DEADLETTER  Save response in ~/dead.letter.
+       **      ESM_POSTMASTER  Mail response to the postmaster.
+       **      ESM_PANIC       Save response anywhere possible.
        */
 
        */
 
-       if (BerkNet)
+       /* determine starting state */
+       switch (e->e_errormode)
        {
        {
+         case EM_WRITE:
+               state = ESM_REPORT;
+               break;
+
+         case EM_BERKNET:
+               /* mail back, but return o.k. exit status */
                ExitStat = EX_OK;
                ExitStat = EX_OK;
-               MailBack = TRUE;
-       }
-       if (!bitset(M_LOCAL, CurEnv->e_returnto->q_mailer->m_flags))
-               MailBack = TRUE;
 
 
-       /*
-       **  If writing back, do it.
-       **      If the user is still logged in on the same terminal,
-       **      then write the error messages back to hir (sic).
-       **      If not, set the MailBack flag so that it will get
-       **      mailed back instead.
-       */
+               /* fall through.... */
 
 
-       if (WriteBack)
+         case EM_MAIL:
+               state = ESM_MAIL;
+               break;
+
+         case EM_PRINT:
+         case '\0':
+               state = ESM_QUIET;
+               break;
+
+         case EM_QUIET:
+               /* no need to return anything at all */
+               return;
+
+         default:
+               syserr("554 savemail: bogus errormode x%x\n", e->e_errormode);
+               state = ESM_MAIL;
+               break;
+       }
+
+       /* if this is already an error response, send to postmaster */
+       if (bitset(EF_RESPONSE, e->e_flags))
        {
        {
-               p = ttypath();
-               if (p == NULL || freopen(p, "w", stdout) == NULL)
+               if (e->e_parent != NULL &&
+                   bitset(EF_RESPONSE, e->e_parent->e_flags))
                {
                {
-                       MailBack = TRUE;
-                       errno = 0;
-               }
-               else
-               {
-                       (void) fflush(Xscript);
-                       xfile = fopen(Transcript, "r");
-                       if (xfile == NULL)
-                               syserr("Cannot open %s", Transcript);
-                       expand("$n", buf, &buf[sizeof buf - 1], CurEnv);
-                       printf("\r\nMessage from %s...\r\n", buf);
-                       printf("Errors occurred while sending mail; transcript follows:\r\n");
-                       while (fgets(buf, sizeof buf, xfile) != NULL && !ferror(stdout))
-                               fputs(buf, stdout);
-                       if (ferror(stdout))
-                               (void) syserr("savemail: stdout: write err");
-                       (void) fclose(xfile);
+                       /* got an error sending a response -- can it */
+                       return;
                }
                }
+               state = ESM_POSTMASTER;
        }
 
        }
 
-       /*
-       **  If mailing back, do it.
-       **      Throw away all further output.  Don't do aliases, since
-       **      this could cause loops, e.g., if joe mails to x:joe,
-       **      and for some reason the network for x: is down, then
-       **      the response gets sent to x:joe, 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 (MailBack)
+       while (state != ESM_DONE)
        {
        {
-               if (CurEnv->e_errorqueue == NULL)
-                       sendto(CurEnv->e_from.q_paddr, 1, (ADDRESS *) NULL, &CurEnv->e_errorqueue);
-               if (returntosender("Unable to deliver mail", CurEnv->e_returnto, TRUE) == 0)
-                       return;
-       }
+               if (tTd(6, 5))
+                       printf("  state %d\n", state);
 
 
-       /*
-       **  Save the message in dead.letter.
-       **      If we weren't mailing back, and the user is local, we
-       **      should save the message in dead.letter so that the
-       **      poor person doesn't have to type it over again --
-       **      and we all know what poor typists programmers are.
-       **      However, if we are running a "smart" protocol, we don't
-       **      bother to return the message, since the other end is
-       **      expected to handle that.
-       */
+               switch (state)
+               {
+                 case ESM_QUIET:
+                       if (e->e_from.q_mailer == LocalMailer)
+                               state = ESM_DEADLETTER;
+                       else
+                               state = ESM_MAIL;
+                       break;
+
+                 case ESM_REPORT:
+
+                       /*
+                       **  If the user is still logged in on the same terminal,
+                       **  then write the error messages back to hir (sic).
+                       */
+
+                       p = ttypath();
+                       if (p == NULL || freopen(p, "w", stdout) == NULL)
+                       {
+                               state = ESM_MAIL;
+                               break;
+                       }
+
+                       expand("\201n", buf, &buf[sizeof buf - 1], e);
+                       printf("\r\nMessage from %s...\r\n", buf);
+                       printf("Errors occurred while sending mail.\r\n");
+                       if (e->e_xfp != NULL)
+                       {
+                               (void) fflush(e->e_xfp);
+                               fp = fopen(queuename(e, 'x'), "r");
+                       }
+                       else
+                               fp = NULL;
+                       if (fp == NULL)
+                       {
+                               syserr("Cannot open %s", queuename(e, 'x'));
+                               printf("Transcript of session is unavailable.\r\n");
+                       }
+                       else
+                       {
+                               printf("Transcript follows:\r\n");
+                               while (fgets(buf, sizeof buf, fp) != NULL &&
+                                      !ferror(stdout))
+                                       fputs(buf, stdout);
+                               (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:
+                       /*
+                       **  If mailing back, do it.
+                       **      Throw away all further output.  Don't alias,
+                       **      since this could cause loops, e.g., if joe
+                       **      mails to joe@x, and for some reason the network
+                       **      for @x is down, then the response gets sent to
+                       **      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 (strcmp(e->e_from.q_paddr, "<>") != 0)
+                               (void) sendtolist(e->e_from.q_paddr,
+                                         (ADDRESS *) NULL,
+                                         &e->e_errorqueue, e);
+
+                       /* deliver a cc: to the postmaster if desired */
+                       if (PostMasterCopy != NULL)
+                       {
+                               auto ADDRESS *rlist = NULL;
+
+                               (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_POSTMASTER;
+                               break;
+                       }
+                       if (returntosender(e->e_message,
+                                          q, (e->e_class >= 0), e) == 0)
+                       {
+                               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,
+                                          q, (e->e_class >= 0), e) == 0)
+                       {
+                               state = ESM_DONE;
+                               break;
+                       }
+
+                       /* didn't work -- last resort */
+                       state = ESM_USRTMP;
+                       break;
+
+                 case ESM_DEADLETTER:
+                       /*
+                       **  Save the message in dead.letter.
+                       **      If we weren't mailing back, and the user is
+                       **      local, we should save the message in
+                       **      ~/dead.letter so that the poor person doesn't
+                       **      have to type it over again -- and we all know
+                       **      what poor typists UNIX users are.
+                       */
+
+                       p = NULL;
+                       if (e->e_from.q_mailer == LocalMailer)
+                       {
+                               if (e->e_from.q_home != NULL)
+                                       p = e->e_from.q_home;
+                               else if ((pw = getpwnam(e->e_from.q_user)) != NULL)
+                                       p = pw->pw_dir;
+                       }
+                       if (p == NULL)
+                       {
+                               /* no local directory */
+                               state = ESM_MAIL;
+                               break;
+                       }
+                       if (e->e_dfp != NULL)
+                       {
+                               bool oldverb = Verbose;
+
+                               /* we have a home directory; open dead.letter */
+                               define('z', p, e);
+                               expand("\201z/dead.letter", buf, &buf[sizeof buf - 1], e);
+                               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)
+                                       state = ESM_DONE;
+                               else
+                                       state = ESM_MAIL;
+                       }
+                       else
+                       {
+                               /* no data file -- try mailing back */
+                               state = ESM_MAIL;
+                       }
+                       break;
+
+                 case ESM_USRTMP:
+                       /*
+                       **  Log the mail in /usr/tmp/dead.letter.
+                       */
+
+                       if (e->e_class < 0)
+                       {
+                               state = ESM_DONE;
+                               break;
+                       }
+
+                       fp = dfopen("/usr/tmp/dead.letter",
+                                   O_WRONLY|O_CREAT|O_APPEND, FileMode);
+                       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);
+                       (void) fflush(fp);
+                       state = ferror(fp) ? ESM_PANIC : ESM_DONE;
+                       (void) xfclose(fp, "savemail", "/usr/tmp/dead.letter");
+                       break;
 
 
-       if (ArpaMode)
-               return;
-       p = NULL;
-       if (CurEnv->e_returnto->q_mailer == LocalMailer)
-       {
-               if (CurEnv->e_returnto->q_home != NULL)
-                       p = CurEnv->e_returnto->q_home;
-               else if ((pw = getpwnam(CurEnv->e_returnto->q_user)) != NULL)
-                       p = pw->pw_dir;
-       }
-       if (p == NULL)
-       {
-               syserr("Can't return mail to %s", CurEnv->e_returnto->q_paddr);
-# ifdef DEBUG
-               p = "/usr/tmp";
-# endif
-       }
-       if (p != NULL && TempFile != NULL)
-       {
-               auto ADDRESS *q;
-               bool oldverb = Verbose;
-
-               /* we have a home directory; open dead.letter */
-               Verbose = TRUE;
-               message(Arpa_Info, "Saving message in dead.letter");
-               Verbose = oldverb;
-               define('z', p);
-               expand("$z/dead.letter", buf, &buf[sizeof buf - 1], CurEnv);
-               CurEnv->e_to = buf;
-               q = NULL;
-               sendto(buf, -1, (ADDRESS *) NULL, &q);
-               (void) deliver(q);
-       }
+                 default:
+                       syserr("554 savemail: unknown state %d", state);
 
 
-       /* add terminator to writeback message */
-       if (WriteBack)
-               printf("-----\r\n");
+                       /* fall through ... */
+
+                 case ESM_PANIC:
+                       /* leave the locked queue & transcript files around */
+                       syserr("554 savemail: cannot save rejected email anywhere");
+                       exit(EX_SOFTWARE);
+               }
+       }
 }
 \f/*
 **  RETURNTOSENDER -- return a message to the sender with an error.
 **
 **     Parameters:
 **             msg -- the explanatory message.
 }
 \f/*
 **  RETURNTOSENDER -- return a message to the sender with an error.
 **
 **     Parameters:
 **             msg -- the explanatory message.
-**             returnto -- the queue of people to send the message to.
+**             returnq -- the queue of people to send the message to.
 **             sendbody -- if TRUE, also send back the body of the
 **                     message; otherwise just send the header.
 **             sendbody -- if TRUE, also send back the body of the
 **                     message; otherwise just send the header.
+**             e -- the current envelope.
 **
 **     Returns:
 **             zero -- if everything went ok.
 **
 **     Returns:
 **             zero -- if everything went ok.
@@ -205,72 +410,116 @@ savemail()
 static bool    SendBody;
 
 #define MAXRETURNS     6       /* max depth of returning messages */
 static bool    SendBody;
 
 #define MAXRETURNS     6       /* max depth of returning messages */
+#define ERRORFUDGE     100     /* nominal size of error message text */
 
 
-returntosender(msg, returnto, sendbody)
+returntosender(msg, returnq, sendbody, e)
        char *msg;
        char *msg;
-       ADDRESS *returnto;
+       ADDRESS *returnq;
        bool sendbody;
        bool sendbody;
+       register ENVELOPE *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;
        ENVELOPE errenvelope;
        static int returndepth;
+       register ADDRESS *q;
+
+       if (returnq == NULL)
+               return (-1);
+
+       if (msg == NULL)
+               msg = "Unable to deliver mail";
 
 
-# ifdef DEBUG
-       if (Debug > 0)
+       if (tTd(6, 1))
        {
        {
-               printf("Return To Sender: msg=\"%s\", depth=%d, CurEnv=%x,\n",
-                      msg, returndepth, CurEnv);
-               printf("\treturnto=");
-               printaddr(returnto, FALSE);
+               printf("Return To Sender: msg=\"%s\", depth=%d, e=%x, returnq=",
+                      msg, returndepth, e);
+               printaddr(returnq, TRUE);
        }
        }
-# endif DEBUG
 
        if (++returndepth >= MAXRETURNS)
        {
                if (returndepth != MAXRETURNS)
 
        if (++returndepth >= MAXRETURNS)
        {
                if (returndepth != MAXRETURNS)
-                       syserr("returntosender: infinite recursion on %s", returnto->q_paddr);
+                       syserr("554 returntosender: infinite recursion on %s", returnq->q_paddr);
                /* don't "unrecurse" and fake a clean exit */
                /* returndepth--; */
                return (0);
        }
 
                /* don't "unrecurse" and fake a clean exit */
                /* returndepth--; */
                return (0);
        }
 
-       NoAlias = TRUE;
        SendBody = sendbody;
        SendBody = sendbody;
-       ee = newenvelope(&errenvelope);
+       define('g', e->e_from.q_paddr, e);
+       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;
-       addheader("date", "$b", ee);
-       addheader("from", "$g (Mail Delivery Subsystem)", ee);
-       addheader("to", returnto->q_paddr, ee);
-       addheader("subject", msg, ee);
+       ee->e_flags |= EF_RESPONSE;
+       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);
+       for (q = returnq; q != NULL; q = q->q_next)
+       {
+               if (bitset(QBADADDR, q->q_flags))
+                       continue;
+
+               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)
+                       addheader("To", q->q_paddr, ee);
+       }
+
+# ifdef LOG
+       if (LogLevel > 5)
+               syslog(LOG_INFO, "%s: %s: return to sender: %s",
+                       e->e_id, ee->e_id, msg);
+# endif
+
+       (void) sprintf(buf, "Returned mail: %s", msg);
+       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 */
 
        /* fake up an address header for the from person */
-       expand("$n", buf, &buf[sizeof buf - 1], CurEnv);
-       if (parse(buf, &ee->e_from, -1) == NULL)
+       expand("\201n", buf, &buf[sizeof buf - 1], e);
+       if (parseaddr(buf, &ee->e_from, 1, '\0', NULL, e) == NULL)
        {
        {
-               syserr("Can't parse myself!");
+               syserr("553 Can't parse myself!");
                ExitStat = EX_SOFTWARE;
                returndepth--;
                return (-1);
        }
                ExitStat = EX_SOFTWARE;
                returndepth--;
                return (-1);
        }
-       ee->e_sendqueue = returnto;
+       ee->e_sender = ee->e_from.q_paddr;
 
        /* push state into submessage */
        CurEnv = ee;
 
        /* push state into submessage */
        CurEnv = ee;
-       define('f', "$n");
-       define('x', "Mail Delivery Subsystem");
+       define('f', "\201n", ee);
+       define('x', "Mail Delivery Subsystem", ee);
+       eatheader(ee, TRUE);
 
        /* actually deliver the error message */
 
        /* actually deliver the error message */
-       sendall(ee, FALSE);
-
-       /* do any closing error processing */
-       checkerrors(ee);
+       sendall(ee, SM_DEFAULT);
 
        /* restore state */
 
        /* restore state */
-       CurEnv = CurEnv->e_parent;
+       dropenvelope(ee);
+       CurEnv = oldcur;
        returndepth--;
 
        /* should check for delivery errors here */
        returndepth--;
 
        /* should check for delivery errors here */
@@ -283,9 +532,9 @@ returntosender(msg, returnto, sendbody)
 **     original offending message.
 **
 **     Parameters:
 **     original offending message.
 **
 **     Parameters:
-**             xfile -- the transcript file.
 **             fp -- the output file.
 **             fp -- the output file.
-**             xdot -- if set, use the SMTP hidden dot algorithm.
+**             m -- the mailer to output to.
+**             e -- the envelope we are working in.
 **
 **     Returns:
 **             none
 **
 **     Returns:
 **             none
@@ -294,60 +543,217 @@ returntosender(msg, returnto, sendbody)
 **             Outputs the body of an error message.
 */
 
 **             Outputs the body of an error message.
 */
 
-errbody(fp, m, xdot)
+errbody(fp, m, e)
        register FILE *fp;
        register struct mailer *m;
        register FILE *fp;
        register struct mailer *m;
-       bool xdot;
+       register ENVELOPE *e;
 {
        register FILE *xfile;
 {
        register FILE *xfile;
+       char *p;
+       register ADDRESS *q;
+       bool printheader;
        char buf[MAXLINE];
        char buf[MAXLINE];
-       bool fullsmtp = bitset(M_FULLSMTP, m->m_flags);
 
 
-       (void) fflush(stdout);
-       if ((xfile = fopen(Transcript, "r")) == NULL)
-               syserr("Cannot open %s", Transcript);
-       errno = 0;
+       if (e->e_parent == NULL)
+       {
+               syserr("errbody: null parent");
+               putline("   ----- Original message lost -----\n", fp, m);
+               return;
+       }
+
+       /*
+       **  Output MIME header.
+       */
+
+       if (e->e_msgboundary != NULL)
+       {
+               putline("This is a MIME-encapsulated message", fp, m);
+               putline("", fp, m);
+               (void) sprintf(buf, "--%s", e->e_msgboundary);
+               putline(buf, fp, m);
+               putline("", fp, m);
+       }
+
+       /*
+       **  Output error message header (if specified and available).
+       */
+
+       if (ErrMsgFile != NULL)
+       {
+               if (*ErrMsgFile == '/')
+               {
+                       xfile = fopen(ErrMsgFile, "r");
+                       if (xfile != NULL)
+                       {
+                               while (fgets(buf, sizeof buf, xfile) != NULL)
+                               {
+                                       expand(buf, buf, &buf[sizeof buf - 1], e);
+                                       putline(buf, fp, m);
+                               }
+                               (void) fclose(xfile);
+                               putline("\n", fp, m);
+                       }
+               }
+               else
+               {
+                       expand(ErrMsgFile, buf, &buf[sizeof buf - 1], e);
+                       putline(buf, fp, m);
+                       putline("", fp, m);
+               }
+       }
+
+       /*
+       **  Output message introduction
+       */
+
+       printheader = TRUE;
+       for (q = e->e_parent->e_sendqueue; q != NULL; q = q->q_next)
+       {
+               if (bitset(QBADADDR, q->q_flags))
+               {
+                       if (printheader)
+                       {
+                               putline("   ----- The following addresses failed -----",
+                                       fp, m);
+                               printheader = FALSE;
+                       }
+                       if (q->q_alias != NULL)
+                               putline(q->q_alias->q_paddr, fp, m);
+                       else
+                               putline(q->q_paddr, fp, m);
+               }
+       }
+       if (!printheader)
+               putline("\n", fp, m);
 
        /*
        **  Output transcript of errors
        */
 
 
        /*
        **  Output transcript of errors
        */
 
-       fprintf(fp, "   ----- Transcript of session follows -----\n");
-       (void) fflush(Xscript);
-       while (fgets(buf, sizeof buf, xfile) != NULL)
-               putline(buf, fp, fullsmtp);
+       (void) fflush(stdout);
+       p = queuename(e->e_parent, 'x');
+       if ((xfile = fopen(p, "r")) == NULL)
+       {
+               syserr("Cannot open %s", p);
+               putline("   ----- Transcript of session is unavailable -----\n", fp, m);
+       }
+       else
+       {
+               putline("   ----- Transcript of session follows -----\n", fp, m);
+               if (e->e_xfp != NULL)
+                       (void) fflush(e->e_xfp);
+               while (fgets(buf, sizeof buf, xfile) != NULL)
+                       putline(buf, fp, m);
+               (void) xfclose(xfile, "errbody xscript", p);
+       }
+       errno = 0;
 
        /*
        **  Output text of original message
        */
 
        if (NoReturn)
 
        /*
        **  Output text of original message
        */
 
        if (NoReturn)
-               fprintf(fp, "\n   ----- Return message suppressed -----\n\n");
-       else if (TempFile != NULL)
+               SendBody = FALSE;
+       putline("", fp, m);
+       if (e->e_parent->e_df != NULL)
        {
                if (SendBody)
        {
                if (SendBody)
-               {
-                       fprintf(fp, "\n   ----- Unsent message follows -----\n");
-                       (void) fflush(fp);
-                       putheader(fp, m, CurEnv->e_parent);
-                       fprintf(fp, "\n");
-                       putbody(fp, m, xdot);
-               }
+                       putline("   ----- Unsent message follows -----\n", fp, m);
                else
                else
+                       putline("   ----- Message header follows -----\n", fp, m);
+               (void) fflush(fp);
+
+               if (e->e_msgboundary != NULL)
                {
                {
-                       fprintf(fp, "\n  ----- Message header follows -----\n");
-                       (void) fflush(fp);
-                       putheader(fp, m, CurEnv);
+                       putline("", fp, m);
+                       (void) sprintf(buf, "--%s", e->e_msgboundary);
+                       putline(buf, fp, m);
+                       putline("Content-Type: message/rfc822", fp, m);
+                       putline("", fp, m);
                }
                }
+               putheader(fp, m, e->e_parent);
+               putline("", fp, m);
+               if (SendBody)
+                       putbody(fp, m, e->e_parent, e->e_msgboundary);
+               else
+                       putline("   ----- Message body suppressed -----", fp, m);
        }
        else
        }
        else
-               fprintf(fp, "\n  ----- No message was collected -----\n\n");
+       {
+               putline("  ----- No message was collected -----\n", fp, m);
+       }
+
+       if (e->e_msgboundary != NULL)
+       {
+               putline("", fp, m);
+               (void) sprintf(buf, "--%s--", e->e_msgboundary);
+               putline(buf, fp, m);
+       }
+       putline("", fp, m);
 
        /*
        **  Cleanup and exit
        */
 
 
        /*
        **  Cleanup and exit
        */
 
-       (void) fclose(xfile);
        if (errno != 0)
                syserr("errbody: I/O error");
 }
        if (errno != 0)
                syserr("errbody: I/O error");
 }
+\f/*
+**  PRUNEROUTE -- prune an RFC-822 source route
+** 
+**     Trims down a source route to the last internet-registered hop.
+**     This is encouraged by RFC 1123 section 5.3.3.
+** 
+**     Parameters:
+**             addr -- the address
+** 
+**     Returns:
+**             TRUE -- address was modified
+**             FALSE -- address could not be pruned
+** 
+**     Side Effects:
+**             modifies addr in-place
+*/
+
+pruneroute(addr)
+       char *addr;
+{
+#ifdef NAMED_BIND
+       char *start, *at, *comma;
+       char c;
+       int rcode;
+       char hostbuf[BUFSIZ];
+       char *mxhosts[MAXMXHOSTS + 1];
+
+       /* check to see if this is really a route-addr */
+       if (*addr != '<' || addr[1] != '@' || addr[strlen(addr) - 1] != '>')
+               return FALSE;
+       start = strchr(addr, ':');
+       at = strrchr(addr, '@');
+       if (start == NULL || at == NULL || at < start)
+               return FALSE;
+
+       /* slice off the angle brackets */
+       strcpy(hostbuf, at + 1);
+       hostbuf[strlen(hostbuf) - 1] = '\0';
+
+       while (start)
+       {
+               if (getmxrr(hostbuf, mxhosts, FALSE, &rcode) > 0)
+               {
+                       strcpy(addr + 1, start + 1);
+                       return TRUE;
+               }
+               c = *start;
+               *start = '\0';
+               comma = strrchr(addr, ',');
+               if (comma && comma[1] == '@')
+                       strcpy(hostbuf, comma + 2);
+               else
+                       comma = 0;
+               *start = c;
+               start = comma;
+       }
+#endif
+       return FALSE;
+}