fix wildcard MX handling (option W no longer needed); fix
[unix-history] / usr / src / usr.sbin / sendmail / src / deliver.c
index 872ab97..8733dba 100644 (file)
@@ -1,9 +1,25 @@
-# include <signal.h>
-# include <errno.h>
-# include "sendmail.h"
-# include <sys/stat.h>
-
-SCCSID(@(#)deliver.c   4.1             %G%);
+/*
+ * Copyright (c) 1983 Eric P. Allman
+ * Copyright (c) 1988 Regents of the University of California.
+ * All rights reserved.
+ *
+ * %sccs.include.redist.c%
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)deliver.c  6.14 (Berkeley) %G%";
+#endif /* not lint */
+
+#include "sendmail.h"
+#include <sys/signal.h>
+#include <sys/stat.h>
+#include <netdb.h>
+#include <fcntl.h>
+#include <errno.h>
+#ifdef NAMED_BIND
+#include <arpa/nameser.h>
+#include <resolv.h>
+#endif
 
 /*
 **  DELIVER -- Deliver a message to a list of addresses.
 
 /*
 **  DELIVER -- Deliver a message to a list of addresses.
@@ -38,30 +54,42 @@ deliver(e, firstto)
        register char *p;
        register MAILER *m;             /* mailer for this recipient */
        ADDRESS *ctladdr;
        register char *p;
        register MAILER *m;             /* mailer for this recipient */
        ADDRESS *ctladdr;
+       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 */
        register ADDRESS *to = firstto;
        bool clever = FALSE;            /* running user smtp to this mailer */
        ADDRESS *tochain = NULL;        /* chain of users in this mailer call */
-       register int rcode;             /* response code */
+       int rcode;                      /* response code */
+       char *from;                     /* pointer to from person */
+       char *firstsig;                 /* signature of firstto */
        char *pv[MAXPV+1];
        char tobuf[MAXLINE-50];         /* text line of to people */
        char buf[MAXNAME];
        char tfrombuf[MAXNAME];         /* translated from person */
        char *pv[MAXPV+1];
        char tobuf[MAXLINE-50];         /* text line of to people */
        char buf[MAXNAME];
        char tfrombuf[MAXNAME];         /* translated from person */
-       extern bool checkcompat();
+       char rpathbuf[MAXNAME];         /* translated return path */
+       extern int checkcompat();
        extern ADDRESS *getctladdr();
        extern char *remotename();
        extern ADDRESS *getctladdr();
        extern char *remotename();
+       extern MCI *openmailer();
+       extern char *hostsignature();
 
        errno = 0;
        if (!ForceMail && bitset(QDONTSEND|QPSEUDO, to->q_flags))
                return (0);
 
 
        errno = 0;
        if (!ForceMail && bitset(QDONTSEND|QPSEUDO, to->q_flags))
                return (0);
 
+#ifdef NAMED_BIND
+       /* unless interactive, try twice, over a minute */
+       if (OpMode == MD_DAEMON || OpMode == MD_SMTP) {
+               _res.retrans = 30;
+               _res.retry = 2;
+       }
+#endif 
+
        m = to->q_mailer;
        host = to->q_host;
 
        m = to->q_mailer;
        host = to->q_host;
 
-# ifdef DEBUG
        if (tTd(10, 1))
                printf("\n--deliver, mailer=%d, host=`%s', first user=`%s'\n",
                        m->m_mno, host, to->q_user);
        if (tTd(10, 1))
                printf("\n--deliver, mailer=%d, host=`%s', first user=`%s'\n",
                        m->m_mno, host, to->q_user);
-# endif DEBUG
 
        /*
        **  If this mailer is expensive, and if we don't want to make
 
        /*
        **  If this mailer is expensive, and if we don't want to make
@@ -84,7 +112,7 @@ deliver(e, firstto)
                        e->e_to = to->q_paddr;
                        message(Arpa_Info, "queued");
                        if (LogLevel > 4)
                        e->e_to = to->q_paddr;
                        message(Arpa_Info, "queued");
                        if (LogLevel > 4)
-                               logdelivery("queued");
+                               logdelivery("queued", e);
                }
                e->e_to = NULL;
                return (0);
                }
                e->e_to = NULL;
                return (0);
@@ -102,10 +130,20 @@ deliver(e, firstto)
        */
 
        /* rewrite from address, using rewriting rules */
        */
 
        /* rewrite from address, using rewriting rules */
-       expand("$f", buf, &buf[sizeof buf - 1], e);
-       (void) strcpy(tfrombuf, remotename(buf, m, TRUE, TRUE));
+       (void) strcpy(rpathbuf, remotename(e->e_returnpath, m, TRUE, TRUE, e));
+       if (e->e_returnpath == e->e_sender)
+       {
+               from = rpathbuf;
+       }
+       else
+       {
+               (void) strcpy(tfrombuf, remotename(e->e_sender, m, TRUE, TRUE, e));
+               from = tfrombuf;
+       }
 
 
-       define('g', tfrombuf, e);               /* translated sender address */
+       define('f', e->e_returnpath, e);        /* raw return path */
+       define('<', rpathbuf, e);               /* translated return path */
+       define('g', from, e);                   /* translated sender */
        define('h', host, e);                   /* to host */
        Errors = 0;
        pvp = pv;
        define('h', host, e);                   /* to host */
        Errors = 0;
        pvp = pv;
@@ -118,8 +156,7 @@ deliver(e, firstto)
                        *pvp++ = "-f";
                else
                        *pvp++ = "-r";
                        *pvp++ = "-f";
                else
                        *pvp++ = "-r";
-               expand("$g", buf, &buf[sizeof buf - 1], e);
-               *pvp++ = newstr(buf);
+               *pvp++ = newstr(rpathbuf);
        }
 
        /*
        }
 
        /*
@@ -131,7 +168,7 @@ deliver(e, firstto)
 
        for (mvp = m->m_argv; (p = *++mvp) != NULL; )
        {
 
        for (mvp = m->m_argv; (p = *++mvp) != NULL; )
        {
-               while ((p = index(p, '$')) != NULL)
+               while ((p = strchr(p, '\001')) != NULL)
                        if (*++p == 'u')
                                break;
                if (p != NULL)
                        if (*++p == 'u')
                                break;
                if (p != NULL)
@@ -159,11 +196,11 @@ deliver(e, firstto)
 # ifdef SMTP
                clever = TRUE;
                *pvp = NULL;
 # ifdef SMTP
                clever = TRUE;
                *pvp = NULL;
-# else SMTP
+# else /* SMTP */
                /* oops!  we don't implement SMTP */
                syserr("SMTP style mailer");
                return (EX_SOFTWARE);
                /* oops!  we don't implement SMTP */
                syserr("SMTP style mailer");
                return (EX_SOFTWARE);
-# endif SMTP
+# endif /* SMTP */
        }
 
        /*
        }
 
        /*
@@ -176,6 +213,7 @@ deliver(e, firstto)
        tobuf[0] = '\0';
        e->e_to = tobuf;
        ctladdr = NULL;
        tobuf[0] = '\0';
        e->e_to = tobuf;
        ctladdr = NULL;
+       firstsig = hostsignature(firstto->q_mailer, firstto->q_host, e);
        for (; to != NULL; to = to->q_next)
        {
                /* avoid sending multiple recipients to dumb mailers */
        for (; to != NULL; to = to->q_next)
        {
                /* avoid sending multiple recipients to dumb mailers */
@@ -184,21 +222,19 @@ deliver(e, firstto)
 
                /* if already sent or not for this host, don't send */
                if (bitset(QDONTSEND, to->q_flags) ||
 
                /* if already sent or not for this host, don't send */
                if (bitset(QDONTSEND, to->q_flags) ||
-                   strcmp(to->q_host, host) != 0 ||
-                   to->q_mailer != firstto->q_mailer)
+                   to->q_mailer != firstto->q_mailer ||
+                   strcmp(hostsignature(to->q_mailer, to->q_host, e), firstsig) != 0)
                        continue;
 
                /* avoid overflowing tobuf */
                        continue;
 
                /* avoid overflowing tobuf */
-               if (sizeof tobuf - (strlen(to->q_paddr) + strlen(tobuf) + 2) < 0)
+               if (sizeof tobuf < (strlen(to->q_paddr) + strlen(tobuf) + 2))
                        break;
 
                        break;
 
-# ifdef DEBUG
                if (tTd(10, 1))
                {
                        printf("\nsend to ");
                        printaddr(to, FALSE);
                }
                if (tTd(10, 1))
                {
                        printf("\nsend to ");
                        printaddr(to, FALSE);
                }
-# endif DEBUG
 
                /* compute effective uid/gid when sending */
                if (to->q_mailer == ProgMailer)
 
                /* compute effective uid/gid when sending */
                if (to->q_mailer == ProgMailer)
@@ -207,6 +243,11 @@ deliver(e, firstto)
                user = to->q_user;
                e->e_to = to->q_paddr;
                to->q_flags |= QDONTSEND;
                user = to->q_user;
                e->e_to = to->q_paddr;
                to->q_flags |= QDONTSEND;
+               if (tTd(10, 5))
+               {
+                       printf("deliver: QDONTSEND ");
+                       printaddr(to, FALSE);
+               }
 
                /*
                **  Check to see that these people are allowed to
 
                /*
                **  Check to see that these people are allowed to
@@ -215,14 +256,15 @@ 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)
                {
-                       usrerr("Message is too large; %ld bytes max", m->m_maxsize);
                        NoReturn = TRUE;
                        NoReturn = TRUE;
+                       usrerr("Message is too large; %ld bytes max", m->m_maxsize);
                        giveresponse(EX_UNAVAILABLE, m, e);
                        continue;
                }
                        giveresponse(EX_UNAVAILABLE, m, e);
                        continue;
                }
-               if (!checkcompat(to))
+               rcode = checkcompat(to, e);
+               if (rcode != EX_OK)
                {
                {
-                       giveresponse(EX_UNAVAILABLE, m, e);
+                       giveresponse(rcode, m, e);
                        continue;
                }
 
                        continue;
                }
 
@@ -233,13 +275,8 @@ deliver(e, firstto)
 
                if (bitnset(M_STRIPQ, m->m_flags))
                {
 
                if (bitnset(M_STRIPQ, m->m_flags))
                {
-                       stripquotes(user, TRUE);
-                       stripquotes(host, TRUE);
-               }
-               else
-               {
-                       stripquotes(user, FALSE);
-                       stripquotes(host, FALSE);
+                       stripquotes(user);
+                       stripquotes(host);
                }
 
                /* hack attack -- delivermail compatibility */
                }
 
                /* hack attack -- delivermail compatibility */
