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*66005Seric static char sccsid[] = "@(#)srvrsmtp.c	8.28 (Berkeley) 02/05/94 (with SMTP)";
1433731Sbostic #else
15*66005Seric static char sccsid[] = "@(#)srvrsmtp.c	8.28 (Berkeley) 02/05/94 (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 */
9165017Seric char	*CurSmtpClient;			/* who's at the other end of channel */
9211146Seric 
9363937Seric static char	*skipword();
9463937Seric 
9555012Seric smtp(e)
9655012Seric 	register ENVELOPE *e;
974549Seric {
984549Seric 	register char *p;
998544Seric 	register struct cmd *c;
1004549Seric 	char *cmd;
1015003Seric 	auto ADDRESS *vrfyqueue;
10212612Seric 	ADDRESS *a;
10358109Seric 	bool gotmail;			/* mail command received */
10458092Seric 	bool gothello;			/* helo command received */
10558092Seric 	bool vrfy;			/* set if this is a vrfy command */
10658323Seric 	char *protocol;			/* sending protocol */
10759016Seric 	char *sendinghost;		/* sending hostname */
10858333Seric 	long msize;			/* approximate maximum message size */
109*66005Seric 	char *peerhostname;		/* name of SMTP peer or "localhost" */
11058333Seric 	auto char *delimptr;
11158714Seric 	char *id;
11259747Seric 	int nrcpts;			/* number of RCPT commands */
11363787Seric 	bool doublequeue;
1148544Seric 	char inp[MAXLINE];
11557232Seric 	char cmdbuf[MAXLINE];
1167124Seric 	extern char Version[];
11724943Seric 	extern ENVELOPE BlankEnvelope;
1184549Seric 
11959066Seric 	if (fileno(OutChannel) != fileno(stdout))
1207363Seric 	{
1217363Seric 		/* arrange for debugging output to go to remote host */
12259066Seric 		(void) dup2(fileno(OutChannel), fileno(stdout));
1237363Seric 	}
12455012Seric 	settime(e);
125*66005Seric 	peerhostname = RealHostName;
126*66005Seric 	if (peerhostname == NULL)
127*66005Seric 		peerhostname = "localhost";
128*66005Seric 	CurHostName = peerhostname;
12965017Seric 	CurSmtpClient = macvalue('_', e);
13065017Seric 	if (CurSmtpClient == NULL)
13166003Seric 		CurSmtpClient = CurHostName;
13265017Seric 
13365017Seric 	setproctitle("server %s startup", CurSmtpClient);
13458050Seric 	expand("\201e", inp, &inp[sizeof inp], e);
13564496Seric 	if (BrokenSmtpPeers)
13664496Seric 	{
13764496Seric 		message("220 %s", inp);
13864496Seric 	}
13964496Seric 	else
14064496Seric 	{
14164496Seric 		message("220-%s", inp);
14264496Seric 		message("220 ESMTP spoken here");
14364496Seric 	}
14458330Seric 	protocol = NULL;
14559016Seric 	sendinghost = macvalue('s', e);
14658082Seric 	gothello = FALSE;
14758330Seric 	gotmail = FALSE;
1484549Seric 	for (;;)
1494549Seric 	{
15012612Seric 		/* arrange for backout */
15165751Seric 		if (setjmp(TopFrame) > 0)
15259058Seric 		{
15365751Seric 			/* if() nesting is necessary for Cray UNICOS */
15465751Seric 			if (InChild)
15565751Seric 			{
15665751Seric 				QuickAbort = FALSE;
15765751Seric 				SuprErrs = TRUE;
15865751Seric 				finis();
15965751Seric 			}
16059058Seric 		}
16112612Seric 		QuickAbort = FALSE;
16212612Seric 		HoldErrs = FALSE;
16351951Seric 		LogUsrErrs = FALSE;
16463843Seric 		e->e_flags &= ~(EF_VRFYONLY|EF_GLOBALERRS);
16512612Seric 
1667356Seric 		/* setup for the read */
16755012Seric 		e->e_to = NULL;
1684577Seric 		Errors = 0;
1697275Seric 		(void) fflush(stdout);
1707356Seric 
1717356Seric 		/* read the input line */
17261093Seric 		SmtpPhase = "server cmd read";
17361093Seric 		setproctitle("server %s cmd read", CurHostName);
17461093Seric 		p = sfgets(inp, sizeof inp, InChannel, TimeOuts.to_nextcommand,
17561093Seric 				SmtpPhase);
1767356Seric 
1777685Seric 		/* handle errors */
1787356Seric 		if (p == NULL)
1797356Seric 		{
1804549Seric 			/* end of file, just die */
18158151Seric 			message("421 %s Lost input channel from %s",
18265017Seric 				MyHostName, CurSmtpClient);
18355464Seric #ifdef LOG
18463843Seric 			if (LogLevel > (gotmail ? 1 : 19))
18555464Seric 				syslog(LOG_NOTICE, "lost input channel from %s",
18665017Seric 					CurSmtpClient);
18755464Seric #endif
18858069Seric 			if (InChild)
18958069Seric 				ExitStat = EX_QUIT;
1904549Seric 			finis();
1914549Seric 		}
1924549Seric 
1934549Seric 		/* clean up end of line */
1944558Seric 		fixcrlf(inp, TRUE);
1954549Seric 
1964713Seric 		/* echo command to transcript */
19755012Seric 		if (e->e_xfp != NULL)
19855012Seric 			fprintf(e->e_xfp, "<<< %s\n", inp);
1994713Seric 
20059060Seric 		if (e->e_id == NULL)
20165058Seric 			setproctitle("%s: %.80s", CurSmtpClient, inp);
20259060Seric 		else
20365058Seric 			setproctitle("%s %s: %.80s", e->e_id, CurSmtpClient, inp);
20459060Seric 
2054549Seric 		/* break off command */
20658050Seric 		for (p = inp; isascii(*p) && isspace(*p); p++)
2074549Seric 			continue;
20857232Seric 		cmd = cmdbuf;
20958050Seric 		while (*p != '\0' &&
21058050Seric 		       !(isascii(*p) && isspace(*p)) &&
21158050Seric 		       cmd < &cmdbuf[sizeof cmdbuf - 2])
21224981Seric 			*cmd++ = *p++;
21324981Seric 		*cmd = '\0';
2144549Seric 
21525691Seric 		/* throw away leading whitespace */
21658050Seric 		while (isascii(*p) && isspace(*p))
21725691Seric 			p++;
21825691Seric 
2194549Seric 		/* decode command */
2204549Seric 		for (c = CmdTab; c->cmdname != NULL; c++)
2214549Seric 		{
22233725Sbostic 			if (!strcasecmp(c->cmdname, cmdbuf))
2234549Seric 				break;
2244549Seric 		}
2254549Seric 
22651954Seric 		/* reset errors */
22751954Seric 		errno = 0;
22851954Seric 
2294549Seric 		/* process command */
2304549Seric 		switch (c->cmdcode)
2314549Seric 		{
2324976Seric 		  case CMDHELO:		/* hello -- introduce yourself */
23358323Seric 		  case CMDEHLO:		/* extended hello */
23458323Seric 			if (c->cmdcode == CMDEHLO)
23558323Seric 			{
23658323Seric 				protocol = "ESMTP";
23761093Seric 				SmtpPhase = "server EHLO";
23858323Seric 			}
23958323Seric 			else
24058323Seric 			{
24158323Seric 				protocol = "SMTP";
24261093Seric 				SmtpPhase = "server HELO";
24358323Seric 			}
24459016Seric 			sendinghost = newstr(p);
24560210Seric 			gothello = TRUE;
24660210Seric 			if (c->cmdcode != CMDEHLO)
24760239Seric 			{
24860239Seric 				/* print old message and be done with it */
24960239Seric 				message("250 %s Hello %s, pleased to meet you",
25065017Seric 					MyHostName, CurSmtpClient);
25160210Seric 				break;
25260239Seric 			}
25360239Seric 
25460239Seric 			/* print extended message and brag */
25560239Seric 			message("250-%s Hello %s, pleased to meet you",
25660239Seric 				MyHostName, p);
25758323Seric 			if (!bitset(PRIV_NOEXPN, PrivacyFlags))
25858323Seric 				message("250-EXPN");
25964359Seric 			if (MaxMessageSize > 0)
26064359Seric 				message("250-SIZE %ld", MaxMessageSize);
26159271Seric 			else
26259271Seric 				message("250-SIZE");
26358323Seric 			message("250 HELP");
2644976Seric 			break;
2654976Seric 
2664549Seric 		  case CMDMAIL:		/* mail -- designate sender */
26761093Seric 			SmtpPhase = "server MAIL";
26824943Seric 
2699314Seric 			/* check for validity of this command */
27058789Seric 			if (!gothello)
27158082Seric 			{
27258957Seric 				/* set sending host to our known value */
27359016Seric 				if (sendinghost == NULL)
274*66005Seric 					sendinghost = peerhostname;
27558957Seric 
27658789Seric 				if (bitset(PRIV_NEEDMAILHELO, PrivacyFlags))
27758821Seric 				{
27858789Seric 					message("503 Polite people say HELO first");
27958821Seric 					break;
28058821Seric 				}
28158082Seric 			}
28258109Seric 			if (gotmail)
2834558Seric 			{
28458151Seric 				message("503 Sender already specified");
28563843Seric 				if (InChild)
28663843Seric 					finis();
2874558Seric 				break;
2884558Seric 			}
2899339Seric 			if (InChild)
2909339Seric 			{
29136230Skarels 				errno = 0;
29258151Seric 				syserr("503 Nested MAIL command: MAIL %s", p);
29358069Seric 				finis();
2949339Seric 			}
2959339Seric 
2969339Seric 			/* fork a subprocess to process this command */
29755012Seric 			if (runinchild("SMTP-MAIL", e) > 0)
2989339Seric 				break;
29963753Seric 			if (!gothello)
30063753Seric 			{
30163753Seric 				auth_warning(e,
302*66005Seric 					"Host %s didn't use HELO protocol",
303*66005Seric 					peerhostname);
30463753Seric 			}
30565947Seric #ifdef PICKY_HELO_CHECK
306*66005Seric 			if (strcasecmp(sendinghost, peerhostname) != 0 &&
307*66005Seric 			    (strcasecmp(peerhostname, "localhost") != 0 ||
30865823Seric 			     strcasecmp(sendinghost, MyHostName) != 0))
30965823Seric 			{
31065823Seric 				auth_warning(e, "Host %s claimed to be %s",
311*66005Seric 					peerhostname, sendinghost);
31265823Seric 			}
31365947Seric #endif
31465823Seric 
31558323Seric 			if (protocol == NULL)
31658323Seric 				protocol = "SMTP";
31758323Seric 			define('r', protocol, e);
31859016Seric 			define('s', sendinghost, e);
31955012Seric 			initsys(e);
32059747Seric 			nrcpts = 0;
32165089Seric 			e->e_flags |= EF_LOGSENDER;
32265058Seric 			setproctitle("%s %s: %.80s", e->e_id, CurSmtpClient, inp);
3239339Seric 
3249339Seric 			/* child -- go do the processing */
3254549Seric 			p = skipword(p, "from");
3264549Seric 			if (p == NULL)
3274549Seric 				break;
32857977Seric 			if (setjmp(TopFrame) > 0)
32958147Seric 			{
33058147Seric 				/* this failed -- undo work */
33158147Seric 				if (InChild)
33259058Seric 				{
33359058Seric 					QuickAbort = FALSE;
33459058Seric 					SuprErrs = TRUE;
33563787Seric 					e->e_flags &= ~EF_FATALERRS;
33658147Seric 					finis();
33759058Seric 				}
33857977Seric 				break;
33958147Seric 			}
34057977Seric 			QuickAbort = TRUE;
34158333Seric 
34258333Seric 			/* must parse sender first */
34358333Seric 			delimptr = NULL;
34458704Seric 			setsender(p, e, &delimptr, FALSE);
34558333Seric 			p = delimptr;
34658333Seric 			if (p != NULL && *p != '\0')
34758333Seric 				*p++ = '\0';
34858333Seric 
34958333Seric 			/* now parse ESMTP arguments */
35058333Seric 			msize = 0;
35158333Seric 			for (; p != NULL && *p != '\0'; p++)
35258333Seric 			{
35358333Seric 				char *kp;
35458333Seric 				char *vp;
35558333Seric 
35658333Seric 				/* locate the beginning of the keyword */
35758333Seric 				while (isascii(*p) && isspace(*p))
35858333Seric 					p++;
35958333Seric 				if (*p == '\0')
36058333Seric 					break;
36158333Seric 				kp = p;
36258333Seric 
36358333Seric 				/* skip to the value portion */
36458333Seric 				while (isascii(*p) && isalnum(*p) || *p == '-')
36558333Seric 					p++;
36658333Seric 				if (*p == '=')
36758333Seric 				{
36858333Seric 					*p++ = '\0';
36958333Seric 					vp = p;
37058333Seric 
37158333Seric 					/* skip to the end of the value */
37258333Seric 					while (*p != '\0' && *p != ' ' &&
37358333Seric 					       !(isascii(*p) && iscntrl(*p)) &&
37458333Seric 					       *p != '=')
37558333Seric 						p++;
37658333Seric 				}
37758333Seric 
37858333Seric 				if (*p != '\0')
37958333Seric 					*p++ = '\0';
38058333Seric 
38158333Seric 				if (tTd(19, 1))
38258333Seric 					printf("MAIL: got arg %s=%s\n", kp,
38358333Seric 						vp == NULL ? "<null>" : vp);
38458333Seric 
38558333Seric 				if (strcasecmp(kp, "size") == 0)
38658333Seric 				{
38759093Seric 					if (vp == NULL)
38858333Seric 					{
38958333Seric 						usrerr("501 SIZE requires a value");
39058333Seric 						/* NOTREACHED */
39158333Seric 					}
39258333Seric 					msize = atol(vp);
39358333Seric 				}
39459093Seric 				else if (strcasecmp(kp, "body") == 0)
39559093Seric 				{
39659093Seric 					if (vp == NULL)
39759093Seric 					{
39859093Seric 						usrerr("501 BODY requires a value");
39959093Seric 						/* NOTREACHED */
40059093Seric 					}
40159093Seric # ifdef MIME
40259093Seric 					if (strcasecmp(vp, "8bitmime") == 0)
40359093Seric 					{
40459093Seric 						e->e_bodytype = "8BITMIME";
40559709Seric 						SevenBit = FALSE;
40659093Seric 					}
40759093Seric 					else if (strcasecmp(vp, "7bit") == 0)
40859093Seric 					{
40959093Seric 						e->e_bodytype = "7BIT";
41059709Seric 						SevenBit = TRUE;
41159093Seric 					}
41259093Seric 					else
41359093Seric 					{
41459093Seric 						usrerr("501 Unknown BODY type %s",
41559093Seric 							vp);
41659093Seric 					}
41759093Seric # endif
41859093Seric 				}
41958333Seric 				else
42058333Seric 				{
42158333Seric 					usrerr("501 %s parameter unrecognized", kp);
42258333Seric 					/* NOTREACHED */
42358333Seric 				}
42458333Seric 			}
42559284Seric 
42659284Seric 			if (MaxMessageSize > 0 && msize > MaxMessageSize)
42759284Seric 			{
42859284Seric 				usrerr("552 Message size exceeds fixed maximum message size (%ld)",
42959284Seric 					MaxMessageSize);
43059284Seric 				/* NOTREACHED */
43159284Seric 			}
43258333Seric 
43358333Seric 			if (!enoughspace(msize))
43458333Seric 			{
43558333Seric 				message("452 Insufficient disk space; try again later");
43658333Seric 				break;
43758333Seric 			}
43858151Seric 			message("250 Sender ok");
43958147Seric 			gotmail = TRUE;
4404549Seric 			break;
4414549Seric 
4424976Seric 		  case CMDRCPT:		/* rcpt -- designate recipient */
44358850Seric 			if (!gotmail)
44458850Seric 			{
44558850Seric 				usrerr("503 Need MAIL before RCPT");
44658850Seric 				break;
44758850Seric 			}
44861093Seric 			SmtpPhase = "server RCPT";
44912612Seric 			if (setjmp(TopFrame) > 0)
45014785Seric 			{
45155012Seric 				e->e_flags &= ~EF_FATALERRS;
45212612Seric 				break;
45314785Seric 			}
45412612Seric 			QuickAbort = TRUE;
45551951Seric 			LogUsrErrs = TRUE;
45658093Seric 
45759699Seric 			if (e->e_sendmode != SM_DELIVER)
45859699Seric 				e->e_flags |= EF_VRFYONLY;
45958919Seric 
4604549Seric 			p = skipword(p, "to");
4614549Seric 			if (p == NULL)
4624549Seric 				break;
46364284Seric 			a = parseaddr(p, NULLADDR, RF_COPYALL, ' ', NULL, e);
46412612Seric 			if (a == NULL)
46512612Seric 				break;
46616886Seric 			a->q_flags |= QPRIMARY;
46755012Seric 			a = recipient(a, &e->e_sendqueue, e);
46812612Seric 			if (Errors != 0)
46912612Seric 				break;
47012612Seric 
47112612Seric 			/* no errors during parsing, but might be a duplicate */
47255012Seric 			e->e_to = p;
47312612Seric 			if (!bitset(QBADADDR, a->q_flags))
47459747Seric 			{
47564718Seric 				message("250 Recipient ok%s",
47664718Seric 					bitset(QQUEUEUP, a->q_flags) ?
47764718Seric 						" (will queue)" : "");
47859747Seric 				nrcpts++;
47959747Seric 			}
48012612Seric 			else
4814549Seric 			{
48212612Seric 				/* punt -- should keep message in ADDRESS.... */
48358151Seric 				message("550 Addressee unknown");
4844549Seric 			}
48555012Seric 			e->e_to = NULL;
4864549Seric 			break;
4874549Seric 
4884549Seric 		  case CMDDATA:		/* data -- text of mail */
48961093Seric 			SmtpPhase = "server DATA";
49058109Seric 			if (!gotmail)
4914549Seric 			{
49258151Seric 				message("503 Need MAIL command");
4934976Seric 				break;
4944549Seric 			}
49564718Seric 			else if (nrcpts <= 0)
4964549Seric 			{
49758151Seric 				message("503 Need RCPT (recipient)");
4984976Seric 				break;
4994549Seric 			}
5004976Seric 
50158929Seric 			/* check to see if we need to re-expand aliases */
50263787Seric 			/* also reset QBADADDR on already-diagnosted addrs */
50363787Seric 			doublequeue = FALSE;
50458929Seric 			for (a = e->e_sendqueue; a != NULL; a = a->q_next)
50558929Seric 			{
50658929Seric 				if (bitset(QVERIFIED, a->q_flags))
50763787Seric 				{
50863787Seric 					/* need to re-expand aliases */
50963787Seric 					doublequeue = TRUE;
51063787Seric 				}
51163787Seric 				if (bitset(QBADADDR, a->q_flags))
51263787Seric 				{
51363787Seric 					/* make this "go away" */
51463787Seric 					a->q_flags |= QDONTSEND;
51563787Seric 					a->q_flags &= ~QBADADDR;
51663787Seric 				}
51758929Seric 			}
51858929Seric 
5194976Seric 			/* collect the text of the message */
52024943Seric 			SmtpPhase = "collect";
52164766Seric 			collect(TRUE, doublequeue, e);
52264766Seric 			if (Errors != 0)
52364766Seric 				goto abortmessage;
52463965Seric 			HoldErrs = TRUE;
5254976Seric 
5268238Seric 			/*
5278238Seric 			**  Arrange to send to everyone.
5288238Seric 			**	If sending to multiple people, mail back
5298238Seric 			**		errors rather than reporting directly.
5308238Seric 			**	In any case, don't mail back errors for
5318238Seric 			**		anything that has happened up to
5328238Seric 			**		now (the other end will do this).
53310197Seric 			**	Truncate our transcript -- the mail has gotten
53410197Seric 			**		to us successfully, and if we have
53510197Seric 			**		to mail this back, it will be easier
53610197Seric 			**		on the reader.
5378238Seric 			**	Then send to everyone.
5388238Seric 			**	Finally give a reply code.  If an error has
5398238Seric 			**		already been given, don't mail a
5408238Seric 			**		message back.
5419339Seric 			**	We goose error returns by clearing error bit.
5428238Seric 			*/
5438238Seric 
54424943Seric 			SmtpPhase = "delivery";
54563787Seric 			if (nrcpts != 1 && !doublequeue)
5469378Seric 			{
5479378Seric 				HoldErrs = TRUE;
54858734Seric 				e->e_errormode = EM_MAIL;
5499378Seric 			}
55055012Seric 			e->e_xfp = freopen(queuename(e, 'x'), "w", e->e_xfp);
55158714Seric 			id = e->e_id;
5524976Seric 
5534976Seric 			/* send to all recipients */
55463787Seric 			sendall(e, doublequeue ? SM_QUEUE : SM_DEFAULT);
55555012Seric 			e->e_to = NULL;
5564976Seric 
5578238Seric 			/* issue success if appropriate and reset */
5588238Seric 			if (Errors == 0 || HoldErrs)
55958855Seric 				message("250 %s Message accepted for delivery", id);
56059747Seric 
56159747Seric 			if (bitset(EF_FATALERRS, e->e_flags) && !HoldErrs)
56259730Seric 			{
56359730Seric 				/* avoid sending back an extra message */
56459730Seric 				e->e_flags &= ~EF_FATALERRS;
56559747Seric 				e->e_flags |= EF_CLRQUEUE;
56659730Seric 			}
5678238Seric 			else
56858919Seric 			{
56959747Seric 				/* from now on, we have to operate silently */
57059747Seric 				HoldErrs = TRUE;
57159747Seric 				e->e_errormode = EM_MAIL;
57259747Seric 
57359730Seric 				/* if we just queued, poke it */
57463787Seric 				if (doublequeue && e->e_sendmode != SM_QUEUE)
57559730Seric 				{
57664296Seric 					extern pid_t dowork();
57764296Seric 
57859730Seric 					unlockqueue(e);
57964296Seric 					(void) dowork(id, TRUE, TRUE, e);
58059730Seric 				}
58158919Seric 			}
58258883Seric 
58359747Seric   abortmessage:
5849339Seric 			/* if in a child, pop back to our parent */
5859339Seric 			if (InChild)
5869339Seric 				finis();
58724943Seric 
58824943Seric 			/* clean up a bit */
58958109Seric 			gotmail = FALSE;
59055012Seric 			dropenvelope(e);
59158179Seric 			CurEnv = e = newenvelope(e, CurEnv);
59255012Seric 			e->e_flags = BlankEnvelope.e_flags;
5934549Seric 			break;
5944549Seric 
5954549Seric 		  case CMDRSET:		/* rset -- reset state */
59658151Seric 			message("250 Reset state");
59764359Seric 			e->e_flags |= EF_CLRQUEUE;
5989339Seric 			if (InChild)
5999339Seric 				finis();
60058109Seric 
60158109Seric 			/* clean up a bit */
60258109Seric 			gotmail = FALSE;
60358109Seric 			dropenvelope(e);
60458179Seric 			CurEnv = e = newenvelope(e, CurEnv);
6059339Seric 			break;
6064549Seric 
6074549Seric 		  case CMDVRFY:		/* vrfy -- verify address */
60858092Seric 		  case CMDEXPN:		/* expn -- expand address */
60958092Seric 			vrfy = c->cmdcode == CMDVRFY;
61058092Seric 			if (bitset(vrfy ? PRIV_NOVRFY : PRIV_NOEXPN,
61158092Seric 						PrivacyFlags))
61258082Seric 			{
61358412Seric 				if (vrfy)
61458412Seric 					message("252 Who's to say?");
61558412Seric 				else
61665192Seric 					message("502 Sorry, we do not allow this operation");
61765017Seric #ifdef LOG
61865017Seric 				if (LogLevel > 5)
61965017Seric 					syslog(LOG_INFO, "%s: %s [rejected]",
62065017Seric 						CurSmtpClient, inp);
62165017Seric #endif
62258082Seric 				break;
62358082Seric 			}
62458082Seric 			else if (!gothello &&
62558092Seric 				 bitset(vrfy ? PRIV_NEEDVRFYHELO : PRIV_NEEDEXPNHELO,
62658092Seric 						PrivacyFlags))
62758082Seric 			{
62858151Seric 				message("503 I demand that you introduce yourself first");
62958082Seric 				break;
63058082Seric 			}
63158092Seric 			if (runinchild(vrfy ? "SMTP-VRFY" : "SMTP-EXPN", e) > 0)
6329339Seric 				break;
63355173Seric #ifdef LOG
63458020Seric 			if (LogLevel > 5)
63565017Seric 				syslog(LOG_INFO, "%s: %s", CurSmtpClient, inp);
63655173Seric #endif
6375003Seric 			vrfyqueue = NULL;
6387762Seric 			QuickAbort = TRUE;
63958092Seric 			if (vrfy)
64058092Seric 				e->e_flags |= EF_VRFYONLY;
64162373Seric 			while (*p != '\0' && isascii(*p) && isspace(*p))
64262373Seric 				*p++;
64362373Seric 			if (*p == '\0')
64462373Seric 			{
64562373Seric 				message("501 Argument required");
64662373Seric 				Errors++;
64762373Seric 			}
64862373Seric 			else
64962373Seric 			{
65064284Seric 				(void) sendtolist(p, NULLADDR, &vrfyqueue, e);
65162373Seric 			}
6527762Seric 			if (Errors != 0)
6539339Seric 			{
6549339Seric 				if (InChild)
6559339Seric 					finis();
6567762Seric 				break;
6579339Seric 			}
65862373Seric 			if (vrfyqueue == NULL)
65962373Seric 			{
66062373Seric 				message("554 Nothing to %s", vrfy ? "VRFY" : "EXPN");
66162373Seric 			}
6625003Seric 			while (vrfyqueue != NULL)
6635003Seric 			{
66463971Seric 				a = vrfyqueue;
66563971Seric 				while ((a = a->q_next) != NULL &&
66663971Seric 				       bitset(QDONTSEND|QBADADDR, a->q_flags))
66763971Seric 					continue;
6687685Seric 				if (!bitset(QDONTSEND|QBADADDR, vrfyqueue->q_flags))
66958151Seric 					printvrfyaddr(vrfyqueue, a == NULL);
67063847Seric 				vrfyqueue = vrfyqueue->q_next;
6715003Seric 			}
6729339Seric 			if (InChild)
6739339Seric 				finis();
6744549Seric 			break;
6754549Seric 
6764549Seric 		  case CMDHELP:		/* help -- give user info */
6774577Seric 			help(p);
6784549Seric 			break;
6794549Seric 
6804549Seric 		  case CMDNOOP:		/* noop -- do nothing */
68164122Seric 			message("250 OK");
6824549Seric 			break;
6834549Seric 
6844549Seric 		  case CMDQUIT:		/* quit -- leave mail */
68558151Seric 			message("221 %s closing connection", MyHostName);
68661051Seric 
68761051Seric 			/* avoid future 050 messages */
68861051Seric 			Verbose = FALSE;
68961051Seric 
6909339Seric 			if (InChild)
6919339Seric 				ExitStat = EX_QUIT;
6924549Seric 			finis();
6934549Seric 
6948544Seric 		  case CMDVERB:		/* set verbose mode */
69559957Seric 			if (bitset(PRIV_NOEXPN, PrivacyFlags))
69659957Seric 			{
69759957Seric 				/* this would give out the same info */
69859957Seric 				message("502 Verbose unavailable");
69959957Seric 				break;
70059957Seric 			}
7018544Seric 			Verbose = TRUE;
70258734Seric 			e->e_sendmode = SM_DELIVER;
70359957Seric 			message("250 Verbose mode");
7048544Seric 			break;
7058544Seric 
7069314Seric 		  case CMDONEX:		/* doing one transaction only */
7079378Seric 			OneXact = TRUE;
70859957Seric 			message("250 Only one transaction");
7099314Seric 			break;
7109314Seric 
71136230Skarels # ifdef SMTPDEBUG
7129339Seric 		  case CMDDBGQSHOW:	/* show queues */
7136907Seric 			printf("Send Queue=");
71455012Seric 			printaddr(e->e_sendqueue, TRUE);
7155003Seric 			break;
7167275Seric 
7177275Seric 		  case CMDDBGDEBUG:	/* set debug mode */
7187676Seric 			tTsetup(tTdvect, sizeof tTdvect, "0-99.1");
7197676Seric 			tTflag(p);
72058151Seric 			message("200 Debug set");
7217275Seric 			break;
7227275Seric 
72336230Skarels # else /* not SMTPDEBUG */
72436230Skarels 		  case CMDDBGQSHOW:	/* show queues */
72536230Skarels 		  case CMDDBGDEBUG:	/* set debug mode */
72664685Seric # endif /* SMTPDEBUG */
72764685Seric 		  case CMDLOGBOGUS:	/* bogus command */
72836233Skarels # ifdef LOG
72958308Seric 			if (LogLevel > 0)
73064685Seric 				syslog(LOG_CRIT,
73158020Seric 				    "\"%s\" command from %s (%s)",
732*66005Seric 				    c->cmdname, peerhostname,
73358755Seric 				    anynet_ntoa(&RealHostAddr));
73436233Skarels # endif
73536230Skarels 			/* FALL THROUGH */
73636230Skarels 
7374549Seric 		  case CMDERROR:	/* unknown command */
73858151Seric 			message("500 Command unrecognized");
7394549Seric 			break;
7404549Seric 
7414549Seric 		  default:
74236230Skarels 			errno = 0;
74358151Seric 			syserr("500 smtp: unknown code %d", c->cmdcode);
7444549Seric 			break;
7454549Seric 		}
7464549Seric 	}
7474549Seric }
7484549Seric /*
7494549Seric **  SKIPWORD -- skip a fixed word.
7504549Seric **
7514549Seric **	Parameters:
7524549Seric **		p -- place to start looking.
7534549Seric **		w -- word to skip.
7544549Seric **
7554549Seric **	Returns:
7564549Seric **		p following w.
7574549Seric **		NULL on error.
7584549Seric **
7594549Seric **	Side Effects:
7604549Seric **		clobbers the p data area.
7614549Seric */
7624549Seric 
7634549Seric static char *
7644549Seric skipword(p, w)
7654549Seric 	register char *p;
7664549Seric 	char *w;
7674549Seric {
7684549Seric 	register char *q;
769*66005Seric 	char *firstp = p;
7704549Seric 
7714549Seric 	/* find beginning of word */
77258050Seric 	while (isascii(*p) && isspace(*p))
7734549Seric 		p++;
7744549Seric 	q = p;
7754549Seric 
7764549Seric 	/* find end of word */
77758050Seric 	while (*p != '\0' && *p != ':' && !(isascii(*p) && isspace(*p)))
7784549Seric 		p++;
77958050Seric 	while (isascii(*p) && isspace(*p))
7804549Seric 		*p++ = '\0';
7814549Seric 	if (*p != ':')
7824549Seric 	{
7834549Seric 	  syntax:
784*66005Seric 		message("501 Syntax error in parameters scanning \"%s\"",
785*66005Seric 			firstp);
7864549Seric 		Errors++;
7874549Seric 		return (NULL);
7884549Seric 	}
7894549Seric 	*p++ = '\0';
79058050Seric 	while (isascii(*p) && isspace(*p))
7914549Seric 		p++;
7924549Seric 
79362373Seric 	if (*p == '\0')
79462373Seric 		goto syntax;
79562373Seric 
7964549Seric 	/* see if the input word matches desired word */
79733725Sbostic 	if (strcasecmp(q, w))
7984549Seric 		goto syntax;
7994549Seric 
8004549Seric 	return (p);
8014549Seric }
8024577Seric /*
80358151Seric **  PRINTVRFYADDR -- print an entry in the verify queue
80458151Seric **
80558151Seric **	Parameters:
80658151Seric **		a -- the address to print
80758151Seric **		last -- set if this is the last one.
80858151Seric **
80958151Seric **	Returns:
81058151Seric **		none.
81158151Seric **
81258151Seric **	Side Effects:
81358151Seric **		Prints the appropriate 250 codes.
81458151Seric */
81558151Seric 
81658151Seric printvrfyaddr(a, last)
81758151Seric 	register ADDRESS *a;
81858151Seric 	bool last;
81958151Seric {
82058151Seric 	char fmtbuf[20];
82158151Seric 
82258151Seric 	strcpy(fmtbuf, "250");
82358151Seric 	fmtbuf[3] = last ? ' ' : '-';
82458151Seric 
82559746Seric 	if (a->q_fullname == NULL)
82659746Seric 	{
82759746Seric 		if (strchr(a->q_user, '@') == NULL)
82859746Seric 			strcpy(&fmtbuf[4], "<%s@%s>");
82959746Seric 		else
83059746Seric 			strcpy(&fmtbuf[4], "<%s>");
83159746Seric 		message(fmtbuf, a->q_user, MyHostName);
83259746Seric 	}
83358151Seric 	else
83458151Seric 	{
83559746Seric 		if (strchr(a->q_user, '@') == NULL)
83659746Seric 			strcpy(&fmtbuf[4], "%s <%s@%s>");
83759746Seric 		else
83859746Seric 			strcpy(&fmtbuf[4], "%s <%s>");
83959746Seric 		message(fmtbuf, a->q_fullname, a->q_user, MyHostName);
84058151Seric 	}
84158151Seric }
84258151Seric /*
8434577Seric **  HELP -- implement the HELP command.
8444577Seric **
8454577Seric **	Parameters:
8464577Seric **		topic -- the topic we want help for.
8474577Seric **
8484577Seric **	Returns:
8494577Seric **		none.
8504577Seric **
8514577Seric **	Side Effects:
8524577Seric **		outputs the help file to message output.
8534577Seric */
8544577Seric 
8554577Seric help(topic)
8564577Seric 	char *topic;
8574577Seric {
8584577Seric 	register FILE *hf;
8594577Seric 	int len;
8604577Seric 	char buf[MAXLINE];
8614577Seric 	bool noinfo;
8624577Seric 
8638269Seric 	if (HelpFile == NULL || (hf = fopen(HelpFile, "r")) == NULL)
8644577Seric 	{
8654577Seric 		/* no help */
86611931Seric 		errno = 0;
86758151Seric 		message("502 HELP not implemented");
8684577Seric 		return;
8694577Seric 	}
8704577Seric 
87149669Seric 	if (topic == NULL || *topic == '\0')
87249669Seric 		topic = "smtp";
87349669Seric 	else
87449669Seric 		makelower(topic);
87549669Seric 
8764577Seric 	len = strlen(topic);
8774577Seric 	noinfo = TRUE;
8784577Seric 
8794577Seric 	while (fgets(buf, sizeof buf, hf) != NULL)
8804577Seric 	{
8814577Seric 		if (strncmp(buf, topic, len) == 0)
8824577Seric 		{
8834577Seric 			register char *p;
8844577Seric 
88556795Seric 			p = strchr(buf, '\t');
8864577Seric 			if (p == NULL)
8874577Seric 				p = buf;
8884577Seric 			else
8894577Seric 				p++;
8904577Seric 			fixcrlf(p, TRUE);
89158151Seric 			message("214-%s", p);
8924577Seric 			noinfo = FALSE;
8934577Seric 		}
8944577Seric 	}
8954577Seric 
8964577Seric 	if (noinfo)
89758151Seric 		message("504 HELP topic unknown");
8984577Seric 	else
89958151Seric 		message("214 End of HELP info");
9004628Seric 	(void) fclose(hf);
9014577Seric }
9028544Seric /*
9039339Seric **  RUNINCHILD -- return twice -- once in the child, then in the parent again
9049339Seric **
9059339Seric **	Parameters:
9069339Seric **		label -- a string used in error messages
9079339Seric **
9089339Seric **	Returns:
9099339Seric **		zero in the child
9109339Seric **		one in the parent
9119339Seric **
9129339Seric **	Side Effects:
9139339Seric **		none.
9149339Seric */
9158544Seric 
91655012Seric runinchild(label, e)
9179339Seric 	char *label;
91855012Seric 	register ENVELOPE *e;
9199339Seric {
9209339Seric 	int childpid;
9219339Seric 
92216158Seric 	if (!OneXact)
9239339Seric 	{
92416158Seric 		childpid = dofork();
92516158Seric 		if (childpid < 0)
92616158Seric 		{
92716158Seric 			syserr("%s: cannot fork", label);
92816158Seric 			return (1);
92916158Seric 		}
93016158Seric 		if (childpid > 0)
93116158Seric 		{
93216158Seric 			auto int st;
9339339Seric 
93416158Seric 			/* parent -- wait for child to complete */
93561093Seric 			setproctitle("server %s child wait", CurHostName);
93616158Seric 			st = waitfor(childpid);
93716158Seric 			if (st == -1)
93816158Seric 				syserr("%s: lost child", label);
93964948Seric 			else if (!WIFEXITED(st))
94064948Seric 				syserr("%s: died on signal %d",
94164948Seric 					label, st & 0177);
9429339Seric 
94316158Seric 			/* if we exited on a QUIT command, complete the process */
94416158Seric 			if (st == (EX_QUIT << 8))
94516158Seric 				finis();
9469339Seric 
94716158Seric 			return (1);
94816158Seric 		}
94916158Seric 		else
95016158Seric 		{
95116158Seric 			/* child */
95216158Seric 			InChild = TRUE;
95325050Seric 			QuickAbort = FALSE;
95455012Seric 			clearenvelope(e, FALSE);
95516158Seric 		}
9569339Seric 	}
95715256Seric 
95816158Seric 	/* open alias database */
95960537Seric 	initmaps(FALSE, e);
96016158Seric 
96116158Seric 	return (0);
9629339Seric }
9639339Seric 
96456795Seric # endif /* SMTP */
965