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*69972Seric static char sccsid[] = "@(#)srvrsmtp.c	8.83 (Berkeley) 06/21/95 (with SMTP)";
1433731Sbostic #else
15*69972Seric static char sccsid[] = "@(#)srvrsmtp.c	8.83 (Berkeley) 06/21/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
smtp(e)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);
13869792Seric #ifdef LOG
13969792Seric 	if (LogLevel > 11)
14069792Seric 	{
14169792Seric 		/* log connection information */
14269792Seric 		syslog(LOG_INFO, "SMTP connect from %s (%s)",
14369792Seric 			CurSmtpClient, anynet_ntoa(&RealHostAddr));
14469792Seric 	}
14569792Seric #endif
14668769Seric 
14768769Seric 	/* output the first line, inserting "ESMTP" as second word */
14869792Seric 	expand("\201e", inp, sizeof inp, e);
14968769Seric 	p = strchr(inp, '\n');
15068769Seric 	if (p != NULL)
15168769Seric 		*p++ = '\0';
15268769Seric 	id = strchr(inp, ' ');
15368769Seric 	if (id == NULL)
15468769Seric 		id = &inp[strlen(inp)];
15568769Seric 	cmd = p == NULL ? "220 %.*s ESMTP%s" : "220-%.*s ESMTP%s";
15668769Seric 	message(cmd, id - inp, inp, id);
15768769Seric 
15868769Seric 	/* output remaining lines */
15968769Seric 	while ((id = p) != NULL && (p = strchr(id, '\n')) != NULL)
16064496Seric 	{
16168769Seric 		*p++ = '\0';
16269106Seric 		if (isascii(*id) && isspace(*id))
16369106Seric 			id++;
16468769Seric 		message("220-%s", id);
16564496Seric 	}
16668769Seric 	if (id != NULL)
16769106Seric 	{
16869106Seric 		if (isascii(*id) && isspace(*id))
16969106Seric 			id++;
17068769Seric 		message("220 %s", id);
17169106Seric 	}
17266745Seric 
17358330Seric 	protocol = NULL;
17459016Seric 	sendinghost = macvalue('s', e);
17558082Seric 	gothello = FALSE;
17658330Seric 	gotmail = FALSE;
1774549Seric 	for (;;)
1784549Seric 	{
17912612Seric 		/* arrange for backout */
18065751Seric 		if (setjmp(TopFrame) > 0)
18159058Seric 		{
18265751Seric 			/* if() nesting is necessary for Cray UNICOS */
18365751Seric 			if (InChild)
18465751Seric 			{
18565751Seric 				QuickAbort = FALSE;
18665751Seric 				SuprErrs = TRUE;
18765751Seric 				finis();
18865751Seric 			}
18959058Seric 		}
19012612Seric 		QuickAbort = FALSE;
19112612Seric 		HoldErrs = FALSE;
19251951Seric 		LogUsrErrs = FALSE;
19363843Seric 		e->e_flags &= ~(EF_VRFYONLY|EF_GLOBALERRS);
19412612Seric 
1957356Seric 		/* setup for the read */
19655012Seric 		e->e_to = NULL;
1974577Seric 		Errors = 0;
1987275Seric 		(void) fflush(stdout);
1997356Seric 
2007356Seric 		/* read the input line */
20161093Seric 		SmtpPhase = "server cmd read";
20269788Seric 		setproctitle("server %s cmd read", CurSmtpClient);
20361093Seric 		p = sfgets(inp, sizeof inp, InChannel, TimeOuts.to_nextcommand,
20461093Seric 				SmtpPhase);
2057356Seric 
2067685Seric 		/* handle errors */
2077356Seric 		if (p == NULL)
2087356Seric 		{
2094549Seric 			/* end of file, just die */
21066017Seric 			disconnect(1, e);
21158151Seric 			message("421 %s Lost input channel from %s",
21265017Seric 				MyHostName, CurSmtpClient);
21355464Seric #ifdef LOG
21463843Seric 			if (LogLevel > (gotmail ? 1 : 19))
21555464Seric 				syslog(LOG_NOTICE, "lost input channel from %s",
21665017Seric 					CurSmtpClient);
21755464Seric #endif
21858069Seric 			if (InChild)
21958069Seric 				ExitStat = EX_QUIT;
2204549Seric 			finis();
2214549Seric 		}
2224549Seric 
2234549Seric 		/* clean up end of line */
2244558Seric 		fixcrlf(inp, TRUE);
2254549Seric 
2264713Seric 		/* echo command to transcript */
22755012Seric 		if (e->e_xfp != NULL)
22855012Seric 			fprintf(e->e_xfp, "<<< %s\n", inp);
2294713Seric 
23059060Seric 		if (e->e_id == NULL)
23165058Seric 			setproctitle("%s: %.80s", CurSmtpClient, inp);
23259060Seric 		else
23365058Seric 			setproctitle("%s %s: %.80s", e->e_id, CurSmtpClient, inp);
23459060Seric 
2354549Seric 		/* break off command */
23658050Seric 		for (p = inp; isascii(*p) && isspace(*p); p++)
2374549Seric 			continue;
23857232Seric 		cmd = cmdbuf;
23958050Seric 		while (*p != '\0' &&
24058050Seric 		       !(isascii(*p) && isspace(*p)) &&
24158050Seric 		       cmd < &cmdbuf[sizeof cmdbuf - 2])
24224981Seric 			*cmd++ = *p++;
24324981Seric 		*cmd = '\0';
2444549Seric 
24525691Seric 		/* throw away leading whitespace */
24658050Seric 		while (isascii(*p) && isspace(*p))
24725691Seric 			p++;
24825691Seric 
2494549Seric 		/* decode command */
2504549Seric 		for (c = CmdTab; c->cmdname != NULL; c++)
2514549Seric 		{
25233725Sbostic 			if (!strcasecmp(c->cmdname, cmdbuf))
2534549Seric 				break;
2544549Seric 		}
2554549Seric 
25651954Seric 		/* reset errors */
25751954Seric 		errno = 0;
25851954Seric 
2594549Seric 		/* process command */
2604549Seric 		switch (c->cmdcode)
2614549Seric 		{
2624976Seric 		  case CMDHELO:		/* hello -- introduce yourself */
26358323Seric 		  case CMDEHLO:		/* extended hello */
26458323Seric 			if (c->cmdcode == CMDEHLO)
26558323Seric 			{
26658323Seric 				protocol = "ESMTP";
26761093Seric 				SmtpPhase = "server EHLO";
26858323Seric 			}
26958323Seric 			else
27058323Seric 			{
27158323Seric 				protocol = "SMTP";
27261093Seric 				SmtpPhase = "server HELO";
27358323Seric 			}
27467445Seric 
27567445Seric 			/* check for valid domain name (re 1123 5.2.5) */
27667445Seric 			if (*p == '\0')
27767445Seric 			{
27867445Seric 				message("501 %s requires domain address",
27967445Seric 					cmdbuf);
28067445Seric 				break;
28167445Seric 			}
28267445Seric 			else
28367445Seric 			{
28467445Seric 				register char *q;
28567445Seric 
28667445Seric 				for (q = p; *q != '\0'; q++)
28767445Seric 				{
28867445Seric 					if (!isascii(*q))
28967445Seric 						break;
29067445Seric 					if (isalnum(*q))
29167445Seric 						continue;
29267445Seric 					if (strchr("[].-_#", *q) == NULL)
29367445Seric 						break;
29467445Seric 				}
29567445Seric 				if (*q != '\0')
29667445Seric 				{
29767445Seric 					message("501 Invalid domain name");
29867445Seric 					break;
29967445Seric 				}
30067445Seric 			}
30167445Seric 
30259016Seric 			sendinghost = newstr(p);
30360210Seric 			gothello = TRUE;
30460210Seric 			if (c->cmdcode != CMDEHLO)
30560239Seric 			{
30660239Seric 				/* print old message and be done with it */
30760239Seric 				message("250 %s Hello %s, pleased to meet you",
30865017Seric 					MyHostName, CurSmtpClient);
30960210Seric 				break;
31060239Seric 			}
31160239Seric 
31260239Seric 			/* print extended message and brag */
31360239Seric 			message("250-%s Hello %s, pleased to meet you",
31466760Seric 				MyHostName, CurSmtpClient);
31558323Seric 			if (!bitset(PRIV_NOEXPN, PrivacyFlags))
31658323Seric 				message("250-EXPN");
31769480Seric #if MIME8TO7
31867417Seric 			message("250-8BITMIME");
31969480Seric #endif
32064359Seric 			if (MaxMessageSize > 0)
32164359Seric 				message("250-SIZE %ld", MaxMessageSize);
32259271Seric 			else
32359271Seric 				message("250-SIZE");
32468848Seric #if DSN
32569858Seric 			message("250-X-DSN-04 (Draft of May 29, 1995)");
32668028Seric #endif
32758323Seric 			message("250 HELP");
3284976Seric 			break;
3294976Seric 
3304549Seric 		  case CMDMAIL:		/* mail -- designate sender */
33161093Seric 			SmtpPhase = "server MAIL";
33224943Seric 
3339314Seric 			/* check for validity of this command */
33458789Seric 			if (!gothello)
33558082Seric 			{
33658957Seric 				/* set sending host to our known value */
33759016Seric 				if (sendinghost == NULL)
33866005Seric 					sendinghost = peerhostname;
33958957Seric 
34058789Seric 				if (bitset(PRIV_NEEDMAILHELO, PrivacyFlags))
34158821Seric 				{
34258789Seric 					message("503 Polite people say HELO first");
34358821Seric 					break;
34458821Seric 				}
34558082Seric 			}
34658109Seric 			if (gotmail)
3474558Seric 			{
34858151Seric 				message("503 Sender already specified");
34963843Seric 				if (InChild)
35063843Seric 					finis();
3514558Seric 				break;
3524558Seric 			}
3539339Seric 			if (InChild)
3549339Seric 			{
35536230Skarels 				errno = 0;
35658151Seric 				syserr("503 Nested MAIL command: MAIL %s", p);
35758069Seric 				finis();
3589339Seric 			}
3599339Seric 
3609339Seric 			/* fork a subprocess to process this command */
36155012Seric 			if (runinchild("SMTP-MAIL", e) > 0)
3629339Seric 				break;
36363753Seric 			if (!gothello)
36463753Seric 			{
36563753Seric 				auth_warning(e,
36666005Seric 					"Host %s didn't use HELO protocol",
36769788Seric 					CurSmtpClient);
36863753Seric 			}
36965947Seric #ifdef PICKY_HELO_CHECK
37066005Seric 			if (strcasecmp(sendinghost, peerhostname) != 0 &&
37166005Seric 			    (strcasecmp(peerhostname, "localhost") != 0 ||
37265823Seric 			     strcasecmp(sendinghost, MyHostName) != 0))
37365823Seric 			{
37465823Seric 				auth_warning(e, "Host %s claimed to be %s",
37569788Seric 					CurSmtpClient, sendinghost);
37665823Seric 			}
37765947Seric #endif
37865823Seric 
37958323Seric 			if (protocol == NULL)
38058323Seric 				protocol = "SMTP";
38158323Seric 			define('r', protocol, e);
38259016Seric 			define('s', sendinghost, e);
38355012Seric 			initsys(e);
38459747Seric 			nrcpts = 0;
38568582Seric 			e->e_flags |= EF_LOGSENDER|EF_CLRQUEUE;
38665058Seric 			setproctitle("%s %s: %.80s", e->e_id, CurSmtpClient, inp);
3879339Seric 
3889339Seric 			/* child -- go do the processing */
3894549Seric 			p = skipword(p, "from");
3904549Seric 			if (p == NULL)
3914549Seric 				break;
39257977Seric 			if (setjmp(TopFrame) > 0)
39358147Seric 			{
39458147Seric 				/* this failed -- undo work */
39558147Seric 				if (InChild)
39659058Seric 				{
39759058Seric 					QuickAbort = FALSE;
39859058Seric 					SuprErrs = TRUE;
39963787Seric 					e->e_flags &= ~EF_FATALERRS;
40058147Seric 					finis();
40159058Seric 				}
40257977Seric 				break;
40358147Seric 			}
40457977Seric 			QuickAbort = TRUE;
40558333Seric 
40658333Seric 			/* must parse sender first */
40758333Seric 			delimptr = NULL;
40858704Seric 			setsender(p, e, &delimptr, FALSE);
40958333Seric 			p = delimptr;
41058333Seric 			if (p != NULL && *p != '\0')
41158333Seric 				*p++ = '\0';
41258333Seric 
41366325Seric 			/* check for possible spoofing */
41466325Seric 			if (RealUid != 0 && OpMode == MD_SMTP &&
41569757Seric 			    !wordinclass(RealUserName, 't') &&
41667473Seric 			    !bitnset(M_LOCALMAILER, e->e_from.q_mailer->m_flags) &&
41767473Seric 			    strcmp(e->e_from.q_user, RealUserName) != 0)
41866325Seric 			{
41966325Seric 				auth_warning(e, "%s owned process doing -bs",
42066325Seric 					RealUserName);
42166325Seric 			}
42266325Seric 
42358333Seric 			/* now parse ESMTP arguments */
42468560Seric 			e->e_msgsize = 0;
42566764Seric 			while (p != NULL && *p != '\0')
42658333Seric 			{
42758333Seric 				char *kp;
42866304Seric 				char *vp = NULL;
42969748Seric 				extern void mail_esmtp_args __P((char *, char *, ENVELOPE *));
43058333Seric 
43158333Seric 				/* locate the beginning of the keyword */
43258333Seric 				while (isascii(*p) && isspace(*p))
43358333Seric 					p++;
43458333Seric 				if (*p == '\0')
43558333Seric 					break;
43658333Seric 				kp = p;
43758333Seric 
43858333Seric 				/* skip to the value portion */
43958333Seric 				while (isascii(*p) && isalnum(*p) || *p == '-')
44058333Seric 					p++;
44158333Seric 				if (*p == '=')
44258333Seric 				{
44358333Seric 					*p++ = '\0';
44458333Seric 					vp = p;
44558333Seric 
44658333Seric 					/* skip to the end of the value */
44758333Seric 					while (*p != '\0' && *p != ' ' &&
44858333Seric 					       !(isascii(*p) && iscntrl(*p)) &&
44958333Seric 					       *p != '=')
45058333Seric 						p++;
45158333Seric 				}
45258333Seric 
45358333Seric 				if (*p != '\0')
45458333Seric 					*p++ = '\0';
45558333Seric 
45658333Seric 				if (tTd(19, 1))
45766764Seric 					printf("MAIL: got arg %s=\"%s\"\n", kp,
45858333Seric 						vp == NULL ? "<null>" : vp);
45958333Seric 
46068559Seric 				mail_esmtp_args(kp, vp, e);
46158333Seric 			}
46259284Seric 
46368560Seric 			if (MaxMessageSize > 0 && e->e_msgsize > MaxMessageSize)
46459284Seric 			{
46559284Seric 				usrerr("552 Message size exceeds fixed maximum message size (%ld)",
46659284Seric 					MaxMessageSize);
46759284Seric 				/* NOTREACHED */
46859284Seric 			}
46958333Seric 
47069801Seric 			if (!enoughdiskspace(e->e_msgsize))
47158333Seric 			{
47258333Seric 				message("452 Insufficient disk space; try again later");
47358333Seric 				break;
47458333Seric 			}
47558151Seric 			message("250 Sender ok");
47658147Seric 			gotmail = TRUE;
4774549Seric 			break;
4784549Seric 
4794976Seric 		  case CMDRCPT:		/* rcpt -- designate recipient */
48058850Seric 			if (!gotmail)
48158850Seric 			{
48258850Seric 				usrerr("503 Need MAIL before RCPT");
48358850Seric 				break;
48458850Seric 			}
48561093Seric 			SmtpPhase = "server RCPT";
48612612Seric 			if (setjmp(TopFrame) > 0)
48714785Seric 			{
48855012Seric 				e->e_flags &= ~EF_FATALERRS;
48912612Seric 				break;
49014785Seric 			}
49112612Seric 			QuickAbort = TRUE;
49251951Seric 			LogUsrErrs = TRUE;
49358093Seric 
49459699Seric 			if (e->e_sendmode != SM_DELIVER)
49559699Seric 				e->e_flags |= EF_VRFYONLY;
49658919Seric 
4974549Seric 			p = skipword(p, "to");
4984549Seric 			if (p == NULL)
4994549Seric 				break;
50067880Seric 			a = parseaddr(p, NULLADDR, RF_COPYALL, ' ', &delimptr, e);
50112612Seric 			if (a == NULL)
50212612Seric 				break;
50367880Seric 			p = delimptr;
50467880Seric 
50567880Seric 			/* now parse ESMTP arguments */
50667880Seric 			while (p != NULL && *p != '\0')
50767880Seric 			{
50867880Seric 				char *kp;
50967880Seric 				char *vp = NULL;
51069748Seric 				extern void rcpt_esmtp_args __P((ADDRESS *, char *, char *, ENVELOPE *));
51167880Seric 
51267880Seric 				/* locate the beginning of the keyword */
51367880Seric 				while (isascii(*p) && isspace(*p))
51467880Seric 					p++;
51567880Seric 				if (*p == '\0')
51667880Seric 					break;
51767880Seric 				kp = p;
51867880Seric 
51967880Seric 				/* skip to the value portion */
52067880Seric 				while (isascii(*p) && isalnum(*p) || *p == '-')
52167880Seric 					p++;
52267880Seric 				if (*p == '=')
52367880Seric 				{
52467880Seric 					*p++ = '\0';
52567880Seric 					vp = p;
52667880Seric 
52767880Seric 					/* skip to the end of the value */
52867880Seric 					while (*p != '\0' && *p != ' ' &&
52967880Seric 					       !(isascii(*p) && iscntrl(*p)) &&
53067880Seric 					       *p != '=')
53167880Seric 						p++;
53267880Seric 				}
53367880Seric 
53467880Seric 				if (*p != '\0')
53567880Seric 					*p++ = '\0';
53667880Seric 
53767880Seric 				if (tTd(19, 1))
53867880Seric 					printf("RCPT: got arg %s=\"%s\"\n", kp,
53967880Seric 						vp == NULL ? "<null>" : vp);
54067880Seric 
54167963Seric 				rcpt_esmtp_args(a, kp, vp, e);
54267880Seric 			}
54367980Seric 
54467980Seric 			/* save in recipient list after ESMTP mods */
54567982Seric 			a = recipient(a, &e->e_sendqueue, 0, e);
54667980Seric 
54712612Seric 			if (Errors != 0)
54812612Seric 				break;
54912612Seric 
55012612Seric 			/* no errors during parsing, but might be a duplicate */
55155012Seric 			e->e_to = p;
55212612Seric 			if (!bitset(QBADADDR, a->q_flags))
55359747Seric 			{
55464718Seric 				message("250 Recipient ok%s",
55564718Seric 					bitset(QQUEUEUP, a->q_flags) ?
55664718Seric 						" (will queue)" : "");
55759747Seric 				nrcpts++;
55859747Seric 			}
55912612Seric 			else
5604549Seric 			{
56112612Seric 				/* punt -- should keep message in ADDRESS.... */
56258151Seric 				message("550 Addressee unknown");
5634549Seric 			}
56455012Seric 			e->e_to = NULL;
5654549Seric 			break;
5664549Seric 
5674549Seric 		  case CMDDATA:		/* data -- text of mail */
56861093Seric 			SmtpPhase = "server DATA";
56958109Seric 			if (!gotmail)
5704549Seric 			{
57158151Seric 				message("503 Need MAIL command");
5724976Seric 				break;
5734549Seric 			}
57464718Seric 			else if (nrcpts <= 0)
5754549Seric 			{
57658151Seric 				message("503 Need RCPT (recipient)");
5774976Seric 				break;
5784549Seric 			}
5794976Seric 
58058929Seric 			/* check to see if we need to re-expand aliases */
58163787Seric 			/* also reset QBADADDR on already-diagnosted addrs */
58263787Seric 			doublequeue = FALSE;
58358929Seric 			for (a = e->e_sendqueue; a != NULL; a = a->q_next)
58458929Seric 			{
58558929Seric 				if (bitset(QVERIFIED, a->q_flags))
58663787Seric 				{
58763787Seric 					/* need to re-expand aliases */
58863787Seric 					doublequeue = TRUE;
58963787Seric 				}
59063787Seric 				if (bitset(QBADADDR, a->q_flags))
59163787Seric 				{
59263787Seric 					/* make this "go away" */
59363787Seric 					a->q_flags |= QDONTSEND;
59463787Seric 					a->q_flags &= ~QBADADDR;
59563787Seric 				}
59658929Seric 			}
59758929Seric 
5984976Seric 			/* collect the text of the message */
59924943Seric 			SmtpPhase = "collect";
60068692Seric 			buffer_errors();
60167546Seric 			collect(InChannel, TRUE, doublequeue, NULL, e);
60268692Seric 			flush_errors(TRUE);
60364766Seric 			if (Errors != 0)
60464766Seric 				goto abortmessage;
60567131Seric 
60668582Seric 			/* make sure we actually do delivery */
60768582Seric 			e->e_flags &= ~EF_CLRQUEUE;
60868582Seric 
60967131Seric 			/* from now on, we have to operate silently */
61068692Seric 			buffer_errors();
61167131Seric 			e->e_errormode = EM_MAIL;
6124976Seric 
6138238Seric 			/*
6148238Seric 			**  Arrange to send to everyone.
6158238Seric 			**	If sending to multiple people, mail back
6168238Seric 			**		errors rather than reporting directly.
6178238Seric 			**	In any case, don't mail back errors for
6188238Seric 			**		anything that has happened up to
6198238Seric 			**		now (the other end will do this).
62010197Seric 			**	Truncate our transcript -- the mail has gotten
62110197Seric 			**		to us successfully, and if we have
62210197Seric 			**		to mail this back, it will be easier
62310197Seric 			**		on the reader.
6248238Seric 			**	Then send to everyone.
6258238Seric 			**	Finally give a reply code.  If an error has
6268238Seric 			**		already been given, don't mail a
6278238Seric 			**		message back.
6289339Seric 			**	We goose error returns by clearing error bit.
6298238Seric 			*/
6308238Seric 
63124943Seric 			SmtpPhase = "delivery";
63255012Seric 			e->e_xfp = freopen(queuename(e, 'x'), "w", e->e_xfp);
63358714Seric 			id = e->e_id;
6344976Seric 
63567131Seric 			if (doublequeue)
63659730Seric 			{
63767131Seric 				/* make sure it is in the queue */
63867131Seric 				queueup(e, TRUE, FALSE);
63968035Seric 				if (e->e_sendmode == SM_QUEUE)
64068035Seric 					e->e_flags |= EF_KEEPQUEUE;
64159730Seric 			}
6428238Seric 			else
64358919Seric 			{
64467131Seric 				/* send to all recipients */
64567131Seric 				sendall(e, SM_DEFAULT);
64667131Seric 			}
64767131Seric 			e->e_to = NULL;
64859747Seric 
64967131Seric 			/* issue success message */
65067131Seric 			message("250 %s Message accepted for delivery", id);
65164296Seric 
65267131Seric 			/* if we just queued, poke it */
65367131Seric 			if (doublequeue && e->e_sendmode != SM_QUEUE)
65467131Seric 			{
65567131Seric 				extern pid_t dowork();
65667131Seric 
65767131Seric 				unlockqueue(e);
65867131Seric 				(void) dowork(id, TRUE, TRUE, e);
65958919Seric 			}
66058883Seric 
66159747Seric   abortmessage:
6629339Seric 			/* if in a child, pop back to our parent */
6639339Seric 			if (InChild)
6649339Seric 				finis();
66524943Seric 
66624943Seric 			/* clean up a bit */
66758109Seric 			gotmail = FALSE;
66855012Seric 			dropenvelope(e);
66958179Seric 			CurEnv = e = newenvelope(e, CurEnv);
67055012Seric 			e->e_flags = BlankEnvelope.e_flags;
6714549Seric 			break;
6724549Seric 
6734549Seric 		  case CMDRSET:		/* rset -- reset state */
67458151Seric 			message("250 Reset state");
67568603Seric 
67668603Seric 			/* arrange to ignore any current send list */
67768603Seric 			e->e_sendqueue = NULL;
67864359Seric 			e->e_flags |= EF_CLRQUEUE;
6799339Seric 			if (InChild)
6809339Seric 				finis();
68158109Seric 
68258109Seric 			/* clean up a bit */
68358109Seric 			gotmail = FALSE;
68458109Seric 			dropenvelope(e);
68558179Seric 			CurEnv = e = newenvelope(e, CurEnv);
6869339Seric 			break;
6874549Seric 
6884549Seric 		  case CMDVRFY:		/* vrfy -- verify address */
68958092Seric 		  case CMDEXPN:		/* expn -- expand address */
69058092Seric 			vrfy = c->cmdcode == CMDVRFY;
69158092Seric 			if (bitset(vrfy ? PRIV_NOVRFY : PRIV_NOEXPN,
69258092Seric 						PrivacyFlags))
69358082Seric 			{
69458412Seric 				if (vrfy)
69567160Seric 					message("252 Cannot VRFY user; try RCPT to attempt delivery (or try finger)");
69658412Seric 				else
69765192Seric 					message("502 Sorry, we do not allow this operation");
69865017Seric #ifdef LOG
69965017Seric 				if (LogLevel > 5)
70065017Seric 					syslog(LOG_INFO, "%s: %s [rejected]",
70165017Seric 						CurSmtpClient, inp);
70265017Seric #endif
70358082Seric 				break;
70458082Seric 			}
70558082Seric 			else if (!gothello &&
70658092Seric 				 bitset(vrfy ? PRIV_NEEDVRFYHELO : PRIV_NEEDEXPNHELO,
70758092Seric 						PrivacyFlags))
70858082Seric 			{
70958151Seric 				message("503 I demand that you introduce yourself first");
71058082Seric 				break;
71158082Seric 			}
71258092Seric 			if (runinchild(vrfy ? "SMTP-VRFY" : "SMTP-EXPN", e) > 0)
7139339Seric 				break;
71455173Seric #ifdef LOG
71558020Seric 			if (LogLevel > 5)
71665017Seric 				syslog(LOG_INFO, "%s: %s", CurSmtpClient, inp);
71755173Seric #endif
7185003Seric 			vrfyqueue = NULL;
7197762Seric 			QuickAbort = TRUE;
72058092Seric 			if (vrfy)
72158092Seric 				e->e_flags |= EF_VRFYONLY;
72262373Seric 			while (*p != '\0' && isascii(*p) && isspace(*p))
72367615Seric 				p++;
72462373Seric 			if (*p == '\0')
72562373Seric 			{
72662373Seric 				message("501 Argument required");
72762373Seric 				Errors++;
72862373Seric 			}
72962373Seric 			else
73062373Seric 			{
73167990Seric 				(void) sendtolist(p, NULLADDR, &vrfyqueue, 0, e);
73262373Seric 			}
7337762Seric 			if (Errors != 0)
7349339Seric 			{
7359339Seric 				if (InChild)
7369339Seric 					finis();
7377762Seric 				break;
7389339Seric 			}
73962373Seric 			if (vrfyqueue == NULL)
74062373Seric 			{
74162373Seric 				message("554 Nothing to %s", vrfy ? "VRFY" : "EXPN");
74262373Seric 			}
7435003Seric 			while (vrfyqueue != NULL)
7445003Seric 			{
74569748Seric 				extern void printvrfyaddr __P((ADDRESS *, bool));
74669748Seric 
74763971Seric 				a = vrfyqueue;
74863971Seric 				while ((a = a->q_next) != NULL &&
74963971Seric 				       bitset(QDONTSEND|QBADADDR, a->q_flags))
75063971Seric 					continue;
7517685Seric 				if (!bitset(QDONTSEND|QBADADDR, vrfyqueue->q_flags))
75258151Seric 					printvrfyaddr(vrfyqueue, a == NULL);
75363847Seric 				vrfyqueue = vrfyqueue->q_next;
7545003Seric 			}
7559339Seric 			if (InChild)
7569339Seric 				finis();
7574549Seric 			break;
7584549Seric 
7594549Seric 		  case CMDHELP:		/* help -- give user info */
7604577Seric 			help(p);
7614549Seric 			break;
7624549Seric 
7634549Seric 		  case CMDNOOP:		/* noop -- do nothing */
76464122Seric 			message("250 OK");
7654549Seric 			break;
7664549Seric 
7674549Seric 		  case CMDQUIT:		/* quit -- leave mail */
76858151Seric 			message("221 %s closing connection", MyHostName);
76961051Seric 
77066283Seric doquit:
77168603Seric 			/* arrange to ignore any current send list */
77268603Seric 			e->e_sendqueue = NULL;
77368603Seric 
77461051Seric 			/* avoid future 050 messages */
77566017Seric 			disconnect(1, e);
77661051Seric 
7779339Seric 			if (InChild)
7789339Seric 				ExitStat = EX_QUIT;
7794549Seric 			finis();
7804549Seric 
7818544Seric 		  case CMDVERB:		/* set verbose mode */
78259957Seric 			if (bitset(PRIV_NOEXPN, PrivacyFlags))
78359957Seric 			{
78459957Seric 				/* this would give out the same info */
78559957Seric 				message("502 Verbose unavailable");
78659957Seric 				break;
78759957Seric 			}
7888544Seric 			Verbose = TRUE;
78958734Seric 			e->e_sendmode = SM_DELIVER;
79059957Seric 			message("250 Verbose mode");
7918544Seric 			break;
7928544Seric 
7939314Seric 		  case CMDONEX:		/* doing one transaction only */
7949378Seric 			OneXact = TRUE;
79559957Seric 			message("250 Only one transaction");
7969314Seric 			break;
7979314Seric 
79836230Skarels # ifdef SMTPDEBUG
7999339Seric 		  case CMDDBGQSHOW:	/* show queues */
8006907Seric 			printf("Send Queue=");
80155012Seric 			printaddr(e->e_sendqueue, TRUE);
8025003Seric 			break;
8037275Seric 
8047275Seric 		  case CMDDBGDEBUG:	/* set debug mode */
8057676Seric 			tTsetup(tTdvect, sizeof tTdvect, "0-99.1");
8067676Seric 			tTflag(p);
80758151Seric 			message("200 Debug set");
8087275Seric 			break;
8097275Seric 
81036230Skarels # else /* not SMTPDEBUG */
81136230Skarels 		  case CMDDBGQSHOW:	/* show queues */
81236230Skarels 		  case CMDDBGDEBUG:	/* set debug mode */
81364685Seric # endif /* SMTPDEBUG */
81464685Seric 		  case CMDLOGBOGUS:	/* bogus command */
81536233Skarels # ifdef LOG
81658308Seric 			if (LogLevel > 0)
81764685Seric 				syslog(LOG_CRIT,
81858020Seric 				    "\"%s\" command from %s (%s)",
81969788Seric 				    c->cmdname, CurSmtpClient,
82058755Seric 				    anynet_ntoa(&RealHostAddr));
82136233Skarels # endif
82236230Skarels 			/* FALL THROUGH */
82336230Skarels 
8244549Seric 		  case CMDERROR:	/* unknown command */
82566283Seric 			if (++badcommands > MAXBADCOMMANDS)
82666283Seric 			{
82766283Seric 				message("421 %s Too many bad commands; closing connection",
82866283Seric 					MyHostName);
82966283Seric 				goto doquit;
83066283Seric 			}
83166283Seric 
83258151Seric 			message("500 Command unrecognized");
8334549Seric 			break;
8344549Seric 
8354549Seric 		  default:
83636230Skarels 			errno = 0;
83758151Seric 			syserr("500 smtp: unknown code %d", c->cmdcode);
8384549Seric 			break;
8394549Seric 		}
8404549Seric 	}
8414549Seric }
8424549Seric /*
8434549Seric **  SKIPWORD -- skip a fixed word.
8444549Seric **
8454549Seric **	Parameters:
8464549Seric **		p -- place to start looking.
8474549Seric **		w -- word to skip.
8484549Seric **
8494549Seric **	Returns:
8504549Seric **		p following w.
8514549Seric **		NULL on error.
8524549Seric **
8534549Seric **	Side Effects:
8544549Seric **		clobbers the p data area.
8554549Seric */
8564549Seric 
8574549Seric static char *
skipword(p,w)8584549Seric skipword(p, w)
8594549Seric 	register char *p;
8604549Seric 	char *w;
8614549Seric {
8624549Seric 	register char *q;
86366005Seric 	char *firstp = p;
8644549Seric 
8654549Seric 	/* find beginning of word */
86658050Seric 	while (isascii(*p) && isspace(*p))
8674549Seric 		p++;
8684549Seric 	q = p;
8694549Seric 
8704549Seric 	/* find end of word */
87158050Seric 	while (*p != '\0' && *p != ':' && !(isascii(*p) && isspace(*p)))
8724549Seric 		p++;
87358050Seric 	while (isascii(*p) && isspace(*p))
8744549Seric 		*p++ = '\0';
8754549Seric 	if (*p != ':')
8764549Seric 	{
8774549Seric 	  syntax:
87866005Seric 		message("501 Syntax error in parameters scanning \"%s\"",
87966005Seric 			firstp);
8804549Seric 		Errors++;
8814549Seric 		return (NULL);
8824549Seric 	}
8834549Seric 	*p++ = '\0';
88458050Seric 	while (isascii(*p) && isspace(*p))
8854549Seric 		p++;
8864549Seric 
88762373Seric 	if (*p == '\0')
88862373Seric 		goto syntax;
88962373Seric 
8904549Seric 	/* see if the input word matches desired word */
89133725Sbostic 	if (strcasecmp(q, w))
8924549Seric 		goto syntax;
8934549Seric 
8944549Seric 	return (p);
8954549Seric }
8964577Seric /*
89768559Seric **  MAIL_ESMTP_ARGS -- process ESMTP arguments from MAIL line
89868559Seric **
89968559Seric **	Parameters:
90068559Seric **		kp -- the parameter key.
90168559Seric **		vp -- the value of that parameter.
90268559Seric **		e -- the envelope.
90368559Seric **
90468559Seric **	Returns:
90568559Seric **		none.
90668559Seric */
90768559Seric 
90869748Seric void
mail_esmtp_args(kp,vp,e)90968559Seric mail_esmtp_args(kp, vp, e)
91068559Seric 	char *kp;
91168559Seric 	char *vp;
91268559Seric 	ENVELOPE *e;
91368559Seric {
91468559Seric 	if (strcasecmp(kp, "size") == 0)
91568559Seric 	{
91668559Seric 		if (vp == NULL)
91768559Seric 		{
91868559Seric 			usrerr("501 SIZE requires a value");
91968559Seric 			/* NOTREACHED */
92068559Seric 		}
92168890Seric # if defined(__STDC__) && !defined(BROKEN_ANSI_LIBRARY)
92268560Seric 		e->e_msgsize = strtoul(vp, (char **) NULL, 10);
92368559Seric # else
92468560Seric 		e->e_msgsize = strtol(vp, (char **) NULL, 10);
92568559Seric # endif
92668559Seric 	}
92768559Seric 	else if (strcasecmp(kp, "body") == 0)
92868559Seric 	{
92968559Seric 		if (vp == NULL)
93068559Seric 		{
93168559Seric 			usrerr("501 BODY requires a value");
93268559Seric 			/* NOTREACHED */
93368559Seric 		}
934*69972Seric 		else if (strcasecmp(vp, "8bitmime") == 0)
93568559Seric 		{
93668559Seric 			SevenBitInput = FALSE;
93768559Seric 		}
93868559Seric 		else if (strcasecmp(vp, "7bit") == 0)
93968559Seric 		{
94068559Seric 			SevenBitInput = TRUE;
94168559Seric 		}
94268559Seric 		else
94368559Seric 		{
94468559Seric 			usrerr("501 Unknown BODY type %s",
94568559Seric 				vp);
94668559Seric 			/* NOTREACHED */
94768559Seric 		}
94868559Seric 		e->e_bodytype = newstr(vp);
94968559Seric 	}
95068559Seric 	else if (strcasecmp(kp, "envid") == 0)
95168559Seric 	{
95268559Seric 		if (vp == NULL)
95368559Seric 		{
95468559Seric 			usrerr("501 ENVID requires a value");
95568559Seric 			/* NOTREACHED */
95668559Seric 		}
95768583Seric 		if (!xtextok(vp))
95868583Seric 		{
95968583Seric 			usrerr("501 Syntax error in ENVID parameter value");
96068583Seric 			/* NOTREACHED */
96168583Seric 		}
96268583Seric 		if (e->e_envid != NULL)
96368583Seric 		{
96468583Seric 			usrerr("501 Duplicate ENVID parameter");
96568583Seric 			/* NOTREACHED */
96668583Seric 		}
96768559Seric 		e->e_envid = newstr(vp);
96868559Seric 	}
96968559Seric 	else if (strcasecmp(kp, "ret") == 0)
97068559Seric 	{
97168559Seric 		if (vp == NULL)
97268559Seric 		{
97368559Seric 			usrerr("501 RET requires a value");
97468559Seric 			/* NOTREACHED */
97568559Seric 		}
97668583Seric 		if (bitset(EF_RET_PARAM, e->e_flags))
97768583Seric 		{
97868583Seric 			usrerr("501 Duplicate RET parameter");
97968583Seric 			/* NOTREACHED */
98068583Seric 		}
98168559Seric 		e->e_flags |= EF_RET_PARAM;
98268559Seric 		if (strcasecmp(vp, "hdrs") == 0)
98368559Seric 			e->e_flags |= EF_NO_BODY_RETN;
98468559Seric 		else if (strcasecmp(vp, "full") != 0)
98568559Seric 		{
98668559Seric 			usrerr("501 Bad argument \"%s\" to RET", vp);
98768559Seric 			/* NOTREACHED */
98868559Seric 		}
98968559Seric 	}
99068559Seric 	else
99168559Seric 	{
99268559Seric 		usrerr("501 %s parameter unrecognized", kp);
99368559Seric 		/* NOTREACHED */
99468559Seric 	}
99568559Seric }
99668559Seric /*
99767963Seric **  RCPT_ESMTP_ARGS -- process ESMTP arguments from RCPT line
99867963Seric **
99967963Seric **	Parameters:
100067963Seric **		a -- the address corresponding to the To: parameter.
100167963Seric **		kp -- the parameter key.
100267963Seric **		vp -- the value of that parameter.
100367963Seric **		e -- the envelope.
100467963Seric **
100567963Seric **	Returns:
100667963Seric **		none.
100767963Seric */
100867963Seric 
100969748Seric void
rcpt_esmtp_args(a,kp,vp,e)101067963Seric rcpt_esmtp_args(a, kp, vp, e)
101167963Seric 	ADDRESS *a;
101267963Seric 	char *kp;
101367963Seric 	char *vp;
101467963Seric 	ENVELOPE *e;
101567963Seric {
101667963Seric 	if (strcasecmp(kp, "notify") == 0)
101767963Seric 	{
101867963Seric 		char *p;
101967963Seric 
102067963Seric 		if (vp == NULL)
102167963Seric 		{
102267963Seric 			usrerr("501 NOTIFY requires a value");
102367963Seric 			/* NOTREACHED */
102467963Seric 		}
102567963Seric 		a->q_flags &= ~(QPINGONSUCCESS|QPINGONFAILURE|QPINGONDELAY);
102668595Seric 		a->q_flags |= QHASNOTIFY;
102767963Seric 		if (strcasecmp(vp, "never") == 0)
102867963Seric 			return;
102967963Seric 		for (p = vp; p != NULL; vp = p)
103067963Seric 		{
103167963Seric 			p = strchr(p, ',');
103267963Seric 			if (p != NULL)
103367963Seric 				*p++ = '\0';
103467963Seric 			if (strcasecmp(vp, "success") == 0)
103567963Seric 				a->q_flags |= QPINGONSUCCESS;
103667963Seric 			else if (strcasecmp(vp, "failure") == 0)
103767963Seric 				a->q_flags |= QPINGONFAILURE;
103867963Seric 			else if (strcasecmp(vp, "delay") == 0)
103967963Seric 				a->q_flags |= QPINGONDELAY;
104067963Seric 			else
104167963Seric 			{
104267963Seric 				usrerr("501 Bad argument \"%s\"  to NOTIFY",
104367963Seric 					vp);
104467963Seric 				/* NOTREACHED */
104567963Seric 			}
104667963Seric 		}
104767963Seric 	}
104867963Seric 	else if (strcasecmp(kp, "orcpt") == 0)
104967963Seric 	{
105067963Seric 		if (vp == NULL)
105167963Seric 		{
105267963Seric 			usrerr("501 ORCPT requires a value");
105367963Seric 			/* NOTREACHED */
105467963Seric 		}
105569888Seric 		if (strchr(vp, ';') == NULL || !xtextok(vp))
105668583Seric 		{
105768583Seric 			usrerr("501 Syntax error in ORCPT parameter value");
105868583Seric 			/* NOTREACHED */
105968583Seric 		}
106068583Seric 		if (a->q_orcpt != NULL)
106168583Seric 		{
106268583Seric 			usrerr("501 Duplicate ORCPT parameter");
106368583Seric 			/* NOTREACHED */
106468583Seric 		}
106567963Seric 		a->q_orcpt = newstr(vp);
106667963Seric 	}
106767963Seric 	else
106867963Seric 	{
106967963Seric 		usrerr("501 %s parameter unrecognized", kp);
107067963Seric 		/* NOTREACHED */
107167963Seric 	}
107267963Seric }
107367963Seric /*
107458151Seric **  PRINTVRFYADDR -- print an entry in the verify queue
107558151Seric **
107658151Seric **	Parameters:
107758151Seric **		a -- the address to print
107858151Seric **		last -- set if this is the last one.
107958151Seric **
108058151Seric **	Returns:
108158151Seric **		none.
108258151Seric **
108358151Seric **	Side Effects:
108458151Seric **		Prints the appropriate 250 codes.
108558151Seric */
108658151Seric 
108769748Seric void
printvrfyaddr(a,last)108858151Seric printvrfyaddr(a, last)
108958151Seric 	register ADDRESS *a;
109058151Seric 	bool last;
109158151Seric {
109258151Seric 	char fmtbuf[20];
109358151Seric 
109458151Seric 	strcpy(fmtbuf, "250");
109558151Seric 	fmtbuf[3] = last ? ' ' : '-';
109658151Seric 
109759746Seric 	if (a->q_fullname == NULL)
109859746Seric 	{
109959746Seric 		if (strchr(a->q_user, '@') == NULL)
110059746Seric 			strcpy(&fmtbuf[4], "<%s@%s>");
110159746Seric 		else
110259746Seric 			strcpy(&fmtbuf[4], "<%s>");
110359746Seric 		message(fmtbuf, a->q_user, MyHostName);
110459746Seric 	}
110558151Seric 	else
110658151Seric 	{
110759746Seric 		if (strchr(a->q_user, '@') == NULL)
110859746Seric 			strcpy(&fmtbuf[4], "%s <%s@%s>");
110959746Seric 		else
111059746Seric 			strcpy(&fmtbuf[4], "%s <%s>");
111159746Seric 		message(fmtbuf, a->q_fullname, a->q_user, MyHostName);
111258151Seric 	}
111358151Seric }
111458151Seric /*
11154577Seric **  HELP -- implement the HELP command.
11164577Seric **
11174577Seric **	Parameters:
11184577Seric **		topic -- the topic we want help for.
11194577Seric **
11204577Seric **	Returns:
11214577Seric **		none.
11224577Seric **
11234577Seric **	Side Effects:
11244577Seric **		outputs the help file to message output.
11254577Seric */
11264577Seric 
112769748Seric void
help(topic)11284577Seric help(topic)
11294577Seric 	char *topic;
11304577Seric {
11314577Seric 	register FILE *hf;
11324577Seric 	int len;
113368751Seric 	bool noinfo;
11344577Seric 	char buf[MAXLINE];
113568751Seric 	extern char Version[];
11364577Seric 
113768751Seric 
11388269Seric 	if (HelpFile == NULL || (hf = fopen(HelpFile, "r")) == NULL)
11394577Seric 	{
11404577Seric 		/* no help */
114111931Seric 		errno = 0;
114268751Seric 		message("502 Sendmail %s -- HELP not implemented", Version);
11434577Seric 		return;
11444577Seric 	}
11454577Seric 
114649669Seric 	if (topic == NULL || *topic == '\0')
114768751Seric 	{
114849669Seric 		topic = "smtp";
114968751Seric 		message("214-This is Sendmail version %s", Version);
115068751Seric 		noinfo = FALSE;
115168751Seric 	}
115249669Seric 	else
115368751Seric 	{
115449669Seric 		makelower(topic);
115568751Seric 		noinfo = TRUE;
115668751Seric 	}
115749669Seric 
11584577Seric 	len = strlen(topic);
11594577Seric 
11604577Seric 	while (fgets(buf, sizeof buf, hf) != NULL)
11614577Seric 	{
11624577Seric 		if (strncmp(buf, topic, len) == 0)
11634577Seric 		{
11644577Seric 			register char *p;
11654577Seric 
116656795Seric 			p = strchr(buf, '\t');
11674577Seric 			if (p == NULL)
11684577Seric 				p = buf;
11694577Seric 			else
11704577Seric 				p++;
11714577Seric 			fixcrlf(p, TRUE);
117258151Seric 			message("214-%s", p);
11734577Seric 			noinfo = FALSE;
11744577Seric 		}
11754577Seric 	}
11764577Seric 
11774577Seric 	if (noinfo)
117858151Seric 		message("504 HELP topic unknown");
11794577Seric 	else
118058151Seric 		message("214 End of HELP info");
11814628Seric 	(void) fclose(hf);
11824577Seric }
11838544Seric /*
11849339Seric **  RUNINCHILD -- return twice -- once in the child, then in the parent again
11859339Seric **
11869339Seric **	Parameters:
11879339Seric **		label -- a string used in error messages
11889339Seric **
11899339Seric **	Returns:
11909339Seric **		zero in the child
11919339Seric **		one in the parent
11929339Seric **
11939339Seric **	Side Effects:
11949339Seric **		none.
11959339Seric */
11968544Seric 
119769748Seric int
runinchild(label,e)119855012Seric runinchild(label, e)
11999339Seric 	char *label;
120055012Seric 	register ENVELOPE *e;
12019339Seric {
12029339Seric 	int childpid;
12039339Seric 
120416158Seric 	if (!OneXact)
12059339Seric 	{
120616158Seric 		childpid = dofork();
120716158Seric 		if (childpid < 0)
120816158Seric 		{
120969716Seric 			syserr("451 %s: cannot fork", label);
121016158Seric 			return (1);
121116158Seric 		}
121216158Seric 		if (childpid > 0)
121316158Seric 		{
121416158Seric 			auto int st;
12159339Seric 
121616158Seric 			/* parent -- wait for child to complete */
121769788Seric 			setproctitle("server %s child wait", CurSmtpClient);
121816158Seric 			st = waitfor(childpid);
121916158Seric 			if (st == -1)
122069716Seric 				syserr("451 %s: lost child", label);
122164948Seric 			else if (!WIFEXITED(st))
122269716Seric 				syserr("451 %s: died on signal %d",
122364948Seric 					label, st & 0177);
12249339Seric 
122516158Seric 			/* if we exited on a QUIT command, complete the process */
122666017Seric 			if (WEXITSTATUS(st) == EX_QUIT)
122766017Seric 			{
122866017Seric 				disconnect(1, e);
122916158Seric 				finis();
123066017Seric 			}
12319339Seric 
123216158Seric 			return (1);
123316158Seric 		}
123416158Seric 		else
123516158Seric 		{
123616158Seric 			/* child */
123716158Seric 			InChild = TRUE;
123825050Seric 			QuickAbort = FALSE;
123955012Seric 			clearenvelope(e, FALSE);
124016158Seric 		}
12419339Seric 	}
124215256Seric 
124316158Seric 	/* open alias database */
124460537Seric 	initmaps(FALSE, e);
124516158Seric 
124616158Seric 	return (0);
12479339Seric }
12489339Seric 
124956795Seric # endif /* SMTP */
1250