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*58082Seric static char sccsid[] = "@(#)srvrsmtp.c	6.11 (Berkeley) 02/20/93 (with SMTP)";
1433731Sbostic #else
15*58082Seric static char sccsid[] = "@(#)srvrsmtp.c	6.11 (Berkeley) 02/20/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;
100*58082Seric 	bool gothello;
1018544Seric 	char inp[MAXLINE];
10257232Seric 	char cmdbuf[MAXLINE];
1037124Seric 	extern char Version[];
10411151Seric 	extern char *macvalue();
10512612Seric 	extern ADDRESS *recipient();
10624943Seric 	extern ENVELOPE BlankEnvelope;
10724943Seric 	extern ENVELOPE *newenvelope();
1084549Seric 
1095003Seric 	hasmail = FALSE;
1107363Seric 	if (OutChannel != stdout)
1117363Seric 	{
1127363Seric 		/* arrange for debugging output to go to remote host */
1137363Seric 		(void) close(1);
1147363Seric 		(void) dup(fileno(OutChannel));
1157363Seric 	}
11655012Seric 	settime(e);
11757642Seric 	if (RealHostName == NULL)
11857642Seric 		RealHostName = MyHostName;
11957642Seric 	CurHostName = RealHostName;
12057642Seric 	setproctitle("srvrsmtp %s", CurHostName);
12158050Seric 	expand("\201e", inp, &inp[sizeof inp], e);
12255360Seric 	message("220", "%s", inp);
12324943Seric 	SmtpPhase = "startup";
12430448Seric 	sendinghost = NULL;
125*58082Seric 	gothello = FALSE;
1264549Seric 	for (;;)
1274549Seric 	{
12812612Seric 		/* arrange for backout */
12912612Seric 		if (setjmp(TopFrame) > 0 && InChild)
13012612Seric 			finis();
13112612Seric 		QuickAbort = FALSE;
13212612Seric 		HoldErrs = FALSE;
13351951Seric 		LogUsrErrs = FALSE;
13412612Seric 
1357356Seric 		/* setup for the read */
13655012Seric 		e->e_to = NULL;
1374577Seric 		Errors = 0;
1387275Seric 		(void) fflush(stdout);
1397356Seric 
1407356Seric 		/* read the input line */
14157389Seric 		p = sfgets(inp, sizeof inp, InChannel, ReadTimeout);
1427356Seric 
1437685Seric 		/* handle errors */
1447356Seric 		if (p == NULL)
1457356Seric 		{
1464549Seric 			/* end of file, just die */
14736230Skarels 			message("421", "%s Lost input channel from %s",
14825050Seric 				MyHostName, CurHostName);
14955464Seric #ifdef LOG
15058020Seric 			if (LogLevel > 1)
15155464Seric 				syslog(LOG_NOTICE, "lost input channel from %s",
15255464Seric 					CurHostName);
15355464Seric #endif
15458069Seric 			if (InChild)
15558069Seric 				ExitStat = EX_QUIT;
1564549Seric 			finis();
1574549Seric 		}
1584549Seric 
1594549Seric 		/* clean up end of line */
1604558Seric 		fixcrlf(inp, TRUE);
1614549Seric 
1624713Seric 		/* echo command to transcript */
16355012Seric 		if (e->e_xfp != NULL)
16455012Seric 			fprintf(e->e_xfp, "<<< %s\n", inp);
1654713Seric 
1664549Seric 		/* break off command */
16758050Seric 		for (p = inp; isascii(*p) && isspace(*p); p++)
1684549Seric 			continue;
16957232Seric 		cmd = cmdbuf;
17058050Seric 		while (*p != '\0' &&
17158050Seric 		       !(isascii(*p) && isspace(*p)) &&
17258050Seric 		       cmd < &cmdbuf[sizeof cmdbuf - 2])
17324981Seric 			*cmd++ = *p++;
17424981Seric 		*cmd = '\0';
1754549Seric 
17625691Seric 		/* throw away leading whitespace */
17758050Seric 		while (isascii(*p) && 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);
217*58082Seric 			gothello = TRUE;
2184976Seric 			break;
2194976Seric 
2204549Seric 		  case CMDMAIL:		/* mail -- designate sender */
22124943Seric 			SmtpPhase = "MAIL";
22224943Seric 
22311151Seric 			/* force a sending host even if no HELO given */
22458064Seric 			if (sendinghost == NULL && macvalue('s', e) == NULL)
22530448Seric 				sendinghost = RealHostName;
22611151Seric 
2279314Seric 			/* check for validity of this command */
228*58082Seric 			if (!gothello && bitset(PRIV_NEEDMAILHELO, PrivacyFlags))
229*58082Seric 			{
230*58082Seric 				message("503", "Polite people say HELO first");
231*58082Seric 				break;
232*58082Seric 			}
2334558Seric 			if (hasmail)
2344558Seric 			{
2354558Seric 				message("503", "Sender already specified");
2364558Seric 				break;
2374558Seric 			}
2389339Seric 			if (InChild)
2399339Seric 			{
24036230Skarels 				errno = 0;
24158008Seric 				syserr("Nested MAIL command: MAIL %s", p);
24258069Seric 				finis();
2439339Seric 			}
244*58082Seric 			if (!enoughspace())
245*58082Seric 			{
246*58082Seric 				message("452", "Insufficient disk space; try again later");
247*58082Seric 				break;
248*58082Seric 			}
2499339Seric 
2509339Seric 			/* fork a subprocess to process this command */
25155012Seric 			if (runinchild("SMTP-MAIL", e) > 0)
2529339Seric 				break;
25358064Seric 			if (sendinghost != NULL)
25458064Seric 				define('s', sendinghost, e);
25555012Seric 			define('r', "SMTP", e);
25655012Seric 			initsys(e);
25757389Seric 			setproctitle("%s %s: %s", e->e_id, CurHostName, inp);
2589339Seric 
2599339Seric 			/* child -- go do the processing */
2604549Seric 			p = skipword(p, "from");
2614549Seric 			if (p == NULL)
2624549Seric 				break;
26357977Seric 			if (setjmp(TopFrame) > 0)
26457977Seric 				break;
26557977Seric 			QuickAbort = TRUE;
26655012Seric 			setsender(p, e);
2674577Seric 			if (Errors == 0)
2684549Seric 			{
2694549Seric 				message("250", "Sender ok");
2704549Seric 				hasmail = TRUE;
2714549Seric 			}
2729339Seric 			else if (InChild)
2739339Seric 				finis();
2744549Seric 			break;
2754549Seric 
2764976Seric 		  case CMDRCPT:		/* rcpt -- designate recipient */
27724943Seric 			SmtpPhase = "RCPT";
27857389Seric 			setproctitle("%s %s: %s", e->e_id, CurHostName, inp);
27912612Seric 			if (setjmp(TopFrame) > 0)
28014785Seric 			{
28155012Seric 				e->e_flags &= ~EF_FATALERRS;
28212612Seric 				break;
28314785Seric 			}
28412612Seric 			QuickAbort = TRUE;
28551951Seric 			LogUsrErrs = TRUE;
2864549Seric 			p = skipword(p, "to");
2874549Seric 			if (p == NULL)
2884549Seric 				break;
28955012Seric 			a = parseaddr(p, (ADDRESS *) NULL, 1, '\0', e);
29012612Seric 			if (a == NULL)
29112612Seric 				break;
29216886Seric 			a->q_flags |= QPRIMARY;
29355012Seric 			a = recipient(a, &e->e_sendqueue, e);
29412612Seric 			if (Errors != 0)
29512612Seric 				break;
29612612Seric 
29712612Seric 			/* no errors during parsing, but might be a duplicate */
29855012Seric 			e->e_to = p;
29912612Seric 			if (!bitset(QBADADDR, a->q_flags))
30012612Seric 				message("250", "Recipient ok");
30112612Seric 			else
3024549Seric 			{
30312612Seric 				/* punt -- should keep message in ADDRESS.... */
30412612Seric 				message("550", "Addressee unknown");
3054549Seric 			}
30655012Seric 			e->e_to = NULL;
3074549Seric 			break;
3084549Seric 
3094549Seric 		  case CMDDATA:		/* data -- text of mail */
31024943Seric 			SmtpPhase = "DATA";
3114976Seric 			if (!hasmail)
3124549Seric 			{
3134976Seric 				message("503", "Need MAIL command");
3144976Seric 				break;
3154549Seric 			}
31655012Seric 			else if (e->e_nrcpts <= 0)
3174549Seric 			{
3184976Seric 				message("503", "Need RCPT (recipient)");
3194976Seric 				break;
3204549Seric 			}
3214976Seric 
3224976Seric 			/* collect the text of the message */
32324943Seric 			SmtpPhase = "collect";
32457389Seric 			setproctitle("%s %s: %s", e->e_id, CurHostName, inp);
32555012Seric 			collect(TRUE, e);
3264976Seric 			if (Errors != 0)
3274976Seric 				break;
3284976Seric 
3298238Seric 			/*
3308238Seric 			**  Arrange to send to everyone.
3318238Seric 			**	If sending to multiple people, mail back
3328238Seric 			**		errors rather than reporting directly.
3338238Seric 			**	In any case, don't mail back errors for
3348238Seric 			**		anything that has happened up to
3358238Seric 			**		now (the other end will do this).
33610197Seric 			**	Truncate our transcript -- the mail has gotten
33710197Seric 			**		to us successfully, and if we have
33810197Seric 			**		to mail this back, it will be easier
33910197Seric 			**		on the reader.
3408238Seric 			**	Then send to everyone.
3418238Seric 			**	Finally give a reply code.  If an error has
3428238Seric 			**		already been given, don't mail a
3438238Seric 			**		message back.
3449339Seric 			**	We goose error returns by clearing error bit.
3458238Seric 			*/
3468238Seric 
34724943Seric 			SmtpPhase = "delivery";
34855012Seric 			if (e->e_nrcpts != 1)
3499378Seric 			{
3509378Seric 				HoldErrs = TRUE;
35116886Seric 				ErrorMode = EM_MAIL;
3529378Seric 			}
35355012Seric 			e->e_flags &= ~EF_FATALERRS;
35455012Seric 			e->e_xfp = freopen(queuename(e, 'x'), "w", e->e_xfp);
3554976Seric 
3564976Seric 			/* send to all recipients */
35755012Seric 			sendall(e, SM_DEFAULT);
35855012Seric 			e->e_to = NULL;
3594976Seric 
36023516Seric 			/* save statistics */
36155012Seric 			markstats(e, (ADDRESS *) NULL);
36223516Seric 
3638238Seric 			/* issue success if appropriate and reset */
3648238Seric 			if (Errors == 0 || HoldErrs)
3659283Seric 				message("250", "Ok");
3668238Seric 			else
36755012Seric 				e->e_flags &= ~EF_FATALERRS;
3689339Seric 
3699339Seric 			/* if in a child, pop back to our parent */
3709339Seric 			if (InChild)
3719339Seric 				finis();
37224943Seric 
37324943Seric 			/* clean up a bit */
37458008Seric 			hasmail = FALSE;
37555012Seric 			dropenvelope(e);
37655012Seric 			CurEnv = e = newenvelope(e);
37755012Seric 			e->e_flags = BlankEnvelope.e_flags;
3784549Seric 			break;
3794549Seric 
3804549Seric 		  case CMDRSET:		/* rset -- reset state */
3814549Seric 			message("250", "Reset state");
3829339Seric 			if (InChild)
3839339Seric 				finis();
3849339Seric 			break;
3854549Seric 
3864549Seric 		  case CMDVRFY:		/* vrfy -- verify address */
387*58082Seric 			if (bitset(PRIV_NOVRFY|PRIV_NOEXPN, PrivacyFlags))
388*58082Seric 			{
389*58082Seric 				message("502", "That's none of your business");
390*58082Seric 				break;
391*58082Seric 			}
392*58082Seric 			else if (!gothello &&
393*58082Seric 				 bitset(PRIV_NEEDVRFYHELO|PRIV_NEEDEXPNHELO, PrivacyFlags))
394*58082Seric 			{
395*58082Seric 				message("503", "I demand that you introduce yourself first");
396*58082Seric 				break;
397*58082Seric 			}
39855012Seric 			if (runinchild("SMTP-VRFY", e) > 0)
3999339Seric 				break;
40025050Seric 			setproctitle("%s: %s", CurHostName, inp);
40155173Seric #ifdef LOG
40258020Seric 			if (LogLevel > 5)
40355173Seric 				syslog(LOG_INFO, "%s: %s", CurHostName, inp);
40455173Seric #endif
4055003Seric 			vrfyqueue = NULL;
4067762Seric 			QuickAbort = TRUE;
407*58082Seric 			(void) sendtolist(p, (ADDRESS *) NULL, &vrfyqueue, e);
4087762Seric 			if (Errors != 0)
4099339Seric 			{
4109339Seric 				if (InChild)
4119339Seric 					finis();
4127762Seric 				break;
4139339Seric 			}
4145003Seric 			while (vrfyqueue != NULL)
4155003Seric 			{
4165003Seric 				register ADDRESS *a = vrfyqueue->q_next;
4175003Seric 				char *code;
4185003Seric 
4197685Seric 				while (a != NULL && bitset(QDONTSEND|QBADADDR, a->q_flags))
4205003Seric 					a = a->q_next;
4215003Seric 
4227685Seric 				if (!bitset(QDONTSEND|QBADADDR, vrfyqueue->q_flags))
4235003Seric 				{
4245003Seric 					if (a != NULL)
4255003Seric 						code = "250-";
4265003Seric 					else
4275003Seric 						code = "250";
42858010Seric 					if (strchr(vrfyqueue->q_paddr, '<') != NULL)
42958010Seric 						message(code, "%s", vrfyqueue->q_paddr);
43058010Seric 					else if (vrfyqueue->q_fullname == NULL)
4315003Seric 						message(code, "<%s>", vrfyqueue->q_paddr);
4325003Seric 					else
4335003Seric 						message(code, "%s <%s>",
4345003Seric 						    vrfyqueue->q_fullname, vrfyqueue->q_paddr);
4355003Seric 				}
4365003Seric 				else if (a == NULL)
4375003Seric 					message("554", "Self destructive alias loop");
4385003Seric 				vrfyqueue = a;
4395003Seric 			}
4409339Seric 			if (InChild)
4419339Seric 				finis();
4424549Seric 			break;
4434549Seric 
4444549Seric 		  case CMDHELP:		/* help -- give user info */
4454577Seric 			help(p);
4464549Seric 			break;
4474549Seric 
4484549Seric 		  case CMDNOOP:		/* noop -- do nothing */
4494549Seric 			message("200", "OK");
4504549Seric 			break;
4514549Seric 
4524549Seric 		  case CMDQUIT:		/* quit -- leave mail */
45325050Seric 			message("221", "%s closing connection", MyHostName);
4549339Seric 			if (InChild)
4559339Seric 				ExitStat = EX_QUIT;
4564549Seric 			finis();
4574549Seric 
4588544Seric 		  case CMDVERB:		/* set verbose mode */
4598544Seric 			Verbose = TRUE;
46025025Seric 			SendMode = SM_DELIVER;
4618544Seric 			message("200", "Verbose mode");
4628544Seric 			break;
4638544Seric 
4649314Seric 		  case CMDONEX:		/* doing one transaction only */
4659378Seric 			OneXact = TRUE;
4669314Seric 			message("200", "Only one transaction");
4679314Seric 			break;
4689314Seric 
46936230Skarels # ifdef SMTPDEBUG
4709339Seric 		  case CMDDBGQSHOW:	/* show queues */
4716907Seric 			printf("Send Queue=");
47255012Seric 			printaddr(e->e_sendqueue, TRUE);
4735003Seric 			break;
4747275Seric 
4757275Seric 		  case CMDDBGDEBUG:	/* set debug mode */
4767676Seric 			tTsetup(tTdvect, sizeof tTdvect, "0-99.1");
4777676Seric 			tTflag(p);
4787676Seric 			message("200", "Debug set");
4797275Seric 			break;
4807275Seric 
48136230Skarels # else /* not SMTPDEBUG */
48224945Seric 
48336230Skarels 		  case CMDDBGQSHOW:	/* show queues */
48436230Skarels 		  case CMDDBGDEBUG:	/* set debug mode */
48536233Skarels # ifdef LOG
48636233Skarels 			if (RealHostName != NULL && LogLevel > 0)
48736230Skarels 				syslog(LOG_NOTICE,
48858020Seric 				    "\"%s\" command from %s (%s)",
48936230Skarels 				    c->cmdname, RealHostName,
49036230Skarels 				    inet_ntoa(RealHostAddr.sin_addr));
49136233Skarels # endif
49236230Skarels 			/* FALL THROUGH */
49336230Skarels # endif /* SMTPDEBUG */
49436230Skarels 
4954549Seric 		  case CMDERROR:	/* unknown command */
4964549Seric 			message("500", "Command unrecognized");
4974549Seric 			break;
4984549Seric 
4994549Seric 		  default:
50036230Skarels 			errno = 0;
5014549Seric 			syserr("smtp: unknown code %d", c->cmdcode);
5024549Seric 			break;
5034549Seric 		}
5044549Seric 	}
5054549Seric }
5064549Seric /*
5074549Seric **  SKIPWORD -- skip a fixed word.
5084549Seric **
5094549Seric **	Parameters:
5104549Seric **		p -- place to start looking.
5114549Seric **		w -- word to skip.
5124549Seric **
5134549Seric **	Returns:
5144549Seric **		p following w.
5154549Seric **		NULL on error.
5164549Seric **
5174549Seric **	Side Effects:
5184549Seric **		clobbers the p data area.
5194549Seric */
5204549Seric 
5214549Seric static char *
5224549Seric skipword(p, w)
5234549Seric 	register char *p;
5244549Seric 	char *w;
5254549Seric {
5264549Seric 	register char *q;
5274549Seric 
5284549Seric 	/* find beginning of word */
52958050Seric 	while (isascii(*p) && isspace(*p))
5304549Seric 		p++;
5314549Seric 	q = p;
5324549Seric 
5334549Seric 	/* find end of word */
53458050Seric 	while (*p != '\0' && *p != ':' && !(isascii(*p) && isspace(*p)))
5354549Seric 		p++;
53658050Seric 	while (isascii(*p) && isspace(*p))
5374549Seric 		*p++ = '\0';
5384549Seric 	if (*p != ':')
5394549Seric 	{
5404549Seric 	  syntax:
5414549Seric 		message("501", "Syntax error");
5424549Seric 		Errors++;
5434549Seric 		return (NULL);
5444549Seric 	}
5454549Seric 	*p++ = '\0';
54658050Seric 	while (isascii(*p) && isspace(*p))
5474549Seric 		p++;
5484549Seric 
5494549Seric 	/* see if the input word matches desired word */
55033725Sbostic 	if (strcasecmp(q, w))
5514549Seric 		goto syntax;
5524549Seric 
5534549Seric 	return (p);
5544549Seric }
5554577Seric /*
5564577Seric **  HELP -- implement the HELP command.
5574577Seric **
5584577Seric **	Parameters:
5594577Seric **		topic -- the topic we want help for.
5604577Seric **
5614577Seric **	Returns:
5624577Seric **		none.
5634577Seric **
5644577Seric **	Side Effects:
5654577Seric **		outputs the help file to message output.
5664577Seric */
5674577Seric 
5684577Seric help(topic)
5694577Seric 	char *topic;
5704577Seric {
5714577Seric 	register FILE *hf;
5724577Seric 	int len;
5734577Seric 	char buf[MAXLINE];
5744577Seric 	bool noinfo;
5754577Seric 
5768269Seric 	if (HelpFile == NULL || (hf = fopen(HelpFile, "r")) == NULL)
5774577Seric 	{
5784577Seric 		/* no help */
57911931Seric 		errno = 0;
5804577Seric 		message("502", "HELP not implemented");
5814577Seric 		return;
5824577Seric 	}
5834577Seric 
58449669Seric 	if (topic == NULL || *topic == '\0')
58549669Seric 		topic = "smtp";
58649669Seric 	else
58749669Seric 		makelower(topic);
58849669Seric 
5894577Seric 	len = strlen(topic);
5904577Seric 	noinfo = TRUE;
5914577Seric 
5924577Seric 	while (fgets(buf, sizeof buf, hf) != NULL)
5934577Seric 	{
5944577Seric 		if (strncmp(buf, topic, len) == 0)
5954577Seric 		{
5964577Seric 			register char *p;
5974577Seric 
59856795Seric 			p = strchr(buf, '\t');
5994577Seric 			if (p == NULL)
6004577Seric 				p = buf;
6014577Seric 			else
6024577Seric 				p++;
6034577Seric 			fixcrlf(p, TRUE);
6044577Seric 			message("214-", p);
6054577Seric 			noinfo = FALSE;
6064577Seric 		}
6074577Seric 	}
6084577Seric 
6094577Seric 	if (noinfo)
6104577Seric 		message("504", "HELP topic unknown");
6114577Seric 	else
6124577Seric 		message("214", "End of HELP info");
6134628Seric 	(void) fclose(hf);
6144577Seric }
6158544Seric /*
6169339Seric **  RUNINCHILD -- return twice -- once in the child, then in the parent again
6179339Seric **
6189339Seric **	Parameters:
6199339Seric **		label -- a string used in error messages
6209339Seric **
6219339Seric **	Returns:
6229339Seric **		zero in the child
6239339Seric **		one in the parent
6249339Seric **
6259339Seric **	Side Effects:
6269339Seric **		none.
6279339Seric */
6288544Seric 
62955012Seric runinchild(label, e)
6309339Seric 	char *label;
63155012Seric 	register ENVELOPE *e;
6329339Seric {
6339339Seric 	int childpid;
6349339Seric 
63516158Seric 	if (!OneXact)
6369339Seric 	{
63716158Seric 		childpid = dofork();
63816158Seric 		if (childpid < 0)
63916158Seric 		{
64016158Seric 			syserr("%s: cannot fork", label);
64116158Seric 			return (1);
64216158Seric 		}
64316158Seric 		if (childpid > 0)
64416158Seric 		{
64516158Seric 			auto int st;
6469339Seric 
64716158Seric 			/* parent -- wait for child to complete */
64816158Seric 			st = waitfor(childpid);
64916158Seric 			if (st == -1)
65016158Seric 				syserr("%s: lost child", label);
6519339Seric 
65216158Seric 			/* if we exited on a QUIT command, complete the process */
65316158Seric 			if (st == (EX_QUIT << 8))
65416158Seric 				finis();
6559339Seric 
65616158Seric 			return (1);
65716158Seric 		}
65816158Seric 		else
65916158Seric 		{
66016158Seric 			/* child */
66116158Seric 			InChild = TRUE;
66225050Seric 			QuickAbort = FALSE;
66355012Seric 			clearenvelope(e, FALSE);
66416158Seric 		}
6659339Seric 	}
66615256Seric 
66716158Seric 	/* open alias database */
66855012Seric 	initaliases(AliasFile, FALSE, e);
66916158Seric 
67016158Seric 	return (0);
6719339Seric }
6729339Seric 
67356795Seric # endif /* SMTP */
674