122712Sdist /*
2*33731Sbostic  * Copyright (c) 1988 Regents of the University of California.
3*33731Sbostic  * All rights reserved.
4*33731Sbostic  *
5*33731Sbostic  * Redistribution and use in source and binary forms are permitted
6*33731Sbostic  * provided that this notice is preserved and that due credit is given
7*33731Sbostic  * to the University of California at Berkeley. The name of the University
8*33731Sbostic  * may not be used to endorse or promote products derived from this
9*33731Sbostic  * software without specific prior written permission. This software
10*33731Sbostic  * is provided ``as is'' without express or implied warranty.
11*33731Sbostic  *
12*33731Sbostic  *  Sendmail
13*33731Sbostic  *  Copyright (c) 1983  Eric P. Allman
14*33731Sbostic  *  Berkeley, California
15*33731Sbostic  */
1622712Sdist 
17*33731Sbostic # include "sendmail.h"
1822712Sdist 
19*33731Sbostic #ifndef lint
20*33731Sbostic #ifdef SMTP
21*33731Sbostic static char sccsid[] = "@(#)srvrsmtp.c	5.21 (Berkeley) 03/13/88 (with SMTP)";
22*33731Sbostic #else
23*33731Sbostic static char sccsid[] = "@(#)srvrsmtp.c	5.21 (Berkeley) 03/13/88 (without SMTP)";
24*33731Sbostic #endif
25*33731Sbostic #endif /* not lint */
26*33731Sbostic 
279339Seric # include <errno.h>
2811728Seric # include <signal.h>
294549Seric 
30*33731Sbostic # ifdef SMTP
314556Seric 
324549Seric /*
334549Seric **  SMTP -- run the SMTP protocol.
344549Seric **
354549Seric **	Parameters:
364549Seric **		none.
374549Seric **
384549Seric **	Returns:
394549Seric **		never.
404549Seric **
414549Seric **	Side Effects:
424549Seric **		Reads commands from the input channel and processes
434549Seric **			them.
444549Seric */
454549Seric 
464549Seric struct cmd
474549Seric {
484549Seric 	char	*cmdname;	/* command name */
494549Seric 	int	cmdcode;	/* internal code, see below */
504549Seric };
514549Seric 
524549Seric /* values for cmdcode */
534549Seric # define CMDERROR	0	/* bad command */
544549Seric # define CMDMAIL	1	/* mail -- designate sender */
554976Seric # define CMDRCPT	2	/* rcpt -- designate recipient */
564549Seric # define CMDDATA	3	/* data -- send message text */
579339Seric # define CMDRSET	4	/* rset -- reset state */
589339Seric # define CMDVRFY	5	/* vrfy -- verify address */
599339Seric # define CMDHELP	6	/* help -- give usage info */
609339Seric # define CMDNOOP	7	/* noop -- do nothing */
619339Seric # define CMDQUIT	8	/* quit -- close connection and die */
629339Seric # define CMDHELO	9	/* helo -- be polite */
639339Seric # define CMDDBGQSHOW	10	/* showq -- show send queue (DEBUG) */
649339Seric # define CMDDBGDEBUG	11	/* debug -- set debug mode */
659339Seric # define CMDVERB	12	/* verb -- go into verbose mode */
669339Seric # define CMDDBGKILL	13	/* kill -- kill sendmail */
679339Seric # define CMDDBGWIZ	14	/* wiz -- become a wizard */
689339Seric # define CMDONEX	15	/* onex -- sending one transaction only */
694549Seric 
704549Seric static struct cmd	CmdTab[] =
714549Seric {
724549Seric 	"mail",		CMDMAIL,
734976Seric 	"rcpt",		CMDRCPT,
744549Seric 	"data",		CMDDATA,
754549Seric 	"rset",		CMDRSET,
764549Seric 	"vrfy",		CMDVRFY,
777762Seric 	"expn",		CMDVRFY,
784549Seric 	"help",		CMDHELP,
794549Seric 	"noop",		CMDNOOP,
804549Seric 	"quit",		CMDQUIT,
814976Seric 	"helo",		CMDHELO,
828544Seric 	"verb",		CMDVERB,
839314Seric 	"onex",		CMDONEX,
845003Seric # ifdef DEBUG
859339Seric 	"showq",	CMDDBGQSHOW,
868544Seric 	"debug",	CMDDBGDEBUG,
8724945Seric # endif DEBUG
8824945Seric # ifdef WIZ
898544Seric 	"kill",		CMDDBGKILL,
9024945Seric # endif WIZ
918544Seric 	"wiz",		CMDDBGWIZ,
924549Seric 	NULL,		CMDERROR,
934549Seric };
944549Seric 
9524955Seric # ifdef WIZ
968544Seric bool	IsWiz = FALSE;			/* set if we are a wizard */
9724955Seric # endif WIZ
9815596Seric char	*WizWord;			/* the wizard word to compare against */
999339Seric bool	InChild = FALSE;		/* true if running in a subprocess */
1009378Seric bool	OneXact = FALSE;		/* one xaction only this run */
10111146Seric 
1029339Seric #define EX_QUIT		22		/* special code for QUIT command */
1038544Seric 
1044549Seric smtp()
1054549Seric {
1064549Seric 	register char *p;
1078544Seric 	register struct cmd *c;
1084549Seric 	char *cmd;
1094549Seric 	extern char *skipword();
1104549Seric 	bool hasmail;			/* mail command received */
1115003Seric 	auto ADDRESS *vrfyqueue;
11212612Seric 	ADDRESS *a;
11330448Seric 	char *sendinghost;
1148544Seric 	char inp[MAXLINE];
11524981Seric 	char cmdbuf[100];
1167124Seric 	extern char Version[];
1177356Seric 	extern tick();
1188544Seric 	extern bool iswiz();
1199349Seric 	extern char *arpadate();
12011151Seric 	extern char *macvalue();
12112612Seric 	extern ADDRESS *recipient();
12224943Seric 	extern ENVELOPE BlankEnvelope;
12324943Seric 	extern ENVELOPE *newenvelope();
1244549Seric 
1255003Seric 	hasmail = FALSE;
1267363Seric 	if (OutChannel != stdout)
1277363Seric 	{
1287363Seric 		/* arrange for debugging output to go to remote host */
1297363Seric 		(void) close(1);
1307363Seric 		(void) dup(fileno(OutChannel));
1317363Seric 	}
13211931Seric 	settime();
13324971Seric 	if (RealHostName != NULL)
13425050Seric 	{
13525050Seric 		CurHostName = RealHostName;
13625050Seric 		setproctitle("srvrsmtp %s", CurHostName);
13725050Seric 	}
13825050Seric 	else
13925050Seric 	{
14025050Seric 		/* this must be us!! */
14125050Seric 		CurHostName = MyHostName;
14225050Seric 	}
14316153Seric 	expand("\001e", inp, &inp[sizeof inp], CurEnv);
14410708Seric 	message("220", inp);
14524943Seric 	SmtpPhase = "startup";
14630448Seric 	sendinghost = NULL;
1474549Seric 	for (;;)
1484549Seric 	{
14912612Seric 		/* arrange for backout */
15012612Seric 		if (setjmp(TopFrame) > 0 && InChild)
15112612Seric 			finis();
15212612Seric 		QuickAbort = FALSE;
15312612Seric 		HoldErrs = FALSE;
15412612Seric 
1557356Seric 		/* setup for the read */
1566907Seric 		CurEnv->e_to = NULL;
1574577Seric 		Errors = 0;
1587275Seric 		(void) fflush(stdout);
1597356Seric 
1607356Seric 		/* read the input line */
1617685Seric 		p = sfgets(inp, sizeof inp, InChannel);
1627356Seric 
1637685Seric 		/* handle errors */
1647356Seric 		if (p == NULL)
1657356Seric 		{
1664549Seric 			/* end of file, just die */
16725050Seric 			message("421", "%s Lost input channel to %s",
16825050Seric 				MyHostName, CurHostName);
1694549Seric 			finis();
1704549Seric 		}
1714549Seric 
1724549Seric 		/* clean up end of line */
1734558Seric 		fixcrlf(inp, TRUE);
1744549Seric 
1754713Seric 		/* echo command to transcript */
1769545Seric 		if (CurEnv->e_xfp != NULL)
1779545Seric 			fprintf(CurEnv->e_xfp, "<<< %s\n", inp);
1784713Seric 
1794549Seric 		/* break off command */
1804549Seric 		for (p = inp; isspace(*p); p++)
1814549Seric 			continue;
1824549Seric 		cmd = p;
18324981Seric 		for (cmd = cmdbuf; *p != '\0' && !isspace(*p); )
18424981Seric 			*cmd++ = *p++;
18524981Seric 		*cmd = '\0';
1864549Seric 
18725691Seric 		/* throw away leading whitespace */
18825691Seric 		while (isspace(*p))
18925691Seric 			p++;
19025691Seric 
1914549Seric 		/* decode command */
1924549Seric 		for (c = CmdTab; c->cmdname != NULL; c++)
1934549Seric 		{
19433725Sbostic 			if (!strcasecmp(c->cmdname, cmdbuf))
1954549Seric 				break;
1964549Seric 		}
1974549Seric 
1984549Seric 		/* process command */
1994549Seric 		switch (c->cmdcode)
2004549Seric 		{
2014976Seric 		  case CMDHELO:		/* hello -- introduce yourself */
20224943Seric 			SmtpPhase = "HELO";
20325050Seric 			setproctitle("%s: %s", CurHostName, inp);
20433725Sbostic 			if (!strcasecmp(p, MyHostName))
20514877Seric 			{
20614877Seric 				/* connected to an echo server */
20714877Seric 				message("553", "%s I refuse to talk to myself",
20825050Seric 					MyHostName);
20914877Seric 				break;
21014877Seric 			}
21133725Sbostic 			if (RealHostName != NULL && strcasecmp(p, RealHostName))
21211146Seric 			{
21324981Seric 				char hostbuf[MAXNAME];
21411146Seric 
21524981Seric 				(void) sprintf(hostbuf, "%s (%s)", p, RealHostName);
21630448Seric 				sendinghost = newstr(hostbuf);
21711146Seric 			}
21811146Seric 			else
21930448Seric 				sendinghost = newstr(p);
2204997Seric 			message("250", "%s Hello %s, pleased to meet you",
22125050Seric 				MyHostName, p);
2224976Seric 			break;
2234976Seric 
2244549Seric 		  case CMDMAIL:		/* mail -- designate sender */
22524943Seric 			SmtpPhase = "MAIL";
22624943Seric 
22711151Seric 			/* force a sending host even if no HELO given */
22811151Seric 			if (RealHostName != NULL && macvalue('s', CurEnv) == NULL)
22930448Seric 				sendinghost = RealHostName;
23011151Seric 
2319314Seric 			/* check for validity of this command */
2324558Seric 			if (hasmail)
2334558Seric 			{
2344558Seric 				message("503", "Sender already specified");
2354558Seric 				break;
2364558Seric 			}
2379339Seric 			if (InChild)
2389339Seric 			{
2399339Seric 				syserr("Nested MAIL command");
2409339Seric 				exit(0);
2419339Seric 			}
2429339Seric 
2439339Seric 			/* fork a subprocess to process this command */
2449339Seric 			if (runinchild("SMTP-MAIL") > 0)
2459339Seric 				break;
24630448Seric 			define('s', sendinghost, CurEnv);
2479339Seric 			initsys();
24825016Seric 			setproctitle("%s %s: %s", CurEnv->e_id,
24925050Seric 				CurHostName, inp);
2509339Seric 
2519339Seric 			/* child -- go do the processing */
2524549Seric 			p = skipword(p, "from");
2534549Seric 			if (p == NULL)
2544549Seric 				break;
2554549Seric 			setsender(p);
2564577Seric 			if (Errors == 0)
2574549Seric 			{
2584549Seric 				message("250", "Sender ok");
2594549Seric 				hasmail = TRUE;
2604549Seric 			}
2619339Seric 			else if (InChild)
2629339Seric 				finis();
2634549Seric 			break;
2644549Seric 
2654976Seric 		  case CMDRCPT:		/* rcpt -- designate recipient */
26624943Seric 			SmtpPhase = "RCPT";
26725016Seric 			setproctitle("%s %s: %s", CurEnv->e_id,
26825050Seric 				CurHostName, inp);
26912612Seric 			if (setjmp(TopFrame) > 0)
27014785Seric 			{
27114785Seric 				CurEnv->e_flags &= ~EF_FATALERRS;
27212612Seric 				break;
27314785Seric 			}
27412612Seric 			QuickAbort = TRUE;
2754549Seric 			p = skipword(p, "to");
2764549Seric 			if (p == NULL)
2774549Seric 				break;
27816140Seric 			a = parseaddr(p, (ADDRESS *) NULL, 1, '\0');
27912612Seric 			if (a == NULL)
28012612Seric 				break;
28116886Seric 			a->q_flags |= QPRIMARY;
28212612Seric 			a = recipient(a, &CurEnv->e_sendqueue);
28312612Seric 			if (Errors != 0)
28412612Seric 				break;
28512612Seric 
28612612Seric 			/* no errors during parsing, but might be a duplicate */
28712612Seric 			CurEnv->e_to = p;
28812612Seric 			if (!bitset(QBADADDR, a->q_flags))
28912612Seric 				message("250", "Recipient ok");
29012612Seric 			else
2914549Seric 			{
29212612Seric 				/* punt -- should keep message in ADDRESS.... */
29312612Seric 				message("550", "Addressee unknown");
2944549Seric 			}
29512612Seric 			CurEnv->e_to = NULL;
2964549Seric 			break;
2974549Seric 
2984549Seric 		  case CMDDATA:		/* data -- text of mail */
29924943Seric 			SmtpPhase = "DATA";
3004976Seric 			if (!hasmail)
3014549Seric 			{
3024976Seric 				message("503", "Need MAIL command");
3034976Seric 				break;
3044549Seric 			}
30524943Seric 			else if (CurEnv->e_nrcpts <= 0)
3064549Seric 			{
3074976Seric 				message("503", "Need RCPT (recipient)");
3084976Seric 				break;
3094549Seric 			}
3104976Seric 
3114976Seric 			/* collect the text of the message */
31224943Seric 			SmtpPhase = "collect";
31325016Seric 			setproctitle("%s %s: %s", CurEnv->e_id,
31425050Seric 				CurHostName, inp);
3154976Seric 			collect(TRUE);
3164976Seric 			if (Errors != 0)
3174976Seric 				break;
3184976Seric 
3198238Seric 			/*
3208238Seric 			**  Arrange to send to everyone.
3218238Seric 			**	If sending to multiple people, mail back
3228238Seric 			**		errors rather than reporting directly.
3238238Seric 			**	In any case, don't mail back errors for
3248238Seric 			**		anything that has happened up to
3258238Seric 			**		now (the other end will do this).
32610197Seric 			**	Truncate our transcript -- the mail has gotten
32710197Seric 			**		to us successfully, and if we have
32810197Seric 			**		to mail this back, it will be easier
32910197Seric 			**		on the reader.
3308238Seric 			**	Then send to everyone.
3318238Seric 			**	Finally give a reply code.  If an error has
3328238Seric 			**		already been given, don't mail a
3338238Seric 			**		message back.
3349339Seric 			**	We goose error returns by clearing error bit.
3358238Seric 			*/
3368238Seric 
33724943Seric 			SmtpPhase = "delivery";
33824943Seric 			if (CurEnv->e_nrcpts != 1)
3399378Seric 			{
3409378Seric 				HoldErrs = TRUE;
34116886Seric 				ErrorMode = EM_MAIL;
3429378Seric 			}
3439339Seric 			CurEnv->e_flags &= ~EF_FATALERRS;
34410197Seric 			CurEnv->e_xfp = freopen(queuename(CurEnv, 'x'), "w", CurEnv->e_xfp);
3454976Seric 
3464976Seric 			/* send to all recipients */
34714877Seric 			sendall(CurEnv, SM_DEFAULT);
3486907Seric 			CurEnv->e_to = NULL;
3494976Seric 
35023516Seric 			/* save statistics */
35123516Seric 			markstats(CurEnv, (ADDRESS *) NULL);
35223516Seric 
3538238Seric 			/* issue success if appropriate and reset */
3548238Seric 			if (Errors == 0 || HoldErrs)
3559283Seric 				message("250", "Ok");
3568238Seric 			else
3579339Seric 				CurEnv->e_flags &= ~EF_FATALERRS;
3589339Seric 
3599339Seric 			/* if in a child, pop back to our parent */
3609339Seric 			if (InChild)
3619339Seric 				finis();
36224943Seric 
36324943Seric 			/* clean up a bit */
36424943Seric 			hasmail = 0;
36524943Seric 			dropenvelope(CurEnv);
36624943Seric 			CurEnv = newenvelope(CurEnv);
36724943Seric 			CurEnv->e_flags = BlankEnvelope.e_flags;
3684549Seric 			break;
3694549Seric 
3704549Seric 		  case CMDRSET:		/* rset -- reset state */
3714549Seric 			message("250", "Reset state");
3729339Seric 			if (InChild)
3739339Seric 				finis();
3749339Seric 			break;
3754549Seric 
3764549Seric 		  case CMDVRFY:		/* vrfy -- verify address */
3779339Seric 			if (runinchild("SMTP-VRFY") > 0)
3789339Seric 				break;
37925050Seric 			setproctitle("%s: %s", CurHostName, inp);
3805003Seric 			vrfyqueue = NULL;
3817762Seric 			QuickAbort = TRUE;
3829619Seric 			sendtolist(p, (ADDRESS *) NULL, &vrfyqueue);
3837762Seric 			if (Errors != 0)
3849339Seric 			{
3859339Seric 				if (InChild)
3869339Seric 					finis();
3877762Seric 				break;
3889339Seric 			}
3895003Seric 			while (vrfyqueue != NULL)
3905003Seric 			{
3915003Seric 				register ADDRESS *a = vrfyqueue->q_next;
3925003Seric 				char *code;
3935003Seric 
3947685Seric 				while (a != NULL && bitset(QDONTSEND|QBADADDR, a->q_flags))
3955003Seric 					a = a->q_next;
3965003Seric 
3977685Seric 				if (!bitset(QDONTSEND|QBADADDR, vrfyqueue->q_flags))
3985003Seric 				{
3995003Seric 					if (a != NULL)
4005003Seric 						code = "250-";
4015003Seric 					else
4025003Seric 						code = "250";
4035003Seric 					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 			if (*p == '\0')
4194577Seric 				p = "SMTP";
4204577Seric 			help(p);
4214549Seric 			break;
4224549Seric 
4234549Seric 		  case CMDNOOP:		/* noop -- do nothing */
4244549Seric 			message("200", "OK");
4254549Seric 			break;
4264549Seric 
4274549Seric 		  case CMDQUIT:		/* quit -- leave mail */
42825050Seric 			message("221", "%s closing connection", MyHostName);
4299339Seric 			if (InChild)
4309339Seric 				ExitStat = EX_QUIT;
4314549Seric 			finis();
4324549Seric 
4338544Seric 		  case CMDVERB:		/* set verbose mode */
4348544Seric 			Verbose = TRUE;
43525025Seric 			SendMode = SM_DELIVER;
4368544Seric 			message("200", "Verbose mode");
4378544Seric 			break;
4388544Seric 
4399314Seric 		  case CMDONEX:		/* doing one transaction only */
4409378Seric 			OneXact = TRUE;
4419314Seric 			message("200", "Only one transaction");
4429314Seric 			break;
4439314Seric 
4445003Seric # ifdef DEBUG
4459339Seric 		  case CMDDBGQSHOW:	/* show queues */
4466907Seric 			printf("Send Queue=");
4476907Seric 			printaddr(CurEnv->e_sendqueue, TRUE);
4485003Seric 			break;
4497275Seric 
4507275Seric 		  case CMDDBGDEBUG:	/* set debug mode */
4517676Seric 			tTsetup(tTdvect, sizeof tTdvect, "0-99.1");
4527676Seric 			tTflag(p);
4537676Seric 			message("200", "Debug set");
4547275Seric 			break;
45524945Seric # endif DEBUG
4567275Seric 
45724945Seric # ifdef WIZ
4587282Seric 		  case CMDDBGKILL:	/* kill the parent */
4598544Seric 			if (!iswiz())
4608544Seric 				break;
4617282Seric 			if (kill(MotherPid, SIGTERM) >= 0)
4627282Seric 				message("200", "Mother is dead");
4637282Seric 			else
4647282Seric 				message("500", "Can't kill Mom");
4657282Seric 			break;
4668544Seric 
4678544Seric 		  case CMDDBGWIZ:	/* become a wizard */
4688544Seric 			if (WizWord != NULL)
4698544Seric 			{
4708544Seric 				char seed[3];
4718544Seric 				extern char *crypt();
4728544Seric 
47323106Seric 				(void) strncpy(seed, WizWord, 2);
47415596Seric 				if (strcmp(WizWord, crypt(p, seed)) == 0)
4758544Seric 				{
47615596Seric 					IsWiz = TRUE;
47715596Seric 					message("200", "Please pass, oh mighty wizard");
4788544Seric 					break;
4798544Seric 				}
4808544Seric 			}
48115596Seric 			message("500", "You are no wizard!");
4828544Seric 			break;
4835003Seric 
48424945Seric # else WIZ
48524945Seric 		  case CMDDBGWIZ:	/* try to become a wizard */
48624945Seric 			message("500", "You wascal wabbit!  Wandering wizards won't win!");
48724945Seric 			break;
48824945Seric # endif WIZ
48924945Seric 
4904549Seric 		  case CMDERROR:	/* unknown command */
4914549Seric 			message("500", "Command unrecognized");
4924549Seric 			break;
4934549Seric 
4944549Seric 		  default:
4954549Seric 			syserr("smtp: unknown code %d", c->cmdcode);
4964549Seric 			break;
4974549Seric 		}
4984549Seric 	}
4994549Seric }
5004549Seric /*
5014549Seric **  SKIPWORD -- skip a fixed word.
5024549Seric **
5034549Seric **	Parameters:
5044549Seric **		p -- place to start looking.
5054549Seric **		w -- word to skip.
5064549Seric **
5074549Seric **	Returns:
5084549Seric **		p following w.
5094549Seric **		NULL on error.
5104549Seric **
5114549Seric **	Side Effects:
5124549Seric **		clobbers the p data area.
5134549Seric */
5144549Seric 
5154549Seric static char *
5164549Seric skipword(p, w)
5174549Seric 	register char *p;
5184549Seric 	char *w;
5194549Seric {
5204549Seric 	register char *q;
5214549Seric 
5224549Seric 	/* find beginning of word */
5234549Seric 	while (isspace(*p))
5244549Seric 		p++;
5254549Seric 	q = p;
5264549Seric 
5274549Seric 	/* find end of word */
5284549Seric 	while (*p != '\0' && *p != ':' && !isspace(*p))
5294549Seric 		p++;
5304549Seric 	while (isspace(*p))
5314549Seric 		*p++ = '\0';
5324549Seric 	if (*p != ':')
5334549Seric 	{
5344549Seric 	  syntax:
5354549Seric 		message("501", "Syntax error");
5364549Seric 		Errors++;
5374549Seric 		return (NULL);
5384549Seric 	}
5394549Seric 	*p++ = '\0';
5404549Seric 	while (isspace(*p))
5414549Seric 		p++;
5424549Seric 
5434549Seric 	/* see if the input word matches desired word */
54433725Sbostic 	if (strcasecmp(q, w))
5454549Seric 		goto syntax;
5464549Seric 
5474549Seric 	return (p);
5484549Seric }
5494577Seric /*
5504577Seric **  HELP -- implement the HELP command.
5514577Seric **
5524577Seric **	Parameters:
5534577Seric **		topic -- the topic we want help for.
5544577Seric **
5554577Seric **	Returns:
5564577Seric **		none.
5574577Seric **
5584577Seric **	Side Effects:
5594577Seric **		outputs the help file to message output.
5604577Seric */
5614577Seric 
5624577Seric help(topic)
5634577Seric 	char *topic;
5644577Seric {
5654577Seric 	register FILE *hf;
5664577Seric 	int len;
5674577Seric 	char buf[MAXLINE];
5684577Seric 	bool noinfo;
5694577Seric 
5708269Seric 	if (HelpFile == NULL || (hf = fopen(HelpFile, "r")) == NULL)
5714577Seric 	{
5724577Seric 		/* no help */
57311931Seric 		errno = 0;
5744577Seric 		message("502", "HELP not implemented");
5754577Seric 		return;
5764577Seric 	}
5774577Seric 
5784577Seric 	len = strlen(topic);
5794577Seric 	makelower(topic);
5804577Seric 	noinfo = TRUE;
5814577Seric 
5824577Seric 	while (fgets(buf, sizeof buf, hf) != NULL)
5834577Seric 	{
5844577Seric 		if (strncmp(buf, topic, len) == 0)
5854577Seric 		{
5864577Seric 			register char *p;
5874577Seric 
5884577Seric 			p = index(buf, '\t');
5894577Seric 			if (p == NULL)
5904577Seric 				p = buf;
5914577Seric 			else
5924577Seric 				p++;
5934577Seric 			fixcrlf(p, TRUE);
5944577Seric 			message("214-", p);
5954577Seric 			noinfo = FALSE;
5964577Seric 		}
5974577Seric 	}
5984577Seric 
5994577Seric 	if (noinfo)
6004577Seric 		message("504", "HELP topic unknown");
6014577Seric 	else
6024577Seric 		message("214", "End of HELP info");
6034628Seric 	(void) fclose(hf);
6044577Seric }
6058544Seric /*
6068544Seric **  ISWIZ -- tell us if we are a wizard
6078544Seric **
6088544Seric **	If not, print a nasty message.
6098544Seric **
6108544Seric **	Parameters:
6118544Seric **		none.
6128544Seric **
6138544Seric **	Returns:
6148544Seric **		TRUE if we are a wizard.
6158544Seric **		FALSE if we are not a wizard.
6168544Seric **
6178544Seric **	Side Effects:
6188544Seric **		Prints a 500 exit stat if we are not a wizard.
6198544Seric */
6205181Seric 
62124945Seric #ifdef WIZ
62219038Seric 
6238544Seric bool
6248544Seric iswiz()
6258544Seric {
6268544Seric 	if (!IsWiz)
6278544Seric 		message("500", "Mere mortals musn't mutter that mantra");
6288544Seric 	return (IsWiz);
6298544Seric }
63019038Seric 
63124945Seric #endif WIZ
6329339Seric /*
6339339Seric **  RUNINCHILD -- return twice -- once in the child, then in the parent again
6349339Seric **
6359339Seric **	Parameters:
6369339Seric **		label -- a string used in error messages
6379339Seric **
6389339Seric **	Returns:
6399339Seric **		zero in the child
6409339Seric **		one in the parent
6419339Seric **
6429339Seric **	Side Effects:
6439339Seric **		none.
6449339Seric */
6458544Seric 
6469339Seric runinchild(label)
6479339Seric 	char *label;
6489339Seric {
6499339Seric 	int childpid;
6509339Seric 
65116158Seric 	if (!OneXact)
6529339Seric 	{
65316158Seric 		childpid = dofork();
65416158Seric 		if (childpid < 0)
65516158Seric 		{
65616158Seric 			syserr("%s: cannot fork", label);
65716158Seric 			return (1);
65816158Seric 		}
65916158Seric 		if (childpid > 0)
66016158Seric 		{
66116158Seric 			auto int st;
6629339Seric 
66316158Seric 			/* parent -- wait for child to complete */
66416158Seric 			st = waitfor(childpid);
66516158Seric 			if (st == -1)
66616158Seric 				syserr("%s: lost child", label);
6679339Seric 
66816158Seric 			/* if we exited on a QUIT command, complete the process */
66916158Seric 			if (st == (EX_QUIT << 8))
67016158Seric 				finis();
6719339Seric 
67216158Seric 			return (1);
67316158Seric 		}
67416158Seric 		else
67516158Seric 		{
67616158Seric 			/* child */
67716158Seric 			InChild = TRUE;
67825050Seric 			QuickAbort = FALSE;
67925614Seric 			clearenvelope(CurEnv, FALSE);
68016158Seric 		}
6819339Seric 	}
68215256Seric 
68316158Seric 	/* open alias database */
68416158Seric 	initaliases(AliasFile, FALSE);
68516158Seric 
68616158Seric 	return (0);
6879339Seric }
6889339Seric 
6895181Seric # endif SMTP
690