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*67417Seric static char sccsid[] = "@(#)srvrsmtp.c	8.40 (Berkeley) 06/17/94 (with SMTP)";
1433731Sbostic #else
15*67417Seric static char sccsid[] = "@(#)srvrsmtp.c	8.40 (Berkeley) 06/17/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();
9466325Seric extern char	RealUserName[];
9563937Seric 
9666325Seric 
9766283Seric #define MAXBADCOMMANDS	25		/* maximum number of bad commands */
9866283Seric 
9955012Seric smtp(e)
10055012Seric 	register ENVELOPE *e;
1014549Seric {
1024549Seric 	register char *p;
1038544Seric 	register struct cmd *c;
1044549Seric 	char *cmd;
1055003Seric 	auto ADDRESS *vrfyqueue;
10612612Seric 	ADDRESS *a;
10758109Seric 	bool gotmail;			/* mail command received */
10858092Seric 	bool gothello;			/* helo command received */
10958092Seric 	bool vrfy;			/* set if this is a vrfy command */
11058323Seric 	char *protocol;			/* sending protocol */
11159016Seric 	char *sendinghost;		/* sending hostname */
11266772Seric 	unsigned long msize;		/* approximate maximum message size */
11366005Seric 	char *peerhostname;		/* name of SMTP peer or "localhost" */
11458333Seric 	auto char *delimptr;
11558714Seric 	char *id;
11659747Seric 	int nrcpts;			/* number of RCPT commands */
11763787Seric 	bool doublequeue;
11866283Seric 	int badcommands = 0;		/* count of bad commands */
1198544Seric 	char inp[MAXLINE];
12057232Seric 	char cmdbuf[MAXLINE];
1217124Seric 	extern char Version[];
12224943Seric 	extern ENVELOPE BlankEnvelope;
1234549Seric 
12459066Seric 	if (fileno(OutChannel) != fileno(stdout))
1257363Seric 	{
1267363Seric 		/* arrange for debugging output to go to remote host */
12759066Seric 		(void) dup2(fileno(OutChannel), fileno(stdout));
1287363Seric 	}
12955012Seric 	settime(e);
13066005Seric 	peerhostname = RealHostName;
13166005Seric 	if (peerhostname == NULL)
13266005Seric 		peerhostname = "localhost";
13366005Seric 	CurHostName = peerhostname;
13465017Seric 	CurSmtpClient = macvalue('_', e);
13565017Seric 	if (CurSmtpClient == NULL)
13666003Seric 		CurSmtpClient = CurHostName;
13765017Seric 
13865017Seric 	setproctitle("server %s startup", CurSmtpClient);
13958050Seric 	expand("\201e", inp, &inp[sizeof inp], e);
14064496Seric 	if (BrokenSmtpPeers)
14164496Seric 	{
14266762Seric 		p = strchr(inp, '\n');
14366762Seric 		if (p != NULL)
14466762Seric 			*p = '\0';
14564496Seric 		message("220 %s", inp);
14664496Seric 	}
14764496Seric 	else
14864496Seric 	{
14966745Seric 		char *q = inp;
15066745Seric 
15166745Seric 		while (q != NULL)
15266745Seric 		{
15366762Seric 			p = strchr(q, '\n');
15466762Seric 			if (p != NULL)
15566762Seric 				*p++ = '\0';
15666745Seric 			message("220-%s", q);
15766745Seric 			q = p;
15866745Seric 		}
15964496Seric 		message("220 ESMTP spoken here");
16064496Seric 	}
16158330Seric 	protocol = NULL;
16259016Seric 	sendinghost = macvalue('s', e);
16358082Seric 	gothello = FALSE;
16458330Seric 	gotmail = FALSE;
1654549Seric 	for (;;)
1664549Seric 	{
16712612Seric 		/* arrange for backout */
16865751Seric 		if (setjmp(TopFrame) > 0)
16959058Seric 		{
17065751Seric 			/* if() nesting is necessary for Cray UNICOS */
17165751Seric 			if (InChild)
17265751Seric 			{
17365751Seric 				QuickAbort = FALSE;
17465751Seric 				SuprErrs = TRUE;
17565751Seric 				finis();
17665751Seric 			}
17759058Seric 		}
17812612Seric 		QuickAbort = FALSE;
17912612Seric 		HoldErrs = FALSE;
18051951Seric 		LogUsrErrs = FALSE;
18163843Seric 		e->e_flags &= ~(EF_VRFYONLY|EF_GLOBALERRS);
18212612Seric 
1837356Seric 		/* setup for the read */
18455012Seric 		e->e_to = NULL;
1854577Seric 		Errors = 0;
1867275Seric 		(void) fflush(stdout);
1877356Seric 
1887356Seric 		/* read the input line */
18961093Seric 		SmtpPhase = "server cmd read";
19061093Seric 		setproctitle("server %s cmd read", CurHostName);
19161093Seric 		p = sfgets(inp, sizeof inp, InChannel, TimeOuts.to_nextcommand,
19261093Seric 				SmtpPhase);
1937356Seric 
1947685Seric 		/* handle errors */
1957356Seric 		if (p == NULL)
1967356Seric 		{
1974549Seric 			/* end of file, just die */
19866017Seric 			disconnect(1, e);
19958151Seric 			message("421 %s Lost input channel from %s",
20065017Seric 				MyHostName, CurSmtpClient);
20155464Seric #ifdef LOG
20263843Seric 			if (LogLevel > (gotmail ? 1 : 19))
20355464Seric 				syslog(LOG_NOTICE, "lost input channel from %s",
20465017Seric 					CurSmtpClient);
20555464Seric #endif
20658069Seric 			if (InChild)
20758069Seric 				ExitStat = EX_QUIT;
2084549Seric 			finis();
2094549Seric 		}
2104549Seric 
2114549Seric 		/* clean up end of line */
2124558Seric 		fixcrlf(inp, TRUE);
2134549Seric 
2144713Seric 		/* echo command to transcript */
21555012Seric 		if (e->e_xfp != NULL)
21655012Seric 			fprintf(e->e_xfp, "<<< %s\n", inp);
2174713Seric 
21859060Seric 		if (e->e_id == NULL)
21965058Seric 			setproctitle("%s: %.80s", CurSmtpClient, inp);
22059060Seric 		else
22165058Seric 			setproctitle("%s %s: %.80s", e->e_id, CurSmtpClient, inp);
22259060Seric 
2234549Seric 		/* break off command */
22458050Seric 		for (p = inp; isascii(*p) && isspace(*p); p++)
2254549Seric 			continue;
22657232Seric 		cmd = cmdbuf;
22758050Seric 		while (*p != '\0' &&
22858050Seric 		       !(isascii(*p) && isspace(*p)) &&
22958050Seric 		       cmd < &cmdbuf[sizeof cmdbuf - 2])
23024981Seric 			*cmd++ = *p++;
23124981Seric 		*cmd = '\0';
2324549Seric 
23325691Seric 		/* throw away leading whitespace */
23458050Seric 		while (isascii(*p) && isspace(*p))
23525691Seric 			p++;
23625691Seric 
2374549Seric 		/* decode command */
2384549Seric 		for (c = CmdTab; c->cmdname != NULL; c++)
2394549Seric 		{
24033725Sbostic 			if (!strcasecmp(c->cmdname, cmdbuf))
2414549Seric 				break;
2424549Seric 		}
2434549Seric 
24451954Seric 		/* reset errors */
24551954Seric 		errno = 0;
24651954Seric 
2474549Seric 		/* process command */
2484549Seric 		switch (c->cmdcode)
2494549Seric 		{
2504976Seric 		  case CMDHELO:		/* hello -- introduce yourself */
25158323Seric 		  case CMDEHLO:		/* extended hello */
25258323Seric 			if (c->cmdcode == CMDEHLO)
25358323Seric 			{
25458323Seric 				protocol = "ESMTP";
25561093Seric 				SmtpPhase = "server EHLO";
25658323Seric 			}
25758323Seric 			else
25858323Seric 			{
25958323Seric 				protocol = "SMTP";
26061093Seric 				SmtpPhase = "server HELO";
26158323Seric 			}
26259016Seric 			sendinghost = newstr(p);
26360210Seric 			gothello = TRUE;
26460210Seric 			if (c->cmdcode != CMDEHLO)
26560239Seric 			{
26660239Seric 				/* print old message and be done with it */
26760239Seric 				message("250 %s Hello %s, pleased to meet you",
26865017Seric 					MyHostName, CurSmtpClient);
26960210Seric 				break;
27060239Seric 			}
27160239Seric 
27260239Seric 			/* print extended message and brag */
27360239Seric 			message("250-%s Hello %s, pleased to meet you",
27466760Seric 				MyHostName, CurSmtpClient);
27558323Seric 			if (!bitset(PRIV_NOEXPN, PrivacyFlags))
27658323Seric 				message("250-EXPN");
277*67417Seric #ifdef ADVERTISE_MIME
278*67417Seric 			message("250-8BITMIME");
279*67417Seric #endif
28064359Seric 			if (MaxMessageSize > 0)
28164359Seric 				message("250-SIZE %ld", MaxMessageSize);
28259271Seric 			else
28359271Seric 				message("250-SIZE");
28458323Seric 			message("250 HELP");
2854976Seric 			break;
2864976Seric 
2874549Seric 		  case CMDMAIL:		/* mail -- designate sender */
28861093Seric 			SmtpPhase = "server MAIL";
28924943Seric 
2909314Seric 			/* check for validity of this command */
29158789Seric 			if (!gothello)
29258082Seric 			{
29358957Seric 				/* set sending host to our known value */
29459016Seric 				if (sendinghost == NULL)
29566005Seric 					sendinghost = peerhostname;
29658957Seric 
29758789Seric 				if (bitset(PRIV_NEEDMAILHELO, PrivacyFlags))
29858821Seric 				{
29958789Seric 					message("503 Polite people say HELO first");
30058821Seric 					break;
30158821Seric 				}
30258082Seric 			}
30358109Seric 			if (gotmail)
3044558Seric 			{
30558151Seric 				message("503 Sender already specified");
30663843Seric 				if (InChild)
30763843Seric 					finis();
3084558Seric 				break;
3094558Seric 			}
3109339Seric 			if (InChild)
3119339Seric 			{
31236230Skarels 				errno = 0;
31358151Seric 				syserr("503 Nested MAIL command: MAIL %s", p);
31458069Seric 				finis();
3159339Seric 			}
3169339Seric 
3179339Seric 			/* fork a subprocess to process this command */
31855012Seric 			if (runinchild("SMTP-MAIL", e) > 0)
3199339Seric 				break;
32063753Seric 			if (!gothello)
32163753Seric 			{
32263753Seric 				auth_warning(e,
32366005Seric 					"Host %s didn't use HELO protocol",
32466005Seric 					peerhostname);
32563753Seric 			}
32665947Seric #ifdef PICKY_HELO_CHECK
32766005Seric 			if (strcasecmp(sendinghost, peerhostname) != 0 &&
32866005Seric 			    (strcasecmp(peerhostname, "localhost") != 0 ||
32965823Seric 			     strcasecmp(sendinghost, MyHostName) != 0))
33065823Seric 			{
33165823Seric 				auth_warning(e, "Host %s claimed to be %s",
33266005Seric 					peerhostname, sendinghost);
33365823Seric 			}
33465947Seric #endif
33565823Seric 
33658323Seric 			if (protocol == NULL)
33758323Seric 				protocol = "SMTP";
33858323Seric 			define('r', protocol, e);
33959016Seric 			define('s', sendinghost, e);
34055012Seric 			initsys(e);
34159747Seric 			nrcpts = 0;
34265089Seric 			e->e_flags |= EF_LOGSENDER;
34365058Seric 			setproctitle("%s %s: %.80s", e->e_id, CurSmtpClient, inp);
3449339Seric 
3459339Seric 			/* child -- go do the processing */
3464549Seric 			p = skipword(p, "from");
3474549Seric 			if (p == NULL)
3484549Seric 				break;
34957977Seric 			if (setjmp(TopFrame) > 0)
35058147Seric 			{
35158147Seric 				/* this failed -- undo work */
35258147Seric 				if (InChild)
35359058Seric 				{
35459058Seric 					QuickAbort = FALSE;
35559058Seric 					SuprErrs = TRUE;
35663787Seric 					e->e_flags &= ~EF_FATALERRS;
35758147Seric 					finis();
35859058Seric 				}
35957977Seric 				break;
36058147Seric 			}
36157977Seric 			QuickAbort = TRUE;
36258333Seric 
36358333Seric 			/* must parse sender first */
36458333Seric 			delimptr = NULL;
36558704Seric 			setsender(p, e, &delimptr, FALSE);
36658333Seric 			p = delimptr;
36758333Seric 			if (p != NULL && *p != '\0')
36858333Seric 				*p++ = '\0';
36958333Seric 
37066325Seric 			/* check for possible spoofing */
37166325Seric 			if (RealUid != 0 && OpMode == MD_SMTP &&
37266325Seric 			    (e->e_from.q_mailer != LocalMailer &&
37366325Seric 			     strcmp(e->e_from.q_user, RealUserName) != 0))
37466325Seric 			{
37566325Seric 				auth_warning(e, "%s owned process doing -bs",
37666325Seric 					RealUserName);
37766325Seric 			}
37866325Seric 
37958333Seric 			/* now parse ESMTP arguments */
38058333Seric 			msize = 0;
38166764Seric 			while (p != NULL && *p != '\0')
38258333Seric 			{
38358333Seric 				char *kp;
38466304Seric 				char *vp = NULL;
38558333Seric 
38658333Seric 				/* locate the beginning of the keyword */
38758333Seric 				while (isascii(*p) && isspace(*p))
38858333Seric 					p++;
38958333Seric 				if (*p == '\0')
39058333Seric 					break;
39158333Seric 				kp = p;
39258333Seric 
39358333Seric 				/* skip to the value portion */
39458333Seric 				while (isascii(*p) && isalnum(*p) || *p == '-')
39558333Seric 					p++;
39658333Seric 				if (*p == '=')
39758333Seric 				{
39858333Seric 					*p++ = '\0';
39958333Seric 					vp = p;
40058333Seric 
40158333Seric 					/* skip to the end of the value */
40258333Seric 					while (*p != '\0' && *p != ' ' &&
40358333Seric 					       !(isascii(*p) && iscntrl(*p)) &&
40458333Seric 					       *p != '=')
40558333Seric 						p++;
40658333Seric 				}
40758333Seric 
40858333Seric 				if (*p != '\0')
40958333Seric 					*p++ = '\0';
41058333Seric 
41158333Seric 				if (tTd(19, 1))
41266764Seric 					printf("MAIL: got arg %s=\"%s\"\n", kp,
41358333Seric 						vp == NULL ? "<null>" : vp);
41458333Seric 
41558333Seric 				if (strcasecmp(kp, "size") == 0)
41658333Seric 				{
41759093Seric 					if (vp == NULL)
41858333Seric 					{
41958333Seric 						usrerr("501 SIZE requires a value");
42058333Seric 						/* NOTREACHED */
42158333Seric 					}
42266772Seric # ifdef __STDC__
42366772Seric 					msize = strtoul(vp, (char **) NULL, 10);
42466772Seric # else
42566772Seric 					msize = strtol(vp, (char **) NULL, 10);
42666772Seric # endif
42758333Seric 				}
42859093Seric 				else if (strcasecmp(kp, "body") == 0)
42959093Seric 				{
43059093Seric 					if (vp == NULL)
43159093Seric 					{
43259093Seric 						usrerr("501 BODY requires a value");
43359093Seric 						/* NOTREACHED */
43459093Seric 					}
435*67417Seric 					e->e_bodytype = newstr(vp);
43659093Seric 					if (strcasecmp(vp, "8bitmime") == 0)
43759093Seric 					{
43859709Seric 						SevenBit = FALSE;
43959093Seric 					}
44059093Seric 					else if (strcasecmp(vp, "7bit") == 0)
44159093Seric 					{
44259709Seric 						SevenBit = TRUE;
44359093Seric 					}
44459093Seric 					else
44559093Seric 					{
44659093Seric 						usrerr("501 Unknown BODY type %s",
44759093Seric 							vp);
448*67417Seric 						/* NOTREACHED */
44959093Seric 					}
45059093Seric 				}
45158333Seric 				else
45258333Seric 				{
45358333Seric 					usrerr("501 %s parameter unrecognized", kp);
45458333Seric 					/* NOTREACHED */
45558333Seric 				}
45658333Seric 			}
45759284Seric 
45859284Seric 			if (MaxMessageSize > 0 && msize > MaxMessageSize)
45959284Seric 			{
46059284Seric 				usrerr("552 Message size exceeds fixed maximum message size (%ld)",
46159284Seric 					MaxMessageSize);
46259284Seric 				/* NOTREACHED */
46359284Seric 			}
46458333Seric 
46558333Seric 			if (!enoughspace(msize))
46658333Seric 			{
46758333Seric 				message("452 Insufficient disk space; try again later");
46858333Seric 				break;
46958333Seric 			}
47058151Seric 			message("250 Sender ok");
47158147Seric 			gotmail = TRUE;
4724549Seric 			break;
4734549Seric 
4744976Seric 		  case CMDRCPT:		/* rcpt -- designate recipient */
47558850Seric 			if (!gotmail)
47658850Seric 			{
47758850Seric 				usrerr("503 Need MAIL before RCPT");
47858850Seric 				break;
47958850Seric 			}
48061093Seric 			SmtpPhase = "server RCPT";
48112612Seric 			if (setjmp(TopFrame) > 0)
48214785Seric 			{
48355012Seric 				e->e_flags &= ~EF_FATALERRS;
48412612Seric 				break;
48514785Seric 			}
48612612Seric 			QuickAbort = TRUE;
48751951Seric 			LogUsrErrs = TRUE;
48858093Seric 
48959699Seric 			if (e->e_sendmode != SM_DELIVER)
49059699Seric 				e->e_flags |= EF_VRFYONLY;
49158919Seric 
4924549Seric 			p = skipword(p, "to");
4934549Seric 			if (p == NULL)
4944549Seric 				break;
49564284Seric 			a = parseaddr(p, NULLADDR, RF_COPYALL, ' ', NULL, e);
49612612Seric 			if (a == NULL)
49712612Seric 				break;
49816886Seric 			a->q_flags |= QPRIMARY;
49955012Seric 			a = recipient(a, &e->e_sendqueue, e);
50012612Seric 			if (Errors != 0)
50112612Seric 				break;
50212612Seric 
50312612Seric 			/* no errors during parsing, but might be a duplicate */
50455012Seric 			e->e_to = p;
50512612Seric 			if (!bitset(QBADADDR, a->q_flags))
50659747Seric 			{
50764718Seric 				message("250 Recipient ok%s",
50864718Seric 					bitset(QQUEUEUP, a->q_flags) ?
50964718Seric 						" (will queue)" : "");
51059747Seric 				nrcpts++;
51159747Seric 			}
51212612Seric 			else
5134549Seric 			{
51412612Seric 				/* punt -- should keep message in ADDRESS.... */
51558151Seric 				message("550 Addressee unknown");
5164549Seric 			}
51755012Seric 			e->e_to = NULL;
5184549Seric 			break;
5194549Seric 
5204549Seric 		  case CMDDATA:		/* data -- text of mail */
52161093Seric 			SmtpPhase = "server DATA";
52258109Seric 			if (!gotmail)
5234549Seric 			{
52458151Seric 				message("503 Need MAIL command");
5254976Seric 				break;
5264549Seric 			}
52764718Seric 			else if (nrcpts <= 0)
5284549Seric 			{
52958151Seric 				message("503 Need RCPT (recipient)");
5304976Seric 				break;
5314549Seric 			}
5324976Seric 
53358929Seric 			/* check to see if we need to re-expand aliases */
53463787Seric 			/* also reset QBADADDR on already-diagnosted addrs */
53563787Seric 			doublequeue = FALSE;
53658929Seric 			for (a = e->e_sendqueue; a != NULL; a = a->q_next)
53758929Seric 			{
53858929Seric 				if (bitset(QVERIFIED, a->q_flags))
53963787Seric 				{
54063787Seric 					/* need to re-expand aliases */
54163787Seric 					doublequeue = TRUE;
54263787Seric 				}
54363787Seric 				if (bitset(QBADADDR, a->q_flags))
54463787Seric 				{
54563787Seric 					/* make this "go away" */
54663787Seric 					a->q_flags |= QDONTSEND;
54763787Seric 					a->q_flags &= ~QBADADDR;
54863787Seric 				}
54958929Seric 			}
55058929Seric 
5514976Seric 			/* collect the text of the message */
55224943Seric 			SmtpPhase = "collect";
55364766Seric 			collect(TRUE, doublequeue, e);
55464766Seric 			if (Errors != 0)
55564766Seric 				goto abortmessage;
55667131Seric 
55767131Seric 			/* from now on, we have to operate silently */
55863965Seric 			HoldErrs = TRUE;
55967131Seric 			e->e_errormode = EM_MAIL;
5604976Seric 
5618238Seric 			/*
5628238Seric 			**  Arrange to send to everyone.
5638238Seric 			**	If sending to multiple people, mail back
5648238Seric 			**		errors rather than reporting directly.
5658238Seric 			**	In any case, don't mail back errors for
5668238Seric 			**		anything that has happened up to
5678238Seric 			**		now (the other end will do this).
56810197Seric 			**	Truncate our transcript -- the mail has gotten
56910197Seric 			**		to us successfully, and if we have
57010197Seric 			**		to mail this back, it will be easier
57110197Seric 			**		on the reader.
5728238Seric 			**	Then send to everyone.
5738238Seric 			**	Finally give a reply code.  If an error has
5748238Seric 			**		already been given, don't mail a
5758238Seric 			**		message back.
5769339Seric 			**	We goose error returns by clearing error bit.
5778238Seric 			*/
5788238Seric 
57924943Seric 			SmtpPhase = "delivery";
58055012Seric 			e->e_xfp = freopen(queuename(e, 'x'), "w", e->e_xfp);
58158714Seric 			id = e->e_id;
5824976Seric 
58367131Seric 			if (doublequeue)
58459730Seric 			{
58567131Seric 				/* make sure it is in the queue */
58667131Seric 				queueup(e, TRUE, FALSE);
58759730Seric 			}
5888238Seric 			else
58958919Seric 			{
59067131Seric 				/* send to all recipients */
59167131Seric 				sendall(e, SM_DEFAULT);
59267131Seric 			}
59367131Seric 			e->e_to = NULL;
59459747Seric 
59567131Seric 			/* issue success message */
59667131Seric 			message("250 %s Message accepted for delivery", id);
59764296Seric 
59867131Seric 			/* if we just queued, poke it */
59967131Seric 			if (doublequeue && e->e_sendmode != SM_QUEUE)
60067131Seric 			{
60167131Seric 				extern pid_t dowork();
60267131Seric 
60367131Seric 				unlockqueue(e);
60467131Seric 				(void) dowork(id, TRUE, TRUE, e);
60558919Seric 			}
60658883Seric 
60759747Seric   abortmessage:
6089339Seric 			/* if in a child, pop back to our parent */
6099339Seric 			if (InChild)
6109339Seric 				finis();
61124943Seric 
61224943Seric 			/* clean up a bit */
61358109Seric 			gotmail = FALSE;
61455012Seric 			dropenvelope(e);
61558179Seric 			CurEnv = e = newenvelope(e, CurEnv);
61655012Seric 			e->e_flags = BlankEnvelope.e_flags;
6174549Seric 			break;
6184549Seric 
6194549Seric 		  case CMDRSET:		/* rset -- reset state */
62058151Seric 			message("250 Reset state");
62164359Seric 			e->e_flags |= EF_CLRQUEUE;
6229339Seric 			if (InChild)
6239339Seric 				finis();
62458109Seric 
62558109Seric 			/* clean up a bit */
62658109Seric 			gotmail = FALSE;
62758109Seric 			dropenvelope(e);
62858179Seric 			CurEnv = e = newenvelope(e, CurEnv);
6299339Seric 			break;
6304549Seric 
6314549Seric 		  case CMDVRFY:		/* vrfy -- verify address */
63258092Seric 		  case CMDEXPN:		/* expn -- expand address */
63358092Seric 			vrfy = c->cmdcode == CMDVRFY;
63458092Seric 			if (bitset(vrfy ? PRIV_NOVRFY : PRIV_NOEXPN,
63558092Seric 						PrivacyFlags))
63658082Seric 			{
63758412Seric 				if (vrfy)
63867160Seric 					message("252 Cannot VRFY user; try RCPT to attempt delivery (or try finger)");
63958412Seric 				else
64065192Seric 					message("502 Sorry, we do not allow this operation");
64165017Seric #ifdef LOG
64265017Seric 				if (LogLevel > 5)
64365017Seric 					syslog(LOG_INFO, "%s: %s [rejected]",
64465017Seric 						CurSmtpClient, inp);
64565017Seric #endif
64658082Seric 				break;
64758082Seric 			}
64858082Seric 			else if (!gothello &&
64958092Seric 				 bitset(vrfy ? PRIV_NEEDVRFYHELO : PRIV_NEEDEXPNHELO,
65058092Seric 						PrivacyFlags))
65158082Seric 			{
65258151Seric 				message("503 I demand that you introduce yourself first");
65358082Seric 				break;
65458082Seric 			}
65558092Seric 			if (runinchild(vrfy ? "SMTP-VRFY" : "SMTP-EXPN", e) > 0)
6569339Seric 				break;
65755173Seric #ifdef LOG
65858020Seric 			if (LogLevel > 5)
65965017Seric 				syslog(LOG_INFO, "%s: %s", CurSmtpClient, inp);
66055173Seric #endif
6615003Seric 			vrfyqueue = NULL;
6627762Seric 			QuickAbort = TRUE;
66358092Seric 			if (vrfy)
66458092Seric 				e->e_flags |= EF_VRFYONLY;
66562373Seric 			while (*p != '\0' && isascii(*p) && isspace(*p))
66662373Seric 				*p++;
66762373Seric 			if (*p == '\0')
66862373Seric 			{
66962373Seric 				message("501 Argument required");
67062373Seric 				Errors++;
67162373Seric 			}
67262373Seric 			else
67362373Seric 			{
67464284Seric 				(void) sendtolist(p, NULLADDR, &vrfyqueue, e);
67562373Seric 			}
6767762Seric 			if (Errors != 0)
6779339Seric 			{
6789339Seric 				if (InChild)
6799339Seric 					finis();
6807762Seric 				break;
6819339Seric 			}
68262373Seric 			if (vrfyqueue == NULL)
68362373Seric 			{
68462373Seric 				message("554 Nothing to %s", vrfy ? "VRFY" : "EXPN");
68562373Seric 			}
6865003Seric 			while (vrfyqueue != NULL)
6875003Seric 			{
68863971Seric 				a = vrfyqueue;
68963971Seric 				while ((a = a->q_next) != NULL &&
69063971Seric 				       bitset(QDONTSEND|QBADADDR, a->q_flags))
69163971Seric 					continue;
6927685Seric 				if (!bitset(QDONTSEND|QBADADDR, vrfyqueue->q_flags))
69358151Seric 					printvrfyaddr(vrfyqueue, a == NULL);
69463847Seric 				vrfyqueue = vrfyqueue->q_next;
6955003Seric 			}
6969339Seric 			if (InChild)
6979339Seric 				finis();
6984549Seric 			break;
6994549Seric 
7004549Seric 		  case CMDHELP:		/* help -- give user info */
7014577Seric 			help(p);
7024549Seric 			break;
7034549Seric 
7044549Seric 		  case CMDNOOP:		/* noop -- do nothing */
70564122Seric 			message("250 OK");
7064549Seric 			break;
7074549Seric 
7084549Seric 		  case CMDQUIT:		/* quit -- leave mail */
70958151Seric 			message("221 %s closing connection", MyHostName);
71061051Seric 
71166283Seric doquit:
71261051Seric 			/* avoid future 050 messages */
71366017Seric 			disconnect(1, e);
71461051Seric 
7159339Seric 			if (InChild)
7169339Seric 				ExitStat = EX_QUIT;
7174549Seric 			finis();
7184549Seric 
7198544Seric 		  case CMDVERB:		/* set verbose mode */
72059957Seric 			if (bitset(PRIV_NOEXPN, PrivacyFlags))
72159957Seric 			{
72259957Seric 				/* this would give out the same info */
72359957Seric 				message("502 Verbose unavailable");
72459957Seric 				break;
72559957Seric 			}
7268544Seric 			Verbose = TRUE;
72758734Seric 			e->e_sendmode = SM_DELIVER;
72859957Seric 			message("250 Verbose mode");
7298544Seric 			break;
7308544Seric 
7319314Seric 		  case CMDONEX:		/* doing one transaction only */
7329378Seric 			OneXact = TRUE;
73359957Seric 			message("250 Only one transaction");
7349314Seric 			break;
7359314Seric 
73636230Skarels # ifdef SMTPDEBUG
7379339Seric 		  case CMDDBGQSHOW:	/* show queues */
7386907Seric 			printf("Send Queue=");
73955012Seric 			printaddr(e->e_sendqueue, TRUE);
7405003Seric 			break;
7417275Seric 
7427275Seric 		  case CMDDBGDEBUG:	/* set debug mode */
7437676Seric 			tTsetup(tTdvect, sizeof tTdvect, "0-99.1");
7447676Seric 			tTflag(p);
74558151Seric 			message("200 Debug set");
7467275Seric 			break;
7477275Seric 
74836230Skarels # else /* not SMTPDEBUG */
74936230Skarels 		  case CMDDBGQSHOW:	/* show queues */
75036230Skarels 		  case CMDDBGDEBUG:	/* set debug mode */
75164685Seric # endif /* SMTPDEBUG */
75264685Seric 		  case CMDLOGBOGUS:	/* bogus command */
75336233Skarels # ifdef LOG
75458308Seric 			if (LogLevel > 0)
75564685Seric 				syslog(LOG_CRIT,
75658020Seric 				    "\"%s\" command from %s (%s)",
75766005Seric 				    c->cmdname, peerhostname,
75858755Seric 				    anynet_ntoa(&RealHostAddr));
75936233Skarels # endif
76036230Skarels 			/* FALL THROUGH */
76136230Skarels 
7624549Seric 		  case CMDERROR:	/* unknown command */
76366283Seric 			if (++badcommands > MAXBADCOMMANDS)
76466283Seric 			{
76566283Seric 				message("421 %s Too many bad commands; closing connection",
76666283Seric 					MyHostName);
76766283Seric 				goto doquit;
76866283Seric 			}
76966283Seric 
77058151Seric 			message("500 Command unrecognized");
7714549Seric 			break;
7724549Seric 
7734549Seric 		  default:
77436230Skarels 			errno = 0;
77558151Seric 			syserr("500 smtp: unknown code %d", c->cmdcode);
7764549Seric 			break;
7774549Seric 		}
7784549Seric 	}
7794549Seric }
7804549Seric /*
7814549Seric **  SKIPWORD -- skip a fixed word.
7824549Seric **
7834549Seric **	Parameters:
7844549Seric **		p -- place to start looking.
7854549Seric **		w -- word to skip.
7864549Seric **
7874549Seric **	Returns:
7884549Seric **		p following w.
7894549Seric **		NULL on error.
7904549Seric **
7914549Seric **	Side Effects:
7924549Seric **		clobbers the p data area.
7934549Seric */
7944549Seric 
7954549Seric static char *
7964549Seric skipword(p, w)
7974549Seric 	register char *p;
7984549Seric 	char *w;
7994549Seric {
8004549Seric 	register char *q;
80166005Seric 	char *firstp = p;
8024549Seric 
8034549Seric 	/* find beginning of word */
80458050Seric 	while (isascii(*p) && isspace(*p))
8054549Seric 		p++;
8064549Seric 	q = p;
8074549Seric 
8084549Seric 	/* find end of word */
80958050Seric 	while (*p != '\0' && *p != ':' && !(isascii(*p) && isspace(*p)))
8104549Seric 		p++;
81158050Seric 	while (isascii(*p) && isspace(*p))
8124549Seric 		*p++ = '\0';
8134549Seric 	if (*p != ':')
8144549Seric 	{
8154549Seric 	  syntax:
81666005Seric 		message("501 Syntax error in parameters scanning \"%s\"",
81766005Seric 			firstp);
8184549Seric 		Errors++;
8194549Seric 		return (NULL);
8204549Seric 	}
8214549Seric 	*p++ = '\0';
82258050Seric 	while (isascii(*p) && isspace(*p))
8234549Seric 		p++;
8244549Seric 
82562373Seric 	if (*p == '\0')
82662373Seric 		goto syntax;
82762373Seric 
8284549Seric 	/* see if the input word matches desired word */
82933725Sbostic 	if (strcasecmp(q, w))
8304549Seric 		goto syntax;
8314549Seric 
8324549Seric 	return (p);
8334549Seric }
8344577Seric /*
83558151Seric **  PRINTVRFYADDR -- print an entry in the verify queue
83658151Seric **
83758151Seric **	Parameters:
83858151Seric **		a -- the address to print
83958151Seric **		last -- set if this is the last one.
84058151Seric **
84158151Seric **	Returns:
84258151Seric **		none.
84358151Seric **
84458151Seric **	Side Effects:
84558151Seric **		Prints the appropriate 250 codes.
84658151Seric */
84758151Seric 
84858151Seric printvrfyaddr(a, last)
84958151Seric 	register ADDRESS *a;
85058151Seric 	bool last;
85158151Seric {
85258151Seric 	char fmtbuf[20];
85358151Seric 
85458151Seric 	strcpy(fmtbuf, "250");
85558151Seric 	fmtbuf[3] = last ? ' ' : '-';
85658151Seric 
85759746Seric 	if (a->q_fullname == NULL)
85859746Seric 	{
85959746Seric 		if (strchr(a->q_user, '@') == NULL)
86059746Seric 			strcpy(&fmtbuf[4], "<%s@%s>");
86159746Seric 		else
86259746Seric 			strcpy(&fmtbuf[4], "<%s>");
86359746Seric 		message(fmtbuf, a->q_user, MyHostName);
86459746Seric 	}
86558151Seric 	else
86658151Seric 	{
86759746Seric 		if (strchr(a->q_user, '@') == NULL)
86859746Seric 			strcpy(&fmtbuf[4], "%s <%s@%s>");
86959746Seric 		else
87059746Seric 			strcpy(&fmtbuf[4], "%s <%s>");
87159746Seric 		message(fmtbuf, a->q_fullname, a->q_user, MyHostName);
87258151Seric 	}
87358151Seric }
87458151Seric /*
8754577Seric **  HELP -- implement the HELP command.
8764577Seric **
8774577Seric **	Parameters:
8784577Seric **		topic -- the topic we want help for.
8794577Seric **
8804577Seric **	Returns:
8814577Seric **		none.
8824577Seric **
8834577Seric **	Side Effects:
8844577Seric **		outputs the help file to message output.
8854577Seric */
8864577Seric 
8874577Seric help(topic)
8884577Seric 	char *topic;
8894577Seric {
8904577Seric 	register FILE *hf;
8914577Seric 	int len;
8924577Seric 	char buf[MAXLINE];
8934577Seric 	bool noinfo;
8944577Seric 
8958269Seric 	if (HelpFile == NULL || (hf = fopen(HelpFile, "r")) == NULL)
8964577Seric 	{
8974577Seric 		/* no help */
89811931Seric 		errno = 0;
89958151Seric 		message("502 HELP not implemented");
9004577Seric 		return;
9014577Seric 	}
9024577Seric 
90349669Seric 	if (topic == NULL || *topic == '\0')
90449669Seric 		topic = "smtp";
90549669Seric 	else
90649669Seric 		makelower(topic);
90749669Seric 
9084577Seric 	len = strlen(topic);
9094577Seric 	noinfo = TRUE;
9104577Seric 
9114577Seric 	while (fgets(buf, sizeof buf, hf) != NULL)
9124577Seric 	{
9134577Seric 		if (strncmp(buf, topic, len) == 0)
9144577Seric 		{
9154577Seric 			register char *p;
9164577Seric 
91756795Seric 			p = strchr(buf, '\t');
9184577Seric 			if (p == NULL)
9194577Seric 				p = buf;
9204577Seric 			else
9214577Seric 				p++;
9224577Seric 			fixcrlf(p, TRUE);
92358151Seric 			message("214-%s", p);
9244577Seric 			noinfo = FALSE;
9254577Seric 		}
9264577Seric 	}
9274577Seric 
9284577Seric 	if (noinfo)
92958151Seric 		message("504 HELP topic unknown");
9304577Seric 	else
93158151Seric 		message("214 End of HELP info");
9324628Seric 	(void) fclose(hf);
9334577Seric }
9348544Seric /*
9359339Seric **  RUNINCHILD -- return twice -- once in the child, then in the parent again
9369339Seric **
9379339Seric **	Parameters:
9389339Seric **		label -- a string used in error messages
9399339Seric **
9409339Seric **	Returns:
9419339Seric **		zero in the child
9429339Seric **		one in the parent
9439339Seric **
9449339Seric **	Side Effects:
9459339Seric **		none.
9469339Seric */
9478544Seric 
94855012Seric runinchild(label, e)
9499339Seric 	char *label;
95055012Seric 	register ENVELOPE *e;
9519339Seric {
9529339Seric 	int childpid;
9539339Seric 
95416158Seric 	if (!OneXact)
9559339Seric 	{
95616158Seric 		childpid = dofork();
95716158Seric 		if (childpid < 0)
95816158Seric 		{
95916158Seric 			syserr("%s: cannot fork", label);
96016158Seric 			return (1);
96116158Seric 		}
96216158Seric 		if (childpid > 0)
96316158Seric 		{
96416158Seric 			auto int st;
9659339Seric 
96616158Seric 			/* parent -- wait for child to complete */
96761093Seric 			setproctitle("server %s child wait", CurHostName);
96816158Seric 			st = waitfor(childpid);
96916158Seric 			if (st == -1)
97016158Seric 				syserr("%s: lost child", label);
97164948Seric 			else if (!WIFEXITED(st))
97264948Seric 				syserr("%s: died on signal %d",
97364948Seric 					label, st & 0177);
9749339Seric 
97516158Seric 			/* if we exited on a QUIT command, complete the process */
97666017Seric 			if (WEXITSTATUS(st) == EX_QUIT)
97766017Seric 			{
97866017Seric 				disconnect(1, e);
97916158Seric 				finis();
98066017Seric 			}
9819339Seric 
98216158Seric 			return (1);
98316158Seric 		}
98416158Seric 		else
98516158Seric 		{
98616158Seric 			/* child */
98716158Seric 			InChild = TRUE;
98825050Seric 			QuickAbort = FALSE;
98955012Seric 			clearenvelope(e, FALSE);
99016158Seric 		}
9919339Seric 	}
99215256Seric 
99316158Seric 	/* open alias database */
99460537Seric 	initmaps(FALSE, e);
99516158Seric 
99616158Seric 	return (0);
9979339Seric }
9989339Seric 
99956795Seric # endif /* SMTP */
1000