more cleanup for DSN drafts
[unix-history] / usr / src / usr.sbin / sendmail / src / srvrsmtp.c
index 5b0572e..5280aac 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * 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.
  *
  * %sccs.include.redist.c%
  */
  *
  * %sccs.include.redist.c%
  */
 
 #ifndef lint
 #ifdef SMTP
 
 #ifndef lint
 #ifdef SMTP
-static char sccsid[] = "@(#)srvrsmtp.c 6.35.1.1 (Berkeley) %G% (with SMTP)";
+static char sccsid[] = "@(#)srvrsmtp.c 8.62 (Berkeley) %G% (with SMTP)";
 #else
 #else
-static char sccsid[] = "@(#)srvrsmtp.c 6.35.1.1 (Berkeley) %G% (without SMTP)";
+static char sccsid[] = "@(#)srvrsmtp.c 8.62 (Berkeley) %G% (without SMTP)";
 #endif
 #endif /* not lint */
 
 # include <errno.h>
 #endif
 #endif /* not lint */
 
 # include <errno.h>
-# include <signal.h>
 
 # ifdef SMTP
 
 
 # ifdef SMTP
 
@@ -58,6 +57,8 @@ struct cmd
 /* non-standard commands */
 # define CMDONEX       16      /* onex -- sending one transaction only */
 # define CMDVERB       17      /* verb -- go into verbose mode */
 /* non-standard commands */
 # define CMDONEX       16      /* onex -- sending one transaction only */
 # define CMDVERB       17      /* verb -- go into verbose mode */
+/* use this to catch and log "door handle" attempts on your system */
+# define CMDLOGBOGUS   23      /* bogus command that should be logged */
 /* debugging-only commands, only enabled if SMTPDEBUG is defined */
 # define CMDDBGQSHOW   24      /* showq -- show send queue */
 # define CMDDBGDEBUG   25      /* debug -- set debug mode */
 /* debugging-only commands, only enabled if SMTPDEBUG is defined */
 # define CMDDBGQSHOW   24      /* showq -- show send queue */
 # define CMDDBGDEBUG   25      /* debug -- set debug mode */
@@ -85,13 +86,18 @@ static struct cmd   CmdTab[] =
         */
        "showq",        CMDDBGQSHOW,
        "debug",        CMDDBGDEBUG,
         */
        "showq",        CMDDBGQSHOW,
        "debug",        CMDDBGDEBUG,
+       "wiz",          CMDLOGBOGUS,
        NULL,           CMDERROR,
 };
 
        NULL,           CMDERROR,
 };
 
-bool   InChild = FALSE;                /* true if running in a subprocess */
 bool   OneXact = FALSE;                /* one xaction only this run */
 bool   OneXact = FALSE;                /* one xaction only this run */
+char   *CurSmtpClient;                 /* who's at the other end of channel */
 
 
-#define EX_QUIT                22              /* special code for QUIT command */
+static char    *skipword();
+extern char    RealUserName[];
+
+
+#define MAXBADCOMMANDS 25              /* maximum number of bad commands */
 
 smtp(e)
        register ENVELOPE *e;
 
 smtp(e)
        register ENVELOPE *e;
@@ -99,36 +105,68 @@ smtp(e)
        register char *p;
        register struct cmd *c;
        char *cmd;
        register char *p;
        register struct cmd *c;
        char *cmd;
-       static char *skipword();
        extern ADDRESS *sendto();
        ADDRESS *a;
 
        hasmail = FALSE;
        extern ADDRESS *sendto();
        ADDRESS *a;
 
        hasmail = FALSE;
-       if (OutChannel != stdout)
+       if (fileno(OutChannel) != fileno(stdout))
        {
                /* arrange for debugging output to go to remote host */
        {
                /* arrange for debugging output to go to remote host */
-               (void) close(1);
-               (void) dup(fileno(OutChannel));
+               (void) dup2(fileno(OutChannel), fileno(stdout));
        }
        settime(e);
        }
        settime(e);
