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*64718Seric static char sccsid[] = "@(#)srvrsmtp.c	8.17 (Berkeley) 10/15/93 (with SMTP)";
1433731Sbostic #else
15*64718Seric static char sccsid[] = "@(#)srvrsmtp.c	8.17 (Berkeley) 10/15/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 */
9111146Seric 
9263937Seric static char	*skipword();
9363937Seric 
9455012Seric smtp(e)
9555012Seric 	register ENVELOPE *e;
964549Seric {
974549Seric 	register char *p;
988544Seric 	register struct cmd *c;
994549Seric 	char *cmd;
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);
12664496Seric 	if (BrokenSmtpPeers)
12764496Seric 	{
12864496Seric 		message("220 %s", inp);
12964496Seric 	}
13064496Seric 	else
13164496Seric 	{
13264496Seric 		message("220-%s", inp);
13364496Seric 		message("220 ESMTP spoken here");
13464496Seric 	}
13558330Seric 	protocol = NULL;
13659016Seric 	sendinghost = macvalue('s', e);
13758082Seric 	gothello = FALSE;
13858330Seric 	gotmail = FALSE;
1394549Seric 	for (;;)
1404549Seric 	{
14112612Seric 		/* arrange for backout */
14212612Seric 		if (setjmp(TopFrame) > 0 && InChild)
14359058Seric 		{
14459058Seric 			QuickAbort = FALSE;
14559058Seric 			SuprErrs = TRUE;
14612612Seric 			finis();
14759058Seric 		}
14812612Seric 		QuickAbort = FALSE;
14912612Seric 		HoldErrs = FALSE;
15051951Seric 		LogUsrErrs = FALSE;
15163843Seric 		e->e_flags &= ~(EF_VRFYONLY|EF_GLOBALERRS);
15212612Seric 
1537356Seric 		/* setup for the read */
15455012Seric 		e->e_to = NULL;
1554577Seric 		Errors = 0;
1567275Seric 		(void) fflush(stdout);
1577356Seric 
1587356Seric 		/* read the input line */
15961093Seric 		SmtpPhase = "server cmd read";
16061093Seric 		setproctitle("server %s cmd read", CurHostName);
16161093Seric 		p = sfgets(inp, sizeof inp, InChannel, TimeOuts.to_nextcommand,
16261093Seric 				SmtpPhase);
1637356Seric 
1647685Seric 		/* handle errors */
1657356Seric 		if (p == NULL)
1667356Seric 		{
1674549Seric 			/* end of file, just die */
16858151Seric 			message("421 %s Lost input channel from %s",
16925050Seric 				MyHostName, CurHostName);
17055464Seric #ifdef LOG
17163843Seric 			if (LogLevel > (gotmail ? 1 : 19))
17255464Seric 				syslog(LOG_NOTICE, "lost input channel from %s",
17355464Seric 					CurHostName);
17455464Seric #endif
17558069Seric 			if (InChild)
17658069Seric 				ExitStat = EX_QUIT;
1774549Seric 			finis();
1784549Seric 		}
1794549Seric 
1804549Seric 		/* clean up end of line */
1814558Seric 		fixcrlf(inp, TRUE);
1824549Seric 
1834713Seric 		/* echo command to transcript */
18455012Seric 		if (e->e_xfp != NULL)
18555012Seric 			fprintf(e->e_xfp, "<<< %s\n", inp);
1864713Seric 
18759060Seric 		if (e->e_id == NULL)
18859060Seric 			setproctitle("%s: %s", CurHostName, inp);
18959060Seric 		else
19059060Seric 			setproctitle("%s %s: %s", e->e_id, CurHostName, inp);
19159060Seric 
1924549Seric 		/* break off command */
19358050Seric 		for (p = inp; isascii(*p) && isspace(*p); p++)
1944549Seric 			continue;
19557232Seric 		cmd = cmdbuf;
19658050Seric 		while (*p != '\0' &&
19758050Seric 		       !(isascii(*p) && isspace(*p)) &&
19858050Seric 		       cmd < &cmdbuf[sizeof cmdbuf - 2])
19924981Seric 			*cmd++ = *p++;
20024981Seric 		*cmd = '\0';
2014549Seric 
20225691Seric 		/* throw away leading whitespace */
20358050Seric 		while (isascii(*p) && isspace(*p))
20425691Seric 			p++;
20525691Seric 
2064549Seric 		/* decode command */
2074549Seric 		for (c = CmdTab; c->cmdname != NULL; c++)
2084549Seric 		{
20933725Sbostic 			if (!strcasecmp(c->cmdname, cmdbuf))
2104549Seric 				break;
2114549Seric 		}
2124549Seric 
21351954Seric 		/* reset errors */
21451954Seric 		errno = 0;
21551954Seric 
2164549Seric 		/* process command */
2174549Seric 		switch (c->cmdcode)
2184549Seric 		{
2194976Seric 		  case CMDHELO:		/* hello -- introduce yourself */
22058323Seric 		  case CMDEHLO:		/* extended hello */
22158323Seric 			if (c->cmdcode == CMDEHLO)
22258323Seric 			{
22358323Seric 				protocol = "ESMTP";
22461093Seric 				SmtpPhase = "server EHLO";
22558323Seric 			}
22658323Seric 			else
22758323Seric 			{
22858323Seric 				protocol = "SMTP";
22961093Seric 				SmtpPhase = "server HELO";
23058323Seric 			}
23159016Seric 			sendinghost = newstr(p);
23264284Seric 			if (strcasecmp(p, RealHostName) != 0 &&
23364284Seric 			    (strcasecmp(RealHostName, "localhost") != 0 ||
23464284Seric 			     strcasecmp(p, MyHostName) != 0))
23511146Seric 			{
23658789Seric 				auth_warning(e, "Host %s claimed to be %s",
23758789Seric 					RealHostName, p);
23811146Seric 			}
23958957Seric 			p = macvalue('_', e);
24058957Seric 			if (p == NULL)
24159016Seric 				p = RealHostName;
24258323Seric 
24360210Seric 			gothello = TRUE;
24460210Seric 			if (c->cmdcode != CMDEHLO)
24560239Seric 			{
24660239Seric 				/* print old message and be done with it */
24760239Seric 				message("250 %s Hello %s, pleased to meet you",
24860239Seric 					MyHostName, p);
24960210Seric 				break;
25060239Seric 			}
25160239Seric 
25260239Seric 			/* print extended message and brag */
25360239Seric 			message("250-%s Hello %s, pleased to meet you",
25460239Seric 				MyHostName, p);
25558323Seric 			if (!bitset(PRIV_NOEXPN, PrivacyFlags))
25658323Seric 				message("250-EXPN");
25764359Seric 			if (MaxMessageSize > 0)
25864359Seric 				message("250-SIZE %ld", MaxMessageSize);
25959271Seric 			else
26059271Seric 				message("250-SIZE");
26158323Seric 			message("250 HELP");
2624976Seric 			break;
2634976Seric 
2644549Seric 		  case CMDMAIL:		/* mail -- designate sender */
26561093Seric 			SmtpPhase = "server MAIL";
26624943Seric 
2679314Seric 			/* check for validity of this command */
26858789Seric 			if (!gothello)
26958082Seric 			{
27058957Seric 				/* set sending host to our known value */
27159016Seric 				if (sendinghost == NULL)
27259016Seric 					sendinghost = RealHostName;
27358957Seric 
27458789Seric 				if (bitset(PRIV_NEEDMAILHELO, PrivacyFlags))
27558821Seric 				{
27658789Seric 					message("503 Polite people say HELO first");
27758821Seric 					break;
27858821Seric 				}
27958082Seric 			}
28058109Seric 			if (gotmail)
2814558Seric 			{
28258151Seric 				message("503 Sender already specified");
28363843Seric 				if (InChild)
28463843Seric 					finis();
2854558Seric 				break;
2864558Seric 			}
2879339Seric 			if (InChild)
2889339Seric 			{
28936230Skarels 				errno = 0;
29058151Seric 				syserr("503 Nested MAIL command: MAIL %s", p);
29158069Seric 				finis();
2929339Seric 			}
2939339Seric 
2949339Seric 			/* fork a subprocess to process this command */
29555012Seric 			if (runinchild("SMTP-MAIL", e) > 0)
2969339Seric 				break;
29763753Seric 			if (!gothello)
29863753Seric 			{
29963753Seric 				auth_warning(e,
30063753Seric 					"Host %s didn't use HELO protocol",
30163753Seric 					RealHostName);
30263753Seric 			}
30358323Seric 			if (protocol == NULL)
30458323Seric 				protocol = "SMTP";
30558323Seric 			define('r', protocol, e);
30659016Seric 			define('s', sendinghost, e);
30755012Seric 			initsys(e);
30859747Seric 			nrcpts = 0;
30957389Seric 			setproctitle("%s %s: %s", e->e_id, CurHostName, inp);
3109339Seric 
3119339Seric 			/* child -- go do the processing */
3124549Seric 			p = skipword(p, "from");
3134549Seric 			if (p == NULL)
3144549Seric 				break;
31557977Seric 			if (setjmp(TopFrame) > 0)
31658147Seric 			{
31758147Seric 				/* this failed -- undo work */
31858147Seric 				if (InChild)
31959058Seric 				{
32059058Seric 					QuickAbort = FALSE;
32159058Seric 					SuprErrs = TRUE;
32263787Seric 					e->e_flags &= ~EF_FATALERRS;
32358147Seric 					finis();
32459058Seric 				}
32557977Seric 				break;
32658147Seric 			}
32757977Seric 			QuickAbort = TRUE;
32858333Seric 
32958333Seric 			/* must parse sender first */
33058333Seric 			delimptr = NULL;
33158704Seric 			setsender(p, e, &delimptr, FALSE);
33258333Seric 			p = delimptr;
33358333Seric 			if (p != NULL && *p != '\0')
33458333Seric 				*p++ = '\0';
33558333Seric 
33658333Seric 			/* now parse ESMTP arguments */
33758333Seric 			msize = 0;
33858333Seric 			for (; p != NULL && *p != '\0'; p++)
33958333Seric 			{
34058333Seric 				char *kp;
34158333Seric 				char *vp;
34258333Seric 
34358333Seric 				/* locate the beginning of the keyword */
34458333Seric 				while (isascii(*p) && isspace(*p))
34558333Seric 					p++;
34658333Seric 				if (*p == '\0')
34758333Seric 					break;
34858333Seric 				kp = p;
34958333Seric 
35058333Seric 				/* skip to the value portion */
35158333Seric 				while (isascii(*p) && isalnum(*p) || *p == '-')
35258333Seric 					p++;
35358333Seric 				if (*p == '=')
35458333Seric 				{
35558333Seric 					*p++ = '\0';
35658333Seric 					vp = p;
35758333Seric 
35858333Seric 					/* skip to the end of the value */
35958333Seric 					while (*p != '\0' && *p != ' ' &&
36058333Seric 					       !(isascii(*p) && iscntrl(*p)) &&
36158333Seric 					       *p != '=')
36258333Seric 						p++;
36358333Seric 				}
36458333Seric 
36558333Seric 				if (*p != '\0')
36658333Seric 					*p++ = '\0';
36758333Seric 
36858333Seric 				if (tTd(19, 1))
36958333Seric 					printf("MAIL: got arg %s=%s\n", kp,
37058333Seric 						vp == NULL ? "<null>" : vp);
37158333Seric 
37258333Seric 				if (strcasecmp(kp, "size") == 0)
37358333Seric 				{
37459093Seric 					if (vp == NULL)
37558333Seric 					{
37658333Seric 						usrerr("501 SIZE requires a value");
37758333Seric 						/* NOTREACHED */
37858333Seric 					}
37958333Seric 					msize = atol(vp);
38058333Seric 				}
38159093Seric 				else if (strcasecmp(kp, "body") == 0)
38259093Seric 				{
38359093Seric 					if (vp == NULL)
38459093Seric 					{
38559093Seric 						usrerr("501 BODY requires a value");
38659093Seric 						/* NOTREACHED */
38759093Seric 					}
38859093Seric # ifdef MIME
38959093Seric 					if (strcasecmp(vp, "8bitmime") == 0)
39059093Seric 					{
39159093Seric 						e->e_bodytype = "8BITMIME";
39259709Seric 						SevenBit = FALSE;
39359093Seric 					}
39459093Seric 					else if (strcasecmp(vp, "7bit") == 0)
39559093Seric 					{
39659093Seric 						e->e_bodytype = "7BIT";
39759709Seric 						SevenBit = TRUE;
39859093Seric 					}
39959093Seric 					else
40059093Seric 					{
40159093Seric 						usrerr("501 Unknown BODY type %s",
40259093Seric 							vp);
40359093Seric 					}
40459093Seric # endif
40559093Seric 				}
40658333Seric 				else
40758333Seric 				{
40858333Seric 					usrerr("501 %s parameter unrecognized", kp);
40958333Seric 					/* NOTREACHED */
41058333Seric 				}
41158333Seric 			}
41259284Seric 
41359284Seric 			if (MaxMessageSize > 0 && msize > MaxMessageSize)
41459284Seric 			{
41559284Seric 				usrerr("552 Message size exceeds fixed maximum message size (%ld)",
41659284Seric 					MaxMessageSize);
41759284Seric 				/* NOTREACHED */
41859284Seric 			}
41958333Seric 
42058333Seric 			if (!enoughspace(msize))
42158333Seric 			{
42258333Seric 				message("452 Insufficient disk space; try again later");
42358333Seric 				break;
42458333Seric 			}
42558151Seric 			message("250 Sender ok");
42658147Seric 			gotmail = TRUE;
4274549Seric 			break;
4284549Seric 
4294976Seric 		  case CMDRCPT:		/* rcpt -- designate recipient */
43058850Seric 			if (!gotmail)
43158850Seric 			{
43258850Seric 				usrerr("503 Need MAIL before RCPT");
43358850Seric 				break;
43458850Seric 			}
43561093Seric 			SmtpPhase = "server RCPT";
43612612Seric 			if (setjmp(TopFrame) > 0)
43714785Seric 			{
43855012Seric 				e->e_flags &= ~EF_FATALERRS;
43912612Seric 				break;
44014785Seric 			}
44112612Seric 			QuickAbort = TRUE;
44251951Seric 			LogUsrErrs = TRUE;
44358093Seric 
44459699Seric 			if (e->e_sendmode != SM_DELIVER)
44559699Seric 				e->e_flags |= EF_VRFYONLY;
44658919Seric 
4474549Seric 			p = skipword(p, "to");
4484549Seric 			if (p == NULL)
4494549Seric 				break;
45064284Seric 			a = parseaddr(p, NULLADDR, RF_COPYALL, ' ', NULL, e);
45112612Seric 			if (a == NULL)
45212612Seric 				break;
45316886Seric 			a->q_flags |= QPRIMARY;
45455012Seric 			a = recipient(a, &e->e_sendqueue, e);
45512612Seric 			if (Errors != 0)
45612612Seric 				break;
45712612Seric 
45812612Seric 			/* no errors during parsing, but might be a duplicate */
45955012Seric 			e->e_to = p;
46012612Seric 			if (!bitset(QBADADDR, a->q_flags))
46159747Seric 			{
462*64718Seric 				message("250 Recipient ok%s",
463*64718Seric 					bitset(QQUEUEUP, a->q_flags) ?
464*64718Seric 						" (will queue)" : "");
46559747Seric 				nrcpts++;
46659747Seric 			}
46712612Seric 			else
4684549Seric 			{
46912612Seric 				/* punt -- should keep message in ADDRESS.... */
47058151Seric 				message("550 Addressee unknown");
4714549Seric 			}
47255012Seric 			e->e_to = NULL;
4734549Seric 			break;
4744549Seric 
4754549Seric 		  case CMDDATA:		/* data -- text of mail */
47661093Seric 			SmtpPhase = "server DATA";
47758109Seric 			if (!gotmail)
4784549Seric 			{
47958151Seric 				message("503 Need MAIL command");
4804976Seric 				break;
4814549Seric 			}
482*64718Seric 			else if (nrcpts <= 0)
4834549Seric 			{
48458151Seric 				message("503 Need RCPT (recipient)");
4854976Seric 				break;
4864549Seric 			}
4874976Seric 
48858929Seric 			/* check to see if we need to re-expand aliases */
48963787Seric 			/* also reset QBADADDR on already-diagnosted addrs */
49063787Seric 			doublequeue = FALSE;
49158929Seric 			for (a = e->e_sendqueue; a != NULL; a = a->q_next)
49258929Seric 			{
49358929Seric 				if (bitset(QVERIFIED, a->q_flags))
49463787Seric 				{
49563787Seric 					/* need to re-expand aliases */
49663787Seric 					doublequeue = TRUE;
49763787Seric 				}
49863787Seric 				if (bitset(QBADADDR, a->q_flags))
49963787Seric 				{
50063787Seric 					/* make this "go away" */
50163787Seric 					a->q_flags |= QDONTSEND;
50263787Seric 					a->q_flags &= ~QBADADDR;
50363787Seric 				}
50458929Seric 			}
50558929Seric 
5064976Seric 			/* collect the text of the message */
50724943Seric 			SmtpPhase = "collect";
50863965Seric 			HoldErrs = TRUE;
50963787Seric 			collect(TRUE, doublequeue, e);
5104976Seric 
5118238Seric 			/*
5128238Seric 			**  Arrange to send to everyone.
5138238Seric 			**	If sending to multiple people, mail back
5148238Seric 			**		errors rather than reporting directly.
5158238Seric 			**	In any case, don't mail back errors for
5168238Seric 			**		anything that has happened up to
5178238Seric 			**		now (the other end will do this).
51810197Seric 			**	Truncate our transcript -- the mail has gotten
51910197Seric 			**		to us successfully, and if we have
52010197Seric 			**		to mail this back, it will be easier
52110197Seric 			**		on the reader.
5228238Seric 			**	Then send to everyone.
5238238Seric 			**	Finally give a reply code.  If an error has
5248238Seric 			**		already been given, don't mail a
5258238Seric 			**		message back.
5269339Seric 			**	We goose error returns by clearing error bit.
5278238Seric 			*/
5288238Seric 
52924943Seric 			SmtpPhase = "delivery";
53063787Seric 			if (nrcpts != 1 && !doublequeue)
5319378Seric 			{
5329378Seric 				HoldErrs = TRUE;
53358734Seric 				e->e_errormode = EM_MAIL;
5349378Seric 			}
53555012Seric 			e->e_xfp = freopen(queuename(e, 'x'), "w", e->e_xfp);
53658714Seric 			id = e->e_id;
5374976Seric 
5384976Seric 			/* send to all recipients */
53963787Seric 			sendall(e, doublequeue ? SM_QUEUE : SM_DEFAULT);
54055012Seric 			e->e_to = NULL;
5414976Seric 
5428238Seric 			/* issue success if appropriate and reset */
5438238Seric 			if (Errors == 0 || HoldErrs)
54458855Seric 				message("250 %s Message accepted for delivery", id);
54559747Seric 
54659747Seric 			if (bitset(EF_FATALERRS, e->e_flags) && !HoldErrs)
54759730Seric 			{
54859730Seric 				/* avoid sending back an extra message */
54959730Seric 				e->e_flags &= ~EF_FATALERRS;
55059747Seric 				e->e_flags |= EF_CLRQUEUE;
55159730Seric 			}
5528238Seric 			else
55358919Seric 			{
55459747Seric 				/* from now on, we have to operate silently */
55559747Seric 				HoldErrs = TRUE;
55659747Seric 				e->e_errormode = EM_MAIL;
55759747Seric 
55859730Seric 				/* if we just queued, poke it */
55963787Seric 				if (doublequeue && e->e_sendmode != SM_QUEUE)
56059730Seric 				{
56164296Seric 					extern pid_t dowork();
56264296Seric 
56359730Seric 					unlockqueue(e);
56464296Seric 					(void) dowork(id, TRUE, TRUE, e);
56559730Seric 				}
56658919Seric 			}
56758883Seric 
56859747Seric   abortmessage:
5699339Seric 			/* if in a child, pop back to our parent */
5709339Seric 			if (InChild)
5719339Seric 				finis();
57224943Seric 
57324943Seric 			/* clean up a bit */
57458109Seric 			gotmail = FALSE;
57555012Seric 			dropenvelope(e);
57658179Seric 			CurEnv = e = newenvelope(e, CurEnv);
57755012Seric 			e->e_flags = BlankEnvelope.e_flags;
5784549Seric 			break;
5794549Seric 
5804549Seric 		  case CMDRSET:		/* rset -- reset state */
58158151Seric 			message("250 Reset state");
58264359Seric 			e->e_flags |= EF_CLRQUEUE;
5839339Seric 			if (InChild)
5849339Seric 				finis();
58558109Seric 
58658109Seric 			/* clean up a bit */
58758109Seric 			gotmail = FALSE;
58858109Seric 			dropenvelope(e);
58958179Seric 			CurEnv = e = newenvelope(e, CurEnv);
5909339Seric 			break;
5914549Seric 
5924549Seric 		  case CMDVRFY:		/* vrfy -- verify address */
59358092Seric 		  case CMDEXPN:		/* expn -- expand address */
59458092Seric 			vrfy = c->cmdcode == CMDVRFY;
59558092Seric 			if (bitset(vrfy ? PRIV_NOVRFY : PRIV_NOEXPN,
59658092Seric 						PrivacyFlags))
59758082Seric 			{
59858412Seric 				if (vrfy)
59958412Seric 					message("252 Who's to say?");
60058412Seric 				else
60158412Seric 					message("502 That's none of your business");
60258082Seric 				break;
60358082Seric 			}
60458082Seric 			else if (!gothello &&
60558092Seric 				 bitset(vrfy ? PRIV_NEEDVRFYHELO : PRIV_NEEDEXPNHELO,
60658092Seric 						PrivacyFlags))
60758082Seric 			{
60858151Seric 				message("503 I demand that you introduce yourself first");
60958082Seric 				break;
61058082Seric 			}
61158092Seric 			if (runinchild(vrfy ? "SMTP-VRFY" : "SMTP-EXPN", e) > 0)
6129339Seric 				break;
61355173Seric #ifdef LOG
61458020Seric 			if (LogLevel > 5)
61555173Seric 				syslog(LOG_INFO, "%s: %s", CurHostName, inp);
61655173Seric #endif
6175003Seric 			vrfyqueue = NULL;
6187762Seric 			QuickAbort = TRUE;
61958092Seric 			if (vrfy)
62058092Seric 				e->e_flags |= EF_VRFYONLY;
62162373Seric 			while (*p != '\0' && isascii(*p) && isspace(*p))
62262373Seric 				*p++;
62362373Seric 			if (*p == '\0')
62462373Seric 			{
62562373Seric 				message("501 Argument required");
62662373Seric 				Errors++;
62762373Seric 			}
62862373Seric 			else
62962373Seric 			{
63064284Seric 				(void) sendtolist(p, NULLADDR, &vrfyqueue, e);
63162373Seric 			}
6327762Seric 			if (Errors != 0)
6339339Seric 			{
6349339Seric 				if (InChild)
6359339Seric 					finis();
6367762Seric 				break;
6379339Seric 			}
63862373Seric 			if (vrfyqueue == NULL)
63962373Seric 			{
64062373Seric 				message("554 Nothing to %s", vrfy ? "VRFY" : "EXPN");
64162373Seric 			}
6425003Seric 			while (vrfyqueue != NULL)
6435003Seric 			{
64463971Seric 				a = vrfyqueue;
64563971Seric 				while ((a = a->q_next) != NULL &&
64663971Seric 				       bitset(QDONTSEND|QBADADDR, a->q_flags))
64763971Seric 					continue;
6487685Seric 				if (!bitset(QDONTSEND|QBADADDR, vrfyqueue->q_flags))
64958151Seric 					printvrfyaddr(vrfyqueue, a == NULL);
65063847Seric 				vrfyqueue = vrfyqueue->q_next;
6515003Seric 			}
6529339Seric 			if (InChild)
6539339Seric 				finis();
6544549Seric 			break;
6554549Seric 
6564549Seric 		  case CMDHELP:		/* help -- give user info */
6574577Seric 			help(p);
6584549Seric 			break;
6594549Seric 
6604549Seric 		  case CMDNOOP:		/* noop -- do nothing */
66164122Seric 			message("250 OK");
6624549Seric 			break;
6634549Seric 
6644549Seric 		  case CMDQUIT:		/* quit -- leave mail */
66558151Seric 			message("221 %s closing connection", MyHostName);
66661051Seric 
66761051Seric 			/* avoid future 050 messages */
66861051Seric 			Verbose = FALSE;
66961051Seric 
6709339Seric 			if (InChild)
6719339Seric 				ExitStat = EX_QUIT;
6724549Seric 			finis();
6734549Seric 
6748544Seric 		  case CMDVERB:		/* set verbose mode */
67559957Seric 			if (bitset(PRIV_NOEXPN, PrivacyFlags))
67659957Seric 			{
67759957Seric 				/* this would give out the same info */
67859957Seric 				message("502 Verbose unavailable");
67959957Seric 				break;
68059957Seric 			}
6818544Seric 			Verbose = TRUE;
68258734Seric 			e->e_sendmode = SM_DELIVER;
68359957Seric 			message("250 Verbose mode");
6848544Seric 			break;
6858544Seric 
6869314Seric 		  case CMDONEX:		/* doing one transaction only */
6879378Seric 			OneXact = TRUE;
68859957Seric 			message("250 Only one transaction");
6899314Seric 			break;
6909314Seric 
69136230Skarels # ifdef SMTPDEBUG
6929339Seric 		  case CMDDBGQSHOW:	/* show queues */
6936907Seric 			printf("Send Queue=");
69455012Seric 			printaddr(e->e_sendqueue, TRUE);
6955003Seric 			break;
6967275Seric 
6977275Seric 		  case CMDDBGDEBUG:	/* set debug mode */
6987676Seric 			tTsetup(tTdvect, sizeof tTdvect, "0-99.1");
6997676Seric 			tTflag(p);
70058151Seric 			message("200 Debug set");
7017275Seric 			break;
7027275Seric 
70336230Skarels # else /* not SMTPDEBUG */
70436230Skarels 		  case CMDDBGQSHOW:	/* show queues */
70536230Skarels 		  case CMDDBGDEBUG:	/* set debug mode */
70664685Seric # endif /* SMTPDEBUG */
70764685Seric 		  case CMDLOGBOGUS:	/* bogus command */
70836233Skarels # ifdef LOG
70958308Seric 			if (LogLevel > 0)
71064685Seric 				syslog(LOG_CRIT,
71158020Seric 				    "\"%s\" command from %s (%s)",
71236230Skarels 				    c->cmdname, RealHostName,
71358755Seric 				    anynet_ntoa(&RealHostAddr));
71436233Skarels # endif
71536230Skarels 			/* FALL THROUGH */
71636230Skarels 
7174549Seric 		  case CMDERROR:	/* unknown command */
71858151Seric 			message("500 Command unrecognized");
7194549Seric 			break;
7204549Seric 
7214549Seric 		  default:
72236230Skarels 			errno = 0;
72358151Seric 			syserr("500 smtp: unknown code %d", c->cmdcode);
7244549Seric 			break;
7254549Seric 		}
7264549Seric 	}
7274549Seric }
7284549Seric /*
7294549Seric **  SKIPWORD -- skip a fixed word.
7304549Seric **
7314549Seric **	Parameters:
7324549Seric **		p -- place to start looking.
7334549Seric **		w -- word to skip.
7344549Seric **
7354549Seric **	Returns:
7364549Seric **		p following w.
7374549Seric **		NULL on error.
7384549Seric **
7394549Seric **	Side Effects:
7404549Seric **		clobbers the p data area.
7414549Seric */
7424549Seric 
7434549Seric static char *
7444549Seric skipword(p, w)
7454549Seric 	register char *p;
7464549Seric 	char *w;
7474549Seric {
7484549Seric 	register char *q;
7494549Seric 
7504549Seric 	/* find beginning of word */
75158050Seric 	while (isascii(*p) && isspace(*p))
7524549Seric 		p++;
7534549Seric 	q = p;
7544549Seric 
7554549Seric 	/* find end of word */
75658050Seric 	while (*p != '\0' && *p != ':' && !(isascii(*p) && isspace(*p)))
7574549Seric 		p++;
75858050Seric 	while (isascii(*p) && isspace(*p))
7594549Seric 		*p++ = '\0';
7604549Seric 	if (*p != ':')
7614549Seric 	{
7624549Seric 	  syntax:
76362373Seric 		message("501 Syntax error in parameters");
7644549Seric 		Errors++;
7654549Seric 		return (NULL);
7664549Seric 	}
7674549Seric 	*p++ = '\0';
76858050Seric 	while (isascii(*p) && isspace(*p))
7694549Seric 		p++;
7704549Seric 
77162373Seric 	if (*p == '\0')
77262373Seric 		goto syntax;
77362373Seric 
7744549Seric 	/* see if the input word matches desired word */
77533725Sbostic 	if (strcasecmp(q, w))
7764549Seric 		goto syntax;
7774549Seric 
7784549Seric 	return (p);
7794549Seric }
7804577Seric /*
78158151Seric **  PRINTVRFYADDR -- print an entry in the verify queue
78258151Seric **
78358151Seric **	Parameters:
78458151Seric **		a -- the address to print
78558151Seric **		last -- set if this is the last one.
78658151Seric **
78758151Seric **	Returns:
78858151Seric **		none.
78958151Seric **
79058151Seric **	Side Effects:
79158151Seric **		Prints the appropriate 250 codes.
79258151Seric */
79358151Seric 
79458151Seric printvrfyaddr(a, last)
79558151Seric 	register ADDRESS *a;
79658151Seric 	bool last;
79758151Seric {
79858151Seric 	char fmtbuf[20];
79958151Seric 
80058151Seric 	strcpy(fmtbuf, "250");
80158151Seric 	fmtbuf[3] = last ? ' ' : '-';
80258151Seric 
80359746Seric 	if (a->q_fullname == NULL)
80459746Seric 	{
80559746Seric 		if (strchr(a->q_user, '@') == NULL)
80659746Seric 			strcpy(&fmtbuf[4], "<%s@%s>");
80759746Seric 		else
80859746Seric 			strcpy(&fmtbuf[4], "<%s>");
80959746Seric 		message(fmtbuf, a->q_user, MyHostName);
81059746Seric 	}
81158151Seric 	else
81258151Seric 	{
81359746Seric 		if (strchr(a->q_user, '@') == NULL)
81459746Seric 			strcpy(&fmtbuf[4], "%s <%s@%s>");
81559746Seric 		else
81659746Seric 			strcpy(&fmtbuf[4], "%s <%s>");
81759746Seric 		message(fmtbuf, a->q_fullname, a->q_user, MyHostName);
81858151Seric 	}
81958151Seric }
82058151Seric /*
8214577Seric **  HELP -- implement the HELP command.
8224577Seric **
8234577Seric **	Parameters:
8244577Seric **		topic -- the topic we want help for.
8254577Seric **
8264577Seric **	Returns:
8274577Seric **		none.
8284577Seric **
8294577Seric **	Side Effects:
8304577Seric **		outputs the help file to message output.
8314577Seric */
8324577Seric 
8334577Seric help(topic)
8344577Seric 	char *topic;
8354577Seric {
8364577Seric 	register FILE *hf;
8374577Seric 	int len;
8384577Seric 	char buf[MAXLINE];
8394577Seric 	bool noinfo;
8404577Seric 
8418269Seric 	if (HelpFile == NULL || (hf = fopen(HelpFile, "r")) == NULL)
8424577Seric 	{
8434577Seric 		/* no help */
84411931Seric 		errno = 0;
84558151Seric 		message("502 HELP not implemented");
8464577Seric 		return;
8474577Seric 	}
8484577Seric 
84949669Seric 	if (topic == NULL || *topic == '\0')
85049669Seric 		topic = "smtp";
85149669Seric 	else
85249669Seric 		makelower(topic);
85349669Seric 
8544577Seric 	len = strlen(topic);
8554577Seric 	noinfo = TRUE;
8564577Seric 
8574577Seric 	while (fgets(buf, sizeof buf, hf) != NULL)
8584577Seric 	{
8594577Seric 		if (strncmp(buf, topic, len) == 0)
8604577Seric 		{
8614577Seric 			register char *p;
8624577Seric 
86356795Seric 			p = strchr(buf, '\t');
8644577Seric 			if (p == NULL)
8654577Seric 				p = buf;
8664577Seric 			else
8674577Seric 				p++;
8684577Seric 			fixcrlf(p, TRUE);
86958151Seric 			message("214-%s", p);
8704577Seric 			noinfo = FALSE;
8714577Seric 		}
8724577Seric 	}
8734577Seric 
8744577Seric 	if (noinfo)
87558151Seric 		message("504 HELP topic unknown");
8764577Seric 	else
87758151Seric 		message("214 End of HELP info");
8784628Seric 	(void) fclose(hf);
8794577Seric }
8808544Seric /*
8819339Seric **  RUNINCHILD -- return twice -- once in the child, then in the parent again
8829339Seric **
8839339Seric **	Parameters:
8849339Seric **		label -- a string used in error messages
8859339Seric **
8869339Seric **	Returns:
8879339Seric **		zero in the child
8889339Seric **		one in the parent
8899339Seric **
8909339Seric **	Side Effects:
8919339Seric **		none.
8929339Seric */
8938544Seric 
89455012Seric runinchild(label, e)
8959339Seric 	char *label;
89655012Seric 	register ENVELOPE *e;
8979339Seric {
8989339Seric 	int childpid;
8999339Seric 
90016158Seric 	if (!OneXact)
9019339Seric 	{
90216158Seric 		childpid = dofork();
90316158Seric 		if (childpid < 0)
90416158Seric 		{
90516158Seric 			syserr("%s: cannot fork", label);
90616158Seric 			return (1);
90716158Seric 		}
90816158Seric 		if (childpid > 0)
90916158Seric 		{
91016158Seric 			auto int st;
9119339Seric 
91216158Seric 			/* parent -- wait for child to complete */
91361093Seric 			setproctitle("server %s child wait", CurHostName);
91416158Seric 			st = waitfor(childpid);
91516158Seric 			if (st == -1)
91616158Seric 				syserr("%s: lost child", label);
9179339Seric 
91816158Seric 			/* if we exited on a QUIT command, complete the process */
91916158Seric 			if (st == (EX_QUIT << 8))
92016158Seric 				finis();
9219339Seric 
92216158Seric 			return (1);
92316158Seric 		}
92416158Seric 		else
92516158Seric 		{
92616158Seric 			/* child */
92716158Seric 			InChild = TRUE;
92825050Seric 			QuickAbort = FALSE;
92955012Seric 			clearenvelope(e, FALSE);
93016158Seric 		}
9319339Seric 	}
93215256Seric 
93316158Seric 	/* open alias database */
93460537Seric 	initmaps(FALSE, e);
93516158Seric 
93616158Seric 	return (0);
9379339Seric }
9389339Seric 
93956795Seric # endif /* SMTP */
940