122712Sdist /*
234921Sbostic  * Copyright (c) 1983 Eric P. Allman
3*62532Sbostic  * Copyright (c) 1988, 1993
4*62532Sbostic  *	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*62532Sbostic static char sccsid[] = "@(#)srvrsmtp.c	8.1 (Berkeley) 06/07/93 (with SMTP)";
1433731Sbostic #else
15*62532Sbostic static char sccsid[] = "@(#)srvrsmtp.c	8.1 (Berkeley) 06/07/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;
12361093Seric 	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 */
15161093Seric 		SmtpPhase = "server cmd read";
15261093Seric 		setproctitle("server %s cmd read", CurHostName);
15361093Seric 		p = sfgets(inp, sizeof inp, InChannel, TimeOuts.to_nextcommand,
15461093Seric 				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";
21661093Seric 				SmtpPhase = "server EHLO";
21758323Seric 			}
21858323Seric 			else
21958323Seric 			{
22058323Seric 				protocol = "SMTP";
22161093Seric 				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 */
25561093Seric 			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 			}
42261093Seric 			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 */
46161093Seric 			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;
59862373Seric 			while (*p != '\0' && isascii(*p) && isspace(*p))
59962373Seric 				*p++;
60062373Seric 			if (*p == '\0')
60162373Seric 			{
60262373Seric 				message("501 Argument required");
60362373Seric 				Errors++;
60462373Seric 			}
60562373Seric 			else
60662373Seric 			{
60762373Seric 				(void) sendtolist(p, (ADDRESS *) NULL,
60862373Seric 						  &vrfyqueue, e);
60962373Seric 			}
6107762Seric 			if (Errors != 0)
6119339Seric 			{
6129339Seric 				if (InChild)
6139339Seric 					finis();
6147762Seric 				break;
6159339Seric 			}
61662373Seric 			if (vrfyqueue == NULL)
61762373Seric 			{
61862373Seric 				message("554 Nothing to %s", vrfy ? "VRFY" : "EXPN");
61962373Seric 			}
6205003Seric 			while (vrfyqueue != NULL)
6215003Seric 			{
6225003Seric 				register ADDRESS *a = vrfyqueue->q_next;
6235003Seric 
6247685Seric 				while (a != NULL && bitset(QDONTSEND|QBADADDR, a->q_flags))
6255003Seric 					a = a->q_next;
6265003Seric 
6277685Seric 				if (!bitset(QDONTSEND|QBADADDR, vrfyqueue->q_flags))
62858151Seric 					printvrfyaddr(vrfyqueue, a == NULL);
6295003Seric 				else if (a == NULL)
63058151Seric 					message("554 Self destructive alias loop");
6315003Seric 				vrfyqueue = a;
6325003Seric 			}
6339339Seric 			if (InChild)
6349339Seric 				finis();
6354549Seric 			break;
6364549Seric 
6374549Seric 		  case CMDHELP:		/* help -- give user info */
6384577Seric 			help(p);
6394549Seric 			break;
6404549Seric 
6414549Seric 		  case CMDNOOP:		/* noop -- do nothing */
64258151Seric 			message("200 OK");
6434549Seric 			break;
6444549Seric 
6454549Seric 		  case CMDQUIT:		/* quit -- leave mail */
64658151Seric 			message("221 %s closing connection", MyHostName);
64761051Seric 
64861051Seric 			/* avoid future 050 messages */
64961051Seric 			Verbose = FALSE;
65061051Seric 
6519339Seric 			if (InChild)
6529339Seric 				ExitStat = EX_QUIT;
6534549Seric 			finis();
6544549Seric 
6558544Seric 		  case CMDVERB:		/* set verbose mode */
65659957Seric 			if (bitset(PRIV_NOEXPN, PrivacyFlags))
65759957Seric 			{
65859957Seric 				/* this would give out the same info */
65959957Seric 				message("502 Verbose unavailable");
66059957Seric 				break;
66159957Seric 			}
6628544Seric 			Verbose = TRUE;
66358734Seric 			e->e_sendmode = SM_DELIVER;
66459957Seric 			message("250 Verbose mode");
6658544Seric 			break;
6668544Seric 
6679314Seric 		  case CMDONEX:		/* doing one transaction only */
6689378Seric 			OneXact = TRUE;
66959957Seric 			message("250 Only one transaction");
6709314Seric 			break;
6719314Seric 
67236230Skarels # ifdef SMTPDEBUG
6739339Seric 		  case CMDDBGQSHOW:	/* show queues */
6746907Seric 			printf("Send Queue=");
67555012Seric 			printaddr(e->e_sendqueue, TRUE);
6765003Seric 			break;
6777275Seric 
6787275Seric 		  case CMDDBGDEBUG:	/* set debug mode */
6797676Seric 			tTsetup(tTdvect, sizeof tTdvect, "0-99.1");
6807676Seric 			tTflag(p);
68158151Seric 			message("200 Debug set");
6827275Seric 			break;
6837275Seric 
68436230Skarels # else /* not SMTPDEBUG */
68524945Seric 
68636230Skarels 		  case CMDDBGQSHOW:	/* show queues */
68736230Skarels 		  case CMDDBGDEBUG:	/* set debug mode */
68836233Skarels # ifdef LOG
68958308Seric 			if (LogLevel > 0)
69036230Skarels 				syslog(LOG_NOTICE,
69158020Seric 				    "\"%s\" command from %s (%s)",
69236230Skarels 				    c->cmdname, RealHostName,
69358755Seric 				    anynet_ntoa(&RealHostAddr));
69436233Skarels # endif
69536230Skarels 			/* FALL THROUGH */
69636230Skarels # endif /* SMTPDEBUG */
69736230Skarels 
6984549Seric 		  case CMDERROR:	/* unknown command */
69958151Seric 			message("500 Command unrecognized");
7004549Seric 			break;
7014549Seric 
7024549Seric 		  default:
70336230Skarels 			errno = 0;
70458151Seric 			syserr("500 smtp: unknown code %d", c->cmdcode);
7054549Seric 			break;
7064549Seric 		}
7074549Seric 	}
7084549Seric }
7094549Seric /*
7104549Seric **  SKIPWORD -- skip a fixed word.
7114549Seric **
7124549Seric **	Parameters:
7134549Seric **		p -- place to start looking.
7144549Seric **		w -- word to skip.
7154549Seric **
7164549Seric **	Returns:
7174549Seric **		p following w.
7184549Seric **		NULL on error.
7194549Seric **
7204549Seric **	Side Effects:
7214549Seric **		clobbers the p data area.
7224549Seric */
7234549Seric 
7244549Seric static char *
7254549Seric skipword(p, w)
7264549Seric 	register char *p;
7274549Seric 	char *w;
7284549Seric {
7294549Seric 	register char *q;
7304549Seric 
7314549Seric 	/* find beginning of word */
73258050Seric 	while (isascii(*p) && isspace(*p))
7334549Seric 		p++;
7344549Seric 	q = p;
7354549Seric 
7364549Seric 	/* find end of word */
73758050Seric 	while (*p != '\0' && *p != ':' && !(isascii(*p) && isspace(*p)))
7384549Seric 		p++;
73958050Seric 	while (isascii(*p) && isspace(*p))
7404549Seric 		*p++ = '\0';
7414549Seric 	if (*p != ':')
7424549Seric 	{
7434549Seric 	  syntax:
74462373Seric 		message("501 Syntax error in parameters");
7454549Seric 		Errors++;
7464549Seric 		return (NULL);
7474549Seric 	}
7484549Seric 	*p++ = '\0';
74958050Seric 	while (isascii(*p) && isspace(*p))
7504549Seric 		p++;
7514549Seric 
75262373Seric 	if (*p == '\0')
75362373Seric 		goto syntax;
75462373Seric 
7554549Seric 	/* see if the input word matches desired word */
75633725Sbostic 	if (strcasecmp(q, w))
7574549Seric 		goto syntax;
7584549Seric 
7594549Seric 	return (p);
7604549Seric }
7614577Seric /*
76258151Seric **  PRINTVRFYADDR -- print an entry in the verify queue
76358151Seric **
76458151Seric **	Parameters:
76558151Seric **		a -- the address to print
76658151Seric **		last -- set if this is the last one.
76758151Seric **
76858151Seric **	Returns:
76958151Seric **		none.
77058151Seric **
77158151Seric **	Side Effects:
77258151Seric **		Prints the appropriate 250 codes.
77358151Seric */
77458151Seric 
77558151Seric printvrfyaddr(a, last)
77658151Seric 	register ADDRESS *a;
77758151Seric 	bool last;
77858151Seric {
77958151Seric 	char fmtbuf[20];
78058151Seric 
78158151Seric 	strcpy(fmtbuf, "250");
78258151Seric 	fmtbuf[3] = last ? ' ' : '-';
78358151Seric 
78459746Seric 	if (a->q_fullname == NULL)
78559746Seric 	{
78659746Seric 		if (strchr(a->q_user, '@') == NULL)
78759746Seric 			strcpy(&fmtbuf[4], "<%s@%s>");
78859746Seric 		else
78959746Seric 			strcpy(&fmtbuf[4], "<%s>");
79059746Seric 		message(fmtbuf, a->q_user, MyHostName);
79159746Seric 	}
79258151Seric 	else
79358151Seric 	{
79459746Seric 		if (strchr(a->q_user, '@') == NULL)
79559746Seric 			strcpy(&fmtbuf[4], "%s <%s@%s>");
79659746Seric 		else
79759746Seric 			strcpy(&fmtbuf[4], "%s <%s>");
79859746Seric 		message(fmtbuf, a->q_fullname, a->q_user, MyHostName);
79958151Seric 	}
80058151Seric }
80158151Seric /*
8024577Seric **  HELP -- implement the HELP command.
8034577Seric **
8044577Seric **	Parameters:
8054577Seric **		topic -- the topic we want help for.
8064577Seric **
8074577Seric **	Returns:
8084577Seric **		none.
8094577Seric **
8104577Seric **	Side Effects:
8114577Seric **		outputs the help file to message output.
8124577Seric */
8134577Seric 
8144577Seric help(topic)
8154577Seric 	char *topic;
8164577Seric {
8174577Seric 	register FILE *hf;
8184577Seric 	int len;
8194577Seric 	char buf[MAXLINE];
8204577Seric 	bool noinfo;
8214577Seric 
8228269Seric 	if (HelpFile == NULL || (hf = fopen(HelpFile, "r")) == NULL)
8234577Seric 	{
8244577Seric 		/* no help */
82511931Seric 		errno = 0;
82658151Seric 		message("502 HELP not implemented");
8274577Seric 		return;
8284577Seric 	}
8294577Seric 
83049669Seric 	if (topic == NULL || *topic == '\0')
83149669Seric 		topic = "smtp";
83249669Seric 	else
83349669Seric 		makelower(topic);
83449669Seric 
8354577Seric 	len = strlen(topic);
8364577Seric 	noinfo = TRUE;
8374577Seric 
8384577Seric 	while (fgets(buf, sizeof buf, hf) != NULL)
8394577Seric 	{
8404577Seric 		if (strncmp(buf, topic, len) == 0)
8414577Seric 		{
8424577Seric 			register char *p;
8434577Seric 
84456795Seric 			p = strchr(buf, '\t');
8454577Seric 			if (p == NULL)
8464577Seric 				p = buf;
8474577Seric 			else
8484577Seric 				p++;
8494577Seric 			fixcrlf(p, TRUE);
85058151Seric 			message("214-%s", p);
8514577Seric 			noinfo = FALSE;
8524577Seric 		}
8534577Seric 	}
8544577Seric 
8554577Seric 	if (noinfo)
85658151Seric 		message("504 HELP topic unknown");
8574577Seric 	else
85858151Seric 		message("214 End of HELP info");
8594628Seric 	(void) fclose(hf);
8604577Seric }
8618544Seric /*
8629339Seric **  RUNINCHILD -- return twice -- once in the child, then in the parent again
8639339Seric **
8649339Seric **	Parameters:
8659339Seric **		label -- a string used in error messages
8669339Seric **
8679339Seric **	Returns:
8689339Seric **		zero in the child
8699339Seric **		one in the parent
8709339Seric **
8719339Seric **	Side Effects:
8729339Seric **		none.
8739339Seric */
8748544Seric 
87555012Seric runinchild(label, e)
8769339Seric 	char *label;
87755012Seric 	register ENVELOPE *e;
8789339Seric {
8799339Seric 	int childpid;
8809339Seric 
88116158Seric 	if (!OneXact)
8829339Seric 	{
88316158Seric 		childpid = dofork();
88416158Seric 		if (childpid < 0)
88516158Seric 		{
88616158Seric 			syserr("%s: cannot fork", label);
88716158Seric 			return (1);
88816158Seric 		}
88916158Seric 		if (childpid > 0)
89016158Seric 		{
89116158Seric 			auto int st;
8929339Seric 
89316158Seric 			/* parent -- wait for child to complete */
89461093Seric 			setproctitle("server %s child wait", CurHostName);
89516158Seric 			st = waitfor(childpid);
89616158Seric 			if (st == -1)
89716158Seric 				syserr("%s: lost child", label);
8989339Seric 
89916158Seric 			/* if we exited on a QUIT command, complete the process */
90016158Seric 			if (st == (EX_QUIT << 8))
90116158Seric 				finis();
9029339Seric 
90316158Seric 			return (1);
90416158Seric 		}
90516158Seric 		else
90616158Seric 		{
90716158Seric 			/* child */
90816158Seric 			InChild = TRUE;
90925050Seric 			QuickAbort = FALSE;
91055012Seric 			clearenvelope(e, FALSE);
91116158Seric 		}
9129339Seric 	}
91315256Seric 
91416158Seric 	/* open alias database */
91560537Seric 	initmaps(FALSE, e);
91616158Seric 
91716158Seric 	return (0);
9189339Seric }
9199339Seric 
92056795Seric # endif /* SMTP */
921