122712Sdist /*
234921Sbostic  * Copyright (c) 1983 Eric P. Allman
362532Sbostic  * Copyright (c) 1988, 1993
462532Sbostic  *	The Regents of the University of California.  All rights reserved.
533731Sbostic  *
642829Sbostic  * %sccs.include.redist.c%
733731Sbostic  */
822712Sdist 
933731Sbostic # include "sendmail.h"
1022712Sdist 
1133731Sbostic #ifndef lint
1233731Sbostic #ifdef SMTP
13*65751Seric static char sccsid[] = "@(#)srvrsmtp.c	8.24 (Berkeley) 01/15/94 (with SMTP)";
1433731Sbostic #else
15*65751Seric static char sccsid[] = "@(#)srvrsmtp.c	8.24 (Berkeley) 01/15/94 (without SMTP)";
1633731Sbostic #endif
1733731Sbostic #endif /* not lint */
1833731Sbostic 
199339Seric # include <errno.h>
204549Seric 
2133731Sbostic # ifdef SMTP
224556Seric 
234549Seric /*
244549Seric **  SMTP -- run the SMTP protocol.
254549Seric **
264549Seric **	Parameters:
274549Seric **		none.
284549Seric **
294549Seric **	Returns:
304549Seric **		never.
314549Seric **
324549Seric **	Side Effects:
334549Seric **		Reads commands from the input channel and processes
344549Seric **			them.
354549Seric */
364549Seric 
374549Seric struct cmd
384549Seric {
394549Seric 	char	*cmdname;	/* command name */
404549Seric 	int	cmdcode;	/* internal code, see below */
414549Seric };
424549Seric 
434549Seric /* values for cmdcode */
444549Seric # define CMDERROR	0	/* bad command */
454549Seric # define CMDMAIL	1	/* mail -- designate sender */
464976Seric # define CMDRCPT	2	/* rcpt -- designate recipient */
474549Seric # define CMDDATA	3	/* data -- send message text */
489339Seric # define CMDRSET	4	/* rset -- reset state */
499339Seric # define CMDVRFY	5	/* vrfy -- verify address */
5058092Seric # define CMDEXPN	6	/* expn -- expand address */
519339Seric # define CMDNOOP	7	/* noop -- do nothing */
529339Seric # define CMDQUIT	8	/* quit -- close connection and die */
539339Seric # define CMDHELO	9	/* helo -- be polite */
5458092Seric # define CMDHELP	10	/* help -- give usage info */
5558323Seric # define CMDEHLO	11	/* ehlo -- extended helo (RFC 1425) */
5658092Seric /* non-standard commands */
5758092Seric # define CMDONEX	16	/* onex -- sending one transaction only */
5858092Seric # define CMDVERB	17	/* verb -- go into verbose mode */
5964685Seric /* use this to catch and log "door handle" attempts on your system */
6064685Seric # define CMDLOGBOGUS	23	/* bogus command that should be logged */
6136230Skarels /* debugging-only commands, only enabled if SMTPDEBUG is defined */
6258092Seric # define CMDDBGQSHOW	24	/* showq -- show send queue */
6358092Seric # define CMDDBGDEBUG	25	/* debug -- set debug mode */
644549Seric 
654549Seric static struct cmd	CmdTab[] =
664549Seric {
674549Seric 	"mail",		CMDMAIL,
684976Seric 	"rcpt",		CMDRCPT,
694549Seric 	"data",		CMDDATA,
704549Seric 	"rset",		CMDRSET,
714549Seric 	"vrfy",		CMDVRFY,
7258092Seric 	"expn",		CMDEXPN,
734549Seric 	"help",		CMDHELP,
744549Seric 	"noop",		CMDNOOP,
754549Seric 	"quit",		CMDQUIT,
764976Seric 	"helo",		CMDHELO,
7758323Seric 	"ehlo",		CMDEHLO,
788544Seric 	"verb",		CMDVERB,
799314Seric 	"onex",		CMDONEX,
8036230Skarels 	/*
8136230Skarels 	 * remaining commands are here only
8236230Skarels 	 * to trap and log attempts to use them
8336230Skarels 	 */
849339Seric 	"showq",	CMDDBGQSHOW,
858544Seric 	"debug",	CMDDBGDEBUG,
8664685Seric 	"wiz",		CMDLOGBOGUS,
874549Seric 	NULL,		CMDERROR,
884549Seric };
894549Seric 
909378Seric bool	OneXact = FALSE;		/* one xaction only this run */
9165017Seric char	*CurSmtpClient;			/* who's at the other end of channel */
9211146Seric 
9363937Seric static char	*skipword();
9463937Seric 
9555012Seric smtp(e)
9655012Seric 	register ENVELOPE *e;
974549Seric {
984549Seric 	register char *p;
998544Seric 	register struct cmd *c;
1004549Seric 	char *cmd;
1015003Seric 	auto ADDRESS *vrfyqueue;
10212612Seric 	ADDRESS *a;
10358109Seric 	bool gotmail;			/* mail command received */
10458092Seric 	bool gothello;			/* helo command received */
10558092Seric 	bool vrfy;			/* set if this is a vrfy command */
10658323Seric 	char *protocol;			/* sending protocol */
10759016Seric 	char *sendinghost;		/* sending hostname */
10858333Seric 	long msize;			/* approximate maximum message size */
10958333Seric 	auto char *delimptr;
11058714Seric 	char *id;
11159747Seric 	int nrcpts;			/* number of RCPT commands */
11263787Seric 	bool doublequeue;
1138544Seric 	char inp[MAXLINE];
11457232Seric 	char cmdbuf[MAXLINE];
1157124Seric 	extern char Version[];
11624943Seric 	extern ENVELOPE BlankEnvelope;
1174549Seric 
11859066Seric 	if (fileno(OutChannel) != fileno(stdout))
1197363Seric 	{
1207363Seric 		/* arrange for debugging output to go to remote host */
12159066Seric 		(void) dup2(fileno(OutChannel), fileno(stdout));
1227363Seric 	}
12355012Seric 	settime(e);
12457642Seric 	CurHostName = RealHostName;
12565017Seric 	CurSmtpClient = macvalue('_', e);
12665017Seric 	if (CurSmtpClient == NULL)
12765017Seric 		CurSmtpClient = RealHostName;
12865017Seric 
12965017Seric 	setproctitle("server %s startup", CurSmtpClient);
13058050Seric 	expand("\201e", inp, &inp[sizeof inp], e);
13164496Seric 	if (BrokenSmtpPeers)
13264496Seric 	{
13364496Seric 		message("220 %s", inp);
13464496Seric 	}
13564496Seric 	else
13664496Seric 	{
13764496Seric 		message("220-%s", inp);
13864496Seric 		message("220 ESMTP spoken here");
13964496Seric 	}
14058330Seric 	protocol = NULL;
14159016Seric 	sendinghost = macvalue('s', e);
14258082Seric 	gothello = FALSE;
14358330Seric 	gotmail = FALSE;
1444549Seric 	for (;;)
1454549Seric 	{
14612612Seric 		/* arrange for backout */
147*65751Seric 		if (setjmp(TopFrame) > 0)
14859058Seric 		{
149*65751Seric 			/* if() nesting is necessary for Cray UNICOS */
150*65751Seric 			if (InChild)
151*65751Seric 			{
152*65751Seric 				QuickAbort = FALSE;
153*65751Seric 				SuprErrs = TRUE;
154*65751Seric 				finis();
155*65751Seric 			}
15659058Seric 		}
15712612Seric 		QuickAbort = FALSE;
15812612Seric 		HoldErrs = FALSE;
15951951Seric 		LogUsrErrs = FALSE;
16063843Seric 		e->e_flags &= ~(EF_VRFYONLY|EF_GLOBALERRS);
16112612Seric 
1627356Seric 		/* setup for the read */
16355012Seric 		e->e_to = NULL;
1644577Seric 		Errors = 0;
1657275Seric 		(void) fflush(stdout);
1667356Seric 
1677356Seric 		/* read the input line */
16861093Seric 		SmtpPhase = "server cmd read";
16961093Seric 		setproctitle("server %s cmd read", CurHostName);
17061093Seric 		p = sfgets(inp, sizeof inp, InChannel, TimeOuts.to_nextcommand,
17161093Seric 				SmtpPhase);
1727356Seric 
1737685Seric 		/* handle errors */
1747356Seric 		if (p == NULL)
1757356Seric 		{
1764549Seric 			/* end of file, just die */
17758151Seric 			message("421 %s Lost input channel from %s",
17865017Seric 				MyHostName, CurSmtpClient);
17955464Seric #ifdef LOG
18063843Seric 			if (LogLevel > (gotmail ? 1 : 19))
18155464Seric 				syslog(LOG_NOTICE, "lost input channel from %s",
18265017Seric 					CurSmtpClient);
18355464Seric #endif
18458069Seric 			if (InChild)
18558069Seric 				ExitStat = EX_QUIT;
1864549Seric 			finis();
1874549Seric 		}
1884549Seric 
1894549Seric 		/* clean up end of line */
1904558Seric 		fixcrlf(inp, TRUE);
1914549Seric 
1924713Seric 		/* echo command to transcript */
19355012Seric 		if (e->e_xfp != NULL)
19455012Seric 			fprintf(e->e_xfp, "<<< %s\n", inp);
1954713Seric 
19659060Seric 		if (e->e_id == NULL)
19765058Seric 			setproctitle("%s: %.80s", CurSmtpClient, inp);
19859060Seric 		else
19965058Seric 			setproctitle("%s %s: %.80s", e->e_id, CurSmtpClient, inp);
20059060Seric 
2014549Seric 		/* break off command */
20258050Seric 		for (p = inp; isascii(*p) && isspace(*p); p++)
2034549Seric 			continue;
20457232Seric 		cmd = cmdbuf;
20558050Seric 		while (*p != '\0' &&
20658050Seric 		       !(isascii(*p) && isspace(*p)) &&
20758050Seric 		       cmd < &cmdbuf[sizeof cmdbuf - 2])
20824981Seric 			*cmd++ = *p++;
20924981Seric 		*cmd = '\0';
2104549Seric 
21125691Seric 		/* throw away leading whitespace */
21258050Seric 		while (isascii(*p) && isspace(*p))
21325691Seric 			p++;
21425691Seric 
2154549Seric 		/* decode command */
2164549Seric 		for (c = CmdTab; c->cmdname != NULL; c++)
2174549Seric 		{
21833725Sbostic 			if (!strcasecmp(c->cmdname, cmdbuf))
2194549Seric 				break;
2204549Seric 		}
2214549Seric 
22251954Seric 		/* reset errors */
22351954Seric 		errno = 0;
22451954Seric 
2254549Seric 		/* process command */
2264549Seric 		switch (c->cmdcode)
2274549Seric 		{
2284976Seric 		  case CMDHELO:		/* hello -- introduce yourself */
22958323Seric 		  case CMDEHLO:		/* extended hello */
23058323Seric 			if (c->cmdcode == CMDEHLO)
23158323Seric 			{
23258323Seric 				protocol = "ESMTP";
23361093Seric 				SmtpPhase = "server EHLO";
23458323Seric 			}
23558323Seric 			else
23658323Seric 			{
23758323Seric 				protocol = "SMTP";
23861093Seric 				SmtpPhase = "server HELO";
23958323Seric 			}
24059016Seric 			sendinghost = newstr(p);
24164284Seric 			if (strcasecmp(p, RealHostName) != 0 &&
24264284Seric 			    (strcasecmp(RealHostName, "localhost") != 0 ||
24364284Seric 			     strcasecmp(p, MyHostName) != 0))
24411146Seric 			{
24558789Seric 				auth_warning(e, "Host %s claimed to be %s",
24658789Seric 					RealHostName, p);
24711146Seric 			}
24858323Seric 
24960210Seric 			gothello = TRUE;
25060210Seric 			if (c->cmdcode != CMDEHLO)
25160239Seric 			{
25260239Seric 				/* print old message and be done with it */
25360239Seric 				message("250 %s Hello %s, pleased to meet you",
25465017Seric 					MyHostName, CurSmtpClient);
25560210Seric 				break;
25660239Seric 			}
25760239Seric 
25860239Seric 			/* print extended message and brag */
25960239Seric 			message("250-%s Hello %s, pleased to meet you",
26060239Seric 				MyHostName, p);
26158323Seric 			if (!bitset(PRIV_NOEXPN, PrivacyFlags))
26258323Seric 				message("250-EXPN");
26364359Seric 			if (MaxMessageSize > 0)
26464359Seric 				message("250-SIZE %ld", MaxMessageSize);
26559271Seric 			else
26659271Seric 				message("250-SIZE");
26758323Seric 			message("250 HELP");
2684976Seric 			break;
2694976Seric 
2704549Seric 		  case CMDMAIL:		/* mail -- designate sender */
27161093Seric 			SmtpPhase = "server MAIL";
27224943Seric 
2739314Seric 			/* check for validity of this command */
27458789Seric 			if (!gothello)
27558082Seric 			{
27658957Seric 				/* set sending host to our known value */
27759016Seric 				if (sendinghost == NULL)
27859016Seric 					sendinghost = RealHostName;
27958957Seric 
28058789Seric 				if (bitset(PRIV_NEEDMAILHELO, PrivacyFlags))
28158821Seric 				{
28258789Seric 					message("503 Polite people say HELO first");
28358821Seric 					break;
28458821Seric 				}
28558082Seric 			}
28658109Seric 			if (gotmail)
2874558Seric 			{
28858151Seric 				message("503 Sender already specified");
28963843Seric 				if (InChild)
29063843Seric 					finis();
2914558Seric 				break;
2924558Seric 			}
2939339Seric 			if (InChild)
2949339Seric 			{
29536230Skarels 				errno = 0;
29658151Seric 				syserr("503 Nested MAIL command: MAIL %s", p);
29758069Seric 				finis();
2989339Seric 			}
2999339Seric 
3009339Seric 			/* fork a subprocess to process this command */
30155012Seric 			if (runinchild("SMTP-MAIL", e) > 0)
3029339Seric 				break;
30363753Seric 			if (!gothello)
30463753Seric 			{
30563753Seric 				auth_warning(e,
30663753Seric 					"Host %s didn't use HELO protocol",
30763753Seric 					RealHostName);
30863753Seric 			}
30958323Seric 			if (protocol == NULL)
31058323Seric 				protocol = "SMTP";
31158323Seric 			define('r', protocol, e);
31259016Seric 			define('s', sendinghost, e);
31355012Seric 			initsys(e);
31459747Seric 			nrcpts = 0;
31565089Seric 			e->e_flags |= EF_LOGSENDER;
31665058Seric 			setproctitle("%s %s: %.80s", e->e_id, CurSmtpClient, inp);
3179339Seric 
3189339Seric 			/* child -- go do the processing */
3194549Seric 			p = skipword(p, "from");
3204549Seric 			if (p == NULL)
3214549Seric 				break;
32257977Seric 			if (setjmp(TopFrame) > 0)
32358147Seric 			{
32458147Seric 				/* this failed -- undo work */
32558147Seric 				if (InChild)
32659058Seric 				{
32759058Seric 					QuickAbort = FALSE;
32859058Seric 					SuprErrs = TRUE;
32963787Seric 					e->e_flags &= ~EF_FATALERRS;
33058147Seric 					finis();
33159058Seric 				}
33257977Seric 				break;
33358147Seric 			}
33457977Seric 			QuickAbort = TRUE;
33558333Seric 
33658333Seric 			/* must parse sender first */
33758333Seric 			delimptr = NULL;
33858704Seric 			setsender(p, e, &delimptr, FALSE);
33958333Seric 			p = delimptr;
34058333Seric 			if (p != NULL && *p != '\0')
34158333Seric 				*p++ = '\0';
34258333Seric 
34358333Seric 			/* now parse ESMTP arguments */
34458333Seric 			msize = 0;
34558333Seric 			for (; p != NULL && *p != '\0'; p++)
34658333Seric 			{
34758333Seric 				char *kp;
34858333Seric 				char *vp;
34958333Seric 
35058333Seric 				/* locate the beginning of the keyword */
35158333Seric 				while (isascii(*p) && isspace(*p))
35258333Seric 					p++;
35358333Seric 				if (*p == '\0')
35458333Seric 					break;
35558333Seric 				kp = p;
35658333Seric 
35758333Seric 				/* skip to the value portion */
35858333Seric 				while (isascii(*p) && isalnum(*p) || *p == '-')
35958333Seric 					p++;
36058333Seric 				if (*p == '=')
36158333Seric 				{
36258333Seric 					*p++ = '\0';
36358333Seric 					vp = p;
36458333Seric 
36558333Seric 					/* skip to the end of the value */
36658333Seric 					while (*p != '\0' && *p != ' ' &&
36758333Seric 					       !(isascii(*p) && iscntrl(*p)) &&
36858333Seric 					       *p != '=')
36958333Seric 						p++;
37058333Seric 				}
37158333Seric 
37258333Seric 				if (*p != '\0')
37358333Seric 					*p++ = '\0';
37458333Seric 
37558333Seric 				if (tTd(19, 1))
37658333Seric 					printf("MAIL: got arg %s=%s\n", kp,
37758333Seric 						vp == NULL ? "<null>" : vp);
37858333Seric 
37958333Seric 				if (strcasecmp(kp, "size") == 0)
38058333Seric 				{
38159093Seric 					if (vp == NULL)
38258333Seric 					{
38358333Seric 						usrerr("501 SIZE requires a value");
38458333Seric 						/* NOTREACHED */
38558333Seric 					}
38658333Seric 					msize = atol(vp);
38758333Seric 				}
38859093Seric 				else if (strcasecmp(kp, "body") == 0)
38959093Seric 				{
39059093Seric 					if (vp == NULL)
39159093Seric 					{
39259093Seric 						usrerr("501 BODY requires a value");
39359093Seric 						/* NOTREACHED */
39459093Seric 					}
39559093Seric # ifdef MIME
39659093Seric 					if (strcasecmp(vp, "8bitmime") == 0)
39759093Seric 					{
39859093Seric 						e->e_bodytype = "8BITMIME";
39959709Seric 						SevenBit = FALSE;
40059093Seric 					}
40159093Seric 					else if (strcasecmp(vp, "7bit") == 0)
40259093Seric 					{
40359093Seric 						e->e_bodytype = "7BIT";
40459709Seric 						SevenBit = TRUE;
40559093Seric 					}
40659093Seric 					else
40759093Seric 					{
40859093Seric 						usrerr("501 Unknown BODY type %s",
40959093Seric 							vp);
41059093Seric 					}
41159093Seric # endif
41259093Seric 				}
41358333Seric 				else
41458333Seric 				{
41558333Seric 					usrerr("501 %s parameter unrecognized", kp);
41658333Seric 					/* NOTREACHED */
41758333Seric 				}
41858333Seric 			}
41959284Seric 
42059284Seric 			if (MaxMessageSize > 0 && msize > MaxMessageSize)
42159284Seric 			{
42259284Seric 				usrerr("552 Message size exceeds fixed maximum message size (%ld)",
42359284Seric 					MaxMessageSize);
42459284Seric 				/* NOTREACHED */
42559284Seric 			}
42658333Seric 
42758333Seric 			if (!enoughspace(msize))
42858333Seric 			{
42958333Seric 				message("452 Insufficient disk space; try again later");
43058333Seric 				break;
43158333Seric 			}
43258151Seric 			message("250 Sender ok");
43358147Seric 			gotmail = TRUE;
4344549Seric 			break;
4354549Seric 
4364976Seric 		  case CMDRCPT:		/* rcpt -- designate recipient */
43758850Seric 			if (!gotmail)
43858850Seric 			{
43958850Seric 				usrerr("503 Need MAIL before RCPT");
44058850Seric 				break;
44158850Seric 			}
44261093Seric 			SmtpPhase = "server RCPT";
44312612Seric 			if (setjmp(TopFrame) > 0)
44414785Seric 			{
44555012Seric 				e->e_flags &= ~EF_FATALERRS;
44612612Seric 				break;
44714785Seric 			}
44812612Seric 			QuickAbort = TRUE;
44951951Seric 			LogUsrErrs = TRUE;
45058093Seric 
45159699Seric 			if (e->e_sendmode != SM_DELIVER)
45259699Seric 				e->e_flags |= EF_VRFYONLY;
45358919Seric 
4544549Seric 			p = skipword(p, "to");
4554549Seric 			if (p == NULL)
4564549Seric 				break;
45764284Seric 			a = parseaddr(p, NULLADDR, RF_COPYALL, ' ', NULL, e);
45812612Seric 			if (a == NULL)
45912612Seric 				break;
46016886Seric 			a->q_flags |= QPRIMARY;
46155012Seric 			a = recipient(a, &e->e_sendqueue, e);
46212612Seric 			if (Errors != 0)
46312612Seric 				break;
46412612Seric 
46512612Seric 			/* no errors during parsing, but might be a duplicate */
46655012Seric 			e->e_to = p;
46712612Seric 			if (!bitset(QBADADDR, a->q_flags))
46859747Seric 			{
46964718Seric 				message("250 Recipient ok%s",
47064718Seric 					bitset(QQUEUEUP, a->q_flags) ?
47164718Seric 						" (will queue)" : "");
47259747Seric 				nrcpts++;
47359747Seric 			}
47412612Seric 			else
4754549Seric 			{
47612612Seric 				/* punt -- should keep message in ADDRESS.... */
47758151Seric 				message("550 Addressee unknown");
4784549Seric 			}
47955012Seric 			e->e_to = NULL;
4804549Seric 			break;
4814549Seric 
4824549Seric 		  case CMDDATA:		/* data -- text of mail */
48361093Seric 			SmtpPhase = "server DATA";
48458109Seric 			if (!gotmail)
4854549Seric 			{
48658151Seric 				message("503 Need MAIL command");
4874976Seric 				break;
4884549Seric 			}
48964718Seric 			else if (nrcpts <= 0)
4904549Seric 			{
49158151Seric 				message("503 Need RCPT (recipient)");
4924976Seric 				break;
4934549Seric 			}
4944976Seric 
49558929Seric 			/* check to see if we need to re-expand aliases */
49663787Seric 			/* also reset QBADADDR on already-diagnosted addrs */
49763787Seric 			doublequeue = FALSE;
49858929Seric 			for (a = e->e_sendqueue; a != NULL; a = a->q_next)
49958929Seric 			{
50058929Seric 				if (bitset(QVERIFIED, a->q_flags))
50163787Seric 				{
50263787Seric 					/* need to re-expand aliases */
50363787Seric 					doublequeue = TRUE;
50463787Seric 				}
50563787Seric 				if (bitset(QBADADDR, a->q_flags))
50663787Seric 				{
50763787Seric 					/* make this "go away" */
50863787Seric 					a->q_flags |= QDONTSEND;
50963787Seric 					a->q_flags &= ~QBADADDR;
51063787Seric 				}
51158929Seric 			}
51258929Seric 
5134976Seric 			/* collect the text of the message */
51424943Seric 			SmtpPhase = "collect";
51564766Seric 			collect(TRUE, doublequeue, e);
51664766Seric 			if (Errors != 0)
51764766Seric 				goto abortmessage;
51863965Seric 			HoldErrs = TRUE;
5194976Seric 
5208238Seric 			/*
5218238Seric 			**  Arrange to send to everyone.
5228238Seric 			**	If sending to multiple people, mail back
5238238Seric 			**		errors rather than reporting directly.
5248238Seric 			**	In any case, don't mail back errors for
5258238Seric 			**		anything that has happened up to
5268238Seric 			**		now (the other end will do this).
52710197Seric 			**	Truncate our transcript -- the mail has gotten
52810197Seric 			**		to us successfully, and if we have
52910197Seric 			**		to mail this back, it will be easier
53010197Seric 			**		on the reader.
5318238Seric 			**	Then send to everyone.
5328238Seric 			**	Finally give a reply code.  If an error has
5338238Seric 			**		already been given, don't mail a
5348238Seric 			**		message back.
5359339Seric 			**	We goose error returns by clearing error bit.
5368238Seric 			*/
5378238Seric 
53824943Seric 			SmtpPhase = "delivery";
53963787Seric 			if (nrcpts != 1 && !doublequeue)
5409378Seric 			{
5419378Seric 				HoldErrs = TRUE;
54258734Seric 				e->e_errormode = EM_MAIL;
5439378Seric 			}
54455012Seric 			e->e_xfp = freopen(queuename(e, 'x'), "w", e->e_xfp);
54558714Seric 			id = e->e_id;
5464976Seric 
5474976Seric 			/* send to all recipients */
54863787Seric 			sendall(e, doublequeue ? SM_QUEUE : SM_DEFAULT);
54955012Seric 			e->e_to = NULL;
5504976Seric 
5518238Seric 			/* issue success if appropriate and reset */
5528238Seric 			if (Errors == 0 || HoldErrs)
55358855Seric 				message("250 %s Message accepted for delivery", id);
55459747Seric 
55559747Seric 			if (bitset(EF_FATALERRS, e->e_flags) && !HoldErrs)
55659730Seric 			{
55759730Seric 				/* avoid sending back an extra message */
55859730Seric 				e->e_flags &= ~EF_FATALERRS;
55959747Seric 				e->e_flags |= EF_CLRQUEUE;
56059730Seric 			}
5618238Seric 			else
56258919Seric 			{
56359747Seric 				/* from now on, we have to operate silently */
56459747Seric 				HoldErrs = TRUE;
56559747Seric 				e->e_errormode = EM_MAIL;
56659747Seric 
56759730Seric 				/* if we just queued, poke it */
56863787Seric 				if (doublequeue && e->e_sendmode != SM_QUEUE)
56959730Seric 				{
57064296Seric 					extern pid_t dowork();
57164296Seric 
57259730Seric 					unlockqueue(e);
57364296Seric 					(void) dowork(id, TRUE, TRUE, e);
57459730Seric 				}
57558919Seric 			}
57658883Seric 
57759747Seric   abortmessage:
5789339Seric 			/* if in a child, pop back to our parent */
5799339Seric 			if (InChild)
5809339Seric 				finis();
58124943Seric 
58224943Seric 			/* clean up a bit */
58358109Seric 			gotmail = FALSE;
58455012Seric 			dropenvelope(e);
58558179Seric 			CurEnv = e = newenvelope(e, CurEnv);
58655012Seric 			e->e_flags = BlankEnvelope.e_flags;
5874549Seric 			break;
5884549Seric 
5894549Seric 		  case CMDRSET:		/* rset -- reset state */
59058151Seric 			message("250 Reset state");
59164359Seric 			e->e_flags |= EF_CLRQUEUE;
5929339Seric 			if (InChild)
5939339Seric 				finis();
59458109Seric 
59558109Seric 			/* clean up a bit */
59658109Seric 			gotmail = FALSE;
59758109Seric 			dropenvelope(e);
59858179Seric 			CurEnv = e = newenvelope(e, CurEnv);
5999339Seric 			break;
6004549Seric 
6014549Seric 		  case CMDVRFY:		/* vrfy -- verify address */
60258092Seric 		  case CMDEXPN:		/* expn -- expand address */
60358092Seric 			vrfy = c->cmdcode == CMDVRFY;
60458092Seric 			if (bitset(vrfy ? PRIV_NOVRFY : PRIV_NOEXPN,
60558092Seric 						PrivacyFlags))
60658082Seric 			{
60758412Seric 				if (vrfy)
60858412Seric 					message("252 Who's to say?");
60958412Seric 				else
61065192Seric 					message("502 Sorry, we do not allow this operation");
61165017Seric #ifdef LOG
61265017Seric 				if (LogLevel > 5)
61365017Seric 					syslog(LOG_INFO, "%s: %s [rejected]",
61465017Seric 						CurSmtpClient, inp);
61565017Seric #endif
61658082Seric 				break;
61758082Seric 			}
61858082Seric 			else if (!gothello &&
61958092Seric 				 bitset(vrfy ? PRIV_NEEDVRFYHELO : PRIV_NEEDEXPNHELO,
62058092Seric 						PrivacyFlags))
62158082Seric 			{
62258151Seric 				message("503 I demand that you introduce yourself first");
62358082Seric 				break;
62458082Seric 			}
62558092Seric 			if (runinchild(vrfy ? "SMTP-VRFY" : "SMTP-EXPN", e) > 0)
6269339Seric 				break;
62755173Seric #ifdef LOG
62858020Seric 			if (LogLevel > 5)
62965017Seric 				syslog(LOG_INFO, "%s: %s", CurSmtpClient, inp);
63055173Seric #endif
6315003Seric 			vrfyqueue = NULL;
6327762Seric 			QuickAbort = TRUE;
63358092Seric 			if (vrfy)
63458092Seric 				e->e_flags |= EF_VRFYONLY;
63562373Seric 			while (*p != '\0' && isascii(*p) && isspace(*p))
63662373Seric 				*p++;
63762373Seric 			if (*p == '\0')
63862373Seric 			{
63962373Seric 				message("501 Argument required");
64062373Seric 				Errors++;
64162373Seric 			}
64262373Seric 			else
64362373Seric 			{
64464284Seric 				(void) sendtolist(p, NULLADDR, &vrfyqueue, e);
64562373Seric 			}
6467762Seric 			if (Errors != 0)
6479339Seric 			{
6489339Seric 				if (InChild)
6499339Seric 					finis();
6507762Seric 				break;
6519339Seric 			}
65262373Seric 			if (vrfyqueue == NULL)
65362373Seric 			{
65462373Seric 				message("554 Nothing to %s", vrfy ? "VRFY" : "EXPN");
65562373Seric 			}
6565003Seric 			while (vrfyqueue != NULL)
6575003Seric 			{
65863971Seric 				a = vrfyqueue;
65963971Seric 				while ((a = a->q_next) != NULL &&
66063971Seric 				       bitset(QDONTSEND|QBADADDR, a->q_flags))
66163971Seric 					continue;
6627685Seric 				if (!bitset(QDONTSEND|QBADADDR, vrfyqueue->q_flags))
66358151Seric 					printvrfyaddr(vrfyqueue, a == NULL);
66463847Seric 				vrfyqueue = vrfyqueue->q_next;
6655003Seric 			}
6669339Seric 			if (InChild)
6679339Seric 				finis();
6684549Seric 			break;
6694549Seric 
6704549Seric 		  case CMDHELP:		/* help -- give user info */
6714577Seric 			help(p);
6724549Seric 			break;
6734549Seric 
6744549Seric 		  case CMDNOOP:		/* noop -- do nothing */
67564122Seric 			message("250 OK");
6764549Seric 			break;
6774549Seric 
6784549Seric 		  case CMDQUIT:		/* quit -- leave mail */
67958151Seric 			message("221 %s closing connection", MyHostName);
68061051Seric 
68161051Seric 			/* avoid future 050 messages */
68261051Seric 			Verbose = FALSE;
68361051Seric 
6849339Seric 			if (InChild)
6859339Seric 				ExitStat = EX_QUIT;
6864549Seric 			finis();
6874549Seric 
6888544Seric 		  case CMDVERB:		/* set verbose mode */
68959957Seric 			if (bitset(PRIV_NOEXPN, PrivacyFlags))
69059957Seric 			{
69159957Seric 				/* this would give out the same info */
69259957Seric 				message("502 Verbose unavailable");
69359957Seric 				break;
69459957Seric 			}
6958544Seric 			Verbose = TRUE;
69658734Seric 			e->e_sendmode = SM_DELIVER;
69759957Seric 			message("250 Verbose mode");
6988544Seric 			break;
6998544Seric 
7009314Seric 		  case CMDONEX:		/* doing one transaction only */
7019378Seric 			OneXact = TRUE;
70259957Seric 			message("250 Only one transaction");
7039314Seric 			break;
7049314Seric 
70536230Skarels # ifdef SMTPDEBUG
7069339Seric 		  case CMDDBGQSHOW:	/* show queues */
7076907Seric 			printf("Send Queue=");
70855012Seric 			printaddr(e->e_sendqueue, TRUE);
7095003Seric 			break;
7107275Seric 
7117275Seric 		  case CMDDBGDEBUG:	/* set debug mode */
7127676Seric 			tTsetup(tTdvect, sizeof tTdvect, "0-99.1");
7137676Seric 			tTflag(p);
71458151Seric 			message("200 Debug set");
7157275Seric 			break;
7167275Seric 
71736230Skarels # else /* not SMTPDEBUG */
71836230Skarels 		  case CMDDBGQSHOW:	/* show queues */
71936230Skarels 		  case CMDDBGDEBUG:	/* set debug mode */
72064685Seric # endif /* SMTPDEBUG */
72164685Seric 		  case CMDLOGBOGUS:	/* bogus command */
72236233Skarels # ifdef LOG
72358308Seric 			if (LogLevel > 0)
72464685Seric 				syslog(LOG_CRIT,
72558020Seric 				    "\"%s\" command from %s (%s)",
72636230Skarels 				    c->cmdname, RealHostName,
72758755Seric 				    anynet_ntoa(&RealHostAddr));
72836233Skarels # endif
72936230Skarels 			/* FALL THROUGH */
73036230Skarels 
7314549Seric 		  case CMDERROR:	/* unknown command */
73258151Seric 			message("500 Command unrecognized");
7334549Seric 			break;
7344549Seric 
7354549Seric 		  default:
73636230Skarels 			errno = 0;
73758151Seric 			syserr("500 smtp: unknown code %d", c->cmdcode);
7384549Seric 			break;
7394549Seric 		}
7404549Seric 	}
7414549Seric }
7424549Seric /*
7434549Seric **  SKIPWORD -- skip a fixed word.
7444549Seric **
7454549Seric **	Parameters:
7464549Seric **		p -- place to start looking.
7474549Seric **		w -- word to skip.
7484549Seric **
7494549Seric **	Returns:
7504549Seric **		p following w.
7514549Seric **		NULL on error.
7524549Seric **
7534549Seric **	Side Effects:
7544549Seric **		clobbers the p data area.
7554549Seric */
7564549Seric 
7574549Seric static char *
7584549Seric skipword(p, w)
7594549Seric 	register char *p;
7604549Seric 	char *w;
7614549Seric {
7624549Seric 	register char *q;
7634549Seric 
7644549Seric 	/* find beginning of word */
76558050Seric 	while (isascii(*p) && isspace(*p))
7664549Seric 		p++;
7674549Seric 	q = p;
7684549Seric 
7694549Seric 	/* find end of word */
77058050Seric 	while (*p != '\0' && *p != ':' && !(isascii(*p) && isspace(*p)))
7714549Seric 		p++;
77258050Seric 	while (isascii(*p) && isspace(*p))
7734549Seric 		*p++ = '\0';
7744549Seric 	if (*p != ':')
7754549Seric 	{
7764549Seric 	  syntax:
77762373Seric 		message("501 Syntax error in parameters");
7784549Seric 		Errors++;
7794549Seric 		return (NULL);
7804549Seric 	}
7814549Seric 	*p++ = '\0';
78258050Seric 	while (isascii(*p) && isspace(*p))
7834549Seric 		p++;
7844549Seric 
78562373Seric 	if (*p == '\0')
78662373Seric 		goto syntax;
78762373Seric 
7884549Seric 	/* see if the input word matches desired word */
78933725Sbostic 	if (strcasecmp(q, w))
7904549Seric 		goto syntax;
7914549Seric 
7924549Seric 	return (p);
7934549Seric }
7944577Seric /*
79558151Seric **  PRINTVRFYADDR -- print an entry in the verify queue
79658151Seric **
79758151Seric **	Parameters:
79858151Seric **		a -- the address to print
79958151Seric **		last -- set if this is the last one.
80058151Seric **
80158151Seric **	Returns:
80258151Seric **		none.
80358151Seric **
80458151Seric **	Side Effects:
80558151Seric **		Prints the appropriate 250 codes.
80658151Seric */
80758151Seric 
80858151Seric printvrfyaddr(a, last)
80958151Seric 	register ADDRESS *a;
81058151Seric 	bool last;
81158151Seric {
81258151Seric 	char fmtbuf[20];
81358151Seric 
81458151Seric 	strcpy(fmtbuf, "250");
81558151Seric 	fmtbuf[3] = last ? ' ' : '-';
81658151Seric 
81759746Seric 	if (a->q_fullname == NULL)
81859746Seric 	{
81959746Seric 		if (strchr(a->q_user, '@') == NULL)
82059746Seric 			strcpy(&fmtbuf[4], "<%s@%s>");
82159746Seric 		else
82259746Seric 			strcpy(&fmtbuf[4], "<%s>");
82359746Seric 		message(fmtbuf, a->q_user, MyHostName);
82459746Seric 	}
82558151Seric 	else
82658151Seric 	{
82759746Seric 		if (strchr(a->q_user, '@') == NULL)
82859746Seric 			strcpy(&fmtbuf[4], "%s <%s@%s>");
82959746Seric 		else
83059746Seric 			strcpy(&fmtbuf[4], "%s <%s>");
83159746Seric 		message(fmtbuf, a->q_fullname, a->q_user, MyHostName);
83258151Seric 	}
83358151Seric }
83458151Seric /*
8354577Seric **  HELP -- implement the HELP command.
8364577Seric **
8374577Seric **	Parameters:
8384577Seric **		topic -- the topic we want help for.
8394577Seric **
8404577Seric **	Returns:
8414577Seric **		none.
8424577Seric **
8434577Seric **	Side Effects:
8444577Seric **		outputs the help file to message output.
8454577Seric */
8464577Seric 
8474577Seric help(topic)
8484577Seric 	char *topic;
8494577Seric {
8504577Seric 	register FILE *hf;
8514577Seric 	int len;
8524577Seric 	char buf[MAXLINE];
8534577Seric 	bool noinfo;
8544577Seric 
8558269Seric 	if (HelpFile == NULL || (hf = fopen(HelpFile, "r")) == NULL)
8564577Seric 	{
8574577Seric 		/* no help */
85811931Seric 		errno = 0;
85958151Seric 		message("502 HELP not implemented");
8604577Seric 		return;
8614577Seric 	}
8624577Seric 
86349669Seric 	if (topic == NULL || *topic == '\0')
86449669Seric 		topic = "smtp";
86549669Seric 	else
86649669Seric 		makelower(topic);
86749669Seric 
8684577Seric 	len = strlen(topic);
8694577Seric 	noinfo = TRUE;
8704577Seric 
8714577Seric 	while (fgets(buf, sizeof buf, hf) != NULL)
8724577Seric 	{
8734577Seric 		if (strncmp(buf, topic, len) == 0)
8744577Seric 		{
8754577Seric 			register char *p;
8764577Seric 
87756795Seric 			p = strchr(buf, '\t');
8784577Seric 			if (p == NULL)
8794577Seric 				p = buf;
8804577Seric 			else
8814577Seric 				p++;
8824577Seric 			fixcrlf(p, TRUE);
88358151Seric 			message("214-%s", p);
8844577Seric 			noinfo = FALSE;
8854577Seric 		}
8864577Seric 	}
8874577Seric 
8884577Seric 	if (noinfo)
88958151Seric 		message("504 HELP topic unknown");
8904577Seric 	else
89158151Seric 		message("214 End of HELP info");
8924628Seric 	(void) fclose(hf);
8934577Seric }
8948544Seric /*
8959339Seric **  RUNINCHILD -- return twice -- once in the child, then in the parent again
8969339Seric **
8979339Seric **	Parameters:
8989339Seric **		label -- a string used in error messages
8999339Seric **
9009339Seric **	Returns:
9019339Seric **		zero in the child
9029339Seric **		one in the parent
9039339Seric **
9049339Seric **	Side Effects:
9059339Seric **		none.
9069339Seric */
9078544Seric 
90855012Seric runinchild(label, e)
9099339Seric 	char *label;
91055012Seric 	register ENVELOPE *e;
9119339Seric {
9129339Seric 	int childpid;
9139339Seric 
91416158Seric 	if (!OneXact)
9159339Seric 	{
91616158Seric 		childpid = dofork();
91716158Seric 		if (childpid < 0)
91816158Seric 		{
91916158Seric 			syserr("%s: cannot fork", label);
92016158Seric 			return (1);
92116158Seric 		}
92216158Seric 		if (childpid > 0)
92316158Seric 		{
92416158Seric 			auto int st;
9259339Seric 
92616158Seric 			/* parent -- wait for child to complete */
92761093Seric 			setproctitle("server %s child wait", CurHostName);
92816158Seric 			st = waitfor(childpid);
92916158Seric 			if (st == -1)
93016158Seric 				syserr("%s: lost child", label);
93164948Seric 			else if (!WIFEXITED(st))
93264948Seric 				syserr("%s: died on signal %d",
93364948Seric 					label, st & 0177);
9349339Seric 
93516158Seric 			/* if we exited on a QUIT command, complete the process */
93616158Seric 			if (st == (EX_QUIT << 8))
93716158Seric 				finis();
9389339Seric 
93916158Seric 			return (1);
94016158Seric 		}
94116158Seric 		else
94216158Seric 		{
94316158Seric 			/* child */
94416158Seric 			InChild = TRUE;
94525050Seric 			QuickAbort = FALSE;
94655012Seric 			clearenvelope(e, FALSE);
94716158Seric 		}
9489339Seric 	}
94915256Seric 
95016158Seric 	/* open alias database */
95160537Seric 	initmaps(FALSE, e);
95216158Seric 
95316158Seric 	return (0);
9549339Seric }
9559339Seric 
95656795Seric # endif /* SMTP */
957