122712Sdist /*
234921Sbostic  * Copyright (c) 1983 Eric P. Allman
333731Sbostic  * Copyright (c) 1988 Regents of the University of California.
433731Sbostic  * All rights reserved.
533731Sbostic  *
642829Sbostic  * %sccs.include.redist.c%
733731Sbostic  */
822712Sdist 
933731Sbostic # include "sendmail.h"
1022712Sdist 
1133731Sbostic #ifndef lint
1233731Sbostic #ifdef SMTP
13*61093Seric static char sccsid[] = "@(#)srvrsmtp.c	6.59 (Berkeley) 06/03/93 (with SMTP)";
1433731Sbostic #else
15*61093Seric static char sccsid[] = "@(#)srvrsmtp.c	6.59 (Berkeley) 06/03/93 (without SMTP)";
1633731Sbostic #endif
1733731Sbostic #endif /* not lint */
1833731Sbostic 
199339Seric # include <errno.h>
2011728Seric # include <signal.h>
214549Seric 
2233731Sbostic # ifdef SMTP
234556Seric 
244549Seric /*
254549Seric **  SMTP -- run the SMTP protocol.
264549Seric **
274549Seric **	Parameters:
284549Seric **		none.
294549Seric **
304549Seric **	Returns:
314549Seric **		never.
324549Seric **
334549Seric **	Side Effects:
344549Seric **		Reads commands from the input channel and processes
354549Seric **			them.
364549Seric */
374549Seric 
384549Seric struct cmd
394549Seric {
404549Seric 	char	*cmdname;	/* command name */
414549Seric 	int	cmdcode;	/* internal code, see below */
424549Seric };
434549Seric 
444549Seric /* values for cmdcode */
454549Seric # define CMDERROR	0	/* bad command */
464549Seric # define CMDMAIL	1	/* mail -- designate sender */
474976Seric # define CMDRCPT	2	/* rcpt -- designate recipient */
484549Seric # define CMDDATA	3	/* data -- send message text */
499339Seric # define CMDRSET	4	/* rset -- reset state */
509339Seric # define CMDVRFY	5	/* vrfy -- verify address */
5158092Seric # define CMDEXPN	6	/* expn -- expand address */
529339Seric # define CMDNOOP	7	/* noop -- do nothing */
539339Seric # define CMDQUIT	8	/* quit -- close connection and die */
549339Seric # define CMDHELO	9	/* helo -- be polite */
5558092Seric # define CMDHELP	10	/* help -- give usage info */
5658323Seric # define CMDEHLO	11	/* ehlo -- extended helo (RFC 1425) */
5758092Seric /* non-standard commands */
5858092Seric # define CMDONEX	16	/* onex -- sending one transaction only */
5958092Seric # define CMDVERB	17	/* verb -- go into verbose mode */
6036230Skarels /* debugging-only commands, only enabled if SMTPDEBUG is defined */
6158092Seric # define CMDDBGQSHOW	24	/* showq -- show send queue */
6258092Seric # define CMDDBGDEBUG	25	/* debug -- set debug mode */
634549Seric 
644549Seric static struct cmd	CmdTab[] =
654549Seric {
664549Seric 	"mail",		CMDMAIL,
674976Seric 	"rcpt",		CMDRCPT,
684549Seric 	"data",		CMDDATA,
694549Seric 	"rset",		CMDRSET,
704549Seric 	"vrfy",		CMDVRFY,
7158092Seric 	"expn",		CMDEXPN,
724549Seric 	"help",		CMDHELP,
734549Seric 	"noop",		CMDNOOP,
744549Seric 	"quit",		CMDQUIT,
754976Seric 	"helo",		CMDHELO,
7658323Seric 	"ehlo",		CMDEHLO,
778544Seric 	"verb",		CMDVERB,
789314Seric 	"onex",		CMDONEX,
7936230Skarels 	/*
8036230Skarels 	 * remaining commands are here only
8136230Skarels 	 * to trap and log attempts to use them
8236230Skarels 	 */
839339Seric 	"showq",	CMDDBGQSHOW,
848544Seric 	"debug",	CMDDBGDEBUG,
854549Seric 	NULL,		CMDERROR,
864549Seric };
874549Seric 
889339Seric bool	InChild = FALSE;		/* true if running in a subprocess */
899378Seric bool	OneXact = FALSE;		/* one xaction only this run */
9011146Seric 
919339Seric #define EX_QUIT		22		/* special code for QUIT command */
928544Seric 
9355012Seric smtp(e)
9455012Seric 	register ENVELOPE *e;
954549Seric {
964549Seric 	register char *p;
978544Seric 	register struct cmd *c;
984549Seric 	char *cmd;
9946928Sbostic 	static char *skipword();
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 */
1118544Seric 	char inp[MAXLINE];
11257232Seric 	char cmdbuf[MAXLINE];
1137124Seric 	extern char Version[];
11424943Seric 	extern ENVELOPE BlankEnvelope;
1154549Seric 
11659066Seric 	if (fileno(OutChannel) != fileno(stdout))
1177363Seric 	{
1187363Seric 		/* arrange for debugging output to go to remote host */
11959066Seric 		(void) dup2(fileno(OutChannel), fileno(stdout));
1207363Seric 	}
12155012Seric 	settime(e);
12257642Seric 	CurHostName = RealHostName;
123*61093Seric 	setproctitle("server %s startup", CurHostName);
12458050Seric 	expand("\201e", inp, &inp[sizeof inp], e);
12560210Seric 	message("220-%s", inp);
12660210Seric 	message("220 ESMTP spoken here");
12758330Seric 	protocol = NULL;
12859016Seric 	sendinghost = macvalue('s', e);
12958082Seric 	gothello = FALSE;
13058330Seric 	gotmail = FALSE;
1314549Seric 	for (;;)
1324549Seric 	{
13312612Seric 		/* arrange for backout */
13412612Seric 		if (setjmp(TopFrame) > 0 && InChild)
13559058Seric 		{
13659058Seric 			QuickAbort = FALSE;
13759058Seric 			SuprErrs = TRUE;
13812612Seric 			finis();
13959058Seric 		}
14012612Seric 		QuickAbort = FALSE;
14112612Seric 		HoldErrs = FALSE;
14251951Seric 		LogUsrErrs = FALSE;
14358092Seric 		e->e_flags &= ~EF_VRFYONLY;
14412612Seric 
1457356Seric 		/* setup for the read */
14655012Seric 		e->e_to = NULL;
1474577Seric 		Errors = 0;
1487275Seric 		(void) fflush(stdout);
1497356Seric 
1507356Seric 		/* read the input line */
151*61093Seric 		SmtpPhase = "server cmd read";
152*61093Seric 		setproctitle("server %s cmd read", CurHostName);
153*61093Seric 		p = sfgets(inp, sizeof inp, InChannel, TimeOuts.to_nextcommand,
154*61093Seric 				SmtpPhase);
1557356Seric 
1567685Seric 		/* handle errors */
1577356Seric 		if (p == NULL)
1587356Seric 		{
1594549Seric 			/* end of file, just die */
16058151Seric 			message("421 %s Lost input channel from %s",
16125050Seric 				MyHostName, CurHostName);
16255464Seric #ifdef LOG
16358020Seric 			if (LogLevel > 1)
16455464Seric 				syslog(LOG_NOTICE, "lost input channel from %s",
16555464Seric 					CurHostName);
16655464Seric #endif
16758069Seric 			if (InChild)
16858069Seric 				ExitStat = EX_QUIT;
1694549Seric 			finis();
1704549Seric 		}
1714549Seric 
1724549Seric 		/* clean up end of line */
1734558Seric 		fixcrlf(inp, TRUE);
1744549Seric 
1754713Seric 		/* echo command to transcript */
17655012Seric 		if (e->e_xfp != NULL)
17755012Seric 			fprintf(e->e_xfp, "<<< %s\n", inp);
1784713Seric 
17959060Seric 		if (e->e_id == NULL)
18059060Seric 			setproctitle("%s: %s", CurHostName, inp);
18159060Seric 		else
18259060Seric 			setproctitle("%s %s: %s", e->e_id, CurHostName, inp);
18359060Seric 
1844549Seric 		/* break off command */
18558050Seric 		for (p = inp; isascii(*p) && isspace(*p); p++)
1864549Seric 			continue;
18757232Seric 		cmd = cmdbuf;
18858050Seric 		while (*p != '\0' &&
18958050Seric 		       !(isascii(*p) && isspace(*p)) &&
19058050Seric 		       cmd < &cmdbuf[sizeof cmdbuf - 2])
19124981Seric 			*cmd++ = *p++;
19224981Seric 		*cmd = '\0';
1934549Seric 
19425691Seric 		/* throw away leading whitespace */
19558050Seric 		while (isascii(*p) && isspace(*p))
19625691Seric 			p++;
19725691Seric 
1984549Seric 		/* decode command */
1994549Seric 		for (c = CmdTab; c->cmdname != NULL; c++)
2004549Seric 		{
20133725Sbostic 			if (!strcasecmp(c->cmdname, cmdbuf))
2024549Seric 				break;
2034549Seric 		}
2044549Seric 
20551954Seric 		/* reset errors */
20651954Seric 		errno = 0;
20751954Seric 
2084549Seric 		/* process command */
2094549Seric 		switch (c->cmdcode)
2104549Seric 		{
2114976Seric 		  case CMDHELO:		/* hello -- introduce yourself */
21258323Seric 		  case CMDEHLO:		/* extended hello */
21358323Seric 			if (c->cmdcode == CMDEHLO)
21458323Seric 			{
21558323Seric 				protocol = "ESMTP";
216*61093Seric 				SmtpPhase = "server EHLO";
21758323Seric 			}
21858323Seric 			else
21958323Seric 			{
22058323Seric 				protocol = "SMTP";
221*61093Seric 				SmtpPhase = "server HELO";
22258323Seric 			}
22359016Seric 			sendinghost = newstr(p);
22458308Seric 			if (strcasecmp(p, RealHostName) != 0)
22511146Seric 			{
22658789Seric 				auth_warning(e, "Host %s claimed to be %s",
22758789Seric 					RealHostName, p);
22811146Seric 			}
22958957Seric 			p = macvalue('_', e);
23058957Seric 			if (p == NULL)
23159016Seric 				p = RealHostName;
23258323Seric 
23360210Seric 			gothello = TRUE;
23460210Seric 			if (c->cmdcode != CMDEHLO)
23560239Seric 			{
23660239Seric 				/* print old message and be done with it */
23760239Seric 				message("250 %s Hello %s, pleased to meet you",
23860239Seric 					MyHostName, p);
23960210Seric 				break;
24060239Seric 			}
24160239Seric 
24260239Seric 			/* print extended message and brag */
24360239Seric 			message("250-%s Hello %s, pleased to meet you",
24460239Seric 				MyHostName, p);
24558323Seric 			if (!bitset(PRIV_NOEXPN, PrivacyFlags))
24658323Seric 				message("250-EXPN");
24759271Seric 			if (MaxMessageSize > 0)
24859271Seric 				message("250-SIZE %ld", MaxMessageSize);
24959271Seric 			else
25059271Seric 				message("250-SIZE");
25158323Seric 			message("250 HELP");
2524976Seric 			break;
2534976Seric 
2544549Seric 		  case CMDMAIL:		/* mail -- designate sender */
255*61093Seric 			SmtpPhase = "server MAIL";
25624943Seric 
2579314Seric 			/* check for validity of this command */
25858789Seric 			if (!gothello)
25958082Seric 			{
26058957Seric 				/* set sending host to our known value */
26159016Seric 				if (sendinghost == NULL)
26259016Seric 					sendinghost = RealHostName;
26358957Seric 
26458789Seric 				if (bitset(PRIV_NEEDMAILHELO, PrivacyFlags))
26558821Seric 				{
26658789Seric 					message("503 Polite people say HELO first");
26758821Seric 					break;
26858821Seric 				}
26958789Seric 				else
27058821Seric 				{
27158789Seric 					auth_warning(e,
27258789Seric 						"Host %s didn't use HELO protocol",
27358789Seric 						RealHostName);
27458821Seric 				}
27558082Seric 			}
27658109Seric 			if (gotmail)
2774558Seric 			{
27858151Seric 				message("503 Sender already specified");
2794558Seric 				break;
2804558Seric 			}
2819339Seric 			if (InChild)
2829339Seric 			{
28336230Skarels 				errno = 0;
28458151Seric 				syserr("503 Nested MAIL command: MAIL %s", p);
28558069Seric 				finis();
2869339Seric 			}
2879339Seric 
2889339Seric 			/* fork a subprocess to process this command */
28955012Seric 			if (runinchild("SMTP-MAIL", e) > 0)
2909339Seric 				break;
29158323Seric 			if (protocol == NULL)
29258323Seric 				protocol = "SMTP";
29358323Seric 			define('r', protocol, e);
29459016Seric 			define('s', sendinghost, e);
29555012Seric 			initsys(e);
29659747Seric 			nrcpts = 0;
29757389Seric 			setproctitle("%s %s: %s", e->e_id, CurHostName, inp);
2989339Seric 
2999339Seric 			/* child -- go do the processing */
3004549Seric 			p = skipword(p, "from");
3014549Seric 			if (p == NULL)
3024549Seric 				break;
30357977Seric 			if (setjmp(TopFrame) > 0)
30458147Seric 			{
30558147Seric 				/* this failed -- undo work */
30658147Seric 				if (InChild)
30759058Seric 				{
30859058Seric 					QuickAbort = FALSE;
30959058Seric 					SuprErrs = TRUE;
31058147Seric 					finis();
31159058Seric 				}
31257977Seric 				break;
31358147Seric 			}
31457977Seric 			QuickAbort = TRUE;
31558333Seric 
31658333Seric 			/* must parse sender first */
31758333Seric 			delimptr = NULL;
31858704Seric 			setsender(p, e, &delimptr, FALSE);
31958333Seric 			p = delimptr;
32058333Seric 			if (p != NULL && *p != '\0')
32158333Seric 				*p++ = '\0';
32258333Seric 
32358333Seric 			/* now parse ESMTP arguments */
32458333Seric 			msize = 0;
32558333Seric 			for (; p != NULL && *p != '\0'; p++)
32658333Seric 			{
32758333Seric 				char *kp;
32858333Seric 				char *vp;
32958333Seric 
33058333Seric 				/* locate the beginning of the keyword */
33158333Seric 				while (isascii(*p) && isspace(*p))
33258333Seric 					p++;
33358333Seric 				if (*p == '\0')
33458333Seric 					break;
33558333Seric 				kp = p;
33658333Seric 
33758333Seric 				/* skip to the value portion */
33858333Seric 				while (isascii(*p) && isalnum(*p) || *p == '-')
33958333Seric 					p++;
34058333Seric 				if (*p == '=')
34158333Seric 				{
34258333Seric 					*p++ = '\0';
34358333Seric 					vp = p;
34458333Seric 
34558333Seric 					/* skip to the end of the value */
34658333Seric 					while (*p != '\0' && *p != ' ' &&
34758333Seric 					       !(isascii(*p) && iscntrl(*p)) &&
34858333Seric 					       *p != '=')
34958333Seric 						p++;
35058333Seric 				}
35158333Seric 
35258333Seric 				if (*p != '\0')
35358333Seric 					*p++ = '\0';
35458333Seric 
35558333Seric 				if (tTd(19, 1))
35658333Seric 					printf("MAIL: got arg %s=%s\n", kp,
35758333Seric 						vp == NULL ? "<null>" : vp);
35858333Seric 
35958333Seric 				if (strcasecmp(kp, "size") == 0)
36058333Seric 				{
36159093Seric 					if (vp == NULL)
36258333Seric 					{
36358333Seric 						usrerr("501 SIZE requires a value");
36458333Seric 						/* NOTREACHED */
36558333Seric 					}
36658333Seric 					msize = atol(vp);
36758333Seric 				}
36859093Seric 				else if (strcasecmp(kp, "body") == 0)
36959093Seric 				{
37059093Seric 					if (vp == NULL)
37159093Seric 					{
37259093Seric 						usrerr("501 BODY requires a value");
37359093Seric 						/* NOTREACHED */
37459093Seric 					}
37559093Seric # ifdef MIME
37659093Seric 					if (strcasecmp(vp, "8bitmime") == 0)
37759093Seric 					{
37859093Seric 						e->e_bodytype = "8BITMIME";
37959709Seric 						SevenBit = FALSE;
38059093Seric 					}
38159093Seric 					else if (strcasecmp(vp, "7bit") == 0)
38259093Seric 					{
38359093Seric 						e->e_bodytype = "7BIT";
38459709Seric 						SevenBit = TRUE;
38559093Seric 					}
38659093Seric 					else
38759093Seric 					{
38859093Seric 						usrerr("501 Unknown BODY type %s",
38959093Seric 							vp);
39059093Seric 					}
39159093Seric # endif
39259093Seric 				}
39358333Seric 				else
39458333Seric 				{
39558333Seric 					usrerr("501 %s parameter unrecognized", kp);
39658333Seric 					/* NOTREACHED */
39758333Seric 				}
39858333Seric 			}
39959284Seric 
40059284Seric 			if (MaxMessageSize > 0 && msize > MaxMessageSize)
40159284Seric 			{
40259284Seric 				usrerr("552 Message size exceeds fixed maximum message size (%ld)",
40359284Seric 					MaxMessageSize);
40459284Seric 				/* NOTREACHED */
40559284Seric 			}
40658333Seric 
40758333Seric 			if (!enoughspace(msize))
40858333Seric 			{
40958333Seric 				message("452 Insufficient disk space; try again later");
41058333Seric 				break;
41158333Seric 			}
41258151Seric 			message("250 Sender ok");
41358147Seric 			gotmail = TRUE;
4144549Seric 			break;
4154549Seric 
4164976Seric 		  case CMDRCPT:		/* rcpt -- designate recipient */
41758850Seric 			if (!gotmail)
41858850Seric 			{
41958850Seric 				usrerr("503 Need MAIL before RCPT");
42058850Seric 				break;
42158850Seric 			}
422*61093Seric 			SmtpPhase = "server RCPT";
42312612Seric 			if (setjmp(TopFrame) > 0)
42414785Seric 			{
42555012Seric 				e->e_flags &= ~EF_FATALERRS;
42612612Seric 				break;
42714785Seric 			}
42812612Seric 			QuickAbort = TRUE;
42951951Seric 			LogUsrErrs = TRUE;
43058093Seric 
43159699Seric 			if (e->e_sendmode != SM_DELIVER)
43259699Seric 				e->e_flags |= EF_VRFYONLY;
43358919Seric 
4344549Seric 			p = skipword(p, "to");
4354549Seric 			if (p == NULL)
4364549Seric 				break;
43758333Seric 			a = parseaddr(p, (ADDRESS *) NULL, 1, ' ', NULL, e);
43812612Seric 			if (a == NULL)
43912612Seric 				break;
44016886Seric 			a->q_flags |= QPRIMARY;
44155012Seric 			a = recipient(a, &e->e_sendqueue, e);
44212612Seric 			if (Errors != 0)
44312612Seric 				break;
44412612Seric 
44512612Seric 			/* no errors during parsing, but might be a duplicate */
44655012Seric 			e->e_to = p;
44712612Seric 			if (!bitset(QBADADDR, a->q_flags))
44859747Seric 			{
44958151Seric 				message("250 Recipient ok");
45059747Seric 				nrcpts++;
45159747Seric 			}
45212612Seric 			else
4534549Seric 			{
45412612Seric 				/* punt -- should keep message in ADDRESS.... */
45558151Seric 				message("550 Addressee unknown");
4564549Seric 			}
45755012Seric 			e->e_to = NULL;
4584549Seric 			break;
4594549Seric 
4604549Seric 		  case CMDDATA:		/* data -- text of mail */
461*61093Seric 			SmtpPhase = "server DATA";
46258109Seric 			if (!gotmail)
4634549Seric 			{
46458151Seric 				message("503 Need MAIL command");
4654976Seric 				break;
4664549Seric 			}
46755012Seric 			else if (e->e_nrcpts <= 0)
4684549Seric 			{
46958151Seric 				message("503 Need RCPT (recipient)");
4704976Seric 				break;
4714549Seric 			}
4724976Seric 
47358929Seric 			/* check to see if we need to re-expand aliases */
47458929Seric 			for (a = e->e_sendqueue; a != NULL; a = a->q_next)
47558929Seric 			{
47658929Seric 				if (bitset(QVERIFIED, a->q_flags))
47758929Seric 					break;
47858929Seric 			}
47958929Seric 
4804976Seric 			/* collect the text of the message */
48124943Seric 			SmtpPhase = "collect";
48258929Seric 			collect(TRUE, a != NULL, e);
48359730Seric 			e->e_flags &= ~EF_FATALERRS;
4844976Seric 			if (Errors != 0)
48559747Seric 				goto abortmessage;
4864976Seric 
4878238Seric 			/*
4888238Seric 			**  Arrange to send to everyone.
4898238Seric 			**	If sending to multiple people, mail back
4908238Seric 			**		errors rather than reporting directly.
4918238Seric 			**	In any case, don't mail back errors for
4928238Seric 			**		anything that has happened up to
4938238Seric 			**		now (the other end will do this).
49410197Seric 			**	Truncate our transcript -- the mail has gotten
49510197Seric 			**		to us successfully, and if we have
49610197Seric 			**		to mail this back, it will be easier
49710197Seric 			**		on the reader.
4988238Seric 			**	Then send to everyone.
4998238Seric 			**	Finally give a reply code.  If an error has
5008238Seric 			**		already been given, don't mail a
5018238Seric 			**		message back.
5029339Seric 			**	We goose error returns by clearing error bit.
5038238Seric 			*/
5048238Seric 
50524943Seric 			SmtpPhase = "delivery";
50659747Seric 			if (nrcpts != 1 && a == NULL)
5079378Seric 			{
5089378Seric 				HoldErrs = TRUE;
50958734Seric 				e->e_errormode = EM_MAIL;
5109378Seric 			}
51155012Seric 			e->e_xfp = freopen(queuename(e, 'x'), "w", e->e_xfp);
51258714Seric 			id = e->e_id;
5134976Seric 
5144976Seric 			/* send to all recipients */
51558919Seric 			sendall(e, a == NULL ? SM_DEFAULT : SM_QUEUE);
51655012Seric 			e->e_to = NULL;
5174976Seric 
51823516Seric 			/* save statistics */
51955012Seric 			markstats(e, (ADDRESS *) NULL);
52023516Seric 
5218238Seric 			/* issue success if appropriate and reset */
5228238Seric 			if (Errors == 0 || HoldErrs)
52358855Seric 				message("250 %s Message accepted for delivery", id);
52459747Seric 
52559747Seric 			if (bitset(EF_FATALERRS, e->e_flags) && !HoldErrs)
52659730Seric 			{
52759730Seric 				/* avoid sending back an extra message */
52859730Seric 				e->e_flags &= ~EF_FATALERRS;
52959747Seric 				e->e_flags |= EF_CLRQUEUE;
53059730Seric 			}
5318238Seric 			else
53258919Seric 			{
53359747Seric 				/* from now on, we have to operate silently */
53459747Seric 				HoldErrs = TRUE;
53559747Seric 				e->e_errormode = EM_MAIL;
53659747Seric 
53759730Seric 				/* if we just queued, poke it */
53859730Seric 				if (a != NULL && e->e_sendmode != SM_QUEUE)
53959730Seric 				{
54059730Seric 					unlockqueue(e);
54159730Seric 					dowork(id, TRUE, TRUE, e);
54259730Seric 					e->e_id = NULL;
54359730Seric 				}
54458919Seric 			}
54558883Seric 
54659747Seric   abortmessage:
5479339Seric 			/* if in a child, pop back to our parent */
5489339Seric 			if (InChild)
5499339Seric 				finis();
55024943Seric 
55124943Seric 			/* clean up a bit */
55258109Seric 			gotmail = FALSE;
55355012Seric 			dropenvelope(e);
55458179Seric 			CurEnv = e = newenvelope(e, CurEnv);
55555012Seric 			e->e_flags = BlankEnvelope.e_flags;
5564549Seric 			break;
5574549Seric 
5584549Seric 		  case CMDRSET:		/* rset -- reset state */
55958151Seric 			message("250 Reset state");
5609339Seric 			if (InChild)
5619339Seric 				finis();
56258109Seric 
56358109Seric 			/* clean up a bit */
56458109Seric 			gotmail = FALSE;
56558109Seric 			dropenvelope(e);
56658179Seric 			CurEnv = e = newenvelope(e, CurEnv);
5679339Seric 			break;
5684549Seric 
5694549Seric 		  case CMDVRFY:		/* vrfy -- verify address */
57058092Seric 		  case CMDEXPN:		/* expn -- expand address */
57158092Seric 			vrfy = c->cmdcode == CMDVRFY;
57258092Seric 			if (bitset(vrfy ? PRIV_NOVRFY : PRIV_NOEXPN,
57358092Seric 						PrivacyFlags))
57458082Seric 			{
57558412Seric 				if (vrfy)
57658412Seric 					message("252 Who's to say?");
57758412Seric 				else
57858412Seric 					message("502 That's none of your business");
57958082Seric 				break;
58058082Seric 			}
58158082Seric 			else if (!gothello &&
58258092Seric 				 bitset(vrfy ? PRIV_NEEDVRFYHELO : PRIV_NEEDEXPNHELO,
58358092Seric 						PrivacyFlags))
58458082Seric 			{
58558151Seric 				message("503 I demand that you introduce yourself first");
58658082Seric 				break;
58758082Seric 			}
58858092Seric 			if (runinchild(vrfy ? "SMTP-VRFY" : "SMTP-EXPN", e) > 0)
5899339Seric 				break;
59055173Seric #ifdef LOG
59158020Seric 			if (LogLevel > 5)
59255173Seric 				syslog(LOG_INFO, "%s: %s", CurHostName, inp);
59355173Seric #endif
5945003Seric 			vrfyqueue = NULL;
5957762Seric 			QuickAbort = TRUE;
59658092Seric 			if (vrfy)
59758092Seric 				e->e_flags |= EF_VRFYONLY;
59858082Seric 			(void) sendtolist(p, (ADDRESS *) NULL, &vrfyqueue, e);
5997762Seric 			if (Errors != 0)
6009339Seric 			{
6019339Seric 				if (InChild)
6029339Seric 					finis();
6037762Seric 				break;
6049339Seric 			}
6055003Seric 			while (vrfyqueue != NULL)
6065003Seric 			{
6075003Seric 				register ADDRESS *a = vrfyqueue->q_next;
6085003Seric 				char *code;
6095003Seric 
6107685Seric 				while (a != NULL && bitset(QDONTSEND|QBADADDR, a->q_flags))
6115003Seric 					a = a->q_next;
6125003Seric 
6137685Seric 				if (!bitset(QDONTSEND|QBADADDR, vrfyqueue->q_flags))
61458151Seric 					printvrfyaddr(vrfyqueue, a == NULL);
6155003Seric 				else if (a == NULL)
61658151Seric 					message("554 Self destructive alias loop");
6175003Seric 				vrfyqueue = a;
6185003Seric 			}
6199339Seric 			if (InChild)
6209339Seric 				finis();
6214549Seric 			break;
6224549Seric 
6234549Seric 		  case CMDHELP:		/* help -- give user info */
6244577Seric 			help(p);
6254549Seric 			break;
6264549Seric 
6274549Seric 		  case CMDNOOP:		/* noop -- do nothing */
62858151Seric 			message("200 OK");
6294549Seric 			break;
6304549Seric 
6314549Seric 		  case CMDQUIT:		/* quit -- leave mail */
63258151Seric 			message("221 %s closing connection", MyHostName);
63361051Seric 
63461051Seric 			/* avoid future 050 messages */
63561051Seric 			Verbose = FALSE;
63661051Seric 
6379339Seric 			if (InChild)
6389339Seric 				ExitStat = EX_QUIT;
6394549Seric 			finis();
6404549Seric 
6418544Seric 		  case CMDVERB:		/* set verbose mode */
64259957Seric 			if (bitset(PRIV_NOEXPN, PrivacyFlags))
64359957Seric 			{
64459957Seric 				/* this would give out the same info */
64559957Seric 				message("502 Verbose unavailable");
64659957Seric 				break;
64759957Seric 			}
6488544Seric 			Verbose = TRUE;
64958734Seric 			e->e_sendmode = SM_DELIVER;
65059957Seric 			message("250 Verbose mode");
6518544Seric 			break;
6528544Seric 
6539314Seric 		  case CMDONEX:		/* doing one transaction only */
6549378Seric 			OneXact = TRUE;
65559957Seric 			message("250 Only one transaction");
6569314Seric 			break;
6579314Seric 
65836230Skarels # ifdef SMTPDEBUG
6599339Seric 		  case CMDDBGQSHOW:	/* show queues */
6606907Seric 			printf("Send Queue=");
66155012Seric 			printaddr(e->e_sendqueue, TRUE);
6625003Seric 			break;
6637275Seric 
6647275Seric 		  case CMDDBGDEBUG:	/* set debug mode */
6657676Seric 			tTsetup(tTdvect, sizeof tTdvect, "0-99.1");
6667676Seric 			tTflag(p);
66758151Seric 			message("200 Debug set");
6687275Seric 			break;
6697275Seric 
67036230Skarels # else /* not SMTPDEBUG */
67124945Seric 
67236230Skarels 		  case CMDDBGQSHOW:	/* show queues */
67336230Skarels 		  case CMDDBGDEBUG:	/* set debug mode */
67436233Skarels # ifdef LOG
67558308Seric 			if (LogLevel > 0)
67636230Skarels 				syslog(LOG_NOTICE,
67758020Seric 				    "\"%s\" command from %s (%s)",
67836230Skarels 				    c->cmdname, RealHostName,
67958755Seric 				    anynet_ntoa(&RealHostAddr));
68036233Skarels # endif
68136230Skarels 			/* FALL THROUGH */
68236230Skarels # endif /* SMTPDEBUG */
68336230Skarels 
6844549Seric 		  case CMDERROR:	/* unknown command */
68558151Seric 			message("500 Command unrecognized");
6864549Seric 			break;
6874549Seric 
6884549Seric 		  default:
68936230Skarels 			errno = 0;
69058151Seric 			syserr("500 smtp: unknown code %d", c->cmdcode);
6914549Seric 			break;
6924549Seric 		}
6934549Seric 	}
6944549Seric }
6954549Seric /*
6964549Seric **  SKIPWORD -- skip a fixed word.
6974549Seric **
6984549Seric **	Parameters:
6994549Seric **		p -- place to start looking.
7004549Seric **		w -- word to skip.
7014549Seric **
7024549Seric **	Returns:
7034549Seric **		p following w.
7044549Seric **		NULL on error.
7054549Seric **
7064549Seric **	Side Effects:
7074549Seric **		clobbers the p data area.
7084549Seric */
7094549Seric 
7104549Seric static char *
7114549Seric skipword(p, w)
7124549Seric 	register char *p;
7134549Seric 	char *w;
7144549Seric {
7154549Seric 	register char *q;
7164549Seric 
7174549Seric 	/* find beginning of word */
71858050Seric 	while (isascii(*p) && isspace(*p))
7194549Seric 		p++;
7204549Seric 	q = p;
7214549Seric 
7224549Seric 	/* find end of word */
72358050Seric 	while (*p != '\0' && *p != ':' && !(isascii(*p) && isspace(*p)))
7244549Seric 		p++;
72558050Seric 	while (isascii(*p) && isspace(*p))
7264549Seric 		*p++ = '\0';
7274549Seric 	if (*p != ':')
7284549Seric 	{
7294549Seric 	  syntax:
73058151Seric 		message("501 Syntax error");
7314549Seric 		Errors++;
7324549Seric 		return (NULL);
7334549Seric 	}
7344549Seric 	*p++ = '\0';
73558050Seric 	while (isascii(*p) && isspace(*p))
7364549Seric 		p++;
7374549Seric 
7384549Seric 	/* see if the input word matches desired word */
73933725Sbostic 	if (strcasecmp(q, w))
7404549Seric 		goto syntax;
7414549Seric 
7424549Seric 	return (p);
7434549Seric }
7444577Seric /*
74558151Seric **  PRINTVRFYADDR -- print an entry in the verify queue
74658151Seric **
74758151Seric **	Parameters:
74858151Seric **		a -- the address to print
74958151Seric **		last -- set if this is the last one.
75058151Seric **
75158151Seric **	Returns:
75258151Seric **		none.
75358151Seric **
75458151Seric **	Side Effects:
75558151Seric **		Prints the appropriate 250 codes.
75658151Seric */
75758151Seric 
75858151Seric printvrfyaddr(a, last)
75958151Seric 	register ADDRESS *a;
76058151Seric 	bool last;
76158151Seric {
76258151Seric 	char fmtbuf[20];
76358151Seric 
76458151Seric 	strcpy(fmtbuf, "250");
76558151Seric 	fmtbuf[3] = last ? ' ' : '-';
76658151Seric 
76759746Seric 	if (a->q_fullname == NULL)
76859746Seric 	{
76959746Seric 		if (strchr(a->q_user, '@') == NULL)
77059746Seric 			strcpy(&fmtbuf[4], "<%s@%s>");
77159746Seric 		else
77259746Seric 			strcpy(&fmtbuf[4], "<%s>");
77359746Seric 		message(fmtbuf, a->q_user, MyHostName);
77459746Seric 	}
77558151Seric 	else
77658151Seric 	{
77759746Seric 		if (strchr(a->q_user, '@') == NULL)
77859746Seric 			strcpy(&fmtbuf[4], "%s <%s@%s>");
77959746Seric 		else
78059746Seric 			strcpy(&fmtbuf[4], "%s <%s>");
78159746Seric 		message(fmtbuf, a->q_fullname, a->q_user, MyHostName);
78258151Seric 	}
78358151Seric }
78458151Seric /*
7854577Seric **  HELP -- implement the HELP command.
7864577Seric **
7874577Seric **	Parameters:
7884577Seric **		topic -- the topic we want help for.
7894577Seric **
7904577Seric **	Returns:
7914577Seric **		none.
7924577Seric **
7934577Seric **	Side Effects:
7944577Seric **		outputs the help file to message output.
7954577Seric */
7964577Seric 
7974577Seric help(topic)
7984577Seric 	char *topic;
7994577Seric {
8004577Seric 	register FILE *hf;
8014577Seric 	int len;
8024577Seric 	char buf[MAXLINE];
8034577Seric 	bool noinfo;
8044577Seric 
8058269Seric 	if (HelpFile == NULL || (hf = fopen(HelpFile, "r")) == NULL)
8064577Seric 	{
8074577Seric 		/* no help */
80811931Seric 		errno = 0;
80958151Seric 		message("502 HELP not implemented");
8104577Seric 		return;
8114577Seric 	}
8124577Seric 
81349669Seric 	if (topic == NULL || *topic == '\0')
81449669Seric 		topic = "smtp";
81549669Seric 	else
81649669Seric 		makelower(topic);
81749669Seric 
8184577Seric 	len = strlen(topic);
8194577Seric 	noinfo = TRUE;
8204577Seric 
8214577Seric 	while (fgets(buf, sizeof buf, hf) != NULL)
8224577Seric 	{
8234577Seric 		if (strncmp(buf, topic, len) == 0)
8244577Seric 		{
8254577Seric 			register char *p;
8264577Seric 
82756795Seric 			p = strchr(buf, '\t');
8284577Seric 			if (p == NULL)
8294577Seric 				p = buf;
8304577Seric 			else
8314577Seric 				p++;
8324577Seric 			fixcrlf(p, TRUE);
83358151Seric 			message("214-%s", p);
8344577Seric 			noinfo = FALSE;
8354577Seric 		}
8364577Seric 	}
8374577Seric 
8384577Seric 	if (noinfo)
83958151Seric 		message("504 HELP topic unknown");
8404577Seric 	else
84158151Seric 		message("214 End of HELP info");
8424628Seric 	(void) fclose(hf);
8434577Seric }
8448544Seric /*
8459339Seric **  RUNINCHILD -- return twice -- once in the child, then in the parent again
8469339Seric **
8479339Seric **	Parameters:
8489339Seric **		label -- a string used in error messages
8499339Seric **
8509339Seric **	Returns:
8519339Seric **		zero in the child
8529339Seric **		one in the parent
8539339Seric **
8549339Seric **	Side Effects:
8559339Seric **		none.
8569339Seric */
8578544Seric 
85855012Seric runinchild(label, e)
8599339Seric 	char *label;
86055012Seric 	register ENVELOPE *e;
8619339Seric {
8629339Seric 	int childpid;
8639339Seric 
86416158Seric 	if (!OneXact)
8659339Seric 	{
86616158Seric 		childpid = dofork();
86716158Seric 		if (childpid < 0)
86816158Seric 		{
86916158Seric 			syserr("%s: cannot fork", label);
87016158Seric 			return (1);
87116158Seric 		}
87216158Seric 		if (childpid > 0)
87316158Seric 		{
87416158Seric 			auto int st;
8759339Seric 
87616158Seric 			/* parent -- wait for child to complete */
877*61093Seric 			setproctitle("server %s child wait", CurHostName);
87816158Seric 			st = waitfor(childpid);
87916158Seric 			if (st == -1)
88016158Seric 				syserr("%s: lost child", label);
8819339Seric 
88216158Seric 			/* if we exited on a QUIT command, complete the process */
88316158Seric 			if (st == (EX_QUIT << 8))
88416158Seric 				finis();
8859339Seric 
88616158Seric 			return (1);
88716158Seric 		}
88816158Seric 		else
88916158Seric 		{
89016158Seric 			/* child */
89116158Seric 			InChild = TRUE;
89225050Seric 			QuickAbort = FALSE;
89355012Seric 			clearenvelope(e, FALSE);
89416158Seric 		}
8959339Seric 	}
89615256Seric 
89716158Seric 	/* open alias database */
89860537Seric 	initmaps(FALSE, e);
89916158Seric 
90016158Seric 	return (0);
9019339Seric }
9029339Seric 
90356795Seric # endif /* SMTP */
904