BSD 4_4_Lite2 release
[unix-history] / usr / src / usr.sbin / sendmail / src / srvrsmtp.c
index 9d18d8a..b32d4bd 100644 (file)
@@ -1,18 +1,44 @@
 /*
 /*
- * Copyright (c) 1983 Eric P. Allman
+ * Copyright (c) 1983, 1995 Eric P. Allman
  * Copyright (c) 1988, 1993
  *     The Regents of the University of California.  All rights reserved.
  *
  * Copyright (c) 1988, 1993
  *     The Regents of the University of California.  All rights reserved.
  *
- * %sccs.include.redist.c%
+ * 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[] = "@(#)srvrsmtp.c 8.37.1.1 (Berkeley) %G% (with SMTP)";
+static char sccsid[] = "@(#)srvrsmtp.c 8.83 (Berkeley) 6/21/95 (with SMTP)";
 #else
 #else
-static char sccsid[] = "@(#)srvrsmtp.c 8.37.1.1 (Berkeley) %G% (without SMTP)";
+static char sccsid[] = "@(#)srvrsmtp.c 8.83 (Berkeley) 6/21/95 (without SMTP)";
 #endif
 #endif /* not lint */
 
 #endif
 #endif /* not lint */
 
@@ -45,7 +71,6 @@ struct cmd
 # define CMDMAIL       1       /* mail -- designate sender */
 # define CMDRCPT       2       /* rcpt -- designate recipient */
 # define CMDDATA       3       /* data -- send message text */
 # define CMDMAIL       1       /* mail -- designate sender */
 # define CMDRCPT       2       /* rcpt -- designate recipient */
 # define CMDDATA       3       /* data -- send message text */
-# define CMDHOPS       4       /* hops -- specify hop count */
 # define CMDRSET       4       /* rset -- reset state */
 # define CMDVRFY       5       /* vrfy -- verify address */
 # define CMDEXPN       6       /* expn -- expand address */
 # define CMDRSET       4       /* rset -- reset state */
 # define CMDVRFY       5       /* vrfy -- verify address */
 # define CMDEXPN       6       /* expn -- expand address */
@@ -71,7 +96,6 @@ static struct cmd     CmdTab[] =
        "rset",         CMDRSET,
        "vrfy",         CMDVRFY,
        "expn",         CMDEXPN,
        "rset",         CMDRSET,
        "vrfy",         CMDVRFY,
        "expn",         CMDEXPN,
-       "expn",         CMDVRFY,
        "help",         CMDHELP,
        "noop",         CMDNOOP,
        "quit",         CMDQUIT,
        "help",         CMDHELP,
        "noop",         CMDNOOP,
        "quit",         CMDQUIT,
@@ -79,7 +103,6 @@ static struct cmd    CmdTab[] =
        "ehlo",         CMDEHLO,
        "verb",         CMDVERB,
        "onex",         CMDONEX,
        "ehlo",         CMDEHLO,
        "verb",         CMDVERB,
        "onex",         CMDONEX,
-       "hops",         CMDHOPS,
        /*
         * remaining commands are here only
         * to trap and log attempts to use them
        /*
         * remaining commands are here only
         * to trap and log attempts to use them
@@ -94,21 +117,35 @@ bool       OneXact = FALSE;                /* one xaction only this run */
 char   *CurSmtpClient;                 /* who's at the other end of channel */
 
 static char    *skipword();
 char   *CurSmtpClient;                 /* who's at the other end of channel */
 
 static char    *skipword();
-extern char    RealUserName[];
 
 
 #define MAXBADCOMMANDS 25              /* maximum number of bad commands */
 
 
 
 #define MAXBADCOMMANDS 25              /* maximum number of bad commands */
 
