install correct aliases file
[unix-history] / usr / src / usr.sbin / sendmail / src / deliver.c
index 7abb432..82a8d86 100644 (file)
@@ -1,9 +1,35 @@
-# include <signal.h>
-# include <errno.h>
-# include "sendmail.h"
-# include <sys/stat.h>
-
-SCCSID(@(#)deliver.c   3.133           %G%);
+/*
+ * Copyright (c) 1983 Eric P. Allman
+ * Copyright (c) 1988 Regents of the University of California.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that the above copyright notice and this paragraph are
+ * duplicated in all such forms and that any documentation,
+ * advertising materials, and other materials related to such
+ * distribution and use acknowledge that the software was developed
+ * by the University of California, Berkeley.  The name of the
+ * University may not be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)deliver.c  5.25 (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.
@@ -36,24 +62,32 @@ deliver(e, firstto)
        char **pvp;
        register char **mvp;
        register char *p;
        char **pvp;
        register char **mvp;
        register char *p;
-       register MAILER *m;     /* mailer for this recipient */
-       extern bool checkcompat();
-       char *pv[MAXPV+1];
-       char tobuf[MAXLINE-50];         /* text line of to people */
-       char buf[MAXNAME];
+       register MAILER *m;             /* mailer for this recipient */
        ADDRESS *ctladdr;
        ADDRESS *ctladdr;
-       extern ADDRESS *getctladdr();
-       char tfrombuf[MAXNAME];         /* translated from person */
-       extern char **prescan();
        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 *pv[MAXPV+1];
+       char tobuf[MAXLINE-50];         /* text line of to people */
+       char buf[MAXNAME];
+       char tfrombuf[MAXNAME];         /* translated from person */
+       extern bool checkcompat();
+       extern ADDRESS *getctladdr();
+       extern char *remotename();
 
        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 NAMED_BIND
+
        m = to->q_mailer;
        host = to->q_host;
 
        m = to->q_mailer;
        host = to->q_host;
 
@@ -73,7 +107,7 @@ deliver(e, firstto)
        **              This should be on a per-mailer basis.
        */
 
        **              This should be on a per-mailer basis.
        */
 
-       if (NoConnect && !QueueRun && bitset(M_EXPENSIVE, m->m_flags) &&
+       if (NoConnect && !QueueRun && bitnset(M_EXPENSIVE, m->m_flags) &&
            !Verbose)
        {
                for (; to != NULL; to = to->q_next)
            !Verbose)
        {
                for (; to != NULL; to = to->q_next)
@@ -102,12 +136,8 @@ deliver(e, firstto)
        */
 
        /* rewrite from address, using rewriting rules */
        */
 
        /* rewrite from address, using rewriting rules */
-       expand("$f", buf, &buf[sizeof buf - 1], e);
-       mvp = prescan(buf, '\0');
-       rewrite(mvp, 3);
-       rewrite(mvp, 1);
-       rewrite(mvp, m->m_s_rwset);
-       cataddr(mvp, tfrombuf, sizeof tfrombuf);
+       expand("\001f", buf, &buf[sizeof buf - 1], e);
+       (void) strcpy(tfrombuf, remotename(buf, m, TRUE, TRUE));
 
        define('g', tfrombuf, e);               /* translated sender address */
        define('h', host, e);                   /* to host */
 
        define('g', tfrombuf, e);               /* translated sender address */
        define('h', host, e);                   /* to host */
@@ -116,13 +146,13 @@ deliver(e, firstto)
        *pvp++ = m->m_argv[0];
 
        /* insert -f or -r flag as appropriate */
        *pvp++ = m->m_argv[0];
 
        /* insert -f or -r flag as appropriate */
-       if (bitset(M_FOPT|M_ROPT, m->m_flags) && FromFlag)
+       if (FromFlag && (bitnset(M_FOPT, m->m_flags) || bitnset(M_ROPT, m->m_flags)))
        {
        {
-               if (bitset(M_FOPT, m->m_flags))
+               if (bitnset(M_FOPT, m->m_flags))
                        *pvp++ = "-f";
                else
                        *pvp++ = "-r";
                        *pvp++ = "-f";
                else
                        *pvp++ = "-r";
-               expand("$g", buf, &buf[sizeof buf - 1], e);
+               expand("\001g", buf, &buf[sizeof buf - 1], e);
                *pvp++ = newstr(buf);
        }
 
                *pvp++ = newstr(buf);
        }
 
@@ -135,7 +165,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 = index(p, '\001')) != NULL)
                        if (*++p == 'u')
                                break;
                if (p != NULL)
                        if (*++p == 'u')
                                break;
                if (p != NULL)
@@ -183,7 +213,7 @@ deliver(e, firstto)
        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 */
-               if (tobuf[0] != '\0' && !bitset(M_MUSER, m->m_flags))
+               if (tobuf[0] != '\0' && !bitnset(M_MUSER, m->m_flags))
                        break;
 
                /* if already sent or not for this host, don't send */
                        break;
 
                /* if already sent or not for this host, don't send */
@@ -217,9 +247,16 @@ deliver(e, firstto)
                **  talk to each other.
                */
 
                **  talk to each other.
                */
 
+               if (m->m_maxsize != 0 && e->e_msgsize > m->m_maxsize)
+               {
+                       NoReturn = TRUE;
+                       usrerr("Message is too large; %ld bytes max", m->m_maxsize);
+                       giveresponse(EX_UNAVAILABLE, m, e);
+                       continue;
+               }
                if (!checkcompat(to))
                {
                if (!checkcompat(to))
                {
-                       giveresponse(EX_UNAVAILABLE, m);
+                       giveresponse(EX_UNAVAILABLE, m, e);
                        continue;
                }
 
                        continue;
                }
 
@@ -228,7 +265,7 @@ deliver(e, firstto)
                **      about them.
                */
 
                **      about them.
                */
 
-               if (bitset(M_STRIPQ, m->m_flags))
+               if (bitnset(M_STRIPQ, m->m_flags))
                {
                        stripquotes(user, TRUE);
                        stripquotes(host, TRUE);
                {
                        stripquotes(user, TRUE);
                        stripquotes(host, TRUE);
@@ -271,7 +308,7 @@ deliver(e, firstto)
                        if (user[0] == '/')
                        {
                                rcode = mailfile(user, getctladdr(to));
                        if (user[0] == '/')
                        {
                                rcode = mailfile(user, getctladdr(to));
-                               giveresponse(rcode, m);
+                               giveresponse(rcode, m, e);
                                continue;
                        }
                }
                                continue;
                        }
                }
@@ -286,8 +323,7 @@ deliver(e, firstto)
                tochain = to;
 
                /* create list of users for error messages */
                tochain = to;
 
                /* create list of users for error messages */
-               if (tobuf[0] != '\0')
-                       (void) strcat(tobuf, ",");
+               (void) strcat(tobuf, ",");
                (void) strcat(tobuf, to->q_paddr);
                define('u', user, e);           /* to user */
                define('z', to->q_home, e);     /* user's home */
                (void) strcat(tobuf, to->q_paddr);
                define('u', user, e);           /* to user */
                define('z', to->q_home, e);     /* user's home */
@@ -316,7 +352,7 @@ deliver(e, firstto)
        }
 
        /* print out messages as full list */
        }
 
        /* print out messages as full list */
