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*57232Seric static char sccsid[] = "@(#)srvrsmtp.c	5.40 (Berkeley) 12/20/92 (with SMTP)";
1433731Sbostic #else
15*57232Seric static char sccsid[] = "@(#)srvrsmtp.c	5.40 (Berkeley) 12/20/92 (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 */
519339Seric # define CMDHELP	6	/* help -- give usage info */
529339Seric # define CMDNOOP	7	/* noop -- do nothing */
539339Seric # define CMDQUIT	8	/* quit -- close connection and die */
549339Seric # define CMDHELO	9	/* helo -- be polite */
5536230Skarels # define CMDONEX	10	/* onex -- sending one transaction only */
5636230Skarels # define CMDVERB	11	/* verb -- go into verbose mode */
5736230Skarels /* debugging-only commands, only enabled if SMTPDEBUG is defined */
5836230Skarels # define CMDDBGQSHOW	12	/* showq -- show send queue */
5936230Skarels # define CMDDBGDEBUG	13	/* debug -- set debug mode */
604549Seric 
614549Seric static struct cmd	CmdTab[] =
624549Seric {
634549Seric 	"mail",		CMDMAIL,
644976Seric 	"rcpt",		CMDRCPT,
654549Seric 	"data",		CMDDATA,
664549Seric 	"rset",		CMDRSET,
674549Seric 	"vrfy",		CMDVRFY,
687762Seric 	"expn",		CMDVRFY,
694549Seric 	"help",		CMDHELP,
704549Seric 	"noop",		CMDNOOP,
714549Seric 	"quit",		CMDQUIT,
724976Seric 	"helo",		CMDHELO,
738544Seric 	"verb",		CMDVERB,
749314Seric 	"onex",		CMDONEX,
7536230Skarels 	/*
7636230Skarels 	 * remaining commands are here only
7736230Skarels 	 * to trap and log attempts to use them
7836230Skarels 	 */
799339Seric 	"showq",	CMDDBGQSHOW,
808544Seric 	"debug",	CMDDBGDEBUG,
814549Seric 	NULL,		CMDERROR,
824549Seric };
834549Seric 
849339Seric bool	InChild = FALSE;		/* true if running in a subprocess */
859378Seric bool	OneXact = FALSE;		/* one xaction only this run */
8611146Seric 
879339Seric #define EX_QUIT		22		/* special code for QUIT command */
888544Seric 
8955012Seric smtp(e)
9055012Seric 	register ENVELOPE *e;
914549Seric {
924549Seric 	register char *p;
938544Seric 	register struct cmd *c;
944549Seric 	char *cmd;
9546928Sbostic 	static char *skipword();
964549Seric 	bool hasmail;			/* mail command received */
975003Seric 	auto ADDRESS *vrfyqueue;
9812612Seric 	ADDRESS *a;
9930448Seric 	char *sendinghost;
1008544Seric 	char inp[MAXLINE];
101*57232Seric 	char cmdbuf[MAXLINE];
1027124Seric 	extern char Version[];
10311151Seric 	extern char *macvalue();
10412612Seric 	extern ADDRESS *recipient();
10524943Seric 	extern ENVELOPE BlankEnvelope;
10624943Seric 	extern ENVELOPE *newenvelope();
1074549Seric 
1085003Seric 	hasmail = FALSE;
1097363Seric 	if (OutChannel != stdout)
1107363Seric 	{
1117363Seric 		/* arrange for debugging output to go to remote host */
1127363Seric 		(void) close(1);
1137363Seric 		(void) dup(fileno(OutChannel));
1147363Seric 	}
11555012Seric 	settime(e);
11624971Seric 	if (RealHostName != NULL)
11725050Seric 	{
11825050Seric 		CurHostName = RealHostName;
11925050Seric 		setproctitle("srvrsmtp %s", CurHostName);
12025050Seric 	}
12125050Seric 	else
12225050Seric 	{
12325050Seric 		/* this must be us!! */
12425050Seric 		CurHostName = MyHostName;
12525050Seric 	}
12655012Seric 	expand("\001e", inp, &inp[sizeof inp], e);
12755360Seric 	message("220", "%s", inp);
12824943Seric 	SmtpPhase = "startup";
12930448Seric 	sendinghost = NULL;
1304549Seric 	for (;;)
1314549Seric 	{
13212612Seric 		/* arrange for backout */
13312612Seric 		if (setjmp(TopFrame) > 0 && InChild)
13412612Seric 			finis();
13512612Seric 		QuickAbort = FALSE;
13612612Seric 		HoldErrs = FALSE;
13751951Seric 		LogUsrErrs = FALSE;
13812612Seric 
1397356Seric 		/* setup for the read */
14055012Seric 		e->e_to = NULL;
1414577Seric 		Errors = 0;
1427275Seric 		(void) fflush(stdout);
1437356Seric 
1447356Seric 		/* read the input line */
1457685Seric 		p = sfgets(inp, sizeof inp, InChannel);
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
15455464Seric 			if (LogLevel >= 4)
15555464Seric 				syslog(LOG_NOTICE, "lost input channel from %s",
15655464Seric 					CurHostName);
15755464Seric #endif
1584549Seric 			finis();
1594549Seric 		}
1604549Seric 
1614549Seric 		/* clean up end of line */
1624558Seric 		fixcrlf(inp, TRUE);
1634549Seric 
1644713Seric 		/* echo command to transcript */
16555012Seric 		if (e->e_xfp != NULL)
16655012Seric 			fprintf(e->e_xfp, "<<< %s\n", inp);
1674713Seric 
1684549Seric 		/* break off command */
1694549Seric 		for (p = inp; isspace(*p); p++)
1704549Seric 			continue;
171*57232Seric 		cmd = cmdbuf;
172*57232Seric 		while (*p != '\0' && !isspace(*p) && cmd < &cmdbuf[sizeof cmdbuf - 2])
17324981Seric 			*cmd++ = *p++;
17424981Seric 		*cmd = '\0';
1754549Seric 
17625691Seric 		/* throw away leading whitespace */
17725691Seric 		while (isspace(*p))
17825691Seric 			p++;
17925691Seric 
1804549Seric 		/* decode command */
1814549Seric 		for (c = CmdTab; c->cmdname != NULL; c++)
1824549Seric 		{
18333725Sbostic 			if (!strcasecmp(c->cmdname, cmdbuf))
1844549Seric 				break;
1854549Seric 		}
1864549Seric 
18751954Seric 		/* reset errors */
18851954Seric 		errno = 0;
18951954Seric 
1904549Seric 		/* process command */
1914549Seric 		switch (c->cmdcode)
1924549Seric 		{
1934976Seric 		  case CMDHELO:		/* hello -- introduce yourself */
19424943Seric 			SmtpPhase = "HELO";
19525050Seric 			setproctitle("%s: %s", CurHostName, inp);
19633725Sbostic 			if (!strcasecmp(p, MyHostName))
19714877Seric 			{
19836230Skarels 				/*
19936230Skarels 				 * didn't know about alias,
20036230Skarels 				 * or connected to an echo server
20136230Skarels 				 */
20247570Seric 				message("553", "%s config error: mail loops back to myself",
20347570Seric 					MyHostName);
20414877Seric 				break;
20514877Seric 			}
20633725Sbostic 			if (RealHostName != NULL && strcasecmp(p, RealHostName))
20711146Seric 			{
20824981Seric 				char hostbuf[MAXNAME];
20911146Seric 
21024981Seric 				(void) sprintf(hostbuf, "%s (%s)", p, RealHostName);
21130448Seric 				sendinghost = newstr(hostbuf);
21211146Seric 			}
21311146Seric 			else
21430448Seric 				sendinghost = newstr(p);
2154997Seric 			message("250", "%s Hello %s, pleased to meet you",
21636230Skarels 				MyHostName, sendinghost);
2174976Seric 			break;
2184976Seric 
2194549Seric 		  case CMDMAIL:		/* mail -- designate sender */
22024943Seric 			SmtpPhase = "MAIL";
22124943Seric 
22211151Seric 			/* force a sending host even if no HELO given */
22355012Seric 			if (RealHostName != NULL && macvalue('s', e) == NULL)
22430448Seric 				sendinghost = RealHostName;
22511151Seric 
2269314Seric 			/* check for validity of this command */
2274558Seric 			if (hasmail)
2284558Seric 			{
2294558Seric 				message("503", "Sender already specified");
2304558Seric 				break;
2314558Seric 			}
2329339Seric 			if (InChild)
2339339Seric 			{
23436230Skarels 				errno = 0;
2359339Seric 				syserr("Nested MAIL command");
2369339Seric 				exit(0);
2379339Seric 			}
2389339Seric 
2399339Seric 			/* fork a subprocess to process this command */
24055012Seric 			if (runinchild("SMTP-MAIL", e) > 0)
2419339Seric 				break;
24255012Seric 			define('s', sendinghost, e);
24355012Seric 			define('r', "SMTP", e);
24455012Seric 			initsys(e);
24555012Seric 			setproctitle("%s %s: %s", e->e_id,
24625050Seric 				CurHostName, inp);
2479339Seric 
2489339Seric 			/* child -- go do the processing */
2494549Seric 			p = skipword(p, "from");
2504549Seric 			if (p == NULL)
2514549Seric 				break;
25255012Seric 			setsender(p, e);
2534577Seric 			if (Errors == 0)
2544549Seric 			{
2554549Seric 				message("250", "Sender ok");
2564549Seric 				hasmail = TRUE;
2574549Seric 			}
2589339Seric 			else if (InChild)
2599339Seric 				finis();
2604549Seric 			break;
2614549Seric 
2624976Seric 		  case CMDRCPT:		/* rcpt -- designate recipient */
26324943Seric 			SmtpPhase = "RCPT";
26455012Seric 			setproctitle("%s %s: %s", e->e_id,
26525050Seric 				CurHostName, inp);
26612612Seric 			if (setjmp(TopFrame) > 0)
26714785Seric 			{
26855012Seric 				e->e_flags &= ~EF_FATALERRS;
26912612Seric 				break;
27014785Seric 			}
27112612Seric 			QuickAbort = TRUE;
27251951Seric 			LogUsrErrs = TRUE;
2734549Seric 			p = skipword(p, "to");
2744549Seric 			if (p == NULL)
2754549Seric 				break;
27655012Seric 			a = parseaddr(p, (ADDRESS *) NULL, 1, '\0', e);
27712612Seric 			if (a == NULL)
27812612Seric 				break;
27916886Seric 			a->q_flags |= QPRIMARY;
28055012Seric 			a = recipient(a, &e->e_sendqueue, e);
28112612Seric 			if (Errors != 0)
28212612Seric 				break;
28312612Seric 
28412612Seric 			/* no errors during parsing, but might be a duplicate */
28555012Seric 			e->e_to = p;
28612612Seric 			if (!bitset(QBADADDR, a->q_flags))
28712612Seric 				message("250", "Recipient ok");
28812612Seric 			else
2894549Seric 			{
29012612Seric 				/* punt -- should keep message in ADDRESS.... */
29112612Seric 				message("550", "Addressee unknown");
2924549Seric 			}
29355012Seric 			e->e_to = NULL;
2944549Seric 			break;
2954549Seric 
2964549Seric 		  case CMDDATA:		/* data -- text of mail */
29724943Seric 			SmtpPhase = "DATA";
2984976Seric 			if (!hasmail)
2994549Seric 			{
3004976Seric 				message("503", "Need MAIL command");
3014976Seric 				break;
3024549Seric 			}
30355012Seric 			else if (e->e_nrcpts <= 0)
3044549Seric 			{
3054976Seric 				message("503", "Need RCPT (recipient)");
3064976Seric 				break;
3074549Seric 			}
3084976Seric 
3094976Seric 			/* collect the text of the message */
31024943Seric 			SmtpPhase = "collect";
31155012Seric 			setproctitle("%s %s: %s", e->e_id,
31225050Seric 				CurHostName, inp);
31355012Seric 			collect(TRUE, e);
3144976Seric 			if (Errors != 0)
3154976Seric 				break;
3164976Seric 
3178238Seric 			/*
3188238Seric 			**  Arrange to send to everyone.
3198238Seric 			**	If sending to multiple people, mail back
3208238Seric 			**		errors rather than reporting directly.
3218238Seric 			**	In any case, don't mail back errors for
3228238Seric 			**		anything that has happened up to
3238238Seric 			**		now (the other end will do this).
32410197Seric 			**	Truncate our transcript -- the mail has gotten
32510197Seric 			**		to us successfully, and if we have
32610197Seric 			**		to mail this back, it will be easier
32710197Seric 			**		on the reader.
3288238Seric 			**	Then send to everyone.
3298238Seric 			**	Finally give a reply code.  If an error has
3308238Seric 			**		already been given, don't mail a
3318238Seric 			**		message back.
3329339Seric 			**	We goose error returns by clearing error bit.
3338238Seric 			*/
3348238Seric 
33524943Seric 			SmtpPhase = "delivery";
33655012Seric 			if (e->e_nrcpts != 1)
3379378Seric 			{
3389378Seric 				HoldErrs = TRUE;
33916886Seric 				ErrorMode = EM_MAIL;
3409378Seric 			}
34155012Seric 			e->e_flags &= ~EF_FATALERRS;
34255012Seric 			e->e_xfp = freopen(queuename(e, 'x'), "w", e->e_xfp);
3434976Seric 
3444976Seric 			/* send to all recipients */
34555012Seric 			sendall(e, SM_DEFAULT);
34655012Seric 			e->e_to = NULL;
3474976Seric 
34823516Seric 			/* save statistics */
34955012Seric 			markstats(e, (ADDRESS *) NULL);
35023516Seric 
3518238Seric 			/* issue success if appropriate and reset */
3528238Seric 			if (Errors == 0 || HoldErrs)
3539283Seric 				message("250", "Ok");
3548238Seric 			else
35555012Seric 				e->e_flags &= ~EF_FATALERRS;
3569339Seric 
3579339Seric 			/* if in a child, pop back to our parent */
3589339Seric 			if (InChild)
3599339Seric 				finis();
36024943Seric 
36124943Seric 			/* clean up a bit */
36224943Seric 			hasmail = 0;
36355012Seric 			dropenvelope(e);
36455012Seric 			CurEnv = e = newenvelope(e);
36555012Seric 			e->e_flags = BlankEnvelope.e_flags;
3664549Seric 			break;
3674549Seric 
3684549Seric 		  case CMDRSET:		/* rset -- reset state */
3694549Seric 			message("250", "Reset state");
3709339Seric 			if (InChild)
3719339Seric 				finis();
3729339Seric 			break;
3734549Seric 
3744549Seric 		  case CMDVRFY:		/* vrfy -- verify address */
37555012Seric 			if (runinchild("SMTP-VRFY", e) > 0)
3769339Seric 				break;
37725050Seric 			setproctitle("%s: %s", CurHostName, inp);
37855173Seric #ifdef LOG
37955173Seric 			if (LogLevel >= 9)
38055173Seric 				syslog(LOG_INFO, "%s: %s", CurHostName, inp);
38155173Seric #endif
3825003Seric 			vrfyqueue = NULL;
3837762Seric 			QuickAbort = TRUE;
38455012Seric 			sendtolist(p, (ADDRESS *) NULL, &vrfyqueue, e);
3857762Seric 			if (Errors != 0)
3869339Seric 			{
3879339Seric 				if (InChild)
3889339Seric 					finis();
3897762Seric 				break;
3909339Seric 			}
3915003Seric 			while (vrfyqueue != NULL)
3925003Seric 			{
3935003Seric 				register ADDRESS *a = vrfyqueue->q_next;
3945003Seric 				char *code;
3955003Seric 
3967685Seric 				while (a != NULL && bitset(QDONTSEND|QBADADDR, a->q_flags))
3975003Seric 					a = a->q_next;
3985003Seric 
3997685Seric 				if (!bitset(QDONTSEND|QBADADDR, vrfyqueue->q_flags))
4005003Seric 				{
4015003Seric 					if (a != NULL)
4025003Seric 						code = "250-";
4035003Seric 					else
4045003Seric 						code = "250";
4055003Seric 					if (vrfyqueue->q_fullname == NULL)
4065003Seric 						message(code, "<%s>", vrfyqueue->q_paddr);
4075003Seric 					else
4085003Seric 						message(code, "%s <%s>",
4095003Seric 						    vrfyqueue->q_fullname, vrfyqueue->q_paddr);
4105003Seric 				}
4115003Seric 				else if (a == NULL)
4125003Seric 					message("554", "Self destructive alias loop");
4135003Seric 				vrfyqueue = a;
4145003Seric 			}
4159339Seric 			if (InChild)
4169339Seric 				finis();
4174549Seric 			break;
4184549Seric 
4194549Seric 		  case CMDHELP:		/* help -- give user info */
4204577Seric 			help(p);
4214549Seric 			break;
4224549Seric 
4234549Seric 		  case CMDNOOP:		/* noop -- do nothing */
4244549Seric 			message("200", "OK");
4254549Seric 			break;
4264549Seric 
4274549Seric 		  case CMDQUIT:		/* quit -- leave mail */
42825050Seric 			message("221", "%s closing connection", MyHostName);
4299339Seric 			if (InChild)
4309339Seric 				ExitStat = EX_QUIT;
4314549Seric 			finis();
4324549Seric 
4338544Seric 		  case CMDVERB:		/* set verbose mode */
4348544Seric 			Verbose = TRUE;
43525025Seric 			SendMode = SM_DELIVER;
4368544Seric 			message("200", "Verbose mode");
4378544Seric 			break;
4388544Seric 
4399314Seric 		  case CMDONEX:		/* doing one transaction only */
4409378Seric 			OneXact = TRUE;
4419314Seric 			message("200", "Only one transaction");
4429314Seric 			break;
4439314Seric 
44436230Skarels # ifdef SMTPDEBUG
4459339Seric 		  case CMDDBGQSHOW:	/* show queues */
4466907Seric 			printf("Send Queue=");
44755012Seric 			printaddr(e->e_sendqueue, TRUE);
4485003Seric 			break;
4497275Seric 
4507275Seric 		  case CMDDBGDEBUG:	/* set debug mode */
4517676Seric 			tTsetup(tTdvect, sizeof tTdvect, "0-99.1");
4527676Seric 			tTflag(p);
4537676Seric 			message("200", "Debug set");
4547275Seric 			break;
4557275Seric 
45636230Skarels # else /* not SMTPDEBUG */
45724945Seric 
45836230Skarels 		  case CMDDBGQSHOW:	/* show queues */
45936230Skarels 		  case CMDDBGDEBUG:	/* set debug mode */
46036233Skarels # ifdef LOG
46136233Skarels 			if (RealHostName != NULL && LogLevel > 0)
46236230Skarels 				syslog(LOG_NOTICE,
46336230Skarels 				    "\"%s\" command from %s (%s)\n",
46436230Skarels 				    c->cmdname, RealHostName,
46536230Skarels 				    inet_ntoa(RealHostAddr.sin_addr));
46636233Skarels # endif
46736230Skarels 			/* FALL THROUGH */
46836230Skarels # endif /* SMTPDEBUG */
46936230Skarels 
4704549Seric 		  case CMDERROR:	/* unknown command */
4714549Seric 			message("500", "Command unrecognized");
4724549Seric 			break;
4734549Seric 
4744549Seric 		  default:
47536230Skarels 			errno = 0;
4764549Seric 			syserr("smtp: unknown code %d", c->cmdcode);
4774549Seric 			break;
4784549Seric 		}
4794549Seric 	}
4804549Seric }
4814549Seric /*
4824549Seric **  SKIPWORD -- skip a fixed word.
4834549Seric **
4844549Seric **	Parameters:
4854549Seric **		p -- place to start looking.
4864549Seric **		w -- word to skip.
4874549Seric **
4884549Seric **	Returns:
4894549Seric **		p following w.
4904549Seric **		NULL on error.
4914549Seric **
4924549Seric **	Side Effects:
4934549Seric **		clobbers the p data area.
4944549Seric */
4954549Seric 
4964549Seric static char *
4974549Seric skipword(p, w)
4984549Seric 	register char *p;
4994549Seric 	char *w;
5004549Seric {
5014549Seric 	register char *q;
5024549Seric 
5034549Seric 	/* find beginning of word */
5044549Seric 	while (isspace(*p))
5054549Seric 		p++;
5064549Seric 	q = p;
5074549Seric 
5084549Seric 	/* find end of word */
5094549Seric 	while (*p != '\0' && *p != ':' && !isspace(*p))
5104549Seric 		p++;
5114549Seric 	while (isspace(*p))
5124549Seric 		*p++ = '\0';
5134549Seric 	if (*p != ':')
5144549Seric 	{
5154549Seric 	  syntax:
5164549Seric 		message("501", "Syntax error");
5174549Seric 		Errors++;
5184549Seric 		return (NULL);
5194549Seric 	}
5204549Seric 	*p++ = '\0';
5214549Seric 	while (isspace(*p))
5224549Seric 		p++;
5234549Seric 
5244549Seric 	/* see if the input word matches desired word */
52533725Sbostic 	if (strcasecmp(q, w))
5264549Seric 		goto syntax;
5274549Seric 
5284549Seric 	return (p);
5294549Seric }
5304577Seric /*
5314577Seric **  HELP -- implement the HELP command.
5324577Seric **
5334577Seric **	Parameters:
5344577Seric **		topic -- the topic we want help for.
5354577Seric **
5364577Seric **	Returns:
5374577Seric **		none.
5384577Seric **
5394577Seric **	Side Effects:
5404577Seric **		outputs the help file to message output.
5414577Seric */
5424577Seric 
5434577Seric help(topic)
5444577Seric 	char *topic;
5454577Seric {
5464577Seric 	register FILE *hf;
5474577Seric 	int len;
5484577Seric 	char buf[MAXLINE];
5494577Seric 	bool noinfo;
5504577Seric 
5518269Seric 	if (HelpFile == NULL || (hf = fopen(HelpFile, "r")) == NULL)
5524577Seric 	{
5534577Seric 		/* no help */
55411931Seric 		errno = 0;
5554577Seric 		message("502", "HELP not implemented");
5564577Seric 		return;
5574577Seric 	}
5584577Seric 
55949669Seric 	if (topic == NULL || *topic == '\0')
56049669Seric 		topic = "smtp";
56149669Seric 	else
56249669Seric 		makelower(topic);
56349669Seric 
5644577Seric 	len = strlen(topic);
5654577Seric 	noinfo = TRUE;
5664577Seric 
5674577Seric 	while (fgets(buf, sizeof buf, hf) != NULL)
5684577Seric 	{
5694577Seric 		if (strncmp(buf, topic, len) == 0)
5704577Seric 		{
5714577Seric 			register char *p;
5724577Seric 
57356795Seric 			p = strchr(buf, '\t');
5744577Seric 			if (p == NULL)
5754577Seric 				p = buf;
5764577Seric 			else
5774577Seric 				p++;
5784577Seric 			fixcrlf(p, TRUE);
5794577Seric 			message("214-", p);
5804577Seric 			noinfo = FALSE;
5814577Seric 		}
5824577Seric 	}
5834577Seric 
5844577Seric 	if (noinfo)
5854577Seric 		message("504", "HELP topic unknown");
5864577Seric 	else
5874577Seric 		message("214", "End of HELP info");
5884628Seric 	(void) fclose(hf);
5894577Seric }
5908544Seric /*
5919339Seric **  RUNINCHILD -- return twice -- once in the child, then in the parent again
5929339Seric **
5939339Seric **	Parameters:
5949339Seric **		label -- a string used in error messages
5959339Seric **
5969339Seric **	Returns:
5979339Seric **		zero in the child
5989339Seric **		one in the parent
5999339Seric **
6009339Seric **	Side Effects:
6019339Seric **		none.
6029339Seric */
6038544Seric 
60455012Seric runinchild(label, e)
6059339Seric 	char *label;
60655012Seric 	register ENVELOPE *e;
6079339Seric {
6089339Seric 	int childpid;
6099339Seric 
61016158Seric 	if (!OneXact)
6119339Seric 	{
61216158Seric 		childpid = dofork();
61316158Seric 		if (childpid < 0)
61416158Seric 		{
61516158Seric 			syserr("%s: cannot fork", label);
61616158Seric 			return (1);
61716158Seric 		}
61816158Seric 		if (childpid > 0)
61916158Seric 		{
62016158Seric 			auto int st;
6219339Seric 
62216158Seric 			/* parent -- wait for child to complete */
62316158Seric 			st = waitfor(childpid);
62416158Seric 			if (st == -1)
62516158Seric 				syserr("%s: lost child", label);
6269339Seric 
62716158Seric 			/* if we exited on a QUIT command, complete the process */
62816158Seric 			if (st == (EX_QUIT << 8))
62916158Seric 				finis();
6309339Seric 
63116158Seric 			return (1);
63216158Seric 		}
63316158Seric 		else
63416158Seric 		{
63516158Seric 			/* child */
63616158Seric 			InChild = TRUE;
63725050Seric 			QuickAbort = FALSE;
63855012Seric 			clearenvelope(e, FALSE);
63916158Seric 		}
6409339Seric 	}
64115256Seric 
64216158Seric 	/* open alias database */
64355012Seric 	initaliases(AliasFile, FALSE, e);
64416158Seric 
64516158Seric 	return (0);
6469339Seric }
6479339Seric 
64856795Seric # endif /* SMTP */
649