122712Sdist /*
234921Sbostic  * Copyright (c) 1983 Eric P. Allman
333731Sbostic  * Copyright (c) 1988 Regents of the University of California.
433731Sbostic  * All rights reserved.
533731Sbostic  *
633731Sbostic  * Redistribution and use in source and binary forms are permitted
734921Sbostic  * provided that the above copyright notice and this paragraph are
834921Sbostic  * duplicated in all such forms and that any documentation,
934921Sbostic  * advertising materials, and other materials related to such
1034921Sbostic  * distribution and use acknowledge that the software was developed
1134921Sbostic  * by the University of California, Berkeley.  The name of the
1234921Sbostic  * University may not be used to endorse or promote products derived
1334921Sbostic  * from this software without specific prior written permission.
1434921Sbostic  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
1534921Sbostic  * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
1634921Sbostic  * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
1733731Sbostic  */
1822712Sdist 
1933731Sbostic # include "sendmail.h"
2022712Sdist 
2133731Sbostic #ifndef lint
2233731Sbostic #ifdef SMTP
23*36579Sbostic static char sccsid[] = "@(#)srvrsmtp.c	5.27 (Berkeley) 01/18/89 (with SMTP)";
2433731Sbostic #else
25*36579Sbostic static char sccsid[] = "@(#)srvrsmtp.c	5.27 (Berkeley) 01/18/89 (without SMTP)";
2633731Sbostic #endif
2733731Sbostic #endif /* not lint */
2833731Sbostic 
299339Seric # include <errno.h>
3011728Seric # include <signal.h>
314549Seric 
3233731Sbostic # ifdef SMTP
334556Seric 
344549Seric /*
354549Seric **  SMTP -- run the SMTP protocol.
364549Seric **
374549Seric **	Parameters:
384549Seric **		none.
394549Seric **
404549Seric **	Returns:
414549Seric **		never.
424549Seric **
434549Seric **	Side Effects:
444549Seric **		Reads commands from the input channel and processes
454549Seric **			them.
464549Seric */
474549Seric 
484549Seric struct cmd
494549Seric {
504549Seric 	char	*cmdname;	/* command name */
514549Seric 	int	cmdcode;	/* internal code, see below */
524549Seric };
534549Seric 
544549Seric /* values for cmdcode */
554549Seric # define CMDERROR	0	/* bad command */
564549Seric # define CMDMAIL	1	/* mail -- designate sender */
574976Seric # define CMDRCPT	2	/* rcpt -- designate recipient */
584549Seric # define CMDDATA	3	/* data -- send message text */
599339Seric # define CMDRSET	4	/* rset -- reset state */
609339Seric # define CMDVRFY	5	/* vrfy -- verify address */
619339Seric # define CMDHELP	6	/* help -- give usage info */
629339Seric # define CMDNOOP	7	/* noop -- do nothing */
639339Seric # define CMDQUIT	8	/* quit -- close connection and die */
649339Seric # define CMDHELO	9	/* helo -- be polite */
6536230Skarels # define CMDONEX	10	/* onex -- sending one transaction only */
6636230Skarels # define CMDVERB	11	/* verb -- go into verbose mode */
6736230Skarels /* debugging-only commands, only enabled if SMTPDEBUG is defined */
6836230Skarels # define CMDDBGQSHOW	12	/* showq -- show send queue */
6936230Skarels # define CMDDBGDEBUG	13	/* debug -- set debug mode */
704549Seric 
714549Seric static struct cmd	CmdTab[] =
724549Seric {
734549Seric 	"mail",		CMDMAIL,
744976Seric 	"rcpt",		CMDRCPT,
754549Seric 	"data",		CMDDATA,
764549Seric 	"rset",		CMDRSET,
774549Seric 	"vrfy",		CMDVRFY,
787762Seric 	"expn",		CMDVRFY,
794549Seric 	"help",		CMDHELP,
804549Seric 	"noop",		CMDNOOP,
814549Seric 	"quit",		CMDQUIT,
824976Seric 	"helo",		CMDHELO,
838544Seric 	"verb",		CMDVERB,
849314Seric 	"onex",		CMDONEX,
8536230Skarels 	/*
8636230Skarels 	 * remaining commands are here only
8736230Skarels 	 * to trap and log attempts to use them
8836230Skarels 	 */
899339Seric 	"showq",	CMDDBGQSHOW,
908544Seric 	"debug",	CMDDBGDEBUG,
914549Seric 	NULL,		CMDERROR,
924549Seric };
934549Seric 
949339Seric bool	InChild = FALSE;		/* true if running in a subprocess */
959378Seric bool	OneXact = FALSE;		/* one xaction only this run */
9611146Seric 
979339Seric #define EX_QUIT		22		/* special code for QUIT command */
988544Seric 
994549Seric smtp()
1004549Seric {
1014549Seric 	register char *p;
1028544Seric 	register struct cmd *c;
1034549Seric 	char *cmd;
1044549Seric 	extern char *skipword();
1054549Seric 	bool hasmail;			/* mail command received */
1065003Seric 	auto ADDRESS *vrfyqueue;
10712612Seric 	ADDRESS *a;
10830448Seric 	char *sendinghost;
1098544Seric 	char inp[MAXLINE];
11024981Seric 	char cmdbuf[100];
1117124Seric 	extern char Version[];
11211151Seric 	extern char *macvalue();
11312612Seric 	extern ADDRESS *recipient();
11424943Seric 	extern ENVELOPE BlankEnvelope;
11524943Seric 	extern ENVELOPE *newenvelope();
1164549Seric 
1175003Seric 	hasmail = FALSE;
1187363Seric 	if (OutChannel != stdout)
1197363Seric 	{
1207363Seric 		/* arrange for debugging output to go to remote host */
1217363Seric 		(void) close(1);
1227363Seric 		(void) dup(fileno(OutChannel));
1237363Seric 	}
12411931Seric 	settime();
12524971Seric 	if (RealHostName != NULL)
12625050Seric 	{
12725050Seric 		CurHostName = RealHostName;
12825050Seric 		setproctitle("srvrsmtp %s", CurHostName);
12925050Seric 	}
13025050Seric 	else
13125050Seric 	{
13225050Seric 		/* this must be us!! */
13325050Seric 		CurHostName = MyHostName;
13425050Seric 	}
13516153Seric 	expand("\001e", inp, &inp[sizeof inp], CurEnv);
13610708Seric 	message("220", inp);
13724943Seric 	SmtpPhase = "startup";
13830448Seric 	sendinghost = NULL;
1394549Seric 	for (;;)
1404549Seric 	{
14112612Seric 		/* arrange for backout */
14212612Seric 		if (setjmp(TopFrame) > 0 && InChild)
14312612Seric 			finis();
14412612Seric 		QuickAbort = FALSE;
14512612Seric 		HoldErrs = FALSE;
14612612Seric 
1477356Seric 		/* setup for the read */
1486907Seric 		CurEnv->e_to = NULL;
1494577Seric 		Errors = 0;
1507275Seric 		(void) fflush(stdout);
1517356Seric 
1527356Seric 		/* read the input line */
1537685Seric 		p = sfgets(inp, sizeof inp, InChannel);
1547356Seric 
1557685Seric 		/* handle errors */
1567356Seric 		if (p == NULL)
1577356Seric 		{
1584549Seric 			/* end of file, just die */
15936230Skarels 			message("421", "%s Lost input channel from %s",
16025050Seric 				MyHostName, CurHostName);
1614549Seric 			finis();
1624549Seric 		}
1634549Seric 
1644549Seric 		/* clean up end of line */
1654558Seric 		fixcrlf(inp, TRUE);
1664549Seric 
1674713Seric 		/* echo command to transcript */
1689545Seric 		if (CurEnv->e_xfp != NULL)
1699545Seric 			fprintf(CurEnv->e_xfp, "<<< %s\n", inp);
1704713Seric 
1714549Seric 		/* break off command */
1724549Seric 		for (p = inp; isspace(*p); p++)
1734549Seric 			continue;
1744549Seric 		cmd = p;
17524981Seric 		for (cmd = cmdbuf; *p != '\0' && !isspace(*p); )
17624981Seric 			*cmd++ = *p++;
17724981Seric 		*cmd = '\0';
1784549Seric 
17925691Seric 		/* throw away leading whitespace */
18025691Seric 		while (isspace(*p))
18125691Seric 			p++;
18225691Seric 
1834549Seric 		/* decode command */
1844549Seric 		for (c = CmdTab; c->cmdname != NULL; c++)
1854549Seric 		{
18633725Sbostic 			if (!strcasecmp(c->cmdname, cmdbuf))
1874549Seric 				break;
1884549Seric 		}
1894549Seric 
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 				 */
20236230Skarels 				message("553", "Local configuration error, hostname not recognized as local");
20314877Seric 				break;
20414877Seric 			}
20533725Sbostic 			if (RealHostName != NULL && strcasecmp(p, RealHostName))
20611146Seric 			{
20724981Seric 				char hostbuf[MAXNAME];
20811146Seric 
20924981Seric 				(void) sprintf(hostbuf, "%s (%s)", p, RealHostName);
21030448Seric 				sendinghost = newstr(hostbuf);
21111146Seric 			}
21211146Seric 			else
21330448Seric 				sendinghost = newstr(p);
2144997Seric 			message("250", "%s Hello %s, pleased to meet you",
21536230Skarels 				MyHostName, sendinghost);
2164976Seric 			break;
2174976Seric 
2184549Seric 		  case CMDMAIL:		/* mail -- designate sender */
21924943Seric 			SmtpPhase = "MAIL";
22024943Seric 
22111151Seric 			/* force a sending host even if no HELO given */
22211151Seric 			if (RealHostName != NULL && macvalue('s', CurEnv) == NULL)
22330448Seric 				sendinghost = RealHostName;
22411151Seric 
2259314Seric 			/* check for validity of this command */
2264558Seric 			if (hasmail)
2274558Seric 			{
2284558Seric 				message("503", "Sender already specified");
2294558Seric 				break;
2304558Seric 			}
2319339Seric 			if (InChild)
2329339Seric 			{
23336230Skarels 				errno = 0;
2349339Seric 				syserr("Nested MAIL command");
2359339Seric 				exit(0);
2369339Seric 			}
2379339Seric 
2389339Seric 			/* fork a subprocess to process this command */
2399339Seric 			if (runinchild("SMTP-MAIL") > 0)
2409339Seric 				break;
24130448Seric 			define('s', sendinghost, CurEnv);
242*36579Sbostic 			define('r', "SMTP", CurEnv);
2439339Seric 			initsys();
24425016Seric 			setproctitle("%s %s: %s", CurEnv->e_id,
24525050Seric 				CurHostName, inp);
2469339Seric 
2479339Seric 			/* child -- go do the processing */
2484549Seric 			p = skipword(p, "from");
2494549Seric 			if (p == NULL)
2504549Seric 				break;
2514549Seric 			setsender(p);
2524577Seric 			if (Errors == 0)
2534549Seric 			{
2544549Seric 				message("250", "Sender ok");
2554549Seric 				hasmail = TRUE;
2564549Seric 			}
2579339Seric 			else if (InChild)
2589339Seric 				finis();
2594549Seric 			break;
2604549Seric 
2614976Seric 		  case CMDRCPT:		/* rcpt -- designate recipient */
26224943Seric 			SmtpPhase = "RCPT";
26325016Seric 			setproctitle("%s %s: %s", CurEnv->e_id,
26425050Seric 				CurHostName, inp);
26512612Seric 			if (setjmp(TopFrame) > 0)
26614785Seric 			{
26714785Seric 				CurEnv->e_flags &= ~EF_FATALERRS;
26812612Seric 				break;
26914785Seric 			}
27012612Seric 			QuickAbort = TRUE;
2714549Seric 			p = skipword(p, "to");
2724549Seric 			if (p == NULL)
2734549Seric 				break;
27416140Seric 			a = parseaddr(p, (ADDRESS *) NULL, 1, '\0');
27512612Seric 			if (a == NULL)
27612612Seric 				break;
27716886Seric 			a->q_flags |= QPRIMARY;
27812612Seric 			a = recipient(a, &CurEnv->e_sendqueue);
27912612Seric 			if (Errors != 0)
28012612Seric 				break;
28112612Seric 
28212612Seric 			/* no errors during parsing, but might be a duplicate */
28312612Seric 			CurEnv->e_to = p;
28412612Seric 			if (!bitset(QBADADDR, a->q_flags))
28512612Seric 				message("250", "Recipient ok");
28612612Seric 			else
2874549Seric 			{
28812612Seric 				/* punt -- should keep message in ADDRESS.... */
28912612Seric 				message("550", "Addressee unknown");
2904549Seric 			}
29112612Seric 			CurEnv->e_to = NULL;
2924549Seric 			break;
2934549Seric 
2944549Seric 		  case CMDDATA:		/* data -- text of mail */
29524943Seric 			SmtpPhase = "DATA";
2964976Seric 			if (!hasmail)
2974549Seric 			{
2984976Seric 				message("503", "Need MAIL command");
2994976Seric 				break;
3004549Seric 			}
30124943Seric 			else if (CurEnv->e_nrcpts <= 0)
3024549Seric 			{
3034976Seric 				message("503", "Need RCPT (recipient)");
3044976Seric 				break;
3054549Seric 			}
3064976Seric 
3074976Seric 			/* collect the text of the message */
30824943Seric 			SmtpPhase = "collect";
30925016Seric 			setproctitle("%s %s: %s", CurEnv->e_id,
31025050Seric 				CurHostName, inp);
3114976Seric 			collect(TRUE);
3124976Seric 			if (Errors != 0)
3134976Seric 				break;
3144976Seric 
3158238Seric 			/*
3168238Seric 			**  Arrange to send to everyone.
3178238Seric 			**	If sending to multiple people, mail back
3188238Seric 			**		errors rather than reporting directly.
3198238Seric 			**	In any case, don't mail back errors for
3208238Seric 			**		anything that has happened up to
3218238Seric 			**		now (the other end will do this).
32210197Seric 			**	Truncate our transcript -- the mail has gotten
32310197Seric 			**		to us successfully, and if we have
32410197Seric 			**		to mail this back, it will be easier
32510197Seric 			**		on the reader.
3268238Seric 			**	Then send to everyone.
3278238Seric 			**	Finally give a reply code.  If an error has
3288238Seric 			**		already been given, don't mail a
3298238Seric 			**		message back.
3309339Seric 			**	We goose error returns by clearing error bit.
3318238Seric 			*/
3328238Seric 
33324943Seric 			SmtpPhase = "delivery";
33424943Seric 			if (CurEnv->e_nrcpts != 1)
3359378Seric 			{
3369378Seric 				HoldErrs = TRUE;
33716886Seric 				ErrorMode = EM_MAIL;
3389378Seric 			}
3399339Seric 			CurEnv->e_flags &= ~EF_FATALERRS;
34010197Seric 			CurEnv->e_xfp = freopen(queuename(CurEnv, 'x'), "w", CurEnv->e_xfp);
3414976Seric 
3424976Seric 			/* send to all recipients */
34314877Seric 			sendall(CurEnv, SM_DEFAULT);
3446907Seric 			CurEnv->e_to = NULL;
3454976Seric 
34623516Seric 			/* save statistics */
34723516Seric 			markstats(CurEnv, (ADDRESS *) NULL);
34823516Seric 
3498238Seric 			/* issue success if appropriate and reset */
3508238Seric 			if (Errors == 0 || HoldErrs)
3519283Seric 				message("250", "Ok");
3528238Seric 			else
3539339Seric 				CurEnv->e_flags &= ~EF_FATALERRS;
3549339Seric 
3559339Seric 			/* if in a child, pop back to our parent */
3569339Seric 			if (InChild)
3579339Seric 				finis();
35824943Seric 
35924943Seric 			/* clean up a bit */
36024943Seric 			hasmail = 0;
36124943Seric 			dropenvelope(CurEnv);
36224943Seric 			CurEnv = newenvelope(CurEnv);
36324943Seric 			CurEnv->e_flags = BlankEnvelope.e_flags;
3644549Seric 			break;
3654549Seric 
3664549Seric 		  case CMDRSET:		/* rset -- reset state */
3674549Seric 			message("250", "Reset state");
3689339Seric 			if (InChild)
3699339Seric 				finis();
3709339Seric 			break;
3714549Seric 
3724549Seric 		  case CMDVRFY:		/* vrfy -- verify address */
3739339Seric 			if (runinchild("SMTP-VRFY") > 0)
3749339Seric 				break;
37525050Seric 			setproctitle("%s: %s", CurHostName, inp);
3765003Seric 			vrfyqueue = NULL;
3777762Seric 			QuickAbort = TRUE;
3789619Seric 			sendtolist(p, (ADDRESS *) NULL, &vrfyqueue);
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";
3995003Seric 					if (vrfyqueue->q_fullname == NULL)
4005003Seric 						message(code, "<%s>", vrfyqueue->q_paddr);
4015003Seric 					else
4025003Seric 						message(code, "%s <%s>",
4035003Seric 						    vrfyqueue->q_fullname, vrfyqueue->q_paddr);
4045003Seric 				}
4055003Seric 				else if (a == NULL)
4065003Seric 					message("554", "Self destructive alias loop");
4075003Seric 				vrfyqueue = a;
4085003Seric 			}
4099339Seric 			if (InChild)
4109339Seric 				finis();
4114549Seric 			break;
4124549Seric 
4134549Seric 		  case CMDHELP:		/* help -- give user info */
4144577Seric 			if (*p == '\0')
4154577Seric 				p = "SMTP";
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=");
4436907Seric 			printaddr(CurEnv->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 
5554577Seric 	len = strlen(topic);
5564577Seric 	makelower(topic);
5574577Seric 	noinfo = TRUE;
5584577Seric 
5594577Seric 	while (fgets(buf, sizeof buf, hf) != NULL)
5604577Seric 	{
5614577Seric 		if (strncmp(buf, topic, len) == 0)
5624577Seric 		{
5634577Seric 			register char *p;
5644577Seric 
5654577Seric 			p = index(buf, '\t');
5664577Seric 			if (p == NULL)
5674577Seric 				p = buf;
5684577Seric 			else
5694577Seric 				p++;
5704577Seric 			fixcrlf(p, TRUE);
5714577Seric 			message("214-", p);
5724577Seric 			noinfo = FALSE;
5734577Seric 		}
5744577Seric 	}
5754577Seric 
5764577Seric 	if (noinfo)
5774577Seric 		message("504", "HELP topic unknown");
5784577Seric 	else
5794577Seric 		message("214", "End of HELP info");
5804628Seric 	(void) fclose(hf);
5814577Seric }
5828544Seric /*
5839339Seric **  RUNINCHILD -- return twice -- once in the child, then in the parent again
5849339Seric **
5859339Seric **	Parameters:
5869339Seric **		label -- a string used in error messages
5879339Seric **
5889339Seric **	Returns:
5899339Seric **		zero in the child
5909339Seric **		one in the parent
5919339Seric **
5929339Seric **	Side Effects:
5939339Seric **		none.
5949339Seric */
5958544Seric 
5969339Seric runinchild(label)
5979339Seric 	char *label;
5989339Seric {
5999339Seric 	int childpid;
6009339Seric 
60116158Seric 	if (!OneXact)
6029339Seric 	{
60316158Seric 		childpid = dofork();
60416158Seric 		if (childpid < 0)
60516158Seric 		{
60616158Seric 			syserr("%s: cannot fork", label);
60716158Seric 			return (1);
60816158Seric 		}
60916158Seric 		if (childpid > 0)
61016158Seric 		{
61116158Seric 			auto int st;
6129339Seric 
61316158Seric 			/* parent -- wait for child to complete */
61416158Seric 			st = waitfor(childpid);
61516158Seric 			if (st == -1)
61616158Seric 				syserr("%s: lost child", label);
6179339Seric 
61816158Seric 			/* if we exited on a QUIT command, complete the process */
61916158Seric 			if (st == (EX_QUIT << 8))
62016158Seric 				finis();
6219339Seric 
62216158Seric 			return (1);
62316158Seric 		}
62416158Seric 		else
62516158Seric 		{
62616158Seric 			/* child */
62716158Seric 			InChild = TRUE;
62825050Seric 			QuickAbort = FALSE;
62925614Seric 			clearenvelope(CurEnv, FALSE);
63016158Seric 		}
6319339Seric 	}
63215256Seric 
63316158Seric 	/* open alias database */
63416158Seric 	initaliases(AliasFile, FALSE);
63516158Seric 
63616158Seric 	return (0);
6379339Seric }
6389339Seric 
6395181Seric # endif SMTP
640