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*63843Seric static char sccsid[] = "@(#)srvrsmtp.c	8.4 (Berkeley) 07/16/93 (with SMTP)";
1433731Sbostic #else
15*63843Seric static char sccsid[] = "@(#)srvrsmtp.c	8.4 (Berkeley) 07/16/93 (without SMTP)";
1633731Sbostic #endif
1733731Sbostic #endif /* not lint */
1833731Sbostic 
199339Seric # include <errno.h>
2011728Seric # include <signal.h>
214549Seric 
2233731Sbostic # ifdef SMTP
234556Seric 
244549Seric /*
254549Seric **  SMTP -- run the SMTP protocol.
264549Seric **
274549Seric **	Parameters:
284549Seric **		none.
294549Seric **
304549Seric **	Returns:
314549Seric **		never.
324549Seric **
334549Seric **	Side Effects:
344549Seric **		Reads commands from the input channel and processes
354549Seric **			them.
364549Seric */
374549Seric 
384549Seric struct cmd
394549Seric {
404549Seric 	char	*cmdname;	/* command name */
414549Seric 	int	cmdcode;	/* internal code, see below */
424549Seric };
434549Seric 
444549Seric /* values for cmdcode */
454549Seric # define CMDERROR	0	/* bad command */
464549Seric # define CMDMAIL	1	/* mail -- designate sender */
474976Seric # define CMDRCPT	2	/* rcpt -- designate recipient */
484549Seric # define CMDDATA	3	/* data -- send message text */
499339Seric # define CMDRSET	4	/* rset -- reset state */
509339Seric # define CMDVRFY	5	/* vrfy -- verify address */
5158092Seric # define CMDEXPN	6	/* expn -- expand address */
529339Seric # define CMDNOOP	7	/* noop -- do nothing */
539339Seric # define CMDQUIT	8	/* quit -- close connection and die */
549339Seric # define CMDHELO	9	/* helo -- be polite */
5558092Seric # define CMDHELP	10	/* help -- give usage info */
5658323Seric # define CMDEHLO	11	/* ehlo -- extended helo (RFC 1425) */
5758092Seric /* non-standard commands */
5858092Seric # define CMDONEX	16	/* onex -- sending one transaction only */
5958092Seric # define CMDVERB	17	/* verb -- go into verbose mode */
6036230Skarels /* debugging-only commands, only enabled if SMTPDEBUG is defined */
6158092Seric # define CMDDBGQSHOW	24	/* showq -- show send queue */
6258092Seric # define CMDDBGDEBUG	25	/* debug -- set debug mode */
634549Seric 
644549Seric static struct cmd	CmdTab[] =
654549Seric {
664549Seric 	"mail",		CMDMAIL,
674976Seric 	"rcpt",		CMDRCPT,
684549Seric 	"data",		CMDDATA,
694549Seric 	"rset",		CMDRSET,
704549Seric 	"vrfy",		CMDVRFY,
7158092Seric 	"expn",		CMDEXPN,
724549Seric 	"help",		CMDHELP,
734549Seric 	"noop",		CMDNOOP,
744549Seric 	"quit",		CMDQUIT,
754976Seric 	"helo",		CMDHELO,
7658323Seric 	"ehlo",		CMDEHLO,
778544Seric 	"verb",		CMDVERB,
789314Seric 	"onex",		CMDONEX,
7936230Skarels 	/*
8036230Skarels 	 * remaining commands are here only
8136230Skarels 	 * to trap and log attempts to use them
8236230Skarels 	 */
839339Seric 	"showq",	CMDDBGQSHOW,
848544Seric 	"debug",	CMDDBGDEBUG,
854549Seric 	NULL,		CMDERROR,
864549Seric };
874549Seric 
889339Seric bool	InChild = FALSE;		/* true if running in a subprocess */
899378Seric bool	OneXact = FALSE;		/* one xaction only this run */
9011146Seric 
919339Seric #define EX_QUIT		22		/* special code for QUIT command */
928544Seric 
9355012Seric smtp(e)
9455012Seric 	register ENVELOPE *e;
954549Seric {
964549Seric 	register char *p;
978544Seric 	register struct cmd *c;
984549Seric 	char *cmd;
9946928Sbostic 	static char *skipword();
1005003Seric 	auto ADDRESS *vrfyqueue;
10112612Seric 	ADDRESS *a;
10258109Seric 	bool gotmail;			/* mail command received */
10358092Seric 	bool gothello;			/* helo command received */
10458092Seric 	bool vrfy;			/* set if this is a vrfy command */
10558323Seric 	char *protocol;			/* sending protocol */
10659016Seric 	char *sendinghost;		/* sending hostname */
10758333Seric 	long msize;			/* approximate maximum message size */
10858333Seric 	auto char *delimptr;
10958714Seric 	char *id;
11059747Seric 	int nrcpts;			/* number of RCPT commands */
11163787Seric 	bool doublequeue;
1128544Seric 	char inp[MAXLINE];
11357232Seric 	char cmdbuf[MAXLINE];
1147124Seric 	extern char Version[];
11524943Seric 	extern ENVELOPE BlankEnvelope;
1164549Seric 
11759066Seric 	if (fileno(OutChannel) != fileno(stdout))
1187363Seric 	{
1197363Seric 		/* arrange for debugging output to go to remote host */
12059066Seric 		(void) dup2(fileno(OutChannel), fileno(stdout));
1217363Seric 	}
12255012Seric 	settime(e);
12357642Seric 	CurHostName = RealHostName;
12461093Seric 	setproctitle("server %s startup", CurHostName);
12558050Seric 	expand("\201e", inp, &inp[sizeof inp], e);
12660210Seric 	message("220-%s", inp);
12760210Seric 	message("220 ESMTP spoken here");
12858330Seric 	protocol = NULL;
12959016Seric 	sendinghost = macvalue('s', e);
13058082Seric 	gothello = FALSE;
13158330Seric 	gotmail = FALSE;
1324549Seric 	for (;;)
1334549Seric 	{
13412612Seric 		/* arrange for backout */
13512612Seric 		if (setjmp(TopFrame) > 0 && InChild)
13659058Seric 		{
13759058Seric 			QuickAbort = FALSE;
13859058Seric 			SuprErrs = TRUE;
13912612Seric 			finis();
14059058Seric 		}
14112612Seric 		QuickAbort = FALSE;
14212612Seric 		HoldErrs = FALSE;
14351951Seric 		LogUsrErrs = FALSE;
144*63843Seric 		e->e_flags &= ~(EF_VRFYONLY|EF_GLOBALERRS);
14512612Seric 
1467356Seric 		/* setup for the read */
14755012Seric 		e->e_to = NULL;
1484577Seric 		Errors = 0;
1497275Seric 		(void) fflush(stdout);
1507356Seric 
1517356Seric 		/* read the input line */
15261093Seric 		SmtpPhase = "server cmd read";
15361093Seric 		setproctitle("server %s cmd read", CurHostName);
15461093Seric 		p = sfgets(inp, sizeof inp, InChannel, TimeOuts.to_nextcommand,
15561093Seric 				SmtpPhase);
1567356Seric 
1577685Seric 		/* handle errors */
1587356Seric 		if (p == NULL)
1597356Seric 		{
1604549Seric 			/* end of file, just die */
16158151Seric 			message("421 %s Lost input channel from %s",
16225050Seric 				MyHostName, CurHostName);
16355464Seric #ifdef LOG
164*63843Seric 			if (LogLevel > (gotmail ? 1 : 19))
16555464Seric 				syslog(LOG_NOTICE, "lost input channel from %s",
16655464Seric 					CurHostName);
16755464Seric #endif
16858069Seric 			if (InChild)
16958069Seric 				ExitStat = EX_QUIT;
1704549Seric 			finis();
1714549Seric 		}
1724549Seric 
1734549Seric 		/* clean up end of line */
1744558Seric 		fixcrlf(inp, TRUE);
1754549Seric 
1764713Seric 		/* echo command to transcript */
17755012Seric 		if (e->e_xfp != NULL)
17855012Seric 			fprintf(e->e_xfp, "<<< %s\n", inp);
1794713Seric 
18059060Seric 		if (e->e_id == NULL)
18159060Seric 			setproctitle("%s: %s", CurHostName, inp);
18259060Seric 		else
18359060Seric 			setproctitle("%s %s: %s", e->e_id, CurHostName, inp);
18459060Seric 
1854549Seric 		/* break off command */
18658050Seric 		for (p = inp; isascii(*p) && isspace(*p); p++)
1874549Seric 			continue;
18857232Seric 		cmd = cmdbuf;
18958050Seric 		while (*p != '\0' &&
19058050Seric 		       !(isascii(*p) && isspace(*p)) &&
19158050Seric 		       cmd < &cmdbuf[sizeof cmdbuf - 2])
19224981Seric 			*cmd++ = *p++;
19324981Seric 		*cmd = '\0';
1944549Seric 
19525691Seric 		/* throw away leading whitespace */
19658050Seric 		while (isascii(*p) && isspace(*p))
19725691Seric 			p++;
19825691Seric 
1994549Seric 		/* decode command */
2004549Seric 		for (c = CmdTab; c->cmdname != NULL; c++)
2014549Seric 		{
20233725Sbostic 			if (!strcasecmp(c->cmdname, cmdbuf))
2034549Seric 				break;
2044549Seric 		}
2054549Seric 
20651954Seric 		/* reset errors */
20751954Seric 		errno = 0;
20851954Seric 
2094549Seric 		/* process command */
2104549Seric 		switch (c->cmdcode)
2114549Seric 		{
2124976Seric 		  case CMDHELO:		/* hello -- introduce yourself */
21358323Seric 		  case CMDEHLO:		/* extended hello */
21458323Seric 			if (c->cmdcode == CMDEHLO)
21558323Seric 			{
21658323Seric 				protocol = "ESMTP";
21761093Seric 				SmtpPhase = "server EHLO";
21858323Seric 			}
21958323Seric 			else
22058323Seric 			{
22158323Seric 				protocol = "SMTP";
22261093Seric 				SmtpPhase = "server HELO";
22358323Seric 			}
22459016Seric 			sendinghost = newstr(p);
22558308Seric 			if (strcasecmp(p, RealHostName) != 0)
22611146Seric 			{
22758789Seric 				auth_warning(e, "Host %s claimed to be %s",
22858789Seric 					RealHostName, p);
22911146Seric 			}
23058957Seric 			p = macvalue('_', e);
23158957Seric 			if (p == NULL)
23259016Seric 				p = RealHostName;
23358323Seric 
23460210Seric 			gothello = TRUE;
23560210Seric 			if (c->cmdcode != CMDEHLO)
23660239Seric 			{
23760239Seric 				/* print old message and be done with it */
23860239Seric 				message("250 %s Hello %s, pleased to meet you",
23960239Seric 					MyHostName, p);
24060210Seric 				break;
24160239Seric 			}
24260239Seric 
24360239Seric 			/* print extended message and brag */
24460239Seric 			message("250-%s Hello %s, pleased to meet you",
24560239Seric 				MyHostName, p);
24658323Seric 			if (!bitset(PRIV_NOEXPN, PrivacyFlags))
24758323Seric 				message("250-EXPN");
24859271Seric 			if (MaxMessageSize > 0)
24959271Seric 				message("250-SIZE %ld", MaxMessageSize);
25059271Seric 			else
25159271Seric 				message("250-SIZE");
25258323Seric 			message("250 HELP");
2534976Seric 			break;
2544976Seric 
2554549Seric 		  case CMDMAIL:		/* mail -- designate sender */
25661093Seric 			SmtpPhase = "server MAIL";
25724943Seric 
2589314Seric 			/* check for validity of this command */
25958789Seric 			if (!gothello)
26058082Seric 			{
26158957Seric 				/* set sending host to our known value */
26259016Seric 				if (sendinghost == NULL)
26359016Seric 					sendinghost = RealHostName;
26458957Seric 
26558789Seric 				if (bitset(PRIV_NEEDMAILHELO, PrivacyFlags))
26658821Seric 				{
26758789Seric 					message("503 Polite people say HELO first");
26858821Seric 					break;
26958821Seric 				}
27058082Seric 			}
27158109Seric 			if (gotmail)
2724558Seric 			{
27358151Seric 				message("503 Sender already specified");
274*63843Seric 				if (InChild)
275*63843Seric 					finis();
2764558Seric 				break;
2774558Seric 			}
2789339Seric 			if (InChild)
2799339Seric 			{
28036230Skarels 				errno = 0;
28158151Seric 				syserr("503 Nested MAIL command: MAIL %s", p);
28258069Seric 				finis();
2839339Seric 			}
2849339Seric 
2859339Seric 			/* fork a subprocess to process this command */
28655012Seric 			if (runinchild("SMTP-MAIL", e) > 0)
2879339Seric 				break;
28863753Seric 			if (!gothello)
28963753Seric 			{
29063753Seric 				auth_warning(e,
29163753Seric 					"Host %s didn't use HELO protocol",
29263753Seric 					RealHostName);
29363753Seric 			}
29458323Seric 			if (protocol == NULL)
29558323Seric 				protocol = "SMTP";
29658323Seric 			define('r', protocol, e);
29759016Seric 			define('s', sendinghost, e);
29855012Seric 			initsys(e);
29959747Seric 			nrcpts = 0;
30057389Seric 			setproctitle("%s %s: %s", e->e_id, CurHostName, inp);
3019339Seric 
3029339Seric 			/* child -- go do the processing */
3034549Seric 			p = skipword(p, "from");
3044549Seric 			if (p == NULL)
3054549Seric 				break;
30657977Seric 			if (setjmp(TopFrame) > 0)
30758147Seric 			{
30858147Seric 				/* this failed -- undo work */
30958147Seric 				if (InChild)
31059058Seric 				{
31159058Seric 					QuickAbort = FALSE;
31259058Seric 					SuprErrs = TRUE;
31363787Seric 					e->e_flags &= ~EF_FATALERRS;
31458147Seric 					finis();
31559058Seric 				}
31657977Seric 				break;
31758147Seric 			}
31857977Seric 			QuickAbort = TRUE;
31958333Seric 
32058333Seric 			/* must parse sender first */
32158333Seric 			delimptr = NULL;
32258704Seric 			setsender(p, e, &delimptr, FALSE);
32358333Seric 			p = delimptr;
32458333Seric 			if (p != NULL && *p != '\0')
32558333Seric 				*p++ = '\0';
32658333Seric 
32758333Seric 			/* now parse ESMTP arguments */
32858333Seric 			msize = 0;
32958333Seric 			for (; p != NULL && *p != '\0'; p++)
33058333Seric 			{
33158333Seric 				char *kp;
33258333Seric 				char *vp;
33358333Seric 
33458333Seric 				/* locate the beginning of the keyword */
33558333Seric 				while (isascii(*p) && isspace(*p))
33658333Seric 					p++;
33758333Seric 				if (*p == '\0')
33858333Seric 					break;
33958333Seric 				kp = p;
34058333Seric 
34158333Seric 				/* skip to the value portion */
34258333Seric 				while (isascii(*p) && isalnum(*p) || *p == '-')
34358333Seric 					p++;
34458333Seric 				if (*p == '=')
34558333Seric 				{
34658333Seric 					*p++ = '\0';
34758333Seric 					vp = p;
34858333Seric 
34958333Seric 					/* skip to the end of the value */
35058333Seric 					while (*p != '\0' && *p != ' ' &&
35158333Seric 					       !(isascii(*p) && iscntrl(*p)) &&
35258333Seric 					       *p != '=')
35358333Seric 						p++;
35458333Seric 				}
35558333Seric 
35658333Seric 				if (*p != '\0')
35758333Seric 					*p++ = '\0';
35858333Seric 
35958333Seric 				if (tTd(19, 1))
36058333Seric 					printf("MAIL: got arg %s=%s\n", kp,
36158333Seric 						vp == NULL ? "<null>" : vp);
36258333Seric 
36358333Seric 				if (strcasecmp(kp, "size") == 0)
36458333Seric 				{
36559093Seric 					if (vp == NULL)
36658333Seric 					{
36758333Seric 						usrerr("501 SIZE requires a value");
36858333Seric 						/* NOTREACHED */
36958333Seric 					}
37058333Seric 					msize = atol(vp);
37158333Seric 				}
37259093Seric 				else if (strcasecmp(kp, "body") == 0)
37359093Seric 				{
37459093Seric 					if (vp == NULL)
37559093Seric 					{
37659093Seric 						usrerr("501 BODY requires a value");
37759093Seric 						/* NOTREACHED */
37859093Seric 					}
37959093Seric # ifdef MIME
38059093Seric 					if (strcasecmp(vp, "8bitmime") == 0)
38159093Seric 					{
38259093Seric 						e->e_bodytype = "8BITMIME";
38359709Seric 						SevenBit = FALSE;
38459093Seric 					}
38559093Seric 					else if (strcasecmp(vp, "7bit") == 0)
38659093Seric 					{
38759093Seric 						e->e_bodytype = "7BIT";
38859709Seric 						SevenBit = TRUE;
38959093Seric 					}
39059093Seric 					else
39159093Seric 					{
39259093Seric 						usrerr("501 Unknown BODY type %s",
39359093Seric 							vp);
39459093Seric 					}
39559093Seric # endif
39659093Seric 				}
39758333Seric 				else
39858333Seric 				{
39958333Seric 					usrerr("501 %s parameter unrecognized", kp);
40058333Seric 					/* NOTREACHED */
40158333Seric 				}
40258333Seric 			}
40359284Seric 
40459284Seric 			if (MaxMessageSize > 0 && msize > MaxMessageSize)
40559284Seric 			{
40659284Seric 				usrerr("552 Message size exceeds fixed maximum message size (%ld)",
40759284Seric 					MaxMessageSize);
40859284Seric 				/* NOTREACHED */
40959284Seric 			}
41058333Seric 
41158333Seric 			if (!enoughspace(msize))
41258333Seric 			{
41358333Seric 				message("452 Insufficient disk space; try again later");
41458333Seric 				break;
41558333Seric 			}
41658151Seric 			message("250 Sender ok");
41758147Seric 			gotmail = TRUE;
4184549Seric 			break;
4194549Seric 
4204976Seric 		  case CMDRCPT:		/* rcpt -- designate recipient */
42158850Seric 			if (!gotmail)
42258850Seric 			{
42358850Seric 				usrerr("503 Need MAIL before RCPT");
42458850Seric 				break;
42558850Seric 			}
42661093Seric 			SmtpPhase = "server RCPT";
42712612Seric 			if (setjmp(TopFrame) > 0)
42814785Seric 			{
42955012Seric 				e->e_flags &= ~EF_FATALERRS;
43012612Seric 				break;
43114785Seric 			}
43212612Seric 			QuickAbort = TRUE;
43351951Seric 			LogUsrErrs = TRUE;
43458093Seric 
43559699Seric 			if (e->e_sendmode != SM_DELIVER)
43659699Seric 				e->e_flags |= EF_VRFYONLY;
43758919Seric 
4384549Seric 			p = skipword(p, "to");
4394549Seric 			if (p == NULL)
4404549Seric 				break;
44158333Seric 			a = parseaddr(p, (ADDRESS *) NULL, 1, ' ', NULL, e);
44212612Seric 			if (a == NULL)
44312612Seric 				break;
44416886Seric 			a->q_flags |= QPRIMARY;
44555012Seric 			a = recipient(a, &e->e_sendqueue, e);
44612612Seric 			if (Errors != 0)
44712612Seric 				break;
44812612Seric 
44912612Seric 			/* no errors during parsing, but might be a duplicate */
45055012Seric 			e->e_to = p;
45112612Seric 			if (!bitset(QBADADDR, a->q_flags))
45259747Seric 			{
45358151Seric 				message("250 Recipient ok");
45459747Seric 				nrcpts++;
45559747Seric 			}
45612612Seric 			else
4574549Seric 			{
45812612Seric 				/* punt -- should keep message in ADDRESS.... */
45958151Seric 				message("550 Addressee unknown");
4604549Seric 			}
46155012Seric 			e->e_to = NULL;
4624549Seric 			break;
4634549Seric 
4644549Seric 		  case CMDDATA:		/* data -- text of mail */
46561093Seric 			SmtpPhase = "server DATA";
46658109Seric 			if (!gotmail)
4674549Seric 			{
46858151Seric 				message("503 Need MAIL command");
4694976Seric 				break;
4704549Seric 			}
47155012Seric 			else if (e->e_nrcpts <= 0)
4724549Seric 			{
47358151Seric 				message("503 Need RCPT (recipient)");
4744976Seric 				break;
4754549Seric 			}
4764976Seric 
47758929Seric 			/* check to see if we need to re-expand aliases */
47863787Seric 			/* also reset QBADADDR on already-diagnosted addrs */
47963787Seric 			doublequeue = FALSE;
48058929Seric 			for (a = e->e_sendqueue; a != NULL; a = a->q_next)
48158929Seric 			{
48258929Seric 				if (bitset(QVERIFIED, a->q_flags))
48363787Seric 				{
48463787Seric 					/* need to re-expand aliases */
48563787Seric 					doublequeue = TRUE;
48663787Seric 				}
48763787Seric 				if (bitset(QBADADDR, a->q_flags))
48863787Seric 				{
48963787Seric 					/* make this "go away" */
49063787Seric 					a->q_flags |= QDONTSEND;
49163787Seric 					a->q_flags &= ~QBADADDR;
49263787Seric 				}
49358929Seric 			}
49458929Seric 
4954976Seric 			/* collect the text of the message */
49624943Seric 			SmtpPhase = "collect";
497*63843Seric 			SuprErrs = TRUE;
49863787Seric 			collect(TRUE, doublequeue, e);
4994976Seric 			if (Errors != 0)
50059747Seric 				goto abortmessage;
5014976Seric 
5028238Seric 			/*
5038238Seric 			**  Arrange to send to everyone.
5048238Seric 			**	If sending to multiple people, mail back
5058238Seric 			**		errors rather than reporting directly.
5068238Seric 			**	In any case, don't mail back errors for
5078238Seric 			**		anything that has happened up to
5088238Seric 			**		now (the other end will do this).
50910197Seric 			**	Truncate our transcript -- the mail has gotten
51010197Seric 			**		to us successfully, and if we have
51110197Seric 			**		to mail this back, it will be easier
51210197Seric 			**		on the reader.
5138238Seric 			**	Then send to everyone.
5148238Seric 			**	Finally give a reply code.  If an error has
5158238Seric 			**		already been given, don't mail a
5168238Seric 			**		message back.
5179339Seric 			**	We goose error returns by clearing error bit.
5188238Seric 			*/
5198238Seric 
52024943Seric 			SmtpPhase = "delivery";
52163787Seric 			if (nrcpts != 1 && !doublequeue)
5229378Seric 			{
5239378Seric 				HoldErrs = TRUE;
52458734Seric 				e->e_errormode = EM_MAIL;
5259378Seric 			}
52655012Seric 			e->e_xfp = freopen(queuename(e, 'x'), "w", e->e_xfp);
52758714Seric 			id = e->e_id;
5284976Seric 
5294976Seric 			/* send to all recipients */
53063787Seric 			sendall(e, doublequeue ? SM_QUEUE : SM_DEFAULT);
53155012Seric 			e->e_to = NULL;
532*63843Seric 			SuprErrs = FALSE;
5334976Seric 
53423516Seric 			/* save statistics */
53555012Seric 			markstats(e, (ADDRESS *) NULL);
53623516Seric 
5378238Seric 			/* issue success if appropriate and reset */
5388238Seric 			if (Errors == 0 || HoldErrs)
53958855Seric 				message("250 %s Message accepted for delivery", id);
54059747Seric 
54159747Seric 			if (bitset(EF_FATALERRS, e->e_flags) && !HoldErrs)
54259730Seric 			{
54359730Seric 				/* avoid sending back an extra message */
54459730Seric 				e->e_flags &= ~EF_FATALERRS;
54559747Seric 				e->e_flags |= EF_CLRQUEUE;
54659730Seric 			}
5478238Seric 			else
54858919Seric 			{
54959747Seric 				/* from now on, we have to operate silently */
55059747Seric 				HoldErrs = TRUE;
55159747Seric 				e->e_errormode = EM_MAIL;
55259747Seric 
55359730Seric 				/* if we just queued, poke it */
55463787Seric 				if (doublequeue && e->e_sendmode != SM_QUEUE)
55559730Seric 				{
55659730Seric 					unlockqueue(e);
55759730Seric 					dowork(id, TRUE, TRUE, e);
55859730Seric 					e->e_id = NULL;
55959730Seric 				}
56058919Seric 			}
56158883Seric 
56259747Seric   abortmessage:
5639339Seric 			/* if in a child, pop back to our parent */
5649339Seric 			if (InChild)
5659339Seric 				finis();
56624943Seric 
56724943Seric 			/* clean up a bit */
56858109Seric 			gotmail = FALSE;
56955012Seric 			dropenvelope(e);
57058179Seric 			CurEnv = e = newenvelope(e, CurEnv);
57155012Seric 			e->e_flags = BlankEnvelope.e_flags;
5724549Seric 			break;
5734549Seric 
5744549Seric 		  case CMDRSET:		/* rset -- reset state */
57558151Seric 			message("250 Reset state");
5769339Seric 			if (InChild)
5779339Seric 				finis();
57858109Seric 
57958109Seric 			/* clean up a bit */
58058109Seric 			gotmail = FALSE;
58158109Seric 			dropenvelope(e);
58258179Seric 			CurEnv = e = newenvelope(e, CurEnv);
5839339Seric 			break;
5844549Seric 
5854549Seric 		  case CMDVRFY:		/* vrfy -- verify address */
58658092Seric 		  case CMDEXPN:		/* expn -- expand address */
58758092Seric 			vrfy = c->cmdcode == CMDVRFY;
58858092Seric 			if (bitset(vrfy ? PRIV_NOVRFY : PRIV_NOEXPN,
58958092Seric 						PrivacyFlags))
59058082Seric 			{
59158412Seric 				if (vrfy)
59258412Seric 					message("252 Who's to say?");
59358412Seric 				else
59458412Seric 					message("502 That's none of your business");
59558082Seric 				break;
59658082Seric 			}
59758082Seric 			else if (!gothello &&
59858092Seric 				 bitset(vrfy ? PRIV_NEEDVRFYHELO : PRIV_NEEDEXPNHELO,
59958092Seric 						PrivacyFlags))
60058082Seric 			{
60158151Seric 				message("503 I demand that you introduce yourself first");
60258082Seric 				break;
60358082Seric 			}
60458092Seric 			if (runinchild(vrfy ? "SMTP-VRFY" : "SMTP-EXPN", e) > 0)
6059339Seric 				break;
60655173Seric #ifdef LOG
60758020Seric 			if (LogLevel > 5)
60855173Seric 				syslog(LOG_INFO, "%s: %s", CurHostName, inp);
60955173Seric #endif
6105003Seric 			vrfyqueue = NULL;
6117762Seric 			QuickAbort = TRUE;
61258092Seric 			if (vrfy)
61358092Seric 				e->e_flags |= EF_VRFYONLY;
61462373Seric 			while (*p != '\0' && isascii(*p) && isspace(*p))
61562373Seric 				*p++;
61662373Seric 			if (*p == '\0')
61762373Seric 			{
61862373Seric 				message("501 Argument required");
61962373Seric 				Errors++;
62062373Seric 			}
62162373Seric 			else
62262373Seric 			{
62362373Seric 				(void) sendtolist(p, (ADDRESS *) NULL,
62462373Seric 						  &vrfyqueue, e);
62562373Seric 			}
6267762Seric 			if (Errors != 0)
6279339Seric 			{
6289339Seric 				if (InChild)
6299339Seric 					finis();
6307762Seric 				break;
6319339Seric 			}
63262373Seric 			if (vrfyqueue == NULL)
63362373Seric 			{
63462373Seric 				message("554 Nothing to %s", vrfy ? "VRFY" : "EXPN");
63562373Seric 			}
6365003Seric 			while (vrfyqueue != NULL)
6375003Seric 			{
6385003Seric 				register ADDRESS *a = vrfyqueue->q_next;
6395003Seric 
6407685Seric 				while (a != NULL && bitset(QDONTSEND|QBADADDR, a->q_flags))
6415003Seric 					a = a->q_next;
6425003Seric 
6437685Seric 				if (!bitset(QDONTSEND|QBADADDR, vrfyqueue->q_flags))
64458151Seric 					printvrfyaddr(vrfyqueue, a == NULL);
6455003Seric 				else if (a == NULL)
64658151Seric 					message("554 Self destructive alias loop");
6475003Seric 				vrfyqueue = a;
6485003Seric 			}
6499339Seric 			if (InChild)
6509339Seric 				finis();
6514549Seric 			break;
6524549Seric 
6534549Seric 		  case CMDHELP:		/* help -- give user info */
6544577Seric 			help(p);
6554549Seric 			break;
6564549Seric 
6574549Seric 		  case CMDNOOP:		/* noop -- do nothing */
65858151Seric 			message("200 OK");
6594549Seric 			break;
6604549Seric 
6614549Seric 		  case CMDQUIT:		/* quit -- leave mail */
66258151Seric 			message("221 %s closing connection", MyHostName);
66361051Seric 
66461051Seric 			/* avoid future 050 messages */
66561051Seric 			Verbose = FALSE;
66661051Seric 
6679339Seric 			if (InChild)
6689339Seric 				ExitStat = EX_QUIT;
6694549Seric 			finis();
6704549Seric 
6718544Seric 		  case CMDVERB:		/* set verbose mode */
67259957Seric 			if (bitset(PRIV_NOEXPN, PrivacyFlags))
67359957Seric 			{
67459957Seric 				/* this would give out the same info */
67559957Seric 				message("502 Verbose unavailable");
67659957Seric 				break;
67759957Seric 			}
6788544Seric 			Verbose = TRUE;
67958734Seric 			e->e_sendmode = SM_DELIVER;
68059957Seric 			message("250 Verbose mode");
6818544Seric 			break;
6828544Seric 
6839314Seric 		  case CMDONEX:		/* doing one transaction only */
6849378Seric 			OneXact = TRUE;
68559957Seric 			message("250 Only one transaction");
6869314Seric 			break;
6879314Seric 
68836230Skarels # ifdef SMTPDEBUG
6899339Seric 		  case CMDDBGQSHOW:	/* show queues */
6906907Seric 			printf("Send Queue=");
69155012Seric 			printaddr(e->e_sendqueue, TRUE);
6925003Seric 			break;
6937275Seric 
6947275Seric 		  case CMDDBGDEBUG:	/* set debug mode */
6957676Seric 			tTsetup(tTdvect, sizeof tTdvect, "0-99.1");
6967676Seric 			tTflag(p);
69758151Seric 			message("200 Debug set");
6987275Seric 			break;
6997275Seric 
70036230Skarels # else /* not SMTPDEBUG */
70124945Seric 
70236230Skarels 		  case CMDDBGQSHOW:	/* show queues */
70336230Skarels 		  case CMDDBGDEBUG:	/* set debug mode */
70436233Skarels # ifdef LOG
70558308Seric 			if (LogLevel > 0)
70636230Skarels 				syslog(LOG_NOTICE,
70758020Seric 				    "\"%s\" command from %s (%s)",
70836230Skarels 				    c->cmdname, RealHostName,
70958755Seric 				    anynet_ntoa(&RealHostAddr));
71036233Skarels # endif
71136230Skarels 			/* FALL THROUGH */
71236230Skarels # endif /* SMTPDEBUG */
71336230Skarels 
7144549Seric 		  case CMDERROR:	/* unknown command */
71558151Seric 			message("500 Command unrecognized");
7164549Seric 			break;
7174549Seric 
7184549Seric 		  default:
71936230Skarels 			errno = 0;
72058151Seric 			syserr("500 smtp: unknown code %d", c->cmdcode);
7214549Seric 			break;
7224549Seric 		}
7234549Seric 	}
7244549Seric }
7254549Seric /*
7264549Seric **  SKIPWORD -- skip a fixed word.
7274549Seric **
7284549Seric **	Parameters:
7294549Seric **		p -- place to start looking.
7304549Seric **		w -- word to skip.
7314549Seric **
7324549Seric **	Returns:
7334549Seric **		p following w.
7344549Seric **		NULL on error.
7354549Seric **
7364549Seric **	Side Effects:
7374549Seric **		clobbers the p data area.
7384549Seric */
7394549Seric 
7404549Seric static char *
7414549Seric skipword(p, w)
7424549Seric 	register char *p;
7434549Seric 	char *w;
7444549Seric {
7454549Seric 	register char *q;
7464549Seric 
7474549Seric 	/* find beginning of word */
74858050Seric 	while (isascii(*p) && isspace(*p))
7494549Seric 		p++;
7504549Seric 	q = p;
7514549Seric 
7524549Seric 	/* find end of word */
75358050Seric 	while (*p != '\0' && *p != ':' && !(isascii(*p) && isspace(*p)))
7544549Seric 		p++;
75558050Seric 	while (isascii(*p) && isspace(*p))
7564549Seric 		*p++ = '\0';
7574549Seric 	if (*p != ':')
7584549Seric 	{
7594549Seric 	  syntax:
76062373Seric 		message("501 Syntax error in parameters");
7614549Seric 		Errors++;
7624549Seric 		return (NULL);
7634549Seric 	}
7644549Seric 	*p++ = '\0';
76558050Seric 	while (isascii(*p) && isspace(*p))
7664549Seric 		p++;
7674549Seric 
76862373Seric 	if (*p == '\0')
76962373Seric 		goto syntax;
77062373Seric 
7714549Seric 	/* see if the input word matches desired word */
77233725Sbostic 	if (strcasecmp(q, w))
7734549Seric 		goto syntax;
7744549Seric 
7754549Seric 	return (p);
7764549Seric }
7774577Seric /*
77858151Seric **  PRINTVRFYADDR -- print an entry in the verify queue
77958151Seric **
78058151Seric **	Parameters:
78158151Seric **		a -- the address to print
78258151Seric **		last -- set if this is the last one.
78358151Seric **
78458151Seric **	Returns:
78558151Seric **		none.
78658151Seric **
78758151Seric **	Side Effects:
78858151Seric **		Prints the appropriate 250 codes.
78958151Seric */
79058151Seric 
79158151Seric printvrfyaddr(a, last)
79258151Seric 	register ADDRESS *a;
79358151Seric 	bool last;
79458151Seric {
79558151Seric 	char fmtbuf[20];
79658151Seric 
79758151Seric 	strcpy(fmtbuf, "250");
79858151Seric 	fmtbuf[3] = last ? ' ' : '-';
79958151Seric 
80059746Seric 	if (a->q_fullname == NULL)
80159746Seric 	{
80259746Seric 		if (strchr(a->q_user, '@') == NULL)
80359746Seric 			strcpy(&fmtbuf[4], "<%s@%s>");
80459746Seric 		else
80559746Seric 			strcpy(&fmtbuf[4], "<%s>");
80659746Seric 		message(fmtbuf, a->q_user, MyHostName);
80759746Seric 	}
80858151Seric 	else
80958151Seric 	{
81059746Seric 		if (strchr(a->q_user, '@') == NULL)
81159746Seric 			strcpy(&fmtbuf[4], "%s <%s@%s>");
81259746Seric 		else
81359746Seric 			strcpy(&fmtbuf[4], "%s <%s>");
81459746Seric 		message(fmtbuf, a->q_fullname, a->q_user, MyHostName);
81558151Seric 	}
81658151Seric }
81758151Seric /*
8184577Seric **  HELP -- implement the HELP command.
8194577Seric **
8204577Seric **	Parameters:
8214577Seric **		topic -- the topic we want help for.
8224577Seric **
8234577Seric **	Returns:
8244577Seric **		none.
8254577Seric **
8264577Seric **	Side Effects:
8274577Seric **		outputs the help file to message output.
8284577Seric */
8294577Seric 
8304577Seric help(topic)
8314577Seric 	char *topic;
8324577Seric {
8334577Seric 	register FILE *hf;
8344577Seric 	int len;
8354577Seric 	char buf[MAXLINE];
8364577Seric 	bool noinfo;
8374577Seric 
8388269Seric 	if (HelpFile == NULL || (hf = fopen(HelpFile, "r")) == NULL)
8394577Seric 	{
8404577Seric 		/* no help */
84111931Seric 		errno = 0;
84258151Seric 		message("502 HELP not implemented");
8434577Seric 		return;
8444577Seric 	}
8454577Seric 
84649669Seric 	if (topic == NULL || *topic == '\0')
84749669Seric 		topic = "smtp";
84849669Seric 	else
84949669Seric 		makelower(topic);
85049669Seric 
8514577Seric 	len = strlen(topic);
8524577Seric 	noinfo = TRUE;
8534577Seric 
8544577Seric 	while (fgets(buf, sizeof buf, hf) != NULL)
8554577Seric 	{
8564577Seric 		if (strncmp(buf, topic, len) == 0)
8574577Seric 		{
8584577Seric 			register char *p;
8594577Seric 
86056795Seric 			p = strchr(buf, '\t');
8614577Seric 			if (p == NULL)
8624577Seric 				p = buf;
8634577Seric 			else
8644577Seric 				p++;
8654577Seric 			fixcrlf(p, TRUE);
86658151Seric 			message("214-%s", p);
8674577Seric 			noinfo = FALSE;
8684577Seric 		}
8694577Seric 	}
8704577Seric 
8714577Seric 	if (noinfo)
87258151Seric 		message("504 HELP topic unknown");
8734577Seric 	else
87458151Seric 		message("214 End of HELP info");
8754628Seric 	(void) fclose(hf);
8764577Seric }
8778544Seric /*
8789339Seric **  RUNINCHILD -- return twice -- once in the child, then in the parent again
8799339Seric **
8809339Seric **	Parameters:
8819339Seric **		label -- a string used in error messages
8829339Seric **
8839339Seric **	Returns:
8849339Seric **		zero in the child
8859339Seric **		one in the parent
8869339Seric **
8879339Seric **	Side Effects:
8889339Seric **		none.
8899339Seric */
8908544Seric 
89155012Seric runinchild(label, e)
8929339Seric 	char *label;
89355012Seric 	register ENVELOPE *e;
8949339Seric {
8959339Seric 	int childpid;
8969339Seric 
89716158Seric 	if (!OneXact)
8989339Seric 	{
89916158Seric 		childpid = dofork();
90016158Seric 		if (childpid < 0)
90116158Seric 		{
90216158Seric 			syserr("%s: cannot fork", label);
90316158Seric 			return (1);
90416158Seric 		}
90516158Seric 		if (childpid > 0)
90616158Seric 		{
90716158Seric 			auto int st;
9089339Seric 
90916158Seric 			/* parent -- wait for child to complete */
91061093Seric 			setproctitle("server %s child wait", CurHostName);
91116158Seric 			st = waitfor(childpid);
91216158Seric 			if (st == -1)
91316158Seric 				syserr("%s: lost child", label);
9149339Seric 
91516158Seric 			/* if we exited on a QUIT command, complete the process */
91616158Seric 			if (st == (EX_QUIT << 8))
91716158Seric 				finis();
9189339Seric 
91916158Seric 			return (1);
92016158Seric 		}
92116158Seric 		else
92216158Seric 		{
92316158Seric 			/* child */
92416158Seric 			InChild = TRUE;
92525050Seric 			QuickAbort = FALSE;
92655012Seric 			clearenvelope(e, FALSE);
92716158Seric 		}
9289339Seric 	}
92915256Seric 
93016158Seric 	/* open alias database */
93160537Seric 	initmaps(FALSE, e);
93216158Seric 
93316158Seric 	return (0);
9349339Seric }
9359339Seric 
93656795Seric # endif /* SMTP */
937