more cleanup for DSN drafts
[unix-history] / usr / src / usr.sbin / sendmail / src / deliver.c
index 7762fdf..6c2deeb 100644 (file)
@@ -7,14 +7,13 @@
  */
 
 #ifndef lint
  */
 
 #ifndef lint
-static char sccsid[] = "@(#)deliver.c  8.58 (Berkeley) %G%";
+static char sccsid[] = "@(#)deliver.c  8.137 (Berkeley) %G%";
 #endif /* not lint */
 
 #include "sendmail.h"
 #include <netdb.h>
 #include <errno.h>
 #endif /* not lint */
 
 #include "sendmail.h"
 #include <netdb.h>
 #include <errno.h>
-#ifdef NAMED_BIND
-#include <arpa/nameser.h>
+#if NAMED_BIND
 #include <resolv.h>
 
 extern int     h_errno;
 #include <resolv.h>
 
 extern int     h_errno;
@@ -40,6 +39,7 @@ extern char   SmtpError[];
 **                     appropriate action.
 */
 
 **                     appropriate action.
 */
 
+void
 sendall(e, mode)
        ENVELOPE *e;
        char mode;
 sendall(e, mode)
        ENVELOPE *e;
        char mode;
@@ -47,9 +47,10 @@ sendall(e, mode)
        register ADDRESS *q;
        char *owner;
        int otherowners;
        register ADDRESS *q;
        char *owner;
        int otherowners;
-       register ENVELOPE *ee;
-       ENVELOPE *splitenv = NULL;
-       bool announcequeueup;
+       bool oldverbose = Verbose;
+       bool somedeliveries = FALSE;
+       int pid;
+       extern void sendenvelope();
        int pid;
 #ifdef LOCKF
        struct flock lfd;
        int pid;
 #ifdef LOCKF
        struct flock lfd;
@@ -62,7 +63,8 @@ sendall(e, mode)
        **  addresses to be sent.
        */
 
        **  addresses to be sent.
        */
 
-       if (bitset(EF_FATALERRS, e->e_flags) && OpMode == MD_SMTP)
+       if (bitset(EF_FATALERRS, e->e_flags) &&
+           (OpMode == MD_SMTP || OpMode == MD_DAEMON))
        {
                e->e_flags |= EF_CLRQUEUE;
                return;
        {
                e->e_flags |= EF_CLRQUEUE;
                return;
@@ -76,16 +78,17 @@ sendall(e, mode)
                if (mode != SM_VERIFY &&
                    shouldqueue(e->e_msgpriority, e->e_ctime))
                        mode = SM_QUEUE;
                if (mode != SM_VERIFY &&
                    shouldqueue(e->e_msgpriority, e->e_ctime))
                        mode = SM_QUEUE;
-               announcequeueup = mode == SM_QUEUE;
        }
        }
-       else
-               announcequeueup = FALSE;
 
        if (tTd(13, 1))
        {
 
        if (tTd(13, 1))
        {
+               extern void printenvflags();
+
                printf("\n===== SENDALL: mode %c, id %s, e_from ",
                        mode, e->e_id);
                printaddr(&e->e_from, FALSE);
                printf("\n===== SENDALL: mode %c, id %s, e_from ",
                        mode, e->e_id);
                printaddr(&e->e_from, FALSE);
+               printf("\te_flags = ");
+               printenvflags(e);
                printf("sendqueue:\n");
                printaddr(e->e_sendqueue, TRUE);
        }
                printf("sendqueue:\n");
                printaddr(e->e_sendqueue, TRUE);
        }
@@ -101,9 +104,12 @@ sendall(e, mode)
        if (e->e_hopcount > MaxHopCount)
        {
                errno = 0;
        if (e->e_hopcount > MaxHopCount)
        {
                errno = 0;
+               queueup(e, TRUE, mode == SM_QUEUE);
+               e->e_flags |= EF_FATALERRS|EF_PM_NOTIFY|EF_CLRQUEUE;
                syserr("554 too many hops %d (%d max): from %s via %s, to %s",
                        e->e_hopcount, MaxHopCount, e->e_from.q_paddr,
                syserr("554 too many hops %d (%d max): from %s via %s, to %s",
                        e->e_hopcount, MaxHopCount, e->e_from.q_paddr,
-                       RealHostName, e->e_sendqueue->q_paddr);
+                       RealHostName == NULL ? "localhost" : RealHostName,
+                       e->e_sendqueue->q_paddr);
                return;
        }
 
                return;
        }
 
@@ -125,7 +131,7 @@ sendall(e, mode)
                        printaddr(&e->e_from, FALSE);
                }
                e->e_from.q_flags |= QDONTSEND;
                        printaddr(&e->e_from, FALSE);
                }
                e->e_from.q_flags |= QDONTSEND;
-               (void) recipient(&e->e_from, &e->e_sendqueue, e);
+               (void) recipient(&e->e_from, &e->e_sendqueue, 0, e);
        }
 
 # ifdef QUEUE
        }
 
 # ifdef QUEUE
@@ -318,10 +324,39 @@ sendall(e, mode)
                        {
                                otherowners++;
                        }
                        {
                                otherowners++;
                        }
+
+                       /*
+                       **  If this mailer is expensive, and if we don't
+                       **  want to make connections now, just mark these
+                       **  addresses and return.  This is useful if we
+                       **  want to batch connections to reduce load.  This
+                       **  will cause the messages to be queued up, and a
+                       **  daemon will come along to send the messages later.
+                       */
+
+                       if (bitset(QBADADDR|QQUEUEUP, q->q_flags))
+                               continue;
+                       if (NoConnect && !Verbose &&
+                           bitnset(M_EXPENSIVE, q->q_mailer->m_flags))
+                       {
+                               q->q_flags |= QQUEUEUP;
+                               e->e_to = q->q_paddr;
+                               message("queued");
+                               if (LogLevel > 8)
+                                       logdelivery(q->q_mailer, NULL,
+                                                   "queued", NULL,
+                                                   (time_t) 0, e);
+                               e->e_to = NULL;
+                       }
+                       else
+                       {
+                               somedeliveries = TRUE;
+                       }
                }
 
                if (owner != NULL && otherowners > 0)
                {
                }
 
                if (owner != NULL && otherowners > 0)
                {
+                       register ENVELOPE *ee;
                        extern HDR *copyheader();
                        extern ADDRESS *copyqueue();
 
                        extern HDR *copyheader();
                        extern ADDRESS *copyqueue();
 
@@ -341,7 +376,8 @@ sendall(e, mode)
                        ee->e_header = copyheader(e->e_header);
                        ee->e_sendqueue = copyqueue(e->e_sendqueue);
                        ee->e_errorqueue = copyqueue(e->e_errorqueue);
                        ee->e_header = copyheader(e->e_header);
                        ee->e_sendqueue = copyqueue(e->e_sendqueue);
                        ee->e_errorqueue = copyqueue(e->e_errorqueue);
-                       ee->e_flags = e->e_flags & ~(EF_INQUEUE|EF_CLRQUEUE|EF_FATALERRS);
+                       ee->e_flags = e->e_flags & ~(EF_INQUEUE|EF_CLRQUEUE|EF_FATALERRS|EF_SENDRECEIPT);
+                       ee->e_flags |= EF_NORECEIPT;
                        setsender(owner, ee, NULL, TRUE);
                        if (tTd(13, 5))
                        {
                        setsender(owner, ee, NULL, TRUE);
                        if (tTd(13, 5))
                        {
@@ -351,40 +387,49 @@ sendall(e, mode)
                        ee->e_from.q_flags |= QDONTSEND;
                        ee->e_dfp = NULL;
                        ee->e_xfp = NULL;
                        ee->e_from.q_flags |= QDONTSEND;
                        ee->e_dfp = NULL;
                        ee->e_xfp = NULL;
-                       ee->e_df = NULL;
                        ee->e_errormode = EM_MAIL;
                        ee->e_errormode = EM_MAIL;
-                       ee->e_sibling = splitenv;
-                       splitenv = ee;
                        
                        for (q = e->e_sendqueue; q != NULL; q = q->q_next)
                                if (q->q_owner == owner)
                        
                        for (q = e->e_sendqueue; q != NULL; q = q->q_next)
                                if (q->q_owner == owner)
+                               {
                                        q->q_flags |= QDONTSEND;
                                        q->q_flags |= QDONTSEND;
+                                       q->q_flags &= ~QQUEUEUP;
+                               }
                        for (q = ee->e_sendqueue; q != NULL; q = q->q_next)
                                if (q->q_owner != owner)
                        for (q = ee->e_sendqueue; q != NULL; q = q->q_next)
                                if (q->q_owner != owner)
+                               {
                                        q->q_flags |= QDONTSEND;
                                        q->q_flags |= QDONTSEND;
+                                       q->q_flags &= ~QQUEUEUP;
+                               }
 
 
-                       if (e->e_df != NULL && mode != SM_VERIFY)
+                       if (mode != SM_VERIFY && bitset(EF_HAS_DF, e->e_flags))
                        {
                        {
+                               char df1buf[20], df2buf[20];
+
                                ee->e_dfp = NULL;
                                ee->e_dfp = NULL;
-                               ee->e_df = queuename(ee, 'd');
-                               ee->e_df = newstr(ee->e_df);
-                               if (link(e->e_df, ee->e_df) < 0)
+                               strcpy(df1buf, queuename(e, 'd'));
+                               strcpy(df2buf, queuename(ee, 'd'));
+                               if (link(df1buf, df2buf) < 0)
                                {
                                        syserr("sendall: link(%s, %s)",
                                {
                                        syserr("sendall: link(%s, %s)",
-                                               e->e_df, ee->e_df);
+                                               df1buf, df2buf);
                                }
                        }
                                }
                        }
-
-                       if (mode != SM_VERIFY)
-                               openxscript(ee);
 #ifdef LOG
                        if (LogLevel > 4)
 #ifdef LOG
                        if (LogLevel > 4)
-                               syslog(LOG_INFO, "%s: clone %s",
-                                       ee->e_id, e->e_id);
+                               syslog(LOG_INFO, "%s: clone %s, owner=%s",
+                                       ee->e_id, e->e_id, owner);
 #endif
 #endif
+                       CurEnv = ee;
+                       sendenvelope(ee, mode, announcequeueup);
+                       dropenvelope(ee);
                }
        }
 
                }
        }
 