@@ -269,14 +306,13 @@ 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 == LocalMailer)
+               if (m == FileMailer)
                {
                {
-                       if (user[0] == '/')
-                       {
-                               rcode = mailfile(user, getctladdr(to));
-                               giveresponse(rcode, m, e);
-                               continue;
-                       }
+                       rcode = mailfile(user, getctladdr(to), e);
+                       giveresponse(rcode, m, e);
+                       if (rcode == EX_OK)
+                               to->q_flags |= QSENT;
+                       continue;
                }
 
                /*
                }
 
                /*
@@ -314,6 +350,7 @@ deliver(e, firstto)
        if (tobuf[0] == '\0')
        {
                define('g', (char *) NULL, e);
        if (tobuf[0] == '\0')
        {
                define('g', (char *) NULL, e);
+               define('<', (char *) NULL, e);
                return (0);
        }
 
                return (0);
        }
 
@@ -341,54 +378,113 @@ deliver(e, firstto)
        **      If we are running SMTP, we just need to clean up.
        */
 
        **      If we are running SMTP, we just need to clean up.
        */
 
-       message(Arpa_Info, "Connecting to %s.%s...", host, m->m_name);
-
        if (ctladdr == NULL)
                ctladdr = &e->e_from;
        if (ctladdr == NULL)
                ctladdr = &e->e_from;
-# ifdef SMTP
-       if (clever)
+#ifdef NAMED_BIND
+       if (ConfigLevel < 2)
+               _res.options &= ~(RES_DEFNAMES | RES_DNSRCH);   /* XXX */
+#endif
+       mci = openmailer(m, pv, ctladdr, clever, e);
+       if (mci == NULL)
+       {
+               /* catastrophic error */
+               rcode = -1;
+               goto give_up;
+       }
+       else if (mci->mci_state != MCIS_OPEN)
+       {
+               /* couldn't open the mailer */
+               rcode = mci->mci_exitstat;
+               errno = mci->mci_errno;
+               if (rcode == EX_OK)
+               {
+                       /* shouldn't happen */
+                       syserr("deliver: rcode=%d, mci_state=%d, sig=%s",
+                               rcode, mci->mci_state, firstsig);
+                       rcode = EX_SOFTWARE;
+               }
+       }
+       else if (!clever)
+       {
+               /*
+               **  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);
+
+               /* get the exit status */
+               rcode = endmailer(mci, pv[0]);
+       }
+       else
+#ifdef SMTP
        {
        {
-               /* send the initial SMTP protocol */
-               rcode = smtpinit(m, pv);
+               /*
+               **  Send the MAIL FROM: protocol
+               */
 
 
+               rcode = smtpmailfrom(m, mci, e);
                if (rcode == EX_OK)
                {
                if (rcode == EX_OK)
                {
+                       register char *t = tobuf;
+                       register int i;
+
                        /* send the recipient list */
                        tobuf[0] = '\0';
                        for (to = tochain; to != NULL; to = to->q_tchain)
                        {
                        /* send the recipient list */
                        tobuf[0] = '\0';
                        for (to = tochain; to != NULL; to = to->q_tchain)
                        {
-                               int i;
-
                                e->e_to = to->q_paddr;
                                e->e_to = to->q_paddr;
-                               i = smtprcpt(to, m);
-                               if (i != EX_OK)
+                               if ((i = smtprcpt(to, m, mci, e)) != EX_OK)
                                {
                                        markfailure(e, to, i);
                                        giveresponse(i, m, e);
                                }
                                else
                                {
                                {
                                        markfailure(e, to, i);
                                        giveresponse(i, m, e);
                                }
                                else
                                {
-                                       strcat(tobuf, ",");
-                                       strcat(tobuf, to->q_paddr);
+                                       *t++ = ',';
+                                       for (p = to->q_paddr; *p; *t++ = *p++)
+                                               continue;
                                }
                        }
 
                        /* now send the data */
                        if (tobuf[0] == '\0')
                                }
                        }
 
                        /* now send the data */
                        if (tobuf[0] == '\0')
+                       {
                                e->e_to = NULL;
                                e->e_to = NULL;
+                               if (bitset(MCIF_CACHED, mci->mci_flags))
+                                       smtprset(m, mci, e);
+                       }
                        else
                        {
                                e->e_to = tobuf + 1;
                        else
                        {
                                e->e_to = tobuf + 1;
-                               rcode = smtpdata(m, e);
+                               rcode = smtpdata(m, mci, e);
                        }
 
                        /* now close the connection */
                        }
 
                        /* now close the connection */
-                       smtpquit(pv[0], m);
+                       if (!bitset(MCIF_CACHED, mci->mci_flags))
+                               smtpquit(m, mci, e);
                }
        }
                }
        }
-       else
-# endif SMTP
-               rcode = sendoff(e, m, pv, ctladdr);
+#else /* not SMTP */
+       {
+               syserr("deliver: need SMTP compiled to use clever mailer");
+               rcode = -1;
+               goto give_up;
+       }
+#endif /* SMTP */
+#ifdef NAMED_BIND
+       if (ConfigLevel < 2)
+               _res.options |= RES_DEFNAMES | RES_DNSRCH;      /* XXX */
+#endif
+
+       /* arrange a return receipt if requested */
+       if (e->e_receiptto != NULL && bitnset(M_LOCAL, m->m_flags))
+       {
+               e->e_flags |= EF_SENDRECEIPT;
+               /* do we want to send back more info? */
+       }
 
        /*
        **  Do final status disposal.
 
        /*
        **  Do final status disposal.
@@ -397,16 +493,27 @@ deliver(e, firstto)
        **              addressees.
        */
 
        **              addressees.
        */
 
+  give_up:
        if (tobuf[0] != '\0')
                giveresponse(rcode, m, e);
        if (tobuf[0] != '\0')
                giveresponse(rcode, m, e);
-       if (rcode != EX_OK)
+       for (to = tochain; to != NULL; to = to->q_tchain)
        {
        {
-               for (to = tochain; to != NULL; to = to->q_tchain)
+               if (rcode != EX_OK)
                        markfailure(e, to, rcode);
                        markfailure(e, to, rcode);
+               else
+               {
+                       to->q_flags |= QSENT;
+                       e->e_nsent++;
+               }
        }
 
        }
 
+       /*
+       **  Restore state and return.
+       */
+
        errno = 0;
        define('g', (char *) NULL, e);
        errno = 0;
        define('g', (char *) NULL, e);
+       define('<', (char *) NULL, e);
        return (rcode);
 }
 \f/*
        return (rcode);
 }
 \f/*
@@ -433,7 +540,7 @@ markfailure(e, q, rcode)
 {
        if (rcode == EX_OK)
                return;
 {
        if (rcode == EX_OK)
                return;
-       else if (rcode != EX_TEMPFAIL)
+       else if (rcode != EX_TEMPFAIL && rcode != EX_IOERR && rcode != EX_OSERR)
                q->q_flags |= QBADADDR;
        else if (curtime() > e->e_ctime + TimeOut)
        {
                q->q_flags |= QBADADDR;
        else if (curtime() > e->e_ctime + TimeOut)
        {
@@ -451,6 +558,7 @@ markfailure(e, q, rcode)
                }
                q->q_flags |= QBADADDR;
                e->e_flags |= EF_TIMEOUT;
                }
                q->q_flags |= QBADADDR;
                e->e_flags |= EF_TIMEOUT;
+               fprintf(e->e_xfp, "421 %s... Message timed out\n", q->q_paddr);
        }
        else
                q->q_flags |= QQUEUEUP;
        }
        else
                q->q_flags |= QQUEUEUP;
@@ -478,22 +586,22 @@ markfailure(e, q, rcode)
 */
 
 # define NFORKTRIES    5
 */
 
 # define NFORKTRIES    5
