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*66017Seric static char sccsid[] = "@(#)srvrsmtp.c	8.29 (Berkeley) 02/06/94 (with SMTP)";
1433731Sbostic #else
15*66017Seric static char sccsid[] = "@(#)srvrsmtp.c	8.29 (Berkeley) 02/06/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 */
10966005Seric 	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);
12566005Seric 	peerhostname = RealHostName;
12666005Seric 	if (peerhostname == NULL)
12766005Seric 		peerhostname = "localhost";
12866005Seric 	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 */
181*66017Seric 			disconnect(1, e);
18258151Seric 			message("421 %s Lost input channel from %s",
18365017Seric 				MyHostName, CurSmtpClient);
18455464Seric #ifdef LOG
18563843Seric 			if (LogLevel > (gotmail ? 1 : 19))
18655464Seric 				syslog(LOG_NOTICE, "lost input channel from %s",
18765017Seric 					CurSmtpClient);
18855464Seric #endif
18958069Seric 			if (InChild)
19058069Seric 				ExitStat = EX_QUIT;
1914549Seric 			finis();
1924549Seric 		}
1934549Seric 
1944549Seric 		/* clean up end of line */
1954558Seric 		fixcrlf(inp, TRUE);
1964549Seric 
1974713Seric 		/* echo command to transcript */
19855012Seric 		if (e->e_xfp != NULL)
19955012Seric 			fprintf(e->e_xfp, "<<< %s\n", inp);
2004713Seric 
20159060Seric 		if (e->e_id == NULL)
20265058Seric 			setproctitle("%s: %.80s", CurSmtpClient, inp);
20359060Seric 		else
20465058Seric 			setproctitle("%s %s: %.80s", e->e_id, CurSmtpClient, inp);
20559060Seric 
2064549Seric 		/* break off command */
20758050Seric 		for (p = inp; isascii(*p) && isspace(*p); p++)
2084549Seric 			continue;
20957232Seric 		cmd = cmdbuf;
21058050Seric 		while (*p != '\0' &&
21158050Seric 		       !(isascii(*p) && isspace(*p)) &&
21258050Seric 		       cmd < &cmdbuf[sizeof cmdbuf - 2])
21324981Seric 			*cmd++ = *p++;
21424981Seric 		*cmd = '\0';
2154549Seric 
21625691Seric 		/* throw away leading whitespace */
21758050Seric 		while (isascii(*p) && isspace(*p))
21825691Seric 			p++;
21925691Seric 
2204549Seric 		/* decode command */
2214549Seric 		for (c = CmdTab; c->cmdname != NULL; c++)
2224549Seric 		{
22333725Sbostic 			if (!strcasecmp(c->cmdname, cmdbuf))
2244549Seric 				break;
2254549Seric 		}
2264549Seric 
22751954Seric 		/* reset errors */
22851954Seric 		errno = 0;
22951954Seric 
2304549Seric 		/* process command */
2314549Seric 		switch (c->cmdcode)
2324549Seric 		{
2334976Seric 		  case CMDHELO:		/* hello -- introduce yourself */
23458323Seric 		  case CMDEHLO:		/* extended hello */
23558323Seric 			if (c->cmdcode == CMDEHLO)
23658323Seric 			{
23758323Seric 				protocol = "ESMTP";
23861093Seric 				SmtpPhase = "server EHLO";
23958323Seric 			}
24058323Seric 			else
24158323Seric 			{
24258323Seric 				protocol = "SMTP";
24361093Seric 				SmtpPhase = "server HELO";
24458323Seric 			}
24559016Seric 			sendinghost = newstr(p);
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",
25165017Seric 					MyHostName, CurSmtpClient);
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)
27566005Seric 					sendinghost = peerhostname;
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,
30366005Seric 					"Host %s didn't use HELO protocol",
30466005Seric 					peerhostname);
30563753Seric 			}
30665947Seric #ifdef PICKY_HELO_CHECK
30766005Seric 			if (strcasecmp(sendinghost, peerhostname) != 0 &&
30866005Seric 			    (strcasecmp(peerhostname, "localhost") != 0 ||
30965823Seric 			     strcasecmp(sendinghost, MyHostName) != 0))
31065823Seric 			{
31165823Seric 				auth_warning(e, "Host %s claimed to be %s",
31266005Seric 					peerhostname, sendinghost);
31365823Seric 			}
31465947Seric #endif
31565823Seric 
31658323Seric 			if (protocol == NULL)
31758323Seric 				protocol = "SMTP";
31858323Seric 			define('r', protocol, e);
31959016Seric 			define('s', sendinghost, e);
32055012Seric 			initsys(e);
32159747Seric 			nrcpts = 0;
32265089Seric 			e->e_flags |= EF_LOGSENDER;
32365058Seric 			setproctitle("%s %s: %.80s", e->e_id, CurSmtpClient, inp);
3249339Seric 
3259339Seric 			/* child -- go do the processing */
3264549Seric 			p = skipword(p, "from");
3274549Seric 			if (p == NULL)
3284549Seric 				break;
32957977Seric 			if (setjmp(TopFrame) > 0)
33058147Seric 			{
33158147Seric 				/* this failed -- undo work */
33258147Seric 				if (InChild)
33359058Seric 				{
33459058Seric 					QuickAbort = FALSE;
33559058Seric 					SuprErrs = TRUE;
33663787Seric 					e->e_flags &= ~EF_FATALERRS;
33758147Seric 					finis();
33859058Seric 				}
33957977Seric 				break;
34058147Seric 			}
34157977Seric 			QuickAbort = TRUE;
34258333Seric 
34358333Seric 			/* must parse sender first */
34458333Seric 			delimptr = NULL;
34558704Seric 			setsender(p, e, &delimptr, FALSE);
34658333Seric 			p = delimptr;
34758333Seric 			if (p != NULL && *p != '\0')
34858333Seric 				*p++ = '\0';
34958333Seric 
35058333Seric 			/* now parse ESMTP arguments */
35158333Seric 			msize = 0;
35258333Seric 			for (; p != NULL && *p != '\0'; p++)
35358333Seric 			{
35458333Seric 				char *kp;
35558333Seric 				char *vp;
35658333Seric 
35758333Seric 				/* locate the beginning of the keyword */
35858333Seric 				while (isascii(*p) && isspace(*p))
35958333Seric 					p++;
36058333Seric 				if (*p == '\0')
36158333Seric 					break;
36258333Seric 				kp = p;
36358333Seric 
36458333Seric 				/* skip to the value portion */
36558333Seric 				while (isascii(*p) && isalnum(*p) || *p == '-')
36658333Seric 					p++;
36758333Seric 				if (*p == '=')
36858333Seric 				{
36958333Seric 					*p++ = '\0';
37058333Seric 					vp = p;
37158333Seric 
37258333Seric 					/* skip to the end of the value */
37358333Seric 					while (*p != '\0' && *p != ' ' &&
37458333Seric 					       !(isascii(*p) && iscntrl(*p)) &&
37558333Seric 					       *p != '=')
37658333Seric 						p++;
37758333Seric 				}
37858333Seric 
37958333Seric 				if (*p != '\0')
38058333Seric 					*p++ = '\0';
38158333Seric 
38258333Seric 				if (tTd(19, 1))
38358333Seric 					printf("MAIL: got arg %s=%s\n", kp,
38458333Seric 						vp == NULL ? "<null>" : vp);
38558333Seric 
38658333Seric 				if (strcasecmp(kp, "size") == 0)
38758333Seric 				{
38859093Seric 					if (vp == NULL)
38958333Seric 					{
39058333Seric 						usrerr("501 SIZE requires a value");
39158333Seric 						/* NOTREACHED */
39258333Seric 					}
39358333Seric 					msize = atol(vp);
39458333Seric 				}
39559093Seric 				else if (strcasecmp(kp, "body") == 0)
39659093Seric 				{
39759093Seric 					if (vp == NULL)
39859093Seric 					{
39959093Seric 						usrerr("501 BODY requires a value");
40059093Seric 						/* NOTREACHED */
40159093Seric 					}
40259093Seric # ifdef MIME
40359093Seric 					if (strcasecmp(vp, "8bitmime") == 0)
40459093Seric 					{
40559093Seric 						e->e_bodytype = "8BITMIME";
40659709Seric 						SevenBit = FALSE;
40759093Seric 					}
40859093Seric 					else if (strcasecmp(vp, "7bit") == 0)
40959093Seric 					{
41059093Seric 						e->e_bodytype = "7BIT";
41159709Seric 						SevenBit = TRUE;
41259093Seric 					}
41359093Seric 					else
41459093Seric 					{
41559093Seric 						usrerr("501 Unknown BODY type %s",
41659093Seric 							vp);
41759093Seric 					}
41859093Seric # endif
41959093Seric 				}
42058333Seric 				else
42158333Seric 				{
42258333Seric 					usrerr("501 %s parameter unrecognized", kp);
42358333Seric 					/* NOTREACHED */
42458333Seric 				}
42558333Seric 			}
42659284Seric 
42759284Seric 			if (MaxMessageSize > 0 && msize > MaxMessageSize)
42859284Seric 			{
42959284Seric 				usrerr("552 Message size exceeds fixed maximum message size (%ld)",
43059284Seric 					MaxMessageSize);
43159284Seric 				/* NOTREACHED */
43259284Seric 			}
43358333Seric 
43458333Seric 			if (!enoughspace(msize))
43558333Seric 			{
43658333Seric 				message("452 Insufficient disk space; try again later");
43758333Seric 				break;
43858333Seric 			}
43958151Seric 			message("250 Sender ok");
44058147Seric 			gotmail = TRUE;
4414549Seric 			break;
4424549Seric 
4434976Seric 		  case CMDRCPT:		/* rcpt -- designate recipient */
44458850Seric 			if (!gotmail)
44558850Seric 			{
44658850Seric 				usrerr("503 Need MAIL before RCPT");
44758850Seric 				break;
44858850Seric 			}
44961093Seric 			SmtpPhase = "server RCPT";
45012612Seric 			if (setjmp(TopFrame) > 0)
45114785Seric 			{
45255012Seric 				e->e_flags &= ~EF_FATALERRS;
45312612Seric 				break;
45414785Seric 			}
45512612Seric 			QuickAbort = TRUE;
45651951Seric 			LogUsrErrs = TRUE;
45758093Seric 
45859699Seric 			if (e->e_sendmode != SM_DELIVER)
45959699Seric 				e->e_flags |= EF_VRFYONLY;
46058919Seric 
4614549Seric 			p = skipword(p, "to");
4624549Seric 			if (p == NULL)
4634549Seric 				break;
46464284Seric 			a = parseaddr(p, NULLADDR, RF_COPYALL, ' ', NULL, e);
46512612Seric 			if (a == NULL)
46612612Seric 				break;
46716886Seric 			a->q_flags |= QPRIMARY;
46855012Seric 			a = recipient(a, &e->e_sendqueue, e);
46912612Seric 			if (Errors != 0)
47012612Seric 				break;
47112612Seric 
47212612Seric 			/* no errors during parsing, but might be a duplicate */
47355012Seric 			e->e_to = p;
47412612Seric 			if (!bitset(QBADADDR, a->q_flags))
47559747Seric 			{
47664718Seric 				message("250 Recipient ok%s",
47764718Seric 					bitset(QQUEUEUP, a->q_flags) ?
47864718Seric 						" (will queue)" : "");
47959747Seric 				nrcpts++;
48059747Seric 			}
48112612Seric 			else
4824549Seric 			{
48312612Seric 				/* punt -- should keep message in ADDRESS.... */
48458151Seric 				message("550 Addressee unknown");
4854549Seric 			}
48655012Seric 			e->e_to = NULL;
4874549Seric 			break;
4884549Seric 
4894549Seric 		  case CMDDATA:		/* data -- text of mail */
49061093Seric 			SmtpPhase = "server DATA";
49158109Seric 			if (!gotmail)
4924549Seric 			{
49358151Seric 				message("503 Need MAIL command");
4944976Seric 				break;
4954549Seric 			}
49664718Seric 			else if (nrcpts <= 0)
4974549Seric 			{
49858151Seric 				message("503 Need RCPT (recipient)");
4994976Seric 				break;
5004549Seric 			}
5014976Seric 
50258929Seric 			/* check to see if we need to re-expand aliases */
50363787Seric 			/* also reset QBADADDR on already-diagnosted addrs */
50463787Seric 			doublequeue = FALSE;
50558929Seric 			for (a = e->e_sendqueue; a != NULL; a = a->q_next)
50658929Seric 			{
50758929Seric 				if (bitset(QVERIFIED, a->q_flags))
50863787Seric 				{
50963787Seric 					/* need to re-expand aliases */
51063787Seric 					doublequeue = TRUE;
51163787Seric 				}
51263787Seric 				if (bitset(QBADADDR, a->q_flags))
51363787Seric 				{
51463787Seric 					/* make this "go away" */
51563787Seric 					a->q_flags |= QDONTSEND;
51663787Seric 					a->q_flags &= ~QBADADDR;
51763787Seric 				}
51858929Seric 			}
51958929Seric 
5204976Seric 			/* collect the text of the message */
52124943Seric 			SmtpPhase = "collect";
52264766Seric 			collect(TRUE, doublequeue, e);
52364766Seric 			if (Errors != 0)
52464766Seric 				goto abortmessage;
52563965Seric 			HoldErrs = TRUE;
5264976Seric 
5278238Seric 			/*
5288238Seric 			**  Arrange to send to everyone.
5298238Seric 			**	If sending to multiple people, mail back
5308238Seric 			**		errors rather than reporting directly.
5318238Seric 			**	In any case, don't mail back errors for
5328238Seric 			**		anything that has happened up to
5338238Seric 			**		now (the other end will do this).
53410197Seric 			**	Truncate our transcript -- the mail has gotten
53510197Seric 			**		to us successfully, and if we have
53610197Seric 			**		to mail this back, it will be easier
53710197Seric 			**		on the reader.
5388238Seric 			**	Then send to everyone.
5398238Seric 			**	Finally give a reply code.  If an error has
5408238Seric 			**		already been given, don't mail a
5418238Seric 			**		message back.
5429339Seric 			**	We goose error returns by clearing error bit.
5438238Seric 			*/
5448238Seric 
54524943Seric 			SmtpPhase = "delivery";
54663787Seric 			if (nrcpts != 1 && !doublequeue)
5479378Seric 			{
5489378Seric 				HoldErrs = TRUE;
54958734Seric 				e->e_errormode = EM_MAIL;
5509378Seric 			}
55155012Seric 			e->e_xfp = freopen(queuename(e, 'x'), "w", e->e_xfp);
55258714Seric 			id = e->e_id;
5534976Seric 
5544976Seric 			/* send to all recipients */
55563787Seric 			sendall(e, doublequeue ? SM_QUEUE : SM_DEFAULT);
55655012Seric 			e->e_to = NULL;
5574976Seric 
5588238Seric 			/* issue success if appropriate and reset */
5598238Seric 			if (Errors == 0 || HoldErrs)
56058855Seric 				message("250 %s Message accepted for delivery", id);
56159747Seric 
56259747Seric 			if (bitset(EF_FATALERRS, e->e_flags) && !HoldErrs)
56359730Seric 			{
56459730Seric 				/* avoid sending back an extra message */
56559730Seric 				e->e_flags &= ~EF_FATALERRS;
56659747Seric 				e->e_flags |= EF_CLRQUEUE;
56759730Seric 			}
5688238Seric 			else
56958919Seric 			{
57059747Seric 				/* from now on, we have to operate silently */
57159747Seric 				HoldErrs = TRUE;
57259747Seric 				e->e_errormode = EM_MAIL;
57359747Seric 
57459730Seric 				/* if we just queued, poke it */
57563787Seric 				if (doublequeue && e->e_sendmode != SM_QUEUE)
57659730Seric 				{
57764296Seric 					extern pid_t dowork();
57864296Seric 
57959730Seric 					unlockqueue(e);
58064296Seric 					(void) dowork(id, TRUE, TRUE, e);
58159730Seric 				}
58258919Seric 			}
58358883Seric 
58459747Seric   abortmessage:
5859339Seric 			/* if in a child, pop back to our parent */
5869339Seric 			if (InChild)
5879339Seric 				finis();
58824943Seric 
58924943Seric 			/* clean up a bit */
59058109Seric 			gotmail = FALSE;
59155012Seric 			dropenvelope(e);
59258179Seric 			CurEnv = e = newenvelope(e, CurEnv);
59355012Seric 			e->e_flags = BlankEnvelope.e_flags;
5944549Seric 			break;
5954549Seric 
5964549Seric 		  case CMDRSET:		/* rset -- reset state */
59758151Seric 			message("250 Reset state");
59864359Seric 			e->e_flags |= EF_CLRQUEUE;
5999339Seric 			if (InChild)
6009339Seric 				finis();
60158109Seric 
60258109Seric 			/* clean up a bit */
60358109Seric 			gotmail = FALSE;
60458109Seric 			dropenvelope(e);
60558179Seric 			CurEnv = e = newenvelope(e, CurEnv);
6069339Seric 			break;
6074549Seric 
6084549Seric 		  case CMDVRFY:		/* vrfy -- verify address */
60958092Seric 		  case CMDEXPN:		/* expn -- expand address */
61058092Seric 			vrfy = c->cmdcode == CMDVRFY;
61158092Seric 			if (bitset(vrfy ? PRIV_NOVRFY : PRIV_NOEXPN,
61258092Seric 						PrivacyFlags))
61358082Seric 			{
61458412Seric 				if (vrfy)
61558412Seric 					message("252 Who's to say?");
61658412Seric 				else
61765192Seric 					message("502 Sorry, we do not allow this operation");
61865017Seric #ifdef LOG
61965017Seric 				if (LogLevel > 5)
62065017Seric 					syslog(LOG_INFO, "%s: %s [rejected]",
62165017Seric 						CurSmtpClient, inp);
62265017Seric #endif
62358082Seric 				break;
62458082Seric 			}
62558082Seric 			else if (!gothello &&
62658092Seric 				 bitset(vrfy ? PRIV_NEEDVRFYHELO : PRIV_NEEDEXPNHELO,
62758092Seric 						PrivacyFlags))
62858082Seric 			{
62958151Seric 				message("503 I demand that you introduce yourself first");
63058082Seric 				break;
63158082Seric 			}
63258092Seric 			if (runinchild(vrfy ? "SMTP-VRFY" : "SMTP-EXPN", e) > 0)
6339339Seric 				break;
63455173Seric #ifdef LOG
63558020Seric 			if (LogLevel > 5)
63665017Seric 				syslog(LOG_INFO, "%s: %s", CurSmtpClient, inp);
63755173Seric #endif
6385003Seric 			vrfyqueue = NULL;
6397762Seric 			QuickAbort = TRUE;
64058092Seric 			if (vrfy)
64158092Seric 				e->e_flags |= EF_VRFYONLY;
64262373Seric 			while (*p != '\0' && isascii(*p) && isspace(*p))
64362373Seric 				*p++;
64462373Seric 			if (*p == '\0')
64562373Seric 			{
64662373Seric 				message("501 Argument required");
64762373Seric 				Errors++;
64862373Seric 			}
64962373Seric 			else
65062373Seric 			{
65164284Seric 				(void) sendtolist(p, NULLADDR, &vrfyqueue, e);
65262373Seric 			}
6537762Seric 			if (Errors != 0)
6549339Seric 			{
6559339Seric 				if (InChild)
6569339Seric 					finis();
6577762Seric 				break;
6589339Seric 			}
65962373Seric 			if (vrfyqueue == NULL)
66062373Seric 			{
66162373Seric 				message("554 Nothing to %s", vrfy ? "VRFY" : "EXPN");
66262373Seric 			}
6635003Seric 			while (vrfyqueue != NULL)
6645003Seric 			{
66563971Seric 				a = vrfyqueue;
66663971Seric 				while ((a = a->q_next) != NULL &&
66763971Seric 				       bitset(QDONTSEND|QBADADDR, a->q_flags))
66863971Seric 					continue;
6697685Seric 				if (!bitset(QDONTSEND|QBADADDR, vrfyqueue->q_flags))
67058151Seric 					printvrfyaddr(vrfyqueue, a == NULL);
67163847Seric 				vrfyqueue = vrfyqueue->q_next;
6725003Seric 			}
6739339Seric 			if (InChild)
6749339Seric 				finis();
6754549Seric 			break;
6764549Seric 
6774549Seric 		  case CMDHELP:		/* help -- give user info */
6784577Seric 			help(p);
6794549Seric 			break;
6804549Seric 
6814549Seric 		  case CMDNOOP:		/* noop -- do nothing */
68264122Seric 			message("250 OK");
6834549Seric 			break;
6844549Seric 
6854549Seric 		  case CMDQUIT:		/* quit -- leave mail */
68658151Seric 			message("221 %s closing connection", MyHostName);
68761051Seric 
68861051Seric 			/* avoid future 050 messages */
689*66017Seric 			disconnect(1, e);
69061051Seric 
6919339Seric 			if (InChild)
6929339Seric 				ExitStat = EX_QUIT;
6934549Seric 			finis();
6944549Seric 
6958544Seric 		  case CMDVERB:		/* set verbose mode */
69659957Seric 			if (bitset(PRIV_NOEXPN, PrivacyFlags))
69759957Seric 			{
69859957Seric 				/* this would give out the same info */
69959957Seric 				message("502 Verbose unavailable");
70059957Seric 				break;
70159957Seric 			}
7028544Seric 			Verbose = TRUE;
70358734Seric 			e->e_sendmode = SM_DELIVER;
70459957Seric 			message("250 Verbose mode");
7058544Seric 			break;
7068544Seric 
7079314Seric 		  case CMDONEX:		/* doing one transaction only */
7089378Seric 			OneXact = TRUE;
70959957Seric 			message("250 Only one transaction");
7109314Seric 			break;
7119314Seric 
71236230Skarels # ifdef SMTPDEBUG
7139339Seric 		  case CMDDBGQSHOW:	/* show queues */
7146907Seric 			printf("Send Queue=");
71555012Seric 			printaddr(e->e_sendqueue, TRUE);
7165003Seric 			break;
7177275Seric 
7187275Seric 		  case CMDDBGDEBUG:	/* set debug mode */
7197676Seric 			tTsetup(tTdvect, sizeof tTdvect, "0-99.1");
7207676Seric 			tTflag(p);
72158151Seric 			message("200 Debug set");
7227275Seric 			break;
7237275Seric 
72436230Skarels # else /* not SMTPDEBUG */
72536230Skarels 		  case CMDDBGQSHOW:	/* show queues */
72636230Skarels 		  case CMDDBGDEBUG:	/* set debug mode */
72764685Seric # endif /* SMTPDEBUG */
72864685Seric 		  case CMDLOGBOGUS:	/* bogus command */
72936233Skarels # ifdef LOG
73058308Seric 			if (LogLevel > 0)
73164685Seric 				syslog(LOG_CRIT,
73258020Seric 				    "\"%s\" command from %s (%s)",
73366005Seric 				    c->cmdname, peerhostname,
73458755Seric 				    anynet_ntoa(&RealHostAddr));
73536233Skarels # endif
73636230Skarels 			/* FALL THROUGH */
73736230Skarels 
7384549Seric 		  case CMDERROR:	/* unknown command */
73958151Seric 			message("500 Command unrecognized");
7404549Seric 			break;
7414549Seric 
7424549Seric 		  default:
74336230Skarels 			errno = 0;
74458151Seric 			syserr("500 smtp: unknown code %d", c->cmdcode);
7454549Seric 			break;
7464549Seric 		}
7474549Seric 	}
7484549Seric }
7494549Seric /*
7504549Seric **  SKIPWORD -- skip a fixed word.
7514549Seric **
7524549Seric **	Parameters:
7534549Seric **		p -- place to start looking.
7544549Seric **		w -- word to skip.
7554549Seric **
7564549Seric **	Returns:
7574549Seric **		p following w.
7584549Seric **		NULL on error.
7594549Seric **
7604549Seric **	Side Effects:
7614549Seric **		clobbers the p data area.
7624549Seric */
7634549Seric 
7644549Seric static char *
7654549Seric skipword(p, w)
7664549Seric 	register char *p;
7674549Seric 	char *w;
7684549Seric {
7694549Seric 	register char *q;
77066005Seric 	char *firstp = p;
7714549Seric 
7724549Seric 	/* find beginning of word */
77358050Seric 	while (isascii(*p) && isspace(*p))
7744549Seric 		p++;
7754549Seric 	q = p;
7764549Seric 
7774549Seric 	/* find end of word */
77858050Seric 	while (*p != '\0' && *p != ':' && !(isascii(*p) && isspace(*p)))
7794549Seric 		p++;
78058050Seric 	while (isascii(*p) && isspace(*p))
7814549Seric 		*p++ = '\0';
7824549Seric 	if (*p != ':')
7834549Seric 	{
7844549Seric 	  syntax:
78566005Seric 		message("501 Syntax error in parameters scanning \"%s\"",
78666005Seric 			firstp);
7874549Seric 		Errors++;
7884549Seric 		return (NULL);
7894549Seric 	}
7904549Seric 	*p++ = '\0';
79158050Seric 	while (isascii(*p) && isspace(*p))
7924549Seric 		p++;
7934549Seric 
79462373Seric 	if (*p == '\0')
79562373Seric 		goto syntax;
79662373Seric 
7974549Seric 	/* see if the input word matches desired word */
79833725Sbostic 	if (strcasecmp(q, w))
7994549Seric 		goto syntax;
8004549Seric 
8014549Seric 	return (p);
8024549Seric }
8034577Seric /*
80458151Seric **  PRINTVRFYADDR -- print an entry in the verify queue
80558151Seric **
80658151Seric **	Parameters:
80758151Seric **		a -- the address to print
80858151Seric **		last -- set if this is the last one.
80958151Seric **
81058151Seric **	Returns:
81158151Seric **		none.
81258151Seric **
81358151Seric **	Side Effects:
81458151Seric **		Prints the appropriate 250 codes.
81558151Seric */
81658151Seric 
81758151Seric printvrfyaddr(a, last)
81858151Seric 	register ADDRESS *a;
81958151Seric 	bool last;
82058151Seric {
82158151Seric 	char fmtbuf[20];
82258151Seric 
82358151Seric 	strcpy(fmtbuf, "250");
82458151Seric 	fmtbuf[3] = last ? ' ' : '-';
82558151Seric 
82659746Seric 	if (a->q_fullname == NULL)
82759746Seric 	{
82859746Seric 		if (strchr(a->q_user, '@') == NULL)
82959746Seric 			strcpy(&fmtbuf[4], "<%s@%s>");
83059746Seric 		else
83159746Seric 			strcpy(&fmtbuf[4], "<%s>");
83259746Seric 		message(fmtbuf, a->q_user, MyHostName);
83359746Seric 	}
83458151Seric 	else
83558151Seric 	{
83659746Seric 		if (strchr(a->q_user, '@') == NULL)
83759746Seric 			strcpy(&fmtbuf[4], "%s <%s@%s>");
83859746Seric 		else
83959746Seric 			strcpy(&fmtbuf[4], "%s <%s>");
84059746Seric 		message(fmtbuf, a->q_fullname, a->q_user, MyHostName);
84158151Seric 	}
84258151Seric }
84358151Seric /*
8444577Seric **  HELP -- implement the HELP command.
8454577Seric **
8464577Seric **	Parameters:
8474577Seric **		topic -- the topic we want help for.
8484577Seric **
8494577Seric **	Returns:
8504577Seric **		none.
8514577Seric **
8524577Seric **	Side Effects:
8534577Seric **		outputs the help file to message output.
8544577Seric */
8554577Seric 
8564577Seric help(topic)
8574577Seric 	char *topic;
8584577Seric {
8594577Seric 	register FILE *hf;
8604577Seric 	int len;
8614577Seric 	char buf[MAXLINE];
8624577Seric 	bool noinfo;
8634577Seric 
8648269Seric 	if (HelpFile == NULL || (hf = fopen(HelpFile, "r")) == NULL)
8654577Seric 	{
8664577Seric 		/* no help */
86711931Seric 		errno = 0;
86858151Seric 		message("502 HELP not implemented");
8694577Seric 		return;
8704577Seric 	}
8714577Seric 
87249669Seric 	if (topic == NULL || *topic == '\0')
87349669Seric 		topic = "smtp";
87449669Seric 	else
87549669Seric 		makelower(topic);
87649669Seric 
8774577Seric 	len = strlen(topic);
8784577Seric 	noinfo = TRUE;
8794577Seric 
8804577Seric 	while (fgets(buf, sizeof buf, hf) != NULL)
8814577Seric 	{
8824577Seric 		if (strncmp(buf, topic, len) == 0)
8834577Seric 		{
8844577Seric 			register char *p;
8854577Seric 
88656795Seric 			p = strchr(buf, '\t');
8874577Seric 			if (p == NULL)
8884577Seric 				p = buf;
8894577Seric 			else
8904577Seric 				p++;
8914577Seric 			fixcrlf(p, TRUE);
89258151Seric 			message("214-%s", p);
8934577Seric 			noinfo = FALSE;
8944577Seric 		}
8954577Seric 	}
8964577Seric 
8974577Seric 	if (noinfo)
89858151Seric 		message("504 HELP topic unknown");
8994577Seric 	else
90058151Seric 		message("214 End of HELP info");
9014628Seric 	(void) fclose(hf);
9024577Seric }
9038544Seric /*
9049339Seric **  RUNINCHILD -- return twice -- once in the child, then in the parent again
9059339Seric **
9069339Seric **	Parameters:
9079339Seric **		label -- a string used in error messages
9089339Seric **
9099339Seric **	Returns:
9109339Seric **		zero in the child
9119339Seric **		one in the parent
9129339Seric **
9139339Seric **	Side Effects:
9149339Seric **		none.
9159339Seric */
9168544Seric 
91755012Seric runinchild(label, e)
9189339Seric 	char *label;
91955012Seric 	register ENVELOPE *e;
9209339Seric {
9219339Seric 	int childpid;
9229339Seric 
92316158Seric 	if (!OneXact)
9249339Seric 	{
92516158Seric 		childpid = dofork();
92616158Seric 		if (childpid < 0)
92716158Seric 		{
92816158Seric 			syserr("%s: cannot fork", label);
92916158Seric 			return (1);
93016158Seric 		}
93116158Seric 		if (childpid > 0)
93216158Seric 		{
93316158Seric 			auto int st;
9349339Seric 
93516158Seric 			/* parent -- wait for child to complete */
93661093Seric 			setproctitle("server %s child wait", CurHostName);
93716158Seric 			st = waitfor(childpid);
93816158Seric 			if (st == -1)
93916158Seric 				syserr("%s: lost child", label);
94064948Seric 			else if (!WIFEXITED(st))
94164948Seric 				syserr("%s: died on signal %d",
94264948Seric 					label, st & 0177);
9439339Seric 
94416158Seric 			/* if we exited on a QUIT command, complete the process */
945*66017Seric 			if (WEXITSTATUS(st) == EX_QUIT)
946*66017Seric 			{
947*66017Seric 				disconnect(1, e);
94816158Seric 				finis();
949*66017Seric 			}
9509339Seric 
95116158Seric 			return (1);
95216158Seric 		}
95316158Seric 		else
95416158Seric 		{
95516158Seric 			/* child */
95616158Seric 			InChild = TRUE;
95725050Seric 			QuickAbort = FALSE;
95855012Seric 			clearenvelope(e, FALSE);
95916158Seric 		}
9609339Seric 	}
96115256Seric 
96216158Seric 	/* open alias database */
96360537Seric 	initmaps(FALSE, e);
96416158Seric 
96516158Seric 	return (0);
9669339Seric }
9679339Seric 
96856795Seric # endif /* SMTP */
969