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*66283Seric static char sccsid[] = "@(#)srvrsmtp.c	8.30 (Berkeley) 02/28/94 (with SMTP)";
1433731Sbostic #else
15*66283Seric static char sccsid[] = "@(#)srvrsmtp.c	8.30 (Berkeley) 02/28/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*66283Seric #define MAXBADCOMMANDS	25		/* maximum number of bad commands */
96*66283Seric 
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 */
11166005Seric 	char *peerhostname;		/* name of SMTP peer or "localhost" */
11258333Seric 	auto char *delimptr;
11358714Seric 	char *id;
11459747Seric 	int nrcpts;			/* number of RCPT commands */
11563787Seric 	bool doublequeue;
116*66283Seric 	int badcommands = 0;		/* count of bad commands */
1178544Seric 	char inp[MAXLINE];
11857232Seric 	char cmdbuf[MAXLINE];
1197124Seric 	extern char Version[];
12024943Seric 	extern ENVELOPE BlankEnvelope;
1214549Seric 
12259066Seric 	if (fileno(OutChannel) != fileno(stdout))
1237363Seric 	{
1247363Seric 		/* arrange for debugging output to go to remote host */
12559066Seric 		(void) dup2(fileno(OutChannel), fileno(stdout));
1267363Seric 	}
12755012Seric 	settime(e);
12866005Seric 	peerhostname = RealHostName;
12966005Seric 	if (peerhostname == NULL)
13066005Seric 		peerhostname = "localhost";
13166005Seric 	CurHostName = peerhostname;
13265017Seric 	CurSmtpClient = macvalue('_', e);
13365017Seric 	if (CurSmtpClient == NULL)
13466003Seric 		CurSmtpClient = CurHostName;
13565017Seric 
13665017Seric 	setproctitle("server %s startup", CurSmtpClient);
13758050Seric 	expand("\201e", inp, &inp[sizeof inp], e);
13864496Seric 	if (BrokenSmtpPeers)
13964496Seric 	{
14064496Seric 		message("220 %s", inp);
14164496Seric 	}
14264496Seric 	else
14364496Seric 	{
14464496Seric 		message("220-%s", inp);
14564496Seric 		message("220 ESMTP spoken here");
14664496Seric 	}
14758330Seric 	protocol = NULL;
14859016Seric 	sendinghost = macvalue('s', e);
14958082Seric 	gothello = FALSE;
15058330Seric 	gotmail = FALSE;
1514549Seric 	for (;;)
1524549Seric 	{
15312612Seric 		/* arrange for backout */
15465751Seric 		if (setjmp(TopFrame) > 0)
15559058Seric 		{
15665751Seric 			/* if() nesting is necessary for Cray UNICOS */
15765751Seric 			if (InChild)
15865751Seric 			{
15965751Seric 				QuickAbort = FALSE;
16065751Seric 				SuprErrs = TRUE;
16165751Seric 				finis();
16265751Seric 			}
16359058Seric 		}
16412612Seric 		QuickAbort = FALSE;
16512612Seric 		HoldErrs = FALSE;
16651951Seric 		LogUsrErrs = FALSE;
16763843Seric 		e->e_flags &= ~(EF_VRFYONLY|EF_GLOBALERRS);
16812612Seric 
1697356Seric 		/* setup for the read */
17055012Seric 		e->e_to = NULL;
1714577Seric 		Errors = 0;
1727275Seric 		(void) fflush(stdout);
1737356Seric 
1747356Seric 		/* read the input line */
17561093Seric 		SmtpPhase = "server cmd read";
17661093Seric 		setproctitle("server %s cmd read", CurHostName);
17761093Seric 		p = sfgets(inp, sizeof inp, InChannel, TimeOuts.to_nextcommand,
17861093Seric 				SmtpPhase);
1797356Seric 
1807685Seric 		/* handle errors */
1817356Seric 		if (p == NULL)
1827356Seric 		{
1834549Seric 			/* end of file, just die */
18466017Seric 			disconnect(1, e);
18558151Seric 			message("421 %s Lost input channel from %s",
18665017Seric 				MyHostName, CurSmtpClient);
18755464Seric #ifdef LOG
18863843Seric 			if (LogLevel > (gotmail ? 1 : 19))
18955464Seric 				syslog(LOG_NOTICE, "lost input channel from %s",
19065017Seric 					CurSmtpClient);
19155464Seric #endif
19258069Seric 			if (InChild)
19358069Seric 				ExitStat = EX_QUIT;
1944549Seric 			finis();
1954549Seric 		}
1964549Seric 
1974549Seric 		/* clean up end of line */
1984558Seric 		fixcrlf(inp, TRUE);
1994549Seric 
2004713Seric 		/* echo command to transcript */
20155012Seric 		if (e->e_xfp != NULL)
20255012Seric 			fprintf(e->e_xfp, "<<< %s\n", inp);
2034713Seric 
20459060Seric 		if (e->e_id == NULL)
20565058Seric 			setproctitle("%s: %.80s", CurSmtpClient, inp);
20659060Seric 		else
20765058Seric 			setproctitle("%s %s: %.80s", e->e_id, CurSmtpClient, inp);
20859060Seric 
2094549Seric 		/* break off command */
21058050Seric 		for (p = inp; isascii(*p) && isspace(*p); p++)
2114549Seric 			continue;
21257232Seric 		cmd = cmdbuf;
21358050Seric 		while (*p != '\0' &&
21458050Seric 		       !(isascii(*p) && isspace(*p)) &&
21558050Seric 		       cmd < &cmdbuf[sizeof cmdbuf - 2])
21624981Seric 			*cmd++ = *p++;
21724981Seric 		*cmd = '\0';
2184549Seric 
21925691Seric 		/* throw away leading whitespace */
22058050Seric 		while (isascii(*p) && isspace(*p))
22125691Seric 			p++;
22225691Seric 
2234549Seric 		/* decode command */
2244549Seric 		for (c = CmdTab; c->cmdname != NULL; c++)
2254549Seric 		{
22633725Sbostic 			if (!strcasecmp(c->cmdname, cmdbuf))
2274549Seric 				break;
2284549Seric 		}
2294549Seric 
23051954Seric 		/* reset errors */
23151954Seric 		errno = 0;
23251954Seric 
2334549Seric 		/* process command */
2344549Seric 		switch (c->cmdcode)
2354549Seric 		{
2364976Seric 		  case CMDHELO:		/* hello -- introduce yourself */
23758323Seric 		  case CMDEHLO:		/* extended hello */
23858323Seric 			if (c->cmdcode == CMDEHLO)
23958323Seric 			{
24058323Seric 				protocol = "ESMTP";
24161093Seric 				SmtpPhase = "server EHLO";
24258323Seric 			}
24358323Seric 			else
24458323Seric 			{
24558323Seric 				protocol = "SMTP";
24661093Seric 				SmtpPhase = "server HELO";
24758323Seric 			}
24859016Seric 			sendinghost = newstr(p);
24960210Seric 			gothello = TRUE;
25060210Seric 			if (c->cmdcode != CMDEHLO)
25160239Seric 			{
25260239Seric 				/* print old message and be done with it */
25360239Seric 				message("250 %s Hello %s, pleased to meet you",
25465017Seric 					MyHostName, CurSmtpClient);
25560210Seric 				break;
25660239Seric 			}
25760239Seric 
25860239Seric 			/* print extended message and brag */
25960239Seric 			message("250-%s Hello %s, pleased to meet you",
26060239Seric 				MyHostName, p);
26158323Seric 			if (!bitset(PRIV_NOEXPN, PrivacyFlags))
26258323Seric 				message("250-EXPN");
26364359Seric 			if (MaxMessageSize > 0)
26464359Seric 				message("250-SIZE %ld", MaxMessageSize);
26559271Seric 			else
26659271Seric 				message("250-SIZE");
26758323Seric 			message("250 HELP");
2684976Seric 			break;
2694976Seric 
2704549Seric 		  case CMDMAIL:		/* mail -- designate sender */
27161093Seric 			SmtpPhase = "server MAIL";
27224943Seric 
2739314Seric 			/* check for validity of this command */
27458789Seric 			if (!gothello)
27558082Seric 			{
27658957Seric 				/* set sending host to our known value */
27759016Seric 				if (sendinghost == NULL)
27866005Seric 					sendinghost = peerhostname;
27958957Seric 
28058789Seric 				if (bitset(PRIV_NEEDMAILHELO, PrivacyFlags))
28158821Seric 				{
28258789Seric 					message("503 Polite people say HELO first");
28358821Seric 					break;
28458821Seric 				}
28558082Seric 			}
28658109Seric 			if (gotmail)
2874558Seric 			{
28858151Seric 				message("503 Sender already specified");
28963843Seric 				if (InChild)
29063843Seric 					finis();
2914558Seric 				break;
2924558Seric 			}
2939339Seric 			if (InChild)
2949339Seric 			{
29536230Skarels 				errno = 0;
29658151Seric 				syserr("503 Nested MAIL command: MAIL %s", p);
29758069Seric 				finis();
2989339Seric 			}
2999339Seric 
3009339Seric 			/* fork a subprocess to process this command */
30155012Seric 			if (runinchild("SMTP-MAIL", e) > 0)
3029339Seric 				break;
30363753Seric 			if (!gothello)
30463753Seric 			{
30563753Seric 				auth_warning(e,
30666005Seric 					"Host %s didn't use HELO protocol",
30766005Seric 					peerhostname);
30863753Seric 			}
30965947Seric #ifdef PICKY_HELO_CHECK
31066005Seric 			if (strcasecmp(sendinghost, peerhostname) != 0 &&
31166005Seric 			    (strcasecmp(peerhostname, "localhost") != 0 ||
31265823Seric 			     strcasecmp(sendinghost, MyHostName) != 0))
31365823Seric 			{
31465823Seric 				auth_warning(e, "Host %s claimed to be %s",
31566005Seric 					peerhostname, sendinghost);
31665823Seric 			}
31765947Seric #endif
31865823Seric 
31958323Seric 			if (protocol == NULL)
32058323Seric 				protocol = "SMTP";
32158323Seric 			define('r', protocol, e);
32259016Seric 			define('s', sendinghost, e);
32355012Seric 			initsys(e);
32459747Seric 			nrcpts = 0;
32565089Seric 			e->e_flags |= EF_LOGSENDER;
32665058Seric 			setproctitle("%s %s: %.80s", e->e_id, CurSmtpClient, inp);
3279339Seric 
3289339Seric 			/* child -- go do the processing */
3294549Seric 			p = skipword(p, "from");
3304549Seric 			if (p == NULL)
3314549Seric 				break;
33257977Seric 			if (setjmp(TopFrame) > 0)
33358147Seric 			{
33458147Seric 				/* this failed -- undo work */
33558147Seric 				if (InChild)
33659058Seric 				{
33759058Seric 					QuickAbort = FALSE;
33859058Seric 					SuprErrs = TRUE;
33963787Seric 					e->e_flags &= ~EF_FATALERRS;
34058147Seric 					finis();
34159058Seric 				}
34257977Seric 				break;
34358147Seric 			}
34457977Seric 			QuickAbort = TRUE;
34558333Seric 
34658333Seric 			/* must parse sender first */
34758333Seric 			delimptr = NULL;
34858704Seric 			setsender(p, e, &delimptr, FALSE);
34958333Seric 			p = delimptr;
35058333Seric 			if (p != NULL && *p != '\0')
35158333Seric 				*p++ = '\0';
35258333Seric 
35358333Seric 			/* now parse ESMTP arguments */
35458333Seric 			msize = 0;
35558333Seric 			for (; p != NULL && *p != '\0'; p++)
35658333Seric 			{
35758333Seric 				char *kp;
35858333Seric 				char *vp;
35958333Seric 
36058333Seric 				/* locate the beginning of the keyword */
36158333Seric 				while (isascii(*p) && isspace(*p))
36258333Seric 					p++;
36358333Seric 				if (*p == '\0')
36458333Seric 					break;
36558333Seric 				kp = p;
36658333Seric 
36758333Seric 				/* skip to the value portion */
36858333Seric 				while (isascii(*p) && isalnum(*p) || *p == '-')
36958333Seric 					p++;
37058333Seric 				if (*p == '=')
37158333Seric 				{
37258333Seric 					*p++ = '\0';
37358333Seric 					vp = p;
37458333Seric 
37558333Seric 					/* skip to the end of the value */
37658333Seric 					while (*p != '\0' && *p != ' ' &&
37758333Seric 					       !(isascii(*p) && iscntrl(*p)) &&
37858333Seric 					       *p != '=')
37958333Seric 						p++;
38058333Seric 				}
38158333Seric 
38258333Seric 				if (*p != '\0')
38358333Seric 					*p++ = '\0';
38458333Seric 
38558333Seric 				if (tTd(19, 1))
38658333Seric 					printf("MAIL: got arg %s=%s\n", kp,
38758333Seric 						vp == NULL ? "<null>" : vp);
38858333Seric 
38958333Seric 				if (strcasecmp(kp, "size") == 0)
39058333Seric 				{
39159093Seric 					if (vp == NULL)
39258333Seric 					{
39358333Seric 						usrerr("501 SIZE requires a value");
39458333Seric 						/* NOTREACHED */
39558333Seric 					}
39658333Seric 					msize = atol(vp);
39758333Seric 				}
39859093Seric 				else if (strcasecmp(kp, "body") == 0)
39959093Seric 				{
40059093Seric 					if (vp == NULL)
40159093Seric 					{
40259093Seric 						usrerr("501 BODY requires a value");
40359093Seric 						/* NOTREACHED */
40459093Seric 					}
40559093Seric # ifdef MIME
40659093Seric 					if (strcasecmp(vp, "8bitmime") == 0)
40759093Seric 					{
40859093Seric 						e->e_bodytype = "8BITMIME";
40959709Seric 						SevenBit = FALSE;
41059093Seric 					}
41159093Seric 					else if (strcasecmp(vp, "7bit") == 0)
41259093Seric 					{
41359093Seric 						e->e_bodytype = "7BIT";
41459709Seric 						SevenBit = TRUE;
41559093Seric 					}
41659093Seric 					else
41759093Seric 					{
41859093Seric 						usrerr("501 Unknown BODY type %s",
41959093Seric 							vp);
42059093Seric 					}
42159093Seric # endif
42259093Seric 				}
42358333Seric 				else
42458333Seric 				{
42558333Seric 					usrerr("501 %s parameter unrecognized", kp);
42658333Seric 					/* NOTREACHED */
42758333Seric 				}
42858333Seric 			}
42959284Seric 
43059284Seric 			if (MaxMessageSize > 0 && msize > MaxMessageSize)
43159284Seric 			{
43259284Seric 				usrerr("552 Message size exceeds fixed maximum message size (%ld)",
43359284Seric 					MaxMessageSize);
43459284Seric 				/* NOTREACHED */
43559284Seric 			}
43658333Seric 
43758333Seric 			if (!enoughspace(msize))
43858333Seric 			{
43958333Seric 				message("452 Insufficient disk space; try again later");
44058333Seric 				break;
44158333Seric 			}
44258151Seric 			message("250 Sender ok");
44358147Seric 			gotmail = TRUE;
4444549Seric 			break;
4454549Seric 
4464976Seric 		  case CMDRCPT:		/* rcpt -- designate recipient */
44758850Seric 			if (!gotmail)
44858850Seric 			{
44958850Seric 				usrerr("503 Need MAIL before RCPT");
45058850Seric 				break;
45158850Seric 			}
45261093Seric 			SmtpPhase = "server RCPT";
45312612Seric 			if (setjmp(TopFrame) > 0)
45414785Seric 			{
45555012Seric 				e->e_flags &= ~EF_FATALERRS;
45612612Seric 				break;
45714785Seric 			}
45812612Seric 			QuickAbort = TRUE;
45951951Seric 			LogUsrErrs = TRUE;
46058093Seric 
46159699Seric 			if (e->e_sendmode != SM_DELIVER)
46259699Seric 				e->e_flags |= EF_VRFYONLY;
46358919Seric 
4644549Seric 			p = skipword(p, "to");
4654549Seric 			if (p == NULL)
4664549Seric 				break;
46764284Seric 			a = parseaddr(p, NULLADDR, RF_COPYALL, ' ', NULL, e);
46812612Seric 			if (a == NULL)
46912612Seric 				break;
47016886Seric 			a->q_flags |= QPRIMARY;
47155012Seric 			a = recipient(a, &e->e_sendqueue, e);
47212612Seric 			if (Errors != 0)
47312612Seric 				break;
47412612Seric 
47512612Seric 			/* no errors during parsing, but might be a duplicate */
47655012Seric 			e->e_to = p;
47712612Seric 			if (!bitset(QBADADDR, a->q_flags))
47859747Seric 			{
47964718Seric 				message("250 Recipient ok%s",
48064718Seric 					bitset(QQUEUEUP, a->q_flags) ?
48164718Seric 						" (will queue)" : "");
48259747Seric 				nrcpts++;
48359747Seric 			}
48412612Seric 			else
4854549Seric 			{
48612612Seric 				/* punt -- should keep message in ADDRESS.... */
48758151Seric 				message("550 Addressee unknown");
4884549Seric 			}
48955012Seric 			e->e_to = NULL;
4904549Seric 			break;
4914549Seric 
4924549Seric 		  case CMDDATA:		/* data -- text of mail */
49361093Seric 			SmtpPhase = "server DATA";
49458109Seric 			if (!gotmail)
4954549Seric 			{
49658151Seric 				message("503 Need MAIL command");
4974976Seric 				break;
4984549Seric 			}
49964718Seric 			else if (nrcpts <= 0)
5004549Seric 			{
50158151Seric 				message("503 Need RCPT (recipient)");
5024976Seric 				break;
5034549Seric 			}
5044976Seric 
50558929Seric 			/* check to see if we need to re-expand aliases */
50663787Seric 			/* also reset QBADADDR on already-diagnosted addrs */
50763787Seric 			doublequeue = FALSE;
50858929Seric 			for (a = e->e_sendqueue; a != NULL; a = a->q_next)
50958929Seric 			{
51058929Seric 				if (bitset(QVERIFIED, a->q_flags))
51163787Seric 				{
51263787Seric 					/* need to re-expand aliases */
51363787Seric 					doublequeue = TRUE;
51463787Seric 				}
51563787Seric 				if (bitset(QBADADDR, a->q_flags))
51663787Seric 				{
51763787Seric 					/* make this "go away" */
51863787Seric 					a->q_flags |= QDONTSEND;
51963787Seric 					a->q_flags &= ~QBADADDR;
52063787Seric 				}
52158929Seric 			}
52258929Seric 
5234976Seric 			/* collect the text of the message */
52424943Seric 			SmtpPhase = "collect";
52564766Seric 			collect(TRUE, doublequeue, e);
52664766Seric 			if (Errors != 0)
52764766Seric 				goto abortmessage;
52863965Seric 			HoldErrs = TRUE;
5294976Seric 
5308238Seric 			/*
5318238Seric 			**  Arrange to send to everyone.
5328238Seric 			**	If sending to multiple people, mail back
5338238Seric 			**		errors rather than reporting directly.
5348238Seric 			**	In any case, don't mail back errors for
5358238Seric 			**		anything that has happened up to
5368238Seric 			**		now (the other end will do this).
53710197Seric 			**	Truncate our transcript -- the mail has gotten
53810197Seric 			**		to us successfully, and if we have
53910197Seric 			**		to mail this back, it will be easier
54010197Seric 			**		on the reader.
5418238Seric 			**	Then send to everyone.
5428238Seric 			**	Finally give a reply code.  If an error has
5438238Seric 			**		already been given, don't mail a
5448238Seric 			**		message back.
5459339Seric 			**	We goose error returns by clearing error bit.
5468238Seric 			*/
5478238Seric 
54824943Seric 			SmtpPhase = "delivery";
54963787Seric 			if (nrcpts != 1 && !doublequeue)
5509378Seric 			{
5519378Seric 				HoldErrs = TRUE;
55258734Seric 				e->e_errormode = EM_MAIL;
5539378Seric 			}
55455012Seric 			e->e_xfp = freopen(queuename(e, 'x'), "w", e->e_xfp);
55558714Seric 			id = e->e_id;
5564976Seric 
5574976Seric 			/* send to all recipients */
55863787Seric 			sendall(e, doublequeue ? SM_QUEUE : SM_DEFAULT);
55955012Seric 			e->e_to = NULL;
5604976Seric 
5618238Seric 			/* issue success if appropriate and reset */
5628238Seric 			if (Errors == 0 || HoldErrs)
56358855Seric 				message("250 %s Message accepted for delivery", id);
56459747Seric 
56559747Seric 			if (bitset(EF_FATALERRS, e->e_flags) && !HoldErrs)
56659730Seric 			{
56759730Seric 				/* avoid sending back an extra message */
56859730Seric 				e->e_flags &= ~EF_FATALERRS;
56959747Seric 				e->e_flags |= EF_CLRQUEUE;
57059730Seric 			}
5718238Seric 			else
57258919Seric 			{
57359747Seric 				/* from now on, we have to operate silently */
57459747Seric 				HoldErrs = TRUE;
57559747Seric 				e->e_errormode = EM_MAIL;
57659747Seric 
57759730Seric 				/* if we just queued, poke it */
57863787Seric 				if (doublequeue && e->e_sendmode != SM_QUEUE)
57959730Seric 				{
58064296Seric 					extern pid_t dowork();
58164296Seric 
58259730Seric 					unlockqueue(e);
58364296Seric 					(void) dowork(id, TRUE, TRUE, e);
58459730Seric 				}
58558919Seric 			}
58658883Seric 
58759747Seric   abortmessage:
5889339Seric 			/* if in a child, pop back to our parent */
5899339Seric 			if (InChild)
5909339Seric 				finis();
59124943Seric 
59224943Seric 			/* clean up a bit */
59358109Seric 			gotmail = FALSE;
59455012Seric 			dropenvelope(e);
59558179Seric 			CurEnv = e = newenvelope(e, CurEnv);
59655012Seric 			e->e_flags = BlankEnvelope.e_flags;
5974549Seric 			break;
5984549Seric 
5994549Seric 		  case CMDRSET:		/* rset -- reset state */
60058151Seric 			message("250 Reset state");
60164359Seric 			e->e_flags |= EF_CLRQUEUE;
6029339Seric 			if (InChild)
6039339Seric 				finis();
60458109Seric 
60558109Seric 			/* clean up a bit */
60658109Seric 			gotmail = FALSE;
60758109Seric 			dropenvelope(e);
60858179Seric 			CurEnv = e = newenvelope(e, CurEnv);
6099339Seric 			break;
6104549Seric 
6114549Seric 		  case CMDVRFY:		/* vrfy -- verify address */
61258092Seric 		  case CMDEXPN:		/* expn -- expand address */
61358092Seric 			vrfy = c->cmdcode == CMDVRFY;
61458092Seric 			if (bitset(vrfy ? PRIV_NOVRFY : PRIV_NOEXPN,
61558092Seric 						PrivacyFlags))
61658082Seric 			{
61758412Seric 				if (vrfy)
61858412Seric 					message("252 Who's to say?");
61958412Seric 				else
62065192Seric 					message("502 Sorry, we do not allow this operation");
62165017Seric #ifdef LOG
62265017Seric 				if (LogLevel > 5)
62365017Seric 					syslog(LOG_INFO, "%s: %s [rejected]",
62465017Seric 						CurSmtpClient, inp);
62565017Seric #endif
62658082Seric 				break;
62758082Seric 			}
62858082Seric 			else if (!gothello &&
62958092Seric 				 bitset(vrfy ? PRIV_NEEDVRFYHELO : PRIV_NEEDEXPNHELO,
63058092Seric 						PrivacyFlags))
63158082Seric 			{
63258151Seric 				message("503 I demand that you introduce yourself first");
63358082Seric 				break;
63458082Seric 			}
63558092Seric 			if (runinchild(vrfy ? "SMTP-VRFY" : "SMTP-EXPN", e) > 0)
6369339Seric 				break;
63755173Seric #ifdef LOG
63858020Seric 			if (LogLevel > 5)
63965017Seric 				syslog(LOG_INFO, "%s: %s", CurSmtpClient, inp);
64055173Seric #endif
6415003Seric 			vrfyqueue = NULL;
6427762Seric 			QuickAbort = TRUE;
64358092Seric 			if (vrfy)
64458092Seric 				e->e_flags |= EF_VRFYONLY;
64562373Seric 			while (*p != '\0' && isascii(*p) && isspace(*p))
64662373Seric 				*p++;
64762373Seric 			if (*p == '\0')
64862373Seric 			{
64962373Seric 				message("501 Argument required");
65062373Seric 				Errors++;
65162373Seric 			}
65262373Seric 			else
65362373Seric 			{
65464284Seric 				(void) sendtolist(p, NULLADDR, &vrfyqueue, e);
65562373Seric 			}
6567762Seric 			if (Errors != 0)
6579339Seric 			{
6589339Seric 				if (InChild)
6599339Seric 					finis();
6607762Seric 				break;
6619339Seric 			}
66262373Seric 			if (vrfyqueue == NULL)
66362373Seric 			{
66462373Seric 				message("554 Nothing to %s", vrfy ? "VRFY" : "EXPN");
66562373Seric 			}
6665003Seric 			while (vrfyqueue != NULL)
6675003Seric 			{
66863971Seric 				a = vrfyqueue;
66963971Seric 				while ((a = a->q_next) != NULL &&
67063971Seric 				       bitset(QDONTSEND|QBADADDR, a->q_flags))
67163971Seric 					continue;
6727685Seric 				if (!bitset(QDONTSEND|QBADADDR, vrfyqueue->q_flags))
67358151Seric 					printvrfyaddr(vrfyqueue, a == NULL);
67463847Seric 				vrfyqueue = vrfyqueue->q_next;
6755003Seric 			}
6769339Seric 			if (InChild)
6779339Seric 				finis();
6784549Seric 			break;
6794549Seric 
6804549Seric 		  case CMDHELP:		/* help -- give user info */
6814577Seric 			help(p);
6824549Seric 			break;
6834549Seric 
6844549Seric 		  case CMDNOOP:		/* noop -- do nothing */
68564122Seric 			message("250 OK");
6864549Seric 			break;
6874549Seric 
6884549Seric 		  case CMDQUIT:		/* quit -- leave mail */
68958151Seric 			message("221 %s closing connection", MyHostName);
69061051Seric 
691*66283Seric doquit:
69261051Seric 			/* avoid future 050 messages */
69366017Seric 			disconnect(1, e);
69461051Seric 
6959339Seric 			if (InChild)
6969339Seric 				ExitStat = EX_QUIT;
6974549Seric 			finis();
6984549Seric 
6998544Seric 		  case CMDVERB:		/* set verbose mode */
70059957Seric 			if (bitset(PRIV_NOEXPN, PrivacyFlags))
70159957Seric 			{
70259957Seric 				/* this would give out the same info */
70359957Seric 				message("502 Verbose unavailable");
70459957Seric 				break;
70559957Seric 			}
7068544Seric 			Verbose = TRUE;
70758734Seric 			e->e_sendmode = SM_DELIVER;
70859957Seric 			message("250 Verbose mode");
7098544Seric 			break;
7108544Seric 
7119314Seric 		  case CMDONEX:		/* doing one transaction only */
7129378Seric 			OneXact = TRUE;
71359957Seric 			message("250 Only one transaction");
7149314Seric 			break;
7159314Seric 
71636230Skarels # ifdef SMTPDEBUG
7179339Seric 		  case CMDDBGQSHOW:	/* show queues */
7186907Seric 			printf("Send Queue=");
71955012Seric 			printaddr(e->e_sendqueue, TRUE);
7205003Seric 			break;
7217275Seric 
7227275Seric 		  case CMDDBGDEBUG:	/* set debug mode */
7237676Seric 			tTsetup(tTdvect, sizeof tTdvect, "0-99.1");
7247676Seric 			tTflag(p);
72558151Seric 			message("200 Debug set");
7267275Seric 			break;
7277275Seric 
72836230Skarels # else /* not SMTPDEBUG */
72936230Skarels 		  case CMDDBGQSHOW:	/* show queues */
73036230Skarels 		  case CMDDBGDEBUG:	/* set debug mode */
73164685Seric # endif /* SMTPDEBUG */
73264685Seric 		  case CMDLOGBOGUS:	/* bogus command */
73336233Skarels # ifdef LOG
73458308Seric 			if (LogLevel > 0)
73564685Seric 				syslog(LOG_CRIT,
73658020Seric 				    "\"%s\" command from %s (%s)",
73766005Seric 				    c->cmdname, peerhostname,
73858755Seric 				    anynet_ntoa(&RealHostAddr));
73936233Skarels # endif
74036230Skarels 			/* FALL THROUGH */
74136230Skarels 
7424549Seric 		  case CMDERROR:	/* unknown command */
743*66283Seric 			if (++badcommands > MAXBADCOMMANDS)
744*66283Seric 			{
745*66283Seric 				message("421 %s Too many bad commands; closing connection",
746*66283Seric 					MyHostName);
747*66283Seric 				goto doquit;
748*66283Seric 			}
749*66283Seric 
75058151Seric 			message("500 Command unrecognized");
7514549Seric 			break;
7524549Seric 
7534549Seric 		  default:
75436230Skarels 			errno = 0;
75558151Seric 			syserr("500 smtp: unknown code %d", c->cmdcode);
7564549Seric 			break;
7574549Seric 		}
7584549Seric 	}
7594549Seric }
7604549Seric /*
7614549Seric **  SKIPWORD -- skip a fixed word.
7624549Seric **
7634549Seric **	Parameters:
7644549Seric **		p -- place to start looking.
7654549Seric **		w -- word to skip.
7664549Seric **
7674549Seric **	Returns:
7684549Seric **		p following w.
7694549Seric **		NULL on error.
7704549Seric **
7714549Seric **	Side Effects:
7724549Seric **		clobbers the p data area.
7734549Seric */
7744549Seric 
7754549Seric static char *
7764549Seric skipword(p, w)
7774549Seric 	register char *p;
7784549Seric 	char *w;
7794549Seric {
7804549Seric 	register char *q;
78166005Seric 	char *firstp = p;
7824549Seric 
7834549Seric 	/* find beginning of word */
78458050Seric 	while (isascii(*p) && isspace(*p))
7854549Seric 		p++;
7864549Seric 	q = p;
7874549Seric 
7884549Seric 	/* find end of word */
78958050Seric 	while (*p != '\0' && *p != ':' && !(isascii(*p) && isspace(*p)))
7904549Seric 		p++;
79158050Seric 	while (isascii(*p) && isspace(*p))
7924549Seric 		*p++ = '\0';
7934549Seric 	if (*p != ':')
7944549Seric 	{
7954549Seric 	  syntax:
79666005Seric 		message("501 Syntax error in parameters scanning \"%s\"",
79766005Seric 			firstp);
7984549Seric 		Errors++;
7994549Seric 		return (NULL);
8004549Seric 	}
8014549Seric 	*p++ = '\0';
80258050Seric 	while (isascii(*p) && isspace(*p))
8034549Seric 		p++;
8044549Seric 
80562373Seric 	if (*p == '\0')
80662373Seric 		goto syntax;
80762373Seric 
8084549Seric 	/* see if the input word matches desired word */
80933725Sbostic 	if (strcasecmp(q, w))
8104549Seric 		goto syntax;
8114549Seric 
8124549Seric 	return (p);
8134549Seric }
8144577Seric /*
81558151Seric **  PRINTVRFYADDR -- print an entry in the verify queue
81658151Seric **
81758151Seric **	Parameters:
81858151Seric **		a -- the address to print
81958151Seric **		last -- set if this is the last one.
82058151Seric **
82158151Seric **	Returns:
82258151Seric **		none.
82358151Seric **
82458151Seric **	Side Effects:
82558151Seric **		Prints the appropriate 250 codes.
82658151Seric */
82758151Seric 
82858151Seric printvrfyaddr(a, last)
82958151Seric 	register ADDRESS *a;
83058151Seric 	bool last;
83158151Seric {
83258151Seric 	char fmtbuf[20];
83358151Seric 
83458151Seric 	strcpy(fmtbuf, "250");
83558151Seric 	fmtbuf[3] = last ? ' ' : '-';
83658151Seric 
83759746Seric 	if (a->q_fullname == NULL)
83859746Seric 	{
83959746Seric 		if (strchr(a->q_user, '@') == NULL)
84059746Seric 			strcpy(&fmtbuf[4], "<%s@%s>");
84159746Seric 		else
84259746Seric 			strcpy(&fmtbuf[4], "<%s>");
84359746Seric 		message(fmtbuf, a->q_user, MyHostName);
84459746Seric 	}
84558151Seric 	else
84658151Seric 	{
84759746Seric 		if (strchr(a->q_user, '@') == NULL)
84859746Seric 			strcpy(&fmtbuf[4], "%s <%s@%s>");
84959746Seric 		else
85059746Seric 			strcpy(&fmtbuf[4], "%s <%s>");
85159746Seric 		message(fmtbuf, a->q_fullname, a->q_user, MyHostName);
85258151Seric 	}
85358151Seric }
85458151Seric /*
8554577Seric **  HELP -- implement the HELP command.
8564577Seric **
8574577Seric **	Parameters:
8584577Seric **		topic -- the topic we want help for.
8594577Seric **
8604577Seric **	Returns:
8614577Seric **		none.
8624577Seric **
8634577Seric **	Side Effects:
8644577Seric **		outputs the help file to message output.
8654577Seric */
8664577Seric 
8674577Seric help(topic)
8684577Seric 	char *topic;
8694577Seric {
8704577Seric 	register FILE *hf;
8714577Seric 	int len;
8724577Seric 	char buf[MAXLINE];
8734577Seric 	bool noinfo;
8744577Seric 
8758269Seric 	if (HelpFile == NULL || (hf = fopen(HelpFile, "r")) == NULL)
8764577Seric 	{
8774577Seric 		/* no help */
87811931Seric 		errno = 0;
87958151Seric 		message("502 HELP not implemented");
8804577Seric 		return;
8814577Seric 	}
8824577Seric 
88349669Seric 	if (topic == NULL || *topic == '\0')
88449669Seric 		topic = "smtp";
88549669Seric 	else
88649669Seric 		makelower(topic);
88749669Seric 
8884577Seric 	len = strlen(topic);
8894577Seric 	noinfo = TRUE;
8904577Seric 
8914577Seric 	while (fgets(buf, sizeof buf, hf) != NULL)
8924577Seric 	{
8934577Seric 		if (strncmp(buf, topic, len) == 0)
8944577Seric 		{
8954577Seric 			register char *p;
8964577Seric 
89756795Seric 			p = strchr(buf, '\t');
8984577Seric 			if (p == NULL)
8994577Seric 				p = buf;
9004577Seric 			else
9014577Seric 				p++;
9024577Seric 			fixcrlf(p, TRUE);
90358151Seric 			message("214-%s", p);
9044577Seric 			noinfo = FALSE;
9054577Seric 		}
9064577Seric 	}
9074577Seric 
9084577Seric 	if (noinfo)
90958151Seric 		message("504 HELP topic unknown");
9104577Seric 	else
91158151Seric 		message("214 End of HELP info");
9124628Seric 	(void) fclose(hf);
9134577Seric }
9148544Seric /*
9159339Seric **  RUNINCHILD -- return twice -- once in the child, then in the parent again
9169339Seric **
9179339Seric **	Parameters:
9189339Seric **		label -- a string used in error messages
9199339Seric **
9209339Seric **	Returns:
9219339Seric **		zero in the child
9229339Seric **		one in the parent
9239339Seric **
9249339Seric **	Side Effects:
9259339Seric **		none.
9269339Seric */
9278544Seric 
92855012Seric runinchild(label, e)
9299339Seric 	char *label;
93055012Seric 	register ENVELOPE *e;
9319339Seric {
9329339Seric 	int childpid;
9339339Seric 
93416158Seric 	if (!OneXact)
9359339Seric 	{
93616158Seric 		childpid = dofork();
93716158Seric 		if (childpid < 0)
93816158Seric 		{
93916158Seric 			syserr("%s: cannot fork", label);
94016158Seric 			return (1);
94116158Seric 		}
94216158Seric 		if (childpid > 0)
94316158Seric 		{
94416158Seric 			auto int st;
9459339Seric 
94616158Seric 			/* parent -- wait for child to complete */
94761093Seric 			setproctitle("server %s child wait", CurHostName);
94816158Seric 			st = waitfor(childpid);
94916158Seric 			if (st == -1)
95016158Seric 				syserr("%s: lost child", label);
95164948Seric 			else if (!WIFEXITED(st))
95264948Seric 				syserr("%s: died on signal %d",
95364948Seric 					label, st & 0177);
9549339Seric 
95516158Seric 			/* if we exited on a QUIT command, complete the process */
95666017Seric 			if (WEXITSTATUS(st) == EX_QUIT)
95766017Seric 			{
95866017Seric 				disconnect(1, e);
95916158Seric 				finis();
96066017Seric 			}
9619339Seric 
96216158Seric 			return (1);
96316158Seric 		}
96416158Seric 		else
96516158Seric 		{
96616158Seric 			/* child */
96716158Seric 			InChild = TRUE;
96825050Seric 			QuickAbort = FALSE;
96955012Seric 			clearenvelope(e, FALSE);
97016158Seric 		}
9719339Seric 	}
97215256Seric 
97316158Seric 	/* open alias database */
97460537Seric 	initmaps(FALSE, e);
97516158Seric 
97616158Seric 	return (0);
9779339Seric }
9789339Seric 
97956795Seric # endif /* SMTP */
980