122712Sdist /*
2*34921Sbostic  * Copyright (c) 1983 Eric P. Allman
333731Sbostic  * Copyright (c) 1988 Regents of the University of California.
433731Sbostic  * All rights reserved.
533731Sbostic  *
633731Sbostic  * Redistribution and use in source and binary forms are permitted
7*34921Sbostic  * provided that the above copyright notice and this paragraph are
8*34921Sbostic  * duplicated in all such forms and that any documentation,
9*34921Sbostic  * advertising materials, and other materials related to such
10*34921Sbostic  * distribution and use acknowledge that the software was developed
11*34921Sbostic  * by the University of California, Berkeley.  The name of the
12*34921Sbostic  * University may not be used to endorse or promote products derived
13*34921Sbostic  * from this software without specific prior written permission.
14*34921Sbostic  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
15*34921Sbostic  * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
16*34921Sbostic  * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
1733731Sbostic  */
1822712Sdist 
1933731Sbostic # include "sendmail.h"
2022712Sdist 
2133731Sbostic #ifndef lint
2233731Sbostic #ifdef SMTP
23*34921Sbostic static char sccsid[] = "@(#)srvrsmtp.c	5.22 (Berkeley) 06/30/88 (with SMTP)";
2433731Sbostic #else
25*34921Sbostic static char sccsid[] = "@(#)srvrsmtp.c	5.22 (Berkeley) 06/30/88 (without SMTP)";
2633731Sbostic #endif
2733731Sbostic #endif /* not lint */
2833731Sbostic 
299339Seric # include <errno.h>
3011728Seric # include <signal.h>
314549Seric 
3233731Sbostic # ifdef SMTP
334556Seric 
344549Seric /*
354549Seric **  SMTP -- run the SMTP protocol.
364549Seric **
374549Seric **	Parameters:
384549Seric **		none.
394549Seric **
404549Seric **	Returns:
414549Seric **		never.
424549Seric **
434549Seric **	Side Effects:
444549Seric **		Reads commands from the input channel and processes
454549Seric **			them.
464549Seric */
474549Seric 
484549Seric struct cmd
494549Seric {
504549Seric 	char	*cmdname;	/* command name */
514549Seric 	int	cmdcode;	/* internal code, see below */
524549Seric };
534549Seric 
544549Seric /* values for cmdcode */
554549Seric # define CMDERROR	0	/* bad command */
564549Seric # define CMDMAIL	1	/* mail -- designate sender */
574976Seric # define CMDRCPT	2	/* rcpt -- designate recipient */
584549Seric # define CMDDATA	3	/* data -- send message text */
599339Seric # define CMDRSET	4	/* rset -- reset state */
609339Seric # define CMDVRFY	5	/* vrfy -- verify address */
619339Seric # define CMDHELP	6	/* help -- give usage info */
629339Seric # define CMDNOOP	7	/* noop -- do nothing */
639339Seric # define CMDQUIT	8	/* quit -- close connection and die */
649339Seric # define CMDHELO	9	/* helo -- be polite */
659339Seric # define CMDDBGQSHOW	10	/* showq -- show send queue (DEBUG) */
669339Seric # define CMDDBGDEBUG	11	/* debug -- set debug mode */
679339Seric # define CMDVERB	12	/* verb -- go into verbose mode */
689339Seric # define CMDDBGKILL	13	/* kill -- kill sendmail */
699339Seric # define CMDDBGWIZ	14	/* wiz -- become a wizard */
709339Seric # define CMDONEX	15	/* onex -- sending one transaction only */
714549Seric 
724549Seric static struct cmd	CmdTab[] =
734549Seric {
744549Seric 	"mail",		CMDMAIL,
754976Seric 	"rcpt",		CMDRCPT,
764549Seric 	"data",		CMDDATA,
774549Seric 	"rset",		CMDRSET,
784549Seric 	"vrfy",		CMDVRFY,
797762Seric 	"expn",		CMDVRFY,
804549Seric 	"help",		CMDHELP,
814549Seric 	"noop",		CMDNOOP,
824549Seric 	"quit",		CMDQUIT,
834976Seric 	"helo",		CMDHELO,
848544Seric 	"verb",		CMDVERB,
859314Seric 	"onex",		CMDONEX,
865003Seric # ifdef DEBUG
879339Seric 	"showq",	CMDDBGQSHOW,
888544Seric 	"debug",	CMDDBGDEBUG,
8924945Seric # endif DEBUG
9024945Seric # ifdef WIZ
918544Seric 	"kill",		CMDDBGKILL,
9224945Seric # endif WIZ
938544Seric 	"wiz",		CMDDBGWIZ,
944549Seric 	NULL,		CMDERROR,
954549Seric };
964549Seric 
9724955Seric # ifdef WIZ
988544Seric bool	IsWiz = FALSE;			/* set if we are a wizard */
9924955Seric # endif WIZ
10015596Seric char	*WizWord;			/* the wizard word to compare against */
1019339Seric bool	InChild = FALSE;		/* true if running in a subprocess */
1029378Seric bool	OneXact = FALSE;		/* one xaction only this run */
10311146Seric 
1049339Seric #define EX_QUIT		22		/* special code for QUIT command */
1058544Seric 
1064549Seric smtp()
1074549Seric {
1084549Seric 	register char *p;
1098544Seric 	register struct cmd *c;
1104549Seric 	char *cmd;
1114549Seric 	extern char *skipword();
1124549Seric 	bool hasmail;			/* mail command received */
1135003Seric 	auto ADDRESS *vrfyqueue;
11412612Seric 	ADDRESS *a;
11530448Seric 	char *sendinghost;
1168544Seric 	char inp[MAXLINE];
11724981Seric 	char cmdbuf[100];
1187124Seric 	extern char Version[];
1197356Seric 	extern tick();
1208544Seric 	extern bool iswiz();
1219349Seric 	extern char *arpadate();
12211151Seric 	extern char *macvalue();
12312612Seric 	extern ADDRESS *recipient();
12424943Seric 	extern ENVELOPE BlankEnvelope;
12524943Seric 	extern ENVELOPE *newenvelope();
1264549Seric 
1275003Seric 	hasmail = FALSE;
1287363Seric 	if (OutChannel != stdout)
1297363Seric 	{
1307363Seric 		/* arrange for debugging output to go to remote host */
1317363Seric 		(void) close(1);
1327363Seric 		(void) dup(fileno(OutChannel));
1337363Seric 	}
13411931Seric 	settime();
13524971Seric 	if (RealHostName != NULL)
13625050Seric 	{
13725050Seric 		CurHostName = RealHostName;
13825050Seric 		setproctitle("srvrsmtp %s", CurHostName);
13925050Seric 	}
14025050Seric 	else
14125050Seric 	{
14225050Seric 		/* this must be us!! */
14325050Seric 		CurHostName = MyHostName;
14425050Seric 	}
14516153Seric 	expand("\001e", inp, &inp[sizeof inp], CurEnv);
14610708Seric 	message("220", inp);
14724943Seric 	SmtpPhase = "startup";
14830448Seric 	sendinghost = NULL;
1494549Seric 	for (;;)
1504549Seric 	{
15112612Seric 		/* arrange for backout */
15212612Seric 		if (setjmp(TopFrame) > 0 && InChild)
15312612Seric 			finis();
15412612Seric 		QuickAbort = FALSE;
15512612Seric 		HoldErrs = FALSE;
15612612Seric 
1577356Seric 		/* setup for the read */
1586907Seric 		CurEnv->e_to = NULL;
1594577Seric 		Errors = 0;
1607275Seric 		(void) fflush(stdout);
1617356Seric 
1627356Seric 		/* read the input line */
1637685Seric 		p = sfgets(inp, sizeof inp, InChannel);
1647356Seric 
1657685Seric 		/* handle errors */
1667356Seric 		if (p == NULL)
1677356Seric 		{
1684549Seric 			/* end of file, just die */
16925050Seric 			message("421", "%s Lost input channel to %s",
17025050Seric 				MyHostName, CurHostName);
1714549Seric 			finis();
1724549Seric 		}
1734549Seric 
1744549Seric 		/* clean up end of line */
1754558Seric 		fixcrlf(inp, TRUE);
1764549Seric 
1774713Seric 		/* echo command to transcript */
1789545Seric 		if (CurEnv->e_xfp != NULL)
1799545Seric 			fprintf(CurEnv->e_xfp, "<<< %s\n", inp);
1804713Seric 
1814549Seric 		/* break off command */
1824549Seric 		for (p = inp; isspace(*p); p++)
1834549Seric 			continue;
1844549Seric 		cmd = p;
18524981Seric 		for (cmd = cmdbuf; *p != '\0' && !isspace(*p); )
18624981Seric 			*cmd++ = *p++;
18724981Seric 		*cmd = '\0';
1884549Seric 
18925691Seric 		/* throw away leading whitespace */
19025691Seric 		while (isspace(*p))
19125691Seric 			p++;
19225691Seric 
1934549Seric 		/* decode command */
1944549Seric 		for (c = CmdTab; c->cmdname != NULL; c++)
1954549Seric 		{
19633725Sbostic 			if (!strcasecmp(c->cmdname, cmdbuf))
1974549Seric 				break;
1984549Seric 		}
1994549Seric 
2004549Seric 		/* process command */
2014549Seric 		switch (c->cmdcode)
2024549Seric 		{
2034976Seric 		  case CMDHELO:		/* hello -- introduce yourself */
20424943Seric 			SmtpPhase = "HELO";
20525050Seric 			setproctitle("%s: %s", CurHostName, inp);
20633725Sbostic 			if (!strcasecmp(p, MyHostName))
20714877Seric 			{
20814877Seric 				/* connected to an echo server */
20914877Seric 				message("553", "%s I refuse to talk to myself",
21025050Seric 					MyHostName);
21114877Seric 				break;
21214877Seric 			}
21333725Sbostic 			if (RealHostName != NULL && strcasecmp(p, RealHostName))
21411146Seric 			{
21524981Seric 				char hostbuf[MAXNAME];
21611146Seric 
21724981Seric 				(void) sprintf(hostbuf, "%s (%s)", p, RealHostName);
21830448Seric 				sendinghost = newstr(hostbuf);
21911146Seric 			}
22011146Seric 			else
22130448Seric 				sendinghost = newstr(p);
2224997Seric 			message("250", "%s Hello %s, pleased to meet you",
22325050Seric 				MyHostName, p);
2244976Seric 			break;
2254976Seric 
2264549Seric 		  case CMDMAIL:		/* mail -- designate sender */
22724943Seric 			SmtpPhase = "MAIL";
22824943Seric 
22911151Seric 			/* force a sending host even if no HELO given */
23011151Seric 			if (RealHostName != NULL && macvalue('s', CurEnv) == NULL)
23130448Seric 				sendinghost = RealHostName;
23211151Seric 
2339314Seric 			/* check for validity of this command */
2344558Seric 			if (hasmail)
2354558Seric 			{
2364558Seric 				message("503", "Sender already specified");
2374558Seric 				break;
2384558Seric 			}
2399339Seric 			if (InChild)
2409339Seric 			{
2419339Seric 				syserr("Nested MAIL command");
2429339Seric 				exit(0);
2439339Seric 			}
2449339Seric 
2459339Seric 			/* fork a subprocess to process this command */
2469339Seric 			if (runinchild("SMTP-MAIL") > 0)
2479339Seric 				break;
24830448Seric 			define('s', sendinghost, CurEnv);
2499339Seric 			initsys();
25025016Seric 			setproctitle("%s %s: %s", CurEnv->e_id,
25125050Seric 				CurHostName, inp);
2529339Seric 
2539339Seric 			/* child -- go do the processing */
2544549Seric 			p = skipword(p, "from");
2554549Seric 			if (p == NULL)
2564549Seric 				break;
2574549Seric 			setsender(p);
2584577Seric 			if (Errors == 0)
2594549Seric 			{
2604549Seric 				message("250", "Sender ok");
2614549Seric 				hasmail = TRUE;
2624549Seric 			}
2639339Seric 			else if (InChild)
2649339Seric 				finis();
2654549Seric 			break;
2664549Seric 
2674976Seric 		  case CMDRCPT:		/* rcpt -- designate recipient */
26824943Seric 			SmtpPhase = "RCPT";
26925016Seric 			setproctitle("%s %s: %s", CurEnv->e_id,
27025050Seric 				CurHostName, inp);
27112612Seric 			if (setjmp(TopFrame) > 0)
27214785Seric 			{
27314785Seric 				CurEnv->e_flags &= ~EF_FATALERRS;
27412612Seric 				break;
27514785Seric 			}
27612612Seric 			QuickAbort = TRUE;
2774549Seric 			p = skipword(p, "to");
2784549Seric 			if (p == NULL)
2794549Seric 				break;
28016140Seric 			a = parseaddr(p, (ADDRESS *) NULL, 1, '\0');
28112612Seric 			if (a == NULL)
28212612Seric 				break;
28316886Seric 			a->q_flags |= QPRIMARY;
28412612Seric 			a = recipient(a, &CurEnv->e_sendqueue);
28512612Seric 			if (Errors != 0)
28612612Seric 				break;
28712612Seric 
28812612Seric 			/* no errors during parsing, but might be a duplicate */
28912612Seric 			CurEnv->e_to = p;
29012612Seric 			if (!bitset(QBADADDR, a->q_flags))
29112612Seric 				message("250", "Recipient ok");
29212612Seric 			else
2934549Seric 			{
29412612Seric 				/* punt -- should keep message in ADDRESS.... */
29512612Seric 				message("550", "Addressee unknown");
2964549Seric 			}
29712612Seric 			CurEnv->e_to = NULL;
2984549Seric 			break;
2994549Seric 
3004549Seric 		  case CMDDATA:		/* data -- text of mail */
30124943Seric 			SmtpPhase = "DATA";
3024976Seric 			if (!hasmail)
3034549Seric 			{
3044976Seric 				message("503", "Need MAIL command");
3054976Seric 				break;
3064549Seric 			}
30724943Seric 			else if (CurEnv->e_nrcpts <= 0)
3084549Seric 			{
3094976Seric 				message("503", "Need RCPT (recipient)");
3104976Seric 				break;
3114549Seric 			}
3124976Seric 
3134976Seric 			/* collect the text of the message */
31424943Seric 			SmtpPhase = "collect";
31525016Seric 			setproctitle("%s %s: %s", CurEnv->e_id,
31625050Seric 				CurHostName, inp);
3174976Seric 			collect(TRUE);
3184976Seric 			if (Errors != 0)
3194976Seric 				break;
3204976Seric 
3218238Seric 			/*
3228238Seric 			**  Arrange to send to everyone.
3238238Seric 			**	If sending to multiple people, mail back
3248238Seric 			**		errors rather than reporting directly.
3258238Seric 			**	In any case, don't mail back errors for
3268238Seric 			**		anything that has happened up to
3278238Seric 			**		now (the other end will do this).
32810197Seric 			**	Truncate our transcript -- the mail has gotten
32910197Seric 			**		to us successfully, and if we have
33010197Seric 			**		to mail this back, it will be easier
33110197Seric 			**		on the reader.
3328238Seric 			**	Then send to everyone.
3338238Seric 			**	Finally give a reply code.  If an error has
3348238Seric 			**		already been given, don't mail a
3358238Seric 			**		message back.
3369339Seric 			**	We goose error returns by clearing error bit.
3378238Seric 			*/
3388238Seric 
33924943Seric 			SmtpPhase = "delivery";
34024943Seric 			if (CurEnv->e_nrcpts != 1)
3419378Seric 			{
3429378Seric 				HoldErrs = TRUE;
34316886Seric 				ErrorMode = EM_MAIL;
3449378Seric 			}
3459339Seric 			CurEnv->e_flags &= ~EF_FATALERRS;
34610197Seric 			CurEnv->e_xfp = freopen(queuename(CurEnv, 'x'), "w", CurEnv->e_xfp);
3474976Seric 
3484976Seric 			/* send to all recipients */
34914877Seric 			sendall(CurEnv, SM_DEFAULT);
3506907Seric 			CurEnv->e_to = NULL;
3514976Seric 
35223516Seric 			/* save statistics */
35323516Seric 			markstats(CurEnv, (ADDRESS *) NULL);
35423516Seric 
3558238Seric 			/* issue success if appropriate and reset */
3568238Seric 			if (Errors == 0 || HoldErrs)
3579283Seric 				message("250", "Ok");
3588238Seric 			else
3599339Seric 				CurEnv->e_flags &= ~EF_FATALERRS;
3609339Seric 
3619339Seric 			/* if in a child, pop back to our parent */
3629339Seric 			if (InChild)
3639339Seric 				finis();
36424943Seric 
36524943Seric 			/* clean up a bit */
36624943Seric 			hasmail = 0;
36724943Seric 			dropenvelope(CurEnv);
36824943Seric 			CurEnv = newenvelope(CurEnv);
36924943Seric 			CurEnv->e_flags = BlankEnvelope.e_flags;
3704549Seric 			break;
3714549Seric 
3724549Seric 		  case CMDRSET:		/* rset -- reset state */
3734549Seric 			message("250", "Reset state");
3749339Seric 			if (InChild)
3759339Seric 				finis();
3769339Seric 			break;
3774549Seric 
3784549Seric 		  case CMDVRFY:		/* vrfy -- verify address */
3799339Seric 			if (runinchild("SMTP-VRFY") > 0)
3809339Seric 				break;
38125050Seric 			setproctitle("%s: %s", CurHostName, inp);
3825003Seric 			vrfyqueue = NULL;
3837762Seric 			QuickAbort = TRUE;
3849619Seric 			sendtolist(p, (ADDRESS *) NULL, &vrfyqueue);
3857762Seric 			if (Errors != 0)
3869339Seric 			{
3879339Seric 				if (InChild)
3889339Seric 					finis();
3897762Seric 				break;
3909339Seric 			}
3915003Seric 			while (vrfyqueue != NULL)
3925003Seric 			{
3935003Seric 				register ADDRESS *a = vrfyqueue->q_next;
3945003Seric 				char *code;
3955003Seric 
3967685Seric 				while (a != NULL && bitset(QDONTSEND|QBADADDR, a->q_flags))
3975003Seric 					a = a->q_next;
3985003Seric 
3997685Seric 				if (!bitset(QDONTSEND|QBADADDR, vrfyqueue->q_flags))
4005003Seric 				{
4015003Seric 					if (a != NULL)
4025003Seric 						code = "250-";
4035003Seric 					else
4045003Seric 						code = "250";
4055003Seric 					if (vrfyqueue->q_fullname == NULL)
4065003Seric 						message(code, "<%s>", vrfyqueue->q_paddr);
4075003Seric 					else
4085003Seric 						message(code, "%s <%s>",
4095003Seric 						    vrfyqueue->q_fullname, vrfyqueue->q_paddr);
4105003Seric 				}
4115003Seric 				else if (a == NULL)
4125003Seric 					message("554", "Self destructive alias loop");
4135003Seric 				vrfyqueue = a;
4145003Seric 			}
4159339Seric 			if (InChild)
4169339Seric 				finis();
4174549Seric 			break;
4184549Seric 
4194549Seric 		  case CMDHELP:		/* help -- give user info */
4204577Seric 			if (*p == '\0')
4214577Seric 				p = "SMTP";
4224577Seric 			help(p);
4234549Seric 			break;
4244549Seric 
4254549Seric 		  case CMDNOOP:		/* noop -- do nothing */
4264549Seric 			message("200", "OK");
4274549Seric 			break;
4284549Seric 
4294549Seric 		  case CMDQUIT:		/* quit -- leave mail */
43025050Seric 			message("221", "%s closing connection", MyHostName);
4319339Seric 			if (InChild)
4329339Seric 				ExitStat = EX_QUIT;
4334549Seric 			finis();
4344549Seric 
4358544Seric 		  case CMDVERB:		/* set verbose mode */
4368544Seric 			Verbose = TRUE;
43725025Seric 			SendMode = SM_DELIVER;
4388544Seric 			message("200", "Verbose mode");
4398544Seric 			break;
4408544Seric 
4419314Seric 		  case CMDONEX:		/* doing one transaction only */
4429378Seric 			OneXact = TRUE;
4439314Seric 			message("200", "Only one transaction");
4449314Seric 			break;
4459314Seric 
4465003Seric # ifdef DEBUG
4479339Seric 		  case CMDDBGQSHOW:	/* show queues */
4486907Seric 			printf("Send Queue=");
4496907Seric 			printaddr(CurEnv->e_sendqueue, TRUE);
4505003Seric 			break;
4517275Seric 
4527275Seric 		  case CMDDBGDEBUG:	/* set debug mode */
4537676Seric 			tTsetup(tTdvect, sizeof tTdvect, "0-99.1");
4547676Seric 			tTflag(p);
4557676Seric 			message("200", "Debug set");
4567275Seric 			break;
45724945Seric # endif DEBUG
4587275Seric 
45924945Seric # ifdef WIZ
4607282Seric 		  case CMDDBGKILL:	/* kill the parent */
4618544Seric 			if (!iswiz())
4628544Seric 				break;
4637282Seric 			if (kill(MotherPid, SIGTERM) >= 0)
4647282Seric 				message("200", "Mother is dead");
4657282Seric 			else
4667282Seric 				message("500", "Can't kill Mom");
4677282Seric 			break;
4688544Seric 
4698544Seric 		  case CMDDBGWIZ:	/* become a wizard */
4708544Seric 			if (WizWord != NULL)
4718544Seric 			{
4728544Seric 				char seed[3];
4738544Seric 				extern char *crypt();
4748544Seric 
47523106Seric 				(void) strncpy(seed, WizWord, 2);
47615596Seric 				if (strcmp(WizWord, crypt(p, seed)) == 0)
4778544Seric 				{
47815596Seric 					IsWiz = TRUE;
47915596Seric 					message("200", "Please pass, oh mighty wizard");
4808544Seric 					break;
4818544Seric 				}
4828544Seric 			}
48315596Seric 			message("500", "You are no wizard!");
4848544Seric 			break;
4855003Seric 
48624945Seric # else WIZ
48724945Seric 		  case CMDDBGWIZ:	/* try to become a wizard */
48824945Seric 			message("500", "You wascal wabbit!  Wandering wizards won't win!");
48924945Seric 			break;
49024945Seric # endif WIZ
49124945Seric 
4924549Seric 		  case CMDERROR:	/* unknown command */
4934549Seric 			message("500", "Command unrecognized");
4944549Seric 			break;
4954549Seric 
4964549Seric 		  default:
4974549Seric 			syserr("smtp: unknown code %d", c->cmdcode);
4984549Seric 			break;
4994549Seric 		}
5004549Seric 	}
5014549Seric }
5024549Seric /*
5034549Seric **  SKIPWORD -- skip a fixed word.
5044549Seric **
5054549Seric **	Parameters:
5064549Seric **		p -- place to start looking.
5074549Seric **		w -- word to skip.
5084549Seric **
5094549Seric **	Returns:
5104549Seric **		p following w.
5114549Seric **		NULL on error.
5124549Seric **
5134549Seric **	Side Effects:
5144549Seric **		clobbers the p data area.
5154549Seric */
5164549Seric 
5174549Seric static char *
5184549Seric skipword(p, w)
5194549Seric 	register char *p;
5204549Seric 	char *w;
5214549Seric {
5224549Seric 	register char *q;
5234549Seric 
5244549Seric 	/* find beginning of word */
5254549Seric 	while (isspace(*p))
5264549Seric 		p++;
5274549Seric 	q = p;
5284549Seric 
5294549Seric 	/* find end of word */
5304549Seric 	while (*p != '\0' && *p != ':' && !isspace(*p))
5314549Seric 		p++;
5324549Seric 	while (isspace(*p))
5334549Seric 		*p++ = '\0';
5344549Seric 	if (*p != ':')
5354549Seric 	{
5364549Seric 	  syntax:
5374549Seric 		message("501", "Syntax error");
5384549Seric 		Errors++;
5394549Seric 		return (NULL);
5404549Seric 	}
5414549Seric 	*p++ = '\0';
5424549Seric 	while (isspace(*p))
5434549Seric 		p++;
5444549Seric 
5454549Seric 	/* see if the input word matches desired word */
54633725Sbostic 	if (strcasecmp(q, w))
5474549Seric 		goto syntax;
5484549Seric 
5494549Seric 	return (p);
5504549Seric }
5514577Seric /*
5524577Seric **  HELP -- implement the HELP command.
5534577Seric **
5544577Seric **	Parameters:
5554577Seric **		topic -- the topic we want help for.
5564577Seric **
5574577Seric **	Returns:
5584577Seric **		none.
5594577Seric **
5604577Seric **	Side Effects:
5614577Seric **		outputs the help file to message output.
5624577Seric */
5634577Seric 
5644577Seric help(topic)
5654577Seric 	char *topic;
5664577Seric {
5674577Seric 	register FILE *hf;
5684577Seric 	int len;
5694577Seric 	char buf[MAXLINE];
5704577Seric 	bool noinfo;
5714577Seric 
5728269Seric 	if (HelpFile == NULL || (hf = fopen(HelpFile, "r")) == NULL)
5734577Seric 	{
5744577Seric 		/* no help */
57511931Seric 		errno = 0;
5764577Seric 		message("502", "HELP not implemented");
5774577Seric 		return;
5784577Seric 	}
5794577Seric 
5804577Seric 	len = strlen(topic);
5814577Seric 	makelower(topic);
5824577Seric 	noinfo = TRUE;
5834577Seric 
5844577Seric 	while (fgets(buf, sizeof buf, hf) != NULL)
5854577Seric 	{
5864577Seric 		if (strncmp(buf, topic, len) == 0)
5874577Seric 		{
5884577Seric 			register char *p;
5894577Seric 
5904577Seric 			p = index(buf, '\t');
5914577Seric 			if (p == NULL)
5924577Seric 				p = buf;
5934577Seric 			else
5944577Seric 				p++;
5954577Seric 			fixcrlf(p, TRUE);
5964577Seric 			message("214-", p);
5974577Seric 			noinfo = FALSE;
5984577Seric 		}
5994577Seric 	}
6004577Seric 
6014577Seric 	if (noinfo)
6024577Seric 		message("504", "HELP topic unknown");
6034577Seric 	else
6044577Seric 		message("214", "End of HELP info");
6054628Seric 	(void) fclose(hf);
6064577Seric }
6078544Seric /*
6088544Seric **  ISWIZ -- tell us if we are a wizard
6098544Seric **
6108544Seric **	If not, print a nasty message.
6118544Seric **
6128544Seric **	Parameters:
6138544Seric **		none.
6148544Seric **
6158544Seric **	Returns:
6168544Seric **		TRUE if we are a wizard.
6178544Seric **		FALSE if we are not a wizard.
6188544Seric **
6198544Seric **	Side Effects:
6208544Seric **		Prints a 500 exit stat if we are not a wizard.
6218544Seric */
6225181Seric 
62324945Seric #ifdef WIZ
62419038Seric 
6258544Seric bool
6268544Seric iswiz()
6278544Seric {
6288544Seric 	if (!IsWiz)
6298544Seric 		message("500", "Mere mortals musn't mutter that mantra");
6308544Seric 	return (IsWiz);
6318544Seric }
63219038Seric 
63324945Seric #endif WIZ
6349339Seric /*
6359339Seric **  RUNINCHILD -- return twice -- once in the child, then in the parent again
6369339Seric **
6379339Seric **	Parameters:
6389339Seric **		label -- a string used in error messages
6399339Seric **
6409339Seric **	Returns:
6419339Seric **		zero in the child
6429339Seric **		one in the parent
6439339Seric **
6449339Seric **	Side Effects:
6459339Seric **		none.
6469339Seric */
6478544Seric 
6489339Seric runinchild(label)
6499339Seric 	char *label;
6509339Seric {
6519339Seric 	int childpid;
6529339Seric 
65316158Seric 	if (!OneXact)
6549339Seric 	{
65516158Seric 		childpid = dofork();
65616158Seric 		if (childpid < 0)
65716158Seric 		{
65816158Seric 			syserr("%s: cannot fork", label);
65916158Seric 			return (1);
66016158Seric 		}
66116158Seric 		if (childpid > 0)
66216158Seric 		{
66316158Seric 			auto int st;
6649339Seric 
66516158Seric 			/* parent -- wait for child to complete */
66616158Seric 			st = waitfor(childpid);
66716158Seric 			if (st == -1)
66816158Seric 				syserr("%s: lost child", label);
6699339Seric 
67016158Seric 			/* if we exited on a QUIT command, complete the process */
67116158Seric 			if (st == (EX_QUIT << 8))
67216158Seric 				finis();
6739339Seric 
67416158Seric 			return (1);
67516158Seric 		}
67616158Seric 		else
67716158Seric 		{
67816158Seric 			/* child */
67916158Seric 			InChild = TRUE;
68025050Seric 			QuickAbort = FALSE;
68125614Seric 			clearenvelope(CurEnv, FALSE);
68216158Seric 		}
6839339Seric 	}
68415256Seric 
68516158Seric 	/* open alias database */
68616158Seric 	initaliases(AliasFile, FALSE);
68716158Seric 
68816158Seric 	return (0);
6899339Seric }
6909339Seric 
6915181Seric # endif SMTP
692