+       /*
+       **  Split off envelopes have been sent -- now send original
+       */
+
        if (owner != NULL)
        {
                setsender(owner, e, NULL, TRUE);
        if (owner != NULL)
        {
                setsender(owner, e, NULL, TRUE);
@@ -395,8 +440,15 @@ sendall(e, mode)
                }
                e->e_from.q_flags |= QDONTSEND;
                e->e_errormode = EM_MAIL;
                }
                e->e_from.q_flags |= QDONTSEND;
                e->e_errormode = EM_MAIL;
+               e->e_flags |= EF_NORECEIPT;
        }
 
        }
 
+       /* if nothing to be delivered, just queue up everything */
+       if (!somedeliveries && mode != SM_QUEUE && mode != SM_VERIFY)
+               mode = SM_QUEUE;
+
+       bool oldverbose = Verbose;
+
        if (splitenv != NULL)
        {
                if (tTd(13, 1))
        if (splitenv != NULL)
        {
                if (tTd(13, 1))
@@ -408,23 +460,39 @@ sendall(e, mode)
                for (ee = splitenv; ee != NULL; ee = ee->e_sibling)
                {
                        CurEnv = ee;
                for (ee = splitenv; ee != NULL; ee = ee->e_sibling)
                {
                        CurEnv = ee;
+                       if (mode != SM_VERIFY)
+                               openxscript(ee);
                        sendenvelope(ee, mode);
                        sendenvelope(ee, mode);
+                       dropenvelope(ee);
                }
 
                CurEnv = e;
        }
        sendenvelope(e, mode);
                }
 
                CurEnv = e;
        }
        sendenvelope(e, mode);
-
-       for (; splitenv != NULL; splitenv = splitenv->e_sibling)
-               dropenvelope(splitenv);
+       Verbose = oldverbose;
 }
 
 }
 
+void
 sendenvelope(e, mode)
        register ENVELOPE *e;
        char mode;
 {
        register ADDRESS *q;
 sendenvelope(e, mode)
        register ENVELOPE *e;
        char mode;
 {
        register ADDRESS *q;
-       bool oldverbose = Verbose;
+       bool didany;
+
+       /*
+       **  If we have had global, fatal errors, don't bother sending
+       **  the message at all if we are in SMTP mode.  Local errors
+       **  (e.g., a single address failing) will still cause the other
+       **  addresses to be sent.
+       */
+
+       if (bitset(EF_FATALERRS, e->e_flags) &&
+           (OpMode == MD_SMTP || OpMode == MD_DAEMON))
+       {
+               e->e_flags |= EF_CLRQUEUE;
+               return;
+       }
 
        /*
        **  Run through the list and send everything.
 
        /*
        **  Run through the list and send everything.
@@ -435,6 +503,7 @@ sendenvelope(e, mode)
 
        e->e_nsent = 0;
        e->e_flags |= EF_GLOBALERRS;
 
        e->e_nsent = 0;
        e->e_flags |= EF_GLOBALERRS;
+       didany = FALSE;
 
        /* now run through the queue */
        for (q = e->e_sendqueue; q != NULL; q = q->q_next)
 
        /* now run through the queue */
        for (q = e->e_sendqueue; q != NULL; q = q->q_next)
@@ -475,9 +544,14 @@ sendenvelope(e, mode)
                        }
 # endif /* QUEUE */
                        (void) deliver(e, q);
                        }
 # endif /* QUEUE */
                        (void) deliver(e, q);
+                       didany = TRUE;
                }
        }
                }
        }
-       Verbose = oldverbose;
+       if (didany)
+       {
+               e->e_dtime = curtime();
+               e->e_ntries++;
+       }
 
 #ifdef XDEBUG
        checkfd012("end of sendenvelope");
 
 #ifdef XDEBUG
        checkfd012("end of sendenvelope");