-# ifdef VMUNIX
-# define XFORK vfork
-# else VMUNIX
-# define XFORK fork
-# endif VMUNIX
+
+# ifndef FORK
+# define FORK  fork
+# endif
 
 # define DOFORK(fORKfN) \
 {\
        register int i;\
 \
 
 # define DOFORK(fORKfN) \
 {\
        register int i;\
 \
-       for (i = NFORKTRIES; i-- > 0; )\
+       for (i = NFORKTRIES; --i >= 0; )\
        {\
                pid = fORKfN();\
                if (pid >= 0)\
                        break;\
        {\
                pid = fORKfN();\
                if (pid >= 0)\
                        break;\
-               sleep(NFORKTRIES - i);\
+               if (i > 0)\
+                       sleep((unsigned) NFORKTRIES - i);\
        }\
 }
 \f/*
        }\
 }
 \f/*
@@ -519,63 +627,6 @@ dofork()
        return (pid);
 }
 \f/*
        return (pid);
 }
 \f/*
-**  SENDOFF -- send off call to mailer & collect response.
-**
-**     Parameters:
-**             e -- the envelope to mail.
-**             m -- mailer descriptor.
-**             pvp -- parameter vector to send to it.
-**             ctladdr -- an address pointer controlling the
-**                     user/groupid etc. of the mailer.
-**
-**     Returns:
-**             exit status of mailer.
-**
-**     Side Effects:
-**             none.
-*/
-
-sendoff(e, m, pvp, ctladdr)
-       register ENVELOPE *e;
-       MAILER *m;
-       char **pvp;
-       ADDRESS *ctladdr;
-{
-       auto FILE *mfile;
-       auto FILE *rfile;
-       register int i;
-       int pid;
-
-       /*
-       **  Create connection to mailer.
-       */
-
-       pid = openmailer(m, pvp, ctladdr, FALSE, &mfile, &rfile);
-       if (pid < 0)
-               return (-1);
-
-       /*
-       **  Format and send message.
-       */
-
-       putfromline(mfile, m);
-       (*e->e_puthdr)(mfile, m, e);
-       putline("\n", mfile, m);
-       (*e->e_putbody)(mfile, m, e);
-       (void) fclose(mfile);
-
-       i = endmailer(pid, pvp[0]);
-
-       /* arrange a return receipt if requested */
-       if (e->e_receiptto != NULL && bitnset(M_LOCAL, m->m_flags))
-       {
-               e->e_flags |= EF_SENDRECEIPT;
-               /* do we want to send back more info? */
-       }
-
-       return (i);
-}
-\f/*
 **  ENDMAILER -- Wait for mailer to terminate.
 **
 **     We should never get fatal errors (e.g., segmentation
 **  ENDMAILER -- Wait for mailer to terminate.
 **
 **     We should never get fatal errors (e.g., segmentation
@@ -594,18 +645,26 @@ sendoff(e, m, pvp, ctladdr)
 **             none.
 */
 
 **             none.
 */
 
-endmailer(pid, name)
-       int pid;
+endmailer(mci, name)
+       register MCI *mci;
        char *name;
 {
        int st;
 
        char *name;
 {
        int st;
 
+       /* close any connections */
+       if (mci->mci_in != NULL)
+               (void) fclose(mci->mci_in);
+       if (mci->mci_out != NULL)
+               (void) fclose(mci->mci_out);
+       mci->mci_in = mci->mci_out = NULL;
+       mci->mci_state = MCIS_CLOSED;
+
        /* in the IPC case there is nothing to wait for */
        /* in the IPC case there is nothing to wait for */
-       if (pid == 0)
+       if (mci->mci_pid == 0)
                return (EX_OK);
 
        /* wait for the mailer process to die and collect status */
                return (EX_OK);
 
        /* wait for the mailer process to die and collect status */
-       st = waitfor(pid);
+       st = waitfor(mci->mci_pid);
        if (st == -1)
        {
                syserr("endmailer %s: wait", name);
        if (st == -1)
        {
                syserr("endmailer %s: wait", name);
@@ -615,9 +674,9 @@ endmailer(pid, name)
        /* see if it died a horrid death */
        if ((st & 0377) != 0)
        {
        /* see if it died a horrid death */
        if ((st & 0377) != 0)
        {
-               syserr("endmailer %s: stat %o", name, st);
-               ExitStat = EX_UNAVAILABLE;
-               return (EX_UNAVAILABLE);
+               syserr("mailer %s died with signal %o", name, st);
+               ExitStat = EX_TEMPFAIL;
+               return (EX_TEMPFAIL);
        }
 
        /* normal death -- return status */
        }
 
        /* normal death -- return status */
@@ -632,42 +691,38 @@ endmailer(pid, name)
 **             pvp -- parameter vector to pass to mailer.
 **             ctladdr -- controlling address for user.
 **             clever -- create a full duplex connection.
 **             pvp -- parameter vector to pass to mailer.
 **             ctladdr -- controlling address for user.
 **             clever -- create a full duplex connection.
-**             pmfile -- pointer to mfile (to mailer) connection.
-**             prfile -- pointer to rfile (from mailer) connection.
 **
 **     Returns:
 **
 **     Returns:
-**             pid of mailer ( > 0 ).
-**             -1 on error.
-**             zero on an IPC connection.
+**             The mail connection info struct for this connection.
+**             NULL on failure.
 **
 **     Side Effects:
 **             creates a mailer in a subprocess.
 */
 
 **
 **     Side Effects:
 **             creates a mailer in a subprocess.
 */
 
-openmailer(m, pvp, ctladdr, clever, pmfile, prfile)
+MCI *
+openmailer(m, pvp, ctladdr, clever, e)
        MAILER *m;
        char **pvp;
        ADDRESS *ctladdr;
        bool clever;
        MAILER *m;
        char **pvp;
        ADDRESS *ctladdr;
        bool clever;
-       FILE **pmfile;
-       FILE **prfile;
+       ENVELOPE *e;
 {
        int pid;
 {
        int pid;
+       register MCI *mci;
        int mpvect[2];
        int rpvect[2];
        int mpvect[2];
        int rpvect[2];
-       FILE *mfile;
-       FILE *rfile;
        extern FILE *fdopen();
 
        extern FILE *fdopen();
 
-# ifdef DEBUG
        if (tTd(11, 1))
        {
                printf("openmailer:");
                printav(pvp);
        }
        if (tTd(11, 1))
        {
                printf("openmailer:");
                printav(pvp);
        }
-# endif DEBUG
        errno = 0;
 
        errno = 0;
 
+       CurHostName = m->m_mailer;
+
        /*
        **  Deal with the special case of mail handled through an IPC
        **  connection.
        /*
        **  Deal with the special case of mail handled through an IPC
        **  connection.
@@ -677,21 +732,28 @@ openmailer(m, pvp, ctladdr, clever, pmfile, prfile)
        **  We also handle a debug version that just talks to stdin/out.
        */
 
        **  We also handle a debug version that just talks to stdin/out.
        */
 
-#ifdef DEBUG
        /* 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)
        {
-               *pmfile = stdout;
-               *prfile = stdin;
-               return (0);
+               mci = (MCI *) xalloc(sizeof *mci);
+               bzero((char *) mci, sizeof *mci);
+               mci->mci_in = stdin;
+               mci->mci_out = stdout;
+               mci->mci_state = clever ? MCIS_OPENING : MCIS_OPEN;
+               mci->mci_mailer = m;
        }
        }
-#endif DEBUG
-
-       if (strcmp(m->m_mailer, "[IPC]") == 0)
+       else if (strcmp(m->m_mailer, "[IPC]") == 0 ||
+                strcmp(m->m_mailer, "[TCP]") == 0)
        {
 #ifdef DAEMON
                register int i;
                register u_short port;
        {
 #ifdef DAEMON
                register int i;
                register u_short port;
+               char *curhost;
+               extern MCI *mci_get();
+               extern char *hostsignature();
+
+               CurHostName = pvp[1];
+               curhost = hostsignature(m, pvp[1], e);
 
                if (!clever)
                        syserr("non-clever IPC");
 
                if (!clever)
                        syserr("non-clever IPC");
@@ -699,156 +761,249 @@ openmailer(m, pvp, ctladdr, clever, pmfile, prfile)
                        port = atoi(pvp[2]);
                else
                        port = 0;
                        port = atoi(pvp[2]);
                else
                        port = 0;
-               i = makeconnection(pvp[1], port, pmfile, prfile);
-               if (i != EX_OK)
+               while (*curhost != '\0')
                {
                {
-                       ExitStat = i;
-                       return (-1);
-               }
-               else
-                       return (0);
-#else DAEMON
-               syserr("openmailer: no IPC");
-               return (-1);
-#endif DAEMON
-       }
-
-       /* create a pipe to shove the mail through */
-       if (pipe(mpvect) < 0)
-       {
-               syserr("openmailer: pipe (to mailer)");
-               return (-1);
-       }
+                       register char *p;
+                       char hostbuf[MAXNAME];
+
+                       /* pull the next host from the signature */
+                       p = strchr(curhost, ':');
+                       if (p == NULL)
+                               p = &curhost[strlen(curhost)];
+                       strncpy(hostbuf, curhost, p - curhost);
+                       hostbuf[p - curhost] = '\0';
+                       if (*p != '\0')
+                               p++;
+                       curhost = p;
+
+                       /* see if we already know that this host is fried */
+                       CurHostName = hostbuf;
+                       mci = mci_get(hostbuf, m);
+                       if (mci->mci_state != MCIS_CLOSED)
+                       {
+                               if (tTd(11, 1))
+                               {
+                                       printf("openmailer: ");
+                                       mci_dump(mci);
+                               }
+                               CurHostName = mci->mci_host;
+                               return mci;
+                       }
+                       mci->mci_mailer = m;
+                       if (mci->mci_exitstat != EX_OK)
+                               continue;
 
 
-#ifdef SMTP
-       /* if this mailer speaks smtp, create a return pipe */
-       if (clever && pipe(rpvect) < 0)
-       {
-               syserr("openmailer: pipe (from mailer)");
-               (void) close(mpvect[0]);
-               (void) close(mpvect[1]);
-               return (-1);
-       }
-#endif SMTP
+                       /* try the connection */
+                       setproctitle("%s %s: %s", e->e_id, hostbuf, "user open");
+                       message(Arpa_Info, "Connecting to %s (%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;
+                       if (i == EX_OK)
+                       {
+                               mci->mci_state = MCIS_OPENING;
+                               mci_cache(mci);
+                               break;
+                       }
+                       else if (tTd(11, 1))
+                               printf("openmailer: makeconnection => stat=%d, errno=%d\n",
+                                       i, errno);
 
 
-       /*
-       **  Actually fork the mailer process.
-       **      DOFORK is clever about retrying.
-       */
 
 
-       if (CurEnv->e_xfp != NULL)
-               (void) fflush(CurEnv->e_xfp);           /* for debugging */
-       (void) fflush(stdout);
-       DOFORK(XFORK);
-       /* pid is set by DOFORK */
-       if (pid < 0)
-       {
-               /* failure */
-               syserr("openmailer: cannot fork");
-               (void) close(mpvect[0]);
-               (void) close(mpvect[1]);
-#ifdef SMTP
-               if (clever)
-               {
-                       (void) close(rpvect[0]);
-                       (void) close(rpvect[1]);
+                       /* enter status of this host */
+                       setstat(i);
                }
                }
-#endif SMTP
-               return (-1);
+               mci->mci_pid = 0;
+#else /* no DAEMON */
+               syserr("openmailer: no IPC");
+               if (tTd(11, 1))
+                       printf("openmailer: NULL\n");
+               return NULL;
+#endif /* DAEMON */
        }
        }
-       else if (pid == 0)
+       else
        {
        {
-               /* child -- set up input & exec mailer */
-               /* make diagnostic output be standard output */
-               (void) signal(SIGINT, SIG_IGN);
-               (void) signal(SIGHUP, SIG_IGN);
-               (void) signal(SIGTERM, SIG_DFL);
-
-               /* arrange to filter standard & diag output of command */
-               if (clever)
+               /* create a pipe to shove the mail through */
+               if (pipe(mpvect) < 0)
                {
                {
-                       (void) close(rpvect[0]);
-                       (void) close(1);
-                       (void) dup(rpvect[1]);
-                       (void) close(rpvect[1]);
+                       syserr("openmailer: pipe (to mailer)");
+                       if (tTd(11, 1))
+                               printf("openmailer: NULL\n");
+                       return NULL;
                }
                }
-               else if (OpMode == MD_SMTP || HoldErrs)
+
+               /* if this mailer speaks smtp, create a return pipe */
+               if (clever && pipe(rpvect) < 0)
                {
                {
-                       /* put mailer output in transcript */
-                       (void) close(1);
-                       (void) dup(fileno(CurEnv->e_xfp));
+                       syserr("openmailer: pipe (from mailer)");
+                       (void) close(mpvect[0]);
+                       (void) close(mpvect[1]);
+                       if (tTd(11, 1))
+                               printf("openmailer: NULL\n");
+                       return NULL;
                }
                }
-               (void) close(2);
-               (void) dup(1);
 
 
-               /* arrange to get standard input */
-               (void) close(mpvect[1]);
-               (void) close(0);
-               if (dup(mpvect[0]) < 0)
+               /*
+               **  Actually fork the mailer process.
+               **      DOFORK is clever about retrying.
+               **
+               **      Dispose of SIGCHLD signal catchers that may be laying
+               **      around so that endmail will get it.
+               */
+
+               if (e->e_xfp != NULL)
+                       (void) fflush(e->e_xfp);                /* for debugging */
+               (void) fflush(stdout);
+# ifdef SIGCHLD
+               (void) signal(SIGCHLD, SIG_DFL);
+# endif /* SIGCHLD */
+               DOFORK(FORK);
+               /* pid is set by DOFORK */
+               if (pid < 0)
                {
                {
-                       syserr("Cannot dup to zero!");
-                       _exit(EX_OSERR);
+                       /* failure */
+                       syserr("openmailer: cannot fork");
+                       (void) close(mpvect[0]);
+                       (void) close(mpvect[1]);
+                       if (clever)
+                       {
+                               (void) close(rpvect[0]);
+                               (void) close(rpvect[1]);
+                       }
+                       if (tTd(11, 1))
+                               printf("openmailer: NULL\n");
+                       return NULL;
                }
                }
-               (void) close(mpvect[0]);
-               if (!bitnset(M_RESTR, m->m_flags))
+               else if (pid == 0)
                {
                {
-                       if (ctladdr->q_uid == 0)
+                       int i;
+                       int saveerrno;
+                       char *env[2];
+                       extern int DtableSize;
+
+                       /* child -- set up input & exec mailer */
+                       /* make diagnostic output be standard output */
+                       (void) signal(SIGINT, SIG_IGN);
+                       (void) signal(SIGHUP, SIG_IGN);
+                       (void) signal(SIGTERM, SIG_DFL);
+
+                       /* arrange to filter std & diag output of command */
+                       if (clever)
                        {
                        {
-                               (void) setgid(DefGid);
-                               (void) setuid(DefUid);
+                               (void) close(rpvect[0]);
+                               (void) close(1);
+                               (void) dup(rpvect[1]);
+                               (void) close(rpvect[1]);
                        }
                        }
-                       else
+                       else if (OpMode == MD_SMTP || HoldErrs)
                        {
                        {
-                               (void) setgid(ctladdr->q_gid);
-                               (void) setuid(ctladdr->q_uid);
+                               /* put mailer output in transcript */
+                               (void) close(1);
+                               (void) dup(fileno(e->e_xfp));
+                       }
+                       (void) close(2);
+                       (void) dup(1);
+
+                       /* arrange to get standard input */
+                       (void) close(mpvect[1]);
+                       (void) close(0);
+                       if (dup(mpvect[0]) < 0)
+                       {
+                               syserr("Cannot dup to zero!");
+                               _exit(EX_OSERR);
+                       }
+                       (void) close(mpvect[0]);
+                       if (!bitnset(M_RESTR, m->m_flags))
+                       {
+                               if (ctladdr == NULL || ctladdr->q_uid == 0)
+                               {
+                                       (void) setgid(DefGid);
+#ifndef SYSTEM5
+                                       (void) initgroups(DefUser, DefGid);
+#endif
+                                       (void) setuid(DefUid);
+                               }
+                               else
+                               {
+                                       (void) setgid(ctladdr->q_gid);
+#ifndef SYSTEM5
+                                       (void) initgroups(ctladdr->q_ruser?
+                                               ctladdr->q_ruser: ctladdr->q_user,
+                                               ctladdr->q_gid);
+#endif
+                                       (void) setuid(ctladdr->q_uid);
+                               }
+                       }
+
+                       /* arrange for all the files to be closed */
+                       for (i = 3; i < DtableSize; i++)
+                       {
+                               register int j;
+                               if ((j = fcntl(i, F_GETFD, 0)) != -1)
+                                       (void)fcntl(i, F_SETFD, j|1);
+                       }
+
+                       /* try to execute the mailer */
+                       env[0] = "AGENT=sendmail";
+                       env[1] = NULL;
+                       execve(m->m_mailer, pvp, env);
+                       saveerrno = errno;
+                       syserr("Cannot exec %s", m->m_mailer);
+                       if (m == LocalMailer)
+                               _exit(EX_TEMPFAIL);
+                       switch (saveerrno)
+                       {
+                         case EIO:
+                         case EAGAIN:
+                         case ENOMEM:
+# ifdef EPROCLIM
+                         case EPROCLIM:
+# endif
+                               _exit(EX_TEMPFAIL);
                        }
                        }
+                       _exit(EX_UNAVAILABLE);
                }
 
                /*
                }
 
                /*
-               **  We have to be careful with vfork - we can't mung up the
-               **  memory but we don't want the mailer to inherit any extra
-               **  open files.  Chances are the mailer won't
-               **  care about an extra file, but then again you never know.
-               **  Actually, we would like to close(fileno(pwf)), but it's
-               **  declared static so we can't.  But if we fclose(pwf), which
-               **  is what endpwent does, it closes it in the parent too and
-               **  the next getpwnam will be slower.  If you have a weird
-               **  mailer that chokes on the extra file you should do the
-               **  endpwent().                 -MRH
-               **
-               **  Similar comments apply to log.  However, openlog is
-               **  clever enough to set the FIOCLEX mode on the file,
-               **  so it will be closed automatically on the exec.
+               **  Set up return value.
                */
 
                */
 
-               closeall();
-
-               /* try to execute the mailer */
-               execv(m->m_mailer, pvp);
-
-               /* syserr fails because log is closed */
-               /* syserr("Cannot exec %s", m->m_mailer); */
-               printf("Cannot exec '%s' errno=%d\n", m->m_mailer, errno);
-               (void) fflush(stdout);
-               _exit(EX_UNAVAILABLE);
+               mci = (MCI *) xalloc(sizeof *mci);
+               bzero((char *) mci, sizeof *mci);
+               mci->mci_mailer = m;
+               mci->mci_state = clever ? MCIS_OPENING : MCIS_OPEN;
+               mci->mci_pid = pid;
+               (void) close(mpvect[0]);
+               mci->mci_out = fdopen(mpvect[1], "w");
+               if (clever)
+               {
+                       (void) close(rpvect[1]);
+                       mci->mci_in = fdopen(rpvect[0], "r");
+               }
+               else
+               {
+                       mci->mci_flags |= MCIF_TEMP;
+                       mci->mci_in = NULL;
+               }
        }
 
        /*
        }
 
        /*
-       **  Set up return value.
+       **  If we are in SMTP opening state, send initial protocol.
        */
 
        */
 
-       (void) close(mpvect[0]);
-       mfile = fdopen(mpvect[1], "w");
-       if (clever)
+       if (clever && mci->mci_state != MCIS_CLOSED)
        {
        {
-               (void) close(rpvect[1]);
-               rfile = fdopen(rpvect[0], "r");
+               smtpinit(m, mci, e);
+       }
+       if (tTd(11, 1))
+       {
+               printf("openmailer: ");
+               mci_dump(mci);
        }
 
        }
 
-       *pmfile = mfile;
-       *prfile = rfile;
-
-       return (pid);
+       return mci;
 }
 \f/*
 **  GIVERESPONSE -- Interpret an error response from a mailer
 }
 \f/*
 **  GIVERESPONSE -- Interpret an error response from a mailer
@@ -876,6 +1031,9 @@ giveresponse(stat, m, e)
        extern char *SysExMsg[];
        register int i;
        extern int N_SysEx;
        extern char *SysExMsg[];
        register int i;
        extern int N_SysEx;
+#ifdef NAMED_BIND
+       extern int h_errno;
+#endif
        char buf[MAXLINE];
 
 #ifdef lint
        char buf[MAXLINE];
 
 #ifdef lint
@@ -898,27 +1056,45 @@ giveresponse(stat, m, e)
        }
        else if (stat == EX_TEMPFAIL)
        {
        }
        else if (stat == EX_TEMPFAIL)
        {
-               extern char *sys_errlist[];
-               extern int sys_nerr;
-
                (void) strcpy(buf, SysExMsg[i]);
                (void) strcpy(buf, SysExMsg[i]);
-               if (errno != 0)
+#ifdef NAMED_BIND
+               if (h_errno == TRY_AGAIN)
                {
                {
-                       (void) strcat(buf, ": ");
-                       if (errno > 0 && errno < sys_nerr)
-                               (void) strcat(buf, sys_errlist[errno]);
+                       extern char *errstring();
+
+                       statmsg = errstring(h_errno+MAX_ERRNO);
+               }
+               else
+#endif
+               {
+                       if (errno != 0)
+                       {
+                               extern char *errstring();
+
+                               statmsg = errstring(errno);
+                       }
                        else
                        {
                        else
                        {
-                               char xbuf[30];
+#ifdef SMTP
+                               extern char SmtpError[];
 
 
-                               (void) sprintf(xbuf, "Error %d", errno);
-                               (void) strcat(buf, xbuf);
+                               statmsg = SmtpError;
+#else /* SMTP */
+                               statmsg = NULL;
+#endif /* SMTP */
                        }
                }
                        }
                }
+               if (statmsg != NULL && statmsg[0] != '\0')
+               {
+                       (void) strcat(buf, ": ");
+                       (void) strcat(buf, statmsg);
+               }
                statmsg = buf;
        }
        else
                statmsg = buf;
        }
        else
+       {
                statmsg = SysExMsg[i];
                statmsg = SysExMsg[i];
+       }
 
        /*
        **  Print the message as appropriate
 
        /*
        **  Print the message as appropriate
@@ -940,7 +1116,7 @@ giveresponse(stat, m, e)
        */
 
        if (LogLevel > ((stat == 0 || stat == EX_TEMPFAIL) ? 3 : 2))
        */
 
        if (LogLevel > ((stat == 0 || stat == EX_TEMPFAIL) ? 3 : 2))
-               logdelivery(&statmsg[4]);
+               logdelivery(&statmsg[4], e);
 
        if (stat != EX_TEMPFAIL)
                setstat(stat);
 
        if (stat != EX_TEMPFAIL)
                setstat(stat);
@@ -951,6 +1127,9 @@ giveresponse(stat, m, e)
                e->e_message = newstr(&statmsg[4]);
        }
        errno = 0;
                e->e_message = newstr(&statmsg[4]);
        }
        errno = 0;
