install correct aliases file
[unix-history] / usr / src / usr.sbin / sendmail / src / deliver.c
index 983ad9b..82a8d86 100644 (file)
@@ -1,9 +1,35 @@
-# include <signal.h>
-# include <errno.h>
-# include "sendmail.h"
-# include <sys/stat.h>
-
-SCCSID(@(#)deliver.c   3.106           %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.
@@ -16,6 +42,7 @@ SCCSID(@(#)deliver.c  3.106           %G%);
 **     list.
 **
 **     Parameters:
 **     list.
 **
 **     Parameters:
+**             e -- the envelope to deliver.
 **             firstto -- head of the address list to deliver to.
 **
 **     Returns:
 **             firstto -- head of the address list to deliver to.
 **
 **     Returns:
@@ -26,7 +53,8 @@ SCCSID(@(#)deliver.c  3.106           %G%);
 **             The standard input is passed off to someone.
 */
 
 **             The standard input is passed off to someone.
 */
 
-deliver(firstto)
+deliver(e, firstto)
+       register ENVELOPE *e;
        ADDRESS *firstto;
 {
        char *host;                     /* host being sent to */
        ADDRESS *firstto;
 {
        char *host;                     /* host being sent to */
@@ -34,25 +62,32 @@ deliver(firstto)
        char **pvp;
        register char **mvp;
        register char *p;
        char **pvp;
        register char **mvp;
        register char *p;
-       register struct mailer *m;      /* mailer for this recipient */
-       extern bool checkcompat();
-       char *pv[MAXPV+1];
-       char tobuf[MAXLINE];            /* 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 */
-       bool notopen = TRUE;            /* set if connection not quite open */
-       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;
 
@@ -72,12 +107,20 @@ deliver(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)
        {
                for (; to != NULL; to = to->q_next)
-                       if (!bitset(QDONTSEND, to->q_flags) &&
-                           to->q_mailer == firstto->q_mailer)
-                               to->q_flags |= QQUEUEUP|QDONTSEND;
+               {
+                       if (bitset(QDONTSEND, to->q_flags) || to->q_mailer != m)
+                               continue;
+                       to->q_flags |= QQUEUEUP|QDONTSEND;
+                       e->e_to = to->q_paddr;
+                       message(Arpa_Info, "queued");
+                       if (LogLevel > 4)
+                               logdelivery("queued");
+               }
+               e->e_to = NULL;
                return (0);
        }
 
                return (0);
        }
 
@@ -88,36 +131,28 @@ deliver(firstto)
        **      a picky -f flag, we insert it as appropriate.  This
        **      code does not check for 'pv' overflow; this places a
        **      manifest lower limit of 4 for MAXPV.
        **      a picky -f flag, we insert it as appropriate.  This
        **      code does not check for 'pv' overflow; this places a
        **      manifest lower limit of 4 for MAXPV.
-       **              We rewrite the from address here, being careful
-       **              to also rewrite it again using ruleset 2 to
-       **              eliminate redundancies.
+       **              The from address rewrite is expected to make
+       **              the address relative to the other end.
        */
 
        /* rewrite from address, using rewriting rules */
        */
 
        /* rewrite from address, using rewriting rules */
-       expand(m->m_from, buf, &buf[sizeof buf - 1], CurEnv);
-       mvp = prescan(buf, '\0');
-       if (mvp == NULL)
-       {
-               syserr("bad mailer from translate \"%s\"", buf);
-               return (EX_SOFTWARE);
-       }
-       rewrite(mvp, 2);
-       cataddr(mvp, tfrombuf, sizeof tfrombuf);
+       expand("\001f", buf, &buf[sizeof buf - 1], e);
+       (void) strcpy(tfrombuf, remotename(buf, m, TRUE, TRUE));
 
 
-       define('g', tfrombuf);          /* translated sender address */
-       define('h', host);              /* to host */
+       define('g', tfrombuf, e);               /* translated sender address */
+       define('h', host, e);                   /* to host */
        Errors = 0;
        pvp = pv;
        *pvp++ = m->m_argv[0];
 
        /* insert -f or -r flag as appropriate */
        Errors = 0;
        pvp = pv;
        *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], CurEnv);
+               expand("\001g", buf, &buf[sizeof buf - 1], e);
                *pvp++ = newstr(buf);
        }
 
                *pvp++ = newstr(buf);
        }
 
@@ -130,14 +165,14 @@ deliver(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)
                        break;
 
                /* this entry is safe -- go ahead and process it */
                        if (*++p == 'u')
                                break;
                if (p != NULL)
                        break;
 
                /* this entry is safe -- go ahead and process it */
-               expand(*mvp, buf, &buf[sizeof buf - 1], CurEnv);
+               expand(*mvp, buf, &buf[sizeof buf - 1], e);
                *pvp++ = newstr(buf);
                if (pvp >= &pv[MAXPV - 3])
                {
                *pvp++ = newstr(buf);
                if (pvp >= &pv[MAXPV - 3])
                {
@@ -173,12 +208,12 @@ deliver(firstto)
        */
 
        tobuf[0] = '\0';
        */
 
        tobuf[0] = '\0';
-       CurEnv->e_to = tobuf;
+       e->e_to = tobuf;
        ctladdr = NULL;
        for (; to != NULL; to = to->q_next)
        {
                /* avoid sending multiple recipients to dumb mailers */
        ctladdr = NULL;
        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 */
@@ -187,6 +222,10 @@ deliver(firstto)
                    to->q_mailer != firstto->q_mailer)
                        continue;
 
                    to->q_mailer != firstto->q_mailer)
                        continue;
 
+               /* avoid overflowing tobuf */
+               if (sizeof tobuf - (strlen(to->q_paddr) + strlen(tobuf) + 2) < 0)
+                       break;
+
 # ifdef DEBUG
                if (tTd(10, 1))
                {
 # ifdef DEBUG
                if (tTd(10, 1))
                {
@@ -200,7 +239,7 @@ deliver(firstto)
                        ctladdr = getctladdr(to);
 
                user = to->q_user;
                        ctladdr = getctladdr(to);
 
                user = to->q_user;
-               CurEnv->e_to = to->q_paddr;
+               e->e_to = to->q_paddr;
                to->q_flags |= QDONTSEND;
 
                /*
                to->q_flags |= QDONTSEND;
 
                /*
@@ -208,9 +247,16 @@ deliver(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, TRUE, m);
+                       giveresponse(EX_UNAVAILABLE, m, e);
                        continue;
                }
 
                        continue;
                }
 
@@ -219,7 +265,7 @@ deliver(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);
@@ -230,44 +276,9 @@ deliver(firstto)
                        stripquotes(host, FALSE);
                }
 
                        stripquotes(host, FALSE);
                }
 
-               /*
-               **  Do initial connection setup if needed.
-               */
-
-               if (notopen)
-               {
-                       message(Arpa_Info, "Connecting to %s.%s...", host, m->m_name);
-# ifdef SMTP
-                       if (clever)
-                       {
-                               /* send the initial SMTP protocol */
-                               rcode = smtpinit(m, pv, (ADDRESS *) NULL);
-                       }
-# ifdef SMTP
-                       notopen = FALSE;
-               }
-
-               /*
-               **  Pass it to the other host if we are running SMTP.
-               */
-
-               if (clever)
-               {
-# ifdef SMTP
-                       if (rcode == EX_OK)
-                               rcode = smtprcpt(to);
-                       if (rcode != EX_OK)
-                       {
-                               if (rcode == EX_TEMPFAIL)
-                                       to->q_flags |= QQUEUEUP;
-                               else
-                                       to->q_flags |= QBADADDR;
-                               giveresponse(rcode, TRUE, m);
-                       }
-# else SMTP
-                       syserr("trying to be clever");
-# endif SMTP
-               }
+               /* hack attack -- delivermail compatibility */
+               if (m == ProgMailer && *user == '|')
+                       user++;
 
                /*
                **  If an error message has already been given, don't
 
                /*
                **  If an error message has already been given, don't
@@ -282,8 +293,7 @@ deliver(firstto)
                        continue;
 
                /* save statistics.... */
                        continue;
 
                /* save statistics.... */
-               Stat.stat_nt[to->q_mailer->m_mno]++;
-               Stat.stat_bt[to->q_mailer->m_mno] += kbytes(CurEnv->e_msgsize);
+               markstats(e, to);
 
                /*
                **  See if this user name is "special".
 
                /*
                **  See if this user name is "special".
@@ -298,7 +308,7 @@ deliver(firstto)
                        if (user[0] == '/')
                        {
                                rcode = mailfile(user, getctladdr(to));
                        if (user[0] == '/')
                        {
                                rcode = mailfile(user, getctladdr(to));
-                               giveresponse(rcode, TRUE, m);
+                               giveresponse(rcode, m, e);
                                continue;
                        }
                }
                                continue;
                        }
                }
@@ -313,11 +323,10 @@ deliver(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);
                (void) strcat(tobuf, to->q_paddr);
-               define('u', user);              /* to user */
-               define('z', to->q_home);        /* user's home */
+               define('u', user, e);           /* to user */
+               define('z', to->q_home, e);     /* user's home */
 
                /*
                **  Expand out this user into argument list.
 
                /*
                **  Expand out this user into argument list.
@@ -325,7 +334,7 @@ deliver(firstto)
 
                if (!clever)
                {
 
                if (!clever)
                {
-                       expand(*mvp, buf, &buf[sizeof buf - 1], CurEnv);
+                       expand(*mvp, buf, &buf[sizeof buf - 1], e);
                        *pvp++ = newstr(buf);
                        if (pvp >= &pv[MAXPV - 2])
                        {
                        *pvp++ = newstr(buf);
                        if (pvp >= &pv[MAXPV - 2])
                        {
@@ -338,16 +347,12 @@ deliver(firstto)
        /* see if any addresses still exist */
        if (tobuf[0] == '\0')
        {
        /* see if any addresses still exist */
        if (tobuf[0] == '\0')
        {
-# ifdef SMTP
-               if (clever)
-                       smtpquit(pv[0], FALSE);
-# endif SMTP
-               define('g', (char *) NULL);
+               define('g', (char *) NULL, e);
                return (0);
        }
 
        /* print out messages as full list */
                return (0);
        }
 
        /* print out messages as full list */
-       CurEnv->e_to = tobuf;
+       e->e_to = tobuf + 1;
 
        /*
        **  Fill out any parameters after the $u parameter.
 
        /*
        **  Fill out any parameters after the $u parameter.
@@ -355,7 +360,7 @@ deliver(firstto)
 
        while (!clever && *++mvp != NULL)
        {
 
        while (!clever && *++mvp != NULL)
        {
-               expand(*mvp, buf, &buf[sizeof buf - 1], CurEnv);
+               expand(*mvp, buf, &buf[sizeof buf - 1], e);
                *pvp++ = newstr(buf);
                if (pvp >= &pv[MAXPV])
                        syserr("deliver: pv overflow after $u for %s", pv[0]);
                *pvp++ = newstr(buf);
                if (pvp >= &pv[MAXPV])
                        syserr("deliver: pv overflow after $u for %s", pv[0]);
@@ -371,35 +376,135 @@ deliver(firstto)
        */
 
        if (ctladdr == NULL)
        */
 
        if (ctladdr == NULL)
-               ctladdr = &CurEnv->e_from;
-# ifdef SMTP
+               ctladdr = &e->e_from;
+#ifdef NAMED_BIND
+       _res.options &= ~(RES_DEFNAMES | RES_DNSRCH);           /* XXX */
+#endif
+#ifdef SMTP
        if (clever)
        {
        if (clever)
        {
-               rcode = smtpfinish(m, CurEnv);
-               if (rcode != EX_OK)
-                       giveresponse(rcode, TRUE, m);
-               smtpquit(pv[0], rcode == EX_OK);
+               expand("\001w", buf, &buf[sizeof(buf) - 1], e);
+               rcode = EX_OK;
+#ifdef NAMED_BIND
+               if (host[0] != '[')
+               {
+                       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);
+                       }
+               }
        }
        else
        }
        else
-# endif SMTP
-               rcode = sendoff(m, pv, ctladdr);
+#endif /* SMTP */
+       {
+               message(Arpa_Info, "Connecting to %s (%s)...", host, m->m_name);
+               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;
 
        errno = 0;
-       define('g', (char *) NULL);
+       define('g', (char *) NULL, e);
        return (rcode);
 }
 \f/*
        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
@@ -422,22 +527,23 @@ deliver(firstto)
 */
 
 # define NFORKTRIES    5
 */
 
 # define NFORKTRIES    5
-# ifdef VFORK
+# ifdef VMUNIX
 # define XFORK vfork
 # define XFORK vfork
-# else VFORK
+# else VMUNIX
 # define XFORK fork
 # define XFORK fork
-# endif VFORK
+# endif VMUNIX
 
 # 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/*
@@ -466,6 +572,7 @@ dofork()
 **  SENDOFF -- send off call to mailer & collect response.
 **
 **     Parameters:
 **  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
 **             m -- mailer descriptor.
 **             pvp -- parameter vector to send to it.
 **             ctladdr -- an address pointer controlling the
@@ -477,9 +584,10 @@ dofork()
 **     Side Effects:
 **             none.
 */
 **     Side Effects:
 **             none.
 */
-
-sendoff(m, pvp, ctladdr)
-       struct mailer *m;
+static
+sendoff(e, m, pvp, ctladdr)
+       register ENVELOPE *e;
+       MAILER *m;
        char **pvp;
        ADDRESS *ctladdr;
 {
        char **pvp;
        ADDRESS *ctladdr;
 {
@@ -500,21 +608,18 @@ sendoff(m, pvp, ctladdr)
        **  Format and send message.
        */
 
        **  Format and send message.
        */
 
-       (void) signal(SIGPIPE, SIG_IGN);
        putfromline(mfile, m);
        putfromline(mfile, m);
-       (*CurEnv->e_puthdr)(mfile, m, CurEnv);
-       fprintf(mfile, "\n");
-       (*CurEnv->e_putbody)(mfile, m, FALSE);
+       (*e->e_puthdr)(mfile, m, e);
+       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, TRUE, m);
 
        /* arrange a return receipt if requested */
 
        /* arrange a return receipt if requested */
-       if (CurEnv->e_retreceipt && bitset(M_LOCAL, m->m_flags) && i == EX_OK)
+       if (e->e_receiptto != NULL && bitnset(M_LOCAL, m->m_flags))
        {
        {
-               CurEnv->e_sendreceipt = TRUE;
-               fprintf(Xscript, "%s... successfully delivered\n", CurEnv->e_to);
+               e->e_flags |= EF_SENDRECEIPT;
                /* do we want to send back more info? */
        }
 
                /* do we want to send back more info? */
        }
 
@@ -543,33 +648,31 @@ endmailer(pid, name)
        int pid;
        char *name;
 {
        int pid;
        char *name;
 {
-       register int i;
-       auto int st;
+       int st;
 
        /* in the IPC case there is nothing to wait for */
        if (pid == 0)
                return (EX_OK);
 
        /* wait for the mailer process to die and collect status */
 
        /* in the IPC case there is nothing to wait for */
        if (pid == 0)
                return (EX_OK);
 
        /* wait for the mailer process to die and collect status */
-       while ((i = wait(&st)) > 0 && i != pid)
-               continue;
-       if (i < 0)
+       st = waitfor(pid);
+       if (st == -1)
        {
        {
-               syserr("wait");
-               return (-1);
+               syserr("endmailer %s: wait", name);
+               return (EX_SOFTWARE);
        }
 
        /* see if it died a horrid death */
        if ((st & 0377) != 0)
        {
        }
 
        /* see if it died a horrid death */
        if ((st & 0377) != 0)
        {
-               syserr("%s: stat %o", name, st);
-               ExitStat = EX_UNAVAILABLE;
-               return (-1);
+               syserr("mailer %s died with signal %o", name, st);
+               ExitStat = EX_TEMPFAIL;
+               return (EX_TEMPFAIL);
        }
 
        /* normal death -- return status */
        }
 
        /* normal death -- return status */
-       i = (st >> 8) & 0377;
-       return (i);
+       st = (st >> 8) & 0377;
+       return (st);
 }
 \f/*
 **  OPENMAILER -- open connection to mailer.
 }
 \f/*
 **  OPENMAILER -- open connection to mailer.
@@ -592,7 +695,7 @@ endmailer(pid, name)
 */
 
 openmailer(m, pvp, ctladdr, clever, pmfile, prfile)
 */
 
 openmailer(m, pvp, ctladdr, clever, pmfile, prfile)
-       struct mailer *m;
+       MAILER *m;
        char **pvp;
        ADDRESS *ctladdr;
        bool clever;
        char **pvp;
        ADDRESS *ctladdr;
        bool clever;
@@ -609,84 +712,147 @@ openmailer(m, pvp, ctladdr, clever, pmfile, prfile)
 # ifdef DEBUG
        if (tTd(11, 1))
        {
 # ifdef DEBUG
        if (tTd(11, 1))
        {
-               printf("openmailer:\n");
+               printf("openmailer:");
                printav(pvp);
        }
 # endif DEBUG
        errno = 0;
 
                printav(pvp);
        }
 # endif DEBUG
        errno = 0;
 
-# ifdef DAEMON
+       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)
        {
-               register int i;
+#ifdef HOSTINFO
+               register STAB *st;
+               extern STAB *stab();
+#endif HOSTINFO
+#ifdef DAEMON
+               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);
+#endif DAEMON
        }
        }
-# endif DAEMON
 
        /* create a pipe to shove the mail through */
        if (pipe(mpvect) < 0)
        {
 
        /* create a pipe to shove the mail through */
        if (pipe(mpvect) < 0)
        {
-               syserr("pipe (to mailer)");
+               syserr("openmailer: pipe (to mailer)");
                return (-1);
        }
 
                return (-1);
        }
 
-# ifdef SMTP
+#ifdef SMTP
        /* if this mailer speaks smtp, create a return pipe */
        if (clever && pipe(rpvect) < 0)
        {
        /* if this mailer speaks smtp, create a return pipe */
        if (clever && pipe(rpvect) < 0)
        {
-               syserr("pipe (from mailer)");
+               syserr("openmailer: pipe (from mailer)");
                (void) close(mpvect[0]);
                (void) close(mpvect[1]);
                return (-1);
        }
                (void) close(mpvect[0]);
                (void) close(mpvect[1]);
                return (-1);
        }
-# endif SMTP
+#endif SMTP
 
        /*
        **  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.
        */
 
        */
 
-       (void) fflush(Xscript);                         /* for debugging */
+       if (CurEnv->e_xfp != NULL)
+               (void) fflush(CurEnv->e_xfp);           /* for debugging */
+       (void) fflush(stdout);
+# ifdef SIGCHLD
+       (void) signal(SIGCHLD, SIG_DFL);
+# endif SIGCHLD
        DOFORK(XFORK);
        /* pid is set by DOFORK */
        if (pid < 0)
        {
                /* failure */
        DOFORK(XFORK);
        /* pid is set by DOFORK */
        if (pid < 0)
        {
                /* failure */
-               syserr("Cannot fork");
+               syserr("openmailer: cannot fork");
                (void) close(mpvect[0]);
                (void) close(mpvect[1]);
                (void) close(mpvect[0]);
                (void) close(mpvect[1]);
+#ifdef SMTP
                if (clever)
                {
                        (void) close(rpvect[0]);
                        (void) close(rpvect[1]);
                }
                if (clever)
                {
                        (void) close(rpvect[0]);
                        (void) close(rpvect[1]);
                }
+#endif SMTP
                return (-1);
        }
        else if (pid == 0)
        {
                return (-1);
        }
        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);
@@ -701,10 +867,11 @@ openmailer(m, pvp, ctladdr, clever, pmfile, prfile)
                        (void) dup(rpvect[1]);
                        (void) close(rpvect[1]);
                }
                        (void) dup(rpvect[1]);
                        (void) close(rpvect[1]);
                }
-               else if (OutChannel != stdout)
+               else if (OpMode == MD_SMTP || HoldErrs)
                {
                {
+                       /* put mailer output in transcript */
                        (void) close(1);
                        (void) close(1);
-                       (void) dup(fileno(OutChannel));
+                       (void) dup(fileno(CurEnv->e_xfp));
                }
                (void) close(2);
                (void) dup(1);
                }
                (void) close(2);
                (void) dup(1);
@@ -718,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);
@@ -731,38 +898,22 @@ openmailer(m, pvp, ctladdr, clever, pmfile, prfile)
                                (void) setuid(ctladdr->q_uid);
                        }
                }
                                (void) setuid(ctladdr->q_uid);
                        }
                }
