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*25691Seric static char	SccsId[] = "@(#)srvrsmtp.c	5.18 (Berkeley) 01/05/86	(no SMTP)";
1923122Seric # endif not lint
205181Seric # else SMTP
214556Seric 
2223122Seric # ifndef lint
23*25691Seric static char	SccsId[] = "@(#)srvrsmtp.c	5.18 (Berkeley) 01/05/86";
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];
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";
1404549Seric 	for (;;)
1414549Seric 	{
14212612Seric 		/* arrange for backout */
14312612Seric 		if (setjmp(TopFrame) > 0 && InChild)
14412612Seric 			finis();
14512612Seric 		QuickAbort = FALSE;
14612612Seric 		HoldErrs = FALSE;
14712612Seric 
1487356Seric 		/* setup for the read */
1496907Seric 		CurEnv->e_to = NULL;
1504577Seric 		Errors = 0;
1517275Seric 		(void) fflush(stdout);
1527356Seric 
1537356Seric 		/* read the input line */
1547685Seric 		p = sfgets(inp, sizeof inp, InChannel);
1557356Seric 
1567685Seric 		/* handle errors */
1577356Seric 		if (p == NULL)
1587356Seric 		{
1594549Seric 			/* end of file, just die */
16025050Seric 			message("421", "%s Lost input channel to %s",
16125050Seric 				MyHostName, CurHostName);
1624549Seric 			finis();
1634549Seric 		}
1644549Seric 
1654549Seric 		/* clean up end of line */
1664558Seric 		fixcrlf(inp, TRUE);
1674549Seric 
1684713Seric 		/* echo command to transcript */
1699545Seric 		if (CurEnv->e_xfp != NULL)
1709545Seric 			fprintf(CurEnv->e_xfp, "<<< %s\n", inp);
1714713Seric 
1724549Seric 		/* break off command */
1734549Seric 		for (p = inp; isspace(*p); p++)
1744549Seric 			continue;
1754549Seric 		cmd = p;
17624981Seric 		for (cmd = cmdbuf; *p != '\0' && !isspace(*p); )
17724981Seric 			*cmd++ = *p++;
17824981Seric 		*cmd = '\0';
1794549Seric 
180*25691Seric 		/* throw away leading whitespace */
181*25691Seric 		while (isspace(*p))
182*25691Seric 			p++;
183*25691Seric 
1844549Seric 		/* decode command */
1854549Seric 		for (c = CmdTab; c->cmdname != NULL; c++)
1864549Seric 		{
18724984Seric 			if (sameword(c->cmdname, cmdbuf))
1884549Seric 				break;
1894549Seric 		}
1904549Seric 
1914549Seric 		/* process command */
1924549Seric 		switch (c->cmdcode)
1934549Seric 		{
1944976Seric 		  case CMDHELO:		/* hello -- introduce yourself */
19524943Seric 			SmtpPhase = "HELO";
19625050Seric 			setproctitle("%s: %s", CurHostName, inp);
19725050Seric 			if (sameword(p, MyHostName))
19814877Seric 			{
19914877Seric 				/* connected to an echo server */
20014877Seric 				message("553", "%s I refuse to talk to myself",
20125050Seric 					MyHostName);
20214877Seric 				break;
20314877Seric 			}
20411146Seric 			if (RealHostName != NULL && !sameword(p, RealHostName))
20511146Seric 			{
20624981Seric 				char hostbuf[MAXNAME];
20711146Seric 
20824981Seric 				(void) sprintf(hostbuf, "%s (%s)", p, RealHostName);
20924981Seric 				define('s', newstr(hostbuf), CurEnv);
21011146Seric 			}
21111146Seric 			else
21211146Seric 				define('s', newstr(p), CurEnv);
2134997Seric 			message("250", "%s Hello %s, pleased to meet you",
21425050Seric 				MyHostName, p);
2154976Seric 			break;
2164976Seric 
2174549Seric 		  case CMDMAIL:		/* mail -- designate sender */
21824943Seric 			SmtpPhase = "MAIL";
21924943Seric 
22011151Seric 			/* force a sending host even if no HELO given */
22111151Seric 			if (RealHostName != NULL && macvalue('s', CurEnv) == NULL)
22211151Seric 				define('s', RealHostName, CurEnv);
22311151Seric 
2249314Seric 			/* check for validity of this command */
2254558Seric 			if (hasmail)
2264558Seric 			{
2274558Seric 				message("503", "Sender already specified");
2284558Seric 				break;
2294558Seric 			}
2309339Seric 			if (InChild)
2319339Seric 			{
2329339Seric 				syserr("Nested MAIL command");
2339339Seric 				exit(0);
2349339Seric 			}
2359339Seric 
2369339Seric 			/* fork a subprocess to process this command */
2379339Seric 			if (runinchild("SMTP-MAIL") > 0)
2389339Seric 				break;
2399339Seric 			initsys();
24025016Seric 			setproctitle("%s %s: %s", CurEnv->e_id,
24125050Seric 				CurHostName, inp);
2429339Seric 
2439339Seric 			/* child -- go do the processing */
2444549Seric 			p = skipword(p, "from");
2454549Seric 			if (p == NULL)
2464549Seric 				break;
2474549Seric 			setsender(p);
2484577Seric 			if (Errors == 0)
2494549Seric 			{
2504549Seric 				message("250", "Sender ok");
2514549Seric 				hasmail = TRUE;
2524549Seric 			}
2539339Seric 			else if (InChild)
2549339Seric 				finis();
2554549Seric 			break;
2564549Seric 
2574976Seric 		  case CMDRCPT:		/* rcpt -- designate recipient */
25824943Seric 			SmtpPhase = "RCPT";
25925016Seric 			setproctitle("%s %s: %s", CurEnv->e_id,
26025050Seric 				CurHostName, inp);
26112612Seric 			if (setjmp(TopFrame) > 0)
26214785Seric 			{
26314785Seric 				CurEnv->e_flags &= ~EF_FATALERRS;
26412612Seric 				break;
26514785Seric 			}
26612612Seric 			QuickAbort = TRUE;
2674549Seric 			p = skipword(p, "to");
2684549Seric 			if (p == NULL)
2694549Seric 				break;
27016140Seric 			a = parseaddr(p, (ADDRESS *) NULL, 1, '\0');
27112612Seric 			if (a == NULL)
27212612Seric 				break;
27316886Seric 			a->q_flags |= QPRIMARY;
27412612Seric 			a = recipient(a, &CurEnv->e_sendqueue);
27512612Seric 			if (Errors != 0)
27612612Seric 				break;
27712612Seric 
27812612Seric 			/* no errors during parsing, but might be a duplicate */
27912612Seric 			CurEnv->e_to = p;
28012612Seric 			if (!bitset(QBADADDR, a->q_flags))
28112612Seric 				message("250", "Recipient ok");
28212612Seric 			else
2834549Seric 			{
28412612Seric 				/* punt -- should keep message in ADDRESS.... */
28512612Seric 				message("550", "Addressee unknown");
2864549Seric 			}
28712612Seric 			CurEnv->e_to = NULL;
2884549Seric 			break;
2894549Seric 
2904549Seric 		  case CMDDATA:		/* data -- text of mail */
29124943Seric 			SmtpPhase = "DATA";
2924976Seric 			if (!hasmail)
2934549Seric 			{
2944976Seric 				message("503", "Need MAIL command");
2954976Seric 				break;
2964549Seric 			}
29724943Seric 			else if (CurEnv->e_nrcpts <= 0)
2984549Seric 			{
2994976Seric 				message("503", "Need RCPT (recipient)");
3004976Seric 				break;
3014549Seric 			}
3024976Seric 
3034976Seric 			/* collect the text of the message */
30424943Seric 			SmtpPhase = "collect";
30525016Seric 			setproctitle("%s %s: %s", CurEnv->e_id,
30625050Seric 				CurHostName, inp);
3074976Seric 			collect(TRUE);
3084976Seric 			if (Errors != 0)
3094976Seric 				break;
3104976Seric 
3118238Seric 			/*
3128238Seric 			**  Arrange to send to everyone.
3138238Seric 			**	If sending to multiple people, mail back
3148238Seric 			**		errors rather than reporting directly.
3158238Seric 			**	In any case, don't mail back errors for
3168238Seric 			**		anything that has happened up to
3178238Seric 			**		now (the other end will do this).
31810197Seric 			**	Truncate our transcript -- the mail has gotten
31910197Seric 			**		to us successfully, and if we have
32010197Seric 			**		to mail this back, it will be easier
32110197Seric 			**		on the reader.
3228238Seric 			**	Then send to everyone.
3238238Seric 			**	Finally give a reply code.  If an error has
3248238Seric 			**		already been given, don't mail a
3258238Seric 			**		message back.
3269339Seric 			**	We goose error returns by clearing error bit.
3278238Seric 			*/
3288238Seric 
32924943Seric 			SmtpPhase = "delivery";
33024943Seric 			if (CurEnv->e_nrcpts != 1)
3319378Seric 			{
3329378Seric 				HoldErrs = TRUE;
33316886Seric 				ErrorMode = EM_MAIL;
3349378Seric 			}
3359339Seric 			CurEnv->e_flags &= ~EF_FATALERRS;
33610197Seric 			CurEnv->e_xfp = freopen(queuename(CurEnv, 'x'), "w", CurEnv->e_xfp);
3374976Seric 
3384976Seric 			/* send to all recipients */
33914877Seric 			sendall(CurEnv, SM_DEFAULT);
3406907Seric 			CurEnv->e_to = NULL;
3414976Seric 
34223516Seric 			/* save statistics */
34323516Seric 			markstats(CurEnv, (ADDRESS *) NULL);
34423516Seric 
3458238Seric 			/* issue success if appropriate and reset */
3468238Seric 			if (Errors == 0 || HoldErrs)
3479283Seric 				message("250", "Ok");
3488238Seric 			else
3499339Seric 				CurEnv->e_flags &= ~EF_FATALERRS;
3509339Seric 
3519339Seric 			/* if in a child, pop back to our parent */
3529339Seric 			if (InChild)
3539339Seric 				finis();
35424943Seric 
35524943Seric 			/* clean up a bit */
35624943Seric 			hasmail = 0;
35724943Seric 			dropenvelope(CurEnv);
35824943Seric 			CurEnv = newenvelope(CurEnv);
35924943Seric 			CurEnv->e_flags = BlankEnvelope.e_flags;
3604549Seric 			break;
3614549Seric 
3624549Seric 		  case CMDRSET:		/* rset -- reset state */
3634549Seric 			message("250", "Reset state");
3649339Seric 			if (InChild)
3659339Seric 				finis();
3669339Seric 			break;
3674549Seric 
3684549Seric 		  case CMDVRFY:		/* vrfy -- verify address */
3699339Seric 			if (runinchild("SMTP-VRFY") > 0)
3709339Seric 				break;
37125050Seric 			setproctitle("%s: %s", CurHostName, inp);
3725003Seric 			vrfyqueue = NULL;
3737762Seric 			QuickAbort = TRUE;
3749619Seric 			sendtolist(p, (ADDRESS *) NULL, &vrfyqueue);
3757762Seric 			if (Errors != 0)
3769339Seric 			{
3779339Seric 				if (InChild)
3789339Seric 					finis();
3797762Seric 				break;
3809339Seric 			}
3815003Seric 			while (vrfyqueue != NULL)
3825003Seric 			{
3835003Seric 				register ADDRESS *a = vrfyqueue->q_next;
3845003Seric 				char *code;
3855003Seric 
3867685Seric 				while (a != NULL && bitset(QDONTSEND|QBADADDR, a->q_flags))
3875003Seric 					a = a->q_next;
3885003Seric 
3897685Seric 				if (!bitset(QDONTSEND|QBADADDR, vrfyqueue->q_flags))
3905003Seric 				{
3915003Seric 					if (a != NULL)
3925003Seric 						code = "250-";
3935003Seric 					else
3945003Seric 						code = "250";
3955003Seric 					if (vrfyqueue->q_fullname == NULL)
3965003Seric 						message(code, "<%s>", vrfyqueue->q_paddr);
3975003Seric 					else
3985003Seric 						message(code, "%s <%s>",
3995003Seric 						    vrfyqueue->q_fullname, vrfyqueue->q_paddr);
4005003Seric 				}
4015003Seric 				else if (a == NULL)
4025003Seric 					message("554", "Self destructive alias loop");
4035003Seric 				vrfyqueue = a;
4045003Seric 			}
4059339Seric 			if (InChild)
4069339Seric 				finis();
4074549Seric 			break;
4084549Seric 
4094549Seric 		  case CMDHELP:		/* help -- give user info */
4104577Seric 			if (*p == '\0')
4114577Seric 				p = "SMTP";
4124577Seric 			help(p);
4134549Seric 			break;
4144549Seric 
4154549Seric 		  case CMDNOOP:		/* noop -- do nothing */
4164549Seric 			message("200", "OK");
4174549Seric 			break;
4184549Seric 
4194549Seric 		  case CMDQUIT:		/* quit -- leave mail */
42025050Seric 			message("221", "%s closing connection", MyHostName);
4219339Seric 			if (InChild)
4229339Seric 				ExitStat = EX_QUIT;
4234549Seric 			finis();
4244549Seric 
4258544Seric 		  case CMDVERB:		/* set verbose mode */
4268544Seric 			Verbose = TRUE;
42725025Seric 			SendMode = SM_DELIVER;
4288544Seric 			message("200", "Verbose mode");
4298544Seric 			break;
4308544Seric 
4319314Seric 		  case CMDONEX:		/* doing one transaction only */
4329378Seric 			OneXact = TRUE;
4339314Seric 			message("200", "Only one transaction");
4349314Seric 			break;
4359314Seric 
4365003Seric # ifdef DEBUG
4379339Seric 		  case CMDDBGQSHOW:	/* show queues */
4386907Seric 			printf("Send Queue=");
4396907Seric 			printaddr(CurEnv->e_sendqueue, TRUE);
4405003Seric 			break;
4417275Seric 
4427275Seric 		  case CMDDBGDEBUG:	/* set debug mode */
4437676Seric 			tTsetup(tTdvect, sizeof tTdvect, "0-99.1");
4447676Seric 			tTflag(p);
4457676Seric 			message("200", "Debug set");
4467275Seric 			break;
44724945Seric # endif DEBUG
4487275Seric 
44924945Seric # ifdef WIZ
4507282Seric 		  case CMDDBGKILL:	/* kill the parent */
4518544Seric 			if (!iswiz())
4528544Seric 				break;
4537282Seric 			if (kill(MotherPid, SIGTERM) >= 0)
4547282Seric 				message("200", "Mother is dead");
4557282Seric 			else
4567282Seric 				message("500", "Can't kill Mom");
4577282Seric 			break;
4588544Seric 
4598544Seric 		  case CMDDBGWIZ:	/* become a wizard */
4608544Seric 			if (WizWord != NULL)
4618544Seric 			{
4628544Seric 				char seed[3];
4638544Seric 				extern char *crypt();
4648544Seric 
46523106Seric 				(void) strncpy(seed, WizWord, 2);
46615596Seric 				if (strcmp(WizWord, crypt(p, seed)) == 0)
4678544Seric 				{
46815596Seric 					IsWiz = TRUE;
46915596Seric 					message("200", "Please pass, oh mighty wizard");
4708544Seric 					break;
4718544Seric 				}
4728544Seric 			}
47315596Seric 			message("500", "You are no wizard!");
4748544Seric 			break;
4755003Seric 
47624945Seric # else WIZ
47724945Seric 		  case CMDDBGWIZ:	/* try to become a wizard */
47824945Seric 			message("500", "You wascal wabbit!  Wandering wizards won't win!");
47924945Seric 			break;
48024945Seric # endif WIZ
48124945Seric 
4824549Seric 		  case CMDERROR:	/* unknown command */
4834549Seric 			message("500", "Command unrecognized");
4844549Seric 			break;
4854549Seric 
4864549Seric 		  default:
4874549Seric 			syserr("smtp: unknown code %d", c->cmdcode);
4884549Seric 			break;
4894549Seric 		}
4904549Seric 	}
4914549Seric }
4924549Seric /*
4934549Seric **  SKIPWORD -- skip a fixed word.
4944549Seric **
4954549Seric **	Parameters:
4964549Seric **		p -- place to start looking.
4974549Seric **		w -- word to skip.
4984549Seric **
4994549Seric **	Returns:
5004549Seric **		p following w.
5014549Seric **		NULL on error.
5024549Seric **
5034549Seric **	Side Effects:
5044549Seric **		clobbers the p data area.
5054549Seric */
5064549Seric 
5074549Seric static char *
5084549Seric skipword(p, w)
5094549Seric 	register char *p;
5104549Seric 	char *w;
5114549Seric {
5124549Seric 	register char *q;
5134549Seric 	extern bool sameword();
5144549Seric 
5154549Seric 	/* find beginning of word */
5164549Seric 	while (isspace(*p))
5174549Seric 		p++;
5184549Seric 	q = p;
5194549Seric 
5204549Seric 	/* find end of word */
5214549Seric 	while (*p != '\0' && *p != ':' && !isspace(*p))
5224549Seric 		p++;
5234549Seric 	while (isspace(*p))
5244549Seric 		*p++ = '\0';
5254549Seric 	if (*p != ':')
5264549Seric 	{
5274549Seric 	  syntax:
5284549Seric 		message("501", "Syntax error");
5294549Seric 		Errors++;
5304549Seric 		return (NULL);
5314549Seric 	}
5324549Seric 	*p++ = '\0';
5334549Seric 	while (isspace(*p))
5344549Seric 		p++;
5354549Seric 
5364549Seric 	/* see if the input word matches desired word */
5374549Seric 	if (!sameword(q, w))
5384549Seric 		goto syntax;
5394549Seric 
5404549Seric 	return (p);
5414549Seric }
5424577Seric /*
5434577Seric **  HELP -- implement the HELP command.
5444577Seric **
5454577Seric **	Parameters:
5464577Seric **		topic -- the topic we want help for.
5474577Seric **
5484577Seric **	Returns:
5494577Seric **		none.
5504577Seric **
5514577Seric **	Side Effects:
5524577Seric **		outputs the help file to message output.
5534577Seric */
5544577Seric 
5554577Seric help(topic)
5564577Seric 	char *topic;
5574577Seric {
5584577Seric 	register FILE *hf;
5594577Seric 	int len;
5604577Seric 	char buf[MAXLINE];
5614577Seric 	bool noinfo;
5624577Seric 
5638269Seric 	if (HelpFile == NULL || (hf = fopen(HelpFile, "r")) == NULL)
5644577Seric 	{
5654577Seric 		/* no help */
56611931Seric 		errno = 0;
5674577Seric 		message("502", "HELP not implemented");
5684577Seric 		return;
5694577Seric 	}
5704577Seric 
5714577Seric 	len = strlen(topic);
5724577Seric 	makelower(topic);
5734577Seric 	noinfo = TRUE;
5744577Seric 
5754577Seric 	while (fgets(buf, sizeof buf, hf) != NULL)
5764577Seric 	{
5774577Seric 		if (strncmp(buf, topic, len) == 0)
5784577Seric 		{
5794577Seric 			register char *p;
5804577Seric 
5814577Seric 			p = index(buf, '\t');
5824577Seric 			if (p == NULL)
5834577Seric 				p = buf;
5844577Seric 			else
5854577Seric 				p++;
5864577Seric 			fixcrlf(p, TRUE);
5874577Seric 			message("214-", p);
5884577Seric 			noinfo = FALSE;
5894577Seric 		}
5904577Seric 	}
5914577Seric 
5924577Seric 	if (noinfo)
5934577Seric 		message("504", "HELP topic unknown");
5944577Seric 	else
5954577Seric 		message("214", "End of HELP info");
5964628Seric 	(void) fclose(hf);
5974577Seric }
5988544Seric /*
5998544Seric **  ISWIZ -- tell us if we are a wizard
6008544Seric **
6018544Seric **	If not, print a nasty message.
6028544Seric **
6038544Seric **	Parameters:
6048544Seric **		none.
6058544Seric **
6068544Seric **	Returns:
6078544Seric **		TRUE if we are a wizard.
6088544Seric **		FALSE if we are not a wizard.
6098544Seric **
6108544Seric **	Side Effects:
6118544Seric **		Prints a 500 exit stat if we are not a wizard.
6128544Seric */
6135181Seric 
61424945Seric #ifdef WIZ
61519038Seric 
6168544Seric bool
6178544Seric iswiz()
6188544Seric {
6198544Seric 	if (!IsWiz)
6208544Seric 		message("500", "Mere mortals musn't mutter that mantra");
6218544Seric 	return (IsWiz);
6228544Seric }
62319038Seric 
62424945Seric #endif WIZ
6259339Seric /*
6269339Seric **  RUNINCHILD -- return twice -- once in the child, then in the parent again
6279339Seric **
6289339Seric **	Parameters:
6299339Seric **		label -- a string used in error messages
6309339Seric **
6319339Seric **	Returns:
6329339Seric **		zero in the child
6339339Seric **		one in the parent
6349339Seric **
6359339Seric **	Side Effects:
6369339Seric **		none.
6379339Seric */
6388544Seric 
6399339Seric runinchild(label)
6409339Seric 	char *label;
6419339Seric {
6429339Seric 	int childpid;
6439339Seric 
64416158Seric 	if (!OneXact)
6459339Seric 	{
64616158Seric 		childpid = dofork();
64716158Seric 		if (childpid < 0)
64816158Seric 		{
64916158Seric 			syserr("%s: cannot fork", label);
65016158Seric 			return (1);
65116158Seric 		}
65216158Seric 		if (childpid > 0)
65316158Seric 		{
65416158Seric 			auto int st;
6559339Seric 
65616158Seric 			/* parent -- wait for child to complete */
65716158Seric 			st = waitfor(childpid);
65816158Seric 			if (st == -1)
65916158Seric 				syserr("%s: lost child", label);
6609339Seric 
66116158Seric 			/* if we exited on a QUIT command, complete the process */
66216158Seric 			if (st == (EX_QUIT << 8))
66316158Seric 				finis();
6649339Seric 
66516158Seric 			return (1);
66616158Seric 		}
66716158Seric 		else
66816158Seric 		{
66916158Seric 			/* child */
67016158Seric 			InChild = TRUE;
67125050Seric 			QuickAbort = FALSE;
67225614Seric 			clearenvelope(CurEnv, FALSE);
67316158Seric 		}
6749339Seric 	}
67515256Seric 
67616158Seric 	/* open alias database */
67716158Seric 	initaliases(AliasFile, FALSE);
67816158Seric 
67916158Seric 	return (0);
6809339Seric }
6819339Seric 
6825181Seric # endif SMTP
683