122712Sdist /*
234921Sbostic  * Copyright (c) 1983 Eric P. Allman
333731Sbostic  * Copyright (c) 1988 Regents of the University of California.
433731Sbostic  * All rights reserved.
533731Sbostic  *
642829Sbostic  * %sccs.include.redist.c%
733731Sbostic  */
822712Sdist 
933731Sbostic # include "sendmail.h"
1022712Sdist 
1133731Sbostic #ifndef lint
1233731Sbostic #ifdef SMTP
13*60239Seric static char sccsid[] = "@(#)srvrsmtp.c	6.55 (Berkeley) 05/23/93 (with SMTP)";
1433731Sbostic #else
15*60239Seric static char sccsid[] = "@(#)srvrsmtp.c	6.55 (Berkeley) 05/23/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 */
1118544Seric 	char inp[MAXLINE];
11257232Seric 	char cmdbuf[MAXLINE];
1137124Seric 	extern char Version[];
11411151Seric 	extern char *macvalue();
11512612Seric 	extern ADDRESS *recipient();
11624943Seric 	extern ENVELOPE BlankEnvelope;
11724943Seric 	extern ENVELOPE *newenvelope();
11858755Seric 	extern char *anynet_ntoa();
1194549Seric 
12059066Seric 	if (fileno(OutChannel) != fileno(stdout))
1217363Seric 	{
1227363Seric 		/* arrange for debugging output to go to remote host */
12359066Seric 		(void) dup2(fileno(OutChannel), fileno(stdout));
1247363Seric 	}
12555012Seric 	settime(e);
12657642Seric 	CurHostName = RealHostName;
12759060Seric 	setproctitle("srvrsmtp %s startup", CurHostName);
12858050Seric 	expand("\201e", inp, &inp[sizeof inp], e);
12960210Seric 	message("220-%s", inp);
13060210Seric 	message("220 ESMTP spoken here");
13158330Seric 	protocol = NULL;
13259016Seric 	sendinghost = macvalue('s', e);
13358082Seric 	gothello = FALSE;
13458330Seric 	gotmail = FALSE;
1354549Seric 	for (;;)
1364549Seric 	{
13712612Seric 		/* arrange for backout */
13812612Seric 		if (setjmp(TopFrame) > 0 && InChild)
13959058Seric 		{
14059058Seric 			QuickAbort = FALSE;
14159058Seric 			SuprErrs = TRUE;
14212612Seric 			finis();
14359058Seric 		}
14412612Seric 		QuickAbort = FALSE;
14512612Seric 		HoldErrs = FALSE;
14651951Seric 		LogUsrErrs = FALSE;
14758092Seric 		e->e_flags &= ~EF_VRFYONLY;
14812612Seric 
1497356Seric 		/* setup for the read */
15055012Seric 		e->e_to = NULL;
1514577Seric 		Errors = 0;
1527275Seric 		(void) fflush(stdout);
1537356Seric 
1547356Seric 		/* read the input line */
15559060Seric 		SmtpPhase = "srvrsmtp cmd read";
15659060Seric 		setproctitle("srvrsmtp %s cmd read", CurHostName);
15758109Seric 		p = sfgets(inp, sizeof inp, InChannel, TimeOuts.to_nextcommand);
1587356Seric 
1597685Seric 		/* handle errors */
1607356Seric 		if (p == NULL)
1617356Seric 		{
1624549Seric 			/* end of file, just die */
16358151Seric 			message("421 %s Lost input channel from %s",
16425050Seric 				MyHostName, CurHostName);
16555464Seric #ifdef LOG
16658020Seric 			if (LogLevel > 1)
16755464Seric 				syslog(LOG_NOTICE, "lost input channel from %s",
16855464Seric 					CurHostName);
16955464Seric #endif
17058069Seric 			if (InChild)
17158069Seric 				ExitStat = EX_QUIT;
1724549Seric 			finis();
1734549Seric 		}
1744549Seric 
1754549Seric 		/* clean up end of line */
1764558Seric 		fixcrlf(inp, TRUE);
1774549Seric 
1784713Seric 		/* echo command to transcript */
17955012Seric 		if (e->e_xfp != NULL)
18055012Seric 			fprintf(e->e_xfp, "<<< %s\n", inp);
1814713Seric 
18259060Seric 		if (e->e_id == NULL)
18359060Seric 			setproctitle("%s: %s", CurHostName, inp);
18459060Seric 		else
18559060Seric 			setproctitle("%s %s: %s", e->e_id, CurHostName, inp);
18659060Seric 
1874549Seric 		/* break off command */
18858050Seric 		for (p = inp; isascii(*p) && isspace(*p); p++)
1894549Seric 			continue;
19057232Seric 		cmd = cmdbuf;
19158050Seric 		while (*p != '\0' &&
19258050Seric 		       !(isascii(*p) && isspace(*p)) &&
19358050Seric 		       cmd < &cmdbuf[sizeof cmdbuf - 2])
19424981Seric 			*cmd++ = *p++;
19524981Seric 		*cmd = '\0';
1964549Seric 
19725691Seric 		/* throw away leading whitespace */
19858050Seric 		while (isascii(*p) && isspace(*p))
19925691Seric 			p++;
20025691Seric 
2014549Seric 		/* decode command */
2024549Seric 		for (c = CmdTab; c->cmdname != NULL; c++)
2034549Seric 		{
20433725Sbostic 			if (!strcasecmp(c->cmdname, cmdbuf))
2054549Seric 				break;
2064549Seric 		}
2074549Seric 
20851954Seric 		/* reset errors */
20951954Seric 		errno = 0;
21051954Seric 
2114549Seric 		/* process command */
2124549Seric 		switch (c->cmdcode)
2134549Seric 		{
2144976Seric 		  case CMDHELO:		/* hello -- introduce yourself */
21558323Seric 		  case CMDEHLO:		/* extended hello */
21658323Seric 			if (c->cmdcode == CMDEHLO)
21758323Seric 			{
21858323Seric 				protocol = "ESMTP";
21958323Seric 				SmtpPhase = "EHLO";
22058323Seric 			}
22158323Seric 			else
22258323Seric 			{
22358323Seric 				protocol = "SMTP";
22458323Seric 				SmtpPhase = "HELO";
22558323Seric 			}
22659016Seric 			sendinghost = newstr(p);
22758308Seric 			if (strcasecmp(p, RealHostName) != 0)
22811146Seric 			{
22958789Seric 				auth_warning(e, "Host %s claimed to be %s",
23058789Seric 					RealHostName, p);
23111146Seric 			}
23258957Seric 			p = macvalue('_', e);
23358957Seric 			if (p == NULL)
23459016Seric 				p = RealHostName;
23558323Seric 
23660210Seric 			gothello = TRUE;
23760210Seric 			if (c->cmdcode != CMDEHLO)
238*60239Seric 			{
239*60239Seric 				/* print old message and be done with it */
240*60239Seric 				message("250 %s Hello %s, pleased to meet you",
241*60239Seric 					MyHostName, p);
24260210Seric 				break;
243*60239Seric 			}
244*60239Seric 
245*60239Seric 			/* print extended message and brag */
246*60239Seric 			message("250-%s Hello %s, pleased to meet you",
247*60239Seric 				MyHostName, p);
24858323Seric 			if (!bitset(PRIV_NOEXPN, PrivacyFlags))
24958323Seric 				message("250-EXPN");
25059271Seric 			if (MaxMessageSize > 0)
25159271Seric 				message("250-SIZE %ld", MaxMessageSize);
25259271Seric 			else
25359271Seric 				message("250-SIZE");
25458323Seric 			message("250 HELP");
2554976Seric 			break;
2564976Seric 
2574549Seric 		  case CMDMAIL:		/* mail -- designate sender */
25824943Seric 			SmtpPhase = "MAIL";
25924943Seric 
2609314Seric 			/* check for validity of this command */
26158789Seric 			if (!gothello)
26258082Seric 			{
26358957Seric 				/* set sending host to our known value */
26459016Seric 				if (sendinghost == NULL)
26559016Seric 					sendinghost = RealHostName;
26658957Seric 
26758789Seric 				if (bitset(PRIV_NEEDMAILHELO, PrivacyFlags))
26858821Seric 				{
26958789Seric 					message("503 Polite people say HELO first");
27058821Seric 					break;
27158821Seric 				}
27258789Seric 				else
27358821Seric 				{
27458789Seric 					auth_warning(e,
27558789Seric 						"Host %s didn't use HELO protocol",
27658789Seric 						RealHostName);
27758821Seric 				}
27858082Seric 			}
27958109Seric 			if (gotmail)
2804558Seric 			{
28158151Seric 				message("503 Sender already specified");
2824558Seric 				break;
2834558Seric 			}
2849339Seric 			if (InChild)
2859339Seric 			{
28636230Skarels 				errno = 0;
28758151Seric 				syserr("503 Nested MAIL command: MAIL %s", p);
28858069Seric 				finis();
2899339Seric 			}
2909339Seric 
2919339Seric 			/* fork a subprocess to process this command */
29255012Seric 			if (runinchild("SMTP-MAIL", e) > 0)
2939339Seric 				break;
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;
31358147Seric 					finis();
31459058Seric 				}
31557977Seric 				break;
31658147Seric 			}
31757977Seric 			QuickAbort = TRUE;
31858333Seric 
31958333Seric 			/* must parse sender first */
32058333Seric 			delimptr = NULL;
32158704Seric 			setsender(p, e, &delimptr, FALSE);
32258333Seric 			p = delimptr;
32358333Seric 			if (p != NULL && *p != '\0')
32458333Seric 				*p++ = '\0';
32558333Seric 
32658333Seric 			/* now parse ESMTP arguments */
32758333Seric 			msize = 0;
32858333Seric 			for (; p != NULL && *p != '\0'; p++)
32958333Seric 			{
33058333Seric 				char *kp;
33158333Seric 				char *vp;
33258333Seric 
33358333Seric 				/* locate the beginning of the keyword */
33458333Seric 				while (isascii(*p) && isspace(*p))
33558333Seric 					p++;
33658333Seric 				if (*p == '\0')
33758333Seric 					break;
33858333Seric 				kp = p;
33958333Seric 
34058333Seric 				/* skip to the value portion */
34158333Seric 				while (isascii(*p) && isalnum(*p) || *p == '-')
34258333Seric 					p++;
34358333Seric 				if (*p == '=')
34458333Seric 				{
34558333Seric 					*p++ = '\0';
34658333Seric 					vp = p;
34758333Seric 
34858333Seric 					/* skip to the end of the value */
34958333Seric 					while (*p != '\0' && *p != ' ' &&
35058333Seric 					       !(isascii(*p) && iscntrl(*p)) &&
35158333Seric 					       *p != '=')
35258333Seric 						p++;
35358333Seric 				}
35458333Seric 
35558333Seric 				if (*p != '\0')
35658333Seric 					*p++ = '\0';
35758333Seric 
35858333Seric 				if (tTd(19, 1))
35958333Seric 					printf("MAIL: got arg %s=%s\n", kp,
36058333Seric 						vp == NULL ? "<null>" : vp);
36158333Seric 
36258333Seric 				if (strcasecmp(kp, "size") == 0)
36358333Seric 				{
36459093Seric 					if (vp == NULL)
36558333Seric 					{
36658333Seric 						usrerr("501 SIZE requires a value");
36758333Seric 						/* NOTREACHED */
36858333Seric 					}
36958333Seric 					msize = atol(vp);
37058333Seric 				}
37159093Seric 				else if (strcasecmp(kp, "body") == 0)
37259093Seric 				{
37359093Seric 					if (vp == NULL)
37459093Seric 					{
37559093Seric 						usrerr("501 BODY requires a value");
37659093Seric 						/* NOTREACHED */
37759093Seric 					}
37859093Seric # ifdef MIME
37959093Seric 					if (strcasecmp(vp, "8bitmime") == 0)
38059093Seric 					{
38159093Seric 						e->e_bodytype = "8BITMIME";
38259709Seric 						SevenBit = FALSE;
38359093Seric 					}
38459093Seric 					else if (strcasecmp(vp, "7bit") == 0)
38559093Seric 					{
38659093Seric 						e->e_bodytype = "7BIT";
38759709Seric 						SevenBit = TRUE;
38859093Seric 					}
38959093Seric 					else
39059093Seric 					{
39159093Seric 						usrerr("501 Unknown BODY type %s",
39259093Seric 							vp);
39359093Seric 					}
39459093Seric # endif
39559093Seric 				}
39658333Seric 				else
39758333Seric 				{
39858333Seric 					usrerr("501 %s parameter unrecognized", kp);
39958333Seric 					/* NOTREACHED */
40058333Seric 				}
40158333Seric 			}
40259284Seric 
40359284Seric 			if (MaxMessageSize > 0 && msize > MaxMessageSize)
40459284Seric 			{
40559284Seric 				usrerr("552 Message size exceeds fixed maximum message size (%ld)",
40659284Seric 					MaxMessageSize);
40759284Seric 				/* NOTREACHED */
40859284Seric 			}
40958333Seric 
41058333Seric 			if (!enoughspace(msize))
41158333Seric 			{
41258333Seric 				message("452 Insufficient disk space; try again later");
41358333Seric 				break;
41458333Seric 			}
41558151Seric 			message("250 Sender ok");
41658147Seric 			gotmail = TRUE;
4174549Seric 			break;
4184549Seric 
4194976Seric 		  case CMDRCPT:		/* rcpt -- designate recipient */
42058850Seric 			if (!gotmail)
42158850Seric 			{
42258850Seric 				usrerr("503 Need MAIL before RCPT");
42358850Seric 				break;
42458850Seric 			}
42524943Seric 			SmtpPhase = "RCPT";
42612612Seric 			if (setjmp(TopFrame) > 0)
42714785Seric 			{
42855012Seric 				e->e_flags &= ~EF_FATALERRS;
42912612Seric 				break;
43014785Seric 			}
43112612Seric 			QuickAbort = TRUE;
43251951Seric 			LogUsrErrs = TRUE;
43358093Seric 
43459699Seric 			if (e->e_sendmode != SM_DELIVER)
43559699Seric 				e->e_flags |= EF_VRFYONLY;
43658919Seric 
4374549Seric 			p = skipword(p, "to");
4384549Seric 			if (p == NULL)
4394549Seric 				break;
44058333Seric 			a = parseaddr(p, (ADDRESS *) NULL, 1, ' ', NULL, e);
44112612Seric 			if (a == NULL)
44212612Seric 				break;
44316886Seric 			a->q_flags |= QPRIMARY;
44455012Seric 			a = recipient(a, &e->e_sendqueue, e);
44512612Seric 			if (Errors != 0)
44612612Seric 				break;
44712612Seric 
44812612Seric 			/* no errors during parsing, but might be a duplicate */
44955012Seric 			e->e_to = p;
45012612Seric 			if (!bitset(QBADADDR, a->q_flags))
45159747Seric 			{
45258151Seric 				message("250 Recipient ok");
45359747Seric 				nrcpts++;
45459747Seric 			}
45512612Seric 			else
4564549Seric 			{
45712612Seric 				/* punt -- should keep message in ADDRESS.... */
45858151Seric 				message("550 Addressee unknown");
4594549Seric 			}
46055012Seric 			e->e_to = NULL;
4614549Seric 			break;
4624549Seric 
4634549Seric 		  case CMDDATA:		/* data -- text of mail */
46424943Seric 			SmtpPhase = "DATA";
46558109Seric 			if (!gotmail)
4664549Seric 			{
46758151Seric 				message("503 Need MAIL command");
4684976Seric 				break;
4694549Seric 			}
47055012Seric 			else if (e->e_nrcpts <= 0)
4714549Seric 			{
47258151Seric 				message("503 Need RCPT (recipient)");
4734976Seric 				break;
4744549Seric 			}
4754976Seric 
47658929Seric 			/* check to see if we need to re-expand aliases */
47758929Seric 			for (a = e->e_sendqueue; a != NULL; a = a->q_next)
47858929Seric 			{
47958929Seric 				if (bitset(QVERIFIED, a->q_flags))
48058929Seric 					break;
48158929Seric 			}
48258929Seric 
4834976Seric 			/* collect the text of the message */
48424943Seric 			SmtpPhase = "collect";
48558929Seric 			collect(TRUE, a != NULL, e);
48659730Seric 			e->e_flags &= ~EF_FATALERRS;
4874976Seric 			if (Errors != 0)
48859747Seric 				goto abortmessage;
4894976Seric 
4908238Seric 			/*
4918238Seric 			**  Arrange to send to everyone.
4928238Seric 			**	If sending to multiple people, mail back
4938238Seric 			**		errors rather than reporting directly.
4948238Seric 			**	In any case, don't mail back errors for
4958238Seric 			**		anything that has happened up to
4968238Seric 			**		now (the other end will do this).
49710197Seric 			**	Truncate our transcript -- the mail has gotten
49810197Seric 			**		to us successfully, and if we have
49910197Seric 			**		to mail this back, it will be easier
50010197Seric 			**		on the reader.
5018238Seric 			**	Then send to everyone.
5028238Seric 			**	Finally give a reply code.  If an error has
5038238Seric 			**		already been given, don't mail a
5048238Seric 			**		message back.
5059339Seric 			**	We goose error returns by clearing error bit.
5068238Seric 			*/
5078238Seric 
50824943Seric 			SmtpPhase = "delivery";
50959747Seric 			if (nrcpts != 1 && a == NULL)
5109378Seric 			{
5119378Seric 				HoldErrs = TRUE;
51258734Seric 				e->e_errormode = EM_MAIL;
5139378Seric 			}
51455012Seric 			e->e_xfp = freopen(queuename(e, 'x'), "w", e->e_xfp);
51558714Seric 			id = e->e_id;
5164976Seric 
5174976Seric 			/* send to all recipients */
51858919Seric 			sendall(e, a == NULL ? SM_DEFAULT : SM_QUEUE);
51955012Seric 			e->e_to = NULL;
5204976Seric 
52123516Seric 			/* save statistics */
52255012Seric 			markstats(e, (ADDRESS *) NULL);
52323516Seric 
5248238Seric 			/* issue success if appropriate and reset */
5258238Seric 			if (Errors == 0 || HoldErrs)
52658855Seric 				message("250 %s Message accepted for delivery", id);
52759747Seric 
52859747Seric 			if (bitset(EF_FATALERRS, e->e_flags) && !HoldErrs)
52959730Seric 			{
53059730Seric 				/* avoid sending back an extra message */
53159730Seric 				e->e_flags &= ~EF_FATALERRS;
53259747Seric 				e->e_flags |= EF_CLRQUEUE;
53359730Seric 			}
5348238Seric 			else
53558919Seric 			{
53659747Seric 				/* from now on, we have to operate silently */
53759747Seric 				HoldErrs = TRUE;
53859747Seric 				e->e_errormode = EM_MAIL;
53959747Seric 
54059730Seric 				/* if we just queued, poke it */
54159730Seric 				if (a != NULL && e->e_sendmode != SM_QUEUE)
54259730Seric 				{
54359730Seric 					unlockqueue(e);
54459730Seric 					dowork(id, TRUE, TRUE, e);
54559730Seric 					e->e_id = NULL;
54659730Seric 				}
54758919Seric 			}
54858883Seric 
54959747Seric   abortmessage:
5509339Seric 			/* if in a child, pop back to our parent */
5519339Seric 			if (InChild)
5529339Seric 				finis();
55324943Seric 
55424943Seric 			/* clean up a bit */
55558109Seric 			gotmail = FALSE;
55655012Seric 			dropenvelope(e);
55758179Seric 			CurEnv = e = newenvelope(e, CurEnv);
55855012Seric 			e->e_flags = BlankEnvelope.e_flags;
5594549Seric 			break;
5604549Seric 
5614549Seric 		  case CMDRSET:		/* rset -- reset state */
56258151Seric 			message("250 Reset state");
5639339Seric 			if (InChild)
5649339Seric 				finis();
56558109Seric 
56658109Seric 			/* clean up a bit */
56758109Seric 			gotmail = FALSE;
56858109Seric 			dropenvelope(e);
56958179Seric 			CurEnv = e = newenvelope(e, CurEnv);
5709339Seric 			break;
5714549Seric 
5724549Seric 		  case CMDVRFY:		/* vrfy -- verify address */
57358092Seric 		  case CMDEXPN:		/* expn -- expand address */
57458092Seric 			vrfy = c->cmdcode == CMDVRFY;
57558092Seric 			if (bitset(vrfy ? PRIV_NOVRFY : PRIV_NOEXPN,
57658092Seric 						PrivacyFlags))
57758082Seric 			{
57858412Seric 				if (vrfy)
57958412Seric 					message("252 Who's to say?");
58058412Seric 				else
58158412Seric 					message("502 That's none of your business");
58258082Seric 				break;
58358082Seric 			}
58458082Seric 			else if (!gothello &&
58558092Seric 				 bitset(vrfy ? PRIV_NEEDVRFYHELO : PRIV_NEEDEXPNHELO,
58658092Seric 						PrivacyFlags))
58758082Seric 			{
58858151Seric 				message("503 I demand that you introduce yourself first");
58958082Seric 				break;
59058082Seric 			}
59158092Seric 			if (runinchild(vrfy ? "SMTP-VRFY" : "SMTP-EXPN", e) > 0)
5929339Seric 				break;
59355173Seric #ifdef LOG
59458020Seric 			if (LogLevel > 5)
59555173Seric 				syslog(LOG_INFO, "%s: %s", CurHostName, inp);
59655173Seric #endif
5975003Seric 			vrfyqueue = NULL;
5987762Seric 			QuickAbort = TRUE;
59958092Seric 			if (vrfy)
60058092Seric 				e->e_flags |= EF_VRFYONLY;
60158082Seric 			(void) sendtolist(p, (ADDRESS *) NULL, &vrfyqueue, e);
6027762Seric 			if (Errors != 0)
6039339Seric 			{
6049339Seric 				if (InChild)
6059339Seric 					finis();
6067762Seric 				break;
6079339Seric 			}
6085003Seric 			while (vrfyqueue != NULL)
6095003Seric 			{
6105003Seric 				register ADDRESS *a = vrfyqueue->q_next;
6115003Seric 				char *code;
6125003Seric 
6137685Seric 				while (a != NULL && bitset(QDONTSEND|QBADADDR, a->q_flags))
6145003Seric 					a = a->q_next;
6155003Seric 
6167685Seric 				if (!bitset(QDONTSEND|QBADADDR, vrfyqueue->q_flags))
61758151Seric 					printvrfyaddr(vrfyqueue, a == NULL);
6185003Seric 				else if (a == NULL)
61958151Seric 					message("554 Self destructive alias loop");
6205003Seric 				vrfyqueue = a;
6215003Seric 			}
6229339Seric 			if (InChild)
6239339Seric 				finis();
6244549Seric 			break;
6254549Seric 
6264549Seric 		  case CMDHELP:		/* help -- give user info */
6274577Seric 			help(p);
6284549Seric 			break;
6294549Seric 
6304549Seric 		  case CMDNOOP:		/* noop -- do nothing */
63158151Seric 			message("200 OK");
6324549Seric 			break;
6334549Seric 
6344549Seric 		  case CMDQUIT:		/* quit -- leave mail */
63558151Seric 			message("221 %s closing connection", MyHostName);
6369339Seric 			if (InChild)
6379339Seric 				ExitStat = EX_QUIT;
6384549Seric 			finis();
6394549Seric 
6408544Seric 		  case CMDVERB:		/* set verbose mode */
64159957Seric 			if (bitset(PRIV_NOEXPN, PrivacyFlags))
64259957Seric 			{
64359957Seric 				/* this would give out the same info */
64459957Seric 				message("502 Verbose unavailable");
64559957Seric 				break;
64659957Seric 			}
6478544Seric 			Verbose = TRUE;
64858734Seric 			e->e_sendmode = SM_DELIVER;
64959957Seric 			message("250 Verbose mode");
6508544Seric 			break;
6518544Seric 
6529314Seric 		  case CMDONEX:		/* doing one transaction only */
6539378Seric 			OneXact = TRUE;
65459957Seric 			message("250 Only one transaction");
6559314Seric 			break;
6569314Seric 
65736230Skarels # ifdef SMTPDEBUG
6589339Seric 		  case CMDDBGQSHOW:	/* show queues */
6596907Seric 			printf("Send Queue=");
66055012Seric 			printaddr(e->e_sendqueue, TRUE);
6615003Seric 			break;
6627275Seric 
6637275Seric 		  case CMDDBGDEBUG:	/* set debug mode */
6647676Seric 			tTsetup(tTdvect, sizeof tTdvect, "0-99.1");
6657676Seric 			tTflag(p);
66658151Seric 			message("200 Debug set");
6677275Seric 			break;
6687275Seric 
66936230Skarels # else /* not SMTPDEBUG */
67024945Seric 
67136230Skarels 		  case CMDDBGQSHOW:	/* show queues */
67236230Skarels 		  case CMDDBGDEBUG:	/* set debug mode */
67336233Skarels # ifdef LOG
67458308Seric 			if (LogLevel > 0)
67536230Skarels 				syslog(LOG_NOTICE,
67658020Seric 				    "\"%s\" command from %s (%s)",
67736230Skarels 				    c->cmdname, RealHostName,
67858755Seric 				    anynet_ntoa(&RealHostAddr));
67936233Skarels # endif
68036230Skarels 			/* FALL THROUGH */
68136230Skarels # endif /* SMTPDEBUG */
68236230Skarels 
6834549Seric 		  case CMDERROR:	/* unknown command */
68458151Seric 			message("500 Command unrecognized");
6854549Seric 			break;
6864549Seric 
6874549Seric 		  default:
68836230Skarels 			errno = 0;
68958151Seric 			syserr("500 smtp: unknown code %d", c->cmdcode);
6904549Seric 			break;
6914549Seric 		}
6924549Seric 	}
6934549Seric }
6944549Seric /*
6954549Seric **  SKIPWORD -- skip a fixed word.
6964549Seric **
6974549Seric **	Parameters:
6984549Seric **		p -- place to start looking.
6994549Seric **		w -- word to skip.
7004549Seric **
7014549Seric **	Returns:
7024549Seric **		p following w.
7034549Seric **		NULL on error.
7044549Seric **
7054549Seric **	Side Effects:
7064549Seric **		clobbers the p data area.
7074549Seric */
7084549Seric 
7094549Seric static char *
7104549Seric skipword(p, w)
7114549Seric 	register char *p;
7124549Seric 	char *w;
7134549Seric {
7144549Seric 	register char *q;
7154549Seric 
7164549Seric 	/* find beginning of word */
71758050Seric 	while (isascii(*p) && isspace(*p))
7184549Seric 		p++;
7194549Seric 	q = p;
7204549Seric 
7214549Seric 	/* find end of word */
72258050Seric 	while (*p != '\0' && *p != ':' && !(isascii(*p) && isspace(*p)))
7234549Seric 		p++;
72458050Seric 	while (isascii(*p) && isspace(*p))
7254549Seric 		*p++ = '\0';
7264549Seric 	if (*p != ':')
7274549Seric 	{
7284549Seric 	  syntax:
72958151Seric 		message("501 Syntax error");
7304549Seric 		Errors++;
7314549Seric 		return (NULL);
7324549Seric 	}
7334549Seric 	*p++ = '\0';
73458050Seric 	while (isascii(*p) && isspace(*p))
7354549Seric 		p++;
7364549Seric 
7374549Seric 	/* see if the input word matches desired word */
73833725Sbostic 	if (strcasecmp(q, w))
7394549Seric 		goto syntax;
7404549Seric 
7414549Seric 	return (p);
7424549Seric }
7434577Seric /*
74458151Seric **  PRINTVRFYADDR -- print an entry in the verify queue
74558151Seric **
74658151Seric **	Parameters:
74758151Seric **		a -- the address to print
74858151Seric **		last -- set if this is the last one.
74958151Seric **
75058151Seric **	Returns:
75158151Seric **		none.
75258151Seric **
75358151Seric **	Side Effects:
75458151Seric **		Prints the appropriate 250 codes.
75558151Seric */
75658151Seric 
75758151Seric printvrfyaddr(a, last)
75858151Seric 	register ADDRESS *a;
75958151Seric 	bool last;
76058151Seric {
76158151Seric 	char fmtbuf[20];
76258151Seric 
76358151Seric 	strcpy(fmtbuf, "250");
76458151Seric 	fmtbuf[3] = last ? ' ' : '-';
76558151Seric 
76659746Seric 	if (a->q_fullname == NULL)
76759746Seric 	{
76859746Seric 		if (strchr(a->q_user, '@') == NULL)
76959746Seric 			strcpy(&fmtbuf[4], "<%s@%s>");
77059746Seric 		else
77159746Seric 			strcpy(&fmtbuf[4], "<%s>");
77259746Seric 		message(fmtbuf, a->q_user, MyHostName);
77359746Seric 	}
77458151Seric 	else
77558151Seric 	{
77659746Seric 		if (strchr(a->q_user, '@') == NULL)
77759746Seric 			strcpy(&fmtbuf[4], "%s <%s@%s>");
77859746Seric 		else
77959746Seric 			strcpy(&fmtbuf[4], "%s <%s>");
78059746Seric 		message(fmtbuf, a->q_fullname, a->q_user, MyHostName);
78158151Seric 	}
78258151Seric }
78358151Seric /*
7844577Seric **  HELP -- implement the HELP command.
7854577Seric **
7864577Seric **	Parameters:
7874577Seric **		topic -- the topic we want help for.
7884577Seric **
7894577Seric **	Returns:
7904577Seric **		none.
7914577Seric **
7924577Seric **	Side Effects:
7934577Seric **		outputs the help file to message output.
7944577Seric */
7954577Seric 
7964577Seric help(topic)
7974577Seric 	char *topic;
7984577Seric {
7994577Seric 	register FILE *hf;
8004577Seric 	int len;
8014577Seric 	char buf[MAXLINE];
8024577Seric 	bool noinfo;
8034577Seric 
8048269Seric 	if (HelpFile == NULL || (hf = fopen(HelpFile, "r")) == NULL)
8054577Seric 	{
8064577Seric 		/* no help */
80711931Seric 		errno = 0;
80858151Seric 		message("502 HELP not implemented");
8094577Seric 		return;
8104577Seric 	}
8114577Seric 
81249669Seric 	if (topic == NULL || *topic == '\0')
81349669Seric 		topic = "smtp";
81449669Seric 	else
81549669Seric 		makelower(topic);
81649669Seric 
8174577Seric 	len = strlen(topic);
8184577Seric 	noinfo = TRUE;
8194577Seric 
8204577Seric 	while (fgets(buf, sizeof buf, hf) != NULL)
8214577Seric 	{
8224577Seric 		if (strncmp(buf, topic, len) == 0)
8234577Seric 		{
8244577Seric 			register char *p;
8254577Seric 
82656795Seric 			p = strchr(buf, '\t');
8274577Seric 			if (p == NULL)
8284577Seric 				p = buf;
8294577Seric 			else
8304577Seric 				p++;
8314577Seric 			fixcrlf(p, TRUE);
83258151Seric 			message("214-%s", p);
8334577Seric 			noinfo = FALSE;
8344577Seric 		}
8354577Seric 	}
8364577Seric 
8374577Seric 	if (noinfo)
83858151Seric 		message("504 HELP topic unknown");
8394577Seric 	else
84058151Seric 		message("214 End of HELP info");
8414628Seric 	(void) fclose(hf);
8424577Seric }
8438544Seric /*
8449339Seric **  RUNINCHILD -- return twice -- once in the child, then in the parent again
8459339Seric **
8469339Seric **	Parameters:
8479339Seric **		label -- a string used in error messages
8489339Seric **
8499339Seric **	Returns:
8509339Seric **		zero in the child
8519339Seric **		one in the parent
8529339Seric **
8539339Seric **	Side Effects:
8549339Seric **		none.
8559339Seric */
8568544Seric 
85755012Seric runinchild(label, e)
8589339Seric 	char *label;
85955012Seric 	register ENVELOPE *e;
8609339Seric {
8619339Seric 	int childpid;
8629339Seric 
86316158Seric 	if (!OneXact)
8649339Seric 	{
86516158Seric 		childpid = dofork();
86616158Seric 		if (childpid < 0)
86716158Seric 		{
86816158Seric 			syserr("%s: cannot fork", label);
86916158Seric 			return (1);
87016158Seric 		}
87116158Seric 		if (childpid > 0)
87216158Seric 		{
87316158Seric 			auto int st;
8749339Seric 
87516158Seric 			/* parent -- wait for child to complete */
87659060Seric 			setproctitle("srvrsmtp %s child wait", CurHostName);
87716158Seric 			st = waitfor(childpid);
87816158Seric 			if (st == -1)
87916158Seric 				syserr("%s: lost child", label);
8809339Seric 
88116158Seric 			/* if we exited on a QUIT command, complete the process */
88216158Seric 			if (st == (EX_QUIT << 8))
88316158Seric 				finis();
8849339Seric 
88516158Seric 			return (1);
88616158Seric 		}
88716158Seric 		else
88816158Seric 		{
88916158Seric 			/* child */
89016158Seric 			InChild = TRUE;
89125050Seric 			QuickAbort = FALSE;
89255012Seric 			clearenvelope(e, FALSE);
89316158Seric 		}
8949339Seric 	}
89515256Seric 
89616158Seric 	/* open alias database */
89759672Seric 	initaliases(FALSE, e);
89816158Seric 
89916158Seric 	return (0);
9009339Seric }
9019339Seric 
90256795Seric # endif /* SMTP */
903