-# ifndef VFORK
-               /*
-               **  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().
-               **
-               **  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.
-               */
 
 
-               endpwent();
-# ifdef LOG
-               closelog();
-# endif LOG
-# endif VFORK
+               /* 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);
        }
 
        /*
        }
 
        /*
@@ -789,9 +940,6 @@ openmailer(m, pvp, ctladdr, clever, pmfile, prfile)
 **             stat -- the status code from the mailer (high byte
 **                     only; core dumps must have been taken care of
 **                     already).
 **             stat -- the status code from the mailer (high byte
 **                     only; core dumps must have been taken care of
 **                     already).
-**             force -- if set, force an error message output, even
-**                     if the mailer seems to like to print its own
-**                     messages.
 **             m -- the mailer descriptor for this mailer.
 **
 **     Returns:
 **             m -- the mailer descriptor for this mailer.
 **
 **     Returns:
@@ -802,56 +950,85 @@ openmailer(m, pvp, ctladdr, clever, pmfile, prfile)
 **             ExitStat may be set.
 */
 
 **             ExitStat may be set.
 */
 
-giveresponse(stat, force, m)
+giveresponse(stat, m, e)
        int stat;
        int stat;
-       bool force;
-       register struct mailer *m;
+       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.
        */
 
        i = stat - EX__BASE;
 
        /*
        **  Compute status message from code.
        */
 
        i = stat - EX__BASE;
