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*58147Seric static char sccsid[] = "@(#)srvrsmtp.c	6.15 (Berkeley) 02/22/93 (with SMTP)";
1433731Sbostic #else
15*58147Seric static char sccsid[] = "@(#)srvrsmtp.c	6.15 (Berkeley) 02/22/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;
10158109Seric 	bool gotmail;			/* 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 
11258109Seric 	gotmail = 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 */
14558109Seric 		p = sfgets(inp, sizeof inp, InChannel, TimeOuts.to_nextcommand);
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);
20058109Seric 			if (strcasecmp(p, MyHostName) == 0)
20114877Seric 			{
20236230Skarels 				/*
20358109Seric 				**  Didn't know about alias or MX,
20458109Seric 				**  or connected to an echo server
20558109Seric 				*/
20658109Seric 
20747570Seric 				message("553", "%s config error: mail loops back to myself",
20847570Seric 					MyHostName);
20914877Seric 				break;
21014877Seric 			}
21158109Seric 			if (RealHostName != NULL && strcasecmp(p, RealHostName) != 0)
21211146Seric 			{
21324981Seric 				char hostbuf[MAXNAME];
21411146Seric 
21524981Seric 				(void) sprintf(hostbuf, "%s (%s)", p, RealHostName);
21630448Seric 				sendinghost = newstr(hostbuf);
21711146Seric 			}
21811146Seric 			else
21930448Seric 				sendinghost = newstr(p);
2204997Seric 			message("250", "%s Hello %s, pleased to meet you",
22136230Skarels 				MyHostName, sendinghost);
22258082Seric 			gothello = TRUE;
2234976Seric 			break;
2244976Seric 
2254549Seric 		  case CMDMAIL:		/* mail -- designate sender */
22624943Seric 			SmtpPhase = "MAIL";
22724943Seric 
22811151Seric 			/* force a sending host even if no HELO given */
22958064Seric 			if (sendinghost == NULL && macvalue('s', e) == NULL)
23030448Seric 				sendinghost = RealHostName;
23111151Seric 
2329314Seric 			/* check for validity of this command */
23358082Seric 			if (!gothello && bitset(PRIV_NEEDMAILHELO, PrivacyFlags))
23458082Seric 			{
23558082Seric 				message("503", "Polite people say HELO first");
23658082Seric 				break;
23758082Seric 			}
23858109Seric 			if (gotmail)
2394558Seric 			{
2404558Seric 				message("503", "Sender already specified");
2414558Seric 				break;
2424558Seric 			}
2439339Seric 			if (InChild)
2449339Seric 			{
24536230Skarels 				errno = 0;
24658008Seric 				syserr("Nested MAIL command: MAIL %s", p);
24758069Seric 				finis();
2489339Seric 			}
24958082Seric 			if (!enoughspace())
25058082Seric 			{
25158082Seric 				message("452", "Insufficient disk space; try again later");
25258082Seric 				break;
25358082Seric 			}
2549339Seric 
2559339Seric 			/* fork a subprocess to process this command */
25655012Seric 			if (runinchild("SMTP-MAIL", e) > 0)
2579339Seric 				break;
25858064Seric 			if (sendinghost != NULL)
25958064Seric 				define('s', sendinghost, e);
26055012Seric 			define('r', "SMTP", e);
26155012Seric 			initsys(e);
26257389Seric 			setproctitle("%s %s: %s", e->e_id, CurHostName, inp);
2639339Seric 
2649339Seric 			/* child -- go do the processing */
2654549Seric 			p = skipword(p, "from");
2664549Seric 			if (p == NULL)
2674549Seric 				break;
26857977Seric 			if (setjmp(TopFrame) > 0)
269*58147Seric 			{
270*58147Seric 				/* this failed -- undo work */
271*58147Seric 				if (InChild)
272*58147Seric 					finis();
27357977Seric 				break;
274*58147Seric 			}
27557977Seric 			QuickAbort = TRUE;
27655012Seric 			setsender(p, e);
277*58147Seric 			message("250", "Sender ok");
278*58147Seric 			gotmail = TRUE;
2794549Seric 			break;
2804549Seric 
2814976Seric 		  case CMDRCPT:		/* rcpt -- designate recipient */
28224943Seric 			SmtpPhase = "RCPT";
28357389Seric 			setproctitle("%s %s: %s", e->e_id, CurHostName, inp);
28412612Seric 			if (setjmp(TopFrame) > 0)
28514785Seric 			{
28655012Seric 				e->e_flags &= ~EF_FATALERRS;
28712612Seric 				break;
28814785Seric 			}
28912612Seric 			QuickAbort = TRUE;
29051951Seric 			LogUsrErrs = TRUE;
29158093Seric 
29258093Seric 			/* optimization -- if queueing, don't expand aliases */
29358093Seric 			if (SendMode == SM_QUEUE)
29458093Seric 				e->e_flags |= EF_VRFYONLY;
29558093Seric 
2964549Seric 			p = skipword(p, "to");
2974549Seric 			if (p == NULL)
2984549Seric 				break;
29955012Seric 			a = parseaddr(p, (ADDRESS *) NULL, 1, '\0', e);
30012612Seric 			if (a == NULL)
30112612Seric 				break;
30216886Seric 			a->q_flags |= QPRIMARY;
30355012Seric 			a = recipient(a, &e->e_sendqueue, e);
30412612Seric 			if (Errors != 0)
30512612Seric 				break;
30612612Seric 
30712612Seric 			/* no errors during parsing, but might be a duplicate */
30855012Seric 			e->e_to = p;
30912612Seric 			if (!bitset(QBADADDR, a->q_flags))
31012612Seric 				message("250", "Recipient ok");
31112612Seric 			else
3124549Seric 			{
31312612Seric 				/* punt -- should keep message in ADDRESS.... */
31412612Seric 				message("550", "Addressee unknown");
3154549Seric 			}
31655012Seric 			e->e_to = NULL;
3174549Seric 			break;
3184549Seric 
3194549Seric 		  case CMDDATA:		/* data -- text of mail */
32024943Seric 			SmtpPhase = "DATA";
32158109Seric 			if (!gotmail)
3224549Seric 			{
3234976Seric 				message("503", "Need MAIL command");
3244976Seric 				break;
3254549Seric 			}
32655012Seric 			else if (e->e_nrcpts <= 0)
3274549Seric 			{
3284976Seric 				message("503", "Need RCPT (recipient)");
3294976Seric 				break;
3304549Seric 			}
3314976Seric 
3324976Seric 			/* collect the text of the message */
33324943Seric 			SmtpPhase = "collect";
33457389Seric 			setproctitle("%s %s: %s", e->e_id, CurHostName, inp);
33555012Seric 			collect(TRUE, e);
3364976Seric 			if (Errors != 0)
3374976Seric 				break;
3384976Seric 
3398238Seric 			/*
3408238Seric 			**  Arrange to send to everyone.
3418238Seric 			**	If sending to multiple people, mail back
3428238Seric 			**		errors rather than reporting directly.
3438238Seric 			**	In any case, don't mail back errors for
3448238Seric 			**		anything that has happened up to
3458238Seric 			**		now (the other end will do this).
34610197Seric 			**	Truncate our transcript -- the mail has gotten
34710197Seric 			**		to us successfully, and if we have
34810197Seric 			**		to mail this back, it will be easier
34910197Seric 			**		on the reader.
3508238Seric 			**	Then send to everyone.
3518238Seric 			**	Finally give a reply code.  If an error has
3528238Seric 			**		already been given, don't mail a
3538238Seric 			**		message back.
3549339Seric 			**	We goose error returns by clearing error bit.
3558238Seric 			*/
3568238Seric 
35724943Seric 			SmtpPhase = "delivery";
35855012Seric 			if (e->e_nrcpts != 1)
3599378Seric 			{
3609378Seric 				HoldErrs = TRUE;
36116886Seric 				ErrorMode = EM_MAIL;
3629378Seric 			}
36355012Seric 			e->e_flags &= ~EF_FATALERRS;
36455012Seric 			e->e_xfp = freopen(queuename(e, 'x'), "w", e->e_xfp);
3654976Seric 
3664976Seric 			/* send to all recipients */
36755012Seric 			sendall(e, SM_DEFAULT);
36855012Seric 			e->e_to = NULL;
3694976Seric 
37023516Seric 			/* save statistics */
37155012Seric 			markstats(e, (ADDRESS *) NULL);
37223516Seric 
3738238Seric 			/* issue success if appropriate and reset */
3748238Seric 			if (Errors == 0 || HoldErrs)
3759283Seric 				message("250", "Ok");
3768238Seric 			else
37755012Seric 				e->e_flags &= ~EF_FATALERRS;
3789339Seric 
3799339Seric 			/* if in a child, pop back to our parent */
3809339Seric 			if (InChild)
3819339Seric 				finis();
38224943Seric 
38324943Seric 			/* clean up a bit */
38458109Seric 			gotmail = FALSE;
38555012Seric 			dropenvelope(e);
38655012Seric 			CurEnv = e = newenvelope(e);
38755012Seric 			e->e_flags = BlankEnvelope.e_flags;
3884549Seric 			break;
3894549Seric 
3904549Seric 		  case CMDRSET:		/* rset -- reset state */
3914549Seric 			message("250", "Reset state");
3929339Seric 			if (InChild)
3939339Seric 				finis();
39458109Seric 
39558109Seric 			/* clean up a bit */
39658109Seric 			gotmail = FALSE;
39758109Seric 			dropenvelope(e);
39858109Seric 			CurEnv = e = newenvelope(e);
3999339Seric 			break;
4004549Seric 
4014549Seric 		  case CMDVRFY:		/* vrfy -- verify address */
40258092Seric 		  case CMDEXPN:		/* expn -- expand address */
40358092Seric 			vrfy = c->cmdcode == CMDVRFY;
40458092Seric 			if (bitset(vrfy ? PRIV_NOVRFY : PRIV_NOEXPN,
40558092Seric 						PrivacyFlags))
40658082Seric 			{
40758082Seric 				message("502", "That's none of your business");
40858082Seric 				break;
40958082Seric 			}
41058082Seric 			else if (!gothello &&
41158092Seric 				 bitset(vrfy ? PRIV_NEEDVRFYHELO : PRIV_NEEDEXPNHELO,
41258092Seric 						PrivacyFlags))
41358082Seric 			{
41458082Seric 				message("503", "I demand that you introduce yourself first");
41558082Seric 				break;
41658082Seric 			}
41758092Seric 			if (runinchild(vrfy ? "SMTP-VRFY" : "SMTP-EXPN", e) > 0)
4189339Seric 				break;
41925050Seric 			setproctitle("%s: %s", CurHostName, inp);
42055173Seric #ifdef LOG
42158020Seric 			if (LogLevel > 5)
42255173Seric 				syslog(LOG_INFO, "%s: %s", CurHostName, inp);
42355173Seric #endif
4245003Seric 			vrfyqueue = NULL;
4257762Seric 			QuickAbort = TRUE;
42658092Seric 			if (vrfy)
42758092Seric 				e->e_flags |= EF_VRFYONLY;
42858082Seric 			(void) sendtolist(p, (ADDRESS *) NULL, &vrfyqueue, e);
4297762Seric 			if (Errors != 0)
4309339Seric 			{
4319339Seric 				if (InChild)
4329339Seric 					finis();
4337762Seric 				break;
4349339Seric 			}
4355003Seric 			while (vrfyqueue != NULL)
4365003Seric 			{
4375003Seric 				register ADDRESS *a = vrfyqueue->q_next;
4385003Seric 				char *code;
4395003Seric 
4407685Seric 				while (a != NULL && bitset(QDONTSEND|QBADADDR, a->q_flags))
4415003Seric 					a = a->q_next;
4425003Seric 
4437685Seric 				if (!bitset(QDONTSEND|QBADADDR, vrfyqueue->q_flags))
4445003Seric 				{
4455003Seric 					if (a != NULL)
4465003Seric 						code = "250-";
4475003Seric 					else
4485003Seric 						code = "250";
44958010Seric 					if (strchr(vrfyqueue->q_paddr, '<') != NULL)
45058010Seric 						message(code, "%s", vrfyqueue->q_paddr);
45158010Seric 					else if (vrfyqueue->q_fullname == NULL)
4525003Seric 						message(code, "<%s>", vrfyqueue->q_paddr);
4535003Seric 					else
4545003Seric 						message(code, "%s <%s>",
4555003Seric 						    vrfyqueue->q_fullname, vrfyqueue->q_paddr);
4565003Seric 				}
4575003Seric 				else if (a == NULL)
4585003Seric 					message("554", "Self destructive alias loop");
4595003Seric 				vrfyqueue = a;
4605003Seric 			}
4619339Seric 			if (InChild)
4629339Seric 				finis();
4634549Seric 			break;
4644549Seric 
4654549Seric 		  case CMDHELP:		/* help -- give user info */
4664577Seric 			help(p);
4674549Seric 			break;
4684549Seric 
4694549Seric 		  case CMDNOOP:		/* noop -- do nothing */
4704549Seric 			message("200", "OK");
4714549Seric 			break;
4724549Seric 
4734549Seric 		  case CMDQUIT:		/* quit -- leave mail */
47425050Seric 			message("221", "%s closing connection", MyHostName);
4759339Seric 			if (InChild)
4769339Seric 				ExitStat = EX_QUIT;
4774549Seric 			finis();
4784549Seric 
4798544Seric 		  case CMDVERB:		/* set verbose mode */
4808544Seric 			Verbose = TRUE;
48125025Seric 			SendMode = SM_DELIVER;
4828544Seric 			message("200", "Verbose mode");
4838544Seric 			break;
4848544Seric 
4859314Seric 		  case CMDONEX:		/* doing one transaction only */
4869378Seric 			OneXact = TRUE;
4879314Seric 			message("200", "Only one transaction");
4889314Seric 			break;
4899314Seric 
49036230Skarels # ifdef SMTPDEBUG
4919339Seric 		  case CMDDBGQSHOW:	/* show queues */
4926907Seric 			printf("Send Queue=");
49355012Seric 			printaddr(e->e_sendqueue, TRUE);
4945003Seric 			break;
4957275Seric 
4967275Seric 		  case CMDDBGDEBUG:	/* set debug mode */
4977676Seric 			tTsetup(tTdvect, sizeof tTdvect, "0-99.1");
4987676Seric 			tTflag(p);
4997676Seric 			message("200", "Debug set");
5007275Seric 			break;
5017275Seric 
50236230Skarels # else /* not SMTPDEBUG */
50324945Seric 
50436230Skarels 		  case CMDDBGQSHOW:	/* show queues */
50536230Skarels 		  case CMDDBGDEBUG:	/* set debug mode */
50636233Skarels # ifdef LOG
50736233Skarels 			if (RealHostName != NULL && LogLevel > 0)
50836230Skarels 				syslog(LOG_NOTICE,
50958020Seric 				    "\"%s\" command from %s (%s)",
51036230Skarels 				    c->cmdname, RealHostName,
51136230Skarels 				    inet_ntoa(RealHostAddr.sin_addr));
51236233Skarels # endif
51336230Skarels 			/* FALL THROUGH */
51436230Skarels # endif /* SMTPDEBUG */
51536230Skarels 
5164549Seric 		  case CMDERROR:	/* unknown command */
5174549Seric 			message("500", "Command unrecognized");
5184549Seric 			break;
5194549Seric 
5204549Seric 		  default:
52136230Skarels 			errno = 0;
5224549Seric 			syserr("smtp: unknown code %d", c->cmdcode);
5234549Seric 			break;
5244549Seric 		}
5254549Seric 	}
5264549Seric }
5274549Seric /*
5284549Seric **  SKIPWORD -- skip a fixed word.
5294549Seric **
5304549Seric **	Parameters:
5314549Seric **		p -- place to start looking.
5324549Seric **		w -- word to skip.
5334549Seric **
5344549Seric **	Returns:
5354549Seric **		p following w.
5364549Seric **		NULL on error.
5374549Seric **
5384549Seric **	Side Effects:
5394549Seric **		clobbers the p data area.
5404549Seric */
5414549Seric 
5424549Seric static char *
5434549Seric skipword(p, w)
5444549Seric 	register char *p;
5454549Seric 	char *w;
5464549Seric {
5474549Seric 	register char *q;
5484549Seric 
5494549Seric 	/* find beginning of word */
55058050Seric 	while (isascii(*p) && isspace(*p))
5514549Seric 		p++;
5524549Seric 	q = p;
5534549Seric 
5544549Seric 	/* find end of word */
55558050Seric 	while (*p != '\0' && *p != ':' && !(isascii(*p) && isspace(*p)))
5564549Seric 		p++;
55758050Seric 	while (isascii(*p) && isspace(*p))
5584549Seric 		*p++ = '\0';
5594549Seric 	if (*p != ':')
5604549Seric 	{
5614549Seric 	  syntax:
5624549Seric 		message("501", "Syntax error");
5634549Seric 		Errors++;
5644549Seric 		return (NULL);
5654549Seric 	}
5664549Seric 	*p++ = '\0';
56758050Seric 	while (isascii(*p) && isspace(*p))
5684549Seric 		p++;
5694549Seric 
5704549Seric 	/* see if the input word matches desired word */
57133725Sbostic 	if (strcasecmp(q, w))
5724549Seric 		goto syntax;
5734549Seric 
5744549Seric 	return (p);
5754549Seric }
5764577Seric /*
5774577Seric **  HELP -- implement the HELP command.
5784577Seric **
5794577Seric **	Parameters:
5804577Seric **		topic -- the topic we want help for.
5814577Seric **
5824577Seric **	Returns:
5834577Seric **		none.
5844577Seric **
5854577Seric **	Side Effects:
5864577Seric **		outputs the help file to message output.
5874577Seric */
5884577Seric 
5894577Seric help(topic)
5904577Seric 	char *topic;
5914577Seric {
5924577Seric 	register FILE *hf;
5934577Seric 	int len;
5944577Seric 	char buf[MAXLINE];
5954577Seric 	bool noinfo;
5964577Seric 
5978269Seric 	if (HelpFile == NULL || (hf = fopen(HelpFile, "r")) == NULL)
5984577Seric 	{
5994577Seric 		/* no help */
60011931Seric 		errno = 0;
6014577Seric 		message("502", "HELP not implemented");
6024577Seric 		return;
6034577Seric 	}
6044577Seric 
60549669Seric 	if (topic == NULL || *topic == '\0')
60649669Seric 		topic = "smtp";
60749669Seric 	else
60849669Seric 		makelower(topic);
60949669Seric 
6104577Seric 	len = strlen(topic);
6114577Seric 	noinfo = TRUE;
6124577Seric 
6134577Seric 	while (fgets(buf, sizeof buf, hf) != NULL)
6144577Seric 	{
6154577Seric 		if (strncmp(buf, topic, len) == 0)
6164577Seric 		{
6174577Seric 			register char *p;
6184577Seric 
61956795Seric 			p = strchr(buf, '\t');
6204577Seric 			if (p == NULL)
6214577Seric 				p = buf;
6224577Seric 			else
6234577Seric 				p++;
6244577Seric 			fixcrlf(p, TRUE);
6254577Seric 			message("214-", p);
6264577Seric 			noinfo = FALSE;
6274577Seric 		}
6284577Seric 	}
6294577Seric 
6304577Seric 	if (noinfo)
6314577Seric 		message("504", "HELP topic unknown");
6324577Seric 	else
6334577Seric 		message("214", "End of HELP info");
6344628Seric 	(void) fclose(hf);
6354577Seric }
6368544Seric /*
6379339Seric **  RUNINCHILD -- return twice -- once in the child, then in the parent again
6389339Seric **
6399339Seric **	Parameters:
6409339Seric **		label -- a string used in error messages
6419339Seric **
6429339Seric **	Returns:
6439339Seric **		zero in the child
6449339Seric **		one in the parent
6459339Seric **
6469339Seric **	Side Effects:
6479339Seric **		none.
6489339Seric */
6498544Seric 
65055012Seric runinchild(label, e)
6519339Seric 	char *label;
65255012Seric 	register ENVELOPE *e;
6539339Seric {
6549339Seric 	int childpid;
6559339Seric 
65616158Seric 	if (!OneXact)
6579339Seric 	{
65816158Seric 		childpid = dofork();
65916158Seric 		if (childpid < 0)
66016158Seric 		{
66116158Seric 			syserr("%s: cannot fork", label);
66216158Seric 			return (1);
66316158Seric 		}
66416158Seric 		if (childpid > 0)
66516158Seric 		{
66616158Seric 			auto int st;
6679339Seric 
66816158Seric 			/* parent -- wait for child to complete */
66916158Seric 			st = waitfor(childpid);
67016158Seric 			if (st == -1)
67116158Seric 				syserr("%s: lost child", label);
6729339Seric 
67316158Seric 			/* if we exited on a QUIT command, complete the process */
67416158Seric 			if (st == (EX_QUIT << 8))
67516158Seric 				finis();
6769339Seric 
67716158Seric 			return (1);
67816158Seric 		}
67916158Seric 		else
68016158Seric 		{
68116158Seric 			/* child */
68216158Seric 			InChild = TRUE;
68325050Seric 			QuickAbort = FALSE;
68455012Seric 			clearenvelope(e, FALSE);
68516158Seric 		}
6869339Seric 	}
68715256Seric 
68816158Seric 	/* open alias database */
68955012Seric 	initaliases(AliasFile, FALSE, e);
69016158Seric 
69116158Seric 	return (0);
6929339Seric }
6939339Seric 
69456795Seric # endif /* SMTP */
695