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*68769Seric static char sccsid[] = "@(#)srvrsmtp.c	8.66 (Berkeley) 04/10/95 (with SMTP)";
1433731Sbostic #else
15*68769Seric static char sccsid[] = "@(#)srvrsmtp.c	8.66 (Berkeley) 04/10/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);
138*68769Seric 
139*68769Seric 	/* output the first line, inserting "ESMTP" as second word */
140*68769Seric 	p = strchr(inp, '\n');
141*68769Seric 	if (p != NULL)
142*68769Seric 		*p++ = '\0';
143*68769Seric 	id = strchr(inp, ' ');
144*68769Seric 	if (id == NULL)
145*68769Seric 		id = &inp[strlen(inp)];
146*68769Seric 	cmd = p == NULL ? "220 %.*s ESMTP%s" : "220-%.*s ESMTP%s";
147*68769Seric 	message(cmd, id - inp, inp, id);
148*68769Seric 
149*68769Seric 	/* output remaining lines */
150*68769Seric 	while ((id = p) != NULL && (p = strchr(id, '\n')) != NULL)
15164496Seric 	{
152*68769Seric 		*p++ = '\0';
153*68769Seric 		message("220-%s", id);
15464496Seric 	}
155*68769Seric 	if (id != NULL)
156*68769Seric 		message("220 %s", id);
15766745Seric 
15858330Seric 	protocol = NULL;
15959016Seric 	sendinghost = macvalue('s', e);
16058082Seric 	gothello = FALSE;
16158330Seric 	gotmail = FALSE;
1624549Seric 	for (;;)
1634549Seric 	{
16412612Seric 		/* arrange for backout */
16565751Seric 		if (setjmp(TopFrame) > 0)
16659058Seric 		{
16765751Seric 			/* if() nesting is necessary for Cray UNICOS */
16865751Seric 			if (InChild)
16965751Seric 			{
17065751Seric 				QuickAbort = FALSE;
17165751Seric 				SuprErrs = TRUE;
17265751Seric 				finis();
17365751Seric 			}
17459058Seric 		}
17512612Seric 		QuickAbort = FALSE;
17612612Seric 		HoldErrs = FALSE;
17751951Seric 		LogUsrErrs = FALSE;
17863843Seric 		e->e_flags &= ~(EF_VRFYONLY|EF_GLOBALERRS);
17912612Seric 
1807356Seric 		/* setup for the read */
18155012Seric 		e->e_to = NULL;
1824577Seric 		Errors = 0;
1837275Seric 		(void) fflush(stdout);
1847356Seric 
1857356Seric 		/* read the input line */
18661093Seric 		SmtpPhase = "server cmd read";
18761093Seric 		setproctitle("server %s cmd read", CurHostName);
18861093Seric 		p = sfgets(inp, sizeof inp, InChannel, TimeOuts.to_nextcommand,
18961093Seric 				SmtpPhase);
1907356Seric 
1917685Seric 		/* handle errors */
1927356Seric 		if (p == NULL)
1937356Seric 		{
1944549Seric 			/* end of file, just die */
19566017Seric 			disconnect(1, e);
19658151Seric 			message("421 %s Lost input channel from %s",
19765017Seric 				MyHostName, CurSmtpClient);
19855464Seric #ifdef LOG
19963843Seric 			if (LogLevel > (gotmail ? 1 : 19))
20055464Seric 				syslog(LOG_NOTICE, "lost input channel from %s",
20165017Seric 					CurSmtpClient);
20255464Seric #endif
20358069Seric 			if (InChild)
20458069Seric 				ExitStat = EX_QUIT;
2054549Seric 			finis();
2064549Seric 		}
2074549Seric 
2084549Seric 		/* clean up end of line */
2094558Seric 		fixcrlf(inp, TRUE);
2104549Seric 
2114713Seric 		/* echo command to transcript */
21255012Seric 		if (e->e_xfp != NULL)
21355012Seric 			fprintf(e->e_xfp, "<<< %s\n", inp);
2144713Seric 
21559060Seric 		if (e->e_id == NULL)
21665058Seric 			setproctitle("%s: %.80s", CurSmtpClient, inp);
21759060Seric 		else
21865058Seric 			setproctitle("%s %s: %.80s", e->e_id, CurSmtpClient, inp);
21959060Seric 
2204549Seric 		/* break off command */
22158050Seric 		for (p = inp; isascii(*p) && isspace(*p); p++)
2224549Seric 			continue;
22357232Seric 		cmd = cmdbuf;
22458050Seric 		while (*p != '\0' &&
22558050Seric 		       !(isascii(*p) && isspace(*p)) &&
22658050Seric 		       cmd < &cmdbuf[sizeof cmdbuf - 2])
22724981Seric 			*cmd++ = *p++;
22824981Seric 		*cmd = '\0';
2294549Seric 
23025691Seric 		/* throw away leading whitespace */
23158050Seric 		while (isascii(*p) && isspace(*p))
23225691Seric 			p++;
23325691Seric 
2344549Seric 		/* decode command */
2354549Seric 		for (c = CmdTab; c->cmdname != NULL; c++)
2364549Seric 		{
23733725Sbostic 			if (!strcasecmp(c->cmdname, cmdbuf))
2384549Seric 				break;
2394549Seric 		}
2404549Seric 
24151954Seric 		/* reset errors */
24251954Seric 		errno = 0;
24351954Seric 
2444549Seric 		/* process command */
2454549Seric 		switch (c->cmdcode)
2464549Seric 		{
2474976Seric 		  case CMDHELO:		/* hello -- introduce yourself */
24858323Seric 		  case CMDEHLO:		/* extended hello */
24958323Seric 			if (c->cmdcode == CMDEHLO)
25058323Seric 			{
25158323Seric 				protocol = "ESMTP";
25261093Seric 				SmtpPhase = "server EHLO";
25358323Seric 			}
25458323Seric 			else
25558323Seric 			{
25658323Seric 				protocol = "SMTP";
25761093Seric 				SmtpPhase = "server HELO";
25858323Seric 			}
25967445Seric 
26067445Seric 			/* check for valid domain name (re 1123 5.2.5) */
26167445Seric 			if (*p == '\0')
26267445Seric 			{
26367445Seric 				message("501 %s requires domain address",
26467445Seric 					cmdbuf);
26567445Seric 				break;
26667445Seric 			}
26767445Seric 			else
26867445Seric 			{
26967445Seric 				register char *q;
27067445Seric 
27167445Seric 				for (q = p; *q != '\0'; q++)
27267445Seric 				{
27367445Seric 					if (!isascii(*q))
27467445Seric 						break;
27567445Seric 					if (isalnum(*q))
27667445Seric 						continue;
27767445Seric 					if (strchr("[].-_#", *q) == NULL)
27867445Seric 						break;
27967445Seric 				}
28067445Seric 				if (*q != '\0')
28167445Seric 				{
28267445Seric 					message("501 Invalid domain name");
28367445Seric 					break;
28467445Seric 				}
28567445Seric 			}
28667445Seric 
28759016Seric 			sendinghost = newstr(p);
28860210Seric 			gothello = TRUE;
28960210Seric 			if (c->cmdcode != CMDEHLO)
29060239Seric 			{
29160239Seric 				/* print old message and be done with it */
29260239Seric 				message("250 %s Hello %s, pleased to meet you",
29365017Seric 					MyHostName, CurSmtpClient);
29460210Seric 				break;
29560239Seric 			}
29660239Seric 
29760239Seric 			/* print extended message and brag */
29860239Seric 			message("250-%s Hello %s, pleased to meet you",
29966760Seric 				MyHostName, CurSmtpClient);
30058323Seric 			if (!bitset(PRIV_NOEXPN, PrivacyFlags))
30158323Seric 				message("250-EXPN");
30267417Seric 			message("250-8BITMIME");
30364359Seric 			if (MaxMessageSize > 0)
30464359Seric 				message("250-SIZE %ld", MaxMessageSize);
30559271Seric 			else
30659271Seric 				message("250-SIZE");
30768028Seric #ifdef DSN
30868606Seric 			message("250-X-DSN-03 (Unpublished draft of 12 Mar 1995)");
30968028Seric #endif
31058323Seric 			message("250 HELP");
3114976Seric 			break;
3124976Seric 
3134549Seric 		  case CMDMAIL:		/* mail -- designate sender */
31461093Seric 			SmtpPhase = "server MAIL";
31524943Seric 
3169314Seric 			/* check for validity of this command */
31758789Seric 			if (!gothello)
31858082Seric 			{
31958957Seric 				/* set sending host to our known value */
32059016Seric 				if (sendinghost == NULL)
32166005Seric 					sendinghost = peerhostname;
32258957Seric 
32358789Seric 				if (bitset(PRIV_NEEDMAILHELO, PrivacyFlags))
32458821Seric 				{
32558789Seric 					message("503 Polite people say HELO first");
32658821Seric 					break;
32758821Seric 				}
32858082Seric 			}
32958109Seric 			if (gotmail)
3304558Seric 			{
33158151Seric 				message("503 Sender already specified");
33263843Seric 				if (InChild)
33363843Seric 					finis();
3344558Seric 				break;
3354558Seric 			}
3369339Seric 			if (InChild)
3379339Seric 			{
33836230Skarels 				errno = 0;
33958151Seric 				syserr("503 Nested MAIL command: MAIL %s", p);
34058069Seric 				finis();
3419339Seric 			}
3429339Seric 
3439339Seric 			/* fork a subprocess to process this command */
34455012Seric 			if (runinchild("SMTP-MAIL", e) > 0)
3459339Seric 				break;
34663753Seric 			if (!gothello)
34763753Seric 			{
34863753Seric 				auth_warning(e,
34966005Seric 					"Host %s didn't use HELO protocol",
35066005Seric 					peerhostname);
35163753Seric 			}
35265947Seric #ifdef PICKY_HELO_CHECK
35366005Seric 			if (strcasecmp(sendinghost, peerhostname) != 0 &&
35466005Seric 			    (strcasecmp(peerhostname, "localhost") != 0 ||
35565823Seric 			     strcasecmp(sendinghost, MyHostName) != 0))
35665823Seric 			{
35765823Seric 				auth_warning(e, "Host %s claimed to be %s",
35866005Seric 					peerhostname, sendinghost);
35965823Seric 			}
36065947Seric #endif
36165823Seric 
36258323Seric 			if (protocol == NULL)
36358323Seric 				protocol = "SMTP";
36458323Seric 			define('r', protocol, e);
36559016Seric 			define('s', sendinghost, e);
36655012Seric 			initsys(e);
36759747Seric 			nrcpts = 0;
36868582Seric 			e->e_flags |= EF_LOGSENDER|EF_CLRQUEUE;
36965058Seric 			setproctitle("%s %s: %.80s", e->e_id, CurSmtpClient, inp);
3709339Seric 
3719339Seric 			/* child -- go do the processing */
3724549Seric 			p = skipword(p, "from");
3734549Seric 			if (p == NULL)
3744549Seric 				break;
37557977Seric 			if (setjmp(TopFrame) > 0)
37658147Seric 			{
37758147Seric 				/* this failed -- undo work */
37858147Seric 				if (InChild)
37959058Seric 				{
38059058Seric 					QuickAbort = FALSE;
38159058Seric 					SuprErrs = TRUE;
38263787Seric 					e->e_flags &= ~EF_FATALERRS;
38358147Seric 					finis();
38459058Seric 				}
38557977Seric 				break;
38658147Seric 			}
38757977Seric 			QuickAbort = TRUE;
38858333Seric 
38958333Seric 			/* must parse sender first */
39058333Seric 			delimptr = NULL;
39158704Seric 			setsender(p, e, &delimptr, FALSE);
39258333Seric 			p = delimptr;
39358333Seric 			if (p != NULL && *p != '\0')
39458333Seric 				*p++ = '\0';
39558333Seric 
39666325Seric 			/* check for possible spoofing */
39766325Seric 			if (RealUid != 0 && OpMode == MD_SMTP &&
39867473Seric 			    !bitnset(M_LOCALMAILER, e->e_from.q_mailer->m_flags) &&
39967473Seric 			    strcmp(e->e_from.q_user, RealUserName) != 0)
40066325Seric 			{
40166325Seric 				auth_warning(e, "%s owned process doing -bs",
40266325Seric 					RealUserName);
40366325Seric 			}
40466325Seric 
40558333Seric 			/* now parse ESMTP arguments */
40668560Seric 			e->e_msgsize = 0;
40766764Seric 			while (p != NULL && *p != '\0')
40858333Seric 			{
40958333Seric 				char *kp;
41066304Seric 				char *vp = NULL;
41158333Seric 
41258333Seric 				/* locate the beginning of the keyword */
41358333Seric 				while (isascii(*p) && isspace(*p))
41458333Seric 					p++;
41558333Seric 				if (*p == '\0')
41658333Seric 					break;
41758333Seric 				kp = p;
41858333Seric 
41958333Seric 				/* skip to the value portion */
42058333Seric 				while (isascii(*p) && isalnum(*p) || *p == '-')
42158333Seric 					p++;
42258333Seric 				if (*p == '=')
42358333Seric 				{
42458333Seric 					*p++ = '\0';
42558333Seric 					vp = p;
42658333Seric 
42758333Seric 					/* skip to the end of the value */
42858333Seric 					while (*p != '\0' && *p != ' ' &&
42958333Seric 					       !(isascii(*p) && iscntrl(*p)) &&
43058333Seric 					       *p != '=')
43158333Seric 						p++;
43258333Seric 				}
43358333Seric 
43458333Seric 				if (*p != '\0')
43558333Seric 					*p++ = '\0';
43658333Seric 
43758333Seric 				if (tTd(19, 1))
43866764Seric 					printf("MAIL: got arg %s=\"%s\"\n", kp,
43958333Seric 						vp == NULL ? "<null>" : vp);
44058333Seric 
44168559Seric 				mail_esmtp_args(kp, vp, e);
44258333Seric 			}
44359284Seric 
44468560Seric 			if (MaxMessageSize > 0 && e->e_msgsize > MaxMessageSize)
44559284Seric 			{
44659284Seric 				usrerr("552 Message size exceeds fixed maximum message size (%ld)",
44759284Seric 					MaxMessageSize);
44859284Seric 				/* NOTREACHED */
44959284Seric 			}
45058333Seric 
45168560Seric 			if (!enoughspace(e->e_msgsize))
45258333Seric 			{
45358333Seric 				message("452 Insufficient disk space; try again later");
45458333Seric 				break;
45558333Seric 			}
45658151Seric 			message("250 Sender ok");
45758147Seric 			gotmail = TRUE;
4584549Seric 			break;
4594549Seric 
4604976Seric 		  case CMDRCPT:		/* rcpt -- designate recipient */
46158850Seric 			if (!gotmail)
46258850Seric 			{
46358850Seric 				usrerr("503 Need MAIL before RCPT");
46458850Seric 				break;
46558850Seric 			}
46661093Seric 			SmtpPhase = "server RCPT";
46712612Seric 			if (setjmp(TopFrame) > 0)
46814785Seric 			{
46955012Seric 				e->e_flags &= ~EF_FATALERRS;
47012612Seric 				break;
47114785Seric 			}
47212612Seric 			QuickAbort = TRUE;
47351951Seric 			LogUsrErrs = TRUE;
47458093Seric 
47559699Seric 			if (e->e_sendmode != SM_DELIVER)
47659699Seric 				e->e_flags |= EF_VRFYONLY;
47758919Seric 
4784549Seric 			p = skipword(p, "to");
4794549Seric 			if (p == NULL)
4804549Seric 				break;
48167880Seric 			a = parseaddr(p, NULLADDR, RF_COPYALL, ' ', &delimptr, e);
48212612Seric 			if (a == NULL)
48312612Seric 				break;
48467880Seric 			p = delimptr;
48567880Seric 
48667880Seric 			/* now parse ESMTP arguments */
48767880Seric 			while (p != NULL && *p != '\0')
48867880Seric 			{
48967880Seric 				char *kp;
49067880Seric 				char *vp = NULL;
49167880Seric 
49267880Seric 				/* locate the beginning of the keyword */
49367880Seric 				while (isascii(*p) && isspace(*p))
49467880Seric 					p++;
49567880Seric 				if (*p == '\0')
49667880Seric 					break;
49767880Seric 				kp = p;
49867880Seric 
49967880Seric 				/* skip to the value portion */
50067880Seric 				while (isascii(*p) && isalnum(*p) || *p == '-')
50167880Seric 					p++;
50267880Seric 				if (*p == '=')
50367880Seric 				{
50467880Seric 					*p++ = '\0';
50567880Seric 					vp = p;
50667880Seric 
50767880Seric 					/* skip to the end of the value */
50867880Seric 					while (*p != '\0' && *p != ' ' &&
50967880Seric 					       !(isascii(*p) && iscntrl(*p)) &&
51067880Seric 					       *p != '=')
51167880Seric 						p++;
51267880Seric 				}
51367880Seric 
51467880Seric 				if (*p != '\0')
51567880Seric 					*p++ = '\0';
51667880Seric 
51767880Seric 				if (tTd(19, 1))
51867880Seric 					printf("RCPT: got arg %s=\"%s\"\n", kp,
51967880Seric 						vp == NULL ? "<null>" : vp);
52067880Seric 
52167963Seric 				rcpt_esmtp_args(a, kp, vp, e);
52267963Seric 
52367880Seric 			}
52467980Seric 
52567980Seric 			/* save in recipient list after ESMTP mods */
52667982Seric 			a = recipient(a, &e->e_sendqueue, 0, e);
52767980Seric 
52812612Seric 			if (Errors != 0)
52912612Seric 				break;
53012612Seric 
53112612Seric 			/* no errors during parsing, but might be a duplicate */
53255012Seric 			e->e_to = p;
53312612Seric 			if (!bitset(QBADADDR, a->q_flags))
53459747Seric 			{
53564718Seric 				message("250 Recipient ok%s",
53664718Seric 					bitset(QQUEUEUP, a->q_flags) ?
53764718Seric 						" (will queue)" : "");
53859747Seric 				nrcpts++;
53959747Seric 			}
54012612Seric 			else
5414549Seric 			{
54212612Seric 				/* punt -- should keep message in ADDRESS.... */
54358151Seric 				message("550 Addressee unknown");
5444549Seric 			}
54555012Seric 			e->e_to = NULL;
5464549Seric 			break;
5474549Seric 
5484549Seric 		  case CMDDATA:		/* data -- text of mail */
54961093Seric 			SmtpPhase = "server DATA";
55058109Seric 			if (!gotmail)
5514549Seric 			{
55258151Seric 				message("503 Need MAIL command");
5534976Seric 				break;
5544549Seric 			}
55564718Seric 			else if (nrcpts <= 0)
5564549Seric 			{
55758151Seric 				message("503 Need RCPT (recipient)");
5584976Seric 				break;
5594549Seric 			}
5604976Seric 
56158929Seric 			/* check to see if we need to re-expand aliases */
56263787Seric 			/* also reset QBADADDR on already-diagnosted addrs */
56363787Seric 			doublequeue = FALSE;
56458929Seric 			for (a = e->e_sendqueue; a != NULL; a = a->q_next)
56558929Seric 			{
56658929Seric 				if (bitset(QVERIFIED, a->q_flags))
56763787Seric 				{
56863787Seric 					/* need to re-expand aliases */
56963787Seric 					doublequeue = TRUE;
57063787Seric 				}
57163787Seric 				if (bitset(QBADADDR, a->q_flags))
57263787Seric 				{
57363787Seric 					/* make this "go away" */
57463787Seric 					a->q_flags |= QDONTSEND;
57563787Seric 					a->q_flags &= ~QBADADDR;
57663787Seric 				}
57758929Seric 			}
57858929Seric 
5794976Seric 			/* collect the text of the message */
58024943Seric 			SmtpPhase = "collect";
58168692Seric 			buffer_errors();
58267546Seric 			collect(InChannel, TRUE, doublequeue, NULL, e);
58368692Seric 			flush_errors(TRUE);
58464766Seric 			if (Errors != 0)
58564766Seric 				goto abortmessage;
58667131Seric 
58768582Seric 			/* make sure we actually do delivery */
58868582Seric 			e->e_flags &= ~EF_CLRQUEUE;
58968582Seric 
59067131Seric 			/* from now on, we have to operate silently */
59168692Seric 			buffer_errors();
59267131Seric 			e->e_errormode = EM_MAIL;
5934976Seric 
5948238Seric 			/*
5958238Seric 			**  Arrange to send to everyone.
5968238Seric 			**	If sending to multiple people, mail back
5978238Seric 			**		errors rather than reporting directly.
5988238Seric 			**	In any case, don't mail back errors for
5998238Seric 			**		anything that has happened up to
6008238Seric 			**		now (the other end will do this).
60110197Seric 			**	Truncate our transcript -- the mail has gotten
60210197Seric 			**		to us successfully, and if we have
60310197Seric 			**		to mail this back, it will be easier
60410197Seric 			**		on the reader.
6058238Seric 			**	Then send to everyone.
6068238Seric 			**	Finally give a reply code.  If an error has
6078238Seric 			**		already been given, don't mail a
6088238Seric 			**		message back.
6099339Seric 			**	We goose error returns by clearing error bit.
6108238Seric 			*/
6118238Seric 
61224943Seric 			SmtpPhase = "delivery";
61355012Seric 			e->e_xfp = freopen(queuename(e, 'x'), "w", e->e_xfp);
61458714Seric 			id = e->e_id;
6154976Seric 
61667131Seric 			if (doublequeue)
61759730Seric 			{
61867131Seric 				/* make sure it is in the queue */
61967131Seric 				queueup(e, TRUE, FALSE);
62068035Seric 				if (e->e_sendmode == SM_QUEUE)
62168035Seric 					e->e_flags |= EF_KEEPQUEUE;
62259730Seric 			}
6238238Seric 			else
62458919Seric 			{
62567131Seric 				/* send to all recipients */
62667131Seric 				sendall(e, SM_DEFAULT);
62767131Seric 			}
62867131Seric 			e->e_to = NULL;
62959747Seric 
63067131Seric 			/* issue success message */
63167131Seric 			message("250 %s Message accepted for delivery", id);
63264296Seric 
63367131Seric 			/* if we just queued, poke it */
63467131Seric 			if (doublequeue && e->e_sendmode != SM_QUEUE)
63567131Seric 			{
63667131Seric 				extern pid_t dowork();
63767131Seric 
63867131Seric 				unlockqueue(e);
63967131Seric 				(void) dowork(id, TRUE, TRUE, e);
64058919Seric 			}
64158883Seric 
64259747Seric   abortmessage:
6439339Seric 			/* if in a child, pop back to our parent */
6449339Seric 			if (InChild)
6459339Seric 				finis();
64624943Seric 
64724943Seric 			/* clean up a bit */
64858109Seric 			gotmail = FALSE;
64955012Seric 			dropenvelope(e);
65058179Seric 			CurEnv = e = newenvelope(e, CurEnv);
65155012Seric 			e->e_flags = BlankEnvelope.e_flags;
6524549Seric 			break;
6534549Seric 
6544549Seric 		  case CMDRSET:		/* rset -- reset state */
65558151Seric 			message("250 Reset state");
65668603Seric 
65768603Seric 			/* arrange to ignore any current send list */
65868603Seric 			e->e_sendqueue = NULL;
65964359Seric 			e->e_flags |= EF_CLRQUEUE;
6609339Seric 			if (InChild)
6619339Seric 				finis();
66258109Seric 
66358109Seric 			/* clean up a bit */
66458109Seric 			gotmail = FALSE;
66558109Seric 			dropenvelope(e);
66658179Seric 			CurEnv = e = newenvelope(e, CurEnv);
6679339Seric 			break;
6684549Seric 
6694549Seric 		  case CMDVRFY:		/* vrfy -- verify address */
67058092Seric 		  case CMDEXPN:		/* expn -- expand address */
67158092Seric 			vrfy = c->cmdcode == CMDVRFY;
67258092Seric 			if (bitset(vrfy ? PRIV_NOVRFY : PRIV_NOEXPN,
67358092Seric 						PrivacyFlags))
67458082Seric 			{
67558412Seric 				if (vrfy)
67667160Seric 					message("252 Cannot VRFY user; try RCPT to attempt delivery (or try finger)");
67758412Seric 				else
67865192Seric 					message("502 Sorry, we do not allow this operation");
67965017Seric #ifdef LOG
68065017Seric 				if (LogLevel > 5)
68165017Seric 					syslog(LOG_INFO, "%s: %s [rejected]",
68265017Seric 						CurSmtpClient, inp);
68365017Seric #endif
68458082Seric 				break;
68558082Seric 			}
68658082Seric 			else if (!gothello &&
68758092Seric 				 bitset(vrfy ? PRIV_NEEDVRFYHELO : PRIV_NEEDEXPNHELO,
68858092Seric 						PrivacyFlags))
68958082Seric 			{
69058151Seric 				message("503 I demand that you introduce yourself first");
69158082Seric 				break;
69258082Seric 			}
69358092Seric 			if (runinchild(vrfy ? "SMTP-VRFY" : "SMTP-EXPN", e) > 0)
6949339Seric 				break;
69555173Seric #ifdef LOG
69658020Seric 			if (LogLevel > 5)
69765017Seric 				syslog(LOG_INFO, "%s: %s", CurSmtpClient, inp);
69855173Seric #endif
6995003Seric 			vrfyqueue = NULL;
7007762Seric 			QuickAbort = TRUE;
70158092Seric 			if (vrfy)
70258092Seric 				e->e_flags |= EF_VRFYONLY;
70362373Seric 			while (*p != '\0' && isascii(*p) && isspace(*p))
70467615Seric 				p++;
70562373Seric 			if (*p == '\0')
70662373Seric 			{
70762373Seric 				message("501 Argument required");
70862373Seric 				Errors++;
70962373Seric 			}
71062373Seric 			else
71162373Seric 			{
71267990Seric 				(void) sendtolist(p, NULLADDR, &vrfyqueue, 0, e);
71362373Seric 			}
7147762Seric 			if (Errors != 0)
7159339Seric 			{
7169339Seric 				if (InChild)
7179339Seric 					finis();
7187762Seric 				break;
7199339Seric 			}
72062373Seric 			if (vrfyqueue == NULL)
72162373Seric 			{
72262373Seric 				message("554 Nothing to %s", vrfy ? "VRFY" : "EXPN");
72362373Seric 			}
7245003Seric 			while (vrfyqueue != NULL)
7255003Seric 			{
72663971Seric 				a = vrfyqueue;
72763971Seric 				while ((a = a->q_next) != NULL &&
72863971Seric 				       bitset(QDONTSEND|QBADADDR, a->q_flags))
72963971Seric 					continue;
7307685Seric 				if (!bitset(QDONTSEND|QBADADDR, vrfyqueue->q_flags))
73158151Seric 					printvrfyaddr(vrfyqueue, a == NULL);
73263847Seric 				vrfyqueue = vrfyqueue->q_next;
7335003Seric 			}
7349339Seric 			if (InChild)
7359339Seric 				finis();
7364549Seric 			break;
7374549Seric 
7384549Seric 		  case CMDHELP:		/* help -- give user info */
7394577Seric 			help(p);
7404549Seric 			break;
7414549Seric 
7424549Seric 		  case CMDNOOP:		/* noop -- do nothing */
74364122Seric 			message("250 OK");
7444549Seric 			break;
7454549Seric 
7464549Seric 		  case CMDQUIT:		/* quit -- leave mail */
74758151Seric 			message("221 %s closing connection", MyHostName);
74861051Seric 
74966283Seric doquit:
75068603Seric 			/* arrange to ignore any current send list */
75168603Seric 			e->e_sendqueue = NULL;
75268603Seric 
75361051Seric 			/* avoid future 050 messages */
75466017Seric 			disconnect(1, e);
75561051Seric 
7569339Seric 			if (InChild)
7579339Seric 				ExitStat = EX_QUIT;
7584549Seric 			finis();
7594549Seric 
7608544Seric 		  case CMDVERB:		/* set verbose mode */
76159957Seric 			if (bitset(PRIV_NOEXPN, PrivacyFlags))
76259957Seric 			{
76359957Seric 				/* this would give out the same info */
76459957Seric 				message("502 Verbose unavailable");
76559957Seric 				break;
76659957Seric 			}
7678544Seric 			Verbose = TRUE;
76858734Seric 			e->e_sendmode = SM_DELIVER;
76959957Seric 			message("250 Verbose mode");
7708544Seric 			break;
7718544Seric 
7729314Seric 		  case CMDONEX:		/* doing one transaction only */
7739378Seric 			OneXact = TRUE;
77459957Seric 			message("250 Only one transaction");
7759314Seric 			break;
7769314Seric 
77736230Skarels # ifdef SMTPDEBUG
7789339Seric 		  case CMDDBGQSHOW:	/* show queues */
7796907Seric 			printf("Send Queue=");
78055012Seric 			printaddr(e->e_sendqueue, TRUE);
7815003Seric 			break;
7827275Seric 
7837275Seric 		  case CMDDBGDEBUG:	/* set debug mode */
7847676Seric 			tTsetup(tTdvect, sizeof tTdvect, "0-99.1");
7857676Seric 			tTflag(p);
78658151Seric 			message("200 Debug set");
7877275Seric 			break;
7887275Seric 
78936230Skarels # else /* not SMTPDEBUG */
79036230Skarels 		  case CMDDBGQSHOW:	/* show queues */
79136230Skarels 		  case CMDDBGDEBUG:	/* set debug mode */
79264685Seric # endif /* SMTPDEBUG */
79364685Seric 		  case CMDLOGBOGUS:	/* bogus command */
79436233Skarels # ifdef LOG
79558308Seric 			if (LogLevel > 0)
79664685Seric 				syslog(LOG_CRIT,
79758020Seric 				    "\"%s\" command from %s (%s)",
79866005Seric 				    c->cmdname, peerhostname,
79958755Seric 				    anynet_ntoa(&RealHostAddr));
80036233Skarels # endif
80136230Skarels 			/* FALL THROUGH */
80236230Skarels 
8034549Seric 		  case CMDERROR:	/* unknown command */
80466283Seric 			if (++badcommands > MAXBADCOMMANDS)
80566283Seric 			{
80666283Seric 				message("421 %s Too many bad commands; closing connection",
80766283Seric 					MyHostName);
80866283Seric 				goto doquit;
80966283Seric 			}
81066283Seric 
81158151Seric 			message("500 Command unrecognized");
8124549Seric 			break;
8134549Seric 
8144549Seric 		  default:
81536230Skarels 			errno = 0;
81658151Seric 			syserr("500 smtp: unknown code %d", c->cmdcode);
8174549Seric 			break;
8184549Seric 		}
8194549Seric 	}
8204549Seric }
8214549Seric /*
8224549Seric **  SKIPWORD -- skip a fixed word.
8234549Seric **
8244549Seric **	Parameters:
8254549Seric **		p -- place to start looking.
8264549Seric **		w -- word to skip.
8274549Seric **
8284549Seric **	Returns:
8294549Seric **		p following w.
8304549Seric **		NULL on error.
8314549Seric **
8324549Seric **	Side Effects:
8334549Seric **		clobbers the p data area.
8344549Seric */
8354549Seric 
8364549Seric static char *
8374549Seric skipword(p, w)
8384549Seric 	register char *p;
8394549Seric 	char *w;
8404549Seric {
8414549Seric 	register char *q;
84266005Seric 	char *firstp = p;
8434549Seric 
8444549Seric 	/* find beginning of word */
84558050Seric 	while (isascii(*p) && isspace(*p))
8464549Seric 		p++;
8474549Seric 	q = p;
8484549Seric 
8494549Seric 	/* find end of word */
85058050Seric 	while (*p != '\0' && *p != ':' && !(isascii(*p) && isspace(*p)))
8514549Seric 		p++;
85258050Seric 	while (isascii(*p) && isspace(*p))
8534549Seric 		*p++ = '\0';
8544549Seric 	if (*p != ':')
8554549Seric 	{
8564549Seric 	  syntax:
85766005Seric 		message("501 Syntax error in parameters scanning \"%s\"",
85866005Seric 			firstp);
8594549Seric 		Errors++;
8604549Seric 		return (NULL);
8614549Seric 	}
8624549Seric 	*p++ = '\0';
86358050Seric 	while (isascii(*p) && isspace(*p))
8644549Seric 		p++;
8654549Seric 
86662373Seric 	if (*p == '\0')
86762373Seric 		goto syntax;
86862373Seric 
8694549Seric 	/* see if the input word matches desired word */
87033725Sbostic 	if (strcasecmp(q, w))
8714549Seric 		goto syntax;
8724549Seric 
8734549Seric 	return (p);
8744549Seric }
8754577Seric /*
87668559Seric **  MAIL_ESMTP_ARGS -- process ESMTP arguments from MAIL line
87768559Seric **
87868559Seric **	Parameters:
87968559Seric **		kp -- the parameter key.
88068559Seric **		vp -- the value of that parameter.
88168559Seric **		e -- the envelope.
88268559Seric **
88368559Seric **	Returns:
88468559Seric **		none.
88568559Seric */
88668559Seric 
88768559Seric mail_esmtp_args(kp, vp, e)
88868559Seric 	char *kp;
88968559Seric 	char *vp;
89068559Seric 	ENVELOPE *e;
89168559Seric {
89268559Seric 	if (strcasecmp(kp, "size") == 0)
89368559Seric 	{
89468559Seric 		if (vp == NULL)
89568559Seric 		{
89668559Seric 			usrerr("501 SIZE requires a value");
89768559Seric 			/* NOTREACHED */
89868559Seric 		}
89968559Seric # ifdef __STDC__
90068560Seric 		e->e_msgsize = strtoul(vp, (char **) NULL, 10);
90168559Seric # else
90268560Seric 		e->e_msgsize = strtol(vp, (char **) NULL, 10);
90368559Seric # endif
90468559Seric 	}
90568559Seric 	else if (strcasecmp(kp, "body") == 0)
90668559Seric 	{
90768559Seric 		if (vp == NULL)
90868559Seric 		{
90968559Seric 			usrerr("501 BODY requires a value");
91068559Seric 			/* NOTREACHED */
91168559Seric 		}
91268559Seric 		if (strcasecmp(vp, "8bitmime") == 0)
91368559Seric 		{
91468559Seric 			SevenBitInput = FALSE;
91568559Seric 		}
91668559Seric 		else if (strcasecmp(vp, "7bit") == 0)
91768559Seric 		{
91868559Seric 			SevenBitInput = TRUE;
91968559Seric 		}
92068559Seric 		else
92168559Seric 		{
92268559Seric 			usrerr("501 Unknown BODY type %s",
92368559Seric 				vp);
92468559Seric 			/* NOTREACHED */
92568559Seric 		}
92668559Seric 		e->e_bodytype = newstr(vp);
92768559Seric 	}
92868559Seric 	else if (strcasecmp(kp, "envid") == 0)
92968559Seric 	{
93068559Seric 		if (vp == NULL)
93168559Seric 		{
93268559Seric 			usrerr("501 ENVID requires a value");
93368559Seric 			/* NOTREACHED */
93468559Seric 		}
93568583Seric 		if (!xtextok(vp))
93668583Seric 		{
93768583Seric 			usrerr("501 Syntax error in ENVID parameter value");
93868583Seric 			/* NOTREACHED */
93968583Seric 		}
94068583Seric 		if (e->e_envid != NULL)
94168583Seric 		{
94268583Seric 			usrerr("501 Duplicate ENVID parameter");
94368583Seric 			/* NOTREACHED */
94468583Seric 		}
94568559Seric 		e->e_envid = newstr(vp);
94668559Seric 	}
94768559Seric 	else if (strcasecmp(kp, "ret") == 0)
94868559Seric 	{
94968559Seric 		if (vp == NULL)
95068559Seric 		{
95168559Seric 			usrerr("501 RET requires a value");
95268559Seric 			/* NOTREACHED */
95368559Seric 		}
95468583Seric 		if (bitset(EF_RET_PARAM, e->e_flags))
95568583Seric 		{
95668583Seric 			usrerr("501 Duplicate RET parameter");
95768583Seric 			/* NOTREACHED */
95868583Seric 		}
95968559Seric 		e->e_flags |= EF_RET_PARAM;
96068559Seric 		if (strcasecmp(vp, "hdrs") == 0)
96168559Seric 			e->e_flags |= EF_NO_BODY_RETN;
96268559Seric 		else if (strcasecmp(vp, "full") != 0)
96368559Seric 		{
96468559Seric 			usrerr("501 Bad argument \"%s\" to RET", vp);
96568559Seric 			/* NOTREACHED */
96668559Seric 		}
96768559Seric 	}
96868559Seric 	else
96968559Seric 	{
97068559Seric 		usrerr("501 %s parameter unrecognized", kp);
97168559Seric 		/* NOTREACHED */
97268559Seric 	}
97368559Seric }
97468559Seric /*
97567963Seric **  RCPT_ESMTP_ARGS -- process ESMTP arguments from RCPT line
97667963Seric **
97767963Seric **	Parameters:
97867963Seric **		a -- the address corresponding to the To: parameter.
97967963Seric **		kp -- the parameter key.
98067963Seric **		vp -- the value of that parameter.
98167963Seric **		e -- the envelope.
98267963Seric **
98367963Seric **	Returns:
98467963Seric **		none.
98567963Seric */
98667963Seric 
98767963Seric rcpt_esmtp_args(a, kp, vp, e)
98867963Seric 	ADDRESS *a;
98967963Seric 	char *kp;
99067963Seric 	char *vp;
99167963Seric 	ENVELOPE *e;
99267963Seric {
99367963Seric 	if (strcasecmp(kp, "notify") == 0)
99467963Seric 	{
99567963Seric 		char *p;
99667963Seric 
99767963Seric 		if (vp == NULL)
99867963Seric 		{
99967963Seric 			usrerr("501 NOTIFY requires a value");
100067963Seric 			/* NOTREACHED */
100167963Seric 		}
100267963Seric 		a->q_flags &= ~(QPINGONSUCCESS|QPINGONFAILURE|QPINGONDELAY);
100368595Seric 		a->q_flags |= QHASNOTIFY;
100467963Seric 		if (strcasecmp(vp, "never") == 0)
100567963Seric 			return;
100667963Seric 		for (p = vp; p != NULL; vp = p)
100767963Seric 		{
100867963Seric 			p = strchr(p, ',');
100967963Seric 			if (p != NULL)
101067963Seric 				*p++ = '\0';
101167963Seric 			if (strcasecmp(vp, "success") == 0)
101267963Seric 				a->q_flags |= QPINGONSUCCESS;
101367963Seric 			else if (strcasecmp(vp, "failure") == 0)
101467963Seric 				a->q_flags |= QPINGONFAILURE;
101567963Seric 			else if (strcasecmp(vp, "delay") == 0)
101667963Seric 				a->q_flags |= QPINGONDELAY;
101767963Seric 			else
101867963Seric 			{
101967963Seric 				usrerr("501 Bad argument \"%s\"  to NOTIFY",
102067963Seric 					vp);
102167963Seric 				/* NOTREACHED */
102267963Seric 			}
102367963Seric 		}
102467963Seric 	}
102567963Seric 	else if (strcasecmp(kp, "orcpt") == 0)
102667963Seric 	{
102767963Seric 		if (vp == NULL)
102867963Seric 		{
102967963Seric 			usrerr("501 ORCPT requires a value");
103067963Seric 			/* NOTREACHED */
103167963Seric 		}
103268583Seric 		if (!xtextok(vp))
103368583Seric 		{
103468583Seric 			usrerr("501 Syntax error in ORCPT parameter value");
103568583Seric 			/* NOTREACHED */
103668583Seric 		}
103768583Seric 		if (a->q_orcpt != NULL)
103868583Seric 		{
103968583Seric 			usrerr("501 Duplicate ORCPT parameter");
104068583Seric 			/* NOTREACHED */
104168583Seric 		}
104267963Seric 		a->q_orcpt = newstr(vp);
104367963Seric 	}
104467963Seric 	else
104567963Seric 	{
104667963Seric 		usrerr("501 %s parameter unrecognized", kp);
104767963Seric 		/* NOTREACHED */
104867963Seric 	}
104967963Seric }
105067963Seric /*
105158151Seric **  PRINTVRFYADDR -- print an entry in the verify queue
105258151Seric **
105358151Seric **	Parameters:
105458151Seric **		a -- the address to print
105558151Seric **		last -- set if this is the last one.
105658151Seric **
105758151Seric **	Returns:
105858151Seric **		none.
105958151Seric **
106058151Seric **	Side Effects:
106158151Seric **		Prints the appropriate 250 codes.
106258151Seric */
106358151Seric 
106458151Seric printvrfyaddr(a, last)
106558151Seric 	register ADDRESS *a;
106658151Seric 	bool last;
106758151Seric {
106858151Seric 	char fmtbuf[20];
106958151Seric 
107058151Seric 	strcpy(fmtbuf, "250");
107158151Seric 	fmtbuf[3] = last ? ' ' : '-';
107258151Seric 
107359746Seric 	if (a->q_fullname == NULL)
107459746Seric 	{
107559746Seric 		if (strchr(a->q_user, '@') == NULL)
107659746Seric 			strcpy(&fmtbuf[4], "<%s@%s>");
107759746Seric 		else
107859746Seric 			strcpy(&fmtbuf[4], "<%s>");
107959746Seric 		message(fmtbuf, a->q_user, MyHostName);
108059746Seric 	}
108158151Seric 	else
108258151Seric 	{
108359746Seric 		if (strchr(a->q_user, '@') == NULL)
108459746Seric 			strcpy(&fmtbuf[4], "%s <%s@%s>");
108559746Seric 		else
108659746Seric 			strcpy(&fmtbuf[4], "%s <%s>");
108759746Seric 		message(fmtbuf, a->q_fullname, a->q_user, MyHostName);
108858151Seric 	}
108958151Seric }
109058151Seric /*
10914577Seric **  HELP -- implement the HELP command.
10924577Seric **
10934577Seric **	Parameters:
10944577Seric **		topic -- the topic we want help for.
10954577Seric **
10964577Seric **	Returns:
10974577Seric **		none.
10984577Seric **
10994577Seric **	Side Effects:
11004577Seric **		outputs the help file to message output.
11014577Seric */
11024577Seric 
11034577Seric help(topic)
11044577Seric 	char *topic;
11054577Seric {
11064577Seric 	register FILE *hf;
11074577Seric 	int len;
110868751Seric 	bool noinfo;
11094577Seric 	char buf[MAXLINE];
111068751Seric 	extern char Version[];
11114577Seric 
111268751Seric 
11138269Seric 	if (HelpFile == NULL || (hf = fopen(HelpFile, "r")) == NULL)
11144577Seric 	{
11154577Seric 		/* no help */
111611931Seric 		errno = 0;
111768751Seric 		message("502 Sendmail %s -- HELP not implemented", Version);
11184577Seric 		return;
11194577Seric 	}
11204577Seric 
112149669Seric 	if (topic == NULL || *topic == '\0')
112268751Seric 	{
112349669Seric 		topic = "smtp";
112468751Seric 		message("214-This is Sendmail version %s", Version);
112568751Seric 		noinfo = FALSE;
112668751Seric 	}
112749669Seric 	else
112868751Seric 	{
112949669Seric 		makelower(topic);
113068751Seric 		noinfo = TRUE;
113168751Seric 	}
113249669Seric 
11334577Seric 	len = strlen(topic);
11344577Seric 
11354577Seric 	while (fgets(buf, sizeof buf, hf) != NULL)
11364577Seric 	{
11374577Seric 		if (strncmp(buf, topic, len) == 0)
11384577Seric 		{
11394577Seric 			register char *p;
11404577Seric 
114156795Seric 			p = strchr(buf, '\t');
11424577Seric 			if (p == NULL)
11434577Seric 				p = buf;
11444577Seric 			else
11454577Seric 				p++;
11464577Seric 			fixcrlf(p, TRUE);
114758151Seric 			message("214-%s", p);
11484577Seric 			noinfo = FALSE;
11494577Seric 		}
11504577Seric 	}
11514577Seric 
11524577Seric 	if (noinfo)
115358151Seric 		message("504 HELP topic unknown");
11544577Seric 	else
115558151Seric 		message("214 End of HELP info");
11564628Seric 	(void) fclose(hf);
11574577Seric }
11588544Seric /*
11599339Seric **  RUNINCHILD -- return twice -- once in the child, then in the parent again
11609339Seric **
11619339Seric **	Parameters:
11629339Seric **		label -- a string used in error messages
11639339Seric **
11649339Seric **	Returns:
11659339Seric **		zero in the child
11669339Seric **		one in the parent
11679339Seric **
11689339Seric **	Side Effects:
11699339Seric **		none.
11709339Seric */
11718544Seric 
117255012Seric runinchild(label, e)
11739339Seric 	char *label;
117455012Seric 	register ENVELOPE *e;
11759339Seric {
11769339Seric 	int childpid;
11779339Seric 
117816158Seric 	if (!OneXact)
11799339Seric 	{
118016158Seric 		childpid = dofork();
118116158Seric 		if (childpid < 0)
118216158Seric 		{
118316158Seric 			syserr("%s: cannot fork", label);
118416158Seric 			return (1);
118516158Seric 		}
118616158Seric 		if (childpid > 0)
118716158Seric 		{
118816158Seric 			auto int st;
11899339Seric 
119016158Seric 			/* parent -- wait for child to complete */
119161093Seric 			setproctitle("server %s child wait", CurHostName);
119216158Seric 			st = waitfor(childpid);
119316158Seric 			if (st == -1)
119416158Seric 				syserr("%s: lost child", label);
119564948Seric 			else if (!WIFEXITED(st))
119664948Seric 				syserr("%s: died on signal %d",
119764948Seric 					label, st & 0177);
11989339Seric 
119916158Seric 			/* if we exited on a QUIT command, complete the process */
120066017Seric 			if (WEXITSTATUS(st) == EX_QUIT)
120166017Seric 			{
120266017Seric 				disconnect(1, e);
120316158Seric 				finis();
120466017Seric 			}
12059339Seric 
120616158Seric 			return (1);
120716158Seric 		}
120816158Seric 		else
120916158Seric 		{
121016158Seric 			/* child */
121116158Seric 			InChild = TRUE;
121225050Seric 			QuickAbort = FALSE;
121355012Seric 			clearenvelope(e, FALSE);
121416158Seric 		}
12159339Seric 	}
121615256Seric 
121716158Seric 	/* open alias database */
121860537Seric 	initmaps(FALSE, e);
121916158Seric 
122016158Seric 	return (0);
12219339Seric }
12229339Seric 
122356795Seric # endif /* SMTP */
1224