BSD 4_4 release
[unix-history] / usr / src / usr.sbin / sendmail / src / usersmtp.c
index bbfe335..254f0bc 100644 (file)
@@ -1,30 +1,44 @@
 /*
  * Copyright (c) 1983 Eric P. Allman
 /*
  * Copyright (c) 1983 Eric P. Allman
- * Copyright (c) 1988 Regents of the University of California.
- * All rights reserved.
+ * Copyright (c) 1988, 1993
+ *     The Regents of the University of California.  All rights reserved.
  *
  *
- * Redistribution and use in source and binary forms are permitted provided
- * that: (1) source distributions retain this entire copyright notice and
- * comment, and (2) distributions including binaries display the following
- * acknowledgement:  ``This product includes software developed by the
- * University of California, Berkeley and its contributors'' in the
- * documentation or other materials provided with the distribution and in
- * all advertising materials mentioning features or use of this software.
- * Neither the name of the University nor the names of its contributors may
- * 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
- * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *     This product includes software developed by the University of
+ *     California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
  */
 
 # include "sendmail.h"
 
 #ifndef lint
 #ifdef SMTP
  */
 
 # include "sendmail.h"
 
 #ifndef lint
 #ifdef SMTP
-static char sccsid[] = "@(#)usersmtp.c 5.15 (Berkeley) 6/1/90 (with SMTP)";
+static char sccsid[] = "@(#)usersmtp.c 8.1 (Berkeley) 6/7/93 (with SMTP)";
 #else
 #else
-static char sccsid[] = "@(#)usersmtp.c 5.15 (Berkeley) 6/1/90 (without SMTP)";
+static char sccsid[] = "@(#)usersmtp.c 8.1 (Berkeley) 6/7/93 (without SMTP)";
 #endif
 #endif /* not lint */
 
 #endif
 #endif /* not lint */
 
@@ -46,16 +60,11 @@ static char sccsid[] = "@(#)usersmtp.c      5.15 (Berkeley) 6/1/90 (without SMTP)";
 char   SmtpMsgBuffer[MAXLINE];         /* buffer for commands */
 char   SmtpReplyBuffer[MAXLINE];       /* buffer for replies */
 char   SmtpError[MAXLINE] = "";        /* save failure error messages */
 char   SmtpMsgBuffer[MAXLINE];         /* buffer for commands */
 char   SmtpReplyBuffer[MAXLINE];       /* buffer for replies */
 char   SmtpError[MAXLINE] = "";        /* save failure error messages */
-FILE   *SmtpOut;                       /* output file */
-FILE   *SmtpIn;                        /* input file */
 int    SmtpPid;                        /* pid of mailer */
 
 int    SmtpPid;                        /* pid of mailer */
 
-/* following represents the state of the SMTP connection */
-int    SmtpState;                      /* connection state, see below */
-
-#define SMTP_CLOSED    0               /* connection is closed */
-#define SMTP_OPEN      1               /* connection is open for business */
-#define SMTP_SSD       2               /* service shutting down */
+#ifdef __STDC__
+extern smtpmessage(char *f, MAILER *m, MCI *mci, ...);
+#endif
 \f/*
 **  SMTPINIT -- initialize SMTP.
 **
 \f/*
 **  SMTPINIT -- initialize SMTP.
 **
@@ -67,67 +76,59 @@ int SmtpState;                      /* connection state, see below */
 **                     the mailer.
 **
 **     Returns:
 **                     the mailer.
 **
 **     Returns:
-**             appropriate exit status -- EX_OK on success.
-**             If not EX_OK, it should close the connection.
+**             none.
 **
 **     Side Effects:
 **             creates connection and sends initial protocol.
 */
 
 **
 **     Side Effects:
 **             creates connection and sends initial protocol.
 */
 
-jmp_buf        CtxGreeting;
-
-smtpinit(m, pvp)
+smtpinit(m, mci, e)
        struct mailer *m;
        struct mailer *m;
