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*58412Seric static char sccsid[] = "@(#)srvrsmtp.c	6.23 (Berkeley) 03/03/93 (with SMTP)";
1433731Sbostic #else
15*58412Seric static char sccsid[] = "@(#)srvrsmtp.c	6.23 (Berkeley) 03/03/93 (without SMTP)";
1633731Sbostic #endif
1733731Sbostic #endif /* not lint */
1833731Sbostic 
199339Seric # include <errno.h>
2011728Seric # include <signal.h>
214549Seric 
2233731Sbostic # ifdef SMTP
234556Seric 
244549Seric /*
254549Seric **  SMTP -- run the SMTP protocol.
264549Seric **
274549Seric **	Parameters:
284549Seric **		none.
294549Seric **
304549Seric **	Returns:
314549Seric **		never.
324549Seric **
334549Seric **	Side Effects:
344549Seric **		Reads commands from the input channel and processes
354549Seric **			them.
364549Seric */
374549Seric 
384549Seric struct cmd
394549Seric {
404549Seric 	char	*cmdname;	/* command name */
414549Seric 	int	cmdcode;	/* internal code, see below */
424549Seric };
434549Seric 
444549Seric /* values for cmdcode */
454549Seric # define CMDERROR	0	/* bad command */
464549Seric # define CMDMAIL	1	/* mail -- designate sender */
474976Seric # define CMDRCPT	2	/* rcpt -- designate recipient */
484549Seric # define CMDDATA	3	/* data -- send message text */
499339Seric # define CMDRSET	4	/* rset -- reset state */
509339Seric # define CMDVRFY	5	/* vrfy -- verify address */
5158092Seric # define CMDEXPN	6	/* expn -- expand address */
529339Seric # define CMDNOOP	7	/* noop -- do nothing */
539339Seric # define CMDQUIT	8	/* quit -- close connection and die */
549339Seric # define CMDHELO	9	/* helo -- be polite */
5558092Seric # define CMDHELP	10	/* help -- give usage info */
5658323Seric # define CMDEHLO	11	/* ehlo -- extended helo (RFC 1425) */
5758092Seric /* non-standard commands */
5858092Seric # define CMDONEX	16	/* onex -- sending one transaction only */
5958092Seric # define CMDVERB	17	/* verb -- go into verbose mode */
6036230Skarels /* debugging-only commands, only enabled if SMTPDEBUG is defined */
6158092Seric # define CMDDBGQSHOW	24	/* showq -- show send queue */
6258092Seric # define CMDDBGDEBUG	25	/* debug -- set debug mode */
634549Seric 
644549Seric static struct cmd	CmdTab[] =
654549Seric {
664549Seric 	"mail",		CMDMAIL,
674976Seric 	"rcpt",		CMDRCPT,
684549Seric 	"data",		CMDDATA,
694549Seric 	"rset",		CMDRSET,
704549Seric 	"vrfy",		CMDVRFY,
7158092Seric 	"expn",		CMDEXPN,
724549Seric 	"help",		CMDHELP,
734549Seric 	"noop",		CMDNOOP,
744549Seric 	"quit",		CMDQUIT,
754976Seric 	"helo",		CMDHELO,
7658323Seric 	"ehlo",		CMDEHLO,
778544Seric 	"verb",		CMDVERB,
789314Seric 	"onex",		CMDONEX,
7936230Skarels 	/*
8036230Skarels 	 * remaining commands are here only
8136230Skarels 	 * to trap and log attempts to use them
8236230Skarels 	 */
839339Seric 	"showq",	CMDDBGQSHOW,
848544Seric 	"debug",	CMDDBGDEBUG,
854549Seric 	NULL,		CMDERROR,
864549Seric };
874549Seric 
889339Seric bool	InChild = FALSE;		/* true if running in a subprocess */
899378Seric bool	OneXact = FALSE;		/* one xaction only this run */
9011146Seric 
919339Seric #define EX_QUIT		22		/* special code for QUIT command */
928544Seric 
9355012Seric smtp(e)
9455012Seric 	register ENVELOPE *e;
954549Seric {
964549Seric 	register char *p;
978544Seric 	register struct cmd *c;
984549Seric 	char *cmd;
9946928Sbostic 	static char *skipword();
1005003Seric 	auto ADDRESS *vrfyqueue;
10112612Seric 	ADDRESS *a;
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;
1098544Seric 	char inp[MAXLINE];
11057232Seric 	char cmdbuf[MAXLINE];
1117124Seric 	extern char Version[];
11211151Seric 	extern char *macvalue();
11312612Seric 	extern ADDRESS *recipient();
11424943Seric 	extern ENVELOPE BlankEnvelope;
11524943Seric 	extern ENVELOPE *newenvelope();
1164549Seric 
1177363Seric 	if (OutChannel != stdout)
1187363Seric 	{
1197363Seric 		/* arrange for debugging output to go to remote host */
1207363Seric 		(void) close(1);
1217363Seric 		(void) dup(fileno(OutChannel));
1227363Seric 	}
12355012Seric 	settime(e);
12457642Seric 	CurHostName = RealHostName;
12557642Seric 	setproctitle("srvrsmtp %s", CurHostName);
12658050Seric 	expand("\201e", inp, &inp[sizeof inp], e);
12758151Seric 	message("220 %s", inp);
12824943Seric 	SmtpPhase = "startup";
12930448Seric 	sendinghost = NULL;
13058330Seric 	protocol = NULL;
13158082Seric 	gothello = FALSE;
13258330Seric 	gotmail = FALSE;
1334549Seric 	for (;;)
1344549Seric 	{
13512612Seric 		/* arrange for backout */
13612612Seric 		if (setjmp(TopFrame) > 0 && InChild)
13712612Seric 			finis();
13812612Seric 		QuickAbort = FALSE;
13912612Seric 		HoldErrs = FALSE;
14051951Seric 		LogUsrErrs = FALSE;
14158092Seric 		e->e_flags &= ~EF_VRFYONLY;
14212612Seric 
1437356Seric 		/* setup for the read */
14455012Seric 		e->e_to = NULL;
1454577Seric 		Errors = 0;
1467275Seric 		(void) fflush(stdout);
1477356Seric 
1487356Seric 		/* read the input line */
14958109Seric 		p = sfgets(inp, sizeof inp, InChannel, TimeOuts.to_nextcommand);
1507356Seric 
1517685Seric 		/* handle errors */
1527356Seric 		if (p == NULL)
1537356Seric 		{
1544549Seric 			/* end of file, just die */
15558151Seric 			message("421 %s Lost input channel from %s",
15625050Seric 				MyHostName, CurHostName);
15755464Seric #ifdef LOG
15858020Seric 			if (LogLevel > 1)
15955464Seric 				syslog(LOG_NOTICE, "lost input channel from %s",
16055464Seric 					CurHostName);
16155464Seric #endif
16258069Seric 			if (InChild)
16358069Seric 				ExitStat = EX_QUIT;
1644549Seric 			finis();
1654549Seric 		}
1664549Seric 
1674549Seric 		/* clean up end of line */
1684558Seric 		fixcrlf(inp, TRUE);
1694549Seric 
1704713Seric 		/* echo command to transcript */
17155012Seric 		if (e->e_xfp != NULL)
17255012Seric 			fprintf(e->e_xfp, "<<< %s\n", inp);
1734713Seric 
1744549Seric 		/* break off command */
17558050Seric 		for (p = inp; isascii(*p) && isspace(*p); p++)
1764549Seric 			continue;
17757232Seric 		cmd = cmdbuf;
17858050Seric 		while (*p != '\0' &&
17958050Seric 		       !(isascii(*p) && isspace(*p)) &&
18058050Seric 		       cmd < &cmdbuf[sizeof cmdbuf - 2])
18124981Seric 			*cmd++ = *p++;
18224981Seric 		*cmd = '\0';
1834549Seric 
18425691Seric 		/* throw away leading whitespace */
18558050Seric 		while (isascii(*p) && isspace(*p))
18625691Seric 			p++;
18725691Seric 
1884549Seric 		/* decode command */
1894549Seric 		for (c = CmdTab; c->cmdname != NULL; c++)
1904549Seric 		{
19133725Sbostic 			if (!strcasecmp(c->cmdname, cmdbuf))
1924549Seric 				break;
1934549Seric 		}
1944549Seric 
19551954Seric 		/* reset errors */
19651954Seric 		errno = 0;
19751954Seric 
1984549Seric 		/* process command */
1994549Seric 		switch (c->cmdcode)
2004549Seric 		{
2014976Seric 		  case CMDHELO:		/* hello -- introduce yourself */
20258323Seric 		  case CMDEHLO:		/* extended hello */
20358323Seric 			if (c->cmdcode == CMDEHLO)
20458323Seric 			{
20558323Seric 				protocol = "ESMTP";
20658323Seric 				SmtpPhase = "EHLO";
20758323Seric 			}
20858323Seric 			else
20958323Seric 			{
21058323Seric 				protocol = "SMTP";
21158323Seric 				SmtpPhase = "HELO";
21258323Seric 			}
21325050Seric 			setproctitle("%s: %s", CurHostName, inp);
21458109Seric 			if (strcasecmp(p, MyHostName) == 0)
21514877Seric 			{
21636230Skarels 				/*
21758109Seric 				**  Didn't know about alias or MX,
21858109Seric 				**  or connected to an echo server
21958109Seric 				*/
22058109Seric 
22158151Seric 				message("553 %s config error: mail loops back to myself",
22247570Seric 					MyHostName);
22314877Seric 				break;
22414877Seric 			}
22558308Seric 			if (strcasecmp(p, RealHostName) != 0)
22611146Seric 			{
22724981Seric 				char hostbuf[MAXNAME];
22811146Seric 
22924981Seric 				(void) sprintf(hostbuf, "%s (%s)", p, RealHostName);
23030448Seric 				sendinghost = newstr(hostbuf);
23111146Seric 			}
23211146Seric 			else
23330448Seric 				sendinghost = newstr(p);
23458323Seric 
23558323Seric 			/* send ext. message -- old systems must ignore */
23658323Seric 			message("250-%s Hello %s, pleased to meet you",
23736230Skarels 				MyHostName, sendinghost);
23858323Seric 			if (!bitset(PRIV_NOEXPN, PrivacyFlags))
23958323Seric 				message("250-EXPN");
24058338Seric 			message("250-SIZE");
24158323Seric 			message("250 HELP");
24258082Seric 			gothello = TRUE;
2434976Seric 			break;
2444976Seric 
2454549Seric 		  case CMDMAIL:		/* mail -- designate sender */
24624943Seric 			SmtpPhase = "MAIL";
24724943Seric 
24811151Seric 			/* force a sending host even if no HELO given */
24958064Seric 			if (sendinghost == NULL && macvalue('s', e) == NULL)
25030448Seric 				sendinghost = RealHostName;
25111151Seric 
2529314Seric 			/* check for validity of this command */
25358082Seric 			if (!gothello && bitset(PRIV_NEEDMAILHELO, PrivacyFlags))
25458082Seric 			{
25558151Seric 				message("503 Polite people say HELO first");
25658082Seric 				break;
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;
27358064Seric 			if (sendinghost != NULL)
27458064Seric 				define('s', sendinghost, e);
27558323Seric 			if (protocol == NULL)
27658323Seric 				protocol = "SMTP";
27758323Seric 			define('r', protocol, e);
27855012Seric 			initsys(e);
27957389Seric 			setproctitle("%s %s: %s", e->e_id, CurHostName, inp);
2809339Seric 
2819339Seric 			/* child -- go do the processing */
2824549Seric 			p = skipword(p, "from");
2834549Seric 			if (p == NULL)
2844549Seric 				break;
28557977Seric 			if (setjmp(TopFrame) > 0)
28658147Seric 			{
28758147Seric 				/* this failed -- undo work */
28858147Seric 				if (InChild)
28958147Seric 					finis();
29057977Seric 				break;
29158147Seric 			}
29257977Seric 			QuickAbort = TRUE;
29358333Seric 
29458333Seric 			/* must parse sender first */
29558333Seric 			delimptr = NULL;
29658333Seric 			setsender(p, e, &delimptr);
29758333Seric 			p = delimptr;
29858333Seric 			if (p != NULL && *p != '\0')
29958333Seric 				*p++ = '\0';
30058333Seric 
30158333Seric 			/* now parse ESMTP arguments */
30258333Seric 			msize = 0;
30358333Seric 			for (; p != NULL && *p != '\0'; p++)
30458333Seric 			{
30558333Seric 				char *kp;
30658333Seric 				char *vp;
30758333Seric 
30858333Seric 				/* locate the beginning of the keyword */
30958333Seric 				while (isascii(*p) && isspace(*p))
31058333Seric 					p++;
31158333Seric 				if (*p == '\0')
31258333Seric 					break;
31358333Seric 				kp = p;
31458333Seric 
31558333Seric 				/* skip to the value portion */
31658333Seric 				while (isascii(*p) && isalnum(*p) || *p == '-')
31758333Seric 					p++;
31858333Seric 				if (*p == '=')
31958333Seric 				{
32058333Seric 					*p++ = '\0';
32158333Seric 					vp = p;
32258333Seric 
32358333Seric 					/* skip to the end of the value */
32458333Seric 					while (*p != '\0' && *p != ' ' &&
32558333Seric 					       !(isascii(*p) && iscntrl(*p)) &&
32658333Seric 					       *p != '=')
32758333Seric 						p++;
32858333Seric 				}
32958333Seric 
33058333Seric 				if (*p != '\0')
33158333Seric 					*p++ = '\0';
33258333Seric 
33358333Seric 				if (tTd(19, 1))
33458333Seric 					printf("MAIL: got arg %s=%s\n", kp,
33558333Seric 						vp == NULL ? "<null>" : vp);
33658333Seric 
33758333Seric 				if (strcasecmp(kp, "size") == 0)
33858333Seric 				{
33958333Seric 					if (kp == NULL)
34058333Seric 					{
34158333Seric 						usrerr("501 SIZE requires a value");
34258333Seric 						/* NOTREACHED */
34358333Seric 					}
34458333Seric 					msize = atol(vp);
34558333Seric 				}
34658333Seric 				else
34758333Seric 				{
34858333Seric 					usrerr("501 %s parameter unrecognized", kp);
34958333Seric 					/* NOTREACHED */
35058333Seric 				}
35158333Seric 			}
35258333Seric 
35358333Seric 			if (!enoughspace(msize))
35458333Seric 			{
35558333Seric 				message("452 Insufficient disk space; try again later");
35658333Seric 				break;
35758333Seric 			}
35858151Seric 			message("250 Sender ok");
35958147Seric 			gotmail = TRUE;
3604549Seric 			break;
3614549Seric 
3624976Seric 		  case CMDRCPT:		/* rcpt -- designate recipient */
36324943Seric 			SmtpPhase = "RCPT";
36457389Seric 			setproctitle("%s %s: %s", e->e_id, CurHostName, inp);
36512612Seric 			if (setjmp(TopFrame) > 0)
36614785Seric 			{
36755012Seric 				e->e_flags &= ~EF_FATALERRS;
36812612Seric 				break;
36914785Seric 			}
37012612Seric 			QuickAbort = TRUE;
37151951Seric 			LogUsrErrs = TRUE;
37258093Seric 
37358093Seric 			/* optimization -- if queueing, don't expand aliases */
37458093Seric 			if (SendMode == SM_QUEUE)
37558093Seric 				e->e_flags |= EF_VRFYONLY;
37658093Seric 
3774549Seric 			p = skipword(p, "to");
3784549Seric 			if (p == NULL)
3794549Seric 				break;
38058333Seric 			a = parseaddr(p, (ADDRESS *) NULL, 1, ' ', NULL, e);
38112612Seric 			if (a == NULL)
38212612Seric 				break;
38316886Seric 			a->q_flags |= QPRIMARY;
38455012Seric 			a = recipient(a, &e->e_sendqueue, e);
38512612Seric 			if (Errors != 0)
38612612Seric 				break;
38712612Seric 
38812612Seric 			/* no errors during parsing, but might be a duplicate */
38955012Seric 			e->e_to = p;
39012612Seric 			if (!bitset(QBADADDR, a->q_flags))
39158151Seric 				message("250 Recipient ok");
39212612Seric 			else
3934549Seric 			{
39412612Seric 				/* punt -- should keep message in ADDRESS.... */
39558151Seric 				message("550 Addressee unknown");
3964549Seric 			}
39755012Seric 			e->e_to = NULL;
3984549Seric 			break;
3994549Seric 
4004549Seric 		  case CMDDATA:		/* data -- text of mail */
40124943Seric 			SmtpPhase = "DATA";
40258109Seric 			if (!gotmail)
4034549Seric 			{
40458151Seric 				message("503 Need MAIL command");
4054976Seric 				break;
4064549Seric 			}
40755012Seric 			else if (e->e_nrcpts <= 0)
4084549Seric 			{
40958151Seric 				message("503 Need RCPT (recipient)");
4104976Seric 				break;
4114549Seric 			}
4124976Seric 
4134976Seric 			/* collect the text of the message */
41424943Seric 			SmtpPhase = "collect";
41557389Seric 			setproctitle("%s %s: %s", e->e_id, CurHostName, inp);
41655012Seric 			collect(TRUE, e);
4174976Seric 			if (Errors != 0)
4184976Seric 				break;
4194976Seric 
4208238Seric 			/*
4218238Seric 			**  Arrange to send to everyone.
4228238Seric 			**	If sending to multiple people, mail back
4238238Seric 			**		errors rather than reporting directly.
4248238Seric 			**	In any case, don't mail back errors for
4258238Seric 			**		anything that has happened up to
4268238Seric 			**		now (the other end will do this).
42710197Seric 			**	Truncate our transcript -- the mail has gotten
42810197Seric 			**		to us successfully, and if we have
42910197Seric 			**		to mail this back, it will be easier
43010197Seric 			**		on the reader.
4318238Seric 			**	Then send to everyone.
4328238Seric 			**	Finally give a reply code.  If an error has
4338238Seric 			**		already been given, don't mail a
4348238Seric 			**		message back.
4359339Seric 			**	We goose error returns by clearing error bit.
4368238Seric 			*/
4378238Seric 
43824943Seric 			SmtpPhase = "delivery";
43955012Seric 			if (e->e_nrcpts != 1)
4409378Seric 			{
4419378Seric 				HoldErrs = TRUE;
44216886Seric 				ErrorMode = EM_MAIL;
4439378Seric 			}
44455012Seric 			e->e_flags &= ~EF_FATALERRS;
44555012Seric 			e->e_xfp = freopen(queuename(e, 'x'), "w", e->e_xfp);
4464976Seric 
4474976Seric 			/* send to all recipients */
44855012Seric 			sendall(e, SM_DEFAULT);
44955012Seric 			e->e_to = NULL;
4504976Seric 
45123516Seric 			/* save statistics */
45255012Seric 			markstats(e, (ADDRESS *) NULL);
45323516Seric 
4548238Seric 			/* issue success if appropriate and reset */
4558238Seric 			if (Errors == 0 || HoldErrs)
45658151Seric 				message("250 Ok");
4578238Seric 			else
45855012Seric 				e->e_flags &= ~EF_FATALERRS;
4599339Seric 
4609339Seric 			/* if in a child, pop back to our parent */
4619339Seric 			if (InChild)
4629339Seric 				finis();
46324943Seric 
46424943Seric 			/* clean up a bit */
46558109Seric 			gotmail = FALSE;
46655012Seric 			dropenvelope(e);
46758179Seric 			CurEnv = e = newenvelope(e, CurEnv);
46855012Seric 			e->e_flags = BlankEnvelope.e_flags;
4694549Seric 			break;
4704549Seric 
4714549Seric 		  case CMDRSET:		/* rset -- reset state */
47258151Seric 			message("250 Reset state");
4739339Seric 			if (InChild)
4749339Seric 				finis();
47558109Seric 
47658109Seric 			/* clean up a bit */
47758109Seric 			gotmail = FALSE;
47858109Seric 			dropenvelope(e);
47958179Seric 			CurEnv = e = newenvelope(e, CurEnv);
4809339Seric 			break;
4814549Seric 
4824549Seric 		  case CMDVRFY:		/* vrfy -- verify address */
48358092Seric 		  case CMDEXPN:		/* expn -- expand address */
48458092Seric 			vrfy = c->cmdcode == CMDVRFY;
48558092Seric 			if (bitset(vrfy ? PRIV_NOVRFY : PRIV_NOEXPN,
48658092Seric 						PrivacyFlags))
48758082Seric 			{
488*58412Seric 				if (vrfy)
489*58412Seric 					message("252 Who's to say?");
490*58412Seric 				else
491*58412Seric 					message("502 That's none of your business");
49258082Seric 				break;
49358082Seric 			}
49458082Seric 			else if (!gothello &&
49558092Seric 				 bitset(vrfy ? PRIV_NEEDVRFYHELO : PRIV_NEEDEXPNHELO,
49658092Seric 						PrivacyFlags))
49758082Seric 			{
49858151Seric 				message("503 I demand that you introduce yourself first");
49958082Seric 				break;
50058082Seric 			}
50158092Seric 			if (runinchild(vrfy ? "SMTP-VRFY" : "SMTP-EXPN", e) > 0)
5029339Seric 				break;
50325050Seric 			setproctitle("%s: %s", CurHostName, inp);
50455173Seric #ifdef LOG
50558020Seric 			if (LogLevel > 5)
50655173Seric 				syslog(LOG_INFO, "%s: %s", CurHostName, inp);
50755173Seric #endif
5085003Seric 			vrfyqueue = NULL;
5097762Seric 			QuickAbort = TRUE;
51058092Seric 			if (vrfy)
51158092Seric 				e->e_flags |= EF_VRFYONLY;
51258082Seric 			(void) sendtolist(p, (ADDRESS *) NULL, &vrfyqueue, e);
5137762Seric 			if (Errors != 0)
5149339Seric 			{
5159339Seric 				if (InChild)
5169339Seric 					finis();
5177762Seric 				break;
5189339Seric 			}
5195003Seric 			while (vrfyqueue != NULL)
5205003Seric 			{
5215003Seric 				register ADDRESS *a = vrfyqueue->q_next;
5225003Seric 				char *code;
5235003Seric 
5247685Seric 				while (a != NULL && bitset(QDONTSEND|QBADADDR, a->q_flags))
5255003Seric 					a = a->q_next;
5265003Seric 
5277685Seric 				if (!bitset(QDONTSEND|QBADADDR, vrfyqueue->q_flags))
52858151Seric 					printvrfyaddr(vrfyqueue, a == NULL);
5295003Seric 				else if (a == NULL)
53058151Seric 					message("554 Self destructive alias loop");
5315003Seric 				vrfyqueue = a;
5325003Seric 			}
5339339Seric 			if (InChild)
5349339Seric 				finis();
5354549Seric 			break;
5364549Seric 
5374549Seric 		  case CMDHELP:		/* help -- give user info */
5384577Seric 			help(p);
5394549Seric 			break;
5404549Seric 
5414549Seric 		  case CMDNOOP:		/* noop -- do nothing */
54258151Seric 			message("200 OK");
5434549Seric 			break;
5444549Seric 
5454549Seric 		  case CMDQUIT:		/* quit -- leave mail */
54658151Seric 			message("221 %s closing connection", MyHostName);
5479339Seric 			if (InChild)
5489339Seric 				ExitStat = EX_QUIT;
5494549Seric 			finis();
5504549Seric 
5518544Seric 		  case CMDVERB:		/* set verbose mode */
5528544Seric 			Verbose = TRUE;
55325025Seric 			SendMode = SM_DELIVER;
55458151Seric 			message("200 Verbose mode");
5558544Seric 			break;
5568544Seric 
5579314Seric 		  case CMDONEX:		/* doing one transaction only */
5589378Seric 			OneXact = TRUE;
55958151Seric 			message("200 Only one transaction");
5609314Seric 			break;
5619314Seric 
56236230Skarels # ifdef SMTPDEBUG
5639339Seric 		  case CMDDBGQSHOW:	/* show queues */
5646907Seric 			printf("Send Queue=");
56555012Seric 			printaddr(e->e_sendqueue, TRUE);
5665003Seric 			break;
5677275Seric 
5687275Seric 		  case CMDDBGDEBUG:	/* set debug mode */
5697676Seric 			tTsetup(tTdvect, sizeof tTdvect, "0-99.1");
5707676Seric 			tTflag(p);
57158151Seric 			message("200 Debug set");
5727275Seric 			break;
5737275Seric 
57436230Skarels # else /* not SMTPDEBUG */
57524945Seric 
57636230Skarels 		  case CMDDBGQSHOW:	/* show queues */
57736230Skarels 		  case CMDDBGDEBUG:	/* set debug mode */
57836233Skarels # ifdef LOG
57958308Seric 			if (LogLevel > 0)
58036230Skarels 				syslog(LOG_NOTICE,
58158020Seric 				    "\"%s\" command from %s (%s)",
58236230Skarels 				    c->cmdname, RealHostName,
58336230Skarels 				    inet_ntoa(RealHostAddr.sin_addr));
58436233Skarels # endif
58536230Skarels 			/* FALL THROUGH */
58636230Skarels # endif /* SMTPDEBUG */
58736230Skarels 
5884549Seric 		  case CMDERROR:	/* unknown command */
58958151Seric 			message("500 Command unrecognized");
5904549Seric 			break;
5914549Seric 
5924549Seric 		  default:
59336230Skarels 			errno = 0;
59458151Seric 			syserr("500 smtp: unknown code %d", c->cmdcode);
5954549Seric 			break;
5964549Seric 		}
5974549Seric 	}
5984549Seric }
5994549Seric /*
6004549Seric **  SKIPWORD -- skip a fixed word.
6014549Seric **
6024549Seric **	Parameters:
6034549Seric **		p -- place to start looking.
6044549Seric **		w -- word to skip.
6054549Seric **
6064549Seric **	Returns:
6074549Seric **		p following w.
6084549Seric **		NULL on error.
6094549Seric **
6104549Seric **	Side Effects:
6114549Seric **		clobbers the p data area.
6124549Seric */
6134549Seric 
6144549Seric static char *
6154549Seric skipword(p, w)
6164549Seric 	register char *p;
6174549Seric 	char *w;
6184549Seric {
6194549Seric 	register char *q;
6204549Seric 
6214549Seric 	/* find beginning of word */
62258050Seric 	while (isascii(*p) && isspace(*p))
6234549Seric 		p++;
6244549Seric 	q = p;
6254549Seric 
6264549Seric 	/* find end of word */
62758050Seric 	while (*p != '\0' && *p != ':' && !(isascii(*p) && isspace(*p)))
6284549Seric 		p++;
62958050Seric 	while (isascii(*p) && isspace(*p))
6304549Seric 		*p++ = '\0';
6314549Seric 	if (*p != ':')
6324549Seric 	{
6334549Seric 	  syntax:
63458151Seric 		message("501 Syntax error");
6354549Seric 		Errors++;
6364549Seric 		return (NULL);
6374549Seric 	}
6384549Seric 	*p++ = '\0';
63958050Seric 	while (isascii(*p) && isspace(*p))
6404549Seric 		p++;
6414549Seric 
6424549Seric 	/* see if the input word matches desired word */
64333725Sbostic 	if (strcasecmp(q, w))
6444549Seric 		goto syntax;
6454549Seric 
6464549Seric 	return (p);
6474549Seric }
6484577Seric /*
64958151Seric **  PRINTVRFYADDR -- print an entry in the verify queue
65058151Seric **
65158151Seric **	Parameters:
65258151Seric **		a -- the address to print
65358151Seric **		last -- set if this is the last one.
65458151Seric **
65558151Seric **	Returns:
65658151Seric **		none.
65758151Seric **
65858151Seric **	Side Effects:
65958151Seric **		Prints the appropriate 250 codes.
66058151Seric */
66158151Seric 
66258151Seric printvrfyaddr(a, last)
66358151Seric 	register ADDRESS *a;
66458151Seric 	bool last;
66558151Seric {
66658151Seric 	char fmtbuf[20];
66758151Seric 
66858151Seric 	strcpy(fmtbuf, "250");
66958151Seric 	fmtbuf[3] = last ? ' ' : '-';
67058151Seric 
67158151Seric 	if (strchr(a->q_paddr, '<') != NULL)
67258151Seric 		strcpy(&fmtbuf[4], "%s");
67358151Seric 	else if (a->q_fullname == NULL)
67458151Seric 		strcpy(&fmtbuf[4], "<%s>");
67558151Seric 	else
67658151Seric 	{
67758151Seric 		strcpy(&fmtbuf[4], "%s <%s>");
67858151Seric 		message(fmtbuf, a->q_fullname, a->q_paddr);
67958151Seric 		return;
68058151Seric 	}
68158151Seric 	message(fmtbuf, a->q_paddr);
68258151Seric }
68358151Seric /*
6844577Seric **  HELP -- implement the HELP command.
6854577Seric **
6864577Seric **	Parameters:
6874577Seric **		topic -- the topic we want help for.
6884577Seric **
6894577Seric **	Returns:
6904577Seric **		none.
6914577Seric **
6924577Seric **	Side Effects:
6934577Seric **		outputs the help file to message output.
6944577Seric */
6954577Seric 
6964577Seric help(topic)
6974577Seric 	char *topic;
6984577Seric {
6994577Seric 	register FILE *hf;
7004577Seric 	int len;
7014577Seric 	char buf[MAXLINE];
7024577Seric 	bool noinfo;
7034577Seric 
7048269Seric 	if (HelpFile == NULL || (hf = fopen(HelpFile, "r")) == NULL)
7054577Seric 	{
7064577Seric 		/* no help */
70711931Seric 		errno = 0;
70858151Seric 		message("502 HELP not implemented");
7094577Seric 		return;
7104577Seric 	}
7114577Seric 
71249669Seric 	if (topic == NULL || *topic == '\0')
71349669Seric 		topic = "smtp";
71449669Seric 	else
71549669Seric 		makelower(topic);
71649669Seric 
7174577Seric 	len = strlen(topic);
7184577Seric 	noinfo = TRUE;
7194577Seric 
7204577Seric 	while (fgets(buf, sizeof buf, hf) != NULL)
7214577Seric 	{
7224577Seric 		if (strncmp(buf, topic, len) == 0)
7234577Seric 		{
7244577Seric 			register char *p;
7254577Seric 
72656795Seric 			p = strchr(buf, '\t');
7274577Seric 			if (p == NULL)
7284577Seric 				p = buf;
7294577Seric 			else
7304577Seric 				p++;
7314577Seric 			fixcrlf(p, TRUE);
73258151Seric 			message("214-%s", p);
7334577Seric 			noinfo = FALSE;
7344577Seric 		}
7354577Seric 	}
7364577Seric 
7374577Seric 	if (noinfo)
73858151Seric 		message("504 HELP topic unknown");
7394577Seric 	else
74058151Seric 		message("214 End of HELP info");
7414628Seric 	(void) fclose(hf);
7424577Seric }
7438544Seric /*
7449339Seric **  RUNINCHILD -- return twice -- once in the child, then in the parent again
7459339Seric **
7469339Seric **	Parameters:
7479339Seric **		label -- a string used in error messages
7489339Seric **
7499339Seric **	Returns:
7509339Seric **		zero in the child
7519339Seric **		one in the parent
7529339Seric **
7539339Seric **	Side Effects:
7549339Seric **		none.
7559339Seric */
7568544Seric 
75755012Seric runinchild(label, e)
7589339Seric 	char *label;
75955012Seric 	register ENVELOPE *e;
7609339Seric {
7619339Seric 	int childpid;
7629339Seric 
76316158Seric 	if (!OneXact)
7649339Seric 	{
76516158Seric 		childpid = dofork();
76616158Seric 		if (childpid < 0)
76716158Seric 		{
76816158Seric 			syserr("%s: cannot fork", label);
76916158Seric 			return (1);
77016158Seric 		}
77116158Seric 		if (childpid > 0)
77216158Seric 		{
77316158Seric 			auto int st;
7749339Seric 
77516158Seric 			/* parent -- wait for child to complete */
77616158Seric 			st = waitfor(childpid);
77716158Seric 			if (st == -1)
77816158Seric 				syserr("%s: lost child", label);
7799339Seric 
78016158Seric 			/* if we exited on a QUIT command, complete the process */
78116158Seric 			if (st == (EX_QUIT << 8))
78216158Seric 				finis();
7839339Seric 
78416158Seric 			return (1);
78516158Seric 		}
78616158Seric 		else
78716158Seric 		{
78816158Seric 			/* child */
78916158Seric 			InChild = TRUE;
79025050Seric 			QuickAbort = FALSE;
79155012Seric 			clearenvelope(e, FALSE);
79216158Seric 		}
7939339Seric 	}
79415256Seric 
79516158Seric 	/* open alias database */
79655012Seric 	initaliases(AliasFile, FALSE, e);
79716158Seric 
79816158Seric 	return (0);
7999339Seric }
8009339Seric 
80156795Seric # endif /* SMTP */
802