122712Sdist /*
234921Sbostic  * Copyright (c) 1983 Eric P. Allman
333731Sbostic  * Copyright (c) 1988 Regents of the University of California.
433731Sbostic  * All rights reserved.
533731Sbostic  *
642829Sbostic  * %sccs.include.redist.c%
733731Sbostic  */
822712Sdist 
933731Sbostic # include "sendmail.h"
1022712Sdist 
1133731Sbostic #ifndef lint
1233731Sbostic #ifdef SMTP
13*58093Seric static char sccsid[] = "@(#)srvrsmtp.c	6.13 (Berkeley) 02/20/93 (with SMTP)";
1433731Sbostic #else
15*58093Seric static char sccsid[] = "@(#)srvrsmtp.c	6.13 (Berkeley) 02/20/93 (without SMTP)";
1633731Sbostic #endif
1733731Sbostic #endif /* not lint */
1833731Sbostic 
199339Seric # include <errno.h>
2011728Seric # include <signal.h>
214549Seric 
2233731Sbostic # ifdef SMTP
234556Seric 
244549Seric /*
254549Seric **  SMTP -- run the SMTP protocol.
264549Seric **
274549Seric **	Parameters:
284549Seric **		none.
294549Seric **
304549Seric **	Returns:
314549Seric **		never.
324549Seric **
334549Seric **	Side Effects:
344549Seric **		Reads commands from the input channel and processes
354549Seric **			them.
364549Seric */
374549Seric 
384549Seric struct cmd
394549Seric {
404549Seric 	char	*cmdname;	/* command name */
414549Seric 	int	cmdcode;	/* internal code, see below */
424549Seric };
434549Seric 
444549Seric /* values for cmdcode */
454549Seric # define CMDERROR	0	/* bad command */
464549Seric # define CMDMAIL	1	/* mail -- designate sender */
474976Seric # define CMDRCPT	2	/* rcpt -- designate recipient */
484549Seric # define CMDDATA	3	/* data -- send message text */
499339Seric # define CMDRSET	4	/* rset -- reset state */
509339Seric # define CMDVRFY	5	/* vrfy -- verify address */
5158092Seric # define CMDEXPN	6	/* expn -- expand address */
529339Seric # define CMDNOOP	7	/* noop -- do nothing */
539339Seric # define CMDQUIT	8	/* quit -- close connection and die */
549339Seric # define CMDHELO	9	/* helo -- be polite */
5558092Seric # define CMDHELP	10	/* help -- give usage info */
5658092Seric /* non-standard commands */
5758092Seric # define CMDONEX	16	/* onex -- sending one transaction only */
5858092Seric # define CMDVERB	17	/* verb -- go into verbose mode */
5936230Skarels /* debugging-only commands, only enabled if SMTPDEBUG is defined */
6058092Seric # define CMDDBGQSHOW	24	/* showq -- show send queue */
6158092Seric # define CMDDBGDEBUG	25	/* debug -- set debug mode */
624549Seric 
634549Seric static struct cmd	CmdTab[] =
644549Seric {
654549Seric 	"mail",		CMDMAIL,
664976Seric 	"rcpt",		CMDRCPT,
674549Seric 	"data",		CMDDATA,
684549Seric 	"rset",		CMDRSET,
694549Seric 	"vrfy",		CMDVRFY,
7058092Seric 	"expn",		CMDEXPN,
714549Seric 	"help",		CMDHELP,
724549Seric 	"noop",		CMDNOOP,
734549Seric 	"quit",		CMDQUIT,
744976Seric 	"helo",		CMDHELO,
758544Seric 	"verb",		CMDVERB,
769314Seric 	"onex",		CMDONEX,
7736230Skarels 	/*
7836230Skarels 	 * remaining commands are here only
7936230Skarels 	 * to trap and log attempts to use them
8036230Skarels 	 */
819339Seric 	"showq",	CMDDBGQSHOW,
828544Seric 	"debug",	CMDDBGDEBUG,
834549Seric 	NULL,		CMDERROR,
844549Seric };
854549Seric 
869339Seric bool	InChild = FALSE;		/* true if running in a subprocess */
879378Seric bool	OneXact = FALSE;		/* one xaction only this run */
8811146Seric 
899339Seric #define EX_QUIT		22		/* special code for QUIT command */
908544Seric 
9155012Seric smtp(e)
9255012Seric 	register ENVELOPE *e;
934549Seric {
944549Seric 	register char *p;
958544Seric 	register struct cmd *c;
964549Seric 	char *cmd;
9746928Sbostic 	static char *skipword();
985003Seric 	auto ADDRESS *vrfyqueue;
9912612Seric 	ADDRESS *a;
10030448Seric 	char *sendinghost;
10158092Seric 	bool hasmail;			/* mail command received */
10258092Seric 	bool gothello;			/* helo command received */
10358092Seric 	bool vrfy;			/* set if this is a vrfy command */
1048544Seric 	char inp[MAXLINE];
10557232Seric 	char cmdbuf[MAXLINE];
1067124Seric 	extern char Version[];
10711151Seric 	extern char *macvalue();
10812612Seric 	extern ADDRESS *recipient();
10924943Seric 	extern ENVELOPE BlankEnvelope;
11024943Seric 	extern ENVELOPE *newenvelope();
1114549Seric 
1125003Seric 	hasmail = FALSE;
1137363Seric 	if (OutChannel != stdout)
1147363Seric 	{
1157363Seric 		/* arrange for debugging output to go to remote host */
1167363Seric 		(void) close(1);
1177363Seric 		(void) dup(fileno(OutChannel));
1187363Seric 	}
11955012Seric 	settime(e);
12057642Seric 	if (RealHostName == NULL)
12157642Seric 		RealHostName = MyHostName;
12257642Seric 	CurHostName = RealHostName;
12357642Seric 	setproctitle("srvrsmtp %s", CurHostName);
12458050Seric 	expand("\201e", inp, &inp[sizeof inp], e);
12555360Seric 	message("220", "%s", inp);
12624943Seric 	SmtpPhase = "startup";
12730448Seric 	sendinghost = NULL;
12858082Seric 	gothello = FALSE;
1294549Seric 	for (;;)
1304549Seric 	{
13112612Seric 		/* arrange for backout */
13212612Seric 		if (setjmp(TopFrame) > 0 && InChild)
13312612Seric 			finis();
13412612Seric 		QuickAbort = FALSE;
13512612Seric 		HoldErrs = FALSE;
13651951Seric 		LogUsrErrs = FALSE;
13758092Seric 		e->e_flags &= ~EF_VRFYONLY;
13812612Seric 
1397356Seric 		/* setup for the read */
14055012Seric 		e->e_to = NULL;
1414577Seric 		Errors = 0;
1427275Seric 		(void) fflush(stdout);
1437356Seric 
1447356Seric 		/* read the input line */
14557389Seric 		p = sfgets(inp, sizeof inp, InChannel, ReadTimeout);
1467356Seric 
1477685Seric 		/* handle errors */
1487356Seric 		if (p == NULL)
1497356Seric 		{
1504549Seric 			/* end of file, just die */
15136230Skarels 			message("421", "%s Lost input channel from %s",
15225050Seric 				MyHostName, CurHostName);
15355464Seric #ifdef LOG
15458020Seric 			if (LogLevel > 1)
15555464Seric 				syslog(LOG_NOTICE, "lost input channel from %s",
15655464Seric 					CurHostName);
15755464Seric #endif
15858069Seric 			if (InChild)
15958069Seric 				ExitStat = EX_QUIT;
1604549Seric 			finis();
1614549Seric 		}
1624549Seric 
1634549Seric 		/* clean up end of line */
1644558Seric 		fixcrlf(inp, TRUE);
1654549Seric 
1664713Seric 		/* echo command to transcript */
16755012Seric 		if (e->e_xfp != NULL)
16855012Seric 			fprintf(e->e_xfp, "<<< %s\n", inp);
1694713Seric 
1704549Seric 		/* break off command */
17158050Seric 		for (p = inp; isascii(*p) && isspace(*p); p++)
1724549Seric 			continue;
17357232Seric 		cmd = cmdbuf;
17458050Seric 		while (*p != '\0' &&
17558050Seric 		       !(isascii(*p) && isspace(*p)) &&
17658050Seric 		       cmd < &cmdbuf[sizeof cmdbuf - 2])
17724981Seric 			*cmd++ = *p++;
17824981Seric 		*cmd = '\0';
1794549Seric 
18025691Seric 		/* throw away leading whitespace */
18158050Seric 		while (isascii(*p) && isspace(*p))
18225691Seric 			p++;
18325691Seric 
1844549Seric 		/* decode command */
1854549Seric 		for (c = CmdTab; c->cmdname != NULL; c++)
1864549Seric 		{
18733725Sbostic 			if (!strcasecmp(c->cmdname, cmdbuf))
1884549Seric 				break;
1894549Seric 		}
1904549Seric 
19151954Seric 		/* reset errors */
19251954Seric 		errno = 0;
19351954Seric 
1944549Seric 		/* process command */
1954549Seric 		switch (c->cmdcode)
1964549Seric 		{
1974976Seric 		  case CMDHELO:		/* hello -- introduce yourself */
19824943Seric 			SmtpPhase = "HELO";
19925050Seric 			setproctitle("%s: %s", CurHostName, inp);
20033725Sbostic 			if (!strcasecmp(p, MyHostName))
20114877Seric 			{
20236230Skarels 				/*
20336230Skarels 				 * didn't know about alias,
20436230Skarels 				 * or connected to an echo server
20536230Skarels 				 */
20647570Seric 				message("553", "%s config error: mail loops back to myself",
20747570Seric 					MyHostName);
20814877Seric 				break;
20914877Seric 			}
21033725Sbostic 			if (RealHostName != NULL && strcasecmp(p, RealHostName))
21111146Seric 			{
21224981Seric 				char hostbuf[MAXNAME];
21311146Seric 
21424981Seric 				(void) sprintf(hostbuf, "%s (%s)", p, RealHostName);
21530448Seric 				sendinghost = newstr(hostbuf);
21611146Seric 			}
21711146Seric 			else
21830448Seric 				sendinghost = newstr(p);
2194997Seric 			message("250", "%s Hello %s, pleased to meet you",
22036230Skarels 				MyHostName, sendinghost);
22158082Seric 			gothello = TRUE;
2224976Seric 			break;
2234976Seric 
2244549Seric 		  case CMDMAIL:		/* mail -- designate sender */
22524943Seric 			SmtpPhase = "MAIL";
22624943Seric 
22711151Seric 			/* force a sending host even if no HELO given */
22858064Seric 			if (sendinghost == NULL && macvalue('s', e) == NULL)
22930448Seric 				sendinghost = RealHostName;
23011151Seric 
2319314Seric 			/* check for validity of this command */
23258082Seric 			if (!gothello && bitset(PRIV_NEEDMAILHELO, PrivacyFlags))
23358082Seric 			{
23458082Seric 				message("503", "Polite people say HELO first");
23558082Seric 				break;
23658082Seric 			}
2374558Seric 			if (hasmail)
2384558Seric 			{
2394558Seric 				message("503", "Sender already specified");
2404558Seric 				break;
2414558Seric 			}
2429339Seric 			if (InChild)
2439339Seric 			{
24436230Skarels 				errno = 0;
24558008Seric 				syserr("Nested MAIL command: MAIL %s", p);
24658069Seric 				finis();
2479339Seric 			}
24858082Seric 			if (!enoughspace())
24958082Seric 			{
25058082Seric 				message("452", "Insufficient disk space; try again later");
25158082Seric 				break;
25258082Seric 			}
2539339Seric 
2549339Seric 			/* fork a subprocess to process this command */
25555012Seric 			if (runinchild("SMTP-MAIL", e) > 0)
2569339Seric 				break;
25758064Seric 			if (sendinghost != NULL)
25858064Seric 				define('s', sendinghost, e);
25955012Seric 			define('r', "SMTP", e);
26055012Seric 			initsys(e);
26157389Seric 			setproctitle("%s %s: %s", e->e_id, CurHostName, inp);
2629339Seric 
2639339Seric 			/* child -- go do the processing */
2644549Seric 			p = skipword(p, "from");
2654549Seric 			if (p == NULL)
2664549Seric 				break;
26757977Seric 			if (setjmp(TopFrame) > 0)
26857977Seric 				break;
26957977Seric 			QuickAbort = TRUE;
27055012Seric 			setsender(p, e);
2714577Seric 			if (Errors == 0)
2724549Seric 			{
2734549Seric 				message("250", "Sender ok");
2744549Seric 				hasmail = TRUE;
2754549Seric 			}
2769339Seric 			else if (InChild)
2779339Seric 				finis();
2784549Seric 			break;
2794549Seric 
2804976Seric 		  case CMDRCPT:		/* rcpt -- designate recipient */
28124943Seric 			SmtpPhase = "RCPT";
28257389Seric 			setproctitle("%s %s: %s", e->e_id, CurHostName, inp);
28312612Seric 			if (setjmp(TopFrame) > 0)
28414785Seric 			{
28555012Seric 				e->e_flags &= ~EF_FATALERRS;
28612612Seric 				break;
28714785Seric 			}
28812612Seric 			QuickAbort = TRUE;
28951951Seric 			LogUsrErrs = TRUE;
290*58093Seric 
291*58093Seric 			/* optimization -- if queueing, don't expand aliases */
292*58093Seric 			if (SendMode == SM_QUEUE)
293*58093Seric 				e->e_flags |= EF_VRFYONLY;
294*58093Seric 
2954549Seric 			p = skipword(p, "to");
2964549Seric 			if (p == NULL)
2974549Seric 				break;
29855012Seric 			a = parseaddr(p, (ADDRESS *) NULL, 1, '\0', e);
29912612Seric 			if (a == NULL)
30012612Seric 				break;
30116886Seric 			a->q_flags |= QPRIMARY;
30255012Seric 			a = recipient(a, &e->e_sendqueue, e);
30312612Seric 			if (Errors != 0)
30412612Seric 				break;
30512612Seric 
30612612Seric 			/* no errors during parsing, but might be a duplicate */
30755012Seric 			e->e_to = p;
30812612Seric 			if (!bitset(QBADADDR, a->q_flags))
30912612Seric 				message("250", "Recipient ok");
31012612Seric 			else
3114549Seric 			{
31212612Seric 				/* punt -- should keep message in ADDRESS.... */
31312612Seric 				message("550", "Addressee unknown");
3144549Seric 			}
31555012Seric 			e->e_to = NULL;
3164549Seric 			break;
3174549Seric 
3184549Seric 		  case CMDDATA:		/* data -- text of mail */
31924943Seric 			SmtpPhase = "DATA";
3204976Seric 			if (!hasmail)
3214549Seric 			{
3224976Seric 				message("503", "Need MAIL command");
3234976Seric 				break;
3244549Seric 			}
32555012Seric 			else if (e->e_nrcpts <= 0)
3264549Seric 			{
3274976Seric 				message("503", "Need RCPT (recipient)");
3284976Seric 				break;
3294549Seric 			}
3304976Seric 
3314976Seric 			/* collect the text of the message */
33224943Seric 			SmtpPhase = "collect";
33357389Seric 			setproctitle("%s %s: %s", e->e_id, CurHostName, inp);
33455012Seric 			collect(TRUE, e);
3354976Seric 			if (Errors != 0)
3364976Seric 				break;
3374976Seric 
3388238Seric 			/*
3398238Seric 			**  Arrange to send to everyone.
3408238Seric 			**	If sending to multiple people, mail back
3418238Seric 			**		errors rather than reporting directly.
3428238Seric 			**	In any case, don't mail back errors for
3438238Seric 			**		anything that has happened up to
3448238Seric 			**		now (the other end will do this).
34510197Seric 			**	Truncate our transcript -- the mail has gotten
34610197Seric 			**		to us successfully, and if we have
34710197Seric 			**		to mail this back, it will be easier
34810197Seric 			**		on the reader.
3498238Seric 			**	Then send to everyone.
3508238Seric 			**	Finally give a reply code.  If an error has
3518238Seric 			**		already been given, don't mail a
3528238Seric 			**		message back.
3539339Seric 			**	We goose error returns by clearing error bit.
3548238Seric 			*/
3558238Seric 
35624943Seric 			SmtpPhase = "delivery";
35755012Seric 			if (e->e_nrcpts != 1)
3589378Seric 			{
3599378Seric 				HoldErrs = TRUE;
36016886Seric 				ErrorMode = EM_MAIL;
3619378Seric 			}
36255012Seric 			e->e_flags &= ~EF_FATALERRS;
36355012Seric 			e->e_xfp = freopen(queuename(e, 'x'), "w", e->e_xfp);
3644976Seric 
3654976Seric 			/* send to all recipients */
36655012Seric 			sendall(e, SM_DEFAULT);
36755012Seric 			e->e_to = NULL;
3684976Seric 
36923516Seric 			/* save statistics */
37055012Seric 			markstats(e, (ADDRESS *) NULL);
37123516Seric 
3728238Seric 			/* issue success if appropriate and reset */
3738238Seric 			if (Errors == 0 || HoldErrs)
3749283Seric 				message("250", "Ok");
3758238Seric 			else
37655012Seric 				e->e_flags &= ~EF_FATALERRS;
3779339Seric 
3789339Seric 			/* if in a child, pop back to our parent */
3799339Seric 			if (InChild)
3809339Seric 				finis();
38124943Seric 
38224943Seric 			/* clean up a bit */
38358008Seric 			hasmail = FALSE;
38455012Seric 			dropenvelope(e);
38555012Seric 			CurEnv = e = newenvelope(e);
38655012Seric 			e->e_flags = BlankEnvelope.e_flags;
3874549Seric 			break;
3884549Seric 
3894549Seric 		  case CMDRSET:		/* rset -- reset state */
3904549Seric 			message("250", "Reset state");
3919339Seric 			if (InChild)
3929339Seric 				finis();
3939339Seric 			break;
3944549Seric 
3954549Seric 		  case CMDVRFY:		/* vrfy -- verify address */
39658092Seric 		  case CMDEXPN:		/* expn -- expand address */
39758092Seric 			vrfy = c->cmdcode == CMDVRFY;
39858092Seric 			if (bitset(vrfy ? PRIV_NOVRFY : PRIV_NOEXPN,
39958092Seric 						PrivacyFlags))
40058082Seric 			{
40158082Seric 				message("502", "That's none of your business");
40258082Seric 				break;
40358082Seric 			}
40458082Seric 			else if (!gothello &&
40558092Seric 				 bitset(vrfy ? PRIV_NEEDVRFYHELO : PRIV_NEEDEXPNHELO,
40658092Seric 						PrivacyFlags))
40758082Seric 			{
40858082Seric 				message("503", "I demand that you introduce yourself first");
40958082Seric 				break;
41058082Seric 			}
41158092Seric 			if (runinchild(vrfy ? "SMTP-VRFY" : "SMTP-EXPN", e) > 0)
4129339Seric 				break;
41325050Seric 			setproctitle("%s: %s", CurHostName, inp);
41455173Seric #ifdef LOG
41558020Seric 			if (LogLevel > 5)
41655173Seric 				syslog(LOG_INFO, "%s: %s", CurHostName, inp);
41755173Seric #endif
4185003Seric 			vrfyqueue = NULL;
4197762Seric 			QuickAbort = TRUE;
42058092Seric 			if (vrfy)
42158092Seric 				e->e_flags |= EF_VRFYONLY;
42258082Seric 			(void) sendtolist(p, (ADDRESS *) NULL, &vrfyqueue, e);
4237762Seric 			if (Errors != 0)
4249339Seric 			{
4259339Seric 				if (InChild)
4269339Seric 					finis();
4277762Seric 				break;
4289339Seric 			}
4295003Seric 			while (vrfyqueue != NULL)
4305003Seric 			{
4315003Seric 				register ADDRESS *a = vrfyqueue->q_next;
4325003Seric 				char *code;
4335003Seric 
4347685Seric 				while (a != NULL && bitset(QDONTSEND|QBADADDR, a->q_flags))
4355003Seric 					a = a->q_next;
4365003Seric 
4377685Seric 				if (!bitset(QDONTSEND|QBADADDR, vrfyqueue->q_flags))
4385003Seric 				{
4395003Seric 					if (a != NULL)
4405003Seric 						code = "250-";
4415003Seric 					else
4425003Seric 						code = "250";
44358010Seric 					if (strchr(vrfyqueue->q_paddr, '<') != NULL)
44458010Seric 						message(code, "%s", vrfyqueue->q_paddr);
44558010Seric 					else if (vrfyqueue->q_fullname == NULL)
4465003Seric 						message(code, "<%s>", vrfyqueue->q_paddr);
4475003Seric 					else
4485003Seric 						message(code, "%s <%s>",
4495003Seric 						    vrfyqueue->q_fullname, vrfyqueue->q_paddr);
4505003Seric 				}
4515003Seric 				else if (a == NULL)
4525003Seric 					message("554", "Self destructive alias loop");
4535003Seric 				vrfyqueue = a;
4545003Seric 			}
4559339Seric 			if (InChild)
4569339Seric 				finis();
4574549Seric 			break;
4584549Seric 
4594549Seric 		  case CMDHELP:		/* help -- give user info */
4604577Seric 			help(p);
4614549Seric 			break;
4624549Seric 
4634549Seric 		  case CMDNOOP:		/* noop -- do nothing */
4644549Seric 			message("200", "OK");
4654549Seric 			break;
4664549Seric 
4674549Seric 		  case CMDQUIT:		/* quit -- leave mail */
46825050Seric 			message("221", "%s closing connection", MyHostName);
4699339Seric 			if (InChild)
4709339Seric 				ExitStat = EX_QUIT;
4714549Seric 			finis();
4724549Seric 
4738544Seric 		  case CMDVERB:		/* set verbose mode */
4748544Seric 			Verbose = TRUE;
47525025Seric 			SendMode = SM_DELIVER;
4768544Seric 			message("200", "Verbose mode");
4778544Seric 			break;
4788544Seric 
4799314Seric 		  case CMDONEX:		/* doing one transaction only */
4809378Seric 			OneXact = TRUE;
4819314Seric 			message("200", "Only one transaction");
4829314Seric 			break;
4839314Seric 
48436230Skarels # ifdef SMTPDEBUG
4859339Seric 		  case CMDDBGQSHOW:	/* show queues */
4866907Seric 			printf("Send Queue=");
48755012Seric 			printaddr(e->e_sendqueue, TRUE);
4885003Seric 			break;
4897275Seric 
4907275Seric 		  case CMDDBGDEBUG:	/* set debug mode */
4917676Seric 			tTsetup(tTdvect, sizeof tTdvect, "0-99.1");
4927676Seric 			tTflag(p);
4937676Seric 			message("200", "Debug set");
4947275Seric 			break;
4957275Seric 
49636230Skarels # else /* not SMTPDEBUG */
49724945Seric 
49836230Skarels 		  case CMDDBGQSHOW:	/* show queues */
49936230Skarels 		  case CMDDBGDEBUG:	/* set debug mode */
50036233Skarels # ifdef LOG
50136233Skarels 			if (RealHostName != NULL && LogLevel > 0)
50236230Skarels 				syslog(LOG_NOTICE,
50358020Seric 				    "\"%s\" command from %s (%s)",
50436230Skarels 				    c->cmdname, RealHostName,
50536230Skarels 				    inet_ntoa(RealHostAddr.sin_addr));
50636233Skarels # 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 */
54458050Seric 	while (isascii(*p) && isspace(*p))
5454549Seric 		p++;
5464549Seric 	q = p;
5474549Seric 
5484549Seric 	/* find end of word */
54958050Seric 	while (*p != '\0' && *p != ':' && !(isascii(*p) && isspace(*p)))
5504549Seric 		p++;
55158050Seric 	while (isascii(*p) && 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';
56158050Seric 	while (isascii(*p) && 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 
59949669Seric 	if (topic == NULL || *topic == '\0')
60049669Seric 		topic = "smtp";
60149669Seric 	else
60249669Seric 		makelower(topic);
60349669Seric 
6044577Seric 	len = strlen(topic);
6054577Seric 	noinfo = TRUE;
6064577Seric 
6074577Seric 	while (fgets(buf, sizeof buf, hf) != NULL)
6084577Seric 	{
6094577Seric 		if (strncmp(buf, topic, len) == 0)
6104577Seric 		{
6114577Seric 			register char *p;
6124577Seric 
61356795Seric 			p = strchr(buf, '\t');
6144577Seric 			if (p == NULL)
6154577Seric 				p = buf;
6164577Seric 			else
6174577Seric 				p++;
6184577Seric 			fixcrlf(p, TRUE);
6194577Seric 			message("214-", p);
6204577Seric 			noinfo = FALSE;
6214577Seric 		}
6224577Seric 	}
6234577Seric 
6244577Seric 	if (noinfo)
6254577Seric 		message("504", "HELP topic unknown");
6264577Seric 	else
6274577Seric 		message("214", "End of HELP info");
6284628Seric 	(void) fclose(hf);
6294577Seric }
6308544Seric /*
6319339Seric **  RUNINCHILD -- return twice -- once in the child, then in the parent again
6329339Seric **
6339339Seric **	Parameters:
6349339Seric **		label -- a string used in error messages
6359339Seric **
6369339Seric **	Returns:
6379339Seric **		zero in the child
6389339Seric **		one in the parent
6399339Seric **
6409339Seric **	Side Effects:
6419339Seric **		none.
6429339Seric */
6438544Seric 
64455012Seric runinchild(label, e)
6459339Seric 	char *label;
64655012Seric 	register ENVELOPE *e;
6479339Seric {
6489339Seric 	int childpid;
6499339Seric 
65016158Seric 	if (!OneXact)
6519339Seric 	{
65216158Seric 		childpid = dofork();
65316158Seric 		if (childpid < 0)
65416158Seric 		{
65516158Seric 			syserr("%s: cannot fork", label);
65616158Seric 			return (1);
65716158Seric 		}
65816158Seric 		if (childpid > 0)
65916158Seric 		{
66016158Seric 			auto int st;
6619339Seric 
66216158Seric 			/* parent -- wait for child to complete */
66316158Seric 			st = waitfor(childpid);
66416158Seric 			if (st == -1)
66516158Seric 				syserr("%s: lost child", label);
6669339Seric 
66716158Seric 			/* if we exited on a QUIT command, complete the process */
66816158Seric 			if (st == (EX_QUIT << 8))
66916158Seric 				finis();
6709339Seric 
67116158Seric 			return (1);
67216158Seric 		}
67316158Seric 		else
67416158Seric 		{
67516158Seric 			/* child */
67616158Seric 			InChild = TRUE;
67725050Seric 			QuickAbort = FALSE;
67855012Seric 			clearenvelope(e, FALSE);
67916158Seric 		}
6809339Seric 	}
68115256Seric 
68216158Seric 	/* open alias database */
68355012Seric 	initaliases(AliasFile, FALSE, e);
68416158Seric 
68516158Seric 	return (0);
6869339Seric }
6879339Seric 
68856795Seric # endif /* SMTP */
689