-       if (i < 0 || i > N_SysEx)
-               statmsg = NULL;
-       else
-               statmsg = SysExMsg[i];
        if (stat == 0)
        if (stat == 0)
+               statmsg = "250 Sent";
+       else if (i < 0 || i > N_SysEx)
        {
        {
-               statmsg = "250 sent";
-               message(Arpa_Info, &statmsg[4]);
+               (void) sprintf(buf, "554 unknown mailer error %d", stat);
+               stat = EX_UNAVAILABLE;
+               statmsg = buf;
        }
        else if (stat == EX_TEMPFAIL)
        {
        }
        else if (stat == EX_TEMPFAIL)
        {
-               message(Arpa_Info, "deferred");
+               (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
+       {
+               statmsg = SysExMsg[i];
        }
        }
+
+       /*
+       **  Print the message as appropriate
+       */
+
+       if (stat == EX_OK || stat == EX_TEMPFAIL)
+               message(Arpa_Info, &statmsg[4]);
        else
        {
                Errors++;
        else
        {
                Errors++;
-               FatalErrors = TRUE;
-               if (statmsg == NULL && m->m_badstat != 0)
-               {
-                       stat = m->m_badstat;
-                       i = stat - EX__BASE;
-# ifdef DEBUG
-                       if (i < 0 || i >= N_SysEx)
-                               syserr("Bad m_badstat %d", stat);
-                       else
-# endif DEBUG
-                               statmsg = SysExMsg[i];
-               }
-               if (statmsg == NULL)
-                       usrerr("unknown mailer response %d", stat);
-               else if (force || !bitset(M_QUIET, m->m_flags) || Verbose)
-                       usrerr(statmsg);
-               else
-                       fprintf(Xscript, "%s\n", &statmsg[4]);
+               usrerr(statmsg);
        }
 
        /*
        }
 
        /*
@@ -861,24 +1038,42 @@ giveresponse(stat, force, m)
        **      that.
        */
 
        **      that.
        */
 
-       if (statmsg == NULL)
+       if (LogLevel > ((stat == 0 || stat == EX_TEMPFAIL) ? 3 : 2))
+               logdelivery(&statmsg[4]);
+
+       if (stat != EX_TEMPFAIL)
+               setstat(stat);
+       if (stat != EX_OK)
        {
        {
-               (void) sprintf(buf, "554 error %d", stat);
-               statmsg = buf;
+               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
+**
+**     Parameters:
+**             stat -- the message to print for the status
+**
+**     Returns:
+**             none
+**
+**     Side Effects:
+**             none
+*/
 
 
-# ifdef LOG
-       if (LogLevel > ((stat == 0 || stat == EX_TEMPFAIL) ? 3 : 2))
-       {
-               extern char *pintvl();
+logdelivery(stat)
+       char *stat;
+{
+       extern char *pintvl();
 
 
-               syslog(LOG_INFO, "%s: to=%s, delay=%s, stat=%s", CurEnv->e_id,
-                      CurEnv->e_to, pintvl(curtime() - CurEnv->e_ctime, TRUE),
-                      &statmsg[4]);
-       }
+# 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
 # endif LOG
-       if (stat != EX_TEMPFAIL)
-               setstat(stat);
 }
 \f/*
 **  PUTFROMLINE -- output a UNIX-style from line (or whatever)
 }
 \f/*
 **  PUTFROMLINE -- output a UNIX-style from line (or whatever)
@@ -887,11 +1082,10 @@ giveresponse(stat, force, m)
 **     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
-**     "remote 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.
@@ -908,315 +1102,83 @@ 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');
-               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 = '!';
-       }
-       else
-# endif UGLYUUCP
-               expand("$l\n", buf, &buf[sizeof buf - 1], CurEnv);
-       putline(buf, fp, bitset(M_FULLSMTP, m->m_flags));
-}
-\f/*
-**  PUTHEADER -- put the header part of a message from the in-core copy
-**
-**     Parameters:
-**             fp -- file to put it on.
-**             m -- mailer to use.
-**             e -- envelope to use.
-**
-**     Returns:
-**             none.
-**
-**     Side Effects:
-**             none.
-*/
-
-putheader(fp, m, e)
-       register FILE *fp;
-       register struct mailer *m;
-       register ENVELOPE *e;
-{
-       char buf[BUFSIZ];
-       register HDR *h;
-       extern char *arpadate();
-       extern char *capitalize();
-       extern char *hvalue();
-       extern bool samefrom();
-       char *of_line;
-       char obuf[MAXLINE];
-       register char *obp;
-       bool fullsmtp = bitset(M_FULLSMTP, m->m_flags);
-
-       of_line = hvalue("original-from");
-       for (h = e->e_header; h != NULL; h = h->h_link)
-       {
-               register char *p;
-               char *origfrom = e->e_origfrom;
-               bool nooutput;
-
-               nooutput = FALSE;
-               if (bitset(H_CHECK|H_ACHECK, h->h_flags) && !bitset(h->h_mflags, m->m_flags))
-                       nooutput = TRUE;
-
-               /* use From: line from message if generated is the same */
-               p = h->h_value;
-               if (strcmp(h->h_field, "from") == 0 && origfrom != NULL &&
-                   strcmp(m->m_from, "$f") == 0 && of_line == NULL)
-               {
-                       p = origfrom;
-                       origfrom = NULL;
-               }
-               else if (bitset(H_DEFAULT, h->h_flags))
-               {
-                       /* macro expand value if generated internally */
-                       expand(h->h_value, buf, &buf[sizeof buf], e);
-                       p = buf;
-               }
-               else if (bitset(H_ADDR, h->h_flags))
-               {
-                       if (p == NULL || *p == '\0' || nooutput)
-                               continue;
-                       commaize(p, capitalize(h->h_field), fp, e->e_oldstyle, m);
-                       nooutput = TRUE;
-               }
-               if (p == NULL || *p == '\0')
-                       continue;
-
-               /* hack, hack -- output Original-From field if different */
-               if (strcmp(h->h_field, "from") == 0 && origfrom != NULL)
                {
                {
-                       /* output new Original-From line if needed */
-                       if (of_line == NULL && !samefrom(p, origfrom))
-                       {
-                               (void) sprintf(obuf, "Original-From: %s\n", origfrom);
-                               putline(obuf, fp, fullsmtp);
-                       }
-                       if (of_line != NULL && !nooutput && samefrom(p, of_line))
-                       {
-                               /* delete Original-From: line if redundant */
-                               p = of_line;
-                               of_line = NULL;
-                       }
-               }
-               else if (strcmp(h->h_field, "original-from") == 0 && of_line == NULL)
-                       nooutput = TRUE;
-
-               /* finally, output the header line */
-               if (!nooutput)
-               {
-                       (void) sprintf(obuf, "%s: %s\n", capitalize(h->h_field), p);
-                       putline(obuf, fp, fullsmtp);
-                       h->h_flags |= H_USED;
+                       *bang++ = '\0';
+                       (void) sprintf(xbuf, "From %s  \001d remote from %s\n", bang, buf);
+                       template = xbuf;
                }
        }
                }
        }
+# endif UGLYUUCP
+       expand(template, buf, &buf[sizeof buf - 1], CurEnv);
+       putline(buf, fp, m);
 }
 \f/*
 }
 \f/*
-**  COMMAIZE -- output a header field, making a comma-translated list.
+**  PUTBODY -- put the body of a message.
 **
 **     Parameters:
 **
 **     Parameters:
-**             p -- the field to output.
-**             tag -- the tag to associate with it.
-**             fp -- file to put it to.
-**             oldstyle -- TRUE if this is an old style header.
-**             m -- a pointer to the mailer descriptor.
+**             fp -- file to output onto.
+**             m -- a mailer descriptor to control output format.
+**             e -- the envelope to put out.
 **
 **     Returns:
 **             none.
 **
 **     Side Effects:
 **
 **     Returns:
 **             none.
 **
 **     Side Effects:
-**             outputs "p" to file "fp".
+**             The message is written onto fp.
 */
 
 */
 
-commaize(p, tag, fp, oldstyle, m)
-       register char *p;
-       char *tag;
+putbody(fp, m, e)
        FILE *fp;
        FILE *fp;
-       bool oldstyle;
        MAILER *m;
        MAILER *m;
+       register ENVELOPE *e;
 {
 {
-       register int opos;
-       bool firstone = TRUE;
-       char obuf[MAXLINE];
-       register char *obp;
-       bool fullsmtp = bitset(M_FULLSMTP, m->m_flags);
-
-       /*
-       **  Output the address list translated by the
-       **  mailer and with commas.
-       */
-
-# ifdef DEBUG
-       if (tTd(14, 2))
-               printf("commaize(%s: %s)\n", tag, p);
-# endif DEBUG
-
-       obp = obuf;
-       (void) sprintf(obp, "%s: ", tag);
-       opos = strlen(tag) + 2;
-       obp += opos;
+       char buf[MAXLINE];
 
        /*
 
        /*
-       **  Run through the list of values.
+       **  Output the body of the message
        */
 
        */
 
-       while (*p != '\0')
+       if (e->e_dfp == NULL)
        {
        {
-               register char *name;
-               extern char *remotename();
-               char savechar;
-               int commentlevel;
-               bool inquote;
-
-               /*
-               **  Find the end of the name.  New style names
-               **  end with a comma, old style names end with
-               **  a space character.  However, spaces do not
-               **  necessarily delimit an old-style name -- at
-               **  signs mean keep going.
-               */
-
-               /* clean up the leading trash in source */
-               while (*p != '\0' && (isspace(*p) || *p == ','))
-                       p++;
-               name = p;
-
-               /* find end of name */
-               commentlevel = 0;
-               inquote = FALSE;
-               while (*p != '\0' && (*p != ',' || commentlevel > 0 || inquote))
-               {
-                       extern bool isatword();
-                       char *oldp;
-
-                       if (*p == '(')
-                               commentlevel++;
-                       else if (*p == ')' && commentlevel > 0)
-                               commentlevel--;
-                       else if (*p == '"')
-                               inquote = !inquote;
-                       if (!oldstyle || !isspace(*p))
-                       {
-                               p++;
-                               continue;
-                       }
-
-                       /* look to see if we have an at sign */
-                       oldp = p;
-                       while (*p != '\0' && isspace(*p))
-                               p++;
-
-                       if (*p != '@' && !isatword(p))
-                       {
-                               p = oldp;
-                               break;
-                       }
-                       p += *p == '@' ? 1 : 2;
-                       while (*p != '\0' && isspace(*p))
-                               p++;
-               }
-               /* at the end of one complete name */
-
-               /* strip off trailing white space */
-               while (p >= name && (isspace(*p) || *p == ',' || *p == '\0'))
-                       p--;
-               if (++p == name)
-                       continue;
-               savechar = *p;
-               *p = '\0';
-
-               /* translate the name to be relative */
-               name = remotename(name, m, FALSE);
-               if (*name == '\0')
+               if (e->e_df != NULL)
                {
                {
-                       *p = savechar;
-                       continue;
+                       e->e_dfp = fopen(e->e_df, "r");
+                       if (e->e_dfp == NULL)
+                               syserr("Cannot open %s", e->e_df);
                }
                }
-
-               /* output the name with nice formatting */
-               opos += strlen(name);
-               if (!firstone)
-                       opos += 2;
-               if (opos > 78 && !firstone)
-               {
-                       (void) sprintf(obp, ",\n");
-                       putline(obuf, fp, fullsmtp);
-                       obp = obuf;
-                       (void) sprintf(obp, "        ");
-                       obp += strlen(obp);
-                       opos = 8 + strlen(name);
-               }
-               else if (!firstone)
-               {
-                       (void) sprintf(obp, ", ");
-                       obp += 2;
-               }
-               (void) sprintf(obp, "%s", name);
-               obp += strlen(obp);
-               firstone = FALSE;
-               *p = savechar;
+               else
+                       putline("<<< No Message Collected >>>", fp, m);
        }
        }
-       (void) strcpy(obp, "\n");
-       putline(obuf, fp, fullsmtp);
-}
-\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.
-**
-**     Returns:
-**             none.
-**
-**     Side Effects:
-**             The message is written onto fp.
-*/
-
-putbody(fp, m, xdot)
-       FILE *fp;
-       struct mailer *m;
-       bool xdot;
-{
-       char buf[MAXLINE + 1];
-       bool fullsmtp = bitset(M_FULLSMTP, m->m_flags);
-
-       /*
-       **  Output the body of the message
-       */
-
-#ifdef lint
-       /* m will be needed later for complete smtp emulation */
-       if (m == NULL)
-               return;
-#endif lint
-
-       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);
+               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;
@@ -1232,85 +1194,6 @@ putbody(fp, m, xdot)
        errno = 0;
 }
 \f/*
        errno = 0;
 }
 \f/*
-**  ISATWORD -- tell if the word we are pointing to is "at".
-**
-**     Parameters:
-**             p -- word to check.
-**
-**     Returns:
-**             TRUE -- if p is the word at.
-**             FALSE -- otherwise.
-**
-**     Side Effects:
-**             none.
-*/
-
-bool
-isatword(p)
-       register char *p;
-{
-       extern char lower();
-
-       if (lower(p[0]) == 'a' && lower(p[1]) == 't' &&
-           p[2] != '\0' && isspace(p[2]))
-               return (TRUE);
-       return (FALSE);
-}
-\f/*
-**  SAMEFROM -- tell if two text addresses represent the same from address.
-**
-**     Parameters:
-**             ifrom -- internally generated form of from address.
-**             efrom -- external form of from address.
-**
-**     Returns:
-**             TRUE -- if they convey the same info.
-**             FALSE -- if any information has been lost.
-**
-**     Side Effects:
-**             none.
-*/
-
-bool
-samefrom(ifrom, efrom)
-       char *ifrom;
-       char *efrom;
-{
-       register char *p;
-       char buf[MAXNAME + 4];
-
-# ifdef DEBUG
-       if (tTd(3, 8))
-               printf("samefrom(%s,%s)-->", ifrom, efrom);
-# endif DEBUG
-       if (strcmp(ifrom, efrom) == 0)
-               goto success;
-       p = index(ifrom, '@');
-       if (p == NULL)
-               goto failure;
-       *p = '\0';
-       (void) strcpy(buf, ifrom);
-       (void) strcat(buf, " at ");
-       *p++ = '@';
-       (void) strcat(buf, p);
-       if (strcmp(buf, efrom) == 0)
-               goto success;
-
-  failure:
-# ifdef DEBUG
-       if (tTd(3, 8))
-               printf("FALSE\n");
-# endif DEBUG
-       return (FALSE);
-
-  success:
-# ifdef DEBUG
-       if (tTd(3, 8))
-               printf("TRUE\n");
-# endif DEBUG
-       return (TRUE);
-}
-\f/*
 **  MAILFILE -- Send a message to a file.
 **
 **     If the file has the setuid/setgid bits set, but NO execute
 **  MAILFILE -- Send a message to a file.
 **
 **     If the file has the setuid/setgid bits set, but NO execute
@@ -1318,6 +1201,12 @@ samefrom(ifrom, efrom)
 **     rather than the real user.  Obviously, this only works if
 **     sendmail runs as root.
 **
 **     rather than the real user.  Obviously, this only works if
 **     sendmail runs as root.
 **
+**     This could be done as a subordinate mailer, except that it
+**     is used implicitly to save messages in ~/dead.letter.  We
+**     view this as being sufficiently important as to include it
+**     here.  For example, if the system is dying, we shouldn't have
+**     to create another process plus some pipes to save the message.
+**
 **     Parameters:
 **             filename -- the name of the file to send to.
 **             ctladdr -- the controlling address header -- includes
 **     Parameters:
 **             filename -- the name of the file to send to.
 **             ctladdr -- the controlling address header -- includes
@@ -1355,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)
@@ -1380,11 +1272,11 @@ mailfile(filename, ctladdr)
                if (f == NULL)
                        exit(EX_CANTCREAT);
 
                if (f == NULL)
                        exit(EX_CANTCREAT);
 
-               putfromline(f, Mailer[1]);
-               (*CurEnv->e_puthdr)(f, Mailer[1], CurEnv);
-               fputs("\n", f);
-               (*CurEnv->e_putbody)(f, Mailer[1], FALSE);
-               fputs("\n", f);
+               putfromline(f, ProgMailer);
+               (*CurEnv->e_puthdr)(f, ProgMailer, CurEnv);
+               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);
 
@@ -1396,20 +1288,13 @@ mailfile(filename, ctladdr)
        else
        {
                /* parent -- wait for exit status */
        else
        {
                /* parent -- wait for exit status */
-               register int i;
-               auto int stat;
+               int st;
 
 
-               while ((i = wait(&stat)) != pid)
-               {
-                       if (i < 0)
-                       {
-                               stat = EX_OSERR << 8;
-                               break;
-                       }
-               }
-               if ((stat & 0377) != 0)
-                       stat = EX_UNAVAILABLE << 8;
-               return ((stat >> 8) & 0377);
+               st = waitfor(pid);
+               if ((st & 0377) != 0)
+                       return (EX_UNAVAILABLE);
+               else
+                       return ((st >> 8) & 0377);
        }
 }
 \f/*
        }
 }
 \f/*
@@ -1417,7 +1302,8 @@ mailfile(filename, ctladdr)
 **
 **     Parameters:
 **             e -- the envelope to send.
 **
 **     Parameters:
 **             e -- the envelope to send.
-**             verifyonly -- if set, only give verification messages.
+**             mode -- the delivery mode to use.  If SM_DEFAULT, use
+**                     the current SendMode.
 **
 **     Returns:
 **             none.
 **
 **     Returns:
 **             none.
@@ -1425,40 +1311,117 @@ mailfile(filename, ctladdr)
 **     Side Effects:
 **             Scans the send lists and sends everything it finds.
 **             Delivers any appropriate error messages.
 **     Side Effects:
 **             Scans the send lists and sends everything it finds.
 **             Delivers any appropriate error messages.
+**             If we are running in a non-interactive mode, takes the
+**                     appropriate action.
 */
 
 */
 
-sendall(e, verifyonly)
+sendall(e, mode)
        ENVELOPE *e;
        ENVELOPE *e;
-       bool verifyonly;
+       char mode;
 {
        register ADDRESS *q;
        bool oldverbose;
 {
        register ADDRESS *q;
        bool oldverbose;
+       int pid;
 
 
-# ifdef DEBUG
-       if (tTd(13, 2))
+       /* determine actual delivery mode */
+       if (mode == SM_DEFAULT)
        {
        {
-               printf("\nSend Queue:\n");
+               extern bool shouldqueue();
+
+               if (shouldqueue(e->e_msgpriority))
+                       mode = SM_QUEUE;
+               else
+                       mode = SendMode;
+       }
+
+#ifdef DEBUG
+       if (tTd(13, 1))
+       {
+               printf("\nSENDALL: mode %c, sendqueue:\n", mode);
                printaddr(e->e_sendqueue, TRUE);
        }
                printaddr(e->e_sendqueue, TRUE);
        }
-# endif DEBUG
+#endif DEBUG
 
        /*
 
        /*
-       **  Run through the list and send everything.
+       **  Do any preprocessing necessary for the mode we are running.
+       **      Check to make sure the hop count is reasonable.
+       **      Delete sends to the sender in mailing lists.
        */
 
        */
 
+       CurEnv = e;
+
+       if (e->e_hopcount > MAXHOP)
+       {
+               syserr("sendall: too many hops (%d max)", MAXHOP);
+               return;
+       }
+
+       if (!MeToo)
+       {
+               extern ADDRESS *recipient();
+
+               e->e_from.q_flags |= QDONTSEND;
+               (void) recipient(&e->e_from, &e->e_sendqueue);
+       }
+
+# ifdef QUEUE
+       if ((mode == SM_QUEUE || mode == SM_FORK ||
+            (mode != SM_VERIFY && SuperSafe)) &&
+           !bitset(EF_INQUEUE, e->e_flags))
+               queueup(e, TRUE, mode == SM_QUEUE);
+#endif QUEUE
+
        oldverbose = Verbose;
        oldverbose = Verbose;
-       if (verifyonly)
+       switch (mode)
+       {
+         case SM_VERIFY:
                Verbose = TRUE;
                Verbose = TRUE;
+               break;
+
+         case SM_QUEUE:
+               e->e_flags |= EF_INQUEUE|EF_KEEPQUEUE;
+               return;
+
+         case SM_FORK:
+               if (e->e_xfp != NULL)
+                       (void) fflush(e->e_xfp);
+               pid = fork();
+               if (pid < 0)
+               {
+                       mode = SM_DELIVER;
+                       break;
+               }
+               else if (pid > 0)
+               {
+                       /* be sure we leave the temp files to our child */
+                       e->e_id = e->e_df = NULL;
+                       return;
+               }
+
+               /* double fork to avoid zombies */
+               if (fork() > 0)
+                       exit(EX_OK);
+
+               /* be sure we are immune from the terminal */
+               disconnect(FALSE);
+
+               break;
+       }
+
+       /*
+       **  Run through the list and send everything.
+       */
+
        for (q = e->e_sendqueue; q != NULL; q = q->q_next)
        {
        for (q = e->e_sendqueue; q != NULL; q = q->q_next)
        {
-               if (verifyonly)
+               if (mode == SM_VERIFY)
                {
                {
-                       CurEnv->e_to = q->q_paddr;
+                       e->e_to = q->q_paddr;
                        if (!bitset(QDONTSEND|QBADADDR, q->q_flags))
                                message(Arpa_Info, "deliverable");
                }
                else
                        if (!bitset(QDONTSEND|QBADADDR, q->q_flags))
                                message(Arpa_Info, "deliverable");
                }
                else
-                       (void) deliver(q);
+                       (void) deliver(e, q);
        }
        Verbose = oldverbose;
 
        }
        Verbose = oldverbose;
 
@@ -1466,15 +1429,22 @@ sendall(e, verifyonly)
        **  Now run through and check for errors.
        */
 
        **  Now run through and check for errors.
        */
 
-       if (verifyonly)
+       if (mode == SM_VERIFY)
                return;
 
        for (q = e->e_sendqueue; q != NULL; q = q->q_next)
        {
                register ADDRESS *qq;
 
                return;
 
        for (q = e->e_sendqueue; q != NULL; q = q->q_next)
        {
                register ADDRESS *qq;
 
-               if (bitset(QQUEUEUP, q->q_flags))
-                       e->e_queueup = TRUE;
+# ifdef DEBUG
+               if (tTd(13, 3))
+               {
+                       printf("Checking ");
+                       printaddr(q, FALSE);
+               }
+# endif DEBUG
+
+               /* only send errors if the message failed */
                if (!bitset(QBADADDR, q->q_flags))
                        continue;
 
                if (!bitset(QBADADDR, q->q_flags))
                        continue;
 
@@ -1485,7 +1455,7 @@ sendall(e, verifyonly)
                        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 */
@@ -1497,62 +1467,22 @@ sendall(e, verifyonly)
                        if (aliaslookup(obuf) == NULL)
                                continue;
 
                        if (aliaslookup(obuf) == NULL)
                                continue;
 
+# ifdef DEBUG
+                       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 */
-                       qq->q_flags &= ~QPRIMARY;
-                       sendto(obuf, 1, qq, &e->e_errorqueue);
-                       MailBack = TRUE;
+                       sendtolist(obuf, (ADDRESS *) NULL, &e->e_errorqueue);
+                       ErrorMode = EM_MAIL;
                        break;
                }
 
                /* if we did not find an owner, send to the sender */
                        break;
                }
 
                /* if we did not find an owner, send to the sender */
-               if (qq == NULL)
-                       sendto(e->e_from.q_paddr, 1, qq, &e->e_errorqueue);
+               if (qq == NULL && bitset(QBADADDR, q->q_flags))
+                       sendtolist(e->e_from.q_paddr, qq, &e->e_errorqueue);
        }
        }
-}
-\f/*
-**  CHECKERRORS -- check a queue of addresses and process errors.
-**
-**     Parameters:
-**             e -- the envelope to check.
-**
-**     Returns:
-**             none.
-**
-**     Side Effects:
-**             Arranges to queue all tempfailed messages in q
-**                     or deliver error responses.
-*/
 
 
-checkerrors(e)
-       register ENVELOPE *e;
-{
-       register ADDRESS *q;
-
-# ifdef DEBUG
-       if (tTd(4, 1))
-       {
-               printf("\ncheckerrors: FatalErrors %d, errorqueue:\n");
-               printaddr(e->e_errorqueue, TRUE);
-       }
-# endif DEBUG
-
-       /* mail back the transcript on errors */
-       if (FatalErrors)
-               savemail();
-
-       /* queue up anything laying around */
-       if (e->e_dontqueue)
-               return;
-       for (q = e->e_sendqueue; q != NULL; q = q->q_next)
-       {
-               if (bitset(QQUEUEUP, q->q_flags))
-               {
-# ifdef QUEUE
-                       queueup(e, FALSE);
-# else QUEUE
-                       syserr("checkerrors: trying to queue %s", e->e_df);
-# endif QUEUE
-                       break;
-               }
-       }
+       if (mode == SM_FORK)
+               finis();
 }
 }