-       e->e_to = tobuf;
+       e->e_to = tobuf + 1;
 
        /*
        **  Fill out any parameters after the $u parameter.
 
        /*
        **  Fill out any parameters after the $u parameter.
@@ -339,62 +375,136 @@ 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
+#ifdef NAMED_BIND
+       _res.options &= ~(RES_DEFNAMES | RES_DNSRCH);           /* XXX */
+#endif
+#ifdef SMTP
        if (clever)
        {
        if (clever)
        {
-               /* send the initial SMTP protocol */
-               rcode = smtpinit(m, pv, (ADDRESS *) NULL);
-
-               /* send the recipient list */
-               for (to = tochain; to != NULL; to = to->q_tchain)
+               expand("\001w", buf, &buf[sizeof(buf) - 1], e);
+               rcode = EX_OK;
+#ifdef NAMED_BIND
+               if (host[0] != '[')
                {
                {
-                       int i;
-
-                       if (rcode == EX_OK)
-                               i = smtprcpt(to);
-                       else
-                               i = rcode;
-                       if (i != EX_OK)
-                       {
-                               if (i == EX_TEMPFAIL)
-                                       to->q_flags |= QQUEUEUP;
-                               else
-                                       to->q_flags |= QBADADDR;
-                               giveresponse(rcode, m);
+                       Nmx = getmxrr(host, MxHosts, buf, &rcode);
+               }
+               else
+#endif
+               {
+                       Nmx = 1;
+                       MxHosts[0] = host;
+               }
+               if (Nmx >= 0)
+               {
+                       message(Arpa_Info, "Connecting to %s (%s)...",
+                           MxHosts[0], m->m_name);
+                       if ((rcode = smtpinit(m, pv)) == EX_OK) {
+                               register char *t = tobuf;
+                               register int i;
+
+                               /* send the recipient list */
+                               tobuf[0] = '\0';
+                               for (to = tochain; to; to = to->q_tchain) {
+                                       e->e_to = to->q_paddr;
+                                       if ((i = smtprcpt(to, m)) != EX_OK) {
+                                               markfailure(e, to, i);
+                                               giveresponse(i, m, e);
+                                       }
+                                       else {
+                                               *t++ = ',';
+                                               for (p = to->q_paddr; *p; *t++ = *p++);
+                                       }
+                               }
+
+                               /* now send the data */
+                               if (tobuf[0] == '\0')
+                                       e->e_to = NULL;
+                               else {
+                                       e->e_to = tobuf + 1;
+                                       rcode = smtpdata(m, e);
+                               }
+
+                               /* now close the connection */
+                               smtpquit(m);
                        }
                }
                        }
                }
-
-               /* now send the closing protocol */
-               if (rcode == EX_OK)
-                       rcode = smtpfinish(m, e);
-               if (rcode != EX_OK)
-                       giveresponse(rcode, m);
-               smtpquit(pv[0], rcode == EX_OK);
        }
        else
        }
        else
