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*69788Seric static char sccsid[] = "@(#)srvrsmtp.c	8.78 (Berkeley) 05/31/95 (with SMTP)";
1433731Sbostic #else
15*69788Seric static char sccsid[] = "@(#)srvrsmtp.c	8.78 (Berkeley) 05/31/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();
9463937Seric 
9566325Seric 
9666283Seric #define MAXBADCOMMANDS	25		/* maximum number of bad commands */
9766283Seric 
9869748Seric void
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;
12169748Seric 	extern void help __P((char *));
1224549Seric 
12359066Seric 	if (fileno(OutChannel) != fileno(stdout))
1247363Seric 	{
1257363Seric 		/* arrange for debugging output to go to remote host */
12659066Seric 		(void) dup2(fileno(OutChannel), fileno(stdout));
1277363Seric 	}
12855012Seric 	settime(e);
12966005Seric 	peerhostname = RealHostName;
13066005Seric 	if (peerhostname == NULL)
13166005Seric 		peerhostname = "localhost";
13266005Seric 	CurHostName = peerhostname;
13365017Seric 	CurSmtpClient = macvalue('_', e);
13465017Seric 	if (CurSmtpClient == NULL)
13566003Seric 		CurSmtpClient = CurHostName;
13665017Seric 
13765017Seric 	setproctitle("server %s startup", CurSmtpClient);
13868529Seric 	expand("\201e", inp, sizeof inp, e);
13968769Seric 
14068769Seric 	/* output the first line, inserting "ESMTP" as second word */
14168769Seric 	p = strchr(inp, '\n');
14268769Seric 	if (p != NULL)
14368769Seric 		*p++ = '\0';
14468769Seric 	id = strchr(inp, ' ');
14568769Seric 	if (id == NULL)
14668769Seric 		id = &inp[strlen(inp)];
14768769Seric 	cmd = p == NULL ? "220 %.*s ESMTP%s" : "220-%.*s ESMTP%s";
14868769Seric 	message(cmd, id - inp, inp, id);
14968769Seric 
15068769Seric 	/* output remaining lines */
15168769Seric 	while ((id = p) != NULL && (p = strchr(id, '\n')) != NULL)
15264496Seric 	{
15368769Seric 		*p++ = '\0';
15469106Seric 		if (isascii(*id) && isspace(*id))
15569106Seric 			id++;
15668769Seric 		message("220-%s", id);
15764496Seric 	}
15868769Seric 	if (id != NULL)
15969106Seric 	{
16069106Seric 		if (isascii(*id) && isspace(*id))
16169106Seric 			id++;
16268769Seric 		message("220 %s", id);
16369106Seric 	}
16466745Seric 
16558330Seric 	protocol = NULL;
16659016Seric 	sendinghost = macvalue('s', e);
16758082Seric 	gothello = FALSE;
16858330Seric 	gotmail = FALSE;
1694549Seric 	for (;;)
1704549Seric 	{
17112612Seric 		/* arrange for backout */
17265751Seric 		if (setjmp(TopFrame) > 0)
17359058Seric 		{
17465751Seric 			/* if() nesting is necessary for Cray UNICOS */
17565751Seric 			if (InChild)
17665751Seric 			{
17765751Seric 				QuickAbort = FALSE;
17865751Seric 				SuprErrs = TRUE;
17965751Seric 				finis();
18065751Seric 			}
18159058Seric 		}
18212612Seric 		QuickAbort = FALSE;
18312612Seric 		HoldErrs = FALSE;
18451951Seric 		LogUsrErrs = FALSE;
18563843Seric 		e->e_flags &= ~(EF_VRFYONLY|EF_GLOBALERRS);
18612612Seric 
1877356Seric 		/* setup for the read */
18855012Seric 		e->e_to = NULL;
1894577Seric 		Errors = 0;
1907275Seric 		(void) fflush(stdout);
1917356Seric 
1927356Seric 		/* read the input line */
19361093Seric 		SmtpPhase = "server cmd read";
194*69788Seric 		setproctitle("server %s cmd read", CurSmtpClient);
19561093Seric 		p = sfgets(inp, sizeof inp, InChannel, TimeOuts.to_nextcommand,
19661093Seric 				SmtpPhase);
1977356Seric 
1987685Seric 		/* handle errors */
1997356Seric 		if (p == NULL)
2007356Seric 		{
2014549Seric 			/* end of file, just die */
20266017Seric 			disconnect(1, e);
20358151Seric 			message("421 %s Lost input channel from %s",
20465017Seric 				MyHostName, CurSmtpClient);
20555464Seric #ifdef LOG
20663843Seric 			if (LogLevel > (gotmail ? 1 : 19))
20755464Seric 				syslog(LOG_NOTICE, "lost input channel from %s",
20865017Seric 					CurSmtpClient);
20955464Seric #endif
21058069Seric 			if (InChild)
21158069Seric 				ExitStat = EX_QUIT;
2124549Seric 			finis();
2134549Seric 		}
2144549Seric 
2154549Seric 		/* clean up end of line */
2164558Seric 		fixcrlf(inp, TRUE);
2174549Seric 
2184713Seric 		/* echo command to transcript */
21955012Seric 		if (e->e_xfp != NULL)
22055012Seric 			fprintf(e->e_xfp, "<<< %s\n", inp);
2214713Seric 
22259060Seric 		if (e->e_id == NULL)
22365058Seric 			setproctitle("%s: %.80s", CurSmtpClient, inp);
22459060Seric 		else
22565058Seric 			setproctitle("%s %s: %.80s", e->e_id, CurSmtpClient, inp);
22659060Seric 
2274549Seric 		/* break off command */
22858050Seric 		for (p = inp; isascii(*p) && isspace(*p); p++)
2294549Seric 			continue;
23057232Seric 		cmd = cmdbuf;
23158050Seric 		while (*p != '\0' &&
23258050Seric 		       !(isascii(*p) && isspace(*p)) &&
23358050Seric 		       cmd < &cmdbuf[sizeof cmdbuf - 2])
23424981Seric 			*cmd++ = *p++;
23524981Seric 		*cmd = '\0';
2364549Seric 
23725691Seric 		/* throw away leading whitespace */
23858050Seric 		while (isascii(*p) && isspace(*p))
23925691Seric 			p++;
24025691Seric 
2414549Seric 		/* decode command */
2424549Seric 		for (c = CmdTab; c->cmdname != NULL; c++)
2434549Seric 		{
24433725Sbostic 			if (!strcasecmp(c->cmdname, cmdbuf))
2454549Seric 				break;
2464549Seric 		}
2474549Seric 
24851954Seric 		/* reset errors */
24951954Seric 		errno = 0;
25051954Seric 
2514549Seric 		/* process command */
2524549Seric 		switch (c->cmdcode)
2534549Seric 		{
2544976Seric 		  case CMDHELO:		/* hello -- introduce yourself */
25558323Seric 		  case CMDEHLO:		/* extended hello */
25658323Seric 			if (c->cmdcode == CMDEHLO)
25758323Seric 			{
25858323Seric 				protocol = "ESMTP";
25961093Seric 				SmtpPhase = "server EHLO";
26058323Seric 			}
26158323Seric 			else
26258323Seric 			{
26358323Seric 				protocol = "SMTP";
26461093Seric 				SmtpPhase = "server HELO";
26558323Seric 			}
26667445Seric 
26767445Seric 			/* check for valid domain name (re 1123 5.2.5) */
26867445Seric 			if (*p == '\0')
26967445Seric 			{
27067445Seric 				message("501 %s requires domain address",
27167445Seric 					cmdbuf);
27267445Seric 				break;
27367445Seric 			}
27467445Seric 			else
27567445Seric 			{
27667445Seric 				register char *q;
27767445Seric 
27867445Seric 				for (q = p; *q != '\0'; q++)
27967445Seric 				{
28067445Seric 					if (!isascii(*q))
28167445Seric 						break;
28267445Seric 					if (isalnum(*q))
28367445Seric 						continue;
28467445Seric 					if (strchr("[].-_#", *q) == NULL)
28567445Seric 						break;
28667445Seric 				}
28767445Seric 				if (*q != '\0')
28867445Seric 				{
28967445Seric 					message("501 Invalid domain name");
29067445Seric 					break;
29167445Seric 				}
29267445Seric 			}
29367445Seric 
29459016Seric 			sendinghost = newstr(p);
29560210Seric 			gothello = TRUE;
29660210Seric 			if (c->cmdcode != CMDEHLO)
29760239Seric 			{
29860239Seric 				/* print old message and be done with it */
29960239Seric 				message("250 %s Hello %s, pleased to meet you",
30065017Seric 					MyHostName, CurSmtpClient);
30160210Seric 				break;
30260239Seric 			}
30360239Seric 
30460239Seric 			/* print extended message and brag */
30560239Seric 			message("250-%s Hello %s, pleased to meet you",
30666760Seric 				MyHostName, CurSmtpClient);
30758323Seric 			if (!bitset(PRIV_NOEXPN, PrivacyFlags))
30858323Seric 				message("250-EXPN");
30969480Seric #if MIME8TO7
31067417Seric 			message("250-8BITMIME");
31169480Seric #endif
31264359Seric 			if (MaxMessageSize > 0)
31364359Seric 				message("250-SIZE %ld", MaxMessageSize);
31459271Seric 			else
31559271Seric 				message("250-SIZE");
31668848Seric #if DSN
31769545Seric 			message("250-X-DSN-03 (Draft of 12 Mar 1995)");
31868028Seric #endif
31958323Seric 			message("250 HELP");
3204976Seric 			break;
3214976Seric 
3224549Seric 		  case CMDMAIL:		/* mail -- designate sender */
32361093Seric 			SmtpPhase = "server MAIL";
32424943Seric 
3259314Seric 			/* check for validity of this command */
32658789Seric 			if (!gothello)
32758082Seric 			{
32858957Seric 				/* set sending host to our known value */
32959016Seric 				if (sendinghost == NULL)
33066005Seric 					sendinghost = peerhostname;
33158957Seric 
33258789Seric 				if (bitset(PRIV_NEEDMAILHELO, PrivacyFlags))
33358821Seric 				{
33458789Seric 					message("503 Polite people say HELO first");
33558821Seric 					break;
33658821Seric 				}
33758082Seric 			}
33858109Seric 			if (gotmail)
3394558Seric 			{
34058151Seric 				message("503 Sender already specified");
34163843Seric 				if (InChild)
34263843Seric 					finis();
3434558Seric 				break;
3444558Seric 			}
3459339Seric 			if (InChild)
3469339Seric 			{
34736230Skarels 				errno = 0;
34858151Seric 				syserr("503 Nested MAIL command: MAIL %s", p);
34958069Seric 				finis();
3509339Seric 			}
3519339Seric 
3529339Seric 			/* fork a subprocess to process this command */
35355012Seric 			if (runinchild("SMTP-MAIL", e) > 0)
3549339Seric 				break;
35563753Seric 			if (!gothello)
35663753Seric 			{
35763753Seric 				auth_warning(e,
35866005Seric 					"Host %s didn't use HELO protocol",
359*69788Seric 					CurSmtpClient);
36063753Seric 			}
36165947Seric #ifdef PICKY_HELO_CHECK
36266005Seric 			if (strcasecmp(sendinghost, peerhostname) != 0 &&
36366005Seric 			    (strcasecmp(peerhostname, "localhost") != 0 ||
36465823Seric 			     strcasecmp(sendinghost, MyHostName) != 0))
36565823Seric 			{
36665823Seric 				auth_warning(e, "Host %s claimed to be %s",
367*69788Seric 					CurSmtpClient, sendinghost);
36865823Seric 			}
36965947Seric #endif
37065823Seric 
37158323Seric 			if (protocol == NULL)
37258323Seric 				protocol = "SMTP";
37358323Seric 			define('r', protocol, e);
37459016Seric 			define('s', sendinghost, e);
37555012Seric 			initsys(e);
37659747Seric 			nrcpts = 0;
37768582Seric 			e->e_flags |= EF_LOGSENDER|EF_CLRQUEUE;
37865058Seric 			setproctitle("%s %s: %.80s", e->e_id, CurSmtpClient, inp);
3799339Seric 
3809339Seric 			/* child -- go do the processing */
3814549Seric 			p = skipword(p, "from");
3824549Seric 			if (p == NULL)
3834549Seric 				break;
38457977Seric 			if (setjmp(TopFrame) > 0)
38558147Seric 			{
38658147Seric 				/* this failed -- undo work */
38758147Seric 				if (InChild)
38859058Seric 				{
38959058Seric 					QuickAbort = FALSE;
39059058Seric 					SuprErrs = TRUE;
39163787Seric 					e->e_flags &= ~EF_FATALERRS;
39258147Seric 					finis();
39359058Seric 				}
39457977Seric 				break;
39558147Seric 			}
39657977Seric 			QuickAbort = TRUE;
39758333Seric 
39858333Seric 			/* must parse sender first */
39958333Seric 			delimptr = NULL;
40058704Seric 			setsender(p, e, &delimptr, FALSE);
40158333Seric 			p = delimptr;
40258333Seric 			if (p != NULL && *p != '\0')
40358333Seric 				*p++ = '\0';
40458333Seric 
40566325Seric 			/* check for possible spoofing */
40666325Seric 			if (RealUid != 0 && OpMode == MD_SMTP &&
40769757Seric 			    !wordinclass(RealUserName, 't') &&
40867473Seric 			    !bitnset(M_LOCALMAILER, e->e_from.q_mailer->m_flags) &&
40967473Seric 			    strcmp(e->e_from.q_user, RealUserName) != 0)
41066325Seric 			{
41166325Seric 				auth_warning(e, "%s owned process doing -bs",
41266325Seric 					RealUserName);
41366325Seric 			}
41466325Seric 
41558333Seric 			/* now parse ESMTP arguments */
41668560Seric 			e->e_msgsize = 0;
41766764Seric 			while (p != NULL && *p != '\0')
41858333Seric 			{
41958333Seric 				char *kp;
42066304Seric 				char *vp = NULL;
42169748Seric 				extern void mail_esmtp_args __P((char *, char *, ENVELOPE *));
42258333Seric 
42358333Seric 				/* locate the beginning of the keyword */
42458333Seric 				while (isascii(*p) && isspace(*p))
42558333Seric 					p++;
42658333Seric 				if (*p == '\0')
42758333Seric 					break;
42858333Seric 				kp = p;
42958333Seric 
43058333Seric 				/* skip to the value portion */
43158333Seric 				while (isascii(*p) && isalnum(*p) || *p == '-')
43258333Seric 					p++;
43358333Seric 				if (*p == '=')
43458333Seric 				{
43558333Seric 					*p++ = '\0';
43658333Seric 					vp = p;
43758333Seric 
43858333Seric 					/* skip to the end of the value */
43958333Seric 					while (*p != '\0' && *p != ' ' &&
44058333Seric 					       !(isascii(*p) && iscntrl(*p)) &&
44158333Seric 					       *p != '=')
44258333Seric 						p++;
44358333Seric 				}
44458333Seric 
44558333Seric 				if (*p != '\0')
44658333Seric 					*p++ = '\0';
44758333Seric 
44858333Seric 				if (tTd(19, 1))
44966764Seric 					printf("MAIL: got arg %s=\"%s\"\n", kp,
45058333Seric 						vp == NULL ? "<null>" : vp);
45158333Seric 
45268559Seric 				mail_esmtp_args(kp, vp, e);
45358333Seric 			}
45459284Seric 
45568560Seric 			if (MaxMessageSize > 0 && e->e_msgsize > MaxMessageSize)
45659284Seric 			{
45759284Seric 				usrerr("552 Message size exceeds fixed maximum message size (%ld)",
45859284Seric 					MaxMessageSize);
45959284Seric 				/* NOTREACHED */
46059284Seric 			}
46158333Seric 
46268560Seric 			if (!enoughspace(e->e_msgsize))
46358333Seric 			{
46458333Seric 				message("452 Insufficient disk space; try again later");
46558333Seric 				break;
46658333Seric 			}
46758151Seric 			message("250 Sender ok");
46858147Seric 			gotmail = TRUE;
4694549Seric 			break;
4704549Seric 
4714976Seric 		  case CMDRCPT:		/* rcpt -- designate recipient */
47258850Seric 			if (!gotmail)
47358850Seric 			{
47458850Seric 				usrerr("503 Need MAIL before RCPT");
47558850Seric 				break;
47658850Seric 			}
47761093Seric 			SmtpPhase = "server RCPT";
47812612Seric 			if (setjmp(TopFrame) > 0)
47914785Seric 			{
48055012Seric 				e->e_flags &= ~EF_FATALERRS;
48112612Seric 				break;
48214785Seric 			}
48312612Seric 			QuickAbort = TRUE;
48451951Seric 			LogUsrErrs = TRUE;
48558093Seric 
48659699Seric 			if (e->e_sendmode != SM_DELIVER)
48759699Seric 				e->e_flags |= EF_VRFYONLY;
48858919Seric 
4894549Seric 			p = skipword(p, "to");
4904549Seric 			if (p == NULL)
4914549Seric 				break;
49267880Seric 			a = parseaddr(p, NULLADDR, RF_COPYALL, ' ', &delimptr, e);
49312612Seric 			if (a == NULL)
49412612Seric 				break;
49567880Seric 			p = delimptr;
49667880Seric 
49767880Seric 			/* now parse ESMTP arguments */
49867880Seric 			while (p != NULL && *p != '\0')
49967880Seric 			{
50067880Seric 				char *kp;
50167880Seric 				char *vp = NULL;
50269748Seric 				extern void rcpt_esmtp_args __P((ADDRESS *, char *, char *, ENVELOPE *));
50367880Seric 
50467880Seric 				/* locate the beginning of the keyword */
50567880Seric 				while (isascii(*p) && isspace(*p))
50667880Seric 					p++;
50767880Seric 				if (*p == '\0')
50867880Seric 					break;
50967880Seric 				kp = p;
51067880Seric 
51167880Seric 				/* skip to the value portion */
51267880Seric 				while (isascii(*p) && isalnum(*p) || *p == '-')
51367880Seric 					p++;
51467880Seric 				if (*p == '=')
51567880Seric 				{
51667880Seric 					*p++ = '\0';
51767880Seric 					vp = p;
51867880Seric 
51967880Seric 					/* skip to the end of the value */
52067880Seric 					while (*p != '\0' && *p != ' ' &&
52167880Seric 					       !(isascii(*p) && iscntrl(*p)) &&
52267880Seric 					       *p != '=')
52367880Seric 						p++;
52467880Seric 				}
52567880Seric 
52667880Seric 				if (*p != '\0')
52767880Seric 					*p++ = '\0';
52867880Seric 
52967880Seric 				if (tTd(19, 1))
53067880Seric 					printf("RCPT: got arg %s=\"%s\"\n", kp,
53167880Seric 						vp == NULL ? "<null>" : vp);
53267880Seric 
53367963Seric 				rcpt_esmtp_args(a, kp, vp, e);
53467880Seric 			}
53567980Seric 
53667980Seric 			/* save in recipient list after ESMTP mods */
53767982Seric 			a = recipient(a, &e->e_sendqueue, 0, e);
53867980Seric 
53912612Seric 			if (Errors != 0)
54012612Seric 				break;
54112612Seric 
54212612Seric 			/* no errors during parsing, but might be a duplicate */
54355012Seric 			e->e_to = p;
54412612Seric 			if (!bitset(QBADADDR, a->q_flags))
54559747Seric 			{
54664718Seric 				message("250 Recipient ok%s",
54764718Seric 					bitset(QQUEUEUP, a->q_flags) ?
54864718Seric 						" (will queue)" : "");
54959747Seric 				nrcpts++;
55059747Seric 			}
55112612Seric 			else
5524549Seric 			{
55312612Seric 				/* punt -- should keep message in ADDRESS.... */
55458151Seric 				message("550 Addressee unknown");
5554549Seric 			}
55655012Seric 			e->e_to = NULL;
5574549Seric 			break;
5584549Seric 
5594549Seric 		  case CMDDATA:		/* data -- text of mail */
56061093Seric 			SmtpPhase = "server DATA";
56158109Seric 			if (!gotmail)
5624549Seric 			{
56358151Seric 				message("503 Need MAIL command");
5644976Seric 				break;
5654549Seric 			}
56664718Seric 			else if (nrcpts <= 0)
5674549Seric 			{
56858151Seric 				message("503 Need RCPT (recipient)");
5694976Seric 				break;
5704549Seric 			}
5714976Seric 
57258929Seric 			/* check to see if we need to re-expand aliases */
57363787Seric 			/* also reset QBADADDR on already-diagnosted addrs */
57463787Seric 			doublequeue = FALSE;
57558929Seric 			for (a = e->e_sendqueue; a != NULL; a = a->q_next)
57658929Seric 			{
57758929Seric 				if (bitset(QVERIFIED, a->q_flags))
57863787Seric 				{
57963787Seric 					/* need to re-expand aliases */
58063787Seric 					doublequeue = TRUE;
58163787Seric 				}
58263787Seric 				if (bitset(QBADADDR, a->q_flags))
58363787Seric 				{
58463787Seric 					/* make this "go away" */
58563787Seric 					a->q_flags |= QDONTSEND;
58663787Seric 					a->q_flags &= ~QBADADDR;
58763787Seric 				}
58858929Seric 			}
58958929Seric 
5904976Seric 			/* collect the text of the message */
59124943Seric 			SmtpPhase = "collect";
59268692Seric 			buffer_errors();
59367546Seric 			collect(InChannel, TRUE, doublequeue, NULL, e);
59468692Seric 			flush_errors(TRUE);
59564766Seric 			if (Errors != 0)
59664766Seric 				goto abortmessage;
59767131Seric 
59868582Seric 			/* make sure we actually do delivery */
59968582Seric 			e->e_flags &= ~EF_CLRQUEUE;
60068582Seric 
60167131Seric 			/* from now on, we have to operate silently */
60268692Seric 			buffer_errors();
60367131Seric 			e->e_errormode = EM_MAIL;
6044976Seric 
6058238Seric 			/*
6068238Seric 			**  Arrange to send to everyone.
6078238Seric 			**	If sending to multiple people, mail back
6088238Seric 			**		errors rather than reporting directly.
6098238Seric 			**	In any case, don't mail back errors for
6108238Seric 			**		anything that has happened up to
6118238Seric 			**		now (the other end will do this).
61210197Seric 			**	Truncate our transcript -- the mail has gotten
61310197Seric 			**		to us successfully, and if we have
61410197Seric 			**		to mail this back, it will be easier
61510197Seric 			**		on the reader.
6168238Seric 			**	Then send to everyone.
6178238Seric 			**	Finally give a reply code.  If an error has
6188238Seric 			**		already been given, don't mail a
6198238Seric 			**		message back.
6209339Seric 			**	We goose error returns by clearing error bit.
6218238Seric 			*/
6228238Seric 
62324943Seric 			SmtpPhase = "delivery";
62455012Seric 			e->e_xfp = freopen(queuename(e, 'x'), "w", e->e_xfp);
62558714Seric 			id = e->e_id;
6264976Seric 
62767131Seric 			if (doublequeue)
62859730Seric 			{
62967131Seric 				/* make sure it is in the queue */
63067131Seric 				queueup(e, TRUE, FALSE);
63168035Seric 				if (e->e_sendmode == SM_QUEUE)
63268035Seric 					e->e_flags |= EF_KEEPQUEUE;
63359730Seric 			}
6348238Seric 			else
63558919Seric 			{
63667131Seric 				/* send to all recipients */
63767131Seric 				sendall(e, SM_DEFAULT);
63867131Seric 			}
63967131Seric 			e->e_to = NULL;
64059747Seric 
64167131Seric 			/* issue success message */
64267131Seric 			message("250 %s Message accepted for delivery", id);
64364296Seric 
64467131Seric 			/* if we just queued, poke it */
64567131Seric 			if (doublequeue && e->e_sendmode != SM_QUEUE)
64667131Seric 			{
64767131Seric 				extern pid_t dowork();
64867131Seric 
64967131Seric 				unlockqueue(e);
65067131Seric 				(void) dowork(id, TRUE, TRUE, e);
65158919Seric 			}
65258883Seric 
65359747Seric   abortmessage:
6549339Seric 			/* if in a child, pop back to our parent */
6559339Seric 			if (InChild)
6569339Seric 				finis();
65724943Seric 
65824943Seric 			/* clean up a bit */
65958109Seric 			gotmail = FALSE;
66055012Seric 			dropenvelope(e);
66158179Seric 			CurEnv = e = newenvelope(e, CurEnv);
66255012Seric 			e->e_flags = BlankEnvelope.e_flags;
6634549Seric 			break;
6644549Seric 
6654549Seric 		  case CMDRSET:		/* rset -- reset state */
66658151Seric 			message("250 Reset state");
66768603Seric 
66868603Seric 			/* arrange to ignore any current send list */
66968603Seric 			e->e_sendqueue = NULL;
67064359Seric 			e->e_flags |= EF_CLRQUEUE;
6719339Seric 			if (InChild)
6729339Seric 				finis();
67358109Seric 
67458109Seric 			/* clean up a bit */
67558109Seric 			gotmail = FALSE;
67658109Seric 			dropenvelope(e);
67758179Seric 			CurEnv = e = newenvelope(e, CurEnv);
6789339Seric 			break;
6794549Seric 
6804549Seric 		  case CMDVRFY:		/* vrfy -- verify address */
68158092Seric 		  case CMDEXPN:		/* expn -- expand address */
68258092Seric 			vrfy = c->cmdcode == CMDVRFY;
68358092Seric 			if (bitset(vrfy ? PRIV_NOVRFY : PRIV_NOEXPN,
68458092Seric 						PrivacyFlags))
68558082Seric 			{
68658412Seric 				if (vrfy)
68767160Seric 					message("252 Cannot VRFY user; try RCPT to attempt delivery (or try finger)");
68858412Seric 				else
68965192Seric 					message("502 Sorry, we do not allow this operation");
69065017Seric #ifdef LOG
69165017Seric 				if (LogLevel > 5)
69265017Seric 					syslog(LOG_INFO, "%s: %s [rejected]",
69365017Seric 						CurSmtpClient, inp);
69465017Seric #endif
69558082Seric 				break;
69658082Seric 			}
69758082Seric 			else if (!gothello &&
69858092Seric 				 bitset(vrfy ? PRIV_NEEDVRFYHELO : PRIV_NEEDEXPNHELO,
69958092Seric 						PrivacyFlags))
70058082Seric 			{
70158151Seric 				message("503 I demand that you introduce yourself first");
70258082Seric 				break;
70358082Seric 			}
70458092Seric 			if (runinchild(vrfy ? "SMTP-VRFY" : "SMTP-EXPN", e) > 0)
7059339Seric 				break;
70655173Seric #ifdef LOG
70758020Seric 			if (LogLevel > 5)
70865017Seric 				syslog(LOG_INFO, "%s: %s", CurSmtpClient, inp);
70955173Seric #endif
7105003Seric 			vrfyqueue = NULL;
7117762Seric 			QuickAbort = TRUE;
71258092Seric 			if (vrfy)
71358092Seric 				e->e_flags |= EF_VRFYONLY;
71462373Seric 			while (*p != '\0' && isascii(*p) && isspace(*p))
71567615Seric 				p++;
71662373Seric 			if (*p == '\0')
71762373Seric 			{
71862373Seric 				message("501 Argument required");
71962373Seric 				Errors++;
72062373Seric 			}
72162373Seric 			else
72262373Seric 			{
72367990Seric 				(void) sendtolist(p, NULLADDR, &vrfyqueue, 0, e);
72462373Seric 			}
7257762Seric 			if (Errors != 0)
7269339Seric 			{
7279339Seric 				if (InChild)
7289339Seric 					finis();
7297762Seric 				break;
7309339Seric 			}
73162373Seric 			if (vrfyqueue == NULL)
73262373Seric 			{
73362373Seric 				message("554 Nothing to %s", vrfy ? "VRFY" : "EXPN");
73462373Seric 			}
7355003Seric 			while (vrfyqueue != NULL)
7365003Seric 			{
73769748Seric 				extern void printvrfyaddr __P((ADDRESS *, bool));
73869748Seric 
73963971Seric 				a = vrfyqueue;
74063971Seric 				while ((a = a->q_next) != NULL &&
74163971Seric 				       bitset(QDONTSEND|QBADADDR, a->q_flags))
74263971Seric 					continue;
7437685Seric 				if (!bitset(QDONTSEND|QBADADDR, vrfyqueue->q_flags))
74458151Seric 					printvrfyaddr(vrfyqueue, a == NULL);
74563847Seric 				vrfyqueue = vrfyqueue->q_next;
7465003Seric 			}
7479339Seric 			if (InChild)
7489339Seric 				finis();
7494549Seric 			break;
7504549Seric 
7514549Seric 		  case CMDHELP:		/* help -- give user info */
7524577Seric 			help(p);
7534549Seric 			break;
7544549Seric 
7554549Seric 		  case CMDNOOP:		/* noop -- do nothing */
75664122Seric 			message("250 OK");
7574549Seric 			break;
7584549Seric 
7594549Seric 		  case CMDQUIT:		/* quit -- leave mail */
76058151Seric 			message("221 %s closing connection", MyHostName);
76161051Seric 
76266283Seric doquit:
76368603Seric 			/* arrange to ignore any current send list */
76468603Seric 			e->e_sendqueue = NULL;
76568603Seric 
76661051Seric 			/* avoid future 050 messages */
76766017Seric 			disconnect(1, e);
76861051Seric 
7699339Seric 			if (InChild)
7709339Seric 				ExitStat = EX_QUIT;
7714549Seric 			finis();
7724549Seric 
7738544Seric 		  case CMDVERB:		/* set verbose mode */
77459957Seric 			if (bitset(PRIV_NOEXPN, PrivacyFlags))
77559957Seric 			{
77659957Seric 				/* this would give out the same info */
77759957Seric 				message("502 Verbose unavailable");
77859957Seric 				break;
77959957Seric 			}
7808544Seric 			Verbose = TRUE;
78158734Seric 			e->e_sendmode = SM_DELIVER;
78259957Seric 			message("250 Verbose mode");
7838544Seric 			break;
7848544Seric 
7859314Seric 		  case CMDONEX:		/* doing one transaction only */
7869378Seric 			OneXact = TRUE;
78759957Seric 			message("250 Only one transaction");
7889314Seric 			break;
7899314Seric 
79036230Skarels # ifdef SMTPDEBUG
7919339Seric 		  case CMDDBGQSHOW:	/* show queues */
7926907Seric 			printf("Send Queue=");
79355012Seric 			printaddr(e->e_sendqueue, TRUE);
7945003Seric 			break;
7957275Seric 
7967275Seric 		  case CMDDBGDEBUG:	/* set debug mode */
7977676Seric 			tTsetup(tTdvect, sizeof tTdvect, "0-99.1");
7987676Seric 			tTflag(p);
79958151Seric 			message("200 Debug set");
8007275Seric 			break;
8017275Seric 
80236230Skarels # else /* not SMTPDEBUG */
80336230Skarels 		  case CMDDBGQSHOW:	/* show queues */
80436230Skarels 		  case CMDDBGDEBUG:	/* set debug mode */
80564685Seric # endif /* SMTPDEBUG */
80664685Seric 		  case CMDLOGBOGUS:	/* bogus command */
80736233Skarels # ifdef LOG
80858308Seric 			if (LogLevel > 0)
80964685Seric 				syslog(LOG_CRIT,
81058020Seric 				    "\"%s\" command from %s (%s)",
811*69788Seric 				    c->cmdname, CurSmtpClient,
81258755Seric 				    anynet_ntoa(&RealHostAddr));
81336233Skarels # endif
81436230Skarels 			/* FALL THROUGH */
81536230Skarels 
8164549Seric 		  case CMDERROR:	/* unknown command */
81766283Seric 			if (++badcommands > MAXBADCOMMANDS)
81866283Seric 			{
81966283Seric 				message("421 %s Too many bad commands; closing connection",
82066283Seric 					MyHostName);
82166283Seric 				goto doquit;
82266283Seric 			}
82366283Seric 
82458151Seric 			message("500 Command unrecognized");
8254549Seric 			break;
8264549Seric 
8274549Seric 		  default:
82836230Skarels 			errno = 0;
82958151Seric 			syserr("500 smtp: unknown code %d", c->cmdcode);
8304549Seric 			break;
8314549Seric 		}
8324549Seric 	}
8334549Seric }
8344549Seric /*
8354549Seric **  SKIPWORD -- skip a fixed word.
8364549Seric **
8374549Seric **	Parameters:
8384549Seric **		p -- place to start looking.
8394549Seric **		w -- word to skip.
8404549Seric **
8414549Seric **	Returns:
8424549Seric **		p following w.
8434549Seric **		NULL on error.
8444549Seric **
8454549Seric **	Side Effects:
8464549Seric **		clobbers the p data area.
8474549Seric */
8484549Seric 
8494549Seric static char *
8504549Seric skipword(p, w)
8514549Seric 	register char *p;
8524549Seric 	char *w;
8534549Seric {
8544549Seric 	register char *q;
85566005Seric 	char *firstp = p;
8564549Seric 
8574549Seric 	/* find beginning of word */
85858050Seric 	while (isascii(*p) && isspace(*p))
8594549Seric 		p++;
8604549Seric 	q = p;
8614549Seric 
8624549Seric 	/* find end of word */
86358050Seric 	while (*p != '\0' && *p != ':' && !(isascii(*p) && isspace(*p)))
8644549Seric 		p++;
86558050Seric 	while (isascii(*p) && isspace(*p))
8664549Seric 		*p++ = '\0';
8674549Seric 	if (*p != ':')
8684549Seric 	{
8694549Seric 	  syntax:
87066005Seric 		message("501 Syntax error in parameters scanning \"%s\"",
87166005Seric 			firstp);
8724549Seric 		Errors++;
8734549Seric 		return (NULL);
8744549Seric 	}
8754549Seric 	*p++ = '\0';
87658050Seric 	while (isascii(*p) && isspace(*p))
8774549Seric 		p++;
8784549Seric 
87962373Seric 	if (*p == '\0')
88062373Seric 		goto syntax;
88162373Seric 
8824549Seric 	/* see if the input word matches desired word */
88333725Sbostic 	if (strcasecmp(q, w))
8844549Seric 		goto syntax;
8854549Seric 
8864549Seric 	return (p);
8874549Seric }
8884577Seric /*
88968559Seric **  MAIL_ESMTP_ARGS -- process ESMTP arguments from MAIL line
89068559Seric **
89168559Seric **	Parameters:
89268559Seric **		kp -- the parameter key.
89368559Seric **		vp -- the value of that parameter.
89468559Seric **		e -- the envelope.
89568559Seric **
89668559Seric **	Returns:
89768559Seric **		none.
89868559Seric */
89968559Seric 
90069748Seric void
90168559Seric mail_esmtp_args(kp, vp, e)
90268559Seric 	char *kp;
90368559Seric 	char *vp;
90468559Seric 	ENVELOPE *e;
90568559Seric {
90668559Seric 	if (strcasecmp(kp, "size") == 0)
90768559Seric 	{
90868559Seric 		if (vp == NULL)
90968559Seric 		{
91068559Seric 			usrerr("501 SIZE requires a value");
91168559Seric 			/* NOTREACHED */
91268559Seric 		}
91368890Seric # if defined(__STDC__) && !defined(BROKEN_ANSI_LIBRARY)
91468560Seric 		e->e_msgsize = strtoul(vp, (char **) NULL, 10);
91568559Seric # else
91668560Seric 		e->e_msgsize = strtol(vp, (char **) NULL, 10);
91768559Seric # endif
91868559Seric 	}
91968559Seric 	else if (strcasecmp(kp, "body") == 0)
92068559Seric 	{
92168559Seric 		if (vp == NULL)
92268559Seric 		{
92368559Seric 			usrerr("501 BODY requires a value");
92468559Seric 			/* NOTREACHED */
92568559Seric 		}
92668559Seric 		if (strcasecmp(vp, "8bitmime") == 0)
92768559Seric 		{
92868559Seric 			SevenBitInput = FALSE;
92968883Seric 			e->e_flags |= EF_NL_NOT_EOL;
93068559Seric 		}
93168559Seric 		else if (strcasecmp(vp, "7bit") == 0)
93268559Seric 		{
93368559Seric 			SevenBitInput = TRUE;
93468559Seric 		}
93568559Seric 		else
93668559Seric 		{
93768559Seric 			usrerr("501 Unknown BODY type %s",
93868559Seric 				vp);
93968559Seric 			/* NOTREACHED */
94068559Seric 		}
94168559Seric 		e->e_bodytype = newstr(vp);
94268559Seric 	}
94368559Seric 	else if (strcasecmp(kp, "envid") == 0)
94468559Seric 	{
94568559Seric 		if (vp == NULL)
94668559Seric 		{
94768559Seric 			usrerr("501 ENVID requires a value");
94868559Seric 			/* NOTREACHED */
94968559Seric 		}
95068583Seric 		if (!xtextok(vp))
95168583Seric 		{
95268583Seric 			usrerr("501 Syntax error in ENVID parameter value");
95368583Seric 			/* NOTREACHED */
95468583Seric 		}
95568583Seric 		if (e->e_envid != NULL)
95668583Seric 		{
95768583Seric 			usrerr("501 Duplicate ENVID parameter");
95868583Seric 			/* NOTREACHED */
95968583Seric 		}
96068559Seric 		e->e_envid = newstr(vp);
96168559Seric 	}
96268559Seric 	else if (strcasecmp(kp, "ret") == 0)
96368559Seric 	{
96468559Seric 		if (vp == NULL)
96568559Seric 		{
96668559Seric 			usrerr("501 RET requires a value");
96768559Seric 			/* NOTREACHED */
96868559Seric 		}
96968583Seric 		if (bitset(EF_RET_PARAM, e->e_flags))
97068583Seric 		{
97168583Seric 			usrerr("501 Duplicate RET parameter");
97268583Seric 			/* NOTREACHED */
97368583Seric 		}
97468559Seric 		e->e_flags |= EF_RET_PARAM;
97568559Seric 		if (strcasecmp(vp, "hdrs") == 0)
97668559Seric 			e->e_flags |= EF_NO_BODY_RETN;
97768559Seric 		else if (strcasecmp(vp, "full") != 0)
97868559Seric 		{
97968559Seric 			usrerr("501 Bad argument \"%s\" to RET", vp);
98068559Seric 			/* NOTREACHED */
98168559Seric 		}
98268559Seric 	}
98368559Seric 	else
98468559Seric 	{
98568559Seric 		usrerr("501 %s parameter unrecognized", kp);
98668559Seric 		/* NOTREACHED */
98768559Seric 	}
98868559Seric }
98968559Seric /*
99067963Seric **  RCPT_ESMTP_ARGS -- process ESMTP arguments from RCPT line
99167963Seric **
99267963Seric **	Parameters:
99367963Seric **		a -- the address corresponding to the To: parameter.
99467963Seric **		kp -- the parameter key.
99567963Seric **		vp -- the value of that parameter.
99667963Seric **		e -- the envelope.
99767963Seric **
99867963Seric **	Returns:
99967963Seric **		none.
100067963Seric */
100167963Seric 
100269748Seric void
100367963Seric rcpt_esmtp_args(a, kp, vp, e)
100467963Seric 	ADDRESS *a;
100567963Seric 	char *kp;
100667963Seric 	char *vp;
100767963Seric 	ENVELOPE *e;
100867963Seric {
100967963Seric 	if (strcasecmp(kp, "notify") == 0)
101067963Seric 	{
101167963Seric 		char *p;
101267963Seric 
101367963Seric 		if (vp == NULL)
101467963Seric 		{
101567963Seric 			usrerr("501 NOTIFY requires a value");
101667963Seric 			/* NOTREACHED */
101767963Seric 		}
101867963Seric 		a->q_flags &= ~(QPINGONSUCCESS|QPINGONFAILURE|QPINGONDELAY);
101968595Seric 		a->q_flags |= QHASNOTIFY;
102067963Seric 		if (strcasecmp(vp, "never") == 0)
102167963Seric 			return;
102267963Seric 		for (p = vp; p != NULL; vp = p)
102367963Seric 		{
102467963Seric 			p = strchr(p, ',');
102567963Seric 			if (p != NULL)
102667963Seric 				*p++ = '\0';
102767963Seric 			if (strcasecmp(vp, "success") == 0)
102867963Seric 				a->q_flags |= QPINGONSUCCESS;
102967963Seric 			else if (strcasecmp(vp, "failure") == 0)
103067963Seric 				a->q_flags |= QPINGONFAILURE;
103167963Seric 			else if (strcasecmp(vp, "delay") == 0)
103267963Seric 				a->q_flags |= QPINGONDELAY;
103367963Seric 			else
103467963Seric 			{
103567963Seric 				usrerr("501 Bad argument \"%s\"  to NOTIFY",
103667963Seric 					vp);
103767963Seric 				/* NOTREACHED */
103867963Seric 			}
103967963Seric 		}
104067963Seric 	}
104167963Seric 	else if (strcasecmp(kp, "orcpt") == 0)
104267963Seric 	{
104367963Seric 		if (vp == NULL)
104467963Seric 		{
104567963Seric 			usrerr("501 ORCPT requires a value");
104667963Seric 			/* NOTREACHED */
104767963Seric 		}
104868583Seric 		if (!xtextok(vp))
104968583Seric 		{
105068583Seric 			usrerr("501 Syntax error in ORCPT parameter value");
105168583Seric 			/* NOTREACHED */
105268583Seric 		}
105368583Seric 		if (a->q_orcpt != NULL)
105468583Seric 		{
105568583Seric 			usrerr("501 Duplicate ORCPT parameter");
105668583Seric 			/* NOTREACHED */
105768583Seric 		}
105867963Seric 		a->q_orcpt = newstr(vp);
105967963Seric 	}
106067963Seric 	else
106167963Seric 	{
106267963Seric 		usrerr("501 %s parameter unrecognized", kp);
106367963Seric 		/* NOTREACHED */
106467963Seric 	}
106567963Seric }
106667963Seric /*
106758151Seric **  PRINTVRFYADDR -- print an entry in the verify queue
106858151Seric **
106958151Seric **	Parameters:
107058151Seric **		a -- the address to print
107158151Seric **		last -- set if this is the last one.
107258151Seric **
107358151Seric **	Returns:
107458151Seric **		none.
107558151Seric **
107658151Seric **	Side Effects:
107758151Seric **		Prints the appropriate 250 codes.
107858151Seric */
107958151Seric 
108069748Seric void
108158151Seric printvrfyaddr(a, last)
108258151Seric 	register ADDRESS *a;
108358151Seric 	bool last;
108458151Seric {
108558151Seric 	char fmtbuf[20];
108658151Seric 
108758151Seric 	strcpy(fmtbuf, "250");
108858151Seric 	fmtbuf[3] = last ? ' ' : '-';
108958151Seric 
109059746Seric 	if (a->q_fullname == NULL)
109159746Seric 	{
109259746Seric 		if (strchr(a->q_user, '@') == NULL)
109359746Seric 			strcpy(&fmtbuf[4], "<%s@%s>");
109459746Seric 		else
109559746Seric 			strcpy(&fmtbuf[4], "<%s>");
109659746Seric 		message(fmtbuf, a->q_user, MyHostName);
109759746Seric 	}
109858151Seric 	else
109958151Seric 	{
110059746Seric 		if (strchr(a->q_user, '@') == NULL)
110159746Seric 			strcpy(&fmtbuf[4], "%s <%s@%s>");
110259746Seric 		else
110359746Seric 			strcpy(&fmtbuf[4], "%s <%s>");
110459746Seric 		message(fmtbuf, a->q_fullname, a->q_user, MyHostName);
110558151Seric 	}
110658151Seric }
110758151Seric /*
11084577Seric **  HELP -- implement the HELP command.
11094577Seric **
11104577Seric **	Parameters:
11114577Seric **		topic -- the topic we want help for.
11124577Seric **
11134577Seric **	Returns:
11144577Seric **		none.
11154577Seric **
11164577Seric **	Side Effects:
11174577Seric **		outputs the help file to message output.
11184577Seric */
11194577Seric 
112069748Seric void
11214577Seric help(topic)
11224577Seric 	char *topic;
11234577Seric {
11244577Seric 	register FILE *hf;
11254577Seric 	int len;
112668751Seric 	bool noinfo;
11274577Seric 	char buf[MAXLINE];
112868751Seric 	extern char Version[];
11294577Seric 
113068751Seric 
11318269Seric 	if (HelpFile == NULL || (hf = fopen(HelpFile, "r")) == NULL)
11324577Seric 	{
11334577Seric 		/* no help */
113411931Seric 		errno = 0;
113568751Seric 		message("502 Sendmail %s -- HELP not implemented", Version);
11364577Seric 		return;
11374577Seric 	}
11384577Seric 
113949669Seric 	if (topic == NULL || *topic == '\0')
114068751Seric 	{
114149669Seric 		topic = "smtp";
114268751Seric 		message("214-This is Sendmail version %s", Version);
114368751Seric 		noinfo = FALSE;
114468751Seric 	}
114549669Seric 	else
114668751Seric 	{
114749669Seric 		makelower(topic);
114868751Seric 		noinfo = TRUE;
114968751Seric 	}
115049669Seric 
11514577Seric 	len = strlen(topic);
11524577Seric 
11534577Seric 	while (fgets(buf, sizeof buf, hf) != NULL)
11544577Seric 	{
11554577Seric 		if (strncmp(buf, topic, len) == 0)
11564577Seric 		{
11574577Seric 			register char *p;
11584577Seric 
115956795Seric 			p = strchr(buf, '\t');
11604577Seric 			if (p == NULL)
11614577Seric 				p = buf;
11624577Seric 			else
11634577Seric 				p++;
11644577Seric 			fixcrlf(p, TRUE);
116558151Seric 			message("214-%s", p);
11664577Seric 			noinfo = FALSE;
11674577Seric 		}
11684577Seric 	}
11694577Seric 
11704577Seric 	if (noinfo)
117158151Seric 		message("504 HELP topic unknown");
11724577Seric 	else
117358151Seric 		message("214 End of HELP info");
11744628Seric 	(void) fclose(hf);
11754577Seric }
11768544Seric /*
11779339Seric **  RUNINCHILD -- return twice -- once in the child, then in the parent again
11789339Seric **
11799339Seric **	Parameters:
11809339Seric **		label -- a string used in error messages
11819339Seric **
11829339Seric **	Returns:
11839339Seric **		zero in the child
11849339Seric **		one in the parent
11859339Seric **
11869339Seric **	Side Effects:
11879339Seric **		none.
11889339Seric */
11898544Seric 
119069748Seric int
119155012Seric runinchild(label, e)
11929339Seric 	char *label;
119355012Seric 	register ENVELOPE *e;
11949339Seric {
11959339Seric 	int childpid;
11969339Seric 
119716158Seric 	if (!OneXact)
11989339Seric 	{
119916158Seric 		childpid = dofork();
120016158Seric 		if (childpid < 0)
120116158Seric 		{
120269716Seric 			syserr("451 %s: cannot fork", label);
120316158Seric 			return (1);
120416158Seric 		}
120516158Seric 		if (childpid > 0)
120616158Seric 		{
120716158Seric 			auto int st;
12089339Seric 
120916158Seric 			/* parent -- wait for child to complete */
1210*69788Seric 			setproctitle("server %s child wait", CurSmtpClient);
121116158Seric 			st = waitfor(childpid);
121216158Seric 			if (st == -1)
121369716Seric 				syserr("451 %s: lost child", label);
121464948Seric 			else if (!WIFEXITED(st))
121569716Seric 				syserr("451 %s: died on signal %d",
121664948Seric 					label, st & 0177);
12179339Seric 
121816158Seric 			/* if we exited on a QUIT command, complete the process */
121966017Seric 			if (WEXITSTATUS(st) == EX_QUIT)
122066017Seric 			{
122166017Seric 				disconnect(1, e);
122216158Seric 				finis();
122366017Seric 			}
12249339Seric 
122516158Seric 			return (1);
122616158Seric 		}
122716158Seric 		else
122816158Seric 		{
122916158Seric 			/* child */
123016158Seric 			InChild = TRUE;
123125050Seric 			QuickAbort = FALSE;
123255012Seric 			clearenvelope(e, FALSE);
123316158Seric 		}
12349339Seric 	}
123515256Seric 
123616158Seric 	/* open alias database */
123760537Seric 	initmaps(FALSE, e);
123816158Seric 
123916158Seric 	return (0);
12409339Seric }
12419339Seric 
124256795Seric # endif /* SMTP */
1243