122712Sdist /*
268839Seric  * Copyright (c) 1983, 1995 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*69716Seric static char sccsid[] = "@(#)srvrsmtp.c	8.74 (Berkeley) 05/27/95 (with SMTP)";
1433731Sbostic #else
15*69716Seric static char sccsid[] = "@(#)srvrsmtp.c	8.74 (Berkeley) 05/27/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];
12024943Seric 	extern ENVELOPE BlankEnvelope;
1214549Seric 
12259066Seric 	if (fileno(OutChannel) != fileno(stdout))
1237363Seric 	{
1247363Seric 		/* arrange for debugging output to go to remote host */
12559066Seric 		(void) dup2(fileno(OutChannel), fileno(stdout));
1267363Seric 	}
12755012Seric 	settime(e);
12866005Seric 	peerhostname = RealHostName;
12966005Seric 	if (peerhostname == NULL)
13066005Seric 		peerhostname = "localhost";
13166005Seric 	CurHostName = peerhostname;
13265017Seric 	CurSmtpClient = macvalue('_', e);
13365017Seric 	if (CurSmtpClient == NULL)
13466003Seric 		CurSmtpClient = CurHostName;
13565017Seric 
13665017Seric 	setproctitle("server %s startup", CurSmtpClient);
13768529Seric 	expand("\201e", inp, sizeof inp, e);
13868769Seric 
13968769Seric 	/* output the first line, inserting "ESMTP" as second word */
14068769Seric 	p = strchr(inp, '\n');
14168769Seric 	if (p != NULL)
14268769Seric 		*p++ = '\0';
14368769Seric 	id = strchr(inp, ' ');
14468769Seric 	if (id == NULL)
14568769Seric 		id = &inp[strlen(inp)];
14668769Seric 	cmd = p == NULL ? "220 %.*s ESMTP%s" : "220-%.*s ESMTP%s";
14768769Seric 	message(cmd, id - inp, inp, id);
14868769Seric 
14968769Seric 	/* output remaining lines */
15068769Seric 	while ((id = p) != NULL && (p = strchr(id, '\n')) != NULL)
15164496Seric 	{
15268769Seric 		*p++ = '\0';
15369106Seric 		if (isascii(*id) && isspace(*id))
15469106Seric 			id++;
15568769Seric 		message("220-%s", id);
15664496Seric 	}
15768769Seric 	if (id != NULL)
15869106Seric 	{
15969106Seric 		if (isascii(*id) && isspace(*id))
16069106Seric 			id++;
16168769Seric 		message("220 %s", id);
16269106Seric 	}
16366745Seric 
16458330Seric 	protocol = NULL;
16559016Seric 	sendinghost = macvalue('s', e);
16658082Seric 	gothello = FALSE;
16758330Seric 	gotmail = FALSE;
1684549Seric 	for (;;)
1694549Seric 	{
17012612Seric 		/* arrange for backout */
17165751Seric 		if (setjmp(TopFrame) > 0)
17259058Seric 		{
17365751Seric 			/* if() nesting is necessary for Cray UNICOS */
17465751Seric 			if (InChild)
17565751Seric 			{
17665751Seric 				QuickAbort = FALSE;
17765751Seric 				SuprErrs = TRUE;
17865751Seric 				finis();
17965751Seric 			}
18059058Seric 		}
18112612Seric 		QuickAbort = FALSE;
18212612Seric 		HoldErrs = FALSE;
18351951Seric 		LogUsrErrs = FALSE;
18463843Seric 		e->e_flags &= ~(EF_VRFYONLY|EF_GLOBALERRS);
18512612Seric 
1867356Seric 		/* setup for the read */
18755012Seric 		e->e_to = NULL;
1884577Seric 		Errors = 0;
1897275Seric 		(void) fflush(stdout);
1907356Seric 
1917356Seric 		/* read the input line */
19261093Seric 		SmtpPhase = "server cmd read";
19361093Seric 		setproctitle("server %s cmd read", CurHostName);
19461093Seric 		p = sfgets(inp, sizeof inp, InChannel, TimeOuts.to_nextcommand,
19561093Seric 				SmtpPhase);
1967356Seric 
1977685Seric 		/* handle errors */
1987356Seric 		if (p == NULL)
1997356Seric 		{
2004549Seric 			/* end of file, just die */
20166017Seric 			disconnect(1, e);
20258151Seric 			message("421 %s Lost input channel from %s",
20365017Seric 				MyHostName, CurSmtpClient);
20455464Seric #ifdef LOG
20563843Seric 			if (LogLevel > (gotmail ? 1 : 19))
20655464Seric 				syslog(LOG_NOTICE, "lost input channel from %s",
20765017Seric 					CurSmtpClient);
20855464Seric #endif
20958069Seric 			if (InChild)
21058069Seric 				ExitStat = EX_QUIT;
2114549Seric 			finis();
2124549Seric 		}
2134549Seric 
2144549Seric 		/* clean up end of line */
2154558Seric 		fixcrlf(inp, TRUE);
2164549Seric 
2174713Seric 		/* echo command to transcript */
21855012Seric 		if (e->e_xfp != NULL)
21955012Seric 			fprintf(e->e_xfp, "<<< %s\n", inp);
2204713Seric 
22159060Seric 		if (e->e_id == NULL)
22265058Seric 			setproctitle("%s: %.80s", CurSmtpClient, inp);
22359060Seric 		else
22465058Seric 			setproctitle("%s %s: %.80s", e->e_id, CurSmtpClient, inp);
22559060Seric 
2264549Seric 		/* break off command */
22758050Seric 		for (p = inp; isascii(*p) && isspace(*p); p++)
2284549Seric 			continue;
22957232Seric 		cmd = cmdbuf;
23058050Seric 		while (*p != '\0' &&
23158050Seric 		       !(isascii(*p) && isspace(*p)) &&
23258050Seric 		       cmd < &cmdbuf[sizeof cmdbuf - 2])
23324981Seric 			*cmd++ = *p++;
23424981Seric 		*cmd = '\0';
2354549Seric 
23625691Seric 		/* throw away leading whitespace */
23758050Seric 		while (isascii(*p) && isspace(*p))
23825691Seric 			p++;
23925691Seric 
2404549Seric 		/* decode command */
2414549Seric 		for (c = CmdTab; c->cmdname != NULL; c++)
2424549Seric 		{
24333725Sbostic 			if (!strcasecmp(c->cmdname, cmdbuf))
2444549Seric 				break;
2454549Seric 		}
2464549Seric 
24751954Seric 		/* reset errors */
24851954Seric 		errno = 0;
24951954Seric 
2504549Seric 		/* process command */
2514549Seric 		switch (c->cmdcode)
2524549Seric 		{
2534976Seric 		  case CMDHELO:		/* hello -- introduce yourself */
25458323Seric 		  case CMDEHLO:		/* extended hello */
25558323Seric 			if (c->cmdcode == CMDEHLO)
25658323Seric 			{
25758323Seric 				protocol = "ESMTP";
25861093Seric 				SmtpPhase = "server EHLO";
25958323Seric 			}
26058323Seric 			else
26158323Seric 			{
26258323Seric 				protocol = "SMTP";
26361093Seric 				SmtpPhase = "server HELO";
26458323Seric 			}
26567445Seric 
26667445Seric 			/* check for valid domain name (re 1123 5.2.5) */
26767445Seric 			if (*p == '\0')
26867445Seric 			{
26967445Seric 				message("501 %s requires domain address",
27067445Seric 					cmdbuf);
27167445Seric 				break;
27267445Seric 			}
27367445Seric 			else
27467445Seric 			{
27567445Seric 				register char *q;
27667445Seric 
27767445Seric 				for (q = p; *q != '\0'; q++)
27867445Seric 				{
27967445Seric 					if (!isascii(*q))
28067445Seric 						break;
28167445Seric 					if (isalnum(*q))
28267445Seric 						continue;
28367445Seric 					if (strchr("[].-_#", *q) == NULL)
28467445Seric 						break;
28567445Seric 				}
28667445Seric 				if (*q != '\0')
28767445Seric 				{
28867445Seric 					message("501 Invalid domain name");
28967445Seric 					break;
29067445Seric 				}
29167445Seric 			}
29267445Seric 
29359016Seric 			sendinghost = newstr(p);
29460210Seric 			gothello = TRUE;
29560210Seric 			if (c->cmdcode != CMDEHLO)
29660239Seric 			{
29760239Seric 				/* print old message and be done with it */
29860239Seric 				message("250 %s Hello %s, pleased to meet you",
29965017Seric 					MyHostName, CurSmtpClient);
30060210Seric 				break;
30160239Seric 			}
30260239Seric 
30360239Seric 			/* print extended message and brag */
30460239Seric 			message("250-%s Hello %s, pleased to meet you",
30566760Seric 				MyHostName, CurSmtpClient);
30658323Seric 			if (!bitset(PRIV_NOEXPN, PrivacyFlags))
30758323Seric 				message("250-EXPN");
30869480Seric #if MIME8TO7
30967417Seric 			message("250-8BITMIME");
31069480Seric #endif
31164359Seric 			if (MaxMessageSize > 0)
31264359Seric 				message("250-SIZE %ld", MaxMessageSize);
31359271Seric 			else
31459271Seric 				message("250-SIZE");
31568848Seric #if DSN
31669545Seric 			message("250-X-DSN-03 (Draft of 12 Mar 1995)");
31768028Seric #endif
31858323Seric 			message("250 HELP");
3194976Seric 			break;
3204976Seric 
3214549Seric 		  case CMDMAIL:		/* mail -- designate sender */
32261093Seric 			SmtpPhase = "server MAIL";
32324943Seric 
3249314Seric 			/* check for validity of this command */
32558789Seric 			if (!gothello)
32658082Seric 			{
32758957Seric 				/* set sending host to our known value */
32859016Seric 				if (sendinghost == NULL)
32966005Seric 					sendinghost = peerhostname;
33058957Seric 
33158789Seric 				if (bitset(PRIV_NEEDMAILHELO, PrivacyFlags))
33258821Seric 				{
33358789Seric 					message("503 Polite people say HELO first");
33458821Seric 					break;
33558821Seric 				}
33658082Seric 			}
33758109Seric 			if (gotmail)
3384558Seric 			{
33958151Seric 				message("503 Sender already specified");
34063843Seric 				if (InChild)
34163843Seric 					finis();
3424558Seric 				break;
3434558Seric 			}
3449339Seric 			if (InChild)
3459339Seric 			{
34636230Skarels 				errno = 0;
34758151Seric 				syserr("503 Nested MAIL command: MAIL %s", p);
34858069Seric 				finis();
3499339Seric 			}
3509339Seric 
3519339Seric 			/* fork a subprocess to process this command */
35255012Seric 			if (runinchild("SMTP-MAIL", e) > 0)
3539339Seric 				break;
35463753Seric 			if (!gothello)
35563753Seric 			{
35663753Seric 				auth_warning(e,
35766005Seric 					"Host %s didn't use HELO protocol",
35866005Seric 					peerhostname);
35963753Seric 			}
36065947Seric #ifdef PICKY_HELO_CHECK
36166005Seric 			if (strcasecmp(sendinghost, peerhostname) != 0 &&
36266005Seric 			    (strcasecmp(peerhostname, "localhost") != 0 ||
36365823Seric 			     strcasecmp(sendinghost, MyHostName) != 0))
36465823Seric 			{
36565823Seric 				auth_warning(e, "Host %s claimed to be %s",
36666005Seric 					peerhostname, sendinghost);
36765823Seric 			}
36865947Seric #endif
36965823Seric 
37058323Seric 			if (protocol == NULL)
37158323Seric 				protocol = "SMTP";
37258323Seric 			define('r', protocol, e);
37359016Seric 			define('s', sendinghost, e);
37455012Seric 			initsys(e);
37559747Seric 			nrcpts = 0;
37668582Seric 			e->e_flags |= EF_LOGSENDER|EF_CLRQUEUE;
37765058Seric 			setproctitle("%s %s: %.80s", e->e_id, CurSmtpClient, inp);
3789339Seric 
3799339Seric 			/* child -- go do the processing */
3804549Seric 			p = skipword(p, "from");
3814549Seric 			if (p == NULL)
3824549Seric 				break;
38357977Seric 			if (setjmp(TopFrame) > 0)
38458147Seric 			{
38558147Seric 				/* this failed -- undo work */
38658147Seric 				if (InChild)
38759058Seric 				{
38859058Seric 					QuickAbort = FALSE;
38959058Seric 					SuprErrs = TRUE;
39063787Seric 					e->e_flags &= ~EF_FATALERRS;
39158147Seric 					finis();
39259058Seric 				}
39357977Seric 				break;
39458147Seric 			}
39557977Seric 			QuickAbort = TRUE;
39658333Seric 
39758333Seric 			/* must parse sender first */
39858333Seric 			delimptr = NULL;
39958704Seric 			setsender(p, e, &delimptr, FALSE);
40058333Seric 			p = delimptr;
40158333Seric 			if (p != NULL && *p != '\0')
40258333Seric 				*p++ = '\0';
40358333Seric 
40466325Seric 			/* check for possible spoofing */
40566325Seric 			if (RealUid != 0 && OpMode == MD_SMTP &&
40667473Seric 			    !bitnset(M_LOCALMAILER, e->e_from.q_mailer->m_flags) &&
40767473Seric 			    strcmp(e->e_from.q_user, RealUserName) != 0)
40866325Seric 			{
40966325Seric 				auth_warning(e, "%s owned process doing -bs",
41066325Seric 					RealUserName);
41166325Seric 			}
41266325Seric 
41358333Seric 			/* now parse ESMTP arguments */
41468560Seric 			e->e_msgsize = 0;
41566764Seric 			while (p != NULL && *p != '\0')
41658333Seric 			{
41758333Seric 				char *kp;
41866304Seric 				char *vp = NULL;
41958333Seric 
42058333Seric 				/* locate the beginning of the keyword */
42158333Seric 				while (isascii(*p) && isspace(*p))
42258333Seric 					p++;
42358333Seric 				if (*p == '\0')
42458333Seric 					break;
42558333Seric 				kp = p;
42658333Seric 
42758333Seric 				/* skip to the value portion */
42858333Seric 				while (isascii(*p) && isalnum(*p) || *p == '-')
42958333Seric 					p++;
43058333Seric 				if (*p == '=')
43158333Seric 				{
43258333Seric 					*p++ = '\0';
43358333Seric 					vp = p;
43458333Seric 
43558333Seric 					/* skip to the end of the value */
43658333Seric 					while (*p != '\0' && *p != ' ' &&
43758333Seric 					       !(isascii(*p) && iscntrl(*p)) &&
43858333Seric 					       *p != '=')
43958333Seric 						p++;
44058333Seric 				}
44158333Seric 
44258333Seric 				if (*p != '\0')
44358333Seric 					*p++ = '\0';
44458333Seric 
44558333Seric 				if (tTd(19, 1))
44666764Seric 					printf("MAIL: got arg %s=\"%s\"\n", kp,
44758333Seric 						vp == NULL ? "<null>" : vp);
44858333Seric 
44968559Seric 				mail_esmtp_args(kp, vp, e);
45058333Seric 			}
45159284Seric 
45268560Seric 			if (MaxMessageSize > 0 && e->e_msgsize > MaxMessageSize)
45359284Seric 			{
45459284Seric 				usrerr("552 Message size exceeds fixed maximum message size (%ld)",
45559284Seric 					MaxMessageSize);
45659284Seric 				/* NOTREACHED */
45759284Seric 			}
45858333Seric 
45968560Seric 			if (!enoughspace(e->e_msgsize))
46058333Seric 			{
46158333Seric 				message("452 Insufficient disk space; try again later");
46258333Seric 				break;
46358333Seric 			}
46458151Seric 			message("250 Sender ok");
46558147Seric 			gotmail = TRUE;
4664549Seric 			break;
4674549Seric 
4684976Seric 		  case CMDRCPT:		/* rcpt -- designate recipient */
46958850Seric 			if (!gotmail)
47058850Seric 			{
47158850Seric 				usrerr("503 Need MAIL before RCPT");
47258850Seric 				break;
47358850Seric 			}
47461093Seric 			SmtpPhase = "server RCPT";
47512612Seric 			if (setjmp(TopFrame) > 0)
47614785Seric 			{
47755012Seric 				e->e_flags &= ~EF_FATALERRS;
47812612Seric 				break;
47914785Seric 			}
48012612Seric 			QuickAbort = TRUE;
48151951Seric 			LogUsrErrs = TRUE;
48258093Seric 
48359699Seric 			if (e->e_sendmode != SM_DELIVER)
48459699Seric 				e->e_flags |= EF_VRFYONLY;
48558919Seric 
4864549Seric 			p = skipword(p, "to");
4874549Seric 			if (p == NULL)
4884549Seric 				break;
48967880Seric 			a = parseaddr(p, NULLADDR, RF_COPYALL, ' ', &delimptr, e);
49012612Seric 			if (a == NULL)
49112612Seric 				break;
49267880Seric 			p = delimptr;
49367880Seric 
49467880Seric 			/* now parse ESMTP arguments */
49567880Seric 			while (p != NULL && *p != '\0')
49667880Seric 			{
49767880Seric 				char *kp;
49867880Seric 				char *vp = NULL;
49967880Seric 
50067880Seric 				/* locate the beginning of the keyword */
50167880Seric 				while (isascii(*p) && isspace(*p))
50267880Seric 					p++;
50367880Seric 				if (*p == '\0')
50467880Seric 					break;
50567880Seric 				kp = p;
50667880Seric 
50767880Seric 				/* skip to the value portion */
50867880Seric 				while (isascii(*p) && isalnum(*p) || *p == '-')
50967880Seric 					p++;
51067880Seric 				if (*p == '=')
51167880Seric 				{
51267880Seric 					*p++ = '\0';
51367880Seric 					vp = p;
51467880Seric 
51567880Seric 					/* skip to the end of the value */
51667880Seric 					while (*p != '\0' && *p != ' ' &&
51767880Seric 					       !(isascii(*p) && iscntrl(*p)) &&
51867880Seric 					       *p != '=')
51967880Seric 						p++;
52067880Seric 				}
52167880Seric 
52267880Seric 				if (*p != '\0')
52367880Seric 					*p++ = '\0';
52467880Seric 
52567880Seric 				if (tTd(19, 1))
52667880Seric 					printf("RCPT: got arg %s=\"%s\"\n", kp,
52767880Seric 						vp == NULL ? "<null>" : vp);
52867880Seric 
52967963Seric 				rcpt_esmtp_args(a, kp, vp, e);
53067963Seric 
53167880Seric 			}
53267980Seric 
53367980Seric 			/* save in recipient list after ESMTP mods */
53467982Seric 			a = recipient(a, &e->e_sendqueue, 0, e);
53567980Seric 
53612612Seric 			if (Errors != 0)
53712612Seric 				break;
53812612Seric 
53912612Seric 			/* no errors during parsing, but might be a duplicate */
54055012Seric 			e->e_to = p;
54112612Seric 			if (!bitset(QBADADDR, a->q_flags))
54259747Seric 			{
54364718Seric 				message("250 Recipient ok%s",
54464718Seric 					bitset(QQUEUEUP, a->q_flags) ?
54564718Seric 						" (will queue)" : "");
54659747Seric 				nrcpts++;
54759747Seric 			}
54812612Seric 			else
5494549Seric 			{
55012612Seric 				/* punt -- should keep message in ADDRESS.... */
55158151Seric 				message("550 Addressee unknown");
5524549Seric 			}
55355012Seric 			e->e_to = NULL;
5544549Seric 			break;
5554549Seric 
5564549Seric 		  case CMDDATA:		/* data -- text of mail */
55761093Seric 			SmtpPhase = "server DATA";
55858109Seric 			if (!gotmail)
5594549Seric 			{
56058151Seric 				message("503 Need MAIL command");
5614976Seric 				break;
5624549Seric 			}
56364718Seric 			else if (nrcpts <= 0)
5644549Seric 			{
56558151Seric 				message("503 Need RCPT (recipient)");
5664976Seric 				break;
5674549Seric 			}
5684976Seric 
56958929Seric 			/* check to see if we need to re-expand aliases */
57063787Seric 			/* also reset QBADADDR on already-diagnosted addrs */
57163787Seric 			doublequeue = FALSE;
57258929Seric 			for (a = e->e_sendqueue; a != NULL; a = a->q_next)
57358929Seric 			{
57458929Seric 				if (bitset(QVERIFIED, a->q_flags))
57563787Seric 				{
57663787Seric 					/* need to re-expand aliases */
57763787Seric 					doublequeue = TRUE;
57863787Seric 				}
57963787Seric 				if (bitset(QBADADDR, a->q_flags))
58063787Seric 				{
58163787Seric 					/* make this "go away" */
58263787Seric 					a->q_flags |= QDONTSEND;
58363787Seric 					a->q_flags &= ~QBADADDR;
58463787Seric 				}
58558929Seric 			}
58658929Seric 
5874976Seric 			/* collect the text of the message */
58824943Seric 			SmtpPhase = "collect";
58968692Seric 			buffer_errors();
59067546Seric 			collect(InChannel, TRUE, doublequeue, NULL, e);
59168692Seric 			flush_errors(TRUE);
59264766Seric 			if (Errors != 0)
59364766Seric 				goto abortmessage;
59467131Seric 
59568582Seric 			/* make sure we actually do delivery */
59668582Seric 			e->e_flags &= ~EF_CLRQUEUE;
59768582Seric 
59867131Seric 			/* from now on, we have to operate silently */
59968692Seric 			buffer_errors();
60067131Seric 			e->e_errormode = EM_MAIL;
6014976Seric 
6028238Seric 			/*
6038238Seric 			**  Arrange to send to everyone.
6048238Seric 			**	If sending to multiple people, mail back
6058238Seric 			**		errors rather than reporting directly.
6068238Seric 			**	In any case, don't mail back errors for
6078238Seric 			**		anything that has happened up to
6088238Seric 			**		now (the other end will do this).
60910197Seric 			**	Truncate our transcript -- the mail has gotten
61010197Seric 			**		to us successfully, and if we have
61110197Seric 			**		to mail this back, it will be easier
61210197Seric 			**		on the reader.
6138238Seric 			**	Then send to everyone.
6148238Seric 			**	Finally give a reply code.  If an error has
6158238Seric 			**		already been given, don't mail a
6168238Seric 			**		message back.
6179339Seric 			**	We goose error returns by clearing error bit.
6188238Seric 			*/
6198238Seric 
62024943Seric 			SmtpPhase = "delivery";
62155012Seric 			e->e_xfp = freopen(queuename(e, 'x'), "w", e->e_xfp);
62258714Seric 			id = e->e_id;
6234976Seric 
62467131Seric 			if (doublequeue)
62559730Seric 			{
62667131Seric 				/* make sure it is in the queue */
62767131Seric 				queueup(e, TRUE, FALSE);
62868035Seric 				if (e->e_sendmode == SM_QUEUE)
62968035Seric 					e->e_flags |= EF_KEEPQUEUE;
63059730Seric 			}
6318238Seric 			else
63258919Seric 			{
63367131Seric 				/* send to all recipients */
63467131Seric 				sendall(e, SM_DEFAULT);
63567131Seric 			}
63667131Seric 			e->e_to = NULL;
63759747Seric 
63867131Seric 			/* issue success message */
63967131Seric 			message("250 %s Message accepted for delivery", id);
64064296Seric 
64167131Seric 			/* if we just queued, poke it */
64267131Seric 			if (doublequeue && e->e_sendmode != SM_QUEUE)
64367131Seric 			{
64467131Seric 				extern pid_t dowork();
64567131Seric 
64667131Seric 				unlockqueue(e);
64767131Seric 				(void) dowork(id, TRUE, TRUE, e);
64858919Seric 			}
64958883Seric 
65059747Seric   abortmessage:
6519339Seric 			/* if in a child, pop back to our parent */
6529339Seric 			if (InChild)
6539339Seric 				finis();
65424943Seric 
65524943Seric 			/* clean up a bit */
65658109Seric 			gotmail = FALSE;
65755012Seric 			dropenvelope(e);
65858179Seric 			CurEnv = e = newenvelope(e, CurEnv);
65955012Seric 			e->e_flags = BlankEnvelope.e_flags;
6604549Seric 			break;
6614549Seric 
6624549Seric 		  case CMDRSET:		/* rset -- reset state */
66358151Seric 			message("250 Reset state");
66468603Seric 
66568603Seric 			/* arrange to ignore any current send list */
66668603Seric 			e->e_sendqueue = NULL;
66764359Seric 			e->e_flags |= EF_CLRQUEUE;
6689339Seric 			if (InChild)
6699339Seric 				finis();
67058109Seric 
67158109Seric 			/* clean up a bit */
67258109Seric 			gotmail = FALSE;
67358109Seric 			dropenvelope(e);
67458179Seric 			CurEnv = e = newenvelope(e, CurEnv);
6759339Seric 			break;
6764549Seric 
6774549Seric 		  case CMDVRFY:		/* vrfy -- verify address */
67858092Seric 		  case CMDEXPN:		/* expn -- expand address */
67958092Seric 			vrfy = c->cmdcode == CMDVRFY;
68058092Seric 			if (bitset(vrfy ? PRIV_NOVRFY : PRIV_NOEXPN,
68158092Seric 						PrivacyFlags))
68258082Seric 			{
68358412Seric 				if (vrfy)
68467160Seric 					message("252 Cannot VRFY user; try RCPT to attempt delivery (or try finger)");
68558412Seric 				else
68665192Seric 					message("502 Sorry, we do not allow this operation");
68765017Seric #ifdef LOG
68865017Seric 				if (LogLevel > 5)
68965017Seric 					syslog(LOG_INFO, "%s: %s [rejected]",
69065017Seric 						CurSmtpClient, inp);
69165017Seric #endif
69258082Seric 				break;
69358082Seric 			}
69458082Seric 			else if (!gothello &&
69558092Seric 				 bitset(vrfy ? PRIV_NEEDVRFYHELO : PRIV_NEEDEXPNHELO,
69658092Seric 						PrivacyFlags))
69758082Seric 			{
69858151Seric 				message("503 I demand that you introduce yourself first");
69958082Seric 				break;
70058082Seric 			}
70158092Seric 			if (runinchild(vrfy ? "SMTP-VRFY" : "SMTP-EXPN", e) > 0)
7029339Seric 				break;
70355173Seric #ifdef LOG
70458020Seric 			if (LogLevel > 5)
70565017Seric 				syslog(LOG_INFO, "%s: %s", CurSmtpClient, inp);
70655173Seric #endif
7075003Seric 			vrfyqueue = NULL;
7087762Seric 			QuickAbort = TRUE;
70958092Seric 			if (vrfy)
71058092Seric 				e->e_flags |= EF_VRFYONLY;
71162373Seric 			while (*p != '\0' && isascii(*p) && isspace(*p))
71267615Seric 				p++;
71362373Seric 			if (*p == '\0')
71462373Seric 			{
71562373Seric 				message("501 Argument required");
71662373Seric 				Errors++;
71762373Seric 			}
71862373Seric 			else
71962373Seric 			{
72067990Seric 				(void) sendtolist(p, NULLADDR, &vrfyqueue, 0, e);
72162373Seric 			}
7227762Seric 			if (Errors != 0)
7239339Seric 			{
7249339Seric 				if (InChild)
7259339Seric 					finis();
7267762Seric 				break;
7279339Seric 			}
72862373Seric 			if (vrfyqueue == NULL)
72962373Seric 			{
73062373Seric 				message("554 Nothing to %s", vrfy ? "VRFY" : "EXPN");
73162373Seric 			}
7325003Seric 			while (vrfyqueue != NULL)
7335003Seric 			{
73463971Seric 				a = vrfyqueue;
73563971Seric 				while ((a = a->q_next) != NULL &&
73663971Seric 				       bitset(QDONTSEND|QBADADDR, a->q_flags))
73763971Seric 					continue;
7387685Seric 				if (!bitset(QDONTSEND|QBADADDR, vrfyqueue->q_flags))
73958151Seric 					printvrfyaddr(vrfyqueue, a == NULL);
74063847Seric 				vrfyqueue = vrfyqueue->q_next;
7415003Seric 			}
7429339Seric 			if (InChild)
7439339Seric 				finis();
7444549Seric 			break;
7454549Seric 
7464549Seric 		  case CMDHELP:		/* help -- give user info */
7474577Seric 			help(p);
7484549Seric 			break;
7494549Seric 
7504549Seric 		  case CMDNOOP:		/* noop -- do nothing */
75164122Seric 			message("250 OK");
7524549Seric 			break;
7534549Seric 
7544549Seric 		  case CMDQUIT:		/* quit -- leave mail */
75558151Seric 			message("221 %s closing connection", MyHostName);
75661051Seric 
75766283Seric doquit:
75868603Seric 			/* arrange to ignore any current send list */
75968603Seric 			e->e_sendqueue = NULL;
76068603Seric 
76161051Seric 			/* avoid future 050 messages */
76266017Seric 			disconnect(1, e);
76361051Seric 
7649339Seric 			if (InChild)
7659339Seric 				ExitStat = EX_QUIT;
7664549Seric 			finis();
7674549Seric 
7688544Seric 		  case CMDVERB:		/* set verbose mode */
76959957Seric 			if (bitset(PRIV_NOEXPN, PrivacyFlags))
77059957Seric 			{
77159957Seric 				/* this would give out the same info */
77259957Seric 				message("502 Verbose unavailable");
77359957Seric 				break;
77459957Seric 			}
7758544Seric 			Verbose = TRUE;
77658734Seric 			e->e_sendmode = SM_DELIVER;
77759957Seric 			message("250 Verbose mode");
7788544Seric 			break;
7798544Seric 
7809314Seric 		  case CMDONEX:		/* doing one transaction only */
7819378Seric 			OneXact = TRUE;
78259957Seric 			message("250 Only one transaction");
7839314Seric 			break;
7849314Seric 
78536230Skarels # ifdef SMTPDEBUG
7869339Seric 		  case CMDDBGQSHOW:	/* show queues */
7876907Seric 			printf("Send Queue=");
78855012Seric 			printaddr(e->e_sendqueue, TRUE);
7895003Seric 			break;
7907275Seric 
7917275Seric 		  case CMDDBGDEBUG:	/* set debug mode */
7927676Seric 			tTsetup(tTdvect, sizeof tTdvect, "0-99.1");
7937676Seric 			tTflag(p);
79458151Seric 			message("200 Debug set");
7957275Seric 			break;
7967275Seric 
79736230Skarels # else /* not SMTPDEBUG */
79836230Skarels 		  case CMDDBGQSHOW:	/* show queues */
79936230Skarels 		  case CMDDBGDEBUG:	/* set debug mode */
80064685Seric # endif /* SMTPDEBUG */
80164685Seric 		  case CMDLOGBOGUS:	/* bogus command */
80236233Skarels # ifdef LOG
80358308Seric 			if (LogLevel > 0)
80464685Seric 				syslog(LOG_CRIT,
80558020Seric 				    "\"%s\" command from %s (%s)",
80666005Seric 				    c->cmdname, peerhostname,
80758755Seric 				    anynet_ntoa(&RealHostAddr));
80836233Skarels # endif
80936230Skarels 			/* FALL THROUGH */
81036230Skarels 
8114549Seric 		  case CMDERROR:	/* unknown command */
81266283Seric 			if (++badcommands > MAXBADCOMMANDS)
81366283Seric 			{
81466283Seric 				message("421 %s Too many bad commands; closing connection",
81566283Seric 					MyHostName);
81666283Seric 				goto doquit;
81766283Seric 			}
81866283Seric 
81958151Seric 			message("500 Command unrecognized");
8204549Seric 			break;
8214549Seric 
8224549Seric 		  default:
82336230Skarels 			errno = 0;
82458151Seric 			syserr("500 smtp: unknown code %d", c->cmdcode);
8254549Seric 			break;
8264549Seric 		}
8274549Seric 	}
8284549Seric }
8294549Seric /*
8304549Seric **  SKIPWORD -- skip a fixed word.
8314549Seric **
8324549Seric **	Parameters:
8334549Seric **		p -- place to start looking.
8344549Seric **		w -- word to skip.
8354549Seric **
8364549Seric **	Returns:
8374549Seric **		p following w.
8384549Seric **		NULL on error.
8394549Seric **
8404549Seric **	Side Effects:
8414549Seric **		clobbers the p data area.
8424549Seric */
8434549Seric 
8444549Seric static char *
8454549Seric skipword(p, w)
8464549Seric 	register char *p;
8474549Seric 	char *w;
8484549Seric {
8494549Seric 	register char *q;
85066005Seric 	char *firstp = p;
8514549Seric 
8524549Seric 	/* find beginning of word */
85358050Seric 	while (isascii(*p) && isspace(*p))
8544549Seric 		p++;
8554549Seric 	q = p;
8564549Seric 
8574549Seric 	/* find end of word */
85858050Seric 	while (*p != '\0' && *p != ':' && !(isascii(*p) && isspace(*p)))
8594549Seric 		p++;
86058050Seric 	while (isascii(*p) && isspace(*p))
8614549Seric 		*p++ = '\0';
8624549Seric 	if (*p != ':')
8634549Seric 	{
8644549Seric 	  syntax:
86566005Seric 		message("501 Syntax error in parameters scanning \"%s\"",
86666005Seric 			firstp);
8674549Seric 		Errors++;
8684549Seric 		return (NULL);
8694549Seric 	}
8704549Seric 	*p++ = '\0';
87158050Seric 	while (isascii(*p) && isspace(*p))
8724549Seric 		p++;
8734549Seric 
87462373Seric 	if (*p == '\0')
87562373Seric 		goto syntax;
87662373Seric 
8774549Seric 	/* see if the input word matches desired word */
87833725Sbostic 	if (strcasecmp(q, w))
8794549Seric 		goto syntax;
8804549Seric 
8814549Seric 	return (p);
8824549Seric }
8834577Seric /*
88468559Seric **  MAIL_ESMTP_ARGS -- process ESMTP arguments from MAIL line
88568559Seric **
88668559Seric **	Parameters:
88768559Seric **		kp -- the parameter key.
88868559Seric **		vp -- the value of that parameter.
88968559Seric **		e -- the envelope.
89068559Seric **
89168559Seric **	Returns:
89268559Seric **		none.
89368559Seric */
89468559Seric 
89568559Seric mail_esmtp_args(kp, vp, e)
89668559Seric 	char *kp;
89768559Seric 	char *vp;
89868559Seric 	ENVELOPE *e;
89968559Seric {
90068559Seric 	if (strcasecmp(kp, "size") == 0)
90168559Seric 	{
90268559Seric 		if (vp == NULL)
90368559Seric 		{
90468559Seric 			usrerr("501 SIZE requires a value");
90568559Seric 			/* NOTREACHED */
90668559Seric 		}
90768890Seric # if defined(__STDC__) && !defined(BROKEN_ANSI_LIBRARY)
90868560Seric 		e->e_msgsize = strtoul(vp, (char **) NULL, 10);
90968559Seric # else
91068560Seric 		e->e_msgsize = strtol(vp, (char **) NULL, 10);
91168559Seric # endif
91268559Seric 	}
91368559Seric 	else if (strcasecmp(kp, "body") == 0)
91468559Seric 	{
91568559Seric 		if (vp == NULL)
91668559Seric 		{
91768559Seric 			usrerr("501 BODY requires a value");
91868559Seric 			/* NOTREACHED */
91968559Seric 		}
92068559Seric 		if (strcasecmp(vp, "8bitmime") == 0)
92168559Seric 		{
92268559Seric 			SevenBitInput = FALSE;
92368883Seric 			e->e_flags |= EF_NL_NOT_EOL;
92468559Seric 		}
92568559Seric 		else if (strcasecmp(vp, "7bit") == 0)
92668559Seric 		{
92768559Seric 			SevenBitInput = TRUE;
92868559Seric 		}
92968559Seric 		else
93068559Seric 		{
93168559Seric 			usrerr("501 Unknown BODY type %s",
93268559Seric 				vp);
93368559Seric 			/* NOTREACHED */
93468559Seric 		}
93568559Seric 		e->e_bodytype = newstr(vp);
93668559Seric 	}
93768559Seric 	else if (strcasecmp(kp, "envid") == 0)
93868559Seric 	{
93968559Seric 		if (vp == NULL)
94068559Seric 		{
94168559Seric 			usrerr("501 ENVID requires a value");
94268559Seric 			/* NOTREACHED */
94368559Seric 		}
94468583Seric 		if (!xtextok(vp))
94568583Seric 		{
94668583Seric 			usrerr("501 Syntax error in ENVID parameter value");
94768583Seric 			/* NOTREACHED */
94868583Seric 		}
94968583Seric 		if (e->e_envid != NULL)
95068583Seric 		{
95168583Seric 			usrerr("501 Duplicate ENVID parameter");
95268583Seric 			/* NOTREACHED */
95368583Seric 		}
95468559Seric 		e->e_envid = newstr(vp);
95568559Seric 	}
95668559Seric 	else if (strcasecmp(kp, "ret") == 0)
95768559Seric 	{
95868559Seric 		if (vp == NULL)
95968559Seric 		{
96068559Seric 			usrerr("501 RET requires a value");
96168559Seric 			/* NOTREACHED */
96268559Seric 		}
96368583Seric 		if (bitset(EF_RET_PARAM, e->e_flags))
96468583Seric 		{
96568583Seric 			usrerr("501 Duplicate RET parameter");
96668583Seric 			/* NOTREACHED */
96768583Seric 		}
96868559Seric 		e->e_flags |= EF_RET_PARAM;
96968559Seric 		if (strcasecmp(vp, "hdrs") == 0)
97068559Seric 			e->e_flags |= EF_NO_BODY_RETN;
97168559Seric 		else if (strcasecmp(vp, "full") != 0)
97268559Seric 		{
97368559Seric 			usrerr("501 Bad argument \"%s\" to RET", vp);
97468559Seric 			/* NOTREACHED */
97568559Seric 		}
97668559Seric 	}
97768559Seric 	else
97868559Seric 	{
97968559Seric 		usrerr("501 %s parameter unrecognized", kp);
98068559Seric 		/* NOTREACHED */
98168559Seric 	}
98268559Seric }
98368559Seric /*
98467963Seric **  RCPT_ESMTP_ARGS -- process ESMTP arguments from RCPT line
98567963Seric **
98667963Seric **	Parameters:
98767963Seric **		a -- the address corresponding to the To: parameter.
98867963Seric **		kp -- the parameter key.
98967963Seric **		vp -- the value of that parameter.
99067963Seric **		e -- the envelope.
99167963Seric **
99267963Seric **	Returns:
99367963Seric **		none.
99467963Seric */
99567963Seric 
99667963Seric rcpt_esmtp_args(a, kp, vp, e)
99767963Seric 	ADDRESS *a;
99867963Seric 	char *kp;
99967963Seric 	char *vp;
100067963Seric 	ENVELOPE *e;
100167963Seric {
100267963Seric 	if (strcasecmp(kp, "notify") == 0)
100367963Seric 	{
100467963Seric 		char *p;
100567963Seric 
100667963Seric 		if (vp == NULL)
100767963Seric 		{
100867963Seric 			usrerr("501 NOTIFY requires a value");
100967963Seric 			/* NOTREACHED */
101067963Seric 		}
101167963Seric 		a->q_flags &= ~(QPINGONSUCCESS|QPINGONFAILURE|QPINGONDELAY);
101268595Seric 		a->q_flags |= QHASNOTIFY;
101367963Seric 		if (strcasecmp(vp, "never") == 0)
101467963Seric 			return;
101567963Seric 		for (p = vp; p != NULL; vp = p)
101667963Seric 		{
101767963Seric 			p = strchr(p, ',');
101867963Seric 			if (p != NULL)
101967963Seric 				*p++ = '\0';
102067963Seric 			if (strcasecmp(vp, "success") == 0)
102167963Seric 				a->q_flags |= QPINGONSUCCESS;
102267963Seric 			else if (strcasecmp(vp, "failure") == 0)
102367963Seric 				a->q_flags |= QPINGONFAILURE;
102467963Seric 			else if (strcasecmp(vp, "delay") == 0)
102567963Seric 				a->q_flags |= QPINGONDELAY;
102667963Seric 			else
102767963Seric 			{
102867963Seric 				usrerr("501 Bad argument \"%s\"  to NOTIFY",
102967963Seric 					vp);
103067963Seric 				/* NOTREACHED */
103167963Seric 			}
103267963Seric 		}
103367963Seric 	}
103467963Seric 	else if (strcasecmp(kp, "orcpt") == 0)
103567963Seric 	{
103667963Seric 		if (vp == NULL)
103767963Seric 		{
103867963Seric 			usrerr("501 ORCPT requires a value");
103967963Seric 			/* NOTREACHED */
104067963Seric 		}
104168583Seric 		if (!xtextok(vp))
104268583Seric 		{
104368583Seric 			usrerr("501 Syntax error in ORCPT parameter value");
104468583Seric 			/* NOTREACHED */
104568583Seric 		}
104668583Seric 		if (a->q_orcpt != NULL)
104768583Seric 		{
104868583Seric 			usrerr("501 Duplicate ORCPT parameter");
104968583Seric 			/* NOTREACHED */
105068583Seric 		}
105167963Seric 		a->q_orcpt = newstr(vp);
105267963Seric 	}
105367963Seric 	else
105467963Seric 	{
105567963Seric 		usrerr("501 %s parameter unrecognized", kp);
105667963Seric 		/* NOTREACHED */
105767963Seric 	}
105867963Seric }
105967963Seric /*
106058151Seric **  PRINTVRFYADDR -- print an entry in the verify queue
106158151Seric **
106258151Seric **	Parameters:
106358151Seric **		a -- the address to print
106458151Seric **		last -- set if this is the last one.
106558151Seric **
106658151Seric **	Returns:
106758151Seric **		none.
106858151Seric **
106958151Seric **	Side Effects:
107058151Seric **		Prints the appropriate 250 codes.
107158151Seric */
107258151Seric 
107358151Seric printvrfyaddr(a, last)
107458151Seric 	register ADDRESS *a;
107558151Seric 	bool last;
107658151Seric {
107758151Seric 	char fmtbuf[20];
107858151Seric 
107958151Seric 	strcpy(fmtbuf, "250");
108058151Seric 	fmtbuf[3] = last ? ' ' : '-';
108158151Seric 
108259746Seric 	if (a->q_fullname == NULL)
108359746Seric 	{
108459746Seric 		if (strchr(a->q_user, '@') == NULL)
108559746Seric 			strcpy(&fmtbuf[4], "<%s@%s>");
108659746Seric 		else
108759746Seric 			strcpy(&fmtbuf[4], "<%s>");
108859746Seric 		message(fmtbuf, a->q_user, MyHostName);
108959746Seric 	}
109058151Seric 	else
109158151Seric 	{
109259746Seric 		if (strchr(a->q_user, '@') == NULL)
109359746Seric 			strcpy(&fmtbuf[4], "%s <%s@%s>");
109459746Seric 		else
109559746Seric 			strcpy(&fmtbuf[4], "%s <%s>");
109659746Seric 		message(fmtbuf, a->q_fullname, a->q_user, MyHostName);
109758151Seric 	}
109858151Seric }
109958151Seric /*
11004577Seric **  HELP -- implement the HELP command.
11014577Seric **
11024577Seric **	Parameters:
11034577Seric **		topic -- the topic we want help for.
11044577Seric **
11054577Seric **	Returns:
11064577Seric **		none.
11074577Seric **
11084577Seric **	Side Effects:
11094577Seric **		outputs the help file to message output.
11104577Seric */
11114577Seric 
11124577Seric help(topic)
11134577Seric 	char *topic;
11144577Seric {
11154577Seric 	register FILE *hf;
11164577Seric 	int len;
111768751Seric 	bool noinfo;
11184577Seric 	char buf[MAXLINE];
111968751Seric 	extern char Version[];
11204577Seric 
112168751Seric 
11228269Seric 	if (HelpFile == NULL || (hf = fopen(HelpFile, "r")) == NULL)
11234577Seric 	{
11244577Seric 		/* no help */
112511931Seric 		errno = 0;
112668751Seric 		message("502 Sendmail %s -- HELP not implemented", Version);
11274577Seric 		return;
11284577Seric 	}
11294577Seric 
113049669Seric 	if (topic == NULL || *topic == '\0')
113168751Seric 	{
113249669Seric 		topic = "smtp";
113368751Seric 		message("214-This is Sendmail version %s", Version);
113468751Seric 		noinfo = FALSE;
113568751Seric 	}
113649669Seric 	else
113768751Seric 	{
113849669Seric 		makelower(topic);
113968751Seric 		noinfo = TRUE;
114068751Seric 	}
114149669Seric 
11424577Seric 	len = strlen(topic);
11434577Seric 
11444577Seric 	while (fgets(buf, sizeof buf, hf) != NULL)
11454577Seric 	{
11464577Seric 		if (strncmp(buf, topic, len) == 0)
11474577Seric 		{
11484577Seric 			register char *p;
11494577Seric 
115056795Seric 			p = strchr(buf, '\t');
11514577Seric 			if (p == NULL)
11524577Seric 				p = buf;
11534577Seric 			else
11544577Seric 				p++;
11554577Seric 			fixcrlf(p, TRUE);
115658151Seric 			message("214-%s", p);
11574577Seric 			noinfo = FALSE;
11584577Seric 		}
11594577Seric 	}
11604577Seric 
11614577Seric 	if (noinfo)
116258151Seric 		message("504 HELP topic unknown");
11634577Seric 	else
116458151Seric 		message("214 End of HELP info");
11654628Seric 	(void) fclose(hf);
11664577Seric }
11678544Seric /*
11689339Seric **  RUNINCHILD -- return twice -- once in the child, then in the parent again
11699339Seric **
11709339Seric **	Parameters:
11719339Seric **		label -- a string used in error messages
11729339Seric **
11739339Seric **	Returns:
11749339Seric **		zero in the child
11759339Seric **		one in the parent
11769339Seric **
11779339Seric **	Side Effects:
11789339Seric **		none.
11799339Seric */
11808544Seric 
118155012Seric runinchild(label, e)
11829339Seric 	char *label;
118355012Seric 	register ENVELOPE *e;
11849339Seric {
11859339Seric 	int childpid;
11869339Seric 
118716158Seric 	if (!OneXact)
11889339Seric 	{
118916158Seric 		childpid = dofork();
119016158Seric 		if (childpid < 0)
119116158Seric 		{
1192*69716Seric 			syserr("451 %s: cannot fork", label);
119316158Seric 			return (1);
119416158Seric 		}
119516158Seric 		if (childpid > 0)
119616158Seric 		{
119716158Seric 			auto int st;
11989339Seric 
119916158Seric 			/* parent -- wait for child to complete */
120061093Seric 			setproctitle("server %s child wait", CurHostName);
120116158Seric 			st = waitfor(childpid);
120216158Seric 			if (st == -1)
1203*69716Seric 				syserr("451 %s: lost child", label);
120464948Seric 			else if (!WIFEXITED(st))
1205*69716Seric 				syserr("451 %s: died on signal %d",
120664948Seric 					label, st & 0177);
12079339Seric 
120816158Seric 			/* if we exited on a QUIT command, complete the process */
120966017Seric 			if (WEXITSTATUS(st) == EX_QUIT)
121066017Seric 			{
121166017Seric 				disconnect(1, e);
121216158Seric 				finis();
121366017Seric 			}
12149339Seric 
121516158Seric 			return (1);
121616158Seric 		}
121716158Seric 		else
121816158Seric 		{
121916158Seric 			/* child */
122016158Seric 			InChild = TRUE;
122125050Seric 			QuickAbort = FALSE;
122255012Seric 			clearenvelope(e, FALSE);
122316158Seric 		}
12249339Seric 	}
122515256Seric 
122616158Seric 	/* open alias database */
122760537Seric 	initmaps(FALSE, e);
122816158Seric 
122916158Seric 	return (0);
12309339Seric }
12319339Seric 
123256795Seric # endif /* SMTP */
1233