make a bunch of #defines into 0/1 instead of ifdef/ifndef
[unix-history] / usr / src / usr.sbin / sendmail / src / deliver.c
index 888f90a..b7eb70e 100644 (file)
@@ -1,5 +1,5 @@
 /*
 /*
- * Copyright (c) 1983 Eric P. Allman
+ * Copyright (c) 1983, 1995 Eric P. Allman
  * Copyright (c) 1988, 1993
  *     The Regents of the University of California.  All rights reserved.
  *
  * Copyright (c) 1988, 1993
  *     The Regents of the University of California.  All rights reserved.
  *
@@ -7,11 +7,10 @@
  */
 
 #ifndef lint
  */
 
 #ifndef lint
-static char sccsid[] = "@(#)deliver.c  8.104 (Berkeley) %G%";
+static char sccsid[] = "@(#)deliver.c  8.158 (Berkeley) %G%";
 #endif /* not lint */
 
 #include "sendmail.h"
 #endif /* not lint */
 
 #include "sendmail.h"
-#include <netdb.h>
 #include <errno.h>
 #if NAMED_BIND
 #include <resolv.h>
 #include <errno.h>
 #if NAMED_BIND
 #include <resolv.h>
@@ -39,6 +38,7 @@ extern char   SmtpError[];
 **                     appropriate action.
 */
 
 **                     appropriate action.
 */
 
+void
 sendall(e, mode)
        ENVELOPE *e;
        char mode;
 sendall(e, mode)
        ENVELOPE *e;
        char mode;
@@ -46,7 +46,10 @@ sendall(e, mode)
        register ADDRESS *q;
        char *owner;
        int otherowners;
        register ADDRESS *q;
        char *owner;
        int otherowners;
-       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;
@@ -74,16 +77,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);
        }
@@ -99,11 +103,13 @@ 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;
                e->e_flags |= EF_FATALERRS|EF_PM_NOTIFY|EF_CLRQUEUE;
-               syserr("554 too many hops %d (%d max): from %s via %s, to %s",
+               syserr("554 Too many hops %d (%d max): from %s via %s, to %s",
                        e->e_hopcount, MaxHopCount, e->e_from.q_paddr,
                        RealHostName == NULL ? "localhost" : RealHostName,
                        e->e_sendqueue->q_paddr);
                        e->e_hopcount, MaxHopCount, e->e_from.q_paddr,
                        RealHostName == NULL ? "localhost" : RealHostName,
                        e->e_sendqueue->q_paddr);
+               e->e_sendqueue->q_status = "5.4.6";
                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,6 +324,34 @@ 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)
@@ -342,7 +376,7 @@ 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|EF_SENDRECEIPT);
+                       ee->e_flags = e->e_flags & ~(EF_INQUEUE|EF_CLRQUEUE|EF_FATALERRS|EF_SENDRECEIPT|EF_RET_PARAM);
                        ee->e_flags |= EF_NORECEIPT;
                        setsender(owner, ee, NULL, TRUE);
                        if (tTd(13, 5))
                        ee->e_flags |= EF_NORECEIPT;
                        setsender(owner, ee, NULL, TRUE);
                        if (tTd(13, 5))
@@ -353,31 +387,42 @@ 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;
                        
                        for (q = e->e_sendqueue; q != NULL; q = q->q_next)
                        ee->e_errormode = EM_MAIL;
                        
                        for (q = e->e_sendqueue; q != NULL; q = q->q_next)
+                       {
                                if (q->q_owner == owner)
                                {
                                        q->q_flags |= QDONTSEND;
                                        q->q_flags &= ~QQUEUEUP;
                                }
                                if (q->q_owner == owner)
                                {
                                        q->q_flags |= QDONTSEND;
                                        q->q_flags &= ~QQUEUEUP;
                                }
+                       }
                        for (q = ee->e_sendqueue; q != NULL; q = q->q_next)
                        for (q = ee->e_sendqueue; q != NULL; q = q->q_next)
+                       {
                                if (q->q_owner != owner)
                                {
                                        q->q_flags |= QDONTSEND;
                                        q->q_flags &= ~QQUEUEUP;
                                }
                                if (q->q_owner != owner)
                                {
                                        q->q_flags |= QDONTSEND;
                                        q->q_flags &= ~QQUEUEUP;
                                }
+                               else
+                               {
+                                       /* clear DSN parameters */
+                                       q->q_flags &= ~(QHASNOTIFY|QPINGONSUCCESS);
+                                       q->q_flags |= QPINGONFAILURE|QPINGONDELAY;
+                               }
+                       }
 
 
-                       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);
                                }
                        }
 #ifdef LOG
                                }
                        }
 #ifdef LOG
