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*65089Seric static char sccsid[] = "@(#)srvrsmtp.c	8.22 (Berkeley) 12/11/93 (with SMTP)";
1433731Sbostic #else
15*65089Seric static char sccsid[] = "@(#)srvrsmtp.c	8.22 (Berkeley) 12/11/93 (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 */
14712612Seric 		if (setjmp(TopFrame) > 0 && InChild)
14859058Seric 		{
14959058Seric 			QuickAbort = FALSE;
15059058Seric 			SuprErrs = TRUE;
15112612Seric 			finis();
15259058Seric 		}
15312612Seric 		QuickAbort = FALSE;
15412612Seric 		HoldErrs = FALSE;
15551951Seric 		LogUsrErrs = FALSE;
15663843Seric 		e->e_flags &= ~(EF_VRFYONLY|EF_GLOBALERRS);
15712612Seric 
1587356Seric 		/* setup for the read */
15955012Seric 		e->e_to = NULL;
1604577Seric 		Errors = 0;
1617275Seric 		(void) fflush(stdout);
1627356Seric 
1637356Seric 		/* read the input line */
16461093Seric 		SmtpPhase = "server cmd read";
16561093Seric 		setproctitle("server %s cmd read", CurHostName);
16661093Seric 		p = sfgets(inp, sizeof inp, InChannel, TimeOuts.to_nextcommand,
16761093Seric 				SmtpPhase);
1687356Seric 
1697685Seric 		/* handle errors */
1707356Seric 		if (p == NULL)
1717356Seric 		{
1724549Seric 			/* end of file, just die */
17358151Seric 			message("421 %s Lost input channel from %s",
17465017Seric 				MyHostName, CurSmtpClient);
17555464Seric #ifdef LOG
17663843Seric 			if (LogLevel > (gotmail ? 1 : 19))
17755464Seric 				syslog(LOG_NOTICE, "lost input channel from %s",
17865017Seric 					CurSmtpClient);
17955464Seric #endif
18058069Seric 			if (InChild)
18158069Seric 				ExitStat = EX_QUIT;
1824549Seric 			finis();
1834549Seric 		}
1844549Seric 
1854549Seric 		/* clean up end of line */
1864558Seric 		fixcrlf(inp, TRUE);
1874549Seric 
1884713Seric 		/* echo command to transcript */
18955012Seric 		if (e->e_xfp != NULL)
19055012Seric 			fprintf(e->e_xfp, "<<< %s\n", inp);
1914713Seric 
19259060Seric 		if (e->e_id == NULL)
19365058Seric 			setproctitle("%s: %.80s", CurSmtpClient, inp);
19459060Seric 		else
19565058Seric 			setproctitle("%s %s: %.80s", e->e_id, CurSmtpClient, inp);
19659060Seric 
1974549Seric 		/* break off command */
19858050Seric 		for (p = inp; isascii(*p) && isspace(*p); p++)
1994549Seric 			continue;
20057232Seric 		cmd = cmdbuf;
20158050Seric 		while (*p != '\0' &&
20258050Seric 		       !(isascii(*p) && isspace(*p)) &&
20358050Seric 		       cmd < &cmdbuf[sizeof cmdbuf - 2])
20424981Seric 			*cmd++ = *p++;
20524981Seric 		*cmd = '\0';
2064549Seric 
20725691Seric 		/* throw away leading whitespace */
20858050Seric 		while (isascii(*p) && isspace(*p))
20925691Seric 			p++;
21025691Seric 
2114549Seric 		/* decode command */
2124549Seric 		for (c = CmdTab; c->cmdname != NULL; c++)
2134549Seric 		{
21433725Sbostic 			if (!strcasecmp(c->cmdname, cmdbuf))
2154549Seric 				break;
2164549Seric 		}
2174549Seric 
21851954Seric 		/* reset errors */
21951954Seric 		errno = 0;
22051954Seric 
2214549Seric 		/* process command */
2224549Seric 		switch (c->cmdcode)
2234549Seric 		{
2244976Seric 		  case CMDHELO:		/* hello -- introduce yourself */
22558323Seric 		  case CMDEHLO:		/* extended hello */
22658323Seric 			if (c->cmdcode == CMDEHLO)
22758323Seric 			{
22858323Seric 				protocol = "ESMTP";
22961093Seric 				SmtpPhase = "server EHLO";
23058323Seric 			}
23158323Seric 			else
23258323Seric 			{
23358323Seric 				protocol = "SMTP";
23461093Seric 				SmtpPhase = "server HELO";
23558323Seric 			}
23659016Seric 			sendinghost = newstr(p);
23764284Seric 			if (strcasecmp(p, RealHostName) != 0 &&
23864284Seric 			    (strcasecmp(RealHostName, "localhost") != 0 ||
23964284Seric 			     strcasecmp(p, MyHostName) != 0))
24011146Seric 			{
24158789Seric 				auth_warning(e, "Host %s claimed to be %s",
24258789Seric 					RealHostName, p);
24311146Seric 			}
24458323Seric 
24560210Seric 			gothello = TRUE;
24660210Seric 			if (c->cmdcode != CMDEHLO)
24760239Seric 			{
24860239Seric 				/* print old message and be done with it */
24960239Seric 				message("250 %s Hello %s, pleased to meet you",
25065017Seric 					MyHostName, CurSmtpClient);
25160210Seric 				break;
25260239Seric 			}
25360239Seric 
25460239Seric 			/* print extended message and brag */
25560239Seric 			message("250-%s Hello %s, pleased to meet you",
25660239Seric 				MyHostName, p);
25758323Seric 			if (!bitset(PRIV_NOEXPN, PrivacyFlags))
25858323Seric 				message("250-EXPN");
25964359Seric 			if (MaxMessageSize > 0)
26064359Seric 				message("250-SIZE %ld", MaxMessageSize);
26159271Seric 			else
26259271Seric 				message("250-SIZE");
26358323Seric 			message("250 HELP");
2644976Seric 			break;
2654976Seric 
2664549Seric 		  case CMDMAIL:		/* mail -- designate sender */
26761093Seric 			SmtpPhase = "server MAIL";
26824943Seric 
2699314Seric 			/* check for validity of this command */
27058789Seric 			if (!gothello)
27158082Seric 			{
27258957Seric 				/* set sending host to our known value */
27359016Seric 				if (sendinghost == NULL)
27459016Seric 					sendinghost = RealHostName;
27558957Seric 
27658789Seric 				if (bitset(PRIV_NEEDMAILHELO, PrivacyFlags))
27758821Seric 				{
27858789Seric 					message("503 Polite people say HELO first");
27958821Seric 					break;
28058821Seric 				}
28158082Seric 			}
28258109Seric 			if (gotmail)
2834558Seric 			{
28458151Seric 				message("503 Sender already specified");
28563843Seric 				if (InChild)
28663843Seric 					finis();
2874558Seric 				break;
2884558Seric 			}
2899339Seric 			if (InChild)
2909339Seric 			{
29136230Skarels 				errno = 0;
29258151Seric 				syserr("503 Nested MAIL command: MAIL %s", p);
29358069Seric 				finis();
2949339Seric 			}
2959339Seric 
2969339Seric 			/* fork a subprocess to process this command */
29755012Seric 			if (runinchild("SMTP-MAIL", e) > 0)
2989339Seric 				break;
29963753Seric 			if (!gothello)
30063753Seric 			{
30163753Seric 				auth_warning(e,
30263753Seric 					"Host %s didn't use HELO protocol",
30363753Seric 					RealHostName);
30463753Seric 			}
30558323Seric 			if (protocol == NULL)
30658323Seric 				protocol = "SMTP";
30758323Seric 			define('r', protocol, e);
30859016Seric 			define('s', sendinghost, e);
30955012Seric 			initsys(e);
31059747Seric 			nrcpts = 0;
311*65089Seric 			e->e_flags |= EF_LOGSENDER;
31265058Seric 			setproctitle("%s %s: %.80s", e->e_id, CurSmtpClient, inp);
3139339Seric 
3149339Seric 			/* child -- go do the processing */
3154549Seric 			p = skipword(p, "from");
3164549Seric 			if (p == NULL)
3174549Seric 				break;
31857977Seric 			if (setjmp(TopFrame) > 0)
31958147Seric 			{
32058147Seric 				/* this failed -- undo work */
32158147Seric 				if (InChild)
32259058Seric 				{
32359058Seric 					QuickAbort = FALSE;
32459058Seric 					SuprErrs = TRUE;
32563787Seric 					e->e_flags &= ~EF_FATALERRS;
32658147Seric 					finis();
32759058Seric 				}
32857977Seric 				break;
32958147Seric 			}
33057977Seric 			QuickAbort = TRUE;
33158333Seric 
33258333Seric 			/* must parse sender first */
33358333Seric 			delimptr = NULL;
33458704Seric 			setsender(p, e, &delimptr, FALSE);
33558333Seric 			p = delimptr;
33658333Seric 			if (p != NULL && *p != '\0')
33758333Seric 				*p++ = '\0';
33858333Seric 
33958333Seric 			/* now parse ESMTP arguments */
34058333Seric 			msize = 0;
34158333Seric 			for (; p != NULL && *p != '\0'; p++)
34258333Seric 			{
34358333Seric 				char *kp;
34458333Seric 				char *vp;
34558333Seric 
34658333Seric 				/* locate the beginning of the keyword */
34758333Seric 				while (isascii(*p) && isspace(*p))
34858333Seric 					p++;
34958333Seric 				if (*p == '\0')
35058333Seric 					break;
35158333Seric 				kp = p;
35258333Seric 
35358333Seric 				/* skip to the value portion */
35458333Seric 				while (isascii(*p) && isalnum(*p) || *p == '-')
35558333Seric 					p++;
35658333Seric 				if (*p == '=')
35758333Seric 				{
35858333Seric 					*p++ = '\0';
35958333Seric 					vp = p;
36058333Seric 
36158333Seric 					/* skip to the end of the value */
36258333Seric 					while (*p != '\0' && *p != ' ' &&
36358333Seric 					       !(isascii(*p) && iscntrl(*p)) &&
36458333Seric 					       *p != '=')
36558333Seric 						p++;
36658333Seric 				}
36758333Seric 
36858333Seric 				if (*p != '\0')
36958333Seric 					*p++ = '\0';
37058333Seric 
37158333Seric 				if (tTd(19, 1))
37258333Seric 					printf("MAIL: got arg %s=%s\n", kp,
37358333Seric 						vp == NULL ? "<null>" : vp);
37458333Seric 
37558333Seric 				if (strcasecmp(kp, "size") == 0)
37658333Seric 				{
37759093Seric 					if (vp == NULL)
37858333Seric 					{
37958333Seric 						usrerr("501 SIZE requires a value");
38058333Seric 						/* NOTREACHED */
38158333Seric 					}
38258333Seric 					msize = atol(vp);
38358333Seric 				}
38459093Seric 				else if (strcasecmp(kp, "body") == 0)
38559093Seric 				{
38659093Seric 					if (vp == NULL)
38759093Seric 					{
38859093Seric 						usrerr("501 BODY requires a value");
38959093Seric 						/* NOTREACHED */
39059093Seric 					}
39159093Seric # ifdef MIME
39259093Seric 					if (strcasecmp(vp, "8bitmime") == 0)
39359093Seric 					{
39459093Seric 						e->e_bodytype = "8BITMIME";
39559709Seric 						SevenBit = FALSE;
39659093Seric 					}
39759093Seric 					else if (strcasecmp(vp, "7bit") == 0)
39859093Seric 					{
39959093Seric 						e->e_bodytype = "7BIT";
40059709Seric 						SevenBit = TRUE;
40159093Seric 					}
40259093Seric 					else
40359093Seric 					{
40459093Seric 						usrerr("501 Unknown BODY type %s",
40559093Seric 							vp);
40659093Seric 					}
40759093Seric # endif
40859093Seric 				}
40958333Seric 				else
41058333Seric 				{
41158333Seric 					usrerr("501 %s parameter unrecognized", kp);
41258333Seric 					/* NOTREACHED */
41358333Seric 				}
41458333Seric 			}
41559284Seric 
41659284Seric 			if (MaxMessageSize > 0 && msize > MaxMessageSize)
41759284Seric 			{
41859284Seric 				usrerr("552 Message size exceeds fixed maximum message size (%ld)",
41959284Seric 					MaxMessageSize);
42059284Seric 				/* NOTREACHED */
42159284Seric 			}
42258333Seric 
42358333Seric 			if (!enoughspace(msize))
42458333Seric 			{
42558333Seric 				message("452 Insufficient disk space; try again later");
42658333Seric 				break;
42758333Seric 			}
42858151Seric 			message("250 Sender ok");
42958147Seric 			gotmail = TRUE;
4304549Seric 			break;
4314549Seric 
4324976Seric 		  case CMDRCPT:		/* rcpt -- designate recipient */
43358850Seric 			if (!gotmail)
43458850Seric 			{
43558850Seric 				usrerr("503 Need MAIL before RCPT");
43658850Seric 				break;
43758850Seric 			}
43861093Seric 			SmtpPhase = "server RCPT";
43912612Seric 			if (setjmp(TopFrame) > 0)
44014785Seric 			{
44155012Seric 				e->e_flags &= ~EF_FATALERRS;
44212612Seric 				break;
44314785Seric 			}
44412612Seric 			QuickAbort = TRUE;
44551951Seric 			LogUsrErrs = TRUE;
44658093Seric 
44759699Seric 			if (e->e_sendmode != SM_DELIVER)
44859699Seric 				e->e_flags |= EF_VRFYONLY;
44958919Seric 
4504549Seric 			p = skipword(p, "to");
4514549Seric 			if (p == NULL)
4524549Seric 				break;
45364284Seric 			a = parseaddr(p, NULLADDR, RF_COPYALL, ' ', NULL, e);
45412612Seric 			if (a == NULL)
45512612Seric 				break;
45616886Seric 			a->q_flags |= QPRIMARY;
45755012Seric 			a = recipient(a, &e->e_sendqueue, e);
45812612Seric 			if (Errors != 0)
45912612Seric 				break;
46012612Seric 
46112612Seric 			/* no errors during parsing, but might be a duplicate */
46255012Seric 			e->e_to = p;
46312612Seric 			if (!bitset(QBADADDR, a->q_flags))
46459747Seric 			{
46564718Seric 				message("250 Recipient ok%s",
46664718Seric 					bitset(QQUEUEUP, a->q_flags) ?
46764718Seric 						" (will queue)" : "");
46859747Seric 				nrcpts++;
46959747Seric 			}
47012612Seric 			else
4714549Seric 			{
47212612Seric 				/* punt -- should keep message in ADDRESS.... */
47358151Seric 				message("550 Addressee unknown");
4744549Seric 			}
47555012Seric 			e->e_to = NULL;
4764549Seric 			break;
4774549Seric 
4784549Seric 		  case CMDDATA:		/* data -- text of mail */
47961093Seric 			SmtpPhase = "server DATA";
48058109Seric 			if (!gotmail)
4814549Seric 			{
48258151Seric 				message("503 Need MAIL command");
4834976Seric 				break;
4844549Seric 			}
48564718Seric 			else if (nrcpts <= 0)
4864549Seric 			{
48758151Seric 				message("503 Need RCPT (recipient)");
4884976Seric 				break;
4894549Seric 			}
4904976Seric 
49158929Seric 			/* check to see if we need to re-expand aliases */
49263787Seric 			/* also reset QBADADDR on already-diagnosted addrs */
49363787Seric 			doublequeue = FALSE;
49458929Seric 			for (a = e->e_sendqueue; a != NULL; a = a->q_next)
49558929Seric 			{
49658929Seric 				if (bitset(QVERIFIED, a->q_flags))
49763787Seric 				{
49863787Seric 					/* need to re-expand aliases */
49963787Seric 					doublequeue = TRUE;
50063787Seric 				}
50163787Seric 				if (bitset(QBADADDR, a->q_flags))
50263787Seric 				{
50363787Seric 					/* make this "go away" */
50463787Seric 					a->q_flags |= QDONTSEND;
50563787Seric 					a->q_flags &= ~QBADADDR;
50663787Seric 				}
50758929Seric 			}
50858929Seric 
5094976Seric 			/* collect the text of the message */
51024943Seric 			SmtpPhase = "collect";
51164766Seric 			collect(TRUE, doublequeue, e);
51264766Seric 			if (Errors != 0)
51364766Seric 				goto abortmessage;
51463965Seric 			HoldErrs = TRUE;
5154976Seric 
5168238Seric 			/*
5178238Seric 			**  Arrange to send to everyone.
5188238Seric 			**	If sending to multiple people, mail back
5198238Seric 			**		errors rather than reporting directly.
5208238Seric 			**	In any case, don't mail back errors for
5218238Seric 			**		anything that has happened up to
5228238Seric 			**		now (the other end will do this).
52310197Seric 			**	Truncate our transcript -- the mail has gotten
52410197Seric 			**		to us successfully, and if we have
52510197Seric 			**		to mail this back, it will be easier
52610197Seric 			**		on the reader.
5278238Seric 			**	Then send to everyone.
5288238Seric 			**	Finally give a reply code.  If an error has
5298238Seric 			**		already been given, don't mail a
5308238Seric 			**		message back.
5319339Seric 			**	We goose error returns by clearing error bit.
5328238Seric 			*/
5338238Seric 
53424943Seric 			SmtpPhase = "delivery";
53563787Seric 			if (nrcpts != 1 && !doublequeue)
5369378Seric 			{
5379378Seric 				HoldErrs = TRUE;
53858734Seric 				e->e_errormode = EM_MAIL;
5399378Seric 			}
54055012Seric 			e->e_xfp = freopen(queuename(e, 'x'), "w", e->e_xfp);
54158714Seric 			id = e->e_id;
5424976Seric 
5434976Seric 			/* send to all recipients */
54463787Seric 			sendall(e, doublequeue ? SM_QUEUE : SM_DEFAULT);
54555012Seric 			e->e_to = NULL;
5464976Seric 
5478238Seric 			/* issue success if appropriate and reset */
5488238Seric 			if (Errors == 0 || HoldErrs)
54958855Seric 				message("250 %s Message accepted for delivery", id);
55059747Seric 
55159747Seric 			if (bitset(EF_FATALERRS, e->e_flags) && !HoldErrs)
55259730Seric 			{
55359730Seric 				/* avoid sending back an extra message */
55459730Seric 				e->e_flags &= ~EF_FATALERRS;
55559747Seric 				e->e_flags |= EF_CLRQUEUE;
55659730Seric 			}
5578238Seric 			else
55858919Seric 			{
55959747Seric 				/* from now on, we have to operate silently */
56059747Seric 				HoldErrs = TRUE;
56159747Seric 				e->e_errormode = EM_MAIL;
56259747Seric 
56359730Seric 				/* if we just queued, poke it */
56463787Seric 				if (doublequeue && e->e_sendmode != SM_QUEUE)
56559730Seric 				{
56664296Seric 					extern pid_t dowork();
56764296Seric 
56859730Seric 					unlockqueue(e);
56964296Seric 					(void) dowork(id, TRUE, TRUE, e);
57059730Seric 				}
57158919Seric 			}
57258883Seric 
57359747Seric   abortmessage:
5749339Seric 			/* if in a child, pop back to our parent */
5759339Seric 			if (InChild)
5769339Seric 				finis();
57724943Seric 
57824943Seric 			/* clean up a bit */
57958109Seric 			gotmail = FALSE;
58055012Seric 			dropenvelope(e);
58158179Seric 			CurEnv = e = newenvelope(e, CurEnv);
58255012Seric 			e->e_flags = BlankEnvelope.e_flags;
5834549Seric 			break;
5844549Seric 
5854549Seric 		  case CMDRSET:		/* rset -- reset state */
58658151Seric 			message("250 Reset state");
58764359Seric 			e->e_flags |= EF_CLRQUEUE;
5889339Seric 			if (InChild)
5899339Seric 				finis();
59058109Seric 
59158109Seric 			/* clean up a bit */
59258109Seric 			gotmail = FALSE;
59358109Seric 			dropenvelope(e);
59458179Seric 			CurEnv = e = newenvelope(e, CurEnv);
5959339Seric 			break;
5964549Seric 
5974549Seric 		  case CMDVRFY:		/* vrfy -- verify address */
59858092Seric 		  case CMDEXPN:		/* expn -- expand address */
59958092Seric 			vrfy = c->cmdcode == CMDVRFY;
60058092Seric 			if (bitset(vrfy ? PRIV_NOVRFY : PRIV_NOEXPN,
60158092Seric 						PrivacyFlags))
60258082Seric 			{
60358412Seric 				if (vrfy)
60458412Seric 					message("252 Who's to say?");
60558412Seric 				else
60658412Seric 					message("502 That's none of your business");
60765017Seric #ifdef LOG
60865017Seric 				if (LogLevel > 5)
60965017Seric 					syslog(LOG_INFO, "%s: %s [rejected]",
61065017Seric 						CurSmtpClient, inp);
61165017Seric #endif
61258082Seric 				break;
61358082Seric 			}
61458082Seric 			else if (!gothello &&
61558092Seric 				 bitset(vrfy ? PRIV_NEEDVRFYHELO : PRIV_NEEDEXPNHELO,
61658092Seric 						PrivacyFlags))
61758082Seric 			{
61858151Seric 				message("503 I demand that you introduce yourself first");
61958082Seric 				break;
62058082Seric 			}
62158092Seric 			if (runinchild(vrfy ? "SMTP-VRFY" : "SMTP-EXPN", e) > 0)
6229339Seric 				break;
62355173Seric #ifdef LOG
62458020Seric 			if (LogLevel > 5)
62565017Seric 				syslog(LOG_INFO, "%s: %s", CurSmtpClient, inp);
62655173Seric #endif
6275003Seric 			vrfyqueue = NULL;
6287762Seric 			QuickAbort = TRUE;
62958092Seric 			if (vrfy)
63058092Seric 				e->e_flags |= EF_VRFYONLY;
63162373Seric 			while (*p != '\0' && isascii(*p) && isspace(*p))
63262373Seric 				*p++;
63362373Seric 			if (*p == '\0')
63462373Seric 			{
63562373Seric 				message("501 Argument required");
63662373Seric 				Errors++;
63762373Seric 			}
63862373Seric 			else
63962373Seric 			{
64064284Seric 				(void) sendtolist(p, NULLADDR, &vrfyqueue, e);
64162373Seric 			}
6427762Seric 			if (Errors != 0)
6439339Seric 			{
6449339Seric 				if (InChild)
6459339Seric 					finis();
6467762Seric 				break;
6479339Seric 			}
64862373Seric 			if (vrfyqueue == NULL)
64962373Seric 			{
65062373Seric 				message("554 Nothing to %s", vrfy ? "VRFY" : "EXPN");
65162373Seric 			}
6525003Seric 			while (vrfyqueue != NULL)
6535003Seric 			{
65463971Seric 				a = vrfyqueue;
65563971Seric 				while ((a = a->q_next) != NULL &&
65663971Seric 				       bitset(QDONTSEND|QBADADDR, a->q_flags))
65763971Seric 					continue;
6587685Seric 				if (!bitset(QDONTSEND|QBADADDR, vrfyqueue->q_flags))
65958151Seric 					printvrfyaddr(vrfyqueue, a == NULL);
66063847Seric 				vrfyqueue = vrfyqueue->q_next;
6615003Seric 			}
6629339Seric 			if (InChild)
6639339Seric 				finis();
6644549Seric 			break;
6654549Seric 
6664549Seric 		  case CMDHELP:		/* help -- give user info */
6674577Seric 			help(p);
6684549Seric 			break;
6694549Seric 
6704549Seric 		  case CMDNOOP:		/* noop -- do nothing */
67164122Seric 			message("250 OK");
6724549Seric 			break;
6734549Seric 
6744549Seric 		  case CMDQUIT:		/* quit -- leave mail */
67558151Seric 			message("221 %s closing connection", MyHostName);
67661051Seric 
67761051Seric 			/* avoid future 050 messages */
67861051Seric 			Verbose = FALSE;
67961051Seric 
6809339Seric 			if (InChild)
6819339Seric 				ExitStat = EX_QUIT;
6824549Seric 			finis();
6834549Seric 
6848544Seric 		  case CMDVERB:		/* set verbose mode */
68559957Seric 			if (bitset(PRIV_NOEXPN, PrivacyFlags))
68659957Seric 			{
68759957Seric 				/* this would give out the same info */
68859957Seric 				message("502 Verbose unavailable");
68959957Seric 				break;
69059957Seric 			}
6918544Seric 			Verbose = TRUE;
69258734Seric 			e->e_sendmode = SM_DELIVER;
69359957Seric 			message("250 Verbose mode");
6948544Seric 			break;
6958544Seric 
6969314Seric 		  case CMDONEX:		/* doing one transaction only */
6979378Seric 			OneXact = TRUE;
69859957Seric 			message("250 Only one transaction");
6999314Seric 			break;
7009314Seric 
70136230Skarels # ifdef SMTPDEBUG
7029339Seric 		  case CMDDBGQSHOW:	/* show queues */
7036907Seric 			printf("Send Queue=");
70455012Seric 			printaddr(e->e_sendqueue, TRUE);
7055003Seric 			break;
7067275Seric 
7077275Seric 		  case CMDDBGDEBUG:	/* set debug mode */
7087676Seric 			tTsetup(tTdvect, sizeof tTdvect, "0-99.1");
7097676Seric 			tTflag(p);
71058151Seric 			message("200 Debug set");
7117275Seric 			break;
7127275Seric 
71336230Skarels # else /* not SMTPDEBUG */
71436230Skarels 		  case CMDDBGQSHOW:	/* show queues */
71536230Skarels 		  case CMDDBGDEBUG:	/* set debug mode */
71664685Seric # endif /* SMTPDEBUG */
71764685Seric 		  case CMDLOGBOGUS:	/* bogus command */
71836233Skarels # ifdef LOG
71958308Seric 			if (LogLevel > 0)
72064685Seric 				syslog(LOG_CRIT,
72158020Seric 				    "\"%s\" command from %s (%s)",
72236230Skarels 				    c->cmdname, RealHostName,
72358755Seric 				    anynet_ntoa(&RealHostAddr));
72436233Skarels # endif
72536230Skarels 			/* FALL THROUGH */
72636230Skarels 
7274549Seric 		  case CMDERROR:	/* unknown command */
72858151Seric 			message("500 Command unrecognized");
7294549Seric 			break;
7304549Seric 
7314549Seric 		  default:
73236230Skarels 			errno = 0;
73358151Seric 			syserr("500 smtp: unknown code %d", c->cmdcode);
7344549Seric 			break;
7354549Seric 		}
7364549Seric 	}
7374549Seric }
7384549Seric /*
7394549Seric **  SKIPWORD -- skip a fixed word.
7404549Seric **
7414549Seric **	Parameters:
7424549Seric **		p -- place to start looking.
7434549Seric **		w -- word to skip.
7444549Seric **
7454549Seric **	Returns:
7464549Seric **		p following w.
7474549Seric **		NULL on error.
7484549Seric **
7494549Seric **	Side Effects:
7504549Seric **		clobbers the p data area.
7514549Seric */
7524549Seric 
7534549Seric static char *
7544549Seric skipword(p, w)
7554549Seric 	register char *p;
7564549Seric 	char *w;
7574549Seric {
7584549Seric 	register char *q;
7594549Seric 
7604549Seric 	/* find beginning of word */
76158050Seric 	while (isascii(*p) && isspace(*p))
7624549Seric 		p++;
7634549Seric 	q = p;
7644549Seric 
7654549Seric 	/* find end of word */
76658050Seric 	while (*p != '\0' && *p != ':' && !(isascii(*p) && isspace(*p)))
7674549Seric 		p++;
76858050Seric 	while (isascii(*p) && isspace(*p))
7694549Seric 		*p++ = '\0';
7704549Seric 	if (*p != ':')
7714549Seric 	{
7724549Seric 	  syntax:
77362373Seric 		message("501 Syntax error in parameters");
7744549Seric 		Errors++;
7754549Seric 		return (NULL);
7764549Seric 	}
7774549Seric 	*p++ = '\0';
77858050Seric 	while (isascii(*p) && isspace(*p))
7794549Seric 		p++;
7804549Seric 
78162373Seric 	if (*p == '\0')
78262373Seric 		goto syntax;
78362373Seric 
7844549Seric 	/* see if the input word matches desired word */
78533725Sbostic 	if (strcasecmp(q, w))
7864549Seric 		goto syntax;
7874549Seric 
7884549Seric 	return (p);
7894549Seric }
7904577Seric /*
79158151Seric **  PRINTVRFYADDR -- print an entry in the verify queue
79258151Seric **
79358151Seric **	Parameters:
79458151Seric **		a -- the address to print
79558151Seric **		last -- set if this is the last one.
79658151Seric **
79758151Seric **	Returns:
79858151Seric **		none.
79958151Seric **
80058151Seric **	Side Effects:
80158151Seric **		Prints the appropriate 250 codes.
80258151Seric */
80358151Seric 
80458151Seric printvrfyaddr(a, last)
80558151Seric 	register ADDRESS *a;
80658151Seric 	bool last;
80758151Seric {
80858151Seric 	char fmtbuf[20];
80958151Seric 
81058151Seric 	strcpy(fmtbuf, "250");
81158151Seric 	fmtbuf[3] = last ? ' ' : '-';
81258151Seric 
81359746Seric 	if (a->q_fullname == NULL)
81459746Seric 	{
81559746Seric 		if (strchr(a->q_user, '@') == NULL)
81659746Seric 			strcpy(&fmtbuf[4], "<%s@%s>");
81759746Seric 		else
81859746Seric 			strcpy(&fmtbuf[4], "<%s>");
81959746Seric 		message(fmtbuf, a->q_user, MyHostName);
82059746Seric 	}
82158151Seric 	else
82258151Seric 	{
82359746Seric 		if (strchr(a->q_user, '@') == NULL)
82459746Seric 			strcpy(&fmtbuf[4], "%s <%s@%s>");
82559746Seric 		else
82659746Seric 			strcpy(&fmtbuf[4], "%s <%s>");
82759746Seric 		message(fmtbuf, a->q_fullname, a->q_user, MyHostName);
82858151Seric 	}
82958151Seric }
83058151Seric /*
8314577Seric **  HELP -- implement the HELP command.
8324577Seric **
8334577Seric **	Parameters:
8344577Seric **		topic -- the topic we want help for.
8354577Seric **
8364577Seric **	Returns:
8374577Seric **		none.
8384577Seric **
8394577Seric **	Side Effects:
8404577Seric **		outputs the help file to message output.
8414577Seric */
8424577Seric 
8434577Seric help(topic)
8444577Seric 	char *topic;
8454577Seric {
8464577Seric 	register FILE *hf;
8474577Seric 	int len;
8484577Seric 	char buf[MAXLINE];
8494577Seric 	bool noinfo;
8504577Seric 
8518269Seric 	if (HelpFile == NULL || (hf = fopen(HelpFile, "r")) == NULL)
8524577Seric 	{
8534577Seric 		/* no help */
85411931Seric 		errno = 0;
85558151Seric 		message("502 HELP not implemented");
8564577Seric 		return;
8574577Seric 	}
8584577Seric 
85949669Seric 	if (topic == NULL || *topic == '\0')
86049669Seric 		topic = "smtp";
86149669Seric 	else
86249669Seric 		makelower(topic);
86349669Seric 
8644577Seric 	len = strlen(topic);
8654577Seric 	noinfo = TRUE;
8664577Seric 
8674577Seric 	while (fgets(buf, sizeof buf, hf) != NULL)
8684577Seric 	{
8694577Seric 		if (strncmp(buf, topic, len) == 0)
8704577Seric 		{
8714577Seric 			register char *p;
8724577Seric 
87356795Seric 			p = strchr(buf, '\t');
8744577Seric 			if (p == NULL)
8754577Seric 				p = buf;
8764577Seric 			else
8774577Seric 				p++;
8784577Seric 			fixcrlf(p, TRUE);
87958151Seric 			message("214-%s", p);
8804577Seric 			noinfo = FALSE;
8814577Seric 		}
8824577Seric 	}
8834577Seric 
8844577Seric 	if (noinfo)
88558151Seric 		message("504 HELP topic unknown");
8864577Seric 	else
88758151Seric 		message("214 End of HELP info");
8884628Seric 	(void) fclose(hf);
8894577Seric }
8908544Seric /*
8919339Seric **  RUNINCHILD -- return twice -- once in the child, then in the parent again
8929339Seric **
8939339Seric **	Parameters:
8949339Seric **		label -- a string used in error messages
8959339Seric **
8969339Seric **	Returns:
8979339Seric **		zero in the child
8989339Seric **		one in the parent
8999339Seric **
9009339Seric **	Side Effects:
9019339Seric **		none.
9029339Seric */
9038544Seric 
90455012Seric runinchild(label, e)
9059339Seric 	char *label;
90655012Seric 	register ENVELOPE *e;
9079339Seric {
9089339Seric 	int childpid;
9099339Seric 
91016158Seric 	if (!OneXact)
9119339Seric 	{
91216158Seric 		childpid = dofork();
91316158Seric 		if (childpid < 0)
91416158Seric 		{
91516158Seric 			syserr("%s: cannot fork", label);
91616158Seric 			return (1);
91716158Seric 		}
91816158Seric 		if (childpid > 0)
91916158Seric 		{
92016158Seric 			auto int st;
9219339Seric 
92216158Seric 			/* parent -- wait for child to complete */
92361093Seric 			setproctitle("server %s child wait", CurHostName);
92416158Seric 			st = waitfor(childpid);
92516158Seric 			if (st == -1)
92616158Seric 				syserr("%s: lost child", label);
92764948Seric 			else if (!WIFEXITED(st))
92864948Seric 				syserr("%s: died on signal %d",
92964948Seric 					label, st & 0177);
9309339Seric 
93116158Seric 			/* if we exited on a QUIT command, complete the process */
93216158Seric 			if (st == (EX_QUIT << 8))
93316158Seric 				finis();
9349339Seric 
93516158Seric 			return (1);
93616158Seric 		}
93716158Seric 		else
93816158Seric 		{
93916158Seric 			/* child */
94016158Seric 			InChild = TRUE;
94125050Seric 			QuickAbort = FALSE;
94255012Seric 			clearenvelope(e, FALSE);
94316158Seric 		}
9449339Seric 	}
94515256Seric 
94616158Seric 	/* open alias database */
94760537Seric 	initmaps(FALSE, e);
94816158Seric 
94916158Seric 	return (0);
9509339Seric }
9519339Seric 
95256795Seric # endif /* SMTP */
953