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*64496Seric static char sccsid[] = "@(#)srvrsmtp.c	8.15 (Berkeley) 09/18/93 (with SMTP)";
1433731Sbostic #else
15*64496Seric static char sccsid[] = "@(#)srvrsmtp.c	8.15 (Berkeley) 09/18/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 */
5936230Skarels /* debugging-only commands, only enabled if SMTPDEBUG is defined */
6058092Seric # define CMDDBGQSHOW	24	/* showq -- show send queue */
6158092Seric # define CMDDBGDEBUG	25	/* debug -- set debug mode */
624549Seric 
634549Seric static struct cmd	CmdTab[] =
644549Seric {
654549Seric 	"mail",		CMDMAIL,
664976Seric 	"rcpt",		CMDRCPT,
674549Seric 	"data",		CMDDATA,
684549Seric 	"rset",		CMDRSET,
694549Seric 	"vrfy",		CMDVRFY,
7058092Seric 	"expn",		CMDEXPN,
714549Seric 	"help",		CMDHELP,
724549Seric 	"noop",		CMDNOOP,
734549Seric 	"quit",		CMDQUIT,
744976Seric 	"helo",		CMDHELO,
7558323Seric 	"ehlo",		CMDEHLO,
768544Seric 	"verb",		CMDVERB,
779314Seric 	"onex",		CMDONEX,
7836230Skarels 	/*
7936230Skarels 	 * remaining commands are here only
8036230Skarels 	 * to trap and log attempts to use them
8136230Skarels 	 */
829339Seric 	"showq",	CMDDBGQSHOW,
838544Seric 	"debug",	CMDDBGDEBUG,
844549Seric 	NULL,		CMDERROR,
854549Seric };
864549Seric 
879339Seric bool	InChild = FALSE;		/* true if running in a subprocess */
889378Seric bool	OneXact = FALSE;		/* one xaction only this run */
8911146Seric 
909339Seric #define EX_QUIT		22		/* special code for QUIT command */
918544Seric 
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);
126*64496Seric 	if (BrokenSmtpPeers)
127*64496Seric 	{
128*64496Seric 		message("220 %s", inp);
129*64496Seric 	}
130*64496Seric 	else
131*64496Seric 	{
132*64496Seric 		message("220-%s", inp);
133*64496Seric 		message("220 ESMTP spoken here");
134*64496Seric 	}
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 			{
46258151Seric 				message("250 Recipient ok");
46359747Seric 				nrcpts++;
46459747Seric 			}
46512612Seric 			else
4664549Seric 			{
46712612Seric 				/* punt -- should keep message in ADDRESS.... */
46858151Seric 				message("550 Addressee unknown");
4694549Seric 			}
47055012Seric 			e->e_to = NULL;
4714549Seric 			break;
4724549Seric 
4734549Seric 		  case CMDDATA:		/* data -- text of mail */
47461093Seric 			SmtpPhase = "server DATA";
47558109Seric 			if (!gotmail)
4764549Seric 			{
47758151Seric 				message("503 Need MAIL command");
4784976Seric 				break;
4794549Seric 			}
48055012Seric 			else if (e->e_nrcpts <= 0)
4814549Seric 			{
48258151Seric 				message("503 Need RCPT (recipient)");
4834976Seric 				break;
4844549Seric 			}
4854976Seric 
48658929Seric 			/* check to see if we need to re-expand aliases */
48763787Seric 			/* also reset QBADADDR on already-diagnosted addrs */
48863787Seric 			doublequeue = FALSE;
48958929Seric 			for (a = e->e_sendqueue; a != NULL; a = a->q_next)
49058929Seric 			{
49158929Seric 				if (bitset(QVERIFIED, a->q_flags))
49263787Seric 				{
49363787Seric 					/* need to re-expand aliases */
49463787Seric 					doublequeue = TRUE;
49563787Seric 				}
49663787Seric 				if (bitset(QBADADDR, a->q_flags))
49763787Seric 				{
49863787Seric 					/* make this "go away" */
49963787Seric 					a->q_flags |= QDONTSEND;
50063787Seric 					a->q_flags &= ~QBADADDR;
50163787Seric 				}
50258929Seric 			}
50358929Seric 
5044976Seric 			/* collect the text of the message */
50524943Seric 			SmtpPhase = "collect";
50663965Seric 			HoldErrs = TRUE;
50763787Seric 			collect(TRUE, doublequeue, e);
5084976Seric 
5098238Seric 			/*
5108238Seric 			**  Arrange to send to everyone.
5118238Seric 			**	If sending to multiple people, mail back
5128238Seric 			**		errors rather than reporting directly.
5138238Seric 			**	In any case, don't mail back errors for
5148238Seric 			**		anything that has happened up to
5158238Seric 			**		now (the other end will do this).
51610197Seric 			**	Truncate our transcript -- the mail has gotten
51710197Seric 			**		to us successfully, and if we have
51810197Seric 			**		to mail this back, it will be easier
51910197Seric 			**		on the reader.
5208238Seric 			**	Then send to everyone.
5218238Seric 			**	Finally give a reply code.  If an error has
5228238Seric 			**		already been given, don't mail a
5238238Seric 			**		message back.
5249339Seric 			**	We goose error returns by clearing error bit.
5258238Seric 			*/
5268238Seric 
52724943Seric 			SmtpPhase = "delivery";
52863787Seric 			if (nrcpts != 1 && !doublequeue)
5299378Seric 			{
5309378Seric 				HoldErrs = TRUE;
53158734Seric 				e->e_errormode = EM_MAIL;
5329378Seric 			}
53355012Seric 			e->e_xfp = freopen(queuename(e, 'x'), "w", e->e_xfp);
53458714Seric 			id = e->e_id;
5354976Seric 
5364976Seric 			/* send to all recipients */
53763787Seric 			sendall(e, doublequeue ? SM_QUEUE : SM_DEFAULT);
53855012Seric 			e->e_to = NULL;
5394976Seric 
5408238Seric 			/* issue success if appropriate and reset */
5418238Seric 			if (Errors == 0 || HoldErrs)
54258855Seric 				message("250 %s Message accepted for delivery", id);
54359747Seric 
54459747Seric 			if (bitset(EF_FATALERRS, e->e_flags) && !HoldErrs)
54559730Seric 			{
54659730Seric 				/* avoid sending back an extra message */
54759730Seric 				e->e_flags &= ~EF_FATALERRS;
54859747Seric 				e->e_flags |= EF_CLRQUEUE;
54959730Seric 			}
5508238Seric 			else
55158919Seric 			{
55259747Seric 				/* from now on, we have to operate silently */
55359747Seric 				HoldErrs = TRUE;
55459747Seric 				e->e_errormode = EM_MAIL;
55559747Seric 
55659730Seric 				/* if we just queued, poke it */
55763787Seric 				if (doublequeue && e->e_sendmode != SM_QUEUE)
55859730Seric 				{
55964296Seric 					extern pid_t dowork();
56064296Seric 
56159730Seric 					unlockqueue(e);
56264296Seric 					(void) dowork(id, TRUE, TRUE, e);
56359730Seric 				}
56458919Seric 			}
56558883Seric 
56659747Seric   abortmessage:
5679339Seric 			/* if in a child, pop back to our parent */
5689339Seric 			if (InChild)
5699339Seric 				finis();
57024943Seric 
57124943Seric 			/* clean up a bit */
57258109Seric 			gotmail = FALSE;
57355012Seric 			dropenvelope(e);
57458179Seric 			CurEnv = e = newenvelope(e, CurEnv);
57555012Seric 			e->e_flags = BlankEnvelope.e_flags;
5764549Seric 			break;
5774549Seric 
5784549Seric 		  case CMDRSET:		/* rset -- reset state */
57958151Seric 			message("250 Reset state");
58064359Seric 			e->e_flags |= EF_CLRQUEUE;
5819339Seric 			if (InChild)
5829339Seric 				finis();
58358109Seric 
58458109Seric 			/* clean up a bit */
58558109Seric 			gotmail = FALSE;
58658109Seric 			dropenvelope(e);
58758179Seric 			CurEnv = e = newenvelope(e, CurEnv);
5889339Seric 			break;
5894549Seric 
5904549Seric 		  case CMDVRFY:		/* vrfy -- verify address */
59158092Seric 		  case CMDEXPN:		/* expn -- expand address */
59258092Seric 			vrfy = c->cmdcode == CMDVRFY;
59358092Seric 			if (bitset(vrfy ? PRIV_NOVRFY : PRIV_NOEXPN,
59458092Seric 						PrivacyFlags))
59558082Seric 			{
59658412Seric 				if (vrfy)
59758412Seric 					message("252 Who's to say?");
59858412Seric 				else
59958412Seric 					message("502 That's none of your business");
60058082Seric 				break;
60158082Seric 			}
60258082Seric 			else if (!gothello &&
60358092Seric 				 bitset(vrfy ? PRIV_NEEDVRFYHELO : PRIV_NEEDEXPNHELO,
60458092Seric 						PrivacyFlags))
60558082Seric 			{
60658151Seric 				message("503 I demand that you introduce yourself first");
60758082Seric 				break;
60858082Seric 			}
60958092Seric 			if (runinchild(vrfy ? "SMTP-VRFY" : "SMTP-EXPN", e) > 0)
6109339Seric 				break;
61155173Seric #ifdef LOG
61258020Seric 			if (LogLevel > 5)
61355173Seric 				syslog(LOG_INFO, "%s: %s", CurHostName, inp);
61455173Seric #endif
6155003Seric 			vrfyqueue = NULL;
6167762Seric 			QuickAbort = TRUE;
61758092Seric 			if (vrfy)
61858092Seric 				e->e_flags |= EF_VRFYONLY;
61962373Seric 			while (*p != '\0' && isascii(*p) && isspace(*p))
62062373Seric 				*p++;
62162373Seric 			if (*p == '\0')
62262373Seric 			{
62362373Seric 				message("501 Argument required");
62462373Seric 				Errors++;
62562373Seric 			}
62662373Seric 			else
62762373Seric 			{
62864284Seric 				(void) sendtolist(p, NULLADDR, &vrfyqueue, e);
62962373Seric 			}
6307762Seric 			if (Errors != 0)
6319339Seric 			{
6329339Seric 				if (InChild)
6339339Seric 					finis();
6347762Seric 				break;
6359339Seric 			}
63662373Seric 			if (vrfyqueue == NULL)
63762373Seric 			{
63862373Seric 				message("554 Nothing to %s", vrfy ? "VRFY" : "EXPN");
63962373Seric 			}
6405003Seric 			while (vrfyqueue != NULL)
6415003Seric 			{
64263971Seric 				a = vrfyqueue;
64363971Seric 				while ((a = a->q_next) != NULL &&
64463971Seric 				       bitset(QDONTSEND|QBADADDR, a->q_flags))
64563971Seric 					continue;
6467685Seric 				if (!bitset(QDONTSEND|QBADADDR, vrfyqueue->q_flags))
64758151Seric 					printvrfyaddr(vrfyqueue, a == NULL);
64863847Seric 				vrfyqueue = vrfyqueue->q_next;
6495003Seric 			}
6509339Seric 			if (InChild)
6519339Seric 				finis();
6524549Seric 			break;
6534549Seric 
6544549Seric 		  case CMDHELP:		/* help -- give user info */
6554577Seric 			help(p);
6564549Seric 			break;
6574549Seric 
6584549Seric 		  case CMDNOOP:		/* noop -- do nothing */
65964122Seric 			message("250 OK");
6604549Seric 			break;
6614549Seric 
6624549Seric 		  case CMDQUIT:		/* quit -- leave mail */
66358151Seric 			message("221 %s closing connection", MyHostName);
66461051Seric 
66561051Seric 			/* avoid future 050 messages */
66661051Seric 			Verbose = FALSE;
66761051Seric 
6689339Seric 			if (InChild)
6699339Seric 				ExitStat = EX_QUIT;
6704549Seric 			finis();
6714549Seric 
6728544Seric 		  case CMDVERB:		/* set verbose mode */
67359957Seric 			if (bitset(PRIV_NOEXPN, PrivacyFlags))
67459957Seric 			{
67559957Seric 				/* this would give out the same info */
67659957Seric 				message("502 Verbose unavailable");
67759957Seric 				break;
67859957Seric 			}
6798544Seric 			Verbose = TRUE;
68058734Seric 			e->e_sendmode = SM_DELIVER;
68159957Seric 			message("250 Verbose mode");
6828544Seric 			break;
6838544Seric 
6849314Seric 		  case CMDONEX:		/* doing one transaction only */
6859378Seric 			OneXact = TRUE;
68659957Seric 			message("250 Only one transaction");
6879314Seric 			break;
6889314Seric 
68936230Skarels # ifdef SMTPDEBUG
6909339Seric 		  case CMDDBGQSHOW:	/* show queues */
6916907Seric 			printf("Send Queue=");
69255012Seric 			printaddr(e->e_sendqueue, TRUE);
6935003Seric 			break;
6947275Seric 
6957275Seric 		  case CMDDBGDEBUG:	/* set debug mode */
6967676Seric 			tTsetup(tTdvect, sizeof tTdvect, "0-99.1");
6977676Seric 			tTflag(p);
69858151Seric 			message("200 Debug set");
6997275Seric 			break;
7007275Seric 
70136230Skarels # else /* not SMTPDEBUG */
70224945Seric 
70336230Skarels 		  case CMDDBGQSHOW:	/* show queues */
70436230Skarels 		  case CMDDBGDEBUG:	/* set debug mode */
70536233Skarels # ifdef LOG
70658308Seric 			if (LogLevel > 0)
70736230Skarels 				syslog(LOG_NOTICE,
70858020Seric 				    "\"%s\" command from %s (%s)",
70936230Skarels 				    c->cmdname, RealHostName,
71058755Seric 				    anynet_ntoa(&RealHostAddr));
71136233Skarels # endif
71236230Skarels 			/* FALL THROUGH */
71336230Skarels # endif /* SMTPDEBUG */
71436230Skarels 
7154549Seric 		  case CMDERROR:	/* unknown command */
71658151Seric 			message("500 Command unrecognized");
7174549Seric 			break;
7184549Seric 
7194549Seric 		  default:
72036230Skarels 			errno = 0;
72158151Seric 			syserr("500 smtp: unknown code %d", c->cmdcode);
7224549Seric 			break;
7234549Seric 		}
7244549Seric 	}
7254549Seric }
7264549Seric /*
7274549Seric **  SKIPWORD -- skip a fixed word.
7284549Seric **
7294549Seric **	Parameters:
7304549Seric **		p -- place to start looking.
7314549Seric **		w -- word to skip.
7324549Seric **
7334549Seric **	Returns:
7344549Seric **		p following w.
7354549Seric **		NULL on error.
7364549Seric **
7374549Seric **	Side Effects:
7384549Seric **		clobbers the p data area.
7394549Seric */
7404549Seric 
7414549Seric static char *
7424549Seric skipword(p, w)
7434549Seric 	register char *p;
7444549Seric 	char *w;
7454549Seric {
7464549Seric 	register char *q;
7474549Seric 
7484549Seric 	/* find beginning of word */
74958050Seric 	while (isascii(*p) && isspace(*p))
7504549Seric 		p++;
7514549Seric 	q = p;
7524549Seric 
7534549Seric 	/* find end of word */
75458050Seric 	while (*p != '\0' && *p != ':' && !(isascii(*p) && isspace(*p)))
7554549Seric 		p++;
75658050Seric 	while (isascii(*p) && isspace(*p))
7574549Seric 		*p++ = '\0';
7584549Seric 	if (*p != ':')
7594549Seric 	{
7604549Seric 	  syntax:
76162373Seric 		message("501 Syntax error in parameters");
7624549Seric 		Errors++;
7634549Seric 		return (NULL);
7644549Seric 	}
7654549Seric 	*p++ = '\0';
76658050Seric 	while (isascii(*p) && isspace(*p))
7674549Seric 		p++;
7684549Seric 
76962373Seric 	if (*p == '\0')
77062373Seric 		goto syntax;
77162373Seric 
7724549Seric 	/* see if the input word matches desired word */
77333725Sbostic 	if (strcasecmp(q, w))
7744549Seric 		goto syntax;
7754549Seric 
7764549Seric 	return (p);
7774549Seric }
7784577Seric /*
77958151Seric **  PRINTVRFYADDR -- print an entry in the verify queue
78058151Seric **
78158151Seric **	Parameters:
78258151Seric **		a -- the address to print
78358151Seric **		last -- set if this is the last one.
78458151Seric **
78558151Seric **	Returns:
78658151Seric **		none.
78758151Seric **
78858151Seric **	Side Effects:
78958151Seric **		Prints the appropriate 250 codes.
79058151Seric */
79158151Seric 
79258151Seric printvrfyaddr(a, last)
79358151Seric 	register ADDRESS *a;
79458151Seric 	bool last;
79558151Seric {
79658151Seric 	char fmtbuf[20];
79758151Seric 
79858151Seric 	strcpy(fmtbuf, "250");
79958151Seric 	fmtbuf[3] = last ? ' ' : '-';
80058151Seric 
80159746Seric 	if (a->q_fullname == NULL)
80259746Seric 	{
80359746Seric 		if (strchr(a->q_user, '@') == NULL)
80459746Seric 			strcpy(&fmtbuf[4], "<%s@%s>");
80559746Seric 		else
80659746Seric 			strcpy(&fmtbuf[4], "<%s>");
80759746Seric 		message(fmtbuf, a->q_user, MyHostName);
80859746Seric 	}
80958151Seric 	else
81058151Seric 	{
81159746Seric 		if (strchr(a->q_user, '@') == NULL)
81259746Seric 			strcpy(&fmtbuf[4], "%s <%s@%s>");
81359746Seric 		else
81459746Seric 			strcpy(&fmtbuf[4], "%s <%s>");
81559746Seric 		message(fmtbuf, a->q_fullname, a->q_user, MyHostName);
81658151Seric 	}
81758151Seric }
81858151Seric /*
8194577Seric **  HELP -- implement the HELP command.
8204577Seric **
8214577Seric **	Parameters:
8224577Seric **		topic -- the topic we want help for.
8234577Seric **
8244577Seric **	Returns:
8254577Seric **		none.
8264577Seric **
8274577Seric **	Side Effects:
8284577Seric **		outputs the help file to message output.
8294577Seric */
8304577Seric 
8314577Seric help(topic)
8324577Seric 	char *topic;
8334577Seric {
8344577Seric 	register FILE *hf;
8354577Seric 	int len;
8364577Seric 	char buf[MAXLINE];
8374577Seric 	bool noinfo;
8384577Seric 
8398269Seric 	if (HelpFile == NULL || (hf = fopen(HelpFile, "r")) == NULL)
8404577Seric 	{
8414577Seric 		/* no help */
84211931Seric 		errno = 0;
84358151Seric 		message("502 HELP not implemented");
8444577Seric 		return;
8454577Seric 	}
8464577Seric 
84749669Seric 	if (topic == NULL || *topic == '\0')
84849669Seric 		topic = "smtp";
84949669Seric 	else
85049669Seric 		makelower(topic);
85149669Seric 
8524577Seric 	len = strlen(topic);
8534577Seric 	noinfo = TRUE;
8544577Seric 
8554577Seric 	while (fgets(buf, sizeof buf, hf) != NULL)
8564577Seric 	{
8574577Seric 		if (strncmp(buf, topic, len) == 0)
8584577Seric 		{
8594577Seric 			register char *p;
8604577Seric 
86156795Seric 			p = strchr(buf, '\t');
8624577Seric 			if (p == NULL)
8634577Seric 				p = buf;
8644577Seric 			else
8654577Seric 				p++;
8664577Seric 			fixcrlf(p, TRUE);
86758151Seric 			message("214-%s", p);
8684577Seric 			noinfo = FALSE;
8694577Seric 		}
8704577Seric 	}
8714577Seric 
8724577Seric 	if (noinfo)
87358151Seric 		message("504 HELP topic unknown");
8744577Seric 	else
87558151Seric 		message("214 End of HELP info");
8764628Seric 	(void) fclose(hf);
8774577Seric }
8788544Seric /*
8799339Seric **  RUNINCHILD -- return twice -- once in the child, then in the parent again
8809339Seric **
8819339Seric **	Parameters:
8829339Seric **		label -- a string used in error messages
8839339Seric **
8849339Seric **	Returns:
8859339Seric **		zero in the child
8869339Seric **		one in the parent
8879339Seric **
8889339Seric **	Side Effects:
8899339Seric **		none.
8909339Seric */
8918544Seric 
89255012Seric runinchild(label, e)
8939339Seric 	char *label;
89455012Seric 	register ENVELOPE *e;
8959339Seric {
8969339Seric 	int childpid;
8979339Seric 
89816158Seric 	if (!OneXact)
8999339Seric 	{
90016158Seric 		childpid = dofork();
90116158Seric 		if (childpid < 0)
90216158Seric 		{
90316158Seric 			syserr("%s: cannot fork", label);
90416158Seric 			return (1);
90516158Seric 		}
90616158Seric 		if (childpid > 0)
90716158Seric 		{
90816158Seric 			auto int st;
9099339Seric 
91016158Seric 			/* parent -- wait for child to complete */
91161093Seric 			setproctitle("server %s child wait", CurHostName);
91216158Seric 			st = waitfor(childpid);
91316158Seric 			if (st == -1)
91416158Seric 				syserr("%s: lost child", label);
9159339Seric 
91616158Seric 			/* if we exited on a QUIT command, complete the process */
91716158Seric 			if (st == (EX_QUIT << 8))
91816158Seric 				finis();
9199339Seric 
92016158Seric 			return (1);
92116158Seric 		}
92216158Seric 		else
92316158Seric 		{
92416158Seric 			/* child */
92516158Seric 			InChild = TRUE;
92625050Seric 			QuickAbort = FALSE;
92755012Seric 			clearenvelope(e, FALSE);
92816158Seric 		}
9299339Seric 	}
93015256Seric 
93116158Seric 	/* open alias database */
93260537Seric 	initmaps(FALSE, e);
93316158Seric 
93416158Seric 	return (0);
9359339Seric }
9369339Seric 
93756795Seric # endif /* SMTP */
938