122712Sdist /*
234921Sbostic  * 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
734921Sbostic  * provided that the above copyright notice and this paragraph are
834921Sbostic  * duplicated in all such forms and that any documentation,
934921Sbostic  * advertising materials, and other materials related to such
1034921Sbostic  * distribution and use acknowledge that the software was developed
1134921Sbostic  * by the University of California, Berkeley.  The name of the
1234921Sbostic  * University may not be used to endorse or promote products derived
1334921Sbostic  * from this software without specific prior written permission.
1434921Sbostic  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
1534921Sbostic  * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
1634921Sbostic  * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
1733731Sbostic  */
1822712Sdist 
1933731Sbostic # include "sendmail.h"
2022712Sdist 
2133731Sbostic #ifndef lint
2233731Sbostic #ifdef SMTP
23*36233Skarels static char sccsid[] = "@(#)srvrsmtp.c	5.25 (Berkeley) 11/17/88 (with SMTP)";
2433731Sbostic #else
25*36233Skarels static char sccsid[] = "@(#)srvrsmtp.c	5.25 (Berkeley) 11/17/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 */
6536230Skarels # define CMDONEX	10	/* onex -- sending one transaction only */
6636230Skarels # define CMDVERB	11	/* verb -- go into verbose mode */
6736230Skarels /* debugging-only commands, only enabled if SMTPDEBUG is defined */
6836230Skarels # define CMDDBGQSHOW	12	/* showq -- show send queue */
6936230Skarels # define CMDDBGDEBUG	13	/* debug -- set debug mode */
7036230Skarels # define CMDDBGKILL	14	/* kill -- kill sendmail */
7136230Skarels # define CMDDBGWIZ	15	/* wiz -- become a wizard */
724549Seric 
734549Seric static struct cmd	CmdTab[] =
744549Seric {
754549Seric 	"mail",		CMDMAIL,
764976Seric 	"rcpt",		CMDRCPT,
774549Seric 	"data",		CMDDATA,
784549Seric 	"rset",		CMDRSET,
794549Seric 	"vrfy",		CMDVRFY,
807762Seric 	"expn",		CMDVRFY,
814549Seric 	"help",		CMDHELP,
824549Seric 	"noop",		CMDNOOP,
834549Seric 	"quit",		CMDQUIT,
844976Seric 	"helo",		CMDHELO,
858544Seric 	"verb",		CMDVERB,
869314Seric 	"onex",		CMDONEX,
8736230Skarels 	/*
8836230Skarels 	 * remaining commands are here only
8936230Skarels 	 * to trap and log attempts to use them
9036230Skarels 	 */
919339Seric 	"showq",	CMDDBGQSHOW,
928544Seric 	"debug",	CMDDBGDEBUG,
938544Seric 	"kill",		CMDDBGKILL,
948544Seric 	"wiz",		CMDDBGWIZ,
954549Seric 	NULL,		CMDERROR,
964549Seric };
974549Seric 
9824955Seric # ifdef WIZ
998544Seric bool	IsWiz = FALSE;			/* set if we are a wizard */
10036230Skarels char	*WizWord;			/* the wizard word to compare against */
10124955Seric # endif WIZ
1029339Seric bool	InChild = FALSE;		/* true if running in a subprocess */
1039378Seric bool	OneXact = FALSE;		/* one xaction only this run */
10411146Seric 
1059339Seric #define EX_QUIT		22		/* special code for QUIT command */
1068544Seric 
1074549Seric smtp()
1084549Seric {
1094549Seric 	register char *p;
1108544Seric 	register struct cmd *c;
1114549Seric 	char *cmd;
1124549Seric 	extern char *skipword();
1134549Seric 	bool hasmail;			/* mail command received */
1145003Seric 	auto ADDRESS *vrfyqueue;
11512612Seric 	ADDRESS *a;
11630448Seric 	char *sendinghost;
1178544Seric 	char inp[MAXLINE];
11824981Seric 	char cmdbuf[100];
1197124Seric 	extern char Version[];
1207356Seric 	extern tick();
1218544Seric 	extern bool iswiz();
1229349Seric 	extern char *arpadate();
12311151Seric 	extern char *macvalue();
12412612Seric 	extern ADDRESS *recipient();
12524943Seric 	extern ENVELOPE BlankEnvelope;
12624943Seric 	extern ENVELOPE *newenvelope();
1274549Seric 
1285003Seric 	hasmail = FALSE;
1297363Seric 	if (OutChannel != stdout)
1307363Seric 	{
1317363Seric 		/* arrange for debugging output to go to remote host */
1327363Seric 		(void) close(1);
1337363Seric 		(void) dup(fileno(OutChannel));
1347363Seric 	}
13511931Seric 	settime();
13624971Seric 	if (RealHostName != NULL)
13725050Seric 	{
13825050Seric 		CurHostName = RealHostName;
13925050Seric 		setproctitle("srvrsmtp %s", CurHostName);
14025050Seric 	}
14125050Seric 	else
14225050Seric 	{
14325050Seric 		/* this must be us!! */
14425050Seric 		CurHostName = MyHostName;
14525050Seric 	}
14616153Seric 	expand("\001e", inp, &inp[sizeof inp], CurEnv);
14710708Seric 	message("220", inp);
14824943Seric 	SmtpPhase = "startup";
14930448Seric 	sendinghost = NULL;
1504549Seric 	for (;;)
1514549Seric 	{
15212612Seric 		/* arrange for backout */
15312612Seric 		if (setjmp(TopFrame) > 0 && InChild)
15412612Seric 			finis();
15512612Seric 		QuickAbort = FALSE;
15612612Seric 		HoldErrs = FALSE;
15712612Seric 
1587356Seric 		/* setup for the read */
1596907Seric 		CurEnv->e_to = NULL;
1604577Seric 		Errors = 0;
1617275Seric 		(void) fflush(stdout);
1627356Seric 
1637356Seric 		/* read the input line */
1647685Seric 		p = sfgets(inp, sizeof inp, InChannel);
1657356Seric 
1667685Seric 		/* handle errors */
1677356Seric 		if (p == NULL)
1687356Seric 		{
1694549Seric 			/* end of file, just die */
17036230Skarels 			message("421", "%s Lost input channel from %s",
17125050Seric 				MyHostName, CurHostName);
1724549Seric 			finis();
1734549Seric 		}
1744549Seric 
1754549Seric 		/* clean up end of line */
1764558Seric 		fixcrlf(inp, TRUE);
1774549Seric 
1784713Seric 		/* echo command to transcript */
1799545Seric 		if (CurEnv->e_xfp != NULL)
1809545Seric 			fprintf(CurEnv->e_xfp, "<<< %s\n", inp);
1814713Seric 
1824549Seric 		/* break off command */
1834549Seric 		for (p = inp; isspace(*p); p++)
1844549Seric 			continue;
1854549Seric 		cmd = p;
18624981Seric 		for (cmd = cmdbuf; *p != '\0' && !isspace(*p); )
18724981Seric 			*cmd++ = *p++;
18824981Seric 		*cmd = '\0';
1894549Seric 
19025691Seric 		/* throw away leading whitespace */
19125691Seric 		while (isspace(*p))
19225691Seric 			p++;
19325691Seric 
1944549Seric 		/* decode command */
1954549Seric 		for (c = CmdTab; c->cmdname != NULL; c++)
1964549Seric 		{
19733725Sbostic 			if (!strcasecmp(c->cmdname, cmdbuf))
1984549Seric 				break;
1994549Seric 		}
2004549Seric 
2014549Seric 		/* process command */
2024549Seric 		switch (c->cmdcode)
2034549Seric 		{
2044976Seric 		  case CMDHELO:		/* hello -- introduce yourself */
20524943Seric 			SmtpPhase = "HELO";
20625050Seric 			setproctitle("%s: %s", CurHostName, inp);
20733725Sbostic 			if (!strcasecmp(p, MyHostName))
20814877Seric 			{
20936230Skarels 				/*
21036230Skarels 				 * didn't know about alias,
21136230Skarels 				 * or connected to an echo server
21236230Skarels 				 */
21336230Skarels 				message("553", "Local configuration error, hostname not recognized as local");
21414877Seric 				break;
21514877Seric 			}
21633725Sbostic 			if (RealHostName != NULL && strcasecmp(p, RealHostName))
21711146Seric 			{
21824981Seric 				char hostbuf[MAXNAME];
21911146Seric 
22024981Seric 				(void) sprintf(hostbuf, "%s (%s)", p, RealHostName);
22130448Seric 				sendinghost = newstr(hostbuf);
22211146Seric 			}
22311146Seric 			else
22430448Seric 				sendinghost = newstr(p);
2254997Seric 			message("250", "%s Hello %s, pleased to meet you",
22636230Skarels 				MyHostName, sendinghost);
2274976Seric 			break;
2284976Seric 
2294549Seric 		  case CMDMAIL:		/* mail -- designate sender */
23024943Seric 			SmtpPhase = "MAIL";
23124943Seric 
23211151Seric 			/* force a sending host even if no HELO given */
23311151Seric 			if (RealHostName != NULL && macvalue('s', CurEnv) == NULL)
23430448Seric 				sendinghost = RealHostName;
23511151Seric 
2369314Seric 			/* check for validity of this command */
2374558Seric 			if (hasmail)
2384558Seric 			{
2394558Seric 				message("503", "Sender already specified");
2404558Seric 				break;
2414558Seric 			}
2429339Seric 			if (InChild)
2439339Seric 			{
24436230Skarels 				errno = 0;
2459339Seric 				syserr("Nested MAIL command");
2469339Seric 				exit(0);
2479339Seric 			}
2489339Seric 
2499339Seric 			/* fork a subprocess to process this command */
2509339Seric 			if (runinchild("SMTP-MAIL") > 0)
2519339Seric 				break;
25230448Seric 			define('s', sendinghost, CurEnv);
2539339Seric 			initsys();
25425016Seric 			setproctitle("%s %s: %s", CurEnv->e_id,
25525050Seric 				CurHostName, inp);
2569339Seric 
2579339Seric 			/* child -- go do the processing */
2584549Seric 			p = skipword(p, "from");
2594549Seric 			if (p == NULL)
2604549Seric 				break;
2614549Seric 			setsender(p);
2624577Seric 			if (Errors == 0)
2634549Seric 			{
2644549Seric 				message("250", "Sender ok");
2654549Seric 				hasmail = TRUE;
2664549Seric 			}
2679339Seric 			else if (InChild)
2689339Seric 				finis();
2694549Seric 			break;
2704549Seric 
2714976Seric 		  case CMDRCPT:		/* rcpt -- designate recipient */
27224943Seric 			SmtpPhase = "RCPT";
27325016Seric 			setproctitle("%s %s: %s", CurEnv->e_id,
27425050Seric 				CurHostName, inp);
27512612Seric 			if (setjmp(TopFrame) > 0)
27614785Seric 			{
27714785Seric 				CurEnv->e_flags &= ~EF_FATALERRS;
27812612Seric 				break;
27914785Seric 			}
28012612Seric 			QuickAbort = TRUE;
2814549Seric 			p = skipword(p, "to");
2824549Seric 			if (p == NULL)
2834549Seric 				break;
28416140Seric 			a = parseaddr(p, (ADDRESS *) NULL, 1, '\0');
28512612Seric 			if (a == NULL)
28612612Seric 				break;
28716886Seric 			a->q_flags |= QPRIMARY;
28812612Seric 			a = recipient(a, &CurEnv->e_sendqueue);
28912612Seric 			if (Errors != 0)
29012612Seric 				break;
29112612Seric 
29212612Seric 			/* no errors during parsing, but might be a duplicate */
29312612Seric 			CurEnv->e_to = p;
29412612Seric 			if (!bitset(QBADADDR, a->q_flags))
29512612Seric 				message("250", "Recipient ok");
29612612Seric 			else
2974549Seric 			{
29812612Seric 				/* punt -- should keep message in ADDRESS.... */
29912612Seric 				message("550", "Addressee unknown");
3004549Seric 			}
30112612Seric 			CurEnv->e_to = NULL;
3024549Seric 			break;
3034549Seric 
3044549Seric 		  case CMDDATA:		/* data -- text of mail */
30524943Seric 			SmtpPhase = "DATA";
3064976Seric 			if (!hasmail)
3074549Seric 			{
3084976Seric 				message("503", "Need MAIL command");
3094976Seric 				break;
3104549Seric 			}
31124943Seric 			else if (CurEnv->e_nrcpts <= 0)
3124549Seric 			{
3134976Seric 				message("503", "Need RCPT (recipient)");
3144976Seric 				break;
3154549Seric 			}
3164976Seric 
3174976Seric 			/* collect the text of the message */
31824943Seric 			SmtpPhase = "collect";
31925016Seric 			setproctitle("%s %s: %s", CurEnv->e_id,
32025050Seric 				CurHostName, inp);
3214976Seric 			collect(TRUE);
3224976Seric 			if (Errors != 0)
3234976Seric 				break;
3244976Seric 
3258238Seric 			/*
3268238Seric 			**  Arrange to send to everyone.
3278238Seric 			**	If sending to multiple people, mail back
3288238Seric 			**		errors rather than reporting directly.
3298238Seric 			**	In any case, don't mail back errors for
3308238Seric 			**		anything that has happened up to
3318238Seric 			**		now (the other end will do this).
33210197Seric 			**	Truncate our transcript -- the mail has gotten
33310197Seric 			**		to us successfully, and if we have
33410197Seric 			**		to mail this back, it will be easier
33510197Seric 			**		on the reader.
3368238Seric 			**	Then send to everyone.
3378238Seric 			**	Finally give a reply code.  If an error has
3388238Seric 			**		already been given, don't mail a
3398238Seric 			**		message back.
3409339Seric 			**	We goose error returns by clearing error bit.
3418238Seric 			*/
3428238Seric 
34324943Seric 			SmtpPhase = "delivery";
34424943Seric 			if (CurEnv->e_nrcpts != 1)
3459378Seric 			{
3469378Seric 				HoldErrs = TRUE;
34716886Seric 				ErrorMode = EM_MAIL;
3489378Seric 			}
3499339Seric 			CurEnv->e_flags &= ~EF_FATALERRS;
35010197Seric 			CurEnv->e_xfp = freopen(queuename(CurEnv, 'x'), "w", CurEnv->e_xfp);
3514976Seric 
3524976Seric 			/* send to all recipients */
35314877Seric 			sendall(CurEnv, SM_DEFAULT);
3546907Seric 			CurEnv->e_to = NULL;
3554976Seric 
35623516Seric 			/* save statistics */
35723516Seric 			markstats(CurEnv, (ADDRESS *) NULL);
35823516Seric 
3598238Seric 			/* issue success if appropriate and reset */
3608238Seric 			if (Errors == 0 || HoldErrs)
3619283Seric 				message("250", "Ok");
3628238Seric 			else
3639339Seric 				CurEnv->e_flags &= ~EF_FATALERRS;
3649339Seric 
3659339Seric 			/* if in a child, pop back to our parent */
3669339Seric 			if (InChild)
3679339Seric 				finis();
36824943Seric 
36924943Seric 			/* clean up a bit */
37024943Seric 			hasmail = 0;
37124943Seric 			dropenvelope(CurEnv);
37224943Seric 			CurEnv = newenvelope(CurEnv);
37324943Seric 			CurEnv->e_flags = BlankEnvelope.e_flags;
3744549Seric 			break;
3754549Seric 
3764549Seric 		  case CMDRSET:		/* rset -- reset state */
3774549Seric 			message("250", "Reset state");
3789339Seric 			if (InChild)
3799339Seric 				finis();
3809339Seric 			break;
3814549Seric 
3824549Seric 		  case CMDVRFY:		/* vrfy -- verify address */
3839339Seric 			if (runinchild("SMTP-VRFY") > 0)
3849339Seric 				break;
38525050Seric 			setproctitle("%s: %s", CurHostName, inp);
3865003Seric 			vrfyqueue = NULL;
3877762Seric 			QuickAbort = TRUE;
3889619Seric 			sendtolist(p, (ADDRESS *) NULL, &vrfyqueue);
3897762Seric 			if (Errors != 0)
3909339Seric 			{
3919339Seric 				if (InChild)
3929339Seric 					finis();
3937762Seric 				break;
3949339Seric 			}
3955003Seric 			while (vrfyqueue != NULL)
3965003Seric 			{
3975003Seric 				register ADDRESS *a = vrfyqueue->q_next;
3985003Seric 				char *code;
3995003Seric 
4007685Seric 				while (a != NULL && bitset(QDONTSEND|QBADADDR, a->q_flags))
4015003Seric 					a = a->q_next;
4025003Seric 
4037685Seric 				if (!bitset(QDONTSEND|QBADADDR, vrfyqueue->q_flags))
4045003Seric 				{
4055003Seric 					if (a != NULL)
4065003Seric 						code = "250-";
4075003Seric 					else
4085003Seric 						code = "250";
4095003Seric 					if (vrfyqueue->q_fullname == NULL)
4105003Seric 						message(code, "<%s>", vrfyqueue->q_paddr);
4115003Seric 					else
4125003Seric 						message(code, "%s <%s>",
4135003Seric 						    vrfyqueue->q_fullname, vrfyqueue->q_paddr);
4145003Seric 				}
4155003Seric 				else if (a == NULL)
4165003Seric 					message("554", "Self destructive alias loop");
4175003Seric 				vrfyqueue = a;
4185003Seric 			}
4199339Seric 			if (InChild)
4209339Seric 				finis();
4214549Seric 			break;
4224549Seric 
4234549Seric 		  case CMDHELP:		/* help -- give user info */
4244577Seric 			if (*p == '\0')
4254577Seric 				p = "SMTP";
4264577Seric 			help(p);
4274549Seric 			break;
4284549Seric 
4294549Seric 		  case CMDNOOP:		/* noop -- do nothing */
4304549Seric 			message("200", "OK");
4314549Seric 			break;
4324549Seric 
4334549Seric 		  case CMDQUIT:		/* quit -- leave mail */
43425050Seric 			message("221", "%s closing connection", MyHostName);
4359339Seric 			if (InChild)
4369339Seric 				ExitStat = EX_QUIT;
4374549Seric 			finis();
4384549Seric 
4398544Seric 		  case CMDVERB:		/* set verbose mode */
4408544Seric 			Verbose = TRUE;
44125025Seric 			SendMode = SM_DELIVER;
4428544Seric 			message("200", "Verbose mode");
4438544Seric 			break;
4448544Seric 
4459314Seric 		  case CMDONEX:		/* doing one transaction only */
4469378Seric 			OneXact = TRUE;
4479314Seric 			message("200", "Only one transaction");
4489314Seric 			break;
4499314Seric 
45036230Skarels # ifdef SMTPDEBUG
4519339Seric 		  case CMDDBGQSHOW:	/* show queues */
4526907Seric 			printf("Send Queue=");
4536907Seric 			printaddr(CurEnv->e_sendqueue, TRUE);
4545003Seric 			break;
4557275Seric 
4567275Seric 		  case CMDDBGDEBUG:	/* set debug mode */
4577676Seric 			tTsetup(tTdvect, sizeof tTdvect, "0-99.1");
4587676Seric 			tTflag(p);
4597676Seric 			message("200", "Debug set");
4607275Seric 			break;
4617275Seric 
46224945Seric # ifdef WIZ
4637282Seric 		  case CMDDBGKILL:	/* kill the parent */
4648544Seric 			if (!iswiz())
4658544Seric 				break;
4667282Seric 			if (kill(MotherPid, SIGTERM) >= 0)
4677282Seric 				message("200", "Mother is dead");
4687282Seric 			else
4697282Seric 				message("500", "Can't kill Mom");
4707282Seric 			break;
4718544Seric 
4728544Seric 		  case CMDDBGWIZ:	/* become a wizard */
4738544Seric 			if (WizWord != NULL)
4748544Seric 			{
4758544Seric 				char seed[3];
4768544Seric 				extern char *crypt();
4778544Seric 
47823106Seric 				(void) strncpy(seed, WizWord, 2);
47915596Seric 				if (strcmp(WizWord, crypt(p, seed)) == 0)
4808544Seric 				{
48115596Seric 					IsWiz = TRUE;
48215596Seric 					message("200", "Please pass, oh mighty wizard");
4838544Seric 					break;
4848544Seric 				}
4858544Seric 			}
48615596Seric 			message("500", "You are no wizard!");
4878544Seric 			break;
4885003Seric 
48924945Seric # else WIZ
49024945Seric 		  case CMDDBGWIZ:	/* try to become a wizard */
49124945Seric 			message("500", "You wascal wabbit!  Wandering wizards won't win!");
49224945Seric 			break;
49324945Seric # endif WIZ
49436230Skarels # else /* not SMTPDEBUG */
49524945Seric 
49636230Skarels 		  case CMDDBGQSHOW:	/* show queues */
49736230Skarels 		  case CMDDBGDEBUG:	/* set debug mode */
49836230Skarels 		  case CMDDBGKILL:	/* kill the parent */
49936230Skarels 		  case CMDDBGWIZ:	/* become a wizard */
500*36233Skarels # ifdef LOG
501*36233Skarels 			if (RealHostName != NULL && LogLevel > 0)
50236230Skarels 				syslog(LOG_NOTICE,
50336230Skarels 				    "\"%s\" command from %s (%s)\n",
50436230Skarels 				    c->cmdname, RealHostName,
50536230Skarels 				    inet_ntoa(RealHostAddr.sin_addr));
506*36233Skarels # endif
50736230Skarels 			/* FALL THROUGH */
50836230Skarels # endif /* SMTPDEBUG */
50936230Skarels 
5104549Seric 		  case CMDERROR:	/* unknown command */
5114549Seric 			message("500", "Command unrecognized");
5124549Seric 			break;
5134549Seric 
5144549Seric 		  default:
51536230Skarels 			errno = 0;
5164549Seric 			syserr("smtp: unknown code %d", c->cmdcode);
5174549Seric 			break;
5184549Seric 		}
5194549Seric 	}
5204549Seric }
5214549Seric /*
5224549Seric **  SKIPWORD -- skip a fixed word.
5234549Seric **
5244549Seric **	Parameters:
5254549Seric **		p -- place to start looking.
5264549Seric **		w -- word to skip.
5274549Seric **
5284549Seric **	Returns:
5294549Seric **		p following w.
5304549Seric **		NULL on error.
5314549Seric **
5324549Seric **	Side Effects:
5334549Seric **		clobbers the p data area.
5344549Seric */
5354549Seric 
5364549Seric static char *
5374549Seric skipword(p, w)
5384549Seric 	register char *p;
5394549Seric 	char *w;
5404549Seric {
5414549Seric 	register char *q;
5424549Seric 
5434549Seric 	/* find beginning of word */
5444549Seric 	while (isspace(*p))
5454549Seric 		p++;
5464549Seric 	q = p;
5474549Seric 
5484549Seric 	/* find end of word */
5494549Seric 	while (*p != '\0' && *p != ':' && !isspace(*p))
5504549Seric 		p++;
5514549Seric 	while (isspace(*p))
5524549Seric 		*p++ = '\0';
5534549Seric 	if (*p != ':')
5544549Seric 	{
5554549Seric 	  syntax:
5564549Seric 		message("501", "Syntax error");
5574549Seric 		Errors++;
5584549Seric 		return (NULL);
5594549Seric 	}
5604549Seric 	*p++ = '\0';
5614549Seric 	while (isspace(*p))
5624549Seric 		p++;
5634549Seric 
5644549Seric 	/* see if the input word matches desired word */
56533725Sbostic 	if (strcasecmp(q, w))
5664549Seric 		goto syntax;
5674549Seric 
5684549Seric 	return (p);
5694549Seric }
5704577Seric /*
5714577Seric **  HELP -- implement the HELP command.
5724577Seric **
5734577Seric **	Parameters:
5744577Seric **		topic -- the topic we want help for.
5754577Seric **
5764577Seric **	Returns:
5774577Seric **		none.
5784577Seric **
5794577Seric **	Side Effects:
5804577Seric **		outputs the help file to message output.
5814577Seric */
5824577Seric 
5834577Seric help(topic)
5844577Seric 	char *topic;
5854577Seric {
5864577Seric 	register FILE *hf;
5874577Seric 	int len;
5884577Seric 	char buf[MAXLINE];
5894577Seric 	bool noinfo;
5904577Seric 
5918269Seric 	if (HelpFile == NULL || (hf = fopen(HelpFile, "r")) == NULL)
5924577Seric 	{
5934577Seric 		/* no help */
59411931Seric 		errno = 0;
5954577Seric 		message("502", "HELP not implemented");
5964577Seric 		return;
5974577Seric 	}
5984577Seric 
5994577Seric 	len = strlen(topic);
6004577Seric 	makelower(topic);
6014577Seric 	noinfo = TRUE;
6024577Seric 
6034577Seric 	while (fgets(buf, sizeof buf, hf) != NULL)
6044577Seric 	{
6054577Seric 		if (strncmp(buf, topic, len) == 0)
6064577Seric 		{
6074577Seric 			register char *p;
6084577Seric 
6094577Seric 			p = index(buf, '\t');
6104577Seric 			if (p == NULL)
6114577Seric 				p = buf;
6124577Seric 			else
6134577Seric 				p++;
6144577Seric 			fixcrlf(p, TRUE);
6154577Seric 			message("214-", p);
6164577Seric 			noinfo = FALSE;
6174577Seric 		}
6184577Seric 	}
6194577Seric 
6204577Seric 	if (noinfo)
6214577Seric 		message("504", "HELP topic unknown");
6224577Seric 	else
6234577Seric 		message("214", "End of HELP info");
6244628Seric 	(void) fclose(hf);
6254577Seric }
6268544Seric /*
6278544Seric **  ISWIZ -- tell us if we are a wizard
6288544Seric **
6298544Seric **	If not, print a nasty message.
6308544Seric **
6318544Seric **	Parameters:
6328544Seric **		none.
6338544Seric **
6348544Seric **	Returns:
6358544Seric **		TRUE if we are a wizard.
6368544Seric **		FALSE if we are not a wizard.
6378544Seric **
6388544Seric **	Side Effects:
6398544Seric **		Prints a 500 exit stat if we are not a wizard.
6408544Seric */
6415181Seric 
64224945Seric #ifdef WIZ
64319038Seric 
6448544Seric bool
6458544Seric iswiz()
6468544Seric {
6478544Seric 	if (!IsWiz)
6488544Seric 		message("500", "Mere mortals musn't mutter that mantra");
6498544Seric 	return (IsWiz);
6508544Seric }
65119038Seric 
65224945Seric #endif WIZ
6539339Seric /*
6549339Seric **  RUNINCHILD -- return twice -- once in the child, then in the parent again
6559339Seric **
6569339Seric **	Parameters:
6579339Seric **		label -- a string used in error messages
6589339Seric **
6599339Seric **	Returns:
6609339Seric **		zero in the child
6619339Seric **		one in the parent
6629339Seric **
6639339Seric **	Side Effects:
6649339Seric **		none.
6659339Seric */
6668544Seric 
6679339Seric runinchild(label)
6689339Seric 	char *label;
6699339Seric {
6709339Seric 	int childpid;
6719339Seric 
67216158Seric 	if (!OneXact)
6739339Seric 	{
67416158Seric 		childpid = dofork();
67516158Seric 		if (childpid < 0)
67616158Seric 		{
67716158Seric 			syserr("%s: cannot fork", label);
67816158Seric 			return (1);
67916158Seric 		}
68016158Seric 		if (childpid > 0)
68116158Seric 		{
68216158Seric 			auto int st;
6839339Seric 
68416158Seric 			/* parent -- wait for child to complete */
68516158Seric 			st = waitfor(childpid);
68616158Seric 			if (st == -1)
68716158Seric 				syserr("%s: lost child", label);
6889339Seric 
68916158Seric 			/* if we exited on a QUIT command, complete the process */
69016158Seric 			if (st == (EX_QUIT << 8))
69116158Seric 				finis();
6929339Seric 
69316158Seric 			return (1);
69416158Seric 		}
69516158Seric 		else
69616158Seric 		{
69716158Seric 			/* child */
69816158Seric 			InChild = TRUE;
69925050Seric 			QuickAbort = FALSE;
70025614Seric 			clearenvelope(CurEnv, FALSE);
70116158Seric 		}
7029339Seric 	}
70315256Seric 
70416158Seric 	/* open alias database */
70516158Seric 	initaliases(AliasFile, FALSE);
70616158Seric 
70716158Seric 	return (0);
7089339Seric }
7099339Seric 
7105181Seric # endif SMTP
711