deliver.c revision 125820
138032Speter/*
2111823Sgshapiro * Copyright (c) 1998-2003 Sendmail, Inc. and its suppliers.
364562Sgshapiro *	All rights reserved.
438032Speter * Copyright (c) 1983, 1995-1997 Eric P. Allman.  All rights reserved.
538032Speter * Copyright (c) 1988, 1993
638032Speter *	The Regents of the University of California.  All rights reserved.
738032Speter *
838032Speter * By using this file, you agree to the terms and conditions set
938032Speter * forth in the LICENSE file which can be found at the top level of
1038032Speter * the sendmail distribution.
1138032Speter *
1238032Speter */
1338032Speter
1464562Sgshapiro#include <sendmail.h>
1590792Sgshapiro#include <sys/time.h>
1638032Speter
17125820SgshapiroSM_RCSID("@(#)$Id: deliver.c,v 8.940.2.20 2003/09/26 18:26:19 ca Exp $")
1864562Sgshapiro
1938032Speter#if HASSETUSERCONTEXT
2038032Speter# include <login_cap.h>
2164562Sgshapiro#endif /* HASSETUSERCONTEXT */
2238032Speter
2394334Sgshapiro#if NETINET || NETINET6
2494334Sgshapiro# include <arpa/inet.h>
2594334Sgshapiro#endif /* NETINET || NETINET6 */
2694334Sgshapiro
2790792Sgshapiro#if STARTTLS || SASL
2864562Sgshapiro# include "sfsasl.h"
2990792Sgshapiro#endif /* STARTTLS || SASL */
3064562Sgshapiro
3190792Sgshapirovoid		markfailure __P((ENVELOPE *, ADDRESS *, MCI *, int, bool));
3264562Sgshapirostatic int	deliver __P((ENVELOPE *, ADDRESS *));
3364562Sgshapirostatic void	dup_queue_file __P((ENVELOPE *, ENVELOPE *, int));
3464562Sgshapirostatic void	mailfiletimeout __P((void));
3564562Sgshapirostatic int	parse_hostsignature __P((char *, char **, MAILER *));
3664562Sgshapirostatic void	sendenvelope __P((ENVELOPE *, int));
3790792Sgshapiroextern MCI	*mci_new __P((SM_RPOOL_T *));
3890792Sgshapirostatic int	coloncmp __P((const char *, const char *));
3964562Sgshapiro
4090792Sgshapiro#if STARTTLS
4164562Sgshapirostatic int	starttls __P((MAILER *, MCI *, ENVELOPE *));
4290792Sgshapirostatic int	endtlsclt __P((MCI *));
4390792Sgshapiro#endif /* STARTTLS */
4490792Sgshapiro# if STARTTLS || SASL
4590792Sgshapirostatic bool	iscltflgset __P((ENVELOPE *, int));
4690792Sgshapiro# endif /* STARTTLS || SASL */
4738032Speter
4838032Speter/*
4938032Speter**  SENDALL -- actually send all the messages.
5038032Speter**
5138032Speter**	Parameters:
5238032Speter**		e -- the envelope to send.
5338032Speter**		mode -- the delivery mode to use.  If SM_DEFAULT, use
5438032Speter**			the current e->e_sendmode.
5538032Speter**
5638032Speter**	Returns:
5738032Speter**		none.
5838032Speter**
5938032Speter**	Side Effects:
6038032Speter**		Scans the send lists and sends everything it finds.
6138032Speter**		Delivers any appropriate error messages.
6238032Speter**		If we are running in a non-interactive mode, takes the
6338032Speter**			appropriate action.
6438032Speter*/
6538032Speter
6638032Spetervoid
6738032Spetersendall(e, mode)
6838032Speter	ENVELOPE *e;
6938032Speter	int mode;
7038032Speter{
7138032Speter	register ADDRESS *q;
7238032Speter	char *owner;
7338032Speter	int otherowners;
7464562Sgshapiro	int save_errno;
7538032Speter	register ENVELOPE *ee;
7638032Speter	ENVELOPE *splitenv = NULL;
7738032Speter	int oldverbose = Verbose;
7890792Sgshapiro	bool somedeliveries = false, expensive = false;
7938032Speter	pid_t pid;
8038032Speter
8138032Speter	/*
8238032Speter	**  If this message is to be discarded, don't bother sending
8338032Speter	**  the message at all.
8438032Speter	*/
8538032Speter
8638032Speter	if (bitset(EF_DISCARD, e->e_flags))
8738032Speter	{
8838032Speter		if (tTd(13, 1))
8990792Sgshapiro			sm_dprintf("sendall: discarding id %s\n", e->e_id);
9038032Speter		e->e_flags |= EF_CLRQUEUE;
9190792Sgshapiro		if (LogLevel > 9)
9290792Sgshapiro			logundelrcpts(e, "discarded", 9, true);
9390792Sgshapiro		else if (LogLevel > 4)
9438032Speter			sm_syslog(LOG_INFO, e->e_id, "discarded");
9590792Sgshapiro		markstats(e, NULL, STATS_REJECT);
9638032Speter		return;
9738032Speter	}
9838032Speter
9938032Speter	/*
10038032Speter	**  If we have had global, fatal errors, don't bother sending
10138032Speter	**  the message at all if we are in SMTP mode.  Local errors
10238032Speter	**  (e.g., a single address failing) will still cause the other
10338032Speter	**  addresses to be sent.
10438032Speter	*/
10538032Speter
10638032Speter	if (bitset(EF_FATALERRS, e->e_flags) &&
10738032Speter	    (OpMode == MD_SMTP || OpMode == MD_DAEMON))
10838032Speter	{
10938032Speter		e->e_flags |= EF_CLRQUEUE;
11038032Speter		return;
11138032Speter	}
11238032Speter
11338032Speter	/* determine actual delivery mode */
11438032Speter	if (mode == SM_DEFAULT)
11538032Speter	{
11638032Speter		mode = e->e_sendmode;
11738032Speter		if (mode != SM_VERIFY && mode != SM_DEFER &&
11838032Speter		    shouldqueue(e->e_msgpriority, e->e_ctime))
11938032Speter			mode = SM_QUEUE;
12038032Speter	}
12138032Speter
12238032Speter	if (tTd(13, 1))
12338032Speter	{
12490792Sgshapiro		sm_dprintf("\n===== SENDALL: mode %c, id %s, e_from ",
12538032Speter			mode, e->e_id);
12690792Sgshapiro		printaddr(&e->e_from, false);
12790792Sgshapiro		sm_dprintf("\te_flags = ");
12838032Speter		printenvflags(e);
12990792Sgshapiro		sm_dprintf("sendqueue:\n");
13090792Sgshapiro		printaddr(e->e_sendqueue, true);
13138032Speter	}
13238032Speter
13338032Speter	/*
13438032Speter	**  Do any preprocessing necessary for the mode we are running.
13538032Speter	**	Check to make sure the hop count is reasonable.
13638032Speter	**	Delete sends to the sender in mailing lists.
13738032Speter	*/
13838032Speter
13938032Speter	CurEnv = e;
14038032Speter	if (tTd(62, 1))
14138032Speter		checkfds(NULL);
14238032Speter
14338032Speter	if (e->e_hopcount > MaxHopCount)
14438032Speter	{
14577349Sgshapiro		char *recip;
14677349Sgshapiro
14777349Sgshapiro		if (e->e_sendqueue != NULL &&
14877349Sgshapiro		    e->e_sendqueue->q_paddr != NULL)
14977349Sgshapiro			recip = e->e_sendqueue->q_paddr;
15077349Sgshapiro		else
15177349Sgshapiro			recip = "(nobody)";
15277349Sgshapiro
15338032Speter		errno = 0;
15490792Sgshapiro		queueup(e, WILL_BE_QUEUED(mode), false);
15538032Speter		e->e_flags |= EF_FATALERRS|EF_PM_NOTIFY|EF_CLRQUEUE;
15664562Sgshapiro		ExitStat = EX_UNAVAILABLE;
15777349Sgshapiro		syserr("554 5.4.6 Too many hops %d (%d max): from %s via %s, to %s",
15877349Sgshapiro		       e->e_hopcount, MaxHopCount, e->e_from.q_paddr,
15977349Sgshapiro		       RealHostName == NULL ? "localhost" : RealHostName,
16077349Sgshapiro		       recip);
16164562Sgshapiro		for (q = e->e_sendqueue; q != NULL; q = q->q_next)
16264562Sgshapiro		{
16364562Sgshapiro			if (QS_IS_DEAD(q->q_state))
16464562Sgshapiro				continue;
16564562Sgshapiro			q->q_state = QS_BADADDR;
16664562Sgshapiro			q->q_status = "5.4.6";
16777349Sgshapiro			q->q_rstatus = "554 5.4.6 Too many hops";
16864562Sgshapiro		}
16938032Speter		return;
17038032Speter	}
17138032Speter
17238032Speter	/*
17338032Speter	**  Do sender deletion.
17438032Speter	**
17564562Sgshapiro	**	If the sender should be queued up, skip this.
17638032Speter	**	This can happen if the name server is hosed when you
17738032Speter	**	are trying to send mail.  The result is that the sender
17838032Speter	**	is instantiated in the queue as a recipient.
17938032Speter	*/
18038032Speter
18138032Speter	if (!bitset(EF_METOO, e->e_flags) &&
18264562Sgshapiro	    !QS_IS_QUEUEUP(e->e_from.q_state))
18338032Speter	{
18438032Speter		if (tTd(13, 5))
18538032Speter		{
18690792Sgshapiro			sm_dprintf("sendall: QS_SENDER ");
18790792Sgshapiro			printaddr(&e->e_from, false);
18838032Speter		}
18964562Sgshapiro		e->e_from.q_state = QS_SENDER;
19038032Speter		(void) recipient(&e->e_from, &e->e_sendqueue, 0, e);
19138032Speter	}
19238032Speter
19338032Speter	/*
19438032Speter	**  Handle alias owners.
19538032Speter	**
19638032Speter	**	We scan up the q_alias chain looking for owners.
19738032Speter	**	We discard owners that are the same as the return path.
19838032Speter	*/
19938032Speter
20038032Speter	for (q = e->e_sendqueue; q != NULL; q = q->q_next)
20138032Speter	{
20238032Speter		register struct address *a;
20338032Speter
20438032Speter		for (a = q; a != NULL && a->q_owner == NULL; a = a->q_alias)
20538032Speter			continue;
20638032Speter		if (a != NULL)
20738032Speter			q->q_owner = a->q_owner;
20838032Speter
20938032Speter		if (q->q_owner != NULL &&
21064562Sgshapiro		    !QS_IS_DEAD(q->q_state) &&
21138032Speter		    strcmp(q->q_owner, e->e_from.q_paddr) == 0)
21238032Speter			q->q_owner = NULL;
21338032Speter	}
21438032Speter
21538032Speter	if (tTd(13, 25))
21638032Speter	{
21790792Sgshapiro		sm_dprintf("\nAfter first owner pass, sendq =\n");
21890792Sgshapiro		printaddr(e->e_sendqueue, true);
21938032Speter	}
22038032Speter
22138032Speter	owner = "";
22238032Speter	otherowners = 1;
22338032Speter	while (owner != NULL && otherowners > 0)
22438032Speter	{
22538032Speter		if (tTd(13, 28))
22690792Sgshapiro			sm_dprintf("owner = \"%s\", otherowners = %d\n",
22790792Sgshapiro				   owner, otherowners);
22838032Speter		owner = NULL;
22938032Speter		otherowners = bitset(EF_SENDRECEIPT, e->e_flags) ? 1 : 0;
23038032Speter
23138032Speter		for (q = e->e_sendqueue; q != NULL; q = q->q_next)
23238032Speter		{
23338032Speter			if (tTd(13, 30))
23438032Speter			{
23590792Sgshapiro				sm_dprintf("Checking ");
23690792Sgshapiro				printaddr(q, false);
23738032Speter			}
23864562Sgshapiro			if (QS_IS_DEAD(q->q_state))
23938032Speter			{
24038032Speter				if (tTd(13, 30))
24190792Sgshapiro					sm_dprintf("    ... QS_IS_DEAD\n");
24238032Speter				continue;
24338032Speter			}
24438032Speter			if (tTd(13, 29) && !tTd(13, 30))
24538032Speter			{
24690792Sgshapiro				sm_dprintf("Checking ");
24790792Sgshapiro				printaddr(q, false);
24838032Speter			}
24938032Speter
25038032Speter			if (q->q_owner != NULL)
25138032Speter			{
25238032Speter				if (owner == NULL)
25338032Speter				{
25438032Speter					if (tTd(13, 40))
25590792Sgshapiro						sm_dprintf("    ... First owner = \"%s\"\n",
25690792Sgshapiro							   q->q_owner);
25738032Speter					owner = q->q_owner;
25838032Speter				}
25938032Speter				else if (owner != q->q_owner)
26038032Speter				{
26138032Speter					if (strcmp(owner, q->q_owner) == 0)
26238032Speter					{
26338032Speter						if (tTd(13, 40))
26490792Sgshapiro							sm_dprintf("    ... Same owner = \"%s\"\n",
26590792Sgshapiro								   owner);
26638032Speter
26738032Speter						/* make future comparisons cheap */
26838032Speter						q->q_owner = owner;
26938032Speter					}
27038032Speter					else
27138032Speter					{
27238032Speter						if (tTd(13, 40))
27390792Sgshapiro							sm_dprintf("    ... Another owner \"%s\"\n",
27490792Sgshapiro								   q->q_owner);
27538032Speter						otherowners++;
27638032Speter					}
27738032Speter					owner = q->q_owner;
27838032Speter				}
27938032Speter				else if (tTd(13, 40))
28090792Sgshapiro					sm_dprintf("    ... Same owner = \"%s\"\n",
28190792Sgshapiro						   owner);
28238032Speter			}
28338032Speter			else
28438032Speter			{
28538032Speter				if (tTd(13, 40))
28690792Sgshapiro					sm_dprintf("    ... Null owner\n");
28738032Speter				otherowners++;
28838032Speter			}
28938032Speter
29064562Sgshapiro			if (QS_IS_BADADDR(q->q_state))
29164562Sgshapiro			{
29264562Sgshapiro				if (tTd(13, 30))
29390792Sgshapiro					sm_dprintf("    ... QS_IS_BADADDR\n");
29464562Sgshapiro				continue;
29564562Sgshapiro			}
29664562Sgshapiro
29764562Sgshapiro			if (QS_IS_QUEUEUP(q->q_state))
29864562Sgshapiro			{
29964562Sgshapiro				MAILER *m = q->q_mailer;
30064562Sgshapiro
30164562Sgshapiro				/*
30264562Sgshapiro				**  If we have temporary address failures
30364562Sgshapiro				**  (e.g., dns failure) and a fallback MX is
30464562Sgshapiro				**  set, send directly to the fallback MX host.
30564562Sgshapiro				*/
30664562Sgshapiro
30764562Sgshapiro				if (FallBackMX != NULL &&
30864562Sgshapiro				    !wordinclass(FallBackMX, 'w') &&
30964562Sgshapiro				    mode != SM_VERIFY &&
31090792Sgshapiro				    !bitnset(M_NOMX, m->m_flags) &&
31190792Sgshapiro				    strcmp(m->m_mailer, "[IPC]") == 0 &&
31264562Sgshapiro				    m->m_argv[0] != NULL &&
31390792Sgshapiro				    strcmp(m->m_argv[0], "TCP") == 0)
31464562Sgshapiro				{
31564562Sgshapiro					int len;
31664562Sgshapiro					char *p;
31764562Sgshapiro
31864562Sgshapiro					if (tTd(13, 30))
31990792Sgshapiro						sm_dprintf("    ... FallBackMX\n");
32064562Sgshapiro
32190792Sgshapiro					len = strlen(FallBackMX) + 1;
32290792Sgshapiro					p = sm_rpool_malloc_x(e->e_rpool, len);
32390792Sgshapiro					(void) sm_strlcpy(p, FallBackMX, len);
32464562Sgshapiro					q->q_state = QS_OK;
32564562Sgshapiro					q->q_host = p;
32664562Sgshapiro				}
32764562Sgshapiro				else
32864562Sgshapiro				{
32964562Sgshapiro					if (tTd(13, 30))
33090792Sgshapiro						sm_dprintf("    ... QS_IS_QUEUEUP\n");
33164562Sgshapiro					continue;
33264562Sgshapiro				}
33364562Sgshapiro			}
33464562Sgshapiro
33538032Speter			/*
33638032Speter			**  If this mailer is expensive, and if we don't
33738032Speter			**  want to make connections now, just mark these
33838032Speter			**  addresses and return.  This is useful if we
33938032Speter			**  want to batch connections to reduce load.  This
34038032Speter			**  will cause the messages to be queued up, and a
34138032Speter			**  daemon will come along to send the messages later.
34238032Speter			*/
34338032Speter
34464562Sgshapiro			if (NoConnect && !Verbose &&
34564562Sgshapiro			    bitnset(M_EXPENSIVE, q->q_mailer->m_flags))
34638032Speter			{
34738032Speter				if (tTd(13, 30))
34890792Sgshapiro					sm_dprintf("    ... expensive\n");
34964562Sgshapiro				q->q_state = QS_QUEUEUP;
35090792Sgshapiro				expensive = true;
35138032Speter			}
35264562Sgshapiro			else if (bitnset(M_HOLD, q->q_mailer->m_flags) &&
35364562Sgshapiro				 QueueLimitId == NULL &&
35464562Sgshapiro				 QueueLimitSender == NULL &&
35564562Sgshapiro				 QueueLimitRecipient == NULL)
35638032Speter			{
35738032Speter				if (tTd(13, 30))
35890792Sgshapiro					sm_dprintf("    ... hold\n");
35964562Sgshapiro				q->q_state = QS_QUEUEUP;
36090792Sgshapiro				expensive = true;
36138032Speter			}
36290792Sgshapiro#if _FFR_QUARANTINE
36390792Sgshapiro			else if (QueueMode != QM_QUARANTINE &&
36490792Sgshapiro				 e->e_quarmsg != NULL)
36590792Sgshapiro			{
36690792Sgshapiro				if (tTd(13, 30))
36790792Sgshapiro					sm_dprintf("    ... quarantine: %s\n",
36890792Sgshapiro						   e->e_quarmsg);
36990792Sgshapiro				q->q_state = QS_QUEUEUP;
37090792Sgshapiro				expensive = true;
37190792Sgshapiro			}
37290792Sgshapiro#endif /* _FFR_QUARANTINE */
37338032Speter			else
37438032Speter			{
37538032Speter				if (tTd(13, 30))
37690792Sgshapiro					sm_dprintf("    ... deliverable\n");
37790792Sgshapiro				somedeliveries = true;
37838032Speter			}
37938032Speter		}
38038032Speter
38138032Speter		if (owner != NULL && otherowners > 0)
38238032Speter		{
38338032Speter			/*
38438032Speter			**  Split this envelope into two.
38538032Speter			*/
38638032Speter
38790792Sgshapiro			ee = (ENVELOPE *) sm_rpool_malloc_x(e->e_rpool,
38890792Sgshapiro							    sizeof *ee);
38990792Sgshapiro			STRUCTCOPY(*e, *ee);
39064562Sgshapiro			ee->e_message = NULL;
39138032Speter			ee->e_id = NULL;
39264562Sgshapiro			assign_queueid(ee);
39338032Speter
39438032Speter			if (tTd(13, 1))
39590792Sgshapiro				sm_dprintf("sendall: split %s into %s, owner = \"%s\", otherowners = %d\n",
39690792Sgshapiro					   e->e_id, ee->e_id, owner,
39790792Sgshapiro					   otherowners);
39838032Speter
39990792Sgshapiro			ee->e_header = copyheader(e->e_header, ee->e_rpool);
40090792Sgshapiro			ee->e_sendqueue = copyqueue(e->e_sendqueue,
40190792Sgshapiro						    ee->e_rpool);
40290792Sgshapiro			ee->e_errorqueue = copyqueue(e->e_errorqueue,
40390792Sgshapiro						     ee->e_rpool);
40438032Speter			ee->e_flags = e->e_flags & ~(EF_INQUEUE|EF_CLRQUEUE|EF_FATALERRS|EF_SENDRECEIPT|EF_RET_PARAM);
40538032Speter			ee->e_flags |= EF_NORECEIPT;
40690792Sgshapiro			setsender(owner, ee, NULL, '\0', true);
40738032Speter			if (tTd(13, 5))
40838032Speter			{
40990792Sgshapiro				sm_dprintf("sendall(split): QS_SENDER ");
41090792Sgshapiro				printaddr(&ee->e_from, false);
41138032Speter			}
41264562Sgshapiro			ee->e_from.q_state = QS_SENDER;
41338032Speter			ee->e_dfp = NULL;
41464562Sgshapiro			ee->e_lockfp = NULL;
41538032Speter			ee->e_xfp = NULL;
41690792Sgshapiro			ee->e_qgrp = e->e_qgrp;
41790792Sgshapiro			ee->e_qdir = e->e_qdir;
41838032Speter			ee->e_errormode = EM_MAIL;
41938032Speter			ee->e_sibling = splitenv;
42064562Sgshapiro			ee->e_statmsg = NULL;
42190792Sgshapiro#if _FFR_QUARANTINE
42290792Sgshapiro			if (e->e_quarmsg != NULL)
42390792Sgshapiro				ee->e_quarmsg = sm_rpool_strdup_x(ee->e_rpool,
42490792Sgshapiro								  e->e_quarmsg);
42590792Sgshapiro#endif /* _FFR_QUARANTINE */
42638032Speter			splitenv = ee;
42738032Speter
42838032Speter			for (q = e->e_sendqueue; q != NULL; q = q->q_next)
42938032Speter			{
43038032Speter				if (q->q_owner == owner)
43138032Speter				{
43264562Sgshapiro					q->q_state = QS_CLONED;
43338032Speter					if (tTd(13, 6))
43490792Sgshapiro						sm_dprintf("\t... stripping %s from original envelope\n",
43590792Sgshapiro							   q->q_paddr);
43638032Speter				}
43738032Speter			}
43838032Speter			for (q = ee->e_sendqueue; q != NULL; q = q->q_next)
43938032Speter			{
44038032Speter				if (q->q_owner != owner)
44138032Speter				{
44264562Sgshapiro					q->q_state = QS_CLONED;
44338032Speter					if (tTd(13, 6))
44490792Sgshapiro						sm_dprintf("\t... dropping %s from cloned envelope\n",
44590792Sgshapiro							   q->q_paddr);
44638032Speter				}
44738032Speter				else
44838032Speter				{
44938032Speter					/* clear DSN parameters */
45038032Speter					q->q_flags &= ~(QHASNOTIFY|Q_PINGFLAGS);
45138032Speter					q->q_flags |= DefaultNotify & ~QPINGONSUCCESS;
45238032Speter					if (tTd(13, 6))
45390792Sgshapiro						sm_dprintf("\t... moving %s to cloned envelope\n",
45490792Sgshapiro							   q->q_paddr);
45538032Speter				}
45638032Speter			}
45738032Speter
45838032Speter			if (mode != SM_VERIFY && bitset(EF_HAS_DF, e->e_flags))
45990792Sgshapiro				dup_queue_file(e, ee, DATAFL_LETTER);
46064562Sgshapiro
46164562Sgshapiro			/*
46264562Sgshapiro			**  Give the split envelope access to the parent
46364562Sgshapiro			**  transcript file for errors obtained while
46464562Sgshapiro			**  processing the recipients (done before the
46564562Sgshapiro			**  envelope splitting).
46664562Sgshapiro			*/
46764562Sgshapiro
46864562Sgshapiro			if (e->e_xfp != NULL)
46990792Sgshapiro				ee->e_xfp = sm_io_dup(e->e_xfp);
47064562Sgshapiro
47164562Sgshapiro			/* failed to dup e->e_xfp, start a new transcript */
47264562Sgshapiro			if (ee->e_xfp == NULL)
47364562Sgshapiro				openxscript(ee);
47464562Sgshapiro
47542575Speter			if (mode != SM_VERIFY && LogLevel > 4)
47690792Sgshapiro				sm_syslog(LOG_INFO, e->e_id,
47790792Sgshapiro					  "%s: clone: owner=%s",
47890792Sgshapiro					  ee->e_id, owner);
47938032Speter		}
48038032Speter	}
48138032Speter
48238032Speter	if (owner != NULL)
48338032Speter	{
48490792Sgshapiro		setsender(owner, e, NULL, '\0', true);
48538032Speter		if (tTd(13, 5))
48638032Speter		{
48790792Sgshapiro			sm_dprintf("sendall(owner): QS_SENDER ");
48890792Sgshapiro			printaddr(&e->e_from, false);
48938032Speter		}
49064562Sgshapiro		e->e_from.q_state = QS_SENDER;
49138032Speter		e->e_errormode = EM_MAIL;
49238032Speter		e->e_flags |= EF_NORECEIPT;
49338032Speter		e->e_flags &= ~EF_FATALERRS;
49438032Speter	}
49538032Speter
49638032Speter	/* if nothing to be delivered, just queue up everything */
49790792Sgshapiro	if (!somedeliveries && !WILL_BE_QUEUED(mode) &&
49838032Speter	    mode != SM_VERIFY)
49938032Speter	{
50090792Sgshapiro		time_t now;
50171345Sgshapiro
50238032Speter		if (tTd(13, 29))
50390792Sgshapiro			sm_dprintf("No deliveries: auto-queuing\n");
50438032Speter		mode = SM_QUEUE;
50590792Sgshapiro		now = curtime();
50638032Speter
50738032Speter		/* treat this as a delivery in terms of counting tries */
50871345Sgshapiro		e->e_dtime = now;
50938032Speter		if (!expensive)
51038032Speter			e->e_ntries++;
51138032Speter		for (ee = splitenv; ee != NULL; ee = ee->e_sibling)
51238032Speter		{
51371345Sgshapiro			ee->e_dtime = now;
51438032Speter			if (!expensive)
51538032Speter				ee->e_ntries++;
51638032Speter		}
51738032Speter	}
51838032Speter
51990792Sgshapiro	if ((WILL_BE_QUEUED(mode) || mode == SM_FORK ||
52090792Sgshapiro	     (mode != SM_VERIFY && SuperSafe == SAFE_REALLY)) &&
52138032Speter	    (!bitset(EF_INQUEUE, e->e_flags) || splitenv != NULL))
52238032Speter	{
52390792Sgshapiro		bool msync;
52490792Sgshapiro
52566494Sgshapiro		/*
52666494Sgshapiro		**  Be sure everything is instantiated in the queue.
52766494Sgshapiro		**  Split envelopes first in case the machine crashes.
52866494Sgshapiro		**  If the original were done first, we may lose
52966494Sgshapiro		**  recipients.
53066494Sgshapiro		*/
53166494Sgshapiro
53290792Sgshapiro#if !HASFLOCK
53390792Sgshapiro		msync = false;
53490792Sgshapiro#else /* !HASFLOCK */
53590792Sgshapiro		msync = mode == SM_FORK;
53690792Sgshapiro#endif /* !HASFLOCK */
53790792Sgshapiro
53838032Speter		for (ee = splitenv; ee != NULL; ee = ee->e_sibling)
53990792Sgshapiro			queueup(ee, WILL_BE_QUEUED(mode), msync);
54090792Sgshapiro		queueup(e, WILL_BE_QUEUED(mode), msync);
54138032Speter	}
54238032Speter
54338032Speter	if (tTd(62, 10))
54438032Speter		checkfds("after envelope splitting");
54538032Speter
54638032Speter	/*
54738032Speter	**  If we belong in background, fork now.
54838032Speter	*/
54938032Speter
55038032Speter	if (tTd(13, 20))
55138032Speter	{
55290792Sgshapiro		sm_dprintf("sendall: final mode = %c\n", mode);
55338032Speter		if (tTd(13, 21))
55438032Speter		{
55590792Sgshapiro			sm_dprintf("\n================ Final Send Queue(s) =====================\n");
55690792Sgshapiro			sm_dprintf("\n  *** Envelope %s, e_from=%s ***\n",
55790792Sgshapiro				   e->e_id, e->e_from.q_paddr);
55890792Sgshapiro			printaddr(e->e_sendqueue, true);
55938032Speter			for (ee = splitenv; ee != NULL; ee = ee->e_sibling)
56038032Speter			{
56190792Sgshapiro				sm_dprintf("\n  *** Envelope %s, e_from=%s ***\n",
56290792Sgshapiro					   ee->e_id, ee->e_from.q_paddr);
56390792Sgshapiro				printaddr(ee->e_sendqueue, true);
56438032Speter			}
56590792Sgshapiro			sm_dprintf("==========================================================\n\n");
56638032Speter		}
56738032Speter	}
56838032Speter	switch (mode)
56938032Speter	{
57038032Speter	  case SM_VERIFY:
57138032Speter		Verbose = 2;
57238032Speter		break;
57338032Speter
57438032Speter	  case SM_QUEUE:
57538032Speter	  case SM_DEFER:
57664562Sgshapiro#if HASFLOCK
57738032Speter  queueonly:
57864562Sgshapiro#endif /* HASFLOCK */
57938032Speter		if (e->e_nrcpts > 0)
58038032Speter			e->e_flags |= EF_INQUEUE;
58190792Sgshapiro		dropenvelope(e, splitenv != NULL, true);
58238032Speter		for (ee = splitenv; ee != NULL; ee = ee->e_sibling)
58338032Speter		{
58438032Speter			if (ee->e_nrcpts > 0)
58538032Speter				ee->e_flags |= EF_INQUEUE;
58690792Sgshapiro			dropenvelope(ee, false, true);
58738032Speter		}
58838032Speter		return;
58938032Speter
59038032Speter	  case SM_FORK:
59138032Speter		if (e->e_xfp != NULL)
59290792Sgshapiro			(void) sm_io_flush(e->e_xfp, SM_TIME_DEFAULT);
59338032Speter
59464562Sgshapiro#if !HASFLOCK
59538032Speter		/*
59638032Speter		**  Since fcntl locking has the interesting semantic that
59738032Speter		**  the lock is owned by a process, not by an open file
59838032Speter		**  descriptor, we have to flush this to the queue, and
59938032Speter		**  then restart from scratch in the child.
60038032Speter		*/
60138032Speter
60238032Speter		{
60338032Speter			/* save id for future use */
60438032Speter			char *qid = e->e_id;
60538032Speter
60638032Speter			/* now drop the envelope in the parent */
60738032Speter			e->e_flags |= EF_INQUEUE;
60890792Sgshapiro			dropenvelope(e, splitenv != NULL, false);
60938032Speter
61038032Speter			/* arrange to reacquire lock after fork */
61138032Speter			e->e_id = qid;
61238032Speter		}
61338032Speter
61438032Speter		for (ee = splitenv; ee != NULL; ee = ee->e_sibling)
61538032Speter		{
61638032Speter			/* save id for future use */
61738032Speter			char *qid = ee->e_id;
61838032Speter
61938032Speter			/* drop envelope in parent */
62038032Speter			ee->e_flags |= EF_INQUEUE;
62190792Sgshapiro			dropenvelope(ee, false, false);
62238032Speter
62338032Speter			/* and save qid for reacquisition */
62438032Speter			ee->e_id = qid;
62538032Speter		}
62664562Sgshapiro#endif /* !HASFLOCK */
62738032Speter
62864562Sgshapiro		/*
62964562Sgshapiro		**  Since the delivery may happen in a child and the parent
63064562Sgshapiro		**  does not wait, the parent may close the maps thereby
63164562Sgshapiro		**  removing any shared memory used by the map.  Therefore,
63264562Sgshapiro		**  close the maps now so the child will dynamically open
63364562Sgshapiro		**  them if necessary.
63464562Sgshapiro		*/
63564562Sgshapiro
63690792Sgshapiro		closemaps(false);
63764562Sgshapiro
63838032Speter		pid = fork();
63938032Speter		if (pid < 0)
64038032Speter		{
64164562Sgshapiro			syserr("deliver: fork 1");
64264562Sgshapiro#if HASFLOCK
64338032Speter			goto queueonly;
64464562Sgshapiro#else /* HASFLOCK */
64538032Speter			e->e_id = NULL;
64638032Speter			for (ee = splitenv; ee != NULL; ee = ee->e_sibling)
64738032Speter				ee->e_id = NULL;
64838032Speter			return;
64964562Sgshapiro#endif /* HASFLOCK */
65038032Speter		}
65138032Speter		else if (pid > 0)
65238032Speter		{
65364562Sgshapiro#if HASFLOCK
65438032Speter			/* be sure we leave the temp files to our child */
65538032Speter			/* close any random open files in the envelope */
65638032Speter			closexscript(e);
65738032Speter			if (e->e_dfp != NULL)
65890792Sgshapiro				(void) sm_io_close(e->e_dfp, SM_TIME_DEFAULT);
65938032Speter			e->e_dfp = NULL;
66038032Speter			e->e_flags &= ~EF_HAS_DF;
66138032Speter
66238032Speter			/* can't call unlockqueue to avoid unlink of xfp */
66338032Speter			if (e->e_lockfp != NULL)
66490792Sgshapiro				(void) sm_io_close(e->e_lockfp, SM_TIME_DEFAULT);
66564562Sgshapiro			else
66664562Sgshapiro				syserr("%s: sendall: null lockfp", e->e_id);
66738032Speter			e->e_lockfp = NULL;
66864562Sgshapiro#endif /* HASFLOCK */
66938032Speter
67038032Speter			/* make sure the parent doesn't own the envelope */
67138032Speter			e->e_id = NULL;
67238032Speter
67390792Sgshapiro#if USE_DOUBLE_FORK
67438032Speter			/* catch intermediate zombie */
67538032Speter			(void) waitfor(pid);
67690792Sgshapiro#endif /* USE_DOUBLE_FORK */
67738032Speter			return;
67838032Speter		}
67938032Speter
68077349Sgshapiro		/* Reset global flags */
68177349Sgshapiro		RestartRequest = NULL;
68290792Sgshapiro		RestartWorkGroup = false;
68377349Sgshapiro		ShutdownRequest = NULL;
68477349Sgshapiro		PendingSignal = 0;
68577349Sgshapiro
68666494Sgshapiro		/*
68790792Sgshapiro		**  Initialize exception stack and default exception
68890792Sgshapiro		**  handler for child process.
68990792Sgshapiro		*/
69090792Sgshapiro
69190792Sgshapiro		sm_exc_newthread(fatal_error);
69290792Sgshapiro
69390792Sgshapiro		/*
69466494Sgshapiro		**  Since we have accepted responsbility for the message,
69566494Sgshapiro		**  change the SIGTERM handler.  intsig() (the old handler)
69666494Sgshapiro		**  would remove the envelope if this was a command line
69766494Sgshapiro		**  message submission.
69866494Sgshapiro		*/
69966494Sgshapiro
70090792Sgshapiro		(void) sm_signal(SIGTERM, SIG_DFL);
70166494Sgshapiro
70290792Sgshapiro#if USE_DOUBLE_FORK
70338032Speter		/* double fork to avoid zombies */
70438032Speter		pid = fork();
70538032Speter		if (pid > 0)
70638032Speter			exit(EX_OK);
70764562Sgshapiro		save_errno = errno;
70890792Sgshapiro#endif /* USE_DOUBLE_FORK */
70938032Speter
71090792Sgshapiro		CurrentPid = getpid();
71190792Sgshapiro
71238032Speter		/* be sure we are immune from the terminal */
71338032Speter		disconnect(2, e);
71464562Sgshapiro		clearstats();
71538032Speter
71638032Speter		/* prevent parent from waiting if there was an error */
71738032Speter		if (pid < 0)
71838032Speter		{
71964562Sgshapiro			errno = save_errno;
72064562Sgshapiro			syserr("deliver: fork 2");
72164562Sgshapiro#if HASFLOCK
72238032Speter			e->e_flags |= EF_INQUEUE;
72364562Sgshapiro#else /* HASFLOCK */
72438032Speter			e->e_id = NULL;
72564562Sgshapiro#endif /* HASFLOCK */
72690792Sgshapiro			finis(true, true, ExitStat);
72738032Speter		}
72838032Speter
72938032Speter		/* be sure to give error messages in child */
73090792Sgshapiro		QuickAbort = false;
73138032Speter
73238032Speter		/*
73338032Speter		**  Close any cached connections.
73438032Speter		**
73538032Speter		**	We don't send the QUIT protocol because the parent
73638032Speter		**	still knows about the connection.
73738032Speter		**
73838032Speter		**	This should only happen when delivering an error
73938032Speter		**	message.
74038032Speter		*/
74138032Speter
74290792Sgshapiro		mci_flush(false, NULL);
74338032Speter
74464562Sgshapiro#if HASFLOCK
74538032Speter		break;
74664562Sgshapiro#else /* HASFLOCK */
74738032Speter
74838032Speter		/*
74938032Speter		**  Now reacquire and run the various queue files.
75038032Speter		*/
75138032Speter
75238032Speter		for (ee = splitenv; ee != NULL; ee = ee->e_sibling)
75338032Speter		{
75438032Speter			ENVELOPE *sibling = ee->e_sibling;
75538032Speter
75690792Sgshapiro			(void) dowork(ee->e_qgrp, ee->e_qdir, ee->e_id,
75790792Sgshapiro				      false, false, ee);
75838032Speter			ee->e_sibling = sibling;
75938032Speter		}
76090792Sgshapiro		(void) dowork(e->e_qgrp, e->e_qdir, e->e_id,
76190792Sgshapiro			      false, false, e);
76290792Sgshapiro		finis(true, true, ExitStat);
76364562Sgshapiro#endif /* HASFLOCK */
76438032Speter	}
76538032Speter
76638032Speter	sendenvelope(e, mode);
76790792Sgshapiro	dropenvelope(e, true, true);
76838032Speter	for (ee = splitenv; ee != NULL; ee = ee->e_sibling)
76938032Speter	{
77038032Speter		CurEnv = ee;
77138032Speter		if (mode != SM_VERIFY)
77238032Speter			openxscript(ee);
77338032Speter		sendenvelope(ee, mode);
77490792Sgshapiro		dropenvelope(ee, true, true);
77538032Speter	}
77638032Speter	CurEnv = e;
77738032Speter
77838032Speter	Verbose = oldverbose;
77938032Speter	if (mode == SM_FORK)
78090792Sgshapiro		finis(true, true, ExitStat);
78138032Speter}
78238032Speter
78364562Sgshapirostatic void
78438032Spetersendenvelope(e, mode)
78538032Speter	register ENVELOPE *e;
78638032Speter	int mode;
78738032Speter{
78838032Speter	register ADDRESS *q;
78938032Speter	bool didany;
79038032Speter
79138032Speter	if (tTd(13, 10))
79290792Sgshapiro		sm_dprintf("sendenvelope(%s) e_flags=0x%lx\n",
79390792Sgshapiro			   e->e_id == NULL ? "[NOQUEUE]" : e->e_id,
79490792Sgshapiro			   e->e_flags);
79538032Speter	if (LogLevel > 80)
79638032Speter		sm_syslog(LOG_DEBUG, e->e_id,
79764562Sgshapiro			  "sendenvelope, flags=0x%lx",
79864562Sgshapiro			  e->e_flags);
79938032Speter
80038032Speter	/*
80138032Speter	**  If we have had global, fatal errors, don't bother sending
80238032Speter	**  the message at all if we are in SMTP mode.  Local errors
80338032Speter	**  (e.g., a single address failing) will still cause the other
80438032Speter	**  addresses to be sent.
80538032Speter	*/
80638032Speter
80738032Speter	if (bitset(EF_FATALERRS, e->e_flags) &&
80838032Speter	    (OpMode == MD_SMTP || OpMode == MD_DAEMON))
80938032Speter	{
81038032Speter		e->e_flags |= EF_CLRQUEUE;
81138032Speter		return;
81238032Speter	}
81338032Speter
81490792Sgshapiro	/*
81590792Sgshapiro	**  Don't attempt deliveries if we want to bounce now
81690792Sgshapiro	**  or if deliver-by time is exceeded.
81790792Sgshapiro	*/
81890792Sgshapiro
81964562Sgshapiro	if (!bitset(EF_RESPONSE, e->e_flags) &&
82090792Sgshapiro	    (TimeOuts.to_q_return[e->e_timeoutclass] == NOW ||
82190792Sgshapiro	     (IS_DLVR_RETURN(e) && e->e_deliver_by > 0 &&
82290792Sgshapiro	      curtime() > e->e_ctime + e->e_deliver_by)))
82364562Sgshapiro		return;
82464562Sgshapiro
82538032Speter	/*
82638032Speter	**  Run through the list and send everything.
82738032Speter	**
82838032Speter	**	Set EF_GLOBALERRS so that error messages during delivery
82938032Speter	**	result in returned mail.
83038032Speter	*/
83138032Speter
83238032Speter	e->e_nsent = 0;
83338032Speter	e->e_flags |= EF_GLOBALERRS;
83464562Sgshapiro
83590792Sgshapiro	macdefine(&e->e_macro, A_PERM, macid("{envid}"), e->e_envid);
83690792Sgshapiro	macdefine(&e->e_macro, A_PERM, macid("{bodytype}"), e->e_bodytype);
83790792Sgshapiro	didany = false;
83838032Speter
83990792Sgshapiro	if (!bitset(EF_SPLIT, e->e_flags))
84090792Sgshapiro	{
84190792Sgshapiro		ENVELOPE *oldsib;
84290792Sgshapiro		ENVELOPE *ee;
84390792Sgshapiro
84490792Sgshapiro		/*
84590792Sgshapiro		**  Save old sibling and set it to NULL to avoid
84690792Sgshapiro		**  queueing up the same envelopes again.
84790792Sgshapiro		**  This requires that envelopes in that list have
84890792Sgshapiro		**  been take care of before (or at some other place).
84990792Sgshapiro		*/
85090792Sgshapiro
85190792Sgshapiro		oldsib = e->e_sibling;
85290792Sgshapiro		e->e_sibling = NULL;
85390792Sgshapiro		if (!split_by_recipient(e) &&
85490792Sgshapiro		    bitset(EF_FATALERRS, e->e_flags))
85590792Sgshapiro		{
85690792Sgshapiro			if (OpMode == MD_SMTP || OpMode == MD_DAEMON)
85790792Sgshapiro				e->e_flags |= EF_CLRQUEUE;
85890792Sgshapiro			return;
85990792Sgshapiro		}
86090792Sgshapiro		for (ee = e->e_sibling; ee != NULL; ee = ee->e_sibling)
86190792Sgshapiro			queueup(ee, false, true);
86290792Sgshapiro
86390792Sgshapiro		/* clean up */
86490792Sgshapiro		for (ee = e->e_sibling; ee != NULL; ee = ee->e_sibling)
86590792Sgshapiro		{
86690792Sgshapiro			/* now unlock the job */
86790792Sgshapiro			closexscript(ee);
86890792Sgshapiro			unlockqueue(ee);
86990792Sgshapiro
87090792Sgshapiro			/* this envelope is marked unused */
87190792Sgshapiro			if (ee->e_dfp != NULL)
87290792Sgshapiro			{
87390792Sgshapiro				(void) sm_io_close(ee->e_dfp, SM_TIME_DEFAULT);
87490792Sgshapiro				ee->e_dfp = NULL;
87590792Sgshapiro			}
87690792Sgshapiro			ee->e_id = NULL;
87790792Sgshapiro			ee->e_flags &= ~EF_HAS_DF;
87890792Sgshapiro		}
87990792Sgshapiro		e->e_sibling = oldsib;
88090792Sgshapiro	}
88190792Sgshapiro
88238032Speter	/* now run through the queue */
88338032Speter	for (q = e->e_sendqueue; q != NULL; q = q->q_next)
88438032Speter	{
88538032Speter#if XDEBUG
88638032Speter		char wbuf[MAXNAME + 20];
88738032Speter
88890792Sgshapiro		(void) sm_snprintf(wbuf, sizeof wbuf, "sendall(%.*s)",
88990792Sgshapiro				   MAXNAME, q->q_paddr);
89038032Speter		checkfd012(wbuf);
89164562Sgshapiro#endif /* XDEBUG */
89238032Speter		if (mode == SM_VERIFY)
89338032Speter		{
89438032Speter			e->e_to = q->q_paddr;
89564562Sgshapiro			if (QS_IS_SENDABLE(q->q_state))
89638032Speter			{
89738032Speter				if (q->q_host != NULL && q->q_host[0] != '\0')
89838032Speter					message("deliverable: mailer %s, host %s, user %s",
89938032Speter						q->q_mailer->m_name,
90038032Speter						q->q_host,
90138032Speter						q->q_user);
90238032Speter				else
90338032Speter					message("deliverable: mailer %s, user %s",
90438032Speter						q->q_mailer->m_name,
90538032Speter						q->q_user);
90638032Speter			}
90738032Speter		}
90864562Sgshapiro		else if (QS_IS_OK(q->q_state))
90938032Speter		{
91038032Speter			/*
91138032Speter			**  Checkpoint the send list every few addresses
91238032Speter			*/
91338032Speter
91466494Sgshapiro			if (CheckpointInterval > 0 &&
91566494Sgshapiro			    e->e_nsent >= CheckpointInterval)
91638032Speter			{
91790792Sgshapiro				queueup(e, false, false);
91838032Speter				e->e_nsent = 0;
91938032Speter			}
92038032Speter			(void) deliver(e, q);
92190792Sgshapiro			didany = true;
92238032Speter		}
92338032Speter	}
92438032Speter	if (didany)
92538032Speter	{
92638032Speter		e->e_dtime = curtime();
92738032Speter		e->e_ntries++;
92838032Speter	}
92938032Speter
93038032Speter#if XDEBUG
93138032Speter	checkfd012("end of sendenvelope");
93264562Sgshapiro#endif /* XDEBUG */
93338032Speter}
93490792Sgshapiro
93590792Sgshapiro#if REQUIRES_DIR_FSYNC
93690792Sgshapiro/*
93790792Sgshapiro**  SYNC_DIR -- fsync a directory based on a filename
93890792Sgshapiro**
93990792Sgshapiro**	Parameters:
94090792Sgshapiro**		filename -- path of file
94190792Sgshapiro**		panic -- panic?
94290792Sgshapiro**
94390792Sgshapiro**	Returns:
94490792Sgshapiro**		none
94590792Sgshapiro*/
94690792Sgshapiro
94790792Sgshapirovoid
94890792Sgshapirosync_dir(filename, panic)
94990792Sgshapiro	char *filename;
95090792Sgshapiro	bool panic;
95190792Sgshapiro{
95290792Sgshapiro	int dirfd;
95390792Sgshapiro	char *dirp;
95490792Sgshapiro	char dir[MAXPATHLEN];
95590792Sgshapiro
956110560Sgshapiro#if _FFR_REQ_DIR_FSYNC_OPT
957110560Sgshapiro	if (!RequiresDirfsync)
958110560Sgshapiro		return;
959110560Sgshapiro#endif /* _FFR_REQ_DIR_FSYNC_OPT */
960110560Sgshapiro
96190792Sgshapiro	/* filesystems which require the directory be synced */
96290792Sgshapiro	dirp = strrchr(filename, '/');
96390792Sgshapiro	if (dirp != NULL)
96490792Sgshapiro	{
96590792Sgshapiro		if (sm_strlcpy(dir, filename, sizeof dir) >= sizeof dir)
96690792Sgshapiro			return;
96790792Sgshapiro		dir[dirp - filename] = '\0';
96890792Sgshapiro		dirp = dir;
96990792Sgshapiro	}
97090792Sgshapiro	else
97190792Sgshapiro		dirp = ".";
97290792Sgshapiro	dirfd = open(dirp, O_RDONLY, 0700);
97390792Sgshapiro	if (tTd(40,32))
97490792Sgshapiro		sm_syslog(LOG_INFO, NOQID, "sync_dir: %s: fsync(%d)",
97590792Sgshapiro			  dirp, dirfd);
97690792Sgshapiro	if (dirfd >= 0)
97790792Sgshapiro	{
97890792Sgshapiro		if (fsync(dirfd) < 0)
97990792Sgshapiro		{
98090792Sgshapiro			if (panic)
98190792Sgshapiro				syserr("!sync_dir: cannot fsync directory %s",
98290792Sgshapiro				       dirp);
98390792Sgshapiro			else if (LogLevel > 1)
98490792Sgshapiro				sm_syslog(LOG_ERR, NOQID,
98590792Sgshapiro					  "sync_dir: cannot fsync directory %s: %s",
98690792Sgshapiro					  dirp, sm_errstring(errno));
98790792Sgshapiro		}
98890792Sgshapiro		(void) close(dirfd);
98990792Sgshapiro	}
99090792Sgshapiro}
99190792Sgshapiro#endif /* REQUIRES_DIR_FSYNC */
99290792Sgshapiro/*
99338032Speter**  DUP_QUEUE_FILE -- duplicate a queue file into a split queue
99438032Speter**
99538032Speter**	Parameters:
99638032Speter**		e -- the existing envelope
99738032Speter**		ee -- the new envelope
99890792Sgshapiro**		type -- the queue file type (e.g., DATAFL_LETTER)
99938032Speter**
100038032Speter**	Returns:
100138032Speter**		none
100238032Speter*/
100338032Speter
100464562Sgshapirostatic void
100538032Speterdup_queue_file(e, ee, type)
100690792Sgshapiro	ENVELOPE *e, *ee;
100738032Speter	int type;
100838032Speter{
100964562Sgshapiro	char f1buf[MAXPATHLEN], f2buf[MAXPATHLEN];
101038032Speter
101138032Speter	ee->e_dfp = NULL;
101238032Speter	ee->e_xfp = NULL;
101364562Sgshapiro
101464562Sgshapiro	/*
101564562Sgshapiro	**  Make sure both are in the same directory.
101664562Sgshapiro	*/
101764562Sgshapiro
101890792Sgshapiro	(void) sm_strlcpy(f1buf, queuename(e, type), sizeof f1buf);
101990792Sgshapiro	(void) sm_strlcpy(f2buf, queuename(ee, type), sizeof f2buf);
1020102528Sgshapiro
1021102528Sgshapiro	/* Force the df to disk if it's not there yet */
1022102528Sgshapiro	if (type == DATAFL_LETTER && e->e_dfp != NULL &&
1023102528Sgshapiro	    sm_io_setinfo(e->e_dfp, SM_BF_COMMIT, NULL) < 0 &&
1024102528Sgshapiro	    errno != EINVAL)
1025102528Sgshapiro	{
1026102528Sgshapiro		syserr("!dup_queue_file: can't commit %s", f1buf);
1027102528Sgshapiro		/* NOTREACHED */
1028102528Sgshapiro	}
1029102528Sgshapiro
103038032Speter	if (link(f1buf, f2buf) < 0)
103138032Speter	{
103264562Sgshapiro		int save_errno = errno;
103338032Speter
103438032Speter		syserr("sendall: link(%s, %s)", f1buf, f2buf);
103564562Sgshapiro		if (save_errno == EEXIST)
103638032Speter		{
103738032Speter			if (unlink(f2buf) < 0)
103838032Speter			{
103938032Speter				syserr("!sendall: unlink(%s): permanent",
104090792Sgshapiro				       f2buf);
104164562Sgshapiro				/* NOTREACHED */
104238032Speter			}
104338032Speter			if (link(f1buf, f2buf) < 0)
104438032Speter			{
104538032Speter				syserr("!sendall: link(%s, %s): permanent",
104690792Sgshapiro				       f1buf, f2buf);
104764562Sgshapiro				/* NOTREACHED */
104838032Speter			}
104938032Speter		}
105038032Speter	}
105190792Sgshapiro	SYNC_DIR(f2buf, true);
105238032Speter}
105390792Sgshapiro/*
105438032Speter**  DOFORK -- do a fork, retrying a couple of times on failure.
105538032Speter**
105638032Speter**	This MUST be a macro, since after a vfork we are running
105738032Speter**	two processes on the same stack!!!
105838032Speter**
105938032Speter**	Parameters:
106038032Speter**		none.
106138032Speter**
106238032Speter**	Returns:
106338032Speter**		From a macro???  You've got to be kidding!
106438032Speter**
106538032Speter**	Side Effects:
106638032Speter**		Modifies the ==> LOCAL <== variable 'pid', leaving:
106738032Speter**			pid of child in parent, zero in child.
106838032Speter**			-1 on unrecoverable error.
106938032Speter**
107038032Speter**	Notes:
107138032Speter**		I'm awfully sorry this looks so awful.  That's
107238032Speter**		vfork for you.....
107338032Speter*/
107438032Speter
107564562Sgshapiro#define NFORKTRIES	5
107638032Speter
107764562Sgshapiro#ifndef FORK
107838032Speter# define FORK	fork
107964562Sgshapiro#endif /* ! FORK */
108038032Speter
108164562Sgshapiro#define DOFORK(fORKfN) \
108238032Speter{\
108338032Speter	register int i;\
108438032Speter\
108538032Speter	for (i = NFORKTRIES; --i >= 0; )\
108638032Speter	{\
108738032Speter		pid = fORKfN();\
108838032Speter		if (pid >= 0)\
108938032Speter			break;\
109038032Speter		if (i > 0)\
109164562Sgshapiro			(void) sleep((unsigned) NFORKTRIES - i);\
109238032Speter	}\
109338032Speter}
109490792Sgshapiro/*
109538032Speter**  DOFORK -- simple fork interface to DOFORK.
109638032Speter**
109738032Speter**	Parameters:
109838032Speter**		none.
109938032Speter**
110038032Speter**	Returns:
110138032Speter**		pid of child in parent.
110238032Speter**		zero in child.
110338032Speter**		-1 on error.
110438032Speter**
110538032Speter**	Side Effects:
110638032Speter**		returns twice, once in parent and once in child.
110738032Speter*/
110838032Speter
110977349Sgshapiropid_t
111038032Speterdofork()
111138032Speter{
111238032Speter	register pid_t pid = -1;
111338032Speter
111438032Speter	DOFORK(fork);
111564562Sgshapiro	return pid;
111638032Speter}
111790792Sgshapiro
111890792Sgshapiro/*
111990792Sgshapiro**  COLONCMP -- compare host-signatures up to first ':' or EOS
112090792Sgshapiro**
112190792Sgshapiro**	This takes two strings which happen to be host-signatures and
112290792Sgshapiro**	compares them. If the lowest preference portions of the MX-RR's
112390792Sgshapiro**	match (up to ':' or EOS, whichever is first), then we have
112490792Sgshapiro**	match. This is used for coattail-piggybacking messages during
112590792Sgshapiro**	message delivery.
112690792Sgshapiro**	If the signatures are the same up to the first ':' the remainder of
112790792Sgshapiro**	the signatures are then compared with a normal strcmp(). This saves
112890792Sgshapiro**	re-examining the first part of the signatures.
112990792Sgshapiro**
113090792Sgshapiro**	Parameters:
113190792Sgshapiro**		a - first host-signature
113290792Sgshapiro**		b - second host-signature
113390792Sgshapiro**
113490792Sgshapiro**	Returns:
113590792Sgshapiro**		HS_MATCH_NO -- no "match".
113690792Sgshapiro**		HS_MATCH_FIRST -- "match" for the first MX preference
113790792Sgshapiro**			(up to the first colon (':')).
113890792Sgshapiro**		HS_MATCH_FULL -- match for the entire MX record.
113990792Sgshapiro**
114090792Sgshapiro**	Side Effects:
114190792Sgshapiro**		none.
114290792Sgshapiro*/
114390792Sgshapiro
114490792Sgshapiro#define HS_MATCH_NO	0
114590792Sgshapiro#define HS_MATCH_FIRST	1
114690792Sgshapiro#define HS_MATCH_FULL	2
114790792Sgshapiro
114890792Sgshapirostatic int
114990792Sgshapirocoloncmp(a, b)
115090792Sgshapiro	register const char *a;
115190792Sgshapiro	register const char *b;
115290792Sgshapiro{
115390792Sgshapiro	int ret = HS_MATCH_NO;
115490792Sgshapiro	int braclev = 0;
115590792Sgshapiro
115690792Sgshapiro	while (*a == *b++)
115790792Sgshapiro	{
115890792Sgshapiro		/* Need to account for IPv6 bracketed addresses */
115990792Sgshapiro		if (*a == '[')
116090792Sgshapiro			braclev++;
1161112810Sgshapiro		else if (*a == ']' && braclev > 0)
116290792Sgshapiro			braclev--;
116390792Sgshapiro		else if (*a == ':' && braclev <= 0)
116490792Sgshapiro		{
116590792Sgshapiro			ret = HS_MATCH_FIRST;
116690792Sgshapiro			a++;
116790792Sgshapiro			break;
116890792Sgshapiro		}
116990792Sgshapiro		else if (*a == '\0')
117090792Sgshapiro			return HS_MATCH_FULL; /* a full match */
117190792Sgshapiro		a++;
117290792Sgshapiro	}
117390792Sgshapiro	if (ret == HS_MATCH_NO &&
117490792Sgshapiro	    braclev <= 0 &&
117590792Sgshapiro	    ((*a == '\0' && *(b - 1) == ':') ||
117690792Sgshapiro	     (*a == ':' && *(b - 1) == '\0')))
117790792Sgshapiro		return HS_MATCH_FIRST;
117890792Sgshapiro	if (ret == HS_MATCH_FIRST && strcmp(a, b) == 0)
117990792Sgshapiro		return HS_MATCH_FULL;
118090792Sgshapiro
118190792Sgshapiro	return ret;
118290792Sgshapiro}
118390792Sgshapiro/*
118438032Speter**  DELIVER -- Deliver a message to a list of addresses.
118538032Speter**
118638032Speter**	This routine delivers to everyone on the same host as the
118738032Speter**	user on the head of the list.  It is clever about mailers
118838032Speter**	that don't handle multiple users.  It is NOT guaranteed
118938032Speter**	that it will deliver to all these addresses however -- so
119038032Speter**	deliver should be called once for each address on the
119138032Speter**	list.
119290792Sgshapiro**	Deliver tries to be as opportunistic as possible about piggybacking
119390792Sgshapiro**	messages. Some definitions to make understanding easier follow below.
119490792Sgshapiro**	Piggybacking occurs when an existing connection to a mail host can
119590792Sgshapiro**	be used to send the same message to more than one recipient at the
119690792Sgshapiro**	same time. So "no piggybacking" means one message for one recipient
119790792Sgshapiro**	per connection. "Intentional piggybacking" happens when the
119890792Sgshapiro**	recipients' host address (not the mail host address) is used to
119990792Sgshapiro**	attempt piggybacking. Recipients with the same host address
120090792Sgshapiro**	have the same mail host. "Coincidental piggybacking" relies on
120190792Sgshapiro**	piggybacking based on all the mail host addresses in the MX-RR. This
120290792Sgshapiro**	is "coincidental" in the fact it could not be predicted until the
120390792Sgshapiro**	MX Resource Records for the hosts were obtained and examined. For
120490792Sgshapiro**	example (preference order and equivalence is important, not values):
120590792Sgshapiro**		domain1 IN MX 10 mxhost-A
120690792Sgshapiro**			IN MX 20 mxhost-B
120790792Sgshapiro**		domain2 IN MX  4 mxhost-A
120890792Sgshapiro**			IN MX  8 mxhost-B
120990792Sgshapiro**	Domain1 and domain2 can piggyback the same message to mxhost-A or
121090792Sgshapiro**	mxhost-B (if mxhost-A cannot be reached).
121190792Sgshapiro**	"Coattail piggybacking" relaxes the strictness of "coincidental
121290792Sgshapiro**	piggybacking" in the hope that most significant (lowest value)
121390792Sgshapiro**	MX preference host(s) can create more piggybacking. For example
121490792Sgshapiro**	(again, preference order and equivalence is important, not values):
121590792Sgshapiro**		domain3 IN MX 100 mxhost-C
121690792Sgshapiro**			IN MX 100 mxhost-D
121790792Sgshapiro**			IN MX 200 mxhost-E
121890792Sgshapiro**		domain4 IN MX  50 mxhost-C
121990792Sgshapiro**			IN MX  50 mxhost-D
122090792Sgshapiro**			IN MX  80 mxhost-F
122190792Sgshapiro**	A message for domain3 and domain4 can piggyback to mxhost-C if mxhost-C
122290792Sgshapiro**	is available. Same with mxhost-D because in both RR's the preference
122390792Sgshapiro**	value is the same as mxhost-C, respectively.
122490792Sgshapiro**	So deliver attempts coattail piggybacking when possible. If the
122590792Sgshapiro**	first MX preference level hosts cannot be used then the piggybacking
122690792Sgshapiro**	reverts to coincidental piggybacking. Using the above example you
122790792Sgshapiro**	cannot deliver to mxhost-F for domain3 regardless of preference value.
122890792Sgshapiro**	("Coattail" from "riding on the coattails of your predecessor" meaning
122990792Sgshapiro**	gaining benefit from a predecessor effort with no or little addition
123090792Sgshapiro**	effort. The predecessor here being the preceding MX RR).
123138032Speter**
123238032Speter**	Parameters:
123338032Speter**		e -- the envelope to deliver.
123438032Speter**		firstto -- head of the address list to deliver to.
123538032Speter**
123638032Speter**	Returns:
123738032Speter**		zero -- successfully delivered.
123838032Speter**		else -- some failure, see ExitStat for more info.
123938032Speter**
124038032Speter**	Side Effects:
124138032Speter**		The standard input is passed off to someone.
124238032Speter*/
124338032Speter
124438032Speter#ifndef NO_UID
124538032Speter# define NO_UID		-1
124664562Sgshapiro#endif /* ! NO_UID */
124738032Speter#ifndef NO_GID
124838032Speter# define NO_GID		-1
124964562Sgshapiro#endif /* ! NO_GID */
125038032Speter
125164562Sgshapirostatic int
125238032Speterdeliver(e, firstto)
125338032Speter	register ENVELOPE *e;
125438032Speter	ADDRESS *firstto;
125538032Speter{
125638032Speter	char *host;			/* host being sent to */
125738032Speter	char *user;			/* user being sent to */
125838032Speter	char **pvp;
125938032Speter	register char **mvp;
126038032Speter	register char *p;
126138032Speter	register MAILER *m;		/* mailer for this recipient */
126238032Speter	ADDRESS *volatile ctladdr;
126390792Sgshapiro#if HASSETUSERCONTEXT
126438032Speter	ADDRESS *volatile contextaddr = NULL;
126590792Sgshapiro#endif /* HASSETUSERCONTEXT */
126638032Speter	register MCI *volatile mci;
126790792Sgshapiro	register ADDRESS *SM_NONVOLATILE to = firstto;
126890792Sgshapiro	volatile bool clever = false;	/* running user smtp to this mailer */
126938032Speter	ADDRESS *volatile tochain = NULL; /* users chain in this mailer call */
127038032Speter	int rcode;			/* response code */
127190792Sgshapiro	SM_NONVOLATILE int lmtp_rcode = EX_OK;
127290792Sgshapiro	SM_NONVOLATILE int nummxhosts = 0; /* number of MX hosts available */
127390792Sgshapiro	SM_NONVOLATILE int hostnum = 0;	/* current MX host index */
127438032Speter	char *firstsig;			/* signature of firstto */
127590792Sgshapiro	volatile pid_t pid = -1;
127638032Speter	char *volatile curhost;
127790792Sgshapiro	SM_NONVOLATILE unsigned short port = 0;
127890792Sgshapiro	SM_NONVOLATILE time_t enough = 0;
127964562Sgshapiro#if NETUNIX
128090792Sgshapiro	char *SM_NONVOLATILE mux_path = NULL;	/* path to UNIX domain socket */
128164562Sgshapiro#endif /* NETUNIX */
128238032Speter	time_t xstart;
128338032Speter	bool suidwarn;
128438032Speter	bool anyok;			/* at least one address was OK */
128590792Sgshapiro	SM_NONVOLATILE bool goodmxfound = false; /* at least one MX was OK */
128664562Sgshapiro	bool ovr;
128790792Sgshapiro#if _FFR_QUARANTINE
128890792Sgshapiro	bool quarantine;
128990792Sgshapiro#endif /* _FFR_QUARANTINE */
129064562Sgshapiro	int strsize;
129164562Sgshapiro	int rcptcount;
129290792Sgshapiro	int ret;
129364562Sgshapiro	static int tobufsize = 0;
129464562Sgshapiro	static char *tobuf = NULL;
129590792Sgshapiro	char *rpath;	/* translated return path */
129638032Speter	int mpvect[2];
129738032Speter	int rpvect[2];
129864562Sgshapiro	char *mxhosts[MAXMXHOSTS + 1];
129964562Sgshapiro	char *pv[MAXPV + 1];
130038032Speter	char buf[MAXNAME + 1];
130198121Sgshapiro	char cbuf[MAXPATHLEN];
130238032Speter
130338032Speter	errno = 0;
130464562Sgshapiro	if (!QS_IS_OK(to->q_state))
130564562Sgshapiro		return 0;
130638032Speter
130738032Speter	suidwarn = geteuid() == 0;
130838032Speter
130938032Speter	m = to->q_mailer;
131038032Speter	host = to->q_host;
131138032Speter	CurEnv = e;			/* just in case */
131238032Speter	e->e_statmsg = NULL;
131338032Speter	SmtpError[0] = '\0';
131438032Speter	xstart = curtime();
131538032Speter
131638032Speter	if (tTd(10, 1))
131790792Sgshapiro		sm_dprintf("\n--deliver, id=%s, mailer=%s, host=`%s', first user=`%s'\n",
131838032Speter			e->e_id, m->m_name, host, to->q_user);
131938032Speter	if (tTd(10, 100))
132090792Sgshapiro		printopenfds(false);
132138032Speter
132238032Speter	/*
132390792Sgshapiro	**  Clear {client_*} macros if this is a bounce message to
132438032Speter	**  prevent rejection by check_compat ruleset.
132538032Speter	*/
132664562Sgshapiro
132738032Speter	if (bitset(EF_RESPONSE, e->e_flags))
132838032Speter	{
132990792Sgshapiro		macdefine(&e->e_macro, A_PERM, macid("{client_name}"), "");
133090792Sgshapiro		macdefine(&e->e_macro, A_PERM, macid("{client_addr}"), "");
133190792Sgshapiro		macdefine(&e->e_macro, A_PERM, macid("{client_port}"), "");
133290792Sgshapiro		macdefine(&e->e_macro, A_PERM, macid("{client_resolve}"), "");
133338032Speter	}
133464562Sgshapiro
133590792Sgshapiro	SM_TRY
133690792Sgshapiro	{
133790792Sgshapiro	ADDRESS *skip_back = NULL;
133890792Sgshapiro
133938032Speter	/*
134038032Speter	**  Do initial argv setup.
134138032Speter	**	Insert the mailer name.  Notice that $x expansion is
134238032Speter	**	NOT done on the mailer name.  Then, if the mailer has
134338032Speter	**	a picky -f flag, we insert it as appropriate.  This
134438032Speter	**	code does not check for 'pv' overflow; this places a
134538032Speter	**	manifest lower limit of 4 for MAXPV.
134638032Speter	**		The from address rewrite is expected to make
134738032Speter	**		the address relative to the other end.
134838032Speter	*/
134938032Speter
135038032Speter	/* rewrite from address, using rewriting rules */
135138032Speter	rcode = EX_OK;
135238032Speter	if (bitnset(M_UDBENVELOPE, e->e_from.q_mailer->m_flags))
135338032Speter		p = e->e_sender;
135438032Speter	else
135538032Speter		p = e->e_from.q_paddr;
135690792Sgshapiro	rpath = remotename(p, m, RF_SENDERADDR|RF_CANONICAL, &rcode, e);
135790792Sgshapiro	if (strlen(rpath) > MAXSHORTSTR)
135838032Speter	{
135990792Sgshapiro		rpath = shortenstring(rpath, MAXSHORTSTR);
136090792Sgshapiro
136190792Sgshapiro		/* avoid bogus errno */
136290792Sgshapiro		errno = 0;
136390792Sgshapiro		syserr("remotename: huge return path %s", rpath);
136438032Speter	}
136590792Sgshapiro	rpath = sm_rpool_strdup_x(e->e_rpool, rpath);
136690792Sgshapiro	macdefine(&e->e_macro, A_PERM, 'g', rpath);
136790792Sgshapiro	macdefine(&e->e_macro, A_PERM, 'h', host);
136838032Speter	Errors = 0;
136938032Speter	pvp = pv;
137038032Speter	*pvp++ = m->m_argv[0];
137138032Speter
137238032Speter	/* insert -f or -r flag as appropriate */
137364562Sgshapiro	if (FromFlag &&
137464562Sgshapiro	    (bitnset(M_FOPT, m->m_flags) ||
137564562Sgshapiro	     bitnset(M_ROPT, m->m_flags)))
137638032Speter	{
137738032Speter		if (bitnset(M_FOPT, m->m_flags))
137838032Speter			*pvp++ = "-f";
137938032Speter		else
138038032Speter			*pvp++ = "-r";
138190792Sgshapiro		*pvp++ = rpath;
138238032Speter	}
138338032Speter
138438032Speter	/*
138538032Speter	**  Append the other fixed parts of the argv.  These run
138638032Speter	**  up to the first entry containing "$u".  There can only
138738032Speter	**  be one of these, and there are only a few more slots
138838032Speter	**  in the pv after it.
138938032Speter	*/
139038032Speter
139138032Speter	for (mvp = m->m_argv; (p = *++mvp) != NULL; )
139238032Speter	{
139338032Speter		/* can't use strchr here because of sign extension problems */
139438032Speter		while (*p != '\0')
139538032Speter		{
139638032Speter			if ((*p++ & 0377) == MACROEXPAND)
139738032Speter			{
139838032Speter				if (*p == 'u')
139938032Speter					break;
140038032Speter			}
140138032Speter		}
140238032Speter
140338032Speter		if (*p != '\0')
140438032Speter			break;
140538032Speter
140638032Speter		/* this entry is safe -- go ahead and process it */
140738032Speter		expand(*mvp, buf, sizeof buf, e);
140890792Sgshapiro		*pvp++ = sm_rpool_strdup_x(e->e_rpool, buf);
140938032Speter		if (pvp >= &pv[MAXPV - 3])
141038032Speter		{
141164562Sgshapiro			syserr("554 5.3.5 Too many parameters to %s before $u",
141264562Sgshapiro			       pv[0]);
141390792Sgshapiro			rcode = -1;
141490792Sgshapiro			goto cleanup;
141538032Speter		}
141638032Speter	}
141738032Speter
141838032Speter	/*
141938032Speter	**  If we have no substitution for the user name in the argument
142038032Speter	**  list, we know that we must supply the names otherwise -- and
142138032Speter	**  SMTP is the answer!!
142238032Speter	*/
142338032Speter
142438032Speter	if (*mvp == NULL)
142538032Speter	{
142673188Sgshapiro		/* running LMTP or SMTP */
142790792Sgshapiro		clever = true;
142838032Speter		*pvp = NULL;
142938032Speter	}
143073188Sgshapiro	else if (bitnset(M_LMTP, m->m_flags))
143173188Sgshapiro	{
143273188Sgshapiro		/* not running LMTP */
143373188Sgshapiro		sm_syslog(LOG_ERR, NULL,
143473188Sgshapiro			  "Warning: mailer %s: LMTP flag (F=z) turned off",
143573188Sgshapiro			  m->m_name);
143673188Sgshapiro		clrbitn(M_LMTP, m->m_flags);
143773188Sgshapiro	}
143838032Speter
143938032Speter	/*
144038032Speter	**  At this point *mvp points to the argument with $u.  We
144138032Speter	**  run through our address list and append all the addresses
144238032Speter	**  we can.  If we run out of space, do not fret!  We can
144338032Speter	**  always send another copy later.
144438032Speter	*/
144538032Speter
144664562Sgshapiro	e->e_to = NULL;
144764562Sgshapiro	strsize = 2;
144864562Sgshapiro	rcptcount = 0;
144990792Sgshapiro	ctladdr = NULL;
145090792Sgshapiro	if (firstto->q_signature == NULL)
145190792Sgshapiro		firstto->q_signature = hostsignature(firstto->q_mailer,
145290792Sgshapiro						     firstto->q_host);
145390792Sgshapiro	firstsig = firstto->q_signature;
145464562Sgshapiro
145538032Speter	for (; to != NULL; to = to->q_next)
145638032Speter	{
145738032Speter		/* avoid sending multiple recipients to dumb mailers */
145864562Sgshapiro		if (tochain != NULL && !bitnset(M_MUSER, m->m_flags))
145964562Sgshapiro			break;
146038032Speter
146138032Speter		/* if already sent or not for this host, don't send */
146290792Sgshapiro		if (!QS_IS_OK(to->q_state)) /* already sent; look at next */
146338032Speter			continue;
146438032Speter
146590792Sgshapiro		/*
146690792Sgshapiro		**  Must be same mailer to keep grouping rcpts.
146790792Sgshapiro		**  If mailers don't match: continue; sendqueue is not
146890792Sgshapiro		**  sorted by mailers, so don't break;
146990792Sgshapiro		*/
147090792Sgshapiro
147190792Sgshapiro		if (to->q_mailer != firstto->q_mailer)
147290792Sgshapiro			continue;
147390792Sgshapiro
147490792Sgshapiro		if (to->q_signature == NULL) /* for safety */
147590792Sgshapiro			to->q_signature = hostsignature(to->q_mailer,
147690792Sgshapiro							to->q_host);
147790792Sgshapiro
147890792Sgshapiro		/*
147990792Sgshapiro		**  This is for coincidental and tailcoat piggybacking messages
148090792Sgshapiro		**  to the same mail host. While the signatures are identical
148190792Sgshapiro		**  (that's the MX-RR's are identical) we can do coincidental
148290792Sgshapiro		**  piggybacking. We try hard for coattail piggybacking
148390792Sgshapiro		**  with the same mail host when the next recipient has the
148490792Sgshapiro		**  same host at lowest preference. It may be that this
148590792Sgshapiro		**  won't work out, so 'skip_back' is maintained if a backup
148690792Sgshapiro		**  to coincidental piggybacking or full signature must happen.
148790792Sgshapiro		*/
148890792Sgshapiro
148990792Sgshapiro		ret = firstto == to ? HS_MATCH_FULL :
149090792Sgshapiro				      coloncmp(to->q_signature, firstsig);
149190792Sgshapiro		if (ret == HS_MATCH_FULL)
149290792Sgshapiro			skip_back = to;
149390792Sgshapiro		else if (ret == HS_MATCH_NO)
149464562Sgshapiro			break;
149564562Sgshapiro
149690792Sgshapiro		if (!clever)
149790792Sgshapiro		{
149890792Sgshapiro			/* avoid overflowing tobuf */
149990792Sgshapiro			strsize += strlen(to->q_paddr) + 1;
150090792Sgshapiro			if (strsize > TOBUFSIZE)
150190792Sgshapiro				break;
150290792Sgshapiro		}
150390792Sgshapiro
150464562Sgshapiro		if (++rcptcount > to->q_mailer->m_maxrcpt)
150564562Sgshapiro			break;
150638032Speter
150738032Speter		if (tTd(10, 1))
150838032Speter		{
150990792Sgshapiro			sm_dprintf("\nsend to ");
151090792Sgshapiro			printaddr(to, false);
151138032Speter		}
151238032Speter
151338032Speter		/* compute effective uid/gid when sending */
151438032Speter		if (bitnset(M_RUNASRCPT, to->q_mailer->m_flags))
151590792Sgshapiro# if HASSETUSERCONTEXT
151638032Speter			contextaddr = ctladdr = getctladdr(to);
151790792Sgshapiro# else /* HASSETUSERCONTEXT */
151890792Sgshapiro			ctladdr = getctladdr(to);
151990792Sgshapiro# endif /* HASSETUSERCONTEXT */
152038032Speter
152138032Speter		if (tTd(10, 2))
152238032Speter		{
152390792Sgshapiro			sm_dprintf("ctladdr=");
152490792Sgshapiro			printaddr(ctladdr, false);
152538032Speter		}
152638032Speter
152738032Speter		user = to->q_user;
152838032Speter		e->e_to = to->q_paddr;
152938032Speter
153038032Speter		/*
153138032Speter		**  Check to see that these people are allowed to
153238032Speter		**  talk to each other.
153366494Sgshapiro		**  Check also for overflow of e_msgsize.
153438032Speter		*/
153538032Speter
153666494Sgshapiro		if (m->m_maxsize != 0 &&
153766494Sgshapiro		    (e->e_msgsize > m->m_maxsize || e->e_msgsize < 0))
153838032Speter		{
153938032Speter			e->e_flags |= EF_NO_BODY_RETN;
154038032Speter			if (bitnset(M_LOCALMAILER, to->q_mailer->m_flags))
154138032Speter				to->q_status = "5.2.3";
154238032Speter			else
154338032Speter				to->q_status = "5.3.4";
154490792Sgshapiro
154564562Sgshapiro			/* set to->q_rstatus = NULL; or to the following? */
154664562Sgshapiro			usrerrenh(to->q_status,
154764562Sgshapiro				  "552 Message is too large; %ld bytes max",
154864562Sgshapiro				  m->m_maxsize);
154990792Sgshapiro			markfailure(e, to, NULL, EX_UNAVAILABLE, false);
155064562Sgshapiro			giveresponse(EX_UNAVAILABLE, to->q_status, m,
155190792Sgshapiro				     NULL, ctladdr, xstart, e, to);
155238032Speter			continue;
155338032Speter		}
155473188Sgshapiro		SM_SET_H_ERRNO(0);
155590792Sgshapiro		ovr = true;
155638032Speter
155738032Speter		/* do config file checking of compatibility */
155890792Sgshapiro#if _FFR_QUARANTINE
155990792Sgshapiro		quarantine = (e->e_quarmsg != NULL);
156090792Sgshapiro#endif /* _FFR_QUARANTINE */
156164562Sgshapiro		rcode = rscheck("check_compat", e->e_from.q_paddr, to->q_paddr,
1562102528Sgshapiro				e, RSF_RMCOMM|RSF_COUNT, 3, NULL,
1563102528Sgshapiro				e->e_id);
156438032Speter		if (rcode == EX_OK)
156538032Speter		{
156642575Speter			/* do in-code checking if not discarding */
156742575Speter			if (!bitset(EF_DISCARD, e->e_flags))
156864562Sgshapiro			{
156942575Speter				rcode = checkcompat(to, e);
157090792Sgshapiro				ovr = false;
157164562Sgshapiro			}
157238032Speter		}
157338032Speter		if (rcode != EX_OK)
157438032Speter		{
157564562Sgshapiro			markfailure(e, to, NULL, rcode, ovr);
157664562Sgshapiro			giveresponse(rcode, to->q_status, m,
157790792Sgshapiro				     NULL, ctladdr, xstart, e, to);
157838032Speter			continue;
157938032Speter		}
158090792Sgshapiro#if _FFR_QUARANTINE
158190792Sgshapiro		if (!quarantine && e->e_quarmsg != NULL)
158290792Sgshapiro		{
158390792Sgshapiro			/*
158490792Sgshapiro			**  check_compat or checkcompat() has tried
158590792Sgshapiro			**  to quarantine but that isn't supported.
158690792Sgshapiro			**  Revert the attempt.
158790792Sgshapiro			*/
158890792Sgshapiro
158990792Sgshapiro			e->e_quarmsg = NULL;
159090792Sgshapiro			macdefine(&e->e_macro, A_PERM,
159190792Sgshapiro				  macid("{quarantine}"), "");
159290792Sgshapiro		}
159390792Sgshapiro#endif /* _FFR_QUARANTINE */
159442575Speter		if (bitset(EF_DISCARD, e->e_flags))
159542575Speter		{
159642575Speter			if (tTd(10, 5))
159742575Speter			{
159890792Sgshapiro				sm_dprintf("deliver: discarding recipient ");
159990792Sgshapiro				printaddr(to, false);
160042575Speter			}
160138032Speter
160264562Sgshapiro			/* pretend the message was sent */
160364562Sgshapiro			/* XXX should we log something here? */
160464562Sgshapiro			to->q_state = QS_DISCARDED;
160564562Sgshapiro
160642575Speter			/*
160742575Speter			**  Remove discard bit to prevent discard of
160864562Sgshapiro			**  future recipients.  This is safe because the
160964562Sgshapiro			**  true "global discard" has been handled before
161064562Sgshapiro			**  we get here.
161142575Speter			*/
161264562Sgshapiro
161342575Speter			e->e_flags &= ~EF_DISCARD;
161442575Speter			continue;
161542575Speter		}
161642575Speter
161738032Speter		/*
161838032Speter		**  Strip quote bits from names if the mailer is dumb
161938032Speter		**	about them.
162038032Speter		*/
162138032Speter
162238032Speter		if (bitnset(M_STRIPQ, m->m_flags))
162338032Speter		{
162438032Speter			stripquotes(user);
162538032Speter			stripquotes(host);
162638032Speter		}
1627110560Sgshapiro#if _FFR_STRIPBACKSL
1628110560Sgshapiro		/*
1629112810Sgshapiro		**  Strip one leading backslash if requested and the
1630110560Sgshapiro		**  next character is alphanumerical (the latter can
1631110560Sgshapiro		**  probably relaxed a bit, see RFC2821).
1632110560Sgshapiro		*/
163338032Speter
1634110560Sgshapiro		if (bitnset(M_STRIPBACKSL, m->m_flags) && user[0] == '\\')
1635110560Sgshapiro			stripbackslash(user);
1636110560Sgshapiro#endif /* _FFR_STRIPBACKSL */
1637110560Sgshapiro
163838032Speter		/* hack attack -- delivermail compatibility */
163938032Speter		if (m == ProgMailer && *user == '|')
164038032Speter			user++;
164138032Speter
164238032Speter		/*
164338032Speter		**  If an error message has already been given, don't
164438032Speter		**	bother to send to this address.
164538032Speter		**
164638032Speter		**	>>>>>>>>>> This clause assumes that the local mailer
164738032Speter		**	>> NOTE >> cannot do any further aliasing; that
164838032Speter		**	>>>>>>>>>> function is subsumed by sendmail.
164938032Speter		*/
165038032Speter
165164562Sgshapiro		if (!QS_IS_OK(to->q_state))
165238032Speter			continue;
165338032Speter
165438032Speter		/*
165538032Speter		**  See if this user name is "special".
165638032Speter		**	If the user name has a slash in it, assume that this
165738032Speter		**	is a file -- send it off without further ado.  Note
165838032Speter		**	that this type of addresses is not processed along
165938032Speter		**	with the others, so we fudge on the To person.
166038032Speter		*/
166138032Speter
166238032Speter		if (strcmp(m->m_mailer, "[FILE]") == 0)
166338032Speter		{
166490792Sgshapiro			macdefine(&e->e_macro, A_PERM, 'u', user);
166538032Speter			p = to->q_home;
166638032Speter			if (p == NULL && ctladdr != NULL)
166738032Speter				p = ctladdr->q_home;
166890792Sgshapiro			macdefine(&e->e_macro, A_PERM, 'z', p);
166938032Speter			expand(m->m_argv[1], buf, sizeof buf, e);
167038032Speter			if (strlen(buf) > 0)
167138032Speter				rcode = mailfile(buf, m, ctladdr, SFF_CREAT, e);
167238032Speter			else
167338032Speter			{
167438032Speter				syserr("empty filename specification for mailer %s",
167538032Speter				       m->m_name);
167638032Speter				rcode = EX_CONFIG;
167738032Speter			}
167864562Sgshapiro			giveresponse(rcode, to->q_status, m, NULL,
167990792Sgshapiro				     ctladdr, xstart, e, to);
168090792Sgshapiro			markfailure(e, to, NULL, rcode, true);
168138032Speter			e->e_nsent++;
168238032Speter			if (rcode == EX_OK)
168338032Speter			{
168464562Sgshapiro				to->q_state = QS_SENT;
168538032Speter				if (bitnset(M_LOCALMAILER, m->m_flags) &&
168638032Speter				    bitset(QPINGONSUCCESS, to->q_flags))
168738032Speter				{
168838032Speter					to->q_flags |= QDELIVERED;
168938032Speter					to->q_status = "2.1.5";
169090792Sgshapiro					(void) sm_io_fprintf(e->e_xfp,
169190792Sgshapiro							     SM_TIME_DEFAULT,
169290792Sgshapiro							     "%s... Successfully delivered\n",
169390792Sgshapiro							     to->q_paddr);
169438032Speter				}
169538032Speter			}
169638032Speter			to->q_statdate = curtime();
169790792Sgshapiro			markstats(e, to, STATS_NORMAL);
169838032Speter			continue;
169938032Speter		}
170038032Speter
170138032Speter		/*
170238032Speter		**  Address is verified -- add this user to mailer
170338032Speter		**  argv, and add it to the print list of recipients.
170438032Speter		*/
170538032Speter
170638032Speter		/* link together the chain of recipients */
170738032Speter		to->q_tchain = tochain;
170838032Speter		tochain = to;
170964562Sgshapiro		e->e_to = "[CHAIN]";
171064562Sgshapiro
171190792Sgshapiro		macdefine(&e->e_macro, A_PERM, 'u', user);  /* to user */
171238032Speter		p = to->q_home;
171338032Speter		if (p == NULL && ctladdr != NULL)
171438032Speter			p = ctladdr->q_home;
171590792Sgshapiro		macdefine(&e->e_macro, A_PERM, 'z', p);  /* user's home */
171638032Speter
171764562Sgshapiro		/* set the ${dsn_notify} macro if applicable */
171864562Sgshapiro		if (bitset(QHASNOTIFY, to->q_flags))
171964562Sgshapiro		{
172064562Sgshapiro			char notify[MAXLINE];
172164562Sgshapiro
172264562Sgshapiro			notify[0] = '\0';
172364562Sgshapiro			if (bitset(QPINGONSUCCESS, to->q_flags))
172490792Sgshapiro				(void) sm_strlcat(notify, "SUCCESS,",
172590792Sgshapiro						  sizeof notify);
172664562Sgshapiro			if (bitset(QPINGONFAILURE, to->q_flags))
172790792Sgshapiro				(void) sm_strlcat(notify, "FAILURE,",
172890792Sgshapiro						  sizeof notify);
172964562Sgshapiro			if (bitset(QPINGONDELAY, to->q_flags))
173090792Sgshapiro				(void) sm_strlcat(notify, "DELAY,",
173190792Sgshapiro						  sizeof notify);
173264562Sgshapiro
173364562Sgshapiro			/* Set to NEVER or drop trailing comma */
173464562Sgshapiro			if (notify[0] == '\0')
173590792Sgshapiro				(void) sm_strlcat(notify, "NEVER",
173690792Sgshapiro						  sizeof notify);
173764562Sgshapiro			else
173864562Sgshapiro				notify[strlen(notify) - 1] = '\0';
173964562Sgshapiro
174090792Sgshapiro			macdefine(&e->e_macro, A_TEMP,
174190792Sgshapiro				macid("{dsn_notify}"), notify);
174264562Sgshapiro		}
174364562Sgshapiro		else
174490792Sgshapiro			macdefine(&e->e_macro, A_PERM,
174590792Sgshapiro				macid("{dsn_notify}"), NULL);
174664562Sgshapiro
174738032Speter		/*
174838032Speter		**  Expand out this user into argument list.
174938032Speter		*/
175038032Speter
175138032Speter		if (!clever)
175238032Speter		{
175338032Speter			expand(*mvp, buf, sizeof buf, e);
175490792Sgshapiro			*pvp++ = sm_rpool_strdup_x(e->e_rpool, buf);
175538032Speter			if (pvp >= &pv[MAXPV - 2])
175638032Speter			{
175738032Speter				/* allow some space for trailing parms */
175838032Speter				break;
175938032Speter			}
176038032Speter		}
176138032Speter	}
176238032Speter
176338032Speter	/* see if any addresses still exist */
176464562Sgshapiro	if (tochain == NULL)
176538032Speter	{
176690792Sgshapiro		rcode = 0;
176790792Sgshapiro		goto cleanup;
176838032Speter	}
176938032Speter
177038032Speter	/* print out messages as full list */
177190792Sgshapiro	strsize = 1;
177290792Sgshapiro	for (to = tochain; to != NULL; to = to->q_tchain)
177390792Sgshapiro		strsize += strlen(to->q_paddr) + 1;
177490792Sgshapiro	if (strsize < TOBUFSIZE)
177590792Sgshapiro		strsize = TOBUFSIZE;
177690792Sgshapiro	if (strsize > tobufsize)
177764562Sgshapiro	{
177890792Sgshapiro		SM_FREE_CLR(tobuf);
177990792Sgshapiro		tobuf = sm_pmalloc_x(strsize);
178090792Sgshapiro		tobufsize = strsize;
178164562Sgshapiro	}
178290792Sgshapiro	p = tobuf;
178390792Sgshapiro	*p = '\0';
178490792Sgshapiro	for (to = tochain; to != NULL; to = to->q_tchain)
178590792Sgshapiro	{
178690792Sgshapiro		(void) sm_strlcpyn(p, tobufsize - (p - tobuf), 2,
178790792Sgshapiro				   ",", to->q_paddr);
178890792Sgshapiro		p += strlen(p);
178990792Sgshapiro	}
179038032Speter	e->e_to = tobuf + 1;
179138032Speter
179238032Speter	/*
179338032Speter	**  Fill out any parameters after the $u parameter.
179438032Speter	*/
179538032Speter
179690792Sgshapiro	if (!clever)
179738032Speter	{
179890792Sgshapiro		while (*++mvp != NULL)
179990792Sgshapiro		{
180090792Sgshapiro			expand(*mvp, buf, sizeof buf, e);
180190792Sgshapiro			*pvp++ = sm_rpool_strdup_x(e->e_rpool, buf);
180290792Sgshapiro			if (pvp >= &pv[MAXPV])
180390792Sgshapiro				syserr("554 5.3.0 deliver: pv overflow after $u for %s",
180490792Sgshapiro				       pv[0]);
180590792Sgshapiro		}
180638032Speter	}
180738032Speter	*pvp++ = NULL;
180838032Speter
180938032Speter	/*
181038032Speter	**  Call the mailer.
181138032Speter	**	The argument vector gets built, pipes
181238032Speter	**	are created as necessary, and we fork & exec as
181338032Speter	**	appropriate.
181438032Speter	**	If we are running SMTP, we just need to clean up.
181538032Speter	*/
181638032Speter
181764562Sgshapiro	/* XXX this seems a bit wierd */
181838032Speter	if (ctladdr == NULL && m != ProgMailer && m != FileMailer &&
181938032Speter	    bitset(QGOODUID, e->e_from.q_flags))
182038032Speter		ctladdr = &e->e_from;
182138032Speter
182238032Speter#if NAMED_BIND
182338032Speter	if (ConfigLevel < 2)
182438032Speter		_res.options &= ~(RES_DEFNAMES | RES_DNSRCH);	/* XXX */
182564562Sgshapiro#endif /* NAMED_BIND */
182638032Speter
182738032Speter	if (tTd(11, 1))
182838032Speter	{
182990792Sgshapiro		sm_dprintf("openmailer:");
183038032Speter		printav(pv);
183138032Speter	}
183238032Speter	errno = 0;
183373188Sgshapiro	SM_SET_H_ERRNO(0);
183438032Speter	CurHostName = NULL;
183538032Speter
183638032Speter	/*
183738032Speter	**  Deal with the special case of mail handled through an IPC
183838032Speter	**  connection.
183938032Speter	**	In this case we don't actually fork.  We must be
184038032Speter	**	running SMTP for this to work.  We will return a
184138032Speter	**	zero pid to indicate that we are running IPC.
184238032Speter	**  We also handle a debug version that just talks to stdin/out.
184338032Speter	*/
184438032Speter
184538032Speter	curhost = NULL;
184638032Speter	SmtpPhase = NULL;
184738032Speter	mci = NULL;
184838032Speter
184938032Speter#if XDEBUG
185038032Speter	{
185138032Speter		char wbuf[MAXLINE];
185238032Speter
185338032Speter		/* make absolutely certain 0, 1, and 2 are in use */
185490792Sgshapiro		(void) sm_snprintf(wbuf, sizeof wbuf, "%s... openmailer(%s)",
185590792Sgshapiro				   shortenstring(e->e_to, MAXSHORTSTR),
185690792Sgshapiro				   m->m_name);
185738032Speter		checkfd012(wbuf);
185838032Speter	}
185964562Sgshapiro#endif /* XDEBUG */
186038032Speter
186138032Speter	/* check for 8-bit available */
186238032Speter	if (bitset(EF_HAS8BIT, e->e_flags) &&
186338032Speter	    bitnset(M_7BITS, m->m_flags) &&
186438032Speter	    (bitset(EF_DONT_MIME, e->e_flags) ||
186538032Speter	     !(bitset(MM_MIME8BIT, MimeMode) ||
186638032Speter	       (bitset(EF_IS_MIME, e->e_flags) &&
186764562Sgshapiro		bitset(MM_CVTMIME, MimeMode)))))
186838032Speter	{
186964562Sgshapiro		e->e_status = "5.6.3";
187064562Sgshapiro		usrerrenh(e->e_status,
187190792Sgshapiro			  "554 Cannot send 8-bit data to 7-bit destination");
187238032Speter		rcode = EX_DATAERR;
187338032Speter		goto give_up;
187438032Speter	}
187538032Speter
187638032Speter	if (tTd(62, 8))
187738032Speter		checkfds("before delivery");
187838032Speter
187938032Speter	/* check for Local Person Communication -- not for mortals!!! */
188038032Speter	if (strcmp(m->m_mailer, "[LPC]") == 0)
188138032Speter	{
188290792Sgshapiro#if _FFR_CACHE_LPC
188390792Sgshapiro		if (clever)
188490792Sgshapiro		{
188590792Sgshapiro			/* flush any expired connections */
188690792Sgshapiro			(void) mci_scan(NULL);
188790792Sgshapiro
188890792Sgshapiro			/* try to get a cached connection or just a slot */
188990792Sgshapiro			mci = mci_get(m->m_name, m);
189090792Sgshapiro			if (mci->mci_host == NULL)
189190792Sgshapiro				mci->mci_host = m->m_name;
189290792Sgshapiro			CurHostName = mci->mci_host;
189390792Sgshapiro			if (mci->mci_state != MCIS_CLOSED)
189490792Sgshapiro			{
189590792Sgshapiro				message("Using cached SMTP/LPC connection for %s...",
189690792Sgshapiro					m->m_name);
189790792Sgshapiro				mci->mci_deliveries++;
189890792Sgshapiro				goto do_transfer;
189990792Sgshapiro			}
190090792Sgshapiro		}
190190792Sgshapiro		else
190290792Sgshapiro		{
190390792Sgshapiro			mci = mci_new(e->e_rpool);
190490792Sgshapiro		}
190590792Sgshapiro		mci->mci_in = smioin;
190690792Sgshapiro		mci->mci_out = smioout;
190790792Sgshapiro		mci->mci_mailer = m;
190890792Sgshapiro		mci->mci_host = m->m_name;
190990792Sgshapiro		if (clever)
191090792Sgshapiro		{
191190792Sgshapiro			mci->mci_state = MCIS_OPENING;
191290792Sgshapiro			mci_cache(mci);
191390792Sgshapiro		}
191490792Sgshapiro		else
191590792Sgshapiro			mci->mci_state = MCIS_OPEN;
191690792Sgshapiro#else /* _FFR_CACHE_LPC */
191790792Sgshapiro		mci = mci_new(e->e_rpool);
191890792Sgshapiro		mci->mci_in = smioin;
191990792Sgshapiro		mci->mci_out = smioout;
192038032Speter		mci->mci_state = clever ? MCIS_OPENING : MCIS_OPEN;
192138032Speter		mci->mci_mailer = m;
192290792Sgshapiro#endif /* _FFR_CACHE_LPC */
192338032Speter	}
192490792Sgshapiro	else if (strcmp(m->m_mailer, "[IPC]") == 0)
192538032Speter	{
192638032Speter		register int i;
192738032Speter
192838032Speter		if (pv[0] == NULL || pv[1] == NULL || pv[1][0] == '\0')
192938032Speter		{
193064562Sgshapiro			syserr("null destination for %s mailer", m->m_mailer);
193138032Speter			rcode = EX_CONFIG;
193238032Speter			goto give_up;
193338032Speter		}
193438032Speter
193564562Sgshapiro# if NETUNIX
193664562Sgshapiro		if (strcmp(pv[0], "FILE") == 0)
193764562Sgshapiro		{
193864562Sgshapiro			curhost = CurHostName = "localhost";
193964562Sgshapiro			mux_path = pv[1];
194064562Sgshapiro		}
194164562Sgshapiro		else
194264562Sgshapiro# endif /* NETUNIX */
194364562Sgshapiro		{
194464562Sgshapiro			CurHostName = pv[1];
194564562Sgshapiro			curhost = hostsignature(m, pv[1]);
194664562Sgshapiro		}
194738032Speter
194838032Speter		if (curhost == NULL || curhost[0] == '\0')
194938032Speter		{
195038032Speter			syserr("null host signature for %s", pv[1]);
195138032Speter			rcode = EX_CONFIG;
195238032Speter			goto give_up;
195338032Speter		}
195438032Speter
195538032Speter		if (!clever)
195638032Speter		{
195764562Sgshapiro			syserr("554 5.3.5 non-clever IPC");
195838032Speter			rcode = EX_CONFIG;
195938032Speter			goto give_up;
196038032Speter		}
196164562Sgshapiro		if (pv[2] != NULL
196264562Sgshapiro# if NETUNIX
196364562Sgshapiro		    && mux_path == NULL
196464562Sgshapiro# endif /* NETUNIX */
196564562Sgshapiro		    )
196638032Speter		{
196790792Sgshapiro			port = htons((unsigned short) atoi(pv[2]));
196838032Speter			if (port == 0)
196938032Speter			{
197064562Sgshapiro# ifdef NO_GETSERVBYNAME
197164562Sgshapiro				syserr("Invalid port number: %s", pv[2]);
197264562Sgshapiro# else /* NO_GETSERVBYNAME */
197338032Speter				struct servent *sp = getservbyname(pv[2], "tcp");
197438032Speter
197538032Speter				if (sp == NULL)
197638032Speter					syserr("Service %s unknown", pv[2]);
197738032Speter				else
197838032Speter					port = sp->s_port;
197964562Sgshapiro# endif /* NO_GETSERVBYNAME */
198038032Speter			}
198138032Speter		}
198264562Sgshapiro
198364562Sgshapiro		nummxhosts = parse_hostsignature(curhost, mxhosts, m);
198490792Sgshapiro		if (TimeOuts.to_aconnect > 0)
198590792Sgshapiro			enough = curtime() + TimeOuts.to_aconnect;
198638032Spetertryhost:
198764562Sgshapiro		while (hostnum < nummxhosts)
198838032Speter		{
198964562Sgshapiro			char sep = ':';
199064562Sgshapiro			char *endp;
199138032Speter			static char hostbuf[MAXNAME + 1];
199238032Speter
199364562Sgshapiro# if NETINET6
199464562Sgshapiro			if (*mxhosts[hostnum] == '[')
199538032Speter			{
199664562Sgshapiro				endp = strchr(mxhosts[hostnum] + 1, ']');
199764562Sgshapiro				if (endp != NULL)
199864562Sgshapiro					endp = strpbrk(endp + 1, ":,");
199964562Sgshapiro			}
200064562Sgshapiro			else
200164562Sgshapiro				endp = strpbrk(mxhosts[hostnum], ":,");
200264562Sgshapiro# else /* NETINET6 */
200364562Sgshapiro			endp = strpbrk(mxhosts[hostnum], ":,");
200464562Sgshapiro# endif /* NETINET6 */
200564562Sgshapiro			if (endp != NULL)
200664562Sgshapiro			{
200764562Sgshapiro				sep = *endp;
200864562Sgshapiro				*endp = '\0';
200964562Sgshapiro			}
201064562Sgshapiro
201190792Sgshapiro			if (hostnum == 1 && skip_back != NULL)
201290792Sgshapiro			{
201390792Sgshapiro				/*
201490792Sgshapiro				**  Coattail piggybacking is no longer an
201590792Sgshapiro				**  option with the mail host next to be tried
201690792Sgshapiro				**  no longer the lowest MX preference
201790792Sgshapiro				**  (hostnum == 1 meaning we're on the second
201890792Sgshapiro				**  preference). We do not try to coattail
201990792Sgshapiro				**  piggyback more than the first MX preference.
202090792Sgshapiro				**  Revert 'tochain' to last location for
202190792Sgshapiro				**  coincidental piggybacking. This works this
202290792Sgshapiro				**  easily because the q_tchain kept getting
202390792Sgshapiro				**  added to the top of the linked list.
202490792Sgshapiro				*/
202590792Sgshapiro
202690792Sgshapiro				tochain = skip_back;
202790792Sgshapiro			}
202890792Sgshapiro
202964562Sgshapiro			if (*mxhosts[hostnum] == '\0')
203064562Sgshapiro			{
203138032Speter				syserr("deliver: null host name in signature");
203264562Sgshapiro				hostnum++;
203364562Sgshapiro				if (endp != NULL)
203464562Sgshapiro					*endp = sep;
203538032Speter				continue;
203638032Speter			}
203790792Sgshapiro			(void) sm_strlcpy(hostbuf, mxhosts[hostnum],
203890792Sgshapiro					  sizeof hostbuf);
203964562Sgshapiro			hostnum++;
204064562Sgshapiro			if (endp != NULL)
204164562Sgshapiro				*endp = sep;
204238032Speter
204338032Speter			/* see if we already know that this host is fried */
204438032Speter			CurHostName = hostbuf;
204538032Speter			mci = mci_get(hostbuf, m);
204638032Speter			if (mci->mci_state != MCIS_CLOSED)
204738032Speter			{
204890792Sgshapiro				char *type;
204990792Sgshapiro
205038032Speter				if (tTd(11, 1))
205138032Speter				{
205290792Sgshapiro					sm_dprintf("openmailer: ");
205390792Sgshapiro					mci_dump(mci, false);
205438032Speter				}
205538032Speter				CurHostName = mci->mci_host;
205690792Sgshapiro				if (bitnset(M_LMTP, m->m_flags))
205790792Sgshapiro					type = "L";
205890792Sgshapiro				else if (bitset(MCIF_ESMTP, mci->mci_flags))
205990792Sgshapiro					type = "ES";
206090792Sgshapiro				else
206190792Sgshapiro					type = "S";
206290792Sgshapiro				message("Using cached %sMTP connection to %s via %s...",
206390792Sgshapiro					type, hostbuf, m->m_name);
206464562Sgshapiro				mci->mci_deliveries++;
206538032Speter				break;
206638032Speter			}
206738032Speter			mci->mci_mailer = m;
206838032Speter			if (mci->mci_exitstat != EX_OK)
206938032Speter			{
207038032Speter				if (mci->mci_exitstat == EX_TEMPFAIL)
207190792Sgshapiro					goodmxfound = true;
207238032Speter				continue;
207338032Speter			}
207438032Speter
207538032Speter			if (mci_lock_host(mci) != EX_OK)
207638032Speter			{
207738032Speter				mci_setstat(mci, EX_TEMPFAIL, "4.4.5", NULL);
207890792Sgshapiro				goodmxfound = true;
207938032Speter				continue;
208038032Speter			}
208138032Speter
208238032Speter			/* try the connection */
208390792Sgshapiro			sm_setproctitle(true, e, "%s %s: %s",
208464562Sgshapiro					qid_printname(e),
208564562Sgshapiro					hostbuf, "user open");
208664562Sgshapiro# if NETUNIX
208764562Sgshapiro			if (mux_path != NULL)
208864562Sgshapiro			{
208938032Speter				message("Connecting to %s via %s...",
209064562Sgshapiro					mux_path, m->m_name);
209190792Sgshapiro				i = makeconnection_ds((char *) mux_path, mci);
209264562Sgshapiro			}
209338032Speter			else
209464562Sgshapiro# endif /* NETUNIX */
209564562Sgshapiro			{
209664562Sgshapiro				if (port == 0)
209764562Sgshapiro					message("Connecting to %s via %s...",
209864562Sgshapiro						hostbuf, m->m_name);
209964562Sgshapiro				else
210064562Sgshapiro					message("Connecting to %s port %d via %s...",
210164562Sgshapiro						hostbuf, ntohs(port),
210264562Sgshapiro						m->m_name);
210390792Sgshapiro				i = makeconnection(hostbuf, port, mci, e,
210490792Sgshapiro						   enough);
210564562Sgshapiro			}
210677349Sgshapiro			mci->mci_errno = errno;
210738032Speter			mci->mci_lastuse = curtime();
210864562Sgshapiro			mci->mci_deliveries = 0;
210938032Speter			mci->mci_exitstat = i;
211064562Sgshapiro# if NAMED_BIND
211138032Speter			mci->mci_herrno = h_errno;
211264562Sgshapiro# endif /* NAMED_BIND */
211390792Sgshapiro
211490792Sgshapiro			/*
211590792Sgshapiro			**  Have we tried long enough to get a connection?
211690792Sgshapiro			**	If yes, skip to the fallback MX hosts
211790792Sgshapiro			**	(if existent).
211890792Sgshapiro			*/
211990792Sgshapiro
212090792Sgshapiro			if (enough > 0 && mci->mci_lastuse >= enough)
212190792Sgshapiro			{
212290792Sgshapiro				int h;
212390792Sgshapiro# if NAMED_BIND
212490792Sgshapiro				extern int NumFallBackMXHosts;
212590792Sgshapiro# else /* NAMED_BIND */
212690792Sgshapiro				const int NumFallBackMXHosts = 0;
212790792Sgshapiro# endif /* NAMED_BIND */
212890792Sgshapiro
212990792Sgshapiro				if (hostnum < nummxhosts && LogLevel > 9)
213090792Sgshapiro					sm_syslog(LOG_INFO, e->e_id,
213190792Sgshapiro						  "Timeout.to_aconnect occurred before exhausting all addresses");
213290792Sgshapiro
213390792Sgshapiro				/* turn off timeout if fallback available */
213490792Sgshapiro				if (NumFallBackMXHosts > 0)
213590792Sgshapiro					enough = 0;
213690792Sgshapiro
213790792Sgshapiro				/* skip to a fallback MX host */
213890792Sgshapiro				h = nummxhosts - NumFallBackMXHosts;
213990792Sgshapiro				if (hostnum < h)
214090792Sgshapiro					hostnum = h;
214190792Sgshapiro			}
214238032Speter			if (i == EX_OK)
214338032Speter			{
214490792Sgshapiro				goodmxfound = true;
214594334Sgshapiro				markstats(e, firstto, STATS_CONNECT);
214638032Speter				mci->mci_state = MCIS_OPENING;
214738032Speter				mci_cache(mci);
214838032Speter				if (TrafficLogFile != NULL)
214990792Sgshapiro					(void) sm_io_fprintf(TrafficLogFile,
215090792Sgshapiro							     SM_TIME_DEFAULT,
215190792Sgshapiro							     "%05d === CONNECT %s\n",
215290792Sgshapiro							     (int) CurrentPid,
215390792Sgshapiro							     hostbuf);
215438032Speter				break;
215538032Speter			}
215638032Speter			else
215738032Speter			{
215864562Sgshapiro				if (tTd(11, 1))
215990792Sgshapiro					sm_dprintf("openmailer: makeconnection => stat=%d, errno=%d\n",
216090792Sgshapiro						   i, errno);
216138032Speter				if (i == EX_TEMPFAIL)
216290792Sgshapiro					goodmxfound = true;
216338032Speter				mci_unlock_host(mci);
216438032Speter			}
216538032Speter
216638032Speter			/* enter status of this host */
216738032Speter			setstat(i);
216838032Speter
216938032Speter			/* should print some message here for -v mode */
217038032Speter		}
217138032Speter		if (mci == NULL)
217238032Speter		{
217338032Speter			syserr("deliver: no host name");
217438032Speter			rcode = EX_SOFTWARE;
217538032Speter			goto give_up;
217638032Speter		}
217738032Speter		mci->mci_pid = 0;
217838032Speter	}
217938032Speter	else
218038032Speter	{
218138032Speter		/* flush any expired connections */
218238032Speter		(void) mci_scan(NULL);
218338032Speter		mci = NULL;
218438032Speter
218538032Speter		if (bitnset(M_LMTP, m->m_flags))
218638032Speter		{
218738032Speter			/* try to get a cached connection */
218838032Speter			mci = mci_get(m->m_name, m);
218938032Speter			if (mci->mci_host == NULL)
219038032Speter				mci->mci_host = m->m_name;
219138032Speter			CurHostName = mci->mci_host;
219238032Speter			if (mci->mci_state != MCIS_CLOSED)
219338032Speter			{
219438032Speter				message("Using cached LMTP connection for %s...",
219538032Speter					m->m_name);
219664562Sgshapiro				mci->mci_deliveries++;
219738032Speter				goto do_transfer;
219838032Speter			}
219938032Speter		}
220038032Speter
220138032Speter		/* announce the connection to verbose listeners */
220238032Speter		if (host == NULL || host[0] == '\0')
220338032Speter			message("Connecting to %s...", m->m_name);
220438032Speter		else
220538032Speter			message("Connecting to %s via %s...", host, m->m_name);
220638032Speter		if (TrafficLogFile != NULL)
220738032Speter		{
220838032Speter			char **av;
220938032Speter
221090792Sgshapiro			(void) sm_io_fprintf(TrafficLogFile, SM_TIME_DEFAULT,
221190792Sgshapiro					     "%05d === EXEC", (int) CurrentPid);
221238032Speter			for (av = pv; *av != NULL; av++)
221390792Sgshapiro				(void) sm_io_fprintf(TrafficLogFile,
221490792Sgshapiro						     SM_TIME_DEFAULT, " %s",
221590792Sgshapiro						     *av);
221690792Sgshapiro			(void) sm_io_fprintf(TrafficLogFile, SM_TIME_DEFAULT,
221790792Sgshapiro					     "\n");
221838032Speter		}
221938032Speter
222038032Speter#if XDEBUG
222138032Speter		checkfd012("before creating mail pipe");
222264562Sgshapiro#endif /* XDEBUG */
222338032Speter
222438032Speter		/* create a pipe to shove the mail through */
222538032Speter		if (pipe(mpvect) < 0)
222638032Speter		{
222738032Speter			syserr("%s... openmailer(%s): pipe (to mailer)",
222890792Sgshapiro			       shortenstring(e->e_to, MAXSHORTSTR), m->m_name);
222938032Speter			if (tTd(11, 1))
223090792Sgshapiro				sm_dprintf("openmailer: NULL\n");
223138032Speter			rcode = EX_OSERR;
223238032Speter			goto give_up;
223338032Speter		}
223438032Speter
223538032Speter#if XDEBUG
223638032Speter		/* make sure we didn't get one of the standard I/O files */
223738032Speter		if (mpvect[0] < 3 || mpvect[1] < 3)
223838032Speter		{
223938032Speter			syserr("%s... openmailer(%s): bogus mpvect %d %d",
224090792Sgshapiro			       shortenstring(e->e_to, MAXSHORTSTR), m->m_name,
224190792Sgshapiro			       mpvect[0], mpvect[1]);
224290792Sgshapiro			printopenfds(true);
224338032Speter			if (tTd(11, 1))
224490792Sgshapiro				sm_dprintf("openmailer: NULL\n");
224538032Speter			rcode = EX_OSERR;
224638032Speter			goto give_up;
224738032Speter		}
224838032Speter
224938032Speter		/* make sure system call isn't dead meat */
225038032Speter		checkfdopen(mpvect[0], "mpvect[0]");
225138032Speter		checkfdopen(mpvect[1], "mpvect[1]");
225238032Speter		if (mpvect[0] == mpvect[1] ||
225338032Speter		    (e->e_lockfp != NULL &&
225490792Sgshapiro		     (mpvect[0] == sm_io_getinfo(e->e_lockfp, SM_IO_WHAT_FD,
225590792Sgshapiro						 NULL) ||
225690792Sgshapiro		      mpvect[1] == sm_io_getinfo(e->e_lockfp, SM_IO_WHAT_FD,
225790792Sgshapiro						 NULL))))
225838032Speter		{
225938032Speter			if (e->e_lockfp == NULL)
226038032Speter				syserr("%s... openmailer(%s): overlapping mpvect %d %d",
226190792Sgshapiro				       shortenstring(e->e_to, MAXSHORTSTR),
226290792Sgshapiro				       m->m_name, mpvect[0], mpvect[1]);
226338032Speter			else
226438032Speter				syserr("%s... openmailer(%s): overlapping mpvect %d %d, lockfp = %d",
226590792Sgshapiro				       shortenstring(e->e_to, MAXSHORTSTR),
226690792Sgshapiro				       m->m_name, mpvect[0], mpvect[1],
226790792Sgshapiro				       sm_io_getinfo(e->e_lockfp,
226890792Sgshapiro						     SM_IO_WHAT_FD, NULL));
226938032Speter		}
227064562Sgshapiro#endif /* XDEBUG */
227138032Speter
227264562Sgshapiro		/* create a return pipe */
227364562Sgshapiro		if (pipe(rpvect) < 0)
227438032Speter		{
227564562Sgshapiro			syserr("%s... openmailer(%s): pipe (from mailer)",
227690792Sgshapiro			       shortenstring(e->e_to, MAXSHORTSTR),
227790792Sgshapiro			       m->m_name);
227864562Sgshapiro			(void) close(mpvect[0]);
227964562Sgshapiro			(void) close(mpvect[1]);
228064562Sgshapiro			if (tTd(11, 1))
228190792Sgshapiro				sm_dprintf("openmailer: NULL\n");
228264562Sgshapiro			rcode = EX_OSERR;
228364562Sgshapiro			goto give_up;
228438032Speter		}
228564562Sgshapiro#if XDEBUG
228664562Sgshapiro		checkfdopen(rpvect[0], "rpvect[0]");
228764562Sgshapiro		checkfdopen(rpvect[1], "rpvect[1]");
228864562Sgshapiro#endif /* XDEBUG */
228938032Speter
229038032Speter		/*
229138032Speter		**  Actually fork the mailer process.
229238032Speter		**	DOFORK is clever about retrying.
229338032Speter		**
229438032Speter		**	Dispose of SIGCHLD signal catchers that may be laying
229564562Sgshapiro		**	around so that endmailer will get it.
229638032Speter		*/
229738032Speter
229890792Sgshapiro		if (e->e_xfp != NULL)	/* for debugging */
229990792Sgshapiro			(void) sm_io_flush(e->e_xfp, SM_TIME_DEFAULT);
230090792Sgshapiro		(void) sm_io_flush(smioout, SM_TIME_DEFAULT);
230190792Sgshapiro		(void) sm_signal(SIGCHLD, SIG_DFL);
230264562Sgshapiro
230364562Sgshapiro
230438032Speter		DOFORK(FORK);
230538032Speter		/* pid is set by DOFORK */
230664562Sgshapiro
230738032Speter		if (pid < 0)
230838032Speter		{
230938032Speter			/* failure */
231038032Speter			syserr("%s... openmailer(%s): cannot fork",
231190792Sgshapiro			       shortenstring(e->e_to, MAXSHORTSTR), m->m_name);
231238032Speter			(void) close(mpvect[0]);
231338032Speter			(void) close(mpvect[1]);
231464562Sgshapiro			(void) close(rpvect[0]);
231564562Sgshapiro			(void) close(rpvect[1]);
231638032Speter			if (tTd(11, 1))
231790792Sgshapiro				sm_dprintf("openmailer: NULL\n");
231838032Speter			rcode = EX_OSERR;
231938032Speter			goto give_up;
232038032Speter		}
232138032Speter		else if (pid == 0)
232238032Speter		{
232338032Speter			int i;
232464562Sgshapiro			int save_errno;
232590792Sgshapiro			int sff;
232638032Speter			int new_euid = NO_UID;
232738032Speter			int new_ruid = NO_UID;
232838032Speter			int new_gid = NO_GID;
232990792Sgshapiro			char *user = NULL;
233038032Speter			struct stat stb;
233138032Speter			extern int DtableSize;
233238032Speter
233390792Sgshapiro			CurrentPid = getpid();
233490792Sgshapiro
233580785Sgshapiro			/* clear the events to turn off SIGALRMs */
233690792Sgshapiro			sm_clear_events();
233780785Sgshapiro
233877349Sgshapiro			/* Reset global flags */
233977349Sgshapiro			RestartRequest = NULL;
234090792Sgshapiro			RestartWorkGroup = false;
234177349Sgshapiro			ShutdownRequest = NULL;
234277349Sgshapiro			PendingSignal = 0;
234377349Sgshapiro
234438032Speter			if (e->e_lockfp != NULL)
234590792Sgshapiro				(void) close(sm_io_getinfo(e->e_lockfp,
234690792Sgshapiro							   SM_IO_WHAT_FD,
234790792Sgshapiro							   NULL));
234838032Speter
234938032Speter			/* child -- set up input & exec mailer */
235090792Sgshapiro			(void) sm_signal(SIGALRM, sm_signal_noop);
235190792Sgshapiro			(void) sm_signal(SIGCHLD, SIG_DFL);
235290792Sgshapiro			(void) sm_signal(SIGHUP, SIG_IGN);
235390792Sgshapiro			(void) sm_signal(SIGINT, SIG_IGN);
235490792Sgshapiro			(void) sm_signal(SIGTERM, SIG_DFL);
235580785Sgshapiro# ifdef SIGUSR1
235690792Sgshapiro			(void) sm_signal(SIGUSR1, sm_signal_noop);
235780785Sgshapiro# endif /* SIGUSR1 */
235838032Speter
235938032Speter			if (m != FileMailer || stat(tochain->q_user, &stb) < 0)
236038032Speter				stb.st_mode = 0;
236138032Speter
236264562Sgshapiro# if HASSETUSERCONTEXT
236338032Speter			/*
236438032Speter			**  Set user resources.
236538032Speter			*/
236638032Speter
236738032Speter			if (contextaddr != NULL)
236838032Speter			{
2369110560Sgshapiro				int sucflags;
237038032Speter				struct passwd *pwd;
237138032Speter
237238032Speter				if (contextaddr->q_ruser != NULL)
237338032Speter					pwd = sm_getpwnam(contextaddr->q_ruser);
237438032Speter				else
237538032Speter					pwd = sm_getpwnam(contextaddr->q_user);
2376110560Sgshapiro				sucflags = LOGIN_SETRESOURCES|LOGIN_SETPRIORITY;
2377110560Sgshapiro#ifdef LOGIN_SETMAC
2378110560Sgshapiro				sucflags |= LOGIN_SETMAC;
2379110560Sgshapiro#endif /* LOGIN_SETMAC */
2380102528Sgshapiro				if (pwd != NULL &&
2381102528Sgshapiro				    setusercontext(NULL, pwd, pwd->pw_uid,
2382110560Sgshapiro						   sucflags) == -1 &&
2383102528Sgshapiro				    suidwarn)
2384102528Sgshapiro				{
2385102528Sgshapiro					syserr("openmailer: setusercontext() failed");
2386102528Sgshapiro					exit(EX_TEMPFAIL);
2387102528Sgshapiro				}
238838032Speter			}
238964562Sgshapiro# endif /* HASSETUSERCONTEXT */
239038032Speter
239190792Sgshapiro#if HASNICE
239238032Speter			/* tweak niceness */
239338032Speter			if (m->m_nice != 0)
239464562Sgshapiro				(void) nice(m->m_nice);
239590792Sgshapiro#endif /* HASNICE */
239638032Speter
239738032Speter			/* reset group id */
239838032Speter			if (bitnset(M_SPECIFIC_UID, m->m_flags))
239938032Speter				new_gid = m->m_gid;
240038032Speter			else if (bitset(S_ISGID, stb.st_mode))
240138032Speter				new_gid = stb.st_gid;
240238032Speter			else if (ctladdr != NULL && ctladdr->q_gid != 0)
240338032Speter			{
240438032Speter				if (!DontInitGroups)
240538032Speter				{
240690792Sgshapiro					user = ctladdr->q_ruser;
240790792Sgshapiro					if (user == NULL)
240890792Sgshapiro						user = ctladdr->q_user;
240938032Speter
241090792Sgshapiro					if (initgroups(user,
241190792Sgshapiro						       ctladdr->q_gid) == -1
241290792Sgshapiro					    && suidwarn)
241364562Sgshapiro					{
241438032Speter						syserr("openmailer: initgroups(%s, %d) failed",
241590792Sgshapiro							user, ctladdr->q_gid);
241664562Sgshapiro						exit(EX_TEMPFAIL);
241764562Sgshapiro					}
241838032Speter				}
241938032Speter				else
242038032Speter				{
242138032Speter					GIDSET_T gidset[1];
242238032Speter
242338032Speter					gidset[0] = ctladdr->q_gid;
242490792Sgshapiro					if (setgroups(1, gidset) == -1
242590792Sgshapiro					    && suidwarn)
242664562Sgshapiro					{
242738032Speter						syserr("openmailer: setgroups() failed");
242864562Sgshapiro						exit(EX_TEMPFAIL);
242964562Sgshapiro					}
243038032Speter				}
243138032Speter				new_gid = ctladdr->q_gid;
243238032Speter			}
243338032Speter			else
243438032Speter			{
243538032Speter				if (!DontInitGroups)
243638032Speter				{
243790792Sgshapiro					user = DefUser;
243890792Sgshapiro					if (initgroups(DefUser, DefGid) == -1 &&
243990792Sgshapiro					    suidwarn)
244064562Sgshapiro					{
244138032Speter						syserr("openmailer: initgroups(%s, %d) failed",
244290792Sgshapiro						       DefUser, DefGid);
244364562Sgshapiro						exit(EX_TEMPFAIL);
244464562Sgshapiro					}
244538032Speter				}
244638032Speter				else
244738032Speter				{
244838032Speter					GIDSET_T gidset[1];
244938032Speter
245038032Speter					gidset[0] = DefGid;
245190792Sgshapiro					if (setgroups(1, gidset) == -1
245290792Sgshapiro					    && suidwarn)
245364562Sgshapiro					{
245438032Speter						syserr("openmailer: setgroups() failed");
245564562Sgshapiro						exit(EX_TEMPFAIL);
245664562Sgshapiro					}
245738032Speter				}
245838032Speter				if (m->m_gid == 0)
245938032Speter					new_gid = DefGid;
246038032Speter				else
246138032Speter					new_gid = m->m_gid;
246238032Speter			}
246364562Sgshapiro			if (new_gid != NO_GID)
246464562Sgshapiro			{
246564562Sgshapiro				if (RunAsUid != 0 &&
246664562Sgshapiro				    bitnset(M_SPECIFIC_UID, m->m_flags) &&
246764562Sgshapiro				    new_gid != getgid() &&
246864562Sgshapiro				    new_gid != getegid())
246964562Sgshapiro				{
247064562Sgshapiro					/* Only root can change the gid */
247190792Sgshapiro					syserr("openmailer: insufficient privileges to change gid, RunAsUid=%d, new_gid=%d, gid=%d, egid=%d",
247290792Sgshapiro					       (int) RunAsUid, (int) new_gid,
247390792Sgshapiro					       (int) getgid(), (int) getegid());
247464562Sgshapiro					exit(EX_TEMPFAIL);
247564562Sgshapiro				}
247638032Speter
247764562Sgshapiro				if (setgid(new_gid) < 0 && suidwarn)
247864562Sgshapiro				{
247964562Sgshapiro					syserr("openmailer: setgid(%ld) failed",
248064562Sgshapiro					       (long) new_gid);
248164562Sgshapiro					exit(EX_TEMPFAIL);
248264562Sgshapiro				}
248364562Sgshapiro			}
248464562Sgshapiro
248564562Sgshapiro			/* change root to some "safe" directory */
248664562Sgshapiro			if (m->m_rootdir != NULL)
248764562Sgshapiro			{
248898121Sgshapiro				expand(m->m_rootdir, cbuf, sizeof cbuf, e);
248964562Sgshapiro				if (tTd(11, 20))
249090792Sgshapiro					sm_dprintf("openmailer: chroot %s\n",
249198121Sgshapiro						   cbuf);
249298121Sgshapiro				if (chroot(cbuf) < 0)
249364562Sgshapiro				{
249464562Sgshapiro					syserr("openmailer: Cannot chroot(%s)",
249598121Sgshapiro					       cbuf);
249664562Sgshapiro					exit(EX_TEMPFAIL);
249764562Sgshapiro				}
249864562Sgshapiro				if (chdir("/") < 0)
249964562Sgshapiro				{
250064562Sgshapiro					syserr("openmailer: cannot chdir(/)");
250164562Sgshapiro					exit(EX_TEMPFAIL);
250264562Sgshapiro				}
250364562Sgshapiro			}
250464562Sgshapiro
250538032Speter			/* reset user id */
250638032Speter			endpwent();
250790792Sgshapiro			sm_mbdb_terminate();
250838032Speter			if (bitnset(M_SPECIFIC_UID, m->m_flags))
250980785Sgshapiro			{
251038032Speter				new_euid = m->m_uid;
251180785Sgshapiro
251280785Sgshapiro				/*
251380785Sgshapiro				**  Undo the effects of the uid change in main
251480785Sgshapiro				**  for signal handling.  The real uid may
251580785Sgshapiro				**  be used by mailer in adding a "From "
251680785Sgshapiro				**  line.
251780785Sgshapiro				*/
251880785Sgshapiro
251980785Sgshapiro				if (RealUid != 0 && RealUid != getuid())
252090792Sgshapiro				{
252190792Sgshapiro# if MAILER_SETUID_METHOD == USE_SETEUID
252290792Sgshapiro#  if HASSETREUID
252390792Sgshapiro					if (setreuid(RealUid, geteuid()) < 0)
252490792Sgshapiro					{
252590792Sgshapiro						syserr("openmailer: setreuid(%d, %d) failed",
252690792Sgshapiro						       (int) RealUid, (int) geteuid());
252790792Sgshapiro						exit(EX_OSERR);
252890792Sgshapiro					}
252990792Sgshapiro#  endif /* HASSETREUID */
253090792Sgshapiro# endif /* MAILER_SETUID_METHOD == USE_SETEUID */
253190792Sgshapiro# if MAILER_SETUID_METHOD == USE_SETREUID
253280785Sgshapiro					new_ruid = RealUid;
253390792Sgshapiro# endif /* MAILER_SETUID_METHOD == USE_SETREUID */
253490792Sgshapiro				}
253580785Sgshapiro			}
253638032Speter			else if (bitset(S_ISUID, stb.st_mode))
253738032Speter				new_ruid = stb.st_uid;
253838032Speter			else if (ctladdr != NULL && ctladdr->q_uid != 0)
253938032Speter				new_ruid = ctladdr->q_uid;
254038032Speter			else if (m->m_uid != 0)
254138032Speter				new_ruid = m->m_uid;
254238032Speter			else
254338032Speter				new_ruid = DefUid;
254494334Sgshapiro
254594334Sgshapiro# if _FFR_USE_SETLOGIN
254694334Sgshapiro			/* run disconnected from terminal and set login name */
254794334Sgshapiro			if (setsid() >= 0 &&
254894334Sgshapiro			    ctladdr != NULL && ctladdr->q_uid != 0 &&
254994334Sgshapiro			    new_euid == ctladdr->q_uid)
255094334Sgshapiro			{
255194334Sgshapiro				struct passwd *pwd;
255294334Sgshapiro
255394334Sgshapiro				pwd = sm_getpwuid(ctladdr->q_uid);
255494334Sgshapiro				if (pwd != NULL && suidwarn)
255594334Sgshapiro					(void) setlogin(pwd->pw_name);
255694334Sgshapiro				endpwent();
255794334Sgshapiro			}
255894334Sgshapiro# endif /* _FFR_USE_SETLOGIN */
255994334Sgshapiro
256038032Speter			if (new_euid != NO_UID)
256138032Speter			{
256264562Sgshapiro				if (RunAsUid != 0 && new_euid != RunAsUid)
256364562Sgshapiro				{
256464562Sgshapiro					/* Only root can change the uid */
256590792Sgshapiro					syserr("openmailer: insufficient privileges to change uid, new_euid=%d, RunAsUid=%d",
256690792Sgshapiro					       (int) new_euid, (int) RunAsUid);
256764562Sgshapiro					exit(EX_TEMPFAIL);
256864562Sgshapiro				}
256964562Sgshapiro
257038032Speter				vendor_set_uid(new_euid);
257164562Sgshapiro# if MAILER_SETUID_METHOD == USE_SETEUID
257238032Speter				if (seteuid(new_euid) < 0 && suidwarn)
257364562Sgshapiro				{
257438032Speter					syserr("openmailer: seteuid(%ld) failed",
257590792Sgshapiro					       (long) new_euid);
257664562Sgshapiro					exit(EX_TEMPFAIL);
257764562Sgshapiro				}
257864562Sgshapiro# endif /* MAILER_SETUID_METHOD == USE_SETEUID */
257964562Sgshapiro# if MAILER_SETUID_METHOD == USE_SETREUID
258038032Speter				if (setreuid(new_ruid, new_euid) < 0 && suidwarn)
258164562Sgshapiro				{
258238032Speter					syserr("openmailer: setreuid(%ld, %ld) failed",
258390792Sgshapiro					       (long) new_ruid, (long) new_euid);
258464562Sgshapiro					exit(EX_TEMPFAIL);
258564562Sgshapiro				}
258664562Sgshapiro# endif /* MAILER_SETUID_METHOD == USE_SETREUID */
258764562Sgshapiro# if MAILER_SETUID_METHOD == USE_SETUID
258838032Speter				if (new_euid != geteuid() && setuid(new_euid) < 0 && suidwarn)
258964562Sgshapiro				{
259038032Speter					syserr("openmailer: setuid(%ld) failed",
259190792Sgshapiro					       (long) new_euid);
259264562Sgshapiro					exit(EX_TEMPFAIL);
259364562Sgshapiro				}
259464562Sgshapiro# endif /* MAILER_SETUID_METHOD == USE_SETUID */
259538032Speter			}
259638032Speter			else if (new_ruid != NO_UID)
259738032Speter			{
259838032Speter				vendor_set_uid(new_ruid);
259938032Speter				if (setuid(new_ruid) < 0 && suidwarn)
260064562Sgshapiro				{
260138032Speter					syserr("openmailer: setuid(%ld) failed",
260290792Sgshapiro					       (long) new_ruid);
260364562Sgshapiro					exit(EX_TEMPFAIL);
260464562Sgshapiro				}
260538032Speter			}
260638032Speter
260738032Speter			if (tTd(11, 2))
260890792Sgshapiro				sm_dprintf("openmailer: running as r/euid=%d/%d, r/egid=%d/%d\n",
260990792Sgshapiro					   (int) getuid(), (int) geteuid(),
261090792Sgshapiro					   (int) getgid(), (int) getegid());
261138032Speter
261238032Speter			/* move into some "safe" directory */
261338032Speter			if (m->m_execdir != NULL)
261438032Speter			{
261538032Speter				char *q;
261638032Speter
261738032Speter				for (p = m->m_execdir; p != NULL; p = q)
261838032Speter				{
261938032Speter					q = strchr(p, ':');
262038032Speter					if (q != NULL)
262138032Speter						*q = '\0';
262298121Sgshapiro					expand(p, cbuf, sizeof cbuf, e);
262338032Speter					if (q != NULL)
262438032Speter						*q++ = ':';
262538032Speter					if (tTd(11, 20))
262690792Sgshapiro						sm_dprintf("openmailer: trydir %s\n",
262798121Sgshapiro							   cbuf);
262898121Sgshapiro					if (cbuf[0] != '\0' &&
262998121Sgshapiro					    chdir(cbuf) >= 0)
263038032Speter						break;
263138032Speter				}
263238032Speter			}
263338032Speter
263490792Sgshapiro			/* Check safety of program to be run */
263590792Sgshapiro			sff = SFF_ROOTOK|SFF_EXECOK;
263690792Sgshapiro			if (!bitnset(DBS_RUNWRITABLEPROGRAM,
263790792Sgshapiro				     DontBlameSendmail))
263890792Sgshapiro				sff |= SFF_NOGWFILES|SFF_NOWWFILES;
263990792Sgshapiro			if (bitnset(DBS_RUNPROGRAMINUNSAFEDIRPATH,
264090792Sgshapiro				    DontBlameSendmail))
264190792Sgshapiro				sff |= SFF_NOPATHCHECK;
264290792Sgshapiro			else
264390792Sgshapiro				sff |= SFF_SAFEDIRPATH;
264490792Sgshapiro			ret = safefile(m->m_mailer, getuid(), getgid(),
264590792Sgshapiro				       user, sff, 0, NULL);
264690792Sgshapiro			if (ret != 0)
264790792Sgshapiro				sm_syslog(LOG_INFO, e->e_id,
264890792Sgshapiro					  "Warning: program %s unsafe: %s",
264990792Sgshapiro					  m->m_mailer, sm_errstring(ret));
265090792Sgshapiro
265138032Speter			/* arrange to filter std & diag output of command */
265264562Sgshapiro			(void) close(rpvect[0]);
265364562Sgshapiro			if (dup2(rpvect[1], STDOUT_FILENO) < 0)
265438032Speter			{
265564562Sgshapiro				syserr("%s... openmailer(%s): cannot dup pipe %d for stdout",
265664562Sgshapiro				       shortenstring(e->e_to, MAXSHORTSTR),
265764562Sgshapiro				       m->m_name, rpvect[1]);
265864562Sgshapiro				_exit(EX_OSERR);
265938032Speter			}
266064562Sgshapiro			(void) close(rpvect[1]);
266164562Sgshapiro
266238032Speter			if (dup2(STDOUT_FILENO, STDERR_FILENO) < 0)
266338032Speter			{
266438032Speter				syserr("%s... openmailer(%s): cannot dup stdout for stderr",
266590792Sgshapiro				       shortenstring(e->e_to, MAXSHORTSTR),
266690792Sgshapiro				       m->m_name);
266738032Speter				_exit(EX_OSERR);
266838032Speter			}
266938032Speter
267038032Speter			/* arrange to get standard input */
267138032Speter			(void) close(mpvect[1]);
267238032Speter			if (dup2(mpvect[0], STDIN_FILENO) < 0)
267338032Speter			{
267438032Speter				syserr("%s... openmailer(%s): cannot dup pipe %d for stdin",
267590792Sgshapiro				       shortenstring(e->e_to, MAXSHORTSTR),
267690792Sgshapiro				       m->m_name, mpvect[0]);
267738032Speter				_exit(EX_OSERR);
267838032Speter			}
267938032Speter			(void) close(mpvect[0]);
268038032Speter
268138032Speter			/* arrange for all the files to be closed */
268238032Speter			for (i = 3; i < DtableSize; i++)
268338032Speter			{
268438032Speter				register int j;
268538032Speter
268638032Speter				if ((j = fcntl(i, F_GETFD, 0)) != -1)
268764562Sgshapiro					(void) fcntl(i, F_SETFD,
268864562Sgshapiro						     j | FD_CLOEXEC);
268938032Speter			}
269038032Speter
269194334Sgshapiro# if !_FFR_USE_SETLOGIN
269238032Speter			/* run disconnected from terminal */
269338032Speter			(void) setsid();
269494334Sgshapiro# endif /* !_FFR_USE_SETLOGIN */
269538032Speter
269638032Speter			/* try to execute the mailer */
269764562Sgshapiro			(void) execve(m->m_mailer, (ARGV_T) pv,
269864562Sgshapiro				      (ARGV_T) UserEnviron);
269964562Sgshapiro			save_errno = errno;
270038032Speter			syserr("Cannot exec %s", m->m_mailer);
270138032Speter			if (bitnset(M_LOCALMAILER, m->m_flags) ||
270264562Sgshapiro			    transienterror(save_errno))
270338032Speter				_exit(EX_OSERR);
270438032Speter			_exit(EX_UNAVAILABLE);
270538032Speter		}
270638032Speter
270738032Speter		/*
270838032Speter		**  Set up return value.
270938032Speter		*/
271038032Speter
271138032Speter		if (mci == NULL)
271238032Speter		{
271390792Sgshapiro			if (clever)
271490792Sgshapiro			{
271590792Sgshapiro				/*
271690792Sgshapiro				**  Allocate from general heap, not
271790792Sgshapiro				**  envelope rpool, because this mci
271890792Sgshapiro				**  is going to be cached.
271990792Sgshapiro				*/
272090792Sgshapiro
272190792Sgshapiro				mci = mci_new(NULL);
272290792Sgshapiro			}
272390792Sgshapiro			else
272490792Sgshapiro			{
272590792Sgshapiro				/*
272690792Sgshapiro				**  Prevent a storage leak by allocating
272790792Sgshapiro				**  this from the envelope rpool.
272890792Sgshapiro				*/
272990792Sgshapiro
273090792Sgshapiro				mci = mci_new(e->e_rpool);
273190792Sgshapiro			}
273238032Speter		}
273338032Speter		mci->mci_mailer = m;
273438032Speter		if (clever)
273538032Speter		{
273638032Speter			mci->mci_state = MCIS_OPENING;
273738032Speter			mci_cache(mci);
273838032Speter		}
273938032Speter		else
274038032Speter		{
274138032Speter			mci->mci_state = MCIS_OPEN;
274238032Speter		}
274338032Speter		mci->mci_pid = pid;
274438032Speter		(void) close(mpvect[0]);
274590792Sgshapiro		mci->mci_out = sm_io_open(SmFtStdiofd, SM_TIME_DEFAULT,
274690792Sgshapiro					  (void *) &(mpvect[1]), SM_IO_WRONLY,
274790792Sgshapiro					  NULL);
274838032Speter		if (mci->mci_out == NULL)
274938032Speter		{
275038032Speter			syserr("deliver: cannot create mailer output channel, fd=%d",
275190792Sgshapiro			       mpvect[1]);
275238032Speter			(void) close(mpvect[1]);
275364562Sgshapiro			(void) close(rpvect[0]);
275464562Sgshapiro			(void) close(rpvect[1]);
275538032Speter			rcode = EX_OSERR;
275638032Speter			goto give_up;
275738032Speter		}
275864562Sgshapiro
275964562Sgshapiro		(void) close(rpvect[1]);
276090792Sgshapiro		mci->mci_in = sm_io_open(SmFtStdiofd, SM_TIME_DEFAULT,
276190792Sgshapiro					 (void *) &(rpvect[0]), SM_IO_RDONLY,
276290792Sgshapiro					 NULL);
276364562Sgshapiro		if (mci->mci_in == NULL)
276438032Speter		{
276564562Sgshapiro			syserr("deliver: cannot create mailer input channel, fd=%d",
276664562Sgshapiro			       mpvect[1]);
276764562Sgshapiro			(void) close(rpvect[0]);
276890792Sgshapiro			(void) sm_io_close(mci->mci_out, SM_TIME_DEFAULT);
276964562Sgshapiro			mci->mci_out = NULL;
277064562Sgshapiro			rcode = EX_OSERR;
277164562Sgshapiro			goto give_up;
277238032Speter		}
277338032Speter	}
277438032Speter
277538032Speter	/*
277638032Speter	**  If we are in SMTP opening state, send initial protocol.
277738032Speter	*/
277838032Speter
277938032Speter	if (bitnset(M_7BITS, m->m_flags) &&
278038032Speter	    (!clever || mci->mci_state == MCIS_OPENING))
278138032Speter		mci->mci_flags |= MCIF_7BIT;
278238032Speter	if (clever && mci->mci_state != MCIS_CLOSED)
278338032Speter	{
278490792Sgshapiro# if STARTTLS || SASL
278590792Sgshapiro		int dotpos;
278690792Sgshapiro		char *srvname;
278790792Sgshapiro		extern SOCKADDR CurHostAddr;
278890792Sgshapiro# endif /* STARTTLS || SASL */
278990792Sgshapiro
279090792Sgshapiro# if SASL
279171345Sgshapiro#  define DONE_AUTH(f)		bitset(MCIF_AUTHACT, f)
279290792Sgshapiro# endif /* SASL */
279364562Sgshapiro# if STARTTLS
279471345Sgshapiro#  define DONE_STARTTLS(f)	bitset(MCIF_TLSACT, f)
279564562Sgshapiro# endif /* STARTTLS */
279671345Sgshapiro# define ONLY_HELO(f)		bitset(MCIF_ONLY_EHLO, f)
279771345Sgshapiro# define SET_HELO(f)		f |= MCIF_ONLY_EHLO
279871345Sgshapiro# define CLR_HELO(f)		f &= ~MCIF_ONLY_EHLO
279938032Speter
280090792Sgshapiro# if STARTTLS || SASL
280190792Sgshapiro		/* don't use CurHostName, it is changed in many places */
280290792Sgshapiro		if (mci->mci_host != NULL)
280390792Sgshapiro		{
280490792Sgshapiro			srvname = mci->mci_host;
280590792Sgshapiro			dotpos = strlen(srvname) - 1;
280690792Sgshapiro			if (dotpos >= 0)
280790792Sgshapiro			{
280890792Sgshapiro				if (srvname[dotpos] == '.')
280990792Sgshapiro					srvname[dotpos] = '\0';
281090792Sgshapiro				else
281190792Sgshapiro					dotpos = -1;
281290792Sgshapiro			}
281390792Sgshapiro		}
281490792Sgshapiro		else if (mci->mci_mailer != NULL)
281590792Sgshapiro		{
281690792Sgshapiro			srvname = mci->mci_mailer->m_name;
281790792Sgshapiro			dotpos = -1;
281890792Sgshapiro		}
281990792Sgshapiro		else
282090792Sgshapiro		{
282190792Sgshapiro			srvname = "local";
282290792Sgshapiro			dotpos = -1;
282390792Sgshapiro		}
282471345Sgshapiro
282590792Sgshapiro		/* don't set {server_name} to NULL or "": see getauth() */
282690792Sgshapiro		macdefine(&mci->mci_macro, A_TEMP, macid("{server_name}"),
282790792Sgshapiro			  srvname);
282864562Sgshapiro
282990792Sgshapiro		/* CurHostAddr is set by makeconnection() and mci_get() */
283090792Sgshapiro		if (CurHostAddr.sa.sa_family != 0)
283190792Sgshapiro		{
283290792Sgshapiro			macdefine(&mci->mci_macro, A_TEMP,
283390792Sgshapiro				  macid("{server_addr}"),
283490792Sgshapiro				  anynet_ntoa(&CurHostAddr));
283590792Sgshapiro		}
283690792Sgshapiro		else if (mci->mci_mailer != NULL)
283790792Sgshapiro		{
283890792Sgshapiro			/* mailer name is unique, use it as address */
283990792Sgshapiro			macdefine(&mci->mci_macro, A_PERM,
284090792Sgshapiro				  macid("{server_addr}"),
284190792Sgshapiro				  mci->mci_mailer->m_name);
284290792Sgshapiro		}
284390792Sgshapiro		else
284490792Sgshapiro		{
284590792Sgshapiro			/* don't set it to NULL or "": see getauth() */
284690792Sgshapiro			macdefine(&mci->mci_macro, A_PERM,
284790792Sgshapiro				  macid("{server_addr}"), "0");
284890792Sgshapiro		}
284990792Sgshapiro
285090792Sgshapiro		/* undo change of srvname (mci->mci_host) */
285190792Sgshapiro		if (dotpos >= 0)
285290792Sgshapiro			srvname[dotpos] = '.';
285390792Sgshapiro
285490792Sgshapiroreconnect:	/* after switching to an encrypted connection */
285590792Sgshapiro# endif /* STARTTLS || SASL */
285690792Sgshapiro
285790792Sgshapiro		/* set the current connection information */
285890792Sgshapiro		e->e_mci = mci;
285964562Sgshapiro# if SASL
286064562Sgshapiro		mci->mci_saslcap = NULL;
286164562Sgshapiro# endif /* SASL */
286271345Sgshapiro		smtpinit(m, mci, e, ONLY_HELO(mci->mci_flags));
286371345Sgshapiro		CLR_HELO(mci->mci_flags);
286464562Sgshapiro
286590792Sgshapiro		if (IS_DLVR_RETURN(e))
286690792Sgshapiro		{
286790792Sgshapiro			/*
286890792Sgshapiro			**  Check whether other side can deliver e-mail
286990792Sgshapiro			**  fast enough
287090792Sgshapiro			*/
287190792Sgshapiro
287290792Sgshapiro			if (!bitset(MCIF_DLVR_BY, mci->mci_flags))
287390792Sgshapiro			{
287490792Sgshapiro				e->e_status = "5.4.7";
287590792Sgshapiro				usrerrenh(e->e_status,
287690792Sgshapiro					  "554 Server does not support Deliver By");
287790792Sgshapiro				rcode = EX_UNAVAILABLE;
287890792Sgshapiro				goto give_up;
287990792Sgshapiro			}
288090792Sgshapiro			if (e->e_deliver_by > 0 &&
288190792Sgshapiro			    e->e_deliver_by - (curtime() - e->e_ctime) <
288290792Sgshapiro			    mci->mci_min_by)
288390792Sgshapiro			{
288490792Sgshapiro				e->e_status = "5.4.7";
288590792Sgshapiro				usrerrenh(e->e_status,
288690792Sgshapiro					  "554 Message can't be delivered in time; %ld < %ld",
288790792Sgshapiro					  e->e_deliver_by - (curtime() - e->e_ctime),
288890792Sgshapiro					  mci->mci_min_by);
288990792Sgshapiro				rcode = EX_UNAVAILABLE;
289090792Sgshapiro				goto give_up;
289190792Sgshapiro			}
289290792Sgshapiro		}
289390792Sgshapiro
289464562Sgshapiro# if STARTTLS
289564562Sgshapiro		/* first TLS then AUTH to provide a security layer */
289671345Sgshapiro		if (mci->mci_state != MCIS_CLOSED &&
289771345Sgshapiro		    !DONE_STARTTLS(mci->mci_flags))
289864562Sgshapiro		{
289964562Sgshapiro			int olderrors;
290064562Sgshapiro			bool usetls;
290164562Sgshapiro			bool saveQuickAbort = QuickAbort;
290264562Sgshapiro			bool saveSuprErrs = SuprErrs;
290371345Sgshapiro			char *host = NULL;
290464562Sgshapiro
290564562Sgshapiro			rcode = EX_OK;
290664562Sgshapiro			usetls = bitset(MCIF_TLS, mci->mci_flags);
290790792Sgshapiro			if (usetls)
290890792Sgshapiro				usetls = !iscltflgset(e, D_NOTLS);
290966494Sgshapiro
291064562Sgshapiro			if (usetls)
291164562Sgshapiro			{
291290792Sgshapiro				host = macvalue(macid("{server_name}"), e);
291364562Sgshapiro				olderrors = Errors;
291490792Sgshapiro				QuickAbort = false;
291590792Sgshapiro				SuprErrs = true;
2916102528Sgshapiro				if (rscheck("try_tls", host, NULL, e,
2917102528Sgshapiro					    RSF_RMCOMM, 7, host, NOQID) != EX_OK
291864562Sgshapiro				    || Errors > olderrors)
291990792Sgshapiro					usetls = false;
292064562Sgshapiro				SuprErrs = saveSuprErrs;
292164562Sgshapiro				QuickAbort = saveQuickAbort;
292264562Sgshapiro			}
292364562Sgshapiro
292464562Sgshapiro			if (usetls)
292564562Sgshapiro			{
292664562Sgshapiro				if ((rcode = starttls(m, mci, e)) == EX_OK)
292764562Sgshapiro				{
292864562Sgshapiro					/* start again without STARTTLS */
292964562Sgshapiro					mci->mci_flags |= MCIF_TLSACT;
293064562Sgshapiro				}
293164562Sgshapiro				else
293264562Sgshapiro				{
293364562Sgshapiro					char *s;
293464562Sgshapiro
293564562Sgshapiro					/*
293664562Sgshapiro					**  TLS negotation failed, what to do?
293764562Sgshapiro					**  fall back to unencrypted connection
293864562Sgshapiro					**  or abort? How to decide?
293964562Sgshapiro					**  set a macro and call a ruleset.
294064562Sgshapiro					*/
294190792Sgshapiro
294264562Sgshapiro					mci->mci_flags &= ~MCIF_TLS;
294364562Sgshapiro					switch (rcode)
294464562Sgshapiro					{
294564562Sgshapiro					  case EX_TEMPFAIL:
294664562Sgshapiro						s = "TEMP";
294764562Sgshapiro						break;
294864562Sgshapiro					  case EX_USAGE:
294964562Sgshapiro						s = "USAGE";
295064562Sgshapiro						break;
295164562Sgshapiro					  case EX_PROTOCOL:
295264562Sgshapiro						s = "PROTOCOL";
295364562Sgshapiro						break;
295464562Sgshapiro					  case EX_SOFTWARE:
295564562Sgshapiro						s = "SOFTWARE";
295664562Sgshapiro						break;
295764562Sgshapiro
295864562Sgshapiro					  /* everything else is a failure */
295964562Sgshapiro					  default:
296064562Sgshapiro						s = "FAILURE";
296164562Sgshapiro						rcode = EX_TEMPFAIL;
296264562Sgshapiro					}
296390792Sgshapiro					macdefine(&e->e_macro, A_PERM,
296490792Sgshapiro						  macid("{verify}"), s);
296564562Sgshapiro				}
296664562Sgshapiro			}
296764562Sgshapiro			else
296890792Sgshapiro				macdefine(&e->e_macro, A_PERM,
296990792Sgshapiro					  macid("{verify}"), "NONE");
297064562Sgshapiro			olderrors = Errors;
297190792Sgshapiro			QuickAbort = false;
297290792Sgshapiro			SuprErrs = true;
297364562Sgshapiro
297464562Sgshapiro			/*
297564562Sgshapiro			**  rcode == EX_SOFTWARE is special:
297664562Sgshapiro			**  the TLS negotation failed
297764562Sgshapiro			**  we have to drop the connection no matter what
297864562Sgshapiro			**  However, we call tls_server to give it the chance
297964562Sgshapiro			**  to log the problem and return an appropriate
298064562Sgshapiro			**  error code.
298164562Sgshapiro			*/
298290792Sgshapiro
298364562Sgshapiro			if (rscheck("tls_server",
298490792Sgshapiro				    macvalue(macid("{verify}"), e),
2985102528Sgshapiro				    NULL, e, RSF_RMCOMM|RSF_COUNT, 5,
2986102528Sgshapiro				    host, NOQID) != EX_OK ||
298764562Sgshapiro			    Errors > olderrors ||
298864562Sgshapiro			    rcode == EX_SOFTWARE)
298964562Sgshapiro			{
299064562Sgshapiro				char enhsc[ENHSCLEN];
299164562Sgshapiro				extern char MsgBuf[];
299264562Sgshapiro
299364562Sgshapiro				if (ISSMTPCODE(MsgBuf) &&
299464562Sgshapiro				    extenhsc(MsgBuf + 4, ' ', enhsc) > 0)
299564562Sgshapiro				{
299690792Sgshapiro					p = sm_rpool_strdup_x(e->e_rpool,
299790792Sgshapiro							      MsgBuf);
299864562Sgshapiro				}
299964562Sgshapiro				else
300064562Sgshapiro				{
300164562Sgshapiro					p = "403 4.7.0 server not authenticated.";
300290792Sgshapiro					(void) sm_strlcpy(enhsc, "4.7.0",
300390792Sgshapiro							  sizeof enhsc);
300464562Sgshapiro				}
300564562Sgshapiro				SuprErrs = saveSuprErrs;
300664562Sgshapiro				QuickAbort = saveQuickAbort;
300764562Sgshapiro
300864562Sgshapiro				if (rcode == EX_SOFTWARE)
300964562Sgshapiro				{
301064562Sgshapiro					/* drop the connection */
301164562Sgshapiro					mci->mci_state = MCIS_QUITING;
301264562Sgshapiro					if (mci->mci_in != NULL)
301364562Sgshapiro					{
301490792Sgshapiro						(void) sm_io_close(mci->mci_in,
301590792Sgshapiro								   SM_TIME_DEFAULT);
301664562Sgshapiro						mci->mci_in = NULL;
301764562Sgshapiro					}
301864562Sgshapiro					mci->mci_flags &= ~MCIF_TLSACT;
301964562Sgshapiro					(void) endmailer(mci, e, pv);
302064562Sgshapiro				}
302164562Sgshapiro				else
302264562Sgshapiro				{
302364562Sgshapiro					/* abort transfer */
302464562Sgshapiro					smtpquit(m, mci, e);
302564562Sgshapiro				}
302664562Sgshapiro
302771345Sgshapiro				/* avoid bogus error msg */
302871345Sgshapiro				mci->mci_errno = 0;
302971345Sgshapiro
303064562Sgshapiro				/* temp or permanent failure? */
303164562Sgshapiro				rcode = (*p == '4') ? EX_TEMPFAIL
303264562Sgshapiro						    : EX_UNAVAILABLE;
303390792Sgshapiro				mci_setstat(mci, rcode, enhsc, p);
303464562Sgshapiro
303564562Sgshapiro				/*
303664562Sgshapiro				**  hack to get the error message into
303764562Sgshapiro				**  the envelope (done in giveresponse())
303864562Sgshapiro				*/
303990792Sgshapiro
304090792Sgshapiro				(void) sm_strlcpy(SmtpError, p,
304190792Sgshapiro						  sizeof SmtpError);
304264562Sgshapiro			}
304364562Sgshapiro			QuickAbort = saveQuickAbort;
304464562Sgshapiro			SuprErrs = saveSuprErrs;
304571345Sgshapiro			if (DONE_STARTTLS(mci->mci_flags) &&
304671345Sgshapiro			    mci->mci_state != MCIS_CLOSED)
304764562Sgshapiro			{
304871345Sgshapiro				SET_HELO(mci->mci_flags);
304964562Sgshapiro				mci->mci_flags &= ~MCIF_EXTENS;
305064562Sgshapiro				goto reconnect;
305164562Sgshapiro			}
305264562Sgshapiro		}
305364562Sgshapiro# endif /* STARTTLS */
305464562Sgshapiro# if SASL
305564562Sgshapiro		/* if other server supports authentication let's authenticate */
305664562Sgshapiro		if (mci->mci_state != MCIS_CLOSED &&
305764562Sgshapiro		    mci->mci_saslcap != NULL &&
305890792Sgshapiro		    !DONE_AUTH(mci->mci_flags) && !iscltflgset(e, D_NOAUTH))
305964562Sgshapiro		{
306090792Sgshapiro			/* Should we require some minimum authentication? */
306190792Sgshapiro			if ((ret = smtpauth(m, mci, e)) == EX_OK)
306264562Sgshapiro			{
306364562Sgshapiro				int result;
306490792Sgshapiro				sasl_ssf_t *ssf = NULL;
306564562Sgshapiro
306690792Sgshapiro				/* Get security strength (features) */
306764562Sgshapiro				result = sasl_getprop(mci->mci_conn, SASL_SSF,
306898121Sgshapiro# if SASL >= 20000
306998121Sgshapiro						      (const void **) &ssf);
307098121Sgshapiro# else /* SASL >= 20000 */
307164562Sgshapiro						      (void **) &ssf);
307298121Sgshapiro# endif /* SASL >= 20000 */
307390792Sgshapiro
307490792Sgshapiro				/* XXX authid? */
307564562Sgshapiro				if (LogLevel > 9)
307664562Sgshapiro					sm_syslog(LOG_INFO, NOQID,
307790792Sgshapiro						  "AUTH=client, relay=%.100s, mech=%.16s, bits=%d",
307864562Sgshapiro						  mci->mci_host,
307990792Sgshapiro						  macvalue(macid("{auth_type}"), e),
308090792Sgshapiro						  result == SASL_OK ? *ssf : 0);
308177349Sgshapiro
308264562Sgshapiro				/*
308390792Sgshapiro				**  Only switch to encrypted connection
308464562Sgshapiro				**  if a security layer has been negotiated
308564562Sgshapiro				*/
308690792Sgshapiro
308764562Sgshapiro				if (result == SASL_OK && *ssf > 0)
308864562Sgshapiro				{
308964562Sgshapiro					/*
309090792Sgshapiro					**  Convert I/O layer to use SASL.
309190792Sgshapiro					**  If the call fails, the connection
309290792Sgshapiro					**  is aborted.
309364562Sgshapiro					*/
309490792Sgshapiro
309590792Sgshapiro					if (sfdcsasl(&mci->mci_in,
309690792Sgshapiro						     &mci->mci_out,
309764562Sgshapiro						     mci->mci_conn) == 0)
309864562Sgshapiro					{
309964562Sgshapiro						mci->mci_flags &= ~MCIF_EXTENS;
310090792Sgshapiro						mci->mci_flags |= MCIF_AUTHACT|
310190792Sgshapiro								  MCIF_ONLY_EHLO;
310264562Sgshapiro						goto reconnect;
310364562Sgshapiro					}
310490792Sgshapiro					syserr("AUTH TLS switch failed in client");
310564562Sgshapiro				}
310664562Sgshapiro				/* else? XXX */
310764562Sgshapiro				mci->mci_flags |= MCIF_AUTHACT;
310864562Sgshapiro
310964562Sgshapiro			}
311090792Sgshapiro			else if (ret == EX_TEMPFAIL)
311190792Sgshapiro			{
311290792Sgshapiro				if (LogLevel > 8)
311390792Sgshapiro					sm_syslog(LOG_ERR, NOQID,
311490792Sgshapiro						  "AUTH=client, relay=%.100s, temporary failure, connection abort",
311590792Sgshapiro						  mci->mci_host);
311690792Sgshapiro				smtpquit(m, mci, e);
311790792Sgshapiro
311890792Sgshapiro				/* avoid bogus error msg */
311990792Sgshapiro				mci->mci_errno = 0;
312090792Sgshapiro				rcode = EX_TEMPFAIL;
312190792Sgshapiro				mci_setstat(mci, rcode, "4.7.1", p);
312290792Sgshapiro
312390792Sgshapiro				/*
312490792Sgshapiro				**  hack to get the error message into
312590792Sgshapiro				**  the envelope (done in giveresponse())
312690792Sgshapiro				*/
312790792Sgshapiro
312890792Sgshapiro				(void) sm_strlcpy(SmtpError,
312990792Sgshapiro						  "Temporary AUTH failure",
313090792Sgshapiro						  sizeof SmtpError);
313190792Sgshapiro			}
313264562Sgshapiro		}
313364562Sgshapiro# endif /* SASL */
313438032Speter	}
313538032Speter
313664562Sgshapiro
313738032Speterdo_transfer:
313838032Speter	/* clear out per-message flags from connection structure */
313938032Speter	mci->mci_flags &= ~(MCIF_CVT7TO8|MCIF_CVT8TO7);
314038032Speter
314138032Speter	if (bitset(EF_HAS8BIT, e->e_flags) &&
314238032Speter	    !bitset(EF_DONT_MIME, e->e_flags) &&
314338032Speter	    bitnset(M_7BITS, m->m_flags))
314438032Speter		mci->mci_flags |= MCIF_CVT8TO7;
314538032Speter
314638032Speter#if MIME7TO8
314738032Speter	if (bitnset(M_MAKE8BIT, m->m_flags) &&
314838032Speter	    !bitset(MCIF_7BIT, mci->mci_flags) &&
314938032Speter	    (p = hvalue("Content-Transfer-Encoding", e->e_header)) != NULL &&
315090792Sgshapiro	     (sm_strcasecmp(p, "quoted-printable") == 0 ||
315190792Sgshapiro	      sm_strcasecmp(p, "base64") == 0) &&
315238032Speter	    (p = hvalue("Content-Type", e->e_header)) != NULL)
315338032Speter	{
315438032Speter		/* may want to convert 7 -> 8 */
315538032Speter		/* XXX should really parse it here -- and use a class XXX */
315690792Sgshapiro		if (sm_strncasecmp(p, "text/plain", 10) == 0 &&
315738032Speter		    (p[10] == '\0' || p[10] == ' ' || p[10] == ';'))
315838032Speter			mci->mci_flags |= MCIF_CVT7TO8;
315938032Speter	}
316064562Sgshapiro#endif /* MIME7TO8 */
316138032Speter
316238032Speter	if (tTd(11, 1))
316338032Speter	{
316490792Sgshapiro		sm_dprintf("openmailer: ");
316590792Sgshapiro		mci_dump(mci, false);
316638032Speter	}
316738032Speter
316890792Sgshapiro#if _FFR_CLIENT_SIZE
316990792Sgshapiro	/*
317090792Sgshapiro	**  See if we know the maximum size and
317190792Sgshapiro	**  abort if the message is too big.
317290792Sgshapiro	**
317390792Sgshapiro	**  NOTE: _FFR_CLIENT_SIZE is untested.
317490792Sgshapiro	*/
317590792Sgshapiro
317690792Sgshapiro	if (bitset(MCIF_SIZE, mci->mci_flags) &&
317790792Sgshapiro	    mci->mci_maxsize > 0 &&
317890792Sgshapiro	    e->e_msgsize > mci->mci_maxsize)
317990792Sgshapiro	{
318090792Sgshapiro		e->e_flags |= EF_NO_BODY_RETN;
318190792Sgshapiro		if (bitnset(M_LOCALMAILER, m->m_flags))
318290792Sgshapiro			e->e_status = "5.2.3";
318390792Sgshapiro		else
318490792Sgshapiro			e->e_status = "5.3.4";
318590792Sgshapiro
318690792Sgshapiro		usrerrenh(e->e_status,
318790792Sgshapiro			  "552 Message is too large; %ld bytes max",
318890792Sgshapiro			  mci->mci_maxsize);
318990792Sgshapiro		rcode = EX_DATAERR;
319090792Sgshapiro
319190792Sgshapiro		/* Need an e_message for error */
319290792Sgshapiro		(void) sm_snprintf(SmtpError, sizeof SmtpError,
319390792Sgshapiro				   "Message is too large; %ld bytes max",
319490792Sgshapiro				   mci->mci_maxsize);
319590792Sgshapiro		goto give_up;
319690792Sgshapiro	}
319790792Sgshapiro#endif /* _FFR_CLIENT_SIZE */
319890792Sgshapiro
319938032Speter	if (mci->mci_state != MCIS_OPEN)
320038032Speter	{
320138032Speter		/* couldn't open the mailer */
320238032Speter		rcode = mci->mci_exitstat;
320338032Speter		errno = mci->mci_errno;
320473188Sgshapiro		SM_SET_H_ERRNO(mci->mci_herrno);
320538032Speter		if (rcode == EX_OK)
320638032Speter		{
320738032Speter			/* shouldn't happen */
320864562Sgshapiro			syserr("554 5.3.5 deliver: mci=%lx rcode=%d errno=%d state=%d sig=%s",
320990792Sgshapiro			       (unsigned long) mci, rcode, errno,
321090792Sgshapiro			       mci->mci_state, firstsig);
321190792Sgshapiro			mci_dump_all(true);
321238032Speter			rcode = EX_SOFTWARE;
321338032Speter		}
321464562Sgshapiro		else if (nummxhosts > hostnum)
321538032Speter		{
321638032Speter			/* try next MX site */
321738032Speter			goto tryhost;
321838032Speter		}
321938032Speter	}
322038032Speter	else if (!clever)
322138032Speter	{
322238032Speter		/*
322338032Speter		**  Format and send message.
322438032Speter		*/
322538032Speter
322638032Speter		putfromline(mci, e);
322743730Speter		(*e->e_puthdr)(mci, e->e_header, e, M87F_OUTER);
322838032Speter		(*e->e_putbody)(mci, e, NULL);
322938032Speter
323038032Speter		/* get the exit status */
323138032Speter		rcode = endmailer(mci, e, pv);
323290792Sgshapiro		if (rcode == EX_TEMPFAIL && SmtpError[0] == '\0')
323373188Sgshapiro		{
323473188Sgshapiro			/*
323573188Sgshapiro			**  Need an e_message for mailq display.
323673188Sgshapiro			**  We set SmtpError as
323773188Sgshapiro			*/
323873188Sgshapiro
323990792Sgshapiro			(void) sm_snprintf(SmtpError, sizeof SmtpError,
324090792Sgshapiro					   "%s mailer (%s) exited with EX_TEMPFAIL",
324190792Sgshapiro					   m->m_name, m->m_mailer);
324273188Sgshapiro		}
324338032Speter	}
324438032Speter	else
324538032Speter	{
324638032Speter		/*
324738032Speter		**  Send the MAIL FROM: protocol
324838032Speter		*/
324938032Speter
325090792Sgshapiro		/* XXX this isn't pipelined... */
325138032Speter		rcode = smtpmailfrom(m, mci, e);
325238032Speter		if (rcode == EX_OK)
325338032Speter		{
325438032Speter			register int i;
325590792Sgshapiro# if PIPELINING
325690792Sgshapiro			ADDRESS *volatile pchain;
325790792Sgshapiro# endif /* PIPELINING */
325838032Speter
325938032Speter			/* send the recipient list */
326038032Speter			tobuf[0] = '\0';
326190792Sgshapiro			mci->mci_retryrcpt = false;
326290792Sgshapiro			mci->mci_tolist = tobuf;
326390792Sgshapiro# if PIPELINING
326490792Sgshapiro			pchain = NULL;
326590792Sgshapiro			mci->mci_nextaddr = NULL;
326690792Sgshapiro# endif /* PIPELINING */
326764562Sgshapiro
326838032Speter			for (to = tochain; to != NULL; to = to->q_tchain)
326938032Speter			{
327090792Sgshapiro				if (!QS_IS_UNMARKED(to->q_state))
327138032Speter					continue;
327264562Sgshapiro
327390792Sgshapiro				/* mark recipient state as "ok so far" */
327490792Sgshapiro				to->q_state = QS_OK;
327590792Sgshapiro				e->e_to = to->q_paddr;
327664562Sgshapiro# if STARTTLS
327764562Sgshapiro				i = rscheck("tls_rcpt", to->q_user, NULL, e,
3278102528Sgshapiro					    RSF_RMCOMM|RSF_COUNT, 3,
3279102528Sgshapiro					    mci->mci_host, e->e_id);
328064562Sgshapiro				if (i != EX_OK)
328138032Speter				{
328290792Sgshapiro					markfailure(e, to, mci, i, false);
328390792Sgshapiro					giveresponse(i, to->q_status,  m, mci,
328490792Sgshapiro						     ctladdr, xstart, e, to);
328590792Sgshapiro					if (i == EX_TEMPFAIL)
328690792Sgshapiro					{
328790792Sgshapiro						mci->mci_retryrcpt = true;
328890792Sgshapiro						to->q_state = QS_RETRY;
328990792Sgshapiro					}
329064562Sgshapiro					continue;
329138032Speter				}
329264562Sgshapiro# endif /* STARTTLS */
329364562Sgshapiro
329490792Sgshapiro				i = smtprcpt(to, m, mci, e, ctladdr, xstart);
329590792Sgshapiro# if PIPELINING
329690792Sgshapiro				if (i == EX_OK &&
329790792Sgshapiro				    bitset(MCIF_PIPELINED, mci->mci_flags))
329864562Sgshapiro				{
329990792Sgshapiro					/*
330090792Sgshapiro					**  Add new element to list of
330190792Sgshapiro					**  recipients for pipelining.
330290792Sgshapiro					*/
330390792Sgshapiro
330490792Sgshapiro					to->q_pchain = NULL;
330590792Sgshapiro					if (mci->mci_nextaddr == NULL)
330690792Sgshapiro						mci->mci_nextaddr = to;
330790792Sgshapiro					if (pchain == NULL)
330890792Sgshapiro						pchain = to;
330990792Sgshapiro					else
331090792Sgshapiro					{
331190792Sgshapiro						pchain->q_pchain = to;
331290792Sgshapiro						pchain = pchain->q_pchain;
331390792Sgshapiro					}
331464562Sgshapiro				}
331590792Sgshapiro# endif /* PIPELINING */
331690792Sgshapiro				if (i != EX_OK)
331738032Speter				{
331890792Sgshapiro					markfailure(e, to, mci, i, false);
331998841Sgshapiro					giveresponse(i, to->q_status, m, mci,
332090792Sgshapiro						     ctladdr, xstart, e, to);
332190792Sgshapiro					if (i == EX_TEMPFAIL)
332290792Sgshapiro						to->q_state = QS_RETRY;
332338032Speter				}
332438032Speter			}
332538032Speter
332690792Sgshapiro			/* No recipients in list and no missing responses? */
332790792Sgshapiro			if (tobuf[0] == '\0'
332890792Sgshapiro# if PIPELINING
332990792Sgshapiro			    && mci->mci_nextaddr == NULL
333090792Sgshapiro# endif /* PIPELINING */
333190792Sgshapiro			   )
333238032Speter			{
333338032Speter				rcode = EX_OK;
333438032Speter				e->e_to = NULL;
333538032Speter				if (bitset(MCIF_CACHED, mci->mci_flags))
333638032Speter					smtprset(m, mci, e);
333738032Speter			}
333838032Speter			else
333938032Speter			{
334038032Speter				e->e_to = tobuf + 1;
334190792Sgshapiro				rcode = smtpdata(m, mci, e, ctladdr, xstart);
334238032Speter			}
334338032Speter		}
334464562Sgshapiro		if (rcode == EX_TEMPFAIL && nummxhosts > hostnum)
334538032Speter		{
334638032Speter			/* try next MX site */
334738032Speter			goto tryhost;
334838032Speter		}
334938032Speter	}
335038032Speter#if NAMED_BIND
335138032Speter	if (ConfigLevel < 2)
335238032Speter		_res.options |= RES_DEFNAMES | RES_DNSRCH;	/* XXX */
335364562Sgshapiro#endif /* NAMED_BIND */
335438032Speter
335538032Speter	if (tTd(62, 1))
335638032Speter		checkfds("after delivery");
335738032Speter
335838032Speter	/*
335938032Speter	**  Do final status disposal.
336038032Speter	**	We check for something in tobuf for the SMTP case.
336138032Speter	**	If we got a temporary failure, arrange to queue the
336238032Speter	**		addressees.
336338032Speter	*/
336438032Speter
336538032Speter  give_up:
336638032Speter	if (bitnset(M_LMTP, m->m_flags))
336738032Speter	{
336838032Speter		lmtp_rcode = rcode;
336938032Speter		tobuf[0] = '\0';
337090792Sgshapiro		anyok = false;
337190792Sgshapiro		strsize = 0;
337238032Speter	}
337338032Speter	else
337438032Speter		anyok = rcode == EX_OK;
337538032Speter
337638032Speter	for (to = tochain; to != NULL; to = to->q_tchain)
337738032Speter	{
337838032Speter		/* see if address already marked */
337964562Sgshapiro		if (!QS_IS_OK(to->q_state))
338038032Speter			continue;
338138032Speter
338238032Speter		/* if running LMTP, get the status for each address */
338338032Speter		if (bitnset(M_LMTP, m->m_flags))
338438032Speter		{
338538032Speter			if (lmtp_rcode == EX_OK)
338638032Speter				rcode = smtpgetstat(m, mci, e);
338738032Speter			if (rcode == EX_OK)
338838032Speter			{
338990792Sgshapiro				strsize += sm_strlcat2(tobuf + strsize, ",",
339090792Sgshapiro						to->q_paddr,
339190792Sgshapiro						tobufsize - strsize);
339290792Sgshapiro				SM_ASSERT(strsize < tobufsize);
339390792Sgshapiro				anyok = true;
339438032Speter			}
339538032Speter			else
339638032Speter			{
339738032Speter				e->e_to = to->q_paddr;
339890792Sgshapiro				markfailure(e, to, mci, rcode, true);
339964562Sgshapiro				giveresponse(rcode, to->q_status, m, mci,
340090792Sgshapiro					     ctladdr, xstart, e, to);
340138032Speter				e->e_to = tobuf + 1;
340238032Speter				continue;
340338032Speter			}
340438032Speter		}
340538032Speter		else
340638032Speter		{
340738032Speter			/* mark bad addresses */
340838032Speter			if (rcode != EX_OK)
340938032Speter			{
341038032Speter				if (goodmxfound && rcode == EX_NOHOST)
341138032Speter					rcode = EX_TEMPFAIL;
341290792Sgshapiro				markfailure(e, to, mci, rcode, true);
341338032Speter				continue;
341438032Speter			}
341538032Speter		}
341638032Speter
341738032Speter		/* successful delivery */
341864562Sgshapiro		to->q_state = QS_SENT;
341938032Speter		to->q_statdate = curtime();
342038032Speter		e->e_nsent++;
342164562Sgshapiro
342264562Sgshapiro		/*
342364562Sgshapiro		**  Checkpoint the send list every few addresses
342464562Sgshapiro		*/
342564562Sgshapiro
342666494Sgshapiro		if (CheckpointInterval > 0 && e->e_nsent >= CheckpointInterval)
342764562Sgshapiro		{
342890792Sgshapiro			queueup(e, false, false);
342964562Sgshapiro			e->e_nsent = 0;
343064562Sgshapiro		}
343164562Sgshapiro
343238032Speter		if (bitnset(M_LOCALMAILER, m->m_flags) &&
343338032Speter		    bitset(QPINGONSUCCESS, to->q_flags))
343438032Speter		{
343538032Speter			to->q_flags |= QDELIVERED;
343638032Speter			to->q_status = "2.1.5";
343790792Sgshapiro			(void) sm_io_fprintf(e->e_xfp, SM_TIME_DEFAULT,
343890792Sgshapiro					     "%s... Successfully delivered\n",
343990792Sgshapiro					     to->q_paddr);
344038032Speter		}
344138032Speter		else if (bitset(QPINGONSUCCESS, to->q_flags) &&
344238032Speter			 bitset(QPRIMARY, to->q_flags) &&
344338032Speter			 !bitset(MCIF_DSN, mci->mci_flags))
344438032Speter		{
344538032Speter			to->q_flags |= QRELAYED;
344690792Sgshapiro			(void) sm_io_fprintf(e->e_xfp, SM_TIME_DEFAULT,
344790792Sgshapiro					     "%s... relayed; expect no further notifications\n",
344890792Sgshapiro					     to->q_paddr);
344938032Speter		}
345090792Sgshapiro		else if (IS_DLVR_NOTIFY(e) &&
345190792Sgshapiro			 !bitset(MCIF_DLVR_BY, mci->mci_flags) &&
345290792Sgshapiro			 bitset(QPRIMARY, to->q_flags) &&
345390792Sgshapiro			 (!bitset(QHASNOTIFY, to->q_flags) ||
345490792Sgshapiro			  bitset(QPINGONSUCCESS, to->q_flags) ||
345590792Sgshapiro			  bitset(QPINGONFAILURE, to->q_flags) ||
345690792Sgshapiro			  bitset(QPINGONDELAY, to->q_flags)))
345790792Sgshapiro		{
345890792Sgshapiro			/* RFC 2852, 4.1.4.2: no NOTIFY, or not NEVER */
345990792Sgshapiro			to->q_flags |= QBYNRELAY;
346090792Sgshapiro			(void) sm_io_fprintf(e->e_xfp, SM_TIME_DEFAULT,
346190792Sgshapiro					     "%s... Deliver-by notify: relayed\n",
346290792Sgshapiro					     to->q_paddr);
346390792Sgshapiro		}
346490792Sgshapiro		else if (IS_DLVR_TRACE(e) &&
346590792Sgshapiro			 (!bitset(QHASNOTIFY, to->q_flags) ||
346690792Sgshapiro			  bitset(QPINGONSUCCESS, to->q_flags) ||
346790792Sgshapiro			  bitset(QPINGONFAILURE, to->q_flags) ||
346890792Sgshapiro			  bitset(QPINGONDELAY, to->q_flags)) &&
346990792Sgshapiro			 bitset(QPRIMARY, to->q_flags))
347090792Sgshapiro		{
347190792Sgshapiro			/* RFC 2852, 4.1.4: no NOTIFY, or not NEVER */
347290792Sgshapiro			to->q_flags |= QBYTRACE;
347390792Sgshapiro			(void) sm_io_fprintf(e->e_xfp, SM_TIME_DEFAULT,
347490792Sgshapiro					     "%s... Deliver-By trace: relayed\n",
347590792Sgshapiro					     to->q_paddr);
347690792Sgshapiro		}
347738032Speter	}
347838032Speter
347938032Speter	if (bitnset(M_LMTP, m->m_flags))
348038032Speter	{
348138032Speter		/*
348238032Speter		**  Global information applies to the last recipient only;
348338032Speter		**  clear it out to avoid bogus errors.
348438032Speter		*/
348538032Speter
348638032Speter		rcode = EX_OK;
348738032Speter		e->e_statmsg = NULL;
348838032Speter
348938032Speter		/* reset the mci state for the next transaction */
349090792Sgshapiro		if (mci != NULL &&
349190792Sgshapiro		    (mci->mci_state == MCIS_MAIL ||
349290792Sgshapiro		     mci->mci_state == MCIS_RCPT ||
349390792Sgshapiro		     mci->mci_state == MCIS_DATA))
3494125820Sgshapiro		{
349538032Speter			mci->mci_state = MCIS_OPEN;
3496125820Sgshapiro			SmtpPhase = mci->mci_phase = "idle";
3497125820Sgshapiro			sm_setproctitle(true, e, "%s: %s", CurHostName,
3498125820Sgshapiro					mci->mci_phase);
3499125820Sgshapiro		}
350038032Speter	}
350138032Speter
350238032Speter	if (tobuf[0] != '\0')
350390792Sgshapiro	{
350490792Sgshapiro		giveresponse(rcode, NULL, m, mci, ctladdr, xstart, e, tochain);
350590792Sgshapiro#if 0
350690792Sgshapiro		/*
350790792Sgshapiro		**  This code is disabled for now because I am not
350890792Sgshapiro		**  sure that copying status from the first recipient
350990792Sgshapiro		**  to all non-status'ed recipients is a good idea.
351090792Sgshapiro		*/
351190792Sgshapiro
351290792Sgshapiro		if (tochain->q_message != NULL &&
351390792Sgshapiro		    !bitnset(M_LMTP, m->m_flags) && rcode != EX_OK)
351490792Sgshapiro		{
351590792Sgshapiro			for (to = tochain->q_tchain; to != NULL;
351690792Sgshapiro			     to = to->q_tchain)
351790792Sgshapiro			{
351890792Sgshapiro				/* see if address already marked */
351990792Sgshapiro				if (QS_IS_QUEUEUP(to->q_state) &&
352090792Sgshapiro				    to->q_message == NULL)
352190792Sgshapiro					to->q_message = sm_rpool_strdup_x(e->e_rpool,
352290792Sgshapiro							tochain->q_message);
352390792Sgshapiro			}
352490792Sgshapiro		}
352590792Sgshapiro#endif /* 0 */
352690792Sgshapiro	}
352738032Speter	if (anyok)
352890792Sgshapiro		markstats(e, tochain, STATS_NORMAL);
352938032Speter	mci_store_persistent(mci);
353038032Speter
353190792Sgshapiro	/* Some recipients were tempfailed, try them on the next host */
353290792Sgshapiro	if (mci != NULL && mci->mci_retryrcpt && nummxhosts > hostnum)
353390792Sgshapiro	{
353490792Sgshapiro		/* try next MX site */
353590792Sgshapiro		goto tryhost;
353690792Sgshapiro	}
353790792Sgshapiro
353838032Speter	/* now close the connection */
353938032Speter	if (clever && mci != NULL && mci->mci_state != MCIS_CLOSED &&
354038032Speter	    !bitset(MCIF_CACHED, mci->mci_flags))
354138032Speter		smtpquit(m, mci, e);
354238032Speter
354390792Sgshapirocleanup: ;
354490792Sgshapiro	}
354590792Sgshapiro	SM_FINALLY
354690792Sgshapiro	{
354790792Sgshapiro		/*
354890792Sgshapiro		**  Restore state and return.
354990792Sgshapiro		*/
355038032Speter#if XDEBUG
355138032Speter		char wbuf[MAXLINE];
355238032Speter
355338032Speter		/* make absolutely certain 0, 1, and 2 are in use */
355490792Sgshapiro		(void) sm_snprintf(wbuf, sizeof wbuf,
355590792Sgshapiro				   "%s... end of deliver(%s)",
355690792Sgshapiro				   e->e_to == NULL ? "NO-TO-LIST"
355790792Sgshapiro						   : shortenstring(e->e_to,
355890792Sgshapiro								   MAXSHORTSTR),
355990792Sgshapiro				  m->m_name);
356038032Speter		checkfd012(wbuf);
356164562Sgshapiro#endif /* XDEBUG */
356238032Speter
356390792Sgshapiro		errno = 0;
356490792Sgshapiro
356590792Sgshapiro		/*
356690792Sgshapiro		**  It was originally necessary to set macro 'g' to NULL
356790792Sgshapiro		**  because it previously pointed to an auto buffer.
356890792Sgshapiro		**  We don't do this any more, so this may be unnecessary.
356990792Sgshapiro		*/
357090792Sgshapiro
357190792Sgshapiro		macdefine(&e->e_macro, A_PERM, 'g', (char *) NULL);
357290792Sgshapiro		e->e_to = NULL;
357390792Sgshapiro	}
357490792Sgshapiro	SM_END_TRY
357564562Sgshapiro	return rcode;
357638032Speter}
357764562Sgshapiro
357890792Sgshapiro/*
357938032Speter**  MARKFAILURE -- mark a failure on a specific address.
358038032Speter**
358138032Speter**	Parameters:
358238032Speter**		e -- the envelope we are sending.
358338032Speter**		q -- the address to mark.
358438032Speter**		mci -- mailer connection information.
358538032Speter**		rcode -- the code signifying the particular failure.
358664562Sgshapiro**		ovr -- override an existing code?
358738032Speter**
358838032Speter**	Returns:
358938032Speter**		none.
359038032Speter**
359138032Speter**	Side Effects:
359238032Speter**		marks the address (and possibly the envelope) with the
359338032Speter**			failure so that an error will be returned or
359438032Speter**			the message will be queued, as appropriate.
359538032Speter*/
359638032Speter
359790792Sgshapirovoid
359864562Sgshapiromarkfailure(e, q, mci, rcode, ovr)
359938032Speter	register ENVELOPE *e;
360038032Speter	register ADDRESS *q;
360138032Speter	register MCI *mci;
360238032Speter	int rcode;
360364562Sgshapiro	bool ovr;
360438032Speter{
360590792Sgshapiro	int save_errno = errno;
360664562Sgshapiro	char *status = NULL;
360764562Sgshapiro	char *rstatus = NULL;
360838032Speter
360938032Speter	switch (rcode)
361038032Speter	{
361138032Speter	  case EX_OK:
361238032Speter		break;
361338032Speter
361438032Speter	  case EX_TEMPFAIL:
361538032Speter	  case EX_IOERR:
361638032Speter	  case EX_OSERR:
361764562Sgshapiro		q->q_state = QS_QUEUEUP;
361838032Speter		break;
361938032Speter
362038032Speter	  default:
362164562Sgshapiro		q->q_state = QS_BADADDR;
362238032Speter		break;
362338032Speter	}
362438032Speter
362538032Speter	/* find most specific error code possible */
362638032Speter	if (mci != NULL && mci->mci_status != NULL)
362738032Speter	{
362890792Sgshapiro		status = sm_rpool_strdup_x(e->e_rpool, mci->mci_status);
362938032Speter		if (mci->mci_rstatus != NULL)
363090792Sgshapiro			rstatus = sm_rpool_strdup_x(e->e_rpool,
363190792Sgshapiro						    mci->mci_rstatus);
363238032Speter		else
363364562Sgshapiro			rstatus = NULL;
363438032Speter	}
363538032Speter	else if (e->e_status != NULL)
363638032Speter	{
363764562Sgshapiro		status = e->e_status;
363864562Sgshapiro		rstatus = NULL;
363938032Speter	}
364038032Speter	else
364138032Speter	{
364238032Speter		switch (rcode)
364338032Speter		{
364438032Speter		  case EX_USAGE:
364564562Sgshapiro			status = "5.5.4";
364638032Speter			break;
364738032Speter
364838032Speter		  case EX_DATAERR:
364964562Sgshapiro			status = "5.5.2";
365038032Speter			break;
365138032Speter
365238032Speter		  case EX_NOUSER:
365364562Sgshapiro			status = "5.1.1";
365438032Speter			break;
365538032Speter
365638032Speter		  case EX_NOHOST:
365764562Sgshapiro			status = "5.1.2";
365838032Speter			break;
365938032Speter
366038032Speter		  case EX_NOINPUT:
366138032Speter		  case EX_CANTCREAT:
366238032Speter		  case EX_NOPERM:
366364562Sgshapiro			status = "5.3.0";
366438032Speter			break;
366538032Speter
366638032Speter		  case EX_UNAVAILABLE:
366738032Speter		  case EX_SOFTWARE:
366838032Speter		  case EX_OSFILE:
366938032Speter		  case EX_PROTOCOL:
367038032Speter		  case EX_CONFIG:
367164562Sgshapiro			status = "5.5.0";
367238032Speter			break;
367338032Speter
367438032Speter		  case EX_OSERR:
367538032Speter		  case EX_IOERR:
367664562Sgshapiro			status = "4.5.0";
367738032Speter			break;
367838032Speter
367938032Speter		  case EX_TEMPFAIL:
368064562Sgshapiro			status = "4.2.0";
368138032Speter			break;
368238032Speter		}
368338032Speter	}
368438032Speter
368564562Sgshapiro	/* new status? */
368664562Sgshapiro	if (status != NULL && *status != '\0' && (ovr || q->q_status == NULL ||
368764562Sgshapiro	    *q->q_status == '\0' || *q->q_status < *status))
368864562Sgshapiro	{
368964562Sgshapiro		q->q_status = status;
369064562Sgshapiro		q->q_rstatus = rstatus;
369164562Sgshapiro	}
369238032Speter	if (rcode != EX_OK && q->q_rstatus == NULL &&
369338032Speter	    q->q_mailer != NULL && q->q_mailer->m_diagtype != NULL &&
369490792Sgshapiro	    sm_strcasecmp(q->q_mailer->m_diagtype, "X-UNIX") == 0)
369538032Speter	{
369664562Sgshapiro		char buf[16];
369738032Speter
369890792Sgshapiro		(void) sm_snprintf(buf, sizeof buf, "%d", rcode);
369990792Sgshapiro		q->q_rstatus = sm_rpool_strdup_x(e->e_rpool, buf);
370038032Speter	}
370164562Sgshapiro
370264562Sgshapiro	q->q_statdate = curtime();
370364562Sgshapiro	if (CurHostName != NULL && CurHostName[0] != '\0' &&
370464562Sgshapiro	    mci != NULL && !bitset(M_LOCALMAILER, mci->mci_flags))
370590792Sgshapiro		q->q_statmta = sm_rpool_strdup_x(e->e_rpool, CurHostName);
370690792Sgshapiro
370790792Sgshapiro	/* restore errno */
370890792Sgshapiro	errno = save_errno;
370938032Speter}
371090792Sgshapiro/*
371138032Speter**  ENDMAILER -- Wait for mailer to terminate.
371238032Speter**
371338032Speter**	We should never get fatal errors (e.g., segmentation
371438032Speter**	violation), so we report those specially.  For other
371538032Speter**	errors, we choose a status message (into statmsg),
371638032Speter**	and if it represents an error, we print it.
371738032Speter**
371838032Speter**	Parameters:
371980785Sgshapiro**		mci -- the mailer connection info.
372038032Speter**		e -- the current envelope.
372138032Speter**		pv -- the parameter vector that invoked the mailer
372238032Speter**			(for error messages).
372338032Speter**
372438032Speter**	Returns:
372538032Speter**		exit code of mailer.
372638032Speter**
372738032Speter**	Side Effects:
372838032Speter**		none.
372938032Speter*/
373038032Speter
373164562Sgshapirostatic jmp_buf	EndWaitTimeout;
373264562Sgshapiro
373364562Sgshapirostatic void
373464562Sgshapiroendwaittimeout()
373564562Sgshapiro{
373677349Sgshapiro	/*
373777349Sgshapiro	**  NOTE: THIS CAN BE CALLED FROM A SIGNAL HANDLER.  DO NOT ADD
373877349Sgshapiro	**	ANYTHING TO THIS ROUTINE UNLESS YOU KNOW WHAT YOU ARE
373977349Sgshapiro	**	DOING.
374077349Sgshapiro	*/
374177349Sgshapiro
374264562Sgshapiro	errno = ETIMEDOUT;
374364562Sgshapiro	longjmp(EndWaitTimeout, 1);
374464562Sgshapiro}
374564562Sgshapiro
374638032Speterint
374738032Speterendmailer(mci, e, pv)
374838032Speter	register MCI *mci;
374938032Speter	register ENVELOPE *e;
375038032Speter	char **pv;
375138032Speter{
375238032Speter	int st;
375364562Sgshapiro	int save_errno = errno;
375464562Sgshapiro	char buf[MAXLINE];
375590792Sgshapiro	SM_EVENT *ev = NULL;
375638032Speter
375764562Sgshapiro
375838032Speter	mci_unlock_host(mci);
375938032Speter
376077349Sgshapiro	/* close output to mailer */
376177349Sgshapiro	if (mci->mci_out != NULL)
376290792Sgshapiro		(void) sm_io_close(mci->mci_out, SM_TIME_DEFAULT);
376377349Sgshapiro
376477349Sgshapiro	/* copy any remaining input to transcript */
376577349Sgshapiro	if (mci->mci_in != NULL && mci->mci_state != MCIS_ERROR &&
376677349Sgshapiro	    e->e_xfp != NULL)
376777349Sgshapiro	{
376877349Sgshapiro		while (sfgets(buf, sizeof buf, mci->mci_in,
376990792Sgshapiro			      TimeOuts.to_quit, "Draining Input") != NULL)
377090792Sgshapiro			(void) sm_io_fputs(e->e_xfp, SM_TIME_DEFAULT, buf);
377177349Sgshapiro	}
377277349Sgshapiro
377364562Sgshapiro#if SASL
377490792Sgshapiro	/* close SASL connection */
377564562Sgshapiro	if (bitset(MCIF_AUTHACT, mci->mci_flags))
377664562Sgshapiro	{
377764562Sgshapiro		sasl_dispose(&mci->mci_conn);
377864562Sgshapiro		mci->mci_flags &= ~MCIF_AUTHACT;
377964562Sgshapiro	}
378064562Sgshapiro#endif /* SASL */
378164562Sgshapiro
378264562Sgshapiro#if STARTTLS
378364562Sgshapiro	/* shutdown TLS */
378464562Sgshapiro	(void) endtlsclt(mci);
378564562Sgshapiro#endif /* STARTTLS */
378664562Sgshapiro
378764562Sgshapiro	/* now close the input */
378838032Speter	if (mci->mci_in != NULL)
378990792Sgshapiro		(void) sm_io_close(mci->mci_in, SM_TIME_DEFAULT);
379038032Speter	mci->mci_in = mci->mci_out = NULL;
379138032Speter	mci->mci_state = MCIS_CLOSED;
379238032Speter
379364562Sgshapiro	errno = save_errno;
379464562Sgshapiro
379538032Speter	/* in the IPC case there is nothing to wait for */
379638032Speter	if (mci->mci_pid == 0)
379764562Sgshapiro		return EX_OK;
379838032Speter
379964562Sgshapiro	/* put a timeout around the wait */
380064562Sgshapiro	if (mci->mci_mailer->m_wait > 0)
380164562Sgshapiro	{
380264562Sgshapiro		if (setjmp(EndWaitTimeout) == 0)
380390792Sgshapiro			ev = sm_setevent(mci->mci_mailer->m_wait,
380490792Sgshapiro					 endwaittimeout, 0);
380564562Sgshapiro		else
380664562Sgshapiro		{
380766494Sgshapiro			syserr("endmailer %s: wait timeout (%ld)",
380864562Sgshapiro			       mci->mci_mailer->m_name,
380966494Sgshapiro			       (long) mci->mci_mailer->m_wait);
381064562Sgshapiro			return EX_TEMPFAIL;
381164562Sgshapiro		}
381264562Sgshapiro	}
381338032Speter
381464562Sgshapiro	/* wait for the mailer process, collect status */
381538032Speter	st = waitfor(mci->mci_pid);
381664562Sgshapiro	save_errno = errno;
381764562Sgshapiro	if (ev != NULL)
381890792Sgshapiro		sm_clrevent(ev);
381964562Sgshapiro	errno = save_errno;
382064562Sgshapiro
382138032Speter	if (st == -1)
382238032Speter	{
382338032Speter		syserr("endmailer %s: wait", mci->mci_mailer->m_name);
382464562Sgshapiro		return EX_SOFTWARE;
382538032Speter	}
382638032Speter
382738032Speter	if (WIFEXITED(st))
382838032Speter	{
382938032Speter		/* normal death -- return status */
383038032Speter		return (WEXITSTATUS(st));
383138032Speter	}
383238032Speter
383338032Speter	/* it died a horrid death */
383464562Sgshapiro	syserr("451 4.3.0 mailer %s died with signal %d%s",
383564562Sgshapiro		mci->mci_mailer->m_name, WTERMSIG(st),
383664562Sgshapiro		WCOREDUMP(st) ? " (core dumped)" :
383764562Sgshapiro		(WIFSTOPPED(st) ? " (stopped)" : ""));
383838032Speter
383938032Speter	/* log the arguments */
384038032Speter	if (pv != NULL && e->e_xfp != NULL)
384138032Speter	{
384238032Speter		register char **av;
384338032Speter
384490792Sgshapiro		(void) sm_io_fprintf(e->e_xfp, SM_TIME_DEFAULT, "Arguments:");
384538032Speter		for (av = pv; *av != NULL; av++)
384690792Sgshapiro			(void) sm_io_fprintf(e->e_xfp, SM_TIME_DEFAULT, " %s",
384790792Sgshapiro					     *av);
384890792Sgshapiro		(void) sm_io_fprintf(e->e_xfp, SM_TIME_DEFAULT, "\n");
384938032Speter	}
385038032Speter
385138032Speter	ExitStat = EX_TEMPFAIL;
385264562Sgshapiro	return EX_TEMPFAIL;
385338032Speter}
385490792Sgshapiro/*
385538032Speter**  GIVERESPONSE -- Interpret an error response from a mailer
385638032Speter**
385738032Speter**	Parameters:
385864562Sgshapiro**		status -- the status code from the mailer (high byte
385938032Speter**			only; core dumps must have been taken care of
386038032Speter**			already).
386164562Sgshapiro**		dsn -- the DSN associated with the address, if any.
386238032Speter**		m -- the mailer info for this mailer.
386338032Speter**		mci -- the mailer connection info -- can be NULL if the
386438032Speter**			response is given before the connection is made.
386538032Speter**		ctladdr -- the controlling address for the recipient
386638032Speter**			address(es).
386738032Speter**		xstart -- the transaction start time, for computing
386838032Speter**			transaction delays.
386938032Speter**		e -- the current envelope.
387090792Sgshapiro**		to -- the current recipient (NULL if none).
387138032Speter**
387238032Speter**	Returns:
387338032Speter**		none.
387438032Speter**
387538032Speter**	Side Effects:
387638032Speter**		Errors may be incremented.
387738032Speter**		ExitStat may be set.
387838032Speter*/
387938032Speter
388038032Spetervoid
388190792Sgshapirogiveresponse(status, dsn, m, mci, ctladdr, xstart, e, to)
388264562Sgshapiro	int status;
388364562Sgshapiro	char *dsn;
388438032Speter	register MAILER *m;
388538032Speter	register MCI *mci;
388638032Speter	ADDRESS *ctladdr;
388738032Speter	time_t xstart;
388838032Speter	ENVELOPE *e;
388990792Sgshapiro	ADDRESS *to;
389038032Speter{
389138032Speter	register const char *statmsg;
389264562Sgshapiro	int errnum = errno;
389364562Sgshapiro	int off = 4;
389490792Sgshapiro	bool usestat = false;
389564562Sgshapiro	char dsnbuf[ENHSCLEN];
389638032Speter	char buf[MAXLINE];
389790792Sgshapiro	char *exmsg;
389838032Speter
389938032Speter	if (e == NULL)
390038032Speter		syserr("giveresponse: null envelope");
390138032Speter
390238032Speter	/*
390338032Speter	**  Compute status message from code.
390438032Speter	*/
390538032Speter
390690792Sgshapiro	exmsg = sm_sysexmsg(status);
390764562Sgshapiro	if (status == 0)
390838032Speter	{
390964562Sgshapiro		statmsg = "250 2.0.0 Sent";
391038032Speter		if (e->e_statmsg != NULL)
391138032Speter		{
391290792Sgshapiro			(void) sm_snprintf(buf, sizeof buf, "%s (%s)",
391390792Sgshapiro					   statmsg,
391490792Sgshapiro					   shortenstring(e->e_statmsg, 403));
391538032Speter			statmsg = buf;
391638032Speter		}
391738032Speter	}
391890792Sgshapiro	else if (exmsg == NULL)
391938032Speter	{
392090792Sgshapiro		(void) sm_snprintf(buf, sizeof buf,
392190792Sgshapiro				   "554 5.3.0 unknown mailer error %d",
392290792Sgshapiro				   status);
392364562Sgshapiro		status = EX_UNAVAILABLE;
392438032Speter		statmsg = buf;
392590792Sgshapiro		usestat = true;
392638032Speter	}
392764562Sgshapiro	else if (status == EX_TEMPFAIL)
392838032Speter	{
392938032Speter		char *bp = buf;
393038032Speter
393190792Sgshapiro		(void) sm_strlcpy(bp, exmsg + 1, SPACELEFT(buf, bp));
393238032Speter		bp += strlen(bp);
393338032Speter#if NAMED_BIND
393438032Speter		if (h_errno == TRY_AGAIN)
393590792Sgshapiro			statmsg = sm_errstring(h_errno + E_DNSBASE);
393638032Speter		else
393764562Sgshapiro#endif /* NAMED_BIND */
393838032Speter		{
393964562Sgshapiro			if (errnum != 0)
394090792Sgshapiro				statmsg = sm_errstring(errnum);
394138032Speter			else
394238032Speter				statmsg = SmtpError;
394338032Speter		}
394438032Speter		if (statmsg != NULL && statmsg[0] != '\0')
394564562Sgshapiro		{
394664562Sgshapiro			switch (errnum)
394764562Sgshapiro			{
394864562Sgshapiro#ifdef ENETDOWN
394964562Sgshapiro			  case ENETDOWN:	/* Network is down */
395064562Sgshapiro#endif /* ENETDOWN */
395164562Sgshapiro#ifdef ENETUNREACH
395264562Sgshapiro			  case ENETUNREACH:	/* Network is unreachable */
395364562Sgshapiro#endif /* ENETUNREACH */
395464562Sgshapiro#ifdef ENETRESET
395564562Sgshapiro			  case ENETRESET:	/* Network dropped connection on reset */
395664562Sgshapiro#endif /* ENETRESET */
395764562Sgshapiro#ifdef ECONNABORTED
395864562Sgshapiro			  case ECONNABORTED:	/* Software caused connection abort */
395964562Sgshapiro#endif /* ECONNABORTED */
396064562Sgshapiro#ifdef EHOSTDOWN
396164562Sgshapiro			  case EHOSTDOWN:	/* Host is down */
396264562Sgshapiro#endif /* EHOSTDOWN */
396364562Sgshapiro#ifdef EHOSTUNREACH
396464562Sgshapiro			  case EHOSTUNREACH:	/* No route to host */
396564562Sgshapiro#endif /* EHOSTUNREACH */
396664562Sgshapiro				if (mci->mci_host != NULL)
396764562Sgshapiro				{
396890792Sgshapiro					(void) sm_strlcpyn(bp,
396990792Sgshapiro							   SPACELEFT(buf, bp),
397090792Sgshapiro							   2, ": ",
397190792Sgshapiro							   mci->mci_host);
397264562Sgshapiro					bp += strlen(bp);
397364562Sgshapiro				}
397464562Sgshapiro				break;
397564562Sgshapiro			}
397690792Sgshapiro			(void) sm_strlcpyn(bp, SPACELEFT(buf, bp), 2, ": ",
397790792Sgshapiro					   statmsg);
397890792Sgshapiro			usestat = true;
397964562Sgshapiro		}
398038032Speter		statmsg = buf;
398138032Speter	}
398238032Speter#if NAMED_BIND
398364562Sgshapiro	else if (status == EX_NOHOST && h_errno != 0)
398438032Speter	{
398590792Sgshapiro		statmsg = sm_errstring(h_errno + E_DNSBASE);
398690792Sgshapiro		(void) sm_snprintf(buf, sizeof buf, "%s (%s)", exmsg + 1,
398790792Sgshapiro				   statmsg);
398838032Speter		statmsg = buf;
398990792Sgshapiro		usestat = true;
399038032Speter	}
399164562Sgshapiro#endif /* NAMED_BIND */
399238032Speter	else
399338032Speter	{
399490792Sgshapiro		statmsg = exmsg;
399564562Sgshapiro		if (*statmsg++ == ':' && errnum != 0)
399638032Speter		{
399790792Sgshapiro			(void) sm_snprintf(buf, sizeof buf, "%s: %s", statmsg,
399890792Sgshapiro					   sm_errstring(errnum));
399938032Speter			statmsg = buf;
400090792Sgshapiro			usestat = true;
400138032Speter		}
400294334Sgshapiro		else if (bitnset(M_LMTP, m->m_flags) && e->e_statmsg != NULL)
400394334Sgshapiro		{
400494334Sgshapiro			(void) sm_snprintf(buf, sizeof buf, "%s (%s)", statmsg,
400594334Sgshapiro					   shortenstring(e->e_statmsg, 403));
400694334Sgshapiro			statmsg = buf;
400794334Sgshapiro			usestat = true;
400894334Sgshapiro		}
400938032Speter	}
401038032Speter
401138032Speter	/*
401238032Speter	**  Print the message as appropriate
401338032Speter	*/
401438032Speter
401564562Sgshapiro	if (status == EX_OK || status == EX_TEMPFAIL)
401638032Speter	{
401738032Speter		extern char MsgBuf[];
401838032Speter
401964562Sgshapiro		if ((off = isenhsc(statmsg + 4, ' ')) > 0)
402064562Sgshapiro		{
402164562Sgshapiro			if (dsn == NULL)
402264562Sgshapiro			{
402390792Sgshapiro				(void) sm_snprintf(dsnbuf, sizeof dsnbuf,
402490792Sgshapiro						   "%.*s", off, statmsg + 4);
402564562Sgshapiro				dsn = dsnbuf;
402664562Sgshapiro			}
402764562Sgshapiro			off += 5;
402864562Sgshapiro		}
402964562Sgshapiro		else
403064562Sgshapiro		{
403164562Sgshapiro			off = 4;
403264562Sgshapiro		}
403364562Sgshapiro		message("%s", statmsg + off);
403464562Sgshapiro		if (status == EX_TEMPFAIL && e->e_xfp != NULL)
403590792Sgshapiro			(void) sm_io_fprintf(e->e_xfp, SM_TIME_DEFAULT, "%s\n",
403690792Sgshapiro					     &MsgBuf[4]);
403738032Speter	}
403838032Speter	else
403938032Speter	{
404064562Sgshapiro		char mbuf[ENHSCLEN + 4];
404138032Speter
404238032Speter		Errors++;
404364562Sgshapiro		if ((off = isenhsc(statmsg + 4, ' ')) > 0 &&
404464562Sgshapiro		    off < sizeof mbuf - 4)
404564562Sgshapiro		{
404664562Sgshapiro			if (dsn == NULL)
404764562Sgshapiro			{
404890792Sgshapiro				(void) sm_snprintf(dsnbuf, sizeof dsnbuf,
404990792Sgshapiro						   "%.*s", off, statmsg + 4);
405064562Sgshapiro				dsn = dsnbuf;
405164562Sgshapiro			}
405264562Sgshapiro			off += 5;
405390792Sgshapiro
405490792Sgshapiro			/* copy only part of statmsg to mbuf */
405590792Sgshapiro			(void) sm_strlcpy(mbuf, statmsg, off);
405690792Sgshapiro			(void) sm_strlcat(mbuf, " %s", sizeof mbuf);
405764562Sgshapiro		}
405864562Sgshapiro		else
405964562Sgshapiro		{
406064562Sgshapiro			dsnbuf[0] = '\0';
406190792Sgshapiro			(void) sm_snprintf(mbuf, sizeof mbuf, "%.3s %%s",
406290792Sgshapiro					   statmsg);
406364562Sgshapiro			off = 4;
406464562Sgshapiro		}
406564562Sgshapiro		usrerr(mbuf, &statmsg[off]);
406638032Speter	}
406738032Speter
406838032Speter	/*
406938032Speter	**  Final cleanup.
407038032Speter	**	Log a record of the transaction.  Compute the new
407138032Speter	**	ExitStat -- if we already had an error, stick with
407238032Speter	**	that.
407338032Speter	*/
407438032Speter
407538032Speter	if (OpMode != MD_VERIFY && !bitset(EF_VRFYONLY, e->e_flags) &&
407664562Sgshapiro	    LogLevel > ((status == EX_TEMPFAIL) ? 8 : (status == EX_OK) ? 7 : 6))
407764562Sgshapiro		logdelivery(m, mci, dsn, statmsg + off, ctladdr, xstart, e);
407838032Speter
407938032Speter	if (tTd(11, 2))
408090792Sgshapiro		sm_dprintf("giveresponse: status=%d, dsn=%s, e->e_message=%s, errnum=%d\n",
408190792Sgshapiro			   status,
408290792Sgshapiro			   dsn == NULL ? "<NULL>" : dsn,
408390792Sgshapiro			   e->e_message == NULL ? "<NULL>" : e->e_message,
408490792Sgshapiro			   errnum);
408538032Speter
408664562Sgshapiro	if (status != EX_TEMPFAIL)
408764562Sgshapiro		setstat(status);
408864562Sgshapiro	if (status != EX_OK && (status != EX_TEMPFAIL || e->e_message == NULL))
408990792Sgshapiro		e->e_message = sm_rpool_strdup_x(e->e_rpool, statmsg + off);
409090792Sgshapiro	if (status != EX_OK && to != NULL && to->q_message == NULL)
409138032Speter	{
409290792Sgshapiro		if (!usestat && e->e_message != NULL)
409390792Sgshapiro			to->q_message = sm_rpool_strdup_x(e->e_rpool,
409490792Sgshapiro							  e->e_message);
409590792Sgshapiro		else
409690792Sgshapiro			to->q_message = sm_rpool_strdup_x(e->e_rpool,
409790792Sgshapiro							  statmsg + off);
409838032Speter	}
409938032Speter	errno = 0;
410073188Sgshapiro	SM_SET_H_ERRNO(0);
410138032Speter}
410290792Sgshapiro/*
410338032Speter**  LOGDELIVERY -- log the delivery in the system log
410438032Speter**
410538032Speter**	Care is taken to avoid logging lines that are too long, because
410638032Speter**	some versions of syslog have an unfortunate proclivity for core
410738032Speter**	dumping.  This is a hack, to be sure, that is at best empirical.
410838032Speter**
410938032Speter**	Parameters:
411038032Speter**		m -- the mailer info.  Can be NULL for initial queue.
411138032Speter**		mci -- the mailer connection info -- can be NULL if the
411264562Sgshapiro**			log is occurring when no connection is active.
411364562Sgshapiro**		dsn -- the DSN attached to the status.
411464562Sgshapiro**		status -- the message to print for the status.
411538032Speter**		ctladdr -- the controlling address for the to list.
411638032Speter**		xstart -- the transaction start time, used for
411738032Speter**			computing transaction delay.
411838032Speter**		e -- the current envelope.
411938032Speter**
412038032Speter**	Returns:
412138032Speter**		none
412238032Speter**
412338032Speter**	Side Effects:
412438032Speter**		none
412538032Speter*/
412638032Speter
412738032Spetervoid
412864562Sgshapirologdelivery(m, mci, dsn, status, ctladdr, xstart, e)
412938032Speter	MAILER *m;
413038032Speter	register MCI *mci;
413164562Sgshapiro	char *dsn;
413264562Sgshapiro	const char *status;
413338032Speter	ADDRESS *ctladdr;
413438032Speter	time_t xstart;
413538032Speter	register ENVELOPE *e;
413638032Speter{
413738032Speter	register char *bp;
413838032Speter	register char *p;
413938032Speter	int l;
414090792Sgshapiro	time_t now = curtime();
414138032Speter	char buf[1024];
414238032Speter
414364562Sgshapiro#if (SYSLOG_BUFSIZE) >= 256
414438032Speter	/* ctladdr: max 106 bytes */
414538032Speter	bp = buf;
414638032Speter	if (ctladdr != NULL)
414738032Speter	{
414890792Sgshapiro		(void) sm_strlcpyn(bp, SPACELEFT(buf, bp), 2, ", ctladdr=",
414990792Sgshapiro				   shortenstring(ctladdr->q_paddr, 83));
415038032Speter		bp += strlen(bp);
415138032Speter		if (bitset(QGOODUID, ctladdr->q_flags))
415238032Speter		{
415390792Sgshapiro			(void) sm_snprintf(bp, SPACELEFT(buf, bp), " (%d/%d)",
415490792Sgshapiro					   (int) ctladdr->q_uid,
415590792Sgshapiro					   (int) ctladdr->q_gid);
415638032Speter			bp += strlen(bp);
415738032Speter		}
415838032Speter	}
415938032Speter
416038032Speter	/* delay & xdelay: max 41 bytes */
416190792Sgshapiro	(void) sm_strlcpyn(bp, SPACELEFT(buf, bp), 2, ", delay=",
416290792Sgshapiro			   pintvl(now - e->e_ctime, true));
416338032Speter	bp += strlen(bp);
416438032Speter
416538032Speter	if (xstart != (time_t) 0)
416638032Speter	{
416790792Sgshapiro		(void) sm_strlcpyn(bp, SPACELEFT(buf, bp), 2, ", xdelay=",
416890792Sgshapiro				   pintvl(now - xstart, true));
416938032Speter		bp += strlen(bp);
417038032Speter	}
417138032Speter
417238032Speter	/* mailer: assume about 19 bytes (max 10 byte mailer name) */
417338032Speter	if (m != NULL)
417438032Speter	{
417590792Sgshapiro		(void) sm_strlcpyn(bp, SPACELEFT(buf, bp), 2, ", mailer=",
417690792Sgshapiro				   m->m_name);
417738032Speter		bp += strlen(bp);
417838032Speter	}
417938032Speter
418064562Sgshapiro	/* pri: changes with each delivery attempt */
418190792Sgshapiro	(void) sm_snprintf(bp, SPACELEFT(buf, bp), ", pri=%ld",
418290792Sgshapiro		e->e_msgpriority);
418364562Sgshapiro	bp += strlen(bp);
418464562Sgshapiro
418538032Speter	/* relay: max 66 bytes for IPv4 addresses */
418638032Speter	if (mci != NULL && mci->mci_host != NULL)
418738032Speter	{
418838032Speter		extern SOCKADDR CurHostAddr;
418938032Speter
419090792Sgshapiro		(void) sm_strlcpyn(bp, SPACELEFT(buf, bp), 2, ", relay=",
419190792Sgshapiro				   shortenstring(mci->mci_host, 40));
419238032Speter		bp += strlen(bp);
419338032Speter
419438032Speter		if (CurHostAddr.sa.sa_family != 0)
419538032Speter		{
419690792Sgshapiro			(void) sm_snprintf(bp, SPACELEFT(buf, bp), " [%s]",
419790792Sgshapiro					   anynet_ntoa(&CurHostAddr));
419838032Speter		}
419938032Speter	}
420090792Sgshapiro#if _FFR_QUARANTINE
420190792Sgshapiro	else if (strcmp(status, "quarantined") == 0)
420290792Sgshapiro	{
420390792Sgshapiro		if (e->e_quarmsg != NULL)
420490792Sgshapiro			(void) sm_snprintf(bp, SPACELEFT(buf, bp),
420590792Sgshapiro					   ", quarantine=%s",
420690792Sgshapiro					   shortenstring(e->e_quarmsg, 40));
420790792Sgshapiro	}
420890792Sgshapiro#endif /* _FFR_QUARANTINE */
420964562Sgshapiro	else if (strcmp(status, "queued") != 0)
421038032Speter	{
421138032Speter		p = macvalue('h', e);
421238032Speter		if (p != NULL && p[0] != '\0')
421338032Speter		{
421490792Sgshapiro			(void) sm_snprintf(bp, SPACELEFT(buf, bp),
421590792Sgshapiro					   ", relay=%s", shortenstring(p, 40));
421638032Speter		}
421738032Speter	}
421838032Speter	bp += strlen(bp);
421938032Speter
422064562Sgshapiro	/* dsn */
422164562Sgshapiro	if (dsn != NULL && *dsn != '\0')
422264562Sgshapiro	{
422390792Sgshapiro		(void) sm_strlcpyn(bp, SPACELEFT(buf, bp), 2, ", dsn=",
422490792Sgshapiro				   shortenstring(dsn, ENHSCLEN));
422564562Sgshapiro		bp += strlen(bp);
422664562Sgshapiro	}
422738032Speter
422864562Sgshapiro# define STATLEN		(((SYSLOG_BUFSIZE) - 100) / 4)
422964562Sgshapiro# if (STATLEN) < 63
423064562Sgshapiro#  undef STATLEN
423164562Sgshapiro#  define STATLEN	63
423264562Sgshapiro# endif /* (STATLEN) < 63 */
423364562Sgshapiro# if (STATLEN) > 203
423464562Sgshapiro#  undef STATLEN
423564562Sgshapiro#  define STATLEN	203
423664562Sgshapiro# endif /* (STATLEN) > 203 */
423764562Sgshapiro
423838032Speter	/* stat: max 210 bytes */
423938032Speter	if ((bp - buf) > (sizeof buf - ((STATLEN) + 20)))
424038032Speter	{
424138032Speter		/* desperation move -- truncate data */
424238032Speter		bp = buf + sizeof buf - ((STATLEN) + 17);
424390792Sgshapiro		(void) sm_strlcpy(bp, "...", SPACELEFT(buf, bp));
424438032Speter		bp += 3;
424538032Speter	}
424638032Speter
424790792Sgshapiro	(void) sm_strlcpy(bp, ", stat=", SPACELEFT(buf, bp));
424838032Speter	bp += strlen(bp);
424938032Speter
425090792Sgshapiro	(void) sm_strlcpy(bp, shortenstring(status, STATLEN),
425190792Sgshapiro			  SPACELEFT(buf, bp));
425238032Speter
425338032Speter	/* id, to: max 13 + TOBUFSIZE bytes */
425438032Speter	l = SYSLOG_BUFSIZE - 100 - strlen(buf);
425590792Sgshapiro	if (l < 0)
425690792Sgshapiro		l = 0;
425764562Sgshapiro	p = e->e_to == NULL ? "NO-TO-LIST" : e->e_to;
425890792Sgshapiro	while (strlen(p) >= l)
425938032Speter	{
426064562Sgshapiro		register char *q;
426138032Speter
426264562Sgshapiro		for (q = p + l; q > p; q--)
426364562Sgshapiro		{
426464562Sgshapiro			if (*q == ',')
426564562Sgshapiro				break;
426664562Sgshapiro		}
426764562Sgshapiro		if (p == q)
426864562Sgshapiro			break;
426990792Sgshapiro		sm_syslog(LOG_INFO, e->e_id, "to=%.*s [more]%s",
427066494Sgshapiro			  (int) (++q - p), p, buf);
427138032Speter		p = q;
427238032Speter	}
427364562Sgshapiro	sm_syslog(LOG_INFO, e->e_id, "to=%.*s%s", l, p, buf);
427438032Speter
427564562Sgshapiro#else /* (SYSLOG_BUFSIZE) >= 256 */
427638032Speter
427738032Speter	l = SYSLOG_BUFSIZE - 85;
427890792Sgshapiro	if (l < 0)
427990792Sgshapiro		l = 0;
428064562Sgshapiro	p = e->e_to == NULL ? "NO-TO-LIST" : e->e_to;
428190792Sgshapiro	while (strlen(p) >= l)
428238032Speter	{
428364562Sgshapiro		register char *q;
428438032Speter
428564562Sgshapiro		for (q = p + l; q > p; q--)
428664562Sgshapiro		{
428764562Sgshapiro			if (*q == ',')
428864562Sgshapiro				break;
428964562Sgshapiro		}
429064562Sgshapiro		if (p == q)
429164562Sgshapiro			break;
429264562Sgshapiro
429390792Sgshapiro		sm_syslog(LOG_INFO, e->e_id, "to=%.*s [more]",
429466494Sgshapiro			  (int) (++q - p), p);
429538032Speter		p = q;
429638032Speter	}
429764562Sgshapiro	sm_syslog(LOG_INFO, e->e_id, "to=%.*s", l, p);
429838032Speter
429938032Speter	if (ctladdr != NULL)
430038032Speter	{
430138032Speter		bp = buf;
430290792Sgshapiro		(void) sm_strlcpyn(bp, SPACELEFT(buf, bp), 2, "ctladdr=",
430390792Sgshapiro				   shortenstring(ctladdr->q_paddr, 83));
430438032Speter		bp += strlen(bp);
430538032Speter		if (bitset(QGOODUID, ctladdr->q_flags))
430638032Speter		{
430790792Sgshapiro			(void) sm_snprintf(bp, SPACELEFT(buf, bp), " (%d/%d)",
430890792Sgshapiro					   ctladdr->q_uid, ctladdr->q_gid);
430938032Speter			bp += strlen(bp);
431038032Speter		}
431138032Speter		sm_syslog(LOG_INFO, e->e_id, "%s", buf);
431238032Speter	}
431338032Speter	bp = buf;
431490792Sgshapiro	(void) sm_strlcpyn(bp, SPACELEFT(buf, bp), 2, "delay=",
431590792Sgshapiro			   pintvl(now - e->e_ctime, true));
431638032Speter	bp += strlen(bp);
431738032Speter	if (xstart != (time_t) 0)
431838032Speter	{
431990792Sgshapiro		(void) sm_strlcpyn(bp, SPACELEFT(buf, bp), 2, ", xdelay=",
432090792Sgshapiro				   pintvl(now - xstart, true));
432138032Speter		bp += strlen(bp);
432238032Speter	}
432338032Speter
432438032Speter	if (m != NULL)
432538032Speter	{
432690792Sgshapiro		(void) sm_strlcpyn(bp, SPACELEFT(buf, bp), 2, ", mailer=",
432790792Sgshapiro				   m->m_name);
432838032Speter		bp += strlen(bp);
432938032Speter	}
433038032Speter	sm_syslog(LOG_INFO, e->e_id, "%.1000s", buf);
433138032Speter
433238032Speter	buf[0] = '\0';
433338032Speter	bp = buf;
433438032Speter	if (mci != NULL && mci->mci_host != NULL)
433538032Speter	{
433638032Speter		extern SOCKADDR CurHostAddr;
433738032Speter
433890792Sgshapiro		(void) sm_snprintf(bp, SPACELEFT(buf, bp), "relay=%.100s",
433990792Sgshapiro				   mci->mci_host);
434038032Speter		bp += strlen(bp);
434138032Speter
434238032Speter		if (CurHostAddr.sa.sa_family != 0)
434390792Sgshapiro			(void) sm_snprintf(bp, SPACELEFT(buf, bp),
434490792Sgshapiro					   " [%.100s]",
434590792Sgshapiro					   anynet_ntoa(&CurHostAddr));
434638032Speter	}
434790792Sgshapiro#if _FFR_QUARANTINE
434890792Sgshapiro	else if (strcmp(status, "quarantined") == 0)
434990792Sgshapiro	{
435090792Sgshapiro		if (e->e_quarmsg != NULL)
435190792Sgshapiro			(void) sm_snprintf(bp, SPACELEFT(buf, bp),
435290792Sgshapiro					   ", quarantine=%.100s",
435390792Sgshapiro					   e->e_quarmsg);
435490792Sgshapiro	}
435590792Sgshapiro#endif /* _FFR_QUARANTINE */
435664562Sgshapiro	else if (strcmp(status, "queued") != 0)
435738032Speter	{
435838032Speter		p = macvalue('h', e);
435938032Speter		if (p != NULL && p[0] != '\0')
436090792Sgshapiro			(void) sm_snprintf(buf, sizeof buf, "relay=%.100s", p);
436138032Speter	}
436238032Speter	if (buf[0] != '\0')
436338032Speter		sm_syslog(LOG_INFO, e->e_id, "%.1000s", buf);
436438032Speter
436564562Sgshapiro	sm_syslog(LOG_INFO, e->e_id, "stat=%s", shortenstring(status, 63));
436664562Sgshapiro#endif /* (SYSLOG_BUFSIZE) >= 256 */
436738032Speter}
436890792Sgshapiro/*
436938032Speter**  PUTFROMLINE -- output a UNIX-style from line (or whatever)
437038032Speter**
437138032Speter**	This can be made an arbitrary message separator by changing $l
437238032Speter**
437338032Speter**	One of the ugliest hacks seen by human eyes is contained herein:
437438032Speter**	UUCP wants those stupid "remote from <host>" lines.  Why oh why
437538032Speter**	does a well-meaning programmer such as myself have to deal with
437638032Speter**	this kind of antique garbage????
437738032Speter**
437838032Speter**	Parameters:
437938032Speter**		mci -- the connection information.
438038032Speter**		e -- the envelope.
438138032Speter**
438238032Speter**	Returns:
438338032Speter**		none
438438032Speter**
438538032Speter**	Side Effects:
438638032Speter**		outputs some text to fp.
438738032Speter*/
438838032Speter
438938032Spetervoid
439038032Speterputfromline(mci, e)
439138032Speter	register MCI *mci;
439238032Speter	ENVELOPE *e;
439338032Speter{
439438032Speter	char *template = UnixFromLine;
439538032Speter	char buf[MAXLINE];
439638032Speter	char xbuf[MAXLINE];
439738032Speter
439838032Speter	if (bitnset(M_NHDR, mci->mci_mailer->m_flags))
439938032Speter		return;
440038032Speter
440138032Speter	mci->mci_flags |= MCIF_INHEADER;
440238032Speter
440338032Speter	if (bitnset(M_UGLYUUCP, mci->mci_mailer->m_flags))
440438032Speter	{
440538032Speter		char *bang;
440638032Speter
440738032Speter		expand("\201g", buf, sizeof buf, e);
440838032Speter		bang = strchr(buf, '!');
440938032Speter		if (bang == NULL)
441038032Speter		{
441138032Speter			char *at;
441238032Speter			char hname[MAXNAME];
441338032Speter
441464562Sgshapiro			/*
441542575Speter			**  If we can construct a UUCP path, do so
441638032Speter			*/
441738032Speter
441838032Speter			at = strrchr(buf, '@');
441938032Speter			if (at == NULL)
442038032Speter			{
442164562Sgshapiro				expand("\201k", hname, sizeof hname, e);
442238032Speter				at = hname;
442338032Speter			}
442438032Speter			else
442538032Speter				*at++ = '\0';
442690792Sgshapiro			(void) sm_snprintf(xbuf, sizeof xbuf,
442790792Sgshapiro					   "From %.800s  \201d remote from %.100s\n",
442890792Sgshapiro					   buf, at);
442938032Speter		}
443038032Speter		else
443138032Speter		{
443238032Speter			*bang++ = '\0';
443390792Sgshapiro			(void) sm_snprintf(xbuf, sizeof xbuf,
443490792Sgshapiro					   "From %.800s  \201d remote from %.100s\n",
443590792Sgshapiro					   bang, buf);
443638032Speter			template = xbuf;
443738032Speter		}
443838032Speter	}
443938032Speter	expand(template, buf, sizeof buf, e);
444038032Speter	putxline(buf, strlen(buf), mci, PXLF_HEADER);
444138032Speter}
444290792Sgshapiro/*
444338032Speter**  PUTBODY -- put the body of a message.
444438032Speter**
444538032Speter**	Parameters:
444638032Speter**		mci -- the connection information.
444738032Speter**		e -- the envelope to put out.
444838032Speter**		separator -- if non-NULL, a message separator that must
444938032Speter**			not be permitted in the resulting message.
445038032Speter**
445138032Speter**	Returns:
445238032Speter**		none.
445338032Speter**
445438032Speter**	Side Effects:
445538032Speter**		The message is written onto fp.
445638032Speter*/
445738032Speter
445838032Speter/* values for output state variable */
445938032Speter#define OS_HEAD		0	/* at beginning of line */
446038032Speter#define OS_CR		1	/* read a carriage return */
446138032Speter#define OS_INLINE	2	/* putting rest of line */
446238032Speter
446338032Spetervoid
446438032Speterputbody(mci, e, separator)
446538032Speter	register MCI *mci;
446638032Speter	register ENVELOPE *e;
446738032Speter	char *separator;
446838032Speter{
446990792Sgshapiro	bool dead = false;
447038032Speter	char buf[MAXLINE];
447190792Sgshapiro#if MIME8TO7
447242575Speter	char *boundaries[MAXMIMENESTING + 1];
447390792Sgshapiro#endif /* MIME8TO7 */
447438032Speter
447538032Speter	/*
447638032Speter	**  Output the body of the message
447738032Speter	*/
447838032Speter
447938032Speter	if (e->e_dfp == NULL && bitset(EF_HAS_DF, e->e_flags))
448038032Speter	{
448190792Sgshapiro		char *df = queuename(e, DATAFL_LETTER);
448238032Speter
448390792Sgshapiro		e->e_dfp = sm_io_open(SmFtStdio, SM_TIME_DEFAULT, df,
4484120256Sgshapiro				      SM_IO_RDONLY_B, NULL);
448538032Speter		if (e->e_dfp == NULL)
448664562Sgshapiro		{
448764562Sgshapiro			char *msg = "!putbody: Cannot open %s for %s from %s";
448864562Sgshapiro
448964562Sgshapiro			if (errno == ENOENT)
449064562Sgshapiro				msg++;
449164562Sgshapiro			syserr(msg, df, e->e_to, e->e_from.q_paddr);
449264562Sgshapiro		}
449390792Sgshapiro
449438032Speter	}
449538032Speter	if (e->e_dfp == NULL)
449638032Speter	{
449738032Speter		if (bitset(MCIF_INHEADER, mci->mci_flags))
449838032Speter		{
449938032Speter			putline("", mci);
450038032Speter			mci->mci_flags &= ~MCIF_INHEADER;
450138032Speter		}
450238032Speter		putline("<<< No Message Collected >>>", mci);
450338032Speter		goto endofmessage;
450438032Speter	}
450564562Sgshapiro
450638032Speter	if (e->e_dfino == (ino_t) 0)
450738032Speter	{
450838032Speter		struct stat stbuf;
450938032Speter
451090792Sgshapiro		if (fstat(sm_io_getinfo(e->e_dfp, SM_IO_WHAT_FD, NULL), &stbuf)
451190792Sgshapiro		    < 0)
451238032Speter			e->e_dfino = -1;
451338032Speter		else
451438032Speter		{
451538032Speter			e->e_dfdev = stbuf.st_dev;
451638032Speter			e->e_dfino = stbuf.st_ino;
451738032Speter		}
451838032Speter	}
451938032Speter
452090792Sgshapiro	/* paranoia: the data file should always be in a rewound state */
452164562Sgshapiro	(void) bfrewind(e->e_dfp);
452264562Sgshapiro
452338032Speter#if MIME8TO7
452438032Speter	if (bitset(MCIF_CVT8TO7, mci->mci_flags))
452538032Speter	{
452638032Speter		/*
452738032Speter		**  Do 8 to 7 bit MIME conversion.
452838032Speter		*/
452938032Speter
453038032Speter		/* make sure it looks like a MIME message */
453138032Speter		if (hvalue("MIME-Version", e->e_header) == NULL)
453238032Speter			putline("MIME-Version: 1.0", mci);
453338032Speter
453438032Speter		if (hvalue("Content-Type", e->e_header) == NULL)
453538032Speter		{
453690792Sgshapiro			(void) sm_snprintf(buf, sizeof buf,
453790792Sgshapiro					   "Content-Type: text/plain; charset=%s",
453890792Sgshapiro					   defcharset(e));
453938032Speter			putline(buf, mci);
454038032Speter		}
454138032Speter
454238032Speter		/* now do the hard work */
454338032Speter		boundaries[0] = NULL;
454438032Speter		mci->mci_flags |= MCIF_INHEADER;
454564562Sgshapiro		(void) mime8to7(mci, e->e_header, e, boundaries, M87F_OUTER);
454638032Speter	}
454738032Speter# if MIME7TO8
454838032Speter	else if (bitset(MCIF_CVT7TO8, mci->mci_flags))
454938032Speter	{
455064562Sgshapiro		(void) mime7to8(mci, e->e_header, e);
455138032Speter	}
455264562Sgshapiro# endif /* MIME7TO8 */
455342575Speter	else if (MaxMimeHeaderLength > 0 || MaxMimeFieldLength > 0)
455442575Speter	{
455564562Sgshapiro		bool oldsuprerrs = SuprErrs;
455664562Sgshapiro
455742575Speter		/* Use mime8to7 to check multipart for MIME header overflows */
455842575Speter		boundaries[0] = NULL;
455942575Speter		mci->mci_flags |= MCIF_INHEADER;
456064562Sgshapiro
456164562Sgshapiro		/*
456264562Sgshapiro		**  If EF_DONT_MIME is set, we have a broken MIME message
456364562Sgshapiro		**  and don't want to generate a new bounce message whose
456464562Sgshapiro		**  body propagates the broken MIME.  We can't just not call
456564562Sgshapiro		**  mime8to7() as is done above since we need the security
456664562Sgshapiro		**  checks.  The best we can do is suppress the errors.
456764562Sgshapiro		*/
456864562Sgshapiro
456964562Sgshapiro		if (bitset(EF_DONT_MIME, e->e_flags))
457090792Sgshapiro			SuprErrs = true;
457164562Sgshapiro
457264562Sgshapiro		(void) mime8to7(mci, e->e_header, e, boundaries,
457364562Sgshapiro				M87F_OUTER|M87F_NO8TO7);
457464562Sgshapiro
457564562Sgshapiro		/* restore SuprErrs */
457664562Sgshapiro		SuprErrs = oldsuprerrs;
457742575Speter	}
457838032Speter	else
457964562Sgshapiro#endif /* MIME8TO7 */
458038032Speter	{
458138032Speter		int ostate;
458238032Speter		register char *bp;
458338032Speter		register char *pbp;
458438032Speter		register int c;
458538032Speter		register char *xp;
458638032Speter		int padc;
458738032Speter		char *buflim;
458838032Speter		int pos = 0;
458964562Sgshapiro		char peekbuf[12];
459038032Speter
459138032Speter		if (bitset(MCIF_INHEADER, mci->mci_flags))
459238032Speter		{
459338032Speter			putline("", mci);
459438032Speter			mci->mci_flags &= ~MCIF_INHEADER;
459538032Speter		}
459638032Speter
459738032Speter		/* determine end of buffer; allow for short mailer lines */
459838032Speter		buflim = &buf[sizeof buf - 1];
459938032Speter		if (mci->mci_mailer->m_linelimit > 0 &&
460038032Speter		    mci->mci_mailer->m_linelimit < sizeof buf - 1)
460138032Speter			buflim = &buf[mci->mci_mailer->m_linelimit - 1];
460238032Speter
460338032Speter		/* copy temp file to output with mapping */
460438032Speter		ostate = OS_HEAD;
460538032Speter		bp = buf;
460638032Speter		pbp = peekbuf;
460790792Sgshapiro		while (!sm_io_error(mci->mci_out) && !dead)
460838032Speter		{
460938032Speter			if (pbp > peekbuf)
461038032Speter				c = *--pbp;
461190792Sgshapiro			else if ((c = sm_io_getc(e->e_dfp, SM_TIME_DEFAULT))
461290792Sgshapiro				 == SM_IO_EOF)
461338032Speter				break;
461438032Speter			if (bitset(MCIF_7BIT, mci->mci_flags))
461538032Speter				c &= 0x7f;
461638032Speter			switch (ostate)
461738032Speter			{
461838032Speter			  case OS_HEAD:
461938032Speter				if (c == '\0' &&
462090792Sgshapiro				    bitnset(M_NONULLS,
462190792Sgshapiro					    mci->mci_mailer->m_flags))
462238032Speter					break;
462338032Speter				if (c != '\r' && c != '\n' && bp < buflim)
462438032Speter				{
462538032Speter					*bp++ = c;
462638032Speter					break;
462738032Speter				}
462838032Speter
462938032Speter				/* check beginning of line for special cases */
463038032Speter				*bp = '\0';
463138032Speter				pos = 0;
463290792Sgshapiro				padc = SM_IO_EOF;
463338032Speter				if (buf[0] == 'F' &&
463490792Sgshapiro				    bitnset(M_ESCFROM, mci->mci_mailer->m_flags)
463590792Sgshapiro				    && strncmp(buf, "From ", 5) == 0)
463638032Speter				{
463738032Speter					padc = '>';
463838032Speter				}
463938032Speter				if (buf[0] == '-' && buf[1] == '-' &&
464038032Speter				    separator != NULL)
464138032Speter				{
464238032Speter					/* possible separator */
464338032Speter					int sl = strlen(separator);
464438032Speter
464590792Sgshapiro					if (strncmp(&buf[2], separator, sl)
464690792Sgshapiro					    == 0)
464738032Speter						padc = ' ';
464838032Speter				}
464938032Speter				if (buf[0] == '.' &&
465038032Speter				    bitnset(M_XDOT, mci->mci_mailer->m_flags))
465138032Speter				{
465238032Speter					padc = '.';
465338032Speter				}
465438032Speter
465538032Speter				/* now copy out saved line */
465638032Speter				if (TrafficLogFile != NULL)
465738032Speter				{
465890792Sgshapiro					(void) sm_io_fprintf(TrafficLogFile,
465990792Sgshapiro							     SM_TIME_DEFAULT,
466090792Sgshapiro							     "%05d >>> ",
466190792Sgshapiro							     (int) CurrentPid);
466290792Sgshapiro					if (padc != SM_IO_EOF)
466390792Sgshapiro						(void) sm_io_putc(TrafficLogFile,
466490792Sgshapiro								  SM_TIME_DEFAULT,
466590792Sgshapiro								  padc);
466638032Speter					for (xp = buf; xp < bp; xp++)
466790792Sgshapiro						(void) sm_io_putc(TrafficLogFile,
466890792Sgshapiro								  SM_TIME_DEFAULT,
466990792Sgshapiro								  (unsigned char) *xp);
467038032Speter					if (c == '\n')
467190792Sgshapiro						(void) sm_io_fputs(TrafficLogFile,
467290792Sgshapiro								   SM_TIME_DEFAULT,
467390792Sgshapiro								   mci->mci_mailer->m_eol);
467438032Speter				}
467590792Sgshapiro				if (padc != SM_IO_EOF)
467638032Speter				{
467790792Sgshapiro					if (sm_io_putc(mci->mci_out,
467890792Sgshapiro						       SM_TIME_DEFAULT, padc)
467990792Sgshapiro					    == SM_IO_EOF)
468064562Sgshapiro					{
468190792Sgshapiro						dead = true;
468264562Sgshapiro						continue;
468364562Sgshapiro					}
468471345Sgshapiro					else
468571345Sgshapiro					{
468671345Sgshapiro						/* record progress for DATA timeout */
468790792Sgshapiro						DataProgress = true;
468871345Sgshapiro					}
468938032Speter					pos++;
469038032Speter				}
469138032Speter				for (xp = buf; xp < bp; xp++)
469238032Speter				{
469390792Sgshapiro					if (sm_io_putc(mci->mci_out,
469490792Sgshapiro						       SM_TIME_DEFAULT,
469590792Sgshapiro						       (unsigned char) *xp)
469690792Sgshapiro					    == SM_IO_EOF)
469764562Sgshapiro					{
469890792Sgshapiro						dead = true;
469964562Sgshapiro						break;
470064562Sgshapiro					}
470171345Sgshapiro					else
470271345Sgshapiro					{
470371345Sgshapiro						/* record progress for DATA timeout */
470490792Sgshapiro						DataProgress = true;
470571345Sgshapiro					}
470638032Speter				}
470764562Sgshapiro				if (dead)
470864562Sgshapiro					continue;
470938032Speter				if (c == '\n')
471038032Speter				{
471190792Sgshapiro					if (sm_io_fputs(mci->mci_out,
471290792Sgshapiro							SM_TIME_DEFAULT,
471390792Sgshapiro							mci->mci_mailer->m_eol)
471490792Sgshapiro							== SM_IO_EOF)
471564562Sgshapiro						break;
471671345Sgshapiro					else
471771345Sgshapiro					{
471871345Sgshapiro						/* record progress for DATA timeout */
471990792Sgshapiro						DataProgress = true;
472071345Sgshapiro					}
472138032Speter					pos = 0;
472238032Speter				}
472338032Speter				else
472438032Speter				{
472538032Speter					pos += bp - buf;
472638032Speter					if (c != '\r')
4727112810Sgshapiro					{
4728112810Sgshapiro						SM_ASSERT(pbp < peekbuf +
4729112810Sgshapiro								sizeof(peekbuf));
473038032Speter						*pbp++ = c;
4731112810Sgshapiro					}
473238032Speter				}
473364562Sgshapiro
473438032Speter				bp = buf;
473538032Speter
473638032Speter				/* determine next state */
473738032Speter				if (c == '\n')
473838032Speter					ostate = OS_HEAD;
473938032Speter				else if (c == '\r')
474038032Speter					ostate = OS_CR;
474138032Speter				else
474238032Speter					ostate = OS_INLINE;
474338032Speter				continue;
474438032Speter
474538032Speter			  case OS_CR:
474638032Speter				if (c == '\n')
474738032Speter				{
474838032Speter					/* got CRLF */
474990792Sgshapiro					if (sm_io_fputs(mci->mci_out,
475090792Sgshapiro							SM_TIME_DEFAULT,
475190792Sgshapiro							mci->mci_mailer->m_eol)
475290792Sgshapiro							== SM_IO_EOF)
475364562Sgshapiro						continue;
475471345Sgshapiro					else
475571345Sgshapiro					{
475671345Sgshapiro						/* record progress for DATA timeout */
475790792Sgshapiro						DataProgress = true;
475871345Sgshapiro					}
475964562Sgshapiro
476038032Speter					if (TrafficLogFile != NULL)
476138032Speter					{
476290792Sgshapiro						(void) sm_io_fputs(TrafficLogFile,
476390792Sgshapiro								   SM_TIME_DEFAULT,
476490792Sgshapiro								   mci->mci_mailer->m_eol);
476538032Speter					}
476638032Speter					ostate = OS_HEAD;
476738032Speter					continue;
476838032Speter				}
476938032Speter
477038032Speter				/* had a naked carriage return */
4771112810Sgshapiro				SM_ASSERT(pbp < peekbuf + sizeof(peekbuf));
477238032Speter				*pbp++ = c;
477338032Speter				c = '\r';
477438032Speter				ostate = OS_INLINE;
477538032Speter				goto putch;
477638032Speter
477738032Speter			  case OS_INLINE:
477838032Speter				if (c == '\r')
477938032Speter				{
478038032Speter					ostate = OS_CR;
478138032Speter					continue;
478238032Speter				}
478338032Speter				if (c == '\0' &&
478490792Sgshapiro				    bitnset(M_NONULLS,
478590792Sgshapiro					    mci->mci_mailer->m_flags))
478638032Speter					break;
478738032Speterputch:
478838032Speter				if (mci->mci_mailer->m_linelimit > 0 &&
478964562Sgshapiro				    pos >= mci->mci_mailer->m_linelimit - 1 &&
479038032Speter				    c != '\n')
479138032Speter				{
479264562Sgshapiro					int d;
479364562Sgshapiro
479464562Sgshapiro					/* check next character for EOL */
479564562Sgshapiro					if (pbp > peekbuf)
479664562Sgshapiro						d = *(pbp - 1);
479790792Sgshapiro					else if ((d = sm_io_getc(e->e_dfp,
479890792Sgshapiro								 SM_TIME_DEFAULT))
479990792Sgshapiro						 != SM_IO_EOF)
4800112810Sgshapiro					{
4801112810Sgshapiro						SM_ASSERT(pbp < peekbuf +
4802112810Sgshapiro								sizeof(peekbuf));
480364562Sgshapiro						*pbp++ = d;
4804112810Sgshapiro					}
480564562Sgshapiro
480690792Sgshapiro					if (d == '\n' || d == SM_IO_EOF)
480764562Sgshapiro					{
480864562Sgshapiro						if (TrafficLogFile != NULL)
480990792Sgshapiro							(void) sm_io_putc(TrafficLogFile,
481090792Sgshapiro									  SM_TIME_DEFAULT,
481190792Sgshapiro									  (unsigned char) c);
481290792Sgshapiro						if (sm_io_putc(mci->mci_out,
481390792Sgshapiro							       SM_TIME_DEFAULT,
481490792Sgshapiro							       (unsigned char) c)
481590792Sgshapiro							       == SM_IO_EOF)
481664562Sgshapiro						{
481790792Sgshapiro							dead = true;
481864562Sgshapiro							continue;
481964562Sgshapiro						}
482071345Sgshapiro						else
482171345Sgshapiro						{
482271345Sgshapiro							/* record progress for DATA timeout */
482390792Sgshapiro							DataProgress = true;
482471345Sgshapiro						}
482564562Sgshapiro						pos++;
482664562Sgshapiro						continue;
482764562Sgshapiro					}
482864562Sgshapiro
482990792Sgshapiro					if (sm_io_putc(mci->mci_out,
483090792Sgshapiro						       SM_TIME_DEFAULT, '!')
483190792Sgshapiro					    == SM_IO_EOF ||
483290792Sgshapiro					    sm_io_fputs(mci->mci_out,
483390792Sgshapiro							SM_TIME_DEFAULT,
483490792Sgshapiro							mci->mci_mailer->m_eol)
483590792Sgshapiro					    == SM_IO_EOF)
483664562Sgshapiro					{
483790792Sgshapiro						dead = true;
483864562Sgshapiro						continue;
483964562Sgshapiro					}
484071345Sgshapiro					else
484171345Sgshapiro					{
484271345Sgshapiro						/* record progress for DATA timeout */
484390792Sgshapiro						DataProgress = true;
484471345Sgshapiro					}
484564562Sgshapiro
484638032Speter					if (TrafficLogFile != NULL)
484738032Speter					{
484890792Sgshapiro						(void) sm_io_fprintf(TrafficLogFile,
484990792Sgshapiro								     SM_TIME_DEFAULT,
485090792Sgshapiro								     "!%s",
485190792Sgshapiro								     mci->mci_mailer->m_eol);
485238032Speter					}
485338032Speter					ostate = OS_HEAD;
4854112810Sgshapiro					SM_ASSERT(pbp < peekbuf +
4855112810Sgshapiro							sizeof(peekbuf));
485638032Speter					*pbp++ = c;
485738032Speter					continue;
485838032Speter				}
485938032Speter				if (c == '\n')
486038032Speter				{
486138032Speter					if (TrafficLogFile != NULL)
486290792Sgshapiro						(void) sm_io_fputs(TrafficLogFile,
486390792Sgshapiro								   SM_TIME_DEFAULT,
486490792Sgshapiro								   mci->mci_mailer->m_eol);
486590792Sgshapiro					if (sm_io_fputs(mci->mci_out,
486690792Sgshapiro							SM_TIME_DEFAULT,
486790792Sgshapiro							mci->mci_mailer->m_eol)
486890792Sgshapiro							== SM_IO_EOF)
486964562Sgshapiro						continue;
487071345Sgshapiro					else
487171345Sgshapiro					{
487271345Sgshapiro						/* record progress for DATA timeout */
487390792Sgshapiro						DataProgress = true;
487471345Sgshapiro					}
487538032Speter					pos = 0;
487638032Speter					ostate = OS_HEAD;
487738032Speter				}
487838032Speter				else
487938032Speter				{
488038032Speter					if (TrafficLogFile != NULL)
488190792Sgshapiro						(void) sm_io_putc(TrafficLogFile,
488290792Sgshapiro								  SM_TIME_DEFAULT,
488390792Sgshapiro								  (unsigned char) c);
488490792Sgshapiro					if (sm_io_putc(mci->mci_out,
488590792Sgshapiro						       SM_TIME_DEFAULT,
488690792Sgshapiro						       (unsigned char) c)
488790792Sgshapiro					    == SM_IO_EOF)
488864562Sgshapiro					{
488990792Sgshapiro						dead = true;
489064562Sgshapiro						continue;
489164562Sgshapiro					}
489271345Sgshapiro					else
489371345Sgshapiro					{
489471345Sgshapiro						/* record progress for DATA timeout */
489590792Sgshapiro						DataProgress = true;
489671345Sgshapiro					}
489738032Speter					pos++;
489838032Speter					ostate = OS_INLINE;
489938032Speter				}
490038032Speter				break;
490138032Speter			}
490238032Speter		}
490338032Speter
490438032Speter		/* make sure we are at the beginning of a line */
490538032Speter		if (bp > buf)
490638032Speter		{
490738032Speter			if (TrafficLogFile != NULL)
490838032Speter			{
490938032Speter				for (xp = buf; xp < bp; xp++)
491090792Sgshapiro					(void) sm_io_putc(TrafficLogFile,
491190792Sgshapiro							  SM_TIME_DEFAULT,
491290792Sgshapiro							  (unsigned char) *xp);
491338032Speter			}
491438032Speter			for (xp = buf; xp < bp; xp++)
491538032Speter			{
491690792Sgshapiro				if (sm_io_putc(mci->mci_out, SM_TIME_DEFAULT,
491790792Sgshapiro					       (unsigned char) *xp)
491890792Sgshapiro				    == SM_IO_EOF)
491964562Sgshapiro				{
492090792Sgshapiro					dead = true;
492164562Sgshapiro					break;
492264562Sgshapiro				}
492371345Sgshapiro				else
492471345Sgshapiro				{
492571345Sgshapiro					/* record progress for DATA timeout */
492690792Sgshapiro					DataProgress = true;
492771345Sgshapiro				}
492838032Speter			}
492938032Speter			pos += bp - buf;
493038032Speter		}
493164562Sgshapiro		if (!dead && pos > 0)
493238032Speter		{
493338032Speter			if (TrafficLogFile != NULL)
493490792Sgshapiro				(void) sm_io_fputs(TrafficLogFile,
493590792Sgshapiro						   SM_TIME_DEFAULT,
493690792Sgshapiro						   mci->mci_mailer->m_eol);
493790792Sgshapiro			(void) sm_io_fputs(mci->mci_out, SM_TIME_DEFAULT,
493890792Sgshapiro					   mci->mci_mailer->m_eol);
493964562Sgshapiro
494064562Sgshapiro			/* record progress for DATA timeout */
494190792Sgshapiro			DataProgress = true;
494238032Speter		}
494338032Speter	}
494438032Speter
494590792Sgshapiro	if (sm_io_error(e->e_dfp))
494638032Speter	{
494790792Sgshapiro		syserr("putbody: %s/%cf%s: read error",
494890792Sgshapiro		       qid_printqueue(e->e_dfqgrp, e->e_dfqdir),
494990792Sgshapiro		       DATAFL_LETTER, e->e_id);
495038032Speter		ExitStat = EX_IOERR;
495138032Speter	}
495238032Speter
495338032Speterendofmessage:
495464562Sgshapiro	/*
495564562Sgshapiro	**  Since mailfile() uses e_dfp in a child process,
495664562Sgshapiro	**  the file offset in the stdio library for the
495764562Sgshapiro	**  parent process will not agree with the in-kernel
495864562Sgshapiro	**  file offset since the file descriptor is shared
495964562Sgshapiro	**  between the processes.  Therefore, it is vital
496064562Sgshapiro	**  that the file always be rewound.  This forces the
496164562Sgshapiro	**  kernel offset (lseek) and stdio library (ftell)
496264562Sgshapiro	**  offset to match.
496364562Sgshapiro	*/
496464562Sgshapiro
496564562Sgshapiro	if (e->e_dfp != NULL)
496664562Sgshapiro		(void) bfrewind(e->e_dfp);
496764562Sgshapiro
496838032Speter	/* some mailers want extra blank line at end of message */
496964562Sgshapiro	if (!dead && bitnset(M_BLANKEND, mci->mci_mailer->m_flags) &&
497038032Speter	    buf[0] != '\0' && buf[0] != '\n')
497138032Speter		putline("", mci);
497238032Speter
497390792Sgshapiro	(void) sm_io_flush(mci->mci_out, SM_TIME_DEFAULT);
497490792Sgshapiro	if (sm_io_error(mci->mci_out) && errno != EPIPE)
497538032Speter	{
497638032Speter		syserr("putbody: write error");
497738032Speter		ExitStat = EX_IOERR;
497838032Speter	}
497964562Sgshapiro
498038032Speter	errno = 0;
498138032Speter}
498290792Sgshapiro/*
498338032Speter**  MAILFILE -- Send a message to a file.
498438032Speter**
498590792Sgshapiro**	If the file has the set-user-ID/set-group-ID bits set, but NO
498690792Sgshapiro**	execute bits, sendmail will try to become the owner of that file
498738032Speter**	rather than the real user.  Obviously, this only works if
498838032Speter**	sendmail runs as root.
498938032Speter**
499038032Speter**	This could be done as a subordinate mailer, except that it
499138032Speter**	is used implicitly to save messages in ~/dead.letter.  We
499238032Speter**	view this as being sufficiently important as to include it
499338032Speter**	here.  For example, if the system is dying, we shouldn't have
499438032Speter**	to create another process plus some pipes to save the message.
499538032Speter**
499638032Speter**	Parameters:
499738032Speter**		filename -- the name of the file to send to.
499838032Speter**		mailer -- mailer definition for recipient -- if NULL,
499938032Speter**			use FileMailer.
500038032Speter**		ctladdr -- the controlling address header -- includes
500138032Speter**			the userid/groupid to be when sending.
500238032Speter**		sfflags -- flags for opening.
500338032Speter**		e -- the current envelope.
500438032Speter**
500538032Speter**	Returns:
500638032Speter**		The exit code associated with the operation.
500738032Speter**
500838032Speter**	Side Effects:
500938032Speter**		none.
501038032Speter*/
501138032Speter
501290792Sgshapiro# define RETURN(st)			exit(st);
501390792Sgshapiro
501438032Speterstatic jmp_buf	CtxMailfileTimeout;
501538032Speter
501638032Speterint
501738032Spetermailfile(filename, mailer, ctladdr, sfflags, e)
501838032Speter	char *volatile filename;
501938032Speter	MAILER *volatile mailer;
502038032Speter	ADDRESS *ctladdr;
502164562Sgshapiro	volatile long sfflags;
502238032Speter	register ENVELOPE *e;
502338032Speter{
502490792Sgshapiro	register SM_FILE_T *f;
502538032Speter	register pid_t pid = -1;
502664562Sgshapiro	volatile int mode;
502764562Sgshapiro	int len;
502864562Sgshapiro	off_t curoff;
502938032Speter	bool suidwarn = geteuid() == 0;
503038032Speter	char *p;
503164562Sgshapiro	char *volatile realfile;
503290792Sgshapiro	SM_EVENT *ev;
503398121Sgshapiro	char buf[MAXPATHLEN];
503498121Sgshapiro	char targetfile[MAXPATHLEN];
503538032Speter
503638032Speter	if (tTd(11, 1))
503738032Speter	{
503890792Sgshapiro		sm_dprintf("mailfile %s\n  ctladdr=", filename);
503990792Sgshapiro		printaddr(ctladdr, false);
504038032Speter	}
504138032Speter
504238032Speter	if (mailer == NULL)
504338032Speter		mailer = FileMailer;
504438032Speter
504538032Speter	if (e->e_xfp != NULL)
504690792Sgshapiro		(void) sm_io_flush(e->e_xfp, SM_TIME_DEFAULT);
504738032Speter
504838032Speter	/*
504938032Speter	**  Special case /dev/null.  This allows us to restrict file
505038032Speter	**  delivery to regular files only.
505138032Speter	*/
505238032Speter
505390792Sgshapiro	if (sm_path_isdevnull(filename))
505438032Speter		return EX_OK;
505538032Speter
505638032Speter	/* check for 8-bit available */
505738032Speter	if (bitset(EF_HAS8BIT, e->e_flags) &&
505838032Speter	    bitnset(M_7BITS, mailer->m_flags) &&
505938032Speter	    (bitset(EF_DONT_MIME, e->e_flags) ||
506038032Speter	     !(bitset(MM_MIME8BIT, MimeMode) ||
506138032Speter	       (bitset(EF_IS_MIME, e->e_flags) &&
506238032Speter		bitset(MM_CVTMIME, MimeMode)))))
506338032Speter	{
506438032Speter		e->e_status = "5.6.3";
506564562Sgshapiro		usrerrenh(e->e_status,
506690792Sgshapiro			  "554 Cannot send 8-bit data to 7-bit destination");
506790792Sgshapiro		errno = 0;
506864562Sgshapiro		return EX_DATAERR;
506938032Speter	}
507038032Speter
507164562Sgshapiro	/* Find the actual file */
507264562Sgshapiro	if (SafeFileEnv != NULL && SafeFileEnv[0] != '\0')
507364562Sgshapiro	{
507464562Sgshapiro		len = strlen(SafeFileEnv);
507564562Sgshapiro
507664562Sgshapiro		if (strncmp(SafeFileEnv, filename, len) == 0)
507764562Sgshapiro			filename += len;
507864562Sgshapiro
507990792Sgshapiro		if (len + strlen(filename) + 1 >= sizeof targetfile)
508064562Sgshapiro		{
508164562Sgshapiro			syserr("mailfile: filename too long (%s/%s)",
508264562Sgshapiro			       SafeFileEnv, filename);
508364562Sgshapiro			return EX_CANTCREAT;
508464562Sgshapiro		}
508590792Sgshapiro		(void) sm_strlcpy(targetfile, SafeFileEnv, sizeof targetfile);
508664562Sgshapiro		realfile = targetfile + len;
508764562Sgshapiro		if (*filename == '/')
508864562Sgshapiro			filename++;
508994334Sgshapiro		if (*filename != '\0')
509094334Sgshapiro		{
509194334Sgshapiro			/* paranoia: trailing / should be removed in readcf */
509294334Sgshapiro			if (targetfile[len - 1] != '/')
509394334Sgshapiro				(void) sm_strlcat(targetfile,
509494334Sgshapiro						  "/", sizeof targetfile);
509594334Sgshapiro			(void) sm_strlcat(targetfile, filename,
509694334Sgshapiro					  sizeof targetfile);
509794334Sgshapiro		}
509864562Sgshapiro	}
509964562Sgshapiro	else if (mailer->m_rootdir != NULL)
510064562Sgshapiro	{
510164562Sgshapiro		expand(mailer->m_rootdir, targetfile, sizeof targetfile, e);
510264562Sgshapiro		len = strlen(targetfile);
510364562Sgshapiro
510464562Sgshapiro		if (strncmp(targetfile, filename, len) == 0)
510564562Sgshapiro			filename += len;
510664562Sgshapiro
510790792Sgshapiro		if (len + strlen(filename) + 1 >= sizeof targetfile)
510864562Sgshapiro		{
510964562Sgshapiro			syserr("mailfile: filename too long (%s/%s)",
511064562Sgshapiro			       targetfile, filename);
511164562Sgshapiro			return EX_CANTCREAT;
511264562Sgshapiro		}
511364562Sgshapiro		realfile = targetfile + len;
511464562Sgshapiro		if (targetfile[len - 1] != '/')
511590792Sgshapiro			(void) sm_strlcat(targetfile, "/", sizeof targetfile);
511664562Sgshapiro		if (*filename == '/')
511790792Sgshapiro			(void) sm_strlcat(targetfile, filename + 1,
511890792Sgshapiro					  sizeof targetfile);
511964562Sgshapiro		else
512090792Sgshapiro			(void) sm_strlcat(targetfile, filename,
512190792Sgshapiro					  sizeof targetfile);
512264562Sgshapiro	}
512364562Sgshapiro	else
512464562Sgshapiro	{
512590792Sgshapiro		if (sm_strlcpy(targetfile, filename, sizeof targetfile) >=
512690792Sgshapiro		    sizeof targetfile)
512764562Sgshapiro		{
512864562Sgshapiro			syserr("mailfile: filename too long (%s)", filename);
512964562Sgshapiro			return EX_CANTCREAT;
513064562Sgshapiro		}
513164562Sgshapiro		realfile = targetfile;
513264562Sgshapiro	}
513364562Sgshapiro
513438032Speter	/*
513538032Speter	**  Fork so we can change permissions here.
513638032Speter	**	Note that we MUST use fork, not vfork, because of
513738032Speter	**	the complications of calling subroutines, etc.
513838032Speter	*/
513938032Speter
514094334Sgshapiro
514194334Sgshapiro	/*
514294334Sgshapiro	**  Dispose of SIGCHLD signal catchers that may be laying
514394334Sgshapiro	**  around so that the waitfor() below will get it.
514494334Sgshapiro	*/
514594334Sgshapiro
514694334Sgshapiro	(void) sm_signal(SIGCHLD, SIG_DFL);
514794334Sgshapiro
514838032Speter	DOFORK(fork);
514938032Speter
515038032Speter	if (pid < 0)
515164562Sgshapiro		return EX_OSERR;
515238032Speter	else if (pid == 0)
515338032Speter	{
515438032Speter		/* child -- actually write to file */
515538032Speter		struct stat stb;
515638032Speter		MCI mcibuf;
515742575Speter		int err;
515838032Speter		volatile int oflags = O_WRONLY|O_APPEND;
515938032Speter
516077349Sgshapiro		/* Reset global flags */
516177349Sgshapiro		RestartRequest = NULL;
516290792Sgshapiro		RestartWorkGroup = false;
516377349Sgshapiro		ShutdownRequest = NULL;
516477349Sgshapiro		PendingSignal = 0;
516590792Sgshapiro		CurrentPid = getpid();
516677349Sgshapiro
516738032Speter		if (e->e_lockfp != NULL)
516890792Sgshapiro			(void) close(sm_io_getinfo(e->e_lockfp, SM_IO_WHAT_FD,
516990792Sgshapiro				     NULL));
517038032Speter
517190792Sgshapiro		(void) sm_signal(SIGINT, SIG_DFL);
517290792Sgshapiro		(void) sm_signal(SIGHUP, SIG_DFL);
517390792Sgshapiro		(void) sm_signal(SIGTERM, SIG_DFL);
517438032Speter		(void) umask(OldUmask);
517538032Speter		e->e_to = filename;
517638032Speter		ExitStat = EX_OK;
517738032Speter
517838032Speter		if (setjmp(CtxMailfileTimeout) != 0)
517938032Speter		{
518090792Sgshapiro			RETURN(EX_TEMPFAIL);
518138032Speter		}
518238032Speter
518338032Speter		if (TimeOuts.to_fileopen > 0)
518490792Sgshapiro			ev = sm_setevent(TimeOuts.to_fileopen, mailfiletimeout,
518590792Sgshapiro					 0);
518638032Speter		else
518738032Speter			ev = NULL;
518838032Speter
518990792Sgshapiro		/* check file mode to see if set-user-ID */
519064562Sgshapiro		if (stat(targetfile, &stb) < 0)
519164562Sgshapiro			mode = FileMode;
519242575Speter		else
519338032Speter			mode = stb.st_mode;
519438032Speter
519538032Speter		/* limit the errors to those actually caused in the child */
519638032Speter		errno = 0;
519738032Speter		ExitStat = EX_OK;
519838032Speter
519964562Sgshapiro		/* Allow alias expansions to use the S_IS{U,G}ID bits */
520064562Sgshapiro		if ((ctladdr != NULL && !bitset(QALIAS, ctladdr->q_flags)) ||
520164562Sgshapiro		    bitset(SFF_RUNASREALUID, sfflags))
520238032Speter		{
520390792Sgshapiro			/* ignore set-user-ID and set-group-ID bits */
520438032Speter			mode &= ~(S_ISGID|S_ISUID);
520564562Sgshapiro			if (tTd(11, 20))
520690792Sgshapiro				sm_dprintf("mailfile: ignoring set-user-ID/set-group-ID bits\n");
520738032Speter		}
520838032Speter
520990792Sgshapiro		/* we have to open the data file BEFORE setuid() */
521038032Speter		if (e->e_dfp == NULL && bitset(EF_HAS_DF, e->e_flags))
521138032Speter		{
521290792Sgshapiro			char *df = queuename(e, DATAFL_LETTER);
521338032Speter
521490792Sgshapiro			e->e_dfp = sm_io_open(SmFtStdio, SM_TIME_DEFAULT, df,
5215120256Sgshapiro					      SM_IO_RDONLY_B, NULL);
521638032Speter			if (e->e_dfp == NULL)
521738032Speter			{
521838032Speter				syserr("mailfile: Cannot open %s for %s from %s",
521938032Speter					df, e->e_to, e->e_from.q_paddr);
522038032Speter			}
522138032Speter		}
522238032Speter
522338032Speter		/* select a new user to run as */
522438032Speter		if (!bitset(SFF_RUNASREALUID, sfflags))
522538032Speter		{
522638032Speter			if (bitnset(M_SPECIFIC_UID, mailer->m_flags))
522738032Speter			{
522838032Speter				RealUserName = NULL;
522938032Speter				RealUid = mailer->m_uid;
523064562Sgshapiro				if (RunAsUid != 0 && RealUid != RunAsUid)
523164562Sgshapiro				{
523264562Sgshapiro					/* Only root can change the uid */
523390792Sgshapiro					syserr("mailfile: insufficient privileges to change uid, RunAsUid=%d, RealUid=%d",
523490792Sgshapiro						(int) RunAsUid, (int) RealUid);
523590792Sgshapiro					RETURN(EX_TEMPFAIL);
523664562Sgshapiro				}
523738032Speter			}
523838032Speter			else if (bitset(S_ISUID, mode))
523938032Speter			{
524038032Speter				RealUserName = NULL;
524138032Speter				RealUid = stb.st_uid;
524238032Speter			}
524338032Speter			else if (ctladdr != NULL && ctladdr->q_uid != 0)
524438032Speter			{
524538032Speter				if (ctladdr->q_ruser != NULL)
524638032Speter					RealUserName = ctladdr->q_ruser;
524738032Speter				else
524838032Speter					RealUserName = ctladdr->q_user;
524938032Speter				RealUid = ctladdr->q_uid;
525038032Speter			}
525138032Speter			else if (mailer != NULL && mailer->m_uid != 0)
525238032Speter			{
525338032Speter				RealUserName = DefUser;
525438032Speter				RealUid = mailer->m_uid;
525538032Speter			}
525638032Speter			else
525738032Speter			{
525838032Speter				RealUserName = DefUser;
525938032Speter				RealUid = DefUid;
526038032Speter			}
526138032Speter
526238032Speter			/* select a new group to run as */
526338032Speter			if (bitnset(M_SPECIFIC_UID, mailer->m_flags))
526464562Sgshapiro			{
526538032Speter				RealGid = mailer->m_gid;
526664562Sgshapiro				if (RunAsUid != 0 &&
526764562Sgshapiro				    (RealGid != getgid() ||
526864562Sgshapiro				     RealGid != getegid()))
526964562Sgshapiro				{
527064562Sgshapiro					/* Only root can change the gid */
527190792Sgshapiro					syserr("mailfile: insufficient privileges to change gid, RealGid=%d, RunAsUid=%d, gid=%d, egid=%d",
527290792Sgshapiro					       (int) RealGid, (int) RunAsUid,
527390792Sgshapiro					       (int) getgid(), (int) getegid());
527490792Sgshapiro					RETURN(EX_TEMPFAIL);
527564562Sgshapiro				}
527664562Sgshapiro			}
527738032Speter			else if (bitset(S_ISGID, mode))
527838032Speter				RealGid = stb.st_gid;
527964562Sgshapiro			else if (ctladdr != NULL &&
528064562Sgshapiro				 ctladdr->q_uid == DefUid &&
528164562Sgshapiro				 ctladdr->q_gid == 0)
528271345Sgshapiro			{
528371345Sgshapiro				/*
528471345Sgshapiro				**  Special case:  This means it is an
528571345Sgshapiro				**  alias and we should act as DefaultUser.
528671345Sgshapiro				**  See alias()'s comments.
528771345Sgshapiro				*/
528871345Sgshapiro
528964562Sgshapiro				RealGid = DefGid;
529071345Sgshapiro				RealUserName = DefUser;
529171345Sgshapiro			}
529271345Sgshapiro			else if (ctladdr != NULL && ctladdr->q_uid != 0)
529371345Sgshapiro				RealGid = ctladdr->q_gid;
529438032Speter			else if (mailer != NULL && mailer->m_gid != 0)
529538032Speter				RealGid = mailer->m_gid;
529638032Speter			else
529738032Speter				RealGid = DefGid;
529838032Speter		}
529938032Speter
530038032Speter		/* last ditch */
530138032Speter		if (!bitset(SFF_ROOTOK, sfflags))
530238032Speter		{
530338032Speter			if (RealUid == 0)
530438032Speter				RealUid = DefUid;
530538032Speter			if (RealGid == 0)
530638032Speter				RealGid = DefGid;
530738032Speter		}
530838032Speter
530938032Speter		/* set group id list (needs /etc/group access) */
531038032Speter		if (RealUserName != NULL && !DontInitGroups)
531138032Speter		{
531238032Speter			if (initgroups(RealUserName, RealGid) == -1 && suidwarn)
531364562Sgshapiro			{
531438032Speter				syserr("mailfile: initgroups(%s, %d) failed",
531538032Speter					RealUserName, RealGid);
531690792Sgshapiro				RETURN(EX_TEMPFAIL);
531764562Sgshapiro			}
531838032Speter		}
531938032Speter		else
532038032Speter		{
532138032Speter			GIDSET_T gidset[1];
532238032Speter
532338032Speter			gidset[0] = RealGid;
532438032Speter			if (setgroups(1, gidset) == -1 && suidwarn)
532564562Sgshapiro			{
532638032Speter				syserr("mailfile: setgroups() failed");
532790792Sgshapiro				RETURN(EX_TEMPFAIL);
532864562Sgshapiro			}
532938032Speter		}
533038032Speter
533164562Sgshapiro		/*
533264562Sgshapiro		**  If you have a safe environment, go into it.
533364562Sgshapiro		*/
533464562Sgshapiro
533564562Sgshapiro		if (realfile != targetfile)
533638032Speter		{
533794334Sgshapiro			char save;
533894334Sgshapiro
533994334Sgshapiro			save = *realfile;
534064562Sgshapiro			*realfile = '\0';
534164562Sgshapiro			if (tTd(11, 20))
534290792Sgshapiro				sm_dprintf("mailfile: chroot %s\n", targetfile);
534364562Sgshapiro			if (chroot(targetfile) < 0)
534438032Speter			{
534538032Speter				syserr("mailfile: Cannot chroot(%s)",
534664562Sgshapiro				       targetfile);
534790792Sgshapiro				RETURN(EX_CANTCREAT);
534838032Speter			}
534994334Sgshapiro			*realfile = save;
535038032Speter		}
535164562Sgshapiro
535264562Sgshapiro		if (tTd(11, 40))
535390792Sgshapiro			sm_dprintf("mailfile: deliver to %s\n", realfile);
535464562Sgshapiro
535538032Speter		if (chdir("/") < 0)
535664562Sgshapiro		{
535738032Speter			syserr("mailfile: cannot chdir(/)");
535890792Sgshapiro			RETURN(EX_CANTCREAT);
535964562Sgshapiro		}
536038032Speter
536138032Speter		/* now reset the group and user ids */
536238032Speter		endpwent();
536390792Sgshapiro		sm_mbdb_terminate();
536438032Speter		if (setgid(RealGid) < 0 && suidwarn)
536564562Sgshapiro		{
536638032Speter			syserr("mailfile: setgid(%ld) failed", (long) RealGid);
536790792Sgshapiro			RETURN(EX_TEMPFAIL);
536864562Sgshapiro		}
536938032Speter		vendor_set_uid(RealUid);
537038032Speter		if (setuid(RealUid) < 0 && suidwarn)
537164562Sgshapiro		{
537238032Speter			syserr("mailfile: setuid(%ld) failed", (long) RealUid);
537390792Sgshapiro			RETURN(EX_TEMPFAIL);
537464562Sgshapiro		}
537538032Speter
537664562Sgshapiro		if (tTd(11, 2))
537790792Sgshapiro			sm_dprintf("mailfile: running as r/euid=%d/%d, r/egid=%d/%d\n",
537864562Sgshapiro				(int) getuid(), (int) geteuid(),
537964562Sgshapiro				(int) getgid(), (int) getegid());
538064562Sgshapiro
538164562Sgshapiro
538238032Speter		/* move into some "safe" directory */
538338032Speter		if (mailer->m_execdir != NULL)
538438032Speter		{
538538032Speter			char *q;
538638032Speter
538738032Speter			for (p = mailer->m_execdir; p != NULL; p = q)
538838032Speter			{
538938032Speter				q = strchr(p, ':');
539038032Speter				if (q != NULL)
539138032Speter					*q = '\0';
539238032Speter				expand(p, buf, sizeof buf, e);
539338032Speter				if (q != NULL)
539438032Speter					*q++ = ':';
539538032Speter				if (tTd(11, 20))
539690792Sgshapiro					sm_dprintf("mailfile: trydir %s\n",
539790792Sgshapiro						   buf);
539838032Speter				if (buf[0] != '\0' && chdir(buf) >= 0)
539938032Speter					break;
540038032Speter			}
540138032Speter		}
540238032Speter
540364562Sgshapiro		/*
540464562Sgshapiro		**  Recheck the file after we have assumed the ID of the
540564562Sgshapiro		**  delivery user to make sure we can deliver to it as
540664562Sgshapiro		**  that user.  This is necessary if sendmail is running
540764562Sgshapiro		**  as root and the file is on an NFS mount which treats
540864562Sgshapiro		**  root as nobody.
540964562Sgshapiro		*/
541064562Sgshapiro
541164562Sgshapiro#if HASLSTAT
541264562Sgshapiro		if (bitnset(DBS_FILEDELIVERYTOSYMLINK, DontBlameSendmail))
541364562Sgshapiro			err = stat(realfile, &stb);
541464562Sgshapiro		else
541564562Sgshapiro			err = lstat(realfile, &stb);
541664562Sgshapiro#else /* HASLSTAT */
541764562Sgshapiro		err = stat(realfile, &stb);
541864562Sgshapiro#endif /* HASLSTAT */
541964562Sgshapiro
542064562Sgshapiro		if (err < 0)
542164562Sgshapiro		{
542264562Sgshapiro			stb.st_mode = ST_MODE_NOFILE;
542364562Sgshapiro			mode = FileMode;
542464562Sgshapiro			oflags |= O_CREAT|O_EXCL;
542564562Sgshapiro		}
542664562Sgshapiro		else if (bitset(S_IXUSR|S_IXGRP|S_IXOTH, mode) ||
542764562Sgshapiro			 (!bitnset(DBS_FILEDELIVERYTOHARDLINK,
542864562Sgshapiro				   DontBlameSendmail) &&
542964562Sgshapiro			  stb.st_nlink != 1) ||
543064562Sgshapiro			 (realfile != targetfile && !S_ISREG(mode)))
543164562Sgshapiro			exit(EX_CANTCREAT);
543264562Sgshapiro		else
543364562Sgshapiro			mode = stb.st_mode;
543464562Sgshapiro
543564562Sgshapiro		if (!bitnset(DBS_FILEDELIVERYTOSYMLINK, DontBlameSendmail))
543638032Speter			sfflags |= SFF_NOSLINK;
543764562Sgshapiro		if (!bitnset(DBS_FILEDELIVERYTOHARDLINK, DontBlameSendmail))
543838032Speter			sfflags |= SFF_NOHLINK;
543938032Speter		sfflags &= ~SFF_OPENASROOT;
544064562Sgshapiro		f = safefopen(realfile, oflags, mode, sfflags);
544138032Speter		if (f == NULL)
544238032Speter		{
544364562Sgshapiro			if (transienterror(errno))
544464562Sgshapiro			{
544564562Sgshapiro				usrerr("454 4.3.0 cannot open %s: %s",
544664562Sgshapiro				       shortenstring(realfile, MAXSHORTSTR),
544790792Sgshapiro				       sm_errstring(errno));
544890792Sgshapiro				RETURN(EX_TEMPFAIL);
544964562Sgshapiro			}
545064562Sgshapiro			else
545164562Sgshapiro			{
545264562Sgshapiro				usrerr("554 5.3.0 cannot open %s: %s",
545364562Sgshapiro				       shortenstring(realfile, MAXSHORTSTR),
545490792Sgshapiro				       sm_errstring(errno));
545590792Sgshapiro				RETURN(EX_CANTCREAT);
545664562Sgshapiro			}
545738032Speter		}
545890792Sgshapiro		if (filechanged(realfile, sm_io_getinfo(f, SM_IO_WHAT_FD, NULL),
545990792Sgshapiro		    &stb))
546038032Speter		{
546164562Sgshapiro			syserr("554 5.3.0 file changed after open");
546290792Sgshapiro			RETURN(EX_CANTCREAT);
546338032Speter		}
546490792Sgshapiro		if (fstat(sm_io_getinfo(f, SM_IO_WHAT_FD, NULL), &stb) < 0)
546538032Speter		{
546690792Sgshapiro			syserr("554 5.3.0 cannot fstat %s",
546790792Sgshapiro				sm_errstring(errno));
546890792Sgshapiro			RETURN(EX_CANTCREAT);
546938032Speter		}
547038032Speter
547164562Sgshapiro		curoff = stb.st_size;
547264562Sgshapiro
547338032Speter		if (ev != NULL)
547490792Sgshapiro			sm_clrevent(ev);
547538032Speter
547664562Sgshapiro		memset(&mcibuf, '\0', sizeof mcibuf);
547738032Speter		mcibuf.mci_mailer = mailer;
547838032Speter		mcibuf.mci_out = f;
547938032Speter		if (bitnset(M_7BITS, mailer->m_flags))
548038032Speter			mcibuf.mci_flags |= MCIF_7BIT;
548138032Speter
548238032Speter		/* clear out per-message flags from connection structure */
548338032Speter		mcibuf.mci_flags &= ~(MCIF_CVT7TO8|MCIF_CVT8TO7);
548438032Speter
548538032Speter		if (bitset(EF_HAS8BIT, e->e_flags) &&
548638032Speter		    !bitset(EF_DONT_MIME, e->e_flags) &&
548738032Speter		    bitnset(M_7BITS, mailer->m_flags))
548838032Speter			mcibuf.mci_flags |= MCIF_CVT8TO7;
548938032Speter
549038032Speter#if MIME7TO8
549138032Speter		if (bitnset(M_MAKE8BIT, mailer->m_flags) &&
549238032Speter		    !bitset(MCIF_7BIT, mcibuf.mci_flags) &&
549338032Speter		    (p = hvalue("Content-Transfer-Encoding", e->e_header)) != NULL &&
549490792Sgshapiro		    (sm_strcasecmp(p, "quoted-printable") == 0 ||
549590792Sgshapiro		     sm_strcasecmp(p, "base64") == 0) &&
549638032Speter		    (p = hvalue("Content-Type", e->e_header)) != NULL)
549738032Speter		{
549838032Speter			/* may want to convert 7 -> 8 */
549938032Speter			/* XXX should really parse it here -- and use a class XXX */
550090792Sgshapiro			if (sm_strncasecmp(p, "text/plain", 10) == 0 &&
550164562Sgshapiro			    (p[10] == '\0' || p[10] == ' ' || p[10] == ';'))
550238032Speter				mcibuf.mci_flags |= MCIF_CVT7TO8;
550338032Speter		}
550464562Sgshapiro#endif /* MIME7TO8 */
550538032Speter
550638032Speter		putfromline(&mcibuf, e);
550743730Speter		(*e->e_puthdr)(&mcibuf, e->e_header, e, M87F_OUTER);
550838032Speter		(*e->e_putbody)(&mcibuf, e, NULL);
550938032Speter		putline("\n", &mcibuf);
551090792Sgshapiro		if (sm_io_flush(f, SM_TIME_DEFAULT) != 0 ||
551190792Sgshapiro		    (SuperSafe != SAFE_NO &&
551290792Sgshapiro		     fsync(sm_io_getinfo(f, SM_IO_WHAT_FD, NULL)) < 0) ||
551390792Sgshapiro		    sm_io_error(f))
551438032Speter		{
551538032Speter			setstat(EX_IOERR);
551664562Sgshapiro#if !NOFTRUNCATE
551790792Sgshapiro			(void) ftruncate(sm_io_getinfo(f, SM_IO_WHAT_FD, NULL),
551890792Sgshapiro					 curoff);
551964562Sgshapiro#endif /* !NOFTRUNCATE */
552038032Speter		}
552138032Speter
552238032Speter		/* reset ISUID & ISGID bits for paranoid systems */
552338032Speter#if HASFCHMOD
552490792Sgshapiro		(void) fchmod(sm_io_getinfo(f, SM_IO_WHAT_FD, NULL),
552590792Sgshapiro			      (MODE_T) mode);
552664562Sgshapiro#else /* HASFCHMOD */
552764562Sgshapiro		(void) chmod(filename, (MODE_T) mode);
552864562Sgshapiro#endif /* HASFCHMOD */
552990792Sgshapiro		if (sm_io_close(f, SM_TIME_DEFAULT) < 0)
553064562Sgshapiro			setstat(EX_IOERR);
553190792Sgshapiro		(void) sm_io_flush(smioout, SM_TIME_DEFAULT);
553264562Sgshapiro		(void) setuid(RealUid);
553338032Speter		exit(ExitStat);
553464562Sgshapiro		/* NOTREACHED */
553538032Speter	}
553638032Speter	else
553738032Speter	{
553838032Speter		/* parent -- wait for exit status */
553938032Speter		int st;
554038032Speter
554138032Speter		st = waitfor(pid);
554238032Speter		if (st == -1)
554338032Speter		{
554438032Speter			syserr("mailfile: %s: wait", mailer->m_name);
554564562Sgshapiro			return EX_SOFTWARE;
554638032Speter		}
554738032Speter		if (WIFEXITED(st))
554890792Sgshapiro		{
554990792Sgshapiro			errno = 0;
555038032Speter			return (WEXITSTATUS(st));
555190792Sgshapiro		}
555238032Speter		else
555338032Speter		{
555438032Speter			syserr("mailfile: %s: child died on signal %d",
555538032Speter			       mailer->m_name, st);
555664562Sgshapiro			return EX_UNAVAILABLE;
555738032Speter		}
555864562Sgshapiro		/* NOTREACHED */
555938032Speter	}
556038032Speter	return EX_UNAVAILABLE;	/* avoid compiler warning on IRIX */
556138032Speter}
556238032Speter
556338032Speterstatic void
556438032Spetermailfiletimeout()
556538032Speter{
556677349Sgshapiro	/*
556777349Sgshapiro	**  NOTE: THIS CAN BE CALLED FROM A SIGNAL HANDLER.  DO NOT ADD
556877349Sgshapiro	**	ANYTHING TO THIS ROUTINE UNLESS YOU KNOW WHAT YOU ARE
556977349Sgshapiro	**	DOING.
557077349Sgshapiro	*/
557177349Sgshapiro
557277349Sgshapiro	errno = ETIMEDOUT;
557338032Speter	longjmp(CtxMailfileTimeout, 1);
557438032Speter}
557590792Sgshapiro/*
557638032Speter**  HOSTSIGNATURE -- return the "signature" for a host.
557738032Speter**
557838032Speter**	The signature describes how we are going to send this -- it
557938032Speter**	can be just the hostname (for non-Internet hosts) or can be
558038032Speter**	an ordered list of MX hosts.
558138032Speter**
558238032Speter**	Parameters:
558338032Speter**		m -- the mailer describing this host.
558438032Speter**		host -- the host name.
558538032Speter**
558638032Speter**	Returns:
558738032Speter**		The signature for this host.
558838032Speter**
558938032Speter**	Side Effects:
559038032Speter**		Can tweak the symbol table.
559138032Speter*/
559290792Sgshapiro
559364562Sgshapiro#define MAXHOSTSIGNATURE	8192	/* max len of hostsignature */
559438032Speter
559590792Sgshapirochar *
559664562Sgshapirohostsignature(m, host)
559738032Speter	register MAILER *m;
559838032Speter	char *host;
559938032Speter{
560038032Speter	register char *p;
560138032Speter	register STAB *s;
560290792Sgshapiro	time_t now;
560364562Sgshapiro#if NAMED_BIND
560464562Sgshapiro	char sep = ':';
560564562Sgshapiro	char prevsep = ':';
560638032Speter	int i;
560738032Speter	int len;
560838032Speter	int nmx;
560964562Sgshapiro	int hl;
561038032Speter	char *hp;
561138032Speter	char *endp;
561238032Speter	int oldoptions = _res.options;
561338032Speter	char *mxhosts[MAXMXHOSTS + 1];
561490792Sgshapiro	unsigned short mxprefs[MAXMXHOSTS + 1];
561564562Sgshapiro#endif /* NAMED_BIND */
561638032Speter
561764562Sgshapiro	if (tTd(17, 3))
561890792Sgshapiro		sm_dprintf("hostsignature(%s)\n", host);
561964562Sgshapiro
562038032Speter	/*
562177349Sgshapiro	**  If local delivery (and not remote), just return a constant.
562264562Sgshapiro	*/
562364562Sgshapiro
562477349Sgshapiro	if (bitnset(M_LOCALMAILER, m->m_flags) &&
562590792Sgshapiro	    strcmp(m->m_mailer, "[IPC]") != 0 &&
562690792Sgshapiro	    !(m->m_argv[0] != NULL && strcmp(m->m_argv[0], "TCP") == 0))
562764562Sgshapiro		return "localhost";
562864562Sgshapiro
562964562Sgshapiro	/*
563038032Speter	**  Check to see if this uses IPC -- if not, it can't have MX records.
563138032Speter	*/
563238032Speter
563390792Sgshapiro	if (strcmp(m->m_mailer, "[IPC]") != 0 ||
563490792Sgshapiro	    CurEnv->e_sendmode == SM_DEFER)
563538032Speter	{
563690792Sgshapiro		/* just an ordinary mailer or deferred mode */
563738032Speter		return host;
563838032Speter	}
563964562Sgshapiro#if NETUNIX
564064562Sgshapiro	else if (m->m_argv[0] != NULL &&
564164562Sgshapiro		 strcmp(m->m_argv[0], "FILE") == 0)
564264562Sgshapiro	{
564364562Sgshapiro		/* rendezvous in the file system, no MX records */
564464562Sgshapiro		return host;
564564562Sgshapiro	}
564664562Sgshapiro#endif /* NETUNIX */
564738032Speter
564838032Speter	/*
564938032Speter	**  Look it up in the symbol table.
565038032Speter	*/
565138032Speter
565290792Sgshapiro	now = curtime();
565338032Speter	s = stab(host, ST_HOSTSIG, ST_ENTER);
565490792Sgshapiro	if (s->s_hostsig.hs_sig != NULL)
565564562Sgshapiro	{
565690792Sgshapiro		if (s->s_hostsig.hs_exp >= now)
565790792Sgshapiro		{
565890792Sgshapiro			if (tTd(17, 3))
565990792Sgshapiro				sm_dprintf("hostsignature(): stab(%s) found %s\n", host,
566090792Sgshapiro					   s->s_hostsig.hs_sig);
566190792Sgshapiro			return s->s_hostsig.hs_sig;
566290792Sgshapiro		}
566390792Sgshapiro
566490792Sgshapiro		/* signature is expired: clear it */
566590792Sgshapiro		sm_free(s->s_hostsig.hs_sig);
566690792Sgshapiro		s->s_hostsig.hs_sig = NULL;
566764562Sgshapiro	}
566838032Speter
566990792Sgshapiro	/* set default TTL */
567090792Sgshapiro	s->s_hostsig.hs_exp = now + SM_DEFAULT_TTL;
567190792Sgshapiro
567238032Speter	/*
567390792Sgshapiro	**  Not already there or expired -- create a signature.
567438032Speter	*/
567538032Speter
567638032Speter#if NAMED_BIND
567738032Speter	if (ConfigLevel < 2)
567838032Speter		_res.options &= ~(RES_DEFNAMES | RES_DNSRCH);	/* XXX */
567938032Speter
568038032Speter	for (hp = host; hp != NULL; hp = endp)
568138032Speter	{
568264562Sgshapiro#if NETINET6
568364562Sgshapiro		if (*hp == '[')
568464562Sgshapiro		{
568564562Sgshapiro			endp = strchr(hp + 1, ']');
568664562Sgshapiro			if (endp != NULL)
568764562Sgshapiro				endp = strpbrk(endp + 1, ":,");
568864562Sgshapiro		}
568964562Sgshapiro		else
569064562Sgshapiro			endp = strpbrk(hp, ":,");
569164562Sgshapiro#else /* NETINET6 */
569264562Sgshapiro		endp = strpbrk(hp, ":,");
569364562Sgshapiro#endif /* NETINET6 */
569438032Speter		if (endp != NULL)
569564562Sgshapiro		{
569664562Sgshapiro			sep = *endp;
569738032Speter			*endp = '\0';
569864562Sgshapiro		}
569938032Speter
570038032Speter		if (bitnset(M_NOMX, m->m_flags))
570138032Speter		{
570238032Speter			/* skip MX lookups */
570338032Speter			nmx = 1;
570438032Speter			mxhosts[0] = hp;
570538032Speter		}
570638032Speter		else
570738032Speter		{
570838032Speter			auto int rcode;
570990792Sgshapiro			int ttl;
571038032Speter
571190792Sgshapiro			nmx = getmxrr(hp, mxhosts, mxprefs, true, &rcode, true,
571290792Sgshapiro				      &ttl);
571338032Speter			if (nmx <= 0)
571438032Speter			{
571580785Sgshapiro				int save_errno;
571638032Speter				register MCI *mci;
571738032Speter
571838032Speter				/* update the connection info for this host */
571980785Sgshapiro				save_errno = errno;
572038032Speter				mci = mci_get(hp, m);
572180785Sgshapiro				mci->mci_errno = save_errno;
572238032Speter				mci->mci_herrno = h_errno;
572371345Sgshapiro				mci->mci_lastuse = now;
572464562Sgshapiro				if (rcode == EX_NOHOST)
572564562Sgshapiro					mci_setstat(mci, rcode, "5.1.2",
572690792Sgshapiro						    "550 Host unknown");
572764562Sgshapiro				else
572864562Sgshapiro					mci_setstat(mci, rcode, NULL, NULL);
572938032Speter
573038032Speter				/* use the original host name as signature */
573138032Speter				nmx = 1;
573238032Speter				mxhosts[0] = hp;
573338032Speter			}
573464562Sgshapiro			if (tTd(17, 3))
573590792Sgshapiro				sm_dprintf("hostsignature(): getmxrr() returned %d, mxhosts[0]=%s\n",
573690792Sgshapiro					   nmx, mxhosts[0]);
573790792Sgshapiro
573890792Sgshapiro			/*
573990792Sgshapiro			**  Set new TTL: we use only one!
574090792Sgshapiro			**	We could try to use the minimum instead.
574190792Sgshapiro			*/
574290792Sgshapiro
574390792Sgshapiro			s->s_hostsig.hs_exp = now + SM_MIN(ttl, SM_DEFAULT_TTL);
574438032Speter		}
574538032Speter
574638032Speter		len = 0;
574738032Speter		for (i = 0; i < nmx; i++)
574838032Speter			len += strlen(mxhosts[i]) + 1;
574990792Sgshapiro		if (s->s_hostsig.hs_sig != NULL)
575090792Sgshapiro			len += strlen(s->s_hostsig.hs_sig) + 1;
575190792Sgshapiro		if (len < 0 || len >= MAXHOSTSIGNATURE)
575264562Sgshapiro		{
575364562Sgshapiro			sm_syslog(LOG_WARNING, NOQID, "hostsignature for host '%s' exceeds maxlen (%d): %d",
575464562Sgshapiro				  host, MAXHOSTSIGNATURE, len);
575564562Sgshapiro			len = MAXHOSTSIGNATURE;
575664562Sgshapiro		}
575790792Sgshapiro		p = sm_pmalloc_x(len);
575890792Sgshapiro		if (s->s_hostsig.hs_sig != NULL)
575938032Speter		{
576090792Sgshapiro			(void) sm_strlcpy(p, s->s_hostsig.hs_sig, len);
576190792Sgshapiro			sm_free(s->s_hostsig.hs_sig); /* XXX */
576290792Sgshapiro			s->s_hostsig.hs_sig = p;
576364562Sgshapiro			hl = strlen(p);
576464562Sgshapiro			p += hl;
576564562Sgshapiro			*p++ = prevsep;
576664562Sgshapiro			len -= hl + 1;
576738032Speter		}
576838032Speter		else
576990792Sgshapiro			s->s_hostsig.hs_sig = p;
577038032Speter		for (i = 0; i < nmx; i++)
577138032Speter		{
577264562Sgshapiro			hl = strlen(mxhosts[i]);
577364562Sgshapiro			if (len - 1 < hl || len <= 1)
577464562Sgshapiro			{
577564562Sgshapiro				/* force to drop out of outer loop */
577664562Sgshapiro				len = -1;
577764562Sgshapiro				break;
577864562Sgshapiro			}
577938032Speter			if (i != 0)
578064562Sgshapiro			{
578164562Sgshapiro				if (mxprefs[i] == mxprefs[i - 1])
578264562Sgshapiro					*p++ = ',';
578364562Sgshapiro				else
578464562Sgshapiro					*p++ = ':';
578564562Sgshapiro				len--;
578664562Sgshapiro			}
578790792Sgshapiro			(void) sm_strlcpy(p, mxhosts[i], len);
578864562Sgshapiro			p += hl;
578964562Sgshapiro			len -= hl;
579038032Speter		}
579164562Sgshapiro
579264562Sgshapiro		/*
579364562Sgshapiro		**  break out of loop if len exceeded MAXHOSTSIGNATURE
579464562Sgshapiro		**  because we won't have more space for further hosts
579564562Sgshapiro		**  anyway (separated by : in the .cf file).
579664562Sgshapiro		*/
579764562Sgshapiro
579864562Sgshapiro		if (len < 0)
579964562Sgshapiro			break;
580038032Speter		if (endp != NULL)
580164562Sgshapiro			*endp++ = sep;
580264562Sgshapiro		prevsep = sep;
580338032Speter	}
580490792Sgshapiro	makelower(s->s_hostsig.hs_sig);
580538032Speter	if (ConfigLevel < 2)
580638032Speter		_res.options = oldoptions;
580764562Sgshapiro#else /* NAMED_BIND */
580838032Speter	/* not using BIND -- the signature is just the host name */
580990792Sgshapiro	/*
581090792Sgshapiro	**  'host' points to storage that will be freed after we are
581190792Sgshapiro	**  done processing the current envelope, so we copy it.
581290792Sgshapiro	*/
581390792Sgshapiro	s->s_hostsig.hs_sig = sm_pstrdup_x(host);
581464562Sgshapiro#endif /* NAMED_BIND */
581538032Speter	if (tTd(17, 1))
581690792Sgshapiro		sm_dprintf("hostsignature(%s) = %s\n", host, s->s_hostsig.hs_sig);
581790792Sgshapiro	return s->s_hostsig.hs_sig;
581838032Speter}
581990792Sgshapiro/*
582064562Sgshapiro**  PARSE_HOSTSIGNATURE -- parse the "signature" and return MX host array.
582164562Sgshapiro**
582264562Sgshapiro**	The signature describes how we are going to send this -- it
582364562Sgshapiro**	can be just the hostname (for non-Internet hosts) or can be
582464562Sgshapiro**	an ordered list of MX hosts which must be randomized for equal
582564562Sgshapiro**	MX preference values.
582664562Sgshapiro**
582764562Sgshapiro**	Parameters:
582864562Sgshapiro**		sig -- the host signature.
582964562Sgshapiro**		mxhosts -- array to populate.
583090792Sgshapiro**		mailer -- mailer.
583164562Sgshapiro**
583264562Sgshapiro**	Returns:
583364562Sgshapiro**		The number of hosts inserted into mxhosts array.
583464562Sgshapiro**
583564562Sgshapiro**	Side Effects:
583664562Sgshapiro**		Randomizes equal MX preference hosts in mxhosts.
583764562Sgshapiro*/
583864562Sgshapiro
583964562Sgshapirostatic int
584064562Sgshapiroparse_hostsignature(sig, mxhosts, mailer)
584164562Sgshapiro	char *sig;
584264562Sgshapiro	char **mxhosts;
584364562Sgshapiro	MAILER *mailer;
584464562Sgshapiro{
584590792Sgshapiro	unsigned short curpref = 0;
584690792Sgshapiro	int nmx = 0, i, j;	/* NOTE: i, j, and nmx must have same type */
584764562Sgshapiro	char *hp, *endp;
584890792Sgshapiro	unsigned short prefer[MAXMXHOSTS];
584964562Sgshapiro	long rndm[MAXMXHOSTS];
585064562Sgshapiro
585164562Sgshapiro	for (hp = sig; hp != NULL; hp = endp)
585264562Sgshapiro	{
585364562Sgshapiro		char sep = ':';
585464562Sgshapiro
585564562Sgshapiro#if NETINET6
585664562Sgshapiro		if (*hp == '[')
585764562Sgshapiro		{
585864562Sgshapiro			endp = strchr(hp + 1, ']');
585964562Sgshapiro			if (endp != NULL)
586064562Sgshapiro				endp = strpbrk(endp + 1, ":,");
586164562Sgshapiro		}
586264562Sgshapiro		else
586364562Sgshapiro			endp = strpbrk(hp, ":,");
586464562Sgshapiro#else /* NETINET6 */
586564562Sgshapiro		endp = strpbrk(hp, ":,");
586664562Sgshapiro#endif /* NETINET6 */
586764562Sgshapiro		if (endp != NULL)
586864562Sgshapiro		{
586964562Sgshapiro			sep = *endp;
587064562Sgshapiro			*endp = '\0';
587164562Sgshapiro		}
587264562Sgshapiro
587364562Sgshapiro		mxhosts[nmx] = hp;
587464562Sgshapiro		prefer[nmx] = curpref;
587564562Sgshapiro		if (mci_match(hp, mailer))
587664562Sgshapiro			rndm[nmx] = 0;
587764562Sgshapiro		else
587864562Sgshapiro			rndm[nmx] = get_random();
587964562Sgshapiro
588064562Sgshapiro		if (endp != NULL)
588164562Sgshapiro		{
588264562Sgshapiro			/*
588364562Sgshapiro			**  Since we don't have the original MX prefs,
588464562Sgshapiro			**  make our own.  If the separator is a ':', that
588564562Sgshapiro			**  means the preference for the next host will be
588664562Sgshapiro			**  higher than this one, so simply increment curpref.
588764562Sgshapiro			*/
588864562Sgshapiro
588964562Sgshapiro			if (sep == ':')
589064562Sgshapiro				curpref++;
589164562Sgshapiro
589264562Sgshapiro			*endp++ = sep;
589364562Sgshapiro		}
589464562Sgshapiro		if (++nmx >= MAXMXHOSTS)
589564562Sgshapiro			break;
589664562Sgshapiro	}
589764562Sgshapiro
589864562Sgshapiro	/* sort the records using the random factor for equal preferences */
589964562Sgshapiro	for (i = 0; i < nmx; i++)
590064562Sgshapiro	{
590164562Sgshapiro		for (j = i + 1; j < nmx; j++)
590264562Sgshapiro		{
590364562Sgshapiro			/*
590464562Sgshapiro			**  List is already sorted by MX preference, only
590564562Sgshapiro			**  need to look for equal preference MX records
590664562Sgshapiro			*/
590764562Sgshapiro
590864562Sgshapiro			if (prefer[i] < prefer[j])
590964562Sgshapiro				break;
591064562Sgshapiro
591164562Sgshapiro			if (prefer[i] > prefer[j] ||
591264562Sgshapiro			    (prefer[i] == prefer[j] && rndm[i] > rndm[j]))
591364562Sgshapiro			{
591490792Sgshapiro				register unsigned short tempp;
591564562Sgshapiro				register long tempr;
591664562Sgshapiro				register char *temp1;
591764562Sgshapiro
591864562Sgshapiro				tempp = prefer[i];
591964562Sgshapiro				prefer[i] = prefer[j];
592064562Sgshapiro				prefer[j] = tempp;
592164562Sgshapiro				temp1 = mxhosts[i];
592264562Sgshapiro				mxhosts[i] = mxhosts[j];
592364562Sgshapiro				mxhosts[j] = temp1;
592464562Sgshapiro				tempr = rndm[i];
592564562Sgshapiro				rndm[i] = rndm[j];
592664562Sgshapiro				rndm[j] = tempr;
592764562Sgshapiro			}
592864562Sgshapiro		}
592964562Sgshapiro	}
593064562Sgshapiro	return nmx;
593164562Sgshapiro}
593264562Sgshapiro
593364562Sgshapiro# if STARTTLS
593464562Sgshapirostatic SSL_CTX	*clt_ctx = NULL;
593590792Sgshapirostatic bool	tls_ok_clt = true;
593664562Sgshapiro
593790792Sgshapiro/*
593890792Sgshapiro**  SETCLTTLS -- client side TLS: allow/disallow.
593964562Sgshapiro**
594064562Sgshapiro**	Parameters:
594190792Sgshapiro**		tls_ok -- should tls be done?
594290792Sgshapiro**
594390792Sgshapiro**	Returns:
594464562Sgshapiro**		none.
594564562Sgshapiro**
594690792Sgshapiro**	Side Effects:
594790792Sgshapiro**		sets tls_ok_clt (static variable in this module)
594890792Sgshapiro*/
594990792Sgshapiro
595090792Sgshapirovoid
595190792Sgshapirosetclttls(tls_ok)
595290792Sgshapiro	bool tls_ok;
595390792Sgshapiro{
595490792Sgshapiro	tls_ok_clt = tls_ok;
595590792Sgshapiro	return;
595690792Sgshapiro}
595790792Sgshapiro/*
595890792Sgshapiro**  INITCLTTLS -- initialize client side TLS
595990792Sgshapiro**
596090792Sgshapiro**	Parameters:
596190792Sgshapiro**		tls_ok -- should tls initialization be done?
596290792Sgshapiro**
596364562Sgshapiro**	Returns:
596464562Sgshapiro**		succeeded?
596590792Sgshapiro**
596690792Sgshapiro**	Side Effects:
596790792Sgshapiro**		sets tls_ok_clt (static variable in this module)
596864562Sgshapiro*/
596964562Sgshapiro
597064562Sgshapirobool
597190792Sgshapiroinitclttls(tls_ok)
597290792Sgshapiro	bool tls_ok;
597364562Sgshapiro{
597490792Sgshapiro	if (!tls_ok_clt)
597590792Sgshapiro		return false;
597690792Sgshapiro	tls_ok_clt = tls_ok;
597790792Sgshapiro	if (!tls_ok_clt)
597890792Sgshapiro		return false;
597964562Sgshapiro	if (clt_ctx != NULL)
598090792Sgshapiro		return true;	/* already done */
5981110560Sgshapiro	tls_ok_clt = inittls(&clt_ctx, TLS_I_CLT, false, CltCertFile,
5982110560Sgshapiro			     CltKeyFile, CACertPath, CACertFile, DHParams);
598390792Sgshapiro	return tls_ok_clt;
598464562Sgshapiro}
598564562Sgshapiro
598690792Sgshapiro/*
598764562Sgshapiro**  STARTTLS -- try to start secure connection (client side)
598864562Sgshapiro**
598964562Sgshapiro**	Parameters:
599064562Sgshapiro**		m -- the mailer.
599164562Sgshapiro**		mci -- the mailer connection info.
599264562Sgshapiro**		e -- the envelope.
599364562Sgshapiro**
599464562Sgshapiro**	Returns:
599564562Sgshapiro**		success?
599664562Sgshapiro**		(maybe this should be some other code than EX_
599764562Sgshapiro**		that denotes which stage failed.)
599864562Sgshapiro*/
599964562Sgshapiro
600064562Sgshapirostatic int
600164562Sgshapirostarttls(m, mci, e)
600264562Sgshapiro	MAILER *m;
600364562Sgshapiro	MCI *mci;
600464562Sgshapiro	ENVELOPE *e;
600564562Sgshapiro{
600664562Sgshapiro	int smtpresult;
600766494Sgshapiro	int result = 0;
600866494Sgshapiro	int rfd, wfd;
600964562Sgshapiro	SSL *clt_ssl = NULL;
601090792Sgshapiro	time_t tlsstart;
601164562Sgshapiro
601290792Sgshapiro	if (clt_ctx == NULL && !initclttls(true))
601366494Sgshapiro		return EX_TEMPFAIL;
601464562Sgshapiro	smtpmessage("STARTTLS", m, mci);
601564562Sgshapiro
601664562Sgshapiro	/* get the reply */
601790792Sgshapiro	smtpresult = reply(m, mci, e, TimeOuts.to_starttls, NULL, NULL);
601864562Sgshapiro
601964562Sgshapiro	/* check return code from server */
602064562Sgshapiro	if (smtpresult == 454)
602164562Sgshapiro		return EX_TEMPFAIL;
602264562Sgshapiro	if (smtpresult == 501)
602364562Sgshapiro		return EX_USAGE;
602464562Sgshapiro	if (smtpresult == -1)
602564562Sgshapiro		return smtpresult;
602664562Sgshapiro	if (smtpresult != 220)
602764562Sgshapiro		return EX_PROTOCOL;
602864562Sgshapiro
602964562Sgshapiro	if (LogLevel > 13)
603090792Sgshapiro		sm_syslog(LOG_INFO, NOQID, "STARTTLS=client, start=ok");
603164562Sgshapiro
603264562Sgshapiro	/* start connection */
603364562Sgshapiro	if ((clt_ssl = SSL_new(clt_ctx)) == NULL)
603464562Sgshapiro	{
603564562Sgshapiro		if (LogLevel > 5)
603664562Sgshapiro		{
603790792Sgshapiro			sm_syslog(LOG_ERR, NOQID,
603890792Sgshapiro				  "STARTTLS=client, error: SSL_new failed");
603964562Sgshapiro			if (LogLevel > 9)
604090792Sgshapiro				tlslogerr("client");
604164562Sgshapiro		}
604264562Sgshapiro		return EX_SOFTWARE;
604364562Sgshapiro	}
604464562Sgshapiro
604590792Sgshapiro	rfd = sm_io_getinfo(mci->mci_in, SM_IO_WHAT_FD, NULL);
604690792Sgshapiro	wfd = sm_io_getinfo(mci->mci_out, SM_IO_WHAT_FD, NULL);
604766494Sgshapiro
604864562Sgshapiro	/* SSL_clear(clt_ssl); ? */
604966494Sgshapiro	if (rfd < 0 || wfd < 0 ||
605090792Sgshapiro	    (result = SSL_set_rfd(clt_ssl, rfd)) != 1 ||
605190792Sgshapiro	    (result = SSL_set_wfd(clt_ssl, wfd)) != 1)
605264562Sgshapiro	{
605364562Sgshapiro		if (LogLevel > 5)
605464562Sgshapiro		{
605590792Sgshapiro			sm_syslog(LOG_ERR, NOQID,
605690792Sgshapiro				  "STARTTLS=client, error: SSL_set_xfd failed=%d",
605790792Sgshapiro				  result);
605864562Sgshapiro			if (LogLevel > 9)
605990792Sgshapiro				tlslogerr("client");
606064562Sgshapiro		}
606164562Sgshapiro		return EX_SOFTWARE;
606264562Sgshapiro	}
606364562Sgshapiro	SSL_set_connect_state(clt_ssl);
606490792Sgshapiro	tlsstart = curtime();
606590792Sgshapiro
606690792Sgshapirossl_retry:
606764562Sgshapiro	if ((result = SSL_connect(clt_ssl)) <= 0)
606864562Sgshapiro	{
606964562Sgshapiro		int i;
607090792Sgshapiro		bool timedout;
607190792Sgshapiro		time_t left;
607290792Sgshapiro		time_t now = curtime();
607390792Sgshapiro		struct timeval tv;
607464562Sgshapiro
607564562Sgshapiro		/* what to do in this case? */
607664562Sgshapiro		i = SSL_get_error(clt_ssl, result);
607790792Sgshapiro
607890792Sgshapiro		/*
607990792Sgshapiro		**  For SSL_ERROR_WANT_{READ,WRITE}:
608090792Sgshapiro		**  There is not a complete SSL record available yet
608190792Sgshapiro		**  or there is only a partial SSL record removed from
608290792Sgshapiro		**  the network (socket) buffer into the SSL buffer.
608390792Sgshapiro		**  The SSL_connect will only succeed when a full
608490792Sgshapiro		**  SSL record is available (assuming a "real" error
608590792Sgshapiro		**  doesn't happen). To handle when a "real" error
608690792Sgshapiro		**  does happen the select is set for exceptions too.
608790792Sgshapiro		**  The connection may be re-negotiated during this time
608890792Sgshapiro		**  so both read and write "want errors" need to be handled.
608990792Sgshapiro		**  A select() exception loops back so that a proper SSL
609090792Sgshapiro		**  error message can be gotten.
609190792Sgshapiro		*/
609290792Sgshapiro
609390792Sgshapiro		left = TimeOuts.to_starttls - (now - tlsstart);
609490792Sgshapiro		timedout = left <= 0;
609590792Sgshapiro		if (!timedout)
609690792Sgshapiro		{
609790792Sgshapiro			tv.tv_sec = left;
609890792Sgshapiro			tv.tv_usec = 0;
609990792Sgshapiro		}
610090792Sgshapiro
6101110560Sgshapiro		if (!timedout && FD_SETSIZE > 0 &&
6102110560Sgshapiro		    (rfd >= FD_SETSIZE ||
6103110560Sgshapiro		     (i == SSL_ERROR_WANT_WRITE && wfd >= FD_SETSIZE)))
6104110560Sgshapiro		{
6105110560Sgshapiro			if (LogLevel > 5)
6106110560Sgshapiro			{
6107110560Sgshapiro				sm_syslog(LOG_ERR, e->e_id,
6108110560Sgshapiro					  "STARTTLS=client, error: fd %d/%d too large",
6109110560Sgshapiro					  rfd, wfd);
6110110560Sgshapiro			if (LogLevel > 8)
6111110560Sgshapiro				tlslogerr("client");
6112110560Sgshapiro			}
6113110560Sgshapiro			errno = EINVAL;
6114110560Sgshapiro			goto tlsfail;
6115110560Sgshapiro		}
611690792Sgshapiro		if (!timedout && i == SSL_ERROR_WANT_READ)
611790792Sgshapiro		{
611890792Sgshapiro			fd_set ssl_maskr, ssl_maskx;
611990792Sgshapiro
612090792Sgshapiro			FD_ZERO(&ssl_maskr);
612190792Sgshapiro			FD_SET(rfd, &ssl_maskr);
612290792Sgshapiro			FD_ZERO(&ssl_maskx);
612390792Sgshapiro			FD_SET(rfd, &ssl_maskx);
612490792Sgshapiro			if (select(rfd + 1, &ssl_maskr, NULL, &ssl_maskx, &tv)
612590792Sgshapiro			    > 0)
612690792Sgshapiro				goto ssl_retry;
612790792Sgshapiro		}
612890792Sgshapiro		if (!timedout && i == SSL_ERROR_WANT_WRITE)
612990792Sgshapiro		{
613090792Sgshapiro			fd_set ssl_maskw, ssl_maskx;
613190792Sgshapiro
613290792Sgshapiro			FD_ZERO(&ssl_maskw);
613390792Sgshapiro			FD_SET(wfd, &ssl_maskw);
613490792Sgshapiro			FD_ZERO(&ssl_maskx);
613590792Sgshapiro			FD_SET(rfd, &ssl_maskx);
613690792Sgshapiro			if (select(wfd + 1, NULL, &ssl_maskw, &ssl_maskx, &tv)
613790792Sgshapiro			    > 0)
613890792Sgshapiro				goto ssl_retry;
613990792Sgshapiro		}
614064562Sgshapiro		if (LogLevel > 5)
614164562Sgshapiro		{
614264562Sgshapiro			sm_syslog(LOG_ERR, e->e_id,
6143111823Sgshapiro				  "STARTTLS=client, error: connect failed=%d, SSL_error=%d, timedout=%d, errno=%d",
6144111823Sgshapiro				  result, i, (int) timedout, errno);
614590792Sgshapiro			if (LogLevel > 8)
614690792Sgshapiro				tlslogerr("client");
614764562Sgshapiro		}
6148110560Sgshapirotlsfail:
614964562Sgshapiro		SSL_free(clt_ssl);
615064562Sgshapiro		clt_ssl = NULL;
615164562Sgshapiro		return EX_SOFTWARE;
615264562Sgshapiro	}
615364562Sgshapiro	mci->mci_ssl = clt_ssl;
615490792Sgshapiro	result = tls_get_info(mci->mci_ssl, false, mci->mci_host,
615590792Sgshapiro			      &mci->mci_macro, true);
615664562Sgshapiro
615790792Sgshapiro	/* switch to use TLS... */
615864562Sgshapiro	if (sfdctls(&mci->mci_in, &mci->mci_out, mci->mci_ssl) == 0)
615964562Sgshapiro		return EX_OK;
616064562Sgshapiro
616164562Sgshapiro	/* failure */
616264562Sgshapiro	SSL_free(clt_ssl);
616364562Sgshapiro	clt_ssl = NULL;
616464562Sgshapiro	return EX_SOFTWARE;
616564562Sgshapiro}
616690792Sgshapiro/*
616764562Sgshapiro**  ENDTLSCLT -- shutdown secure connection (client side)
616864562Sgshapiro**
616964562Sgshapiro**	Parameters:
617064562Sgshapiro**		mci -- the mailer connection info.
617164562Sgshapiro**
617264562Sgshapiro**	Returns:
617364562Sgshapiro**		success?
617464562Sgshapiro*/
617590792Sgshapiro
617690792Sgshapirostatic int
617764562Sgshapiroendtlsclt(mci)
617864562Sgshapiro	MCI *mci;
617964562Sgshapiro{
618064562Sgshapiro	int r;
618164562Sgshapiro
618264562Sgshapiro	if (!bitset(MCIF_TLSACT, mci->mci_flags))
618364562Sgshapiro		return EX_OK;
618464562Sgshapiro	r = endtls(mci->mci_ssl, "client");
618564562Sgshapiro	mci->mci_flags &= ~MCIF_TLSACT;
618664562Sgshapiro	return r;
618764562Sgshapiro}
618890792Sgshapiro# endif /* STARTTLS */
618990792Sgshapiro# if STARTTLS || SASL
619090792Sgshapiro/*
619190792Sgshapiro**  ISCLTFLGSET -- check whether client flag is set.
619264562Sgshapiro**
619364562Sgshapiro**	Parameters:
619490792Sgshapiro**		e -- envelope.
619590792Sgshapiro**		flag -- flag to check in {client_flags}
619664562Sgshapiro**
619764562Sgshapiro**	Returns:
619890792Sgshapiro**		true iff flag is set.
619964562Sgshapiro*/
620064562Sgshapiro
620190792Sgshapirostatic bool
620290792Sgshapiroiscltflgset(e, flag)
620390792Sgshapiro	ENVELOPE *e;
620490792Sgshapiro	int flag;
620564562Sgshapiro{
620690792Sgshapiro	char *p;
620773188Sgshapiro
620890792Sgshapiro	p = macvalue(macid("{client_flags}"), e);
620990792Sgshapiro	if (p == NULL)
621090792Sgshapiro		return false;
621190792Sgshapiro	for (; *p != '\0'; p++)
621264562Sgshapiro	{
621390792Sgshapiro		/* look for just this one flag */
621490792Sgshapiro		if (*p == (char) flag)
621590792Sgshapiro			return true;
621664562Sgshapiro	}
621790792Sgshapiro	return false;
621864562Sgshapiro}
621990792Sgshapiro# endif /* STARTTLS || SASL */
6220