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*58924Seric static char sccsid[] = "@(#)srvrsmtp.c	6.37 (Berkeley) 04/01/93 (with SMTP)";
1433731Sbostic #else
15*58924Seric static char sccsid[] = "@(#)srvrsmtp.c	6.37 (Berkeley) 04/01/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;
10230448Seric 	char *sendinghost;
10358109Seric 	bool gotmail;			/* mail command received */
10458092Seric 	bool gothello;			/* helo command received */
10558092Seric 	bool vrfy;			/* set if this is a vrfy command */
10658323Seric 	char *protocol;			/* sending protocol */
10758333Seric 	long msize;			/* approximate maximum message size */
10858333Seric 	auto char *delimptr;
10958714Seric 	char *id;
1108544Seric 	char inp[MAXLINE];
11157232Seric 	char cmdbuf[MAXLINE];
11258512Seric 	char hostbuf[MAXNAME];
1137124Seric 	extern char Version[];
11411151Seric 	extern char *macvalue();
11512612Seric 	extern ADDRESS *recipient();
11624943Seric 	extern ENVELOPE BlankEnvelope;
11724943Seric 	extern ENVELOPE *newenvelope();
11858755Seric 	extern char *anynet_ntoa();
1194549Seric 
1207363Seric 	if (OutChannel != stdout)
1217363Seric 	{
1227363Seric 		/* arrange for debugging output to go to remote host */
1237363Seric 		(void) close(1);
1247363Seric 		(void) dup(fileno(OutChannel));
1257363Seric 	}
12655012Seric 	settime(e);
12757642Seric 	CurHostName = RealHostName;
12857642Seric 	setproctitle("srvrsmtp %s", CurHostName);
12958050Seric 	expand("\201e", inp, &inp[sizeof inp], e);
13058151Seric 	message("220 %s", inp);
13124943Seric 	SmtpPhase = "startup";
13230448Seric 	sendinghost = NULL;
13358330Seric 	protocol = NULL;
13458082Seric 	gothello = FALSE;
13558330Seric 	gotmail = FALSE;
1364549Seric 	for (;;)
1374549Seric 	{
13812612Seric 		/* arrange for backout */
13912612Seric 		if (setjmp(TopFrame) > 0 && InChild)
14012612Seric 			finis();
14112612Seric 		QuickAbort = FALSE;
14212612Seric 		HoldErrs = FALSE;
14351951Seric 		LogUsrErrs = FALSE;
14458092Seric 		e->e_flags &= ~EF_VRFYONLY;
14512612Seric 
1467356Seric 		/* setup for the read */
14755012Seric 		e->e_to = NULL;
1484577Seric 		Errors = 0;
1497275Seric 		(void) fflush(stdout);
1507356Seric 
1517356Seric 		/* read the input line */
15258109Seric 		p = sfgets(inp, sizeof inp, InChannel, TimeOuts.to_nextcommand);
1537356Seric 
1547685Seric 		/* handle errors */
1557356Seric 		if (p == NULL)
1567356Seric 		{
1574549Seric 			/* end of file, just die */
15858151Seric 			message("421 %s Lost input channel from %s",
15925050Seric 				MyHostName, CurHostName);
16055464Seric #ifdef LOG
16158020Seric 			if (LogLevel > 1)
16255464Seric 				syslog(LOG_NOTICE, "lost input channel from %s",
16355464Seric 					CurHostName);
16455464Seric #endif
16558069Seric 			if (InChild)
16658069Seric 				ExitStat = EX_QUIT;
1674549Seric 			finis();
1684549Seric 		}
1694549Seric 
1704549Seric 		/* clean up end of line */
1714558Seric 		fixcrlf(inp, TRUE);
1724549Seric 
1734713Seric 		/* echo command to transcript */
17455012Seric 		if (e->e_xfp != NULL)
17555012Seric 			fprintf(e->e_xfp, "<<< %s\n", inp);
1764713Seric 
1774549Seric 		/* break off command */
17858050Seric 		for (p = inp; isascii(*p) && isspace(*p); p++)
1794549Seric 			continue;
18057232Seric 		cmd = cmdbuf;
18158050Seric 		while (*p != '\0' &&
18258050Seric 		       !(isascii(*p) && isspace(*p)) &&
18358050Seric 		       cmd < &cmdbuf[sizeof cmdbuf - 2])
18424981Seric 			*cmd++ = *p++;
18524981Seric 		*cmd = '\0';
1864549Seric 
18725691Seric 		/* throw away leading whitespace */
18858050Seric 		while (isascii(*p) && isspace(*p))
18925691Seric 			p++;
19025691Seric 
1914549Seric 		/* decode command */
1924549Seric 		for (c = CmdTab; c->cmdname != NULL; c++)
1934549Seric 		{
19433725Sbostic 			if (!strcasecmp(c->cmdname, cmdbuf))
1954549Seric 				break;
1964549Seric 		}
1974549Seric 
19851954Seric 		/* reset errors */
19951954Seric 		errno = 0;
20051954Seric 
2014549Seric 		/* process command */
2024549Seric 		switch (c->cmdcode)
2034549Seric 		{
2044976Seric 		  case CMDHELO:		/* hello -- introduce yourself */
20558323Seric 		  case CMDEHLO:		/* extended hello */
20658323Seric 			if (c->cmdcode == CMDEHLO)
20758323Seric 			{
20858323Seric 				protocol = "ESMTP";
20958323Seric 				SmtpPhase = "EHLO";
21058323Seric 			}
21158323Seric 			else
21258323Seric 			{
21358323Seric 				protocol = "SMTP";
21458323Seric 				SmtpPhase = "HELO";
21558323Seric 			}
21625050Seric 			setproctitle("%s: %s", CurHostName, inp);
21758109Seric 			if (strcasecmp(p, MyHostName) == 0)
21814877Seric 			{
21936230Skarels 				/*
22058109Seric 				**  Didn't know about alias or MX,
22158109Seric 				**  or connected to an echo server
22258109Seric 				*/
22358109Seric 
22458151Seric 				message("553 %s config error: mail loops back to myself",
22547570Seric 					MyHostName);
22614877Seric 				break;
22714877Seric 			}
22858512Seric 			(void) strcpy(hostbuf, p);
22958512Seric 			(void) strcat(hostbuf, " (");
23058755Seric 			(void) strcat(hostbuf, anynet_ntoa(&RealHostAddr));
23158308Seric 			if (strcasecmp(p, RealHostName) != 0)
23211146Seric 			{
23358789Seric 				auth_warning(e, "Host %s claimed to be %s",
23458789Seric 					RealHostName, p);
23558512Seric 				(void) strcat(hostbuf, "; ");
23658512Seric 				(void) strcat(hostbuf, RealHostName);
23711146Seric 			}
23858512Seric 			(void) strcat(hostbuf, ")");
23958512Seric 			sendinghost = newstr(hostbuf);
24058323Seric 
24158323Seric 			/* send ext. message -- old systems must ignore */
24258323Seric 			message("250-%s Hello %s, pleased to meet you",
24336230Skarels 				MyHostName, sendinghost);
24458323Seric 			if (!bitset(PRIV_NOEXPN, PrivacyFlags))
24558323Seric 				message("250-EXPN");
24658338Seric 			message("250-SIZE");
24758323Seric 			message("250 HELP");
24858082Seric 			gothello = TRUE;
2494976Seric 			break;
2504976Seric 
2514549Seric 		  case CMDMAIL:		/* mail -- designate sender */
25224943Seric 			SmtpPhase = "MAIL";
25324943Seric 
25411151Seric 			/* force a sending host even if no HELO given */
25558064Seric 			if (sendinghost == NULL && macvalue('s', e) == NULL)
25630448Seric 				sendinghost = RealHostName;
25711151Seric 
2589314Seric 			/* check for validity of this command */
25958789Seric 			if (!gothello)
26058082Seric 			{
26158789Seric 				if (bitset(PRIV_NEEDMAILHELO, PrivacyFlags))
26258821Seric 				{
26358789Seric 					message("503 Polite people say HELO first");
26458821Seric 					break;
26558821Seric 				}
26658789Seric 				else
26758821Seric 				{
26858789Seric 					auth_warning(e,
26958789Seric 						"Host %s didn't use HELO protocol",
27058789Seric 						RealHostName);
27158821Seric 				}
27258082Seric 			}
27358109Seric 			if (gotmail)
2744558Seric 			{
27558151Seric 				message("503 Sender already specified");
2764558Seric 				break;
2774558Seric 			}
2789339Seric 			if (InChild)
2799339Seric 			{
28036230Skarels 				errno = 0;
28158151Seric 				syserr("503 Nested MAIL command: MAIL %s", p);
28258069Seric 				finis();
2839339Seric 			}
2849339Seric 
2859339Seric 			/* fork a subprocess to process this command */
28655012Seric 			if (runinchild("SMTP-MAIL", e) > 0)
2879339Seric 				break;
28858064Seric 			if (sendinghost != NULL)
28958064Seric 				define('s', sendinghost, e);
29058323Seric 			if (protocol == NULL)
29158323Seric 				protocol = "SMTP";
29258323Seric 			define('r', protocol, e);
29355012Seric 			initsys(e);
29457389Seric 			setproctitle("%s %s: %s", e->e_id, CurHostName, inp);
2959339Seric 
2969339Seric 			/* child -- go do the processing */
2974549Seric 			p = skipword(p, "from");
2984549Seric 			if (p == NULL)
2994549Seric 				break;
30057977Seric 			if (setjmp(TopFrame) > 0)
30158147Seric 			{
30258147Seric 				/* this failed -- undo work */
30358147Seric 				if (InChild)
30458147Seric 					finis();
30557977Seric 				break;
30658147Seric 			}
30757977Seric 			QuickAbort = TRUE;
30858333Seric 
30958333Seric 			/* must parse sender first */
31058333Seric 			delimptr = NULL;
31158704Seric 			setsender(p, e, &delimptr, FALSE);
31258333Seric 			p = delimptr;
31358333Seric 			if (p != NULL && *p != '\0')
31458333Seric 				*p++ = '\0';
31558333Seric 
31658333Seric 			/* now parse ESMTP arguments */
31758333Seric 			msize = 0;
31858333Seric 			for (; p != NULL && *p != '\0'; p++)
31958333Seric 			{
32058333Seric 				char *kp;
32158333Seric 				char *vp;
32258333Seric 
32358333Seric 				/* locate the beginning of the keyword */
32458333Seric 				while (isascii(*p) && isspace(*p))
32558333Seric 					p++;
32658333Seric 				if (*p == '\0')
32758333Seric 					break;
32858333Seric 				kp = p;
32958333Seric 
33058333Seric 				/* skip to the value portion */
33158333Seric 				while (isascii(*p) && isalnum(*p) || *p == '-')
33258333Seric 					p++;
33358333Seric 				if (*p == '=')
33458333Seric 				{
33558333Seric 					*p++ = '\0';
33658333Seric 					vp = p;
33758333Seric 
33858333Seric 					/* skip to the end of the value */
33958333Seric 					while (*p != '\0' && *p != ' ' &&
34058333Seric 					       !(isascii(*p) && iscntrl(*p)) &&
34158333Seric 					       *p != '=')
34258333Seric 						p++;
34358333Seric 				}
34458333Seric 
34558333Seric 				if (*p != '\0')
34658333Seric 					*p++ = '\0';
34758333Seric 
34858333Seric 				if (tTd(19, 1))
34958333Seric 					printf("MAIL: got arg %s=%s\n", kp,
35058333Seric 						vp == NULL ? "<null>" : vp);
35158333Seric 
35258333Seric 				if (strcasecmp(kp, "size") == 0)
35358333Seric 				{
35458333Seric 					if (kp == NULL)
35558333Seric 					{
35658333Seric 						usrerr("501 SIZE requires a value");
35758333Seric 						/* NOTREACHED */
35858333Seric 					}
35958333Seric 					msize = atol(vp);
36058333Seric 				}
36158333Seric 				else
36258333Seric 				{
36358333Seric 					usrerr("501 %s parameter unrecognized", kp);
36458333Seric 					/* NOTREACHED */
36558333Seric 				}
36658333Seric 			}
36758333Seric 
36858333Seric 			if (!enoughspace(msize))
36958333Seric 			{
37058333Seric 				message("452 Insufficient disk space; try again later");
37158333Seric 				break;
37258333Seric 			}
37358151Seric 			message("250 Sender ok");
37458147Seric 			gotmail = TRUE;
3754549Seric 			break;
3764549Seric 
3774976Seric 		  case CMDRCPT:		/* rcpt -- designate recipient */
37858850Seric 			if (!gotmail)
37958850Seric 			{
38058850Seric 				usrerr("503 Need MAIL before RCPT");
38158850Seric 				break;
38258850Seric 			}
38324943Seric 			SmtpPhase = "RCPT";
38457389Seric 			setproctitle("%s %s: %s", e->e_id, CurHostName, inp);
38512612Seric 			if (setjmp(TopFrame) > 0)
38614785Seric 			{
38755012Seric 				e->e_flags &= ~EF_FATALERRS;
38812612Seric 				break;
38914785Seric 			}
39012612Seric 			QuickAbort = TRUE;
39151951Seric 			LogUsrErrs = TRUE;
39258093Seric 
39358919Seric 			e->e_flags |= EF_VRFYONLY;
39458919Seric 
3954549Seric 			p = skipword(p, "to");
3964549Seric 			if (p == NULL)
3974549Seric 				break;
39858333Seric 			a = parseaddr(p, (ADDRESS *) NULL, 1, ' ', NULL, e);
39912612Seric 			if (a == NULL)
40012612Seric 				break;
40116886Seric 			a->q_flags |= QPRIMARY;
40255012Seric 			a = recipient(a, &e->e_sendqueue, e);
40312612Seric 			if (Errors != 0)
40412612Seric 				break;
40512612Seric 
40612612Seric 			/* no errors during parsing, but might be a duplicate */
40755012Seric 			e->e_to = p;
40812612Seric 			if (!bitset(QBADADDR, a->q_flags))
40958151Seric 				message("250 Recipient ok");
41012612Seric 			else
4114549Seric 			{
41212612Seric 				/* punt -- should keep message in ADDRESS.... */
41358151Seric 				message("550 Addressee unknown");
4144549Seric 			}
41555012Seric 			e->e_to = NULL;
4164549Seric 			break;
4174549Seric 
4184549Seric 		  case CMDDATA:		/* data -- text of mail */
41924943Seric 			SmtpPhase = "DATA";
42058109Seric 			if (!gotmail)
4214549Seric 			{
42258151Seric 				message("503 Need MAIL command");
4234976Seric 				break;
4244549Seric 			}
42555012Seric 			else if (e->e_nrcpts <= 0)
4264549Seric 			{
42758151Seric 				message("503 Need RCPT (recipient)");
4284976Seric 				break;
4294549Seric 			}
4304976Seric 
4314976Seric 			/* collect the text of the message */
43224943Seric 			SmtpPhase = "collect";
43357389Seric 			setproctitle("%s %s: %s", e->e_id, CurHostName, inp);
43455012Seric 			collect(TRUE, e);
4354976Seric 			if (Errors != 0)
4364976Seric 				break;
4374976Seric 
4388238Seric 			/*
4398238Seric 			**  Arrange to send to everyone.
4408238Seric 			**	If sending to multiple people, mail back
4418238Seric 			**		errors rather than reporting directly.
4428238Seric 			**	In any case, don't mail back errors for
4438238Seric 			**		anything that has happened up to
4448238Seric 			**		now (the other end will do this).
44510197Seric 			**	Truncate our transcript -- the mail has gotten
44610197Seric 			**		to us successfully, and if we have
44710197Seric 			**		to mail this back, it will be easier
44810197Seric 			**		on the reader.
4498238Seric 			**	Then send to everyone.
4508238Seric 			**	Finally give a reply code.  If an error has
4518238Seric 			**		already been given, don't mail a
4528238Seric 			**		message back.
4539339Seric 			**	We goose error returns by clearing error bit.
4548238Seric 			*/
4558238Seric 
45624943Seric 			SmtpPhase = "delivery";
45755012Seric 			if (e->e_nrcpts != 1)
4589378Seric 			{
4599378Seric 				HoldErrs = TRUE;
46058734Seric 				e->e_errormode = EM_MAIL;
4619378Seric 			}
46255012Seric 			e->e_flags &= ~EF_FATALERRS;
46355012Seric 			e->e_xfp = freopen(queuename(e, 'x'), "w", e->e_xfp);
46458714Seric 			id = e->e_id;
4654976Seric 
46658919Seric 			/* check to see if we need to re-expand aliases */
46758919Seric 			for (a = e->e_sendqueue; a != NULL; a = a->q_next)
46858919Seric 			{
46958919Seric 				if (bitset(QVERIFIED, a->q_flags))
47058919Seric 					break;
47158919Seric 			}
47258919Seric 
4734976Seric 			/* send to all recipients */
47458919Seric 			sendall(e, a == NULL ? SM_DEFAULT : SM_QUEUE);
47555012Seric 			e->e_to = NULL;
4764976Seric 
47723516Seric 			/* save statistics */
47855012Seric 			markstats(e, (ADDRESS *) NULL);
47923516Seric 
4808238Seric 			/* issue success if appropriate and reset */
4818238Seric 			if (Errors == 0 || HoldErrs)
48258855Seric 				message("250 %s Message accepted for delivery", id);
4838238Seric 			else
48455012Seric 				e->e_flags &= ~EF_FATALERRS;
4859339Seric 
48658919Seric 			/* if we just queued, poke it */
48758919Seric 			if (a != NULL && e->e_sendmode != SM_QUEUE)
48858919Seric 			{
48958919Seric 				unlockqueue(e);
490*58924Seric 				dowork(id, TRUE, TRUE, e);
49158919Seric 				e->e_id = NULL;
49258919Seric 			}
49358883Seric 
4949339Seric 			/* if in a child, pop back to our parent */
4959339Seric 			if (InChild)
4969339Seric 				finis();
49724943Seric 
49824943Seric 			/* clean up a bit */
49958109Seric 			gotmail = FALSE;
50055012Seric 			dropenvelope(e);
50158179Seric 			CurEnv = e = newenvelope(e, CurEnv);
50255012Seric 			e->e_flags = BlankEnvelope.e_flags;
5034549Seric 			break;
5044549Seric 
5054549Seric 		  case CMDRSET:		/* rset -- reset state */
50658151Seric 			message("250 Reset state");
5079339Seric 			if (InChild)
5089339Seric 				finis();
50958109Seric 
51058109Seric 			/* clean up a bit */
51158109Seric 			gotmail = FALSE;
51258109Seric 			dropenvelope(e);
51358179Seric 			CurEnv = e = newenvelope(e, CurEnv);
5149339Seric 			break;
5154549Seric 
5164549Seric 		  case CMDVRFY:		/* vrfy -- verify address */
51758092Seric 		  case CMDEXPN:		/* expn -- expand address */
51858092Seric 			vrfy = c->cmdcode == CMDVRFY;
51958092Seric 			if (bitset(vrfy ? PRIV_NOVRFY : PRIV_NOEXPN,
52058092Seric 						PrivacyFlags))
52158082Seric 			{
52258412Seric 				if (vrfy)
52358412Seric 					message("252 Who's to say?");
52458412Seric 				else
52558412Seric 					message("502 That's none of your business");
52658082Seric 				break;
52758082Seric 			}
52858082Seric 			else if (!gothello &&
52958092Seric 				 bitset(vrfy ? PRIV_NEEDVRFYHELO : PRIV_NEEDEXPNHELO,
53058092Seric 						PrivacyFlags))
53158082Seric 			{
53258151Seric 				message("503 I demand that you introduce yourself first");
53358082Seric 				break;
53458082Seric 			}
53558092Seric 			if (runinchild(vrfy ? "SMTP-VRFY" : "SMTP-EXPN", e) > 0)
5369339Seric 				break;
53725050Seric 			setproctitle("%s: %s", CurHostName, inp);
53855173Seric #ifdef LOG
53958020Seric 			if (LogLevel > 5)
54055173Seric 				syslog(LOG_INFO, "%s: %s", CurHostName, inp);
54155173Seric #endif
5425003Seric 			vrfyqueue = NULL;
5437762Seric 			QuickAbort = TRUE;
54458092Seric 			if (vrfy)
54558092Seric 				e->e_flags |= EF_VRFYONLY;
54658082Seric 			(void) sendtolist(p, (ADDRESS *) NULL, &vrfyqueue, e);
5477762Seric 			if (Errors != 0)
5489339Seric 			{
5499339Seric 				if (InChild)
5509339Seric 					finis();
5517762Seric 				break;
5529339Seric 			}
5535003Seric 			while (vrfyqueue != NULL)
5545003Seric 			{
5555003Seric 				register ADDRESS *a = vrfyqueue->q_next;
5565003Seric 				char *code;
5575003Seric 
5587685Seric 				while (a != NULL && bitset(QDONTSEND|QBADADDR, a->q_flags))
5595003Seric 					a = a->q_next;
5605003Seric 
5617685Seric 				if (!bitset(QDONTSEND|QBADADDR, vrfyqueue->q_flags))
56258151Seric 					printvrfyaddr(vrfyqueue, a == NULL);
5635003Seric 				else if (a == NULL)
56458151Seric 					message("554 Self destructive alias loop");
5655003Seric 				vrfyqueue = a;
5665003Seric 			}
5679339Seric 			if (InChild)
5689339Seric 				finis();
5694549Seric 			break;
5704549Seric 
5714549Seric 		  case CMDHELP:		/* help -- give user info */
5724577Seric 			help(p);
5734549Seric 			break;
5744549Seric 
5754549Seric 		  case CMDNOOP:		/* noop -- do nothing */
57658151Seric 			message("200 OK");
5774549Seric 			break;
5784549Seric 
5794549Seric 		  case CMDQUIT:		/* quit -- leave mail */
58058151Seric 			message("221 %s closing connection", MyHostName);
5819339Seric 			if (InChild)
5829339Seric 				ExitStat = EX_QUIT;
5834549Seric 			finis();
5844549Seric 
5858544Seric 		  case CMDVERB:		/* set verbose mode */
5868544Seric 			Verbose = TRUE;
58758734Seric 			e->e_sendmode = SM_DELIVER;
58858151Seric 			message("200 Verbose mode");
5898544Seric 			break;
5908544Seric 
5919314Seric 		  case CMDONEX:		/* doing one transaction only */
5929378Seric 			OneXact = TRUE;
59358151Seric 			message("200 Only one transaction");
5949314Seric 			break;
5959314Seric 
59636230Skarels # ifdef SMTPDEBUG
5979339Seric 		  case CMDDBGQSHOW:	/* show queues */
5986907Seric 			printf("Send Queue=");
59955012Seric 			printaddr(e->e_sendqueue, TRUE);
6005003Seric 			break;
6017275Seric 
6027275Seric 		  case CMDDBGDEBUG:	/* set debug mode */
6037676Seric 			tTsetup(tTdvect, sizeof tTdvect, "0-99.1");
6047676Seric 			tTflag(p);
60558151Seric 			message("200 Debug set");
6067275Seric 			break;
6077275Seric 
60836230Skarels # else /* not SMTPDEBUG */
60924945Seric 
61036230Skarels 		  case CMDDBGQSHOW:	/* show queues */
61136230Skarels 		  case CMDDBGDEBUG:	/* set debug mode */
61236233Skarels # ifdef LOG
61358308Seric 			if (LogLevel > 0)
61436230Skarels 				syslog(LOG_NOTICE,
61558020Seric 				    "\"%s\" command from %s (%s)",
61636230Skarels 				    c->cmdname, RealHostName,
61758755Seric 				    anynet_ntoa(&RealHostAddr));
61836233Skarels # endif
61936230Skarels 			/* FALL THROUGH */
62036230Skarels # endif /* SMTPDEBUG */
62136230Skarels 
6224549Seric 		  case CMDERROR:	/* unknown command */
62358151Seric 			message("500 Command unrecognized");
6244549Seric 			break;
6254549Seric 
6264549Seric 		  default:
62736230Skarels 			errno = 0;
62858151Seric 			syserr("500 smtp: unknown code %d", c->cmdcode);
6294549Seric 			break;
6304549Seric 		}
6314549Seric 	}
6324549Seric }
6334549Seric /*
6344549Seric **  SKIPWORD -- skip a fixed word.
6354549Seric **
6364549Seric **	Parameters:
6374549Seric **		p -- place to start looking.
6384549Seric **		w -- word to skip.
6394549Seric **
6404549Seric **	Returns:
6414549Seric **		p following w.
6424549Seric **		NULL on error.
6434549Seric **
6444549Seric **	Side Effects:
6454549Seric **		clobbers the p data area.
6464549Seric */
6474549Seric 
6484549Seric static char *
6494549Seric skipword(p, w)
6504549Seric 	register char *p;
6514549Seric 	char *w;
6524549Seric {
6534549Seric 	register char *q;
6544549Seric 
6554549Seric 	/* find beginning of word */
65658050Seric 	while (isascii(*p) && isspace(*p))
6574549Seric 		p++;
6584549Seric 	q = p;
6594549Seric 
6604549Seric 	/* find end of word */
66158050Seric 	while (*p != '\0' && *p != ':' && !(isascii(*p) && isspace(*p)))
6624549Seric 		p++;
66358050Seric 	while (isascii(*p) && isspace(*p))
6644549Seric 		*p++ = '\0';
6654549Seric 	if (*p != ':')
6664549Seric 	{
6674549Seric 	  syntax:
66858151Seric 		message("501 Syntax error");
6694549Seric 		Errors++;
6704549Seric 		return (NULL);
6714549Seric 	}
6724549Seric 	*p++ = '\0';
67358050Seric 	while (isascii(*p) && isspace(*p))
6744549Seric 		p++;
6754549Seric 
6764549Seric 	/* see if the input word matches desired word */
67733725Sbostic 	if (strcasecmp(q, w))
6784549Seric 		goto syntax;
6794549Seric 
6804549Seric 	return (p);
6814549Seric }
6824577Seric /*
68358151Seric **  PRINTVRFYADDR -- print an entry in the verify queue
68458151Seric **
68558151Seric **	Parameters:
68658151Seric **		a -- the address to print
68758151Seric **		last -- set if this is the last one.
68858151Seric **
68958151Seric **	Returns:
69058151Seric **		none.
69158151Seric **
69258151Seric **	Side Effects:
69358151Seric **		Prints the appropriate 250 codes.
69458151Seric */
69558151Seric 
69658151Seric printvrfyaddr(a, last)
69758151Seric 	register ADDRESS *a;
69858151Seric 	bool last;
69958151Seric {
70058151Seric 	char fmtbuf[20];
70158151Seric 
70258151Seric 	strcpy(fmtbuf, "250");
70358151Seric 	fmtbuf[3] = last ? ' ' : '-';
70458151Seric 
70558151Seric 	if (strchr(a->q_paddr, '<') != NULL)
70658151Seric 		strcpy(&fmtbuf[4], "%s");
70758151Seric 	else if (a->q_fullname == NULL)
70858151Seric 		strcpy(&fmtbuf[4], "<%s>");
70958151Seric 	else
71058151Seric 	{
71158151Seric 		strcpy(&fmtbuf[4], "%s <%s>");
71258151Seric 		message(fmtbuf, a->q_fullname, a->q_paddr);
71358151Seric 		return;
71458151Seric 	}
71558151Seric 	message(fmtbuf, a->q_paddr);
71658151Seric }
71758151Seric /*
7184577Seric **  HELP -- implement the HELP command.
7194577Seric **
7204577Seric **	Parameters:
7214577Seric **		topic -- the topic we want help for.
7224577Seric **
7234577Seric **	Returns:
7244577Seric **		none.
7254577Seric **
7264577Seric **	Side Effects:
7274577Seric **		outputs the help file to message output.
7284577Seric */
7294577Seric 
7304577Seric help(topic)
7314577Seric 	char *topic;
7324577Seric {
7334577Seric 	register FILE *hf;
7344577Seric 	int len;
7354577Seric 	char buf[MAXLINE];
7364577Seric 	bool noinfo;
7374577Seric 
7388269Seric 	if (HelpFile == NULL || (hf = fopen(HelpFile, "r")) == NULL)
7394577Seric 	{
7404577Seric 		/* no help */
74111931Seric 		errno = 0;
74258151Seric 		message("502 HELP not implemented");
7434577Seric 		return;
7444577Seric 	}
7454577Seric 
74649669Seric 	if (topic == NULL || *topic == '\0')
74749669Seric 		topic = "smtp";
74849669Seric 	else
74949669Seric 		makelower(topic);
75049669Seric 
7514577Seric 	len = strlen(topic);
7524577Seric 	noinfo = TRUE;
7534577Seric 
7544577Seric 	while (fgets(buf, sizeof buf, hf) != NULL)
7554577Seric 	{
7564577Seric 		if (strncmp(buf, topic, len) == 0)
7574577Seric 		{
7584577Seric 			register char *p;
7594577Seric 
76056795Seric 			p = strchr(buf, '\t');
7614577Seric 			if (p == NULL)
7624577Seric 				p = buf;
7634577Seric 			else
7644577Seric 				p++;
7654577Seric 			fixcrlf(p, TRUE);
76658151Seric 			message("214-%s", p);
7674577Seric 			noinfo = FALSE;
7684577Seric 		}
7694577Seric 	}
7704577Seric 
7714577Seric 	if (noinfo)
77258151Seric 		message("504 HELP topic unknown");
7734577Seric 	else
77458151Seric 		message("214 End of HELP info");
7754628Seric 	(void) fclose(hf);
7764577Seric }
7778544Seric /*
7789339Seric **  RUNINCHILD -- return twice -- once in the child, then in the parent again
7799339Seric **
7809339Seric **	Parameters:
7819339Seric **		label -- a string used in error messages
7829339Seric **
7839339Seric **	Returns:
7849339Seric **		zero in the child
7859339Seric **		one in the parent
7869339Seric **
7879339Seric **	Side Effects:
7889339Seric **		none.
7899339Seric */
7908544Seric 
79155012Seric runinchild(label, e)
7929339Seric 	char *label;
79355012Seric 	register ENVELOPE *e;
7949339Seric {
7959339Seric 	int childpid;
7969339Seric 
79716158Seric 	if (!OneXact)
7989339Seric 	{
79916158Seric 		childpid = dofork();
80016158Seric 		if (childpid < 0)
80116158Seric 		{
80216158Seric 			syserr("%s: cannot fork", label);
80316158Seric 			return (1);
80416158Seric 		}
80516158Seric 		if (childpid > 0)
80616158Seric 		{
80716158Seric 			auto int st;
8089339Seric 
80916158Seric 			/* parent -- wait for child to complete */
81016158Seric 			st = waitfor(childpid);
81116158Seric 			if (st == -1)
81216158Seric 				syserr("%s: lost child", label);
8139339Seric 
81416158Seric 			/* if we exited on a QUIT command, complete the process */
81516158Seric 			if (st == (EX_QUIT << 8))
81616158Seric 				finis();
8179339Seric 
81816158Seric 			return (1);
81916158Seric 		}
82016158Seric 		else
82116158Seric 		{
82216158Seric 			/* child */
82316158Seric 			InChild = TRUE;
82425050Seric 			QuickAbort = FALSE;
82555012Seric 			clearenvelope(e, FALSE);
82616158Seric 		}
8279339Seric 	}
82815256Seric 
82916158Seric 	/* open alias database */
83055012Seric 	initaliases(AliasFile, FALSE, e);
83116158Seric 
83216158Seric 	return (0);
8339339Seric }
8349339Seric 
83556795Seric # endif /* SMTP */
836