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*65947Seric static char sccsid[] = "@(#)srvrsmtp.c	8.26 (Berkeley) 01/31/94 (with SMTP)";
1433731Sbostic #else
15*65947Seric static char sccsid[] = "@(#)srvrsmtp.c	8.26 (Berkeley) 01/31/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 */
10958333Seric 	auto char *delimptr;
11058714Seric 	char *id;
11159747Seric 	int nrcpts;			/* number of RCPT commands */
11263787Seric 	bool doublequeue;
1138544Seric 	char inp[MAXLINE];
11457232Seric 	char cmdbuf[MAXLINE];
1157124Seric 	extern char Version[];
11624943Seric 	extern ENVELOPE BlankEnvelope;
1174549Seric 
11859066Seric 	if (fileno(OutChannel) != fileno(stdout))
1197363Seric 	{
1207363Seric 		/* arrange for debugging output to go to remote host */
12159066Seric 		(void) dup2(fileno(OutChannel), fileno(stdout));
1227363Seric 	}
12355012Seric 	settime(e);
12457642Seric 	CurHostName = RealHostName;
12565017Seric 	CurSmtpClient = macvalue('_', e);
12665017Seric 	if (CurSmtpClient == NULL)
12765017Seric 		CurSmtpClient = RealHostName;
12865017Seric 
12965017Seric 	setproctitle("server %s startup", CurSmtpClient);
13058050Seric 	expand("\201e", inp, &inp[sizeof inp], e);
13164496Seric 	if (BrokenSmtpPeers)
13264496Seric 	{
13364496Seric 		message("220 %s", inp);
13464496Seric 	}
13564496Seric 	else
13664496Seric 	{
13764496Seric 		message("220-%s", inp);
13864496Seric 		message("220 ESMTP spoken here");
13964496Seric 	}
14058330Seric 	protocol = NULL;
14159016Seric 	sendinghost = macvalue('s', e);
14258082Seric 	gothello = FALSE;
14358330Seric 	gotmail = FALSE;
1444549Seric 	for (;;)
1454549Seric 	{
14612612Seric 		/* arrange for backout */
14765751Seric 		if (setjmp(TopFrame) > 0)
14859058Seric 		{
14965751Seric 			/* if() nesting is necessary for Cray UNICOS */
15065751Seric 			if (InChild)
15165751Seric 			{
15265751Seric 				QuickAbort = FALSE;
15365751Seric 				SuprErrs = TRUE;
15465751Seric 				finis();
15565751Seric 			}
15659058Seric 		}
15712612Seric 		QuickAbort = FALSE;
15812612Seric 		HoldErrs = FALSE;
15951951Seric 		LogUsrErrs = FALSE;
16063843Seric 		e->e_flags &= ~(EF_VRFYONLY|EF_GLOBALERRS);
16112612Seric 
1627356Seric 		/* setup for the read */
16355012Seric 		e->e_to = NULL;
1644577Seric 		Errors = 0;
1657275Seric 		(void) fflush(stdout);
1667356Seric 
1677356Seric 		/* read the input line */
16861093Seric 		SmtpPhase = "server cmd read";
16961093Seric 		setproctitle("server %s cmd read", CurHostName);
17061093Seric 		p = sfgets(inp, sizeof inp, InChannel, TimeOuts.to_nextcommand,
17161093Seric 				SmtpPhase);
1727356Seric 
1737685Seric 		/* handle errors */
1747356Seric 		if (p == NULL)
1757356Seric 		{
1764549Seric 			/* end of file, just die */
17758151Seric 			message("421 %s Lost input channel from %s",
17865017Seric 				MyHostName, CurSmtpClient);
17955464Seric #ifdef LOG
18063843Seric 			if (LogLevel > (gotmail ? 1 : 19))
18155464Seric 				syslog(LOG_NOTICE, "lost input channel from %s",
18265017Seric 					CurSmtpClient);
18355464Seric #endif
18458069Seric 			if (InChild)
18558069Seric 				ExitStat = EX_QUIT;
1864549Seric 			finis();
1874549Seric 		}
1884549Seric 
1894549Seric 		/* clean up end of line */
1904558Seric 		fixcrlf(inp, TRUE);
1914549Seric 
1924713Seric 		/* echo command to transcript */
19355012Seric 		if (e->e_xfp != NULL)
19455012Seric 			fprintf(e->e_xfp, "<<< %s\n", inp);
1954713Seric 
19659060Seric 		if (e->e_id == NULL)
19765058Seric 			setproctitle("%s: %.80s", CurSmtpClient, inp);
19859060Seric 		else
19965058Seric 			setproctitle("%s %s: %.80s", e->e_id, CurSmtpClient, inp);
20059060Seric 
2014549Seric 		/* break off command */
20258050Seric 		for (p = inp; isascii(*p) && isspace(*p); p++)
2034549Seric 			continue;
20457232Seric 		cmd = cmdbuf;
20558050Seric 		while (*p != '\0' &&
20658050Seric 		       !(isascii(*p) && isspace(*p)) &&
20758050Seric 		       cmd < &cmdbuf[sizeof cmdbuf - 2])
20824981Seric 			*cmd++ = *p++;
20924981Seric 		*cmd = '\0';
2104549Seric 
21125691Seric 		/* throw away leading whitespace */
21258050Seric 		while (isascii(*p) && isspace(*p))
21325691Seric 			p++;
21425691Seric 
2154549Seric 		/* decode command */
2164549Seric 		for (c = CmdTab; c->cmdname != NULL; c++)
2174549Seric 		{
21833725Sbostic 			if (!strcasecmp(c->cmdname, cmdbuf))
2194549Seric 				break;
2204549Seric 		}
2214549Seric 
22251954Seric 		/* reset errors */
22351954Seric 		errno = 0;
22451954Seric 
2254549Seric 		/* process command */
2264549Seric 		switch (c->cmdcode)
2274549Seric 		{
2284976Seric 		  case CMDHELO:		/* hello -- introduce yourself */
22958323Seric 		  case CMDEHLO:		/* extended hello */
23058323Seric 			if (c->cmdcode == CMDEHLO)
23158323Seric 			{
23258323Seric 				protocol = "ESMTP";
23361093Seric 				SmtpPhase = "server EHLO";
23458323Seric 			}
23558323Seric 			else
23658323Seric 			{
23758323Seric 				protocol = "SMTP";
23861093Seric 				SmtpPhase = "server HELO";
23958323Seric 			}
24059016Seric 			sendinghost = newstr(p);
24160210Seric 			gothello = TRUE;
24260210Seric 			if (c->cmdcode != CMDEHLO)
24360239Seric 			{
24460239Seric 				/* print old message and be done with it */
24560239Seric 				message("250 %s Hello %s, pleased to meet you",
24665017Seric 					MyHostName, CurSmtpClient);
24760210Seric 				break;
24860239Seric 			}
24960239Seric 
25060239Seric 			/* print extended message and brag */
25160239Seric 			message("250-%s Hello %s, pleased to meet you",
25260239Seric 				MyHostName, p);
25358323Seric 			if (!bitset(PRIV_NOEXPN, PrivacyFlags))
25458323Seric 				message("250-EXPN");
25564359Seric 			if (MaxMessageSize > 0)
25664359Seric 				message("250-SIZE %ld", MaxMessageSize);
25759271Seric 			else
25859271Seric 				message("250-SIZE");
25958323Seric 			message("250 HELP");
2604976Seric 			break;
2614976Seric 
2624549Seric 		  case CMDMAIL:		/* mail -- designate sender */
26361093Seric 			SmtpPhase = "server MAIL";
26424943Seric 
2659314Seric 			/* check for validity of this command */
26658789Seric 			if (!gothello)
26758082Seric 			{
26858957Seric 				/* set sending host to our known value */
26959016Seric 				if (sendinghost == NULL)
27059016Seric 					sendinghost = RealHostName;
27158957Seric 
27258789Seric 				if (bitset(PRIV_NEEDMAILHELO, PrivacyFlags))
27358821Seric 				{
27458789Seric 					message("503 Polite people say HELO first");
27558821Seric 					break;
27658821Seric 				}
27758082Seric 			}
27858109Seric 			if (gotmail)
2794558Seric 			{
28058151Seric 				message("503 Sender already specified");
28163843Seric 				if (InChild)
28263843Seric 					finis();
2834558Seric 				break;
2844558Seric 			}
2859339Seric 			if (InChild)
2869339Seric 			{
28736230Skarels 				errno = 0;
28858151Seric 				syserr("503 Nested MAIL command: MAIL %s", p);
28958069Seric 				finis();
2909339Seric 			}
2919339Seric 
2929339Seric 			/* fork a subprocess to process this command */
29355012Seric 			if (runinchild("SMTP-MAIL", e) > 0)
2949339Seric 				break;
29563753Seric 			if (!gothello)
29663753Seric 			{
29763753Seric 				auth_warning(e,
29863753Seric 					"Host %s didn't use HELO protocol",
29963753Seric 					RealHostName);
30063753Seric 			}
301*65947Seric #ifdef PICKY_HELO_CHECK
30265823Seric 			if (strcasecmp(sendinghost, RealHostName) != 0 &&
30365823Seric 			    (strcasecmp(RealHostName, "localhost") != 0 ||
30465823Seric 			     strcasecmp(sendinghost, MyHostName) != 0))
30565823Seric 			{
30665823Seric 				auth_warning(e, "Host %s claimed to be %s",
30765823Seric 					RealHostName, sendinghost);
30865823Seric 			}
309*65947Seric #endif
31065823Seric 
31158323Seric 			if (protocol == NULL)
31258323Seric 				protocol = "SMTP";
31358323Seric 			define('r', protocol, e);
31459016Seric 			define('s', sendinghost, e);
31555012Seric 			initsys(e);
31659747Seric 			nrcpts = 0;
31765089Seric 			e->e_flags |= EF_LOGSENDER;
31865058Seric 			setproctitle("%s %s: %.80s", e->e_id, CurSmtpClient, inp);
3199339Seric 
3209339Seric 			/* child -- go do the processing */
3214549Seric 			p = skipword(p, "from");
3224549Seric 			if (p == NULL)
3234549Seric 				break;
32457977Seric 			if (setjmp(TopFrame) > 0)
32558147Seric 			{
32658147Seric 				/* this failed -- undo work */
32758147Seric 				if (InChild)
32859058Seric 				{
32959058Seric 					QuickAbort = FALSE;
33059058Seric 					SuprErrs = TRUE;
33163787Seric 					e->e_flags &= ~EF_FATALERRS;
33258147Seric 					finis();
33359058Seric 				}
33457977Seric 				break;
33558147Seric 			}
33657977Seric 			QuickAbort = TRUE;
33758333Seric 
33858333Seric 			/* must parse sender first */
33958333Seric 			delimptr = NULL;
34058704Seric 			setsender(p, e, &delimptr, FALSE);
34158333Seric 			p = delimptr;
34258333Seric 			if (p != NULL && *p != '\0')
34358333Seric 				*p++ = '\0';
34458333Seric 
34558333Seric 			/* now parse ESMTP arguments */
34658333Seric 			msize = 0;
34758333Seric 			for (; p != NULL && *p != '\0'; p++)
34858333Seric 			{
34958333Seric 				char *kp;
35058333Seric 				char *vp;
35158333Seric 
35258333Seric 				/* locate the beginning of the keyword */
35358333Seric 				while (isascii(*p) && isspace(*p))
35458333Seric 					p++;
35558333Seric 				if (*p == '\0')
35658333Seric 					break;
35758333Seric 				kp = p;
35858333Seric 
35958333Seric 				/* skip to the value portion */
36058333Seric 				while (isascii(*p) && isalnum(*p) || *p == '-')
36158333Seric 					p++;
36258333Seric 				if (*p == '=')
36358333Seric 				{
36458333Seric 					*p++ = '\0';
36558333Seric 					vp = p;
36658333Seric 
36758333Seric 					/* skip to the end of the value */
36858333Seric 					while (*p != '\0' && *p != ' ' &&
36958333Seric 					       !(isascii(*p) && iscntrl(*p)) &&
37058333Seric 					       *p != '=')
37158333Seric 						p++;
37258333Seric 				}
37358333Seric 
37458333Seric 				if (*p != '\0')
37558333Seric 					*p++ = '\0';
37658333Seric 
37758333Seric 				if (tTd(19, 1))
37858333Seric 					printf("MAIL: got arg %s=%s\n", kp,
37958333Seric 						vp == NULL ? "<null>" : vp);
38058333Seric 
38158333Seric 				if (strcasecmp(kp, "size") == 0)
38258333Seric 				{
38359093Seric 					if (vp == NULL)
38458333Seric 					{
38558333Seric 						usrerr("501 SIZE requires a value");
38658333Seric 						/* NOTREACHED */
38758333Seric 					}
38858333Seric 					msize = atol(vp);
38958333Seric 				}
39059093Seric 				else if (strcasecmp(kp, "body") == 0)
39159093Seric 				{
39259093Seric 					if (vp == NULL)
39359093Seric 					{
39459093Seric 						usrerr("501 BODY requires a value");
39559093Seric 						/* NOTREACHED */
39659093Seric 					}
39759093Seric # ifdef MIME
39859093Seric 					if (strcasecmp(vp, "8bitmime") == 0)
39959093Seric 					{
40059093Seric 						e->e_bodytype = "8BITMIME";
40159709Seric 						SevenBit = FALSE;
40259093Seric 					}
40359093Seric 					else if (strcasecmp(vp, "7bit") == 0)
40459093Seric 					{
40559093Seric 						e->e_bodytype = "7BIT";
40659709Seric 						SevenBit = TRUE;
40759093Seric 					}
40859093Seric 					else
40959093Seric 					{
41059093Seric 						usrerr("501 Unknown BODY type %s",
41159093Seric 							vp);
41259093Seric 					}
41359093Seric # endif
41459093Seric 				}
41558333Seric 				else
41658333Seric 				{
41758333Seric 					usrerr("501 %s parameter unrecognized", kp);
41858333Seric 					/* NOTREACHED */
41958333Seric 				}
42058333Seric 			}
42159284Seric 
42259284Seric 			if (MaxMessageSize > 0 && msize > MaxMessageSize)
42359284Seric 			{
42459284Seric 				usrerr("552 Message size exceeds fixed maximum message size (%ld)",
42559284Seric 					MaxMessageSize);
42659284Seric 				/* NOTREACHED */
42759284Seric 			}
42858333Seric 
42958333Seric 			if (!enoughspace(msize))
43058333Seric 			{
43158333Seric 				message("452 Insufficient disk space; try again later");
43258333Seric 				break;
43358333Seric 			}
43458151Seric 			message("250 Sender ok");
43558147Seric 			gotmail = TRUE;
4364549Seric 			break;
4374549Seric 
4384976Seric 		  case CMDRCPT:		/* rcpt -- designate recipient */
43958850Seric 			if (!gotmail)
44058850Seric 			{
44158850Seric 				usrerr("503 Need MAIL before RCPT");
44258850Seric 				break;
44358850Seric 			}
44461093Seric 			SmtpPhase = "server RCPT";
44512612Seric 			if (setjmp(TopFrame) > 0)
44614785Seric 			{
44755012Seric 				e->e_flags &= ~EF_FATALERRS;
44812612Seric 				break;
44914785Seric 			}
45012612Seric 			QuickAbort = TRUE;
45151951Seric 			LogUsrErrs = TRUE;
45258093Seric 
45359699Seric 			if (e->e_sendmode != SM_DELIVER)
45459699Seric 				e->e_flags |= EF_VRFYONLY;
45558919Seric 
4564549Seric 			p = skipword(p, "to");
4574549Seric 			if (p == NULL)
4584549Seric 				break;
45964284Seric 			a = parseaddr(p, NULLADDR, RF_COPYALL, ' ', NULL, e);
46012612Seric 			if (a == NULL)
46112612Seric 				break;
46216886Seric 			a->q_flags |= QPRIMARY;
46355012Seric 			a = recipient(a, &e->e_sendqueue, e);
46412612Seric 			if (Errors != 0)
46512612Seric 				break;
46612612Seric 
46712612Seric 			/* no errors during parsing, but might be a duplicate */
46855012Seric 			e->e_to = p;
46912612Seric 			if (!bitset(QBADADDR, a->q_flags))
47059747Seric 			{
47164718Seric 				message("250 Recipient ok%s",
47264718Seric 					bitset(QQUEUEUP, a->q_flags) ?
47364718Seric 						" (will queue)" : "");
47459747Seric 				nrcpts++;
47559747Seric 			}
47612612Seric 			else
4774549Seric 			{
47812612Seric 				/* punt -- should keep message in ADDRESS.... */
47958151Seric 				message("550 Addressee unknown");
4804549Seric 			}
48155012Seric 			e->e_to = NULL;
4824549Seric 			break;
4834549Seric 
4844549Seric 		  case CMDDATA:		/* data -- text of mail */
48561093Seric 			SmtpPhase = "server DATA";
48658109Seric 			if (!gotmail)
4874549Seric 			{
48858151Seric 				message("503 Need MAIL command");
4894976Seric 				break;
4904549Seric 			}
49164718Seric 			else if (nrcpts <= 0)
4924549Seric 			{
49358151Seric 				message("503 Need RCPT (recipient)");
4944976Seric 				break;
4954549Seric 			}
4964976Seric 
49758929Seric 			/* check to see if we need to re-expand aliases */
49863787Seric 			/* also reset QBADADDR on already-diagnosted addrs */
49963787Seric 			doublequeue = FALSE;
50058929Seric 			for (a = e->e_sendqueue; a != NULL; a = a->q_next)
50158929Seric 			{
50258929Seric 				if (bitset(QVERIFIED, a->q_flags))
50363787Seric 				{
50463787Seric 					/* need to re-expand aliases */
50563787Seric 					doublequeue = TRUE;
50663787Seric 				}
50763787Seric 				if (bitset(QBADADDR, a->q_flags))
50863787Seric 				{
50963787Seric 					/* make this "go away" */
51063787Seric 					a->q_flags |= QDONTSEND;
51163787Seric 					a->q_flags &= ~QBADADDR;
51263787Seric 				}
51358929Seric 			}
51458929Seric 
5154976Seric 			/* collect the text of the message */
51624943Seric 			SmtpPhase = "collect";
51764766Seric 			collect(TRUE, doublequeue, e);
51864766Seric 			if (Errors != 0)
51964766Seric 				goto abortmessage;
52063965Seric 			HoldErrs = TRUE;
5214976Seric 
5228238Seric 			/*
5238238Seric 			**  Arrange to send to everyone.
5248238Seric 			**	If sending to multiple people, mail back
5258238Seric 			**		errors rather than reporting directly.
5268238Seric 			**	In any case, don't mail back errors for
5278238Seric 			**		anything that has happened up to
5288238Seric 			**		now (the other end will do this).
52910197Seric 			**	Truncate our transcript -- the mail has gotten
53010197Seric 			**		to us successfully, and if we have
53110197Seric 			**		to mail this back, it will be easier
53210197Seric 			**		on the reader.
5338238Seric 			**	Then send to everyone.
5348238Seric 			**	Finally give a reply code.  If an error has
5358238Seric 			**		already been given, don't mail a
5368238Seric 			**		message back.
5379339Seric 			**	We goose error returns by clearing error bit.
5388238Seric 			*/
5398238Seric 
54024943Seric 			SmtpPhase = "delivery";
54163787Seric 			if (nrcpts != 1 && !doublequeue)
5429378Seric 			{
5439378Seric 				HoldErrs = TRUE;
54458734Seric 				e->e_errormode = EM_MAIL;
5459378Seric 			}
54655012Seric 			e->e_xfp = freopen(queuename(e, 'x'), "w", e->e_xfp);
54758714Seric 			id = e->e_id;
5484976Seric 
5494976Seric 			/* send to all recipients */
55063787Seric 			sendall(e, doublequeue ? SM_QUEUE : SM_DEFAULT);
55155012Seric 			e->e_to = NULL;
5524976Seric 
5538238Seric 			/* issue success if appropriate and reset */
5548238Seric 			if (Errors == 0 || HoldErrs)
55558855Seric 				message("250 %s Message accepted for delivery", id);
55659747Seric 
55759747Seric 			if (bitset(EF_FATALERRS, e->e_flags) && !HoldErrs)
55859730Seric 			{
55959730Seric 				/* avoid sending back an extra message */
56059730Seric 				e->e_flags &= ~EF_FATALERRS;
56159747Seric 				e->e_flags |= EF_CLRQUEUE;
56259730Seric 			}
5638238Seric 			else
56458919Seric 			{
56559747Seric 				/* from now on, we have to operate silently */
56659747Seric 				HoldErrs = TRUE;
56759747Seric 				e->e_errormode = EM_MAIL;
56859747Seric 
56959730Seric 				/* if we just queued, poke it */
57063787Seric 				if (doublequeue && e->e_sendmode != SM_QUEUE)
57159730Seric 				{
57264296Seric 					extern pid_t dowork();
57364296Seric 
57459730Seric 					unlockqueue(e);
57564296Seric 					(void) dowork(id, TRUE, TRUE, e);
57659730Seric 				}
57758919Seric 			}
57858883Seric 
57959747Seric   abortmessage:
5809339Seric 			/* if in a child, pop back to our parent */
5819339Seric 			if (InChild)
5829339Seric 				finis();
58324943Seric 
58424943Seric 			/* clean up a bit */
58558109Seric 			gotmail = FALSE;
58655012Seric 			dropenvelope(e);
58758179Seric 			CurEnv = e = newenvelope(e, CurEnv);
58855012Seric 			e->e_flags = BlankEnvelope.e_flags;
5894549Seric 			break;
5904549Seric 
5914549Seric 		  case CMDRSET:		/* rset -- reset state */
59258151Seric 			message("250 Reset state");
59364359Seric 			e->e_flags |= EF_CLRQUEUE;
5949339Seric 			if (InChild)
5959339Seric 				finis();
59658109Seric 
59758109Seric 			/* clean up a bit */
59858109Seric 			gotmail = FALSE;
59958109Seric 			dropenvelope(e);
60058179Seric 			CurEnv = e = newenvelope(e, CurEnv);
6019339Seric 			break;
6024549Seric 
6034549Seric 		  case CMDVRFY:		/* vrfy -- verify address */
60458092Seric 		  case CMDEXPN:		/* expn -- expand address */
60558092Seric 			vrfy = c->cmdcode == CMDVRFY;
60658092Seric 			if (bitset(vrfy ? PRIV_NOVRFY : PRIV_NOEXPN,
60758092Seric 						PrivacyFlags))
60858082Seric 			{
60958412Seric 				if (vrfy)
61058412Seric 					message("252 Who's to say?");
61158412Seric 				else
61265192Seric 					message("502 Sorry, we do not allow this operation");
61365017Seric #ifdef LOG
61465017Seric 				if (LogLevel > 5)
61565017Seric 					syslog(LOG_INFO, "%s: %s [rejected]",
61665017Seric 						CurSmtpClient, inp);
61765017Seric #endif
61858082Seric 				break;
61958082Seric 			}
62058082Seric 			else if (!gothello &&
62158092Seric 				 bitset(vrfy ? PRIV_NEEDVRFYHELO : PRIV_NEEDEXPNHELO,
62258092Seric 						PrivacyFlags))
62358082Seric 			{
62458151Seric 				message("503 I demand that you introduce yourself first");
62558082Seric 				break;
62658082Seric 			}
62758092Seric 			if (runinchild(vrfy ? "SMTP-VRFY" : "SMTP-EXPN", e) > 0)
6289339Seric 				break;
62955173Seric #ifdef LOG
63058020Seric 			if (LogLevel > 5)
63165017Seric 				syslog(LOG_INFO, "%s: %s", CurSmtpClient, inp);
63255173Seric #endif
6335003Seric 			vrfyqueue = NULL;
6347762Seric 			QuickAbort = TRUE;
63558092Seric 			if (vrfy)
63658092Seric 				e->e_flags |= EF_VRFYONLY;
63762373Seric 			while (*p != '\0' && isascii(*p) && isspace(*p))
63862373Seric 				*p++;
63962373Seric 			if (*p == '\0')
64062373Seric 			{
64162373Seric 				message("501 Argument required");
64262373Seric 				Errors++;
64362373Seric 			}
64462373Seric 			else
64562373Seric 			{
64664284Seric 				(void) sendtolist(p, NULLADDR, &vrfyqueue, e);
64762373Seric 			}
6487762Seric 			if (Errors != 0)
6499339Seric 			{
6509339Seric 				if (InChild)
6519339Seric 					finis();
6527762Seric 				break;
6539339Seric 			}
65462373Seric 			if (vrfyqueue == NULL)
65562373Seric 			{
65662373Seric 				message("554 Nothing to %s", vrfy ? "VRFY" : "EXPN");
65762373Seric 			}
6585003Seric 			while (vrfyqueue != NULL)
6595003Seric 			{
66063971Seric 				a = vrfyqueue;
66163971Seric 				while ((a = a->q_next) != NULL &&
66263971Seric 				       bitset(QDONTSEND|QBADADDR, a->q_flags))
66363971Seric 					continue;
6647685Seric 				if (!bitset(QDONTSEND|QBADADDR, vrfyqueue->q_flags))
66558151Seric 					printvrfyaddr(vrfyqueue, a == NULL);
66663847Seric 				vrfyqueue = vrfyqueue->q_next;
6675003Seric 			}
6689339Seric 			if (InChild)
6699339Seric 				finis();
6704549Seric 			break;
6714549Seric 
6724549Seric 		  case CMDHELP:		/* help -- give user info */
6734577Seric 			help(p);
6744549Seric 			break;
6754549Seric 
6764549Seric 		  case CMDNOOP:		/* noop -- do nothing */
67764122Seric 			message("250 OK");
6784549Seric 			break;
6794549Seric 
6804549Seric 		  case CMDQUIT:		/* quit -- leave mail */
68158151Seric 			message("221 %s closing connection", MyHostName);
68261051Seric 
68361051Seric 			/* avoid future 050 messages */
68461051Seric 			Verbose = FALSE;
68561051Seric 
6869339Seric 			if (InChild)
6879339Seric 				ExitStat = EX_QUIT;
6884549Seric 			finis();
6894549Seric 
6908544Seric 		  case CMDVERB:		/* set verbose mode */
69159957Seric 			if (bitset(PRIV_NOEXPN, PrivacyFlags))
69259957Seric 			{
69359957Seric 				/* this would give out the same info */
69459957Seric 				message("502 Verbose unavailable");
69559957Seric 				break;
69659957Seric 			}
6978544Seric 			Verbose = TRUE;
69858734Seric 			e->e_sendmode = SM_DELIVER;
69959957Seric 			message("250 Verbose mode");
7008544Seric 			break;
7018544Seric 
7029314Seric 		  case CMDONEX:		/* doing one transaction only */
7039378Seric 			OneXact = TRUE;
70459957Seric 			message("250 Only one transaction");
7059314Seric 			break;
7069314Seric 
70736230Skarels # ifdef SMTPDEBUG
7089339Seric 		  case CMDDBGQSHOW:	/* show queues */
7096907Seric 			printf("Send Queue=");
71055012Seric 			printaddr(e->e_sendqueue, TRUE);
7115003Seric 			break;
7127275Seric 
7137275Seric 		  case CMDDBGDEBUG:	/* set debug mode */
7147676Seric 			tTsetup(tTdvect, sizeof tTdvect, "0-99.1");
7157676Seric 			tTflag(p);
71658151Seric 			message("200 Debug set");
7177275Seric 			break;
7187275Seric 
71936230Skarels # else /* not SMTPDEBUG */
72036230Skarels 		  case CMDDBGQSHOW:	/* show queues */
72136230Skarels 		  case CMDDBGDEBUG:	/* set debug mode */
72264685Seric # endif /* SMTPDEBUG */
72364685Seric 		  case CMDLOGBOGUS:	/* bogus command */
72436233Skarels # ifdef LOG
72558308Seric 			if (LogLevel > 0)
72664685Seric 				syslog(LOG_CRIT,
72758020Seric 				    "\"%s\" command from %s (%s)",
72836230Skarels 				    c->cmdname, RealHostName,
72958755Seric 				    anynet_ntoa(&RealHostAddr));
73036233Skarels # endif
73136230Skarels 			/* FALL THROUGH */
73236230Skarels 
7334549Seric 		  case CMDERROR:	/* unknown command */
73458151Seric 			message("500 Command unrecognized");
7354549Seric 			break;
7364549Seric 
7374549Seric 		  default:
73836230Skarels 			errno = 0;
73958151Seric 			syserr("500 smtp: unknown code %d", c->cmdcode);
7404549Seric 			break;
7414549Seric 		}
7424549Seric 	}
7434549Seric }
7444549Seric /*
7454549Seric **  SKIPWORD -- skip a fixed word.
7464549Seric **
7474549Seric **	Parameters:
7484549Seric **		p -- place to start looking.
7494549Seric **		w -- word to skip.
7504549Seric **
7514549Seric **	Returns:
7524549Seric **		p following w.
7534549Seric **		NULL on error.
7544549Seric **
7554549Seric **	Side Effects:
7564549Seric **		clobbers the p data area.
7574549Seric */
7584549Seric 
7594549Seric static char *
7604549Seric skipword(p, w)
7614549Seric 	register char *p;
7624549Seric 	char *w;
7634549Seric {
7644549Seric 	register char *q;
7654549Seric 
7664549Seric 	/* find beginning of word */
76758050Seric 	while (isascii(*p) && isspace(*p))
7684549Seric 		p++;
7694549Seric 	q = p;
7704549Seric 
7714549Seric 	/* find end of word */
77258050Seric 	while (*p != '\0' && *p != ':' && !(isascii(*p) && isspace(*p)))
7734549Seric 		p++;
77458050Seric 	while (isascii(*p) && isspace(*p))
7754549Seric 		*p++ = '\0';
7764549Seric 	if (*p != ':')
7774549Seric 	{
7784549Seric 	  syntax:
77962373Seric 		message("501 Syntax error in parameters");
7804549Seric 		Errors++;
7814549Seric 		return (NULL);
7824549Seric 	}
7834549Seric 	*p++ = '\0';
78458050Seric 	while (isascii(*p) && isspace(*p))
7854549Seric 		p++;
7864549Seric 
78762373Seric 	if (*p == '\0')
78862373Seric 		goto syntax;
78962373Seric 
7904549Seric 	/* see if the input word matches desired word */
79133725Sbostic 	if (strcasecmp(q, w))
7924549Seric 		goto syntax;
7934549Seric 
7944549Seric 	return (p);
7954549Seric }
7964577Seric /*
79758151Seric **  PRINTVRFYADDR -- print an entry in the verify queue
79858151Seric **
79958151Seric **	Parameters:
80058151Seric **		a -- the address to print
80158151Seric **		last -- set if this is the last one.
80258151Seric **
80358151Seric **	Returns:
80458151Seric **		none.
80558151Seric **
80658151Seric **	Side Effects:
80758151Seric **		Prints the appropriate 250 codes.
80858151Seric */
80958151Seric 
81058151Seric printvrfyaddr(a, last)
81158151Seric 	register ADDRESS *a;
81258151Seric 	bool last;
81358151Seric {
81458151Seric 	char fmtbuf[20];
81558151Seric 
81658151Seric 	strcpy(fmtbuf, "250");
81758151Seric 	fmtbuf[3] = last ? ' ' : '-';
81858151Seric 
81959746Seric 	if (a->q_fullname == NULL)
82059746Seric 	{
82159746Seric 		if (strchr(a->q_user, '@') == NULL)
82259746Seric 			strcpy(&fmtbuf[4], "<%s@%s>");
82359746Seric 		else
82459746Seric 			strcpy(&fmtbuf[4], "<%s>");
82559746Seric 		message(fmtbuf, a->q_user, MyHostName);
82659746Seric 	}
82758151Seric 	else
82858151Seric 	{
82959746Seric 		if (strchr(a->q_user, '@') == NULL)
83059746Seric 			strcpy(&fmtbuf[4], "%s <%s@%s>");
83159746Seric 		else
83259746Seric 			strcpy(&fmtbuf[4], "%s <%s>");
83359746Seric 		message(fmtbuf, a->q_fullname, a->q_user, MyHostName);
83458151Seric 	}
83558151Seric }
83658151Seric /*
8374577Seric **  HELP -- implement the HELP command.
8384577Seric **
8394577Seric **	Parameters:
8404577Seric **		topic -- the topic we want help for.
8414577Seric **
8424577Seric **	Returns:
8434577Seric **		none.
8444577Seric **
8454577Seric **	Side Effects:
8464577Seric **		outputs the help file to message output.
8474577Seric */
8484577Seric 
8494577Seric help(topic)
8504577Seric 	char *topic;
8514577Seric {
8524577Seric 	register FILE *hf;
8534577Seric 	int len;
8544577Seric 	char buf[MAXLINE];
8554577Seric 	bool noinfo;
8564577Seric 
8578269Seric 	if (HelpFile == NULL || (hf = fopen(HelpFile, "r")) == NULL)
8584577Seric 	{
8594577Seric 		/* no help */
86011931Seric 		errno = 0;
86158151Seric 		message("502 HELP not implemented");
8624577Seric 		return;
8634577Seric 	}
8644577Seric 
86549669Seric 	if (topic == NULL || *topic == '\0')
86649669Seric 		topic = "smtp";
86749669Seric 	else
86849669Seric 		makelower(topic);
86949669Seric 
8704577Seric 	len = strlen(topic);
8714577Seric 	noinfo = TRUE;
8724577Seric 
8734577Seric 	while (fgets(buf, sizeof buf, hf) != NULL)
8744577Seric 	{
8754577Seric 		if (strncmp(buf, topic, len) == 0)
8764577Seric 		{
8774577Seric 			register char *p;
8784577Seric 
87956795Seric 			p = strchr(buf, '\t');
8804577Seric 			if (p == NULL)
8814577Seric 				p = buf;
8824577Seric 			else
8834577Seric 				p++;
8844577Seric 			fixcrlf(p, TRUE);
88558151Seric 			message("214-%s", p);
8864577Seric 			noinfo = FALSE;
8874577Seric 		}
8884577Seric 	}
8894577Seric 
8904577Seric 	if (noinfo)
89158151Seric 		message("504 HELP topic unknown");
8924577Seric 	else
89358151Seric 		message("214 End of HELP info");
8944628Seric 	(void) fclose(hf);
8954577Seric }
8968544Seric /*
8979339Seric **  RUNINCHILD -- return twice -- once in the child, then in the parent again
8989339Seric **
8999339Seric **	Parameters:
9009339Seric **		label -- a string used in error messages
9019339Seric **
9029339Seric **	Returns:
9039339Seric **		zero in the child
9049339Seric **		one in the parent
9059339Seric **
9069339Seric **	Side Effects:
9079339Seric **		none.
9089339Seric */
9098544Seric 
91055012Seric runinchild(label, e)
9119339Seric 	char *label;
91255012Seric 	register ENVELOPE *e;
9139339Seric {
9149339Seric 	int childpid;
9159339Seric 
91616158Seric 	if (!OneXact)
9179339Seric 	{
91816158Seric 		childpid = dofork();
91916158Seric 		if (childpid < 0)
92016158Seric 		{
92116158Seric 			syserr("%s: cannot fork", label);
92216158Seric 			return (1);
92316158Seric 		}
92416158Seric 		if (childpid > 0)
92516158Seric 		{
92616158Seric 			auto int st;
9279339Seric 
92816158Seric 			/* parent -- wait for child to complete */
92961093Seric 			setproctitle("server %s child wait", CurHostName);
93016158Seric 			st = waitfor(childpid);
93116158Seric 			if (st == -1)
93216158Seric 				syserr("%s: lost child", label);
93364948Seric 			else if (!WIFEXITED(st))
93464948Seric 				syserr("%s: died on signal %d",
93564948Seric 					label, st & 0177);
9369339Seric 
93716158Seric 			/* if we exited on a QUIT command, complete the process */
93816158Seric 			if (st == (EX_QUIT << 8))
93916158Seric 				finis();
9409339Seric 
94116158Seric 			return (1);
94216158Seric 		}
94316158Seric 		else
94416158Seric 		{
94516158Seric 			/* child */
94616158Seric 			InChild = TRUE;
94725050Seric 			QuickAbort = FALSE;
94855012Seric 			clearenvelope(e, FALSE);
94916158Seric 		}
9509339Seric 	}
95115256Seric 
95216158Seric 	/* open alias database */
95360537Seric 	initmaps(FALSE, e);
95416158Seric 
95516158Seric 	return (0);
9569339Seric }
9579339Seric 
95856795Seric # endif /* SMTP */
959