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*30448Seric static char	SccsId[] = "@(#)srvrsmtp.c	5.19 (Berkeley) 02/03/87	(no SMTP)";
1923122Seric # endif not lint
205181Seric # else SMTP
214556Seric 
2223122Seric # ifndef lint
23*30448Seric static char	SccsId[] = "@(#)srvrsmtp.c	5.19 (Berkeley) 02/03/87";
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;
108*30448Seric 	char *sendinghost;
1098544Seric 	char inp[MAXLINE];
11024981Seric 	char cmdbuf[100];
1117124Seric 	extern char Version[];
1127356Seric 	extern tick();
1138544Seric 	extern bool iswiz();
1149349Seric 	extern char *arpadate();
11511151Seric 	extern char *macvalue();
11612612Seric 	extern ADDRESS *recipient();
11724943Seric 	extern ENVELOPE BlankEnvelope;
11824943Seric 	extern ENVELOPE *newenvelope();
1194549Seric 
1205003Seric 	hasmail = FALSE;
1217363Seric 	if (OutChannel != stdout)
1227363Seric 	{
1237363Seric 		/* arrange for debugging output to go to remote host */
1247363Seric 		(void) close(1);
1257363Seric 		(void) dup(fileno(OutChannel));
1267363Seric 	}
12711931Seric 	settime();
12824971Seric 	if (RealHostName != NULL)
12925050Seric 	{
13025050Seric 		CurHostName = RealHostName;
13125050Seric 		setproctitle("srvrsmtp %s", CurHostName);
13225050Seric 	}
13325050Seric 	else
13425050Seric 	{
13525050Seric 		/* this must be us!! */
13625050Seric 		CurHostName = MyHostName;
13725050Seric 	}
13816153Seric 	expand("\001e", inp, &inp[sizeof inp], CurEnv);
13910708Seric 	message("220", inp);
14024943Seric 	SmtpPhase = "startup";
141*30448Seric 	sendinghost = NULL;
1424549Seric 	for (;;)
1434549Seric 	{
14412612Seric 		/* arrange for backout */
14512612Seric 		if (setjmp(TopFrame) > 0 && InChild)
14612612Seric 			finis();
14712612Seric 		QuickAbort = FALSE;
14812612Seric 		HoldErrs = FALSE;
14912612Seric 
1507356Seric 		/* setup for the read */
1516907Seric 		CurEnv->e_to = NULL;
1524577Seric 		Errors = 0;
1537275Seric 		(void) fflush(stdout);
1547356Seric 
1557356Seric 		/* read the input line */
1567685Seric 		p = sfgets(inp, sizeof inp, InChannel);
1577356Seric 
1587685Seric 		/* handle errors */
1597356Seric 		if (p == NULL)
1607356Seric 		{
1614549Seric 			/* end of file, just die */
16225050Seric 			message("421", "%s Lost input channel to %s",
16325050Seric 				MyHostName, CurHostName);
1644549Seric 			finis();
1654549Seric 		}
1664549Seric 
1674549Seric 		/* clean up end of line */
1684558Seric 		fixcrlf(inp, TRUE);
1694549Seric 
1704713Seric 		/* echo command to transcript */
1719545Seric 		if (CurEnv->e_xfp != NULL)
1729545Seric 			fprintf(CurEnv->e_xfp, "<<< %s\n", inp);
1734713Seric 
1744549Seric 		/* break off command */
1754549Seric 		for (p = inp; isspace(*p); p++)
1764549Seric 			continue;
1774549Seric 		cmd = p;
17824981Seric 		for (cmd = cmdbuf; *p != '\0' && !isspace(*p); )
17924981Seric 			*cmd++ = *p++;
18024981Seric 		*cmd = '\0';
1814549Seric 
18225691Seric 		/* throw away leading whitespace */
18325691Seric 		while (isspace(*p))
18425691Seric 			p++;
18525691Seric 
1864549Seric 		/* decode command */
1874549Seric 		for (c = CmdTab; c->cmdname != NULL; c++)
1884549Seric 		{
18924984Seric 			if (sameword(c->cmdname, cmdbuf))
1904549Seric 				break;
1914549Seric 		}
1924549Seric 
1934549Seric 		/* process command */
1944549Seric 		switch (c->cmdcode)
1954549Seric 		{
1964976Seric 		  case CMDHELO:		/* hello -- introduce yourself */
19724943Seric 			SmtpPhase = "HELO";
19825050Seric 			setproctitle("%s: %s", CurHostName, inp);
19925050Seric 			if (sameword(p, MyHostName))
20014877Seric 			{
20114877Seric 				/* connected to an echo server */
20214877Seric 				message("553", "%s I refuse to talk to myself",
20325050Seric 					MyHostName);
20414877Seric 				break;
20514877Seric 			}
20611146Seric 			if (RealHostName != NULL && !sameword(p, RealHostName))
20711146Seric 			{
20824981Seric 				char hostbuf[MAXNAME];
20911146Seric 
21024981Seric 				(void) sprintf(hostbuf, "%s (%s)", p, RealHostName);
211*30448Seric 				sendinghost = newstr(hostbuf);
21211146Seric 			}
21311146Seric 			else
214*30448Seric 				sendinghost = newstr(p);
2154997Seric 			message("250", "%s Hello %s, pleased to meet you",
21625050Seric 				MyHostName, p);
2174976Seric 			break;
2184976Seric 
2194549Seric 		  case CMDMAIL:		/* mail -- designate sender */
22024943Seric 			SmtpPhase = "MAIL";
22124943Seric 
22211151Seric 			/* force a sending host even if no HELO given */
22311151Seric 			if (RealHostName != NULL && macvalue('s', CurEnv) == NULL)
224*30448Seric 				sendinghost = RealHostName;
22511151Seric 
2269314Seric 			/* check for validity of this command */
2274558Seric 			if (hasmail)
2284558Seric 			{
2294558Seric 				message("503", "Sender already specified");
2304558Seric 				break;
2314558Seric 			}
2329339Seric 			if (InChild)
2339339Seric 			{
2349339Seric 				syserr("Nested MAIL command");
2359339Seric 				exit(0);
2369339Seric 			}
2379339Seric 
2389339Seric 			/* fork a subprocess to process this command */
2399339Seric 			if (runinchild("SMTP-MAIL") > 0)
2409339Seric 				break;
241*30448Seric 			define('s', sendinghost, CurEnv);
2429339Seric 			initsys();
24325016Seric 			setproctitle("%s %s: %s", CurEnv->e_id,
24425050Seric 				CurHostName, inp);
2459339Seric 
2469339Seric 			/* child -- go do the processing */
2474549Seric 			p = skipword(p, "from");
2484549Seric 			if (p == NULL)
2494549Seric 				break;
2504549Seric 			setsender(p);
2514577Seric 			if (Errors == 0)
2524549Seric 			{
2534549Seric 				message("250", "Sender ok");
2544549Seric 				hasmail = TRUE;
2554549Seric 			}
2569339Seric 			else if (InChild)
2579339Seric 				finis();
2584549Seric 			break;
2594549Seric 
2604976Seric 		  case CMDRCPT:		/* rcpt -- designate recipient */
26124943Seric 			SmtpPhase = "RCPT";
26225016Seric 			setproctitle("%s %s: %s", CurEnv->e_id,
26325050Seric 				CurHostName, inp);
26412612Seric 			if (setjmp(TopFrame) > 0)
26514785Seric 			{
26614785Seric 				CurEnv->e_flags &= ~EF_FATALERRS;
26712612Seric 				break;
26814785Seric 			}
26912612Seric 			QuickAbort = TRUE;
2704549Seric 			p = skipword(p, "to");
2714549Seric 			if (p == NULL)
2724549Seric 				break;
27316140Seric 			a = parseaddr(p, (ADDRESS *) NULL, 1, '\0');
27412612Seric 			if (a == NULL)
27512612Seric 				break;
27616886Seric 			a->q_flags |= QPRIMARY;
27712612Seric 			a = recipient(a, &CurEnv->e_sendqueue);
27812612Seric 			if (Errors != 0)
27912612Seric 				break;
28012612Seric 
28112612Seric 			/* no errors during parsing, but might be a duplicate */
28212612Seric 			CurEnv->e_to = p;
28312612Seric 			if (!bitset(QBADADDR, a->q_flags))
28412612Seric 				message("250", "Recipient ok");
28512612Seric 			else
2864549Seric 			{
28712612Seric 				/* punt -- should keep message in ADDRESS.... */
28812612Seric 				message("550", "Addressee unknown");
2894549Seric 			}
29012612Seric 			CurEnv->e_to = NULL;
2914549Seric 			break;
2924549Seric 
2934549Seric 		  case CMDDATA:		/* data -- text of mail */
29424943Seric 			SmtpPhase = "DATA";
2954976Seric 			if (!hasmail)
2964549Seric 			{
2974976Seric 				message("503", "Need MAIL command");
2984976Seric 				break;
2994549Seric 			}
30024943Seric 			else if (CurEnv->e_nrcpts <= 0)
3014549Seric 			{
3024976Seric 				message("503", "Need RCPT (recipient)");
3034976Seric 				break;
3044549Seric 			}
3054976Seric 
3064976Seric 			/* collect the text of the message */
30724943Seric 			SmtpPhase = "collect";
30825016Seric 			setproctitle("%s %s: %s", CurEnv->e_id,
30925050Seric 				CurHostName, inp);
3104976Seric 			collect(TRUE);
3114976Seric 			if (Errors != 0)
3124976Seric 				break;
3134976Seric 
3148238Seric 			/*
3158238Seric 			**  Arrange to send to everyone.
3168238Seric 			**	If sending to multiple people, mail back
3178238Seric 			**		errors rather than reporting directly.
3188238Seric 			**	In any case, don't mail back errors for
3198238Seric 			**		anything that has happened up to
3208238Seric 			**		now (the other end will do this).
32110197Seric 			**	Truncate our transcript -- the mail has gotten
32210197Seric 			**		to us successfully, and if we have
32310197Seric 			**		to mail this back, it will be easier
32410197Seric 			**		on the reader.
3258238Seric 			**	Then send to everyone.
3268238Seric 			**	Finally give a reply code.  If an error has
3278238Seric 			**		already been given, don't mail a
3288238Seric 			**		message back.
3299339Seric 			**	We goose error returns by clearing error bit.
3308238Seric 			*/
3318238Seric 
33224943Seric 			SmtpPhase = "delivery";
33324943Seric 			if (CurEnv->e_nrcpts != 1)
3349378Seric 			{
3359378Seric 				HoldErrs = TRUE;
33616886Seric 				ErrorMode = EM_MAIL;
3379378Seric 			}
3389339Seric 			CurEnv->e_flags &= ~EF_FATALERRS;
33910197Seric 			CurEnv->e_xfp = freopen(queuename(CurEnv, 'x'), "w", CurEnv->e_xfp);
3404976Seric 
3414976Seric 			/* send to all recipients */
34214877Seric 			sendall(CurEnv, SM_DEFAULT);
3436907Seric 			CurEnv->e_to = NULL;
3444976Seric 
34523516Seric 			/* save statistics */
34623516Seric 			markstats(CurEnv, (ADDRESS *) NULL);
34723516Seric 
3488238Seric 			/* issue success if appropriate and reset */
3498238Seric 			if (Errors == 0 || HoldErrs)
3509283Seric 				message("250", "Ok");
3518238Seric 			else
3529339Seric 				CurEnv->e_flags &= ~EF_FATALERRS;
3539339Seric 
3549339Seric 			/* if in a child, pop back to our parent */
3559339Seric 			if (InChild)
3569339Seric 				finis();
35724943Seric 
35824943Seric 			/* clean up a bit */
35924943Seric 			hasmail = 0;
36024943Seric 			dropenvelope(CurEnv);
36124943Seric 			CurEnv = newenvelope(CurEnv);
36224943Seric 			CurEnv->e_flags = BlankEnvelope.e_flags;
3634549Seric 			break;
3644549Seric 
3654549Seric 		  case CMDRSET:		/* rset -- reset state */
3664549Seric 			message("250", "Reset state");
3679339Seric 			if (InChild)
3689339Seric 				finis();
3699339Seric 			break;
3704549Seric 
3714549Seric 		  case CMDVRFY:		/* vrfy -- verify address */
3729339Seric 			if (runinchild("SMTP-VRFY") > 0)
3739339Seric 				break;
37425050Seric 			setproctitle("%s: %s", CurHostName, inp);
3755003Seric 			vrfyqueue = NULL;
3767762Seric 			QuickAbort = TRUE;
3779619Seric 			sendtolist(p, (ADDRESS *) NULL, &vrfyqueue);
3787762Seric 			if (Errors != 0)
3799339Seric 			{
3809339Seric 				if (InChild)
3819339Seric 					finis();
3827762Seric 				break;
3839339Seric 			}
3845003Seric 			while (vrfyqueue != NULL)
3855003Seric 			{
3865003Seric 				register ADDRESS *a = vrfyqueue->q_next;
3875003Seric 				char *code;
3885003Seric 
3897685Seric 				while (a != NULL && bitset(QDONTSEND|QBADADDR, a->q_flags))
3905003Seric 					a = a->q_next;
3915003Seric 
3927685Seric 				if (!bitset(QDONTSEND|QBADADDR, vrfyqueue->q_flags))
3935003Seric 				{
3945003Seric 					if (a != NULL)
3955003Seric 						code = "250-";
3965003Seric 					else
3975003Seric 						code = "250";
3985003Seric 					if (vrfyqueue->q_fullname == NULL)
3995003Seric 						message(code, "<%s>", vrfyqueue->q_paddr);
4005003Seric 					else
4015003Seric 						message(code, "%s <%s>",
4025003Seric 						    vrfyqueue->q_fullname, vrfyqueue->q_paddr);
4035003Seric 				}
4045003Seric 				else if (a == NULL)
4055003Seric 					message("554", "Self destructive alias loop");
4065003Seric 				vrfyqueue = a;
4075003Seric 			}
4089339Seric 			if (InChild)
4099339Seric 				finis();
4104549Seric 			break;
4114549Seric 
4124549Seric 		  case CMDHELP:		/* help -- give user info */
4134577Seric 			if (*p == '\0')
4144577Seric 				p = "SMTP";
4154577Seric 			help(p);
4164549Seric 			break;
4174549Seric 
4184549Seric 		  case CMDNOOP:		/* noop -- do nothing */
4194549Seric 			message("200", "OK");
4204549Seric 			break;
4214549Seric 
4224549Seric 		  case CMDQUIT:		/* quit -- leave mail */
42325050Seric 			message("221", "%s closing connection", MyHostName);
4249339Seric 			if (InChild)
4259339Seric 				ExitStat = EX_QUIT;
4264549Seric 			finis();
4274549Seric 
4288544Seric 		  case CMDVERB:		/* set verbose mode */
4298544Seric 			Verbose = TRUE;
43025025Seric 			SendMode = SM_DELIVER;
4318544Seric 			message("200", "Verbose mode");
4328544Seric 			break;
4338544Seric 
4349314Seric 		  case CMDONEX:		/* doing one transaction only */
4359378Seric 			OneXact = TRUE;
4369314Seric 			message("200", "Only one transaction");
4379314Seric 			break;
4389314Seric 
4395003Seric # ifdef DEBUG
4409339Seric 		  case CMDDBGQSHOW:	/* show queues */
4416907Seric 			printf("Send Queue=");
4426907Seric 			printaddr(CurEnv->e_sendqueue, TRUE);
4435003Seric 			break;
4447275Seric 
4457275Seric 		  case CMDDBGDEBUG:	/* set debug mode */
4467676Seric 			tTsetup(tTdvect, sizeof tTdvect, "0-99.1");
4477676Seric 			tTflag(p);
4487676Seric 			message("200", "Debug set");
4497275Seric 			break;
45024945Seric # endif DEBUG
4517275Seric 
45224945Seric # ifdef WIZ
4537282Seric 		  case CMDDBGKILL:	/* kill the parent */
4548544Seric 			if (!iswiz())
4558544Seric 				break;
4567282Seric 			if (kill(MotherPid, SIGTERM) >= 0)
4577282Seric 				message("200", "Mother is dead");
4587282Seric 			else
4597282Seric 				message("500", "Can't kill Mom");
4607282Seric 			break;
4618544Seric 
4628544Seric 		  case CMDDBGWIZ:	/* become a wizard */
4638544Seric 			if (WizWord != NULL)
4648544Seric 			{
4658544Seric 				char seed[3];
4668544Seric 				extern char *crypt();
4678544Seric 
46823106Seric 				(void) strncpy(seed, WizWord, 2);
46915596Seric 				if (strcmp(WizWord, crypt(p, seed)) == 0)
4708544Seric 				{
47115596Seric 					IsWiz = TRUE;
47215596Seric 					message("200", "Please pass, oh mighty wizard");
4738544Seric 					break;
4748544Seric 				}
4758544Seric 			}
47615596Seric 			message("500", "You are no wizard!");
4778544Seric 			break;
4785003Seric 
47924945Seric # else WIZ
48024945Seric 		  case CMDDBGWIZ:	/* try to become a wizard */
48124945Seric 			message("500", "You wascal wabbit!  Wandering wizards won't win!");
48224945Seric 			break;
48324945Seric # endif WIZ
48424945Seric 
4854549Seric 		  case CMDERROR:	/* unknown command */
4864549Seric 			message("500", "Command unrecognized");
4874549Seric 			break;
4884549Seric 
4894549Seric 		  default:
4904549Seric 			syserr("smtp: unknown code %d", c->cmdcode);
4914549Seric 			break;
4924549Seric 		}
4934549Seric 	}
4944549Seric }
4954549Seric /*
4964549Seric **  SKIPWORD -- skip a fixed word.
4974549Seric **
4984549Seric **	Parameters:
4994549Seric **		p -- place to start looking.
5004549Seric **		w -- word to skip.
5014549Seric **
5024549Seric **	Returns:
5034549Seric **		p following w.
5044549Seric **		NULL on error.
5054549Seric **
5064549Seric **	Side Effects:
5074549Seric **		clobbers the p data area.
5084549Seric */
5094549Seric 
5104549Seric static char *
5114549Seric skipword(p, w)
5124549Seric 	register char *p;
5134549Seric 	char *w;
5144549Seric {
5154549Seric 	register char *q;
5164549Seric 	extern bool sameword();
5174549Seric 
5184549Seric 	/* find beginning of word */
5194549Seric 	while (isspace(*p))
5204549Seric 		p++;
5214549Seric 	q = p;
5224549Seric 
5234549Seric 	/* find end of word */
5244549Seric 	while (*p != '\0' && *p != ':' && !isspace(*p))
5254549Seric 		p++;
5264549Seric 	while (isspace(*p))
5274549Seric 		*p++ = '\0';
5284549Seric 	if (*p != ':')
5294549Seric 	{
5304549Seric 	  syntax:
5314549Seric 		message("501", "Syntax error");
5324549Seric 		Errors++;
5334549Seric 		return (NULL);
5344549Seric 	}
5354549Seric 	*p++ = '\0';
5364549Seric 	while (isspace(*p))
5374549Seric 		p++;
5384549Seric 
5394549Seric 	/* see if the input word matches desired word */
5404549Seric 	if (!sameword(q, w))
5414549Seric 		goto syntax;
5424549Seric 
5434549Seric 	return (p);
5444549Seric }
5454577Seric /*
5464577Seric **  HELP -- implement the HELP command.
5474577Seric **
5484577Seric **	Parameters:
5494577Seric **		topic -- the topic we want help for.
5504577Seric **
5514577Seric **	Returns:
5524577Seric **		none.
5534577Seric **
5544577Seric **	Side Effects:
5554577Seric **		outputs the help file to message output.
5564577Seric */
5574577Seric 
5584577Seric help(topic)
5594577Seric 	char *topic;
5604577Seric {
5614577Seric 	register FILE *hf;
5624577Seric 	int len;
5634577Seric 	char buf[MAXLINE];
5644577Seric 	bool noinfo;
5654577Seric 
5668269Seric 	if (HelpFile == NULL || (hf = fopen(HelpFile, "r")) == NULL)
5674577Seric 	{
5684577Seric 		/* no help */
56911931Seric 		errno = 0;
5704577Seric 		message("502", "HELP not implemented");
5714577Seric 		return;
5724577Seric 	}
5734577Seric 
5744577Seric 	len = strlen(topic);
5754577Seric 	makelower(topic);
5764577Seric 	noinfo = TRUE;
5774577Seric 
5784577Seric 	while (fgets(buf, sizeof buf, hf) != NULL)
5794577Seric 	{
5804577Seric 		if (strncmp(buf, topic, len) == 0)
5814577Seric 		{
5824577Seric 			register char *p;
5834577Seric 
5844577Seric 			p = index(buf, '\t');
5854577Seric 			if (p == NULL)
5864577Seric 				p = buf;
5874577Seric 			else
5884577Seric 				p++;
5894577Seric 			fixcrlf(p, TRUE);
5904577Seric 			message("214-", p);
5914577Seric 			noinfo = FALSE;
5924577Seric 		}
5934577Seric 	}
5944577Seric 
5954577Seric 	if (noinfo)
5964577Seric 		message("504", "HELP topic unknown");
5974577Seric 	else
5984577Seric 		message("214", "End of HELP info");
5994628Seric 	(void) fclose(hf);
6004577Seric }
6018544Seric /*
6028544Seric **  ISWIZ -- tell us if we are a wizard
6038544Seric **
6048544Seric **	If not, print a nasty message.
6058544Seric **
6068544Seric **	Parameters:
6078544Seric **		none.
6088544Seric **
6098544Seric **	Returns:
6108544Seric **		TRUE if we are a wizard.
6118544Seric **		FALSE if we are not a wizard.
6128544Seric **
6138544Seric **	Side Effects:
6148544Seric **		Prints a 500 exit stat if we are not a wizard.
6158544Seric */
6165181Seric 
61724945Seric #ifdef WIZ
61819038Seric 
6198544Seric bool
6208544Seric iswiz()
6218544Seric {
6228544Seric 	if (!IsWiz)
6238544Seric 		message("500", "Mere mortals musn't mutter that mantra");
6248544Seric 	return (IsWiz);
6258544Seric }
62619038Seric 
62724945Seric #endif WIZ
6289339Seric /*
6299339Seric **  RUNINCHILD -- return twice -- once in the child, then in the parent again
6309339Seric **
6319339Seric **	Parameters:
6329339Seric **		label -- a string used in error messages
6339339Seric **
6349339Seric **	Returns:
6359339Seric **		zero in the child
6369339Seric **		one in the parent
6379339Seric **
6389339Seric **	Side Effects:
6399339Seric **		none.
6409339Seric */
6418544Seric 
6429339Seric runinchild(label)
6439339Seric 	char *label;
6449339Seric {
6459339Seric 	int childpid;
6469339Seric 
64716158Seric 	if (!OneXact)
6489339Seric 	{
64916158Seric 		childpid = dofork();
65016158Seric 		if (childpid < 0)
65116158Seric 		{
65216158Seric 			syserr("%s: cannot fork", label);
65316158Seric 			return (1);
65416158Seric 		}
65516158Seric 		if (childpid > 0)
65616158Seric 		{
65716158Seric 			auto int st;
6589339Seric 
65916158Seric 			/* parent -- wait for child to complete */
66016158Seric 			st = waitfor(childpid);
66116158Seric 			if (st == -1)
66216158Seric 				syserr("%s: lost child", label);
6639339Seric 
66416158Seric 			/* if we exited on a QUIT command, complete the process */
66516158Seric 			if (st == (EX_QUIT << 8))
66616158Seric 				finis();
6679339Seric 
66816158Seric 			return (1);
66916158Seric 		}
67016158Seric 		else
67116158Seric 		{
67216158Seric 			/* child */
67316158Seric 			InChild = TRUE;
67425050Seric 			QuickAbort = FALSE;
67525614Seric 			clearenvelope(CurEnv, FALSE);
67616158Seric 		}
6779339Seric 	}
67815256Seric 
67916158Seric 	/* open alias database */
68016158Seric 	initaliases(AliasFile, FALSE);
68116158Seric 
68216158Seric 	return (0);
6839339Seric }
6849339Seric 
6855181Seric # endif SMTP
686