+#ifdef NAMED_BIND
+       h_errno = 0;
+#endif
 }
 \f/*
 **  LOGDELIVERY -- log the delivery in the system log
 }
 \f/*
 **  LOGDELIVERY -- log the delivery in the system log
@@ -965,15 +1144,16 @@ giveresponse(stat, m, e)
 **             none
 */
 
 **             none
 */
 
-logdelivery(stat)
+logdelivery(stat, e)
        char *stat;
        char *stat;
+       register ENVELOPE *e;
 {
        extern char *pintvl();
 
 # ifdef LOG
 {
        extern char *pintvl();
 
 # ifdef LOG
-       syslog(LOG_INFO, "%s: to=%s, delay=%s, stat=%s", CurEnv->e_id,
-              CurEnv->e_to, pintvl(curtime() - CurEnv->e_ctime, TRUE), stat);
-# endif LOG
+       syslog(LOG_INFO, "%s: to=%s, delay=%s, stat=%s", e->e_id,
+              e->e_to, pintvl(curtime() - e->e_ctime, TRUE), stat);
+# endif /* LOG */
 }
 \f/*
 **  PUTFROMLINE -- output a UNIX-style from line (or whatever)
 }
 \f/*
 **  PUTFROMLINE -- output a UNIX-style from line (or whatever)
@@ -982,11 +1162,10 @@ logdelivery(stat)
 **     managed to extract a date already, use that; otherwise,
 **     use the current date/time.
 **
 **     managed to extract a date already, use that; otherwise,
 **     use the current date/time.
 **
-**     One of the ugliest hacks seen by human eyes is
-**     contained herein: UUCP wants those stupid
-**     "emote from <host>" lines.  Why oh why does a
-**     well-meaning programmer such as myself have to
-**     deal with this kind of antique garbage????
+**     One of the ugliest hacks seen by human eyes is contained herein:
+**     UUCP wants those stupid "remote from <host>" lines.  Why oh why
+**     does a well-meaning programmer such as myself have to deal with
+**     this kind of antique garbage????
 **
 **     Parameters:
 **             fp -- the file to output to.
 **
 **     Parameters:
 **             fp -- the file to output to.
@@ -999,11 +1178,12 @@ logdelivery(stat)
 **             outputs some text to fp.
 */
 
 **             outputs some text to fp.
 */
 
-putfromline(fp, m)
+putfromline(fp, m, e)
        register FILE *fp;
        register MAILER *m;
        register FILE *fp;
        register MAILER *m;
+       ENVELOPE *e;
 {
 {
-       char *template = "$l\n";
+       char *template = "\001l\n";
        char buf[MAXLINE];
        extern char SentDate[];
 
        char buf[MAXLINE];
        extern char SentDate[];
 
@@ -1016,19 +1196,19 @@ putfromline(fp, m)
                char *bang;
                char xbuf[MAXLINE];
 
                char *bang;
                char xbuf[MAXLINE];
 
-               expand("$g", buf, &buf[sizeof buf - 1], CurEnv);
-               bang = index(buf, '!');
+               expand("\001<", buf, &buf[sizeof buf - 1], e);
+               bang = strchr(buf, '!');
                if (bang == NULL)
                        syserr("No ! in UUCP! (%s)", buf);
                else
                {
                        *bang++ = '\0';
                if (bang == NULL)
                        syserr("No ! in UUCP! (%s)", buf);
                else
                {
                        *bang++ = '\0';
-                       (void) sprintf(xbuf, "From %s  $d remote from %s\n", bang, buf);
+                       (void) sprintf(xbuf, "From %s  \001d remote from %s\n", bang, buf);
                        template = xbuf;
                }
        }
                        template = xbuf;
                }
        }
-# endif UGLYUUCP
-       expand(template, buf, &buf[sizeof buf - 1], CurEnv);
+# endif /* UGLYUUCP */
+       expand(template, buf, &buf[sizeof buf - 1], e);
        putline(buf, fp, m);
 }
 \f/*
        putline(buf, fp, m);
 }
 \f/*
@@ -1063,7 +1243,8 @@ putbody(fp, m, e)
                {
                        e->e_dfp = fopen(e->e_df, "r");
                        if (e->e_dfp == NULL)
                {
                        e->e_dfp = fopen(e->e_df, "r");
                        if (e->e_dfp == NULL)
-                               syserr("Cannot open %s", e->e_df);
+                               syserr("putbody: Cannot open %s for %s from %s",
+                               e->e_df, e->e_to, e->e_from);
                }
                else
                        putline("<<< No Message Collected >>>", fp, m);
                }
                else
                        putline("<<< No Message Collected >>>", fp, m);
@@ -1072,7 +1253,12 @@ putbody(fp, m, e)
        {
                rewind(e->e_dfp);
                while (!ferror(fp) && fgets(buf, sizeof buf, e->e_dfp) != NULL)
        {
                rewind(e->e_dfp);
                while (!ferror(fp) && fgets(buf, sizeof buf, e->e_dfp) != NULL)
+               {
+                       if (buf[0] == 'F' && bitnset(M_ESCFROM, m->m_flags) &&
+                           strncmp(buf, "From ", 5) == 0)
+                               (void) putc('>', fp);
                        putline(buf, fp, m);
                        putline(buf, fp, m);
+               }
 
                if (ferror(e->e_dfp))
                {
 
                if (ferror(e->e_dfp))
                {
@@ -1115,12 +1301,14 @@ putbody(fp, m, e)
 **             none.
 */
 
 **             none.
 */
 