@@ -408,17 +453,56 @@ sendall(e, mode)
                e->e_flags |= EF_NORECEIPT;
        }
 
                e->e_flags |= EF_NORECEIPT;
        }
 
-       CurEnv = e;
-       sendenvelope(e, mode, announcequeueup);
+       /* 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))
+               {
+                       printf("\nsendall: Split queue; remaining queue:\n");
+                       printaddr(e->e_sendqueue, TRUE);
+               }
+
+               for (ee = splitenv; ee != NULL; ee = ee->e_sibling)
+               {
+                       CurEnv = ee;
+                       if (mode != SM_VERIFY)
+                               openxscript(ee);
+                       sendenvelope(ee, mode);
+                       dropenvelope(ee);
+               }
+
+               CurEnv = e;
+       }
+       sendenvelope(e, mode);
+       Verbose = oldverbose;
 }
 
 }
 
-sendenvelope(e, mode, announcequeueup)
+void
+sendenvelope(e, mode)
        register ENVELOPE *e;
        char mode;
        register ENVELOPE *e;
        char mode;
-       bool announcequeueup;
 {
        register ADDRESS *q;
 {
        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.
@@ -434,7 +518,7 @@ sendenvelope(e, mode, announcequeueup)
        /* 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)
        {
-#ifdef XDEBUG
+#if XDEBUG
                char wbuf[MAXNAME + 20];
 
                (void) sprintf(wbuf, "sendall(%s)", q->q_paddr);
                char wbuf[MAXNAME + 20];
 
                (void) sprintf(wbuf, "sendall(%s)", q->q_paddr);
@@ -473,14 +557,13 @@ sendenvelope(e, mode, announcequeueup)
                        didany = TRUE;
                }
        }
                        didany = TRUE;
                }
        }
-       Verbose = oldverbose;
        if (didany)
        {
                e->e_dtime = curtime();
                e->e_ntries++;
        }
 
        if (didany)
        {
                e->e_dtime = curtime();
                e->e_ntries++;
        }
 
-#ifdef XDEBUG
+#if XDEBUG
        checkfd012("end of sendenvelope");
 #endif
 
        checkfd012("end of sendenvelope");
 #endif
 
@@ -543,9 +626,10 @@ sendenvelope(e, mode, announcequeueup)
 **             returns twice, once in parent and once in child.
 */
 
 **             returns twice, once in parent and once in child.
 */
 
+int
 dofork()
 {
 dofork()
 {
-       register int pid;
+       register int pid = -1;
 
        DOFORK(fork);
        return (pid);
 
        DOFORK(fork);
        return (pid);
@@ -572,6 +656,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;
@@ -586,18 +671,20 @@ 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();
        extern int checkcompat();
+       extern void markfailure __P((ENVELOPE *, ADDRESS *, MCI *, int));
 
        errno = 0;
        if (!ForceMail && bitset(QDONTSEND|QPSEUDO, to->q_flags))
 
        errno = 0;
        if (!ForceMail && bitset(QDONTSEND|QPSEUDO, to->q_flags))
@@ -617,6 +704,7 @@ 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))
                printf("\n--deliver, id=%s, mailer=%s, host=`%s', first user=`%s'\n",
 
        if (tTd(10, 1))
                printf("\n--deliver, id=%s, mailer=%s, host=`%s', first user=`%s'\n",
@@ -624,33 +712,6 @@ deliver(e, firstto)
        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
@@ -710,7 +771,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])
                {
@@ -775,6 +836,12 @@ deliver(e, firstto)
                if (bitnset(M_RUNASRCPT, to->q_mailer->m_flags))
                        ctladdr = getctladdr(to);
 
                if (bitnset(M_RUNASRCPT, to->q_mailer->m_flags))
                        ctladdr = getctladdr(to);
 
+               if (tTd(10, 2))
+               {
+                       printf("ctladdr=");
+                       printaddr(ctladdr, FALSE);
+               }
+
                user = to->q_user;
                e->e_to = to->q_paddr;
                if (tTd(10, 5))
                user = to->q_user;
                e->e_to = to->q_paddr;
                if (tTd(10, 5))
@@ -791,9 +858,10 @@ 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)
                {
-                       e->e_flags |= EF_NORETURN;
+                       e->e_flags |= EF_NO_BODY_RETN;
+                       to->q_status = "5.2.3";
                        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;
                }
 #if NAMED_BIND
                        continue;
                }
 #if NAMED_BIND