-       char **pvp;
+       register MCI *mci;
+       ENVELOPE *e;
 {
        register int r;
 {
        register int r;
-       EVENT *gte;
-       char buf[MAXNAME];
-       extern greettimeout();
+       register char *p;
+       extern void esmtp_check();
+       extern void helo_options();
+
+       if (tTd(17, 1))
+       {
+               printf("smtpinit ");
+               mci_dump(mci);
+       }
 
        /*
        **  Open the connection to the mailer.
        */
 
 
        /*
        **  Open the connection to the mailer.
        */
 
-       if (SmtpState == SMTP_OPEN)
-               syserr("smtpinit: already open");
-
-       SmtpIn = SmtpOut = NULL;
-       SmtpState = SMTP_CLOSED;
        SmtpError[0] = '\0';
        SmtpError[0] = '\0';
-       SmtpPhase = "user open";
-       setproctitle("%s %s: %s", CurEnv->e_id, pvp[1], SmtpPhase);
-       SmtpPid = openmailer(m, pvp, (ADDRESS *) NULL, TRUE, &SmtpOut, &SmtpIn);
-       if (SmtpPid < 0)
+       CurHostName = mci->mci_host;            /* XXX UGLY XXX */
+       switch (mci->mci_state)
        {
        {
-               if (tTd(18, 1))
-                       printf("smtpinit: cannot open %s: stat %d errno %d\n",
-                          pvp[0], ExitStat, errno);
-               if (CurEnv->e_xfp != NULL)
-               {
-                       register char *p;
-                       extern char *errstring();
-                       extern char *statstring();
+         case MCIS_ACTIVE:
+               /* need to clear old information */
+               smtprset(m, mci, e);
+               /* fall through */
 
 
-                       if (errno == 0)
-                       {
-                               p = statstring(ExitStat);
-                               fprintf(CurEnv->e_xfp,
-                                       "%.3s %s.%s... %s\n",
-                                       p, pvp[1], m->m_name, p);
-                       }
-                       else
-                       {
-                               r = errno;
-                               fprintf(CurEnv->e_xfp,
-                                       "421 %s.%s... Deferred: %s\n",
-                                       pvp[1], m->m_name, errstring(errno));
-                               errno = r;
-                       }
-               }
-               return (ExitStat);
+         case MCIS_OPEN:
+               return;
+
+         case MCIS_ERROR:
+         case MCIS_SSD:
+               /* shouldn't happen */
+               smtpquit(m, mci, e);
+               /* fall through */
+
+         case MCIS_CLOSED:
+               syserr("451 smtpinit: state CLOSED");
+               return;
+
+         case MCIS_OPENING:
+               break;
        }
        }
-       SmtpState = SMTP_OPEN;
+
+       mci->mci_state = MCIS_OPENING;
 
        /*
        **  Get the greeting message.
 
        /*
        **  Get the greeting message.
@@ -135,31 +136,66 @@ smtpinit(m, pvp)
        **      happen.
        */
 
        **      happen.
        */
 
-       if (setjmp(CtxGreeting) != 0)
-               goto tempfail;
-       gte = setevent((time_t) 300, greettimeout, 0);
-       SmtpPhase = "greeting wait";
-       setproctitle("%s %s: %s", CurEnv->e_id, CurHostName, SmtpPhase);
-       r = reply(m);
-       clrevent(gte);
+       SmtpPhase = mci->mci_phase = "client greeting";
+       setproctitle("%s %s: %s", e->e_id, CurHostName, mci->mci_phase);
+       r = reply(m, mci, e, TimeOuts.to_initial, esmtp_check);
        if (r < 0 || REPLYTYPE(r) != 2)
        if (r < 0 || REPLYTYPE(r) != 2)
-               goto tempfail;
+               goto tempfail1;
 
        /*
        **  Send the HELO command.
        **      My mother taught me to always introduce myself.
        */
 
 
        /*
        **  Send the HELO command.
        **      My mother taught me to always introduce myself.
        */
 
-       smtpmessage("HELO %s", m, MyHostName);
-       SmtpPhase = "HELO wait";
-       setproctitle("%s %s: %s", CurEnv->e_id, CurHostName, SmtpPhase);
-       r = reply(m);
+       if (bitnset(M_ESMTP, m->m_flags))
+               mci->mci_flags |= MCIF_ESMTP;
+
+tryhelo:
+       if (bitset(MCIF_ESMTP, mci->mci_flags))
+       {
+               smtpmessage("EHLO %s", m, mci, MyHostName);
+               SmtpPhase = mci->mci_phase = "client EHLO";
+       }
+       else
+       {
+               smtpmessage("HELO %s", m, mci, MyHostName);
+               SmtpPhase = mci->mci_phase = "client HELO";
+       }
+       setproctitle("%s %s: %s", e->e_id, CurHostName, mci->mci_phase);
+       r = reply(m, mci, e, TimeOuts.to_helo, helo_options);
        if (r < 0)
        if (r < 0)
-               goto tempfail;
+               goto tempfail1;
        else if (REPLYTYPE(r) == 5)
        else if (REPLYTYPE(r) == 5)
+       {
+               if (bitset(MCIF_ESMTP, mci->mci_flags))
+               {
+                       /* try old SMTP instead */
+                       mci->mci_flags &= ~MCIF_ESMTP;
+                       goto tryhelo;
+               }
                goto unavailable;
                goto unavailable;
+       }
        else if (REPLYTYPE(r) != 2)
        else if (REPLYTYPE(r) != 2)
-               goto tempfail;
+               goto tempfail1;
+
+       /*
+       **  Check to see if we actually ended up talking to ourself.
+       **  This means we didn't know about an alias or MX, or we managed
+       **  to connect to an echo server.
+       */
+
+       p = strchr(&SmtpReplyBuffer[4], ' ');
+       if (p != NULL)
+               *p = '\0';
+       if (strcasecmp(&SmtpReplyBuffer[4], MyHostName) == 0)
+       {
+               syserr("553 %s config error: mail loops back to myself",
+                       MyHostName);
+               mci->mci_exitstat = EX_CONFIG;
+               mci->mci_errno = 0;
+               smtpquit(m, mci, e);
+               return;
+       }
 
        /*
        **  If this is expected to be another sendmail, send some internal
 
        /*
        **  If this is expected to be another sendmail, send some internal
@@ -169,65 +205,180 @@ smtpinit(m, pvp)
        if (bitnset(M_INTERNAL, m->m_flags))
        {
                /* tell it to be verbose */
        if (bitnset(M_INTERNAL, m->m_flags))
        {
                /* tell it to be verbose */
-               smtpmessage("VERB", m);
-               r = reply(m);
+               smtpmessage("VERB", m, mci);
+               r = reply(m, mci, e, TimeOuts.to_miscshort, NULL);
                if (r < 0)
                if (r < 0)
-                       goto tempfail;
+                       goto tempfail2;
+       }
 
 
-               /* tell it we will be sending one transaction only */
-               smtpmessage("ONEX", m);
-               r = reply(m);
-               if (r < 0)
-                       goto tempfail;
+       mci->mci_state = MCIS_OPEN;
+       return;
+
+  tempfail1:
+  tempfail2:
+       mci->mci_exitstat = EX_TEMPFAIL;
+       if (mci->mci_errno == 0)
+               mci->mci_errno = errno;
+       if (mci->mci_state != MCIS_CLOSED)
+               smtpquit(m, mci, e);
+       return;
+
+  unavailable:
+       mci->mci_exitstat = EX_UNAVAILABLE;
+       mci->mci_errno = errno;
+       smtpquit(m, mci, e);
+       return;
+}
+\f/*
+**  ESMTP_CHECK -- check to see if this implementation likes ESMTP protocol
+**
+**
+**     Parameters:
+**             line -- the response line.
+**             m -- the mailer.
+**             mci -- the mailer connection info.
+**             e -- the envelope.
+**
+**     Returns:
+**             none.
+*/
+
+void
+esmtp_check(line, m, mci, e)
+       char *line;
+       MAILER *m;
+       register MCI *mci;
+       ENVELOPE *e;
+{
+       if (strlen(line) < 5)
+               return;
+       line += 4;
+       if (strncmp(line, "ESMTP ", 6) == 0)
+               mci->mci_flags |= MCIF_ESMTP;
+}
+\f/*
+**  HELO_OPTIONS -- process the options on a HELO line.
+**
+**     Parameters:
+**             line -- the response line.
+**             m -- the mailer.
+**             mci -- the mailer connection info.
+**             e -- the envelope.
+**
+**     Returns:
+**             none.
+*/
+
+void
+helo_options(line, m, mci, e)
+       char *line;
+       MAILER *m;
+       register MCI *mci;
+       ENVELOPE *e;
+{
+       register char *p;
+
+       if (strlen(line) < 5)
+               return;
+       line += 4;
+       p = strchr(line, ' ');
+       if (p != NULL)
+               *p++ = '\0';
+       if (strcasecmp(line, "size") == 0)
+       {
+               mci->mci_flags |= MCIF_SIZE;
+               if (p != NULL)
+                       mci->mci_maxsize = atol(p);
        }
        }
+       else if (strcasecmp(line, "8bitmime") == 0)
+               mci->mci_flags |= MCIF_8BITMIME;
+       else if (strcasecmp(line, "expn") == 0)
+               mci->mci_flags |= MCIF_EXPN;
+}
+\f/*
+**  SMTPMAILFROM -- send MAIL command
+**
+**     Parameters:
+**             m -- the mailer.
+**             mci -- the mailer connection structure.
+**             e -- the envelope (including the sender to specify).
+*/
+
+smtpmailfrom(m, mci, e)
+       struct mailer *m;
+       MCI *mci;
+       ENVELOPE *e;
+{
+       int r;
+       char buf[MAXNAME];
+       char optbuf[MAXLINE];
+
+       if (tTd(17, 2))
+               printf("smtpmailfrom: CurHost=%s\n", CurHostName);
+
+       /* set up appropriate options to include */
+       if (bitset(MCIF_SIZE, mci->mci_flags))
+               sprintf(optbuf, " SIZE=%ld", e->e_msgsize);
+       else
+               strcpy(optbuf, "");
 
        /*
        **  Send the MAIL command.
        **      Designates the sender.
        */
 
 
        /*
        **  Send the MAIL command.
        **      Designates the sender.
        */
 
-       expand("\001g", buf, &buf[sizeof buf - 1], CurEnv);
-       if (CurEnv->e_from.q_mailer == LocalMailer ||
+       mci->mci_state = MCIS_ACTIVE;
+
+       if (bitset(EF_RESPONSE, e->e_flags) &&
+           !bitnset(M_NO_NULL_FROM, m->m_flags))
+               (void) strcpy(buf, "");
+       else
+               expand("\201g", buf, &buf[sizeof buf - 1], e);
+       if (e->e_from.q_mailer == LocalMailer ||
            !bitnset(M_FROMPATH, m->m_flags))
        {
            !bitnset(M_FROMPATH, m->m_flags))
        {
-               smtpmessage("MAIL From:<%s>", m, buf);
+               smtpmessage("MAIL From:<%s>%s", m, mci, buf, optbuf);
        }
        else
        {
        }
        else
        {
-               smtpmessage("MAIL From:<@%s%c%s>", m, MyHostName,
-                       buf[0] == '@' ? ',' : ':', buf);
+               smtpmessage("MAIL From:<@%s%c%s>%s", m, mci, MyHostName,
+                       buf[0] == '@' ? ',' : ':', buf, optbuf);
        }
        }
-       SmtpPhase = "MAIL wait";
-       setproctitle("%s %s: %s", CurEnv->e_id, CurHostName, SmtpPhase);
-       r = reply(m);
+       SmtpPhase = mci->mci_phase = "client MAIL";
+       setproctitle("%s %s: %s", e->e_id, CurHostName, mci->mci_phase);
+       r = reply(m, mci, e, TimeOuts.to_mail, NULL);
        if (r < 0 || REPLYTYPE(r) == 4)
        if (r < 0 || REPLYTYPE(r) == 4)
-               goto tempfail;
+       {
+               mci->mci_exitstat = EX_TEMPFAIL;
+               mci->mci_errno = errno;
+               smtpquit(m, mci, e);
+               return EX_TEMPFAIL;
+       }
        else if (r == 250)
        else if (r == 250)
-               return (EX_OK);
+       {
+               mci->mci_exitstat = EX_OK;
+               return EX_OK;
+       }
        else if (r == 552)
        else if (r == 552)
-               goto unavailable;
-
-       /* protocol error -- close up */
-       smtpquit(m);
-       return (EX_PROTOCOL);
-
-       /* signal a temporary failure */
-  tempfail:
-       smtpquit(m);
-       return (EX_TEMPFAIL);
-
-       /* signal service unavailable */
-  unavailable:
-       smtpquit(m);
-       return (EX_UNAVAILABLE);
-}
+       {
+               /* signal service unavailable */
+               mci->mci_exitstat = EX_UNAVAILABLE;
+               smtpquit(m, mci, e);
+               return EX_UNAVAILABLE;
+       }
 
 
+#ifdef LOG
+       if (LogLevel > 1)
+       {
+               syslog(LOG_CRIT, "%s: SMTP MAIL protocol error: %s",
+                       e->e_id, SmtpReplyBuffer);
+       }
+#endif
 
 
-static
-greettimeout()
-{
-       /* timeout reading the greeting message */
-       longjmp(CtxGreeting, 1);
+       /* protocol error -- close up */
+       smtpquit(m, mci, e);
+       mci->mci_exitstat = EX_PROTOCOL;
+       return EX_PROTOCOL;
 }
 \f/*
 **  SMTPRCPT -- designate recipient.
 }
 \f/*
 **  SMTPRCPT -- designate recipient.
@@ -235,6 +386,8 @@ greettimeout()
 **     Parameters:
 **             to -- address of recipient.
 **             m -- the mailer we are sending to.
 **     Parameters:
 **             to -- address of recipient.
 **             m -- the mailer we are sending to.
+**             mci -- the connection info for this transaction.
+**             e -- the envelope for this transaction.
 **
 **     Returns:
 **             exit status corresponding to recipient status.
 **
 **     Returns:
 **             exit status corresponding to recipient status.
@@ -243,18 +396,19 @@ greettimeout()
 **             Sends the mail via SMTP.
 */
 
 **             Sends the mail via SMTP.
 */
 