-mailfile(filename, ctladdr)
+mailfile(filename, ctladdr, e)
        char *filename;
        ADDRESS *ctladdr;
        char *filename;
        ADDRESS *ctladdr;
+       register ENVELOPE *e;
 {
        register FILE *f;
        register int pid;
 {
        register FILE *f;
        register int pid;
+       int mode;
 
        /*
        **  Fork so we can change permissions here.
 
        /*
        **  Fork so we can change permissions here.
@@ -1140,45 +1328,88 @@ mailfile(filename, ctladdr)
                (void) signal(SIGINT, SIG_DFL);
                (void) signal(SIGHUP, SIG_DFL);
                (void) signal(SIGTERM, SIG_DFL);
                (void) signal(SIGINT, SIG_DFL);
                (void) signal(SIGHUP, SIG_DFL);
                (void) signal(SIGTERM, SIG_DFL);
-               umask(OldUmask);
+               (void) umask(OldUmask);
+
                if (stat(filename, &stb) < 0)
                if (stat(filename, &stb) < 0)
-               {
-                       errno = 0;
                        stb.st_mode = 0666;
                        stb.st_mode = 0666;
-               }
+               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 (bitset(0111, stb.st_mode))
                        exit(EX_CANTCREAT);
                if (ctladdr == NULL)
-                       ctladdr = &CurEnv->e_from;
-               if (!bitset(S_ISGID, stb.st_mode) || setgid(stb.st_gid) < 0)
+                       ctladdr = &e->e_from;
+               else
+               {
+                       /* 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)
+               {
+                       e->e_dfp = fopen(e->e_df, "r");
+                       if (e->e_dfp == NULL)
+                       {
+                               syserr("mailfile: Cannot open %s for %s from %s",
+                                       e->e_df, e->e_to, e->e_from);
+                       }
+               }
+
+               if (!bitset(S_ISGID, mode) || setgid(stb.st_gid) < 0)
                {
                        if (ctladdr->q_uid == 0)
                {
                        if (ctladdr->q_uid == 0)
+                       {
                                (void) setgid(DefGid);
                                (void) setgid(DefGid);
+#ifndef SYSTEM5
+                               (void) initgroups(DefUser, DefGid);
+#endif
+                       }
                        else
                        else
+                       {
                                (void) setgid(ctladdr->q_gid);
                                (void) setgid(ctladdr->q_gid);
+#ifndef SYSTEM5
+                               (void) initgroups(ctladdr->q_ruser ?
+                                       ctladdr->q_ruser : ctladdr->q_user,
+                                       ctladdr->q_gid);
+#endif
+                       }
                }
                }
-               if (!bitset(S_ISUID, stb.st_mode) || setuid(stb.st_uid) < 0)
+               if (!bitset(S_ISUID, mode) || setuid(stb.st_uid) < 0)
                {
                        if (ctladdr->q_uid == 0)
                                (void) setuid(DefUid);
                        else
                                (void) setuid(ctladdr->q_uid);
                }
                {
                        if (ctladdr->q_uid == 0)
                                (void) setuid(DefUid);
                        else
                                (void) setuid(ctladdr->q_uid);
                }
+               FileName = filename;
+               LineNumber = 0;
                f = dfopen(filename, "a");
                if (f == NULL)
                f = dfopen(filename, "a");
                if (f == NULL)
+               {
+                       message("cannot open");
                        exit(EX_CANTCREAT);
                        exit(EX_CANTCREAT);
+               }
 
 
-               putfromline(f, ProgMailer);
-               (*CurEnv->e_puthdr)(f, ProgMailer, CurEnv);
+               putfromline(f, ProgMailer, e);
+               (*e->e_puthdr)(f, ProgMailer, e);
                putline("\n", f, ProgMailer);
                putline("\n", f, ProgMailer);
-               (*CurEnv->e_putbody)(f, ProgMailer, CurEnv);
+               (*e->e_putbody)(f, ProgMailer, e);
                putline("\n", f, ProgMailer);
                putline("\n", f, ProgMailer);
+               if (ferror(f))
+               {
+                       message("I/O error");
+                       setstat(EX_IOERR);
+               }
                (void) fclose(f);
                (void) fflush(stdout);
 
                /* reset ISUID & ISGID bits for paranoid systems */
                (void) chmod(filename, (int) stb.st_mode);
                (void) fclose(f);
                (void) fflush(stdout);
 
                /* reset ISUID & ISGID bits for paranoid systems */
                (void) chmod(filename, (int) stb.st_mode);
-               exit(EX_OK);
+               exit(ExitStat);
                /*NOTREACHED*/
        }
        else
                /*NOTREACHED*/
        }
        else