+void
 smtp(e)
        register ENVELOPE *e;
 {
        register char *p;
        register struct cmd *c;
        char *cmd;
 smtp(e)
        register ENVELOPE *e;
 {
        register char *p;
        register struct cmd *c;
        char *cmd;
-       extern ADDRESS *sendto();
+       auto ADDRESS *vrfyqueue;
        ADDRESS *a;
        ADDRESS *a;
+       bool gotmail;                   /* mail command received */
+       bool gothello;                  /* helo command received */
+       bool vrfy;                      /* set if this is a vrfy command */
+       char *protocol;                 /* sending protocol */
+       char *sendinghost;              /* sending hostname */
+       char *peerhostname;             /* name of SMTP peer or "localhost" */
+       auto char *delimptr;
+       char *id;
+       int nrcpts = 0;                 /* number of RCPT commands */
+       bool doublequeue;
+       int badcommands = 0;            /* count of bad commands */
+       char inp[MAXLINE];
+       char cmdbuf[MAXLINE];
+       extern ENVELOPE BlankEnvelope;
+       extern void help __P((char *));
 
 
-       hasmail = FALSE;
        if (fileno(OutChannel) != fileno(stdout))
        {
                /* arrange for debugging output to go to remote host */
        if (fileno(OutChannel) != fileno(stdout))
        {
                /* arrange for debugging output to go to remote host */
@@ -124,28 +161,41 @@ smtp(e)
                CurSmtpClient = CurHostName;
 
        setproctitle("server %s startup", CurSmtpClient);
                CurSmtpClient = CurHostName;
 
        setproctitle("server %s startup", CurSmtpClient);
-       expand("\201e", inp, &inp[sizeof inp], e);
-       if (BrokenSmtpPeers)
+#ifdef LOG
+       if (LogLevel > 11)
        {
        {
-               p = strchr(inp, '\n');
-               if (p != NULL)
-                       *p = '\0';
-               message("220 %s", inp);
+               /* log connection information */
+               syslog(LOG_INFO, "SMTP connect from %s (%s)",
+                       CurSmtpClient, anynet_ntoa(&RealHostAddr));
        }
        }
-       else
-       {
-               char *q = inp;
+#endif
 
 
-               while (q != NULL)
-               {
-                       p = strchr(q, '\n');
-                       if (p != NULL)
-                               *p++ = '\0';
-                       message("220-%s", q);
-                       q = p;
-               }
-               message("220 ESMTP spoken here");
+       /* output the first line, inserting "ESMTP" as second word */
+       expand("\201e", inp, sizeof inp, e);
+       p = strchr(inp, '\n');
+       if (p != NULL)
+               *p++ = '\0';
+       id = strchr(inp, ' ');
+       if (id == NULL)
+               id = &inp[strlen(inp)];
+       cmd = p == NULL ? "220 %.*s ESMTP%s" : "220-%.*s ESMTP%s";
+       message(cmd, id - inp, inp, id);
+
+       /* output remaining lines */
+       while ((id = p) != NULL && (p = strchr(id, '\n')) != NULL)
+       {
+               *p++ = '\0';
+               if (isascii(*id) && isspace(*id))
+                       id++;
+               message("220-%s", id);
        }
        }
+       if (id != NULL)
+       {
+               if (isascii(*id) && isspace(*id))
+                       id++;
+               message("220 %s", id);
+       }
+
        protocol = NULL;
        sendinghost = macvalue('s', e);
        gothello = FALSE;
        protocol = NULL;
        sendinghost = macvalue('s', e);
        gothello = FALSE;
@@ -175,7 +225,7 @@ smtp(e)
 
                /* read the input line */
                SmtpPhase = "server cmd read";
 
                /* read the input line */
                SmtpPhase = "server cmd read";
-               setproctitle("server %s cmd read", CurHostName);
+               setproctitle("server %s cmd read", CurSmtpClient);
                p = sfgets(inp, sizeof inp, InChannel, TimeOuts.to_nextcommand,
                                SmtpPhase);
 
                p = sfgets(inp, sizeof inp, InChannel, TimeOuts.to_nextcommand,
                                SmtpPhase);
 
@@ -276,7 +326,31 @@ smtp(e)
                        }
 
                        sendinghost = newstr(p);
                        }
 
                        sendinghost = newstr(p);
-                       message("250", "%s Hello %s, pleased to meet you", HostName, p);
+                       gothello = TRUE;
+                       if (c->cmdcode != CMDEHLO)
+                       {
+                               /* print old message and be done with it */
+                               message("250 %s Hello %s, pleased to meet you",
+                                       MyHostName, CurSmtpClient);
+                               break;
+                       }
+                       
+                       /* print extended message and brag */
+                       message("250-%s Hello %s, pleased to meet you",
+                               MyHostName, CurSmtpClient);
+                       if (!bitset(PRIV_NOEXPN, PrivacyFlags))
+                               message("250-EXPN");
+#if MIME8TO7
+                       message("250-8BITMIME");
+#endif
+                       if (MaxMessageSize > 0)
+                               message("250-SIZE %ld", MaxMessageSize);
+                       else
+                               message("250-SIZE");
+#if DSN
+                       message("250-X-DSN-04 (Draft of May 29, 1995)");
+#endif
+                       message("250 HELP");
                        break;
 
                  case CMDMAIL:         /* mail -- designate sender */
                        break;
 
                  case CMDMAIL:         /* mail -- designate sender */
@@ -316,7 +390,7 @@ smtp(e)
                        {
                                auth_warning(e,
                                        "Host %s didn't use HELO protocol",
                        {
                                auth_warning(e,
                                        "Host %s didn't use HELO protocol",
-                                       peerhostname);
+                                       CurSmtpClient);
                        }
 #ifdef PICKY_HELO_CHECK
                        if (strcasecmp(sendinghost, peerhostname) != 0 &&
                        }
 #ifdef PICKY_HELO_CHECK
                        if (strcasecmp(sendinghost, peerhostname) != 0 &&
@@ -324,7 +398,7 @@ smtp(e)
                             strcasecmp(sendinghost, MyHostName) != 0))
                        {
                                auth_warning(e, "Host %s claimed to be %s",
                             strcasecmp(sendinghost, MyHostName) != 0))
                        {
                                auth_warning(e, "Host %s claimed to be %s",
-                                       peerhostname, sendinghost);
+                                       CurSmtpClient, sendinghost);
                        }
 #endif
 
                        }
 #endif
 
