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*69757Seric static char sccsid[] = "@(#)srvrsmtp.c	8.76 (Berkeley) 05/29/95 (with SMTP)";
1433731Sbostic #else
15*69757Seric static char sccsid[] = "@(#)srvrsmtp.c	8.76 (Berkeley) 05/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 
9969748Seric void
10055012Seric smtp(e)
10155012Seric 	register ENVELOPE *e;
1024549Seric {
1034549Seric 	register char *p;
1048544Seric 	register struct cmd *c;
1054549Seric 	char *cmd;
1065003Seric 	auto ADDRESS *vrfyqueue;
10712612Seric 	ADDRESS *a;
10858109Seric 	bool gotmail;			/* mail command received */
10958092Seric 	bool gothello;			/* helo command received */
11058092Seric 	bool vrfy;			/* set if this is a vrfy command */
11158323Seric 	char *protocol;			/* sending protocol */
11259016Seric 	char *sendinghost;		/* sending hostname */
11366005Seric 	char *peerhostname;		/* name of SMTP peer or "localhost" */
11458333Seric 	auto char *delimptr;
11558714Seric 	char *id;
11668433Seric 	int nrcpts = 0;			/* number of RCPT commands */
11763787Seric 	bool doublequeue;
11866283Seric 	int badcommands = 0;		/* count of bad commands */
1198544Seric 	char inp[MAXLINE];
12057232Seric 	char cmdbuf[MAXLINE];
12124943Seric 	extern ENVELOPE BlankEnvelope;
12269748Seric 	extern void help __P((char *));
1234549Seric 
12459066Seric 	if (fileno(OutChannel) != fileno(stdout))
1257363Seric 	{
1267363Seric 		/* arrange for debugging output to go to remote host */
12759066Seric 		(void) dup2(fileno(OutChannel), fileno(stdout));
1287363Seric 	}
12955012Seric 	settime(e);
13066005Seric 	peerhostname = RealHostName;
13166005Seric 	if (peerhostname == NULL)
13266005Seric 		peerhostname = "localhost";
13366005Seric 	CurHostName = peerhostname;
13465017Seric 	CurSmtpClient = macvalue('_', e);
13565017Seric 	if (CurSmtpClient == NULL)
13666003Seric 		CurSmtpClient = CurHostName;
13765017Seric 
13865017Seric 	setproctitle("server %s startup", CurSmtpClient);
13968529Seric 	expand("\201e", inp, sizeof inp, e);
14068769Seric 
14168769Seric 	/* output the first line, inserting "ESMTP" as second word */
14268769Seric 	p = strchr(inp, '\n');
14368769Seric 	if (p != NULL)
14468769Seric 		*p++ = '\0';
14568769Seric 	id = strchr(inp, ' ');
14668769Seric 	if (id == NULL)
14768769Seric 		id = &inp[strlen(inp)];
14868769Seric 	cmd = p == NULL ? "220 %.*s ESMTP%s" : "220-%.*s ESMTP%s";
14968769Seric 	message(cmd, id - inp, inp, id);
15068769Seric 
15168769Seric 	/* output remaining lines */
15268769Seric 	while ((id = p) != NULL && (p = strchr(id, '\n')) != NULL)
15364496Seric 	{
15468769Seric 		*p++ = '\0';
15569106Seric 		if (isascii(*id) && isspace(*id))
15669106Seric 			id++;
15768769Seric 		message("220-%s", id);
15864496Seric 	}
15968769Seric 	if (id != NULL)
16069106Seric 	{
16169106Seric 		if (isascii(*id) && isspace(*id))
16269106Seric 			id++;
16368769Seric 		message("220 %s", id);
16469106Seric 	}
16566745Seric 
16658330Seric 	protocol = NULL;
16759016Seric 	sendinghost = macvalue('s', e);
16858082Seric 	gothello = FALSE;
16958330Seric 	gotmail = FALSE;
1704549Seric 	for (;;)
1714549Seric 	{
17212612Seric 		/* arrange for backout */
17365751Seric 		if (setjmp(TopFrame) > 0)
17459058Seric 		{
17565751Seric 			/* if() nesting is necessary for Cray UNICOS */
17665751Seric 			if (InChild)
17765751Seric 			{
17865751Seric 				QuickAbort = FALSE;
17965751Seric 				SuprErrs = TRUE;
18065751Seric 				finis();
18165751Seric 			}
18259058Seric 		}
18312612Seric 		QuickAbort = FALSE;
18412612Seric 		HoldErrs = FALSE;
18551951Seric 		LogUsrErrs = FALSE;
18663843Seric 		e->e_flags &= ~(EF_VRFYONLY|EF_GLOBALERRS);
18712612Seric 
1887356Seric 		/* setup for the read */
18955012Seric 		e->e_to = NULL;
1904577Seric 		Errors = 0;
1917275Seric 		(void) fflush(stdout);
1927356Seric 
1937356Seric 		/* read the input line */
19461093Seric 		SmtpPhase = "server cmd read";
19561093Seric 		setproctitle("server %s cmd read", CurHostName);
19661093Seric 		p = sfgets(inp, sizeof inp, InChannel, TimeOuts.to_nextcommand,
19761093Seric 				SmtpPhase);
1987356Seric 
1997685Seric 		/* handle errors */
2007356Seric 		if (p == NULL)
2017356Seric 		{
2024549Seric 			/* end of file, just die */
20366017Seric 			disconnect(1, e);
20458151Seric 			message("421 %s Lost input channel from %s",
20565017Seric 				MyHostName, CurSmtpClient);
20655464Seric #ifdef LOG
20763843Seric 			if (LogLevel > (gotmail ? 1 : 19))
20855464Seric 				syslog(LOG_NOTICE, "lost input channel from %s",
20965017Seric 					CurSmtpClient);
21055464Seric #endif
21158069Seric 			if (InChild)
21258069Seric 				ExitStat = EX_QUIT;
2134549Seric 			finis();
2144549Seric 		}
2154549Seric 
2164549Seric 		/* clean up end of line */
2174558Seric 		fixcrlf(inp, TRUE);
2184549Seric 
2194713Seric 		/* echo command to transcript */
22055012Seric 		if (e->e_xfp != NULL)
22155012Seric 			fprintf(e->e_xfp, "<<< %s\n", inp);
2224713Seric 
22359060Seric 		if (e->e_id == NULL)
22465058Seric 			setproctitle("%s: %.80s", CurSmtpClient, inp);
22559060Seric 		else
22665058Seric 			setproctitle("%s %s: %.80s", e->e_id, CurSmtpClient, inp);
22759060Seric 
2284549Seric 		/* break off command */
22958050Seric 		for (p = inp; isascii(*p) && isspace(*p); p++)
2304549Seric 			continue;
23157232Seric 		cmd = cmdbuf;
23258050Seric 		while (*p != '\0' &&
23358050Seric 		       !(isascii(*p) && isspace(*p)) &&
23458050Seric 		       cmd < &cmdbuf[sizeof cmdbuf - 2])
23524981Seric 			*cmd++ = *p++;
23624981Seric 		*cmd = '\0';
2374549Seric 
23825691Seric 		/* throw away leading whitespace */
23958050Seric 		while (isascii(*p) && isspace(*p))
24025691Seric 			p++;
24125691Seric 
2424549Seric 		/* decode command */
2434549Seric 		for (c = CmdTab; c->cmdname != NULL; c++)
2444549Seric 		{
24533725Sbostic 			if (!strcasecmp(c->cmdname, cmdbuf))
2464549Seric 				break;
2474549Seric 		}
2484549Seric 
24951954Seric 		/* reset errors */
25051954Seric 		errno = 0;
25151954Seric 
2524549Seric 		/* process command */
2534549Seric 		switch (c->cmdcode)
2544549Seric 		{
2554976Seric 		  case CMDHELO:		/* hello -- introduce yourself */
25658323Seric 		  case CMDEHLO:		/* extended hello */
25758323Seric 			if (c->cmdcode == CMDEHLO)
25858323Seric 			{
25958323Seric 				protocol = "ESMTP";
26061093Seric 				SmtpPhase = "server EHLO";
26158323Seric 			}
26258323Seric 			else
26358323Seric 			{
26458323Seric 				protocol = "SMTP";
26561093Seric 				SmtpPhase = "server HELO";
26658323Seric 			}
26767445Seric 
26867445Seric 			/* check for valid domain name (re 1123 5.2.5) */
26967445Seric 			if (*p == '\0')
27067445Seric 			{
27167445Seric 				message("501 %s requires domain address",
27267445Seric 					cmdbuf);
27367445Seric 				break;
27467445Seric 			}
27567445Seric 			else
27667445Seric 			{
27767445Seric 				register char *q;
27867445Seric 
27967445Seric 				for (q = p; *q != '\0'; q++)
28067445Seric 				{
28167445Seric 					if (!isascii(*q))
28267445Seric 						break;
28367445Seric 					if (isalnum(*q))
28467445Seric 						continue;
28567445Seric 					if (strchr("[].-_#", *q) == NULL)
28667445Seric 						break;
28767445Seric 				}
28867445Seric 				if (*q != '\0')
28967445Seric 				{
29067445Seric 					message("501 Invalid domain name");
29167445Seric 					break;
29267445Seric 				}
29367445Seric 			}
29467445Seric 
29559016Seric 			sendinghost = newstr(p);
29660210Seric 			gothello = TRUE;
29760210Seric 			if (c->cmdcode != CMDEHLO)
29860239Seric 			{
29960239Seric 				/* print old message and be done with it */
30060239Seric 				message("250 %s Hello %s, pleased to meet you",
30165017Seric 					MyHostName, CurSmtpClient);
30260210Seric 				break;
30360239Seric 			}
30460239Seric 
30560239Seric 			/* print extended message and brag */
30660239Seric 			message("250-%s Hello %s, pleased to meet you",
30766760Seric 				MyHostName, CurSmtpClient);
30858323Seric 			if (!bitset(PRIV_NOEXPN, PrivacyFlags))
30958323Seric 				message("250-EXPN");
31069480Seric #if MIME8TO7
31167417Seric 			message("250-8BITMIME");
31269480Seric #endif
31364359Seric 			if (MaxMessageSize > 0)
31464359Seric 				message("250-SIZE %ld", MaxMessageSize);
31559271Seric 			else
31659271Seric 				message("250-SIZE");
31768848Seric #if DSN
31869545Seric 			message("250-X-DSN-03 (Draft of 12 Mar 1995)");
31968028Seric #endif
32058323Seric 			message("250 HELP");
3214976Seric 			break;
3224976Seric 
3234549Seric 		  case CMDMAIL:		/* mail -- designate sender */
32461093Seric 			SmtpPhase = "server MAIL";
32524943Seric 
3269314Seric 			/* check for validity of this command */
32758789Seric 			if (!gothello)
32858082Seric 			{
32958957Seric 				/* set sending host to our known value */
33059016Seric 				if (sendinghost == NULL)
33166005Seric 					sendinghost = peerhostname;
33258957Seric 
33358789Seric 				if (bitset(PRIV_NEEDMAILHELO, PrivacyFlags))
33458821Seric 				{
33558789Seric 					message("503 Polite people say HELO first");
33658821Seric 					break;
33758821Seric 				}
33858082Seric 			}
33958109Seric 			if (gotmail)
3404558Seric 			{
34158151Seric 				message("503 Sender already specified");
34263843Seric 				if (InChild)
34363843Seric 					finis();
3444558Seric 				break;
3454558Seric 			}
3469339Seric 			if (InChild)
3479339Seric 			{
34836230Skarels 				errno = 0;
34958151Seric 				syserr("503 Nested MAIL command: MAIL %s", p);
35058069Seric 				finis();
3519339Seric 			}
3529339Seric 
3539339Seric 			/* fork a subprocess to process this command */
35455012Seric 			if (runinchild("SMTP-MAIL", e) > 0)
3559339Seric 				break;
35663753Seric 			if (!gothello)
35763753Seric 			{
35863753Seric 				auth_warning(e,
35966005Seric 					"Host %s didn't use HELO protocol",
36066005Seric 					peerhostname);
36163753Seric 			}
36265947Seric #ifdef PICKY_HELO_CHECK
36366005Seric 			if (strcasecmp(sendinghost, peerhostname) != 0 &&
36466005Seric 			    (strcasecmp(peerhostname, "localhost") != 0 ||
36565823Seric 			     strcasecmp(sendinghost, MyHostName) != 0))
36665823Seric 			{
36765823Seric 				auth_warning(e, "Host %s claimed to be %s",
36866005Seric 					peerhostname, sendinghost);
36965823Seric 			}
37065947Seric #endif
37165823Seric 
37258323Seric 			if (protocol == NULL)
37358323Seric 				protocol = "SMTP";
37458323Seric 			define('r', protocol, e);
37559016Seric 			define('s', sendinghost, e);
37655012Seric 			initsys(e);
37759747Seric 			nrcpts = 0;
37868582Seric 			e->e_flags |= EF_LOGSENDER|EF_CLRQUEUE;
37965058Seric 			setproctitle("%s %s: %.80s", e->e_id, CurSmtpClient, inp);
3809339Seric 
3819339Seric 			/* child -- go do the processing */
3824549Seric 			p = skipword(p, "from");
3834549Seric 			if (p == NULL)
3844549Seric 				break;
38557977Seric 			if (setjmp(TopFrame) > 0)
38658147Seric 			{
38758147Seric 				/* this failed -- undo work */
38858147Seric 				if (InChild)
38959058Seric 				{
39059058Seric 					QuickAbort = FALSE;
39159058Seric 					SuprErrs = TRUE;
39263787Seric 					e->e_flags &= ~EF_FATALERRS;
39358147Seric 					finis();
39459058Seric 				}
39557977Seric 				break;
39658147Seric 			}
39757977Seric 			QuickAbort = TRUE;
39858333Seric 
39958333Seric 			/* must parse sender first */
40058333Seric 			delimptr = NULL;
40158704Seric 			setsender(p, e, &delimptr, FALSE);
40258333Seric 			p = delimptr;
40358333Seric 			if (p != NULL && *p != '\0')
40458333Seric 				*p++ = '\0';
40558333Seric 
40666325Seric 			/* check for possible spoofing */
40766325Seric 			if (RealUid != 0 && OpMode == MD_SMTP &&
408*69757Seric 			    !wordinclass(RealUserName, 't') &&
40967473Seric 			    !bitnset(M_LOCALMAILER, e->e_from.q_mailer->m_flags) &&
41067473Seric 			    strcmp(e->e_from.q_user, RealUserName) != 0)
41166325Seric 			{
41266325Seric 				auth_warning(e, "%s owned process doing -bs",
41366325Seric 					RealUserName);
41466325Seric 			}
41566325Seric 
41658333Seric 			/* now parse ESMTP arguments */
41768560Seric 			e->e_msgsize = 0;
41866764Seric 			while (p != NULL && *p != '\0')
41958333Seric 			{
42058333Seric 				char *kp;
42166304Seric 				char *vp = NULL;
42269748Seric 				extern void mail_esmtp_args __P((char *, char *, ENVELOPE *));
42358333Seric 
42458333Seric 				/* locate the beginning of the keyword */
42558333Seric 				while (isascii(*p) && isspace(*p))
42658333Seric 					p++;
42758333Seric 				if (*p == '\0')
42858333Seric 					break;
42958333Seric 				kp = p;
43058333Seric 
43158333Seric 				/* skip to the value portion */
43258333Seric 				while (isascii(*p) && isalnum(*p) || *p == '-')
43358333Seric 					p++;
43458333Seric 				if (*p == '=')
43558333Seric 				{
43658333Seric 					*p++ = '\0';
43758333Seric 					vp = p;
43858333Seric 
43958333Seric 					/* skip to the end of the value */
44058333Seric 					while (*p != '\0' && *p != ' ' &&
44158333Seric 					       !(isascii(*p) && iscntrl(*p)) &&
44258333Seric 					       *p != '=')
44358333Seric 						p++;
44458333Seric 				}
44558333Seric 
44658333Seric 				if (*p != '\0')
44758333Seric 					*p++ = '\0';
44858333Seric 
44958333Seric 				if (tTd(19, 1))
45066764Seric 					printf("MAIL: got arg %s=\"%s\"\n", kp,
45158333Seric 						vp == NULL ? "<null>" : vp);
45258333Seric 
45368559Seric 				mail_esmtp_args(kp, vp, e);
45458333Seric 			}
45559284Seric 
45668560Seric 			if (MaxMessageSize > 0 && e->e_msgsize > MaxMessageSize)
45759284Seric 			{
45859284Seric 				usrerr("552 Message size exceeds fixed maximum message size (%ld)",
45959284Seric 					MaxMessageSize);
46059284Seric 				/* NOTREACHED */
46159284Seric 			}
46258333Seric 
46368560Seric 			if (!enoughspace(e->e_msgsize))
46458333Seric 			{
46558333Seric 				message("452 Insufficient disk space; try again later");
46658333Seric 				break;
46758333Seric 			}
46858151Seric 			message("250 Sender ok");
46958147Seric 			gotmail = TRUE;
4704549Seric 			break;
4714549Seric 
4724976Seric 		  case CMDRCPT:		/* rcpt -- designate recipient */
47358850Seric 			if (!gotmail)
47458850Seric 			{
47558850Seric 				usrerr("503 Need MAIL before RCPT");
47658850Seric 				break;
47758850Seric 			}
47861093Seric 			SmtpPhase = "server RCPT";
47912612Seric 			if (setjmp(TopFrame) > 0)
48014785Seric 			{
48155012Seric 				e->e_flags &= ~EF_FATALERRS;
48212612Seric 				break;
48314785Seric 			}
48412612Seric 			QuickAbort = TRUE;
48551951Seric 			LogUsrErrs = TRUE;
48658093Seric 
48759699Seric 			if (e->e_sendmode != SM_DELIVER)
48859699Seric 				e->e_flags |= EF_VRFYONLY;
48958919Seric 
4904549Seric 			p = skipword(p, "to");
4914549Seric 			if (p == NULL)
4924549Seric 				break;
49367880Seric 			a = parseaddr(p, NULLADDR, RF_COPYALL, ' ', &delimptr, e);
49412612Seric 			if (a == NULL)
49512612Seric 				break;
49667880Seric 			p = delimptr;
49767880Seric 
49867880Seric 			/* now parse ESMTP arguments */
49967880Seric 			while (p != NULL && *p != '\0')
50067880Seric 			{
50167880Seric 				char *kp;
50267880Seric 				char *vp = NULL;
50369748Seric 				extern void rcpt_esmtp_args __P((ADDRESS *, char *, char *, ENVELOPE *));
50467880Seric 
50567880Seric 				/* locate the beginning of the keyword */
50667880Seric 				while (isascii(*p) && isspace(*p))
50767880Seric 					p++;
50867880Seric 				if (*p == '\0')
50967880Seric 					break;
51067880Seric 				kp = p;
51167880Seric 
51267880Seric 				/* skip to the value portion */
51367880Seric 				while (isascii(*p) && isalnum(*p) || *p == '-')
51467880Seric 					p++;
51567880Seric 				if (*p == '=')
51667880Seric 				{
51767880Seric 					*p++ = '\0';
51867880Seric 					vp = p;
51967880Seric 
52067880Seric 					/* skip to the end of the value */
52167880Seric 					while (*p != '\0' && *p != ' ' &&
52267880Seric 					       !(isascii(*p) && iscntrl(*p)) &&
52367880Seric 					       *p != '=')
52467880Seric 						p++;
52567880Seric 				}
52667880Seric 
52767880Seric 				if (*p != '\0')
52867880Seric 					*p++ = '\0';
52967880Seric 
53067880Seric 				if (tTd(19, 1))
53167880Seric 					printf("RCPT: got arg %s=\"%s\"\n", kp,
53267880Seric 						vp == NULL ? "<null>" : vp);
53367880Seric 
53467963Seric 				rcpt_esmtp_args(a, kp, vp, e);
53567880Seric 			}
53667980Seric 
53767980Seric 			/* save in recipient list after ESMTP mods */
53867982Seric 			a = recipient(a, &e->e_sendqueue, 0, e);
53967980Seric 
54012612Seric 			if (Errors != 0)
54112612Seric 				break;
54212612Seric 
54312612Seric 			/* no errors during parsing, but might be a duplicate */
54455012Seric 			e->e_to = p;
54512612Seric 			if (!bitset(QBADADDR, a->q_flags))
54659747Seric 			{
54764718Seric 				message("250 Recipient ok%s",
54864718Seric 					bitset(QQUEUEUP, a->q_flags) ?
54964718Seric 						" (will queue)" : "");
55059747Seric 				nrcpts++;
55159747Seric 			}
55212612Seric 			else
5534549Seric 			{
55412612Seric 				/* punt -- should keep message in ADDRESS.... */
55558151Seric 				message("550 Addressee unknown");
5564549Seric 			}
55755012Seric 			e->e_to = NULL;
5584549Seric 			break;
5594549Seric 
5604549Seric 		  case CMDDATA:		/* data -- text of mail */
56161093Seric 			SmtpPhase = "server DATA";
56258109Seric 			if (!gotmail)
5634549Seric 			{
56458151Seric 				message("503 Need MAIL command");
5654976Seric 				break;
5664549Seric 			}
56764718Seric 			else if (nrcpts <= 0)
5684549Seric 			{
56958151Seric 				message("503 Need RCPT (recipient)");
5704976Seric 				break;
5714549Seric 			}
5724976Seric 
57358929Seric 			/* check to see if we need to re-expand aliases */
57463787Seric 			/* also reset QBADADDR on already-diagnosted addrs */
57563787Seric 			doublequeue = FALSE;
57658929Seric 			for (a = e->e_sendqueue; a != NULL; a = a->q_next)
57758929Seric 			{
57858929Seric 				if (bitset(QVERIFIED, a->q_flags))
57963787Seric 				{
58063787Seric 					/* need to re-expand aliases */
58163787Seric 					doublequeue = TRUE;
58263787Seric 				}
58363787Seric 				if (bitset(QBADADDR, a->q_flags))
58463787Seric 				{
58563787Seric 					/* make this "go away" */
58663787Seric 					a->q_flags |= QDONTSEND;
58763787Seric 					a->q_flags &= ~QBADADDR;
58863787Seric 				}
58958929Seric 			}
59058929Seric 
5914976Seric 			/* collect the text of the message */
59224943Seric 			SmtpPhase = "collect";
59368692Seric 			buffer_errors();
59467546Seric 			collect(InChannel, TRUE, doublequeue, NULL, e);
59568692Seric 			flush_errors(TRUE);
59664766Seric 			if (Errors != 0)
59764766Seric 				goto abortmessage;
59867131Seric 
59968582Seric 			/* make sure we actually do delivery */
60068582Seric 			e->e_flags &= ~EF_CLRQUEUE;
60168582Seric 
60267131Seric 			/* from now on, we have to operate silently */
60368692Seric 			buffer_errors();
60467131Seric 			e->e_errormode = EM_MAIL;
6054976Seric 
6068238Seric 			/*
6078238Seric 			**  Arrange to send to everyone.
6088238Seric 			**	If sending to multiple people, mail back
6098238Seric 			**		errors rather than reporting directly.
6108238Seric 			**	In any case, don't mail back errors for
6118238Seric 			**		anything that has happened up to
6128238Seric 			**		now (the other end will do this).
61310197Seric 			**	Truncate our transcript -- the mail has gotten
61410197Seric 			**		to us successfully, and if we have
61510197Seric 			**		to mail this back, it will be easier
61610197Seric 			**		on the reader.
6178238Seric 			**	Then send to everyone.
6188238Seric 			**	Finally give a reply code.  If an error has
6198238Seric 			**		already been given, don't mail a
6208238Seric 			**		message back.
6219339Seric 			**	We goose error returns by clearing error bit.
6228238Seric 			*/
6238238Seric 
62424943Seric 			SmtpPhase = "delivery";
62555012Seric 			e->e_xfp = freopen(queuename(e, 'x'), "w", e->e_xfp);
62658714Seric 			id = e->e_id;
6274976Seric 
62867131Seric 			if (doublequeue)
62959730Seric 			{
63067131Seric 				/* make sure it is in the queue */
63167131Seric 				queueup(e, TRUE, FALSE);
63268035Seric 				if (e->e_sendmode == SM_QUEUE)
63368035Seric 					e->e_flags |= EF_KEEPQUEUE;
63459730Seric 			}
6358238Seric 			else
63658919Seric 			{
63767131Seric 				/* send to all recipients */
63867131Seric 				sendall(e, SM_DEFAULT);
63967131Seric 			}
64067131Seric 			e->e_to = NULL;
64159747Seric 
64267131Seric 			/* issue success message */
64367131Seric 			message("250 %s Message accepted for delivery", id);
64464296Seric 
64567131Seric 			/* if we just queued, poke it */
64667131Seric 			if (doublequeue && e->e_sendmode != SM_QUEUE)
64767131Seric 			{
64867131Seric 				extern pid_t dowork();
64967131Seric 
65067131Seric 				unlockqueue(e);
65167131Seric 				(void) dowork(id, TRUE, TRUE, e);
65258919Seric 			}
65358883Seric 
65459747Seric   abortmessage:
6559339Seric 			/* if in a child, pop back to our parent */
6569339Seric 			if (InChild)
6579339Seric 				finis();
65824943Seric 
65924943Seric 			/* clean up a bit */
66058109Seric 			gotmail = FALSE;
66155012Seric 			dropenvelope(e);
66258179Seric 			CurEnv = e = newenvelope(e, CurEnv);
66355012Seric 			e->e_flags = BlankEnvelope.e_flags;
6644549Seric 			break;
6654549Seric 
6664549Seric 		  case CMDRSET:		/* rset -- reset state */
66758151Seric 			message("250 Reset state");
66868603Seric 
66968603Seric 			/* arrange to ignore any current send list */
67068603Seric 			e->e_sendqueue = NULL;
67164359Seric 			e->e_flags |= EF_CLRQUEUE;
6729339Seric 			if (InChild)
6739339Seric 				finis();
67458109Seric 
67558109Seric 			/* clean up a bit */
67658109Seric 			gotmail = FALSE;
67758109Seric 			dropenvelope(e);
67858179Seric 			CurEnv = e = newenvelope(e, CurEnv);
6799339Seric 			break;
6804549Seric 
6814549Seric 		  case CMDVRFY:		/* vrfy -- verify address */
68258092Seric 		  case CMDEXPN:		/* expn -- expand address */
68358092Seric 			vrfy = c->cmdcode == CMDVRFY;
68458092Seric 			if (bitset(vrfy ? PRIV_NOVRFY : PRIV_NOEXPN,
68558092Seric 						PrivacyFlags))
68658082Seric 			{
68758412Seric 				if (vrfy)
68867160Seric 					message("252 Cannot VRFY user; try RCPT to attempt delivery (or try finger)");
68958412Seric 				else
69065192Seric 					message("502 Sorry, we do not allow this operation");
69165017Seric #ifdef LOG
69265017Seric 				if (LogLevel > 5)
69365017Seric 					syslog(LOG_INFO, "%s: %s [rejected]",
69465017Seric 						CurSmtpClient, inp);
69565017Seric #endif
69658082Seric 				break;
69758082Seric 			}
69858082Seric 			else if (!gothello &&
69958092Seric 				 bitset(vrfy ? PRIV_NEEDVRFYHELO : PRIV_NEEDEXPNHELO,
70058092Seric 						PrivacyFlags))
70158082Seric 			{
70258151Seric 				message("503 I demand that you introduce yourself first");
70358082Seric 				break;
70458082Seric 			}
70558092Seric 			if (runinchild(vrfy ? "SMTP-VRFY" : "SMTP-EXPN", e) > 0)
7069339Seric 				break;
70755173Seric #ifdef LOG
70858020Seric 			if (LogLevel > 5)
70965017Seric 				syslog(LOG_INFO, "%s: %s", CurSmtpClient, inp);
71055173Seric #endif
7115003Seric 			vrfyqueue = NULL;
7127762Seric 			QuickAbort = TRUE;
71358092Seric 			if (vrfy)
71458092Seric 				e->e_flags |= EF_VRFYONLY;
71562373Seric 			while (*p != '\0' && isascii(*p) && isspace(*p))
71667615Seric 				p++;
71762373Seric 			if (*p == '\0')
71862373Seric 			{
71962373Seric 				message("501 Argument required");
72062373Seric 				Errors++;
72162373Seric 			}
72262373Seric 			else
72362373Seric 			{
72467990Seric 				(void) sendtolist(p, NULLADDR, &vrfyqueue, 0, e);
72562373Seric 			}
7267762Seric 			if (Errors != 0)
7279339Seric 			{
7289339Seric 				if (InChild)
7299339Seric 					finis();
7307762Seric 				break;
7319339Seric 			}
73262373Seric 			if (vrfyqueue == NULL)
73362373Seric 			{
73462373Seric 				message("554 Nothing to %s", vrfy ? "VRFY" : "EXPN");
73562373Seric 			}
7365003Seric 			while (vrfyqueue != NULL)
7375003Seric 			{
73869748Seric 				extern void printvrfyaddr __P((ADDRESS *, bool));
73969748Seric 
74063971Seric 				a = vrfyqueue;
74163971Seric 				while ((a = a->q_next) != NULL &&
74263971Seric 				       bitset(QDONTSEND|QBADADDR, a->q_flags))
74363971Seric 					continue;
7447685Seric 				if (!bitset(QDONTSEND|QBADADDR, vrfyqueue->q_flags))
74558151Seric 					printvrfyaddr(vrfyqueue, a == NULL);
74663847Seric 				vrfyqueue = vrfyqueue->q_next;
7475003Seric 			}
7489339Seric 			if (InChild)
7499339Seric 				finis();
7504549Seric 			break;
7514549Seric 
7524549Seric 		  case CMDHELP:		/* help -- give user info */
7534577Seric 			help(p);
7544549Seric 			break;
7554549Seric 
7564549Seric 		  case CMDNOOP:		/* noop -- do nothing */
75764122Seric 			message("250 OK");
7584549Seric 			break;
7594549Seric 
7604549Seric 		  case CMDQUIT:		/* quit -- leave mail */
76158151Seric 			message("221 %s closing connection", MyHostName);
76261051Seric 
76366283Seric doquit:
76468603Seric 			/* arrange to ignore any current send list */
76568603Seric 			e->e_sendqueue = NULL;
76668603Seric 
76761051Seric 			/* avoid future 050 messages */
76866017Seric 			disconnect(1, e);
76961051Seric 
7709339Seric 			if (InChild)
7719339Seric 				ExitStat = EX_QUIT;
7724549Seric 			finis();
7734549Seric 
7748544Seric 		  case CMDVERB:		/* set verbose mode */
77559957Seric 			if (bitset(PRIV_NOEXPN, PrivacyFlags))
77659957Seric 			{
77759957Seric 				/* this would give out the same info */
77859957Seric 				message("502 Verbose unavailable");
77959957Seric 				break;
78059957Seric 			}
7818544Seric 			Verbose = TRUE;
78258734Seric 			e->e_sendmode = SM_DELIVER;
78359957Seric 			message("250 Verbose mode");
7848544Seric 			break;
7858544Seric 
7869314Seric 		  case CMDONEX:		/* doing one transaction only */
7879378Seric 			OneXact = TRUE;
78859957Seric 			message("250 Only one transaction");
7899314Seric 			break;
7909314Seric 
79136230Skarels # ifdef SMTPDEBUG
7929339Seric 		  case CMDDBGQSHOW:	/* show queues */
7936907Seric 			printf("Send Queue=");
79455012Seric 			printaddr(e->e_sendqueue, TRUE);
7955003Seric 			break;
7967275Seric 
7977275Seric 		  case CMDDBGDEBUG:	/* set debug mode */
7987676Seric 			tTsetup(tTdvect, sizeof tTdvect, "0-99.1");
7997676Seric 			tTflag(p);
80058151Seric 			message("200 Debug set");
8017275Seric 			break;
8027275Seric 
80336230Skarels # else /* not SMTPDEBUG */
80436230Skarels 		  case CMDDBGQSHOW:	/* show queues */
80536230Skarels 		  case CMDDBGDEBUG:	/* set debug mode */
80664685Seric # endif /* SMTPDEBUG */
80764685Seric 		  case CMDLOGBOGUS:	/* bogus command */
80836233Skarels # ifdef LOG
80958308Seric 			if (LogLevel > 0)
81064685Seric 				syslog(LOG_CRIT,
81158020Seric 				    "\"%s\" command from %s (%s)",
81266005Seric 				    c->cmdname, peerhostname,
81358755Seric 				    anynet_ntoa(&RealHostAddr));
81436233Skarels # endif
81536230Skarels 			/* FALL THROUGH */
81636230Skarels 
8174549Seric 		  case CMDERROR:	/* unknown command */
81866283Seric 			if (++badcommands > MAXBADCOMMANDS)
81966283Seric 			{
82066283Seric 				message("421 %s Too many bad commands; closing connection",
82166283Seric 					MyHostName);
82266283Seric 				goto doquit;
82366283Seric 			}
82466283Seric 
82558151Seric 			message("500 Command unrecognized");
8264549Seric 			break;
8274549Seric 
8284549Seric 		  default:
82936230Skarels 			errno = 0;
83058151Seric 			syserr("500 smtp: unknown code %d", c->cmdcode);
8314549Seric 			break;
8324549Seric 		}
8334549Seric 	}
8344549Seric }
8354549Seric /*
8364549Seric **  SKIPWORD -- skip a fixed word.
8374549Seric **
8384549Seric **	Parameters:
8394549Seric **		p -- place to start looking.
8404549Seric **		w -- word to skip.
8414549Seric **
8424549Seric **	Returns:
8434549Seric **		p following w.
8444549Seric **		NULL on error.
8454549Seric **
8464549Seric **	Side Effects:
8474549Seric **		clobbers the p data area.
8484549Seric */
8494549Seric 
8504549Seric static char *
8514549Seric skipword(p, w)
8524549Seric 	register char *p;
8534549Seric 	char *w;
8544549Seric {
8554549Seric 	register char *q;
85666005Seric 	char *firstp = p;
8574549Seric 
8584549Seric 	/* find beginning of word */
85958050Seric 	while (isascii(*p) && isspace(*p))
8604549Seric 		p++;
8614549Seric 	q = p;
8624549Seric 
8634549Seric 	/* find end of word */
86458050Seric 	while (*p != '\0' && *p != ':' && !(isascii(*p) && isspace(*p)))
8654549Seric 		p++;
86658050Seric 	while (isascii(*p) && isspace(*p))
8674549Seric 		*p++ = '\0';
8684549Seric 	if (*p != ':')
8694549Seric 	{
8704549Seric 	  syntax:
87166005Seric 		message("501 Syntax error in parameters scanning \"%s\"",
87266005Seric 			firstp);
8734549Seric 		Errors++;
8744549Seric 		return (NULL);
8754549Seric 	}
8764549Seric 	*p++ = '\0';
87758050Seric 	while (isascii(*p) && isspace(*p))
8784549Seric 		p++;
8794549Seric 
88062373Seric 	if (*p == '\0')
88162373Seric 		goto syntax;
88262373Seric 
8834549Seric 	/* see if the input word matches desired word */
88433725Sbostic 	if (strcasecmp(q, w))
8854549Seric 		goto syntax;
8864549Seric 
8874549Seric 	return (p);
8884549Seric }
8894577Seric /*
89068559Seric **  MAIL_ESMTP_ARGS -- process ESMTP arguments from MAIL line
89168559Seric **
89268559Seric **	Parameters:
89368559Seric **		kp -- the parameter key.
89468559Seric **		vp -- the value of that parameter.
89568559Seric **		e -- the envelope.
89668559Seric **
89768559Seric **	Returns:
89868559Seric **		none.
89968559Seric */
90068559Seric 
90169748Seric void
90268559Seric mail_esmtp_args(kp, vp, e)
90368559Seric 	char *kp;
90468559Seric 	char *vp;
90568559Seric 	ENVELOPE *e;
90668559Seric {
90768559Seric 	if (strcasecmp(kp, "size") == 0)
90868559Seric 	{
90968559Seric 		if (vp == NULL)
91068559Seric 		{
91168559Seric 			usrerr("501 SIZE requires a value");
91268559Seric 			/* NOTREACHED */
91368559Seric 		}
91468890Seric # if defined(__STDC__) && !defined(BROKEN_ANSI_LIBRARY)
91568560Seric 		e->e_msgsize = strtoul(vp, (char **) NULL, 10);
91668559Seric # else
91768560Seric 		e->e_msgsize = strtol(vp, (char **) NULL, 10);
91868559Seric # endif
91968559Seric 	}
92068559Seric 	else if (strcasecmp(kp, "body") == 0)
92168559Seric 	{
92268559Seric 		if (vp == NULL)
92368559Seric 		{
92468559Seric 			usrerr("501 BODY requires a value");
92568559Seric 			/* NOTREACHED */
92668559Seric 		}
92768559Seric 		if (strcasecmp(vp, "8bitmime") == 0)
92868559Seric 		{
92968559Seric 			SevenBitInput = FALSE;
93068883Seric 			e->e_flags |= EF_NL_NOT_EOL;
93168559Seric 		}
93268559Seric 		else if (strcasecmp(vp, "7bit") == 0)
93368559Seric 		{
93468559Seric 			SevenBitInput = TRUE;
93568559Seric 		}
93668559Seric 		else
93768559Seric 		{
93868559Seric 			usrerr("501 Unknown BODY type %s",
93968559Seric 				vp);
94068559Seric 			/* NOTREACHED */
94168559Seric 		}
94268559Seric 		e->e_bodytype = newstr(vp);
94368559Seric 	}
94468559Seric 	else if (strcasecmp(kp, "envid") == 0)
94568559Seric 	{
94668559Seric 		if (vp == NULL)
94768559Seric 		{
94868559Seric 			usrerr("501 ENVID requires a value");
94968559Seric 			/* NOTREACHED */
95068559Seric 		}
95168583Seric 		if (!xtextok(vp))
95268583Seric 		{
95368583Seric 			usrerr("501 Syntax error in ENVID parameter value");
95468583Seric 			/* NOTREACHED */
95568583Seric 		}
95668583Seric 		if (e->e_envid != NULL)
95768583Seric 		{
95868583Seric 			usrerr("501 Duplicate ENVID parameter");
95968583Seric 			/* NOTREACHED */
96068583Seric 		}
96168559Seric 		e->e_envid = newstr(vp);
96268559Seric 	}
96368559Seric 	else if (strcasecmp(kp, "ret") == 0)
96468559Seric 	{
96568559Seric 		if (vp == NULL)
96668559Seric 		{
96768559Seric 			usrerr("501 RET requires a value");
96868559Seric 			/* NOTREACHED */
96968559Seric 		}
97068583Seric 		if (bitset(EF_RET_PARAM, e->e_flags))
97168583Seric 		{
97268583Seric 			usrerr("501 Duplicate RET parameter");
97368583Seric 			/* NOTREACHED */
97468583Seric 		}
97568559Seric 		e->e_flags |= EF_RET_PARAM;
97668559Seric 		if (strcasecmp(vp, "hdrs") == 0)
97768559Seric 			e->e_flags |= EF_NO_BODY_RETN;
97868559Seric 		else if (strcasecmp(vp, "full") != 0)
97968559Seric 		{
98068559Seric 			usrerr("501 Bad argument \"%s\" to RET", vp);
98168559Seric 			/* NOTREACHED */
98268559Seric 		}
98368559Seric 	}
98468559Seric 	else
98568559Seric 	{
98668559Seric 		usrerr("501 %s parameter unrecognized", kp);
98768559Seric 		/* NOTREACHED */
98868559Seric 	}
98968559Seric }
99068559Seric /*
99167963Seric **  RCPT_ESMTP_ARGS -- process ESMTP arguments from RCPT line
99267963Seric **
99367963Seric **	Parameters:
99467963Seric **		a -- the address corresponding to the To: parameter.
99567963Seric **		kp -- the parameter key.
99667963Seric **		vp -- the value of that parameter.
99767963Seric **		e -- the envelope.
99867963Seric **
99967963Seric **	Returns:
100067963Seric **		none.
100167963Seric */
100267963Seric 
100369748Seric void
100467963Seric rcpt_esmtp_args(a, kp, vp, e)
100567963Seric 	ADDRESS *a;
100667963Seric 	char *kp;
100767963Seric 	char *vp;
100867963Seric 	ENVELOPE *e;
100967963Seric {
101067963Seric 	if (strcasecmp(kp, "notify") == 0)
101167963Seric 	{
101267963Seric 		char *p;
101367963Seric 
101467963Seric 		if (vp == NULL)
101567963Seric 		{
101667963Seric 			usrerr("501 NOTIFY requires a value");
101767963Seric 			/* NOTREACHED */
101867963Seric 		}
101967963Seric 		a->q_flags &= ~(QPINGONSUCCESS|QPINGONFAILURE|QPINGONDELAY);
102068595Seric 		a->q_flags |= QHASNOTIFY;
102167963Seric 		if (strcasecmp(vp, "never") == 0)
102267963Seric 			return;
102367963Seric 		for (p = vp; p != NULL; vp = p)
102467963Seric 		{
102567963Seric 			p = strchr(p, ',');
102667963Seric 			if (p != NULL)
102767963Seric 				*p++ = '\0';
102867963Seric 			if (strcasecmp(vp, "success") == 0)
102967963Seric 				a->q_flags |= QPINGONSUCCESS;
103067963Seric 			else if (strcasecmp(vp, "failure") == 0)
103167963Seric 				a->q_flags |= QPINGONFAILURE;
103267963Seric 			else if (strcasecmp(vp, "delay") == 0)
103367963Seric 				a->q_flags |= QPINGONDELAY;
103467963Seric 			else
103567963Seric 			{
103667963Seric 				usrerr("501 Bad argument \"%s\"  to NOTIFY",
103767963Seric 					vp);
103867963Seric 				/* NOTREACHED */
103967963Seric 			}
104067963Seric 		}
104167963Seric 	}
104267963Seric 	else if (strcasecmp(kp, "orcpt") == 0)
104367963Seric 	{
104467963Seric 		if (vp == NULL)
104567963Seric 		{
104667963Seric 			usrerr("501 ORCPT requires a value");
104767963Seric 			/* NOTREACHED */
104867963Seric 		}
104968583Seric 		if (!xtextok(vp))
105068583Seric 		{
105168583Seric 			usrerr("501 Syntax error in ORCPT parameter value");
105268583Seric 			/* NOTREACHED */
105368583Seric 		}
105468583Seric 		if (a->q_orcpt != NULL)
105568583Seric 		{
105668583Seric 			usrerr("501 Duplicate ORCPT parameter");
105768583Seric 			/* NOTREACHED */
105868583Seric 		}
105967963Seric 		a->q_orcpt = newstr(vp);
106067963Seric 	}
106167963Seric 	else
106267963Seric 	{
106367963Seric 		usrerr("501 %s parameter unrecognized", kp);
106467963Seric 		/* NOTREACHED */
106567963Seric 	}
106667963Seric }
106767963Seric /*
106858151Seric **  PRINTVRFYADDR -- print an entry in the verify queue
106958151Seric **
107058151Seric **	Parameters:
107158151Seric **		a -- the address to print
107258151Seric **		last -- set if this is the last one.
107358151Seric **
107458151Seric **	Returns:
107558151Seric **		none.
107658151Seric **
107758151Seric **	Side Effects:
107858151Seric **		Prints the appropriate 250 codes.
107958151Seric */
108058151Seric 
108169748Seric void
108258151Seric printvrfyaddr(a, last)
108358151Seric 	register ADDRESS *a;
108458151Seric 	bool last;
108558151Seric {
108658151Seric 	char fmtbuf[20];
108758151Seric 
108858151Seric 	strcpy(fmtbuf, "250");
108958151Seric 	fmtbuf[3] = last ? ' ' : '-';
109058151Seric 
109159746Seric 	if (a->q_fullname == NULL)
109259746Seric 	{
109359746Seric 		if (strchr(a->q_user, '@') == NULL)
109459746Seric 			strcpy(&fmtbuf[4], "<%s@%s>");
109559746Seric 		else
109659746Seric 			strcpy(&fmtbuf[4], "<%s>");
109759746Seric 		message(fmtbuf, a->q_user, MyHostName);
109859746Seric 	}
109958151Seric 	else
110058151Seric 	{
110159746Seric 		if (strchr(a->q_user, '@') == NULL)
110259746Seric 			strcpy(&fmtbuf[4], "%s <%s@%s>");
110359746Seric 		else
110459746Seric 			strcpy(&fmtbuf[4], "%s <%s>");
110559746Seric 		message(fmtbuf, a->q_fullname, a->q_user, MyHostName);
110658151Seric 	}
110758151Seric }
110858151Seric /*
11094577Seric **  HELP -- implement the HELP command.
11104577Seric **
11114577Seric **	Parameters:
11124577Seric **		topic -- the topic we want help for.
11134577Seric **
11144577Seric **	Returns:
11154577Seric **		none.
11164577Seric **
11174577Seric **	Side Effects:
11184577Seric **		outputs the help file to message output.
11194577Seric */
11204577Seric 
112169748Seric void
11224577Seric help(topic)
11234577Seric 	char *topic;
11244577Seric {
11254577Seric 	register FILE *hf;
11264577Seric 	int len;
112768751Seric 	bool noinfo;
11284577Seric 	char buf[MAXLINE];
112968751Seric 	extern char Version[];
11304577Seric 
113168751Seric 
11328269Seric 	if (HelpFile == NULL || (hf = fopen(HelpFile, "r")) == NULL)
11334577Seric 	{
11344577Seric 		/* no help */
113511931Seric 		errno = 0;
113668751Seric 		message("502 Sendmail %s -- HELP not implemented", Version);
11374577Seric 		return;
11384577Seric 	}
11394577Seric 
114049669Seric 	if (topic == NULL || *topic == '\0')
114168751Seric 	{
114249669Seric 		topic = "smtp";
114368751Seric 		message("214-This is Sendmail version %s", Version);
114468751Seric 		noinfo = FALSE;
114568751Seric 	}
114649669Seric 	else
114768751Seric 	{
114849669Seric 		makelower(topic);
114968751Seric 		noinfo = TRUE;
115068751Seric 	}
115149669Seric 
11524577Seric 	len = strlen(topic);
11534577Seric 
11544577Seric 	while (fgets(buf, sizeof buf, hf) != NULL)
11554577Seric 	{
11564577Seric 		if (strncmp(buf, topic, len) == 0)
11574577Seric 		{
11584577Seric 			register char *p;
11594577Seric 
116056795Seric 			p = strchr(buf, '\t');
11614577Seric 			if (p == NULL)
11624577Seric 				p = buf;
11634577Seric 			else
11644577Seric 				p++;
11654577Seric 			fixcrlf(p, TRUE);
116658151Seric 			message("214-%s", p);
11674577Seric 			noinfo = FALSE;
11684577Seric 		}
11694577Seric 	}
11704577Seric 
11714577Seric 	if (noinfo)
117258151Seric 		message("504 HELP topic unknown");
11734577Seric 	else
117458151Seric 		message("214 End of HELP info");
11754628Seric 	(void) fclose(hf);
11764577Seric }
11778544Seric /*
11789339Seric **  RUNINCHILD -- return twice -- once in the child, then in the parent again
11799339Seric **
11809339Seric **	Parameters:
11819339Seric **		label -- a string used in error messages
11829339Seric **
11839339Seric **	Returns:
11849339Seric **		zero in the child
11859339Seric **		one in the parent
11869339Seric **
11879339Seric **	Side Effects:
11889339Seric **		none.
11899339Seric */
11908544Seric 
119169748Seric int
119255012Seric runinchild(label, e)
11939339Seric 	char *label;
119455012Seric 	register ENVELOPE *e;
11959339Seric {
11969339Seric 	int childpid;
11979339Seric 
119816158Seric 	if (!OneXact)
11999339Seric 	{
120016158Seric 		childpid = dofork();
120116158Seric 		if (childpid < 0)
120216158Seric 		{
120369716Seric 			syserr("451 %s: cannot fork", label);
120416158Seric 			return (1);
120516158Seric 		}
120616158Seric 		if (childpid > 0)
120716158Seric 		{
120816158Seric 			auto int st;
12099339Seric 
121016158Seric 			/* parent -- wait for child to complete */
121161093Seric 			setproctitle("server %s child wait", CurHostName);
121216158Seric 			st = waitfor(childpid);
121316158Seric 			if (st == -1)
121469716Seric 				syserr("451 %s: lost child", label);
121564948Seric 			else if (!WIFEXITED(st))
121669716Seric 				syserr("451 %s: died on signal %d",
121764948Seric 					label, st & 0177);
12189339Seric 
121916158Seric 			/* if we exited on a QUIT command, complete the process */
122066017Seric 			if (WEXITSTATUS(st) == EX_QUIT)
122166017Seric 			{
122266017Seric 				disconnect(1, e);
122316158Seric 				finis();
122466017Seric 			}
12259339Seric 
122616158Seric 			return (1);
122716158Seric 		}
122816158Seric 		else
122916158Seric 		{
123016158Seric 			/* child */
123116158Seric 			InChild = TRUE;
123225050Seric 			QuickAbort = FALSE;
123355012Seric 			clearenvelope(e, FALSE);
123416158Seric 		}
12359339Seric 	}
123615256Seric 
123716158Seric 	/* open alias database */
123860537Seric 	initmaps(FALSE, e);
123916158Seric 
124016158Seric 	return (0);
12419339Seric }
12429339Seric 
124356795Seric # endif /* SMTP */
1244