@@ -1191,6 +1422,7 @@ mailfile(filename, ctladdr)
                        return (EX_UNAVAILABLE);
                else
                        return ((st >> 8) & 0377);
                        return (EX_UNAVAILABLE);
                else
                        return ((st >> 8) & 0377);
+               /*NOTREACHED*/
        }
 }
 \f/*
        }
 }
 \f/*
@@ -1198,7 +1430,8 @@ mailfile(filename, ctladdr)
 **
 **     Parameters:
 **             e -- the envelope to send.
 **
 **     Parameters:
 **             e -- the envelope to send.
-**             mode -- the delivery mode to use.
+**             mode -- the delivery mode to use.  If SM_DEFAULT, use
+**                     the current SendMode.
 **
 **     Returns:
 **             none.
 **
 **     Returns:
 **             none.
@@ -1217,14 +1450,26 @@ sendall(e, mode)
        register ADDRESS *q;
        bool oldverbose;
        int pid;
        register ADDRESS *q;
        bool oldverbose;
        int pid;
+# ifdef LOCKF
+       struct flock lfd;
+# endif
+
+       /* determine actual delivery mode */
+       if (mode == SM_DEFAULT)
+       {
+               extern bool shouldqueue();
+
+               if (shouldqueue(e->e_msgpriority, e->e_ctime))
+                       mode = SM_QUEUE;
+               else
+                       mode = SendMode;
+       }
 
 
-#ifdef DEBUG
        if (tTd(13, 1))
        {
                printf("\nSENDALL: mode %c, sendqueue:\n", mode);
                printaddr(e->e_sendqueue, TRUE);
        }
        if (tTd(13, 1))
        {
                printf("\nSENDALL: mode %c, sendqueue:\n", mode);
                printaddr(e->e_sendqueue, TRUE);
        }
