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*69106Seric static char sccsid[] = "@(#)srvrsmtp.c	8.71 (Berkeley) 04/29/95 (with SMTP)";
1433731Sbostic #else
15*69106Seric static char sccsid[] = "@(#)srvrsmtp.c	8.71 (Berkeley) 04/29/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';
153*69106Seric 		if (isascii(*id) && isspace(*id))
154*69106Seric 			id++;
15568769Seric 		message("220-%s", id);
15664496Seric 	}
15768769Seric 	if (id != NULL)
158*69106Seric 	{
159*69106Seric 		if (isascii(*id) && isspace(*id))
160*69106Seric 			id++;
16168769Seric 		message("220 %s", id);
162*69106Seric 	}
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");
30867417Seric 			message("250-8BITMIME");
30964359Seric 			if (MaxMessageSize > 0)
31064359Seric 				message("250-SIZE %ld", MaxMessageSize);
31159271Seric 			else
31259271Seric 				message("250-SIZE");
31368848Seric #if DSN
31468606Seric 			message("250-X-DSN-03 (Unpublished draft of 12 Mar 1995)");
31568028Seric #endif
31658323Seric 			message("250 HELP");
3174976Seric 			break;
3184976Seric 
3194549Seric 		  case CMDMAIL:		/* mail -- designate sender */
32061093Seric 			SmtpPhase = "server MAIL";
32124943Seric 
3229314Seric 			/* check for validity of this command */
32358789Seric 			if (!gothello)
32458082Seric 			{
32558957Seric 				/* set sending host to our known value */
32659016Seric 				if (sendinghost == NULL)
32766005Seric 					sendinghost = peerhostname;
32858957Seric 
32958789Seric 				if (bitset(PRIV_NEEDMAILHELO, PrivacyFlags))
33058821Seric 				{
33158789Seric 					message("503 Polite people say HELO first");
33258821Seric 					break;
33358821Seric 				}
33458082Seric 			}
33558109Seric 			if (gotmail)
3364558Seric 			{
33758151Seric 				message("503 Sender already specified");
33863843Seric 				if (InChild)
33963843Seric 					finis();
3404558Seric 				break;
3414558Seric 			}
3429339Seric 			if (InChild)
3439339Seric 			{
34436230Skarels 				errno = 0;
34558151Seric 				syserr("503 Nested MAIL command: MAIL %s", p);
34658069Seric 				finis();
3479339Seric 			}
3489339Seric 
3499339Seric 			/* fork a subprocess to process this command */
35055012Seric 			if (runinchild("SMTP-MAIL", e) > 0)
3519339Seric 				break;
35263753Seric 			if (!gothello)
35363753Seric 			{
35463753Seric 				auth_warning(e,
35566005Seric 					"Host %s didn't use HELO protocol",
35666005Seric 					peerhostname);
35763753Seric 			}
35865947Seric #ifdef PICKY_HELO_CHECK
35966005Seric 			if (strcasecmp(sendinghost, peerhostname) != 0 &&
36066005Seric 			    (strcasecmp(peerhostname, "localhost") != 0 ||
36165823Seric 			     strcasecmp(sendinghost, MyHostName) != 0))
36265823Seric 			{
36365823Seric 				auth_warning(e, "Host %s claimed to be %s",
36466005Seric 					peerhostname, sendinghost);
36565823Seric 			}
36665947Seric #endif
36765823Seric 
36858323Seric 			if (protocol == NULL)
36958323Seric 				protocol = "SMTP";
37058323Seric 			define('r', protocol, e);
37159016Seric 			define('s', sendinghost, e);
37255012Seric 			initsys(e);
37359747Seric 			nrcpts = 0;
37468582Seric 			e->e_flags |= EF_LOGSENDER|EF_CLRQUEUE;
37565058Seric 			setproctitle("%s %s: %.80s", e->e_id, CurSmtpClient, inp);
3769339Seric 
3779339Seric 			/* child -- go do the processing */
3784549Seric 			p = skipword(p, "from");
3794549Seric 			if (p == NULL)
3804549Seric 				break;
38157977Seric 			if (setjmp(TopFrame) > 0)
38258147Seric 			{
38358147Seric 				/* this failed -- undo work */
38458147Seric 				if (InChild)
38559058Seric 				{
38659058Seric 					QuickAbort = FALSE;
38759058Seric 					SuprErrs = TRUE;
38863787Seric 					e->e_flags &= ~EF_FATALERRS;
38958147Seric 					finis();
39059058Seric 				}
39157977Seric 				break;
39258147Seric 			}
39357977Seric 			QuickAbort = TRUE;
39458333Seric 
39558333Seric 			/* must parse sender first */
39658333Seric 			delimptr = NULL;
39758704Seric 			setsender(p, e, &delimptr, FALSE);
39858333Seric 			p = delimptr;
39958333Seric 			if (p != NULL && *p != '\0')
40058333Seric 				*p++ = '\0';
40158333Seric 
40266325Seric 			/* check for possible spoofing */
40366325Seric 			if (RealUid != 0 && OpMode == MD_SMTP &&
40467473Seric 			    !bitnset(M_LOCALMAILER, e->e_from.q_mailer->m_flags) &&
40567473Seric 			    strcmp(e->e_from.q_user, RealUserName) != 0)
40666325Seric 			{
40766325Seric 				auth_warning(e, "%s owned process doing -bs",
40866325Seric 					RealUserName);
40966325Seric 			}
41066325Seric 
41158333Seric 			/* now parse ESMTP arguments */
41268560Seric 			e->e_msgsize = 0;
41366764Seric 			while (p != NULL && *p != '\0')
41458333Seric 			{
41558333Seric 				char *kp;
41666304Seric 				char *vp = NULL;
41758333Seric 
41858333Seric 				/* locate the beginning of the keyword */
41958333Seric 				while (isascii(*p) && isspace(*p))
42058333Seric 					p++;
42158333Seric 				if (*p == '\0')
42258333Seric 					break;
42358333Seric 				kp = p;
42458333Seric 
42558333Seric 				/* skip to the value portion */
42658333Seric 				while (isascii(*p) && isalnum(*p) || *p == '-')
42758333Seric 					p++;
42858333Seric 				if (*p == '=')
42958333Seric 				{
43058333Seric 					*p++ = '\0';
43158333Seric 					vp = p;
43258333Seric 
43358333Seric 					/* skip to the end of the value */
43458333Seric 					while (*p != '\0' && *p != ' ' &&
43558333Seric 					       !(isascii(*p) && iscntrl(*p)) &&
43658333Seric 					       *p != '=')
43758333Seric 						p++;
43858333Seric 				}
43958333Seric 
44058333Seric 				if (*p != '\0')
44158333Seric 					*p++ = '\0';
44258333Seric 
44358333Seric 				if (tTd(19, 1))
44466764Seric 					printf("MAIL: got arg %s=\"%s\"\n", kp,
44558333Seric 						vp == NULL ? "<null>" : vp);
44658333Seric 
44768559Seric 				mail_esmtp_args(kp, vp, e);
44858333Seric 			}
44959284Seric 
45068560Seric 			if (MaxMessageSize > 0 && e->e_msgsize > MaxMessageSize)
45159284Seric 			{
45259284Seric 				usrerr("552 Message size exceeds fixed maximum message size (%ld)",
45359284Seric 					MaxMessageSize);
45459284Seric 				/* NOTREACHED */
45559284Seric 			}
45658333Seric 
45768560Seric 			if (!enoughspace(e->e_msgsize))
45858333Seric 			{
45958333Seric 				message("452 Insufficient disk space; try again later");
46058333Seric 				break;
46158333Seric 			}
46258151Seric 			message("250 Sender ok");
46358147Seric 			gotmail = TRUE;
4644549Seric 			break;
4654549Seric 
4664976Seric 		  case CMDRCPT:		/* rcpt -- designate recipient */
46758850Seric 			if (!gotmail)
46858850Seric 			{
46958850Seric 				usrerr("503 Need MAIL before RCPT");
47058850Seric 				break;
47158850Seric 			}
47261093Seric 			SmtpPhase = "server RCPT";
47312612Seric 			if (setjmp(TopFrame) > 0)
47414785Seric 			{
47555012Seric 				e->e_flags &= ~EF_FATALERRS;
47612612Seric 				break;
47714785Seric 			}
47812612Seric 			QuickAbort = TRUE;
47951951Seric 			LogUsrErrs = TRUE;
48058093Seric 
48159699Seric 			if (e->e_sendmode != SM_DELIVER)
48259699Seric 				e->e_flags |= EF_VRFYONLY;
48358919Seric 
4844549Seric 			p = skipword(p, "to");
4854549Seric 			if (p == NULL)
4864549Seric 				break;
48767880Seric 			a = parseaddr(p, NULLADDR, RF_COPYALL, ' ', &delimptr, e);
48812612Seric 			if (a == NULL)
48912612Seric 				break;
49067880Seric 			p = delimptr;
49167880Seric 
49267880Seric 			/* now parse ESMTP arguments */
49367880Seric 			while (p != NULL && *p != '\0')
49467880Seric 			{
49567880Seric 				char *kp;
49667880Seric 				char *vp = NULL;
49767880Seric 
49867880Seric 				/* locate the beginning of the keyword */
49967880Seric 				while (isascii(*p) && isspace(*p))
50067880Seric 					p++;
50167880Seric 				if (*p == '\0')
50267880Seric 					break;
50367880Seric 				kp = p;
50467880Seric 
50567880Seric 				/* skip to the value portion */
50667880Seric 				while (isascii(*p) && isalnum(*p) || *p == '-')
50767880Seric 					p++;
50867880Seric 				if (*p == '=')
50967880Seric 				{
51067880Seric 					*p++ = '\0';
51167880Seric 					vp = p;
51267880Seric 
51367880Seric 					/* skip to the end of the value */
51467880Seric 					while (*p != '\0' && *p != ' ' &&
51567880Seric 					       !(isascii(*p) && iscntrl(*p)) &&
51667880Seric 					       *p != '=')
51767880Seric 						p++;
51867880Seric 				}
51967880Seric 
52067880Seric 				if (*p != '\0')
52167880Seric 					*p++ = '\0';
52267880Seric 
52367880Seric 				if (tTd(19, 1))
52467880Seric 					printf("RCPT: got arg %s=\"%s\"\n", kp,
52567880Seric 						vp == NULL ? "<null>" : vp);
52667880Seric 
52767963Seric 				rcpt_esmtp_args(a, kp, vp, e);
52867963Seric 
52967880Seric 			}
53067980Seric 
53167980Seric 			/* save in recipient list after ESMTP mods */
53267982Seric 			a = recipient(a, &e->e_sendqueue, 0, e);
53367980Seric 
53412612Seric 			if (Errors != 0)
53512612Seric 				break;
53612612Seric 
53712612Seric 			/* no errors during parsing, but might be a duplicate */
53855012Seric 			e->e_to = p;
53912612Seric 			if (!bitset(QBADADDR, a->q_flags))
54059747Seric 			{
54164718Seric 				message("250 Recipient ok%s",
54264718Seric 					bitset(QQUEUEUP, a->q_flags) ?
54364718Seric 						" (will queue)" : "");
54459747Seric 				nrcpts++;
54559747Seric 			}
54612612Seric 			else
5474549Seric 			{
54812612Seric 				/* punt -- should keep message in ADDRESS.... */
54958151Seric 				message("550 Addressee unknown");
5504549Seric 			}
55155012Seric 			e->e_to = NULL;
5524549Seric 			break;
5534549Seric 
5544549Seric 		  case CMDDATA:		/* data -- text of mail */
55561093Seric 			SmtpPhase = "server DATA";
55658109Seric 			if (!gotmail)
5574549Seric 			{
55858151Seric 				message("503 Need MAIL command");
5594976Seric 				break;
5604549Seric 			}
56164718Seric 			else if (nrcpts <= 0)
5624549Seric 			{
56358151Seric 				message("503 Need RCPT (recipient)");
5644976Seric 				break;
5654549Seric 			}
5664976Seric 
56758929Seric 			/* check to see if we need to re-expand aliases */
56863787Seric 			/* also reset QBADADDR on already-diagnosted addrs */
56963787Seric 			doublequeue = FALSE;
57058929Seric 			for (a = e->e_sendqueue; a != NULL; a = a->q_next)
57158929Seric 			{
57258929Seric 				if (bitset(QVERIFIED, a->q_flags))
57363787Seric 				{
57463787Seric 					/* need to re-expand aliases */
57563787Seric 					doublequeue = TRUE;
57663787Seric 				}
57763787Seric 				if (bitset(QBADADDR, a->q_flags))
57863787Seric 				{
57963787Seric 					/* make this "go away" */
58063787Seric 					a->q_flags |= QDONTSEND;
58163787Seric 					a->q_flags &= ~QBADADDR;
58263787Seric 				}
58358929Seric 			}
58458929Seric 
5854976Seric 			/* collect the text of the message */
58624943Seric 			SmtpPhase = "collect";
58768692Seric 			buffer_errors();
58867546Seric 			collect(InChannel, TRUE, doublequeue, NULL, e);
58968692Seric 			flush_errors(TRUE);
59064766Seric 			if (Errors != 0)
59164766Seric 				goto abortmessage;
59267131Seric 
59368582Seric 			/* make sure we actually do delivery */
59468582Seric 			e->e_flags &= ~EF_CLRQUEUE;
59568582Seric 
59667131Seric 			/* from now on, we have to operate silently */
59768692Seric 			buffer_errors();
59867131Seric 			e->e_errormode = EM_MAIL;
5994976Seric 
6008238Seric 			/*
6018238Seric 			**  Arrange to send to everyone.
6028238Seric 			**	If sending to multiple people, mail back
6038238Seric 			**		errors rather than reporting directly.
6048238Seric 			**	In any case, don't mail back errors for
6058238Seric 			**		anything that has happened up to
6068238Seric 			**		now (the other end will do this).
60710197Seric 			**	Truncate our transcript -- the mail has gotten
60810197Seric 			**		to us successfully, and if we have
60910197Seric 			**		to mail this back, it will be easier
61010197Seric 			**		on the reader.
6118238Seric 			**	Then send to everyone.
6128238Seric 			**	Finally give a reply code.  If an error has
6138238Seric 			**		already been given, don't mail a
6148238Seric 			**		message back.
6159339Seric 			**	We goose error returns by clearing error bit.
6168238Seric 			*/
6178238Seric 
61824943Seric 			SmtpPhase = "delivery";
61955012Seric 			e->e_xfp = freopen(queuename(e, 'x'), "w", e->e_xfp);
62058714Seric 			id = e->e_id;
6214976Seric 
62267131Seric 			if (doublequeue)
62359730Seric 			{
62467131Seric 				/* make sure it is in the queue */
62567131Seric 				queueup(e, TRUE, FALSE);
62668035Seric 				if (e->e_sendmode == SM_QUEUE)
62768035Seric 					e->e_flags |= EF_KEEPQUEUE;
62859730Seric 			}
6298238Seric 			else
63058919Seric 			{
63167131Seric 				/* send to all recipients */
63267131Seric 				sendall(e, SM_DEFAULT);
63367131Seric 			}
63467131Seric 			e->e_to = NULL;
63559747Seric 
63667131Seric 			/* issue success message */
63767131Seric 			message("250 %s Message accepted for delivery", id);
63864296Seric 
63967131Seric 			/* if we just queued, poke it */
64067131Seric 			if (doublequeue && e->e_sendmode != SM_QUEUE)
64167131Seric 			{
64267131Seric 				extern pid_t dowork();
64367131Seric 
64467131Seric 				unlockqueue(e);
64567131Seric 				(void) dowork(id, TRUE, TRUE, e);
64658919Seric 			}
64758883Seric 
64859747Seric   abortmessage:
6499339Seric 			/* if in a child, pop back to our parent */
6509339Seric 			if (InChild)
6519339Seric 				finis();
65224943Seric 
65324943Seric 			/* clean up a bit */
65458109Seric 			gotmail = FALSE;
65555012Seric 			dropenvelope(e);
65658179Seric 			CurEnv = e = newenvelope(e, CurEnv);
65755012Seric 			e->e_flags = BlankEnvelope.e_flags;
6584549Seric 			break;
6594549Seric 
6604549Seric 		  case CMDRSET:		/* rset -- reset state */
66158151Seric 			message("250 Reset state");
66268603Seric 
66368603Seric 			/* arrange to ignore any current send list */
66468603Seric 			e->e_sendqueue = NULL;
66564359Seric 			e->e_flags |= EF_CLRQUEUE;
6669339Seric 			if (InChild)
6679339Seric 				finis();
66858109Seric 
66958109Seric 			/* clean up a bit */
67058109Seric 			gotmail = FALSE;
67158109Seric 			dropenvelope(e);
67258179Seric 			CurEnv = e = newenvelope(e, CurEnv);
6739339Seric 			break;
6744549Seric 
6754549Seric 		  case CMDVRFY:		/* vrfy -- verify address */
67658092Seric 		  case CMDEXPN:		/* expn -- expand address */
67758092Seric 			vrfy = c->cmdcode == CMDVRFY;
67858092Seric 			if (bitset(vrfy ? PRIV_NOVRFY : PRIV_NOEXPN,
67958092Seric 						PrivacyFlags))
68058082Seric 			{
68158412Seric 				if (vrfy)
68267160Seric 					message("252 Cannot VRFY user; try RCPT to attempt delivery (or try finger)");
68358412Seric 				else
68465192Seric 					message("502 Sorry, we do not allow this operation");
68565017Seric #ifdef LOG
68665017Seric 				if (LogLevel > 5)
68765017Seric 					syslog(LOG_INFO, "%s: %s [rejected]",
68865017Seric 						CurSmtpClient, inp);
68965017Seric #endif
69058082Seric 				break;
69158082Seric 			}
69258082Seric 			else if (!gothello &&
69358092Seric 				 bitset(vrfy ? PRIV_NEEDVRFYHELO : PRIV_NEEDEXPNHELO,
69458092Seric 						PrivacyFlags))
69558082Seric 			{
69658151Seric 				message("503 I demand that you introduce yourself first");
69758082Seric 				break;
69858082Seric 			}
69958092Seric 			if (runinchild(vrfy ? "SMTP-VRFY" : "SMTP-EXPN", e) > 0)
7009339Seric 				break;
70155173Seric #ifdef LOG
70258020Seric 			if (LogLevel > 5)
70365017Seric 				syslog(LOG_INFO, "%s: %s", CurSmtpClient, inp);
70455173Seric #endif
7055003Seric 			vrfyqueue = NULL;
7067762Seric 			QuickAbort = TRUE;
70758092Seric 			if (vrfy)
70858092Seric 				e->e_flags |= EF_VRFYONLY;
70962373Seric 			while (*p != '\0' && isascii(*p) && isspace(*p))
71067615Seric 				p++;
71162373Seric 			if (*p == '\0')
71262373Seric 			{
71362373Seric 				message("501 Argument required");
71462373Seric 				Errors++;
71562373Seric 			}
71662373Seric 			else
71762373Seric 			{
71867990Seric 				(void) sendtolist(p, NULLADDR, &vrfyqueue, 0, e);
71962373Seric 			}
7207762Seric 			if (Errors != 0)
7219339Seric 			{
7229339Seric 				if (InChild)
7239339Seric 					finis();
7247762Seric 				break;
7259339Seric 			}
72662373Seric 			if (vrfyqueue == NULL)
72762373Seric 			{
72862373Seric 				message("554 Nothing to %s", vrfy ? "VRFY" : "EXPN");
72962373Seric 			}
7305003Seric 			while (vrfyqueue != NULL)
7315003Seric 			{
73263971Seric 				a = vrfyqueue;
73363971Seric 				while ((a = a->q_next) != NULL &&
73463971Seric 				       bitset(QDONTSEND|QBADADDR, a->q_flags))
73563971Seric 					continue;
7367685Seric 				if (!bitset(QDONTSEND|QBADADDR, vrfyqueue->q_flags))
73758151Seric 					printvrfyaddr(vrfyqueue, a == NULL);
73863847Seric 				vrfyqueue = vrfyqueue->q_next;
7395003Seric 			}
7409339Seric 			if (InChild)
7419339Seric 				finis();
7424549Seric 			break;
7434549Seric 
7444549Seric 		  case CMDHELP:		/* help -- give user info */
7454577Seric 			help(p);
7464549Seric 			break;
7474549Seric 
7484549Seric 		  case CMDNOOP:		/* noop -- do nothing */
74964122Seric 			message("250 OK");
7504549Seric 			break;
7514549Seric 
7524549Seric 		  case CMDQUIT:		/* quit -- leave mail */
75358151Seric 			message("221 %s closing connection", MyHostName);
75461051Seric 
75566283Seric doquit:
75668603Seric 			/* arrange to ignore any current send list */
75768603Seric 			e->e_sendqueue = NULL;
75868603Seric 
75961051Seric 			/* avoid future 050 messages */
76066017Seric 			disconnect(1, e);
76161051Seric 
7629339Seric 			if (InChild)
7639339Seric 				ExitStat = EX_QUIT;
7644549Seric 			finis();
7654549Seric 
7668544Seric 		  case CMDVERB:		/* set verbose mode */
76759957Seric 			if (bitset(PRIV_NOEXPN, PrivacyFlags))
76859957Seric 			{
76959957Seric 				/* this would give out the same info */
77059957Seric 				message("502 Verbose unavailable");
77159957Seric 				break;
77259957Seric 			}
7738544Seric 			Verbose = TRUE;
77458734Seric 			e->e_sendmode = SM_DELIVER;
77559957Seric 			message("250 Verbose mode");
7768544Seric 			break;
7778544Seric 
7789314Seric 		  case CMDONEX:		/* doing one transaction only */
7799378Seric 			OneXact = TRUE;
78059957Seric 			message("250 Only one transaction");
7819314Seric 			break;
7829314Seric 
78336230Skarels # ifdef SMTPDEBUG
7849339Seric 		  case CMDDBGQSHOW:	/* show queues */
7856907Seric 			printf("Send Queue=");
78655012Seric 			printaddr(e->e_sendqueue, TRUE);
7875003Seric 			break;
7887275Seric 
7897275Seric 		  case CMDDBGDEBUG:	/* set debug mode */
7907676Seric 			tTsetup(tTdvect, sizeof tTdvect, "0-99.1");
7917676Seric 			tTflag(p);
79258151Seric 			message("200 Debug set");
7937275Seric 			break;
7947275Seric 
79536230Skarels # else /* not SMTPDEBUG */
79636230Skarels 		  case CMDDBGQSHOW:	/* show queues */
79736230Skarels 		  case CMDDBGDEBUG:	/* set debug mode */
79864685Seric # endif /* SMTPDEBUG */
79964685Seric 		  case CMDLOGBOGUS:	/* bogus command */
80036233Skarels # ifdef LOG
80158308Seric 			if (LogLevel > 0)
80264685Seric 				syslog(LOG_CRIT,
80358020Seric 				    "\"%s\" command from %s (%s)",
80466005Seric 				    c->cmdname, peerhostname,
80558755Seric 				    anynet_ntoa(&RealHostAddr));
80636233Skarels # endif
80736230Skarels 			/* FALL THROUGH */
80836230Skarels 
8094549Seric 		  case CMDERROR:	/* unknown command */
81066283Seric 			if (++badcommands > MAXBADCOMMANDS)
81166283Seric 			{
81266283Seric 				message("421 %s Too many bad commands; closing connection",
81366283Seric 					MyHostName);
81466283Seric 				goto doquit;
81566283Seric 			}
81666283Seric 
81758151Seric 			message("500 Command unrecognized");
8184549Seric 			break;
8194549Seric 
8204549Seric 		  default:
82136230Skarels 			errno = 0;
82258151Seric 			syserr("500 smtp: unknown code %d", c->cmdcode);
8234549Seric 			break;
8244549Seric 		}
8254549Seric 	}
8264549Seric }
8274549Seric /*
8284549Seric **  SKIPWORD -- skip a fixed word.
8294549Seric **
8304549Seric **	Parameters:
8314549Seric **		p -- place to start looking.
8324549Seric **		w -- word to skip.
8334549Seric **
8344549Seric **	Returns:
8354549Seric **		p following w.
8364549Seric **		NULL on error.
8374549Seric **
8384549Seric **	Side Effects:
8394549Seric **		clobbers the p data area.
8404549Seric */
8414549Seric 
8424549Seric static char *
8434549Seric skipword(p, w)
8444549Seric 	register char *p;
8454549Seric 	char *w;
8464549Seric {
8474549Seric 	register char *q;
84866005Seric 	char *firstp = p;
8494549Seric 
8504549Seric 	/* find beginning of word */
85158050Seric 	while (isascii(*p) && isspace(*p))
8524549Seric 		p++;
8534549Seric 	q = p;
8544549Seric 
8554549Seric 	/* find end of word */
85658050Seric 	while (*p != '\0' && *p != ':' && !(isascii(*p) && isspace(*p)))
8574549Seric 		p++;
85858050Seric 	while (isascii(*p) && isspace(*p))
8594549Seric 		*p++ = '\0';
8604549Seric 	if (*p != ':')
8614549Seric 	{
8624549Seric 	  syntax:
86366005Seric 		message("501 Syntax error in parameters scanning \"%s\"",
86466005Seric 			firstp);
8654549Seric 		Errors++;
8664549Seric 		return (NULL);
8674549Seric 	}
8684549Seric 	*p++ = '\0';
86958050Seric 	while (isascii(*p) && isspace(*p))
8704549Seric 		p++;
8714549Seric 
87262373Seric 	if (*p == '\0')
87362373Seric 		goto syntax;
87462373Seric 
8754549Seric 	/* see if the input word matches desired word */
87633725Sbostic 	if (strcasecmp(q, w))
8774549Seric 		goto syntax;
8784549Seric 
8794549Seric 	return (p);
8804549Seric }
8814577Seric /*
88268559Seric **  MAIL_ESMTP_ARGS -- process ESMTP arguments from MAIL line
88368559Seric **
88468559Seric **	Parameters:
88568559Seric **		kp -- the parameter key.
88668559Seric **		vp -- the value of that parameter.
88768559Seric **		e -- the envelope.
88868559Seric **
88968559Seric **	Returns:
89068559Seric **		none.
89168559Seric */
89268559Seric 
89368559Seric mail_esmtp_args(kp, vp, e)
89468559Seric 	char *kp;
89568559Seric 	char *vp;
89668559Seric 	ENVELOPE *e;
89768559Seric {
89868559Seric 	if (strcasecmp(kp, "size") == 0)
89968559Seric 	{
90068559Seric 		if (vp == NULL)
90168559Seric 		{
90268559Seric 			usrerr("501 SIZE requires a value");
90368559Seric 			/* NOTREACHED */
90468559Seric 		}
90568890Seric # if defined(__STDC__) && !defined(BROKEN_ANSI_LIBRARY)
90668560Seric 		e->e_msgsize = strtoul(vp, (char **) NULL, 10);
90768559Seric # else
90868560Seric 		e->e_msgsize = strtol(vp, (char **) NULL, 10);
90968559Seric # endif
91068559Seric 	}
91168559Seric 	else if (strcasecmp(kp, "body") == 0)
91268559Seric 	{
91368559Seric 		if (vp == NULL)
91468559Seric 		{
91568559Seric 			usrerr("501 BODY requires a value");
91668559Seric 			/* NOTREACHED */
91768559Seric 		}
91868559Seric 		if (strcasecmp(vp, "8bitmime") == 0)
91968559Seric 		{
92068559Seric 			SevenBitInput = FALSE;
92168883Seric 			e->e_flags |= EF_NL_NOT_EOL;
92268559Seric 		}
92368559Seric 		else if (strcasecmp(vp, "7bit") == 0)
92468559Seric 		{
92568559Seric 			SevenBitInput = TRUE;
92668559Seric 		}
92768559Seric 		else
92868559Seric 		{
92968559Seric 			usrerr("501 Unknown BODY type %s",
93068559Seric 				vp);
93168559Seric 			/* NOTREACHED */
93268559Seric 		}
93368559Seric 		e->e_bodytype = newstr(vp);
93468559Seric 	}
93568559Seric 	else if (strcasecmp(kp, "envid") == 0)
93668559Seric 	{
93768559Seric 		if (vp == NULL)
93868559Seric 		{
93968559Seric 			usrerr("501 ENVID requires a value");
94068559Seric 			/* NOTREACHED */
94168559Seric 		}
94268583Seric 		if (!xtextok(vp))
94368583Seric 		{
94468583Seric 			usrerr("501 Syntax error in ENVID parameter value");
94568583Seric 			/* NOTREACHED */
94668583Seric 		}
94768583Seric 		if (e->e_envid != NULL)
94868583Seric 		{
94968583Seric 			usrerr("501 Duplicate ENVID parameter");
95068583Seric 			/* NOTREACHED */
95168583Seric 		}
95268559Seric 		e->e_envid = newstr(vp);
95368559Seric 	}
95468559Seric 	else if (strcasecmp(kp, "ret") == 0)
95568559Seric 	{
95668559Seric 		if (vp == NULL)
95768559Seric 		{
95868559Seric 			usrerr("501 RET requires a value");
95968559Seric 			/* NOTREACHED */
96068559Seric 		}
96168583Seric 		if (bitset(EF_RET_PARAM, e->e_flags))
96268583Seric 		{
96368583Seric 			usrerr("501 Duplicate RET parameter");
96468583Seric 			/* NOTREACHED */
96568583Seric 		}
96668559Seric 		e->e_flags |= EF_RET_PARAM;
96768559Seric 		if (strcasecmp(vp, "hdrs") == 0)
96868559Seric 			e->e_flags |= EF_NO_BODY_RETN;
96968559Seric 		else if (strcasecmp(vp, "full") != 0)
97068559Seric 		{
97168559Seric 			usrerr("501 Bad argument \"%s\" to RET", vp);
97268559Seric 			/* NOTREACHED */
97368559Seric 		}
97468559Seric 	}
97568559Seric 	else
97668559Seric 	{
97768559Seric 		usrerr("501 %s parameter unrecognized", kp);
97868559Seric 		/* NOTREACHED */
97968559Seric 	}
98068559Seric }
98168559Seric /*
98267963Seric **  RCPT_ESMTP_ARGS -- process ESMTP arguments from RCPT line
98367963Seric **
98467963Seric **	Parameters:
98567963Seric **		a -- the address corresponding to the To: parameter.
98667963Seric **		kp -- the parameter key.
98767963Seric **		vp -- the value of that parameter.
98867963Seric **		e -- the envelope.
98967963Seric **
99067963Seric **	Returns:
99167963Seric **		none.
99267963Seric */
99367963Seric 
99467963Seric rcpt_esmtp_args(a, kp, vp, e)
99567963Seric 	ADDRESS *a;
99667963Seric 	char *kp;
99767963Seric 	char *vp;
99867963Seric 	ENVELOPE *e;
99967963Seric {
100067963Seric 	if (strcasecmp(kp, "notify") == 0)
100167963Seric 	{
100267963Seric 		char *p;
100367963Seric 
100467963Seric 		if (vp == NULL)
100567963Seric 		{
100667963Seric 			usrerr("501 NOTIFY requires a value");
100767963Seric 			/* NOTREACHED */
100867963Seric 		}
100967963Seric 		a->q_flags &= ~(QPINGONSUCCESS|QPINGONFAILURE|QPINGONDELAY);
101068595Seric 		a->q_flags |= QHASNOTIFY;
101167963Seric 		if (strcasecmp(vp, "never") == 0)
101267963Seric 			return;
101367963Seric 		for (p = vp; p != NULL; vp = p)
101467963Seric 		{
101567963Seric 			p = strchr(p, ',');
101667963Seric 			if (p != NULL)
101767963Seric 				*p++ = '\0';
101867963Seric 			if (strcasecmp(vp, "success") == 0)
101967963Seric 				a->q_flags |= QPINGONSUCCESS;
102067963Seric 			else if (strcasecmp(vp, "failure") == 0)
102167963Seric 				a->q_flags |= QPINGONFAILURE;
102267963Seric 			else if (strcasecmp(vp, "delay") == 0)
102367963Seric 				a->q_flags |= QPINGONDELAY;
102467963Seric 			else
102567963Seric 			{
102667963Seric 				usrerr("501 Bad argument \"%s\"  to NOTIFY",
102767963Seric 					vp);
102867963Seric 				/* NOTREACHED */
102967963Seric 			}
103067963Seric 		}
103167963Seric 	}
103267963Seric 	else if (strcasecmp(kp, "orcpt") == 0)
103367963Seric 	{
103467963Seric 		if (vp == NULL)
103567963Seric 		{
103667963Seric 			usrerr("501 ORCPT requires a value");
103767963Seric 			/* NOTREACHED */
103867963Seric 		}
103968583Seric 		if (!xtextok(vp))
104068583Seric 		{
104168583Seric 			usrerr("501 Syntax error in ORCPT parameter value");
104268583Seric 			/* NOTREACHED */
104368583Seric 		}
104468583Seric 		if (a->q_orcpt != NULL)
104568583Seric 		{
104668583Seric 			usrerr("501 Duplicate ORCPT parameter");
104768583Seric 			/* NOTREACHED */
104868583Seric 		}
104967963Seric 		a->q_orcpt = newstr(vp);
105067963Seric 	}
105167963Seric 	else
105267963Seric 	{
105367963Seric 		usrerr("501 %s parameter unrecognized", kp);
105467963Seric 		/* NOTREACHED */
105567963Seric 	}
105667963Seric }
105767963Seric /*
105858151Seric **  PRINTVRFYADDR -- print an entry in the verify queue
105958151Seric **
106058151Seric **	Parameters:
106158151Seric **		a -- the address to print
106258151Seric **		last -- set if this is the last one.
106358151Seric **
106458151Seric **	Returns:
106558151Seric **		none.
106658151Seric **
106758151Seric **	Side Effects:
106858151Seric **		Prints the appropriate 250 codes.
106958151Seric */
107058151Seric 
107158151Seric printvrfyaddr(a, last)
107258151Seric 	register ADDRESS *a;
107358151Seric 	bool last;
107458151Seric {
107558151Seric 	char fmtbuf[20];
107658151Seric 
107758151Seric 	strcpy(fmtbuf, "250");
107858151Seric 	fmtbuf[3] = last ? ' ' : '-';
107958151Seric 
108059746Seric 	if (a->q_fullname == NULL)
108159746Seric 	{
108259746Seric 		if (strchr(a->q_user, '@') == NULL)
108359746Seric 			strcpy(&fmtbuf[4], "<%s@%s>");
108459746Seric 		else
108559746Seric 			strcpy(&fmtbuf[4], "<%s>");
108659746Seric 		message(fmtbuf, a->q_user, MyHostName);
108759746Seric 	}
108858151Seric 	else
108958151Seric 	{
109059746Seric 		if (strchr(a->q_user, '@') == NULL)
109159746Seric 			strcpy(&fmtbuf[4], "%s <%s@%s>");
109259746Seric 		else
109359746Seric 			strcpy(&fmtbuf[4], "%s <%s>");
109459746Seric 		message(fmtbuf, a->q_fullname, a->q_user, MyHostName);
109558151Seric 	}
109658151Seric }
109758151Seric /*
10984577Seric **  HELP -- implement the HELP command.
10994577Seric **
11004577Seric **	Parameters:
11014577Seric **		topic -- the topic we want help for.
11024577Seric **
11034577Seric **	Returns:
11044577Seric **		none.
11054577Seric **
11064577Seric **	Side Effects:
11074577Seric **		outputs the help file to message output.
11084577Seric */
11094577Seric 
11104577Seric help(topic)
11114577Seric 	char *topic;
11124577Seric {
11134577Seric 	register FILE *hf;
11144577Seric 	int len;
111568751Seric 	bool noinfo;
11164577Seric 	char buf[MAXLINE];
111768751Seric 	extern char Version[];
11184577Seric 
111968751Seric 
11208269Seric 	if (HelpFile == NULL || (hf = fopen(HelpFile, "r")) == NULL)
11214577Seric 	{
11224577Seric 		/* no help */
112311931Seric 		errno = 0;
112468751Seric 		message("502 Sendmail %s -- HELP not implemented", Version);
11254577Seric 		return;
11264577Seric 	}
11274577Seric 
112849669Seric 	if (topic == NULL || *topic == '\0')
112968751Seric 	{
113049669Seric 		topic = "smtp";
113168751Seric 		message("214-This is Sendmail version %s", Version);
113268751Seric 		noinfo = FALSE;
113368751Seric 	}
113449669Seric 	else
113568751Seric 	{
113649669Seric 		makelower(topic);
113768751Seric 		noinfo = TRUE;
113868751Seric 	}
113949669Seric 
11404577Seric 	len = strlen(topic);
11414577Seric 
11424577Seric 	while (fgets(buf, sizeof buf, hf) != NULL)
11434577Seric 	{
11444577Seric 		if (strncmp(buf, topic, len) == 0)
11454577Seric 		{
11464577Seric 			register char *p;
11474577Seric 
114856795Seric 			p = strchr(buf, '\t');
11494577Seric 			if (p == NULL)
11504577Seric 				p = buf;
11514577Seric 			else
11524577Seric 				p++;
11534577Seric 			fixcrlf(p, TRUE);
115458151Seric 			message("214-%s", p);
11554577Seric 			noinfo = FALSE;
11564577Seric 		}
11574577Seric 	}
11584577Seric 
11594577Seric 	if (noinfo)
116058151Seric 		message("504 HELP topic unknown");
11614577Seric 	else
116258151Seric 		message("214 End of HELP info");
11634628Seric 	(void) fclose(hf);
11644577Seric }
11658544Seric /*
11669339Seric **  RUNINCHILD -- return twice -- once in the child, then in the parent again
11679339Seric **
11689339Seric **	Parameters:
11699339Seric **		label -- a string used in error messages
11709339Seric **
11719339Seric **	Returns:
11729339Seric **		zero in the child
11739339Seric **		one in the parent
11749339Seric **
11759339Seric **	Side Effects:
11769339Seric **		none.
11779339Seric */
11788544Seric 
117955012Seric runinchild(label, e)
11809339Seric 	char *label;
118155012Seric 	register ENVELOPE *e;
11829339Seric {
11839339Seric 	int childpid;
11849339Seric 
118516158Seric 	if (!OneXact)
11869339Seric 	{
118716158Seric 		childpid = dofork();
118816158Seric 		if (childpid < 0)
118916158Seric 		{
119016158Seric 			syserr("%s: cannot fork", label);
119116158Seric 			return (1);
119216158Seric 		}
119316158Seric 		if (childpid > 0)
119416158Seric 		{
119516158Seric 			auto int st;
11969339Seric 
119716158Seric 			/* parent -- wait for child to complete */
119861093Seric 			setproctitle("server %s child wait", CurHostName);
119916158Seric 			st = waitfor(childpid);
120016158Seric 			if (st == -1)
120116158Seric 				syserr("%s: lost child", label);
120264948Seric 			else if (!WIFEXITED(st))
120364948Seric 				syserr("%s: died on signal %d",
120464948Seric 					label, st & 0177);
12059339Seric 
120616158Seric 			/* if we exited on a QUIT command, complete the process */
120766017Seric 			if (WEXITSTATUS(st) == EX_QUIT)
120866017Seric 			{
120966017Seric 				disconnect(1, e);
121016158Seric 				finis();
121166017Seric 			}
12129339Seric 
121316158Seric 			return (1);
121416158Seric 		}
121516158Seric 		else
121616158Seric 		{
121716158Seric 			/* child */
121816158Seric 			InChild = TRUE;
121925050Seric 			QuickAbort = FALSE;
122055012Seric 			clearenvelope(e, FALSE);
122116158Seric 		}
12229339Seric 	}
122315256Seric 
122416158Seric 	/* open alias database */
122560537Seric 	initmaps(FALSE, e);
122616158Seric 
122716158Seric 	return (0);
12289339Seric }
12299339Seric 
123056795Seric # endif /* SMTP */
1231