-smtprcpt(to, m)
+smtprcpt(to, m, mci, e)
        ADDRESS *to;
        register MAILER *m;
        ADDRESS *to;
        register MAILER *m;
+       MCI *mci;
+       ENVELOPE *e;
 {
        register int r;
 {
        register int r;
-       extern char *remotename();
 
 
-       smtpmessage("RCPT To:<%s>", m, remotename(to->q_user, m, FALSE, TRUE));
+       smtpmessage("RCPT To:<%s>", m, mci, to->q_user);
 
 
-       SmtpPhase = "RCPT wait";
-       setproctitle("%s %s: %s", CurEnv->e_id, CurHostName, SmtpPhase);
-       r = reply(m);
+       SmtpPhase = mci->mci_phase = "client RCPT";
+       setproctitle("%s %s: %s", e->e_id, CurHostName, mci->mci_phase);
+       r = reply(m, mci, e, TimeOuts.to_rcpt, NULL);
        if (r < 0 || REPLYTYPE(r) == 4)
                return (EX_TEMPFAIL);
        else if (REPLYTYPE(r) == 2)
        if (r < 0 || REPLYTYPE(r) == 4)
                return (EX_TEMPFAIL);
        else if (REPLYTYPE(r) == 2)
@@ -263,6 +417,15 @@ smtprcpt(to, m)
                return (EX_NOUSER);
        else if (r == 552 || r == 554)
                return (EX_UNAVAILABLE);
                return (EX_NOUSER);
        else if (r == 552 || r == 554)
                return (EX_UNAVAILABLE);
+
+#ifdef LOG
+       if (LogLevel > 1)
+       {
+               syslog(LOG_CRIT, "%s: SMTP RCPT protocol error: %s",
+                       e->e_id, SmtpReplyBuffer);
+       }
+#endif
+
        return (EX_PROTOCOL);
 }
 \f/*
        return (EX_PROTOCOL);
 }
 \f/*
@@ -279,8 +442,9 @@ smtprcpt(to, m)
 **             none.
 */
 
 **             none.
 */
 
-smtpdata(m, e)
+smtpdata(m, mci, e)
        struct mailer *m;
        struct mailer *m;
+       register MCI *mci;
        register ENVELOPE *e;
 {
        register int r;
        register ENVELOPE *e;
 {
        register int r;
@@ -294,37 +458,67 @@ smtpdata(m, e)
        */
 
        /* send the command and check ok to proceed */
        */
 
        /* send the command and check ok to proceed */
-       smtpmessage("DATA", m);
-       SmtpPhase = "DATA wait";
-       setproctitle("%s %s: %s", CurEnv->e_id, CurHostName, SmtpPhase);
-       r = reply(m);
+       smtpmessage("DATA", m, mci);
+       SmtpPhase = mci->mci_phase = "client DATA 354";
+       setproctitle("%s %s: %s", e->e_id, CurHostName, mci->mci_phase);
+       r = reply(m, mci, e, TimeOuts.to_datainit, NULL);
        if (r < 0 || REPLYTYPE(r) == 4)
        if (r < 0 || REPLYTYPE(r) == 4)
+       {
+               smtpquit(m, mci, e);
                return (EX_TEMPFAIL);
                return (EX_TEMPFAIL);
+       }
        else if (r == 554)
        else if (r == 554)
+       {
+               smtprset(m, mci, e);
                return (EX_UNAVAILABLE);
                return (EX_UNAVAILABLE);
+       }
        else if (r != 354)
        else if (r != 354)
+       {
+#ifdef LOG
+               if (LogLevel > 1)
+               {
+                       syslog(LOG_CRIT, "%s: SMTP DATA-1 protocol error: %s",
+                               e->e_id, SmtpReplyBuffer);
+               }
+#endif
+               smtprset(m, mci, e);
                return (EX_PROTOCOL);
                return (EX_PROTOCOL);
+       }
 
        /* now output the actual message */
 
        /* now output the actual message */
-       (*e->e_puthdr)(SmtpOut, m, CurEnv);
-       putline("\n", SmtpOut, m);
-       (*e->e_putbody)(SmtpOut, m, CurEnv);
+       (*e->e_puthdr)(mci->mci_out, m, e);
+       putline("\n", mci->mci_out, m);
+       (*e->e_putbody)(mci->mci_out, m, e, NULL);
 
        /* terminate the message */
 
        /* terminate the message */
-       fprintf(SmtpOut, ".%s", m->m_eol);
-       if (Verbose && !HoldErrs)
-               nmessage(Arpa_Info, ">>> .");
+       fprintf(mci->mci_out, ".%s", m->m_eol);
+       if (Verbose)
+               nmessage(">>> .");
 
        /* check for the results of the transaction */
 
        /* check for the results of the transaction */
-       SmtpPhase = "result wait";
-       setproctitle("%s %s: %s", CurEnv->e_id, CurHostName, SmtpPhase);
-       r = reply(m);
-       if (r < 0 || REPLYTYPE(r) == 4)
+       SmtpPhase = mci->mci_phase = "client DATA 250";
+       setproctitle("%s %s: %s", e->e_id, CurHostName, mci->mci_phase);
+       r = reply(m, mci, e, TimeOuts.to_datafinal, NULL);
+       if (r < 0)
+       {
+               smtpquit(m, mci, e);
+               return (EX_TEMPFAIL);
+       }
+       mci->mci_state = MCIS_OPEN;
+       e->e_statmsg = newstr(&SmtpReplyBuffer[4]);
+       if (REPLYTYPE(r) == 4)
                return (EX_TEMPFAIL);
        else if (r == 250)
                return (EX_OK);
        else if (r == 552 || r == 554)
                return (EX_UNAVAILABLE);
                return (EX_TEMPFAIL);
        else if (r == 250)
                return (EX_OK);
        else if (r == 552 || r == 554)
                return (EX_UNAVAILABLE);
+#ifdef LOG
+       if (LogLevel > 1)
+       {
+               syslog(LOG_CRIT, "%s: SMTP DATA-2 protocol error: %s",
+                       e->e_id, SmtpReplyBuffer);
+       }
+#endif
        return (EX_PROTOCOL);
 }
 \f/*
        return (EX_PROTOCOL);
 }
 \f/*
@@ -340,40 +534,81 @@ smtpdata(m, e)
 **             sends the final protocol and closes the connection.
 */
 
 **             sends the final protocol and closes the connection.
 */
 
-smtpquit(m)
+smtpquit(m, mci, e)
        register MAILER *m;
        register MAILER *m;
+       register MCI *mci;
+       ENVELOPE *e;
 {
        int i;
 
 {
        int i;
 
-       /* if the connection is already closed, don't bother */
-       if (SmtpIn == NULL)
-               return;
-
-       /* send the quit message if not a forced quit */
-       if (SmtpState == SMTP_OPEN || SmtpState == SMTP_SSD)
+       /* send the quit message if we haven't gotten I/O error */
+       if (mci->mci_state != MCIS_ERROR)
        {
        {
-               smtpmessage("QUIT", m);
-               (void) reply(m);
-               if (SmtpState == SMTP_CLOSED)
+               SmtpPhase = "client QUIT";
+               smtpmessage("QUIT", m, mci);
+               (void) reply(m, mci, e, TimeOuts.to_quit, NULL);
+               if (mci->mci_state == MCIS_CLOSED)
                        return;
        }
 
                        return;
        }
 
-       /* now actually close the connection */
-       (void) fclose(SmtpIn);
-       (void) fclose(SmtpOut);
-       SmtpIn = SmtpOut = NULL;
-       SmtpState = SMTP_CLOSED;
-
-       /* and pick up the zombie */
-       i = endmailer(SmtpPid, m->m_argv[0]);
+       /* now actually close the connection and pick up the zombie */
+       i = endmailer(mci, e, m->m_argv);
        if (i != EX_OK)
        if (i != EX_OK)
-               syserr("smtpquit %s: stat %d", m->m_argv[0], i);
+               syserr("451 smtpquit %s: stat %d", m->m_argv[0], i);
+}
+\f/*
+**  SMTPRSET -- send a RSET (reset) command
+*/
+
+smtprset(m, mci, e)
+       register MAILER *m;
+       register MCI *mci;
+       ENVELOPE *e;
+{
+       int r;
+
+       SmtpPhase = "client RSET";
+       smtpmessage("RSET", m, mci);
+       r = reply(m, mci, e, TimeOuts.to_rset, NULL);
+       if (r < 0)
+               mci->mci_state = MCIS_ERROR;
+       else if (REPLYTYPE(r) == 2)
+       {
+               mci->mci_state = MCIS_OPEN;
+               return;
+       }
+       smtpquit(m, mci, e);
+}
+\f/*
+**  SMTPPROBE -- check the connection state
+*/
+
+smtpprobe(mci)
+       register MCI *mci;
+{
+       int r;
+       MAILER *m = mci->mci_mailer;
+       extern ENVELOPE BlankEnvelope;
+       ENVELOPE *e = &BlankEnvelope;
+
+       SmtpPhase = "client probe";
+       smtpmessage("RSET", m, mci);
+       r = reply(m, mci, e, TimeOuts.to_miscshort, NULL);
+       if (r < 0 || REPLYTYPE(r) != 2)
+               smtpquit(m, mci, e);
+       return r;
 }
 \f/*
 **  REPLY -- read arpanet reply
 **
 **     Parameters:
 **             m -- the mailer we are reading the reply from.
 }
 \f/*
 **  REPLY -- read arpanet reply
 **
 **     Parameters:
 **             m -- the mailer we are reading the reply from.
+**             mci -- the mailer connection info structure.
+**             e -- the current envelope.
+**             timeout -- the timeout for reads.
+**             pfunc -- processing function for second and subsequent
+**                     lines of response -- if null, no special
+**                     processing is done.
 **
 **     Returns:
 **             reply code it reads.
 **
 **     Returns:
 **             reply code it reads.
@@ -382,10 +617,20 @@ smtpquit(m)
 **             flushes the mail file.
 */
 
 **             flushes the mail file.
 */
 
-reply(m)
+reply(m, mci, e, timeout, pfunc)
        MAILER *m;
        MAILER *m;
+       MCI *mci;
+       ENVELOPE *e;
+       time_t timeout;
+       void (*pfunc)();
 {
 {
-       (void) fflush(SmtpOut);
+       register char *bufp;
+       register int r;
+       bool firstline = TRUE;
+       char junkbuf[MAXLINE];
+
+       if (mci->mci_out != NULL)
+               (void) fflush(mci->mci_out);
 
        if (tTd(18, 1))
                printf("reply\n");
 
        if (tTd(18, 1))
                printf("reply\n");
@@ -394,87 +639,111 @@ reply(m)
        **  Read the input line, being careful not to hang.
        */
 
        **  Read the input line, being careful not to hang.
        */
 
-       for (;;)
+       for (bufp = SmtpReplyBuffer;; bufp = junkbuf)
        {
        {
-               register int r;
                register char *p;
                register char *p;
+               extern time_t curtime();
 
                /* actually do the read */
 
                /* actually do the read */
-               if (CurEnv->e_xfp != NULL)
-                       (void) fflush(CurEnv->e_xfp);   /* for debugging */
+               if (e->e_xfp != NULL)
+                       (void) fflush(e->e_xfp);        /* for debugging */
 
                /* if we are in the process of closing just give the code */
 
                /* if we are in the process of closing just give the code */
-               if (SmtpState == SMTP_CLOSED)
+               if (mci->mci_state == MCIS_CLOSED)
                        return (SMTPCLOSING);
 
                        return (SMTPCLOSING);
 
+               if (mci->mci_out != NULL)
+                       fflush(mci->mci_out);
+
                /* get the line from the other side */
                /* get the line from the other side */
-               p = sfgets(SmtpReplyBuffer, sizeof SmtpReplyBuffer, SmtpIn);
+               p = sfgets(bufp, MAXLINE, mci->mci_in, timeout, SmtpPhase);
+               mci->mci_lastuse = curtime();
+
                if (p == NULL)
                {
                        extern char MsgBuf[];           /* err.c */
                if (p == NULL)
                {
                        extern char MsgBuf[];           /* err.c */
-                       extern char Arpa_TSyserr[];     /* conf.c */
 
                        /* if the remote end closed early, fake an error */
                        if (errno == 0)
 # ifdef ECONNRESET
                                errno = ECONNRESET;
 
                        /* if the remote end closed early, fake an error */
                        if (errno == 0)
 # ifdef ECONNRESET
                                errno = ECONNRESET;
-# else ECONNRESET
+# else /* ECONNRESET */
                                errno = EPIPE;
                                errno = EPIPE;
-# endif ECONNRESET
+# endif /* ECONNRESET */
 
 
-                       message(Arpa_TSyserr, "reply: read error");
+                       mci->mci_errno = errno;
+                       mci->mci_exitstat = EX_TEMPFAIL;
+                       message("451 %s: reply: read error from %s",
+                               e->e_id == NULL ? "NOQUEUE" : e->e_id,
+                               mci->mci_host);
                        /* if debugging, pause so we can see state */
                        if (tTd(18, 100))
                                pause();
 # ifdef LOG
                        /* if debugging, pause so we can see state */
                        if (tTd(18, 100))
                                pause();
 # ifdef LOG
-                       syslog(LOG_INFO, "%s", &MsgBuf[4]);
-# endif LOG
-                       SmtpState = SMTP_CLOSED;
-                       smtpquit(m);
+                       if (LogLevel > 1)
+                               syslog(LOG_INFO, "%s", &MsgBuf[4]);
+# endif /* LOG */
+                       mci->mci_state = MCIS_ERROR;
+                       smtpquit(m, mci, e);
                        return (-1);
                }
                        return (-1);
                }
-               fixcrlf(SmtpReplyBuffer, TRUE);
+               fixcrlf(bufp, TRUE);
 
 
-               if (CurEnv->e_xfp != NULL && index("45", SmtpReplyBuffer[0]) != NULL)
+               if (e->e_xfp != NULL && strchr("45", bufp[0]) != NULL)
                {
                        /* serious error -- log the previous command */
                        if (SmtpMsgBuffer[0] != '\0')
                {
                        /* serious error -- log the previous command */
                        if (SmtpMsgBuffer[0] != '\0')
-                               fprintf(CurEnv->e_xfp, ">>> %s\n", SmtpMsgBuffer);
+                               fprintf(e->e_xfp, ">>> %s\n", SmtpMsgBuffer);
                        SmtpMsgBuffer[0] = '\0';
 
                        /* now log the message as from the other side */
                        SmtpMsgBuffer[0] = '\0';
 
                        /* now log the message as from the other side */
-                       fprintf(CurEnv->e_xfp, "<<< %s\n", SmtpReplyBuffer);
+                       fprintf(e->e_xfp, "<<< %s\n", bufp);
                }
 
                /* display the input for verbose mode */
                }
 
                /* display the input for verbose mode */
-               if (Verbose && !HoldErrs)
-                       nmessage(Arpa_Info, "%s", SmtpReplyBuffer);
+               if (Verbose)
+                       nmessage("050 %s", bufp);
+
+               /* process the line */
+               if (pfunc != NULL && !firstline)
+                       (*pfunc)(bufp, m, mci, e);
+
+               firstline = FALSE;
 
                /* if continuation is required, we can go on */
 
                /* if continuation is required, we can go on */
-               if (SmtpReplyBuffer[3] == '-' || !isdigit(SmtpReplyBuffer[0]))
+               if (bufp[3] == '-')
+                       continue;
+
+               /* ignore improperly formated input */
+               if (!(isascii(bufp[0]) && isdigit(bufp[0])))
                        continue;
 
                /* decode the reply code */
                        continue;
 
                /* decode the reply code */
-               r = atoi(SmtpReplyBuffer);
+               r = atoi(bufp);
 
                /* extra semantics: 0xx codes are "informational" */
 
                /* extra semantics: 0xx codes are "informational" */
-               if (r < 100)
-                       continue;
+               if (r >= 100)
+                       break;
+       }
 
 
-               /* reply code 421 is "Service Shutting Down" */
-               if (r == SMTPCLOSING && SmtpState != SMTP_SSD)
-               {
-                       /* send the quit protocol */
-                       SmtpState = SMTP_SSD;
-                       smtpquit(m);
-               }
+       /*
+       **  Now look at SmtpReplyBuffer -- only care about the first
+       **  line of the response from here on out.
+       */
 
 
-               /* save temporary failure messages for posterity */
-               if (SmtpReplyBuffer[0] == '4' && SmtpError[0] == '\0')
-                       (void) strcpy(SmtpError, &SmtpReplyBuffer[4]);
+       /* save temporary failure messages for posterity */
+       if (SmtpReplyBuffer[0] == '4' && SmtpError[0] == '\0')
+               (void) strcpy(SmtpError, SmtpReplyBuffer);
 
 
-               return (r);
+       /* reply code 421 is "Service Shutting Down" */
+       if (r == SMTPCLOSING && mci->mci_state != MCIS_SSD)
+       {
+               /* send the quit protocol */
+               mci->mci_state = MCIS_SSD;
+               smtpquit(m, mci, e);
        }
        }
+
+       return (r);
 }
 \f/*
 **  SMTPMESSAGE -- send message to server
 }
 \f/*
 **  SMTPMESSAGE -- send message to server
@@ -488,20 +757,37 @@ reply(m)
 **             none.
 **
 **     Side Effects:
 **             none.
 **
 **     Side Effects:
-**             writes message to SmtpOut.
+**             writes message to mci->mci_out.
 */
 
 /*VARARGS1*/
 */
 
 /*VARARGS1*/
-smtpmessage(f, m, a, b, c)
+#ifdef __STDC__
+smtpmessage(char *f, MAILER *m, MCI *mci, ...)
+#else
+smtpmessage(f, m, mci, va_alist)
        char *f;
        MAILER *m;
        char *f;
        MAILER *m;
+       MCI *mci;
+       va_dcl
+#endif
 {
 {
-       (void) sprintf(SmtpMsgBuffer, f, a, b, c);
-       if (tTd(18, 1) || (Verbose && !HoldErrs))
-               nmessage(Arpa_Info, ">>> %s", SmtpMsgBuffer);
-       if (SmtpOut != NULL)
-               fprintf(SmtpOut, "%s%s", SmtpMsgBuffer,
-                       m == 0 ? "\r\n" : m->m_eol);
+       VA_LOCAL_DECL
+
+       VA_START(mci);
+       (void) vsprintf(SmtpMsgBuffer, f, ap);
+       VA_END;
+
+       if (tTd(18, 1) || Verbose)
+               nmessage(">>> %s", SmtpMsgBuffer);
+       if (mci->mci_out != NULL)
+       {
+               fprintf(mci->mci_out, "%s%s", SmtpMsgBuffer,
+                       m == NULL ? "\r\n" : m->m_eol);
+       }
+       else if (tTd(18, 1))
+       {
+               printf("smtpmessage: NULL mci_out\n");
+       }
 }
 
 }
 
-# endif SMTP
+# endif /* SMTP */