-#endif DEBUG
 
        /*
        **  Do any preprocessing necessary for the mode we are running.
 
        /*
        **  Do any preprocessing necessary for the mode we are running.
@@ -1234,9 +1479,12 @@ sendall(e, mode)
 
        CurEnv = e;
 
 
        CurEnv = e;
 
-       if (e->e_hopcount > MAXHOP)
+       if (e->e_hopcount > MaxHopCount)
        {
        {
-               syserr("sendall: too many hops (%d max)", MAXHOP);
+               errno = 0;
+               syserr("sendall: too many hops %d (%d max): from %s, to %s",
+                       e->e_hopcount, MaxHopCount, e->e_from.q_paddr,
+                       e->e_sendqueue->q_paddr);
                return;
        }
 
                return;
        }
 
@@ -1245,7 +1493,12 @@ sendall(e, mode)
                extern ADDRESS *recipient();
 
                e->e_from.q_flags |= QDONTSEND;
                extern ADDRESS *recipient();
 
                e->e_from.q_flags |= QDONTSEND;
-               (void) recipient(&e->e_from, &e->e_sendqueue);
+               if (tTd(13, 5))
+               {
+                       printf("sendall: QDONTSEND ");
+                       printaddr(&e->e_from, FALSE);
+               }
+               (void) recipient(&e->e_from, &e->e_sendqueue, e);
        }
 
 # ifdef QUEUE
        }
 
 # ifdef QUEUE
@@ -1253,7 +1506,7 @@ sendall(e, mode)
             (mode != SM_VERIFY && SuperSafe)) &&
            !bitset(EF_INQUEUE, e->e_flags))
                queueup(e, TRUE, mode == SM_QUEUE);
             (mode != SM_VERIFY && SuperSafe)) &&
            !bitset(EF_INQUEUE, e->e_flags))
                queueup(e, TRUE, mode == SM_QUEUE);
-#endif QUEUE
+#endif /* QUEUE */
 
        oldverbose = Verbose;
        switch (mode)
 
        oldverbose = Verbose;
        switch (mode)
@@ -1263,22 +1516,57 @@ sendall(e, mode)
                break;
 
          case SM_QUEUE:
                break;
 
          case SM_QUEUE:
+  queueonly:
                e->e_flags |= EF_INQUEUE|EF_KEEPQUEUE;
                return;
 
          case SM_FORK:
                if (e->e_xfp != NULL)
                        (void) fflush(e->e_xfp);
                e->e_flags |= EF_INQUEUE|EF_KEEPQUEUE;
                return;
 
          case SM_FORK:
                if (e->e_xfp != NULL)
                        (void) fflush(e->e_xfp);
+
+# ifdef LOCKF
+               /*
+               **  Since lockf has the interesting semantic that the
+               **  lock is lost when we fork, we have to risk losing
+               **  the lock here by closing before the fork, and then
+               **  trying to get it back in the child.
+               */
+
+               if (e->e_lockfp != NULL)
+               {
+                       (void) fclose(e->e_lockfp);
+                       e->e_lockfp = NULL;
+               }
+# endif /* LOCKF */
+
                pid = fork();
                if (pid < 0)
                {
                pid = fork();
                if (pid < 0)
                {
-                       mode = SM_DELIVER;
-                       break;
+                       goto queueonly;
                }
                else if (pid > 0)
                {
                        /* be sure we leave the temp files to our child */
                        e->e_id = e->e_df = NULL;
                }
                else if (pid > 0)
                {
                        /* be sure we leave the temp files to our child */
                        e->e_id = e->e_df = NULL;
+# ifndef LOCKF
+                       if (e->e_lockfp != NULL)
+                       {
+                               (void) fclose(e->e_lockfp);
+                               e->e_lockfp = NULL;
+                       }
+# endif
+
+                       /* close any random open files in the envelope */
+                       if (e->e_dfp != NULL)
+                       {
+                               (void) fclose(e->e_dfp);
+                               e->e_dfp = NULL;
+                       }
+                       if (e->e_xfp != NULL)
+                       {
+                               (void) fclose(e->e_xfp);
+                               e->e_xfp = NULL;
+                       }
                        return;
                }
 
                        return;
                }
 
