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*58010Seric static char sccsid[] = "@(#)srvrsmtp.c	6.6 (Berkeley) 02/15/93 (with SMTP)";
1433731Sbostic #else
15*58010Seric static char sccsid[] = "@(#)srvrsmtp.c	6.6 (Berkeley) 02/15/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 */
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];
10157232Seric 	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);
11657642Seric 	if (RealHostName == NULL)
11757642Seric 		RealHostName = MyHostName;
11857642Seric 	CurHostName = RealHostName;
11957642Seric 	setproctitle("srvrsmtp %s", CurHostName);
12055012Seric 	expand("\001e", inp, &inp[sizeof inp], e);
12155360Seric 	message("220", "%s", inp);
12224943Seric 	SmtpPhase = "startup";
12330448Seric 	sendinghost = NULL;
1244549Seric 	for (;;)
1254549Seric 	{
12612612Seric 		/* arrange for backout */
12712612Seric 		if (setjmp(TopFrame) > 0 && InChild)
12812612Seric 			finis();
12912612Seric 		QuickAbort = FALSE;
13012612Seric 		HoldErrs = FALSE;
13151951Seric 		LogUsrErrs = FALSE;
13212612Seric 
1337356Seric 		/* setup for the read */
13455012Seric 		e->e_to = NULL;
1354577Seric 		Errors = 0;
1367275Seric 		(void) fflush(stdout);
1377356Seric 
1387356Seric 		/* read the input line */
13957389Seric 		p = sfgets(inp, sizeof inp, InChannel, ReadTimeout);
1407356Seric 
1417685Seric 		/* handle errors */
1427356Seric 		if (p == NULL)
1437356Seric 		{
1444549Seric 			/* end of file, just die */
14536230Skarels 			message("421", "%s Lost input channel from %s",
14625050Seric 				MyHostName, CurHostName);
14755464Seric #ifdef LOG
14855464Seric 			if (LogLevel >= 4)
14955464Seric 				syslog(LOG_NOTICE, "lost input channel from %s",
15055464Seric 					CurHostName);
15155464Seric #endif
1524549Seric 			finis();
1534549Seric 		}
1544549Seric 
1554549Seric 		/* clean up end of line */
1564558Seric 		fixcrlf(inp, TRUE);
1574549Seric 
1584713Seric 		/* echo command to transcript */
15955012Seric 		if (e->e_xfp != NULL)
16055012Seric 			fprintf(e->e_xfp, "<<< %s\n", inp);
1614713Seric 
1624549Seric 		/* break off command */
1634549Seric 		for (p = inp; isspace(*p); p++)
1644549Seric 			continue;
16557232Seric 		cmd = cmdbuf;
16657232Seric 		while (*p != '\0' && !isspace(*p) && cmd < &cmdbuf[sizeof cmdbuf - 2])
16724981Seric 			*cmd++ = *p++;
16824981Seric 		*cmd = '\0';
1694549Seric 
17025691Seric 		/* throw away leading whitespace */
17125691Seric 		while (isspace(*p))
17225691Seric 			p++;
17325691Seric 
1744549Seric 		/* decode command */
1754549Seric 		for (c = CmdTab; c->cmdname != NULL; c++)
1764549Seric 		{
17733725Sbostic 			if (!strcasecmp(c->cmdname, cmdbuf))
1784549Seric 				break;
1794549Seric 		}
1804549Seric 
18151954Seric 		/* reset errors */
18251954Seric 		errno = 0;
18351954Seric 
1844549Seric 		/* process command */
1854549Seric 		switch (c->cmdcode)
1864549Seric 		{
1874976Seric 		  case CMDHELO:		/* hello -- introduce yourself */
18824943Seric 			SmtpPhase = "HELO";
18925050Seric 			setproctitle("%s: %s", CurHostName, inp);
19033725Sbostic 			if (!strcasecmp(p, MyHostName))
19114877Seric 			{
19236230Skarels 				/*
19336230Skarels 				 * didn't know about alias,
19436230Skarels 				 * or connected to an echo server
19536230Skarels 				 */
19647570Seric 				message("553", "%s config error: mail loops back to myself",
19747570Seric 					MyHostName);
19814877Seric 				break;
19914877Seric 			}
20033725Sbostic 			if (RealHostName != NULL && strcasecmp(p, RealHostName))
20111146Seric 			{
20224981Seric 				char hostbuf[MAXNAME];
20311146Seric 
20424981Seric 				(void) sprintf(hostbuf, "%s (%s)", p, RealHostName);
20530448Seric 				sendinghost = newstr(hostbuf);
20611146Seric 			}
20711146Seric 			else
20830448Seric 				sendinghost = newstr(p);
2094997Seric 			message("250", "%s Hello %s, pleased to meet you",
21036230Skarels 				MyHostName, sendinghost);
2114976Seric 			break;
2124976Seric 
2134549Seric 		  case CMDMAIL:		/* mail -- designate sender */
21424943Seric 			SmtpPhase = "MAIL";
21524943Seric 
21611151Seric 			/* force a sending host even if no HELO given */
21755012Seric 			if (RealHostName != NULL && macvalue('s', e) == NULL)
21830448Seric 				sendinghost = RealHostName;
21911151Seric 
2209314Seric 			/* check for validity of this command */
2214558Seric 			if (hasmail)
2224558Seric 			{
2234558Seric 				message("503", "Sender already specified");
2244558Seric 				break;
2254558Seric 			}
2269339Seric 			if (InChild)
2279339Seric 			{
22836230Skarels 				errno = 0;
22958008Seric 				syserr("Nested MAIL command: MAIL %s", p);
2309339Seric 				exit(0);
2319339Seric 			}
2329339Seric 
2339339Seric 			/* fork a subprocess to process this command */
23455012Seric 			if (runinchild("SMTP-MAIL", e) > 0)
2359339Seric 				break;
23655012Seric 			define('s', sendinghost, e);
23755012Seric 			define('r', "SMTP", e);
23855012Seric 			initsys(e);
23957389Seric 			setproctitle("%s %s: %s", e->e_id, CurHostName, inp);
2409339Seric 
2419339Seric 			/* child -- go do the processing */
2424549Seric 			p = skipword(p, "from");
2434549Seric 			if (p == NULL)
2444549Seric 				break;
24557977Seric 			if (setjmp(TopFrame) > 0)
24657977Seric 				break;
24757977Seric 			QuickAbort = TRUE;
24855012Seric 			setsender(p, e);
2494577Seric 			if (Errors == 0)
2504549Seric 			{
2514549Seric 				message("250", "Sender ok");
2524549Seric 				hasmail = TRUE;
2534549Seric 			}
2549339Seric 			else if (InChild)
2559339Seric 				finis();
2564549Seric 			break;
2574549Seric 
2584976Seric 		  case CMDRCPT:		/* rcpt -- designate recipient */
25924943Seric 			SmtpPhase = "RCPT";
26057389Seric 			setproctitle("%s %s: %s", e->e_id, CurHostName, inp);
26112612Seric 			if (setjmp(TopFrame) > 0)
26214785Seric 			{
26355012Seric 				e->e_flags &= ~EF_FATALERRS;
26412612Seric 				break;
26514785Seric 			}
26612612Seric 			QuickAbort = TRUE;
26751951Seric 			LogUsrErrs = TRUE;
2684549Seric 			p = skipword(p, "to");
2694549Seric 			if (p == NULL)
2704549Seric 				break;
27155012Seric 			a = parseaddr(p, (ADDRESS *) NULL, 1, '\0', e);
27212612Seric 			if (a == NULL)
27312612Seric 				break;
27416886Seric 			a->q_flags |= QPRIMARY;
27555012Seric 			a = recipient(a, &e->e_sendqueue, e);
27612612Seric 			if (Errors != 0)
27712612Seric 				break;
27812612Seric 
27912612Seric 			/* no errors during parsing, but might be a duplicate */
28055012Seric 			e->e_to = p;
28112612Seric 			if (!bitset(QBADADDR, a->q_flags))
28212612Seric 				message("250", "Recipient ok");
28312612Seric 			else
2844549Seric 			{
28512612Seric 				/* punt -- should keep message in ADDRESS.... */
28612612Seric 				message("550", "Addressee unknown");
2874549Seric 			}
28855012Seric 			e->e_to = NULL;
2894549Seric 			break;
2904549Seric 
2914549Seric 		  case CMDDATA:		/* data -- text of mail */
29224943Seric 			SmtpPhase = "DATA";
2934976Seric 			if (!hasmail)
2944549Seric 			{
2954976Seric 				message("503", "Need MAIL command");
2964976Seric 				break;
2974549Seric 			}
29855012Seric 			else if (e->e_nrcpts <= 0)
2994549Seric 			{
3004976Seric 				message("503", "Need RCPT (recipient)");
3014976Seric 				break;
3024549Seric 			}
3034976Seric 
3044976Seric 			/* collect the text of the message */
30524943Seric 			SmtpPhase = "collect";
30657389Seric 			setproctitle("%s %s: %s", e->e_id, CurHostName, inp);
30755012Seric 			collect(TRUE, e);
3084976Seric 			if (Errors != 0)
3094976Seric 				break;
3104976Seric 
3118238Seric 			/*
3128238Seric 			**  Arrange to send to everyone.
3138238Seric 			**	If sending to multiple people, mail back
3148238Seric 			**		errors rather than reporting directly.
3158238Seric 			**	In any case, don't mail back errors for
3168238Seric 			**		anything that has happened up to
3178238Seric 			**		now (the other end will do this).
31810197Seric 			**	Truncate our transcript -- the mail has gotten
31910197Seric 			**		to us successfully, and if we have
32010197Seric 			**		to mail this back, it will be easier
32110197Seric 			**		on the reader.
3228238Seric 			**	Then send to everyone.
3238238Seric 			**	Finally give a reply code.  If an error has
3248238Seric 			**		already been given, don't mail a
3258238Seric 			**		message back.
3269339Seric 			**	We goose error returns by clearing error bit.
3278238Seric 			*/
3288238Seric 
32924943Seric 			SmtpPhase = "delivery";
33055012Seric 			if (e->e_nrcpts != 1)
3319378Seric 			{
3329378Seric 				HoldErrs = TRUE;
33316886Seric 				ErrorMode = EM_MAIL;
3349378Seric 			}
33555012Seric 			e->e_flags &= ~EF_FATALERRS;
33655012Seric 			e->e_xfp = freopen(queuename(e, 'x'), "w", e->e_xfp);
3374976Seric 
3384976Seric 			/* send to all recipients */
33955012Seric 			sendall(e, SM_DEFAULT);
34055012Seric 			e->e_to = NULL;
3414976Seric 
34223516Seric 			/* save statistics */
34355012Seric 			markstats(e, (ADDRESS *) NULL);
34423516Seric 
3458238Seric 			/* issue success if appropriate and reset */
3468238Seric 			if (Errors == 0 || HoldErrs)
3479283Seric 				message("250", "Ok");
3488238Seric 			else
34955012Seric 				e->e_flags &= ~EF_FATALERRS;
3509339Seric 
3519339Seric 			/* if in a child, pop back to our parent */
3529339Seric 			if (InChild)
3539339Seric 				finis();
35424943Seric 
35524943Seric 			/* clean up a bit */
35658008Seric 			hasmail = FALSE;
35755012Seric 			dropenvelope(e);
35855012Seric 			CurEnv = e = newenvelope(e);
35955012Seric 			e->e_flags = BlankEnvelope.e_flags;
3604549Seric 			break;
3614549Seric 
3624549Seric 		  case CMDRSET:		/* rset -- reset state */
3634549Seric 			message("250", "Reset state");
3649339Seric 			if (InChild)
3659339Seric 				finis();
3669339Seric 			break;
3674549Seric 
3684549Seric 		  case CMDVRFY:		/* vrfy -- verify address */
36955012Seric 			if (runinchild("SMTP-VRFY", e) > 0)
3709339Seric 				break;
37125050Seric 			setproctitle("%s: %s", CurHostName, inp);
37255173Seric #ifdef LOG
37355173Seric 			if (LogLevel >= 9)
37455173Seric 				syslog(LOG_INFO, "%s: %s", CurHostName, inp);
37555173Seric #endif
3765003Seric 			vrfyqueue = NULL;
3777762Seric 			QuickAbort = TRUE;
37855012Seric 			sendtolist(p, (ADDRESS *) NULL, &vrfyqueue, e);
3797762Seric 			if (Errors != 0)
3809339Seric 			{
3819339Seric 				if (InChild)
3829339Seric 					finis();
3837762Seric 				break;
3849339Seric 			}
3855003Seric 			while (vrfyqueue != NULL)
3865003Seric 			{
3875003Seric 				register ADDRESS *a = vrfyqueue->q_next;
3885003Seric 				char *code;
3895003Seric 
3907685Seric 				while (a != NULL && bitset(QDONTSEND|QBADADDR, a->q_flags))
3915003Seric 					a = a->q_next;
3925003Seric 
3937685Seric 				if (!bitset(QDONTSEND|QBADADDR, vrfyqueue->q_flags))
3945003Seric 				{
3955003Seric 					if (a != NULL)
3965003Seric 						code = "250-";
3975003Seric 					else
3985003Seric 						code = "250";
399*58010Seric 					if (strchr(vrfyqueue->q_paddr, '<') != NULL)
400*58010Seric 						message(code, "%s", vrfyqueue->q_paddr);
401*58010Seric 					else if (vrfyqueue->q_fullname == NULL)
4025003Seric 						message(code, "<%s>", vrfyqueue->q_paddr);
4035003Seric 					else
4045003Seric 						message(code, "%s <%s>",
4055003Seric 						    vrfyqueue->q_fullname, vrfyqueue->q_paddr);
4065003Seric 				}
4075003Seric 				else if (a == NULL)
4085003Seric 					message("554", "Self destructive alias loop");
4095003Seric 				vrfyqueue = a;
4105003Seric 			}
4119339Seric 			if (InChild)
4129339Seric 				finis();
4134549Seric 			break;
4144549Seric 
4154549Seric 		  case CMDHELP:		/* help -- give user info */
4164577Seric 			help(p);
4174549Seric 			break;
4184549Seric 
4194549Seric 		  case CMDNOOP:		/* noop -- do nothing */
4204549Seric 			message("200", "OK");
4214549Seric 			break;
4224549Seric 
4234549Seric 		  case CMDQUIT:		/* quit -- leave mail */
42425050Seric 			message("221", "%s closing connection", MyHostName);
4259339Seric 			if (InChild)
4269339Seric 				ExitStat = EX_QUIT;
4274549Seric 			finis();
4284549Seric 
4298544Seric 		  case CMDVERB:		/* set verbose mode */
4308544Seric 			Verbose = TRUE;
43125025Seric 			SendMode = SM_DELIVER;
4328544Seric 			message("200", "Verbose mode");
4338544Seric 			break;
4348544Seric 
4359314Seric 		  case CMDONEX:		/* doing one transaction only */
4369378Seric 			OneXact = TRUE;
4379314Seric 			message("200", "Only one transaction");
4389314Seric 			break;
4399314Seric 
44036230Skarels # ifdef SMTPDEBUG
4419339Seric 		  case CMDDBGQSHOW:	/* show queues */
4426907Seric 			printf("Send Queue=");
44355012Seric 			printaddr(e->e_sendqueue, TRUE);
4445003Seric 			break;
4457275Seric 
4467275Seric 		  case CMDDBGDEBUG:	/* set debug mode */
4477676Seric 			tTsetup(tTdvect, sizeof tTdvect, "0-99.1");
4487676Seric 			tTflag(p);
4497676Seric 			message("200", "Debug set");
4507275Seric 			break;
4517275Seric 
45236230Skarels # else /* not SMTPDEBUG */
45324945Seric 
45436230Skarels 		  case CMDDBGQSHOW:	/* show queues */
45536230Skarels 		  case CMDDBGDEBUG:	/* set debug mode */
45636233Skarels # ifdef LOG
45736233Skarels 			if (RealHostName != NULL && LogLevel > 0)
45836230Skarels 				syslog(LOG_NOTICE,
45936230Skarels 				    "\"%s\" command from %s (%s)\n",
46036230Skarels 				    c->cmdname, RealHostName,
46136230Skarels 				    inet_ntoa(RealHostAddr.sin_addr));
46236233Skarels # endif
46336230Skarels 			/* FALL THROUGH */
46436230Skarels # endif /* SMTPDEBUG */
46536230Skarels 
4664549Seric 		  case CMDERROR:	/* unknown command */
4674549Seric 			message("500", "Command unrecognized");
4684549Seric 			break;
4694549Seric 
4704549Seric 		  default:
47136230Skarels 			errno = 0;
4724549Seric 			syserr("smtp: unknown code %d", c->cmdcode);
4734549Seric 			break;
4744549Seric 		}
4754549Seric 	}
4764549Seric }
4774549Seric /*
4784549Seric **  SKIPWORD -- skip a fixed word.
4794549Seric **
4804549Seric **	Parameters:
4814549Seric **		p -- place to start looking.
4824549Seric **		w -- word to skip.
4834549Seric **
4844549Seric **	Returns:
4854549Seric **		p following w.
4864549Seric **		NULL on error.
4874549Seric **
4884549Seric **	Side Effects:
4894549Seric **		clobbers the p data area.
4904549Seric */
4914549Seric 
4924549Seric static char *
4934549Seric skipword(p, w)
4944549Seric 	register char *p;
4954549Seric 	char *w;
4964549Seric {
4974549Seric 	register char *q;
4984549Seric 
4994549Seric 	/* find beginning of word */
5004549Seric 	while (isspace(*p))
5014549Seric 		p++;
5024549Seric 	q = p;
5034549Seric 
5044549Seric 	/* find end of word */
5054549Seric 	while (*p != '\0' && *p != ':' && !isspace(*p))
5064549Seric 		p++;
5074549Seric 	while (isspace(*p))
5084549Seric 		*p++ = '\0';
5094549Seric 	if (*p != ':')
5104549Seric 	{
5114549Seric 	  syntax:
5124549Seric 		message("501", "Syntax error");
5134549Seric 		Errors++;
5144549Seric 		return (NULL);
5154549Seric 	}
5164549Seric 	*p++ = '\0';
5174549Seric 	while (isspace(*p))
5184549Seric 		p++;
5194549Seric 
5204549Seric 	/* see if the input word matches desired word */
52133725Sbostic 	if (strcasecmp(q, w))
5224549Seric 		goto syntax;
5234549Seric 
5244549Seric 	return (p);
5254549Seric }
5264577Seric /*
5274577Seric **  HELP -- implement the HELP command.
5284577Seric **
5294577Seric **	Parameters:
5304577Seric **		topic -- the topic we want help for.
5314577Seric **
5324577Seric **	Returns:
5334577Seric **		none.
5344577Seric **
5354577Seric **	Side Effects:
5364577Seric **		outputs the help file to message output.
5374577Seric */
5384577Seric 
5394577Seric help(topic)
5404577Seric 	char *topic;
5414577Seric {
5424577Seric 	register FILE *hf;
5434577Seric 	int len;
5444577Seric 	char buf[MAXLINE];
5454577Seric 	bool noinfo;
5464577Seric 
5478269Seric 	if (HelpFile == NULL || (hf = fopen(HelpFile, "r")) == NULL)
5484577Seric 	{
5494577Seric 		/* no help */
55011931Seric 		errno = 0;
5514577Seric 		message("502", "HELP not implemented");
5524577Seric 		return;
5534577Seric 	}
5544577Seric 
55549669Seric 	if (topic == NULL || *topic == '\0')
55649669Seric 		topic = "smtp";
55749669Seric 	else
55849669Seric 		makelower(topic);
55949669Seric 
5604577Seric 	len = strlen(topic);
5614577Seric 	noinfo = TRUE;
5624577Seric 
5634577Seric 	while (fgets(buf, sizeof buf, hf) != NULL)
5644577Seric 	{
5654577Seric 		if (strncmp(buf, topic, len) == 0)
5664577Seric 		{
5674577Seric 			register char *p;
5684577Seric 
56956795Seric 			p = strchr(buf, '\t');
5704577Seric 			if (p == NULL)
5714577Seric 				p = buf;
5724577Seric 			else
5734577Seric 				p++;
5744577Seric 			fixcrlf(p, TRUE);
5754577Seric 			message("214-", p);
5764577Seric 			noinfo = FALSE;
5774577Seric 		}
5784577Seric 	}
5794577Seric 
5804577Seric 	if (noinfo)
5814577Seric 		message("504", "HELP topic unknown");
5824577Seric 	else
5834577Seric 		message("214", "End of HELP info");
5844628Seric 	(void) fclose(hf);
5854577Seric }
5868544Seric /*
5879339Seric **  RUNINCHILD -- return twice -- once in the child, then in the parent again
5889339Seric **
5899339Seric **	Parameters:
5909339Seric **		label -- a string used in error messages
5919339Seric **
5929339Seric **	Returns:
5939339Seric **		zero in the child
5949339Seric **		one in the parent
5959339Seric **
5969339Seric **	Side Effects:
5979339Seric **		none.
5989339Seric */
5998544Seric 
60055012Seric runinchild(label, e)
6019339Seric 	char *label;
60255012Seric 	register ENVELOPE *e;
6039339Seric {
6049339Seric 	int childpid;
6059339Seric 
60616158Seric 	if (!OneXact)
6079339Seric 	{
60816158Seric 		childpid = dofork();
60916158Seric 		if (childpid < 0)
61016158Seric 		{
61116158Seric 			syserr("%s: cannot fork", label);
61216158Seric 			return (1);
61316158Seric 		}
61416158Seric 		if (childpid > 0)
61516158Seric 		{
61616158Seric 			auto int st;
6179339Seric 
61816158Seric 			/* parent -- wait for child to complete */
61916158Seric 			st = waitfor(childpid);
62016158Seric 			if (st == -1)
62116158Seric 				syserr("%s: lost child", label);
6229339Seric 
62316158Seric 			/* if we exited on a QUIT command, complete the process */
62416158Seric 			if (st == (EX_QUIT << 8))
62516158Seric 				finis();
6269339Seric 
62716158Seric 			return (1);
62816158Seric 		}
62916158Seric 		else
63016158Seric 		{
63116158Seric 			/* child */
63216158Seric 			InChild = TRUE;
63325050Seric 			QuickAbort = FALSE;
63455012Seric 			clearenvelope(e, FALSE);
63516158Seric 		}
6369339Seric 	}
63715256Seric 
63816158Seric 	/* open alias database */
63955012Seric 	initaliases(AliasFile, FALSE, e);
64016158Seric 
64116158Seric 	return (0);
6429339Seric }
6439339Seric 
64456795Seric # endif /* SMTP */
645