-# endif SMTP
+#endif /* SMTP */
+       {
+               message(Arpa_Info, "Connecting to %s (%s)...", host, m->m_name);
                rcode = sendoff(e, m, pv, ctladdr);
                rcode = sendoff(e, m, pv, ctladdr);
+       }
+#ifdef NAMED_BIND
+       _res.options |= RES_DEFNAMES | RES_DNSRCH;      /* XXX */
+#endif
 
        /*
 
        /*
-       **  If we got a temporary failure, arrange to queue the
-       **  addressees.
+       **  Do final status disposal.
+       **      We check for something in tobuf for the SMTP case.
+       **      If we got a temporary failure, arrange to queue the
+       **              addressees.
        */
 
        */
 
-       if (rcode == EX_TEMPFAIL)
-       {
+       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)
-                       to->q_flags |= QQUEUEUP;
-       }
+                       markfailure(e, to, rcode);
 
        errno = 0;
        define('g', (char *) NULL, e);
        return (rcode);
 }
 \f/*
 
        errno = 0;
        define('g', (char *) NULL, e);
        return (rcode);
 }
 \f/*
+**  MARKFAILURE -- mark a failure on a specific address.
+**
+**     Parameters:
+**             e -- the envelope we are sending.
+**             q -- the address to mark.
+**             rcode -- the code signifying the particular failure.
+**
+**     Returns:
+**             none.
+**
+**     Side Effects:
+**             marks the address (and possibly the envelope) with the
+**                     failure so that an error will be returned or
+**                     the message will be queued, as appropriate.
+*/
+
+markfailure(e, q, rcode)
+       register ENVELOPE *e;
+       register ADDRESS *q;
+       int rcode;
+{
+       if (rcode == EX_OK)
+               return;
+       else if (rcode != EX_TEMPFAIL)
+               q->q_flags |= QBADADDR;
+       else if (curtime() > e->e_ctime + TimeOut)
+       {
+               extern char *pintvl();
+               char buf[MAXLINE];
+
+               if (!bitset(EF_TIMEOUT, e->e_flags))
+               {
+                       (void) sprintf(buf, "Cannot send message for %s",
+                               pintvl(TimeOut, FALSE));
+                       if (e->e_message != NULL)
+                               free(e->e_message);
+                       e->e_message = newstr(buf);
+                       message(Arpa_Info, buf);
+               }
+               q->q_flags |= QBADADDR;
+               e->e_flags |= EF_TIMEOUT;
+       }
+       else
+               q->q_flags |= QQUEUEUP;
+}
+\f/*
 **  DOFORK -- do a fork, retrying a couple of times on failure.
 **
 **     This MUST be a macro, since after a vfork we are running
 **  DOFORK -- do a fork, retrying a couple of times on failure.
 **
 **     This MUST be a macro, since after a vfork we are running
@@ -427,12 +537,13 @@ deliver(e, firstto)
 {\
        register int i;\
 \
 {\
        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/*
@@ -473,7 +584,7 @@ dofork()
 **     Side Effects:
 **             none.
 */
 **     Side Effects:
 **             none.
 */