@@ -334,7 +408,7 @@ smtp(e)
                        define('s', sendinghost, e);
                        initsys(e);
                        nrcpts = 0;
                        define('s', sendinghost, e);
                        initsys(e);
                        nrcpts = 0;
-                       e->e_flags |= EF_LOGSENDER;
+                       e->e_flags |= EF_LOGSENDER|EF_CLRQUEUE;
                        setproctitle("%s %s: %.80s", e->e_id, CurSmtpClient, inp);
 
                        /* child -- go do the processing */
                        setproctitle("%s %s: %.80s", e->e_id, CurSmtpClient, inp);
 
                        /* child -- go do the processing */
@@ -364,6 +438,7 @@ smtp(e)
 
                        /* check for possible spoofing */
                        if (RealUid != 0 && OpMode == MD_SMTP &&
 
                        /* check for possible spoofing */
                        if (RealUid != 0 && OpMode == MD_SMTP &&
+                           !wordinclass(RealUserName, 't') &&
                            !bitnset(M_LOCALMAILER, e->e_from.q_mailer->m_flags) &&
                            strcmp(e->e_from.q_user, RealUserName) != 0)
                        {
                            !bitnset(M_LOCALMAILER, e->e_from.q_mailer->m_flags) &&
                            strcmp(e->e_from.q_user, RealUserName) != 0)
                        {
@@ -372,11 +447,12 @@ smtp(e)
                        }
 
                        /* now parse ESMTP arguments */
                        }
 
                        /* now parse ESMTP arguments */
