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*64284Seric static char sccsid[] = "@(#)srvrsmtp.c	8.12 (Berkeley) 08/17/93 (with SMTP)";
1433731Sbostic #else
15*64284Seric static char sccsid[] = "@(#)srvrsmtp.c	8.12 (Berkeley) 08/17/93 (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 */
5936230Skarels /* debugging-only commands, only enabled if SMTPDEBUG is defined */
6058092Seric # define CMDDBGQSHOW	24	/* showq -- show send queue */
6158092Seric # define CMDDBGDEBUG	25	/* debug -- set debug mode */
624549Seric 
634549Seric static struct cmd	CmdTab[] =
644549Seric {
654549Seric 	"mail",		CMDMAIL,
664976Seric 	"rcpt",		CMDRCPT,
674549Seric 	"data",		CMDDATA,
684549Seric 	"rset",		CMDRSET,
694549Seric 	"vrfy",		CMDVRFY,
7058092Seric 	"expn",		CMDEXPN,
714549Seric 	"help",		CMDHELP,
724549Seric 	"noop",		CMDNOOP,
734549Seric 	"quit",		CMDQUIT,
744976Seric 	"helo",		CMDHELO,
7558323Seric 	"ehlo",		CMDEHLO,
768544Seric 	"verb",		CMDVERB,
779314Seric 	"onex",		CMDONEX,
7836230Skarels 	/*
7936230Skarels 	 * remaining commands are here only
8036230Skarels 	 * to trap and log attempts to use them
8136230Skarels 	 */
829339Seric 	"showq",	CMDDBGQSHOW,
838544Seric 	"debug",	CMDDBGDEBUG,
844549Seric 	NULL,		CMDERROR,
854549Seric };
864549Seric 
879339Seric bool	InChild = FALSE;		/* true if running in a subprocess */
889378Seric bool	OneXact = FALSE;		/* one xaction only this run */
8911146Seric 
909339Seric #define EX_QUIT		22		/* special code for QUIT command */
918544Seric 
9263937Seric static char	*skipword();
9363937Seric 
9455012Seric smtp(e)
9555012Seric 	register ENVELOPE *e;
964549Seric {
974549Seric 	register char *p;
988544Seric 	register struct cmd *c;
994549Seric 	char *cmd;
1005003Seric 	auto ADDRESS *vrfyqueue;
10112612Seric 	ADDRESS *a;
10258109Seric 	bool gotmail;			/* mail command received */
10358092Seric 	bool gothello;			/* helo command received */
10458092Seric 	bool vrfy;			/* set if this is a vrfy command */
10558323Seric 	char *protocol;			/* sending protocol */
10659016Seric 	char *sendinghost;		/* sending hostname */
10758333Seric 	long msize;			/* approximate maximum message size */
10858333Seric 	auto char *delimptr;
10958714Seric 	char *id;
11059747Seric 	int nrcpts;			/* number of RCPT commands */
11163787Seric 	bool doublequeue;
1128544Seric 	char inp[MAXLINE];
11357232Seric 	char cmdbuf[MAXLINE];
1147124Seric 	extern char Version[];
11524943Seric 	extern ENVELOPE BlankEnvelope;
1164549Seric 
11759066Seric 	if (fileno(OutChannel) != fileno(stdout))
1187363Seric 	{
1197363Seric 		/* arrange for debugging output to go to remote host */
12059066Seric 		(void) dup2(fileno(OutChannel), fileno(stdout));
1217363Seric 	}
12255012Seric 	settime(e);
12357642Seric 	CurHostName = RealHostName;
12461093Seric 	setproctitle("server %s startup", CurHostName);
12558050Seric 	expand("\201e", inp, &inp[sizeof inp], e);
12660210Seric 	message("220-%s", inp);
12760210Seric 	message("220 ESMTP spoken here");
12858330Seric 	protocol = NULL;
12959016Seric 	sendinghost = macvalue('s', e);
13058082Seric 	gothello = FALSE;
13158330Seric 	gotmail = FALSE;
1324549Seric 	for (;;)
1334549Seric 	{
13412612Seric 		/* arrange for backout */
13512612Seric 		if (setjmp(TopFrame) > 0 && InChild)
13659058Seric 		{
13759058Seric 			QuickAbort = FALSE;
13859058Seric 			SuprErrs = TRUE;
13912612Seric 			finis();
14059058Seric 		}
14112612Seric 		QuickAbort = FALSE;
14212612Seric 		HoldErrs = FALSE;
14351951Seric 		LogUsrErrs = FALSE;
14463843Seric 		e->e_flags &= ~(EF_VRFYONLY|EF_GLOBALERRS);
14512612Seric 
1467356Seric 		/* setup for the read */
14755012Seric 		e->e_to = NULL;
1484577Seric 		Errors = 0;
1497275Seric 		(void) fflush(stdout);
1507356Seric 
1517356Seric 		/* read the input line */
15261093Seric 		SmtpPhase = "server cmd read";
15361093Seric 		setproctitle("server %s cmd read", CurHostName);
15461093Seric 		p = sfgets(inp, sizeof inp, InChannel, TimeOuts.to_nextcommand,
15561093Seric 				SmtpPhase);
1567356Seric 
1577685Seric 		/* handle errors */
1587356Seric 		if (p == NULL)
1597356Seric 		{
1604549Seric 			/* end of file, just die */
16158151Seric 			message("421 %s Lost input channel from %s",
16225050Seric 				MyHostName, CurHostName);
16355464Seric #ifdef LOG
16463843Seric 			if (LogLevel > (gotmail ? 1 : 19))
16555464Seric 				syslog(LOG_NOTICE, "lost input channel from %s",
16655464Seric 					CurHostName);
16755464Seric #endif
16858069Seric 			if (InChild)
16958069Seric 				ExitStat = EX_QUIT;
1704549Seric 			finis();
1714549Seric 		}
1724549Seric 
1734549Seric 		/* clean up end of line */
1744558Seric 		fixcrlf(inp, TRUE);
1754549Seric 
1764713Seric 		/* echo command to transcript */
17755012Seric 		if (e->e_xfp != NULL)
17855012Seric 			fprintf(e->e_xfp, "<<< %s\n", inp);
1794713Seric 
18059060Seric 		if (e->e_id == NULL)
18159060Seric 			setproctitle("%s: %s", CurHostName, inp);
18259060Seric 		else
18359060Seric 			setproctitle("%s %s: %s", e->e_id, CurHostName, inp);
18459060Seric 
1854549Seric 		/* break off command */
18658050Seric 		for (p = inp; isascii(*p) && isspace(*p); p++)
1874549Seric 			continue;
18857232Seric 		cmd = cmdbuf;
18958050Seric 		while (*p != '\0' &&
19058050Seric 		       !(isascii(*p) && isspace(*p)) &&
19158050Seric 		       cmd < &cmdbuf[sizeof cmdbuf - 2])
19224981Seric 			*cmd++ = *p++;
19324981Seric 		*cmd = '\0';
1944549Seric 
19525691Seric 		/* throw away leading whitespace */
19658050Seric 		while (isascii(*p) && isspace(*p))
19725691Seric 			p++;
19825691Seric 
1994549Seric 		/* decode command */
2004549Seric 		for (c = CmdTab; c->cmdname != NULL; c++)
2014549Seric 		{
20233725Sbostic 			if (!strcasecmp(c->cmdname, cmdbuf))
2034549Seric 				break;
2044549Seric 		}
2054549Seric 
20651954Seric 		/* reset errors */
20751954Seric 		errno = 0;
20851954Seric 
2094549Seric 		/* process command */
2104549Seric 		switch (c->cmdcode)
2114549Seric 		{
2124976Seric 		  case CMDHELO:		/* hello -- introduce yourself */
21358323Seric 		  case CMDEHLO:		/* extended hello */
21458323Seric 			if (c->cmdcode == CMDEHLO)
21558323Seric 			{
21658323Seric 				protocol = "ESMTP";
21761093Seric 				SmtpPhase = "server EHLO";
21858323Seric 			}
21958323Seric 			else
22058323Seric 			{
22158323Seric 				protocol = "SMTP";
22261093Seric 				SmtpPhase = "server HELO";
22358323Seric 			}
22459016Seric 			sendinghost = newstr(p);
225*64284Seric 			if (strcasecmp(p, RealHostName) != 0 &&
226*64284Seric 			    (strcasecmp(RealHostName, "localhost") != 0 ||
227*64284Seric 			     strcasecmp(p, MyHostName) != 0))
22811146Seric 			{
22958789Seric 				auth_warning(e, "Host %s claimed to be %s",
23058789Seric 					RealHostName, p);
23111146Seric 			}
23258957Seric 			p = macvalue('_', e);
23358957Seric 			if (p == NULL)
23459016Seric 				p = RealHostName;
23558323Seric 
23660210Seric 			gothello = TRUE;
23760210Seric 			if (c->cmdcode != CMDEHLO)
23860239Seric 			{
23960239Seric 				/* print old message and be done with it */
24060239Seric 				message("250 %s Hello %s, pleased to meet you",
24160239Seric 					MyHostName, p);
24260210Seric 				break;
24360239Seric 			}
24460239Seric 
24560239Seric 			/* print extended message and brag */
24660239Seric 			message("250-%s Hello %s, pleased to meet you",
24760239Seric 				MyHostName, p);
24858323Seric 			if (!bitset(PRIV_NOEXPN, PrivacyFlags))
24958323Seric 				message("250-EXPN");
25059271Seric 			if (MaxMessageSize > 0)
25159271Seric 				message("250-SIZE %ld", MaxMessageSize);
25259271Seric 			else
25359271Seric 				message("250-SIZE");
25458323Seric 			message("250 HELP");
2554976Seric 			break;
2564976Seric 
2574549Seric 		  case CMDMAIL:		/* mail -- designate sender */
25861093Seric 			SmtpPhase = "server MAIL";
25924943Seric 
2609314Seric 			/* check for validity of this command */
26158789Seric 			if (!gothello)
26258082Seric 			{
26358957Seric 				/* set sending host to our known value */
26459016Seric 				if (sendinghost == NULL)
26559016Seric 					sendinghost = RealHostName;
26658957Seric 
26758789Seric 				if (bitset(PRIV_NEEDMAILHELO, PrivacyFlags))
26858821Seric 				{
26958789Seric 					message("503 Polite people say HELO first");
27058821Seric 					break;
27158821Seric 				}
27258082Seric 			}
27358109Seric 			if (gotmail)
2744558Seric 			{
27558151Seric 				message("503 Sender already specified");
27663843Seric 				if (InChild)
27763843Seric 					finis();
2784558Seric 				break;
2794558Seric 			}
2809339Seric 			if (InChild)
2819339Seric 			{
28236230Skarels 				errno = 0;
28358151Seric 				syserr("503 Nested MAIL command: MAIL %s", p);
28458069Seric 				finis();
2859339Seric 			}
2869339Seric 
2879339Seric 			/* fork a subprocess to process this command */
28855012Seric 			if (runinchild("SMTP-MAIL", e) > 0)
2899339Seric 				break;
29063753Seric 			if (!gothello)
29163753Seric 			{
29263753Seric 				auth_warning(e,
29363753Seric 					"Host %s didn't use HELO protocol",
29463753Seric 					RealHostName);
29563753Seric 			}
29658323Seric 			if (protocol == NULL)
29758323Seric 				protocol = "SMTP";
29858323Seric 			define('r', protocol, e);
29959016Seric 			define('s', sendinghost, e);
30055012Seric 			initsys(e);
30159747Seric 			nrcpts = 0;
30257389Seric 			setproctitle("%s %s: %s", e->e_id, CurHostName, inp);
3039339Seric 
3049339Seric 			/* child -- go do the processing */
3054549Seric 			p = skipword(p, "from");
3064549Seric 			if (p == NULL)
3074549Seric 				break;
30857977Seric 			if (setjmp(TopFrame) > 0)
30958147Seric 			{
31058147Seric 				/* this failed -- undo work */
31158147Seric 				if (InChild)
31259058Seric 				{
31359058Seric 					QuickAbort = FALSE;
31459058Seric 					SuprErrs = TRUE;
31563787Seric 					e->e_flags &= ~EF_FATALERRS;
31658147Seric 					finis();
31759058Seric 				}
31857977Seric 				break;
31958147Seric 			}
32057977Seric 			QuickAbort = TRUE;
32158333Seric 
32258333Seric 			/* must parse sender first */
32358333Seric 			delimptr = NULL;
32458704Seric 			setsender(p, e, &delimptr, FALSE);
32558333Seric 			p = delimptr;
32658333Seric 			if (p != NULL && *p != '\0')
32758333Seric 				*p++ = '\0';
32858333Seric 
32958333Seric 			/* now parse ESMTP arguments */
33058333Seric 			msize = 0;
33158333Seric 			for (; p != NULL && *p != '\0'; p++)
33258333Seric 			{
33358333Seric 				char *kp;
33458333Seric 				char *vp;
33558333Seric 
33658333Seric 				/* locate the beginning of the keyword */
33758333Seric 				while (isascii(*p) && isspace(*p))
33858333Seric 					p++;
33958333Seric 				if (*p == '\0')
34058333Seric 					break;
34158333Seric 				kp = p;
34258333Seric 
34358333Seric 				/* skip to the value portion */
34458333Seric 				while (isascii(*p) && isalnum(*p) || *p == '-')
34558333Seric 					p++;
34658333Seric 				if (*p == '=')
34758333Seric 				{
34858333Seric 					*p++ = '\0';
34958333Seric 					vp = p;
35058333Seric 
35158333Seric 					/* skip to the end of the value */
35258333Seric 					while (*p != '\0' && *p != ' ' &&
35358333Seric 					       !(isascii(*p) && iscntrl(*p)) &&
35458333Seric 					       *p != '=')
35558333Seric 						p++;
35658333Seric 				}
35758333Seric 
35858333Seric 				if (*p != '\0')
35958333Seric 					*p++ = '\0';
36058333Seric 
36158333Seric 				if (tTd(19, 1))
36258333Seric 					printf("MAIL: got arg %s=%s\n", kp,
36358333Seric 						vp == NULL ? "<null>" : vp);
36458333Seric 
36558333Seric 				if (strcasecmp(kp, "size") == 0)
36658333Seric 				{
36759093Seric 					if (vp == NULL)
36858333Seric 					{
36958333Seric 						usrerr("501 SIZE requires a value");
37058333Seric 						/* NOTREACHED */
37158333Seric 					}
37258333Seric 					msize = atol(vp);
37358333Seric 				}
37459093Seric 				else if (strcasecmp(kp, "body") == 0)
37559093Seric 				{
37659093Seric 					if (vp == NULL)
37759093Seric 					{
37859093Seric 						usrerr("501 BODY requires a value");
37959093Seric 						/* NOTREACHED */
38059093Seric 					}
38159093Seric # ifdef MIME
38259093Seric 					if (strcasecmp(vp, "8bitmime") == 0)
38359093Seric 					{
38459093Seric 						e->e_bodytype = "8BITMIME";
38559709Seric 						SevenBit = FALSE;
38659093Seric 					}
38759093Seric 					else if (strcasecmp(vp, "7bit") == 0)
38859093Seric 					{
38959093Seric 						e->e_bodytype = "7BIT";
39059709Seric 						SevenBit = TRUE;
39159093Seric 					}
39259093Seric 					else
39359093Seric 					{
39459093Seric 						usrerr("501 Unknown BODY type %s",
39559093Seric 							vp);
39659093Seric 					}
39759093Seric # endif
39859093Seric 				}
39958333Seric 				else
40058333Seric 				{
40158333Seric 					usrerr("501 %s parameter unrecognized", kp);
40258333Seric 					/* NOTREACHED */
40358333Seric 				}
40458333Seric 			}
40559284Seric 
40659284Seric 			if (MaxMessageSize > 0 && msize > MaxMessageSize)
40759284Seric 			{
40859284Seric 				usrerr("552 Message size exceeds fixed maximum message size (%ld)",
40959284Seric 					MaxMessageSize);
41059284Seric 				/* NOTREACHED */
41159284Seric 			}
41258333Seric 
41358333Seric 			if (!enoughspace(msize))
41458333Seric 			{
41558333Seric 				message("452 Insufficient disk space; try again later");
41658333Seric 				break;
41758333Seric 			}
41858151Seric 			message("250 Sender ok");
41958147Seric 			gotmail = TRUE;
4204549Seric 			break;
4214549Seric 
4224976Seric 		  case CMDRCPT:		/* rcpt -- designate recipient */
42358850Seric 			if (!gotmail)
42458850Seric 			{
42558850Seric 				usrerr("503 Need MAIL before RCPT");
42658850Seric 				break;
42758850Seric 			}
42861093Seric 			SmtpPhase = "server RCPT";
42912612Seric 			if (setjmp(TopFrame) > 0)
43014785Seric 			{
43155012Seric 				e->e_flags &= ~EF_FATALERRS;
43212612Seric 				break;
43314785Seric 			}
43412612Seric 			QuickAbort = TRUE;
43551951Seric 			LogUsrErrs = TRUE;
43658093Seric 
43759699Seric 			if (e->e_sendmode != SM_DELIVER)
43859699Seric 				e->e_flags |= EF_VRFYONLY;
43958919Seric 
4404549Seric 			p = skipword(p, "to");
4414549Seric 			if (p == NULL)
4424549Seric 				break;
443*64284Seric 			a = parseaddr(p, NULLADDR, RF_COPYALL, ' ', NULL, e);
44412612Seric 			if (a == NULL)
44512612Seric 				break;
44616886Seric 			a->q_flags |= QPRIMARY;
44755012Seric 			a = recipient(a, &e->e_sendqueue, e);
44812612Seric 			if (Errors != 0)
44912612Seric 				break;
45012612Seric 
45112612Seric 			/* no errors during parsing, but might be a duplicate */
45255012Seric 			e->e_to = p;
45312612Seric 			if (!bitset(QBADADDR, a->q_flags))
45459747Seric 			{
45558151Seric 				message("250 Recipient ok");
45659747Seric 				nrcpts++;
45759747Seric 			}
45812612Seric 			else
4594549Seric 			{
46012612Seric 				/* punt -- should keep message in ADDRESS.... */
46158151Seric 				message("550 Addressee unknown");
4624549Seric 			}
46355012Seric 			e->e_to = NULL;
4644549Seric 			break;
4654549Seric 
4664549Seric 		  case CMDDATA:		/* data -- text of mail */
46761093Seric 			SmtpPhase = "server DATA";
46858109Seric 			if (!gotmail)
4694549Seric 			{
47058151Seric 				message("503 Need MAIL command");
4714976Seric 				break;
4724549Seric 			}
47355012Seric 			else if (e->e_nrcpts <= 0)
4744549Seric 			{
47558151Seric 				message("503 Need RCPT (recipient)");
4764976Seric 				break;
4774549Seric 			}
4784976Seric 
47958929Seric 			/* check to see if we need to re-expand aliases */
48063787Seric 			/* also reset QBADADDR on already-diagnosted addrs */
48163787Seric 			doublequeue = FALSE;
48258929Seric 			for (a = e->e_sendqueue; a != NULL; a = a->q_next)
48358929Seric 			{
48458929Seric 				if (bitset(QVERIFIED, a->q_flags))
48563787Seric 				{
48663787Seric 					/* need to re-expand aliases */
48763787Seric 					doublequeue = TRUE;
48863787Seric 				}
48963787Seric 				if (bitset(QBADADDR, a->q_flags))
49063787Seric 				{
49163787Seric 					/* make this "go away" */
49263787Seric 					a->q_flags |= QDONTSEND;
49363787Seric 					a->q_flags &= ~QBADADDR;
49463787Seric 				}
49558929Seric 			}
49658929Seric 
4974976Seric 			/* collect the text of the message */
49824943Seric 			SmtpPhase = "collect";
49963965Seric 			HoldErrs = TRUE;
50063787Seric 			collect(TRUE, doublequeue, e);
5014976Seric 
5028238Seric 			/*
5038238Seric 			**  Arrange to send to everyone.
5048238Seric 			**	If sending to multiple people, mail back
5058238Seric 			**		errors rather than reporting directly.
5068238Seric 			**	In any case, don't mail back errors for
5078238Seric 			**		anything that has happened up to
5088238Seric 			**		now (the other end will do this).
50910197Seric 			**	Truncate our transcript -- the mail has gotten
51010197Seric 			**		to us successfully, and if we have
51110197Seric 			**		to mail this back, it will be easier
51210197Seric 			**		on the reader.
5138238Seric 			**	Then send to everyone.
5148238Seric 			**	Finally give a reply code.  If an error has
5158238Seric 			**		already been given, don't mail a
5168238Seric 			**		message back.
5179339Seric 			**	We goose error returns by clearing error bit.
5188238Seric 			*/
5198238Seric 
52024943Seric 			SmtpPhase = "delivery";
52163787Seric 			if (nrcpts != 1 && !doublequeue)
5229378Seric 			{
5239378Seric 				HoldErrs = TRUE;
52458734Seric 				e->e_errormode = EM_MAIL;
5259378Seric 			}
52655012Seric 			e->e_xfp = freopen(queuename(e, 'x'), "w", e->e_xfp);
52758714Seric 			id = e->e_id;
5284976Seric 
5294976Seric 			/* send to all recipients */
53063787Seric 			sendall(e, doublequeue ? SM_QUEUE : SM_DEFAULT);
53155012Seric 			e->e_to = NULL;
5324976Seric 
5338238Seric 			/* issue success if appropriate and reset */
5348238Seric 			if (Errors == 0 || HoldErrs)
53558855Seric 				message("250 %s Message accepted for delivery", id);
53659747Seric 
53759747Seric 			if (bitset(EF_FATALERRS, e->e_flags) && !HoldErrs)
53859730Seric 			{
53959730Seric 				/* avoid sending back an extra message */
54059730Seric 				e->e_flags &= ~EF_FATALERRS;
54159747Seric 				e->e_flags |= EF_CLRQUEUE;
54259730Seric 			}
5438238Seric 			else
54458919Seric 			{
54559747Seric 				/* from now on, we have to operate silently */
54659747Seric 				HoldErrs = TRUE;
54759747Seric 				e->e_errormode = EM_MAIL;
54859747Seric 
54959730Seric 				/* if we just queued, poke it */
55063787Seric 				if (doublequeue && e->e_sendmode != SM_QUEUE)
55159730Seric 				{
55259730Seric 					unlockqueue(e);
55359730Seric 					dowork(id, TRUE, TRUE, e);
55459730Seric 					e->e_id = NULL;
55559730Seric 				}
55658919Seric 			}
55758883Seric 
55859747Seric   abortmessage:
5599339Seric 			/* if in a child, pop back to our parent */
5609339Seric 			if (InChild)
5619339Seric 				finis();
56224943Seric 
56324943Seric 			/* clean up a bit */
56458109Seric 			gotmail = FALSE;
56555012Seric 			dropenvelope(e);
56658179Seric 			CurEnv = e = newenvelope(e, CurEnv);
56755012Seric 			e->e_flags = BlankEnvelope.e_flags;
5684549Seric 			break;
5694549Seric 
5704549Seric 		  case CMDRSET:		/* rset -- reset state */
57158151Seric 			message("250 Reset state");
5729339Seric 			if (InChild)
5739339Seric 				finis();
57458109Seric 
57558109Seric 			/* clean up a bit */
57658109Seric 			gotmail = FALSE;
57758109Seric 			dropenvelope(e);
57858179Seric 			CurEnv = e = newenvelope(e, CurEnv);
5799339Seric 			break;
5804549Seric 
5814549Seric 		  case CMDVRFY:		/* vrfy -- verify address */
58258092Seric 		  case CMDEXPN:		/* expn -- expand address */
58358092Seric 			vrfy = c->cmdcode == CMDVRFY;
58458092Seric 			if (bitset(vrfy ? PRIV_NOVRFY : PRIV_NOEXPN,
58558092Seric 						PrivacyFlags))
58658082Seric 			{
58758412Seric 				if (vrfy)
58858412Seric 					message("252 Who's to say?");
58958412Seric 				else
59058412Seric 					message("502 That's none of your business");
59158082Seric 				break;
59258082Seric 			}
59358082Seric 			else if (!gothello &&
59458092Seric 				 bitset(vrfy ? PRIV_NEEDVRFYHELO : PRIV_NEEDEXPNHELO,
59558092Seric 						PrivacyFlags))
59658082Seric 			{
59758151Seric 				message("503 I demand that you introduce yourself first");
59858082Seric 				break;
59958082Seric 			}
60058092Seric 			if (runinchild(vrfy ? "SMTP-VRFY" : "SMTP-EXPN", e) > 0)
6019339Seric 				break;
60255173Seric #ifdef LOG
60358020Seric 			if (LogLevel > 5)
60455173Seric 				syslog(LOG_INFO, "%s: %s", CurHostName, inp);
60555173Seric #endif
6065003Seric 			vrfyqueue = NULL;
6077762Seric 			QuickAbort = TRUE;
60858092Seric 			if (vrfy)
60958092Seric 				e->e_flags |= EF_VRFYONLY;
61062373Seric 			while (*p != '\0' && isascii(*p) && isspace(*p))
61162373Seric 				*p++;
61262373Seric 			if (*p == '\0')
61362373Seric 			{
61462373Seric 				message("501 Argument required");
61562373Seric 				Errors++;
61662373Seric 			}
61762373Seric 			else
61862373Seric 			{
619*64284Seric 				(void) sendtolist(p, NULLADDR, &vrfyqueue, e);
62062373Seric 			}
6217762Seric 			if (Errors != 0)
6229339Seric 			{
6239339Seric 				if (InChild)
6249339Seric 					finis();
6257762Seric 				break;
6269339Seric 			}
62762373Seric 			if (vrfyqueue == NULL)
62862373Seric 			{
62962373Seric 				message("554 Nothing to %s", vrfy ? "VRFY" : "EXPN");
63062373Seric 			}
6315003Seric 			while (vrfyqueue != NULL)
6325003Seric 			{
63363971Seric 				a = vrfyqueue;
63463971Seric 				while ((a = a->q_next) != NULL &&
63563971Seric 				       bitset(QDONTSEND|QBADADDR, a->q_flags))
63663971Seric 					continue;
6377685Seric 				if (!bitset(QDONTSEND|QBADADDR, vrfyqueue->q_flags))
63858151Seric 					printvrfyaddr(vrfyqueue, a == NULL);
63963847Seric 				vrfyqueue = vrfyqueue->q_next;
6405003Seric 			}
6419339Seric 			if (InChild)
6429339Seric 				finis();
6434549Seric 			break;
6444549Seric 
6454549Seric 		  case CMDHELP:		/* help -- give user info */
6464577Seric 			help(p);
6474549Seric 			break;
6484549Seric 
6494549Seric 		  case CMDNOOP:		/* noop -- do nothing */
65064122Seric 			message("250 OK");
6514549Seric 			break;
6524549Seric 
6534549Seric 		  case CMDQUIT:		/* quit -- leave mail */
65458151Seric 			message("221 %s closing connection", MyHostName);
65561051Seric 
65661051Seric 			/* avoid future 050 messages */
65761051Seric 			Verbose = FALSE;
65861051Seric 
6599339Seric 			if (InChild)
6609339Seric 				ExitStat = EX_QUIT;
6614549Seric 			finis();
6624549Seric 
6638544Seric 		  case CMDVERB:		/* set verbose mode */
66459957Seric 			if (bitset(PRIV_NOEXPN, PrivacyFlags))
66559957Seric 			{
66659957Seric 				/* this would give out the same info */
66759957Seric 				message("502 Verbose unavailable");
66859957Seric 				break;
66959957Seric 			}
6708544Seric 			Verbose = TRUE;
67158734Seric 			e->e_sendmode = SM_DELIVER;
67259957Seric 			message("250 Verbose mode");
6738544Seric 			break;
6748544Seric 
6759314Seric 		  case CMDONEX:		/* doing one transaction only */
6769378Seric 			OneXact = TRUE;
67759957Seric 			message("250 Only one transaction");
6789314Seric 			break;
6799314Seric 
68036230Skarels # ifdef SMTPDEBUG
6819339Seric 		  case CMDDBGQSHOW:	/* show queues */
6826907Seric 			printf("Send Queue=");
68355012Seric 			printaddr(e->e_sendqueue, TRUE);
6845003Seric 			break;
6857275Seric 
6867275Seric 		  case CMDDBGDEBUG:	/* set debug mode */
6877676Seric 			tTsetup(tTdvect, sizeof tTdvect, "0-99.1");
6887676Seric 			tTflag(p);
68958151Seric 			message("200 Debug set");
6907275Seric 			break;
6917275Seric 
69236230Skarels # else /* not SMTPDEBUG */
69324945Seric 
69436230Skarels 		  case CMDDBGQSHOW:	/* show queues */
69536230Skarels 		  case CMDDBGDEBUG:	/* set debug mode */
69636233Skarels # ifdef LOG
69758308Seric 			if (LogLevel > 0)
69836230Skarels 				syslog(LOG_NOTICE,
69958020Seric 				    "\"%s\" command from %s (%s)",
70036230Skarels 				    c->cmdname, RealHostName,
70158755Seric 				    anynet_ntoa(&RealHostAddr));
70236233Skarels # endif
70336230Skarels 			/* FALL THROUGH */
70436230Skarels # endif /* SMTPDEBUG */
70536230Skarels 
7064549Seric 		  case CMDERROR:	/* unknown command */
70758151Seric 			message("500 Command unrecognized");
7084549Seric 			break;
7094549Seric 
7104549Seric 		  default:
71136230Skarels 			errno = 0;
71258151Seric 			syserr("500 smtp: unknown code %d", c->cmdcode);
7134549Seric 			break;
7144549Seric 		}
7154549Seric 	}
7164549Seric }
7174549Seric /*
7184549Seric **  SKIPWORD -- skip a fixed word.
7194549Seric **
7204549Seric **	Parameters:
7214549Seric **		p -- place to start looking.
7224549Seric **		w -- word to skip.
7234549Seric **
7244549Seric **	Returns:
7254549Seric **		p following w.
7264549Seric **		NULL on error.
7274549Seric **
7284549Seric **	Side Effects:
7294549Seric **		clobbers the p data area.
7304549Seric */
7314549Seric 
7324549Seric static char *
7334549Seric skipword(p, w)
7344549Seric 	register char *p;
7354549Seric 	char *w;
7364549Seric {
7374549Seric 	register char *q;
7384549Seric 
7394549Seric 	/* find beginning of word */
74058050Seric 	while (isascii(*p) && isspace(*p))
7414549Seric 		p++;
7424549Seric 	q = p;
7434549Seric 
7444549Seric 	/* find end of word */
74558050Seric 	while (*p != '\0' && *p != ':' && !(isascii(*p) && isspace(*p)))
7464549Seric 		p++;
74758050Seric 	while (isascii(*p) && isspace(*p))
7484549Seric 		*p++ = '\0';
7494549Seric 	if (*p != ':')
7504549Seric 	{
7514549Seric 	  syntax:
75262373Seric 		message("501 Syntax error in parameters");
7534549Seric 		Errors++;
7544549Seric 		return (NULL);
7554549Seric 	}
7564549Seric 	*p++ = '\0';
75758050Seric 	while (isascii(*p) && isspace(*p))
7584549Seric 		p++;
7594549Seric 
76062373Seric 	if (*p == '\0')
76162373Seric 		goto syntax;
76262373Seric 
7634549Seric 	/* see if the input word matches desired word */
76433725Sbostic 	if (strcasecmp(q, w))
7654549Seric 		goto syntax;
7664549Seric 
7674549Seric 	return (p);
7684549Seric }
7694577Seric /*
77058151Seric **  PRINTVRFYADDR -- print an entry in the verify queue
77158151Seric **
77258151Seric **	Parameters:
77358151Seric **		a -- the address to print
77458151Seric **		last -- set if this is the last one.
77558151Seric **
77658151Seric **	Returns:
77758151Seric **		none.
77858151Seric **
77958151Seric **	Side Effects:
78058151Seric **		Prints the appropriate 250 codes.
78158151Seric */
78258151Seric 
78358151Seric printvrfyaddr(a, last)
78458151Seric 	register ADDRESS *a;
78558151Seric 	bool last;
78658151Seric {
78758151Seric 	char fmtbuf[20];
78858151Seric 
78958151Seric 	strcpy(fmtbuf, "250");
79058151Seric 	fmtbuf[3] = last ? ' ' : '-';
79158151Seric 
79259746Seric 	if (a->q_fullname == NULL)
79359746Seric 	{
79459746Seric 		if (strchr(a->q_user, '@') == NULL)
79559746Seric 			strcpy(&fmtbuf[4], "<%s@%s>");
79659746Seric 		else
79759746Seric 			strcpy(&fmtbuf[4], "<%s>");
79859746Seric 		message(fmtbuf, a->q_user, MyHostName);
79959746Seric 	}
80058151Seric 	else
80158151Seric 	{
80259746Seric 		if (strchr(a->q_user, '@') == NULL)
80359746Seric 			strcpy(&fmtbuf[4], "%s <%s@%s>");
80459746Seric 		else
80559746Seric 			strcpy(&fmtbuf[4], "%s <%s>");
80659746Seric 		message(fmtbuf, a->q_fullname, a->q_user, MyHostName);
80758151Seric 	}
80858151Seric }
80958151Seric /*
8104577Seric **  HELP -- implement the HELP command.
8114577Seric **
8124577Seric **	Parameters:
8134577Seric **		topic -- the topic we want help for.
8144577Seric **
8154577Seric **	Returns:
8164577Seric **		none.
8174577Seric **
8184577Seric **	Side Effects:
8194577Seric **		outputs the help file to message output.
8204577Seric */
8214577Seric 
8224577Seric help(topic)
8234577Seric 	char *topic;
8244577Seric {
8254577Seric 	register FILE *hf;
8264577Seric 	int len;
8274577Seric 	char buf[MAXLINE];
8284577Seric 	bool noinfo;
8294577Seric 
8308269Seric 	if (HelpFile == NULL || (hf = fopen(HelpFile, "r")) == NULL)
8314577Seric 	{
8324577Seric 		/* no help */
83311931Seric 		errno = 0;
83458151Seric 		message("502 HELP not implemented");
8354577Seric 		return;
8364577Seric 	}
8374577Seric 
83849669Seric 	if (topic == NULL || *topic == '\0')
83949669Seric 		topic = "smtp";
84049669Seric 	else
84149669Seric 		makelower(topic);
84249669Seric 
8434577Seric 	len = strlen(topic);
8444577Seric 	noinfo = TRUE;
8454577Seric 
8464577Seric 	while (fgets(buf, sizeof buf, hf) != NULL)
8474577Seric 	{
8484577Seric 		if (strncmp(buf, topic, len) == 0)
8494577Seric 		{
8504577Seric 			register char *p;
8514577Seric 
85256795Seric 			p = strchr(buf, '\t');
8534577Seric 			if (p == NULL)
8544577Seric 				p = buf;
8554577Seric 			else
8564577Seric 				p++;
8574577Seric 			fixcrlf(p, TRUE);
85858151Seric 			message("214-%s", p);
8594577Seric 			noinfo = FALSE;
8604577Seric 		}
8614577Seric 	}
8624577Seric 
8634577Seric 	if (noinfo)
86458151Seric 		message("504 HELP topic unknown");
8654577Seric 	else
86658151Seric 		message("214 End of HELP info");
8674628Seric 	(void) fclose(hf);
8684577Seric }
8698544Seric /*
8709339Seric **  RUNINCHILD -- return twice -- once in the child, then in the parent again
8719339Seric **
8729339Seric **	Parameters:
8739339Seric **		label -- a string used in error messages
8749339Seric **
8759339Seric **	Returns:
8769339Seric **		zero in the child
8779339Seric **		one in the parent
8789339Seric **
8799339Seric **	Side Effects:
8809339Seric **		none.
8819339Seric */
8828544Seric 
88355012Seric runinchild(label, e)
8849339Seric 	char *label;
88555012Seric 	register ENVELOPE *e;
8869339Seric {
8879339Seric 	int childpid;
8889339Seric 
88916158Seric 	if (!OneXact)
8909339Seric 	{
89116158Seric 		childpid = dofork();
89216158Seric 		if (childpid < 0)
89316158Seric 		{
89416158Seric 			syserr("%s: cannot fork", label);
89516158Seric 			return (1);
89616158Seric 		}
89716158Seric 		if (childpid > 0)
89816158Seric 		{
89916158Seric 			auto int st;
9009339Seric 
90116158Seric 			/* parent -- wait for child to complete */
90261093Seric 			setproctitle("server %s child wait", CurHostName);
90316158Seric 			st = waitfor(childpid);
90416158Seric 			if (st == -1)
90516158Seric 				syserr("%s: lost child", label);
9069339Seric 
90716158Seric 			/* if we exited on a QUIT command, complete the process */
90816158Seric 			if (st == (EX_QUIT << 8))
90916158Seric 				finis();
9109339Seric 
91116158Seric 			return (1);
91216158Seric 		}
91316158Seric 		else
91416158Seric 		{
91516158Seric 			/* child */
91616158Seric 			InChild = TRUE;
91725050Seric 			QuickAbort = FALSE;
91855012Seric 			clearenvelope(e, FALSE);
91916158Seric 		}
9209339Seric 	}
92115256Seric 
92216158Seric 	/* open alias database */
92360537Seric 	initmaps(FALSE, e);
92416158Seric 
92516158Seric 	return (0);
9269339Seric }
9279339Seric 
92856795Seric # endif /* SMTP */
929