-
+static
 sendoff(e, m, pvp, ctladdr)
        register ENVELOPE *e;
        MAILER *m;
 sendoff(e, m, pvp, ctladdr)
        register ENVELOPE *e;
        MAILER *m;
@@ -495,28 +606,20 @@ sendoff(e, m, pvp, ctladdr)
 
        /*
        **  Format and send message.
 
        /*
        **  Format and send message.
-       **      We ignore broken pipes so that the mailer need not read
-       **      its input if it is not convenient to do so (e.g., on
-       **      some error).
        */
 
        */
 
-       (void) signal(SIGPIPE, SIG_IGN);
        putfromline(mfile, m);
        (*e->e_puthdr)(mfile, m, e);
        putfromline(mfile, m);
        (*e->e_puthdr)(mfile, m, e);
-       fprintf(mfile, "\n");
-       (*e->e_putbody)(mfile, m, FALSE);
+       putline("\n", mfile, m);
+       (*e->e_putbody)(mfile, m, e);
        (void) fclose(mfile);
 
        i = endmailer(pid, pvp[0]);
        (void) fclose(mfile);
 
        i = endmailer(pid, pvp[0]);
-       giveresponse(i, m);
 
        /* arrange a return receipt if requested */
 
        /* arrange a return receipt if requested */
-       if (e->e_receiptto != NULL && bitset(M_LOCAL, m->m_flags))
+       if (e->e_receiptto != NULL && bitnset(M_LOCAL, m->m_flags))
        {
                e->e_flags |= EF_SENDRECEIPT;
        {
                e->e_flags |= EF_SENDRECEIPT;
-               if (ExitStat == EX_OK && Xscript != NULL)
-                       fprintf(Xscript, "%s... successfully delivered\n",
-                               e->e_to);
                /* do we want to send back more info? */
        }
 
                /* do we want to send back more info? */
        }
 
@@ -562,9 +665,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 */
@@ -615,34 +718,81 @@ openmailer(m, pvp, ctladdr, clever, pmfile, prfile)
 # endif DEBUG
        errno = 0;
 
 # endif DEBUG
        errno = 0;
 
