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*59066Seric static char sccsid[] = "@(#)srvrsmtp.c	6.43 (Berkeley) 04/13/93 (with SMTP)";
1433731Sbostic #else
15*59066Seric static char sccsid[] = "@(#)srvrsmtp.c	6.43 (Berkeley) 04/13/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;
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 
119*59066Seric 	if (fileno(OutChannel) != fileno(stdout))
1207363Seric 	{
1217363Seric 		/* arrange for debugging output to go to remote host */
122*59066Seric 		(void) dup2(fileno(OutChannel), fileno(stdout));
1237363Seric 	}
12455012Seric 	settime(e);
12557642Seric 	CurHostName = RealHostName;
12659060Seric 	setproctitle("srvrsmtp %s startup", CurHostName);
12758050Seric 	expand("\201e", inp, &inp[sizeof inp], e);
12858151Seric 	message("220 %s", inp);
12958330Seric 	protocol = NULL;
13059016Seric 	sendinghost = macvalue('s', e);
13158082Seric 	gothello = FALSE;
13258330Seric 	gotmail = FALSE;
1334549Seric 	for (;;)
1344549Seric 	{
13512612Seric 		/* arrange for backout */
13612612Seric 		if (setjmp(TopFrame) > 0 && InChild)
13759058Seric 		{
13859058Seric 			QuickAbort = FALSE;
13959058Seric 			SuprErrs = TRUE;
14012612Seric 			finis();
14159058Seric 		}
14212612Seric 		QuickAbort = FALSE;
14312612Seric 		HoldErrs = FALSE;
14451951Seric 		LogUsrErrs = FALSE;
14558092Seric 		e->e_flags &= ~EF_VRFYONLY;
14612612Seric 
1477356Seric 		/* setup for the read */
14855012Seric 		e->e_to = NULL;
1494577Seric 		Errors = 0;
1507275Seric 		(void) fflush(stdout);
1517356Seric 
1527356Seric 		/* read the input line */
15359060Seric 		SmtpPhase = "srvrsmtp cmd read";
15459060Seric 		setproctitle("srvrsmtp %s cmd read", CurHostName);
15558109Seric 		p = sfgets(inp, sizeof inp, InChannel, TimeOuts.to_nextcommand);
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
16458020Seric 			if (LogLevel > 1)
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";
21758323Seric 				SmtpPhase = "EHLO";
21858323Seric 			}
21958323Seric 			else
22058323Seric 			{
22158323Seric 				protocol = "SMTP";
22258323Seric 				SmtpPhase = "HELO";
22358323Seric 			}
22459016Seric 			sendinghost = newstr(p);
22558308Seric 			if (strcasecmp(p, RealHostName) != 0)
22611146Seric 			{
22758789Seric 				auth_warning(e, "Host %s claimed to be %s",
22858789Seric 					RealHostName, p);
22911146Seric 			}
23058957Seric 			p = macvalue('_', e);
23158957Seric 			if (p == NULL)
23259016Seric 				p = RealHostName;
23358323Seric 
23458323Seric 			/* send ext. message -- old systems must ignore */
23558323Seric 			message("250-%s Hello %s, pleased to meet you",
23658957Seric 				MyHostName, p);
23758323Seric 			if (!bitset(PRIV_NOEXPN, PrivacyFlags))
23858323Seric 				message("250-EXPN");
23958338Seric 			message("250-SIZE");
24058323Seric 			message("250 HELP");
24158082Seric 			gothello = TRUE;
2424976Seric 			break;
2434976Seric 
2444549Seric 		  case CMDMAIL:		/* mail -- designate sender */
24524943Seric 			SmtpPhase = "MAIL";
24624943Seric 
2479314Seric 			/* check for validity of this command */
24858789Seric 			if (!gothello)
24958082Seric 			{
25058957Seric 				/* set sending host to our known value */
25159016Seric 				if (sendinghost == NULL)
25259016Seric 					sendinghost = RealHostName;
25358957Seric 
25458789Seric 				if (bitset(PRIV_NEEDMAILHELO, PrivacyFlags))
25558821Seric 				{
25658789Seric 					message("503 Polite people say HELO first");
25758821Seric 					break;
25858821Seric 				}
25958789Seric 				else
26058821Seric 				{
26158789Seric 					auth_warning(e,
26258789Seric 						"Host %s didn't use HELO protocol",
26358789Seric 						RealHostName);
26458821Seric 				}
26558082Seric 			}
26658109Seric 			if (gotmail)
2674558Seric 			{
26858151Seric 				message("503 Sender already specified");
2694558Seric 				break;
2704558Seric 			}
2719339Seric 			if (InChild)
2729339Seric 			{
27336230Skarels 				errno = 0;
27458151Seric 				syserr("503 Nested MAIL command: MAIL %s", p);
27558069Seric 				finis();
2769339Seric 			}
2779339Seric 
2789339Seric 			/* fork a subprocess to process this command */
27955012Seric 			if (runinchild("SMTP-MAIL", e) > 0)
2809339Seric 				break;
28158323Seric 			if (protocol == NULL)
28258323Seric 				protocol = "SMTP";
28358323Seric 			define('r', protocol, e);
28459016Seric 			define('s', sendinghost, e);
28555012Seric 			initsys(e);
28657389Seric 			setproctitle("%s %s: %s", e->e_id, CurHostName, inp);
2879339Seric 
2889339Seric 			/* child -- go do the processing */
2894549Seric 			p = skipword(p, "from");
2904549Seric 			if (p == NULL)
2914549Seric 				break;
29257977Seric 			if (setjmp(TopFrame) > 0)
29358147Seric 			{
29458147Seric 				/* this failed -- undo work */
29558147Seric 				if (InChild)
29659058Seric 				{
29759058Seric 					QuickAbort = FALSE;
29859058Seric 					SuprErrs = TRUE;
29958147Seric 					finis();
30059058Seric 				}
30157977Seric 				break;
30258147Seric 			}
30357977Seric 			QuickAbort = TRUE;
30458333Seric 
30558333Seric 			/* must parse sender first */
30658333Seric 			delimptr = NULL;
30758704Seric 			setsender(p, e, &delimptr, FALSE);
30858333Seric 			p = delimptr;
30958333Seric 			if (p != NULL && *p != '\0')
31058333Seric 				*p++ = '\0';
31158333Seric 
31258333Seric 			/* now parse ESMTP arguments */
31358333Seric 			msize = 0;
31458333Seric 			for (; p != NULL && *p != '\0'; p++)
31558333Seric 			{
31658333Seric 				char *kp;
31758333Seric 				char *vp;
31858333Seric 
31958333Seric 				/* locate the beginning of the keyword */
32058333Seric 				while (isascii(*p) && isspace(*p))
32158333Seric 					p++;
32258333Seric 				if (*p == '\0')
32358333Seric 					break;
32458333Seric 				kp = p;
32558333Seric 
32658333Seric 				/* skip to the value portion */
32758333Seric 				while (isascii(*p) && isalnum(*p) || *p == '-')
32858333Seric 					p++;
32958333Seric 				if (*p == '=')
33058333Seric 				{
33158333Seric 					*p++ = '\0';
33258333Seric 					vp = p;
33358333Seric 
33458333Seric 					/* skip to the end of the value */
33558333Seric 					while (*p != '\0' && *p != ' ' &&
33658333Seric 					       !(isascii(*p) && iscntrl(*p)) &&
33758333Seric 					       *p != '=')
33858333Seric 						p++;
33958333Seric 				}
34058333Seric 
34158333Seric 				if (*p != '\0')
34258333Seric 					*p++ = '\0';
34358333Seric 
34458333Seric 				if (tTd(19, 1))
34558333Seric 					printf("MAIL: got arg %s=%s\n", kp,
34658333Seric 						vp == NULL ? "<null>" : vp);
34758333Seric 
34858333Seric 				if (strcasecmp(kp, "size") == 0)
34958333Seric 				{
35058333Seric 					if (kp == NULL)
35158333Seric 					{
35258333Seric 						usrerr("501 SIZE requires a value");
35358333Seric 						/* NOTREACHED */
35458333Seric 					}
35558333Seric 					msize = atol(vp);
35658333Seric 				}
35758333Seric 				else
35858333Seric 				{
35958333Seric 					usrerr("501 %s parameter unrecognized", kp);
36058333Seric 					/* NOTREACHED */
36158333Seric 				}
36258333Seric 			}
36358333Seric 
36458333Seric 			if (!enoughspace(msize))
36558333Seric 			{
36658333Seric 				message("452 Insufficient disk space; try again later");
36758333Seric 				break;
36858333Seric 			}
36958151Seric 			message("250 Sender ok");
37058147Seric 			gotmail = TRUE;
3714549Seric 			break;
3724549Seric 
3734976Seric 		  case CMDRCPT:		/* rcpt -- designate recipient */
37458850Seric 			if (!gotmail)
37558850Seric 			{
37658850Seric 				usrerr("503 Need MAIL before RCPT");
37758850Seric 				break;
37858850Seric 			}
37924943Seric 			SmtpPhase = "RCPT";
38012612Seric 			if (setjmp(TopFrame) > 0)
38114785Seric 			{
38255012Seric 				e->e_flags &= ~EF_FATALERRS;
38312612Seric 				break;
38414785Seric 			}
38512612Seric 			QuickAbort = TRUE;
38651951Seric 			LogUsrErrs = TRUE;
38758093Seric 
38858919Seric 			e->e_flags |= EF_VRFYONLY;
38958919Seric 
3904549Seric 			p = skipword(p, "to");
3914549Seric 			if (p == NULL)
3924549Seric 				break;
39358333Seric 			a = parseaddr(p, (ADDRESS *) NULL, 1, ' ', NULL, e);
39412612Seric 			if (a == NULL)
39512612Seric 				break;
39616886Seric 			a->q_flags |= QPRIMARY;
39755012Seric 			a = recipient(a, &e->e_sendqueue, e);
39812612Seric 			if (Errors != 0)
39912612Seric 				break;
40012612Seric 
40112612Seric 			/* no errors during parsing, but might be a duplicate */
40255012Seric 			e->e_to = p;
40312612Seric 			if (!bitset(QBADADDR, a->q_flags))
40458151Seric 				message("250 Recipient ok");
40512612Seric 			else
4064549Seric 			{
40712612Seric 				/* punt -- should keep message in ADDRESS.... */
40858151Seric 				message("550 Addressee unknown");
4094549Seric 			}
41055012Seric 			e->e_to = NULL;
4114549Seric 			break;
4124549Seric 
4134549Seric 		  case CMDDATA:		/* data -- text of mail */
41424943Seric 			SmtpPhase = "DATA";
41558109Seric 			if (!gotmail)
4164549Seric 			{
41758151Seric 				message("503 Need MAIL command");
4184976Seric 				break;
4194549Seric 			}
42055012Seric 			else if (e->e_nrcpts <= 0)
4214549Seric 			{
42258151Seric 				message("503 Need RCPT (recipient)");
4234976Seric 				break;
4244549Seric 			}
4254976Seric 
42658929Seric 			/* check to see if we need to re-expand aliases */
42758929Seric 			for (a = e->e_sendqueue; a != NULL; a = a->q_next)
42858929Seric 			{
42958929Seric 				if (bitset(QVERIFIED, a->q_flags))
43058929Seric 					break;
43158929Seric 			}
43258929Seric 
4334976Seric 			/* collect the text of the message */
43424943Seric 			SmtpPhase = "collect";
43558929Seric 			collect(TRUE, a != NULL, e);
4364976Seric 			if (Errors != 0)
4374976Seric 				break;
4384976Seric 
4398238Seric 			/*
4408238Seric 			**  Arrange to send to everyone.
4418238Seric 			**	If sending to multiple people, mail back
4428238Seric 			**		errors rather than reporting directly.
4438238Seric 			**	In any case, don't mail back errors for
4448238Seric 			**		anything that has happened up to
4458238Seric 			**		now (the other end will do this).
44610197Seric 			**	Truncate our transcript -- the mail has gotten
44710197Seric 			**		to us successfully, and if we have
44810197Seric 			**		to mail this back, it will be easier
44910197Seric 			**		on the reader.
4508238Seric 			**	Then send to everyone.
4518238Seric 			**	Finally give a reply code.  If an error has
4528238Seric 			**		already been given, don't mail a
4538238Seric 			**		message back.
4549339Seric 			**	We goose error returns by clearing error bit.
4558238Seric 			*/
4568238Seric 
45724943Seric 			SmtpPhase = "delivery";
45855012Seric 			if (e->e_nrcpts != 1)
4599378Seric 			{
4609378Seric 				HoldErrs = TRUE;
46158734Seric 				e->e_errormode = EM_MAIL;
4629378Seric 			}
46355012Seric 			e->e_flags &= ~EF_FATALERRS;
46455012Seric 			e->e_xfp = freopen(queuename(e, 'x'), "w", e->e_xfp);
46558714Seric 			id = e->e_id;
4664976Seric 
4674976Seric 			/* send to all recipients */
46858919Seric 			sendall(e, a == NULL ? SM_DEFAULT : SM_QUEUE);
46955012Seric 			e->e_to = NULL;
4704976Seric 
47123516Seric 			/* save statistics */
47255012Seric 			markstats(e, (ADDRESS *) NULL);
47323516Seric 
4748238Seric 			/* issue success if appropriate and reset */
4758238Seric 			if (Errors == 0 || HoldErrs)
47658855Seric 				message("250 %s Message accepted for delivery", id);
4778238Seric 			else
47855012Seric 				e->e_flags &= ~EF_FATALERRS;
4799339Seric 
48058919Seric 			/* if we just queued, poke it */
48158919Seric 			if (a != NULL && e->e_sendmode != SM_QUEUE)
48258919Seric 			{
48358919Seric 				unlockqueue(e);
48458924Seric 				dowork(id, TRUE, TRUE, e);
48558919Seric 				e->e_id = NULL;
48658919Seric 			}
48758883Seric 
4889339Seric 			/* if in a child, pop back to our parent */
4899339Seric 			if (InChild)
4909339Seric 				finis();
49124943Seric 
49224943Seric 			/* clean up a bit */
49358109Seric 			gotmail = FALSE;
49455012Seric 			dropenvelope(e);
49558179Seric 			CurEnv = e = newenvelope(e, CurEnv);
49655012Seric 			e->e_flags = BlankEnvelope.e_flags;
4974549Seric 			break;
4984549Seric 
4994549Seric 		  case CMDRSET:		/* rset -- reset state */
50058151Seric 			message("250 Reset state");
5019339Seric 			if (InChild)
5029339Seric 				finis();
50358109Seric 
50458109Seric 			/* clean up a bit */
50558109Seric 			gotmail = FALSE;
50658109Seric 			dropenvelope(e);
50758179Seric 			CurEnv = e = newenvelope(e, CurEnv);
5089339Seric 			break;
5094549Seric 
5104549Seric 		  case CMDVRFY:		/* vrfy -- verify address */
51158092Seric 		  case CMDEXPN:		/* expn -- expand address */
51258092Seric 			vrfy = c->cmdcode == CMDVRFY;
51358092Seric 			if (bitset(vrfy ? PRIV_NOVRFY : PRIV_NOEXPN,
51458092Seric 						PrivacyFlags))
51558082Seric 			{
51658412Seric 				if (vrfy)
51758412Seric 					message("252 Who's to say?");
51858412Seric 				else
51958412Seric 					message("502 That's none of your business");
52058082Seric 				break;
52158082Seric 			}
52258082Seric 			else if (!gothello &&
52358092Seric 				 bitset(vrfy ? PRIV_NEEDVRFYHELO : PRIV_NEEDEXPNHELO,
52458092Seric 						PrivacyFlags))
52558082Seric 			{
52658151Seric 				message("503 I demand that you introduce yourself first");
52758082Seric 				break;
52858082Seric 			}
52958092Seric 			if (runinchild(vrfy ? "SMTP-VRFY" : "SMTP-EXPN", e) > 0)
5309339Seric 				break;
53155173Seric #ifdef LOG
53258020Seric 			if (LogLevel > 5)
53355173Seric 				syslog(LOG_INFO, "%s: %s", CurHostName, inp);
53455173Seric #endif
5355003Seric 			vrfyqueue = NULL;
5367762Seric 			QuickAbort = TRUE;
53758092Seric 			if (vrfy)
53858092Seric 				e->e_flags |= EF_VRFYONLY;
53958082Seric 			(void) sendtolist(p, (ADDRESS *) NULL, &vrfyqueue, e);
5407762Seric 			if (Errors != 0)
5419339Seric 			{
5429339Seric 				if (InChild)
5439339Seric 					finis();
5447762Seric 				break;
5459339Seric 			}
5465003Seric 			while (vrfyqueue != NULL)
5475003Seric 			{
5485003Seric 				register ADDRESS *a = vrfyqueue->q_next;
5495003Seric 				char *code;
5505003Seric 
5517685Seric 				while (a != NULL && bitset(QDONTSEND|QBADADDR, a->q_flags))
5525003Seric 					a = a->q_next;
5535003Seric 
5547685Seric 				if (!bitset(QDONTSEND|QBADADDR, vrfyqueue->q_flags))
55558151Seric 					printvrfyaddr(vrfyqueue, a == NULL);
5565003Seric 				else if (a == NULL)
55758151Seric 					message("554 Self destructive alias loop");
5585003Seric 				vrfyqueue = a;
5595003Seric 			}
5609339Seric 			if (InChild)
5619339Seric 				finis();
5624549Seric 			break;
5634549Seric 
5644549Seric 		  case CMDHELP:		/* help -- give user info */
5654577Seric 			help(p);
5664549Seric 			break;
5674549Seric 
5684549Seric 		  case CMDNOOP:		/* noop -- do nothing */
56958151Seric 			message("200 OK");
5704549Seric 			break;
5714549Seric 
5724549Seric 		  case CMDQUIT:		/* quit -- leave mail */
57358151Seric 			message("221 %s closing connection", MyHostName);
5749339Seric 			if (InChild)
5759339Seric 				ExitStat = EX_QUIT;
5764549Seric 			finis();
5774549Seric 
5788544Seric 		  case CMDVERB:		/* set verbose mode */
5798544Seric 			Verbose = TRUE;
58058734Seric 			e->e_sendmode = SM_DELIVER;
58158151Seric 			message("200 Verbose mode");
5828544Seric 			break;
5838544Seric 
5849314Seric 		  case CMDONEX:		/* doing one transaction only */
5859378Seric 			OneXact = TRUE;
58658151Seric 			message("200 Only one transaction");
5879314Seric 			break;
5889314Seric 
58936230Skarels # ifdef SMTPDEBUG
5909339Seric 		  case CMDDBGQSHOW:	/* show queues */
5916907Seric 			printf("Send Queue=");
59255012Seric 			printaddr(e->e_sendqueue, TRUE);
5935003Seric 			break;
5947275Seric 
5957275Seric 		  case CMDDBGDEBUG:	/* set debug mode */
5967676Seric 			tTsetup(tTdvect, sizeof tTdvect, "0-99.1");
5977676Seric 			tTflag(p);
59858151Seric 			message("200 Debug set");
5997275Seric 			break;
6007275Seric 
60136230Skarels # else /* not SMTPDEBUG */
60224945Seric 
60336230Skarels 		  case CMDDBGQSHOW:	/* show queues */
60436230Skarels 		  case CMDDBGDEBUG:	/* set debug mode */
60536233Skarels # ifdef LOG
60658308Seric 			if (LogLevel > 0)
60736230Skarels 				syslog(LOG_NOTICE,
60858020Seric 				    "\"%s\" command from %s (%s)",
60936230Skarels 				    c->cmdname, RealHostName,
61058755Seric 				    anynet_ntoa(&RealHostAddr));
61136233Skarels # endif
61236230Skarels 			/* FALL THROUGH */
61336230Skarels # endif /* SMTPDEBUG */
61436230Skarels 
6154549Seric 		  case CMDERROR:	/* unknown command */
61658151Seric 			message("500 Command unrecognized");
6174549Seric 			break;
6184549Seric 
6194549Seric 		  default:
62036230Skarels 			errno = 0;
62158151Seric 			syserr("500 smtp: unknown code %d", c->cmdcode);
6224549Seric 			break;
6234549Seric 		}
6244549Seric 	}
6254549Seric }
6264549Seric /*
6274549Seric **  SKIPWORD -- skip a fixed word.
6284549Seric **
6294549Seric **	Parameters:
6304549Seric **		p -- place to start looking.
6314549Seric **		w -- word to skip.
6324549Seric **
6334549Seric **	Returns:
6344549Seric **		p following w.
6354549Seric **		NULL on error.
6364549Seric **
6374549Seric **	Side Effects:
6384549Seric **		clobbers the p data area.
6394549Seric */
6404549Seric 
6414549Seric static char *
6424549Seric skipword(p, w)
6434549Seric 	register char *p;
6444549Seric 	char *w;
6454549Seric {
6464549Seric 	register char *q;
6474549Seric 
6484549Seric 	/* find beginning of word */
64958050Seric 	while (isascii(*p) && isspace(*p))
6504549Seric 		p++;
6514549Seric 	q = p;
6524549Seric 
6534549Seric 	/* find end of word */
65458050Seric 	while (*p != '\0' && *p != ':' && !(isascii(*p) && isspace(*p)))
6554549Seric 		p++;
65658050Seric 	while (isascii(*p) && isspace(*p))
6574549Seric 		*p++ = '\0';
6584549Seric 	if (*p != ':')
6594549Seric 	{
6604549Seric 	  syntax:
66158151Seric 		message("501 Syntax error");
6624549Seric 		Errors++;
6634549Seric 		return (NULL);
6644549Seric 	}
6654549Seric 	*p++ = '\0';
66658050Seric 	while (isascii(*p) && isspace(*p))
6674549Seric 		p++;
6684549Seric 
6694549Seric 	/* see if the input word matches desired word */
67033725Sbostic 	if (strcasecmp(q, w))
6714549Seric 		goto syntax;
6724549Seric 
6734549Seric 	return (p);
6744549Seric }
6754577Seric /*
67658151Seric **  PRINTVRFYADDR -- print an entry in the verify queue
67758151Seric **
67858151Seric **	Parameters:
67958151Seric **		a -- the address to print
68058151Seric **		last -- set if this is the last one.
68158151Seric **
68258151Seric **	Returns:
68358151Seric **		none.
68458151Seric **
68558151Seric **	Side Effects:
68658151Seric **		Prints the appropriate 250 codes.
68758151Seric */
68858151Seric 
68958151Seric printvrfyaddr(a, last)
69058151Seric 	register ADDRESS *a;
69158151Seric 	bool last;
69258151Seric {
69358151Seric 	char fmtbuf[20];
69458151Seric 
69558151Seric 	strcpy(fmtbuf, "250");
69658151Seric 	fmtbuf[3] = last ? ' ' : '-';
69758151Seric 
69858151Seric 	if (strchr(a->q_paddr, '<') != NULL)
69958151Seric 		strcpy(&fmtbuf[4], "%s");
70058151Seric 	else if (a->q_fullname == NULL)
70158151Seric 		strcpy(&fmtbuf[4], "<%s>");
70258151Seric 	else
70358151Seric 	{
70458151Seric 		strcpy(&fmtbuf[4], "%s <%s>");
70558151Seric 		message(fmtbuf, a->q_fullname, a->q_paddr);
70658151Seric 		return;
70758151Seric 	}
70858151Seric 	message(fmtbuf, a->q_paddr);
70958151Seric }
71058151Seric /*
7114577Seric **  HELP -- implement the HELP command.
7124577Seric **
7134577Seric **	Parameters:
7144577Seric **		topic -- the topic we want help for.
7154577Seric **
7164577Seric **	Returns:
7174577Seric **		none.
7184577Seric **
7194577Seric **	Side Effects:
7204577Seric **		outputs the help file to message output.
7214577Seric */
7224577Seric 
7234577Seric help(topic)
7244577Seric 	char *topic;
7254577Seric {
7264577Seric 	register FILE *hf;
7274577Seric 	int len;
7284577Seric 	char buf[MAXLINE];
7294577Seric 	bool noinfo;
7304577Seric 
7318269Seric 	if (HelpFile == NULL || (hf = fopen(HelpFile, "r")) == NULL)
7324577Seric 	{
7334577Seric 		/* no help */
73411931Seric 		errno = 0;
73558151Seric 		message("502 HELP not implemented");
7364577Seric 		return;
7374577Seric 	}
7384577Seric 
73949669Seric 	if (topic == NULL || *topic == '\0')
74049669Seric 		topic = "smtp";
74149669Seric 	else
74249669Seric 		makelower(topic);
74349669Seric 
7444577Seric 	len = strlen(topic);
7454577Seric 	noinfo = TRUE;
7464577Seric 
7474577Seric 	while (fgets(buf, sizeof buf, hf) != NULL)
7484577Seric 	{
7494577Seric 		if (strncmp(buf, topic, len) == 0)
7504577Seric 		{
7514577Seric 			register char *p;
7524577Seric 
75356795Seric 			p = strchr(buf, '\t');
7544577Seric 			if (p == NULL)
7554577Seric 				p = buf;
7564577Seric 			else
7574577Seric 				p++;
7584577Seric 			fixcrlf(p, TRUE);
75958151Seric 			message("214-%s", p);
7604577Seric 			noinfo = FALSE;
7614577Seric 		}
7624577Seric 	}
7634577Seric 
7644577Seric 	if (noinfo)
76558151Seric 		message("504 HELP topic unknown");
7664577Seric 	else
76758151Seric 		message("214 End of HELP info");
7684628Seric 	(void) fclose(hf);
7694577Seric }
7708544Seric /*
7719339Seric **  RUNINCHILD -- return twice -- once in the child, then in the parent again
7729339Seric **
7739339Seric **	Parameters:
7749339Seric **		label -- a string used in error messages
7759339Seric **
7769339Seric **	Returns:
7779339Seric **		zero in the child
7789339Seric **		one in the parent
7799339Seric **
7809339Seric **	Side Effects:
7819339Seric **		none.
7829339Seric */
7838544Seric 
78455012Seric runinchild(label, e)
7859339Seric 	char *label;
78655012Seric 	register ENVELOPE *e;
7879339Seric {
7889339Seric 	int childpid;
7899339Seric 
79016158Seric 	if (!OneXact)
7919339Seric 	{
79216158Seric 		childpid = dofork();
79316158Seric 		if (childpid < 0)
79416158Seric 		{
79516158Seric 			syserr("%s: cannot fork", label);
79616158Seric 			return (1);
79716158Seric 		}
79816158Seric 		if (childpid > 0)
79916158Seric 		{
80016158Seric 			auto int st;
8019339Seric 
80216158Seric 			/* parent -- wait for child to complete */
80359060Seric 			setproctitle("srvrsmtp %s child wait", CurHostName);
80416158Seric 			st = waitfor(childpid);
80516158Seric 			if (st == -1)
80616158Seric 				syserr("%s: lost child", label);
8079339Seric 
80816158Seric 			/* if we exited on a QUIT command, complete the process */
80916158Seric 			if (st == (EX_QUIT << 8))
81016158Seric 				finis();
8119339Seric 
81216158Seric 			return (1);
81316158Seric 		}
81416158Seric 		else
81516158Seric 		{
81616158Seric 			/* child */
81716158Seric 			InChild = TRUE;
81825050Seric 			QuickAbort = FALSE;
81955012Seric 			clearenvelope(e, FALSE);
82016158Seric 		}
8219339Seric 	}
82215256Seric 
82316158Seric 	/* open alias database */
82455012Seric 	initaliases(AliasFile, FALSE, e);
82516158Seric 
82616158Seric 	return (0);
8279339Seric }
8289339Seric 
82956795Seric # endif /* SMTP */
830