xref: /freebsd/contrib/sendmail/src/deliver.c (revision 94c01205742119acdf56dbbd22fc274e5e38cfd4)
1c2aa98e2SPeter Wemm /*
240266059SGregory Neil Shapiro  * Copyright (c) 1998-2002 Sendmail, Inc. and its suppliers.
306f25ae9SGregory Neil Shapiro  *	All rights reserved.
4c2aa98e2SPeter Wemm  * Copyright (c) 1983, 1995-1997 Eric P. Allman.  All rights reserved.
5c2aa98e2SPeter Wemm  * Copyright (c) 1988, 1993
6c2aa98e2SPeter Wemm  *	The Regents of the University of California.  All rights reserved.
7c2aa98e2SPeter Wemm  *
8c2aa98e2SPeter Wemm  * By using this file, you agree to the terms and conditions set
9c2aa98e2SPeter Wemm  * forth in the LICENSE file which can be found at the top level of
10c2aa98e2SPeter Wemm  * the sendmail distribution.
11c2aa98e2SPeter Wemm  *
12c2aa98e2SPeter Wemm  */
13c2aa98e2SPeter Wemm 
1406f25ae9SGregory Neil Shapiro #include <sendmail.h>
1540266059SGregory Neil Shapiro #include <sys/time.h>
1606f25ae9SGregory Neil Shapiro 
1794c01205SGregory Neil Shapiro SM_RCSID("@(#)$Id: deliver.c,v 8.939 2002/05/25 00:46:00 gshapiro Exp $")
18c2aa98e2SPeter Wemm 
19c2aa98e2SPeter Wemm #if HASSETUSERCONTEXT
20c2aa98e2SPeter Wemm # include <login_cap.h>
2106f25ae9SGregory Neil Shapiro #endif /* HASSETUSERCONTEXT */
2206f25ae9SGregory Neil Shapiro 
23605302a5SGregory Neil Shapiro #if NETINET || NETINET6
24605302a5SGregory Neil Shapiro # include <arpa/inet.h>
25605302a5SGregory Neil Shapiro #endif /* NETINET || NETINET6 */
26605302a5SGregory Neil Shapiro 
2740266059SGregory Neil Shapiro #if STARTTLS || SASL
2806f25ae9SGregory Neil Shapiro # include "sfsasl.h"
2940266059SGregory Neil Shapiro #endif /* STARTTLS || SASL */
3006f25ae9SGregory Neil Shapiro 
3140266059SGregory Neil Shapiro void		markfailure __P((ENVELOPE *, ADDRESS *, MCI *, int, bool));
3206f25ae9SGregory Neil Shapiro static int	deliver __P((ENVELOPE *, ADDRESS *));
3306f25ae9SGregory Neil Shapiro static void	dup_queue_file __P((ENVELOPE *, ENVELOPE *, int));
3406f25ae9SGregory Neil Shapiro static void	mailfiletimeout __P((void));
3506f25ae9SGregory Neil Shapiro static int	parse_hostsignature __P((char *, char **, MAILER *));
3606f25ae9SGregory Neil Shapiro static void	sendenvelope __P((ENVELOPE *, int));
3740266059SGregory Neil Shapiro extern MCI	*mci_new __P((SM_RPOOL_T *));
3840266059SGregory Neil Shapiro static int	coloncmp __P((const char *, const char *));
39c2aa98e2SPeter Wemm 
4006f25ae9SGregory Neil Shapiro #if STARTTLS
4106f25ae9SGregory Neil Shapiro static int	starttls __P((MAILER *, MCI *, ENVELOPE *));
4240266059SGregory Neil Shapiro static int	endtlsclt __P((MCI *));
4306f25ae9SGregory Neil Shapiro #endif /* STARTTLS */
4440266059SGregory Neil Shapiro # if STARTTLS || SASL
4540266059SGregory Neil Shapiro static bool	iscltflgset __P((ENVELOPE *, int));
4640266059SGregory Neil Shapiro # endif /* STARTTLS || SASL */
47c2aa98e2SPeter Wemm 
48c2aa98e2SPeter Wemm /*
49c2aa98e2SPeter Wemm **  SENDALL -- actually send all the messages.
50c2aa98e2SPeter Wemm **
51c2aa98e2SPeter Wemm **	Parameters:
52c2aa98e2SPeter Wemm **		e -- the envelope to send.
53c2aa98e2SPeter Wemm **		mode -- the delivery mode to use.  If SM_DEFAULT, use
54c2aa98e2SPeter Wemm **			the current e->e_sendmode.
55c2aa98e2SPeter Wemm **
56c2aa98e2SPeter Wemm **	Returns:
57c2aa98e2SPeter Wemm **		none.
58c2aa98e2SPeter Wemm **
59c2aa98e2SPeter Wemm **	Side Effects:
60c2aa98e2SPeter Wemm **		Scans the send lists and sends everything it finds.
61c2aa98e2SPeter Wemm **		Delivers any appropriate error messages.
62c2aa98e2SPeter Wemm **		If we are running in a non-interactive mode, takes the
63c2aa98e2SPeter Wemm **			appropriate action.
64c2aa98e2SPeter Wemm */
65c2aa98e2SPeter Wemm 
66c2aa98e2SPeter Wemm void
67c2aa98e2SPeter Wemm sendall(e, mode)
68c2aa98e2SPeter Wemm 	ENVELOPE *e;
69c2aa98e2SPeter Wemm 	int mode;
70c2aa98e2SPeter Wemm {
71c2aa98e2SPeter Wemm 	register ADDRESS *q;
72c2aa98e2SPeter Wemm 	char *owner;
73c2aa98e2SPeter Wemm 	int otherowners;
7406f25ae9SGregory Neil Shapiro 	int save_errno;
75c2aa98e2SPeter Wemm 	register ENVELOPE *ee;
76c2aa98e2SPeter Wemm 	ENVELOPE *splitenv = NULL;
77c2aa98e2SPeter Wemm 	int oldverbose = Verbose;
7840266059SGregory Neil Shapiro 	bool somedeliveries = false, expensive = false;
79c2aa98e2SPeter Wemm 	pid_t pid;
80c2aa98e2SPeter Wemm 
81c2aa98e2SPeter Wemm 	/*
82c2aa98e2SPeter Wemm 	**  If this message is to be discarded, don't bother sending
83c2aa98e2SPeter Wemm 	**  the message at all.
84c2aa98e2SPeter Wemm 	*/
85c2aa98e2SPeter Wemm 
86c2aa98e2SPeter Wemm 	if (bitset(EF_DISCARD, e->e_flags))
87c2aa98e2SPeter Wemm 	{
88c2aa98e2SPeter Wemm 		if (tTd(13, 1))
8940266059SGregory Neil Shapiro 			sm_dprintf("sendall: discarding id %s\n", e->e_id);
90c2aa98e2SPeter Wemm 		e->e_flags |= EF_CLRQUEUE;
9140266059SGregory Neil Shapiro 		if (LogLevel > 9)
9240266059SGregory Neil Shapiro 			logundelrcpts(e, "discarded", 9, true);
9340266059SGregory Neil Shapiro 		else if (LogLevel > 4)
94c2aa98e2SPeter Wemm 			sm_syslog(LOG_INFO, e->e_id, "discarded");
9540266059SGregory Neil Shapiro 		markstats(e, NULL, STATS_REJECT);
96c2aa98e2SPeter Wemm 		return;
97c2aa98e2SPeter Wemm 	}
98c2aa98e2SPeter Wemm 
99c2aa98e2SPeter Wemm 	/*
100c2aa98e2SPeter Wemm 	**  If we have had global, fatal errors, don't bother sending
101c2aa98e2SPeter Wemm 	**  the message at all if we are in SMTP mode.  Local errors
102c2aa98e2SPeter Wemm 	**  (e.g., a single address failing) will still cause the other
103c2aa98e2SPeter Wemm 	**  addresses to be sent.
104c2aa98e2SPeter Wemm 	*/
105c2aa98e2SPeter Wemm 
106c2aa98e2SPeter Wemm 	if (bitset(EF_FATALERRS, e->e_flags) &&
107c2aa98e2SPeter Wemm 	    (OpMode == MD_SMTP || OpMode == MD_DAEMON))
108c2aa98e2SPeter Wemm 	{
109c2aa98e2SPeter Wemm 		e->e_flags |= EF_CLRQUEUE;
110c2aa98e2SPeter Wemm 		return;
111c2aa98e2SPeter Wemm 	}
112c2aa98e2SPeter Wemm 
113c2aa98e2SPeter Wemm 	/* determine actual delivery mode */
114c2aa98e2SPeter Wemm 	if (mode == SM_DEFAULT)
115c2aa98e2SPeter Wemm 	{
116c2aa98e2SPeter Wemm 		mode = e->e_sendmode;
117c2aa98e2SPeter Wemm 		if (mode != SM_VERIFY && mode != SM_DEFER &&
118c2aa98e2SPeter Wemm 		    shouldqueue(e->e_msgpriority, e->e_ctime))
119c2aa98e2SPeter Wemm 			mode = SM_QUEUE;
120c2aa98e2SPeter Wemm 	}
121c2aa98e2SPeter Wemm 
122c2aa98e2SPeter Wemm 	if (tTd(13, 1))
123c2aa98e2SPeter Wemm 	{
12440266059SGregory Neil Shapiro 		sm_dprintf("\n===== SENDALL: mode %c, id %s, e_from ",
125c2aa98e2SPeter Wemm 			mode, e->e_id);
12640266059SGregory Neil Shapiro 		printaddr(&e->e_from, false);
12740266059SGregory Neil Shapiro 		sm_dprintf("\te_flags = ");
128c2aa98e2SPeter Wemm 		printenvflags(e);
12940266059SGregory Neil Shapiro 		sm_dprintf("sendqueue:\n");
13040266059SGregory Neil Shapiro 		printaddr(e->e_sendqueue, true);
131c2aa98e2SPeter Wemm 	}
132c2aa98e2SPeter Wemm 
133c2aa98e2SPeter Wemm 	/*
134c2aa98e2SPeter Wemm 	**  Do any preprocessing necessary for the mode we are running.
135c2aa98e2SPeter Wemm 	**	Check to make sure the hop count is reasonable.
136c2aa98e2SPeter Wemm 	**	Delete sends to the sender in mailing lists.
137c2aa98e2SPeter Wemm 	*/
138c2aa98e2SPeter Wemm 
139c2aa98e2SPeter Wemm 	CurEnv = e;
140c2aa98e2SPeter Wemm 	if (tTd(62, 1))
141c2aa98e2SPeter Wemm 		checkfds(NULL);
142c2aa98e2SPeter Wemm 
143c2aa98e2SPeter Wemm 	if (e->e_hopcount > MaxHopCount)
144c2aa98e2SPeter Wemm 	{
1458774250cSGregory Neil Shapiro 		char *recip;
1468774250cSGregory Neil Shapiro 
1478774250cSGregory Neil Shapiro 		if (e->e_sendqueue != NULL &&
1488774250cSGregory Neil Shapiro 		    e->e_sendqueue->q_paddr != NULL)
1498774250cSGregory Neil Shapiro 			recip = e->e_sendqueue->q_paddr;
1508774250cSGregory Neil Shapiro 		else
1518774250cSGregory Neil Shapiro 			recip = "(nobody)";
1528774250cSGregory Neil Shapiro 
153c2aa98e2SPeter Wemm 		errno = 0;
15440266059SGregory Neil Shapiro 		queueup(e, WILL_BE_QUEUED(mode), false);
155c2aa98e2SPeter Wemm 		e->e_flags |= EF_FATALERRS|EF_PM_NOTIFY|EF_CLRQUEUE;
15606f25ae9SGregory Neil Shapiro 		ExitStat = EX_UNAVAILABLE;
1578774250cSGregory Neil Shapiro 		syserr("554 5.4.6 Too many hops %d (%d max): from %s via %s, to %s",
158c2aa98e2SPeter Wemm 		       e->e_hopcount, MaxHopCount, e->e_from.q_paddr,
159c2aa98e2SPeter Wemm 		       RealHostName == NULL ? "localhost" : RealHostName,
1608774250cSGregory Neil Shapiro 		       recip);
16106f25ae9SGregory Neil Shapiro 		for (q = e->e_sendqueue; q != NULL; q = q->q_next)
16206f25ae9SGregory Neil Shapiro 		{
16306f25ae9SGregory Neil Shapiro 			if (QS_IS_DEAD(q->q_state))
16406f25ae9SGregory Neil Shapiro 				continue;
16506f25ae9SGregory Neil Shapiro 			q->q_state = QS_BADADDR;
16606f25ae9SGregory Neil Shapiro 			q->q_status = "5.4.6";
1678774250cSGregory Neil Shapiro 			q->q_rstatus = "554 5.4.6 Too many hops";
16806f25ae9SGregory Neil Shapiro 		}
169c2aa98e2SPeter Wemm 		return;
170c2aa98e2SPeter Wemm 	}
171c2aa98e2SPeter Wemm 
172c2aa98e2SPeter Wemm 	/*
173c2aa98e2SPeter Wemm 	**  Do sender deletion.
174c2aa98e2SPeter Wemm 	**
17506f25ae9SGregory Neil Shapiro 	**	If the sender should be queued up, skip this.
176c2aa98e2SPeter Wemm 	**	This can happen if the name server is hosed when you
177c2aa98e2SPeter Wemm 	**	are trying to send mail.  The result is that the sender
178c2aa98e2SPeter Wemm 	**	is instantiated in the queue as a recipient.
179c2aa98e2SPeter Wemm 	*/
180c2aa98e2SPeter Wemm 
181c2aa98e2SPeter Wemm 	if (!bitset(EF_METOO, e->e_flags) &&
18206f25ae9SGregory Neil Shapiro 	    !QS_IS_QUEUEUP(e->e_from.q_state))
183c2aa98e2SPeter Wemm 	{
184c2aa98e2SPeter Wemm 		if (tTd(13, 5))
185c2aa98e2SPeter Wemm 		{
18640266059SGregory Neil Shapiro 			sm_dprintf("sendall: QS_SENDER ");
18740266059SGregory Neil Shapiro 			printaddr(&e->e_from, false);
188c2aa98e2SPeter Wemm 		}
18906f25ae9SGregory Neil Shapiro 		e->e_from.q_state = QS_SENDER;
190c2aa98e2SPeter Wemm 		(void) recipient(&e->e_from, &e->e_sendqueue, 0, e);
191c2aa98e2SPeter Wemm 	}
192c2aa98e2SPeter Wemm 
193c2aa98e2SPeter Wemm 	/*
194c2aa98e2SPeter Wemm 	**  Handle alias owners.
195c2aa98e2SPeter Wemm 	**
196c2aa98e2SPeter Wemm 	**	We scan up the q_alias chain looking for owners.
197c2aa98e2SPeter Wemm 	**	We discard owners that are the same as the return path.
198c2aa98e2SPeter Wemm 	*/
199c2aa98e2SPeter Wemm 
200c2aa98e2SPeter Wemm 	for (q = e->e_sendqueue; q != NULL; q = q->q_next)
201c2aa98e2SPeter Wemm 	{
202c2aa98e2SPeter Wemm 		register struct address *a;
203c2aa98e2SPeter Wemm 
204c2aa98e2SPeter Wemm 		for (a = q; a != NULL && a->q_owner == NULL; a = a->q_alias)
205c2aa98e2SPeter Wemm 			continue;
206c2aa98e2SPeter Wemm 		if (a != NULL)
207c2aa98e2SPeter Wemm 			q->q_owner = a->q_owner;
208c2aa98e2SPeter Wemm 
209c2aa98e2SPeter Wemm 		if (q->q_owner != NULL &&
21006f25ae9SGregory Neil Shapiro 		    !QS_IS_DEAD(q->q_state) &&
211c2aa98e2SPeter Wemm 		    strcmp(q->q_owner, e->e_from.q_paddr) == 0)
212c2aa98e2SPeter Wemm 			q->q_owner = NULL;
213c2aa98e2SPeter Wemm 	}
214c2aa98e2SPeter Wemm 
215c2aa98e2SPeter Wemm 	if (tTd(13, 25))
216c2aa98e2SPeter Wemm 	{
21740266059SGregory Neil Shapiro 		sm_dprintf("\nAfter first owner pass, sendq =\n");
21840266059SGregory Neil Shapiro 		printaddr(e->e_sendqueue, true);
219c2aa98e2SPeter Wemm 	}
220c2aa98e2SPeter Wemm 
221c2aa98e2SPeter Wemm 	owner = "";
222c2aa98e2SPeter Wemm 	otherowners = 1;
223c2aa98e2SPeter Wemm 	while (owner != NULL && otherowners > 0)
224c2aa98e2SPeter Wemm 	{
225c2aa98e2SPeter Wemm 		if (tTd(13, 28))
22640266059SGregory Neil Shapiro 			sm_dprintf("owner = \"%s\", otherowners = %d\n",
227c2aa98e2SPeter Wemm 				   owner, otherowners);
228c2aa98e2SPeter Wemm 		owner = NULL;
229c2aa98e2SPeter Wemm 		otherowners = bitset(EF_SENDRECEIPT, e->e_flags) ? 1 : 0;
230c2aa98e2SPeter Wemm 
231c2aa98e2SPeter Wemm 		for (q = e->e_sendqueue; q != NULL; q = q->q_next)
232c2aa98e2SPeter Wemm 		{
233c2aa98e2SPeter Wemm 			if (tTd(13, 30))
234c2aa98e2SPeter Wemm 			{
23540266059SGregory Neil Shapiro 				sm_dprintf("Checking ");
23640266059SGregory Neil Shapiro 				printaddr(q, false);
237c2aa98e2SPeter Wemm 			}
23806f25ae9SGregory Neil Shapiro 			if (QS_IS_DEAD(q->q_state))
239c2aa98e2SPeter Wemm 			{
240c2aa98e2SPeter Wemm 				if (tTd(13, 30))
24140266059SGregory Neil Shapiro 					sm_dprintf("    ... QS_IS_DEAD\n");
242c2aa98e2SPeter Wemm 				continue;
243c2aa98e2SPeter Wemm 			}
244c2aa98e2SPeter Wemm 			if (tTd(13, 29) && !tTd(13, 30))
245c2aa98e2SPeter Wemm 			{
24640266059SGregory Neil Shapiro 				sm_dprintf("Checking ");
24740266059SGregory Neil Shapiro 				printaddr(q, false);
248c2aa98e2SPeter Wemm 			}
249c2aa98e2SPeter Wemm 
250c2aa98e2SPeter Wemm 			if (q->q_owner != NULL)
251c2aa98e2SPeter Wemm 			{
252c2aa98e2SPeter Wemm 				if (owner == NULL)
253c2aa98e2SPeter Wemm 				{
254c2aa98e2SPeter Wemm 					if (tTd(13, 40))
25540266059SGregory Neil Shapiro 						sm_dprintf("    ... First owner = \"%s\"\n",
256c2aa98e2SPeter Wemm 							   q->q_owner);
257c2aa98e2SPeter Wemm 					owner = q->q_owner;
258c2aa98e2SPeter Wemm 				}
259c2aa98e2SPeter Wemm 				else if (owner != q->q_owner)
260c2aa98e2SPeter Wemm 				{
261c2aa98e2SPeter Wemm 					if (strcmp(owner, q->q_owner) == 0)
262c2aa98e2SPeter Wemm 					{
263c2aa98e2SPeter Wemm 						if (tTd(13, 40))
26440266059SGregory Neil Shapiro 							sm_dprintf("    ... Same owner = \"%s\"\n",
265c2aa98e2SPeter Wemm 								   owner);
266c2aa98e2SPeter Wemm 
267c2aa98e2SPeter Wemm 						/* make future comparisons cheap */
268c2aa98e2SPeter Wemm 						q->q_owner = owner;
269c2aa98e2SPeter Wemm 					}
270c2aa98e2SPeter Wemm 					else
271c2aa98e2SPeter Wemm 					{
272c2aa98e2SPeter Wemm 						if (tTd(13, 40))
27340266059SGregory Neil Shapiro 							sm_dprintf("    ... Another owner \"%s\"\n",
274c2aa98e2SPeter Wemm 								   q->q_owner);
275c2aa98e2SPeter Wemm 						otherowners++;
276c2aa98e2SPeter Wemm 					}
277c2aa98e2SPeter Wemm 					owner = q->q_owner;
278c2aa98e2SPeter Wemm 				}
279c2aa98e2SPeter Wemm 				else if (tTd(13, 40))
28040266059SGregory Neil Shapiro 					sm_dprintf("    ... Same owner = \"%s\"\n",
281c2aa98e2SPeter Wemm 						   owner);
282c2aa98e2SPeter Wemm 			}
283c2aa98e2SPeter Wemm 			else
284c2aa98e2SPeter Wemm 			{
285c2aa98e2SPeter Wemm 				if (tTd(13, 40))
28640266059SGregory Neil Shapiro 					sm_dprintf("    ... Null owner\n");
287c2aa98e2SPeter Wemm 				otherowners++;
288c2aa98e2SPeter Wemm 			}
289c2aa98e2SPeter Wemm 
29006f25ae9SGregory Neil Shapiro 			if (QS_IS_BADADDR(q->q_state))
29106f25ae9SGregory Neil Shapiro 			{
29206f25ae9SGregory Neil Shapiro 				if (tTd(13, 30))
29340266059SGregory Neil Shapiro 					sm_dprintf("    ... QS_IS_BADADDR\n");
29406f25ae9SGregory Neil Shapiro 				continue;
29506f25ae9SGregory Neil Shapiro 			}
29606f25ae9SGregory Neil Shapiro 
29706f25ae9SGregory Neil Shapiro 			if (QS_IS_QUEUEUP(q->q_state))
29806f25ae9SGregory Neil Shapiro 			{
29906f25ae9SGregory Neil Shapiro 				MAILER *m = q->q_mailer;
30006f25ae9SGregory Neil Shapiro 
30106f25ae9SGregory Neil Shapiro 				/*
30206f25ae9SGregory Neil Shapiro 				**  If we have temporary address failures
30306f25ae9SGregory Neil Shapiro 				**  (e.g., dns failure) and a fallback MX is
30406f25ae9SGregory Neil Shapiro 				**  set, send directly to the fallback MX host.
30506f25ae9SGregory Neil Shapiro 				*/
30606f25ae9SGregory Neil Shapiro 
30706f25ae9SGregory Neil Shapiro 				if (FallBackMX != NULL &&
30806f25ae9SGregory Neil Shapiro 				    !wordinclass(FallBackMX, 'w') &&
30906f25ae9SGregory Neil Shapiro 				    mode != SM_VERIFY &&
31040266059SGregory Neil Shapiro 				    !bitnset(M_NOMX, m->m_flags) &&
31140266059SGregory Neil Shapiro 				    strcmp(m->m_mailer, "[IPC]") == 0 &&
31206f25ae9SGregory Neil Shapiro 				    m->m_argv[0] != NULL &&
31340266059SGregory Neil Shapiro 				    strcmp(m->m_argv[0], "TCP") == 0)
31406f25ae9SGregory Neil Shapiro 				{
31506f25ae9SGregory Neil Shapiro 					int len;
31606f25ae9SGregory Neil Shapiro 					char *p;
31706f25ae9SGregory Neil Shapiro 
31806f25ae9SGregory Neil Shapiro 					if (tTd(13, 30))
31940266059SGregory Neil Shapiro 						sm_dprintf("    ... FallBackMX\n");
32006f25ae9SGregory Neil Shapiro 
32140266059SGregory Neil Shapiro 					len = strlen(FallBackMX) + 1;
32240266059SGregory Neil Shapiro 					p = sm_rpool_malloc_x(e->e_rpool, len);
32340266059SGregory Neil Shapiro 					(void) sm_strlcpy(p, FallBackMX, len);
32406f25ae9SGregory Neil Shapiro 					q->q_state = QS_OK;
32506f25ae9SGregory Neil Shapiro 					q->q_host = p;
32606f25ae9SGregory Neil Shapiro 				}
32706f25ae9SGregory Neil Shapiro 				else
32806f25ae9SGregory Neil Shapiro 				{
32906f25ae9SGregory Neil Shapiro 					if (tTd(13, 30))
33040266059SGregory Neil Shapiro 						sm_dprintf("    ... QS_IS_QUEUEUP\n");
33106f25ae9SGregory Neil Shapiro 					continue;
33206f25ae9SGregory Neil Shapiro 				}
33306f25ae9SGregory Neil Shapiro 			}
33406f25ae9SGregory Neil Shapiro 
335c2aa98e2SPeter Wemm 			/*
336c2aa98e2SPeter Wemm 			**  If this mailer is expensive, and if we don't
337c2aa98e2SPeter Wemm 			**  want to make connections now, just mark these
338c2aa98e2SPeter Wemm 			**  addresses and return.  This is useful if we
339c2aa98e2SPeter Wemm 			**  want to batch connections to reduce load.  This
340c2aa98e2SPeter Wemm 			**  will cause the messages to be queued up, and a
341c2aa98e2SPeter Wemm 			**  daemon will come along to send the messages later.
342c2aa98e2SPeter Wemm 			*/
343c2aa98e2SPeter Wemm 
344c2aa98e2SPeter Wemm 			if (NoConnect && !Verbose &&
345c2aa98e2SPeter Wemm 			    bitnset(M_EXPENSIVE, q->q_mailer->m_flags))
346c2aa98e2SPeter Wemm 			{
347c2aa98e2SPeter Wemm 				if (tTd(13, 30))
34840266059SGregory Neil Shapiro 					sm_dprintf("    ... expensive\n");
34906f25ae9SGregory Neil Shapiro 				q->q_state = QS_QUEUEUP;
35040266059SGregory Neil Shapiro 				expensive = true;
35106f25ae9SGregory Neil Shapiro 			}
35206f25ae9SGregory Neil Shapiro 			else if (bitnset(M_HOLD, q->q_mailer->m_flags) &&
35306f25ae9SGregory Neil Shapiro 				 QueueLimitId == NULL &&
35406f25ae9SGregory Neil Shapiro 				 QueueLimitSender == NULL &&
35506f25ae9SGregory Neil Shapiro 				 QueueLimitRecipient == NULL)
35606f25ae9SGregory Neil Shapiro 			{
35706f25ae9SGregory Neil Shapiro 				if (tTd(13, 30))
35840266059SGregory Neil Shapiro 					sm_dprintf("    ... hold\n");
35906f25ae9SGregory Neil Shapiro 				q->q_state = QS_QUEUEUP;
36040266059SGregory Neil Shapiro 				expensive = true;
361c2aa98e2SPeter Wemm 			}
36240266059SGregory Neil Shapiro #if _FFR_QUARANTINE
36340266059SGregory Neil Shapiro 			else if (QueueMode != QM_QUARANTINE &&
36440266059SGregory Neil Shapiro 				 e->e_quarmsg != NULL)
36540266059SGregory Neil Shapiro 			{
36640266059SGregory Neil Shapiro 				if (tTd(13, 30))
36740266059SGregory Neil Shapiro 					sm_dprintf("    ... quarantine: %s\n",
36840266059SGregory Neil Shapiro 						   e->e_quarmsg);
36940266059SGregory Neil Shapiro 				q->q_state = QS_QUEUEUP;
37040266059SGregory Neil Shapiro 				expensive = true;
37140266059SGregory Neil Shapiro 			}
37240266059SGregory Neil Shapiro #endif /* _FFR_QUARANTINE */
373c2aa98e2SPeter Wemm 			else
374c2aa98e2SPeter Wemm 			{
375c2aa98e2SPeter Wemm 				if (tTd(13, 30))
37640266059SGregory Neil Shapiro 					sm_dprintf("    ... deliverable\n");
37740266059SGregory Neil Shapiro 				somedeliveries = true;
378c2aa98e2SPeter Wemm 			}
379c2aa98e2SPeter Wemm 		}
380c2aa98e2SPeter Wemm 
381c2aa98e2SPeter Wemm 		if (owner != NULL && otherowners > 0)
382c2aa98e2SPeter Wemm 		{
383c2aa98e2SPeter Wemm 			/*
384c2aa98e2SPeter Wemm 			**  Split this envelope into two.
385c2aa98e2SPeter Wemm 			*/
386c2aa98e2SPeter Wemm 
38740266059SGregory Neil Shapiro 			ee = (ENVELOPE *) sm_rpool_malloc_x(e->e_rpool,
38840266059SGregory Neil Shapiro 							    sizeof *ee);
38940266059SGregory Neil Shapiro 			STRUCTCOPY(*e, *ee);
39006f25ae9SGregory Neil Shapiro 			ee->e_message = NULL;
391c2aa98e2SPeter Wemm 			ee->e_id = NULL;
39206f25ae9SGregory Neil Shapiro 			assign_queueid(ee);
393c2aa98e2SPeter Wemm 
394c2aa98e2SPeter Wemm 			if (tTd(13, 1))
39540266059SGregory Neil Shapiro 				sm_dprintf("sendall: split %s into %s, owner = \"%s\", otherowners = %d\n",
39640266059SGregory Neil Shapiro 					   e->e_id, ee->e_id, owner,
39740266059SGregory Neil Shapiro 					   otherowners);
398c2aa98e2SPeter Wemm 
39940266059SGregory Neil Shapiro 			ee->e_header = copyheader(e->e_header, ee->e_rpool);
40040266059SGregory Neil Shapiro 			ee->e_sendqueue = copyqueue(e->e_sendqueue,
40140266059SGregory Neil Shapiro 						    ee->e_rpool);
40240266059SGregory Neil Shapiro 			ee->e_errorqueue = copyqueue(e->e_errorqueue,
40340266059SGregory Neil Shapiro 						     ee->e_rpool);
404c2aa98e2SPeter Wemm 			ee->e_flags = e->e_flags & ~(EF_INQUEUE|EF_CLRQUEUE|EF_FATALERRS|EF_SENDRECEIPT|EF_RET_PARAM);
405c2aa98e2SPeter Wemm 			ee->e_flags |= EF_NORECEIPT;
40640266059SGregory Neil Shapiro 			setsender(owner, ee, NULL, '\0', true);
407c2aa98e2SPeter Wemm 			if (tTd(13, 5))
408c2aa98e2SPeter Wemm 			{
40940266059SGregory Neil Shapiro 				sm_dprintf("sendall(split): QS_SENDER ");
41040266059SGregory Neil Shapiro 				printaddr(&ee->e_from, false);
411c2aa98e2SPeter Wemm 			}
41206f25ae9SGregory Neil Shapiro 			ee->e_from.q_state = QS_SENDER;
413c2aa98e2SPeter Wemm 			ee->e_dfp = NULL;
41406f25ae9SGregory Neil Shapiro 			ee->e_lockfp = NULL;
415c2aa98e2SPeter Wemm 			ee->e_xfp = NULL;
41640266059SGregory Neil Shapiro 			ee->e_qgrp = e->e_qgrp;
41740266059SGregory Neil Shapiro 			ee->e_qdir = e->e_qdir;
418c2aa98e2SPeter Wemm 			ee->e_errormode = EM_MAIL;
419c2aa98e2SPeter Wemm 			ee->e_sibling = splitenv;
42006f25ae9SGregory Neil Shapiro 			ee->e_statmsg = NULL;
42140266059SGregory Neil Shapiro #if _FFR_QUARANTINE
42240266059SGregory Neil Shapiro 			if (e->e_quarmsg != NULL)
42340266059SGregory Neil Shapiro 				ee->e_quarmsg = sm_rpool_strdup_x(ee->e_rpool,
42440266059SGregory Neil Shapiro 								  e->e_quarmsg);
42540266059SGregory Neil Shapiro #endif /* _FFR_QUARANTINE */
426c2aa98e2SPeter Wemm 			splitenv = ee;
427c2aa98e2SPeter Wemm 
428c2aa98e2SPeter Wemm 			for (q = e->e_sendqueue; q != NULL; q = q->q_next)
429c2aa98e2SPeter Wemm 			{
430c2aa98e2SPeter Wemm 				if (q->q_owner == owner)
431c2aa98e2SPeter Wemm 				{
43206f25ae9SGregory Neil Shapiro 					q->q_state = QS_CLONED;
433c2aa98e2SPeter Wemm 					if (tTd(13, 6))
43440266059SGregory Neil Shapiro 						sm_dprintf("\t... stripping %s from original envelope\n",
435c2aa98e2SPeter Wemm 							   q->q_paddr);
436c2aa98e2SPeter Wemm 				}
437c2aa98e2SPeter Wemm 			}
438c2aa98e2SPeter Wemm 			for (q = ee->e_sendqueue; q != NULL; q = q->q_next)
439c2aa98e2SPeter Wemm 			{
440c2aa98e2SPeter Wemm 				if (q->q_owner != owner)
441c2aa98e2SPeter Wemm 				{
44206f25ae9SGregory Neil Shapiro 					q->q_state = QS_CLONED;
443c2aa98e2SPeter Wemm 					if (tTd(13, 6))
44440266059SGregory Neil Shapiro 						sm_dprintf("\t... dropping %s from cloned envelope\n",
445c2aa98e2SPeter Wemm 							   q->q_paddr);
446c2aa98e2SPeter Wemm 				}
447c2aa98e2SPeter Wemm 				else
448c2aa98e2SPeter Wemm 				{
449c2aa98e2SPeter Wemm 					/* clear DSN parameters */
450c2aa98e2SPeter Wemm 					q->q_flags &= ~(QHASNOTIFY|Q_PINGFLAGS);
451c2aa98e2SPeter Wemm 					q->q_flags |= DefaultNotify & ~QPINGONSUCCESS;
452c2aa98e2SPeter Wemm 					if (tTd(13, 6))
45340266059SGregory Neil Shapiro 						sm_dprintf("\t... moving %s to cloned envelope\n",
454c2aa98e2SPeter Wemm 							   q->q_paddr);
455c2aa98e2SPeter Wemm 				}
456c2aa98e2SPeter Wemm 			}
457c2aa98e2SPeter Wemm 
458c2aa98e2SPeter Wemm 			if (mode != SM_VERIFY && bitset(EF_HAS_DF, e->e_flags))
45940266059SGregory Neil Shapiro 				dup_queue_file(e, ee, DATAFL_LETTER);
46006f25ae9SGregory Neil Shapiro 
46106f25ae9SGregory Neil Shapiro 			/*
46206f25ae9SGregory Neil Shapiro 			**  Give the split envelope access to the parent
46306f25ae9SGregory Neil Shapiro 			**  transcript file for errors obtained while
46406f25ae9SGregory Neil Shapiro 			**  processing the recipients (done before the
46506f25ae9SGregory Neil Shapiro 			**  envelope splitting).
46606f25ae9SGregory Neil Shapiro 			*/
46706f25ae9SGregory Neil Shapiro 
46806f25ae9SGregory Neil Shapiro 			if (e->e_xfp != NULL)
46940266059SGregory Neil Shapiro 				ee->e_xfp = sm_io_dup(e->e_xfp);
47006f25ae9SGregory Neil Shapiro 
47106f25ae9SGregory Neil Shapiro 			/* failed to dup e->e_xfp, start a new transcript */
47206f25ae9SGregory Neil Shapiro 			if (ee->e_xfp == NULL)
473c2aa98e2SPeter Wemm 				openxscript(ee);
47406f25ae9SGregory Neil Shapiro 
475065a643dSPeter Wemm 			if (mode != SM_VERIFY && LogLevel > 4)
47640266059SGregory Neil Shapiro 				sm_syslog(LOG_INFO, e->e_id,
47740266059SGregory Neil Shapiro 					  "%s: clone: owner=%s",
47840266059SGregory Neil Shapiro 					  ee->e_id, owner);
479c2aa98e2SPeter Wemm 		}
480c2aa98e2SPeter Wemm 	}
481c2aa98e2SPeter Wemm 
482c2aa98e2SPeter Wemm 	if (owner != NULL)
483c2aa98e2SPeter Wemm 	{
48440266059SGregory Neil Shapiro 		setsender(owner, e, NULL, '\0', true);
485c2aa98e2SPeter Wemm 		if (tTd(13, 5))
486c2aa98e2SPeter Wemm 		{
48740266059SGregory Neil Shapiro 			sm_dprintf("sendall(owner): QS_SENDER ");
48840266059SGregory Neil Shapiro 			printaddr(&e->e_from, false);
489c2aa98e2SPeter Wemm 		}
49006f25ae9SGregory Neil Shapiro 		e->e_from.q_state = QS_SENDER;
491c2aa98e2SPeter Wemm 		e->e_errormode = EM_MAIL;
492c2aa98e2SPeter Wemm 		e->e_flags |= EF_NORECEIPT;
493c2aa98e2SPeter Wemm 		e->e_flags &= ~EF_FATALERRS;
494c2aa98e2SPeter Wemm 	}
495c2aa98e2SPeter Wemm 
496c2aa98e2SPeter Wemm 	/* if nothing to be delivered, just queue up everything */
49740266059SGregory Neil Shapiro 	if (!somedeliveries && !WILL_BE_QUEUED(mode) &&
498c2aa98e2SPeter Wemm 	    mode != SM_VERIFY)
499c2aa98e2SPeter Wemm 	{
50040266059SGregory Neil Shapiro 		time_t now;
501193538b7SGregory Neil Shapiro 
502c2aa98e2SPeter Wemm 		if (tTd(13, 29))
50340266059SGregory Neil Shapiro 			sm_dprintf("No deliveries: auto-queuing\n");
504c2aa98e2SPeter Wemm 		mode = SM_QUEUE;
50540266059SGregory Neil Shapiro 		now = curtime();
506c2aa98e2SPeter Wemm 
507c2aa98e2SPeter Wemm 		/* treat this as a delivery in terms of counting tries */
508193538b7SGregory Neil Shapiro 		e->e_dtime = now;
509c2aa98e2SPeter Wemm 		if (!expensive)
510c2aa98e2SPeter Wemm 			e->e_ntries++;
511c2aa98e2SPeter Wemm 		for (ee = splitenv; ee != NULL; ee = ee->e_sibling)
512c2aa98e2SPeter Wemm 		{
513193538b7SGregory Neil Shapiro 			ee->e_dtime = now;
514c2aa98e2SPeter Wemm 			if (!expensive)
515c2aa98e2SPeter Wemm 				ee->e_ntries++;
516c2aa98e2SPeter Wemm 		}
517c2aa98e2SPeter Wemm 	}
518c2aa98e2SPeter Wemm 
51940266059SGregory Neil Shapiro 	if ((WILL_BE_QUEUED(mode) || mode == SM_FORK ||
52040266059SGregory Neil Shapiro 	     (mode != SM_VERIFY && SuperSafe == SAFE_REALLY)) &&
521c2aa98e2SPeter Wemm 	    (!bitset(EF_INQUEUE, e->e_flags) || splitenv != NULL))
522c2aa98e2SPeter Wemm 	{
52340266059SGregory Neil Shapiro 		bool msync;
52440266059SGregory Neil Shapiro 
52542e5d165SGregory Neil Shapiro 		/*
52642e5d165SGregory Neil Shapiro 		**  Be sure everything is instantiated in the queue.
52742e5d165SGregory Neil Shapiro 		**  Split envelopes first in case the machine crashes.
52842e5d165SGregory Neil Shapiro 		**  If the original were done first, we may lose
52942e5d165SGregory Neil Shapiro 		**  recipients.
53042e5d165SGregory Neil Shapiro 		*/
53142e5d165SGregory Neil Shapiro 
53240266059SGregory Neil Shapiro #if !HASFLOCK
53340266059SGregory Neil Shapiro 		msync = false;
53440266059SGregory Neil Shapiro #else /* !HASFLOCK */
53540266059SGregory Neil Shapiro 		msync = mode == SM_FORK;
53640266059SGregory Neil Shapiro #endif /* !HASFLOCK */
53740266059SGregory Neil Shapiro 
538c2aa98e2SPeter Wemm 		for (ee = splitenv; ee != NULL; ee = ee->e_sibling)
53940266059SGregory Neil Shapiro 			queueup(ee, WILL_BE_QUEUED(mode), msync);
54040266059SGregory Neil Shapiro 		queueup(e, WILL_BE_QUEUED(mode), msync);
541c2aa98e2SPeter Wemm 	}
542c2aa98e2SPeter Wemm 
543c2aa98e2SPeter Wemm 	if (tTd(62, 10))
544c2aa98e2SPeter Wemm 		checkfds("after envelope splitting");
545c2aa98e2SPeter Wemm 
546c2aa98e2SPeter Wemm 	/*
547c2aa98e2SPeter Wemm 	**  If we belong in background, fork now.
548c2aa98e2SPeter Wemm 	*/
549c2aa98e2SPeter Wemm 
550c2aa98e2SPeter Wemm 	if (tTd(13, 20))
551c2aa98e2SPeter Wemm 	{
55240266059SGregory Neil Shapiro 		sm_dprintf("sendall: final mode = %c\n", mode);
553c2aa98e2SPeter Wemm 		if (tTd(13, 21))
554c2aa98e2SPeter Wemm 		{
55540266059SGregory Neil Shapiro 			sm_dprintf("\n================ Final Send Queue(s) =====================\n");
55640266059SGregory Neil Shapiro 			sm_dprintf("\n  *** Envelope %s, e_from=%s ***\n",
557c2aa98e2SPeter Wemm 				   e->e_id, e->e_from.q_paddr);
55840266059SGregory Neil Shapiro 			printaddr(e->e_sendqueue, true);
559c2aa98e2SPeter Wemm 			for (ee = splitenv; ee != NULL; ee = ee->e_sibling)
560c2aa98e2SPeter Wemm 			{
56140266059SGregory Neil Shapiro 				sm_dprintf("\n  *** Envelope %s, e_from=%s ***\n",
562c2aa98e2SPeter Wemm 					   ee->e_id, ee->e_from.q_paddr);
56340266059SGregory Neil Shapiro 				printaddr(ee->e_sendqueue, true);
564c2aa98e2SPeter Wemm 			}
56540266059SGregory Neil Shapiro 			sm_dprintf("==========================================================\n\n");
566c2aa98e2SPeter Wemm 		}
567c2aa98e2SPeter Wemm 	}
568c2aa98e2SPeter Wemm 	switch (mode)
569c2aa98e2SPeter Wemm 	{
570c2aa98e2SPeter Wemm 	  case SM_VERIFY:
571c2aa98e2SPeter Wemm 		Verbose = 2;
572c2aa98e2SPeter Wemm 		break;
573c2aa98e2SPeter Wemm 
574c2aa98e2SPeter Wemm 	  case SM_QUEUE:
575c2aa98e2SPeter Wemm 	  case SM_DEFER:
576c2aa98e2SPeter Wemm #if HASFLOCK
577c2aa98e2SPeter Wemm   queueonly:
57806f25ae9SGregory Neil Shapiro #endif /* HASFLOCK */
579c2aa98e2SPeter Wemm 		if (e->e_nrcpts > 0)
580c2aa98e2SPeter Wemm 			e->e_flags |= EF_INQUEUE;
58140266059SGregory Neil Shapiro 		dropenvelope(e, splitenv != NULL, true);
582c2aa98e2SPeter Wemm 		for (ee = splitenv; ee != NULL; ee = ee->e_sibling)
583c2aa98e2SPeter Wemm 		{
584c2aa98e2SPeter Wemm 			if (ee->e_nrcpts > 0)
585c2aa98e2SPeter Wemm 				ee->e_flags |= EF_INQUEUE;
58640266059SGregory Neil Shapiro 			dropenvelope(ee, false, true);
587c2aa98e2SPeter Wemm 		}
588c2aa98e2SPeter Wemm 		return;
589c2aa98e2SPeter Wemm 
590c2aa98e2SPeter Wemm 	  case SM_FORK:
591c2aa98e2SPeter Wemm 		if (e->e_xfp != NULL)
59240266059SGregory Neil Shapiro 			(void) sm_io_flush(e->e_xfp, SM_TIME_DEFAULT);
593c2aa98e2SPeter Wemm 
594c2aa98e2SPeter Wemm #if !HASFLOCK
595c2aa98e2SPeter Wemm 		/*
596c2aa98e2SPeter Wemm 		**  Since fcntl locking has the interesting semantic that
597c2aa98e2SPeter Wemm 		**  the lock is owned by a process, not by an open file
598c2aa98e2SPeter Wemm 		**  descriptor, we have to flush this to the queue, and
599c2aa98e2SPeter Wemm 		**  then restart from scratch in the child.
600c2aa98e2SPeter Wemm 		*/
601c2aa98e2SPeter Wemm 
602c2aa98e2SPeter Wemm 		{
603c2aa98e2SPeter Wemm 			/* save id for future use */
604c2aa98e2SPeter Wemm 			char *qid = e->e_id;
605c2aa98e2SPeter Wemm 
606c2aa98e2SPeter Wemm 			/* now drop the envelope in the parent */
607c2aa98e2SPeter Wemm 			e->e_flags |= EF_INQUEUE;
60840266059SGregory Neil Shapiro 			dropenvelope(e, splitenv != NULL, false);
609c2aa98e2SPeter Wemm 
610c2aa98e2SPeter Wemm 			/* arrange to reacquire lock after fork */
611c2aa98e2SPeter Wemm 			e->e_id = qid;
612c2aa98e2SPeter Wemm 		}
613c2aa98e2SPeter Wemm 
614c2aa98e2SPeter Wemm 		for (ee = splitenv; ee != NULL; ee = ee->e_sibling)
615c2aa98e2SPeter Wemm 		{
616c2aa98e2SPeter Wemm 			/* save id for future use */
617c2aa98e2SPeter Wemm 			char *qid = ee->e_id;
618c2aa98e2SPeter Wemm 
619c2aa98e2SPeter Wemm 			/* drop envelope in parent */
620c2aa98e2SPeter Wemm 			ee->e_flags |= EF_INQUEUE;
62140266059SGregory Neil Shapiro 			dropenvelope(ee, false, false);
622c2aa98e2SPeter Wemm 
623c2aa98e2SPeter Wemm 			/* and save qid for reacquisition */
624c2aa98e2SPeter Wemm 			ee->e_id = qid;
625c2aa98e2SPeter Wemm 		}
626c2aa98e2SPeter Wemm 
627c2aa98e2SPeter Wemm #endif /* !HASFLOCK */
628c2aa98e2SPeter Wemm 
62906f25ae9SGregory Neil Shapiro 		/*
63006f25ae9SGregory Neil Shapiro 		**  Since the delivery may happen in a child and the parent
63106f25ae9SGregory Neil Shapiro 		**  does not wait, the parent may close the maps thereby
63206f25ae9SGregory Neil Shapiro 		**  removing any shared memory used by the map.  Therefore,
63306f25ae9SGregory Neil Shapiro 		**  close the maps now so the child will dynamically open
63406f25ae9SGregory Neil Shapiro 		**  them if necessary.
63506f25ae9SGregory Neil Shapiro 		*/
63606f25ae9SGregory Neil Shapiro 
63740266059SGregory Neil Shapiro 		closemaps(false);
63806f25ae9SGregory Neil Shapiro 
639c2aa98e2SPeter Wemm 		pid = fork();
640c2aa98e2SPeter Wemm 		if (pid < 0)
641c2aa98e2SPeter Wemm 		{
64206f25ae9SGregory Neil Shapiro 			syserr("deliver: fork 1");
643c2aa98e2SPeter Wemm #if HASFLOCK
644c2aa98e2SPeter Wemm 			goto queueonly;
64506f25ae9SGregory Neil Shapiro #else /* HASFLOCK */
646c2aa98e2SPeter Wemm 			e->e_id = NULL;
647c2aa98e2SPeter Wemm 			for (ee = splitenv; ee != NULL; ee = ee->e_sibling)
648c2aa98e2SPeter Wemm 				ee->e_id = NULL;
649c2aa98e2SPeter Wemm 			return;
650c2aa98e2SPeter Wemm #endif /* HASFLOCK */
651c2aa98e2SPeter Wemm 		}
652c2aa98e2SPeter Wemm 		else if (pid > 0)
653c2aa98e2SPeter Wemm 		{
654c2aa98e2SPeter Wemm #if HASFLOCK
655c2aa98e2SPeter Wemm 			/* be sure we leave the temp files to our child */
656c2aa98e2SPeter Wemm 			/* close any random open files in the envelope */
657c2aa98e2SPeter Wemm 			closexscript(e);
658c2aa98e2SPeter Wemm 			if (e->e_dfp != NULL)
65940266059SGregory Neil Shapiro 				(void) sm_io_close(e->e_dfp, SM_TIME_DEFAULT);
660c2aa98e2SPeter Wemm 			e->e_dfp = NULL;
661c2aa98e2SPeter Wemm 			e->e_flags &= ~EF_HAS_DF;
662c2aa98e2SPeter Wemm 
663c2aa98e2SPeter Wemm 			/* can't call unlockqueue to avoid unlink of xfp */
664c2aa98e2SPeter Wemm 			if (e->e_lockfp != NULL)
66540266059SGregory Neil Shapiro 				(void) sm_io_close(e->e_lockfp, SM_TIME_DEFAULT);
66606f25ae9SGregory Neil Shapiro 			else
66706f25ae9SGregory Neil Shapiro 				syserr("%s: sendall: null lockfp", e->e_id);
668c2aa98e2SPeter Wemm 			e->e_lockfp = NULL;
66906f25ae9SGregory Neil Shapiro #endif /* HASFLOCK */
670c2aa98e2SPeter Wemm 
671c2aa98e2SPeter Wemm 			/* make sure the parent doesn't own the envelope */
672c2aa98e2SPeter Wemm 			e->e_id = NULL;
673c2aa98e2SPeter Wemm 
67440266059SGregory Neil Shapiro #if USE_DOUBLE_FORK
675c2aa98e2SPeter Wemm 			/* catch intermediate zombie */
676c2aa98e2SPeter Wemm 			(void) waitfor(pid);
67740266059SGregory Neil Shapiro #endif /* USE_DOUBLE_FORK */
678c2aa98e2SPeter Wemm 			return;
679c2aa98e2SPeter Wemm 		}
680c2aa98e2SPeter Wemm 
6818774250cSGregory Neil Shapiro 		/* Reset global flags */
6828774250cSGregory Neil Shapiro 		RestartRequest = NULL;
68340266059SGregory Neil Shapiro 		RestartWorkGroup = false;
6848774250cSGregory Neil Shapiro 		ShutdownRequest = NULL;
6858774250cSGregory Neil Shapiro 		PendingSignal = 0;
6868774250cSGregory Neil Shapiro 
68742e5d165SGregory Neil Shapiro 		/*
68840266059SGregory Neil Shapiro 		**  Initialize exception stack and default exception
68940266059SGregory Neil Shapiro 		**  handler for child process.
69040266059SGregory Neil Shapiro 		*/
69140266059SGregory Neil Shapiro 
69240266059SGregory Neil Shapiro 		sm_exc_newthread(fatal_error);
69340266059SGregory Neil Shapiro 
69440266059SGregory Neil Shapiro 		/*
69542e5d165SGregory Neil Shapiro 		**  Since we have accepted responsbility for the message,
69642e5d165SGregory Neil Shapiro 		**  change the SIGTERM handler.  intsig() (the old handler)
69742e5d165SGregory Neil Shapiro 		**  would remove the envelope if this was a command line
69842e5d165SGregory Neil Shapiro 		**  message submission.
69942e5d165SGregory Neil Shapiro 		*/
70042e5d165SGregory Neil Shapiro 
70140266059SGregory Neil Shapiro 		(void) sm_signal(SIGTERM, SIG_DFL);
70242e5d165SGregory Neil Shapiro 
70340266059SGregory Neil Shapiro #if USE_DOUBLE_FORK
704c2aa98e2SPeter Wemm 		/* double fork to avoid zombies */
705c2aa98e2SPeter Wemm 		pid = fork();
706c2aa98e2SPeter Wemm 		if (pid > 0)
707c2aa98e2SPeter Wemm 			exit(EX_OK);
70806f25ae9SGregory Neil Shapiro 		save_errno = errno;
70940266059SGregory Neil Shapiro #endif /* USE_DOUBLE_FORK */
71040266059SGregory Neil Shapiro 
71140266059SGregory Neil Shapiro 		CurrentPid = getpid();
712c2aa98e2SPeter Wemm 
713c2aa98e2SPeter Wemm 		/* be sure we are immune from the terminal */
714c2aa98e2SPeter Wemm 		disconnect(2, e);
71506f25ae9SGregory Neil Shapiro 		clearstats();
716c2aa98e2SPeter Wemm 
717c2aa98e2SPeter Wemm 		/* prevent parent from waiting if there was an error */
718c2aa98e2SPeter Wemm 		if (pid < 0)
719c2aa98e2SPeter Wemm 		{
72006f25ae9SGregory Neil Shapiro 			errno = save_errno;
72106f25ae9SGregory Neil Shapiro 			syserr("deliver: fork 2");
722c2aa98e2SPeter Wemm #if HASFLOCK
723c2aa98e2SPeter Wemm 			e->e_flags |= EF_INQUEUE;
72406f25ae9SGregory Neil Shapiro #else /* HASFLOCK */
725c2aa98e2SPeter Wemm 			e->e_id = NULL;
726c2aa98e2SPeter Wemm #endif /* HASFLOCK */
72740266059SGregory Neil Shapiro 			finis(true, true, ExitStat);
728c2aa98e2SPeter Wemm 		}
729c2aa98e2SPeter Wemm 
730c2aa98e2SPeter Wemm 		/* be sure to give error messages in child */
73140266059SGregory Neil Shapiro 		QuickAbort = false;
732c2aa98e2SPeter Wemm 
733c2aa98e2SPeter Wemm 		/*
734c2aa98e2SPeter Wemm 		**  Close any cached connections.
735c2aa98e2SPeter Wemm 		**
736c2aa98e2SPeter Wemm 		**	We don't send the QUIT protocol because the parent
737c2aa98e2SPeter Wemm 		**	still knows about the connection.
738c2aa98e2SPeter Wemm 		**
739c2aa98e2SPeter Wemm 		**	This should only happen when delivering an error
740c2aa98e2SPeter Wemm 		**	message.
741c2aa98e2SPeter Wemm 		*/
742c2aa98e2SPeter Wemm 
74340266059SGregory Neil Shapiro 		mci_flush(false, NULL);
744c2aa98e2SPeter Wemm 
745c2aa98e2SPeter Wemm #if HASFLOCK
746c2aa98e2SPeter Wemm 		break;
74706f25ae9SGregory Neil Shapiro #else /* HASFLOCK */
748c2aa98e2SPeter Wemm 
749c2aa98e2SPeter Wemm 		/*
750c2aa98e2SPeter Wemm 		**  Now reacquire and run the various queue files.
751c2aa98e2SPeter Wemm 		*/
752c2aa98e2SPeter Wemm 
753c2aa98e2SPeter Wemm 		for (ee = splitenv; ee != NULL; ee = ee->e_sibling)
754c2aa98e2SPeter Wemm 		{
755c2aa98e2SPeter Wemm 			ENVELOPE *sibling = ee->e_sibling;
756c2aa98e2SPeter Wemm 
75740266059SGregory Neil Shapiro 			(void) dowork(ee->e_qgrp, ee->e_qdir, ee->e_id,
75840266059SGregory Neil Shapiro 				      false, false, ee);
759c2aa98e2SPeter Wemm 			ee->e_sibling = sibling;
760c2aa98e2SPeter Wemm 		}
76140266059SGregory Neil Shapiro 		(void) dowork(e->e_qgrp, e->e_qdir, e->e_id,
76240266059SGregory Neil Shapiro 			      false, false, e);
76340266059SGregory Neil Shapiro 		finis(true, true, ExitStat);
76406f25ae9SGregory Neil Shapiro #endif /* HASFLOCK */
765c2aa98e2SPeter Wemm 	}
766c2aa98e2SPeter Wemm 
767c2aa98e2SPeter Wemm 	sendenvelope(e, mode);
76840266059SGregory Neil Shapiro 	dropenvelope(e, true, true);
769c2aa98e2SPeter Wemm 	for (ee = splitenv; ee != NULL; ee = ee->e_sibling)
770c2aa98e2SPeter Wemm 	{
771c2aa98e2SPeter Wemm 		CurEnv = ee;
772c2aa98e2SPeter Wemm 		if (mode != SM_VERIFY)
773c2aa98e2SPeter Wemm 			openxscript(ee);
774c2aa98e2SPeter Wemm 		sendenvelope(ee, mode);
77540266059SGregory Neil Shapiro 		dropenvelope(ee, true, true);
776c2aa98e2SPeter Wemm 	}
777c2aa98e2SPeter Wemm 	CurEnv = e;
778c2aa98e2SPeter Wemm 
779c2aa98e2SPeter Wemm 	Verbose = oldverbose;
780c2aa98e2SPeter Wemm 	if (mode == SM_FORK)
78140266059SGregory Neil Shapiro 		finis(true, true, ExitStat);
782c2aa98e2SPeter Wemm }
783c2aa98e2SPeter Wemm 
78406f25ae9SGregory Neil Shapiro static void
785c2aa98e2SPeter Wemm sendenvelope(e, mode)
786c2aa98e2SPeter Wemm 	register ENVELOPE *e;
787c2aa98e2SPeter Wemm 	int mode;
788c2aa98e2SPeter Wemm {
789c2aa98e2SPeter Wemm 	register ADDRESS *q;
790c2aa98e2SPeter Wemm 	bool didany;
791c2aa98e2SPeter Wemm 
792c2aa98e2SPeter Wemm 	if (tTd(13, 10))
79340266059SGregory Neil Shapiro 		sm_dprintf("sendenvelope(%s) e_flags=0x%lx\n",
794c2aa98e2SPeter Wemm 			   e->e_id == NULL ? "[NOQUEUE]" : e->e_id,
795c2aa98e2SPeter Wemm 			   e->e_flags);
796c2aa98e2SPeter Wemm 	if (LogLevel > 80)
797c2aa98e2SPeter Wemm 		sm_syslog(LOG_DEBUG, e->e_id,
79806f25ae9SGregory Neil Shapiro 			  "sendenvelope, flags=0x%lx",
799c2aa98e2SPeter Wemm 			  e->e_flags);
800c2aa98e2SPeter Wemm 
801c2aa98e2SPeter Wemm 	/*
802c2aa98e2SPeter Wemm 	**  If we have had global, fatal errors, don't bother sending
803c2aa98e2SPeter Wemm 	**  the message at all if we are in SMTP mode.  Local errors
804c2aa98e2SPeter Wemm 	**  (e.g., a single address failing) will still cause the other
805c2aa98e2SPeter Wemm 	**  addresses to be sent.
806c2aa98e2SPeter Wemm 	*/
807c2aa98e2SPeter Wemm 
808c2aa98e2SPeter Wemm 	if (bitset(EF_FATALERRS, e->e_flags) &&
809c2aa98e2SPeter Wemm 	    (OpMode == MD_SMTP || OpMode == MD_DAEMON))
810c2aa98e2SPeter Wemm 	{
811c2aa98e2SPeter Wemm 		e->e_flags |= EF_CLRQUEUE;
812c2aa98e2SPeter Wemm 		return;
813c2aa98e2SPeter Wemm 	}
814c2aa98e2SPeter Wemm 
81540266059SGregory Neil Shapiro 	/*
81640266059SGregory Neil Shapiro 	**  Don't attempt deliveries if we want to bounce now
81740266059SGregory Neil Shapiro 	**  or if deliver-by time is exceeded.
81840266059SGregory Neil Shapiro 	*/
81940266059SGregory Neil Shapiro 
82006f25ae9SGregory Neil Shapiro 	if (!bitset(EF_RESPONSE, e->e_flags) &&
82140266059SGregory Neil Shapiro 	    (TimeOuts.to_q_return[e->e_timeoutclass] == NOW ||
82240266059SGregory Neil Shapiro 	     (IS_DLVR_RETURN(e) && e->e_deliver_by > 0 &&
82340266059SGregory Neil Shapiro 	      curtime() > e->e_ctime + e->e_deliver_by)))
82406f25ae9SGregory Neil Shapiro 		return;
82506f25ae9SGregory Neil Shapiro 
826c2aa98e2SPeter Wemm 	/*
827c2aa98e2SPeter Wemm 	**  Run through the list and send everything.
828c2aa98e2SPeter Wemm 	**
829c2aa98e2SPeter Wemm 	**	Set EF_GLOBALERRS so that error messages during delivery
830c2aa98e2SPeter Wemm 	**	result in returned mail.
831c2aa98e2SPeter Wemm 	*/
832c2aa98e2SPeter Wemm 
833c2aa98e2SPeter Wemm 	e->e_nsent = 0;
834c2aa98e2SPeter Wemm 	e->e_flags |= EF_GLOBALERRS;
83506f25ae9SGregory Neil Shapiro 
83640266059SGregory Neil Shapiro 	macdefine(&e->e_macro, A_PERM, macid("{envid}"), e->e_envid);
83740266059SGregory Neil Shapiro 	macdefine(&e->e_macro, A_PERM, macid("{bodytype}"), e->e_bodytype);
83840266059SGregory Neil Shapiro 	didany = false;
83940266059SGregory Neil Shapiro 
84040266059SGregory Neil Shapiro 	if (!bitset(EF_SPLIT, e->e_flags))
84140266059SGregory Neil Shapiro 	{
84240266059SGregory Neil Shapiro 		ENVELOPE *oldsib;
84340266059SGregory Neil Shapiro 		ENVELOPE *ee;
84440266059SGregory Neil Shapiro 
84540266059SGregory Neil Shapiro 		/*
84640266059SGregory Neil Shapiro 		**  Save old sibling and set it to NULL to avoid
84740266059SGregory Neil Shapiro 		**  queueing up the same envelopes again.
84840266059SGregory Neil Shapiro 		**  This requires that envelopes in that list have
84940266059SGregory Neil Shapiro 		**  been take care of before (or at some other place).
85040266059SGregory Neil Shapiro 		*/
85140266059SGregory Neil Shapiro 
85240266059SGregory Neil Shapiro 		oldsib = e->e_sibling;
85340266059SGregory Neil Shapiro 		e->e_sibling = NULL;
85440266059SGregory Neil Shapiro 		if (!split_by_recipient(e) &&
85540266059SGregory Neil Shapiro 		    bitset(EF_FATALERRS, e->e_flags))
85640266059SGregory Neil Shapiro 		{
85740266059SGregory Neil Shapiro 			if (OpMode == MD_SMTP || OpMode == MD_DAEMON)
85840266059SGregory Neil Shapiro 				e->e_flags |= EF_CLRQUEUE;
85940266059SGregory Neil Shapiro 			return;
86040266059SGregory Neil Shapiro 		}
86140266059SGregory Neil Shapiro 		for (ee = e->e_sibling; ee != NULL; ee = ee->e_sibling)
86240266059SGregory Neil Shapiro 			queueup(ee, false, true);
86340266059SGregory Neil Shapiro 
86440266059SGregory Neil Shapiro 		/* clean up */
86540266059SGregory Neil Shapiro 		for (ee = e->e_sibling; ee != NULL; ee = ee->e_sibling)
86640266059SGregory Neil Shapiro 		{
86740266059SGregory Neil Shapiro 			/* now unlock the job */
86840266059SGregory Neil Shapiro 			closexscript(ee);
86940266059SGregory Neil Shapiro 			unlockqueue(ee);
87040266059SGregory Neil Shapiro 
87140266059SGregory Neil Shapiro 			/* this envelope is marked unused */
87240266059SGregory Neil Shapiro 			if (ee->e_dfp != NULL)
87340266059SGregory Neil Shapiro 			{
87440266059SGregory Neil Shapiro 				(void) sm_io_close(ee->e_dfp, SM_TIME_DEFAULT);
87540266059SGregory Neil Shapiro 				ee->e_dfp = NULL;
87640266059SGregory Neil Shapiro 			}
87740266059SGregory Neil Shapiro 			ee->e_id = NULL;
87840266059SGregory Neil Shapiro 			ee->e_flags &= ~EF_HAS_DF;
87940266059SGregory Neil Shapiro 		}
88040266059SGregory Neil Shapiro 		e->e_sibling = oldsib;
88140266059SGregory Neil Shapiro 	}
882c2aa98e2SPeter Wemm 
883c2aa98e2SPeter Wemm 	/* now run through the queue */
884c2aa98e2SPeter Wemm 	for (q = e->e_sendqueue; q != NULL; q = q->q_next)
885c2aa98e2SPeter Wemm 	{
886c2aa98e2SPeter Wemm #if XDEBUG
887c2aa98e2SPeter Wemm 		char wbuf[MAXNAME + 20];
888c2aa98e2SPeter Wemm 
88940266059SGregory Neil Shapiro 		(void) sm_snprintf(wbuf, sizeof wbuf, "sendall(%.*s)",
890c2aa98e2SPeter Wemm 				   MAXNAME, q->q_paddr);
891c2aa98e2SPeter Wemm 		checkfd012(wbuf);
89206f25ae9SGregory Neil Shapiro #endif /* XDEBUG */
893c2aa98e2SPeter Wemm 		if (mode == SM_VERIFY)
894c2aa98e2SPeter Wemm 		{
895c2aa98e2SPeter Wemm 			e->e_to = q->q_paddr;
89606f25ae9SGregory Neil Shapiro 			if (QS_IS_SENDABLE(q->q_state))
897c2aa98e2SPeter Wemm 			{
898c2aa98e2SPeter Wemm 				if (q->q_host != NULL && q->q_host[0] != '\0')
899c2aa98e2SPeter Wemm 					message("deliverable: mailer %s, host %s, user %s",
900c2aa98e2SPeter Wemm 						q->q_mailer->m_name,
901c2aa98e2SPeter Wemm 						q->q_host,
902c2aa98e2SPeter Wemm 						q->q_user);
903c2aa98e2SPeter Wemm 				else
904c2aa98e2SPeter Wemm 					message("deliverable: mailer %s, user %s",
905c2aa98e2SPeter Wemm 						q->q_mailer->m_name,
906c2aa98e2SPeter Wemm 						q->q_user);
907c2aa98e2SPeter Wemm 			}
908c2aa98e2SPeter Wemm 		}
90906f25ae9SGregory Neil Shapiro 		else if (QS_IS_OK(q->q_state))
910c2aa98e2SPeter Wemm 		{
911c2aa98e2SPeter Wemm 			/*
912c2aa98e2SPeter Wemm 			**  Checkpoint the send list every few addresses
913c2aa98e2SPeter Wemm 			*/
914c2aa98e2SPeter Wemm 
91542e5d165SGregory Neil Shapiro 			if (CheckpointInterval > 0 &&
91642e5d165SGregory Neil Shapiro 			    e->e_nsent >= CheckpointInterval)
917c2aa98e2SPeter Wemm 			{
91840266059SGregory Neil Shapiro 				queueup(e, false, false);
919c2aa98e2SPeter Wemm 				e->e_nsent = 0;
920c2aa98e2SPeter Wemm 			}
921c2aa98e2SPeter Wemm 			(void) deliver(e, q);
92240266059SGregory Neil Shapiro 			didany = true;
923c2aa98e2SPeter Wemm 		}
924c2aa98e2SPeter Wemm 	}
925c2aa98e2SPeter Wemm 	if (didany)
926c2aa98e2SPeter Wemm 	{
927c2aa98e2SPeter Wemm 		e->e_dtime = curtime();
928c2aa98e2SPeter Wemm 		e->e_ntries++;
929c2aa98e2SPeter Wemm 	}
930c2aa98e2SPeter Wemm 
931c2aa98e2SPeter Wemm #if XDEBUG
932c2aa98e2SPeter Wemm 	checkfd012("end of sendenvelope");
93306f25ae9SGregory Neil Shapiro #endif /* XDEBUG */
934c2aa98e2SPeter Wemm }
93540266059SGregory Neil Shapiro 
93640266059SGregory Neil Shapiro #if REQUIRES_DIR_FSYNC
93740266059SGregory Neil Shapiro /*
93840266059SGregory Neil Shapiro **  SYNC_DIR -- fsync a directory based on a filename
93940266059SGregory Neil Shapiro **
94040266059SGregory Neil Shapiro **	Parameters:
94140266059SGregory Neil Shapiro **		filename -- path of file
94240266059SGregory Neil Shapiro **		panic -- panic?
94340266059SGregory Neil Shapiro **
94440266059SGregory Neil Shapiro **	Returns:
94540266059SGregory Neil Shapiro **		none
94640266059SGregory Neil Shapiro */
94740266059SGregory Neil Shapiro 
94840266059SGregory Neil Shapiro void
94940266059SGregory Neil Shapiro sync_dir(filename, panic)
95040266059SGregory Neil Shapiro 	char *filename;
95140266059SGregory Neil Shapiro 	bool panic;
95240266059SGregory Neil Shapiro {
95340266059SGregory Neil Shapiro 	int dirfd;
95440266059SGregory Neil Shapiro 	char *dirp;
95540266059SGregory Neil Shapiro 	char dir[MAXPATHLEN];
95640266059SGregory Neil Shapiro 
95740266059SGregory Neil Shapiro 	/* filesystems which require the directory be synced */
95840266059SGregory Neil Shapiro 	dirp = strrchr(filename, '/');
95940266059SGregory Neil Shapiro 	if (dirp != NULL)
96040266059SGregory Neil Shapiro 	{
96140266059SGregory Neil Shapiro 		if (sm_strlcpy(dir, filename, sizeof dir) >= sizeof dir)
96240266059SGregory Neil Shapiro 			return;
96340266059SGregory Neil Shapiro 		dir[dirp - filename] = '\0';
96440266059SGregory Neil Shapiro 		dirp = dir;
96540266059SGregory Neil Shapiro 	}
96640266059SGregory Neil Shapiro 	else
96740266059SGregory Neil Shapiro 		dirp = ".";
96840266059SGregory Neil Shapiro 	dirfd = open(dirp, O_RDONLY, 0700);
96940266059SGregory Neil Shapiro 	if (tTd(40,32))
97040266059SGregory Neil Shapiro 		sm_syslog(LOG_INFO, NOQID, "sync_dir: %s: fsync(%d)",
97140266059SGregory Neil Shapiro 			  dirp, dirfd);
97240266059SGregory Neil Shapiro 	if (dirfd >= 0)
97340266059SGregory Neil Shapiro 	{
97440266059SGregory Neil Shapiro 		if (fsync(dirfd) < 0)
97540266059SGregory Neil Shapiro 		{
97640266059SGregory Neil Shapiro 			if (panic)
97740266059SGregory Neil Shapiro 				syserr("!sync_dir: cannot fsync directory %s",
97840266059SGregory Neil Shapiro 				       dirp);
97940266059SGregory Neil Shapiro 			else if (LogLevel > 1)
98040266059SGregory Neil Shapiro 				sm_syslog(LOG_ERR, NOQID,
98140266059SGregory Neil Shapiro 					  "sync_dir: cannot fsync directory %s: %s",
98240266059SGregory Neil Shapiro 					  dirp, sm_errstring(errno));
98340266059SGregory Neil Shapiro 		}
98440266059SGregory Neil Shapiro 		(void) close(dirfd);
98540266059SGregory Neil Shapiro 	}
98640266059SGregory Neil Shapiro }
98740266059SGregory Neil Shapiro #endif /* REQUIRES_DIR_FSYNC */
98840266059SGregory Neil Shapiro /*
989c2aa98e2SPeter Wemm **  DUP_QUEUE_FILE -- duplicate a queue file into a split queue
990c2aa98e2SPeter Wemm **
991c2aa98e2SPeter Wemm **	Parameters:
992c2aa98e2SPeter Wemm **		e -- the existing envelope
993c2aa98e2SPeter Wemm **		ee -- the new envelope
99440266059SGregory Neil Shapiro **		type -- the queue file type (e.g., DATAFL_LETTER)
995c2aa98e2SPeter Wemm **
996c2aa98e2SPeter Wemm **	Returns:
997c2aa98e2SPeter Wemm **		none
998c2aa98e2SPeter Wemm */
999c2aa98e2SPeter Wemm 
100006f25ae9SGregory Neil Shapiro static void
1001c2aa98e2SPeter Wemm dup_queue_file(e, ee, type)
100240266059SGregory Neil Shapiro 	ENVELOPE *e, *ee;
1003c2aa98e2SPeter Wemm 	int type;
1004c2aa98e2SPeter Wemm {
100506f25ae9SGregory Neil Shapiro 	char f1buf[MAXPATHLEN], f2buf[MAXPATHLEN];
1006c2aa98e2SPeter Wemm 
1007c2aa98e2SPeter Wemm 	ee->e_dfp = NULL;
1008c2aa98e2SPeter Wemm 	ee->e_xfp = NULL;
100906f25ae9SGregory Neil Shapiro 
101006f25ae9SGregory Neil Shapiro 	/*
101106f25ae9SGregory Neil Shapiro 	**  Make sure both are in the same directory.
101206f25ae9SGregory Neil Shapiro 	*/
101306f25ae9SGregory Neil Shapiro 
101440266059SGregory Neil Shapiro 	(void) sm_strlcpy(f1buf, queuename(e, type), sizeof f1buf);
101540266059SGregory Neil Shapiro 	(void) sm_strlcpy(f2buf, queuename(ee, type), sizeof f2buf);
1016c2aa98e2SPeter Wemm 	if (link(f1buf, f2buf) < 0)
1017c2aa98e2SPeter Wemm 	{
101806f25ae9SGregory Neil Shapiro 		int save_errno = errno;
1019c2aa98e2SPeter Wemm 
1020c2aa98e2SPeter Wemm 		syserr("sendall: link(%s, %s)", f1buf, f2buf);
102106f25ae9SGregory Neil Shapiro 		if (save_errno == EEXIST)
1022c2aa98e2SPeter Wemm 		{
1023c2aa98e2SPeter Wemm 			if (unlink(f2buf) < 0)
1024c2aa98e2SPeter Wemm 			{
1025c2aa98e2SPeter Wemm 				syserr("!sendall: unlink(%s): permanent",
1026c2aa98e2SPeter Wemm 				       f2buf);
1027c2aa98e2SPeter Wemm 				/* NOTREACHED */
1028c2aa98e2SPeter Wemm 			}
1029c2aa98e2SPeter Wemm 			if (link(f1buf, f2buf) < 0)
1030c2aa98e2SPeter Wemm 			{
1031c2aa98e2SPeter Wemm 				syserr("!sendall: link(%s, %s): permanent",
1032c2aa98e2SPeter Wemm 				       f1buf, f2buf);
1033c2aa98e2SPeter Wemm 				/* NOTREACHED */
1034c2aa98e2SPeter Wemm 			}
1035c2aa98e2SPeter Wemm 		}
1036c2aa98e2SPeter Wemm 	}
103740266059SGregory Neil Shapiro 	SYNC_DIR(f2buf, true);
1038c2aa98e2SPeter Wemm }
103940266059SGregory Neil Shapiro /*
1040c2aa98e2SPeter Wemm **  DOFORK -- do a fork, retrying a couple of times on failure.
1041c2aa98e2SPeter Wemm **
1042c2aa98e2SPeter Wemm **	This MUST be a macro, since after a vfork we are running
1043c2aa98e2SPeter Wemm **	two processes on the same stack!!!
1044c2aa98e2SPeter Wemm **
1045c2aa98e2SPeter Wemm **	Parameters:
1046c2aa98e2SPeter Wemm **		none.
1047c2aa98e2SPeter Wemm **
1048c2aa98e2SPeter Wemm **	Returns:
1049c2aa98e2SPeter Wemm **		From a macro???  You've got to be kidding!
1050c2aa98e2SPeter Wemm **
1051c2aa98e2SPeter Wemm **	Side Effects:
1052c2aa98e2SPeter Wemm **		Modifies the ==> LOCAL <== variable 'pid', leaving:
1053c2aa98e2SPeter Wemm **			pid of child in parent, zero in child.
1054c2aa98e2SPeter Wemm **			-1 on unrecoverable error.
1055c2aa98e2SPeter Wemm **
1056c2aa98e2SPeter Wemm **	Notes:
1057c2aa98e2SPeter Wemm **		I'm awfully sorry this looks so awful.  That's
1058c2aa98e2SPeter Wemm **		vfork for you.....
1059c2aa98e2SPeter Wemm */
1060c2aa98e2SPeter Wemm 
1061c2aa98e2SPeter Wemm #define NFORKTRIES	5
1062c2aa98e2SPeter Wemm 
1063c2aa98e2SPeter Wemm #ifndef FORK
1064c2aa98e2SPeter Wemm # define FORK	fork
106506f25ae9SGregory Neil Shapiro #endif /* ! FORK */
1066c2aa98e2SPeter Wemm 
1067c2aa98e2SPeter Wemm #define DOFORK(fORKfN) \
1068c2aa98e2SPeter Wemm {\
1069c2aa98e2SPeter Wemm 	register int i;\
1070c2aa98e2SPeter Wemm \
1071c2aa98e2SPeter Wemm 	for (i = NFORKTRIES; --i >= 0; )\
1072c2aa98e2SPeter Wemm 	{\
1073c2aa98e2SPeter Wemm 		pid = fORKfN();\
1074c2aa98e2SPeter Wemm 		if (pid >= 0)\
1075c2aa98e2SPeter Wemm 			break;\
1076c2aa98e2SPeter Wemm 		if (i > 0)\
107706f25ae9SGregory Neil Shapiro 			(void) sleep((unsigned) NFORKTRIES - i);\
1078c2aa98e2SPeter Wemm 	}\
1079c2aa98e2SPeter Wemm }
108040266059SGregory Neil Shapiro /*
1081c2aa98e2SPeter Wemm **  DOFORK -- simple fork interface to DOFORK.
1082c2aa98e2SPeter Wemm **
1083c2aa98e2SPeter Wemm **	Parameters:
1084c2aa98e2SPeter Wemm **		none.
1085c2aa98e2SPeter Wemm **
1086c2aa98e2SPeter Wemm **	Returns:
1087c2aa98e2SPeter Wemm **		pid of child in parent.
1088c2aa98e2SPeter Wemm **		zero in child.
1089c2aa98e2SPeter Wemm **		-1 on error.
1090c2aa98e2SPeter Wemm **
1091c2aa98e2SPeter Wemm **	Side Effects:
1092c2aa98e2SPeter Wemm **		returns twice, once in parent and once in child.
1093c2aa98e2SPeter Wemm */
1094c2aa98e2SPeter Wemm 
10958774250cSGregory Neil Shapiro pid_t
1096c2aa98e2SPeter Wemm dofork()
1097c2aa98e2SPeter Wemm {
1098c2aa98e2SPeter Wemm 	register pid_t pid = -1;
1099c2aa98e2SPeter Wemm 
1100c2aa98e2SPeter Wemm 	DOFORK(fork);
110106f25ae9SGregory Neil Shapiro 	return pid;
1102c2aa98e2SPeter Wemm }
110340266059SGregory Neil Shapiro 
110440266059SGregory Neil Shapiro /*
110540266059SGregory Neil Shapiro **  COLONCMP -- compare host-signatures up to first ':' or EOS
110640266059SGregory Neil Shapiro **
110740266059SGregory Neil Shapiro **	This takes two strings which happen to be host-signatures and
110840266059SGregory Neil Shapiro **	compares them. If the lowest preference portions of the MX-RR's
110940266059SGregory Neil Shapiro **	match (up to ':' or EOS, whichever is first), then we have
111040266059SGregory Neil Shapiro **	match. This is used for coattail-piggybacking messages during
111140266059SGregory Neil Shapiro **	message delivery.
111240266059SGregory Neil Shapiro **	If the signatures are the same up to the first ':' the remainder of
111340266059SGregory Neil Shapiro **	the signatures are then compared with a normal strcmp(). This saves
111440266059SGregory Neil Shapiro **	re-examining the first part of the signatures.
111540266059SGregory Neil Shapiro **
111640266059SGregory Neil Shapiro **	Parameters:
111740266059SGregory Neil Shapiro **		a - first host-signature
111840266059SGregory Neil Shapiro **		b - second host-signature
111940266059SGregory Neil Shapiro **
112040266059SGregory Neil Shapiro **	Returns:
112140266059SGregory Neil Shapiro **		HS_MATCH_NO -- no "match".
112240266059SGregory Neil Shapiro **		HS_MATCH_FIRST -- "match" for the first MX preference
112340266059SGregory Neil Shapiro **			(up to the first colon (':')).
112440266059SGregory Neil Shapiro **		HS_MATCH_FULL -- match for the entire MX record.
112540266059SGregory Neil Shapiro **
112640266059SGregory Neil Shapiro **	Side Effects:
112740266059SGregory Neil Shapiro **		none.
112840266059SGregory Neil Shapiro */
112940266059SGregory Neil Shapiro 
113040266059SGregory Neil Shapiro #define HS_MATCH_NO	0
113140266059SGregory Neil Shapiro #define HS_MATCH_FIRST	1
113240266059SGregory Neil Shapiro #define HS_MATCH_FULL	2
113340266059SGregory Neil Shapiro 
113440266059SGregory Neil Shapiro static int
113540266059SGregory Neil Shapiro coloncmp(a, b)
113640266059SGregory Neil Shapiro 	register const char *a;
113740266059SGregory Neil Shapiro 	register const char *b;
113840266059SGregory Neil Shapiro {
113940266059SGregory Neil Shapiro 	int ret = HS_MATCH_NO;
114040266059SGregory Neil Shapiro 	int braclev = 0;
114140266059SGregory Neil Shapiro 
114240266059SGregory Neil Shapiro 	while (*a == *b++)
114340266059SGregory Neil Shapiro 	{
114440266059SGregory Neil Shapiro 		/* Need to account for IPv6 bracketed addresses */
114540266059SGregory Neil Shapiro 		if (*a == '[')
114640266059SGregory Neil Shapiro 			braclev++;
114740266059SGregory Neil Shapiro 		else if (*a == '[' && braclev > 0)
114840266059SGregory Neil Shapiro 			braclev--;
114940266059SGregory Neil Shapiro 		else if (*a == ':' && braclev <= 0)
115040266059SGregory Neil Shapiro 		{
115140266059SGregory Neil Shapiro 			ret = HS_MATCH_FIRST;
115240266059SGregory Neil Shapiro 			a++;
115340266059SGregory Neil Shapiro 			break;
115440266059SGregory Neil Shapiro 		}
115540266059SGregory Neil Shapiro 		else if (*a == '\0')
115640266059SGregory Neil Shapiro 			return HS_MATCH_FULL; /* a full match */
115740266059SGregory Neil Shapiro 		a++;
115840266059SGregory Neil Shapiro 	}
115940266059SGregory Neil Shapiro 	if (ret == HS_MATCH_NO &&
116040266059SGregory Neil Shapiro 	    braclev <= 0 &&
116140266059SGregory Neil Shapiro 	    ((*a == '\0' && *(b - 1) == ':') ||
116240266059SGregory Neil Shapiro 	     (*a == ':' && *(b - 1) == '\0')))
116340266059SGregory Neil Shapiro 		return HS_MATCH_FIRST;
116440266059SGregory Neil Shapiro 	if (ret == HS_MATCH_FIRST && strcmp(a, b) == 0)
116540266059SGregory Neil Shapiro 		return HS_MATCH_FULL;
116640266059SGregory Neil Shapiro 
116740266059SGregory Neil Shapiro 	return ret;
116840266059SGregory Neil Shapiro }
116940266059SGregory Neil Shapiro /*
1170c2aa98e2SPeter Wemm **  DELIVER -- Deliver a message to a list of addresses.
1171c2aa98e2SPeter Wemm **
1172c2aa98e2SPeter Wemm **	This routine delivers to everyone on the same host as the
1173c2aa98e2SPeter Wemm **	user on the head of the list.  It is clever about mailers
1174c2aa98e2SPeter Wemm **	that don't handle multiple users.  It is NOT guaranteed
1175c2aa98e2SPeter Wemm **	that it will deliver to all these addresses however -- so
1176c2aa98e2SPeter Wemm **	deliver should be called once for each address on the
1177c2aa98e2SPeter Wemm **	list.
117840266059SGregory Neil Shapiro **	Deliver tries to be as opportunistic as possible about piggybacking
117940266059SGregory Neil Shapiro **	messages. Some definitions to make understanding easier follow below.
118040266059SGregory Neil Shapiro **	Piggybacking occurs when an existing connection to a mail host can
118140266059SGregory Neil Shapiro **	be used to send the same message to more than one recipient at the
118240266059SGregory Neil Shapiro **	same time. So "no piggybacking" means one message for one recipient
118340266059SGregory Neil Shapiro **	per connection. "Intentional piggybacking" happens when the
118440266059SGregory Neil Shapiro **	recipients' host address (not the mail host address) is used to
118540266059SGregory Neil Shapiro **	attempt piggybacking. Recipients with the same host address
118640266059SGregory Neil Shapiro **	have the same mail host. "Coincidental piggybacking" relies on
118740266059SGregory Neil Shapiro **	piggybacking based on all the mail host addresses in the MX-RR. This
118840266059SGregory Neil Shapiro **	is "coincidental" in the fact it could not be predicted until the
118940266059SGregory Neil Shapiro **	MX Resource Records for the hosts were obtained and examined. For
119040266059SGregory Neil Shapiro **	example (preference order and equivalence is important, not values):
119140266059SGregory Neil Shapiro **		domain1 IN MX 10 mxhost-A
119240266059SGregory Neil Shapiro **			IN MX 20 mxhost-B
119340266059SGregory Neil Shapiro **		domain2 IN MX  4 mxhost-A
119440266059SGregory Neil Shapiro **			IN MX  8 mxhost-B
119540266059SGregory Neil Shapiro **	Domain1 and domain2 can piggyback the same message to mxhost-A or
119640266059SGregory Neil Shapiro **	mxhost-B (if mxhost-A cannot be reached).
119740266059SGregory Neil Shapiro **	"Coattail piggybacking" relaxes the strictness of "coincidental
119840266059SGregory Neil Shapiro **	piggybacking" in the hope that most significant (lowest value)
119940266059SGregory Neil Shapiro **	MX preference host(s) can create more piggybacking. For example
120040266059SGregory Neil Shapiro **	(again, preference order and equivalence is important, not values):
120140266059SGregory Neil Shapiro **		domain3 IN MX 100 mxhost-C
120240266059SGregory Neil Shapiro **			IN MX 100 mxhost-D
120340266059SGregory Neil Shapiro **			IN MX 200 mxhost-E
120440266059SGregory Neil Shapiro **		domain4 IN MX  50 mxhost-C
120540266059SGregory Neil Shapiro **			IN MX  50 mxhost-D
120640266059SGregory Neil Shapiro **			IN MX  80 mxhost-F
120740266059SGregory Neil Shapiro **	A message for domain3 and domain4 can piggyback to mxhost-C if mxhost-C
120840266059SGregory Neil Shapiro **	is available. Same with mxhost-D because in both RR's the preference
120940266059SGregory Neil Shapiro **	value is the same as mxhost-C, respectively.
121040266059SGregory Neil Shapiro **	So deliver attempts coattail piggybacking when possible. If the
121140266059SGregory Neil Shapiro **	first MX preference level hosts cannot be used then the piggybacking
121240266059SGregory Neil Shapiro **	reverts to coincidental piggybacking. Using the above example you
121340266059SGregory Neil Shapiro **	cannot deliver to mxhost-F for domain3 regardless of preference value.
121440266059SGregory Neil Shapiro **	("Coattail" from "riding on the coattails of your predecessor" meaning
121540266059SGregory Neil Shapiro **	gaining benefit from a predecessor effort with no or little addition
121640266059SGregory Neil Shapiro **	effort. The predecessor here being the preceding MX RR).
1217c2aa98e2SPeter Wemm **
1218c2aa98e2SPeter Wemm **	Parameters:
1219c2aa98e2SPeter Wemm **		e -- the envelope to deliver.
1220c2aa98e2SPeter Wemm **		firstto -- head of the address list to deliver to.
1221c2aa98e2SPeter Wemm **
1222c2aa98e2SPeter Wemm **	Returns:
1223c2aa98e2SPeter Wemm **		zero -- successfully delivered.
1224c2aa98e2SPeter Wemm **		else -- some failure, see ExitStat for more info.
1225c2aa98e2SPeter Wemm **
1226c2aa98e2SPeter Wemm **	Side Effects:
1227c2aa98e2SPeter Wemm **		The standard input is passed off to someone.
1228c2aa98e2SPeter Wemm */
1229c2aa98e2SPeter Wemm 
1230c2aa98e2SPeter Wemm #ifndef NO_UID
1231c2aa98e2SPeter Wemm # define NO_UID		-1
123206f25ae9SGregory Neil Shapiro #endif /* ! NO_UID */
1233c2aa98e2SPeter Wemm #ifndef NO_GID
1234c2aa98e2SPeter Wemm # define NO_GID		-1
123506f25ae9SGregory Neil Shapiro #endif /* ! NO_GID */
1236c2aa98e2SPeter Wemm 
123706f25ae9SGregory Neil Shapiro static int
1238c2aa98e2SPeter Wemm deliver(e, firstto)
1239c2aa98e2SPeter Wemm 	register ENVELOPE *e;
1240c2aa98e2SPeter Wemm 	ADDRESS *firstto;
1241c2aa98e2SPeter Wemm {
1242c2aa98e2SPeter Wemm 	char *host;			/* host being sent to */
1243c2aa98e2SPeter Wemm 	char *user;			/* user being sent to */
1244c2aa98e2SPeter Wemm 	char **pvp;
1245c2aa98e2SPeter Wemm 	register char **mvp;
1246c2aa98e2SPeter Wemm 	register char *p;
1247c2aa98e2SPeter Wemm 	register MAILER *m;		/* mailer for this recipient */
1248c2aa98e2SPeter Wemm 	ADDRESS *volatile ctladdr;
124940266059SGregory Neil Shapiro #if HASSETUSERCONTEXT
1250c2aa98e2SPeter Wemm 	ADDRESS *volatile contextaddr = NULL;
125140266059SGregory Neil Shapiro #endif /* HASSETUSERCONTEXT */
1252c2aa98e2SPeter Wemm 	register MCI *volatile mci;
125340266059SGregory Neil Shapiro 	register ADDRESS *SM_NONVOLATILE to = firstto;
125440266059SGregory Neil Shapiro 	volatile bool clever = false;	/* running user smtp to this mailer */
1255c2aa98e2SPeter Wemm 	ADDRESS *volatile tochain = NULL; /* users chain in this mailer call */
1256c2aa98e2SPeter Wemm 	int rcode;			/* response code */
125740266059SGregory Neil Shapiro 	SM_NONVOLATILE int lmtp_rcode = EX_OK;
125840266059SGregory Neil Shapiro 	SM_NONVOLATILE int nummxhosts = 0; /* number of MX hosts available */
125940266059SGregory Neil Shapiro 	SM_NONVOLATILE int hostnum = 0;	/* current MX host index */
1260c2aa98e2SPeter Wemm 	char *firstsig;			/* signature of firstto */
126140266059SGregory Neil Shapiro 	volatile pid_t pid = -1;
1262c2aa98e2SPeter Wemm 	char *volatile curhost;
126340266059SGregory Neil Shapiro 	SM_NONVOLATILE unsigned short port = 0;
126440266059SGregory Neil Shapiro 	SM_NONVOLATILE time_t enough = 0;
126506f25ae9SGregory Neil Shapiro #if NETUNIX
126640266059SGregory Neil Shapiro 	char *SM_NONVOLATILE mux_path = NULL;	/* path to UNIX domain socket */
126706f25ae9SGregory Neil Shapiro #endif /* NETUNIX */
1268c2aa98e2SPeter Wemm 	time_t xstart;
1269c2aa98e2SPeter Wemm 	bool suidwarn;
1270c2aa98e2SPeter Wemm 	bool anyok;			/* at least one address was OK */
127140266059SGregory Neil Shapiro 	SM_NONVOLATILE bool goodmxfound = false; /* at least one MX was OK */
127206f25ae9SGregory Neil Shapiro 	bool ovr;
127340266059SGregory Neil Shapiro #if _FFR_QUARANTINE
127440266059SGregory Neil Shapiro 	bool quarantine;
127540266059SGregory Neil Shapiro #endif /* _FFR_QUARANTINE */
127606f25ae9SGregory Neil Shapiro 	int strsize;
127706f25ae9SGregory Neil Shapiro 	int rcptcount;
127840266059SGregory Neil Shapiro 	int ret;
127906f25ae9SGregory Neil Shapiro 	static int tobufsize = 0;
128006f25ae9SGregory Neil Shapiro 	static char *tobuf = NULL;
128140266059SGregory Neil Shapiro 	char *rpath;	/* translated return path */
1282c2aa98e2SPeter Wemm 	int mpvect[2];
1283c2aa98e2SPeter Wemm 	int rpvect[2];
128406f25ae9SGregory Neil Shapiro 	char *mxhosts[MAXMXHOSTS + 1];
1285c2aa98e2SPeter Wemm 	char *pv[MAXPV + 1];
1286c2aa98e2SPeter Wemm 	char buf[MAXNAME + 1];
128794c01205SGregory Neil Shapiro 	char cbuf[MAXPATHLEN];
1288c2aa98e2SPeter Wemm 
1289c2aa98e2SPeter Wemm 	errno = 0;
129006f25ae9SGregory Neil Shapiro 	if (!QS_IS_OK(to->q_state))
129106f25ae9SGregory Neil Shapiro 		return 0;
1292c2aa98e2SPeter Wemm 
1293c2aa98e2SPeter Wemm 	suidwarn = geteuid() == 0;
1294c2aa98e2SPeter Wemm 
1295c2aa98e2SPeter Wemm 	m = to->q_mailer;
1296c2aa98e2SPeter Wemm 	host = to->q_host;
1297c2aa98e2SPeter Wemm 	CurEnv = e;			/* just in case */
1298c2aa98e2SPeter Wemm 	e->e_statmsg = NULL;
1299c2aa98e2SPeter Wemm 	SmtpError[0] = '\0';
1300c2aa98e2SPeter Wemm 	xstart = curtime();
1301c2aa98e2SPeter Wemm 
1302c2aa98e2SPeter Wemm 	if (tTd(10, 1))
130340266059SGregory Neil Shapiro 		sm_dprintf("\n--deliver, id=%s, mailer=%s, host=`%s', first user=`%s'\n",
1304c2aa98e2SPeter Wemm 			e->e_id, m->m_name, host, to->q_user);
1305c2aa98e2SPeter Wemm 	if (tTd(10, 100))
130640266059SGregory Neil Shapiro 		printopenfds(false);
1307c2aa98e2SPeter Wemm 
1308c2aa98e2SPeter Wemm 	/*
130940266059SGregory Neil Shapiro 	**  Clear {client_*} macros if this is a bounce message to
1310c2aa98e2SPeter Wemm 	**  prevent rejection by check_compat ruleset.
1311c2aa98e2SPeter Wemm 	*/
1312c2aa98e2SPeter Wemm 
1313c2aa98e2SPeter Wemm 	if (bitset(EF_RESPONSE, e->e_flags))
1314c2aa98e2SPeter Wemm 	{
131540266059SGregory Neil Shapiro 		macdefine(&e->e_macro, A_PERM, macid("{client_name}"), "");
131640266059SGregory Neil Shapiro 		macdefine(&e->e_macro, A_PERM, macid("{client_addr}"), "");
131740266059SGregory Neil Shapiro 		macdefine(&e->e_macro, A_PERM, macid("{client_port}"), "");
131840266059SGregory Neil Shapiro 		macdefine(&e->e_macro, A_PERM, macid("{client_resolve}"), "");
1319c2aa98e2SPeter Wemm 	}
1320c2aa98e2SPeter Wemm 
132140266059SGregory Neil Shapiro 	SM_TRY
132240266059SGregory Neil Shapiro 	{
132340266059SGregory Neil Shapiro 	ADDRESS *skip_back = NULL;
132440266059SGregory Neil Shapiro 
1325c2aa98e2SPeter Wemm 	/*
1326c2aa98e2SPeter Wemm 	**  Do initial argv setup.
1327c2aa98e2SPeter Wemm 	**	Insert the mailer name.  Notice that $x expansion is
1328c2aa98e2SPeter Wemm 	**	NOT done on the mailer name.  Then, if the mailer has
1329c2aa98e2SPeter Wemm 	**	a picky -f flag, we insert it as appropriate.  This
1330c2aa98e2SPeter Wemm 	**	code does not check for 'pv' overflow; this places a
1331c2aa98e2SPeter Wemm 	**	manifest lower limit of 4 for MAXPV.
1332c2aa98e2SPeter Wemm 	**		The from address rewrite is expected to make
1333c2aa98e2SPeter Wemm 	**		the address relative to the other end.
1334c2aa98e2SPeter Wemm 	*/
1335c2aa98e2SPeter Wemm 
1336c2aa98e2SPeter Wemm 	/* rewrite from address, using rewriting rules */
1337c2aa98e2SPeter Wemm 	rcode = EX_OK;
1338c2aa98e2SPeter Wemm 	if (bitnset(M_UDBENVELOPE, e->e_from.q_mailer->m_flags))
1339c2aa98e2SPeter Wemm 		p = e->e_sender;
1340c2aa98e2SPeter Wemm 	else
1341c2aa98e2SPeter Wemm 		p = e->e_from.q_paddr;
134240266059SGregory Neil Shapiro 	rpath = remotename(p, m, RF_SENDERADDR|RF_CANONICAL, &rcode, e);
134340266059SGregory Neil Shapiro 	if (strlen(rpath) > MAXSHORTSTR)
1344c2aa98e2SPeter Wemm 	{
134540266059SGregory Neil Shapiro 		rpath = shortenstring(rpath, MAXSHORTSTR);
134640266059SGregory Neil Shapiro 
134740266059SGregory Neil Shapiro 		/* avoid bogus errno */
134840266059SGregory Neil Shapiro 		errno = 0;
134940266059SGregory Neil Shapiro 		syserr("remotename: huge return path %s", rpath);
1350c2aa98e2SPeter Wemm 	}
135140266059SGregory Neil Shapiro 	rpath = sm_rpool_strdup_x(e->e_rpool, rpath);
135240266059SGregory Neil Shapiro 	macdefine(&e->e_macro, A_PERM, 'g', rpath);
135340266059SGregory Neil Shapiro 	macdefine(&e->e_macro, A_PERM, 'h', host);
1354c2aa98e2SPeter Wemm 	Errors = 0;
1355c2aa98e2SPeter Wemm 	pvp = pv;
1356c2aa98e2SPeter Wemm 	*pvp++ = m->m_argv[0];
1357c2aa98e2SPeter Wemm 
1358c2aa98e2SPeter Wemm 	/* insert -f or -r flag as appropriate */
135906f25ae9SGregory Neil Shapiro 	if (FromFlag &&
136006f25ae9SGregory Neil Shapiro 	    (bitnset(M_FOPT, m->m_flags) ||
136106f25ae9SGregory Neil Shapiro 	     bitnset(M_ROPT, m->m_flags)))
1362c2aa98e2SPeter Wemm 	{
1363c2aa98e2SPeter Wemm 		if (bitnset(M_FOPT, m->m_flags))
1364c2aa98e2SPeter Wemm 			*pvp++ = "-f";
1365c2aa98e2SPeter Wemm 		else
1366c2aa98e2SPeter Wemm 			*pvp++ = "-r";
136740266059SGregory Neil Shapiro 		*pvp++ = rpath;
1368c2aa98e2SPeter Wemm 	}
1369c2aa98e2SPeter Wemm 
1370c2aa98e2SPeter Wemm 	/*
1371c2aa98e2SPeter Wemm 	**  Append the other fixed parts of the argv.  These run
1372c2aa98e2SPeter Wemm 	**  up to the first entry containing "$u".  There can only
1373c2aa98e2SPeter Wemm 	**  be one of these, and there are only a few more slots
1374c2aa98e2SPeter Wemm 	**  in the pv after it.
1375c2aa98e2SPeter Wemm 	*/
1376c2aa98e2SPeter Wemm 
1377c2aa98e2SPeter Wemm 	for (mvp = m->m_argv; (p = *++mvp) != NULL; )
1378c2aa98e2SPeter Wemm 	{
1379c2aa98e2SPeter Wemm 		/* can't use strchr here because of sign extension problems */
1380c2aa98e2SPeter Wemm 		while (*p != '\0')
1381c2aa98e2SPeter Wemm 		{
1382c2aa98e2SPeter Wemm 			if ((*p++ & 0377) == MACROEXPAND)
1383c2aa98e2SPeter Wemm 			{
1384c2aa98e2SPeter Wemm 				if (*p == 'u')
1385c2aa98e2SPeter Wemm 					break;
1386c2aa98e2SPeter Wemm 			}
1387c2aa98e2SPeter Wemm 		}
1388c2aa98e2SPeter Wemm 
1389c2aa98e2SPeter Wemm 		if (*p != '\0')
1390c2aa98e2SPeter Wemm 			break;
1391c2aa98e2SPeter Wemm 
1392c2aa98e2SPeter Wemm 		/* this entry is safe -- go ahead and process it */
1393c2aa98e2SPeter Wemm 		expand(*mvp, buf, sizeof buf, e);
139440266059SGregory Neil Shapiro 		*pvp++ = sm_rpool_strdup_x(e->e_rpool, buf);
1395c2aa98e2SPeter Wemm 		if (pvp >= &pv[MAXPV - 3])
1396c2aa98e2SPeter Wemm 		{
139706f25ae9SGregory Neil Shapiro 			syserr("554 5.3.5 Too many parameters to %s before $u",
139806f25ae9SGregory Neil Shapiro 			       pv[0]);
139940266059SGregory Neil Shapiro 			rcode = -1;
140040266059SGregory Neil Shapiro 			goto cleanup;
1401c2aa98e2SPeter Wemm 		}
1402c2aa98e2SPeter Wemm 	}
1403c2aa98e2SPeter Wemm 
1404c2aa98e2SPeter Wemm 	/*
1405c2aa98e2SPeter Wemm 	**  If we have no substitution for the user name in the argument
1406c2aa98e2SPeter Wemm 	**  list, we know that we must supply the names otherwise -- and
1407c2aa98e2SPeter Wemm 	**  SMTP is the answer!!
1408c2aa98e2SPeter Wemm 	*/
1409c2aa98e2SPeter Wemm 
1410c2aa98e2SPeter Wemm 	if (*mvp == NULL)
1411c2aa98e2SPeter Wemm 	{
1412602a2b1bSGregory Neil Shapiro 		/* running LMTP or SMTP */
141340266059SGregory Neil Shapiro 		clever = true;
1414c2aa98e2SPeter Wemm 		*pvp = NULL;
1415c2aa98e2SPeter Wemm 	}
1416602a2b1bSGregory Neil Shapiro 	else if (bitnset(M_LMTP, m->m_flags))
1417602a2b1bSGregory Neil Shapiro 	{
1418602a2b1bSGregory Neil Shapiro 		/* not running LMTP */
1419602a2b1bSGregory Neil Shapiro 		sm_syslog(LOG_ERR, NULL,
1420602a2b1bSGregory Neil Shapiro 			  "Warning: mailer %s: LMTP flag (F=z) turned off",
1421602a2b1bSGregory Neil Shapiro 			  m->m_name);
1422602a2b1bSGregory Neil Shapiro 		clrbitn(M_LMTP, m->m_flags);
1423602a2b1bSGregory Neil Shapiro 	}
1424c2aa98e2SPeter Wemm 
1425c2aa98e2SPeter Wemm 	/*
1426c2aa98e2SPeter Wemm 	**  At this point *mvp points to the argument with $u.  We
1427c2aa98e2SPeter Wemm 	**  run through our address list and append all the addresses
1428c2aa98e2SPeter Wemm 	**  we can.  If we run out of space, do not fret!  We can
1429c2aa98e2SPeter Wemm 	**  always send another copy later.
1430c2aa98e2SPeter Wemm 	*/
1431c2aa98e2SPeter Wemm 
143206f25ae9SGregory Neil Shapiro 	e->e_to = NULL;
143306f25ae9SGregory Neil Shapiro 	strsize = 2;
143406f25ae9SGregory Neil Shapiro 	rcptcount = 0;
1435c2aa98e2SPeter Wemm 	ctladdr = NULL;
143640266059SGregory Neil Shapiro 	if (firstto->q_signature == NULL)
143740266059SGregory Neil Shapiro 		firstto->q_signature = hostsignature(firstto->q_mailer,
143840266059SGregory Neil Shapiro 						     firstto->q_host);
143940266059SGregory Neil Shapiro 	firstsig = firstto->q_signature;
144040266059SGregory Neil Shapiro 
1441c2aa98e2SPeter Wemm 	for (; to != NULL; to = to->q_next)
1442c2aa98e2SPeter Wemm 	{
1443c2aa98e2SPeter Wemm 		/* avoid sending multiple recipients to dumb mailers */
144406f25ae9SGregory Neil Shapiro 		if (tochain != NULL && !bitnset(M_MUSER, m->m_flags))
144506f25ae9SGregory Neil Shapiro 			break;
1446c2aa98e2SPeter Wemm 
1447c2aa98e2SPeter Wemm 		/* if already sent or not for this host, don't send */
144840266059SGregory Neil Shapiro 		if (!QS_IS_OK(to->q_state)) /* already sent; look at next */
1449c2aa98e2SPeter Wemm 			continue;
1450c2aa98e2SPeter Wemm 
145140266059SGregory Neil Shapiro 		/*
145240266059SGregory Neil Shapiro 		**  Must be same mailer to keep grouping rcpts.
145340266059SGregory Neil Shapiro 		**  If mailers don't match: continue; sendqueue is not
145440266059SGregory Neil Shapiro 		**  sorted by mailers, so don't break;
145540266059SGregory Neil Shapiro 		*/
145640266059SGregory Neil Shapiro 
145740266059SGregory Neil Shapiro 		if (to->q_mailer != firstto->q_mailer)
145840266059SGregory Neil Shapiro 			continue;
145940266059SGregory Neil Shapiro 
146040266059SGregory Neil Shapiro 		if (to->q_signature == NULL) /* for safety */
146140266059SGregory Neil Shapiro 			to->q_signature = hostsignature(to->q_mailer,
146240266059SGregory Neil Shapiro 							to->q_host);
146340266059SGregory Neil Shapiro 
146440266059SGregory Neil Shapiro 		/*
146540266059SGregory Neil Shapiro 		**  This is for coincidental and tailcoat piggybacking messages
146640266059SGregory Neil Shapiro 		**  to the same mail host. While the signatures are identical
146740266059SGregory Neil Shapiro 		**  (that's the MX-RR's are identical) we can do coincidental
146840266059SGregory Neil Shapiro 		**  piggybacking. We try hard for coattail piggybacking
146940266059SGregory Neil Shapiro 		**  with the same mail host when the next recipient has the
147040266059SGregory Neil Shapiro 		**  same host at lowest preference. It may be that this
147140266059SGregory Neil Shapiro 		**  won't work out, so 'skip_back' is maintained if a backup
147240266059SGregory Neil Shapiro 		**  to coincidental piggybacking or full signature must happen.
147340266059SGregory Neil Shapiro 		*/
147440266059SGregory Neil Shapiro 
147540266059SGregory Neil Shapiro 		ret = firstto == to ? HS_MATCH_FULL :
147640266059SGregory Neil Shapiro 				      coloncmp(to->q_signature, firstsig);
147740266059SGregory Neil Shapiro 		if (ret == HS_MATCH_FULL)
147840266059SGregory Neil Shapiro 			skip_back = to;
147940266059SGregory Neil Shapiro 		else if (ret == HS_MATCH_NO)
148006f25ae9SGregory Neil Shapiro 			break;
148106f25ae9SGregory Neil Shapiro 
148240266059SGregory Neil Shapiro 		if (!clever)
148340266059SGregory Neil Shapiro 		{
148440266059SGregory Neil Shapiro 			/* avoid overflowing tobuf */
148540266059SGregory Neil Shapiro 			strsize += strlen(to->q_paddr) + 1;
148640266059SGregory Neil Shapiro 			if (strsize > TOBUFSIZE)
148740266059SGregory Neil Shapiro 				break;
148840266059SGregory Neil Shapiro 		}
148940266059SGregory Neil Shapiro 
149006f25ae9SGregory Neil Shapiro 		if (++rcptcount > to->q_mailer->m_maxrcpt)
149106f25ae9SGregory Neil Shapiro 			break;
1492c2aa98e2SPeter Wemm 
1493c2aa98e2SPeter Wemm 		if (tTd(10, 1))
1494c2aa98e2SPeter Wemm 		{
149540266059SGregory Neil Shapiro 			sm_dprintf("\nsend to ");
149640266059SGregory Neil Shapiro 			printaddr(to, false);
1497c2aa98e2SPeter Wemm 		}
1498c2aa98e2SPeter Wemm 
1499c2aa98e2SPeter Wemm 		/* compute effective uid/gid when sending */
1500c2aa98e2SPeter Wemm 		if (bitnset(M_RUNASRCPT, to->q_mailer->m_flags))
150140266059SGregory Neil Shapiro # if HASSETUSERCONTEXT
1502c2aa98e2SPeter Wemm 			contextaddr = ctladdr = getctladdr(to);
150340266059SGregory Neil Shapiro # else /* HASSETUSERCONTEXT */
150440266059SGregory Neil Shapiro 			ctladdr = getctladdr(to);
150540266059SGregory Neil Shapiro # endif /* HASSETUSERCONTEXT */
1506c2aa98e2SPeter Wemm 
1507c2aa98e2SPeter Wemm 		if (tTd(10, 2))
1508c2aa98e2SPeter Wemm 		{
150940266059SGregory Neil Shapiro 			sm_dprintf("ctladdr=");
151040266059SGregory Neil Shapiro 			printaddr(ctladdr, false);
1511c2aa98e2SPeter Wemm 		}
1512c2aa98e2SPeter Wemm 
1513c2aa98e2SPeter Wemm 		user = to->q_user;
1514c2aa98e2SPeter Wemm 		e->e_to = to->q_paddr;
1515c2aa98e2SPeter Wemm 
1516c2aa98e2SPeter Wemm 		/*
1517c2aa98e2SPeter Wemm 		**  Check to see that these people are allowed to
1518c2aa98e2SPeter Wemm 		**  talk to each other.
151942e5d165SGregory Neil Shapiro 		**  Check also for overflow of e_msgsize.
1520c2aa98e2SPeter Wemm 		*/
1521c2aa98e2SPeter Wemm 
152242e5d165SGregory Neil Shapiro 		if (m->m_maxsize != 0 &&
152342e5d165SGregory Neil Shapiro 		    (e->e_msgsize > m->m_maxsize || e->e_msgsize < 0))
1524c2aa98e2SPeter Wemm 		{
1525c2aa98e2SPeter Wemm 			e->e_flags |= EF_NO_BODY_RETN;
1526c2aa98e2SPeter Wemm 			if (bitnset(M_LOCALMAILER, to->q_mailer->m_flags))
1527c2aa98e2SPeter Wemm 				to->q_status = "5.2.3";
1528c2aa98e2SPeter Wemm 			else
1529c2aa98e2SPeter Wemm 				to->q_status = "5.3.4";
153040266059SGregory Neil Shapiro 
153106f25ae9SGregory Neil Shapiro 			/* set to->q_rstatus = NULL; or to the following? */
153206f25ae9SGregory Neil Shapiro 			usrerrenh(to->q_status,
153306f25ae9SGregory Neil Shapiro 				  "552 Message is too large; %ld bytes max",
153406f25ae9SGregory Neil Shapiro 				  m->m_maxsize);
153540266059SGregory Neil Shapiro 			markfailure(e, to, NULL, EX_UNAVAILABLE, false);
153606f25ae9SGregory Neil Shapiro 			giveresponse(EX_UNAVAILABLE, to->q_status, m,
153740266059SGregory Neil Shapiro 				     NULL, ctladdr, xstart, e, to);
1538c2aa98e2SPeter Wemm 			continue;
1539c2aa98e2SPeter Wemm 		}
1540602a2b1bSGregory Neil Shapiro 		SM_SET_H_ERRNO(0);
154140266059SGregory Neil Shapiro 		ovr = true;
1542c2aa98e2SPeter Wemm 
1543c2aa98e2SPeter Wemm 		/* do config file checking of compatibility */
154440266059SGregory Neil Shapiro #if _FFR_QUARANTINE
154540266059SGregory Neil Shapiro 		quarantine = (e->e_quarmsg != NULL);
154640266059SGregory Neil Shapiro #endif /* _FFR_QUARANTINE */
154706f25ae9SGregory Neil Shapiro 		rcode = rscheck("check_compat", e->e_from.q_paddr, to->q_paddr,
154840266059SGregory Neil Shapiro 				e, true, true, 3, NULL, e->e_id);
1549c2aa98e2SPeter Wemm 		if (rcode == EX_OK)
1550c2aa98e2SPeter Wemm 		{
1551065a643dSPeter Wemm 			/* do in-code checking if not discarding */
1552065a643dSPeter Wemm 			if (!bitset(EF_DISCARD, e->e_flags))
155306f25ae9SGregory Neil Shapiro 			{
1554c2aa98e2SPeter Wemm 				rcode = checkcompat(to, e);
155540266059SGregory Neil Shapiro 				ovr = false;
155606f25ae9SGregory Neil Shapiro 			}
1557c2aa98e2SPeter Wemm 		}
1558c2aa98e2SPeter Wemm 		if (rcode != EX_OK)
1559c2aa98e2SPeter Wemm 		{
156006f25ae9SGregory Neil Shapiro 			markfailure(e, to, NULL, rcode, ovr);
156106f25ae9SGregory Neil Shapiro 			giveresponse(rcode, to->q_status, m,
156240266059SGregory Neil Shapiro 				     NULL, ctladdr, xstart, e, to);
1563c2aa98e2SPeter Wemm 			continue;
1564c2aa98e2SPeter Wemm 		}
156540266059SGregory Neil Shapiro #if _FFR_QUARANTINE
156640266059SGregory Neil Shapiro 		if (!quarantine && e->e_quarmsg != NULL)
156740266059SGregory Neil Shapiro 		{
156840266059SGregory Neil Shapiro 			/*
156940266059SGregory Neil Shapiro 			**  check_compat or checkcompat() has tried
157040266059SGregory Neil Shapiro 			**  to quarantine but that isn't supported.
157140266059SGregory Neil Shapiro 			**  Revert the attempt.
157240266059SGregory Neil Shapiro 			*/
157340266059SGregory Neil Shapiro 
157440266059SGregory Neil Shapiro 			e->e_quarmsg = NULL;
157540266059SGregory Neil Shapiro 			macdefine(&e->e_macro, A_PERM,
157640266059SGregory Neil Shapiro 				  macid("{quarantine}"), "");
157740266059SGregory Neil Shapiro 		}
157840266059SGregory Neil Shapiro #endif /* _FFR_QUARANTINE */
1579065a643dSPeter Wemm 		if (bitset(EF_DISCARD, e->e_flags))
1580065a643dSPeter Wemm 		{
1581065a643dSPeter Wemm 			if (tTd(10, 5))
1582065a643dSPeter Wemm 			{
158340266059SGregory Neil Shapiro 				sm_dprintf("deliver: discarding recipient ");
158440266059SGregory Neil Shapiro 				printaddr(to, false);
1585065a643dSPeter Wemm 			}
1586065a643dSPeter Wemm 
158706f25ae9SGregory Neil Shapiro 			/* pretend the message was sent */
158806f25ae9SGregory Neil Shapiro 			/* XXX should we log something here? */
158906f25ae9SGregory Neil Shapiro 			to->q_state = QS_DISCARDED;
159006f25ae9SGregory Neil Shapiro 
1591065a643dSPeter Wemm 			/*
1592065a643dSPeter Wemm 			**  Remove discard bit to prevent discard of
159306f25ae9SGregory Neil Shapiro 			**  future recipients.  This is safe because the
159406f25ae9SGregory Neil Shapiro 			**  true "global discard" has been handled before
159506f25ae9SGregory Neil Shapiro 			**  we get here.
1596065a643dSPeter Wemm 			*/
1597065a643dSPeter Wemm 
159806f25ae9SGregory Neil Shapiro 			e->e_flags &= ~EF_DISCARD;
1599065a643dSPeter Wemm 			continue;
1600065a643dSPeter Wemm 		}
1601c2aa98e2SPeter Wemm 
1602c2aa98e2SPeter Wemm 		/*
1603c2aa98e2SPeter Wemm 		**  Strip quote bits from names if the mailer is dumb
1604c2aa98e2SPeter Wemm 		**	about them.
1605c2aa98e2SPeter Wemm 		*/
1606c2aa98e2SPeter Wemm 
1607c2aa98e2SPeter Wemm 		if (bitnset(M_STRIPQ, m->m_flags))
1608c2aa98e2SPeter Wemm 		{
1609c2aa98e2SPeter Wemm 			stripquotes(user);
1610c2aa98e2SPeter Wemm 			stripquotes(host);
1611c2aa98e2SPeter Wemm 		}
1612c2aa98e2SPeter Wemm 
1613c2aa98e2SPeter Wemm 		/* hack attack -- delivermail compatibility */
1614c2aa98e2SPeter Wemm 		if (m == ProgMailer && *user == '|')
1615c2aa98e2SPeter Wemm 			user++;
1616c2aa98e2SPeter Wemm 
1617c2aa98e2SPeter Wemm 		/*
1618c2aa98e2SPeter Wemm 		**  If an error message has already been given, don't
1619c2aa98e2SPeter Wemm 		**	bother to send to this address.
1620c2aa98e2SPeter Wemm 		**
1621c2aa98e2SPeter Wemm 		**	>>>>>>>>>> This clause assumes that the local mailer
1622c2aa98e2SPeter Wemm 		**	>> NOTE >> cannot do any further aliasing; that
1623c2aa98e2SPeter Wemm 		**	>>>>>>>>>> function is subsumed by sendmail.
1624c2aa98e2SPeter Wemm 		*/
1625c2aa98e2SPeter Wemm 
162606f25ae9SGregory Neil Shapiro 		if (!QS_IS_OK(to->q_state))
1627c2aa98e2SPeter Wemm 			continue;
1628c2aa98e2SPeter Wemm 
1629c2aa98e2SPeter Wemm 		/*
1630c2aa98e2SPeter Wemm 		**  See if this user name is "special".
1631c2aa98e2SPeter Wemm 		**	If the user name has a slash in it, assume that this
1632c2aa98e2SPeter Wemm 		**	is a file -- send it off without further ado.  Note
1633c2aa98e2SPeter Wemm 		**	that this type of addresses is not processed along
1634c2aa98e2SPeter Wemm 		**	with the others, so we fudge on the To person.
1635c2aa98e2SPeter Wemm 		*/
1636c2aa98e2SPeter Wemm 
1637c2aa98e2SPeter Wemm 		if (strcmp(m->m_mailer, "[FILE]") == 0)
1638c2aa98e2SPeter Wemm 		{
163940266059SGregory Neil Shapiro 			macdefine(&e->e_macro, A_PERM, 'u', user);
1640c2aa98e2SPeter Wemm 			p = to->q_home;
1641c2aa98e2SPeter Wemm 			if (p == NULL && ctladdr != NULL)
1642c2aa98e2SPeter Wemm 				p = ctladdr->q_home;
164340266059SGregory Neil Shapiro 			macdefine(&e->e_macro, A_PERM, 'z', p);
1644c2aa98e2SPeter Wemm 			expand(m->m_argv[1], buf, sizeof buf, e);
1645c2aa98e2SPeter Wemm 			if (strlen(buf) > 0)
1646c2aa98e2SPeter Wemm 				rcode = mailfile(buf, m, ctladdr, SFF_CREAT, e);
1647c2aa98e2SPeter Wemm 			else
1648c2aa98e2SPeter Wemm 			{
1649c2aa98e2SPeter Wemm 				syserr("empty filename specification for mailer %s",
1650c2aa98e2SPeter Wemm 				       m->m_name);
1651c2aa98e2SPeter Wemm 				rcode = EX_CONFIG;
1652c2aa98e2SPeter Wemm 			}
165306f25ae9SGregory Neil Shapiro 			giveresponse(rcode, to->q_status, m, NULL,
165440266059SGregory Neil Shapiro 				     ctladdr, xstart, e, to);
165540266059SGregory Neil Shapiro 			markfailure(e, to, NULL, rcode, true);
1656c2aa98e2SPeter Wemm 			e->e_nsent++;
1657c2aa98e2SPeter Wemm 			if (rcode == EX_OK)
1658c2aa98e2SPeter Wemm 			{
165906f25ae9SGregory Neil Shapiro 				to->q_state = QS_SENT;
1660c2aa98e2SPeter Wemm 				if (bitnset(M_LOCALMAILER, m->m_flags) &&
1661c2aa98e2SPeter Wemm 				    bitset(QPINGONSUCCESS, to->q_flags))
1662c2aa98e2SPeter Wemm 				{
1663c2aa98e2SPeter Wemm 					to->q_flags |= QDELIVERED;
1664c2aa98e2SPeter Wemm 					to->q_status = "2.1.5";
166540266059SGregory Neil Shapiro 					(void) sm_io_fprintf(e->e_xfp,
166640266059SGregory Neil Shapiro 							     SM_TIME_DEFAULT,
166740266059SGregory Neil Shapiro 							     "%s... Successfully delivered\n",
1668c2aa98e2SPeter Wemm 							     to->q_paddr);
1669c2aa98e2SPeter Wemm 				}
1670c2aa98e2SPeter Wemm 			}
1671c2aa98e2SPeter Wemm 			to->q_statdate = curtime();
167240266059SGregory Neil Shapiro 			markstats(e, to, STATS_NORMAL);
1673c2aa98e2SPeter Wemm 			continue;
1674c2aa98e2SPeter Wemm 		}
1675c2aa98e2SPeter Wemm 
1676c2aa98e2SPeter Wemm 		/*
1677c2aa98e2SPeter Wemm 		**  Address is verified -- add this user to mailer
1678c2aa98e2SPeter Wemm 		**  argv, and add it to the print list of recipients.
1679c2aa98e2SPeter Wemm 		*/
1680c2aa98e2SPeter Wemm 
1681c2aa98e2SPeter Wemm 		/* link together the chain of recipients */
1682c2aa98e2SPeter Wemm 		to->q_tchain = tochain;
1683c2aa98e2SPeter Wemm 		tochain = to;
168406f25ae9SGregory Neil Shapiro 		e->e_to = "[CHAIN]";
168506f25ae9SGregory Neil Shapiro 
168640266059SGregory Neil Shapiro 		macdefine(&e->e_macro, A_PERM, 'u', user);  /* to user */
1687c2aa98e2SPeter Wemm 		p = to->q_home;
1688c2aa98e2SPeter Wemm 		if (p == NULL && ctladdr != NULL)
1689c2aa98e2SPeter Wemm 			p = ctladdr->q_home;
169040266059SGregory Neil Shapiro 		macdefine(&e->e_macro, A_PERM, 'z', p);  /* user's home */
1691c2aa98e2SPeter Wemm 
169206f25ae9SGregory Neil Shapiro 		/* set the ${dsn_notify} macro if applicable */
169306f25ae9SGregory Neil Shapiro 		if (bitset(QHASNOTIFY, to->q_flags))
169406f25ae9SGregory Neil Shapiro 		{
169506f25ae9SGregory Neil Shapiro 			char notify[MAXLINE];
169606f25ae9SGregory Neil Shapiro 
169706f25ae9SGregory Neil Shapiro 			notify[0] = '\0';
169806f25ae9SGregory Neil Shapiro 			if (bitset(QPINGONSUCCESS, to->q_flags))
169940266059SGregory Neil Shapiro 				(void) sm_strlcat(notify, "SUCCESS,",
170006f25ae9SGregory Neil Shapiro 						  sizeof notify);
170106f25ae9SGregory Neil Shapiro 			if (bitset(QPINGONFAILURE, to->q_flags))
170240266059SGregory Neil Shapiro 				(void) sm_strlcat(notify, "FAILURE,",
170306f25ae9SGregory Neil Shapiro 						  sizeof notify);
170406f25ae9SGregory Neil Shapiro 			if (bitset(QPINGONDELAY, to->q_flags))
170540266059SGregory Neil Shapiro 				(void) sm_strlcat(notify, "DELAY,",
170640266059SGregory Neil Shapiro 						  sizeof notify);
170706f25ae9SGregory Neil Shapiro 
170806f25ae9SGregory Neil Shapiro 			/* Set to NEVER or drop trailing comma */
170906f25ae9SGregory Neil Shapiro 			if (notify[0] == '\0')
171040266059SGregory Neil Shapiro 				(void) sm_strlcat(notify, "NEVER",
171140266059SGregory Neil Shapiro 						  sizeof notify);
171206f25ae9SGregory Neil Shapiro 			else
171306f25ae9SGregory Neil Shapiro 				notify[strlen(notify) - 1] = '\0';
171406f25ae9SGregory Neil Shapiro 
171540266059SGregory Neil Shapiro 			macdefine(&e->e_macro, A_TEMP,
171640266059SGregory Neil Shapiro 				macid("{dsn_notify}"), notify);
171706f25ae9SGregory Neil Shapiro 		}
171806f25ae9SGregory Neil Shapiro 		else
171940266059SGregory Neil Shapiro 			macdefine(&e->e_macro, A_PERM,
172040266059SGregory Neil Shapiro 				macid("{dsn_notify}"), NULL);
172106f25ae9SGregory Neil Shapiro 
1722c2aa98e2SPeter Wemm 		/*
1723c2aa98e2SPeter Wemm 		**  Expand out this user into argument list.
1724c2aa98e2SPeter Wemm 		*/
1725c2aa98e2SPeter Wemm 
1726c2aa98e2SPeter Wemm 		if (!clever)
1727c2aa98e2SPeter Wemm 		{
1728c2aa98e2SPeter Wemm 			expand(*mvp, buf, sizeof buf, e);
172940266059SGregory Neil Shapiro 			*pvp++ = sm_rpool_strdup_x(e->e_rpool, buf);
1730c2aa98e2SPeter Wemm 			if (pvp >= &pv[MAXPV - 2])
1731c2aa98e2SPeter Wemm 			{
1732c2aa98e2SPeter Wemm 				/* allow some space for trailing parms */
1733c2aa98e2SPeter Wemm 				break;
1734c2aa98e2SPeter Wemm 			}
1735c2aa98e2SPeter Wemm 		}
1736c2aa98e2SPeter Wemm 	}
1737c2aa98e2SPeter Wemm 
1738c2aa98e2SPeter Wemm 	/* see if any addresses still exist */
173906f25ae9SGregory Neil Shapiro 	if (tochain == NULL)
1740c2aa98e2SPeter Wemm 	{
174140266059SGregory Neil Shapiro 		rcode = 0;
174240266059SGregory Neil Shapiro 		goto cleanup;
1743c2aa98e2SPeter Wemm 	}
1744c2aa98e2SPeter Wemm 
1745c2aa98e2SPeter Wemm 	/* print out messages as full list */
174640266059SGregory Neil Shapiro 	strsize = 1;
174706f25ae9SGregory Neil Shapiro 	for (to = tochain; to != NULL; to = to->q_tchain)
174840266059SGregory Neil Shapiro 		strsize += strlen(to->q_paddr) + 1;
174940266059SGregory Neil Shapiro 	if (strsize < TOBUFSIZE)
175040266059SGregory Neil Shapiro 		strsize = TOBUFSIZE;
175140266059SGregory Neil Shapiro 	if (strsize > tobufsize)
175206f25ae9SGregory Neil Shapiro 	{
175340266059SGregory Neil Shapiro 		SM_FREE_CLR(tobuf);
175440266059SGregory Neil Shapiro 		tobuf = sm_pmalloc_x(strsize);
175540266059SGregory Neil Shapiro 		tobufsize = strsize;
175606f25ae9SGregory Neil Shapiro 	}
175740266059SGregory Neil Shapiro 	p = tobuf;
175840266059SGregory Neil Shapiro 	*p = '\0';
175906f25ae9SGregory Neil Shapiro 	for (to = tochain; to != NULL; to = to->q_tchain)
176006f25ae9SGregory Neil Shapiro 	{
176140266059SGregory Neil Shapiro 		(void) sm_strlcpyn(p, tobufsize - (p - tobuf), 2,
176240266059SGregory Neil Shapiro 				   ",", to->q_paddr);
176340266059SGregory Neil Shapiro 		p += strlen(p);
176406f25ae9SGregory Neil Shapiro 	}
1765c2aa98e2SPeter Wemm 	e->e_to = tobuf + 1;
1766c2aa98e2SPeter Wemm 
1767c2aa98e2SPeter Wemm 	/*
1768c2aa98e2SPeter Wemm 	**  Fill out any parameters after the $u parameter.
1769c2aa98e2SPeter Wemm 	*/
1770c2aa98e2SPeter Wemm 
177140266059SGregory Neil Shapiro 	if (!clever)
177240266059SGregory Neil Shapiro 	{
177340266059SGregory Neil Shapiro 		while (*++mvp != NULL)
1774c2aa98e2SPeter Wemm 		{
1775c2aa98e2SPeter Wemm 			expand(*mvp, buf, sizeof buf, e);
177640266059SGregory Neil Shapiro 			*pvp++ = sm_rpool_strdup_x(e->e_rpool, buf);
1777c2aa98e2SPeter Wemm 			if (pvp >= &pv[MAXPV])
177806f25ae9SGregory Neil Shapiro 				syserr("554 5.3.0 deliver: pv overflow after $u for %s",
177906f25ae9SGregory Neil Shapiro 				       pv[0]);
1780c2aa98e2SPeter Wemm 		}
178140266059SGregory Neil Shapiro 	}
1782c2aa98e2SPeter Wemm 	*pvp++ = NULL;
1783c2aa98e2SPeter Wemm 
1784c2aa98e2SPeter Wemm 	/*
1785c2aa98e2SPeter Wemm 	**  Call the mailer.
1786c2aa98e2SPeter Wemm 	**	The argument vector gets built, pipes
1787c2aa98e2SPeter Wemm 	**	are created as necessary, and we fork & exec as
1788c2aa98e2SPeter Wemm 	**	appropriate.
1789c2aa98e2SPeter Wemm 	**	If we are running SMTP, we just need to clean up.
1790c2aa98e2SPeter Wemm 	*/
1791c2aa98e2SPeter Wemm 
1792c2aa98e2SPeter Wemm 	/* XXX this seems a bit wierd */
1793c2aa98e2SPeter Wemm 	if (ctladdr == NULL && m != ProgMailer && m != FileMailer &&
1794c2aa98e2SPeter Wemm 	    bitset(QGOODUID, e->e_from.q_flags))
1795c2aa98e2SPeter Wemm 		ctladdr = &e->e_from;
1796c2aa98e2SPeter Wemm 
1797c2aa98e2SPeter Wemm #if NAMED_BIND
1798c2aa98e2SPeter Wemm 	if (ConfigLevel < 2)
1799c2aa98e2SPeter Wemm 		_res.options &= ~(RES_DEFNAMES | RES_DNSRCH);	/* XXX */
180006f25ae9SGregory Neil Shapiro #endif /* NAMED_BIND */
1801c2aa98e2SPeter Wemm 
1802c2aa98e2SPeter Wemm 	if (tTd(11, 1))
1803c2aa98e2SPeter Wemm 	{
180440266059SGregory Neil Shapiro 		sm_dprintf("openmailer:");
1805c2aa98e2SPeter Wemm 		printav(pv);
1806c2aa98e2SPeter Wemm 	}
1807c2aa98e2SPeter Wemm 	errno = 0;
1808602a2b1bSGregory Neil Shapiro 	SM_SET_H_ERRNO(0);
1809c2aa98e2SPeter Wemm 	CurHostName = NULL;
1810c2aa98e2SPeter Wemm 
1811c2aa98e2SPeter Wemm 	/*
1812c2aa98e2SPeter Wemm 	**  Deal with the special case of mail handled through an IPC
1813c2aa98e2SPeter Wemm 	**  connection.
1814c2aa98e2SPeter Wemm 	**	In this case we don't actually fork.  We must be
1815c2aa98e2SPeter Wemm 	**	running SMTP for this to work.  We will return a
1816c2aa98e2SPeter Wemm 	**	zero pid to indicate that we are running IPC.
1817c2aa98e2SPeter Wemm 	**  We also handle a debug version that just talks to stdin/out.
1818c2aa98e2SPeter Wemm 	*/
1819c2aa98e2SPeter Wemm 
1820c2aa98e2SPeter Wemm 	curhost = NULL;
1821c2aa98e2SPeter Wemm 	SmtpPhase = NULL;
1822c2aa98e2SPeter Wemm 	mci = NULL;
1823c2aa98e2SPeter Wemm 
1824c2aa98e2SPeter Wemm #if XDEBUG
1825c2aa98e2SPeter Wemm 	{
1826c2aa98e2SPeter Wemm 		char wbuf[MAXLINE];
1827c2aa98e2SPeter Wemm 
1828c2aa98e2SPeter Wemm 		/* make absolutely certain 0, 1, and 2 are in use */
182940266059SGregory Neil Shapiro 		(void) sm_snprintf(wbuf, sizeof wbuf, "%s... openmailer(%s)",
183040266059SGregory Neil Shapiro 				   shortenstring(e->e_to, MAXSHORTSTR),
183140266059SGregory Neil Shapiro 				   m->m_name);
1832c2aa98e2SPeter Wemm 		checkfd012(wbuf);
1833c2aa98e2SPeter Wemm 	}
183406f25ae9SGregory Neil Shapiro #endif /* XDEBUG */
1835c2aa98e2SPeter Wemm 
1836c2aa98e2SPeter Wemm 	/* check for 8-bit available */
1837c2aa98e2SPeter Wemm 	if (bitset(EF_HAS8BIT, e->e_flags) &&
1838c2aa98e2SPeter Wemm 	    bitnset(M_7BITS, m->m_flags) &&
1839c2aa98e2SPeter Wemm 	    (bitset(EF_DONT_MIME, e->e_flags) ||
1840c2aa98e2SPeter Wemm 	     !(bitset(MM_MIME8BIT, MimeMode) ||
1841c2aa98e2SPeter Wemm 	       (bitset(EF_IS_MIME, e->e_flags) &&
1842c2aa98e2SPeter Wemm 		bitset(MM_CVTMIME, MimeMode)))))
1843c2aa98e2SPeter Wemm 	{
1844c2aa98e2SPeter Wemm 		e->e_status = "5.6.3";
184506f25ae9SGregory Neil Shapiro 		usrerrenh(e->e_status,
184606f25ae9SGregory Neil Shapiro 			  "554 Cannot send 8-bit data to 7-bit destination");
184706f25ae9SGregory Neil Shapiro 		rcode = EX_DATAERR;
1848c2aa98e2SPeter Wemm 		goto give_up;
1849c2aa98e2SPeter Wemm 	}
1850c2aa98e2SPeter Wemm 
1851c2aa98e2SPeter Wemm 	if (tTd(62, 8))
1852c2aa98e2SPeter Wemm 		checkfds("before delivery");
1853c2aa98e2SPeter Wemm 
1854c2aa98e2SPeter Wemm 	/* check for Local Person Communication -- not for mortals!!! */
1855c2aa98e2SPeter Wemm 	if (strcmp(m->m_mailer, "[LPC]") == 0)
1856c2aa98e2SPeter Wemm 	{
185740266059SGregory Neil Shapiro #if _FFR_CACHE_LPC
185840266059SGregory Neil Shapiro 		if (clever)
185940266059SGregory Neil Shapiro 		{
186040266059SGregory Neil Shapiro 			/* flush any expired connections */
186140266059SGregory Neil Shapiro 			(void) mci_scan(NULL);
186240266059SGregory Neil Shapiro 
186340266059SGregory Neil Shapiro 			/* try to get a cached connection or just a slot */
186440266059SGregory Neil Shapiro 			mci = mci_get(m->m_name, m);
186540266059SGregory Neil Shapiro 			if (mci->mci_host == NULL)
186640266059SGregory Neil Shapiro 				mci->mci_host = m->m_name;
186740266059SGregory Neil Shapiro 			CurHostName = mci->mci_host;
186840266059SGregory Neil Shapiro 			if (mci->mci_state != MCIS_CLOSED)
186940266059SGregory Neil Shapiro 			{
187040266059SGregory Neil Shapiro 				message("Using cached SMTP/LPC connection for %s...",
187140266059SGregory Neil Shapiro 					m->m_name);
187240266059SGregory Neil Shapiro 				mci->mci_deliveries++;
187340266059SGregory Neil Shapiro 				goto do_transfer;
187440266059SGregory Neil Shapiro 			}
187540266059SGregory Neil Shapiro 		}
187640266059SGregory Neil Shapiro 		else
187740266059SGregory Neil Shapiro 		{
187840266059SGregory Neil Shapiro 			mci = mci_new(e->e_rpool);
187940266059SGregory Neil Shapiro 		}
188040266059SGregory Neil Shapiro 		mci->mci_in = smioin;
188140266059SGregory Neil Shapiro 		mci->mci_out = smioout;
188240266059SGregory Neil Shapiro 		mci->mci_mailer = m;
188340266059SGregory Neil Shapiro 		mci->mci_host = m->m_name;
188440266059SGregory Neil Shapiro 		if (clever)
188540266059SGregory Neil Shapiro 		{
188640266059SGregory Neil Shapiro 			mci->mci_state = MCIS_OPENING;
188740266059SGregory Neil Shapiro 			mci_cache(mci);
188840266059SGregory Neil Shapiro 		}
188940266059SGregory Neil Shapiro 		else
189040266059SGregory Neil Shapiro 			mci->mci_state = MCIS_OPEN;
189140266059SGregory Neil Shapiro #else /* _FFR_CACHE_LPC */
189240266059SGregory Neil Shapiro 		mci = mci_new(e->e_rpool);
189340266059SGregory Neil Shapiro 		mci->mci_in = smioin;
189440266059SGregory Neil Shapiro 		mci->mci_out = smioout;
1895c2aa98e2SPeter Wemm 		mci->mci_state = clever ? MCIS_OPENING : MCIS_OPEN;
1896c2aa98e2SPeter Wemm 		mci->mci_mailer = m;
189740266059SGregory Neil Shapiro #endif /* _FFR_CACHE_LPC */
1898c2aa98e2SPeter Wemm 	}
189940266059SGregory Neil Shapiro 	else if (strcmp(m->m_mailer, "[IPC]") == 0)
1900c2aa98e2SPeter Wemm 	{
1901c2aa98e2SPeter Wemm 		register int i;
1902c2aa98e2SPeter Wemm 
1903c2aa98e2SPeter Wemm 		if (pv[0] == NULL || pv[1] == NULL || pv[1][0] == '\0')
1904c2aa98e2SPeter Wemm 		{
190506f25ae9SGregory Neil Shapiro 			syserr("null destination for %s mailer", m->m_mailer);
1906c2aa98e2SPeter Wemm 			rcode = EX_CONFIG;
1907c2aa98e2SPeter Wemm 			goto give_up;
1908c2aa98e2SPeter Wemm 		}
1909c2aa98e2SPeter Wemm 
191006f25ae9SGregory Neil Shapiro # if NETUNIX
191106f25ae9SGregory Neil Shapiro 		if (strcmp(pv[0], "FILE") == 0)
191206f25ae9SGregory Neil Shapiro 		{
191306f25ae9SGregory Neil Shapiro 			curhost = CurHostName = "localhost";
191406f25ae9SGregory Neil Shapiro 			mux_path = pv[1];
191506f25ae9SGregory Neil Shapiro 		}
191606f25ae9SGregory Neil Shapiro 		else
191706f25ae9SGregory Neil Shapiro # endif /* NETUNIX */
191806f25ae9SGregory Neil Shapiro 		{
1919c2aa98e2SPeter Wemm 			CurHostName = pv[1];
192006f25ae9SGregory Neil Shapiro 			curhost = hostsignature(m, pv[1]);
192106f25ae9SGregory Neil Shapiro 		}
1922c2aa98e2SPeter Wemm 
1923c2aa98e2SPeter Wemm 		if (curhost == NULL || curhost[0] == '\0')
1924c2aa98e2SPeter Wemm 		{
1925c2aa98e2SPeter Wemm 			syserr("null host signature for %s", pv[1]);
1926c2aa98e2SPeter Wemm 			rcode = EX_CONFIG;
1927c2aa98e2SPeter Wemm 			goto give_up;
1928c2aa98e2SPeter Wemm 		}
1929c2aa98e2SPeter Wemm 
1930c2aa98e2SPeter Wemm 		if (!clever)
1931c2aa98e2SPeter Wemm 		{
193206f25ae9SGregory Neil Shapiro 			syserr("554 5.3.5 non-clever IPC");
1933c2aa98e2SPeter Wemm 			rcode = EX_CONFIG;
1934c2aa98e2SPeter Wemm 			goto give_up;
1935c2aa98e2SPeter Wemm 		}
193606f25ae9SGregory Neil Shapiro 		if (pv[2] != NULL
193706f25ae9SGregory Neil Shapiro # if NETUNIX
193806f25ae9SGregory Neil Shapiro 		    && mux_path == NULL
193906f25ae9SGregory Neil Shapiro # endif /* NETUNIX */
194006f25ae9SGregory Neil Shapiro 		    )
1941c2aa98e2SPeter Wemm 		{
194240266059SGregory Neil Shapiro 			port = htons((unsigned short) atoi(pv[2]));
1943c2aa98e2SPeter Wemm 			if (port == 0)
1944c2aa98e2SPeter Wemm 			{
194506f25ae9SGregory Neil Shapiro # ifdef NO_GETSERVBYNAME
194606f25ae9SGregory Neil Shapiro 				syserr("Invalid port number: %s", pv[2]);
194706f25ae9SGregory Neil Shapiro # else /* NO_GETSERVBYNAME */
1948c2aa98e2SPeter Wemm 				struct servent *sp = getservbyname(pv[2], "tcp");
1949c2aa98e2SPeter Wemm 
1950c2aa98e2SPeter Wemm 				if (sp == NULL)
1951c2aa98e2SPeter Wemm 					syserr("Service %s unknown", pv[2]);
1952c2aa98e2SPeter Wemm 				else
1953c2aa98e2SPeter Wemm 					port = sp->s_port;
195406f25ae9SGregory Neil Shapiro # endif /* NO_GETSERVBYNAME */
1955c2aa98e2SPeter Wemm 			}
1956c2aa98e2SPeter Wemm 		}
1957c2aa98e2SPeter Wemm 
195806f25ae9SGregory Neil Shapiro 		nummxhosts = parse_hostsignature(curhost, mxhosts, m);
195940266059SGregory Neil Shapiro 		if (TimeOuts.to_aconnect > 0)
196040266059SGregory Neil Shapiro 			enough = curtime() + TimeOuts.to_aconnect;
196106f25ae9SGregory Neil Shapiro tryhost:
196206f25ae9SGregory Neil Shapiro 		while (hostnum < nummxhosts)
196306f25ae9SGregory Neil Shapiro 		{
196406f25ae9SGregory Neil Shapiro 			char sep = ':';
196506f25ae9SGregory Neil Shapiro 			char *endp;
196606f25ae9SGregory Neil Shapiro 			static char hostbuf[MAXNAME + 1];
196706f25ae9SGregory Neil Shapiro 
196806f25ae9SGregory Neil Shapiro # if NETINET6
196906f25ae9SGregory Neil Shapiro 			if (*mxhosts[hostnum] == '[')
197006f25ae9SGregory Neil Shapiro 			{
197106f25ae9SGregory Neil Shapiro 				endp = strchr(mxhosts[hostnum] + 1, ']');
197206f25ae9SGregory Neil Shapiro 				if (endp != NULL)
197306f25ae9SGregory Neil Shapiro 					endp = strpbrk(endp + 1, ":,");
197406f25ae9SGregory Neil Shapiro 			}
197506f25ae9SGregory Neil Shapiro 			else
197606f25ae9SGregory Neil Shapiro 				endp = strpbrk(mxhosts[hostnum], ":,");
197706f25ae9SGregory Neil Shapiro # else /* NETINET6 */
197806f25ae9SGregory Neil Shapiro 			endp = strpbrk(mxhosts[hostnum], ":,");
197906f25ae9SGregory Neil Shapiro # endif /* NETINET6 */
198006f25ae9SGregory Neil Shapiro 			if (endp != NULL)
198106f25ae9SGregory Neil Shapiro 			{
198206f25ae9SGregory Neil Shapiro 				sep = *endp;
198306f25ae9SGregory Neil Shapiro 				*endp = '\0';
198406f25ae9SGregory Neil Shapiro 			}
198506f25ae9SGregory Neil Shapiro 
198640266059SGregory Neil Shapiro 			if (hostnum == 1 && skip_back != NULL)
198740266059SGregory Neil Shapiro 			{
198840266059SGregory Neil Shapiro 				/*
198940266059SGregory Neil Shapiro 				**  Coattail piggybacking is no longer an
199040266059SGregory Neil Shapiro 				**  option with the mail host next to be tried
199140266059SGregory Neil Shapiro 				**  no longer the lowest MX preference
199240266059SGregory Neil Shapiro 				**  (hostnum == 1 meaning we're on the second
199340266059SGregory Neil Shapiro 				**  preference). We do not try to coattail
199440266059SGregory Neil Shapiro 				**  piggyback more than the first MX preference.
199540266059SGregory Neil Shapiro 				**  Revert 'tochain' to last location for
199640266059SGregory Neil Shapiro 				**  coincidental piggybacking. This works this
199740266059SGregory Neil Shapiro 				**  easily because the q_tchain kept getting
199840266059SGregory Neil Shapiro 				**  added to the top of the linked list.
199940266059SGregory Neil Shapiro 				*/
200040266059SGregory Neil Shapiro 
200140266059SGregory Neil Shapiro 				tochain = skip_back;
200240266059SGregory Neil Shapiro 			}
200340266059SGregory Neil Shapiro 
200406f25ae9SGregory Neil Shapiro 			if (*mxhosts[hostnum] == '\0')
2005c2aa98e2SPeter Wemm 			{
2006c2aa98e2SPeter Wemm 				syserr("deliver: null host name in signature");
200706f25ae9SGregory Neil Shapiro 				hostnum++;
200806f25ae9SGregory Neil Shapiro 				if (endp != NULL)
200906f25ae9SGregory Neil Shapiro 					*endp = sep;
2010c2aa98e2SPeter Wemm 				continue;
2011c2aa98e2SPeter Wemm 			}
201240266059SGregory Neil Shapiro 			(void) sm_strlcpy(hostbuf, mxhosts[hostnum],
201306f25ae9SGregory Neil Shapiro 					  sizeof hostbuf);
201406f25ae9SGregory Neil Shapiro 			hostnum++;
201506f25ae9SGregory Neil Shapiro 			if (endp != NULL)
201606f25ae9SGregory Neil Shapiro 				*endp = sep;
2017c2aa98e2SPeter Wemm 
2018c2aa98e2SPeter Wemm 			/* see if we already know that this host is fried */
2019c2aa98e2SPeter Wemm 			CurHostName = hostbuf;
2020c2aa98e2SPeter Wemm 			mci = mci_get(hostbuf, m);
2021c2aa98e2SPeter Wemm 			if (mci->mci_state != MCIS_CLOSED)
2022c2aa98e2SPeter Wemm 			{
202340266059SGregory Neil Shapiro 				char *type;
202440266059SGregory Neil Shapiro 
2025c2aa98e2SPeter Wemm 				if (tTd(11, 1))
2026c2aa98e2SPeter Wemm 				{
202740266059SGregory Neil Shapiro 					sm_dprintf("openmailer: ");
202840266059SGregory Neil Shapiro 					mci_dump(mci, false);
2029c2aa98e2SPeter Wemm 				}
2030c2aa98e2SPeter Wemm 				CurHostName = mci->mci_host;
203140266059SGregory Neil Shapiro 				if (bitnset(M_LMTP, m->m_flags))
203240266059SGregory Neil Shapiro 					type = "L";
203340266059SGregory Neil Shapiro 				else if (bitset(MCIF_ESMTP, mci->mci_flags))
203440266059SGregory Neil Shapiro 					type = "ES";
203540266059SGregory Neil Shapiro 				else
203640266059SGregory Neil Shapiro 					type = "S";
203740266059SGregory Neil Shapiro 				message("Using cached %sMTP connection to %s via %s...",
203840266059SGregory Neil Shapiro 					type, hostbuf, m->m_name);
203906f25ae9SGregory Neil Shapiro 				mci->mci_deliveries++;
2040c2aa98e2SPeter Wemm 				break;
2041c2aa98e2SPeter Wemm 			}
2042c2aa98e2SPeter Wemm 			mci->mci_mailer = m;
2043c2aa98e2SPeter Wemm 			if (mci->mci_exitstat != EX_OK)
2044c2aa98e2SPeter Wemm 			{
2045c2aa98e2SPeter Wemm 				if (mci->mci_exitstat == EX_TEMPFAIL)
204640266059SGregory Neil Shapiro 					goodmxfound = true;
2047c2aa98e2SPeter Wemm 				continue;
2048c2aa98e2SPeter Wemm 			}
2049c2aa98e2SPeter Wemm 
2050c2aa98e2SPeter Wemm 			if (mci_lock_host(mci) != EX_OK)
2051c2aa98e2SPeter Wemm 			{
2052c2aa98e2SPeter Wemm 				mci_setstat(mci, EX_TEMPFAIL, "4.4.5", NULL);
205340266059SGregory Neil Shapiro 				goodmxfound = true;
2054c2aa98e2SPeter Wemm 				continue;
2055c2aa98e2SPeter Wemm 			}
2056c2aa98e2SPeter Wemm 
2057c2aa98e2SPeter Wemm 			/* try the connection */
205840266059SGregory Neil Shapiro 			sm_setproctitle(true, e, "%s %s: %s",
205906f25ae9SGregory Neil Shapiro 					qid_printname(e),
206006f25ae9SGregory Neil Shapiro 					hostbuf, "user open");
206106f25ae9SGregory Neil Shapiro # if NETUNIX
206206f25ae9SGregory Neil Shapiro 			if (mux_path != NULL)
206306f25ae9SGregory Neil Shapiro 			{
206406f25ae9SGregory Neil Shapiro 				message("Connecting to %s via %s...",
206506f25ae9SGregory Neil Shapiro 					mux_path, m->m_name);
206640266059SGregory Neil Shapiro 				i = makeconnection_ds((char *) mux_path, mci);
206706f25ae9SGregory Neil Shapiro 			}
206806f25ae9SGregory Neil Shapiro 			else
206906f25ae9SGregory Neil Shapiro # endif /* NETUNIX */
207006f25ae9SGregory Neil Shapiro 			{
2071c2aa98e2SPeter Wemm 				if (port == 0)
2072c2aa98e2SPeter Wemm 					message("Connecting to %s via %s...",
2073c2aa98e2SPeter Wemm 						hostbuf, m->m_name);
2074c2aa98e2SPeter Wemm 				else
2075c2aa98e2SPeter Wemm 					message("Connecting to %s port %d via %s...",
207606f25ae9SGregory Neil Shapiro 						hostbuf, ntohs(port),
207706f25ae9SGregory Neil Shapiro 						m->m_name);
207840266059SGregory Neil Shapiro 				i = makeconnection(hostbuf, port, mci, e,
207940266059SGregory Neil Shapiro 						   enough);
208006f25ae9SGregory Neil Shapiro 			}
20818774250cSGregory Neil Shapiro 			mci->mci_errno = errno;
2082c2aa98e2SPeter Wemm 			mci->mci_lastuse = curtime();
208306f25ae9SGregory Neil Shapiro 			mci->mci_deliveries = 0;
2084c2aa98e2SPeter Wemm 			mci->mci_exitstat = i;
2085c2aa98e2SPeter Wemm # if NAMED_BIND
2086c2aa98e2SPeter Wemm 			mci->mci_herrno = h_errno;
208706f25ae9SGregory Neil Shapiro # endif /* NAMED_BIND */
208840266059SGregory Neil Shapiro 
208940266059SGregory Neil Shapiro 			/*
209040266059SGregory Neil Shapiro 			**  Have we tried long enough to get a connection?
209140266059SGregory Neil Shapiro 			**	If yes, skip to the fallback MX hosts
209240266059SGregory Neil Shapiro 			**	(if existent).
209340266059SGregory Neil Shapiro 			*/
209440266059SGregory Neil Shapiro 
209540266059SGregory Neil Shapiro 			if (enough > 0 && mci->mci_lastuse >= enough)
209640266059SGregory Neil Shapiro 			{
209740266059SGregory Neil Shapiro 				int h;
209840266059SGregory Neil Shapiro # if NAMED_BIND
209940266059SGregory Neil Shapiro 				extern int NumFallBackMXHosts;
210040266059SGregory Neil Shapiro # else /* NAMED_BIND */
210140266059SGregory Neil Shapiro 				const int NumFallBackMXHosts = 0;
210240266059SGregory Neil Shapiro # endif /* NAMED_BIND */
210340266059SGregory Neil Shapiro 
210440266059SGregory Neil Shapiro 				if (hostnum < nummxhosts && LogLevel > 9)
210540266059SGregory Neil Shapiro 					sm_syslog(LOG_INFO, e->e_id,
210640266059SGregory Neil Shapiro 						  "Timeout.to_aconnect occurred before exhausting all addresses");
210740266059SGregory Neil Shapiro 
210840266059SGregory Neil Shapiro 				/* turn off timeout if fallback available */
210940266059SGregory Neil Shapiro 				if (NumFallBackMXHosts > 0)
211040266059SGregory Neil Shapiro 					enough = 0;
211140266059SGregory Neil Shapiro 
211240266059SGregory Neil Shapiro 				/* skip to a fallback MX host */
211340266059SGregory Neil Shapiro 				h = nummxhosts - NumFallBackMXHosts;
211440266059SGregory Neil Shapiro 				if (hostnum < h)
211540266059SGregory Neil Shapiro 					hostnum = h;
211640266059SGregory Neil Shapiro 			}
2117c2aa98e2SPeter Wemm 			if (i == EX_OK)
2118c2aa98e2SPeter Wemm 			{
211940266059SGregory Neil Shapiro 				goodmxfound = true;
2120605302a5SGregory Neil Shapiro 				markstats(e, firstto, STATS_CONNECT);
2121c2aa98e2SPeter Wemm 				mci->mci_state = MCIS_OPENING;
2122c2aa98e2SPeter Wemm 				mci_cache(mci);
2123c2aa98e2SPeter Wemm 				if (TrafficLogFile != NULL)
212440266059SGregory Neil Shapiro 					(void) sm_io_fprintf(TrafficLogFile,
212540266059SGregory Neil Shapiro 							     SM_TIME_DEFAULT,
212640266059SGregory Neil Shapiro 							     "%05d === CONNECT %s\n",
212740266059SGregory Neil Shapiro 							     (int) CurrentPid,
212840266059SGregory Neil Shapiro 							     hostbuf);
2129c2aa98e2SPeter Wemm 				break;
2130c2aa98e2SPeter Wemm 			}
2131c2aa98e2SPeter Wemm 			else
2132c2aa98e2SPeter Wemm 			{
2133c2aa98e2SPeter Wemm 				if (tTd(11, 1))
213440266059SGregory Neil Shapiro 					sm_dprintf("openmailer: makeconnection => stat=%d, errno=%d\n",
2135c2aa98e2SPeter Wemm 						   i, errno);
2136c2aa98e2SPeter Wemm 				if (i == EX_TEMPFAIL)
213740266059SGregory Neil Shapiro 					goodmxfound = true;
2138c2aa98e2SPeter Wemm 				mci_unlock_host(mci);
2139c2aa98e2SPeter Wemm 			}
2140c2aa98e2SPeter Wemm 
2141c2aa98e2SPeter Wemm 			/* enter status of this host */
2142c2aa98e2SPeter Wemm 			setstat(i);
2143c2aa98e2SPeter Wemm 
2144c2aa98e2SPeter Wemm 			/* should print some message here for -v mode */
2145c2aa98e2SPeter Wemm 		}
2146c2aa98e2SPeter Wemm 		if (mci == NULL)
2147c2aa98e2SPeter Wemm 		{
2148c2aa98e2SPeter Wemm 			syserr("deliver: no host name");
2149c2aa98e2SPeter Wemm 			rcode = EX_SOFTWARE;
2150c2aa98e2SPeter Wemm 			goto give_up;
2151c2aa98e2SPeter Wemm 		}
2152c2aa98e2SPeter Wemm 		mci->mci_pid = 0;
2153c2aa98e2SPeter Wemm 	}
2154c2aa98e2SPeter Wemm 	else
2155c2aa98e2SPeter Wemm 	{
2156c2aa98e2SPeter Wemm 		/* flush any expired connections */
2157c2aa98e2SPeter Wemm 		(void) mci_scan(NULL);
2158c2aa98e2SPeter Wemm 		mci = NULL;
2159c2aa98e2SPeter Wemm 
2160c2aa98e2SPeter Wemm 		if (bitnset(M_LMTP, m->m_flags))
2161c2aa98e2SPeter Wemm 		{
2162c2aa98e2SPeter Wemm 			/* try to get a cached connection */
2163c2aa98e2SPeter Wemm 			mci = mci_get(m->m_name, m);
2164c2aa98e2SPeter Wemm 			if (mci->mci_host == NULL)
2165c2aa98e2SPeter Wemm 				mci->mci_host = m->m_name;
2166c2aa98e2SPeter Wemm 			CurHostName = mci->mci_host;
2167c2aa98e2SPeter Wemm 			if (mci->mci_state != MCIS_CLOSED)
2168c2aa98e2SPeter Wemm 			{
2169c2aa98e2SPeter Wemm 				message("Using cached LMTP connection for %s...",
2170c2aa98e2SPeter Wemm 					m->m_name);
217106f25ae9SGregory Neil Shapiro 				mci->mci_deliveries++;
2172c2aa98e2SPeter Wemm 				goto do_transfer;
2173c2aa98e2SPeter Wemm 			}
2174c2aa98e2SPeter Wemm 		}
2175c2aa98e2SPeter Wemm 
2176c2aa98e2SPeter Wemm 		/* announce the connection to verbose listeners */
2177c2aa98e2SPeter Wemm 		if (host == NULL || host[0] == '\0')
2178c2aa98e2SPeter Wemm 			message("Connecting to %s...", m->m_name);
2179c2aa98e2SPeter Wemm 		else
2180c2aa98e2SPeter Wemm 			message("Connecting to %s via %s...", host, m->m_name);
2181c2aa98e2SPeter Wemm 		if (TrafficLogFile != NULL)
2182c2aa98e2SPeter Wemm 		{
2183c2aa98e2SPeter Wemm 			char **av;
2184c2aa98e2SPeter Wemm 
218540266059SGregory Neil Shapiro 			(void) sm_io_fprintf(TrafficLogFile, SM_TIME_DEFAULT,
218640266059SGregory Neil Shapiro 					     "%05d === EXEC", (int) CurrentPid);
2187c2aa98e2SPeter Wemm 			for (av = pv; *av != NULL; av++)
218840266059SGregory Neil Shapiro 				(void) sm_io_fprintf(TrafficLogFile,
218940266059SGregory Neil Shapiro 						     SM_TIME_DEFAULT, " %s",
219040266059SGregory Neil Shapiro 						     *av);
219140266059SGregory Neil Shapiro 			(void) sm_io_fprintf(TrafficLogFile, SM_TIME_DEFAULT,
219240266059SGregory Neil Shapiro 					     "\n");
2193c2aa98e2SPeter Wemm 		}
2194c2aa98e2SPeter Wemm 
2195c2aa98e2SPeter Wemm #if XDEBUG
2196c2aa98e2SPeter Wemm 		checkfd012("before creating mail pipe");
219706f25ae9SGregory Neil Shapiro #endif /* XDEBUG */
2198c2aa98e2SPeter Wemm 
2199c2aa98e2SPeter Wemm 		/* create a pipe to shove the mail through */
2200c2aa98e2SPeter Wemm 		if (pipe(mpvect) < 0)
2201c2aa98e2SPeter Wemm 		{
2202c2aa98e2SPeter Wemm 			syserr("%s... openmailer(%s): pipe (to mailer)",
2203c2aa98e2SPeter Wemm 			       shortenstring(e->e_to, MAXSHORTSTR), m->m_name);
2204c2aa98e2SPeter Wemm 			if (tTd(11, 1))
220540266059SGregory Neil Shapiro 				sm_dprintf("openmailer: NULL\n");
2206c2aa98e2SPeter Wemm 			rcode = EX_OSERR;
2207c2aa98e2SPeter Wemm 			goto give_up;
2208c2aa98e2SPeter Wemm 		}
2209c2aa98e2SPeter Wemm 
2210c2aa98e2SPeter Wemm #if XDEBUG
2211c2aa98e2SPeter Wemm 		/* make sure we didn't get one of the standard I/O files */
2212c2aa98e2SPeter Wemm 		if (mpvect[0] < 3 || mpvect[1] < 3)
2213c2aa98e2SPeter Wemm 		{
2214c2aa98e2SPeter Wemm 			syserr("%s... openmailer(%s): bogus mpvect %d %d",
2215c2aa98e2SPeter Wemm 			       shortenstring(e->e_to, MAXSHORTSTR), m->m_name,
2216c2aa98e2SPeter Wemm 			       mpvect[0], mpvect[1]);
221740266059SGregory Neil Shapiro 			printopenfds(true);
2218c2aa98e2SPeter Wemm 			if (tTd(11, 1))
221940266059SGregory Neil Shapiro 				sm_dprintf("openmailer: NULL\n");
2220c2aa98e2SPeter Wemm 			rcode = EX_OSERR;
2221c2aa98e2SPeter Wemm 			goto give_up;
2222c2aa98e2SPeter Wemm 		}
2223c2aa98e2SPeter Wemm 
2224c2aa98e2SPeter Wemm 		/* make sure system call isn't dead meat */
2225c2aa98e2SPeter Wemm 		checkfdopen(mpvect[0], "mpvect[0]");
2226c2aa98e2SPeter Wemm 		checkfdopen(mpvect[1], "mpvect[1]");
2227c2aa98e2SPeter Wemm 		if (mpvect[0] == mpvect[1] ||
2228c2aa98e2SPeter Wemm 		    (e->e_lockfp != NULL &&
222940266059SGregory Neil Shapiro 		     (mpvect[0] == sm_io_getinfo(e->e_lockfp, SM_IO_WHAT_FD,
223040266059SGregory Neil Shapiro 						 NULL) ||
223140266059SGregory Neil Shapiro 		      mpvect[1] == sm_io_getinfo(e->e_lockfp, SM_IO_WHAT_FD,
223240266059SGregory Neil Shapiro 						 NULL))))
2233c2aa98e2SPeter Wemm 		{
2234c2aa98e2SPeter Wemm 			if (e->e_lockfp == NULL)
2235c2aa98e2SPeter Wemm 				syserr("%s... openmailer(%s): overlapping mpvect %d %d",
2236c2aa98e2SPeter Wemm 				       shortenstring(e->e_to, MAXSHORTSTR),
2237c2aa98e2SPeter Wemm 				       m->m_name, mpvect[0], mpvect[1]);
2238c2aa98e2SPeter Wemm 			else
2239c2aa98e2SPeter Wemm 				syserr("%s... openmailer(%s): overlapping mpvect %d %d, lockfp = %d",
2240c2aa98e2SPeter Wemm 				       shortenstring(e->e_to, MAXSHORTSTR),
2241c2aa98e2SPeter Wemm 				       m->m_name, mpvect[0], mpvect[1],
224240266059SGregory Neil Shapiro 				       sm_io_getinfo(e->e_lockfp,
224340266059SGregory Neil Shapiro 						     SM_IO_WHAT_FD, NULL));
2244c2aa98e2SPeter Wemm 		}
224506f25ae9SGregory Neil Shapiro #endif /* XDEBUG */
2246c2aa98e2SPeter Wemm 
224706f25ae9SGregory Neil Shapiro 		/* create a return pipe */
2248c2aa98e2SPeter Wemm 		if (pipe(rpvect) < 0)
2249c2aa98e2SPeter Wemm 		{
2250c2aa98e2SPeter Wemm 			syserr("%s... openmailer(%s): pipe (from mailer)",
2251c2aa98e2SPeter Wemm 			       shortenstring(e->e_to, MAXSHORTSTR),
2252c2aa98e2SPeter Wemm 			       m->m_name);
2253c2aa98e2SPeter Wemm 			(void) close(mpvect[0]);
2254c2aa98e2SPeter Wemm 			(void) close(mpvect[1]);
2255c2aa98e2SPeter Wemm 			if (tTd(11, 1))
225640266059SGregory Neil Shapiro 				sm_dprintf("openmailer: NULL\n");
2257c2aa98e2SPeter Wemm 			rcode = EX_OSERR;
2258c2aa98e2SPeter Wemm 			goto give_up;
2259c2aa98e2SPeter Wemm 		}
2260c2aa98e2SPeter Wemm #if XDEBUG
2261c2aa98e2SPeter Wemm 		checkfdopen(rpvect[0], "rpvect[0]");
2262c2aa98e2SPeter Wemm 		checkfdopen(rpvect[1], "rpvect[1]");
226306f25ae9SGregory Neil Shapiro #endif /* XDEBUG */
2264c2aa98e2SPeter Wemm 
2265c2aa98e2SPeter Wemm 		/*
2266c2aa98e2SPeter Wemm 		**  Actually fork the mailer process.
2267c2aa98e2SPeter Wemm 		**	DOFORK is clever about retrying.
2268c2aa98e2SPeter Wemm 		**
2269c2aa98e2SPeter Wemm 		**	Dispose of SIGCHLD signal catchers that may be laying
227006f25ae9SGregory Neil Shapiro 		**	around so that endmailer will get it.
2271c2aa98e2SPeter Wemm 		*/
2272c2aa98e2SPeter Wemm 
227340266059SGregory Neil Shapiro 		if (e->e_xfp != NULL)	/* for debugging */
227440266059SGregory Neil Shapiro 			(void) sm_io_flush(e->e_xfp, SM_TIME_DEFAULT);
227540266059SGregory Neil Shapiro 		(void) sm_io_flush(smioout, SM_TIME_DEFAULT);
227640266059SGregory Neil Shapiro 		(void) sm_signal(SIGCHLD, SIG_DFL);
227706f25ae9SGregory Neil Shapiro 
227806f25ae9SGregory Neil Shapiro 
2279c2aa98e2SPeter Wemm 		DOFORK(FORK);
2280c2aa98e2SPeter Wemm 		/* pid is set by DOFORK */
228106f25ae9SGregory Neil Shapiro 
2282c2aa98e2SPeter Wemm 		if (pid < 0)
2283c2aa98e2SPeter Wemm 		{
2284c2aa98e2SPeter Wemm 			/* failure */
2285c2aa98e2SPeter Wemm 			syserr("%s... openmailer(%s): cannot fork",
2286c2aa98e2SPeter Wemm 			       shortenstring(e->e_to, MAXSHORTSTR), m->m_name);
2287c2aa98e2SPeter Wemm 			(void) close(mpvect[0]);
2288c2aa98e2SPeter Wemm 			(void) close(mpvect[1]);
2289c2aa98e2SPeter Wemm 			(void) close(rpvect[0]);
2290c2aa98e2SPeter Wemm 			(void) close(rpvect[1]);
2291c2aa98e2SPeter Wemm 			if (tTd(11, 1))
229240266059SGregory Neil Shapiro 				sm_dprintf("openmailer: NULL\n");
2293c2aa98e2SPeter Wemm 			rcode = EX_OSERR;
2294c2aa98e2SPeter Wemm 			goto give_up;
2295c2aa98e2SPeter Wemm 		}
2296c2aa98e2SPeter Wemm 		else if (pid == 0)
2297c2aa98e2SPeter Wemm 		{
2298c2aa98e2SPeter Wemm 			int i;
229906f25ae9SGregory Neil Shapiro 			int save_errno;
230040266059SGregory Neil Shapiro 			int sff;
2301c2aa98e2SPeter Wemm 			int new_euid = NO_UID;
2302c2aa98e2SPeter Wemm 			int new_ruid = NO_UID;
2303c2aa98e2SPeter Wemm 			int new_gid = NO_GID;
230440266059SGregory Neil Shapiro 			char *user = NULL;
2305c2aa98e2SPeter Wemm 			struct stat stb;
2306c2aa98e2SPeter Wemm 			extern int DtableSize;
2307c2aa98e2SPeter Wemm 
230840266059SGregory Neil Shapiro 			CurrentPid = getpid();
230940266059SGregory Neil Shapiro 
231013058a91SGregory Neil Shapiro 			/* clear the events to turn off SIGALRMs */
231140266059SGregory Neil Shapiro 			sm_clear_events();
231213058a91SGregory Neil Shapiro 
23138774250cSGregory Neil Shapiro 			/* Reset global flags */
23148774250cSGregory Neil Shapiro 			RestartRequest = NULL;
231540266059SGregory Neil Shapiro 			RestartWorkGroup = false;
23168774250cSGregory Neil Shapiro 			ShutdownRequest = NULL;
23178774250cSGregory Neil Shapiro 			PendingSignal = 0;
23188774250cSGregory Neil Shapiro 
2319c2aa98e2SPeter Wemm 			if (e->e_lockfp != NULL)
232040266059SGregory Neil Shapiro 				(void) close(sm_io_getinfo(e->e_lockfp,
232140266059SGregory Neil Shapiro 							   SM_IO_WHAT_FD,
232240266059SGregory Neil Shapiro 							   NULL));
2323c2aa98e2SPeter Wemm 
2324c2aa98e2SPeter Wemm 			/* child -- set up input & exec mailer */
232540266059SGregory Neil Shapiro 			(void) sm_signal(SIGALRM, sm_signal_noop);
232640266059SGregory Neil Shapiro 			(void) sm_signal(SIGCHLD, SIG_DFL);
232740266059SGregory Neil Shapiro 			(void) sm_signal(SIGHUP, SIG_IGN);
232840266059SGregory Neil Shapiro 			(void) sm_signal(SIGINT, SIG_IGN);
232940266059SGregory Neil Shapiro 			(void) sm_signal(SIGTERM, SIG_DFL);
233013058a91SGregory Neil Shapiro # ifdef SIGUSR1
233140266059SGregory Neil Shapiro 			(void) sm_signal(SIGUSR1, sm_signal_noop);
233213058a91SGregory Neil Shapiro # endif /* SIGUSR1 */
2333c2aa98e2SPeter Wemm 
2334c2aa98e2SPeter Wemm 			if (m != FileMailer || stat(tochain->q_user, &stb) < 0)
2335c2aa98e2SPeter Wemm 				stb.st_mode = 0;
2336c2aa98e2SPeter Wemm 
2337c2aa98e2SPeter Wemm # if HASSETUSERCONTEXT
2338c2aa98e2SPeter Wemm 			/*
2339c2aa98e2SPeter Wemm 			**  Set user resources.
2340c2aa98e2SPeter Wemm 			*/
2341c2aa98e2SPeter Wemm 
2342c2aa98e2SPeter Wemm 			if (contextaddr != NULL)
2343c2aa98e2SPeter Wemm 			{
2344c2aa98e2SPeter Wemm 				struct passwd *pwd;
2345c2aa98e2SPeter Wemm 
2346c2aa98e2SPeter Wemm 				if (contextaddr->q_ruser != NULL)
2347c2aa98e2SPeter Wemm 					pwd = sm_getpwnam(contextaddr->q_ruser);
2348c2aa98e2SPeter Wemm 				else
2349c2aa98e2SPeter Wemm 					pwd = sm_getpwnam(contextaddr->q_user);
2350c2aa98e2SPeter Wemm 				if (pwd != NULL)
2351c2aa98e2SPeter Wemm 					(void) setusercontext(NULL,
2352c2aa98e2SPeter Wemm 							      pwd, pwd->pw_uid,
2353c2aa98e2SPeter Wemm 							      LOGIN_SETRESOURCES|LOGIN_SETPRIORITY);
2354c2aa98e2SPeter Wemm 			}
235506f25ae9SGregory Neil Shapiro # endif /* HASSETUSERCONTEXT */
2356c2aa98e2SPeter Wemm 
235740266059SGregory Neil Shapiro #if HASNICE
2358c2aa98e2SPeter Wemm 			/* tweak niceness */
2359c2aa98e2SPeter Wemm 			if (m->m_nice != 0)
236006f25ae9SGregory Neil Shapiro 				(void) nice(m->m_nice);
236140266059SGregory Neil Shapiro #endif /* HASNICE */
2362c2aa98e2SPeter Wemm 
2363c2aa98e2SPeter Wemm 			/* reset group id */
2364c2aa98e2SPeter Wemm 			if (bitnset(M_SPECIFIC_UID, m->m_flags))
2365c2aa98e2SPeter Wemm 				new_gid = m->m_gid;
2366c2aa98e2SPeter Wemm 			else if (bitset(S_ISGID, stb.st_mode))
2367c2aa98e2SPeter Wemm 				new_gid = stb.st_gid;
2368c2aa98e2SPeter Wemm 			else if (ctladdr != NULL && ctladdr->q_gid != 0)
2369c2aa98e2SPeter Wemm 			{
2370c2aa98e2SPeter Wemm 				if (!DontInitGroups)
2371c2aa98e2SPeter Wemm 				{
237240266059SGregory Neil Shapiro 					user = ctladdr->q_ruser;
237340266059SGregory Neil Shapiro 					if (user == NULL)
237440266059SGregory Neil Shapiro 						user = ctladdr->q_user;
2375c2aa98e2SPeter Wemm 
237640266059SGregory Neil Shapiro 					if (initgroups(user,
237740266059SGregory Neil Shapiro 						       ctladdr->q_gid) == -1
237840266059SGregory Neil Shapiro 					    && suidwarn)
237906f25ae9SGregory Neil Shapiro 					{
2380c2aa98e2SPeter Wemm 						syserr("openmailer: initgroups(%s, %d) failed",
238140266059SGregory Neil Shapiro 							user, ctladdr->q_gid);
238206f25ae9SGregory Neil Shapiro 						exit(EX_TEMPFAIL);
238306f25ae9SGregory Neil Shapiro 					}
2384c2aa98e2SPeter Wemm 				}
2385c2aa98e2SPeter Wemm 				else
2386c2aa98e2SPeter Wemm 				{
2387c2aa98e2SPeter Wemm 					GIDSET_T gidset[1];
2388c2aa98e2SPeter Wemm 
2389c2aa98e2SPeter Wemm 					gidset[0] = ctladdr->q_gid;
239040266059SGregory Neil Shapiro 					if (setgroups(1, gidset) == -1
239140266059SGregory Neil Shapiro 					    && suidwarn)
239206f25ae9SGregory Neil Shapiro 					{
2393c2aa98e2SPeter Wemm 						syserr("openmailer: setgroups() failed");
239406f25ae9SGregory Neil Shapiro 						exit(EX_TEMPFAIL);
239506f25ae9SGregory Neil Shapiro 					}
2396c2aa98e2SPeter Wemm 				}
2397c2aa98e2SPeter Wemm 				new_gid = ctladdr->q_gid;
2398c2aa98e2SPeter Wemm 			}
2399c2aa98e2SPeter Wemm 			else
2400c2aa98e2SPeter Wemm 			{
2401c2aa98e2SPeter Wemm 				if (!DontInitGroups)
2402c2aa98e2SPeter Wemm 				{
240340266059SGregory Neil Shapiro 					user = DefUser;
240440266059SGregory Neil Shapiro 					if (initgroups(DefUser, DefGid) == -1 &&
240540266059SGregory Neil Shapiro 					    suidwarn)
240606f25ae9SGregory Neil Shapiro 					{
2407c2aa98e2SPeter Wemm 						syserr("openmailer: initgroups(%s, %d) failed",
2408c2aa98e2SPeter Wemm 						       DefUser, DefGid);
240906f25ae9SGregory Neil Shapiro 						exit(EX_TEMPFAIL);
241006f25ae9SGregory Neil Shapiro 					}
2411c2aa98e2SPeter Wemm 				}
2412c2aa98e2SPeter Wemm 				else
2413c2aa98e2SPeter Wemm 				{
2414c2aa98e2SPeter Wemm 					GIDSET_T gidset[1];
2415c2aa98e2SPeter Wemm 
2416c2aa98e2SPeter Wemm 					gidset[0] = DefGid;
241740266059SGregory Neil Shapiro 					if (setgroups(1, gidset) == -1
241840266059SGregory Neil Shapiro 					    && suidwarn)
241906f25ae9SGregory Neil Shapiro 					{
2420c2aa98e2SPeter Wemm 						syserr("openmailer: setgroups() failed");
242106f25ae9SGregory Neil Shapiro 						exit(EX_TEMPFAIL);
242206f25ae9SGregory Neil Shapiro 					}
2423c2aa98e2SPeter Wemm 				}
2424c2aa98e2SPeter Wemm 				if (m->m_gid == 0)
2425c2aa98e2SPeter Wemm 					new_gid = DefGid;
2426c2aa98e2SPeter Wemm 				else
2427c2aa98e2SPeter Wemm 					new_gid = m->m_gid;
2428c2aa98e2SPeter Wemm 			}
242906f25ae9SGregory Neil Shapiro 			if (new_gid != NO_GID)
243006f25ae9SGregory Neil Shapiro 			{
243106f25ae9SGregory Neil Shapiro 				if (RunAsUid != 0 &&
243206f25ae9SGregory Neil Shapiro 				    bitnset(M_SPECIFIC_UID, m->m_flags) &&
243306f25ae9SGregory Neil Shapiro 				    new_gid != getgid() &&
243406f25ae9SGregory Neil Shapiro 				    new_gid != getegid())
243506f25ae9SGregory Neil Shapiro 				{
243606f25ae9SGregory Neil Shapiro 					/* Only root can change the gid */
243740266059SGregory Neil Shapiro 					syserr("openmailer: insufficient privileges to change gid, RunAsUid=%d, new_gid=%d, gid=%d, egid=%d",
243840266059SGregory Neil Shapiro 					       (int) RunAsUid, (int) new_gid,
243940266059SGregory Neil Shapiro 					       (int) getgid(), (int) getegid());
244006f25ae9SGregory Neil Shapiro 					exit(EX_TEMPFAIL);
244106f25ae9SGregory Neil Shapiro 				}
244206f25ae9SGregory Neil Shapiro 
244306f25ae9SGregory Neil Shapiro 				if (setgid(new_gid) < 0 && suidwarn)
244406f25ae9SGregory Neil Shapiro 				{
2445c2aa98e2SPeter Wemm 					syserr("openmailer: setgid(%ld) failed",
2446c2aa98e2SPeter Wemm 					       (long) new_gid);
244706f25ae9SGregory Neil Shapiro 					exit(EX_TEMPFAIL);
244806f25ae9SGregory Neil Shapiro 				}
244906f25ae9SGregory Neil Shapiro 			}
245006f25ae9SGregory Neil Shapiro 
245106f25ae9SGregory Neil Shapiro 			/* change root to some "safe" directory */
245206f25ae9SGregory Neil Shapiro 			if (m->m_rootdir != NULL)
245306f25ae9SGregory Neil Shapiro 			{
245494c01205SGregory Neil Shapiro 				expand(m->m_rootdir, cbuf, sizeof cbuf, e);
245506f25ae9SGregory Neil Shapiro 				if (tTd(11, 20))
245640266059SGregory Neil Shapiro 					sm_dprintf("openmailer: chroot %s\n",
245794c01205SGregory Neil Shapiro 						   cbuf);
245894c01205SGregory Neil Shapiro 				if (chroot(cbuf) < 0)
245906f25ae9SGregory Neil Shapiro 				{
246006f25ae9SGregory Neil Shapiro 					syserr("openmailer: Cannot chroot(%s)",
246194c01205SGregory Neil Shapiro 					       cbuf);
246206f25ae9SGregory Neil Shapiro 					exit(EX_TEMPFAIL);
246306f25ae9SGregory Neil Shapiro 				}
246406f25ae9SGregory Neil Shapiro 				if (chdir("/") < 0)
246506f25ae9SGregory Neil Shapiro 				{
246606f25ae9SGregory Neil Shapiro 					syserr("openmailer: cannot chdir(/)");
246706f25ae9SGregory Neil Shapiro 					exit(EX_TEMPFAIL);
246806f25ae9SGregory Neil Shapiro 				}
246906f25ae9SGregory Neil Shapiro 			}
2470c2aa98e2SPeter Wemm 
2471c2aa98e2SPeter Wemm 			/* reset user id */
2472c2aa98e2SPeter Wemm 			endpwent();
247340266059SGregory Neil Shapiro 			sm_mbdb_terminate();
2474c2aa98e2SPeter Wemm 			if (bitnset(M_SPECIFIC_UID, m->m_flags))
247513058a91SGregory Neil Shapiro 			{
2476c2aa98e2SPeter Wemm 				new_euid = m->m_uid;
247713058a91SGregory Neil Shapiro 
247813058a91SGregory Neil Shapiro 				/*
247913058a91SGregory Neil Shapiro 				**  Undo the effects of the uid change in main
248013058a91SGregory Neil Shapiro 				**  for signal handling.  The real uid may
248113058a91SGregory Neil Shapiro 				**  be used by mailer in adding a "From "
248213058a91SGregory Neil Shapiro 				**  line.
248313058a91SGregory Neil Shapiro 				*/
248413058a91SGregory Neil Shapiro 
248513058a91SGregory Neil Shapiro 				if (RealUid != 0 && RealUid != getuid())
248640266059SGregory Neil Shapiro 				{
248740266059SGregory Neil Shapiro # if MAILER_SETUID_METHOD == USE_SETEUID
248840266059SGregory Neil Shapiro #  if HASSETREUID
248940266059SGregory Neil Shapiro 					if (setreuid(RealUid, geteuid()) < 0)
249040266059SGregory Neil Shapiro 					{
249140266059SGregory Neil Shapiro 						syserr("openmailer: setreuid(%d, %d) failed",
249240266059SGregory Neil Shapiro 						       (int) RealUid, (int) geteuid());
249340266059SGregory Neil Shapiro 						exit(EX_OSERR);
249440266059SGregory Neil Shapiro 					}
249540266059SGregory Neil Shapiro #  endif /* HASSETREUID */
249640266059SGregory Neil Shapiro # endif /* MAILER_SETUID_METHOD == USE_SETEUID */
249740266059SGregory Neil Shapiro # if MAILER_SETUID_METHOD == USE_SETREUID
249813058a91SGregory Neil Shapiro 					new_ruid = RealUid;
249940266059SGregory Neil Shapiro # endif /* MAILER_SETUID_METHOD == USE_SETREUID */
250040266059SGregory Neil Shapiro 				}
250113058a91SGregory Neil Shapiro 			}
2502c2aa98e2SPeter Wemm 			else if (bitset(S_ISUID, stb.st_mode))
2503c2aa98e2SPeter Wemm 				new_ruid = stb.st_uid;
2504c2aa98e2SPeter Wemm 			else if (ctladdr != NULL && ctladdr->q_uid != 0)
2505c2aa98e2SPeter Wemm 				new_ruid = ctladdr->q_uid;
2506c2aa98e2SPeter Wemm 			else if (m->m_uid != 0)
2507c2aa98e2SPeter Wemm 				new_ruid = m->m_uid;
2508c2aa98e2SPeter Wemm 			else
2509c2aa98e2SPeter Wemm 				new_ruid = DefUid;
2510605302a5SGregory Neil Shapiro 
2511605302a5SGregory Neil Shapiro # if _FFR_USE_SETLOGIN
2512605302a5SGregory Neil Shapiro 			/* run disconnected from terminal and set login name */
2513605302a5SGregory Neil Shapiro 			if (setsid() >= 0 &&
2514605302a5SGregory Neil Shapiro 			    ctladdr != NULL && ctladdr->q_uid != 0 &&
2515605302a5SGregory Neil Shapiro 			    new_euid == ctladdr->q_uid)
2516605302a5SGregory Neil Shapiro 			{
2517605302a5SGregory Neil Shapiro 				struct passwd *pwd;
2518605302a5SGregory Neil Shapiro 
2519605302a5SGregory Neil Shapiro 				pwd = sm_getpwuid(ctladdr->q_uid);
2520605302a5SGregory Neil Shapiro 				if (pwd != NULL && suidwarn)
2521605302a5SGregory Neil Shapiro 					(void) setlogin(pwd->pw_name);
2522605302a5SGregory Neil Shapiro 				endpwent();
2523605302a5SGregory Neil Shapiro 			}
2524605302a5SGregory Neil Shapiro # endif /* _FFR_USE_SETLOGIN */
2525605302a5SGregory Neil Shapiro 
2526c2aa98e2SPeter Wemm 			if (new_euid != NO_UID)
2527c2aa98e2SPeter Wemm 			{
252806f25ae9SGregory Neil Shapiro 				if (RunAsUid != 0 && new_euid != RunAsUid)
252906f25ae9SGregory Neil Shapiro 				{
253006f25ae9SGregory Neil Shapiro 					/* Only root can change the uid */
253140266059SGregory Neil Shapiro 					syserr("openmailer: insufficient privileges to change uid, new_euid=%d, RunAsUid=%d",
253240266059SGregory Neil Shapiro 					       (int) new_euid, (int) RunAsUid);
253306f25ae9SGregory Neil Shapiro 					exit(EX_TEMPFAIL);
253406f25ae9SGregory Neil Shapiro 				}
253506f25ae9SGregory Neil Shapiro 
2536c2aa98e2SPeter Wemm 				vendor_set_uid(new_euid);
253706f25ae9SGregory Neil Shapiro # if MAILER_SETUID_METHOD == USE_SETEUID
2538c2aa98e2SPeter Wemm 				if (seteuid(new_euid) < 0 && suidwarn)
253906f25ae9SGregory Neil Shapiro 				{
2540c2aa98e2SPeter Wemm 					syserr("openmailer: seteuid(%ld) failed",
2541c2aa98e2SPeter Wemm 					       (long) new_euid);
254206f25ae9SGregory Neil Shapiro 					exit(EX_TEMPFAIL);
254306f25ae9SGregory Neil Shapiro 				}
254406f25ae9SGregory Neil Shapiro # endif /* MAILER_SETUID_METHOD == USE_SETEUID */
254506f25ae9SGregory Neil Shapiro # if MAILER_SETUID_METHOD == USE_SETREUID
2546c2aa98e2SPeter Wemm 				if (setreuid(new_ruid, new_euid) < 0 && suidwarn)
254706f25ae9SGregory Neil Shapiro 				{
2548c2aa98e2SPeter Wemm 					syserr("openmailer: setreuid(%ld, %ld) failed",
2549c2aa98e2SPeter Wemm 					       (long) new_ruid, (long) new_euid);
255006f25ae9SGregory Neil Shapiro 					exit(EX_TEMPFAIL);
255106f25ae9SGregory Neil Shapiro 				}
255206f25ae9SGregory Neil Shapiro # endif /* MAILER_SETUID_METHOD == USE_SETREUID */
255306f25ae9SGregory Neil Shapiro # if MAILER_SETUID_METHOD == USE_SETUID
2554c2aa98e2SPeter Wemm 				if (new_euid != geteuid() && setuid(new_euid) < 0 && suidwarn)
255506f25ae9SGregory Neil Shapiro 				{
2556c2aa98e2SPeter Wemm 					syserr("openmailer: setuid(%ld) failed",
2557c2aa98e2SPeter Wemm 					       (long) new_euid);
255806f25ae9SGregory Neil Shapiro 					exit(EX_TEMPFAIL);
255906f25ae9SGregory Neil Shapiro 				}
256006f25ae9SGregory Neil Shapiro # endif /* MAILER_SETUID_METHOD == USE_SETUID */
2561c2aa98e2SPeter Wemm 			}
2562c2aa98e2SPeter Wemm 			else if (new_ruid != NO_UID)
2563c2aa98e2SPeter Wemm 			{
2564c2aa98e2SPeter Wemm 				vendor_set_uid(new_ruid);
2565c2aa98e2SPeter Wemm 				if (setuid(new_ruid) < 0 && suidwarn)
256606f25ae9SGregory Neil Shapiro 				{
2567c2aa98e2SPeter Wemm 					syserr("openmailer: setuid(%ld) failed",
2568c2aa98e2SPeter Wemm 					       (long) new_ruid);
256906f25ae9SGregory Neil Shapiro 					exit(EX_TEMPFAIL);
257006f25ae9SGregory Neil Shapiro 				}
2571c2aa98e2SPeter Wemm 			}
2572c2aa98e2SPeter Wemm 
2573c2aa98e2SPeter Wemm 			if (tTd(11, 2))
257440266059SGregory Neil Shapiro 				sm_dprintf("openmailer: running as r/euid=%d/%d, r/egid=%d/%d\n",
257506f25ae9SGregory Neil Shapiro 					   (int) getuid(), (int) geteuid(),
257606f25ae9SGregory Neil Shapiro 					   (int) getgid(), (int) getegid());
2577c2aa98e2SPeter Wemm 
2578c2aa98e2SPeter Wemm 			/* move into some "safe" directory */
2579c2aa98e2SPeter Wemm 			if (m->m_execdir != NULL)
2580c2aa98e2SPeter Wemm 			{
2581c2aa98e2SPeter Wemm 				char *q;
2582c2aa98e2SPeter Wemm 
2583c2aa98e2SPeter Wemm 				for (p = m->m_execdir; p != NULL; p = q)
2584c2aa98e2SPeter Wemm 				{
2585c2aa98e2SPeter Wemm 					q = strchr(p, ':');
2586c2aa98e2SPeter Wemm 					if (q != NULL)
2587c2aa98e2SPeter Wemm 						*q = '\0';
258894c01205SGregory Neil Shapiro 					expand(p, cbuf, sizeof cbuf, e);
2589c2aa98e2SPeter Wemm 					if (q != NULL)
2590c2aa98e2SPeter Wemm 						*q++ = ':';
2591c2aa98e2SPeter Wemm 					if (tTd(11, 20))
259240266059SGregory Neil Shapiro 						sm_dprintf("openmailer: trydir %s\n",
259394c01205SGregory Neil Shapiro 							   cbuf);
259494c01205SGregory Neil Shapiro 					if (cbuf[0] != '\0' &&
259594c01205SGregory Neil Shapiro 					    chdir(cbuf) >= 0)
2596c2aa98e2SPeter Wemm 						break;
2597c2aa98e2SPeter Wemm 				}
2598c2aa98e2SPeter Wemm 			}
2599c2aa98e2SPeter Wemm 
260040266059SGregory Neil Shapiro 			/* Check safety of program to be run */
260140266059SGregory Neil Shapiro 			sff = SFF_ROOTOK|SFF_EXECOK;
260240266059SGregory Neil Shapiro 			if (!bitnset(DBS_RUNWRITABLEPROGRAM,
260340266059SGregory Neil Shapiro 				     DontBlameSendmail))
260440266059SGregory Neil Shapiro 				sff |= SFF_NOGWFILES|SFF_NOWWFILES;
260540266059SGregory Neil Shapiro 			if (bitnset(DBS_RUNPROGRAMINUNSAFEDIRPATH,
260640266059SGregory Neil Shapiro 				    DontBlameSendmail))
260740266059SGregory Neil Shapiro 				sff |= SFF_NOPATHCHECK;
260840266059SGregory Neil Shapiro 			else
260940266059SGregory Neil Shapiro 				sff |= SFF_SAFEDIRPATH;
261040266059SGregory Neil Shapiro 			ret = safefile(m->m_mailer, getuid(), getgid(),
261140266059SGregory Neil Shapiro 				       user, sff, 0, NULL);
261240266059SGregory Neil Shapiro 			if (ret != 0)
261340266059SGregory Neil Shapiro 				sm_syslog(LOG_INFO, e->e_id,
261440266059SGregory Neil Shapiro 					  "Warning: program %s unsafe: %s",
261540266059SGregory Neil Shapiro 					  m->m_mailer, sm_errstring(ret));
261640266059SGregory Neil Shapiro 
2617c2aa98e2SPeter Wemm 			/* arrange to filter std & diag output of command */
2618c2aa98e2SPeter Wemm 			(void) close(rpvect[0]);
2619c2aa98e2SPeter Wemm 			if (dup2(rpvect[1], STDOUT_FILENO) < 0)
2620c2aa98e2SPeter Wemm 			{
2621c2aa98e2SPeter Wemm 				syserr("%s... openmailer(%s): cannot dup pipe %d for stdout",
2622c2aa98e2SPeter Wemm 				       shortenstring(e->e_to, MAXSHORTSTR),
2623c2aa98e2SPeter Wemm 				       m->m_name, rpvect[1]);
2624c2aa98e2SPeter Wemm 				_exit(EX_OSERR);
2625c2aa98e2SPeter Wemm 			}
2626c2aa98e2SPeter Wemm 			(void) close(rpvect[1]);
262706f25ae9SGregory Neil Shapiro 
2628c2aa98e2SPeter Wemm 			if (dup2(STDOUT_FILENO, STDERR_FILENO) < 0)
2629c2aa98e2SPeter Wemm 			{
2630c2aa98e2SPeter Wemm 				syserr("%s... openmailer(%s): cannot dup stdout for stderr",
2631c2aa98e2SPeter Wemm 				       shortenstring(e->e_to, MAXSHORTSTR),
2632c2aa98e2SPeter Wemm 				       m->m_name);
2633c2aa98e2SPeter Wemm 				_exit(EX_OSERR);
2634c2aa98e2SPeter Wemm 			}
2635c2aa98e2SPeter Wemm 
2636c2aa98e2SPeter Wemm 			/* arrange to get standard input */
2637c2aa98e2SPeter Wemm 			(void) close(mpvect[1]);
2638c2aa98e2SPeter Wemm 			if (dup2(mpvect[0], STDIN_FILENO) < 0)
2639c2aa98e2SPeter Wemm 			{
2640c2aa98e2SPeter Wemm 				syserr("%s... openmailer(%s): cannot dup pipe %d for stdin",
2641c2aa98e2SPeter Wemm 				       shortenstring(e->e_to, MAXSHORTSTR),
2642c2aa98e2SPeter Wemm 				       m->m_name, mpvect[0]);
2643c2aa98e2SPeter Wemm 				_exit(EX_OSERR);
2644c2aa98e2SPeter Wemm 			}
2645c2aa98e2SPeter Wemm 			(void) close(mpvect[0]);
2646c2aa98e2SPeter Wemm 
2647c2aa98e2SPeter Wemm 			/* arrange for all the files to be closed */
2648c2aa98e2SPeter Wemm 			for (i = 3; i < DtableSize; i++)
2649c2aa98e2SPeter Wemm 			{
2650c2aa98e2SPeter Wemm 				register int j;
2651c2aa98e2SPeter Wemm 
2652c2aa98e2SPeter Wemm 				if ((j = fcntl(i, F_GETFD, 0)) != -1)
265306f25ae9SGregory Neil Shapiro 					(void) fcntl(i, F_SETFD,
265406f25ae9SGregory Neil Shapiro 						     j | FD_CLOEXEC);
2655c2aa98e2SPeter Wemm 			}
2656c2aa98e2SPeter Wemm 
2657605302a5SGregory Neil Shapiro # if !_FFR_USE_SETLOGIN
2658c2aa98e2SPeter Wemm 			/* run disconnected from terminal */
2659c2aa98e2SPeter Wemm 			(void) setsid();
2660605302a5SGregory Neil Shapiro # endif /* !_FFR_USE_SETLOGIN */
2661c2aa98e2SPeter Wemm 
2662c2aa98e2SPeter Wemm 			/* try to execute the mailer */
266306f25ae9SGregory Neil Shapiro 			(void) execve(m->m_mailer, (ARGV_T) pv,
266406f25ae9SGregory Neil Shapiro 				      (ARGV_T) UserEnviron);
266506f25ae9SGregory Neil Shapiro 			save_errno = errno;
2666c2aa98e2SPeter Wemm 			syserr("Cannot exec %s", m->m_mailer);
2667c2aa98e2SPeter Wemm 			if (bitnset(M_LOCALMAILER, m->m_flags) ||
266806f25ae9SGregory Neil Shapiro 			    transienterror(save_errno))
2669c2aa98e2SPeter Wemm 				_exit(EX_OSERR);
2670c2aa98e2SPeter Wemm 			_exit(EX_UNAVAILABLE);
2671c2aa98e2SPeter Wemm 		}
2672c2aa98e2SPeter Wemm 
2673c2aa98e2SPeter Wemm 		/*
2674c2aa98e2SPeter Wemm 		**  Set up return value.
2675c2aa98e2SPeter Wemm 		*/
2676c2aa98e2SPeter Wemm 
2677c2aa98e2SPeter Wemm 		if (mci == NULL)
2678c2aa98e2SPeter Wemm 		{
267940266059SGregory Neil Shapiro 			if (clever)
268040266059SGregory Neil Shapiro 			{
268140266059SGregory Neil Shapiro 				/*
268240266059SGregory Neil Shapiro 				**  Allocate from general heap, not
268340266059SGregory Neil Shapiro 				**  envelope rpool, because this mci
268440266059SGregory Neil Shapiro 				**  is going to be cached.
268540266059SGregory Neil Shapiro 				*/
268640266059SGregory Neil Shapiro 
268740266059SGregory Neil Shapiro 				mci = mci_new(NULL);
268840266059SGregory Neil Shapiro 			}
268940266059SGregory Neil Shapiro 			else
269040266059SGregory Neil Shapiro 			{
269140266059SGregory Neil Shapiro 				/*
269240266059SGregory Neil Shapiro 				**  Prevent a storage leak by allocating
269340266059SGregory Neil Shapiro 				**  this from the envelope rpool.
269440266059SGregory Neil Shapiro 				*/
269540266059SGregory Neil Shapiro 
269640266059SGregory Neil Shapiro 				mci = mci_new(e->e_rpool);
269740266059SGregory Neil Shapiro 			}
2698c2aa98e2SPeter Wemm 		}
2699c2aa98e2SPeter Wemm 		mci->mci_mailer = m;
2700c2aa98e2SPeter Wemm 		if (clever)
2701c2aa98e2SPeter Wemm 		{
2702c2aa98e2SPeter Wemm 			mci->mci_state = MCIS_OPENING;
2703c2aa98e2SPeter Wemm 			mci_cache(mci);
2704c2aa98e2SPeter Wemm 		}
2705c2aa98e2SPeter Wemm 		else
2706c2aa98e2SPeter Wemm 		{
2707c2aa98e2SPeter Wemm 			mci->mci_state = MCIS_OPEN;
2708c2aa98e2SPeter Wemm 		}
2709c2aa98e2SPeter Wemm 		mci->mci_pid = pid;
2710c2aa98e2SPeter Wemm 		(void) close(mpvect[0]);
271140266059SGregory Neil Shapiro 		mci->mci_out = sm_io_open(SmFtStdiofd, SM_TIME_DEFAULT,
271240266059SGregory Neil Shapiro 					  (void *) &(mpvect[1]), SM_IO_WRONLY,
271340266059SGregory Neil Shapiro 					  NULL);
2714c2aa98e2SPeter Wemm 		if (mci->mci_out == NULL)
2715c2aa98e2SPeter Wemm 		{
2716c2aa98e2SPeter Wemm 			syserr("deliver: cannot create mailer output channel, fd=%d",
2717c2aa98e2SPeter Wemm 			       mpvect[1]);
2718c2aa98e2SPeter Wemm 			(void) close(mpvect[1]);
2719c2aa98e2SPeter Wemm 			(void) close(rpvect[0]);
2720c2aa98e2SPeter Wemm 			(void) close(rpvect[1]);
2721c2aa98e2SPeter Wemm 			rcode = EX_OSERR;
2722c2aa98e2SPeter Wemm 			goto give_up;
2723c2aa98e2SPeter Wemm 		}
272406f25ae9SGregory Neil Shapiro 
2725c2aa98e2SPeter Wemm 		(void) close(rpvect[1]);
272640266059SGregory Neil Shapiro 		mci->mci_in = sm_io_open(SmFtStdiofd, SM_TIME_DEFAULT,
272740266059SGregory Neil Shapiro 					 (void *) &(rpvect[0]), SM_IO_RDONLY,
272840266059SGregory Neil Shapiro 					 NULL);
2729c2aa98e2SPeter Wemm 		if (mci->mci_in == NULL)
2730c2aa98e2SPeter Wemm 		{
2731c2aa98e2SPeter Wemm 			syserr("deliver: cannot create mailer input channel, fd=%d",
2732c2aa98e2SPeter Wemm 			       mpvect[1]);
2733c2aa98e2SPeter Wemm 			(void) close(rpvect[0]);
273440266059SGregory Neil Shapiro 			(void) sm_io_close(mci->mci_out, SM_TIME_DEFAULT);
2735c2aa98e2SPeter Wemm 			mci->mci_out = NULL;
2736c2aa98e2SPeter Wemm 			rcode = EX_OSERR;
2737c2aa98e2SPeter Wemm 			goto give_up;
2738c2aa98e2SPeter Wemm 		}
2739c2aa98e2SPeter Wemm 	}
2740c2aa98e2SPeter Wemm 
2741c2aa98e2SPeter Wemm 	/*
2742c2aa98e2SPeter Wemm 	**  If we are in SMTP opening state, send initial protocol.
2743c2aa98e2SPeter Wemm 	*/
2744c2aa98e2SPeter Wemm 
2745c2aa98e2SPeter Wemm 	if (bitnset(M_7BITS, m->m_flags) &&
2746c2aa98e2SPeter Wemm 	    (!clever || mci->mci_state == MCIS_OPENING))
2747c2aa98e2SPeter Wemm 		mci->mci_flags |= MCIF_7BIT;
2748c2aa98e2SPeter Wemm 	if (clever && mci->mci_state != MCIS_CLOSED)
2749c2aa98e2SPeter Wemm 	{
275040266059SGregory Neil Shapiro # if STARTTLS || SASL
275140266059SGregory Neil Shapiro 		int dotpos;
275240266059SGregory Neil Shapiro 		char *srvname;
275340266059SGregory Neil Shapiro 		extern SOCKADDR CurHostAddr;
275440266059SGregory Neil Shapiro # endif /* STARTTLS || SASL */
275540266059SGregory Neil Shapiro 
275640266059SGregory Neil Shapiro # if SASL
2757193538b7SGregory Neil Shapiro #  define DONE_AUTH(f)		bitset(MCIF_AUTHACT, f)
275840266059SGregory Neil Shapiro # endif /* SASL */
275906f25ae9SGregory Neil Shapiro # if STARTTLS
2760193538b7SGregory Neil Shapiro #  define DONE_STARTTLS(f)	bitset(MCIF_TLSACT, f)
276106f25ae9SGregory Neil Shapiro # endif /* STARTTLS */
2762193538b7SGregory Neil Shapiro # define ONLY_HELO(f)		bitset(MCIF_ONLY_EHLO, f)
2763193538b7SGregory Neil Shapiro # define SET_HELO(f)		f |= MCIF_ONLY_EHLO
2764193538b7SGregory Neil Shapiro # define CLR_HELO(f)		f &= ~MCIF_ONLY_EHLO
2765c2aa98e2SPeter Wemm 
276640266059SGregory Neil Shapiro # if STARTTLS || SASL
276740266059SGregory Neil Shapiro 		/* don't use CurHostName, it is changed in many places */
2768602a2b1bSGregory Neil Shapiro 		if (mci->mci_host != NULL)
2769602a2b1bSGregory Neil Shapiro 		{
2770602a2b1bSGregory Neil Shapiro 			srvname = mci->mci_host;
2771602a2b1bSGregory Neil Shapiro 			dotpos = strlen(srvname) - 1;
2772602a2b1bSGregory Neil Shapiro 			if (dotpos >= 0)
2773602a2b1bSGregory Neil Shapiro 			{
2774602a2b1bSGregory Neil Shapiro 				if (srvname[dotpos] == '.')
2775602a2b1bSGregory Neil Shapiro 					srvname[dotpos] = '\0';
2776602a2b1bSGregory Neil Shapiro 				else
2777602a2b1bSGregory Neil Shapiro 					dotpos = -1;
2778602a2b1bSGregory Neil Shapiro 			}
2779602a2b1bSGregory Neil Shapiro 		}
278040266059SGregory Neil Shapiro 		else if (mci->mci_mailer != NULL)
2781602a2b1bSGregory Neil Shapiro 		{
278240266059SGregory Neil Shapiro 			srvname = mci->mci_mailer->m_name;
2783602a2b1bSGregory Neil Shapiro 			dotpos = -1;
2784602a2b1bSGregory Neil Shapiro 		}
278506f25ae9SGregory Neil Shapiro 		else
278606f25ae9SGregory Neil Shapiro 		{
278740266059SGregory Neil Shapiro 			srvname = "local";
278840266059SGregory Neil Shapiro 			dotpos = -1;
2789193538b7SGregory Neil Shapiro 		}
279006f25ae9SGregory Neil Shapiro 
279140266059SGregory Neil Shapiro 		/* don't set {server_name} to NULL or "": see getauth() */
279240266059SGregory Neil Shapiro 		macdefine(&mci->mci_macro, A_TEMP, macid("{server_name}"),
279340266059SGregory Neil Shapiro 			  srvname);
279440266059SGregory Neil Shapiro 
279540266059SGregory Neil Shapiro 		/* CurHostAddr is set by makeconnection() and mci_get() */
279640266059SGregory Neil Shapiro 		if (CurHostAddr.sa.sa_family != 0)
279740266059SGregory Neil Shapiro 		{
279840266059SGregory Neil Shapiro 			macdefine(&mci->mci_macro, A_TEMP,
279940266059SGregory Neil Shapiro 				  macid("{server_addr}"),
280040266059SGregory Neil Shapiro 				  anynet_ntoa(&CurHostAddr));
280140266059SGregory Neil Shapiro 		}
280240266059SGregory Neil Shapiro 		else if (mci->mci_mailer != NULL)
280340266059SGregory Neil Shapiro 		{
280440266059SGregory Neil Shapiro 			/* mailer name is unique, use it as address */
280540266059SGregory Neil Shapiro 			macdefine(&mci->mci_macro, A_PERM,
280640266059SGregory Neil Shapiro 				  macid("{server_addr}"),
280740266059SGregory Neil Shapiro 				  mci->mci_mailer->m_name);
280840266059SGregory Neil Shapiro 		}
280940266059SGregory Neil Shapiro 		else
281040266059SGregory Neil Shapiro 		{
281140266059SGregory Neil Shapiro 			/* don't set it to NULL or "": see getauth() */
281240266059SGregory Neil Shapiro 			macdefine(&mci->mci_macro, A_PERM,
281340266059SGregory Neil Shapiro 				  macid("{server_addr}"), "0");
281440266059SGregory Neil Shapiro 		}
281540266059SGregory Neil Shapiro 
281640266059SGregory Neil Shapiro 		/* undo change of srvname (mci->mci_host) */
2817602a2b1bSGregory Neil Shapiro 		if (dotpos >= 0)
2818602a2b1bSGregory Neil Shapiro 			srvname[dotpos] = '.';
281940266059SGregory Neil Shapiro 
282040266059SGregory Neil Shapiro reconnect:	/* after switching to an encrypted connection */
282140266059SGregory Neil Shapiro # endif /* STARTTLS || SASL */
282240266059SGregory Neil Shapiro 
282340266059SGregory Neil Shapiro 		/* set the current connection information */
282440266059SGregory Neil Shapiro 		e->e_mci = mci;
282540266059SGregory Neil Shapiro # if SASL
282640266059SGregory Neil Shapiro 		mci->mci_saslcap = NULL;
282740266059SGregory Neil Shapiro # endif /* SASL */
282840266059SGregory Neil Shapiro 		smtpinit(m, mci, e, ONLY_HELO(mci->mci_flags));
282940266059SGregory Neil Shapiro 		CLR_HELO(mci->mci_flags);
283040266059SGregory Neil Shapiro 
283140266059SGregory Neil Shapiro 		if (IS_DLVR_RETURN(e))
283240266059SGregory Neil Shapiro 		{
283340266059SGregory Neil Shapiro 			/*
283440266059SGregory Neil Shapiro 			**  Check whether other side can deliver e-mail
283540266059SGregory Neil Shapiro 			**  fast enough
283640266059SGregory Neil Shapiro 			*/
283740266059SGregory Neil Shapiro 
283840266059SGregory Neil Shapiro 			if (!bitset(MCIF_DLVR_BY, mci->mci_flags))
283940266059SGregory Neil Shapiro 			{
284040266059SGregory Neil Shapiro 				e->e_status = "5.4.7";
284140266059SGregory Neil Shapiro 				usrerrenh(e->e_status,
284240266059SGregory Neil Shapiro 					  "554 Server does not support Deliver By");
284340266059SGregory Neil Shapiro 				rcode = EX_UNAVAILABLE;
284440266059SGregory Neil Shapiro 				goto give_up;
284540266059SGregory Neil Shapiro 			}
284640266059SGregory Neil Shapiro 			if (e->e_deliver_by > 0 &&
284740266059SGregory Neil Shapiro 			    e->e_deliver_by - (curtime() - e->e_ctime) <
284840266059SGregory Neil Shapiro 			    mci->mci_min_by)
284940266059SGregory Neil Shapiro 			{
285040266059SGregory Neil Shapiro 				e->e_status = "5.4.7";
285140266059SGregory Neil Shapiro 				usrerrenh(e->e_status,
285240266059SGregory Neil Shapiro 					  "554 Message can't be delivered in time; %ld < %ld",
285340266059SGregory Neil Shapiro 					  e->e_deliver_by - (curtime() - e->e_ctime),
285440266059SGregory Neil Shapiro 					  mci->mci_min_by);
285540266059SGregory Neil Shapiro 				rcode = EX_UNAVAILABLE;
285640266059SGregory Neil Shapiro 				goto give_up;
285740266059SGregory Neil Shapiro 			}
285840266059SGregory Neil Shapiro 		}
285940266059SGregory Neil Shapiro 
286040266059SGregory Neil Shapiro # if STARTTLS
286140266059SGregory Neil Shapiro 		/* first TLS then AUTH to provide a security layer */
286240266059SGregory Neil Shapiro 		if (mci->mci_state != MCIS_CLOSED &&
286340266059SGregory Neil Shapiro 		    !DONE_STARTTLS(mci->mci_flags))
286440266059SGregory Neil Shapiro 		{
286540266059SGregory Neil Shapiro 			int olderrors;
286640266059SGregory Neil Shapiro 			bool usetls;
286740266059SGregory Neil Shapiro 			bool saveQuickAbort = QuickAbort;
286840266059SGregory Neil Shapiro 			bool saveSuprErrs = SuprErrs;
286940266059SGregory Neil Shapiro 			char *host = NULL;
287040266059SGregory Neil Shapiro 
287140266059SGregory Neil Shapiro 			rcode = EX_OK;
287240266059SGregory Neil Shapiro 			usetls = bitset(MCIF_TLS, mci->mci_flags);
287340266059SGregory Neil Shapiro 			if (usetls)
287440266059SGregory Neil Shapiro 				usetls = !iscltflgset(e, D_NOTLS);
287540266059SGregory Neil Shapiro 
287640266059SGregory Neil Shapiro 			if (usetls)
287740266059SGregory Neil Shapiro 			{
287840266059SGregory Neil Shapiro 				host = macvalue(macid("{server_name}"), e);
287940266059SGregory Neil Shapiro 				olderrors = Errors;
288040266059SGregory Neil Shapiro 				QuickAbort = false;
288140266059SGregory Neil Shapiro 				SuprErrs = true;
288240266059SGregory Neil Shapiro 				if (rscheck("try_tls", host, NULL, e, true,
288340266059SGregory Neil Shapiro 					    false, 7, host, NOQID) != EX_OK
288440266059SGregory Neil Shapiro 				    || Errors > olderrors)
288540266059SGregory Neil Shapiro 					usetls = false;
288640266059SGregory Neil Shapiro 				SuprErrs = saveSuprErrs;
288740266059SGregory Neil Shapiro 				QuickAbort = saveQuickAbort;
288840266059SGregory Neil Shapiro 			}
288940266059SGregory Neil Shapiro 
289006f25ae9SGregory Neil Shapiro 			if (usetls)
289106f25ae9SGregory Neil Shapiro 			{
289206f25ae9SGregory Neil Shapiro 				if ((rcode = starttls(m, mci, e)) == EX_OK)
289306f25ae9SGregory Neil Shapiro 				{
289406f25ae9SGregory Neil Shapiro 					/* start again without STARTTLS */
289506f25ae9SGregory Neil Shapiro 					mci->mci_flags |= MCIF_TLSACT;
289606f25ae9SGregory Neil Shapiro 				}
289706f25ae9SGregory Neil Shapiro 				else
289806f25ae9SGregory Neil Shapiro 				{
289906f25ae9SGregory Neil Shapiro 					char *s;
290006f25ae9SGregory Neil Shapiro 
290106f25ae9SGregory Neil Shapiro 					/*
290206f25ae9SGregory Neil Shapiro 					**  TLS negotation failed, what to do?
290306f25ae9SGregory Neil Shapiro 					**  fall back to unencrypted connection
290406f25ae9SGregory Neil Shapiro 					**  or abort? How to decide?
290506f25ae9SGregory Neil Shapiro 					**  set a macro and call a ruleset.
290606f25ae9SGregory Neil Shapiro 					*/
290740266059SGregory Neil Shapiro 
290806f25ae9SGregory Neil Shapiro 					mci->mci_flags &= ~MCIF_TLS;
290906f25ae9SGregory Neil Shapiro 					switch (rcode)
291006f25ae9SGregory Neil Shapiro 					{
291106f25ae9SGregory Neil Shapiro 					  case EX_TEMPFAIL:
291206f25ae9SGregory Neil Shapiro 						s = "TEMP";
291306f25ae9SGregory Neil Shapiro 						break;
291406f25ae9SGregory Neil Shapiro 					  case EX_USAGE:
291506f25ae9SGregory Neil Shapiro 						s = "USAGE";
291606f25ae9SGregory Neil Shapiro 						break;
291706f25ae9SGregory Neil Shapiro 					  case EX_PROTOCOL:
291806f25ae9SGregory Neil Shapiro 						s = "PROTOCOL";
291906f25ae9SGregory Neil Shapiro 						break;
292006f25ae9SGregory Neil Shapiro 					  case EX_SOFTWARE:
292106f25ae9SGregory Neil Shapiro 						s = "SOFTWARE";
292206f25ae9SGregory Neil Shapiro 						break;
292306f25ae9SGregory Neil Shapiro 
292406f25ae9SGregory Neil Shapiro 					  /* everything else is a failure */
292506f25ae9SGregory Neil Shapiro 					  default:
292606f25ae9SGregory Neil Shapiro 						s = "FAILURE";
292706f25ae9SGregory Neil Shapiro 						rcode = EX_TEMPFAIL;
292806f25ae9SGregory Neil Shapiro 					}
292940266059SGregory Neil Shapiro 					macdefine(&e->e_macro, A_PERM,
293040266059SGregory Neil Shapiro 						  macid("{verify}"), s);
293106f25ae9SGregory Neil Shapiro 				}
293206f25ae9SGregory Neil Shapiro 			}
293306f25ae9SGregory Neil Shapiro 			else
293440266059SGregory Neil Shapiro 				macdefine(&e->e_macro, A_PERM,
293540266059SGregory Neil Shapiro 					  macid("{verify}"), "NONE");
293606f25ae9SGregory Neil Shapiro 			olderrors = Errors;
293740266059SGregory Neil Shapiro 			QuickAbort = false;
293840266059SGregory Neil Shapiro 			SuprErrs = true;
293906f25ae9SGregory Neil Shapiro 
294006f25ae9SGregory Neil Shapiro 			/*
294106f25ae9SGregory Neil Shapiro 			**  rcode == EX_SOFTWARE is special:
294206f25ae9SGregory Neil Shapiro 			**  the TLS negotation failed
294306f25ae9SGregory Neil Shapiro 			**  we have to drop the connection no matter what
294406f25ae9SGregory Neil Shapiro 			**  However, we call tls_server to give it the chance
294506f25ae9SGregory Neil Shapiro 			**  to log the problem and return an appropriate
294606f25ae9SGregory Neil Shapiro 			**  error code.
294706f25ae9SGregory Neil Shapiro 			*/
294840266059SGregory Neil Shapiro 
294906f25ae9SGregory Neil Shapiro 			if (rscheck("tls_server",
295040266059SGregory Neil Shapiro 				    macvalue(macid("{verify}"), e),
295140266059SGregory Neil Shapiro 				    NULL, e, true, true, 5, host,
295240266059SGregory Neil Shapiro 				    NOQID) != EX_OK ||
295306f25ae9SGregory Neil Shapiro 			    Errors > olderrors ||
295406f25ae9SGregory Neil Shapiro 			    rcode == EX_SOFTWARE)
295506f25ae9SGregory Neil Shapiro 			{
295606f25ae9SGregory Neil Shapiro 				char enhsc[ENHSCLEN];
295706f25ae9SGregory Neil Shapiro 				extern char MsgBuf[];
295806f25ae9SGregory Neil Shapiro 
295906f25ae9SGregory Neil Shapiro 				if (ISSMTPCODE(MsgBuf) &&
296006f25ae9SGregory Neil Shapiro 				    extenhsc(MsgBuf + 4, ' ', enhsc) > 0)
296106f25ae9SGregory Neil Shapiro 				{
296240266059SGregory Neil Shapiro 					p = sm_rpool_strdup_x(e->e_rpool,
296340266059SGregory Neil Shapiro 							      MsgBuf);
296406f25ae9SGregory Neil Shapiro 				}
296506f25ae9SGregory Neil Shapiro 				else
296606f25ae9SGregory Neil Shapiro 				{
296706f25ae9SGregory Neil Shapiro 					p = "403 4.7.0 server not authenticated.";
296840266059SGregory Neil Shapiro 					(void) sm_strlcpy(enhsc, "4.7.0",
296906f25ae9SGregory Neil Shapiro 							  sizeof enhsc);
297006f25ae9SGregory Neil Shapiro 				}
297106f25ae9SGregory Neil Shapiro 				SuprErrs = saveSuprErrs;
297206f25ae9SGregory Neil Shapiro 				QuickAbort = saveQuickAbort;
297306f25ae9SGregory Neil Shapiro 
297406f25ae9SGregory Neil Shapiro 				if (rcode == EX_SOFTWARE)
297506f25ae9SGregory Neil Shapiro 				{
297606f25ae9SGregory Neil Shapiro 					/* drop the connection */
297706f25ae9SGregory Neil Shapiro 					mci->mci_state = MCIS_QUITING;
297806f25ae9SGregory Neil Shapiro 					if (mci->mci_in != NULL)
297906f25ae9SGregory Neil Shapiro 					{
298040266059SGregory Neil Shapiro 						(void) sm_io_close(mci->mci_in,
298140266059SGregory Neil Shapiro 								   SM_TIME_DEFAULT);
298206f25ae9SGregory Neil Shapiro 						mci->mci_in = NULL;
298306f25ae9SGregory Neil Shapiro 					}
298406f25ae9SGregory Neil Shapiro 					mci->mci_flags &= ~MCIF_TLSACT;
298506f25ae9SGregory Neil Shapiro 					(void) endmailer(mci, e, pv);
298606f25ae9SGregory Neil Shapiro 				}
298706f25ae9SGregory Neil Shapiro 				else
298806f25ae9SGregory Neil Shapiro 				{
298906f25ae9SGregory Neil Shapiro 					/* abort transfer */
299006f25ae9SGregory Neil Shapiro 					smtpquit(m, mci, e);
299106f25ae9SGregory Neil Shapiro 				}
299206f25ae9SGregory Neil Shapiro 
2993193538b7SGregory Neil Shapiro 				/* avoid bogus error msg */
2994193538b7SGregory Neil Shapiro 				mci->mci_errno = 0;
2995193538b7SGregory Neil Shapiro 
299606f25ae9SGregory Neil Shapiro 				/* temp or permanent failure? */
299706f25ae9SGregory Neil Shapiro 				rcode = (*p == '4') ? EX_TEMPFAIL
299806f25ae9SGregory Neil Shapiro 						    : EX_UNAVAILABLE;
299940266059SGregory Neil Shapiro 				mci_setstat(mci, rcode, enhsc, p);
300006f25ae9SGregory Neil Shapiro 
300106f25ae9SGregory Neil Shapiro 				/*
300206f25ae9SGregory Neil Shapiro 				**  hack to get the error message into
300306f25ae9SGregory Neil Shapiro 				**  the envelope (done in giveresponse())
300406f25ae9SGregory Neil Shapiro 				*/
300540266059SGregory Neil Shapiro 
300640266059SGregory Neil Shapiro 				(void) sm_strlcpy(SmtpError, p,
300740266059SGregory Neil Shapiro 						  sizeof SmtpError);
300806f25ae9SGregory Neil Shapiro 			}
300906f25ae9SGregory Neil Shapiro 			QuickAbort = saveQuickAbort;
301006f25ae9SGregory Neil Shapiro 			SuprErrs = saveSuprErrs;
3011193538b7SGregory Neil Shapiro 			if (DONE_STARTTLS(mci->mci_flags) &&
3012193538b7SGregory Neil Shapiro 			    mci->mci_state != MCIS_CLOSED)
301306f25ae9SGregory Neil Shapiro 			{
3014193538b7SGregory Neil Shapiro 				SET_HELO(mci->mci_flags);
301506f25ae9SGregory Neil Shapiro 				mci->mci_flags &= ~MCIF_EXTENS;
301606f25ae9SGregory Neil Shapiro 				goto reconnect;
301706f25ae9SGregory Neil Shapiro 			}
301806f25ae9SGregory Neil Shapiro 		}
301906f25ae9SGregory Neil Shapiro # endif /* STARTTLS */
302006f25ae9SGregory Neil Shapiro # if SASL
302106f25ae9SGregory Neil Shapiro 		/* if other server supports authentication let's authenticate */
302206f25ae9SGregory Neil Shapiro 		if (mci->mci_state != MCIS_CLOSED &&
302306f25ae9SGregory Neil Shapiro 		    mci->mci_saslcap != NULL &&
302440266059SGregory Neil Shapiro 		    !DONE_AUTH(mci->mci_flags) && !iscltflgset(e, D_NOAUTH))
302506f25ae9SGregory Neil Shapiro 		{
302640266059SGregory Neil Shapiro 			/* Should we require some minimum authentication? */
302740266059SGregory Neil Shapiro 			if ((ret = smtpauth(m, mci, e)) == EX_OK)
302806f25ae9SGregory Neil Shapiro 			{
302906f25ae9SGregory Neil Shapiro 				int result;
303040266059SGregory Neil Shapiro 				sasl_ssf_t *ssf = NULL;
303106f25ae9SGregory Neil Shapiro 
303240266059SGregory Neil Shapiro 				/* Get security strength (features) */
303306f25ae9SGregory Neil Shapiro 				result = sasl_getprop(mci->mci_conn, SASL_SSF,
303494c01205SGregory Neil Shapiro # if SASL >= 20000
303594c01205SGregory Neil Shapiro 						      (const void **) &ssf);
303694c01205SGregory Neil Shapiro # else /* SASL >= 20000 */
303706f25ae9SGregory Neil Shapiro 						      (void **) &ssf);
303894c01205SGregory Neil Shapiro # endif /* SASL >= 20000 */
303940266059SGregory Neil Shapiro 
304040266059SGregory Neil Shapiro 				/* XXX authid? */
304106f25ae9SGregory Neil Shapiro 				if (LogLevel > 9)
304206f25ae9SGregory Neil Shapiro 					sm_syslog(LOG_INFO, NOQID,
304340266059SGregory Neil Shapiro 						  "AUTH=client, relay=%.100s, mech=%.16s, bits=%d",
304406f25ae9SGregory Neil Shapiro 						  mci->mci_host,
304540266059SGregory Neil Shapiro 						  macvalue(macid("{auth_type}"), e),
304640266059SGregory Neil Shapiro 						  result == SASL_OK ? *ssf : 0);
30478774250cSGregory Neil Shapiro 
304806f25ae9SGregory Neil Shapiro 				/*
304940266059SGregory Neil Shapiro 				**  Only switch to encrypted connection
305006f25ae9SGregory Neil Shapiro 				**  if a security layer has been negotiated
305106f25ae9SGregory Neil Shapiro 				*/
305240266059SGregory Neil Shapiro 
305306f25ae9SGregory Neil Shapiro 				if (result == SASL_OK && *ssf > 0)
305406f25ae9SGregory Neil Shapiro 				{
305506f25ae9SGregory Neil Shapiro 					/*
305640266059SGregory Neil Shapiro 					**  Convert I/O layer to use SASL.
305740266059SGregory Neil Shapiro 					**  If the call fails, the connection
305840266059SGregory Neil Shapiro 					**  is aborted.
305906f25ae9SGregory Neil Shapiro 					*/
306040266059SGregory Neil Shapiro 
306140266059SGregory Neil Shapiro 					if (sfdcsasl(&mci->mci_in,
306240266059SGregory Neil Shapiro 						     &mci->mci_out,
306306f25ae9SGregory Neil Shapiro 						     mci->mci_conn) == 0)
306406f25ae9SGregory Neil Shapiro 					{
306506f25ae9SGregory Neil Shapiro 						mci->mci_flags &= ~MCIF_EXTENS;
306640266059SGregory Neil Shapiro 						mci->mci_flags |= MCIF_AUTHACT|
306740266059SGregory Neil Shapiro 								  MCIF_ONLY_EHLO;
306806f25ae9SGregory Neil Shapiro 						goto reconnect;
306906f25ae9SGregory Neil Shapiro 					}
307040266059SGregory Neil Shapiro 					syserr("AUTH TLS switch failed in client");
307106f25ae9SGregory Neil Shapiro 				}
307206f25ae9SGregory Neil Shapiro 				/* else? XXX */
307306f25ae9SGregory Neil Shapiro 				mci->mci_flags |= MCIF_AUTHACT;
307406f25ae9SGregory Neil Shapiro 
307506f25ae9SGregory Neil Shapiro 			}
307640266059SGregory Neil Shapiro 			else if (ret == EX_TEMPFAIL)
307740266059SGregory Neil Shapiro 			{
307840266059SGregory Neil Shapiro 				if (LogLevel > 8)
307940266059SGregory Neil Shapiro 					sm_syslog(LOG_ERR, NOQID,
308040266059SGregory Neil Shapiro 						  "AUTH=client, relay=%.100s, temporary failure, connection abort",
308140266059SGregory Neil Shapiro 						  mci->mci_host);
308240266059SGregory Neil Shapiro 				smtpquit(m, mci, e);
308340266059SGregory Neil Shapiro 
308440266059SGregory Neil Shapiro 				/* avoid bogus error msg */
308540266059SGregory Neil Shapiro 				mci->mci_errno = 0;
308640266059SGregory Neil Shapiro 				rcode = EX_TEMPFAIL;
308740266059SGregory Neil Shapiro 				mci_setstat(mci, rcode, "4.7.1", p);
308840266059SGregory Neil Shapiro 
308940266059SGregory Neil Shapiro 				/*
309040266059SGregory Neil Shapiro 				**  hack to get the error message into
309140266059SGregory Neil Shapiro 				**  the envelope (done in giveresponse())
309240266059SGregory Neil Shapiro 				*/
309340266059SGregory Neil Shapiro 
309440266059SGregory Neil Shapiro 				(void) sm_strlcpy(SmtpError,
309540266059SGregory Neil Shapiro 						  "Temporary AUTH failure",
309640266059SGregory Neil Shapiro 						  sizeof SmtpError);
309740266059SGregory Neil Shapiro 			}
309806f25ae9SGregory Neil Shapiro 		}
309906f25ae9SGregory Neil Shapiro # endif /* SASL */
310006f25ae9SGregory Neil Shapiro 	}
310106f25ae9SGregory Neil Shapiro 
3102c2aa98e2SPeter Wemm 
3103c2aa98e2SPeter Wemm do_transfer:
3104c2aa98e2SPeter Wemm 	/* clear out per-message flags from connection structure */
3105c2aa98e2SPeter Wemm 	mci->mci_flags &= ~(MCIF_CVT7TO8|MCIF_CVT8TO7);
3106c2aa98e2SPeter Wemm 
3107c2aa98e2SPeter Wemm 	if (bitset(EF_HAS8BIT, e->e_flags) &&
3108c2aa98e2SPeter Wemm 	    !bitset(EF_DONT_MIME, e->e_flags) &&
3109c2aa98e2SPeter Wemm 	    bitnset(M_7BITS, m->m_flags))
3110c2aa98e2SPeter Wemm 		mci->mci_flags |= MCIF_CVT8TO7;
3111c2aa98e2SPeter Wemm 
3112c2aa98e2SPeter Wemm #if MIME7TO8
3113c2aa98e2SPeter Wemm 	if (bitnset(M_MAKE8BIT, m->m_flags) &&
3114c2aa98e2SPeter Wemm 	    !bitset(MCIF_7BIT, mci->mci_flags) &&
3115c2aa98e2SPeter Wemm 	    (p = hvalue("Content-Transfer-Encoding", e->e_header)) != NULL &&
311640266059SGregory Neil Shapiro 	     (sm_strcasecmp(p, "quoted-printable") == 0 ||
311740266059SGregory Neil Shapiro 	      sm_strcasecmp(p, "base64") == 0) &&
3118c2aa98e2SPeter Wemm 	    (p = hvalue("Content-Type", e->e_header)) != NULL)
3119c2aa98e2SPeter Wemm 	{
3120c2aa98e2SPeter Wemm 		/* may want to convert 7 -> 8 */
3121c2aa98e2SPeter Wemm 		/* XXX should really parse it here -- and use a class XXX */
312240266059SGregory Neil Shapiro 		if (sm_strncasecmp(p, "text/plain", 10) == 0 &&
3123c2aa98e2SPeter Wemm 		    (p[10] == '\0' || p[10] == ' ' || p[10] == ';'))
3124c2aa98e2SPeter Wemm 			mci->mci_flags |= MCIF_CVT7TO8;
3125c2aa98e2SPeter Wemm 	}
312606f25ae9SGregory Neil Shapiro #endif /* MIME7TO8 */
3127c2aa98e2SPeter Wemm 
3128c2aa98e2SPeter Wemm 	if (tTd(11, 1))
3129c2aa98e2SPeter Wemm 	{
313040266059SGregory Neil Shapiro 		sm_dprintf("openmailer: ");
313140266059SGregory Neil Shapiro 		mci_dump(mci, false);
3132c2aa98e2SPeter Wemm 	}
3133c2aa98e2SPeter Wemm 
313440266059SGregory Neil Shapiro #if _FFR_CLIENT_SIZE
313540266059SGregory Neil Shapiro 	/*
313640266059SGregory Neil Shapiro 	**  See if we know the maximum size and
313740266059SGregory Neil Shapiro 	**  abort if the message is too big.
313840266059SGregory Neil Shapiro 	**
313940266059SGregory Neil Shapiro 	**  NOTE: _FFR_CLIENT_SIZE is untested.
314040266059SGregory Neil Shapiro 	*/
314140266059SGregory Neil Shapiro 
314240266059SGregory Neil Shapiro 	if (bitset(MCIF_SIZE, mci->mci_flags) &&
314340266059SGregory Neil Shapiro 	    mci->mci_maxsize > 0 &&
314440266059SGregory Neil Shapiro 	    e->e_msgsize > mci->mci_maxsize)
314540266059SGregory Neil Shapiro 	{
314640266059SGregory Neil Shapiro 		e->e_flags |= EF_NO_BODY_RETN;
314740266059SGregory Neil Shapiro 		if (bitnset(M_LOCALMAILER, m->m_flags))
314840266059SGregory Neil Shapiro 			e->e_status = "5.2.3";
314940266059SGregory Neil Shapiro 		else
315040266059SGregory Neil Shapiro 			e->e_status = "5.3.4";
315140266059SGregory Neil Shapiro 
315240266059SGregory Neil Shapiro 		usrerrenh(e->e_status,
315340266059SGregory Neil Shapiro 			  "552 Message is too large; %ld bytes max",
315440266059SGregory Neil Shapiro 			  mci->mci_maxsize);
315540266059SGregory Neil Shapiro 		rcode = EX_DATAERR;
315640266059SGregory Neil Shapiro 
315740266059SGregory Neil Shapiro 		/* Need an e_message for error */
315840266059SGregory Neil Shapiro 		(void) sm_snprintf(SmtpError, sizeof SmtpError,
315940266059SGregory Neil Shapiro 				   "Message is too large; %ld bytes max",
316040266059SGregory Neil Shapiro 				   mci->mci_maxsize);
316140266059SGregory Neil Shapiro 		goto give_up;
316240266059SGregory Neil Shapiro 	}
316340266059SGregory Neil Shapiro #endif /* _FFR_CLIENT_SIZE */
316440266059SGregory Neil Shapiro 
3165c2aa98e2SPeter Wemm 	if (mci->mci_state != MCIS_OPEN)
3166c2aa98e2SPeter Wemm 	{
3167c2aa98e2SPeter Wemm 		/* couldn't open the mailer */
3168c2aa98e2SPeter Wemm 		rcode = mci->mci_exitstat;
3169c2aa98e2SPeter Wemm 		errno = mci->mci_errno;
3170602a2b1bSGregory Neil Shapiro 		SM_SET_H_ERRNO(mci->mci_herrno);
3171c2aa98e2SPeter Wemm 		if (rcode == EX_OK)
3172c2aa98e2SPeter Wemm 		{
3173c2aa98e2SPeter Wemm 			/* shouldn't happen */
317406f25ae9SGregory Neil Shapiro 			syserr("554 5.3.5 deliver: mci=%lx rcode=%d errno=%d state=%d sig=%s",
317540266059SGregory Neil Shapiro 			       (unsigned long) mci, rcode, errno,
317640266059SGregory Neil Shapiro 			       mci->mci_state, firstsig);
317740266059SGregory Neil Shapiro 			mci_dump_all(true);
3178c2aa98e2SPeter Wemm 			rcode = EX_SOFTWARE;
3179c2aa98e2SPeter Wemm 		}
318006f25ae9SGregory Neil Shapiro 		else if (nummxhosts > hostnum)
3181c2aa98e2SPeter Wemm 		{
3182c2aa98e2SPeter Wemm 			/* try next MX site */
3183c2aa98e2SPeter Wemm 			goto tryhost;
3184c2aa98e2SPeter Wemm 		}
3185c2aa98e2SPeter Wemm 	}
3186c2aa98e2SPeter Wemm 	else if (!clever)
3187c2aa98e2SPeter Wemm 	{
3188c2aa98e2SPeter Wemm 		/*
3189c2aa98e2SPeter Wemm 		**  Format and send message.
3190c2aa98e2SPeter Wemm 		*/
3191c2aa98e2SPeter Wemm 
3192c2aa98e2SPeter Wemm 		putfromline(mci, e);
31932e43090eSPeter Wemm 		(*e->e_puthdr)(mci, e->e_header, e, M87F_OUTER);
3194c2aa98e2SPeter Wemm 		(*e->e_putbody)(mci, e, NULL);
3195c2aa98e2SPeter Wemm 
3196c2aa98e2SPeter Wemm 		/* get the exit status */
3197c2aa98e2SPeter Wemm 		rcode = endmailer(mci, e, pv);
319840266059SGregory Neil Shapiro 		if (rcode == EX_TEMPFAIL && SmtpError[0] == '\0')
3199602a2b1bSGregory Neil Shapiro 		{
3200602a2b1bSGregory Neil Shapiro 			/*
3201602a2b1bSGregory Neil Shapiro 			**  Need an e_message for mailq display.
3202602a2b1bSGregory Neil Shapiro 			**  We set SmtpError as
3203602a2b1bSGregory Neil Shapiro 			*/
3204602a2b1bSGregory Neil Shapiro 
320540266059SGregory Neil Shapiro 			(void) sm_snprintf(SmtpError, sizeof SmtpError,
3206602a2b1bSGregory Neil Shapiro 					   "%s mailer (%s) exited with EX_TEMPFAIL",
3207602a2b1bSGregory Neil Shapiro 					   m->m_name, m->m_mailer);
3208602a2b1bSGregory Neil Shapiro 		}
3209c2aa98e2SPeter Wemm 	}
3210c2aa98e2SPeter Wemm 	else
3211c2aa98e2SPeter Wemm 	{
3212c2aa98e2SPeter Wemm 		/*
3213c2aa98e2SPeter Wemm 		**  Send the MAIL FROM: protocol
3214c2aa98e2SPeter Wemm 		*/
3215c2aa98e2SPeter Wemm 
321640266059SGregory Neil Shapiro 		/* XXX this isn't pipelined... */
3217c2aa98e2SPeter Wemm 		rcode = smtpmailfrom(m, mci, e);
3218c2aa98e2SPeter Wemm 		if (rcode == EX_OK)
3219c2aa98e2SPeter Wemm 		{
3220c2aa98e2SPeter Wemm 			register int i;
322140266059SGregory Neil Shapiro # if PIPELINING
322240266059SGregory Neil Shapiro 			ADDRESS *volatile pchain;
322340266059SGregory Neil Shapiro # endif /* PIPELINING */
3224c2aa98e2SPeter Wemm 
3225c2aa98e2SPeter Wemm 			/* send the recipient list */
3226c2aa98e2SPeter Wemm 			tobuf[0] = '\0';
322740266059SGregory Neil Shapiro 			mci->mci_retryrcpt = false;
322840266059SGregory Neil Shapiro 			mci->mci_tolist = tobuf;
322940266059SGregory Neil Shapiro # if PIPELINING
323040266059SGregory Neil Shapiro 			pchain = NULL;
323140266059SGregory Neil Shapiro 			mci->mci_nextaddr = NULL;
323240266059SGregory Neil Shapiro # endif /* PIPELINING */
323306f25ae9SGregory Neil Shapiro 
3234c2aa98e2SPeter Wemm 			for (to = tochain; to != NULL; to = to->q_tchain)
3235c2aa98e2SPeter Wemm 			{
323640266059SGregory Neil Shapiro 				if (!QS_IS_UNMARKED(to->q_state))
3237c2aa98e2SPeter Wemm 					continue;
323806f25ae9SGregory Neil Shapiro 
323940266059SGregory Neil Shapiro 				/* mark recipient state as "ok so far" */
324040266059SGregory Neil Shapiro 				to->q_state = QS_OK;
324140266059SGregory Neil Shapiro 				e->e_to = to->q_paddr;
324206f25ae9SGregory Neil Shapiro # if STARTTLS
324306f25ae9SGregory Neil Shapiro 				i = rscheck("tls_rcpt", to->q_user, NULL, e,
324440266059SGregory Neil Shapiro 					    true, true, 3, mci->mci_host,
324540266059SGregory Neil Shapiro 					    e->e_id);
324606f25ae9SGregory Neil Shapiro 				if (i != EX_OK)
3247c2aa98e2SPeter Wemm 				{
324840266059SGregory Neil Shapiro 					markfailure(e, to, mci, i, false);
324940266059SGregory Neil Shapiro 					giveresponse(i, to->q_status,  m, mci,
325040266059SGregory Neil Shapiro 						     ctladdr, xstart, e, to);
325140266059SGregory Neil Shapiro 					if (i == EX_TEMPFAIL)
325240266059SGregory Neil Shapiro 					{
325340266059SGregory Neil Shapiro 						mci->mci_retryrcpt = true;
325440266059SGregory Neil Shapiro 						to->q_state = QS_RETRY;
325540266059SGregory Neil Shapiro 					}
325606f25ae9SGregory Neil Shapiro 					continue;
325706f25ae9SGregory Neil Shapiro 				}
325806f25ae9SGregory Neil Shapiro # endif /* STARTTLS */
325906f25ae9SGregory Neil Shapiro 
326040266059SGregory Neil Shapiro 				i = smtprcpt(to, m, mci, e, ctladdr, xstart);
326140266059SGregory Neil Shapiro # if PIPELINING
326240266059SGregory Neil Shapiro 				if (i == EX_OK &&
326340266059SGregory Neil Shapiro 				    bitset(MCIF_PIPELINED, mci->mci_flags))
326406f25ae9SGregory Neil Shapiro 				{
326540266059SGregory Neil Shapiro 					/*
326640266059SGregory Neil Shapiro 					**  Add new element to list of
326740266059SGregory Neil Shapiro 					**  recipients for pipelining.
326840266059SGregory Neil Shapiro 					*/
326940266059SGregory Neil Shapiro 
327040266059SGregory Neil Shapiro 					to->q_pchain = NULL;
327140266059SGregory Neil Shapiro 					if (mci->mci_nextaddr == NULL)
327240266059SGregory Neil Shapiro 						mci->mci_nextaddr = to;
327340266059SGregory Neil Shapiro 					if (pchain == NULL)
327440266059SGregory Neil Shapiro 						pchain = to;
3275c2aa98e2SPeter Wemm 					else
3276c2aa98e2SPeter Wemm 					{
327740266059SGregory Neil Shapiro 						pchain->q_pchain = to;
327840266059SGregory Neil Shapiro 						pchain = pchain->q_pchain;
327940266059SGregory Neil Shapiro 					}
328040266059SGregory Neil Shapiro 				}
328140266059SGregory Neil Shapiro # endif /* PIPELINING */
328240266059SGregory Neil Shapiro 				if (i != EX_OK)
328340266059SGregory Neil Shapiro 				{
328440266059SGregory Neil Shapiro 					markfailure(e, to, mci, i, false);
328540266059SGregory Neil Shapiro 					giveresponse(i, to->q_status,  m, mci,
328640266059SGregory Neil Shapiro 						     ctladdr, xstart, e, to);
328740266059SGregory Neil Shapiro 					if (i == EX_TEMPFAIL)
328840266059SGregory Neil Shapiro 						to->q_state = QS_RETRY;
3289c2aa98e2SPeter Wemm 				}
3290c2aa98e2SPeter Wemm 			}
3291c2aa98e2SPeter Wemm 
329240266059SGregory Neil Shapiro 			/* No recipients in list and no missing responses? */
329340266059SGregory Neil Shapiro 			if (tobuf[0] == '\0'
329440266059SGregory Neil Shapiro # if PIPELINING
329540266059SGregory Neil Shapiro 			    && mci->mci_nextaddr == NULL
329640266059SGregory Neil Shapiro # endif /* PIPELINING */
329740266059SGregory Neil Shapiro 			   )
3298c2aa98e2SPeter Wemm 			{
3299c2aa98e2SPeter Wemm 				rcode = EX_OK;
3300c2aa98e2SPeter Wemm 				e->e_to = NULL;
3301c2aa98e2SPeter Wemm 				if (bitset(MCIF_CACHED, mci->mci_flags))
3302c2aa98e2SPeter Wemm 					smtprset(m, mci, e);
3303c2aa98e2SPeter Wemm 			}
3304c2aa98e2SPeter Wemm 			else
3305c2aa98e2SPeter Wemm 			{
3306c2aa98e2SPeter Wemm 				e->e_to = tobuf + 1;
330740266059SGregory Neil Shapiro 				rcode = smtpdata(m, mci, e, ctladdr, xstart);
3308c2aa98e2SPeter Wemm 			}
3309c2aa98e2SPeter Wemm 		}
331006f25ae9SGregory Neil Shapiro 		if (rcode == EX_TEMPFAIL && nummxhosts > hostnum)
3311c2aa98e2SPeter Wemm 		{
3312c2aa98e2SPeter Wemm 			/* try next MX site */
3313c2aa98e2SPeter Wemm 			goto tryhost;
3314c2aa98e2SPeter Wemm 		}
3315c2aa98e2SPeter Wemm 	}
3316c2aa98e2SPeter Wemm #if NAMED_BIND
3317c2aa98e2SPeter Wemm 	if (ConfigLevel < 2)
3318c2aa98e2SPeter Wemm 		_res.options |= RES_DEFNAMES | RES_DNSRCH;	/* XXX */
331906f25ae9SGregory Neil Shapiro #endif /* NAMED_BIND */
3320c2aa98e2SPeter Wemm 
3321c2aa98e2SPeter Wemm 	if (tTd(62, 1))
3322c2aa98e2SPeter Wemm 		checkfds("after delivery");
3323c2aa98e2SPeter Wemm 
3324c2aa98e2SPeter Wemm 	/*
3325c2aa98e2SPeter Wemm 	**  Do final status disposal.
3326c2aa98e2SPeter Wemm 	**	We check for something in tobuf for the SMTP case.
3327c2aa98e2SPeter Wemm 	**	If we got a temporary failure, arrange to queue the
3328c2aa98e2SPeter Wemm 	**		addressees.
3329c2aa98e2SPeter Wemm 	*/
3330c2aa98e2SPeter Wemm 
3331c2aa98e2SPeter Wemm   give_up:
3332c2aa98e2SPeter Wemm 	if (bitnset(M_LMTP, m->m_flags))
3333c2aa98e2SPeter Wemm 	{
3334c2aa98e2SPeter Wemm 		lmtp_rcode = rcode;
3335c2aa98e2SPeter Wemm 		tobuf[0] = '\0';
333640266059SGregory Neil Shapiro 		anyok = false;
333740266059SGregory Neil Shapiro 		strsize = 0;
3338c2aa98e2SPeter Wemm 	}
3339c2aa98e2SPeter Wemm 	else
3340c2aa98e2SPeter Wemm 		anyok = rcode == EX_OK;
3341c2aa98e2SPeter Wemm 
3342c2aa98e2SPeter Wemm 	for (to = tochain; to != NULL; to = to->q_tchain)
3343c2aa98e2SPeter Wemm 	{
3344c2aa98e2SPeter Wemm 		/* see if address already marked */
334506f25ae9SGregory Neil Shapiro 		if (!QS_IS_OK(to->q_state))
3346c2aa98e2SPeter Wemm 			continue;
3347c2aa98e2SPeter Wemm 
3348c2aa98e2SPeter Wemm 		/* if running LMTP, get the status for each address */
3349c2aa98e2SPeter Wemm 		if (bitnset(M_LMTP, m->m_flags))
3350c2aa98e2SPeter Wemm 		{
3351c2aa98e2SPeter Wemm 			if (lmtp_rcode == EX_OK)
3352c2aa98e2SPeter Wemm 				rcode = smtpgetstat(m, mci, e);
3353c2aa98e2SPeter Wemm 			if (rcode == EX_OK)
3354c2aa98e2SPeter Wemm 			{
335540266059SGregory Neil Shapiro 				strsize += sm_strlcat2(tobuf + strsize, ",",
335640266059SGregory Neil Shapiro 						to->q_paddr,
335740266059SGregory Neil Shapiro 						tobufsize - strsize);
335840266059SGregory Neil Shapiro 				SM_ASSERT(strsize < tobufsize);
335940266059SGregory Neil Shapiro 				anyok = true;
3360c2aa98e2SPeter Wemm 			}
3361c2aa98e2SPeter Wemm 			else
3362c2aa98e2SPeter Wemm 			{
3363c2aa98e2SPeter Wemm 				e->e_to = to->q_paddr;
336440266059SGregory Neil Shapiro 				markfailure(e, to, mci, rcode, true);
336506f25ae9SGregory Neil Shapiro 				giveresponse(rcode, to->q_status, m, mci,
336640266059SGregory Neil Shapiro 					     ctladdr, xstart, e, to);
3367c2aa98e2SPeter Wemm 				e->e_to = tobuf + 1;
3368c2aa98e2SPeter Wemm 				continue;
3369c2aa98e2SPeter Wemm 			}
3370c2aa98e2SPeter Wemm 		}
3371c2aa98e2SPeter Wemm 		else
3372c2aa98e2SPeter Wemm 		{
3373c2aa98e2SPeter Wemm 			/* mark bad addresses */
3374c2aa98e2SPeter Wemm 			if (rcode != EX_OK)
3375c2aa98e2SPeter Wemm 			{
3376c2aa98e2SPeter Wemm 				if (goodmxfound && rcode == EX_NOHOST)
3377c2aa98e2SPeter Wemm 					rcode = EX_TEMPFAIL;
337840266059SGregory Neil Shapiro 				markfailure(e, to, mci, rcode, true);
3379c2aa98e2SPeter Wemm 				continue;
3380c2aa98e2SPeter Wemm 			}
3381c2aa98e2SPeter Wemm 		}
3382c2aa98e2SPeter Wemm 
3383c2aa98e2SPeter Wemm 		/* successful delivery */
338406f25ae9SGregory Neil Shapiro 		to->q_state = QS_SENT;
3385c2aa98e2SPeter Wemm 		to->q_statdate = curtime();
3386c2aa98e2SPeter Wemm 		e->e_nsent++;
338706f25ae9SGregory Neil Shapiro 
338806f25ae9SGregory Neil Shapiro 		/*
338906f25ae9SGregory Neil Shapiro 		**  Checkpoint the send list every few addresses
339006f25ae9SGregory Neil Shapiro 		*/
339106f25ae9SGregory Neil Shapiro 
339242e5d165SGregory Neil Shapiro 		if (CheckpointInterval > 0 && e->e_nsent >= CheckpointInterval)
339306f25ae9SGregory Neil Shapiro 		{
339440266059SGregory Neil Shapiro 			queueup(e, false, false);
339506f25ae9SGregory Neil Shapiro 			e->e_nsent = 0;
339606f25ae9SGregory Neil Shapiro 		}
339706f25ae9SGregory Neil Shapiro 
3398c2aa98e2SPeter Wemm 		if (bitnset(M_LOCALMAILER, m->m_flags) &&
3399c2aa98e2SPeter Wemm 		    bitset(QPINGONSUCCESS, to->q_flags))
3400c2aa98e2SPeter Wemm 		{
3401c2aa98e2SPeter Wemm 			to->q_flags |= QDELIVERED;
3402c2aa98e2SPeter Wemm 			to->q_status = "2.1.5";
340340266059SGregory Neil Shapiro 			(void) sm_io_fprintf(e->e_xfp, SM_TIME_DEFAULT,
340440266059SGregory Neil Shapiro 					     "%s... Successfully delivered\n",
3405c2aa98e2SPeter Wemm 					     to->q_paddr);
3406c2aa98e2SPeter Wemm 		}
3407c2aa98e2SPeter Wemm 		else if (bitset(QPINGONSUCCESS, to->q_flags) &&
3408c2aa98e2SPeter Wemm 			 bitset(QPRIMARY, to->q_flags) &&
3409c2aa98e2SPeter Wemm 			 !bitset(MCIF_DSN, mci->mci_flags))
3410c2aa98e2SPeter Wemm 		{
3411c2aa98e2SPeter Wemm 			to->q_flags |= QRELAYED;
341240266059SGregory Neil Shapiro 			(void) sm_io_fprintf(e->e_xfp, SM_TIME_DEFAULT,
341340266059SGregory Neil Shapiro 					     "%s... relayed; expect no further notifications\n",
341440266059SGregory Neil Shapiro 					     to->q_paddr);
341540266059SGregory Neil Shapiro 		}
341640266059SGregory Neil Shapiro 		else if (IS_DLVR_NOTIFY(e) &&
341740266059SGregory Neil Shapiro 			 !bitset(MCIF_DLVR_BY, mci->mci_flags) &&
341840266059SGregory Neil Shapiro 			 bitset(QPRIMARY, to->q_flags) &&
341940266059SGregory Neil Shapiro 			 (!bitset(QHASNOTIFY, to->q_flags) ||
342040266059SGregory Neil Shapiro 			  bitset(QPINGONSUCCESS, to->q_flags) ||
342140266059SGregory Neil Shapiro 			  bitset(QPINGONFAILURE, to->q_flags) ||
342240266059SGregory Neil Shapiro 			  bitset(QPINGONDELAY, to->q_flags)))
342340266059SGregory Neil Shapiro 		{
342440266059SGregory Neil Shapiro 			/* RFC 2852, 4.1.4.2: no NOTIFY, or not NEVER */
342540266059SGregory Neil Shapiro 			to->q_flags |= QBYNRELAY;
342640266059SGregory Neil Shapiro 			(void) sm_io_fprintf(e->e_xfp, SM_TIME_DEFAULT,
342740266059SGregory Neil Shapiro 					     "%s... Deliver-by notify: relayed\n",
342840266059SGregory Neil Shapiro 					     to->q_paddr);
342940266059SGregory Neil Shapiro 		}
343040266059SGregory Neil Shapiro 		else if (IS_DLVR_TRACE(e) &&
343140266059SGregory Neil Shapiro 			 (!bitset(QHASNOTIFY, to->q_flags) ||
343240266059SGregory Neil Shapiro 			  bitset(QPINGONSUCCESS, to->q_flags) ||
343340266059SGregory Neil Shapiro 			  bitset(QPINGONFAILURE, to->q_flags) ||
343440266059SGregory Neil Shapiro 			  bitset(QPINGONDELAY, to->q_flags)) &&
343540266059SGregory Neil Shapiro 			 bitset(QPRIMARY, to->q_flags))
343640266059SGregory Neil Shapiro 		{
343740266059SGregory Neil Shapiro 			/* RFC 2852, 4.1.4: no NOTIFY, or not NEVER */
343840266059SGregory Neil Shapiro 			to->q_flags |= QBYTRACE;
343940266059SGregory Neil Shapiro 			(void) sm_io_fprintf(e->e_xfp, SM_TIME_DEFAULT,
344040266059SGregory Neil Shapiro 					     "%s... Deliver-By trace: relayed\n",
3441c2aa98e2SPeter Wemm 					     to->q_paddr);
3442c2aa98e2SPeter Wemm 		}
3443c2aa98e2SPeter Wemm 	}
3444c2aa98e2SPeter Wemm 
3445c2aa98e2SPeter Wemm 	if (bitnset(M_LMTP, m->m_flags))
3446c2aa98e2SPeter Wemm 	{
3447c2aa98e2SPeter Wemm 		/*
3448c2aa98e2SPeter Wemm 		**  Global information applies to the last recipient only;
3449c2aa98e2SPeter Wemm 		**  clear it out to avoid bogus errors.
3450c2aa98e2SPeter Wemm 		*/
3451c2aa98e2SPeter Wemm 
3452c2aa98e2SPeter Wemm 		rcode = EX_OK;
3453c2aa98e2SPeter Wemm 		e->e_statmsg = NULL;
3454c2aa98e2SPeter Wemm 
3455c2aa98e2SPeter Wemm 		/* reset the mci state for the next transaction */
345640266059SGregory Neil Shapiro 		if (mci != NULL &&
345740266059SGregory Neil Shapiro 		    (mci->mci_state == MCIS_MAIL ||
345840266059SGregory Neil Shapiro 		     mci->mci_state == MCIS_RCPT ||
345940266059SGregory Neil Shapiro 		     mci->mci_state == MCIS_DATA))
3460c2aa98e2SPeter Wemm 			mci->mci_state = MCIS_OPEN;
3461c2aa98e2SPeter Wemm 	}
3462c2aa98e2SPeter Wemm 
3463c2aa98e2SPeter Wemm 	if (tobuf[0] != '\0')
346440266059SGregory Neil Shapiro 	{
346540266059SGregory Neil Shapiro 		giveresponse(rcode, NULL, m, mci, ctladdr, xstart, e, tochain);
346640266059SGregory Neil Shapiro #if 0
346740266059SGregory Neil Shapiro 		/*
346840266059SGregory Neil Shapiro 		**  This code is disabled for now because I am not
346940266059SGregory Neil Shapiro 		**  sure that copying status from the first recipient
347040266059SGregory Neil Shapiro 		**  to all non-status'ed recipients is a good idea.
347140266059SGregory Neil Shapiro 		*/
347240266059SGregory Neil Shapiro 
347340266059SGregory Neil Shapiro 		if (tochain->q_message != NULL &&
347440266059SGregory Neil Shapiro 		    !bitnset(M_LMTP, m->m_flags) && rcode != EX_OK)
347540266059SGregory Neil Shapiro 		{
347640266059SGregory Neil Shapiro 			for (to = tochain->q_tchain; to != NULL;
347740266059SGregory Neil Shapiro 			     to = to->q_tchain)
347840266059SGregory Neil Shapiro 			{
347940266059SGregory Neil Shapiro 				/* see if address already marked */
348040266059SGregory Neil Shapiro 				if (QS_IS_QUEUEUP(to->q_state) &&
348140266059SGregory Neil Shapiro 				    to->q_message == NULL)
348240266059SGregory Neil Shapiro 					to->q_message = sm_rpool_strdup_x(e->e_rpool,
348340266059SGregory Neil Shapiro 							tochain->q_message);
348440266059SGregory Neil Shapiro 			}
348540266059SGregory Neil Shapiro 		}
348640266059SGregory Neil Shapiro #endif /* 0 */
348740266059SGregory Neil Shapiro 	}
3488c2aa98e2SPeter Wemm 	if (anyok)
348940266059SGregory Neil Shapiro 		markstats(e, tochain, STATS_NORMAL);
3490c2aa98e2SPeter Wemm 	mci_store_persistent(mci);
3491c2aa98e2SPeter Wemm 
349240266059SGregory Neil Shapiro 	/* Some recipients were tempfailed, try them on the next host */
349340266059SGregory Neil Shapiro 	if (mci != NULL && mci->mci_retryrcpt && nummxhosts > hostnum)
349440266059SGregory Neil Shapiro 	{
349540266059SGregory Neil Shapiro 		/* try next MX site */
349640266059SGregory Neil Shapiro 		goto tryhost;
349740266059SGregory Neil Shapiro 	}
349840266059SGregory Neil Shapiro 
3499c2aa98e2SPeter Wemm 	/* now close the connection */
3500c2aa98e2SPeter Wemm 	if (clever && mci != NULL && mci->mci_state != MCIS_CLOSED &&
3501c2aa98e2SPeter Wemm 	    !bitset(MCIF_CACHED, mci->mci_flags))
3502c2aa98e2SPeter Wemm 		smtpquit(m, mci, e);
3503c2aa98e2SPeter Wemm 
350440266059SGregory Neil Shapiro cleanup: ;
350540266059SGregory Neil Shapiro 	}
350640266059SGregory Neil Shapiro 	SM_FINALLY
350740266059SGregory Neil Shapiro 	{
3508c2aa98e2SPeter Wemm 		/*
3509c2aa98e2SPeter Wemm 		**  Restore state and return.
3510c2aa98e2SPeter Wemm 		*/
3511c2aa98e2SPeter Wemm #if XDEBUG
3512c2aa98e2SPeter Wemm 		char wbuf[MAXLINE];
3513c2aa98e2SPeter Wemm 
3514c2aa98e2SPeter Wemm 		/* make absolutely certain 0, 1, and 2 are in use */
351540266059SGregory Neil Shapiro 		(void) sm_snprintf(wbuf, sizeof wbuf,
351640266059SGregory Neil Shapiro 				   "%s... end of deliver(%s)",
3517c2aa98e2SPeter Wemm 				   e->e_to == NULL ? "NO-TO-LIST"
351840266059SGregory Neil Shapiro 						   : shortenstring(e->e_to,
351940266059SGregory Neil Shapiro 								   MAXSHORTSTR),
3520c2aa98e2SPeter Wemm 				  m->m_name);
3521c2aa98e2SPeter Wemm 		checkfd012(wbuf);
352206f25ae9SGregory Neil Shapiro #endif /* XDEBUG */
3523c2aa98e2SPeter Wemm 
3524c2aa98e2SPeter Wemm 		errno = 0;
352540266059SGregory Neil Shapiro 
352640266059SGregory Neil Shapiro 		/*
352740266059SGregory Neil Shapiro 		**  It was originally necessary to set macro 'g' to NULL
352840266059SGregory Neil Shapiro 		**  because it previously pointed to an auto buffer.
352940266059SGregory Neil Shapiro 		**  We don't do this any more, so this may be unnecessary.
353040266059SGregory Neil Shapiro 		*/
353140266059SGregory Neil Shapiro 
353240266059SGregory Neil Shapiro 		macdefine(&e->e_macro, A_PERM, 'g', (char *) NULL);
353306f25ae9SGregory Neil Shapiro 		e->e_to = NULL;
353440266059SGregory Neil Shapiro 	}
353540266059SGregory Neil Shapiro 	SM_END_TRY
353606f25ae9SGregory Neil Shapiro 	return rcode;
3537c2aa98e2SPeter Wemm }
353806f25ae9SGregory Neil Shapiro 
353940266059SGregory Neil Shapiro /*
3540c2aa98e2SPeter Wemm **  MARKFAILURE -- mark a failure on a specific address.
3541c2aa98e2SPeter Wemm **
3542c2aa98e2SPeter Wemm **	Parameters:
3543c2aa98e2SPeter Wemm **		e -- the envelope we are sending.
3544c2aa98e2SPeter Wemm **		q -- the address to mark.
3545c2aa98e2SPeter Wemm **		mci -- mailer connection information.
3546c2aa98e2SPeter Wemm **		rcode -- the code signifying the particular failure.
354706f25ae9SGregory Neil Shapiro **		ovr -- override an existing code?
3548c2aa98e2SPeter Wemm **
3549c2aa98e2SPeter Wemm **	Returns:
3550c2aa98e2SPeter Wemm **		none.
3551c2aa98e2SPeter Wemm **
3552c2aa98e2SPeter Wemm **	Side Effects:
3553c2aa98e2SPeter Wemm **		marks the address (and possibly the envelope) with the
3554c2aa98e2SPeter Wemm **			failure so that an error will be returned or
3555c2aa98e2SPeter Wemm **			the message will be queued, as appropriate.
3556c2aa98e2SPeter Wemm */
3557c2aa98e2SPeter Wemm 
355840266059SGregory Neil Shapiro void
355906f25ae9SGregory Neil Shapiro markfailure(e, q, mci, rcode, ovr)
3560c2aa98e2SPeter Wemm 	register ENVELOPE *e;
3561c2aa98e2SPeter Wemm 	register ADDRESS *q;
3562c2aa98e2SPeter Wemm 	register MCI *mci;
3563c2aa98e2SPeter Wemm 	int rcode;
356406f25ae9SGregory Neil Shapiro 	bool ovr;
3565c2aa98e2SPeter Wemm {
356640266059SGregory Neil Shapiro 	int save_errno = errno;
356706f25ae9SGregory Neil Shapiro 	char *status = NULL;
356806f25ae9SGregory Neil Shapiro 	char *rstatus = NULL;
3569c2aa98e2SPeter Wemm 
3570c2aa98e2SPeter Wemm 	switch (rcode)
3571c2aa98e2SPeter Wemm 	{
3572c2aa98e2SPeter Wemm 	  case EX_OK:
3573c2aa98e2SPeter Wemm 		break;
3574c2aa98e2SPeter Wemm 
3575c2aa98e2SPeter Wemm 	  case EX_TEMPFAIL:
3576c2aa98e2SPeter Wemm 	  case EX_IOERR:
3577c2aa98e2SPeter Wemm 	  case EX_OSERR:
357806f25ae9SGregory Neil Shapiro 		q->q_state = QS_QUEUEUP;
3579c2aa98e2SPeter Wemm 		break;
3580c2aa98e2SPeter Wemm 
3581c2aa98e2SPeter Wemm 	  default:
358206f25ae9SGregory Neil Shapiro 		q->q_state = QS_BADADDR;
3583c2aa98e2SPeter Wemm 		break;
3584c2aa98e2SPeter Wemm 	}
3585c2aa98e2SPeter Wemm 
3586c2aa98e2SPeter Wemm 	/* find most specific error code possible */
3587c2aa98e2SPeter Wemm 	if (mci != NULL && mci->mci_status != NULL)
3588c2aa98e2SPeter Wemm 	{
358940266059SGregory Neil Shapiro 		status = sm_rpool_strdup_x(e->e_rpool, mci->mci_status);
3590c2aa98e2SPeter Wemm 		if (mci->mci_rstatus != NULL)
359140266059SGregory Neil Shapiro 			rstatus = sm_rpool_strdup_x(e->e_rpool,
359240266059SGregory Neil Shapiro 						    mci->mci_rstatus);
3593c2aa98e2SPeter Wemm 		else
359406f25ae9SGregory Neil Shapiro 			rstatus = NULL;
3595c2aa98e2SPeter Wemm 	}
3596c2aa98e2SPeter Wemm 	else if (e->e_status != NULL)
3597c2aa98e2SPeter Wemm 	{
359806f25ae9SGregory Neil Shapiro 		status = e->e_status;
359906f25ae9SGregory Neil Shapiro 		rstatus = NULL;
3600c2aa98e2SPeter Wemm 	}
3601c2aa98e2SPeter Wemm 	else
3602c2aa98e2SPeter Wemm 	{
3603c2aa98e2SPeter Wemm 		switch (rcode)
3604c2aa98e2SPeter Wemm 		{
3605c2aa98e2SPeter Wemm 		  case EX_USAGE:
360606f25ae9SGregory Neil Shapiro 			status = "5.5.4";
3607c2aa98e2SPeter Wemm 			break;
3608c2aa98e2SPeter Wemm 
3609c2aa98e2SPeter Wemm 		  case EX_DATAERR:
361006f25ae9SGregory Neil Shapiro 			status = "5.5.2";
3611c2aa98e2SPeter Wemm 			break;
3612c2aa98e2SPeter Wemm 
3613c2aa98e2SPeter Wemm 		  case EX_NOUSER:
361406f25ae9SGregory Neil Shapiro 			status = "5.1.1";
3615c2aa98e2SPeter Wemm 			break;
3616c2aa98e2SPeter Wemm 
3617c2aa98e2SPeter Wemm 		  case EX_NOHOST:
361806f25ae9SGregory Neil Shapiro 			status = "5.1.2";
3619c2aa98e2SPeter Wemm 			break;
3620c2aa98e2SPeter Wemm 
3621c2aa98e2SPeter Wemm 		  case EX_NOINPUT:
3622c2aa98e2SPeter Wemm 		  case EX_CANTCREAT:
3623c2aa98e2SPeter Wemm 		  case EX_NOPERM:
362406f25ae9SGregory Neil Shapiro 			status = "5.3.0";
3625c2aa98e2SPeter Wemm 			break;
3626c2aa98e2SPeter Wemm 
3627c2aa98e2SPeter Wemm 		  case EX_UNAVAILABLE:
3628c2aa98e2SPeter Wemm 		  case EX_SOFTWARE:
3629c2aa98e2SPeter Wemm 		  case EX_OSFILE:
3630c2aa98e2SPeter Wemm 		  case EX_PROTOCOL:
3631c2aa98e2SPeter Wemm 		  case EX_CONFIG:
363206f25ae9SGregory Neil Shapiro 			status = "5.5.0";
3633c2aa98e2SPeter Wemm 			break;
3634c2aa98e2SPeter Wemm 
3635c2aa98e2SPeter Wemm 		  case EX_OSERR:
3636c2aa98e2SPeter Wemm 		  case EX_IOERR:
363706f25ae9SGregory Neil Shapiro 			status = "4.5.0";
3638c2aa98e2SPeter Wemm 			break;
3639c2aa98e2SPeter Wemm 
3640c2aa98e2SPeter Wemm 		  case EX_TEMPFAIL:
364106f25ae9SGregory Neil Shapiro 			status = "4.2.0";
3642c2aa98e2SPeter Wemm 			break;
3643c2aa98e2SPeter Wemm 		}
3644c2aa98e2SPeter Wemm 	}
3645c2aa98e2SPeter Wemm 
364606f25ae9SGregory Neil Shapiro 	/* new status? */
364706f25ae9SGregory Neil Shapiro 	if (status != NULL && *status != '\0' && (ovr || q->q_status == NULL ||
364806f25ae9SGregory Neil Shapiro 	    *q->q_status == '\0' || *q->q_status < *status))
364906f25ae9SGregory Neil Shapiro 	{
365006f25ae9SGregory Neil Shapiro 		q->q_status = status;
365106f25ae9SGregory Neil Shapiro 		q->q_rstatus = rstatus;
365206f25ae9SGregory Neil Shapiro 	}
3653c2aa98e2SPeter Wemm 	if (rcode != EX_OK && q->q_rstatus == NULL &&
3654c2aa98e2SPeter Wemm 	    q->q_mailer != NULL && q->q_mailer->m_diagtype != NULL &&
365540266059SGregory Neil Shapiro 	    sm_strcasecmp(q->q_mailer->m_diagtype, "X-UNIX") == 0)
3656c2aa98e2SPeter Wemm 	{
365706f25ae9SGregory Neil Shapiro 		char buf[16];
3658c2aa98e2SPeter Wemm 
365940266059SGregory Neil Shapiro 		(void) sm_snprintf(buf, sizeof buf, "%d", rcode);
366040266059SGregory Neil Shapiro 		q->q_rstatus = sm_rpool_strdup_x(e->e_rpool, buf);
3661c2aa98e2SPeter Wemm 	}
366206f25ae9SGregory Neil Shapiro 
366306f25ae9SGregory Neil Shapiro 	q->q_statdate = curtime();
366406f25ae9SGregory Neil Shapiro 	if (CurHostName != NULL && CurHostName[0] != '\0' &&
366506f25ae9SGregory Neil Shapiro 	    mci != NULL && !bitset(M_LOCALMAILER, mci->mci_flags))
366640266059SGregory Neil Shapiro 		q->q_statmta = sm_rpool_strdup_x(e->e_rpool, CurHostName);
366740266059SGregory Neil Shapiro 
366840266059SGregory Neil Shapiro 	/* restore errno */
366940266059SGregory Neil Shapiro 	errno = save_errno;
3670c2aa98e2SPeter Wemm }
367140266059SGregory Neil Shapiro /*
3672c2aa98e2SPeter Wemm **  ENDMAILER -- Wait for mailer to terminate.
3673c2aa98e2SPeter Wemm **
3674c2aa98e2SPeter Wemm **	We should never get fatal errors (e.g., segmentation
3675c2aa98e2SPeter Wemm **	violation), so we report those specially.  For other
3676c2aa98e2SPeter Wemm **	errors, we choose a status message (into statmsg),
3677c2aa98e2SPeter Wemm **	and if it represents an error, we print it.
3678c2aa98e2SPeter Wemm **
3679c2aa98e2SPeter Wemm **	Parameters:
368013058a91SGregory Neil Shapiro **		mci -- the mailer connection info.
3681c2aa98e2SPeter Wemm **		e -- the current envelope.
3682c2aa98e2SPeter Wemm **		pv -- the parameter vector that invoked the mailer
3683c2aa98e2SPeter Wemm **			(for error messages).
3684c2aa98e2SPeter Wemm **
3685c2aa98e2SPeter Wemm **	Returns:
3686c2aa98e2SPeter Wemm **		exit code of mailer.
3687c2aa98e2SPeter Wemm **
3688c2aa98e2SPeter Wemm **	Side Effects:
3689c2aa98e2SPeter Wemm **		none.
3690c2aa98e2SPeter Wemm */
3691c2aa98e2SPeter Wemm 
369206f25ae9SGregory Neil Shapiro static jmp_buf	EndWaitTimeout;
369306f25ae9SGregory Neil Shapiro 
369406f25ae9SGregory Neil Shapiro static void
369506f25ae9SGregory Neil Shapiro endwaittimeout()
369606f25ae9SGregory Neil Shapiro {
36978774250cSGregory Neil Shapiro 	/*
36988774250cSGregory Neil Shapiro 	**  NOTE: THIS CAN BE CALLED FROM A SIGNAL HANDLER.  DO NOT ADD
36998774250cSGregory Neil Shapiro 	**	ANYTHING TO THIS ROUTINE UNLESS YOU KNOW WHAT YOU ARE
37008774250cSGregory Neil Shapiro 	**	DOING.
37018774250cSGregory Neil Shapiro 	*/
37028774250cSGregory Neil Shapiro 
370306f25ae9SGregory Neil Shapiro 	errno = ETIMEDOUT;
370406f25ae9SGregory Neil Shapiro 	longjmp(EndWaitTimeout, 1);
370506f25ae9SGregory Neil Shapiro }
370606f25ae9SGregory Neil Shapiro 
3707c2aa98e2SPeter Wemm int
3708c2aa98e2SPeter Wemm endmailer(mci, e, pv)
3709c2aa98e2SPeter Wemm 	register MCI *mci;
3710c2aa98e2SPeter Wemm 	register ENVELOPE *e;
3711c2aa98e2SPeter Wemm 	char **pv;
3712c2aa98e2SPeter Wemm {
3713c2aa98e2SPeter Wemm 	int st;
371406f25ae9SGregory Neil Shapiro 	int save_errno = errno;
371506f25ae9SGregory Neil Shapiro 	char buf[MAXLINE];
371640266059SGregory Neil Shapiro 	SM_EVENT *ev = NULL;
371706f25ae9SGregory Neil Shapiro 
3718c2aa98e2SPeter Wemm 
3719c2aa98e2SPeter Wemm 	mci_unlock_host(mci);
3720c2aa98e2SPeter Wemm 
37218774250cSGregory Neil Shapiro 	/* close output to mailer */
37228774250cSGregory Neil Shapiro 	if (mci->mci_out != NULL)
372340266059SGregory Neil Shapiro 		(void) sm_io_close(mci->mci_out, SM_TIME_DEFAULT);
37248774250cSGregory Neil Shapiro 
37258774250cSGregory Neil Shapiro 	/* copy any remaining input to transcript */
37268774250cSGregory Neil Shapiro 	if (mci->mci_in != NULL && mci->mci_state != MCIS_ERROR &&
37278774250cSGregory Neil Shapiro 	    e->e_xfp != NULL)
37288774250cSGregory Neil Shapiro 	{
37298774250cSGregory Neil Shapiro 		while (sfgets(buf, sizeof buf, mci->mci_in,
37308774250cSGregory Neil Shapiro 			      TimeOuts.to_quit, "Draining Input") != NULL)
373140266059SGregory Neil Shapiro 			(void) sm_io_fputs(e->e_xfp, SM_TIME_DEFAULT, buf);
37328774250cSGregory Neil Shapiro 	}
37338774250cSGregory Neil Shapiro 
373406f25ae9SGregory Neil Shapiro #if SASL
373540266059SGregory Neil Shapiro 	/* close SASL connection */
373606f25ae9SGregory Neil Shapiro 	if (bitset(MCIF_AUTHACT, mci->mci_flags))
373706f25ae9SGregory Neil Shapiro 	{
373806f25ae9SGregory Neil Shapiro 		sasl_dispose(&mci->mci_conn);
373906f25ae9SGregory Neil Shapiro 		mci->mci_flags &= ~MCIF_AUTHACT;
374006f25ae9SGregory Neil Shapiro 	}
374106f25ae9SGregory Neil Shapiro #endif /* SASL */
374206f25ae9SGregory Neil Shapiro 
374306f25ae9SGregory Neil Shapiro #if STARTTLS
374406f25ae9SGregory Neil Shapiro 	/* shutdown TLS */
374506f25ae9SGregory Neil Shapiro 	(void) endtlsclt(mci);
374606f25ae9SGregory Neil Shapiro #endif /* STARTTLS */
374706f25ae9SGregory Neil Shapiro 
374806f25ae9SGregory Neil Shapiro 	/* now close the input */
374906f25ae9SGregory Neil Shapiro 	if (mci->mci_in != NULL)
375040266059SGregory Neil Shapiro 		(void) sm_io_close(mci->mci_in, SM_TIME_DEFAULT);
3751c2aa98e2SPeter Wemm 	mci->mci_in = mci->mci_out = NULL;
3752c2aa98e2SPeter Wemm 	mci->mci_state = MCIS_CLOSED;
3753c2aa98e2SPeter Wemm 
375406f25ae9SGregory Neil Shapiro 	errno = save_errno;
375506f25ae9SGregory Neil Shapiro 
3756c2aa98e2SPeter Wemm 	/* in the IPC case there is nothing to wait for */
3757c2aa98e2SPeter Wemm 	if (mci->mci_pid == 0)
375806f25ae9SGregory Neil Shapiro 		return EX_OK;
3759c2aa98e2SPeter Wemm 
376006f25ae9SGregory Neil Shapiro 	/* put a timeout around the wait */
376106f25ae9SGregory Neil Shapiro 	if (mci->mci_mailer->m_wait > 0)
376206f25ae9SGregory Neil Shapiro 	{
376306f25ae9SGregory Neil Shapiro 		if (setjmp(EndWaitTimeout) == 0)
376440266059SGregory Neil Shapiro 			ev = sm_setevent(mci->mci_mailer->m_wait,
376506f25ae9SGregory Neil Shapiro 					 endwaittimeout, 0);
376606f25ae9SGregory Neil Shapiro 		else
376706f25ae9SGregory Neil Shapiro 		{
376842e5d165SGregory Neil Shapiro 			syserr("endmailer %s: wait timeout (%ld)",
376906f25ae9SGregory Neil Shapiro 			       mci->mci_mailer->m_name,
377042e5d165SGregory Neil Shapiro 			       (long) mci->mci_mailer->m_wait);
377106f25ae9SGregory Neil Shapiro 			return EX_TEMPFAIL;
377206f25ae9SGregory Neil Shapiro 		}
377306f25ae9SGregory Neil Shapiro 	}
3774c2aa98e2SPeter Wemm 
377506f25ae9SGregory Neil Shapiro 	/* wait for the mailer process, collect status */
3776c2aa98e2SPeter Wemm 	st = waitfor(mci->mci_pid);
377706f25ae9SGregory Neil Shapiro 	save_errno = errno;
377806f25ae9SGregory Neil Shapiro 	if (ev != NULL)
377940266059SGregory Neil Shapiro 		sm_clrevent(ev);
378006f25ae9SGregory Neil Shapiro 	errno = save_errno;
378106f25ae9SGregory Neil Shapiro 
3782c2aa98e2SPeter Wemm 	if (st == -1)
3783c2aa98e2SPeter Wemm 	{
3784c2aa98e2SPeter Wemm 		syserr("endmailer %s: wait", mci->mci_mailer->m_name);
378506f25ae9SGregory Neil Shapiro 		return EX_SOFTWARE;
3786c2aa98e2SPeter Wemm 	}
3787c2aa98e2SPeter Wemm 
3788c2aa98e2SPeter Wemm 	if (WIFEXITED(st))
3789c2aa98e2SPeter Wemm 	{
3790c2aa98e2SPeter Wemm 		/* normal death -- return status */
3791c2aa98e2SPeter Wemm 		return (WEXITSTATUS(st));
3792c2aa98e2SPeter Wemm 	}
3793c2aa98e2SPeter Wemm 
3794c2aa98e2SPeter Wemm 	/* it died a horrid death */
379506f25ae9SGregory Neil Shapiro 	syserr("451 4.3.0 mailer %s died with signal %d%s",
379606f25ae9SGregory Neil Shapiro 		mci->mci_mailer->m_name, WTERMSIG(st),
379706f25ae9SGregory Neil Shapiro 		WCOREDUMP(st) ? " (core dumped)" :
379806f25ae9SGregory Neil Shapiro 		(WIFSTOPPED(st) ? " (stopped)" : ""));
3799c2aa98e2SPeter Wemm 
3800c2aa98e2SPeter Wemm 	/* log the arguments */
3801c2aa98e2SPeter Wemm 	if (pv != NULL && e->e_xfp != NULL)
3802c2aa98e2SPeter Wemm 	{
3803c2aa98e2SPeter Wemm 		register char **av;
3804c2aa98e2SPeter Wemm 
380540266059SGregory Neil Shapiro 		(void) sm_io_fprintf(e->e_xfp, SM_TIME_DEFAULT, "Arguments:");
3806c2aa98e2SPeter Wemm 		for (av = pv; *av != NULL; av++)
380740266059SGregory Neil Shapiro 			(void) sm_io_fprintf(e->e_xfp, SM_TIME_DEFAULT, " %s",
380840266059SGregory Neil Shapiro 					     *av);
380940266059SGregory Neil Shapiro 		(void) sm_io_fprintf(e->e_xfp, SM_TIME_DEFAULT, "\n");
3810c2aa98e2SPeter Wemm 	}
3811c2aa98e2SPeter Wemm 
3812c2aa98e2SPeter Wemm 	ExitStat = EX_TEMPFAIL;
381306f25ae9SGregory Neil Shapiro 	return EX_TEMPFAIL;
3814c2aa98e2SPeter Wemm }
381540266059SGregory Neil Shapiro /*
3816c2aa98e2SPeter Wemm **  GIVERESPONSE -- Interpret an error response from a mailer
3817c2aa98e2SPeter Wemm **
3818c2aa98e2SPeter Wemm **	Parameters:
381906f25ae9SGregory Neil Shapiro **		status -- the status code from the mailer (high byte
3820c2aa98e2SPeter Wemm **			only; core dumps must have been taken care of
3821c2aa98e2SPeter Wemm **			already).
382206f25ae9SGregory Neil Shapiro **		dsn -- the DSN associated with the address, if any.
3823c2aa98e2SPeter Wemm **		m -- the mailer info for this mailer.
3824c2aa98e2SPeter Wemm **		mci -- the mailer connection info -- can be NULL if the
3825c2aa98e2SPeter Wemm **			response is given before the connection is made.
3826c2aa98e2SPeter Wemm **		ctladdr -- the controlling address for the recipient
3827c2aa98e2SPeter Wemm **			address(es).
3828c2aa98e2SPeter Wemm **		xstart -- the transaction start time, for computing
3829c2aa98e2SPeter Wemm **			transaction delays.
3830c2aa98e2SPeter Wemm **		e -- the current envelope.
383140266059SGregory Neil Shapiro **		to -- the current recipient (NULL if none).
3832c2aa98e2SPeter Wemm **
3833c2aa98e2SPeter Wemm **	Returns:
3834c2aa98e2SPeter Wemm **		none.
3835c2aa98e2SPeter Wemm **
3836c2aa98e2SPeter Wemm **	Side Effects:
3837c2aa98e2SPeter Wemm **		Errors may be incremented.
3838c2aa98e2SPeter Wemm **		ExitStat may be set.
3839c2aa98e2SPeter Wemm */
3840c2aa98e2SPeter Wemm 
3841c2aa98e2SPeter Wemm void
384240266059SGregory Neil Shapiro giveresponse(status, dsn, m, mci, ctladdr, xstart, e, to)
384306f25ae9SGregory Neil Shapiro 	int status;
384406f25ae9SGregory Neil Shapiro 	char *dsn;
3845c2aa98e2SPeter Wemm 	register MAILER *m;
3846c2aa98e2SPeter Wemm 	register MCI *mci;
3847c2aa98e2SPeter Wemm 	ADDRESS *ctladdr;
3848c2aa98e2SPeter Wemm 	time_t xstart;
3849c2aa98e2SPeter Wemm 	ENVELOPE *e;
385040266059SGregory Neil Shapiro 	ADDRESS *to;
3851c2aa98e2SPeter Wemm {
3852c2aa98e2SPeter Wemm 	register const char *statmsg;
385306f25ae9SGregory Neil Shapiro 	int errnum = errno;
385406f25ae9SGregory Neil Shapiro 	int off = 4;
385540266059SGregory Neil Shapiro 	bool usestat = false;
385606f25ae9SGregory Neil Shapiro 	char dsnbuf[ENHSCLEN];
3857c2aa98e2SPeter Wemm 	char buf[MAXLINE];
385840266059SGregory Neil Shapiro 	char *exmsg;
3859c2aa98e2SPeter Wemm 
3860c2aa98e2SPeter Wemm 	if (e == NULL)
3861c2aa98e2SPeter Wemm 		syserr("giveresponse: null envelope");
3862c2aa98e2SPeter Wemm 
3863c2aa98e2SPeter Wemm 	/*
3864c2aa98e2SPeter Wemm 	**  Compute status message from code.
3865c2aa98e2SPeter Wemm 	*/
3866c2aa98e2SPeter Wemm 
386740266059SGregory Neil Shapiro 	exmsg = sm_sysexmsg(status);
386806f25ae9SGregory Neil Shapiro 	if (status == 0)
3869c2aa98e2SPeter Wemm 	{
387006f25ae9SGregory Neil Shapiro 		statmsg = "250 2.0.0 Sent";
3871c2aa98e2SPeter Wemm 		if (e->e_statmsg != NULL)
3872c2aa98e2SPeter Wemm 		{
387340266059SGregory Neil Shapiro 			(void) sm_snprintf(buf, sizeof buf, "%s (%s)",
387406f25ae9SGregory Neil Shapiro 					   statmsg,
387506f25ae9SGregory Neil Shapiro 					   shortenstring(e->e_statmsg, 403));
3876c2aa98e2SPeter Wemm 			statmsg = buf;
3877c2aa98e2SPeter Wemm 		}
3878c2aa98e2SPeter Wemm 	}
387940266059SGregory Neil Shapiro 	else if (exmsg == NULL)
3880c2aa98e2SPeter Wemm 	{
388140266059SGregory Neil Shapiro 		(void) sm_snprintf(buf, sizeof buf,
388206f25ae9SGregory Neil Shapiro 				   "554 5.3.0 unknown mailer error %d",
388306f25ae9SGregory Neil Shapiro 				   status);
388406f25ae9SGregory Neil Shapiro 		status = EX_UNAVAILABLE;
3885c2aa98e2SPeter Wemm 		statmsg = buf;
388640266059SGregory Neil Shapiro 		usestat = true;
3887c2aa98e2SPeter Wemm 	}
388806f25ae9SGregory Neil Shapiro 	else if (status == EX_TEMPFAIL)
3889c2aa98e2SPeter Wemm 	{
3890c2aa98e2SPeter Wemm 		char *bp = buf;
3891c2aa98e2SPeter Wemm 
389240266059SGregory Neil Shapiro 		(void) sm_strlcpy(bp, exmsg + 1, SPACELEFT(buf, bp));
3893c2aa98e2SPeter Wemm 		bp += strlen(bp);
3894c2aa98e2SPeter Wemm #if NAMED_BIND
3895c2aa98e2SPeter Wemm 		if (h_errno == TRY_AGAIN)
389640266059SGregory Neil Shapiro 			statmsg = sm_errstring(h_errno + E_DNSBASE);
3897c2aa98e2SPeter Wemm 		else
389806f25ae9SGregory Neil Shapiro #endif /* NAMED_BIND */
3899c2aa98e2SPeter Wemm 		{
390006f25ae9SGregory Neil Shapiro 			if (errnum != 0)
390140266059SGregory Neil Shapiro 				statmsg = sm_errstring(errnum);
3902c2aa98e2SPeter Wemm 			else
3903c2aa98e2SPeter Wemm 				statmsg = SmtpError;
3904c2aa98e2SPeter Wemm 		}
3905c2aa98e2SPeter Wemm 		if (statmsg != NULL && statmsg[0] != '\0')
390606f25ae9SGregory Neil Shapiro 		{
390706f25ae9SGregory Neil Shapiro 			switch (errnum)
390806f25ae9SGregory Neil Shapiro 			{
390906f25ae9SGregory Neil Shapiro #ifdef ENETDOWN
391006f25ae9SGregory Neil Shapiro 			  case ENETDOWN:	/* Network is down */
391106f25ae9SGregory Neil Shapiro #endif /* ENETDOWN */
391206f25ae9SGregory Neil Shapiro #ifdef ENETUNREACH
391306f25ae9SGregory Neil Shapiro 			  case ENETUNREACH:	/* Network is unreachable */
391406f25ae9SGregory Neil Shapiro #endif /* ENETUNREACH */
391506f25ae9SGregory Neil Shapiro #ifdef ENETRESET
391606f25ae9SGregory Neil Shapiro 			  case ENETRESET:	/* Network dropped connection on reset */
391706f25ae9SGregory Neil Shapiro #endif /* ENETRESET */
391806f25ae9SGregory Neil Shapiro #ifdef ECONNABORTED
391906f25ae9SGregory Neil Shapiro 			  case ECONNABORTED:	/* Software caused connection abort */
392006f25ae9SGregory Neil Shapiro #endif /* ECONNABORTED */
392106f25ae9SGregory Neil Shapiro #ifdef EHOSTDOWN
392206f25ae9SGregory Neil Shapiro 			  case EHOSTDOWN:	/* Host is down */
392306f25ae9SGregory Neil Shapiro #endif /* EHOSTDOWN */
392406f25ae9SGregory Neil Shapiro #ifdef EHOSTUNREACH
392506f25ae9SGregory Neil Shapiro 			  case EHOSTUNREACH:	/* No route to host */
392606f25ae9SGregory Neil Shapiro #endif /* EHOSTUNREACH */
392706f25ae9SGregory Neil Shapiro 				if (mci->mci_host != NULL)
392806f25ae9SGregory Neil Shapiro 				{
392940266059SGregory Neil Shapiro 					(void) sm_strlcpyn(bp,
393040266059SGregory Neil Shapiro 							   SPACELEFT(buf, bp),
393140266059SGregory Neil Shapiro 							   2, ": ",
393240266059SGregory Neil Shapiro 							   mci->mci_host);
393306f25ae9SGregory Neil Shapiro 					bp += strlen(bp);
393406f25ae9SGregory Neil Shapiro 				}
393506f25ae9SGregory Neil Shapiro 				break;
393606f25ae9SGregory Neil Shapiro 			}
393740266059SGregory Neil Shapiro 			(void) sm_strlcpyn(bp, SPACELEFT(buf, bp), 2, ": ",
393840266059SGregory Neil Shapiro 					   statmsg);
393940266059SGregory Neil Shapiro 			usestat = true;
394006f25ae9SGregory Neil Shapiro 		}
3941c2aa98e2SPeter Wemm 		statmsg = buf;
3942c2aa98e2SPeter Wemm 	}
3943c2aa98e2SPeter Wemm #if NAMED_BIND
394406f25ae9SGregory Neil Shapiro 	else if (status == EX_NOHOST && h_errno != 0)
3945c2aa98e2SPeter Wemm 	{
394640266059SGregory Neil Shapiro 		statmsg = sm_errstring(h_errno + E_DNSBASE);
394740266059SGregory Neil Shapiro 		(void) sm_snprintf(buf, sizeof buf, "%s (%s)", exmsg + 1,
394840266059SGregory Neil Shapiro 				   statmsg);
3949c2aa98e2SPeter Wemm 		statmsg = buf;
395040266059SGregory Neil Shapiro 		usestat = true;
3951c2aa98e2SPeter Wemm 	}
395206f25ae9SGregory Neil Shapiro #endif /* NAMED_BIND */
3953c2aa98e2SPeter Wemm 	else
3954c2aa98e2SPeter Wemm 	{
395540266059SGregory Neil Shapiro 		statmsg = exmsg;
395606f25ae9SGregory Neil Shapiro 		if (*statmsg++ == ':' && errnum != 0)
3957c2aa98e2SPeter Wemm 		{
395840266059SGregory Neil Shapiro 			(void) sm_snprintf(buf, sizeof buf, "%s: %s", statmsg,
395940266059SGregory Neil Shapiro 					   sm_errstring(errnum));
3960c2aa98e2SPeter Wemm 			statmsg = buf;
396140266059SGregory Neil Shapiro 			usestat = true;
3962c2aa98e2SPeter Wemm 		}
3963605302a5SGregory Neil Shapiro 		else if (bitnset(M_LMTP, m->m_flags) && e->e_statmsg != NULL)
3964605302a5SGregory Neil Shapiro 		{
3965605302a5SGregory Neil Shapiro 			(void) sm_snprintf(buf, sizeof buf, "%s (%s)", statmsg,
3966605302a5SGregory Neil Shapiro 					   shortenstring(e->e_statmsg, 403));
3967605302a5SGregory Neil Shapiro 			statmsg = buf;
3968605302a5SGregory Neil Shapiro 			usestat = true;
3969605302a5SGregory Neil Shapiro 		}
3970c2aa98e2SPeter Wemm 	}
3971c2aa98e2SPeter Wemm 
3972c2aa98e2SPeter Wemm 	/*
3973c2aa98e2SPeter Wemm 	**  Print the message as appropriate
3974c2aa98e2SPeter Wemm 	*/
3975c2aa98e2SPeter Wemm 
397606f25ae9SGregory Neil Shapiro 	if (status == EX_OK || status == EX_TEMPFAIL)
3977c2aa98e2SPeter Wemm 	{
3978c2aa98e2SPeter Wemm 		extern char MsgBuf[];
3979c2aa98e2SPeter Wemm 
398006f25ae9SGregory Neil Shapiro 		if ((off = isenhsc(statmsg + 4, ' ')) > 0)
398106f25ae9SGregory Neil Shapiro 		{
398206f25ae9SGregory Neil Shapiro 			if (dsn == NULL)
398306f25ae9SGregory Neil Shapiro 			{
398440266059SGregory Neil Shapiro 				(void) sm_snprintf(dsnbuf, sizeof dsnbuf,
398506f25ae9SGregory Neil Shapiro 						   "%.*s", off, statmsg + 4);
398606f25ae9SGregory Neil Shapiro 				dsn = dsnbuf;
398706f25ae9SGregory Neil Shapiro 			}
398806f25ae9SGregory Neil Shapiro 			off += 5;
398906f25ae9SGregory Neil Shapiro 		}
399006f25ae9SGregory Neil Shapiro 		else
399106f25ae9SGregory Neil Shapiro 		{
399206f25ae9SGregory Neil Shapiro 			off = 4;
399306f25ae9SGregory Neil Shapiro 		}
399406f25ae9SGregory Neil Shapiro 		message("%s", statmsg + off);
399506f25ae9SGregory Neil Shapiro 		if (status == EX_TEMPFAIL && e->e_xfp != NULL)
399640266059SGregory Neil Shapiro 			(void) sm_io_fprintf(e->e_xfp, SM_TIME_DEFAULT, "%s\n",
399740266059SGregory Neil Shapiro 					     &MsgBuf[4]);
3998c2aa98e2SPeter Wemm 	}
3999c2aa98e2SPeter Wemm 	else
4000c2aa98e2SPeter Wemm 	{
400106f25ae9SGregory Neil Shapiro 		char mbuf[ENHSCLEN + 4];
4002c2aa98e2SPeter Wemm 
4003c2aa98e2SPeter Wemm 		Errors++;
400406f25ae9SGregory Neil Shapiro 		if ((off = isenhsc(statmsg + 4, ' ')) > 0 &&
400506f25ae9SGregory Neil Shapiro 		    off < sizeof mbuf - 4)
400606f25ae9SGregory Neil Shapiro 		{
400706f25ae9SGregory Neil Shapiro 			if (dsn == NULL)
400806f25ae9SGregory Neil Shapiro 			{
400940266059SGregory Neil Shapiro 				(void) sm_snprintf(dsnbuf, sizeof dsnbuf,
401006f25ae9SGregory Neil Shapiro 						   "%.*s", off, statmsg + 4);
401106f25ae9SGregory Neil Shapiro 				dsn = dsnbuf;
401206f25ae9SGregory Neil Shapiro 			}
401306f25ae9SGregory Neil Shapiro 			off += 5;
401440266059SGregory Neil Shapiro 
401540266059SGregory Neil Shapiro 			/* copy only part of statmsg to mbuf */
401640266059SGregory Neil Shapiro 			(void) sm_strlcpy(mbuf, statmsg, off);
401740266059SGregory Neil Shapiro 			(void) sm_strlcat(mbuf, " %s", sizeof mbuf);
401806f25ae9SGregory Neil Shapiro 		}
401906f25ae9SGregory Neil Shapiro 		else
402006f25ae9SGregory Neil Shapiro 		{
402106f25ae9SGregory Neil Shapiro 			dsnbuf[0] = '\0';
402240266059SGregory Neil Shapiro 			(void) sm_snprintf(mbuf, sizeof mbuf, "%.3s %%s",
402340266059SGregory Neil Shapiro 					   statmsg);
402406f25ae9SGregory Neil Shapiro 			off = 4;
402506f25ae9SGregory Neil Shapiro 		}
402606f25ae9SGregory Neil Shapiro 		usrerr(mbuf, &statmsg[off]);
4027c2aa98e2SPeter Wemm 	}
4028c2aa98e2SPeter Wemm 
4029c2aa98e2SPeter Wemm 	/*
4030c2aa98e2SPeter Wemm 	**  Final cleanup.
4031c2aa98e2SPeter Wemm 	**	Log a record of the transaction.  Compute the new
4032c2aa98e2SPeter Wemm 	**	ExitStat -- if we already had an error, stick with
4033c2aa98e2SPeter Wemm 	**	that.
4034c2aa98e2SPeter Wemm 	*/
4035c2aa98e2SPeter Wemm 
4036c2aa98e2SPeter Wemm 	if (OpMode != MD_VERIFY && !bitset(EF_VRFYONLY, e->e_flags) &&
403706f25ae9SGregory Neil Shapiro 	    LogLevel > ((status == EX_TEMPFAIL) ? 8 : (status == EX_OK) ? 7 : 6))
403806f25ae9SGregory Neil Shapiro 		logdelivery(m, mci, dsn, statmsg + off, ctladdr, xstart, e);
4039c2aa98e2SPeter Wemm 
4040c2aa98e2SPeter Wemm 	if (tTd(11, 2))
404140266059SGregory Neil Shapiro 		sm_dprintf("giveresponse: status=%d, dsn=%s, e->e_message=%s, errnum=%d\n",
404206f25ae9SGregory Neil Shapiro 			   status,
404306f25ae9SGregory Neil Shapiro 			   dsn == NULL ? "<NULL>" : dsn,
404440266059SGregory Neil Shapiro 			   e->e_message == NULL ? "<NULL>" : e->e_message,
404540266059SGregory Neil Shapiro 			   errnum);
4046c2aa98e2SPeter Wemm 
404706f25ae9SGregory Neil Shapiro 	if (status != EX_TEMPFAIL)
404806f25ae9SGregory Neil Shapiro 		setstat(status);
404906f25ae9SGregory Neil Shapiro 	if (status != EX_OK && (status != EX_TEMPFAIL || e->e_message == NULL))
405040266059SGregory Neil Shapiro 		e->e_message = sm_rpool_strdup_x(e->e_rpool, statmsg + off);
405140266059SGregory Neil Shapiro 	if (status != EX_OK && to != NULL && to->q_message == NULL)
4052c2aa98e2SPeter Wemm 	{
405340266059SGregory Neil Shapiro 		if (!usestat && e->e_message != NULL)
405440266059SGregory Neil Shapiro 			to->q_message = sm_rpool_strdup_x(e->e_rpool,
405540266059SGregory Neil Shapiro 							  e->e_message);
405640266059SGregory Neil Shapiro 		else
405740266059SGregory Neil Shapiro 			to->q_message = sm_rpool_strdup_x(e->e_rpool,
405840266059SGregory Neil Shapiro 							  statmsg + off);
4059c2aa98e2SPeter Wemm 	}
4060c2aa98e2SPeter Wemm 	errno = 0;
4061602a2b1bSGregory Neil Shapiro 	SM_SET_H_ERRNO(0);
4062c2aa98e2SPeter Wemm }
406340266059SGregory Neil Shapiro /*
4064c2aa98e2SPeter Wemm **  LOGDELIVERY -- log the delivery in the system log
4065c2aa98e2SPeter Wemm **
4066c2aa98e2SPeter Wemm **	Care is taken to avoid logging lines that are too long, because
4067c2aa98e2SPeter Wemm **	some versions of syslog have an unfortunate proclivity for core
4068c2aa98e2SPeter Wemm **	dumping.  This is a hack, to be sure, that is at best empirical.
4069c2aa98e2SPeter Wemm **
4070c2aa98e2SPeter Wemm **	Parameters:
4071c2aa98e2SPeter Wemm **		m -- the mailer info.  Can be NULL for initial queue.
4072c2aa98e2SPeter Wemm **		mci -- the mailer connection info -- can be NULL if the
407306f25ae9SGregory Neil Shapiro **			log is occurring when no connection is active.
407406f25ae9SGregory Neil Shapiro **		dsn -- the DSN attached to the status.
407506f25ae9SGregory Neil Shapiro **		status -- the message to print for the status.
4076c2aa98e2SPeter Wemm **		ctladdr -- the controlling address for the to list.
4077c2aa98e2SPeter Wemm **		xstart -- the transaction start time, used for
4078c2aa98e2SPeter Wemm **			computing transaction delay.
4079c2aa98e2SPeter Wemm **		e -- the current envelope.
4080c2aa98e2SPeter Wemm **
4081c2aa98e2SPeter Wemm **	Returns:
4082c2aa98e2SPeter Wemm **		none
4083c2aa98e2SPeter Wemm **
4084c2aa98e2SPeter Wemm **	Side Effects:
4085c2aa98e2SPeter Wemm **		none
4086c2aa98e2SPeter Wemm */
4087c2aa98e2SPeter Wemm 
4088c2aa98e2SPeter Wemm void
408906f25ae9SGregory Neil Shapiro logdelivery(m, mci, dsn, status, ctladdr, xstart, e)
4090c2aa98e2SPeter Wemm 	MAILER *m;
4091c2aa98e2SPeter Wemm 	register MCI *mci;
409206f25ae9SGregory Neil Shapiro 	char *dsn;
409306f25ae9SGregory Neil Shapiro 	const char *status;
4094c2aa98e2SPeter Wemm 	ADDRESS *ctladdr;
4095c2aa98e2SPeter Wemm 	time_t xstart;
4096c2aa98e2SPeter Wemm 	register ENVELOPE *e;
4097c2aa98e2SPeter Wemm {
4098c2aa98e2SPeter Wemm 	register char *bp;
4099c2aa98e2SPeter Wemm 	register char *p;
4100c2aa98e2SPeter Wemm 	int l;
410140266059SGregory Neil Shapiro 	time_t now = curtime();
4102c2aa98e2SPeter Wemm 	char buf[1024];
4103c2aa98e2SPeter Wemm 
4104c2aa98e2SPeter Wemm #if (SYSLOG_BUFSIZE) >= 256
4105c2aa98e2SPeter Wemm 	/* ctladdr: max 106 bytes */
4106c2aa98e2SPeter Wemm 	bp = buf;
4107c2aa98e2SPeter Wemm 	if (ctladdr != NULL)
4108c2aa98e2SPeter Wemm 	{
410940266059SGregory Neil Shapiro 		(void) sm_strlcpyn(bp, SPACELEFT(buf, bp), 2, ", ctladdr=",
4110c2aa98e2SPeter Wemm 				   shortenstring(ctladdr->q_paddr, 83));
4111c2aa98e2SPeter Wemm 		bp += strlen(bp);
4112c2aa98e2SPeter Wemm 		if (bitset(QGOODUID, ctladdr->q_flags))
4113c2aa98e2SPeter Wemm 		{
411440266059SGregory Neil Shapiro 			(void) sm_snprintf(bp, SPACELEFT(buf, bp), " (%d/%d)",
411506f25ae9SGregory Neil Shapiro 					   (int) ctladdr->q_uid,
411606f25ae9SGregory Neil Shapiro 					   (int) ctladdr->q_gid);
4117c2aa98e2SPeter Wemm 			bp += strlen(bp);
4118c2aa98e2SPeter Wemm 		}
4119c2aa98e2SPeter Wemm 	}
4120c2aa98e2SPeter Wemm 
4121c2aa98e2SPeter Wemm 	/* delay & xdelay: max 41 bytes */
412240266059SGregory Neil Shapiro 	(void) sm_strlcpyn(bp, SPACELEFT(buf, bp), 2, ", delay=",
412340266059SGregory Neil Shapiro 			   pintvl(now - e->e_ctime, true));
4124c2aa98e2SPeter Wemm 	bp += strlen(bp);
4125c2aa98e2SPeter Wemm 
4126c2aa98e2SPeter Wemm 	if (xstart != (time_t) 0)
4127c2aa98e2SPeter Wemm 	{
412840266059SGregory Neil Shapiro 		(void) sm_strlcpyn(bp, SPACELEFT(buf, bp), 2, ", xdelay=",
412940266059SGregory Neil Shapiro 				   pintvl(now - xstart, true));
4130c2aa98e2SPeter Wemm 		bp += strlen(bp);
4131c2aa98e2SPeter Wemm 	}
4132c2aa98e2SPeter Wemm 
4133c2aa98e2SPeter Wemm 	/* mailer: assume about 19 bytes (max 10 byte mailer name) */
4134c2aa98e2SPeter Wemm 	if (m != NULL)
4135c2aa98e2SPeter Wemm 	{
413640266059SGregory Neil Shapiro 		(void) sm_strlcpyn(bp, SPACELEFT(buf, bp), 2, ", mailer=",
413740266059SGregory Neil Shapiro 				   m->m_name);
4138c2aa98e2SPeter Wemm 		bp += strlen(bp);
4139c2aa98e2SPeter Wemm 	}
4140c2aa98e2SPeter Wemm 
414106f25ae9SGregory Neil Shapiro 	/* pri: changes with each delivery attempt */
414240266059SGregory Neil Shapiro 	(void) sm_snprintf(bp, SPACELEFT(buf, bp), ", pri=%ld",
414340266059SGregory Neil Shapiro 		e->e_msgpriority);
414406f25ae9SGregory Neil Shapiro 	bp += strlen(bp);
414506f25ae9SGregory Neil Shapiro 
4146c2aa98e2SPeter Wemm 	/* relay: max 66 bytes for IPv4 addresses */
4147c2aa98e2SPeter Wemm 	if (mci != NULL && mci->mci_host != NULL)
4148c2aa98e2SPeter Wemm 	{
4149c2aa98e2SPeter Wemm 		extern SOCKADDR CurHostAddr;
4150c2aa98e2SPeter Wemm 
415140266059SGregory Neil Shapiro 		(void) sm_strlcpyn(bp, SPACELEFT(buf, bp), 2, ", relay=",
4152c2aa98e2SPeter Wemm 				   shortenstring(mci->mci_host, 40));
4153c2aa98e2SPeter Wemm 		bp += strlen(bp);
4154c2aa98e2SPeter Wemm 
4155c2aa98e2SPeter Wemm 		if (CurHostAddr.sa.sa_family != 0)
4156c2aa98e2SPeter Wemm 		{
415740266059SGregory Neil Shapiro 			(void) sm_snprintf(bp, SPACELEFT(buf, bp), " [%s]",
4158c2aa98e2SPeter Wemm 					   anynet_ntoa(&CurHostAddr));
4159c2aa98e2SPeter Wemm 		}
4160c2aa98e2SPeter Wemm 	}
416140266059SGregory Neil Shapiro #if _FFR_QUARANTINE
416240266059SGregory Neil Shapiro 	else if (strcmp(status, "quarantined") == 0)
416340266059SGregory Neil Shapiro 	{
416440266059SGregory Neil Shapiro 		if (e->e_quarmsg != NULL)
416540266059SGregory Neil Shapiro 			(void) sm_snprintf(bp, SPACELEFT(buf, bp),
416640266059SGregory Neil Shapiro 					   ", quarantine=%s",
416740266059SGregory Neil Shapiro 					   shortenstring(e->e_quarmsg, 40));
416840266059SGregory Neil Shapiro 	}
416940266059SGregory Neil Shapiro #endif /* _FFR_QUARANTINE */
417006f25ae9SGregory Neil Shapiro 	else if (strcmp(status, "queued") != 0)
4171c2aa98e2SPeter Wemm 	{
4172c2aa98e2SPeter Wemm 		p = macvalue('h', e);
4173c2aa98e2SPeter Wemm 		if (p != NULL && p[0] != '\0')
4174c2aa98e2SPeter Wemm 		{
417540266059SGregory Neil Shapiro 			(void) sm_snprintf(bp, SPACELEFT(buf, bp),
417640266059SGregory Neil Shapiro 					   ", relay=%s", shortenstring(p, 40));
4177c2aa98e2SPeter Wemm 		}
4178c2aa98e2SPeter Wemm 	}
4179c2aa98e2SPeter Wemm 	bp += strlen(bp);
4180c2aa98e2SPeter Wemm 
418106f25ae9SGregory Neil Shapiro 	/* dsn */
418206f25ae9SGregory Neil Shapiro 	if (dsn != NULL && *dsn != '\0')
418306f25ae9SGregory Neil Shapiro 	{
418440266059SGregory Neil Shapiro 		(void) sm_strlcpyn(bp, SPACELEFT(buf, bp), 2, ", dsn=",
418506f25ae9SGregory Neil Shapiro 				   shortenstring(dsn, ENHSCLEN));
418606f25ae9SGregory Neil Shapiro 		bp += strlen(bp);
418706f25ae9SGregory Neil Shapiro 	}
418806f25ae9SGregory Neil Shapiro 
4189c2aa98e2SPeter Wemm # define STATLEN		(((SYSLOG_BUFSIZE) - 100) / 4)
4190c2aa98e2SPeter Wemm # if (STATLEN) < 63
4191c2aa98e2SPeter Wemm #  undef STATLEN
4192c2aa98e2SPeter Wemm #  define STATLEN	63
419306f25ae9SGregory Neil Shapiro # endif /* (STATLEN) < 63 */
4194c2aa98e2SPeter Wemm # if (STATLEN) > 203
4195c2aa98e2SPeter Wemm #  undef STATLEN
4196c2aa98e2SPeter Wemm #  define STATLEN	203
419706f25ae9SGregory Neil Shapiro # endif /* (STATLEN) > 203 */
4198c2aa98e2SPeter Wemm 
4199c2aa98e2SPeter Wemm 	/* stat: max 210 bytes */
4200c2aa98e2SPeter Wemm 	if ((bp - buf) > (sizeof buf - ((STATLEN) + 20)))
4201c2aa98e2SPeter Wemm 	{
4202c2aa98e2SPeter Wemm 		/* desperation move -- truncate data */
4203c2aa98e2SPeter Wemm 		bp = buf + sizeof buf - ((STATLEN) + 17);
420440266059SGregory Neil Shapiro 		(void) sm_strlcpy(bp, "...", SPACELEFT(buf, bp));
4205c2aa98e2SPeter Wemm 		bp += 3;
4206c2aa98e2SPeter Wemm 	}
4207c2aa98e2SPeter Wemm 
420840266059SGregory Neil Shapiro 	(void) sm_strlcpy(bp, ", stat=", SPACELEFT(buf, bp));
4209c2aa98e2SPeter Wemm 	bp += strlen(bp);
4210c2aa98e2SPeter Wemm 
421140266059SGregory Neil Shapiro 	(void) sm_strlcpy(bp, shortenstring(status, STATLEN),
421240266059SGregory Neil Shapiro 			  SPACELEFT(buf, bp));
4213c2aa98e2SPeter Wemm 
4214c2aa98e2SPeter Wemm 	/* id, to: max 13 + TOBUFSIZE bytes */
4215c2aa98e2SPeter Wemm 	l = SYSLOG_BUFSIZE - 100 - strlen(buf);
421640266059SGregory Neil Shapiro 	if (l < 0)
421740266059SGregory Neil Shapiro 		l = 0;
421806f25ae9SGregory Neil Shapiro 	p = e->e_to == NULL ? "NO-TO-LIST" : e->e_to;
421940266059SGregory Neil Shapiro 	while (strlen(p) >= l)
4220c2aa98e2SPeter Wemm 	{
422106f25ae9SGregory Neil Shapiro 		register char *q;
4222c2aa98e2SPeter Wemm 
422306f25ae9SGregory Neil Shapiro 		for (q = p + l; q > p; q--)
422406f25ae9SGregory Neil Shapiro 		{
422506f25ae9SGregory Neil Shapiro 			if (*q == ',')
422606f25ae9SGregory Neil Shapiro 				break;
422706f25ae9SGregory Neil Shapiro 		}
422806f25ae9SGregory Neil Shapiro 		if (p == q)
422906f25ae9SGregory Neil Shapiro 			break;
423040266059SGregory Neil Shapiro 		sm_syslog(LOG_INFO, e->e_id, "to=%.*s [more]%s",
423142e5d165SGregory Neil Shapiro 			  (int) (++q - p), p, buf);
4232c2aa98e2SPeter Wemm 		p = q;
4233c2aa98e2SPeter Wemm 	}
423406f25ae9SGregory Neil Shapiro 	sm_syslog(LOG_INFO, e->e_id, "to=%.*s%s", l, p, buf);
4235c2aa98e2SPeter Wemm 
423606f25ae9SGregory Neil Shapiro #else /* (SYSLOG_BUFSIZE) >= 256 */
4237c2aa98e2SPeter Wemm 
4238c2aa98e2SPeter Wemm 	l = SYSLOG_BUFSIZE - 85;
423940266059SGregory Neil Shapiro 	if (l < 0)
424040266059SGregory Neil Shapiro 		l = 0;
424106f25ae9SGregory Neil Shapiro 	p = e->e_to == NULL ? "NO-TO-LIST" : e->e_to;
424240266059SGregory Neil Shapiro 	while (strlen(p) >= l)
4243c2aa98e2SPeter Wemm 	{
424406f25ae9SGregory Neil Shapiro 		register char *q;
4245c2aa98e2SPeter Wemm 
424606f25ae9SGregory Neil Shapiro 		for (q = p + l; q > p; q--)
424706f25ae9SGregory Neil Shapiro 		{
424806f25ae9SGregory Neil Shapiro 			if (*q == ',')
424906f25ae9SGregory Neil Shapiro 				break;
425006f25ae9SGregory Neil Shapiro 		}
425106f25ae9SGregory Neil Shapiro 		if (p == q)
425206f25ae9SGregory Neil Shapiro 			break;
425306f25ae9SGregory Neil Shapiro 
425440266059SGregory Neil Shapiro 		sm_syslog(LOG_INFO, e->e_id, "to=%.*s [more]",
425542e5d165SGregory Neil Shapiro 			  (int) (++q - p), p);
4256c2aa98e2SPeter Wemm 		p = q;
4257c2aa98e2SPeter Wemm 	}
425806f25ae9SGregory Neil Shapiro 	sm_syslog(LOG_INFO, e->e_id, "to=%.*s", l, p);
4259c2aa98e2SPeter Wemm 
4260c2aa98e2SPeter Wemm 	if (ctladdr != NULL)
4261c2aa98e2SPeter Wemm 	{
4262c2aa98e2SPeter Wemm 		bp = buf;
426340266059SGregory Neil Shapiro 		(void) sm_strlcpyn(bp, SPACELEFT(buf, bp), 2, "ctladdr=",
4264c2aa98e2SPeter Wemm 				   shortenstring(ctladdr->q_paddr, 83));
4265c2aa98e2SPeter Wemm 		bp += strlen(bp);
4266c2aa98e2SPeter Wemm 		if (bitset(QGOODUID, ctladdr->q_flags))
4267c2aa98e2SPeter Wemm 		{
426840266059SGregory Neil Shapiro 			(void) sm_snprintf(bp, SPACELEFT(buf, bp), " (%d/%d)",
4269c2aa98e2SPeter Wemm 					   ctladdr->q_uid, ctladdr->q_gid);
4270c2aa98e2SPeter Wemm 			bp += strlen(bp);
4271c2aa98e2SPeter Wemm 		}
4272c2aa98e2SPeter Wemm 		sm_syslog(LOG_INFO, e->e_id, "%s", buf);
4273c2aa98e2SPeter Wemm 	}
4274c2aa98e2SPeter Wemm 	bp = buf;
427540266059SGregory Neil Shapiro 	(void) sm_strlcpyn(bp, SPACELEFT(buf, bp), 2, "delay=",
427640266059SGregory Neil Shapiro 			   pintvl(now - e->e_ctime, true));
4277c2aa98e2SPeter Wemm 	bp += strlen(bp);
4278c2aa98e2SPeter Wemm 	if (xstart != (time_t) 0)
4279c2aa98e2SPeter Wemm 	{
428040266059SGregory Neil Shapiro 		(void) sm_strlcpyn(bp, SPACELEFT(buf, bp), 2, ", xdelay=",
428140266059SGregory Neil Shapiro 				   pintvl(now - xstart, true));
4282c2aa98e2SPeter Wemm 		bp += strlen(bp);
4283c2aa98e2SPeter Wemm 	}
4284c2aa98e2SPeter Wemm 
4285c2aa98e2SPeter Wemm 	if (m != NULL)
4286c2aa98e2SPeter Wemm 	{
428740266059SGregory Neil Shapiro 		(void) sm_strlcpyn(bp, SPACELEFT(buf, bp), 2, ", mailer=",
428840266059SGregory Neil Shapiro 				   m->m_name);
4289c2aa98e2SPeter Wemm 		bp += strlen(bp);
4290c2aa98e2SPeter Wemm 	}
4291c2aa98e2SPeter Wemm 	sm_syslog(LOG_INFO, e->e_id, "%.1000s", buf);
4292c2aa98e2SPeter Wemm 
4293c2aa98e2SPeter Wemm 	buf[0] = '\0';
4294c2aa98e2SPeter Wemm 	bp = buf;
4295c2aa98e2SPeter Wemm 	if (mci != NULL && mci->mci_host != NULL)
4296c2aa98e2SPeter Wemm 	{
4297c2aa98e2SPeter Wemm 		extern SOCKADDR CurHostAddr;
4298c2aa98e2SPeter Wemm 
429940266059SGregory Neil Shapiro 		(void) sm_snprintf(bp, SPACELEFT(buf, bp), "relay=%.100s",
430040266059SGregory Neil Shapiro 				   mci->mci_host);
4301c2aa98e2SPeter Wemm 		bp += strlen(bp);
4302c2aa98e2SPeter Wemm 
4303c2aa98e2SPeter Wemm 		if (CurHostAddr.sa.sa_family != 0)
430440266059SGregory Neil Shapiro 			(void) sm_snprintf(bp, SPACELEFT(buf, bp),
430540266059SGregory Neil Shapiro 					   " [%.100s]",
4306c2aa98e2SPeter Wemm 					   anynet_ntoa(&CurHostAddr));
4307c2aa98e2SPeter Wemm 	}
430840266059SGregory Neil Shapiro #if _FFR_QUARANTINE
430940266059SGregory Neil Shapiro 	else if (strcmp(status, "quarantined") == 0)
431040266059SGregory Neil Shapiro 	{
431140266059SGregory Neil Shapiro 		if (e->e_quarmsg != NULL)
431240266059SGregory Neil Shapiro 			(void) sm_snprintf(bp, SPACELEFT(buf, bp),
431340266059SGregory Neil Shapiro 					   ", quarantine=%.100s",
431440266059SGregory Neil Shapiro 					   e->e_quarmsg);
431540266059SGregory Neil Shapiro 	}
431640266059SGregory Neil Shapiro #endif /* _FFR_QUARANTINE */
431706f25ae9SGregory Neil Shapiro 	else if (strcmp(status, "queued") != 0)
4318c2aa98e2SPeter Wemm 	{
4319c2aa98e2SPeter Wemm 		p = macvalue('h', e);
4320c2aa98e2SPeter Wemm 		if (p != NULL && p[0] != '\0')
432140266059SGregory Neil Shapiro 			(void) sm_snprintf(buf, sizeof buf, "relay=%.100s", p);
4322c2aa98e2SPeter Wemm 	}
4323c2aa98e2SPeter Wemm 	if (buf[0] != '\0')
4324c2aa98e2SPeter Wemm 		sm_syslog(LOG_INFO, e->e_id, "%.1000s", buf);
4325c2aa98e2SPeter Wemm 
432606f25ae9SGregory Neil Shapiro 	sm_syslog(LOG_INFO, e->e_id, "stat=%s", shortenstring(status, 63));
432706f25ae9SGregory Neil Shapiro #endif /* (SYSLOG_BUFSIZE) >= 256 */
4328c2aa98e2SPeter Wemm }
432940266059SGregory Neil Shapiro /*
4330c2aa98e2SPeter Wemm **  PUTFROMLINE -- output a UNIX-style from line (or whatever)
4331c2aa98e2SPeter Wemm **
4332c2aa98e2SPeter Wemm **	This can be made an arbitrary message separator by changing $l
4333c2aa98e2SPeter Wemm **
4334c2aa98e2SPeter Wemm **	One of the ugliest hacks seen by human eyes is contained herein:
4335c2aa98e2SPeter Wemm **	UUCP wants those stupid "remote from <host>" lines.  Why oh why
4336c2aa98e2SPeter Wemm **	does a well-meaning programmer such as myself have to deal with
4337c2aa98e2SPeter Wemm **	this kind of antique garbage????
4338c2aa98e2SPeter Wemm **
4339c2aa98e2SPeter Wemm **	Parameters:
4340c2aa98e2SPeter Wemm **		mci -- the connection information.
4341c2aa98e2SPeter Wemm **		e -- the envelope.
4342c2aa98e2SPeter Wemm **
4343c2aa98e2SPeter Wemm **	Returns:
4344c2aa98e2SPeter Wemm **		none
4345c2aa98e2SPeter Wemm **
4346c2aa98e2SPeter Wemm **	Side Effects:
4347c2aa98e2SPeter Wemm **		outputs some text to fp.
4348c2aa98e2SPeter Wemm */
4349c2aa98e2SPeter Wemm 
4350c2aa98e2SPeter Wemm void
4351c2aa98e2SPeter Wemm putfromline(mci, e)
4352c2aa98e2SPeter Wemm 	register MCI *mci;
4353c2aa98e2SPeter Wemm 	ENVELOPE *e;
4354c2aa98e2SPeter Wemm {
4355c2aa98e2SPeter Wemm 	char *template = UnixFromLine;
4356c2aa98e2SPeter Wemm 	char buf[MAXLINE];
4357c2aa98e2SPeter Wemm 	char xbuf[MAXLINE];
4358c2aa98e2SPeter Wemm 
4359c2aa98e2SPeter Wemm 	if (bitnset(M_NHDR, mci->mci_mailer->m_flags))
4360c2aa98e2SPeter Wemm 		return;
4361c2aa98e2SPeter Wemm 
4362c2aa98e2SPeter Wemm 	mci->mci_flags |= MCIF_INHEADER;
4363c2aa98e2SPeter Wemm 
4364c2aa98e2SPeter Wemm 	if (bitnset(M_UGLYUUCP, mci->mci_mailer->m_flags))
4365c2aa98e2SPeter Wemm 	{
4366c2aa98e2SPeter Wemm 		char *bang;
4367c2aa98e2SPeter Wemm 
4368c2aa98e2SPeter Wemm 		expand("\201g", buf, sizeof buf, e);
4369c2aa98e2SPeter Wemm 		bang = strchr(buf, '!');
4370c2aa98e2SPeter Wemm 		if (bang == NULL)
4371c2aa98e2SPeter Wemm 		{
4372c2aa98e2SPeter Wemm 			char *at;
4373c2aa98e2SPeter Wemm 			char hname[MAXNAME];
4374c2aa98e2SPeter Wemm 
4375c2aa98e2SPeter Wemm 			/*
4376c2aa98e2SPeter Wemm 			**  If we can construct a UUCP path, do so
4377c2aa98e2SPeter Wemm 			*/
4378c2aa98e2SPeter Wemm 
4379c2aa98e2SPeter Wemm 			at = strrchr(buf, '@');
4380c2aa98e2SPeter Wemm 			if (at == NULL)
4381c2aa98e2SPeter Wemm 			{
4382c2aa98e2SPeter Wemm 				expand("\201k", hname, sizeof hname, e);
4383c2aa98e2SPeter Wemm 				at = hname;
4384c2aa98e2SPeter Wemm 			}
4385c2aa98e2SPeter Wemm 			else
4386c2aa98e2SPeter Wemm 				*at++ = '\0';
438740266059SGregory Neil Shapiro 			(void) sm_snprintf(xbuf, sizeof xbuf,
4388c2aa98e2SPeter Wemm 					   "From %.800s  \201d remote from %.100s\n",
4389c2aa98e2SPeter Wemm 					   buf, at);
4390c2aa98e2SPeter Wemm 		}
4391c2aa98e2SPeter Wemm 		else
4392c2aa98e2SPeter Wemm 		{
4393c2aa98e2SPeter Wemm 			*bang++ = '\0';
439440266059SGregory Neil Shapiro 			(void) sm_snprintf(xbuf, sizeof xbuf,
4395c2aa98e2SPeter Wemm 					   "From %.800s  \201d remote from %.100s\n",
4396c2aa98e2SPeter Wemm 					   bang, buf);
4397c2aa98e2SPeter Wemm 			template = xbuf;
4398c2aa98e2SPeter Wemm 		}
4399c2aa98e2SPeter Wemm 	}
4400c2aa98e2SPeter Wemm 	expand(template, buf, sizeof buf, e);
4401c2aa98e2SPeter Wemm 	putxline(buf, strlen(buf), mci, PXLF_HEADER);
4402c2aa98e2SPeter Wemm }
440340266059SGregory Neil Shapiro /*
4404c2aa98e2SPeter Wemm **  PUTBODY -- put the body of a message.
4405c2aa98e2SPeter Wemm **
4406c2aa98e2SPeter Wemm **	Parameters:
4407c2aa98e2SPeter Wemm **		mci -- the connection information.
4408c2aa98e2SPeter Wemm **		e -- the envelope to put out.
4409c2aa98e2SPeter Wemm **		separator -- if non-NULL, a message separator that must
4410c2aa98e2SPeter Wemm **			not be permitted in the resulting message.
4411c2aa98e2SPeter Wemm **
4412c2aa98e2SPeter Wemm **	Returns:
4413c2aa98e2SPeter Wemm **		none.
4414c2aa98e2SPeter Wemm **
4415c2aa98e2SPeter Wemm **	Side Effects:
4416c2aa98e2SPeter Wemm **		The message is written onto fp.
4417c2aa98e2SPeter Wemm */
4418c2aa98e2SPeter Wemm 
4419c2aa98e2SPeter Wemm /* values for output state variable */
4420c2aa98e2SPeter Wemm #define OS_HEAD		0	/* at beginning of line */
4421c2aa98e2SPeter Wemm #define OS_CR		1	/* read a carriage return */
4422c2aa98e2SPeter Wemm #define OS_INLINE	2	/* putting rest of line */
4423c2aa98e2SPeter Wemm 
4424c2aa98e2SPeter Wemm void
4425c2aa98e2SPeter Wemm putbody(mci, e, separator)
4426c2aa98e2SPeter Wemm 	register MCI *mci;
4427c2aa98e2SPeter Wemm 	register ENVELOPE *e;
4428c2aa98e2SPeter Wemm 	char *separator;
4429c2aa98e2SPeter Wemm {
443040266059SGregory Neil Shapiro 	bool dead = false;
4431c2aa98e2SPeter Wemm 	char buf[MAXLINE];
443240266059SGregory Neil Shapiro #if MIME8TO7
4433065a643dSPeter Wemm 	char *boundaries[MAXMIMENESTING + 1];
443440266059SGregory Neil Shapiro #endif /* MIME8TO7 */
4435c2aa98e2SPeter Wemm 
4436c2aa98e2SPeter Wemm 	/*
4437c2aa98e2SPeter Wemm 	**  Output the body of the message
4438c2aa98e2SPeter Wemm 	*/
4439c2aa98e2SPeter Wemm 
4440c2aa98e2SPeter Wemm 	if (e->e_dfp == NULL && bitset(EF_HAS_DF, e->e_flags))
4441c2aa98e2SPeter Wemm 	{
444240266059SGregory Neil Shapiro 		char *df = queuename(e, DATAFL_LETTER);
4443c2aa98e2SPeter Wemm 
444440266059SGregory Neil Shapiro 		e->e_dfp = sm_io_open(SmFtStdio, SM_TIME_DEFAULT, df,
444540266059SGregory Neil Shapiro 				      SM_IO_RDONLY, NULL);
4446c2aa98e2SPeter Wemm 		if (e->e_dfp == NULL)
444706f25ae9SGregory Neil Shapiro 		{
444806f25ae9SGregory Neil Shapiro 			char *msg = "!putbody: Cannot open %s for %s from %s";
444906f25ae9SGregory Neil Shapiro 
445006f25ae9SGregory Neil Shapiro 			if (errno == ENOENT)
445106f25ae9SGregory Neil Shapiro 				msg++;
445206f25ae9SGregory Neil Shapiro 			syserr(msg, df, e->e_to, e->e_from.q_paddr);
445306f25ae9SGregory Neil Shapiro 		}
445440266059SGregory Neil Shapiro 
4455c2aa98e2SPeter Wemm 	}
4456c2aa98e2SPeter Wemm 	if (e->e_dfp == NULL)
4457c2aa98e2SPeter Wemm 	{
4458c2aa98e2SPeter Wemm 		if (bitset(MCIF_INHEADER, mci->mci_flags))
4459c2aa98e2SPeter Wemm 		{
4460c2aa98e2SPeter Wemm 			putline("", mci);
4461c2aa98e2SPeter Wemm 			mci->mci_flags &= ~MCIF_INHEADER;
4462c2aa98e2SPeter Wemm 		}
4463c2aa98e2SPeter Wemm 		putline("<<< No Message Collected >>>", mci);
4464c2aa98e2SPeter Wemm 		goto endofmessage;
4465c2aa98e2SPeter Wemm 	}
446606f25ae9SGregory Neil Shapiro 
4467c2aa98e2SPeter Wemm 	if (e->e_dfino == (ino_t) 0)
4468c2aa98e2SPeter Wemm 	{
4469c2aa98e2SPeter Wemm 		struct stat stbuf;
4470c2aa98e2SPeter Wemm 
447140266059SGregory Neil Shapiro 		if (fstat(sm_io_getinfo(e->e_dfp, SM_IO_WHAT_FD, NULL), &stbuf)
447240266059SGregory Neil Shapiro 		    < 0)
4473c2aa98e2SPeter Wemm 			e->e_dfino = -1;
4474c2aa98e2SPeter Wemm 		else
4475c2aa98e2SPeter Wemm 		{
4476c2aa98e2SPeter Wemm 			e->e_dfdev = stbuf.st_dev;
4477c2aa98e2SPeter Wemm 			e->e_dfino = stbuf.st_ino;
4478c2aa98e2SPeter Wemm 		}
4479c2aa98e2SPeter Wemm 	}
448006f25ae9SGregory Neil Shapiro 
448140266059SGregory Neil Shapiro 	/* paranoia: the data file should always be in a rewound state */
448206f25ae9SGregory Neil Shapiro 	(void) bfrewind(e->e_dfp);
4483c2aa98e2SPeter Wemm 
4484c2aa98e2SPeter Wemm #if MIME8TO7
4485c2aa98e2SPeter Wemm 	if (bitset(MCIF_CVT8TO7, mci->mci_flags))
4486c2aa98e2SPeter Wemm 	{
4487c2aa98e2SPeter Wemm 		/*
4488c2aa98e2SPeter Wemm 		**  Do 8 to 7 bit MIME conversion.
4489c2aa98e2SPeter Wemm 		*/
4490c2aa98e2SPeter Wemm 
4491c2aa98e2SPeter Wemm 		/* make sure it looks like a MIME message */
4492c2aa98e2SPeter Wemm 		if (hvalue("MIME-Version", e->e_header) == NULL)
4493c2aa98e2SPeter Wemm 			putline("MIME-Version: 1.0", mci);
4494c2aa98e2SPeter Wemm 
4495c2aa98e2SPeter Wemm 		if (hvalue("Content-Type", e->e_header) == NULL)
4496c2aa98e2SPeter Wemm 		{
449740266059SGregory Neil Shapiro 			(void) sm_snprintf(buf, sizeof buf,
4498c2aa98e2SPeter Wemm 					   "Content-Type: text/plain; charset=%s",
4499c2aa98e2SPeter Wemm 					   defcharset(e));
4500c2aa98e2SPeter Wemm 			putline(buf, mci);
4501c2aa98e2SPeter Wemm 		}
4502c2aa98e2SPeter Wemm 
4503c2aa98e2SPeter Wemm 		/* now do the hard work */
4504c2aa98e2SPeter Wemm 		boundaries[0] = NULL;
4505c2aa98e2SPeter Wemm 		mci->mci_flags |= MCIF_INHEADER;
450606f25ae9SGregory Neil Shapiro 		(void) mime8to7(mci, e->e_header, e, boundaries, M87F_OUTER);
4507c2aa98e2SPeter Wemm 	}
4508c2aa98e2SPeter Wemm # if MIME7TO8
4509c2aa98e2SPeter Wemm 	else if (bitset(MCIF_CVT7TO8, mci->mci_flags))
4510c2aa98e2SPeter Wemm 	{
451106f25ae9SGregory Neil Shapiro 		(void) mime7to8(mci, e->e_header, e);
4512c2aa98e2SPeter Wemm 	}
451306f25ae9SGregory Neil Shapiro # endif /* MIME7TO8 */
4514065a643dSPeter Wemm 	else if (MaxMimeHeaderLength > 0 || MaxMimeFieldLength > 0)
4515065a643dSPeter Wemm 	{
451606f25ae9SGregory Neil Shapiro 		bool oldsuprerrs = SuprErrs;
451706f25ae9SGregory Neil Shapiro 
4518065a643dSPeter Wemm 		/* Use mime8to7 to check multipart for MIME header overflows */
4519065a643dSPeter Wemm 		boundaries[0] = NULL;
4520065a643dSPeter Wemm 		mci->mci_flags |= MCIF_INHEADER;
452106f25ae9SGregory Neil Shapiro 
452206f25ae9SGregory Neil Shapiro 		/*
452306f25ae9SGregory Neil Shapiro 		**  If EF_DONT_MIME is set, we have a broken MIME message
452406f25ae9SGregory Neil Shapiro 		**  and don't want to generate a new bounce message whose
452506f25ae9SGregory Neil Shapiro 		**  body propagates the broken MIME.  We can't just not call
452606f25ae9SGregory Neil Shapiro 		**  mime8to7() as is done above since we need the security
452706f25ae9SGregory Neil Shapiro 		**  checks.  The best we can do is suppress the errors.
452806f25ae9SGregory Neil Shapiro 		*/
452906f25ae9SGregory Neil Shapiro 
453006f25ae9SGregory Neil Shapiro 		if (bitset(EF_DONT_MIME, e->e_flags))
453140266059SGregory Neil Shapiro 			SuprErrs = true;
453206f25ae9SGregory Neil Shapiro 
453306f25ae9SGregory Neil Shapiro 		(void) mime8to7(mci, e->e_header, e, boundaries,
453406f25ae9SGregory Neil Shapiro 				M87F_OUTER|M87F_NO8TO7);
453506f25ae9SGregory Neil Shapiro 
453606f25ae9SGregory Neil Shapiro 		/* restore SuprErrs */
453706f25ae9SGregory Neil Shapiro 		SuprErrs = oldsuprerrs;
4538065a643dSPeter Wemm 	}
4539c2aa98e2SPeter Wemm 	else
454006f25ae9SGregory Neil Shapiro #endif /* MIME8TO7 */
4541c2aa98e2SPeter Wemm 	{
4542c2aa98e2SPeter Wemm 		int ostate;
4543c2aa98e2SPeter Wemm 		register char *bp;
4544c2aa98e2SPeter Wemm 		register char *pbp;
4545c2aa98e2SPeter Wemm 		register int c;
4546c2aa98e2SPeter Wemm 		register char *xp;
4547c2aa98e2SPeter Wemm 		int padc;
4548c2aa98e2SPeter Wemm 		char *buflim;
4549c2aa98e2SPeter Wemm 		int pos = 0;
455006f25ae9SGregory Neil Shapiro 		char peekbuf[12];
4551c2aa98e2SPeter Wemm 
4552c2aa98e2SPeter Wemm 		if (bitset(MCIF_INHEADER, mci->mci_flags))
4553c2aa98e2SPeter Wemm 		{
4554c2aa98e2SPeter Wemm 			putline("", mci);
4555c2aa98e2SPeter Wemm 			mci->mci_flags &= ~MCIF_INHEADER;
4556c2aa98e2SPeter Wemm 		}
4557c2aa98e2SPeter Wemm 
4558c2aa98e2SPeter Wemm 		/* determine end of buffer; allow for short mailer lines */
4559c2aa98e2SPeter Wemm 		buflim = &buf[sizeof buf - 1];
4560c2aa98e2SPeter Wemm 		if (mci->mci_mailer->m_linelimit > 0 &&
4561c2aa98e2SPeter Wemm 		    mci->mci_mailer->m_linelimit < sizeof buf - 1)
4562c2aa98e2SPeter Wemm 			buflim = &buf[mci->mci_mailer->m_linelimit - 1];
4563c2aa98e2SPeter Wemm 
4564c2aa98e2SPeter Wemm 		/* copy temp file to output with mapping */
4565c2aa98e2SPeter Wemm 		ostate = OS_HEAD;
4566c2aa98e2SPeter Wemm 		bp = buf;
4567c2aa98e2SPeter Wemm 		pbp = peekbuf;
456840266059SGregory Neil Shapiro 		while (!sm_io_error(mci->mci_out) && !dead)
4569c2aa98e2SPeter Wemm 		{
4570c2aa98e2SPeter Wemm 			if (pbp > peekbuf)
4571c2aa98e2SPeter Wemm 				c = *--pbp;
457240266059SGregory Neil Shapiro 			else if ((c = sm_io_getc(e->e_dfp, SM_TIME_DEFAULT))
457340266059SGregory Neil Shapiro 				 == SM_IO_EOF)
4574c2aa98e2SPeter Wemm 				break;
4575c2aa98e2SPeter Wemm 			if (bitset(MCIF_7BIT, mci->mci_flags))
4576c2aa98e2SPeter Wemm 				c &= 0x7f;
4577c2aa98e2SPeter Wemm 			switch (ostate)
4578c2aa98e2SPeter Wemm 			{
4579c2aa98e2SPeter Wemm 			  case OS_HEAD:
4580c2aa98e2SPeter Wemm 				if (c == '\0' &&
458140266059SGregory Neil Shapiro 				    bitnset(M_NONULLS,
458240266059SGregory Neil Shapiro 					    mci->mci_mailer->m_flags))
4583c2aa98e2SPeter Wemm 					break;
4584c2aa98e2SPeter Wemm 				if (c != '\r' && c != '\n' && bp < buflim)
4585c2aa98e2SPeter Wemm 				{
4586c2aa98e2SPeter Wemm 					*bp++ = c;
4587c2aa98e2SPeter Wemm 					break;
4588c2aa98e2SPeter Wemm 				}
4589c2aa98e2SPeter Wemm 
4590c2aa98e2SPeter Wemm 				/* check beginning of line for special cases */
4591c2aa98e2SPeter Wemm 				*bp = '\0';
4592c2aa98e2SPeter Wemm 				pos = 0;
459340266059SGregory Neil Shapiro 				padc = SM_IO_EOF;
4594c2aa98e2SPeter Wemm 				if (buf[0] == 'F' &&
459540266059SGregory Neil Shapiro 				    bitnset(M_ESCFROM, mci->mci_mailer->m_flags)
459640266059SGregory Neil Shapiro 				    && strncmp(buf, "From ", 5) == 0)
4597c2aa98e2SPeter Wemm 				{
4598c2aa98e2SPeter Wemm 					padc = '>';
4599c2aa98e2SPeter Wemm 				}
4600c2aa98e2SPeter Wemm 				if (buf[0] == '-' && buf[1] == '-' &&
4601c2aa98e2SPeter Wemm 				    separator != NULL)
4602c2aa98e2SPeter Wemm 				{
4603c2aa98e2SPeter Wemm 					/* possible separator */
4604c2aa98e2SPeter Wemm 					int sl = strlen(separator);
4605c2aa98e2SPeter Wemm 
460640266059SGregory Neil Shapiro 					if (strncmp(&buf[2], separator, sl)
460740266059SGregory Neil Shapiro 					    == 0)
4608c2aa98e2SPeter Wemm 						padc = ' ';
4609c2aa98e2SPeter Wemm 				}
4610c2aa98e2SPeter Wemm 				if (buf[0] == '.' &&
4611c2aa98e2SPeter Wemm 				    bitnset(M_XDOT, mci->mci_mailer->m_flags))
4612c2aa98e2SPeter Wemm 				{
4613c2aa98e2SPeter Wemm 					padc = '.';
4614c2aa98e2SPeter Wemm 				}
4615c2aa98e2SPeter Wemm 
4616c2aa98e2SPeter Wemm 				/* now copy out saved line */
4617c2aa98e2SPeter Wemm 				if (TrafficLogFile != NULL)
4618c2aa98e2SPeter Wemm 				{
461940266059SGregory Neil Shapiro 					(void) sm_io_fprintf(TrafficLogFile,
462040266059SGregory Neil Shapiro 							     SM_TIME_DEFAULT,
462140266059SGregory Neil Shapiro 							     "%05d >>> ",
462240266059SGregory Neil Shapiro 							     (int) CurrentPid);
462340266059SGregory Neil Shapiro 					if (padc != SM_IO_EOF)
462440266059SGregory Neil Shapiro 						(void) sm_io_putc(TrafficLogFile,
462540266059SGregory Neil Shapiro 								  SM_TIME_DEFAULT,
462640266059SGregory Neil Shapiro 								  padc);
4627c2aa98e2SPeter Wemm 					for (xp = buf; xp < bp; xp++)
462840266059SGregory Neil Shapiro 						(void) sm_io_putc(TrafficLogFile,
462940266059SGregory Neil Shapiro 								  SM_TIME_DEFAULT,
463040266059SGregory Neil Shapiro 								  (unsigned char) *xp);
4631c2aa98e2SPeter Wemm 					if (c == '\n')
463240266059SGregory Neil Shapiro 						(void) sm_io_fputs(TrafficLogFile,
463340266059SGregory Neil Shapiro 								   SM_TIME_DEFAULT,
463440266059SGregory Neil Shapiro 								   mci->mci_mailer->m_eol);
4635c2aa98e2SPeter Wemm 				}
463640266059SGregory Neil Shapiro 				if (padc != SM_IO_EOF)
4637c2aa98e2SPeter Wemm 				{
463840266059SGregory Neil Shapiro 					if (sm_io_putc(mci->mci_out,
463940266059SGregory Neil Shapiro 						       SM_TIME_DEFAULT, padc)
464040266059SGregory Neil Shapiro 					    == SM_IO_EOF)
464106f25ae9SGregory Neil Shapiro 					{
464240266059SGregory Neil Shapiro 						dead = true;
464306f25ae9SGregory Neil Shapiro 						continue;
464406f25ae9SGregory Neil Shapiro 					}
4645193538b7SGregory Neil Shapiro 					else
4646193538b7SGregory Neil Shapiro 					{
4647193538b7SGregory Neil Shapiro 						/* record progress for DATA timeout */
464840266059SGregory Neil Shapiro 						DataProgress = true;
4649193538b7SGregory Neil Shapiro 					}
4650c2aa98e2SPeter Wemm 					pos++;
4651c2aa98e2SPeter Wemm 				}
4652c2aa98e2SPeter Wemm 				for (xp = buf; xp < bp; xp++)
4653c2aa98e2SPeter Wemm 				{
465440266059SGregory Neil Shapiro 					if (sm_io_putc(mci->mci_out,
465540266059SGregory Neil Shapiro 						       SM_TIME_DEFAULT,
465640266059SGregory Neil Shapiro 						       (unsigned char) *xp)
465740266059SGregory Neil Shapiro 					    == SM_IO_EOF)
465806f25ae9SGregory Neil Shapiro 					{
465940266059SGregory Neil Shapiro 						dead = true;
466006f25ae9SGregory Neil Shapiro 						break;
4661c2aa98e2SPeter Wemm 					}
4662193538b7SGregory Neil Shapiro 					else
4663193538b7SGregory Neil Shapiro 					{
466406f25ae9SGregory Neil Shapiro 						/* record progress for DATA timeout */
466540266059SGregory Neil Shapiro 						DataProgress = true;
466606f25ae9SGregory Neil Shapiro 					}
4667193538b7SGregory Neil Shapiro 				}
466806f25ae9SGregory Neil Shapiro 				if (dead)
466906f25ae9SGregory Neil Shapiro 					continue;
4670c2aa98e2SPeter Wemm 				if (c == '\n')
4671c2aa98e2SPeter Wemm 				{
467240266059SGregory Neil Shapiro 					if (sm_io_fputs(mci->mci_out,
467340266059SGregory Neil Shapiro 							SM_TIME_DEFAULT,
467440266059SGregory Neil Shapiro 							mci->mci_mailer->m_eol)
467540266059SGregory Neil Shapiro 							== SM_IO_EOF)
467606f25ae9SGregory Neil Shapiro 						break;
4677193538b7SGregory Neil Shapiro 					else
4678193538b7SGregory Neil Shapiro 					{
4679193538b7SGregory Neil Shapiro 						/* record progress for DATA timeout */
468040266059SGregory Neil Shapiro 						DataProgress = true;
4681193538b7SGregory Neil Shapiro 					}
4682c2aa98e2SPeter Wemm 					pos = 0;
4683c2aa98e2SPeter Wemm 				}
4684c2aa98e2SPeter Wemm 				else
4685c2aa98e2SPeter Wemm 				{
4686c2aa98e2SPeter Wemm 					pos += bp - buf;
4687c2aa98e2SPeter Wemm 					if (c != '\r')
4688c2aa98e2SPeter Wemm 						*pbp++ = c;
4689c2aa98e2SPeter Wemm 				}
469006f25ae9SGregory Neil Shapiro 
4691c2aa98e2SPeter Wemm 				bp = buf;
4692c2aa98e2SPeter Wemm 
4693c2aa98e2SPeter Wemm 				/* determine next state */
4694c2aa98e2SPeter Wemm 				if (c == '\n')
4695c2aa98e2SPeter Wemm 					ostate = OS_HEAD;
4696c2aa98e2SPeter Wemm 				else if (c == '\r')
4697c2aa98e2SPeter Wemm 					ostate = OS_CR;
4698c2aa98e2SPeter Wemm 				else
4699c2aa98e2SPeter Wemm 					ostate = OS_INLINE;
4700c2aa98e2SPeter Wemm 				continue;
4701c2aa98e2SPeter Wemm 
4702c2aa98e2SPeter Wemm 			  case OS_CR:
4703c2aa98e2SPeter Wemm 				if (c == '\n')
4704c2aa98e2SPeter Wemm 				{
4705c2aa98e2SPeter Wemm 					/* got CRLF */
470640266059SGregory Neil Shapiro 					if (sm_io_fputs(mci->mci_out,
470740266059SGregory Neil Shapiro 							SM_TIME_DEFAULT,
470840266059SGregory Neil Shapiro 							mci->mci_mailer->m_eol)
470940266059SGregory Neil Shapiro 							== SM_IO_EOF)
471006f25ae9SGregory Neil Shapiro 						continue;
4711193538b7SGregory Neil Shapiro 					else
4712193538b7SGregory Neil Shapiro 					{
471306f25ae9SGregory Neil Shapiro 						/* record progress for DATA timeout */
471440266059SGregory Neil Shapiro 						DataProgress = true;
4715193538b7SGregory Neil Shapiro 					}
471606f25ae9SGregory Neil Shapiro 
4717c2aa98e2SPeter Wemm 					if (TrafficLogFile != NULL)
4718c2aa98e2SPeter Wemm 					{
471940266059SGregory Neil Shapiro 						(void) sm_io_fputs(TrafficLogFile,
472040266059SGregory Neil Shapiro 								   SM_TIME_DEFAULT,
472140266059SGregory Neil Shapiro 								   mci->mci_mailer->m_eol);
4722c2aa98e2SPeter Wemm 					}
4723c2aa98e2SPeter Wemm 					ostate = OS_HEAD;
4724c2aa98e2SPeter Wemm 					continue;
4725c2aa98e2SPeter Wemm 				}
4726c2aa98e2SPeter Wemm 
4727c2aa98e2SPeter Wemm 				/* had a naked carriage return */
4728c2aa98e2SPeter Wemm 				*pbp++ = c;
4729c2aa98e2SPeter Wemm 				c = '\r';
4730c2aa98e2SPeter Wemm 				ostate = OS_INLINE;
4731c2aa98e2SPeter Wemm 				goto putch;
4732c2aa98e2SPeter Wemm 
4733c2aa98e2SPeter Wemm 			  case OS_INLINE:
4734c2aa98e2SPeter Wemm 				if (c == '\r')
4735c2aa98e2SPeter Wemm 				{
4736c2aa98e2SPeter Wemm 					ostate = OS_CR;
4737c2aa98e2SPeter Wemm 					continue;
4738c2aa98e2SPeter Wemm 				}
4739c2aa98e2SPeter Wemm 				if (c == '\0' &&
474040266059SGregory Neil Shapiro 				    bitnset(M_NONULLS,
474140266059SGregory Neil Shapiro 					    mci->mci_mailer->m_flags))
4742c2aa98e2SPeter Wemm 					break;
4743c2aa98e2SPeter Wemm putch:
4744c2aa98e2SPeter Wemm 				if (mci->mci_mailer->m_linelimit > 0 &&
474506f25ae9SGregory Neil Shapiro 				    pos >= mci->mci_mailer->m_linelimit - 1 &&
4746c2aa98e2SPeter Wemm 				    c != '\n')
4747c2aa98e2SPeter Wemm 				{
474806f25ae9SGregory Neil Shapiro 					int d;
474906f25ae9SGregory Neil Shapiro 
475006f25ae9SGregory Neil Shapiro 					/* check next character for EOL */
475106f25ae9SGregory Neil Shapiro 					if (pbp > peekbuf)
475206f25ae9SGregory Neil Shapiro 						d = *(pbp - 1);
475340266059SGregory Neil Shapiro 					else if ((d = sm_io_getc(e->e_dfp,
475440266059SGregory Neil Shapiro 								 SM_TIME_DEFAULT))
475540266059SGregory Neil Shapiro 						 != SM_IO_EOF)
475606f25ae9SGregory Neil Shapiro 						*pbp++ = d;
475706f25ae9SGregory Neil Shapiro 
475840266059SGregory Neil Shapiro 					if (d == '\n' || d == SM_IO_EOF)
475906f25ae9SGregory Neil Shapiro 					{
476006f25ae9SGregory Neil Shapiro 						if (TrafficLogFile != NULL)
476140266059SGregory Neil Shapiro 							(void) sm_io_putc(TrafficLogFile,
476240266059SGregory Neil Shapiro 									  SM_TIME_DEFAULT,
476340266059SGregory Neil Shapiro 									  (unsigned char) c);
476440266059SGregory Neil Shapiro 						if (sm_io_putc(mci->mci_out,
476540266059SGregory Neil Shapiro 							       SM_TIME_DEFAULT,
476640266059SGregory Neil Shapiro 							       (unsigned char) c)
476740266059SGregory Neil Shapiro 							       == SM_IO_EOF)
476806f25ae9SGregory Neil Shapiro 						{
476940266059SGregory Neil Shapiro 							dead = true;
477006f25ae9SGregory Neil Shapiro 							continue;
477106f25ae9SGregory Neil Shapiro 						}
4772193538b7SGregory Neil Shapiro 						else
4773193538b7SGregory Neil Shapiro 						{
4774193538b7SGregory Neil Shapiro 							/* record progress for DATA timeout */
477540266059SGregory Neil Shapiro 							DataProgress = true;
4776193538b7SGregory Neil Shapiro 						}
477706f25ae9SGregory Neil Shapiro 						pos++;
477806f25ae9SGregory Neil Shapiro 						continue;
477906f25ae9SGregory Neil Shapiro 					}
478006f25ae9SGregory Neil Shapiro 
478140266059SGregory Neil Shapiro 					if (sm_io_putc(mci->mci_out,
478240266059SGregory Neil Shapiro 						       SM_TIME_DEFAULT, '!')
478340266059SGregory Neil Shapiro 					    == SM_IO_EOF ||
478440266059SGregory Neil Shapiro 					    sm_io_fputs(mci->mci_out,
478540266059SGregory Neil Shapiro 							SM_TIME_DEFAULT,
478640266059SGregory Neil Shapiro 							mci->mci_mailer->m_eol)
478740266059SGregory Neil Shapiro 					    == SM_IO_EOF)
478806f25ae9SGregory Neil Shapiro 					{
478940266059SGregory Neil Shapiro 						dead = true;
479006f25ae9SGregory Neil Shapiro 						continue;
479106f25ae9SGregory Neil Shapiro 					}
4792193538b7SGregory Neil Shapiro 					else
4793193538b7SGregory Neil Shapiro 					{
479406f25ae9SGregory Neil Shapiro 						/* record progress for DATA timeout */
479540266059SGregory Neil Shapiro 						DataProgress = true;
4796193538b7SGregory Neil Shapiro 					}
479706f25ae9SGregory Neil Shapiro 
4798c2aa98e2SPeter Wemm 					if (TrafficLogFile != NULL)
4799c2aa98e2SPeter Wemm 					{
480040266059SGregory Neil Shapiro 						(void) sm_io_fprintf(TrafficLogFile,
480140266059SGregory Neil Shapiro 								     SM_TIME_DEFAULT,
480240266059SGregory Neil Shapiro 								     "!%s",
4803c2aa98e2SPeter Wemm 								     mci->mci_mailer->m_eol);
4804c2aa98e2SPeter Wemm 					}
4805c2aa98e2SPeter Wemm 					ostate = OS_HEAD;
4806c2aa98e2SPeter Wemm 					*pbp++ = c;
4807c2aa98e2SPeter Wemm 					continue;
4808c2aa98e2SPeter Wemm 				}
4809c2aa98e2SPeter Wemm 				if (c == '\n')
4810c2aa98e2SPeter Wemm 				{
4811c2aa98e2SPeter Wemm 					if (TrafficLogFile != NULL)
481240266059SGregory Neil Shapiro 						(void) sm_io_fputs(TrafficLogFile,
481340266059SGregory Neil Shapiro 								   SM_TIME_DEFAULT,
481440266059SGregory Neil Shapiro 								   mci->mci_mailer->m_eol);
481540266059SGregory Neil Shapiro 					if (sm_io_fputs(mci->mci_out,
481640266059SGregory Neil Shapiro 							SM_TIME_DEFAULT,
481740266059SGregory Neil Shapiro 							mci->mci_mailer->m_eol)
481840266059SGregory Neil Shapiro 							== SM_IO_EOF)
481906f25ae9SGregory Neil Shapiro 						continue;
4820193538b7SGregory Neil Shapiro 					else
4821193538b7SGregory Neil Shapiro 					{
4822193538b7SGregory Neil Shapiro 						/* record progress for DATA timeout */
482340266059SGregory Neil Shapiro 						DataProgress = true;
4824193538b7SGregory Neil Shapiro 					}
4825c2aa98e2SPeter Wemm 					pos = 0;
4826c2aa98e2SPeter Wemm 					ostate = OS_HEAD;
4827c2aa98e2SPeter Wemm 				}
4828c2aa98e2SPeter Wemm 				else
4829c2aa98e2SPeter Wemm 				{
4830c2aa98e2SPeter Wemm 					if (TrafficLogFile != NULL)
483140266059SGregory Neil Shapiro 						(void) sm_io_putc(TrafficLogFile,
483240266059SGregory Neil Shapiro 								  SM_TIME_DEFAULT,
483340266059SGregory Neil Shapiro 								  (unsigned char) c);
483440266059SGregory Neil Shapiro 					if (sm_io_putc(mci->mci_out,
483540266059SGregory Neil Shapiro 						       SM_TIME_DEFAULT,
483640266059SGregory Neil Shapiro 						       (unsigned char) c)
483740266059SGregory Neil Shapiro 					    == SM_IO_EOF)
483806f25ae9SGregory Neil Shapiro 					{
483940266059SGregory Neil Shapiro 						dead = true;
484006f25ae9SGregory Neil Shapiro 						continue;
484106f25ae9SGregory Neil Shapiro 					}
4842193538b7SGregory Neil Shapiro 					else
4843193538b7SGregory Neil Shapiro 					{
4844193538b7SGregory Neil Shapiro 						/* record progress for DATA timeout */
484540266059SGregory Neil Shapiro 						DataProgress = true;
4846193538b7SGregory Neil Shapiro 					}
4847c2aa98e2SPeter Wemm 					pos++;
4848c2aa98e2SPeter Wemm 					ostate = OS_INLINE;
4849c2aa98e2SPeter Wemm 				}
4850c2aa98e2SPeter Wemm 				break;
4851c2aa98e2SPeter Wemm 			}
4852c2aa98e2SPeter Wemm 		}
4853c2aa98e2SPeter Wemm 
4854c2aa98e2SPeter Wemm 		/* make sure we are at the beginning of a line */
4855c2aa98e2SPeter Wemm 		if (bp > buf)
4856c2aa98e2SPeter Wemm 		{
4857c2aa98e2SPeter Wemm 			if (TrafficLogFile != NULL)
4858c2aa98e2SPeter Wemm 			{
4859c2aa98e2SPeter Wemm 				for (xp = buf; xp < bp; xp++)
486040266059SGregory Neil Shapiro 					(void) sm_io_putc(TrafficLogFile,
486140266059SGregory Neil Shapiro 							  SM_TIME_DEFAULT,
486240266059SGregory Neil Shapiro 							  (unsigned char) *xp);
4863c2aa98e2SPeter Wemm 			}
4864c2aa98e2SPeter Wemm 			for (xp = buf; xp < bp; xp++)
4865c2aa98e2SPeter Wemm 			{
486640266059SGregory Neil Shapiro 				if (sm_io_putc(mci->mci_out, SM_TIME_DEFAULT,
486740266059SGregory Neil Shapiro 					       (unsigned char) *xp)
486840266059SGregory Neil Shapiro 				    == SM_IO_EOF)
486906f25ae9SGregory Neil Shapiro 				{
487040266059SGregory Neil Shapiro 					dead = true;
487106f25ae9SGregory Neil Shapiro 					break;
487206f25ae9SGregory Neil Shapiro 				}
4873193538b7SGregory Neil Shapiro 				else
4874193538b7SGregory Neil Shapiro 				{
487506f25ae9SGregory Neil Shapiro 					/* record progress for DATA timeout */
487640266059SGregory Neil Shapiro 					DataProgress = true;
4877c2aa98e2SPeter Wemm 				}
4878193538b7SGregory Neil Shapiro 			}
4879c2aa98e2SPeter Wemm 			pos += bp - buf;
4880c2aa98e2SPeter Wemm 		}
488106f25ae9SGregory Neil Shapiro 		if (!dead && pos > 0)
4882c2aa98e2SPeter Wemm 		{
4883c2aa98e2SPeter Wemm 			if (TrafficLogFile != NULL)
488440266059SGregory Neil Shapiro 				(void) sm_io_fputs(TrafficLogFile,
488540266059SGregory Neil Shapiro 						   SM_TIME_DEFAULT,
488640266059SGregory Neil Shapiro 						   mci->mci_mailer->m_eol);
488740266059SGregory Neil Shapiro 			(void) sm_io_fputs(mci->mci_out, SM_TIME_DEFAULT,
488840266059SGregory Neil Shapiro 					   mci->mci_mailer->m_eol);
488906f25ae9SGregory Neil Shapiro 
489006f25ae9SGregory Neil Shapiro 			/* record progress for DATA timeout */
489140266059SGregory Neil Shapiro 			DataProgress = true;
4892c2aa98e2SPeter Wemm 		}
4893c2aa98e2SPeter Wemm 	}
4894c2aa98e2SPeter Wemm 
489540266059SGregory Neil Shapiro 	if (sm_io_error(e->e_dfp))
4896c2aa98e2SPeter Wemm 	{
489740266059SGregory Neil Shapiro 		syserr("putbody: %s/%cf%s: read error",
489840266059SGregory Neil Shapiro 		       qid_printqueue(e->e_dfqgrp, e->e_dfqdir),
489940266059SGregory Neil Shapiro 		       DATAFL_LETTER, e->e_id);
4900c2aa98e2SPeter Wemm 		ExitStat = EX_IOERR;
4901c2aa98e2SPeter Wemm 	}
4902c2aa98e2SPeter Wemm 
4903c2aa98e2SPeter Wemm endofmessage:
490406f25ae9SGregory Neil Shapiro 	/*
490506f25ae9SGregory Neil Shapiro 	**  Since mailfile() uses e_dfp in a child process,
490606f25ae9SGregory Neil Shapiro 	**  the file offset in the stdio library for the
490706f25ae9SGregory Neil Shapiro 	**  parent process will not agree with the in-kernel
490806f25ae9SGregory Neil Shapiro 	**  file offset since the file descriptor is shared
490906f25ae9SGregory Neil Shapiro 	**  between the processes.  Therefore, it is vital
491006f25ae9SGregory Neil Shapiro 	**  that the file always be rewound.  This forces the
491106f25ae9SGregory Neil Shapiro 	**  kernel offset (lseek) and stdio library (ftell)
491206f25ae9SGregory Neil Shapiro 	**  offset to match.
491306f25ae9SGregory Neil Shapiro 	*/
491406f25ae9SGregory Neil Shapiro 
491506f25ae9SGregory Neil Shapiro 	if (e->e_dfp != NULL)
491606f25ae9SGregory Neil Shapiro 		(void) bfrewind(e->e_dfp);
491706f25ae9SGregory Neil Shapiro 
4918c2aa98e2SPeter Wemm 	/* some mailers want extra blank line at end of message */
491906f25ae9SGregory Neil Shapiro 	if (!dead && bitnset(M_BLANKEND, mci->mci_mailer->m_flags) &&
4920c2aa98e2SPeter Wemm 	    buf[0] != '\0' && buf[0] != '\n')
4921c2aa98e2SPeter Wemm 		putline("", mci);
4922c2aa98e2SPeter Wemm 
492340266059SGregory Neil Shapiro 	(void) sm_io_flush(mci->mci_out, SM_TIME_DEFAULT);
492440266059SGregory Neil Shapiro 	if (sm_io_error(mci->mci_out) && errno != EPIPE)
4925c2aa98e2SPeter Wemm 	{
4926c2aa98e2SPeter Wemm 		syserr("putbody: write error");
4927c2aa98e2SPeter Wemm 		ExitStat = EX_IOERR;
4928c2aa98e2SPeter Wemm 	}
492906f25ae9SGregory Neil Shapiro 
4930c2aa98e2SPeter Wemm 	errno = 0;
4931c2aa98e2SPeter Wemm }
493240266059SGregory Neil Shapiro /*
4933c2aa98e2SPeter Wemm **  MAILFILE -- Send a message to a file.
4934c2aa98e2SPeter Wemm **
493540266059SGregory Neil Shapiro **	If the file has the set-user-ID/set-group-ID bits set, but NO
493640266059SGregory Neil Shapiro **	execute bits, sendmail will try to become the owner of that file
4937c2aa98e2SPeter Wemm **	rather than the real user.  Obviously, this only works if
4938c2aa98e2SPeter Wemm **	sendmail runs as root.
4939c2aa98e2SPeter Wemm **
4940c2aa98e2SPeter Wemm **	This could be done as a subordinate mailer, except that it
4941c2aa98e2SPeter Wemm **	is used implicitly to save messages in ~/dead.letter.  We
4942c2aa98e2SPeter Wemm **	view this as being sufficiently important as to include it
4943c2aa98e2SPeter Wemm **	here.  For example, if the system is dying, we shouldn't have
4944c2aa98e2SPeter Wemm **	to create another process plus some pipes to save the message.
4945c2aa98e2SPeter Wemm **
4946c2aa98e2SPeter Wemm **	Parameters:
4947c2aa98e2SPeter Wemm **		filename -- the name of the file to send to.
4948c2aa98e2SPeter Wemm **		mailer -- mailer definition for recipient -- if NULL,
4949c2aa98e2SPeter Wemm **			use FileMailer.
4950c2aa98e2SPeter Wemm **		ctladdr -- the controlling address header -- includes
4951c2aa98e2SPeter Wemm **			the userid/groupid to be when sending.
4952c2aa98e2SPeter Wemm **		sfflags -- flags for opening.
4953c2aa98e2SPeter Wemm **		e -- the current envelope.
4954c2aa98e2SPeter Wemm **
4955c2aa98e2SPeter Wemm **	Returns:
4956c2aa98e2SPeter Wemm **		The exit code associated with the operation.
4957c2aa98e2SPeter Wemm **
4958c2aa98e2SPeter Wemm **	Side Effects:
4959c2aa98e2SPeter Wemm **		none.
4960c2aa98e2SPeter Wemm */
4961c2aa98e2SPeter Wemm 
496240266059SGregory Neil Shapiro # define RETURN(st)			exit(st);
496340266059SGregory Neil Shapiro 
4964c2aa98e2SPeter Wemm static jmp_buf	CtxMailfileTimeout;
4965c2aa98e2SPeter Wemm 
4966c2aa98e2SPeter Wemm int
4967c2aa98e2SPeter Wemm mailfile(filename, mailer, ctladdr, sfflags, e)
4968c2aa98e2SPeter Wemm 	char *volatile filename;
4969c2aa98e2SPeter Wemm 	MAILER *volatile mailer;
4970c2aa98e2SPeter Wemm 	ADDRESS *ctladdr;
497106f25ae9SGregory Neil Shapiro 	volatile long sfflags;
4972c2aa98e2SPeter Wemm 	register ENVELOPE *e;
4973c2aa98e2SPeter Wemm {
497440266059SGregory Neil Shapiro 	register SM_FILE_T *f;
4975c2aa98e2SPeter Wemm 	register pid_t pid = -1;
497606f25ae9SGregory Neil Shapiro 	volatile int mode;
497706f25ae9SGregory Neil Shapiro 	int len;
497806f25ae9SGregory Neil Shapiro 	off_t curoff;
4979c2aa98e2SPeter Wemm 	bool suidwarn = geteuid() == 0;
4980c2aa98e2SPeter Wemm 	char *p;
498106f25ae9SGregory Neil Shapiro 	char *volatile realfile;
498240266059SGregory Neil Shapiro 	SM_EVENT *ev;
498394c01205SGregory Neil Shapiro 	char buf[MAXPATHLEN];
498494c01205SGregory Neil Shapiro 	char targetfile[MAXPATHLEN];
4985c2aa98e2SPeter Wemm 
4986c2aa98e2SPeter Wemm 	if (tTd(11, 1))
4987c2aa98e2SPeter Wemm 	{
498840266059SGregory Neil Shapiro 		sm_dprintf("mailfile %s\n  ctladdr=", filename);
498940266059SGregory Neil Shapiro 		printaddr(ctladdr, false);
4990c2aa98e2SPeter Wemm 	}
4991c2aa98e2SPeter Wemm 
4992c2aa98e2SPeter Wemm 	if (mailer == NULL)
4993c2aa98e2SPeter Wemm 		mailer = FileMailer;
4994c2aa98e2SPeter Wemm 
4995c2aa98e2SPeter Wemm 	if (e->e_xfp != NULL)
499640266059SGregory Neil Shapiro 		(void) sm_io_flush(e->e_xfp, SM_TIME_DEFAULT);
4997c2aa98e2SPeter Wemm 
4998c2aa98e2SPeter Wemm 	/*
4999c2aa98e2SPeter Wemm 	**  Special case /dev/null.  This allows us to restrict file
5000c2aa98e2SPeter Wemm 	**  delivery to regular files only.
5001c2aa98e2SPeter Wemm 	*/
5002c2aa98e2SPeter Wemm 
500340266059SGregory Neil Shapiro 	if (sm_path_isdevnull(filename))
5004c2aa98e2SPeter Wemm 		return EX_OK;
5005c2aa98e2SPeter Wemm 
5006c2aa98e2SPeter Wemm 	/* check for 8-bit available */
5007c2aa98e2SPeter Wemm 	if (bitset(EF_HAS8BIT, e->e_flags) &&
5008c2aa98e2SPeter Wemm 	    bitnset(M_7BITS, mailer->m_flags) &&
5009c2aa98e2SPeter Wemm 	    (bitset(EF_DONT_MIME, e->e_flags) ||
5010c2aa98e2SPeter Wemm 	     !(bitset(MM_MIME8BIT, MimeMode) ||
5011c2aa98e2SPeter Wemm 	       (bitset(EF_IS_MIME, e->e_flags) &&
5012c2aa98e2SPeter Wemm 		bitset(MM_CVTMIME, MimeMode)))))
5013c2aa98e2SPeter Wemm 	{
5014c2aa98e2SPeter Wemm 		e->e_status = "5.6.3";
501506f25ae9SGregory Neil Shapiro 		usrerrenh(e->e_status,
501606f25ae9SGregory Neil Shapiro 			  "554 Cannot send 8-bit data to 7-bit destination");
501740266059SGregory Neil Shapiro 		errno = 0;
501806f25ae9SGregory Neil Shapiro 		return EX_DATAERR;
501906f25ae9SGregory Neil Shapiro 	}
502006f25ae9SGregory Neil Shapiro 
502106f25ae9SGregory Neil Shapiro 	/* Find the actual file */
502206f25ae9SGregory Neil Shapiro 	if (SafeFileEnv != NULL && SafeFileEnv[0] != '\0')
502306f25ae9SGregory Neil Shapiro 	{
502406f25ae9SGregory Neil Shapiro 		len = strlen(SafeFileEnv);
502506f25ae9SGregory Neil Shapiro 
502606f25ae9SGregory Neil Shapiro 		if (strncmp(SafeFileEnv, filename, len) == 0)
502706f25ae9SGregory Neil Shapiro 			filename += len;
502806f25ae9SGregory Neil Shapiro 
502940266059SGregory Neil Shapiro 		if (len + strlen(filename) + 1 >= sizeof targetfile)
503006f25ae9SGregory Neil Shapiro 		{
503106f25ae9SGregory Neil Shapiro 			syserr("mailfile: filename too long (%s/%s)",
503206f25ae9SGregory Neil Shapiro 			       SafeFileEnv, filename);
503306f25ae9SGregory Neil Shapiro 			return EX_CANTCREAT;
503406f25ae9SGregory Neil Shapiro 		}
503540266059SGregory Neil Shapiro 		(void) sm_strlcpy(targetfile, SafeFileEnv, sizeof targetfile);
503606f25ae9SGregory Neil Shapiro 		realfile = targetfile + len;
503706f25ae9SGregory Neil Shapiro 		if (*filename == '/')
503806f25ae9SGregory Neil Shapiro 			filename++;
5039605302a5SGregory Neil Shapiro 		if (*filename != '\0')
5040605302a5SGregory Neil Shapiro 		{
5041605302a5SGregory Neil Shapiro 			/* paranoia: trailing / should be removed in readcf */
5042605302a5SGregory Neil Shapiro 			if (targetfile[len - 1] != '/')
5043605302a5SGregory Neil Shapiro 				(void) sm_strlcat(targetfile,
5044605302a5SGregory Neil Shapiro 						  "/", sizeof targetfile);
5045605302a5SGregory Neil Shapiro 			(void) sm_strlcat(targetfile, filename,
5046605302a5SGregory Neil Shapiro 					  sizeof targetfile);
5047605302a5SGregory Neil Shapiro 		}
504806f25ae9SGregory Neil Shapiro 	}
504906f25ae9SGregory Neil Shapiro 	else if (mailer->m_rootdir != NULL)
505006f25ae9SGregory Neil Shapiro 	{
505106f25ae9SGregory Neil Shapiro 		expand(mailer->m_rootdir, targetfile, sizeof targetfile, e);
505206f25ae9SGregory Neil Shapiro 		len = strlen(targetfile);
505306f25ae9SGregory Neil Shapiro 
505406f25ae9SGregory Neil Shapiro 		if (strncmp(targetfile, filename, len) == 0)
505506f25ae9SGregory Neil Shapiro 			filename += len;
505606f25ae9SGregory Neil Shapiro 
505740266059SGregory Neil Shapiro 		if (len + strlen(filename) + 1 >= sizeof targetfile)
505806f25ae9SGregory Neil Shapiro 		{
505906f25ae9SGregory Neil Shapiro 			syserr("mailfile: filename too long (%s/%s)",
506006f25ae9SGregory Neil Shapiro 			       targetfile, filename);
506106f25ae9SGregory Neil Shapiro 			return EX_CANTCREAT;
506206f25ae9SGregory Neil Shapiro 		}
506306f25ae9SGregory Neil Shapiro 		realfile = targetfile + len;
506406f25ae9SGregory Neil Shapiro 		if (targetfile[len - 1] != '/')
506540266059SGregory Neil Shapiro 			(void) sm_strlcat(targetfile, "/", sizeof targetfile);
506606f25ae9SGregory Neil Shapiro 		if (*filename == '/')
506740266059SGregory Neil Shapiro 			(void) sm_strlcat(targetfile, filename + 1,
506806f25ae9SGregory Neil Shapiro 					  sizeof targetfile);
506906f25ae9SGregory Neil Shapiro 		else
507040266059SGregory Neil Shapiro 			(void) sm_strlcat(targetfile, filename,
507140266059SGregory Neil Shapiro 					  sizeof targetfile);
507206f25ae9SGregory Neil Shapiro 	}
507306f25ae9SGregory Neil Shapiro 	else
507406f25ae9SGregory Neil Shapiro 	{
507540266059SGregory Neil Shapiro 		if (sm_strlcpy(targetfile, filename, sizeof targetfile) >=
507640266059SGregory Neil Shapiro 		    sizeof targetfile)
507706f25ae9SGregory Neil Shapiro 		{
507806f25ae9SGregory Neil Shapiro 			syserr("mailfile: filename too long (%s)", filename);
507906f25ae9SGregory Neil Shapiro 			return EX_CANTCREAT;
508006f25ae9SGregory Neil Shapiro 		}
508106f25ae9SGregory Neil Shapiro 		realfile = targetfile;
5082c2aa98e2SPeter Wemm 	}
5083c2aa98e2SPeter Wemm 
5084c2aa98e2SPeter Wemm 	/*
5085c2aa98e2SPeter Wemm 	**  Fork so we can change permissions here.
5086c2aa98e2SPeter Wemm 	**	Note that we MUST use fork, not vfork, because of
5087c2aa98e2SPeter Wemm 	**	the complications of calling subroutines, etc.
5088c2aa98e2SPeter Wemm 	*/
5089c2aa98e2SPeter Wemm 
5090605302a5SGregory Neil Shapiro 
5091605302a5SGregory Neil Shapiro 	/*
5092605302a5SGregory Neil Shapiro 	**  Dispose of SIGCHLD signal catchers that may be laying
5093605302a5SGregory Neil Shapiro 	**  around so that the waitfor() below will get it.
5094605302a5SGregory Neil Shapiro 	*/
5095605302a5SGregory Neil Shapiro 
5096605302a5SGregory Neil Shapiro 	(void) sm_signal(SIGCHLD, SIG_DFL);
5097605302a5SGregory Neil Shapiro 
5098c2aa98e2SPeter Wemm 	DOFORK(fork);
5099c2aa98e2SPeter Wemm 
5100c2aa98e2SPeter Wemm 	if (pid < 0)
510106f25ae9SGregory Neil Shapiro 		return EX_OSERR;
5102c2aa98e2SPeter Wemm 	else if (pid == 0)
5103c2aa98e2SPeter Wemm 	{
5104c2aa98e2SPeter Wemm 		/* child -- actually write to file */
5105c2aa98e2SPeter Wemm 		struct stat stb;
5106c2aa98e2SPeter Wemm 		MCI mcibuf;
5107065a643dSPeter Wemm 		int err;
5108c2aa98e2SPeter Wemm 		volatile int oflags = O_WRONLY|O_APPEND;
5109c2aa98e2SPeter Wemm 
51108774250cSGregory Neil Shapiro 		/* Reset global flags */
51118774250cSGregory Neil Shapiro 		RestartRequest = NULL;
511240266059SGregory Neil Shapiro 		RestartWorkGroup = false;
51138774250cSGregory Neil Shapiro 		ShutdownRequest = NULL;
51148774250cSGregory Neil Shapiro 		PendingSignal = 0;
511540266059SGregory Neil Shapiro 		CurrentPid = getpid();
51168774250cSGregory Neil Shapiro 
5117c2aa98e2SPeter Wemm 		if (e->e_lockfp != NULL)
511840266059SGregory Neil Shapiro 			(void) close(sm_io_getinfo(e->e_lockfp, SM_IO_WHAT_FD,
511940266059SGregory Neil Shapiro 				     NULL));
5120c2aa98e2SPeter Wemm 
512140266059SGregory Neil Shapiro 		(void) sm_signal(SIGINT, SIG_DFL);
512240266059SGregory Neil Shapiro 		(void) sm_signal(SIGHUP, SIG_DFL);
512340266059SGregory Neil Shapiro 		(void) sm_signal(SIGTERM, SIG_DFL);
5124c2aa98e2SPeter Wemm 		(void) umask(OldUmask);
5125c2aa98e2SPeter Wemm 		e->e_to = filename;
5126c2aa98e2SPeter Wemm 		ExitStat = EX_OK;
5127c2aa98e2SPeter Wemm 
5128c2aa98e2SPeter Wemm 		if (setjmp(CtxMailfileTimeout) != 0)
5129c2aa98e2SPeter Wemm 		{
513040266059SGregory Neil Shapiro 			RETURN(EX_TEMPFAIL);
5131c2aa98e2SPeter Wemm 		}
5132c2aa98e2SPeter Wemm 
5133c2aa98e2SPeter Wemm 		if (TimeOuts.to_fileopen > 0)
513440266059SGregory Neil Shapiro 			ev = sm_setevent(TimeOuts.to_fileopen, mailfiletimeout,
513540266059SGregory Neil Shapiro 					 0);
5136c2aa98e2SPeter Wemm 		else
5137c2aa98e2SPeter Wemm 			ev = NULL;
5138c2aa98e2SPeter Wemm 
513940266059SGregory Neil Shapiro 		/* check file mode to see if set-user-ID */
514006f25ae9SGregory Neil Shapiro 		if (stat(targetfile, &stb) < 0)
5141c2aa98e2SPeter Wemm 			mode = FileMode;
514206f25ae9SGregory Neil Shapiro 		else
5143c2aa98e2SPeter Wemm 			mode = stb.st_mode;
5144c2aa98e2SPeter Wemm 
5145c2aa98e2SPeter Wemm 		/* limit the errors to those actually caused in the child */
5146c2aa98e2SPeter Wemm 		errno = 0;
5147c2aa98e2SPeter Wemm 		ExitStat = EX_OK;
5148c2aa98e2SPeter Wemm 
514906f25ae9SGregory Neil Shapiro 		/* Allow alias expansions to use the S_IS{U,G}ID bits */
515006f25ae9SGregory Neil Shapiro 		if ((ctladdr != NULL && !bitset(QALIAS, ctladdr->q_flags)) ||
515106f25ae9SGregory Neil Shapiro 		    bitset(SFF_RUNASREALUID, sfflags))
5152c2aa98e2SPeter Wemm 		{
515340266059SGregory Neil Shapiro 			/* ignore set-user-ID and set-group-ID bits */
5154c2aa98e2SPeter Wemm 			mode &= ~(S_ISGID|S_ISUID);
515506f25ae9SGregory Neil Shapiro 			if (tTd(11, 20))
515640266059SGregory Neil Shapiro 				sm_dprintf("mailfile: ignoring set-user-ID/set-group-ID bits\n");
5157c2aa98e2SPeter Wemm 		}
5158c2aa98e2SPeter Wemm 
515940266059SGregory Neil Shapiro 		/* we have to open the data file BEFORE setuid() */
5160c2aa98e2SPeter Wemm 		if (e->e_dfp == NULL && bitset(EF_HAS_DF, e->e_flags))
5161c2aa98e2SPeter Wemm 		{
516240266059SGregory Neil Shapiro 			char *df = queuename(e, DATAFL_LETTER);
5163c2aa98e2SPeter Wemm 
516440266059SGregory Neil Shapiro 			e->e_dfp = sm_io_open(SmFtStdio, SM_TIME_DEFAULT, df,
516540266059SGregory Neil Shapiro 					      SM_IO_RDONLY, NULL);
5166c2aa98e2SPeter Wemm 			if (e->e_dfp == NULL)
5167c2aa98e2SPeter Wemm 			{
5168c2aa98e2SPeter Wemm 				syserr("mailfile: Cannot open %s for %s from %s",
5169c2aa98e2SPeter Wemm 					df, e->e_to, e->e_from.q_paddr);
5170c2aa98e2SPeter Wemm 			}
5171c2aa98e2SPeter Wemm 		}
5172c2aa98e2SPeter Wemm 
5173c2aa98e2SPeter Wemm 		/* select a new user to run as */
5174c2aa98e2SPeter Wemm 		if (!bitset(SFF_RUNASREALUID, sfflags))
5175c2aa98e2SPeter Wemm 		{
5176c2aa98e2SPeter Wemm 			if (bitnset(M_SPECIFIC_UID, mailer->m_flags))
5177c2aa98e2SPeter Wemm 			{
5178c2aa98e2SPeter Wemm 				RealUserName = NULL;
5179c2aa98e2SPeter Wemm 				RealUid = mailer->m_uid;
518006f25ae9SGregory Neil Shapiro 				if (RunAsUid != 0 && RealUid != RunAsUid)
518106f25ae9SGregory Neil Shapiro 				{
518206f25ae9SGregory Neil Shapiro 					/* Only root can change the uid */
518340266059SGregory Neil Shapiro 					syserr("mailfile: insufficient privileges to change uid, RunAsUid=%d, RealUid=%d",
518440266059SGregory Neil Shapiro 						(int) RunAsUid, (int) RealUid);
518540266059SGregory Neil Shapiro 					RETURN(EX_TEMPFAIL);
518606f25ae9SGregory Neil Shapiro 				}
5187c2aa98e2SPeter Wemm 			}
5188c2aa98e2SPeter Wemm 			else if (bitset(S_ISUID, mode))
5189c2aa98e2SPeter Wemm 			{
5190c2aa98e2SPeter Wemm 				RealUserName = NULL;
5191c2aa98e2SPeter Wemm 				RealUid = stb.st_uid;
5192c2aa98e2SPeter Wemm 			}
5193c2aa98e2SPeter Wemm 			else if (ctladdr != NULL && ctladdr->q_uid != 0)
5194c2aa98e2SPeter Wemm 			{
5195c2aa98e2SPeter Wemm 				if (ctladdr->q_ruser != NULL)
5196c2aa98e2SPeter Wemm 					RealUserName = ctladdr->q_ruser;
5197c2aa98e2SPeter Wemm 				else
5198c2aa98e2SPeter Wemm 					RealUserName = ctladdr->q_user;
5199c2aa98e2SPeter Wemm 				RealUid = ctladdr->q_uid;
5200c2aa98e2SPeter Wemm 			}
5201c2aa98e2SPeter Wemm 			else if (mailer != NULL && mailer->m_uid != 0)
5202c2aa98e2SPeter Wemm 			{
5203c2aa98e2SPeter Wemm 				RealUserName = DefUser;
5204c2aa98e2SPeter Wemm 				RealUid = mailer->m_uid;
5205c2aa98e2SPeter Wemm 			}
5206c2aa98e2SPeter Wemm 			else
5207c2aa98e2SPeter Wemm 			{
5208c2aa98e2SPeter Wemm 				RealUserName = DefUser;
5209c2aa98e2SPeter Wemm 				RealUid = DefUid;
5210c2aa98e2SPeter Wemm 			}
5211c2aa98e2SPeter Wemm 
5212c2aa98e2SPeter Wemm 			/* select a new group to run as */
5213c2aa98e2SPeter Wemm 			if (bitnset(M_SPECIFIC_UID, mailer->m_flags))
521406f25ae9SGregory Neil Shapiro 			{
5215c2aa98e2SPeter Wemm 				RealGid = mailer->m_gid;
521606f25ae9SGregory Neil Shapiro 				if (RunAsUid != 0 &&
521706f25ae9SGregory Neil Shapiro 				    (RealGid != getgid() ||
521806f25ae9SGregory Neil Shapiro 				     RealGid != getegid()))
521906f25ae9SGregory Neil Shapiro 				{
522006f25ae9SGregory Neil Shapiro 					/* Only root can change the gid */
522140266059SGregory Neil Shapiro 					syserr("mailfile: insufficient privileges to change gid, RealGid=%d, RunAsUid=%d, gid=%d, egid=%d",
522240266059SGregory Neil Shapiro 					       (int) RealGid, (int) RunAsUid,
522340266059SGregory Neil Shapiro 					       (int) getgid(), (int) getegid());
522440266059SGregory Neil Shapiro 					RETURN(EX_TEMPFAIL);
522506f25ae9SGregory Neil Shapiro 				}
522606f25ae9SGregory Neil Shapiro 			}
5227c2aa98e2SPeter Wemm 			else if (bitset(S_ISGID, mode))
5228c2aa98e2SPeter Wemm 				RealGid = stb.st_gid;
522906f25ae9SGregory Neil Shapiro 			else if (ctladdr != NULL &&
523006f25ae9SGregory Neil Shapiro 				 ctladdr->q_uid == DefUid &&
523106f25ae9SGregory Neil Shapiro 				 ctladdr->q_gid == 0)
5232193538b7SGregory Neil Shapiro 			{
5233193538b7SGregory Neil Shapiro 				/*
5234193538b7SGregory Neil Shapiro 				**  Special case:  This means it is an
5235193538b7SGregory Neil Shapiro 				**  alias and we should act as DefaultUser.
5236193538b7SGregory Neil Shapiro 				**  See alias()'s comments.
5237193538b7SGregory Neil Shapiro 				*/
5238193538b7SGregory Neil Shapiro 
523906f25ae9SGregory Neil Shapiro 				RealGid = DefGid;
5240193538b7SGregory Neil Shapiro 				RealUserName = DefUser;
5241193538b7SGregory Neil Shapiro 			}
5242193538b7SGregory Neil Shapiro 			else if (ctladdr != NULL && ctladdr->q_uid != 0)
5243193538b7SGregory Neil Shapiro 				RealGid = ctladdr->q_gid;
5244c2aa98e2SPeter Wemm 			else if (mailer != NULL && mailer->m_gid != 0)
5245c2aa98e2SPeter Wemm 				RealGid = mailer->m_gid;
5246c2aa98e2SPeter Wemm 			else
5247c2aa98e2SPeter Wemm 				RealGid = DefGid;
5248c2aa98e2SPeter Wemm 		}
5249c2aa98e2SPeter Wemm 
5250c2aa98e2SPeter Wemm 		/* last ditch */
5251c2aa98e2SPeter Wemm 		if (!bitset(SFF_ROOTOK, sfflags))
5252c2aa98e2SPeter Wemm 		{
5253c2aa98e2SPeter Wemm 			if (RealUid == 0)
5254c2aa98e2SPeter Wemm 				RealUid = DefUid;
5255c2aa98e2SPeter Wemm 			if (RealGid == 0)
5256c2aa98e2SPeter Wemm 				RealGid = DefGid;
5257c2aa98e2SPeter Wemm 		}
5258c2aa98e2SPeter Wemm 
5259c2aa98e2SPeter Wemm 		/* set group id list (needs /etc/group access) */
5260c2aa98e2SPeter Wemm 		if (RealUserName != NULL && !DontInitGroups)
5261c2aa98e2SPeter Wemm 		{
5262c2aa98e2SPeter Wemm 			if (initgroups(RealUserName, RealGid) == -1 && suidwarn)
526306f25ae9SGregory Neil Shapiro 			{
5264c2aa98e2SPeter Wemm 				syserr("mailfile: initgroups(%s, %d) failed",
5265c2aa98e2SPeter Wemm 					RealUserName, RealGid);
526640266059SGregory Neil Shapiro 				RETURN(EX_TEMPFAIL);
526706f25ae9SGregory Neil Shapiro 			}
5268c2aa98e2SPeter Wemm 		}
5269c2aa98e2SPeter Wemm 		else
5270c2aa98e2SPeter Wemm 		{
5271c2aa98e2SPeter Wemm 			GIDSET_T gidset[1];
5272c2aa98e2SPeter Wemm 
5273c2aa98e2SPeter Wemm 			gidset[0] = RealGid;
5274c2aa98e2SPeter Wemm 			if (setgroups(1, gidset) == -1 && suidwarn)
527506f25ae9SGregory Neil Shapiro 			{
5276c2aa98e2SPeter Wemm 				syserr("mailfile: setgroups() failed");
527740266059SGregory Neil Shapiro 				RETURN(EX_TEMPFAIL);
527806f25ae9SGregory Neil Shapiro 			}
5279c2aa98e2SPeter Wemm 		}
5280c2aa98e2SPeter Wemm 
528106f25ae9SGregory Neil Shapiro 		/*
528206f25ae9SGregory Neil Shapiro 		**  If you have a safe environment, go into it.
528306f25ae9SGregory Neil Shapiro 		*/
5284c2aa98e2SPeter Wemm 
528506f25ae9SGregory Neil Shapiro 		if (realfile != targetfile)
528606f25ae9SGregory Neil Shapiro 		{
5287605302a5SGregory Neil Shapiro 			char save;
5288605302a5SGregory Neil Shapiro 
5289605302a5SGregory Neil Shapiro 			save = *realfile;
529006f25ae9SGregory Neil Shapiro 			*realfile = '\0';
529106f25ae9SGregory Neil Shapiro 			if (tTd(11, 20))
529240266059SGregory Neil Shapiro 				sm_dprintf("mailfile: chroot %s\n", targetfile);
529306f25ae9SGregory Neil Shapiro 			if (chroot(targetfile) < 0)
5294c2aa98e2SPeter Wemm 			{
5295c2aa98e2SPeter Wemm 				syserr("mailfile: Cannot chroot(%s)",
529606f25ae9SGregory Neil Shapiro 				       targetfile);
529740266059SGregory Neil Shapiro 				RETURN(EX_CANTCREAT);
5298c2aa98e2SPeter Wemm 			}
5299605302a5SGregory Neil Shapiro 			*realfile = save;
5300c2aa98e2SPeter Wemm 		}
530106f25ae9SGregory Neil Shapiro 
530206f25ae9SGregory Neil Shapiro 		if (tTd(11, 40))
530340266059SGregory Neil Shapiro 			sm_dprintf("mailfile: deliver to %s\n", realfile);
530406f25ae9SGregory Neil Shapiro 
5305c2aa98e2SPeter Wemm 		if (chdir("/") < 0)
530606f25ae9SGregory Neil Shapiro 		{
5307c2aa98e2SPeter Wemm 			syserr("mailfile: cannot chdir(/)");
530840266059SGregory Neil Shapiro 			RETURN(EX_CANTCREAT);
530906f25ae9SGregory Neil Shapiro 		}
5310c2aa98e2SPeter Wemm 
5311c2aa98e2SPeter Wemm 		/* now reset the group and user ids */
5312c2aa98e2SPeter Wemm 		endpwent();
531340266059SGregory Neil Shapiro 		sm_mbdb_terminate();
5314c2aa98e2SPeter Wemm 		if (setgid(RealGid) < 0 && suidwarn)
531506f25ae9SGregory Neil Shapiro 		{
5316c2aa98e2SPeter Wemm 			syserr("mailfile: setgid(%ld) failed", (long) RealGid);
531740266059SGregory Neil Shapiro 			RETURN(EX_TEMPFAIL);
531806f25ae9SGregory Neil Shapiro 		}
5319c2aa98e2SPeter Wemm 		vendor_set_uid(RealUid);
5320c2aa98e2SPeter Wemm 		if (setuid(RealUid) < 0 && suidwarn)
532106f25ae9SGregory Neil Shapiro 		{
5322c2aa98e2SPeter Wemm 			syserr("mailfile: setuid(%ld) failed", (long) RealUid);
532340266059SGregory Neil Shapiro 			RETURN(EX_TEMPFAIL);
532406f25ae9SGregory Neil Shapiro 		}
532506f25ae9SGregory Neil Shapiro 
532606f25ae9SGregory Neil Shapiro 		if (tTd(11, 2))
532740266059SGregory Neil Shapiro 			sm_dprintf("mailfile: running as r/euid=%d/%d, r/egid=%d/%d\n",
532806f25ae9SGregory Neil Shapiro 				(int) getuid(), (int) geteuid(),
532906f25ae9SGregory Neil Shapiro 				(int) getgid(), (int) getegid());
533006f25ae9SGregory Neil Shapiro 
5331c2aa98e2SPeter Wemm 
5332c2aa98e2SPeter Wemm 		/* move into some "safe" directory */
5333c2aa98e2SPeter Wemm 		if (mailer->m_execdir != NULL)
5334c2aa98e2SPeter Wemm 		{
5335c2aa98e2SPeter Wemm 			char *q;
5336c2aa98e2SPeter Wemm 
5337c2aa98e2SPeter Wemm 			for (p = mailer->m_execdir; p != NULL; p = q)
5338c2aa98e2SPeter Wemm 			{
5339c2aa98e2SPeter Wemm 				q = strchr(p, ':');
5340c2aa98e2SPeter Wemm 				if (q != NULL)
5341c2aa98e2SPeter Wemm 					*q = '\0';
5342c2aa98e2SPeter Wemm 				expand(p, buf, sizeof buf, e);
5343c2aa98e2SPeter Wemm 				if (q != NULL)
5344c2aa98e2SPeter Wemm 					*q++ = ':';
5345c2aa98e2SPeter Wemm 				if (tTd(11, 20))
534640266059SGregory Neil Shapiro 					sm_dprintf("mailfile: trydir %s\n",
534740266059SGregory Neil Shapiro 						   buf);
5348c2aa98e2SPeter Wemm 				if (buf[0] != '\0' && chdir(buf) >= 0)
5349c2aa98e2SPeter Wemm 					break;
5350c2aa98e2SPeter Wemm 			}
5351c2aa98e2SPeter Wemm 		}
5352c2aa98e2SPeter Wemm 
535306f25ae9SGregory Neil Shapiro 		/*
535406f25ae9SGregory Neil Shapiro 		**  Recheck the file after we have assumed the ID of the
535506f25ae9SGregory Neil Shapiro 		**  delivery user to make sure we can deliver to it as
535606f25ae9SGregory Neil Shapiro 		**  that user.  This is necessary if sendmail is running
535706f25ae9SGregory Neil Shapiro 		**  as root and the file is on an NFS mount which treats
535806f25ae9SGregory Neil Shapiro 		**  root as nobody.
535906f25ae9SGregory Neil Shapiro 		*/
536006f25ae9SGregory Neil Shapiro 
536106f25ae9SGregory Neil Shapiro #if HASLSTAT
536206f25ae9SGregory Neil Shapiro 		if (bitnset(DBS_FILEDELIVERYTOSYMLINK, DontBlameSendmail))
536306f25ae9SGregory Neil Shapiro 			err = stat(realfile, &stb);
536406f25ae9SGregory Neil Shapiro 		else
536506f25ae9SGregory Neil Shapiro 			err = lstat(realfile, &stb);
536606f25ae9SGregory Neil Shapiro #else /* HASLSTAT */
536706f25ae9SGregory Neil Shapiro 		err = stat(realfile, &stb);
536806f25ae9SGregory Neil Shapiro #endif /* HASLSTAT */
536906f25ae9SGregory Neil Shapiro 
537006f25ae9SGregory Neil Shapiro 		if (err < 0)
537106f25ae9SGregory Neil Shapiro 		{
537206f25ae9SGregory Neil Shapiro 			stb.st_mode = ST_MODE_NOFILE;
537306f25ae9SGregory Neil Shapiro 			mode = FileMode;
537406f25ae9SGregory Neil Shapiro 			oflags |= O_CREAT|O_EXCL;
537506f25ae9SGregory Neil Shapiro 		}
537606f25ae9SGregory Neil Shapiro 		else if (bitset(S_IXUSR|S_IXGRP|S_IXOTH, mode) ||
537706f25ae9SGregory Neil Shapiro 			 (!bitnset(DBS_FILEDELIVERYTOHARDLINK,
537806f25ae9SGregory Neil Shapiro 				   DontBlameSendmail) &&
537906f25ae9SGregory Neil Shapiro 			  stb.st_nlink != 1) ||
538006f25ae9SGregory Neil Shapiro 			 (realfile != targetfile && !S_ISREG(mode)))
538106f25ae9SGregory Neil Shapiro 			exit(EX_CANTCREAT);
538206f25ae9SGregory Neil Shapiro 		else
538306f25ae9SGregory Neil Shapiro 			mode = stb.st_mode;
538406f25ae9SGregory Neil Shapiro 
538506f25ae9SGregory Neil Shapiro 		if (!bitnset(DBS_FILEDELIVERYTOSYMLINK, DontBlameSendmail))
5386c2aa98e2SPeter Wemm 			sfflags |= SFF_NOSLINK;
538706f25ae9SGregory Neil Shapiro 		if (!bitnset(DBS_FILEDELIVERYTOHARDLINK, DontBlameSendmail))
5388c2aa98e2SPeter Wemm 			sfflags |= SFF_NOHLINK;
5389c2aa98e2SPeter Wemm 		sfflags &= ~SFF_OPENASROOT;
539006f25ae9SGregory Neil Shapiro 		f = safefopen(realfile, oflags, mode, sfflags);
5391c2aa98e2SPeter Wemm 		if (f == NULL)
5392c2aa98e2SPeter Wemm 		{
539306f25ae9SGregory Neil Shapiro 			if (transienterror(errno))
539406f25ae9SGregory Neil Shapiro 			{
539506f25ae9SGregory Neil Shapiro 				usrerr("454 4.3.0 cannot open %s: %s",
539606f25ae9SGregory Neil Shapiro 				       shortenstring(realfile, MAXSHORTSTR),
539740266059SGregory Neil Shapiro 				       sm_errstring(errno));
539840266059SGregory Neil Shapiro 				RETURN(EX_TEMPFAIL);
539906f25ae9SGregory Neil Shapiro 			}
540006f25ae9SGregory Neil Shapiro 			else
540106f25ae9SGregory Neil Shapiro 			{
540206f25ae9SGregory Neil Shapiro 				usrerr("554 5.3.0 cannot open %s: %s",
540306f25ae9SGregory Neil Shapiro 				       shortenstring(realfile, MAXSHORTSTR),
540440266059SGregory Neil Shapiro 				       sm_errstring(errno));
540540266059SGregory Neil Shapiro 				RETURN(EX_CANTCREAT);
5406c2aa98e2SPeter Wemm 			}
540706f25ae9SGregory Neil Shapiro 		}
540840266059SGregory Neil Shapiro 		if (filechanged(realfile, sm_io_getinfo(f, SM_IO_WHAT_FD, NULL),
540940266059SGregory Neil Shapiro 		    &stb))
5410c2aa98e2SPeter Wemm 		{
541106f25ae9SGregory Neil Shapiro 			syserr("554 5.3.0 file changed after open");
541240266059SGregory Neil Shapiro 			RETURN(EX_CANTCREAT);
5413c2aa98e2SPeter Wemm 		}
541440266059SGregory Neil Shapiro 		if (fstat(sm_io_getinfo(f, SM_IO_WHAT_FD, NULL), &stb) < 0)
5415c2aa98e2SPeter Wemm 		{
541640266059SGregory Neil Shapiro 			syserr("554 5.3.0 cannot fstat %s",
541740266059SGregory Neil Shapiro 				sm_errstring(errno));
541840266059SGregory Neil Shapiro 			RETURN(EX_CANTCREAT);
5419c2aa98e2SPeter Wemm 		}
5420c2aa98e2SPeter Wemm 
542106f25ae9SGregory Neil Shapiro 		curoff = stb.st_size;
542206f25ae9SGregory Neil Shapiro 
5423c2aa98e2SPeter Wemm 		if (ev != NULL)
542440266059SGregory Neil Shapiro 			sm_clrevent(ev);
5425c2aa98e2SPeter Wemm 
542606f25ae9SGregory Neil Shapiro 		memset(&mcibuf, '\0', sizeof mcibuf);
5427c2aa98e2SPeter Wemm 		mcibuf.mci_mailer = mailer;
5428c2aa98e2SPeter Wemm 		mcibuf.mci_out = f;
5429c2aa98e2SPeter Wemm 		if (bitnset(M_7BITS, mailer->m_flags))
5430c2aa98e2SPeter Wemm 			mcibuf.mci_flags |= MCIF_7BIT;
5431c2aa98e2SPeter Wemm 
5432c2aa98e2SPeter Wemm 		/* clear out per-message flags from connection structure */
5433c2aa98e2SPeter Wemm 		mcibuf.mci_flags &= ~(MCIF_CVT7TO8|MCIF_CVT8TO7);
5434c2aa98e2SPeter Wemm 
5435c2aa98e2SPeter Wemm 		if (bitset(EF_HAS8BIT, e->e_flags) &&
5436c2aa98e2SPeter Wemm 		    !bitset(EF_DONT_MIME, e->e_flags) &&
5437c2aa98e2SPeter Wemm 		    bitnset(M_7BITS, mailer->m_flags))
5438c2aa98e2SPeter Wemm 			mcibuf.mci_flags |= MCIF_CVT8TO7;
5439c2aa98e2SPeter Wemm 
5440c2aa98e2SPeter Wemm #if MIME7TO8
5441c2aa98e2SPeter Wemm 		if (bitnset(M_MAKE8BIT, mailer->m_flags) &&
5442c2aa98e2SPeter Wemm 		    !bitset(MCIF_7BIT, mcibuf.mci_flags) &&
5443c2aa98e2SPeter Wemm 		    (p = hvalue("Content-Transfer-Encoding", e->e_header)) != NULL &&
544440266059SGregory Neil Shapiro 		    (sm_strcasecmp(p, "quoted-printable") == 0 ||
544540266059SGregory Neil Shapiro 		     sm_strcasecmp(p, "base64") == 0) &&
5446c2aa98e2SPeter Wemm 		    (p = hvalue("Content-Type", e->e_header)) != NULL)
5447c2aa98e2SPeter Wemm 		{
5448c2aa98e2SPeter Wemm 			/* may want to convert 7 -> 8 */
5449c2aa98e2SPeter Wemm 			/* XXX should really parse it here -- and use a class XXX */
545040266059SGregory Neil Shapiro 			if (sm_strncasecmp(p, "text/plain", 10) == 0 &&
5451c2aa98e2SPeter Wemm 			    (p[10] == '\0' || p[10] == ' ' || p[10] == ';'))
5452c2aa98e2SPeter Wemm 				mcibuf.mci_flags |= MCIF_CVT7TO8;
5453c2aa98e2SPeter Wemm 		}
545406f25ae9SGregory Neil Shapiro #endif /* MIME7TO8 */
5455c2aa98e2SPeter Wemm 
5456c2aa98e2SPeter Wemm 		putfromline(&mcibuf, e);
54572e43090eSPeter Wemm 		(*e->e_puthdr)(&mcibuf, e->e_header, e, M87F_OUTER);
5458c2aa98e2SPeter Wemm 		(*e->e_putbody)(&mcibuf, e, NULL);
5459c2aa98e2SPeter Wemm 		putline("\n", &mcibuf);
546040266059SGregory Neil Shapiro 		if (sm_io_flush(f, SM_TIME_DEFAULT) != 0 ||
546140266059SGregory Neil Shapiro 		    (SuperSafe != SAFE_NO &&
546240266059SGregory Neil Shapiro 		     fsync(sm_io_getinfo(f, SM_IO_WHAT_FD, NULL)) < 0) ||
546340266059SGregory Neil Shapiro 		    sm_io_error(f))
5464c2aa98e2SPeter Wemm 		{
5465c2aa98e2SPeter Wemm 			setstat(EX_IOERR);
546606f25ae9SGregory Neil Shapiro #if !NOFTRUNCATE
546740266059SGregory Neil Shapiro 			(void) ftruncate(sm_io_getinfo(f, SM_IO_WHAT_FD, NULL),
546840266059SGregory Neil Shapiro 					 curoff);
546906f25ae9SGregory Neil Shapiro #endif /* !NOFTRUNCATE */
5470c2aa98e2SPeter Wemm 		}
5471c2aa98e2SPeter Wemm 
5472c2aa98e2SPeter Wemm 		/* reset ISUID & ISGID bits for paranoid systems */
5473c2aa98e2SPeter Wemm #if HASFCHMOD
547440266059SGregory Neil Shapiro 		(void) fchmod(sm_io_getinfo(f, SM_IO_WHAT_FD, NULL),
547540266059SGregory Neil Shapiro 			      (MODE_T) mode);
547606f25ae9SGregory Neil Shapiro #else /* HASFCHMOD */
547706f25ae9SGregory Neil Shapiro 		(void) chmod(filename, (MODE_T) mode);
547806f25ae9SGregory Neil Shapiro #endif /* HASFCHMOD */
547940266059SGregory Neil Shapiro 		if (sm_io_close(f, SM_TIME_DEFAULT) < 0)
548006f25ae9SGregory Neil Shapiro 			setstat(EX_IOERR);
548140266059SGregory Neil Shapiro 		(void) sm_io_flush(smioout, SM_TIME_DEFAULT);
548206f25ae9SGregory Neil Shapiro 		(void) setuid(RealUid);
5483c2aa98e2SPeter Wemm 		exit(ExitStat);
5484c2aa98e2SPeter Wemm 		/* NOTREACHED */
5485c2aa98e2SPeter Wemm 	}
5486c2aa98e2SPeter Wemm 	else
5487c2aa98e2SPeter Wemm 	{
5488c2aa98e2SPeter Wemm 		/* parent -- wait for exit status */
5489c2aa98e2SPeter Wemm 		int st;
5490c2aa98e2SPeter Wemm 
5491c2aa98e2SPeter Wemm 		st = waitfor(pid);
5492c2aa98e2SPeter Wemm 		if (st == -1)
5493c2aa98e2SPeter Wemm 		{
5494c2aa98e2SPeter Wemm 			syserr("mailfile: %s: wait", mailer->m_name);
549506f25ae9SGregory Neil Shapiro 			return EX_SOFTWARE;
5496c2aa98e2SPeter Wemm 		}
5497c2aa98e2SPeter Wemm 		if (WIFEXITED(st))
549840266059SGregory Neil Shapiro 		{
549940266059SGregory Neil Shapiro 			errno = 0;
5500c2aa98e2SPeter Wemm 			return (WEXITSTATUS(st));
550140266059SGregory Neil Shapiro 		}
5502c2aa98e2SPeter Wemm 		else
5503c2aa98e2SPeter Wemm 		{
5504c2aa98e2SPeter Wemm 			syserr("mailfile: %s: child died on signal %d",
5505c2aa98e2SPeter Wemm 			       mailer->m_name, st);
550606f25ae9SGregory Neil Shapiro 			return EX_UNAVAILABLE;
5507c2aa98e2SPeter Wemm 		}
5508c2aa98e2SPeter Wemm 		/* NOTREACHED */
5509c2aa98e2SPeter Wemm 	}
5510c2aa98e2SPeter Wemm 	return EX_UNAVAILABLE;	/* avoid compiler warning on IRIX */
5511c2aa98e2SPeter Wemm }
5512c2aa98e2SPeter Wemm 
5513c2aa98e2SPeter Wemm static void
5514c2aa98e2SPeter Wemm mailfiletimeout()
5515c2aa98e2SPeter Wemm {
55168774250cSGregory Neil Shapiro 	/*
55178774250cSGregory Neil Shapiro 	**  NOTE: THIS CAN BE CALLED FROM A SIGNAL HANDLER.  DO NOT ADD
55188774250cSGregory Neil Shapiro 	**	ANYTHING TO THIS ROUTINE UNLESS YOU KNOW WHAT YOU ARE
55198774250cSGregory Neil Shapiro 	**	DOING.
55208774250cSGregory Neil Shapiro 	*/
55218774250cSGregory Neil Shapiro 
55228774250cSGregory Neil Shapiro 	errno = ETIMEDOUT;
5523c2aa98e2SPeter Wemm 	longjmp(CtxMailfileTimeout, 1);
5524c2aa98e2SPeter Wemm }
552540266059SGregory Neil Shapiro /*
5526c2aa98e2SPeter Wemm **  HOSTSIGNATURE -- return the "signature" for a host.
5527c2aa98e2SPeter Wemm **
5528c2aa98e2SPeter Wemm **	The signature describes how we are going to send this -- it
5529c2aa98e2SPeter Wemm **	can be just the hostname (for non-Internet hosts) or can be
5530c2aa98e2SPeter Wemm **	an ordered list of MX hosts.
5531c2aa98e2SPeter Wemm **
5532c2aa98e2SPeter Wemm **	Parameters:
5533c2aa98e2SPeter Wemm **		m -- the mailer describing this host.
5534c2aa98e2SPeter Wemm **		host -- the host name.
5535c2aa98e2SPeter Wemm **
5536c2aa98e2SPeter Wemm **	Returns:
5537c2aa98e2SPeter Wemm **		The signature for this host.
5538c2aa98e2SPeter Wemm **
5539c2aa98e2SPeter Wemm **	Side Effects:
5540c2aa98e2SPeter Wemm **		Can tweak the symbol table.
5541c2aa98e2SPeter Wemm */
554240266059SGregory Neil Shapiro 
554306f25ae9SGregory Neil Shapiro #define MAXHOSTSIGNATURE	8192	/* max len of hostsignature */
5544c2aa98e2SPeter Wemm 
554540266059SGregory Neil Shapiro char *
554606f25ae9SGregory Neil Shapiro hostsignature(m, host)
5547c2aa98e2SPeter Wemm 	register MAILER *m;
5548c2aa98e2SPeter Wemm 	char *host;
5549c2aa98e2SPeter Wemm {
5550c2aa98e2SPeter Wemm 	register char *p;
5551c2aa98e2SPeter Wemm 	register STAB *s;
555240266059SGregory Neil Shapiro 	time_t now;
555306f25ae9SGregory Neil Shapiro #if NAMED_BIND
555406f25ae9SGregory Neil Shapiro 	char sep = ':';
555506f25ae9SGregory Neil Shapiro 	char prevsep = ':';
5556c2aa98e2SPeter Wemm 	int i;
5557c2aa98e2SPeter Wemm 	int len;
5558c2aa98e2SPeter Wemm 	int nmx;
555906f25ae9SGregory Neil Shapiro 	int hl;
5560c2aa98e2SPeter Wemm 	char *hp;
5561c2aa98e2SPeter Wemm 	char *endp;
5562c2aa98e2SPeter Wemm 	int oldoptions = _res.options;
5563c2aa98e2SPeter Wemm 	char *mxhosts[MAXMXHOSTS + 1];
556440266059SGregory Neil Shapiro 	unsigned short mxprefs[MAXMXHOSTS + 1];
556506f25ae9SGregory Neil Shapiro #endif /* NAMED_BIND */
556606f25ae9SGregory Neil Shapiro 
556706f25ae9SGregory Neil Shapiro 	if (tTd(17, 3))
556840266059SGregory Neil Shapiro 		sm_dprintf("hostsignature(%s)\n", host);
556906f25ae9SGregory Neil Shapiro 
557006f25ae9SGregory Neil Shapiro 	/*
55718774250cSGregory Neil Shapiro 	**  If local delivery (and not remote), just return a constant.
557206f25ae9SGregory Neil Shapiro 	*/
557306f25ae9SGregory Neil Shapiro 
55748774250cSGregory Neil Shapiro 	if (bitnset(M_LOCALMAILER, m->m_flags) &&
557540266059SGregory Neil Shapiro 	    strcmp(m->m_mailer, "[IPC]") != 0 &&
557640266059SGregory Neil Shapiro 	    !(m->m_argv[0] != NULL && strcmp(m->m_argv[0], "TCP") == 0))
557706f25ae9SGregory Neil Shapiro 		return "localhost";
5578c2aa98e2SPeter Wemm 
5579c2aa98e2SPeter Wemm 	/*
5580c2aa98e2SPeter Wemm 	**  Check to see if this uses IPC -- if not, it can't have MX records.
5581c2aa98e2SPeter Wemm 	*/
5582c2aa98e2SPeter Wemm 
558340266059SGregory Neil Shapiro 	if (strcmp(m->m_mailer, "[IPC]") != 0 ||
558440266059SGregory Neil Shapiro 	    CurEnv->e_sendmode == SM_DEFER)
5585c2aa98e2SPeter Wemm 	{
558640266059SGregory Neil Shapiro 		/* just an ordinary mailer or deferred mode */
5587c2aa98e2SPeter Wemm 		return host;
5588c2aa98e2SPeter Wemm 	}
558906f25ae9SGregory Neil Shapiro #if NETUNIX
559006f25ae9SGregory Neil Shapiro 	else if (m->m_argv[0] != NULL &&
559106f25ae9SGregory Neil Shapiro 		 strcmp(m->m_argv[0], "FILE") == 0)
559206f25ae9SGregory Neil Shapiro 	{
559306f25ae9SGregory Neil Shapiro 		/* rendezvous in the file system, no MX records */
559406f25ae9SGregory Neil Shapiro 		return host;
559506f25ae9SGregory Neil Shapiro 	}
559606f25ae9SGregory Neil Shapiro #endif /* NETUNIX */
5597c2aa98e2SPeter Wemm 
5598c2aa98e2SPeter Wemm 	/*
5599c2aa98e2SPeter Wemm 	**  Look it up in the symbol table.
5600c2aa98e2SPeter Wemm 	*/
5601c2aa98e2SPeter Wemm 
560240266059SGregory Neil Shapiro 	now = curtime();
5603c2aa98e2SPeter Wemm 	s = stab(host, ST_HOSTSIG, ST_ENTER);
560440266059SGregory Neil Shapiro 	if (s->s_hostsig.hs_sig != NULL)
560540266059SGregory Neil Shapiro 	{
560640266059SGregory Neil Shapiro 		if (s->s_hostsig.hs_exp >= now)
560706f25ae9SGregory Neil Shapiro 		{
560806f25ae9SGregory Neil Shapiro 			if (tTd(17, 3))
560940266059SGregory Neil Shapiro 				sm_dprintf("hostsignature(): stab(%s) found %s\n", host,
561040266059SGregory Neil Shapiro 					   s->s_hostsig.hs_sig);
561140266059SGregory Neil Shapiro 			return s->s_hostsig.hs_sig;
561206f25ae9SGregory Neil Shapiro 		}
5613c2aa98e2SPeter Wemm 
561440266059SGregory Neil Shapiro 		/* signature is expired: clear it */
561540266059SGregory Neil Shapiro 		sm_free(s->s_hostsig.hs_sig);
561640266059SGregory Neil Shapiro 		s->s_hostsig.hs_sig = NULL;
561740266059SGregory Neil Shapiro 	}
561840266059SGregory Neil Shapiro 
561940266059SGregory Neil Shapiro 	/* set default TTL */
562040266059SGregory Neil Shapiro 	s->s_hostsig.hs_exp = now + SM_DEFAULT_TTL;
562140266059SGregory Neil Shapiro 
5622c2aa98e2SPeter Wemm 	/*
562340266059SGregory Neil Shapiro 	**  Not already there or expired -- create a signature.
5624c2aa98e2SPeter Wemm 	*/
5625c2aa98e2SPeter Wemm 
5626c2aa98e2SPeter Wemm #if NAMED_BIND
5627c2aa98e2SPeter Wemm 	if (ConfigLevel < 2)
5628c2aa98e2SPeter Wemm 		_res.options &= ~(RES_DEFNAMES | RES_DNSRCH);	/* XXX */
5629c2aa98e2SPeter Wemm 
5630c2aa98e2SPeter Wemm 	for (hp = host; hp != NULL; hp = endp)
5631c2aa98e2SPeter Wemm 	{
563206f25ae9SGregory Neil Shapiro #if NETINET6
563306f25ae9SGregory Neil Shapiro 		if (*hp == '[')
563406f25ae9SGregory Neil Shapiro 		{
563506f25ae9SGregory Neil Shapiro 			endp = strchr(hp + 1, ']');
5636c2aa98e2SPeter Wemm 			if (endp != NULL)
563706f25ae9SGregory Neil Shapiro 				endp = strpbrk(endp + 1, ":,");
563806f25ae9SGregory Neil Shapiro 		}
563906f25ae9SGregory Neil Shapiro 		else
564006f25ae9SGregory Neil Shapiro 			endp = strpbrk(hp, ":,");
564106f25ae9SGregory Neil Shapiro #else /* NETINET6 */
564206f25ae9SGregory Neil Shapiro 		endp = strpbrk(hp, ":,");
564306f25ae9SGregory Neil Shapiro #endif /* NETINET6 */
564406f25ae9SGregory Neil Shapiro 		if (endp != NULL)
564506f25ae9SGregory Neil Shapiro 		{
564606f25ae9SGregory Neil Shapiro 			sep = *endp;
5647c2aa98e2SPeter Wemm 			*endp = '\0';
564806f25ae9SGregory Neil Shapiro 		}
5649c2aa98e2SPeter Wemm 
5650c2aa98e2SPeter Wemm 		if (bitnset(M_NOMX, m->m_flags))
5651c2aa98e2SPeter Wemm 		{
5652c2aa98e2SPeter Wemm 			/* skip MX lookups */
5653c2aa98e2SPeter Wemm 			nmx = 1;
5654c2aa98e2SPeter Wemm 			mxhosts[0] = hp;
5655c2aa98e2SPeter Wemm 		}
5656c2aa98e2SPeter Wemm 		else
5657c2aa98e2SPeter Wemm 		{
5658c2aa98e2SPeter Wemm 			auto int rcode;
565940266059SGregory Neil Shapiro 			int ttl;
5660c2aa98e2SPeter Wemm 
566140266059SGregory Neil Shapiro 			nmx = getmxrr(hp, mxhosts, mxprefs, true, &rcode, true,
566240266059SGregory Neil Shapiro 				      &ttl);
5663c2aa98e2SPeter Wemm 			if (nmx <= 0)
5664c2aa98e2SPeter Wemm 			{
566513058a91SGregory Neil Shapiro 				int save_errno;
5666c2aa98e2SPeter Wemm 				register MCI *mci;
5667c2aa98e2SPeter Wemm 
5668c2aa98e2SPeter Wemm 				/* update the connection info for this host */
566913058a91SGregory Neil Shapiro 				save_errno = errno;
5670c2aa98e2SPeter Wemm 				mci = mci_get(hp, m);
567113058a91SGregory Neil Shapiro 				mci->mci_errno = save_errno;
5672c2aa98e2SPeter Wemm 				mci->mci_herrno = h_errno;
5673193538b7SGregory Neil Shapiro 				mci->mci_lastuse = now;
567406f25ae9SGregory Neil Shapiro 				if (rcode == EX_NOHOST)
567506f25ae9SGregory Neil Shapiro 					mci_setstat(mci, rcode, "5.1.2",
567606f25ae9SGregory Neil Shapiro 						    "550 Host unknown");
567706f25ae9SGregory Neil Shapiro 				else
5678c2aa98e2SPeter Wemm 					mci_setstat(mci, rcode, NULL, NULL);
5679c2aa98e2SPeter Wemm 
5680c2aa98e2SPeter Wemm 				/* use the original host name as signature */
5681c2aa98e2SPeter Wemm 				nmx = 1;
5682c2aa98e2SPeter Wemm 				mxhosts[0] = hp;
5683c2aa98e2SPeter Wemm 			}
568406f25ae9SGregory Neil Shapiro 			if (tTd(17, 3))
568540266059SGregory Neil Shapiro 				sm_dprintf("hostsignature(): getmxrr() returned %d, mxhosts[0]=%s\n",
568606f25ae9SGregory Neil Shapiro 					   nmx, mxhosts[0]);
568740266059SGregory Neil Shapiro 
568840266059SGregory Neil Shapiro 			/*
568940266059SGregory Neil Shapiro 			**  Set new TTL: we use only one!
569040266059SGregory Neil Shapiro 			**	We could try to use the minimum instead.
569140266059SGregory Neil Shapiro 			*/
569240266059SGregory Neil Shapiro 
569340266059SGregory Neil Shapiro 			s->s_hostsig.hs_exp = now + SM_MIN(ttl, SM_DEFAULT_TTL);
5694c2aa98e2SPeter Wemm 		}
5695c2aa98e2SPeter Wemm 
5696c2aa98e2SPeter Wemm 		len = 0;
5697c2aa98e2SPeter Wemm 		for (i = 0; i < nmx; i++)
5698c2aa98e2SPeter Wemm 			len += strlen(mxhosts[i]) + 1;
569940266059SGregory Neil Shapiro 		if (s->s_hostsig.hs_sig != NULL)
570040266059SGregory Neil Shapiro 			len += strlen(s->s_hostsig.hs_sig) + 1;
570140266059SGregory Neil Shapiro 		if (len < 0 || len >= MAXHOSTSIGNATURE)
570206f25ae9SGregory Neil Shapiro 		{
570306f25ae9SGregory Neil Shapiro 			sm_syslog(LOG_WARNING, NOQID, "hostsignature for host '%s' exceeds maxlen (%d): %d",
570406f25ae9SGregory Neil Shapiro 				  host, MAXHOSTSIGNATURE, len);
570506f25ae9SGregory Neil Shapiro 			len = MAXHOSTSIGNATURE;
570606f25ae9SGregory Neil Shapiro 		}
570740266059SGregory Neil Shapiro 		p = sm_pmalloc_x(len);
570840266059SGregory Neil Shapiro 		if (s->s_hostsig.hs_sig != NULL)
5709c2aa98e2SPeter Wemm 		{
571040266059SGregory Neil Shapiro 			(void) sm_strlcpy(p, s->s_hostsig.hs_sig, len);
571140266059SGregory Neil Shapiro 			sm_free(s->s_hostsig.hs_sig); /* XXX */
571240266059SGregory Neil Shapiro 			s->s_hostsig.hs_sig = p;
571306f25ae9SGregory Neil Shapiro 			hl = strlen(p);
571406f25ae9SGregory Neil Shapiro 			p += hl;
571506f25ae9SGregory Neil Shapiro 			*p++ = prevsep;
571606f25ae9SGregory Neil Shapiro 			len -= hl + 1;
5717c2aa98e2SPeter Wemm 		}
5718c2aa98e2SPeter Wemm 		else
571940266059SGregory Neil Shapiro 			s->s_hostsig.hs_sig = p;
5720c2aa98e2SPeter Wemm 		for (i = 0; i < nmx; i++)
5721c2aa98e2SPeter Wemm 		{
572206f25ae9SGregory Neil Shapiro 			hl = strlen(mxhosts[i]);
572306f25ae9SGregory Neil Shapiro 			if (len - 1 < hl || len <= 1)
572406f25ae9SGregory Neil Shapiro 			{
572506f25ae9SGregory Neil Shapiro 				/* force to drop out of outer loop */
572606f25ae9SGregory Neil Shapiro 				len = -1;
572706f25ae9SGregory Neil Shapiro 				break;
5728c2aa98e2SPeter Wemm 			}
572906f25ae9SGregory Neil Shapiro 			if (i != 0)
573006f25ae9SGregory Neil Shapiro 			{
573106f25ae9SGregory Neil Shapiro 				if (mxprefs[i] == mxprefs[i - 1])
573206f25ae9SGregory Neil Shapiro 					*p++ = ',';
573306f25ae9SGregory Neil Shapiro 				else
573406f25ae9SGregory Neil Shapiro 					*p++ = ':';
573506f25ae9SGregory Neil Shapiro 				len--;
573606f25ae9SGregory Neil Shapiro 			}
573740266059SGregory Neil Shapiro 			(void) sm_strlcpy(p, mxhosts[i], len);
573806f25ae9SGregory Neil Shapiro 			p += hl;
573906f25ae9SGregory Neil Shapiro 			len -= hl;
574006f25ae9SGregory Neil Shapiro 		}
574106f25ae9SGregory Neil Shapiro 
574206f25ae9SGregory Neil Shapiro 		/*
574306f25ae9SGregory Neil Shapiro 		**  break out of loop if len exceeded MAXHOSTSIGNATURE
574406f25ae9SGregory Neil Shapiro 		**  because we won't have more space for further hosts
574506f25ae9SGregory Neil Shapiro 		**  anyway (separated by : in the .cf file).
574606f25ae9SGregory Neil Shapiro 		*/
574706f25ae9SGregory Neil Shapiro 
574806f25ae9SGregory Neil Shapiro 		if (len < 0)
574906f25ae9SGregory Neil Shapiro 			break;
5750c2aa98e2SPeter Wemm 		if (endp != NULL)
575106f25ae9SGregory Neil Shapiro 			*endp++ = sep;
575206f25ae9SGregory Neil Shapiro 		prevsep = sep;
5753c2aa98e2SPeter Wemm 	}
575440266059SGregory Neil Shapiro 	makelower(s->s_hostsig.hs_sig);
5755c2aa98e2SPeter Wemm 	if (ConfigLevel < 2)
5756c2aa98e2SPeter Wemm 		_res.options = oldoptions;
575706f25ae9SGregory Neil Shapiro #else /* NAMED_BIND */
5758c2aa98e2SPeter Wemm 	/* not using BIND -- the signature is just the host name */
575940266059SGregory Neil Shapiro 	/*
576040266059SGregory Neil Shapiro 	**  'host' points to storage that will be freed after we are
576140266059SGregory Neil Shapiro 	**  done processing the current envelope, so we copy it.
576240266059SGregory Neil Shapiro 	*/
576340266059SGregory Neil Shapiro 	s->s_hostsig.hs_sig = sm_pstrdup_x(host);
576406f25ae9SGregory Neil Shapiro #endif /* NAMED_BIND */
5765c2aa98e2SPeter Wemm 	if (tTd(17, 1))
576640266059SGregory Neil Shapiro 		sm_dprintf("hostsignature(%s) = %s\n", host, s->s_hostsig.hs_sig);
576740266059SGregory Neil Shapiro 	return s->s_hostsig.hs_sig;
5768c2aa98e2SPeter Wemm }
576940266059SGregory Neil Shapiro /*
577006f25ae9SGregory Neil Shapiro **  PARSE_HOSTSIGNATURE -- parse the "signature" and return MX host array.
577106f25ae9SGregory Neil Shapiro **
577206f25ae9SGregory Neil Shapiro **	The signature describes how we are going to send this -- it
577306f25ae9SGregory Neil Shapiro **	can be just the hostname (for non-Internet hosts) or can be
577406f25ae9SGregory Neil Shapiro **	an ordered list of MX hosts which must be randomized for equal
577506f25ae9SGregory Neil Shapiro **	MX preference values.
577606f25ae9SGregory Neil Shapiro **
577706f25ae9SGregory Neil Shapiro **	Parameters:
577806f25ae9SGregory Neil Shapiro **		sig -- the host signature.
577906f25ae9SGregory Neil Shapiro **		mxhosts -- array to populate.
578040266059SGregory Neil Shapiro **		mailer -- mailer.
578106f25ae9SGregory Neil Shapiro **
578206f25ae9SGregory Neil Shapiro **	Returns:
578306f25ae9SGregory Neil Shapiro **		The number of hosts inserted into mxhosts array.
578406f25ae9SGregory Neil Shapiro **
578506f25ae9SGregory Neil Shapiro **	Side Effects:
578606f25ae9SGregory Neil Shapiro **		Randomizes equal MX preference hosts in mxhosts.
578706f25ae9SGregory Neil Shapiro */
578806f25ae9SGregory Neil Shapiro 
578906f25ae9SGregory Neil Shapiro static int
579006f25ae9SGregory Neil Shapiro parse_hostsignature(sig, mxhosts, mailer)
579106f25ae9SGregory Neil Shapiro 	char *sig;
579206f25ae9SGregory Neil Shapiro 	char **mxhosts;
579306f25ae9SGregory Neil Shapiro 	MAILER *mailer;
579406f25ae9SGregory Neil Shapiro {
579540266059SGregory Neil Shapiro 	unsigned short curpref = 0;
579640266059SGregory Neil Shapiro 	int nmx = 0, i, j;	/* NOTE: i, j, and nmx must have same type */
579706f25ae9SGregory Neil Shapiro 	char *hp, *endp;
579840266059SGregory Neil Shapiro 	unsigned short prefer[MAXMXHOSTS];
579906f25ae9SGregory Neil Shapiro 	long rndm[MAXMXHOSTS];
580006f25ae9SGregory Neil Shapiro 
580106f25ae9SGregory Neil Shapiro 	for (hp = sig; hp != NULL; hp = endp)
580206f25ae9SGregory Neil Shapiro 	{
580306f25ae9SGregory Neil Shapiro 		char sep = ':';
580406f25ae9SGregory Neil Shapiro 
580506f25ae9SGregory Neil Shapiro #if NETINET6
580606f25ae9SGregory Neil Shapiro 		if (*hp == '[')
580706f25ae9SGregory Neil Shapiro 		{
580806f25ae9SGregory Neil Shapiro 			endp = strchr(hp + 1, ']');
580906f25ae9SGregory Neil Shapiro 			if (endp != NULL)
581006f25ae9SGregory Neil Shapiro 				endp = strpbrk(endp + 1, ":,");
581106f25ae9SGregory Neil Shapiro 		}
581206f25ae9SGregory Neil Shapiro 		else
581306f25ae9SGregory Neil Shapiro 			endp = strpbrk(hp, ":,");
581406f25ae9SGregory Neil Shapiro #else /* NETINET6 */
581506f25ae9SGregory Neil Shapiro 		endp = strpbrk(hp, ":,");
581606f25ae9SGregory Neil Shapiro #endif /* NETINET6 */
581706f25ae9SGregory Neil Shapiro 		if (endp != NULL)
581806f25ae9SGregory Neil Shapiro 		{
581906f25ae9SGregory Neil Shapiro 			sep = *endp;
582006f25ae9SGregory Neil Shapiro 			*endp = '\0';
582106f25ae9SGregory Neil Shapiro 		}
582206f25ae9SGregory Neil Shapiro 
582306f25ae9SGregory Neil Shapiro 		mxhosts[nmx] = hp;
582406f25ae9SGregory Neil Shapiro 		prefer[nmx] = curpref;
582506f25ae9SGregory Neil Shapiro 		if (mci_match(hp, mailer))
582606f25ae9SGregory Neil Shapiro 			rndm[nmx] = 0;
582706f25ae9SGregory Neil Shapiro 		else
582806f25ae9SGregory Neil Shapiro 			rndm[nmx] = get_random();
582906f25ae9SGregory Neil Shapiro 
583006f25ae9SGregory Neil Shapiro 		if (endp != NULL)
583106f25ae9SGregory Neil Shapiro 		{
583206f25ae9SGregory Neil Shapiro 			/*
583306f25ae9SGregory Neil Shapiro 			**  Since we don't have the original MX prefs,
583406f25ae9SGregory Neil Shapiro 			**  make our own.  If the separator is a ':', that
583506f25ae9SGregory Neil Shapiro 			**  means the preference for the next host will be
583606f25ae9SGregory Neil Shapiro 			**  higher than this one, so simply increment curpref.
583706f25ae9SGregory Neil Shapiro 			*/
583806f25ae9SGregory Neil Shapiro 
583906f25ae9SGregory Neil Shapiro 			if (sep == ':')
584006f25ae9SGregory Neil Shapiro 				curpref++;
584106f25ae9SGregory Neil Shapiro 
584206f25ae9SGregory Neil Shapiro 			*endp++ = sep;
584306f25ae9SGregory Neil Shapiro 		}
584406f25ae9SGregory Neil Shapiro 		if (++nmx >= MAXMXHOSTS)
584506f25ae9SGregory Neil Shapiro 			break;
584606f25ae9SGregory Neil Shapiro 	}
584706f25ae9SGregory Neil Shapiro 
584806f25ae9SGregory Neil Shapiro 	/* sort the records using the random factor for equal preferences */
584906f25ae9SGregory Neil Shapiro 	for (i = 0; i < nmx; i++)
585006f25ae9SGregory Neil Shapiro 	{
585106f25ae9SGregory Neil Shapiro 		for (j = i + 1; j < nmx; j++)
585206f25ae9SGregory Neil Shapiro 		{
585306f25ae9SGregory Neil Shapiro 			/*
585406f25ae9SGregory Neil Shapiro 			**  List is already sorted by MX preference, only
585506f25ae9SGregory Neil Shapiro 			**  need to look for equal preference MX records
585606f25ae9SGregory Neil Shapiro 			*/
585706f25ae9SGregory Neil Shapiro 
585806f25ae9SGregory Neil Shapiro 			if (prefer[i] < prefer[j])
585906f25ae9SGregory Neil Shapiro 				break;
586006f25ae9SGregory Neil Shapiro 
586106f25ae9SGregory Neil Shapiro 			if (prefer[i] > prefer[j] ||
586206f25ae9SGregory Neil Shapiro 			    (prefer[i] == prefer[j] && rndm[i] > rndm[j]))
586306f25ae9SGregory Neil Shapiro 			{
586440266059SGregory Neil Shapiro 				register unsigned short tempp;
586506f25ae9SGregory Neil Shapiro 				register long tempr;
586606f25ae9SGregory Neil Shapiro 				register char *temp1;
586706f25ae9SGregory Neil Shapiro 
586806f25ae9SGregory Neil Shapiro 				tempp = prefer[i];
586906f25ae9SGregory Neil Shapiro 				prefer[i] = prefer[j];
587006f25ae9SGregory Neil Shapiro 				prefer[j] = tempp;
587106f25ae9SGregory Neil Shapiro 				temp1 = mxhosts[i];
587206f25ae9SGregory Neil Shapiro 				mxhosts[i] = mxhosts[j];
587306f25ae9SGregory Neil Shapiro 				mxhosts[j] = temp1;
587406f25ae9SGregory Neil Shapiro 				tempr = rndm[i];
587506f25ae9SGregory Neil Shapiro 				rndm[i] = rndm[j];
587606f25ae9SGregory Neil Shapiro 				rndm[j] = tempr;
587706f25ae9SGregory Neil Shapiro 			}
587806f25ae9SGregory Neil Shapiro 		}
587906f25ae9SGregory Neil Shapiro 	}
588006f25ae9SGregory Neil Shapiro 	return nmx;
588106f25ae9SGregory Neil Shapiro }
588206f25ae9SGregory Neil Shapiro 
588306f25ae9SGregory Neil Shapiro # if STARTTLS
588406f25ae9SGregory Neil Shapiro static SSL_CTX	*clt_ctx = NULL;
588540266059SGregory Neil Shapiro static bool	tls_ok_clt = true;
588606f25ae9SGregory Neil Shapiro 
588740266059SGregory Neil Shapiro /*
588840266059SGregory Neil Shapiro **  SETCLTTLS -- client side TLS: allow/disallow.
588940266059SGregory Neil Shapiro **
589040266059SGregory Neil Shapiro **	Parameters:
589140266059SGregory Neil Shapiro **		tls_ok -- should tls be done?
589240266059SGregory Neil Shapiro **
589340266059SGregory Neil Shapiro **	Returns:
589440266059SGregory Neil Shapiro **		none.
589540266059SGregory Neil Shapiro **
589640266059SGregory Neil Shapiro **	Side Effects:
589740266059SGregory Neil Shapiro **		sets tls_ok_clt (static variable in this module)
589840266059SGregory Neil Shapiro */
589940266059SGregory Neil Shapiro 
590040266059SGregory Neil Shapiro void
590140266059SGregory Neil Shapiro setclttls(tls_ok)
590240266059SGregory Neil Shapiro 	bool tls_ok;
590340266059SGregory Neil Shapiro {
590440266059SGregory Neil Shapiro 	tls_ok_clt = tls_ok;
590540266059SGregory Neil Shapiro 	return;
590640266059SGregory Neil Shapiro }
590740266059SGregory Neil Shapiro /*
590806f25ae9SGregory Neil Shapiro **  INITCLTTLS -- initialize client side TLS
590906f25ae9SGregory Neil Shapiro **
591006f25ae9SGregory Neil Shapiro **	Parameters:
591140266059SGregory Neil Shapiro **		tls_ok -- should tls initialization be done?
591206f25ae9SGregory Neil Shapiro **
591306f25ae9SGregory Neil Shapiro **	Returns:
591406f25ae9SGregory Neil Shapiro **		succeeded?
591540266059SGregory Neil Shapiro **
591640266059SGregory Neil Shapiro **	Side Effects:
591740266059SGregory Neil Shapiro **		sets tls_ok_clt (static variable in this module)
591806f25ae9SGregory Neil Shapiro */
591906f25ae9SGregory Neil Shapiro 
592006f25ae9SGregory Neil Shapiro bool
592140266059SGregory Neil Shapiro initclttls(tls_ok)
592240266059SGregory Neil Shapiro 	bool tls_ok;
592306f25ae9SGregory Neil Shapiro {
592440266059SGregory Neil Shapiro 	if (!tls_ok_clt)
592540266059SGregory Neil Shapiro 		return false;
592640266059SGregory Neil Shapiro 	tls_ok_clt = tls_ok;
592740266059SGregory Neil Shapiro 	if (!tls_ok_clt)
592840266059SGregory Neil Shapiro 		return false;
592906f25ae9SGregory Neil Shapiro 	if (clt_ctx != NULL)
593040266059SGregory Neil Shapiro 		return true;	/* already done */
593140266059SGregory Neil Shapiro 	tls_ok_clt = inittls(&clt_ctx, TLS_I_CLT, false, CltCERTfile,
593240266059SGregory Neil Shapiro 			     Cltkeyfile, CACERTpath, CACERTfile, DHParams);
593340266059SGregory Neil Shapiro 	return tls_ok_clt;
593406f25ae9SGregory Neil Shapiro }
593506f25ae9SGregory Neil Shapiro 
593640266059SGregory Neil Shapiro /*
593706f25ae9SGregory Neil Shapiro **  STARTTLS -- try to start secure connection (client side)
593806f25ae9SGregory Neil Shapiro **
593906f25ae9SGregory Neil Shapiro **	Parameters:
594006f25ae9SGregory Neil Shapiro **		m -- the mailer.
594106f25ae9SGregory Neil Shapiro **		mci -- the mailer connection info.
594206f25ae9SGregory Neil Shapiro **		e -- the envelope.
594306f25ae9SGregory Neil Shapiro **
594406f25ae9SGregory Neil Shapiro **	Returns:
594506f25ae9SGregory Neil Shapiro **		success?
594606f25ae9SGregory Neil Shapiro **		(maybe this should be some other code than EX_
594706f25ae9SGregory Neil Shapiro **		that denotes which stage failed.)
594806f25ae9SGregory Neil Shapiro */
594906f25ae9SGregory Neil Shapiro 
595006f25ae9SGregory Neil Shapiro static int
595106f25ae9SGregory Neil Shapiro starttls(m, mci, e)
595206f25ae9SGregory Neil Shapiro 	MAILER *m;
595306f25ae9SGregory Neil Shapiro 	MCI *mci;
595406f25ae9SGregory Neil Shapiro 	ENVELOPE *e;
595506f25ae9SGregory Neil Shapiro {
595606f25ae9SGregory Neil Shapiro 	int smtpresult;
595742e5d165SGregory Neil Shapiro 	int result = 0;
595842e5d165SGregory Neil Shapiro 	int rfd, wfd;
595906f25ae9SGregory Neil Shapiro 	SSL *clt_ssl = NULL;
596040266059SGregory Neil Shapiro 	time_t tlsstart;
596106f25ae9SGregory Neil Shapiro 
596240266059SGregory Neil Shapiro 	if (clt_ctx == NULL && !initclttls(true))
596342e5d165SGregory Neil Shapiro 		return EX_TEMPFAIL;
596406f25ae9SGregory Neil Shapiro 	smtpmessage("STARTTLS", m, mci);
596506f25ae9SGregory Neil Shapiro 
596606f25ae9SGregory Neil Shapiro 	/* get the reply */
596740266059SGregory Neil Shapiro 	smtpresult = reply(m, mci, e, TimeOuts.to_starttls, NULL, NULL);
596806f25ae9SGregory Neil Shapiro 
596906f25ae9SGregory Neil Shapiro 	/* check return code from server */
597006f25ae9SGregory Neil Shapiro 	if (smtpresult == 454)
597106f25ae9SGregory Neil Shapiro 		return EX_TEMPFAIL;
597206f25ae9SGregory Neil Shapiro 	if (smtpresult == 501)
597306f25ae9SGregory Neil Shapiro 		return EX_USAGE;
597406f25ae9SGregory Neil Shapiro 	if (smtpresult == -1)
597506f25ae9SGregory Neil Shapiro 		return smtpresult;
597606f25ae9SGregory Neil Shapiro 	if (smtpresult != 220)
597706f25ae9SGregory Neil Shapiro 		return EX_PROTOCOL;
597806f25ae9SGregory Neil Shapiro 
597906f25ae9SGregory Neil Shapiro 	if (LogLevel > 13)
598040266059SGregory Neil Shapiro 		sm_syslog(LOG_INFO, NOQID, "STARTTLS=client, start=ok");
598106f25ae9SGregory Neil Shapiro 
598206f25ae9SGregory Neil Shapiro 	/* start connection */
598306f25ae9SGregory Neil Shapiro 	if ((clt_ssl = SSL_new(clt_ctx)) == NULL)
598406f25ae9SGregory Neil Shapiro 	{
598506f25ae9SGregory Neil Shapiro 		if (LogLevel > 5)
598606f25ae9SGregory Neil Shapiro 		{
598740266059SGregory Neil Shapiro 			sm_syslog(LOG_ERR, NOQID,
598840266059SGregory Neil Shapiro 				  "STARTTLS=client, error: SSL_new failed");
598906f25ae9SGregory Neil Shapiro 			if (LogLevel > 9)
599040266059SGregory Neil Shapiro 				tlslogerr("client");
599106f25ae9SGregory Neil Shapiro 		}
599206f25ae9SGregory Neil Shapiro 		return EX_SOFTWARE;
599306f25ae9SGregory Neil Shapiro 	}
599406f25ae9SGregory Neil Shapiro 
599540266059SGregory Neil Shapiro 	rfd = sm_io_getinfo(mci->mci_in, SM_IO_WHAT_FD, NULL);
599640266059SGregory Neil Shapiro 	wfd = sm_io_getinfo(mci->mci_out, SM_IO_WHAT_FD, NULL);
599742e5d165SGregory Neil Shapiro 
599806f25ae9SGregory Neil Shapiro 	/* SSL_clear(clt_ssl); ? */
599942e5d165SGregory Neil Shapiro 	if (rfd < 0 || wfd < 0 ||
600040266059SGregory Neil Shapiro 	    (result = SSL_set_rfd(clt_ssl, rfd)) != 1 ||
600140266059SGregory Neil Shapiro 	    (result = SSL_set_wfd(clt_ssl, wfd)) != 1)
600206f25ae9SGregory Neil Shapiro 	{
600306f25ae9SGregory Neil Shapiro 		if (LogLevel > 5)
600406f25ae9SGregory Neil Shapiro 		{
600540266059SGregory Neil Shapiro 			sm_syslog(LOG_ERR, NOQID,
600640266059SGregory Neil Shapiro 				  "STARTTLS=client, error: SSL_set_xfd failed=%d",
600740266059SGregory Neil Shapiro 				  result);
600806f25ae9SGregory Neil Shapiro 			if (LogLevel > 9)
600940266059SGregory Neil Shapiro 				tlslogerr("client");
601006f25ae9SGregory Neil Shapiro 		}
601106f25ae9SGregory Neil Shapiro 		return EX_SOFTWARE;
601206f25ae9SGregory Neil Shapiro 	}
601306f25ae9SGregory Neil Shapiro 	SSL_set_connect_state(clt_ssl);
601440266059SGregory Neil Shapiro 	tlsstart = curtime();
601540266059SGregory Neil Shapiro 
601640266059SGregory Neil Shapiro ssl_retry:
601706f25ae9SGregory Neil Shapiro 	if ((result = SSL_connect(clt_ssl)) <= 0)
601806f25ae9SGregory Neil Shapiro 	{
601906f25ae9SGregory Neil Shapiro 		int i;
602040266059SGregory Neil Shapiro 		bool timedout;
602140266059SGregory Neil Shapiro 		time_t left;
602240266059SGregory Neil Shapiro 		time_t now = curtime();
602340266059SGregory Neil Shapiro 		struct timeval tv;
602406f25ae9SGregory Neil Shapiro 
602506f25ae9SGregory Neil Shapiro 		/* what to do in this case? */
602606f25ae9SGregory Neil Shapiro 		i = SSL_get_error(clt_ssl, result);
602740266059SGregory Neil Shapiro 
602840266059SGregory Neil Shapiro 		/*
602940266059SGregory Neil Shapiro 		**  For SSL_ERROR_WANT_{READ,WRITE}:
603040266059SGregory Neil Shapiro 		**  There is not a complete SSL record available yet
603140266059SGregory Neil Shapiro 		**  or there is only a partial SSL record removed from
603240266059SGregory Neil Shapiro 		**  the network (socket) buffer into the SSL buffer.
603340266059SGregory Neil Shapiro 		**  The SSL_connect will only succeed when a full
603440266059SGregory Neil Shapiro 		**  SSL record is available (assuming a "real" error
603540266059SGregory Neil Shapiro 		**  doesn't happen). To handle when a "real" error
603640266059SGregory Neil Shapiro 		**  does happen the select is set for exceptions too.
603740266059SGregory Neil Shapiro 		**  The connection may be re-negotiated during this time
603840266059SGregory Neil Shapiro 		**  so both read and write "want errors" need to be handled.
603940266059SGregory Neil Shapiro 		**  A select() exception loops back so that a proper SSL
604040266059SGregory Neil Shapiro 		**  error message can be gotten.
604140266059SGregory Neil Shapiro 		*/
604240266059SGregory Neil Shapiro 
604340266059SGregory Neil Shapiro 		left = TimeOuts.to_starttls - (now - tlsstart);
604440266059SGregory Neil Shapiro 		timedout = left <= 0;
604540266059SGregory Neil Shapiro 		if (!timedout)
604640266059SGregory Neil Shapiro 		{
604740266059SGregory Neil Shapiro 			tv.tv_sec = left;
604840266059SGregory Neil Shapiro 			tv.tv_usec = 0;
604940266059SGregory Neil Shapiro 		}
605040266059SGregory Neil Shapiro 
605140266059SGregory Neil Shapiro 		if (!timedout && i == SSL_ERROR_WANT_READ)
605240266059SGregory Neil Shapiro 		{
605340266059SGregory Neil Shapiro 			fd_set ssl_maskr, ssl_maskx;
605440266059SGregory Neil Shapiro 
605540266059SGregory Neil Shapiro 			FD_ZERO(&ssl_maskr);
605640266059SGregory Neil Shapiro 			FD_SET(rfd, &ssl_maskr);
605740266059SGregory Neil Shapiro 			FD_ZERO(&ssl_maskx);
605840266059SGregory Neil Shapiro 			FD_SET(rfd, &ssl_maskx);
605940266059SGregory Neil Shapiro 			if (select(rfd + 1, &ssl_maskr, NULL, &ssl_maskx, &tv)
606040266059SGregory Neil Shapiro 			    > 0)
606140266059SGregory Neil Shapiro 				goto ssl_retry;
606240266059SGregory Neil Shapiro 		}
606340266059SGregory Neil Shapiro 		if (!timedout && i == SSL_ERROR_WANT_WRITE)
606440266059SGregory Neil Shapiro 		{
606540266059SGregory Neil Shapiro 			fd_set ssl_maskw, ssl_maskx;
606640266059SGregory Neil Shapiro 
606740266059SGregory Neil Shapiro 			FD_ZERO(&ssl_maskw);
606840266059SGregory Neil Shapiro 			FD_SET(wfd, &ssl_maskw);
606940266059SGregory Neil Shapiro 			FD_ZERO(&ssl_maskx);
607040266059SGregory Neil Shapiro 			FD_SET(rfd, &ssl_maskx);
607140266059SGregory Neil Shapiro 			if (select(wfd + 1, NULL, &ssl_maskw, &ssl_maskx, &tv)
607240266059SGregory Neil Shapiro 			    > 0)
607340266059SGregory Neil Shapiro 				goto ssl_retry;
607440266059SGregory Neil Shapiro 		}
607506f25ae9SGregory Neil Shapiro 		if (LogLevel > 5)
607606f25ae9SGregory Neil Shapiro 		{
607706f25ae9SGregory Neil Shapiro 			sm_syslog(LOG_ERR, e->e_id,
607840266059SGregory Neil Shapiro 				  "STARTTLS=client, error: connect failed=%d, SSL_error=%d, timedout=%d",
607940266059SGregory Neil Shapiro 				  result, i, (int) timedout);
608040266059SGregory Neil Shapiro 			if (LogLevel > 8)
608140266059SGregory Neil Shapiro 				tlslogerr("client");
608206f25ae9SGregory Neil Shapiro 		}
608306f25ae9SGregory Neil Shapiro 		SSL_free(clt_ssl);
608406f25ae9SGregory Neil Shapiro 		clt_ssl = NULL;
608506f25ae9SGregory Neil Shapiro 		return EX_SOFTWARE;
608606f25ae9SGregory Neil Shapiro 	}
608706f25ae9SGregory Neil Shapiro 	mci->mci_ssl = clt_ssl;
608840266059SGregory Neil Shapiro 	result = tls_get_info(mci->mci_ssl, false, mci->mci_host,
608940266059SGregory Neil Shapiro 			      &mci->mci_macro, true);
609006f25ae9SGregory Neil Shapiro 
609140266059SGregory Neil Shapiro 	/* switch to use TLS... */
609206f25ae9SGregory Neil Shapiro 	if (sfdctls(&mci->mci_in, &mci->mci_out, mci->mci_ssl) == 0)
609306f25ae9SGregory Neil Shapiro 		return EX_OK;
609406f25ae9SGregory Neil Shapiro 
609506f25ae9SGregory Neil Shapiro 	/* failure */
609606f25ae9SGregory Neil Shapiro 	SSL_free(clt_ssl);
609706f25ae9SGregory Neil Shapiro 	clt_ssl = NULL;
609806f25ae9SGregory Neil Shapiro 	return EX_SOFTWARE;
609906f25ae9SGregory Neil Shapiro }
610040266059SGregory Neil Shapiro /*
610106f25ae9SGregory Neil Shapiro **  ENDTLSCLT -- shutdown secure connection (client side)
610206f25ae9SGregory Neil Shapiro **
610306f25ae9SGregory Neil Shapiro **	Parameters:
610406f25ae9SGregory Neil Shapiro **		mci -- the mailer connection info.
610506f25ae9SGregory Neil Shapiro **
610606f25ae9SGregory Neil Shapiro **	Returns:
610706f25ae9SGregory Neil Shapiro **		success?
610806f25ae9SGregory Neil Shapiro */
610940266059SGregory Neil Shapiro 
611040266059SGregory Neil Shapiro static int
611106f25ae9SGregory Neil Shapiro endtlsclt(mci)
611206f25ae9SGregory Neil Shapiro 	MCI *mci;
611306f25ae9SGregory Neil Shapiro {
611406f25ae9SGregory Neil Shapiro 	int r;
611506f25ae9SGregory Neil Shapiro 
611606f25ae9SGregory Neil Shapiro 	if (!bitset(MCIF_TLSACT, mci->mci_flags))
611706f25ae9SGregory Neil Shapiro 		return EX_OK;
611806f25ae9SGregory Neil Shapiro 	r = endtls(mci->mci_ssl, "client");
611906f25ae9SGregory Neil Shapiro 	mci->mci_flags &= ~MCIF_TLSACT;
612006f25ae9SGregory Neil Shapiro 	return r;
612106f25ae9SGregory Neil Shapiro }
612240266059SGregory Neil Shapiro # endif /* STARTTLS */
612340266059SGregory Neil Shapiro # if STARTTLS || SASL
612440266059SGregory Neil Shapiro /*
612540266059SGregory Neil Shapiro **  ISCLTFLGSET -- check whether client flag is set.
612606f25ae9SGregory Neil Shapiro **
612706f25ae9SGregory Neil Shapiro **	Parameters:
612840266059SGregory Neil Shapiro **		e -- envelope.
612940266059SGregory Neil Shapiro **		flag -- flag to check in {client_flags}
613006f25ae9SGregory Neil Shapiro **
613106f25ae9SGregory Neil Shapiro **	Returns:
613240266059SGregory Neil Shapiro **		true iff flag is set.
613306f25ae9SGregory Neil Shapiro */
613406f25ae9SGregory Neil Shapiro 
613540266059SGregory Neil Shapiro static bool
613640266059SGregory Neil Shapiro iscltflgset(e, flag)
613740266059SGregory Neil Shapiro 	ENVELOPE *e;
613840266059SGregory Neil Shapiro 	int flag;
613906f25ae9SGregory Neil Shapiro {
614040266059SGregory Neil Shapiro 	char *p;
6141602a2b1bSGregory Neil Shapiro 
614240266059SGregory Neil Shapiro 	p = macvalue(macid("{client_flags}"), e);
614340266059SGregory Neil Shapiro 	if (p == NULL)
614440266059SGregory Neil Shapiro 		return false;
614540266059SGregory Neil Shapiro 	for (; *p != '\0'; p++)
614606f25ae9SGregory Neil Shapiro 	{
614740266059SGregory Neil Shapiro 		/* look for just this one flag */
614840266059SGregory Neil Shapiro 		if (*p == (char) flag)
614940266059SGregory Neil Shapiro 			return true;
615006f25ae9SGregory Neil Shapiro 	}
615140266059SGregory Neil Shapiro 	return false;
615206f25ae9SGregory Neil Shapiro }
615340266059SGregory Neil Shapiro # endif /* STARTTLS || SASL */
6154