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*66003Seric static char sccsid[] = "@(#)srvrsmtp.c	8.27 (Berkeley) 02/05/94 (with SMTP)";
1433731Sbostic #else
15*66003Seric static char sccsid[] = "@(#)srvrsmtp.c	8.27 (Berkeley) 02/05/94 (without SMTP)";
1633731Sbostic #endif
1733731Sbostic #endif /* not lint */
1833731Sbostic 
199339Seric # include <errno.h>
204549Seric 
2133731Sbostic # ifdef SMTP
224556Seric 
234549Seric /*
244549Seric **  SMTP -- run the SMTP protocol.
254549Seric **
264549Seric **	Parameters:
274549Seric **		none.
284549Seric **
294549Seric **	Returns:
304549Seric **		never.
314549Seric **
324549Seric **	Side Effects:
334549Seric **		Reads commands from the input channel and processes
344549Seric **			them.
354549Seric */
364549Seric 
374549Seric struct cmd
384549Seric {
394549Seric 	char	*cmdname;	/* command name */
404549Seric 	int	cmdcode;	/* internal code, see below */
414549Seric };
424549Seric 
434549Seric /* values for cmdcode */
444549Seric # define CMDERROR	0	/* bad command */
454549Seric # define CMDMAIL	1	/* mail -- designate sender */
464976Seric # define CMDRCPT	2	/* rcpt -- designate recipient */
474549Seric # define CMDDATA	3	/* data -- send message text */
489339Seric # define CMDRSET	4	/* rset -- reset state */
499339Seric # define CMDVRFY	5	/* vrfy -- verify address */
5058092Seric # define CMDEXPN	6	/* expn -- expand address */
519339Seric # define CMDNOOP	7	/* noop -- do nothing */
529339Seric # define CMDQUIT	8	/* quit -- close connection and die */
539339Seric # define CMDHELO	9	/* helo -- be polite */
5458092Seric # define CMDHELP	10	/* help -- give usage info */
5558323Seric # define CMDEHLO	11	/* ehlo -- extended helo (RFC 1425) */
5658092Seric /* non-standard commands */
5758092Seric # define CMDONEX	16	/* onex -- sending one transaction only */
5858092Seric # define CMDVERB	17	/* verb -- go into verbose mode */
5964685Seric /* use this to catch and log "door handle" attempts on your system */
6064685Seric # define CMDLOGBOGUS	23	/* bogus command that should be logged */
6136230Skarels /* debugging-only commands, only enabled if SMTPDEBUG is defined */
6258092Seric # define CMDDBGQSHOW	24	/* showq -- show send queue */
6358092Seric # define CMDDBGDEBUG	25	/* debug -- set debug mode */
644549Seric 
654549Seric static struct cmd	CmdTab[] =
664549Seric {
674549Seric 	"mail",		CMDMAIL,
684976Seric 	"rcpt",		CMDRCPT,
694549Seric 	"data",		CMDDATA,
704549Seric 	"rset",		CMDRSET,
714549Seric 	"vrfy",		CMDVRFY,
7258092Seric 	"expn",		CMDEXPN,
734549Seric 	"help",		CMDHELP,
744549Seric 	"noop",		CMDNOOP,
754549Seric 	"quit",		CMDQUIT,
764976Seric 	"helo",		CMDHELO,
7758323Seric 	"ehlo",		CMDEHLO,
788544Seric 	"verb",		CMDVERB,
799314Seric 	"onex",		CMDONEX,
8036230Skarels 	/*
8136230Skarels 	 * remaining commands are here only
8236230Skarels 	 * to trap and log attempts to use them
8336230Skarels 	 */
849339Seric 	"showq",	CMDDBGQSHOW,
858544Seric 	"debug",	CMDDBGDEBUG,
8664685Seric 	"wiz",		CMDLOGBOGUS,
874549Seric 	NULL,		CMDERROR,
884549Seric };
894549Seric 
909378Seric bool	OneXact = FALSE;		/* one xaction only this run */
9165017Seric char	*CurSmtpClient;			/* who's at the other end of channel */
9211146Seric 
9363937Seric static char	*skipword();
9463937Seric 
95*66003Seric #define REALHOSTNAME	(RealHostName == NULL ? "localhost" : RealHostName)
96*66003Seric 
9755012Seric smtp(e)
9855012Seric 	register ENVELOPE *e;
994549Seric {
1004549Seric 	register char *p;
1018544Seric 	register struct cmd *c;
1024549Seric 	char *cmd;
1035003Seric 	auto ADDRESS *vrfyqueue;
10412612Seric 	ADDRESS *a;
10558109Seric 	bool gotmail;			/* mail command received */
10658092Seric 	bool gothello;			/* helo command received */
10758092Seric 	bool vrfy;			/* set if this is a vrfy command */
10858323Seric 	char *protocol;			/* sending protocol */
10959016Seric 	char *sendinghost;		/* sending hostname */
11058333Seric 	long msize;			/* approximate maximum message size */
11158333Seric 	auto char *delimptr;
11258714Seric 	char *id;
11359747Seric 	int nrcpts;			/* number of RCPT commands */
11463787Seric 	bool doublequeue;
1158544Seric 	char inp[MAXLINE];
11657232Seric 	char cmdbuf[MAXLINE];
1177124Seric 	extern char Version[];
11824943Seric 	extern ENVELOPE BlankEnvelope;
1194549Seric 
12059066Seric 	if (fileno(OutChannel) != fileno(stdout))
1217363Seric 	{
1227363Seric 		/* arrange for debugging output to go to remote host */
12359066Seric 		(void) dup2(fileno(OutChannel), fileno(stdout));
1247363Seric 	}
12555012Seric 	settime(e);
126*66003Seric 	CurHostName = REALHOSTNAME;
12765017Seric 	CurSmtpClient = macvalue('_', e);
12865017Seric 	if (CurSmtpClient == NULL)
129*66003Seric 		CurSmtpClient = CurHostName;
13065017Seric 
13165017Seric 	setproctitle("server %s startup", CurSmtpClient);
13258050Seric 	expand("\201e", inp, &inp[sizeof inp], e);
13364496Seric 	if (BrokenSmtpPeers)
13464496Seric 	{
13564496Seric 		message("220 %s", inp);
13664496Seric 	}
13764496Seric 	else
13864496Seric 	{
13964496Seric 		message("220-%s", inp);
14064496Seric 		message("220 ESMTP spoken here");
14164496Seric 	}
14258330Seric 	protocol = NULL;
14359016Seric 	sendinghost = macvalue('s', e);
14458082Seric 	gothello = FALSE;
14558330Seric 	gotmail = FALSE;
1464549Seric 	for (;;)
1474549Seric 	{
14812612Seric 		/* arrange for backout */
14965751Seric 		if (setjmp(TopFrame) > 0)
15059058Seric 		{
15165751Seric 			/* if() nesting is necessary for Cray UNICOS */
15265751Seric 			if (InChild)
15365751Seric 			{
15465751Seric 				QuickAbort = FALSE;
15565751Seric 				SuprErrs = TRUE;
15665751Seric 				finis();
15765751Seric 			}
15859058Seric 		}
15912612Seric 		QuickAbort = FALSE;
16012612Seric 		HoldErrs = FALSE;
16151951Seric 		LogUsrErrs = FALSE;
16263843Seric 		e->e_flags &= ~(EF_VRFYONLY|EF_GLOBALERRS);
16312612Seric 
1647356Seric 		/* setup for the read */
16555012Seric 		e->e_to = NULL;
1664577Seric 		Errors = 0;
1677275Seric 		(void) fflush(stdout);
1687356Seric 
1697356Seric 		/* read the input line */
17061093Seric 		SmtpPhase = "server cmd read";
17161093Seric 		setproctitle("server %s cmd read", CurHostName);
17261093Seric 		p = sfgets(inp, sizeof inp, InChannel, TimeOuts.to_nextcommand,
17361093Seric 				SmtpPhase);
1747356Seric 
1757685Seric 		/* handle errors */
1767356Seric 		if (p == NULL)
1777356Seric 		{
1784549Seric 			/* end of file, just die */
17958151Seric 			message("421 %s Lost input channel from %s",
18065017Seric 				MyHostName, CurSmtpClient);
18155464Seric #ifdef LOG
18263843Seric 			if (LogLevel > (gotmail ? 1 : 19))
18355464Seric 				syslog(LOG_NOTICE, "lost input channel from %s",
18465017Seric 					CurSmtpClient);
18555464Seric #endif
18658069Seric 			if (InChild)
18758069Seric 				ExitStat = EX_QUIT;
1884549Seric 			finis();
1894549Seric 		}
1904549Seric 
1914549Seric 		/* clean up end of line */
1924558Seric 		fixcrlf(inp, TRUE);
1934549Seric 
1944713Seric 		/* echo command to transcript */
19555012Seric 		if (e->e_xfp != NULL)
19655012Seric 			fprintf(e->e_xfp, "<<< %s\n", inp);
1974713Seric 
19859060Seric 		if (e->e_id == NULL)
19965058Seric 			setproctitle("%s: %.80s", CurSmtpClient, inp);
20059060Seric 		else
20165058Seric 			setproctitle("%s %s: %.80s", e->e_id, CurSmtpClient, inp);
20259060Seric 
2034549Seric 		/* break off command */
20458050Seric 		for (p = inp; isascii(*p) && isspace(*p); p++)
2054549Seric 			continue;
20657232Seric 		cmd = cmdbuf;
20758050Seric 		while (*p != '\0' &&
20858050Seric 		       !(isascii(*p) && isspace(*p)) &&
20958050Seric 		       cmd < &cmdbuf[sizeof cmdbuf - 2])
21024981Seric 			*cmd++ = *p++;
21124981Seric 		*cmd = '\0';
2124549Seric 
21325691Seric 		/* throw away leading whitespace */
21458050Seric 		while (isascii(*p) && isspace(*p))
21525691Seric 			p++;
21625691Seric 
2174549Seric 		/* decode command */
2184549Seric 		for (c = CmdTab; c->cmdname != NULL; c++)
2194549Seric 		{
22033725Sbostic 			if (!strcasecmp(c->cmdname, cmdbuf))
2214549Seric 				break;
2224549Seric 		}
2234549Seric 
22451954Seric 		/* reset errors */
22551954Seric 		errno = 0;
22651954Seric 
2274549Seric 		/* process command */
2284549Seric 		switch (c->cmdcode)
2294549Seric 		{
2304976Seric 		  case CMDHELO:		/* hello -- introduce yourself */
23158323Seric 		  case CMDEHLO:		/* extended hello */
23258323Seric 			if (c->cmdcode == CMDEHLO)
23358323Seric 			{
23458323Seric 				protocol = "ESMTP";
23561093Seric 				SmtpPhase = "server EHLO";
23658323Seric 			}
23758323Seric 			else
23858323Seric 			{
23958323Seric 				protocol = "SMTP";
24061093Seric 				SmtpPhase = "server HELO";
24158323Seric 			}
24259016Seric 			sendinghost = newstr(p);
24360210Seric 			gothello = TRUE;
24460210Seric 			if (c->cmdcode != CMDEHLO)
24560239Seric 			{
24660239Seric 				/* print old message and be done with it */
24760239Seric 				message("250 %s Hello %s, pleased to meet you",
24865017Seric 					MyHostName, CurSmtpClient);
24960210Seric 				break;
25060239Seric 			}
25160239Seric 
25260239Seric 			/* print extended message and brag */
25360239Seric 			message("250-%s Hello %s, pleased to meet you",
25460239Seric 				MyHostName, p);
25558323Seric 			if (!bitset(PRIV_NOEXPN, PrivacyFlags))
25658323Seric 				message("250-EXPN");
25764359Seric 			if (MaxMessageSize > 0)
25864359Seric 				message("250-SIZE %ld", MaxMessageSize);
25959271Seric 			else
26059271Seric 				message("250-SIZE");
26158323Seric 			message("250 HELP");
2624976Seric 			break;
2634976Seric 
2644549Seric 		  case CMDMAIL:		/* mail -- designate sender */
26561093Seric 			SmtpPhase = "server MAIL";
26624943Seric 
2679314Seric 			/* check for validity of this command */
26858789Seric 			if (!gothello)
26958082Seric 			{
27058957Seric 				/* set sending host to our known value */
27159016Seric 				if (sendinghost == NULL)
272*66003Seric 					sendinghost = REALHOSTNAME;
27358957Seric 
27458789Seric 				if (bitset(PRIV_NEEDMAILHELO, PrivacyFlags))
27558821Seric 				{
27658789Seric 					message("503 Polite people say HELO first");
27758821Seric 					break;
27858821Seric 				}
27958082Seric 			}
28058109Seric 			if (gotmail)
2814558Seric 			{
28258151Seric 				message("503 Sender already specified");
28363843Seric 				if (InChild)
28463843Seric 					finis();
2854558Seric 				break;
2864558Seric 			}
2879339Seric 			if (InChild)
2889339Seric 			{
28936230Skarels 				errno = 0;
29058151Seric 				syserr("503 Nested MAIL command: MAIL %s", p);
29158069Seric 				finis();
2929339Seric 			}
2939339Seric 
2949339Seric 			/* fork a subprocess to process this command */
29555012Seric 			if (runinchild("SMTP-MAIL", e) > 0)
2969339Seric 				break;
297*66003Seric 			p = REALHOSTNAME;
29863753Seric 			if (!gothello)
29963753Seric 			{
30063753Seric 				auth_warning(e,
301*66003Seric 					"Host %s didn't use HELO protocol", p);
30263753Seric 			}
30365947Seric #ifdef PICKY_HELO_CHECK
304*66003Seric 			if (strcasecmp(sendinghost, p) != 0 &&
305*66003Seric 			    (strcasecmp(p, "localhost") != 0 ||
30665823Seric 			     strcasecmp(sendinghost, MyHostName) != 0))
30765823Seric 			{
30865823Seric 				auth_warning(e, "Host %s claimed to be %s",
309*66003Seric 					p, sendinghost);
31065823Seric 			}
31165947Seric #endif
31265823Seric 
31358323Seric 			if (protocol == NULL)
31458323Seric 				protocol = "SMTP";
31558323Seric 			define('r', protocol, e);
31659016Seric 			define('s', sendinghost, e);
31755012Seric 			initsys(e);
31859747Seric 			nrcpts = 0;
31965089Seric 			e->e_flags |= EF_LOGSENDER;
32065058Seric 			setproctitle("%s %s: %.80s", e->e_id, CurSmtpClient, inp);
3219339Seric 
3229339Seric 			/* child -- go do the processing */
3234549Seric 			p = skipword(p, "from");
3244549Seric 			if (p == NULL)
3254549Seric 				break;
32657977Seric 			if (setjmp(TopFrame) > 0)
32758147Seric 			{
32858147Seric 				/* this failed -- undo work */
32958147Seric 				if (InChild)
33059058Seric 				{
33159058Seric 					QuickAbort = FALSE;
33259058Seric 					SuprErrs = TRUE;
33363787Seric 					e->e_flags &= ~EF_FATALERRS;
33458147Seric 					finis();
33559058Seric 				}
33657977Seric 				break;
33758147Seric 			}
33857977Seric 			QuickAbort = TRUE;
33958333Seric 
34058333Seric 			/* must parse sender first */
34158333Seric 			delimptr = NULL;
34258704Seric 			setsender(p, e, &delimptr, FALSE);
34358333Seric 			p = delimptr;
34458333Seric 			if (p != NULL && *p != '\0')
34558333Seric 				*p++ = '\0';
34658333Seric 
34758333Seric 			/* now parse ESMTP arguments */
34858333Seric 			msize = 0;
34958333Seric 			for (; p != NULL && *p != '\0'; p++)
35058333Seric 			{
35158333Seric 				char *kp;
35258333Seric 				char *vp;
35358333Seric 
35458333Seric 				/* locate the beginning of the keyword */
35558333Seric 				while (isascii(*p) && isspace(*p))
35658333Seric 					p++;
35758333Seric 				if (*p == '\0')
35858333Seric 					break;
35958333Seric 				kp = p;
36058333Seric 
36158333Seric 				/* skip to the value portion */
36258333Seric 				while (isascii(*p) && isalnum(*p) || *p == '-')
36358333Seric 					p++;
36458333Seric 				if (*p == '=')
36558333Seric 				{
36658333Seric 					*p++ = '\0';
36758333Seric 					vp = p;
36858333Seric 
36958333Seric 					/* skip to the end of the value */
37058333Seric 					while (*p != '\0' && *p != ' ' &&
37158333Seric 					       !(isascii(*p) && iscntrl(*p)) &&
37258333Seric 					       *p != '=')
37358333Seric 						p++;
37458333Seric 				}
37558333Seric 
37658333Seric 				if (*p != '\0')
37758333Seric 					*p++ = '\0';
37858333Seric 
37958333Seric 				if (tTd(19, 1))
38058333Seric 					printf("MAIL: got arg %s=%s\n", kp,
38158333Seric 						vp == NULL ? "<null>" : vp);
38258333Seric 
38358333Seric 				if (strcasecmp(kp, "size") == 0)
38458333Seric 				{
38559093Seric 					if (vp == NULL)
38658333Seric 					{
38758333Seric 						usrerr("501 SIZE requires a value");
38858333Seric 						/* NOTREACHED */
38958333Seric 					}
39058333Seric 					msize = atol(vp);
39158333Seric 				}
39259093Seric 				else if (strcasecmp(kp, "body") == 0)
39359093Seric 				{
39459093Seric 					if (vp == NULL)
39559093Seric 					{
39659093Seric 						usrerr("501 BODY requires a value");
39759093Seric 						/* NOTREACHED */
39859093Seric 					}
39959093Seric # ifdef MIME
40059093Seric 					if (strcasecmp(vp, "8bitmime") == 0)
40159093Seric 					{
40259093Seric 						e->e_bodytype = "8BITMIME";
40359709Seric 						SevenBit = FALSE;
40459093Seric 					}
40559093Seric 					else if (strcasecmp(vp, "7bit") == 0)
40659093Seric 					{
40759093Seric 						e->e_bodytype = "7BIT";
40859709Seric 						SevenBit = TRUE;
40959093Seric 					}
41059093Seric 					else
41159093Seric 					{
41259093Seric 						usrerr("501 Unknown BODY type %s",
41359093Seric 							vp);
41459093Seric 					}
41559093Seric # endif
41659093Seric 				}
41758333Seric 				else
41858333Seric 				{
41958333Seric 					usrerr("501 %s parameter unrecognized", kp);
42058333Seric 					/* NOTREACHED */
42158333Seric 				}
42258333Seric 			}
42359284Seric 
42459284Seric 			if (MaxMessageSize > 0 && msize > MaxMessageSize)
42559284Seric 			{
42659284Seric 				usrerr("552 Message size exceeds fixed maximum message size (%ld)",
42759284Seric 					MaxMessageSize);
42859284Seric 				/* NOTREACHED */
42959284Seric 			}
43058333Seric 
43158333Seric 			if (!enoughspace(msize))
43258333Seric 			{
43358333Seric 				message("452 Insufficient disk space; try again later");
43458333Seric 				break;
43558333Seric 			}
43658151Seric 			message("250 Sender ok");
43758147Seric 			gotmail = TRUE;
4384549Seric 			break;
4394549Seric 
4404976Seric 		  case CMDRCPT:		/* rcpt -- designate recipient */
44158850Seric 			if (!gotmail)
44258850Seric 			{
44358850Seric 				usrerr("503 Need MAIL before RCPT");
44458850Seric 				break;
44558850Seric 			}
44661093Seric 			SmtpPhase = "server RCPT";
44712612Seric 			if (setjmp(TopFrame) > 0)
44814785Seric 			{
44955012Seric 				e->e_flags &= ~EF_FATALERRS;
45012612Seric 				break;
45114785Seric 			}
45212612Seric 			QuickAbort = TRUE;
45351951Seric 			LogUsrErrs = TRUE;
45458093Seric 
45559699Seric 			if (e->e_sendmode != SM_DELIVER)
45659699Seric 				e->e_flags |= EF_VRFYONLY;
45758919Seric 
4584549Seric 			p = skipword(p, "to");
4594549Seric 			if (p == NULL)
4604549Seric 				break;
46164284Seric 			a = parseaddr(p, NULLADDR, RF_COPYALL, ' ', NULL, e);
46212612Seric 			if (a == NULL)
46312612Seric 				break;
46416886Seric 			a->q_flags |= QPRIMARY;
46555012Seric 			a = recipient(a, &e->e_sendqueue, e);
46612612Seric 			if (Errors != 0)
46712612Seric 				break;
46812612Seric 
46912612Seric 			/* no errors during parsing, but might be a duplicate */
47055012Seric 			e->e_to = p;
47112612Seric 			if (!bitset(QBADADDR, a->q_flags))
47259747Seric 			{
47364718Seric 				message("250 Recipient ok%s",
47464718Seric 					bitset(QQUEUEUP, a->q_flags) ?
47564718Seric 						" (will queue)" : "");
47659747Seric 				nrcpts++;
47759747Seric 			}
47812612Seric 			else
4794549Seric 			{
48012612Seric 				/* punt -- should keep message in ADDRESS.... */
48158151Seric 				message("550 Addressee unknown");
4824549Seric 			}
48355012Seric 			e->e_to = NULL;
4844549Seric 			break;
4854549Seric 
4864549Seric 		  case CMDDATA:		/* data -- text of mail */
48761093Seric 			SmtpPhase = "server DATA";
48858109Seric 			if (!gotmail)
4894549Seric 			{
49058151Seric 				message("503 Need MAIL command");
4914976Seric 				break;
4924549Seric 			}
49364718Seric 			else if (nrcpts <= 0)
4944549Seric 			{
49558151Seric 				message("503 Need RCPT (recipient)");
4964976Seric 				break;
4974549Seric 			}
4984976Seric 
49958929Seric 			/* check to see if we need to re-expand aliases */
50063787Seric 			/* also reset QBADADDR on already-diagnosted addrs */
50163787Seric 			doublequeue = FALSE;
50258929Seric 			for (a = e->e_sendqueue; a != NULL; a = a->q_next)
50358929Seric 			{
50458929Seric 				if (bitset(QVERIFIED, a->q_flags))
50563787Seric 				{
50663787Seric 					/* need to re-expand aliases */
50763787Seric 					doublequeue = TRUE;
50863787Seric 				}
50963787Seric 				if (bitset(QBADADDR, a->q_flags))
51063787Seric 				{
51163787Seric 					/* make this "go away" */
51263787Seric 					a->q_flags |= QDONTSEND;
51363787Seric 					a->q_flags &= ~QBADADDR;
51463787Seric 				}
51558929Seric 			}
51658929Seric 
5174976Seric 			/* collect the text of the message */
51824943Seric 			SmtpPhase = "collect";
51964766Seric 			collect(TRUE, doublequeue, e);
52064766Seric 			if (Errors != 0)
52164766Seric 				goto abortmessage;
52263965Seric 			HoldErrs = TRUE;
5234976Seric 
5248238Seric 			/*
5258238Seric 			**  Arrange to send to everyone.
5268238Seric 			**	If sending to multiple people, mail back
5278238Seric 			**		errors rather than reporting directly.
5288238Seric 			**	In any case, don't mail back errors for
5298238Seric 			**		anything that has happened up to
5308238Seric 			**		now (the other end will do this).
53110197Seric 			**	Truncate our transcript -- the mail has gotten
53210197Seric 			**		to us successfully, and if we have
53310197Seric 			**		to mail this back, it will be easier
53410197Seric 			**		on the reader.
5358238Seric 			**	Then send to everyone.
5368238Seric 			**	Finally give a reply code.  If an error has
5378238Seric 			**		already been given, don't mail a
5388238Seric 			**		message back.
5399339Seric 			**	We goose error returns by clearing error bit.
5408238Seric 			*/
5418238Seric 
54224943Seric 			SmtpPhase = "delivery";
54363787Seric 			if (nrcpts != 1 && !doublequeue)
5449378Seric 			{
5459378Seric 				HoldErrs = TRUE;
54658734Seric 				e->e_errormode = EM_MAIL;
5479378Seric 			}
54855012Seric 			e->e_xfp = freopen(queuename(e, 'x'), "w", e->e_xfp);
54958714Seric 			id = e->e_id;
5504976Seric 
5514976Seric 			/* send to all recipients */
55263787Seric 			sendall(e, doublequeue ? SM_QUEUE : SM_DEFAULT);
55355012Seric 			e->e_to = NULL;
5544976Seric 
5558238Seric 			/* issue success if appropriate and reset */
5568238Seric 			if (Errors == 0 || HoldErrs)
55758855Seric 				message("250 %s Message accepted for delivery", id);
55859747Seric 
55959747Seric 			if (bitset(EF_FATALERRS, e->e_flags) && !HoldErrs)
56059730Seric 			{
56159730Seric 				/* avoid sending back an extra message */
56259730Seric 				e->e_flags &= ~EF_FATALERRS;
56359747Seric 				e->e_flags |= EF_CLRQUEUE;
56459730Seric 			}
5658238Seric 			else
56658919Seric 			{
56759747Seric 				/* from now on, we have to operate silently */
56859747Seric 				HoldErrs = TRUE;
56959747Seric 				e->e_errormode = EM_MAIL;
57059747Seric 
57159730Seric 				/* if we just queued, poke it */
57263787Seric 				if (doublequeue && e->e_sendmode != SM_QUEUE)
57359730Seric 				{
57464296Seric 					extern pid_t dowork();
57564296Seric 
57659730Seric 					unlockqueue(e);
57764296Seric 					(void) dowork(id, TRUE, TRUE, e);
57859730Seric 				}
57958919Seric 			}
58058883Seric 
58159747Seric   abortmessage:
5829339Seric 			/* if in a child, pop back to our parent */
5839339Seric 			if (InChild)
5849339Seric 				finis();
58524943Seric 
58624943Seric 			/* clean up a bit */
58758109Seric 			gotmail = FALSE;
58855012Seric 			dropenvelope(e);
58958179Seric 			CurEnv = e = newenvelope(e, CurEnv);
59055012Seric 			e->e_flags = BlankEnvelope.e_flags;
5914549Seric 			break;
5924549Seric 
5934549Seric 		  case CMDRSET:		/* rset -- reset state */
59458151Seric 			message("250 Reset state");
59564359Seric 			e->e_flags |= EF_CLRQUEUE;
5969339Seric 			if (InChild)
5979339Seric 				finis();
59858109Seric 
59958109Seric 			/* clean up a bit */
60058109Seric 			gotmail = FALSE;
60158109Seric 			dropenvelope(e);
60258179Seric 			CurEnv = e = newenvelope(e, CurEnv);
6039339Seric 			break;
6044549Seric 
6054549Seric 		  case CMDVRFY:		/* vrfy -- verify address */
60658092Seric 		  case CMDEXPN:		/* expn -- expand address */
60758092Seric 			vrfy = c->cmdcode == CMDVRFY;
60858092Seric 			if (bitset(vrfy ? PRIV_NOVRFY : PRIV_NOEXPN,
60958092Seric 						PrivacyFlags))
61058082Seric 			{
61158412Seric 				if (vrfy)
61258412Seric 					message("252 Who's to say?");
61358412Seric 				else
61465192Seric 					message("502 Sorry, we do not allow this operation");
61565017Seric #ifdef LOG
61665017Seric 				if (LogLevel > 5)
61765017Seric 					syslog(LOG_INFO, "%s: %s [rejected]",
61865017Seric 						CurSmtpClient, inp);
61965017Seric #endif
62058082Seric 				break;
62158082Seric 			}
62258082Seric 			else if (!gothello &&
62358092Seric 				 bitset(vrfy ? PRIV_NEEDVRFYHELO : PRIV_NEEDEXPNHELO,
62458092Seric 						PrivacyFlags))
62558082Seric 			{
62658151Seric 				message("503 I demand that you introduce yourself first");
62758082Seric 				break;
62858082Seric 			}
62958092Seric 			if (runinchild(vrfy ? "SMTP-VRFY" : "SMTP-EXPN", e) > 0)
6309339Seric 				break;
63155173Seric #ifdef LOG
63258020Seric 			if (LogLevel > 5)
63365017Seric 				syslog(LOG_INFO, "%s: %s", CurSmtpClient, inp);
63455173Seric #endif
6355003Seric 			vrfyqueue = NULL;
6367762Seric 			QuickAbort = TRUE;
63758092Seric 			if (vrfy)
63858092Seric 				e->e_flags |= EF_VRFYONLY;
63962373Seric 			while (*p != '\0' && isascii(*p) && isspace(*p))
64062373Seric 				*p++;
64162373Seric 			if (*p == '\0')
64262373Seric 			{
64362373Seric 				message("501 Argument required");
64462373Seric 				Errors++;
64562373Seric 			}
64662373Seric 			else
64762373Seric 			{
64864284Seric 				(void) sendtolist(p, NULLADDR, &vrfyqueue, e);
64962373Seric 			}
6507762Seric 			if (Errors != 0)
6519339Seric 			{
6529339Seric 				if (InChild)
6539339Seric 					finis();
6547762Seric 				break;
6559339Seric 			}
65662373Seric 			if (vrfyqueue == NULL)
65762373Seric 			{
65862373Seric 				message("554 Nothing to %s", vrfy ? "VRFY" : "EXPN");
65962373Seric 			}
6605003Seric 			while (vrfyqueue != NULL)
6615003Seric 			{
66263971Seric 				a = vrfyqueue;
66363971Seric 				while ((a = a->q_next) != NULL &&
66463971Seric 				       bitset(QDONTSEND|QBADADDR, a->q_flags))
66563971Seric 					continue;
6667685Seric 				if (!bitset(QDONTSEND|QBADADDR, vrfyqueue->q_flags))
66758151Seric 					printvrfyaddr(vrfyqueue, a == NULL);
66863847Seric 				vrfyqueue = vrfyqueue->q_next;
6695003Seric 			}
6709339Seric 			if (InChild)
6719339Seric 				finis();
6724549Seric 			break;
6734549Seric 
6744549Seric 		  case CMDHELP:		/* help -- give user info */
6754577Seric 			help(p);
6764549Seric 			break;
6774549Seric 
6784549Seric 		  case CMDNOOP:		/* noop -- do nothing */
67964122Seric 			message("250 OK");
6804549Seric 			break;
6814549Seric 
6824549Seric 		  case CMDQUIT:		/* quit -- leave mail */
68358151Seric 			message("221 %s closing connection", MyHostName);
68461051Seric 
68561051Seric 			/* avoid future 050 messages */
68661051Seric 			Verbose = FALSE;
68761051Seric 
6889339Seric 			if (InChild)
6899339Seric 				ExitStat = EX_QUIT;
6904549Seric 			finis();
6914549Seric 
6928544Seric 		  case CMDVERB:		/* set verbose mode */
69359957Seric 			if (bitset(PRIV_NOEXPN, PrivacyFlags))
69459957Seric 			{
69559957Seric 				/* this would give out the same info */
69659957Seric 				message("502 Verbose unavailable");
69759957Seric 				break;
69859957Seric 			}
6998544Seric 			Verbose = TRUE;
70058734Seric 			e->e_sendmode = SM_DELIVER;
70159957Seric 			message("250 Verbose mode");
7028544Seric 			break;
7038544Seric 
7049314Seric 		  case CMDONEX:		/* doing one transaction only */
7059378Seric 			OneXact = TRUE;
70659957Seric 			message("250 Only one transaction");
7079314Seric 			break;
7089314Seric 
70936230Skarels # ifdef SMTPDEBUG
7109339Seric 		  case CMDDBGQSHOW:	/* show queues */
7116907Seric 			printf("Send Queue=");
71255012Seric 			printaddr(e->e_sendqueue, TRUE);
7135003Seric 			break;
7147275Seric 
7157275Seric 		  case CMDDBGDEBUG:	/* set debug mode */
7167676Seric 			tTsetup(tTdvect, sizeof tTdvect, "0-99.1");
7177676Seric 			tTflag(p);
71858151Seric 			message("200 Debug set");
7197275Seric 			break;
7207275Seric 
72136230Skarels # else /* not SMTPDEBUG */
72236230Skarels 		  case CMDDBGQSHOW:	/* show queues */
72336230Skarels 		  case CMDDBGDEBUG:	/* set debug mode */
72464685Seric # endif /* SMTPDEBUG */
72564685Seric 		  case CMDLOGBOGUS:	/* bogus command */
72636233Skarels # ifdef LOG
72758308Seric 			if (LogLevel > 0)
72864685Seric 				syslog(LOG_CRIT,
72958020Seric 				    "\"%s\" command from %s (%s)",
730*66003Seric 				    c->cmdname, REALHOSTNAME,
73158755Seric 				    anynet_ntoa(&RealHostAddr));
73236233Skarels # endif
73336230Skarels 			/* FALL THROUGH */
73436230Skarels 
7354549Seric 		  case CMDERROR:	/* unknown command */
73658151Seric 			message("500 Command unrecognized");
7374549Seric 			break;
7384549Seric 
7394549Seric 		  default:
74036230Skarels 			errno = 0;
74158151Seric 			syserr("500 smtp: unknown code %d", c->cmdcode);
7424549Seric 			break;
7434549Seric 		}
7444549Seric 	}
7454549Seric }
7464549Seric /*
7474549Seric **  SKIPWORD -- skip a fixed word.
7484549Seric **
7494549Seric **	Parameters:
7504549Seric **		p -- place to start looking.
7514549Seric **		w -- word to skip.
7524549Seric **
7534549Seric **	Returns:
7544549Seric **		p following w.
7554549Seric **		NULL on error.
7564549Seric **
7574549Seric **	Side Effects:
7584549Seric **		clobbers the p data area.
7594549Seric */
7604549Seric 
7614549Seric static char *
7624549Seric skipword(p, w)
7634549Seric 	register char *p;
7644549Seric 	char *w;
7654549Seric {
7664549Seric 	register char *q;
7674549Seric 
7684549Seric 	/* find beginning of word */
76958050Seric 	while (isascii(*p) && isspace(*p))
7704549Seric 		p++;
7714549Seric 	q = p;
7724549Seric 
7734549Seric 	/* find end of word */
77458050Seric 	while (*p != '\0' && *p != ':' && !(isascii(*p) && isspace(*p)))
7754549Seric 		p++;
77658050Seric 	while (isascii(*p) && isspace(*p))
7774549Seric 		*p++ = '\0';
7784549Seric 	if (*p != ':')
7794549Seric 	{
7804549Seric 	  syntax:
78162373Seric 		message("501 Syntax error in parameters");
7824549Seric 		Errors++;
7834549Seric 		return (NULL);
7844549Seric 	}
7854549Seric 	*p++ = '\0';
78658050Seric 	while (isascii(*p) && isspace(*p))
7874549Seric 		p++;
7884549Seric 
78962373Seric 	if (*p == '\0')
79062373Seric 		goto syntax;
79162373Seric 
7924549Seric 	/* see if the input word matches desired word */
79333725Sbostic 	if (strcasecmp(q, w))
7944549Seric 		goto syntax;
7954549Seric 
7964549Seric 	return (p);
7974549Seric }
7984577Seric /*
79958151Seric **  PRINTVRFYADDR -- print an entry in the verify queue
80058151Seric **
80158151Seric **	Parameters:
80258151Seric **		a -- the address to print
80358151Seric **		last -- set if this is the last one.
80458151Seric **
80558151Seric **	Returns:
80658151Seric **		none.
80758151Seric **
80858151Seric **	Side Effects:
80958151Seric **		Prints the appropriate 250 codes.
81058151Seric */
81158151Seric 
81258151Seric printvrfyaddr(a, last)
81358151Seric 	register ADDRESS *a;
81458151Seric 	bool last;
81558151Seric {
81658151Seric 	char fmtbuf[20];
81758151Seric 
81858151Seric 	strcpy(fmtbuf, "250");
81958151Seric 	fmtbuf[3] = last ? ' ' : '-';
82058151Seric 
82159746Seric 	if (a->q_fullname == NULL)
82259746Seric 	{
82359746Seric 		if (strchr(a->q_user, '@') == NULL)
82459746Seric 			strcpy(&fmtbuf[4], "<%s@%s>");
82559746Seric 		else
82659746Seric 			strcpy(&fmtbuf[4], "<%s>");
82759746Seric 		message(fmtbuf, a->q_user, MyHostName);
82859746Seric 	}
82958151Seric 	else
83058151Seric 	{
83159746Seric 		if (strchr(a->q_user, '@') == NULL)
83259746Seric 			strcpy(&fmtbuf[4], "%s <%s@%s>");
83359746Seric 		else
83459746Seric 			strcpy(&fmtbuf[4], "%s <%s>");
83559746Seric 		message(fmtbuf, a->q_fullname, a->q_user, MyHostName);
83658151Seric 	}
83758151Seric }
83858151Seric /*
8394577Seric **  HELP -- implement the HELP command.
8404577Seric **
8414577Seric **	Parameters:
8424577Seric **		topic -- the topic we want help for.
8434577Seric **
8444577Seric **	Returns:
8454577Seric **		none.
8464577Seric **
8474577Seric **	Side Effects:
8484577Seric **		outputs the help file to message output.
8494577Seric */
8504577Seric 
8514577Seric help(topic)
8524577Seric 	char *topic;
8534577Seric {
8544577Seric 	register FILE *hf;
8554577Seric 	int len;
8564577Seric 	char buf[MAXLINE];
8574577Seric 	bool noinfo;
8584577Seric 
8598269Seric 	if (HelpFile == NULL || (hf = fopen(HelpFile, "r")) == NULL)
8604577Seric 	{
8614577Seric 		/* no help */
86211931Seric 		errno = 0;
86358151Seric 		message("502 HELP not implemented");
8644577Seric 		return;
8654577Seric 	}
8664577Seric 
86749669Seric 	if (topic == NULL || *topic == '\0')
86849669Seric 		topic = "smtp";
86949669Seric 	else
87049669Seric 		makelower(topic);
87149669Seric 
8724577Seric 	len = strlen(topic);
8734577Seric 	noinfo = TRUE;
8744577Seric 
8754577Seric 	while (fgets(buf, sizeof buf, hf) != NULL)
8764577Seric 	{
8774577Seric 		if (strncmp(buf, topic, len) == 0)
8784577Seric 		{
8794577Seric 			register char *p;
8804577Seric 
88156795Seric 			p = strchr(buf, '\t');
8824577Seric 			if (p == NULL)
8834577Seric 				p = buf;
8844577Seric 			else
8854577Seric 				p++;
8864577Seric 			fixcrlf(p, TRUE);
88758151Seric 			message("214-%s", p);
8884577Seric 			noinfo = FALSE;
8894577Seric 		}
8904577Seric 	}
8914577Seric 
8924577Seric 	if (noinfo)
89358151Seric 		message("504 HELP topic unknown");
8944577Seric 	else
89558151Seric 		message("214 End of HELP info");
8964628Seric 	(void) fclose(hf);
8974577Seric }
8988544Seric /*
8999339Seric **  RUNINCHILD -- return twice -- once in the child, then in the parent again
9009339Seric **
9019339Seric **	Parameters:
9029339Seric **		label -- a string used in error messages
9039339Seric **
9049339Seric **	Returns:
9059339Seric **		zero in the child
9069339Seric **		one in the parent
9079339Seric **
9089339Seric **	Side Effects:
9099339Seric **		none.
9109339Seric */
9118544Seric 
91255012Seric runinchild(label, e)
9139339Seric 	char *label;
91455012Seric 	register ENVELOPE *e;
9159339Seric {
9169339Seric 	int childpid;
9179339Seric 
91816158Seric 	if (!OneXact)
9199339Seric 	{
92016158Seric 		childpid = dofork();
92116158Seric 		if (childpid < 0)
92216158Seric 		{
92316158Seric 			syserr("%s: cannot fork", label);
92416158Seric 			return (1);
92516158Seric 		}
92616158Seric 		if (childpid > 0)
92716158Seric 		{
92816158Seric 			auto int st;
9299339Seric 
93016158Seric 			/* parent -- wait for child to complete */
93161093Seric 			setproctitle("server %s child wait", CurHostName);
93216158Seric 			st = waitfor(childpid);
93316158Seric 			if (st == -1)
93416158Seric 				syserr("%s: lost child", label);
93564948Seric 			else if (!WIFEXITED(st))
93664948Seric 				syserr("%s: died on signal %d",
93764948Seric 					label, st & 0177);
9389339Seric 
93916158Seric 			/* if we exited on a QUIT command, complete the process */
94016158Seric 			if (st == (EX_QUIT << 8))
94116158Seric 				finis();
9429339Seric 
94316158Seric 			return (1);
94416158Seric 		}
94516158Seric 		else
94616158Seric 		{
94716158Seric 			/* child */
94816158Seric 			InChild = TRUE;
94925050Seric 			QuickAbort = FALSE;
95055012Seric 			clearenvelope(e, FALSE);
95116158Seric 		}
9529339Seric 	}
95315256Seric 
95416158Seric 	/* open alias database */
95560537Seric 	initmaps(FALSE, e);
95616158Seric 
95716158Seric 	return (0);
9589339Seric }
9599339Seric 
96056795Seric # endif /* SMTP */
961