@@ -802,8 +870,8 @@ deliver(e, firstto)
                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;
                }
 
@@ -845,12 +913,24 @@ deliver(e, firstto)
                **      with the others, so we fudge on the To person.
                */
 
                **      with the others, so we fudge on the To person.
                */
 
-               if (m == FileMailer)
+               if (strcmp(m->m_mailer, "[FILE]") == 0)
                {
                {
-                       rcode = mailfile(user, ctladdr, e);
-                       giveresponse(rcode, m, NULL, ctladdr, e);
+                       rcode = mailfile(user, ctladdr, SFF_CREAT, 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 |= QDELIVERED;
+                                       to->q_status = "2.1.5";
+                                       fprintf(e->e_xfp, "%s... Successfully delivered\n",
+                                               to->q_paddr);
+                               }
+                       }
                        to->q_statdate = curtime();
                        continue;
                }
                        to->q_statdate = curtime();
                        continue;
                }
@@ -879,7 +959,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])
                        {
@@ -905,7 +985,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]);
@@ -921,7 +1001,7 @@ deliver(e, firstto)
        */
 
        /*XXX this seems a bit wierd */
        */
 
        /*XXX this seems a bit wierd */
-       if (ctladdr == NULL && m != ProgMailer &&
+       if (ctladdr == NULL && m != ProgMailer && m != FileMailer &&
            bitset(QGOODUID, e->e_from.q_flags))
                ctladdr = &e->e_from;
 
            bitset(QGOODUID, e->e_from.q_flags))
                ctladdr = &e->e_from;
 
@@ -940,7 +1020,7 @@ deliver(e, firstto)
        h_errno = 0;
 #endif
 
        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
@@ -955,7 +1035,7 @@ deliver(e, firstto)
        SmtpPhase = NULL;
        mci = NULL;
 
        SmtpPhase = NULL;
        mci = NULL;
 
-#ifdef XDEBUG
+#if XDEBUG
        {
                char wbuf[MAXLINE];
 
        {
                char wbuf[MAXLINE];
 
@@ -972,6 +1052,7 @@ deliver(e, firstto)
        {
                usrerr("554 Cannot send 8-bit data to 7-bit destination");
                rcode = EX_DATAERR;
        {
                usrerr("554 Cannot send 8-bit data to 7-bit destination");
                rcode = EX_DATAERR;
+               e->e_status = "5.6.3";
                goto give_up;
        }
 
                goto give_up;
        }
 
@@ -990,7 +1071,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')
                {
@@ -1016,14 +1097,23 @@ deliver(e, firstto)
                        goto give_up;
                }
                if (pv[2] != NULL)
                        goto give_up;
                }
                if (pv[2] != NULL)
-                       port = atoi(pv[2]);
-               else
-                       port = 0;
+               {
+                       port = htons(atoi(pv[2]));
+                       if (port == 0)
+                       {
+                               struct servent *sp = getservbyname(pv[2], "tcp");
+
+                               if (sp == NULL)
+                                       syserr("Service %s unknown", pv[2]);
+                               else
+                                       port = sp->s_port;
+                       }
+               }
 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, ':');
