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*58069Seric static char sccsid[] = "@(#)srvrsmtp.c	6.10 (Berkeley) 02/19/93 (with SMTP)";
1433731Sbostic #else
15*58069Seric static char sccsid[] = "@(#)srvrsmtp.c	6.10 (Berkeley) 02/19/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);
12058050Seric 	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
152*58069Seric 			if (InChild)
153*58069Seric 				ExitStat = EX_QUIT;
1544549Seric 			finis();
1554549Seric 		}
1564549Seric 
1574549Seric 		/* clean up end of line */
1584558Seric 		fixcrlf(inp, TRUE);
1594549Seric 
1604713Seric 		/* echo command to transcript */
16155012Seric 		if (e->e_xfp != NULL)
16255012Seric 			fprintf(e->e_xfp, "<<< %s\n", inp);
1634713Seric 
1644549Seric 		/* break off command */
16558050Seric 		for (p = inp; isascii(*p) && isspace(*p); p++)
1664549Seric 			continue;
16757232Seric 		cmd = cmdbuf;
16858050Seric 		while (*p != '\0' &&
16958050Seric 		       !(isascii(*p) && isspace(*p)) &&
17058050Seric 		       cmd < &cmdbuf[sizeof cmdbuf - 2])
17124981Seric 			*cmd++ = *p++;
17224981Seric 		*cmd = '\0';
1734549Seric 
17425691Seric 		/* throw away leading whitespace */
17558050Seric 		while (isascii(*p) && isspace(*p))
17625691Seric 			p++;
17725691Seric 
1784549Seric 		/* decode command */
1794549Seric 		for (c = CmdTab; c->cmdname != NULL; c++)
1804549Seric 		{
18133725Sbostic 			if (!strcasecmp(c->cmdname, cmdbuf))
1824549Seric 				break;
1834549Seric 		}
1844549Seric 
18551954Seric 		/* reset errors */
18651954Seric 		errno = 0;
18751954Seric 
1884549Seric 		/* process command */
1894549Seric 		switch (c->cmdcode)
1904549Seric 		{
1914976Seric 		  case CMDHELO:		/* hello -- introduce yourself */
19224943Seric 			SmtpPhase = "HELO";
19325050Seric 			setproctitle("%s: %s", CurHostName, inp);
19433725Sbostic 			if (!strcasecmp(p, MyHostName))
19514877Seric 			{
19636230Skarels 				/*
19736230Skarels 				 * didn't know about alias,
19836230Skarels 				 * or connected to an echo server
19936230Skarels 				 */
20047570Seric 				message("553", "%s config error: mail loops back to myself",
20147570Seric 					MyHostName);
20214877Seric 				break;
20314877Seric 			}
20433725Sbostic 			if (RealHostName != NULL && strcasecmp(p, RealHostName))
20511146Seric 			{
20624981Seric 				char hostbuf[MAXNAME];
20711146Seric 
20824981Seric 				(void) sprintf(hostbuf, "%s (%s)", p, RealHostName);
20930448Seric 				sendinghost = newstr(hostbuf);
21011146Seric 			}
21111146Seric 			else
21230448Seric 				sendinghost = newstr(p);
2134997Seric 			message("250", "%s Hello %s, pleased to meet you",
21436230Skarels 				MyHostName, sendinghost);
2154976Seric 			break;
2164976Seric 
2174549Seric 		  case CMDMAIL:		/* mail -- designate sender */
21824943Seric 			SmtpPhase = "MAIL";
21924943Seric 
22011151Seric 			/* force a sending host even if no HELO given */
22158064Seric 			if (sendinghost == NULL && macvalue('s', e) == NULL)
22230448Seric 				sendinghost = RealHostName;
22311151Seric 
2249314Seric 			/* check for validity of this command */
2254558Seric 			if (hasmail)
2264558Seric 			{
2274558Seric 				message("503", "Sender already specified");
2284558Seric 				break;
2294558Seric 			}
2309339Seric 			if (InChild)
2319339Seric 			{
23236230Skarels 				errno = 0;
23358008Seric 				syserr("Nested MAIL command: MAIL %s", p);
234*58069Seric 				finis();
2359339Seric 			}
2369339Seric 
2379339Seric 			/* fork a subprocess to process this command */
23855012Seric 			if (runinchild("SMTP-MAIL", e) > 0)
2399339Seric 				break;
24058064Seric 			if (sendinghost != NULL)
24158064Seric 				define('s', sendinghost, e);
24255012Seric 			define('r', "SMTP", e);
24355012Seric 			initsys(e);
24457389Seric 			setproctitle("%s %s: %s", e->e_id, CurHostName, inp);
2459339Seric 
2469339Seric 			/* child -- go do the processing */
2474549Seric 			p = skipword(p, "from");
2484549Seric 			if (p == NULL)
2494549Seric 				break;
25057977Seric 			if (setjmp(TopFrame) > 0)
25157977Seric 				break;
25257977Seric 			QuickAbort = TRUE;
25355012Seric 			setsender(p, e);
2544577Seric 			if (Errors == 0)
2554549Seric 			{
2564549Seric 				message("250", "Sender ok");
2574549Seric 				hasmail = TRUE;
2584549Seric 			}
2599339Seric 			else if (InChild)
2609339Seric 				finis();
2614549Seric 			break;
2624549Seric 
2634976Seric 		  case CMDRCPT:		/* rcpt -- designate recipient */
26424943Seric 			SmtpPhase = "RCPT";
26557389Seric 			setproctitle("%s %s: %s", e->e_id, 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";
31157389Seric 			setproctitle("%s %s: %s", e->e_id, CurHostName, inp);
31255012Seric 			collect(TRUE, e);
3134976Seric 			if (Errors != 0)
3144976Seric 				break;
3154976Seric 
3168238Seric 			/*
3178238Seric 			**  Arrange to send to everyone.
3188238Seric 			**	If sending to multiple people, mail back
3198238Seric 			**		errors rather than reporting directly.
3208238Seric 			**	In any case, don't mail back errors for
3218238Seric 			**		anything that has happened up to
3228238Seric 			**		now (the other end will do this).
32310197Seric 			**	Truncate our transcript -- the mail has gotten
32410197Seric 			**		to us successfully, and if we have
32510197Seric 			**		to mail this back, it will be easier
32610197Seric 			**		on the reader.
3278238Seric 			**	Then send to everyone.
3288238Seric 			**	Finally give a reply code.  If an error has
3298238Seric 			**		already been given, don't mail a
3308238Seric 			**		message back.
3319339Seric 			**	We goose error returns by clearing error bit.
3328238Seric 			*/
3338238Seric 
33424943Seric 			SmtpPhase = "delivery";
33555012Seric 			if (e->e_nrcpts != 1)
3369378Seric 			{
3379378Seric 				HoldErrs = TRUE;
33816886Seric 				ErrorMode = EM_MAIL;
3399378Seric 			}
34055012Seric 			e->e_flags &= ~EF_FATALERRS;
34155012Seric 			e->e_xfp = freopen(queuename(e, 'x'), "w", e->e_xfp);
3424976Seric 
3434976Seric 			/* send to all recipients */
34455012Seric 			sendall(e, SM_DEFAULT);
34555012Seric 			e->e_to = NULL;
3464976Seric 
34723516Seric 			/* save statistics */
34855012Seric 			markstats(e, (ADDRESS *) NULL);
34923516Seric 
3508238Seric 			/* issue success if appropriate and reset */
3518238Seric 			if (Errors == 0 || HoldErrs)
3529283Seric 				message("250", "Ok");
3538238Seric 			else
35455012Seric 				e->e_flags &= ~EF_FATALERRS;
3559339Seric 
3569339Seric 			/* if in a child, pop back to our parent */
3579339Seric 			if (InChild)
3589339Seric 				finis();
35924943Seric 
36024943Seric 			/* clean up a bit */
36158008Seric 			hasmail = FALSE;
36255012Seric 			dropenvelope(e);
36355012Seric 			CurEnv = e = newenvelope(e);
36455012Seric 			e->e_flags = BlankEnvelope.e_flags;
3654549Seric 			break;
3664549Seric 
3674549Seric 		  case CMDRSET:		/* rset -- reset state */
3684549Seric 			message("250", "Reset state");
3699339Seric 			if (InChild)
3709339Seric 				finis();
3719339Seric 			break;
3724549Seric 
3734549Seric 		  case CMDVRFY:		/* vrfy -- verify address */
37455012Seric 			if (runinchild("SMTP-VRFY", e) > 0)
3759339Seric 				break;
37625050Seric 			setproctitle("%s: %s", CurHostName, inp);
37755173Seric #ifdef LOG
37858020Seric 			if (LogLevel > 5)
37955173Seric 				syslog(LOG_INFO, "%s: %s", CurHostName, inp);
38055173Seric #endif
3815003Seric 			vrfyqueue = NULL;
3827762Seric 			QuickAbort = TRUE;
38355012Seric 			sendtolist(p, (ADDRESS *) NULL, &vrfyqueue, e);
3847762Seric 			if (Errors != 0)
3859339Seric 			{
3869339Seric 				if (InChild)
3879339Seric 					finis();
3887762Seric 				break;
3899339Seric 			}
3905003Seric 			while (vrfyqueue != NULL)
3915003Seric 			{
3925003Seric 				register ADDRESS *a = vrfyqueue->q_next;
3935003Seric 				char *code;
3945003Seric 
3957685Seric 				while (a != NULL && bitset(QDONTSEND|QBADADDR, a->q_flags))
3965003Seric 					a = a->q_next;
3975003Seric 
3987685Seric 				if (!bitset(QDONTSEND|QBADADDR, vrfyqueue->q_flags))
3995003Seric 				{
4005003Seric 					if (a != NULL)
4015003Seric 						code = "250-";
4025003Seric 					else
4035003Seric 						code = "250";
40458010Seric 					if (strchr(vrfyqueue->q_paddr, '<') != NULL)
40558010Seric 						message(code, "%s", vrfyqueue->q_paddr);
40658010Seric 					else if (vrfyqueue->q_fullname == NULL)
4075003Seric 						message(code, "<%s>", vrfyqueue->q_paddr);
4085003Seric 					else
4095003Seric 						message(code, "%s <%s>",
4105003Seric 						    vrfyqueue->q_fullname, vrfyqueue->q_paddr);
4115003Seric 				}
4125003Seric 				else if (a == NULL)
4135003Seric 					message("554", "Self destructive alias loop");
4145003Seric 				vrfyqueue = a;
4155003Seric 			}
4169339Seric 			if (InChild)
4179339Seric 				finis();
4184549Seric 			break;
4194549Seric 
4204549Seric 		  case CMDHELP:		/* help -- give user info */
4214577Seric 			help(p);
4224549Seric 			break;
4234549Seric 
4244549Seric 		  case CMDNOOP:		/* noop -- do nothing */
4254549Seric 			message("200", "OK");
4264549Seric 			break;
4274549Seric 
4284549Seric 		  case CMDQUIT:		/* quit -- leave mail */
42925050Seric 			message("221", "%s closing connection", MyHostName);
4309339Seric 			if (InChild)
4319339Seric 				ExitStat = EX_QUIT;
4324549Seric 			finis();
4334549Seric 
4348544Seric 		  case CMDVERB:		/* set verbose mode */
4358544Seric 			Verbose = TRUE;
43625025Seric 			SendMode = SM_DELIVER;
4378544Seric 			message("200", "Verbose mode");
4388544Seric 			break;
4398544Seric 
4409314Seric 		  case CMDONEX:		/* doing one transaction only */
4419378Seric 			OneXact = TRUE;
4429314Seric 			message("200", "Only one transaction");
4439314Seric 			break;
4449314Seric 
44536230Skarels # ifdef SMTPDEBUG
4469339Seric 		  case CMDDBGQSHOW:	/* show queues */
4476907Seric 			printf("Send Queue=");
44855012Seric 			printaddr(e->e_sendqueue, TRUE);
4495003Seric 			break;
4507275Seric 
4517275Seric 		  case CMDDBGDEBUG:	/* set debug mode */
4527676Seric 			tTsetup(tTdvect, sizeof tTdvect, "0-99.1");
4537676Seric 			tTflag(p);
4547676Seric 			message("200", "Debug set");
4557275Seric 			break;
4567275Seric 
45736230Skarels # else /* not SMTPDEBUG */
45824945Seric 
45936230Skarels 		  case CMDDBGQSHOW:	/* show queues */
46036230Skarels 		  case CMDDBGDEBUG:	/* set debug mode */
46136233Skarels # ifdef LOG
46236233Skarels 			if (RealHostName != NULL && LogLevel > 0)
46336230Skarels 				syslog(LOG_NOTICE,
46458020Seric 				    "\"%s\" command from %s (%s)",
46536230Skarels 				    c->cmdname, RealHostName,
46636230Skarels 				    inet_ntoa(RealHostAddr.sin_addr));
46736233Skarels # endif
46836230Skarels 			/* FALL THROUGH */
46936230Skarels # endif /* SMTPDEBUG */
47036230Skarels 
4714549Seric 		  case CMDERROR:	/* unknown command */
4724549Seric 			message("500", "Command unrecognized");
4734549Seric 			break;
4744549Seric 
4754549Seric 		  default:
47636230Skarels 			errno = 0;
4774549Seric 			syserr("smtp: unknown code %d", c->cmdcode);
4784549Seric 			break;
4794549Seric 		}
4804549Seric 	}
4814549Seric }
4824549Seric /*
4834549Seric **  SKIPWORD -- skip a fixed word.
4844549Seric **
4854549Seric **	Parameters:
4864549Seric **		p -- place to start looking.
4874549Seric **		w -- word to skip.
4884549Seric **
4894549Seric **	Returns:
4904549Seric **		p following w.
4914549Seric **		NULL on error.
4924549Seric **
4934549Seric **	Side Effects:
4944549Seric **		clobbers the p data area.
4954549Seric */
4964549Seric 
4974549Seric static char *
4984549Seric skipword(p, w)
4994549Seric 	register char *p;
5004549Seric 	char *w;
5014549Seric {
5024549Seric 	register char *q;
5034549Seric 
5044549Seric 	/* find beginning of word */
50558050Seric 	while (isascii(*p) && isspace(*p))
5064549Seric 		p++;
5074549Seric 	q = p;
5084549Seric 
5094549Seric 	/* find end of word */
51058050Seric 	while (*p != '\0' && *p != ':' && !(isascii(*p) && isspace(*p)))
5114549Seric 		p++;
51258050Seric 	while (isascii(*p) && isspace(*p))
5134549Seric 		*p++ = '\0';
5144549Seric 	if (*p != ':')
5154549Seric 	{
5164549Seric 	  syntax:
5174549Seric 		message("501", "Syntax error");
5184549Seric 		Errors++;
5194549Seric 		return (NULL);
5204549Seric 	}
5214549Seric 	*p++ = '\0';
52258050Seric 	while (isascii(*p) && isspace(*p))
5234549Seric 		p++;
5244549Seric 
5254549Seric 	/* see if the input word matches desired word */
52633725Sbostic 	if (strcasecmp(q, w))
5274549Seric 		goto syntax;
5284549Seric 
5294549Seric 	return (p);
5304549Seric }
5314577Seric /*
5324577Seric **  HELP -- implement the HELP command.
5334577Seric **
5344577Seric **	Parameters:
5354577Seric **		topic -- the topic we want help for.
5364577Seric **
5374577Seric **	Returns:
5384577Seric **		none.
5394577Seric **
5404577Seric **	Side Effects:
5414577Seric **		outputs the help file to message output.
5424577Seric */
5434577Seric 
5444577Seric help(topic)
5454577Seric 	char *topic;
5464577Seric {
5474577Seric 	register FILE *hf;
5484577Seric 	int len;
5494577Seric 	char buf[MAXLINE];
5504577Seric 	bool noinfo;
5514577Seric 
5528269Seric 	if (HelpFile == NULL || (hf = fopen(HelpFile, "r")) == NULL)
5534577Seric 	{
5544577Seric 		/* no help */
55511931Seric 		errno = 0;
5564577Seric 		message("502", "HELP not implemented");
5574577Seric 		return;
5584577Seric 	}
5594577Seric 
56049669Seric 	if (topic == NULL || *topic == '\0')
56149669Seric 		topic = "smtp";
56249669Seric 	else
56349669Seric 		makelower(topic);
56449669Seric 
5654577Seric 	len = strlen(topic);
5664577Seric 	noinfo = TRUE;
5674577Seric 
5684577Seric 	while (fgets(buf, sizeof buf, hf) != NULL)
5694577Seric 	{
5704577Seric 		if (strncmp(buf, topic, len) == 0)
5714577Seric 		{
5724577Seric 			register char *p;
5734577Seric 
57456795Seric 			p = strchr(buf, '\t');
5754577Seric 			if (p == NULL)
5764577Seric 				p = buf;
5774577Seric 			else
5784577Seric 				p++;
5794577Seric 			fixcrlf(p, TRUE);
5804577Seric 			message("214-", p);
5814577Seric 			noinfo = FALSE;
5824577Seric 		}
5834577Seric 	}
5844577Seric 
5854577Seric 	if (noinfo)
5864577Seric 		message("504", "HELP topic unknown");
5874577Seric 	else
5884577Seric 		message("214", "End of HELP info");
5894628Seric 	(void) fclose(hf);
5904577Seric }
5918544Seric /*
5929339Seric **  RUNINCHILD -- return twice -- once in the child, then in the parent again
5939339Seric **
5949339Seric **	Parameters:
5959339Seric **		label -- a string used in error messages
5969339Seric **
5979339Seric **	Returns:
5989339Seric **		zero in the child
5999339Seric **		one in the parent
6009339Seric **
6019339Seric **	Side Effects:
6029339Seric **		none.
6039339Seric */
6048544Seric 
60555012Seric runinchild(label, e)
6069339Seric 	char *label;
60755012Seric 	register ENVELOPE *e;
6089339Seric {
6099339Seric 	int childpid;
6109339Seric 
61116158Seric 	if (!OneXact)
6129339Seric 	{
61316158Seric 		childpid = dofork();
61416158Seric 		if (childpid < 0)
61516158Seric 		{
61616158Seric 			syserr("%s: cannot fork", label);
61716158Seric 			return (1);
61816158Seric 		}
61916158Seric 		if (childpid > 0)
62016158Seric 		{
62116158Seric 			auto int st;
6229339Seric 
62316158Seric 			/* parent -- wait for child to complete */
62416158Seric 			st = waitfor(childpid);
62516158Seric 			if (st == -1)
62616158Seric 				syserr("%s: lost child", label);
6279339Seric 
62816158Seric 			/* if we exited on a QUIT command, complete the process */
62916158Seric 			if (st == (EX_QUIT << 8))
63016158Seric 				finis();
6319339Seric 
63216158Seric 			return (1);
63316158Seric 		}
63416158Seric 		else
63516158Seric 		{
63616158Seric 			/* child */
63716158Seric 			InChild = TRUE;
63825050Seric 			QuickAbort = FALSE;
63955012Seric 			clearenvelope(e, FALSE);
64016158Seric 		}
6419339Seric 	}
64215256Seric 
64316158Seric 	/* open alias database */
64455012Seric 	initaliases(AliasFile, FALSE, e);
64516158Seric 
64616158Seric 	return (0);
6479339Seric }
6489339Seric 
64956795Seric # endif /* SMTP */
650