+       CurHostName = m->m_mailer;
+
        /*
        **  Deal with the special case of mail handled through an IPC
        **  connection.
        **      In this case we don't actually fork.  We must be
        **      running SMTP for this to work.  We will return a
        **      zero pid to indicate that we are running IPC.
        /*
        **  Deal with the special case of mail handled through an IPC
        **  connection.
        **      In this case we don't actually fork.  We must be
        **      running SMTP for this to work.  We will return a
        **      zero pid to indicate that we are running IPC.
+       **  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)
+       {
+               *pmfile = stdout;
+               *prfile = stdin;
+               return (0);
+       }
+#endif DEBUG
+
        if (strcmp(m->m_mailer, "[IPC]") == 0)
        {
        if (strcmp(m->m_mailer, "[IPC]") == 0)
        {
+#ifdef HOSTINFO
+               register STAB *st;
+               extern STAB *stab();
+#endif HOSTINFO
 #ifdef DAEMON
 #ifdef DAEMON
-               register int i;
+               register int i, j;
                register u_short port;
 
                register u_short port;
 
+               CurHostName = pvp[1];
                if (!clever)
                        syserr("non-clever IPC");
                if (pvp[2] != NULL)
                        port = atoi(pvp[2]);
                else
                        port = 0;
                if (!clever)
                        syserr("non-clever IPC");
                if (pvp[2] != NULL)
                        port = atoi(pvp[2]);
                else
                        port = 0;
-               i = makeconnection(pvp[1], port, pmfile, prfile);
-               if (i != EX_OK)
+               for (j = 0; j < Nmx; j++)
                {
                {
-                       ExitStat = i;
-                       return (-1);
+                       CurHostName = MxHosts[j];
+#ifdef HOSTINFO
+               /* see if we have already determined that this host is fried */
+                       st = stab(MxHosts[j], ST_HOST, ST_FIND);
+                       if (st == NULL || st->s_host.ho_exitstat == EX_OK) {
+                               if (j > 1)
+                                       message(Arpa_Info,
+                                           "Connecting to %s (%s)...",
+                                           MxHosts[j], m->m_name);
+                               i = makeconnection(MxHosts[j], port, pmfile, prfile);
+                       }
+                       else
+                       {
+                               i = st->s_host.ho_exitstat;
+                               errno = st->s_host.ho_errno;
+                       }
+#else HOSTINFO
+                       i = makeconnection(MxHosts[j], port, pmfile, prfile);
+#endif HOSTINFO
+                       if (i != EX_OK)
+                       {
+#ifdef HOSTINFO
+                               /* enter status of this host */
+                               if (st == NULL)
+                                       st = stab(MxHosts[j], ST_HOST, ST_ENTER);
+                               st->s_host.ho_exitstat = i;
+                               st->s_host.ho_errno = errno;
+#endif HOSTINFO
+                               ExitStat = i;
+                               continue;
+                       }
+                       else
+                               return (0);
                }
                }
-               else
-                       return (0);
+               return (-1);
 #else DAEMON
                syserr("openmailer: no IPC");
                return (-1);
 #else DAEMON
                syserr("openmailer: no IPC");
                return (-1);
@@ -670,11 +820,17 @@ openmailer(m, pvp, ctladdr, clever, pmfile, prfile)
        /*
        **  Actually fork the mailer process.
        **      DOFORK is clever about retrying.
        /*
        **  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 (Xscript != NULL)
-               (void) fflush(Xscript);                 /* for debugging */
+       if (CurEnv->e_xfp != NULL)
+               (void) fflush(CurEnv->e_xfp);           /* for debugging */
        (void) fflush(stdout);
        (void) fflush(stdout);
+# ifdef SIGCHLD
+       (void) signal(SIGCHLD, SIG_DFL);
+# endif SIGCHLD
        DOFORK(XFORK);
        /* pid is set by DOFORK */
        if (pid < 0)
        DOFORK(XFORK);
        /* pid is set by DOFORK */
        if (pid < 0)
@@ -694,6 +850,9 @@ openmailer(m, pvp, ctladdr, clever, pmfile, prfile)
        }
        else if (pid == 0)
        {
        }
        else if (pid == 0)
        {
+               int i;
+               extern int DtableSize;
+
                /* child -- set up input & exec mailer */
                /* make diagnostic output be standard output */
                (void) signal(SIGINT, SIG_IGN);
                /* child -- set up input & exec mailer */
                /* make diagnostic output be standard output */
                (void) signal(SIGINT, SIG_IGN);
@@ -712,7 +871,7 @@ openmailer(m, pvp, ctladdr, clever, pmfile, prfile)
                {
                        /* put mailer output in transcript */
                        (void) close(1);
                {
                        /* put mailer output in transcript */
                        (void) close(1);
-                       (void) dup(fileno(Xscript));
+                       (void) dup(fileno(CurEnv->e_xfp));
                }
                (void) close(2);
                (void) dup(1);
                }
                (void) close(2);
                (void) dup(1);
@@ -726,9 +885,9 @@ openmailer(m, pvp, ctladdr, clever, pmfile, prfile)
                        _exit(EX_OSERR);
                }
                (void) close(mpvect[0]);
                        _exit(EX_OSERR);
                }
                (void) close(mpvect[0]);
-               if (!bitset(M_RESTR, m->m_flags))
+               if (!bitnset(M_RESTR, m->m_flags))
                {
                {
-                       if (ctladdr->q_uid == 0)
+                       if (ctladdr == NULL || ctladdr->q_uid == 0)
                        {
                                (void) setgid(DefGid);
                                (void) setuid(DefUid);
                        {
                                (void) setgid(DefGid);
                                (void) setuid(DefUid);
@@ -740,33 +899,21 @@ openmailer(m, pvp, ctladdr, clever, pmfile, prfile)
                        }
                }
 
                        }
                }
 
