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*67880Seric static char sccsid[] = "@(#)srvrsmtp.c	8.45 (Berkeley) 11/04/94 (with SMTP)";
1433731Sbostic #else
15*67880Seric static char sccsid[] = "@(#)srvrsmtp.c	8.45 (Berkeley) 11/04/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 			message("250-8BITMIME");
30664359Seric 			if (MaxMessageSize > 0)
30764359Seric 				message("250-SIZE %ld", MaxMessageSize);
30859271Seric 			else
30959271Seric 				message("250-SIZE");
310*67880Seric 			message("250-DSN");
31158323Seric 			message("250 HELP");
3124976Seric 			break;
3134976Seric 
3144549Seric 		  case CMDMAIL:		/* mail -- designate sender */
31561093Seric 			SmtpPhase = "server MAIL";
31624943Seric 
3179314Seric 			/* check for validity of this command */
31858789Seric 			if (!gothello)
31958082Seric 			{
32058957Seric 				/* set sending host to our known value */
32159016Seric 				if (sendinghost == NULL)
32266005Seric 					sendinghost = peerhostname;
32358957Seric 
32458789Seric 				if (bitset(PRIV_NEEDMAILHELO, PrivacyFlags))
32558821Seric 				{
32658789Seric 					message("503 Polite people say HELO first");
32758821Seric 					break;
32858821Seric 				}
32958082Seric 			}
33058109Seric 			if (gotmail)
3314558Seric 			{
33258151Seric 				message("503 Sender already specified");
33363843Seric 				if (InChild)
33463843Seric 					finis();
3354558Seric 				break;
3364558Seric 			}
3379339Seric 			if (InChild)
3389339Seric 			{
33936230Skarels 				errno = 0;
34058151Seric 				syserr("503 Nested MAIL command: MAIL %s", p);
34158069Seric 				finis();
3429339Seric 			}
3439339Seric 
3449339Seric 			/* fork a subprocess to process this command */
34555012Seric 			if (runinchild("SMTP-MAIL", e) > 0)
3469339Seric 				break;
34763753Seric 			if (!gothello)
34863753Seric 			{
34963753Seric 				auth_warning(e,
35066005Seric 					"Host %s didn't use HELO protocol",
35166005Seric 					peerhostname);
35263753Seric 			}
35365947Seric #ifdef PICKY_HELO_CHECK
35466005Seric 			if (strcasecmp(sendinghost, peerhostname) != 0 &&
35566005Seric 			    (strcasecmp(peerhostname, "localhost") != 0 ||
35665823Seric 			     strcasecmp(sendinghost, MyHostName) != 0))
35765823Seric 			{
35865823Seric 				auth_warning(e, "Host %s claimed to be %s",
35966005Seric 					peerhostname, sendinghost);
36065823Seric 			}
36165947Seric #endif
36265823Seric 
36358323Seric 			if (protocol == NULL)
36458323Seric 				protocol = "SMTP";
36558323Seric 			define('r', protocol, e);
36659016Seric 			define('s', sendinghost, e);
36755012Seric 			initsys(e);
36859747Seric 			nrcpts = 0;
36965089Seric 			e->e_flags |= EF_LOGSENDER;
37065058Seric 			setproctitle("%s %s: %.80s", e->e_id, CurSmtpClient, inp);
3719339Seric 
3729339Seric 			/* child -- go do the processing */
3734549Seric 			p = skipword(p, "from");
3744549Seric 			if (p == NULL)
3754549Seric 				break;
37657977Seric 			if (setjmp(TopFrame) > 0)
37758147Seric 			{
37858147Seric 				/* this failed -- undo work */
37958147Seric 				if (InChild)
38059058Seric 				{
38159058Seric 					QuickAbort = FALSE;
38259058Seric 					SuprErrs = TRUE;
38363787Seric 					e->e_flags &= ~EF_FATALERRS;
38458147Seric 					finis();
38559058Seric 				}
38657977Seric 				break;
38758147Seric 			}
38857977Seric 			QuickAbort = TRUE;
38958333Seric 
39058333Seric 			/* must parse sender first */
39158333Seric 			delimptr = NULL;
39258704Seric 			setsender(p, e, &delimptr, FALSE);
39358333Seric 			p = delimptr;
39458333Seric 			if (p != NULL && *p != '\0')
39558333Seric 				*p++ = '\0';
39658333Seric 
39766325Seric 			/* check for possible spoofing */
39866325Seric 			if (RealUid != 0 && OpMode == MD_SMTP &&
39967473Seric 			    !bitnset(M_LOCALMAILER, e->e_from.q_mailer->m_flags) &&
40067473Seric 			    strcmp(e->e_from.q_user, RealUserName) != 0)
40166325Seric 			{
40266325Seric 				auth_warning(e, "%s owned process doing -bs",
40366325Seric 					RealUserName);
40466325Seric 			}
40566325Seric 
40658333Seric 			/* now parse ESMTP arguments */
40758333Seric 			msize = 0;
40866764Seric 			while (p != NULL && *p != '\0')
40958333Seric 			{
41058333Seric 				char *kp;
41166304Seric 				char *vp = NULL;
41258333Seric 
41358333Seric 				/* locate the beginning of the keyword */
41458333Seric 				while (isascii(*p) && isspace(*p))
41558333Seric 					p++;
41658333Seric 				if (*p == '\0')
41758333Seric 					break;
41858333Seric 				kp = p;
41958333Seric 
42058333Seric 				/* skip to the value portion */
42158333Seric 				while (isascii(*p) && isalnum(*p) || *p == '-')
42258333Seric 					p++;
42358333Seric 				if (*p == '=')
42458333Seric 				{
42558333Seric 					*p++ = '\0';
42658333Seric 					vp = p;
42758333Seric 
42858333Seric 					/* skip to the end of the value */
42958333Seric 					while (*p != '\0' && *p != ' ' &&
43058333Seric 					       !(isascii(*p) && iscntrl(*p)) &&
43158333Seric 					       *p != '=')
43258333Seric 						p++;
43358333Seric 				}
43458333Seric 
43558333Seric 				if (*p != '\0')
43658333Seric 					*p++ = '\0';
43758333Seric 
43858333Seric 				if (tTd(19, 1))
43966764Seric 					printf("MAIL: got arg %s=\"%s\"\n", kp,
44058333Seric 						vp == NULL ? "<null>" : vp);
44158333Seric 
44258333Seric 				if (strcasecmp(kp, "size") == 0)
44358333Seric 				{
44459093Seric 					if (vp == NULL)
44558333Seric 					{
44658333Seric 						usrerr("501 SIZE requires a value");
44758333Seric 						/* NOTREACHED */
44858333Seric 					}
44966772Seric # ifdef __STDC__
45066772Seric 					msize = strtoul(vp, (char **) NULL, 10);
45166772Seric # else
45266772Seric 					msize = strtol(vp, (char **) NULL, 10);
45366772Seric # endif
45458333Seric 				}
45559093Seric 				else if (strcasecmp(kp, "body") == 0)
45659093Seric 				{
45759093Seric 					if (vp == NULL)
45859093Seric 					{
45959093Seric 						usrerr("501 BODY requires a value");
46059093Seric 						/* NOTREACHED */
46159093Seric 					}
46267417Seric 					e->e_bodytype = newstr(vp);
46359093Seric 					if (strcasecmp(vp, "8bitmime") == 0)
46459093Seric 					{
46567546Seric 						SevenBitInput = FALSE;
46659093Seric 					}
46759093Seric 					else if (strcasecmp(vp, "7bit") == 0)
46859093Seric 					{
46967546Seric 						SevenBitInput = TRUE;
47059093Seric 					}
47159093Seric 					else
47259093Seric 					{
47359093Seric 						usrerr("501 Unknown BODY type %s",
47459093Seric 							vp);
47567417Seric 						/* NOTREACHED */
47659093Seric 					}
47759093Seric 				}
478*67880Seric 				else if (strcasecmp(kp, "envid") == 0)
479*67880Seric 				{
480*67880Seric 					if (vp == NULL)
481*67880Seric 					{
482*67880Seric 						usrerr("501 ENVID requires a value");
483*67880Seric 						/* NOTREACHED */
484*67880Seric 					}
485*67880Seric 					e->e_envid = newstr(vp);
486*67880Seric 				}
48758333Seric 				else
48858333Seric 				{
48958333Seric 					usrerr("501 %s parameter unrecognized", kp);
49058333Seric 					/* NOTREACHED */
49158333Seric 				}
49258333Seric 			}
49359284Seric 
49459284Seric 			if (MaxMessageSize > 0 && msize > MaxMessageSize)
49559284Seric 			{
49659284Seric 				usrerr("552 Message size exceeds fixed maximum message size (%ld)",
49759284Seric 					MaxMessageSize);
49859284Seric 				/* NOTREACHED */
49959284Seric 			}
50058333Seric 
50158333Seric 			if (!enoughspace(msize))
50258333Seric 			{
50358333Seric 				message("452 Insufficient disk space; try again later");
50458333Seric 				break;
50558333Seric 			}
50658151Seric 			message("250 Sender ok");
50758147Seric 			gotmail = TRUE;
5084549Seric 			break;
5094549Seric 
5104976Seric 		  case CMDRCPT:		/* rcpt -- designate recipient */
51158850Seric 			if (!gotmail)
51258850Seric 			{
51358850Seric 				usrerr("503 Need MAIL before RCPT");
51458850Seric 				break;
51558850Seric 			}
51661093Seric 			SmtpPhase = "server RCPT";
51712612Seric 			if (setjmp(TopFrame) > 0)
51814785Seric 			{
51955012Seric 				e->e_flags &= ~EF_FATALERRS;
52012612Seric 				break;
52114785Seric 			}
52212612Seric 			QuickAbort = TRUE;
52351951Seric 			LogUsrErrs = TRUE;
52458093Seric 
52559699Seric 			if (e->e_sendmode != SM_DELIVER)
52659699Seric 				e->e_flags |= EF_VRFYONLY;
52758919Seric 
5284549Seric 			p = skipword(p, "to");
5294549Seric 			if (p == NULL)
5304549Seric 				break;
531*67880Seric 			a = parseaddr(p, NULLADDR, RF_COPYALL, ' ', &delimptr, e);
53212612Seric 			if (a == NULL)
53312612Seric 				break;
534*67880Seric 			p = delimptr;
53516886Seric 			a->q_flags |= QPRIMARY;
53655012Seric 			a = recipient(a, &e->e_sendqueue, e);
537*67880Seric 
538*67880Seric 			/* now parse ESMTP arguments */
539*67880Seric 			while (p != NULL && *p != '\0')
540*67880Seric 			{
541*67880Seric 				char *kp;
542*67880Seric 				char *vp = NULL;
543*67880Seric 
544*67880Seric 				/* locate the beginning of the keyword */
545*67880Seric 				while (isascii(*p) && isspace(*p))
546*67880Seric 					p++;
547*67880Seric 				if (*p == '\0')
548*67880Seric 					break;
549*67880Seric 				kp = p;
550*67880Seric 
551*67880Seric 				/* skip to the value portion */
552*67880Seric 				while (isascii(*p) && isalnum(*p) || *p == '-')
553*67880Seric 					p++;
554*67880Seric 				if (*p == '=')
555*67880Seric 				{
556*67880Seric 					*p++ = '\0';
557*67880Seric 					vp = p;
558*67880Seric 
559*67880Seric 					/* skip to the end of the value */
560*67880Seric 					while (*p != '\0' && *p != ' ' &&
561*67880Seric 					       !(isascii(*p) && iscntrl(*p)) &&
562*67880Seric 					       *p != '=')
563*67880Seric 						p++;
564*67880Seric 				}
565*67880Seric 
566*67880Seric 				if (*p != '\0')
567*67880Seric 					*p++ = '\0';
568*67880Seric 
569*67880Seric 				if (tTd(19, 1))
570*67880Seric 					printf("RCPT: got arg %s=\"%s\"\n", kp,
571*67880Seric 						vp == NULL ? "<null>" : vp);
572*67880Seric 
573*67880Seric 				if (strcasecmp(kp, "notify") == 0)
574*67880Seric 				{
575*67880Seric 					if (vp == NULL)
576*67880Seric 					{
577*67880Seric 						usrerr("501 NOTIFY requires a value");
578*67880Seric 						/* NOTREACHED */
579*67880Seric 					}
580*67880Seric 					a->q_flags &= ~(QPINGONSUCCESS|QPINGONFAILURE);
581*67880Seric 					if (strcasecmp(vp, "success") == 0)
582*67880Seric 						a->q_flags |= QPINGONSUCCESS;
583*67880Seric 					else if (strcasecmp(vp, "failure") == 0)
584*67880Seric 						a->q_flags |= QPINGONFAILURE;
585*67880Seric 					else if (strcasecmp(vp, "always") == 0)
586*67880Seric 						a->q_flags |= QPINGONSUCCESS |
587*67880Seric 							      QPINGONFAILURE;
588*67880Seric 					else if (strcasecmp(vp, "never") != 0)
589*67880Seric 					{
590*67880Seric 						usrerr("501 Bad argument \"%s\"  to NOTIFY",
591*67880Seric 							vp);
592*67880Seric 						/* NOTREACHED */
593*67880Seric 					}
594*67880Seric 				}
595*67880Seric 				else if (strcasecmp(kp, "ret") == 0)
596*67880Seric 				{
597*67880Seric 					if (vp == NULL)
598*67880Seric 					{
599*67880Seric 						usrerr("501 RET requires a value");
600*67880Seric 						/* NOTREACHED */
601*67880Seric 					}
602*67880Seric 					a->q_flags |= QHASRETPARAM;
603*67880Seric 					if (strcasecmp(vp, "no") == 0)
604*67880Seric 						a->q_flags |= QNOBODYRETURN;
605*67880Seric 					else if (strcasecmp(vp, "yes") != 0)
606*67880Seric 					{
607*67880Seric 						usrerr("501 Bad argument \"%s\" to RET",
608*67880Seric 							vp);
609*67880Seric 						/* NOTREACHED */
610*67880Seric 					}
611*67880Seric 				}
612*67880Seric 				else if (strcasecmp(kp, "orcpt") == 0)
613*67880Seric 				{
614*67880Seric 					if (vp == NULL)
615*67880Seric 					{
616*67880Seric 						usrerr("501 ORCPT requires a value");
617*67880Seric 						/* NOTREACHED */
618*67880Seric 					}
619*67880Seric 					a->q_orcpt = newstr(vp);
620*67880Seric 				}
621*67880Seric 				else
622*67880Seric 				{
623*67880Seric 					usrerr("501 %s parameter unrecognized", kp);
624*67880Seric 					/* NOTREACHED */
625*67880Seric 				}
626*67880Seric 			}
62712612Seric 			if (Errors != 0)
62812612Seric 				break;
62912612Seric 
63012612Seric 			/* no errors during parsing, but might be a duplicate */
63155012Seric 			e->e_to = p;
63212612Seric 			if (!bitset(QBADADDR, a->q_flags))
63359747Seric 			{
63464718Seric 				message("250 Recipient ok%s",
63564718Seric 					bitset(QQUEUEUP, a->q_flags) ?
63664718Seric 						" (will queue)" : "");
63759747Seric 				nrcpts++;
63859747Seric 			}
63912612Seric 			else
6404549Seric 			{
64112612Seric 				/* punt -- should keep message in ADDRESS.... */
64258151Seric 				message("550 Addressee unknown");
6434549Seric 			}
64455012Seric 			e->e_to = NULL;
6454549Seric 			break;
6464549Seric 
6474549Seric 		  case CMDDATA:		/* data -- text of mail */
64861093Seric 			SmtpPhase = "server DATA";
64958109Seric 			if (!gotmail)
6504549Seric 			{
65158151Seric 				message("503 Need MAIL command");
6524976Seric 				break;
6534549Seric 			}
65464718Seric 			else if (nrcpts <= 0)
6554549Seric 			{
65658151Seric 				message("503 Need RCPT (recipient)");
6574976Seric 				break;
6584549Seric 			}
6594976Seric 
66058929Seric 			/* check to see if we need to re-expand aliases */
66163787Seric 			/* also reset QBADADDR on already-diagnosted addrs */
66263787Seric 			doublequeue = FALSE;
66358929Seric 			for (a = e->e_sendqueue; a != NULL; a = a->q_next)
66458929Seric 			{
66558929Seric 				if (bitset(QVERIFIED, a->q_flags))
66663787Seric 				{
66763787Seric 					/* need to re-expand aliases */
66863787Seric 					doublequeue = TRUE;
66963787Seric 				}
67063787Seric 				if (bitset(QBADADDR, a->q_flags))
67163787Seric 				{
67263787Seric 					/* make this "go away" */
67363787Seric 					a->q_flags |= QDONTSEND;
67463787Seric 					a->q_flags &= ~QBADADDR;
67563787Seric 				}
67658929Seric 			}
67758929Seric 
6784976Seric 			/* collect the text of the message */
67924943Seric 			SmtpPhase = "collect";
68067546Seric 			collect(InChannel, TRUE, doublequeue, NULL, e);
68164766Seric 			if (Errors != 0)
68264766Seric 				goto abortmessage;
68367131Seric 
68467131Seric 			/* from now on, we have to operate silently */
68563965Seric 			HoldErrs = TRUE;
68667131Seric 			e->e_errormode = EM_MAIL;
6874976Seric 
6888238Seric 			/*
6898238Seric 			**  Arrange to send to everyone.
6908238Seric 			**	If sending to multiple people, mail back
6918238Seric 			**		errors rather than reporting directly.
6928238Seric 			**	In any case, don't mail back errors for
6938238Seric 			**		anything that has happened up to
6948238Seric 			**		now (the other end will do this).
69510197Seric 			**	Truncate our transcript -- the mail has gotten
69610197Seric 			**		to us successfully, and if we have
69710197Seric 			**		to mail this back, it will be easier
69810197Seric 			**		on the reader.
6998238Seric 			**	Then send to everyone.
7008238Seric 			**	Finally give a reply code.  If an error has
7018238Seric 			**		already been given, don't mail a
7028238Seric 			**		message back.
7039339Seric 			**	We goose error returns by clearing error bit.
7048238Seric 			*/
7058238Seric 
70624943Seric 			SmtpPhase = "delivery";
70755012Seric 			e->e_xfp = freopen(queuename(e, 'x'), "w", e->e_xfp);
70858714Seric 			id = e->e_id;
7094976Seric 
71067131Seric 			if (doublequeue)
71159730Seric 			{
71267131Seric 				/* make sure it is in the queue */
71367131Seric 				queueup(e, TRUE, FALSE);
71459730Seric 			}
7158238Seric 			else
71658919Seric 			{
71767131Seric 				/* send to all recipients */
71867131Seric 				sendall(e, SM_DEFAULT);
71967131Seric 			}
72067131Seric 			e->e_to = NULL;
72159747Seric 
72267131Seric 			/* issue success message */
72367131Seric 			message("250 %s Message accepted for delivery", id);
72464296Seric 
72567131Seric 			/* if we just queued, poke it */
72667131Seric 			if (doublequeue && e->e_sendmode != SM_QUEUE)
72767131Seric 			{
72867131Seric 				extern pid_t dowork();
72967131Seric 
73067131Seric 				unlockqueue(e);
73167131Seric 				(void) dowork(id, TRUE, TRUE, e);
73258919Seric 			}
73358883Seric 
73459747Seric   abortmessage:
7359339Seric 			/* if in a child, pop back to our parent */
7369339Seric 			if (InChild)
7379339Seric 				finis();
73824943Seric 
73924943Seric 			/* clean up a bit */
74058109Seric 			gotmail = FALSE;
74155012Seric 			dropenvelope(e);
74258179Seric 			CurEnv = e = newenvelope(e, CurEnv);
74355012Seric 			e->e_flags = BlankEnvelope.e_flags;
7444549Seric 			break;
7454549Seric 
7464549Seric 		  case CMDRSET:		/* rset -- reset state */
74758151Seric 			message("250 Reset state");
74864359Seric 			e->e_flags |= EF_CLRQUEUE;
7499339Seric 			if (InChild)
7509339Seric 				finis();
75158109Seric 
75258109Seric 			/* clean up a bit */
75358109Seric 			gotmail = FALSE;
75458109Seric 			dropenvelope(e);
75558179Seric 			CurEnv = e = newenvelope(e, CurEnv);
7569339Seric 			break;
7574549Seric 
7584549Seric 		  case CMDVRFY:		/* vrfy -- verify address */
75958092Seric 		  case CMDEXPN:		/* expn -- expand address */
76058092Seric 			vrfy = c->cmdcode == CMDVRFY;
76158092Seric 			if (bitset(vrfy ? PRIV_NOVRFY : PRIV_NOEXPN,
76258092Seric 						PrivacyFlags))
76358082Seric 			{
76458412Seric 				if (vrfy)
76567160Seric 					message("252 Cannot VRFY user; try RCPT to attempt delivery (or try finger)");
76658412Seric 				else
76765192Seric 					message("502 Sorry, we do not allow this operation");
76865017Seric #ifdef LOG
76965017Seric 				if (LogLevel > 5)
77065017Seric 					syslog(LOG_INFO, "%s: %s [rejected]",
77165017Seric 						CurSmtpClient, inp);
77265017Seric #endif
77358082Seric 				break;
77458082Seric 			}
77558082Seric 			else if (!gothello &&
77658092Seric 				 bitset(vrfy ? PRIV_NEEDVRFYHELO : PRIV_NEEDEXPNHELO,
77758092Seric 						PrivacyFlags))
77858082Seric 			{
77958151Seric 				message("503 I demand that you introduce yourself first");
78058082Seric 				break;
78158082Seric 			}
78258092Seric 			if (runinchild(vrfy ? "SMTP-VRFY" : "SMTP-EXPN", e) > 0)
7839339Seric 				break;
78455173Seric #ifdef LOG
78558020Seric 			if (LogLevel > 5)
78665017Seric 				syslog(LOG_INFO, "%s: %s", CurSmtpClient, inp);
78755173Seric #endif
7885003Seric 			vrfyqueue = NULL;
7897762Seric 			QuickAbort = TRUE;
79058092Seric 			if (vrfy)
79158092Seric 				e->e_flags |= EF_VRFYONLY;
79262373Seric 			while (*p != '\0' && isascii(*p) && isspace(*p))
79367615Seric 				p++;
79462373Seric 			if (*p == '\0')
79562373Seric 			{
79662373Seric 				message("501 Argument required");
79762373Seric 				Errors++;
79862373Seric 			}
79962373Seric 			else
80062373Seric 			{
80164284Seric 				(void) sendtolist(p, NULLADDR, &vrfyqueue, e);
80262373Seric 			}
8037762Seric 			if (Errors != 0)
8049339Seric 			{
8059339Seric 				if (InChild)
8069339Seric 					finis();
8077762Seric 				break;
8089339Seric 			}
80962373Seric 			if (vrfyqueue == NULL)
81062373Seric 			{
81162373Seric 				message("554 Nothing to %s", vrfy ? "VRFY" : "EXPN");
81262373Seric 			}
8135003Seric 			while (vrfyqueue != NULL)
8145003Seric 			{
81563971Seric 				a = vrfyqueue;
81663971Seric 				while ((a = a->q_next) != NULL &&
81763971Seric 				       bitset(QDONTSEND|QBADADDR, a->q_flags))
81863971Seric 					continue;
8197685Seric 				if (!bitset(QDONTSEND|QBADADDR, vrfyqueue->q_flags))
82058151Seric 					printvrfyaddr(vrfyqueue, a == NULL);
82163847Seric 				vrfyqueue = vrfyqueue->q_next;
8225003Seric 			}
8239339Seric 			if (InChild)
8249339Seric 				finis();
8254549Seric 			break;
8264549Seric 
8274549Seric 		  case CMDHELP:		/* help -- give user info */
8284577Seric 			help(p);
8294549Seric 			break;
8304549Seric 
8314549Seric 		  case CMDNOOP:		/* noop -- do nothing */
83264122Seric 			message("250 OK");
8334549Seric 			break;
8344549Seric 
8354549Seric 		  case CMDQUIT:		/* quit -- leave mail */
83658151Seric 			message("221 %s closing connection", MyHostName);
83761051Seric 
83866283Seric doquit:
83961051Seric 			/* avoid future 050 messages */
84066017Seric 			disconnect(1, e);
84161051Seric 
8429339Seric 			if (InChild)
8439339Seric 				ExitStat = EX_QUIT;
8444549Seric 			finis();
8454549Seric 
8468544Seric 		  case CMDVERB:		/* set verbose mode */
84759957Seric 			if (bitset(PRIV_NOEXPN, PrivacyFlags))
84859957Seric 			{
84959957Seric 				/* this would give out the same info */
85059957Seric 				message("502 Verbose unavailable");
85159957Seric 				break;
85259957Seric 			}
8538544Seric 			Verbose = TRUE;
85458734Seric 			e->e_sendmode = SM_DELIVER;
85559957Seric 			message("250 Verbose mode");
8568544Seric 			break;
8578544Seric 
8589314Seric 		  case CMDONEX:		/* doing one transaction only */
8599378Seric 			OneXact = TRUE;
86059957Seric 			message("250 Only one transaction");
8619314Seric 			break;
8629314Seric 
86336230Skarels # ifdef SMTPDEBUG
8649339Seric 		  case CMDDBGQSHOW:	/* show queues */
8656907Seric 			printf("Send Queue=");
86655012Seric 			printaddr(e->e_sendqueue, TRUE);
8675003Seric 			break;
8687275Seric 
8697275Seric 		  case CMDDBGDEBUG:	/* set debug mode */
8707676Seric 			tTsetup(tTdvect, sizeof tTdvect, "0-99.1");
8717676Seric 			tTflag(p);
87258151Seric 			message("200 Debug set");
8737275Seric 			break;
8747275Seric 
87536230Skarels # else /* not SMTPDEBUG */
87636230Skarels 		  case CMDDBGQSHOW:	/* show queues */
87736230Skarels 		  case CMDDBGDEBUG:	/* set debug mode */
87864685Seric # endif /* SMTPDEBUG */
87964685Seric 		  case CMDLOGBOGUS:	/* bogus command */
88036233Skarels # ifdef LOG
88158308Seric 			if (LogLevel > 0)
88264685Seric 				syslog(LOG_CRIT,
88358020Seric 				    "\"%s\" command from %s (%s)",
88466005Seric 				    c->cmdname, peerhostname,
88558755Seric 				    anynet_ntoa(&RealHostAddr));
88636233Skarels # endif
88736230Skarels 			/* FALL THROUGH */
88836230Skarels 
8894549Seric 		  case CMDERROR:	/* unknown command */
89066283Seric 			if (++badcommands > MAXBADCOMMANDS)
89166283Seric 			{
89266283Seric 				message("421 %s Too many bad commands; closing connection",
89366283Seric 					MyHostName);
89466283Seric 				goto doquit;
89566283Seric 			}
89666283Seric 
89758151Seric 			message("500 Command unrecognized");
8984549Seric 			break;
8994549Seric 
9004549Seric 		  default:
90136230Skarels 			errno = 0;
90258151Seric 			syserr("500 smtp: unknown code %d", c->cmdcode);
9034549Seric 			break;
9044549Seric 		}
9054549Seric 	}
9064549Seric }
9074549Seric /*
9084549Seric **  SKIPWORD -- skip a fixed word.
9094549Seric **
9104549Seric **	Parameters:
9114549Seric **		p -- place to start looking.
9124549Seric **		w -- word to skip.
9134549Seric **
9144549Seric **	Returns:
9154549Seric **		p following w.
9164549Seric **		NULL on error.
9174549Seric **
9184549Seric **	Side Effects:
9194549Seric **		clobbers the p data area.
9204549Seric */
9214549Seric 
9224549Seric static char *
9234549Seric skipword(p, w)
9244549Seric 	register char *p;
9254549Seric 	char *w;
9264549Seric {
9274549Seric 	register char *q;
92866005Seric 	char *firstp = p;
9294549Seric 
9304549Seric 	/* find beginning of word */
93158050Seric 	while (isascii(*p) && isspace(*p))
9324549Seric 		p++;
9334549Seric 	q = p;
9344549Seric 
9354549Seric 	/* find end of word */
93658050Seric 	while (*p != '\0' && *p != ':' && !(isascii(*p) && isspace(*p)))
9374549Seric 		p++;
93858050Seric 	while (isascii(*p) && isspace(*p))
9394549Seric 		*p++ = '\0';
9404549Seric 	if (*p != ':')
9414549Seric 	{
9424549Seric 	  syntax:
94366005Seric 		message("501 Syntax error in parameters scanning \"%s\"",
94466005Seric 			firstp);
9454549Seric 		Errors++;
9464549Seric 		return (NULL);
9474549Seric 	}
9484549Seric 	*p++ = '\0';
94958050Seric 	while (isascii(*p) && isspace(*p))
9504549Seric 		p++;
9514549Seric 
95262373Seric 	if (*p == '\0')
95362373Seric 		goto syntax;
95462373Seric 
9554549Seric 	/* see if the input word matches desired word */
95633725Sbostic 	if (strcasecmp(q, w))
9574549Seric 		goto syntax;
9584549Seric 
9594549Seric 	return (p);
9604549Seric }
9614577Seric /*
96258151Seric **  PRINTVRFYADDR -- print an entry in the verify queue
96358151Seric **
96458151Seric **	Parameters:
96558151Seric **		a -- the address to print
96658151Seric **		last -- set if this is the last one.
96758151Seric **
96858151Seric **	Returns:
96958151Seric **		none.
97058151Seric **
97158151Seric **	Side Effects:
97258151Seric **		Prints the appropriate 250 codes.
97358151Seric */
97458151Seric 
97558151Seric printvrfyaddr(a, last)
97658151Seric 	register ADDRESS *a;
97758151Seric 	bool last;
97858151Seric {
97958151Seric 	char fmtbuf[20];
98058151Seric 
98158151Seric 	strcpy(fmtbuf, "250");
98258151Seric 	fmtbuf[3] = last ? ' ' : '-';
98358151Seric 
98459746Seric 	if (a->q_fullname == NULL)
98559746Seric 	{
98659746Seric 		if (strchr(a->q_user, '@') == NULL)
98759746Seric 			strcpy(&fmtbuf[4], "<%s@%s>");
98859746Seric 		else
98959746Seric 			strcpy(&fmtbuf[4], "<%s>");
99059746Seric 		message(fmtbuf, a->q_user, MyHostName);
99159746Seric 	}
99258151Seric 	else
99358151Seric 	{
99459746Seric 		if (strchr(a->q_user, '@') == NULL)
99559746Seric 			strcpy(&fmtbuf[4], "%s <%s@%s>");
99659746Seric 		else
99759746Seric 			strcpy(&fmtbuf[4], "%s <%s>");
99859746Seric 		message(fmtbuf, a->q_fullname, a->q_user, MyHostName);
99958151Seric 	}
100058151Seric }
100158151Seric /*
10024577Seric **  HELP -- implement the HELP command.
10034577Seric **
10044577Seric **	Parameters:
10054577Seric **		topic -- the topic we want help for.
10064577Seric **
10074577Seric **	Returns:
10084577Seric **		none.
10094577Seric **
10104577Seric **	Side Effects:
10114577Seric **		outputs the help file to message output.
10124577Seric */
10134577Seric 
10144577Seric help(topic)
10154577Seric 	char *topic;
10164577Seric {
10174577Seric 	register FILE *hf;
10184577Seric 	int len;
10194577Seric 	char buf[MAXLINE];
10204577Seric 	bool noinfo;
10214577Seric 
10228269Seric 	if (HelpFile == NULL || (hf = fopen(HelpFile, "r")) == NULL)
10234577Seric 	{
10244577Seric 		/* no help */
102511931Seric 		errno = 0;
102658151Seric 		message("502 HELP not implemented");
10274577Seric 		return;
10284577Seric 	}
10294577Seric 
103049669Seric 	if (topic == NULL || *topic == '\0')
103149669Seric 		topic = "smtp";
103249669Seric 	else
103349669Seric 		makelower(topic);
103449669Seric 
10354577Seric 	len = strlen(topic);
10364577Seric 	noinfo = TRUE;
10374577Seric 
10384577Seric 	while (fgets(buf, sizeof buf, hf) != NULL)
10394577Seric 	{
10404577Seric 		if (strncmp(buf, topic, len) == 0)
10414577Seric 		{
10424577Seric 			register char *p;
10434577Seric 
104456795Seric 			p = strchr(buf, '\t');
10454577Seric 			if (p == NULL)
10464577Seric 				p = buf;
10474577Seric 			else
10484577Seric 				p++;
10494577Seric 			fixcrlf(p, TRUE);
105058151Seric 			message("214-%s", p);
10514577Seric 			noinfo = FALSE;
10524577Seric 		}
10534577Seric 	}
10544577Seric 
10554577Seric 	if (noinfo)
105658151Seric 		message("504 HELP topic unknown");
10574577Seric 	else
105858151Seric 		message("214 End of HELP info");
10594628Seric 	(void) fclose(hf);
10604577Seric }
10618544Seric /*
10629339Seric **  RUNINCHILD -- return twice -- once in the child, then in the parent again
10639339Seric **
10649339Seric **	Parameters:
10659339Seric **		label -- a string used in error messages
10669339Seric **
10679339Seric **	Returns:
10689339Seric **		zero in the child
10699339Seric **		one in the parent
10709339Seric **
10719339Seric **	Side Effects:
10729339Seric **		none.
10739339Seric */
10748544Seric 
107555012Seric runinchild(label, e)
10769339Seric 	char *label;
107755012Seric 	register ENVELOPE *e;
10789339Seric {
10799339Seric 	int childpid;
10809339Seric 
108116158Seric 	if (!OneXact)
10829339Seric 	{
108316158Seric 		childpid = dofork();
108416158Seric 		if (childpid < 0)
108516158Seric 		{
108616158Seric 			syserr("%s: cannot fork", label);
108716158Seric 			return (1);
108816158Seric 		}
108916158Seric 		if (childpid > 0)
109016158Seric 		{
109116158Seric 			auto int st;
10929339Seric 
109316158Seric 			/* parent -- wait for child to complete */
109461093Seric 			setproctitle("server %s child wait", CurHostName);
109516158Seric 			st = waitfor(childpid);
109616158Seric 			if (st == -1)
109716158Seric 				syserr("%s: lost child", label);
109864948Seric 			else if (!WIFEXITED(st))
109964948Seric 				syserr("%s: died on signal %d",
110064948Seric 					label, st & 0177);
11019339Seric 
110216158Seric 			/* if we exited on a QUIT command, complete the process */
110366017Seric 			if (WEXITSTATUS(st) == EX_QUIT)
110466017Seric 			{
110566017Seric 				disconnect(1, e);
110616158Seric 				finis();
110766017Seric 			}
11089339Seric 
110916158Seric 			return (1);
111016158Seric 		}
111116158Seric 		else
111216158Seric 		{
111316158Seric 			/* child */
111416158Seric 			InChild = TRUE;
111525050Seric 			QuickAbort = FALSE;
111655012Seric 			clearenvelope(e, FALSE);
111716158Seric 		}
11189339Seric 	}
111915256Seric 
112016158Seric 	/* open alias database */
112160537Seric 	initmaps(FALSE, e);
112216158Seric 
112316158Seric 	return (0);
11249339Seric }
11259339Seric 
112656795Seric # endif /* SMTP */
1127