@@ -1289,6 +1577,27 @@ sendall(e, mode)
                /* be sure we are immune from the terminal */
                disconnect(FALSE);
 
                /* be sure we are immune from the terminal */
                disconnect(FALSE);
 
+# ifdef LOCKF
+               /*
+               **  Now try to get our lock back.
+               */
+
+               lfd.l_type = F_WRLCK;
+               lfd.l_whence = lfd.l_start = lfd.l_len = 0;
+               e->e_lockfp = fopen(queuename(e, 'q'), "r+");
+               if (e->e_lockfp == NULL ||
+                   fcntl(fileno(e->e_lockfp), F_SETLK, &lfd) < 0)
+               {
+                       /* oops....  lost it */
+# ifdef LOG
+                       if (LogLevel > 5)
+                               syslog(LOG_NOTICE, "%s: lost lock: %m",
+                                       e->e_id);
+# endif /* LOG */
+                       exit(EX_OK);
+               }
+# endif /* LOCKF */
+
                break;
        }
 
                break;
        }
 
@@ -1296,6 +1605,7 @@ sendall(e, mode)
        **  Run through the list and send everything.
        */
 
        **  Run through the list and send everything.
        */
 
+       e->e_nsent = 0;
        for (q = e->e_sendqueue; q != NULL; q = q->q_next)
        {
                if (mode == SM_VERIFY)
        for (q = e->e_sendqueue; q != NULL; q = q->q_next)
        {
                if (mode == SM_VERIFY)
@@ -1304,8 +1614,21 @@ sendall(e, mode)
                        if (!bitset(QDONTSEND|QBADADDR, q->q_flags))
                                message(Arpa_Info, "deliverable");
                }
                        if (!bitset(QDONTSEND|QBADADDR, q->q_flags))
                                message(Arpa_Info, "deliverable");
                }
-               else
+               else if (!bitset(QDONTSEND, q->q_flags))
+               {
+# ifdef QUEUE
+                       /*
+                       **  Checkpoint the send list every few addresses
+                       */
+
+                       if (e->e_nsent >= CheckpointInterval)
+                       {
+                               queueup(e, TRUE, FALSE);
+                               e->e_nsent = 0;
+                       }
+# endif /* QUEUE */
                        (void) deliver(e, q);
                        (void) deliver(e, q);
+               }
        }
        Verbose = oldverbose;
 
        }
        Verbose = oldverbose;
 
@@ -1320,13 +1643,11 @@ sendall(e, mode)
        {
                register ADDRESS *qq;
 
        {
                register ADDRESS *qq;
 
-# ifdef DEBUG
                if (tTd(13, 3))
                {
                        printf("Checking ");
                        printaddr(q, FALSE);
                }
                if (tTd(13, 3))
                {
                        printf("Checking ");
                        printaddr(q, FALSE);
                }
-# endif DEBUG
 
                /* only send errors if the message failed */
                if (!bitset(QBADADDR, q->q_flags))
 
                /* only send errors if the message failed */
                if (!bitset(QBADADDR, q->q_flags))
@@ -1348,25 +1669,138 @@ sendall(e, mode)
                                (void) strcat(obuf, "owner");
                        else
                                (void) strcat(obuf, qq->q_user);
                                (void) strcat(obuf, "owner");
                        else
                                (void) strcat(obuf, qq->q_user);
+                       if (!bitnset(M_USR_UPPER, qq->q_mailer->m_flags))
+                               makelower(obuf);
                        if (aliaslookup(obuf) == NULL)
                                continue;
 
                        if (aliaslookup(obuf) == NULL)
                                continue;
 
-# ifdef DEBUG
                        if (tTd(13, 4))
                                printf("Errors to %s\n", obuf);
                        if (tTd(13, 4))
                                printf("Errors to %s\n", obuf);
-# endif DEBUG
 
                        /* owner list exists -- add it to the error queue */
 
                        /* owner list exists -- add it to the error queue */
-                       sendtolist(obuf, (ADDRESS *) NULL, &e->e_errorqueue);
-                       ErrorMode == EM_MAIL;
+                       sendtolist(obuf, (ADDRESS *) NULL, &e->e_errorqueue, e);
+
+                       /* and set the return path to point to it */
+                       e->e_returnpath = newstr(obuf);
+
+                       ErrorMode = EM_MAIL;
                        break;
                }
 
                /* if we did not find an owner, send to the sender */
                if (qq == NULL && bitset(QBADADDR, q->q_flags))
                        break;
                }
 
                /* if we did not find an owner, send to the sender */
                if (qq == NULL && bitset(QBADADDR, q->q_flags))
-                       sendtolist(e->e_from.q_paddr, qq, &e->e_errorqueue);
+                       sendtolist(e->e_from.q_paddr, qq, &e->e_errorqueue, e);
        }
 
        if (mode == SM_FORK)
                finis();
 }
        }
 
        if (mode == SM_FORK)
                finis();
 }
+\f/*
+**  HOSTSIGNATURE -- return the "signature" for a host.
+**
+**     The signature describes how we are going to send this -- it
+**     can be just the hostname (for non-Internet hosts) or can be
+**     an ordered list of MX hosts.
+**
+**     Parameters:
+**             m -- the mailer describing this host.
+**             host -- the host name.
+**             e -- the current envelope.
+**
+**     Returns:
+**             The signature for this host.
+**
+**     Side Effects:
+**             Can tweak the symbol table.
+*/
+
+char *
+hostsignature(m, host, e)
+       register MAILER *m;
+       char *host;
+       ENVELOPE *e;
+{
+       register char *p;
+       register STAB *s;
+       int i;
+       int len;
+#ifdef NAMED_BIND
+       int nmx;
+       auto int rcode;
+       char *mxhosts[MAXMXHOSTS + 1];
+       static char myhostbuf[MAXNAME];
+#endif
+
+       /*
+       **  Check to see if this uses IPC -- if not, it can't have MX records.
+       */
+
+       p = m->m_mailer;
+       if (strcmp(p, "[IPC]") != 0 && strcmp(p, "[TCP]") != 0)
+       {
+               /* just an ordinary mailer */
+               return host;
+       }
+
+       /*
+       **  If it is a numeric address, just return it.
+       */
+
+       if (host[0] == '[')
+               return host;
+
+       /*
+       **  Look it up in the symbol table.
+       */
+
+       s = stab(host, ST_HOSTSIG, ST_ENTER);
+       if (s->s_hostsig != NULL)
+               return s->s_hostsig;
+
+       /*
+       **  Not already there -- create a signature.
+       */
+
+#ifdef NAMED_BIND
+       if (myhostbuf[0] == '\0')
+               expand("\001j", myhostbuf, &myhostbuf[sizeof myhostbuf - 1], e);
+
+       nmx = getmxrr(host, mxhosts, myhostbuf, &rcode);
+       if (nmx <= 0)
+       {
+               register MCI *mci;
+               extern int errno;
+               extern MCI *mci_get();
+
+               /* update the connection info for this host */
+               mci = mci_get(host, m);
+               mci->mci_exitstat = rcode;
+               mci->mci_errno = errno;
+
+               /* and return the original host name as the signature */
+               s->s_hostsig = host;
+               return host;
+       }
+
+       len = 0;
+       for (i = 0; i < nmx; i++)
+       {
+               len += strlen(mxhosts[i]) + 1;
+       }
+       s->s_hostsig = p = xalloc(len);
+       for (i = 0; i < nmx; i++)
+       {
+               if (i != 0)
+                       *p++ = ':';
+               strcpy(p, mxhosts[i]);
+               p += strlen(p);
+       }
+       makelower(s->s_hostsig);
+#else
+       /* not using BIND -- the signature is just the host name */
+       s->s_hostsig = host;
+#endif
+       if (tTd(17, 1))
+               printf("hostsignature(%s) = %s\n", host, s->s_hostsig);
+       return s->s_hostsig;
+}