deliver.c revision 141858
138032Speter/*
2141858Sgshapiro * Copyright (c) 1998-2005 Sendmail, Inc. and its suppliers.
364562Sgshapiro *	All rights reserved.
438032Speter * Copyright (c) 1983, 1995-1997 Eric P. Allman.  All rights reserved.
538032Speter * Copyright (c) 1988, 1993
638032Speter *	The Regents of the University of California.  All rights reserved.
738032Speter *
838032Speter * By using this file, you agree to the terms and conditions set
938032Speter * forth in the LICENSE file which can be found at the top level of
1038032Speter * the sendmail distribution.
1138032Speter *
1238032Speter */
1338032Speter
1464562Sgshapiro#include <sendmail.h>
1590792Sgshapiro#include <sys/time.h>
1638032Speter
17141858SgshapiroSM_RCSID("@(#)$Id: deliver.c,v 8.983 2005/01/07 17:43:22 ca Exp $")
1864562Sgshapiro
1938032Speter#if HASSETUSERCONTEXT
2038032Speter# include <login_cap.h>
2164562Sgshapiro#endif /* HASSETUSERCONTEXT */
2238032Speter
2394334Sgshapiro#if NETINET || NETINET6
2494334Sgshapiro# include <arpa/inet.h>
2594334Sgshapiro#endif /* NETINET || NETINET6 */
2694334Sgshapiro
2790792Sgshapiro#if STARTTLS || SASL
2864562Sgshapiro# include "sfsasl.h"
2990792Sgshapiro#endif /* STARTTLS || SASL */
3064562Sgshapiro
3164562Sgshapirostatic int	deliver __P((ENVELOPE *, ADDRESS *));
3264562Sgshapirostatic void	dup_queue_file __P((ENVELOPE *, ENVELOPE *, int));
33141858Sgshapirostatic void	mailfiletimeout __P((int));
34141858Sgshapirostatic void	endwaittimeout __P((int));
3564562Sgshapirostatic int	parse_hostsignature __P((char *, char **, MAILER *));
3664562Sgshapirostatic void	sendenvelope __P((ENVELOPE *, int));
3790792Sgshapiroextern MCI	*mci_new __P((SM_RPOOL_T *));
3890792Sgshapirostatic int	coloncmp __P((const char *, const char *));
3964562Sgshapiro
4090792Sgshapiro#if STARTTLS
4164562Sgshapirostatic int	starttls __P((MAILER *, MCI *, ENVELOPE *));
4290792Sgshapirostatic int	endtlsclt __P((MCI *));
4390792Sgshapiro#endif /* STARTTLS */
4490792Sgshapiro# if STARTTLS || SASL
4590792Sgshapirostatic bool	iscltflgset __P((ENVELOPE *, int));
4690792Sgshapiro# endif /* STARTTLS || SASL */
4738032Speter
4838032Speter/*
4938032Speter**  SENDALL -- actually send all the messages.
5038032Speter**
5138032Speter**	Parameters:
5238032Speter**		e -- the envelope to send.
5338032Speter**		mode -- the delivery mode to use.  If SM_DEFAULT, use
5438032Speter**			the current e->e_sendmode.
5538032Speter**
5638032Speter**	Returns:
5738032Speter**		none.
5838032Speter**
5938032Speter**	Side Effects:
6038032Speter**		Scans the send lists and sends everything it finds.
6138032Speter**		Delivers any appropriate error messages.
6238032Speter**		If we are running in a non-interactive mode, takes the
6338032Speter**			appropriate action.
6438032Speter*/
6538032Speter
6638032Spetervoid
6738032Spetersendall(e, mode)
6838032Speter	ENVELOPE *e;
6938032Speter	int mode;
7038032Speter{
7138032Speter	register ADDRESS *q;
7238032Speter	char *owner;
7338032Speter	int otherowners;
7464562Sgshapiro	int save_errno;
7538032Speter	register ENVELOPE *ee;
7638032Speter	ENVELOPE *splitenv = NULL;
7738032Speter	int oldverbose = Verbose;
7890792Sgshapiro	bool somedeliveries = false, expensive = false;
7938032Speter	pid_t pid;
8038032Speter
8138032Speter	/*
8238032Speter	**  If this message is to be discarded, don't bother sending
8338032Speter	**  the message at all.
8438032Speter	*/
8538032Speter
8638032Speter	if (bitset(EF_DISCARD, e->e_flags))
8738032Speter	{
8838032Speter		if (tTd(13, 1))
8990792Sgshapiro			sm_dprintf("sendall: discarding id %s\n", e->e_id);
9038032Speter		e->e_flags |= EF_CLRQUEUE;
9190792Sgshapiro		if (LogLevel > 9)
9290792Sgshapiro			logundelrcpts(e, "discarded", 9, true);
9390792Sgshapiro		else if (LogLevel > 4)
9438032Speter			sm_syslog(LOG_INFO, e->e_id, "discarded");
9590792Sgshapiro		markstats(e, NULL, STATS_REJECT);
9638032Speter		return;
9738032Speter	}
9838032Speter
9938032Speter	/*
10038032Speter	**  If we have had global, fatal errors, don't bother sending
10138032Speter	**  the message at all if we are in SMTP mode.  Local errors
10238032Speter	**  (e.g., a single address failing) will still cause the other
10338032Speter	**  addresses to be sent.
10438032Speter	*/
10538032Speter
10638032Speter	if (bitset(EF_FATALERRS, e->e_flags) &&
10738032Speter	    (OpMode == MD_SMTP || OpMode == MD_DAEMON))
10838032Speter	{
10938032Speter		e->e_flags |= EF_CLRQUEUE;
11038032Speter		return;
11138032Speter	}
11238032Speter
11338032Speter	/* determine actual delivery mode */
11438032Speter	if (mode == SM_DEFAULT)
11538032Speter	{
11638032Speter		mode = e->e_sendmode;
11738032Speter		if (mode != SM_VERIFY && mode != SM_DEFER &&
11838032Speter		    shouldqueue(e->e_msgpriority, e->e_ctime))
11938032Speter			mode = SM_QUEUE;
12038032Speter	}
12138032Speter
12238032Speter	if (tTd(13, 1))
12338032Speter	{
12490792Sgshapiro		sm_dprintf("\n===== SENDALL: mode %c, id %s, e_from ",
12538032Speter			mode, e->e_id);
126132943Sgshapiro		printaddr(sm_debug_file(), &e->e_from, false);
12790792Sgshapiro		sm_dprintf("\te_flags = ");
12838032Speter		printenvflags(e);
12990792Sgshapiro		sm_dprintf("sendqueue:\n");
130132943Sgshapiro		printaddr(sm_debug_file(), e->e_sendqueue, true);
13138032Speter	}
13238032Speter
13338032Speter	/*
13438032Speter	**  Do any preprocessing necessary for the mode we are running.
13538032Speter	**	Check to make sure the hop count is reasonable.
13638032Speter	**	Delete sends to the sender in mailing lists.
13738032Speter	*/
13838032Speter
13938032Speter	CurEnv = e;
14038032Speter	if (tTd(62, 1))
14138032Speter		checkfds(NULL);
14238032Speter
14338032Speter	if (e->e_hopcount > MaxHopCount)
14438032Speter	{
14577349Sgshapiro		char *recip;
14677349Sgshapiro
14777349Sgshapiro		if (e->e_sendqueue != NULL &&
14877349Sgshapiro		    e->e_sendqueue->q_paddr != NULL)
14977349Sgshapiro			recip = e->e_sendqueue->q_paddr;
15077349Sgshapiro		else
15177349Sgshapiro			recip = "(nobody)";
15277349Sgshapiro
15338032Speter		errno = 0;
15490792Sgshapiro		queueup(e, WILL_BE_QUEUED(mode), false);
15538032Speter		e->e_flags |= EF_FATALERRS|EF_PM_NOTIFY|EF_CLRQUEUE;
15664562Sgshapiro		ExitStat = EX_UNAVAILABLE;
15777349Sgshapiro		syserr("554 5.4.6 Too many hops %d (%d max): from %s via %s, to %s",
15877349Sgshapiro		       e->e_hopcount, MaxHopCount, e->e_from.q_paddr,
15977349Sgshapiro		       RealHostName == NULL ? "localhost" : RealHostName,
16077349Sgshapiro		       recip);
16164562Sgshapiro		for (q = e->e_sendqueue; q != NULL; q = q->q_next)
16264562Sgshapiro		{
16364562Sgshapiro			if (QS_IS_DEAD(q->q_state))
16464562Sgshapiro				continue;
16564562Sgshapiro			q->q_state = QS_BADADDR;
16664562Sgshapiro			q->q_status = "5.4.6";
16777349Sgshapiro			q->q_rstatus = "554 5.4.6 Too many hops";
16864562Sgshapiro		}
16938032Speter		return;
17038032Speter	}
17138032Speter
17238032Speter	/*
17338032Speter	**  Do sender deletion.
17438032Speter	**
17564562Sgshapiro	**	If the sender should be queued up, skip this.
17638032Speter	**	This can happen if the name server is hosed when you
17738032Speter	**	are trying to send mail.  The result is that the sender
17838032Speter	**	is instantiated in the queue as a recipient.
17938032Speter	*/
18038032Speter
18138032Speter	if (!bitset(EF_METOO, e->e_flags) &&
18264562Sgshapiro	    !QS_IS_QUEUEUP(e->e_from.q_state))
18338032Speter	{
18438032Speter		if (tTd(13, 5))
18538032Speter		{
18690792Sgshapiro			sm_dprintf("sendall: QS_SENDER ");
187132943Sgshapiro			printaddr(sm_debug_file(), &e->e_from, false);
18838032Speter		}
18964562Sgshapiro		e->e_from.q_state = QS_SENDER;
19038032Speter		(void) recipient(&e->e_from, &e->e_sendqueue, 0, e);
19138032Speter	}
19238032Speter
19338032Speter	/*
19438032Speter	**  Handle alias owners.
19538032Speter	**
19638032Speter	**	We scan up the q_alias chain looking for owners.
19738032Speter	**	We discard owners that are the same as the return path.
19838032Speter	*/
19938032Speter
20038032Speter	for (q = e->e_sendqueue; q != NULL; q = q->q_next)
20138032Speter	{
20238032Speter		register struct address *a;
20338032Speter
20438032Speter		for (a = q; a != NULL && a->q_owner == NULL; a = a->q_alias)
20538032Speter			continue;
20638032Speter		if (a != NULL)
20738032Speter			q->q_owner = a->q_owner;
20838032Speter
20938032Speter		if (q->q_owner != NULL &&
21064562Sgshapiro		    !QS_IS_DEAD(q->q_state) &&
21138032Speter		    strcmp(q->q_owner, e->e_from.q_paddr) == 0)
21238032Speter			q->q_owner = NULL;
21338032Speter	}
21438032Speter
21538032Speter	if (tTd(13, 25))
21638032Speter	{
21790792Sgshapiro		sm_dprintf("\nAfter first owner pass, sendq =\n");
218132943Sgshapiro		printaddr(sm_debug_file(), e->e_sendqueue, true);
21938032Speter	}
22038032Speter
22138032Speter	owner = "";
22238032Speter	otherowners = 1;
22338032Speter	while (owner != NULL && otherowners > 0)
22438032Speter	{
22538032Speter		if (tTd(13, 28))
22690792Sgshapiro			sm_dprintf("owner = \"%s\", otherowners = %d\n",
22790792Sgshapiro				   owner, otherowners);
22838032Speter		owner = NULL;
22938032Speter		otherowners = bitset(EF_SENDRECEIPT, e->e_flags) ? 1 : 0;
23038032Speter
23138032Speter		for (q = e->e_sendqueue; q != NULL; q = q->q_next)
23238032Speter		{
23338032Speter			if (tTd(13, 30))
23438032Speter			{
23590792Sgshapiro				sm_dprintf("Checking ");
236132943Sgshapiro				printaddr(sm_debug_file(), q, false);
23738032Speter			}
23864562Sgshapiro			if (QS_IS_DEAD(q->q_state))
23938032Speter			{
24038032Speter				if (tTd(13, 30))
24190792Sgshapiro					sm_dprintf("    ... QS_IS_DEAD\n");
24238032Speter				continue;
24338032Speter			}
24438032Speter			if (tTd(13, 29) && !tTd(13, 30))
24538032Speter			{
24690792Sgshapiro				sm_dprintf("Checking ");
247132943Sgshapiro				printaddr(sm_debug_file(), q, false);
24838032Speter			}
24938032Speter
25038032Speter			if (q->q_owner != NULL)
25138032Speter			{
25238032Speter				if (owner == NULL)
25338032Speter				{
25438032Speter					if (tTd(13, 40))
25590792Sgshapiro						sm_dprintf("    ... First owner = \"%s\"\n",
25690792Sgshapiro							   q->q_owner);
25738032Speter					owner = q->q_owner;
25838032Speter				}
25938032Speter				else if (owner != q->q_owner)
26038032Speter				{
26138032Speter					if (strcmp(owner, q->q_owner) == 0)
26238032Speter					{
26338032Speter						if (tTd(13, 40))
26490792Sgshapiro							sm_dprintf("    ... Same owner = \"%s\"\n",
26590792Sgshapiro								   owner);
26638032Speter
26738032Speter						/* make future comparisons cheap */
26838032Speter						q->q_owner = owner;
26938032Speter					}
27038032Speter					else
27138032Speter					{
27238032Speter						if (tTd(13, 40))
27390792Sgshapiro							sm_dprintf("    ... Another owner \"%s\"\n",
27490792Sgshapiro								   q->q_owner);
27538032Speter						otherowners++;
27638032Speter					}
27738032Speter					owner = q->q_owner;
27838032Speter				}
27938032Speter				else if (tTd(13, 40))
28090792Sgshapiro					sm_dprintf("    ... Same owner = \"%s\"\n",
28190792Sgshapiro						   owner);
28238032Speter			}
28338032Speter			else
28438032Speter			{
28538032Speter				if (tTd(13, 40))
28690792Sgshapiro					sm_dprintf("    ... Null owner\n");
28738032Speter				otherowners++;
28838032Speter			}
28938032Speter
29064562Sgshapiro			if (QS_IS_BADADDR(q->q_state))
29164562Sgshapiro			{
29264562Sgshapiro				if (tTd(13, 30))
29390792Sgshapiro					sm_dprintf("    ... QS_IS_BADADDR\n");
29464562Sgshapiro				continue;
29564562Sgshapiro			}
29664562Sgshapiro
29764562Sgshapiro			if (QS_IS_QUEUEUP(q->q_state))
29864562Sgshapiro			{
29964562Sgshapiro				MAILER *m = q->q_mailer;
30064562Sgshapiro
30164562Sgshapiro				/*
30264562Sgshapiro				**  If we have temporary address failures
30364562Sgshapiro				**  (e.g., dns failure) and a fallback MX is
30464562Sgshapiro				**  set, send directly to the fallback MX host.
30564562Sgshapiro				*/
30664562Sgshapiro
307132943Sgshapiro				if (FallbackMX != NULL &&
308132943Sgshapiro				    !wordinclass(FallbackMX, 'w') &&
30964562Sgshapiro				    mode != SM_VERIFY &&
31090792Sgshapiro				    !bitnset(M_NOMX, m->m_flags) &&
31190792Sgshapiro				    strcmp(m->m_mailer, "[IPC]") == 0 &&
31264562Sgshapiro				    m->m_argv[0] != NULL &&
31390792Sgshapiro				    strcmp(m->m_argv[0], "TCP") == 0)
31464562Sgshapiro				{
31564562Sgshapiro					int len;
31664562Sgshapiro					char *p;
31764562Sgshapiro
31864562Sgshapiro					if (tTd(13, 30))
319132943Sgshapiro						sm_dprintf("    ... FallbackMX\n");
32064562Sgshapiro
321132943Sgshapiro					len = strlen(FallbackMX) + 1;
32290792Sgshapiro					p = sm_rpool_malloc_x(e->e_rpool, len);
323132943Sgshapiro					(void) sm_strlcpy(p, FallbackMX, len);
32464562Sgshapiro					q->q_state = QS_OK;
32564562Sgshapiro					q->q_host = p;
32664562Sgshapiro				}
32764562Sgshapiro				else
32864562Sgshapiro				{
32964562Sgshapiro					if (tTd(13, 30))
33090792Sgshapiro						sm_dprintf("    ... QS_IS_QUEUEUP\n");
33164562Sgshapiro					continue;
33264562Sgshapiro				}
33364562Sgshapiro			}
33464562Sgshapiro
33538032Speter			/*
33638032Speter			**  If this mailer is expensive, and if we don't
33738032Speter			**  want to make connections now, just mark these
33838032Speter			**  addresses and return.  This is useful if we
33938032Speter			**  want to batch connections to reduce load.  This
34038032Speter			**  will cause the messages to be queued up, and a
34138032Speter			**  daemon will come along to send the messages later.
34238032Speter			*/
34338032Speter
34464562Sgshapiro			if (NoConnect && !Verbose &&
34564562Sgshapiro			    bitnset(M_EXPENSIVE, q->q_mailer->m_flags))
34638032Speter			{
34738032Speter				if (tTd(13, 30))
34890792Sgshapiro					sm_dprintf("    ... expensive\n");
34964562Sgshapiro				q->q_state = QS_QUEUEUP;
35090792Sgshapiro				expensive = true;
35138032Speter			}
35264562Sgshapiro			else if (bitnset(M_HOLD, q->q_mailer->m_flags) &&
35364562Sgshapiro				 QueueLimitId == NULL &&
35464562Sgshapiro				 QueueLimitSender == NULL &&
35564562Sgshapiro				 QueueLimitRecipient == NULL)
35638032Speter			{
35738032Speter				if (tTd(13, 30))
35890792Sgshapiro					sm_dprintf("    ... hold\n");
35964562Sgshapiro				q->q_state = QS_QUEUEUP;
36090792Sgshapiro				expensive = true;
36138032Speter			}
36290792Sgshapiro			else if (QueueMode != QM_QUARANTINE &&
36390792Sgshapiro				 e->e_quarmsg != NULL)
36490792Sgshapiro			{
36590792Sgshapiro				if (tTd(13, 30))
36690792Sgshapiro					sm_dprintf("    ... quarantine: %s\n",
36790792Sgshapiro						   e->e_quarmsg);
36890792Sgshapiro				q->q_state = QS_QUEUEUP;
36990792Sgshapiro				expensive = true;
37090792Sgshapiro			}
37138032Speter			else
37238032Speter			{
37338032Speter				if (tTd(13, 30))
37490792Sgshapiro					sm_dprintf("    ... deliverable\n");
37590792Sgshapiro				somedeliveries = true;
37638032Speter			}
37738032Speter		}
37838032Speter
37938032Speter		if (owner != NULL && otherowners > 0)
38038032Speter		{
38138032Speter			/*
38238032Speter			**  Split this envelope into two.
38338032Speter			*/
38438032Speter
38590792Sgshapiro			ee = (ENVELOPE *) sm_rpool_malloc_x(e->e_rpool,
38690792Sgshapiro							    sizeof *ee);
38790792Sgshapiro			STRUCTCOPY(*e, *ee);
38864562Sgshapiro			ee->e_message = NULL;
38938032Speter			ee->e_id = NULL;
39064562Sgshapiro			assign_queueid(ee);
39138032Speter
39238032Speter			if (tTd(13, 1))
39390792Sgshapiro				sm_dprintf("sendall: split %s into %s, owner = \"%s\", otherowners = %d\n",
39490792Sgshapiro					   e->e_id, ee->e_id, owner,
39590792Sgshapiro					   otherowners);
39638032Speter
39790792Sgshapiro			ee->e_header = copyheader(e->e_header, ee->e_rpool);
39890792Sgshapiro			ee->e_sendqueue = copyqueue(e->e_sendqueue,
39990792Sgshapiro						    ee->e_rpool);
40090792Sgshapiro			ee->e_errorqueue = copyqueue(e->e_errorqueue,
40190792Sgshapiro						     ee->e_rpool);
40238032Speter			ee->e_flags = e->e_flags & ~(EF_INQUEUE|EF_CLRQUEUE|EF_FATALERRS|EF_SENDRECEIPT|EF_RET_PARAM);
40338032Speter			ee->e_flags |= EF_NORECEIPT;
40490792Sgshapiro			setsender(owner, ee, NULL, '\0', true);
40538032Speter			if (tTd(13, 5))
40638032Speter			{
40790792Sgshapiro				sm_dprintf("sendall(split): QS_SENDER ");
408132943Sgshapiro				printaddr(sm_debug_file(), &ee->e_from, false);
40938032Speter			}
41064562Sgshapiro			ee->e_from.q_state = QS_SENDER;
41138032Speter			ee->e_dfp = NULL;
41264562Sgshapiro			ee->e_lockfp = NULL;
41338032Speter			ee->e_xfp = NULL;
41490792Sgshapiro			ee->e_qgrp = e->e_qgrp;
41590792Sgshapiro			ee->e_qdir = e->e_qdir;
41638032Speter			ee->e_errormode = EM_MAIL;
41738032Speter			ee->e_sibling = splitenv;
41864562Sgshapiro			ee->e_statmsg = NULL;
41990792Sgshapiro			if (e->e_quarmsg != NULL)
42090792Sgshapiro				ee->e_quarmsg = sm_rpool_strdup_x(ee->e_rpool,
42190792Sgshapiro								  e->e_quarmsg);
42238032Speter			splitenv = ee;
42338032Speter
42438032Speter			for (q = e->e_sendqueue; q != NULL; q = q->q_next)
42538032Speter			{
42638032Speter				if (q->q_owner == owner)
42738032Speter				{
42864562Sgshapiro					q->q_state = QS_CLONED;
42938032Speter					if (tTd(13, 6))
43090792Sgshapiro						sm_dprintf("\t... stripping %s from original envelope\n",
43190792Sgshapiro							   q->q_paddr);
43238032Speter				}
43338032Speter			}
43438032Speter			for (q = ee->e_sendqueue; q != NULL; q = q->q_next)
43538032Speter			{
43638032Speter				if (q->q_owner != owner)
43738032Speter				{
43864562Sgshapiro					q->q_state = QS_CLONED;
43938032Speter					if (tTd(13, 6))
44090792Sgshapiro						sm_dprintf("\t... dropping %s from cloned envelope\n",
44190792Sgshapiro							   q->q_paddr);
44238032Speter				}
44338032Speter				else
44438032Speter				{
44538032Speter					/* clear DSN parameters */
44638032Speter					q->q_flags &= ~(QHASNOTIFY|Q_PINGFLAGS);
44738032Speter					q->q_flags |= DefaultNotify & ~QPINGONSUCCESS;
44838032Speter					if (tTd(13, 6))
44990792Sgshapiro						sm_dprintf("\t... moving %s to cloned envelope\n",
45090792Sgshapiro							   q->q_paddr);
45138032Speter				}
45238032Speter			}
45338032Speter
45438032Speter			if (mode != SM_VERIFY && bitset(EF_HAS_DF, e->e_flags))
45590792Sgshapiro				dup_queue_file(e, ee, DATAFL_LETTER);
45664562Sgshapiro
45764562Sgshapiro			/*
45864562Sgshapiro			**  Give the split envelope access to the parent
45964562Sgshapiro			**  transcript file for errors obtained while
46064562Sgshapiro			**  processing the recipients (done before the
46164562Sgshapiro			**  envelope splitting).
46264562Sgshapiro			*/
46364562Sgshapiro
46464562Sgshapiro			if (e->e_xfp != NULL)
46590792Sgshapiro				ee->e_xfp = sm_io_dup(e->e_xfp);
46664562Sgshapiro
46764562Sgshapiro			/* failed to dup e->e_xfp, start a new transcript */
46864562Sgshapiro			if (ee->e_xfp == NULL)
46964562Sgshapiro				openxscript(ee);
47064562Sgshapiro
47142575Speter			if (mode != SM_VERIFY && LogLevel > 4)
47290792Sgshapiro				sm_syslog(LOG_INFO, e->e_id,
47390792Sgshapiro					  "%s: clone: owner=%s",
47490792Sgshapiro					  ee->e_id, owner);
47538032Speter		}
47638032Speter	}
47738032Speter
47838032Speter	if (owner != NULL)
47938032Speter	{
48090792Sgshapiro		setsender(owner, e, NULL, '\0', true);
48138032Speter		if (tTd(13, 5))
48238032Speter		{
48390792Sgshapiro			sm_dprintf("sendall(owner): QS_SENDER ");
484132943Sgshapiro			printaddr(sm_debug_file(), &e->e_from, false);
48538032Speter		}
48664562Sgshapiro		e->e_from.q_state = QS_SENDER;
48738032Speter		e->e_errormode = EM_MAIL;
48838032Speter		e->e_flags |= EF_NORECEIPT;
48938032Speter		e->e_flags &= ~EF_FATALERRS;
49038032Speter	}
49138032Speter
49238032Speter	/* if nothing to be delivered, just queue up everything */
49390792Sgshapiro	if (!somedeliveries && !WILL_BE_QUEUED(mode) &&
49438032Speter	    mode != SM_VERIFY)
49538032Speter	{
49690792Sgshapiro		time_t now;
49771345Sgshapiro
49838032Speter		if (tTd(13, 29))
49990792Sgshapiro			sm_dprintf("No deliveries: auto-queuing\n");
50038032Speter		mode = SM_QUEUE;
50190792Sgshapiro		now = curtime();
50238032Speter
50338032Speter		/* treat this as a delivery in terms of counting tries */
50471345Sgshapiro		e->e_dtime = now;
50538032Speter		if (!expensive)
50638032Speter			e->e_ntries++;
50738032Speter		for (ee = splitenv; ee != NULL; ee = ee->e_sibling)
50838032Speter		{
50971345Sgshapiro			ee->e_dtime = now;
51038032Speter			if (!expensive)
51138032Speter				ee->e_ntries++;
51238032Speter		}
51338032Speter	}
51438032Speter
51590792Sgshapiro	if ((WILL_BE_QUEUED(mode) || mode == SM_FORK ||
516132943Sgshapiro	     (mode != SM_VERIFY &&
517132943Sgshapiro	      (SuperSafe == SAFE_REALLY ||
518132943Sgshapiro	       SuperSafe == SAFE_REALLY_POSTMILTER))) &&
51938032Speter	    (!bitset(EF_INQUEUE, e->e_flags) || splitenv != NULL))
52038032Speter	{
52190792Sgshapiro		bool msync;
52290792Sgshapiro
52366494Sgshapiro		/*
52466494Sgshapiro		**  Be sure everything is instantiated in the queue.
52566494Sgshapiro		**  Split envelopes first in case the machine crashes.
52666494Sgshapiro		**  If the original were done first, we may lose
52766494Sgshapiro		**  recipients.
52866494Sgshapiro		*/
52966494Sgshapiro
53090792Sgshapiro#if !HASFLOCK
53190792Sgshapiro		msync = false;
53290792Sgshapiro#else /* !HASFLOCK */
53390792Sgshapiro		msync = mode == SM_FORK;
53490792Sgshapiro#endif /* !HASFLOCK */
53590792Sgshapiro
53638032Speter		for (ee = splitenv; ee != NULL; ee = ee->e_sibling)
53790792Sgshapiro			queueup(ee, WILL_BE_QUEUED(mode), msync);
53890792Sgshapiro		queueup(e, WILL_BE_QUEUED(mode), msync);
53938032Speter	}
54038032Speter
54138032Speter	if (tTd(62, 10))
54238032Speter		checkfds("after envelope splitting");
54338032Speter
54438032Speter	/*
54538032Speter	**  If we belong in background, fork now.
54638032Speter	*/
54738032Speter
54838032Speter	if (tTd(13, 20))
54938032Speter	{
55090792Sgshapiro		sm_dprintf("sendall: final mode = %c\n", mode);
55138032Speter		if (tTd(13, 21))
55238032Speter		{
55390792Sgshapiro			sm_dprintf("\n================ Final Send Queue(s) =====================\n");
55490792Sgshapiro			sm_dprintf("\n  *** Envelope %s, e_from=%s ***\n",
55590792Sgshapiro				   e->e_id, e->e_from.q_paddr);
556132943Sgshapiro			printaddr(sm_debug_file(), e->e_sendqueue, true);
55738032Speter			for (ee = splitenv; ee != NULL; ee = ee->e_sibling)
55838032Speter			{
55990792Sgshapiro				sm_dprintf("\n  *** Envelope %s, e_from=%s ***\n",
56090792Sgshapiro					   ee->e_id, ee->e_from.q_paddr);
561132943Sgshapiro				printaddr(sm_debug_file(), ee->e_sendqueue, true);
56238032Speter			}
56390792Sgshapiro			sm_dprintf("==========================================================\n\n");
56438032Speter		}
56538032Speter	}
56638032Speter	switch (mode)
56738032Speter	{
56838032Speter	  case SM_VERIFY:
56938032Speter		Verbose = 2;
57038032Speter		break;
57138032Speter
57238032Speter	  case SM_QUEUE:
57338032Speter	  case SM_DEFER:
57464562Sgshapiro#if HASFLOCK
57538032Speter  queueonly:
57664562Sgshapiro#endif /* HASFLOCK */
57738032Speter		if (e->e_nrcpts > 0)
57838032Speter			e->e_flags |= EF_INQUEUE;
57990792Sgshapiro		dropenvelope(e, splitenv != NULL, true);
58038032Speter		for (ee = splitenv; ee != NULL; ee = ee->e_sibling)
58138032Speter		{
58238032Speter			if (ee->e_nrcpts > 0)
58338032Speter				ee->e_flags |= EF_INQUEUE;
58490792Sgshapiro			dropenvelope(ee, false, true);
58538032Speter		}
58638032Speter		return;
58738032Speter
58838032Speter	  case SM_FORK:
58938032Speter		if (e->e_xfp != NULL)
59090792Sgshapiro			(void) sm_io_flush(e->e_xfp, SM_TIME_DEFAULT);
59138032Speter
59264562Sgshapiro#if !HASFLOCK
59338032Speter		/*
59438032Speter		**  Since fcntl locking has the interesting semantic that
59538032Speter		**  the lock is owned by a process, not by an open file
59638032Speter		**  descriptor, we have to flush this to the queue, and
59738032Speter		**  then restart from scratch in the child.
59838032Speter		*/
59938032Speter
60038032Speter		{
60138032Speter			/* save id for future use */
60238032Speter			char *qid = e->e_id;
60338032Speter
60438032Speter			/* now drop the envelope in the parent */
60538032Speter			e->e_flags |= EF_INQUEUE;
60690792Sgshapiro			dropenvelope(e, splitenv != NULL, false);
60738032Speter
60838032Speter			/* arrange to reacquire lock after fork */
60938032Speter			e->e_id = qid;
61038032Speter		}
61138032Speter
61238032Speter		for (ee = splitenv; ee != NULL; ee = ee->e_sibling)
61338032Speter		{
61438032Speter			/* save id for future use */
61538032Speter			char *qid = ee->e_id;
61638032Speter
61738032Speter			/* drop envelope in parent */
61838032Speter			ee->e_flags |= EF_INQUEUE;
61990792Sgshapiro			dropenvelope(ee, false, false);
62038032Speter
62138032Speter			/* and save qid for reacquisition */
62238032Speter			ee->e_id = qid;
62338032Speter		}
624132943Sgshapiro
62564562Sgshapiro#endif /* !HASFLOCK */
62638032Speter
62764562Sgshapiro		/*
62864562Sgshapiro		**  Since the delivery may happen in a child and the parent
62964562Sgshapiro		**  does not wait, the parent may close the maps thereby
63064562Sgshapiro		**  removing any shared memory used by the map.  Therefore,
63164562Sgshapiro		**  close the maps now so the child will dynamically open
63264562Sgshapiro		**  them if necessary.
63364562Sgshapiro		*/
63464562Sgshapiro
63590792Sgshapiro		closemaps(false);
63664562Sgshapiro
63738032Speter		pid = fork();
63838032Speter		if (pid < 0)
63938032Speter		{
64064562Sgshapiro			syserr("deliver: fork 1");
64164562Sgshapiro#if HASFLOCK
64238032Speter			goto queueonly;
64364562Sgshapiro#else /* HASFLOCK */
64438032Speter			e->e_id = NULL;
64538032Speter			for (ee = splitenv; ee != NULL; ee = ee->e_sibling)
64638032Speter				ee->e_id = NULL;
64738032Speter			return;
64864562Sgshapiro#endif /* HASFLOCK */
64938032Speter		}
65038032Speter		else if (pid > 0)
65138032Speter		{
65264562Sgshapiro#if HASFLOCK
65338032Speter			/* be sure we leave the temp files to our child */
65438032Speter			/* close any random open files in the envelope */
65538032Speter			closexscript(e);
65638032Speter			if (e->e_dfp != NULL)
65790792Sgshapiro				(void) sm_io_close(e->e_dfp, SM_TIME_DEFAULT);
65838032Speter			e->e_dfp = NULL;
65938032Speter			e->e_flags &= ~EF_HAS_DF;
66038032Speter
66138032Speter			/* can't call unlockqueue to avoid unlink of xfp */
66238032Speter			if (e->e_lockfp != NULL)
66390792Sgshapiro				(void) sm_io_close(e->e_lockfp, SM_TIME_DEFAULT);
66464562Sgshapiro			else
66564562Sgshapiro				syserr("%s: sendall: null lockfp", e->e_id);
66638032Speter			e->e_lockfp = NULL;
66764562Sgshapiro#endif /* HASFLOCK */
66838032Speter
66938032Speter			/* make sure the parent doesn't own the envelope */
67038032Speter			e->e_id = NULL;
67138032Speter
67290792Sgshapiro#if USE_DOUBLE_FORK
67338032Speter			/* catch intermediate zombie */
67438032Speter			(void) waitfor(pid);
67590792Sgshapiro#endif /* USE_DOUBLE_FORK */
67638032Speter			return;
67738032Speter		}
67838032Speter
67977349Sgshapiro		/* Reset global flags */
68077349Sgshapiro		RestartRequest = NULL;
68190792Sgshapiro		RestartWorkGroup = false;
68277349Sgshapiro		ShutdownRequest = NULL;
68377349Sgshapiro		PendingSignal = 0;
68477349Sgshapiro
68566494Sgshapiro		/*
68690792Sgshapiro		**  Initialize exception stack and default exception
68790792Sgshapiro		**  handler for child process.
68890792Sgshapiro		*/
68990792Sgshapiro
69090792Sgshapiro		sm_exc_newthread(fatal_error);
69190792Sgshapiro
69290792Sgshapiro		/*
69366494Sgshapiro		**  Since we have accepted responsbility for the message,
69466494Sgshapiro		**  change the SIGTERM handler.  intsig() (the old handler)
69566494Sgshapiro		**  would remove the envelope if this was a command line
69666494Sgshapiro		**  message submission.
69766494Sgshapiro		*/
69866494Sgshapiro
69990792Sgshapiro		(void) sm_signal(SIGTERM, SIG_DFL);
70066494Sgshapiro
70190792Sgshapiro#if USE_DOUBLE_FORK
70238032Speter		/* double fork to avoid zombies */
70338032Speter		pid = fork();
70438032Speter		if (pid > 0)
70538032Speter			exit(EX_OK);
70664562Sgshapiro		save_errno = errno;
70790792Sgshapiro#endif /* USE_DOUBLE_FORK */
70838032Speter
70990792Sgshapiro		CurrentPid = getpid();
71090792Sgshapiro
71138032Speter		/* be sure we are immune from the terminal */
71238032Speter		disconnect(2, e);
71364562Sgshapiro		clearstats();
71438032Speter
71538032Speter		/* prevent parent from waiting if there was an error */
71638032Speter		if (pid < 0)
71738032Speter		{
71864562Sgshapiro			errno = save_errno;
71964562Sgshapiro			syserr("deliver: fork 2");
72064562Sgshapiro#if HASFLOCK
72138032Speter			e->e_flags |= EF_INQUEUE;
72264562Sgshapiro#else /* HASFLOCK */
72338032Speter			e->e_id = NULL;
72464562Sgshapiro#endif /* HASFLOCK */
72590792Sgshapiro			finis(true, true, ExitStat);
72638032Speter		}
72738032Speter
72838032Speter		/* be sure to give error messages in child */
72990792Sgshapiro		QuickAbort = false;
73038032Speter
73138032Speter		/*
73238032Speter		**  Close any cached connections.
73338032Speter		**
73438032Speter		**	We don't send the QUIT protocol because the parent
73538032Speter		**	still knows about the connection.
73638032Speter		**
73738032Speter		**	This should only happen when delivering an error
73838032Speter		**	message.
73938032Speter		*/
74038032Speter
74190792Sgshapiro		mci_flush(false, NULL);
74238032Speter
74364562Sgshapiro#if HASFLOCK
74438032Speter		break;
74564562Sgshapiro#else /* HASFLOCK */
74638032Speter
74738032Speter		/*
74838032Speter		**  Now reacquire and run the various queue files.
74938032Speter		*/
75038032Speter
75138032Speter		for (ee = splitenv; ee != NULL; ee = ee->e_sibling)
75238032Speter		{
75338032Speter			ENVELOPE *sibling = ee->e_sibling;
75438032Speter
75590792Sgshapiro			(void) dowork(ee->e_qgrp, ee->e_qdir, ee->e_id,
75690792Sgshapiro				      false, false, ee);
75738032Speter			ee->e_sibling = sibling;
75838032Speter		}
75990792Sgshapiro		(void) dowork(e->e_qgrp, e->e_qdir, e->e_id,
76090792Sgshapiro			      false, false, e);
76190792Sgshapiro		finis(true, true, ExitStat);
76264562Sgshapiro#endif /* HASFLOCK */
76338032Speter	}
76438032Speter
76538032Speter	sendenvelope(e, mode);
76690792Sgshapiro	dropenvelope(e, true, true);
76738032Speter	for (ee = splitenv; ee != NULL; ee = ee->e_sibling)
76838032Speter	{
76938032Speter		CurEnv = ee;
77038032Speter		if (mode != SM_VERIFY)
77138032Speter			openxscript(ee);
77238032Speter		sendenvelope(ee, mode);
77390792Sgshapiro		dropenvelope(ee, true, true);
77438032Speter	}
77538032Speter	CurEnv = e;
77638032Speter
77738032Speter	Verbose = oldverbose;
77838032Speter	if (mode == SM_FORK)
77990792Sgshapiro		finis(true, true, ExitStat);
78038032Speter}
78138032Speter
78264562Sgshapirostatic void
78338032Spetersendenvelope(e, mode)
78438032Speter	register ENVELOPE *e;
78538032Speter	int mode;
78638032Speter{
78738032Speter	register ADDRESS *q;
78838032Speter	bool didany;
78938032Speter
79038032Speter	if (tTd(13, 10))
79190792Sgshapiro		sm_dprintf("sendenvelope(%s) e_flags=0x%lx\n",
79290792Sgshapiro			   e->e_id == NULL ? "[NOQUEUE]" : e->e_id,
79390792Sgshapiro			   e->e_flags);
79438032Speter	if (LogLevel > 80)
79538032Speter		sm_syslog(LOG_DEBUG, e->e_id,
79664562Sgshapiro			  "sendenvelope, flags=0x%lx",
79764562Sgshapiro			  e->e_flags);
79838032Speter
79938032Speter	/*
80038032Speter	**  If we have had global, fatal errors, don't bother sending
80138032Speter	**  the message at all if we are in SMTP mode.  Local errors
80238032Speter	**  (e.g., a single address failing) will still cause the other
80338032Speter	**  addresses to be sent.
80438032Speter	*/
80538032Speter
80638032Speter	if (bitset(EF_FATALERRS, e->e_flags) &&
80738032Speter	    (OpMode == MD_SMTP || OpMode == MD_DAEMON))
80838032Speter	{
80938032Speter		e->e_flags |= EF_CLRQUEUE;
81038032Speter		return;
81138032Speter	}
81238032Speter
81390792Sgshapiro	/*
81490792Sgshapiro	**  Don't attempt deliveries if we want to bounce now
81590792Sgshapiro	**  or if deliver-by time is exceeded.
81690792Sgshapiro	*/
81790792Sgshapiro
81864562Sgshapiro	if (!bitset(EF_RESPONSE, e->e_flags) &&
81990792Sgshapiro	    (TimeOuts.to_q_return[e->e_timeoutclass] == NOW ||
82090792Sgshapiro	     (IS_DLVR_RETURN(e) && e->e_deliver_by > 0 &&
82190792Sgshapiro	      curtime() > e->e_ctime + e->e_deliver_by)))
82264562Sgshapiro		return;
82364562Sgshapiro
82438032Speter	/*
82538032Speter	**  Run through the list and send everything.
82638032Speter	**
82738032Speter	**	Set EF_GLOBALERRS so that error messages during delivery
82838032Speter	**	result in returned mail.
82938032Speter	*/
83038032Speter
83138032Speter	e->e_nsent = 0;
83238032Speter	e->e_flags |= EF_GLOBALERRS;
83364562Sgshapiro
83490792Sgshapiro	macdefine(&e->e_macro, A_PERM, macid("{envid}"), e->e_envid);
83590792Sgshapiro	macdefine(&e->e_macro, A_PERM, macid("{bodytype}"), e->e_bodytype);
83690792Sgshapiro	didany = false;
83738032Speter
83890792Sgshapiro	if (!bitset(EF_SPLIT, e->e_flags))
83990792Sgshapiro	{
84090792Sgshapiro		ENVELOPE *oldsib;
84190792Sgshapiro		ENVELOPE *ee;
84290792Sgshapiro
84390792Sgshapiro		/*
84490792Sgshapiro		**  Save old sibling and set it to NULL to avoid
84590792Sgshapiro		**  queueing up the same envelopes again.
84690792Sgshapiro		**  This requires that envelopes in that list have
84790792Sgshapiro		**  been take care of before (or at some other place).
84890792Sgshapiro		*/
84990792Sgshapiro
85090792Sgshapiro		oldsib = e->e_sibling;
85190792Sgshapiro		e->e_sibling = NULL;
85290792Sgshapiro		if (!split_by_recipient(e) &&
85390792Sgshapiro		    bitset(EF_FATALERRS, e->e_flags))
85490792Sgshapiro		{
85590792Sgshapiro			if (OpMode == MD_SMTP || OpMode == MD_DAEMON)
85690792Sgshapiro				e->e_flags |= EF_CLRQUEUE;
85790792Sgshapiro			return;
85890792Sgshapiro		}
85990792Sgshapiro		for (ee = e->e_sibling; ee != NULL; ee = ee->e_sibling)
86090792Sgshapiro			queueup(ee, false, true);
86190792Sgshapiro
86290792Sgshapiro		/* clean up */
86390792Sgshapiro		for (ee = e->e_sibling; ee != NULL; ee = ee->e_sibling)
86490792Sgshapiro		{
86590792Sgshapiro			/* now unlock the job */
86690792Sgshapiro			closexscript(ee);
86790792Sgshapiro			unlockqueue(ee);
86890792Sgshapiro
86990792Sgshapiro			/* this envelope is marked unused */
87090792Sgshapiro			if (ee->e_dfp != NULL)
87190792Sgshapiro			{
87290792Sgshapiro				(void) sm_io_close(ee->e_dfp, SM_TIME_DEFAULT);
87390792Sgshapiro				ee->e_dfp = NULL;
87490792Sgshapiro			}
87590792Sgshapiro			ee->e_id = NULL;
87690792Sgshapiro			ee->e_flags &= ~EF_HAS_DF;
87790792Sgshapiro		}
87890792Sgshapiro		e->e_sibling = oldsib;
87990792Sgshapiro	}
88090792Sgshapiro
88138032Speter	/* now run through the queue */
88238032Speter	for (q = e->e_sendqueue; q != NULL; q = q->q_next)
88338032Speter	{
88438032Speter#if XDEBUG
88538032Speter		char wbuf[MAXNAME + 20];
88638032Speter
88790792Sgshapiro		(void) sm_snprintf(wbuf, sizeof wbuf, "sendall(%.*s)",
88890792Sgshapiro				   MAXNAME, q->q_paddr);
88938032Speter		checkfd012(wbuf);
89064562Sgshapiro#endif /* XDEBUG */
89138032Speter		if (mode == SM_VERIFY)
89238032Speter		{
89338032Speter			e->e_to = q->q_paddr;
89464562Sgshapiro			if (QS_IS_SENDABLE(q->q_state))
89538032Speter			{
89638032Speter				if (q->q_host != NULL && q->q_host[0] != '\0')
89738032Speter					message("deliverable: mailer %s, host %s, user %s",
89838032Speter						q->q_mailer->m_name,
89938032Speter						q->q_host,
90038032Speter						q->q_user);
90138032Speter				else
90238032Speter					message("deliverable: mailer %s, user %s",
90338032Speter						q->q_mailer->m_name,
90438032Speter						q->q_user);
90538032Speter			}
90638032Speter		}
90764562Sgshapiro		else if (QS_IS_OK(q->q_state))
90838032Speter		{
90938032Speter			/*
91038032Speter			**  Checkpoint the send list every few addresses
91138032Speter			*/
91238032Speter
91366494Sgshapiro			if (CheckpointInterval > 0 &&
91466494Sgshapiro			    e->e_nsent >= CheckpointInterval)
91538032Speter			{
91690792Sgshapiro				queueup(e, false, false);
91738032Speter				e->e_nsent = 0;
91838032Speter			}
91938032Speter			(void) deliver(e, q);
92090792Sgshapiro			didany = true;
92138032Speter		}
92238032Speter	}
92338032Speter	if (didany)
92438032Speter	{
92538032Speter		e->e_dtime = curtime();
92638032Speter		e->e_ntries++;
92738032Speter	}
92838032Speter
92938032Speter#if XDEBUG
93038032Speter	checkfd012("end of sendenvelope");
93164562Sgshapiro#endif /* XDEBUG */
93238032Speter}
93390792Sgshapiro
93490792Sgshapiro#if REQUIRES_DIR_FSYNC
93590792Sgshapiro/*
93690792Sgshapiro**  SYNC_DIR -- fsync a directory based on a filename
93790792Sgshapiro**
93890792Sgshapiro**	Parameters:
93990792Sgshapiro**		filename -- path of file
94090792Sgshapiro**		panic -- panic?
94190792Sgshapiro**
94290792Sgshapiro**	Returns:
94390792Sgshapiro**		none
94490792Sgshapiro*/
94590792Sgshapiro
94690792Sgshapirovoid
94790792Sgshapirosync_dir(filename, panic)
94890792Sgshapiro	char *filename;
94990792Sgshapiro	bool panic;
95090792Sgshapiro{
95190792Sgshapiro	int dirfd;
95290792Sgshapiro	char *dirp;
95390792Sgshapiro	char dir[MAXPATHLEN];
95490792Sgshapiro
955110560Sgshapiro	if (!RequiresDirfsync)
956110560Sgshapiro		return;
957110560Sgshapiro
95890792Sgshapiro	/* filesystems which require the directory be synced */
95990792Sgshapiro	dirp = strrchr(filename, '/');
96090792Sgshapiro	if (dirp != NULL)
96190792Sgshapiro	{
96290792Sgshapiro		if (sm_strlcpy(dir, filename, sizeof dir) >= sizeof dir)
96390792Sgshapiro			return;
96490792Sgshapiro		dir[dirp - filename] = '\0';
96590792Sgshapiro		dirp = dir;
96690792Sgshapiro	}
96790792Sgshapiro	else
96890792Sgshapiro		dirp = ".";
96990792Sgshapiro	dirfd = open(dirp, O_RDONLY, 0700);
97090792Sgshapiro	if (tTd(40,32))
97190792Sgshapiro		sm_syslog(LOG_INFO, NOQID, "sync_dir: %s: fsync(%d)",
97290792Sgshapiro			  dirp, dirfd);
97390792Sgshapiro	if (dirfd >= 0)
97490792Sgshapiro	{
97590792Sgshapiro		if (fsync(dirfd) < 0)
97690792Sgshapiro		{
97790792Sgshapiro			if (panic)
97890792Sgshapiro				syserr("!sync_dir: cannot fsync directory %s",
97990792Sgshapiro				       dirp);
98090792Sgshapiro			else if (LogLevel > 1)
98190792Sgshapiro				sm_syslog(LOG_ERR, NOQID,
98290792Sgshapiro					  "sync_dir: cannot fsync directory %s: %s",
98390792Sgshapiro					  dirp, sm_errstring(errno));
98490792Sgshapiro		}
98590792Sgshapiro		(void) close(dirfd);
98690792Sgshapiro	}
98790792Sgshapiro}
98890792Sgshapiro#endif /* REQUIRES_DIR_FSYNC */
98990792Sgshapiro/*
99038032Speter**  DUP_QUEUE_FILE -- duplicate a queue file into a split queue
99138032Speter**
99238032Speter**	Parameters:
99338032Speter**		e -- the existing envelope
99438032Speter**		ee -- the new envelope
99590792Sgshapiro**		type -- the queue file type (e.g., DATAFL_LETTER)
99638032Speter**
99738032Speter**	Returns:
99838032Speter**		none
99938032Speter*/
100038032Speter
100164562Sgshapirostatic void
100238032Speterdup_queue_file(e, ee, type)
100390792Sgshapiro	ENVELOPE *e, *ee;
100438032Speter	int type;
100538032Speter{
100664562Sgshapiro	char f1buf[MAXPATHLEN], f2buf[MAXPATHLEN];
100738032Speter
100838032Speter	ee->e_dfp = NULL;
100938032Speter	ee->e_xfp = NULL;
101064562Sgshapiro
101164562Sgshapiro	/*
101264562Sgshapiro	**  Make sure both are in the same directory.
101364562Sgshapiro	*/
101464562Sgshapiro
101590792Sgshapiro	(void) sm_strlcpy(f1buf, queuename(e, type), sizeof f1buf);
101690792Sgshapiro	(void) sm_strlcpy(f2buf, queuename(ee, type), sizeof f2buf);
1017102528Sgshapiro
1018102528Sgshapiro	/* Force the df to disk if it's not there yet */
1019102528Sgshapiro	if (type == DATAFL_LETTER && e->e_dfp != NULL &&
1020102528Sgshapiro	    sm_io_setinfo(e->e_dfp, SM_BF_COMMIT, NULL) < 0 &&
1021102528Sgshapiro	    errno != EINVAL)
1022102528Sgshapiro	{
1023102528Sgshapiro		syserr("!dup_queue_file: can't commit %s", f1buf);
1024102528Sgshapiro		/* NOTREACHED */
1025102528Sgshapiro	}
1026102528Sgshapiro
102738032Speter	if (link(f1buf, f2buf) < 0)
102838032Speter	{
102964562Sgshapiro		int save_errno = errno;
103038032Speter
103138032Speter		syserr("sendall: link(%s, %s)", f1buf, f2buf);
103264562Sgshapiro		if (save_errno == EEXIST)
103338032Speter		{
103438032Speter			if (unlink(f2buf) < 0)
103538032Speter			{
103638032Speter				syserr("!sendall: unlink(%s): permanent",
103790792Sgshapiro				       f2buf);
103864562Sgshapiro				/* NOTREACHED */
103938032Speter			}
104038032Speter			if (link(f1buf, f2buf) < 0)
104138032Speter			{
104238032Speter				syserr("!sendall: link(%s, %s): permanent",
104390792Sgshapiro				       f1buf, f2buf);
104464562Sgshapiro				/* NOTREACHED */
104538032Speter			}
104638032Speter		}
104738032Speter	}
104890792Sgshapiro	SYNC_DIR(f2buf, true);
104938032Speter}
105090792Sgshapiro/*
105138032Speter**  DOFORK -- do a fork, retrying a couple of times on failure.
105238032Speter**
105338032Speter**	This MUST be a macro, since after a vfork we are running
105438032Speter**	two processes on the same stack!!!
105538032Speter**
105638032Speter**	Parameters:
105738032Speter**		none.
105838032Speter**
105938032Speter**	Returns:
106038032Speter**		From a macro???  You've got to be kidding!
106138032Speter**
106238032Speter**	Side Effects:
106338032Speter**		Modifies the ==> LOCAL <== variable 'pid', leaving:
106438032Speter**			pid of child in parent, zero in child.
106538032Speter**			-1 on unrecoverable error.
106638032Speter**
106738032Speter**	Notes:
106838032Speter**		I'm awfully sorry this looks so awful.  That's
106938032Speter**		vfork for you.....
107038032Speter*/
107138032Speter
107264562Sgshapiro#define NFORKTRIES	5
107338032Speter
107464562Sgshapiro#ifndef FORK
107538032Speter# define FORK	fork
107664562Sgshapiro#endif /* ! FORK */
107738032Speter
107864562Sgshapiro#define DOFORK(fORKfN) \
107938032Speter{\
108038032Speter	register int i;\
108138032Speter\
108238032Speter	for (i = NFORKTRIES; --i >= 0; )\
108338032Speter	{\
108438032Speter		pid = fORKfN();\
108538032Speter		if (pid >= 0)\
108638032Speter			break;\
108738032Speter		if (i > 0)\
108864562Sgshapiro			(void) sleep((unsigned) NFORKTRIES - i);\
108938032Speter	}\
109038032Speter}
109190792Sgshapiro/*
109238032Speter**  DOFORK -- simple fork interface to DOFORK.
109338032Speter**
109438032Speter**	Parameters:
109538032Speter**		none.
109638032Speter**
109738032Speter**	Returns:
109838032Speter**		pid of child in parent.
109938032Speter**		zero in child.
110038032Speter**		-1 on error.
110138032Speter**
110238032Speter**	Side Effects:
110338032Speter**		returns twice, once in parent and once in child.
110438032Speter*/
110538032Speter
110677349Sgshapiropid_t
110738032Speterdofork()
110838032Speter{
110938032Speter	register pid_t pid = -1;
111038032Speter
111138032Speter	DOFORK(fork);
111264562Sgshapiro	return pid;
111338032Speter}
111490792Sgshapiro
111590792Sgshapiro/*
111690792Sgshapiro**  COLONCMP -- compare host-signatures up to first ':' or EOS
111790792Sgshapiro**
111890792Sgshapiro**	This takes two strings which happen to be host-signatures and
111990792Sgshapiro**	compares them. If the lowest preference portions of the MX-RR's
112090792Sgshapiro**	match (up to ':' or EOS, whichever is first), then we have
112190792Sgshapiro**	match. This is used for coattail-piggybacking messages during
112290792Sgshapiro**	message delivery.
112390792Sgshapiro**	If the signatures are the same up to the first ':' the remainder of
112490792Sgshapiro**	the signatures are then compared with a normal strcmp(). This saves
112590792Sgshapiro**	re-examining the first part of the signatures.
112690792Sgshapiro**
112790792Sgshapiro**	Parameters:
112890792Sgshapiro**		a - first host-signature
112990792Sgshapiro**		b - second host-signature
113090792Sgshapiro**
113190792Sgshapiro**	Returns:
113290792Sgshapiro**		HS_MATCH_NO -- no "match".
113390792Sgshapiro**		HS_MATCH_FIRST -- "match" for the first MX preference
113490792Sgshapiro**			(up to the first colon (':')).
113590792Sgshapiro**		HS_MATCH_FULL -- match for the entire MX record.
113690792Sgshapiro**
113790792Sgshapiro**	Side Effects:
113890792Sgshapiro**		none.
113990792Sgshapiro*/
114090792Sgshapiro
114190792Sgshapiro#define HS_MATCH_NO	0
114290792Sgshapiro#define HS_MATCH_FIRST	1
114390792Sgshapiro#define HS_MATCH_FULL	2
114490792Sgshapiro
114590792Sgshapirostatic int
114690792Sgshapirocoloncmp(a, b)
114790792Sgshapiro	register const char *a;
114890792Sgshapiro	register const char *b;
114990792Sgshapiro{
115090792Sgshapiro	int ret = HS_MATCH_NO;
115190792Sgshapiro	int braclev = 0;
115290792Sgshapiro
115390792Sgshapiro	while (*a == *b++)
115490792Sgshapiro	{
115590792Sgshapiro		/* Need to account for IPv6 bracketed addresses */
115690792Sgshapiro		if (*a == '[')
115790792Sgshapiro			braclev++;
1158112810Sgshapiro		else if (*a == ']' && braclev > 0)
115990792Sgshapiro			braclev--;
116090792Sgshapiro		else if (*a == ':' && braclev <= 0)
116190792Sgshapiro		{
116290792Sgshapiro			ret = HS_MATCH_FIRST;
116390792Sgshapiro			a++;
116490792Sgshapiro			break;
116590792Sgshapiro		}
116690792Sgshapiro		else if (*a == '\0')
116790792Sgshapiro			return HS_MATCH_FULL; /* a full match */
116890792Sgshapiro		a++;
116990792Sgshapiro	}
117090792Sgshapiro	if (ret == HS_MATCH_NO &&
117190792Sgshapiro	    braclev <= 0 &&
117290792Sgshapiro	    ((*a == '\0' && *(b - 1) == ':') ||
117390792Sgshapiro	     (*a == ':' && *(b - 1) == '\0')))
117490792Sgshapiro		return HS_MATCH_FIRST;
117590792Sgshapiro	if (ret == HS_MATCH_FIRST && strcmp(a, b) == 0)
117690792Sgshapiro		return HS_MATCH_FULL;
117790792Sgshapiro
117890792Sgshapiro	return ret;
117990792Sgshapiro}
1180132943Sgshapiro
118190792Sgshapiro/*
1182132943Sgshapiro**  SHOULD_TRY_FBSH -- Should try FallbackSmartHost?
1183132943Sgshapiro**
1184132943Sgshapiro**	Parameters:
1185132943Sgshapiro**		e -- envelope
1186132943Sgshapiro**		tried_fallbacksmarthost -- has been tried already? (in/out)
1187132943Sgshapiro**		hostbuf -- buffer for hostname (expand FallbackSmartHost) (out)
1188132943Sgshapiro**		hbsz -- size of hostbuf
1189132943Sgshapiro**		status -- current delivery status
1190132943Sgshapiro**
1191132943Sgshapiro**	Returns:
1192132943Sgshapiro**		true iff FallbackSmartHost should be tried.
1193132943Sgshapiro*/
1194132943Sgshapiro
1195132943Sgshapirostatic bool
1196132943Sgshapiroshould_try_fbsh(e, tried_fallbacksmarthost, hostbuf, hbsz, status)
1197132943Sgshapiro	ENVELOPE *e;
1198132943Sgshapiro	bool *tried_fallbacksmarthost;
1199132943Sgshapiro	char *hostbuf;
1200132943Sgshapiro	size_t hbsz;
1201132943Sgshapiro	int status;
1202132943Sgshapiro{
1203132943Sgshapiro	/*
1204132943Sgshapiro	**  If the host was not found and a FallbackSmartHost is defined
1205132943Sgshapiro	**  (and we have not yet tried it), then make one last try with
1206132943Sgshapiro	**  it as the host.
1207132943Sgshapiro	*/
1208132943Sgshapiro
1209132943Sgshapiro	if (status == EX_NOHOST && FallbackSmartHost != NULL &&
1210132943Sgshapiro	    !*tried_fallbacksmarthost)
1211132943Sgshapiro	{
1212132943Sgshapiro		*tried_fallbacksmarthost = true;
1213132943Sgshapiro		expand(FallbackSmartHost, hostbuf, hbsz, e);
1214132943Sgshapiro		if (!wordinclass(hostbuf, 'w'))
1215132943Sgshapiro		{
1216132943Sgshapiro			if (tTd(11, 1))
1217132943Sgshapiro				sm_dprintf("one last try with FallbackSmartHost %s\n",
1218132943Sgshapiro					   hostbuf);
1219132943Sgshapiro			return true;
1220132943Sgshapiro		}
1221132943Sgshapiro	}
1222132943Sgshapiro	return false;
1223132943Sgshapiro}
1224132943Sgshapiro/*
122538032Speter**  DELIVER -- Deliver a message to a list of addresses.
122638032Speter**
122738032Speter**	This routine delivers to everyone on the same host as the
122838032Speter**	user on the head of the list.  It is clever about mailers
122938032Speter**	that don't handle multiple users.  It is NOT guaranteed
123038032Speter**	that it will deliver to all these addresses however -- so
123138032Speter**	deliver should be called once for each address on the
123238032Speter**	list.
123390792Sgshapiro**	Deliver tries to be as opportunistic as possible about piggybacking
123490792Sgshapiro**	messages. Some definitions to make understanding easier follow below.
123590792Sgshapiro**	Piggybacking occurs when an existing connection to a mail host can
123690792Sgshapiro**	be used to send the same message to more than one recipient at the
123790792Sgshapiro**	same time. So "no piggybacking" means one message for one recipient
123890792Sgshapiro**	per connection. "Intentional piggybacking" happens when the
123990792Sgshapiro**	recipients' host address (not the mail host address) is used to
124090792Sgshapiro**	attempt piggybacking. Recipients with the same host address
124190792Sgshapiro**	have the same mail host. "Coincidental piggybacking" relies on
124290792Sgshapiro**	piggybacking based on all the mail host addresses in the MX-RR. This
124390792Sgshapiro**	is "coincidental" in the fact it could not be predicted until the
124490792Sgshapiro**	MX Resource Records for the hosts were obtained and examined. For
124590792Sgshapiro**	example (preference order and equivalence is important, not values):
124690792Sgshapiro**		domain1 IN MX 10 mxhost-A
124790792Sgshapiro**			IN MX 20 mxhost-B
124890792Sgshapiro**		domain2 IN MX  4 mxhost-A
124990792Sgshapiro**			IN MX  8 mxhost-B
125090792Sgshapiro**	Domain1 and domain2 can piggyback the same message to mxhost-A or
125190792Sgshapiro**	mxhost-B (if mxhost-A cannot be reached).
125290792Sgshapiro**	"Coattail piggybacking" relaxes the strictness of "coincidental
125390792Sgshapiro**	piggybacking" in the hope that most significant (lowest value)
125490792Sgshapiro**	MX preference host(s) can create more piggybacking. For example
125590792Sgshapiro**	(again, preference order and equivalence is important, not values):
125690792Sgshapiro**		domain3 IN MX 100 mxhost-C
125790792Sgshapiro**			IN MX 100 mxhost-D
125890792Sgshapiro**			IN MX 200 mxhost-E
125990792Sgshapiro**		domain4 IN MX  50 mxhost-C
126090792Sgshapiro**			IN MX  50 mxhost-D
126190792Sgshapiro**			IN MX  80 mxhost-F
126290792Sgshapiro**	A message for domain3 and domain4 can piggyback to mxhost-C if mxhost-C
126390792Sgshapiro**	is available. Same with mxhost-D because in both RR's the preference
126490792Sgshapiro**	value is the same as mxhost-C, respectively.
126590792Sgshapiro**	So deliver attempts coattail piggybacking when possible. If the
126690792Sgshapiro**	first MX preference level hosts cannot be used then the piggybacking
126790792Sgshapiro**	reverts to coincidental piggybacking. Using the above example you
126890792Sgshapiro**	cannot deliver to mxhost-F for domain3 regardless of preference value.
126990792Sgshapiro**	("Coattail" from "riding on the coattails of your predecessor" meaning
127090792Sgshapiro**	gaining benefit from a predecessor effort with no or little addition
127190792Sgshapiro**	effort. The predecessor here being the preceding MX RR).
127238032Speter**
127338032Speter**	Parameters:
127438032Speter**		e -- the envelope to deliver.
127538032Speter**		firstto -- head of the address list to deliver to.
127638032Speter**
127738032Speter**	Returns:
127838032Speter**		zero -- successfully delivered.
127938032Speter**		else -- some failure, see ExitStat for more info.
128038032Speter**
128138032Speter**	Side Effects:
128238032Speter**		The standard input is passed off to someone.
128338032Speter*/
128438032Speter
128564562Sgshapirostatic int
128638032Speterdeliver(e, firstto)
128738032Speter	register ENVELOPE *e;
128838032Speter	ADDRESS *firstto;
128938032Speter{
129038032Speter	char *host;			/* host being sent to */
129138032Speter	char *user;			/* user being sent to */
129238032Speter	char **pvp;
129338032Speter	register char **mvp;
129438032Speter	register char *p;
129538032Speter	register MAILER *m;		/* mailer for this recipient */
129638032Speter	ADDRESS *volatile ctladdr;
129790792Sgshapiro#if HASSETUSERCONTEXT
129838032Speter	ADDRESS *volatile contextaddr = NULL;
129990792Sgshapiro#endif /* HASSETUSERCONTEXT */
130038032Speter	register MCI *volatile mci;
130190792Sgshapiro	register ADDRESS *SM_NONVOLATILE to = firstto;
130290792Sgshapiro	volatile bool clever = false;	/* running user smtp to this mailer */
130338032Speter	ADDRESS *volatile tochain = NULL; /* users chain in this mailer call */
130438032Speter	int rcode;			/* response code */
130590792Sgshapiro	SM_NONVOLATILE int lmtp_rcode = EX_OK;
130690792Sgshapiro	SM_NONVOLATILE int nummxhosts = 0; /* number of MX hosts available */
130790792Sgshapiro	SM_NONVOLATILE int hostnum = 0;	/* current MX host index */
130838032Speter	char *firstsig;			/* signature of firstto */
130990792Sgshapiro	volatile pid_t pid = -1;
131038032Speter	char *volatile curhost;
131190792Sgshapiro	SM_NONVOLATILE unsigned short port = 0;
131290792Sgshapiro	SM_NONVOLATILE time_t enough = 0;
131364562Sgshapiro#if NETUNIX
131490792Sgshapiro	char *SM_NONVOLATILE mux_path = NULL;	/* path to UNIX domain socket */
131564562Sgshapiro#endif /* NETUNIX */
131638032Speter	time_t xstart;
131738032Speter	bool suidwarn;
131838032Speter	bool anyok;			/* at least one address was OK */
131990792Sgshapiro	SM_NONVOLATILE bool goodmxfound = false; /* at least one MX was OK */
132064562Sgshapiro	bool ovr;
132190792Sgshapiro	bool quarantine;
132264562Sgshapiro	int strsize;
132364562Sgshapiro	int rcptcount;
132490792Sgshapiro	int ret;
132564562Sgshapiro	static int tobufsize = 0;
132664562Sgshapiro	static char *tobuf = NULL;
132790792Sgshapiro	char *rpath;	/* translated return path */
132838032Speter	int mpvect[2];
132938032Speter	int rpvect[2];
133064562Sgshapiro	char *mxhosts[MAXMXHOSTS + 1];
133164562Sgshapiro	char *pv[MAXPV + 1];
133238032Speter	char buf[MAXNAME + 1];
133398121Sgshapiro	char cbuf[MAXPATHLEN];
133438032Speter
133538032Speter	errno = 0;
133664562Sgshapiro	if (!QS_IS_OK(to->q_state))
133764562Sgshapiro		return 0;
133838032Speter
133938032Speter	suidwarn = geteuid() == 0;
134038032Speter
134138032Speter	m = to->q_mailer;
134238032Speter	host = to->q_host;
134338032Speter	CurEnv = e;			/* just in case */
134438032Speter	e->e_statmsg = NULL;
134538032Speter	SmtpError[0] = '\0';
134638032Speter	xstart = curtime();
134738032Speter
134838032Speter	if (tTd(10, 1))
134990792Sgshapiro		sm_dprintf("\n--deliver, id=%s, mailer=%s, host=`%s', first user=`%s'\n",
135038032Speter			e->e_id, m->m_name, host, to->q_user);
135138032Speter	if (tTd(10, 100))
135290792Sgshapiro		printopenfds(false);
135338032Speter
135438032Speter	/*
135590792Sgshapiro	**  Clear {client_*} macros if this is a bounce message to
135638032Speter	**  prevent rejection by check_compat ruleset.
135738032Speter	*/
135864562Sgshapiro
135938032Speter	if (bitset(EF_RESPONSE, e->e_flags))
136038032Speter	{
136190792Sgshapiro		macdefine(&e->e_macro, A_PERM, macid("{client_name}"), "");
1362132943Sgshapiro		macdefine(&e->e_macro, A_PERM, macid("{client_ptr}"), "");
136390792Sgshapiro		macdefine(&e->e_macro, A_PERM, macid("{client_addr}"), "");
136490792Sgshapiro		macdefine(&e->e_macro, A_PERM, macid("{client_port}"), "");
136590792Sgshapiro		macdefine(&e->e_macro, A_PERM, macid("{client_resolve}"), "");
136638032Speter	}
136764562Sgshapiro
136890792Sgshapiro	SM_TRY
136990792Sgshapiro	{
137090792Sgshapiro	ADDRESS *skip_back = NULL;
137190792Sgshapiro
137238032Speter	/*
137338032Speter	**  Do initial argv setup.
137438032Speter	**	Insert the mailer name.  Notice that $x expansion is
137538032Speter	**	NOT done on the mailer name.  Then, if the mailer has
137638032Speter	**	a picky -f flag, we insert it as appropriate.  This
137738032Speter	**	code does not check for 'pv' overflow; this places a
137838032Speter	**	manifest lower limit of 4 for MAXPV.
137938032Speter	**		The from address rewrite is expected to make
138038032Speter	**		the address relative to the other end.
138138032Speter	*/
138238032Speter
138338032Speter	/* rewrite from address, using rewriting rules */
138438032Speter	rcode = EX_OK;
138538032Speter	if (bitnset(M_UDBENVELOPE, e->e_from.q_mailer->m_flags))
138638032Speter		p = e->e_sender;
138738032Speter	else
138838032Speter		p = e->e_from.q_paddr;
138990792Sgshapiro	rpath = remotename(p, m, RF_SENDERADDR|RF_CANONICAL, &rcode, e);
139090792Sgshapiro	if (strlen(rpath) > MAXSHORTSTR)
139138032Speter	{
139290792Sgshapiro		rpath = shortenstring(rpath, MAXSHORTSTR);
139390792Sgshapiro
139490792Sgshapiro		/* avoid bogus errno */
139590792Sgshapiro		errno = 0;
139690792Sgshapiro		syserr("remotename: huge return path %s", rpath);
139738032Speter	}
139890792Sgshapiro	rpath = sm_rpool_strdup_x(e->e_rpool, rpath);
139990792Sgshapiro	macdefine(&e->e_macro, A_PERM, 'g', rpath);
140090792Sgshapiro	macdefine(&e->e_macro, A_PERM, 'h', host);
140138032Speter	Errors = 0;
140238032Speter	pvp = pv;
140338032Speter	*pvp++ = m->m_argv[0];
140438032Speter
1405132943Sgshapiro	/* ignore long term host status information if mailer flag W is set */
1406132943Sgshapiro	if (bitnset(M_NOHOSTSTAT, m->m_flags))
1407132943Sgshapiro		IgnoreHostStatus = true;
1408132943Sgshapiro
140938032Speter	/* insert -f or -r flag as appropriate */
141064562Sgshapiro	if (FromFlag &&
141164562Sgshapiro	    (bitnset(M_FOPT, m->m_flags) ||
141264562Sgshapiro	     bitnset(M_ROPT, m->m_flags)))
141338032Speter	{
141438032Speter		if (bitnset(M_FOPT, m->m_flags))
141538032Speter			*pvp++ = "-f";
141638032Speter		else
141738032Speter			*pvp++ = "-r";
141890792Sgshapiro		*pvp++ = rpath;
141938032Speter	}
142038032Speter
142138032Speter	/*
142238032Speter	**  Append the other fixed parts of the argv.  These run
142338032Speter	**  up to the first entry containing "$u".  There can only
142438032Speter	**  be one of these, and there are only a few more slots
142538032Speter	**  in the pv after it.
142638032Speter	*/
142738032Speter
142838032Speter	for (mvp = m->m_argv; (p = *++mvp) != NULL; )
142938032Speter	{
143038032Speter		/* can't use strchr here because of sign extension problems */
143138032Speter		while (*p != '\0')
143238032Speter		{
143338032Speter			if ((*p++ & 0377) == MACROEXPAND)
143438032Speter			{
143538032Speter				if (*p == 'u')
143638032Speter					break;
143738032Speter			}
143838032Speter		}
143938032Speter
144038032Speter		if (*p != '\0')
144138032Speter			break;
144238032Speter
144338032Speter		/* this entry is safe -- go ahead and process it */
144438032Speter		expand(*mvp, buf, sizeof buf, e);
144590792Sgshapiro		*pvp++ = sm_rpool_strdup_x(e->e_rpool, buf);
144638032Speter		if (pvp >= &pv[MAXPV - 3])
144738032Speter		{
144864562Sgshapiro			syserr("554 5.3.5 Too many parameters to %s before $u",
144964562Sgshapiro			       pv[0]);
145090792Sgshapiro			rcode = -1;
145190792Sgshapiro			goto cleanup;
145238032Speter		}
145338032Speter	}
145438032Speter
145538032Speter	/*
145638032Speter	**  If we have no substitution for the user name in the argument
145738032Speter	**  list, we know that we must supply the names otherwise -- and
145838032Speter	**  SMTP is the answer!!
145938032Speter	*/
146038032Speter
146138032Speter	if (*mvp == NULL)
146238032Speter	{
146373188Sgshapiro		/* running LMTP or SMTP */
146490792Sgshapiro		clever = true;
146538032Speter		*pvp = NULL;
146638032Speter	}
146773188Sgshapiro	else if (bitnset(M_LMTP, m->m_flags))
146873188Sgshapiro	{
146973188Sgshapiro		/* not running LMTP */
147073188Sgshapiro		sm_syslog(LOG_ERR, NULL,
147173188Sgshapiro			  "Warning: mailer %s: LMTP flag (F=z) turned off",
147273188Sgshapiro			  m->m_name);
147373188Sgshapiro		clrbitn(M_LMTP, m->m_flags);
147473188Sgshapiro	}
147538032Speter
147638032Speter	/*
147738032Speter	**  At this point *mvp points to the argument with $u.  We
147838032Speter	**  run through our address list and append all the addresses
147938032Speter	**  we can.  If we run out of space, do not fret!  We can
148038032Speter	**  always send another copy later.
148138032Speter	*/
148238032Speter
148364562Sgshapiro	e->e_to = NULL;
148464562Sgshapiro	strsize = 2;
148564562Sgshapiro	rcptcount = 0;
148690792Sgshapiro	ctladdr = NULL;
148790792Sgshapiro	if (firstto->q_signature == NULL)
148890792Sgshapiro		firstto->q_signature = hostsignature(firstto->q_mailer,
148990792Sgshapiro						     firstto->q_host);
149090792Sgshapiro	firstsig = firstto->q_signature;
149164562Sgshapiro
149238032Speter	for (; to != NULL; to = to->q_next)
149338032Speter	{
149438032Speter		/* avoid sending multiple recipients to dumb mailers */
149564562Sgshapiro		if (tochain != NULL && !bitnset(M_MUSER, m->m_flags))
149664562Sgshapiro			break;
149738032Speter
149838032Speter		/* if already sent or not for this host, don't send */
149990792Sgshapiro		if (!QS_IS_OK(to->q_state)) /* already sent; look at next */
150038032Speter			continue;
150138032Speter
150290792Sgshapiro		/*
150390792Sgshapiro		**  Must be same mailer to keep grouping rcpts.
150490792Sgshapiro		**  If mailers don't match: continue; sendqueue is not
150590792Sgshapiro		**  sorted by mailers, so don't break;
150690792Sgshapiro		*/
150790792Sgshapiro
150890792Sgshapiro		if (to->q_mailer != firstto->q_mailer)
150990792Sgshapiro			continue;
151090792Sgshapiro
151190792Sgshapiro		if (to->q_signature == NULL) /* for safety */
151290792Sgshapiro			to->q_signature = hostsignature(to->q_mailer,
151390792Sgshapiro							to->q_host);
151490792Sgshapiro
151590792Sgshapiro		/*
151690792Sgshapiro		**  This is for coincidental and tailcoat piggybacking messages
151790792Sgshapiro		**  to the same mail host. While the signatures are identical
151890792Sgshapiro		**  (that's the MX-RR's are identical) we can do coincidental
151990792Sgshapiro		**  piggybacking. We try hard for coattail piggybacking
152090792Sgshapiro		**  with the same mail host when the next recipient has the
152190792Sgshapiro		**  same host at lowest preference. It may be that this
152290792Sgshapiro		**  won't work out, so 'skip_back' is maintained if a backup
152390792Sgshapiro		**  to coincidental piggybacking or full signature must happen.
152490792Sgshapiro		*/
152590792Sgshapiro
152690792Sgshapiro		ret = firstto == to ? HS_MATCH_FULL :
152790792Sgshapiro				      coloncmp(to->q_signature, firstsig);
152890792Sgshapiro		if (ret == HS_MATCH_FULL)
152990792Sgshapiro			skip_back = to;
153090792Sgshapiro		else if (ret == HS_MATCH_NO)
153164562Sgshapiro			break;
153264562Sgshapiro
153390792Sgshapiro		if (!clever)
153490792Sgshapiro		{
153590792Sgshapiro			/* avoid overflowing tobuf */
153690792Sgshapiro			strsize += strlen(to->q_paddr) + 1;
153790792Sgshapiro			if (strsize > TOBUFSIZE)
153890792Sgshapiro				break;
153990792Sgshapiro		}
154090792Sgshapiro
154164562Sgshapiro		if (++rcptcount > to->q_mailer->m_maxrcpt)
154264562Sgshapiro			break;
154338032Speter
154438032Speter		if (tTd(10, 1))
154538032Speter		{
154690792Sgshapiro			sm_dprintf("\nsend to ");
1547132943Sgshapiro			printaddr(sm_debug_file(), to, false);
154838032Speter		}
154938032Speter
155038032Speter		/* compute effective uid/gid when sending */
155138032Speter		if (bitnset(M_RUNASRCPT, to->q_mailer->m_flags))
155290792Sgshapiro# if HASSETUSERCONTEXT
155338032Speter			contextaddr = ctladdr = getctladdr(to);
155490792Sgshapiro# else /* HASSETUSERCONTEXT */
155590792Sgshapiro			ctladdr = getctladdr(to);
155690792Sgshapiro# endif /* HASSETUSERCONTEXT */
155738032Speter
155838032Speter		if (tTd(10, 2))
155938032Speter		{
156090792Sgshapiro			sm_dprintf("ctladdr=");
1561132943Sgshapiro			printaddr(sm_debug_file(), ctladdr, false);
156238032Speter		}
156338032Speter
156438032Speter		user = to->q_user;
156538032Speter		e->e_to = to->q_paddr;
156638032Speter
156738032Speter		/*
156838032Speter		**  Check to see that these people are allowed to
156938032Speter		**  talk to each other.
157066494Sgshapiro		**  Check also for overflow of e_msgsize.
157138032Speter		*/
157238032Speter
157366494Sgshapiro		if (m->m_maxsize != 0 &&
157466494Sgshapiro		    (e->e_msgsize > m->m_maxsize || e->e_msgsize < 0))
157538032Speter		{
157638032Speter			e->e_flags |= EF_NO_BODY_RETN;
157738032Speter			if (bitnset(M_LOCALMAILER, to->q_mailer->m_flags))
157838032Speter				to->q_status = "5.2.3";
157938032Speter			else
158038032Speter				to->q_status = "5.3.4";
158190792Sgshapiro
158264562Sgshapiro			/* set to->q_rstatus = NULL; or to the following? */
158364562Sgshapiro			usrerrenh(to->q_status,
158464562Sgshapiro				  "552 Message is too large; %ld bytes max",
158564562Sgshapiro				  m->m_maxsize);
158690792Sgshapiro			markfailure(e, to, NULL, EX_UNAVAILABLE, false);
158764562Sgshapiro			giveresponse(EX_UNAVAILABLE, to->q_status, m,
158890792Sgshapiro				     NULL, ctladdr, xstart, e, to);
158938032Speter			continue;
159038032Speter		}
159173188Sgshapiro		SM_SET_H_ERRNO(0);
159290792Sgshapiro		ovr = true;
159338032Speter
159438032Speter		/* do config file checking of compatibility */
159590792Sgshapiro		quarantine = (e->e_quarmsg != NULL);
159664562Sgshapiro		rcode = rscheck("check_compat", e->e_from.q_paddr, to->q_paddr,
1597102528Sgshapiro				e, RSF_RMCOMM|RSF_COUNT, 3, NULL,
1598102528Sgshapiro				e->e_id);
159938032Speter		if (rcode == EX_OK)
160038032Speter		{
160142575Speter			/* do in-code checking if not discarding */
160242575Speter			if (!bitset(EF_DISCARD, e->e_flags))
160364562Sgshapiro			{
160442575Speter				rcode = checkcompat(to, e);
160590792Sgshapiro				ovr = false;
160664562Sgshapiro			}
160738032Speter		}
160838032Speter		if (rcode != EX_OK)
160938032Speter		{
161064562Sgshapiro			markfailure(e, to, NULL, rcode, ovr);
161164562Sgshapiro			giveresponse(rcode, to->q_status, m,
161290792Sgshapiro				     NULL, ctladdr, xstart, e, to);
161338032Speter			continue;
161438032Speter		}
161590792Sgshapiro		if (!quarantine && e->e_quarmsg != NULL)
161690792Sgshapiro		{
161790792Sgshapiro			/*
161890792Sgshapiro			**  check_compat or checkcompat() has tried
161990792Sgshapiro			**  to quarantine but that isn't supported.
162090792Sgshapiro			**  Revert the attempt.
162190792Sgshapiro			*/
162290792Sgshapiro
162390792Sgshapiro			e->e_quarmsg = NULL;
162490792Sgshapiro			macdefine(&e->e_macro, A_PERM,
162590792Sgshapiro				  macid("{quarantine}"), "");
162690792Sgshapiro		}
162742575Speter		if (bitset(EF_DISCARD, e->e_flags))
162842575Speter		{
162942575Speter			if (tTd(10, 5))
163042575Speter			{
163190792Sgshapiro				sm_dprintf("deliver: discarding recipient ");
1632132943Sgshapiro				printaddr(sm_debug_file(), to, false);
163342575Speter			}
163438032Speter
163564562Sgshapiro			/* pretend the message was sent */
163664562Sgshapiro			/* XXX should we log something here? */
163764562Sgshapiro			to->q_state = QS_DISCARDED;
163864562Sgshapiro
163942575Speter			/*
164042575Speter			**  Remove discard bit to prevent discard of
164164562Sgshapiro			**  future recipients.  This is safe because the
164264562Sgshapiro			**  true "global discard" has been handled before
164364562Sgshapiro			**  we get here.
164442575Speter			*/
164564562Sgshapiro
164642575Speter			e->e_flags &= ~EF_DISCARD;
164742575Speter			continue;
164842575Speter		}
164942575Speter
165038032Speter		/*
165138032Speter		**  Strip quote bits from names if the mailer is dumb
165238032Speter		**	about them.
165338032Speter		*/
165438032Speter
165538032Speter		if (bitnset(M_STRIPQ, m->m_flags))
165638032Speter		{
165738032Speter			stripquotes(user);
165838032Speter			stripquotes(host);
165938032Speter		}
1660132943Sgshapiro
1661110560Sgshapiro		/*
1662141858Sgshapiro		**  Strip all leading backslashes if requested and the
1663110560Sgshapiro		**  next character is alphanumerical (the latter can
1664110560Sgshapiro		**  probably relaxed a bit, see RFC2821).
1665110560Sgshapiro		*/
166638032Speter
1667110560Sgshapiro		if (bitnset(M_STRIPBACKSL, m->m_flags) && user[0] == '\\')
1668110560Sgshapiro			stripbackslash(user);
1669110560Sgshapiro
167038032Speter		/* hack attack -- delivermail compatibility */
167138032Speter		if (m == ProgMailer && *user == '|')
167238032Speter			user++;
167338032Speter
167438032Speter		/*
167538032Speter		**  If an error message has already been given, don't
167638032Speter		**	bother to send to this address.
167738032Speter		**
167838032Speter		**	>>>>>>>>>> This clause assumes that the local mailer
167938032Speter		**	>> NOTE >> cannot do any further aliasing; that
168038032Speter		**	>>>>>>>>>> function is subsumed by sendmail.
168138032Speter		*/
168238032Speter
168364562Sgshapiro		if (!QS_IS_OK(to->q_state))
168438032Speter			continue;
168538032Speter
168638032Speter		/*
168738032Speter		**  See if this user name is "special".
168838032Speter		**	If the user name has a slash in it, assume that this
168938032Speter		**	is a file -- send it off without further ado.  Note
169038032Speter		**	that this type of addresses is not processed along
169138032Speter		**	with the others, so we fudge on the To person.
169238032Speter		*/
169338032Speter
169438032Speter		if (strcmp(m->m_mailer, "[FILE]") == 0)
169538032Speter		{
169690792Sgshapiro			macdefine(&e->e_macro, A_PERM, 'u', user);
169738032Speter			p = to->q_home;
169838032Speter			if (p == NULL && ctladdr != NULL)
169938032Speter				p = ctladdr->q_home;
170090792Sgshapiro			macdefine(&e->e_macro, A_PERM, 'z', p);
170138032Speter			expand(m->m_argv[1], buf, sizeof buf, e);
170238032Speter			if (strlen(buf) > 0)
170338032Speter				rcode = mailfile(buf, m, ctladdr, SFF_CREAT, e);
170438032Speter			else
170538032Speter			{
170638032Speter				syserr("empty filename specification for mailer %s",
170738032Speter				       m->m_name);
170838032Speter				rcode = EX_CONFIG;
170938032Speter			}
171064562Sgshapiro			giveresponse(rcode, to->q_status, m, NULL,
171190792Sgshapiro				     ctladdr, xstart, e, to);
171290792Sgshapiro			markfailure(e, to, NULL, rcode, true);
171338032Speter			e->e_nsent++;
171438032Speter			if (rcode == EX_OK)
171538032Speter			{
171664562Sgshapiro				to->q_state = QS_SENT;
171738032Speter				if (bitnset(M_LOCALMAILER, m->m_flags) &&
171838032Speter				    bitset(QPINGONSUCCESS, to->q_flags))
171938032Speter				{
172038032Speter					to->q_flags |= QDELIVERED;
172138032Speter					to->q_status = "2.1.5";
172290792Sgshapiro					(void) sm_io_fprintf(e->e_xfp,
172390792Sgshapiro							     SM_TIME_DEFAULT,
172490792Sgshapiro							     "%s... Successfully delivered\n",
172590792Sgshapiro							     to->q_paddr);
172638032Speter				}
172738032Speter			}
172838032Speter			to->q_statdate = curtime();
172990792Sgshapiro			markstats(e, to, STATS_NORMAL);
173038032Speter			continue;
173138032Speter		}
173238032Speter
173338032Speter		/*
173438032Speter		**  Address is verified -- add this user to mailer
173538032Speter		**  argv, and add it to the print list of recipients.
173638032Speter		*/
173738032Speter
173838032Speter		/* link together the chain of recipients */
173938032Speter		to->q_tchain = tochain;
174038032Speter		tochain = to;
174164562Sgshapiro		e->e_to = "[CHAIN]";
174264562Sgshapiro
174390792Sgshapiro		macdefine(&e->e_macro, A_PERM, 'u', user);  /* to user */
174438032Speter		p = to->q_home;
174538032Speter		if (p == NULL && ctladdr != NULL)
174638032Speter			p = ctladdr->q_home;
174790792Sgshapiro		macdefine(&e->e_macro, A_PERM, 'z', p);  /* user's home */
174838032Speter
174964562Sgshapiro		/* set the ${dsn_notify} macro if applicable */
175064562Sgshapiro		if (bitset(QHASNOTIFY, to->q_flags))
175164562Sgshapiro		{
175264562Sgshapiro			char notify[MAXLINE];
175364562Sgshapiro
175464562Sgshapiro			notify[0] = '\0';
175564562Sgshapiro			if (bitset(QPINGONSUCCESS, to->q_flags))
175690792Sgshapiro				(void) sm_strlcat(notify, "SUCCESS,",
175790792Sgshapiro						  sizeof notify);
175864562Sgshapiro			if (bitset(QPINGONFAILURE, to->q_flags))
175990792Sgshapiro				(void) sm_strlcat(notify, "FAILURE,",
176090792Sgshapiro						  sizeof notify);
176164562Sgshapiro			if (bitset(QPINGONDELAY, to->q_flags))
176290792Sgshapiro				(void) sm_strlcat(notify, "DELAY,",
176390792Sgshapiro						  sizeof notify);
176464562Sgshapiro
176564562Sgshapiro			/* Set to NEVER or drop trailing comma */
176664562Sgshapiro			if (notify[0] == '\0')
176790792Sgshapiro				(void) sm_strlcat(notify, "NEVER",
176890792Sgshapiro						  sizeof notify);
176964562Sgshapiro			else
177064562Sgshapiro				notify[strlen(notify) - 1] = '\0';
177164562Sgshapiro
177290792Sgshapiro			macdefine(&e->e_macro, A_TEMP,
177390792Sgshapiro				macid("{dsn_notify}"), notify);
177464562Sgshapiro		}
177564562Sgshapiro		else
177690792Sgshapiro			macdefine(&e->e_macro, A_PERM,
177790792Sgshapiro				macid("{dsn_notify}"), NULL);
177864562Sgshapiro
177938032Speter		/*
178038032Speter		**  Expand out this user into argument list.
178138032Speter		*/
178238032Speter
178338032Speter		if (!clever)
178438032Speter		{
178538032Speter			expand(*mvp, buf, sizeof buf, e);
178690792Sgshapiro			*pvp++ = sm_rpool_strdup_x(e->e_rpool, buf);
178738032Speter			if (pvp >= &pv[MAXPV - 2])
178838032Speter			{
178938032Speter				/* allow some space for trailing parms */
179038032Speter				break;
179138032Speter			}
179238032Speter		}
179338032Speter	}
179438032Speter
179538032Speter	/* see if any addresses still exist */
179664562Sgshapiro	if (tochain == NULL)
179738032Speter	{
179890792Sgshapiro		rcode = 0;
179990792Sgshapiro		goto cleanup;
180038032Speter	}
180138032Speter
180238032Speter	/* print out messages as full list */
180390792Sgshapiro	strsize = 1;
180490792Sgshapiro	for (to = tochain; to != NULL; to = to->q_tchain)
180590792Sgshapiro		strsize += strlen(to->q_paddr) + 1;
180690792Sgshapiro	if (strsize < TOBUFSIZE)
180790792Sgshapiro		strsize = TOBUFSIZE;
180890792Sgshapiro	if (strsize > tobufsize)
180964562Sgshapiro	{
181090792Sgshapiro		SM_FREE_CLR(tobuf);
181190792Sgshapiro		tobuf = sm_pmalloc_x(strsize);
181290792Sgshapiro		tobufsize = strsize;
181364562Sgshapiro	}
181490792Sgshapiro	p = tobuf;
181590792Sgshapiro	*p = '\0';
181690792Sgshapiro	for (to = tochain; to != NULL; to = to->q_tchain)
181790792Sgshapiro	{
181890792Sgshapiro		(void) sm_strlcpyn(p, tobufsize - (p - tobuf), 2,
181990792Sgshapiro				   ",", to->q_paddr);
182090792Sgshapiro		p += strlen(p);
182190792Sgshapiro	}
182238032Speter	e->e_to = tobuf + 1;
182338032Speter
182438032Speter	/*
182538032Speter	**  Fill out any parameters after the $u parameter.
182638032Speter	*/
182738032Speter
182890792Sgshapiro	if (!clever)
182938032Speter	{
183090792Sgshapiro		while (*++mvp != NULL)
183190792Sgshapiro		{
183290792Sgshapiro			expand(*mvp, buf, sizeof buf, e);
183390792Sgshapiro			*pvp++ = sm_rpool_strdup_x(e->e_rpool, buf);
183490792Sgshapiro			if (pvp >= &pv[MAXPV])
183590792Sgshapiro				syserr("554 5.3.0 deliver: pv overflow after $u for %s",
183690792Sgshapiro				       pv[0]);
183790792Sgshapiro		}
183838032Speter	}
183938032Speter	*pvp++ = NULL;
184038032Speter
184138032Speter	/*
184238032Speter	**  Call the mailer.
184338032Speter	**	The argument vector gets built, pipes
184438032Speter	**	are created as necessary, and we fork & exec as
184538032Speter	**	appropriate.
184638032Speter	**	If we are running SMTP, we just need to clean up.
184738032Speter	*/
184838032Speter
184964562Sgshapiro	/* XXX this seems a bit wierd */
185038032Speter	if (ctladdr == NULL && m != ProgMailer && m != FileMailer &&
185138032Speter	    bitset(QGOODUID, e->e_from.q_flags))
185238032Speter		ctladdr = &e->e_from;
185338032Speter
185438032Speter#if NAMED_BIND
185538032Speter	if (ConfigLevel < 2)
185638032Speter		_res.options &= ~(RES_DEFNAMES | RES_DNSRCH);	/* XXX */
185764562Sgshapiro#endif /* NAMED_BIND */
185838032Speter
185938032Speter	if (tTd(11, 1))
186038032Speter	{
186190792Sgshapiro		sm_dprintf("openmailer:");
1862132943Sgshapiro		printav(sm_debug_file(), pv);
186338032Speter	}
186438032Speter	errno = 0;
186573188Sgshapiro	SM_SET_H_ERRNO(0);
186638032Speter	CurHostName = NULL;
186738032Speter
186838032Speter	/*
186938032Speter	**  Deal with the special case of mail handled through an IPC
187038032Speter	**  connection.
187138032Speter	**	In this case we don't actually fork.  We must be
187238032Speter	**	running SMTP for this to work.  We will return a
187338032Speter	**	zero pid to indicate that we are running IPC.
187438032Speter	**  We also handle a debug version that just talks to stdin/out.
187538032Speter	*/
187638032Speter
187738032Speter	curhost = NULL;
187838032Speter	SmtpPhase = NULL;
187938032Speter	mci = NULL;
188038032Speter
188138032Speter#if XDEBUG
188238032Speter	{
188338032Speter		char wbuf[MAXLINE];
188438032Speter
188538032Speter		/* make absolutely certain 0, 1, and 2 are in use */
188690792Sgshapiro		(void) sm_snprintf(wbuf, sizeof wbuf, "%s... openmailer(%s)",
188790792Sgshapiro				   shortenstring(e->e_to, MAXSHORTSTR),
188890792Sgshapiro				   m->m_name);
188938032Speter		checkfd012(wbuf);
189038032Speter	}
189164562Sgshapiro#endif /* XDEBUG */
189238032Speter
189338032Speter	/* check for 8-bit available */
189438032Speter	if (bitset(EF_HAS8BIT, e->e_flags) &&
189538032Speter	    bitnset(M_7BITS, m->m_flags) &&
189638032Speter	    (bitset(EF_DONT_MIME, e->e_flags) ||
189738032Speter	     !(bitset(MM_MIME8BIT, MimeMode) ||
189838032Speter	       (bitset(EF_IS_MIME, e->e_flags) &&
189964562Sgshapiro		bitset(MM_CVTMIME, MimeMode)))))
190038032Speter	{
190164562Sgshapiro		e->e_status = "5.6.3";
190264562Sgshapiro		usrerrenh(e->e_status,
190390792Sgshapiro			  "554 Cannot send 8-bit data to 7-bit destination");
190438032Speter		rcode = EX_DATAERR;
190538032Speter		goto give_up;
190638032Speter	}
190738032Speter
190838032Speter	if (tTd(62, 8))
190938032Speter		checkfds("before delivery");
191038032Speter
191138032Speter	/* check for Local Person Communication -- not for mortals!!! */
191238032Speter	if (strcmp(m->m_mailer, "[LPC]") == 0)
191338032Speter	{
191490792Sgshapiro		if (clever)
191590792Sgshapiro		{
191690792Sgshapiro			/* flush any expired connections */
191790792Sgshapiro			(void) mci_scan(NULL);
191890792Sgshapiro
191990792Sgshapiro			/* try to get a cached connection or just a slot */
192090792Sgshapiro			mci = mci_get(m->m_name, m);
192190792Sgshapiro			if (mci->mci_host == NULL)
192290792Sgshapiro				mci->mci_host = m->m_name;
192390792Sgshapiro			CurHostName = mci->mci_host;
192490792Sgshapiro			if (mci->mci_state != MCIS_CLOSED)
192590792Sgshapiro			{
192690792Sgshapiro				message("Using cached SMTP/LPC connection for %s...",
192790792Sgshapiro					m->m_name);
192890792Sgshapiro				mci->mci_deliveries++;
192990792Sgshapiro				goto do_transfer;
193090792Sgshapiro			}
193190792Sgshapiro		}
193290792Sgshapiro		else
193390792Sgshapiro		{
193490792Sgshapiro			mci = mci_new(e->e_rpool);
193590792Sgshapiro		}
193690792Sgshapiro		mci->mci_in = smioin;
193790792Sgshapiro		mci->mci_out = smioout;
193890792Sgshapiro		mci->mci_mailer = m;
193990792Sgshapiro		mci->mci_host = m->m_name;
194090792Sgshapiro		if (clever)
194190792Sgshapiro		{
194290792Sgshapiro			mci->mci_state = MCIS_OPENING;
194390792Sgshapiro			mci_cache(mci);
194490792Sgshapiro		}
194590792Sgshapiro		else
194690792Sgshapiro			mci->mci_state = MCIS_OPEN;
194738032Speter	}
194890792Sgshapiro	else if (strcmp(m->m_mailer, "[IPC]") == 0)
194938032Speter	{
195038032Speter		register int i;
195138032Speter
195238032Speter		if (pv[0] == NULL || pv[1] == NULL || pv[1][0] == '\0')
195338032Speter		{
195464562Sgshapiro			syserr("null destination for %s mailer", m->m_mailer);
195538032Speter			rcode = EX_CONFIG;
195638032Speter			goto give_up;
195738032Speter		}
195838032Speter
195964562Sgshapiro# if NETUNIX
196064562Sgshapiro		if (strcmp(pv[0], "FILE") == 0)
196164562Sgshapiro		{
196264562Sgshapiro			curhost = CurHostName = "localhost";
196364562Sgshapiro			mux_path = pv[1];
196464562Sgshapiro		}
196564562Sgshapiro		else
196664562Sgshapiro# endif /* NETUNIX */
196764562Sgshapiro		{
196864562Sgshapiro			CurHostName = pv[1];
196964562Sgshapiro			curhost = hostsignature(m, pv[1]);
197064562Sgshapiro		}
197138032Speter
197238032Speter		if (curhost == NULL || curhost[0] == '\0')
197338032Speter		{
197438032Speter			syserr("null host signature for %s", pv[1]);
197538032Speter			rcode = EX_CONFIG;
197638032Speter			goto give_up;
197738032Speter		}
197838032Speter
197938032Speter		if (!clever)
198038032Speter		{
198164562Sgshapiro			syserr("554 5.3.5 non-clever IPC");
198238032Speter			rcode = EX_CONFIG;
198338032Speter			goto give_up;
198438032Speter		}
198564562Sgshapiro		if (pv[2] != NULL
198664562Sgshapiro# if NETUNIX
198764562Sgshapiro		    && mux_path == NULL
198864562Sgshapiro# endif /* NETUNIX */
198964562Sgshapiro		    )
199038032Speter		{
199190792Sgshapiro			port = htons((unsigned short) atoi(pv[2]));
199238032Speter			if (port == 0)
199338032Speter			{
199464562Sgshapiro# ifdef NO_GETSERVBYNAME
199564562Sgshapiro				syserr("Invalid port number: %s", pv[2]);
199664562Sgshapiro# else /* NO_GETSERVBYNAME */
199738032Speter				struct servent *sp = getservbyname(pv[2], "tcp");
199838032Speter
199938032Speter				if (sp == NULL)
200038032Speter					syserr("Service %s unknown", pv[2]);
200138032Speter				else
200238032Speter					port = sp->s_port;
200364562Sgshapiro# endif /* NO_GETSERVBYNAME */
200438032Speter			}
200538032Speter		}
200664562Sgshapiro
200764562Sgshapiro		nummxhosts = parse_hostsignature(curhost, mxhosts, m);
200890792Sgshapiro		if (TimeOuts.to_aconnect > 0)
200990792Sgshapiro			enough = curtime() + TimeOuts.to_aconnect;
201038032Spetertryhost:
201164562Sgshapiro		while (hostnum < nummxhosts)
201238032Speter		{
201364562Sgshapiro			char sep = ':';
201464562Sgshapiro			char *endp;
201538032Speter			static char hostbuf[MAXNAME + 1];
2016132943Sgshapiro			bool tried_fallbacksmarthost = false;
201738032Speter
201864562Sgshapiro# if NETINET6
201964562Sgshapiro			if (*mxhosts[hostnum] == '[')
202038032Speter			{
202164562Sgshapiro				endp = strchr(mxhosts[hostnum] + 1, ']');
202264562Sgshapiro				if (endp != NULL)
202364562Sgshapiro					endp = strpbrk(endp + 1, ":,");
202464562Sgshapiro			}
202564562Sgshapiro			else
202664562Sgshapiro				endp = strpbrk(mxhosts[hostnum], ":,");
202764562Sgshapiro# else /* NETINET6 */
202864562Sgshapiro			endp = strpbrk(mxhosts[hostnum], ":,");
202964562Sgshapiro# endif /* NETINET6 */
203064562Sgshapiro			if (endp != NULL)
203164562Sgshapiro			{
203264562Sgshapiro				sep = *endp;
203364562Sgshapiro				*endp = '\0';
203464562Sgshapiro			}
203564562Sgshapiro
203690792Sgshapiro			if (hostnum == 1 && skip_back != NULL)
203790792Sgshapiro			{
203890792Sgshapiro				/*
203990792Sgshapiro				**  Coattail piggybacking is no longer an
204090792Sgshapiro				**  option with the mail host next to be tried
204190792Sgshapiro				**  no longer the lowest MX preference
204290792Sgshapiro				**  (hostnum == 1 meaning we're on the second
204390792Sgshapiro				**  preference). We do not try to coattail
204490792Sgshapiro				**  piggyback more than the first MX preference.
204590792Sgshapiro				**  Revert 'tochain' to last location for
204690792Sgshapiro				**  coincidental piggybacking. This works this
204790792Sgshapiro				**  easily because the q_tchain kept getting
204890792Sgshapiro				**  added to the top of the linked list.
204990792Sgshapiro				*/
205090792Sgshapiro
205190792Sgshapiro				tochain = skip_back;
205290792Sgshapiro			}
205390792Sgshapiro
205464562Sgshapiro			if (*mxhosts[hostnum] == '\0')
205564562Sgshapiro			{
205638032Speter				syserr("deliver: null host name in signature");
205764562Sgshapiro				hostnum++;
205864562Sgshapiro				if (endp != NULL)
205964562Sgshapiro					*endp = sep;
206038032Speter				continue;
206138032Speter			}
206290792Sgshapiro			(void) sm_strlcpy(hostbuf, mxhosts[hostnum],
206390792Sgshapiro					  sizeof hostbuf);
206464562Sgshapiro			hostnum++;
206564562Sgshapiro			if (endp != NULL)
206664562Sgshapiro				*endp = sep;
206738032Speter
2068132943Sgshapiro  one_last_try:
206938032Speter			/* see if we already know that this host is fried */
207038032Speter			CurHostName = hostbuf;
207138032Speter			mci = mci_get(hostbuf, m);
207238032Speter			if (mci->mci_state != MCIS_CLOSED)
207338032Speter			{
207490792Sgshapiro				char *type;
207590792Sgshapiro
207638032Speter				if (tTd(11, 1))
207738032Speter				{
207890792Sgshapiro					sm_dprintf("openmailer: ");
2079132943Sgshapiro					mci_dump(sm_debug_file(), mci, false);
208038032Speter				}
208138032Speter				CurHostName = mci->mci_host;
208290792Sgshapiro				if (bitnset(M_LMTP, m->m_flags))
208390792Sgshapiro					type = "L";
208490792Sgshapiro				else if (bitset(MCIF_ESMTP, mci->mci_flags))
208590792Sgshapiro					type = "ES";
208690792Sgshapiro				else
208790792Sgshapiro					type = "S";
208890792Sgshapiro				message("Using cached %sMTP connection to %s via %s...",
208990792Sgshapiro					type, hostbuf, m->m_name);
209064562Sgshapiro				mci->mci_deliveries++;
209138032Speter				break;
209238032Speter			}
209338032Speter			mci->mci_mailer = m;
209438032Speter			if (mci->mci_exitstat != EX_OK)
209538032Speter			{
209638032Speter				if (mci->mci_exitstat == EX_TEMPFAIL)
209790792Sgshapiro					goodmxfound = true;
2098132943Sgshapiro
2099132943Sgshapiro				/* Try FallbackSmartHost? */
2100132943Sgshapiro				if (should_try_fbsh(e, &tried_fallbacksmarthost,
2101132943Sgshapiro						    hostbuf, sizeof hostbuf,
2102132943Sgshapiro						    mci->mci_exitstat))
2103132943Sgshapiro					goto one_last_try;
2104132943Sgshapiro
210538032Speter				continue;
210638032Speter			}
210738032Speter
210838032Speter			if (mci_lock_host(mci) != EX_OK)
210938032Speter			{
211038032Speter				mci_setstat(mci, EX_TEMPFAIL, "4.4.5", NULL);
211190792Sgshapiro				goodmxfound = true;
211238032Speter				continue;
211338032Speter			}
211438032Speter
211538032Speter			/* try the connection */
211690792Sgshapiro			sm_setproctitle(true, e, "%s %s: %s",
211764562Sgshapiro					qid_printname(e),
211864562Sgshapiro					hostbuf, "user open");
211964562Sgshapiro# if NETUNIX
212064562Sgshapiro			if (mux_path != NULL)
212164562Sgshapiro			{
212238032Speter				message("Connecting to %s via %s...",
212364562Sgshapiro					mux_path, m->m_name);
212490792Sgshapiro				i = makeconnection_ds((char *) mux_path, mci);
212564562Sgshapiro			}
212638032Speter			else
212764562Sgshapiro# endif /* NETUNIX */
212864562Sgshapiro			{
212964562Sgshapiro				if (port == 0)
213064562Sgshapiro					message("Connecting to %s via %s...",
213164562Sgshapiro						hostbuf, m->m_name);
213264562Sgshapiro				else
213364562Sgshapiro					message("Connecting to %s port %d via %s...",
213464562Sgshapiro						hostbuf, ntohs(port),
213564562Sgshapiro						m->m_name);
213690792Sgshapiro				i = makeconnection(hostbuf, port, mci, e,
213790792Sgshapiro						   enough);
213864562Sgshapiro			}
213977349Sgshapiro			mci->mci_errno = errno;
214038032Speter			mci->mci_lastuse = curtime();
214164562Sgshapiro			mci->mci_deliveries = 0;
214238032Speter			mci->mci_exitstat = i;
214364562Sgshapiro# if NAMED_BIND
214438032Speter			mci->mci_herrno = h_errno;
214564562Sgshapiro# endif /* NAMED_BIND */
214690792Sgshapiro
214790792Sgshapiro			/*
214890792Sgshapiro			**  Have we tried long enough to get a connection?
214990792Sgshapiro			**	If yes, skip to the fallback MX hosts
215090792Sgshapiro			**	(if existent).
215190792Sgshapiro			*/
215290792Sgshapiro
215390792Sgshapiro			if (enough > 0 && mci->mci_lastuse >= enough)
215490792Sgshapiro			{
215590792Sgshapiro				int h;
215690792Sgshapiro# if NAMED_BIND
2157132943Sgshapiro				extern int NumFallbackMXHosts;
215890792Sgshapiro# else /* NAMED_BIND */
2159132943Sgshapiro				const int NumFallbackMXHosts = 0;
216090792Sgshapiro# endif /* NAMED_BIND */
216190792Sgshapiro
216290792Sgshapiro				if (hostnum < nummxhosts && LogLevel > 9)
216390792Sgshapiro					sm_syslog(LOG_INFO, e->e_id,
216490792Sgshapiro						  "Timeout.to_aconnect occurred before exhausting all addresses");
216590792Sgshapiro
216690792Sgshapiro				/* turn off timeout if fallback available */
2167132943Sgshapiro				if (NumFallbackMXHosts > 0)
216890792Sgshapiro					enough = 0;
216990792Sgshapiro
217090792Sgshapiro				/* skip to a fallback MX host */
2171132943Sgshapiro				h = nummxhosts - NumFallbackMXHosts;
217290792Sgshapiro				if (hostnum < h)
217390792Sgshapiro					hostnum = h;
217490792Sgshapiro			}
217538032Speter			if (i == EX_OK)
217638032Speter			{
217790792Sgshapiro				goodmxfound = true;
217894334Sgshapiro				markstats(e, firstto, STATS_CONNECT);
217938032Speter				mci->mci_state = MCIS_OPENING;
218038032Speter				mci_cache(mci);
218138032Speter				if (TrafficLogFile != NULL)
218290792Sgshapiro					(void) sm_io_fprintf(TrafficLogFile,
218390792Sgshapiro							     SM_TIME_DEFAULT,
218490792Sgshapiro							     "%05d === CONNECT %s\n",
218590792Sgshapiro							     (int) CurrentPid,
218690792Sgshapiro							     hostbuf);
218738032Speter				break;
218838032Speter			}
218938032Speter			else
219038032Speter			{
2191132943Sgshapiro				/* Try FallbackSmartHost? */
2192132943Sgshapiro				if (should_try_fbsh(e, &tried_fallbacksmarthost,
2193132943Sgshapiro						    hostbuf, sizeof hostbuf, i))
2194132943Sgshapiro					goto one_last_try;
2195132943Sgshapiro
219664562Sgshapiro				if (tTd(11, 1))
219790792Sgshapiro					sm_dprintf("openmailer: makeconnection => stat=%d, errno=%d\n",
219890792Sgshapiro						   i, errno);
219938032Speter				if (i == EX_TEMPFAIL)
220090792Sgshapiro					goodmxfound = true;
220138032Speter				mci_unlock_host(mci);
220238032Speter			}
220338032Speter
220438032Speter			/* enter status of this host */
220538032Speter			setstat(i);
220638032Speter
220738032Speter			/* should print some message here for -v mode */
220838032Speter		}
220938032Speter		if (mci == NULL)
221038032Speter		{
221138032Speter			syserr("deliver: no host name");
221238032Speter			rcode = EX_SOFTWARE;
221338032Speter			goto give_up;
221438032Speter		}
221538032Speter		mci->mci_pid = 0;
221638032Speter	}
221738032Speter	else
221838032Speter	{
221938032Speter		/* flush any expired connections */
222038032Speter		(void) mci_scan(NULL);
222138032Speter		mci = NULL;
222238032Speter
222338032Speter		if (bitnset(M_LMTP, m->m_flags))
222438032Speter		{
222538032Speter			/* try to get a cached connection */
222638032Speter			mci = mci_get(m->m_name, m);
222738032Speter			if (mci->mci_host == NULL)
222838032Speter				mci->mci_host = m->m_name;
222938032Speter			CurHostName = mci->mci_host;
223038032Speter			if (mci->mci_state != MCIS_CLOSED)
223138032Speter			{
223238032Speter				message("Using cached LMTP connection for %s...",
223338032Speter					m->m_name);
223464562Sgshapiro				mci->mci_deliveries++;
223538032Speter				goto do_transfer;
223638032Speter			}
223738032Speter		}
223838032Speter
223938032Speter		/* announce the connection to verbose listeners */
224038032Speter		if (host == NULL || host[0] == '\0')
224138032Speter			message("Connecting to %s...", m->m_name);
224238032Speter		else
224338032Speter			message("Connecting to %s via %s...", host, m->m_name);
224438032Speter		if (TrafficLogFile != NULL)
224538032Speter		{
224638032Speter			char **av;
224738032Speter
224890792Sgshapiro			(void) sm_io_fprintf(TrafficLogFile, SM_TIME_DEFAULT,
224990792Sgshapiro					     "%05d === EXEC", (int) CurrentPid);
225038032Speter			for (av = pv; *av != NULL; av++)
225190792Sgshapiro				(void) sm_io_fprintf(TrafficLogFile,
225290792Sgshapiro						     SM_TIME_DEFAULT, " %s",
225390792Sgshapiro						     *av);
225490792Sgshapiro			(void) sm_io_fprintf(TrafficLogFile, SM_TIME_DEFAULT,
225590792Sgshapiro					     "\n");
225638032Speter		}
225738032Speter
225838032Speter#if XDEBUG
225938032Speter		checkfd012("before creating mail pipe");
226064562Sgshapiro#endif /* XDEBUG */
226138032Speter
226238032Speter		/* create a pipe to shove the mail through */
226338032Speter		if (pipe(mpvect) < 0)
226438032Speter		{
226538032Speter			syserr("%s... openmailer(%s): pipe (to mailer)",
226690792Sgshapiro			       shortenstring(e->e_to, MAXSHORTSTR), m->m_name);
226738032Speter			if (tTd(11, 1))
226890792Sgshapiro				sm_dprintf("openmailer: NULL\n");
226938032Speter			rcode = EX_OSERR;
227038032Speter			goto give_up;
227138032Speter		}
227238032Speter
227338032Speter#if XDEBUG
227438032Speter		/* make sure we didn't get one of the standard I/O files */
227538032Speter		if (mpvect[0] < 3 || mpvect[1] < 3)
227638032Speter		{
227738032Speter			syserr("%s... openmailer(%s): bogus mpvect %d %d",
227890792Sgshapiro			       shortenstring(e->e_to, MAXSHORTSTR), m->m_name,
227990792Sgshapiro			       mpvect[0], mpvect[1]);
228090792Sgshapiro			printopenfds(true);
228138032Speter			if (tTd(11, 1))
228290792Sgshapiro				sm_dprintf("openmailer: NULL\n");
228338032Speter			rcode = EX_OSERR;
228438032Speter			goto give_up;
228538032Speter		}
228638032Speter
228738032Speter		/* make sure system call isn't dead meat */
228838032Speter		checkfdopen(mpvect[0], "mpvect[0]");
228938032Speter		checkfdopen(mpvect[1], "mpvect[1]");
229038032Speter		if (mpvect[0] == mpvect[1] ||
229138032Speter		    (e->e_lockfp != NULL &&
229290792Sgshapiro		     (mpvect[0] == sm_io_getinfo(e->e_lockfp, SM_IO_WHAT_FD,
229390792Sgshapiro						 NULL) ||
229490792Sgshapiro		      mpvect[1] == sm_io_getinfo(e->e_lockfp, SM_IO_WHAT_FD,
229590792Sgshapiro						 NULL))))
229638032Speter		{
229738032Speter			if (e->e_lockfp == NULL)
229838032Speter				syserr("%s... openmailer(%s): overlapping mpvect %d %d",
229990792Sgshapiro				       shortenstring(e->e_to, MAXSHORTSTR),
230090792Sgshapiro				       m->m_name, mpvect[0], mpvect[1]);
230138032Speter			else
230238032Speter				syserr("%s... openmailer(%s): overlapping mpvect %d %d, lockfp = %d",
230390792Sgshapiro				       shortenstring(e->e_to, MAXSHORTSTR),
230490792Sgshapiro				       m->m_name, mpvect[0], mpvect[1],
230590792Sgshapiro				       sm_io_getinfo(e->e_lockfp,
230690792Sgshapiro						     SM_IO_WHAT_FD, NULL));
230738032Speter		}
230864562Sgshapiro#endif /* XDEBUG */
230938032Speter
231064562Sgshapiro		/* create a return pipe */
231164562Sgshapiro		if (pipe(rpvect) < 0)
231238032Speter		{
231364562Sgshapiro			syserr("%s... openmailer(%s): pipe (from mailer)",
231490792Sgshapiro			       shortenstring(e->e_to, MAXSHORTSTR),
231590792Sgshapiro			       m->m_name);
231664562Sgshapiro			(void) close(mpvect[0]);
231764562Sgshapiro			(void) close(mpvect[1]);
231864562Sgshapiro			if (tTd(11, 1))
231990792Sgshapiro				sm_dprintf("openmailer: NULL\n");
232064562Sgshapiro			rcode = EX_OSERR;
232164562Sgshapiro			goto give_up;
232238032Speter		}
232364562Sgshapiro#if XDEBUG
232464562Sgshapiro		checkfdopen(rpvect[0], "rpvect[0]");
232564562Sgshapiro		checkfdopen(rpvect[1], "rpvect[1]");
232664562Sgshapiro#endif /* XDEBUG */
232738032Speter
232838032Speter		/*
232938032Speter		**  Actually fork the mailer process.
233038032Speter		**	DOFORK is clever about retrying.
233138032Speter		**
233238032Speter		**	Dispose of SIGCHLD signal catchers that may be laying
233364562Sgshapiro		**	around so that endmailer will get it.
233438032Speter		*/
233538032Speter
233690792Sgshapiro		if (e->e_xfp != NULL)	/* for debugging */
233790792Sgshapiro			(void) sm_io_flush(e->e_xfp, SM_TIME_DEFAULT);
233890792Sgshapiro		(void) sm_io_flush(smioout, SM_TIME_DEFAULT);
233990792Sgshapiro		(void) sm_signal(SIGCHLD, SIG_DFL);
234064562Sgshapiro
234164562Sgshapiro
234238032Speter		DOFORK(FORK);
234338032Speter		/* pid is set by DOFORK */
234464562Sgshapiro
234538032Speter		if (pid < 0)
234638032Speter		{
234738032Speter			/* failure */
234838032Speter			syserr("%s... openmailer(%s): cannot fork",
234990792Sgshapiro			       shortenstring(e->e_to, MAXSHORTSTR), m->m_name);
235038032Speter			(void) close(mpvect[0]);
235138032Speter			(void) close(mpvect[1]);
235264562Sgshapiro			(void) close(rpvect[0]);
235364562Sgshapiro			(void) close(rpvect[1]);
235438032Speter			if (tTd(11, 1))
235590792Sgshapiro				sm_dprintf("openmailer: NULL\n");
235638032Speter			rcode = EX_OSERR;
235738032Speter			goto give_up;
235838032Speter		}
235938032Speter		else if (pid == 0)
236038032Speter		{
236164562Sgshapiro			int save_errno;
236290792Sgshapiro			int sff;
236338032Speter			int new_euid = NO_UID;
236438032Speter			int new_ruid = NO_UID;
236538032Speter			int new_gid = NO_GID;
236690792Sgshapiro			char *user = NULL;
236738032Speter			struct stat stb;
236838032Speter			extern int DtableSize;
236938032Speter
237090792Sgshapiro			CurrentPid = getpid();
237190792Sgshapiro
237280785Sgshapiro			/* clear the events to turn off SIGALRMs */
237390792Sgshapiro			sm_clear_events();
237480785Sgshapiro
237577349Sgshapiro			/* Reset global flags */
237677349Sgshapiro			RestartRequest = NULL;
237790792Sgshapiro			RestartWorkGroup = false;
237877349Sgshapiro			ShutdownRequest = NULL;
237977349Sgshapiro			PendingSignal = 0;
238077349Sgshapiro
238138032Speter			if (e->e_lockfp != NULL)
238290792Sgshapiro				(void) close(sm_io_getinfo(e->e_lockfp,
238390792Sgshapiro							   SM_IO_WHAT_FD,
238490792Sgshapiro							   NULL));
238538032Speter
238638032Speter			/* child -- set up input & exec mailer */
238790792Sgshapiro			(void) sm_signal(SIGALRM, sm_signal_noop);
238890792Sgshapiro			(void) sm_signal(SIGCHLD, SIG_DFL);
238990792Sgshapiro			(void) sm_signal(SIGHUP, SIG_IGN);
239090792Sgshapiro			(void) sm_signal(SIGINT, SIG_IGN);
239190792Sgshapiro			(void) sm_signal(SIGTERM, SIG_DFL);
239280785Sgshapiro# ifdef SIGUSR1
239390792Sgshapiro			(void) sm_signal(SIGUSR1, sm_signal_noop);
239480785Sgshapiro# endif /* SIGUSR1 */
239538032Speter
239638032Speter			if (m != FileMailer || stat(tochain->q_user, &stb) < 0)
239738032Speter				stb.st_mode = 0;
239838032Speter
239964562Sgshapiro# if HASSETUSERCONTEXT
240038032Speter			/*
240138032Speter			**  Set user resources.
240238032Speter			*/
240338032Speter
240438032Speter			if (contextaddr != NULL)
240538032Speter			{
2406110560Sgshapiro				int sucflags;
240738032Speter				struct passwd *pwd;
240838032Speter
240938032Speter				if (contextaddr->q_ruser != NULL)
241038032Speter					pwd = sm_getpwnam(contextaddr->q_ruser);
241138032Speter				else
241238032Speter					pwd = sm_getpwnam(contextaddr->q_user);
2413110560Sgshapiro				sucflags = LOGIN_SETRESOURCES|LOGIN_SETPRIORITY;
2414110560Sgshapiro#ifdef LOGIN_SETMAC
2415110560Sgshapiro				sucflags |= LOGIN_SETMAC;
2416110560Sgshapiro#endif /* LOGIN_SETMAC */
2417102528Sgshapiro				if (pwd != NULL &&
2418102528Sgshapiro				    setusercontext(NULL, pwd, pwd->pw_uid,
2419110560Sgshapiro						   sucflags) == -1 &&
2420102528Sgshapiro				    suidwarn)
2421102528Sgshapiro				{
2422102528Sgshapiro					syserr("openmailer: setusercontext() failed");
2423102528Sgshapiro					exit(EX_TEMPFAIL);
2424102528Sgshapiro				}
242538032Speter			}
242664562Sgshapiro# endif /* HASSETUSERCONTEXT */
242738032Speter
242890792Sgshapiro#if HASNICE
242938032Speter			/* tweak niceness */
243038032Speter			if (m->m_nice != 0)
243164562Sgshapiro				(void) nice(m->m_nice);
243290792Sgshapiro#endif /* HASNICE */
243338032Speter
243438032Speter			/* reset group id */
243538032Speter			if (bitnset(M_SPECIFIC_UID, m->m_flags))
2436132943Sgshapiro			{
2437132943Sgshapiro				if (m->m_gid == NO_GID)
2438132943Sgshapiro					new_gid = RunAsGid;
2439132943Sgshapiro				else
2440132943Sgshapiro					new_gid = m->m_gid;
2441132943Sgshapiro			}
244238032Speter			else if (bitset(S_ISGID, stb.st_mode))
244338032Speter				new_gid = stb.st_gid;
244438032Speter			else if (ctladdr != NULL && ctladdr->q_gid != 0)
244538032Speter			{
244638032Speter				if (!DontInitGroups)
244738032Speter				{
244890792Sgshapiro					user = ctladdr->q_ruser;
244990792Sgshapiro					if (user == NULL)
245090792Sgshapiro						user = ctladdr->q_user;
245138032Speter
245290792Sgshapiro					if (initgroups(user,
245390792Sgshapiro						       ctladdr->q_gid) == -1
245490792Sgshapiro					    && suidwarn)
245564562Sgshapiro					{
245638032Speter						syserr("openmailer: initgroups(%s, %d) failed",
245790792Sgshapiro							user, ctladdr->q_gid);
245864562Sgshapiro						exit(EX_TEMPFAIL);
245964562Sgshapiro					}
246038032Speter				}
246138032Speter				else
246238032Speter				{
246338032Speter					GIDSET_T gidset[1];
246438032Speter
246538032Speter					gidset[0] = ctladdr->q_gid;
246690792Sgshapiro					if (setgroups(1, gidset) == -1
246790792Sgshapiro					    && suidwarn)
246864562Sgshapiro					{
246938032Speter						syserr("openmailer: setgroups() failed");
247064562Sgshapiro						exit(EX_TEMPFAIL);
247164562Sgshapiro					}
247238032Speter				}
247338032Speter				new_gid = ctladdr->q_gid;
247438032Speter			}
247538032Speter			else
247638032Speter			{
247738032Speter				if (!DontInitGroups)
247838032Speter				{
247990792Sgshapiro					user = DefUser;
248090792Sgshapiro					if (initgroups(DefUser, DefGid) == -1 &&
248190792Sgshapiro					    suidwarn)
248264562Sgshapiro					{
248338032Speter						syserr("openmailer: initgroups(%s, %d) failed",
248490792Sgshapiro						       DefUser, DefGid);
248564562Sgshapiro						exit(EX_TEMPFAIL);
248664562Sgshapiro					}
248738032Speter				}
248838032Speter				else
248938032Speter				{
249038032Speter					GIDSET_T gidset[1];
249138032Speter
249238032Speter					gidset[0] = DefGid;
249390792Sgshapiro					if (setgroups(1, gidset) == -1
249490792Sgshapiro					    && suidwarn)
249564562Sgshapiro					{
249638032Speter						syserr("openmailer: setgroups() failed");
249764562Sgshapiro						exit(EX_TEMPFAIL);
249864562Sgshapiro					}
249938032Speter				}
2500132943Sgshapiro				if (m->m_gid == NO_GID)
250138032Speter					new_gid = DefGid;
250238032Speter				else
250338032Speter					new_gid = m->m_gid;
250438032Speter			}
250564562Sgshapiro			if (new_gid != NO_GID)
250664562Sgshapiro			{
250764562Sgshapiro				if (RunAsUid != 0 &&
250864562Sgshapiro				    bitnset(M_SPECIFIC_UID, m->m_flags) &&
250964562Sgshapiro				    new_gid != getgid() &&
251064562Sgshapiro				    new_gid != getegid())
251164562Sgshapiro				{
251264562Sgshapiro					/* Only root can change the gid */
251390792Sgshapiro					syserr("openmailer: insufficient privileges to change gid, RunAsUid=%d, new_gid=%d, gid=%d, egid=%d",
251490792Sgshapiro					       (int) RunAsUid, (int) new_gid,
251590792Sgshapiro					       (int) getgid(), (int) getegid());
251664562Sgshapiro					exit(EX_TEMPFAIL);
251764562Sgshapiro				}
251838032Speter
251964562Sgshapiro				if (setgid(new_gid) < 0 && suidwarn)
252064562Sgshapiro				{
252164562Sgshapiro					syserr("openmailer: setgid(%ld) failed",
252264562Sgshapiro					       (long) new_gid);
252364562Sgshapiro					exit(EX_TEMPFAIL);
252464562Sgshapiro				}
252564562Sgshapiro			}
252664562Sgshapiro
252764562Sgshapiro			/* change root to some "safe" directory */
252864562Sgshapiro			if (m->m_rootdir != NULL)
252964562Sgshapiro			{
253098121Sgshapiro				expand(m->m_rootdir, cbuf, sizeof cbuf, e);
253164562Sgshapiro				if (tTd(11, 20))
253290792Sgshapiro					sm_dprintf("openmailer: chroot %s\n",
253398121Sgshapiro						   cbuf);
253498121Sgshapiro				if (chroot(cbuf) < 0)
253564562Sgshapiro				{
253664562Sgshapiro					syserr("openmailer: Cannot chroot(%s)",
253798121Sgshapiro					       cbuf);
253864562Sgshapiro					exit(EX_TEMPFAIL);
253964562Sgshapiro				}
254064562Sgshapiro				if (chdir("/") < 0)
254164562Sgshapiro				{
254264562Sgshapiro					syserr("openmailer: cannot chdir(/)");
254364562Sgshapiro					exit(EX_TEMPFAIL);
254464562Sgshapiro				}
254564562Sgshapiro			}
254664562Sgshapiro
254738032Speter			/* reset user id */
254838032Speter			endpwent();
254990792Sgshapiro			sm_mbdb_terminate();
255038032Speter			if (bitnset(M_SPECIFIC_UID, m->m_flags))
255180785Sgshapiro			{
2552132943Sgshapiro				if (m->m_uid == NO_UID)
2553132943Sgshapiro					new_euid = RunAsUid;
2554132943Sgshapiro				else
2555132943Sgshapiro					new_euid = m->m_uid;
255680785Sgshapiro
255780785Sgshapiro				/*
255880785Sgshapiro				**  Undo the effects of the uid change in main
255980785Sgshapiro				**  for signal handling.  The real uid may
256080785Sgshapiro				**  be used by mailer in adding a "From "
256180785Sgshapiro				**  line.
256280785Sgshapiro				*/
256380785Sgshapiro
256480785Sgshapiro				if (RealUid != 0 && RealUid != getuid())
256590792Sgshapiro				{
256690792Sgshapiro# if MAILER_SETUID_METHOD == USE_SETEUID
256790792Sgshapiro#  if HASSETREUID
256890792Sgshapiro					if (setreuid(RealUid, geteuid()) < 0)
256990792Sgshapiro					{
257090792Sgshapiro						syserr("openmailer: setreuid(%d, %d) failed",
257190792Sgshapiro						       (int) RealUid, (int) geteuid());
257290792Sgshapiro						exit(EX_OSERR);
257390792Sgshapiro					}
257490792Sgshapiro#  endif /* HASSETREUID */
257590792Sgshapiro# endif /* MAILER_SETUID_METHOD == USE_SETEUID */
257690792Sgshapiro# if MAILER_SETUID_METHOD == USE_SETREUID
257780785Sgshapiro					new_ruid = RealUid;
257890792Sgshapiro# endif /* MAILER_SETUID_METHOD == USE_SETREUID */
257990792Sgshapiro				}
258080785Sgshapiro			}
258138032Speter			else if (bitset(S_ISUID, stb.st_mode))
258238032Speter				new_ruid = stb.st_uid;
258338032Speter			else if (ctladdr != NULL && ctladdr->q_uid != 0)
258438032Speter				new_ruid = ctladdr->q_uid;
2585132943Sgshapiro			else if (m->m_uid != NO_UID)
258638032Speter				new_ruid = m->m_uid;
258738032Speter			else
258838032Speter				new_ruid = DefUid;
258994334Sgshapiro
259094334Sgshapiro# if _FFR_USE_SETLOGIN
259194334Sgshapiro			/* run disconnected from terminal and set login name */
259294334Sgshapiro			if (setsid() >= 0 &&
259394334Sgshapiro			    ctladdr != NULL && ctladdr->q_uid != 0 &&
259494334Sgshapiro			    new_euid == ctladdr->q_uid)
259594334Sgshapiro			{
259694334Sgshapiro				struct passwd *pwd;
259794334Sgshapiro
259894334Sgshapiro				pwd = sm_getpwuid(ctladdr->q_uid);
259994334Sgshapiro				if (pwd != NULL && suidwarn)
260094334Sgshapiro					(void) setlogin(pwd->pw_name);
260194334Sgshapiro				endpwent();
260294334Sgshapiro			}
260394334Sgshapiro# endif /* _FFR_USE_SETLOGIN */
260494334Sgshapiro
260538032Speter			if (new_euid != NO_UID)
260638032Speter			{
260764562Sgshapiro				if (RunAsUid != 0 && new_euid != RunAsUid)
260864562Sgshapiro				{
260964562Sgshapiro					/* Only root can change the uid */
261090792Sgshapiro					syserr("openmailer: insufficient privileges to change uid, new_euid=%d, RunAsUid=%d",
261190792Sgshapiro					       (int) new_euid, (int) RunAsUid);
261264562Sgshapiro					exit(EX_TEMPFAIL);
261364562Sgshapiro				}
261464562Sgshapiro
261538032Speter				vendor_set_uid(new_euid);
261664562Sgshapiro# if MAILER_SETUID_METHOD == USE_SETEUID
261738032Speter				if (seteuid(new_euid) < 0 && suidwarn)
261864562Sgshapiro				{
261938032Speter					syserr("openmailer: seteuid(%ld) failed",
262090792Sgshapiro					       (long) new_euid);
262164562Sgshapiro					exit(EX_TEMPFAIL);
262264562Sgshapiro				}
262364562Sgshapiro# endif /* MAILER_SETUID_METHOD == USE_SETEUID */
262464562Sgshapiro# if MAILER_SETUID_METHOD == USE_SETREUID
262538032Speter				if (setreuid(new_ruid, new_euid) < 0 && suidwarn)
262664562Sgshapiro				{
262738032Speter					syserr("openmailer: setreuid(%ld, %ld) failed",
262890792Sgshapiro					       (long) new_ruid, (long) new_euid);
262964562Sgshapiro					exit(EX_TEMPFAIL);
263064562Sgshapiro				}
263164562Sgshapiro# endif /* MAILER_SETUID_METHOD == USE_SETREUID */
263264562Sgshapiro# if MAILER_SETUID_METHOD == USE_SETUID
263338032Speter				if (new_euid != geteuid() && setuid(new_euid) < 0 && suidwarn)
263464562Sgshapiro				{
263538032Speter					syserr("openmailer: setuid(%ld) failed",
263690792Sgshapiro					       (long) new_euid);
263764562Sgshapiro					exit(EX_TEMPFAIL);
263864562Sgshapiro				}
263964562Sgshapiro# endif /* MAILER_SETUID_METHOD == USE_SETUID */
264038032Speter			}
264138032Speter			else if (new_ruid != NO_UID)
264238032Speter			{
264338032Speter				vendor_set_uid(new_ruid);
264438032Speter				if (setuid(new_ruid) < 0 && suidwarn)
264564562Sgshapiro				{
264638032Speter					syserr("openmailer: setuid(%ld) failed",
264790792Sgshapiro					       (long) new_ruid);
264864562Sgshapiro					exit(EX_TEMPFAIL);
264964562Sgshapiro				}
265038032Speter			}
265138032Speter
265238032Speter			if (tTd(11, 2))
265390792Sgshapiro				sm_dprintf("openmailer: running as r/euid=%d/%d, r/egid=%d/%d\n",
265490792Sgshapiro					   (int) getuid(), (int) geteuid(),
265590792Sgshapiro					   (int) getgid(), (int) getegid());
265638032Speter
265738032Speter			/* move into some "safe" directory */
265838032Speter			if (m->m_execdir != NULL)
265938032Speter			{
266038032Speter				char *q;
266138032Speter
266238032Speter				for (p = m->m_execdir; p != NULL; p = q)
266338032Speter				{
266438032Speter					q = strchr(p, ':');
266538032Speter					if (q != NULL)
266638032Speter						*q = '\0';
266798121Sgshapiro					expand(p, cbuf, sizeof cbuf, e);
266838032Speter					if (q != NULL)
266938032Speter						*q++ = ':';
267038032Speter					if (tTd(11, 20))
267190792Sgshapiro						sm_dprintf("openmailer: trydir %s\n",
267298121Sgshapiro							   cbuf);
267398121Sgshapiro					if (cbuf[0] != '\0' &&
267498121Sgshapiro					    chdir(cbuf) >= 0)
267538032Speter						break;
267638032Speter				}
267738032Speter			}
267838032Speter
267990792Sgshapiro			/* Check safety of program to be run */
268090792Sgshapiro			sff = SFF_ROOTOK|SFF_EXECOK;
268190792Sgshapiro			if (!bitnset(DBS_RUNWRITABLEPROGRAM,
268290792Sgshapiro				     DontBlameSendmail))
268390792Sgshapiro				sff |= SFF_NOGWFILES|SFF_NOWWFILES;
268490792Sgshapiro			if (bitnset(DBS_RUNPROGRAMINUNSAFEDIRPATH,
268590792Sgshapiro				    DontBlameSendmail))
268690792Sgshapiro				sff |= SFF_NOPATHCHECK;
268790792Sgshapiro			else
268890792Sgshapiro				sff |= SFF_SAFEDIRPATH;
268990792Sgshapiro			ret = safefile(m->m_mailer, getuid(), getgid(),
269090792Sgshapiro				       user, sff, 0, NULL);
269190792Sgshapiro			if (ret != 0)
269290792Sgshapiro				sm_syslog(LOG_INFO, e->e_id,
269390792Sgshapiro					  "Warning: program %s unsafe: %s",
269490792Sgshapiro					  m->m_mailer, sm_errstring(ret));
269590792Sgshapiro
269638032Speter			/* arrange to filter std & diag output of command */
269764562Sgshapiro			(void) close(rpvect[0]);
269864562Sgshapiro			if (dup2(rpvect[1], STDOUT_FILENO) < 0)
269938032Speter			{
270064562Sgshapiro				syserr("%s... openmailer(%s): cannot dup pipe %d for stdout",
270164562Sgshapiro				       shortenstring(e->e_to, MAXSHORTSTR),
270264562Sgshapiro				       m->m_name, rpvect[1]);
270364562Sgshapiro				_exit(EX_OSERR);
270438032Speter			}
270564562Sgshapiro			(void) close(rpvect[1]);
270664562Sgshapiro
270738032Speter			if (dup2(STDOUT_FILENO, STDERR_FILENO) < 0)
270838032Speter			{
270938032Speter				syserr("%s... openmailer(%s): cannot dup stdout for stderr",
271090792Sgshapiro				       shortenstring(e->e_to, MAXSHORTSTR),
271190792Sgshapiro				       m->m_name);
271238032Speter				_exit(EX_OSERR);
271338032Speter			}
271438032Speter
271538032Speter			/* arrange to get standard input */
271638032Speter			(void) close(mpvect[1]);
271738032Speter			if (dup2(mpvect[0], STDIN_FILENO) < 0)
271838032Speter			{
271938032Speter				syserr("%s... openmailer(%s): cannot dup pipe %d for stdin",
272090792Sgshapiro				       shortenstring(e->e_to, MAXSHORTSTR),
272190792Sgshapiro				       m->m_name, mpvect[0]);
272238032Speter				_exit(EX_OSERR);
272338032Speter			}
272438032Speter			(void) close(mpvect[0]);
272538032Speter
272638032Speter			/* arrange for all the files to be closed */
2727132943Sgshapiro			sm_close_on_exec(STDERR_FILENO + 1, DtableSize);
272838032Speter
272994334Sgshapiro# if !_FFR_USE_SETLOGIN
273038032Speter			/* run disconnected from terminal */
273138032Speter			(void) setsid();
273294334Sgshapiro# endif /* !_FFR_USE_SETLOGIN */
273338032Speter
273438032Speter			/* try to execute the mailer */
273564562Sgshapiro			(void) execve(m->m_mailer, (ARGV_T) pv,
273664562Sgshapiro				      (ARGV_T) UserEnviron);
273764562Sgshapiro			save_errno = errno;
273838032Speter			syserr("Cannot exec %s", m->m_mailer);
273938032Speter			if (bitnset(M_LOCALMAILER, m->m_flags) ||
274064562Sgshapiro			    transienterror(save_errno))
274138032Speter				_exit(EX_OSERR);
274238032Speter			_exit(EX_UNAVAILABLE);
274338032Speter		}
274438032Speter
274538032Speter		/*
274638032Speter		**  Set up return value.
274738032Speter		*/
274838032Speter
274938032Speter		if (mci == NULL)
275038032Speter		{
275190792Sgshapiro			if (clever)
275290792Sgshapiro			{
275390792Sgshapiro				/*
275490792Sgshapiro				**  Allocate from general heap, not
275590792Sgshapiro				**  envelope rpool, because this mci
275690792Sgshapiro				**  is going to be cached.
275790792Sgshapiro				*/
275890792Sgshapiro
275990792Sgshapiro				mci = mci_new(NULL);
276090792Sgshapiro			}
276190792Sgshapiro			else
276290792Sgshapiro			{
276390792Sgshapiro				/*
276490792Sgshapiro				**  Prevent a storage leak by allocating
276590792Sgshapiro				**  this from the envelope rpool.
276690792Sgshapiro				*/
276790792Sgshapiro
276890792Sgshapiro				mci = mci_new(e->e_rpool);
276990792Sgshapiro			}
277038032Speter		}
277138032Speter		mci->mci_mailer = m;
277238032Speter		if (clever)
277338032Speter		{
277438032Speter			mci->mci_state = MCIS_OPENING;
277538032Speter			mci_cache(mci);
277638032Speter		}
277738032Speter		else
277838032Speter		{
277938032Speter			mci->mci_state = MCIS_OPEN;
278038032Speter		}
278138032Speter		mci->mci_pid = pid;
278238032Speter		(void) close(mpvect[0]);
278390792Sgshapiro		mci->mci_out = sm_io_open(SmFtStdiofd, SM_TIME_DEFAULT,
2784132943Sgshapiro					  (void *) &(mpvect[1]), SM_IO_WRONLY_B,
278590792Sgshapiro					  NULL);
278638032Speter		if (mci->mci_out == NULL)
278738032Speter		{
278838032Speter			syserr("deliver: cannot create mailer output channel, fd=%d",
278990792Sgshapiro			       mpvect[1]);
279038032Speter			(void) close(mpvect[1]);
279164562Sgshapiro			(void) close(rpvect[0]);
279264562Sgshapiro			(void) close(rpvect[1]);
279338032Speter			rcode = EX_OSERR;
279438032Speter			goto give_up;
279538032Speter		}
279664562Sgshapiro
279764562Sgshapiro		(void) close(rpvect[1]);
279890792Sgshapiro		mci->mci_in = sm_io_open(SmFtStdiofd, SM_TIME_DEFAULT,
2799132943Sgshapiro					 (void *) &(rpvect[0]), SM_IO_RDONLY_B,
280090792Sgshapiro					 NULL);
280164562Sgshapiro		if (mci->mci_in == NULL)
280238032Speter		{
280364562Sgshapiro			syserr("deliver: cannot create mailer input channel, fd=%d",
280464562Sgshapiro			       mpvect[1]);
280564562Sgshapiro			(void) close(rpvect[0]);
280690792Sgshapiro			(void) sm_io_close(mci->mci_out, SM_TIME_DEFAULT);
280764562Sgshapiro			mci->mci_out = NULL;
280864562Sgshapiro			rcode = EX_OSERR;
280964562Sgshapiro			goto give_up;
281038032Speter		}
281138032Speter	}
281238032Speter
281338032Speter	/*
281438032Speter	**  If we are in SMTP opening state, send initial protocol.
281538032Speter	*/
281638032Speter
281738032Speter	if (bitnset(M_7BITS, m->m_flags) &&
281838032Speter	    (!clever || mci->mci_state == MCIS_OPENING))
281938032Speter		mci->mci_flags |= MCIF_7BIT;
282038032Speter	if (clever && mci->mci_state != MCIS_CLOSED)
282138032Speter	{
282290792Sgshapiro# if STARTTLS || SASL
282390792Sgshapiro		int dotpos;
282490792Sgshapiro		char *srvname;
282590792Sgshapiro		extern SOCKADDR CurHostAddr;
282690792Sgshapiro# endif /* STARTTLS || SASL */
282790792Sgshapiro
282890792Sgshapiro# if SASL
282971345Sgshapiro#  define DONE_AUTH(f)		bitset(MCIF_AUTHACT, f)
283090792Sgshapiro# endif /* SASL */
283164562Sgshapiro# if STARTTLS
283271345Sgshapiro#  define DONE_STARTTLS(f)	bitset(MCIF_TLSACT, f)
283364562Sgshapiro# endif /* STARTTLS */
283471345Sgshapiro# define ONLY_HELO(f)		bitset(MCIF_ONLY_EHLO, f)
283571345Sgshapiro# define SET_HELO(f)		f |= MCIF_ONLY_EHLO
283671345Sgshapiro# define CLR_HELO(f)		f &= ~MCIF_ONLY_EHLO
283738032Speter
283890792Sgshapiro# if STARTTLS || SASL
283990792Sgshapiro		/* don't use CurHostName, it is changed in many places */
284090792Sgshapiro		if (mci->mci_host != NULL)
284190792Sgshapiro		{
284290792Sgshapiro			srvname = mci->mci_host;
284390792Sgshapiro			dotpos = strlen(srvname) - 1;
284490792Sgshapiro			if (dotpos >= 0)
284590792Sgshapiro			{
284690792Sgshapiro				if (srvname[dotpos] == '.')
284790792Sgshapiro					srvname[dotpos] = '\0';
284890792Sgshapiro				else
284990792Sgshapiro					dotpos = -1;
285090792Sgshapiro			}
285190792Sgshapiro		}
285290792Sgshapiro		else if (mci->mci_mailer != NULL)
285390792Sgshapiro		{
285490792Sgshapiro			srvname = mci->mci_mailer->m_name;
285590792Sgshapiro			dotpos = -1;
285690792Sgshapiro		}
285790792Sgshapiro		else
285890792Sgshapiro		{
285990792Sgshapiro			srvname = "local";
286090792Sgshapiro			dotpos = -1;
286190792Sgshapiro		}
286271345Sgshapiro
286390792Sgshapiro		/* don't set {server_name} to NULL or "": see getauth() */
286490792Sgshapiro		macdefine(&mci->mci_macro, A_TEMP, macid("{server_name}"),
286590792Sgshapiro			  srvname);
286664562Sgshapiro
286790792Sgshapiro		/* CurHostAddr is set by makeconnection() and mci_get() */
286890792Sgshapiro		if (CurHostAddr.sa.sa_family != 0)
286990792Sgshapiro		{
287090792Sgshapiro			macdefine(&mci->mci_macro, A_TEMP,
287190792Sgshapiro				  macid("{server_addr}"),
287290792Sgshapiro				  anynet_ntoa(&CurHostAddr));
287390792Sgshapiro		}
287490792Sgshapiro		else if (mci->mci_mailer != NULL)
287590792Sgshapiro		{
287690792Sgshapiro			/* mailer name is unique, use it as address */
287790792Sgshapiro			macdefine(&mci->mci_macro, A_PERM,
287890792Sgshapiro				  macid("{server_addr}"),
287990792Sgshapiro				  mci->mci_mailer->m_name);
288090792Sgshapiro		}
288190792Sgshapiro		else
288290792Sgshapiro		{
288390792Sgshapiro			/* don't set it to NULL or "": see getauth() */
288490792Sgshapiro			macdefine(&mci->mci_macro, A_PERM,
288590792Sgshapiro				  macid("{server_addr}"), "0");
288690792Sgshapiro		}
288790792Sgshapiro
288890792Sgshapiro		/* undo change of srvname (mci->mci_host) */
288990792Sgshapiro		if (dotpos >= 0)
289090792Sgshapiro			srvname[dotpos] = '.';
289190792Sgshapiro
289290792Sgshapiroreconnect:	/* after switching to an encrypted connection */
289390792Sgshapiro# endif /* STARTTLS || SASL */
289490792Sgshapiro
289590792Sgshapiro		/* set the current connection information */
289690792Sgshapiro		e->e_mci = mci;
289764562Sgshapiro# if SASL
289864562Sgshapiro		mci->mci_saslcap = NULL;
289964562Sgshapiro# endif /* SASL */
290071345Sgshapiro		smtpinit(m, mci, e, ONLY_HELO(mci->mci_flags));
290171345Sgshapiro		CLR_HELO(mci->mci_flags);
290264562Sgshapiro
290390792Sgshapiro		if (IS_DLVR_RETURN(e))
290490792Sgshapiro		{
290590792Sgshapiro			/*
290690792Sgshapiro			**  Check whether other side can deliver e-mail
290790792Sgshapiro			**  fast enough
290890792Sgshapiro			*/
290990792Sgshapiro
291090792Sgshapiro			if (!bitset(MCIF_DLVR_BY, mci->mci_flags))
291190792Sgshapiro			{
291290792Sgshapiro				e->e_status = "5.4.7";
291390792Sgshapiro				usrerrenh(e->e_status,
291490792Sgshapiro					  "554 Server does not support Deliver By");
291590792Sgshapiro				rcode = EX_UNAVAILABLE;
291690792Sgshapiro				goto give_up;
291790792Sgshapiro			}
291890792Sgshapiro			if (e->e_deliver_by > 0 &&
291990792Sgshapiro			    e->e_deliver_by - (curtime() - e->e_ctime) <
292090792Sgshapiro			    mci->mci_min_by)
292190792Sgshapiro			{
292290792Sgshapiro				e->e_status = "5.4.7";
292390792Sgshapiro				usrerrenh(e->e_status,
292490792Sgshapiro					  "554 Message can't be delivered in time; %ld < %ld",
292590792Sgshapiro					  e->e_deliver_by - (curtime() - e->e_ctime),
292690792Sgshapiro					  mci->mci_min_by);
292790792Sgshapiro				rcode = EX_UNAVAILABLE;
292890792Sgshapiro				goto give_up;
292990792Sgshapiro			}
293090792Sgshapiro		}
293190792Sgshapiro
293264562Sgshapiro# if STARTTLS
293364562Sgshapiro		/* first TLS then AUTH to provide a security layer */
293471345Sgshapiro		if (mci->mci_state != MCIS_CLOSED &&
293571345Sgshapiro		    !DONE_STARTTLS(mci->mci_flags))
293664562Sgshapiro		{
293764562Sgshapiro			int olderrors;
293864562Sgshapiro			bool usetls;
293964562Sgshapiro			bool saveQuickAbort = QuickAbort;
294064562Sgshapiro			bool saveSuprErrs = SuprErrs;
294171345Sgshapiro			char *host = NULL;
294264562Sgshapiro
294364562Sgshapiro			rcode = EX_OK;
294464562Sgshapiro			usetls = bitset(MCIF_TLS, mci->mci_flags);
294590792Sgshapiro			if (usetls)
294690792Sgshapiro				usetls = !iscltflgset(e, D_NOTLS);
294766494Sgshapiro
294864562Sgshapiro			if (usetls)
294964562Sgshapiro			{
295090792Sgshapiro				host = macvalue(macid("{server_name}"), e);
295164562Sgshapiro				olderrors = Errors;
295290792Sgshapiro				QuickAbort = false;
295390792Sgshapiro				SuprErrs = true;
2954102528Sgshapiro				if (rscheck("try_tls", host, NULL, e,
2955102528Sgshapiro					    RSF_RMCOMM, 7, host, NOQID) != EX_OK
295664562Sgshapiro				    || Errors > olderrors)
295790792Sgshapiro					usetls = false;
295864562Sgshapiro				SuprErrs = saveSuprErrs;
295964562Sgshapiro				QuickAbort = saveQuickAbort;
296064562Sgshapiro			}
296164562Sgshapiro
296264562Sgshapiro			if (usetls)
296364562Sgshapiro			{
296464562Sgshapiro				if ((rcode = starttls(m, mci, e)) == EX_OK)
296564562Sgshapiro				{
296664562Sgshapiro					/* start again without STARTTLS */
296764562Sgshapiro					mci->mci_flags |= MCIF_TLSACT;
296864562Sgshapiro				}
296964562Sgshapiro				else
297064562Sgshapiro				{
297164562Sgshapiro					char *s;
297264562Sgshapiro
297364562Sgshapiro					/*
297464562Sgshapiro					**  TLS negotation failed, what to do?
297564562Sgshapiro					**  fall back to unencrypted connection
297664562Sgshapiro					**  or abort? How to decide?
297764562Sgshapiro					**  set a macro and call a ruleset.
297864562Sgshapiro					*/
297990792Sgshapiro
298064562Sgshapiro					mci->mci_flags &= ~MCIF_TLS;
298164562Sgshapiro					switch (rcode)
298264562Sgshapiro					{
298364562Sgshapiro					  case EX_TEMPFAIL:
298464562Sgshapiro						s = "TEMP";
298564562Sgshapiro						break;
298664562Sgshapiro					  case EX_USAGE:
298764562Sgshapiro						s = "USAGE";
298864562Sgshapiro						break;
298964562Sgshapiro					  case EX_PROTOCOL:
299064562Sgshapiro						s = "PROTOCOL";
299164562Sgshapiro						break;
299264562Sgshapiro					  case EX_SOFTWARE:
299364562Sgshapiro						s = "SOFTWARE";
299464562Sgshapiro						break;
299564562Sgshapiro
299664562Sgshapiro					  /* everything else is a failure */
299764562Sgshapiro					  default:
299864562Sgshapiro						s = "FAILURE";
299964562Sgshapiro						rcode = EX_TEMPFAIL;
300064562Sgshapiro					}
300190792Sgshapiro					macdefine(&e->e_macro, A_PERM,
300290792Sgshapiro						  macid("{verify}"), s);
300364562Sgshapiro				}
300464562Sgshapiro			}
300564562Sgshapiro			else
300690792Sgshapiro				macdefine(&e->e_macro, A_PERM,
300790792Sgshapiro					  macid("{verify}"), "NONE");
300864562Sgshapiro			olderrors = Errors;
300990792Sgshapiro			QuickAbort = false;
301090792Sgshapiro			SuprErrs = true;
301164562Sgshapiro
301264562Sgshapiro			/*
301364562Sgshapiro			**  rcode == EX_SOFTWARE is special:
301464562Sgshapiro			**  the TLS negotation failed
301564562Sgshapiro			**  we have to drop the connection no matter what
301664562Sgshapiro			**  However, we call tls_server to give it the chance
301764562Sgshapiro			**  to log the problem and return an appropriate
301864562Sgshapiro			**  error code.
301964562Sgshapiro			*/
302090792Sgshapiro
302164562Sgshapiro			if (rscheck("tls_server",
302290792Sgshapiro				    macvalue(macid("{verify}"), e),
3023102528Sgshapiro				    NULL, e, RSF_RMCOMM|RSF_COUNT, 5,
3024102528Sgshapiro				    host, NOQID) != EX_OK ||
302564562Sgshapiro			    Errors > olderrors ||
302664562Sgshapiro			    rcode == EX_SOFTWARE)
302764562Sgshapiro			{
302864562Sgshapiro				char enhsc[ENHSCLEN];
302964562Sgshapiro				extern char MsgBuf[];
303064562Sgshapiro
303164562Sgshapiro				if (ISSMTPCODE(MsgBuf) &&
303264562Sgshapiro				    extenhsc(MsgBuf + 4, ' ', enhsc) > 0)
303364562Sgshapiro				{
303490792Sgshapiro					p = sm_rpool_strdup_x(e->e_rpool,
303590792Sgshapiro							      MsgBuf);
303664562Sgshapiro				}
303764562Sgshapiro				else
303864562Sgshapiro				{
303964562Sgshapiro					p = "403 4.7.0 server not authenticated.";
304090792Sgshapiro					(void) sm_strlcpy(enhsc, "4.7.0",
304190792Sgshapiro							  sizeof enhsc);
304264562Sgshapiro				}
304364562Sgshapiro				SuprErrs = saveSuprErrs;
304464562Sgshapiro				QuickAbort = saveQuickAbort;
304564562Sgshapiro
304664562Sgshapiro				if (rcode == EX_SOFTWARE)
304764562Sgshapiro				{
304864562Sgshapiro					/* drop the connection */
304964562Sgshapiro					mci->mci_state = MCIS_QUITING;
305064562Sgshapiro					if (mci->mci_in != NULL)
305164562Sgshapiro					{
305290792Sgshapiro						(void) sm_io_close(mci->mci_in,
305390792Sgshapiro								   SM_TIME_DEFAULT);
305464562Sgshapiro						mci->mci_in = NULL;
305564562Sgshapiro					}
305664562Sgshapiro					mci->mci_flags &= ~MCIF_TLSACT;
305764562Sgshapiro					(void) endmailer(mci, e, pv);
305864562Sgshapiro				}
305964562Sgshapiro				else
306064562Sgshapiro				{
306164562Sgshapiro					/* abort transfer */
306264562Sgshapiro					smtpquit(m, mci, e);
306364562Sgshapiro				}
306464562Sgshapiro
306571345Sgshapiro				/* avoid bogus error msg */
306671345Sgshapiro				mci->mci_errno = 0;
306771345Sgshapiro
306864562Sgshapiro				/* temp or permanent failure? */
306964562Sgshapiro				rcode = (*p == '4') ? EX_TEMPFAIL
307064562Sgshapiro						    : EX_UNAVAILABLE;
307190792Sgshapiro				mci_setstat(mci, rcode, enhsc, p);
307264562Sgshapiro
307364562Sgshapiro				/*
307464562Sgshapiro				**  hack to get the error message into
307564562Sgshapiro				**  the envelope (done in giveresponse())
307664562Sgshapiro				*/
307790792Sgshapiro
307890792Sgshapiro				(void) sm_strlcpy(SmtpError, p,
307990792Sgshapiro						  sizeof SmtpError);
308064562Sgshapiro			}
308164562Sgshapiro			QuickAbort = saveQuickAbort;
308264562Sgshapiro			SuprErrs = saveSuprErrs;
308371345Sgshapiro			if (DONE_STARTTLS(mci->mci_flags) &&
308471345Sgshapiro			    mci->mci_state != MCIS_CLOSED)
308564562Sgshapiro			{
308671345Sgshapiro				SET_HELO(mci->mci_flags);
308764562Sgshapiro				mci->mci_flags &= ~MCIF_EXTENS;
308864562Sgshapiro				goto reconnect;
308964562Sgshapiro			}
309064562Sgshapiro		}
309164562Sgshapiro# endif /* STARTTLS */
309264562Sgshapiro# if SASL
309364562Sgshapiro		/* if other server supports authentication let's authenticate */
309464562Sgshapiro		if (mci->mci_state != MCIS_CLOSED &&
309564562Sgshapiro		    mci->mci_saslcap != NULL &&
309690792Sgshapiro		    !DONE_AUTH(mci->mci_flags) && !iscltflgset(e, D_NOAUTH))
309764562Sgshapiro		{
309890792Sgshapiro			/* Should we require some minimum authentication? */
309990792Sgshapiro			if ((ret = smtpauth(m, mci, e)) == EX_OK)
310064562Sgshapiro			{
310164562Sgshapiro				int result;
310290792Sgshapiro				sasl_ssf_t *ssf = NULL;
310364562Sgshapiro
310490792Sgshapiro				/* Get security strength (features) */
310564562Sgshapiro				result = sasl_getprop(mci->mci_conn, SASL_SSF,
310698121Sgshapiro# if SASL >= 20000
310798121Sgshapiro						      (const void **) &ssf);
310898121Sgshapiro# else /* SASL >= 20000 */
310964562Sgshapiro						      (void **) &ssf);
311098121Sgshapiro# endif /* SASL >= 20000 */
311190792Sgshapiro
311290792Sgshapiro				/* XXX authid? */
311364562Sgshapiro				if (LogLevel > 9)
311464562Sgshapiro					sm_syslog(LOG_INFO, NOQID,
311590792Sgshapiro						  "AUTH=client, relay=%.100s, mech=%.16s, bits=%d",
311664562Sgshapiro						  mci->mci_host,
311790792Sgshapiro						  macvalue(macid("{auth_type}"), e),
311890792Sgshapiro						  result == SASL_OK ? *ssf : 0);
311977349Sgshapiro
312064562Sgshapiro				/*
312190792Sgshapiro				**  Only switch to encrypted connection
312264562Sgshapiro				**  if a security layer has been negotiated
312364562Sgshapiro				*/
312490792Sgshapiro
312564562Sgshapiro				if (result == SASL_OK && *ssf > 0)
312664562Sgshapiro				{
312764562Sgshapiro					/*
312890792Sgshapiro					**  Convert I/O layer to use SASL.
312990792Sgshapiro					**  If the call fails, the connection
313090792Sgshapiro					**  is aborted.
313164562Sgshapiro					*/
313290792Sgshapiro
313390792Sgshapiro					if (sfdcsasl(&mci->mci_in,
313490792Sgshapiro						     &mci->mci_out,
313564562Sgshapiro						     mci->mci_conn) == 0)
313664562Sgshapiro					{
313764562Sgshapiro						mci->mci_flags &= ~MCIF_EXTENS;
313890792Sgshapiro						mci->mci_flags |= MCIF_AUTHACT|
313990792Sgshapiro								  MCIF_ONLY_EHLO;
314064562Sgshapiro						goto reconnect;
314164562Sgshapiro					}
314290792Sgshapiro					syserr("AUTH TLS switch failed in client");
314364562Sgshapiro				}
314464562Sgshapiro				/* else? XXX */
314564562Sgshapiro				mci->mci_flags |= MCIF_AUTHACT;
314664562Sgshapiro
314764562Sgshapiro			}
314890792Sgshapiro			else if (ret == EX_TEMPFAIL)
314990792Sgshapiro			{
315090792Sgshapiro				if (LogLevel > 8)
315190792Sgshapiro					sm_syslog(LOG_ERR, NOQID,
315290792Sgshapiro						  "AUTH=client, relay=%.100s, temporary failure, connection abort",
315390792Sgshapiro						  mci->mci_host);
315490792Sgshapiro				smtpquit(m, mci, e);
315590792Sgshapiro
315690792Sgshapiro				/* avoid bogus error msg */
315790792Sgshapiro				mci->mci_errno = 0;
315890792Sgshapiro				rcode = EX_TEMPFAIL;
3159132943Sgshapiro				mci_setstat(mci, rcode, "4.3.0", p);
316090792Sgshapiro
316190792Sgshapiro				/*
316290792Sgshapiro				**  hack to get the error message into
316390792Sgshapiro				**  the envelope (done in giveresponse())
316490792Sgshapiro				*/
316590792Sgshapiro
316690792Sgshapiro				(void) sm_strlcpy(SmtpError,
316790792Sgshapiro						  "Temporary AUTH failure",
316890792Sgshapiro						  sizeof SmtpError);
316990792Sgshapiro			}
317064562Sgshapiro		}
317164562Sgshapiro# endif /* SASL */
317238032Speter	}
317338032Speter
317464562Sgshapiro
317538032Speterdo_transfer:
317638032Speter	/* clear out per-message flags from connection structure */
317738032Speter	mci->mci_flags &= ~(MCIF_CVT7TO8|MCIF_CVT8TO7);
317838032Speter
317938032Speter	if (bitset(EF_HAS8BIT, e->e_flags) &&
318038032Speter	    !bitset(EF_DONT_MIME, e->e_flags) &&
318138032Speter	    bitnset(M_7BITS, m->m_flags))
318238032Speter		mci->mci_flags |= MCIF_CVT8TO7;
318338032Speter
318438032Speter#if MIME7TO8
318538032Speter	if (bitnset(M_MAKE8BIT, m->m_flags) &&
318638032Speter	    !bitset(MCIF_7BIT, mci->mci_flags) &&
318738032Speter	    (p = hvalue("Content-Transfer-Encoding", e->e_header)) != NULL &&
318890792Sgshapiro	     (sm_strcasecmp(p, "quoted-printable") == 0 ||
318990792Sgshapiro	      sm_strcasecmp(p, "base64") == 0) &&
319038032Speter	    (p = hvalue("Content-Type", e->e_header)) != NULL)
319138032Speter	{
319238032Speter		/* may want to convert 7 -> 8 */
319338032Speter		/* XXX should really parse it here -- and use a class XXX */
319490792Sgshapiro		if (sm_strncasecmp(p, "text/plain", 10) == 0 &&
319538032Speter		    (p[10] == '\0' || p[10] == ' ' || p[10] == ';'))
319638032Speter			mci->mci_flags |= MCIF_CVT7TO8;
319738032Speter	}
319864562Sgshapiro#endif /* MIME7TO8 */
319938032Speter
320038032Speter	if (tTd(11, 1))
320138032Speter	{
320290792Sgshapiro		sm_dprintf("openmailer: ");
3203132943Sgshapiro		mci_dump(sm_debug_file(), mci, false);
320438032Speter	}
320538032Speter
320690792Sgshapiro#if _FFR_CLIENT_SIZE
320790792Sgshapiro	/*
320890792Sgshapiro	**  See if we know the maximum size and
320990792Sgshapiro	**  abort if the message is too big.
321090792Sgshapiro	**
321190792Sgshapiro	**  NOTE: _FFR_CLIENT_SIZE is untested.
321290792Sgshapiro	*/
321390792Sgshapiro
321490792Sgshapiro	if (bitset(MCIF_SIZE, mci->mci_flags) &&
321590792Sgshapiro	    mci->mci_maxsize > 0 &&
321690792Sgshapiro	    e->e_msgsize > mci->mci_maxsize)
321790792Sgshapiro	{
321890792Sgshapiro		e->e_flags |= EF_NO_BODY_RETN;
321990792Sgshapiro		if (bitnset(M_LOCALMAILER, m->m_flags))
322090792Sgshapiro			e->e_status = "5.2.3";
322190792Sgshapiro		else
322290792Sgshapiro			e->e_status = "5.3.4";
322390792Sgshapiro
322490792Sgshapiro		usrerrenh(e->e_status,
322590792Sgshapiro			  "552 Message is too large; %ld bytes max",
322690792Sgshapiro			  mci->mci_maxsize);
322790792Sgshapiro		rcode = EX_DATAERR;
322890792Sgshapiro
322990792Sgshapiro		/* Need an e_message for error */
323090792Sgshapiro		(void) sm_snprintf(SmtpError, sizeof SmtpError,
323190792Sgshapiro				   "Message is too large; %ld bytes max",
323290792Sgshapiro				   mci->mci_maxsize);
323390792Sgshapiro		goto give_up;
323490792Sgshapiro	}
323590792Sgshapiro#endif /* _FFR_CLIENT_SIZE */
323690792Sgshapiro
323738032Speter	if (mci->mci_state != MCIS_OPEN)
323838032Speter	{
323938032Speter		/* couldn't open the mailer */
324038032Speter		rcode = mci->mci_exitstat;
324138032Speter		errno = mci->mci_errno;
324273188Sgshapiro		SM_SET_H_ERRNO(mci->mci_herrno);
324338032Speter		if (rcode == EX_OK)
324438032Speter		{
324538032Speter			/* shouldn't happen */
324664562Sgshapiro			syserr("554 5.3.5 deliver: mci=%lx rcode=%d errno=%d state=%d sig=%s",
324790792Sgshapiro			       (unsigned long) mci, rcode, errno,
324890792Sgshapiro			       mci->mci_state, firstsig);
3249132943Sgshapiro			mci_dump_all(smioout, true);
325038032Speter			rcode = EX_SOFTWARE;
325138032Speter		}
325264562Sgshapiro		else if (nummxhosts > hostnum)
325338032Speter		{
325438032Speter			/* try next MX site */
325538032Speter			goto tryhost;
325638032Speter		}
325738032Speter	}
325838032Speter	else if (!clever)
325938032Speter	{
326038032Speter		/*
326138032Speter		**  Format and send message.
326238032Speter		*/
326338032Speter
326438032Speter		putfromline(mci, e);
326543730Speter		(*e->e_puthdr)(mci, e->e_header, e, M87F_OUTER);
326638032Speter		(*e->e_putbody)(mci, e, NULL);
326738032Speter
326838032Speter		/* get the exit status */
326938032Speter		rcode = endmailer(mci, e, pv);
327090792Sgshapiro		if (rcode == EX_TEMPFAIL && SmtpError[0] == '\0')
327173188Sgshapiro		{
327273188Sgshapiro			/*
327373188Sgshapiro			**  Need an e_message for mailq display.
327473188Sgshapiro			**  We set SmtpError as
327573188Sgshapiro			*/
327673188Sgshapiro
327790792Sgshapiro			(void) sm_snprintf(SmtpError, sizeof SmtpError,
327890792Sgshapiro					   "%s mailer (%s) exited with EX_TEMPFAIL",
327990792Sgshapiro					   m->m_name, m->m_mailer);
328073188Sgshapiro		}
328138032Speter	}
328238032Speter	else
328338032Speter	{
328438032Speter		/*
328538032Speter		**  Send the MAIL FROM: protocol
328638032Speter		*/
328738032Speter
328890792Sgshapiro		/* XXX this isn't pipelined... */
328938032Speter		rcode = smtpmailfrom(m, mci, e);
329038032Speter		if (rcode == EX_OK)
329138032Speter		{
329238032Speter			register int i;
329390792Sgshapiro# if PIPELINING
329490792Sgshapiro			ADDRESS *volatile pchain;
329590792Sgshapiro# endif /* PIPELINING */
329638032Speter
329738032Speter			/* send the recipient list */
329838032Speter			tobuf[0] = '\0';
329990792Sgshapiro			mci->mci_retryrcpt = false;
330090792Sgshapiro			mci->mci_tolist = tobuf;
330190792Sgshapiro# if PIPELINING
330290792Sgshapiro			pchain = NULL;
330390792Sgshapiro			mci->mci_nextaddr = NULL;
330490792Sgshapiro# endif /* PIPELINING */
330564562Sgshapiro
330638032Speter			for (to = tochain; to != NULL; to = to->q_tchain)
330738032Speter			{
330890792Sgshapiro				if (!QS_IS_UNMARKED(to->q_state))
330938032Speter					continue;
331064562Sgshapiro
331190792Sgshapiro				/* mark recipient state as "ok so far" */
331290792Sgshapiro				to->q_state = QS_OK;
331390792Sgshapiro				e->e_to = to->q_paddr;
331464562Sgshapiro# if STARTTLS
331564562Sgshapiro				i = rscheck("tls_rcpt", to->q_user, NULL, e,
3316102528Sgshapiro					    RSF_RMCOMM|RSF_COUNT, 3,
3317102528Sgshapiro					    mci->mci_host, e->e_id);
331864562Sgshapiro				if (i != EX_OK)
331938032Speter				{
332090792Sgshapiro					markfailure(e, to, mci, i, false);
332190792Sgshapiro					giveresponse(i, to->q_status,  m, mci,
332290792Sgshapiro						     ctladdr, xstart, e, to);
332390792Sgshapiro					if (i == EX_TEMPFAIL)
332490792Sgshapiro					{
332590792Sgshapiro						mci->mci_retryrcpt = true;
332690792Sgshapiro						to->q_state = QS_RETRY;
332790792Sgshapiro					}
332864562Sgshapiro					continue;
332938032Speter				}
333064562Sgshapiro# endif /* STARTTLS */
333164562Sgshapiro
333290792Sgshapiro				i = smtprcpt(to, m, mci, e, ctladdr, xstart);
333390792Sgshapiro# if PIPELINING
333490792Sgshapiro				if (i == EX_OK &&
333590792Sgshapiro				    bitset(MCIF_PIPELINED, mci->mci_flags))
333664562Sgshapiro				{
333790792Sgshapiro					/*
333890792Sgshapiro					**  Add new element to list of
333990792Sgshapiro					**  recipients for pipelining.
334090792Sgshapiro					*/
334190792Sgshapiro
334290792Sgshapiro					to->q_pchain = NULL;
334390792Sgshapiro					if (mci->mci_nextaddr == NULL)
334490792Sgshapiro						mci->mci_nextaddr = to;
334590792Sgshapiro					if (pchain == NULL)
334690792Sgshapiro						pchain = to;
334790792Sgshapiro					else
334890792Sgshapiro					{
334990792Sgshapiro						pchain->q_pchain = to;
335090792Sgshapiro						pchain = pchain->q_pchain;
335190792Sgshapiro					}
335264562Sgshapiro				}
335390792Sgshapiro# endif /* PIPELINING */
335490792Sgshapiro				if (i != EX_OK)
335538032Speter				{
335690792Sgshapiro					markfailure(e, to, mci, i, false);
335798841Sgshapiro					giveresponse(i, to->q_status, m, mci,
335890792Sgshapiro						     ctladdr, xstart, e, to);
335990792Sgshapiro					if (i == EX_TEMPFAIL)
336090792Sgshapiro						to->q_state = QS_RETRY;
336138032Speter				}
336238032Speter			}
336338032Speter
336490792Sgshapiro			/* No recipients in list and no missing responses? */
336590792Sgshapiro			if (tobuf[0] == '\0'
336690792Sgshapiro# if PIPELINING
336790792Sgshapiro			    && mci->mci_nextaddr == NULL
336890792Sgshapiro# endif /* PIPELINING */
336990792Sgshapiro			   )
337038032Speter			{
337138032Speter				rcode = EX_OK;
337238032Speter				e->e_to = NULL;
337338032Speter				if (bitset(MCIF_CACHED, mci->mci_flags))
337438032Speter					smtprset(m, mci, e);
337538032Speter			}
337638032Speter			else
337738032Speter			{
337838032Speter				e->e_to = tobuf + 1;
337990792Sgshapiro				rcode = smtpdata(m, mci, e, ctladdr, xstart);
338038032Speter			}
338138032Speter		}
338264562Sgshapiro		if (rcode == EX_TEMPFAIL && nummxhosts > hostnum)
338338032Speter		{
338438032Speter			/* try next MX site */
338538032Speter			goto tryhost;
338638032Speter		}
338738032Speter	}
338838032Speter#if NAMED_BIND
338938032Speter	if (ConfigLevel < 2)
339038032Speter		_res.options |= RES_DEFNAMES | RES_DNSRCH;	/* XXX */
339164562Sgshapiro#endif /* NAMED_BIND */
339238032Speter
339338032Speter	if (tTd(62, 1))
339438032Speter		checkfds("after delivery");
339538032Speter
339638032Speter	/*
339738032Speter	**  Do final status disposal.
339838032Speter	**	We check for something in tobuf for the SMTP case.
339938032Speter	**	If we got a temporary failure, arrange to queue the
340038032Speter	**		addressees.
340138032Speter	*/
340238032Speter
340338032Speter  give_up:
340438032Speter	if (bitnset(M_LMTP, m->m_flags))
340538032Speter	{
340638032Speter		lmtp_rcode = rcode;
340738032Speter		tobuf[0] = '\0';
340890792Sgshapiro		anyok = false;
340990792Sgshapiro		strsize = 0;
341038032Speter	}
341138032Speter	else
341238032Speter		anyok = rcode == EX_OK;
341338032Speter
341438032Speter	for (to = tochain; to != NULL; to = to->q_tchain)
341538032Speter	{
341638032Speter		/* see if address already marked */
341764562Sgshapiro		if (!QS_IS_OK(to->q_state))
341838032Speter			continue;
341938032Speter
342038032Speter		/* if running LMTP, get the status for each address */
342138032Speter		if (bitnset(M_LMTP, m->m_flags))
342238032Speter		{
342338032Speter			if (lmtp_rcode == EX_OK)
342438032Speter				rcode = smtpgetstat(m, mci, e);
342538032Speter			if (rcode == EX_OK)
342638032Speter			{
342790792Sgshapiro				strsize += sm_strlcat2(tobuf + strsize, ",",
342890792Sgshapiro						to->q_paddr,
342990792Sgshapiro						tobufsize - strsize);
343090792Sgshapiro				SM_ASSERT(strsize < tobufsize);
343190792Sgshapiro				anyok = true;
343238032Speter			}
343338032Speter			else
343438032Speter			{
343538032Speter				e->e_to = to->q_paddr;
343690792Sgshapiro				markfailure(e, to, mci, rcode, true);
343764562Sgshapiro				giveresponse(rcode, to->q_status, m, mci,
343890792Sgshapiro					     ctladdr, xstart, e, to);
343938032Speter				e->e_to = tobuf + 1;
344038032Speter				continue;
344138032Speter			}
344238032Speter		}
344338032Speter		else
344438032Speter		{
344538032Speter			/* mark bad addresses */
344638032Speter			if (rcode != EX_OK)
344738032Speter			{
344838032Speter				if (goodmxfound && rcode == EX_NOHOST)
344938032Speter					rcode = EX_TEMPFAIL;
345090792Sgshapiro				markfailure(e, to, mci, rcode, true);
345138032Speter				continue;
345238032Speter			}
345338032Speter		}
345438032Speter
345538032Speter		/* successful delivery */
345664562Sgshapiro		to->q_state = QS_SENT;
345738032Speter		to->q_statdate = curtime();
345838032Speter		e->e_nsent++;
345964562Sgshapiro
346064562Sgshapiro		/*
346164562Sgshapiro		**  Checkpoint the send list every few addresses
346264562Sgshapiro		*/
346364562Sgshapiro
346466494Sgshapiro		if (CheckpointInterval > 0 && e->e_nsent >= CheckpointInterval)
346564562Sgshapiro		{
346690792Sgshapiro			queueup(e, false, false);
346764562Sgshapiro			e->e_nsent = 0;
346864562Sgshapiro		}
346964562Sgshapiro
347038032Speter		if (bitnset(M_LOCALMAILER, m->m_flags) &&
347138032Speter		    bitset(QPINGONSUCCESS, to->q_flags))
347238032Speter		{
347338032Speter			to->q_flags |= QDELIVERED;
347438032Speter			to->q_status = "2.1.5";
347590792Sgshapiro			(void) sm_io_fprintf(e->e_xfp, SM_TIME_DEFAULT,
347690792Sgshapiro					     "%s... Successfully delivered\n",
347790792Sgshapiro					     to->q_paddr);
347838032Speter		}
347938032Speter		else if (bitset(QPINGONSUCCESS, to->q_flags) &&
348038032Speter			 bitset(QPRIMARY, to->q_flags) &&
348138032Speter			 !bitset(MCIF_DSN, mci->mci_flags))
348238032Speter		{
348338032Speter			to->q_flags |= QRELAYED;
348490792Sgshapiro			(void) sm_io_fprintf(e->e_xfp, SM_TIME_DEFAULT,
348590792Sgshapiro					     "%s... relayed; expect no further notifications\n",
348690792Sgshapiro					     to->q_paddr);
348738032Speter		}
348890792Sgshapiro		else if (IS_DLVR_NOTIFY(e) &&
348990792Sgshapiro			 !bitset(MCIF_DLVR_BY, mci->mci_flags) &&
349090792Sgshapiro			 bitset(QPRIMARY, to->q_flags) &&
349190792Sgshapiro			 (!bitset(QHASNOTIFY, to->q_flags) ||
349290792Sgshapiro			  bitset(QPINGONSUCCESS, to->q_flags) ||
349390792Sgshapiro			  bitset(QPINGONFAILURE, to->q_flags) ||
349490792Sgshapiro			  bitset(QPINGONDELAY, to->q_flags)))
349590792Sgshapiro		{
349690792Sgshapiro			/* RFC 2852, 4.1.4.2: no NOTIFY, or not NEVER */
349790792Sgshapiro			to->q_flags |= QBYNRELAY;
349890792Sgshapiro			(void) sm_io_fprintf(e->e_xfp, SM_TIME_DEFAULT,
349990792Sgshapiro					     "%s... Deliver-by notify: relayed\n",
350090792Sgshapiro					     to->q_paddr);
350190792Sgshapiro		}
350290792Sgshapiro		else if (IS_DLVR_TRACE(e) &&
350390792Sgshapiro			 (!bitset(QHASNOTIFY, to->q_flags) ||
350490792Sgshapiro			  bitset(QPINGONSUCCESS, to->q_flags) ||
350590792Sgshapiro			  bitset(QPINGONFAILURE, to->q_flags) ||
350690792Sgshapiro			  bitset(QPINGONDELAY, to->q_flags)) &&
350790792Sgshapiro			 bitset(QPRIMARY, to->q_flags))
350890792Sgshapiro		{
350990792Sgshapiro			/* RFC 2852, 4.1.4: no NOTIFY, or not NEVER */
351090792Sgshapiro			to->q_flags |= QBYTRACE;
351190792Sgshapiro			(void) sm_io_fprintf(e->e_xfp, SM_TIME_DEFAULT,
351290792Sgshapiro					     "%s... Deliver-By trace: relayed\n",
351390792Sgshapiro					     to->q_paddr);
351490792Sgshapiro		}
351538032Speter	}
351638032Speter
351738032Speter	if (bitnset(M_LMTP, m->m_flags))
351838032Speter	{
351938032Speter		/*
352038032Speter		**  Global information applies to the last recipient only;
352138032Speter		**  clear it out to avoid bogus errors.
352238032Speter		*/
352338032Speter
352438032Speter		rcode = EX_OK;
352538032Speter		e->e_statmsg = NULL;
352638032Speter
352738032Speter		/* reset the mci state for the next transaction */
352890792Sgshapiro		if (mci != NULL &&
352990792Sgshapiro		    (mci->mci_state == MCIS_MAIL ||
353090792Sgshapiro		     mci->mci_state == MCIS_RCPT ||
353190792Sgshapiro		     mci->mci_state == MCIS_DATA))
3532125820Sgshapiro		{
353338032Speter			mci->mci_state = MCIS_OPEN;
3534125820Sgshapiro			SmtpPhase = mci->mci_phase = "idle";
3535125820Sgshapiro			sm_setproctitle(true, e, "%s: %s", CurHostName,
3536125820Sgshapiro					mci->mci_phase);
3537125820Sgshapiro		}
353838032Speter	}
353938032Speter
354038032Speter	if (tobuf[0] != '\0')
354190792Sgshapiro	{
354290792Sgshapiro		giveresponse(rcode, NULL, m, mci, ctladdr, xstart, e, tochain);
354390792Sgshapiro#if 0
354490792Sgshapiro		/*
354590792Sgshapiro		**  This code is disabled for now because I am not
354690792Sgshapiro		**  sure that copying status from the first recipient
354790792Sgshapiro		**  to all non-status'ed recipients is a good idea.
354890792Sgshapiro		*/
354990792Sgshapiro
355090792Sgshapiro		if (tochain->q_message != NULL &&
355190792Sgshapiro		    !bitnset(M_LMTP, m->m_flags) && rcode != EX_OK)
355290792Sgshapiro		{
355390792Sgshapiro			for (to = tochain->q_tchain; to != NULL;
355490792Sgshapiro			     to = to->q_tchain)
355590792Sgshapiro			{
355690792Sgshapiro				/* see if address already marked */
355790792Sgshapiro				if (QS_IS_QUEUEUP(to->q_state) &&
355890792Sgshapiro				    to->q_message == NULL)
355990792Sgshapiro					to->q_message = sm_rpool_strdup_x(e->e_rpool,
356090792Sgshapiro							tochain->q_message);
356190792Sgshapiro			}
356290792Sgshapiro		}
356390792Sgshapiro#endif /* 0 */
356490792Sgshapiro	}
356538032Speter	if (anyok)
356690792Sgshapiro		markstats(e, tochain, STATS_NORMAL);
356738032Speter	mci_store_persistent(mci);
356838032Speter
356990792Sgshapiro	/* Some recipients were tempfailed, try them on the next host */
357090792Sgshapiro	if (mci != NULL && mci->mci_retryrcpt && nummxhosts > hostnum)
357190792Sgshapiro	{
357290792Sgshapiro		/* try next MX site */
357390792Sgshapiro		goto tryhost;
357490792Sgshapiro	}
357590792Sgshapiro
357638032Speter	/* now close the connection */
357738032Speter	if (clever && mci != NULL && mci->mci_state != MCIS_CLOSED &&
357838032Speter	    !bitset(MCIF_CACHED, mci->mci_flags))
357938032Speter		smtpquit(m, mci, e);
358038032Speter
358190792Sgshapirocleanup: ;
358290792Sgshapiro	}
358390792Sgshapiro	SM_FINALLY
358490792Sgshapiro	{
358590792Sgshapiro		/*
358690792Sgshapiro		**  Restore state and return.
358790792Sgshapiro		*/
358838032Speter#if XDEBUG
358938032Speter		char wbuf[MAXLINE];
359038032Speter
359138032Speter		/* make absolutely certain 0, 1, and 2 are in use */
359290792Sgshapiro		(void) sm_snprintf(wbuf, sizeof wbuf,
359390792Sgshapiro				   "%s... end of deliver(%s)",
359490792Sgshapiro				   e->e_to == NULL ? "NO-TO-LIST"
359590792Sgshapiro						   : shortenstring(e->e_to,
359690792Sgshapiro								   MAXSHORTSTR),
359790792Sgshapiro				  m->m_name);
359838032Speter		checkfd012(wbuf);
359964562Sgshapiro#endif /* XDEBUG */
360038032Speter
360190792Sgshapiro		errno = 0;
360290792Sgshapiro
360390792Sgshapiro		/*
360490792Sgshapiro		**  It was originally necessary to set macro 'g' to NULL
360590792Sgshapiro		**  because it previously pointed to an auto buffer.
360690792Sgshapiro		**  We don't do this any more, so this may be unnecessary.
360790792Sgshapiro		*/
360890792Sgshapiro
360990792Sgshapiro		macdefine(&e->e_macro, A_PERM, 'g', (char *) NULL);
361090792Sgshapiro		e->e_to = NULL;
361190792Sgshapiro	}
361290792Sgshapiro	SM_END_TRY
361364562Sgshapiro	return rcode;
361438032Speter}
361564562Sgshapiro
361690792Sgshapiro/*
361738032Speter**  MARKFAILURE -- mark a failure on a specific address.
361838032Speter**
361938032Speter**	Parameters:
362038032Speter**		e -- the envelope we are sending.
362138032Speter**		q -- the address to mark.
362238032Speter**		mci -- mailer connection information.
362338032Speter**		rcode -- the code signifying the particular failure.
362464562Sgshapiro**		ovr -- override an existing code?
362538032Speter**
362638032Speter**	Returns:
362738032Speter**		none.
362838032Speter**
362938032Speter**	Side Effects:
363038032Speter**		marks the address (and possibly the envelope) with the
363138032Speter**			failure so that an error will be returned or
363238032Speter**			the message will be queued, as appropriate.
363338032Speter*/
363438032Speter
363590792Sgshapirovoid
363664562Sgshapiromarkfailure(e, q, mci, rcode, ovr)
363738032Speter	register ENVELOPE *e;
363838032Speter	register ADDRESS *q;
363938032Speter	register MCI *mci;
364038032Speter	int rcode;
364164562Sgshapiro	bool ovr;
364238032Speter{
364390792Sgshapiro	int save_errno = errno;
364464562Sgshapiro	char *status = NULL;
364564562Sgshapiro	char *rstatus = NULL;
364638032Speter
364738032Speter	switch (rcode)
364838032Speter	{
364938032Speter	  case EX_OK:
365038032Speter		break;
365138032Speter
365238032Speter	  case EX_TEMPFAIL:
365338032Speter	  case EX_IOERR:
365438032Speter	  case EX_OSERR:
365564562Sgshapiro		q->q_state = QS_QUEUEUP;
365638032Speter		break;
365738032Speter
365838032Speter	  default:
365964562Sgshapiro		q->q_state = QS_BADADDR;
366038032Speter		break;
366138032Speter	}
366238032Speter
366338032Speter	/* find most specific error code possible */
366438032Speter	if (mci != NULL && mci->mci_status != NULL)
366538032Speter	{
366690792Sgshapiro		status = sm_rpool_strdup_x(e->e_rpool, mci->mci_status);
366738032Speter		if (mci->mci_rstatus != NULL)
366890792Sgshapiro			rstatus = sm_rpool_strdup_x(e->e_rpool,
366990792Sgshapiro						    mci->mci_rstatus);
367038032Speter		else
367164562Sgshapiro			rstatus = NULL;
367238032Speter	}
367338032Speter	else if (e->e_status != NULL)
367438032Speter	{
367564562Sgshapiro		status = e->e_status;
367664562Sgshapiro		rstatus = NULL;
367738032Speter	}
367838032Speter	else
367938032Speter	{
368038032Speter		switch (rcode)
368138032Speter		{
368238032Speter		  case EX_USAGE:
368364562Sgshapiro			status = "5.5.4";
368438032Speter			break;
368538032Speter
368638032Speter		  case EX_DATAERR:
368764562Sgshapiro			status = "5.5.2";
368838032Speter			break;
368938032Speter
369038032Speter		  case EX_NOUSER:
369164562Sgshapiro			status = "5.1.1";
369238032Speter			break;
369338032Speter
369438032Speter		  case EX_NOHOST:
369564562Sgshapiro			status = "5.1.2";
369638032Speter			break;
369738032Speter
369838032Speter		  case EX_NOINPUT:
369938032Speter		  case EX_CANTCREAT:
370038032Speter		  case EX_NOPERM:
370164562Sgshapiro			status = "5.3.0";
370238032Speter			break;
370338032Speter
370438032Speter		  case EX_UNAVAILABLE:
370538032Speter		  case EX_SOFTWARE:
370638032Speter		  case EX_OSFILE:
370738032Speter		  case EX_PROTOCOL:
370838032Speter		  case EX_CONFIG:
370964562Sgshapiro			status = "5.5.0";
371038032Speter			break;
371138032Speter
371238032Speter		  case EX_OSERR:
371338032Speter		  case EX_IOERR:
371464562Sgshapiro			status = "4.5.0";
371538032Speter			break;
371638032Speter
371738032Speter		  case EX_TEMPFAIL:
371864562Sgshapiro			status = "4.2.0";
371938032Speter			break;
372038032Speter		}
372138032Speter	}
372238032Speter
372364562Sgshapiro	/* new status? */
372464562Sgshapiro	if (status != NULL && *status != '\0' && (ovr || q->q_status == NULL ||
372564562Sgshapiro	    *q->q_status == '\0' || *q->q_status < *status))
372664562Sgshapiro	{
372764562Sgshapiro		q->q_status = status;
372864562Sgshapiro		q->q_rstatus = rstatus;
372964562Sgshapiro	}
373038032Speter	if (rcode != EX_OK && q->q_rstatus == NULL &&
373138032Speter	    q->q_mailer != NULL && q->q_mailer->m_diagtype != NULL &&
373290792Sgshapiro	    sm_strcasecmp(q->q_mailer->m_diagtype, "X-UNIX") == 0)
373338032Speter	{
373464562Sgshapiro		char buf[16];
373538032Speter
373690792Sgshapiro		(void) sm_snprintf(buf, sizeof buf, "%d", rcode);
373790792Sgshapiro		q->q_rstatus = sm_rpool_strdup_x(e->e_rpool, buf);
373838032Speter	}
373964562Sgshapiro
374064562Sgshapiro	q->q_statdate = curtime();
374164562Sgshapiro	if (CurHostName != NULL && CurHostName[0] != '\0' &&
374264562Sgshapiro	    mci != NULL && !bitset(M_LOCALMAILER, mci->mci_flags))
374390792Sgshapiro		q->q_statmta = sm_rpool_strdup_x(e->e_rpool, CurHostName);
374490792Sgshapiro
374590792Sgshapiro	/* restore errno */
374690792Sgshapiro	errno = save_errno;
374738032Speter}
374890792Sgshapiro/*
374938032Speter**  ENDMAILER -- Wait for mailer to terminate.
375038032Speter**
375138032Speter**	We should never get fatal errors (e.g., segmentation
375238032Speter**	violation), so we report those specially.  For other
375338032Speter**	errors, we choose a status message (into statmsg),
375438032Speter**	and if it represents an error, we print it.
375538032Speter**
375638032Speter**	Parameters:
375780785Sgshapiro**		mci -- the mailer connection info.
375838032Speter**		e -- the current envelope.
375938032Speter**		pv -- the parameter vector that invoked the mailer
376038032Speter**			(for error messages).
376138032Speter**
376238032Speter**	Returns:
376338032Speter**		exit code of mailer.
376438032Speter**
376538032Speter**	Side Effects:
376638032Speter**		none.
376738032Speter*/
376838032Speter
376964562Sgshapirostatic jmp_buf	EndWaitTimeout;
377064562Sgshapiro
377164562Sgshapirostatic void
3772141858Sgshapiroendwaittimeout(ignore)
3773141858Sgshapiro	int ignore;
377464562Sgshapiro{
377577349Sgshapiro	/*
377677349Sgshapiro	**  NOTE: THIS CAN BE CALLED FROM A SIGNAL HANDLER.  DO NOT ADD
377777349Sgshapiro	**	ANYTHING TO THIS ROUTINE UNLESS YOU KNOW WHAT YOU ARE
377877349Sgshapiro	**	DOING.
377977349Sgshapiro	*/
378077349Sgshapiro
378164562Sgshapiro	errno = ETIMEDOUT;
378264562Sgshapiro	longjmp(EndWaitTimeout, 1);
378364562Sgshapiro}
378464562Sgshapiro
378538032Speterint
378638032Speterendmailer(mci, e, pv)
378738032Speter	register MCI *mci;
378838032Speter	register ENVELOPE *e;
378938032Speter	char **pv;
379038032Speter{
379138032Speter	int st;
379264562Sgshapiro	int save_errno = errno;
379364562Sgshapiro	char buf[MAXLINE];
379490792Sgshapiro	SM_EVENT *ev = NULL;
379538032Speter
379664562Sgshapiro
379738032Speter	mci_unlock_host(mci);
379838032Speter
379977349Sgshapiro	/* close output to mailer */
380077349Sgshapiro	if (mci->mci_out != NULL)
3801141858Sgshapiro	{
380290792Sgshapiro		(void) sm_io_close(mci->mci_out, SM_TIME_DEFAULT);
3803141858Sgshapiro		mci->mci_out = NULL;
3804141858Sgshapiro	}
380577349Sgshapiro
380677349Sgshapiro	/* copy any remaining input to transcript */
380777349Sgshapiro	if (mci->mci_in != NULL && mci->mci_state != MCIS_ERROR &&
380877349Sgshapiro	    e->e_xfp != NULL)
380977349Sgshapiro	{
381077349Sgshapiro		while (sfgets(buf, sizeof buf, mci->mci_in,
381190792Sgshapiro			      TimeOuts.to_quit, "Draining Input") != NULL)
381290792Sgshapiro			(void) sm_io_fputs(e->e_xfp, SM_TIME_DEFAULT, buf);
381377349Sgshapiro	}
381477349Sgshapiro
381564562Sgshapiro#if SASL
381690792Sgshapiro	/* close SASL connection */
381764562Sgshapiro	if (bitset(MCIF_AUTHACT, mci->mci_flags))
381864562Sgshapiro	{
381964562Sgshapiro		sasl_dispose(&mci->mci_conn);
382064562Sgshapiro		mci->mci_flags &= ~MCIF_AUTHACT;
382164562Sgshapiro	}
382264562Sgshapiro#endif /* SASL */
382364562Sgshapiro
382464562Sgshapiro#if STARTTLS
382564562Sgshapiro	/* shutdown TLS */
382664562Sgshapiro	(void) endtlsclt(mci);
382764562Sgshapiro#endif /* STARTTLS */
382864562Sgshapiro
382964562Sgshapiro	/* now close the input */
383038032Speter	if (mci->mci_in != NULL)
3831141858Sgshapiro	{
383290792Sgshapiro		(void) sm_io_close(mci->mci_in, SM_TIME_DEFAULT);
3833141858Sgshapiro		mci->mci_in = NULL;
3834141858Sgshapiro	}
383538032Speter	mci->mci_state = MCIS_CLOSED;
383638032Speter
383764562Sgshapiro	errno = save_errno;
383864562Sgshapiro
383938032Speter	/* in the IPC case there is nothing to wait for */
384038032Speter	if (mci->mci_pid == 0)
384164562Sgshapiro		return EX_OK;
384238032Speter
384364562Sgshapiro	/* put a timeout around the wait */
384464562Sgshapiro	if (mci->mci_mailer->m_wait > 0)
384564562Sgshapiro	{
384664562Sgshapiro		if (setjmp(EndWaitTimeout) == 0)
384790792Sgshapiro			ev = sm_setevent(mci->mci_mailer->m_wait,
384890792Sgshapiro					 endwaittimeout, 0);
384964562Sgshapiro		else
385064562Sgshapiro		{
385166494Sgshapiro			syserr("endmailer %s: wait timeout (%ld)",
385264562Sgshapiro			       mci->mci_mailer->m_name,
385366494Sgshapiro			       (long) mci->mci_mailer->m_wait);
385464562Sgshapiro			return EX_TEMPFAIL;
385564562Sgshapiro		}
385664562Sgshapiro	}
385738032Speter
385864562Sgshapiro	/* wait for the mailer process, collect status */
385938032Speter	st = waitfor(mci->mci_pid);
386064562Sgshapiro	save_errno = errno;
386164562Sgshapiro	if (ev != NULL)
386290792Sgshapiro		sm_clrevent(ev);
386364562Sgshapiro	errno = save_errno;
386464562Sgshapiro
386538032Speter	if (st == -1)
386638032Speter	{
386738032Speter		syserr("endmailer %s: wait", mci->mci_mailer->m_name);
386864562Sgshapiro		return EX_SOFTWARE;
386938032Speter	}
387038032Speter
387138032Speter	if (WIFEXITED(st))
387238032Speter	{
387338032Speter		/* normal death -- return status */
387438032Speter		return (WEXITSTATUS(st));
387538032Speter	}
387638032Speter
387738032Speter	/* it died a horrid death */
387864562Sgshapiro	syserr("451 4.3.0 mailer %s died with signal %d%s",
387964562Sgshapiro		mci->mci_mailer->m_name, WTERMSIG(st),
388064562Sgshapiro		WCOREDUMP(st) ? " (core dumped)" :
388164562Sgshapiro		(WIFSTOPPED(st) ? " (stopped)" : ""));
388238032Speter
388338032Speter	/* log the arguments */
388438032Speter	if (pv != NULL && e->e_xfp != NULL)
388538032Speter	{
388638032Speter		register char **av;
388738032Speter
388890792Sgshapiro		(void) sm_io_fprintf(e->e_xfp, SM_TIME_DEFAULT, "Arguments:");
388938032Speter		for (av = pv; *av != NULL; av++)
389090792Sgshapiro			(void) sm_io_fprintf(e->e_xfp, SM_TIME_DEFAULT, " %s",
389190792Sgshapiro					     *av);
389290792Sgshapiro		(void) sm_io_fprintf(e->e_xfp, SM_TIME_DEFAULT, "\n");
389338032Speter	}
389438032Speter
389538032Speter	ExitStat = EX_TEMPFAIL;
389664562Sgshapiro	return EX_TEMPFAIL;
389738032Speter}
389890792Sgshapiro/*
389938032Speter**  GIVERESPONSE -- Interpret an error response from a mailer
390038032Speter**
390138032Speter**	Parameters:
390264562Sgshapiro**		status -- the status code from the mailer (high byte
390338032Speter**			only; core dumps must have been taken care of
390438032Speter**			already).
390564562Sgshapiro**		dsn -- the DSN associated with the address, if any.
390638032Speter**		m -- the mailer info for this mailer.
390738032Speter**		mci -- the mailer connection info -- can be NULL if the
390838032Speter**			response is given before the connection is made.
390938032Speter**		ctladdr -- the controlling address for the recipient
391038032Speter**			address(es).
391138032Speter**		xstart -- the transaction start time, for computing
391238032Speter**			transaction delays.
391338032Speter**		e -- the current envelope.
391490792Sgshapiro**		to -- the current recipient (NULL if none).
391538032Speter**
391638032Speter**	Returns:
391738032Speter**		none.
391838032Speter**
391938032Speter**	Side Effects:
392038032Speter**		Errors may be incremented.
392138032Speter**		ExitStat may be set.
392238032Speter*/
392338032Speter
392438032Spetervoid
392590792Sgshapirogiveresponse(status, dsn, m, mci, ctladdr, xstart, e, to)
392664562Sgshapiro	int status;
392764562Sgshapiro	char *dsn;
392838032Speter	register MAILER *m;
392938032Speter	register MCI *mci;
393038032Speter	ADDRESS *ctladdr;
393138032Speter	time_t xstart;
393238032Speter	ENVELOPE *e;
393390792Sgshapiro	ADDRESS *to;
393438032Speter{
393538032Speter	register const char *statmsg;
393664562Sgshapiro	int errnum = errno;
393764562Sgshapiro	int off = 4;
393890792Sgshapiro	bool usestat = false;
393964562Sgshapiro	char dsnbuf[ENHSCLEN];
394038032Speter	char buf[MAXLINE];
394190792Sgshapiro	char *exmsg;
394238032Speter
394338032Speter	if (e == NULL)
394438032Speter		syserr("giveresponse: null envelope");
394538032Speter
394638032Speter	/*
394738032Speter	**  Compute status message from code.
394838032Speter	*/
394938032Speter
395090792Sgshapiro	exmsg = sm_sysexmsg(status);
395164562Sgshapiro	if (status == 0)
395238032Speter	{
395364562Sgshapiro		statmsg = "250 2.0.0 Sent";
395438032Speter		if (e->e_statmsg != NULL)
395538032Speter		{
395690792Sgshapiro			(void) sm_snprintf(buf, sizeof buf, "%s (%s)",
395790792Sgshapiro					   statmsg,
395890792Sgshapiro					   shortenstring(e->e_statmsg, 403));
395938032Speter			statmsg = buf;
396038032Speter		}
396138032Speter	}
396290792Sgshapiro	else if (exmsg == NULL)
396338032Speter	{
396490792Sgshapiro		(void) sm_snprintf(buf, sizeof buf,
396590792Sgshapiro				   "554 5.3.0 unknown mailer error %d",
396690792Sgshapiro				   status);
396764562Sgshapiro		status = EX_UNAVAILABLE;
396838032Speter		statmsg = buf;
396990792Sgshapiro		usestat = true;
397038032Speter	}
397164562Sgshapiro	else if (status == EX_TEMPFAIL)
397238032Speter	{
397338032Speter		char *bp = buf;
397438032Speter
397590792Sgshapiro		(void) sm_strlcpy(bp, exmsg + 1, SPACELEFT(buf, bp));
397638032Speter		bp += strlen(bp);
397738032Speter#if NAMED_BIND
397838032Speter		if (h_errno == TRY_AGAIN)
397990792Sgshapiro			statmsg = sm_errstring(h_errno + E_DNSBASE);
398038032Speter		else
398164562Sgshapiro#endif /* NAMED_BIND */
398238032Speter		{
398364562Sgshapiro			if (errnum != 0)
398490792Sgshapiro				statmsg = sm_errstring(errnum);
398538032Speter			else
398638032Speter				statmsg = SmtpError;
398738032Speter		}
398838032Speter		if (statmsg != NULL && statmsg[0] != '\0')
398964562Sgshapiro		{
399064562Sgshapiro			switch (errnum)
399164562Sgshapiro			{
399264562Sgshapiro#ifdef ENETDOWN
399364562Sgshapiro			  case ENETDOWN:	/* Network is down */
399464562Sgshapiro#endif /* ENETDOWN */
399564562Sgshapiro#ifdef ENETUNREACH
399664562Sgshapiro			  case ENETUNREACH:	/* Network is unreachable */
399764562Sgshapiro#endif /* ENETUNREACH */
399864562Sgshapiro#ifdef ENETRESET
399964562Sgshapiro			  case ENETRESET:	/* Network dropped connection on reset */
400064562Sgshapiro#endif /* ENETRESET */
400164562Sgshapiro#ifdef ECONNABORTED
400264562Sgshapiro			  case ECONNABORTED:	/* Software caused connection abort */
400364562Sgshapiro#endif /* ECONNABORTED */
400464562Sgshapiro#ifdef EHOSTDOWN
400564562Sgshapiro			  case EHOSTDOWN:	/* Host is down */
400664562Sgshapiro#endif /* EHOSTDOWN */
400764562Sgshapiro#ifdef EHOSTUNREACH
400864562Sgshapiro			  case EHOSTUNREACH:	/* No route to host */
400964562Sgshapiro#endif /* EHOSTUNREACH */
4010141858Sgshapiro				if (mci != NULL && mci->mci_host != NULL)
401164562Sgshapiro				{
401290792Sgshapiro					(void) sm_strlcpyn(bp,
401390792Sgshapiro							   SPACELEFT(buf, bp),
401490792Sgshapiro							   2, ": ",
401590792Sgshapiro							   mci->mci_host);
401664562Sgshapiro					bp += strlen(bp);
401764562Sgshapiro				}
401864562Sgshapiro				break;
401964562Sgshapiro			}
402090792Sgshapiro			(void) sm_strlcpyn(bp, SPACELEFT(buf, bp), 2, ": ",
402190792Sgshapiro					   statmsg);
402290792Sgshapiro			usestat = true;
402364562Sgshapiro		}
402438032Speter		statmsg = buf;
402538032Speter	}
402638032Speter#if NAMED_BIND
402764562Sgshapiro	else if (status == EX_NOHOST && h_errno != 0)
402838032Speter	{
402990792Sgshapiro		statmsg = sm_errstring(h_errno + E_DNSBASE);
403090792Sgshapiro		(void) sm_snprintf(buf, sizeof buf, "%s (%s)", exmsg + 1,
403190792Sgshapiro				   statmsg);
403238032Speter		statmsg = buf;
403390792Sgshapiro		usestat = true;
403438032Speter	}
403564562Sgshapiro#endif /* NAMED_BIND */
403638032Speter	else
403738032Speter	{
403890792Sgshapiro		statmsg = exmsg;
403964562Sgshapiro		if (*statmsg++ == ':' && errnum != 0)
404038032Speter		{
404190792Sgshapiro			(void) sm_snprintf(buf, sizeof buf, "%s: %s", statmsg,
404290792Sgshapiro					   sm_errstring(errnum));
404338032Speter			statmsg = buf;
404490792Sgshapiro			usestat = true;
404538032Speter		}
404694334Sgshapiro		else if (bitnset(M_LMTP, m->m_flags) && e->e_statmsg != NULL)
404794334Sgshapiro		{
404894334Sgshapiro			(void) sm_snprintf(buf, sizeof buf, "%s (%s)", statmsg,
404994334Sgshapiro					   shortenstring(e->e_statmsg, 403));
405094334Sgshapiro			statmsg = buf;
405194334Sgshapiro			usestat = true;
405294334Sgshapiro		}
405338032Speter	}
405438032Speter
405538032Speter	/*
405638032Speter	**  Print the message as appropriate
405738032Speter	*/
405838032Speter
405964562Sgshapiro	if (status == EX_OK || status == EX_TEMPFAIL)
406038032Speter	{
406138032Speter		extern char MsgBuf[];
406238032Speter
406364562Sgshapiro		if ((off = isenhsc(statmsg + 4, ' ')) > 0)
406464562Sgshapiro		{
406564562Sgshapiro			if (dsn == NULL)
406664562Sgshapiro			{
406790792Sgshapiro				(void) sm_snprintf(dsnbuf, sizeof dsnbuf,
406890792Sgshapiro						   "%.*s", off, statmsg + 4);
406964562Sgshapiro				dsn = dsnbuf;
407064562Sgshapiro			}
407164562Sgshapiro			off += 5;
407264562Sgshapiro		}
407364562Sgshapiro		else
407464562Sgshapiro		{
407564562Sgshapiro			off = 4;
407664562Sgshapiro		}
407764562Sgshapiro		message("%s", statmsg + off);
407864562Sgshapiro		if (status == EX_TEMPFAIL && e->e_xfp != NULL)
407990792Sgshapiro			(void) sm_io_fprintf(e->e_xfp, SM_TIME_DEFAULT, "%s\n",
408090792Sgshapiro					     &MsgBuf[4]);
408138032Speter	}
408238032Speter	else
408338032Speter	{
408464562Sgshapiro		char mbuf[ENHSCLEN + 4];
408538032Speter
408638032Speter		Errors++;
408764562Sgshapiro		if ((off = isenhsc(statmsg + 4, ' ')) > 0 &&
408864562Sgshapiro		    off < sizeof mbuf - 4)
408964562Sgshapiro		{
409064562Sgshapiro			if (dsn == NULL)
409164562Sgshapiro			{
409290792Sgshapiro				(void) sm_snprintf(dsnbuf, sizeof dsnbuf,
409390792Sgshapiro						   "%.*s", off, statmsg + 4);
409464562Sgshapiro				dsn = dsnbuf;
409564562Sgshapiro			}
409664562Sgshapiro			off += 5;
409790792Sgshapiro
409890792Sgshapiro			/* copy only part of statmsg to mbuf */
409990792Sgshapiro			(void) sm_strlcpy(mbuf, statmsg, off);
410090792Sgshapiro			(void) sm_strlcat(mbuf, " %s", sizeof mbuf);
410164562Sgshapiro		}
410264562Sgshapiro		else
410364562Sgshapiro		{
410464562Sgshapiro			dsnbuf[0] = '\0';
410590792Sgshapiro			(void) sm_snprintf(mbuf, sizeof mbuf, "%.3s %%s",
410690792Sgshapiro					   statmsg);
410764562Sgshapiro			off = 4;
410864562Sgshapiro		}
410964562Sgshapiro		usrerr(mbuf, &statmsg[off]);
411038032Speter	}
411138032Speter
411238032Speter	/*
411338032Speter	**  Final cleanup.
411438032Speter	**	Log a record of the transaction.  Compute the new
411538032Speter	**	ExitStat -- if we already had an error, stick with
411638032Speter	**	that.
411738032Speter	*/
411838032Speter
411938032Speter	if (OpMode != MD_VERIFY && !bitset(EF_VRFYONLY, e->e_flags) &&
412064562Sgshapiro	    LogLevel > ((status == EX_TEMPFAIL) ? 8 : (status == EX_OK) ? 7 : 6))
412164562Sgshapiro		logdelivery(m, mci, dsn, statmsg + off, ctladdr, xstart, e);
412238032Speter
412338032Speter	if (tTd(11, 2))
412490792Sgshapiro		sm_dprintf("giveresponse: status=%d, dsn=%s, e->e_message=%s, errnum=%d\n",
412590792Sgshapiro			   status,
412690792Sgshapiro			   dsn == NULL ? "<NULL>" : dsn,
412790792Sgshapiro			   e->e_message == NULL ? "<NULL>" : e->e_message,
412890792Sgshapiro			   errnum);
412938032Speter
413064562Sgshapiro	if (status != EX_TEMPFAIL)
413164562Sgshapiro		setstat(status);
413264562Sgshapiro	if (status != EX_OK && (status != EX_TEMPFAIL || e->e_message == NULL))
413390792Sgshapiro		e->e_message = sm_rpool_strdup_x(e->e_rpool, statmsg + off);
413490792Sgshapiro	if (status != EX_OK && to != NULL && to->q_message == NULL)
413538032Speter	{
413690792Sgshapiro		if (!usestat && e->e_message != NULL)
413790792Sgshapiro			to->q_message = sm_rpool_strdup_x(e->e_rpool,
413890792Sgshapiro							  e->e_message);
413990792Sgshapiro		else
414090792Sgshapiro			to->q_message = sm_rpool_strdup_x(e->e_rpool,
414190792Sgshapiro							  statmsg + off);
414238032Speter	}
414338032Speter	errno = 0;
414473188Sgshapiro	SM_SET_H_ERRNO(0);
414538032Speter}
414690792Sgshapiro/*
414738032Speter**  LOGDELIVERY -- log the delivery in the system log
414838032Speter**
414938032Speter**	Care is taken to avoid logging lines that are too long, because
415038032Speter**	some versions of syslog have an unfortunate proclivity for core
415138032Speter**	dumping.  This is a hack, to be sure, that is at best empirical.
415238032Speter**
415338032Speter**	Parameters:
415438032Speter**		m -- the mailer info.  Can be NULL for initial queue.
415538032Speter**		mci -- the mailer connection info -- can be NULL if the
415664562Sgshapiro**			log is occurring when no connection is active.
415764562Sgshapiro**		dsn -- the DSN attached to the status.
415864562Sgshapiro**		status -- the message to print for the status.
415938032Speter**		ctladdr -- the controlling address for the to list.
416038032Speter**		xstart -- the transaction start time, used for
416138032Speter**			computing transaction delay.
416238032Speter**		e -- the current envelope.
416338032Speter**
416438032Speter**	Returns:
416538032Speter**		none
416638032Speter**
416738032Speter**	Side Effects:
416838032Speter**		none
416938032Speter*/
417038032Speter
417138032Spetervoid
417264562Sgshapirologdelivery(m, mci, dsn, status, ctladdr, xstart, e)
417338032Speter	MAILER *m;
417438032Speter	register MCI *mci;
417564562Sgshapiro	char *dsn;
417664562Sgshapiro	const char *status;
417738032Speter	ADDRESS *ctladdr;
417838032Speter	time_t xstart;
417938032Speter	register ENVELOPE *e;
418038032Speter{
418138032Speter	register char *bp;
418238032Speter	register char *p;
418338032Speter	int l;
418490792Sgshapiro	time_t now = curtime();
418538032Speter	char buf[1024];
418638032Speter
418764562Sgshapiro#if (SYSLOG_BUFSIZE) >= 256
418838032Speter	/* ctladdr: max 106 bytes */
418938032Speter	bp = buf;
419038032Speter	if (ctladdr != NULL)
419138032Speter	{
419290792Sgshapiro		(void) sm_strlcpyn(bp, SPACELEFT(buf, bp), 2, ", ctladdr=",
419390792Sgshapiro				   shortenstring(ctladdr->q_paddr, 83));
419438032Speter		bp += strlen(bp);
419538032Speter		if (bitset(QGOODUID, ctladdr->q_flags))
419638032Speter		{
419790792Sgshapiro			(void) sm_snprintf(bp, SPACELEFT(buf, bp), " (%d/%d)",
419890792Sgshapiro					   (int) ctladdr->q_uid,
419990792Sgshapiro					   (int) ctladdr->q_gid);
420038032Speter			bp += strlen(bp);
420138032Speter		}
420238032Speter	}
420338032Speter
420438032Speter	/* delay & xdelay: max 41 bytes */
420590792Sgshapiro	(void) sm_strlcpyn(bp, SPACELEFT(buf, bp), 2, ", delay=",
420690792Sgshapiro			   pintvl(now - e->e_ctime, true));
420738032Speter	bp += strlen(bp);
420838032Speter
420938032Speter	if (xstart != (time_t) 0)
421038032Speter	{
421190792Sgshapiro		(void) sm_strlcpyn(bp, SPACELEFT(buf, bp), 2, ", xdelay=",
421290792Sgshapiro				   pintvl(now - xstart, true));
421338032Speter		bp += strlen(bp);
421438032Speter	}
421538032Speter
421638032Speter	/* mailer: assume about 19 bytes (max 10 byte mailer name) */
421738032Speter	if (m != NULL)
421838032Speter	{
421990792Sgshapiro		(void) sm_strlcpyn(bp, SPACELEFT(buf, bp), 2, ", mailer=",
422090792Sgshapiro				   m->m_name);
422138032Speter		bp += strlen(bp);
422238032Speter	}
422338032Speter
422464562Sgshapiro	/* pri: changes with each delivery attempt */
422590792Sgshapiro	(void) sm_snprintf(bp, SPACELEFT(buf, bp), ", pri=%ld",
422690792Sgshapiro		e->e_msgpriority);
422764562Sgshapiro	bp += strlen(bp);
422864562Sgshapiro
422938032Speter	/* relay: max 66 bytes for IPv4 addresses */
423038032Speter	if (mci != NULL && mci->mci_host != NULL)
423138032Speter	{
423238032Speter		extern SOCKADDR CurHostAddr;
423338032Speter
423490792Sgshapiro		(void) sm_strlcpyn(bp, SPACELEFT(buf, bp), 2, ", relay=",
423590792Sgshapiro				   shortenstring(mci->mci_host, 40));
423638032Speter		bp += strlen(bp);
423738032Speter
423838032Speter		if (CurHostAddr.sa.sa_family != 0)
423938032Speter		{
424090792Sgshapiro			(void) sm_snprintf(bp, SPACELEFT(buf, bp), " [%s]",
424190792Sgshapiro					   anynet_ntoa(&CurHostAddr));
424238032Speter		}
424338032Speter	}
424490792Sgshapiro	else if (strcmp(status, "quarantined") == 0)
424590792Sgshapiro	{
424690792Sgshapiro		if (e->e_quarmsg != NULL)
424790792Sgshapiro			(void) sm_snprintf(bp, SPACELEFT(buf, bp),
424890792Sgshapiro					   ", quarantine=%s",
424990792Sgshapiro					   shortenstring(e->e_quarmsg, 40));
425090792Sgshapiro	}
425164562Sgshapiro	else if (strcmp(status, "queued") != 0)
425238032Speter	{
425338032Speter		p = macvalue('h', e);
425438032Speter		if (p != NULL && p[0] != '\0')
425538032Speter		{
425690792Sgshapiro			(void) sm_snprintf(bp, SPACELEFT(buf, bp),
425790792Sgshapiro					   ", relay=%s", shortenstring(p, 40));
425838032Speter		}
425938032Speter	}
426038032Speter	bp += strlen(bp);
426138032Speter
426264562Sgshapiro	/* dsn */
426364562Sgshapiro	if (dsn != NULL && *dsn != '\0')
426464562Sgshapiro	{
426590792Sgshapiro		(void) sm_strlcpyn(bp, SPACELEFT(buf, bp), 2, ", dsn=",
426690792Sgshapiro				   shortenstring(dsn, ENHSCLEN));
426764562Sgshapiro		bp += strlen(bp);
426864562Sgshapiro	}
426938032Speter
427064562Sgshapiro# define STATLEN		(((SYSLOG_BUFSIZE) - 100) / 4)
427164562Sgshapiro# if (STATLEN) < 63
427264562Sgshapiro#  undef STATLEN
427364562Sgshapiro#  define STATLEN	63
427464562Sgshapiro# endif /* (STATLEN) < 63 */
427564562Sgshapiro# if (STATLEN) > 203
427664562Sgshapiro#  undef STATLEN
427764562Sgshapiro#  define STATLEN	203
427864562Sgshapiro# endif /* (STATLEN) > 203 */
427964562Sgshapiro
428038032Speter	/* stat: max 210 bytes */
428138032Speter	if ((bp - buf) > (sizeof buf - ((STATLEN) + 20)))
428238032Speter	{
428338032Speter		/* desperation move -- truncate data */
428438032Speter		bp = buf + sizeof buf - ((STATLEN) + 17);
428590792Sgshapiro		(void) sm_strlcpy(bp, "...", SPACELEFT(buf, bp));
428638032Speter		bp += 3;
428738032Speter	}
428838032Speter
428990792Sgshapiro	(void) sm_strlcpy(bp, ", stat=", SPACELEFT(buf, bp));
429038032Speter	bp += strlen(bp);
429138032Speter
429290792Sgshapiro	(void) sm_strlcpy(bp, shortenstring(status, STATLEN),
429390792Sgshapiro			  SPACELEFT(buf, bp));
429438032Speter
429538032Speter	/* id, to: max 13 + TOBUFSIZE bytes */
429638032Speter	l = SYSLOG_BUFSIZE - 100 - strlen(buf);
429790792Sgshapiro	if (l < 0)
429890792Sgshapiro		l = 0;
429964562Sgshapiro	p = e->e_to == NULL ? "NO-TO-LIST" : e->e_to;
430090792Sgshapiro	while (strlen(p) >= l)
430138032Speter	{
430264562Sgshapiro		register char *q;
430338032Speter
430464562Sgshapiro		for (q = p + l; q > p; q--)
430564562Sgshapiro		{
430664562Sgshapiro			if (*q == ',')
430764562Sgshapiro				break;
430864562Sgshapiro		}
430964562Sgshapiro		if (p == q)
431064562Sgshapiro			break;
431190792Sgshapiro		sm_syslog(LOG_INFO, e->e_id, "to=%.*s [more]%s",
431266494Sgshapiro			  (int) (++q - p), p, buf);
431338032Speter		p = q;
431438032Speter	}
431564562Sgshapiro	sm_syslog(LOG_INFO, e->e_id, "to=%.*s%s", l, p, buf);
431638032Speter
431764562Sgshapiro#else /* (SYSLOG_BUFSIZE) >= 256 */
431838032Speter
431938032Speter	l = SYSLOG_BUFSIZE - 85;
432090792Sgshapiro	if (l < 0)
432190792Sgshapiro		l = 0;
432264562Sgshapiro	p = e->e_to == NULL ? "NO-TO-LIST" : e->e_to;
432390792Sgshapiro	while (strlen(p) >= l)
432438032Speter	{
432564562Sgshapiro		register char *q;
432638032Speter
432764562Sgshapiro		for (q = p + l; q > p; q--)
432864562Sgshapiro		{
432964562Sgshapiro			if (*q == ',')
433064562Sgshapiro				break;
433164562Sgshapiro		}
433264562Sgshapiro		if (p == q)
433364562Sgshapiro			break;
433464562Sgshapiro
433590792Sgshapiro		sm_syslog(LOG_INFO, e->e_id, "to=%.*s [more]",
433666494Sgshapiro			  (int) (++q - p), p);
433738032Speter		p = q;
433838032Speter	}
433964562Sgshapiro	sm_syslog(LOG_INFO, e->e_id, "to=%.*s", l, p);
434038032Speter
434138032Speter	if (ctladdr != NULL)
434238032Speter	{
434338032Speter		bp = buf;
434490792Sgshapiro		(void) sm_strlcpyn(bp, SPACELEFT(buf, bp), 2, "ctladdr=",
434590792Sgshapiro				   shortenstring(ctladdr->q_paddr, 83));
434638032Speter		bp += strlen(bp);
434738032Speter		if (bitset(QGOODUID, ctladdr->q_flags))
434838032Speter		{
434990792Sgshapiro			(void) sm_snprintf(bp, SPACELEFT(buf, bp), " (%d/%d)",
435090792Sgshapiro					   ctladdr->q_uid, ctladdr->q_gid);
435138032Speter			bp += strlen(bp);
435238032Speter		}
435338032Speter		sm_syslog(LOG_INFO, e->e_id, "%s", buf);
435438032Speter	}
435538032Speter	bp = buf;
435690792Sgshapiro	(void) sm_strlcpyn(bp, SPACELEFT(buf, bp), 2, "delay=",
435790792Sgshapiro			   pintvl(now - e->e_ctime, true));
435838032Speter	bp += strlen(bp);
435938032Speter	if (xstart != (time_t) 0)
436038032Speter	{
436190792Sgshapiro		(void) sm_strlcpyn(bp, SPACELEFT(buf, bp), 2, ", xdelay=",
436290792Sgshapiro				   pintvl(now - xstart, true));
436338032Speter		bp += strlen(bp);
436438032Speter	}
436538032Speter
436638032Speter	if (m != NULL)
436738032Speter	{
436890792Sgshapiro		(void) sm_strlcpyn(bp, SPACELEFT(buf, bp), 2, ", mailer=",
436990792Sgshapiro				   m->m_name);
437038032Speter		bp += strlen(bp);
437138032Speter	}
437238032Speter	sm_syslog(LOG_INFO, e->e_id, "%.1000s", buf);
437338032Speter
437438032Speter	buf[0] = '\0';
437538032Speter	bp = buf;
437638032Speter	if (mci != NULL && mci->mci_host != NULL)
437738032Speter	{
437838032Speter		extern SOCKADDR CurHostAddr;
437938032Speter
438090792Sgshapiro		(void) sm_snprintf(bp, SPACELEFT(buf, bp), "relay=%.100s",
438190792Sgshapiro				   mci->mci_host);
438238032Speter		bp += strlen(bp);
438338032Speter
438438032Speter		if (CurHostAddr.sa.sa_family != 0)
438590792Sgshapiro			(void) sm_snprintf(bp, SPACELEFT(buf, bp),
438690792Sgshapiro					   " [%.100s]",
438790792Sgshapiro					   anynet_ntoa(&CurHostAddr));
438838032Speter	}
438990792Sgshapiro	else if (strcmp(status, "quarantined") == 0)
439090792Sgshapiro	{
439190792Sgshapiro		if (e->e_quarmsg != NULL)
439290792Sgshapiro			(void) sm_snprintf(bp, SPACELEFT(buf, bp),
439390792Sgshapiro					   ", quarantine=%.100s",
439490792Sgshapiro					   e->e_quarmsg);
439590792Sgshapiro	}
439664562Sgshapiro	else if (strcmp(status, "queued") != 0)
439738032Speter	{
439838032Speter		p = macvalue('h', e);
439938032Speter		if (p != NULL && p[0] != '\0')
440090792Sgshapiro			(void) sm_snprintf(buf, sizeof buf, "relay=%.100s", p);
440138032Speter	}
440238032Speter	if (buf[0] != '\0')
440338032Speter		sm_syslog(LOG_INFO, e->e_id, "%.1000s", buf);
440438032Speter
440564562Sgshapiro	sm_syslog(LOG_INFO, e->e_id, "stat=%s", shortenstring(status, 63));
440664562Sgshapiro#endif /* (SYSLOG_BUFSIZE) >= 256 */
440738032Speter}
440890792Sgshapiro/*
440938032Speter**  PUTFROMLINE -- output a UNIX-style from line (or whatever)
441038032Speter**
441138032Speter**	This can be made an arbitrary message separator by changing $l
441238032Speter**
441338032Speter**	One of the ugliest hacks seen by human eyes is contained herein:
441438032Speter**	UUCP wants those stupid "remote from <host>" lines.  Why oh why
441538032Speter**	does a well-meaning programmer such as myself have to deal with
441638032Speter**	this kind of antique garbage????
441738032Speter**
441838032Speter**	Parameters:
441938032Speter**		mci -- the connection information.
442038032Speter**		e -- the envelope.
442138032Speter**
442238032Speter**	Returns:
442338032Speter**		none
442438032Speter**
442538032Speter**	Side Effects:
442638032Speter**		outputs some text to fp.
442738032Speter*/
442838032Speter
442938032Spetervoid
443038032Speterputfromline(mci, e)
443138032Speter	register MCI *mci;
443238032Speter	ENVELOPE *e;
443338032Speter{
443438032Speter	char *template = UnixFromLine;
443538032Speter	char buf[MAXLINE];
443638032Speter	char xbuf[MAXLINE];
443738032Speter
443838032Speter	if (bitnset(M_NHDR, mci->mci_mailer->m_flags))
443938032Speter		return;
444038032Speter
444138032Speter	mci->mci_flags |= MCIF_INHEADER;
444238032Speter
444338032Speter	if (bitnset(M_UGLYUUCP, mci->mci_mailer->m_flags))
444438032Speter	{
444538032Speter		char *bang;
444638032Speter
444738032Speter		expand("\201g", buf, sizeof buf, e);
444838032Speter		bang = strchr(buf, '!');
444938032Speter		if (bang == NULL)
445038032Speter		{
445138032Speter			char *at;
445238032Speter			char hname[MAXNAME];
445338032Speter
445464562Sgshapiro			/*
445542575Speter			**  If we can construct a UUCP path, do so
445638032Speter			*/
445738032Speter
445838032Speter			at = strrchr(buf, '@');
445938032Speter			if (at == NULL)
446038032Speter			{
446164562Sgshapiro				expand("\201k", hname, sizeof hname, e);
446238032Speter				at = hname;
446338032Speter			}
446438032Speter			else
446538032Speter				*at++ = '\0';
446690792Sgshapiro			(void) sm_snprintf(xbuf, sizeof xbuf,
446790792Sgshapiro					   "From %.800s  \201d remote from %.100s\n",
446890792Sgshapiro					   buf, at);
446938032Speter		}
447038032Speter		else
447138032Speter		{
447238032Speter			*bang++ = '\0';
447390792Sgshapiro			(void) sm_snprintf(xbuf, sizeof xbuf,
447490792Sgshapiro					   "From %.800s  \201d remote from %.100s\n",
447590792Sgshapiro					   bang, buf);
447638032Speter			template = xbuf;
447738032Speter		}
447838032Speter	}
447938032Speter	expand(template, buf, sizeof buf, e);
448038032Speter	putxline(buf, strlen(buf), mci, PXLF_HEADER);
448138032Speter}
448290792Sgshapiro/*
448338032Speter**  PUTBODY -- put the body of a message.
448438032Speter**
448538032Speter**	Parameters:
448638032Speter**		mci -- the connection information.
448738032Speter**		e -- the envelope to put out.
448838032Speter**		separator -- if non-NULL, a message separator that must
448938032Speter**			not be permitted in the resulting message.
449038032Speter**
449138032Speter**	Returns:
449238032Speter**		none.
449338032Speter**
449438032Speter**	Side Effects:
449538032Speter**		The message is written onto fp.
449638032Speter*/
449738032Speter
449838032Speter/* values for output state variable */
449938032Speter#define OS_HEAD		0	/* at beginning of line */
450038032Speter#define OS_CR		1	/* read a carriage return */
450138032Speter#define OS_INLINE	2	/* putting rest of line */
450238032Speter
450338032Spetervoid
450438032Speterputbody(mci, e, separator)
450538032Speter	register MCI *mci;
450638032Speter	register ENVELOPE *e;
450738032Speter	char *separator;
450838032Speter{
450990792Sgshapiro	bool dead = false;
451038032Speter	char buf[MAXLINE];
451190792Sgshapiro#if MIME8TO7
451242575Speter	char *boundaries[MAXMIMENESTING + 1];
451390792Sgshapiro#endif /* MIME8TO7 */
451438032Speter
451538032Speter	/*
451638032Speter	**  Output the body of the message
451738032Speter	*/
451838032Speter
451938032Speter	if (e->e_dfp == NULL && bitset(EF_HAS_DF, e->e_flags))
452038032Speter	{
452190792Sgshapiro		char *df = queuename(e, DATAFL_LETTER);
452238032Speter
452390792Sgshapiro		e->e_dfp = sm_io_open(SmFtStdio, SM_TIME_DEFAULT, df,
4524120256Sgshapiro				      SM_IO_RDONLY_B, NULL);
452538032Speter		if (e->e_dfp == NULL)
452664562Sgshapiro		{
452764562Sgshapiro			char *msg = "!putbody: Cannot open %s for %s from %s";
452864562Sgshapiro
452964562Sgshapiro			if (errno == ENOENT)
453064562Sgshapiro				msg++;
453164562Sgshapiro			syserr(msg, df, e->e_to, e->e_from.q_paddr);
453264562Sgshapiro		}
453390792Sgshapiro
453438032Speter	}
453538032Speter	if (e->e_dfp == NULL)
453638032Speter	{
453738032Speter		if (bitset(MCIF_INHEADER, mci->mci_flags))
453838032Speter		{
453938032Speter			putline("", mci);
454038032Speter			mci->mci_flags &= ~MCIF_INHEADER;
454138032Speter		}
454238032Speter		putline("<<< No Message Collected >>>", mci);
454338032Speter		goto endofmessage;
454438032Speter	}
454564562Sgshapiro
454638032Speter	if (e->e_dfino == (ino_t) 0)
454738032Speter	{
454838032Speter		struct stat stbuf;
454938032Speter
455090792Sgshapiro		if (fstat(sm_io_getinfo(e->e_dfp, SM_IO_WHAT_FD, NULL), &stbuf)
455190792Sgshapiro		    < 0)
455238032Speter			e->e_dfino = -1;
455338032Speter		else
455438032Speter		{
455538032Speter			e->e_dfdev = stbuf.st_dev;
455638032Speter			e->e_dfino = stbuf.st_ino;
455738032Speter		}
455838032Speter	}
455938032Speter
456090792Sgshapiro	/* paranoia: the data file should always be in a rewound state */
456164562Sgshapiro	(void) bfrewind(e->e_dfp);
456264562Sgshapiro
456338032Speter#if MIME8TO7
456438032Speter	if (bitset(MCIF_CVT8TO7, mci->mci_flags))
456538032Speter	{
456638032Speter		/*
456738032Speter		**  Do 8 to 7 bit MIME conversion.
456838032Speter		*/
456938032Speter
457038032Speter		/* make sure it looks like a MIME message */
457138032Speter		if (hvalue("MIME-Version", e->e_header) == NULL)
457238032Speter			putline("MIME-Version: 1.0", mci);
457338032Speter
457438032Speter		if (hvalue("Content-Type", e->e_header) == NULL)
457538032Speter		{
457690792Sgshapiro			(void) sm_snprintf(buf, sizeof buf,
457790792Sgshapiro					   "Content-Type: text/plain; charset=%s",
457890792Sgshapiro					   defcharset(e));
457938032Speter			putline(buf, mci);
458038032Speter		}
458138032Speter
458238032Speter		/* now do the hard work */
458338032Speter		boundaries[0] = NULL;
458438032Speter		mci->mci_flags |= MCIF_INHEADER;
458564562Sgshapiro		(void) mime8to7(mci, e->e_header, e, boundaries, M87F_OUTER);
458638032Speter	}
458738032Speter# if MIME7TO8
458838032Speter	else if (bitset(MCIF_CVT7TO8, mci->mci_flags))
458938032Speter	{
459064562Sgshapiro		(void) mime7to8(mci, e->e_header, e);
459138032Speter	}
459264562Sgshapiro# endif /* MIME7TO8 */
459342575Speter	else if (MaxMimeHeaderLength > 0 || MaxMimeFieldLength > 0)
459442575Speter	{
459564562Sgshapiro		bool oldsuprerrs = SuprErrs;
459664562Sgshapiro
459742575Speter		/* Use mime8to7 to check multipart for MIME header overflows */
459842575Speter		boundaries[0] = NULL;
459942575Speter		mci->mci_flags |= MCIF_INHEADER;
460064562Sgshapiro
460164562Sgshapiro		/*
460264562Sgshapiro		**  If EF_DONT_MIME is set, we have a broken MIME message
460364562Sgshapiro		**  and don't want to generate a new bounce message whose
460464562Sgshapiro		**  body propagates the broken MIME.  We can't just not call
460564562Sgshapiro		**  mime8to7() as is done above since we need the security
460664562Sgshapiro		**  checks.  The best we can do is suppress the errors.
460764562Sgshapiro		*/
460864562Sgshapiro
460964562Sgshapiro		if (bitset(EF_DONT_MIME, e->e_flags))
461090792Sgshapiro			SuprErrs = true;
461164562Sgshapiro
461264562Sgshapiro		(void) mime8to7(mci, e->e_header, e, boundaries,
461364562Sgshapiro				M87F_OUTER|M87F_NO8TO7);
461464562Sgshapiro
461564562Sgshapiro		/* restore SuprErrs */
461664562Sgshapiro		SuprErrs = oldsuprerrs;
461742575Speter	}
461838032Speter	else
461964562Sgshapiro#endif /* MIME8TO7 */
462038032Speter	{
462138032Speter		int ostate;
462238032Speter		register char *bp;
462338032Speter		register char *pbp;
462438032Speter		register int c;
462538032Speter		register char *xp;
462638032Speter		int padc;
462738032Speter		char *buflim;
462838032Speter		int pos = 0;
462964562Sgshapiro		char peekbuf[12];
463038032Speter
463138032Speter		if (bitset(MCIF_INHEADER, mci->mci_flags))
463238032Speter		{
463338032Speter			putline("", mci);
463438032Speter			mci->mci_flags &= ~MCIF_INHEADER;
463538032Speter		}
463638032Speter
463738032Speter		/* determine end of buffer; allow for short mailer lines */
463838032Speter		buflim = &buf[sizeof buf - 1];
463938032Speter		if (mci->mci_mailer->m_linelimit > 0 &&
464038032Speter		    mci->mci_mailer->m_linelimit < sizeof buf - 1)
464138032Speter			buflim = &buf[mci->mci_mailer->m_linelimit - 1];
464238032Speter
464338032Speter		/* copy temp file to output with mapping */
464438032Speter		ostate = OS_HEAD;
464538032Speter		bp = buf;
464638032Speter		pbp = peekbuf;
464790792Sgshapiro		while (!sm_io_error(mci->mci_out) && !dead)
464838032Speter		{
464938032Speter			if (pbp > peekbuf)
465038032Speter				c = *--pbp;
465190792Sgshapiro			else if ((c = sm_io_getc(e->e_dfp, SM_TIME_DEFAULT))
465290792Sgshapiro				 == SM_IO_EOF)
465338032Speter				break;
465438032Speter			if (bitset(MCIF_7BIT, mci->mci_flags))
465538032Speter				c &= 0x7f;
465638032Speter			switch (ostate)
465738032Speter			{
465838032Speter			  case OS_HEAD:
465938032Speter				if (c == '\0' &&
466090792Sgshapiro				    bitnset(M_NONULLS,
466190792Sgshapiro					    mci->mci_mailer->m_flags))
466238032Speter					break;
466338032Speter				if (c != '\r' && c != '\n' && bp < buflim)
466438032Speter				{
466538032Speter					*bp++ = c;
466638032Speter					break;
466738032Speter				}
466838032Speter
466938032Speter				/* check beginning of line for special cases */
467038032Speter				*bp = '\0';
467138032Speter				pos = 0;
467290792Sgshapiro				padc = SM_IO_EOF;
467338032Speter				if (buf[0] == 'F' &&
467490792Sgshapiro				    bitnset(M_ESCFROM, mci->mci_mailer->m_flags)
467590792Sgshapiro				    && strncmp(buf, "From ", 5) == 0)
467638032Speter				{
467738032Speter					padc = '>';
467838032Speter				}
467938032Speter				if (buf[0] == '-' && buf[1] == '-' &&
468038032Speter				    separator != NULL)
468138032Speter				{
468238032Speter					/* possible separator */
468338032Speter					int sl = strlen(separator);
468438032Speter
468590792Sgshapiro					if (strncmp(&buf[2], separator, sl)
468690792Sgshapiro					    == 0)
468738032Speter						padc = ' ';
468838032Speter				}
468938032Speter				if (buf[0] == '.' &&
469038032Speter				    bitnset(M_XDOT, mci->mci_mailer->m_flags))
469138032Speter				{
469238032Speter					padc = '.';
469338032Speter				}
469438032Speter
469538032Speter				/* now copy out saved line */
469638032Speter				if (TrafficLogFile != NULL)
469738032Speter				{
469890792Sgshapiro					(void) sm_io_fprintf(TrafficLogFile,
469990792Sgshapiro							     SM_TIME_DEFAULT,
470090792Sgshapiro							     "%05d >>> ",
470190792Sgshapiro							     (int) CurrentPid);
470290792Sgshapiro					if (padc != SM_IO_EOF)
470390792Sgshapiro						(void) sm_io_putc(TrafficLogFile,
470490792Sgshapiro								  SM_TIME_DEFAULT,
470590792Sgshapiro								  padc);
470638032Speter					for (xp = buf; xp < bp; xp++)
470790792Sgshapiro						(void) sm_io_putc(TrafficLogFile,
470890792Sgshapiro								  SM_TIME_DEFAULT,
470990792Sgshapiro								  (unsigned char) *xp);
471038032Speter					if (c == '\n')
471190792Sgshapiro						(void) sm_io_fputs(TrafficLogFile,
471290792Sgshapiro								   SM_TIME_DEFAULT,
471390792Sgshapiro								   mci->mci_mailer->m_eol);
471438032Speter				}
471590792Sgshapiro				if (padc != SM_IO_EOF)
471638032Speter				{
471790792Sgshapiro					if (sm_io_putc(mci->mci_out,
471890792Sgshapiro						       SM_TIME_DEFAULT, padc)
471990792Sgshapiro					    == SM_IO_EOF)
472064562Sgshapiro					{
472190792Sgshapiro						dead = true;
472264562Sgshapiro						continue;
472364562Sgshapiro					}
472471345Sgshapiro					else
472571345Sgshapiro					{
472671345Sgshapiro						/* record progress for DATA timeout */
472790792Sgshapiro						DataProgress = true;
472871345Sgshapiro					}
472938032Speter					pos++;
473038032Speter				}
473138032Speter				for (xp = buf; xp < bp; xp++)
473238032Speter				{
473390792Sgshapiro					if (sm_io_putc(mci->mci_out,
473490792Sgshapiro						       SM_TIME_DEFAULT,
473590792Sgshapiro						       (unsigned char) *xp)
473690792Sgshapiro					    == SM_IO_EOF)
473764562Sgshapiro					{
473890792Sgshapiro						dead = true;
473964562Sgshapiro						break;
474064562Sgshapiro					}
474171345Sgshapiro					else
474271345Sgshapiro					{
474371345Sgshapiro						/* record progress for DATA timeout */
474490792Sgshapiro						DataProgress = true;
474571345Sgshapiro					}
474638032Speter				}
474764562Sgshapiro				if (dead)
474864562Sgshapiro					continue;
474938032Speter				if (c == '\n')
475038032Speter				{
475190792Sgshapiro					if (sm_io_fputs(mci->mci_out,
475290792Sgshapiro							SM_TIME_DEFAULT,
475390792Sgshapiro							mci->mci_mailer->m_eol)
475490792Sgshapiro							== SM_IO_EOF)
475564562Sgshapiro						break;
475671345Sgshapiro					else
475771345Sgshapiro					{
475871345Sgshapiro						/* record progress for DATA timeout */
475990792Sgshapiro						DataProgress = true;
476071345Sgshapiro					}
476138032Speter					pos = 0;
476238032Speter				}
476338032Speter				else
476438032Speter				{
476538032Speter					pos += bp - buf;
476638032Speter					if (c != '\r')
4767112810Sgshapiro					{
4768112810Sgshapiro						SM_ASSERT(pbp < peekbuf +
4769112810Sgshapiro								sizeof(peekbuf));
477038032Speter						*pbp++ = c;
4771112810Sgshapiro					}
477238032Speter				}
477364562Sgshapiro
477438032Speter				bp = buf;
477538032Speter
477638032Speter				/* determine next state */
477738032Speter				if (c == '\n')
477838032Speter					ostate = OS_HEAD;
477938032Speter				else if (c == '\r')
478038032Speter					ostate = OS_CR;
478138032Speter				else
478238032Speter					ostate = OS_INLINE;
478338032Speter				continue;
478438032Speter
478538032Speter			  case OS_CR:
478638032Speter				if (c == '\n')
478738032Speter				{
478838032Speter					/* got CRLF */
478990792Sgshapiro					if (sm_io_fputs(mci->mci_out,
479090792Sgshapiro							SM_TIME_DEFAULT,
479190792Sgshapiro							mci->mci_mailer->m_eol)
479290792Sgshapiro							== SM_IO_EOF)
479364562Sgshapiro						continue;
479471345Sgshapiro					else
479571345Sgshapiro					{
479671345Sgshapiro						/* record progress for DATA timeout */
479790792Sgshapiro						DataProgress = true;
479871345Sgshapiro					}
479964562Sgshapiro
480038032Speter					if (TrafficLogFile != NULL)
480138032Speter					{
480290792Sgshapiro						(void) sm_io_fputs(TrafficLogFile,
480390792Sgshapiro								   SM_TIME_DEFAULT,
480490792Sgshapiro								   mci->mci_mailer->m_eol);
480538032Speter					}
480638032Speter					ostate = OS_HEAD;
480738032Speter					continue;
480838032Speter				}
480938032Speter
481038032Speter				/* had a naked carriage return */
4811112810Sgshapiro				SM_ASSERT(pbp < peekbuf + sizeof(peekbuf));
481238032Speter				*pbp++ = c;
481338032Speter				c = '\r';
481438032Speter				ostate = OS_INLINE;
481538032Speter				goto putch;
481638032Speter
481738032Speter			  case OS_INLINE:
481838032Speter				if (c == '\r')
481938032Speter				{
482038032Speter					ostate = OS_CR;
482138032Speter					continue;
482238032Speter				}
482338032Speter				if (c == '\0' &&
482490792Sgshapiro				    bitnset(M_NONULLS,
482590792Sgshapiro					    mci->mci_mailer->m_flags))
482638032Speter					break;
482738032Speterputch:
482838032Speter				if (mci->mci_mailer->m_linelimit > 0 &&
482964562Sgshapiro				    pos >= mci->mci_mailer->m_linelimit - 1 &&
483038032Speter				    c != '\n')
483138032Speter				{
483264562Sgshapiro					int d;
483364562Sgshapiro
483464562Sgshapiro					/* check next character for EOL */
483564562Sgshapiro					if (pbp > peekbuf)
483664562Sgshapiro						d = *(pbp - 1);
483790792Sgshapiro					else if ((d = sm_io_getc(e->e_dfp,
483890792Sgshapiro								 SM_TIME_DEFAULT))
483990792Sgshapiro						 != SM_IO_EOF)
4840112810Sgshapiro					{
4841112810Sgshapiro						SM_ASSERT(pbp < peekbuf +
4842112810Sgshapiro								sizeof(peekbuf));
484364562Sgshapiro						*pbp++ = d;
4844112810Sgshapiro					}
484564562Sgshapiro
484690792Sgshapiro					if (d == '\n' || d == SM_IO_EOF)
484764562Sgshapiro					{
484864562Sgshapiro						if (TrafficLogFile != NULL)
484990792Sgshapiro							(void) sm_io_putc(TrafficLogFile,
485090792Sgshapiro									  SM_TIME_DEFAULT,
485190792Sgshapiro									  (unsigned char) c);
485290792Sgshapiro						if (sm_io_putc(mci->mci_out,
485390792Sgshapiro							       SM_TIME_DEFAULT,
485490792Sgshapiro							       (unsigned char) c)
485590792Sgshapiro							       == SM_IO_EOF)
485664562Sgshapiro						{
485790792Sgshapiro							dead = true;
485864562Sgshapiro							continue;
485964562Sgshapiro						}
486071345Sgshapiro						else
486171345Sgshapiro						{
486271345Sgshapiro							/* record progress for DATA timeout */
486390792Sgshapiro							DataProgress = true;
486471345Sgshapiro						}
486564562Sgshapiro						pos++;
486664562Sgshapiro						continue;
486764562Sgshapiro					}
486864562Sgshapiro
486990792Sgshapiro					if (sm_io_putc(mci->mci_out,
487090792Sgshapiro						       SM_TIME_DEFAULT, '!')
487190792Sgshapiro					    == SM_IO_EOF ||
487290792Sgshapiro					    sm_io_fputs(mci->mci_out,
487390792Sgshapiro							SM_TIME_DEFAULT,
487490792Sgshapiro							mci->mci_mailer->m_eol)
487590792Sgshapiro					    == SM_IO_EOF)
487664562Sgshapiro					{
487790792Sgshapiro						dead = true;
487864562Sgshapiro						continue;
487964562Sgshapiro					}
488071345Sgshapiro					else
488171345Sgshapiro					{
488271345Sgshapiro						/* record progress for DATA timeout */
488390792Sgshapiro						DataProgress = true;
488471345Sgshapiro					}
488564562Sgshapiro
488638032Speter					if (TrafficLogFile != NULL)
488738032Speter					{
488890792Sgshapiro						(void) sm_io_fprintf(TrafficLogFile,
488990792Sgshapiro								     SM_TIME_DEFAULT,
489090792Sgshapiro								     "!%s",
489190792Sgshapiro								     mci->mci_mailer->m_eol);
489238032Speter					}
489338032Speter					ostate = OS_HEAD;
4894112810Sgshapiro					SM_ASSERT(pbp < peekbuf +
4895112810Sgshapiro							sizeof(peekbuf));
489638032Speter					*pbp++ = c;
489738032Speter					continue;
489838032Speter				}
489938032Speter				if (c == '\n')
490038032Speter				{
490138032Speter					if (TrafficLogFile != NULL)
490290792Sgshapiro						(void) sm_io_fputs(TrafficLogFile,
490390792Sgshapiro								   SM_TIME_DEFAULT,
490490792Sgshapiro								   mci->mci_mailer->m_eol);
490590792Sgshapiro					if (sm_io_fputs(mci->mci_out,
490690792Sgshapiro							SM_TIME_DEFAULT,
490790792Sgshapiro							mci->mci_mailer->m_eol)
490890792Sgshapiro							== SM_IO_EOF)
490964562Sgshapiro						continue;
491071345Sgshapiro					else
491171345Sgshapiro					{
491271345Sgshapiro						/* record progress for DATA timeout */
491390792Sgshapiro						DataProgress = true;
491471345Sgshapiro					}
491538032Speter					pos = 0;
491638032Speter					ostate = OS_HEAD;
491738032Speter				}
491838032Speter				else
491938032Speter				{
492038032Speter					if (TrafficLogFile != NULL)
492190792Sgshapiro						(void) sm_io_putc(TrafficLogFile,
492290792Sgshapiro								  SM_TIME_DEFAULT,
492390792Sgshapiro								  (unsigned char) c);
492490792Sgshapiro					if (sm_io_putc(mci->mci_out,
492590792Sgshapiro						       SM_TIME_DEFAULT,
492690792Sgshapiro						       (unsigned char) c)
492790792Sgshapiro					    == SM_IO_EOF)
492864562Sgshapiro					{
492990792Sgshapiro						dead = true;
493064562Sgshapiro						continue;
493164562Sgshapiro					}
493271345Sgshapiro					else
493371345Sgshapiro					{
493471345Sgshapiro						/* record progress for DATA timeout */
493590792Sgshapiro						DataProgress = true;
493671345Sgshapiro					}
493738032Speter					pos++;
493838032Speter					ostate = OS_INLINE;
493938032Speter				}
494038032Speter				break;
494138032Speter			}
494238032Speter		}
494338032Speter
494438032Speter		/* make sure we are at the beginning of a line */
494538032Speter		if (bp > buf)
494638032Speter		{
494738032Speter			if (TrafficLogFile != NULL)
494838032Speter			{
494938032Speter				for (xp = buf; xp < bp; xp++)
495090792Sgshapiro					(void) sm_io_putc(TrafficLogFile,
495190792Sgshapiro							  SM_TIME_DEFAULT,
495290792Sgshapiro							  (unsigned char) *xp);
495338032Speter			}
495438032Speter			for (xp = buf; xp < bp; xp++)
495538032Speter			{
495690792Sgshapiro				if (sm_io_putc(mci->mci_out, SM_TIME_DEFAULT,
495790792Sgshapiro					       (unsigned char) *xp)
495890792Sgshapiro				    == SM_IO_EOF)
495964562Sgshapiro				{
496090792Sgshapiro					dead = true;
496164562Sgshapiro					break;
496264562Sgshapiro				}
496371345Sgshapiro				else
496471345Sgshapiro				{
496571345Sgshapiro					/* record progress for DATA timeout */
496690792Sgshapiro					DataProgress = true;
496771345Sgshapiro				}
496838032Speter			}
496938032Speter			pos += bp - buf;
497038032Speter		}
497164562Sgshapiro		if (!dead && pos > 0)
497238032Speter		{
497338032Speter			if (TrafficLogFile != NULL)
497490792Sgshapiro				(void) sm_io_fputs(TrafficLogFile,
497590792Sgshapiro						   SM_TIME_DEFAULT,
497690792Sgshapiro						   mci->mci_mailer->m_eol);
497790792Sgshapiro			(void) sm_io_fputs(mci->mci_out, SM_TIME_DEFAULT,
497890792Sgshapiro					   mci->mci_mailer->m_eol);
497964562Sgshapiro
498064562Sgshapiro			/* record progress for DATA timeout */
498190792Sgshapiro			DataProgress = true;
498238032Speter		}
498338032Speter	}
498438032Speter
498590792Sgshapiro	if (sm_io_error(e->e_dfp))
498638032Speter	{
498790792Sgshapiro		syserr("putbody: %s/%cf%s: read error",
498890792Sgshapiro		       qid_printqueue(e->e_dfqgrp, e->e_dfqdir),
498990792Sgshapiro		       DATAFL_LETTER, e->e_id);
499038032Speter		ExitStat = EX_IOERR;
499138032Speter	}
499238032Speter
499338032Speterendofmessage:
499464562Sgshapiro	/*
499564562Sgshapiro	**  Since mailfile() uses e_dfp in a child process,
499664562Sgshapiro	**  the file offset in the stdio library for the
499764562Sgshapiro	**  parent process will not agree with the in-kernel
499864562Sgshapiro	**  file offset since the file descriptor is shared
499964562Sgshapiro	**  between the processes.  Therefore, it is vital
500064562Sgshapiro	**  that the file always be rewound.  This forces the
500164562Sgshapiro	**  kernel offset (lseek) and stdio library (ftell)
500264562Sgshapiro	**  offset to match.
500364562Sgshapiro	*/
500464562Sgshapiro
500564562Sgshapiro	if (e->e_dfp != NULL)
500664562Sgshapiro		(void) bfrewind(e->e_dfp);
500764562Sgshapiro
500838032Speter	/* some mailers want extra blank line at end of message */
500964562Sgshapiro	if (!dead && bitnset(M_BLANKEND, mci->mci_mailer->m_flags) &&
501038032Speter	    buf[0] != '\0' && buf[0] != '\n')
501138032Speter		putline("", mci);
501238032Speter
501390792Sgshapiro	(void) sm_io_flush(mci->mci_out, SM_TIME_DEFAULT);
501490792Sgshapiro	if (sm_io_error(mci->mci_out) && errno != EPIPE)
501538032Speter	{
501638032Speter		syserr("putbody: write error");
501738032Speter		ExitStat = EX_IOERR;
501838032Speter	}
501964562Sgshapiro
502038032Speter	errno = 0;
502138032Speter}
502290792Sgshapiro/*
502338032Speter**  MAILFILE -- Send a message to a file.
502438032Speter**
502590792Sgshapiro**	If the file has the set-user-ID/set-group-ID bits set, but NO
502690792Sgshapiro**	execute bits, sendmail will try to become the owner of that file
502738032Speter**	rather than the real user.  Obviously, this only works if
502838032Speter**	sendmail runs as root.
502938032Speter**
503038032Speter**	This could be done as a subordinate mailer, except that it
503138032Speter**	is used implicitly to save messages in ~/dead.letter.  We
503238032Speter**	view this as being sufficiently important as to include it
503338032Speter**	here.  For example, if the system is dying, we shouldn't have
503438032Speter**	to create another process plus some pipes to save the message.
503538032Speter**
503638032Speter**	Parameters:
503738032Speter**		filename -- the name of the file to send to.
503838032Speter**		mailer -- mailer definition for recipient -- if NULL,
503938032Speter**			use FileMailer.
504038032Speter**		ctladdr -- the controlling address header -- includes
504138032Speter**			the userid/groupid to be when sending.
504238032Speter**		sfflags -- flags for opening.
504338032Speter**		e -- the current envelope.
504438032Speter**
504538032Speter**	Returns:
504638032Speter**		The exit code associated with the operation.
504738032Speter**
504838032Speter**	Side Effects:
504938032Speter**		none.
505038032Speter*/
505138032Speter
505290792Sgshapiro# define RETURN(st)			exit(st);
505390792Sgshapiro
505438032Speterstatic jmp_buf	CtxMailfileTimeout;
505538032Speter
505638032Speterint
505738032Spetermailfile(filename, mailer, ctladdr, sfflags, e)
505838032Speter	char *volatile filename;
505938032Speter	MAILER *volatile mailer;
506038032Speter	ADDRESS *ctladdr;
506164562Sgshapiro	volatile long sfflags;
506238032Speter	register ENVELOPE *e;
506338032Speter{
506490792Sgshapiro	register SM_FILE_T *f;
506538032Speter	register pid_t pid = -1;
506664562Sgshapiro	volatile int mode;
506764562Sgshapiro	int len;
506864562Sgshapiro	off_t curoff;
506938032Speter	bool suidwarn = geteuid() == 0;
507038032Speter	char *p;
507164562Sgshapiro	char *volatile realfile;
507290792Sgshapiro	SM_EVENT *ev;
507398121Sgshapiro	char buf[MAXPATHLEN];
507498121Sgshapiro	char targetfile[MAXPATHLEN];
507538032Speter
507638032Speter	if (tTd(11, 1))
507738032Speter	{
507890792Sgshapiro		sm_dprintf("mailfile %s\n  ctladdr=", filename);
5079132943Sgshapiro		printaddr(sm_debug_file(), ctladdr, false);
508038032Speter	}
508138032Speter
508238032Speter	if (mailer == NULL)
508338032Speter		mailer = FileMailer;
508438032Speter
508538032Speter	if (e->e_xfp != NULL)
508690792Sgshapiro		(void) sm_io_flush(e->e_xfp, SM_TIME_DEFAULT);
508738032Speter
508838032Speter	/*
508938032Speter	**  Special case /dev/null.  This allows us to restrict file
509038032Speter	**  delivery to regular files only.
509138032Speter	*/
509238032Speter
509390792Sgshapiro	if (sm_path_isdevnull(filename))
509438032Speter		return EX_OK;
509538032Speter
509638032Speter	/* check for 8-bit available */
509738032Speter	if (bitset(EF_HAS8BIT, e->e_flags) &&
509838032Speter	    bitnset(M_7BITS, mailer->m_flags) &&
509938032Speter	    (bitset(EF_DONT_MIME, e->e_flags) ||
510038032Speter	     !(bitset(MM_MIME8BIT, MimeMode) ||
510138032Speter	       (bitset(EF_IS_MIME, e->e_flags) &&
510238032Speter		bitset(MM_CVTMIME, MimeMode)))))
510338032Speter	{
510438032Speter		e->e_status = "5.6.3";
510564562Sgshapiro		usrerrenh(e->e_status,
510690792Sgshapiro			  "554 Cannot send 8-bit data to 7-bit destination");
510790792Sgshapiro		errno = 0;
510864562Sgshapiro		return EX_DATAERR;
510938032Speter	}
511038032Speter
511164562Sgshapiro	/* Find the actual file */
511264562Sgshapiro	if (SafeFileEnv != NULL && SafeFileEnv[0] != '\0')
511364562Sgshapiro	{
511464562Sgshapiro		len = strlen(SafeFileEnv);
511564562Sgshapiro
511664562Sgshapiro		if (strncmp(SafeFileEnv, filename, len) == 0)
511764562Sgshapiro			filename += len;
511864562Sgshapiro
511990792Sgshapiro		if (len + strlen(filename) + 1 >= sizeof targetfile)
512064562Sgshapiro		{
512164562Sgshapiro			syserr("mailfile: filename too long (%s/%s)",
512264562Sgshapiro			       SafeFileEnv, filename);
512364562Sgshapiro			return EX_CANTCREAT;
512464562Sgshapiro		}
512590792Sgshapiro		(void) sm_strlcpy(targetfile, SafeFileEnv, sizeof targetfile);
512664562Sgshapiro		realfile = targetfile + len;
512764562Sgshapiro		if (*filename == '/')
512864562Sgshapiro			filename++;
512994334Sgshapiro		if (*filename != '\0')
513094334Sgshapiro		{
513194334Sgshapiro			/* paranoia: trailing / should be removed in readcf */
513294334Sgshapiro			if (targetfile[len - 1] != '/')
513394334Sgshapiro				(void) sm_strlcat(targetfile,
513494334Sgshapiro						  "/", sizeof targetfile);
513594334Sgshapiro			(void) sm_strlcat(targetfile, filename,
513694334Sgshapiro					  sizeof targetfile);
513794334Sgshapiro		}
513864562Sgshapiro	}
513964562Sgshapiro	else if (mailer->m_rootdir != NULL)
514064562Sgshapiro	{
514164562Sgshapiro		expand(mailer->m_rootdir, targetfile, sizeof targetfile, e);
514264562Sgshapiro		len = strlen(targetfile);
514364562Sgshapiro
514464562Sgshapiro		if (strncmp(targetfile, filename, len) == 0)
514564562Sgshapiro			filename += len;
514664562Sgshapiro
514790792Sgshapiro		if (len + strlen(filename) + 1 >= sizeof targetfile)
514864562Sgshapiro		{
514964562Sgshapiro			syserr("mailfile: filename too long (%s/%s)",
515064562Sgshapiro			       targetfile, filename);
515164562Sgshapiro			return EX_CANTCREAT;
515264562Sgshapiro		}
515364562Sgshapiro		realfile = targetfile + len;
515464562Sgshapiro		if (targetfile[len - 1] != '/')
515590792Sgshapiro			(void) sm_strlcat(targetfile, "/", sizeof targetfile);
515664562Sgshapiro		if (*filename == '/')
515790792Sgshapiro			(void) sm_strlcat(targetfile, filename + 1,
515890792Sgshapiro					  sizeof targetfile);
515964562Sgshapiro		else
516090792Sgshapiro			(void) sm_strlcat(targetfile, filename,
516190792Sgshapiro					  sizeof targetfile);
516264562Sgshapiro	}
516364562Sgshapiro	else
516464562Sgshapiro	{
516590792Sgshapiro		if (sm_strlcpy(targetfile, filename, sizeof targetfile) >=
516690792Sgshapiro		    sizeof targetfile)
516764562Sgshapiro		{
516864562Sgshapiro			syserr("mailfile: filename too long (%s)", filename);
516964562Sgshapiro			return EX_CANTCREAT;
517064562Sgshapiro		}
517164562Sgshapiro		realfile = targetfile;
517264562Sgshapiro	}
517364562Sgshapiro
517438032Speter	/*
517538032Speter	**  Fork so we can change permissions here.
517638032Speter	**	Note that we MUST use fork, not vfork, because of
517738032Speter	**	the complications of calling subroutines, etc.
517838032Speter	*/
517938032Speter
518094334Sgshapiro
518194334Sgshapiro	/*
518294334Sgshapiro	**  Dispose of SIGCHLD signal catchers that may be laying
518394334Sgshapiro	**  around so that the waitfor() below will get it.
518494334Sgshapiro	*/
518594334Sgshapiro
518694334Sgshapiro	(void) sm_signal(SIGCHLD, SIG_DFL);
518794334Sgshapiro
518838032Speter	DOFORK(fork);
518938032Speter
519038032Speter	if (pid < 0)
519164562Sgshapiro		return EX_OSERR;
519238032Speter	else if (pid == 0)
519338032Speter	{
519438032Speter		/* child -- actually write to file */
519538032Speter		struct stat stb;
519638032Speter		MCI mcibuf;
519742575Speter		int err;
519838032Speter		volatile int oflags = O_WRONLY|O_APPEND;
519938032Speter
520077349Sgshapiro		/* Reset global flags */
520177349Sgshapiro		RestartRequest = NULL;
520290792Sgshapiro		RestartWorkGroup = false;
520377349Sgshapiro		ShutdownRequest = NULL;
520477349Sgshapiro		PendingSignal = 0;
520590792Sgshapiro		CurrentPid = getpid();
520677349Sgshapiro
520738032Speter		if (e->e_lockfp != NULL)
520890792Sgshapiro			(void) close(sm_io_getinfo(e->e_lockfp, SM_IO_WHAT_FD,
520990792Sgshapiro				     NULL));
521038032Speter
521190792Sgshapiro		(void) sm_signal(SIGINT, SIG_DFL);
521290792Sgshapiro		(void) sm_signal(SIGHUP, SIG_DFL);
521390792Sgshapiro		(void) sm_signal(SIGTERM, SIG_DFL);
521438032Speter		(void) umask(OldUmask);
521538032Speter		e->e_to = filename;
521638032Speter		ExitStat = EX_OK;
521738032Speter
521838032Speter		if (setjmp(CtxMailfileTimeout) != 0)
521938032Speter		{
522090792Sgshapiro			RETURN(EX_TEMPFAIL);
522138032Speter		}
522238032Speter
522338032Speter		if (TimeOuts.to_fileopen > 0)
522490792Sgshapiro			ev = sm_setevent(TimeOuts.to_fileopen, mailfiletimeout,
522590792Sgshapiro					 0);
522638032Speter		else
522738032Speter			ev = NULL;
522838032Speter
522990792Sgshapiro		/* check file mode to see if set-user-ID */
523064562Sgshapiro		if (stat(targetfile, &stb) < 0)
523164562Sgshapiro			mode = FileMode;
523242575Speter		else
523338032Speter			mode = stb.st_mode;
523438032Speter
523538032Speter		/* limit the errors to those actually caused in the child */
523638032Speter		errno = 0;
523738032Speter		ExitStat = EX_OK;
523838032Speter
523964562Sgshapiro		/* Allow alias expansions to use the S_IS{U,G}ID bits */
524064562Sgshapiro		if ((ctladdr != NULL && !bitset(QALIAS, ctladdr->q_flags)) ||
524164562Sgshapiro		    bitset(SFF_RUNASREALUID, sfflags))
524238032Speter		{
524390792Sgshapiro			/* ignore set-user-ID and set-group-ID bits */
524438032Speter			mode &= ~(S_ISGID|S_ISUID);
524564562Sgshapiro			if (tTd(11, 20))
524690792Sgshapiro				sm_dprintf("mailfile: ignoring set-user-ID/set-group-ID bits\n");
524738032Speter		}
524838032Speter
524990792Sgshapiro		/* we have to open the data file BEFORE setuid() */
525038032Speter		if (e->e_dfp == NULL && bitset(EF_HAS_DF, e->e_flags))
525138032Speter		{
525290792Sgshapiro			char *df = queuename(e, DATAFL_LETTER);
525338032Speter
525490792Sgshapiro			e->e_dfp = sm_io_open(SmFtStdio, SM_TIME_DEFAULT, df,
5255120256Sgshapiro					      SM_IO_RDONLY_B, NULL);
525638032Speter			if (e->e_dfp == NULL)
525738032Speter			{
525838032Speter				syserr("mailfile: Cannot open %s for %s from %s",
525938032Speter					df, e->e_to, e->e_from.q_paddr);
526038032Speter			}
526138032Speter		}
526238032Speter
526338032Speter		/* select a new user to run as */
526438032Speter		if (!bitset(SFF_RUNASREALUID, sfflags))
526538032Speter		{
526638032Speter			if (bitnset(M_SPECIFIC_UID, mailer->m_flags))
526738032Speter			{
526838032Speter				RealUserName = NULL;
5269132943Sgshapiro				if (mailer->m_uid == NO_UID)
5270132943Sgshapiro					RealUid = RunAsUid;
5271132943Sgshapiro				else
5272132943Sgshapiro					RealUid = mailer->m_uid;
527364562Sgshapiro				if (RunAsUid != 0 && RealUid != RunAsUid)
527464562Sgshapiro				{
527564562Sgshapiro					/* Only root can change the uid */
527690792Sgshapiro					syserr("mailfile: insufficient privileges to change uid, RunAsUid=%d, RealUid=%d",
527790792Sgshapiro						(int) RunAsUid, (int) RealUid);
527890792Sgshapiro					RETURN(EX_TEMPFAIL);
527964562Sgshapiro				}
528038032Speter			}
528138032Speter			else if (bitset(S_ISUID, mode))
528238032Speter			{
528338032Speter				RealUserName = NULL;
528438032Speter				RealUid = stb.st_uid;
528538032Speter			}
528638032Speter			else if (ctladdr != NULL && ctladdr->q_uid != 0)
528738032Speter			{
528838032Speter				if (ctladdr->q_ruser != NULL)
528938032Speter					RealUserName = ctladdr->q_ruser;
529038032Speter				else
529138032Speter					RealUserName = ctladdr->q_user;
529238032Speter				RealUid = ctladdr->q_uid;
529338032Speter			}
5294132943Sgshapiro			else if (mailer != NULL && mailer->m_uid != NO_UID)
529538032Speter			{
529638032Speter				RealUserName = DefUser;
529738032Speter				RealUid = mailer->m_uid;
529838032Speter			}
529938032Speter			else
530038032Speter			{
530138032Speter				RealUserName = DefUser;
530238032Speter				RealUid = DefUid;
530338032Speter			}
530438032Speter
530538032Speter			/* select a new group to run as */
530638032Speter			if (bitnset(M_SPECIFIC_UID, mailer->m_flags))
530764562Sgshapiro			{
5308132943Sgshapiro				if (mailer->m_gid == NO_GID)
5309132943Sgshapiro					RealGid = RunAsGid;
5310132943Sgshapiro				else
5311132943Sgshapiro					RealGid = mailer->m_gid;
531264562Sgshapiro				if (RunAsUid != 0 &&
531364562Sgshapiro				    (RealGid != getgid() ||
531464562Sgshapiro				     RealGid != getegid()))
531564562Sgshapiro				{
531664562Sgshapiro					/* Only root can change the gid */
531790792Sgshapiro					syserr("mailfile: insufficient privileges to change gid, RealGid=%d, RunAsUid=%d, gid=%d, egid=%d",
531890792Sgshapiro					       (int) RealGid, (int) RunAsUid,
531990792Sgshapiro					       (int) getgid(), (int) getegid());
532090792Sgshapiro					RETURN(EX_TEMPFAIL);
532164562Sgshapiro				}
532264562Sgshapiro			}
532338032Speter			else if (bitset(S_ISGID, mode))
532438032Speter				RealGid = stb.st_gid;
532564562Sgshapiro			else if (ctladdr != NULL &&
532664562Sgshapiro				 ctladdr->q_uid == DefUid &&
532764562Sgshapiro				 ctladdr->q_gid == 0)
532871345Sgshapiro			{
532971345Sgshapiro				/*
533071345Sgshapiro				**  Special case:  This means it is an
533171345Sgshapiro				**  alias and we should act as DefaultUser.
533271345Sgshapiro				**  See alias()'s comments.
533371345Sgshapiro				*/
533471345Sgshapiro
533564562Sgshapiro				RealGid = DefGid;
533671345Sgshapiro				RealUserName = DefUser;
533771345Sgshapiro			}
533871345Sgshapiro			else if (ctladdr != NULL && ctladdr->q_uid != 0)
533971345Sgshapiro				RealGid = ctladdr->q_gid;
5340132943Sgshapiro			else if (mailer != NULL && mailer->m_gid != NO_GID)
534138032Speter				RealGid = mailer->m_gid;
534238032Speter			else
534338032Speter				RealGid = DefGid;
534438032Speter		}
534538032Speter
534638032Speter		/* last ditch */
534738032Speter		if (!bitset(SFF_ROOTOK, sfflags))
534838032Speter		{
534938032Speter			if (RealUid == 0)
535038032Speter				RealUid = DefUid;
535138032Speter			if (RealGid == 0)
535238032Speter				RealGid = DefGid;
535338032Speter		}
535438032Speter
535538032Speter		/* set group id list (needs /etc/group access) */
535638032Speter		if (RealUserName != NULL && !DontInitGroups)
535738032Speter		{
535838032Speter			if (initgroups(RealUserName, RealGid) == -1 && suidwarn)
535964562Sgshapiro			{
536038032Speter				syserr("mailfile: initgroups(%s, %d) failed",
536138032Speter					RealUserName, RealGid);
536290792Sgshapiro				RETURN(EX_TEMPFAIL);
536364562Sgshapiro			}
536438032Speter		}
536538032Speter		else
536638032Speter		{
536738032Speter			GIDSET_T gidset[1];
536838032Speter
536938032Speter			gidset[0] = RealGid;
537038032Speter			if (setgroups(1, gidset) == -1 && suidwarn)
537164562Sgshapiro			{
537238032Speter				syserr("mailfile: setgroups() failed");
537390792Sgshapiro				RETURN(EX_TEMPFAIL);
537464562Sgshapiro			}
537538032Speter		}
537638032Speter
537764562Sgshapiro		/*
537864562Sgshapiro		**  If you have a safe environment, go into it.
537964562Sgshapiro		*/
538064562Sgshapiro
538164562Sgshapiro		if (realfile != targetfile)
538238032Speter		{
538394334Sgshapiro			char save;
538494334Sgshapiro
538594334Sgshapiro			save = *realfile;
538664562Sgshapiro			*realfile = '\0';
538764562Sgshapiro			if (tTd(11, 20))
538890792Sgshapiro				sm_dprintf("mailfile: chroot %s\n", targetfile);
538964562Sgshapiro			if (chroot(targetfile) < 0)
539038032Speter			{
539138032Speter				syserr("mailfile: Cannot chroot(%s)",
539264562Sgshapiro				       targetfile);
539390792Sgshapiro				RETURN(EX_CANTCREAT);
539438032Speter			}
539594334Sgshapiro			*realfile = save;
539638032Speter		}
539764562Sgshapiro
539864562Sgshapiro		if (tTd(11, 40))
539990792Sgshapiro			sm_dprintf("mailfile: deliver to %s\n", realfile);
540064562Sgshapiro
540138032Speter		if (chdir("/") < 0)
540264562Sgshapiro		{
540338032Speter			syserr("mailfile: cannot chdir(/)");
540490792Sgshapiro			RETURN(EX_CANTCREAT);
540564562Sgshapiro		}
540638032Speter
540738032Speter		/* now reset the group and user ids */
540838032Speter		endpwent();
540990792Sgshapiro		sm_mbdb_terminate();
541038032Speter		if (setgid(RealGid) < 0 && suidwarn)
541164562Sgshapiro		{
541238032Speter			syserr("mailfile: setgid(%ld) failed", (long) RealGid);
541390792Sgshapiro			RETURN(EX_TEMPFAIL);
541464562Sgshapiro		}
541538032Speter		vendor_set_uid(RealUid);
541638032Speter		if (setuid(RealUid) < 0 && suidwarn)
541764562Sgshapiro		{
541838032Speter			syserr("mailfile: setuid(%ld) failed", (long) RealUid);
541990792Sgshapiro			RETURN(EX_TEMPFAIL);
542064562Sgshapiro		}
542138032Speter
542264562Sgshapiro		if (tTd(11, 2))
542390792Sgshapiro			sm_dprintf("mailfile: running as r/euid=%d/%d, r/egid=%d/%d\n",
542464562Sgshapiro				(int) getuid(), (int) geteuid(),
542564562Sgshapiro				(int) getgid(), (int) getegid());
542664562Sgshapiro
542764562Sgshapiro
542838032Speter		/* move into some "safe" directory */
542938032Speter		if (mailer->m_execdir != NULL)
543038032Speter		{
543138032Speter			char *q;
543238032Speter
543338032Speter			for (p = mailer->m_execdir; p != NULL; p = q)
543438032Speter			{
543538032Speter				q = strchr(p, ':');
543638032Speter				if (q != NULL)
543738032Speter					*q = '\0';
543838032Speter				expand(p, buf, sizeof buf, e);
543938032Speter				if (q != NULL)
544038032Speter					*q++ = ':';
544138032Speter				if (tTd(11, 20))
544290792Sgshapiro					sm_dprintf("mailfile: trydir %s\n",
544390792Sgshapiro						   buf);
544438032Speter				if (buf[0] != '\0' && chdir(buf) >= 0)
544538032Speter					break;
544638032Speter			}
544738032Speter		}
544838032Speter
544964562Sgshapiro		/*
545064562Sgshapiro		**  Recheck the file after we have assumed the ID of the
545164562Sgshapiro		**  delivery user to make sure we can deliver to it as
545264562Sgshapiro		**  that user.  This is necessary if sendmail is running
545364562Sgshapiro		**  as root and the file is on an NFS mount which treats
545464562Sgshapiro		**  root as nobody.
545564562Sgshapiro		*/
545664562Sgshapiro
545764562Sgshapiro#if HASLSTAT
545864562Sgshapiro		if (bitnset(DBS_FILEDELIVERYTOSYMLINK, DontBlameSendmail))
545964562Sgshapiro			err = stat(realfile, &stb);
546064562Sgshapiro		else
546164562Sgshapiro			err = lstat(realfile, &stb);
546264562Sgshapiro#else /* HASLSTAT */
546364562Sgshapiro		err = stat(realfile, &stb);
546464562Sgshapiro#endif /* HASLSTAT */
546564562Sgshapiro
546664562Sgshapiro		if (err < 0)
546764562Sgshapiro		{
546864562Sgshapiro			stb.st_mode = ST_MODE_NOFILE;
546964562Sgshapiro			mode = FileMode;
547064562Sgshapiro			oflags |= O_CREAT|O_EXCL;
547164562Sgshapiro		}
547264562Sgshapiro		else if (bitset(S_IXUSR|S_IXGRP|S_IXOTH, mode) ||
547364562Sgshapiro			 (!bitnset(DBS_FILEDELIVERYTOHARDLINK,
547464562Sgshapiro				   DontBlameSendmail) &&
547564562Sgshapiro			  stb.st_nlink != 1) ||
547664562Sgshapiro			 (realfile != targetfile && !S_ISREG(mode)))
547764562Sgshapiro			exit(EX_CANTCREAT);
547864562Sgshapiro		else
547964562Sgshapiro			mode = stb.st_mode;
548064562Sgshapiro
548164562Sgshapiro		if (!bitnset(DBS_FILEDELIVERYTOSYMLINK, DontBlameSendmail))
548238032Speter			sfflags |= SFF_NOSLINK;
548364562Sgshapiro		if (!bitnset(DBS_FILEDELIVERYTOHARDLINK, DontBlameSendmail))
548438032Speter			sfflags |= SFF_NOHLINK;
548538032Speter		sfflags &= ~SFF_OPENASROOT;
548664562Sgshapiro		f = safefopen(realfile, oflags, mode, sfflags);
548738032Speter		if (f == NULL)
548838032Speter		{
548964562Sgshapiro			if (transienterror(errno))
549064562Sgshapiro			{
549164562Sgshapiro				usrerr("454 4.3.0 cannot open %s: %s",
549264562Sgshapiro				       shortenstring(realfile, MAXSHORTSTR),
549390792Sgshapiro				       sm_errstring(errno));
549490792Sgshapiro				RETURN(EX_TEMPFAIL);
549564562Sgshapiro			}
549664562Sgshapiro			else
549764562Sgshapiro			{
549864562Sgshapiro				usrerr("554 5.3.0 cannot open %s: %s",
549964562Sgshapiro				       shortenstring(realfile, MAXSHORTSTR),
550090792Sgshapiro				       sm_errstring(errno));
550190792Sgshapiro				RETURN(EX_CANTCREAT);
550264562Sgshapiro			}
550338032Speter		}
550490792Sgshapiro		if (filechanged(realfile, sm_io_getinfo(f, SM_IO_WHAT_FD, NULL),
550590792Sgshapiro		    &stb))
550638032Speter		{
550764562Sgshapiro			syserr("554 5.3.0 file changed after open");
550890792Sgshapiro			RETURN(EX_CANTCREAT);
550938032Speter		}
551090792Sgshapiro		if (fstat(sm_io_getinfo(f, SM_IO_WHAT_FD, NULL), &stb) < 0)
551138032Speter		{
551290792Sgshapiro			syserr("554 5.3.0 cannot fstat %s",
551390792Sgshapiro				sm_errstring(errno));
551490792Sgshapiro			RETURN(EX_CANTCREAT);
551538032Speter		}
551638032Speter
551764562Sgshapiro		curoff = stb.st_size;
551864562Sgshapiro
551938032Speter		if (ev != NULL)
552090792Sgshapiro			sm_clrevent(ev);
552138032Speter
552264562Sgshapiro		memset(&mcibuf, '\0', sizeof mcibuf);
552338032Speter		mcibuf.mci_mailer = mailer;
552438032Speter		mcibuf.mci_out = f;
552538032Speter		if (bitnset(M_7BITS, mailer->m_flags))
552638032Speter			mcibuf.mci_flags |= MCIF_7BIT;
552738032Speter
552838032Speter		/* clear out per-message flags from connection structure */
552938032Speter		mcibuf.mci_flags &= ~(MCIF_CVT7TO8|MCIF_CVT8TO7);
553038032Speter
553138032Speter		if (bitset(EF_HAS8BIT, e->e_flags) &&
553238032Speter		    !bitset(EF_DONT_MIME, e->e_flags) &&
553338032Speter		    bitnset(M_7BITS, mailer->m_flags))
553438032Speter			mcibuf.mci_flags |= MCIF_CVT8TO7;
553538032Speter
553638032Speter#if MIME7TO8
553738032Speter		if (bitnset(M_MAKE8BIT, mailer->m_flags) &&
553838032Speter		    !bitset(MCIF_7BIT, mcibuf.mci_flags) &&
553938032Speter		    (p = hvalue("Content-Transfer-Encoding", e->e_header)) != NULL &&
554090792Sgshapiro		    (sm_strcasecmp(p, "quoted-printable") == 0 ||
554190792Sgshapiro		     sm_strcasecmp(p, "base64") == 0) &&
554238032Speter		    (p = hvalue("Content-Type", e->e_header)) != NULL)
554338032Speter		{
554438032Speter			/* may want to convert 7 -> 8 */
554538032Speter			/* XXX should really parse it here -- and use a class XXX */
554690792Sgshapiro			if (sm_strncasecmp(p, "text/plain", 10) == 0 &&
554764562Sgshapiro			    (p[10] == '\0' || p[10] == ' ' || p[10] == ';'))
554838032Speter				mcibuf.mci_flags |= MCIF_CVT7TO8;
554938032Speter		}
555064562Sgshapiro#endif /* MIME7TO8 */
555138032Speter
555238032Speter		putfromline(&mcibuf, e);
555343730Speter		(*e->e_puthdr)(&mcibuf, e->e_header, e, M87F_OUTER);
555438032Speter		(*e->e_putbody)(&mcibuf, e, NULL);
555538032Speter		putline("\n", &mcibuf);
555690792Sgshapiro		if (sm_io_flush(f, SM_TIME_DEFAULT) != 0 ||
555790792Sgshapiro		    (SuperSafe != SAFE_NO &&
555890792Sgshapiro		     fsync(sm_io_getinfo(f, SM_IO_WHAT_FD, NULL)) < 0) ||
555990792Sgshapiro		    sm_io_error(f))
556038032Speter		{
556138032Speter			setstat(EX_IOERR);
556264562Sgshapiro#if !NOFTRUNCATE
556390792Sgshapiro			(void) ftruncate(sm_io_getinfo(f, SM_IO_WHAT_FD, NULL),
556490792Sgshapiro					 curoff);
556564562Sgshapiro#endif /* !NOFTRUNCATE */
556638032Speter		}
556738032Speter
556838032Speter		/* reset ISUID & ISGID bits for paranoid systems */
556938032Speter#if HASFCHMOD
557090792Sgshapiro		(void) fchmod(sm_io_getinfo(f, SM_IO_WHAT_FD, NULL),
557190792Sgshapiro			      (MODE_T) mode);
557264562Sgshapiro#else /* HASFCHMOD */
557364562Sgshapiro		(void) chmod(filename, (MODE_T) mode);
557464562Sgshapiro#endif /* HASFCHMOD */
557590792Sgshapiro		if (sm_io_close(f, SM_TIME_DEFAULT) < 0)
557664562Sgshapiro			setstat(EX_IOERR);
557790792Sgshapiro		(void) sm_io_flush(smioout, SM_TIME_DEFAULT);
557864562Sgshapiro		(void) setuid(RealUid);
557938032Speter		exit(ExitStat);
558064562Sgshapiro		/* NOTREACHED */
558138032Speter	}
558238032Speter	else
558338032Speter	{
558438032Speter		/* parent -- wait for exit status */
558538032Speter		int st;
558638032Speter
558738032Speter		st = waitfor(pid);
558838032Speter		if (st == -1)
558938032Speter		{
559038032Speter			syserr("mailfile: %s: wait", mailer->m_name);
559164562Sgshapiro			return EX_SOFTWARE;
559238032Speter		}
559338032Speter		if (WIFEXITED(st))
559490792Sgshapiro		{
559590792Sgshapiro			errno = 0;
559638032Speter			return (WEXITSTATUS(st));
559790792Sgshapiro		}
559838032Speter		else
559938032Speter		{
560038032Speter			syserr("mailfile: %s: child died on signal %d",
560138032Speter			       mailer->m_name, st);
560264562Sgshapiro			return EX_UNAVAILABLE;
560338032Speter		}
560464562Sgshapiro		/* NOTREACHED */
560538032Speter	}
560638032Speter	return EX_UNAVAILABLE;	/* avoid compiler warning on IRIX */
560738032Speter}
560838032Speter
560938032Speterstatic void
5610141858Sgshapiromailfiletimeout(ignore)
5611141858Sgshapiro	int ignore;
561238032Speter{
561377349Sgshapiro	/*
561477349Sgshapiro	**  NOTE: THIS CAN BE CALLED FROM A SIGNAL HANDLER.  DO NOT ADD
561577349Sgshapiro	**	ANYTHING TO THIS ROUTINE UNLESS YOU KNOW WHAT YOU ARE
561677349Sgshapiro	**	DOING.
561777349Sgshapiro	*/
561877349Sgshapiro
561977349Sgshapiro	errno = ETIMEDOUT;
562038032Speter	longjmp(CtxMailfileTimeout, 1);
562138032Speter}
562290792Sgshapiro/*
562338032Speter**  HOSTSIGNATURE -- return the "signature" for a host.
562438032Speter**
562538032Speter**	The signature describes how we are going to send this -- it
562638032Speter**	can be just the hostname (for non-Internet hosts) or can be
562738032Speter**	an ordered list of MX hosts.
562838032Speter**
562938032Speter**	Parameters:
563038032Speter**		m -- the mailer describing this host.
563138032Speter**		host -- the host name.
563238032Speter**
563338032Speter**	Returns:
563438032Speter**		The signature for this host.
563538032Speter**
563638032Speter**	Side Effects:
563738032Speter**		Can tweak the symbol table.
563838032Speter*/
563990792Sgshapiro
564064562Sgshapiro#define MAXHOSTSIGNATURE	8192	/* max len of hostsignature */
564138032Speter
564290792Sgshapirochar *
564364562Sgshapirohostsignature(m, host)
564438032Speter	register MAILER *m;
564538032Speter	char *host;
564638032Speter{
564738032Speter	register char *p;
564838032Speter	register STAB *s;
564990792Sgshapiro	time_t now;
565064562Sgshapiro#if NAMED_BIND
565164562Sgshapiro	char sep = ':';
565264562Sgshapiro	char prevsep = ':';
565338032Speter	int i;
565438032Speter	int len;
565538032Speter	int nmx;
565664562Sgshapiro	int hl;
565738032Speter	char *hp;
565838032Speter	char *endp;
565938032Speter	int oldoptions = _res.options;
566038032Speter	char *mxhosts[MAXMXHOSTS + 1];
566190792Sgshapiro	unsigned short mxprefs[MAXMXHOSTS + 1];
566264562Sgshapiro#endif /* NAMED_BIND */
566338032Speter
566464562Sgshapiro	if (tTd(17, 3))
566590792Sgshapiro		sm_dprintf("hostsignature(%s)\n", host);
566664562Sgshapiro
566738032Speter	/*
566877349Sgshapiro	**  If local delivery (and not remote), just return a constant.
566964562Sgshapiro	*/
567064562Sgshapiro
567177349Sgshapiro	if (bitnset(M_LOCALMAILER, m->m_flags) &&
567290792Sgshapiro	    strcmp(m->m_mailer, "[IPC]") != 0 &&
567390792Sgshapiro	    !(m->m_argv[0] != NULL && strcmp(m->m_argv[0], "TCP") == 0))
567464562Sgshapiro		return "localhost";
567564562Sgshapiro
567664562Sgshapiro	/*
567738032Speter	**  Check to see if this uses IPC -- if not, it can't have MX records.
567838032Speter	*/
567938032Speter
568090792Sgshapiro	if (strcmp(m->m_mailer, "[IPC]") != 0 ||
568190792Sgshapiro	    CurEnv->e_sendmode == SM_DEFER)
568238032Speter	{
568390792Sgshapiro		/* just an ordinary mailer or deferred mode */
568438032Speter		return host;
568538032Speter	}
568664562Sgshapiro#if NETUNIX
568764562Sgshapiro	else if (m->m_argv[0] != NULL &&
568864562Sgshapiro		 strcmp(m->m_argv[0], "FILE") == 0)
568964562Sgshapiro	{
569064562Sgshapiro		/* rendezvous in the file system, no MX records */
569164562Sgshapiro		return host;
569264562Sgshapiro	}
569364562Sgshapiro#endif /* NETUNIX */
569438032Speter
569538032Speter	/*
569638032Speter	**  Look it up in the symbol table.
569738032Speter	*/
569838032Speter
569990792Sgshapiro	now = curtime();
570038032Speter	s = stab(host, ST_HOSTSIG, ST_ENTER);
570190792Sgshapiro	if (s->s_hostsig.hs_sig != NULL)
570264562Sgshapiro	{
570390792Sgshapiro		if (s->s_hostsig.hs_exp >= now)
570490792Sgshapiro		{
570590792Sgshapiro			if (tTd(17, 3))
570690792Sgshapiro				sm_dprintf("hostsignature(): stab(%s) found %s\n", host,
570790792Sgshapiro					   s->s_hostsig.hs_sig);
570890792Sgshapiro			return s->s_hostsig.hs_sig;
570990792Sgshapiro		}
571090792Sgshapiro
571190792Sgshapiro		/* signature is expired: clear it */
571290792Sgshapiro		sm_free(s->s_hostsig.hs_sig);
571390792Sgshapiro		s->s_hostsig.hs_sig = NULL;
571464562Sgshapiro	}
571538032Speter
571690792Sgshapiro	/* set default TTL */
571790792Sgshapiro	s->s_hostsig.hs_exp = now + SM_DEFAULT_TTL;
571890792Sgshapiro
571938032Speter	/*
572090792Sgshapiro	**  Not already there or expired -- create a signature.
572138032Speter	*/
572238032Speter
572338032Speter#if NAMED_BIND
572438032Speter	if (ConfigLevel < 2)
572538032Speter		_res.options &= ~(RES_DEFNAMES | RES_DNSRCH);	/* XXX */
572638032Speter
572738032Speter	for (hp = host; hp != NULL; hp = endp)
572838032Speter	{
572964562Sgshapiro#if NETINET6
573064562Sgshapiro		if (*hp == '[')
573164562Sgshapiro		{
573264562Sgshapiro			endp = strchr(hp + 1, ']');
573364562Sgshapiro			if (endp != NULL)
573464562Sgshapiro				endp = strpbrk(endp + 1, ":,");
573564562Sgshapiro		}
573664562Sgshapiro		else
573764562Sgshapiro			endp = strpbrk(hp, ":,");
573864562Sgshapiro#else /* NETINET6 */
573964562Sgshapiro		endp = strpbrk(hp, ":,");
574064562Sgshapiro#endif /* NETINET6 */
574138032Speter		if (endp != NULL)
574264562Sgshapiro		{
574364562Sgshapiro			sep = *endp;
574438032Speter			*endp = '\0';
574564562Sgshapiro		}
574638032Speter
574738032Speter		if (bitnset(M_NOMX, m->m_flags))
574838032Speter		{
574938032Speter			/* skip MX lookups */
575038032Speter			nmx = 1;
575138032Speter			mxhosts[0] = hp;
575238032Speter		}
575338032Speter		else
575438032Speter		{
575538032Speter			auto int rcode;
575690792Sgshapiro			int ttl;
575738032Speter
575890792Sgshapiro			nmx = getmxrr(hp, mxhosts, mxprefs, true, &rcode, true,
575990792Sgshapiro				      &ttl);
576038032Speter			if (nmx <= 0)
576138032Speter			{
576280785Sgshapiro				int save_errno;
576338032Speter				register MCI *mci;
576438032Speter
576538032Speter				/* update the connection info for this host */
576680785Sgshapiro				save_errno = errno;
576738032Speter				mci = mci_get(hp, m);
576880785Sgshapiro				mci->mci_errno = save_errno;
576938032Speter				mci->mci_herrno = h_errno;
577071345Sgshapiro				mci->mci_lastuse = now;
577164562Sgshapiro				if (rcode == EX_NOHOST)
577264562Sgshapiro					mci_setstat(mci, rcode, "5.1.2",
577390792Sgshapiro						    "550 Host unknown");
577464562Sgshapiro				else
577564562Sgshapiro					mci_setstat(mci, rcode, NULL, NULL);
577638032Speter
577738032Speter				/* use the original host name as signature */
577838032Speter				nmx = 1;
577938032Speter				mxhosts[0] = hp;
578038032Speter			}
578164562Sgshapiro			if (tTd(17, 3))
578290792Sgshapiro				sm_dprintf("hostsignature(): getmxrr() returned %d, mxhosts[0]=%s\n",
578390792Sgshapiro					   nmx, mxhosts[0]);
578490792Sgshapiro
578590792Sgshapiro			/*
578690792Sgshapiro			**  Set new TTL: we use only one!
578790792Sgshapiro			**	We could try to use the minimum instead.
578890792Sgshapiro			*/
578990792Sgshapiro
579090792Sgshapiro			s->s_hostsig.hs_exp = now + SM_MIN(ttl, SM_DEFAULT_TTL);
579138032Speter		}
579238032Speter
579338032Speter		len = 0;
579438032Speter		for (i = 0; i < nmx; i++)
579538032Speter			len += strlen(mxhosts[i]) + 1;
579690792Sgshapiro		if (s->s_hostsig.hs_sig != NULL)
579790792Sgshapiro			len += strlen(s->s_hostsig.hs_sig) + 1;
579890792Sgshapiro		if (len < 0 || len >= MAXHOSTSIGNATURE)
579964562Sgshapiro		{
580064562Sgshapiro			sm_syslog(LOG_WARNING, NOQID, "hostsignature for host '%s' exceeds maxlen (%d): %d",
580164562Sgshapiro				  host, MAXHOSTSIGNATURE, len);
580264562Sgshapiro			len = MAXHOSTSIGNATURE;
580364562Sgshapiro		}
580490792Sgshapiro		p = sm_pmalloc_x(len);
580590792Sgshapiro		if (s->s_hostsig.hs_sig != NULL)
580638032Speter		{
580790792Sgshapiro			(void) sm_strlcpy(p, s->s_hostsig.hs_sig, len);
580890792Sgshapiro			sm_free(s->s_hostsig.hs_sig); /* XXX */
580990792Sgshapiro			s->s_hostsig.hs_sig = p;
581064562Sgshapiro			hl = strlen(p);
581164562Sgshapiro			p += hl;
581264562Sgshapiro			*p++ = prevsep;
581364562Sgshapiro			len -= hl + 1;
581438032Speter		}
581538032Speter		else
581690792Sgshapiro			s->s_hostsig.hs_sig = p;
581738032Speter		for (i = 0; i < nmx; i++)
581838032Speter		{
581964562Sgshapiro			hl = strlen(mxhosts[i]);
582064562Sgshapiro			if (len - 1 < hl || len <= 1)
582164562Sgshapiro			{
582264562Sgshapiro				/* force to drop out of outer loop */
582364562Sgshapiro				len = -1;
582464562Sgshapiro				break;
582564562Sgshapiro			}
582638032Speter			if (i != 0)
582764562Sgshapiro			{
582864562Sgshapiro				if (mxprefs[i] == mxprefs[i - 1])
582964562Sgshapiro					*p++ = ',';
583064562Sgshapiro				else
583164562Sgshapiro					*p++ = ':';
583264562Sgshapiro				len--;
583364562Sgshapiro			}
583490792Sgshapiro			(void) sm_strlcpy(p, mxhosts[i], len);
583564562Sgshapiro			p += hl;
583664562Sgshapiro			len -= hl;
583738032Speter		}
583864562Sgshapiro
583964562Sgshapiro		/*
584064562Sgshapiro		**  break out of loop if len exceeded MAXHOSTSIGNATURE
584164562Sgshapiro		**  because we won't have more space for further hosts
584264562Sgshapiro		**  anyway (separated by : in the .cf file).
584364562Sgshapiro		*/
584464562Sgshapiro
584564562Sgshapiro		if (len < 0)
584664562Sgshapiro			break;
584738032Speter		if (endp != NULL)
584864562Sgshapiro			*endp++ = sep;
584964562Sgshapiro		prevsep = sep;
585038032Speter	}
585190792Sgshapiro	makelower(s->s_hostsig.hs_sig);
585238032Speter	if (ConfigLevel < 2)
585338032Speter		_res.options = oldoptions;
585464562Sgshapiro#else /* NAMED_BIND */
585538032Speter	/* not using BIND -- the signature is just the host name */
585690792Sgshapiro	/*
585790792Sgshapiro	**  'host' points to storage that will be freed after we are
585890792Sgshapiro	**  done processing the current envelope, so we copy it.
585990792Sgshapiro	*/
586090792Sgshapiro	s->s_hostsig.hs_sig = sm_pstrdup_x(host);
586164562Sgshapiro#endif /* NAMED_BIND */
586238032Speter	if (tTd(17, 1))
586390792Sgshapiro		sm_dprintf("hostsignature(%s) = %s\n", host, s->s_hostsig.hs_sig);
586490792Sgshapiro	return s->s_hostsig.hs_sig;
586538032Speter}
586690792Sgshapiro/*
586764562Sgshapiro**  PARSE_HOSTSIGNATURE -- parse the "signature" and return MX host array.
586864562Sgshapiro**
586964562Sgshapiro**	The signature describes how we are going to send this -- it
587064562Sgshapiro**	can be just the hostname (for non-Internet hosts) or can be
587164562Sgshapiro**	an ordered list of MX hosts which must be randomized for equal
587264562Sgshapiro**	MX preference values.
587364562Sgshapiro**
587464562Sgshapiro**	Parameters:
587564562Sgshapiro**		sig -- the host signature.
587664562Sgshapiro**		mxhosts -- array to populate.
587790792Sgshapiro**		mailer -- mailer.
587864562Sgshapiro**
587964562Sgshapiro**	Returns:
588064562Sgshapiro**		The number of hosts inserted into mxhosts array.
588164562Sgshapiro**
588264562Sgshapiro**	Side Effects:
588364562Sgshapiro**		Randomizes equal MX preference hosts in mxhosts.
588464562Sgshapiro*/
588564562Sgshapiro
588664562Sgshapirostatic int
588764562Sgshapiroparse_hostsignature(sig, mxhosts, mailer)
588864562Sgshapiro	char *sig;
588964562Sgshapiro	char **mxhosts;
589064562Sgshapiro	MAILER *mailer;
589164562Sgshapiro{
589290792Sgshapiro	unsigned short curpref = 0;
589390792Sgshapiro	int nmx = 0, i, j;	/* NOTE: i, j, and nmx must have same type */
589464562Sgshapiro	char *hp, *endp;
589590792Sgshapiro	unsigned short prefer[MAXMXHOSTS];
589664562Sgshapiro	long rndm[MAXMXHOSTS];
589764562Sgshapiro
589864562Sgshapiro	for (hp = sig; hp != NULL; hp = endp)
589964562Sgshapiro	{
590064562Sgshapiro		char sep = ':';
590164562Sgshapiro
590264562Sgshapiro#if NETINET6
590364562Sgshapiro		if (*hp == '[')
590464562Sgshapiro		{
590564562Sgshapiro			endp = strchr(hp + 1, ']');
590664562Sgshapiro			if (endp != NULL)
590764562Sgshapiro				endp = strpbrk(endp + 1, ":,");
590864562Sgshapiro		}
590964562Sgshapiro		else
591064562Sgshapiro			endp = strpbrk(hp, ":,");
591164562Sgshapiro#else /* NETINET6 */
591264562Sgshapiro		endp = strpbrk(hp, ":,");
591364562Sgshapiro#endif /* NETINET6 */
591464562Sgshapiro		if (endp != NULL)
591564562Sgshapiro		{
591664562Sgshapiro			sep = *endp;
591764562Sgshapiro			*endp = '\0';
591864562Sgshapiro		}
591964562Sgshapiro
592064562Sgshapiro		mxhosts[nmx] = hp;
592164562Sgshapiro		prefer[nmx] = curpref;
592264562Sgshapiro		if (mci_match(hp, mailer))
592364562Sgshapiro			rndm[nmx] = 0;
592464562Sgshapiro		else
592564562Sgshapiro			rndm[nmx] = get_random();
592664562Sgshapiro
592764562Sgshapiro		if (endp != NULL)
592864562Sgshapiro		{
592964562Sgshapiro			/*
593064562Sgshapiro			**  Since we don't have the original MX prefs,
593164562Sgshapiro			**  make our own.  If the separator is a ':', that
593264562Sgshapiro			**  means the preference for the next host will be
593364562Sgshapiro			**  higher than this one, so simply increment curpref.
593464562Sgshapiro			*/
593564562Sgshapiro
593664562Sgshapiro			if (sep == ':')
593764562Sgshapiro				curpref++;
593864562Sgshapiro
593964562Sgshapiro			*endp++ = sep;
594064562Sgshapiro		}
594164562Sgshapiro		if (++nmx >= MAXMXHOSTS)
594264562Sgshapiro			break;
594364562Sgshapiro	}
594464562Sgshapiro
594564562Sgshapiro	/* sort the records using the random factor for equal preferences */
594664562Sgshapiro	for (i = 0; i < nmx; i++)
594764562Sgshapiro	{
594864562Sgshapiro		for (j = i + 1; j < nmx; j++)
594964562Sgshapiro		{
595064562Sgshapiro			/*
595164562Sgshapiro			**  List is already sorted by MX preference, only
595264562Sgshapiro			**  need to look for equal preference MX records
595364562Sgshapiro			*/
595464562Sgshapiro
595564562Sgshapiro			if (prefer[i] < prefer[j])
595664562Sgshapiro				break;
595764562Sgshapiro
595864562Sgshapiro			if (prefer[i] > prefer[j] ||
595964562Sgshapiro			    (prefer[i] == prefer[j] && rndm[i] > rndm[j]))
596064562Sgshapiro			{
596190792Sgshapiro				register unsigned short tempp;
596264562Sgshapiro				register long tempr;
596364562Sgshapiro				register char *temp1;
596464562Sgshapiro
596564562Sgshapiro				tempp = prefer[i];
596664562Sgshapiro				prefer[i] = prefer[j];
596764562Sgshapiro				prefer[j] = tempp;
596864562Sgshapiro				temp1 = mxhosts[i];
596964562Sgshapiro				mxhosts[i] = mxhosts[j];
597064562Sgshapiro				mxhosts[j] = temp1;
597164562Sgshapiro				tempr = rndm[i];
597264562Sgshapiro				rndm[i] = rndm[j];
597364562Sgshapiro				rndm[j] = tempr;
597464562Sgshapiro			}
597564562Sgshapiro		}
597664562Sgshapiro	}
597764562Sgshapiro	return nmx;
597864562Sgshapiro}
597964562Sgshapiro
598064562Sgshapiro# if STARTTLS
598164562Sgshapirostatic SSL_CTX	*clt_ctx = NULL;
598290792Sgshapirostatic bool	tls_ok_clt = true;
598364562Sgshapiro
598490792Sgshapiro/*
598590792Sgshapiro**  SETCLTTLS -- client side TLS: allow/disallow.
598664562Sgshapiro**
598764562Sgshapiro**	Parameters:
598890792Sgshapiro**		tls_ok -- should tls be done?
598990792Sgshapiro**
599090792Sgshapiro**	Returns:
599164562Sgshapiro**		none.
599264562Sgshapiro**
599390792Sgshapiro**	Side Effects:
599490792Sgshapiro**		sets tls_ok_clt (static variable in this module)
599590792Sgshapiro*/
599690792Sgshapiro
599790792Sgshapirovoid
599890792Sgshapirosetclttls(tls_ok)
599990792Sgshapiro	bool tls_ok;
600090792Sgshapiro{
600190792Sgshapiro	tls_ok_clt = tls_ok;
600290792Sgshapiro	return;
600390792Sgshapiro}
600490792Sgshapiro/*
600590792Sgshapiro**  INITCLTTLS -- initialize client side TLS
600690792Sgshapiro**
600790792Sgshapiro**	Parameters:
600890792Sgshapiro**		tls_ok -- should tls initialization be done?
600990792Sgshapiro**
601064562Sgshapiro**	Returns:
601164562Sgshapiro**		succeeded?
601290792Sgshapiro**
601390792Sgshapiro**	Side Effects:
601490792Sgshapiro**		sets tls_ok_clt (static variable in this module)
601564562Sgshapiro*/
601664562Sgshapiro
601764562Sgshapirobool
601890792Sgshapiroinitclttls(tls_ok)
601990792Sgshapiro	bool tls_ok;
602064562Sgshapiro{
602190792Sgshapiro	if (!tls_ok_clt)
602290792Sgshapiro		return false;
602390792Sgshapiro	tls_ok_clt = tls_ok;
602490792Sgshapiro	if (!tls_ok_clt)
602590792Sgshapiro		return false;
602664562Sgshapiro	if (clt_ctx != NULL)
602790792Sgshapiro		return true;	/* already done */
6028110560Sgshapiro	tls_ok_clt = inittls(&clt_ctx, TLS_I_CLT, false, CltCertFile,
6029110560Sgshapiro			     CltKeyFile, CACertPath, CACertFile, DHParams);
603090792Sgshapiro	return tls_ok_clt;
603164562Sgshapiro}
603264562Sgshapiro
603390792Sgshapiro/*
603464562Sgshapiro**  STARTTLS -- try to start secure connection (client side)
603564562Sgshapiro**
603664562Sgshapiro**	Parameters:
603764562Sgshapiro**		m -- the mailer.
603864562Sgshapiro**		mci -- the mailer connection info.
603964562Sgshapiro**		e -- the envelope.
604064562Sgshapiro**
604164562Sgshapiro**	Returns:
604264562Sgshapiro**		success?
604364562Sgshapiro**		(maybe this should be some other code than EX_
604464562Sgshapiro**		that denotes which stage failed.)
604564562Sgshapiro*/
604664562Sgshapiro
604764562Sgshapirostatic int
604864562Sgshapirostarttls(m, mci, e)
604964562Sgshapiro	MAILER *m;
605064562Sgshapiro	MCI *mci;
605164562Sgshapiro	ENVELOPE *e;
605264562Sgshapiro{
605364562Sgshapiro	int smtpresult;
605466494Sgshapiro	int result = 0;
605566494Sgshapiro	int rfd, wfd;
605664562Sgshapiro	SSL *clt_ssl = NULL;
605790792Sgshapiro	time_t tlsstart;
605864562Sgshapiro
605990792Sgshapiro	if (clt_ctx == NULL && !initclttls(true))
606066494Sgshapiro		return EX_TEMPFAIL;
606164562Sgshapiro	smtpmessage("STARTTLS", m, mci);
606264562Sgshapiro
606364562Sgshapiro	/* get the reply */
6064132943Sgshapiro	smtpresult = reply(m, mci, e, TimeOuts.to_starttls, NULL, NULL,
6065132943Sgshapiro			XS_STARTTLS);
606664562Sgshapiro
606764562Sgshapiro	/* check return code from server */
606864562Sgshapiro	if (smtpresult == 454)
606964562Sgshapiro		return EX_TEMPFAIL;
607064562Sgshapiro	if (smtpresult == 501)
607164562Sgshapiro		return EX_USAGE;
607264562Sgshapiro	if (smtpresult == -1)
607364562Sgshapiro		return smtpresult;
607464562Sgshapiro	if (smtpresult != 220)
607564562Sgshapiro		return EX_PROTOCOL;
607664562Sgshapiro
607764562Sgshapiro	if (LogLevel > 13)
607890792Sgshapiro		sm_syslog(LOG_INFO, NOQID, "STARTTLS=client, start=ok");
607964562Sgshapiro
608064562Sgshapiro	/* start connection */
608164562Sgshapiro	if ((clt_ssl = SSL_new(clt_ctx)) == NULL)
608264562Sgshapiro	{
608364562Sgshapiro		if (LogLevel > 5)
608464562Sgshapiro		{
608590792Sgshapiro			sm_syslog(LOG_ERR, NOQID,
608690792Sgshapiro				  "STARTTLS=client, error: SSL_new failed");
608764562Sgshapiro			if (LogLevel > 9)
608890792Sgshapiro				tlslogerr("client");
608964562Sgshapiro		}
609064562Sgshapiro		return EX_SOFTWARE;
609164562Sgshapiro	}
609264562Sgshapiro
609390792Sgshapiro	rfd = sm_io_getinfo(mci->mci_in, SM_IO_WHAT_FD, NULL);
609490792Sgshapiro	wfd = sm_io_getinfo(mci->mci_out, SM_IO_WHAT_FD, NULL);
609566494Sgshapiro
609664562Sgshapiro	/* SSL_clear(clt_ssl); ? */
609766494Sgshapiro	if (rfd < 0 || wfd < 0 ||
609890792Sgshapiro	    (result = SSL_set_rfd(clt_ssl, rfd)) != 1 ||
609990792Sgshapiro	    (result = SSL_set_wfd(clt_ssl, wfd)) != 1)
610064562Sgshapiro	{
610164562Sgshapiro		if (LogLevel > 5)
610264562Sgshapiro		{
610390792Sgshapiro			sm_syslog(LOG_ERR, NOQID,
610490792Sgshapiro				  "STARTTLS=client, error: SSL_set_xfd failed=%d",
610590792Sgshapiro				  result);
610664562Sgshapiro			if (LogLevel > 9)
610790792Sgshapiro				tlslogerr("client");
610864562Sgshapiro		}
610964562Sgshapiro		return EX_SOFTWARE;
611064562Sgshapiro	}
611164562Sgshapiro	SSL_set_connect_state(clt_ssl);
611290792Sgshapiro	tlsstart = curtime();
611390792Sgshapiro
611490792Sgshapirossl_retry:
611564562Sgshapiro	if ((result = SSL_connect(clt_ssl)) <= 0)
611664562Sgshapiro	{
611764562Sgshapiro		int i;
611890792Sgshapiro		bool timedout;
611990792Sgshapiro		time_t left;
612090792Sgshapiro		time_t now = curtime();
612190792Sgshapiro		struct timeval tv;
612264562Sgshapiro
612364562Sgshapiro		/* what to do in this case? */
612464562Sgshapiro		i = SSL_get_error(clt_ssl, result);
612590792Sgshapiro
612690792Sgshapiro		/*
612790792Sgshapiro		**  For SSL_ERROR_WANT_{READ,WRITE}:
612890792Sgshapiro		**  There is not a complete SSL record available yet
612990792Sgshapiro		**  or there is only a partial SSL record removed from
613090792Sgshapiro		**  the network (socket) buffer into the SSL buffer.
613190792Sgshapiro		**  The SSL_connect will only succeed when a full
613290792Sgshapiro		**  SSL record is available (assuming a "real" error
613390792Sgshapiro		**  doesn't happen). To handle when a "real" error
613490792Sgshapiro		**  does happen the select is set for exceptions too.
613590792Sgshapiro		**  The connection may be re-negotiated during this time
613690792Sgshapiro		**  so both read and write "want errors" need to be handled.
613790792Sgshapiro		**  A select() exception loops back so that a proper SSL
613890792Sgshapiro		**  error message can be gotten.
613990792Sgshapiro		*/
614090792Sgshapiro
614190792Sgshapiro		left = TimeOuts.to_starttls - (now - tlsstart);
614290792Sgshapiro		timedout = left <= 0;
614390792Sgshapiro		if (!timedout)
614490792Sgshapiro		{
614590792Sgshapiro			tv.tv_sec = left;
614690792Sgshapiro			tv.tv_usec = 0;
614790792Sgshapiro		}
614890792Sgshapiro
6149110560Sgshapiro		if (!timedout && FD_SETSIZE > 0 &&
6150110560Sgshapiro		    (rfd >= FD_SETSIZE ||
6151110560Sgshapiro		     (i == SSL_ERROR_WANT_WRITE && wfd >= FD_SETSIZE)))
6152110560Sgshapiro		{
6153110560Sgshapiro			if (LogLevel > 5)
6154110560Sgshapiro			{
6155110560Sgshapiro				sm_syslog(LOG_ERR, e->e_id,
6156110560Sgshapiro					  "STARTTLS=client, error: fd %d/%d too large",
6157110560Sgshapiro					  rfd, wfd);
6158110560Sgshapiro			if (LogLevel > 8)
6159110560Sgshapiro				tlslogerr("client");
6160110560Sgshapiro			}
6161110560Sgshapiro			errno = EINVAL;
6162110560Sgshapiro			goto tlsfail;
6163110560Sgshapiro		}
616490792Sgshapiro		if (!timedout && i == SSL_ERROR_WANT_READ)
616590792Sgshapiro		{
616690792Sgshapiro			fd_set ssl_maskr, ssl_maskx;
616790792Sgshapiro
616890792Sgshapiro			FD_ZERO(&ssl_maskr);
616990792Sgshapiro			FD_SET(rfd, &ssl_maskr);
617090792Sgshapiro			FD_ZERO(&ssl_maskx);
617190792Sgshapiro			FD_SET(rfd, &ssl_maskx);
617290792Sgshapiro			if (select(rfd + 1, &ssl_maskr, NULL, &ssl_maskx, &tv)
617390792Sgshapiro			    > 0)
617490792Sgshapiro				goto ssl_retry;
617590792Sgshapiro		}
617690792Sgshapiro		if (!timedout && i == SSL_ERROR_WANT_WRITE)
617790792Sgshapiro		{
617890792Sgshapiro			fd_set ssl_maskw, ssl_maskx;
617990792Sgshapiro
618090792Sgshapiro			FD_ZERO(&ssl_maskw);
618190792Sgshapiro			FD_SET(wfd, &ssl_maskw);
618290792Sgshapiro			FD_ZERO(&ssl_maskx);
618390792Sgshapiro			FD_SET(rfd, &ssl_maskx);
618490792Sgshapiro			if (select(wfd + 1, NULL, &ssl_maskw, &ssl_maskx, &tv)
618590792Sgshapiro			    > 0)
618690792Sgshapiro				goto ssl_retry;
618790792Sgshapiro		}
618864562Sgshapiro		if (LogLevel > 5)
618964562Sgshapiro		{
619064562Sgshapiro			sm_syslog(LOG_ERR, e->e_id,
6191111823Sgshapiro				  "STARTTLS=client, error: connect failed=%d, SSL_error=%d, timedout=%d, errno=%d",
6192111823Sgshapiro				  result, i, (int) timedout, errno);
619390792Sgshapiro			if (LogLevel > 8)
619490792Sgshapiro				tlslogerr("client");
619564562Sgshapiro		}
6196110560Sgshapirotlsfail:
619764562Sgshapiro		SSL_free(clt_ssl);
619864562Sgshapiro		clt_ssl = NULL;
619964562Sgshapiro		return EX_SOFTWARE;
620064562Sgshapiro	}
620164562Sgshapiro	mci->mci_ssl = clt_ssl;
620290792Sgshapiro	result = tls_get_info(mci->mci_ssl, false, mci->mci_host,
620390792Sgshapiro			      &mci->mci_macro, true);
620464562Sgshapiro
620590792Sgshapiro	/* switch to use TLS... */
620664562Sgshapiro	if (sfdctls(&mci->mci_in, &mci->mci_out, mci->mci_ssl) == 0)
620764562Sgshapiro		return EX_OK;
620864562Sgshapiro
620964562Sgshapiro	/* failure */
621064562Sgshapiro	SSL_free(clt_ssl);
621164562Sgshapiro	clt_ssl = NULL;
621264562Sgshapiro	return EX_SOFTWARE;
621364562Sgshapiro}
621490792Sgshapiro/*
621564562Sgshapiro**  ENDTLSCLT -- shutdown secure connection (client side)
621664562Sgshapiro**
621764562Sgshapiro**	Parameters:
621864562Sgshapiro**		mci -- the mailer connection info.
621964562Sgshapiro**
622064562Sgshapiro**	Returns:
622164562Sgshapiro**		success?
622264562Sgshapiro*/
622390792Sgshapiro
622490792Sgshapirostatic int
622564562Sgshapiroendtlsclt(mci)
622664562Sgshapiro	MCI *mci;
622764562Sgshapiro{
622864562Sgshapiro	int r;
622964562Sgshapiro
623064562Sgshapiro	if (!bitset(MCIF_TLSACT, mci->mci_flags))
623164562Sgshapiro		return EX_OK;
623264562Sgshapiro	r = endtls(mci->mci_ssl, "client");
623364562Sgshapiro	mci->mci_flags &= ~MCIF_TLSACT;
623464562Sgshapiro	return r;
623564562Sgshapiro}
623690792Sgshapiro# endif /* STARTTLS */
623790792Sgshapiro# if STARTTLS || SASL
623890792Sgshapiro/*
623990792Sgshapiro**  ISCLTFLGSET -- check whether client flag is set.
624064562Sgshapiro**
624164562Sgshapiro**	Parameters:
624290792Sgshapiro**		e -- envelope.
624390792Sgshapiro**		flag -- flag to check in {client_flags}
624464562Sgshapiro**
624564562Sgshapiro**	Returns:
624690792Sgshapiro**		true iff flag is set.
624764562Sgshapiro*/
624864562Sgshapiro
624990792Sgshapirostatic bool
625090792Sgshapiroiscltflgset(e, flag)
625190792Sgshapiro	ENVELOPE *e;
625290792Sgshapiro	int flag;
625364562Sgshapiro{
625490792Sgshapiro	char *p;
625573188Sgshapiro
625690792Sgshapiro	p = macvalue(macid("{client_flags}"), e);
625790792Sgshapiro	if (p == NULL)
625890792Sgshapiro		return false;
625990792Sgshapiro	for (; *p != '\0'; p++)
626064562Sgshapiro	{
626190792Sgshapiro		/* look for just this one flag */
626290792Sgshapiro		if (*p == (char) flag)
626390792Sgshapiro			return true;
626464562Sgshapiro	}
626590792Sgshapiro	return false;
626664562Sgshapiro}
626790792Sgshapiro# endif /* STARTTLS || SASL */
6268