@@ -1185,29 +1275,35 @@ tryhost:
                {
                        int i;
                        int saveerrno;
                {
                        int i;
                        int saveerrno;
-                       char **ep;
-                       char *env[MAXUSERENVIRON];
-                       extern char **environ;
+                       struct stat stb;
                        extern int DtableSize;
 
                        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);
 
                        /* 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 (m != FileMailer || stat(tochain->q_user, &stb) < 0)
+                               stb.st_mode = 0;
+
+                       /* tweak niceness */
+                       if (m->m_nice != 0)
+                               nice(m->m_nice);
+
+                       /* reset group id */
                        if (bitnset(M_SPECIFIC_UID, m->m_flags))
                        if (bitnset(M_SPECIFIC_UID, m->m_flags))
-                       {
                                (void) setgid(m->m_gid);
                                (void) setgid(m->m_gid);
-                               (void) setuid(m->m_uid);
-                       }
-                       else if (ctladdr != NULL && ctladdr->q_uid != 0)
+                       else if (bitset(S_ISGID, stb.st_mode))
+                               (void) setgid(stb.st_gid);
+                       else if (ctladdr != NULL && ctladdr->q_gid != 0)
                        {
                                (void) initgroups(ctladdr->q_ruser?
                                        ctladdr->q_ruser: ctladdr->q_user,
                                        ctladdr->q_gid);
                                (void) setgid(ctladdr->q_gid);
                        {
                                (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
                        {
                        }
                        else
                        {
@@ -1216,6 +1312,17 @@ tryhost:
                                        (void) setgid(DefGid);
                                else
                                        (void) setgid(m->m_gid);
                                        (void) setgid(DefGid);
                                else
                                        (void) setgid(m->m_gid);
+                       }
+
+                       /* reset user id */
+                       if (bitnset(M_SPECIFIC_UID, m->m_flags))
+                               (void) setuid(m->m_uid);
+                       else if (bitset(S_ISUID, stb.st_mode))
+                               (void) setuid(stb.st_uid);
+                       else if (ctladdr != NULL && ctladdr->q_uid != 0)
+                               (void) setuid(ctladdr->q_uid);
+                       else
+                       {
                                if (m->m_uid == 0)
                                        (void) setuid(DefUid);
                                else
                                if (m->m_uid == 0)
                                        (void) setuid(DefUid);
                                else
@@ -1230,14 +1337,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))
@@ -1298,31 +1405,11 @@ tryhost:
                                        (void) fcntl(i, F_SETFD, j | 1);
                        }
 
                                        (void) fcntl(i, F_SETFD, j | 1);
                        }
 
-                       /*
-                       **  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";
-                       env[i++] = "_FORCE_MAIL_LOCAL_=yes";
-                       for (ep = environ; *ep != NULL; ep++)
-                       {
-                               if (strncmp(*ep, "TZ=", 3) == 0 ||
-                                   strncmp(*ep, "ISP=", 4) == 0 ||
-                                   strncmp(*ep, "SYSTYPE=", 8) == 0)
-                                       env[i++] = *ep;
-                       }
-                       env[i] = NULL;
-
                        /* run disconnected from terminal */
                        (void) setsid();
 
                        /* try to execute the mailer */
                        /* run disconnected from terminal */
                        (void) setsid();
 
                        /* try to execute the mailer */
-                       execve(m->m_mailer, pv, env);
+                       execve(m->m_mailer, (ARGV_T) pv, (ARGV_T) UserEnviron);
                        saveerrno = errno;
                        syserr("Cannot exec %s", m->m_mailer);
                        if (bitnset(M_LOCALMAILER, m->m_flags) ||
                        saveerrno = errno;
                        syserr("Cannot exec %s", m->m_mailer);
                        if (bitnset(M_LOCALMAILER, m->m_flags) ||
@@ -1412,7 +1499,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;
@@ -1451,8 +1538,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
                                {
@@ -1516,23 +1603,33 @@ 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++;
                        if (bitnset(M_LOCALMAILER, m->m_flags) &&
                            (e->e_receiptto != NULL ||
                             bitset(QPINGONSUCCESS, to->q_flags)))
                        {
                        e->e_nsent++;
                        if (bitnset(M_LOCALMAILER, m->m_flags) &&
                            (e->e_receiptto != NULL ||
                             bitset(QPINGONSUCCESS, to->q_flags)))
                        {
-                               to->q_flags |= QREPORT;
+                               to->q_flags |= QDELIVERED;
+                               to->q_status = "2.1.5";
                                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);
+                       }
                }
        }
 
                }
        }
 
@@ -1540,7 +1637,7 @@ tryhost:
        **  Restore state and return.
        */
 
        **  Restore state and return.
        */
 
-#ifdef XDEBUG
+#if XDEBUG
        {
                char wbuf[MAXLINE];
 
        {
                char wbuf[MAXLINE];
 
@@ -1562,6 +1659,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:
@@ -1573,12 +1671,14 @@ tryhost:
 **                     the message will be queued, as appropriate.
 */
 
 **                     the message will be queued, as appropriate.
 */
 
-markfailure(e, q, rcode)
+void
+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)
        {
@@ -1595,8 +1695,69 @@ markfailure(e, q, rcode)
                q->q_flags |= QBADADDR;
                break;
        }
                q->q_flags |= QBADADDR;
                break;
        }
