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*64685Seric static char sccsid[] = "@(#)srvrsmtp.c	8.16 (Berkeley) 09/30/93 (with SMTP)";
1433731Sbostic #else
15*64685Seric static char sccsid[] = "@(#)srvrsmtp.c	8.16 (Berkeley) 09/30/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 */
59*64685Seric /* use this to catch and log "door handle" attempts on your system */
60*64685Seric # 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,
86*64685Seric 	"wiz",		CMDLOGBOGUS,
874549Seric 	NULL,		CMDERROR,
884549Seric };
894549Seric 
909339Seric bool	InChild = FALSE;		/* true if running in a subprocess */
919378Seric bool	OneXact = FALSE;		/* one xaction only this run */
9211146Seric 
939339Seric #define EX_QUIT		22		/* special code for QUIT command */
948544Seric 
9563937Seric static char	*skipword();
9663937Seric 
9755012Seric smtp(e)
9855012Seric 	register ENVELOPE *e;
994549Seric {
1004549Seric 	register char *p;
1018544Seric 	register struct cmd *c;
1024549Seric 	char *cmd;
1035003Seric 	auto ADDRESS *vrfyqueue;
10412612Seric 	ADDRESS *a;
10558109Seric 	bool gotmail;			/* mail command received */
10658092Seric 	bool gothello;			/* helo command received */
10758092Seric 	bool vrfy;			/* set if this is a vrfy command */
10858323Seric 	char *protocol;			/* sending protocol */
10959016Seric 	char *sendinghost;		/* sending hostname */
11058333Seric 	long msize;			/* approximate maximum message size */
11158333Seric 	auto char *delimptr;
11258714Seric 	char *id;
11359747Seric 	int nrcpts;			/* number of RCPT commands */
11463787Seric 	bool doublequeue;
1158544Seric 	char inp[MAXLINE];
11657232Seric 	char cmdbuf[MAXLINE];
1177124Seric 	extern char Version[];
11824943Seric 	extern ENVELOPE BlankEnvelope;
1194549Seric 
12059066Seric 	if (fileno(OutChannel) != fileno(stdout))
1217363Seric 	{
1227363Seric 		/* arrange for debugging output to go to remote host */
12359066Seric 		(void) dup2(fileno(OutChannel), fileno(stdout));
1247363Seric 	}
12555012Seric 	settime(e);
12657642Seric 	CurHostName = RealHostName;
12761093Seric 	setproctitle("server %s startup", CurHostName);
12858050Seric 	expand("\201e", inp, &inp[sizeof inp], e);
12964496Seric 	if (BrokenSmtpPeers)
13064496Seric 	{
13164496Seric 		message("220 %s", inp);
13264496Seric 	}
13364496Seric 	else
13464496Seric 	{
13564496Seric 		message("220-%s", inp);
13664496Seric 		message("220 ESMTP spoken here");
13764496Seric 	}
13858330Seric 	protocol = NULL;
13959016Seric 	sendinghost = macvalue('s', e);
14058082Seric 	gothello = FALSE;
14158330Seric 	gotmail = FALSE;
1424549Seric 	for (;;)
1434549Seric 	{
14412612Seric 		/* arrange for backout */
14512612Seric 		if (setjmp(TopFrame) > 0 && InChild)
14659058Seric 		{
14759058Seric 			QuickAbort = FALSE;
14859058Seric 			SuprErrs = TRUE;
14912612Seric 			finis();
15059058Seric 		}
15112612Seric 		QuickAbort = FALSE;
15212612Seric 		HoldErrs = FALSE;
15351951Seric 		LogUsrErrs = FALSE;
15463843Seric 		e->e_flags &= ~(EF_VRFYONLY|EF_GLOBALERRS);
15512612Seric 
1567356Seric 		/* setup for the read */
15755012Seric 		e->e_to = NULL;
1584577Seric 		Errors = 0;
1597275Seric 		(void) fflush(stdout);
1607356Seric 
1617356Seric 		/* read the input line */
16261093Seric 		SmtpPhase = "server cmd read";
16361093Seric 		setproctitle("server %s cmd read", CurHostName);
16461093Seric 		p = sfgets(inp, sizeof inp, InChannel, TimeOuts.to_nextcommand,
16561093Seric 				SmtpPhase);
1667356Seric 
1677685Seric 		/* handle errors */
1687356Seric 		if (p == NULL)
1697356Seric 		{
1704549Seric 			/* end of file, just die */
17158151Seric 			message("421 %s Lost input channel from %s",
17225050Seric 				MyHostName, CurHostName);
17355464Seric #ifdef LOG
17463843Seric 			if (LogLevel > (gotmail ? 1 : 19))
17555464Seric 				syslog(LOG_NOTICE, "lost input channel from %s",
17655464Seric 					CurHostName);
17755464Seric #endif
17858069Seric 			if (InChild)
17958069Seric 				ExitStat = EX_QUIT;
1804549Seric 			finis();
1814549Seric 		}
1824549Seric 
1834549Seric 		/* clean up end of line */
1844558Seric 		fixcrlf(inp, TRUE);
1854549Seric 
1864713Seric 		/* echo command to transcript */
18755012Seric 		if (e->e_xfp != NULL)
18855012Seric 			fprintf(e->e_xfp, "<<< %s\n", inp);
1894713Seric 
19059060Seric 		if (e->e_id == NULL)
19159060Seric 			setproctitle("%s: %s", CurHostName, inp);
19259060Seric 		else
19359060Seric 			setproctitle("%s %s: %s", e->e_id, CurHostName, inp);
19459060Seric 
1954549Seric 		/* break off command */
19658050Seric 		for (p = inp; isascii(*p) && isspace(*p); p++)
1974549Seric 			continue;
19857232Seric 		cmd = cmdbuf;
19958050Seric 		while (*p != '\0' &&
20058050Seric 		       !(isascii(*p) && isspace(*p)) &&
20158050Seric 		       cmd < &cmdbuf[sizeof cmdbuf - 2])
20224981Seric 			*cmd++ = *p++;
20324981Seric 		*cmd = '\0';
2044549Seric 
20525691Seric 		/* throw away leading whitespace */
20658050Seric 		while (isascii(*p) && isspace(*p))
20725691Seric 			p++;
20825691Seric 
2094549Seric 		/* decode command */
2104549Seric 		for (c = CmdTab; c->cmdname != NULL; c++)
2114549Seric 		{
21233725Sbostic 			if (!strcasecmp(c->cmdname, cmdbuf))
2134549Seric 				break;
2144549Seric 		}
2154549Seric 
21651954Seric 		/* reset errors */
21751954Seric 		errno = 0;
21851954Seric 
2194549Seric 		/* process command */
2204549Seric 		switch (c->cmdcode)
2214549Seric 		{
2224976Seric 		  case CMDHELO:		/* hello -- introduce yourself */
22358323Seric 		  case CMDEHLO:		/* extended hello */
22458323Seric 			if (c->cmdcode == CMDEHLO)
22558323Seric 			{
22658323Seric 				protocol = "ESMTP";
22761093Seric 				SmtpPhase = "server EHLO";
22858323Seric 			}
22958323Seric 			else
23058323Seric 			{
23158323Seric 				protocol = "SMTP";
23261093Seric 				SmtpPhase = "server HELO";
23358323Seric 			}
23459016Seric 			sendinghost = newstr(p);
23564284Seric 			if (strcasecmp(p, RealHostName) != 0 &&
23664284Seric 			    (strcasecmp(RealHostName, "localhost") != 0 ||
23764284Seric 			     strcasecmp(p, MyHostName) != 0))
23811146Seric 			{
23958789Seric 				auth_warning(e, "Host %s claimed to be %s",
24058789Seric 					RealHostName, p);
24111146Seric 			}
24258957Seric 			p = macvalue('_', e);
24358957Seric 			if (p == NULL)
24459016Seric 				p = RealHostName;
24558323Seric 
24660210Seric 			gothello = TRUE;
24760210Seric 			if (c->cmdcode != CMDEHLO)
24860239Seric 			{
24960239Seric 				/* print old message and be done with it */
25060239Seric 				message("250 %s Hello %s, pleased to meet you",
25160239Seric 					MyHostName, p);
25260210Seric 				break;
25360239Seric 			}
25460239Seric 
25560239Seric 			/* print extended message and brag */
25660239Seric 			message("250-%s Hello %s, pleased to meet you",
25760239Seric 				MyHostName, p);
25858323Seric 			if (!bitset(PRIV_NOEXPN, PrivacyFlags))
25958323Seric 				message("250-EXPN");
26064359Seric 			if (MaxMessageSize > 0)
26164359Seric 				message("250-SIZE %ld", MaxMessageSize);
26259271Seric 			else
26359271Seric 				message("250-SIZE");
26458323Seric 			message("250 HELP");
2654976Seric 			break;
2664976Seric 
2674549Seric 		  case CMDMAIL:		/* mail -- designate sender */
26861093Seric 			SmtpPhase = "server MAIL";
26924943Seric 
2709314Seric 			/* check for validity of this command */
27158789Seric 			if (!gothello)
27258082Seric 			{
27358957Seric 				/* set sending host to our known value */
27459016Seric 				if (sendinghost == NULL)
27559016Seric 					sendinghost = RealHostName;
27658957Seric 
27758789Seric 				if (bitset(PRIV_NEEDMAILHELO, PrivacyFlags))
27858821Seric 				{
27958789Seric 					message("503 Polite people say HELO first");
28058821Seric 					break;
28158821Seric 				}
28258082Seric 			}
28358109Seric 			if (gotmail)
2844558Seric 			{
28558151Seric 				message("503 Sender already specified");
28663843Seric 				if (InChild)
28763843Seric 					finis();
2884558Seric 				break;
2894558Seric 			}
2909339Seric 			if (InChild)
2919339Seric 			{
29236230Skarels 				errno = 0;
29358151Seric 				syserr("503 Nested MAIL command: MAIL %s", p);
29458069Seric 				finis();
2959339Seric 			}
2969339Seric 
2979339Seric 			/* fork a subprocess to process this command */
29855012Seric 			if (runinchild("SMTP-MAIL", e) > 0)
2999339Seric 				break;
30063753Seric 			if (!gothello)
30163753Seric 			{
30263753Seric 				auth_warning(e,
30363753Seric 					"Host %s didn't use HELO protocol",
30463753Seric 					RealHostName);
30563753Seric 			}
30658323Seric 			if (protocol == NULL)
30758323Seric 				protocol = "SMTP";
30858323Seric 			define('r', protocol, e);
30959016Seric 			define('s', sendinghost, e);
31055012Seric 			initsys(e);
31159747Seric 			nrcpts = 0;
31257389Seric 			setproctitle("%s %s: %s", e->e_id, CurHostName, inp);
3139339Seric 
3149339Seric 			/* child -- go do the processing */
3154549Seric 			p = skipword(p, "from");
3164549Seric 			if (p == NULL)
3174549Seric 				break;
31857977Seric 			if (setjmp(TopFrame) > 0)
31958147Seric 			{
32058147Seric 				/* this failed -- undo work */
32158147Seric 				if (InChild)
32259058Seric 				{
32359058Seric 					QuickAbort = FALSE;
32459058Seric 					SuprErrs = TRUE;
32563787Seric 					e->e_flags &= ~EF_FATALERRS;
32658147Seric 					finis();
32759058Seric 				}
32857977Seric 				break;
32958147Seric 			}
33057977Seric 			QuickAbort = TRUE;
33158333Seric 
33258333Seric 			/* must parse sender first */
33358333Seric 			delimptr = NULL;
33458704Seric 			setsender(p, e, &delimptr, FALSE);
33558333Seric 			p = delimptr;
33658333Seric 			if (p != NULL && *p != '\0')
33758333Seric 				*p++ = '\0';
33858333Seric 
33958333Seric 			/* now parse ESMTP arguments */
34058333Seric 			msize = 0;
34158333Seric 			for (; p != NULL && *p != '\0'; p++)
34258333Seric 			{
34358333Seric 				char *kp;
34458333Seric 				char *vp;
34558333Seric 
34658333Seric 				/* locate the beginning of the keyword */
34758333Seric 				while (isascii(*p) && isspace(*p))
34858333Seric 					p++;
34958333Seric 				if (*p == '\0')
35058333Seric 					break;
35158333Seric 				kp = p;
35258333Seric 
35358333Seric 				/* skip to the value portion */
35458333Seric 				while (isascii(*p) && isalnum(*p) || *p == '-')
35558333Seric 					p++;
35658333Seric 				if (*p == '=')
35758333Seric 				{
35858333Seric 					*p++ = '\0';
35958333Seric 					vp = p;
36058333Seric 
36158333Seric 					/* skip to the end of the value */
36258333Seric 					while (*p != '\0' && *p != ' ' &&
36358333Seric 					       !(isascii(*p) && iscntrl(*p)) &&
36458333Seric 					       *p != '=')
36558333Seric 						p++;
36658333Seric 				}
36758333Seric 
36858333Seric 				if (*p != '\0')
36958333Seric 					*p++ = '\0';
37058333Seric 
37158333Seric 				if (tTd(19, 1))
37258333Seric 					printf("MAIL: got arg %s=%s\n", kp,
37358333Seric 						vp == NULL ? "<null>" : vp);
37458333Seric 
37558333Seric 				if (strcasecmp(kp, "size") == 0)
37658333Seric 				{
37759093Seric 					if (vp == NULL)
37858333Seric 					{
37958333Seric 						usrerr("501 SIZE requires a value");
38058333Seric 						/* NOTREACHED */
38158333Seric 					}
38258333Seric 					msize = atol(vp);
38358333Seric 				}
38459093Seric 				else if (strcasecmp(kp, "body") == 0)
38559093Seric 				{
38659093Seric 					if (vp == NULL)
38759093Seric 					{
38859093Seric 						usrerr("501 BODY requires a value");
38959093Seric 						/* NOTREACHED */
39059093Seric 					}
39159093Seric # ifdef MIME
39259093Seric 					if (strcasecmp(vp, "8bitmime") == 0)
39359093Seric 					{
39459093Seric 						e->e_bodytype = "8BITMIME";
39559709Seric 						SevenBit = FALSE;
39659093Seric 					}
39759093Seric 					else if (strcasecmp(vp, "7bit") == 0)
39859093Seric 					{
39959093Seric 						e->e_bodytype = "7BIT";
40059709Seric 						SevenBit = TRUE;
40159093Seric 					}
40259093Seric 					else
40359093Seric 					{
40459093Seric 						usrerr("501 Unknown BODY type %s",
40559093Seric 							vp);
40659093Seric 					}
40759093Seric # endif
40859093Seric 				}
40958333Seric 				else
41058333Seric 				{
41158333Seric 					usrerr("501 %s parameter unrecognized", kp);
41258333Seric 					/* NOTREACHED */
41358333Seric 				}
41458333Seric 			}
41559284Seric 
41659284Seric 			if (MaxMessageSize > 0 && msize > MaxMessageSize)
41759284Seric 			{
41859284Seric 				usrerr("552 Message size exceeds fixed maximum message size (%ld)",
41959284Seric 					MaxMessageSize);
42059284Seric 				/* NOTREACHED */
42159284Seric 			}
42258333Seric 
42358333Seric 			if (!enoughspace(msize))
42458333Seric 			{
42558333Seric 				message("452 Insufficient disk space; try again later");
42658333Seric 				break;
42758333Seric 			}
42858151Seric 			message("250 Sender ok");
42958147Seric 			gotmail = TRUE;
4304549Seric 			break;
4314549Seric 
4324976Seric 		  case CMDRCPT:		/* rcpt -- designate recipient */
43358850Seric 			if (!gotmail)
43458850Seric 			{
43558850Seric 				usrerr("503 Need MAIL before RCPT");
43658850Seric 				break;
43758850Seric 			}
43861093Seric 			SmtpPhase = "server RCPT";
43912612Seric 			if (setjmp(TopFrame) > 0)
44014785Seric 			{
44155012Seric 				e->e_flags &= ~EF_FATALERRS;
44212612Seric 				break;
44314785Seric 			}
44412612Seric 			QuickAbort = TRUE;
44551951Seric 			LogUsrErrs = TRUE;
44658093Seric 
44759699Seric 			if (e->e_sendmode != SM_DELIVER)
44859699Seric 				e->e_flags |= EF_VRFYONLY;
44958919Seric 
4504549Seric 			p = skipword(p, "to");
4514549Seric 			if (p == NULL)
4524549Seric 				break;
45364284Seric 			a = parseaddr(p, NULLADDR, RF_COPYALL, ' ', NULL, e);
45412612Seric 			if (a == NULL)
45512612Seric 				break;
45616886Seric 			a->q_flags |= QPRIMARY;
45755012Seric 			a = recipient(a, &e->e_sendqueue, e);
45812612Seric 			if (Errors != 0)
45912612Seric 				break;
46012612Seric 
46112612Seric 			/* no errors during parsing, but might be a duplicate */
46255012Seric 			e->e_to = p;
46312612Seric 			if (!bitset(QBADADDR, a->q_flags))
46459747Seric 			{
46558151Seric 				message("250 Recipient ok");
46659747Seric 				nrcpts++;
46759747Seric 			}
46812612Seric 			else
4694549Seric 			{
47012612Seric 				/* punt -- should keep message in ADDRESS.... */
47158151Seric 				message("550 Addressee unknown");
4724549Seric 			}
47355012Seric 			e->e_to = NULL;
4744549Seric 			break;
4754549Seric 
4764549Seric 		  case CMDDATA:		/* data -- text of mail */
47761093Seric 			SmtpPhase = "server DATA";
47858109Seric 			if (!gotmail)
4794549Seric 			{
48058151Seric 				message("503 Need MAIL command");
4814976Seric 				break;
4824549Seric 			}
48355012Seric 			else if (e->e_nrcpts <= 0)
4844549Seric 			{
48558151Seric 				message("503 Need RCPT (recipient)");
4864976Seric 				break;
4874549Seric 			}
4884976Seric 
48958929Seric 			/* check to see if we need to re-expand aliases */
49063787Seric 			/* also reset QBADADDR on already-diagnosted addrs */
49163787Seric 			doublequeue = FALSE;
49258929Seric 			for (a = e->e_sendqueue; a != NULL; a = a->q_next)
49358929Seric 			{
49458929Seric 				if (bitset(QVERIFIED, a->q_flags))
49563787Seric 				{
49663787Seric 					/* need to re-expand aliases */
49763787Seric 					doublequeue = TRUE;
49863787Seric 				}
49963787Seric 				if (bitset(QBADADDR, a->q_flags))
50063787Seric 				{
50163787Seric 					/* make this "go away" */
50263787Seric 					a->q_flags |= QDONTSEND;
50363787Seric 					a->q_flags &= ~QBADADDR;
50463787Seric 				}
50558929Seric 			}
50658929Seric 
5074976Seric 			/* collect the text of the message */
50824943Seric 			SmtpPhase = "collect";
50963965Seric 			HoldErrs = TRUE;
51063787Seric 			collect(TRUE, doublequeue, e);
5114976Seric 
5128238Seric 			/*
5138238Seric 			**  Arrange to send to everyone.
5148238Seric 			**	If sending to multiple people, mail back
5158238Seric 			**		errors rather than reporting directly.
5168238Seric 			**	In any case, don't mail back errors for
5178238Seric 			**		anything that has happened up to
5188238Seric 			**		now (the other end will do this).
51910197Seric 			**	Truncate our transcript -- the mail has gotten
52010197Seric 			**		to us successfully, and if we have
52110197Seric 			**		to mail this back, it will be easier
52210197Seric 			**		on the reader.
5238238Seric 			**	Then send to everyone.
5248238Seric 			**	Finally give a reply code.  If an error has
5258238Seric 			**		already been given, don't mail a
5268238Seric 			**		message back.
5279339Seric 			**	We goose error returns by clearing error bit.
5288238Seric 			*/
5298238Seric 
53024943Seric 			SmtpPhase = "delivery";
53163787Seric 			if (nrcpts != 1 && !doublequeue)
5329378Seric 			{
5339378Seric 				HoldErrs = TRUE;
53458734Seric 				e->e_errormode = EM_MAIL;
5359378Seric 			}
53655012Seric 			e->e_xfp = freopen(queuename(e, 'x'), "w", e->e_xfp);
53758714Seric 			id = e->e_id;
5384976Seric 
5394976Seric 			/* send to all recipients */
54063787Seric 			sendall(e, doublequeue ? SM_QUEUE : SM_DEFAULT);
54155012Seric 			e->e_to = NULL;
5424976Seric 
5438238Seric 			/* issue success if appropriate and reset */
5448238Seric 			if (Errors == 0 || HoldErrs)
54558855Seric 				message("250 %s Message accepted for delivery", id);
54659747Seric 
54759747Seric 			if (bitset(EF_FATALERRS, e->e_flags) && !HoldErrs)
54859730Seric 			{
54959730Seric 				/* avoid sending back an extra message */
55059730Seric 				e->e_flags &= ~EF_FATALERRS;
55159747Seric 				e->e_flags |= EF_CLRQUEUE;
55259730Seric 			}
5538238Seric 			else
55458919Seric 			{
55559747Seric 				/* from now on, we have to operate silently */
55659747Seric 				HoldErrs = TRUE;
55759747Seric 				e->e_errormode = EM_MAIL;
55859747Seric 
55959730Seric 				/* if we just queued, poke it */
56063787Seric 				if (doublequeue && e->e_sendmode != SM_QUEUE)
56159730Seric 				{
56264296Seric 					extern pid_t dowork();
56364296Seric 
56459730Seric 					unlockqueue(e);
56564296Seric 					(void) dowork(id, TRUE, TRUE, e);
56659730Seric 				}
56758919Seric 			}
56858883Seric 
56959747Seric   abortmessage:
5709339Seric 			/* if in a child, pop back to our parent */
5719339Seric 			if (InChild)
5729339Seric 				finis();
57324943Seric 
57424943Seric 			/* clean up a bit */
57558109Seric 			gotmail = FALSE;
57655012Seric 			dropenvelope(e);
57758179Seric 			CurEnv = e = newenvelope(e, CurEnv);
57855012Seric 			e->e_flags = BlankEnvelope.e_flags;
5794549Seric 			break;
5804549Seric 
5814549Seric 		  case CMDRSET:		/* rset -- reset state */
58258151Seric 			message("250 Reset state");
58364359Seric 			e->e_flags |= EF_CLRQUEUE;
5849339Seric 			if (InChild)
5859339Seric 				finis();
58658109Seric 
58758109Seric 			/* clean up a bit */
58858109Seric 			gotmail = FALSE;
58958109Seric 			dropenvelope(e);
59058179Seric 			CurEnv = e = newenvelope(e, CurEnv);
5919339Seric 			break;
5924549Seric 
5934549Seric 		  case CMDVRFY:		/* vrfy -- verify address */
59458092Seric 		  case CMDEXPN:		/* expn -- expand address */
59558092Seric 			vrfy = c->cmdcode == CMDVRFY;
59658092Seric 			if (bitset(vrfy ? PRIV_NOVRFY : PRIV_NOEXPN,
59758092Seric 						PrivacyFlags))
59858082Seric 			{
59958412Seric 				if (vrfy)
60058412Seric 					message("252 Who's to say?");
60158412Seric 				else
60258412Seric 					message("502 That's none of your business");
60358082Seric 				break;
60458082Seric 			}
60558082Seric 			else if (!gothello &&
60658092Seric 				 bitset(vrfy ? PRIV_NEEDVRFYHELO : PRIV_NEEDEXPNHELO,
60758092Seric 						PrivacyFlags))
60858082Seric 			{
60958151Seric 				message("503 I demand that you introduce yourself first");
61058082Seric 				break;
61158082Seric 			}
61258092Seric 			if (runinchild(vrfy ? "SMTP-VRFY" : "SMTP-EXPN", e) > 0)
6139339Seric 				break;
61455173Seric #ifdef LOG
61558020Seric 			if (LogLevel > 5)
61655173Seric 				syslog(LOG_INFO, "%s: %s", CurHostName, inp);
61755173Seric #endif
6185003Seric 			vrfyqueue = NULL;
6197762Seric 			QuickAbort = TRUE;
62058092Seric 			if (vrfy)
62158092Seric 				e->e_flags |= EF_VRFYONLY;
62262373Seric 			while (*p != '\0' && isascii(*p) && isspace(*p))
62362373Seric 				*p++;
62462373Seric 			if (*p == '\0')
62562373Seric 			{
62662373Seric 				message("501 Argument required");
62762373Seric 				Errors++;
62862373Seric 			}
62962373Seric 			else
63062373Seric 			{
63164284Seric 				(void) sendtolist(p, NULLADDR, &vrfyqueue, e);
63262373Seric 			}
6337762Seric 			if (Errors != 0)
6349339Seric 			{
6359339Seric 				if (InChild)
6369339Seric 					finis();
6377762Seric 				break;
6389339Seric 			}
63962373Seric 			if (vrfyqueue == NULL)
64062373Seric 			{
64162373Seric 				message("554 Nothing to %s", vrfy ? "VRFY" : "EXPN");
64262373Seric 			}
6435003Seric 			while (vrfyqueue != NULL)
6445003Seric 			{
64563971Seric 				a = vrfyqueue;
64663971Seric 				while ((a = a->q_next) != NULL &&
64763971Seric 				       bitset(QDONTSEND|QBADADDR, a->q_flags))
64863971Seric 					continue;
6497685Seric 				if (!bitset(QDONTSEND|QBADADDR, vrfyqueue->q_flags))
65058151Seric 					printvrfyaddr(vrfyqueue, a == NULL);
65163847Seric 				vrfyqueue = vrfyqueue->q_next;
6525003Seric 			}
6539339Seric 			if (InChild)
6549339Seric 				finis();
6554549Seric 			break;
6564549Seric 
6574549Seric 		  case CMDHELP:		/* help -- give user info */
6584577Seric 			help(p);
6594549Seric 			break;
6604549Seric 
6614549Seric 		  case CMDNOOP:		/* noop -- do nothing */
66264122Seric 			message("250 OK");
6634549Seric 			break;
6644549Seric 
6654549Seric 		  case CMDQUIT:		/* quit -- leave mail */
66658151Seric 			message("221 %s closing connection", MyHostName);
66761051Seric 
66861051Seric 			/* avoid future 050 messages */
66961051Seric 			Verbose = FALSE;
67061051Seric 
6719339Seric 			if (InChild)
6729339Seric 				ExitStat = EX_QUIT;
6734549Seric 			finis();
6744549Seric 
6758544Seric 		  case CMDVERB:		/* set verbose mode */
67659957Seric 			if (bitset(PRIV_NOEXPN, PrivacyFlags))
67759957Seric 			{
67859957Seric 				/* this would give out the same info */
67959957Seric 				message("502 Verbose unavailable");
68059957Seric 				break;
68159957Seric 			}
6828544Seric 			Verbose = TRUE;
68358734Seric 			e->e_sendmode = SM_DELIVER;
68459957Seric 			message("250 Verbose mode");
6858544Seric 			break;
6868544Seric 
6879314Seric 		  case CMDONEX:		/* doing one transaction only */
6889378Seric 			OneXact = TRUE;
68959957Seric 			message("250 Only one transaction");
6909314Seric 			break;
6919314Seric 
69236230Skarels # ifdef SMTPDEBUG
6939339Seric 		  case CMDDBGQSHOW:	/* show queues */
6946907Seric 			printf("Send Queue=");
69555012Seric 			printaddr(e->e_sendqueue, TRUE);
6965003Seric 			break;
6977275Seric 
6987275Seric 		  case CMDDBGDEBUG:	/* set debug mode */
6997676Seric 			tTsetup(tTdvect, sizeof tTdvect, "0-99.1");
7007676Seric 			tTflag(p);
70158151Seric 			message("200 Debug set");
7027275Seric 			break;
7037275Seric 
70436230Skarels # else /* not SMTPDEBUG */
70536230Skarels 		  case CMDDBGQSHOW:	/* show queues */
70636230Skarels 		  case CMDDBGDEBUG:	/* set debug mode */
707*64685Seric # endif /* SMTPDEBUG */
708*64685Seric 		  case CMDLOGBOGUS:	/* bogus command */
70936233Skarels # ifdef LOG
71058308Seric 			if (LogLevel > 0)
711*64685Seric 				syslog(LOG_CRIT,
71258020Seric 				    "\"%s\" command from %s (%s)",
71336230Skarels 				    c->cmdname, RealHostName,
71458755Seric 				    anynet_ntoa(&RealHostAddr));
71536233Skarels # endif
71636230Skarels 			/* FALL THROUGH */
71736230Skarels 
7184549Seric 		  case CMDERROR:	/* unknown command */
71958151Seric 			message("500 Command unrecognized");
7204549Seric 			break;
7214549Seric 
7224549Seric 		  default:
72336230Skarels 			errno = 0;
72458151Seric 			syserr("500 smtp: unknown code %d", c->cmdcode);
7254549Seric 			break;
7264549Seric 		}
7274549Seric 	}
7284549Seric }
7294549Seric /*
7304549Seric **  SKIPWORD -- skip a fixed word.
7314549Seric **
7324549Seric **	Parameters:
7334549Seric **		p -- place to start looking.
7344549Seric **		w -- word to skip.
7354549Seric **
7364549Seric **	Returns:
7374549Seric **		p following w.
7384549Seric **		NULL on error.
7394549Seric **
7404549Seric **	Side Effects:
7414549Seric **		clobbers the p data area.
7424549Seric */
7434549Seric 
7444549Seric static char *
7454549Seric skipword(p, w)
7464549Seric 	register char *p;
7474549Seric 	char *w;
7484549Seric {
7494549Seric 	register char *q;
7504549Seric 
7514549Seric 	/* find beginning of word */
75258050Seric 	while (isascii(*p) && isspace(*p))
7534549Seric 		p++;
7544549Seric 	q = p;
7554549Seric 
7564549Seric 	/* find end of word */
75758050Seric 	while (*p != '\0' && *p != ':' && !(isascii(*p) && isspace(*p)))
7584549Seric 		p++;
75958050Seric 	while (isascii(*p) && isspace(*p))
7604549Seric 		*p++ = '\0';
7614549Seric 	if (*p != ':')
7624549Seric 	{
7634549Seric 	  syntax:
76462373Seric 		message("501 Syntax error in parameters");
7654549Seric 		Errors++;
7664549Seric 		return (NULL);
7674549Seric 	}
7684549Seric 	*p++ = '\0';
76958050Seric 	while (isascii(*p) && isspace(*p))
7704549Seric 		p++;
7714549Seric 
77262373Seric 	if (*p == '\0')
77362373Seric 		goto syntax;
77462373Seric 
7754549Seric 	/* see if the input word matches desired word */
77633725Sbostic 	if (strcasecmp(q, w))
7774549Seric 		goto syntax;
7784549Seric 
7794549Seric 	return (p);
7804549Seric }
7814577Seric /*
78258151Seric **  PRINTVRFYADDR -- print an entry in the verify queue
78358151Seric **
78458151Seric **	Parameters:
78558151Seric **		a -- the address to print
78658151Seric **		last -- set if this is the last one.
78758151Seric **
78858151Seric **	Returns:
78958151Seric **		none.
79058151Seric **
79158151Seric **	Side Effects:
79258151Seric **		Prints the appropriate 250 codes.
79358151Seric */
79458151Seric 
79558151Seric printvrfyaddr(a, last)
79658151Seric 	register ADDRESS *a;
79758151Seric 	bool last;
79858151Seric {
79958151Seric 	char fmtbuf[20];
80058151Seric 
80158151Seric 	strcpy(fmtbuf, "250");
80258151Seric 	fmtbuf[3] = last ? ' ' : '-';
80358151Seric 
80459746Seric 	if (a->q_fullname == NULL)
80559746Seric 	{
80659746Seric 		if (strchr(a->q_user, '@') == NULL)
80759746Seric 			strcpy(&fmtbuf[4], "<%s@%s>");
80859746Seric 		else
80959746Seric 			strcpy(&fmtbuf[4], "<%s>");
81059746Seric 		message(fmtbuf, a->q_user, MyHostName);
81159746Seric 	}
81258151Seric 	else
81358151Seric 	{
81459746Seric 		if (strchr(a->q_user, '@') == NULL)
81559746Seric 			strcpy(&fmtbuf[4], "%s <%s@%s>");
81659746Seric 		else
81759746Seric 			strcpy(&fmtbuf[4], "%s <%s>");
81859746Seric 		message(fmtbuf, a->q_fullname, a->q_user, MyHostName);
81958151Seric 	}
82058151Seric }
82158151Seric /*
8224577Seric **  HELP -- implement the HELP command.
8234577Seric **
8244577Seric **	Parameters:
8254577Seric **		topic -- the topic we want help for.
8264577Seric **
8274577Seric **	Returns:
8284577Seric **		none.
8294577Seric **
8304577Seric **	Side Effects:
8314577Seric **		outputs the help file to message output.
8324577Seric */
8334577Seric 
8344577Seric help(topic)
8354577Seric 	char *topic;
8364577Seric {
8374577Seric 	register FILE *hf;
8384577Seric 	int len;
8394577Seric 	char buf[MAXLINE];
8404577Seric 	bool noinfo;
8414577Seric 
8428269Seric 	if (HelpFile == NULL || (hf = fopen(HelpFile, "r")) == NULL)
8434577Seric 	{
8444577Seric 		/* no help */
84511931Seric 		errno = 0;
84658151Seric 		message("502 HELP not implemented");
8474577Seric 		return;
8484577Seric 	}
8494577Seric 
85049669Seric 	if (topic == NULL || *topic == '\0')
85149669Seric 		topic = "smtp";
85249669Seric 	else
85349669Seric 		makelower(topic);
85449669Seric 
8554577Seric 	len = strlen(topic);
8564577Seric 	noinfo = TRUE;
8574577Seric 
8584577Seric 	while (fgets(buf, sizeof buf, hf) != NULL)
8594577Seric 	{
8604577Seric 		if (strncmp(buf, topic, len) == 0)
8614577Seric 		{
8624577Seric 			register char *p;
8634577Seric 
86456795Seric 			p = strchr(buf, '\t');
8654577Seric 			if (p == NULL)
8664577Seric 				p = buf;
8674577Seric 			else
8684577Seric 				p++;
8694577Seric 			fixcrlf(p, TRUE);
87058151Seric 			message("214-%s", p);
8714577Seric 			noinfo = FALSE;
8724577Seric 		}
8734577Seric 	}
8744577Seric 
8754577Seric 	if (noinfo)
87658151Seric 		message("504 HELP topic unknown");
8774577Seric 	else
87858151Seric 		message("214 End of HELP info");
8794628Seric 	(void) fclose(hf);
8804577Seric }
8818544Seric /*
8829339Seric **  RUNINCHILD -- return twice -- once in the child, then in the parent again
8839339Seric **
8849339Seric **	Parameters:
8859339Seric **		label -- a string used in error messages
8869339Seric **
8879339Seric **	Returns:
8889339Seric **		zero in the child
8899339Seric **		one in the parent
8909339Seric **
8919339Seric **	Side Effects:
8929339Seric **		none.
8939339Seric */
8948544Seric 
89555012Seric runinchild(label, e)
8969339Seric 	char *label;
89755012Seric 	register ENVELOPE *e;
8989339Seric {
8999339Seric 	int childpid;
9009339Seric 
90116158Seric 	if (!OneXact)
9029339Seric 	{
90316158Seric 		childpid = dofork();
90416158Seric 		if (childpid < 0)
90516158Seric 		{
90616158Seric 			syserr("%s: cannot fork", label);
90716158Seric 			return (1);
90816158Seric 		}
90916158Seric 		if (childpid > 0)
91016158Seric 		{
91116158Seric 			auto int st;
9129339Seric 
91316158Seric 			/* parent -- wait for child to complete */
91461093Seric 			setproctitle("server %s child wait", CurHostName);
91516158Seric 			st = waitfor(childpid);
91616158Seric 			if (st == -1)
91716158Seric 				syserr("%s: lost child", label);
9189339Seric 
91916158Seric 			/* if we exited on a QUIT command, complete the process */
92016158Seric 			if (st == (EX_QUIT << 8))
92116158Seric 				finis();
9229339Seric 
92316158Seric 			return (1);
92416158Seric 		}
92516158Seric 		else
92616158Seric 		{
92716158Seric 			/* child */
92816158Seric 			InChild = TRUE;
92925050Seric 			QuickAbort = FALSE;
93055012Seric 			clearenvelope(e, FALSE);
93116158Seric 		}
9329339Seric 	}
93315256Seric 
93416158Seric 	/* open alias database */
93560537Seric 	initmaps(FALSE, e);
93616158Seric 
93716158Seric 	return (0);
9389339Seric }
9399339Seric 
94056795Seric # endif /* SMTP */
941