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*66325Seric static char sccsid[] = "@(#)srvrsmtp.c	8.32 (Berkeley) 03/08/94 (with SMTP)";
1433731Sbostic #else
15*66325Seric static char sccsid[] = "@(#)srvrsmtp.c	8.32 (Berkeley) 03/08/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();
94*66325Seric extern char	RealUserName[];
9563937Seric 
96*66325Seric 
9766283Seric #define MAXBADCOMMANDS	25		/* maximum number of bad commands */
9866283Seric 
9955012Seric smtp(e)
10055012Seric 	register ENVELOPE *e;
1014549Seric {
1024549Seric 	register char *p;
1038544Seric 	register struct cmd *c;
1044549Seric 	char *cmd;
1055003Seric 	auto ADDRESS *vrfyqueue;
10612612Seric 	ADDRESS *a;
10758109Seric 	bool gotmail;			/* mail command received */
10858092Seric 	bool gothello;			/* helo command received */
10958092Seric 	bool vrfy;			/* set if this is a vrfy command */
11058323Seric 	char *protocol;			/* sending protocol */
11159016Seric 	char *sendinghost;		/* sending hostname */
11258333Seric 	long msize;			/* approximate maximum message size */
11366005Seric 	char *peerhostname;		/* name of SMTP peer or "localhost" */
11458333Seric 	auto char *delimptr;
11558714Seric 	char *id;
11659747Seric 	int nrcpts;			/* number of RCPT commands */
11763787Seric 	bool doublequeue;
11866283Seric 	int badcommands = 0;		/* count of bad commands */
1198544Seric 	char inp[MAXLINE];
12057232Seric 	char cmdbuf[MAXLINE];
1217124Seric 	extern char Version[];
12224943Seric 	extern ENVELOPE BlankEnvelope;
1234549Seric 
12459066Seric 	if (fileno(OutChannel) != fileno(stdout))
1257363Seric 	{
1267363Seric 		/* arrange for debugging output to go to remote host */
12759066Seric 		(void) dup2(fileno(OutChannel), fileno(stdout));
1287363Seric 	}
12955012Seric 	settime(e);
13066005Seric 	peerhostname = RealHostName;
13166005Seric 	if (peerhostname == NULL)
13266005Seric 		peerhostname = "localhost";
13366005Seric 	CurHostName = peerhostname;
13465017Seric 	CurSmtpClient = macvalue('_', e);
13565017Seric 	if (CurSmtpClient == NULL)
13666003Seric 		CurSmtpClient = CurHostName;
13765017Seric 
13865017Seric 	setproctitle("server %s startup", CurSmtpClient);
13958050Seric 	expand("\201e", inp, &inp[sizeof inp], e);
14064496Seric 	if (BrokenSmtpPeers)
14164496Seric 	{
14264496Seric 		message("220 %s", inp);
14364496Seric 	}
14464496Seric 	else
14564496Seric 	{
14664496Seric 		message("220-%s", inp);
14764496Seric 		message("220 ESMTP spoken here");
14864496Seric 	}
14958330Seric 	protocol = NULL;
15059016Seric 	sendinghost = macvalue('s', e);
15158082Seric 	gothello = FALSE;
15258330Seric 	gotmail = FALSE;
1534549Seric 	for (;;)
1544549Seric 	{
15512612Seric 		/* arrange for backout */
15665751Seric 		if (setjmp(TopFrame) > 0)
15759058Seric 		{
15865751Seric 			/* if() nesting is necessary for Cray UNICOS */
15965751Seric 			if (InChild)
16065751Seric 			{
16165751Seric 				QuickAbort = FALSE;
16265751Seric 				SuprErrs = TRUE;
16365751Seric 				finis();
16465751Seric 			}
16559058Seric 		}
16612612Seric 		QuickAbort = FALSE;
16712612Seric 		HoldErrs = FALSE;
16851951Seric 		LogUsrErrs = FALSE;
16963843Seric 		e->e_flags &= ~(EF_VRFYONLY|EF_GLOBALERRS);
17012612Seric 
1717356Seric 		/* setup for the read */
17255012Seric 		e->e_to = NULL;
1734577Seric 		Errors = 0;
1747275Seric 		(void) fflush(stdout);
1757356Seric 
1767356Seric 		/* read the input line */
17761093Seric 		SmtpPhase = "server cmd read";
17861093Seric 		setproctitle("server %s cmd read", CurHostName);
17961093Seric 		p = sfgets(inp, sizeof inp, InChannel, TimeOuts.to_nextcommand,
18061093Seric 				SmtpPhase);
1817356Seric 
1827685Seric 		/* handle errors */
1837356Seric 		if (p == NULL)
1847356Seric 		{
1854549Seric 			/* end of file, just die */
18666017Seric 			disconnect(1, e);
18758151Seric 			message("421 %s Lost input channel from %s",
18865017Seric 				MyHostName, CurSmtpClient);
18955464Seric #ifdef LOG
19063843Seric 			if (LogLevel > (gotmail ? 1 : 19))
19155464Seric 				syslog(LOG_NOTICE, "lost input channel from %s",
19265017Seric 					CurSmtpClient);
19355464Seric #endif
19458069Seric 			if (InChild)
19558069Seric 				ExitStat = EX_QUIT;
1964549Seric 			finis();
1974549Seric 		}
1984549Seric 
1994549Seric 		/* clean up end of line */
2004558Seric 		fixcrlf(inp, TRUE);
2014549Seric 
2024713Seric 		/* echo command to transcript */
20355012Seric 		if (e->e_xfp != NULL)
20455012Seric 			fprintf(e->e_xfp, "<<< %s\n", inp);
2054713Seric 
20659060Seric 		if (e->e_id == NULL)
20765058Seric 			setproctitle("%s: %.80s", CurSmtpClient, inp);
20859060Seric 		else
20965058Seric 			setproctitle("%s %s: %.80s", e->e_id, CurSmtpClient, inp);
21059060Seric 
2114549Seric 		/* break off command */
21258050Seric 		for (p = inp; isascii(*p) && isspace(*p); p++)
2134549Seric 			continue;
21457232Seric 		cmd = cmdbuf;
21558050Seric 		while (*p != '\0' &&
21658050Seric 		       !(isascii(*p) && isspace(*p)) &&
21758050Seric 		       cmd < &cmdbuf[sizeof cmdbuf - 2])
21824981Seric 			*cmd++ = *p++;
21924981Seric 		*cmd = '\0';
2204549Seric 
22125691Seric 		/* throw away leading whitespace */
22258050Seric 		while (isascii(*p) && isspace(*p))
22325691Seric 			p++;
22425691Seric 
2254549Seric 		/* decode command */
2264549Seric 		for (c = CmdTab; c->cmdname != NULL; c++)
2274549Seric 		{
22833725Sbostic 			if (!strcasecmp(c->cmdname, cmdbuf))
2294549Seric 				break;
2304549Seric 		}
2314549Seric 
23251954Seric 		/* reset errors */
23351954Seric 		errno = 0;
23451954Seric 
2354549Seric 		/* process command */
2364549Seric 		switch (c->cmdcode)
2374549Seric 		{
2384976Seric 		  case CMDHELO:		/* hello -- introduce yourself */
23958323Seric 		  case CMDEHLO:		/* extended hello */
24058323Seric 			if (c->cmdcode == CMDEHLO)
24158323Seric 			{
24258323Seric 				protocol = "ESMTP";
24361093Seric 				SmtpPhase = "server EHLO";
24458323Seric 			}
24558323Seric 			else
24658323Seric 			{
24758323Seric 				protocol = "SMTP";
24861093Seric 				SmtpPhase = "server HELO";
24958323Seric 			}
25059016Seric 			sendinghost = newstr(p);
25160210Seric 			gothello = TRUE;
25260210Seric 			if (c->cmdcode != CMDEHLO)
25360239Seric 			{
25460239Seric 				/* print old message and be done with it */
25560239Seric 				message("250 %s Hello %s, pleased to meet you",
25665017Seric 					MyHostName, CurSmtpClient);
25760210Seric 				break;
25860239Seric 			}
25960239Seric 
26060239Seric 			/* print extended message and brag */
26160239Seric 			message("250-%s Hello %s, pleased to meet you",
26260239Seric 				MyHostName, p);
26358323Seric 			if (!bitset(PRIV_NOEXPN, PrivacyFlags))
26458323Seric 				message("250-EXPN");
26564359Seric 			if (MaxMessageSize > 0)
26664359Seric 				message("250-SIZE %ld", MaxMessageSize);
26759271Seric 			else
26859271Seric 				message("250-SIZE");
26958323Seric 			message("250 HELP");
2704976Seric 			break;
2714976Seric 
2724549Seric 		  case CMDMAIL:		/* mail -- designate sender */
27361093Seric 			SmtpPhase = "server MAIL";
27424943Seric 
2759314Seric 			/* check for validity of this command */
27658789Seric 			if (!gothello)
27758082Seric 			{
27858957Seric 				/* set sending host to our known value */
27959016Seric 				if (sendinghost == NULL)
28066005Seric 					sendinghost = peerhostname;
28158957Seric 
28258789Seric 				if (bitset(PRIV_NEEDMAILHELO, PrivacyFlags))
28358821Seric 				{
28458789Seric 					message("503 Polite people say HELO first");
28558821Seric 					break;
28658821Seric 				}
28758082Seric 			}
28858109Seric 			if (gotmail)
2894558Seric 			{
29058151Seric 				message("503 Sender already specified");
29163843Seric 				if (InChild)
29263843Seric 					finis();
2934558Seric 				break;
2944558Seric 			}
2959339Seric 			if (InChild)
2969339Seric 			{
29736230Skarels 				errno = 0;
29858151Seric 				syserr("503 Nested MAIL command: MAIL %s", p);
29958069Seric 				finis();
3009339Seric 			}
3019339Seric 
3029339Seric 			/* fork a subprocess to process this command */
30355012Seric 			if (runinchild("SMTP-MAIL", e) > 0)
3049339Seric 				break;
30563753Seric 			if (!gothello)
30663753Seric 			{
30763753Seric 				auth_warning(e,
30866005Seric 					"Host %s didn't use HELO protocol",
30966005Seric 					peerhostname);
31063753Seric 			}
31165947Seric #ifdef PICKY_HELO_CHECK
31266005Seric 			if (strcasecmp(sendinghost, peerhostname) != 0 &&
31366005Seric 			    (strcasecmp(peerhostname, "localhost") != 0 ||
31465823Seric 			     strcasecmp(sendinghost, MyHostName) != 0))
31565823Seric 			{
31665823Seric 				auth_warning(e, "Host %s claimed to be %s",
31766005Seric 					peerhostname, sendinghost);
31865823Seric 			}
31965947Seric #endif
32065823Seric 
32158323Seric 			if (protocol == NULL)
32258323Seric 				protocol = "SMTP";
32358323Seric 			define('r', protocol, e);
32459016Seric 			define('s', sendinghost, e);
32555012Seric 			initsys(e);
32659747Seric 			nrcpts = 0;
32765089Seric 			e->e_flags |= EF_LOGSENDER;
32865058Seric 			setproctitle("%s %s: %.80s", e->e_id, CurSmtpClient, inp);
3299339Seric 
3309339Seric 			/* child -- go do the processing */
3314549Seric 			p = skipword(p, "from");
3324549Seric 			if (p == NULL)
3334549Seric 				break;
33457977Seric 			if (setjmp(TopFrame) > 0)
33558147Seric 			{
33658147Seric 				/* this failed -- undo work */
33758147Seric 				if (InChild)
33859058Seric 				{
33959058Seric 					QuickAbort = FALSE;
34059058Seric 					SuprErrs = TRUE;
34163787Seric 					e->e_flags &= ~EF_FATALERRS;
34258147Seric 					finis();
34359058Seric 				}
34457977Seric 				break;
34558147Seric 			}
34657977Seric 			QuickAbort = TRUE;
34758333Seric 
34858333Seric 			/* must parse sender first */
34958333Seric 			delimptr = NULL;
35058704Seric 			setsender(p, e, &delimptr, FALSE);
35158333Seric 			p = delimptr;
35258333Seric 			if (p != NULL && *p != '\0')
35358333Seric 				*p++ = '\0';
35458333Seric 
355*66325Seric 			/* check for possible spoofing */
356*66325Seric 			if (RealUid != 0 && OpMode == MD_SMTP &&
357*66325Seric 			    (e->e_from.q_mailer != LocalMailer &&
358*66325Seric 			     strcmp(e->e_from.q_user, RealUserName) != 0))
359*66325Seric 			{
360*66325Seric 				auth_warning(e, "%s owned process doing -bs",
361*66325Seric 					RealUserName);
362*66325Seric 			}
363*66325Seric 
36458333Seric 			/* now parse ESMTP arguments */
36558333Seric 			msize = 0;
36658333Seric 			for (; p != NULL && *p != '\0'; p++)
36758333Seric 			{
36858333Seric 				char *kp;
36966304Seric 				char *vp = NULL;
37058333Seric 
37158333Seric 				/* locate the beginning of the keyword */
37258333Seric 				while (isascii(*p) && isspace(*p))
37358333Seric 					p++;
37458333Seric 				if (*p == '\0')
37558333Seric 					break;
37658333Seric 				kp = p;
37758333Seric 
37858333Seric 				/* skip to the value portion */
37958333Seric 				while (isascii(*p) && isalnum(*p) || *p == '-')
38058333Seric 					p++;
38158333Seric 				if (*p == '=')
38258333Seric 				{
38358333Seric 					*p++ = '\0';
38458333Seric 					vp = p;
38558333Seric 
38658333Seric 					/* skip to the end of the value */
38758333Seric 					while (*p != '\0' && *p != ' ' &&
38858333Seric 					       !(isascii(*p) && iscntrl(*p)) &&
38958333Seric 					       *p != '=')
39058333Seric 						p++;
39158333Seric 				}
39258333Seric 
39358333Seric 				if (*p != '\0')
39458333Seric 					*p++ = '\0';
39558333Seric 
39658333Seric 				if (tTd(19, 1))
39758333Seric 					printf("MAIL: got arg %s=%s\n", kp,
39858333Seric 						vp == NULL ? "<null>" : vp);
39958333Seric 
40058333Seric 				if (strcasecmp(kp, "size") == 0)
40158333Seric 				{
40259093Seric 					if (vp == NULL)
40358333Seric 					{
40458333Seric 						usrerr("501 SIZE requires a value");
40558333Seric 						/* NOTREACHED */
40658333Seric 					}
40758333Seric 					msize = atol(vp);
40858333Seric 				}
40959093Seric 				else if (strcasecmp(kp, "body") == 0)
41059093Seric 				{
41159093Seric 					if (vp == NULL)
41259093Seric 					{
41359093Seric 						usrerr("501 BODY requires a value");
41459093Seric 						/* NOTREACHED */
41559093Seric 					}
41659093Seric # ifdef MIME
41759093Seric 					if (strcasecmp(vp, "8bitmime") == 0)
41859093Seric 					{
41959093Seric 						e->e_bodytype = "8BITMIME";
42059709Seric 						SevenBit = FALSE;
42159093Seric 					}
42259093Seric 					else if (strcasecmp(vp, "7bit") == 0)
42359093Seric 					{
42459093Seric 						e->e_bodytype = "7BIT";
42559709Seric 						SevenBit = TRUE;
42659093Seric 					}
42759093Seric 					else
42859093Seric 					{
42959093Seric 						usrerr("501 Unknown BODY type %s",
43059093Seric 							vp);
43159093Seric 					}
43259093Seric # endif
43359093Seric 				}
43458333Seric 				else
43558333Seric 				{
43658333Seric 					usrerr("501 %s parameter unrecognized", kp);
43758333Seric 					/* NOTREACHED */
43858333Seric 				}
43958333Seric 			}
44059284Seric 
44159284Seric 			if (MaxMessageSize > 0 && msize > MaxMessageSize)
44259284Seric 			{
44359284Seric 				usrerr("552 Message size exceeds fixed maximum message size (%ld)",
44459284Seric 					MaxMessageSize);
44559284Seric 				/* NOTREACHED */
44659284Seric 			}
44758333Seric 
44858333Seric 			if (!enoughspace(msize))
44958333Seric 			{
45058333Seric 				message("452 Insufficient disk space; try again later");
45158333Seric 				break;
45258333Seric 			}
45358151Seric 			message("250 Sender ok");
45458147Seric 			gotmail = TRUE;
4554549Seric 			break;
4564549Seric 
4574976Seric 		  case CMDRCPT:		/* rcpt -- designate recipient */
45858850Seric 			if (!gotmail)
45958850Seric 			{
46058850Seric 				usrerr("503 Need MAIL before RCPT");
46158850Seric 				break;
46258850Seric 			}
46361093Seric 			SmtpPhase = "server RCPT";
46412612Seric 			if (setjmp(TopFrame) > 0)
46514785Seric 			{
46655012Seric 				e->e_flags &= ~EF_FATALERRS;
46712612Seric 				break;
46814785Seric 			}
46912612Seric 			QuickAbort = TRUE;
47051951Seric 			LogUsrErrs = TRUE;
47158093Seric 
47259699Seric 			if (e->e_sendmode != SM_DELIVER)
47359699Seric 				e->e_flags |= EF_VRFYONLY;
47458919Seric 
4754549Seric 			p = skipword(p, "to");
4764549Seric 			if (p == NULL)
4774549Seric 				break;
47864284Seric 			a = parseaddr(p, NULLADDR, RF_COPYALL, ' ', NULL, e);
47912612Seric 			if (a == NULL)
48012612Seric 				break;
48116886Seric 			a->q_flags |= QPRIMARY;
48255012Seric 			a = recipient(a, &e->e_sendqueue, e);
48312612Seric 			if (Errors != 0)
48412612Seric 				break;
48512612Seric 
48612612Seric 			/* no errors during parsing, but might be a duplicate */
48755012Seric 			e->e_to = p;
48812612Seric 			if (!bitset(QBADADDR, a->q_flags))
48959747Seric 			{
49064718Seric 				message("250 Recipient ok%s",
49164718Seric 					bitset(QQUEUEUP, a->q_flags) ?
49264718Seric 						" (will queue)" : "");
49359747Seric 				nrcpts++;
49459747Seric 			}
49512612Seric 			else
4964549Seric 			{
49712612Seric 				/* punt -- should keep message in ADDRESS.... */
49858151Seric 				message("550 Addressee unknown");
4994549Seric 			}
50055012Seric 			e->e_to = NULL;
5014549Seric 			break;
5024549Seric 
5034549Seric 		  case CMDDATA:		/* data -- text of mail */
50461093Seric 			SmtpPhase = "server DATA";
50558109Seric 			if (!gotmail)
5064549Seric 			{
50758151Seric 				message("503 Need MAIL command");
5084976Seric 				break;
5094549Seric 			}
51064718Seric 			else if (nrcpts <= 0)
5114549Seric 			{
51258151Seric 				message("503 Need RCPT (recipient)");
5134976Seric 				break;
5144549Seric 			}
5154976Seric 
51658929Seric 			/* check to see if we need to re-expand aliases */
51763787Seric 			/* also reset QBADADDR on already-diagnosted addrs */
51863787Seric 			doublequeue = FALSE;
51958929Seric 			for (a = e->e_sendqueue; a != NULL; a = a->q_next)
52058929Seric 			{
52158929Seric 				if (bitset(QVERIFIED, a->q_flags))
52263787Seric 				{
52363787Seric 					/* need to re-expand aliases */
52463787Seric 					doublequeue = TRUE;
52563787Seric 				}
52663787Seric 				if (bitset(QBADADDR, a->q_flags))
52763787Seric 				{
52863787Seric 					/* make this "go away" */
52963787Seric 					a->q_flags |= QDONTSEND;
53063787Seric 					a->q_flags &= ~QBADADDR;
53163787Seric 				}
53258929Seric 			}
53358929Seric 
5344976Seric 			/* collect the text of the message */
53524943Seric 			SmtpPhase = "collect";
53664766Seric 			collect(TRUE, doublequeue, e);
53764766Seric 			if (Errors != 0)
53864766Seric 				goto abortmessage;
53963965Seric 			HoldErrs = TRUE;
5404976Seric 
5418238Seric 			/*
5428238Seric 			**  Arrange to send to everyone.
5438238Seric 			**	If sending to multiple people, mail back
5448238Seric 			**		errors rather than reporting directly.
5458238Seric 			**	In any case, don't mail back errors for
5468238Seric 			**		anything that has happened up to
5478238Seric 			**		now (the other end will do this).
54810197Seric 			**	Truncate our transcript -- the mail has gotten
54910197Seric 			**		to us successfully, and if we have
55010197Seric 			**		to mail this back, it will be easier
55110197Seric 			**		on the reader.
5528238Seric 			**	Then send to everyone.
5538238Seric 			**	Finally give a reply code.  If an error has
5548238Seric 			**		already been given, don't mail a
5558238Seric 			**		message back.
5569339Seric 			**	We goose error returns by clearing error bit.
5578238Seric 			*/
5588238Seric 
55924943Seric 			SmtpPhase = "delivery";
56063787Seric 			if (nrcpts != 1 && !doublequeue)
5619378Seric 			{
5629378Seric 				HoldErrs = TRUE;
56358734Seric 				e->e_errormode = EM_MAIL;
5649378Seric 			}
56555012Seric 			e->e_xfp = freopen(queuename(e, 'x'), "w", e->e_xfp);
56658714Seric 			id = e->e_id;
5674976Seric 
5684976Seric 			/* send to all recipients */
56963787Seric 			sendall(e, doublequeue ? SM_QUEUE : SM_DEFAULT);
57055012Seric 			e->e_to = NULL;
5714976Seric 
5728238Seric 			/* issue success if appropriate and reset */
5738238Seric 			if (Errors == 0 || HoldErrs)
57458855Seric 				message("250 %s Message accepted for delivery", id);
57559747Seric 
57659747Seric 			if (bitset(EF_FATALERRS, e->e_flags) && !HoldErrs)
57759730Seric 			{
57859730Seric 				/* avoid sending back an extra message */
57959730Seric 				e->e_flags &= ~EF_FATALERRS;
58059747Seric 				e->e_flags |= EF_CLRQUEUE;
58159730Seric 			}
5828238Seric 			else
58358919Seric 			{
58459747Seric 				/* from now on, we have to operate silently */
58559747Seric 				HoldErrs = TRUE;
58659747Seric 				e->e_errormode = EM_MAIL;
58759747Seric 
58859730Seric 				/* if we just queued, poke it */
58963787Seric 				if (doublequeue && e->e_sendmode != SM_QUEUE)
59059730Seric 				{
59164296Seric 					extern pid_t dowork();
59264296Seric 
59359730Seric 					unlockqueue(e);
59464296Seric 					(void) dowork(id, TRUE, TRUE, e);
59559730Seric 				}
59658919Seric 			}
59758883Seric 
59859747Seric   abortmessage:
5999339Seric 			/* if in a child, pop back to our parent */
6009339Seric 			if (InChild)
6019339Seric 				finis();
60224943Seric 
60324943Seric 			/* clean up a bit */
60458109Seric 			gotmail = FALSE;
60555012Seric 			dropenvelope(e);
60658179Seric 			CurEnv = e = newenvelope(e, CurEnv);
60755012Seric 			e->e_flags = BlankEnvelope.e_flags;
6084549Seric 			break;
6094549Seric 
6104549Seric 		  case CMDRSET:		/* rset -- reset state */
61158151Seric 			message("250 Reset state");
61264359Seric 			e->e_flags |= EF_CLRQUEUE;
6139339Seric 			if (InChild)
6149339Seric 				finis();
61558109Seric 
61658109Seric 			/* clean up a bit */
61758109Seric 			gotmail = FALSE;
61858109Seric 			dropenvelope(e);
61958179Seric 			CurEnv = e = newenvelope(e, CurEnv);
6209339Seric 			break;
6214549Seric 
6224549Seric 		  case CMDVRFY:		/* vrfy -- verify address */
62358092Seric 		  case CMDEXPN:		/* expn -- expand address */
62458092Seric 			vrfy = c->cmdcode == CMDVRFY;
62558092Seric 			if (bitset(vrfy ? PRIV_NOVRFY : PRIV_NOEXPN,
62658092Seric 						PrivacyFlags))
62758082Seric 			{
62858412Seric 				if (vrfy)
62958412Seric 					message("252 Who's to say?");
63058412Seric 				else
63165192Seric 					message("502 Sorry, we do not allow this operation");
63265017Seric #ifdef LOG
63365017Seric 				if (LogLevel > 5)
63465017Seric 					syslog(LOG_INFO, "%s: %s [rejected]",
63565017Seric 						CurSmtpClient, inp);
63665017Seric #endif
63758082Seric 				break;
63858082Seric 			}
63958082Seric 			else if (!gothello &&
64058092Seric 				 bitset(vrfy ? PRIV_NEEDVRFYHELO : PRIV_NEEDEXPNHELO,
64158092Seric 						PrivacyFlags))
64258082Seric 			{
64358151Seric 				message("503 I demand that you introduce yourself first");
64458082Seric 				break;
64558082Seric 			}
64658092Seric 			if (runinchild(vrfy ? "SMTP-VRFY" : "SMTP-EXPN", e) > 0)
6479339Seric 				break;
64855173Seric #ifdef LOG
64958020Seric 			if (LogLevel > 5)
65065017Seric 				syslog(LOG_INFO, "%s: %s", CurSmtpClient, inp);
65155173Seric #endif
6525003Seric 			vrfyqueue = NULL;
6537762Seric 			QuickAbort = TRUE;
65458092Seric 			if (vrfy)
65558092Seric 				e->e_flags |= EF_VRFYONLY;
65662373Seric 			while (*p != '\0' && isascii(*p) && isspace(*p))
65762373Seric 				*p++;
65862373Seric 			if (*p == '\0')
65962373Seric 			{
66062373Seric 				message("501 Argument required");
66162373Seric 				Errors++;
66262373Seric 			}
66362373Seric 			else
66462373Seric 			{
66564284Seric 				(void) sendtolist(p, NULLADDR, &vrfyqueue, e);
66662373Seric 			}
6677762Seric 			if (Errors != 0)
6689339Seric 			{
6699339Seric 				if (InChild)
6709339Seric 					finis();
6717762Seric 				break;
6729339Seric 			}
67362373Seric 			if (vrfyqueue == NULL)
67462373Seric 			{
67562373Seric 				message("554 Nothing to %s", vrfy ? "VRFY" : "EXPN");
67662373Seric 			}
6775003Seric 			while (vrfyqueue != NULL)
6785003Seric 			{
67963971Seric 				a = vrfyqueue;
68063971Seric 				while ((a = a->q_next) != NULL &&
68163971Seric 				       bitset(QDONTSEND|QBADADDR, a->q_flags))
68263971Seric 					continue;
6837685Seric 				if (!bitset(QDONTSEND|QBADADDR, vrfyqueue->q_flags))
68458151Seric 					printvrfyaddr(vrfyqueue, a == NULL);
68563847Seric 				vrfyqueue = vrfyqueue->q_next;
6865003Seric 			}
6879339Seric 			if (InChild)
6889339Seric 				finis();
6894549Seric 			break;
6904549Seric 
6914549Seric 		  case CMDHELP:		/* help -- give user info */
6924577Seric 			help(p);
6934549Seric 			break;
6944549Seric 
6954549Seric 		  case CMDNOOP:		/* noop -- do nothing */
69664122Seric 			message("250 OK");
6974549Seric 			break;
6984549Seric 
6994549Seric 		  case CMDQUIT:		/* quit -- leave mail */
70058151Seric 			message("221 %s closing connection", MyHostName);
70161051Seric 
70266283Seric doquit:
70361051Seric 			/* avoid future 050 messages */
70466017Seric 			disconnect(1, e);
70561051Seric 
7069339Seric 			if (InChild)
7079339Seric 				ExitStat = EX_QUIT;
7084549Seric 			finis();
7094549Seric 
7108544Seric 		  case CMDVERB:		/* set verbose mode */
71159957Seric 			if (bitset(PRIV_NOEXPN, PrivacyFlags))
71259957Seric 			{
71359957Seric 				/* this would give out the same info */
71459957Seric 				message("502 Verbose unavailable");
71559957Seric 				break;
71659957Seric 			}
7178544Seric 			Verbose = TRUE;
71858734Seric 			e->e_sendmode = SM_DELIVER;
71959957Seric 			message("250 Verbose mode");
7208544Seric 			break;
7218544Seric 
7229314Seric 		  case CMDONEX:		/* doing one transaction only */
7239378Seric 			OneXact = TRUE;
72459957Seric 			message("250 Only one transaction");
7259314Seric 			break;
7269314Seric 
72736230Skarels # ifdef SMTPDEBUG
7289339Seric 		  case CMDDBGQSHOW:	/* show queues */
7296907Seric 			printf("Send Queue=");
73055012Seric 			printaddr(e->e_sendqueue, TRUE);
7315003Seric 			break;
7327275Seric 
7337275Seric 		  case CMDDBGDEBUG:	/* set debug mode */
7347676Seric 			tTsetup(tTdvect, sizeof tTdvect, "0-99.1");
7357676Seric 			tTflag(p);
73658151Seric 			message("200 Debug set");
7377275Seric 			break;
7387275Seric 
73936230Skarels # else /* not SMTPDEBUG */
74036230Skarels 		  case CMDDBGQSHOW:	/* show queues */
74136230Skarels 		  case CMDDBGDEBUG:	/* set debug mode */
74264685Seric # endif /* SMTPDEBUG */
74364685Seric 		  case CMDLOGBOGUS:	/* bogus command */
74436233Skarels # ifdef LOG
74558308Seric 			if (LogLevel > 0)
74664685Seric 				syslog(LOG_CRIT,
74758020Seric 				    "\"%s\" command from %s (%s)",
74866005Seric 				    c->cmdname, peerhostname,
74958755Seric 				    anynet_ntoa(&RealHostAddr));
75036233Skarels # endif
75136230Skarels 			/* FALL THROUGH */
75236230Skarels 
7534549Seric 		  case CMDERROR:	/* unknown command */
75466283Seric 			if (++badcommands > MAXBADCOMMANDS)
75566283Seric 			{
75666283Seric 				message("421 %s Too many bad commands; closing connection",
75766283Seric 					MyHostName);
75866283Seric 				goto doquit;
75966283Seric 			}
76066283Seric 
76158151Seric 			message("500 Command unrecognized");
7624549Seric 			break;
7634549Seric 
7644549Seric 		  default:
76536230Skarels 			errno = 0;
76658151Seric 			syserr("500 smtp: unknown code %d", c->cmdcode);
7674549Seric 			break;
7684549Seric 		}
7694549Seric 	}
7704549Seric }
7714549Seric /*
7724549Seric **  SKIPWORD -- skip a fixed word.
7734549Seric **
7744549Seric **	Parameters:
7754549Seric **		p -- place to start looking.
7764549Seric **		w -- word to skip.
7774549Seric **
7784549Seric **	Returns:
7794549Seric **		p following w.
7804549Seric **		NULL on error.
7814549Seric **
7824549Seric **	Side Effects:
7834549Seric **		clobbers the p data area.
7844549Seric */
7854549Seric 
7864549Seric static char *
7874549Seric skipword(p, w)
7884549Seric 	register char *p;
7894549Seric 	char *w;
7904549Seric {
7914549Seric 	register char *q;
79266005Seric 	char *firstp = p;
7934549Seric 
7944549Seric 	/* find beginning of word */
79558050Seric 	while (isascii(*p) && isspace(*p))
7964549Seric 		p++;
7974549Seric 	q = p;
7984549Seric 
7994549Seric 	/* find end of word */
80058050Seric 	while (*p != '\0' && *p != ':' && !(isascii(*p) && isspace(*p)))
8014549Seric 		p++;
80258050Seric 	while (isascii(*p) && isspace(*p))
8034549Seric 		*p++ = '\0';
8044549Seric 	if (*p != ':')
8054549Seric 	{
8064549Seric 	  syntax:
80766005Seric 		message("501 Syntax error in parameters scanning \"%s\"",
80866005Seric 			firstp);
8094549Seric 		Errors++;
8104549Seric 		return (NULL);
8114549Seric 	}
8124549Seric 	*p++ = '\0';
81358050Seric 	while (isascii(*p) && isspace(*p))
8144549Seric 		p++;
8154549Seric 
81662373Seric 	if (*p == '\0')
81762373Seric 		goto syntax;
81862373Seric 
8194549Seric 	/* see if the input word matches desired word */
82033725Sbostic 	if (strcasecmp(q, w))
8214549Seric 		goto syntax;
8224549Seric 
8234549Seric 	return (p);
8244549Seric }
8254577Seric /*
82658151Seric **  PRINTVRFYADDR -- print an entry in the verify queue
82758151Seric **
82858151Seric **	Parameters:
82958151Seric **		a -- the address to print
83058151Seric **		last -- set if this is the last one.
83158151Seric **
83258151Seric **	Returns:
83358151Seric **		none.
83458151Seric **
83558151Seric **	Side Effects:
83658151Seric **		Prints the appropriate 250 codes.
83758151Seric */
83858151Seric 
83958151Seric printvrfyaddr(a, last)
84058151Seric 	register ADDRESS *a;
84158151Seric 	bool last;
84258151Seric {
84358151Seric 	char fmtbuf[20];
84458151Seric 
84558151Seric 	strcpy(fmtbuf, "250");
84658151Seric 	fmtbuf[3] = last ? ' ' : '-';
84758151Seric 
84859746Seric 	if (a->q_fullname == NULL)
84959746Seric 	{
85059746Seric 		if (strchr(a->q_user, '@') == NULL)
85159746Seric 			strcpy(&fmtbuf[4], "<%s@%s>");
85259746Seric 		else
85359746Seric 			strcpy(&fmtbuf[4], "<%s>");
85459746Seric 		message(fmtbuf, a->q_user, MyHostName);
85559746Seric 	}
85658151Seric 	else
85758151Seric 	{
85859746Seric 		if (strchr(a->q_user, '@') == NULL)
85959746Seric 			strcpy(&fmtbuf[4], "%s <%s@%s>");
86059746Seric 		else
86159746Seric 			strcpy(&fmtbuf[4], "%s <%s>");
86259746Seric 		message(fmtbuf, a->q_fullname, a->q_user, MyHostName);
86358151Seric 	}
86458151Seric }
86558151Seric /*
8664577Seric **  HELP -- implement the HELP command.
8674577Seric **
8684577Seric **	Parameters:
8694577Seric **		topic -- the topic we want help for.
8704577Seric **
8714577Seric **	Returns:
8724577Seric **		none.
8734577Seric **
8744577Seric **	Side Effects:
8754577Seric **		outputs the help file to message output.
8764577Seric */
8774577Seric 
8784577Seric help(topic)
8794577Seric 	char *topic;
8804577Seric {
8814577Seric 	register FILE *hf;
8824577Seric 	int len;
8834577Seric 	char buf[MAXLINE];
8844577Seric 	bool noinfo;
8854577Seric 
8868269Seric 	if (HelpFile == NULL || (hf = fopen(HelpFile, "r")) == NULL)
8874577Seric 	{
8884577Seric 		/* no help */
88911931Seric 		errno = 0;
89058151Seric 		message("502 HELP not implemented");
8914577Seric 		return;
8924577Seric 	}
8934577Seric 
89449669Seric 	if (topic == NULL || *topic == '\0')
89549669Seric 		topic = "smtp";
89649669Seric 	else
89749669Seric 		makelower(topic);
89849669Seric 
8994577Seric 	len = strlen(topic);
9004577Seric 	noinfo = TRUE;
9014577Seric 
9024577Seric 	while (fgets(buf, sizeof buf, hf) != NULL)
9034577Seric 	{
9044577Seric 		if (strncmp(buf, topic, len) == 0)
9054577Seric 		{
9064577Seric 			register char *p;
9074577Seric 
90856795Seric 			p = strchr(buf, '\t');
9094577Seric 			if (p == NULL)
9104577Seric 				p = buf;
9114577Seric 			else
9124577Seric 				p++;
9134577Seric 			fixcrlf(p, TRUE);
91458151Seric 			message("214-%s", p);
9154577Seric 			noinfo = FALSE;
9164577Seric 		}
9174577Seric 	}
9184577Seric 
9194577Seric 	if (noinfo)
92058151Seric 		message("504 HELP topic unknown");
9214577Seric 	else
92258151Seric 		message("214 End of HELP info");
9234628Seric 	(void) fclose(hf);
9244577Seric }
9258544Seric /*
9269339Seric **  RUNINCHILD -- return twice -- once in the child, then in the parent again
9279339Seric **
9289339Seric **	Parameters:
9299339Seric **		label -- a string used in error messages
9309339Seric **
9319339Seric **	Returns:
9329339Seric **		zero in the child
9339339Seric **		one in the parent
9349339Seric **
9359339Seric **	Side Effects:
9369339Seric **		none.
9379339Seric */
9388544Seric 
93955012Seric runinchild(label, e)
9409339Seric 	char *label;
94155012Seric 	register ENVELOPE *e;
9429339Seric {
9439339Seric 	int childpid;
9449339Seric 
94516158Seric 	if (!OneXact)
9469339Seric 	{
94716158Seric 		childpid = dofork();
94816158Seric 		if (childpid < 0)
94916158Seric 		{
95016158Seric 			syserr("%s: cannot fork", label);
95116158Seric 			return (1);
95216158Seric 		}
95316158Seric 		if (childpid > 0)
95416158Seric 		{
95516158Seric 			auto int st;
9569339Seric 
95716158Seric 			/* parent -- wait for child to complete */
95861093Seric 			setproctitle("server %s child wait", CurHostName);
95916158Seric 			st = waitfor(childpid);
96016158Seric 			if (st == -1)
96116158Seric 				syserr("%s: lost child", label);
96264948Seric 			else if (!WIFEXITED(st))
96364948Seric 				syserr("%s: died on signal %d",
96464948Seric 					label, st & 0177);
9659339Seric 
96616158Seric 			/* if we exited on a QUIT command, complete the process */
96766017Seric 			if (WEXITSTATUS(st) == EX_QUIT)
96866017Seric 			{
96966017Seric 				disconnect(1, e);
97016158Seric 				finis();
97166017Seric 			}
9729339Seric 
97316158Seric 			return (1);
97416158Seric 		}
97516158Seric 		else
97616158Seric 		{
97716158Seric 			/* child */
97816158Seric 			InChild = TRUE;
97925050Seric 			QuickAbort = FALSE;
98055012Seric 			clearenvelope(e, FALSE);
98116158Seric 		}
9829339Seric 	}
98315256Seric 
98416158Seric 	/* open alias database */
98560537Seric 	initmaps(FALSE, e);
98616158Seric 
98716158Seric 	return (0);
9889339Seric }
9899339Seric 
99056795Seric # endif /* SMTP */
991