-                       msize = 0;
+                       e->e_msgsize = 0;
                        while (p != NULL && *p != '\0')
                        {
                                char *kp;
                                char *vp = NULL;
                        while (p != NULL && *p != '\0')
                        {
                                char *kp;
                                char *vp = NULL;
+                               extern void mail_esmtp_args __P((char *, char *, ENVELOPE *));
 
                                /* locate the beginning of the keyword */
                                while (isascii(*p) && isspace(*p))
 
                                /* locate the beginning of the keyword */
                                while (isascii(*p) && isspace(*p))
@@ -407,77 +483,23 @@ smtp(e)
                                        printf("MAIL: got arg %s=\"%s\"\n", kp,
                                                vp == NULL ? "<null>" : vp);
 
                                        printf("MAIL: got arg %s=\"%s\"\n", kp,
                                                vp == NULL ? "<null>" : vp);
 
-                               if (strcasecmp(kp, "size") == 0)
-                               {
-                                       if (vp == NULL)
-                                       {
-                                               usrerr("501 SIZE requires a value");
-                                               /* NOTREACHED */
-                                       }
-# ifdef __STDC__
-                                       msize = strtoul(vp, (char **) NULL, 10);
-# else
-                                       msize = strtol(vp, (char **) NULL, 10);
-# endif
-                               }
-                               else if (strcasecmp(kp, "body") == 0)
-                               {
-                                       if (vp == NULL)
-                                       {
-                                               usrerr("501 BODY requires a value");
-                                               /* NOTREACHED */
-                                       }
-                                       if (strcasecmp(vp, "8bitmime") == 0)
-                                       {
-                                               SevenBitInput = FALSE;
-                                       }
-                                       else if (strcasecmp(vp, "7bit") == 0)
-                                       {
-                                               SevenBitInput = TRUE;
-                                       }
-                                       else
-                                       {
-                                               usrerr("501 Unknown BODY type %s",
-                                                       vp);
-                                               /* NOTREACHED */
-                                       }
-                                       e->e_bodytype = newstr(vp);
-                               }
-                               else if (strcasecmp(kp, "envid") == 0)
-                               {
-                                       if (vp == NULL)
-                                       {
-                                               usrerr("501 ENVID requires a value");
-                                               /* NOTREACHED */
-                                       }
-                                       e->e_envid = newstr(vp);
-                               }
-                               else
-                               {
-                                       usrerr("501 %s parameter unrecognized", kp);
-                                       /* NOTREACHED */
-                               }
+                               mail_esmtp_args(kp, vp, e);
                        }
 
                        }
 
-                       if (MaxMessageSize > 0 && msize > MaxMessageSize)
+                       if (MaxMessageSize > 0 && e->e_msgsize > MaxMessageSize)
                        {
                                usrerr("552 Message size exceeds fixed maximum message size (%ld)",
                                        MaxMessageSize);
                                /* NOTREACHED */
                        }
                                
                        {
                                usrerr("552 Message size exceeds fixed maximum message size (%ld)",
                                        MaxMessageSize);
                                /* NOTREACHED */
                        }
                                
-                       if (!enoughspace(msize))
+                       if (!enoughdiskspace(e->e_msgsize))
                        {
                                message("452 Insufficient disk space; try again later");
                                break;
                        }
                        message("250 Sender ok");
                        gotmail = TRUE;
                        {
                                message("452 Insufficient disk space; try again later");
                                break;
                        }
                        message("250 Sender ok");
                        gotmail = TRUE;
-
-                       /* optimize: non-interactive, don't expand aliases */
-                       if (e->e_sendmode != SM_DELIVER)
-                               e->e_flags |= EF_VRFYONLY;
-
                        break;
 
                  case CMDRCPT:         /* rcpt -- designate recipient */
                        break;
 
                  case CMDRCPT:         /* rcpt -- designate recipient */
@@ -494,14 +516,60 @@ smtp(e)
                        }
                        QuickAbort = TRUE;
                        LogUsrErrs = TRUE;
                        }
                        QuickAbort = TRUE;
                        LogUsrErrs = TRUE;
+
+                       if (e->e_sendmode != SM_DELIVER)
+                               e->e_flags |= EF_VRFYONLY;
+
                        p = skipword(p, "to");
                        if (p == NULL)
                                break;
                        p = skipword(p, "to");
                        if (p == NULL)
                                break;