-               /*
-               **  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.
-               */
-
-               closeall();
+               /* 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 */
 
                /* 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);
+               execve(m->m_mailer, pvp, UserEnviron);
+               syserr("Cannot exec %s", m->m_mailer);
+               if (m == LocalMailer || errno == EIO || errno == EAGAIN ||
+                   errno == ENOMEM || errno == EPROCLIM)
+                       _exit(EX_TEMPFAIL);
+               else
+                       _exit(EX_UNAVAILABLE);
        }
 
        /*
        }
 
        /*
@@ -803,16 +950,21 @@ openmailer(m, pvp, ctladdr, clever, pmfile, prfile)
 **             ExitStat may be set.
 */
 
 **             ExitStat may be set.
 */
 
-/*ARGSUSED*/
-giveresponse(stat, m)
+giveresponse(stat, m, e)
        int stat;
        register MAILER *m;
        int stat;
        register MAILER *m;
+       ENVELOPE *e;
 {
        register char *statmsg;
        extern char *SysExMsg[];
        register int i;
 {
        register char *statmsg;
        extern char *SysExMsg[];
        register int i;
-       extern int N_SysEx;
-       char buf[30];
+       extern int N_SysEx, h_errno;
+       char buf[MAXLINE];
+
+#ifdef lint
+       if (m == NULL)
+               return;
+#endif lint
 
        /*
        **  Compute status message from code.
 
        /*
        **  Compute status message from code.
@@ -827,22 +979,56 @@ giveresponse(stat, m)
                stat = EX_UNAVAILABLE;
                statmsg = buf;
        }
                stat = EX_UNAVAILABLE;
                statmsg = buf;
        }
+       else if (stat == EX_TEMPFAIL)
+       {
+               (void) strcpy(buf, SysExMsg[i]);
+               if (h_errno == TRY_AGAIN)
+               {
+                       extern char *errstring();
+
+                       statmsg = errstring(h_errno+MAX_ERRNO);
+               }
+               else
+               {
+                       if (errno != 0)
+                       {
+                               extern char *errstring();
+
+                               statmsg = errstring(errno);
+                       }
+                       else
+                       {
+#ifdef SMTP
+                               extern char SmtpError[];
+
+                               statmsg = SmtpError;
+#else SMTP
+                               statmsg = NULL;
+#endif SMTP
+                       }
+               }
+               if (statmsg != NULL && statmsg[0] != '\0')
+               {
+                       (void) strcat(buf, ": ");
+                       (void) strcat(buf, statmsg);
+               }
+               statmsg = buf;
+       }
        else
        else
+       {
                statmsg = SysExMsg[i];
                statmsg = SysExMsg[i];
+       }
 
        /*
        **  Print the message as appropriate
        */
 
 
        /*
        **  Print the message as appropriate
        */
 
-       if (stat == 0)
+       if (stat == EX_OK || stat == EX_TEMPFAIL)
                message(Arpa_Info, &statmsg[4]);
                message(Arpa_Info, &statmsg[4]);
-       else if (stat == EX_TEMPFAIL)
-               message(Arpa_Info, "deferred");
        else
        {
                Errors++;
                usrerr(statmsg);
        else
        {
                Errors++;
                usrerr(statmsg);
-               CurEnv->e_flags |= EF_FATALERRS;
        }
 
        /*
        }
 
        /*
@@ -857,6 +1043,14 @@ giveresponse(stat, m)
 
        if (stat != EX_TEMPFAIL)
                setstat(stat);
 
        if (stat != EX_TEMPFAIL)
                setstat(stat);
+       if (stat != EX_OK)
+       {
+               if (e->e_message != NULL)
+                       free(e->e_message);
+               e->e_message = newstr(&statmsg[4]);
+       }
+       errno = 0;
+       h_errno = 0;
 }
 \f/*
 **  LOGDELIVERY -- log the delivery in the system log
 }
 \f/*
 **  LOGDELIVERY -- log the delivery in the system log
@@ -888,11 +1082,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.
@@ -909,41 +1102,41 @@ putfromline(fp, m)
        register FILE *fp;
        register MAILER *m;
 {
        register FILE *fp;
        register MAILER *m;
 {
+       char *template = "\001l\n";
        char buf[MAXLINE];
        extern char SentDate[];
 
        char buf[MAXLINE];
        extern char SentDate[];
 
-       if (bitset(M_NHDR, m->m_flags))
+       if (bitnset(M_NHDR, m->m_flags))
                return;
 
 # ifdef UGLYUUCP
                return;
 
 # ifdef UGLYUUCP
-       if (bitset(M_UGLYUUCP, m->m_flags))
+       if (bitnset(M_UGLYUUCP, m->m_flags))
        {
        {
-               extern char *macvalue();
-               char *sys = macvalue('g', CurEnv);
-               char *bang = index(sys, '!');
+               char *bang;
+               char xbuf[MAXLINE];
 
 
+               expand("\001g", buf, &buf[sizeof buf - 1], CurEnv);
+               bang = index(buf, '!');
                if (bang == NULL)
                if (bang == NULL)
-                       syserr("No ! in UUCP! (%s)", sys);
+                       syserr("No ! in UUCP! (%s)", buf);
                else
                {
                else
                {
-                       *bang = '\0';
-                       expand("From $f  $d remote from $g\n", buf,
-                                       &buf[sizeof buf - 1], CurEnv);
-                       *bang = '!';
+                       *bang++ = '\0';
+                       (void) sprintf(xbuf, "From %s  \001d remote from %s\n", bang, buf);
+                       template = xbuf;
                }
        }
                }
        }
-       else
 # endif UGLYUUCP
 # endif UGLYUUCP
-               expand("$l\n", buf, &buf[sizeof buf - 1], CurEnv);
-       putline(buf, fp, bitset(M_FULLSMTP, m->m_flags));
+       expand(template, buf, &buf[sizeof buf - 1], CurEnv);
+       putline(buf, fp, m);
 }
 \f/*
 **  PUTBODY -- put the body of a message.
 **
 **     Parameters:
 **             fp -- file to output onto.
 }
 \f/*
 **  PUTBODY -- put the body of a message.
 **
 **     Parameters:
 **             fp -- file to output onto.
-**             m -- a mailer descriptor.
-**             xdot -- if set, use SMTP hidden dot algorithm.
+**             m -- a mailer descriptor to control output format.
+**             e -- the envelope to put out.
 **
 **     Returns:
 **             none.
 **
 **     Returns:
 **             none.
@@ -952,26 +1145,40 @@ putfromline(fp, m)
 **             The message is written onto fp.
 */
 
 **             The message is written onto fp.
 */
 
-putbody(fp, m, xdot)
+putbody(fp, m, e)
        FILE *fp;
        MAILER *m;
        FILE *fp;
        MAILER *m;
-       bool xdot;
+       register ENVELOPE *e;
 {
 {
-       char buf[MAXLINE + 1];
-       bool fullsmtp = bitset(M_FULLSMTP, m->m_flags);
+       char buf[MAXLINE];
 
        /*
        **  Output the body of the message
        */
 
 
        /*
        **  Output the body of the message
        */
 
-       if (TempFile != NULL)
+       if (e->e_dfp == NULL)
        {
        {
-               rewind(TempFile);
-               buf[0] = '.';
-               while (!ferror(fp) && fgets(&buf[1], sizeof buf - 1, TempFile) != NULL)
-                       putline((xdot && buf[1] == '.') ? buf : &buf[1], fp, fullsmtp);
+               if (e->e_df != NULL)
+               {
+                       e->e_dfp = fopen(e->e_df, "r");
+                       if (e->e_dfp == NULL)
+                               syserr("Cannot open %s", e->e_df);
+               }
+               else
+                       putline("<<< No Message Collected >>>", fp, m);
+       }
+       if (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", 4) == 0)
+                               (void) putc('>', fp);
+                       putline(buf, fp, m);
+               }
 
 
-               if (ferror(TempFile))
+               if (ferror(e->e_dfp))
                {
                        syserr("putbody: read error");
                        ExitStat = EX_IOERR;
                {
                        syserr("putbody: read error");
                        ExitStat = EX_IOERR;
@@ -1037,9 +1244,12 @@ 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;
+               }
                if (bitset(0111, stb.st_mode))
                        exit(EX_CANTCREAT);
                if (ctladdr == NULL)
                if (bitset(0111, stb.st_mode))
                        exit(EX_CANTCREAT);
                if (ctladdr == NULL)
@@ -1064,9 +1274,9 @@ mailfile(filename, ctladdr)
 
                putfromline(f, ProgMailer);
                (*CurEnv->e_puthdr)(f, ProgMailer, CurEnv);
 
                putfromline(f, ProgMailer);
                (*CurEnv->e_puthdr)(f, ProgMailer, CurEnv);
-               fputs("\n", f);
-               (*CurEnv->e_putbody)(f, ProgMailer, FALSE);
-               fputs("\n", f);
+               putline("\n", f, ProgMailer);
+               (*CurEnv->e_putbody)(f, ProgMailer, CurEnv);
+               putline("\n", f, ProgMailer);
                (void) fclose(f);
                (void) fflush(stdout);
 
                (void) fclose(f);
                (void) fflush(stdout);
 
@@ -1092,7 +1302,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.
@@ -1112,6 +1323,17 @@ sendall(e, mode)
        bool oldverbose;
        int pid;
 
        bool oldverbose;
        int pid;
 
+       /* determine actual delivery mode */
+       if (mode == SM_DEFAULT)
+       {
+               extern bool shouldqueue();
+
+               if (shouldqueue(e->e_msgpriority))
+                       mode = SM_QUEUE;
+               else
+                       mode = SendMode;
+       }
+
 #ifdef DEBUG
        if (tTd(13, 1))
        {
 #ifdef DEBUG
        if (tTd(13, 1))
        {
@@ -1136,8 +1358,10 @@ sendall(e, mode)
 
        if (!MeToo)
        {
 
        if (!MeToo)
        {
+               extern ADDRESS *recipient();
+
                e->e_from.q_flags |= QDONTSEND;
                e->e_from.q_flags |= QDONTSEND;
-               recipient(&e->e_from, &e->e_sendqueue);
+               (void) recipient(&e->e_from, &e->e_sendqueue);
        }
 
 # ifdef QUEUE
        }
 
 # ifdef QUEUE
@@ -1159,8 +1383,8 @@ sendall(e, mode)
                return;
 
          case SM_FORK:
                return;
 
          case SM_FORK:
-               if (Xscript != NULL)
-                       (void) fflush(Xscript);
+               if (e->e_xfp != NULL)
+                       (void) fflush(e->e_xfp);
                pid = fork();
                if (pid < 0)
                {
                pid = fork();
                if (pid < 0)
                {
@@ -1179,7 +1403,7 @@ sendall(e, mode)
                        exit(EX_OK);
 
                /* be sure we are immune from the terminal */
                        exit(EX_OK);
 
                /* be sure we are immune from the terminal */
-               disconnect(TRUE);
+               disconnect(FALSE);
 
                break;
        }
 
                break;
        }
@@ -1231,7 +1455,7 @@ sendall(e, mode)
                        extern char *aliaslookup();
 
                        /* we can only have owners for local addresses */
                        extern char *aliaslookup();
 
                        /* we can only have owners for local addresses */
-                       if (!bitset(M_LOCAL, qq->q_mailer->m_flags))
+                       if (!bitnset(M_LOCAL, qq->q_mailer->m_flags))
                                continue;
 
                        /* see if the owner list exists */
                                continue;
 
                        /* see if the owner list exists */
@@ -1249,14 +1473,14 @@ sendall(e, mode)
 # endif DEBUG
 
                        /* owner list exists -- add it to the error queue */
 # endif DEBUG
 
                        /* owner list exists -- add it to the error queue */
-                       sendto(obuf, (ADDRESS *) NULL, &e->e_errorqueue);
-                       ErrorMode == EM_MAIL;
+                       sendtolist(obuf, (ADDRESS *) NULL, &e->e_errorqueue);
+                       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))
-                       sendto(e->e_from.q_paddr, qq, &e->e_errorqueue);
+                       sendtolist(e->e_from.q_paddr, qq, &e->e_errorqueue);
        }
 
        if (mode == SM_FORK)
        }
 
        if (mode == SM_FORK)