122712Sdist /*
222712Sdist **  Sendmail
322712Sdist **  Copyright (c) 1983  Eric P. Allman
422712Sdist **  Berkeley, California
522712Sdist **
622712Sdist **  Copyright (c) 1983 Regents of the University of California.
722712Sdist **  All rights reserved.  The Berkeley software License Agreement
822712Sdist **  specifies the terms and conditions for redistribution.
922712Sdist */
1022712Sdist 
1122712Sdist 
129339Seric # include <errno.h>
134549Seric # include "sendmail.h"
1411728Seric # include <signal.h>
154549Seric 
165181Seric # ifndef SMTP
1723122Seric # ifndef lint
18*24981Seric static char	SccsId[] = "@(#)srvrsmtp.c	5.12 (Berkeley) 09/21/85	(no SMTP)";
1923122Seric # endif not lint
205181Seric # else SMTP
214556Seric 
2223122Seric # ifndef lint
23*24981Seric static char	SccsId[] = "@(#)srvrsmtp.c	5.12 (Berkeley) 09/21/85";
2423122Seric # endif not lint
255181Seric 
264549Seric /*
274549Seric **  SMTP -- run the SMTP protocol.
284549Seric **
294549Seric **	Parameters:
304549Seric **		none.
314549Seric **
324549Seric **	Returns:
334549Seric **		never.
344549Seric **
354549Seric **	Side Effects:
364549Seric **		Reads commands from the input channel and processes
374549Seric **			them.
384549Seric */
394549Seric 
404549Seric struct cmd
414549Seric {
424549Seric 	char	*cmdname;	/* command name */
434549Seric 	int	cmdcode;	/* internal code, see below */
444549Seric };
454549Seric 
464549Seric /* values for cmdcode */
474549Seric # define CMDERROR	0	/* bad command */
484549Seric # define CMDMAIL	1	/* mail -- designate sender */
494976Seric # define CMDRCPT	2	/* rcpt -- designate recipient */
504549Seric # define CMDDATA	3	/* data -- send message text */
519339Seric # define CMDRSET	4	/* rset -- reset state */
529339Seric # define CMDVRFY	5	/* vrfy -- verify address */
539339Seric # define CMDHELP	6	/* help -- give usage info */
549339Seric # define CMDNOOP	7	/* noop -- do nothing */
559339Seric # define CMDQUIT	8	/* quit -- close connection and die */
569339Seric # define CMDHELO	9	/* helo -- be polite */
579339Seric # define CMDDBGQSHOW	10	/* showq -- show send queue (DEBUG) */
589339Seric # define CMDDBGDEBUG	11	/* debug -- set debug mode */
599339Seric # define CMDVERB	12	/* verb -- go into verbose mode */
609339Seric # define CMDDBGKILL	13	/* kill -- kill sendmail */
619339Seric # define CMDDBGWIZ	14	/* wiz -- become a wizard */
629339Seric # define CMDONEX	15	/* onex -- sending one transaction only */
634549Seric 
644549Seric static struct cmd	CmdTab[] =
654549Seric {
664549Seric 	"mail",		CMDMAIL,
674976Seric 	"rcpt",		CMDRCPT,
684549Seric 	"data",		CMDDATA,
694549Seric 	"rset",		CMDRSET,
704549Seric 	"vrfy",		CMDVRFY,
717762Seric 	"expn",		CMDVRFY,
724549Seric 	"help",		CMDHELP,
734549Seric 	"noop",		CMDNOOP,
744549Seric 	"quit",		CMDQUIT,
754976Seric 	"helo",		CMDHELO,
768544Seric 	"verb",		CMDVERB,
779314Seric 	"onex",		CMDONEX,
785003Seric # ifdef DEBUG
799339Seric 	"showq",	CMDDBGQSHOW,
808544Seric 	"debug",	CMDDBGDEBUG,
8124945Seric # endif DEBUG
8224945Seric # ifdef WIZ
838544Seric 	"kill",		CMDDBGKILL,
8424945Seric # endif WIZ
858544Seric 	"wiz",		CMDDBGWIZ,
864549Seric 	NULL,		CMDERROR,
874549Seric };
884549Seric 
8924955Seric # ifdef WIZ
908544Seric bool	IsWiz = FALSE;			/* set if we are a wizard */
9124955Seric # endif WIZ
9215596Seric char	*WizWord;			/* the wizard word to compare against */
939339Seric bool	InChild = FALSE;		/* true if running in a subprocess */
949378Seric bool	OneXact = FALSE;		/* one xaction only this run */
9511146Seric 
969339Seric #define EX_QUIT		22		/* special code for QUIT command */
978544Seric 
984549Seric smtp()
994549Seric {
1004549Seric 	register char *p;
1018544Seric 	register struct cmd *c;
1024549Seric 	char *cmd;
1034549Seric 	extern char *skipword();
1044549Seric 	extern bool sameword();
1054549Seric 	bool hasmail;			/* mail command received */
1065003Seric 	auto ADDRESS *vrfyqueue;
10712612Seric 	ADDRESS *a;
1088544Seric 	char inp[MAXLINE];
109*24981Seric 	char cmdbuf[100];
1107124Seric 	extern char Version[];
1117356Seric 	extern tick();
1128544Seric 	extern bool iswiz();
1139349Seric 	extern char *arpadate();
11411151Seric 	extern char *macvalue();
11512612Seric 	extern ADDRESS *recipient();
11624943Seric 	extern ENVELOPE BlankEnvelope;
11724943Seric 	extern ENVELOPE *newenvelope();
1184549Seric 
1195003Seric 	hasmail = FALSE;
1207363Seric 	if (OutChannel != stdout)
1217363Seric 	{
1227363Seric 		/* arrange for debugging output to go to remote host */
1237363Seric 		(void) close(1);
1247363Seric 		(void) dup(fileno(OutChannel));
1257363Seric 	}
12611931Seric 	settime();
12724971Seric 	if (RealHostName != NULL)
128*24981Seric 		setproctitle("talking to %s", RealHostName);
12916153Seric 	expand("\001e", inp, &inp[sizeof inp], CurEnv);
13010708Seric 	message("220", inp);
13124943Seric 	SmtpPhase = "startup";
1324549Seric 	for (;;)
1334549Seric 	{
13412612Seric 		/* arrange for backout */
13512612Seric 		if (setjmp(TopFrame) > 0 && InChild)
13612612Seric 			finis();
13712612Seric 		QuickAbort = FALSE;
13812612Seric 		HoldErrs = FALSE;
13912612Seric 
1407356Seric 		/* setup for the read */
1416907Seric 		CurEnv->e_to = NULL;
1424577Seric 		Errors = 0;
1437275Seric 		(void) fflush(stdout);
1447356Seric 
1457356Seric 		/* read the input line */
1467685Seric 		p = sfgets(inp, sizeof inp, InChannel);
1477356Seric 
1487685Seric 		/* handle errors */
1497356Seric 		if (p == NULL)
1507356Seric 		{
1514549Seric 			/* end of file, just die */
1524558Seric 			message("421", "%s Lost input channel", HostName);
1534549Seric 			finis();
1544549Seric 		}
1554549Seric 
1564549Seric 		/* clean up end of line */
1574558Seric 		fixcrlf(inp, TRUE);
1584549Seric 
1594713Seric 		/* echo command to transcript */
1609545Seric 		if (CurEnv->e_xfp != NULL)
1619545Seric 			fprintf(CurEnv->e_xfp, "<<< %s\n", inp);
1624713Seric 
1634549Seric 		/* break off command */
1644549Seric 		for (p = inp; isspace(*p); p++)
1654549Seric 			continue;
1664549Seric 		cmd = p;
167*24981Seric 		for (cmd = cmdbuf; *p != '\0' && !isspace(*p); )
168*24981Seric 			*cmd++ = *p++;
169*24981Seric 		*cmd = '\0';
1704549Seric 
1714549Seric 		/* decode command */
1724549Seric 		for (c = CmdTab; c->cmdname != NULL; c++)
1734549Seric 		{
1744549Seric 			if (sameword(c->cmdname, cmd))
1754549Seric 				break;
1764549Seric 		}
1774549Seric 
1784549Seric 		/* process command */
1794549Seric 		switch (c->cmdcode)
1804549Seric 		{
1814976Seric 		  case CMDHELO:		/* hello -- introduce yourself */
18224943Seric 			SmtpPhase = "HELO";
183*24981Seric 			setproctitle("talking to %s (%s)", RealHostName, inp);
18414877Seric 			if (sameword(p, HostName))
18514877Seric 			{
18614877Seric 				/* connected to an echo server */
18714877Seric 				message("553", "%s I refuse to talk to myself",
18814877Seric 					HostName);
18914877Seric 				break;
19014877Seric 			}
19111146Seric 			if (RealHostName != NULL && !sameword(p, RealHostName))
19211146Seric 			{
193*24981Seric 				char hostbuf[MAXNAME];
19411146Seric 
195*24981Seric 				(void) sprintf(hostbuf, "%s (%s)", p, RealHostName);
196*24981Seric 				define('s', newstr(hostbuf), CurEnv);
19711146Seric 			}
19811146Seric 			else
19911146Seric 				define('s', newstr(p), CurEnv);
2004997Seric 			message("250", "%s Hello %s, pleased to meet you",
2014997Seric 				HostName, p);
2024976Seric 			break;
2034976Seric 
2044549Seric 		  case CMDMAIL:		/* mail -- designate sender */
20524943Seric 			SmtpPhase = "MAIL";
20624943Seric 
20711151Seric 			/* force a sending host even if no HELO given */
20811151Seric 			if (RealHostName != NULL && macvalue('s', CurEnv) == NULL)
20911151Seric 				define('s', RealHostName, CurEnv);
21011151Seric 
2119314Seric 			/* check for validity of this command */
2124558Seric 			if (hasmail)
2134558Seric 			{
2144558Seric 				message("503", "Sender already specified");
2154558Seric 				break;
2164558Seric 			}
2179339Seric 			if (InChild)
2189339Seric 			{
2199339Seric 				syserr("Nested MAIL command");
2209339Seric 				exit(0);
2219339Seric 			}
2229339Seric 
2239339Seric 			/* fork a subprocess to process this command */
2249339Seric 			if (runinchild("SMTP-MAIL") > 0)
2259339Seric 				break;
2269339Seric 			initsys();
227*24981Seric 			setproctitle("talking to %s (%s - %s)", RealHostName,
228*24981Seric 				CurEnv->e_id, inp);
2299339Seric 
2309339Seric 			/* child -- go do the processing */
2314549Seric 			p = skipword(p, "from");
2324549Seric 			if (p == NULL)
2334549Seric 				break;
2344549Seric 			setsender(p);
2354577Seric 			if (Errors == 0)
2364549Seric 			{
2374549Seric 				message("250", "Sender ok");
2384549Seric 				hasmail = TRUE;
2394549Seric 			}
2409339Seric 			else if (InChild)
2419339Seric 				finis();
2424549Seric 			break;
2434549Seric 
2444976Seric 		  case CMDRCPT:		/* rcpt -- designate recipient */
24524943Seric 			SmtpPhase = "RCPT";
246*24981Seric 			setproctitle("talking to %s (%s - %s)", RealHostName,
247*24981Seric 				CurEnv->e_id, inp);
24812612Seric 			if (setjmp(TopFrame) > 0)
24914785Seric 			{
25014785Seric 				CurEnv->e_flags &= ~EF_FATALERRS;
25112612Seric 				break;
25214785Seric 			}
25312612Seric 			QuickAbort = TRUE;
2544549Seric 			p = skipword(p, "to");
2554549Seric 			if (p == NULL)
2564549Seric 				break;
25716140Seric 			a = parseaddr(p, (ADDRESS *) NULL, 1, '\0');
25812612Seric 			if (a == NULL)
25912612Seric 				break;
26016886Seric 			a->q_flags |= QPRIMARY;
26112612Seric 			a = recipient(a, &CurEnv->e_sendqueue);
26212612Seric 			if (Errors != 0)
26312612Seric 				break;
26412612Seric 
26512612Seric 			/* no errors during parsing, but might be a duplicate */
26612612Seric 			CurEnv->e_to = p;
26712612Seric 			if (!bitset(QBADADDR, a->q_flags))
26812612Seric 				message("250", "Recipient ok");
26912612Seric 			else
2704549Seric 			{
27112612Seric 				/* punt -- should keep message in ADDRESS.... */
27212612Seric 				message("550", "Addressee unknown");
2734549Seric 			}
27412612Seric 			CurEnv->e_to = NULL;
2754549Seric 			break;
2764549Seric 
2774549Seric 		  case CMDDATA:		/* data -- text of mail */
27824943Seric 			SmtpPhase = "DATA";
2794976Seric 			if (!hasmail)
2804549Seric 			{
2814976Seric 				message("503", "Need MAIL command");
2824976Seric 				break;
2834549Seric 			}
28424943Seric 			else if (CurEnv->e_nrcpts <= 0)
2854549Seric 			{
2864976Seric 				message("503", "Need RCPT (recipient)");
2874976Seric 				break;
2884549Seric 			}
2894976Seric 
2904976Seric 			/* collect the text of the message */
29124943Seric 			SmtpPhase = "collect";
292*24981Seric 			setproctitle("talking to %s (%s - %s)", RealHostName,
293*24981Seric 				CurEnv->e_id, inp);
2944976Seric 			collect(TRUE);
2954976Seric 			if (Errors != 0)
2964976Seric 				break;
2974976Seric 
2988238Seric 			/*
2998238Seric 			**  Arrange to send to everyone.
3008238Seric 			**	If sending to multiple people, mail back
3018238Seric 			**		errors rather than reporting directly.
3028238Seric 			**	In any case, don't mail back errors for
3038238Seric 			**		anything that has happened up to
3048238Seric 			**		now (the other end will do this).
30510197Seric 			**	Truncate our transcript -- the mail has gotten
30610197Seric 			**		to us successfully, and if we have
30710197Seric 			**		to mail this back, it will be easier
30810197Seric 			**		on the reader.
3098238Seric 			**	Then send to everyone.
3108238Seric 			**	Finally give a reply code.  If an error has
3118238Seric 			**		already been given, don't mail a
3128238Seric 			**		message back.
3139339Seric 			**	We goose error returns by clearing error bit.
3148238Seric 			*/
3158238Seric 
31624943Seric 			SmtpPhase = "delivery";
31724943Seric 			if (CurEnv->e_nrcpts != 1)
3189378Seric 			{
3199378Seric 				HoldErrs = TRUE;
32016886Seric 				ErrorMode = EM_MAIL;
3219378Seric 			}
3229339Seric 			CurEnv->e_flags &= ~EF_FATALERRS;
32310197Seric 			CurEnv->e_xfp = freopen(queuename(CurEnv, 'x'), "w", CurEnv->e_xfp);
3244976Seric 
3254976Seric 			/* send to all recipients */
32614877Seric 			sendall(CurEnv, SM_DEFAULT);
3276907Seric 			CurEnv->e_to = NULL;
3284976Seric 
32923516Seric 			/* save statistics */
33023516Seric 			markstats(CurEnv, (ADDRESS *) NULL);
33123516Seric 
3328238Seric 			/* issue success if appropriate and reset */
3338238Seric 			if (Errors == 0 || HoldErrs)
3349283Seric 				message("250", "Ok");
3358238Seric 			else
3369339Seric 				CurEnv->e_flags &= ~EF_FATALERRS;
3379339Seric 
3389339Seric 			/* if in a child, pop back to our parent */
3399339Seric 			if (InChild)
3409339Seric 				finis();
34124943Seric 
34224943Seric 			/* clean up a bit */
34324943Seric 			hasmail = 0;
34424943Seric 			dropenvelope(CurEnv);
34524943Seric 			CurEnv = newenvelope(CurEnv);
34624943Seric 			CurEnv->e_flags = BlankEnvelope.e_flags;
3474549Seric 			break;
3484549Seric 
3494549Seric 		  case CMDRSET:		/* rset -- reset state */
3504549Seric 			message("250", "Reset state");
3519339Seric 			if (InChild)
3529339Seric 				finis();
3539339Seric 			break;
3544549Seric 
3554549Seric 		  case CMDVRFY:		/* vrfy -- verify address */
3569339Seric 			if (runinchild("SMTP-VRFY") > 0)
3579339Seric 				break;
358*24981Seric 			setproctitle("talking to %s (%s)", RealHostName, inp);
3595003Seric 			vrfyqueue = NULL;
3607762Seric 			QuickAbort = TRUE;
3619619Seric 			sendtolist(p, (ADDRESS *) NULL, &vrfyqueue);
3627762Seric 			if (Errors != 0)
3639339Seric 			{
3649339Seric 				if (InChild)
3659339Seric 					finis();
3667762Seric 				break;
3679339Seric 			}
3685003Seric 			while (vrfyqueue != NULL)
3695003Seric 			{
3705003Seric 				register ADDRESS *a = vrfyqueue->q_next;
3715003Seric 				char *code;
3725003Seric 
3737685Seric 				while (a != NULL && bitset(QDONTSEND|QBADADDR, a->q_flags))
3745003Seric 					a = a->q_next;
3755003Seric 
3767685Seric 				if (!bitset(QDONTSEND|QBADADDR, vrfyqueue->q_flags))
3775003Seric 				{
3785003Seric 					if (a != NULL)
3795003Seric 						code = "250-";
3805003Seric 					else
3815003Seric 						code = "250";
3825003Seric 					if (vrfyqueue->q_fullname == NULL)
3835003Seric 						message(code, "<%s>", vrfyqueue->q_paddr);
3845003Seric 					else
3855003Seric 						message(code, "%s <%s>",
3865003Seric 						    vrfyqueue->q_fullname, vrfyqueue->q_paddr);
3875003Seric 				}
3885003Seric 				else if (a == NULL)
3895003Seric 					message("554", "Self destructive alias loop");
3905003Seric 				vrfyqueue = a;
3915003Seric 			}
3929339Seric 			if (InChild)
3939339Seric 				finis();
3944549Seric 			break;
3954549Seric 
3964549Seric 		  case CMDHELP:		/* help -- give user info */
3974577Seric 			if (*p == '\0')
3984577Seric 				p = "SMTP";
3994577Seric 			help(p);
4004549Seric 			break;
4014549Seric 
4024549Seric 		  case CMDNOOP:		/* noop -- do nothing */
4034549Seric 			message("200", "OK");
4044549Seric 			break;
4054549Seric 
4064549Seric 		  case CMDQUIT:		/* quit -- leave mail */
4074549Seric 			message("221", "%s closing connection", HostName);
4089339Seric 			if (InChild)
4099339Seric 				ExitStat = EX_QUIT;
4104549Seric 			finis();
4114549Seric 
4128544Seric 		  case CMDVERB:		/* set verbose mode */
4138544Seric 			Verbose = TRUE;
4148544Seric 			message("200", "Verbose mode");
4158544Seric 			break;
4168544Seric 
4179314Seric 		  case CMDONEX:		/* doing one transaction only */
4189378Seric 			OneXact = TRUE;
4199314Seric 			message("200", "Only one transaction");
4209314Seric 			break;
4219314Seric 
4225003Seric # ifdef DEBUG
4239339Seric 		  case CMDDBGQSHOW:	/* show queues */
4246907Seric 			printf("Send Queue=");
4256907Seric 			printaddr(CurEnv->e_sendqueue, TRUE);
4265003Seric 			break;
4277275Seric 
4287275Seric 		  case CMDDBGDEBUG:	/* set debug mode */
4297676Seric 			tTsetup(tTdvect, sizeof tTdvect, "0-99.1");
4307676Seric 			tTflag(p);
4317676Seric 			message("200", "Debug set");
4327275Seric 			break;
43324945Seric # endif DEBUG
4347275Seric 
43524945Seric # ifdef WIZ
4367282Seric 		  case CMDDBGKILL:	/* kill the parent */
4378544Seric 			if (!iswiz())
4388544Seric 				break;
4397282Seric 			if (kill(MotherPid, SIGTERM) >= 0)
4407282Seric 				message("200", "Mother is dead");
4417282Seric 			else
4427282Seric 				message("500", "Can't kill Mom");
4437282Seric 			break;
4448544Seric 
4458544Seric 		  case CMDDBGWIZ:	/* become a wizard */
4468544Seric 			if (WizWord != NULL)
4478544Seric 			{
4488544Seric 				char seed[3];
4498544Seric 				extern char *crypt();
4508544Seric 
45123106Seric 				(void) strncpy(seed, WizWord, 2);
45215596Seric 				if (strcmp(WizWord, crypt(p, seed)) == 0)
4538544Seric 				{
45415596Seric 					IsWiz = TRUE;
45515596Seric 					message("200", "Please pass, oh mighty wizard");
4568544Seric 					break;
4578544Seric 				}
4588544Seric 			}
45915596Seric 			message("500", "You are no wizard!");
4608544Seric 			break;
4615003Seric 
46224945Seric # else WIZ
46324945Seric 		  case CMDDBGWIZ:	/* try to become a wizard */
46424945Seric 			message("500", "You wascal wabbit!  Wandering wizards won't win!");
46524945Seric 			break;
46624945Seric # endif WIZ
46724945Seric 
4684549Seric 		  case CMDERROR:	/* unknown command */
4694549Seric 			message("500", "Command unrecognized");
4704549Seric 			break;
4714549Seric 
4724549Seric 		  default:
4734549Seric 			syserr("smtp: unknown code %d", c->cmdcode);
4744549Seric 			break;
4754549Seric 		}
4764549Seric 	}
4774549Seric }
4784549Seric /*
4794549Seric **  SKIPWORD -- skip a fixed word.
4804549Seric **
4814549Seric **	Parameters:
4824549Seric **		p -- place to start looking.
4834549Seric **		w -- word to skip.
4844549Seric **
4854549Seric **	Returns:
4864549Seric **		p following w.
4874549Seric **		NULL on error.
4884549Seric **
4894549Seric **	Side Effects:
4904549Seric **		clobbers the p data area.
4914549Seric */
4924549Seric 
4934549Seric static char *
4944549Seric skipword(p, w)
4954549Seric 	register char *p;
4964549Seric 	char *w;
4974549Seric {
4984549Seric 	register char *q;
4994549Seric 	extern bool sameword();
5004549Seric 
5014549Seric 	/* find beginning of word */
5024549Seric 	while (isspace(*p))
5034549Seric 		p++;
5044549Seric 	q = p;
5054549Seric 
5064549Seric 	/* find end of word */
5074549Seric 	while (*p != '\0' && *p != ':' && !isspace(*p))
5084549Seric 		p++;
5094549Seric 	while (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';
5194549Seric 	while (isspace(*p))
5204549Seric 		p++;
5214549Seric 
5224549Seric 	/* see if the input word matches desired word */
5234549Seric 	if (!sameword(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 
5574577Seric 	len = strlen(topic);
5584577Seric 	makelower(topic);
5594577Seric 	noinfo = TRUE;
5604577Seric 
5614577Seric 	while (fgets(buf, sizeof buf, hf) != NULL)
5624577Seric 	{
5634577Seric 		if (strncmp(buf, topic, len) == 0)
5644577Seric 		{
5654577Seric 			register char *p;
5664577Seric 
5674577Seric 			p = index(buf, '\t');
5684577Seric 			if (p == NULL)
5694577Seric 				p = buf;
5704577Seric 			else
5714577Seric 				p++;
5724577Seric 			fixcrlf(p, TRUE);
5734577Seric 			message("214-", p);
5744577Seric 			noinfo = FALSE;
5754577Seric 		}
5764577Seric 	}
5774577Seric 
5784577Seric 	if (noinfo)
5794577Seric 		message("504", "HELP topic unknown");
5804577Seric 	else
5814577Seric 		message("214", "End of HELP info");
5824628Seric 	(void) fclose(hf);
5834577Seric }
5848544Seric /*
5858544Seric **  ISWIZ -- tell us if we are a wizard
5868544Seric **
5878544Seric **	If not, print a nasty message.
5888544Seric **
5898544Seric **	Parameters:
5908544Seric **		none.
5918544Seric **
5928544Seric **	Returns:
5938544Seric **		TRUE if we are a wizard.
5948544Seric **		FALSE if we are not a wizard.
5958544Seric **
5968544Seric **	Side Effects:
5978544Seric **		Prints a 500 exit stat if we are not a wizard.
5988544Seric */
5995181Seric 
60024945Seric #ifdef WIZ
60119038Seric 
6028544Seric bool
6038544Seric iswiz()
6048544Seric {
6058544Seric 	if (!IsWiz)
6068544Seric 		message("500", "Mere mortals musn't mutter that mantra");
6078544Seric 	return (IsWiz);
6088544Seric }
60919038Seric 
61024945Seric #endif WIZ
6119339Seric /*
6129339Seric **  RUNINCHILD -- return twice -- once in the child, then in the parent again
6139339Seric **
6149339Seric **	Parameters:
6159339Seric **		label -- a string used in error messages
6169339Seric **
6179339Seric **	Returns:
6189339Seric **		zero in the child
6199339Seric **		one in the parent
6209339Seric **
6219339Seric **	Side Effects:
6229339Seric **		none.
6239339Seric */
6248544Seric 
6259339Seric runinchild(label)
6269339Seric 	char *label;
6279339Seric {
6289339Seric 	int childpid;
6299339Seric 
63016158Seric 	if (!OneXact)
6319339Seric 	{
63216158Seric 		childpid = dofork();
63316158Seric 		if (childpid < 0)
63416158Seric 		{
63516158Seric 			syserr("%s: cannot fork", label);
63616158Seric 			return (1);
63716158Seric 		}
63816158Seric 		if (childpid > 0)
63916158Seric 		{
64016158Seric 			auto int st;
6419339Seric 
64216158Seric 			/* parent -- wait for child to complete */
64316158Seric 			st = waitfor(childpid);
64416158Seric 			if (st == -1)
64516158Seric 				syserr("%s: lost child", label);
6469339Seric 
64716158Seric 			/* if we exited on a QUIT command, complete the process */
64816158Seric 			if (st == (EX_QUIT << 8))
64916158Seric 				finis();
6509339Seric 
65116158Seric 			return (1);
65216158Seric 		}
65316158Seric 		else
65416158Seric 		{
65516158Seric 			/* child */
65616158Seric 			InChild = TRUE;
65724945Seric 			clearenvelope(CurEnv);
65816158Seric 		}
6599339Seric 	}
66015256Seric 
66116158Seric 	/* open alias database */
66216158Seric 	initaliases(AliasFile, FALSE);
66316158Seric 
66416158Seric 	return (0);
6659339Seric }
6669339Seric 
6675181Seric # endif SMTP
668