-                       a = sendto(p, 1, (ADDRESS *) NULL, 0);
-# ifdef DEBUG
-                       if (Debug > 1)
-                               printaddr(a, TRUE);
-# endif DEBUG
+                       a = parseaddr(p, NULLADDR, RF_COPYALL, ' ', &delimptr, e);
+                       if (a == NULL)
+                               break;
+                       p = delimptr;
+
+                       /* now parse ESMTP arguments */
+                       while (p != NULL && *p != '\0')
+                       {
+                               char *kp;
+                               char *vp = NULL;
+                               extern void rcpt_esmtp_args __P((ADDRESS *, char *, char *, ENVELOPE *));
+
+                               /* locate the beginning of the keyword */
+                               while (isascii(*p) && isspace(*p))
+                                       p++;
+                               if (*p == '\0')
+                                       break;
+                               kp = p;
+
+                               /* skip to the value portion */
+                               while (isascii(*p) && isalnum(*p) || *p == '-')
+                                       p++;
+                               if (*p == '=')
+                               {
+                                       *p++ = '\0';
+                                       vp = p;
+
+                                       /* skip to the end of the value */
+                                       while (*p != '\0' && *p != ' ' &&
+                                              !(isascii(*p) && iscntrl(*p)) &&
+                                              *p != '=')
+                                               p++;
+                               }
+
+                               if (*p != '\0')
+                                       *p++ = '\0';
+
+                               if (tTd(19, 1))
+                                       printf("RCPT: got arg %s=\"%s\"\n", kp,
+                                               vp == NULL ? "<null>" : vp);
+
+                               rcpt_esmtp_args(a, kp, vp, e);
+                       }
+
+                       /* save in recipient list after ESMTP mods */
+                       a = recipient(a, &e->e_sendqueue, 0, e);
+
                        if (Errors != 0)
                                break;
 
                        if (Errors != 0)
                                break;
 
@@ -555,12 +623,17 @@ smtp(e)
 
                        /* collect the text of the message */
                        SmtpPhase = "collect";
 
                        /* collect the text of the message */
                        SmtpPhase = "collect";
+                       buffer_errors();
                        collect(InChannel, TRUE, doublequeue, NULL, e);
                        collect(InChannel, TRUE, doublequeue, NULL, e);
+                       flush_errors(TRUE);
                        if (Errors != 0)
                                goto abortmessage;
 
                        if (Errors != 0)
                                goto abortmessage;
 
+                       /* make sure we actually do delivery */
+                       e->e_flags &= ~EF_CLRQUEUE;
+
                        /* from now on, we have to operate silently */
                        /* from now on, we have to operate silently */
-                       HoldErrs = TRUE;
+                       buffer_errors();
                        e->e_errormode = EM_MAIL;
 
                        /*
                        e->e_errormode = EM_MAIL;
 
                        /*
@@ -611,10 +684,6 @@ smtp(e)
                                (void) dowork(id, TRUE, TRUE, e);
                        }
 
                                (void) dowork(id, TRUE, TRUE, e);
                        }
 
-                       /* now make it really happen */
-                       if (!Verbose && e->e_sendmode != SM_QUEUE)
-                               dowork(id, TRUE, e);
-
   abortmessage:
                        /* if in a child, pop back to our parent */
                        if (InChild)
   abortmessage:
                        /* if in a child, pop back to our parent */
                        if (InChild)
@@ -629,6 +698,9 @@ smtp(e)
 
                  case CMDRSET:         /* rset -- reset state */
                        message("250 Reset state");
 
                  case CMDRSET:         /* rset -- reset state */
                        message("250 Reset state");
+
+                       /* arrange to ignore any current send list */
+                       e->e_sendqueue = NULL;
                        e->e_flags |= EF_CLRQUEUE;
                        if (InChild)
                                finis();
                        e->e_flags |= EF_CLRQUEUE;
                        if (InChild)
                                finis();
@@ -669,7 +741,45 @@ smtp(e)
                        if (LogLevel > 5)
                                syslog(LOG_INFO, "%s: %s", CurSmtpClient, inp);
 #endif
                        if (LogLevel > 5)
                                syslog(LOG_INFO, "%s: %s", CurSmtpClient, inp);
 #endif
-                               paddrtree(a);
+                       vrfyqueue = NULL;
+                       QuickAbort = TRUE;
+                       if (vrfy)
+                               e->e_flags |= EF_VRFYONLY;
+                       while (*p != '\0' && isascii(*p) && isspace(*p))
+                               p++;
+                       if (*p == '\0')
+                       {
+                               message("501 Argument required");
+                               Errors++;
+                       }
+                       else
+                       {
+                               (void) sendtolist(p, NULLADDR, &vrfyqueue, 0, e);
+                       }
+                       if (Errors != 0)
+                       {
+                               if (InChild)
+                                       finis();
+                               break;
+                       }
+                       if (vrfyqueue == NULL)
+                       {
+                               message("554 Nothing to %s", vrfy ? "VRFY" : "EXPN");
+                       }
+                       while (vrfyqueue != NULL)
+                       {
+                               extern void printvrfyaddr __P((ADDRESS *, bool));
+
+                               a = vrfyqueue;
+                               while ((a = a->q_next) != NULL &&
+                                      bitset(QDONTSEND|QBADADDR, a->q_flags))
+                                       continue;
+                               if (!bitset(QDONTSEND|QBADADDR, vrfyqueue->q_flags))
+                                       printvrfyaddr(vrfyqueue, a == NULL);
+                               vrfyqueue = vrfyqueue->q_next;
+                       }
+                       if (InChild)
+                               finis();
                        break;
 
                  case CMDHELP:         /* help -- give user info */
                        break;
 
                  case CMDHELP:         /* help -- give user info */