@@ -544,7 +618,7 @@ sendenvelope(e, mode)
 
 dofork()
 {
 
 dofork()
 {
-       register int pid;
+       register int pid = -1;
 
        DOFORK(fork);
        return (pid);
 
        DOFORK(fork);
        return (pid);
@@ -571,6 +645,7 @@ dofork()
 **             The standard input is passed off to someone.
 */
 
 **             The standard input is passed off to someone.
 */
 
+int
 deliver(e, firstto)
        register ENVELOPE *e;
        ADDRESS *firstto;
 deliver(e, firstto)
        register ENVELOPE *e;
        ADDRESS *firstto;
@@ -585,26 +660,28 @@ deliver(e, firstto)
        register MCI *mci;
        register ADDRESS *to = firstto;
        bool clever = FALSE;            /* running user smtp to this mailer */
        register MCI *mci;
        register ADDRESS *to = firstto;
        bool clever = FALSE;            /* running user smtp to this mailer */
-       ADDRESS *tochain = NULL;        /* chain of users in this mailer call */
+       ADDRESS *tochain = NULL;        /* users chain in this mailer call */
        int rcode;                      /* response code */
        char *firstsig;                 /* signature of firstto */
        int rcode;                      /* response code */
        char *firstsig;                 /* signature of firstto */
-       int pid;
+       int pid = -1;
        char *curhost;
        char *curhost;
+       time_t xstart;
        int mpvect[2];
        int rpvect[2];
        char *pv[MAXPV+1];
        char tobuf[TOBUFSIZE];          /* text line of to people */
        int mpvect[2];
        int rpvect[2];
        char *pv[MAXPV+1];
        char tobuf[TOBUFSIZE];          /* text line of to people */
-       char buf[MAXNAME];
-       char rpathbuf[MAXNAME];         /* translated return path */
+       char buf[MAXNAME + 1];
+       char rpathbuf[MAXNAME + 1];     /* translated return path */
        extern int checkcompat();
 
        errno = 0;
        if (!ForceMail && bitset(QDONTSEND|QPSEUDO, to->q_flags))
                return (0);
 
        extern int checkcompat();
 
        errno = 0;
        if (!ForceMail && bitset(QDONTSEND|QPSEUDO, to->q_flags))
                return (0);
 
-#ifdef NAMED_BIND
+#if NAMED_BIND
        /* unless interactive, try twice, over a minute */
        /* unless interactive, try twice, over a minute */
-       if (OpMode == MD_DAEMON || OpMode == MD_SMTP) {
+       if (OpMode == MD_DAEMON || OpMode == MD_SMTP)
+       {
                _res.retrans = 30;
                _res.retry = 2;
        }
                _res.retrans = 30;
                _res.retry = 2;
        }
@@ -615,40 +692,14 @@ deliver(e, firstto)
        CurEnv = e;                     /* just in case */
        e->e_statmsg = NULL;
        SmtpError[0] = '\0';
        CurEnv = e;                     /* just in case */
        e->e_statmsg = NULL;
        SmtpError[0] = '\0';
+       xstart = curtime();
 
        if (tTd(10, 1))
 
        if (tTd(10, 1))
-               printf("\n--deliver, mailer=%d, host=`%s', first user=`%s'\n",
-                       m->m_mno, host, to->q_user);
+               printf("\n--deliver, id=%s, mailer=%s, host=`%s', first user=`%s'\n",
+                       e->e_id, m->m_name, host, to->q_user);
        if (tTd(10, 100))
                printopenfds(FALSE);
 
        if (tTd(10, 100))
                printopenfds(FALSE);
 
-       /*
-       **  If this mailer is expensive, and if we don't want to make
-       **  connections now, just mark these addresses and return.
-       **      This is useful if we want to batch connections to
-       **      reduce load.  This will cause the messages to be
-       **      queued up, and a daemon will come along to send the
-       **      messages later.
-       **              This should be on a per-mailer basis.
-       */
-
-       if (NoConnect && bitnset(M_EXPENSIVE, m->m_flags) && !Verbose)
-       {
-               for (; to != NULL; to = to->q_next)
-               {
-                       if (bitset(QDONTSEND|QBADADDR|QQUEUEUP, to->q_flags) ||
-                           to->q_mailer != m)
-                               continue;
-                       to->q_flags |= QQUEUEUP;
-                       e->e_to = to->q_paddr;
-                       message("queued");
-                       if (LogLevel > 8)
-                               logdelivery(m, NULL, "queued", NULL, e);
-               }
-               e->e_to = NULL;
-               return (0);
-       }
-
        /*
        **  Do initial argv setup.
        **      Insert the mailer name.  Notice that $x expansion is
        /*
        **  Do initial argv setup.
        **      Insert the mailer name.  Notice that $x expansion is
@@ -662,7 +713,11 @@ deliver(e, firstto)
 
        /* rewrite from address, using rewriting rules */
        rcode = EX_OK;
 
        /* rewrite from address, using rewriting rules */
        rcode = EX_OK;
-       (void) strcpy(rpathbuf, remotename(e->e_from.q_paddr, m,
+       if (bitnset(M_UDBENVELOPE, e->e_from.q_mailer->m_flags))
+               p = e->e_sender;
+       else
+               p = e->e_from.q_paddr;
+       (void) strcpy(rpathbuf, remotename(p, m,
                                           RF_SENDERADDR|RF_CANONICAL,
                                           &rcode, e));
        define('g', rpathbuf, e);               /* translated return path */
                                           RF_SENDERADDR|RF_CANONICAL,
                                           &rcode, e));
        define('g', rpathbuf, e);               /* translated return path */
@@ -704,7 +759,7 @@ deliver(e, firstto)
                        break;
 
                /* this entry is safe -- go ahead and process it */
                        break;
 
                /* this entry is safe -- go ahead and process it */
-               expand(*mvp, buf, &buf[sizeof buf - 1], e);
+               expand(*mvp, buf, sizeof buf, e);
                *pvp++ = newstr(buf);
                if (pvp >= &pv[MAXPV - 3])
                {
                *pvp++ = newstr(buf);
                if (pvp >= &pv[MAXPV - 3])
                {
@@ -766,20 +821,13 @@ deliver(e, firstto)
                }
 
                /* compute effective uid/gid when sending */
                }
 
                /* compute effective uid/gid when sending */
-               /* XXX perhaps this should be to->q_mailer != LocalMailer ?? */
-               /* XXX perhaps it should be a mailer flag? */
-               if (to->q_mailer == ProgMailer || to->q_mailer == FileMailer)
-               {
+               if (bitnset(M_RUNASRCPT, to->q_mailer->m_flags))
                        ctladdr = getctladdr(to);
                        ctladdr = getctladdr(to);
-                       if (ctladdr != NULL &&
-                           bitset(QBOGUSSHELL, ctladdr->q_flags))
-                       {
-                               /* user has no valid shell on this machine */
-                               usrerr("550 User %s doesn't have a valid shell on %s",
-                                       ctladdr->q_ruser, MyHostName);
-                               giveresponse(EX_UNAVAILABLE, m, NULL, ctladdr, e);
-                               continue;
-                       }
+
+               if (tTd(10, 2))
+               {
+                       printf("ctladdr=");
+                       printaddr(ctladdr, FALSE);
                }
 
                user = to->q_user;
                }
 
                user = to->q_user;
@@ -798,16 +846,19 @@ deliver(e, firstto)
 
                if (m->m_maxsize != 0 && e->e_msgsize > m->m_maxsize)
                {
 
                if (m->m_maxsize != 0 && e->e_msgsize > m->m_maxsize)
                {
-                       NoReturn = TRUE;
+                       e->e_flags |= EF_NO_BODY_RETN;
                        usrerr("552 Message is too large; %ld bytes max", m->m_maxsize);
                        usrerr("552 Message is too large; %ld bytes max", m->m_maxsize);
-                       giveresponse(EX_UNAVAILABLE, m, NULL, ctladdr, e);
+                       giveresponse(EX_UNAVAILABLE, m, NULL, ctladdr, xstart, e);
                        continue;
                }
                        continue;
                }
+#if NAMED_BIND
+               h_errno = 0;
+#endif
                rcode = checkcompat(to, e);
                if (rcode != EX_OK)
                {
                rcode = checkcompat(to, e);
                if (rcode != EX_OK)
                {
-                       markfailure(e, to, rcode);
-                       giveresponse(rcode, m, NULL, ctladdr, e);
+                       markfailure(e, to, NULL, rcode);
+                       giveresponse(rcode, m, NULL, ctladdr, xstart, e);
                        continue;
                }
 
                        continue;
                }
 
@@ -852,9 +903,21 @@ deliver(e, firstto)
                if (m == FileMailer)
                {
                        rcode = mailfile(user, ctladdr, e);
                if (m == FileMailer)
                {
                        rcode = mailfile(user, ctladdr, e);
-                       giveresponse(rcode, m, NULL, ctladdr, e);
+                       giveresponse(rcode, m, NULL, ctladdr, xstart, e);
+                       e->e_nsent++;
                        if (rcode == EX_OK)
                        if (rcode == EX_OK)
+                       {
                                to->q_flags |= QSENT;
                                to->q_flags |= QSENT;
+                               if (bitnset(M_LOCALMAILER, m->m_flags) &&
+                                   (e->e_receiptto != NULL ||
+                                    bitset(QPINGONSUCCESS, to->q_flags)))
+                               {
+                                       to->q_flags |= QREPORT;
+                                       fprintf(e->e_xfp, "%s... Successfully delivered\n",
+                                               to->q_paddr);
+                               }
+                       }
+                       to->q_statdate = curtime();
                        continue;
                }
 
                        continue;
                }
 
@@ -882,7 +945,7 @@ deliver(e, firstto)
 
                if (!clever)
                {
 
                if (!clever)
                {
-                       expand(*mvp, buf, &buf[sizeof buf - 1], e);
+                       expand(*mvp, buf, sizeof buf, e);
                        *pvp++ = newstr(buf);
                        if (pvp >= &pv[MAXPV - 2])
                        {
                        *pvp++ = newstr(buf);
                        if (pvp >= &pv[MAXPV - 2])
                        {
@@ -908,7 +971,7 @@ deliver(e, firstto)
 
        while (!clever && *++mvp != NULL)
        {
 
        while (!clever && *++mvp != NULL)
        {
-               expand(*mvp, buf, &buf[sizeof buf - 1], e);
+               expand(*mvp, buf, sizeof buf, e);
                *pvp++ = newstr(buf);
                if (pvp >= &pv[MAXPV])
                        syserr("554 deliver: pv overflow after $u for %s", pv[0]);
                *pvp++ = newstr(buf);
                if (pvp >= &pv[MAXPV])
                        syserr("554 deliver: pv overflow after $u for %s", pv[0]);
@@ -924,10 +987,11 @@ deliver(e, firstto)
        */
 
        /*XXX this seems a bit wierd */
        */
 
        /*XXX this seems a bit wierd */
-       if (ctladdr == NULL && bitset(QGOODUID, e->e_from.q_flags))
+       if (ctladdr == NULL && m != ProgMailer &&
+           bitset(QGOODUID, e->e_from.q_flags))
                ctladdr = &e->e_from;
 
                ctladdr = &e->e_from;
 
-#ifdef NAMED_BIND
+#if NAMED_BIND
        if (ConfigLevel < 2)
                _res.options &= ~(RES_DEFNAMES | RES_DNSRCH);   /* XXX */
 #endif
        if (ConfigLevel < 2)
                _res.options &= ~(RES_DEFNAMES | RES_DNSRCH);   /* XXX */
 #endif
@@ -938,8 +1002,11 @@ deliver(e, firstto)
                printav(pv);
        }
        errno = 0;
                printav(pv);
        }
        errno = 0;
+#if NAMED_BIND
+       h_errno = 0;
+#endif
 
 
-       CurHostName = m->m_mailer;
+       CurHostName = NULL;
 
        /*
        **  Deal with the special case of mail handled through an IPC
 
        /*
        **  Deal with the special case of mail handled through an IPC
@@ -964,6 +1031,16 @@ deliver(e, firstto)
        }
 #endif
 
        }
 #endif
 
+       /* check for 8-bit available */
+       if (bitset(EF_HAS8BIT, e->e_flags) &&
+           bitnset(M_7BITS, m->m_flags) &&
+           !bitset(MM_MIME8BIT, MimeMode))
+       {
+               usrerr("554 Cannot send 8-bit data to 7-bit destination");
+               rcode = EX_DATAERR;
+               goto give_up;
+       }
+
        /* check for Local Person Communication -- not for mortals!!! */
        if (strcmp(m->m_mailer, "[LPC]") == 0)
        {
        /* check for Local Person Communication -- not for mortals!!! */
        if (strcmp(m->m_mailer, "[LPC]") == 0)
        {
@@ -979,7 +1056,7 @@ deliver(e, firstto)
        {
 #ifdef DAEMON
                register int i;
        {
 #ifdef DAEMON
                register int i;
-               register u_short port;
+               register u_short port = 0;
 
                if (pv[0] == NULL || pv[1] == NULL || pv[1][0] == '\0')
                {
 
                if (pv[0] == NULL || pv[1] == NULL || pv[1][0] == '\0')
                {
@@ -994,7 +1071,7 @@ deliver(e, firstto)
                if (curhost == NULL || curhost[0] == '\0')
                {
                        syserr("null host signature for %s", pv[1]);
                if (curhost == NULL || curhost[0] == '\0')
                {
                        syserr("null host signature for %s", pv[1]);
-                       rcode = EX_OSERR;
+                       rcode = EX_CONFIG;
                        goto give_up;
                }
 
                        goto give_up;
                }
 
@@ -1006,13 +1083,11 @@ deliver(e, firstto)
                }
                if (pv[2] != NULL)
                        port = atoi(pv[2]);
                }
                if (pv[2] != NULL)
                        port = atoi(pv[2]);
-               else
-                       port = 0;
 tryhost:
                while (*curhost != '\0')
                {
                        register char *p;
 tryhost:
                while (*curhost != '\0')
                {
                        register char *p;
-                       static char hostbuf[MAXNAME];
+                       static char hostbuf[MAXNAME + 1];
 
                        /* pull the next host from the signature */
                        p = strchr(curhost, ':');
 
                        /* pull the next host from the signature */
                        p = strchr(curhost, ':');
@@ -1041,6 +1116,8 @@ tryhost:
                                        mci_dump(mci, FALSE);
                                }
                                CurHostName = mci->mci_host;
                                        mci_dump(mci, FALSE);
                                }
                                CurHostName = mci->mci_host;
+                               message("Using cached connection to %s via %s...",
+                                       hostbuf, m->m_name);
                                break;
                        }
                        mci->mci_mailer = m;
                                break;
                        }
                        mci->mci_mailer = m;
@@ -1049,13 +1126,13 @@ tryhost:
 
                        /* try the connection */
                        setproctitle("%s %s: %s", e->e_id, hostbuf, "user open");
 
                        /* try the connection */
                        setproctitle("%s %s: %s", e->e_id, hostbuf, "user open");
-                       message("Connecting to %s (%s)...",
+                       message("Connecting to %s via %s...",
                                hostbuf, m->m_name);
                        i = makeconnection(hostbuf, port, mci,
                                bitnset(M_SECURE_PORT, m->m_flags));
                        mci->mci_exitstat = i;
                        mci->mci_errno = errno;
                                hostbuf, m->m_name);
                        i = makeconnection(hostbuf, port, mci,
                                bitnset(M_SECURE_PORT, m->m_flags));
                        mci->mci_exitstat = i;
                        mci->mci_errno = errno;
-#ifdef NAMED_BIND
+#if NAMED_BIND
                        mci->mci_herrno = h_errno;
 #endif
                        if (i == EX_OK)
                        mci->mci_herrno = h_errno;
 #endif
                        if (i == EX_OK)
@@ -1093,6 +1170,14 @@ tryhost:
        }
        else
        {
        }
        else
        {
+               /* flush any expired connections */
+               (void) mci_scan(NULL);
+
+               /* announce the connection to verbose listeners */
+               if (host == NULL || host[0] == '\0')
+                       message("Connecting to %s...", m->m_name);
+               else
+                       message("Connecting to %s via %s...", host, m->m_name);
                if (TrafficLogFile != NULL)
                {
                        char **av;
                if (TrafficLogFile != NULL)
                {
                        char **av;
@@ -1169,28 +1254,39 @@ tryhost:
                        extern char **environ;
                        extern int DtableSize;
 
                        extern char **environ;
                        extern int DtableSize;
 
+                       if (e->e_lockfp != NULL)
+                               (void) close(fileno(e->e_lockfp));
+
                        /* child -- set up input & exec mailer */
                        (void) setsignal(SIGINT, SIG_IGN);
                        (void) setsignal(SIGHUP, SIG_IGN);
                        (void) setsignal(SIGTERM, SIG_DFL);
 
                        /* reset user and group */
                        /* child -- set up input & exec mailer */
                        (void) setsignal(SIGINT, SIG_IGN);
                        (void) setsignal(SIGHUP, SIG_IGN);
                        (void) setsignal(SIGTERM, SIG_DFL);
 
                        /* reset user and group */
-                       if (!bitnset(M_RESTR, m->m_flags))
+                       if (bitnset(M_SPECIFIC_UID, m->m_flags))
                        {
                        {
-                               if (ctladdr == NULL || ctladdr->q_uid == 0)
-                               {
-                                       (void) initgroups(DefUser, DefGid);
+                               (void) setgid(m->m_gid);
+                               (void) setuid(m->m_uid);
+                       }
+                       else if (ctladdr != NULL && ctladdr->q_uid != 0)
+                       {
+                               (void) initgroups(ctladdr->q_ruser?
+                                       ctladdr->q_ruser: ctladdr->q_user,
+                                       ctladdr->q_gid);
+                               (void) setgid(ctladdr->q_gid);
+                               (void) setuid(ctladdr->q_uid);
+                       }
+                       else
+                       {
+                               (void) initgroups(DefUser, DefGid);
+                               if (m->m_gid == 0)
                                        (void) setgid(DefGid);
                                        (void) setgid(DefGid);
+                               else
+                                       (void) setgid(m->m_gid);
+                               if (m->m_uid == 0)
                                        (void) setuid(DefUid);
                                        (void) setuid(DefUid);
-                               }
                                else
                                else
-                               {
-                                       (void) initgroups(ctladdr->q_ruser?
-                                               ctladdr->q_ruser: ctladdr->q_user,
-                                               ctladdr->q_gid);
-                                       (void) setgid(ctladdr->q_gid);
-                                       (void) setuid(ctladdr->q_uid);
-                               }
+                                       (void) setuid(m->m_uid);
                        }
 
                        if (tTd(11, 2))
                        }
 
                        if (tTd(11, 2))
@@ -1201,14 +1297,14 @@ tryhost:
                        if (m->m_execdir != NULL)
                        {
                                char *p, *q;
                        if (m->m_execdir != NULL)
                        {
                                char *p, *q;
-                               char buf[MAXLINE];
+                               char buf[MAXLINE + 1];
 
                                for (p = m->m_execdir; p != NULL; p = q)
                                {
                                        q = strchr(p, ':');
                                        if (q != NULL)
                                                *q = '\0';
 
                                for (p = m->m_execdir; p != NULL; p = q)
                                {
                                        q = strchr(p, ':');
                                        if (q != NULL)
                                                *q = '\0';
-                                       expand(p, buf, &buf[sizeof buf] - 1, e);
+                                       expand(p, buf, sizeof buf, e);
                                        if (q != NULL)
                                                *q++ = ':';
                                        if (tTd(11, 20))
                                        if (q != NULL)
                                                *q++ = ':';
                                        if (tTd(11, 20))
@@ -1231,7 +1327,8 @@ tryhost:
                                }
                                (void) close(rpvect[1]);
                        }
                                }
                                (void) close(rpvect[1]);
                        }
-                       else if (OpMode == MD_SMTP || HoldErrs)
+                       else if (OpMode == MD_SMTP || OpMode == MD_DAEMON ||
+                                 HoldErrs || DisConnected)
                        {
                                /* put mailer output in transcript */
                                if (dup2(fileno(e->e_xfp), STDOUT_FILENO) < 0)
                        {
                                /* put mailer output in transcript */
                                if (dup2(fileno(e->e_xfp), STDOUT_FILENO) < 0)
@@ -1268,21 +1365,35 @@ tryhost:
                                        (void) fcntl(i, F_SETFD, j | 1);
                        }
 
                                        (void) fcntl(i, F_SETFD, j | 1);
                        }
 
-                       /* set up the mailer environment */
+                       /*
+                       **  Set up the mailer environment
+                       **      _FORCE_MAIL_LOCAL_ is DG-UX equiv of -d flag.
+                       **      TZ is timezone information.
+                       **      SYSTYPE is Apollo software sys type (required).
+                       **      ISP is Apollo hardware system type (required).
+                       */
+
                        i = 0;
                        env[i++] = "AGENT=sendmail";
                        i = 0;
                        env[i++] = "AGENT=sendmail";
+                       env[i++] = "_FORCE_MAIL_LOCAL_=yes";
                        for (ep = environ; *ep != NULL; ep++)
                        {
                        for (ep = environ; *ep != NULL; ep++)
                        {
-                               if (strncmp(*ep, "TZ=", 3) == 0)
+                               if (strncmp(*ep, "TZ=", 3) == 0 ||
+                                   strncmp(*ep, "ISP=", 4) == 0 ||
+                                   strncmp(*ep, "SYSTYPE=", 8) == 0)
                                        env[i++] = *ep;
                        }
                                        env[i++] = *ep;
                        }
-                       env[i++] = NULL;
+                       env[i] = NULL;
+
+                       /* run disconnected from terminal */
+                       (void) setsid();
 
                        /* try to execute the mailer */
                        execve(m->m_mailer, pv, env);
                        saveerrno = errno;
                        syserr("Cannot exec %s", m->m_mailer);
 
                        /* try to execute the mailer */
                        execve(m->m_mailer, pv, env);
                        saveerrno = errno;
                        syserr("Cannot exec %s", m->m_mailer);
-                       if (m == LocalMailer || transienterror(saveerrno))
+                       if (bitnset(M_LOCALMAILER, m->m_flags) ||
+                           transienterror(saveerrno))
                                _exit(EX_OSERR);
                        _exit(EX_UNAVAILABLE);
                }
                                _exit(EX_OSERR);
                        _exit(EX_UNAVAILABLE);
                }
@@ -1341,6 +1452,12 @@ tryhost:
        {
                smtpinit(m, mci, e);
        }
        {
                smtpinit(m, mci, e);
        }
+
+       if (bitset(EF_HAS8BIT, e->e_flags) && bitnset(M_7BITS, m->m_flags))
+               mci->mci_flags |= MCIF_CVT8TO7;
+       else
+               mci->mci_flags &= ~MCIF_CVT8TO7;
+
        if (tTd(11, 1))
        {
                printf("openmailer: ");
        if (tTd(11, 1))
        {
                printf("openmailer: ");
@@ -1352,7 +1469,7 @@ tryhost:
                /* couldn't open the mailer */
                rcode = mci->mci_exitstat;
                errno = mci->mci_errno;
                /* couldn't open the mailer */
                rcode = mci->mci_exitstat;
                errno = mci->mci_errno;
-#ifdef NAMED_BIND
+#if NAMED_BIND
                h_errno = mci->mci_herrno;
 #endif
                if (rcode == EX_OK)
                h_errno = mci->mci_herrno;
 #endif
                if (rcode == EX_OK)
@@ -1362,7 +1479,7 @@ tryhost:
                                rcode, mci->mci_state, firstsig);
                        rcode = EX_SOFTWARE;
                }
                                rcode, mci->mci_state, firstsig);
                        rcode = EX_SOFTWARE;
                }
-               else if (rcode == EX_TEMPFAIL && curhost != NULL && *curhost != '\0')
+               else if (curhost != NULL && *curhost != '\0')
                {
                        /* try next MX site */
                        goto tryhost;
                {
                        /* try next MX site */
                        goto tryhost;
@@ -1374,10 +1491,9 @@ tryhost:
                **  Format and send message.
                */
 
                **  Format and send message.
                */
 
-               putfromline(mci->mci_out, m, e);
-               (*e->e_puthdr)(mci->mci_out, m, e);
-               putline("\n", mci->mci_out, m);
-               (*e->e_putbody)(mci->mci_out, m, e, NULL);
+               putfromline(mci, e);
+               (*e->e_puthdr)(mci, e->e_header, e);
+               (*e->e_putbody)(mci, e, NULL);
 
                /* get the exit status */
                rcode = endmailer(mci, e, pv);
 
                /* get the exit status */
                rcode = endmailer(mci, e, pv);
@@ -1402,8 +1518,8 @@ tryhost:
                                e->e_to = to->q_paddr;
                                if ((i = smtprcpt(to, m, mci, e)) != EX_OK)
                                {
                                e->e_to = to->q_paddr;
                                if ((i = smtprcpt(to, m, mci, e)) != EX_OK)
                                {
-                                       markfailure(e, to, i);
-                                       giveresponse(i, m, mci, ctladdr, e);
+                                       markfailure(e, to, mci, i);
+                                       giveresponse(i, m, mci, ctladdr, xstart, e);
                                }
                                else
                                {
                                }
                                else
                                {
@@ -1445,7 +1561,7 @@ tryhost:
                goto give_up;
        }
 #endif /* SMTP */
                goto give_up;
        }
 #endif /* SMTP */
-#ifdef NAMED_BIND
+#if NAMED_BIND
        if (ConfigLevel < 2)
                _res.options |= RES_DEFNAMES | RES_DNSRCH;      /* XXX */
 #endif
        if (ConfigLevel < 2)
                _res.options |= RES_DEFNAMES | RES_DNSRCH;      /* XXX */
 #endif
@@ -1467,21 +1583,32 @@ tryhost:
 
   give_up:
        if (tobuf[0] != '\0')
 
   give_up:
        if (tobuf[0] != '\0')
-               giveresponse(rcode, m, mci, ctladdr, e);
+               giveresponse(rcode, m, mci, ctladdr, xstart, e);
        for (to = tochain; to != NULL; to = to->q_tchain)
        {
                if (rcode != EX_OK)
        for (to = tochain; to != NULL; to = to->q_tchain)
        {
                if (rcode != EX_OK)
-                       markfailure(e, to, rcode);
-               else
+                       markfailure(e, to, mci, rcode);
+               else if (!bitset(QBADADDR|QQUEUEUP, to->q_flags))
                {
                        to->q_flags |= QSENT;
                {
                        to->q_flags |= QSENT;
+                       to->q_statdate = curtime();
                        e->e_nsent++;
                        e->e_nsent++;
-                       if (e->e_receiptto != NULL &&
-                           bitnset(M_LOCALMAILER, m->m_flags))
+                       if (bitnset(M_LOCALMAILER, m->m_flags) &&
+                           (e->e_receiptto != NULL ||
+                            bitset(QPINGONSUCCESS, to->q_flags)))
                        {
                        {
+                               to->q_flags |= QREPORT;
                                fprintf(e->e_xfp, "%s... Successfully delivered\n",
                                        to->q_paddr);
                        }
                                fprintf(e->e_xfp, "%s... Successfully delivered\n",
                                        to->q_paddr);
                        }
+                       else if (bitset(QPINGONSUCCESS, to->q_flags) &&
+                                bitset(QPRIMARY, to->q_flags) &&
+                                !bitset(MCIF_DSN, mci->mci_flags))
+                       {
+                               to->q_flags |= QRELAYED;
+                               fprintf(e->e_xfp, "%s... relayed; expect no further notifications\n",
+                                       to->q_paddr);
+                       }
                }
        }
 
                }
        }
 
@@ -1511,6 +1638,7 @@ tryhost:
 **     Parameters:
 **             e -- the envelope we are sending.
 **             q -- the address to mark.
 **     Parameters:
 **             e -- the envelope we are sending.
 **             q -- the address to mark.
+**             mci -- mailer connection information.
 **             rcode -- the code signifying the particular failure.
 **
 **     Returns:
 **             rcode -- the code signifying the particular failure.
 **
 **     Returns:
@@ -1522,12 +1650,13 @@ tryhost:
 **                     the message will be queued, as appropriate.
 */
 
 **                     the message will be queued, as appropriate.
 */
 
-markfailure(e, q, rcode)
+markfailure(e, q, mci, rcode)
        register ENVELOPE *e;
        register ADDRESS *q;
        register ENVELOPE *e;
        register ADDRESS *q;
+       register MCI *mci;
        int rcode;
 {
        int rcode;
 {
-       char buf[MAXLINE];
+       char *stat = NULL;
 
        switch (rcode)
        {
 
        switch (rcode)
        {
@@ -1544,6 +1673,60 @@ markfailure(e, q, rcode)
                q->q_flags |= QBADADDR;
                break;
        }
                q->q_flags |= QBADADDR;
                break;
        }
+
+       if (q->q_status == NULL && mci != NULL)
+               q->q_status = mci->mci_status;
+       switch (rcode)
+       {
+         case EX_USAGE:
+               stat = "5.5.4";
+               break;
+
+         case EX_DATAERR:
+               stat = "5.5.2";
+               break;
+
+         case EX_NOUSER:
+         case EX_NOHOST:
+               stat = "5.1.1";
+               break;
+
+         case EX_NOINPUT:
+         case EX_CANTCREAT:
+         case EX_NOPERM:
+               stat = "5.3.0";
+               break;
+
+         case EX_UNAVAILABLE:
+         case EX_SOFTWARE:
+         case EX_OSFILE:
+         case EX_PROTOCOL:
+         case EX_CONFIG:
+               stat = "5.5.0";
+               break;
+
+         case EX_OSERR:
+         case EX_IOERR:
+               stat = "4.5.0";
+               break;
+
+         case EX_TEMPFAIL:
+               stat = "4.2.0";
+               break;
+       }
+       if (stat != NULL && q->q_status == NULL)
+               q->q_status = stat;
+
+       q->q_statdate = curtime();
+       if (CurHostName != NULL && CurHostName[0] != '\0')
+               q->q_statmta = newstr(CurHostName);
+       if (rcode != EX_OK && q->q_rstatus == NULL)
+       {
+               char buf[30];
+
+               (void) sprintf(buf, "%d", rcode);
+               q->q_rstatus = newstr(buf);
+       }
 }
 \f/*
 **  ENDMAILER -- Wait for mailer to terminate.
 }
 \f/*
 **  ENDMAILER -- Wait for mailer to terminate.
@@ -1600,7 +1783,8 @@ endmailer(mci, e, pv)
        }
 
        /* it died a horrid death */
        }
 
        /* it died a horrid death */
-       syserr("mailer %s died with signal %o", mci->mci_mailer->m_name, st);
+       syserr("451 mailer %s died with signal %o",
+               mci->mci_mailer->m_name, st);
 
        /* log the arguments */
        if (pv != NULL && e->e_xfp != NULL)
 
        /* log the arguments */
        if (pv != NULL && e->e_xfp != NULL)
@@ -1628,6 +1812,8 @@ endmailer(mci, e, pv)
 **                     response is given before the connection is made.
 **             ctladdr -- the controlling address for the recipient
 **                     address(es).
 **                     response is given before the connection is made.
 **             ctladdr -- the controlling address for the recipient
 **                     address(es).
+**             xstart -- the transaction start time, for computing
+**                     transaction delays.
 **             e -- the current envelope.
 **
 **     Returns:
 **             e -- the current envelope.
 **
 **     Returns:
@@ -1638,11 +1824,12 @@ endmailer(mci, e, pv)
 **             ExitStat may be set.
 */
 
 **             ExitStat may be set.
 */
 
-giveresponse(stat, m, mci, ctladdr, e)
+giveresponse(stat, m, mci, ctladdr, xstart, e)
        int stat;
        register MAILER *m;
        register MCI *mci;
        ADDRESS *ctladdr;
        int stat;
        register MAILER *m;
        register MCI *mci;
        ADDRESS *ctladdr;
+       time_t xstart;
        ENVELOPE *e;
 {
        register const char *statmsg;
        ENVELOPE *e;
 {
        register const char *statmsg;
@@ -1674,7 +1861,7 @@ giveresponse(stat, m, mci, ctladdr, e)
        else if (stat == EX_TEMPFAIL)
        {
                (void) strcpy(buf, SysExMsg[i] + 1);
        else if (stat == EX_TEMPFAIL)
        {
                (void) strcpy(buf, SysExMsg[i] + 1);
-#ifdef NAMED_BIND
+#if NAMED_BIND
                if (h_errno == TRY_AGAIN)
                        statmsg = errstring(h_errno+E_DNSBASE);
                else
                if (h_errno == TRY_AGAIN)
                        statmsg = errstring(h_errno+E_DNSBASE);
                else
@@ -1698,11 +1885,11 @@ giveresponse(stat, m, mci, ctladdr, e)
                }
                statmsg = buf;
        }
                }
                statmsg = buf;
        }
-#ifdef NAMED_BIND
+#if NAMED_BIND
        else if (stat == EX_NOHOST && h_errno != 0)
        {
                statmsg = errstring(h_errno + E_DNSBASE);
        else if (stat == EX_NOHOST && h_errno != 0)
        {
                statmsg = errstring(h_errno + E_DNSBASE);
-               (void) sprintf(buf, "%s (%s)", SysExMsg[i], statmsg);
+               (void) sprintf(buf, "%s (%s)", SysExMsg[i] + 1, statmsg);
                statmsg = buf;
        }
 #endif
                statmsg = buf;
        }
 #endif
@@ -1724,14 +1911,17 @@ giveresponse(stat, m, mci, ctladdr, e)
        {
                extern char MsgBuf[];
 
        {
                extern char MsgBuf[];
 
-               message(&statmsg[4], errstring(errno));
+               message("%s", &statmsg[4]);
                if (stat == EX_TEMPFAIL && e->e_xfp != NULL)
                        fprintf(e->e_xfp, "%s\n", &MsgBuf[4]);
        }
        else
        {
                if (stat == EX_TEMPFAIL && e->e_xfp != NULL)
                        fprintf(e->e_xfp, "%s\n", &MsgBuf[4]);
        }
        else
        {
+               char mbuf[8];
+
                Errors++;
                Errors++;
-               usrerr(statmsg, errstring(errno));
+               sprintf(mbuf, "%.3s %%s", statmsg);
+               usrerr(mbuf, &statmsg[4]);
        }
 
        /*
        }
 
        /*
@@ -1742,7 +1932,11 @@ giveresponse(stat, m, mci, ctladdr, e)
        */
 
        if (LogLevel > ((stat == EX_TEMPFAIL) ? 8 : (stat == EX_OK) ? 7 : 6))
        */
 
        if (LogLevel > ((stat == EX_TEMPFAIL) ? 8 : (stat == EX_OK) ? 7 : 6))
-               logdelivery(m, mci, &statmsg[4], ctladdr, e);
+               logdelivery(m, mci, &statmsg[4], ctladdr, xstart, e);
+
+       if (tTd(11, 2))
+               printf("giveresponse: stat=%d, e->e_message=%s\n",
+                       stat, e->e_message == NULL ? "<NULL>" : e->e_message);
 
        if (stat != EX_TEMPFAIL)
                setstat(stat);
 
        if (stat != EX_TEMPFAIL)
                setstat(stat);
@@ -1753,7 +1947,7 @@ giveresponse(stat, m, mci, ctladdr, e)
                e->e_message = newstr(&statmsg[4]);
        }
        errno = 0;
                e->e_message = newstr(&statmsg[4]);
        }
        errno = 0;
-#ifdef NAMED_BIND
+#if NAMED_BIND
        h_errno = 0;
 #endif
 }
        h_errno = 0;
 #endif
 }
@@ -1770,6 +1964,8 @@ giveresponse(stat, m, mci, ctladdr, e)
 **                     log is occuring when no connection is active.
 **             stat -- the message to print for the status.
 **             ctladdr -- the controlling address for the to list.
 **                     log is occuring when no connection is active.
 **             stat -- the message to print for the status.
 **             ctladdr -- the controlling address for the to list.
+**             xstart -- the transaction start time, used for
+**                     computing transaction delay.
 **             e -- the current envelope.
 **
 **     Returns:
 **             e -- the current envelope.
 **
 **     Returns:
@@ -1779,11 +1975,12 @@ giveresponse(stat, m, mci, ctladdr, e)
 **             none
 */
 
 **             none
 */
 
-logdelivery(m, mci, stat, ctladdr, e)
+logdelivery(m, mci, stat, ctladdr, xstart, e)
        MAILER *m;
        register MCI *mci;
        char *stat;
        ADDRESS *ctladdr;
        MAILER *m;
        register MCI *mci;
        char *stat;
        ADDRESS *ctladdr;
+       time_t xstart;
        register ENVELOPE *e;
 {
 # ifdef LOG
        register ENVELOPE *e;
 {
 # ifdef LOG
@@ -1807,9 +2004,15 @@ logdelivery(m, mci, stat, ctladdr, e)
                }
        }
 
                }
        }
 
-       (void) sprintf(bp, ", delay=%s", pintvl(curtime() - e->e_ctime, TRUE));
+       sprintf(bp, ", delay=%s", pintvl(curtime() - e->e_ctime, TRUE));
        bp += strlen(bp);
 
        bp += strlen(bp);
 
+       if (xstart != (time_t) 0)
+       {
+               sprintf(bp, ", xdelay=%s", pintvl(curtime() - xstart, TRUE));
+               bp += strlen(bp);
+       }
+
        if (m != NULL)
        {
                (void) strcpy(bp, ", mailer=");
        if (m != NULL)
        {
                (void) strcpy(bp, ", mailer=");
@@ -1827,12 +2030,12 @@ logdelivery(m, mci, stat, ctladdr, e)
                (void) strcat(bp, mci->mci_host);
 
 # ifdef DAEMON
                (void) strcat(bp, mci->mci_host);
 
 # ifdef DAEMON
-               (void) strcat(bp, " (");
+               (void) strcat(bp, " [");
                (void) strcat(bp, anynet_ntoa(&CurHostAddr));
                (void) strcat(bp, anynet_ntoa(&CurHostAddr));
-               (void) strcat(bp, ")");
+               (void) strcat(bp, "]");
 # endif
        }
 # endif
        }
-       else
+       else if (strcmp(stat, "queued") != 0)
        {
                char *p = macvalue('h', e);
 
        {
                char *p = macvalue('h', e);
 
@@ -1883,7 +2086,7 @@ logdelivery(m, mci, stat, ctladdr, e)
 
 #  else                /* we have a very short log buffer size */
 
 
 #  else                /* we have a very short log buffer size */
 
-       l = SYSLOG_BUFSIZE - 40;
+       l = SYSLOG_BUFSIZE - 85;
        p = e->e_to;
        while (strlen(p) >= l)
        {
        p = e->e_to;
        while (strlen(p) >= l)
        {
@@ -1912,34 +2115,46 @@ logdelivery(m, mci, stat, ctladdr, e)
                }
                syslog(LOG_INFO, "%s: %s", e->e_id, buf);
        }
                }
                syslog(LOG_INFO, "%s: %s", e->e_id, buf);
        }
-       syslog(LOG_INFO, "%s: delay=%s",
-               e->e_id, pintvl(curtime() - e->e_ctime, TRUE));
+       bp = buf;
+       sprintf(bp, "delay=%s", pintvl(curtime() - e->e_ctime, TRUE));
+       bp += strlen(bp);
+       if (xstart != (time_t) 0)
+       {
+               sprintf(bp, ", xdelay=%s", pintvl(curtime() - xstart, TRUE));
+               bp += strlen(bp);
+       }
 
        if (m != NULL)
 
        if (m != NULL)
-               syslog(LOG_INFO, "%s: mailer=%s", e->e_id, m->m_name);
+       {
+               sprintf(bp, ", mailer=%s", m->m_name);
+               bp += strlen(bp);
+       }
+       syslog(LOG_INFO, "%s: %s", e->e_id, buf);
 
 
+       buf[0] = '\0';
        if (mci != NULL && mci->mci_host != NULL)
        {
 # ifdef DAEMON
                extern SOCKADDR CurHostAddr;
 # endif
 
        if (mci != NULL && mci->mci_host != NULL)
        {
 # ifdef DAEMON
                extern SOCKADDR CurHostAddr;
 # endif
 
-               (void) strcpy(buf, mci->mci_host);
+               sprintf(buf, "relay=%s", mci->mci_host);
 
 # ifdef DAEMON
 
 # ifdef DAEMON
-               (void) strcat(buf, " (");
+               (void) strcat(buf, " [");
                (void) strcat(buf, anynet_ntoa(&CurHostAddr));
                (void) strcat(buf, anynet_ntoa(&CurHostAddr));
-               (void) strcat(buf, ")");
+               (void) strcat(buf, "]");
 # endif
 # endif
-               syslog(LOG_INFO, "%s: relay=%s", e->e_id, buf);
        }
        }
-       else
+       else if (strcmp(stat, "queued") != 0)
        {
                char *p = macvalue('h', e);
 
                if (p != NULL && p[0] != '\0')
        {
                char *p = macvalue('h', e);
 
                if (p != NULL && p[0] != '\0')
-                       syslog(LOG_INFO, "%s: relay=%s", e->e_id, p);
+                       sprintf(buf, "relay=%s", p);
        }
        }
+       if (buf[0] != '\0')
+               syslog(LOG_INFO, "%s: %s", e->e_id, buf);
 
        syslog(LOG_INFO, "%s: stat=%s", e->e_id, shortenstring(stat, 63));
 #  endif /* short log buffer */
 
        syslog(LOG_INFO, "%s: stat=%s", e->e_id, shortenstring(stat, 63));
 #  endif /* short log buffer */
@@ -1958,8 +2173,8 @@ logdelivery(m, mci, stat, ctladdr, e)
 **     this kind of antique garbage????
 **
 **     Parameters:
 **     this kind of antique garbage????
 **
 **     Parameters:
-**             fp -- the file to output to.
-**             m -- the mailer describing this entry.
+**             mci -- the connection information.
+**             e -- the envelope.
 **
 **     Returns:
 **             none
 **
 **     Returns:
 **             none
@@ -1968,25 +2183,24 @@ logdelivery(m, mci, stat, ctladdr, e)
 **             outputs some text to fp.
 */
 
 **             outputs some text to fp.
 */
 
-putfromline(fp, m, e)
-       register FILE *fp;
-       register MAILER *m;
+putfromline(mci, e)
+       register MCI *mci;
        ENVELOPE *e;
 {
        char *template = "\201l\n";
        char buf[MAXLINE];
        extern char SentDate[];
 
        ENVELOPE *e;
 {
        char *template = "\201l\n";
        char buf[MAXLINE];
        extern char SentDate[];
 
-       if (bitnset(M_NHDR, m->m_flags))
+       if (bitnset(M_NHDR, mci->mci_mailer->m_flags))
                return;
 
 # ifdef UGLYUUCP
                return;
 
 # ifdef UGLYUUCP
-       if (bitnset(M_UGLYUUCP, m->m_flags))
+       if (bitnset(M_UGLYUUCP, mci->mci_mailer->m_flags))
        {
                char *bang;
                char xbuf[MAXLINE];
 
        {
                char *bang;
                char xbuf[MAXLINE];
 
-               expand("\201g", buf, &buf[sizeof buf - 1], e);
+               expand("\201g", buf, sizeof buf, e);
                bang = strchr(buf, '!');
                if (bang == NULL)
                {
                bang = strchr(buf, '!');
                if (bang == NULL)
                {
@@ -2001,15 +2215,14 @@ putfromline(fp, m, e)
                }
        }
 # endif /* UGLYUUCP */
                }
        }
 # endif /* UGLYUUCP */
-       expand(template, buf, &buf[sizeof buf - 1], e);
-       putline(buf, fp, m);
+       expand(template, buf, sizeof buf, e);
+       putxline(buf, mci, FALSE);
 }
 \f/*
 **  PUTBODY -- put the body of a message.
 **
 **     Parameters:
 }
 \f/*
 **  PUTBODY -- put the body of a message.
 **
 **     Parameters:
-**             fp -- file to output onto.
-**             m -- a mailer descriptor to control output format.
+**             mci -- the connection information.
 **             e -- the envelope to put out.
 **             separator -- if non-NULL, a message separator that must
 **                     not be permitted in the resulting message.
 **             e -- the envelope to put out.
 **             separator -- if non-NULL, a message separator that must
 **                     not be permitted in the resulting message.
@@ -2021,9 +2234,13 @@ putfromline(fp, m, e)
 **             The message is written onto fp.
 */
 
 **             The message is written onto fp.
 */
 
-putbody(fp, m, e, separator)
-       FILE *fp;
-       MAILER *m;
+/* values for output state variable */
+#define OS_HEAD                0       /* at beginning of line */
+#define OS_CR          1       /* read a carriage return */
+#define OS_INLINE      2       /* putting rest of line */
+
+putbody(mci, e, separator)
+       register MCI *mci;
        register ENVELOPE *e;
        char *separator;
 {
        register ENVELOPE *e;
        char *separator;
 {
@@ -2033,50 +2250,241 @@ putbody(fp, m, e, separator)
        **  Output the body of the message
        */
 
        **  Output the body of the message
        */
 
+       if (e->e_dfp == NULL && bitset(EF_HAS_DF, e->e_flags))
+       {
+               char *df = queuename(e, 'd');
+
+               e->e_dfp = fopen(df, "r");
+               if (e->e_dfp == NULL)
+                       syserr("putbody: Cannot open %s for %s from %s",
+                               df, e->e_to, e->e_from.q_paddr);
+       }
        if (e->e_dfp == NULL)
        {
        if (e->e_dfp == NULL)
        {
-               if (e->e_df != NULL)
+               if (bitset(MCIF_INHEADER, mci->mci_flags))
                {
                {
-                       e->e_dfp = fopen(e->e_df, "r");
-                       if (e->e_dfp == NULL)
-                               syserr("putbody: Cannot open %s for %s from %s",
-                               e->e_df, e->e_to, e->e_from.q_paddr);
+                       putline("", mci);
+                       mci->mci_flags &= ~MCIF_INHEADER;
                }
                }
+               putline("<<< No Message Collected >>>", mci);
+               goto endofmessage;
+       }
+       if (e->e_dfino == (ino_t) 0)
+       {
+               struct stat stbuf;
+
+               if (fstat(fileno(e->e_dfp), &stbuf) < 0)
+                       e->e_dfino = -1;
                else
                else
-                       putline("<<< No Message Collected >>>", fp, m);
+               {
+                       e->e_dfdev = stbuf.st_dev;
+                       e->e_dfino = stbuf.st_ino;
+               }
        }
        }
-       if (e->e_dfp != NULL)
+       rewind(e->e_dfp);
+
+       if (bitset(MCIF_CVT8TO7, mci->mci_flags))
        {
        {
-               rewind(e->e_dfp);
-               while (!ferror(fp) && fgets(buf, sizeof buf, e->e_dfp) != NULL)
+               char *boundaries[MAXMIMENESTING + 1];
+
+               /*
+               **  Do 8 to 7 bit MIME conversion.
+               */
+
+               /* make sure it looks like a MIME message */
+               if (hvalue("MIME-Version", e->e_header) == NULL)
+                       putline("MIME-Version: 1.0", mci);
+
+               if (hvalue("Content-Type", e->e_header) == NULL)
                {
                {
-                       if (buf[0] == 'F' && bitnset(M_ESCFROM, m->m_flags) &&
-                           strncmp(buf, "From ", 5) == 0)
-                               (void) putc('>', fp);
-                       if (buf[0] == '-' && buf[1] == '-' && separator != NULL)
-                       {
-                               /* possible separator */
-                               int sl = strlen(separator);
+                       sprintf(buf, "Content-Type: text/plain; charset=%s",
+                               defcharset(e));
+                       putline(buf, mci);
+               }
 
 
-                               if (strncmp(&buf[2], separator, sl) == 0)
-                                       (void) putc(' ', fp);
-                       }
-                       putline(buf, fp, m);
+               /* now do the hard work */
+               boundaries[0] = NULL;
+               mime8to7(mci, e->e_header, e, boundaries, M87F_OUTER);
+       }
+       else
+       {
+               int ostate;
+               register char *bp;
+               register char *pbp;
+               register int c;
+               int padc;
+               char *buflim;
+               int pos = 0;
+               char peekbuf[10];
+
+               /* we can pass it through unmodified */
+               if (bitset(MCIF_INHEADER, mci->mci_flags))
+               {
+                       putline("", mci);
+                       mci->mci_flags &= ~MCIF_INHEADER;
                }
 
                }
 
-               if (ferror(e->e_dfp))
+               /* determine end of buffer; allow for short mailer lines */
+               buflim = &buf[sizeof buf - 1];
+               if (mci->mci_mailer->m_linelimit > 0 &&
+                   mci->mci_mailer->m_linelimit < sizeof buf - 1)
+                       buflim = &buf[mci->mci_mailer->m_linelimit - 1];
+
+               /* copy temp file to output with mapping */
+               ostate = OS_HEAD;
+               bp = buf;
+               pbp = peekbuf;
+               while (!ferror(mci->mci_out))
                {
                {
-                       syserr("putbody: %s: read error", e->e_df);
-                       ExitStat = EX_IOERR;
+                       register char *xp;
+
+                       if (pbp > peekbuf)
+                               c = *--pbp;
+                       else if ((c = fgetc(e->e_dfp)) == EOF)
+                               break;
+                       if (bitset(MCIF_7BIT, mci->mci_flags))
+                               c &= 0x7f;
+                       switch (ostate)
+                       {
+                         case OS_HEAD:
+                               if (c != '\r' && c != '\n' && bp < buflim)
+                               {
+                                       *bp++ = c;
+                                       break;
+                               }
+
+                               /* check beginning of line for special cases */
+                               *bp = '\0';
+                               pos = 0;
+                               padc = EOF;
+                               if (buf[0] == 'F' &&
+                                   bitnset(M_ESCFROM, mci->mci_mailer->m_flags) &&
+                                   strncmp(buf, "From ", 5) == 0)
+                               {
+                                       padc = '>';
+                               }
+                               if (buf[0] == '-' && buf[1] == '-' &&
+                                   separator != NULL)
+                               {
+                                       /* possible separator */
+                                       int sl = strlen(separator);
+
+                                       if (strncmp(&buf[2], separator, sl) == 0)
+                                               padc = ' ';
+                               }
+                               if (buf[0] == '.' &&
+                                   bitnset(M_XDOT, mci->mci_mailer->m_flags))
+                               {
+                                       padc = '.';
+                               }
+
+                               /* now copy out saved line */
+                               if (TrafficLogFile != NULL)
+                               {
+                                       fprintf(TrafficLogFile, "%05d >>> ", getpid());
+                                       if (padc != EOF)
+                                               fputc(padc, TrafficLogFile);
+                                       for (xp = buf; xp < bp; xp++)
+                                               fputc(*xp, TrafficLogFile);
+                                       if (c == '\n')
+                                               fputs(mci->mci_mailer->m_eol,
+                                                     TrafficLogFile);
+                               }
+                               if (padc != EOF)
+                               {
+                                       fputc(padc, mci->mci_out);
+                                       pos++;
+                               }
+                               for (xp = buf; xp < bp; xp++)
+                                       fputc(*xp, mci->mci_out);
+                               if (c == '\n')
+                               {
+                                       fputs(mci->mci_mailer->m_eol,
+                                             mci->mci_out);
+                                       pos = 0;
+                               }
+                               else
+                               {
+                                       pos += bp - buf;
+                                       if (c != '\r')
+                                               *pbp++ = c;
+                               }
+                               bp = buf;
+
+                               /* determine next state */
+                               if (c == '\n')
+                                       ostate = OS_HEAD;
+                               else if (c == '\r')
+                                       ostate = OS_CR;
+                               else
+                                       ostate = OS_INLINE;
+                               continue;
+
+                         case OS_CR:
+                               if (c == '\n')
+                               {
+                                       /* got CRLF */
+                                       fputs(mci->mci_mailer->m_eol, mci->mci_out);
+                                       if (TrafficLogFile != NULL)
+                                       {
+                                               fputs(mci->mci_mailer->m_eol,
+                                                     TrafficLogFile);
+                                       }
+                                       ostate = OS_HEAD;
+                                       continue;
+                               }
+
+                               /* had a naked carriage return */
+                               *pbp++ = c;
+                               c = '\r';
+                               goto putch;
+
+                         case OS_INLINE:
+                               if (c == '\r')
+                               {
+                                       ostate = OS_CR;
+                                       continue;
+                               }
+putch:
+                               if (mci->mci_mailer->m_linelimit > 0 &&
+                                   pos > mci->mci_mailer->m_linelimit &&
+                                   c != '\n')
+                               {
+                                       putc('!', mci->mci_out);
+                                       fputs(mci->mci_mailer->m_eol, mci->mci_out);
+                                       if (TrafficLogFile != NULL)
+                                       {
+                                               fprintf(TrafficLogFile, "!%s",
+                                                       mci->mci_mailer->m_eol);
+                                       }
+                                       ostate = OS_HEAD;
+                                       *pbp++ = c;
+                                       continue;
+                               }
+                               if (TrafficLogFile != NULL)
+                                       fputc(c, TrafficLogFile);
+                               putc(c, mci->mci_out);
+                               pos++;
+                               ostate = c == '\n' ? OS_HEAD : OS_INLINE;
+                               break;
+                       }
                }
        }
 
                }
        }
 
+       if (ferror(e->e_dfp))
+       {
+               syserr("putbody: df%s: read error", e->e_id);
+               ExitStat = EX_IOERR;
+       }
+
+endofmessage:
        /* some mailers want extra blank line at end of message */
        /* some mailers want extra blank line at end of message */
-       if (bitnset(M_BLANKEND, m->m_flags) && buf[0] != '\0' && buf[0] != '\n')
-               putline("", fp, m);
+       if (bitnset(M_BLANKEND, mci->mci_mailer->m_flags) &&
+           buf[0] != '\0' && buf[0] != '\n')
+               putline("", mci);
 
 
-       (void) fflush(fp);
-       if (ferror(fp) && errno != EPIPE)
+       (void) fflush(mci->mci_out);
+       if (ferror(mci->mci_out) && errno != EPIPE)
        {
                syserr("putbody: write error");
                ExitStat = EX_IOERR;
        {
                syserr("putbody: write error");
                ExitStat = EX_IOERR;
@@ -2115,7 +2523,7 @@ mailfile(filename, ctladdr, e)
        register ENVELOPE *e;
 {
        register FILE *f;
        register ENVELOPE *e;
 {
        register FILE *f;
-       register int pid;
+       register int pid = -1;
        int mode;
 
        if (tTd(11, 1))
        int mode;
 
        if (tTd(11, 1))
@@ -2141,22 +2549,42 @@ mailfile(filename, ctladdr, e)
        {
                /* child -- actually write to file */
                struct stat stb;
        {
                /* child -- actually write to file */
                struct stat stb;
+               struct stat fsb;
+               MCI mcibuf;
+               int oflags = O_WRONLY|O_APPEND;
+               int oflags = O_WRONLY|O_APPEND;
+
+               if (e->e_lockfp != NULL)
+               {
+                       fclose(e->e_lockfp);
+                       e->e_lockfp = NULL;
+               }
 
                (void) setsignal(SIGINT, SIG_DFL);
                (void) setsignal(SIGHUP, SIG_DFL);
                (void) setsignal(SIGTERM, SIG_DFL);
                (void) umask(OldUmask);
 
 
                (void) setsignal(SIGINT, SIG_DFL);
                (void) setsignal(SIGHUP, SIG_DFL);
                (void) setsignal(SIGTERM, SIG_DFL);
                (void) umask(OldUmask);
 
+#ifdef HASLSTAT
+               if ((SafeFileEnv != NULL ? lstat(filename, &stb)
+                                        : stat(filename, &stb)) < 0)
+#else
                if (stat(filename, &stb) < 0)
                if (stat(filename, &stb) < 0)
+               {
+#endif
+               {
                        stb.st_mode = FileMode;
                        stb.st_mode = FileMode;
+                       oflags |= O_CREAT|O_EXCL;
+               }
+               else if (bitset(0111, stb.st_mode) || stb.st_nlink != 1 ||
+                        (SafeFileEnv != NULL && !S_ISREG(stb.st_mode)))
+                       exit(EX_CANTCREAT);
                mode = stb.st_mode;
 
                /* limit the errors to those actually caused in the child */
                errno = 0;
                ExitStat = EX_OK;
 
                mode = stb.st_mode;
 
                /* limit the errors to those actually caused in the child */
                errno = 0;
                ExitStat = EX_OK;
 
-               if (bitset(0111, stb.st_mode))
-                       exit(EX_CANTCREAT);
                if (ctladdr != NULL)
                {
                        /* ignore setuid and setgid bits */
                if (ctladdr != NULL)
                {
                        /* ignore setuid and setgid bits */
@@ -2164,50 +2592,84 @@ mailfile(filename, ctladdr, e)
                }
 
                /* we have to open the dfile BEFORE setuid */
                }
 
                /* we have to open the dfile BEFORE setuid */
-               if (e->e_dfp == NULL && e->e_df != NULL)
+               if (e->e_dfp == NULL && bitset(EF_HAS_DF, e->e_flags))
                {
                {
-                       e->e_dfp = fopen(e->e_df, "r");
+                       char *df = queuename(e, 'd');
+
+                       e->e_dfp = fopen(df, "r");
                        if (e->e_dfp == NULL)
                        {
                                syserr("mailfile: Cannot open %s for %s from %s",
                        if (e->e_dfp == NULL)
                        {
                                syserr("mailfile: Cannot open %s for %s from %s",
-                                       e->e_df, e->e_to, e->e_from.q_paddr);
+                                       df, e->e_to, e->e_from.q_paddr);
                        }
                }
 
                        }
                }
 
-               if (!bitset(S_ISGID, mode) || setgid(stb.st_gid) < 0)
+               if (SafeFileEnv != NULL && SafeFileEnv[0] != '\0')
                {
                {
-                       if (ctladdr == NULL || ctladdr->q_uid == 0)
+                       int i;
+
+                       if (chroot(SafeFileEnv) < 0)
                        {
                        {
-                               (void) initgroups(DefUser, DefGid);
+                               syserr("mailfile: Cannot chroot(%s)",
+                                       SafeFileEnv);
+                               exit(EX_CANTCREAT);
                        }
                        }
-                       else
-                       {
+                       i = strlen(SafeFileEnv);
+                       if (strncmp(SafeFileEnv, filename, i) == 0)
+                               filename += i;
+               }
+               if (chdir("/") < 0)
+                       syserr("mailfile: cannot chdir(/)");
+
+               if (!bitset(S_ISGID, mode) || setgid(stb.st_gid) < 0)
+               {
+                       if (ctladdr != NULL && ctladdr->q_uid != 0)
                                (void) initgroups(ctladdr->q_ruser ?
                                        ctladdr->q_ruser : ctladdr->q_user,
                                        ctladdr->q_gid);
                                (void) initgroups(ctladdr->q_ruser ?
                                        ctladdr->q_ruser : ctladdr->q_user,
                                        ctladdr->q_gid);
-                       }
+                       else if (FileMailer != NULL && FileMailer->m_gid != 0)
+                               (void) initgroups(DefUser, FileMailer->m_gid);
+                       else
+                               (void) initgroups(DefUser, DefGid);
                }
                if (!bitset(S_ISUID, mode) || setuid(stb.st_uid) < 0)
                {
                }
                if (!bitset(S_ISUID, mode) || setuid(stb.st_uid) < 0)
                {
-                       if (ctladdr == NULL || ctladdr->q_uid == 0)
-                               (void) setuid(DefUid);
-                       else
+                       if (ctladdr != NULL && ctladdr->q_uid != 0)
                                (void) setuid(ctladdr->q_uid);
                                (void) setuid(ctladdr->q_uid);
+                       else if (FileMailer != NULL && FileMailer->m_uid != 0)
+                               (void) setuid(FileMailer->m_uid);
+                       else
+                               (void) setuid(DefUid);
                }
                FileName = filename;
                LineNumber = 0;
                }
                FileName = filename;
                LineNumber = 0;
-               f = dfopen(filename, O_WRONLY|O_CREAT|O_APPEND, FileMode);
+               f = dfopen(filename, oflags, FileMode);
                if (f == NULL)
                {
                        message("554 cannot open: %s", errstring(errno));
                        exit(EX_CANTCREAT);
                }
                if (f == NULL)
                {
                        message("554 cannot open: %s", errstring(errno));
                        exit(EX_CANTCREAT);
                }
+               if (fstat(fileno(f), &fsb) < 0 ||
+                   (!bitset(O_CREAT, oflags) &&
+                    (stb.st_nlink != fsb.st_nlink ||
+                     stb.st_dev != fsb.st_dev ||
+                     stb.st_ino != fsb.st_ino ||
+                     stb.st_uid != fsb.st_uid)))
+               {
+                       message("554 cannot write: file changed after open");
+                       exit(EX_CANTCREAT);
+               }
 
 
-               putfromline(f, FileMailer, e);
-               (*e->e_puthdr)(f, FileMailer, e);
-               putline("\n", f, FileMailer);
-               (*e->e_putbody)(f, FileMailer, e, NULL);
-               putline("\n", f, FileMailer);
+               bzero(&mcibuf, sizeof mcibuf);
+               mcibuf.mci_mailer = FileMailer;
+               mcibuf.mci_out = f;
+               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);
                if (ferror(f))
                {
                        message("451 I/O error: %s", errstring(errno));
                if (ferror(f))
                {
                        message("451 I/O error: %s", errstring(errno));
@@ -2266,12 +2728,12 @@ hostsignature(m, host, e)
        register STAB *s;
        int i;
        int len;
        register STAB *s;
        int i;
        int len;
-#ifdef NAMED_BIND
+#if NAMED_BIND
        int nmx;
        auto int rcode;
        char *hp;
        char *endp;
        int nmx;
        auto int rcode;
        char *hp;
        char *endp;
-       int oldoptions;
+       int oldoptions = _res.options;
        char *mxhosts[MAXMXHOSTS + 1];
 #endif
 
        char *mxhosts[MAXMXHOSTS + 1];
 #endif
 
@@ -2298,12 +2760,9 @@ hostsignature(m, host, e)
        **  Not already there -- create a signature.
        */
 
        **  Not already there -- create a signature.
        */
 
-#ifdef NAMED_BIND
+#if NAMED_BIND
        if (ConfigLevel < 2)
        if (ConfigLevel < 2)
-       {
-               oldoptions = _res.options;
                _res.options &= ~(RES_DEFNAMES | RES_DNSRCH);   /* XXX */
                _res.options &= ~(RES_DEFNAMES | RES_DNSRCH);   /* XXX */
-       }
 
        for (hp = host; hp != NULL; hp = endp)
        {
 
        for (hp = host; hp != NULL; hp = endp)
        {
@@ -2321,9 +2780,7 @@ hostsignature(m, host, e)
                        mci = mci_get(hp, m);
                        mci->mci_exitstat = rcode;
                        mci->mci_errno = errno;
                        mci = mci_get(hp, m);
                        mci->mci_exitstat = rcode;
                        mci->mci_errno = errno;
-#ifdef NAMED_BIND
                        mci->mci_herrno = h_errno;
                        mci->mci_herrno = h_errno;
-#endif
 
                        /* and return the original host name as the signature */
                        nmx = 1;
 
                        /* and return the original host name as the signature */
                        nmx = 1;
@@ -2369,3 +2826,55 @@ hostsignature(m, host, e)
                printf("hostsignature(%s) = %s\n", host, s->s_hostsig);
        return s->s_hostsig;
 }
                printf("hostsignature(%s) = %s\n", host, s->s_hostsig);
        return s->s_hostsig;
 }
+\f/*
+**  SETSTATUS -- set the address status for return messages
+**
+**     Parameters:
+**             a -- the address to set.
+**             msg -- the text of the message, which must be in standard
+**                     SMTP form (3 digits, a space, and a message).
+**
+**     Returns:
+**             none.
+*/
+
+setstatus(a, msg)
+       register ADDRESS *a;
+       char *msg;
+{
+       char buf[MAXLINE];
+
+       if (a->q_rstatus != NULL)
+               free(a->q_rstatus);
+       if (strlen(msg) > 4)
+       {
+               register char *p, *q;
+               int parenlev = 0;
+
+               strncpy(buf, msg, 4);
+               p = &buf[4];
+               *p++ = '(';
+               for (q = &msg[4]; *q != '\0'; q++)
+               {
+                       switch (*q)
+                       {
+                         case '(':
+                               parenlev++;
+                               break;
+
+                         case ')':
+                               if (parenlev > 0)
+                                       parenlev--;
+                               else
+                                       *p++ = '\\';
+                               break;
+                       }
+                       *p++ = *q;
+               }
+               while (parenlev-- >= 0)
+                       *p++ = ')';
+               *p++ = '\0';
+               msg = buf;
+       }
+       a->q_rstatus = newstr(msg);
+}