+
+       /* find most specific error code possible */
+       if (q->q_status == NULL && mci != NULL)
+               q->q_status = mci->mci_status;
+       if (q->q_status == NULL)
+               q->q_status = e->e_status;
+       if (q->q_status == NULL)
+       {
+               switch (rcode)
+               {
+                 case EX_USAGE:
+                       stat = "5.5.4";
+                       break;
+
+                 case EX_DATAERR:
+                       stat = "5.5.2";
+                       break;
+
+                 case EX_NOUSER:
+                       stat = "5.1.1";
+                       break;
+
+                 case EX_NOHOST:
+                       stat = "5.1.2";
+                       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 = stat;
+       }
+
        q->q_statdate = curtime();
        q->q_statdate = curtime();
-       q->q_statmta = newstr(CurHostName);
+       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.
@@ -1619,6 +1780,7 @@ markfailure(e, q, rcode)
 **             none.
 */
 
 **             none.
 */
 
+int
 endmailer(mci, e, pv)
        register MCI *mci;
        register ENVELOPE *e;
 endmailer(mci, e, pv)
        register MCI *mci;
        register ENVELOPE *e;
@@ -1682,6 +1844,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:
@@ -1692,11 +1856,13 @@ endmailer(mci, e, pv)
 **             ExitStat may be set.
 */
 
 **             ExitStat may be set.
 */
 
-giveresponse(stat, m, mci, ctladdr, e)
+void
+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;
@@ -1799,11 +1965,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",
 
        if (tTd(11, 2))
                printf("giveresponse: stat=%d, e->e_message=%s\n",
-                       stat, e->e_message);
+                       stat, e->e_message == NULL ? "<NULL>" : e->e_message);
 
        if (stat != EX_TEMPFAIL)
                setstat(stat);
 
        if (stat != EX_TEMPFAIL)
                setstat(stat);
@@ -1831,6 +1997,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:
@@ -1840,11 +2008,13 @@ giveresponse(stat, m, mci, ctladdr, e)
 **             none
 */
 
 **             none
 */
 
-logdelivery(m, mci, stat, ctladdr, e)
+void
+logdelivery(m, mci, stat, ctladdr, xstart, e)
        MAILER *m;
        register MCI *mci;
        MAILER *m;
        register MCI *mci;
-       char *stat;
+       const char *stat;
        ADDRESS *ctladdr;
        ADDRESS *ctladdr;
+       time_t xstart;
        register ENVELOPE *e;
 {
 # ifdef LOG
        register ENVELOPE *e;
 {
 # ifdef LOG
@@ -1868,9 +2038,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=");
@@ -1930,7 +2106,7 @@ logdelivery(m, mci, stat, ctladdr, e)
                
        l = SYSLOG_BUFSIZE - 100 - strlen(buf);
        p = e->e_to;
                
        l = SYSLOG_BUFSIZE - 100 - strlen(buf);
        p = e->e_to;
-       while (strlen(p) >= l)
+       while (strlen(p) >= (SIZE_T) l)
        {
                register char *q = strchr(p + l, ',');
 
        {
                register char *q = strchr(p + l, ',');
 
@@ -1976,6 +2152,11 @@ logdelivery(m, mci, stat, ctladdr, e)
        bp = buf;
        sprintf(bp, "delay=%s", pintvl(curtime() - e->e_ctime, TRUE));
        bp += strlen(bp);
        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)
        {
@@ -2036,6 +2217,7 @@ logdelivery(m, mci, stat, ctladdr, e)
 **             outputs some text to fp.
 */
 
 **             outputs some text to fp.
 */
 
+void
 putfromline(mci, e)
        register MCI *mci;
        ENVELOPE *e;
 putfromline(mci, e)
        register MCI *mci;
        ENVELOPE *e;
@@ -2047,13 +2229,12 @@ putfromline(mci, e)
        if (bitnset(M_NHDR, mci->mci_mailer->m_flags))
                return;
 
        if (bitnset(M_NHDR, mci->mci_mailer->m_flags))
                return;
 
-# ifdef UGLYUUCP
        if (bitnset(M_UGLYUUCP, mci->mci_mailer->m_flags))
        {
                char *bang;
                char xbuf[MAXLINE];
 
        if (bitnset(M_UGLYUUCP, mci->mci_mailer->m_flags))
        {
                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)
                {
@@ -2067,9 +2248,8 @@ putfromline(mci, e)
                        template = xbuf;
                }
        }
                        template = xbuf;
                }
        }
-# endif /* UGLYUUCP */
-       expand(template, buf, &buf[sizeof buf - 1], e);
-       putline(buf, mci);
+       expand(template, buf, sizeof buf, e);
+       putxline(buf, mci, PXLF_NOTHINGSPECIAL);
 }
 \f/*
 **  PUTBODY -- put the body of a message.
 }
 \f/*
 **  PUTBODY -- put the body of a message.
@@ -2092,6 +2272,7 @@ putfromline(mci, e)
 #define OS_CR          1       /* read a carriage return */
 #define OS_INLINE      2       /* putting rest of line */
 
 #define OS_CR          1       /* read a carriage return */
 #define OS_INLINE      2       /* putting rest of line */
 
+void
 putbody(mci, e, separator)
        register MCI *mci;
        register ENVELOPE *e;
 putbody(mci, e, separator)
        register MCI *mci;
        register ENVELOPE *e;
@@ -2103,27 +2284,26 @@ putbody(mci, 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)
-               {
-                       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);
-               }
-               else
+               if (bitset(MCIF_INHEADER, mci->mci_flags))
                {
                {
-                       if (bitset(MCIF_INHEADER, mci->mci_flags))
-                       {
-                               putline("", mci);
-                               mci->mci_flags &= ~MCIF_INHEADER;
-                       }
-                       putline("<<< No Message Collected >>>", mci);
-                       goto endofmessage;
+                       putline("", mci);
+                       mci->mci_flags &= ~MCIF_INHEADER;
                }
                }
