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*59016Seric static char sccsid[] = "@(#)srvrsmtp.c	6.40 (Berkeley) 04/09/93 (with SMTP)";
1433731Sbostic #else
15*59016Seric static char sccsid[] = "@(#)srvrsmtp.c	6.40 (Berkeley) 04/09/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 */
106*59016Seric 	char *sendinghost;		/* sending hostname */
10758333Seric 	long msize;			/* approximate maximum message size */
10858333Seric 	auto char *delimptr;
10958714Seric 	char *id;
1108544Seric 	char inp[MAXLINE];
11157232Seric 	char cmdbuf[MAXLINE];
1127124Seric 	extern char Version[];
11311151Seric 	extern char *macvalue();
11412612Seric 	extern ADDRESS *recipient();
11524943Seric 	extern ENVELOPE BlankEnvelope;
11624943Seric 	extern ENVELOPE *newenvelope();
11758755Seric 	extern char *anynet_ntoa();
1184549Seric 
1197363Seric 	if (OutChannel != stdout)
1207363Seric 	{
1217363Seric 		/* arrange for debugging output to go to remote host */
1227363Seric 		(void) close(1);
1237363Seric 		(void) dup(fileno(OutChannel));
1247363Seric 	}
12555012Seric 	settime(e);
12657642Seric 	CurHostName = RealHostName;
12757642Seric 	setproctitle("srvrsmtp %s", CurHostName);
12858050Seric 	expand("\201e", inp, &inp[sizeof inp], e);
12958151Seric 	message("220 %s", inp);
13024943Seric 	SmtpPhase = "startup";
13158330Seric 	protocol = NULL;
132*59016Seric 	sendinghost = macvalue('s', e);
13358082Seric 	gothello = FALSE;
13458330Seric 	gotmail = FALSE;
1354549Seric 	for (;;)
1364549Seric 	{
13712612Seric 		/* arrange for backout */
13812612Seric 		if (setjmp(TopFrame) > 0 && InChild)
13912612Seric 			finis();
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 */
15158109Seric 		p = sfgets(inp, sizeof inp, InChannel, TimeOuts.to_nextcommand);
1527356Seric 
1537685Seric 		/* handle errors */
1547356Seric 		if (p == NULL)
1557356Seric 		{
1564549Seric 			/* end of file, just die */
15758151Seric 			message("421 %s Lost input channel from %s",
15825050Seric 				MyHostName, CurHostName);
15955464Seric #ifdef LOG
16058020Seric 			if (LogLevel > 1)
16155464Seric 				syslog(LOG_NOTICE, "lost input channel from %s",
16255464Seric 					CurHostName);
16355464Seric #endif
16458069Seric 			if (InChild)
16558069Seric 				ExitStat = EX_QUIT;
1664549Seric 			finis();
1674549Seric 		}
1684549Seric 
1694549Seric 		/* clean up end of line */
1704558Seric 		fixcrlf(inp, TRUE);
1714549Seric 
1724713Seric 		/* echo command to transcript */
17355012Seric 		if (e->e_xfp != NULL)
17455012Seric 			fprintf(e->e_xfp, "<<< %s\n", inp);
1754713Seric 
1764549Seric 		/* break off command */
17758050Seric 		for (p = inp; isascii(*p) && isspace(*p); p++)
1784549Seric 			continue;
17957232Seric 		cmd = cmdbuf;
18058050Seric 		while (*p != '\0' &&
18158050Seric 		       !(isascii(*p) && isspace(*p)) &&
18258050Seric 		       cmd < &cmdbuf[sizeof cmdbuf - 2])
18324981Seric 			*cmd++ = *p++;
18424981Seric 		*cmd = '\0';
1854549Seric 
18625691Seric 		/* throw away leading whitespace */
18758050Seric 		while (isascii(*p) && isspace(*p))
18825691Seric 			p++;
18925691Seric 
1904549Seric 		/* decode command */
1914549Seric 		for (c = CmdTab; c->cmdname != NULL; c++)
1924549Seric 		{
19333725Sbostic 			if (!strcasecmp(c->cmdname, cmdbuf))
1944549Seric 				break;
1954549Seric 		}
1964549Seric 
19751954Seric 		/* reset errors */
19851954Seric 		errno = 0;
19951954Seric 
2004549Seric 		/* process command */
2014549Seric 		switch (c->cmdcode)
2024549Seric 		{
2034976Seric 		  case CMDHELO:		/* hello -- introduce yourself */
20458323Seric 		  case CMDEHLO:		/* extended hello */
20558323Seric 			if (c->cmdcode == CMDEHLO)
20658323Seric 			{
20758323Seric 				protocol = "ESMTP";
20858323Seric 				SmtpPhase = "EHLO";
20958323Seric 			}
21058323Seric 			else
21158323Seric 			{
21258323Seric 				protocol = "SMTP";
21358323Seric 				SmtpPhase = "HELO";
21458323Seric 			}
21525050Seric 			setproctitle("%s: %s", CurHostName, inp);
216*59016Seric 			sendinghost = newstr(p);
21758308Seric 			if (strcasecmp(p, RealHostName) != 0)
21811146Seric 			{
21958789Seric 				auth_warning(e, "Host %s claimed to be %s",
22058789Seric 					RealHostName, p);
22111146Seric 			}
22258957Seric 			p = macvalue('_', e);
22358957Seric 			if (p == NULL)
224*59016Seric 				p = RealHostName;
22558323Seric 
22658323Seric 			/* send ext. message -- old systems must ignore */
22758323Seric 			message("250-%s Hello %s, pleased to meet you",
22858957Seric 				MyHostName, p);
22958323Seric 			if (!bitset(PRIV_NOEXPN, PrivacyFlags))
23058323Seric 				message("250-EXPN");
23158338Seric 			message("250-SIZE");
23258323Seric 			message("250 HELP");
23358082Seric 			gothello = TRUE;
2344976Seric 			break;
2354976Seric 
2364549Seric 		  case CMDMAIL:		/* mail -- designate sender */
23724943Seric 			SmtpPhase = "MAIL";
23824943Seric 
2399314Seric 			/* check for validity of this command */
24058789Seric 			if (!gothello)
24158082Seric 			{
24258957Seric 				/* set sending host to our known value */
243*59016Seric 				if (sendinghost == NULL)
244*59016Seric 					sendinghost = RealHostName;
24558957Seric 
24658789Seric 				if (bitset(PRIV_NEEDMAILHELO, PrivacyFlags))
24758821Seric 				{
24858789Seric 					message("503 Polite people say HELO first");
24958821Seric 					break;
25058821Seric 				}
25158789Seric 				else
25258821Seric 				{
25358789Seric 					auth_warning(e,
25458789Seric 						"Host %s didn't use HELO protocol",
25558789Seric 						RealHostName);
25658821Seric 				}
25758082Seric 			}
25858109Seric 			if (gotmail)
2594558Seric 			{
26058151Seric 				message("503 Sender already specified");
2614558Seric 				break;
2624558Seric 			}
2639339Seric 			if (InChild)
2649339Seric 			{
26536230Skarels 				errno = 0;
26658151Seric 				syserr("503 Nested MAIL command: MAIL %s", p);
26758069Seric 				finis();
2689339Seric 			}
2699339Seric 
2709339Seric 			/* fork a subprocess to process this command */
27155012Seric 			if (runinchild("SMTP-MAIL", e) > 0)
2729339Seric 				break;
27358323Seric 			if (protocol == NULL)
27458323Seric 				protocol = "SMTP";
27558323Seric 			define('r', protocol, e);
276*59016Seric 			define('s', sendinghost, e);
27755012Seric 			initsys(e);
27857389Seric 			setproctitle("%s %s: %s", e->e_id, CurHostName, inp);
2799339Seric 
2809339Seric 			/* child -- go do the processing */
2814549Seric 			p = skipword(p, "from");
2824549Seric 			if (p == NULL)
2834549Seric 				break;
28457977Seric 			if (setjmp(TopFrame) > 0)
28558147Seric 			{
28658147Seric 				/* this failed -- undo work */
28758147Seric 				if (InChild)
28858147Seric 					finis();
28957977Seric 				break;
29058147Seric 			}
29157977Seric 			QuickAbort = TRUE;
29258333Seric 
29358333Seric 			/* must parse sender first */
29458333Seric 			delimptr = NULL;
29558704Seric 			setsender(p, e, &delimptr, FALSE);
29658333Seric 			p = delimptr;
29758333Seric 			if (p != NULL && *p != '\0')
29858333Seric 				*p++ = '\0';
29958333Seric 
30058333Seric 			/* now parse ESMTP arguments */
30158333Seric 			msize = 0;
30258333Seric 			for (; p != NULL && *p != '\0'; p++)
30358333Seric 			{
30458333Seric 				char *kp;
30558333Seric 				char *vp;
30658333Seric 
30758333Seric 				/* locate the beginning of the keyword */
30858333Seric 				while (isascii(*p) && isspace(*p))
30958333Seric 					p++;
31058333Seric 				if (*p == '\0')
31158333Seric 					break;
31258333Seric 				kp = p;
31358333Seric 
31458333Seric 				/* skip to the value portion */
31558333Seric 				while (isascii(*p) && isalnum(*p) || *p == '-')
31658333Seric 					p++;
31758333Seric 				if (*p == '=')
31858333Seric 				{
31958333Seric 					*p++ = '\0';
32058333Seric 					vp = p;
32158333Seric 
32258333Seric 					/* skip to the end of the value */
32358333Seric 					while (*p != '\0' && *p != ' ' &&
32458333Seric 					       !(isascii(*p) && iscntrl(*p)) &&
32558333Seric 					       *p != '=')
32658333Seric 						p++;
32758333Seric 				}
32858333Seric 
32958333Seric 				if (*p != '\0')
33058333Seric 					*p++ = '\0';
33158333Seric 
33258333Seric 				if (tTd(19, 1))
33358333Seric 					printf("MAIL: got arg %s=%s\n", kp,
33458333Seric 						vp == NULL ? "<null>" : vp);
33558333Seric 
33658333Seric 				if (strcasecmp(kp, "size") == 0)
33758333Seric 				{
33858333Seric 					if (kp == NULL)
33958333Seric 					{
34058333Seric 						usrerr("501 SIZE requires a value");
34158333Seric 						/* NOTREACHED */
34258333Seric 					}
34358333Seric 					msize = atol(vp);
34458333Seric 				}
34558333Seric 				else
34658333Seric 				{
34758333Seric 					usrerr("501 %s parameter unrecognized", kp);
34858333Seric 					/* NOTREACHED */
34958333Seric 				}
35058333Seric 			}
35158333Seric 
35258333Seric 			if (!enoughspace(msize))
35358333Seric 			{
35458333Seric 				message("452 Insufficient disk space; try again later");
35558333Seric 				break;
35658333Seric 			}
35758151Seric 			message("250 Sender ok");
35858147Seric 			gotmail = TRUE;
3594549Seric 			break;
3604549Seric 
3614976Seric 		  case CMDRCPT:		/* rcpt -- designate recipient */
36258850Seric 			if (!gotmail)
36358850Seric 			{
36458850Seric 				usrerr("503 Need MAIL before RCPT");
36558850Seric 				break;
36658850Seric 			}
36724943Seric 			SmtpPhase = "RCPT";
36857389Seric 			setproctitle("%s %s: %s", e->e_id, CurHostName, inp);
36912612Seric 			if (setjmp(TopFrame) > 0)
37014785Seric 			{
37155012Seric 				e->e_flags &= ~EF_FATALERRS;
37212612Seric 				break;
37314785Seric 			}
37412612Seric 			QuickAbort = TRUE;
37551951Seric 			LogUsrErrs = TRUE;
37658093Seric 
37758919Seric 			e->e_flags |= EF_VRFYONLY;
37858919Seric 
3794549Seric 			p = skipword(p, "to");
3804549Seric 			if (p == NULL)
3814549Seric 				break;
38258333Seric 			a = parseaddr(p, (ADDRESS *) NULL, 1, ' ', NULL, e);
38312612Seric 			if (a == NULL)
38412612Seric 				break;
38516886Seric 			a->q_flags |= QPRIMARY;
38655012Seric 			a = recipient(a, &e->e_sendqueue, e);
38712612Seric 			if (Errors != 0)
38812612Seric 				break;
38912612Seric 
39012612Seric 			/* no errors during parsing, but might be a duplicate */
39155012Seric 			e->e_to = p;
39212612Seric 			if (!bitset(QBADADDR, a->q_flags))
39358151Seric 				message("250 Recipient ok");
39412612Seric 			else
3954549Seric 			{
39612612Seric 				/* punt -- should keep message in ADDRESS.... */
39758151Seric 				message("550 Addressee unknown");
3984549Seric 			}
39955012Seric 			e->e_to = NULL;
4004549Seric 			break;
4014549Seric 
4024549Seric 		  case CMDDATA:		/* data -- text of mail */
40324943Seric 			SmtpPhase = "DATA";
40458109Seric 			if (!gotmail)
4054549Seric 			{
40658151Seric 				message("503 Need MAIL command");
4074976Seric 				break;
4084549Seric 			}
40955012Seric 			else if (e->e_nrcpts <= 0)
4104549Seric 			{
41158151Seric 				message("503 Need RCPT (recipient)");
4124976Seric 				break;
4134549Seric 			}
4144976Seric 
41558929Seric 			/* check to see if we need to re-expand aliases */
41658929Seric 			for (a = e->e_sendqueue; a != NULL; a = a->q_next)
41758929Seric 			{
41858929Seric 				if (bitset(QVERIFIED, a->q_flags))
41958929Seric 					break;
42058929Seric 			}
42158929Seric 
4224976Seric 			/* collect the text of the message */
42324943Seric 			SmtpPhase = "collect";
42457389Seric 			setproctitle("%s %s: %s", e->e_id, CurHostName, inp);
42558929Seric 			collect(TRUE, a != NULL, e);
4264976Seric 			if (Errors != 0)
4274976Seric 				break;
4284976Seric 
4298238Seric 			/*
4308238Seric 			**  Arrange to send to everyone.
4318238Seric 			**	If sending to multiple people, mail back
4328238Seric 			**		errors rather than reporting directly.
4338238Seric 			**	In any case, don't mail back errors for
4348238Seric 			**		anything that has happened up to
4358238Seric 			**		now (the other end will do this).
43610197Seric 			**	Truncate our transcript -- the mail has gotten
43710197Seric 			**		to us successfully, and if we have
43810197Seric 			**		to mail this back, it will be easier
43910197Seric 			**		on the reader.
4408238Seric 			**	Then send to everyone.
4418238Seric 			**	Finally give a reply code.  If an error has
4428238Seric 			**		already been given, don't mail a
4438238Seric 			**		message back.
4449339Seric 			**	We goose error returns by clearing error bit.
4458238Seric 			*/
4468238Seric 
44724943Seric 			SmtpPhase = "delivery";
44855012Seric 			if (e->e_nrcpts != 1)
4499378Seric 			{
4509378Seric 				HoldErrs = TRUE;
45158734Seric 				e->e_errormode = EM_MAIL;
4529378Seric 			}
45355012Seric 			e->e_flags &= ~EF_FATALERRS;
45455012Seric 			e->e_xfp = freopen(queuename(e, 'x'), "w", e->e_xfp);
45558714Seric 			id = e->e_id;
4564976Seric 
4574976Seric 			/* send to all recipients */
45858919Seric 			sendall(e, a == NULL ? SM_DEFAULT : SM_QUEUE);
45955012Seric 			e->e_to = NULL;
4604976Seric 
46123516Seric 			/* save statistics */
46255012Seric 			markstats(e, (ADDRESS *) NULL);
46323516Seric 
4648238Seric 			/* issue success if appropriate and reset */
4658238Seric 			if (Errors == 0 || HoldErrs)
46658855Seric 				message("250 %s Message accepted for delivery", id);
4678238Seric 			else
46855012Seric 				e->e_flags &= ~EF_FATALERRS;
4699339Seric 
47058919Seric 			/* if we just queued, poke it */
47158919Seric 			if (a != NULL && e->e_sendmode != SM_QUEUE)
47258919Seric 			{
47358919Seric 				unlockqueue(e);
47458924Seric 				dowork(id, TRUE, TRUE, e);
47558919Seric 				e->e_id = NULL;
47658919Seric 			}
47758883Seric 
4789339Seric 			/* if in a child, pop back to our parent */
4799339Seric 			if (InChild)
4809339Seric 				finis();
48124943Seric 
48224943Seric 			/* clean up a bit */
48358109Seric 			gotmail = FALSE;
48455012Seric 			dropenvelope(e);
48558179Seric 			CurEnv = e = newenvelope(e, CurEnv);
48655012Seric 			e->e_flags = BlankEnvelope.e_flags;
4874549Seric 			break;
4884549Seric 
4894549Seric 		  case CMDRSET:		/* rset -- reset state */
49058151Seric 			message("250 Reset state");
4919339Seric 			if (InChild)
4929339Seric 				finis();
49358109Seric 
49458109Seric 			/* clean up a bit */
49558109Seric 			gotmail = FALSE;
49658109Seric 			dropenvelope(e);
49758179Seric 			CurEnv = e = newenvelope(e, CurEnv);
4989339Seric 			break;
4994549Seric 
5004549Seric 		  case CMDVRFY:		/* vrfy -- verify address */
50158092Seric 		  case CMDEXPN:		/* expn -- expand address */
50258092Seric 			vrfy = c->cmdcode == CMDVRFY;
50358092Seric 			if (bitset(vrfy ? PRIV_NOVRFY : PRIV_NOEXPN,
50458092Seric 						PrivacyFlags))
50558082Seric 			{
50658412Seric 				if (vrfy)
50758412Seric 					message("252 Who's to say?");
50858412Seric 				else
50958412Seric 					message("502 That's none of your business");
51058082Seric 				break;
51158082Seric 			}
51258082Seric 			else if (!gothello &&
51358092Seric 				 bitset(vrfy ? PRIV_NEEDVRFYHELO : PRIV_NEEDEXPNHELO,
51458092Seric 						PrivacyFlags))
51558082Seric 			{
51658151Seric 				message("503 I demand that you introduce yourself first");
51758082Seric 				break;
51858082Seric 			}
51958092Seric 			if (runinchild(vrfy ? "SMTP-VRFY" : "SMTP-EXPN", e) > 0)
5209339Seric 				break;
52125050Seric 			setproctitle("%s: %s", CurHostName, inp);
52255173Seric #ifdef LOG
52358020Seric 			if (LogLevel > 5)
52455173Seric 				syslog(LOG_INFO, "%s: %s", CurHostName, inp);
52555173Seric #endif
5265003Seric 			vrfyqueue = NULL;
5277762Seric 			QuickAbort = TRUE;
52858092Seric 			if (vrfy)
52958092Seric 				e->e_flags |= EF_VRFYONLY;
53058082Seric 			(void) sendtolist(p, (ADDRESS *) NULL, &vrfyqueue, e);
5317762Seric 			if (Errors != 0)
5329339Seric 			{
5339339Seric 				if (InChild)
5349339Seric 					finis();
5357762Seric 				break;
5369339Seric 			}
5375003Seric 			while (vrfyqueue != NULL)
5385003Seric 			{
5395003Seric 				register ADDRESS *a = vrfyqueue->q_next;
5405003Seric 				char *code;
5415003Seric 
5427685Seric 				while (a != NULL && bitset(QDONTSEND|QBADADDR, a->q_flags))
5435003Seric 					a = a->q_next;
5445003Seric 
5457685Seric 				if (!bitset(QDONTSEND|QBADADDR, vrfyqueue->q_flags))
54658151Seric 					printvrfyaddr(vrfyqueue, a == NULL);
5475003Seric 				else if (a == NULL)
54858151Seric 					message("554 Self destructive alias loop");
5495003Seric 				vrfyqueue = a;
5505003Seric 			}
5519339Seric 			if (InChild)
5529339Seric 				finis();
5534549Seric 			break;
5544549Seric 
5554549Seric 		  case CMDHELP:		/* help -- give user info */
5564577Seric 			help(p);
5574549Seric 			break;
5584549Seric 
5594549Seric 		  case CMDNOOP:		/* noop -- do nothing */
56058151Seric 			message("200 OK");
5614549Seric 			break;
5624549Seric 
5634549Seric 		  case CMDQUIT:		/* quit -- leave mail */
56458151Seric 			message("221 %s closing connection", MyHostName);
5659339Seric 			if (InChild)
5669339Seric 				ExitStat = EX_QUIT;
5674549Seric 			finis();
5684549Seric 
5698544Seric 		  case CMDVERB:		/* set verbose mode */
5708544Seric 			Verbose = TRUE;
57158734Seric 			e->e_sendmode = SM_DELIVER;
57258151Seric 			message("200 Verbose mode");
5738544Seric 			break;
5748544Seric 
5759314Seric 		  case CMDONEX:		/* doing one transaction only */
5769378Seric 			OneXact = TRUE;
57758151Seric 			message("200 Only one transaction");
5789314Seric 			break;
5799314Seric 
58036230Skarels # ifdef SMTPDEBUG
5819339Seric 		  case CMDDBGQSHOW:	/* show queues */
5826907Seric 			printf("Send Queue=");
58355012Seric 			printaddr(e->e_sendqueue, TRUE);
5845003Seric 			break;
5857275Seric 
5867275Seric 		  case CMDDBGDEBUG:	/* set debug mode */
5877676Seric 			tTsetup(tTdvect, sizeof tTdvect, "0-99.1");
5887676Seric 			tTflag(p);
58958151Seric 			message("200 Debug set");
5907275Seric 			break;
5917275Seric 
59236230Skarels # else /* not SMTPDEBUG */
59324945Seric 
59436230Skarels 		  case CMDDBGQSHOW:	/* show queues */
59536230Skarels 		  case CMDDBGDEBUG:	/* set debug mode */
59636233Skarels # ifdef LOG
59758308Seric 			if (LogLevel > 0)
59836230Skarels 				syslog(LOG_NOTICE,
59958020Seric 				    "\"%s\" command from %s (%s)",
60036230Skarels 				    c->cmdname, RealHostName,
60158755Seric 				    anynet_ntoa(&RealHostAddr));
60236233Skarels # endif
60336230Skarels 			/* FALL THROUGH */
60436230Skarels # endif /* SMTPDEBUG */
60536230Skarels 
6064549Seric 		  case CMDERROR:	/* unknown command */
60758151Seric 			message("500 Command unrecognized");
6084549Seric 			break;
6094549Seric 
6104549Seric 		  default:
61136230Skarels 			errno = 0;
61258151Seric 			syserr("500 smtp: unknown code %d", c->cmdcode);
6134549Seric 			break;
6144549Seric 		}
6154549Seric 	}
6164549Seric }
6174549Seric /*
6184549Seric **  SKIPWORD -- skip a fixed word.
6194549Seric **
6204549Seric **	Parameters:
6214549Seric **		p -- place to start looking.
6224549Seric **		w -- word to skip.
6234549Seric **
6244549Seric **	Returns:
6254549Seric **		p following w.
6264549Seric **		NULL on error.
6274549Seric **
6284549Seric **	Side Effects:
6294549Seric **		clobbers the p data area.
6304549Seric */
6314549Seric 
6324549Seric static char *
6334549Seric skipword(p, w)
6344549Seric 	register char *p;
6354549Seric 	char *w;
6364549Seric {
6374549Seric 	register char *q;
6384549Seric 
6394549Seric 	/* find beginning of word */
64058050Seric 	while (isascii(*p) && isspace(*p))
6414549Seric 		p++;
6424549Seric 	q = p;
6434549Seric 
6444549Seric 	/* find end of word */
64558050Seric 	while (*p != '\0' && *p != ':' && !(isascii(*p) && isspace(*p)))
6464549Seric 		p++;
64758050Seric 	while (isascii(*p) && isspace(*p))
6484549Seric 		*p++ = '\0';
6494549Seric 	if (*p != ':')
6504549Seric 	{
6514549Seric 	  syntax:
65258151Seric 		message("501 Syntax error");
6534549Seric 		Errors++;
6544549Seric 		return (NULL);
6554549Seric 	}
6564549Seric 	*p++ = '\0';
65758050Seric 	while (isascii(*p) && isspace(*p))
6584549Seric 		p++;
6594549Seric 
6604549Seric 	/* see if the input word matches desired word */
66133725Sbostic 	if (strcasecmp(q, w))
6624549Seric 		goto syntax;
6634549Seric 
6644549Seric 	return (p);
6654549Seric }
6664577Seric /*
66758151Seric **  PRINTVRFYADDR -- print an entry in the verify queue
66858151Seric **
66958151Seric **	Parameters:
67058151Seric **		a -- the address to print
67158151Seric **		last -- set if this is the last one.
67258151Seric **
67358151Seric **	Returns:
67458151Seric **		none.
67558151Seric **
67658151Seric **	Side Effects:
67758151Seric **		Prints the appropriate 250 codes.
67858151Seric */
67958151Seric 
68058151Seric printvrfyaddr(a, last)
68158151Seric 	register ADDRESS *a;
68258151Seric 	bool last;
68358151Seric {
68458151Seric 	char fmtbuf[20];
68558151Seric 
68658151Seric 	strcpy(fmtbuf, "250");
68758151Seric 	fmtbuf[3] = last ? ' ' : '-';
68858151Seric 
68958151Seric 	if (strchr(a->q_paddr, '<') != NULL)
69058151Seric 		strcpy(&fmtbuf[4], "%s");
69158151Seric 	else if (a->q_fullname == NULL)
69258151Seric 		strcpy(&fmtbuf[4], "<%s>");
69358151Seric 	else
69458151Seric 	{
69558151Seric 		strcpy(&fmtbuf[4], "%s <%s>");
69658151Seric 		message(fmtbuf, a->q_fullname, a->q_paddr);
69758151Seric 		return;
69858151Seric 	}
69958151Seric 	message(fmtbuf, a->q_paddr);
70058151Seric }
70158151Seric /*
7024577Seric **  HELP -- implement the HELP command.
7034577Seric **
7044577Seric **	Parameters:
7054577Seric **		topic -- the topic we want help for.
7064577Seric **
7074577Seric **	Returns:
7084577Seric **		none.
7094577Seric **
7104577Seric **	Side Effects:
7114577Seric **		outputs the help file to message output.
7124577Seric */
7134577Seric 
7144577Seric help(topic)
7154577Seric 	char *topic;
7164577Seric {
7174577Seric 	register FILE *hf;
7184577Seric 	int len;
7194577Seric 	char buf[MAXLINE];
7204577Seric 	bool noinfo;
7214577Seric 
7228269Seric 	if (HelpFile == NULL || (hf = fopen(HelpFile, "r")) == NULL)
7234577Seric 	{
7244577Seric 		/* no help */
72511931Seric 		errno = 0;
72658151Seric 		message("502 HELP not implemented");
7274577Seric 		return;
7284577Seric 	}
7294577Seric 
73049669Seric 	if (topic == NULL || *topic == '\0')
73149669Seric 		topic = "smtp";
73249669Seric 	else
73349669Seric 		makelower(topic);
73449669Seric 
7354577Seric 	len = strlen(topic);
7364577Seric 	noinfo = TRUE;
7374577Seric 
7384577Seric 	while (fgets(buf, sizeof buf, hf) != NULL)
7394577Seric 	{
7404577Seric 		if (strncmp(buf, topic, len) == 0)
7414577Seric 		{
7424577Seric 			register char *p;
7434577Seric 
74456795Seric 			p = strchr(buf, '\t');
7454577Seric 			if (p == NULL)
7464577Seric 				p = buf;
7474577Seric 			else
7484577Seric 				p++;
7494577Seric 			fixcrlf(p, TRUE);
75058151Seric 			message("214-%s", p);
7514577Seric 			noinfo = FALSE;
7524577Seric 		}
7534577Seric 	}
7544577Seric 
7554577Seric 	if (noinfo)
75658151Seric 		message("504 HELP topic unknown");
7574577Seric 	else
75858151Seric 		message("214 End of HELP info");
7594628Seric 	(void) fclose(hf);
7604577Seric }
7618544Seric /*
7629339Seric **  RUNINCHILD -- return twice -- once in the child, then in the parent again
7639339Seric **
7649339Seric **	Parameters:
7659339Seric **		label -- a string used in error messages
7669339Seric **
7679339Seric **	Returns:
7689339Seric **		zero in the child
7699339Seric **		one in the parent
7709339Seric **
7719339Seric **	Side Effects:
7729339Seric **		none.
7739339Seric */
7748544Seric 
77555012Seric runinchild(label, e)
7769339Seric 	char *label;
77755012Seric 	register ENVELOPE *e;
7789339Seric {
7799339Seric 	int childpid;
7809339Seric 
78116158Seric 	if (!OneXact)
7829339Seric 	{
78316158Seric 		childpid = dofork();
78416158Seric 		if (childpid < 0)
78516158Seric 		{
78616158Seric 			syserr("%s: cannot fork", label);
78716158Seric 			return (1);
78816158Seric 		}
78916158Seric 		if (childpid > 0)
79016158Seric 		{
79116158Seric 			auto int st;
7929339Seric 
79316158Seric 			/* parent -- wait for child to complete */
79416158Seric 			st = waitfor(childpid);
79516158Seric 			if (st == -1)
79616158Seric 				syserr("%s: lost child", label);
7979339Seric 
79816158Seric 			/* if we exited on a QUIT command, complete the process */
79916158Seric 			if (st == (EX_QUIT << 8))
80016158Seric 				finis();
8019339Seric 
80216158Seric 			return (1);
80316158Seric 		}
80416158Seric 		else
80516158Seric 		{
80616158Seric 			/* child */
80716158Seric 			InChild = TRUE;
80825050Seric 			QuickAbort = FALSE;
80955012Seric 			clearenvelope(e, FALSE);
81016158Seric 		}
8119339Seric 	}
81215256Seric 
81316158Seric 	/* open alias database */
81455012Seric 	initaliases(AliasFile, FALSE, e);
81516158Seric 
81616158Seric 	return (0);
8179339Seric }
8189339Seric 
81956795Seric # endif /* SMTP */
820