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*58092Seric static char sccsid[] = "@(#)srvrsmtp.c	6.12 (Berkeley) 02/20/93 (with SMTP)";
1433731Sbostic #else
15*58092Seric static char sccsid[] = "@(#)srvrsmtp.c	6.12 (Berkeley) 02/20/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 */
51*58092Seric # 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 */
55*58092Seric # define CMDHELP	10	/* help -- give usage info */
56*58092Seric /* non-standard commands */
57*58092Seric # define CMDONEX	16	/* onex -- sending one transaction only */
58*58092Seric # define CMDVERB	17	/* verb -- go into verbose mode */
5936230Skarels /* debugging-only commands, only enabled if SMTPDEBUG is defined */
60*58092Seric # define CMDDBGQSHOW	24	/* showq -- show send queue */
61*58092Seric # define CMDDBGDEBUG	25	/* debug -- set debug mode */
624549Seric 
634549Seric static struct cmd	CmdTab[] =
644549Seric {
654549Seric 	"mail",		CMDMAIL,
664976Seric 	"rcpt",		CMDRCPT,
674549Seric 	"data",		CMDDATA,
684549Seric 	"rset",		CMDRSET,
694549Seric 	"vrfy",		CMDVRFY,
70*58092Seric 	"expn",		CMDEXPN,
714549Seric 	"help",		CMDHELP,
724549Seric 	"noop",		CMDNOOP,
734549Seric 	"quit",		CMDQUIT,
744976Seric 	"helo",		CMDHELO,
758544Seric 	"verb",		CMDVERB,
769314Seric 	"onex",		CMDONEX,
7736230Skarels 	/*
7836230Skarels 	 * remaining commands are here only
7936230Skarels 	 * to trap and log attempts to use them
8036230Skarels 	 */
819339Seric 	"showq",	CMDDBGQSHOW,
828544Seric 	"debug",	CMDDBGDEBUG,
834549Seric 	NULL,		CMDERROR,
844549Seric };
854549Seric 
869339Seric bool	InChild = FALSE;		/* true if running in a subprocess */
879378Seric bool	OneXact = FALSE;		/* one xaction only this run */
8811146Seric 
899339Seric #define EX_QUIT		22		/* special code for QUIT command */
908544Seric 
9155012Seric smtp(e)
9255012Seric 	register ENVELOPE *e;
934549Seric {
944549Seric 	register char *p;
958544Seric 	register struct cmd *c;
964549Seric 	char *cmd;
9746928Sbostic 	static char *skipword();
985003Seric 	auto ADDRESS *vrfyqueue;
9912612Seric 	ADDRESS *a;
10030448Seric 	char *sendinghost;
101*58092Seric 	bool hasmail;			/* mail command received */
102*58092Seric 	bool gothello;			/* helo command received */
103*58092Seric 	bool vrfy;			/* set if this is a vrfy command */
1048544Seric 	char inp[MAXLINE];
10557232Seric 	char cmdbuf[MAXLINE];
1067124Seric 	extern char Version[];
10711151Seric 	extern char *macvalue();
10812612Seric 	extern ADDRESS *recipient();
10924943Seric 	extern ENVELOPE BlankEnvelope;
11024943Seric 	extern ENVELOPE *newenvelope();
1114549Seric 
1125003Seric 	hasmail = FALSE;
1137363Seric 	if (OutChannel != stdout)
1147363Seric 	{
1157363Seric 		/* arrange for debugging output to go to remote host */
1167363Seric 		(void) close(1);
1177363Seric 		(void) dup(fileno(OutChannel));
1187363Seric 	}
11955012Seric 	settime(e);
12057642Seric 	if (RealHostName == NULL)
12157642Seric 		RealHostName = MyHostName;
12257642Seric 	CurHostName = RealHostName;
12357642Seric 	setproctitle("srvrsmtp %s", CurHostName);
12458050Seric 	expand("\201e", inp, &inp[sizeof inp], e);
12555360Seric 	message("220", "%s", inp);
12624943Seric 	SmtpPhase = "startup";
12730448Seric 	sendinghost = NULL;
12858082Seric 	gothello = FALSE;
1294549Seric 	for (;;)
1304549Seric 	{
13112612Seric 		/* arrange for backout */
13212612Seric 		if (setjmp(TopFrame) > 0 && InChild)
13312612Seric 			finis();
13412612Seric 		QuickAbort = FALSE;
13512612Seric 		HoldErrs = FALSE;
13651951Seric 		LogUsrErrs = FALSE;
137*58092Seric 		e->e_flags &= ~EF_VRFYONLY;
13812612Seric 
1397356Seric 		/* setup for the read */
14055012Seric 		e->e_to = NULL;
1414577Seric 		Errors = 0;
1427275Seric 		(void) fflush(stdout);
1437356Seric 
1447356Seric 		/* read the input line */
14557389Seric 		p = sfgets(inp, sizeof inp, InChannel, ReadTimeout);
1467356Seric 
1477685Seric 		/* handle errors */
1487356Seric 		if (p == NULL)
1497356Seric 		{
1504549Seric 			/* end of file, just die */
15136230Skarels 			message("421", "%s Lost input channel from %s",
15225050Seric 				MyHostName, CurHostName);
15355464Seric #ifdef LOG
15458020Seric 			if (LogLevel > 1)
15555464Seric 				syslog(LOG_NOTICE, "lost input channel from %s",
15655464Seric 					CurHostName);
15755464Seric #endif
15858069Seric 			if (InChild)
15958069Seric 				ExitStat = EX_QUIT;
1604549Seric 			finis();
1614549Seric 		}
1624549Seric 
1634549Seric 		/* clean up end of line */
1644558Seric 		fixcrlf(inp, TRUE);
1654549Seric 
1664713Seric 		/* echo command to transcript */
16755012Seric 		if (e->e_xfp != NULL)
16855012Seric 			fprintf(e->e_xfp, "<<< %s\n", inp);
1694713Seric 
1704549Seric 		/* break off command */
17158050Seric 		for (p = inp; isascii(*p) && isspace(*p); p++)
1724549Seric 			continue;
17357232Seric 		cmd = cmdbuf;
17458050Seric 		while (*p != '\0' &&
17558050Seric 		       !(isascii(*p) && isspace(*p)) &&
17658050Seric 		       cmd < &cmdbuf[sizeof cmdbuf - 2])
17724981Seric 			*cmd++ = *p++;
17824981Seric 		*cmd = '\0';
1794549Seric 
18025691Seric 		/* throw away leading whitespace */
18158050Seric 		while (isascii(*p) && isspace(*p))
18225691Seric 			p++;
18325691Seric 
1844549Seric 		/* decode command */
1854549Seric 		for (c = CmdTab; c->cmdname != NULL; c++)
1864549Seric 		{
18733725Sbostic 			if (!strcasecmp(c->cmdname, cmdbuf))
1884549Seric 				break;
1894549Seric 		}
1904549Seric 
19151954Seric 		/* reset errors */
19251954Seric 		errno = 0;
19351954Seric 
1944549Seric 		/* process command */
1954549Seric 		switch (c->cmdcode)
1964549Seric 		{
1974976Seric 		  case CMDHELO:		/* hello -- introduce yourself */
19824943Seric 			SmtpPhase = "HELO";
19925050Seric 			setproctitle("%s: %s", CurHostName, inp);
20033725Sbostic 			if (!strcasecmp(p, MyHostName))
20114877Seric 			{
20236230Skarels 				/*
20336230Skarels 				 * didn't know about alias,
20436230Skarels 				 * or connected to an echo server
20536230Skarels 				 */
20647570Seric 				message("553", "%s config error: mail loops back to myself",
20747570Seric 					MyHostName);
20814877Seric 				break;
20914877Seric 			}
21033725Sbostic 			if (RealHostName != NULL && strcasecmp(p, RealHostName))
21111146Seric 			{
21224981Seric 				char hostbuf[MAXNAME];
21311146Seric 
21424981Seric 				(void) sprintf(hostbuf, "%s (%s)", p, RealHostName);
21530448Seric 				sendinghost = newstr(hostbuf);
21611146Seric 			}
21711146Seric 			else
21830448Seric 				sendinghost = newstr(p);
2194997Seric 			message("250", "%s Hello %s, pleased to meet you",
22036230Skarels 				MyHostName, sendinghost);
22158082Seric 			gothello = TRUE;
2224976Seric 			break;
2234976Seric 
2244549Seric 		  case CMDMAIL:		/* mail -- designate sender */
22524943Seric 			SmtpPhase = "MAIL";
22624943Seric 
22711151Seric 			/* force a sending host even if no HELO given */
22858064Seric 			if (sendinghost == NULL && macvalue('s', e) == NULL)
22930448Seric 				sendinghost = RealHostName;
23011151Seric 
2319314Seric 			/* check for validity of this command */
23258082Seric 			if (!gothello && bitset(PRIV_NEEDMAILHELO, PrivacyFlags))
23358082Seric 			{
23458082Seric 				message("503", "Polite people say HELO first");
23558082Seric 				break;
23658082Seric 			}
2374558Seric 			if (hasmail)
2384558Seric 			{
2394558Seric 				message("503", "Sender already specified");
2404558Seric 				break;
2414558Seric 			}
2429339Seric 			if (InChild)
2439339Seric 			{
24436230Skarels 				errno = 0;
24558008Seric 				syserr("Nested MAIL command: MAIL %s", p);
24658069Seric 				finis();
2479339Seric 			}
24858082Seric 			if (!enoughspace())
24958082Seric 			{
25058082Seric 				message("452", "Insufficient disk space; try again later");
25158082Seric 				break;
25258082Seric 			}
2539339Seric 
2549339Seric 			/* fork a subprocess to process this command */
25555012Seric 			if (runinchild("SMTP-MAIL", e) > 0)
2569339Seric 				break;
25758064Seric 			if (sendinghost != NULL)
25858064Seric 				define('s', sendinghost, e);
25955012Seric 			define('r', "SMTP", e);
26055012Seric 			initsys(e);
26157389Seric 			setproctitle("%s %s: %s", e->e_id, CurHostName, inp);
2629339Seric 
2639339Seric 			/* child -- go do the processing */
2644549Seric 			p = skipword(p, "from");
2654549Seric 			if (p == NULL)
2664549Seric 				break;
26757977Seric 			if (setjmp(TopFrame) > 0)
26857977Seric 				break;
26957977Seric 			QuickAbort = TRUE;
27055012Seric 			setsender(p, e);
2714577Seric 			if (Errors == 0)
2724549Seric 			{
2734549Seric 				message("250", "Sender ok");
2744549Seric 				hasmail = TRUE;
2754549Seric 			}
2769339Seric 			else if (InChild)
2779339Seric 				finis();
2784549Seric 			break;
2794549Seric 
2804976Seric 		  case CMDRCPT:		/* rcpt -- designate recipient */
28124943Seric 			SmtpPhase = "RCPT";
28257389Seric 			setproctitle("%s %s: %s", e->e_id, CurHostName, inp);
28312612Seric 			if (setjmp(TopFrame) > 0)
28414785Seric 			{
28555012Seric 				e->e_flags &= ~EF_FATALERRS;
28612612Seric 				break;
28714785Seric 			}
28812612Seric 			QuickAbort = TRUE;
28951951Seric 			LogUsrErrs = TRUE;
2904549Seric 			p = skipword(p, "to");
2914549Seric 			if (p == NULL)
2924549Seric 				break;
29355012Seric 			a = parseaddr(p, (ADDRESS *) NULL, 1, '\0', e);
29412612Seric 			if (a == NULL)
29512612Seric 				break;
29616886Seric 			a->q_flags |= QPRIMARY;
29755012Seric 			a = recipient(a, &e->e_sendqueue, e);
29812612Seric 			if (Errors != 0)
29912612Seric 				break;
30012612Seric 
30112612Seric 			/* no errors during parsing, but might be a duplicate */
30255012Seric 			e->e_to = p;
30312612Seric 			if (!bitset(QBADADDR, a->q_flags))
30412612Seric 				message("250", "Recipient ok");
30512612Seric 			else
3064549Seric 			{
30712612Seric 				/* punt -- should keep message in ADDRESS.... */
30812612Seric 				message("550", "Addressee unknown");
3094549Seric 			}
31055012Seric 			e->e_to = NULL;
3114549Seric 			break;
3124549Seric 
3134549Seric 		  case CMDDATA:		/* data -- text of mail */
31424943Seric 			SmtpPhase = "DATA";
3154976Seric 			if (!hasmail)
3164549Seric 			{
3174976Seric 				message("503", "Need MAIL command");
3184976Seric 				break;
3194549Seric 			}
32055012Seric 			else if (e->e_nrcpts <= 0)
3214549Seric 			{
3224976Seric 				message("503", "Need RCPT (recipient)");
3234976Seric 				break;
3244549Seric 			}
3254976Seric 
3264976Seric 			/* collect the text of the message */
32724943Seric 			SmtpPhase = "collect";
32857389Seric 			setproctitle("%s %s: %s", e->e_id, CurHostName, inp);
32955012Seric 			collect(TRUE, e);
3304976Seric 			if (Errors != 0)
3314976Seric 				break;
3324976Seric 
3338238Seric 			/*
3348238Seric 			**  Arrange to send to everyone.
3358238Seric 			**	If sending to multiple people, mail back
3368238Seric 			**		errors rather than reporting directly.
3378238Seric 			**	In any case, don't mail back errors for
3388238Seric 			**		anything that has happened up to
3398238Seric 			**		now (the other end will do this).
34010197Seric 			**	Truncate our transcript -- the mail has gotten
34110197Seric 			**		to us successfully, and if we have
34210197Seric 			**		to mail this back, it will be easier
34310197Seric 			**		on the reader.
3448238Seric 			**	Then send to everyone.
3458238Seric 			**	Finally give a reply code.  If an error has
3468238Seric 			**		already been given, don't mail a
3478238Seric 			**		message back.
3489339Seric 			**	We goose error returns by clearing error bit.
3498238Seric 			*/
3508238Seric 
35124943Seric 			SmtpPhase = "delivery";
35255012Seric 			if (e->e_nrcpts != 1)
3539378Seric 			{
3549378Seric 				HoldErrs = TRUE;
35516886Seric 				ErrorMode = EM_MAIL;
3569378Seric 			}
35755012Seric 			e->e_flags &= ~EF_FATALERRS;
35855012Seric 			e->e_xfp = freopen(queuename(e, 'x'), "w", e->e_xfp);
3594976Seric 
3604976Seric 			/* send to all recipients */
36155012Seric 			sendall(e, SM_DEFAULT);
36255012Seric 			e->e_to = NULL;
3634976Seric 
36423516Seric 			/* save statistics */
36555012Seric 			markstats(e, (ADDRESS *) NULL);
36623516Seric 
3678238Seric 			/* issue success if appropriate and reset */
3688238Seric 			if (Errors == 0 || HoldErrs)
3699283Seric 				message("250", "Ok");
3708238Seric 			else
37155012Seric 				e->e_flags &= ~EF_FATALERRS;
3729339Seric 
3739339Seric 			/* if in a child, pop back to our parent */
3749339Seric 			if (InChild)
3759339Seric 				finis();
37624943Seric 
37724943Seric 			/* clean up a bit */
37858008Seric 			hasmail = FALSE;
37955012Seric 			dropenvelope(e);
38055012Seric 			CurEnv = e = newenvelope(e);
38155012Seric 			e->e_flags = BlankEnvelope.e_flags;
3824549Seric 			break;
3834549Seric 
3844549Seric 		  case CMDRSET:		/* rset -- reset state */
3854549Seric 			message("250", "Reset state");
3869339Seric 			if (InChild)
3879339Seric 				finis();
3889339Seric 			break;
3894549Seric 
3904549Seric 		  case CMDVRFY:		/* vrfy -- verify address */
391*58092Seric 		  case CMDEXPN:		/* expn -- expand address */
392*58092Seric 			vrfy = c->cmdcode == CMDVRFY;
393*58092Seric 			if (bitset(vrfy ? PRIV_NOVRFY : PRIV_NOEXPN,
394*58092Seric 						PrivacyFlags))
39558082Seric 			{
39658082Seric 				message("502", "That's none of your business");
39758082Seric 				break;
39858082Seric 			}
39958082Seric 			else if (!gothello &&
400*58092Seric 				 bitset(vrfy ? PRIV_NEEDVRFYHELO : PRIV_NEEDEXPNHELO,
401*58092Seric 						PrivacyFlags))
40258082Seric 			{
40358082Seric 				message("503", "I demand that you introduce yourself first");
40458082Seric 				break;
40558082Seric 			}
406*58092Seric 			if (runinchild(vrfy ? "SMTP-VRFY" : "SMTP-EXPN", e) > 0)
4079339Seric 				break;
40825050Seric 			setproctitle("%s: %s", CurHostName, inp);
40955173Seric #ifdef LOG
41058020Seric 			if (LogLevel > 5)
41155173Seric 				syslog(LOG_INFO, "%s: %s", CurHostName, inp);
41255173Seric #endif
4135003Seric 			vrfyqueue = NULL;
4147762Seric 			QuickAbort = TRUE;
415*58092Seric 			if (vrfy)
416*58092Seric 				e->e_flags |= EF_VRFYONLY;
41758082Seric 			(void) sendtolist(p, (ADDRESS *) NULL, &vrfyqueue, e);
4187762Seric 			if (Errors != 0)
4199339Seric 			{
4209339Seric 				if (InChild)
4219339Seric 					finis();
4227762Seric 				break;
4239339Seric 			}
4245003Seric 			while (vrfyqueue != NULL)
4255003Seric 			{
4265003Seric 				register ADDRESS *a = vrfyqueue->q_next;
4275003Seric 				char *code;
4285003Seric 
4297685Seric 				while (a != NULL && bitset(QDONTSEND|QBADADDR, a->q_flags))
4305003Seric 					a = a->q_next;
4315003Seric 
4327685Seric 				if (!bitset(QDONTSEND|QBADADDR, vrfyqueue->q_flags))
4335003Seric 				{
4345003Seric 					if (a != NULL)
4355003Seric 						code = "250-";
4365003Seric 					else
4375003Seric 						code = "250";
43858010Seric 					if (strchr(vrfyqueue->q_paddr, '<') != NULL)
43958010Seric 						message(code, "%s", vrfyqueue->q_paddr);
44058010Seric 					else if (vrfyqueue->q_fullname == NULL)
4415003Seric 						message(code, "<%s>", vrfyqueue->q_paddr);
4425003Seric 					else
4435003Seric 						message(code, "%s <%s>",
4445003Seric 						    vrfyqueue->q_fullname, vrfyqueue->q_paddr);
4455003Seric 				}
4465003Seric 				else if (a == NULL)
4475003Seric 					message("554", "Self destructive alias loop");
4485003Seric 				vrfyqueue = a;
4495003Seric 			}
4509339Seric 			if (InChild)
4519339Seric 				finis();
4524549Seric 			break;
4534549Seric 
4544549Seric 		  case CMDHELP:		/* help -- give user info */
4554577Seric 			help(p);
4564549Seric 			break;
4574549Seric 
4584549Seric 		  case CMDNOOP:		/* noop -- do nothing */
4594549Seric 			message("200", "OK");
4604549Seric 			break;
4614549Seric 
4624549Seric 		  case CMDQUIT:		/* quit -- leave mail */
46325050Seric 			message("221", "%s closing connection", MyHostName);
4649339Seric 			if (InChild)
4659339Seric 				ExitStat = EX_QUIT;
4664549Seric 			finis();
4674549Seric 
4688544Seric 		  case CMDVERB:		/* set verbose mode */
4698544Seric 			Verbose = TRUE;
47025025Seric 			SendMode = SM_DELIVER;
4718544Seric 			message("200", "Verbose mode");
4728544Seric 			break;
4738544Seric 
4749314Seric 		  case CMDONEX:		/* doing one transaction only */
4759378Seric 			OneXact = TRUE;
4769314Seric 			message("200", "Only one transaction");
4779314Seric 			break;
4789314Seric 
47936230Skarels # ifdef SMTPDEBUG
4809339Seric 		  case CMDDBGQSHOW:	/* show queues */
4816907Seric 			printf("Send Queue=");
48255012Seric 			printaddr(e->e_sendqueue, TRUE);
4835003Seric 			break;
4847275Seric 
4857275Seric 		  case CMDDBGDEBUG:	/* set debug mode */
4867676Seric 			tTsetup(tTdvect, sizeof tTdvect, "0-99.1");
4877676Seric 			tTflag(p);
4887676Seric 			message("200", "Debug set");
4897275Seric 			break;
4907275Seric 
49136230Skarels # else /* not SMTPDEBUG */
49224945Seric 
49336230Skarels 		  case CMDDBGQSHOW:	/* show queues */
49436230Skarels 		  case CMDDBGDEBUG:	/* set debug mode */
49536233Skarels # ifdef LOG
49636233Skarels 			if (RealHostName != NULL && LogLevel > 0)
49736230Skarels 				syslog(LOG_NOTICE,
49858020Seric 				    "\"%s\" command from %s (%s)",
49936230Skarels 				    c->cmdname, RealHostName,
50036230Skarels 				    inet_ntoa(RealHostAddr.sin_addr));
50136233Skarels # endif
50236230Skarels 			/* FALL THROUGH */
50336230Skarels # endif /* SMTPDEBUG */
50436230Skarels 
5054549Seric 		  case CMDERROR:	/* unknown command */
5064549Seric 			message("500", "Command unrecognized");
5074549Seric 			break;
5084549Seric 
5094549Seric 		  default:
51036230Skarels 			errno = 0;
5114549Seric 			syserr("smtp: unknown code %d", c->cmdcode);
5124549Seric 			break;
5134549Seric 		}
5144549Seric 	}
5154549Seric }
5164549Seric /*
5174549Seric **  SKIPWORD -- skip a fixed word.
5184549Seric **
5194549Seric **	Parameters:
5204549Seric **		p -- place to start looking.
5214549Seric **		w -- word to skip.
5224549Seric **
5234549Seric **	Returns:
5244549Seric **		p following w.
5254549Seric **		NULL on error.
5264549Seric **
5274549Seric **	Side Effects:
5284549Seric **		clobbers the p data area.
5294549Seric */
5304549Seric 
5314549Seric static char *
5324549Seric skipword(p, w)
5334549Seric 	register char *p;
5344549Seric 	char *w;
5354549Seric {
5364549Seric 	register char *q;
5374549Seric 
5384549Seric 	/* find beginning of word */
53958050Seric 	while (isascii(*p) && isspace(*p))
5404549Seric 		p++;
5414549Seric 	q = p;
5424549Seric 
5434549Seric 	/* find end of word */
54458050Seric 	while (*p != '\0' && *p != ':' && !(isascii(*p) && isspace(*p)))
5454549Seric 		p++;
54658050Seric 	while (isascii(*p) && isspace(*p))
5474549Seric 		*p++ = '\0';
5484549Seric 	if (*p != ':')
5494549Seric 	{
5504549Seric 	  syntax:
5514549Seric 		message("501", "Syntax error");
5524549Seric 		Errors++;
5534549Seric 		return (NULL);
5544549Seric 	}
5554549Seric 	*p++ = '\0';
55658050Seric 	while (isascii(*p) && isspace(*p))
5574549Seric 		p++;
5584549Seric 
5594549Seric 	/* see if the input word matches desired word */
56033725Sbostic 	if (strcasecmp(q, w))
5614549Seric 		goto syntax;
5624549Seric 
5634549Seric 	return (p);
5644549Seric }
5654577Seric /*
5664577Seric **  HELP -- implement the HELP command.
5674577Seric **
5684577Seric **	Parameters:
5694577Seric **		topic -- the topic we want help for.
5704577Seric **
5714577Seric **	Returns:
5724577Seric **		none.
5734577Seric **
5744577Seric **	Side Effects:
5754577Seric **		outputs the help file to message output.
5764577Seric */
5774577Seric 
5784577Seric help(topic)
5794577Seric 	char *topic;
5804577Seric {
5814577Seric 	register FILE *hf;
5824577Seric 	int len;
5834577Seric 	char buf[MAXLINE];
5844577Seric 	bool noinfo;
5854577Seric 
5868269Seric 	if (HelpFile == NULL || (hf = fopen(HelpFile, "r")) == NULL)
5874577Seric 	{
5884577Seric 		/* no help */
58911931Seric 		errno = 0;
5904577Seric 		message("502", "HELP not implemented");
5914577Seric 		return;
5924577Seric 	}
5934577Seric 
59449669Seric 	if (topic == NULL || *topic == '\0')
59549669Seric 		topic = "smtp";
59649669Seric 	else
59749669Seric 		makelower(topic);
59849669Seric 
5994577Seric 	len = strlen(topic);
6004577Seric 	noinfo = TRUE;
6014577Seric 
6024577Seric 	while (fgets(buf, sizeof buf, hf) != NULL)
6034577Seric 	{
6044577Seric 		if (strncmp(buf, topic, len) == 0)
6054577Seric 		{
6064577Seric 			register char *p;
6074577Seric 
60856795Seric 			p = strchr(buf, '\t');
6094577Seric 			if (p == NULL)
6104577Seric 				p = buf;
6114577Seric 			else
6124577Seric 				p++;
6134577Seric 			fixcrlf(p, TRUE);
6144577Seric 			message("214-", p);
6154577Seric 			noinfo = FALSE;
6164577Seric 		}
6174577Seric 	}
6184577Seric 
6194577Seric 	if (noinfo)
6204577Seric 		message("504", "HELP topic unknown");
6214577Seric 	else
6224577Seric 		message("214", "End of HELP info");
6234628Seric 	(void) fclose(hf);
6244577Seric }
6258544Seric /*
6269339Seric **  RUNINCHILD -- return twice -- once in the child, then in the parent again
6279339Seric **
6289339Seric **	Parameters:
6299339Seric **		label -- a string used in error messages
6309339Seric **
6319339Seric **	Returns:
6329339Seric **		zero in the child
6339339Seric **		one in the parent
6349339Seric **
6359339Seric **	Side Effects:
6369339Seric **		none.
6379339Seric */
6388544Seric 
63955012Seric runinchild(label, e)
6409339Seric 	char *label;
64155012Seric 	register ENVELOPE *e;
6429339Seric {
6439339Seric 	int childpid;
6449339Seric 
64516158Seric 	if (!OneXact)
6469339Seric 	{
64716158Seric 		childpid = dofork();
64816158Seric 		if (childpid < 0)
64916158Seric 		{
65016158Seric 			syserr("%s: cannot fork", label);
65116158Seric 			return (1);
65216158Seric 		}
65316158Seric 		if (childpid > 0)
65416158Seric 		{
65516158Seric 			auto int st;
6569339Seric 
65716158Seric 			/* parent -- wait for child to complete */
65816158Seric 			st = waitfor(childpid);
65916158Seric 			if (st == -1)
66016158Seric 				syserr("%s: lost child", label);
6619339Seric 
66216158Seric 			/* if we exited on a QUIT command, complete the process */
66316158Seric 			if (st == (EX_QUIT << 8))
66416158Seric 				finis();
6659339Seric 
66616158Seric 			return (1);
66716158Seric 		}
66816158Seric 		else
66916158Seric 		{
67016158Seric 			/* child */
67116158Seric 			InChild = TRUE;
67225050Seric 			QuickAbort = FALSE;
67355012Seric 			clearenvelope(e, FALSE);
67416158Seric 		}
6759339Seric 	}
67615256Seric 
67716158Seric 	/* open alias database */
67855012Seric 	initaliases(AliasFile, FALSE, e);
67916158Seric 
68016158Seric 	return (0);
6819339Seric }
6829339Seric 
68356795Seric # endif /* SMTP */
684