+               putline("<<< No Message Collected >>>", mci);
+               goto endofmessage;
        }
        }
-       if (e->e_dfp != NULL && e->e_dfino == (ino_t) 0)
+       if (e->e_dfino == (ino_t) 0)
        {
                struct stat stbuf;
 
        {
                struct stat stbuf;
 
@@ -2137,8 +2317,11 @@ putbody(mci, e, separator)
        }
        rewind(e->e_dfp);
 
        }
        rewind(e->e_dfp);
 
+#if MIME8TO7
        if (bitset(MCIF_CVT8TO7, mci->mci_flags))
        {
        if (bitset(MCIF_CVT8TO7, mci->mci_flags))
        {
+               char *boundaries[MAXMIMENESTING + 1];
+
                /*
                **  Do 8 to 7 bit MIME conversion.
                */
                /*
                **  Do 8 to 7 bit MIME conversion.
                */
@@ -2149,20 +2332,17 @@ putbody(mci, e, separator)
 
                if (hvalue("Content-Type", e->e_header) == NULL)
                {
 
                if (hvalue("Content-Type", e->e_header) == NULL)
                {
-                       char *charset = DefaultCharSet;
-
-                       /* as recommended by RFC 1428 section 3... */
-                       if (charset == NULL)
-                               charset = "unknown-8bit";
                        sprintf(buf, "Content-Type: text/plain; charset=%s",
                        sprintf(buf, "Content-Type: text/plain; charset=%s",
-                               charset);
+                               defcharset(e));
                        putline(buf, mci);
                }
 
                /* now do the hard work */
                        putline(buf, mci);
                }
 
                /* now do the hard work */
-               mime8to7(mci, e->e_header, e, NULL);
+               boundaries[0] = NULL;
+               mime8to7(mci, e->e_header, e, boundaries, M87F_OUTER);
        }
        else
        }
        else