@@ -684,6 +794,9 @@ smtp(e)
                        message("221 %s closing connection", MyHostName);
 
 doquit:
                        message("221 %s closing connection", MyHostName);
 
 doquit:
+                       /* arrange to ignore any current send list */
+                       e->e_sendqueue = NULL;
+
                        /* avoid future 050 messages */
                        disconnect(1, e);
 
                        /* avoid future 050 messages */
                        disconnect(1, e);
 
@@ -729,7 +842,7 @@ doquit:
                        if (LogLevel > 0)
                                syslog(LOG_CRIT,
                                    "\"%s\" command from %s (%s)",
                        if (LogLevel > 0)
                                syslog(LOG_CRIT,
                                    "\"%s\" command from %s (%s)",
-                                   c->cmdname, peerhostname,
+                                   c->cmdname, CurSmtpClient,
                                    anynet_ntoa(&RealHostAddr));
 # endif
                        /* FALL THROUGH */
                                    anynet_ntoa(&RealHostAddr));
 # endif
                        /* FALL THROUGH */
@@ -807,6 +920,106 @@ skipword(p, w)
        return (p);
 }
 \f/*
        return (p);
 }
 \f/*
+**  MAIL_ESMTP_ARGS -- process ESMTP arguments from MAIL line
+**
+**     Parameters:
+**             kp -- the parameter key.
+**             vp -- the value of that parameter.
+**             e -- the envelope.
+**
+**     Returns:
+**             none.
+*/
+
+void
+mail_esmtp_args(kp, vp, e)
+       char *kp;
+       char *vp;
+       ENVELOPE *e;
+{
+       if (strcasecmp(kp, "size") == 0)
+       {
+               if (vp == NULL)
+               {
+                       usrerr("501 SIZE requires a value");
+                       /* NOTREACHED */
+               }
+# if defined(__STDC__) && !defined(BROKEN_ANSI_LIBRARY)
+               e->e_msgsize = strtoul(vp, (char **) NULL, 10);
+# else
+               e->e_msgsize = strtol(vp, (char **) NULL, 10);
+# endif
+       }
+       else if (strcasecmp(kp, "body") == 0)
+       {
+               if (vp == NULL)
+               {
+                       usrerr("501 BODY requires a value");
+                       /* NOTREACHED */
+               }
+               else if (strcasecmp(vp, "8bitmime") == 0)
+               {
+                       SevenBitInput = FALSE;
+               }
+               else if (strcasecmp(vp, "7bit") == 0)
+               {
+                       SevenBitInput = TRUE;
+               }
+               else
+               {
+                       usrerr("501 Unknown BODY type %s",
+                               vp);
+                       /* NOTREACHED */
+               }
+               e->e_bodytype = newstr(vp);
+       }
+       else if (strcasecmp(kp, "envid") == 0)
+       {
+               if (vp == NULL)
+               {
+                       usrerr("501 ENVID requires a value");
+                       /* NOTREACHED */
+               }
+               if (!xtextok(vp))
+               {
+                       usrerr("501 Syntax error in ENVID parameter value");
+                       /* NOTREACHED */
+               }
+               if (e->e_envid != NULL)
+               {
+                       usrerr("501 Duplicate ENVID parameter");
+                       /* NOTREACHED */
+               }
+               e->e_envid = newstr(vp);
+       }
+       else if (strcasecmp(kp, "ret") == 0)
+       {
+               if (vp == NULL)
+               {
+                       usrerr("501 RET requires a value");
+                       /* NOTREACHED */
+               }
+               if (bitset(EF_RET_PARAM, e->e_flags))
+               {
+                       usrerr("501 Duplicate RET parameter");
+                       /* NOTREACHED */
+               }
+               e->e_flags |= EF_RET_PARAM;
+               if (strcasecmp(vp, "hdrs") == 0)
+                       e->e_flags |= EF_NO_BODY_RETN;
+               else if (strcasecmp(vp, "full") != 0)
+               {
+                       usrerr("501 Bad argument \"%s\" to RET", vp);
+                       /* NOTREACHED */
+               }
+       }
+       else
+       {
+               usrerr("501 %s parameter unrecognized", kp);
+               /* NOTREACHED */
+       }
+}
+\f/*
 **  RCPT_ESMTP_ARGS -- process ESMTP arguments from RCPT line
 **
 **     Parameters:
 **  RCPT_ESMTP_ARGS -- process ESMTP arguments from RCPT line
 **
 **     Parameters:
@@ -819,6 +1032,7 @@ skipword(p, w)
 **             none.
 */
 
 **             none.
 */
 
