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*33725Sbostic static char	SccsId[] = "@(#)srvrsmtp.c	5.20 (Berkeley) 03/13/88	(no SMTP)";
1923122Seric # endif not lint
205181Seric # else SMTP
214556Seric 
2223122Seric # ifndef lint
23*33725Sbostic static char	SccsId[] = "@(#)srvrsmtp.c	5.20 (Berkeley) 03/13/88";
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 	bool hasmail;			/* mail command received */
1055003Seric 	auto ADDRESS *vrfyqueue;
10612612Seric 	ADDRESS *a;
10730448Seric 	char *sendinghost;
1088544Seric 	char inp[MAXLINE];
10924981Seric 	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)
12825050Seric 	{
12925050Seric 		CurHostName = RealHostName;
13025050Seric 		setproctitle("srvrsmtp %s", CurHostName);
13125050Seric 	}
13225050Seric 	else
13325050Seric 	{
13425050Seric 		/* this must be us!! */
13525050Seric 		CurHostName = MyHostName;
13625050Seric 	}
13716153Seric 	expand("\001e", inp, &inp[sizeof inp], CurEnv);
13810708Seric 	message("220", inp);
13924943Seric 	SmtpPhase = "startup";
14030448Seric 	sendinghost = NULL;
1414549Seric 	for (;;)
1424549Seric 	{
14312612Seric 		/* arrange for backout */
14412612Seric 		if (setjmp(TopFrame) > 0 && InChild)
14512612Seric 			finis();
14612612Seric 		QuickAbort = FALSE;
14712612Seric 		HoldErrs = FALSE;
14812612Seric 
1497356Seric 		/* setup for the read */
1506907Seric 		CurEnv->e_to = NULL;
1514577Seric 		Errors = 0;
1527275Seric 		(void) fflush(stdout);
1537356Seric 
1547356Seric 		/* read the input line */
1557685Seric 		p = sfgets(inp, sizeof inp, InChannel);
1567356Seric 
1577685Seric 		/* handle errors */
1587356Seric 		if (p == NULL)
1597356Seric 		{
1604549Seric 			/* end of file, just die */
16125050Seric 			message("421", "%s Lost input channel to %s",
16225050Seric 				MyHostName, CurHostName);
1634549Seric 			finis();
1644549Seric 		}
1654549Seric 
1664549Seric 		/* clean up end of line */
1674558Seric 		fixcrlf(inp, TRUE);
1684549Seric 
1694713Seric 		/* echo command to transcript */
1709545Seric 		if (CurEnv->e_xfp != NULL)
1719545Seric 			fprintf(CurEnv->e_xfp, "<<< %s\n", inp);
1724713Seric 
1734549Seric 		/* break off command */
1744549Seric 		for (p = inp; isspace(*p); p++)
1754549Seric 			continue;
1764549Seric 		cmd = p;
17724981Seric 		for (cmd = cmdbuf; *p != '\0' && !isspace(*p); )
17824981Seric 			*cmd++ = *p++;
17924981Seric 		*cmd = '\0';
1804549Seric 
18125691Seric 		/* throw away leading whitespace */
18225691Seric 		while (isspace(*p))
18325691Seric 			p++;
18425691Seric 
1854549Seric 		/* decode command */
1864549Seric 		for (c = CmdTab; c->cmdname != NULL; c++)
1874549Seric 		{
188*33725Sbostic 			if (!strcasecmp(c->cmdname, cmdbuf))
1894549Seric 				break;
1904549Seric 		}
1914549Seric 
1924549Seric 		/* process command */
1934549Seric 		switch (c->cmdcode)
1944549Seric 		{
1954976Seric 		  case CMDHELO:		/* hello -- introduce yourself */
19624943Seric 			SmtpPhase = "HELO";
19725050Seric 			setproctitle("%s: %s", CurHostName, inp);
198*33725Sbostic 			if (!strcasecmp(p, MyHostName))
19914877Seric 			{
20014877Seric 				/* connected to an echo server */
20114877Seric 				message("553", "%s I refuse to talk to myself",
20225050Seric 					MyHostName);
20314877Seric 				break;
20414877Seric 			}
205*33725Sbostic 			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",
21525050Seric 				MyHostName, p);
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 			{
2339339Seric 				syserr("Nested MAIL command");
2349339Seric 				exit(0);
2359339Seric 			}
2369339Seric 
2379339Seric 			/* fork a subprocess to process this command */
2389339Seric 			if (runinchild("SMTP-MAIL") > 0)
2399339Seric 				break;
24030448Seric 			define('s', sendinghost, CurEnv);
2419339Seric 			initsys();
24225016Seric 			setproctitle("%s %s: %s", CurEnv->e_id,
24325050Seric 				CurHostName, inp);
2449339Seric 
2459339Seric 			/* child -- go do the processing */
2464549Seric 			p = skipword(p, "from");
2474549Seric 			if (p == NULL)
2484549Seric 				break;
2494549Seric 			setsender(p);
2504577Seric 			if (Errors == 0)
2514549Seric 			{
2524549Seric 				message("250", "Sender ok");
2534549Seric 				hasmail = TRUE;
2544549Seric 			}
2559339Seric 			else if (InChild)
2569339Seric 				finis();
2574549Seric 			break;
2584549Seric 
2594976Seric 		  case CMDRCPT:		/* rcpt -- designate recipient */
26024943Seric 			SmtpPhase = "RCPT";
26125016Seric 			setproctitle("%s %s: %s", CurEnv->e_id,
26225050Seric 				CurHostName, inp);
26312612Seric 			if (setjmp(TopFrame) > 0)
26414785Seric 			{
26514785Seric 				CurEnv->e_flags &= ~EF_FATALERRS;
26612612Seric 				break;
26714785Seric 			}
26812612Seric 			QuickAbort = TRUE;
2694549Seric 			p = skipword(p, "to");
2704549Seric 			if (p == NULL)
2714549Seric 				break;
27216140Seric 			a = parseaddr(p, (ADDRESS *) NULL, 1, '\0');
27312612Seric 			if (a == NULL)
27412612Seric 				break;
27516886Seric 			a->q_flags |= QPRIMARY;
27612612Seric 			a = recipient(a, &CurEnv->e_sendqueue);
27712612Seric 			if (Errors != 0)
27812612Seric 				break;
27912612Seric 
28012612Seric 			/* no errors during parsing, but might be a duplicate */
28112612Seric 			CurEnv->e_to = p;
28212612Seric 			if (!bitset(QBADADDR, a->q_flags))
28312612Seric 				message("250", "Recipient ok");
28412612Seric 			else
2854549Seric 			{
28612612Seric 				/* punt -- should keep message in ADDRESS.... */
28712612Seric 				message("550", "Addressee unknown");
2884549Seric 			}
28912612Seric 			CurEnv->e_to = NULL;
2904549Seric 			break;
2914549Seric 
2924549Seric 		  case CMDDATA:		/* data -- text of mail */
29324943Seric 			SmtpPhase = "DATA";
2944976Seric 			if (!hasmail)
2954549Seric 			{
2964976Seric 				message("503", "Need MAIL command");
2974976Seric 				break;
2984549Seric 			}
29924943Seric 			else if (CurEnv->e_nrcpts <= 0)
3004549Seric 			{
3014976Seric 				message("503", "Need RCPT (recipient)");
3024976Seric 				break;
3034549Seric 			}
3044976Seric 
3054976Seric 			/* collect the text of the message */
30624943Seric 			SmtpPhase = "collect";
30725016Seric 			setproctitle("%s %s: %s", CurEnv->e_id,
30825050Seric 				CurHostName, inp);
3094976Seric 			collect(TRUE);
3104976Seric 			if (Errors != 0)
3114976Seric 				break;
3124976Seric 
3138238Seric 			/*
3148238Seric 			**  Arrange to send to everyone.
3158238Seric 			**	If sending to multiple people, mail back
3168238Seric 			**		errors rather than reporting directly.
3178238Seric 			**	In any case, don't mail back errors for
3188238Seric 			**		anything that has happened up to
3198238Seric 			**		now (the other end will do this).
32010197Seric 			**	Truncate our transcript -- the mail has gotten
32110197Seric 			**		to us successfully, and if we have
32210197Seric 			**		to mail this back, it will be easier
32310197Seric 			**		on the reader.
3248238Seric 			**	Then send to everyone.
3258238Seric 			**	Finally give a reply code.  If an error has
3268238Seric 			**		already been given, don't mail a
3278238Seric 			**		message back.
3289339Seric 			**	We goose error returns by clearing error bit.
3298238Seric 			*/
3308238Seric 
33124943Seric 			SmtpPhase = "delivery";
33224943Seric 			if (CurEnv->e_nrcpts != 1)
3339378Seric 			{
3349378Seric 				HoldErrs = TRUE;
33516886Seric 				ErrorMode = EM_MAIL;
3369378Seric 			}
3379339Seric 			CurEnv->e_flags &= ~EF_FATALERRS;
33810197Seric 			CurEnv->e_xfp = freopen(queuename(CurEnv, 'x'), "w", CurEnv->e_xfp);
3394976Seric 
3404976Seric 			/* send to all recipients */
34114877Seric 			sendall(CurEnv, SM_DEFAULT);
3426907Seric 			CurEnv->e_to = NULL;
3434976Seric 
34423516Seric 			/* save statistics */
34523516Seric 			markstats(CurEnv, (ADDRESS *) NULL);
34623516Seric 
3478238Seric 			/* issue success if appropriate and reset */
3488238Seric 			if (Errors == 0 || HoldErrs)
3499283Seric 				message("250", "Ok");
3508238Seric 			else
3519339Seric 				CurEnv->e_flags &= ~EF_FATALERRS;
3529339Seric 
3539339Seric 			/* if in a child, pop back to our parent */
3549339Seric 			if (InChild)
3559339Seric 				finis();
35624943Seric 
35724943Seric 			/* clean up a bit */
35824943Seric 			hasmail = 0;
35924943Seric 			dropenvelope(CurEnv);
36024943Seric 			CurEnv = newenvelope(CurEnv);
36124943Seric 			CurEnv->e_flags = BlankEnvelope.e_flags;
3624549Seric 			break;
3634549Seric 
3644549Seric 		  case CMDRSET:		/* rset -- reset state */
3654549Seric 			message("250", "Reset state");
3669339Seric 			if (InChild)
3679339Seric 				finis();
3689339Seric 			break;
3694549Seric 
3704549Seric 		  case CMDVRFY:		/* vrfy -- verify address */
3719339Seric 			if (runinchild("SMTP-VRFY") > 0)
3729339Seric 				break;
37325050Seric 			setproctitle("%s: %s", CurHostName, inp);
3745003Seric 			vrfyqueue = NULL;
3757762Seric 			QuickAbort = TRUE;
3769619Seric 			sendtolist(p, (ADDRESS *) NULL, &vrfyqueue);
3777762Seric 			if (Errors != 0)
3789339Seric 			{
3799339Seric 				if (InChild)
3809339Seric 					finis();
3817762Seric 				break;
3829339Seric 			}
3835003Seric 			while (vrfyqueue != NULL)
3845003Seric 			{
3855003Seric 				register ADDRESS *a = vrfyqueue->q_next;
3865003Seric 				char *code;
3875003Seric 
3887685Seric 				while (a != NULL && bitset(QDONTSEND|QBADADDR, a->q_flags))
3895003Seric 					a = a->q_next;
3905003Seric 
3917685Seric 				if (!bitset(QDONTSEND|QBADADDR, vrfyqueue->q_flags))
3925003Seric 				{
3935003Seric 					if (a != NULL)
3945003Seric 						code = "250-";
3955003Seric 					else
3965003Seric 						code = "250";
3975003Seric 					if (vrfyqueue->q_fullname == NULL)
3985003Seric 						message(code, "<%s>", vrfyqueue->q_paddr);
3995003Seric 					else
4005003Seric 						message(code, "%s <%s>",
4015003Seric 						    vrfyqueue->q_fullname, vrfyqueue->q_paddr);
4025003Seric 				}
4035003Seric 				else if (a == NULL)
4045003Seric 					message("554", "Self destructive alias loop");
4055003Seric 				vrfyqueue = a;
4065003Seric 			}
4079339Seric 			if (InChild)
4089339Seric 				finis();
4094549Seric 			break;
4104549Seric 
4114549Seric 		  case CMDHELP:		/* help -- give user info */
4124577Seric 			if (*p == '\0')
4134577Seric 				p = "SMTP";
4144577Seric 			help(p);
4154549Seric 			break;
4164549Seric 
4174549Seric 		  case CMDNOOP:		/* noop -- do nothing */
4184549Seric 			message("200", "OK");
4194549Seric 			break;
4204549Seric 
4214549Seric 		  case CMDQUIT:		/* quit -- leave mail */
42225050Seric 			message("221", "%s closing connection", MyHostName);
4239339Seric 			if (InChild)
4249339Seric 				ExitStat = EX_QUIT;
4254549Seric 			finis();
4264549Seric 
4278544Seric 		  case CMDVERB:		/* set verbose mode */
4288544Seric 			Verbose = TRUE;
42925025Seric 			SendMode = SM_DELIVER;
4308544Seric 			message("200", "Verbose mode");
4318544Seric 			break;
4328544Seric 
4339314Seric 		  case CMDONEX:		/* doing one transaction only */
4349378Seric 			OneXact = TRUE;
4359314Seric 			message("200", "Only one transaction");
4369314Seric 			break;
4379314Seric 
4385003Seric # ifdef DEBUG
4399339Seric 		  case CMDDBGQSHOW:	/* show queues */
4406907Seric 			printf("Send Queue=");
4416907Seric 			printaddr(CurEnv->e_sendqueue, TRUE);
4425003Seric 			break;
4437275Seric 
4447275Seric 		  case CMDDBGDEBUG:	/* set debug mode */
4457676Seric 			tTsetup(tTdvect, sizeof tTdvect, "0-99.1");
4467676Seric 			tTflag(p);
4477676Seric 			message("200", "Debug set");
4487275Seric 			break;
44924945Seric # endif DEBUG
4507275Seric 
45124945Seric # ifdef WIZ
4527282Seric 		  case CMDDBGKILL:	/* kill the parent */
4538544Seric 			if (!iswiz())
4548544Seric 				break;
4557282Seric 			if (kill(MotherPid, SIGTERM) >= 0)
4567282Seric 				message("200", "Mother is dead");
4577282Seric 			else
4587282Seric 				message("500", "Can't kill Mom");
4597282Seric 			break;
4608544Seric 
4618544Seric 		  case CMDDBGWIZ:	/* become a wizard */
4628544Seric 			if (WizWord != NULL)
4638544Seric 			{
4648544Seric 				char seed[3];
4658544Seric 				extern char *crypt();
4668544Seric 
46723106Seric 				(void) strncpy(seed, WizWord, 2);
46815596Seric 				if (strcmp(WizWord, crypt(p, seed)) == 0)
4698544Seric 				{
47015596Seric 					IsWiz = TRUE;
47115596Seric 					message("200", "Please pass, oh mighty wizard");
4728544Seric 					break;
4738544Seric 				}
4748544Seric 			}
47515596Seric 			message("500", "You are no wizard!");
4768544Seric 			break;
4775003Seric 
47824945Seric # else WIZ
47924945Seric 		  case CMDDBGWIZ:	/* try to become a wizard */
48024945Seric 			message("500", "You wascal wabbit!  Wandering wizards won't win!");
48124945Seric 			break;
48224945Seric # endif WIZ
48324945Seric 
4844549Seric 		  case CMDERROR:	/* unknown command */
4854549Seric 			message("500", "Command unrecognized");
4864549Seric 			break;
4874549Seric 
4884549Seric 		  default:
4894549Seric 			syserr("smtp: unknown code %d", c->cmdcode);
4904549Seric 			break;
4914549Seric 		}
4924549Seric 	}
4934549Seric }
4944549Seric /*
4954549Seric **  SKIPWORD -- skip a fixed word.
4964549Seric **
4974549Seric **	Parameters:
4984549Seric **		p -- place to start looking.
4994549Seric **		w -- word to skip.
5004549Seric **
5014549Seric **	Returns:
5024549Seric **		p following w.
5034549Seric **		NULL on error.
5044549Seric **
5054549Seric **	Side Effects:
5064549Seric **		clobbers the p data area.
5074549Seric */
5084549Seric 
5094549Seric static char *
5104549Seric skipword(p, w)
5114549Seric 	register char *p;
5124549Seric 	char *w;
5134549Seric {
5144549Seric 	register char *q;
5154549Seric 
5164549Seric 	/* find beginning of word */
5174549Seric 	while (isspace(*p))
5184549Seric 		p++;
5194549Seric 	q = p;
5204549Seric 
5214549Seric 	/* find end of word */
5224549Seric 	while (*p != '\0' && *p != ':' && !isspace(*p))
5234549Seric 		p++;
5244549Seric 	while (isspace(*p))
5254549Seric 		*p++ = '\0';
5264549Seric 	if (*p != ':')
5274549Seric 	{
5284549Seric 	  syntax:
5294549Seric 		message("501", "Syntax error");
5304549Seric 		Errors++;
5314549Seric 		return (NULL);
5324549Seric 	}
5334549Seric 	*p++ = '\0';
5344549Seric 	while (isspace(*p))
5354549Seric 		p++;
5364549Seric 
5374549Seric 	/* see if the input word matches desired word */
538*33725Sbostic 	if (strcasecmp(q, w))
5394549Seric 		goto syntax;
5404549Seric 
5414549Seric 	return (p);
5424549Seric }
5434577Seric /*
5444577Seric **  HELP -- implement the HELP command.
5454577Seric **
5464577Seric **	Parameters:
5474577Seric **		topic -- the topic we want help for.
5484577Seric **
5494577Seric **	Returns:
5504577Seric **		none.
5514577Seric **
5524577Seric **	Side Effects:
5534577Seric **		outputs the help file to message output.
5544577Seric */
5554577Seric 
5564577Seric help(topic)
5574577Seric 	char *topic;
5584577Seric {
5594577Seric 	register FILE *hf;
5604577Seric 	int len;
5614577Seric 	char buf[MAXLINE];
5624577Seric 	bool noinfo;
5634577Seric 
5648269Seric 	if (HelpFile == NULL || (hf = fopen(HelpFile, "r")) == NULL)
5654577Seric 	{
5664577Seric 		/* no help */
56711931Seric 		errno = 0;
5684577Seric 		message("502", "HELP not implemented");
5694577Seric 		return;
5704577Seric 	}
5714577Seric 
5724577Seric 	len = strlen(topic);
5734577Seric 	makelower(topic);
5744577Seric 	noinfo = TRUE;
5754577Seric 
5764577Seric 	while (fgets(buf, sizeof buf, hf) != NULL)
5774577Seric 	{
5784577Seric 		if (strncmp(buf, topic, len) == 0)
5794577Seric 		{
5804577Seric 			register char *p;
5814577Seric 
5824577Seric 			p = index(buf, '\t');
5834577Seric 			if (p == NULL)
5844577Seric 				p = buf;
5854577Seric 			else
5864577Seric 				p++;
5874577Seric 			fixcrlf(p, TRUE);
5884577Seric 			message("214-", p);
5894577Seric 			noinfo = FALSE;
5904577Seric 		}
5914577Seric 	}
5924577Seric 
5934577Seric 	if (noinfo)
5944577Seric 		message("504", "HELP topic unknown");
5954577Seric 	else
5964577Seric 		message("214", "End of HELP info");
5974628Seric 	(void) fclose(hf);
5984577Seric }
5998544Seric /*
6008544Seric **  ISWIZ -- tell us if we are a wizard
6018544Seric **
6028544Seric **	If not, print a nasty message.
6038544Seric **
6048544Seric **	Parameters:
6058544Seric **		none.
6068544Seric **
6078544Seric **	Returns:
6088544Seric **		TRUE if we are a wizard.
6098544Seric **		FALSE if we are not a wizard.
6108544Seric **
6118544Seric **	Side Effects:
6128544Seric **		Prints a 500 exit stat if we are not a wizard.
6138544Seric */
6145181Seric 
61524945Seric #ifdef WIZ
61619038Seric 
6178544Seric bool
6188544Seric iswiz()
6198544Seric {
6208544Seric 	if (!IsWiz)
6218544Seric 		message("500", "Mere mortals musn't mutter that mantra");
6228544Seric 	return (IsWiz);
6238544Seric }
62419038Seric 
62524945Seric #endif WIZ
6269339Seric /*
6279339Seric **  RUNINCHILD -- return twice -- once in the child, then in the parent again
6289339Seric **
6299339Seric **	Parameters:
6309339Seric **		label -- a string used in error messages
6319339Seric **
6329339Seric **	Returns:
6339339Seric **		zero in the child
6349339Seric **		one in the parent
6359339Seric **
6369339Seric **	Side Effects:
6379339Seric **		none.
6389339Seric */
6398544Seric 
6409339Seric runinchild(label)
6419339Seric 	char *label;
6429339Seric {
6439339Seric 	int childpid;
6449339Seric 
64516158Seric 	if (!OneXact)
6469339Seric 	{
64716158Seric 		childpid = dofork();
64816158Seric 		if (childpid < 0)
64916158Seric 		{
65016158Seric 			syserr("%s: cannot fork", label);
65116158Seric 			return (1);
65216158Seric 		}
65316158Seric 		if (childpid > 0)
65416158Seric 		{
65516158Seric 			auto int st;
6569339Seric 
65716158Seric 			/* parent -- wait for child to complete */
65816158Seric 			st = waitfor(childpid);
65916158Seric 			if (st == -1)
66016158Seric 				syserr("%s: lost child", label);
6619339Seric 
66216158Seric 			/* if we exited on a QUIT command, complete the process */
66316158Seric 			if (st == (EX_QUIT << 8))
66416158Seric 				finis();
6659339Seric 
66616158Seric 			return (1);
66716158Seric 		}
66816158Seric 		else
66916158Seric 		{
67016158Seric 			/* child */
67116158Seric 			InChild = TRUE;
67225050Seric 			QuickAbort = FALSE;
67325614Seric 			clearenvelope(CurEnv, FALSE);
67416158Seric 		}
6759339Seric 	}
67615256Seric 
67716158Seric 	/* open alias database */
67816158Seric 	initaliases(AliasFile, FALSE);
67916158Seric 
68016158Seric 	return (0);
6819339Seric }
6829339Seric 
6835181Seric # endif SMTP
684