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*58330Seric static char sccsid[] = "@(#)srvrsmtp.c	6.20 (Berkeley) 03/01/93 (with SMTP)";
1433731Sbostic #else
15*58330Seric static char sccsid[] = "@(#)srvrsmtp.c	6.20 (Berkeley) 03/01/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 */
5658323Seric # define CMDEHLO	11	/* ehlo -- extended helo (RFC 1425) */
5758092Seric /* non-standard commands */
5858092Seric # define CMDONEX	16	/* onex -- sending one transaction only */
5958092Seric # define CMDVERB	17	/* verb -- go into verbose mode */
6036230Skarels /* debugging-only commands, only enabled if SMTPDEBUG is defined */
6158092Seric # define CMDDBGQSHOW	24	/* showq -- show send queue */
6258092Seric # define CMDDBGDEBUG	25	/* debug -- set debug mode */
634549Seric 
644549Seric static struct cmd	CmdTab[] =
654549Seric {
664549Seric 	"mail",		CMDMAIL,
674976Seric 	"rcpt",		CMDRCPT,
684549Seric 	"data",		CMDDATA,
694549Seric 	"rset",		CMDRSET,
704549Seric 	"vrfy",		CMDVRFY,
7158092Seric 	"expn",		CMDEXPN,
724549Seric 	"help",		CMDHELP,
734549Seric 	"noop",		CMDNOOP,
744549Seric 	"quit",		CMDQUIT,
754976Seric 	"helo",		CMDHELO,
7658323Seric 	"ehlo",		CMDEHLO,
778544Seric 	"verb",		CMDVERB,
789314Seric 	"onex",		CMDONEX,
7936230Skarels 	/*
8036230Skarels 	 * remaining commands are here only
8136230Skarels 	 * to trap and log attempts to use them
8236230Skarels 	 */
839339Seric 	"showq",	CMDDBGQSHOW,
848544Seric 	"debug",	CMDDBGDEBUG,
854549Seric 	NULL,		CMDERROR,
864549Seric };
874549Seric 
889339Seric bool	InChild = FALSE;		/* true if running in a subprocess */
899378Seric bool	OneXact = FALSE;		/* one xaction only this run */
9011146Seric 
919339Seric #define EX_QUIT		22		/* special code for QUIT command */
928544Seric 
9355012Seric smtp(e)
9455012Seric 	register ENVELOPE *e;
954549Seric {
964549Seric 	register char *p;
978544Seric 	register struct cmd *c;
984549Seric 	char *cmd;
9946928Sbostic 	static char *skipword();
1005003Seric 	auto ADDRESS *vrfyqueue;
10112612Seric 	ADDRESS *a;
10230448Seric 	char *sendinghost;
10358109Seric 	bool gotmail;			/* mail command received */
10458092Seric 	bool gothello;			/* helo command received */
10558092Seric 	bool vrfy;			/* set if this is a vrfy command */
10658323Seric 	char *protocol;			/* sending protocol */
1078544Seric 	char inp[MAXLINE];
10857232Seric 	char cmdbuf[MAXLINE];
1097124Seric 	extern char Version[];
11011151Seric 	extern char *macvalue();
11112612Seric 	extern ADDRESS *recipient();
11224943Seric 	extern ENVELOPE BlankEnvelope;
11324943Seric 	extern ENVELOPE *newenvelope();
1144549Seric 
1157363Seric 	if (OutChannel != stdout)
1167363Seric 	{
1177363Seric 		/* arrange for debugging output to go to remote host */
1187363Seric 		(void) close(1);
1197363Seric 		(void) dup(fileno(OutChannel));
1207363Seric 	}
12155012Seric 	settime(e);
12257642Seric 	CurHostName = RealHostName;
12357642Seric 	setproctitle("srvrsmtp %s", CurHostName);
12458050Seric 	expand("\201e", inp, &inp[sizeof inp], e);
12558151Seric 	message("220 %s", inp);
12624943Seric 	SmtpPhase = "startup";
12730448Seric 	sendinghost = NULL;
128*58330Seric 	protocol = NULL;
12958082Seric 	gothello = FALSE;
130*58330Seric 	gotmail = FALSE;
1314549Seric 	for (;;)
1324549Seric 	{
13312612Seric 		/* arrange for backout */
13412612Seric 		if (setjmp(TopFrame) > 0 && InChild)
13512612Seric 			finis();
13612612Seric 		QuickAbort = FALSE;
13712612Seric 		HoldErrs = FALSE;
13851951Seric 		LogUsrErrs = FALSE;
13958092Seric 		e->e_flags &= ~EF_VRFYONLY;
14012612Seric 
1417356Seric 		/* setup for the read */
14255012Seric 		e->e_to = NULL;
1434577Seric 		Errors = 0;
1447275Seric 		(void) fflush(stdout);
1457356Seric 
1467356Seric 		/* read the input line */
14758109Seric 		p = sfgets(inp, sizeof inp, InChannel, TimeOuts.to_nextcommand);
1487356Seric 
1497685Seric 		/* handle errors */
1507356Seric 		if (p == NULL)
1517356Seric 		{
1524549Seric 			/* end of file, just die */
15358151Seric 			message("421 %s Lost input channel from %s",
15425050Seric 				MyHostName, CurHostName);
15555464Seric #ifdef LOG
15658020Seric 			if (LogLevel > 1)
15755464Seric 				syslog(LOG_NOTICE, "lost input channel from %s",
15855464Seric 					CurHostName);
15955464Seric #endif
16058069Seric 			if (InChild)
16158069Seric 				ExitStat = EX_QUIT;
1624549Seric 			finis();
1634549Seric 		}
1644549Seric 
1654549Seric 		/* clean up end of line */
1664558Seric 		fixcrlf(inp, TRUE);
1674549Seric 
1684713Seric 		/* echo command to transcript */
16955012Seric 		if (e->e_xfp != NULL)
17055012Seric 			fprintf(e->e_xfp, "<<< %s\n", inp);
1714713Seric 
1724549Seric 		/* break off command */
17358050Seric 		for (p = inp; isascii(*p) && isspace(*p); p++)
1744549Seric 			continue;
17557232Seric 		cmd = cmdbuf;
17658050Seric 		while (*p != '\0' &&
17758050Seric 		       !(isascii(*p) && isspace(*p)) &&
17858050Seric 		       cmd < &cmdbuf[sizeof cmdbuf - 2])
17924981Seric 			*cmd++ = *p++;
18024981Seric 		*cmd = '\0';
1814549Seric 
18225691Seric 		/* throw away leading whitespace */
18358050Seric 		while (isascii(*p) && isspace(*p))
18425691Seric 			p++;
18525691Seric 
1864549Seric 		/* decode command */
1874549Seric 		for (c = CmdTab; c->cmdname != NULL; c++)
1884549Seric 		{
18933725Sbostic 			if (!strcasecmp(c->cmdname, cmdbuf))
1904549Seric 				break;
1914549Seric 		}
1924549Seric 
19351954Seric 		/* reset errors */
19451954Seric 		errno = 0;
19551954Seric 
1964549Seric 		/* process command */
1974549Seric 		switch (c->cmdcode)
1984549Seric 		{
1994976Seric 		  case CMDHELO:		/* hello -- introduce yourself */
20058323Seric 		  case CMDEHLO:		/* extended hello */
20158323Seric 			if (c->cmdcode == CMDEHLO)
20258323Seric 			{
20358323Seric 				protocol = "ESMTP";
20458323Seric 				SmtpPhase = "EHLO";
20558323Seric 			}
20658323Seric 			else
20758323Seric 			{
20858323Seric 				protocol = "SMTP";
20958323Seric 				SmtpPhase = "HELO";
21058323Seric 			}
21125050Seric 			setproctitle("%s: %s", CurHostName, inp);
21258109Seric 			if (strcasecmp(p, MyHostName) == 0)
21314877Seric 			{
21436230Skarels 				/*
21558109Seric 				**  Didn't know about alias or MX,
21658109Seric 				**  or connected to an echo server
21758109Seric 				*/
21858109Seric 
21958151Seric 				message("553 %s config error: mail loops back to myself",
22047570Seric 					MyHostName);
22114877Seric 				break;
22214877Seric 			}
22358308Seric 			if (strcasecmp(p, RealHostName) != 0)
22411146Seric 			{
22524981Seric 				char hostbuf[MAXNAME];
22611146Seric 
22724981Seric 				(void) sprintf(hostbuf, "%s (%s)", p, RealHostName);
22830448Seric 				sendinghost = newstr(hostbuf);
22911146Seric 			}
23011146Seric 			else
23130448Seric 				sendinghost = newstr(p);
23258323Seric 
23358323Seric 			/* send ext. message -- old systems must ignore */
23458323Seric 			message("250-%s Hello %s, pleased to meet you",
23536230Skarels 				MyHostName, sendinghost);
23658323Seric 			if (!bitset(PRIV_NOEXPN, PrivacyFlags))
23758323Seric 				message("250-EXPN");
23858323Seric 			message("250 HELP");
23958082Seric 			gothello = TRUE;
2404976Seric 			break;
2414976Seric 
2424549Seric 		  case CMDMAIL:		/* mail -- designate sender */
24324943Seric 			SmtpPhase = "MAIL";
24424943Seric 
24511151Seric 			/* force a sending host even if no HELO given */
24658064Seric 			if (sendinghost == NULL && macvalue('s', e) == NULL)
24730448Seric 				sendinghost = RealHostName;
24811151Seric 
2499314Seric 			/* check for validity of this command */
25058082Seric 			if (!gothello && bitset(PRIV_NEEDMAILHELO, PrivacyFlags))
25158082Seric 			{
25258151Seric 				message("503 Polite people say HELO first");
25358082Seric 				break;
25458082Seric 			}
25558109Seric 			if (gotmail)
2564558Seric 			{
25758151Seric 				message("503 Sender already specified");
2584558Seric 				break;
2594558Seric 			}
2609339Seric 			if (InChild)
2619339Seric 			{
26236230Skarels 				errno = 0;
26358151Seric 				syserr("503 Nested MAIL command: MAIL %s", p);
26458069Seric 				finis();
2659339Seric 			}
26658082Seric 			if (!enoughspace())
26758082Seric 			{
26858151Seric 				message("452 Insufficient disk space; try again later");
26958082Seric 				break;
27058082Seric 			}
2719339Seric 
2729339Seric 			/* fork a subprocess to process this command */
27355012Seric 			if (runinchild("SMTP-MAIL", e) > 0)
2749339Seric 				break;
27558064Seric 			if (sendinghost != NULL)
27658064Seric 				define('s', sendinghost, e);
27758323Seric 			if (protocol == NULL)
27858323Seric 				protocol = "SMTP";
27958323Seric 			define('r', protocol, e);
28055012Seric 			initsys(e);
28157389Seric 			setproctitle("%s %s: %s", e->e_id, CurHostName, inp);
2829339Seric 
2839339Seric 			/* child -- go do the processing */
2844549Seric 			p = skipword(p, "from");
2854549Seric 			if (p == NULL)
2864549Seric 				break;
28757977Seric 			if (setjmp(TopFrame) > 0)
28858147Seric 			{
28958147Seric 				/* this failed -- undo work */
29058147Seric 				if (InChild)
29158147Seric 					finis();
29257977Seric 				break;
29358147Seric 			}
29457977Seric 			QuickAbort = TRUE;
29555012Seric 			setsender(p, e);
29658151Seric 			message("250 Sender ok");
29758147Seric 			gotmail = TRUE;
2984549Seric 			break;
2994549Seric 
3004976Seric 		  case CMDRCPT:		/* rcpt -- designate recipient */
30124943Seric 			SmtpPhase = "RCPT";
30257389Seric 			setproctitle("%s %s: %s", e->e_id, CurHostName, inp);
30312612Seric 			if (setjmp(TopFrame) > 0)
30414785Seric 			{
30555012Seric 				e->e_flags &= ~EF_FATALERRS;
30612612Seric 				break;
30714785Seric 			}
30812612Seric 			QuickAbort = TRUE;
30951951Seric 			LogUsrErrs = TRUE;
31058093Seric 
31158093Seric 			/* optimization -- if queueing, don't expand aliases */
31258093Seric 			if (SendMode == SM_QUEUE)
31358093Seric 				e->e_flags |= EF_VRFYONLY;
31458093Seric 
3154549Seric 			p = skipword(p, "to");
3164549Seric 			if (p == NULL)
3174549Seric 				break;
31858323Seric 			a = parseaddr(p, (ADDRESS *) NULL, 1, ' ', e);
31912612Seric 			if (a == NULL)
32012612Seric 				break;
32116886Seric 			a->q_flags |= QPRIMARY;
32255012Seric 			a = recipient(a, &e->e_sendqueue, e);
32312612Seric 			if (Errors != 0)
32412612Seric 				break;
32512612Seric 
32612612Seric 			/* no errors during parsing, but might be a duplicate */
32755012Seric 			e->e_to = p;
32812612Seric 			if (!bitset(QBADADDR, a->q_flags))
32958151Seric 				message("250 Recipient ok");
33012612Seric 			else
3314549Seric 			{
33212612Seric 				/* punt -- should keep message in ADDRESS.... */
33358151Seric 				message("550 Addressee unknown");
3344549Seric 			}
33555012Seric 			e->e_to = NULL;
3364549Seric 			break;
3374549Seric 
3384549Seric 		  case CMDDATA:		/* data -- text of mail */
33924943Seric 			SmtpPhase = "DATA";
34058109Seric 			if (!gotmail)
3414549Seric 			{
34258151Seric 				message("503 Need MAIL command");
3434976Seric 				break;
3444549Seric 			}
34555012Seric 			else if (e->e_nrcpts <= 0)
3464549Seric 			{
34758151Seric 				message("503 Need RCPT (recipient)");
3484976Seric 				break;
3494549Seric 			}
3504976Seric 
3514976Seric 			/* collect the text of the message */
35224943Seric 			SmtpPhase = "collect";
35357389Seric 			setproctitle("%s %s: %s", e->e_id, CurHostName, inp);
35455012Seric 			collect(TRUE, e);
3554976Seric 			if (Errors != 0)
3564976Seric 				break;
3574976Seric 
3588238Seric 			/*
3598238Seric 			**  Arrange to send to everyone.
3608238Seric 			**	If sending to multiple people, mail back
3618238Seric 			**		errors rather than reporting directly.
3628238Seric 			**	In any case, don't mail back errors for
3638238Seric 			**		anything that has happened up to
3648238Seric 			**		now (the other end will do this).
36510197Seric 			**	Truncate our transcript -- the mail has gotten
36610197Seric 			**		to us successfully, and if we have
36710197Seric 			**		to mail this back, it will be easier
36810197Seric 			**		on the reader.
3698238Seric 			**	Then send to everyone.
3708238Seric 			**	Finally give a reply code.  If an error has
3718238Seric 			**		already been given, don't mail a
3728238Seric 			**		message back.
3739339Seric 			**	We goose error returns by clearing error bit.
3748238Seric 			*/
3758238Seric 
37624943Seric 			SmtpPhase = "delivery";
37755012Seric 			if (e->e_nrcpts != 1)
3789378Seric 			{
3799378Seric 				HoldErrs = TRUE;
38016886Seric 				ErrorMode = EM_MAIL;
3819378Seric 			}
38255012Seric 			e->e_flags &= ~EF_FATALERRS;
38355012Seric 			e->e_xfp = freopen(queuename(e, 'x'), "w", e->e_xfp);
3844976Seric 
3854976Seric 			/* send to all recipients */
38655012Seric 			sendall(e, SM_DEFAULT);
38755012Seric 			e->e_to = NULL;
3884976Seric 
38923516Seric 			/* save statistics */
39055012Seric 			markstats(e, (ADDRESS *) NULL);
39123516Seric 
3928238Seric 			/* issue success if appropriate and reset */
3938238Seric 			if (Errors == 0 || HoldErrs)
39458151Seric 				message("250 Ok");
3958238Seric 			else
39655012Seric 				e->e_flags &= ~EF_FATALERRS;
3979339Seric 
3989339Seric 			/* if in a child, pop back to our parent */
3999339Seric 			if (InChild)
4009339Seric 				finis();
40124943Seric 
40224943Seric 			/* clean up a bit */
40358109Seric 			gotmail = FALSE;
40455012Seric 			dropenvelope(e);
40558179Seric 			CurEnv = e = newenvelope(e, CurEnv);
40655012Seric 			e->e_flags = BlankEnvelope.e_flags;
4074549Seric 			break;
4084549Seric 
4094549Seric 		  case CMDRSET:		/* rset -- reset state */
41058151Seric 			message("250 Reset state");
4119339Seric 			if (InChild)
4129339Seric 				finis();
41358109Seric 
41458109Seric 			/* clean up a bit */
41558109Seric 			gotmail = FALSE;
41658109Seric 			dropenvelope(e);
41758179Seric 			CurEnv = e = newenvelope(e, CurEnv);
4189339Seric 			break;
4194549Seric 
4204549Seric 		  case CMDVRFY:		/* vrfy -- verify address */
42158092Seric 		  case CMDEXPN:		/* expn -- expand address */
42258092Seric 			vrfy = c->cmdcode == CMDVRFY;
42358092Seric 			if (bitset(vrfy ? PRIV_NOVRFY : PRIV_NOEXPN,
42458092Seric 						PrivacyFlags))
42558082Seric 			{
42658151Seric 				message("502 That's none of your business");
42758082Seric 				break;
42858082Seric 			}
42958082Seric 			else if (!gothello &&
43058092Seric 				 bitset(vrfy ? PRIV_NEEDVRFYHELO : PRIV_NEEDEXPNHELO,
43158092Seric 						PrivacyFlags))
43258082Seric 			{
43358151Seric 				message("503 I demand that you introduce yourself first");
43458082Seric 				break;
43558082Seric 			}
43658092Seric 			if (runinchild(vrfy ? "SMTP-VRFY" : "SMTP-EXPN", e) > 0)
4379339Seric 				break;
43825050Seric 			setproctitle("%s: %s", CurHostName, inp);
43955173Seric #ifdef LOG
44058020Seric 			if (LogLevel > 5)
44155173Seric 				syslog(LOG_INFO, "%s: %s", CurHostName, inp);
44255173Seric #endif
4435003Seric 			vrfyqueue = NULL;
4447762Seric 			QuickAbort = TRUE;
44558092Seric 			if (vrfy)
44658092Seric 				e->e_flags |= EF_VRFYONLY;
44758082Seric 			(void) sendtolist(p, (ADDRESS *) NULL, &vrfyqueue, e);
4487762Seric 			if (Errors != 0)
4499339Seric 			{
4509339Seric 				if (InChild)
4519339Seric 					finis();
4527762Seric 				break;
4539339Seric 			}
4545003Seric 			while (vrfyqueue != NULL)
4555003Seric 			{
4565003Seric 				register ADDRESS *a = vrfyqueue->q_next;
4575003Seric 				char *code;
4585003Seric 
4597685Seric 				while (a != NULL && bitset(QDONTSEND|QBADADDR, a->q_flags))
4605003Seric 					a = a->q_next;
4615003Seric 
4627685Seric 				if (!bitset(QDONTSEND|QBADADDR, vrfyqueue->q_flags))
46358151Seric 					printvrfyaddr(vrfyqueue, a == NULL);
4645003Seric 				else if (a == NULL)
46558151Seric 					message("554 Self destructive alias loop");
4665003Seric 				vrfyqueue = a;
4675003Seric 			}
4689339Seric 			if (InChild)
4699339Seric 				finis();
4704549Seric 			break;
4714549Seric 
4724549Seric 		  case CMDHELP:		/* help -- give user info */
4734577Seric 			help(p);
4744549Seric 			break;
4754549Seric 
4764549Seric 		  case CMDNOOP:		/* noop -- do nothing */
47758151Seric 			message("200 OK");
4784549Seric 			break;
4794549Seric 
4804549Seric 		  case CMDQUIT:		/* quit -- leave mail */
48158151Seric 			message("221 %s closing connection", MyHostName);
4829339Seric 			if (InChild)
4839339Seric 				ExitStat = EX_QUIT;
4844549Seric 			finis();
4854549Seric 
4868544Seric 		  case CMDVERB:		/* set verbose mode */
4878544Seric 			Verbose = TRUE;
48825025Seric 			SendMode = SM_DELIVER;
48958151Seric 			message("200 Verbose mode");
4908544Seric 			break;
4918544Seric 
4929314Seric 		  case CMDONEX:		/* doing one transaction only */
4939378Seric 			OneXact = TRUE;
49458151Seric 			message("200 Only one transaction");
4959314Seric 			break;
4969314Seric 
49736230Skarels # ifdef SMTPDEBUG
4989339Seric 		  case CMDDBGQSHOW:	/* show queues */
4996907Seric 			printf("Send Queue=");
50055012Seric 			printaddr(e->e_sendqueue, TRUE);
5015003Seric 			break;
5027275Seric 
5037275Seric 		  case CMDDBGDEBUG:	/* set debug mode */
5047676Seric 			tTsetup(tTdvect, sizeof tTdvect, "0-99.1");
5057676Seric 			tTflag(p);
50658151Seric 			message("200 Debug set");
5077275Seric 			break;
5087275Seric 
50936230Skarels # else /* not SMTPDEBUG */
51024945Seric 
51136230Skarels 		  case CMDDBGQSHOW:	/* show queues */
51236230Skarels 		  case CMDDBGDEBUG:	/* set debug mode */
51336233Skarels # ifdef LOG
51458308Seric 			if (LogLevel > 0)
51536230Skarels 				syslog(LOG_NOTICE,
51658020Seric 				    "\"%s\" command from %s (%s)",
51736230Skarels 				    c->cmdname, RealHostName,
51836230Skarels 				    inet_ntoa(RealHostAddr.sin_addr));
51936233Skarels # endif
52036230Skarels 			/* FALL THROUGH */
52136230Skarels # endif /* SMTPDEBUG */
52236230Skarels 
5234549Seric 		  case CMDERROR:	/* unknown command */
52458151Seric 			message("500 Command unrecognized");
5254549Seric 			break;
5264549Seric 
5274549Seric 		  default:
52836230Skarels 			errno = 0;
52958151Seric 			syserr("500 smtp: unknown code %d", c->cmdcode);
5304549Seric 			break;
5314549Seric 		}
5324549Seric 	}
5334549Seric }
5344549Seric /*
5354549Seric **  SKIPWORD -- skip a fixed word.
5364549Seric **
5374549Seric **	Parameters:
5384549Seric **		p -- place to start looking.
5394549Seric **		w -- word to skip.
5404549Seric **
5414549Seric **	Returns:
5424549Seric **		p following w.
5434549Seric **		NULL on error.
5444549Seric **
5454549Seric **	Side Effects:
5464549Seric **		clobbers the p data area.
5474549Seric */
5484549Seric 
5494549Seric static char *
5504549Seric skipword(p, w)
5514549Seric 	register char *p;
5524549Seric 	char *w;
5534549Seric {
5544549Seric 	register char *q;
5554549Seric 
5564549Seric 	/* find beginning of word */
55758050Seric 	while (isascii(*p) && isspace(*p))
5584549Seric 		p++;
5594549Seric 	q = p;
5604549Seric 
5614549Seric 	/* find end of word */
56258050Seric 	while (*p != '\0' && *p != ':' && !(isascii(*p) && isspace(*p)))
5634549Seric 		p++;
56458050Seric 	while (isascii(*p) && isspace(*p))
5654549Seric 		*p++ = '\0';
5664549Seric 	if (*p != ':')
5674549Seric 	{
5684549Seric 	  syntax:
56958151Seric 		message("501 Syntax error");
5704549Seric 		Errors++;
5714549Seric 		return (NULL);
5724549Seric 	}
5734549Seric 	*p++ = '\0';
57458050Seric 	while (isascii(*p) && isspace(*p))
5754549Seric 		p++;
5764549Seric 
5774549Seric 	/* see if the input word matches desired word */
57833725Sbostic 	if (strcasecmp(q, w))
5794549Seric 		goto syntax;
5804549Seric 
5814549Seric 	return (p);
5824549Seric }
5834577Seric /*
58458151Seric **  PRINTVRFYADDR -- print an entry in the verify queue
58558151Seric **
58658151Seric **	Parameters:
58758151Seric **		a -- the address to print
58858151Seric **		last -- set if this is the last one.
58958151Seric **
59058151Seric **	Returns:
59158151Seric **		none.
59258151Seric **
59358151Seric **	Side Effects:
59458151Seric **		Prints the appropriate 250 codes.
59558151Seric */
59658151Seric 
59758151Seric printvrfyaddr(a, last)
59858151Seric 	register ADDRESS *a;
59958151Seric 	bool last;
60058151Seric {
60158151Seric 	char fmtbuf[20];
60258151Seric 
60358151Seric 	strcpy(fmtbuf, "250");
60458151Seric 	fmtbuf[3] = last ? ' ' : '-';
60558151Seric 
60658151Seric 	if (strchr(a->q_paddr, '<') != NULL)
60758151Seric 		strcpy(&fmtbuf[4], "%s");
60858151Seric 	else if (a->q_fullname == NULL)
60958151Seric 		strcpy(&fmtbuf[4], "<%s>");
61058151Seric 	else
61158151Seric 	{
61258151Seric 		strcpy(&fmtbuf[4], "%s <%s>");
61358151Seric 		message(fmtbuf, a->q_fullname, a->q_paddr);
61458151Seric 		return;
61558151Seric 	}
61658151Seric 	message(fmtbuf, a->q_paddr);
61758151Seric }
61858151Seric /*
6194577Seric **  HELP -- implement the HELP command.
6204577Seric **
6214577Seric **	Parameters:
6224577Seric **		topic -- the topic we want help for.
6234577Seric **
6244577Seric **	Returns:
6254577Seric **		none.
6264577Seric **
6274577Seric **	Side Effects:
6284577Seric **		outputs the help file to message output.
6294577Seric */
6304577Seric 
6314577Seric help(topic)
6324577Seric 	char *topic;
6334577Seric {
6344577Seric 	register FILE *hf;
6354577Seric 	int len;
6364577Seric 	char buf[MAXLINE];
6374577Seric 	bool noinfo;
6384577Seric 
6398269Seric 	if (HelpFile == NULL || (hf = fopen(HelpFile, "r")) == NULL)
6404577Seric 	{
6414577Seric 		/* no help */
64211931Seric 		errno = 0;
64358151Seric 		message("502 HELP not implemented");
6444577Seric 		return;
6454577Seric 	}
6464577Seric 
64749669Seric 	if (topic == NULL || *topic == '\0')
64849669Seric 		topic = "smtp";
64949669Seric 	else
65049669Seric 		makelower(topic);
65149669Seric 
6524577Seric 	len = strlen(topic);
6534577Seric 	noinfo = TRUE;
6544577Seric 
6554577Seric 	while (fgets(buf, sizeof buf, hf) != NULL)
6564577Seric 	{
6574577Seric 		if (strncmp(buf, topic, len) == 0)
6584577Seric 		{
6594577Seric 			register char *p;
6604577Seric 
66156795Seric 			p = strchr(buf, '\t');
6624577Seric 			if (p == NULL)
6634577Seric 				p = buf;
6644577Seric 			else
6654577Seric 				p++;
6664577Seric 			fixcrlf(p, TRUE);
66758151Seric 			message("214-%s", p);
6684577Seric 			noinfo = FALSE;
6694577Seric 		}
6704577Seric 	}
6714577Seric 
6724577Seric 	if (noinfo)
67358151Seric 		message("504 HELP topic unknown");
6744577Seric 	else
67558151Seric 		message("214 End of HELP info");
6764628Seric 	(void) fclose(hf);
6774577Seric }
6788544Seric /*
6799339Seric **  RUNINCHILD -- return twice -- once in the child, then in the parent again
6809339Seric **
6819339Seric **	Parameters:
6829339Seric **		label -- a string used in error messages
6839339Seric **
6849339Seric **	Returns:
6859339Seric **		zero in the child
6869339Seric **		one in the parent
6879339Seric **
6889339Seric **	Side Effects:
6899339Seric **		none.
6909339Seric */
6918544Seric 
69255012Seric runinchild(label, e)
6939339Seric 	char *label;
69455012Seric 	register ENVELOPE *e;
6959339Seric {
6969339Seric 	int childpid;
6979339Seric 
69816158Seric 	if (!OneXact)
6999339Seric 	{
70016158Seric 		childpid = dofork();
70116158Seric 		if (childpid < 0)
70216158Seric 		{
70316158Seric 			syserr("%s: cannot fork", label);
70416158Seric 			return (1);
70516158Seric 		}
70616158Seric 		if (childpid > 0)
70716158Seric 		{
70816158Seric 			auto int st;
7099339Seric 
71016158Seric 			/* parent -- wait for child to complete */
71116158Seric 			st = waitfor(childpid);
71216158Seric 			if (st == -1)
71316158Seric 				syserr("%s: lost child", label);
7149339Seric 
71516158Seric 			/* if we exited on a QUIT command, complete the process */
71616158Seric 			if (st == (EX_QUIT << 8))
71716158Seric 				finis();
7189339Seric 
71916158Seric 			return (1);
72016158Seric 		}
72116158Seric 		else
72216158Seric 		{
72316158Seric 			/* child */
72416158Seric 			InChild = TRUE;
72525050Seric 			QuickAbort = FALSE;
72655012Seric 			clearenvelope(e, FALSE);
72716158Seric 		}
7289339Seric 	}
72915256Seric 
73016158Seric 	/* open alias database */
73155012Seric 	initaliases(AliasFile, FALSE, e);
73216158Seric 
73316158Seric 	return (0);
7349339Seric }
7359339Seric 
73656795Seric # endif /* SMTP */
737