+void
 rcpt_esmtp_args(a, kp, vp, e)
        ADDRESS *a;
        char *kp;
 rcpt_esmtp_args(a, kp, vp, e)
        ADDRESS *a;
        char *kp;
@@ -835,6 +1049,7 @@ rcpt_esmtp_args(a, kp, vp, e)
                        /* NOTREACHED */
                }
                a->q_flags &= ~(QPINGONSUCCESS|QPINGONFAILURE|QPINGONDELAY);
                        /* NOTREACHED */
                }
                a->q_flags &= ~(QPINGONSUCCESS|QPINGONFAILURE|QPINGONDELAY);
+               a->q_flags |= QHASNOTIFY;
                if (strcasecmp(vp, "never") == 0)
                        return;
                for (p = vp; p != NULL; vp = p)
                if (strcasecmp(vp, "never") == 0)
                        return;
                for (p = vp; p != NULL; vp = p)
@@ -856,27 +1071,21 @@ rcpt_esmtp_args(a, kp, vp, e)
                        }
                }
        }
                        }
                }
        }
-       else if (strcasecmp(kp, "ret") == 0)
+       else if (strcasecmp(kp, "orcpt") == 0)
        {
                if (vp == NULL)
                {
        {
                if (vp == NULL)
                {
-                       usrerr("501 RET requires a value");
+                       usrerr("501 ORCPT requires a value");
                        /* NOTREACHED */
                }
                        /* NOTREACHED */
                }
-               a->q_flags |= QHAS_RET_PARAM;
-               if (strcasecmp(vp, "hdrs") == 0)
-                       a->q_flags |= QRET_HDRS;
-               else if (strcasecmp(vp, "full") != 0)
+               if (strchr(vp, ';') == NULL || !xtextok(vp))
                {
                {
-                       usrerr("501 Bad argument \"%s\" to RET", vp);
+                       usrerr("501 Syntax error in ORCPT parameter value");
                        /* NOTREACHED */
                }
                        /* NOTREACHED */
                }
-       }
-       else if (strcasecmp(kp, "orcpt") == 0)
-       {
-               if (vp == NULL)
+               if (a->q_orcpt != NULL)
                {
                {
-                       usrerr("501 ORCPT requires a value");
+                       usrerr("501 Duplicate ORCPT parameter");
                        /* NOTREACHED */
                }
                a->q_orcpt = newstr(vp);
                        /* NOTREACHED */
                }
                a->q_orcpt = newstr(vp);
@@ -901,6 +1110,7 @@ rcpt_esmtp_args(a, kp, vp, e)
 **             Prints the appropriate 250 codes.
 */
 
 **             Prints the appropriate 250 codes.
 */
 
+void
 printvrfyaddr(a, last)
        register ADDRESS *a;
        bool last;
 printvrfyaddr(a, last)
        register ADDRESS *a;
        bool last;
@@ -940,29 +1150,38 @@ printvrfyaddr(a, last)
 **             outputs the help file to message output.
 */
 
 **             outputs the help file to message output.
 */
 
+void
 help(topic)
        char *topic;
 {
        register FILE *hf;
        int len;
 help(topic)
        char *topic;
 {
        register FILE *hf;
        int len;
-       char buf[MAXLINE];
        bool noinfo;
        bool noinfo;
+       char buf[MAXLINE];
+       extern char Version[];
+
 
        if (HelpFile == NULL || (hf = fopen(HelpFile, "r")) == NULL)
        {
                /* no help */
                errno = 0;
 
        if (HelpFile == NULL || (hf = fopen(HelpFile, "r")) == NULL)
        {
                /* no help */
                errno = 0;
-               message("502 HELP not implemented");
+               message("502 Sendmail %s -- HELP not implemented", Version);
                return;
        }
 
        if (topic == NULL || *topic == '\0')
                return;
        }
 
        if (topic == NULL || *topic == '\0')
+       {
                topic = "smtp";
                topic = "smtp";
+               message("214-This is Sendmail version %s", Version);
+               noinfo = FALSE;
+       }
        else
        else
+       {
                makelower(topic);
                makelower(topic);
+               noinfo = TRUE;
+       }
 
        len = strlen(topic);
 
        len = strlen(topic);
-       noinfo = TRUE;
 
        while (fgets(buf, sizeof buf, hf) != NULL)
        {
 
        while (fgets(buf, sizeof buf, hf) != NULL)
        {
@@ -1001,6 +1220,7 @@ help(topic)
 **             none.
 */
 
 **             none.
 */
 
+int
 runinchild(label, e)
        char *label;
        register ENVELOPE *e;
 runinchild(label, e)
        char *label;
        register ENVELOPE *e;
@@ -1012,7 +1232,7 @@ runinchild(label, e)
                childpid = dofork();
                if (childpid < 0)
                {
                childpid = dofork();
                if (childpid < 0)
                {
-                       syserr("%s: cannot fork", label);
+                       syserr("451 %s: cannot fork", label);
                        return (1);
                }
                if (childpid > 0)
                        return (1);
                }
                if (childpid > 0)
@@ -1020,12 +1240,12 @@ runinchild(label, e)
                        auto int st;
 
                        /* parent -- wait for child to complete */
                        auto int st;
 
                        /* parent -- wait for child to complete */
-                       setproctitle("server %s child wait", CurHostName);
+                       setproctitle("server %s child wait", CurSmtpClient);
                        st = waitfor(childpid);
                        if (st == -1)
                        st = waitfor(childpid);
                        if (st == -1)
-                               syserr("%s: lost child", label);
+                               syserr("451 %s: lost child", label);
                        else if (!WIFEXITED(st))
                        else if (!WIFEXITED(st))
-                               syserr("%s: died on signal %d",
+                               syserr("451 %s: died on signal %d",
                                        label, st & 0177);
 
                        /* if we exited on a QUIT command, complete the process */
                                        label, st & 0177);
 
                        /* if we exited on a QUIT command, complete the process */
@@ -1051,57 +1271,5 @@ runinchild(label, e)
 
        return (0);
 }
 
        return (0);
 }
-\f/*
-**  PADDRTREE -- print address tree
-**
-**     Used by VRFY and EXPD to dump the tree of addresses produced.
-**
-**     Parameters:
-**             a -- address of root.
-**
-**     Returns:
-**             none.
-**
-**     Side Effects:
-**             prints the tree in a nice order.
-*/
-
-paddrtree(a)
-       register ADDRESS *a;
-{
-       static ADDRESS *prev;
-       static int lev;
-
-       if (a == NULL)
-               return;
-       lev++;
-       if (!bitset(QDONTSEND, a->q_flags))
-       {
-               if (prev != NULL)
-               {
-                       if (prev->q_fullname != NULL)
-                               message("250-", "%s <%s>", prev->q_fullname, prev->q_paddr);
-                       else
-                               message("250-", "<%s>", prev->q_paddr);
-               }
-               prev = a;
-       }
-       paddrtree(a->q_child);
-       paddrtree(a->q_sibling);
-       if (--lev <= 0)
-       {
-               if (prev != NULL)
-               {
-                       /* last one */
-                       if (prev->q_fullname != NULL)
-                               message("250", "%s <%s>", prev->q_fullname, prev->q_paddr);
-                       else
-                               message("250", "<%s>", prev->q_paddr);
-                       prev = NULL;
-               }
-               else
-                       message("550", "User unknown");
-       }
-}
 
 # endif /* SMTP */
 
 # endif /* SMTP */