122712Sdist /*
234921Sbostic  * Copyright (c) 1983 Eric P. Allman
362532Sbostic  * Copyright (c) 1988, 1993
462532Sbostic  *	The Regents of the University of California.  All rights reserved.
533731Sbostic  *
642829Sbostic  * %sccs.include.redist.c%
733731Sbostic  */
822712Sdist 
933731Sbostic # include "sendmail.h"
1022712Sdist 
1133731Sbostic #ifndef lint
1233731Sbostic #ifdef SMTP
13*68751Seric static char sccsid[] = "@(#)srvrsmtp.c	8.65 (Berkeley) 04/08/95 (with SMTP)";
1433731Sbostic #else
15*68751Seric static char sccsid[] = "@(#)srvrsmtp.c	8.65 (Berkeley) 04/08/95 (without SMTP)";
1633731Sbostic #endif
1733731Sbostic #endif /* not lint */
1833731Sbostic 
199339Seric # include <errno.h>
204549Seric 
2133731Sbostic # ifdef SMTP
224556Seric 
234549Seric /*
244549Seric **  SMTP -- run the SMTP protocol.
254549Seric **
264549Seric **	Parameters:
274549Seric **		none.
284549Seric **
294549Seric **	Returns:
304549Seric **		never.
314549Seric **
324549Seric **	Side Effects:
334549Seric **		Reads commands from the input channel and processes
344549Seric **			them.
354549Seric */
364549Seric 
374549Seric struct cmd
384549Seric {
394549Seric 	char	*cmdname;	/* command name */
404549Seric 	int	cmdcode;	/* internal code, see below */
414549Seric };
424549Seric 
434549Seric /* values for cmdcode */
444549Seric # define CMDERROR	0	/* bad command */
454549Seric # define CMDMAIL	1	/* mail -- designate sender */
464976Seric # define CMDRCPT	2	/* rcpt -- designate recipient */
474549Seric # define CMDDATA	3	/* data -- send message text */
489339Seric # define CMDRSET	4	/* rset -- reset state */
499339Seric # define CMDVRFY	5	/* vrfy -- verify address */
5058092Seric # define CMDEXPN	6	/* expn -- expand address */
519339Seric # define CMDNOOP	7	/* noop -- do nothing */
529339Seric # define CMDQUIT	8	/* quit -- close connection and die */
539339Seric # define CMDHELO	9	/* helo -- be polite */
5458092Seric # define CMDHELP	10	/* help -- give usage info */
5558323Seric # define CMDEHLO	11	/* ehlo -- extended helo (RFC 1425) */
5658092Seric /* non-standard commands */
5758092Seric # define CMDONEX	16	/* onex -- sending one transaction only */
5858092Seric # define CMDVERB	17	/* verb -- go into verbose mode */
5964685Seric /* use this to catch and log "door handle" attempts on your system */
6064685Seric # define CMDLOGBOGUS	23	/* bogus command that should be logged */
6136230Skarels /* debugging-only commands, only enabled if SMTPDEBUG is defined */
6258092Seric # define CMDDBGQSHOW	24	/* showq -- show send queue */
6358092Seric # define CMDDBGDEBUG	25	/* debug -- set debug mode */
644549Seric 
654549Seric static struct cmd	CmdTab[] =
664549Seric {
674549Seric 	"mail",		CMDMAIL,
684976Seric 	"rcpt",		CMDRCPT,
694549Seric 	"data",		CMDDATA,
704549Seric 	"rset",		CMDRSET,
714549Seric 	"vrfy",		CMDVRFY,
7258092Seric 	"expn",		CMDEXPN,
734549Seric 	"help",		CMDHELP,
744549Seric 	"noop",		CMDNOOP,
754549Seric 	"quit",		CMDQUIT,
764976Seric 	"helo",		CMDHELO,
7758323Seric 	"ehlo",		CMDEHLO,
788544Seric 	"verb",		CMDVERB,
799314Seric 	"onex",		CMDONEX,
8036230Skarels 	/*
8136230Skarels 	 * remaining commands are here only
8236230Skarels 	 * to trap and log attempts to use them
8336230Skarels 	 */
849339Seric 	"showq",	CMDDBGQSHOW,
858544Seric 	"debug",	CMDDBGDEBUG,
8664685Seric 	"wiz",		CMDLOGBOGUS,
874549Seric 	NULL,		CMDERROR,
884549Seric };
894549Seric 
909378Seric bool	OneXact = FALSE;		/* one xaction only this run */
9165017Seric char	*CurSmtpClient;			/* who's at the other end of channel */
9211146Seric 
9363937Seric static char	*skipword();
9466325Seric extern char	RealUserName[];
9563937Seric 
9666325Seric 
9766283Seric #define MAXBADCOMMANDS	25		/* maximum number of bad commands */
9866283Seric 
9955012Seric smtp(e)
10055012Seric 	register ENVELOPE *e;
1014549Seric {
1024549Seric 	register char *p;
1038544Seric 	register struct cmd *c;
1044549Seric 	char *cmd;
1055003Seric 	auto ADDRESS *vrfyqueue;
10612612Seric 	ADDRESS *a;
10758109Seric 	bool gotmail;			/* mail command received */
10858092Seric 	bool gothello;			/* helo command received */
10958092Seric 	bool vrfy;			/* set if this is a vrfy command */
11058323Seric 	char *protocol;			/* sending protocol */
11159016Seric 	char *sendinghost;		/* sending hostname */
11266005Seric 	char *peerhostname;		/* name of SMTP peer or "localhost" */
11358333Seric 	auto char *delimptr;
11458714Seric 	char *id;
11568433Seric 	int nrcpts = 0;			/* number of RCPT commands */
11663787Seric 	bool doublequeue;
11766283Seric 	int badcommands = 0;		/* count of bad commands */
1188544Seric 	char inp[MAXLINE];
11957232Seric 	char cmdbuf[MAXLINE];
1207124Seric 	extern char Version[];
12124943Seric 	extern ENVELOPE BlankEnvelope;
1224549Seric 
12359066Seric 	if (fileno(OutChannel) != fileno(stdout))
1247363Seric 	{
1257363Seric 		/* arrange for debugging output to go to remote host */
12659066Seric 		(void) dup2(fileno(OutChannel), fileno(stdout));
1277363Seric 	}
12855012Seric 	settime(e);
12966005Seric 	peerhostname = RealHostName;
13066005Seric 	if (peerhostname == NULL)
13166005Seric 		peerhostname = "localhost";
13266005Seric 	CurHostName = peerhostname;
13365017Seric 	CurSmtpClient = macvalue('_', e);
13465017Seric 	if (CurSmtpClient == NULL)
13566003Seric 		CurSmtpClient = CurHostName;
13665017Seric 
13765017Seric 	setproctitle("server %s startup", CurSmtpClient);
13868529Seric 	expand("\201e", inp, sizeof inp, e);
13964496Seric 	if (BrokenSmtpPeers)
14064496Seric 	{
14166762Seric 		p = strchr(inp, '\n');
14266762Seric 		if (p != NULL)
14366762Seric 			*p = '\0';
14464496Seric 		message("220 %s", inp);
14564496Seric 	}
14664496Seric 	else
14764496Seric 	{
14866745Seric 		char *q = inp;
14966745Seric 
15066745Seric 		while (q != NULL)
15166745Seric 		{
15266762Seric 			p = strchr(q, '\n');
15366762Seric 			if (p != NULL)
15466762Seric 				*p++ = '\0';
15566745Seric 			message("220-%s", q);
15666745Seric 			q = p;
15766745Seric 		}
15864496Seric 		message("220 ESMTP spoken here");
15964496Seric 	}
16058330Seric 	protocol = NULL;
16159016Seric 	sendinghost = macvalue('s', e);
16258082Seric 	gothello = FALSE;
16358330Seric 	gotmail = FALSE;
1644549Seric 	for (;;)
1654549Seric 	{
16612612Seric 		/* arrange for backout */
16765751Seric 		if (setjmp(TopFrame) > 0)
16859058Seric 		{
16965751Seric 			/* if() nesting is necessary for Cray UNICOS */
17065751Seric 			if (InChild)
17165751Seric 			{
17265751Seric 				QuickAbort = FALSE;
17365751Seric 				SuprErrs = TRUE;
17465751Seric 				finis();
17565751Seric 			}
17659058Seric 		}
17712612Seric 		QuickAbort = FALSE;
17812612Seric 		HoldErrs = FALSE;
17951951Seric 		LogUsrErrs = FALSE;
18063843Seric 		e->e_flags &= ~(EF_VRFYONLY|EF_GLOBALERRS);
18112612Seric 
1827356Seric 		/* setup for the read */
18355012Seric 		e->e_to = NULL;
1844577Seric 		Errors = 0;
1857275Seric 		(void) fflush(stdout);
1867356Seric 
1877356Seric 		/* read the input line */
18861093Seric 		SmtpPhase = "server cmd read";
18961093Seric 		setproctitle("server %s cmd read", CurHostName);
19061093Seric 		p = sfgets(inp, sizeof inp, InChannel, TimeOuts.to_nextcommand,
19161093Seric 				SmtpPhase);
1927356Seric 
1937685Seric 		/* handle errors */
1947356Seric 		if (p == NULL)
1957356Seric 		{
1964549Seric 			/* end of file, just die */
19766017Seric 			disconnect(1, e);
19858151Seric 			message("421 %s Lost input channel from %s",
19965017Seric 				MyHostName, CurSmtpClient);
20055464Seric #ifdef LOG
20163843Seric 			if (LogLevel > (gotmail ? 1 : 19))
20255464Seric 				syslog(LOG_NOTICE, "lost input channel from %s",
20365017Seric 					CurSmtpClient);
20455464Seric #endif
20558069Seric 			if (InChild)
20658069Seric 				ExitStat = EX_QUIT;
2074549Seric 			finis();
2084549Seric 		}
2094549Seric 
2104549Seric 		/* clean up end of line */
2114558Seric 		fixcrlf(inp, TRUE);
2124549Seric 
2134713Seric 		/* echo command to transcript */
21455012Seric 		if (e->e_xfp != NULL)
21555012Seric 			fprintf(e->e_xfp, "<<< %s\n", inp);
2164713Seric 
21759060Seric 		if (e->e_id == NULL)
21865058Seric 			setproctitle("%s: %.80s", CurSmtpClient, inp);
21959060Seric 		else
22065058Seric 			setproctitle("%s %s: %.80s", e->e_id, CurSmtpClient, inp);
22159060Seric 
2224549Seric 		/* break off command */
22358050Seric 		for (p = inp; isascii(*p) && isspace(*p); p++)
2244549Seric 			continue;
22557232Seric 		cmd = cmdbuf;
22658050Seric 		while (*p != '\0' &&
22758050Seric 		       !(isascii(*p) && isspace(*p)) &&
22858050Seric 		       cmd < &cmdbuf[sizeof cmdbuf - 2])
22924981Seric 			*cmd++ = *p++;
23024981Seric 		*cmd = '\0';
2314549Seric 
23225691Seric 		/* throw away leading whitespace */
23358050Seric 		while (isascii(*p) && isspace(*p))
23425691Seric 			p++;
23525691Seric 
2364549Seric 		/* decode command */
2374549Seric 		for (c = CmdTab; c->cmdname != NULL; c++)
2384549Seric 		{
23933725Sbostic 			if (!strcasecmp(c->cmdname, cmdbuf))
2404549Seric 				break;
2414549Seric 		}
2424549Seric 
24351954Seric 		/* reset errors */
24451954Seric 		errno = 0;
24551954Seric 
2464549Seric 		/* process command */
2474549Seric 		switch (c->cmdcode)
2484549Seric 		{
2494976Seric 		  case CMDHELO:		/* hello -- introduce yourself */
25058323Seric 		  case CMDEHLO:		/* extended hello */
25158323Seric 			if (c->cmdcode == CMDEHLO)
25258323Seric 			{
25358323Seric 				protocol = "ESMTP";
25461093Seric 				SmtpPhase = "server EHLO";
25558323Seric 			}
25658323Seric 			else
25758323Seric 			{
25858323Seric 				protocol = "SMTP";
25961093Seric 				SmtpPhase = "server HELO";
26058323Seric 			}
26167445Seric 
26267445Seric 			/* check for valid domain name (re 1123 5.2.5) */
26367445Seric 			if (*p == '\0')
26467445Seric 			{
26567445Seric 				message("501 %s requires domain address",
26667445Seric 					cmdbuf);
26767445Seric 				break;
26867445Seric 			}
26967445Seric 			else
27067445Seric 			{
27167445Seric 				register char *q;
27267445Seric 
27367445Seric 				for (q = p; *q != '\0'; q++)
27467445Seric 				{
27567445Seric 					if (!isascii(*q))
27667445Seric 						break;
27767445Seric 					if (isalnum(*q))
27867445Seric 						continue;
27967445Seric 					if (strchr("[].-_#", *q) == NULL)
28067445Seric 						break;
28167445Seric 				}
28267445Seric 				if (*q != '\0')
28367445Seric 				{
28467445Seric 					message("501 Invalid domain name");
28567445Seric 					break;
28667445Seric 				}
28767445Seric 			}
28867445Seric 
28959016Seric 			sendinghost = newstr(p);
29060210Seric 			gothello = TRUE;
29160210Seric 			if (c->cmdcode != CMDEHLO)
29260239Seric 			{
29360239Seric 				/* print old message and be done with it */
29460239Seric 				message("250 %s Hello %s, pleased to meet you",
29565017Seric 					MyHostName, CurSmtpClient);
29660210Seric 				break;
29760239Seric 			}
29860239Seric 
29960239Seric 			/* print extended message and brag */
30060239Seric 			message("250-%s Hello %s, pleased to meet you",
30166760Seric 				MyHostName, CurSmtpClient);
30258323Seric 			if (!bitset(PRIV_NOEXPN, PrivacyFlags))
30358323Seric 				message("250-EXPN");
30467417Seric 			message("250-8BITMIME");
30564359Seric 			if (MaxMessageSize > 0)
30664359Seric 				message("250-SIZE %ld", MaxMessageSize);
30759271Seric 			else
30859271Seric 				message("250-SIZE");
30968028Seric #ifdef DSN
31068606Seric 			message("250-X-DSN-03 (Unpublished draft of 12 Mar 1995)");
31168028Seric #endif
31258323Seric 			message("250 HELP");
3134976Seric 			break;
3144976Seric 
3154549Seric 		  case CMDMAIL:		/* mail -- designate sender */
31661093Seric 			SmtpPhase = "server MAIL";
31724943Seric 
3189314Seric 			/* check for validity of this command */
31958789Seric 			if (!gothello)
32058082Seric 			{
32158957Seric 				/* set sending host to our known value */
32259016Seric 				if (sendinghost == NULL)
32366005Seric 					sendinghost = peerhostname;
32458957Seric 
32558789Seric 				if (bitset(PRIV_NEEDMAILHELO, PrivacyFlags))
32658821Seric 				{
32758789Seric 					message("503 Polite people say HELO first");
32858821Seric 					break;
32958821Seric 				}
33058082Seric 			}
33158109Seric 			if (gotmail)
3324558Seric 			{
33358151Seric 				message("503 Sender already specified");
33463843Seric 				if (InChild)
33563843Seric 					finis();
3364558Seric 				break;
3374558Seric 			}
3389339Seric 			if (InChild)
3399339Seric 			{
34036230Skarels 				errno = 0;
34158151Seric 				syserr("503 Nested MAIL command: MAIL %s", p);
34258069Seric 				finis();
3439339Seric 			}
3449339Seric 
3459339Seric 			/* fork a subprocess to process this command */
34655012Seric 			if (runinchild("SMTP-MAIL", e) > 0)
3479339Seric 				break;
34863753Seric 			if (!gothello)
34963753Seric 			{
35063753Seric 				auth_warning(e,
35166005Seric 					"Host %s didn't use HELO protocol",
35266005Seric 					peerhostname);
35363753Seric 			}
35465947Seric #ifdef PICKY_HELO_CHECK
35566005Seric 			if (strcasecmp(sendinghost, peerhostname) != 0 &&
35666005Seric 			    (strcasecmp(peerhostname, "localhost") != 0 ||
35765823Seric 			     strcasecmp(sendinghost, MyHostName) != 0))
35865823Seric 			{
35965823Seric 				auth_warning(e, "Host %s claimed to be %s",
36066005Seric 					peerhostname, sendinghost);
36165823Seric 			}
36265947Seric #endif
36365823Seric 
36458323Seric 			if (protocol == NULL)
36558323Seric 				protocol = "SMTP";
36658323Seric 			define('r', protocol, e);
36759016Seric 			define('s', sendinghost, e);
36855012Seric 			initsys(e);
36959747Seric 			nrcpts = 0;
37068582Seric 			e->e_flags |= EF_LOGSENDER|EF_CLRQUEUE;
37165058Seric 			setproctitle("%s %s: %.80s", e->e_id, CurSmtpClient, inp);
3729339Seric 
3739339Seric 			/* child -- go do the processing */
3744549Seric 			p = skipword(p, "from");
3754549Seric 			if (p == NULL)
3764549Seric 				break;
37757977Seric 			if (setjmp(TopFrame) > 0)
37858147Seric 			{
37958147Seric 				/* this failed -- undo work */
38058147Seric 				if (InChild)
38159058Seric 				{
38259058Seric 					QuickAbort = FALSE;
38359058Seric 					SuprErrs = TRUE;
38463787Seric 					e->e_flags &= ~EF_FATALERRS;
38558147Seric 					finis();
38659058Seric 				}
38757977Seric 				break;
38858147Seric 			}
38957977Seric 			QuickAbort = TRUE;
39058333Seric 
39158333Seric 			/* must parse sender first */
39258333Seric 			delimptr = NULL;
39358704Seric 			setsender(p, e, &delimptr, FALSE);
39458333Seric 			p = delimptr;
39558333Seric 			if (p != NULL && *p != '\0')
39658333Seric 				*p++ = '\0';
39758333Seric 
39866325Seric 			/* check for possible spoofing */
39966325Seric 			if (RealUid != 0 && OpMode == MD_SMTP &&
40067473Seric 			    !bitnset(M_LOCALMAILER, e->e_from.q_mailer->m_flags) &&
40167473Seric 			    strcmp(e->e_from.q_user, RealUserName) != 0)
40266325Seric 			{
40366325Seric 				auth_warning(e, "%s owned process doing -bs",
40466325Seric 					RealUserName);
40566325Seric 			}
40666325Seric 
40758333Seric 			/* now parse ESMTP arguments */
40868560Seric 			e->e_msgsize = 0;
40966764Seric 			while (p != NULL && *p != '\0')
41058333Seric 			{
41158333Seric 				char *kp;
41266304Seric 				char *vp = NULL;
41358333Seric 
41458333Seric 				/* locate the beginning of the keyword */
41558333Seric 				while (isascii(*p) && isspace(*p))
41658333Seric 					p++;
41758333Seric 				if (*p == '\0')
41858333Seric 					break;
41958333Seric 				kp = p;
42058333Seric 
42158333Seric 				/* skip to the value portion */
42258333Seric 				while (isascii(*p) && isalnum(*p) || *p == '-')
42358333Seric 					p++;
42458333Seric 				if (*p == '=')
42558333Seric 				{
42658333Seric 					*p++ = '\0';
42758333Seric 					vp = p;
42858333Seric 
42958333Seric 					/* skip to the end of the value */
43058333Seric 					while (*p != '\0' && *p != ' ' &&
43158333Seric 					       !(isascii(*p) && iscntrl(*p)) &&
43258333Seric 					       *p != '=')
43358333Seric 						p++;
43458333Seric 				}
43558333Seric 
43658333Seric 				if (*p != '\0')
43758333Seric 					*p++ = '\0';
43858333Seric 
43958333Seric 				if (tTd(19, 1))
44066764Seric 					printf("MAIL: got arg %s=\"%s\"\n", kp,
44158333Seric 						vp == NULL ? "<null>" : vp);
44258333Seric 
44368559Seric 				mail_esmtp_args(kp, vp, e);
44458333Seric 			}
44559284Seric 
44668560Seric 			if (MaxMessageSize > 0 && e->e_msgsize > MaxMessageSize)
44759284Seric 			{
44859284Seric 				usrerr("552 Message size exceeds fixed maximum message size (%ld)",
44959284Seric 					MaxMessageSize);
45059284Seric 				/* NOTREACHED */
45159284Seric 			}
45258333Seric 
45368560Seric 			if (!enoughspace(e->e_msgsize))
45458333Seric 			{
45558333Seric 				message("452 Insufficient disk space; try again later");
45658333Seric 				break;
45758333Seric 			}
45858151Seric 			message("250 Sender ok");
45958147Seric 			gotmail = TRUE;
4604549Seric 			break;
4614549Seric 
4624976Seric 		  case CMDRCPT:		/* rcpt -- designate recipient */
46358850Seric 			if (!gotmail)
46458850Seric 			{
46558850Seric 				usrerr("503 Need MAIL before RCPT");
46658850Seric 				break;
46758850Seric 			}
46861093Seric 			SmtpPhase = "server RCPT";
46912612Seric 			if (setjmp(TopFrame) > 0)
47014785Seric 			{
47155012Seric 				e->e_flags &= ~EF_FATALERRS;
47212612Seric 				break;
47314785Seric 			}
47412612Seric 			QuickAbort = TRUE;
47551951Seric 			LogUsrErrs = TRUE;
47658093Seric 
47759699Seric 			if (e->e_sendmode != SM_DELIVER)
47859699Seric 				e->e_flags |= EF_VRFYONLY;
47958919Seric 
4804549Seric 			p = skipword(p, "to");
4814549Seric 			if (p == NULL)
4824549Seric 				break;
48367880Seric 			a = parseaddr(p, NULLADDR, RF_COPYALL, ' ', &delimptr, e);
48412612Seric 			if (a == NULL)
48512612Seric 				break;
48667880Seric 			p = delimptr;
48767880Seric 
48867880Seric 			/* now parse ESMTP arguments */
48967880Seric 			while (p != NULL && *p != '\0')
49067880Seric 			{
49167880Seric 				char *kp;
49267880Seric 				char *vp = NULL;
49367880Seric 
49467880Seric 				/* locate the beginning of the keyword */
49567880Seric 				while (isascii(*p) && isspace(*p))
49667880Seric 					p++;
49767880Seric 				if (*p == '\0')
49867880Seric 					break;
49967880Seric 				kp = p;
50067880Seric 
50167880Seric 				/* skip to the value portion */
50267880Seric 				while (isascii(*p) && isalnum(*p) || *p == '-')
50367880Seric 					p++;
50467880Seric 				if (*p == '=')
50567880Seric 				{
50667880Seric 					*p++ = '\0';
50767880Seric 					vp = p;
50867880Seric 
50967880Seric 					/* skip to the end of the value */
51067880Seric 					while (*p != '\0' && *p != ' ' &&
51167880Seric 					       !(isascii(*p) && iscntrl(*p)) &&
51267880Seric 					       *p != '=')
51367880Seric 						p++;
51467880Seric 				}
51567880Seric 
51667880Seric 				if (*p != '\0')
51767880Seric 					*p++ = '\0';
51867880Seric 
51967880Seric 				if (tTd(19, 1))
52067880Seric 					printf("RCPT: got arg %s=\"%s\"\n", kp,
52167880Seric 						vp == NULL ? "<null>" : vp);
52267880Seric 
52367963Seric 				rcpt_esmtp_args(a, kp, vp, e);
52467963Seric 
52567880Seric 			}
52667980Seric 
52767980Seric 			/* save in recipient list after ESMTP mods */
52867982Seric 			a = recipient(a, &e->e_sendqueue, 0, e);
52967980Seric 
53012612Seric 			if (Errors != 0)
53112612Seric 				break;
53212612Seric 
53312612Seric 			/* no errors during parsing, but might be a duplicate */
53455012Seric 			e->e_to = p;
53512612Seric 			if (!bitset(QBADADDR, a->q_flags))
53659747Seric 			{
53764718Seric 				message("250 Recipient ok%s",
53864718Seric 					bitset(QQUEUEUP, a->q_flags) ?
53964718Seric 						" (will queue)" : "");
54059747Seric 				nrcpts++;
54159747Seric 			}
54212612Seric 			else
5434549Seric 			{
54412612Seric 				/* punt -- should keep message in ADDRESS.... */
54558151Seric 				message("550 Addressee unknown");
5464549Seric 			}
54755012Seric 			e->e_to = NULL;
5484549Seric 			break;
5494549Seric 
5504549Seric 		  case CMDDATA:		/* data -- text of mail */
55161093Seric 			SmtpPhase = "server DATA";
55258109Seric 			if (!gotmail)
5534549Seric 			{
55458151Seric 				message("503 Need MAIL command");
5554976Seric 				break;
5564549Seric 			}
55764718Seric 			else if (nrcpts <= 0)
5584549Seric 			{
55958151Seric 				message("503 Need RCPT (recipient)");
5604976Seric 				break;
5614549Seric 			}
5624976Seric 
56358929Seric 			/* check to see if we need to re-expand aliases */
56463787Seric 			/* also reset QBADADDR on already-diagnosted addrs */
56563787Seric 			doublequeue = FALSE;
56658929Seric 			for (a = e->e_sendqueue; a != NULL; a = a->q_next)
56758929Seric 			{
56858929Seric 				if (bitset(QVERIFIED, a->q_flags))
56963787Seric 				{
57063787Seric 					/* need to re-expand aliases */
57163787Seric 					doublequeue = TRUE;
57263787Seric 				}
57363787Seric 				if (bitset(QBADADDR, a->q_flags))
57463787Seric 				{
57563787Seric 					/* make this "go away" */
57663787Seric 					a->q_flags |= QDONTSEND;
57763787Seric 					a->q_flags &= ~QBADADDR;
57863787Seric 				}
57958929Seric 			}
58058929Seric 
5814976Seric 			/* collect the text of the message */
58224943Seric 			SmtpPhase = "collect";
58368692Seric 			buffer_errors();
58467546Seric 			collect(InChannel, TRUE, doublequeue, NULL, e);
58568692Seric 			flush_errors(TRUE);
58664766Seric 			if (Errors != 0)
58764766Seric 				goto abortmessage;
58867131Seric 
58968582Seric 			/* make sure we actually do delivery */
59068582Seric 			e->e_flags &= ~EF_CLRQUEUE;
59168582Seric 
59267131Seric 			/* from now on, we have to operate silently */
59368692Seric 			buffer_errors();
59467131Seric 			e->e_errormode = EM_MAIL;
5954976Seric 
5968238Seric 			/*
5978238Seric 			**  Arrange to send to everyone.
5988238Seric 			**	If sending to multiple people, mail back
5998238Seric 			**		errors rather than reporting directly.
6008238Seric 			**	In any case, don't mail back errors for
6018238Seric 			**		anything that has happened up to
6028238Seric 			**		now (the other end will do this).
60310197Seric 			**	Truncate our transcript -- the mail has gotten
60410197Seric 			**		to us successfully, and if we have
60510197Seric 			**		to mail this back, it will be easier
60610197Seric 			**		on the reader.
6078238Seric 			**	Then send to everyone.
6088238Seric 			**	Finally give a reply code.  If an error has
6098238Seric 			**		already been given, don't mail a
6108238Seric 			**		message back.
6119339Seric 			**	We goose error returns by clearing error bit.
6128238Seric 			*/
6138238Seric 
61424943Seric 			SmtpPhase = "delivery";
61555012Seric 			e->e_xfp = freopen(queuename(e, 'x'), "w", e->e_xfp);
61658714Seric 			id = e->e_id;
6174976Seric 
61867131Seric 			if (doublequeue)
61959730Seric 			{
62067131Seric 				/* make sure it is in the queue */
62167131Seric 				queueup(e, TRUE, FALSE);
62268035Seric 				if (e->e_sendmode == SM_QUEUE)
62368035Seric 					e->e_flags |= EF_KEEPQUEUE;
62459730Seric 			}
6258238Seric 			else
62658919Seric 			{
62767131Seric 				/* send to all recipients */
62867131Seric 				sendall(e, SM_DEFAULT);
62967131Seric 			}
63067131Seric 			e->e_to = NULL;
63159747Seric 
63267131Seric 			/* issue success message */
63367131Seric 			message("250 %s Message accepted for delivery", id);
63464296Seric 
63567131Seric 			/* if we just queued, poke it */
63667131Seric 			if (doublequeue && e->e_sendmode != SM_QUEUE)
63767131Seric 			{
63867131Seric 				extern pid_t dowork();
63967131Seric 
64067131Seric 				unlockqueue(e);
64167131Seric 				(void) dowork(id, TRUE, TRUE, e);
64258919Seric 			}
64358883Seric 
64459747Seric   abortmessage:
6459339Seric 			/* if in a child, pop back to our parent */
6469339Seric 			if (InChild)
6479339Seric 				finis();
64824943Seric 
64924943Seric 			/* clean up a bit */
65058109Seric 			gotmail = FALSE;
65155012Seric 			dropenvelope(e);
65258179Seric 			CurEnv = e = newenvelope(e, CurEnv);
65355012Seric 			e->e_flags = BlankEnvelope.e_flags;
6544549Seric 			break;
6554549Seric 
6564549Seric 		  case CMDRSET:		/* rset -- reset state */
65758151Seric 			message("250 Reset state");
65868603Seric 
65968603Seric 			/* arrange to ignore any current send list */
66068603Seric 			e->e_sendqueue = NULL;
66164359Seric 			e->e_flags |= EF_CLRQUEUE;
6629339Seric 			if (InChild)
6639339Seric 				finis();
66458109Seric 
66558109Seric 			/* clean up a bit */
66658109Seric 			gotmail = FALSE;
66758109Seric 			dropenvelope(e);
66858179Seric 			CurEnv = e = newenvelope(e, CurEnv);
6699339Seric 			break;
6704549Seric 
6714549Seric 		  case CMDVRFY:		/* vrfy -- verify address */
67258092Seric 		  case CMDEXPN:		/* expn -- expand address */
67358092Seric 			vrfy = c->cmdcode == CMDVRFY;
67458092Seric 			if (bitset(vrfy ? PRIV_NOVRFY : PRIV_NOEXPN,
67558092Seric 						PrivacyFlags))
67658082Seric 			{
67758412Seric 				if (vrfy)
67867160Seric 					message("252 Cannot VRFY user; try RCPT to attempt delivery (or try finger)");
67958412Seric 				else
68065192Seric 					message("502 Sorry, we do not allow this operation");
68165017Seric #ifdef LOG
68265017Seric 				if (LogLevel > 5)
68365017Seric 					syslog(LOG_INFO, "%s: %s [rejected]",
68465017Seric 						CurSmtpClient, inp);
68565017Seric #endif
68658082Seric 				break;
68758082Seric 			}
68858082Seric 			else if (!gothello &&
68958092Seric 				 bitset(vrfy ? PRIV_NEEDVRFYHELO : PRIV_NEEDEXPNHELO,
69058092Seric 						PrivacyFlags))
69158082Seric 			{
69258151Seric 				message("503 I demand that you introduce yourself first");
69358082Seric 				break;
69458082Seric 			}
69558092Seric 			if (runinchild(vrfy ? "SMTP-VRFY" : "SMTP-EXPN", e) > 0)
6969339Seric 				break;
69755173Seric #ifdef LOG
69858020Seric 			if (LogLevel > 5)
69965017Seric 				syslog(LOG_INFO, "%s: %s", CurSmtpClient, inp);
70055173Seric #endif
7015003Seric 			vrfyqueue = NULL;
7027762Seric 			QuickAbort = TRUE;
70358092Seric 			if (vrfy)
70458092Seric 				e->e_flags |= EF_VRFYONLY;
70562373Seric 			while (*p != '\0' && isascii(*p) && isspace(*p))
70667615Seric 				p++;
70762373Seric 			if (*p == '\0')
70862373Seric 			{
70962373Seric 				message("501 Argument required");
71062373Seric 				Errors++;
71162373Seric 			}
71262373Seric 			else
71362373Seric 			{
71467990Seric 				(void) sendtolist(p, NULLADDR, &vrfyqueue, 0, e);
71562373Seric 			}
7167762Seric 			if (Errors != 0)
7179339Seric 			{
7189339Seric 				if (InChild)
7199339Seric 					finis();
7207762Seric 				break;
7219339Seric 			}
72262373Seric 			if (vrfyqueue == NULL)
72362373Seric 			{
72462373Seric 				message("554 Nothing to %s", vrfy ? "VRFY" : "EXPN");
72562373Seric 			}
7265003Seric 			while (vrfyqueue != NULL)
7275003Seric 			{
72863971Seric 				a = vrfyqueue;
72963971Seric 				while ((a = a->q_next) != NULL &&
73063971Seric 				       bitset(QDONTSEND|QBADADDR, a->q_flags))
73163971Seric 					continue;
7327685Seric 				if (!bitset(QDONTSEND|QBADADDR, vrfyqueue->q_flags))
73358151Seric 					printvrfyaddr(vrfyqueue, a == NULL);
73463847Seric 				vrfyqueue = vrfyqueue->q_next;
7355003Seric 			}
7369339Seric 			if (InChild)
7379339Seric 				finis();
7384549Seric 			break;
7394549Seric 
7404549Seric 		  case CMDHELP:		/* help -- give user info */
7414577Seric 			help(p);
7424549Seric 			break;
7434549Seric 
7444549Seric 		  case CMDNOOP:		/* noop -- do nothing */
74564122Seric 			message("250 OK");
7464549Seric 			break;
7474549Seric 
7484549Seric 		  case CMDQUIT:		/* quit -- leave mail */
74958151Seric 			message("221 %s closing connection", MyHostName);
75061051Seric 
75166283Seric doquit:
75268603Seric 			/* arrange to ignore any current send list */
75368603Seric 			e->e_sendqueue = NULL;
75468603Seric 
75561051Seric 			/* avoid future 050 messages */
75666017Seric 			disconnect(1, e);
75761051Seric 
7589339Seric 			if (InChild)
7599339Seric 				ExitStat = EX_QUIT;
7604549Seric 			finis();
7614549Seric 
7628544Seric 		  case CMDVERB:		/* set verbose mode */
76359957Seric 			if (bitset(PRIV_NOEXPN, PrivacyFlags))
76459957Seric 			{
76559957Seric 				/* this would give out the same info */
76659957Seric 				message("502 Verbose unavailable");
76759957Seric 				break;
76859957Seric 			}
7698544Seric 			Verbose = TRUE;
77058734Seric 			e->e_sendmode = SM_DELIVER;
77159957Seric 			message("250 Verbose mode");
7728544Seric 			break;
7738544Seric 
7749314Seric 		  case CMDONEX:		/* doing one transaction only */
7759378Seric 			OneXact = TRUE;
77659957Seric 			message("250 Only one transaction");
7779314Seric 			break;
7789314Seric 
77936230Skarels # ifdef SMTPDEBUG
7809339Seric 		  case CMDDBGQSHOW:	/* show queues */
7816907Seric 			printf("Send Queue=");
78255012Seric 			printaddr(e->e_sendqueue, TRUE);
7835003Seric 			break;
7847275Seric 
7857275Seric 		  case CMDDBGDEBUG:	/* set debug mode */
7867676Seric 			tTsetup(tTdvect, sizeof tTdvect, "0-99.1");
7877676Seric 			tTflag(p);
78858151Seric 			message("200 Debug set");
7897275Seric 			break;
7907275Seric 
79136230Skarels # else /* not SMTPDEBUG */
79236230Skarels 		  case CMDDBGQSHOW:	/* show queues */
79336230Skarels 		  case CMDDBGDEBUG:	/* set debug mode */
79464685Seric # endif /* SMTPDEBUG */
79564685Seric 		  case CMDLOGBOGUS:	/* bogus command */
79636233Skarels # ifdef LOG
79758308Seric 			if (LogLevel > 0)
79864685Seric 				syslog(LOG_CRIT,
79958020Seric 				    "\"%s\" command from %s (%s)",
80066005Seric 				    c->cmdname, peerhostname,
80158755Seric 				    anynet_ntoa(&RealHostAddr));
80236233Skarels # endif
80336230Skarels 			/* FALL THROUGH */
80436230Skarels 
8054549Seric 		  case CMDERROR:	/* unknown command */
80666283Seric 			if (++badcommands > MAXBADCOMMANDS)
80766283Seric 			{
80866283Seric 				message("421 %s Too many bad commands; closing connection",
80966283Seric 					MyHostName);
81066283Seric 				goto doquit;
81166283Seric 			}
81266283Seric 
81358151Seric 			message("500 Command unrecognized");
8144549Seric 			break;
8154549Seric 
8164549Seric 		  default:
81736230Skarels 			errno = 0;
81858151Seric 			syserr("500 smtp: unknown code %d", c->cmdcode);
8194549Seric 			break;
8204549Seric 		}
8214549Seric 	}
8224549Seric }
8234549Seric /*
8244549Seric **  SKIPWORD -- skip a fixed word.
8254549Seric **
8264549Seric **	Parameters:
8274549Seric **		p -- place to start looking.
8284549Seric **		w -- word to skip.
8294549Seric **
8304549Seric **	Returns:
8314549Seric **		p following w.
8324549Seric **		NULL on error.
8334549Seric **
8344549Seric **	Side Effects:
8354549Seric **		clobbers the p data area.
8364549Seric */
8374549Seric 
8384549Seric static char *
8394549Seric skipword(p, w)
8404549Seric 	register char *p;
8414549Seric 	char *w;
8424549Seric {
8434549Seric 	register char *q;
84466005Seric 	char *firstp = p;
8454549Seric 
8464549Seric 	/* find beginning of word */
84758050Seric 	while (isascii(*p) && isspace(*p))
8484549Seric 		p++;
8494549Seric 	q = p;
8504549Seric 
8514549Seric 	/* find end of word */
85258050Seric 	while (*p != '\0' && *p != ':' && !(isascii(*p) && isspace(*p)))
8534549Seric 		p++;
85458050Seric 	while (isascii(*p) && isspace(*p))
8554549Seric 		*p++ = '\0';
8564549Seric 	if (*p != ':')
8574549Seric 	{
8584549Seric 	  syntax:
85966005Seric 		message("501 Syntax error in parameters scanning \"%s\"",
86066005Seric 			firstp);
8614549Seric 		Errors++;
8624549Seric 		return (NULL);
8634549Seric 	}
8644549Seric 	*p++ = '\0';
86558050Seric 	while (isascii(*p) && isspace(*p))
8664549Seric 		p++;
8674549Seric 
86862373Seric 	if (*p == '\0')
86962373Seric 		goto syntax;
87062373Seric 
8714549Seric 	/* see if the input word matches desired word */
87233725Sbostic 	if (strcasecmp(q, w))
8734549Seric 		goto syntax;
8744549Seric 
8754549Seric 	return (p);
8764549Seric }
8774577Seric /*
87868559Seric **  MAIL_ESMTP_ARGS -- process ESMTP arguments from MAIL line
87968559Seric **
88068559Seric **	Parameters:
88168559Seric **		kp -- the parameter key.
88268559Seric **		vp -- the value of that parameter.
88368559Seric **		e -- the envelope.
88468559Seric **
88568559Seric **	Returns:
88668559Seric **		none.
88768559Seric */
88868559Seric 
88968559Seric mail_esmtp_args(kp, vp, e)
89068559Seric 	char *kp;
89168559Seric 	char *vp;
89268559Seric 	ENVELOPE *e;
89368559Seric {
89468559Seric 	if (strcasecmp(kp, "size") == 0)
89568559Seric 	{
89668559Seric 		if (vp == NULL)
89768559Seric 		{
89868559Seric 			usrerr("501 SIZE requires a value");
89968559Seric 			/* NOTREACHED */
90068559Seric 		}
90168559Seric # ifdef __STDC__
90268560Seric 		e->e_msgsize = strtoul(vp, (char **) NULL, 10);
90368559Seric # else
90468560Seric 		e->e_msgsize = strtol(vp, (char **) NULL, 10);
90568559Seric # endif
90668559Seric 	}
90768559Seric 	else if (strcasecmp(kp, "body") == 0)
90868559Seric 	{
90968559Seric 		if (vp == NULL)
91068559Seric 		{
91168559Seric 			usrerr("501 BODY requires a value");
91268559Seric 			/* NOTREACHED */
91368559Seric 		}
91468559Seric 		if (strcasecmp(vp, "8bitmime") == 0)
91568559Seric 		{
91668559Seric 			SevenBitInput = FALSE;
91768559Seric 		}
91868559Seric 		else if (strcasecmp(vp, "7bit") == 0)
91968559Seric 		{
92068559Seric 			SevenBitInput = TRUE;
92168559Seric 		}
92268559Seric 		else
92368559Seric 		{
92468559Seric 			usrerr("501 Unknown BODY type %s",
92568559Seric 				vp);
92668559Seric 			/* NOTREACHED */
92768559Seric 		}
92868559Seric 		e->e_bodytype = newstr(vp);
92968559Seric 	}
93068559Seric 	else if (strcasecmp(kp, "envid") == 0)
93168559Seric 	{
93268559Seric 		if (vp == NULL)
93368559Seric 		{
93468559Seric 			usrerr("501 ENVID requires a value");
93568559Seric 			/* NOTREACHED */
93668559Seric 		}
93768583Seric 		if (!xtextok(vp))
93868583Seric 		{
93968583Seric 			usrerr("501 Syntax error in ENVID parameter value");
94068583Seric 			/* NOTREACHED */
94168583Seric 		}
94268583Seric 		if (e->e_envid != NULL)
94368583Seric 		{
94468583Seric 			usrerr("501 Duplicate ENVID parameter");
94568583Seric 			/* NOTREACHED */
94668583Seric 		}
94768559Seric 		e->e_envid = newstr(vp);
94868559Seric 	}
94968559Seric 	else if (strcasecmp(kp, "ret") == 0)
95068559Seric 	{
95168559Seric 		if (vp == NULL)
95268559Seric 		{
95368559Seric 			usrerr("501 RET requires a value");
95468559Seric 			/* NOTREACHED */
95568559Seric 		}
95668583Seric 		if (bitset(EF_RET_PARAM, e->e_flags))
95768583Seric 		{
95868583Seric 			usrerr("501 Duplicate RET parameter");
95968583Seric 			/* NOTREACHED */
96068583Seric 		}
96168559Seric 		e->e_flags |= EF_RET_PARAM;
96268559Seric 		if (strcasecmp(vp, "hdrs") == 0)
96368559Seric 			e->e_flags |= EF_NO_BODY_RETN;
96468559Seric 		else if (strcasecmp(vp, "full") != 0)
96568559Seric 		{
96668559Seric 			usrerr("501 Bad argument \"%s\" to RET", vp);
96768559Seric 			/* NOTREACHED */
96868559Seric 		}
96968559Seric 	}
97068559Seric 	else
97168559Seric 	{
97268559Seric 		usrerr("501 %s parameter unrecognized", kp);
97368559Seric 		/* NOTREACHED */
97468559Seric 	}
97568559Seric }
97668559Seric /*
97767963Seric **  RCPT_ESMTP_ARGS -- process ESMTP arguments from RCPT line
97867963Seric **
97967963Seric **	Parameters:
98067963Seric **		a -- the address corresponding to the To: parameter.
98167963Seric **		kp -- the parameter key.
98267963Seric **		vp -- the value of that parameter.
98367963Seric **		e -- the envelope.
98467963Seric **
98567963Seric **	Returns:
98667963Seric **		none.
98767963Seric */
98867963Seric 
98967963Seric rcpt_esmtp_args(a, kp, vp, e)
99067963Seric 	ADDRESS *a;
99167963Seric 	char *kp;
99267963Seric 	char *vp;
99367963Seric 	ENVELOPE *e;
99467963Seric {
99567963Seric 	if (strcasecmp(kp, "notify") == 0)
99667963Seric 	{
99767963Seric 		char *p;
99867963Seric 
99967963Seric 		if (vp == NULL)
100067963Seric 		{
100167963Seric 			usrerr("501 NOTIFY requires a value");
100267963Seric 			/* NOTREACHED */
100367963Seric 		}
100467963Seric 		a->q_flags &= ~(QPINGONSUCCESS|QPINGONFAILURE|QPINGONDELAY);
100568595Seric 		a->q_flags |= QHASNOTIFY;
100667963Seric 		if (strcasecmp(vp, "never") == 0)
100767963Seric 			return;
100867963Seric 		for (p = vp; p != NULL; vp = p)
100967963Seric 		{
101067963Seric 			p = strchr(p, ',');
101167963Seric 			if (p != NULL)
101267963Seric 				*p++ = '\0';
101367963Seric 			if (strcasecmp(vp, "success") == 0)
101467963Seric 				a->q_flags |= QPINGONSUCCESS;
101567963Seric 			else if (strcasecmp(vp, "failure") == 0)
101667963Seric 				a->q_flags |= QPINGONFAILURE;
101767963Seric 			else if (strcasecmp(vp, "delay") == 0)
101867963Seric 				a->q_flags |= QPINGONDELAY;
101967963Seric 			else
102067963Seric 			{
102167963Seric 				usrerr("501 Bad argument \"%s\"  to NOTIFY",
102267963Seric 					vp);
102367963Seric 				/* NOTREACHED */
102467963Seric 			}
102567963Seric 		}
102667963Seric 	}
102767963Seric 	else if (strcasecmp(kp, "orcpt") == 0)
102867963Seric 	{
102967963Seric 		if (vp == NULL)
103067963Seric 		{
103167963Seric 			usrerr("501 ORCPT requires a value");
103267963Seric 			/* NOTREACHED */
103367963Seric 		}
103468583Seric 		if (!xtextok(vp))
103568583Seric 		{
103668583Seric 			usrerr("501 Syntax error in ORCPT parameter value");
103768583Seric 			/* NOTREACHED */
103868583Seric 		}
103968583Seric 		if (a->q_orcpt != NULL)
104068583Seric 		{
104168583Seric 			usrerr("501 Duplicate ORCPT parameter");
104268583Seric 			/* NOTREACHED */
104368583Seric 		}
104467963Seric 		a->q_orcpt = newstr(vp);
104567963Seric 	}
104667963Seric 	else
104767963Seric 	{
104867963Seric 		usrerr("501 %s parameter unrecognized", kp);
104967963Seric 		/* NOTREACHED */
105067963Seric 	}
105167963Seric }
105267963Seric /*
105358151Seric **  PRINTVRFYADDR -- print an entry in the verify queue
105458151Seric **
105558151Seric **	Parameters:
105658151Seric **		a -- the address to print
105758151Seric **		last -- set if this is the last one.
105858151Seric **
105958151Seric **	Returns:
106058151Seric **		none.
106158151Seric **
106258151Seric **	Side Effects:
106358151Seric **		Prints the appropriate 250 codes.
106458151Seric */
106558151Seric 
106658151Seric printvrfyaddr(a, last)
106758151Seric 	register ADDRESS *a;
106858151Seric 	bool last;
106958151Seric {
107058151Seric 	char fmtbuf[20];
107158151Seric 
107258151Seric 	strcpy(fmtbuf, "250");
107358151Seric 	fmtbuf[3] = last ? ' ' : '-';
107458151Seric 
107559746Seric 	if (a->q_fullname == NULL)
107659746Seric 	{
107759746Seric 		if (strchr(a->q_user, '@') == NULL)
107859746Seric 			strcpy(&fmtbuf[4], "<%s@%s>");
107959746Seric 		else
108059746Seric 			strcpy(&fmtbuf[4], "<%s>");
108159746Seric 		message(fmtbuf, a->q_user, MyHostName);
108259746Seric 	}
108358151Seric 	else
108458151Seric 	{
108559746Seric 		if (strchr(a->q_user, '@') == NULL)
108659746Seric 			strcpy(&fmtbuf[4], "%s <%s@%s>");
108759746Seric 		else
108859746Seric 			strcpy(&fmtbuf[4], "%s <%s>");
108959746Seric 		message(fmtbuf, a->q_fullname, a->q_user, MyHostName);
109058151Seric 	}
109158151Seric }
109258151Seric /*
10934577Seric **  HELP -- implement the HELP command.
10944577Seric **
10954577Seric **	Parameters:
10964577Seric **		topic -- the topic we want help for.
10974577Seric **
10984577Seric **	Returns:
10994577Seric **		none.
11004577Seric **
11014577Seric **	Side Effects:
11024577Seric **		outputs the help file to message output.
11034577Seric */
11044577Seric 
11054577Seric help(topic)
11064577Seric 	char *topic;
11074577Seric {
11084577Seric 	register FILE *hf;
11094577Seric 	int len;
1110*68751Seric 	bool noinfo;
11114577Seric 	char buf[MAXLINE];
1112*68751Seric 	extern char Version[];
11134577Seric 
1114*68751Seric 
11158269Seric 	if (HelpFile == NULL || (hf = fopen(HelpFile, "r")) == NULL)
11164577Seric 	{
11174577Seric 		/* no help */
111811931Seric 		errno = 0;
1119*68751Seric 		message("502 Sendmail %s -- HELP not implemented", Version);
11204577Seric 		return;
11214577Seric 	}
11224577Seric 
112349669Seric 	if (topic == NULL || *topic == '\0')
1124*68751Seric 	{
112549669Seric 		topic = "smtp";
1126*68751Seric 		message("214-This is Sendmail version %s", Version);
1127*68751Seric 		noinfo = FALSE;
1128*68751Seric 	}
112949669Seric 	else
1130*68751Seric 	{
113149669Seric 		makelower(topic);
1132*68751Seric 		noinfo = TRUE;
1133*68751Seric 	}
113449669Seric 
11354577Seric 	len = strlen(topic);
11364577Seric 
11374577Seric 	while (fgets(buf, sizeof buf, hf) != NULL)
11384577Seric 	{
11394577Seric 		if (strncmp(buf, topic, len) == 0)
11404577Seric 		{
11414577Seric 			register char *p;
11424577Seric 
114356795Seric 			p = strchr(buf, '\t');
11444577Seric 			if (p == NULL)
11454577Seric 				p = buf;
11464577Seric 			else
11474577Seric 				p++;
11484577Seric 			fixcrlf(p, TRUE);
114958151Seric 			message("214-%s", p);
11504577Seric 			noinfo = FALSE;
11514577Seric 		}
11524577Seric 	}
11534577Seric 
11544577Seric 	if (noinfo)
115558151Seric 		message("504 HELP topic unknown");
11564577Seric 	else
115758151Seric 		message("214 End of HELP info");
11584628Seric 	(void) fclose(hf);
11594577Seric }
11608544Seric /*
11619339Seric **  RUNINCHILD -- return twice -- once in the child, then in the parent again
11629339Seric **
11639339Seric **	Parameters:
11649339Seric **		label -- a string used in error messages
11659339Seric **
11669339Seric **	Returns:
11679339Seric **		zero in the child
11689339Seric **		one in the parent
11699339Seric **
11709339Seric **	Side Effects:
11719339Seric **		none.
11729339Seric */
11738544Seric 
117455012Seric runinchild(label, e)
11759339Seric 	char *label;
117655012Seric 	register ENVELOPE *e;
11779339Seric {
11789339Seric 	int childpid;
11799339Seric 
118016158Seric 	if (!OneXact)
11819339Seric 	{
118216158Seric 		childpid = dofork();
118316158Seric 		if (childpid < 0)
118416158Seric 		{
118516158Seric 			syserr("%s: cannot fork", label);
118616158Seric 			return (1);
118716158Seric 		}
118816158Seric 		if (childpid > 0)
118916158Seric 		{
119016158Seric 			auto int st;
11919339Seric 
119216158Seric 			/* parent -- wait for child to complete */
119361093Seric 			setproctitle("server %s child wait", CurHostName);
119416158Seric 			st = waitfor(childpid);
119516158Seric 			if (st == -1)
119616158Seric 				syserr("%s: lost child", label);
119764948Seric 			else if (!WIFEXITED(st))
119864948Seric 				syserr("%s: died on signal %d",
119964948Seric 					label, st & 0177);
12009339Seric 
120116158Seric 			/* if we exited on a QUIT command, complete the process */
120266017Seric 			if (WEXITSTATUS(st) == EX_QUIT)
120366017Seric 			{
120466017Seric 				disconnect(1, e);
120516158Seric 				finis();
120666017Seric 			}
12079339Seric 
120816158Seric 			return (1);
120916158Seric 		}
121016158Seric 		else
121116158Seric 		{
121216158Seric 			/* child */
121316158Seric 			InChild = TRUE;
121425050Seric 			QuickAbort = FALSE;
121555012Seric 			clearenvelope(e, FALSE);
121616158Seric 		}
12179339Seric 	}
121815256Seric 
121916158Seric 	/* open alias database */
122060537Seric 	initmaps(FALSE, e);
122116158Seric 
122216158Seric 	return (0);
12239339Seric }
12249339Seric 
122556795Seric # endif /* SMTP */
1226