+#endif
        {
                int ostate;
                register char *bp;
        {
                int ostate;
                register char *bp;
@@ -2170,7 +2350,7 @@ putbody(mci, e, separator)
                register int c;
                int padc;
                char *buflim;
                register int c;
                int padc;
                char *buflim;
-               int pos;
+               int pos = 0;
                char peekbuf[10];
 
                /* we can pass it through unmodified */
                char peekbuf[10];
 
                /* we can pass it through unmodified */
@@ -2196,7 +2376,7 @@ putbody(mci, e, separator)
 
                        if (pbp > peekbuf)
                                c = *--pbp;
 
                        if (pbp > peekbuf)
                                c = *--pbp;
-                       else if ((c = fgetc(e->e_dfp)) == EOF)
+                       else if ((c = getc(e->e_dfp)) == EOF)
                                break;
                        if (bitset(MCIF_7BIT, mci->mci_flags))
                                c &= 0x7f;
                                break;
                        if (bitset(MCIF_7BIT, mci->mci_flags))
                                c &= 0x7f;
@@ -2239,20 +2419,20 @@ putbody(mci, e, separator)
                                {
                                        fprintf(TrafficLogFile, "%05d >>> ", getpid());
                                        if (padc != EOF)
                                {
                                        fprintf(TrafficLogFile, "%05d >>> ", getpid());
                                        if (padc != EOF)
-                                               fputc(padc, TrafficLogFile);
+                                               putc(padc, TrafficLogFile);
                                        for (xp = buf; xp < bp; xp++)
                                        for (xp = buf; xp < bp; xp++)
-                                               fputc(*xp, TrafficLogFile);
+                                               putc(*xp, TrafficLogFile);
                                        if (c == '\n')
                                                fputs(mci->mci_mailer->m_eol,
                                                      TrafficLogFile);
                                }
                                if (padc != EOF)
                                {
                                        if (c == '\n')
                                                fputs(mci->mci_mailer->m_eol,
                                                      TrafficLogFile);
                                }
                                if (padc != EOF)
                                {
-                                       fputc(padc, mci->mci_out);
+                                       putc(padc, mci->mci_out);
                                        pos++;
                                }
                                for (xp = buf; xp < bp; xp++)
                                        pos++;
                                }
                                for (xp = buf; xp < bp; xp++)
-                                       fputc(*xp, mci->mci_out);
+                                       putc(*xp, mci->mci_out);
                                if (c == '\n')
                                {
                                        fputs(mci->mci_mailer->m_eol,
                                if (c == '\n')
                                {
                                        fputs(mci->mci_mailer->m_eol,
@@ -2318,7 +2498,7 @@ putch:
                                        continue;
                                }
                                if (TrafficLogFile != NULL)
                                        continue;
                                }
                                if (TrafficLogFile != NULL)
-                                       fputc(c, TrafficLogFile);
+                                       putc(c, TrafficLogFile);
                                putc(c, mci->mci_out);
                                pos++;
                                ostate = c == '\n' ? OS_HEAD : OS_INLINE;
                                putc(c, mci->mci_out);
                                pos++;
                                ostate = c == '\n' ? OS_HEAD : OS_INLINE;
@@ -2329,7 +2509,7 @@ putch:
 
        if (ferror(e->e_dfp))
        {
 
        if (ferror(e->e_dfp))
        {
-               syserr("putbody: %s: read error", e->e_df);
+               syserr("putbody: df%s: read error", e->e_id);
                ExitStat = EX_IOERR;
        }
 
                ExitStat = EX_IOERR;
        }
 
@@ -2365,6 +2545,8 @@ endofmessage:
 **             filename -- the name of the file to send to.
 **             ctladdr -- the controlling address header -- includes
 **                     the userid/groupid to be when sending.
 **             filename -- the name of the file to send to.
 **             ctladdr -- the controlling address header -- includes
 **                     the userid/groupid to be when sending.
+**             sfflags -- flags for opening.
+**             e -- the current envelope.
 **
 **     Returns:
 **             The exit code associated with the operation.
 **
 **     Returns:
 **             The exit code associated with the operation.
@@ -2373,13 +2555,15 @@ endofmessage:
 **             none.
 */
 
 **             none.
 */
 
-mailfile(filename, ctladdr, e)
+int
+mailfile(filename, ctladdr, sfflags, e)
        char *filename;
        ADDRESS *ctladdr;
        char *filename;
        ADDRESS *ctladdr;
+       int sfflags;
        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))
@@ -2405,63 +2589,134 @@ 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;
                MCI mcibuf;
+               int oflags = O_WRONLY|O_APPEND;
+               int oflags = O_WRONLY|O_APPEND;
+
+               if (e->e_lockfp != NULL)
+                       (void) close(fileno(e->e_lockfp));
 
                (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);
+               e->e_to = filename;
+               ExitStat = EX_OK;
 
 
+#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)
+               if (ctladdr != NULL || bitset(SFF_RUNASREALUID, sfflags))
                {
                        /* ignore setuid and setgid bits */
                        mode &= ~(S_ISGID|S_ISUID);
                }
 
                /* we have to open the dfile BEFORE setuid */
                {
                        /* ignore setuid and setgid bits */
                        mode &= ~(S_ISGID|S_ISUID);
                }
 
                /* 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);
+                       }
+                       i = strlen(SafeFileEnv);
+                       if (strncmp(SafeFileEnv, filename, i) == 0)
+                               filename += i;
+               }
+               if (chdir("/") < 0)
+                       syserr("mailfile: cannot chdir(/)");
+
+               /* select a new user to run as */
+               if (!bitset(SFF_RUNASREALUID, sfflags))
+               {
+                       if (bitset(S_ISUID, mode))
+                       {
+                               RealUserName = NULL;
+                               RealUid = stb.st_uid;
+                       }
+                       else if (ctladdr != NULL && ctladdr->q_uid != 0)
+                       {
+                               if (ctladdr->q_ruser != NULL)
+                                       RealUserName = ctladdr->q_ruser;
+                               else
+                                       RealUserName = ctladdr->q_user;
+                               RealUid = ctladdr->q_uid;
+                       }
+                       else if (FileMailer != NULL && FileMailer->m_uid != 0)
+                       {
+                               RealUserName = DefUser;
+                               RealUid = FileMailer->m_uid;
                        }
                        else
                        {
                        }
                        else
                        {
-                               (void) initgroups(ctladdr->q_ruser ?
-                                       ctladdr->q_ruser : ctladdr->q_user,
-                                       ctladdr->q_gid);
+                               RealUserName = DefUser;
+                               RealUid = DefUid;
                        }
                        }
+
+                       /* select a new group to run as */
+                       if (bitset(S_ISGID, mode))
+                               RealGid = stb.st_gid;
+                       else if (ctladdr != NULL && ctladdr->q_uid != 0)
+                               RealGid = ctladdr->q_gid;
+                       else if (FileMailer != NULL && FileMailer->m_gid != 0)
+                               RealGid = FileMailer->m_gid;
+                       else
+                               RealGid = DefGid;
                }
                }
