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*58050Seric static char sccsid[] = "@(#)srvrsmtp.c	6.8 (Berkeley) 02/18/93 (with SMTP)";
1433731Sbostic #else
15*58050Seric static char sccsid[] = "@(#)srvrsmtp.c	6.8 (Berkeley) 02/18/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);
120*58050Seric 	expand("\201e", 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
14858020Seric 			if (LogLevel > 1)
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 */
163*58050Seric 		for (p = inp; isascii(*p) && isspace(*p); p++)
1644549Seric 			continue;
16557232Seric 		cmd = cmdbuf;
166*58050Seric 		while (*p != '\0' &&
167*58050Seric 		       !(isascii(*p) && isspace(*p)) &&
168*58050Seric 		       cmd < &cmdbuf[sizeof cmdbuf - 2])
16924981Seric 			*cmd++ = *p++;
17024981Seric 		*cmd = '\0';
1714549Seric 
17225691Seric 		/* throw away leading whitespace */
173*58050Seric 		while (isascii(*p) && isspace(*p))
17425691Seric 			p++;
17525691Seric 
1764549Seric 		/* decode command */
1774549Seric 		for (c = CmdTab; c->cmdname != NULL; c++)
1784549Seric 		{
17933725Sbostic 			if (!strcasecmp(c->cmdname, cmdbuf))
1804549Seric 				break;
1814549Seric 		}
1824549Seric 
18351954Seric 		/* reset errors */
18451954Seric 		errno = 0;
18551954Seric 
1864549Seric 		/* process command */
1874549Seric 		switch (c->cmdcode)
1884549Seric 		{
1894976Seric 		  case CMDHELO:		/* hello -- introduce yourself */
19024943Seric 			SmtpPhase = "HELO";
19125050Seric 			setproctitle("%s: %s", CurHostName, inp);
19233725Sbostic 			if (!strcasecmp(p, MyHostName))
19314877Seric 			{
19436230Skarels 				/*
19536230Skarels 				 * didn't know about alias,
19636230Skarels 				 * or connected to an echo server
19736230Skarels 				 */
19847570Seric 				message("553", "%s config error: mail loops back to myself",
19947570Seric 					MyHostName);
20014877Seric 				break;
20114877Seric 			}
20233725Sbostic 			if (RealHostName != NULL && strcasecmp(p, RealHostName))
20311146Seric 			{
20424981Seric 				char hostbuf[MAXNAME];
20511146Seric 
20624981Seric 				(void) sprintf(hostbuf, "%s (%s)", p, RealHostName);
20730448Seric 				sendinghost = newstr(hostbuf);
20811146Seric 			}
20911146Seric 			else
21030448Seric 				sendinghost = newstr(p);
2114997Seric 			message("250", "%s Hello %s, pleased to meet you",
21236230Skarels 				MyHostName, sendinghost);
2134976Seric 			break;
2144976Seric 
2154549Seric 		  case CMDMAIL:		/* mail -- designate sender */
21624943Seric 			SmtpPhase = "MAIL";
21724943Seric 
21811151Seric 			/* force a sending host even if no HELO given */
21955012Seric 			if (RealHostName != NULL && macvalue('s', e) == NULL)
22030448Seric 				sendinghost = RealHostName;
22111151Seric 
2229314Seric 			/* check for validity of this command */
2234558Seric 			if (hasmail)
2244558Seric 			{
2254558Seric 				message("503", "Sender already specified");
2264558Seric 				break;
2274558Seric 			}
2289339Seric 			if (InChild)
2299339Seric 			{
23036230Skarels 				errno = 0;
23158008Seric 				syserr("Nested MAIL command: MAIL %s", p);
2329339Seric 				exit(0);
2339339Seric 			}
2349339Seric 
2359339Seric 			/* fork a subprocess to process this command */
23655012Seric 			if (runinchild("SMTP-MAIL", e) > 0)
2379339Seric 				break;
23855012Seric 			define('s', sendinghost, e);
23955012Seric 			define('r', "SMTP", e);
24055012Seric 			initsys(e);
24157389Seric 			setproctitle("%s %s: %s", e->e_id, CurHostName, inp);
2429339Seric 
2439339Seric 			/* child -- go do the processing */
2444549Seric 			p = skipword(p, "from");
2454549Seric 			if (p == NULL)
2464549Seric 				break;
24757977Seric 			if (setjmp(TopFrame) > 0)
24857977Seric 				break;
24957977Seric 			QuickAbort = TRUE;
25055012Seric 			setsender(p, e);
2514577Seric 			if (Errors == 0)
2524549Seric 			{
2534549Seric 				message("250", "Sender ok");
2544549Seric 				hasmail = TRUE;
2554549Seric 			}
2569339Seric 			else if (InChild)
2579339Seric 				finis();
2584549Seric 			break;
2594549Seric 
2604976Seric 		  case CMDRCPT:		/* rcpt -- designate recipient */
26124943Seric 			SmtpPhase = "RCPT";
26257389Seric 			setproctitle("%s %s: %s", e->e_id, CurHostName, inp);
26312612Seric 			if (setjmp(TopFrame) > 0)
26414785Seric 			{
26555012Seric 				e->e_flags &= ~EF_FATALERRS;
26612612Seric 				break;
26714785Seric 			}
26812612Seric 			QuickAbort = TRUE;
26951951Seric 			LogUsrErrs = TRUE;
2704549Seric 			p = skipword(p, "to");
2714549Seric 			if (p == NULL)
2724549Seric 				break;
27355012Seric 			a = parseaddr(p, (ADDRESS *) NULL, 1, '\0', e);
27412612Seric 			if (a == NULL)
27512612Seric 				break;
27616886Seric 			a->q_flags |= QPRIMARY;
27755012Seric 			a = recipient(a, &e->e_sendqueue, e);
27812612Seric 			if (Errors != 0)
27912612Seric 				break;
28012612Seric 
28112612Seric 			/* no errors during parsing, but might be a duplicate */
28255012Seric 			e->e_to = p;
28312612Seric 			if (!bitset(QBADADDR, a->q_flags))
28412612Seric 				message("250", "Recipient ok");
28512612Seric 			else
2864549Seric 			{
28712612Seric 				/* punt -- should keep message in ADDRESS.... */
28812612Seric 				message("550", "Addressee unknown");
2894549Seric 			}
29055012Seric 			e->e_to = NULL;
2914549Seric 			break;
2924549Seric 
2934549Seric 		  case CMDDATA:		/* data -- text of mail */
29424943Seric 			SmtpPhase = "DATA";
2954976Seric 			if (!hasmail)
2964549Seric 			{
2974976Seric 				message("503", "Need MAIL command");
2984976Seric 				break;
2994549Seric 			}
30055012Seric 			else if (e->e_nrcpts <= 0)
3014549Seric 			{
3024976Seric 				message("503", "Need RCPT (recipient)");
3034976Seric 				break;
3044549Seric 			}
3054976Seric 
3064976Seric 			/* collect the text of the message */
30724943Seric 			SmtpPhase = "collect";
30857389Seric 			setproctitle("%s %s: %s", e->e_id, CurHostName, inp);
30955012Seric 			collect(TRUE, e);
3104976Seric 			if (Errors != 0)
3114976Seric 				break;
3124976Seric 
3138238Seric 			/*
3148238Seric 			**  Arrange to send to everyone.
3158238Seric 			**	If sending to multiple people, mail back
3168238Seric 			**		errors rather than reporting directly.
3178238Seric 			**	In any case, don't mail back errors for
3188238Seric 			**		anything that has happened up to
3198238Seric 			**		now (the other end will do this).
32010197Seric 			**	Truncate our transcript -- the mail has gotten
32110197Seric 			**		to us successfully, and if we have
32210197Seric 			**		to mail this back, it will be easier
32310197Seric 			**		on the reader.
3248238Seric 			**	Then send to everyone.
3258238Seric 			**	Finally give a reply code.  If an error has
3268238Seric 			**		already been given, don't mail a
3278238Seric 			**		message back.
3289339Seric 			**	We goose error returns by clearing error bit.
3298238Seric 			*/
3308238Seric 
33124943Seric 			SmtpPhase = "delivery";
33255012Seric 			if (e->e_nrcpts != 1)
3339378Seric 			{
3349378Seric 				HoldErrs = TRUE;
33516886Seric 				ErrorMode = EM_MAIL;
3369378Seric 			}
33755012Seric 			e->e_flags &= ~EF_FATALERRS;
33855012Seric 			e->e_xfp = freopen(queuename(e, 'x'), "w", e->e_xfp);
3394976Seric 
3404976Seric 			/* send to all recipients */
34155012Seric 			sendall(e, SM_DEFAULT);
34255012Seric 			e->e_to = NULL;
3434976Seric 
34423516Seric 			/* save statistics */
34555012Seric 			markstats(e, (ADDRESS *) NULL);
34623516Seric 
3478238Seric 			/* issue success if appropriate and reset */
3488238Seric 			if (Errors == 0 || HoldErrs)
3499283Seric 				message("250", "Ok");
3508238Seric 			else
35155012Seric 				e->e_flags &= ~EF_FATALERRS;
3529339Seric 
3539339Seric 			/* if in a child, pop back to our parent */
3549339Seric 			if (InChild)
3559339Seric 				finis();
35624943Seric 
35724943Seric 			/* clean up a bit */
35858008Seric 			hasmail = FALSE;
35955012Seric 			dropenvelope(e);
36055012Seric 			CurEnv = e = newenvelope(e);
36155012Seric 			e->e_flags = BlankEnvelope.e_flags;
3624549Seric 			break;
3634549Seric 
3644549Seric 		  case CMDRSET:		/* rset -- reset state */
3654549Seric 			message("250", "Reset state");
3669339Seric 			if (InChild)
3679339Seric 				finis();
3689339Seric 			break;
3694549Seric 
3704549Seric 		  case CMDVRFY:		/* vrfy -- verify address */
37155012Seric 			if (runinchild("SMTP-VRFY", e) > 0)
3729339Seric 				break;
37325050Seric 			setproctitle("%s: %s", CurHostName, inp);
37455173Seric #ifdef LOG
37558020Seric 			if (LogLevel > 5)
37655173Seric 				syslog(LOG_INFO, "%s: %s", CurHostName, inp);
37755173Seric #endif
3785003Seric 			vrfyqueue = NULL;
3797762Seric 			QuickAbort = TRUE;
38055012Seric 			sendtolist(p, (ADDRESS *) NULL, &vrfyqueue, e);
3817762Seric 			if (Errors != 0)
3829339Seric 			{
3839339Seric 				if (InChild)
3849339Seric 					finis();
3857762Seric 				break;
3869339Seric 			}
3875003Seric 			while (vrfyqueue != NULL)
3885003Seric 			{
3895003Seric 				register ADDRESS *a = vrfyqueue->q_next;
3905003Seric 				char *code;
3915003Seric 
3927685Seric 				while (a != NULL && bitset(QDONTSEND|QBADADDR, a->q_flags))
3935003Seric 					a = a->q_next;
3945003Seric 
3957685Seric 				if (!bitset(QDONTSEND|QBADADDR, vrfyqueue->q_flags))
3965003Seric 				{
3975003Seric 					if (a != NULL)
3985003Seric 						code = "250-";
3995003Seric 					else
4005003Seric 						code = "250";
40158010Seric 					if (strchr(vrfyqueue->q_paddr, '<') != NULL)
40258010Seric 						message(code, "%s", vrfyqueue->q_paddr);
40358010Seric 					else if (vrfyqueue->q_fullname == NULL)
4045003Seric 						message(code, "<%s>", vrfyqueue->q_paddr);
4055003Seric 					else
4065003Seric 						message(code, "%s <%s>",
4075003Seric 						    vrfyqueue->q_fullname, vrfyqueue->q_paddr);
4085003Seric 				}
4095003Seric 				else if (a == NULL)
4105003Seric 					message("554", "Self destructive alias loop");
4115003Seric 				vrfyqueue = a;
4125003Seric 			}
4139339Seric 			if (InChild)
4149339Seric 				finis();
4154549Seric 			break;
4164549Seric 
4174549Seric 		  case CMDHELP:		/* help -- give user info */
4184577Seric 			help(p);
4194549Seric 			break;
4204549Seric 
4214549Seric 		  case CMDNOOP:		/* noop -- do nothing */
4224549Seric 			message("200", "OK");
4234549Seric 			break;
4244549Seric 
4254549Seric 		  case CMDQUIT:		/* quit -- leave mail */
42625050Seric 			message("221", "%s closing connection", MyHostName);
4279339Seric 			if (InChild)
4289339Seric 				ExitStat = EX_QUIT;
4294549Seric 			finis();
4304549Seric 
4318544Seric 		  case CMDVERB:		/* set verbose mode */
4328544Seric 			Verbose = TRUE;
43325025Seric 			SendMode = SM_DELIVER;
4348544Seric 			message("200", "Verbose mode");
4358544Seric 			break;
4368544Seric 
4379314Seric 		  case CMDONEX:		/* doing one transaction only */
4389378Seric 			OneXact = TRUE;
4399314Seric 			message("200", "Only one transaction");
4409314Seric 			break;
4419314Seric 
44236230Skarels # ifdef SMTPDEBUG
4439339Seric 		  case CMDDBGQSHOW:	/* show queues */
4446907Seric 			printf("Send Queue=");
44555012Seric 			printaddr(e->e_sendqueue, TRUE);
4465003Seric 			break;
4477275Seric 
4487275Seric 		  case CMDDBGDEBUG:	/* set debug mode */
4497676Seric 			tTsetup(tTdvect, sizeof tTdvect, "0-99.1");
4507676Seric 			tTflag(p);
4517676Seric 			message("200", "Debug set");
4527275Seric 			break;
4537275Seric 
45436230Skarels # else /* not SMTPDEBUG */
45524945Seric 
45636230Skarels 		  case CMDDBGQSHOW:	/* show queues */
45736230Skarels 		  case CMDDBGDEBUG:	/* set debug mode */
45836233Skarels # ifdef LOG
45936233Skarels 			if (RealHostName != NULL && LogLevel > 0)
46036230Skarels 				syslog(LOG_NOTICE,
46158020Seric 				    "\"%s\" command from %s (%s)",
46236230Skarels 				    c->cmdname, RealHostName,
46336230Skarels 				    inet_ntoa(RealHostAddr.sin_addr));
46436233Skarels # endif
46536230Skarels 			/* FALL THROUGH */
46636230Skarels # endif /* SMTPDEBUG */
46736230Skarels 
4684549Seric 		  case CMDERROR:	/* unknown command */
4694549Seric 			message("500", "Command unrecognized");
4704549Seric 			break;
4714549Seric 
4724549Seric 		  default:
47336230Skarels 			errno = 0;
4744549Seric 			syserr("smtp: unknown code %d", c->cmdcode);
4754549Seric 			break;
4764549Seric 		}
4774549Seric 	}
4784549Seric }
4794549Seric /*
4804549Seric **  SKIPWORD -- skip a fixed word.
4814549Seric **
4824549Seric **	Parameters:
4834549Seric **		p -- place to start looking.
4844549Seric **		w -- word to skip.
4854549Seric **
4864549Seric **	Returns:
4874549Seric **		p following w.
4884549Seric **		NULL on error.
4894549Seric **
4904549Seric **	Side Effects:
4914549Seric **		clobbers the p data area.
4924549Seric */
4934549Seric 
4944549Seric static char *
4954549Seric skipword(p, w)
4964549Seric 	register char *p;
4974549Seric 	char *w;
4984549Seric {
4994549Seric 	register char *q;
5004549Seric 
5014549Seric 	/* find beginning of word */
502*58050Seric 	while (isascii(*p) && isspace(*p))
5034549Seric 		p++;
5044549Seric 	q = p;
5054549Seric 
5064549Seric 	/* find end of word */
507*58050Seric 	while (*p != '\0' && *p != ':' && !(isascii(*p) && isspace(*p)))
5084549Seric 		p++;
509*58050Seric 	while (isascii(*p) && isspace(*p))
5104549Seric 		*p++ = '\0';
5114549Seric 	if (*p != ':')
5124549Seric 	{
5134549Seric 	  syntax:
5144549Seric 		message("501", "Syntax error");
5154549Seric 		Errors++;
5164549Seric 		return (NULL);
5174549Seric 	}
5184549Seric 	*p++ = '\0';
519*58050Seric 	while (isascii(*p) && isspace(*p))
5204549Seric 		p++;
5214549Seric 
5224549Seric 	/* see if the input word matches desired word */
52333725Sbostic 	if (strcasecmp(q, w))
5244549Seric 		goto syntax;
5254549Seric 
5264549Seric 	return (p);
5274549Seric }
5284577Seric /*
5294577Seric **  HELP -- implement the HELP command.
5304577Seric **
5314577Seric **	Parameters:
5324577Seric **		topic -- the topic we want help for.
5334577Seric **
5344577Seric **	Returns:
5354577Seric **		none.
5364577Seric **
5374577Seric **	Side Effects:
5384577Seric **		outputs the help file to message output.
5394577Seric */
5404577Seric 
5414577Seric help(topic)
5424577Seric 	char *topic;
5434577Seric {
5444577Seric 	register FILE *hf;
5454577Seric 	int len;
5464577Seric 	char buf[MAXLINE];
5474577Seric 	bool noinfo;
5484577Seric 
5498269Seric 	if (HelpFile == NULL || (hf = fopen(HelpFile, "r")) == NULL)
5504577Seric 	{
5514577Seric 		/* no help */
55211931Seric 		errno = 0;
5534577Seric 		message("502", "HELP not implemented");
5544577Seric 		return;
5554577Seric 	}
5564577Seric 
55749669Seric 	if (topic == NULL || *topic == '\0')
55849669Seric 		topic = "smtp";
55949669Seric 	else
56049669Seric 		makelower(topic);
56149669Seric 
5624577Seric 	len = strlen(topic);
5634577Seric 	noinfo = TRUE;
5644577Seric 
5654577Seric 	while (fgets(buf, sizeof buf, hf) != NULL)
5664577Seric 	{
5674577Seric 		if (strncmp(buf, topic, len) == 0)
5684577Seric 		{
5694577Seric 			register char *p;
5704577Seric 
57156795Seric 			p = strchr(buf, '\t');
5724577Seric 			if (p == NULL)
5734577Seric 				p = buf;
5744577Seric 			else
5754577Seric 				p++;
5764577Seric 			fixcrlf(p, TRUE);
5774577Seric 			message("214-", p);
5784577Seric 			noinfo = FALSE;
5794577Seric 		}
5804577Seric 	}
5814577Seric 
5824577Seric 	if (noinfo)
5834577Seric 		message("504", "HELP topic unknown");
5844577Seric 	else
5854577Seric 		message("214", "End of HELP info");
5864628Seric 	(void) fclose(hf);
5874577Seric }
5888544Seric /*
5899339Seric **  RUNINCHILD -- return twice -- once in the child, then in the parent again
5909339Seric **
5919339Seric **	Parameters:
5929339Seric **		label -- a string used in error messages
5939339Seric **
5949339Seric **	Returns:
5959339Seric **		zero in the child
5969339Seric **		one in the parent
5979339Seric **
5989339Seric **	Side Effects:
5999339Seric **		none.
6009339Seric */
6018544Seric 
60255012Seric runinchild(label, e)
6039339Seric 	char *label;
60455012Seric 	register ENVELOPE *e;
6059339Seric {
6069339Seric 	int childpid;
6079339Seric 
60816158Seric 	if (!OneXact)
6099339Seric 	{
61016158Seric 		childpid = dofork();
61116158Seric 		if (childpid < 0)
61216158Seric 		{
61316158Seric 			syserr("%s: cannot fork", label);
61416158Seric 			return (1);
61516158Seric 		}
61616158Seric 		if (childpid > 0)
61716158Seric 		{
61816158Seric 			auto int st;
6199339Seric 
62016158Seric 			/* parent -- wait for child to complete */
62116158Seric 			st = waitfor(childpid);
62216158Seric 			if (st == -1)
62316158Seric 				syserr("%s: lost child", label);
6249339Seric 
62516158Seric 			/* if we exited on a QUIT command, complete the process */
62616158Seric 			if (st == (EX_QUIT << 8))
62716158Seric 				finis();
6289339Seric 
62916158Seric 			return (1);
63016158Seric 		}
63116158Seric 		else
63216158Seric 		{
63316158Seric 			/* child */
63416158Seric 			InChild = TRUE;
63525050Seric 			QuickAbort = FALSE;
63655012Seric 			clearenvelope(e, FALSE);
63716158Seric 		}
6389339Seric 	}
63915256Seric 
64016158Seric 	/* open alias database */
64155012Seric 	initaliases(AliasFile, FALSE, e);
64216158Seric 
64316158Seric 	return (0);
6449339Seric }
6459339Seric 
64656795Seric # endif /* SMTP */
647