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*64948Seric static char sccsid[] = "@(#)srvrsmtp.c	8.19 (Berkeley) 11/26/93 (with SMTP)";
1433731Sbostic #else
15*64948Seric static char sccsid[] = "@(#)srvrsmtp.c	8.19 (Berkeley) 11/26/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 			{
46264718Seric 				message("250 Recipient ok%s",
46364718Seric 					bitset(QQUEUEUP, a->q_flags) ?
46464718Seric 						" (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 			}
48264718Seric 			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";
50864766Seric 			collect(TRUE, doublequeue, e);
50964766Seric 			if (Errors != 0)
51064766Seric 				goto abortmessage;
51163965Seric 			HoldErrs = TRUE;
5124976Seric 
5138238Seric 			/*
5148238Seric 			**  Arrange to send to everyone.
5158238Seric 			**	If sending to multiple people, mail back
5168238Seric 			**		errors rather than reporting directly.
5178238Seric 			**	In any case, don't mail back errors for
5188238Seric 			**		anything that has happened up to
5198238Seric 			**		now (the other end will do this).
52010197Seric 			**	Truncate our transcript -- the mail has gotten
52110197Seric 			**		to us successfully, and if we have
52210197Seric 			**		to mail this back, it will be easier
52310197Seric 			**		on the reader.
5248238Seric 			**	Then send to everyone.
5258238Seric 			**	Finally give a reply code.  If an error has
5268238Seric 			**		already been given, don't mail a
5278238Seric 			**		message back.
5289339Seric 			**	We goose error returns by clearing error bit.
5298238Seric 			*/
5308238Seric 
53124943Seric 			SmtpPhase = "delivery";
53263787Seric 			if (nrcpts != 1 && !doublequeue)
5339378Seric 			{
5349378Seric 				HoldErrs = TRUE;
53558734Seric 				e->e_errormode = EM_MAIL;
5369378Seric 			}
53755012Seric 			e->e_xfp = freopen(queuename(e, 'x'), "w", e->e_xfp);
53858714Seric 			id = e->e_id;
5394976Seric 
5404976Seric 			/* send to all recipients */
54163787Seric 			sendall(e, doublequeue ? SM_QUEUE : SM_DEFAULT);
54255012Seric 			e->e_to = NULL;
5434976Seric 
5448238Seric 			/* issue success if appropriate and reset */
5458238Seric 			if (Errors == 0 || HoldErrs)
54658855Seric 				message("250 %s Message accepted for delivery", id);
54759747Seric 
54859747Seric 			if (bitset(EF_FATALERRS, e->e_flags) && !HoldErrs)
54959730Seric 			{
55059730Seric 				/* avoid sending back an extra message */
55159730Seric 				e->e_flags &= ~EF_FATALERRS;
55259747Seric 				e->e_flags |= EF_CLRQUEUE;
55359730Seric 			}
5548238Seric 			else
55558919Seric 			{
55659747Seric 				/* from now on, we have to operate silently */
55759747Seric 				HoldErrs = TRUE;
55859747Seric 				e->e_errormode = EM_MAIL;
55959747Seric 
56059730Seric 				/* if we just queued, poke it */
56163787Seric 				if (doublequeue && e->e_sendmode != SM_QUEUE)
56259730Seric 				{
56364296Seric 					extern pid_t dowork();
56464296Seric 
56559730Seric 					unlockqueue(e);
56664296Seric 					(void) dowork(id, TRUE, TRUE, e);
56759730Seric 				}
56858919Seric 			}
56958883Seric 
57059747Seric   abortmessage:
5719339Seric 			/* if in a child, pop back to our parent */
5729339Seric 			if (InChild)
5739339Seric 				finis();
57424943Seric 
57524943Seric 			/* clean up a bit */
57658109Seric 			gotmail = FALSE;
57755012Seric 			dropenvelope(e);
57858179Seric 			CurEnv = e = newenvelope(e, CurEnv);
57955012Seric 			e->e_flags = BlankEnvelope.e_flags;
5804549Seric 			break;
5814549Seric 
5824549Seric 		  case CMDRSET:		/* rset -- reset state */
58358151Seric 			message("250 Reset state");
58464359Seric 			e->e_flags |= EF_CLRQUEUE;
5859339Seric 			if (InChild)
5869339Seric 				finis();
58758109Seric 
58858109Seric 			/* clean up a bit */
58958109Seric 			gotmail = FALSE;
59058109Seric 			dropenvelope(e);
59158179Seric 			CurEnv = e = newenvelope(e, CurEnv);
5929339Seric 			break;
5934549Seric 
5944549Seric 		  case CMDVRFY:		/* vrfy -- verify address */
59558092Seric 		  case CMDEXPN:		/* expn -- expand address */
59658092Seric 			vrfy = c->cmdcode == CMDVRFY;
59758092Seric 			if (bitset(vrfy ? PRIV_NOVRFY : PRIV_NOEXPN,
59858092Seric 						PrivacyFlags))
59958082Seric 			{
60058412Seric 				if (vrfy)
60158412Seric 					message("252 Who's to say?");
60258412Seric 				else
60358412Seric 					message("502 That's none of your business");
60458082Seric 				break;
60558082Seric 			}
60658082Seric 			else if (!gothello &&
60758092Seric 				 bitset(vrfy ? PRIV_NEEDVRFYHELO : PRIV_NEEDEXPNHELO,
60858092Seric 						PrivacyFlags))
60958082Seric 			{
61058151Seric 				message("503 I demand that you introduce yourself first");
61158082Seric 				break;
61258082Seric 			}
61358092Seric 			if (runinchild(vrfy ? "SMTP-VRFY" : "SMTP-EXPN", e) > 0)
6149339Seric 				break;
61555173Seric #ifdef LOG
61658020Seric 			if (LogLevel > 5)
61755173Seric 				syslog(LOG_INFO, "%s: %s", CurHostName, inp);
61855173Seric #endif
6195003Seric 			vrfyqueue = NULL;
6207762Seric 			QuickAbort = TRUE;
62158092Seric 			if (vrfy)
62258092Seric 				e->e_flags |= EF_VRFYONLY;
62362373Seric 			while (*p != '\0' && isascii(*p) && isspace(*p))
62462373Seric 				*p++;
62562373Seric 			if (*p == '\0')
62662373Seric 			{
62762373Seric 				message("501 Argument required");
62862373Seric 				Errors++;
62962373Seric 			}
63062373Seric 			else
63162373Seric 			{
63264284Seric 				(void) sendtolist(p, NULLADDR, &vrfyqueue, e);
63362373Seric 			}
6347762Seric 			if (Errors != 0)
6359339Seric 			{
6369339Seric 				if (InChild)
6379339Seric 					finis();
6387762Seric 				break;
6399339Seric 			}
64062373Seric 			if (vrfyqueue == NULL)
64162373Seric 			{
64262373Seric 				message("554 Nothing to %s", vrfy ? "VRFY" : "EXPN");
64362373Seric 			}
6445003Seric 			while (vrfyqueue != NULL)
6455003Seric 			{
64663971Seric 				a = vrfyqueue;
64763971Seric 				while ((a = a->q_next) != NULL &&
64863971Seric 				       bitset(QDONTSEND|QBADADDR, a->q_flags))
64963971Seric 					continue;
6507685Seric 				if (!bitset(QDONTSEND|QBADADDR, vrfyqueue->q_flags))
65158151Seric 					printvrfyaddr(vrfyqueue, a == NULL);
65263847Seric 				vrfyqueue = vrfyqueue->q_next;
6535003Seric 			}
6549339Seric 			if (InChild)
6559339Seric 				finis();
6564549Seric 			break;
6574549Seric 
6584549Seric 		  case CMDHELP:		/* help -- give user info */
6594577Seric 			help(p);
6604549Seric 			break;
6614549Seric 
6624549Seric 		  case CMDNOOP:		/* noop -- do nothing */
66364122Seric 			message("250 OK");
6644549Seric 			break;
6654549Seric 
6664549Seric 		  case CMDQUIT:		/* quit -- leave mail */
66758151Seric 			message("221 %s closing connection", MyHostName);
66861051Seric 
66961051Seric 			/* avoid future 050 messages */
67061051Seric 			Verbose = FALSE;
67161051Seric 
6729339Seric 			if (InChild)
6739339Seric 				ExitStat = EX_QUIT;
6744549Seric 			finis();
6754549Seric 
6768544Seric 		  case CMDVERB:		/* set verbose mode */
67759957Seric 			if (bitset(PRIV_NOEXPN, PrivacyFlags))
67859957Seric 			{
67959957Seric 				/* this would give out the same info */
68059957Seric 				message("502 Verbose unavailable");
68159957Seric 				break;
68259957Seric 			}
6838544Seric 			Verbose = TRUE;
68458734Seric 			e->e_sendmode = SM_DELIVER;
68559957Seric 			message("250 Verbose mode");
6868544Seric 			break;
6878544Seric 
6889314Seric 		  case CMDONEX:		/* doing one transaction only */
6899378Seric 			OneXact = TRUE;
69059957Seric 			message("250 Only one transaction");
6919314Seric 			break;
6929314Seric 
69336230Skarels # ifdef SMTPDEBUG
6949339Seric 		  case CMDDBGQSHOW:	/* show queues */
6956907Seric 			printf("Send Queue=");
69655012Seric 			printaddr(e->e_sendqueue, TRUE);
6975003Seric 			break;
6987275Seric 
6997275Seric 		  case CMDDBGDEBUG:	/* set debug mode */
7007676Seric 			tTsetup(tTdvect, sizeof tTdvect, "0-99.1");
7017676Seric 			tTflag(p);
70258151Seric 			message("200 Debug set");
7037275Seric 			break;
7047275Seric 
70536230Skarels # else /* not SMTPDEBUG */
70636230Skarels 		  case CMDDBGQSHOW:	/* show queues */
70736230Skarels 		  case CMDDBGDEBUG:	/* set debug mode */
70864685Seric # endif /* SMTPDEBUG */
70964685Seric 		  case CMDLOGBOGUS:	/* bogus command */
71036233Skarels # ifdef LOG
71158308Seric 			if (LogLevel > 0)
71264685Seric 				syslog(LOG_CRIT,
71358020Seric 				    "\"%s\" command from %s (%s)",
71436230Skarels 				    c->cmdname, RealHostName,
71558755Seric 				    anynet_ntoa(&RealHostAddr));
71636233Skarels # endif
71736230Skarels 			/* FALL THROUGH */
71836230Skarels 
7194549Seric 		  case CMDERROR:	/* unknown command */
72058151Seric 			message("500 Command unrecognized");
7214549Seric 			break;
7224549Seric 
7234549Seric 		  default:
72436230Skarels 			errno = 0;
72558151Seric 			syserr("500 smtp: unknown code %d", c->cmdcode);
7264549Seric 			break;
7274549Seric 		}
7284549Seric 	}
7294549Seric }
7304549Seric /*
7314549Seric **  SKIPWORD -- skip a fixed word.
7324549Seric **
7334549Seric **	Parameters:
7344549Seric **		p -- place to start looking.
7354549Seric **		w -- word to skip.
7364549Seric **
7374549Seric **	Returns:
7384549Seric **		p following w.
7394549Seric **		NULL on error.
7404549Seric **
7414549Seric **	Side Effects:
7424549Seric **		clobbers the p data area.
7434549Seric */
7444549Seric 
7454549Seric static char *
7464549Seric skipword(p, w)
7474549Seric 	register char *p;
7484549Seric 	char *w;
7494549Seric {
7504549Seric 	register char *q;
7514549Seric 
7524549Seric 	/* find beginning of word */
75358050Seric 	while (isascii(*p) && isspace(*p))
7544549Seric 		p++;
7554549Seric 	q = p;
7564549Seric 
7574549Seric 	/* find end of word */
75858050Seric 	while (*p != '\0' && *p != ':' && !(isascii(*p) && isspace(*p)))
7594549Seric 		p++;
76058050Seric 	while (isascii(*p) && isspace(*p))
7614549Seric 		*p++ = '\0';
7624549Seric 	if (*p != ':')
7634549Seric 	{
7644549Seric 	  syntax:
76562373Seric 		message("501 Syntax error in parameters");
7664549Seric 		Errors++;
7674549Seric 		return (NULL);
7684549Seric 	}
7694549Seric 	*p++ = '\0';
77058050Seric 	while (isascii(*p) && isspace(*p))
7714549Seric 		p++;
7724549Seric 
77362373Seric 	if (*p == '\0')
77462373Seric 		goto syntax;
77562373Seric 
7764549Seric 	/* see if the input word matches desired word */
77733725Sbostic 	if (strcasecmp(q, w))
7784549Seric 		goto syntax;
7794549Seric 
7804549Seric 	return (p);
7814549Seric }
7824577Seric /*
78358151Seric **  PRINTVRFYADDR -- print an entry in the verify queue
78458151Seric **
78558151Seric **	Parameters:
78658151Seric **		a -- the address to print
78758151Seric **		last -- set if this is the last one.
78858151Seric **
78958151Seric **	Returns:
79058151Seric **		none.
79158151Seric **
79258151Seric **	Side Effects:
79358151Seric **		Prints the appropriate 250 codes.
79458151Seric */
79558151Seric 
79658151Seric printvrfyaddr(a, last)
79758151Seric 	register ADDRESS *a;
79858151Seric 	bool last;
79958151Seric {
80058151Seric 	char fmtbuf[20];
80158151Seric 
80258151Seric 	strcpy(fmtbuf, "250");
80358151Seric 	fmtbuf[3] = last ? ' ' : '-';
80458151Seric 
80559746Seric 	if (a->q_fullname == NULL)
80659746Seric 	{
80759746Seric 		if (strchr(a->q_user, '@') == NULL)
80859746Seric 			strcpy(&fmtbuf[4], "<%s@%s>");
80959746Seric 		else
81059746Seric 			strcpy(&fmtbuf[4], "<%s>");
81159746Seric 		message(fmtbuf, a->q_user, MyHostName);
81259746Seric 	}
81358151Seric 	else
81458151Seric 	{
81559746Seric 		if (strchr(a->q_user, '@') == NULL)
81659746Seric 			strcpy(&fmtbuf[4], "%s <%s@%s>");
81759746Seric 		else
81859746Seric 			strcpy(&fmtbuf[4], "%s <%s>");
81959746Seric 		message(fmtbuf, a->q_fullname, a->q_user, MyHostName);
82058151Seric 	}
82158151Seric }
82258151Seric /*
8234577Seric **  HELP -- implement the HELP command.
8244577Seric **
8254577Seric **	Parameters:
8264577Seric **		topic -- the topic we want help for.
8274577Seric **
8284577Seric **	Returns:
8294577Seric **		none.
8304577Seric **
8314577Seric **	Side Effects:
8324577Seric **		outputs the help file to message output.
8334577Seric */
8344577Seric 
8354577Seric help(topic)
8364577Seric 	char *topic;
8374577Seric {
8384577Seric 	register FILE *hf;
8394577Seric 	int len;
8404577Seric 	char buf[MAXLINE];
8414577Seric 	bool noinfo;
8424577Seric 
8438269Seric 	if (HelpFile == NULL || (hf = fopen(HelpFile, "r")) == NULL)
8444577Seric 	{
8454577Seric 		/* no help */
84611931Seric 		errno = 0;
84758151Seric 		message("502 HELP not implemented");
8484577Seric 		return;
8494577Seric 	}
8504577Seric 
85149669Seric 	if (topic == NULL || *topic == '\0')
85249669Seric 		topic = "smtp";
85349669Seric 	else
85449669Seric 		makelower(topic);
85549669Seric 
8564577Seric 	len = strlen(topic);
8574577Seric 	noinfo = TRUE;
8584577Seric 
8594577Seric 	while (fgets(buf, sizeof buf, hf) != NULL)
8604577Seric 	{
8614577Seric 		if (strncmp(buf, topic, len) == 0)
8624577Seric 		{
8634577Seric 			register char *p;
8644577Seric 
86556795Seric 			p = strchr(buf, '\t');
8664577Seric 			if (p == NULL)
8674577Seric 				p = buf;
8684577Seric 			else
8694577Seric 				p++;
8704577Seric 			fixcrlf(p, TRUE);
87158151Seric 			message("214-%s", p);
8724577Seric 			noinfo = FALSE;
8734577Seric 		}
8744577Seric 	}
8754577Seric 
8764577Seric 	if (noinfo)
87758151Seric 		message("504 HELP topic unknown");
8784577Seric 	else
87958151Seric 		message("214 End of HELP info");
8804628Seric 	(void) fclose(hf);
8814577Seric }
8828544Seric /*
8839339Seric **  RUNINCHILD -- return twice -- once in the child, then in the parent again
8849339Seric **
8859339Seric **	Parameters:
8869339Seric **		label -- a string used in error messages
8879339Seric **
8889339Seric **	Returns:
8899339Seric **		zero in the child
8909339Seric **		one in the parent
8919339Seric **
8929339Seric **	Side Effects:
8939339Seric **		none.
8949339Seric */
8958544Seric 
89655012Seric runinchild(label, e)
8979339Seric 	char *label;
89855012Seric 	register ENVELOPE *e;
8999339Seric {
9009339Seric 	int childpid;
9019339Seric 
90216158Seric 	if (!OneXact)
9039339Seric 	{
90416158Seric 		childpid = dofork();
90516158Seric 		if (childpid < 0)
90616158Seric 		{
90716158Seric 			syserr("%s: cannot fork", label);
90816158Seric 			return (1);
90916158Seric 		}
91016158Seric 		if (childpid > 0)
91116158Seric 		{
91216158Seric 			auto int st;
9139339Seric 
91416158Seric 			/* parent -- wait for child to complete */
91561093Seric 			setproctitle("server %s child wait", CurHostName);
91616158Seric 			st = waitfor(childpid);
91716158Seric 			if (st == -1)
91816158Seric 				syserr("%s: lost child", label);
919*64948Seric 			else if (!WIFEXITED(st))
920*64948Seric 				syserr("%s: died on signal %d",
921*64948Seric 					label, st & 0177);
9229339Seric 
92316158Seric 			/* if we exited on a QUIT command, complete the process */
92416158Seric 			if (st == (EX_QUIT << 8))
92516158Seric 				finis();
9269339Seric 
92716158Seric 			return (1);
92816158Seric 		}
92916158Seric 		else
93016158Seric 		{
93116158Seric 			/* child */
93216158Seric 			InChild = TRUE;
93325050Seric 			QuickAbort = FALSE;
93455012Seric 			clearenvelope(e, FALSE);
93516158Seric 		}
9369339Seric 	}
93715256Seric 
93816158Seric 	/* open alias database */
93960537Seric 	initmaps(FALSE, e);
94016158Seric 
94116158Seric 	return (0);
9429339Seric }
9439339Seric 
94456795Seric # endif /* SMTP */
945