-               if (!bitset(S_ISUID, mode) || setuid(stb.st_uid) < 0)
+
+               /* last ditch */
+               if (!bitset(SFF_ROOTOK, sfflags))
                {
                {
-                       if (ctladdr == NULL || ctladdr->q_uid == 0)
-                               (void) setuid(DefUid);
-                       else
-                               (void) setuid(ctladdr->q_uid);
+                       if (RealUid == 0)
+                               RealUid = DefUid;
+                       if (RealGid == 0)
+                               RealGid = DefGid;
                }
                }
-               FileName = filename;
-               LineNumber = 0;
-               f = dfopen(filename, O_WRONLY|O_CREAT|O_APPEND, FileMode);
+
+               /* now set the group and user ids */
+               if (RealUserName != NULL)
+                       (void) initgroups(RealUserName, RealGid);
+               else
+                       (void) setgid(RealGid);
+               (void) setuid(RealUid);
+
+               sfflags |= SFF_NOPATHCHECK;
+               sfflags &= ~SFF_OPENASROOT;
+               f = safefopen(filename, oflags, FileMode, sfflags);
                if (f == NULL)
                {
                        message("554 cannot open: %s", errstring(errno));
                if (f == NULL)
                {
                        message("554 cannot open: %s", errstring(errno));
@@ -2541,7 +2796,7 @@ hostsignature(m, host, e)
        auto int rcode;
        char *hp;
        char *endp;
        auto int rcode;
        char *hp;
        char *endp;
-       int oldoptions;
+       int oldoptions = _res.options;
        char *mxhosts[MAXMXHOSTS + 1];
 #endif
 
        char *mxhosts[MAXMXHOSTS + 1];
 #endif
 
@@ -2570,10 +2825,7 @@ hostsignature(m, host, e)
 
 #if NAMED_BIND
        if (ConfigLevel < 2)
 
 #if NAMED_BIND
        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)
        {
@@ -2591,9 +2843,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;
-#if 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;
@@ -2639,55 +2889,3 @@ 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_status != NULL)
-               free(a->q_status);
-       if (strlen(msg) > 4)
-       {
-               register char *p, *q;
-               int parenlev = 0;
-
-               strncpy(buf, msg, 4);
-               p = &buf[4];
-               *p++ = '(';
-               for (q = &msg[4]; *q != NULL; 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_status = newstr(msg);
-}