-       CurHostName = RealHostName;
-       setproctitle("srvrsmtp %s", CurHostName);
-       expand("\201e", inp, &inp[sizeof inp], e);
-       message("220 %s", inp);
-       SmtpPhase = "startup";
-       sendinghost = NULL;
+       peerhostname = RealHostName;
+       if (peerhostname == NULL)
+               peerhostname = "localhost";
+       CurHostName = peerhostname;
+       CurSmtpClient = macvalue('_', e);
+       if (CurSmtpClient == NULL)
+               CurSmtpClient = CurHostName;
+
+       setproctitle("server %s startup", CurSmtpClient);
+       expand("\201e", inp, sizeof inp, e);
+       if (BrokenSmtpPeers)
+       {
+               p = strchr(inp, '\n');
+               if (p != NULL)
+                       *p = '\0';
+               message("220 %s", inp);
+       }
+       else
+       {
+               char *q = inp;
+
+               while (q != NULL)
+               {
+                       p = strchr(q, '\n');
+                       if (p != NULL)
+                               *p++ = '\0';
+                       message("220-%s", q);
+                       q = p;
+               }
+               message("220 ESMTP spoken here");
+       }
        protocol = NULL;
        protocol = NULL;
+       sendinghost = macvalue('s', e);
        gothello = FALSE;
        gotmail = FALSE;
        for (;;)
        {
                /* arrange for backout */
        gothello = FALSE;
        gotmail = FALSE;
        for (;;)
        {
                /* arrange for backout */
-               if (setjmp(TopFrame) > 0 && InChild)
-                       finis();
+               if (setjmp(TopFrame) > 0)
+               {
+                       /* if() nesting is necessary for Cray UNICOS */
+                       if (InChild)
+                       {
+                               QuickAbort = FALSE;
+                               SuprErrs = TRUE;
+                               finis();
+                       }
+               }
                QuickAbort = FALSE;
                HoldErrs = FALSE;
                LogUsrErrs = FALSE;
                QuickAbort = FALSE;
                HoldErrs = FALSE;
                LogUsrErrs = FALSE;
-               e->e_flags &= ~EF_VRFYONLY;
+               e->e_flags &= ~(EF_VRFYONLY|EF_GLOBALERRS);
 
                /* setup for the read */
                e->e_to = NULL;
 
                /* setup for the read */
                e->e_to = NULL;
@@ -136,18 +174,22 @@ smtp(e)
                (void) fflush(stdout);
 
                /* read the input line */
                (void) fflush(stdout);
 
                /* read the input line */
-               p = sfgets(inp, sizeof inp, InChannel, TimeOuts.to_nextcommand);
+               SmtpPhase = "server cmd read";
+               setproctitle("server %s cmd read", CurHostName);
+               p = sfgets(inp, sizeof inp, InChannel, TimeOuts.to_nextcommand,
+                               SmtpPhase);
 
                /* handle errors */
                if (p == NULL)
                {
                        /* end of file, just die */
 
                /* handle errors */
                if (p == NULL)
                {
                        /* end of file, just die */
+                       disconnect(1, e);
                        message("421 %s Lost input channel from %s",
                        message("421 %s Lost input channel from %s",
-                               MyHostName, CurHostName);
+                               MyHostName, CurSmtpClient);
 #ifdef LOG
 #ifdef LOG
-                       if (LogLevel > 1)
+                       if (LogLevel > (gotmail ? 1 : 19))
                                syslog(LOG_NOTICE, "lost input channel from %s",
                                syslog(LOG_NOTICE, "lost input channel from %s",
-                                       CurHostName);
+                                       CurSmtpClient);
 #endif
                        if (InChild)
                                ExitStat = EX_QUIT;
 #endif
                        if (InChild)
                                ExitStat = EX_QUIT;
@@ -161,6 +203,11 @@ smtp(e)
                if (e->e_xfp != NULL)
                        fprintf(e->e_xfp, "<<< %s\n", inp);
 
                if (e->e_xfp != NULL)
                        fprintf(e->e_xfp, "<<< %s\n", inp);
 
+               if (e->e_id == NULL)
+                       setproctitle("%s: %.80s", CurSmtpClient, inp);
+               else
+                       setproctitle("%s %s: %.80s", e->e_id, CurSmtpClient, inp);
+
                /* break off command */
                for (p = inp; isascii(*p) && isspace(*p); p++)
                        continue;
                /* break off command */
                for (p = inp; isascii(*p) && isspace(*p); p++)
                        continue;
@@ -193,65 +240,66 @@ smtp(e)
                        if (c->cmdcode == CMDEHLO)
                        {
                                protocol = "ESMTP";
                        if (c->cmdcode == CMDEHLO)
                        {
                                protocol = "ESMTP";
-                               SmtpPhase = "EHLO";
+                               SmtpPhase = "server EHLO";
                        }
                        else
                        {
                                protocol = "SMTP";
                        }
                        else
                        {
                                protocol = "SMTP";
-                               SmtpPhase = "HELO";
+                               SmtpPhase = "server HELO";
                        }
                        }
-                       setproctitle("%s: %s", CurHostName, inp);
-                       if (strcasecmp(p, MyHostName) == 0)
-                       {
-                               /*
-                               **  Didn't know about alias or MX,
-                               **  or connected to an echo server
-                               */
 
 
-                               message("553 %s config error: mail loops back to myself",
-                                       MyHostName);
+                       /* check for valid domain name (re 1123 5.2.5) */
+                       if (*p == '\0')
+                       {
+                               message("501 %s requires domain address",
+                                       cmdbuf);
                                break;
                        }
                                break;
                        }
-                       (void) strcpy(hostbuf, p);
-                       (void) strcat(hostbuf, " (");
-                       (void) strcat(hostbuf, anynet_ntoa(&RealHostAddr));
-                       if (strcasecmp(p, RealHostName) != 0)
+                       else
                        {
                        {
-                               auth_warning(e, "Host %s claimed to be %s",
-                                       RealHostName, p);
-                               (void) strcat(hostbuf, "; ");
-                               (void) strcat(hostbuf, RealHostName);
+                               register char *q;
+
+                               for (q = p; *q != '\0'; q++)
+                               {
+                                       if (!isascii(*q))
+                                               break;
+                                       if (isalnum(*q))
+                                               continue;
+                                       if (strchr("[].-_#", *q) == NULL)
+                                               break;
+                               }
+                               if (*q != '\0')
+                               {
+                                       message("501 Invalid domain name");
+                                       break;
+                               }
                        }
                        }
-                       (void) strcat(hostbuf, ")");
-                       sendinghost = newstr(hostbuf);
+
+                       sendinghost = newstr(p);
                        message("250", "%s Hello %s, pleased to meet you", HostName, p);
                        break;
 
                  case CMDMAIL:         /* mail -- designate sender */
                        message("250", "%s Hello %s, pleased to meet you", HostName, p);
                        break;
 
                  case CMDMAIL:         /* mail -- designate sender */
-                       SmtpPhase = "MAIL";
-
-                       /* force a sending host even if no HELO given */
-                       if (sendinghost == NULL && macvalue('s', e) == NULL)
-                               sendinghost = RealHostName;
+                       SmtpPhase = "server MAIL";
 
                        /* check for validity of this command */
                        if (!gothello)
                        {
 
                        /* check for validity of this command */
                        if (!gothello)
                        {
+                               /* set sending host to our known value */
+                               if (sendinghost == NULL)
+                                       sendinghost = peerhostname;
+
                                if (bitset(PRIV_NEEDMAILHELO, PrivacyFlags))
                                {
                                        message("503 Polite people say HELO first");
                                        break;
                                }
                                if (bitset(PRIV_NEEDMAILHELO, PrivacyFlags))
                                {
                                        message("503 Polite people say HELO first");
                                        break;
                                }
-                               else
-                               {
-                                       auth_warning(e,
-                                               "Host %s didn't use HELO protocol",
-                                               RealHostName);
-                               }
                        }
                        if (gotmail)
                        {
                                message("503 Sender already specified");
                        }
                        if (gotmail)
                        {
                                message("503 Sender already specified");
+                               if (InChild)
+                                       finis();
                                break;
                        }
                        if (InChild)
                                break;
                        }
                        if (InChild)
@@ -264,13 +312,30 @@ smtp(e)
                        /* fork a subprocess to process this command */
                        if (runinchild("SMTP-MAIL", e) > 0)
                                break;
                        /* fork a subprocess to process this command */
                        if (runinchild("SMTP-MAIL", e) > 0)
                                break;
-                       if (sendinghost != NULL)
-                               define('s', sendinghost, e);
+                       if (!gothello)
+                       {
+                               auth_warning(e,
+                                       "Host %s didn't use HELO protocol",
+                                       peerhostname);
+                       }
+#ifdef PICKY_HELO_CHECK
+                       if (strcasecmp(sendinghost, peerhostname) != 0 &&
+                           (strcasecmp(peerhostname, "localhost") != 0 ||
+                            strcasecmp(sendinghost, MyHostName) != 0))
+                       {
+                               auth_warning(e, "Host %s claimed to be %s",
+                                       peerhostname, sendinghost);
+                       }
+#endif
+
                        if (protocol == NULL)
                                protocol = "SMTP";
                        define('r', protocol, e);
                        if (protocol == NULL)
                                protocol = "SMTP";
                        define('r', protocol, e);
+                       define('s', sendinghost, e);
                        initsys(e);
                        initsys(e);
-                       setproctitle("%s %s: %s", e->e_id, CurHostName, inp);
+                       nrcpts = 0;
+                       e->e_flags |= EF_LOGSENDER|EF_CLRQUEUE;
+                       setproctitle("%s %s: %.80s", e->e_id, CurSmtpClient, inp);
 
                        /* child -- go do the processing */
                        p = skipword(p, "from");
 
                        /* child -- go do the processing */
                        p = skipword(p, "from");
@@ -280,7 +345,12 @@ smtp(e)
                        {
                                /* this failed -- undo work */
                                if (InChild)
                        {
                                /* this failed -- undo work */
                                if (InChild)
+                               {
+                                       QuickAbort = FALSE;
+                                       SuprErrs = TRUE;
+                                       e->e_flags &= ~EF_FATALERRS;
                                        finis();
                                        finis();
+                               }
                                break;
                        }
                        QuickAbort = TRUE;
                                break;
                        }
                        QuickAbort = TRUE;
@@ -292,12 +362,21 @@ smtp(e)
                        if (p != NULL && *p != '\0')
                                *p++ = '\0';
 
                        if (p != NULL && *p != '\0')
                                *p++ = '\0';
 
+                       /* check for possible spoofing */
+                       if (RealUid != 0 && OpMode == MD_SMTP &&
+                           !bitnset(M_LOCALMAILER, e->e_from.q_mailer->m_flags) &&
+                           strcmp(e->e_from.q_user, RealUserName) != 0)
+                       {
+                               auth_warning(e, "%s owned process doing -bs",
+                                       RealUserName);
+                       }
+
                        /* now parse ESMTP arguments */
                        /* now parse ESMTP arguments */
-                       msize = 0;
-                       for (; p != NULL && *p != '\0'; p++)
+                       e->e_msgsize = 0;
+                       while (p != NULL && *p != '\0')
                        {
                                char *kp;
                        {
                                char *kp;
-                               char *vp;
+                               char *vp = NULL;
 
                                /* locate the beginning of the keyword */
                                while (isascii(*p) && isspace(*p))
 
                                /* locate the beginning of the keyword */
                                while (isascii(*p) && isspace(*p))
@@ -325,26 +404,20 @@ smtp(e)
                                        *p++ = '\0';
 
                                if (tTd(19, 1))
                                        *p++ = '\0';
 
                                if (tTd(19, 1))
-                                       printf("MAIL: got arg %s=%s\n", kp,
+                                       printf("MAIL: got arg %s=\"%s\"\n", kp,
                                                vp == NULL ? "<null>" : vp);
 
                                                vp == NULL ? "<null>" : vp);
 
-                               if (strcasecmp(kp, "size") == 0)
-                               {
-                                       if (kp == NULL)
-                                       {
-                                               usrerr("501 SIZE requires a value");
-                                               /* NOTREACHED */
-                                       }
-                                       msize = atol(vp);
-                               }
-                               else
-                               {
-                                       usrerr("501 %s parameter unrecognized", kp);
-                                       /* NOTREACHED */
-                               }
+                               mail_esmtp_args(kp, vp, e);
+                       }
+
+                       if (MaxMessageSize > 0 && e->e_msgsize > MaxMessageSize)
+                       {
+                               usrerr("552 Message size exceeds fixed maximum message size (%ld)",
+                                       MaxMessageSize);
+                               /* NOTREACHED */
                        }
                                
                        }
                                
-                       if (!enoughspace(msize))
+                       if (!enoughspace(e->e_msgsize))
                        {
                                message("452 Insufficient disk space; try again later");
                                break;
                        {
                                message("452 Insufficient disk space; try again later");
                                break;
@@ -364,8 +437,7 @@ smtp(e)
                                usrerr("503 Need MAIL before RCPT");
                                break;
                        }
                                usrerr("503 Need MAIL before RCPT");
                                break;
                        }
-                       SmtpPhase = "RCPT";
-                       setproctitle("%s %s: %s", e->e_id, CurHostName, inp);
+                       SmtpPhase = "server RCPT";
                        if (setjmp(TopFrame) > 0)
                        {
                                e->e_flags &= ~EF_FATALERRS;
                        if (setjmp(TopFrame) > 0)
                        {
                                e->e_flags &= ~EF_FATALERRS;
@@ -387,7 +459,12 @@ smtp(e)
                        /* no errors during parsing, but might be a duplicate */
                        e->e_to = p;
                        if (!bitset(QBADADDR, a->q_flags))
                        /* no errors during parsing, but might be a duplicate */
                        e->e_to = p;
                        if (!bitset(QBADADDR, a->q_flags))
-                               message("250 Recipient ok");
+                       {
+                               message("250 Recipient ok%s",
+                                       bitset(QQUEUEUP, a->q_flags) ?
+                                               " (will queue)" : "");
+                               nrcpts++;
+                       }
                        else
                        {
                                /* punt -- should keep message in ADDRESS.... */
                        else
                        {
                                /* punt -- should keep message in ADDRESS.... */
@@ -397,24 +474,48 @@ smtp(e)
                        break;
 
                  case CMDDATA:         /* data -- text of mail */
                        break;
 
                  case CMDDATA:         /* data -- text of mail */
-                       SmtpPhase = "DATA";
+                       SmtpPhase = "server DATA";
                        if (!gotmail)
                        {
                                message("503 Need MAIL command");
                                break;
                        }
                        if (!gotmail)
                        {
                                message("503 Need MAIL command");
                                break;
                        }
-                       else if (e->e_nrcpts <= 0)
+                       else if (nrcpts <= 0)
                        {
                                message("503 Need RCPT (recipient)");
                                break;
                        }
 
                        {
                                message("503 Need RCPT (recipient)");
                                break;
                        }
 
+                       /* check to see if we need to re-expand aliases */
+                       /* also reset QBADADDR on already-diagnosted addrs */
+                       doublequeue = FALSE;
+                       for (a = e->e_sendqueue; a != NULL; a = a->q_next)
+                       {
+                               if (bitset(QVERIFIED, a->q_flags))
+                               {
+                                       /* need to re-expand aliases */
+                                       doublequeue = TRUE;
+                               }
+                               if (bitset(QBADADDR, a->q_flags))
+                               {
+                                       /* make this "go away" */
+                                       a->q_flags |= QDONTSEND;
+                                       a->q_flags &= ~QBADADDR;
+                               }
+                       }
+
                        /* collect the text of the message */
                        SmtpPhase = "collect";
                        /* collect the text of the message */
                        SmtpPhase = "collect";
-                       setproctitle("%s %s: %s", e->e_id, CurHostName, inp);
-                       collect(TRUE, e);
+                       collect(InChannel, TRUE, doublequeue, NULL, e);
                        if (Errors != 0)
                        if (Errors != 0)
-                               break;
+                               goto abortmessage;
+
+                       /* make sure we actually do delivery */
+                       e->e_flags &= ~EF_CLRQUEUE;
+
+                       /* from now on, we have to operate silently */
+                       HoldErrs = TRUE;
+                       e->e_errormode = EM_MAIL;
 
                        /*
                        **  Arrange to send to everyone.
 
                        /*
                        **  Arrange to send to everyone.
@@ -435,34 +536,40 @@ smtp(e)
                        */
 
                        SmtpPhase = "delivery";
                        */
 
                        SmtpPhase = "delivery";
-                       if (e->e_nrcpts != 1)
-                       {
-                               HoldErrs = TRUE;
-                               e->e_errormode = EM_MAIL;
-                       }
-                       e->e_flags &= ~EF_FATALERRS;
                        e->e_xfp = freopen(queuename(e, 'x'), "w", e->e_xfp);
                        id = e->e_id;
 
                        e->e_xfp = freopen(queuename(e, 'x'), "w", e->e_xfp);
                        id = e->e_id;
 
-                       /* send to all recipients */
-                       sendall(e, Verbose ? SM_DELIVER : SM_QUEUE);
+                       if (doublequeue)
+                       {
+                               /* make sure it is in the queue */
+                               queueup(e, TRUE, FALSE);
+                               if (e->e_sendmode == SM_QUEUE)
+                                       e->e_flags |= EF_KEEPQUEUE;
+                       }
+                       else
+                       {
+                               /* send to all recipients */
+                               sendall(e, SM_DEFAULT);
+                       }
                        e->e_to = NULL;
 
                        e->e_to = NULL;
 
-                       /* save statistics */
-                       markstats(e, (ADDRESS *) NULL);
+                       /* issue success message */
+                       message("250 %s Message accepted for delivery", id);
 
 
-                       unlockqueue(e);
+                       /* if we just queued, poke it */
+                       if (doublequeue && e->e_sendmode != SM_QUEUE)
+                       {
+                               extern pid_t dowork();
 
 
-                       /* issue success if appropriate and reset */
-                       if (Errors == 0 || HoldErrs)
-                               message("250 %s Message accepted for delivery", id);
-                       else
-                               e->e_flags &= ~EF_FATALERRS;
+                               unlockqueue(e);
+                               (void) dowork(id, TRUE, TRUE, e);
+                       }
 
                        /* now make it really happen */
                        if (!Verbose && e->e_sendmode != SM_QUEUE)
                                dowork(id, 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)
                                finis();
                        /* if in a child, pop back to our parent */
                        if (InChild)
                                finis();
@@ -476,6 +583,10 @@ 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();
 
                        if (InChild)
                                finis();
 
@@ -492,9 +603,14 @@ smtp(e)
                                                PrivacyFlags))
                        {
                                if (vrfy)
                                                PrivacyFlags))
                        {
                                if (vrfy)
-                                       message("252 Who's to say?");
+                                       message("252 Cannot VRFY user; try RCPT to attempt delivery (or try finger)");
                                else
                                else
-                                       message("502 That's none of your business");
+                                       message("502 Sorry, we do not allow this operation");
+#ifdef LOG
+                               if (LogLevel > 5)
+                                       syslog(LOG_INFO, "%s: %s [rejected]",
+                                               CurSmtpClient, inp);
+#endif
                                break;
                        }
                        else if (!gothello &&
                                break;
                        }
                        else if (!gothello &&
@@ -506,10 +622,9 @@ smtp(e)
                        }
                        if (runinchild(vrfy ? "SMTP-VRFY" : "SMTP-EXPN", e) > 0)
                                break;
                        }
                        if (runinchild(vrfy ? "SMTP-VRFY" : "SMTP-EXPN", e) > 0)
                                break;
-                       setproctitle("%s: %s", CurHostName, inp);
 #ifdef LOG
                        if (LogLevel > 5)
 #ifdef LOG
                        if (LogLevel > 5)
-                               syslog(LOG_INFO, "%s: %s", CurHostName, inp);
+                               syslog(LOG_INFO, "%s: %s", CurSmtpClient, inp);
 #endif
                                paddrtree(a);
                        break;
 #endif
                                paddrtree(a);
                        break;
@@ -519,24 +634,38 @@ smtp(e)
                        break;
 
                  case CMDNOOP:         /* noop -- do nothing */
                        break;
 
                  case CMDNOOP:         /* noop -- do nothing */
-                       message("200 OK");
+                       message("250 OK");
                        break;
 
                  case CMDQUIT:         /* quit -- leave mail */
                        message("221 %s closing connection", MyHostName);
                        break;
 
                  case CMDQUIT:         /* quit -- leave mail */
                        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);
+
                        if (InChild)
                                ExitStat = EX_QUIT;
                        finis();
 
                  case CMDVERB:         /* set verbose mode */
                        if (InChild)
                                ExitStat = EX_QUIT;
                        finis();
 
                  case CMDVERB:         /* set verbose mode */
+                       if (bitset(PRIV_NOEXPN, PrivacyFlags))
+                       {
+                               /* this would give out the same info */
+                               message("502 Verbose unavailable");
+                               break;
+                       }
                        Verbose = TRUE;
                        e->e_sendmode = SM_DELIVER;
                        Verbose = TRUE;
                        e->e_sendmode = SM_DELIVER;
-                       message("200 Verbose mode");
+                       message("250 Verbose mode");
                        break;
 
                  case CMDONEX:         /* doing one transaction only */
                        OneXact = TRUE;
                        break;
 
                  case CMDONEX:         /* doing one transaction only */
                        OneXact = TRUE;
-                       message("200 Only one transaction");
+                       message("250 Only one transaction");
                        break;
 
 # ifdef SMTPDEBUG
                        break;
 
 # ifdef SMTPDEBUG
@@ -552,20 +681,27 @@ smtp(e)
                        break;
 
 # else /* not SMTPDEBUG */
                        break;
 
 # else /* not SMTPDEBUG */
-
                  case CMDDBGQSHOW:     /* show queues */
                  case CMDDBGDEBUG:     /* set debug mode */
                  case CMDDBGQSHOW:     /* show queues */
                  case CMDDBGDEBUG:     /* set debug mode */
+# endif /* SMTPDEBUG */
+                 case CMDLOGBOGUS:     /* bogus command */
 # ifdef LOG
                        if (LogLevel > 0)
 # ifdef LOG
                        if (LogLevel > 0)
-                               syslog(LOG_NOTICE,
+                               syslog(LOG_CRIT,
                                    "\"%s\" command from %s (%s)",
                                    "\"%s\" command from %s (%s)",
-                                   c->cmdname, RealHostName,
+                                   c->cmdname, peerhostname,
                                    anynet_ntoa(&RealHostAddr));
 # endif
                        /* FALL THROUGH */
                                    anynet_ntoa(&RealHostAddr));
 # endif
                        /* FALL THROUGH */
-# endif /* SMTPDEBUG */
 
                  case CMDERROR:        /* unknown command */
 
                  case CMDERROR:        /* unknown command */
+                       if (++badcommands > MAXBADCOMMANDS)
+                       {
+                               message("421 %s Too many bad commands; closing connection",
+                                       MyHostName);
+                               goto doquit;
+                       }
+
                        message("500 Command unrecognized");
                        break;
 
                        message("500 Command unrecognized");
                        break;
 
@@ -597,6 +733,7 @@ skipword(p, w)
        char *w;
 {
        register char *q;
        char *w;
 {
        register char *q;
+       char *firstp = p;
 
        /* find beginning of word */
        while (isascii(*p) && isspace(*p))
 
        /* find beginning of word */
        while (isascii(*p) && isspace(*p))
@@ -611,7 +748,8 @@ skipword(p, w)
        if (*p != ':')
        {
          syntax:
        if (*p != ':')
        {
          syntax:
-               message("501 Syntax error");
+               message("501 Syntax error in parameters scanning \"%s\"",
+                       firstp);
                Errors++;
                return (NULL);
        }
                Errors++;
                return (NULL);
        }
@@ -619,6 +757,9 @@ skipword(p, w)
        while (isascii(*p) && isspace(*p))
                p++;
 
        while (isascii(*p) && isspace(*p))
                p++;
 
+       if (*p == '\0')
+               goto syntax;
+
        /* see if the input word matches desired word */
        if (strcasecmp(q, w))
                goto syntax;
        /* see if the input word matches desired word */
        if (strcasecmp(q, w))
                goto syntax;
@@ -626,6 +767,181 @@ 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.
+*/
+
+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 */
+               }
+# ifdef __STDC__
+               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 */
+               }
+               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:
+**             a -- the address corresponding to the To: parameter.
+**             kp -- the parameter key.
+**             vp -- the value of that parameter.
+**             e -- the envelope.
+**
+**     Returns:
+**             none.
+*/
+
+rcpt_esmtp_args(a, kp, vp, e)
+       ADDRESS *a;
+       char *kp;
+       char *vp;
+       ENVELOPE *e;
+{
+       if (strcasecmp(kp, "notify") == 0)
+       {
+               char *p;
+
+               if (vp == NULL)
+               {
+                       usrerr("501 NOTIFY requires a value");
+                       /* NOTREACHED */
+               }
+               a->q_flags &= ~(QPINGONSUCCESS|QPINGONFAILURE|QPINGONDELAY);
+               a->q_flags |= QHASNOTIFY;
+               if (strcasecmp(vp, "never") == 0)
+                       return;
+               for (p = vp; p != NULL; vp = p)
+               {
+                       p = strchr(p, ',');
+                       if (p != NULL)
+                               *p++ = '\0';
+                       if (strcasecmp(vp, "success") == 0)
+                               a->q_flags |= QPINGONSUCCESS;
+                       else if (strcasecmp(vp, "failure") == 0)
+                               a->q_flags |= QPINGONFAILURE;
+                       else if (strcasecmp(vp, "delay") == 0)
+                               a->q_flags |= QPINGONDELAY;
+                       else
+                       {
+                               usrerr("501 Bad argument \"%s\"  to NOTIFY",
+                                       vp);
+                               /* NOTREACHED */
+                       }
+               }
+       }
+       else if (strcasecmp(kp, "orcpt") == 0)
+       {
+               if (vp == NULL)
+               {
+                       usrerr("501 ORCPT requires a value");
+                       /* NOTREACHED */
+               }
+               if (!xtextok(vp))
+               {
+                       usrerr("501 Syntax error in ORCPT parameter value");
+                       /* NOTREACHED */
+               }
+               if (a->q_orcpt != NULL)
+               {
+                       usrerr("501 Duplicate ORCPT parameter");
+                       /* NOTREACHED */
+               }
+               a->q_orcpt = newstr(vp);
+       }
+       else
+       {
+               usrerr("501 %s parameter unrecognized", kp);
+               /* NOTREACHED */
+       }
+}
+\f/*
 **  PRINTVRFYADDR -- print an entry in the verify queue
 **
 **     Parameters:
 **  PRINTVRFYADDR -- print an entry in the verify queue
 **
 **     Parameters:
@@ -648,17 +964,22 @@ printvrfyaddr(a, last)
        strcpy(fmtbuf, "250");
        fmtbuf[3] = last ? ' ' : '-';
 
        strcpy(fmtbuf, "250");
        fmtbuf[3] = last ? ' ' : '-';
 
-       if (strchr(a->q_paddr, '<') != NULL)
-               strcpy(&fmtbuf[4], "%s");
-       else if (a->q_fullname == NULL)
-               strcpy(&fmtbuf[4], "<%s>");
+       if (a->q_fullname == NULL)
+       {
+               if (strchr(a->q_user, '@') == NULL)
+                       strcpy(&fmtbuf[4], "<%s@%s>");
+               else
+                       strcpy(&fmtbuf[4], "<%s>");
+               message(fmtbuf, a->q_user, MyHostName);
+       }
        else
        {
        else
        {
-               strcpy(&fmtbuf[4], "%s <%s>");
-               message(fmtbuf, a->q_fullname, a->q_paddr);
-               return;
+               if (strchr(a->q_user, '@') == NULL)
+                       strcpy(&fmtbuf[4], "%s <%s@%s>");
+               else
+                       strcpy(&fmtbuf[4], "%s <%s>");
+               message(fmtbuf, a->q_fullname, a->q_user, MyHostName);
        }
        }
-       message(fmtbuf, a->q_paddr);
 }
 \f/*
 **  HELP -- implement the HELP command.
 }
 \f/*
 **  HELP -- implement the HELP command.
@@ -753,13 +1074,20 @@ 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);
                        st = waitfor(childpid);
                        if (st == -1)
                                syserr("%s: lost child", label);
                        st = waitfor(childpid);
                        if (st == -1)
                                syserr("%s: lost child", label);
+                       else if (!WIFEXITED(st))
+                               syserr("%s: died on signal %d",
+                                       label, st & 0177);
 
                        /* if we exited on a QUIT command, complete the process */
 
                        /* if we exited on a QUIT command, complete the process */
-                       if (st == (EX_QUIT << 8))
+                       if (WEXITSTATUS(st) == EX_QUIT)
+                       {
+                               disconnect(1, e);
                                finis();
                                finis();
+                       }
 
                        return (1);
                }
 
                        return (1);
                }
@@ -773,7 +1101,7 @@ runinchild(label, e)
        }
 
        /* open alias database */
        }
 
        /* open alias database */
-       initaliases(AliasFile, FALSE, e);
+       initmaps(FALSE, e);
 
        return (0);
 }
 
        return (0);
 }