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*67546Seric static char sccsid[] = "@(#)srvrsmtp.c	8.43 (Berkeley) 07/23/94 (with SMTP)";
1433731Sbostic #else
15*67546Seric static char sccsid[] = "@(#)srvrsmtp.c	8.43 (Berkeley) 07/23/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 			}
26267445Seric 
26367445Seric 			/* check for valid domain name (re 1123 5.2.5) */
26467445Seric 			if (*p == '\0')
26567445Seric 			{
26667445Seric 				message("501 %s requires domain address",
26767445Seric 					cmdbuf);
26867445Seric 				break;
26967445Seric 			}
27067445Seric 			else
27167445Seric 			{
27267445Seric 				register char *q;
27367445Seric 
27467445Seric 				for (q = p; *q != '\0'; q++)
27567445Seric 				{
27667445Seric 					if (!isascii(*q))
27767445Seric 						break;
27867445Seric 					if (isalnum(*q))
27967445Seric 						continue;
28067445Seric 					if (strchr("[].-_#", *q) == NULL)
28167445Seric 						break;
28267445Seric 				}
28367445Seric 				if (*q != '\0')
28467445Seric 				{
28567445Seric 					message("501 Invalid domain name");
28667445Seric 					break;
28767445Seric 				}
28867445Seric 			}
28967445Seric 
29059016Seric 			sendinghost = newstr(p);
29160210Seric 			gothello = TRUE;
29260210Seric 			if (c->cmdcode != CMDEHLO)
29360239Seric 			{
29460239Seric 				/* print old message and be done with it */
29560239Seric 				message("250 %s Hello %s, pleased to meet you",
29665017Seric 					MyHostName, CurSmtpClient);
29760210Seric 				break;
29860239Seric 			}
29960239Seric 
30060239Seric 			/* print extended message and brag */
30160239Seric 			message("250-%s Hello %s, pleased to meet you",
30266760Seric 				MyHostName, CurSmtpClient);
30358323Seric 			if (!bitset(PRIV_NOEXPN, PrivacyFlags))
30458323Seric 				message("250-EXPN");
30567417Seric #ifdef ADVERTISE_MIME
30667417Seric 			message("250-8BITMIME");
30767417Seric #endif
30864359Seric 			if (MaxMessageSize > 0)
30964359Seric 				message("250-SIZE %ld", MaxMessageSize);
31059271Seric 			else
31159271Seric 				message("250-SIZE");
31258323Seric 			message("250 HELP");
3134976Seric 			break;
3144976Seric 
3154549Seric 		  case CMDMAIL:		/* mail -- designate sender */
31661093Seric 			SmtpPhase = "server MAIL";
31724943Seric 
3189314Seric 			/* check for validity of this command */
31958789Seric 			if (!gothello)
32058082Seric 			{
32158957Seric 				/* set sending host to our known value */
32259016Seric 				if (sendinghost == NULL)
32366005Seric 					sendinghost = peerhostname;
32458957Seric 
32558789Seric 				if (bitset(PRIV_NEEDMAILHELO, PrivacyFlags))
32658821Seric 				{
32758789Seric 					message("503 Polite people say HELO first");
32858821Seric 					break;
32958821Seric 				}
33058082Seric 			}
33158109Seric 			if (gotmail)
3324558Seric 			{
33358151Seric 				message("503 Sender already specified");
33463843Seric 				if (InChild)
33563843Seric 					finis();
3364558Seric 				break;
3374558Seric 			}
3389339Seric 			if (InChild)
3399339Seric 			{
34036230Skarels 				errno = 0;
34158151Seric 				syserr("503 Nested MAIL command: MAIL %s", p);
34258069Seric 				finis();
3439339Seric 			}
3449339Seric 
3459339Seric 			/* fork a subprocess to process this command */
34655012Seric 			if (runinchild("SMTP-MAIL", e) > 0)
3479339Seric 				break;
34863753Seric 			if (!gothello)
34963753Seric 			{
35063753Seric 				auth_warning(e,
35166005Seric 					"Host %s didn't use HELO protocol",
35266005Seric 					peerhostname);
35363753Seric 			}
35465947Seric #ifdef PICKY_HELO_CHECK
35566005Seric 			if (strcasecmp(sendinghost, peerhostname) != 0 &&
35666005Seric 			    (strcasecmp(peerhostname, "localhost") != 0 ||
35765823Seric 			     strcasecmp(sendinghost, MyHostName) != 0))
35865823Seric 			{
35965823Seric 				auth_warning(e, "Host %s claimed to be %s",
36066005Seric 					peerhostname, sendinghost);
36165823Seric 			}
36265947Seric #endif
36365823Seric 
36458323Seric 			if (protocol == NULL)
36558323Seric 				protocol = "SMTP";
36658323Seric 			define('r', protocol, e);
36759016Seric 			define('s', sendinghost, e);
36855012Seric 			initsys(e);
36959747Seric 			nrcpts = 0;
37065089Seric 			e->e_flags |= EF_LOGSENDER;
37165058Seric 			setproctitle("%s %s: %.80s", e->e_id, CurSmtpClient, inp);
3729339Seric 
3739339Seric 			/* child -- go do the processing */
3744549Seric 			p = skipword(p, "from");
3754549Seric 			if (p == NULL)
3764549Seric 				break;
37757977Seric 			if (setjmp(TopFrame) > 0)
37858147Seric 			{
37958147Seric 				/* this failed -- undo work */
38058147Seric 				if (InChild)
38159058Seric 				{
38259058Seric 					QuickAbort = FALSE;
38359058Seric 					SuprErrs = TRUE;
38463787Seric 					e->e_flags &= ~EF_FATALERRS;
38558147Seric 					finis();
38659058Seric 				}
38757977Seric 				break;
38858147Seric 			}
38957977Seric 			QuickAbort = TRUE;
39058333Seric 
39158333Seric 			/* must parse sender first */
39258333Seric 			delimptr = NULL;
39358704Seric 			setsender(p, e, &delimptr, FALSE);
39458333Seric 			p = delimptr;
39558333Seric 			if (p != NULL && *p != '\0')
39658333Seric 				*p++ = '\0';
39758333Seric 
39866325Seric 			/* check for possible spoofing */
39966325Seric 			if (RealUid != 0 && OpMode == MD_SMTP &&
40067473Seric 			    !bitnset(M_LOCALMAILER, e->e_from.q_mailer->m_flags) &&
40167473Seric 			    strcmp(e->e_from.q_user, RealUserName) != 0)
40266325Seric 			{
40366325Seric 				auth_warning(e, "%s owned process doing -bs",
40466325Seric 					RealUserName);
40566325Seric 			}
40666325Seric 
40758333Seric 			/* now parse ESMTP arguments */
40858333Seric 			msize = 0;
40966764Seric 			while (p != NULL && *p != '\0')
41058333Seric 			{
41158333Seric 				char *kp;
41266304Seric 				char *vp = NULL;
41358333Seric 
41458333Seric 				/* locate the beginning of the keyword */
41558333Seric 				while (isascii(*p) && isspace(*p))
41658333Seric 					p++;
41758333Seric 				if (*p == '\0')
41858333Seric 					break;
41958333Seric 				kp = p;
42058333Seric 
42158333Seric 				/* skip to the value portion */
42258333Seric 				while (isascii(*p) && isalnum(*p) || *p == '-')
42358333Seric 					p++;
42458333Seric 				if (*p == '=')
42558333Seric 				{
42658333Seric 					*p++ = '\0';
42758333Seric 					vp = p;
42858333Seric 
42958333Seric 					/* skip to the end of the value */
43058333Seric 					while (*p != '\0' && *p != ' ' &&
43158333Seric 					       !(isascii(*p) && iscntrl(*p)) &&
43258333Seric 					       *p != '=')
43358333Seric 						p++;
43458333Seric 				}
43558333Seric 
43658333Seric 				if (*p != '\0')
43758333Seric 					*p++ = '\0';
43858333Seric 
43958333Seric 				if (tTd(19, 1))
44066764Seric 					printf("MAIL: got arg %s=\"%s\"\n", kp,
44158333Seric 						vp == NULL ? "<null>" : vp);
44258333Seric 
44358333Seric 				if (strcasecmp(kp, "size") == 0)
44458333Seric 				{
44559093Seric 					if (vp == NULL)
44658333Seric 					{
44758333Seric 						usrerr("501 SIZE requires a value");
44858333Seric 						/* NOTREACHED */
44958333Seric 					}
45066772Seric # ifdef __STDC__
45166772Seric 					msize = strtoul(vp, (char **) NULL, 10);
45266772Seric # else
45366772Seric 					msize = strtol(vp, (char **) NULL, 10);
45466772Seric # endif
45558333Seric 				}
45659093Seric 				else if (strcasecmp(kp, "body") == 0)
45759093Seric 				{
45859093Seric 					if (vp == NULL)
45959093Seric 					{
46059093Seric 						usrerr("501 BODY requires a value");
46159093Seric 						/* NOTREACHED */
46259093Seric 					}
46367417Seric 					e->e_bodytype = newstr(vp);
46459093Seric 					if (strcasecmp(vp, "8bitmime") == 0)
46559093Seric 					{
466*67546Seric 						SevenBitInput = FALSE;
46759093Seric 					}
46859093Seric 					else if (strcasecmp(vp, "7bit") == 0)
46959093Seric 					{
470*67546Seric 						SevenBitInput = TRUE;
47159093Seric 					}
47259093Seric 					else
47359093Seric 					{
47459093Seric 						usrerr("501 Unknown BODY type %s",
47559093Seric 							vp);
47667417Seric 						/* NOTREACHED */
47759093Seric 					}
47859093Seric 				}
47958333Seric 				else
48058333Seric 				{
48158333Seric 					usrerr("501 %s parameter unrecognized", kp);
48258333Seric 					/* NOTREACHED */
48358333Seric 				}
48458333Seric 			}
48559284Seric 
48659284Seric 			if (MaxMessageSize > 0 && msize > MaxMessageSize)
48759284Seric 			{
48859284Seric 				usrerr("552 Message size exceeds fixed maximum message size (%ld)",
48959284Seric 					MaxMessageSize);
49059284Seric 				/* NOTREACHED */
49159284Seric 			}
49258333Seric 
49358333Seric 			if (!enoughspace(msize))
49458333Seric 			{
49558333Seric 				message("452 Insufficient disk space; try again later");
49658333Seric 				break;
49758333Seric 			}
49858151Seric 			message("250 Sender ok");
49958147Seric 			gotmail = TRUE;
5004549Seric 			break;
5014549Seric 
5024976Seric 		  case CMDRCPT:		/* rcpt -- designate recipient */
50358850Seric 			if (!gotmail)
50458850Seric 			{
50558850Seric 				usrerr("503 Need MAIL before RCPT");
50658850Seric 				break;
50758850Seric 			}
50861093Seric 			SmtpPhase = "server RCPT";
50912612Seric 			if (setjmp(TopFrame) > 0)
51014785Seric 			{
51155012Seric 				e->e_flags &= ~EF_FATALERRS;
51212612Seric 				break;
51314785Seric 			}
51412612Seric 			QuickAbort = TRUE;
51551951Seric 			LogUsrErrs = TRUE;
51658093Seric 
51759699Seric 			if (e->e_sendmode != SM_DELIVER)
51859699Seric 				e->e_flags |= EF_VRFYONLY;
51958919Seric 
5204549Seric 			p = skipword(p, "to");
5214549Seric 			if (p == NULL)
5224549Seric 				break;
52364284Seric 			a = parseaddr(p, NULLADDR, RF_COPYALL, ' ', NULL, e);
52412612Seric 			if (a == NULL)
52512612Seric 				break;
52616886Seric 			a->q_flags |= QPRIMARY;
52755012Seric 			a = recipient(a, &e->e_sendqueue, e);
52812612Seric 			if (Errors != 0)
52912612Seric 				break;
53012612Seric 
53112612Seric 			/* no errors during parsing, but might be a duplicate */
53255012Seric 			e->e_to = p;
53312612Seric 			if (!bitset(QBADADDR, a->q_flags))
53459747Seric 			{
53564718Seric 				message("250 Recipient ok%s",
53664718Seric 					bitset(QQUEUEUP, a->q_flags) ?
53764718Seric 						" (will queue)" : "");
53859747Seric 				nrcpts++;
53959747Seric 			}
54012612Seric 			else
5414549Seric 			{
54212612Seric 				/* punt -- should keep message in ADDRESS.... */
54358151Seric 				message("550 Addressee unknown");
5444549Seric 			}
54555012Seric 			e->e_to = NULL;
5464549Seric 			break;
5474549Seric 
5484549Seric 		  case CMDDATA:		/* data -- text of mail */
54961093Seric 			SmtpPhase = "server DATA";
55058109Seric 			if (!gotmail)
5514549Seric 			{
55258151Seric 				message("503 Need MAIL command");
5534976Seric 				break;
5544549Seric 			}
55564718Seric 			else if (nrcpts <= 0)
5564549Seric 			{
55758151Seric 				message("503 Need RCPT (recipient)");
5584976Seric 				break;
5594549Seric 			}
5604976Seric 
56158929Seric 			/* check to see if we need to re-expand aliases */
56263787Seric 			/* also reset QBADADDR on already-diagnosted addrs */
56363787Seric 			doublequeue = FALSE;
56458929Seric 			for (a = e->e_sendqueue; a != NULL; a = a->q_next)
56558929Seric 			{
56658929Seric 				if (bitset(QVERIFIED, a->q_flags))
56763787Seric 				{
56863787Seric 					/* need to re-expand aliases */
56963787Seric 					doublequeue = TRUE;
57063787Seric 				}
57163787Seric 				if (bitset(QBADADDR, a->q_flags))
57263787Seric 				{
57363787Seric 					/* make this "go away" */
57463787Seric 					a->q_flags |= QDONTSEND;
57563787Seric 					a->q_flags &= ~QBADADDR;
57663787Seric 				}
57758929Seric 			}
57858929Seric 
5794976Seric 			/* collect the text of the message */
58024943Seric 			SmtpPhase = "collect";
581*67546Seric 			collect(InChannel, TRUE, doublequeue, NULL, e);
58264766Seric 			if (Errors != 0)
58364766Seric 				goto abortmessage;
58467131Seric 
58567131Seric 			/* from now on, we have to operate silently */
58663965Seric 			HoldErrs = TRUE;
58767131Seric 			e->e_errormode = EM_MAIL;
5884976Seric 
5898238Seric 			/*
5908238Seric 			**  Arrange to send to everyone.
5918238Seric 			**	If sending to multiple people, mail back
5928238Seric 			**		errors rather than reporting directly.
5938238Seric 			**	In any case, don't mail back errors for
5948238Seric 			**		anything that has happened up to
5958238Seric 			**		now (the other end will do this).
59610197Seric 			**	Truncate our transcript -- the mail has gotten
59710197Seric 			**		to us successfully, and if we have
59810197Seric 			**		to mail this back, it will be easier
59910197Seric 			**		on the reader.
6008238Seric 			**	Then send to everyone.
6018238Seric 			**	Finally give a reply code.  If an error has
6028238Seric 			**		already been given, don't mail a
6038238Seric 			**		message back.
6049339Seric 			**	We goose error returns by clearing error bit.
6058238Seric 			*/
6068238Seric 
60724943Seric 			SmtpPhase = "delivery";
60855012Seric 			e->e_xfp = freopen(queuename(e, 'x'), "w", e->e_xfp);
60958714Seric 			id = e->e_id;
6104976Seric 
61167131Seric 			if (doublequeue)
61259730Seric 			{
61367131Seric 				/* make sure it is in the queue */
61467131Seric 				queueup(e, TRUE, FALSE);
61559730Seric 			}
6168238Seric 			else
61758919Seric 			{
61867131Seric 				/* send to all recipients */
61967131Seric 				sendall(e, SM_DEFAULT);
62067131Seric 			}
62167131Seric 			e->e_to = NULL;
62259747Seric 
62367131Seric 			/* issue success message */
62467131Seric 			message("250 %s Message accepted for delivery", id);
62564296Seric 
62667131Seric 			/* if we just queued, poke it */
62767131Seric 			if (doublequeue && e->e_sendmode != SM_QUEUE)
62867131Seric 			{
62967131Seric 				extern pid_t dowork();
63067131Seric 
63167131Seric 				unlockqueue(e);
63267131Seric 				(void) dowork(id, TRUE, TRUE, e);
63358919Seric 			}
63458883Seric 
63559747Seric   abortmessage:
6369339Seric 			/* if in a child, pop back to our parent */
6379339Seric 			if (InChild)
6389339Seric 				finis();
63924943Seric 
64024943Seric 			/* clean up a bit */
64158109Seric 			gotmail = FALSE;
64255012Seric 			dropenvelope(e);
64358179Seric 			CurEnv = e = newenvelope(e, CurEnv);
64455012Seric 			e->e_flags = BlankEnvelope.e_flags;
6454549Seric 			break;
6464549Seric 
6474549Seric 		  case CMDRSET:		/* rset -- reset state */
64858151Seric 			message("250 Reset state");
64964359Seric 			e->e_flags |= EF_CLRQUEUE;
6509339Seric 			if (InChild)
6519339Seric 				finis();
65258109Seric 
65358109Seric 			/* clean up a bit */
65458109Seric 			gotmail = FALSE;
65558109Seric 			dropenvelope(e);
65658179Seric 			CurEnv = e = newenvelope(e, CurEnv);
6579339Seric 			break;
6584549Seric 
6594549Seric 		  case CMDVRFY:		/* vrfy -- verify address */
66058092Seric 		  case CMDEXPN:		/* expn -- expand address */
66158092Seric 			vrfy = c->cmdcode == CMDVRFY;
66258092Seric 			if (bitset(vrfy ? PRIV_NOVRFY : PRIV_NOEXPN,
66358092Seric 						PrivacyFlags))
66458082Seric 			{
66558412Seric 				if (vrfy)
66667160Seric 					message("252 Cannot VRFY user; try RCPT to attempt delivery (or try finger)");
66758412Seric 				else
66865192Seric 					message("502 Sorry, we do not allow this operation");
66965017Seric #ifdef LOG
67065017Seric 				if (LogLevel > 5)
67165017Seric 					syslog(LOG_INFO, "%s: %s [rejected]",
67265017Seric 						CurSmtpClient, inp);
67365017Seric #endif
67458082Seric 				break;
67558082Seric 			}
67658082Seric 			else if (!gothello &&
67758092Seric 				 bitset(vrfy ? PRIV_NEEDVRFYHELO : PRIV_NEEDEXPNHELO,
67858092Seric 						PrivacyFlags))
67958082Seric 			{
68058151Seric 				message("503 I demand that you introduce yourself first");
68158082Seric 				break;
68258082Seric 			}
68358092Seric 			if (runinchild(vrfy ? "SMTP-VRFY" : "SMTP-EXPN", e) > 0)
6849339Seric 				break;
68555173Seric #ifdef LOG
68658020Seric 			if (LogLevel > 5)
68765017Seric 				syslog(LOG_INFO, "%s: %s", CurSmtpClient, inp);
68855173Seric #endif
6895003Seric 			vrfyqueue = NULL;
6907762Seric 			QuickAbort = TRUE;
69158092Seric 			if (vrfy)
69258092Seric 				e->e_flags |= EF_VRFYONLY;
69362373Seric 			while (*p != '\0' && isascii(*p) && isspace(*p))
69462373Seric 				*p++;
69562373Seric 			if (*p == '\0')
69662373Seric 			{
69762373Seric 				message("501 Argument required");
69862373Seric 				Errors++;
69962373Seric 			}
70062373Seric 			else
70162373Seric 			{
70264284Seric 				(void) sendtolist(p, NULLADDR, &vrfyqueue, e);
70362373Seric 			}
7047762Seric 			if (Errors != 0)
7059339Seric 			{
7069339Seric 				if (InChild)
7079339Seric 					finis();
7087762Seric 				break;
7099339Seric 			}
71062373Seric 			if (vrfyqueue == NULL)
71162373Seric 			{
71262373Seric 				message("554 Nothing to %s", vrfy ? "VRFY" : "EXPN");
71362373Seric 			}
7145003Seric 			while (vrfyqueue != NULL)
7155003Seric 			{
71663971Seric 				a = vrfyqueue;
71763971Seric 				while ((a = a->q_next) != NULL &&
71863971Seric 				       bitset(QDONTSEND|QBADADDR, a->q_flags))
71963971Seric 					continue;
7207685Seric 				if (!bitset(QDONTSEND|QBADADDR, vrfyqueue->q_flags))
72158151Seric 					printvrfyaddr(vrfyqueue, a == NULL);
72263847Seric 				vrfyqueue = vrfyqueue->q_next;
7235003Seric 			}
7249339Seric 			if (InChild)
7259339Seric 				finis();
7264549Seric 			break;
7274549Seric 
7284549Seric 		  case CMDHELP:		/* help -- give user info */
7294577Seric 			help(p);
7304549Seric 			break;
7314549Seric 
7324549Seric 		  case CMDNOOP:		/* noop -- do nothing */
73364122Seric 			message("250 OK");
7344549Seric 			break;
7354549Seric 
7364549Seric 		  case CMDQUIT:		/* quit -- leave mail */
73758151Seric 			message("221 %s closing connection", MyHostName);
73861051Seric 
73966283Seric doquit:
74061051Seric 			/* avoid future 050 messages */
74166017Seric 			disconnect(1, e);
74261051Seric 
7439339Seric 			if (InChild)
7449339Seric 				ExitStat = EX_QUIT;
7454549Seric 			finis();
7464549Seric 
7478544Seric 		  case CMDVERB:		/* set verbose mode */
74859957Seric 			if (bitset(PRIV_NOEXPN, PrivacyFlags))
74959957Seric 			{
75059957Seric 				/* this would give out the same info */
75159957Seric 				message("502 Verbose unavailable");
75259957Seric 				break;
75359957Seric 			}
7548544Seric 			Verbose = TRUE;
75558734Seric 			e->e_sendmode = SM_DELIVER;
75659957Seric 			message("250 Verbose mode");
7578544Seric 			break;
7588544Seric 
7599314Seric 		  case CMDONEX:		/* doing one transaction only */
7609378Seric 			OneXact = TRUE;
76159957Seric 			message("250 Only one transaction");
7629314Seric 			break;
7639314Seric 
76436230Skarels # ifdef SMTPDEBUG
7659339Seric 		  case CMDDBGQSHOW:	/* show queues */
7666907Seric 			printf("Send Queue=");
76755012Seric 			printaddr(e->e_sendqueue, TRUE);
7685003Seric 			break;
7697275Seric 
7707275Seric 		  case CMDDBGDEBUG:	/* set debug mode */
7717676Seric 			tTsetup(tTdvect, sizeof tTdvect, "0-99.1");
7727676Seric 			tTflag(p);
77358151Seric 			message("200 Debug set");
7747275Seric 			break;
7757275Seric 
77636230Skarels # else /* not SMTPDEBUG */
77736230Skarels 		  case CMDDBGQSHOW:	/* show queues */
77836230Skarels 		  case CMDDBGDEBUG:	/* set debug mode */
77964685Seric # endif /* SMTPDEBUG */
78064685Seric 		  case CMDLOGBOGUS:	/* bogus command */
78136233Skarels # ifdef LOG
78258308Seric 			if (LogLevel > 0)
78364685Seric 				syslog(LOG_CRIT,
78458020Seric 				    "\"%s\" command from %s (%s)",
78566005Seric 				    c->cmdname, peerhostname,
78658755Seric 				    anynet_ntoa(&RealHostAddr));
78736233Skarels # endif
78836230Skarels 			/* FALL THROUGH */
78936230Skarels 
7904549Seric 		  case CMDERROR:	/* unknown command */
79166283Seric 			if (++badcommands > MAXBADCOMMANDS)
79266283Seric 			{
79366283Seric 				message("421 %s Too many bad commands; closing connection",
79466283Seric 					MyHostName);
79566283Seric 				goto doquit;
79666283Seric 			}
79766283Seric 
79858151Seric 			message("500 Command unrecognized");
7994549Seric 			break;
8004549Seric 
8014549Seric 		  default:
80236230Skarels 			errno = 0;
80358151Seric 			syserr("500 smtp: unknown code %d", c->cmdcode);
8044549Seric 			break;
8054549Seric 		}
8064549Seric 	}
8074549Seric }
8084549Seric /*
8094549Seric **  SKIPWORD -- skip a fixed word.
8104549Seric **
8114549Seric **	Parameters:
8124549Seric **		p -- place to start looking.
8134549Seric **		w -- word to skip.
8144549Seric **
8154549Seric **	Returns:
8164549Seric **		p following w.
8174549Seric **		NULL on error.
8184549Seric **
8194549Seric **	Side Effects:
8204549Seric **		clobbers the p data area.
8214549Seric */
8224549Seric 
8234549Seric static char *
8244549Seric skipword(p, w)
8254549Seric 	register char *p;
8264549Seric 	char *w;
8274549Seric {
8284549Seric 	register char *q;
82966005Seric 	char *firstp = p;
8304549Seric 
8314549Seric 	/* find beginning of word */
83258050Seric 	while (isascii(*p) && isspace(*p))
8334549Seric 		p++;
8344549Seric 	q = p;
8354549Seric 
8364549Seric 	/* find end of word */
83758050Seric 	while (*p != '\0' && *p != ':' && !(isascii(*p) && isspace(*p)))
8384549Seric 		p++;
83958050Seric 	while (isascii(*p) && isspace(*p))
8404549Seric 		*p++ = '\0';
8414549Seric 	if (*p != ':')
8424549Seric 	{
8434549Seric 	  syntax:
84466005Seric 		message("501 Syntax error in parameters scanning \"%s\"",
84566005Seric 			firstp);
8464549Seric 		Errors++;
8474549Seric 		return (NULL);
8484549Seric 	}
8494549Seric 	*p++ = '\0';
85058050Seric 	while (isascii(*p) && isspace(*p))
8514549Seric 		p++;
8524549Seric 
85362373Seric 	if (*p == '\0')
85462373Seric 		goto syntax;
85562373Seric 
8564549Seric 	/* see if the input word matches desired word */
85733725Sbostic 	if (strcasecmp(q, w))
8584549Seric 		goto syntax;
8594549Seric 
8604549Seric 	return (p);
8614549Seric }
8624577Seric /*
86358151Seric **  PRINTVRFYADDR -- print an entry in the verify queue
86458151Seric **
86558151Seric **	Parameters:
86658151Seric **		a -- the address to print
86758151Seric **		last -- set if this is the last one.
86858151Seric **
86958151Seric **	Returns:
87058151Seric **		none.
87158151Seric **
87258151Seric **	Side Effects:
87358151Seric **		Prints the appropriate 250 codes.
87458151Seric */
87558151Seric 
87658151Seric printvrfyaddr(a, last)
87758151Seric 	register ADDRESS *a;
87858151Seric 	bool last;
87958151Seric {
88058151Seric 	char fmtbuf[20];
88158151Seric 
88258151Seric 	strcpy(fmtbuf, "250");
88358151Seric 	fmtbuf[3] = last ? ' ' : '-';
88458151Seric 
88559746Seric 	if (a->q_fullname == NULL)
88659746Seric 	{
88759746Seric 		if (strchr(a->q_user, '@') == NULL)
88859746Seric 			strcpy(&fmtbuf[4], "<%s@%s>");
88959746Seric 		else
89059746Seric 			strcpy(&fmtbuf[4], "<%s>");
89159746Seric 		message(fmtbuf, a->q_user, MyHostName);
89259746Seric 	}
89358151Seric 	else
89458151Seric 	{
89559746Seric 		if (strchr(a->q_user, '@') == NULL)
89659746Seric 			strcpy(&fmtbuf[4], "%s <%s@%s>");
89759746Seric 		else
89859746Seric 			strcpy(&fmtbuf[4], "%s <%s>");
89959746Seric 		message(fmtbuf, a->q_fullname, a->q_user, MyHostName);
90058151Seric 	}
90158151Seric }
90258151Seric /*
9034577Seric **  HELP -- implement the HELP command.
9044577Seric **
9054577Seric **	Parameters:
9064577Seric **		topic -- the topic we want help for.
9074577Seric **
9084577Seric **	Returns:
9094577Seric **		none.
9104577Seric **
9114577Seric **	Side Effects:
9124577Seric **		outputs the help file to message output.
9134577Seric */
9144577Seric 
9154577Seric help(topic)
9164577Seric 	char *topic;
9174577Seric {
9184577Seric 	register FILE *hf;
9194577Seric 	int len;
9204577Seric 	char buf[MAXLINE];
9214577Seric 	bool noinfo;
9224577Seric 
9238269Seric 	if (HelpFile == NULL || (hf = fopen(HelpFile, "r")) == NULL)
9244577Seric 	{
9254577Seric 		/* no help */
92611931Seric 		errno = 0;
92758151Seric 		message("502 HELP not implemented");
9284577Seric 		return;
9294577Seric 	}
9304577Seric 
93149669Seric 	if (topic == NULL || *topic == '\0')
93249669Seric 		topic = "smtp";
93349669Seric 	else
93449669Seric 		makelower(topic);
93549669Seric 
9364577Seric 	len = strlen(topic);
9374577Seric 	noinfo = TRUE;
9384577Seric 
9394577Seric 	while (fgets(buf, sizeof buf, hf) != NULL)
9404577Seric 	{
9414577Seric 		if (strncmp(buf, topic, len) == 0)
9424577Seric 		{
9434577Seric 			register char *p;
9444577Seric 
94556795Seric 			p = strchr(buf, '\t');
9464577Seric 			if (p == NULL)
9474577Seric 				p = buf;
9484577Seric 			else
9494577Seric 				p++;
9504577Seric 			fixcrlf(p, TRUE);
95158151Seric 			message("214-%s", p);
9524577Seric 			noinfo = FALSE;
9534577Seric 		}
9544577Seric 	}
9554577Seric 
9564577Seric 	if (noinfo)
95758151Seric 		message("504 HELP topic unknown");
9584577Seric 	else
95958151Seric 		message("214 End of HELP info");
9604628Seric 	(void) fclose(hf);
9614577Seric }
9628544Seric /*
9639339Seric **  RUNINCHILD -- return twice -- once in the child, then in the parent again
9649339Seric **
9659339Seric **	Parameters:
9669339Seric **		label -- a string used in error messages
9679339Seric **
9689339Seric **	Returns:
9699339Seric **		zero in the child
9709339Seric **		one in the parent
9719339Seric **
9729339Seric **	Side Effects:
9739339Seric **		none.
9749339Seric */
9758544Seric 
97655012Seric runinchild(label, e)
9779339Seric 	char *label;
97855012Seric 	register ENVELOPE *e;
9799339Seric {
9809339Seric 	int childpid;
9819339Seric 
98216158Seric 	if (!OneXact)
9839339Seric 	{
98416158Seric 		childpid = dofork();
98516158Seric 		if (childpid < 0)
98616158Seric 		{
98716158Seric 			syserr("%s: cannot fork", label);
98816158Seric 			return (1);
98916158Seric 		}
99016158Seric 		if (childpid > 0)
99116158Seric 		{
99216158Seric 			auto int st;
9939339Seric 
99416158Seric 			/* parent -- wait for child to complete */
99561093Seric 			setproctitle("server %s child wait", CurHostName);
99616158Seric 			st = waitfor(childpid);
99716158Seric 			if (st == -1)
99816158Seric 				syserr("%s: lost child", label);
99964948Seric 			else if (!WIFEXITED(st))
100064948Seric 				syserr("%s: died on signal %d",
100164948Seric 					label, st & 0177);
10029339Seric 
100316158Seric 			/* if we exited on a QUIT command, complete the process */
100466017Seric 			if (WEXITSTATUS(st) == EX_QUIT)
100566017Seric 			{
100666017Seric 				disconnect(1, e);
100716158Seric 				finis();
100866017Seric 			}
10099339Seric 
101016158Seric 			return (1);
101116158Seric 		}
101216158Seric 		else
101316158Seric 		{
101416158Seric 			/* child */
101516158Seric 			InChild = TRUE;
101625050Seric 			QuickAbort = FALSE;
101755012Seric 			clearenvelope(e, FALSE);
101816158Seric 		}
10199339Seric 	}
102015256Seric 
102116158Seric 	/* open alias database */
102260537Seric 	initmaps(FALSE, e);
102316158Seric 
102416158Seric 	return (0);
10259339Seric }
10269339Seric 
102756795Seric # endif /* SMTP */
1028