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*58151Seric static char sccsid[] = "@(#)srvrsmtp.c	6.16 (Berkeley) 02/23/93 (with SMTP)";
1433731Sbostic #else
15*58151Seric static char sccsid[] = "@(#)srvrsmtp.c	6.16 (Berkeley) 02/23/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);
125*58151Seric 	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 */
151*58151Seric 			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 
207*58151Seric 				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);
220*58151Seric 			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 			{
235*58151Seric 				message("503 Polite people say HELO first");
23658082Seric 				break;
23758082Seric 			}
23858109Seric 			if (gotmail)
2394558Seric 			{
240*58151Seric 				message("503 Sender already specified");
2414558Seric 				break;
2424558Seric 			}
2439339Seric 			if (InChild)
2449339Seric 			{
24536230Skarels 				errno = 0;
246*58151Seric 				syserr("503 Nested MAIL command: MAIL %s", p);
24758069Seric 				finis();
2489339Seric 			}
24958082Seric 			if (!enoughspace())
25058082Seric 			{
251*58151Seric 				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)
26958147Seric 			{
27058147Seric 				/* this failed -- undo work */
27158147Seric 				if (InChild)
27258147Seric 					finis();
27357977Seric 				break;
27458147Seric 			}
27557977Seric 			QuickAbort = TRUE;
27655012Seric 			setsender(p, e);
277*58151Seric 			message("250 Sender ok");
27858147Seric 			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))
310*58151Seric 				message("250 Recipient ok");
31112612Seric 			else
3124549Seric 			{
31312612Seric 				/* punt -- should keep message in ADDRESS.... */
314*58151Seric 				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 			{
323*58151Seric 				message("503 Need MAIL command");
3244976Seric 				break;
3254549Seric 			}
32655012Seric 			else if (e->e_nrcpts <= 0)
3274549Seric 			{
328*58151Seric 				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)
375*58151Seric 				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 */
391*58151Seric 			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 			{
407*58151Seric 				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 			{
414*58151Seric 				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))
444*58151Seric 					printvrfyaddr(vrfyqueue, a == NULL);
4455003Seric 				else if (a == NULL)
446*58151Seric 					message("554 Self destructive alias loop");
4475003Seric 				vrfyqueue = a;
4485003Seric 			}
4499339Seric 			if (InChild)
4509339Seric 				finis();
4514549Seric 			break;
4524549Seric 
4534549Seric 		  case CMDHELP:		/* help -- give user info */
4544577Seric 			help(p);
4554549Seric 			break;
4564549Seric 
4574549Seric 		  case CMDNOOP:		/* noop -- do nothing */
458*58151Seric 			message("200 OK");
4594549Seric 			break;
4604549Seric 
4614549Seric 		  case CMDQUIT:		/* quit -- leave mail */
462*58151Seric 			message("221 %s closing connection", MyHostName);
4639339Seric 			if (InChild)
4649339Seric 				ExitStat = EX_QUIT;
4654549Seric 			finis();
4664549Seric 
4678544Seric 		  case CMDVERB:		/* set verbose mode */
4688544Seric 			Verbose = TRUE;
46925025Seric 			SendMode = SM_DELIVER;
470*58151Seric 			message("200 Verbose mode");
4718544Seric 			break;
4728544Seric 
4739314Seric 		  case CMDONEX:		/* doing one transaction only */
4749378Seric 			OneXact = TRUE;
475*58151Seric 			message("200 Only one transaction");
4769314Seric 			break;
4779314Seric 
47836230Skarels # ifdef SMTPDEBUG
4799339Seric 		  case CMDDBGQSHOW:	/* show queues */
4806907Seric 			printf("Send Queue=");
48155012Seric 			printaddr(e->e_sendqueue, TRUE);
4825003Seric 			break;
4837275Seric 
4847275Seric 		  case CMDDBGDEBUG:	/* set debug mode */
4857676Seric 			tTsetup(tTdvect, sizeof tTdvect, "0-99.1");
4867676Seric 			tTflag(p);
487*58151Seric 			message("200 Debug set");
4887275Seric 			break;
4897275Seric 
49036230Skarels # else /* not SMTPDEBUG */
49124945Seric 
49236230Skarels 		  case CMDDBGQSHOW:	/* show queues */
49336230Skarels 		  case CMDDBGDEBUG:	/* set debug mode */
49436233Skarels # ifdef LOG
49536233Skarels 			if (RealHostName != NULL && LogLevel > 0)
49636230Skarels 				syslog(LOG_NOTICE,
49758020Seric 				    "\"%s\" command from %s (%s)",
49836230Skarels 				    c->cmdname, RealHostName,
49936230Skarels 				    inet_ntoa(RealHostAddr.sin_addr));
50036233Skarels # endif
50136230Skarels 			/* FALL THROUGH */
50236230Skarels # endif /* SMTPDEBUG */
50336230Skarels 
5044549Seric 		  case CMDERROR:	/* unknown command */
505*58151Seric 			message("500 Command unrecognized");
5064549Seric 			break;
5074549Seric 
5084549Seric 		  default:
50936230Skarels 			errno = 0;
510*58151Seric 			syserr("500 smtp: unknown code %d", c->cmdcode);
5114549Seric 			break;
5124549Seric 		}
5134549Seric 	}
5144549Seric }
5154549Seric /*
5164549Seric **  SKIPWORD -- skip a fixed word.
5174549Seric **
5184549Seric **	Parameters:
5194549Seric **		p -- place to start looking.
5204549Seric **		w -- word to skip.
5214549Seric **
5224549Seric **	Returns:
5234549Seric **		p following w.
5244549Seric **		NULL on error.
5254549Seric **
5264549Seric **	Side Effects:
5274549Seric **		clobbers the p data area.
5284549Seric */
5294549Seric 
5304549Seric static char *
5314549Seric skipword(p, w)
5324549Seric 	register char *p;
5334549Seric 	char *w;
5344549Seric {
5354549Seric 	register char *q;
5364549Seric 
5374549Seric 	/* find beginning of word */
53858050Seric 	while (isascii(*p) && isspace(*p))
5394549Seric 		p++;
5404549Seric 	q = p;
5414549Seric 
5424549Seric 	/* find end of word */
54358050Seric 	while (*p != '\0' && *p != ':' && !(isascii(*p) && isspace(*p)))
5444549Seric 		p++;
54558050Seric 	while (isascii(*p) && isspace(*p))
5464549Seric 		*p++ = '\0';
5474549Seric 	if (*p != ':')
5484549Seric 	{
5494549Seric 	  syntax:
550*58151Seric 		message("501 Syntax error");
5514549Seric 		Errors++;
5524549Seric 		return (NULL);
5534549Seric 	}
5544549Seric 	*p++ = '\0';
55558050Seric 	while (isascii(*p) && isspace(*p))
5564549Seric 		p++;
5574549Seric 
5584549Seric 	/* see if the input word matches desired word */
55933725Sbostic 	if (strcasecmp(q, w))
5604549Seric 		goto syntax;
5614549Seric 
5624549Seric 	return (p);
5634549Seric }
5644577Seric /*
565*58151Seric **  PRINTVRFYADDR -- print an entry in the verify queue
566*58151Seric **
567*58151Seric **	Parameters:
568*58151Seric **		a -- the address to print
569*58151Seric **		last -- set if this is the last one.
570*58151Seric **
571*58151Seric **	Returns:
572*58151Seric **		none.
573*58151Seric **
574*58151Seric **	Side Effects:
575*58151Seric **		Prints the appropriate 250 codes.
576*58151Seric */
577*58151Seric 
578*58151Seric printvrfyaddr(a, last)
579*58151Seric 	register ADDRESS *a;
580*58151Seric 	bool last;
581*58151Seric {
582*58151Seric 	char fmtbuf[20];
583*58151Seric 
584*58151Seric 	strcpy(fmtbuf, "250");
585*58151Seric 	fmtbuf[3] = last ? ' ' : '-';
586*58151Seric 
587*58151Seric 	if (strchr(a->q_paddr, '<') != NULL)
588*58151Seric 		strcpy(&fmtbuf[4], "%s");
589*58151Seric 	else if (a->q_fullname == NULL)
590*58151Seric 		strcpy(&fmtbuf[4], "<%s>");
591*58151Seric 	else
592*58151Seric 	{
593*58151Seric 		strcpy(&fmtbuf[4], "%s <%s>");
594*58151Seric 		message(fmtbuf, a->q_fullname, a->q_paddr);
595*58151Seric 		return;
596*58151Seric 	}
597*58151Seric 	message(fmtbuf, a->q_paddr);
598*58151Seric }
599*58151Seric /*
6004577Seric **  HELP -- implement the HELP command.
6014577Seric **
6024577Seric **	Parameters:
6034577Seric **		topic -- the topic we want help for.
6044577Seric **
6054577Seric **	Returns:
6064577Seric **		none.
6074577Seric **
6084577Seric **	Side Effects:
6094577Seric **		outputs the help file to message output.
6104577Seric */
6114577Seric 
6124577Seric help(topic)
6134577Seric 	char *topic;
6144577Seric {
6154577Seric 	register FILE *hf;
6164577Seric 	int len;
6174577Seric 	char buf[MAXLINE];
6184577Seric 	bool noinfo;
6194577Seric 
6208269Seric 	if (HelpFile == NULL || (hf = fopen(HelpFile, "r")) == NULL)
6214577Seric 	{
6224577Seric 		/* no help */
62311931Seric 		errno = 0;
624*58151Seric 		message("502 HELP not implemented");
6254577Seric 		return;
6264577Seric 	}
6274577Seric 
62849669Seric 	if (topic == NULL || *topic == '\0')
62949669Seric 		topic = "smtp";
63049669Seric 	else
63149669Seric 		makelower(topic);
63249669Seric 
6334577Seric 	len = strlen(topic);
6344577Seric 	noinfo = TRUE;
6354577Seric 
6364577Seric 	while (fgets(buf, sizeof buf, hf) != NULL)
6374577Seric 	{
6384577Seric 		if (strncmp(buf, topic, len) == 0)
6394577Seric 		{
6404577Seric 			register char *p;
6414577Seric 
64256795Seric 			p = strchr(buf, '\t');
6434577Seric 			if (p == NULL)
6444577Seric 				p = buf;
6454577Seric 			else
6464577Seric 				p++;
6474577Seric 			fixcrlf(p, TRUE);
648*58151Seric 			message("214-%s", p);
6494577Seric 			noinfo = FALSE;
6504577Seric 		}
6514577Seric 	}
6524577Seric 
6534577Seric 	if (noinfo)
654*58151Seric 		message("504 HELP topic unknown");
6554577Seric 	else
656*58151Seric 		message("214 End of HELP info");
6574628Seric 	(void) fclose(hf);
6584577Seric }
6598544Seric /*
6609339Seric **  RUNINCHILD -- return twice -- once in the child, then in the parent again
6619339Seric **
6629339Seric **	Parameters:
6639339Seric **		label -- a string used in error messages
6649339Seric **
6659339Seric **	Returns:
6669339Seric **		zero in the child
6679339Seric **		one in the parent
6689339Seric **
6699339Seric **	Side Effects:
6709339Seric **		none.
6719339Seric */
6728544Seric 
67355012Seric runinchild(label, e)
6749339Seric 	char *label;
67555012Seric 	register ENVELOPE *e;
6769339Seric {
6779339Seric 	int childpid;
6789339Seric 
67916158Seric 	if (!OneXact)
6809339Seric 	{
68116158Seric 		childpid = dofork();
68216158Seric 		if (childpid < 0)
68316158Seric 		{
68416158Seric 			syserr("%s: cannot fork", label);
68516158Seric 			return (1);
68616158Seric 		}
68716158Seric 		if (childpid > 0)
68816158Seric 		{
68916158Seric 			auto int st;
6909339Seric 
69116158Seric 			/* parent -- wait for child to complete */
69216158Seric 			st = waitfor(childpid);
69316158Seric 			if (st == -1)
69416158Seric 				syserr("%s: lost child", label);
6959339Seric 
69616158Seric 			/* if we exited on a QUIT command, complete the process */
69716158Seric 			if (st == (EX_QUIT << 8))
69816158Seric 				finis();
6999339Seric 
70016158Seric 			return (1);
70116158Seric 		}
70216158Seric 		else
70316158Seric 		{
70416158Seric 			/* child */
70516158Seric 			InChild = TRUE;
70625050Seric 			QuickAbort = FALSE;
70755012Seric 			clearenvelope(e, FALSE);
70816158Seric 		}
7099339Seric 	}
71015256Seric 
71116158Seric 	/* open alias database */
71255012Seric 	initaliases(AliasFile, FALSE, e);
71316158Seric 
71416158Seric 	return (0);
7159339Seric }
7169339Seric 
71756795Seric # endif /* SMTP */
718