138032Speter/*
2261194Sgshapiro * Copyright (c) 1998-2010, 2012 Proofpoint, 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>
15157001Sgshapiro#include <sm/time.h>
1638032Speter
17266527SgshapiroSM_RCSID("@(#)$Id: deliver.c,v 8.1030 2013-11-22 20:51:55 ca Exp $")
1864562Sgshapiro
1938032Speter#if HASSETUSERCONTEXT
2038032Speter# include <login_cap.h>
21363466Sgshapiro#endif
2238032Speter
2394334Sgshapiro#if NETINET || NETINET6
2494334Sgshapiro# include <arpa/inet.h>
25363466Sgshapiro#endif
2694334Sgshapiro
2790792Sgshapiro#if STARTTLS || SASL
2864562Sgshapiro# include "sfsasl.h"
29363466Sgshapiro# include "tls.h"
30363466Sgshapiro#endif
3164562Sgshapiro
3264562Sgshapirostatic int	deliver __P((ENVELOPE *, ADDRESS *));
3364562Sgshapirostatic void	dup_queue_file __P((ENVELOPE *, ENVELOPE *, int));
34141858Sgshapirostatic void	mailfiletimeout __P((int));
35141858Sgshapirostatic void	endwaittimeout __P((int));
3664562Sgshapirostatic int	parse_hostsignature __P((char *, char **, MAILER *));
3764562Sgshapirostatic void	sendenvelope __P((ENVELOPE *, int));
3890792Sgshapirostatic int	coloncmp __P((const char *, const char *));
3964562Sgshapiro
4090792Sgshapiro#if STARTTLS
41285229Sgshapiro# include <openssl/err.h>
42363466Sgshapiro# if DANE
43363466Sgshapirostatic int	starttls __P((MAILER *, MCI *, ENVELOPE *, dane_vrfy_ctx_P));
44363466Sgshapiro# else
4564562Sgshapirostatic int	starttls __P((MAILER *, MCI *, ENVELOPE *));
46363466Sgshapiro# endif
4790792Sgshapirostatic int	endtlsclt __P((MCI *));
4890792Sgshapiro#endif /* STARTTLS */
49363466Sgshapiro#if STARTTLS || SASL
5090792Sgshapirostatic bool	iscltflgset __P((ENVELOPE *, int));
51363466Sgshapiro#endif
5238032Speter
53363466Sgshapiro#if _FFR_OCC
54363466Sgshapiro# include <ratectrl.h>
55363466Sgshapiro#endif
56363466Sgshapiro
5738032Speter/*
5838032Speter**  SENDALL -- actually send all the messages.
5938032Speter**
6038032Speter**	Parameters:
6138032Speter**		e -- the envelope to send.
6238032Speter**		mode -- the delivery mode to use.  If SM_DEFAULT, use
6338032Speter**			the current e->e_sendmode.
6438032Speter**
6538032Speter**	Returns:
6638032Speter**		none.
6738032Speter**
6838032Speter**	Side Effects:
6938032Speter**		Scans the send lists and sends everything it finds.
7038032Speter**		Delivers any appropriate error messages.
7138032Speter**		If we are running in a non-interactive mode, takes the
7238032Speter**			appropriate action.
7338032Speter*/
7438032Speter
7538032Spetervoid
7638032Spetersendall(e, mode)
7738032Speter	ENVELOPE *e;
7838032Speter	int mode;
7938032Speter{
8038032Speter	register ADDRESS *q;
8138032Speter	char *owner;
8238032Speter	int otherowners;
8364562Sgshapiro	int save_errno;
8438032Speter	register ENVELOPE *ee;
8538032Speter	ENVELOPE *splitenv = NULL;
8638032Speter	int oldverbose = Verbose;
8790792Sgshapiro	bool somedeliveries = false, expensive = false;
8838032Speter	pid_t pid;
8938032Speter
9038032Speter	/*
9138032Speter	**  If this message is to be discarded, don't bother sending
9238032Speter	**  the message at all.
9338032Speter	*/
9438032Speter
9538032Speter	if (bitset(EF_DISCARD, e->e_flags))
9638032Speter	{
9738032Speter		if (tTd(13, 1))
9890792Sgshapiro			sm_dprintf("sendall: discarding id %s\n", e->e_id);
9938032Speter		e->e_flags |= EF_CLRQUEUE;
10090792Sgshapiro		if (LogLevel > 9)
10190792Sgshapiro			logundelrcpts(e, "discarded", 9, true);
10290792Sgshapiro		else if (LogLevel > 4)
10338032Speter			sm_syslog(LOG_INFO, e->e_id, "discarded");
10490792Sgshapiro		markstats(e, NULL, STATS_REJECT);
10538032Speter		return;
10638032Speter	}
10738032Speter
10838032Speter	/*
10938032Speter	**  If we have had global, fatal errors, don't bother sending
11038032Speter	**  the message at all if we are in SMTP mode.  Local errors
11138032Speter	**  (e.g., a single address failing) will still cause the other
11238032Speter	**  addresses to be sent.
11338032Speter	*/
11438032Speter
11538032Speter	if (bitset(EF_FATALERRS, e->e_flags) &&
11638032Speter	    (OpMode == MD_SMTP || OpMode == MD_DAEMON))
11738032Speter	{
11838032Speter		e->e_flags |= EF_CLRQUEUE;
11938032Speter		return;
12038032Speter	}
12138032Speter
12238032Speter	/* determine actual delivery mode */
12338032Speter	if (mode == SM_DEFAULT)
12438032Speter	{
12538032Speter		mode = e->e_sendmode;
12638032Speter		if (mode != SM_VERIFY && mode != SM_DEFER &&
12738032Speter		    shouldqueue(e->e_msgpriority, e->e_ctime))
12838032Speter			mode = SM_QUEUE;
12938032Speter	}
13038032Speter
13138032Speter	if (tTd(13, 1))
13238032Speter	{
13390792Sgshapiro		sm_dprintf("\n===== SENDALL: mode %c, id %s, e_from ",
13438032Speter			mode, e->e_id);
135132943Sgshapiro		printaddr(sm_debug_file(), &e->e_from, false);
13690792Sgshapiro		sm_dprintf("\te_flags = ");
13738032Speter		printenvflags(e);
13890792Sgshapiro		sm_dprintf("sendqueue:\n");
139132943Sgshapiro		printaddr(sm_debug_file(), e->e_sendqueue, true);
14038032Speter	}
14138032Speter
14238032Speter	/*
14338032Speter	**  Do any preprocessing necessary for the mode we are running.
14438032Speter	**	Check to make sure the hop count is reasonable.
14538032Speter	**	Delete sends to the sender in mailing lists.
14638032Speter	*/
14738032Speter
14838032Speter	CurEnv = e;
14938032Speter	if (tTd(62, 1))
15038032Speter		checkfds(NULL);
15138032Speter
15238032Speter	if (e->e_hopcount > MaxHopCount)
15338032Speter	{
15477349Sgshapiro		char *recip;
15577349Sgshapiro
15677349Sgshapiro		if (e->e_sendqueue != NULL &&
15777349Sgshapiro		    e->e_sendqueue->q_paddr != NULL)
15877349Sgshapiro			recip = e->e_sendqueue->q_paddr;
15977349Sgshapiro		else
16077349Sgshapiro			recip = "(nobody)";
16177349Sgshapiro
16238032Speter		errno = 0;
16390792Sgshapiro		queueup(e, WILL_BE_QUEUED(mode), false);
16438032Speter		e->e_flags |= EF_FATALERRS|EF_PM_NOTIFY|EF_CLRQUEUE;
16564562Sgshapiro		ExitStat = EX_UNAVAILABLE;
16677349Sgshapiro		syserr("554 5.4.6 Too many hops %d (%d max): from %s via %s, to %s",
16777349Sgshapiro		       e->e_hopcount, MaxHopCount, e->e_from.q_paddr,
16877349Sgshapiro		       RealHostName == NULL ? "localhost" : RealHostName,
16977349Sgshapiro		       recip);
17064562Sgshapiro		for (q = e->e_sendqueue; q != NULL; q = q->q_next)
17164562Sgshapiro		{
17264562Sgshapiro			if (QS_IS_DEAD(q->q_state))
17364562Sgshapiro				continue;
17464562Sgshapiro			q->q_state = QS_BADADDR;
17564562Sgshapiro			q->q_status = "5.4.6";
17677349Sgshapiro			q->q_rstatus = "554 5.4.6 Too many hops";
17764562Sgshapiro		}
17838032Speter		return;
17938032Speter	}
18038032Speter
18138032Speter	/*
18238032Speter	**  Do sender deletion.
18338032Speter	**
18464562Sgshapiro	**	If the sender should be queued up, skip this.
18538032Speter	**	This can happen if the name server is hosed when you
18638032Speter	**	are trying to send mail.  The result is that the sender
18738032Speter	**	is instantiated in the queue as a recipient.
18838032Speter	*/
18938032Speter
19038032Speter	if (!bitset(EF_METOO, e->e_flags) &&
19164562Sgshapiro	    !QS_IS_QUEUEUP(e->e_from.q_state))
19238032Speter	{
19338032Speter		if (tTd(13, 5))
19438032Speter		{
19590792Sgshapiro			sm_dprintf("sendall: QS_SENDER ");
196132943Sgshapiro			printaddr(sm_debug_file(), &e->e_from, false);
19738032Speter		}
19864562Sgshapiro		e->e_from.q_state = QS_SENDER;
19938032Speter		(void) recipient(&e->e_from, &e->e_sendqueue, 0, e);
20038032Speter	}
20138032Speter
20238032Speter	/*
20338032Speter	**  Handle alias owners.
20438032Speter	**
20538032Speter	**	We scan up the q_alias chain looking for owners.
20638032Speter	**	We discard owners that are the same as the return path.
20738032Speter	*/
20838032Speter
20938032Speter	for (q = e->e_sendqueue; q != NULL; q = q->q_next)
21038032Speter	{
21138032Speter		register struct address *a;
21238032Speter
21338032Speter		for (a = q; a != NULL && a->q_owner == NULL; a = a->q_alias)
21438032Speter			continue;
21538032Speter		if (a != NULL)
21638032Speter			q->q_owner = a->q_owner;
21738032Speter
21838032Speter		if (q->q_owner != NULL &&
21964562Sgshapiro		    !QS_IS_DEAD(q->q_state) &&
22038032Speter		    strcmp(q->q_owner, e->e_from.q_paddr) == 0)
22138032Speter			q->q_owner = NULL;
22238032Speter	}
22338032Speter
22438032Speter	if (tTd(13, 25))
22538032Speter	{
22690792Sgshapiro		sm_dprintf("\nAfter first owner pass, sendq =\n");
227132943Sgshapiro		printaddr(sm_debug_file(), e->e_sendqueue, true);
22838032Speter	}
22938032Speter
23038032Speter	owner = "";
23138032Speter	otherowners = 1;
23238032Speter	while (owner != NULL && otherowners > 0)
23338032Speter	{
23438032Speter		if (tTd(13, 28))
23590792Sgshapiro			sm_dprintf("owner = \"%s\", otherowners = %d\n",
23690792Sgshapiro				   owner, otherowners);
23738032Speter		owner = NULL;
23838032Speter		otherowners = bitset(EF_SENDRECEIPT, e->e_flags) ? 1 : 0;
23938032Speter
24038032Speter		for (q = e->e_sendqueue; q != NULL; q = q->q_next)
24138032Speter		{
24238032Speter			if (tTd(13, 30))
24338032Speter			{
24490792Sgshapiro				sm_dprintf("Checking ");
245132943Sgshapiro				printaddr(sm_debug_file(), q, false);
24638032Speter			}
24764562Sgshapiro			if (QS_IS_DEAD(q->q_state))
24838032Speter			{
24938032Speter				if (tTd(13, 30))
25090792Sgshapiro					sm_dprintf("    ... QS_IS_DEAD\n");
25138032Speter				continue;
25238032Speter			}
25338032Speter			if (tTd(13, 29) && !tTd(13, 30))
25438032Speter			{
25590792Sgshapiro				sm_dprintf("Checking ");
256132943Sgshapiro				printaddr(sm_debug_file(), q, false);
25738032Speter			}
25838032Speter
25938032Speter			if (q->q_owner != NULL)
26038032Speter			{
26138032Speter				if (owner == NULL)
26238032Speter				{
26338032Speter					if (tTd(13, 40))
26490792Sgshapiro						sm_dprintf("    ... First owner = \"%s\"\n",
26590792Sgshapiro							   q->q_owner);
26638032Speter					owner = q->q_owner;
26738032Speter				}
26838032Speter				else if (owner != q->q_owner)
26938032Speter				{
27038032Speter					if (strcmp(owner, q->q_owner) == 0)
27138032Speter					{
27238032Speter						if (tTd(13, 40))
27390792Sgshapiro							sm_dprintf("    ... Same owner = \"%s\"\n",
27490792Sgshapiro								   owner);
27538032Speter
27638032Speter						/* make future comparisons cheap */
27738032Speter						q->q_owner = owner;
27838032Speter					}
27938032Speter					else
28038032Speter					{
28138032Speter						if (tTd(13, 40))
28290792Sgshapiro							sm_dprintf("    ... Another owner \"%s\"\n",
28390792Sgshapiro								   q->q_owner);
28438032Speter						otherowners++;
28538032Speter					}
28638032Speter					owner = q->q_owner;
28738032Speter				}
28838032Speter				else if (tTd(13, 40))
28990792Sgshapiro					sm_dprintf("    ... Same owner = \"%s\"\n",
29090792Sgshapiro						   owner);
29138032Speter			}
29238032Speter			else
29338032Speter			{
29438032Speter				if (tTd(13, 40))
29590792Sgshapiro					sm_dprintf("    ... Null owner\n");
29638032Speter				otherowners++;
29738032Speter			}
29838032Speter
29964562Sgshapiro			if (QS_IS_BADADDR(q->q_state))
30064562Sgshapiro			{
30164562Sgshapiro				if (tTd(13, 30))
30290792Sgshapiro					sm_dprintf("    ... QS_IS_BADADDR\n");
30364562Sgshapiro				continue;
30464562Sgshapiro			}
30564562Sgshapiro
30664562Sgshapiro			if (QS_IS_QUEUEUP(q->q_state))
30764562Sgshapiro			{
30864562Sgshapiro				MAILER *m = q->q_mailer;
30964562Sgshapiro
31064562Sgshapiro				/*
31164562Sgshapiro				**  If we have temporary address failures
31264562Sgshapiro				**  (e.g., dns failure) and a fallback MX is
31364562Sgshapiro				**  set, send directly to the fallback MX host.
31464562Sgshapiro				*/
31564562Sgshapiro
316132943Sgshapiro				if (FallbackMX != NULL &&
317132943Sgshapiro				    !wordinclass(FallbackMX, 'w') &&
31864562Sgshapiro				    mode != SM_VERIFY &&
31990792Sgshapiro				    !bitnset(M_NOMX, m->m_flags) &&
32090792Sgshapiro				    strcmp(m->m_mailer, "[IPC]") == 0 &&
32164562Sgshapiro				    m->m_argv[0] != NULL &&
32290792Sgshapiro				    strcmp(m->m_argv[0], "TCP") == 0)
32364562Sgshapiro				{
32464562Sgshapiro					int len;
32564562Sgshapiro					char *p;
32664562Sgshapiro
32764562Sgshapiro					if (tTd(13, 30))
328132943Sgshapiro						sm_dprintf("    ... FallbackMX\n");
32964562Sgshapiro
330132943Sgshapiro					len = strlen(FallbackMX) + 1;
33190792Sgshapiro					p = sm_rpool_malloc_x(e->e_rpool, len);
332132943Sgshapiro					(void) sm_strlcpy(p, FallbackMX, len);
33364562Sgshapiro					q->q_state = QS_OK;
33464562Sgshapiro					q->q_host = p;
33564562Sgshapiro				}
33664562Sgshapiro				else
33764562Sgshapiro				{
33864562Sgshapiro					if (tTd(13, 30))
33990792Sgshapiro						sm_dprintf("    ... QS_IS_QUEUEUP\n");
34064562Sgshapiro					continue;
34164562Sgshapiro				}
34264562Sgshapiro			}
34364562Sgshapiro
34438032Speter			/*
34538032Speter			**  If this mailer is expensive, and if we don't
34638032Speter			**  want to make connections now, just mark these
34738032Speter			**  addresses and return.  This is useful if we
34838032Speter			**  want to batch connections to reduce load.  This
34938032Speter			**  will cause the messages to be queued up, and a
35038032Speter			**  daemon will come along to send the messages later.
35138032Speter			*/
35238032Speter
35364562Sgshapiro			if (NoConnect && !Verbose &&
35464562Sgshapiro			    bitnset(M_EXPENSIVE, q->q_mailer->m_flags))
35538032Speter			{
35638032Speter				if (tTd(13, 30))
35790792Sgshapiro					sm_dprintf("    ... expensive\n");
35864562Sgshapiro				q->q_state = QS_QUEUEUP;
35990792Sgshapiro				expensive = true;
36038032Speter			}
36164562Sgshapiro			else if (bitnset(M_HOLD, q->q_mailer->m_flags) &&
36264562Sgshapiro				 QueueLimitId == NULL &&
36364562Sgshapiro				 QueueLimitSender == NULL &&
36464562Sgshapiro				 QueueLimitRecipient == NULL)
36538032Speter			{
36638032Speter				if (tTd(13, 30))
36790792Sgshapiro					sm_dprintf("    ... hold\n");
36864562Sgshapiro				q->q_state = QS_QUEUEUP;
36990792Sgshapiro				expensive = true;
37038032Speter			}
37190792Sgshapiro			else if (QueueMode != QM_QUARANTINE &&
37290792Sgshapiro				 e->e_quarmsg != NULL)
37390792Sgshapiro			{
37490792Sgshapiro				if (tTd(13, 30))
37590792Sgshapiro					sm_dprintf("    ... quarantine: %s\n",
37690792Sgshapiro						   e->e_quarmsg);
37790792Sgshapiro				q->q_state = QS_QUEUEUP;
37890792Sgshapiro				expensive = true;
37990792Sgshapiro			}
38038032Speter			else
38138032Speter			{
38238032Speter				if (tTd(13, 30))
38390792Sgshapiro					sm_dprintf("    ... deliverable\n");
38490792Sgshapiro				somedeliveries = true;
38538032Speter			}
38638032Speter		}
38738032Speter
38838032Speter		if (owner != NULL && otherowners > 0)
38938032Speter		{
39038032Speter			/*
39138032Speter			**  Split this envelope into two.
39238032Speter			*/
39338032Speter
39490792Sgshapiro			ee = (ENVELOPE *) sm_rpool_malloc_x(e->e_rpool,
395168515Sgshapiro							    sizeof(*ee));
39690792Sgshapiro			STRUCTCOPY(*e, *ee);
39764562Sgshapiro			ee->e_message = NULL;
39838032Speter			ee->e_id = NULL;
39964562Sgshapiro			assign_queueid(ee);
40038032Speter
40138032Speter			if (tTd(13, 1))
40290792Sgshapiro				sm_dprintf("sendall: split %s into %s, owner = \"%s\", otherowners = %d\n",
40390792Sgshapiro					   e->e_id, ee->e_id, owner,
40490792Sgshapiro					   otherowners);
40538032Speter
40690792Sgshapiro			ee->e_header = copyheader(e->e_header, ee->e_rpool);
40790792Sgshapiro			ee->e_sendqueue = copyqueue(e->e_sendqueue,
40890792Sgshapiro						    ee->e_rpool);
40990792Sgshapiro			ee->e_errorqueue = copyqueue(e->e_errorqueue,
41090792Sgshapiro						     ee->e_rpool);
41138032Speter			ee->e_flags = e->e_flags & ~(EF_INQUEUE|EF_CLRQUEUE|EF_FATALERRS|EF_SENDRECEIPT|EF_RET_PARAM);
41238032Speter			ee->e_flags |= EF_NORECEIPT;
41390792Sgshapiro			setsender(owner, ee, NULL, '\0', true);
41438032Speter			if (tTd(13, 5))
41538032Speter			{
41690792Sgshapiro				sm_dprintf("sendall(split): QS_SENDER ");
417132943Sgshapiro				printaddr(sm_debug_file(), &ee->e_from, false);
41838032Speter			}
41964562Sgshapiro			ee->e_from.q_state = QS_SENDER;
42038032Speter			ee->e_dfp = NULL;
42164562Sgshapiro			ee->e_lockfp = NULL;
42238032Speter			ee->e_xfp = NULL;
42390792Sgshapiro			ee->e_qgrp = e->e_qgrp;
42490792Sgshapiro			ee->e_qdir = e->e_qdir;
42538032Speter			ee->e_errormode = EM_MAIL;
42638032Speter			ee->e_sibling = splitenv;
42764562Sgshapiro			ee->e_statmsg = NULL;
42890792Sgshapiro			if (e->e_quarmsg != NULL)
42990792Sgshapiro				ee->e_quarmsg = sm_rpool_strdup_x(ee->e_rpool,
43090792Sgshapiro								  e->e_quarmsg);
43138032Speter			splitenv = ee;
43238032Speter
43338032Speter			for (q = e->e_sendqueue; q != NULL; q = q->q_next)
43438032Speter			{
43538032Speter				if (q->q_owner == owner)
43638032Speter				{
43764562Sgshapiro					q->q_state = QS_CLONED;
43838032Speter					if (tTd(13, 6))
43990792Sgshapiro						sm_dprintf("\t... stripping %s from original envelope\n",
44090792Sgshapiro							   q->q_paddr);
44138032Speter				}
44238032Speter			}
44338032Speter			for (q = ee->e_sendqueue; q != NULL; q = q->q_next)
44438032Speter			{
44538032Speter				if (q->q_owner != owner)
44638032Speter				{
44764562Sgshapiro					q->q_state = QS_CLONED;
44838032Speter					if (tTd(13, 6))
44990792Sgshapiro						sm_dprintf("\t... dropping %s from cloned envelope\n",
45090792Sgshapiro							   q->q_paddr);
45138032Speter				}
45238032Speter				else
45338032Speter				{
45438032Speter					/* clear DSN parameters */
45538032Speter					q->q_flags &= ~(QHASNOTIFY|Q_PINGFLAGS);
45638032Speter					q->q_flags |= DefaultNotify & ~QPINGONSUCCESS;
45738032Speter					if (tTd(13, 6))
45890792Sgshapiro						sm_dprintf("\t... moving %s to cloned envelope\n",
45990792Sgshapiro							   q->q_paddr);
46038032Speter				}
46138032Speter			}
46238032Speter
46338032Speter			if (mode != SM_VERIFY && bitset(EF_HAS_DF, e->e_flags))
46490792Sgshapiro				dup_queue_file(e, ee, DATAFL_LETTER);
46564562Sgshapiro
46664562Sgshapiro			/*
46764562Sgshapiro			**  Give the split envelope access to the parent
46864562Sgshapiro			**  transcript file for errors obtained while
46964562Sgshapiro			**  processing the recipients (done before the
47064562Sgshapiro			**  envelope splitting).
47164562Sgshapiro			*/
47264562Sgshapiro
47364562Sgshapiro			if (e->e_xfp != NULL)
47490792Sgshapiro				ee->e_xfp = sm_io_dup(e->e_xfp);
47564562Sgshapiro
47664562Sgshapiro			/* failed to dup e->e_xfp, start a new transcript */
47764562Sgshapiro			if (ee->e_xfp == NULL)
47864562Sgshapiro				openxscript(ee);
47964562Sgshapiro
48042575Speter			if (mode != SM_VERIFY && LogLevel > 4)
48190792Sgshapiro				sm_syslog(LOG_INFO, e->e_id,
48290792Sgshapiro					  "%s: clone: owner=%s",
48390792Sgshapiro					  ee->e_id, owner);
48438032Speter		}
48538032Speter	}
48638032Speter
48738032Speter	if (owner != NULL)
48838032Speter	{
48990792Sgshapiro		setsender(owner, e, NULL, '\0', true);
49038032Speter		if (tTd(13, 5))
49138032Speter		{
49290792Sgshapiro			sm_dprintf("sendall(owner): QS_SENDER ");
493132943Sgshapiro			printaddr(sm_debug_file(), &e->e_from, false);
49438032Speter		}
49564562Sgshapiro		e->e_from.q_state = QS_SENDER;
49638032Speter		e->e_errormode = EM_MAIL;
49738032Speter		e->e_flags |= EF_NORECEIPT;
49838032Speter		e->e_flags &= ~EF_FATALERRS;
49938032Speter	}
50038032Speter
50138032Speter	/* if nothing to be delivered, just queue up everything */
50290792Sgshapiro	if (!somedeliveries && !WILL_BE_QUEUED(mode) &&
50338032Speter	    mode != SM_VERIFY)
50438032Speter	{
50590792Sgshapiro		time_t now;
50671345Sgshapiro
50738032Speter		if (tTd(13, 29))
508173340Sgshapiro			sm_dprintf("No deliveries: auto-queueing\n");
50938032Speter		mode = SM_QUEUE;
51090792Sgshapiro		now = curtime();
51138032Speter
51238032Speter		/* treat this as a delivery in terms of counting tries */
51371345Sgshapiro		e->e_dtime = now;
51438032Speter		if (!expensive)
51538032Speter			e->e_ntries++;
51638032Speter		for (ee = splitenv; ee != NULL; ee = ee->e_sibling)
51738032Speter		{
51871345Sgshapiro			ee->e_dtime = now;
51938032Speter			if (!expensive)
52038032Speter				ee->e_ntries++;
52138032Speter		}
52238032Speter	}
52338032Speter
52490792Sgshapiro	if ((WILL_BE_QUEUED(mode) || mode == SM_FORK ||
525132943Sgshapiro	     (mode != SM_VERIFY &&
526132943Sgshapiro	      (SuperSafe == SAFE_REALLY ||
527132943Sgshapiro	       SuperSafe == SAFE_REALLY_POSTMILTER))) &&
52838032Speter	    (!bitset(EF_INQUEUE, e->e_flags) || splitenv != NULL))
52938032Speter	{
53090792Sgshapiro		bool msync;
53190792Sgshapiro
53266494Sgshapiro		/*
53366494Sgshapiro		**  Be sure everything is instantiated in the queue.
53466494Sgshapiro		**  Split envelopes first in case the machine crashes.
53566494Sgshapiro		**  If the original were done first, we may lose
53666494Sgshapiro		**  recipients.
53766494Sgshapiro		*/
53866494Sgshapiro
53990792Sgshapiro#if !HASFLOCK
54090792Sgshapiro		msync = false;
541363466Sgshapiro#else
54290792Sgshapiro		msync = mode == SM_FORK;
543363466Sgshapiro#endif
54490792Sgshapiro
54538032Speter		for (ee = splitenv; ee != NULL; ee = ee->e_sibling)
54690792Sgshapiro			queueup(ee, WILL_BE_QUEUED(mode), msync);
54790792Sgshapiro		queueup(e, WILL_BE_QUEUED(mode), msync);
54838032Speter	}
54938032Speter
55038032Speter	if (tTd(62, 10))
55138032Speter		checkfds("after envelope splitting");
55238032Speter
55338032Speter	/*
55438032Speter	**  If we belong in background, fork now.
55538032Speter	*/
55638032Speter
55738032Speter	if (tTd(13, 20))
55838032Speter	{
55990792Sgshapiro		sm_dprintf("sendall: final mode = %c\n", mode);
56038032Speter		if (tTd(13, 21))
56138032Speter		{
56290792Sgshapiro			sm_dprintf("\n================ Final Send Queue(s) =====================\n");
56390792Sgshapiro			sm_dprintf("\n  *** Envelope %s, e_from=%s ***\n",
56490792Sgshapiro				   e->e_id, e->e_from.q_paddr);
565132943Sgshapiro			printaddr(sm_debug_file(), e->e_sendqueue, true);
56638032Speter			for (ee = splitenv; ee != NULL; ee = ee->e_sibling)
56738032Speter			{
56890792Sgshapiro				sm_dprintf("\n  *** Envelope %s, e_from=%s ***\n",
56990792Sgshapiro					   ee->e_id, ee->e_from.q_paddr);
570132943Sgshapiro				printaddr(sm_debug_file(), ee->e_sendqueue, true);
57138032Speter			}
57290792Sgshapiro			sm_dprintf("==========================================================\n\n");
57338032Speter		}
57438032Speter	}
57538032Speter	switch (mode)
57638032Speter	{
57738032Speter	  case SM_VERIFY:
57838032Speter		Verbose = 2;
57938032Speter		break;
58038032Speter
58138032Speter	  case SM_QUEUE:
58238032Speter	  case SM_DEFER:
58364562Sgshapiro#if HASFLOCK
58438032Speter  queueonly:
585363466Sgshapiro#endif
58638032Speter		if (e->e_nrcpts > 0)
58738032Speter			e->e_flags |= EF_INQUEUE;
588203004Sgshapiro		(void) dropenvelope(e, splitenv != NULL, true);
58938032Speter		for (ee = splitenv; ee != NULL; ee = ee->e_sibling)
59038032Speter		{
59138032Speter			if (ee->e_nrcpts > 0)
59238032Speter				ee->e_flags |= EF_INQUEUE;
593203004Sgshapiro			(void) dropenvelope(ee, false, true);
59438032Speter		}
59538032Speter		return;
59638032Speter
59738032Speter	  case SM_FORK:
59838032Speter		if (e->e_xfp != NULL)
59990792Sgshapiro			(void) sm_io_flush(e->e_xfp, SM_TIME_DEFAULT);
60038032Speter
60164562Sgshapiro#if !HASFLOCK
60238032Speter		/*
60338032Speter		**  Since fcntl locking has the interesting semantic that
60438032Speter		**  the lock is owned by a process, not by an open file
60538032Speter		**  descriptor, we have to flush this to the queue, and
60638032Speter		**  then restart from scratch in the child.
60738032Speter		*/
60838032Speter
60938032Speter		{
61038032Speter			/* save id for future use */
61138032Speter			char *qid = e->e_id;
61238032Speter
61338032Speter			/* now drop the envelope in the parent */
61438032Speter			e->e_flags |= EF_INQUEUE;
615203004Sgshapiro			(void) dropenvelope(e, splitenv != NULL, false);
61638032Speter
61738032Speter			/* arrange to reacquire lock after fork */
61838032Speter			e->e_id = qid;
61938032Speter		}
62038032Speter
62138032Speter		for (ee = splitenv; ee != NULL; ee = ee->e_sibling)
62238032Speter		{
62338032Speter			/* save id for future use */
62438032Speter			char *qid = ee->e_id;
62538032Speter
62638032Speter			/* drop envelope in parent */
62738032Speter			ee->e_flags |= EF_INQUEUE;
628203004Sgshapiro			(void) dropenvelope(ee, false, false);
62938032Speter
63038032Speter			/* and save qid for reacquisition */
63138032Speter			ee->e_id = qid;
63238032Speter		}
633132943Sgshapiro
63464562Sgshapiro#endif /* !HASFLOCK */
63538032Speter
63664562Sgshapiro		/*
63764562Sgshapiro		**  Since the delivery may happen in a child and the parent
63864562Sgshapiro		**  does not wait, the parent may close the maps thereby
63964562Sgshapiro		**  removing any shared memory used by the map.  Therefore,
64064562Sgshapiro		**  close the maps now so the child will dynamically open
64164562Sgshapiro		**  them if necessary.
64264562Sgshapiro		*/
64364562Sgshapiro
64490792Sgshapiro		closemaps(false);
64564562Sgshapiro
64638032Speter		pid = fork();
64738032Speter		if (pid < 0)
64838032Speter		{
64964562Sgshapiro			syserr("deliver: fork 1");
65064562Sgshapiro#if HASFLOCK
65138032Speter			goto queueonly;
65264562Sgshapiro#else /* HASFLOCK */
65338032Speter			e->e_id = NULL;
65438032Speter			for (ee = splitenv; ee != NULL; ee = ee->e_sibling)
65538032Speter				ee->e_id = NULL;
65638032Speter			return;
65764562Sgshapiro#endif /* HASFLOCK */
65838032Speter		}
65938032Speter		else if (pid > 0)
66038032Speter		{
66164562Sgshapiro#if HASFLOCK
66238032Speter			/* be sure we leave the temp files to our child */
66338032Speter			/* close any random open files in the envelope */
66438032Speter			closexscript(e);
66538032Speter			if (e->e_dfp != NULL)
66690792Sgshapiro				(void) sm_io_close(e->e_dfp, SM_TIME_DEFAULT);
66738032Speter			e->e_dfp = NULL;
66838032Speter			e->e_flags &= ~EF_HAS_DF;
66938032Speter
67038032Speter			/* can't call unlockqueue to avoid unlink of xfp */
67138032Speter			if (e->e_lockfp != NULL)
67290792Sgshapiro				(void) sm_io_close(e->e_lockfp, SM_TIME_DEFAULT);
67364562Sgshapiro			else
67464562Sgshapiro				syserr("%s: sendall: null lockfp", e->e_id);
67538032Speter			e->e_lockfp = NULL;
67664562Sgshapiro#endif /* HASFLOCK */
67738032Speter
67838032Speter			/* make sure the parent doesn't own the envelope */
67938032Speter			e->e_id = NULL;
68038032Speter
68190792Sgshapiro#if USE_DOUBLE_FORK
68238032Speter			/* catch intermediate zombie */
68338032Speter			(void) waitfor(pid);
684363466Sgshapiro#endif
68538032Speter			return;
68638032Speter		}
68738032Speter
68877349Sgshapiro		/* Reset global flags */
68977349Sgshapiro		RestartRequest = NULL;
69090792Sgshapiro		RestartWorkGroup = false;
69177349Sgshapiro		ShutdownRequest = NULL;
69277349Sgshapiro		PendingSignal = 0;
69377349Sgshapiro
69466494Sgshapiro		/*
69590792Sgshapiro		**  Initialize exception stack and default exception
69690792Sgshapiro		**  handler for child process.
69790792Sgshapiro		*/
69890792Sgshapiro
69990792Sgshapiro		sm_exc_newthread(fatal_error);
70090792Sgshapiro
70190792Sgshapiro		/*
70266494Sgshapiro		**  Since we have accepted responsbility for the message,
70366494Sgshapiro		**  change the SIGTERM handler.  intsig() (the old handler)
70466494Sgshapiro		**  would remove the envelope if this was a command line
70566494Sgshapiro		**  message submission.
70666494Sgshapiro		*/
70766494Sgshapiro
70890792Sgshapiro		(void) sm_signal(SIGTERM, SIG_DFL);
70966494Sgshapiro
71090792Sgshapiro#if USE_DOUBLE_FORK
71138032Speter		/* double fork to avoid zombies */
71238032Speter		pid = fork();
71338032Speter		if (pid > 0)
71438032Speter			exit(EX_OK);
71564562Sgshapiro		save_errno = errno;
71690792Sgshapiro#endif /* USE_DOUBLE_FORK */
71738032Speter
71890792Sgshapiro		CurrentPid = getpid();
71990792Sgshapiro
72038032Speter		/* be sure we are immune from the terminal */
72138032Speter		disconnect(2, e);
72264562Sgshapiro		clearstats();
72338032Speter
72438032Speter		/* prevent parent from waiting if there was an error */
72538032Speter		if (pid < 0)
72638032Speter		{
72764562Sgshapiro			errno = save_errno;
72864562Sgshapiro			syserr("deliver: fork 2");
72964562Sgshapiro#if HASFLOCK
73038032Speter			e->e_flags |= EF_INQUEUE;
731363466Sgshapiro#else
73238032Speter			e->e_id = NULL;
733363466Sgshapiro#endif
73490792Sgshapiro			finis(true, true, ExitStat);
73538032Speter		}
73638032Speter
73738032Speter		/* be sure to give error messages in child */
73890792Sgshapiro		QuickAbort = false;
73938032Speter
74038032Speter		/*
74138032Speter		**  Close any cached connections.
74238032Speter		**
74338032Speter		**	We don't send the QUIT protocol because the parent
74438032Speter		**	still knows about the connection.
74538032Speter		**
74638032Speter		**	This should only happen when delivering an error
74738032Speter		**	message.
74838032Speter		*/
74938032Speter
75090792Sgshapiro		mci_flush(false, NULL);
75138032Speter
75264562Sgshapiro#if HASFLOCK
75338032Speter		break;
75464562Sgshapiro#else /* HASFLOCK */
75538032Speter
75638032Speter		/*
75738032Speter		**  Now reacquire and run the various queue files.
75838032Speter		*/
75938032Speter
76038032Speter		for (ee = splitenv; ee != NULL; ee = ee->e_sibling)
76138032Speter		{
76238032Speter			ENVELOPE *sibling = ee->e_sibling;
76338032Speter
76490792Sgshapiro			(void) dowork(ee->e_qgrp, ee->e_qdir, ee->e_id,
76590792Sgshapiro				      false, false, ee);
76638032Speter			ee->e_sibling = sibling;
76738032Speter		}
76890792Sgshapiro		(void) dowork(e->e_qgrp, e->e_qdir, e->e_id,
76990792Sgshapiro			      false, false, e);
77090792Sgshapiro		finis(true, true, ExitStat);
77164562Sgshapiro#endif /* HASFLOCK */
77238032Speter	}
77338032Speter
77438032Speter	sendenvelope(e, mode);
775203004Sgshapiro	(void) dropenvelope(e, true, true);
77638032Speter	for (ee = splitenv; ee != NULL; ee = ee->e_sibling)
77738032Speter	{
77838032Speter		CurEnv = ee;
77938032Speter		if (mode != SM_VERIFY)
78038032Speter			openxscript(ee);
78138032Speter		sendenvelope(ee, mode);
782203004Sgshapiro		(void) dropenvelope(ee, true, true);
78338032Speter	}
78438032Speter	CurEnv = e;
78538032Speter
78638032Speter	Verbose = oldverbose;
78738032Speter	if (mode == SM_FORK)
78890792Sgshapiro		finis(true, true, ExitStat);
78938032Speter}
79038032Speter
79164562Sgshapirostatic void
79238032Spetersendenvelope(e, mode)
79338032Speter	register ENVELOPE *e;
79438032Speter	int mode;
79538032Speter{
79638032Speter	register ADDRESS *q;
79738032Speter	bool didany;
79838032Speter
79938032Speter	if (tTd(13, 10))
80090792Sgshapiro		sm_dprintf("sendenvelope(%s) e_flags=0x%lx\n",
80190792Sgshapiro			   e->e_id == NULL ? "[NOQUEUE]" : e->e_id,
80290792Sgshapiro			   e->e_flags);
80338032Speter	if (LogLevel > 80)
80438032Speter		sm_syslog(LOG_DEBUG, e->e_id,
80564562Sgshapiro			  "sendenvelope, flags=0x%lx",
80664562Sgshapiro			  e->e_flags);
80738032Speter
80838032Speter	/*
80938032Speter	**  If we have had global, fatal errors, don't bother sending
81038032Speter	**  the message at all if we are in SMTP mode.  Local errors
81138032Speter	**  (e.g., a single address failing) will still cause the other
81238032Speter	**  addresses to be sent.
81338032Speter	*/
81438032Speter
81538032Speter	if (bitset(EF_FATALERRS, e->e_flags) &&
81638032Speter	    (OpMode == MD_SMTP || OpMode == MD_DAEMON))
81738032Speter	{
81838032Speter		e->e_flags |= EF_CLRQUEUE;
81938032Speter		return;
82038032Speter	}
82138032Speter
82290792Sgshapiro	/*
82390792Sgshapiro	**  Don't attempt deliveries if we want to bounce now
82490792Sgshapiro	**  or if deliver-by time is exceeded.
82590792Sgshapiro	*/
82690792Sgshapiro
82764562Sgshapiro	if (!bitset(EF_RESPONSE, e->e_flags) &&
82890792Sgshapiro	    (TimeOuts.to_q_return[e->e_timeoutclass] == NOW ||
82990792Sgshapiro	     (IS_DLVR_RETURN(e) && e->e_deliver_by > 0 &&
83090792Sgshapiro	      curtime() > e->e_ctime + e->e_deliver_by)))
83164562Sgshapiro		return;
83264562Sgshapiro
83338032Speter	/*
83438032Speter	**  Run through the list and send everything.
83538032Speter	**
83638032Speter	**	Set EF_GLOBALERRS so that error messages during delivery
83738032Speter	**	result in returned mail.
83838032Speter	*/
83938032Speter
84038032Speter	e->e_nsent = 0;
84138032Speter	e->e_flags |= EF_GLOBALERRS;
84264562Sgshapiro
84390792Sgshapiro	macdefine(&e->e_macro, A_PERM, macid("{envid}"), e->e_envid);
84490792Sgshapiro	macdefine(&e->e_macro, A_PERM, macid("{bodytype}"), e->e_bodytype);
84590792Sgshapiro	didany = false;
84638032Speter
84790792Sgshapiro	if (!bitset(EF_SPLIT, e->e_flags))
84890792Sgshapiro	{
84990792Sgshapiro		ENVELOPE *oldsib;
85090792Sgshapiro		ENVELOPE *ee;
85190792Sgshapiro
85290792Sgshapiro		/*
85390792Sgshapiro		**  Save old sibling and set it to NULL to avoid
85490792Sgshapiro		**  queueing up the same envelopes again.
85590792Sgshapiro		**  This requires that envelopes in that list have
85690792Sgshapiro		**  been take care of before (or at some other place).
85790792Sgshapiro		*/
85890792Sgshapiro
85990792Sgshapiro		oldsib = e->e_sibling;
86090792Sgshapiro		e->e_sibling = NULL;
86190792Sgshapiro		if (!split_by_recipient(e) &&
86290792Sgshapiro		    bitset(EF_FATALERRS, e->e_flags))
86390792Sgshapiro		{
86490792Sgshapiro			if (OpMode == MD_SMTP || OpMode == MD_DAEMON)
86590792Sgshapiro				e->e_flags |= EF_CLRQUEUE;
86690792Sgshapiro			return;
86790792Sgshapiro		}
86890792Sgshapiro		for (ee = e->e_sibling; ee != NULL; ee = ee->e_sibling)
86990792Sgshapiro			queueup(ee, false, true);
87090792Sgshapiro
87190792Sgshapiro		/* clean up */
87290792Sgshapiro		for (ee = e->e_sibling; ee != NULL; ee = ee->e_sibling)
87390792Sgshapiro		{
87490792Sgshapiro			/* now unlock the job */
87590792Sgshapiro			closexscript(ee);
87690792Sgshapiro			unlockqueue(ee);
87790792Sgshapiro
87890792Sgshapiro			/* this envelope is marked unused */
87990792Sgshapiro			if (ee->e_dfp != NULL)
88090792Sgshapiro			{
88190792Sgshapiro				(void) sm_io_close(ee->e_dfp, SM_TIME_DEFAULT);
88290792Sgshapiro				ee->e_dfp = NULL;
88390792Sgshapiro			}
88490792Sgshapiro			ee->e_id = NULL;
88590792Sgshapiro			ee->e_flags &= ~EF_HAS_DF;
88690792Sgshapiro		}
88790792Sgshapiro		e->e_sibling = oldsib;
88890792Sgshapiro	}
88990792Sgshapiro
89038032Speter	/* now run through the queue */
89138032Speter	for (q = e->e_sendqueue; q != NULL; q = q->q_next)
89238032Speter	{
89338032Speter#if XDEBUG
89438032Speter		char wbuf[MAXNAME + 20];
89538032Speter
896168515Sgshapiro		(void) sm_snprintf(wbuf, sizeof(wbuf), "sendall(%.*s)",
89790792Sgshapiro				   MAXNAME, q->q_paddr);
89838032Speter		checkfd012(wbuf);
89964562Sgshapiro#endif /* XDEBUG */
90038032Speter		if (mode == SM_VERIFY)
90138032Speter		{
90238032Speter			e->e_to = q->q_paddr;
90364562Sgshapiro			if (QS_IS_SENDABLE(q->q_state))
90438032Speter			{
90538032Speter				if (q->q_host != NULL && q->q_host[0] != '\0')
90638032Speter					message("deliverable: mailer %s, host %s, user %s",
90738032Speter						q->q_mailer->m_name,
90838032Speter						q->q_host,
90938032Speter						q->q_user);
91038032Speter				else
91138032Speter					message("deliverable: mailer %s, user %s",
91238032Speter						q->q_mailer->m_name,
91338032Speter						q->q_user);
91438032Speter			}
91538032Speter		}
91664562Sgshapiro		else if (QS_IS_OK(q->q_state))
91738032Speter		{
91838032Speter			/*
91938032Speter			**  Checkpoint the send list every few addresses
92038032Speter			*/
92138032Speter
92266494Sgshapiro			if (CheckpointInterval > 0 &&
92366494Sgshapiro			    e->e_nsent >= CheckpointInterval)
92438032Speter			{
92590792Sgshapiro				queueup(e, false, false);
92638032Speter				e->e_nsent = 0;
92738032Speter			}
92838032Speter			(void) deliver(e, q);
92990792Sgshapiro			didany = true;
93038032Speter		}
93138032Speter	}
93238032Speter	if (didany)
93338032Speter	{
93438032Speter		e->e_dtime = curtime();
93538032Speter		e->e_ntries++;
93638032Speter	}
93738032Speter
93838032Speter#if XDEBUG
93938032Speter	checkfd012("end of sendenvelope");
940363466Sgshapiro#endif
94138032Speter}
94290792Sgshapiro
94390792Sgshapiro#if REQUIRES_DIR_FSYNC
94490792Sgshapiro/*
94590792Sgshapiro**  SYNC_DIR -- fsync a directory based on a filename
94690792Sgshapiro**
94790792Sgshapiro**	Parameters:
94890792Sgshapiro**		filename -- path of file
94990792Sgshapiro**		panic -- panic?
95090792Sgshapiro**
95190792Sgshapiro**	Returns:
95290792Sgshapiro**		none
95390792Sgshapiro*/
95490792Sgshapiro
95590792Sgshapirovoid
95690792Sgshapirosync_dir(filename, panic)
95790792Sgshapiro	char *filename;
95890792Sgshapiro	bool panic;
95990792Sgshapiro{
96090792Sgshapiro	int dirfd;
96190792Sgshapiro	char *dirp;
96290792Sgshapiro	char dir[MAXPATHLEN];
96390792Sgshapiro
964110560Sgshapiro	if (!RequiresDirfsync)
965110560Sgshapiro		return;
966110560Sgshapiro
96790792Sgshapiro	/* filesystems which require the directory be synced */
96890792Sgshapiro	dirp = strrchr(filename, '/');
96990792Sgshapiro	if (dirp != NULL)
97090792Sgshapiro	{
971168515Sgshapiro		if (sm_strlcpy(dir, filename, sizeof(dir)) >= sizeof(dir))
97290792Sgshapiro			return;
97390792Sgshapiro		dir[dirp - filename] = '\0';
97490792Sgshapiro		dirp = dir;
97590792Sgshapiro	}
97690792Sgshapiro	else
97790792Sgshapiro		dirp = ".";
97890792Sgshapiro	dirfd = open(dirp, O_RDONLY, 0700);
97990792Sgshapiro	if (tTd(40,32))
98090792Sgshapiro		sm_syslog(LOG_INFO, NOQID, "sync_dir: %s: fsync(%d)",
98190792Sgshapiro			  dirp, dirfd);
98290792Sgshapiro	if (dirfd >= 0)
98390792Sgshapiro	{
98490792Sgshapiro		if (fsync(dirfd) < 0)
98590792Sgshapiro		{
98690792Sgshapiro			if (panic)
98790792Sgshapiro				syserr("!sync_dir: cannot fsync directory %s",
98890792Sgshapiro				       dirp);
98990792Sgshapiro			else if (LogLevel > 1)
99090792Sgshapiro				sm_syslog(LOG_ERR, NOQID,
99190792Sgshapiro					  "sync_dir: cannot fsync directory %s: %s",
99290792Sgshapiro					  dirp, sm_errstring(errno));
99390792Sgshapiro		}
99490792Sgshapiro		(void) close(dirfd);
99590792Sgshapiro	}
99690792Sgshapiro}
99790792Sgshapiro#endif /* REQUIRES_DIR_FSYNC */
99890792Sgshapiro/*
99938032Speter**  DUP_QUEUE_FILE -- duplicate a queue file into a split queue
100038032Speter**
100138032Speter**	Parameters:
100238032Speter**		e -- the existing envelope
100338032Speter**		ee -- the new envelope
100490792Sgshapiro**		type -- the queue file type (e.g., DATAFL_LETTER)
100538032Speter**
100638032Speter**	Returns:
100738032Speter**		none
100838032Speter*/
100938032Speter
101064562Sgshapirostatic void
101138032Speterdup_queue_file(e, ee, type)
101290792Sgshapiro	ENVELOPE *e, *ee;
101338032Speter	int type;
101438032Speter{
101564562Sgshapiro	char f1buf[MAXPATHLEN], f2buf[MAXPATHLEN];
101638032Speter
101738032Speter	ee->e_dfp = NULL;
101838032Speter	ee->e_xfp = NULL;
101964562Sgshapiro
102064562Sgshapiro	/*
102164562Sgshapiro	**  Make sure both are in the same directory.
102264562Sgshapiro	*/
102364562Sgshapiro
1024168515Sgshapiro	(void) sm_strlcpy(f1buf, queuename(e, type), sizeof(f1buf));
1025168515Sgshapiro	(void) sm_strlcpy(f2buf, queuename(ee, type), sizeof(f2buf));
1026102528Sgshapiro
1027102528Sgshapiro	/* Force the df to disk if it's not there yet */
1028102528Sgshapiro	if (type == DATAFL_LETTER && e->e_dfp != NULL &&
1029102528Sgshapiro	    sm_io_setinfo(e->e_dfp, SM_BF_COMMIT, NULL) < 0 &&
1030102528Sgshapiro	    errno != EINVAL)
1031102528Sgshapiro	{
1032102528Sgshapiro		syserr("!dup_queue_file: can't commit %s", f1buf);
1033102528Sgshapiro		/* NOTREACHED */
1034102528Sgshapiro	}
1035102528Sgshapiro
103638032Speter	if (link(f1buf, f2buf) < 0)
103738032Speter	{
103864562Sgshapiro		int save_errno = errno;
103938032Speter
104038032Speter		syserr("sendall: link(%s, %s)", f1buf, f2buf);
104164562Sgshapiro		if (save_errno == EEXIST)
104238032Speter		{
104338032Speter			if (unlink(f2buf) < 0)
104438032Speter			{
104538032Speter				syserr("!sendall: unlink(%s): permanent",
104690792Sgshapiro				       f2buf);
104764562Sgshapiro				/* NOTREACHED */
104838032Speter			}
104938032Speter			if (link(f1buf, f2buf) < 0)
105038032Speter			{
105138032Speter				syserr("!sendall: link(%s, %s): permanent",
105290792Sgshapiro				       f1buf, f2buf);
105364562Sgshapiro				/* NOTREACHED */
105438032Speter			}
105538032Speter		}
105638032Speter	}
105790792Sgshapiro	SYNC_DIR(f2buf, true);
105838032Speter}
105990792Sgshapiro/*
106038032Speter**  DOFORK -- do a fork, retrying a couple of times on failure.
106138032Speter**
106238032Speter**	This MUST be a macro, since after a vfork we are running
106338032Speter**	two processes on the same stack!!!
106438032Speter**
106538032Speter**	Parameters:
106638032Speter**		none.
106738032Speter**
106838032Speter**	Returns:
106938032Speter**		From a macro???  You've got to be kidding!
107038032Speter**
107138032Speter**	Side Effects:
107238032Speter**		Modifies the ==> LOCAL <== variable 'pid', leaving:
107338032Speter**			pid of child in parent, zero in child.
107438032Speter**			-1 on unrecoverable error.
107538032Speter**
107638032Speter**	Notes:
107738032Speter**		I'm awfully sorry this looks so awful.  That's
107838032Speter**		vfork for you.....
107938032Speter*/
108038032Speter
108164562Sgshapiro#define NFORKTRIES	5
108238032Speter
108364562Sgshapiro#ifndef FORK
108438032Speter# define FORK	fork
1085363466Sgshapiro#endif
108638032Speter
108764562Sgshapiro#define DOFORK(fORKfN) \
108838032Speter{\
108938032Speter	register int i;\
109038032Speter\
109138032Speter	for (i = NFORKTRIES; --i >= 0; )\
109238032Speter	{\
109338032Speter		pid = fORKfN();\
109438032Speter		if (pid >= 0)\
109538032Speter			break;\
109638032Speter		if (i > 0)\
109764562Sgshapiro			(void) sleep((unsigned) NFORKTRIES - i);\
109838032Speter	}\
109938032Speter}
110090792Sgshapiro/*
110138032Speter**  DOFORK -- simple fork interface to DOFORK.
110238032Speter**
110338032Speter**	Parameters:
110438032Speter**		none.
110538032Speter**
110638032Speter**	Returns:
110738032Speter**		pid of child in parent.
110838032Speter**		zero in child.
110938032Speter**		-1 on error.
111038032Speter**
111138032Speter**	Side Effects:
111238032Speter**		returns twice, once in parent and once in child.
111338032Speter*/
111438032Speter
111577349Sgshapiropid_t
111638032Speterdofork()
111738032Speter{
111838032Speter	register pid_t pid = -1;
111938032Speter
112038032Speter	DOFORK(fork);
112164562Sgshapiro	return pid;
112238032Speter}
112390792Sgshapiro
112490792Sgshapiro/*
112590792Sgshapiro**  COLONCMP -- compare host-signatures up to first ':' or EOS
112690792Sgshapiro**
112790792Sgshapiro**	This takes two strings which happen to be host-signatures and
112890792Sgshapiro**	compares them. If the lowest preference portions of the MX-RR's
112990792Sgshapiro**	match (up to ':' or EOS, whichever is first), then we have
113090792Sgshapiro**	match. This is used for coattail-piggybacking messages during
113190792Sgshapiro**	message delivery.
113290792Sgshapiro**	If the signatures are the same up to the first ':' the remainder of
113390792Sgshapiro**	the signatures are then compared with a normal strcmp(). This saves
113490792Sgshapiro**	re-examining the first part of the signatures.
113590792Sgshapiro**
113690792Sgshapiro**	Parameters:
113790792Sgshapiro**		a - first host-signature
113890792Sgshapiro**		b - second host-signature
113990792Sgshapiro**
114090792Sgshapiro**	Returns:
114190792Sgshapiro**		HS_MATCH_NO -- no "match".
114290792Sgshapiro**		HS_MATCH_FIRST -- "match" for the first MX preference
114390792Sgshapiro**			(up to the first colon (':')).
114490792Sgshapiro**		HS_MATCH_FULL -- match for the entire MX record.
114590792Sgshapiro**
114690792Sgshapiro**	Side Effects:
114790792Sgshapiro**		none.
114890792Sgshapiro*/
114990792Sgshapiro
115090792Sgshapiro#define HS_MATCH_NO	0
115190792Sgshapiro#define HS_MATCH_FIRST	1
115290792Sgshapiro#define HS_MATCH_FULL	2
115390792Sgshapiro
115490792Sgshapirostatic int
115590792Sgshapirocoloncmp(a, b)
115690792Sgshapiro	register const char *a;
115790792Sgshapiro	register const char *b;
115890792Sgshapiro{
115990792Sgshapiro	int ret = HS_MATCH_NO;
116090792Sgshapiro	int braclev = 0;
116190792Sgshapiro
116290792Sgshapiro	while (*a == *b++)
116390792Sgshapiro	{
116490792Sgshapiro		/* Need to account for IPv6 bracketed addresses */
116590792Sgshapiro		if (*a == '[')
116690792Sgshapiro			braclev++;
1167112810Sgshapiro		else if (*a == ']' && braclev > 0)
116890792Sgshapiro			braclev--;
116990792Sgshapiro		else if (*a == ':' && braclev <= 0)
117090792Sgshapiro		{
117190792Sgshapiro			ret = HS_MATCH_FIRST;
117290792Sgshapiro			a++;
117390792Sgshapiro			break;
117490792Sgshapiro		}
117590792Sgshapiro		else if (*a == '\0')
117690792Sgshapiro			return HS_MATCH_FULL; /* a full match */
117790792Sgshapiro		a++;
117890792Sgshapiro	}
117990792Sgshapiro	if (ret == HS_MATCH_NO &&
118090792Sgshapiro	    braclev <= 0 &&
118190792Sgshapiro	    ((*a == '\0' && *(b - 1) == ':') ||
118290792Sgshapiro	     (*a == ':' && *(b - 1) == '\0')))
118390792Sgshapiro		return HS_MATCH_FIRST;
118490792Sgshapiro	if (ret == HS_MATCH_FIRST && strcmp(a, b) == 0)
118590792Sgshapiro		return HS_MATCH_FULL;
118690792Sgshapiro
118790792Sgshapiro	return ret;
118890792Sgshapiro}
1189132943Sgshapiro
119090792Sgshapiro/*
1191132943Sgshapiro**  SHOULD_TRY_FBSH -- Should try FallbackSmartHost?
1192132943Sgshapiro**
1193132943Sgshapiro**	Parameters:
1194132943Sgshapiro**		e -- envelope
1195132943Sgshapiro**		tried_fallbacksmarthost -- has been tried already? (in/out)
1196132943Sgshapiro**		hostbuf -- buffer for hostname (expand FallbackSmartHost) (out)
1197132943Sgshapiro**		hbsz -- size of hostbuf
1198132943Sgshapiro**		status -- current delivery status
1199132943Sgshapiro**
1200132943Sgshapiro**	Returns:
1201132943Sgshapiro**		true iff FallbackSmartHost should be tried.
1202132943Sgshapiro*/
1203132943Sgshapiro
1204168515Sgshapirostatic bool should_try_fbsh __P((ENVELOPE *, bool *, char *, size_t, int));
1205168515Sgshapiro
1206132943Sgshapirostatic bool
1207132943Sgshapiroshould_try_fbsh(e, tried_fallbacksmarthost, hostbuf, hbsz, status)
1208132943Sgshapiro	ENVELOPE *e;
1209132943Sgshapiro	bool *tried_fallbacksmarthost;
1210132943Sgshapiro	char *hostbuf;
1211132943Sgshapiro	size_t hbsz;
1212132943Sgshapiro	int status;
1213132943Sgshapiro{
1214132943Sgshapiro	/*
1215157001Sgshapiro	**  If the host was not found or a temporary failure occurred
1216157001Sgshapiro	**  and a FallbackSmartHost is defined (and we have not yet
1217157001Sgshapiro	**  tried it), then make one last try with it as the host.
1218132943Sgshapiro	*/
1219132943Sgshapiro
1220157001Sgshapiro	if ((status == EX_NOHOST || status == EX_TEMPFAIL) &&
1221157001Sgshapiro	    FallbackSmartHost != NULL && !*tried_fallbacksmarthost)
1222132943Sgshapiro	{
1223132943Sgshapiro		*tried_fallbacksmarthost = true;
1224132943Sgshapiro		expand(FallbackSmartHost, hostbuf, hbsz, e);
1225132943Sgshapiro		if (!wordinclass(hostbuf, 'w'))
1226132943Sgshapiro		{
1227132943Sgshapiro			if (tTd(11, 1))
1228132943Sgshapiro				sm_dprintf("one last try with FallbackSmartHost %s\n",
1229132943Sgshapiro					   hostbuf);
1230132943Sgshapiro			return true;
1231132943Sgshapiro		}
1232132943Sgshapiro	}
1233132943Sgshapiro	return false;
1234132943Sgshapiro}
1235285229Sgshapiro
1236132943Sgshapiro/*
123738032Speter**  DELIVER -- Deliver a message to a list of addresses.
123838032Speter**
123938032Speter**	This routine delivers to everyone on the same host as the
124038032Speter**	user on the head of the list.  It is clever about mailers
124138032Speter**	that don't handle multiple users.  It is NOT guaranteed
124238032Speter**	that it will deliver to all these addresses however -- so
1243363466Sgshapiro**	deliver should be called once for each address on the list.
124490792Sgshapiro**	Deliver tries to be as opportunistic as possible about piggybacking
124590792Sgshapiro**	messages. Some definitions to make understanding easier follow below.
124690792Sgshapiro**	Piggybacking occurs when an existing connection to a mail host can
124790792Sgshapiro**	be used to send the same message to more than one recipient at the
124890792Sgshapiro**	same time. So "no piggybacking" means one message for one recipient
124990792Sgshapiro**	per connection. "Intentional piggybacking" happens when the
125090792Sgshapiro**	recipients' host address (not the mail host address) is used to
125190792Sgshapiro**	attempt piggybacking. Recipients with the same host address
125290792Sgshapiro**	have the same mail host. "Coincidental piggybacking" relies on
125390792Sgshapiro**	piggybacking based on all the mail host addresses in the MX-RR. This
125490792Sgshapiro**	is "coincidental" in the fact it could not be predicted until the
125590792Sgshapiro**	MX Resource Records for the hosts were obtained and examined. For
125690792Sgshapiro**	example (preference order and equivalence is important, not values):
125790792Sgshapiro**		domain1 IN MX 10 mxhost-A
125890792Sgshapiro**			IN MX 20 mxhost-B
125990792Sgshapiro**		domain2 IN MX  4 mxhost-A
126090792Sgshapiro**			IN MX  8 mxhost-B
126190792Sgshapiro**	Domain1 and domain2 can piggyback the same message to mxhost-A or
126290792Sgshapiro**	mxhost-B (if mxhost-A cannot be reached).
126390792Sgshapiro**	"Coattail piggybacking" relaxes the strictness of "coincidental
126490792Sgshapiro**	piggybacking" in the hope that most significant (lowest value)
126590792Sgshapiro**	MX preference host(s) can create more piggybacking. For example
126690792Sgshapiro**	(again, preference order and equivalence is important, not values):
126790792Sgshapiro**		domain3 IN MX 100 mxhost-C
126890792Sgshapiro**			IN MX 100 mxhost-D
126990792Sgshapiro**			IN MX 200 mxhost-E
127090792Sgshapiro**		domain4 IN MX  50 mxhost-C
127190792Sgshapiro**			IN MX  50 mxhost-D
127290792Sgshapiro**			IN MX  80 mxhost-F
127390792Sgshapiro**	A message for domain3 and domain4 can piggyback to mxhost-C if mxhost-C
127490792Sgshapiro**	is available. Same with mxhost-D because in both RR's the preference
127590792Sgshapiro**	value is the same as mxhost-C, respectively.
127690792Sgshapiro**	So deliver attempts coattail piggybacking when possible. If the
127790792Sgshapiro**	first MX preference level hosts cannot be used then the piggybacking
127890792Sgshapiro**	reverts to coincidental piggybacking. Using the above example you
127990792Sgshapiro**	cannot deliver to mxhost-F for domain3 regardless of preference value.
128090792Sgshapiro**	("Coattail" from "riding on the coattails of your predecessor" meaning
128190792Sgshapiro**	gaining benefit from a predecessor effort with no or little addition
128290792Sgshapiro**	effort. The predecessor here being the preceding MX RR).
128338032Speter**
128438032Speter**	Parameters:
128538032Speter**		e -- the envelope to deliver.
128638032Speter**		firstto -- head of the address list to deliver to.
128738032Speter**
128838032Speter**	Returns:
128938032Speter**		zero -- successfully delivered.
129038032Speter**		else -- some failure, see ExitStat for more info.
129138032Speter**
129238032Speter**	Side Effects:
129338032Speter**		The standard input is passed off to someone.
129438032Speter*/
129538032Speter
129664562Sgshapirostatic int
129738032Speterdeliver(e, firstto)
129838032Speter	register ENVELOPE *e;
129938032Speter	ADDRESS *firstto;
130038032Speter{
130138032Speter	char *host;			/* host being sent to */
130238032Speter	char *user;			/* user being sent to */
130338032Speter	char **pvp;
130438032Speter	register char **mvp;
130538032Speter	register char *p;
130638032Speter	register MAILER *m;		/* mailer for this recipient */
130738032Speter	ADDRESS *volatile ctladdr;
130890792Sgshapiro#if HASSETUSERCONTEXT
130938032Speter	ADDRESS *volatile contextaddr = NULL;
1310363466Sgshapiro#endif
131138032Speter	register MCI *volatile mci;
131290792Sgshapiro	register ADDRESS *SM_NONVOLATILE to = firstto;
131390792Sgshapiro	volatile bool clever = false;	/* running user smtp to this mailer */
131438032Speter	ADDRESS *volatile tochain = NULL; /* users chain in this mailer call */
131538032Speter	int rcode;			/* response code */
131690792Sgshapiro	SM_NONVOLATILE int lmtp_rcode = EX_OK;
131790792Sgshapiro	SM_NONVOLATILE int nummxhosts = 0; /* number of MX hosts available */
131890792Sgshapiro	SM_NONVOLATILE int hostnum = 0;	/* current MX host index */
131938032Speter	char *firstsig;			/* signature of firstto */
132090792Sgshapiro	volatile pid_t pid = -1;
132138032Speter	char *volatile curhost;
132290792Sgshapiro	SM_NONVOLATILE unsigned short port = 0;
132390792Sgshapiro	SM_NONVOLATILE time_t enough = 0;
132464562Sgshapiro#if NETUNIX
132590792Sgshapiro	char *SM_NONVOLATILE mux_path = NULL;	/* path to UNIX domain socket */
1326363466Sgshapiro#endif
132738032Speter	time_t xstart;
132838032Speter	bool suidwarn;
132938032Speter	bool anyok;			/* at least one address was OK */
133090792Sgshapiro	SM_NONVOLATILE bool goodmxfound = false; /* at least one MX was OK */
133164562Sgshapiro	bool ovr;
133290792Sgshapiro	bool quarantine;
1333363466Sgshapiro#if STARTTLS
1334363466Sgshapiro	/* 0: try TLS, 1: try without TLS again, >1: don't try again */
1335363466Sgshapiro	int tlsstate;
1336363466Sgshapiro# if DANE
1337363466Sgshapiro	dane_vrfy_ctx_T	dane_vrfy_ctx;
1338363466Sgshapiro	STAB *ste;
1339363466Sgshapiro# endif
1340363466Sgshapiro#endif
1341363466Sgshapiro#if STARTTLS || SASL
1342363466Sgshapiro	int dotpos;
1343363466Sgshapiro
1344363466Sgshapiro# define RM_TRAIL_DOT(name)				\
1345363466Sgshapiro	do {						\
1346363466Sgshapiro		dotpos = strlen(name) - 1;		\
1347363466Sgshapiro		if (dotpos >= 0)			\
1348363466Sgshapiro		{					\
1349363466Sgshapiro			if (name[dotpos] == '.')	\
1350363466Sgshapiro				name[dotpos] = '\0';	\
1351363466Sgshapiro			else				\
1352363466Sgshapiro				dotpos = -1;		\
1353363466Sgshapiro		}					\
1354363466Sgshapiro	} while (0)
1355363466Sgshapiro
1356363466Sgshapiro# define FIX_TRAIL_DOT(name)				\
1357363466Sgshapiro	do {						\
1358363466Sgshapiro		if (dotpos >= 0)			\
1359363466Sgshapiro			name[dotpos] = '.';		\
1360363466Sgshapiro	} while (0)
1361363466Sgshapiro
1362363466Sgshapiro#endif
136364562Sgshapiro	int strsize;
136464562Sgshapiro	int rcptcount;
136590792Sgshapiro	int ret;
136664562Sgshapiro	static int tobufsize = 0;
136764562Sgshapiro	static char *tobuf = NULL;
136890792Sgshapiro	char *rpath;	/* translated return path */
136938032Speter	int mpvect[2];
137038032Speter	int rpvect[2];
137164562Sgshapiro	char *mxhosts[MAXMXHOSTS + 1];
137264562Sgshapiro	char *pv[MAXPV + 1];
137338032Speter	char buf[MAXNAME + 1];
137498121Sgshapiro	char cbuf[MAXPATHLEN];
137538032Speter
137638032Speter	errno = 0;
1377168515Sgshapiro	SM_REQUIRE(firstto != NULL);	/* same as to */
137864562Sgshapiro	if (!QS_IS_OK(to->q_state))
137964562Sgshapiro		return 0;
138038032Speter
138138032Speter	suidwarn = geteuid() == 0;
138238032Speter
1383168515Sgshapiro	SM_REQUIRE(e != NULL);
138438032Speter	m = to->q_mailer;
138538032Speter	host = to->q_host;
138638032Speter	CurEnv = e;			/* just in case */
138738032Speter	e->e_statmsg = NULL;
138838032Speter	SmtpError[0] = '\0';
138938032Speter	xstart = curtime();
1390363466Sgshapiro#if STARTTLS
1391363466Sgshapiro	tlsstate = 0;
1392363466Sgshapiro# if DANE
1393363466Sgshapiro	memset(&dane_vrfy_ctx, '\0', sizeof(dane_vrfy_ctx));
1394363466Sgshapiro	ste = NULL;
1395363466Sgshapiro# endif
1396363466Sgshapiro#endif
139738032Speter
139838032Speter	if (tTd(10, 1))
139990792Sgshapiro		sm_dprintf("\n--deliver, id=%s, mailer=%s, host=`%s', first user=`%s'\n",
140038032Speter			e->e_id, m->m_name, host, to->q_user);
140138032Speter	if (tTd(10, 100))
140290792Sgshapiro		printopenfds(false);
140338032Speter
140438032Speter	/*
140590792Sgshapiro	**  Clear {client_*} macros if this is a bounce message to
140638032Speter	**  prevent rejection by check_compat ruleset.
140738032Speter	*/
140864562Sgshapiro
140938032Speter	if (bitset(EF_RESPONSE, e->e_flags))
141038032Speter	{
141190792Sgshapiro		macdefine(&e->e_macro, A_PERM, macid("{client_name}"), "");
1412132943Sgshapiro		macdefine(&e->e_macro, A_PERM, macid("{client_ptr}"), "");
141390792Sgshapiro		macdefine(&e->e_macro, A_PERM, macid("{client_addr}"), "");
141490792Sgshapiro		macdefine(&e->e_macro, A_PERM, macid("{client_port}"), "");
141590792Sgshapiro		macdefine(&e->e_macro, A_PERM, macid("{client_resolve}"), "");
141638032Speter	}
141764562Sgshapiro
141890792Sgshapiro	SM_TRY
141990792Sgshapiro	{
142090792Sgshapiro	ADDRESS *skip_back = NULL;
142190792Sgshapiro
142238032Speter	/*
142338032Speter	**  Do initial argv setup.
142438032Speter	**	Insert the mailer name.  Notice that $x expansion is
142538032Speter	**	NOT done on the mailer name.  Then, if the mailer has
142638032Speter	**	a picky -f flag, we insert it as appropriate.  This
142738032Speter	**	code does not check for 'pv' overflow; this places a
142838032Speter	**	manifest lower limit of 4 for MAXPV.
142938032Speter	**		The from address rewrite is expected to make
143038032Speter	**		the address relative to the other end.
143138032Speter	*/
143238032Speter
143338032Speter	/* rewrite from address, using rewriting rules */
143438032Speter	rcode = EX_OK;
1435168515Sgshapiro	SM_ASSERT(e->e_from.q_mailer != NULL);
143638032Speter	if (bitnset(M_UDBENVELOPE, e->e_from.q_mailer->m_flags))
143738032Speter		p = e->e_sender;
143838032Speter	else
143938032Speter		p = e->e_from.q_paddr;
144090792Sgshapiro	rpath = remotename(p, m, RF_SENDERADDR|RF_CANONICAL, &rcode, e);
1441285229Sgshapiro	if (rcode != EX_OK && bitnset(M_xSMTP, m->m_flags))
1442285229Sgshapiro		goto cleanup;
1443203004Sgshapiro	if (strlen(rpath) > MAXNAME)
144438032Speter	{
144590792Sgshapiro		rpath = shortenstring(rpath, MAXSHORTSTR);
144690792Sgshapiro
144790792Sgshapiro		/* avoid bogus errno */
144890792Sgshapiro		errno = 0;
144990792Sgshapiro		syserr("remotename: huge return path %s", rpath);
145038032Speter	}
145190792Sgshapiro	rpath = sm_rpool_strdup_x(e->e_rpool, rpath);
145290792Sgshapiro	macdefine(&e->e_macro, A_PERM, 'g', rpath);
145390792Sgshapiro	macdefine(&e->e_macro, A_PERM, 'h', host);
145438032Speter	Errors = 0;
145538032Speter	pvp = pv;
145638032Speter	*pvp++ = m->m_argv[0];
145738032Speter
1458132943Sgshapiro	/* ignore long term host status information if mailer flag W is set */
1459132943Sgshapiro	if (bitnset(M_NOHOSTSTAT, m->m_flags))
1460132943Sgshapiro		IgnoreHostStatus = true;
1461132943Sgshapiro
146238032Speter	/* insert -f or -r flag as appropriate */
146364562Sgshapiro	if (FromFlag &&
146464562Sgshapiro	    (bitnset(M_FOPT, m->m_flags) ||
146564562Sgshapiro	     bitnset(M_ROPT, m->m_flags)))
146638032Speter	{
146738032Speter		if (bitnset(M_FOPT, m->m_flags))
146838032Speter			*pvp++ = "-f";
146938032Speter		else
147038032Speter			*pvp++ = "-r";
147190792Sgshapiro		*pvp++ = rpath;
147238032Speter	}
147338032Speter
147438032Speter	/*
147538032Speter	**  Append the other fixed parts of the argv.  These run
147638032Speter	**  up to the first entry containing "$u".  There can only
147738032Speter	**  be one of these, and there are only a few more slots
147838032Speter	**  in the pv after it.
147938032Speter	*/
148038032Speter
148138032Speter	for (mvp = m->m_argv; (p = *++mvp) != NULL; )
148238032Speter	{
148338032Speter		/* can't use strchr here because of sign extension problems */
148438032Speter		while (*p != '\0')
148538032Speter		{
148638032Speter			if ((*p++ & 0377) == MACROEXPAND)
148738032Speter			{
148838032Speter				if (*p == 'u')
148938032Speter					break;
149038032Speter			}
149138032Speter		}
149238032Speter
149338032Speter		if (*p != '\0')
149438032Speter			break;
149538032Speter
149638032Speter		/* this entry is safe -- go ahead and process it */
1497168515Sgshapiro		expand(*mvp, buf, sizeof(buf), e);
149890792Sgshapiro		*pvp++ = sm_rpool_strdup_x(e->e_rpool, buf);
149938032Speter		if (pvp >= &pv[MAXPV - 3])
150038032Speter		{
150164562Sgshapiro			syserr("554 5.3.5 Too many parameters to %s before $u",
150264562Sgshapiro			       pv[0]);
150390792Sgshapiro			rcode = -1;
150490792Sgshapiro			goto cleanup;
150538032Speter		}
150638032Speter	}
150738032Speter
150838032Speter	/*
150938032Speter	**  If we have no substitution for the user name in the argument
151038032Speter	**  list, we know that we must supply the names otherwise -- and
151138032Speter	**  SMTP is the answer!!
151238032Speter	*/
151338032Speter
151438032Speter	if (*mvp == NULL)
151538032Speter	{
151673188Sgshapiro		/* running LMTP or SMTP */
151790792Sgshapiro		clever = true;
151838032Speter		*pvp = NULL;
1519285229Sgshapiro		setbitn(M_xSMTP, m->m_flags);
152038032Speter	}
152173188Sgshapiro	else if (bitnset(M_LMTP, m->m_flags))
152273188Sgshapiro	{
152373188Sgshapiro		/* not running LMTP */
152473188Sgshapiro		sm_syslog(LOG_ERR, NULL,
152573188Sgshapiro			  "Warning: mailer %s: LMTP flag (F=z) turned off",
152673188Sgshapiro			  m->m_name);
152773188Sgshapiro		clrbitn(M_LMTP, m->m_flags);
152873188Sgshapiro	}
152938032Speter
153038032Speter	/*
153138032Speter	**  At this point *mvp points to the argument with $u.  We
153238032Speter	**  run through our address list and append all the addresses
153338032Speter	**  we can.  If we run out of space, do not fret!  We can
153438032Speter	**  always send another copy later.
153538032Speter	*/
153638032Speter
153764562Sgshapiro	e->e_to = NULL;
153864562Sgshapiro	strsize = 2;
153964562Sgshapiro	rcptcount = 0;
154090792Sgshapiro	ctladdr = NULL;
154190792Sgshapiro	if (firstto->q_signature == NULL)
154290792Sgshapiro		firstto->q_signature = hostsignature(firstto->q_mailer,
1543363466Sgshapiro						     firstto->q_host,
1544363466Sgshapiro						     firstto->q_flags & QSECURE);
154590792Sgshapiro	firstsig = firstto->q_signature;
154664562Sgshapiro
154738032Speter	for (; to != NULL; to = to->q_next)
154838032Speter	{
154938032Speter		/* avoid sending multiple recipients to dumb mailers */
155064562Sgshapiro		if (tochain != NULL && !bitnset(M_MUSER, m->m_flags))
155164562Sgshapiro			break;
155238032Speter
155338032Speter		/* if already sent or not for this host, don't send */
155490792Sgshapiro		if (!QS_IS_OK(to->q_state)) /* already sent; look at next */
155538032Speter			continue;
155638032Speter
155790792Sgshapiro		/*
155890792Sgshapiro		**  Must be same mailer to keep grouping rcpts.
155990792Sgshapiro		**  If mailers don't match: continue; sendqueue is not
156090792Sgshapiro		**  sorted by mailers, so don't break;
156190792Sgshapiro		*/
156290792Sgshapiro
156390792Sgshapiro		if (to->q_mailer != firstto->q_mailer)
156490792Sgshapiro			continue;
156590792Sgshapiro
156690792Sgshapiro		if (to->q_signature == NULL) /* for safety */
156790792Sgshapiro			to->q_signature = hostsignature(to->q_mailer,
1568363466Sgshapiro							to->q_host,
1569363466Sgshapiro							to->q_flags & QSECURE);
157090792Sgshapiro
157190792Sgshapiro		/*
157290792Sgshapiro		**  This is for coincidental and tailcoat piggybacking messages
157390792Sgshapiro		**  to the same mail host. While the signatures are identical
157490792Sgshapiro		**  (that's the MX-RR's are identical) we can do coincidental
157590792Sgshapiro		**  piggybacking. We try hard for coattail piggybacking
157690792Sgshapiro		**  with the same mail host when the next recipient has the
157790792Sgshapiro		**  same host at lowest preference. It may be that this
157890792Sgshapiro		**  won't work out, so 'skip_back' is maintained if a backup
157990792Sgshapiro		**  to coincidental piggybacking or full signature must happen.
158090792Sgshapiro		*/
158190792Sgshapiro
158290792Sgshapiro		ret = firstto == to ? HS_MATCH_FULL :
158390792Sgshapiro				      coloncmp(to->q_signature, firstsig);
158490792Sgshapiro		if (ret == HS_MATCH_FULL)
158590792Sgshapiro			skip_back = to;
158690792Sgshapiro		else if (ret == HS_MATCH_NO)
158764562Sgshapiro			break;
158864562Sgshapiro
158990792Sgshapiro		if (!clever)
159090792Sgshapiro		{
159190792Sgshapiro			/* avoid overflowing tobuf */
159290792Sgshapiro			strsize += strlen(to->q_paddr) + 1;
159390792Sgshapiro			if (strsize > TOBUFSIZE)
159490792Sgshapiro				break;
159590792Sgshapiro		}
159690792Sgshapiro
159764562Sgshapiro		if (++rcptcount > to->q_mailer->m_maxrcpt)
159864562Sgshapiro			break;
159938032Speter
160038032Speter		if (tTd(10, 1))
160138032Speter		{
160290792Sgshapiro			sm_dprintf("\nsend to ");
1603132943Sgshapiro			printaddr(sm_debug_file(), to, false);
160438032Speter		}
160538032Speter
160638032Speter		/* compute effective uid/gid when sending */
160738032Speter		if (bitnset(M_RUNASRCPT, to->q_mailer->m_flags))
1608363466Sgshapiro#if HASSETUSERCONTEXT
160938032Speter			contextaddr = ctladdr = getctladdr(to);
1610363466Sgshapiro#else
161190792Sgshapiro			ctladdr = getctladdr(to);
1612363466Sgshapiro#endif
161338032Speter
161438032Speter		if (tTd(10, 2))
161538032Speter		{
161690792Sgshapiro			sm_dprintf("ctladdr=");
1617132943Sgshapiro			printaddr(sm_debug_file(), ctladdr, false);
161838032Speter		}
161938032Speter
162038032Speter		user = to->q_user;
162138032Speter		e->e_to = to->q_paddr;
162238032Speter
162338032Speter		/*
162438032Speter		**  Check to see that these people are allowed to
162538032Speter		**  talk to each other.
162666494Sgshapiro		**  Check also for overflow of e_msgsize.
162738032Speter		*/
162838032Speter
162966494Sgshapiro		if (m->m_maxsize != 0 &&
163066494Sgshapiro		    (e->e_msgsize > m->m_maxsize || e->e_msgsize < 0))
163138032Speter		{
163238032Speter			e->e_flags |= EF_NO_BODY_RETN;
163338032Speter			if (bitnset(M_LOCALMAILER, to->q_mailer->m_flags))
163438032Speter				to->q_status = "5.2.3";
163538032Speter			else
163638032Speter				to->q_status = "5.3.4";
163790792Sgshapiro
163864562Sgshapiro			/* set to->q_rstatus = NULL; or to the following? */
163964562Sgshapiro			usrerrenh(to->q_status,
164064562Sgshapiro				  "552 Message is too large; %ld bytes max",
164164562Sgshapiro				  m->m_maxsize);
164290792Sgshapiro			markfailure(e, to, NULL, EX_UNAVAILABLE, false);
164364562Sgshapiro			giveresponse(EX_UNAVAILABLE, to->q_status, m,
164490792Sgshapiro				     NULL, ctladdr, xstart, e, to);
164538032Speter			continue;
164638032Speter		}
164773188Sgshapiro		SM_SET_H_ERRNO(0);
164890792Sgshapiro		ovr = true;
164938032Speter
165038032Speter		/* do config file checking of compatibility */
165190792Sgshapiro		quarantine = (e->e_quarmsg != NULL);
165264562Sgshapiro		rcode = rscheck("check_compat", e->e_from.q_paddr, to->q_paddr,
1653102528Sgshapiro				e, RSF_RMCOMM|RSF_COUNT, 3, NULL,
1654285229Sgshapiro				e->e_id, NULL, NULL);
165538032Speter		if (rcode == EX_OK)
165638032Speter		{
165742575Speter			/* do in-code checking if not discarding */
165842575Speter			if (!bitset(EF_DISCARD, e->e_flags))
165964562Sgshapiro			{
166042575Speter				rcode = checkcompat(to, e);
166190792Sgshapiro				ovr = false;
166264562Sgshapiro			}
166338032Speter		}
166438032Speter		if (rcode != EX_OK)
166538032Speter		{
166664562Sgshapiro			markfailure(e, to, NULL, rcode, ovr);
166764562Sgshapiro			giveresponse(rcode, to->q_status, m,
166890792Sgshapiro				     NULL, ctladdr, xstart, e, to);
166938032Speter			continue;
167038032Speter		}
167190792Sgshapiro		if (!quarantine && e->e_quarmsg != NULL)
167290792Sgshapiro		{
167390792Sgshapiro			/*
167490792Sgshapiro			**  check_compat or checkcompat() has tried
167590792Sgshapiro			**  to quarantine but that isn't supported.
167690792Sgshapiro			**  Revert the attempt.
167790792Sgshapiro			*/
167890792Sgshapiro
167990792Sgshapiro			e->e_quarmsg = NULL;
168090792Sgshapiro			macdefine(&e->e_macro, A_PERM,
168190792Sgshapiro				  macid("{quarantine}"), "");
168290792Sgshapiro		}
168342575Speter		if (bitset(EF_DISCARD, e->e_flags))
168442575Speter		{
168542575Speter			if (tTd(10, 5))
168642575Speter			{
168790792Sgshapiro				sm_dprintf("deliver: discarding recipient ");
1688132943Sgshapiro				printaddr(sm_debug_file(), to, false);
168942575Speter			}
169038032Speter
169164562Sgshapiro			/* pretend the message was sent */
169264562Sgshapiro			/* XXX should we log something here? */
169364562Sgshapiro			to->q_state = QS_DISCARDED;
169464562Sgshapiro
169542575Speter			/*
169642575Speter			**  Remove discard bit to prevent discard of
169764562Sgshapiro			**  future recipients.  This is safe because the
169864562Sgshapiro			**  true "global discard" has been handled before
169964562Sgshapiro			**  we get here.
170042575Speter			*/
170164562Sgshapiro
170242575Speter			e->e_flags &= ~EF_DISCARD;
170342575Speter			continue;
170442575Speter		}
170542575Speter
170638032Speter		/*
170738032Speter		**  Strip quote bits from names if the mailer is dumb
170838032Speter		**	about them.
170938032Speter		*/
171038032Speter
171138032Speter		if (bitnset(M_STRIPQ, m->m_flags))
171238032Speter		{
171338032Speter			stripquotes(user);
171438032Speter			stripquotes(host);
171538032Speter		}
1716132943Sgshapiro
1717110560Sgshapiro		/*
1718141858Sgshapiro		**  Strip all leading backslashes if requested and the
1719110560Sgshapiro		**  next character is alphanumerical (the latter can
1720110560Sgshapiro		**  probably relaxed a bit, see RFC2821).
1721110560Sgshapiro		*/
172238032Speter
1723110560Sgshapiro		if (bitnset(M_STRIPBACKSL, m->m_flags) && user[0] == '\\')
1724110560Sgshapiro			stripbackslash(user);
1725110560Sgshapiro
172638032Speter		/* hack attack -- delivermail compatibility */
172738032Speter		if (m == ProgMailer && *user == '|')
172838032Speter			user++;
172938032Speter
173038032Speter		/*
173138032Speter		**  If an error message has already been given, don't
173238032Speter		**	bother to send to this address.
173338032Speter		**
173438032Speter		**	>>>>>>>>>> This clause assumes that the local mailer
173538032Speter		**	>> NOTE >> cannot do any further aliasing; that
173638032Speter		**	>>>>>>>>>> function is subsumed by sendmail.
173738032Speter		*/
173838032Speter
173964562Sgshapiro		if (!QS_IS_OK(to->q_state))
174038032Speter			continue;
174138032Speter
174238032Speter		/*
174338032Speter		**  See if this user name is "special".
174438032Speter		**	If the user name has a slash in it, assume that this
174538032Speter		**	is a file -- send it off without further ado.  Note
174638032Speter		**	that this type of addresses is not processed along
174738032Speter		**	with the others, so we fudge on the To person.
174838032Speter		*/
174938032Speter
175038032Speter		if (strcmp(m->m_mailer, "[FILE]") == 0)
175138032Speter		{
175290792Sgshapiro			macdefine(&e->e_macro, A_PERM, 'u', user);
175338032Speter			p = to->q_home;
175438032Speter			if (p == NULL && ctladdr != NULL)
175538032Speter				p = ctladdr->q_home;
175690792Sgshapiro			macdefine(&e->e_macro, A_PERM, 'z', p);
1757168515Sgshapiro			expand(m->m_argv[1], buf, sizeof(buf), e);
175838032Speter			if (strlen(buf) > 0)
175938032Speter				rcode = mailfile(buf, m, ctladdr, SFF_CREAT, e);
176038032Speter			else
176138032Speter			{
176238032Speter				syserr("empty filename specification for mailer %s",
176338032Speter				       m->m_name);
176438032Speter				rcode = EX_CONFIG;
176538032Speter			}
176664562Sgshapiro			giveresponse(rcode, to->q_status, m, NULL,
176790792Sgshapiro				     ctladdr, xstart, e, to);
176890792Sgshapiro			markfailure(e, to, NULL, rcode, true);
176938032Speter			e->e_nsent++;
177038032Speter			if (rcode == EX_OK)
177138032Speter			{
177264562Sgshapiro				to->q_state = QS_SENT;
177338032Speter				if (bitnset(M_LOCALMAILER, m->m_flags) &&
177438032Speter				    bitset(QPINGONSUCCESS, to->q_flags))
177538032Speter				{
177638032Speter					to->q_flags |= QDELIVERED;
177738032Speter					to->q_status = "2.1.5";
177890792Sgshapiro					(void) sm_io_fprintf(e->e_xfp,
177990792Sgshapiro							     SM_TIME_DEFAULT,
178090792Sgshapiro							     "%s... Successfully delivered\n",
178190792Sgshapiro							     to->q_paddr);
178238032Speter				}
178338032Speter			}
178438032Speter			to->q_statdate = curtime();
178590792Sgshapiro			markstats(e, to, STATS_NORMAL);
178638032Speter			continue;
178738032Speter		}
178838032Speter
178938032Speter		/*
179038032Speter		**  Address is verified -- add this user to mailer
179138032Speter		**  argv, and add it to the print list of recipients.
179238032Speter		*/
179338032Speter
179438032Speter		/* link together the chain of recipients */
179538032Speter		to->q_tchain = tochain;
179638032Speter		tochain = to;
179764562Sgshapiro		e->e_to = "[CHAIN]";
179864562Sgshapiro
179990792Sgshapiro		macdefine(&e->e_macro, A_PERM, 'u', user);  /* to user */
180038032Speter		p = to->q_home;
180138032Speter		if (p == NULL && ctladdr != NULL)
180238032Speter			p = ctladdr->q_home;
180390792Sgshapiro		macdefine(&e->e_macro, A_PERM, 'z', p);  /* user's home */
180438032Speter
180564562Sgshapiro		/* set the ${dsn_notify} macro if applicable */
180664562Sgshapiro		if (bitset(QHASNOTIFY, to->q_flags))
180764562Sgshapiro		{
180864562Sgshapiro			char notify[MAXLINE];
180964562Sgshapiro
181064562Sgshapiro			notify[0] = '\0';
181164562Sgshapiro			if (bitset(QPINGONSUCCESS, to->q_flags))
181290792Sgshapiro				(void) sm_strlcat(notify, "SUCCESS,",
1813168515Sgshapiro						  sizeof(notify));
181464562Sgshapiro			if (bitset(QPINGONFAILURE, to->q_flags))
181590792Sgshapiro				(void) sm_strlcat(notify, "FAILURE,",
1816168515Sgshapiro						  sizeof(notify));
181764562Sgshapiro			if (bitset(QPINGONDELAY, to->q_flags))
181890792Sgshapiro				(void) sm_strlcat(notify, "DELAY,",
1819168515Sgshapiro						  sizeof(notify));
182064562Sgshapiro
182164562Sgshapiro			/* Set to NEVER or drop trailing comma */
182264562Sgshapiro			if (notify[0] == '\0')
182390792Sgshapiro				(void) sm_strlcat(notify, "NEVER",
1824168515Sgshapiro						  sizeof(notify));
182564562Sgshapiro			else
182664562Sgshapiro				notify[strlen(notify) - 1] = '\0';
182764562Sgshapiro
182890792Sgshapiro			macdefine(&e->e_macro, A_TEMP,
182990792Sgshapiro				macid("{dsn_notify}"), notify);
183064562Sgshapiro		}
183164562Sgshapiro		else
183290792Sgshapiro			macdefine(&e->e_macro, A_PERM,
183390792Sgshapiro				macid("{dsn_notify}"), NULL);
183464562Sgshapiro
183538032Speter		/*
183638032Speter		**  Expand out this user into argument list.
183738032Speter		*/
183838032Speter
183938032Speter		if (!clever)
184038032Speter		{
1841168515Sgshapiro			expand(*mvp, buf, sizeof(buf), e);
184290792Sgshapiro			*pvp++ = sm_rpool_strdup_x(e->e_rpool, buf);
184338032Speter			if (pvp >= &pv[MAXPV - 2])
184438032Speter			{
184538032Speter				/* allow some space for trailing parms */
184638032Speter				break;
184738032Speter			}
184838032Speter		}
184938032Speter	}
185038032Speter
185138032Speter	/* see if any addresses still exist */
185264562Sgshapiro	if (tochain == NULL)
185338032Speter	{
185490792Sgshapiro		rcode = 0;
185590792Sgshapiro		goto cleanup;
185638032Speter	}
185738032Speter
185838032Speter	/* print out messages as full list */
185990792Sgshapiro	strsize = 1;
186090792Sgshapiro	for (to = tochain; to != NULL; to = to->q_tchain)
186190792Sgshapiro		strsize += strlen(to->q_paddr) + 1;
186290792Sgshapiro	if (strsize < TOBUFSIZE)
186390792Sgshapiro		strsize = TOBUFSIZE;
186490792Sgshapiro	if (strsize > tobufsize)
186564562Sgshapiro	{
1866363466Sgshapiro		SM_FREE(tobuf);
186790792Sgshapiro		tobuf = sm_pmalloc_x(strsize);
186890792Sgshapiro		tobufsize = strsize;
186964562Sgshapiro	}
187090792Sgshapiro	p = tobuf;
187190792Sgshapiro	*p = '\0';
187290792Sgshapiro	for (to = tochain; to != NULL; to = to->q_tchain)
187390792Sgshapiro	{
187490792Sgshapiro		(void) sm_strlcpyn(p, tobufsize - (p - tobuf), 2,
187590792Sgshapiro				   ",", to->q_paddr);
187690792Sgshapiro		p += strlen(p);
187790792Sgshapiro	}
187838032Speter	e->e_to = tobuf + 1;
187938032Speter
188038032Speter	/*
188138032Speter	**  Fill out any parameters after the $u parameter.
188238032Speter	*/
188338032Speter
188490792Sgshapiro	if (!clever)
188538032Speter	{
188690792Sgshapiro		while (*++mvp != NULL)
188790792Sgshapiro		{
1888168515Sgshapiro			expand(*mvp, buf, sizeof(buf), e);
188990792Sgshapiro			*pvp++ = sm_rpool_strdup_x(e->e_rpool, buf);
189090792Sgshapiro			if (pvp >= &pv[MAXPV])
189190792Sgshapiro				syserr("554 5.3.0 deliver: pv overflow after $u for %s",
189290792Sgshapiro				       pv[0]);
189390792Sgshapiro		}
189438032Speter	}
189538032Speter	*pvp++ = NULL;
189638032Speter
189738032Speter	/*
189838032Speter	**  Call the mailer.
189938032Speter	**	The argument vector gets built, pipes
190038032Speter	**	are created as necessary, and we fork & exec as
190138032Speter	**	appropriate.
190238032Speter	**	If we are running SMTP, we just need to clean up.
190338032Speter	*/
190438032Speter
1905223067Sgshapiro	/* XXX this seems a bit weird */
190638032Speter	if (ctladdr == NULL && m != ProgMailer && m != FileMailer &&
190738032Speter	    bitset(QGOODUID, e->e_from.q_flags))
190838032Speter		ctladdr = &e->e_from;
190938032Speter
191038032Speter#if NAMED_BIND
191138032Speter	if (ConfigLevel < 2)
191238032Speter		_res.options &= ~(RES_DEFNAMES | RES_DNSRCH);	/* XXX */
1913363466Sgshapiro#endif
191438032Speter
191538032Speter	if (tTd(11, 1))
191638032Speter	{
191790792Sgshapiro		sm_dprintf("openmailer:");
1918132943Sgshapiro		printav(sm_debug_file(), pv);
191938032Speter	}
192038032Speter	errno = 0;
192173188Sgshapiro	SM_SET_H_ERRNO(0);
192238032Speter	CurHostName = NULL;
192338032Speter
192438032Speter	/*
192538032Speter	**  Deal with the special case of mail handled through an IPC
192638032Speter	**  connection.
192738032Speter	**	In this case we don't actually fork.  We must be
192838032Speter	**	running SMTP for this to work.  We will return a
192938032Speter	**	zero pid to indicate that we are running IPC.
193038032Speter	**  We also handle a debug version that just talks to stdin/out.
193138032Speter	*/
193238032Speter
193338032Speter	curhost = NULL;
193438032Speter	SmtpPhase = NULL;
193538032Speter	mci = NULL;
193638032Speter
193738032Speter#if XDEBUG
193838032Speter	{
193938032Speter		char wbuf[MAXLINE];
194038032Speter
194138032Speter		/* make absolutely certain 0, 1, and 2 are in use */
1942168515Sgshapiro		(void) sm_snprintf(wbuf, sizeof(wbuf), "%s... openmailer(%s)",
194390792Sgshapiro				   shortenstring(e->e_to, MAXSHORTSTR),
194490792Sgshapiro				   m->m_name);
194538032Speter		checkfd012(wbuf);
194638032Speter	}
194764562Sgshapiro#endif /* XDEBUG */
194838032Speter
194938032Speter	/* check for 8-bit available */
195038032Speter	if (bitset(EF_HAS8BIT, e->e_flags) &&
195138032Speter	    bitnset(M_7BITS, m->m_flags) &&
195238032Speter	    (bitset(EF_DONT_MIME, e->e_flags) ||
195338032Speter	     !(bitset(MM_MIME8BIT, MimeMode) ||
195438032Speter	       (bitset(EF_IS_MIME, e->e_flags) &&
195564562Sgshapiro		bitset(MM_CVTMIME, MimeMode)))))
195638032Speter	{
195764562Sgshapiro		e->e_status = "5.6.3";
195864562Sgshapiro		usrerrenh(e->e_status,
195990792Sgshapiro			  "554 Cannot send 8-bit data to 7-bit destination");
196038032Speter		rcode = EX_DATAERR;
196138032Speter		goto give_up;
196238032Speter	}
196338032Speter
196438032Speter	if (tTd(62, 8))
196538032Speter		checkfds("before delivery");
196638032Speter
196738032Speter	/* check for Local Person Communication -- not for mortals!!! */
196838032Speter	if (strcmp(m->m_mailer, "[LPC]") == 0)
196938032Speter	{
197090792Sgshapiro		if (clever)
197190792Sgshapiro		{
197290792Sgshapiro			/* flush any expired connections */
197390792Sgshapiro			(void) mci_scan(NULL);
197490792Sgshapiro
197590792Sgshapiro			/* try to get a cached connection or just a slot */
197690792Sgshapiro			mci = mci_get(m->m_name, m);
197790792Sgshapiro			if (mci->mci_host == NULL)
197890792Sgshapiro				mci->mci_host = m->m_name;
197990792Sgshapiro			CurHostName = mci->mci_host;
198090792Sgshapiro			if (mci->mci_state != MCIS_CLOSED)
198190792Sgshapiro			{
198290792Sgshapiro				message("Using cached SMTP/LPC connection for %s...",
198390792Sgshapiro					m->m_name);
198490792Sgshapiro				mci->mci_deliveries++;
198590792Sgshapiro				goto do_transfer;
198690792Sgshapiro			}
198790792Sgshapiro		}
198890792Sgshapiro		else
198990792Sgshapiro		{
199090792Sgshapiro			mci = mci_new(e->e_rpool);
199190792Sgshapiro		}
199290792Sgshapiro		mci->mci_in = smioin;
199390792Sgshapiro		mci->mci_out = smioout;
199490792Sgshapiro		mci->mci_mailer = m;
199590792Sgshapiro		mci->mci_host = m->m_name;
199690792Sgshapiro		if (clever)
199790792Sgshapiro		{
199890792Sgshapiro			mci->mci_state = MCIS_OPENING;
199990792Sgshapiro			mci_cache(mci);
200090792Sgshapiro		}
200190792Sgshapiro		else
200290792Sgshapiro			mci->mci_state = MCIS_OPEN;
200338032Speter	}
200490792Sgshapiro	else if (strcmp(m->m_mailer, "[IPC]") == 0)
200538032Speter	{
200638032Speter		register int i;
200738032Speter
200838032Speter		if (pv[0] == NULL || pv[1] == NULL || pv[1][0] == '\0')
200938032Speter		{
201064562Sgshapiro			syserr("null destination for %s mailer", m->m_mailer);
201138032Speter			rcode = EX_CONFIG;
201238032Speter			goto give_up;
201338032Speter		}
201438032Speter
2015363466Sgshapiro#if NETUNIX
201664562Sgshapiro		if (strcmp(pv[0], "FILE") == 0)
201764562Sgshapiro		{
201864562Sgshapiro			curhost = CurHostName = "localhost";
201964562Sgshapiro			mux_path = pv[1];
202064562Sgshapiro		}
202164562Sgshapiro		else
2022363466Sgshapiro#endif /* NETUNIX */
202364562Sgshapiro		{
202464562Sgshapiro			CurHostName = pv[1];
2025363466Sgshapiro							/* XXX ??? */
2026363466Sgshapiro			curhost = hostsignature(m, pv[1], firstto->q_flags & QSECURE);
202764562Sgshapiro		}
202838032Speter
202938032Speter		if (curhost == NULL || curhost[0] == '\0')
203038032Speter		{
203138032Speter			syserr("null host signature for %s", pv[1]);
203238032Speter			rcode = EX_CONFIG;
203338032Speter			goto give_up;
203438032Speter		}
203538032Speter
203638032Speter		if (!clever)
203738032Speter		{
203864562Sgshapiro			syserr("554 5.3.5 non-clever IPC");
203938032Speter			rcode = EX_CONFIG;
204038032Speter			goto give_up;
204138032Speter		}
204264562Sgshapiro		if (pv[2] != NULL
2043363466Sgshapiro#if NETUNIX
204464562Sgshapiro		    && mux_path == NULL
2045363466Sgshapiro#endif
204664562Sgshapiro		    )
204738032Speter		{
204890792Sgshapiro			port = htons((unsigned short) atoi(pv[2]));
204938032Speter			if (port == 0)
205038032Speter			{
2051363466Sgshapiro#ifdef NO_GETSERVBYNAME
205264562Sgshapiro				syserr("Invalid port number: %s", pv[2]);
2053363466Sgshapiro#else /* NO_GETSERVBYNAME */
205438032Speter				struct servent *sp = getservbyname(pv[2], "tcp");
205538032Speter
205638032Speter				if (sp == NULL)
205738032Speter					syserr("Service %s unknown", pv[2]);
205838032Speter				else
205938032Speter					port = sp->s_port;
2060363466Sgshapiro#endif /* NO_GETSERVBYNAME */
206138032Speter			}
206238032Speter		}
206364562Sgshapiro
206464562Sgshapiro		nummxhosts = parse_hostsignature(curhost, mxhosts, m);
206590792Sgshapiro		if (TimeOuts.to_aconnect > 0)
206690792Sgshapiro			enough = curtime() + TimeOuts.to_aconnect;
206738032Spetertryhost:
206864562Sgshapiro		while (hostnum < nummxhosts)
206938032Speter		{
207064562Sgshapiro			char sep = ':';
207164562Sgshapiro			char *endp;
207238032Speter			static char hostbuf[MAXNAME + 1];
2073132943Sgshapiro			bool tried_fallbacksmarthost = false;
2074363466Sgshapiro#if DANE
2075363466Sgshapiro			unsigned long tlsa_flags;
207638032Speter
2077363466Sgshapiro			ste = NULL;
2078363466Sgshapiro			tlsa_flags = 0;
2079363466Sgshapiro#endif
2080363466Sgshapiro#if NETINET6
208164562Sgshapiro			if (*mxhosts[hostnum] == '[')
208238032Speter			{
208364562Sgshapiro				endp = strchr(mxhosts[hostnum] + 1, ']');
208464562Sgshapiro				if (endp != NULL)
208564562Sgshapiro					endp = strpbrk(endp + 1, ":,");
208664562Sgshapiro			}
208764562Sgshapiro			else
208864562Sgshapiro				endp = strpbrk(mxhosts[hostnum], ":,");
2089363466Sgshapiro#else /* NETINET6 */
209064562Sgshapiro			endp = strpbrk(mxhosts[hostnum], ":,");
2091363466Sgshapiro#endif /* NETINET6 */
209264562Sgshapiro			if (endp != NULL)
209364562Sgshapiro			{
209464562Sgshapiro				sep = *endp;
209564562Sgshapiro				*endp = '\0';
209664562Sgshapiro			}
209764562Sgshapiro
209890792Sgshapiro			if (hostnum == 1 && skip_back != NULL)
209990792Sgshapiro			{
210090792Sgshapiro				/*
210190792Sgshapiro				**  Coattail piggybacking is no longer an
210290792Sgshapiro				**  option with the mail host next to be tried
210390792Sgshapiro				**  no longer the lowest MX preference
210490792Sgshapiro				**  (hostnum == 1 meaning we're on the second
210590792Sgshapiro				**  preference). We do not try to coattail
210690792Sgshapiro				**  piggyback more than the first MX preference.
210790792Sgshapiro				**  Revert 'tochain' to last location for
210890792Sgshapiro				**  coincidental piggybacking. This works this
210990792Sgshapiro				**  easily because the q_tchain kept getting
211090792Sgshapiro				**  added to the top of the linked list.
211190792Sgshapiro				*/
211290792Sgshapiro
211390792Sgshapiro				tochain = skip_back;
211490792Sgshapiro			}
211590792Sgshapiro
211664562Sgshapiro			if (*mxhosts[hostnum] == '\0')
211764562Sgshapiro			{
211838032Speter				syserr("deliver: null host name in signature");
211964562Sgshapiro				hostnum++;
212064562Sgshapiro				if (endp != NULL)
212164562Sgshapiro					*endp = sep;
212238032Speter				continue;
212338032Speter			}
212490792Sgshapiro			(void) sm_strlcpy(hostbuf, mxhosts[hostnum],
2125168515Sgshapiro					  sizeof(hostbuf));
212664562Sgshapiro			hostnum++;
212764562Sgshapiro			if (endp != NULL)
212864562Sgshapiro				*endp = sep;
2129363466Sgshapiro#if STARTTLS
2130363466Sgshapiro			tlsstate = 0;
2131363466Sgshapiro#endif
213238032Speter
2133132943Sgshapiro  one_last_try:
213438032Speter			/* see if we already know that this host is fried */
213538032Speter			CurHostName = hostbuf;
213638032Speter			mci = mci_get(hostbuf, m);
213738032Speter			if (mci->mci_state != MCIS_CLOSED)
213838032Speter			{
213990792Sgshapiro				char *type;
214090792Sgshapiro
214138032Speter				if (tTd(11, 1))
214238032Speter				{
214390792Sgshapiro					sm_dprintf("openmailer: ");
2144132943Sgshapiro					mci_dump(sm_debug_file(), mci, false);
214538032Speter				}
214638032Speter				CurHostName = mci->mci_host;
214790792Sgshapiro				if (bitnset(M_LMTP, m->m_flags))
214890792Sgshapiro					type = "L";
214990792Sgshapiro				else if (bitset(MCIF_ESMTP, mci->mci_flags))
215090792Sgshapiro					type = "ES";
215190792Sgshapiro				else
215290792Sgshapiro					type = "S";
215390792Sgshapiro				message("Using cached %sMTP connection to %s via %s...",
215490792Sgshapiro					type, hostbuf, m->m_name);
215564562Sgshapiro				mci->mci_deliveries++;
215638032Speter				break;
215738032Speter			}
215838032Speter			mci->mci_mailer = m;
2159363466Sgshapiro#if DANE
2160363466Sgshapiro			tlsa_flags = 0;
2161363466Sgshapiro			if (CHK_DANE(Dane))
2162363466Sgshapiro				(void) GETTLSA(hostbuf, &ste, m->m_port);
2163363466Sgshapiro
2164363466Sgshapiro			/* XXX: check expiration! */
2165363466Sgshapiro			if (ste != NULL && TLSA_RR_TEMPFAIL(ste->s_tlsa))
2166363466Sgshapiro			{
2167363466Sgshapiro				if (tTd(11, 1))
2168363466Sgshapiro					sm_dprintf("skip: host=%s, TLSA_RR_lookup=%d\n"
2169363466Sgshapiro						, hostbuf
2170363466Sgshapiro						, ste->s_tlsa->dane_tlsa_dnsrc);
2171363466Sgshapiro
2172363466Sgshapiro				tlsa_flags |= TLSAFLTEMP;
2173363466Sgshapiro			}
2174363466Sgshapiro#endif /* DANE */
2175363466Sgshapiro
217638032Speter			if (mci->mci_exitstat != EX_OK)
217738032Speter			{
217838032Speter				if (mci->mci_exitstat == EX_TEMPFAIL)
217990792Sgshapiro					goodmxfound = true;
2180132943Sgshapiro
2181132943Sgshapiro				/* Try FallbackSmartHost? */
2182132943Sgshapiro				if (should_try_fbsh(e, &tried_fallbacksmarthost,
2183168515Sgshapiro						    hostbuf, sizeof(hostbuf),
2184132943Sgshapiro						    mci->mci_exitstat))
2185132943Sgshapiro					goto one_last_try;
2186132943Sgshapiro
218738032Speter				continue;
218838032Speter			}
218938032Speter
219038032Speter			if (mci_lock_host(mci) != EX_OK)
219138032Speter			{
219238032Speter				mci_setstat(mci, EX_TEMPFAIL, "4.4.5", NULL);
219390792Sgshapiro				goodmxfound = true;
219438032Speter				continue;
219538032Speter			}
219638032Speter
219738032Speter			/* try the connection */
219890792Sgshapiro			sm_setproctitle(true, e, "%s %s: %s",
219964562Sgshapiro					qid_printname(e),
220064562Sgshapiro					hostbuf, "user open");
2201363466Sgshapiro#if NETUNIX
220264562Sgshapiro			if (mux_path != NULL)
220364562Sgshapiro			{
220438032Speter				message("Connecting to %s via %s...",
220564562Sgshapiro					mux_path, m->m_name);
220690792Sgshapiro				i = makeconnection_ds((char *) mux_path, mci);
220764562Sgshapiro			}
220838032Speter			else
2209363466Sgshapiro#endif /* NETUNIX */
221064562Sgshapiro			{
221164562Sgshapiro				if (port == 0)
221264562Sgshapiro					message("Connecting to %s via %s...",
221364562Sgshapiro						hostbuf, m->m_name);
221464562Sgshapiro				else
221564562Sgshapiro					message("Connecting to %s port %d via %s...",
221664562Sgshapiro						hostbuf, ntohs(port),
221764562Sgshapiro						m->m_name);
2218363466Sgshapiro#if DANE
2219363466Sgshapiro				tlsa_flags |= (ste != NULL) ? Dane : DANE_NEVER;
2220363466Sgshapiro				dane_vrfy_ctx.dane_vrfy_chk = tlsa_flags;
2221363466Sgshapiro				dane_vrfy_ctx.dane_vrfy_port = m->m_port;
2222363466Sgshapiro				if (tTd(11, 11))
2223363466Sgshapiro					sm_dprintf("makeconnection: before: chk=%d, mode=%lX\n", dane_vrfy_ctx.dane_vrfy_chk, tlsa_flags);
2224363466Sgshapiro#endif
222590792Sgshapiro				i = makeconnection(hostbuf, port, mci, e,
2226363466Sgshapiro						enough
2227363466Sgshapiro#if DANE
2228363466Sgshapiro						, &tlsa_flags
2229363466Sgshapiro#endif
2230363466Sgshapiro						);
2231363466Sgshapiro#if DANE
2232363466Sgshapiro				if (tTd(11, 11))
2233363466Sgshapiro					sm_dprintf("makeconnection: after: chk=%d, mode=%lX\n", dane_vrfy_ctx.dane_vrfy_chk, tlsa_flags);
2234363466Sgshapiro				if (dane_vrfy_ctx.dane_vrfy_chk != DANE_ALWAYS)
2235363466Sgshapiro					dane_vrfy_ctx.dane_vrfy_chk = DANEMODE(tlsa_flags);
2236363466Sgshapiro				if (EX_TEMPFAIL == i &&
2237363466Sgshapiro				    ((tlsa_flags & (TLSAFLTEMP|DANE_SECURE)) ==
2238363466Sgshapiro				     (TLSAFLTEMP|DANE_SECURE)))
2239363466Sgshapiro				{
2240363466Sgshapiro					(void) sm_strlcpy(SmtpError,
2241363466Sgshapiro						" for TLSA RR",
2242363466Sgshapiro						sizeof(SmtpError));
2243363466Sgshapiro# if NAMED_BIND
2244363466Sgshapiro					SM_SET_H_ERRNO(TRY_AGAIN);
2245363466Sgshapiro# endif
2246363466Sgshapiro				}
2247363466Sgshapiro#endif
224864562Sgshapiro			}
224977349Sgshapiro			mci->mci_errno = errno;
225038032Speter			mci->mci_lastuse = curtime();
225164562Sgshapiro			mci->mci_deliveries = 0;
225238032Speter			mci->mci_exitstat = i;
2253223067Sgshapiro			mci_clr_extensions(mci);
2254363466Sgshapiro#if NAMED_BIND
225538032Speter			mci->mci_herrno = h_errno;
2256363466Sgshapiro#endif
225790792Sgshapiro
225890792Sgshapiro			/*
225990792Sgshapiro			**  Have we tried long enough to get a connection?
226090792Sgshapiro			**	If yes, skip to the fallback MX hosts
226190792Sgshapiro			**	(if existent).
226290792Sgshapiro			*/
226390792Sgshapiro
226490792Sgshapiro			if (enough > 0 && mci->mci_lastuse >= enough)
226590792Sgshapiro			{
226690792Sgshapiro				int h;
2267363466Sgshapiro#if NAMED_BIND
2268132943Sgshapiro				extern int NumFallbackMXHosts;
2269363466Sgshapiro#else
2270132943Sgshapiro				const int NumFallbackMXHosts = 0;
2271363466Sgshapiro#endif
227290792Sgshapiro
227390792Sgshapiro				if (hostnum < nummxhosts && LogLevel > 9)
227490792Sgshapiro					sm_syslog(LOG_INFO, e->e_id,
227590792Sgshapiro						  "Timeout.to_aconnect occurred before exhausting all addresses");
227690792Sgshapiro
227790792Sgshapiro				/* turn off timeout if fallback available */
2278132943Sgshapiro				if (NumFallbackMXHosts > 0)
227990792Sgshapiro					enough = 0;
228090792Sgshapiro
228190792Sgshapiro				/* skip to a fallback MX host */
2282132943Sgshapiro				h = nummxhosts - NumFallbackMXHosts;
228390792Sgshapiro				if (hostnum < h)
228490792Sgshapiro					hostnum = h;
228590792Sgshapiro			}
228638032Speter			if (i == EX_OK)
228738032Speter			{
228890792Sgshapiro				goodmxfound = true;
228994334Sgshapiro				markstats(e, firstto, STATS_CONNECT);
229038032Speter				mci->mci_state = MCIS_OPENING;
229138032Speter				mci_cache(mci);
229238032Speter				if (TrafficLogFile != NULL)
229390792Sgshapiro					(void) sm_io_fprintf(TrafficLogFile,
229490792Sgshapiro							     SM_TIME_DEFAULT,
229590792Sgshapiro							     "%05d === CONNECT %s\n",
229690792Sgshapiro							     (int) CurrentPid,
229790792Sgshapiro							     hostbuf);
229838032Speter				break;
229938032Speter			}
230038032Speter			else
230138032Speter			{
2302132943Sgshapiro				/* Try FallbackSmartHost? */
2303132943Sgshapiro				if (should_try_fbsh(e, &tried_fallbacksmarthost,
2304168515Sgshapiro						    hostbuf, sizeof(hostbuf), i))
2305132943Sgshapiro					goto one_last_try;
2306132943Sgshapiro
230764562Sgshapiro				if (tTd(11, 1))
230890792Sgshapiro					sm_dprintf("openmailer: makeconnection => stat=%d, errno=%d\n",
230990792Sgshapiro						   i, errno);
231038032Speter				if (i == EX_TEMPFAIL)
231190792Sgshapiro					goodmxfound = true;
231238032Speter				mci_unlock_host(mci);
231338032Speter			}
231438032Speter
231538032Speter			/* enter status of this host */
231638032Speter			setstat(i);
231738032Speter
231838032Speter			/* should print some message here for -v mode */
231938032Speter		}
232038032Speter		if (mci == NULL)
232138032Speter		{
232238032Speter			syserr("deliver: no host name");
232338032Speter			rcode = EX_SOFTWARE;
232438032Speter			goto give_up;
232538032Speter		}
232638032Speter		mci->mci_pid = 0;
232738032Speter	}
232838032Speter	else
232938032Speter	{
233038032Speter		/* flush any expired connections */
233138032Speter		(void) mci_scan(NULL);
233238032Speter		mci = NULL;
233338032Speter
233438032Speter		if (bitnset(M_LMTP, m->m_flags))
233538032Speter		{
233638032Speter			/* try to get a cached connection */
233738032Speter			mci = mci_get(m->m_name, m);
233838032Speter			if (mci->mci_host == NULL)
233938032Speter				mci->mci_host = m->m_name;
234038032Speter			CurHostName = mci->mci_host;
234138032Speter			if (mci->mci_state != MCIS_CLOSED)
234238032Speter			{
234338032Speter				message("Using cached LMTP connection for %s...",
234438032Speter					m->m_name);
234564562Sgshapiro				mci->mci_deliveries++;
234638032Speter				goto do_transfer;
234738032Speter			}
234838032Speter		}
234938032Speter
235038032Speter		/* announce the connection to verbose listeners */
235138032Speter		if (host == NULL || host[0] == '\0')
235238032Speter			message("Connecting to %s...", m->m_name);
235338032Speter		else
235438032Speter			message("Connecting to %s via %s...", host, m->m_name);
235538032Speter		if (TrafficLogFile != NULL)
235638032Speter		{
235738032Speter			char **av;
235838032Speter
235990792Sgshapiro			(void) sm_io_fprintf(TrafficLogFile, SM_TIME_DEFAULT,
236090792Sgshapiro					     "%05d === EXEC", (int) CurrentPid);
236138032Speter			for (av = pv; *av != NULL; av++)
236290792Sgshapiro				(void) sm_io_fprintf(TrafficLogFile,
236390792Sgshapiro						     SM_TIME_DEFAULT, " %s",
236490792Sgshapiro						     *av);
236590792Sgshapiro			(void) sm_io_fprintf(TrafficLogFile, SM_TIME_DEFAULT,
236690792Sgshapiro					     "\n");
236738032Speter		}
236838032Speter
236938032Speter#if XDEBUG
237038032Speter		checkfd012("before creating mail pipe");
2371363466Sgshapiro#endif
237238032Speter
237338032Speter		/* create a pipe to shove the mail through */
237438032Speter		if (pipe(mpvect) < 0)
237538032Speter		{
237638032Speter			syserr("%s... openmailer(%s): pipe (to mailer)",
237790792Sgshapiro			       shortenstring(e->e_to, MAXSHORTSTR), m->m_name);
237838032Speter			if (tTd(11, 1))
237990792Sgshapiro				sm_dprintf("openmailer: NULL\n");
238038032Speter			rcode = EX_OSERR;
238138032Speter			goto give_up;
238238032Speter		}
238338032Speter
238438032Speter#if XDEBUG
238538032Speter		/* make sure we didn't get one of the standard I/O files */
238638032Speter		if (mpvect[0] < 3 || mpvect[1] < 3)
238738032Speter		{
238838032Speter			syserr("%s... openmailer(%s): bogus mpvect %d %d",
238990792Sgshapiro			       shortenstring(e->e_to, MAXSHORTSTR), m->m_name,
239090792Sgshapiro			       mpvect[0], mpvect[1]);
239190792Sgshapiro			printopenfds(true);
239238032Speter			if (tTd(11, 1))
239390792Sgshapiro				sm_dprintf("openmailer: NULL\n");
239438032Speter			rcode = EX_OSERR;
239538032Speter			goto give_up;
239638032Speter		}
239738032Speter
239838032Speter		/* make sure system call isn't dead meat */
239938032Speter		checkfdopen(mpvect[0], "mpvect[0]");
240038032Speter		checkfdopen(mpvect[1], "mpvect[1]");
240138032Speter		if (mpvect[0] == mpvect[1] ||
240238032Speter		    (e->e_lockfp != NULL &&
240390792Sgshapiro		     (mpvect[0] == sm_io_getinfo(e->e_lockfp, SM_IO_WHAT_FD,
240490792Sgshapiro						 NULL) ||
240590792Sgshapiro		      mpvect[1] == sm_io_getinfo(e->e_lockfp, SM_IO_WHAT_FD,
240690792Sgshapiro						 NULL))))
240738032Speter		{
240838032Speter			if (e->e_lockfp == NULL)
240938032Speter				syserr("%s... openmailer(%s): overlapping mpvect %d %d",
241090792Sgshapiro				       shortenstring(e->e_to, MAXSHORTSTR),
241190792Sgshapiro				       m->m_name, mpvect[0], mpvect[1]);
241238032Speter			else
241338032Speter				syserr("%s... openmailer(%s): overlapping mpvect %d %d, lockfp = %d",
241490792Sgshapiro				       shortenstring(e->e_to, MAXSHORTSTR),
241590792Sgshapiro				       m->m_name, mpvect[0], mpvect[1],
241690792Sgshapiro				       sm_io_getinfo(e->e_lockfp,
241790792Sgshapiro						     SM_IO_WHAT_FD, NULL));
241838032Speter		}
241964562Sgshapiro#endif /* XDEBUG */
242038032Speter
242164562Sgshapiro		/* create a return pipe */
242264562Sgshapiro		if (pipe(rpvect) < 0)
242338032Speter		{
242464562Sgshapiro			syserr("%s... openmailer(%s): pipe (from mailer)",
242590792Sgshapiro			       shortenstring(e->e_to, MAXSHORTSTR),
242690792Sgshapiro			       m->m_name);
242764562Sgshapiro			(void) close(mpvect[0]);
242864562Sgshapiro			(void) close(mpvect[1]);
242964562Sgshapiro			if (tTd(11, 1))
243090792Sgshapiro				sm_dprintf("openmailer: NULL\n");
243164562Sgshapiro			rcode = EX_OSERR;
243264562Sgshapiro			goto give_up;
243338032Speter		}
243464562Sgshapiro#if XDEBUG
243564562Sgshapiro		checkfdopen(rpvect[0], "rpvect[0]");
243664562Sgshapiro		checkfdopen(rpvect[1], "rpvect[1]");
2437363466Sgshapiro#endif
243838032Speter
243938032Speter		/*
244038032Speter		**  Actually fork the mailer process.
244138032Speter		**	DOFORK is clever about retrying.
244238032Speter		**
244338032Speter		**	Dispose of SIGCHLD signal catchers that may be laying
244464562Sgshapiro		**	around so that endmailer will get it.
244538032Speter		*/
244638032Speter
244790792Sgshapiro		if (e->e_xfp != NULL)	/* for debugging */
244890792Sgshapiro			(void) sm_io_flush(e->e_xfp, SM_TIME_DEFAULT);
244990792Sgshapiro		(void) sm_io_flush(smioout, SM_TIME_DEFAULT);
245090792Sgshapiro		(void) sm_signal(SIGCHLD, SIG_DFL);
245164562Sgshapiro
245264562Sgshapiro
245338032Speter		DOFORK(FORK);
245438032Speter		/* pid is set by DOFORK */
245564562Sgshapiro
245638032Speter		if (pid < 0)
245738032Speter		{
245838032Speter			/* failure */
245938032Speter			syserr("%s... openmailer(%s): cannot fork",
246090792Sgshapiro			       shortenstring(e->e_to, MAXSHORTSTR), m->m_name);
246138032Speter			(void) close(mpvect[0]);
246238032Speter			(void) close(mpvect[1]);
246364562Sgshapiro			(void) close(rpvect[0]);
246464562Sgshapiro			(void) close(rpvect[1]);
246538032Speter			if (tTd(11, 1))
246690792Sgshapiro				sm_dprintf("openmailer: NULL\n");
246738032Speter			rcode = EX_OSERR;
246838032Speter			goto give_up;
246938032Speter		}
247038032Speter		else if (pid == 0)
247138032Speter		{
247264562Sgshapiro			int save_errno;
247390792Sgshapiro			int sff;
247438032Speter			int new_euid = NO_UID;
247538032Speter			int new_ruid = NO_UID;
247638032Speter			int new_gid = NO_GID;
247790792Sgshapiro			char *user = NULL;
247838032Speter			struct stat stb;
247938032Speter			extern int DtableSize;
248038032Speter
248190792Sgshapiro			CurrentPid = getpid();
248290792Sgshapiro
248380785Sgshapiro			/* clear the events to turn off SIGALRMs */
248490792Sgshapiro			sm_clear_events();
248580785Sgshapiro
248677349Sgshapiro			/* Reset global flags */
248777349Sgshapiro			RestartRequest = NULL;
248890792Sgshapiro			RestartWorkGroup = false;
248977349Sgshapiro			ShutdownRequest = NULL;
249077349Sgshapiro			PendingSignal = 0;
249177349Sgshapiro
249238032Speter			if (e->e_lockfp != NULL)
249390792Sgshapiro				(void) close(sm_io_getinfo(e->e_lockfp,
249490792Sgshapiro							   SM_IO_WHAT_FD,
249590792Sgshapiro							   NULL));
249638032Speter
249738032Speter			/* child -- set up input & exec mailer */
249890792Sgshapiro			(void) sm_signal(SIGALRM, sm_signal_noop);
249990792Sgshapiro			(void) sm_signal(SIGCHLD, SIG_DFL);
250090792Sgshapiro			(void) sm_signal(SIGHUP, SIG_IGN);
250190792Sgshapiro			(void) sm_signal(SIGINT, SIG_IGN);
250290792Sgshapiro			(void) sm_signal(SIGTERM, SIG_DFL);
2503363466Sgshapiro#ifdef SIGUSR1
250490792Sgshapiro			(void) sm_signal(SIGUSR1, sm_signal_noop);
2505363466Sgshapiro#endif
250638032Speter
250738032Speter			if (m != FileMailer || stat(tochain->q_user, &stb) < 0)
250838032Speter				stb.st_mode = 0;
250938032Speter
2510363466Sgshapiro#if HASSETUSERCONTEXT
251138032Speter			/*
251238032Speter			**  Set user resources.
251338032Speter			*/
251438032Speter
251538032Speter			if (contextaddr != NULL)
251638032Speter			{
2517110560Sgshapiro				int sucflags;
251838032Speter				struct passwd *pwd;
251938032Speter
252038032Speter				if (contextaddr->q_ruser != NULL)
252138032Speter					pwd = sm_getpwnam(contextaddr->q_ruser);
252238032Speter				else
252338032Speter					pwd = sm_getpwnam(contextaddr->q_user);
2524110560Sgshapiro				sucflags = LOGIN_SETRESOURCES|LOGIN_SETPRIORITY;
2525363466Sgshapiro# ifdef LOGIN_SETCPUMASK
2526223701Strasz				sucflags |= LOGIN_SETCPUMASK;
2527363466Sgshapiro# endif
2528363466Sgshapiro# ifdef LOGIN_SETLOGINCLASS
2529223701Strasz				sucflags |= LOGIN_SETLOGINCLASS;
2530363466Sgshapiro# endif
2531363466Sgshapiro# ifdef LOGIN_SETMAC
2532110560Sgshapiro				sucflags |= LOGIN_SETMAC;
2533363466Sgshapiro# endif
2534102528Sgshapiro				if (pwd != NULL &&
2535102528Sgshapiro				    setusercontext(NULL, pwd, pwd->pw_uid,
2536110560Sgshapiro						   sucflags) == -1 &&
2537102528Sgshapiro				    suidwarn)
2538102528Sgshapiro				{
2539102528Sgshapiro					syserr("openmailer: setusercontext() failed");
2540102528Sgshapiro					exit(EX_TEMPFAIL);
2541102528Sgshapiro				}
254238032Speter			}
2543363466Sgshapiro#endif /* HASSETUSERCONTEXT */
254438032Speter
254590792Sgshapiro#if HASNICE
254638032Speter			/* tweak niceness */
254738032Speter			if (m->m_nice != 0)
254864562Sgshapiro				(void) nice(m->m_nice);
254990792Sgshapiro#endif /* HASNICE */
255038032Speter
255138032Speter			/* reset group id */
255238032Speter			if (bitnset(M_SPECIFIC_UID, m->m_flags))
2553132943Sgshapiro			{
2554132943Sgshapiro				if (m->m_gid == NO_GID)
2555132943Sgshapiro					new_gid = RunAsGid;
2556132943Sgshapiro				else
2557132943Sgshapiro					new_gid = m->m_gid;
2558132943Sgshapiro			}
255938032Speter			else if (bitset(S_ISGID, stb.st_mode))
256038032Speter				new_gid = stb.st_gid;
256138032Speter			else if (ctladdr != NULL && ctladdr->q_gid != 0)
256238032Speter			{
256338032Speter				if (!DontInitGroups)
256438032Speter				{
256590792Sgshapiro					user = ctladdr->q_ruser;
256690792Sgshapiro					if (user == NULL)
256790792Sgshapiro						user = ctladdr->q_user;
256838032Speter
256990792Sgshapiro					if (initgroups(user,
257090792Sgshapiro						       ctladdr->q_gid) == -1
257190792Sgshapiro					    && suidwarn)
257264562Sgshapiro					{
2573285229Sgshapiro						syserr("openmailer: initgroups(%s, %ld) failed",
2574285229Sgshapiro							user, (long) ctladdr->q_gid);
257564562Sgshapiro						exit(EX_TEMPFAIL);
257664562Sgshapiro					}
257738032Speter				}
257838032Speter				else
257938032Speter				{
258038032Speter					GIDSET_T gidset[1];
258138032Speter
258238032Speter					gidset[0] = ctladdr->q_gid;
258390792Sgshapiro					if (setgroups(1, gidset) == -1
258490792Sgshapiro					    && suidwarn)
258564562Sgshapiro					{
258638032Speter						syserr("openmailer: setgroups() failed");
258764562Sgshapiro						exit(EX_TEMPFAIL);
258864562Sgshapiro					}
258938032Speter				}
259038032Speter				new_gid = ctladdr->q_gid;
259138032Speter			}
259238032Speter			else
259338032Speter			{
259438032Speter				if (!DontInitGroups)
259538032Speter				{
259690792Sgshapiro					user = DefUser;
259790792Sgshapiro					if (initgroups(DefUser, DefGid) == -1 &&
259890792Sgshapiro					    suidwarn)
259964562Sgshapiro					{
2600285229Sgshapiro						syserr("openmailer: initgroups(%s, %ld) failed",
2601285229Sgshapiro						       DefUser, (long) DefGid);
260264562Sgshapiro						exit(EX_TEMPFAIL);
260364562Sgshapiro					}
260438032Speter				}
260538032Speter				else
260638032Speter				{
260738032Speter					GIDSET_T gidset[1];
260838032Speter
260938032Speter					gidset[0] = DefGid;
261090792Sgshapiro					if (setgroups(1, gidset) == -1
261190792Sgshapiro					    && suidwarn)
261264562Sgshapiro					{
261338032Speter						syserr("openmailer: setgroups() failed");
261464562Sgshapiro						exit(EX_TEMPFAIL);
261564562Sgshapiro					}
261638032Speter				}
2617132943Sgshapiro				if (m->m_gid == NO_GID)
261838032Speter					new_gid = DefGid;
261938032Speter				else
262038032Speter					new_gid = m->m_gid;
262138032Speter			}
262264562Sgshapiro			if (new_gid != NO_GID)
262364562Sgshapiro			{
262464562Sgshapiro				if (RunAsUid != 0 &&
262564562Sgshapiro				    bitnset(M_SPECIFIC_UID, m->m_flags) &&
262664562Sgshapiro				    new_gid != getgid() &&
262764562Sgshapiro				    new_gid != getegid())
262864562Sgshapiro				{
262964562Sgshapiro					/* Only root can change the gid */
2630285229Sgshapiro					syserr("openmailer: insufficient privileges to change gid, RunAsUid=%ld, new_gid=%ld, gid=%ld, egid=%ld",
2631285229Sgshapiro					       (long) RunAsUid, (long) new_gid,
2632285229Sgshapiro					       (long) getgid(), (long) getegid());
263364562Sgshapiro					exit(EX_TEMPFAIL);
263464562Sgshapiro				}
263538032Speter
263664562Sgshapiro				if (setgid(new_gid) < 0 && suidwarn)
263764562Sgshapiro				{
263864562Sgshapiro					syserr("openmailer: setgid(%ld) failed",
263964562Sgshapiro					       (long) new_gid);
264064562Sgshapiro					exit(EX_TEMPFAIL);
264164562Sgshapiro				}
264264562Sgshapiro			}
264364562Sgshapiro
264464562Sgshapiro			/* change root to some "safe" directory */
264564562Sgshapiro			if (m->m_rootdir != NULL)
264664562Sgshapiro			{
2647168515Sgshapiro				expand(m->m_rootdir, cbuf, sizeof(cbuf), e);
264864562Sgshapiro				if (tTd(11, 20))
264990792Sgshapiro					sm_dprintf("openmailer: chroot %s\n",
265098121Sgshapiro						   cbuf);
265198121Sgshapiro				if (chroot(cbuf) < 0)
265264562Sgshapiro				{
265364562Sgshapiro					syserr("openmailer: Cannot chroot(%s)",
265498121Sgshapiro					       cbuf);
265564562Sgshapiro					exit(EX_TEMPFAIL);
265664562Sgshapiro				}
265764562Sgshapiro				if (chdir("/") < 0)
265864562Sgshapiro				{
265964562Sgshapiro					syserr("openmailer: cannot chdir(/)");
266064562Sgshapiro					exit(EX_TEMPFAIL);
266164562Sgshapiro				}
266264562Sgshapiro			}
266364562Sgshapiro
266438032Speter			/* reset user id */
266538032Speter			endpwent();
266690792Sgshapiro			sm_mbdb_terminate();
266738032Speter			if (bitnset(M_SPECIFIC_UID, m->m_flags))
266880785Sgshapiro			{
2669132943Sgshapiro				if (m->m_uid == NO_UID)
2670132943Sgshapiro					new_euid = RunAsUid;
2671132943Sgshapiro				else
2672132943Sgshapiro					new_euid = m->m_uid;
267380785Sgshapiro
267480785Sgshapiro				/*
267580785Sgshapiro				**  Undo the effects of the uid change in main
267680785Sgshapiro				**  for signal handling.  The real uid may
267780785Sgshapiro				**  be used by mailer in adding a "From "
267880785Sgshapiro				**  line.
267980785Sgshapiro				*/
268080785Sgshapiro
268180785Sgshapiro				if (RealUid != 0 && RealUid != getuid())
268290792Sgshapiro				{
2683363466Sgshapiro#if MAILER_SETUID_METHOD == USE_SETEUID
2684363466Sgshapiro# if HASSETREUID
268590792Sgshapiro					if (setreuid(RealUid, geteuid()) < 0)
268690792Sgshapiro					{
268790792Sgshapiro						syserr("openmailer: setreuid(%d, %d) failed",
268890792Sgshapiro						       (int) RealUid, (int) geteuid());
268990792Sgshapiro						exit(EX_OSERR);
269090792Sgshapiro					}
2691363466Sgshapiro# endif /* HASSETREUID */
2692363466Sgshapiro#endif /* MAILER_SETUID_METHOD == USE_SETEUID */
2693363466Sgshapiro#if MAILER_SETUID_METHOD == USE_SETREUID
269480785Sgshapiro					new_ruid = RealUid;
2695363466Sgshapiro#endif
269690792Sgshapiro				}
269780785Sgshapiro			}
269838032Speter			else if (bitset(S_ISUID, stb.st_mode))
269938032Speter				new_ruid = stb.st_uid;
270038032Speter			else if (ctladdr != NULL && ctladdr->q_uid != 0)
270138032Speter				new_ruid = ctladdr->q_uid;
2702132943Sgshapiro			else if (m->m_uid != NO_UID)
270338032Speter				new_ruid = m->m_uid;
270438032Speter			else
270538032Speter				new_ruid = DefUid;
270694334Sgshapiro
2707363466Sgshapiro#if _FFR_USE_SETLOGIN
270894334Sgshapiro			/* run disconnected from terminal and set login name */
270994334Sgshapiro			if (setsid() >= 0 &&
271094334Sgshapiro			    ctladdr != NULL && ctladdr->q_uid != 0 &&
271194334Sgshapiro			    new_euid == ctladdr->q_uid)
271294334Sgshapiro			{
271394334Sgshapiro				struct passwd *pwd;
271494334Sgshapiro
271594334Sgshapiro				pwd = sm_getpwuid(ctladdr->q_uid);
271694334Sgshapiro				if (pwd != NULL && suidwarn)
271794334Sgshapiro					(void) setlogin(pwd->pw_name);
271894334Sgshapiro				endpwent();
271994334Sgshapiro			}
2720363466Sgshapiro#endif /* _FFR_USE_SETLOGIN */
272194334Sgshapiro
272238032Speter			if (new_euid != NO_UID)
272338032Speter			{
272464562Sgshapiro				if (RunAsUid != 0 && new_euid != RunAsUid)
272564562Sgshapiro				{
272664562Sgshapiro					/* Only root can change the uid */
2727285229Sgshapiro					syserr("openmailer: insufficient privileges to change uid, new_euid=%ld, RunAsUid=%ld",
2728285229Sgshapiro					       (long) new_euid, (long) RunAsUid);
272964562Sgshapiro					exit(EX_TEMPFAIL);
273064562Sgshapiro				}
273164562Sgshapiro
273238032Speter				vendor_set_uid(new_euid);
2733363466Sgshapiro#if MAILER_SETUID_METHOD == USE_SETEUID
273438032Speter				if (seteuid(new_euid) < 0 && suidwarn)
273564562Sgshapiro				{
273638032Speter					syserr("openmailer: seteuid(%ld) failed",
273790792Sgshapiro					       (long) new_euid);
273864562Sgshapiro					exit(EX_TEMPFAIL);
273964562Sgshapiro				}
2740363466Sgshapiro#endif /* MAILER_SETUID_METHOD == USE_SETEUID */
2741363466Sgshapiro#if MAILER_SETUID_METHOD == USE_SETREUID
274238032Speter				if (setreuid(new_ruid, new_euid) < 0 && suidwarn)
274364562Sgshapiro				{
274438032Speter					syserr("openmailer: setreuid(%ld, %ld) failed",
274590792Sgshapiro					       (long) new_ruid, (long) new_euid);
274664562Sgshapiro					exit(EX_TEMPFAIL);
274764562Sgshapiro				}
2748363466Sgshapiro#endif /* MAILER_SETUID_METHOD == USE_SETREUID */
2749363466Sgshapiro#if MAILER_SETUID_METHOD == USE_SETUID
275038032Speter				if (new_euid != geteuid() && setuid(new_euid) < 0 && suidwarn)
275164562Sgshapiro				{
275238032Speter					syserr("openmailer: setuid(%ld) failed",
275390792Sgshapiro					       (long) new_euid);
275464562Sgshapiro					exit(EX_TEMPFAIL);
275564562Sgshapiro				}
2756363466Sgshapiro#endif /* MAILER_SETUID_METHOD == USE_SETUID */
275738032Speter			}
275838032Speter			else if (new_ruid != NO_UID)
275938032Speter			{
276038032Speter				vendor_set_uid(new_ruid);
276138032Speter				if (setuid(new_ruid) < 0 && suidwarn)
276264562Sgshapiro				{
276338032Speter					syserr("openmailer: setuid(%ld) failed",
276490792Sgshapiro					       (long) new_ruid);
276564562Sgshapiro					exit(EX_TEMPFAIL);
276664562Sgshapiro				}
276738032Speter			}
276838032Speter
276938032Speter			if (tTd(11, 2))
2770285229Sgshapiro				sm_dprintf("openmailer: running as r/euid=%ld/%ld, r/egid=%ld/%ld\n",
2771285229Sgshapiro					   (long) getuid(), (long) geteuid(),
2772285229Sgshapiro					   (long) getgid(), (long) getegid());
277338032Speter
277438032Speter			/* move into some "safe" directory */
277538032Speter			if (m->m_execdir != NULL)
277638032Speter			{
277738032Speter				char *q;
277838032Speter
277938032Speter				for (p = m->m_execdir; p != NULL; p = q)
278038032Speter				{
278138032Speter					q = strchr(p, ':');
278238032Speter					if (q != NULL)
278338032Speter						*q = '\0';
2784168515Sgshapiro					expand(p, cbuf, sizeof(cbuf), e);
278538032Speter					if (q != NULL)
278638032Speter						*q++ = ':';
278738032Speter					if (tTd(11, 20))
278890792Sgshapiro						sm_dprintf("openmailer: trydir %s\n",
278998121Sgshapiro							   cbuf);
279098121Sgshapiro					if (cbuf[0] != '\0' &&
279198121Sgshapiro					    chdir(cbuf) >= 0)
279238032Speter						break;
279338032Speter				}
279438032Speter			}
279538032Speter
279690792Sgshapiro			/* Check safety of program to be run */
279790792Sgshapiro			sff = SFF_ROOTOK|SFF_EXECOK;
279890792Sgshapiro			if (!bitnset(DBS_RUNWRITABLEPROGRAM,
279990792Sgshapiro				     DontBlameSendmail))
280090792Sgshapiro				sff |= SFF_NOGWFILES|SFF_NOWWFILES;
280190792Sgshapiro			if (bitnset(DBS_RUNPROGRAMINUNSAFEDIRPATH,
280290792Sgshapiro				    DontBlameSendmail))
280390792Sgshapiro				sff |= SFF_NOPATHCHECK;
280490792Sgshapiro			else
280590792Sgshapiro				sff |= SFF_SAFEDIRPATH;
280690792Sgshapiro			ret = safefile(m->m_mailer, getuid(), getgid(),
280790792Sgshapiro				       user, sff, 0, NULL);
280890792Sgshapiro			if (ret != 0)
280990792Sgshapiro				sm_syslog(LOG_INFO, e->e_id,
281090792Sgshapiro					  "Warning: program %s unsafe: %s",
281190792Sgshapiro					  m->m_mailer, sm_errstring(ret));
281290792Sgshapiro
281338032Speter			/* arrange to filter std & diag output of command */
281464562Sgshapiro			(void) close(rpvect[0]);
281564562Sgshapiro			if (dup2(rpvect[1], STDOUT_FILENO) < 0)
281638032Speter			{
281764562Sgshapiro				syserr("%s... openmailer(%s): cannot dup pipe %d for stdout",
281864562Sgshapiro				       shortenstring(e->e_to, MAXSHORTSTR),
281964562Sgshapiro				       m->m_name, rpvect[1]);
282064562Sgshapiro				_exit(EX_OSERR);
282138032Speter			}
282264562Sgshapiro			(void) close(rpvect[1]);
282364562Sgshapiro
282438032Speter			if (dup2(STDOUT_FILENO, STDERR_FILENO) < 0)
282538032Speter			{
282638032Speter				syserr("%s... openmailer(%s): cannot dup stdout for stderr",
282790792Sgshapiro				       shortenstring(e->e_to, MAXSHORTSTR),
282890792Sgshapiro				       m->m_name);
282938032Speter				_exit(EX_OSERR);
283038032Speter			}
283138032Speter
283238032Speter			/* arrange to get standard input */
283338032Speter			(void) close(mpvect[1]);
283438032Speter			if (dup2(mpvect[0], STDIN_FILENO) < 0)
283538032Speter			{
283638032Speter				syserr("%s... openmailer(%s): cannot dup pipe %d for stdin",
283790792Sgshapiro				       shortenstring(e->e_to, MAXSHORTSTR),
283890792Sgshapiro				       m->m_name, mpvect[0]);
283938032Speter				_exit(EX_OSERR);
284038032Speter			}
284138032Speter			(void) close(mpvect[0]);
284238032Speter
284338032Speter			/* arrange for all the files to be closed */
2844132943Sgshapiro			sm_close_on_exec(STDERR_FILENO + 1, DtableSize);
284538032Speter
2846363466Sgshapiro#if !_FFR_USE_SETLOGIN
284738032Speter			/* run disconnected from terminal */
284838032Speter			(void) setsid();
2849363466Sgshapiro#endif
285038032Speter
285138032Speter			/* try to execute the mailer */
285264562Sgshapiro			(void) execve(m->m_mailer, (ARGV_T) pv,
285364562Sgshapiro				      (ARGV_T) UserEnviron);
285464562Sgshapiro			save_errno = errno;
285538032Speter			syserr("Cannot exec %s", m->m_mailer);
285638032Speter			if (bitnset(M_LOCALMAILER, m->m_flags) ||
285764562Sgshapiro			    transienterror(save_errno))
285838032Speter				_exit(EX_OSERR);
285938032Speter			_exit(EX_UNAVAILABLE);
286038032Speter		}
286138032Speter
286238032Speter		/*
286338032Speter		**  Set up return value.
286438032Speter		*/
286538032Speter
286638032Speter		if (mci == NULL)
286738032Speter		{
286890792Sgshapiro			if (clever)
286990792Sgshapiro			{
287090792Sgshapiro				/*
287190792Sgshapiro				**  Allocate from general heap, not
287290792Sgshapiro				**  envelope rpool, because this mci
287390792Sgshapiro				**  is going to be cached.
287490792Sgshapiro				*/
287590792Sgshapiro
287690792Sgshapiro				mci = mci_new(NULL);
287790792Sgshapiro			}
287890792Sgshapiro			else
287990792Sgshapiro			{
288090792Sgshapiro				/*
288190792Sgshapiro				**  Prevent a storage leak by allocating
288290792Sgshapiro				**  this from the envelope rpool.
288390792Sgshapiro				*/
288490792Sgshapiro
288590792Sgshapiro				mci = mci_new(e->e_rpool);
288690792Sgshapiro			}
288738032Speter		}
288838032Speter		mci->mci_mailer = m;
288938032Speter		if (clever)
289038032Speter		{
289138032Speter			mci->mci_state = MCIS_OPENING;
289238032Speter			mci_cache(mci);
289338032Speter		}
289438032Speter		else
289538032Speter		{
289638032Speter			mci->mci_state = MCIS_OPEN;
289738032Speter		}
289838032Speter		mci->mci_pid = pid;
289938032Speter		(void) close(mpvect[0]);
290090792Sgshapiro		mci->mci_out = sm_io_open(SmFtStdiofd, SM_TIME_DEFAULT,
2901132943Sgshapiro					  (void *) &(mpvect[1]), SM_IO_WRONLY_B,
290290792Sgshapiro					  NULL);
290338032Speter		if (mci->mci_out == NULL)
290438032Speter		{
290538032Speter			syserr("deliver: cannot create mailer output channel, fd=%d",
290690792Sgshapiro			       mpvect[1]);
290738032Speter			(void) close(mpvect[1]);
290864562Sgshapiro			(void) close(rpvect[0]);
290964562Sgshapiro			(void) close(rpvect[1]);
291038032Speter			rcode = EX_OSERR;
291138032Speter			goto give_up;
291238032Speter		}
291364562Sgshapiro
291464562Sgshapiro		(void) close(rpvect[1]);
291590792Sgshapiro		mci->mci_in = sm_io_open(SmFtStdiofd, SM_TIME_DEFAULT,
2916132943Sgshapiro					 (void *) &(rpvect[0]), SM_IO_RDONLY_B,
291790792Sgshapiro					 NULL);
291864562Sgshapiro		if (mci->mci_in == NULL)
291938032Speter		{
292064562Sgshapiro			syserr("deliver: cannot create mailer input channel, fd=%d",
292164562Sgshapiro			       mpvect[1]);
292264562Sgshapiro			(void) close(rpvect[0]);
292390792Sgshapiro			(void) sm_io_close(mci->mci_out, SM_TIME_DEFAULT);
292464562Sgshapiro			mci->mci_out = NULL;
292564562Sgshapiro			rcode = EX_OSERR;
292664562Sgshapiro			goto give_up;
292738032Speter		}
292838032Speter	}
292938032Speter
293038032Speter	/*
293138032Speter	**  If we are in SMTP opening state, send initial protocol.
293238032Speter	*/
293338032Speter
293438032Speter	if (bitnset(M_7BITS, m->m_flags) &&
293538032Speter	    (!clever || mci->mci_state == MCIS_OPENING))
293638032Speter		mci->mci_flags |= MCIF_7BIT;
293738032Speter	if (clever && mci->mci_state != MCIS_CLOSED)
293838032Speter	{
2939363466Sgshapiro#if STARTTLS || SASL
294090792Sgshapiro		char *srvname;
294190792Sgshapiro		extern SOCKADDR CurHostAddr;
2942363466Sgshapiro#endif /* STARTTLS || SASL */
294390792Sgshapiro
2944363466Sgshapiro#if SASL
2945363466Sgshapiro# define DONE_AUTH(f)		bitset(MCIF_AUTHACT, f)
2946363466Sgshapiro#endif
2947363466Sgshapiro#if STARTTLS
2948363466Sgshapiro# define DONE_STARTTLS(f)	bitset(MCIF_TLSACT, f)
2949363466Sgshapiro#endif
2950363466Sgshapiro#define ONLY_HELO(f)		bitset(MCIF_ONLY_EHLO, f)
2951363466Sgshapiro#define SET_HELO(f)		f |= MCIF_ONLY_EHLO
2952363466Sgshapiro#define CLR_HELO(f)		f &= ~MCIF_ONLY_EHLO
295338032Speter
2954363466Sgshapiro#if STARTTLS || SASL
295590792Sgshapiro		/* don't use CurHostName, it is changed in many places */
295690792Sgshapiro		if (mci->mci_host != NULL)
295790792Sgshapiro		{
295890792Sgshapiro			srvname = mci->mci_host;
2959363466Sgshapiro			RM_TRAIL_DOT(srvname);
296090792Sgshapiro		}
296190792Sgshapiro		else if (mci->mci_mailer != NULL)
296290792Sgshapiro		{
296390792Sgshapiro			srvname = mci->mci_mailer->m_name;
296490792Sgshapiro			dotpos = -1;
296590792Sgshapiro		}
296690792Sgshapiro		else
296790792Sgshapiro		{
296890792Sgshapiro			srvname = "local";
296990792Sgshapiro			dotpos = -1;
297090792Sgshapiro		}
297171345Sgshapiro
297290792Sgshapiro		/* don't set {server_name} to NULL or "": see getauth() */
297390792Sgshapiro		macdefine(&mci->mci_macro, A_TEMP, macid("{server_name}"),
297490792Sgshapiro			  srvname);
297564562Sgshapiro
297690792Sgshapiro		/* CurHostAddr is set by makeconnection() and mci_get() */
297790792Sgshapiro		if (CurHostAddr.sa.sa_family != 0)
297890792Sgshapiro		{
297990792Sgshapiro			macdefine(&mci->mci_macro, A_TEMP,
298090792Sgshapiro				  macid("{server_addr}"),
298190792Sgshapiro				  anynet_ntoa(&CurHostAddr));
298290792Sgshapiro		}
298390792Sgshapiro		else if (mci->mci_mailer != NULL)
298490792Sgshapiro		{
298590792Sgshapiro			/* mailer name is unique, use it as address */
298690792Sgshapiro			macdefine(&mci->mci_macro, A_PERM,
298790792Sgshapiro				  macid("{server_addr}"),
298890792Sgshapiro				  mci->mci_mailer->m_name);
298990792Sgshapiro		}
299090792Sgshapiro		else
299190792Sgshapiro		{
299290792Sgshapiro			/* don't set it to NULL or "": see getauth() */
299390792Sgshapiro			macdefine(&mci->mci_macro, A_PERM,
299490792Sgshapiro				  macid("{server_addr}"), "0");
299590792Sgshapiro		}
299690792Sgshapiro
2997363466Sgshapiro# if DANE
2998363466Sgshapiro		SM_FREE(dane_vrfy_ctx.dane_vrfy_host);
2999363466Sgshapiro		SM_FREE(dane_vrfy_ctx.dane_vrfy_sni);
3000363466Sgshapiro		dane_vrfy_ctx.dane_vrfy_fp[0] = '\0';
3001363466Sgshapiro		if (ste != NULL && ste->s_tlsa != NULL &&
3002363466Sgshapiro		    ste->s_tlsa->dane_tlsa_sni != NULL)
3003363466Sgshapiro			dane_vrfy_ctx.dane_vrfy_sni = sm_strdup(ste->s_tlsa->dane_tlsa_sni);
3004363466Sgshapiro		dane_vrfy_ctx.dane_vrfy_host = sm_strdup(srvname);
3005363466Sgshapiro# endif
3006363466Sgshapiro
300790792Sgshapiro		/* undo change of srvname (mci->mci_host) */
3008363466Sgshapiro		FIX_TRAIL_DOT(srvname);
300990792Sgshapiro
301090792Sgshapiroreconnect:	/* after switching to an encrypted connection */
3011363466Sgshapiro# if DANE
3012363466Sgshapiro		if (DONE_STARTTLS(mci->mci_flags))
3013363466Sgshapiro		{
3014363466Sgshapiro			/* use a "reset" function? */
3015363466Sgshapiro			SM_FREE(dane_vrfy_ctx.dane_vrfy_host);
3016363466Sgshapiro			SM_FREE(dane_vrfy_ctx.dane_vrfy_sni);
3017363466Sgshapiro			dane_vrfy_ctx.dane_vrfy_fp[0] = '\0';
3018363466Sgshapiro			dane_vrfy_ctx.dane_vrfy_res = 0;
3019363466Sgshapiro		}
3020363466Sgshapiro# endif
302190792Sgshapiro
3022363466Sgshapiro#endif /* STARTTLS || SASL */
3023363466Sgshapiro
302490792Sgshapiro		/* set the current connection information */
302590792Sgshapiro		e->e_mci = mci;
3026363466Sgshapiro#if SASL
302764562Sgshapiro		mci->mci_saslcap = NULL;
3028363466Sgshapiro#endif
302971345Sgshapiro		smtpinit(m, mci, e, ONLY_HELO(mci->mci_flags));
303071345Sgshapiro		CLR_HELO(mci->mci_flags);
303164562Sgshapiro
303290792Sgshapiro		if (IS_DLVR_RETURN(e))
303390792Sgshapiro		{
303490792Sgshapiro			/*
303590792Sgshapiro			**  Check whether other side can deliver e-mail
303690792Sgshapiro			**  fast enough
303790792Sgshapiro			*/
303890792Sgshapiro
303990792Sgshapiro			if (!bitset(MCIF_DLVR_BY, mci->mci_flags))
304090792Sgshapiro			{
304190792Sgshapiro				e->e_status = "5.4.7";
304290792Sgshapiro				usrerrenh(e->e_status,
304390792Sgshapiro					  "554 Server does not support Deliver By");
304490792Sgshapiro				rcode = EX_UNAVAILABLE;
304590792Sgshapiro				goto give_up;
304690792Sgshapiro			}
304790792Sgshapiro			if (e->e_deliver_by > 0 &&
304890792Sgshapiro			    e->e_deliver_by - (curtime() - e->e_ctime) <
304990792Sgshapiro			    mci->mci_min_by)
305090792Sgshapiro			{
305190792Sgshapiro				e->e_status = "5.4.7";
305290792Sgshapiro				usrerrenh(e->e_status,
305390792Sgshapiro					  "554 Message can't be delivered in time; %ld < %ld",
3054363466Sgshapiro					  e->e_deliver_by - (long) (curtime() -
3055363466Sgshapiro								e->e_ctime),
305690792Sgshapiro					  mci->mci_min_by);
305790792Sgshapiro				rcode = EX_UNAVAILABLE;
305890792Sgshapiro				goto give_up;
305990792Sgshapiro			}
306090792Sgshapiro		}
306190792Sgshapiro
3062363466Sgshapiro#if STARTTLS
306364562Sgshapiro		/* first TLS then AUTH to provide a security layer */
306471345Sgshapiro		if (mci->mci_state != MCIS_CLOSED &&
306571345Sgshapiro		    !DONE_STARTTLS(mci->mci_flags))
306664562Sgshapiro		{
306764562Sgshapiro			int olderrors;
306864562Sgshapiro			bool usetls;
306964562Sgshapiro			bool saveQuickAbort = QuickAbort;
307064562Sgshapiro			bool saveSuprErrs = SuprErrs;
307171345Sgshapiro			char *host = NULL;
307264562Sgshapiro
307364562Sgshapiro			rcode = EX_OK;
307464562Sgshapiro			usetls = bitset(MCIF_TLS, mci->mci_flags);
307590792Sgshapiro			if (usetls)
307690792Sgshapiro				usetls = !iscltflgset(e, D_NOTLS);
3077363466Sgshapiro			if (usetls)
3078363466Sgshapiro				usetls = tlsstate == 0;
307966494Sgshapiro
3080168515Sgshapiro			host = macvalue(macid("{server_name}"), e);
308164562Sgshapiro			if (usetls)
308264562Sgshapiro			{
308364562Sgshapiro				olderrors = Errors;
308490792Sgshapiro				QuickAbort = false;
308590792Sgshapiro				SuprErrs = true;
3086102528Sgshapiro				if (rscheck("try_tls", host, NULL, e,
3087285229Sgshapiro					    RSF_RMCOMM, 7, host, NOQID, NULL,
3088285229Sgshapiro					    NULL) != EX_OK
308964562Sgshapiro				    || Errors > olderrors)
3090168515Sgshapiro				{
309190792Sgshapiro					usetls = false;
3092168515Sgshapiro				}
309364562Sgshapiro				SuprErrs = saveSuprErrs;
309464562Sgshapiro				QuickAbort = saveQuickAbort;
309564562Sgshapiro			}
309664562Sgshapiro
309764562Sgshapiro			if (usetls)
309864562Sgshapiro			{
3099363466Sgshapiro				if ((rcode = starttls(m, mci, e
3100363466Sgshapiro# if DANE
3101363466Sgshapiro							, &dane_vrfy_ctx
3102363466Sgshapiro# endif
3103363466Sgshapiro					)) == EX_OK)
310464562Sgshapiro				{
310564562Sgshapiro					/* start again without STARTTLS */
310664562Sgshapiro					mci->mci_flags |= MCIF_TLSACT;
310764562Sgshapiro				}
310864562Sgshapiro				else
310964562Sgshapiro				{
311064562Sgshapiro					char *s;
311164562Sgshapiro
311264562Sgshapiro					/*
3113203004Sgshapiro					**  TLS negotiation failed, what to do?
311464562Sgshapiro					**  fall back to unencrypted connection
311564562Sgshapiro					**  or abort? How to decide?
311664562Sgshapiro					**  set a macro and call a ruleset.
311764562Sgshapiro					*/
311890792Sgshapiro
311964562Sgshapiro					mci->mci_flags &= ~MCIF_TLS;
312064562Sgshapiro					switch (rcode)
312164562Sgshapiro					{
312264562Sgshapiro					  case EX_TEMPFAIL:
312364562Sgshapiro						s = "TEMP";
312464562Sgshapiro						break;
312564562Sgshapiro					  case EX_USAGE:
312664562Sgshapiro						s = "USAGE";
312764562Sgshapiro						break;
312864562Sgshapiro					  case EX_PROTOCOL:
312964562Sgshapiro						s = "PROTOCOL";
313064562Sgshapiro						break;
313164562Sgshapiro					  case EX_SOFTWARE:
313264562Sgshapiro						s = "SOFTWARE";
313364562Sgshapiro						break;
3134157001Sgshapiro					  case EX_UNAVAILABLE:
3135157001Sgshapiro						s = "NONE";
3136157001Sgshapiro						break;
313764562Sgshapiro
313864562Sgshapiro					  /* everything else is a failure */
313964562Sgshapiro					  default:
314064562Sgshapiro						s = "FAILURE";
314164562Sgshapiro						rcode = EX_TEMPFAIL;
314264562Sgshapiro					}
314390792Sgshapiro					macdefine(&e->e_macro, A_PERM,
314490792Sgshapiro						  macid("{verify}"), s);
314564562Sgshapiro				}
314664562Sgshapiro			}
314764562Sgshapiro			else
3148363466Sgshapiro			{
3149363466Sgshapiro				p = tlsstate == 0 ? "NONE": "CLEAR";
3150363466Sgshapiro# if DANE
3151363466Sgshapiro				/*
3152363466Sgshapiro				**  TLSA found but STARTTLS not offered?
3153363466Sgshapiro				**  What is the best way to "fail"?
3154363466Sgshapiro				**  XXX: check expiration!
3155363466Sgshapiro				*/
3156363466Sgshapiro
3157363466Sgshapiro				if (!bitset(MCIF_TLS, mci->mci_flags) &&
3158363466Sgshapiro				    ste != NULL &&
3159363466Sgshapiro				    ste->s_tlsa != NULL &&
3160363466Sgshapiro				    ste->s_tlsa->dane_tlsa_n > 0)
3161363466Sgshapiro				{
3162363466Sgshapiro					if (LogLevel > 8)
3163363466Sgshapiro						sm_syslog(LOG_NOTICE, NOQID,
3164363466Sgshapiro							"STARTTLS=client, relay=%.100s, warning=DANE configured in DNS but no STARTTLS available",
3165363466Sgshapiro							host);
3166363466Sgshapiro					/* XXX include TLSA RR from DNS? */
3167363466Sgshapiro
3168363466Sgshapiro					p = "DANE_FAIL";
3169363466Sgshapiro				}
3170363466Sgshapiro# endif /* DANE */
317190792Sgshapiro				macdefine(&e->e_macro, A_PERM,
3172363466Sgshapiro					  macid("{verify}"), p);
3173363466Sgshapiro			}
317464562Sgshapiro			olderrors = Errors;
317590792Sgshapiro			QuickAbort = false;
317690792Sgshapiro			SuprErrs = true;
317764562Sgshapiro
317864562Sgshapiro			/*
317964562Sgshapiro			**  rcode == EX_SOFTWARE is special:
3180203004Sgshapiro			**  the TLS negotiation failed
318164562Sgshapiro			**  we have to drop the connection no matter what
318264562Sgshapiro			**  However, we call tls_server to give it the chance
318364562Sgshapiro			**  to log the problem and return an appropriate
318464562Sgshapiro			**  error code.
318564562Sgshapiro			*/
318690792Sgshapiro
318764562Sgshapiro			if (rscheck("tls_server",
318890792Sgshapiro				    macvalue(macid("{verify}"), e),
3189102528Sgshapiro				    NULL, e, RSF_RMCOMM|RSF_COUNT, 5,
3190285229Sgshapiro				    host, NOQID, NULL, NULL) != EX_OK ||
319164562Sgshapiro			    Errors > olderrors ||
319264562Sgshapiro			    rcode == EX_SOFTWARE)
319364562Sgshapiro			{
319464562Sgshapiro				char enhsc[ENHSCLEN];
319564562Sgshapiro				extern char MsgBuf[];
319664562Sgshapiro
319764562Sgshapiro				if (ISSMTPCODE(MsgBuf) &&
319864562Sgshapiro				    extenhsc(MsgBuf + 4, ' ', enhsc) > 0)
319964562Sgshapiro				{
320090792Sgshapiro					p = sm_rpool_strdup_x(e->e_rpool,
320190792Sgshapiro							      MsgBuf);
320264562Sgshapiro				}
320364562Sgshapiro				else
320464562Sgshapiro				{
320564562Sgshapiro					p = "403 4.7.0 server not authenticated.";
320690792Sgshapiro					(void) sm_strlcpy(enhsc, "4.7.0",
3207168515Sgshapiro							  sizeof(enhsc));
320864562Sgshapiro				}
320964562Sgshapiro				SuprErrs = saveSuprErrs;
321064562Sgshapiro				QuickAbort = saveQuickAbort;
321164562Sgshapiro
321264562Sgshapiro				if (rcode == EX_SOFTWARE)
321364562Sgshapiro				{
321464562Sgshapiro					/* drop the connection */
321564562Sgshapiro					mci->mci_state = MCIS_QUITING;
321664562Sgshapiro					if (mci->mci_in != NULL)
321764562Sgshapiro					{
321890792Sgshapiro						(void) sm_io_close(mci->mci_in,
321990792Sgshapiro								   SM_TIME_DEFAULT);
322064562Sgshapiro						mci->mci_in = NULL;
322164562Sgshapiro					}
322264562Sgshapiro					mci->mci_flags &= ~MCIF_TLSACT;
322364562Sgshapiro					(void) endmailer(mci, e, pv);
3224363466Sgshapiro
3225363466Sgshapiro					if ((TLSFallbacktoClear ||
3226363466Sgshapiro					     SM_TLSI_IS(&(mci->mci_tlsi),
3227363466Sgshapiro							TLSI_FL_FB2CLR)) &&
3228363466Sgshapiro					    !SM_TLSI_IS(&(mci->mci_tlsi),
3229363466Sgshapiro							TLSI_FL_NOFB2CLR)
3230363466Sgshapiro# if DANE
3231363466Sgshapiro					     && dane_vrfy_ctx.dane_vrfy_chk !=
3232363466Sgshapiro						DANE_SECURE
3233363466Sgshapiro# endif
3234363466Sgshapiro					    )
3235363466Sgshapiro					{
3236363466Sgshapiro						++tlsstate;
3237363466Sgshapiro					}
323864562Sgshapiro				}
323964562Sgshapiro				else
324064562Sgshapiro				{
324164562Sgshapiro					/* abort transfer */
324264562Sgshapiro					smtpquit(m, mci, e);
324364562Sgshapiro				}
324464562Sgshapiro
324571345Sgshapiro				/* avoid bogus error msg */
324671345Sgshapiro				mci->mci_errno = 0;
324771345Sgshapiro
324864562Sgshapiro				/* temp or permanent failure? */
324964562Sgshapiro				rcode = (*p == '4') ? EX_TEMPFAIL
325064562Sgshapiro						    : EX_UNAVAILABLE;
325190792Sgshapiro				mci_setstat(mci, rcode, enhsc, p);
325264562Sgshapiro
325364562Sgshapiro				/*
325464562Sgshapiro				**  hack to get the error message into
325564562Sgshapiro				**  the envelope (done in giveresponse())
325664562Sgshapiro				*/
325790792Sgshapiro
325890792Sgshapiro				(void) sm_strlcpy(SmtpError, p,
3259168515Sgshapiro						  sizeof(SmtpError));
326064562Sgshapiro			}
3261168515Sgshapiro			else if (mci->mci_state == MCIS_CLOSED)
3262168515Sgshapiro			{
3263168515Sgshapiro				/* connection close caused by 421 */
3264168515Sgshapiro				mci->mci_errno = 0;
3265168515Sgshapiro				rcode = EX_TEMPFAIL;
3266168515Sgshapiro				mci_setstat(mci, rcode, NULL, "421");
3267168515Sgshapiro			}
3268168515Sgshapiro			else
3269168515Sgshapiro				rcode = 0;
3270168515Sgshapiro
327164562Sgshapiro			QuickAbort = saveQuickAbort;
327264562Sgshapiro			SuprErrs = saveSuprErrs;
327371345Sgshapiro			if (DONE_STARTTLS(mci->mci_flags) &&
327471345Sgshapiro			    mci->mci_state != MCIS_CLOSED)
327564562Sgshapiro			{
327671345Sgshapiro				SET_HELO(mci->mci_flags);
3277223067Sgshapiro				mci_clr_extensions(mci);
327864562Sgshapiro				goto reconnect;
327964562Sgshapiro			}
3280363466Sgshapiro			if (tlsstate == 1)
3281363466Sgshapiro			{
3282363466Sgshapiro				if (tTd(11, 1))
3283363466Sgshapiro				{
3284363466Sgshapiro					sm_syslog(LOG_DEBUG, NOQID,
3285363466Sgshapiro						"STARTTLS=client, relay=%.100s, tlsstate=%d, status=trying_again",
3286363466Sgshapiro						mci->mci_host, tlsstate);
3287363466Sgshapiro					mci_dump(NULL, mci, true);
3288363466Sgshapiro				}
3289363466Sgshapiro				++tlsstate;
3290363466Sgshapiro
3291363466Sgshapiro				/*
3292363466Sgshapiro				**  Fake the status so a new connection is
3293363466Sgshapiro				**  tried, otherwise the TLS error will
3294363466Sgshapiro				**  "persist" during this delivery attempt.
3295363466Sgshapiro				*/
3296363466Sgshapiro
3297363466Sgshapiro				mci->mci_errno = 0;
3298363466Sgshapiro				rcode = EX_OK;
3299363466Sgshapiro				mci_setstat(mci, rcode, NULL, NULL);
3300363466Sgshapiro				goto one_last_try;
3301363466Sgshapiro}
330264562Sgshapiro		}
3303363466Sgshapiro#endif /* STARTTLS */
3304363466Sgshapiro#if SASL
330564562Sgshapiro		/* if other server supports authentication let's authenticate */
330664562Sgshapiro		if (mci->mci_state != MCIS_CLOSED &&
330764562Sgshapiro		    mci->mci_saslcap != NULL &&
330890792Sgshapiro		    !DONE_AUTH(mci->mci_flags) && !iscltflgset(e, D_NOAUTH))
330964562Sgshapiro		{
331090792Sgshapiro			/* Should we require some minimum authentication? */
331190792Sgshapiro			if ((ret = smtpauth(m, mci, e)) == EX_OK)
331264562Sgshapiro			{
331364562Sgshapiro				int result;
331490792Sgshapiro				sasl_ssf_t *ssf = NULL;
331564562Sgshapiro
331690792Sgshapiro				/* Get security strength (features) */
331764562Sgshapiro				result = sasl_getprop(mci->mci_conn, SASL_SSF,
331898121Sgshapiro# if SASL >= 20000
331998121Sgshapiro						      (const void **) &ssf);
3320363466Sgshapiro# else
332164562Sgshapiro						      (void **) &ssf);
3322363466Sgshapiro# endif
332390792Sgshapiro
332490792Sgshapiro				/* XXX authid? */
332564562Sgshapiro				if (LogLevel > 9)
332664562Sgshapiro					sm_syslog(LOG_INFO, NOQID,
332790792Sgshapiro						  "AUTH=client, relay=%.100s, mech=%.16s, bits=%d",
332864562Sgshapiro						  mci->mci_host,
332990792Sgshapiro						  macvalue(macid("{auth_type}"), e),
333090792Sgshapiro						  result == SASL_OK ? *ssf : 0);
333177349Sgshapiro
333264562Sgshapiro				/*
333390792Sgshapiro				**  Only switch to encrypted connection
333464562Sgshapiro				**  if a security layer has been negotiated
333564562Sgshapiro				*/
333690792Sgshapiro
333764562Sgshapiro				if (result == SASL_OK && *ssf > 0)
333864562Sgshapiro				{
3339159609Sgshapiro					int tmo;
3340159609Sgshapiro
334164562Sgshapiro					/*
334290792Sgshapiro					**  Convert I/O layer to use SASL.
334390792Sgshapiro					**  If the call fails, the connection
334490792Sgshapiro					**  is aborted.
334564562Sgshapiro					*/
334690792Sgshapiro
3347159609Sgshapiro					tmo = DATA_PROGRESS_TIMEOUT * 1000;
334890792Sgshapiro					if (sfdcsasl(&mci->mci_in,
334990792Sgshapiro						     &mci->mci_out,
3350159609Sgshapiro						     mci->mci_conn, tmo) == 0)
335164562Sgshapiro					{
3352223067Sgshapiro						mci_clr_extensions(mci);
335390792Sgshapiro						mci->mci_flags |= MCIF_AUTHACT|
335490792Sgshapiro								  MCIF_ONLY_EHLO;
335564562Sgshapiro						goto reconnect;
335664562Sgshapiro					}
335790792Sgshapiro					syserr("AUTH TLS switch failed in client");
335864562Sgshapiro				}
335964562Sgshapiro				/* else? XXX */
336064562Sgshapiro				mci->mci_flags |= MCIF_AUTHACT;
336164562Sgshapiro
336264562Sgshapiro			}
336390792Sgshapiro			else if (ret == EX_TEMPFAIL)
336490792Sgshapiro			{
336590792Sgshapiro				if (LogLevel > 8)
336690792Sgshapiro					sm_syslog(LOG_ERR, NOQID,
336790792Sgshapiro						  "AUTH=client, relay=%.100s, temporary failure, connection abort",
336890792Sgshapiro						  mci->mci_host);
336990792Sgshapiro				smtpquit(m, mci, e);
337090792Sgshapiro
337190792Sgshapiro				/* avoid bogus error msg */
337290792Sgshapiro				mci->mci_errno = 0;
337390792Sgshapiro				rcode = EX_TEMPFAIL;
3374132943Sgshapiro				mci_setstat(mci, rcode, "4.3.0", p);
337590792Sgshapiro
337690792Sgshapiro				/*
337790792Sgshapiro				**  hack to get the error message into
337890792Sgshapiro				**  the envelope (done in giveresponse())
337990792Sgshapiro				*/
338090792Sgshapiro
338190792Sgshapiro				(void) sm_strlcpy(SmtpError,
338290792Sgshapiro						  "Temporary AUTH failure",
3383168515Sgshapiro						  sizeof(SmtpError));
338490792Sgshapiro			}
338564562Sgshapiro		}
3386363466Sgshapiro#endif /* SASL */
338738032Speter	}
338838032Speter
338938032Speterdo_transfer:
339038032Speter	/* clear out per-message flags from connection structure */
339138032Speter	mci->mci_flags &= ~(MCIF_CVT7TO8|MCIF_CVT8TO7);
339238032Speter
339338032Speter	if (bitset(EF_HAS8BIT, e->e_flags) &&
339438032Speter	    !bitset(EF_DONT_MIME, e->e_flags) &&
339538032Speter	    bitnset(M_7BITS, m->m_flags))
339638032Speter		mci->mci_flags |= MCIF_CVT8TO7;
339738032Speter
339838032Speter#if MIME7TO8
339938032Speter	if (bitnset(M_MAKE8BIT, m->m_flags) &&
340038032Speter	    !bitset(MCIF_7BIT, mci->mci_flags) &&
340138032Speter	    (p = hvalue("Content-Transfer-Encoding", e->e_header)) != NULL &&
340290792Sgshapiro	     (sm_strcasecmp(p, "quoted-printable") == 0 ||
340390792Sgshapiro	      sm_strcasecmp(p, "base64") == 0) &&
340438032Speter	    (p = hvalue("Content-Type", e->e_header)) != NULL)
340538032Speter	{
340638032Speter		/* may want to convert 7 -> 8 */
340738032Speter		/* XXX should really parse it here -- and use a class XXX */
340890792Sgshapiro		if (sm_strncasecmp(p, "text/plain", 10) == 0 &&
340938032Speter		    (p[10] == '\0' || p[10] == ' ' || p[10] == ';'))
341038032Speter			mci->mci_flags |= MCIF_CVT7TO8;
341138032Speter	}
341264562Sgshapiro#endif /* MIME7TO8 */
341338032Speter
341438032Speter	if (tTd(11, 1))
341538032Speter	{
341690792Sgshapiro		sm_dprintf("openmailer: ");
3417132943Sgshapiro		mci_dump(sm_debug_file(), mci, false);
341838032Speter	}
341938032Speter
342090792Sgshapiro#if _FFR_CLIENT_SIZE
342190792Sgshapiro	/*
342290792Sgshapiro	**  See if we know the maximum size and
342390792Sgshapiro	**  abort if the message is too big.
342490792Sgshapiro	**
342590792Sgshapiro	**  NOTE: _FFR_CLIENT_SIZE is untested.
342690792Sgshapiro	*/
342790792Sgshapiro
342890792Sgshapiro	if (bitset(MCIF_SIZE, mci->mci_flags) &&
342990792Sgshapiro	    mci->mci_maxsize > 0 &&
343090792Sgshapiro	    e->e_msgsize > mci->mci_maxsize)
343190792Sgshapiro	{
343290792Sgshapiro		e->e_flags |= EF_NO_BODY_RETN;
343390792Sgshapiro		if (bitnset(M_LOCALMAILER, m->m_flags))
343490792Sgshapiro			e->e_status = "5.2.3";
343590792Sgshapiro		else
343690792Sgshapiro			e->e_status = "5.3.4";
343790792Sgshapiro
343890792Sgshapiro		usrerrenh(e->e_status,
343990792Sgshapiro			  "552 Message is too large; %ld bytes max",
344090792Sgshapiro			  mci->mci_maxsize);
344190792Sgshapiro		rcode = EX_DATAERR;
344290792Sgshapiro
344390792Sgshapiro		/* Need an e_message for error */
3444168515Sgshapiro		(void) sm_snprintf(SmtpError, sizeof(SmtpError),
344590792Sgshapiro				   "Message is too large; %ld bytes max",
344690792Sgshapiro				   mci->mci_maxsize);
344790792Sgshapiro		goto give_up;
344890792Sgshapiro	}
344990792Sgshapiro#endif /* _FFR_CLIENT_SIZE */
345090792Sgshapiro
345138032Speter	if (mci->mci_state != MCIS_OPEN)
345238032Speter	{
345338032Speter		/* couldn't open the mailer */
345438032Speter		rcode = mci->mci_exitstat;
345538032Speter		errno = mci->mci_errno;
345673188Sgshapiro		SM_SET_H_ERRNO(mci->mci_herrno);
345738032Speter		if (rcode == EX_OK)
345838032Speter		{
345938032Speter			/* shouldn't happen */
346064562Sgshapiro			syserr("554 5.3.5 deliver: mci=%lx rcode=%d errno=%d state=%d sig=%s",
346190792Sgshapiro			       (unsigned long) mci, rcode, errno,
346290792Sgshapiro			       mci->mci_state, firstsig);
3463132943Sgshapiro			mci_dump_all(smioout, true);
346438032Speter			rcode = EX_SOFTWARE;
346538032Speter		}
346664562Sgshapiro		else if (nummxhosts > hostnum)
346738032Speter		{
346838032Speter			/* try next MX site */
346938032Speter			goto tryhost;
347038032Speter		}
347138032Speter	}
347238032Speter	else if (!clever)
347338032Speter	{
3474157001Sgshapiro		bool ok;
3475157001Sgshapiro
347638032Speter		/*
347738032Speter		**  Format and send message.
347838032Speter		*/
347938032Speter
3480157001Sgshapiro		rcode = EX_OK;
3481157001Sgshapiro		errno = 0;
3482157001Sgshapiro		ok = putfromline(mci, e);
3483157001Sgshapiro		if (ok)
3484157001Sgshapiro			ok = (*e->e_puthdr)(mci, e->e_header, e, M87F_OUTER);
3485157001Sgshapiro		if (ok)
3486157001Sgshapiro			ok = (*e->e_putbody)(mci, e, NULL);
3487173340Sgshapiro		if (ok && bitset(MCIF_INLONGLINE, mci->mci_flags))
3488173340Sgshapiro			ok = putline("", mci);
348938032Speter
3490157001Sgshapiro		/*
3491157001Sgshapiro		**  Ignore an I/O error that was caused by EPIPE.
3492157001Sgshapiro		**  Some broken mailers don't read the entire body
3493157001Sgshapiro		**  but just exit() thus causing an I/O error.
3494157001Sgshapiro		*/
3495157001Sgshapiro
3496157001Sgshapiro		if (!ok && (sm_io_error(mci->mci_out) && errno == EPIPE))
3497157001Sgshapiro			ok = true;
3498157001Sgshapiro
3499157001Sgshapiro		/* (always) get the exit status */
350038032Speter		rcode = endmailer(mci, e, pv);
3501157001Sgshapiro		if (!ok)
3502157001Sgshapiro			rcode = EX_TEMPFAIL;
350390792Sgshapiro		if (rcode == EX_TEMPFAIL && SmtpError[0] == '\0')
350473188Sgshapiro		{
350573188Sgshapiro			/*
350673188Sgshapiro			**  Need an e_message for mailq display.
350773188Sgshapiro			**  We set SmtpError as
350873188Sgshapiro			*/
350973188Sgshapiro
3510168515Sgshapiro			(void) sm_snprintf(SmtpError, sizeof(SmtpError),
351190792Sgshapiro					   "%s mailer (%s) exited with EX_TEMPFAIL",
351290792Sgshapiro					   m->m_name, m->m_mailer);
351373188Sgshapiro		}
351438032Speter	}
351538032Speter	else
351638032Speter	{
351738032Speter		/*
351838032Speter		**  Send the MAIL FROM: protocol
351938032Speter		*/
352038032Speter
352190792Sgshapiro		/* XXX this isn't pipelined... */
352238032Speter		rcode = smtpmailfrom(m, mci, e);
352338032Speter		if (rcode == EX_OK)
352438032Speter		{
352538032Speter			register int i;
3526363466Sgshapiro#if PIPELINING
352790792Sgshapiro			ADDRESS *volatile pchain;
3528363466Sgshapiro#endif
352938032Speter
353038032Speter			/* send the recipient list */
353138032Speter			tobuf[0] = '\0';
353290792Sgshapiro			mci->mci_retryrcpt = false;
353390792Sgshapiro			mci->mci_tolist = tobuf;
3534363466Sgshapiro#if PIPELINING
353590792Sgshapiro			pchain = NULL;
353690792Sgshapiro			mci->mci_nextaddr = NULL;
3537363466Sgshapiro#endif
353864562Sgshapiro
353938032Speter			for (to = tochain; to != NULL; to = to->q_tchain)
354038032Speter			{
354190792Sgshapiro				if (!QS_IS_UNMARKED(to->q_state))
354238032Speter					continue;
354364562Sgshapiro
354490792Sgshapiro				/* mark recipient state as "ok so far" */
354590792Sgshapiro				to->q_state = QS_OK;
354690792Sgshapiro				e->e_to = to->q_paddr;
3547363466Sgshapiro#if STARTTLS
354864562Sgshapiro				i = rscheck("tls_rcpt", to->q_user, NULL, e,
3549102528Sgshapiro					    RSF_RMCOMM|RSF_COUNT, 3,
3550285229Sgshapiro					    mci->mci_host, e->e_id, NULL, NULL);
355164562Sgshapiro				if (i != EX_OK)
355238032Speter				{
355390792Sgshapiro					markfailure(e, to, mci, i, false);
355490792Sgshapiro					giveresponse(i, to->q_status,  m, mci,
355590792Sgshapiro						     ctladdr, xstart, e, to);
355690792Sgshapiro					if (i == EX_TEMPFAIL)
355790792Sgshapiro					{
355890792Sgshapiro						mci->mci_retryrcpt = true;
355990792Sgshapiro						to->q_state = QS_RETRY;
356090792Sgshapiro					}
356164562Sgshapiro					continue;
356238032Speter				}
3563363466Sgshapiro#endif /* STARTTLS */
356464562Sgshapiro
356590792Sgshapiro				i = smtprcpt(to, m, mci, e, ctladdr, xstart);
3566363466Sgshapiro#if PIPELINING
356790792Sgshapiro				if (i == EX_OK &&
356890792Sgshapiro				    bitset(MCIF_PIPELINED, mci->mci_flags))
356964562Sgshapiro				{
357090792Sgshapiro					/*
357190792Sgshapiro					**  Add new element to list of
357290792Sgshapiro					**  recipients for pipelining.
357390792Sgshapiro					*/
357490792Sgshapiro
357590792Sgshapiro					to->q_pchain = NULL;
357690792Sgshapiro					if (mci->mci_nextaddr == NULL)
357790792Sgshapiro						mci->mci_nextaddr = to;
357890792Sgshapiro					if (pchain == NULL)
357990792Sgshapiro						pchain = to;
358090792Sgshapiro					else
358190792Sgshapiro					{
358290792Sgshapiro						pchain->q_pchain = to;
358390792Sgshapiro						pchain = pchain->q_pchain;
358490792Sgshapiro					}
358564562Sgshapiro				}
3586363466Sgshapiro#endif /* PIPELINING */
358790792Sgshapiro				if (i != EX_OK)
358838032Speter				{
358990792Sgshapiro					markfailure(e, to, mci, i, false);
359098841Sgshapiro					giveresponse(i, to->q_status, m, mci,
359190792Sgshapiro						     ctladdr, xstart, e, to);
359290792Sgshapiro					if (i == EX_TEMPFAIL)
359390792Sgshapiro						to->q_state = QS_RETRY;
359438032Speter				}
359538032Speter			}
359638032Speter
359790792Sgshapiro			/* No recipients in list and no missing responses? */
359890792Sgshapiro			if (tobuf[0] == '\0'
3599363466Sgshapiro#if PIPELINING
3600173340Sgshapiro			    && bitset(MCIF_PIPELINED, mci->mci_flags)
360190792Sgshapiro			    && mci->mci_nextaddr == NULL
3602363466Sgshapiro#endif
360390792Sgshapiro			   )
360438032Speter			{
360538032Speter				rcode = EX_OK;
360638032Speter				e->e_to = NULL;
360738032Speter				if (bitset(MCIF_CACHED, mci->mci_flags))
360838032Speter					smtprset(m, mci, e);
360938032Speter			}
361038032Speter			else
361138032Speter			{
361238032Speter				e->e_to = tobuf + 1;
361390792Sgshapiro				rcode = smtpdata(m, mci, e, ctladdr, xstart);
361438032Speter			}
361538032Speter		}
361664562Sgshapiro		if (rcode == EX_TEMPFAIL && nummxhosts > hostnum)
361738032Speter		{
361838032Speter			/* try next MX site */
361938032Speter			goto tryhost;
362038032Speter		}
362138032Speter	}
362238032Speter#if NAMED_BIND
362338032Speter	if (ConfigLevel < 2)
362438032Speter		_res.options |= RES_DEFNAMES | RES_DNSRCH;	/* XXX */
3625363466Sgshapiro#endif
362638032Speter
362738032Speter	if (tTd(62, 1))
362838032Speter		checkfds("after delivery");
362938032Speter
363038032Speter	/*
363138032Speter	**  Do final status disposal.
363238032Speter	**	We check for something in tobuf for the SMTP case.
363338032Speter	**	If we got a temporary failure, arrange to queue the
363438032Speter	**		addressees.
363538032Speter	*/
363638032Speter
363738032Speter  give_up:
363838032Speter	if (bitnset(M_LMTP, m->m_flags))
363938032Speter	{
364038032Speter		lmtp_rcode = rcode;
364138032Speter		tobuf[0] = '\0';
364290792Sgshapiro		anyok = false;
364390792Sgshapiro		strsize = 0;
364438032Speter	}
364538032Speter	else
364638032Speter		anyok = rcode == EX_OK;
364738032Speter
364838032Speter	for (to = tochain; to != NULL; to = to->q_tchain)
364938032Speter	{
365038032Speter		/* see if address already marked */
365164562Sgshapiro		if (!QS_IS_OK(to->q_state))
365238032Speter			continue;
365338032Speter
365438032Speter		/* if running LMTP, get the status for each address */
365538032Speter		if (bitnset(M_LMTP, m->m_flags))
365638032Speter		{
365738032Speter			if (lmtp_rcode == EX_OK)
365838032Speter				rcode = smtpgetstat(m, mci, e);
365938032Speter			if (rcode == EX_OK)
366038032Speter			{
366190792Sgshapiro				strsize += sm_strlcat2(tobuf + strsize, ",",
366290792Sgshapiro						to->q_paddr,
366390792Sgshapiro						tobufsize - strsize);
366490792Sgshapiro				SM_ASSERT(strsize < tobufsize);
366590792Sgshapiro				anyok = true;
366638032Speter			}
366738032Speter			else
366838032Speter			{
366938032Speter				e->e_to = to->q_paddr;
367090792Sgshapiro				markfailure(e, to, mci, rcode, true);
367164562Sgshapiro				giveresponse(rcode, to->q_status, m, mci,
367290792Sgshapiro					     ctladdr, xstart, e, to);
367338032Speter				e->e_to = tobuf + 1;
367438032Speter				continue;
367538032Speter			}
367638032Speter		}
367738032Speter		else
367838032Speter		{
367938032Speter			/* mark bad addresses */
368038032Speter			if (rcode != EX_OK)
368138032Speter			{
368238032Speter				if (goodmxfound && rcode == EX_NOHOST)
368338032Speter					rcode = EX_TEMPFAIL;
368490792Sgshapiro				markfailure(e, to, mci, rcode, true);
368538032Speter				continue;
368638032Speter			}
368738032Speter		}
368838032Speter
368938032Speter		/* successful delivery */
369064562Sgshapiro		to->q_state = QS_SENT;
369138032Speter		to->q_statdate = curtime();
369238032Speter		e->e_nsent++;
369364562Sgshapiro
369464562Sgshapiro		/*
369564562Sgshapiro		**  Checkpoint the send list every few addresses
369664562Sgshapiro		*/
369764562Sgshapiro
369866494Sgshapiro		if (CheckpointInterval > 0 && e->e_nsent >= CheckpointInterval)
369964562Sgshapiro		{
370090792Sgshapiro			queueup(e, false, false);
370164562Sgshapiro			e->e_nsent = 0;
370264562Sgshapiro		}
370364562Sgshapiro
370438032Speter		if (bitnset(M_LOCALMAILER, m->m_flags) &&
370538032Speter		    bitset(QPINGONSUCCESS, to->q_flags))
370638032Speter		{
370738032Speter			to->q_flags |= QDELIVERED;
370838032Speter			to->q_status = "2.1.5";
370990792Sgshapiro			(void) sm_io_fprintf(e->e_xfp, SM_TIME_DEFAULT,
371090792Sgshapiro					     "%s... Successfully delivered\n",
371190792Sgshapiro					     to->q_paddr);
371238032Speter		}
371338032Speter		else if (bitset(QPINGONSUCCESS, to->q_flags) &&
371438032Speter			 bitset(QPRIMARY, to->q_flags) &&
371538032Speter			 !bitset(MCIF_DSN, mci->mci_flags))
371638032Speter		{
371738032Speter			to->q_flags |= QRELAYED;
371890792Sgshapiro			(void) sm_io_fprintf(e->e_xfp, SM_TIME_DEFAULT,
371990792Sgshapiro					     "%s... relayed; expect no further notifications\n",
372090792Sgshapiro					     to->q_paddr);
372138032Speter		}
372290792Sgshapiro		else if (IS_DLVR_NOTIFY(e) &&
372390792Sgshapiro			 !bitset(MCIF_DLVR_BY, mci->mci_flags) &&
372490792Sgshapiro			 bitset(QPRIMARY, to->q_flags) &&
372590792Sgshapiro			 (!bitset(QHASNOTIFY, to->q_flags) ||
372690792Sgshapiro			  bitset(QPINGONSUCCESS, to->q_flags) ||
372790792Sgshapiro			  bitset(QPINGONFAILURE, to->q_flags) ||
372890792Sgshapiro			  bitset(QPINGONDELAY, to->q_flags)))
372990792Sgshapiro		{
373090792Sgshapiro			/* RFC 2852, 4.1.4.2: no NOTIFY, or not NEVER */
373190792Sgshapiro			to->q_flags |= QBYNRELAY;
373290792Sgshapiro			(void) sm_io_fprintf(e->e_xfp, SM_TIME_DEFAULT,
373390792Sgshapiro					     "%s... Deliver-by notify: relayed\n",
373490792Sgshapiro					     to->q_paddr);
373590792Sgshapiro		}
373690792Sgshapiro		else if (IS_DLVR_TRACE(e) &&
373790792Sgshapiro			 (!bitset(QHASNOTIFY, to->q_flags) ||
373890792Sgshapiro			  bitset(QPINGONSUCCESS, to->q_flags) ||
373990792Sgshapiro			  bitset(QPINGONFAILURE, to->q_flags) ||
374090792Sgshapiro			  bitset(QPINGONDELAY, to->q_flags)) &&
374190792Sgshapiro			 bitset(QPRIMARY, to->q_flags))
374290792Sgshapiro		{
374390792Sgshapiro			/* RFC 2852, 4.1.4: no NOTIFY, or not NEVER */
374490792Sgshapiro			to->q_flags |= QBYTRACE;
374590792Sgshapiro			(void) sm_io_fprintf(e->e_xfp, SM_TIME_DEFAULT,
374690792Sgshapiro					     "%s... Deliver-By trace: relayed\n",
374790792Sgshapiro					     to->q_paddr);
374890792Sgshapiro		}
374938032Speter	}
375038032Speter
375138032Speter	if (bitnset(M_LMTP, m->m_flags))
375238032Speter	{
375338032Speter		/*
375438032Speter		**  Global information applies to the last recipient only;
375538032Speter		**  clear it out to avoid bogus errors.
375638032Speter		*/
375738032Speter
375838032Speter		rcode = EX_OK;
375938032Speter		e->e_statmsg = NULL;
376038032Speter
376138032Speter		/* reset the mci state for the next transaction */
376290792Sgshapiro		if (mci != NULL &&
376390792Sgshapiro		    (mci->mci_state == MCIS_MAIL ||
376490792Sgshapiro		     mci->mci_state == MCIS_RCPT ||
376590792Sgshapiro		     mci->mci_state == MCIS_DATA))
3766125820Sgshapiro		{
376738032Speter			mci->mci_state = MCIS_OPEN;
3768125820Sgshapiro			SmtpPhase = mci->mci_phase = "idle";
3769125820Sgshapiro			sm_setproctitle(true, e, "%s: %s", CurHostName,
3770125820Sgshapiro					mci->mci_phase);
3771125820Sgshapiro		}
377238032Speter	}
377338032Speter
377438032Speter	if (tobuf[0] != '\0')
377590792Sgshapiro	{
3776285229Sgshapiro		giveresponse(rcode, NULL, m, mci, ctladdr, xstart, e, NULL);
377790792Sgshapiro#if 0
377890792Sgshapiro		/*
377990792Sgshapiro		**  This code is disabled for now because I am not
378090792Sgshapiro		**  sure that copying status from the first recipient
378190792Sgshapiro		**  to all non-status'ed recipients is a good idea.
378290792Sgshapiro		*/
378390792Sgshapiro
378490792Sgshapiro		if (tochain->q_message != NULL &&
378590792Sgshapiro		    !bitnset(M_LMTP, m->m_flags) && rcode != EX_OK)
378690792Sgshapiro		{
378790792Sgshapiro			for (to = tochain->q_tchain; to != NULL;
378890792Sgshapiro			     to = to->q_tchain)
378990792Sgshapiro			{
379090792Sgshapiro				/* see if address already marked */
379190792Sgshapiro				if (QS_IS_QUEUEUP(to->q_state) &&
379290792Sgshapiro				    to->q_message == NULL)
379390792Sgshapiro					to->q_message = sm_rpool_strdup_x(e->e_rpool,
379490792Sgshapiro							tochain->q_message);
379590792Sgshapiro			}
379690792Sgshapiro		}
379790792Sgshapiro#endif /* 0 */
379890792Sgshapiro	}
379938032Speter	if (anyok)
380090792Sgshapiro		markstats(e, tochain, STATS_NORMAL);
380138032Speter	mci_store_persistent(mci);
380238032Speter
3803363466Sgshapiro#if _FFR_OCC
3804363466Sgshapiro	/*
3805363466Sgshapiro	**  HACK: this is NOT the right place to "close" a connection!
3806363466Sgshapiro	**  use smtpquit?
3807363466Sgshapiro	**  add a flag to mci to indicate that rate/conc. was increased?
3808363466Sgshapiro	*/
3809363466Sgshapiro
3810363466Sgshapiro	if (clever)
3811363466Sgshapiro	{
3812363466Sgshapiro		extern SOCKADDR CurHostAddr;
3813363466Sgshapiro
3814363466Sgshapiro		/* check family... {} */
3815363466Sgshapiro		/* r = anynet_pton(AF_INET, p, dst); */
3816363466Sgshapiro		occ_close(e, mci, host, &CurHostAddr);
3817363466Sgshapiro	}
3818363466Sgshapiro#endif /* _FFR_OCC */
3819363466Sgshapiro
382090792Sgshapiro	/* Some recipients were tempfailed, try them on the next host */
382190792Sgshapiro	if (mci != NULL && mci->mci_retryrcpt && nummxhosts > hostnum)
382290792Sgshapiro	{
382390792Sgshapiro		/* try next MX site */
382490792Sgshapiro		goto tryhost;
382590792Sgshapiro	}
382690792Sgshapiro
382738032Speter	/* now close the connection */
382838032Speter	if (clever && mci != NULL && mci->mci_state != MCIS_CLOSED &&
382938032Speter	    !bitset(MCIF_CACHED, mci->mci_flags))
383038032Speter		smtpquit(m, mci, e);
383138032Speter
383290792Sgshapirocleanup: ;
383390792Sgshapiro	}
383490792Sgshapiro	SM_FINALLY
383590792Sgshapiro	{
383690792Sgshapiro		/*
383790792Sgshapiro		**  Restore state and return.
383890792Sgshapiro		*/
383938032Speter#if XDEBUG
384038032Speter		char wbuf[MAXLINE];
384138032Speter
384238032Speter		/* make absolutely certain 0, 1, and 2 are in use */
3843168515Sgshapiro		(void) sm_snprintf(wbuf, sizeof(wbuf),
384490792Sgshapiro				   "%s... end of deliver(%s)",
384590792Sgshapiro				   e->e_to == NULL ? "NO-TO-LIST"
384690792Sgshapiro						   : shortenstring(e->e_to,
384790792Sgshapiro								   MAXSHORTSTR),
384890792Sgshapiro				  m->m_name);
384938032Speter		checkfd012(wbuf);
385064562Sgshapiro#endif /* XDEBUG */
385138032Speter
385290792Sgshapiro		errno = 0;
385390792Sgshapiro
385490792Sgshapiro		/*
385590792Sgshapiro		**  It was originally necessary to set macro 'g' to NULL
385690792Sgshapiro		**  because it previously pointed to an auto buffer.
385790792Sgshapiro		**  We don't do this any more, so this may be unnecessary.
385890792Sgshapiro		*/
385990792Sgshapiro
386090792Sgshapiro		macdefine(&e->e_macro, A_PERM, 'g', (char *) NULL);
386190792Sgshapiro		e->e_to = NULL;
386290792Sgshapiro	}
386390792Sgshapiro	SM_END_TRY
386464562Sgshapiro	return rcode;
386538032Speter}
386664562Sgshapiro
386790792Sgshapiro/*
386838032Speter**  MARKFAILURE -- mark a failure on a specific address.
386938032Speter**
387038032Speter**	Parameters:
387138032Speter**		e -- the envelope we are sending.
387238032Speter**		q -- the address to mark.
387338032Speter**		mci -- mailer connection information.
387438032Speter**		rcode -- the code signifying the particular failure.
387564562Sgshapiro**		ovr -- override an existing code?
387638032Speter**
387738032Speter**	Returns:
387838032Speter**		none.
387938032Speter**
388038032Speter**	Side Effects:
388138032Speter**		marks the address (and possibly the envelope) with the
388238032Speter**			failure so that an error will be returned or
388338032Speter**			the message will be queued, as appropriate.
388438032Speter*/
388538032Speter
388690792Sgshapirovoid
388764562Sgshapiromarkfailure(e, q, mci, rcode, ovr)
388838032Speter	register ENVELOPE *e;
388938032Speter	register ADDRESS *q;
389038032Speter	register MCI *mci;
389138032Speter	int rcode;
389264562Sgshapiro	bool ovr;
389338032Speter{
389490792Sgshapiro	int save_errno = errno;
389564562Sgshapiro	char *status = NULL;
389664562Sgshapiro	char *rstatus = NULL;
389738032Speter
389838032Speter	switch (rcode)
389938032Speter	{
390038032Speter	  case EX_OK:
390138032Speter		break;
390238032Speter
390338032Speter	  case EX_TEMPFAIL:
390438032Speter	  case EX_IOERR:
390538032Speter	  case EX_OSERR:
390664562Sgshapiro		q->q_state = QS_QUEUEUP;
390738032Speter		break;
390838032Speter
390938032Speter	  default:
391064562Sgshapiro		q->q_state = QS_BADADDR;
391138032Speter		break;
391238032Speter	}
391338032Speter
391438032Speter	/* find most specific error code possible */
391538032Speter	if (mci != NULL && mci->mci_status != NULL)
391638032Speter	{
391790792Sgshapiro		status = sm_rpool_strdup_x(e->e_rpool, mci->mci_status);
391838032Speter		if (mci->mci_rstatus != NULL)
391990792Sgshapiro			rstatus = sm_rpool_strdup_x(e->e_rpool,
392090792Sgshapiro						    mci->mci_rstatus);
392138032Speter	}
392238032Speter	else if (e->e_status != NULL)
392338032Speter	{
392464562Sgshapiro		status = e->e_status;
392538032Speter	}
392638032Speter	else
392738032Speter	{
392838032Speter		switch (rcode)
392938032Speter		{
393038032Speter		  case EX_USAGE:
393164562Sgshapiro			status = "5.5.4";
393238032Speter			break;
393338032Speter
393438032Speter		  case EX_DATAERR:
393564562Sgshapiro			status = "5.5.2";
393638032Speter			break;
393738032Speter
393838032Speter		  case EX_NOUSER:
393964562Sgshapiro			status = "5.1.1";
394038032Speter			break;
394138032Speter
394238032Speter		  case EX_NOHOST:
394364562Sgshapiro			status = "5.1.2";
394438032Speter			break;
394538032Speter
394638032Speter		  case EX_NOINPUT:
394738032Speter		  case EX_CANTCREAT:
394838032Speter		  case EX_NOPERM:
394964562Sgshapiro			status = "5.3.0";
395038032Speter			break;
395138032Speter
395238032Speter		  case EX_UNAVAILABLE:
395338032Speter		  case EX_SOFTWARE:
395438032Speter		  case EX_OSFILE:
395538032Speter		  case EX_PROTOCOL:
395638032Speter		  case EX_CONFIG:
395764562Sgshapiro			status = "5.5.0";
395838032Speter			break;
395938032Speter
396038032Speter		  case EX_OSERR:
396138032Speter		  case EX_IOERR:
396264562Sgshapiro			status = "4.5.0";
396338032Speter			break;
396438032Speter
396538032Speter		  case EX_TEMPFAIL:
396664562Sgshapiro			status = "4.2.0";
396738032Speter			break;
396838032Speter		}
396938032Speter	}
397038032Speter
397164562Sgshapiro	/* new status? */
397264562Sgshapiro	if (status != NULL && *status != '\0' && (ovr || q->q_status == NULL ||
397364562Sgshapiro	    *q->q_status == '\0' || *q->q_status < *status))
397464562Sgshapiro	{
397564562Sgshapiro		q->q_status = status;
397664562Sgshapiro		q->q_rstatus = rstatus;
397764562Sgshapiro	}
397838032Speter	if (rcode != EX_OK && q->q_rstatus == NULL &&
397938032Speter	    q->q_mailer != NULL && q->q_mailer->m_diagtype != NULL &&
398090792Sgshapiro	    sm_strcasecmp(q->q_mailer->m_diagtype, "X-UNIX") == 0)
398138032Speter	{
398264562Sgshapiro		char buf[16];
398338032Speter
3984168515Sgshapiro		(void) sm_snprintf(buf, sizeof(buf), "%d", rcode);
398590792Sgshapiro		q->q_rstatus = sm_rpool_strdup_x(e->e_rpool, buf);
398638032Speter	}
398764562Sgshapiro
398864562Sgshapiro	q->q_statdate = curtime();
398964562Sgshapiro	if (CurHostName != NULL && CurHostName[0] != '\0' &&
399064562Sgshapiro	    mci != NULL && !bitset(M_LOCALMAILER, mci->mci_flags))
399190792Sgshapiro		q->q_statmta = sm_rpool_strdup_x(e->e_rpool, CurHostName);
399290792Sgshapiro
399390792Sgshapiro	/* restore errno */
399490792Sgshapiro	errno = save_errno;
399538032Speter}
399690792Sgshapiro/*
399738032Speter**  ENDMAILER -- Wait for mailer to terminate.
399838032Speter**
399938032Speter**	We should never get fatal errors (e.g., segmentation
400038032Speter**	violation), so we report those specially.  For other
400138032Speter**	errors, we choose a status message (into statmsg),
400238032Speter**	and if it represents an error, we print it.
400338032Speter**
400438032Speter**	Parameters:
400580785Sgshapiro**		mci -- the mailer connection info.
400638032Speter**		e -- the current envelope.
400738032Speter**		pv -- the parameter vector that invoked the mailer
400838032Speter**			(for error messages).
400938032Speter**
401038032Speter**	Returns:
401138032Speter**		exit code of mailer.
401238032Speter**
401338032Speter**	Side Effects:
401438032Speter**		none.
401538032Speter*/
401638032Speter
401764562Sgshapirostatic jmp_buf	EndWaitTimeout;
401864562Sgshapiro
401964562Sgshapirostatic void
4020141858Sgshapiroendwaittimeout(ignore)
4021141858Sgshapiro	int ignore;
402264562Sgshapiro{
402377349Sgshapiro	/*
402477349Sgshapiro	**  NOTE: THIS CAN BE CALLED FROM A SIGNAL HANDLER.  DO NOT ADD
402577349Sgshapiro	**	ANYTHING TO THIS ROUTINE UNLESS YOU KNOW WHAT YOU ARE
402677349Sgshapiro	**	DOING.
402777349Sgshapiro	*/
402877349Sgshapiro
402964562Sgshapiro	errno = ETIMEDOUT;
403064562Sgshapiro	longjmp(EndWaitTimeout, 1);
403164562Sgshapiro}
403264562Sgshapiro
403338032Speterint
403438032Speterendmailer(mci, e, pv)
403538032Speter	register MCI *mci;
403638032Speter	register ENVELOPE *e;
403738032Speter	char **pv;
403838032Speter{
403938032Speter	int st;
404064562Sgshapiro	int save_errno = errno;
404164562Sgshapiro	char buf[MAXLINE];
404290792Sgshapiro	SM_EVENT *ev = NULL;
404338032Speter
404464562Sgshapiro
404538032Speter	mci_unlock_host(mci);
404638032Speter
404777349Sgshapiro	/* close output to mailer */
404877349Sgshapiro	if (mci->mci_out != NULL)
4049141858Sgshapiro	{
405090792Sgshapiro		(void) sm_io_close(mci->mci_out, SM_TIME_DEFAULT);
4051141858Sgshapiro		mci->mci_out = NULL;
4052141858Sgshapiro	}
405377349Sgshapiro
405477349Sgshapiro	/* copy any remaining input to transcript */
405577349Sgshapiro	if (mci->mci_in != NULL && mci->mci_state != MCIS_ERROR &&
405677349Sgshapiro	    e->e_xfp != NULL)
405777349Sgshapiro	{
4058168515Sgshapiro		while (sfgets(buf, sizeof(buf), mci->mci_in,
405990792Sgshapiro			      TimeOuts.to_quit, "Draining Input") != NULL)
406090792Sgshapiro			(void) sm_io_fputs(e->e_xfp, SM_TIME_DEFAULT, buf);
406177349Sgshapiro	}
406277349Sgshapiro
406364562Sgshapiro#if SASL
406490792Sgshapiro	/* close SASL connection */
406564562Sgshapiro	if (bitset(MCIF_AUTHACT, mci->mci_flags))
406664562Sgshapiro	{
406764562Sgshapiro		sasl_dispose(&mci->mci_conn);
406864562Sgshapiro		mci->mci_flags &= ~MCIF_AUTHACT;
406964562Sgshapiro	}
407064562Sgshapiro#endif /* SASL */
407164562Sgshapiro
407264562Sgshapiro#if STARTTLS
407364562Sgshapiro	/* shutdown TLS */
407464562Sgshapiro	(void) endtlsclt(mci);
4075363466Sgshapiro#endif
407664562Sgshapiro
407764562Sgshapiro	/* now close the input */
407838032Speter	if (mci->mci_in != NULL)
4079141858Sgshapiro	{
408090792Sgshapiro		(void) sm_io_close(mci->mci_in, SM_TIME_DEFAULT);
4081141858Sgshapiro		mci->mci_in = NULL;
4082141858Sgshapiro	}
408338032Speter	mci->mci_state = MCIS_CLOSED;
408438032Speter
408564562Sgshapiro	errno = save_errno;
408664562Sgshapiro
408738032Speter	/* in the IPC case there is nothing to wait for */
408838032Speter	if (mci->mci_pid == 0)
408964562Sgshapiro		return EX_OK;
409038032Speter
409164562Sgshapiro	/* put a timeout around the wait */
409264562Sgshapiro	if (mci->mci_mailer->m_wait > 0)
409364562Sgshapiro	{
409464562Sgshapiro		if (setjmp(EndWaitTimeout) == 0)
409590792Sgshapiro			ev = sm_setevent(mci->mci_mailer->m_wait,
409690792Sgshapiro					 endwaittimeout, 0);
409764562Sgshapiro		else
409864562Sgshapiro		{
409966494Sgshapiro			syserr("endmailer %s: wait timeout (%ld)",
410064562Sgshapiro			       mci->mci_mailer->m_name,
410166494Sgshapiro			       (long) mci->mci_mailer->m_wait);
410264562Sgshapiro			return EX_TEMPFAIL;
410364562Sgshapiro		}
410464562Sgshapiro	}
410538032Speter
410664562Sgshapiro	/* wait for the mailer process, collect status */
410738032Speter	st = waitfor(mci->mci_pid);
410864562Sgshapiro	save_errno = errno;
410964562Sgshapiro	if (ev != NULL)
411090792Sgshapiro		sm_clrevent(ev);
411164562Sgshapiro	errno = save_errno;
411264562Sgshapiro
411338032Speter	if (st == -1)
411438032Speter	{
411538032Speter		syserr("endmailer %s: wait", mci->mci_mailer->m_name);
411664562Sgshapiro		return EX_SOFTWARE;
411738032Speter	}
411838032Speter
411938032Speter	if (WIFEXITED(st))
412038032Speter	{
412138032Speter		/* normal death -- return status */
412238032Speter		return (WEXITSTATUS(st));
412338032Speter	}
412438032Speter
412538032Speter	/* it died a horrid death */
412664562Sgshapiro	syserr("451 4.3.0 mailer %s died with signal %d%s",
412764562Sgshapiro		mci->mci_mailer->m_name, WTERMSIG(st),
412864562Sgshapiro		WCOREDUMP(st) ? " (core dumped)" :
412964562Sgshapiro		(WIFSTOPPED(st) ? " (stopped)" : ""));
413038032Speter
413138032Speter	/* log the arguments */
413238032Speter	if (pv != NULL && e->e_xfp != NULL)
413338032Speter	{
413438032Speter		register char **av;
413538032Speter
413690792Sgshapiro		(void) sm_io_fprintf(e->e_xfp, SM_TIME_DEFAULT, "Arguments:");
413738032Speter		for (av = pv; *av != NULL; av++)
413890792Sgshapiro			(void) sm_io_fprintf(e->e_xfp, SM_TIME_DEFAULT, " %s",
413990792Sgshapiro					     *av);
414090792Sgshapiro		(void) sm_io_fprintf(e->e_xfp, SM_TIME_DEFAULT, "\n");
414138032Speter	}
414238032Speter
414338032Speter	ExitStat = EX_TEMPFAIL;
414464562Sgshapiro	return EX_TEMPFAIL;
414538032Speter}
414690792Sgshapiro/*
414738032Speter**  GIVERESPONSE -- Interpret an error response from a mailer
414838032Speter**
414938032Speter**	Parameters:
415064562Sgshapiro**		status -- the status code from the mailer (high byte
415138032Speter**			only; core dumps must have been taken care of
415238032Speter**			already).
415364562Sgshapiro**		dsn -- the DSN associated with the address, if any.
415438032Speter**		m -- the mailer info for this mailer.
415538032Speter**		mci -- the mailer connection info -- can be NULL if the
415638032Speter**			response is given before the connection is made.
415738032Speter**		ctladdr -- the controlling address for the recipient
415838032Speter**			address(es).
415938032Speter**		xstart -- the transaction start time, for computing
416038032Speter**			transaction delays.
416138032Speter**		e -- the current envelope.
416290792Sgshapiro**		to -- the current recipient (NULL if none).
416338032Speter**
416438032Speter**	Returns:
416538032Speter**		none.
416638032Speter**
416738032Speter**	Side Effects:
416838032Speter**		Errors may be incremented.
416938032Speter**		ExitStat may be set.
417038032Speter*/
417138032Speter
417238032Spetervoid
417390792Sgshapirogiveresponse(status, dsn, m, mci, ctladdr, xstart, e, to)
417464562Sgshapiro	int status;
417564562Sgshapiro	char *dsn;
417638032Speter	register MAILER *m;
417738032Speter	register MCI *mci;
417838032Speter	ADDRESS *ctladdr;
417938032Speter	time_t xstart;
418038032Speter	ENVELOPE *e;
418190792Sgshapiro	ADDRESS *to;
418238032Speter{
418338032Speter	register const char *statmsg;
418464562Sgshapiro	int errnum = errno;
418564562Sgshapiro	int off = 4;
418690792Sgshapiro	bool usestat = false;
418764562Sgshapiro	char dsnbuf[ENHSCLEN];
418838032Speter	char buf[MAXLINE];
418990792Sgshapiro	char *exmsg;
419038032Speter
419138032Speter	if (e == NULL)
4192159609Sgshapiro	{
419338032Speter		syserr("giveresponse: null envelope");
4194159609Sgshapiro		/* NOTREACHED */
4195159609Sgshapiro		SM_ASSERT(0);
4196159609Sgshapiro	}
419738032Speter
419838032Speter	/*
419938032Speter	**  Compute status message from code.
420038032Speter	*/
420138032Speter
420290792Sgshapiro	exmsg = sm_sysexmsg(status);
420364562Sgshapiro	if (status == 0)
420438032Speter	{
420564562Sgshapiro		statmsg = "250 2.0.0 Sent";
420638032Speter		if (e->e_statmsg != NULL)
420738032Speter		{
4208168515Sgshapiro			(void) sm_snprintf(buf, sizeof(buf), "%s (%s)",
420990792Sgshapiro					   statmsg,
421090792Sgshapiro					   shortenstring(e->e_statmsg, 403));
421138032Speter			statmsg = buf;
421238032Speter		}
421338032Speter	}
421490792Sgshapiro	else if (exmsg == NULL)
421538032Speter	{
4216168515Sgshapiro		(void) sm_snprintf(buf, sizeof(buf),
421790792Sgshapiro				   "554 5.3.0 unknown mailer error %d",
421890792Sgshapiro				   status);
421964562Sgshapiro		status = EX_UNAVAILABLE;
422038032Speter		statmsg = buf;
422190792Sgshapiro		usestat = true;
422238032Speter	}
422364562Sgshapiro	else if (status == EX_TEMPFAIL)
422438032Speter	{
422538032Speter		char *bp = buf;
422638032Speter
422790792Sgshapiro		(void) sm_strlcpy(bp, exmsg + 1, SPACELEFT(buf, bp));
422838032Speter		bp += strlen(bp);
422938032Speter#if NAMED_BIND
423038032Speter		if (h_errno == TRY_AGAIN)
423190792Sgshapiro			statmsg = sm_errstring(h_errno + E_DNSBASE);
423238032Speter		else
423364562Sgshapiro#endif /* NAMED_BIND */
423438032Speter		{
423564562Sgshapiro			if (errnum != 0)
423690792Sgshapiro				statmsg = sm_errstring(errnum);
423738032Speter			else
423838032Speter				statmsg = SmtpError;
423938032Speter		}
424038032Speter		if (statmsg != NULL && statmsg[0] != '\0')
424164562Sgshapiro		{
424264562Sgshapiro			switch (errnum)
424364562Sgshapiro			{
424464562Sgshapiro#ifdef ENETDOWN
424564562Sgshapiro			  case ENETDOWN:	/* Network is down */
4246363466Sgshapiro#endif
424764562Sgshapiro#ifdef ENETUNREACH
424864562Sgshapiro			  case ENETUNREACH:	/* Network is unreachable */
4249363466Sgshapiro#endif
425064562Sgshapiro#ifdef ENETRESET
425164562Sgshapiro			  case ENETRESET:	/* Network dropped connection on reset */
4252363466Sgshapiro#endif
425364562Sgshapiro#ifdef ECONNABORTED
425464562Sgshapiro			  case ECONNABORTED:	/* Software caused connection abort */
4255363466Sgshapiro#endif
425664562Sgshapiro#ifdef EHOSTDOWN
425764562Sgshapiro			  case EHOSTDOWN:	/* Host is down */
4258363466Sgshapiro#endif
425964562Sgshapiro#ifdef EHOSTUNREACH
426064562Sgshapiro			  case EHOSTUNREACH:	/* No route to host */
4261363466Sgshapiro#endif
4262141858Sgshapiro				if (mci != NULL && mci->mci_host != NULL)
426364562Sgshapiro				{
426490792Sgshapiro					(void) sm_strlcpyn(bp,
426590792Sgshapiro							   SPACELEFT(buf, bp),
426690792Sgshapiro							   2, ": ",
426790792Sgshapiro							   mci->mci_host);
426864562Sgshapiro					bp += strlen(bp);
426964562Sgshapiro				}
427064562Sgshapiro				break;
427164562Sgshapiro			}
427290792Sgshapiro			(void) sm_strlcpyn(bp, SPACELEFT(buf, bp), 2, ": ",
427390792Sgshapiro					   statmsg);
4274363466Sgshapiro#if DANE
4275363466Sgshapiro			if (errnum == 0 && SmtpError[0] != '\0' &&
4276363466Sgshapiro			    h_errno == TRY_AGAIN &&
4277363466Sgshapiro			    mci->mci_exitstat == EX_TEMPFAIL)
4278363466Sgshapiro			{
4279363466Sgshapiro				(void) sm_strlcat(bp, SmtpError,
4280363466Sgshapiro					SPACELEFT(buf, bp));
4281363466Sgshapiro				bp += strlen(bp);
4282363466Sgshapiro			}
4283363466Sgshapiro#endif /* DANE */
428490792Sgshapiro			usestat = true;
428564562Sgshapiro		}
428638032Speter		statmsg = buf;
428738032Speter	}
428838032Speter#if NAMED_BIND
428964562Sgshapiro	else if (status == EX_NOHOST && h_errno != 0)
429038032Speter	{
429190792Sgshapiro		statmsg = sm_errstring(h_errno + E_DNSBASE);
4292168515Sgshapiro		(void) sm_snprintf(buf, sizeof(buf), "%s (%s)", exmsg + 1,
429390792Sgshapiro				   statmsg);
429438032Speter		statmsg = buf;
429590792Sgshapiro		usestat = true;
429638032Speter	}
429764562Sgshapiro#endif /* NAMED_BIND */
429838032Speter	else
429938032Speter	{
430090792Sgshapiro		statmsg = exmsg;
430164562Sgshapiro		if (*statmsg++ == ':' && errnum != 0)
430238032Speter		{
4303168515Sgshapiro			(void) sm_snprintf(buf, sizeof(buf), "%s: %s", statmsg,
430490792Sgshapiro					   sm_errstring(errnum));
430538032Speter			statmsg = buf;
430690792Sgshapiro			usestat = true;
430738032Speter		}
430894334Sgshapiro		else if (bitnset(M_LMTP, m->m_flags) && e->e_statmsg != NULL)
430994334Sgshapiro		{
4310168515Sgshapiro			(void) sm_snprintf(buf, sizeof(buf), "%s (%s)", statmsg,
431194334Sgshapiro					   shortenstring(e->e_statmsg, 403));
431294334Sgshapiro			statmsg = buf;
431394334Sgshapiro			usestat = true;
431494334Sgshapiro		}
431538032Speter	}
431638032Speter
431738032Speter	/*
431838032Speter	**  Print the message as appropriate
431938032Speter	*/
432038032Speter
432164562Sgshapiro	if (status == EX_OK || status == EX_TEMPFAIL)
432238032Speter	{
432338032Speter		extern char MsgBuf[];
432438032Speter
432564562Sgshapiro		if ((off = isenhsc(statmsg + 4, ' ')) > 0)
432664562Sgshapiro		{
432764562Sgshapiro			if (dsn == NULL)
432864562Sgshapiro			{
4329168515Sgshapiro				(void) sm_snprintf(dsnbuf, sizeof(dsnbuf),
433090792Sgshapiro						   "%.*s", off, statmsg + 4);
433164562Sgshapiro				dsn = dsnbuf;
433264562Sgshapiro			}
433364562Sgshapiro			off += 5;
433464562Sgshapiro		}
433564562Sgshapiro		else
433664562Sgshapiro		{
433764562Sgshapiro			off = 4;
433864562Sgshapiro		}
433964562Sgshapiro		message("%s", statmsg + off);
434064562Sgshapiro		if (status == EX_TEMPFAIL && e->e_xfp != NULL)
434190792Sgshapiro			(void) sm_io_fprintf(e->e_xfp, SM_TIME_DEFAULT, "%s\n",
434290792Sgshapiro					     &MsgBuf[4]);
434338032Speter	}
434438032Speter	else
434538032Speter	{
434664562Sgshapiro		char mbuf[ENHSCLEN + 4];
434738032Speter
434838032Speter		Errors++;
434964562Sgshapiro		if ((off = isenhsc(statmsg + 4, ' ')) > 0 &&
4350168515Sgshapiro		    off < sizeof(mbuf) - 4)
435164562Sgshapiro		{
435264562Sgshapiro			if (dsn == NULL)
435364562Sgshapiro			{
4354168515Sgshapiro				(void) sm_snprintf(dsnbuf, sizeof(dsnbuf),
435590792Sgshapiro						   "%.*s", off, statmsg + 4);
435664562Sgshapiro				dsn = dsnbuf;
435764562Sgshapiro			}
435864562Sgshapiro			off += 5;
435990792Sgshapiro
436090792Sgshapiro			/* copy only part of statmsg to mbuf */
436190792Sgshapiro			(void) sm_strlcpy(mbuf, statmsg, off);
4362168515Sgshapiro			(void) sm_strlcat(mbuf, " %s", sizeof(mbuf));
436364562Sgshapiro		}
436464562Sgshapiro		else
436564562Sgshapiro		{
436664562Sgshapiro			dsnbuf[0] = '\0';
4367168515Sgshapiro			(void) sm_snprintf(mbuf, sizeof(mbuf), "%.3s %%s",
436890792Sgshapiro					   statmsg);
436964562Sgshapiro			off = 4;
437064562Sgshapiro		}
437164562Sgshapiro		usrerr(mbuf, &statmsg[off]);
437238032Speter	}
437338032Speter
437438032Speter	/*
437538032Speter	**  Final cleanup.
4376285229Sgshapiro	**	Log a record of the transaction.  Compute the new ExitStat
4377285229Sgshapiro	**	-- if we already had an error, stick with that.
437838032Speter	*/
437938032Speter
438038032Speter	if (OpMode != MD_VERIFY && !bitset(EF_VRFYONLY, e->e_flags) &&
438164562Sgshapiro	    LogLevel > ((status == EX_TEMPFAIL) ? 8 : (status == EX_OK) ? 7 : 6))
4382285229Sgshapiro		logdelivery(m, mci, dsn, statmsg + off, ctladdr, xstart, e, to, status);
438338032Speter
438438032Speter	if (tTd(11, 2))
438590792Sgshapiro		sm_dprintf("giveresponse: status=%d, dsn=%s, e->e_message=%s, errnum=%d\n",
438690792Sgshapiro			   status,
438790792Sgshapiro			   dsn == NULL ? "<NULL>" : dsn,
438890792Sgshapiro			   e->e_message == NULL ? "<NULL>" : e->e_message,
438990792Sgshapiro			   errnum);
439038032Speter
439164562Sgshapiro	if (status != EX_TEMPFAIL)
439264562Sgshapiro		setstat(status);
439364562Sgshapiro	if (status != EX_OK && (status != EX_TEMPFAIL || e->e_message == NULL))
439490792Sgshapiro		e->e_message = sm_rpool_strdup_x(e->e_rpool, statmsg + off);
439590792Sgshapiro	if (status != EX_OK && to != NULL && to->q_message == NULL)
439638032Speter	{
439790792Sgshapiro		if (!usestat && e->e_message != NULL)
439890792Sgshapiro			to->q_message = sm_rpool_strdup_x(e->e_rpool,
439990792Sgshapiro							  e->e_message);
440090792Sgshapiro		else
440190792Sgshapiro			to->q_message = sm_rpool_strdup_x(e->e_rpool,
440290792Sgshapiro							  statmsg + off);
440338032Speter	}
440438032Speter	errno = 0;
440573188Sgshapiro	SM_SET_H_ERRNO(0);
440638032Speter}
440790792Sgshapiro/*
440838032Speter**  LOGDELIVERY -- log the delivery in the system log
440938032Speter**
441038032Speter**	Care is taken to avoid logging lines that are too long, because
441138032Speter**	some versions of syslog have an unfortunate proclivity for core
441238032Speter**	dumping.  This is a hack, to be sure, that is at best empirical.
441338032Speter**
441438032Speter**	Parameters:
441538032Speter**		m -- the mailer info.  Can be NULL for initial queue.
441638032Speter**		mci -- the mailer connection info -- can be NULL if the
441764562Sgshapiro**			log is occurring when no connection is active.
441864562Sgshapiro**		dsn -- the DSN attached to the status.
441964562Sgshapiro**		status -- the message to print for the status.
442038032Speter**		ctladdr -- the controlling address for the to list.
442138032Speter**		xstart -- the transaction start time, used for
442238032Speter**			computing transaction delay.
442338032Speter**		e -- the current envelope.
4424285229Sgshapiro**		to -- the current recipient (NULL if none).
4425285229Sgshapiro**		rcode -- status code
442638032Speter**
442738032Speter**	Returns:
442838032Speter**		none
442938032Speter**
443038032Speter**	Side Effects:
443138032Speter**		none
443238032Speter*/
443338032Speter
443438032Spetervoid
4435285229Sgshapirologdelivery(m, mci, dsn, status, ctladdr, xstart, e, to, rcode)
443638032Speter	MAILER *m;
443738032Speter	register MCI *mci;
443864562Sgshapiro	char *dsn;
443964562Sgshapiro	const char *status;
444038032Speter	ADDRESS *ctladdr;
444138032Speter	time_t xstart;
444238032Speter	register ENVELOPE *e;
4443285229Sgshapiro	ADDRESS *to;
4444285229Sgshapiro	int rcode;
444538032Speter{
444638032Speter	register char *bp;
444738032Speter	register char *p;
444838032Speter	int l;
444990792Sgshapiro	time_t now = curtime();
445038032Speter	char buf[1024];
445138032Speter
445264562Sgshapiro#if (SYSLOG_BUFSIZE) >= 256
445338032Speter	/* ctladdr: max 106 bytes */
445438032Speter	bp = buf;
445538032Speter	if (ctladdr != NULL)
445638032Speter	{
445790792Sgshapiro		(void) sm_strlcpyn(bp, SPACELEFT(buf, bp), 2, ", ctladdr=",
445890792Sgshapiro				   shortenstring(ctladdr->q_paddr, 83));
445938032Speter		bp += strlen(bp);
446038032Speter		if (bitset(QGOODUID, ctladdr->q_flags))
446138032Speter		{
446290792Sgshapiro			(void) sm_snprintf(bp, SPACELEFT(buf, bp), " (%d/%d)",
446390792Sgshapiro					   (int) ctladdr->q_uid,
446490792Sgshapiro					   (int) ctladdr->q_gid);
446538032Speter			bp += strlen(bp);
446638032Speter		}
446738032Speter	}
446838032Speter
446938032Speter	/* delay & xdelay: max 41 bytes */
447090792Sgshapiro	(void) sm_strlcpyn(bp, SPACELEFT(buf, bp), 2, ", delay=",
447190792Sgshapiro			   pintvl(now - e->e_ctime, true));
447238032Speter	bp += strlen(bp);
447338032Speter
447438032Speter	if (xstart != (time_t) 0)
447538032Speter	{
447690792Sgshapiro		(void) sm_strlcpyn(bp, SPACELEFT(buf, bp), 2, ", xdelay=",
447790792Sgshapiro				   pintvl(now - xstart, true));
447838032Speter		bp += strlen(bp);
447938032Speter	}
448038032Speter
448138032Speter	/* mailer: assume about 19 bytes (max 10 byte mailer name) */
448238032Speter	if (m != NULL)
448338032Speter	{
448490792Sgshapiro		(void) sm_strlcpyn(bp, SPACELEFT(buf, bp), 2, ", mailer=",
448590792Sgshapiro				   m->m_name);
448638032Speter		bp += strlen(bp);
448738032Speter	}
448838032Speter
4489285229Sgshapiro# if _FFR_LOG_MORE2
4490363466Sgshapiro	LOG_MORE(buf, bp);
4491363466Sgshapiro# endif
4492285229Sgshapiro
449364562Sgshapiro	/* pri: changes with each delivery attempt */
449490792Sgshapiro	(void) sm_snprintf(bp, SPACELEFT(buf, bp), ", pri=%ld",
4495244833Sgshapiro		PRT_NONNEGL(e->e_msgpriority));
449664562Sgshapiro	bp += strlen(bp);
449764562Sgshapiro
449838032Speter	/* relay: max 66 bytes for IPv4 addresses */
449938032Speter	if (mci != NULL && mci->mci_host != NULL)
450038032Speter	{
450138032Speter		extern SOCKADDR CurHostAddr;
450238032Speter
450390792Sgshapiro		(void) sm_strlcpyn(bp, SPACELEFT(buf, bp), 2, ", relay=",
450490792Sgshapiro				   shortenstring(mci->mci_host, 40));
450538032Speter		bp += strlen(bp);
450638032Speter
450738032Speter		if (CurHostAddr.sa.sa_family != 0)
450838032Speter		{
450990792Sgshapiro			(void) sm_snprintf(bp, SPACELEFT(buf, bp), " [%s]",
451090792Sgshapiro					   anynet_ntoa(&CurHostAddr));
451138032Speter		}
451238032Speter	}
451390792Sgshapiro	else if (strcmp(status, "quarantined") == 0)
451490792Sgshapiro	{
451590792Sgshapiro		if (e->e_quarmsg != NULL)
451690792Sgshapiro			(void) sm_snprintf(bp, SPACELEFT(buf, bp),
451790792Sgshapiro					   ", quarantine=%s",
451890792Sgshapiro					   shortenstring(e->e_quarmsg, 40));
451990792Sgshapiro	}
452064562Sgshapiro	else if (strcmp(status, "queued") != 0)
452138032Speter	{
452238032Speter		p = macvalue('h', e);
452338032Speter		if (p != NULL && p[0] != '\0')
452438032Speter		{
452590792Sgshapiro			(void) sm_snprintf(bp, SPACELEFT(buf, bp),
452690792Sgshapiro					   ", relay=%s", shortenstring(p, 40));
452738032Speter		}
452838032Speter	}
452938032Speter	bp += strlen(bp);
453038032Speter
453164562Sgshapiro	/* dsn */
453264562Sgshapiro	if (dsn != NULL && *dsn != '\0')
453364562Sgshapiro	{
453490792Sgshapiro		(void) sm_strlcpyn(bp, SPACELEFT(buf, bp), 2, ", dsn=",
453590792Sgshapiro				   shortenstring(dsn, ENHSCLEN));
453664562Sgshapiro		bp += strlen(bp);
453764562Sgshapiro	}
453838032Speter
4539363466Sgshapiro# if _FFR_LOG_NTRIES
4540147078Sgshapiro	/* ntries */
4541147078Sgshapiro	if (e->e_ntries >= 0)
4542147078Sgshapiro	{
4543147078Sgshapiro		(void) sm_snprintf(bp, SPACELEFT(buf, bp),
4544147078Sgshapiro				   ", ntries=%d", e->e_ntries + 1);
4545147078Sgshapiro		bp += strlen(bp);
4546147078Sgshapiro	}
4547363466Sgshapiro# endif /* _FFR_LOG_NTRIES */
4548147078Sgshapiro
454964562Sgshapiro# define STATLEN		(((SYSLOG_BUFSIZE) - 100) / 4)
455064562Sgshapiro# if (STATLEN) < 63
455164562Sgshapiro#  undef STATLEN
455264562Sgshapiro#  define STATLEN	63
455364562Sgshapiro# endif /* (STATLEN) < 63 */
455464562Sgshapiro# if (STATLEN) > 203
455564562Sgshapiro#  undef STATLEN
455664562Sgshapiro#  define STATLEN	203
455764562Sgshapiro# endif /* (STATLEN) > 203 */
455864562Sgshapiro
4559285229Sgshapiro	/*
4560285229Sgshapiro	**  Notes:
4561285229Sgshapiro	**  per-rcpt status: to->q_rstatus
4562285229Sgshapiro	**  global status: e->e_text
4563285229Sgshapiro	**
4564285229Sgshapiro	**  We (re)use STATLEN here, is that a good choice?
4565285229Sgshapiro	**
4566285229Sgshapiro	**  stat=Deferred: ...
4567285229Sgshapiro	**  has sometimes the same text?
4568285229Sgshapiro	**
4569285229Sgshapiro	**  Note: this doesn't show the stage at which the error happened.
4570285229Sgshapiro	**  can/should we log that?
4571285229Sgshapiro	**  XS_* in reply() basically encodes the state.
4572363466Sgshapiro	**
4573363466Sgshapiro	**  Note: in some case the normal logging might show the same server
4574363466Sgshapiro	**  reply - how to avoid that?
4575285229Sgshapiro	*/
4576285229Sgshapiro
4577285229Sgshapiro	/* only show errors */
4578285229Sgshapiro	if (rcode != EX_OK && to != NULL && to->q_rstatus != NULL &&
4579285229Sgshapiro	    *to->q_rstatus != '\0')
4580285229Sgshapiro	{
4581285229Sgshapiro		(void) sm_snprintf(bp, SPACELEFT(buf, bp),
4582285229Sgshapiro			", reply=%s",
4583285229Sgshapiro			shortenstring(to->q_rstatus, STATLEN));
4584285229Sgshapiro		bp += strlen(bp);
4585285229Sgshapiro	}
4586285229Sgshapiro	else if (rcode != EX_OK && e->e_text != NULL)
4587285229Sgshapiro	{
4588285229Sgshapiro		(void) sm_snprintf(bp, SPACELEFT(buf, bp),
4589285229Sgshapiro			", reply=%d %s%s%s",
4590285229Sgshapiro			e->e_rcode,
4591285229Sgshapiro			e->e_renhsc,
4592285229Sgshapiro			(e->e_renhsc[0] != '\0') ? " " : "",
4593285229Sgshapiro			shortenstring(e->e_text, STATLEN));
4594285229Sgshapiro		bp += strlen(bp);
4595285229Sgshapiro	}
4596285229Sgshapiro
459738032Speter	/* stat: max 210 bytes */
4598168515Sgshapiro	if ((bp - buf) > (sizeof(buf) - ((STATLEN) + 20)))
459938032Speter	{
460038032Speter		/* desperation move -- truncate data */
4601168515Sgshapiro		bp = buf + sizeof(buf) - ((STATLEN) + 17);
460290792Sgshapiro		(void) sm_strlcpy(bp, "...", SPACELEFT(buf, bp));
460338032Speter		bp += 3;
460438032Speter	}
460538032Speter
460690792Sgshapiro	(void) sm_strlcpy(bp, ", stat=", SPACELEFT(buf, bp));
460738032Speter	bp += strlen(bp);
460838032Speter
460990792Sgshapiro	(void) sm_strlcpy(bp, shortenstring(status, STATLEN),
461090792Sgshapiro			  SPACELEFT(buf, bp));
461138032Speter
461238032Speter	/* id, to: max 13 + TOBUFSIZE bytes */
461338032Speter	l = SYSLOG_BUFSIZE - 100 - strlen(buf);
461490792Sgshapiro	if (l < 0)
461590792Sgshapiro		l = 0;
461664562Sgshapiro	p = e->e_to == NULL ? "NO-TO-LIST" : e->e_to;
461790792Sgshapiro	while (strlen(p) >= l)
461838032Speter	{
461964562Sgshapiro		register char *q;
462038032Speter
462164562Sgshapiro		for (q = p + l; q > p; q--)
462264562Sgshapiro		{
4623285229Sgshapiro			/* XXX a comma in an address will break this! */
462464562Sgshapiro			if (*q == ',')
462564562Sgshapiro				break;
462664562Sgshapiro		}
462764562Sgshapiro		if (p == q)
462864562Sgshapiro			break;
462990792Sgshapiro		sm_syslog(LOG_INFO, e->e_id, "to=%.*s [more]%s",
463066494Sgshapiro			  (int) (++q - p), p, buf);
463138032Speter		p = q;
463238032Speter	}
463364562Sgshapiro	sm_syslog(LOG_INFO, e->e_id, "to=%.*s%s", l, p, buf);
463438032Speter
463564562Sgshapiro#else /* (SYSLOG_BUFSIZE) >= 256 */
463638032Speter
463738032Speter	l = SYSLOG_BUFSIZE - 85;
463890792Sgshapiro	if (l < 0)
463990792Sgshapiro		l = 0;
464064562Sgshapiro	p = e->e_to == NULL ? "NO-TO-LIST" : e->e_to;
464190792Sgshapiro	while (strlen(p) >= l)
464238032Speter	{
464364562Sgshapiro		register char *q;
464438032Speter
464564562Sgshapiro		for (q = p + l; q > p; q--)
464664562Sgshapiro		{
464764562Sgshapiro			if (*q == ',')
464864562Sgshapiro				break;
464964562Sgshapiro		}
465064562Sgshapiro		if (p == q)
465164562Sgshapiro			break;
465264562Sgshapiro
465390792Sgshapiro		sm_syslog(LOG_INFO, e->e_id, "to=%.*s [more]",
465466494Sgshapiro			  (int) (++q - p), p);
465538032Speter		p = q;
465638032Speter	}
465764562Sgshapiro	sm_syslog(LOG_INFO, e->e_id, "to=%.*s", l, p);
465838032Speter
465938032Speter	if (ctladdr != NULL)
466038032Speter	{
466138032Speter		bp = buf;
466290792Sgshapiro		(void) sm_strlcpyn(bp, SPACELEFT(buf, bp), 2, "ctladdr=",
466390792Sgshapiro				   shortenstring(ctladdr->q_paddr, 83));
466438032Speter		bp += strlen(bp);
466538032Speter		if (bitset(QGOODUID, ctladdr->q_flags))
466638032Speter		{
466790792Sgshapiro			(void) sm_snprintf(bp, SPACELEFT(buf, bp), " (%d/%d)",
466890792Sgshapiro					   ctladdr->q_uid, ctladdr->q_gid);
466938032Speter			bp += strlen(bp);
467038032Speter		}
467138032Speter		sm_syslog(LOG_INFO, e->e_id, "%s", buf);
467238032Speter	}
467338032Speter	bp = buf;
467490792Sgshapiro	(void) sm_strlcpyn(bp, SPACELEFT(buf, bp), 2, "delay=",
467590792Sgshapiro			   pintvl(now - e->e_ctime, true));
467638032Speter	bp += strlen(bp);
467738032Speter	if (xstart != (time_t) 0)
467838032Speter	{
467990792Sgshapiro		(void) sm_strlcpyn(bp, SPACELEFT(buf, bp), 2, ", xdelay=",
468090792Sgshapiro				   pintvl(now - xstart, true));
468138032Speter		bp += strlen(bp);
468238032Speter	}
468338032Speter
468438032Speter	if (m != NULL)
468538032Speter	{
468690792Sgshapiro		(void) sm_strlcpyn(bp, SPACELEFT(buf, bp), 2, ", mailer=",
468790792Sgshapiro				   m->m_name);
468838032Speter		bp += strlen(bp);
468938032Speter	}
469038032Speter	sm_syslog(LOG_INFO, e->e_id, "%.1000s", buf);
469138032Speter
469238032Speter	buf[0] = '\0';
469338032Speter	bp = buf;
469438032Speter	if (mci != NULL && mci->mci_host != NULL)
469538032Speter	{
469638032Speter		extern SOCKADDR CurHostAddr;
469738032Speter
469890792Sgshapiro		(void) sm_snprintf(bp, SPACELEFT(buf, bp), "relay=%.100s",
469990792Sgshapiro				   mci->mci_host);
470038032Speter		bp += strlen(bp);
470138032Speter
470238032Speter		if (CurHostAddr.sa.sa_family != 0)
470390792Sgshapiro			(void) sm_snprintf(bp, SPACELEFT(buf, bp),
470490792Sgshapiro					   " [%.100s]",
470590792Sgshapiro					   anynet_ntoa(&CurHostAddr));
470638032Speter	}
470790792Sgshapiro	else if (strcmp(status, "quarantined") == 0)
470890792Sgshapiro	{
470990792Sgshapiro		if (e->e_quarmsg != NULL)
471090792Sgshapiro			(void) sm_snprintf(bp, SPACELEFT(buf, bp),
471190792Sgshapiro					   ", quarantine=%.100s",
471290792Sgshapiro					   e->e_quarmsg);
471390792Sgshapiro	}
471464562Sgshapiro	else if (strcmp(status, "queued") != 0)
471538032Speter	{
471638032Speter		p = macvalue('h', e);
471738032Speter		if (p != NULL && p[0] != '\0')
4718168515Sgshapiro			(void) sm_snprintf(buf, sizeof(buf), "relay=%.100s", p);
471938032Speter	}
472038032Speter	if (buf[0] != '\0')
472138032Speter		sm_syslog(LOG_INFO, e->e_id, "%.1000s", buf);
472238032Speter
472364562Sgshapiro	sm_syslog(LOG_INFO, e->e_id, "stat=%s", shortenstring(status, 63));
472464562Sgshapiro#endif /* (SYSLOG_BUFSIZE) >= 256 */
472538032Speter}
472690792Sgshapiro/*
472738032Speter**  PUTFROMLINE -- output a UNIX-style from line (or whatever)
472838032Speter**
472938032Speter**	This can be made an arbitrary message separator by changing $l
473038032Speter**
473138032Speter**	One of the ugliest hacks seen by human eyes is contained herein:
473238032Speter**	UUCP wants those stupid "remote from <host>" lines.  Why oh why
473338032Speter**	does a well-meaning programmer such as myself have to deal with
473438032Speter**	this kind of antique garbage????
473538032Speter**
473638032Speter**	Parameters:
473738032Speter**		mci -- the connection information.
473838032Speter**		e -- the envelope.
473938032Speter**
474038032Speter**	Returns:
4741157001Sgshapiro**		true iff line was written successfully
474238032Speter**
474338032Speter**	Side Effects:
474438032Speter**		outputs some text to fp.
474538032Speter*/
474638032Speter
4747157001Sgshapirobool
474838032Speterputfromline(mci, e)
474938032Speter	register MCI *mci;
475038032Speter	ENVELOPE *e;
475138032Speter{
475238032Speter	char *template = UnixFromLine;
475338032Speter	char buf[MAXLINE];
475438032Speter	char xbuf[MAXLINE];
475538032Speter
475638032Speter	if (bitnset(M_NHDR, mci->mci_mailer->m_flags))
4757157001Sgshapiro		return true;
475838032Speter
475938032Speter	mci->mci_flags |= MCIF_INHEADER;
476038032Speter
476138032Speter	if (bitnset(M_UGLYUUCP, mci->mci_mailer->m_flags))
476238032Speter	{
476338032Speter		char *bang;
476438032Speter
4765168515Sgshapiro		expand("\201g", buf, sizeof(buf), e);
476638032Speter		bang = strchr(buf, '!');
476738032Speter		if (bang == NULL)
476838032Speter		{
476938032Speter			char *at;
477038032Speter			char hname[MAXNAME];
477138032Speter
477264562Sgshapiro			/*
477342575Speter			**  If we can construct a UUCP path, do so
477438032Speter			*/
477538032Speter
477638032Speter			at = strrchr(buf, '@');
477738032Speter			if (at == NULL)
477838032Speter			{
4779168515Sgshapiro				expand("\201k", hname, sizeof(hname), e);
478038032Speter				at = hname;
478138032Speter			}
478238032Speter			else
478338032Speter				*at++ = '\0';
4784168515Sgshapiro			(void) sm_snprintf(xbuf, sizeof(xbuf),
478590792Sgshapiro					   "From %.800s  \201d remote from %.100s\n",
478690792Sgshapiro					   buf, at);
478738032Speter		}
478838032Speter		else
478938032Speter		{
479038032Speter			*bang++ = '\0';
4791168515Sgshapiro			(void) sm_snprintf(xbuf, sizeof(xbuf),
479290792Sgshapiro					   "From %.800s  \201d remote from %.100s\n",
479390792Sgshapiro					   bang, buf);
479438032Speter			template = xbuf;
479538032Speter		}
479638032Speter	}
4797168515Sgshapiro	expand(template, buf, sizeof(buf), e);
4798157001Sgshapiro	return putxline(buf, strlen(buf), mci, PXLF_HEADER);
479938032Speter}
4800157001Sgshapiro
480190792Sgshapiro/*
480238032Speter**  PUTBODY -- put the body of a message.
480338032Speter**
480438032Speter**	Parameters:
480538032Speter**		mci -- the connection information.
480638032Speter**		e -- the envelope to put out.
480738032Speter**		separator -- if non-NULL, a message separator that must
480838032Speter**			not be permitted in the resulting message.
480938032Speter**
481038032Speter**	Returns:
4811157001Sgshapiro**		true iff message was written successfully
481238032Speter**
481338032Speter**	Side Effects:
481438032Speter**		The message is written onto fp.
481538032Speter*/
481638032Speter
481738032Speter/* values for output state variable */
4818157001Sgshapiro#define OSTATE_HEAD	0	/* at beginning of line */
4819157001Sgshapiro#define OSTATE_CR	1	/* read a carriage return */
4820157001Sgshapiro#define OSTATE_INLINE	2	/* putting rest of line */
482138032Speter
4822157001Sgshapirobool
482338032Speterputbody(mci, e, separator)
482438032Speter	register MCI *mci;
482538032Speter	register ENVELOPE *e;
482638032Speter	char *separator;
482738032Speter{
482890792Sgshapiro	bool dead = false;
4829157001Sgshapiro	bool ioerr = false;
4830157001Sgshapiro	int save_errno;
483138032Speter	char buf[MAXLINE];
483290792Sgshapiro#if MIME8TO7
483342575Speter	char *boundaries[MAXMIMENESTING + 1];
4834363466Sgshapiro#endif
483538032Speter
483638032Speter	/*
483738032Speter	**  Output the body of the message
483838032Speter	*/
483938032Speter
484038032Speter	if (e->e_dfp == NULL && bitset(EF_HAS_DF, e->e_flags))
484138032Speter	{
484290792Sgshapiro		char *df = queuename(e, DATAFL_LETTER);
484338032Speter
484490792Sgshapiro		e->e_dfp = sm_io_open(SmFtStdio, SM_TIME_DEFAULT, df,
4845120256Sgshapiro				      SM_IO_RDONLY_B, NULL);
484638032Speter		if (e->e_dfp == NULL)
484764562Sgshapiro		{
484864562Sgshapiro			char *msg = "!putbody: Cannot open %s for %s from %s";
484964562Sgshapiro
485064562Sgshapiro			if (errno == ENOENT)
485164562Sgshapiro				msg++;
485264562Sgshapiro			syserr(msg, df, e->e_to, e->e_from.q_paddr);
485364562Sgshapiro		}
485490792Sgshapiro
485538032Speter	}
485638032Speter	if (e->e_dfp == NULL)
485738032Speter	{
485838032Speter		if (bitset(MCIF_INHEADER, mci->mci_flags))
485938032Speter		{
4860157001Sgshapiro			if (!putline("", mci))
4861157001Sgshapiro				goto writeerr;
486238032Speter			mci->mci_flags &= ~MCIF_INHEADER;
486338032Speter		}
4864157001Sgshapiro		if (!putline("<<< No Message Collected >>>", mci))
4865157001Sgshapiro			goto writeerr;
486638032Speter		goto endofmessage;
486738032Speter	}
486864562Sgshapiro
486938032Speter	if (e->e_dfino == (ino_t) 0)
487038032Speter	{
487138032Speter		struct stat stbuf;
487238032Speter
487390792Sgshapiro		if (fstat(sm_io_getinfo(e->e_dfp, SM_IO_WHAT_FD, NULL), &stbuf)
487490792Sgshapiro		    < 0)
487538032Speter			e->e_dfino = -1;
487638032Speter		else
487738032Speter		{
487838032Speter			e->e_dfdev = stbuf.st_dev;
487938032Speter			e->e_dfino = stbuf.st_ino;
488038032Speter		}
488138032Speter	}
488238032Speter
488390792Sgshapiro	/* paranoia: the data file should always be in a rewound state */
488464562Sgshapiro	(void) bfrewind(e->e_dfp);
488564562Sgshapiro
4886157001Sgshapiro	/* simulate an I/O timeout when used as source */
4887157001Sgshapiro	if (tTd(84, 101))
4888157001Sgshapiro		sleep(319);
4889157001Sgshapiro
489038032Speter#if MIME8TO7
489138032Speter	if (bitset(MCIF_CVT8TO7, mci->mci_flags))
489238032Speter	{
489338032Speter		/*
489438032Speter		**  Do 8 to 7 bit MIME conversion.
489538032Speter		*/
489638032Speter
489738032Speter		/* make sure it looks like a MIME message */
4898157001Sgshapiro		if (hvalue("MIME-Version", e->e_header) == NULL &&
4899157001Sgshapiro		    !putline("MIME-Version: 1.0", mci))
4900157001Sgshapiro			goto writeerr;
490138032Speter
490238032Speter		if (hvalue("Content-Type", e->e_header) == NULL)
490338032Speter		{
4904168515Sgshapiro			(void) sm_snprintf(buf, sizeof(buf),
490590792Sgshapiro					   "Content-Type: text/plain; charset=%s",
490690792Sgshapiro					   defcharset(e));
4907157001Sgshapiro			if (!putline(buf, mci))
4908157001Sgshapiro				goto writeerr;
490938032Speter		}
491038032Speter
491138032Speter		/* now do the hard work */
491238032Speter		boundaries[0] = NULL;
491338032Speter		mci->mci_flags |= MCIF_INHEADER;
4914159609Sgshapiro		if (mime8to7(mci, e->e_header, e, boundaries, M87F_OUTER, 0) ==
4915157001Sgshapiro								SM_IO_EOF)
4916157001Sgshapiro			goto writeerr;
491738032Speter	}
491838032Speter# if MIME7TO8
491938032Speter	else if (bitset(MCIF_CVT7TO8, mci->mci_flags))
492038032Speter	{
4921157001Sgshapiro		if (!mime7to8(mci, e->e_header, e))
4922157001Sgshapiro			goto writeerr;
492338032Speter	}
492464562Sgshapiro# endif /* MIME7TO8 */
492542575Speter	else if (MaxMimeHeaderLength > 0 || MaxMimeFieldLength > 0)
492642575Speter	{
492764562Sgshapiro		bool oldsuprerrs = SuprErrs;
492864562Sgshapiro
492942575Speter		/* Use mime8to7 to check multipart for MIME header overflows */
493042575Speter		boundaries[0] = NULL;
493142575Speter		mci->mci_flags |= MCIF_INHEADER;
493264562Sgshapiro
493364562Sgshapiro		/*
493464562Sgshapiro		**  If EF_DONT_MIME is set, we have a broken MIME message
493564562Sgshapiro		**  and don't want to generate a new bounce message whose
493664562Sgshapiro		**  body propagates the broken MIME.  We can't just not call
493764562Sgshapiro		**  mime8to7() as is done above since we need the security
493864562Sgshapiro		**  checks.  The best we can do is suppress the errors.
493964562Sgshapiro		*/
494064562Sgshapiro
494164562Sgshapiro		if (bitset(EF_DONT_MIME, e->e_flags))
494290792Sgshapiro			SuprErrs = true;
494364562Sgshapiro
4944157001Sgshapiro		if (mime8to7(mci, e->e_header, e, boundaries,
4945159609Sgshapiro				M87F_OUTER|M87F_NO8TO7, 0) == SM_IO_EOF)
4946157001Sgshapiro			goto writeerr;
494764562Sgshapiro
494864562Sgshapiro		/* restore SuprErrs */
494964562Sgshapiro		SuprErrs = oldsuprerrs;
495042575Speter	}
495138032Speter	else
495264562Sgshapiro#endif /* MIME8TO7 */
495338032Speter	{
495438032Speter		int ostate;
495538032Speter		register char *bp;
495638032Speter		register char *pbp;
495738032Speter		register int c;
495838032Speter		register char *xp;
495938032Speter		int padc;
496038032Speter		char *buflim;
496138032Speter		int pos = 0;
496264562Sgshapiro		char peekbuf[12];
496338032Speter
496438032Speter		if (bitset(MCIF_INHEADER, mci->mci_flags))
496538032Speter		{
4966157001Sgshapiro			if (!putline("", mci))
4967157001Sgshapiro				goto writeerr;
496838032Speter			mci->mci_flags &= ~MCIF_INHEADER;
496938032Speter		}
497038032Speter
497138032Speter		/* determine end of buffer; allow for short mailer lines */
4972168515Sgshapiro		buflim = &buf[sizeof(buf) - 1];
497338032Speter		if (mci->mci_mailer->m_linelimit > 0 &&
4974168515Sgshapiro		    mci->mci_mailer->m_linelimit < sizeof(buf) - 1)
497538032Speter			buflim = &buf[mci->mci_mailer->m_linelimit - 1];
497638032Speter
497738032Speter		/* copy temp file to output with mapping */
4978157001Sgshapiro		ostate = OSTATE_HEAD;
497938032Speter		bp = buf;
498038032Speter		pbp = peekbuf;
498190792Sgshapiro		while (!sm_io_error(mci->mci_out) && !dead)
498238032Speter		{
498338032Speter			if (pbp > peekbuf)
498438032Speter				c = *--pbp;
498590792Sgshapiro			else if ((c = sm_io_getc(e->e_dfp, SM_TIME_DEFAULT))
498690792Sgshapiro				 == SM_IO_EOF)
498738032Speter				break;
498838032Speter			if (bitset(MCIF_7BIT, mci->mci_flags))
498938032Speter				c &= 0x7f;
499038032Speter			switch (ostate)
499138032Speter			{
4992157001Sgshapiro			  case OSTATE_HEAD:
499338032Speter				if (c == '\0' &&
499490792Sgshapiro				    bitnset(M_NONULLS,
499590792Sgshapiro					    mci->mci_mailer->m_flags))
499638032Speter					break;
499738032Speter				if (c != '\r' && c != '\n' && bp < buflim)
499838032Speter				{
499938032Speter					*bp++ = c;
500038032Speter					break;
500138032Speter				}
500238032Speter
500338032Speter				/* check beginning of line for special cases */
500438032Speter				*bp = '\0';
500538032Speter				pos = 0;
500690792Sgshapiro				padc = SM_IO_EOF;
500738032Speter				if (buf[0] == 'F' &&
500890792Sgshapiro				    bitnset(M_ESCFROM, mci->mci_mailer->m_flags)
500990792Sgshapiro				    && strncmp(buf, "From ", 5) == 0)
501038032Speter				{
501138032Speter					padc = '>';
501238032Speter				}
501338032Speter				if (buf[0] == '-' && buf[1] == '-' &&
501438032Speter				    separator != NULL)
501538032Speter				{
501638032Speter					/* possible separator */
501738032Speter					int sl = strlen(separator);
501838032Speter
501990792Sgshapiro					if (strncmp(&buf[2], separator, sl)
502090792Sgshapiro					    == 0)
502138032Speter						padc = ' ';
502238032Speter				}
502338032Speter				if (buf[0] == '.' &&
502438032Speter				    bitnset(M_XDOT, mci->mci_mailer->m_flags))
502538032Speter				{
502638032Speter					padc = '.';
502738032Speter				}
502838032Speter
502938032Speter				/* now copy out saved line */
503038032Speter				if (TrafficLogFile != NULL)
503138032Speter				{
503290792Sgshapiro					(void) sm_io_fprintf(TrafficLogFile,
503390792Sgshapiro							     SM_TIME_DEFAULT,
503490792Sgshapiro							     "%05d >>> ",
503590792Sgshapiro							     (int) CurrentPid);
503690792Sgshapiro					if (padc != SM_IO_EOF)
503790792Sgshapiro						(void) sm_io_putc(TrafficLogFile,
503890792Sgshapiro								  SM_TIME_DEFAULT,
503990792Sgshapiro								  padc);
504038032Speter					for (xp = buf; xp < bp; xp++)
504190792Sgshapiro						(void) sm_io_putc(TrafficLogFile,
504290792Sgshapiro								  SM_TIME_DEFAULT,
504390792Sgshapiro								  (unsigned char) *xp);
504438032Speter					if (c == '\n')
504590792Sgshapiro						(void) sm_io_fputs(TrafficLogFile,
504690792Sgshapiro								   SM_TIME_DEFAULT,
504790792Sgshapiro								   mci->mci_mailer->m_eol);
504838032Speter				}
504990792Sgshapiro				if (padc != SM_IO_EOF)
505038032Speter				{
505190792Sgshapiro					if (sm_io_putc(mci->mci_out,
505290792Sgshapiro						       SM_TIME_DEFAULT, padc)
505390792Sgshapiro					    == SM_IO_EOF)
505464562Sgshapiro					{
505590792Sgshapiro						dead = true;
505664562Sgshapiro						continue;
505764562Sgshapiro					}
505838032Speter					pos++;
505938032Speter				}
506038032Speter				for (xp = buf; xp < bp; xp++)
506138032Speter				{
506290792Sgshapiro					if (sm_io_putc(mci->mci_out,
506390792Sgshapiro						       SM_TIME_DEFAULT,
506490792Sgshapiro						       (unsigned char) *xp)
506590792Sgshapiro					    == SM_IO_EOF)
506664562Sgshapiro					{
506790792Sgshapiro						dead = true;
506864562Sgshapiro						break;
506964562Sgshapiro					}
507038032Speter				}
507164562Sgshapiro				if (dead)
507264562Sgshapiro					continue;
507338032Speter				if (c == '\n')
507438032Speter				{
507590792Sgshapiro					if (sm_io_fputs(mci->mci_out,
507690792Sgshapiro							SM_TIME_DEFAULT,
507790792Sgshapiro							mci->mci_mailer->m_eol)
507890792Sgshapiro							== SM_IO_EOF)
507964562Sgshapiro						break;
508038032Speter					pos = 0;
508138032Speter				}
508238032Speter				else
508338032Speter				{
508438032Speter					pos += bp - buf;
508538032Speter					if (c != '\r')
5086112810Sgshapiro					{
5087112810Sgshapiro						SM_ASSERT(pbp < peekbuf +
5088112810Sgshapiro								sizeof(peekbuf));
508938032Speter						*pbp++ = c;
5090112810Sgshapiro					}
509138032Speter				}
509264562Sgshapiro
509338032Speter				bp = buf;
509438032Speter
509538032Speter				/* determine next state */
509638032Speter				if (c == '\n')
5097157001Sgshapiro					ostate = OSTATE_HEAD;
509838032Speter				else if (c == '\r')
5099157001Sgshapiro					ostate = OSTATE_CR;
510038032Speter				else
5101157001Sgshapiro					ostate = OSTATE_INLINE;
510238032Speter				continue;
510338032Speter
5104157001Sgshapiro			  case OSTATE_CR:
510538032Speter				if (c == '\n')
510638032Speter				{
510738032Speter					/* got CRLF */
510890792Sgshapiro					if (sm_io_fputs(mci->mci_out,
510990792Sgshapiro							SM_TIME_DEFAULT,
511090792Sgshapiro							mci->mci_mailer->m_eol)
511190792Sgshapiro							== SM_IO_EOF)
511264562Sgshapiro						continue;
511364562Sgshapiro
511438032Speter					if (TrafficLogFile != NULL)
511538032Speter					{
511690792Sgshapiro						(void) sm_io_fputs(TrafficLogFile,
511790792Sgshapiro								   SM_TIME_DEFAULT,
511890792Sgshapiro								   mci->mci_mailer->m_eol);
511938032Speter					}
5120168515Sgshapiro					pos = 0;
5121157001Sgshapiro					ostate = OSTATE_HEAD;
512238032Speter					continue;
512338032Speter				}
512438032Speter
512538032Speter				/* had a naked carriage return */
5126112810Sgshapiro				SM_ASSERT(pbp < peekbuf + sizeof(peekbuf));
512738032Speter				*pbp++ = c;
512838032Speter				c = '\r';
5129157001Sgshapiro				ostate = OSTATE_INLINE;
513038032Speter				goto putch;
513138032Speter
5132157001Sgshapiro			  case OSTATE_INLINE:
513338032Speter				if (c == '\r')
513438032Speter				{
5135157001Sgshapiro					ostate = OSTATE_CR;
513638032Speter					continue;
513738032Speter				}
513838032Speter				if (c == '\0' &&
513990792Sgshapiro				    bitnset(M_NONULLS,
514090792Sgshapiro					    mci->mci_mailer->m_flags))
514138032Speter					break;
514238032Speterputch:
514338032Speter				if (mci->mci_mailer->m_linelimit > 0 &&
514464562Sgshapiro				    pos >= mci->mci_mailer->m_linelimit - 1 &&
514538032Speter				    c != '\n')
514638032Speter				{
514764562Sgshapiro					int d;
514864562Sgshapiro
514964562Sgshapiro					/* check next character for EOL */
515064562Sgshapiro					if (pbp > peekbuf)
515164562Sgshapiro						d = *(pbp - 1);
515290792Sgshapiro					else if ((d = sm_io_getc(e->e_dfp,
515390792Sgshapiro								 SM_TIME_DEFAULT))
515490792Sgshapiro						 != SM_IO_EOF)
5155112810Sgshapiro					{
5156112810Sgshapiro						SM_ASSERT(pbp < peekbuf +
5157112810Sgshapiro								sizeof(peekbuf));
515864562Sgshapiro						*pbp++ = d;
5159112810Sgshapiro					}
516064562Sgshapiro
516190792Sgshapiro					if (d == '\n' || d == SM_IO_EOF)
516264562Sgshapiro					{
516364562Sgshapiro						if (TrafficLogFile != NULL)
516490792Sgshapiro							(void) sm_io_putc(TrafficLogFile,
516590792Sgshapiro									  SM_TIME_DEFAULT,
516690792Sgshapiro									  (unsigned char) c);
516790792Sgshapiro						if (sm_io_putc(mci->mci_out,
516890792Sgshapiro							       SM_TIME_DEFAULT,
516990792Sgshapiro							       (unsigned char) c)
517090792Sgshapiro							       == SM_IO_EOF)
517164562Sgshapiro						{
517290792Sgshapiro							dead = true;
517364562Sgshapiro							continue;
517464562Sgshapiro						}
517564562Sgshapiro						pos++;
517664562Sgshapiro						continue;
517764562Sgshapiro					}
517864562Sgshapiro
517990792Sgshapiro					if (sm_io_putc(mci->mci_out,
518090792Sgshapiro						       SM_TIME_DEFAULT, '!')
518190792Sgshapiro					    == SM_IO_EOF ||
518290792Sgshapiro					    sm_io_fputs(mci->mci_out,
518390792Sgshapiro							SM_TIME_DEFAULT,
518490792Sgshapiro							mci->mci_mailer->m_eol)
518590792Sgshapiro					    == SM_IO_EOF)
518664562Sgshapiro					{
518790792Sgshapiro						dead = true;
518864562Sgshapiro						continue;
518964562Sgshapiro					}
519064562Sgshapiro
519138032Speter					if (TrafficLogFile != NULL)
519238032Speter					{
519390792Sgshapiro						(void) sm_io_fprintf(TrafficLogFile,
519490792Sgshapiro								     SM_TIME_DEFAULT,
519590792Sgshapiro								     "!%s",
519690792Sgshapiro								     mci->mci_mailer->m_eol);
519738032Speter					}
5198157001Sgshapiro					ostate = OSTATE_HEAD;
5199112810Sgshapiro					SM_ASSERT(pbp < peekbuf +
5200112810Sgshapiro							sizeof(peekbuf));
520138032Speter					*pbp++ = c;
520238032Speter					continue;
520338032Speter				}
520438032Speter				if (c == '\n')
520538032Speter				{
520638032Speter					if (TrafficLogFile != NULL)
520790792Sgshapiro						(void) sm_io_fputs(TrafficLogFile,
520890792Sgshapiro								   SM_TIME_DEFAULT,
520990792Sgshapiro								   mci->mci_mailer->m_eol);
521090792Sgshapiro					if (sm_io_fputs(mci->mci_out,
521190792Sgshapiro							SM_TIME_DEFAULT,
521290792Sgshapiro							mci->mci_mailer->m_eol)
521390792Sgshapiro							== SM_IO_EOF)
521464562Sgshapiro						continue;
521538032Speter					pos = 0;
5216157001Sgshapiro					ostate = OSTATE_HEAD;
521738032Speter				}
521838032Speter				else
521938032Speter				{
522038032Speter					if (TrafficLogFile != NULL)
522190792Sgshapiro						(void) sm_io_putc(TrafficLogFile,
522290792Sgshapiro								  SM_TIME_DEFAULT,
522390792Sgshapiro								  (unsigned char) c);
522490792Sgshapiro					if (sm_io_putc(mci->mci_out,
522590792Sgshapiro						       SM_TIME_DEFAULT,
522690792Sgshapiro						       (unsigned char) c)
522790792Sgshapiro					    == SM_IO_EOF)
522864562Sgshapiro					{
522990792Sgshapiro						dead = true;
523064562Sgshapiro						continue;
523164562Sgshapiro					}
523238032Speter					pos++;
5233157001Sgshapiro					ostate = OSTATE_INLINE;
523438032Speter				}
523538032Speter				break;
523638032Speter			}
523738032Speter		}
523838032Speter
523938032Speter		/* make sure we are at the beginning of a line */
524038032Speter		if (bp > buf)
524138032Speter		{
524238032Speter			if (TrafficLogFile != NULL)
524338032Speter			{
524438032Speter				for (xp = buf; xp < bp; xp++)
524590792Sgshapiro					(void) sm_io_putc(TrafficLogFile,
524690792Sgshapiro							  SM_TIME_DEFAULT,
524790792Sgshapiro							  (unsigned char) *xp);
524838032Speter			}
524938032Speter			for (xp = buf; xp < bp; xp++)
525038032Speter			{
525190792Sgshapiro				if (sm_io_putc(mci->mci_out, SM_TIME_DEFAULT,
525290792Sgshapiro					       (unsigned char) *xp)
525390792Sgshapiro				    == SM_IO_EOF)
525464562Sgshapiro				{
525590792Sgshapiro					dead = true;
525664562Sgshapiro					break;
525764562Sgshapiro				}
525838032Speter			}
525938032Speter			pos += bp - buf;
526038032Speter		}
526164562Sgshapiro		if (!dead && pos > 0)
526238032Speter		{
526338032Speter			if (TrafficLogFile != NULL)
526490792Sgshapiro				(void) sm_io_fputs(TrafficLogFile,
526590792Sgshapiro						   SM_TIME_DEFAULT,
526690792Sgshapiro						   mci->mci_mailer->m_eol);
5267157001Sgshapiro			if (sm_io_fputs(mci->mci_out, SM_TIME_DEFAULT,
5268157001Sgshapiro					   mci->mci_mailer->m_eol) == SM_IO_EOF)
5269157001Sgshapiro				goto writeerr;
527038032Speter		}
527138032Speter	}
527238032Speter
527390792Sgshapiro	if (sm_io_error(e->e_dfp))
527438032Speter	{
527590792Sgshapiro		syserr("putbody: %s/%cf%s: read error",
527690792Sgshapiro		       qid_printqueue(e->e_dfqgrp, e->e_dfqdir),
527790792Sgshapiro		       DATAFL_LETTER, e->e_id);
527838032Speter		ExitStat = EX_IOERR;
5279157001Sgshapiro		ioerr = true;
528038032Speter	}
528138032Speter
528238032Speterendofmessage:
528364562Sgshapiro	/*
528464562Sgshapiro	**  Since mailfile() uses e_dfp in a child process,
528564562Sgshapiro	**  the file offset in the stdio library for the
528664562Sgshapiro	**  parent process will not agree with the in-kernel
528764562Sgshapiro	**  file offset since the file descriptor is shared
528864562Sgshapiro	**  between the processes.  Therefore, it is vital
528964562Sgshapiro	**  that the file always be rewound.  This forces the
529064562Sgshapiro	**  kernel offset (lseek) and stdio library (ftell)
529164562Sgshapiro	**  offset to match.
529264562Sgshapiro	*/
529364562Sgshapiro
5294157001Sgshapiro	save_errno = errno;
529564562Sgshapiro	if (e->e_dfp != NULL)
529664562Sgshapiro		(void) bfrewind(e->e_dfp);
529764562Sgshapiro
529838032Speter	/* some mailers want extra blank line at end of message */
529964562Sgshapiro	if (!dead && bitnset(M_BLANKEND, mci->mci_mailer->m_flags) &&
530038032Speter	    buf[0] != '\0' && buf[0] != '\n')
5301157001Sgshapiro	{
5302157001Sgshapiro		if (!putline("", mci))
5303157001Sgshapiro			goto writeerr;
5304157001Sgshapiro	}
530538032Speter
5306157001Sgshapiro	if (!dead &&
5307157001Sgshapiro	    (sm_io_flush(mci->mci_out, SM_TIME_DEFAULT) == SM_IO_EOF ||
5308157001Sgshapiro	     (sm_io_error(mci->mci_out) && errno != EPIPE)))
530938032Speter	{
5310157001Sgshapiro		save_errno = errno;
531138032Speter		syserr("putbody: write error");
531238032Speter		ExitStat = EX_IOERR;
5313157001Sgshapiro		ioerr = true;
531438032Speter	}
531564562Sgshapiro
5316157001Sgshapiro	errno = save_errno;
5317157001Sgshapiro	return !dead && !ioerr;
5318157001Sgshapiro
5319157001Sgshapiro  writeerr:
5320157001Sgshapiro	return false;
532138032Speter}
5322157001Sgshapiro
532390792Sgshapiro/*
532438032Speter**  MAILFILE -- Send a message to a file.
532538032Speter**
532690792Sgshapiro**	If the file has the set-user-ID/set-group-ID bits set, but NO
532790792Sgshapiro**	execute bits, sendmail will try to become the owner of that file
532838032Speter**	rather than the real user.  Obviously, this only works if
532938032Speter**	sendmail runs as root.
533038032Speter**
533138032Speter**	This could be done as a subordinate mailer, except that it
533238032Speter**	is used implicitly to save messages in ~/dead.letter.  We
533338032Speter**	view this as being sufficiently important as to include it
533438032Speter**	here.  For example, if the system is dying, we shouldn't have
533538032Speter**	to create another process plus some pipes to save the message.
533638032Speter**
533738032Speter**	Parameters:
533838032Speter**		filename -- the name of the file to send to.
533938032Speter**		mailer -- mailer definition for recipient -- if NULL,
534038032Speter**			use FileMailer.
534138032Speter**		ctladdr -- the controlling address header -- includes
534238032Speter**			the userid/groupid to be when sending.
534338032Speter**		sfflags -- flags for opening.
534438032Speter**		e -- the current envelope.
534538032Speter**
534638032Speter**	Returns:
534738032Speter**		The exit code associated with the operation.
534838032Speter**
534938032Speter**	Side Effects:
535038032Speter**		none.
535138032Speter*/
535238032Speter
535390792Sgshapiro# define RETURN(st)			exit(st);
535490792Sgshapiro
535538032Speterstatic jmp_buf	CtxMailfileTimeout;
535638032Speter
535738032Speterint
535838032Spetermailfile(filename, mailer, ctladdr, sfflags, e)
535938032Speter	char *volatile filename;
536038032Speter	MAILER *volatile mailer;
536138032Speter	ADDRESS *ctladdr;
536264562Sgshapiro	volatile long sfflags;
536338032Speter	register ENVELOPE *e;
536438032Speter{
536590792Sgshapiro	register SM_FILE_T *f;
536638032Speter	register pid_t pid = -1;
536764562Sgshapiro	volatile int mode;
536864562Sgshapiro	int len;
536964562Sgshapiro	off_t curoff;
537038032Speter	bool suidwarn = geteuid() == 0;
537138032Speter	char *p;
537264562Sgshapiro	char *volatile realfile;
537390792Sgshapiro	SM_EVENT *ev;
537498121Sgshapiro	char buf[MAXPATHLEN];
537598121Sgshapiro	char targetfile[MAXPATHLEN];
537638032Speter
537738032Speter	if (tTd(11, 1))
537838032Speter	{
537990792Sgshapiro		sm_dprintf("mailfile %s\n  ctladdr=", filename);
5380132943Sgshapiro		printaddr(sm_debug_file(), ctladdr, false);
538138032Speter	}
538238032Speter
538338032Speter	if (mailer == NULL)
538438032Speter		mailer = FileMailer;
538538032Speter
538638032Speter	if (e->e_xfp != NULL)
538790792Sgshapiro		(void) sm_io_flush(e->e_xfp, SM_TIME_DEFAULT);
538838032Speter
538938032Speter	/*
539038032Speter	**  Special case /dev/null.  This allows us to restrict file
539138032Speter	**  delivery to regular files only.
539238032Speter	*/
539338032Speter
539490792Sgshapiro	if (sm_path_isdevnull(filename))
539538032Speter		return EX_OK;
539638032Speter
539738032Speter	/* check for 8-bit available */
539838032Speter	if (bitset(EF_HAS8BIT, e->e_flags) &&
539938032Speter	    bitnset(M_7BITS, mailer->m_flags) &&
540038032Speter	    (bitset(EF_DONT_MIME, e->e_flags) ||
540138032Speter	     !(bitset(MM_MIME8BIT, MimeMode) ||
540238032Speter	       (bitset(EF_IS_MIME, e->e_flags) &&
540338032Speter		bitset(MM_CVTMIME, MimeMode)))))
540438032Speter	{
540538032Speter		e->e_status = "5.6.3";
540664562Sgshapiro		usrerrenh(e->e_status,
540790792Sgshapiro			  "554 Cannot send 8-bit data to 7-bit destination");
540890792Sgshapiro		errno = 0;
540964562Sgshapiro		return EX_DATAERR;
541038032Speter	}
541138032Speter
541264562Sgshapiro	/* Find the actual file */
541364562Sgshapiro	if (SafeFileEnv != NULL && SafeFileEnv[0] != '\0')
541464562Sgshapiro	{
541564562Sgshapiro		len = strlen(SafeFileEnv);
541664562Sgshapiro
541764562Sgshapiro		if (strncmp(SafeFileEnv, filename, len) == 0)
541864562Sgshapiro			filename += len;
541964562Sgshapiro
5420168515Sgshapiro		if (len + strlen(filename) + 1 >= sizeof(targetfile))
542164562Sgshapiro		{
542264562Sgshapiro			syserr("mailfile: filename too long (%s/%s)",
542364562Sgshapiro			       SafeFileEnv, filename);
542464562Sgshapiro			return EX_CANTCREAT;
542564562Sgshapiro		}
5426168515Sgshapiro		(void) sm_strlcpy(targetfile, SafeFileEnv, sizeof(targetfile));
542764562Sgshapiro		realfile = targetfile + len;
542864562Sgshapiro		if (*filename == '/')
542964562Sgshapiro			filename++;
543094334Sgshapiro		if (*filename != '\0')
543194334Sgshapiro		{
543294334Sgshapiro			/* paranoia: trailing / should be removed in readcf */
543394334Sgshapiro			if (targetfile[len - 1] != '/')
543494334Sgshapiro				(void) sm_strlcat(targetfile,
5435168515Sgshapiro						  "/", sizeof(targetfile));
543694334Sgshapiro			(void) sm_strlcat(targetfile, filename,
5437168515Sgshapiro					  sizeof(targetfile));
543894334Sgshapiro		}
543964562Sgshapiro	}
544064562Sgshapiro	else if (mailer->m_rootdir != NULL)
544164562Sgshapiro	{
5442168515Sgshapiro		expand(mailer->m_rootdir, targetfile, sizeof(targetfile), e);
544364562Sgshapiro		len = strlen(targetfile);
544464562Sgshapiro
544564562Sgshapiro		if (strncmp(targetfile, filename, len) == 0)
544664562Sgshapiro			filename += len;
544764562Sgshapiro
5448168515Sgshapiro		if (len + strlen(filename) + 1 >= sizeof(targetfile))
544964562Sgshapiro		{
545064562Sgshapiro			syserr("mailfile: filename too long (%s/%s)",
545164562Sgshapiro			       targetfile, filename);
545264562Sgshapiro			return EX_CANTCREAT;
545364562Sgshapiro		}
545464562Sgshapiro		realfile = targetfile + len;
545564562Sgshapiro		if (targetfile[len - 1] != '/')
5456168515Sgshapiro			(void) sm_strlcat(targetfile, "/", sizeof(targetfile));
545764562Sgshapiro		if (*filename == '/')
545890792Sgshapiro			(void) sm_strlcat(targetfile, filename + 1,
5459168515Sgshapiro					  sizeof(targetfile));
546064562Sgshapiro		else
546190792Sgshapiro			(void) sm_strlcat(targetfile, filename,
5462168515Sgshapiro					  sizeof(targetfile));
546364562Sgshapiro	}
546464562Sgshapiro	else
546564562Sgshapiro	{
5466168515Sgshapiro		if (sm_strlcpy(targetfile, filename, sizeof(targetfile)) >=
5467168515Sgshapiro		    sizeof(targetfile))
546864562Sgshapiro		{
546964562Sgshapiro			syserr("mailfile: filename too long (%s)", filename);
547064562Sgshapiro			return EX_CANTCREAT;
547164562Sgshapiro		}
547264562Sgshapiro		realfile = targetfile;
547364562Sgshapiro	}
547464562Sgshapiro
547538032Speter	/*
547638032Speter	**  Fork so we can change permissions here.
547738032Speter	**	Note that we MUST use fork, not vfork, because of
547838032Speter	**	the complications of calling subroutines, etc.
547938032Speter	*/
548038032Speter
548194334Sgshapiro
548294334Sgshapiro	/*
548394334Sgshapiro	**  Dispose of SIGCHLD signal catchers that may be laying
548494334Sgshapiro	**  around so that the waitfor() below will get it.
548594334Sgshapiro	*/
548694334Sgshapiro
548794334Sgshapiro	(void) sm_signal(SIGCHLD, SIG_DFL);
548894334Sgshapiro
548938032Speter	DOFORK(fork);
549038032Speter
549138032Speter	if (pid < 0)
549264562Sgshapiro		return EX_OSERR;
549338032Speter	else if (pid == 0)
549438032Speter	{
549538032Speter		/* child -- actually write to file */
549638032Speter		struct stat stb;
549738032Speter		MCI mcibuf;
549842575Speter		int err;
549938032Speter		volatile int oflags = O_WRONLY|O_APPEND;
550038032Speter
550177349Sgshapiro		/* Reset global flags */
550277349Sgshapiro		RestartRequest = NULL;
550390792Sgshapiro		RestartWorkGroup = false;
550477349Sgshapiro		ShutdownRequest = NULL;
550577349Sgshapiro		PendingSignal = 0;
550690792Sgshapiro		CurrentPid = getpid();
550777349Sgshapiro
550838032Speter		if (e->e_lockfp != NULL)
5509159609Sgshapiro		{
5510159609Sgshapiro			int fd;
551138032Speter
5512159609Sgshapiro			fd = sm_io_getinfo(e->e_lockfp, SM_IO_WHAT_FD, NULL);
5513159609Sgshapiro			/* SM_ASSERT(fd >= 0); */
5514159609Sgshapiro			if (fd >= 0)
5515159609Sgshapiro				(void) close(fd);
5516159609Sgshapiro		}
5517159609Sgshapiro
551890792Sgshapiro		(void) sm_signal(SIGINT, SIG_DFL);
551990792Sgshapiro		(void) sm_signal(SIGHUP, SIG_DFL);
552090792Sgshapiro		(void) sm_signal(SIGTERM, SIG_DFL);
552138032Speter		(void) umask(OldUmask);
552238032Speter		e->e_to = filename;
552338032Speter		ExitStat = EX_OK;
552438032Speter
552538032Speter		if (setjmp(CtxMailfileTimeout) != 0)
552638032Speter		{
552790792Sgshapiro			RETURN(EX_TEMPFAIL);
552838032Speter		}
552938032Speter
553038032Speter		if (TimeOuts.to_fileopen > 0)
553190792Sgshapiro			ev = sm_setevent(TimeOuts.to_fileopen, mailfiletimeout,
553290792Sgshapiro					 0);
553338032Speter		else
553438032Speter			ev = NULL;
553538032Speter
553690792Sgshapiro		/* check file mode to see if set-user-ID */
553764562Sgshapiro		if (stat(targetfile, &stb) < 0)
553864562Sgshapiro			mode = FileMode;
553942575Speter		else
554038032Speter			mode = stb.st_mode;
554138032Speter
554238032Speter		/* limit the errors to those actually caused in the child */
554338032Speter		errno = 0;
554438032Speter		ExitStat = EX_OK;
554538032Speter
554664562Sgshapiro		/* Allow alias expansions to use the S_IS{U,G}ID bits */
554764562Sgshapiro		if ((ctladdr != NULL && !bitset(QALIAS, ctladdr->q_flags)) ||
554864562Sgshapiro		    bitset(SFF_RUNASREALUID, sfflags))
554938032Speter		{
555090792Sgshapiro			/* ignore set-user-ID and set-group-ID bits */
555138032Speter			mode &= ~(S_ISGID|S_ISUID);
555264562Sgshapiro			if (tTd(11, 20))
555390792Sgshapiro				sm_dprintf("mailfile: ignoring set-user-ID/set-group-ID bits\n");
555438032Speter		}
555538032Speter
555690792Sgshapiro		/* we have to open the data file BEFORE setuid() */
555738032Speter		if (e->e_dfp == NULL && bitset(EF_HAS_DF, e->e_flags))
555838032Speter		{
555990792Sgshapiro			char *df = queuename(e, DATAFL_LETTER);
556038032Speter
556190792Sgshapiro			e->e_dfp = sm_io_open(SmFtStdio, SM_TIME_DEFAULT, df,
5562120256Sgshapiro					      SM_IO_RDONLY_B, NULL);
556338032Speter			if (e->e_dfp == NULL)
556438032Speter			{
556538032Speter				syserr("mailfile: Cannot open %s for %s from %s",
556638032Speter					df, e->e_to, e->e_from.q_paddr);
556738032Speter			}
556838032Speter		}
556938032Speter
557038032Speter		/* select a new user to run as */
557138032Speter		if (!bitset(SFF_RUNASREALUID, sfflags))
557238032Speter		{
557338032Speter			if (bitnset(M_SPECIFIC_UID, mailer->m_flags))
557438032Speter			{
557538032Speter				RealUserName = NULL;
5576132943Sgshapiro				if (mailer->m_uid == NO_UID)
5577132943Sgshapiro					RealUid = RunAsUid;
5578132943Sgshapiro				else
5579132943Sgshapiro					RealUid = mailer->m_uid;
558064562Sgshapiro				if (RunAsUid != 0 && RealUid != RunAsUid)
558164562Sgshapiro				{
558264562Sgshapiro					/* Only root can change the uid */
5583285229Sgshapiro					syserr("mailfile: insufficient privileges to change uid, RunAsUid=%ld, RealUid=%ld",
5584285229Sgshapiro						(long) RunAsUid, (long) RealUid);
558590792Sgshapiro					RETURN(EX_TEMPFAIL);
558664562Sgshapiro				}
558738032Speter			}
558838032Speter			else if (bitset(S_ISUID, mode))
558938032Speter			{
559038032Speter				RealUserName = NULL;
559138032Speter				RealUid = stb.st_uid;
559238032Speter			}
559338032Speter			else if (ctladdr != NULL && ctladdr->q_uid != 0)
559438032Speter			{
559538032Speter				if (ctladdr->q_ruser != NULL)
559638032Speter					RealUserName = ctladdr->q_ruser;
559738032Speter				else
559838032Speter					RealUserName = ctladdr->q_user;
559938032Speter				RealUid = ctladdr->q_uid;
560038032Speter			}
5601132943Sgshapiro			else if (mailer != NULL && mailer->m_uid != NO_UID)
560238032Speter			{
560338032Speter				RealUserName = DefUser;
560438032Speter				RealUid = mailer->m_uid;
560538032Speter			}
560638032Speter			else
560738032Speter			{
560838032Speter				RealUserName = DefUser;
560938032Speter				RealUid = DefUid;
561038032Speter			}
561138032Speter
561238032Speter			/* select a new group to run as */
561338032Speter			if (bitnset(M_SPECIFIC_UID, mailer->m_flags))
561464562Sgshapiro			{
5615132943Sgshapiro				if (mailer->m_gid == NO_GID)
5616132943Sgshapiro					RealGid = RunAsGid;
5617132943Sgshapiro				else
5618132943Sgshapiro					RealGid = mailer->m_gid;
561964562Sgshapiro				if (RunAsUid != 0 &&
562064562Sgshapiro				    (RealGid != getgid() ||
562164562Sgshapiro				     RealGid != getegid()))
562264562Sgshapiro				{
562364562Sgshapiro					/* Only root can change the gid */
5624285229Sgshapiro					syserr("mailfile: insufficient privileges to change gid, RealGid=%ld, RunAsUid=%ld, gid=%ld, egid=%ld",
5625285229Sgshapiro					       (long) RealGid, (long) RunAsUid,
5626285229Sgshapiro					       (long) getgid(), (long) getegid());
562790792Sgshapiro					RETURN(EX_TEMPFAIL);
562864562Sgshapiro				}
562964562Sgshapiro			}
563038032Speter			else if (bitset(S_ISGID, mode))
563138032Speter				RealGid = stb.st_gid;
563264562Sgshapiro			else if (ctladdr != NULL &&
563364562Sgshapiro				 ctladdr->q_uid == DefUid &&
563464562Sgshapiro				 ctladdr->q_gid == 0)
563571345Sgshapiro			{
563671345Sgshapiro				/*
563771345Sgshapiro				**  Special case:  This means it is an
563871345Sgshapiro				**  alias and we should act as DefaultUser.
563971345Sgshapiro				**  See alias()'s comments.
564071345Sgshapiro				*/
564171345Sgshapiro
564264562Sgshapiro				RealGid = DefGid;
564371345Sgshapiro				RealUserName = DefUser;
564471345Sgshapiro			}
564571345Sgshapiro			else if (ctladdr != NULL && ctladdr->q_uid != 0)
564671345Sgshapiro				RealGid = ctladdr->q_gid;
5647132943Sgshapiro			else if (mailer != NULL && mailer->m_gid != NO_GID)
564838032Speter				RealGid = mailer->m_gid;
564938032Speter			else
565038032Speter				RealGid = DefGid;
565138032Speter		}
565238032Speter
565338032Speter		/* last ditch */
565438032Speter		if (!bitset(SFF_ROOTOK, sfflags))
565538032Speter		{
565638032Speter			if (RealUid == 0)
565738032Speter				RealUid = DefUid;
565838032Speter			if (RealGid == 0)
565938032Speter				RealGid = DefGid;
566038032Speter		}
566138032Speter
566238032Speter		/* set group id list (needs /etc/group access) */
566338032Speter		if (RealUserName != NULL && !DontInitGroups)
566438032Speter		{
566538032Speter			if (initgroups(RealUserName, RealGid) == -1 && suidwarn)
566664562Sgshapiro			{
5667285229Sgshapiro				syserr("mailfile: initgroups(%s, %ld) failed",
5668285229Sgshapiro					RealUserName, (long) RealGid);
566990792Sgshapiro				RETURN(EX_TEMPFAIL);
567064562Sgshapiro			}
567138032Speter		}
567238032Speter		else
567338032Speter		{
567438032Speter			GIDSET_T gidset[1];
567538032Speter
567638032Speter			gidset[0] = RealGid;
567738032Speter			if (setgroups(1, gidset) == -1 && suidwarn)
567864562Sgshapiro			{
567938032Speter				syserr("mailfile: setgroups() failed");
568090792Sgshapiro				RETURN(EX_TEMPFAIL);
568164562Sgshapiro			}
568238032Speter		}
568338032Speter
568464562Sgshapiro		/*
568564562Sgshapiro		**  If you have a safe environment, go into it.
568664562Sgshapiro		*/
568764562Sgshapiro
568864562Sgshapiro		if (realfile != targetfile)
568938032Speter		{
569094334Sgshapiro			char save;
569194334Sgshapiro
569294334Sgshapiro			save = *realfile;
569364562Sgshapiro			*realfile = '\0';
569464562Sgshapiro			if (tTd(11, 20))
569590792Sgshapiro				sm_dprintf("mailfile: chroot %s\n", targetfile);
569664562Sgshapiro			if (chroot(targetfile) < 0)
569738032Speter			{
569838032Speter				syserr("mailfile: Cannot chroot(%s)",
569964562Sgshapiro				       targetfile);
570090792Sgshapiro				RETURN(EX_CANTCREAT);
570138032Speter			}
570294334Sgshapiro			*realfile = save;
570338032Speter		}
570464562Sgshapiro
570564562Sgshapiro		if (tTd(11, 40))
570690792Sgshapiro			sm_dprintf("mailfile: deliver to %s\n", realfile);
570764562Sgshapiro
570838032Speter		if (chdir("/") < 0)
570964562Sgshapiro		{
571038032Speter			syserr("mailfile: cannot chdir(/)");
571190792Sgshapiro			RETURN(EX_CANTCREAT);
571264562Sgshapiro		}
571338032Speter
571438032Speter		/* now reset the group and user ids */
571538032Speter		endpwent();
571690792Sgshapiro		sm_mbdb_terminate();
571738032Speter		if (setgid(RealGid) < 0 && suidwarn)
571864562Sgshapiro		{
571938032Speter			syserr("mailfile: setgid(%ld) failed", (long) RealGid);
572090792Sgshapiro			RETURN(EX_TEMPFAIL);
572164562Sgshapiro		}
572238032Speter		vendor_set_uid(RealUid);
572338032Speter		if (setuid(RealUid) < 0 && suidwarn)
572464562Sgshapiro		{
572538032Speter			syserr("mailfile: setuid(%ld) failed", (long) RealUid);
572690792Sgshapiro			RETURN(EX_TEMPFAIL);
572764562Sgshapiro		}
572838032Speter
572964562Sgshapiro		if (tTd(11, 2))
5730285229Sgshapiro			sm_dprintf("mailfile: running as r/euid=%ld/%ld, r/egid=%ld/%ld\n",
5731285229Sgshapiro				(long) getuid(), (long) geteuid(),
5732285229Sgshapiro				(long) getgid(), (long) getegid());
573364562Sgshapiro
573464562Sgshapiro
573538032Speter		/* move into some "safe" directory */
573638032Speter		if (mailer->m_execdir != NULL)
573738032Speter		{
573838032Speter			char *q;
573938032Speter
574038032Speter			for (p = mailer->m_execdir; p != NULL; p = q)
574138032Speter			{
574238032Speter				q = strchr(p, ':');
574338032Speter				if (q != NULL)
574438032Speter					*q = '\0';
5745168515Sgshapiro				expand(p, buf, sizeof(buf), e);
574638032Speter				if (q != NULL)
574738032Speter					*q++ = ':';
574838032Speter				if (tTd(11, 20))
574990792Sgshapiro					sm_dprintf("mailfile: trydir %s\n",
575090792Sgshapiro						   buf);
575138032Speter				if (buf[0] != '\0' && chdir(buf) >= 0)
575238032Speter					break;
575338032Speter			}
575438032Speter		}
575538032Speter
575664562Sgshapiro		/*
575764562Sgshapiro		**  Recheck the file after we have assumed the ID of the
575864562Sgshapiro		**  delivery user to make sure we can deliver to it as
575964562Sgshapiro		**  that user.  This is necessary if sendmail is running
576064562Sgshapiro		**  as root and the file is on an NFS mount which treats
576164562Sgshapiro		**  root as nobody.
576264562Sgshapiro		*/
576364562Sgshapiro
576464562Sgshapiro#if HASLSTAT
576564562Sgshapiro		if (bitnset(DBS_FILEDELIVERYTOSYMLINK, DontBlameSendmail))
576664562Sgshapiro			err = stat(realfile, &stb);
576764562Sgshapiro		else
576864562Sgshapiro			err = lstat(realfile, &stb);
576964562Sgshapiro#else /* HASLSTAT */
577064562Sgshapiro		err = stat(realfile, &stb);
577164562Sgshapiro#endif /* HASLSTAT */
577264562Sgshapiro
577364562Sgshapiro		if (err < 0)
577464562Sgshapiro		{
577564562Sgshapiro			stb.st_mode = ST_MODE_NOFILE;
577664562Sgshapiro			mode = FileMode;
577764562Sgshapiro			oflags |= O_CREAT|O_EXCL;
577864562Sgshapiro		}
577964562Sgshapiro		else if (bitset(S_IXUSR|S_IXGRP|S_IXOTH, mode) ||
578064562Sgshapiro			 (!bitnset(DBS_FILEDELIVERYTOHARDLINK,
578164562Sgshapiro				   DontBlameSendmail) &&
578264562Sgshapiro			  stb.st_nlink != 1) ||
578364562Sgshapiro			 (realfile != targetfile && !S_ISREG(mode)))
578464562Sgshapiro			exit(EX_CANTCREAT);
578564562Sgshapiro		else
578664562Sgshapiro			mode = stb.st_mode;
578764562Sgshapiro
578864562Sgshapiro		if (!bitnset(DBS_FILEDELIVERYTOSYMLINK, DontBlameSendmail))
578938032Speter			sfflags |= SFF_NOSLINK;
579064562Sgshapiro		if (!bitnset(DBS_FILEDELIVERYTOHARDLINK, DontBlameSendmail))
579138032Speter			sfflags |= SFF_NOHLINK;
579238032Speter		sfflags &= ~SFF_OPENASROOT;
579364562Sgshapiro		f = safefopen(realfile, oflags, mode, sfflags);
579438032Speter		if (f == NULL)
579538032Speter		{
579664562Sgshapiro			if (transienterror(errno))
579764562Sgshapiro			{
579864562Sgshapiro				usrerr("454 4.3.0 cannot open %s: %s",
579964562Sgshapiro				       shortenstring(realfile, MAXSHORTSTR),
580090792Sgshapiro				       sm_errstring(errno));
580190792Sgshapiro				RETURN(EX_TEMPFAIL);
580264562Sgshapiro			}
580364562Sgshapiro			else
580464562Sgshapiro			{
580564562Sgshapiro				usrerr("554 5.3.0 cannot open %s: %s",
580664562Sgshapiro				       shortenstring(realfile, MAXSHORTSTR),
580790792Sgshapiro				       sm_errstring(errno));
580890792Sgshapiro				RETURN(EX_CANTCREAT);
580964562Sgshapiro			}
581038032Speter		}
581190792Sgshapiro		if (filechanged(realfile, sm_io_getinfo(f, SM_IO_WHAT_FD, NULL),
581290792Sgshapiro		    &stb))
581338032Speter		{
581464562Sgshapiro			syserr("554 5.3.0 file changed after open");
581590792Sgshapiro			RETURN(EX_CANTCREAT);
581638032Speter		}
581790792Sgshapiro		if (fstat(sm_io_getinfo(f, SM_IO_WHAT_FD, NULL), &stb) < 0)
581838032Speter		{
581990792Sgshapiro			syserr("554 5.3.0 cannot fstat %s",
582090792Sgshapiro				sm_errstring(errno));
582190792Sgshapiro			RETURN(EX_CANTCREAT);
582238032Speter		}
582338032Speter
582464562Sgshapiro		curoff = stb.st_size;
582564562Sgshapiro
582638032Speter		if (ev != NULL)
582790792Sgshapiro			sm_clrevent(ev);
582838032Speter
5829168515Sgshapiro		memset(&mcibuf, '\0', sizeof(mcibuf));
583038032Speter		mcibuf.mci_mailer = mailer;
583138032Speter		mcibuf.mci_out = f;
583238032Speter		if (bitnset(M_7BITS, mailer->m_flags))
583338032Speter			mcibuf.mci_flags |= MCIF_7BIT;
583438032Speter
583538032Speter		/* clear out per-message flags from connection structure */
583638032Speter		mcibuf.mci_flags &= ~(MCIF_CVT7TO8|MCIF_CVT8TO7);
583738032Speter
583838032Speter		if (bitset(EF_HAS8BIT, e->e_flags) &&
583938032Speter		    !bitset(EF_DONT_MIME, e->e_flags) &&
584038032Speter		    bitnset(M_7BITS, mailer->m_flags))
584138032Speter			mcibuf.mci_flags |= MCIF_CVT8TO7;
584238032Speter
584338032Speter#if MIME7TO8
584438032Speter		if (bitnset(M_MAKE8BIT, mailer->m_flags) &&
584538032Speter		    !bitset(MCIF_7BIT, mcibuf.mci_flags) &&
584638032Speter		    (p = hvalue("Content-Transfer-Encoding", e->e_header)) != NULL &&
584790792Sgshapiro		    (sm_strcasecmp(p, "quoted-printable") == 0 ||
584890792Sgshapiro		     sm_strcasecmp(p, "base64") == 0) &&
584938032Speter		    (p = hvalue("Content-Type", e->e_header)) != NULL)
585038032Speter		{
585138032Speter			/* may want to convert 7 -> 8 */
585238032Speter			/* XXX should really parse it here -- and use a class XXX */
585390792Sgshapiro			if (sm_strncasecmp(p, "text/plain", 10) == 0 &&
585464562Sgshapiro			    (p[10] == '\0' || p[10] == ' ' || p[10] == ';'))
585538032Speter				mcibuf.mci_flags |= MCIF_CVT7TO8;
585638032Speter		}
585764562Sgshapiro#endif /* MIME7TO8 */
585838032Speter
5859157001Sgshapiro		if (!putfromline(&mcibuf, e) ||
5860157001Sgshapiro		    !(*e->e_puthdr)(&mcibuf, e->e_header, e, M87F_OUTER) ||
5861157001Sgshapiro		    !(*e->e_putbody)(&mcibuf, e, NULL) ||
5862157001Sgshapiro		    !putline("\n", &mcibuf) ||
5863157001Sgshapiro		    (sm_io_flush(f, SM_TIME_DEFAULT) != 0 ||
586490792Sgshapiro		    (SuperSafe != SAFE_NO &&
586590792Sgshapiro		     fsync(sm_io_getinfo(f, SM_IO_WHAT_FD, NULL)) < 0) ||
5866157001Sgshapiro		    sm_io_error(f)))
586738032Speter		{
586838032Speter			setstat(EX_IOERR);
586964562Sgshapiro#if !NOFTRUNCATE
587090792Sgshapiro			(void) ftruncate(sm_io_getinfo(f, SM_IO_WHAT_FD, NULL),
587190792Sgshapiro					 curoff);
5872363466Sgshapiro#endif
587338032Speter		}
587438032Speter
587538032Speter		/* reset ISUID & ISGID bits for paranoid systems */
587638032Speter#if HASFCHMOD
587790792Sgshapiro		(void) fchmod(sm_io_getinfo(f, SM_IO_WHAT_FD, NULL),
587890792Sgshapiro			      (MODE_T) mode);
587964562Sgshapiro#else /* HASFCHMOD */
588064562Sgshapiro		(void) chmod(filename, (MODE_T) mode);
588164562Sgshapiro#endif /* HASFCHMOD */
588290792Sgshapiro		if (sm_io_close(f, SM_TIME_DEFAULT) < 0)
588364562Sgshapiro			setstat(EX_IOERR);
588490792Sgshapiro		(void) sm_io_flush(smioout, SM_TIME_DEFAULT);
588564562Sgshapiro		(void) setuid(RealUid);
588638032Speter		exit(ExitStat);
588764562Sgshapiro		/* NOTREACHED */
588838032Speter	}
588938032Speter	else
589038032Speter	{
589138032Speter		/* parent -- wait for exit status */
589238032Speter		int st;
589338032Speter
589438032Speter		st = waitfor(pid);
589538032Speter		if (st == -1)
589638032Speter		{
589738032Speter			syserr("mailfile: %s: wait", mailer->m_name);
589864562Sgshapiro			return EX_SOFTWARE;
589938032Speter		}
590038032Speter		if (WIFEXITED(st))
590190792Sgshapiro		{
590290792Sgshapiro			errno = 0;
590338032Speter			return (WEXITSTATUS(st));
590490792Sgshapiro		}
590538032Speter		else
590638032Speter		{
590738032Speter			syserr("mailfile: %s: child died on signal %d",
590838032Speter			       mailer->m_name, st);
590964562Sgshapiro			return EX_UNAVAILABLE;
591038032Speter		}
591164562Sgshapiro		/* NOTREACHED */
591238032Speter	}
591338032Speter	return EX_UNAVAILABLE;	/* avoid compiler warning on IRIX */
591438032Speter}
591538032Speter
591638032Speterstatic void
5917141858Sgshapiromailfiletimeout(ignore)
5918141858Sgshapiro	int ignore;
591938032Speter{
592077349Sgshapiro	/*
592177349Sgshapiro	**  NOTE: THIS CAN BE CALLED FROM A SIGNAL HANDLER.  DO NOT ADD
592277349Sgshapiro	**	ANYTHING TO THIS ROUTINE UNLESS YOU KNOW WHAT YOU ARE
592377349Sgshapiro	**	DOING.
592477349Sgshapiro	*/
592577349Sgshapiro
592677349Sgshapiro	errno = ETIMEDOUT;
592738032Speter	longjmp(CtxMailfileTimeout, 1);
592838032Speter}
5929363466Sgshapiro
5930363466Sgshapiro#if DANE
5931363466Sgshapiro
593290792Sgshapiro/*
5933363466Sgshapiro**  GETMPORT -- return the port of a mailer
5934363466Sgshapiro**
5935363466Sgshapiro**	Parameters:
5936363466Sgshapiro**		m -- the mailer describing this host.
5937363466Sgshapiro**
5938363466Sgshapiro**	Returns:
5939363466Sgshapiro**		the port of the mailer if defined.
5940363466Sgshapiro**		0 otherwise
5941363466Sgshapiro**		<0 error
5942363466Sgshapiro*/
5943363466Sgshapiro
5944363466Sgshapirostatic int getmport __P((MAILER *));
5945363466Sgshapiro
5946363466Sgshapirostatic int
5947363466Sgshapirogetmport(m)
5948363466Sgshapiro	MAILER *m;
5949363466Sgshapiro{
5950363466Sgshapiro	unsigned long ulval;
5951363466Sgshapiro	char *buf, *ep;
5952363466Sgshapiro
5953363466Sgshapiro	if (m->m_port > 0)
5954363466Sgshapiro		return m->m_port;
5955363466Sgshapiro
5956363466Sgshapiro	if (NULL == m->m_argv[0] ||NULL == m->m_argv[1])
5957363466Sgshapiro		return -1;
5958363466Sgshapiro	buf = m->m_argv[2];
5959363466Sgshapiro	if (NULL == buf)
5960363466Sgshapiro		return 0;
5961363466Sgshapiro
5962363466Sgshapiro	errno = 0;
5963363466Sgshapiro	ulval = strtoul(buf, &ep, 0);
5964363466Sgshapiro	if (buf[0] == '\0' || *ep != '\0')
5965363466Sgshapiro		return -1;
5966363466Sgshapiro	if (errno == ERANGE && ulval == ULONG_MAX)
5967363466Sgshapiro		return -1;
5968363466Sgshapiro	if (ulval > USHRT_MAX)
5969363466Sgshapiro		return -1;
5970363466Sgshapiro	m->m_port = (unsigned short) ulval;
5971363466Sgshapiro	if (tTd(17, 30))
5972363466Sgshapiro		sm_dprintf("getmport: mailer=%s, port=%d\n", m->m_name,
5973363466Sgshapiro			m->m_port);
5974363466Sgshapiro	return m->m_port;
5975363466Sgshapiro}
5976363466Sgshapiro# define GETMPORT(m) getmport(m)
5977363466Sgshapiro#else /* DANE */
5978363466Sgshapiro# define GETMPORT(m)	25
5979363466Sgshapiro#endif /* DANE */
5980363466Sgshapiro
5981363466Sgshapiro/*
598238032Speter**  HOSTSIGNATURE -- return the "signature" for a host.
598338032Speter**
598438032Speter**	The signature describes how we are going to send this -- it
598538032Speter**	can be just the hostname (for non-Internet hosts) or can be
598638032Speter**	an ordered list of MX hosts.
598738032Speter**
598838032Speter**	Parameters:
598938032Speter**		m -- the mailer describing this host.
599038032Speter**		host -- the host name.
5991363466Sgshapiro**		ad -- DNSSEC: ad
599238032Speter**
599338032Speter**	Returns:
599438032Speter**		The signature for this host.
599538032Speter**
599638032Speter**	Side Effects:
599738032Speter**		Can tweak the symbol table.
599838032Speter*/
599990792Sgshapiro
600064562Sgshapiro#define MAXHOSTSIGNATURE	8192	/* max len of hostsignature */
600138032Speter
600290792Sgshapirochar *
6003363466Sgshapirohostsignature(m, host, ad)
600438032Speter	register MAILER *m;
600538032Speter	char *host;
6006363466Sgshapiro	bool ad;
600738032Speter{
600838032Speter	register char *p;
600938032Speter	register STAB *s;
601090792Sgshapiro	time_t now;
601164562Sgshapiro#if NAMED_BIND
601264562Sgshapiro	char sep = ':';
601364562Sgshapiro	char prevsep = ':';
601438032Speter	int i;
601538032Speter	int len;
601638032Speter	int nmx;
601764562Sgshapiro	int hl;
601838032Speter	char *hp;
601938032Speter	char *endp;
602038032Speter	int oldoptions = _res.options;
602138032Speter	char *mxhosts[MAXMXHOSTS + 1];
602290792Sgshapiro	unsigned short mxprefs[MAXMXHOSTS + 1];
602364562Sgshapiro#endif /* NAMED_BIND */
602438032Speter
602564562Sgshapiro	if (tTd(17, 3))
6026363466Sgshapiro		sm_dprintf("hostsignature(%s), ad=%d\n", host, ad);
602764562Sgshapiro
602838032Speter	/*
602977349Sgshapiro	**  If local delivery (and not remote), just return a constant.
603064562Sgshapiro	*/
603164562Sgshapiro
603277349Sgshapiro	if (bitnset(M_LOCALMAILER, m->m_flags) &&
603390792Sgshapiro	    strcmp(m->m_mailer, "[IPC]") != 0 &&
603490792Sgshapiro	    !(m->m_argv[0] != NULL && strcmp(m->m_argv[0], "TCP") == 0))
603564562Sgshapiro		return "localhost";
603664562Sgshapiro
6037147078Sgshapiro	/* an empty host does not have MX records */
6038147078Sgshapiro	if (*host == '\0')
6039147078Sgshapiro		return "_empty_";
6040147078Sgshapiro
604164562Sgshapiro	/*
604238032Speter	**  Check to see if this uses IPC -- if not, it can't have MX records.
604338032Speter	*/
604438032Speter
604590792Sgshapiro	if (strcmp(m->m_mailer, "[IPC]") != 0 ||
604690792Sgshapiro	    CurEnv->e_sendmode == SM_DEFER)
604738032Speter	{
604890792Sgshapiro		/* just an ordinary mailer or deferred mode */
604938032Speter		return host;
605038032Speter	}
605164562Sgshapiro#if NETUNIX
605264562Sgshapiro	else if (m->m_argv[0] != NULL &&
605364562Sgshapiro		 strcmp(m->m_argv[0], "FILE") == 0)
605464562Sgshapiro	{
605564562Sgshapiro		/* rendezvous in the file system, no MX records */
605664562Sgshapiro		return host;
605764562Sgshapiro	}
605864562Sgshapiro#endif /* NETUNIX */
605938032Speter
606038032Speter	/*
606138032Speter	**  Look it up in the symbol table.
606238032Speter	*/
606338032Speter
606490792Sgshapiro	now = curtime();
606538032Speter	s = stab(host, ST_HOSTSIG, ST_ENTER);
606690792Sgshapiro	if (s->s_hostsig.hs_sig != NULL)
606764562Sgshapiro	{
606890792Sgshapiro		if (s->s_hostsig.hs_exp >= now)
606990792Sgshapiro		{
607090792Sgshapiro			if (tTd(17, 3))
607190792Sgshapiro				sm_dprintf("hostsignature(): stab(%s) found %s\n", host,
607290792Sgshapiro					   s->s_hostsig.hs_sig);
607390792Sgshapiro			return s->s_hostsig.hs_sig;
607490792Sgshapiro		}
607590792Sgshapiro
607690792Sgshapiro		/* signature is expired: clear it */
607790792Sgshapiro		sm_free(s->s_hostsig.hs_sig);
607890792Sgshapiro		s->s_hostsig.hs_sig = NULL;
607964562Sgshapiro	}
608038032Speter
608190792Sgshapiro	/* set default TTL */
608290792Sgshapiro	s->s_hostsig.hs_exp = now + SM_DEFAULT_TTL;
608390792Sgshapiro
608438032Speter	/*
608590792Sgshapiro	**  Not already there or expired -- create a signature.
608638032Speter	*/
608738032Speter
608838032Speter#if NAMED_BIND
608938032Speter	if (ConfigLevel < 2)
609038032Speter		_res.options &= ~(RES_DEFNAMES | RES_DNSRCH);	/* XXX */
609138032Speter
609238032Speter	for (hp = host; hp != NULL; hp = endp)
609338032Speter	{
6094363466Sgshapiro# if NETINET6
609564562Sgshapiro		if (*hp == '[')
609664562Sgshapiro		{
609764562Sgshapiro			endp = strchr(hp + 1, ']');
609864562Sgshapiro			if (endp != NULL)
609964562Sgshapiro				endp = strpbrk(endp + 1, ":,");
610064562Sgshapiro		}
610164562Sgshapiro		else
610264562Sgshapiro			endp = strpbrk(hp, ":,");
6103363466Sgshapiro# else /* NETINET6 */
610464562Sgshapiro		endp = strpbrk(hp, ":,");
6105363466Sgshapiro# endif /* NETINET6 */
610638032Speter		if (endp != NULL)
610764562Sgshapiro		{
610864562Sgshapiro			sep = *endp;
610938032Speter			*endp = '\0';
611064562Sgshapiro		}
611138032Speter
611238032Speter		if (bitnset(M_NOMX, m->m_flags))
611338032Speter		{
611438032Speter			/* skip MX lookups */
611538032Speter			nmx = 1;
611638032Speter			mxhosts[0] = hp;
611738032Speter		}
611838032Speter		else
611938032Speter		{
612038032Speter			auto int rcode;
612190792Sgshapiro			int ttl;
612238032Speter
6123363466Sgshapiro			GETMPORT(m);
6124363466Sgshapiro			nmx = getmxrr(hp, mxhosts, mxprefs,
6125363466Sgshapiro				      DROPLOCALHOST|TRYFALLBACK|(ad ? ISAD :0),
6126363466Sgshapiro				      &rcode, &ttl, M_PORT(m));
612738032Speter			if (nmx <= 0)
612838032Speter			{
612980785Sgshapiro				int save_errno;
613038032Speter				register MCI *mci;
613138032Speter
613238032Speter				/* update the connection info for this host */
613380785Sgshapiro				save_errno = errno;
613438032Speter				mci = mci_get(hp, m);
613580785Sgshapiro				mci->mci_errno = save_errno;
613638032Speter				mci->mci_herrno = h_errno;
613771345Sgshapiro				mci->mci_lastuse = now;
6138363466Sgshapiro				if (nmx == NULLMX)
6139363466Sgshapiro					mci_setstat(mci, rcode, "5.7.27",
6140363466Sgshapiro						    "550 Host does not accept mail");
6141363466Sgshapiro				else if (rcode == EX_NOHOST)
614264562Sgshapiro					mci_setstat(mci, rcode, "5.1.2",
614390792Sgshapiro						    "550 Host unknown");
614464562Sgshapiro				else
614564562Sgshapiro					mci_setstat(mci, rcode, NULL, NULL);
614638032Speter
614738032Speter				/* use the original host name as signature */
614838032Speter				nmx = 1;
614938032Speter				mxhosts[0] = hp;
615038032Speter			}
615164562Sgshapiro			if (tTd(17, 3))
615290792Sgshapiro				sm_dprintf("hostsignature(): getmxrr() returned %d, mxhosts[0]=%s\n",
615390792Sgshapiro					   nmx, mxhosts[0]);
615490792Sgshapiro
615590792Sgshapiro			/*
615690792Sgshapiro			**  Set new TTL: we use only one!
615790792Sgshapiro			**	We could try to use the minimum instead.
615890792Sgshapiro			*/
615990792Sgshapiro
616090792Sgshapiro			s->s_hostsig.hs_exp = now + SM_MIN(ttl, SM_DEFAULT_TTL);
616138032Speter		}
616238032Speter
616338032Speter		len = 0;
616438032Speter		for (i = 0; i < nmx; i++)
616538032Speter			len += strlen(mxhosts[i]) + 1;
616690792Sgshapiro		if (s->s_hostsig.hs_sig != NULL)
616790792Sgshapiro			len += strlen(s->s_hostsig.hs_sig) + 1;
616890792Sgshapiro		if (len < 0 || len >= MAXHOSTSIGNATURE)
616964562Sgshapiro		{
617064562Sgshapiro			sm_syslog(LOG_WARNING, NOQID, "hostsignature for host '%s' exceeds maxlen (%d): %d",
617164562Sgshapiro				  host, MAXHOSTSIGNATURE, len);
617264562Sgshapiro			len = MAXHOSTSIGNATURE;
617364562Sgshapiro		}
617490792Sgshapiro		p = sm_pmalloc_x(len);
617590792Sgshapiro		if (s->s_hostsig.hs_sig != NULL)
617638032Speter		{
617790792Sgshapiro			(void) sm_strlcpy(p, s->s_hostsig.hs_sig, len);
617890792Sgshapiro			sm_free(s->s_hostsig.hs_sig); /* XXX */
617990792Sgshapiro			s->s_hostsig.hs_sig = p;
618064562Sgshapiro			hl = strlen(p);
618164562Sgshapiro			p += hl;
618264562Sgshapiro			*p++ = prevsep;
618364562Sgshapiro			len -= hl + 1;
618438032Speter		}
618538032Speter		else
618690792Sgshapiro			s->s_hostsig.hs_sig = p;
618738032Speter		for (i = 0; i < nmx; i++)
618838032Speter		{
618964562Sgshapiro			hl = strlen(mxhosts[i]);
619064562Sgshapiro			if (len - 1 < hl || len <= 1)
619164562Sgshapiro			{
619264562Sgshapiro				/* force to drop out of outer loop */
619364562Sgshapiro				len = -1;
619464562Sgshapiro				break;
619564562Sgshapiro			}
619638032Speter			if (i != 0)
619764562Sgshapiro			{
619864562Sgshapiro				if (mxprefs[i] == mxprefs[i - 1])
619964562Sgshapiro					*p++ = ',';
620064562Sgshapiro				else
620164562Sgshapiro					*p++ = ':';
620264562Sgshapiro				len--;
620364562Sgshapiro			}
620490792Sgshapiro			(void) sm_strlcpy(p, mxhosts[i], len);
620564562Sgshapiro			p += hl;
620664562Sgshapiro			len -= hl;
620738032Speter		}
620864562Sgshapiro
620964562Sgshapiro		/*
621064562Sgshapiro		**  break out of loop if len exceeded MAXHOSTSIGNATURE
621164562Sgshapiro		**  because we won't have more space for further hosts
621264562Sgshapiro		**  anyway (separated by : in the .cf file).
621364562Sgshapiro		*/
621464562Sgshapiro
621564562Sgshapiro		if (len < 0)
621664562Sgshapiro			break;
621738032Speter		if (endp != NULL)
621864562Sgshapiro			*endp++ = sep;
621964562Sgshapiro		prevsep = sep;
622038032Speter	}
622190792Sgshapiro	makelower(s->s_hostsig.hs_sig);
622238032Speter	if (ConfigLevel < 2)
622338032Speter		_res.options = oldoptions;
622464562Sgshapiro#else /* NAMED_BIND */
622538032Speter	/* not using BIND -- the signature is just the host name */
622690792Sgshapiro	/*
622790792Sgshapiro	**  'host' points to storage that will be freed after we are
622890792Sgshapiro	**  done processing the current envelope, so we copy it.
622990792Sgshapiro	*/
623090792Sgshapiro	s->s_hostsig.hs_sig = sm_pstrdup_x(host);
623164562Sgshapiro#endif /* NAMED_BIND */
623238032Speter	if (tTd(17, 1))
623390792Sgshapiro		sm_dprintf("hostsignature(%s) = %s\n", host, s->s_hostsig.hs_sig);
623490792Sgshapiro	return s->s_hostsig.hs_sig;
623538032Speter}
623690792Sgshapiro/*
623764562Sgshapiro**  PARSE_HOSTSIGNATURE -- parse the "signature" and return MX host array.
623864562Sgshapiro**
623964562Sgshapiro**	The signature describes how we are going to send this -- it
624064562Sgshapiro**	can be just the hostname (for non-Internet hosts) or can be
624164562Sgshapiro**	an ordered list of MX hosts which must be randomized for equal
624264562Sgshapiro**	MX preference values.
624364562Sgshapiro**
624464562Sgshapiro**	Parameters:
624564562Sgshapiro**		sig -- the host signature.
624664562Sgshapiro**		mxhosts -- array to populate.
624790792Sgshapiro**		mailer -- mailer.
624864562Sgshapiro**
624964562Sgshapiro**	Returns:
625064562Sgshapiro**		The number of hosts inserted into mxhosts array.
625164562Sgshapiro**
625264562Sgshapiro**	Side Effects:
625364562Sgshapiro**		Randomizes equal MX preference hosts in mxhosts.
625464562Sgshapiro*/
625564562Sgshapiro
625664562Sgshapirostatic int
625764562Sgshapiroparse_hostsignature(sig, mxhosts, mailer)
625864562Sgshapiro	char *sig;
625964562Sgshapiro	char **mxhosts;
626064562Sgshapiro	MAILER *mailer;
626164562Sgshapiro{
626290792Sgshapiro	unsigned short curpref = 0;
626390792Sgshapiro	int nmx = 0, i, j;	/* NOTE: i, j, and nmx must have same type */
626464562Sgshapiro	char *hp, *endp;
626590792Sgshapiro	unsigned short prefer[MAXMXHOSTS];
626664562Sgshapiro	long rndm[MAXMXHOSTS];
626764562Sgshapiro
626864562Sgshapiro	for (hp = sig; hp != NULL; hp = endp)
626964562Sgshapiro	{
627064562Sgshapiro		char sep = ':';
627164562Sgshapiro
627264562Sgshapiro#if NETINET6
627364562Sgshapiro		if (*hp == '[')
627464562Sgshapiro		{
627564562Sgshapiro			endp = strchr(hp + 1, ']');
627664562Sgshapiro			if (endp != NULL)
627764562Sgshapiro				endp = strpbrk(endp + 1, ":,");
627864562Sgshapiro		}
627964562Sgshapiro		else
628064562Sgshapiro			endp = strpbrk(hp, ":,");
628164562Sgshapiro#else /* NETINET6 */
628264562Sgshapiro		endp = strpbrk(hp, ":,");
628364562Sgshapiro#endif /* NETINET6 */
628464562Sgshapiro		if (endp != NULL)
628564562Sgshapiro		{
628664562Sgshapiro			sep = *endp;
628764562Sgshapiro			*endp = '\0';
628864562Sgshapiro		}
628964562Sgshapiro
629064562Sgshapiro		mxhosts[nmx] = hp;
629164562Sgshapiro		prefer[nmx] = curpref;
629264562Sgshapiro		if (mci_match(hp, mailer))
629364562Sgshapiro			rndm[nmx] = 0;
629464562Sgshapiro		else
629564562Sgshapiro			rndm[nmx] = get_random();
629664562Sgshapiro
629764562Sgshapiro		if (endp != NULL)
629864562Sgshapiro		{
629964562Sgshapiro			/*
630064562Sgshapiro			**  Since we don't have the original MX prefs,
630164562Sgshapiro			**  make our own.  If the separator is a ':', that
630264562Sgshapiro			**  means the preference for the next host will be
630364562Sgshapiro			**  higher than this one, so simply increment curpref.
630464562Sgshapiro			*/
630564562Sgshapiro
630664562Sgshapiro			if (sep == ':')
630764562Sgshapiro				curpref++;
630864562Sgshapiro
630964562Sgshapiro			*endp++ = sep;
631064562Sgshapiro		}
631164562Sgshapiro		if (++nmx >= MAXMXHOSTS)
631264562Sgshapiro			break;
631364562Sgshapiro	}
631464562Sgshapiro
631564562Sgshapiro	/* sort the records using the random factor for equal preferences */
631664562Sgshapiro	for (i = 0; i < nmx; i++)
631764562Sgshapiro	{
631864562Sgshapiro		for (j = i + 1; j < nmx; j++)
631964562Sgshapiro		{
632064562Sgshapiro			/*
632164562Sgshapiro			**  List is already sorted by MX preference, only
632264562Sgshapiro			**  need to look for equal preference MX records
632364562Sgshapiro			*/
632464562Sgshapiro
632564562Sgshapiro			if (prefer[i] < prefer[j])
632664562Sgshapiro				break;
632764562Sgshapiro
632864562Sgshapiro			if (prefer[i] > prefer[j] ||
632964562Sgshapiro			    (prefer[i] == prefer[j] && rndm[i] > rndm[j]))
633064562Sgshapiro			{
633190792Sgshapiro				register unsigned short tempp;
633264562Sgshapiro				register long tempr;
633364562Sgshapiro				register char *temp1;
633464562Sgshapiro
633564562Sgshapiro				tempp = prefer[i];
633664562Sgshapiro				prefer[i] = prefer[j];
633764562Sgshapiro				prefer[j] = tempp;
633864562Sgshapiro				temp1 = mxhosts[i];
633964562Sgshapiro				mxhosts[i] = mxhosts[j];
634064562Sgshapiro				mxhosts[j] = temp1;
634164562Sgshapiro				tempr = rndm[i];
634264562Sgshapiro				rndm[i] = rndm[j];
634364562Sgshapiro				rndm[j] = tempr;
634464562Sgshapiro			}
634564562Sgshapiro		}
634664562Sgshapiro	}
634764562Sgshapiro	return nmx;
634864562Sgshapiro}
634964562Sgshapiro
6350363466Sgshapiro#if STARTTLS
635164562Sgshapirostatic SSL_CTX	*clt_ctx = NULL;
635290792Sgshapirostatic bool	tls_ok_clt = true;
635364562Sgshapiro
635490792Sgshapiro/*
635590792Sgshapiro**  SETCLTTLS -- client side TLS: allow/disallow.
635664562Sgshapiro**
635764562Sgshapiro**	Parameters:
635890792Sgshapiro**		tls_ok -- should tls be done?
635990792Sgshapiro**
636090792Sgshapiro**	Returns:
636164562Sgshapiro**		none.
636264562Sgshapiro**
636390792Sgshapiro**	Side Effects:
636490792Sgshapiro**		sets tls_ok_clt (static variable in this module)
636590792Sgshapiro*/
636690792Sgshapiro
636790792Sgshapirovoid
636890792Sgshapirosetclttls(tls_ok)
636990792Sgshapiro	bool tls_ok;
637090792Sgshapiro{
637190792Sgshapiro	tls_ok_clt = tls_ok;
637290792Sgshapiro	return;
637390792Sgshapiro}
637490792Sgshapiro/*
637590792Sgshapiro**  INITCLTTLS -- initialize client side TLS
637690792Sgshapiro**
637790792Sgshapiro**	Parameters:
637890792Sgshapiro**		tls_ok -- should tls initialization be done?
637990792Sgshapiro**
638064562Sgshapiro**	Returns:
638164562Sgshapiro**		succeeded?
638290792Sgshapiro**
638390792Sgshapiro**	Side Effects:
638490792Sgshapiro**		sets tls_ok_clt (static variable in this module)
638564562Sgshapiro*/
638664562Sgshapiro
638764562Sgshapirobool
638890792Sgshapiroinitclttls(tls_ok)
638990792Sgshapiro	bool tls_ok;
639064562Sgshapiro{
639190792Sgshapiro	if (!tls_ok_clt)
639290792Sgshapiro		return false;
639390792Sgshapiro	tls_ok_clt = tls_ok;
639490792Sgshapiro	if (!tls_ok_clt)
639590792Sgshapiro		return false;
639664562Sgshapiro	if (clt_ctx != NULL)
639790792Sgshapiro		return true;	/* already done */
6398203004Sgshapiro	tls_ok_clt = inittls(&clt_ctx, TLS_I_CLT, Clt_SSL_Options, false,
6399363466Sgshapiro			CltCertFile, CltKeyFile,
6400363466Sgshapiro# if _FFR_CLIENTCA
6401363466Sgshapiro			(CltCACertPath != NULL) ? CltCACertPath :
6402363466Sgshapiro# endif
6403363466Sgshapiro				CACertPath,
6404363466Sgshapiro# if _FFR_CLIENTCA
6405363466Sgshapiro			(CltCACertFile != NULL) ? CltCACertFile :
6406363466Sgshapiro# endif
6407363466Sgshapiro				CACertFile,
6408363466Sgshapiro			DHParams);
640990792Sgshapiro	return tls_ok_clt;
641064562Sgshapiro}
641164562Sgshapiro
641290792Sgshapiro/*
641364562Sgshapiro**  STARTTLS -- try to start secure connection (client side)
641464562Sgshapiro**
641564562Sgshapiro**	Parameters:
641664562Sgshapiro**		m -- the mailer.
641764562Sgshapiro**		mci -- the mailer connection info.
641864562Sgshapiro**		e -- the envelope.
641964562Sgshapiro**
642064562Sgshapiro**	Returns:
642164562Sgshapiro**		success?
642264562Sgshapiro**		(maybe this should be some other code than EX_
642364562Sgshapiro**		that denotes which stage failed.)
642464562Sgshapiro*/
642564562Sgshapiro
642664562Sgshapirostatic int
6427363466Sgshapirostarttls(m, mci, e
6428363466Sgshapiro# if DANE
6429363466Sgshapiro	, dane_vrfy_ctx
6430363466Sgshapiro# endif
6431363466Sgshapiro	)
643264562Sgshapiro	MAILER *m;
643364562Sgshapiro	MCI *mci;
643464562Sgshapiro	ENVELOPE *e;
6435363466Sgshapiro# if DANE
6436363466Sgshapiro	dane_vrfy_ctx_P	dane_vrfy_ctx;
6437363466Sgshapiro# endif
643864562Sgshapiro{
643964562Sgshapiro	int smtpresult;
644066494Sgshapiro	int result = 0;
644166494Sgshapiro	int rfd, wfd;
644264562Sgshapiro	SSL *clt_ssl = NULL;
644390792Sgshapiro	time_t tlsstart;
6444363466Sgshapiro	extern int TLSsslidx;
644564562Sgshapiro
644690792Sgshapiro	if (clt_ctx == NULL && !initclttls(true))
644766494Sgshapiro		return EX_TEMPFAIL;
6448203004Sgshapiro
6449363466Sgshapiro	if (!TLS_set_engine(SSLEngine, false))
6450203004Sgshapiro	{
6451203004Sgshapiro		sm_syslog(LOG_ERR, NOQID,
6452363466Sgshapiro			  "STARTTLS=client, engine=%s, TLS_set_engine=failed",
6453363466Sgshapiro			  SSLEngine);
6454203004Sgshapiro		return EX_TEMPFAIL;
6455203004Sgshapiro	}
6456203004Sgshapiro
645764562Sgshapiro	smtpmessage("STARTTLS", m, mci);
645864562Sgshapiro
645964562Sgshapiro	/* get the reply */
6460132943Sgshapiro	smtpresult = reply(m, mci, e, TimeOuts.to_starttls, NULL, NULL,
6461132943Sgshapiro			XS_STARTTLS);
646264562Sgshapiro
646364562Sgshapiro	/* check return code from server */
6464157001Sgshapiro	if (REPLYTYPE(smtpresult) == 4)
646564562Sgshapiro		return EX_TEMPFAIL;
646664562Sgshapiro	if (smtpresult == 501)
646764562Sgshapiro		return EX_USAGE;
646864562Sgshapiro	if (smtpresult == -1)
646964562Sgshapiro		return smtpresult;
6470157001Sgshapiro
6471157001Sgshapiro	/* not an expected reply but we have to deal with it */
6472157001Sgshapiro	if (REPLYTYPE(smtpresult) == 5)
6473157001Sgshapiro		return EX_UNAVAILABLE;
647464562Sgshapiro	if (smtpresult != 220)
647564562Sgshapiro		return EX_PROTOCOL;
647664562Sgshapiro
647764562Sgshapiro	if (LogLevel > 13)
647890792Sgshapiro		sm_syslog(LOG_INFO, NOQID, "STARTTLS=client, start=ok");
647964562Sgshapiro
648064562Sgshapiro	/* start connection */
648164562Sgshapiro	if ((clt_ssl = SSL_new(clt_ctx)) == NULL)
648264562Sgshapiro	{
648364562Sgshapiro		if (LogLevel > 5)
648464562Sgshapiro		{
648590792Sgshapiro			sm_syslog(LOG_ERR, NOQID,
648690792Sgshapiro				  "STARTTLS=client, error: SSL_new failed");
6487363466Sgshapiro			tlslogerr(LOG_WARNING, 9, "client");
648864562Sgshapiro		}
648964562Sgshapiro		return EX_SOFTWARE;
649064562Sgshapiro	}
6491285229Sgshapiro	/* SSL_clear(clt_ssl); ? */
649264562Sgshapiro
6493363466Sgshapiro	if (get_tls_se_options(e, clt_ssl, &mci->mci_tlsi, false) != 0)
6494285229Sgshapiro	{
6495285229Sgshapiro		sm_syslog(LOG_ERR, NOQID,
6496285229Sgshapiro			  "STARTTLS=client, get_tls_se_options=fail");
6497285229Sgshapiro		return EX_SOFTWARE;
6498285229Sgshapiro	}
6499363466Sgshapiro	result = SSL_set_ex_data(clt_ssl, TLSsslidx, &mci->mci_tlsi);
6500363466Sgshapiro	if (0 == result)
6501363466Sgshapiro	{
6502363466Sgshapiro		if (LogLevel > 5)
6503363466Sgshapiro		{
6504363466Sgshapiro			sm_syslog(LOG_ERR, NOQID,
6505363466Sgshapiro				  "STARTTLS=client, error: SSL_set_ex_data failed=%d, idx=%d",
6506363466Sgshapiro				  result, TLSsslidx);
6507363466Sgshapiro			tlslogerr(LOG_WARNING, 9, "client");
6508363466Sgshapiro		}
6509363466Sgshapiro		return EX_SOFTWARE;
6510363466Sgshapiro	}
6511363466Sgshapiro# if DANE
6512363466Sgshapiro	if (SM_TLSI_IS(&(mci->mci_tlsi), TLSI_FL_NODANE))
6513363466Sgshapiro		dane_vrfy_ctx->dane_vrfy_chk = DANE_NEVER;
6514363466Sgshapiro	else
6515363466Sgshapiro	{
6516363466Sgshapiro		int r;
6517285229Sgshapiro
6518363466Sgshapiro#  define SM_IS_EMPTY(s)	(NULL == (s) || '\0' == *(s))
6519363466Sgshapiro
6520363466Sgshapiro		/* set SNI only if there is a TLSA RR */
6521363466Sgshapiro		if (dane_get_tlsa(dane_vrfy_ctx) != NULL &&
6522363466Sgshapiro		    !(SM_IS_EMPTY(dane_vrfy_ctx->dane_vrfy_host) &&
6523363466Sgshapiro		      SM_IS_EMPTY(dane_vrfy_ctx->dane_vrfy_sni)) &&
6524363466Sgshapiro		    (r = SSL_set_tlsext_host_name(clt_ssl,
6525363466Sgshapiro				(!SM_IS_EMPTY(dane_vrfy_ctx->dane_vrfy_sni)
6526363466Sgshapiro				? dane_vrfy_ctx->dane_vrfy_sni
6527363466Sgshapiro				: dane_vrfy_ctx->dane_vrfy_host))) <= 0)
6528363466Sgshapiro		{
6529363466Sgshapiro			if (LogLevel > 5)
6530363466Sgshapiro			{
6531363466Sgshapiro				sm_syslog(LOG_ERR, NOQID,
6532363466Sgshapiro					  "STARTTLS=client, host=%s, SSL_set_tlsext_host_name=%d",
6533363466Sgshapiro					  dane_vrfy_ctx->dane_vrfy_host, r);
6534363466Sgshapiro			}
6535363466Sgshapiro			tlslogerr(LOG_ERR, 5, "client");
6536363466Sgshapiro			/* return EX_SOFTWARE; */
6537363466Sgshapiro		}
6538363466Sgshapiro	}
6539363466Sgshapiro	memcpy(&mci->mci_tlsi.tlsi_dvc, dane_vrfy_ctx, sizeof(*dane_vrfy_ctx));
6540363466Sgshapiro# endif /* DANE */
6541363466Sgshapiro
654290792Sgshapiro	rfd = sm_io_getinfo(mci->mci_in, SM_IO_WHAT_FD, NULL);
654390792Sgshapiro	wfd = sm_io_getinfo(mci->mci_out, SM_IO_WHAT_FD, NULL);
654466494Sgshapiro
654566494Sgshapiro	if (rfd < 0 || wfd < 0 ||
654690792Sgshapiro	    (result = SSL_set_rfd(clt_ssl, rfd)) != 1 ||
654790792Sgshapiro	    (result = SSL_set_wfd(clt_ssl, wfd)) != 1)
654864562Sgshapiro	{
654964562Sgshapiro		if (LogLevel > 5)
655064562Sgshapiro		{
655190792Sgshapiro			sm_syslog(LOG_ERR, NOQID,
655290792Sgshapiro				  "STARTTLS=client, error: SSL_set_xfd failed=%d",
655390792Sgshapiro				  result);
6554363466Sgshapiro			tlslogerr(LOG_WARNING, 9, "client");
655564562Sgshapiro		}
655664562Sgshapiro		return EX_SOFTWARE;
655764562Sgshapiro	}
655864562Sgshapiro	SSL_set_connect_state(clt_ssl);
655990792Sgshapiro	tlsstart = curtime();
656090792Sgshapiro
656190792Sgshapirossl_retry:
656264562Sgshapiro	if ((result = SSL_connect(clt_ssl)) <= 0)
656364562Sgshapiro	{
6564157001Sgshapiro		int i, ssl_err;
6565285229Sgshapiro		int save_errno = errno;
656664562Sgshapiro
6567157001Sgshapiro		ssl_err = SSL_get_error(clt_ssl, result);
6568157001Sgshapiro		i = tls_retry(clt_ssl, rfd, wfd, tlsstart,
6569157001Sgshapiro			TimeOuts.to_starttls, ssl_err, "client");
6570157001Sgshapiro		if (i > 0)
6571157001Sgshapiro			goto ssl_retry;
657290792Sgshapiro
6573157001Sgshapiro		if (LogLevel > 5)
657490792Sgshapiro		{
6575244833Sgshapiro			unsigned long l;
6576244833Sgshapiro			const char *sr;
6577244833Sgshapiro
6578244833Sgshapiro			l = ERR_peek_error();
6579244833Sgshapiro			sr = ERR_reason_error_string(l);
6580157001Sgshapiro			sm_syslog(LOG_WARNING, NOQID,
6581244833Sgshapiro				  "STARTTLS=client, error: connect failed=%d, reason=%s, SSL_error=%d, errno=%d, retry=%d",
6582244833Sgshapiro				  result, sr == NULL ? "unknown" : sr, ssl_err,
6583285229Sgshapiro				  save_errno, i);
6584363466Sgshapiro			tlslogerr(LOG_WARNING, 9, "client");
6585110560Sgshapiro		}
658690792Sgshapiro
6587363466Sgshapiro		SM_SSL_FREE(clt_ssl);
658864562Sgshapiro		return EX_SOFTWARE;
658964562Sgshapiro	}
659064562Sgshapiro	mci->mci_ssl = clt_ssl;
659190792Sgshapiro	result = tls_get_info(mci->mci_ssl, false, mci->mci_host,
659290792Sgshapiro			      &mci->mci_macro, true);
659364562Sgshapiro
659490792Sgshapiro	/* switch to use TLS... */
659564562Sgshapiro	if (sfdctls(&mci->mci_in, &mci->mci_out, mci->mci_ssl) == 0)
659664562Sgshapiro		return EX_OK;
659764562Sgshapiro
659864562Sgshapiro	/* failure */
6599363466Sgshapiro	SM_SSL_FREE(clt_ssl);
660064562Sgshapiro	return EX_SOFTWARE;
660164562Sgshapiro}
660290792Sgshapiro/*
660364562Sgshapiro**  ENDTLSCLT -- shutdown secure connection (client side)
660464562Sgshapiro**
660564562Sgshapiro**	Parameters:
660664562Sgshapiro**		mci -- the mailer connection info.
660764562Sgshapiro**
660864562Sgshapiro**	Returns:
660964562Sgshapiro**		success?
661064562Sgshapiro*/
661190792Sgshapiro
661290792Sgshapirostatic int
661364562Sgshapiroendtlsclt(mci)
661464562Sgshapiro	MCI *mci;
661564562Sgshapiro{
661664562Sgshapiro	int r;
661764562Sgshapiro
661864562Sgshapiro	if (!bitset(MCIF_TLSACT, mci->mci_flags))
661964562Sgshapiro		return EX_OK;
6620363466Sgshapiro	r = endtls(&mci->mci_ssl, "client");
662164562Sgshapiro	mci->mci_flags &= ~MCIF_TLSACT;
662264562Sgshapiro	return r;
662364562Sgshapiro}
6624363466Sgshapiro#endif /* STARTTLS */
6625363466Sgshapiro#if STARTTLS || SASL
662690792Sgshapiro/*
662790792Sgshapiro**  ISCLTFLGSET -- check whether client flag is set.
662864562Sgshapiro**
662964562Sgshapiro**	Parameters:
663090792Sgshapiro**		e -- envelope.
663190792Sgshapiro**		flag -- flag to check in {client_flags}
663264562Sgshapiro**
663364562Sgshapiro**	Returns:
663490792Sgshapiro**		true iff flag is set.
663564562Sgshapiro*/
663664562Sgshapiro
663790792Sgshapirostatic bool
663890792Sgshapiroiscltflgset(e, flag)
663990792Sgshapiro	ENVELOPE *e;
664090792Sgshapiro	int flag;
664164562Sgshapiro{
664290792Sgshapiro	char *p;
664373188Sgshapiro
664490792Sgshapiro	p = macvalue(macid("{client_flags}"), e);
664590792Sgshapiro	if (p == NULL)
664690792Sgshapiro		return false;
664790792Sgshapiro	for (; *p != '\0'; p++)
664864562Sgshapiro	{
664990792Sgshapiro		/* look for just this one flag */
665090792Sgshapiro		if (*p == (char) flag)
665190792Sgshapiro			return true;
665264562Sgshapiro	}
665390792Sgshapiro	return false;
665464562Sgshapiro}
6655363466Sgshapiro#endif /* STARTTLS || SASL */
6656