138032Speter/*
2261363Sgshapiro * Copyright (c) 1998-2010, 2012 Proofpoint, Inc. and its suppliers.
364562Sgshapiro *	All rights reserved.
438032Speter * Copyright (c) 1983, 1995-1997 Eric P. Allman.  All rights reserved.
538032Speter * Copyright (c) 1988, 1993
638032Speter *	The Regents of the University of California.  All rights reserved.
738032Speter *
838032Speter * By using this file, you agree to the terms and conditions set
938032Speter * forth in the LICENSE file which can be found at the top level of
1038032Speter * the sendmail distribution.
1138032Speter *
1238032Speter */
1338032Speter
1464562Sgshapiro#include <sendmail.h>
15157001Sgshapiro#include <sm/time.h>
1638032Speter
17266692SgshapiroSM_RCSID("@(#)$Id: deliver.c,v 8.1030 2013-11-22 20:51:55 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));
3790792Sgshapirostatic int	coloncmp __P((const char *, const char *));
3864562Sgshapiro
3990792Sgshapiro#if STARTTLS
40285303Sgshapiro# include <openssl/err.h>
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,
386168515Sgshapiro							    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))
499173340Sgshapiro			sm_dprintf("No deliveries: auto-queueing\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;
579203004Sgshapiro		(void) 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;
584203004Sgshapiro			(void) 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;
606203004Sgshapiro			(void) 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;
619203004Sgshapiro			(void) 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);
766203004Sgshapiro	(void) 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);
773203004Sgshapiro		(void) 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
887168515Sgshapiro		(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	{
962168515Sgshapiro		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
1015168515Sgshapiro	(void) sm_strlcpy(f1buf, queuename(e, type), sizeof(f1buf));
1016168515Sgshapiro	(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
1195168515Sgshapirostatic bool should_try_fbsh __P((ENVELOPE *, bool *, char *, size_t, int));
1196168515Sgshapiro
1197132943Sgshapirostatic bool
1198132943Sgshapiroshould_try_fbsh(e, tried_fallbacksmarthost, hostbuf, hbsz, status)
1199132943Sgshapiro	ENVELOPE *e;
1200132943Sgshapiro	bool *tried_fallbacksmarthost;
1201132943Sgshapiro	char *hostbuf;
1202132943Sgshapiro	size_t hbsz;
1203132943Sgshapiro	int status;
1204132943Sgshapiro{
1205132943Sgshapiro	/*
1206157001Sgshapiro	**  If the host was not found or a temporary failure occurred
1207157001Sgshapiro	**  and a FallbackSmartHost is defined (and we have not yet
1208157001Sgshapiro	**  tried it), then make one last try with it as the host.
1209132943Sgshapiro	*/
1210132943Sgshapiro
1211157001Sgshapiro	if ((status == EX_NOHOST || status == EX_TEMPFAIL) &&
1212157001Sgshapiro	    FallbackSmartHost != NULL && !*tried_fallbacksmarthost)
1213132943Sgshapiro	{
1214132943Sgshapiro		*tried_fallbacksmarthost = true;
1215132943Sgshapiro		expand(FallbackSmartHost, hostbuf, hbsz, e);
1216132943Sgshapiro		if (!wordinclass(hostbuf, 'w'))
1217132943Sgshapiro		{
1218132943Sgshapiro			if (tTd(11, 1))
1219132943Sgshapiro				sm_dprintf("one last try with FallbackSmartHost %s\n",
1220132943Sgshapiro					   hostbuf);
1221132943Sgshapiro			return true;
1222132943Sgshapiro		}
1223132943Sgshapiro	}
1224132943Sgshapiro	return false;
1225132943Sgshapiro}
1226285303Sgshapiro
1227132943Sgshapiro/*
122838032Speter**  DELIVER -- Deliver a message to a list of addresses.
122938032Speter**
123038032Speter**	This routine delivers to everyone on the same host as the
123138032Speter**	user on the head of the list.  It is clever about mailers
123238032Speter**	that don't handle multiple users.  It is NOT guaranteed
123338032Speter**	that it will deliver to all these addresses however -- so
123438032Speter**	deliver should be called once for each address on the
123538032Speter**	list.
123690792Sgshapiro**	Deliver tries to be as opportunistic as possible about piggybacking
123790792Sgshapiro**	messages. Some definitions to make understanding easier follow below.
123890792Sgshapiro**	Piggybacking occurs when an existing connection to a mail host can
123990792Sgshapiro**	be used to send the same message to more than one recipient at the
124090792Sgshapiro**	same time. So "no piggybacking" means one message for one recipient
124190792Sgshapiro**	per connection. "Intentional piggybacking" happens when the
124290792Sgshapiro**	recipients' host address (not the mail host address) is used to
124390792Sgshapiro**	attempt piggybacking. Recipients with the same host address
124490792Sgshapiro**	have the same mail host. "Coincidental piggybacking" relies on
124590792Sgshapiro**	piggybacking based on all the mail host addresses in the MX-RR. This
124690792Sgshapiro**	is "coincidental" in the fact it could not be predicted until the
124790792Sgshapiro**	MX Resource Records for the hosts were obtained and examined. For
124890792Sgshapiro**	example (preference order and equivalence is important, not values):
124990792Sgshapiro**		domain1 IN MX 10 mxhost-A
125090792Sgshapiro**			IN MX 20 mxhost-B
125190792Sgshapiro**		domain2 IN MX  4 mxhost-A
125290792Sgshapiro**			IN MX  8 mxhost-B
125390792Sgshapiro**	Domain1 and domain2 can piggyback the same message to mxhost-A or
125490792Sgshapiro**	mxhost-B (if mxhost-A cannot be reached).
125590792Sgshapiro**	"Coattail piggybacking" relaxes the strictness of "coincidental
125690792Sgshapiro**	piggybacking" in the hope that most significant (lowest value)
125790792Sgshapiro**	MX preference host(s) can create more piggybacking. For example
125890792Sgshapiro**	(again, preference order and equivalence is important, not values):
125990792Sgshapiro**		domain3 IN MX 100 mxhost-C
126090792Sgshapiro**			IN MX 100 mxhost-D
126190792Sgshapiro**			IN MX 200 mxhost-E
126290792Sgshapiro**		domain4 IN MX  50 mxhost-C
126390792Sgshapiro**			IN MX  50 mxhost-D
126490792Sgshapiro**			IN MX  80 mxhost-F
126590792Sgshapiro**	A message for domain3 and domain4 can piggyback to mxhost-C if mxhost-C
126690792Sgshapiro**	is available. Same with mxhost-D because in both RR's the preference
126790792Sgshapiro**	value is the same as mxhost-C, respectively.
126890792Sgshapiro**	So deliver attempts coattail piggybacking when possible. If the
126990792Sgshapiro**	first MX preference level hosts cannot be used then the piggybacking
127090792Sgshapiro**	reverts to coincidental piggybacking. Using the above example you
127190792Sgshapiro**	cannot deliver to mxhost-F for domain3 regardless of preference value.
127290792Sgshapiro**	("Coattail" from "riding on the coattails of your predecessor" meaning
127390792Sgshapiro**	gaining benefit from a predecessor effort with no or little addition
127490792Sgshapiro**	effort. The predecessor here being the preceding MX RR).
127538032Speter**
127638032Speter**	Parameters:
127738032Speter**		e -- the envelope to deliver.
127838032Speter**		firstto -- head of the address list to deliver to.
127938032Speter**
128038032Speter**	Returns:
128138032Speter**		zero -- successfully delivered.
128238032Speter**		else -- some failure, see ExitStat for more info.
128338032Speter**
128438032Speter**	Side Effects:
128538032Speter**		The standard input is passed off to someone.
128638032Speter*/
128738032Speter
128864562Sgshapirostatic int
128938032Speterdeliver(e, firstto)
129038032Speter	register ENVELOPE *e;
129138032Speter	ADDRESS *firstto;
129238032Speter{
129338032Speter	char *host;			/* host being sent to */
129438032Speter	char *user;			/* user being sent to */
129538032Speter	char **pvp;
129638032Speter	register char **mvp;
129738032Speter	register char *p;
129838032Speter	register MAILER *m;		/* mailer for this recipient */
129938032Speter	ADDRESS *volatile ctladdr;
130090792Sgshapiro#if HASSETUSERCONTEXT
130138032Speter	ADDRESS *volatile contextaddr = NULL;
130290792Sgshapiro#endif /* HASSETUSERCONTEXT */
130338032Speter	register MCI *volatile mci;
130490792Sgshapiro	register ADDRESS *SM_NONVOLATILE to = firstto;
130590792Sgshapiro	volatile bool clever = false;	/* running user smtp to this mailer */
130638032Speter	ADDRESS *volatile tochain = NULL; /* users chain in this mailer call */
130738032Speter	int rcode;			/* response code */
130890792Sgshapiro	SM_NONVOLATILE int lmtp_rcode = EX_OK;
130990792Sgshapiro	SM_NONVOLATILE int nummxhosts = 0; /* number of MX hosts available */
131090792Sgshapiro	SM_NONVOLATILE int hostnum = 0;	/* current MX host index */
131138032Speter	char *firstsig;			/* signature of firstto */
131290792Sgshapiro	volatile pid_t pid = -1;
131338032Speter	char *volatile curhost;
131490792Sgshapiro	SM_NONVOLATILE unsigned short port = 0;
131590792Sgshapiro	SM_NONVOLATILE time_t enough = 0;
131664562Sgshapiro#if NETUNIX
131790792Sgshapiro	char *SM_NONVOLATILE mux_path = NULL;	/* path to UNIX domain socket */
131864562Sgshapiro#endif /* NETUNIX */
131938032Speter	time_t xstart;
132038032Speter	bool suidwarn;
132138032Speter	bool anyok;			/* at least one address was OK */
132290792Sgshapiro	SM_NONVOLATILE bool goodmxfound = false; /* at least one MX was OK */
132364562Sgshapiro	bool ovr;
132490792Sgshapiro	bool quarantine;
132564562Sgshapiro	int strsize;
132664562Sgshapiro	int rcptcount;
132790792Sgshapiro	int ret;
132864562Sgshapiro	static int tobufsize = 0;
132964562Sgshapiro	static char *tobuf = NULL;
133090792Sgshapiro	char *rpath;	/* translated return path */
133138032Speter	int mpvect[2];
133238032Speter	int rpvect[2];
133364562Sgshapiro	char *mxhosts[MAXMXHOSTS + 1];
133464562Sgshapiro	char *pv[MAXPV + 1];
133538032Speter	char buf[MAXNAME + 1];
133698121Sgshapiro	char cbuf[MAXPATHLEN];
133738032Speter
133838032Speter	errno = 0;
1339168515Sgshapiro	SM_REQUIRE(firstto != NULL);	/* same as to */
134064562Sgshapiro	if (!QS_IS_OK(to->q_state))
134164562Sgshapiro		return 0;
134238032Speter
134338032Speter	suidwarn = geteuid() == 0;
134438032Speter
1345168515Sgshapiro	SM_REQUIRE(e != NULL);
134638032Speter	m = to->q_mailer;
134738032Speter	host = to->q_host;
134838032Speter	CurEnv = e;			/* just in case */
134938032Speter	e->e_statmsg = NULL;
135038032Speter	SmtpError[0] = '\0';
135138032Speter	xstart = curtime();
135238032Speter
135338032Speter	if (tTd(10, 1))
135490792Sgshapiro		sm_dprintf("\n--deliver, id=%s, mailer=%s, host=`%s', first user=`%s'\n",
135538032Speter			e->e_id, m->m_name, host, to->q_user);
135638032Speter	if (tTd(10, 100))
135790792Sgshapiro		printopenfds(false);
135838032Speter
135938032Speter	/*
136090792Sgshapiro	**  Clear {client_*} macros if this is a bounce message to
136138032Speter	**  prevent rejection by check_compat ruleset.
136238032Speter	*/
136364562Sgshapiro
136438032Speter	if (bitset(EF_RESPONSE, e->e_flags))
136538032Speter	{
136690792Sgshapiro		macdefine(&e->e_macro, A_PERM, macid("{client_name}"), "");
1367132943Sgshapiro		macdefine(&e->e_macro, A_PERM, macid("{client_ptr}"), "");
136890792Sgshapiro		macdefine(&e->e_macro, A_PERM, macid("{client_addr}"), "");
136990792Sgshapiro		macdefine(&e->e_macro, A_PERM, macid("{client_port}"), "");
137090792Sgshapiro		macdefine(&e->e_macro, A_PERM, macid("{client_resolve}"), "");
137138032Speter	}
137264562Sgshapiro
137390792Sgshapiro	SM_TRY
137490792Sgshapiro	{
137590792Sgshapiro	ADDRESS *skip_back = NULL;
137690792Sgshapiro
137738032Speter	/*
137838032Speter	**  Do initial argv setup.
137938032Speter	**	Insert the mailer name.  Notice that $x expansion is
138038032Speter	**	NOT done on the mailer name.  Then, if the mailer has
138138032Speter	**	a picky -f flag, we insert it as appropriate.  This
138238032Speter	**	code does not check for 'pv' overflow; this places a
138338032Speter	**	manifest lower limit of 4 for MAXPV.
138438032Speter	**		The from address rewrite is expected to make
138538032Speter	**		the address relative to the other end.
138638032Speter	*/
138738032Speter
138838032Speter	/* rewrite from address, using rewriting rules */
138938032Speter	rcode = EX_OK;
1390168515Sgshapiro	SM_ASSERT(e->e_from.q_mailer != NULL);
139138032Speter	if (bitnset(M_UDBENVELOPE, e->e_from.q_mailer->m_flags))
139238032Speter		p = e->e_sender;
139338032Speter	else
139438032Speter		p = e->e_from.q_paddr;
139590792Sgshapiro	rpath = remotename(p, m, RF_SENDERADDR|RF_CANONICAL, &rcode, e);
1396285303Sgshapiro	if (rcode != EX_OK && bitnset(M_xSMTP, m->m_flags))
1397285303Sgshapiro		goto cleanup;
1398203004Sgshapiro	if (strlen(rpath) > MAXNAME)
139938032Speter	{
140090792Sgshapiro		rpath = shortenstring(rpath, MAXSHORTSTR);
140190792Sgshapiro
140290792Sgshapiro		/* avoid bogus errno */
140390792Sgshapiro		errno = 0;
140490792Sgshapiro		syserr("remotename: huge return path %s", rpath);
140538032Speter	}
140690792Sgshapiro	rpath = sm_rpool_strdup_x(e->e_rpool, rpath);
140790792Sgshapiro	macdefine(&e->e_macro, A_PERM, 'g', rpath);
140890792Sgshapiro	macdefine(&e->e_macro, A_PERM, 'h', host);
140938032Speter	Errors = 0;
141038032Speter	pvp = pv;
141138032Speter	*pvp++ = m->m_argv[0];
141238032Speter
1413132943Sgshapiro	/* ignore long term host status information if mailer flag W is set */
1414132943Sgshapiro	if (bitnset(M_NOHOSTSTAT, m->m_flags))
1415132943Sgshapiro		IgnoreHostStatus = true;
1416132943Sgshapiro
141738032Speter	/* insert -f or -r flag as appropriate */
141864562Sgshapiro	if (FromFlag &&
141964562Sgshapiro	    (bitnset(M_FOPT, m->m_flags) ||
142064562Sgshapiro	     bitnset(M_ROPT, m->m_flags)))
142138032Speter	{
142238032Speter		if (bitnset(M_FOPT, m->m_flags))
142338032Speter			*pvp++ = "-f";
142438032Speter		else
142538032Speter			*pvp++ = "-r";
142690792Sgshapiro		*pvp++ = rpath;
142738032Speter	}
142838032Speter
142938032Speter	/*
143038032Speter	**  Append the other fixed parts of the argv.  These run
143138032Speter	**  up to the first entry containing "$u".  There can only
143238032Speter	**  be one of these, and there are only a few more slots
143338032Speter	**  in the pv after it.
143438032Speter	*/
143538032Speter
143638032Speter	for (mvp = m->m_argv; (p = *++mvp) != NULL; )
143738032Speter	{
143838032Speter		/* can't use strchr here because of sign extension problems */
143938032Speter		while (*p != '\0')
144038032Speter		{
144138032Speter			if ((*p++ & 0377) == MACROEXPAND)
144238032Speter			{
144338032Speter				if (*p == 'u')
144438032Speter					break;
144538032Speter			}
144638032Speter		}
144738032Speter
144838032Speter		if (*p != '\0')
144938032Speter			break;
145038032Speter
145138032Speter		/* this entry is safe -- go ahead and process it */
1452168515Sgshapiro		expand(*mvp, buf, sizeof(buf), e);
145390792Sgshapiro		*pvp++ = sm_rpool_strdup_x(e->e_rpool, buf);
145438032Speter		if (pvp >= &pv[MAXPV - 3])
145538032Speter		{
145664562Sgshapiro			syserr("554 5.3.5 Too many parameters to %s before $u",
145764562Sgshapiro			       pv[0]);
145890792Sgshapiro			rcode = -1;
145990792Sgshapiro			goto cleanup;
146038032Speter		}
146138032Speter	}
146238032Speter
146338032Speter	/*
146438032Speter	**  If we have no substitution for the user name in the argument
146538032Speter	**  list, we know that we must supply the names otherwise -- and
146638032Speter	**  SMTP is the answer!!
146738032Speter	*/
146838032Speter
146938032Speter	if (*mvp == NULL)
147038032Speter	{
147173188Sgshapiro		/* running LMTP or SMTP */
147290792Sgshapiro		clever = true;
147338032Speter		*pvp = NULL;
1474285303Sgshapiro		setbitn(M_xSMTP, m->m_flags);
147538032Speter	}
147673188Sgshapiro	else if (bitnset(M_LMTP, m->m_flags))
147773188Sgshapiro	{
147873188Sgshapiro		/* not running LMTP */
147973188Sgshapiro		sm_syslog(LOG_ERR, NULL,
148073188Sgshapiro			  "Warning: mailer %s: LMTP flag (F=z) turned off",
148173188Sgshapiro			  m->m_name);
148273188Sgshapiro		clrbitn(M_LMTP, m->m_flags);
148373188Sgshapiro	}
148438032Speter
148538032Speter	/*
148638032Speter	**  At this point *mvp points to the argument with $u.  We
148738032Speter	**  run through our address list and append all the addresses
148838032Speter	**  we can.  If we run out of space, do not fret!  We can
148938032Speter	**  always send another copy later.
149038032Speter	*/
149138032Speter
149264562Sgshapiro	e->e_to = NULL;
149364562Sgshapiro	strsize = 2;
149464562Sgshapiro	rcptcount = 0;
149590792Sgshapiro	ctladdr = NULL;
149690792Sgshapiro	if (firstto->q_signature == NULL)
149790792Sgshapiro		firstto->q_signature = hostsignature(firstto->q_mailer,
149890792Sgshapiro						     firstto->q_host);
149990792Sgshapiro	firstsig = firstto->q_signature;
150064562Sgshapiro
150138032Speter	for (; to != NULL; to = to->q_next)
150238032Speter	{
150338032Speter		/* avoid sending multiple recipients to dumb mailers */
150464562Sgshapiro		if (tochain != NULL && !bitnset(M_MUSER, m->m_flags))
150564562Sgshapiro			break;
150638032Speter
150738032Speter		/* if already sent or not for this host, don't send */
150890792Sgshapiro		if (!QS_IS_OK(to->q_state)) /* already sent; look at next */
150938032Speter			continue;
151038032Speter
151190792Sgshapiro		/*
151290792Sgshapiro		**  Must be same mailer to keep grouping rcpts.
151390792Sgshapiro		**  If mailers don't match: continue; sendqueue is not
151490792Sgshapiro		**  sorted by mailers, so don't break;
151590792Sgshapiro		*/
151690792Sgshapiro
151790792Sgshapiro		if (to->q_mailer != firstto->q_mailer)
151890792Sgshapiro			continue;
151990792Sgshapiro
152090792Sgshapiro		if (to->q_signature == NULL) /* for safety */
152190792Sgshapiro			to->q_signature = hostsignature(to->q_mailer,
152290792Sgshapiro							to->q_host);
152390792Sgshapiro
152490792Sgshapiro		/*
152590792Sgshapiro		**  This is for coincidental and tailcoat piggybacking messages
152690792Sgshapiro		**  to the same mail host. While the signatures are identical
152790792Sgshapiro		**  (that's the MX-RR's are identical) we can do coincidental
152890792Sgshapiro		**  piggybacking. We try hard for coattail piggybacking
152990792Sgshapiro		**  with the same mail host when the next recipient has the
153090792Sgshapiro		**  same host at lowest preference. It may be that this
153190792Sgshapiro		**  won't work out, so 'skip_back' is maintained if a backup
153290792Sgshapiro		**  to coincidental piggybacking or full signature must happen.
153390792Sgshapiro		*/
153490792Sgshapiro
153590792Sgshapiro		ret = firstto == to ? HS_MATCH_FULL :
153690792Sgshapiro				      coloncmp(to->q_signature, firstsig);
153790792Sgshapiro		if (ret == HS_MATCH_FULL)
153890792Sgshapiro			skip_back = to;
153990792Sgshapiro		else if (ret == HS_MATCH_NO)
154064562Sgshapiro			break;
154164562Sgshapiro
154290792Sgshapiro		if (!clever)
154390792Sgshapiro		{
154490792Sgshapiro			/* avoid overflowing tobuf */
154590792Sgshapiro			strsize += strlen(to->q_paddr) + 1;
154690792Sgshapiro			if (strsize > TOBUFSIZE)
154790792Sgshapiro				break;
154890792Sgshapiro		}
154990792Sgshapiro
155064562Sgshapiro		if (++rcptcount > to->q_mailer->m_maxrcpt)
155164562Sgshapiro			break;
155238032Speter
155338032Speter		if (tTd(10, 1))
155438032Speter		{
155590792Sgshapiro			sm_dprintf("\nsend to ");
1556132943Sgshapiro			printaddr(sm_debug_file(), to, false);
155738032Speter		}
155838032Speter
155938032Speter		/* compute effective uid/gid when sending */
156038032Speter		if (bitnset(M_RUNASRCPT, to->q_mailer->m_flags))
156190792Sgshapiro# if HASSETUSERCONTEXT
156238032Speter			contextaddr = ctladdr = getctladdr(to);
156390792Sgshapiro# else /* HASSETUSERCONTEXT */
156490792Sgshapiro			ctladdr = getctladdr(to);
156590792Sgshapiro# endif /* HASSETUSERCONTEXT */
156638032Speter
156738032Speter		if (tTd(10, 2))
156838032Speter		{
156990792Sgshapiro			sm_dprintf("ctladdr=");
1570132943Sgshapiro			printaddr(sm_debug_file(), ctladdr, false);
157138032Speter		}
157238032Speter
157338032Speter		user = to->q_user;
157438032Speter		e->e_to = to->q_paddr;
157538032Speter
157638032Speter		/*
157738032Speter		**  Check to see that these people are allowed to
157838032Speter		**  talk to each other.
157966494Sgshapiro		**  Check also for overflow of e_msgsize.
158038032Speter		*/
158138032Speter
158266494Sgshapiro		if (m->m_maxsize != 0 &&
158366494Sgshapiro		    (e->e_msgsize > m->m_maxsize || e->e_msgsize < 0))
158438032Speter		{
158538032Speter			e->e_flags |= EF_NO_BODY_RETN;
158638032Speter			if (bitnset(M_LOCALMAILER, to->q_mailer->m_flags))
158738032Speter				to->q_status = "5.2.3";
158838032Speter			else
158938032Speter				to->q_status = "5.3.4";
159090792Sgshapiro
159164562Sgshapiro			/* set to->q_rstatus = NULL; or to the following? */
159264562Sgshapiro			usrerrenh(to->q_status,
159364562Sgshapiro				  "552 Message is too large; %ld bytes max",
159464562Sgshapiro				  m->m_maxsize);
159590792Sgshapiro			markfailure(e, to, NULL, EX_UNAVAILABLE, false);
159664562Sgshapiro			giveresponse(EX_UNAVAILABLE, to->q_status, m,
159790792Sgshapiro				     NULL, ctladdr, xstart, e, to);
159838032Speter			continue;
159938032Speter		}
160073188Sgshapiro		SM_SET_H_ERRNO(0);
160190792Sgshapiro		ovr = true;
160238032Speter
160338032Speter		/* do config file checking of compatibility */
160490792Sgshapiro		quarantine = (e->e_quarmsg != NULL);
160564562Sgshapiro		rcode = rscheck("check_compat", e->e_from.q_paddr, to->q_paddr,
1606102528Sgshapiro				e, RSF_RMCOMM|RSF_COUNT, 3, NULL,
1607285303Sgshapiro				e->e_id, NULL, NULL);
160838032Speter		if (rcode == EX_OK)
160938032Speter		{
161042575Speter			/* do in-code checking if not discarding */
161142575Speter			if (!bitset(EF_DISCARD, e->e_flags))
161264562Sgshapiro			{
161342575Speter				rcode = checkcompat(to, e);
161490792Sgshapiro				ovr = false;
161564562Sgshapiro			}
161638032Speter		}
161738032Speter		if (rcode != EX_OK)
161838032Speter		{
161964562Sgshapiro			markfailure(e, to, NULL, rcode, ovr);
162064562Sgshapiro			giveresponse(rcode, to->q_status, m,
162190792Sgshapiro				     NULL, ctladdr, xstart, e, to);
162238032Speter			continue;
162338032Speter		}
162490792Sgshapiro		if (!quarantine && e->e_quarmsg != NULL)
162590792Sgshapiro		{
162690792Sgshapiro			/*
162790792Sgshapiro			**  check_compat or checkcompat() has tried
162890792Sgshapiro			**  to quarantine but that isn't supported.
162990792Sgshapiro			**  Revert the attempt.
163090792Sgshapiro			*/
163190792Sgshapiro
163290792Sgshapiro			e->e_quarmsg = NULL;
163390792Sgshapiro			macdefine(&e->e_macro, A_PERM,
163490792Sgshapiro				  macid("{quarantine}"), "");
163590792Sgshapiro		}
163642575Speter		if (bitset(EF_DISCARD, e->e_flags))
163742575Speter		{
163842575Speter			if (tTd(10, 5))
163942575Speter			{
164090792Sgshapiro				sm_dprintf("deliver: discarding recipient ");
1641132943Sgshapiro				printaddr(sm_debug_file(), to, false);
164242575Speter			}
164338032Speter
164464562Sgshapiro			/* pretend the message was sent */
164564562Sgshapiro			/* XXX should we log something here? */
164664562Sgshapiro			to->q_state = QS_DISCARDED;
164764562Sgshapiro
164842575Speter			/*
164942575Speter			**  Remove discard bit to prevent discard of
165064562Sgshapiro			**  future recipients.  This is safe because the
165164562Sgshapiro			**  true "global discard" has been handled before
165264562Sgshapiro			**  we get here.
165342575Speter			*/
165464562Sgshapiro
165542575Speter			e->e_flags &= ~EF_DISCARD;
165642575Speter			continue;
165742575Speter		}
165842575Speter
165938032Speter		/*
166038032Speter		**  Strip quote bits from names if the mailer is dumb
166138032Speter		**	about them.
166238032Speter		*/
166338032Speter
166438032Speter		if (bitnset(M_STRIPQ, m->m_flags))
166538032Speter		{
166638032Speter			stripquotes(user);
166738032Speter			stripquotes(host);
166838032Speter		}
1669132943Sgshapiro
1670110560Sgshapiro		/*
1671141858Sgshapiro		**  Strip all leading backslashes if requested and the
1672110560Sgshapiro		**  next character is alphanumerical (the latter can
1673110560Sgshapiro		**  probably relaxed a bit, see RFC2821).
1674110560Sgshapiro		*/
167538032Speter
1676110560Sgshapiro		if (bitnset(M_STRIPBACKSL, m->m_flags) && user[0] == '\\')
1677110560Sgshapiro			stripbackslash(user);
1678110560Sgshapiro
167938032Speter		/* hack attack -- delivermail compatibility */
168038032Speter		if (m == ProgMailer && *user == '|')
168138032Speter			user++;
168238032Speter
168338032Speter		/*
168438032Speter		**  If an error message has already been given, don't
168538032Speter		**	bother to send to this address.
168638032Speter		**
168738032Speter		**	>>>>>>>>>> This clause assumes that the local mailer
168838032Speter		**	>> NOTE >> cannot do any further aliasing; that
168938032Speter		**	>>>>>>>>>> function is subsumed by sendmail.
169038032Speter		*/
169138032Speter
169264562Sgshapiro		if (!QS_IS_OK(to->q_state))
169338032Speter			continue;
169438032Speter
169538032Speter		/*
169638032Speter		**  See if this user name is "special".
169738032Speter		**	If the user name has a slash in it, assume that this
169838032Speter		**	is a file -- send it off without further ado.  Note
169938032Speter		**	that this type of addresses is not processed along
170038032Speter		**	with the others, so we fudge on the To person.
170138032Speter		*/
170238032Speter
170338032Speter		if (strcmp(m->m_mailer, "[FILE]") == 0)
170438032Speter		{
170590792Sgshapiro			macdefine(&e->e_macro, A_PERM, 'u', user);
170638032Speter			p = to->q_home;
170738032Speter			if (p == NULL && ctladdr != NULL)
170838032Speter				p = ctladdr->q_home;
170990792Sgshapiro			macdefine(&e->e_macro, A_PERM, 'z', p);
1710168515Sgshapiro			expand(m->m_argv[1], buf, sizeof(buf), e);
171138032Speter			if (strlen(buf) > 0)
171238032Speter				rcode = mailfile(buf, m, ctladdr, SFF_CREAT, e);
171338032Speter			else
171438032Speter			{
171538032Speter				syserr("empty filename specification for mailer %s",
171638032Speter				       m->m_name);
171738032Speter				rcode = EX_CONFIG;
171838032Speter			}
171964562Sgshapiro			giveresponse(rcode, to->q_status, m, NULL,
172090792Sgshapiro				     ctladdr, xstart, e, to);
172190792Sgshapiro			markfailure(e, to, NULL, rcode, true);
172238032Speter			e->e_nsent++;
172338032Speter			if (rcode == EX_OK)
172438032Speter			{
172564562Sgshapiro				to->q_state = QS_SENT;
172638032Speter				if (bitnset(M_LOCALMAILER, m->m_flags) &&
172738032Speter				    bitset(QPINGONSUCCESS, to->q_flags))
172838032Speter				{
172938032Speter					to->q_flags |= QDELIVERED;
173038032Speter					to->q_status = "2.1.5";
173190792Sgshapiro					(void) sm_io_fprintf(e->e_xfp,
173290792Sgshapiro							     SM_TIME_DEFAULT,
173390792Sgshapiro							     "%s... Successfully delivered\n",
173490792Sgshapiro							     to->q_paddr);
173538032Speter				}
173638032Speter			}
173738032Speter			to->q_statdate = curtime();
173890792Sgshapiro			markstats(e, to, STATS_NORMAL);
173938032Speter			continue;
174038032Speter		}
174138032Speter
174238032Speter		/*
174338032Speter		**  Address is verified -- add this user to mailer
174438032Speter		**  argv, and add it to the print list of recipients.
174538032Speter		*/
174638032Speter
174738032Speter		/* link together the chain of recipients */
174838032Speter		to->q_tchain = tochain;
174938032Speter		tochain = to;
175064562Sgshapiro		e->e_to = "[CHAIN]";
175164562Sgshapiro
175290792Sgshapiro		macdefine(&e->e_macro, A_PERM, 'u', user);  /* to user */
175338032Speter		p = to->q_home;
175438032Speter		if (p == NULL && ctladdr != NULL)
175538032Speter			p = ctladdr->q_home;
175690792Sgshapiro		macdefine(&e->e_macro, A_PERM, 'z', p);  /* user's home */
175738032Speter
175864562Sgshapiro		/* set the ${dsn_notify} macro if applicable */
175964562Sgshapiro		if (bitset(QHASNOTIFY, to->q_flags))
176064562Sgshapiro		{
176164562Sgshapiro			char notify[MAXLINE];
176264562Sgshapiro
176364562Sgshapiro			notify[0] = '\0';
176464562Sgshapiro			if (bitset(QPINGONSUCCESS, to->q_flags))
176590792Sgshapiro				(void) sm_strlcat(notify, "SUCCESS,",
1766168515Sgshapiro						  sizeof(notify));
176764562Sgshapiro			if (bitset(QPINGONFAILURE, to->q_flags))
176890792Sgshapiro				(void) sm_strlcat(notify, "FAILURE,",
1769168515Sgshapiro						  sizeof(notify));
177064562Sgshapiro			if (bitset(QPINGONDELAY, to->q_flags))
177190792Sgshapiro				(void) sm_strlcat(notify, "DELAY,",
1772168515Sgshapiro						  sizeof(notify));
177364562Sgshapiro
177464562Sgshapiro			/* Set to NEVER or drop trailing comma */
177564562Sgshapiro			if (notify[0] == '\0')
177690792Sgshapiro				(void) sm_strlcat(notify, "NEVER",
1777168515Sgshapiro						  sizeof(notify));
177864562Sgshapiro			else
177964562Sgshapiro				notify[strlen(notify) - 1] = '\0';
178064562Sgshapiro
178190792Sgshapiro			macdefine(&e->e_macro, A_TEMP,
178290792Sgshapiro				macid("{dsn_notify}"), notify);
178364562Sgshapiro		}
178464562Sgshapiro		else
178590792Sgshapiro			macdefine(&e->e_macro, A_PERM,
178690792Sgshapiro				macid("{dsn_notify}"), NULL);
178764562Sgshapiro
178838032Speter		/*
178938032Speter		**  Expand out this user into argument list.
179038032Speter		*/
179138032Speter
179238032Speter		if (!clever)
179338032Speter		{
1794168515Sgshapiro			expand(*mvp, buf, sizeof(buf), e);
179590792Sgshapiro			*pvp++ = sm_rpool_strdup_x(e->e_rpool, buf);
179638032Speter			if (pvp >= &pv[MAXPV - 2])
179738032Speter			{
179838032Speter				/* allow some space for trailing parms */
179938032Speter				break;
180038032Speter			}
180138032Speter		}
180238032Speter	}
180338032Speter
180438032Speter	/* see if any addresses still exist */
180564562Sgshapiro	if (tochain == NULL)
180638032Speter	{
180790792Sgshapiro		rcode = 0;
180890792Sgshapiro		goto cleanup;
180938032Speter	}
181038032Speter
181138032Speter	/* print out messages as full list */
181290792Sgshapiro	strsize = 1;
181390792Sgshapiro	for (to = tochain; to != NULL; to = to->q_tchain)
181490792Sgshapiro		strsize += strlen(to->q_paddr) + 1;
181590792Sgshapiro	if (strsize < TOBUFSIZE)
181690792Sgshapiro		strsize = TOBUFSIZE;
181790792Sgshapiro	if (strsize > tobufsize)
181864562Sgshapiro	{
181990792Sgshapiro		SM_FREE_CLR(tobuf);
182090792Sgshapiro		tobuf = sm_pmalloc_x(strsize);
182190792Sgshapiro		tobufsize = strsize;
182264562Sgshapiro	}
182390792Sgshapiro	p = tobuf;
182490792Sgshapiro	*p = '\0';
182590792Sgshapiro	for (to = tochain; to != NULL; to = to->q_tchain)
182690792Sgshapiro	{
182790792Sgshapiro		(void) sm_strlcpyn(p, tobufsize - (p - tobuf), 2,
182890792Sgshapiro				   ",", to->q_paddr);
182990792Sgshapiro		p += strlen(p);
183090792Sgshapiro	}
183138032Speter	e->e_to = tobuf + 1;
183238032Speter
183338032Speter	/*
183438032Speter	**  Fill out any parameters after the $u parameter.
183538032Speter	*/
183638032Speter
183790792Sgshapiro	if (!clever)
183838032Speter	{
183990792Sgshapiro		while (*++mvp != NULL)
184090792Sgshapiro		{
1841168515Sgshapiro			expand(*mvp, buf, sizeof(buf), e);
184290792Sgshapiro			*pvp++ = sm_rpool_strdup_x(e->e_rpool, buf);
184390792Sgshapiro			if (pvp >= &pv[MAXPV])
184490792Sgshapiro				syserr("554 5.3.0 deliver: pv overflow after $u for %s",
184590792Sgshapiro				       pv[0]);
184690792Sgshapiro		}
184738032Speter	}
184838032Speter	*pvp++ = NULL;
184938032Speter
185038032Speter	/*
185138032Speter	**  Call the mailer.
185238032Speter	**	The argument vector gets built, pipes
185338032Speter	**	are created as necessary, and we fork & exec as
185438032Speter	**	appropriate.
185538032Speter	**	If we are running SMTP, we just need to clean up.
185638032Speter	*/
185738032Speter
1858223067Sgshapiro	/* XXX this seems a bit weird */
185938032Speter	if (ctladdr == NULL && m != ProgMailer && m != FileMailer &&
186038032Speter	    bitset(QGOODUID, e->e_from.q_flags))
186138032Speter		ctladdr = &e->e_from;
186238032Speter
186338032Speter#if NAMED_BIND
186438032Speter	if (ConfigLevel < 2)
186538032Speter		_res.options &= ~(RES_DEFNAMES | RES_DNSRCH);	/* XXX */
186664562Sgshapiro#endif /* NAMED_BIND */
186738032Speter
186838032Speter	if (tTd(11, 1))
186938032Speter	{
187090792Sgshapiro		sm_dprintf("openmailer:");
1871132943Sgshapiro		printav(sm_debug_file(), pv);
187238032Speter	}
187338032Speter	errno = 0;
187473188Sgshapiro	SM_SET_H_ERRNO(0);
187538032Speter	CurHostName = NULL;
187638032Speter
187738032Speter	/*
187838032Speter	**  Deal with the special case of mail handled through an IPC
187938032Speter	**  connection.
188038032Speter	**	In this case we don't actually fork.  We must be
188138032Speter	**	running SMTP for this to work.  We will return a
188238032Speter	**	zero pid to indicate that we are running IPC.
188338032Speter	**  We also handle a debug version that just talks to stdin/out.
188438032Speter	*/
188538032Speter
188638032Speter	curhost = NULL;
188738032Speter	SmtpPhase = NULL;
188838032Speter	mci = NULL;
188938032Speter
189038032Speter#if XDEBUG
189138032Speter	{
189238032Speter		char wbuf[MAXLINE];
189338032Speter
189438032Speter		/* make absolutely certain 0, 1, and 2 are in use */
1895168515Sgshapiro		(void) sm_snprintf(wbuf, sizeof(wbuf), "%s... openmailer(%s)",
189690792Sgshapiro				   shortenstring(e->e_to, MAXSHORTSTR),
189790792Sgshapiro				   m->m_name);
189838032Speter		checkfd012(wbuf);
189938032Speter	}
190064562Sgshapiro#endif /* XDEBUG */
190138032Speter
190238032Speter	/* check for 8-bit available */
190338032Speter	if (bitset(EF_HAS8BIT, e->e_flags) &&
190438032Speter	    bitnset(M_7BITS, m->m_flags) &&
190538032Speter	    (bitset(EF_DONT_MIME, e->e_flags) ||
190638032Speter	     !(bitset(MM_MIME8BIT, MimeMode) ||
190738032Speter	       (bitset(EF_IS_MIME, e->e_flags) &&
190864562Sgshapiro		bitset(MM_CVTMIME, MimeMode)))))
190938032Speter	{
191064562Sgshapiro		e->e_status = "5.6.3";
191164562Sgshapiro		usrerrenh(e->e_status,
191290792Sgshapiro			  "554 Cannot send 8-bit data to 7-bit destination");
191338032Speter		rcode = EX_DATAERR;
191438032Speter		goto give_up;
191538032Speter	}
191638032Speter
191738032Speter	if (tTd(62, 8))
191838032Speter		checkfds("before delivery");
191938032Speter
192038032Speter	/* check for Local Person Communication -- not for mortals!!! */
192138032Speter	if (strcmp(m->m_mailer, "[LPC]") == 0)
192238032Speter	{
192390792Sgshapiro		if (clever)
192490792Sgshapiro		{
192590792Sgshapiro			/* flush any expired connections */
192690792Sgshapiro			(void) mci_scan(NULL);
192790792Sgshapiro
192890792Sgshapiro			/* try to get a cached connection or just a slot */
192990792Sgshapiro			mci = mci_get(m->m_name, m);
193090792Sgshapiro			if (mci->mci_host == NULL)
193190792Sgshapiro				mci->mci_host = m->m_name;
193290792Sgshapiro			CurHostName = mci->mci_host;
193390792Sgshapiro			if (mci->mci_state != MCIS_CLOSED)
193490792Sgshapiro			{
193590792Sgshapiro				message("Using cached SMTP/LPC connection for %s...",
193690792Sgshapiro					m->m_name);
193790792Sgshapiro				mci->mci_deliveries++;
193890792Sgshapiro				goto do_transfer;
193990792Sgshapiro			}
194090792Sgshapiro		}
194190792Sgshapiro		else
194290792Sgshapiro		{
194390792Sgshapiro			mci = mci_new(e->e_rpool);
194490792Sgshapiro		}
194590792Sgshapiro		mci->mci_in = smioin;
194690792Sgshapiro		mci->mci_out = smioout;
194790792Sgshapiro		mci->mci_mailer = m;
194890792Sgshapiro		mci->mci_host = m->m_name;
194990792Sgshapiro		if (clever)
195090792Sgshapiro		{
195190792Sgshapiro			mci->mci_state = MCIS_OPENING;
195290792Sgshapiro			mci_cache(mci);
195390792Sgshapiro		}
195490792Sgshapiro		else
195590792Sgshapiro			mci->mci_state = MCIS_OPEN;
195638032Speter	}
195790792Sgshapiro	else if (strcmp(m->m_mailer, "[IPC]") == 0)
195838032Speter	{
195938032Speter		register int i;
196038032Speter
196138032Speter		if (pv[0] == NULL || pv[1] == NULL || pv[1][0] == '\0')
196238032Speter		{
196364562Sgshapiro			syserr("null destination for %s mailer", m->m_mailer);
196438032Speter			rcode = EX_CONFIG;
196538032Speter			goto give_up;
196638032Speter		}
196738032Speter
196864562Sgshapiro# if NETUNIX
196964562Sgshapiro		if (strcmp(pv[0], "FILE") == 0)
197064562Sgshapiro		{
197164562Sgshapiro			curhost = CurHostName = "localhost";
197264562Sgshapiro			mux_path = pv[1];
197364562Sgshapiro		}
197464562Sgshapiro		else
197564562Sgshapiro# endif /* NETUNIX */
197664562Sgshapiro		{
197764562Sgshapiro			CurHostName = pv[1];
197864562Sgshapiro			curhost = hostsignature(m, pv[1]);
197964562Sgshapiro		}
198038032Speter
198138032Speter		if (curhost == NULL || curhost[0] == '\0')
198238032Speter		{
198338032Speter			syserr("null host signature for %s", pv[1]);
198438032Speter			rcode = EX_CONFIG;
198538032Speter			goto give_up;
198638032Speter		}
198738032Speter
198838032Speter		if (!clever)
198938032Speter		{
199064562Sgshapiro			syserr("554 5.3.5 non-clever IPC");
199138032Speter			rcode = EX_CONFIG;
199238032Speter			goto give_up;
199338032Speter		}
199464562Sgshapiro		if (pv[2] != NULL
199564562Sgshapiro# if NETUNIX
199664562Sgshapiro		    && mux_path == NULL
199764562Sgshapiro# endif /* NETUNIX */
199864562Sgshapiro		    )
199938032Speter		{
200090792Sgshapiro			port = htons((unsigned short) atoi(pv[2]));
200138032Speter			if (port == 0)
200238032Speter			{
200364562Sgshapiro# ifdef NO_GETSERVBYNAME
200464562Sgshapiro				syserr("Invalid port number: %s", pv[2]);
200564562Sgshapiro# else /* NO_GETSERVBYNAME */
200638032Speter				struct servent *sp = getservbyname(pv[2], "tcp");
200738032Speter
200838032Speter				if (sp == NULL)
200938032Speter					syserr("Service %s unknown", pv[2]);
201038032Speter				else
201138032Speter					port = sp->s_port;
201264562Sgshapiro# endif /* NO_GETSERVBYNAME */
201338032Speter			}
201438032Speter		}
201564562Sgshapiro
201664562Sgshapiro		nummxhosts = parse_hostsignature(curhost, mxhosts, m);
201790792Sgshapiro		if (TimeOuts.to_aconnect > 0)
201890792Sgshapiro			enough = curtime() + TimeOuts.to_aconnect;
201938032Spetertryhost:
202064562Sgshapiro		while (hostnum < nummxhosts)
202138032Speter		{
202264562Sgshapiro			char sep = ':';
202364562Sgshapiro			char *endp;
202438032Speter			static char hostbuf[MAXNAME + 1];
2025132943Sgshapiro			bool tried_fallbacksmarthost = false;
202638032Speter
202764562Sgshapiro# if NETINET6
202864562Sgshapiro			if (*mxhosts[hostnum] == '[')
202938032Speter			{
203064562Sgshapiro				endp = strchr(mxhosts[hostnum] + 1, ']');
203164562Sgshapiro				if (endp != NULL)
203264562Sgshapiro					endp = strpbrk(endp + 1, ":,");
203364562Sgshapiro			}
203464562Sgshapiro			else
203564562Sgshapiro				endp = strpbrk(mxhosts[hostnum], ":,");
203664562Sgshapiro# else /* NETINET6 */
203764562Sgshapiro			endp = strpbrk(mxhosts[hostnum], ":,");
203864562Sgshapiro# endif /* NETINET6 */
203964562Sgshapiro			if (endp != NULL)
204064562Sgshapiro			{
204164562Sgshapiro				sep = *endp;
204264562Sgshapiro				*endp = '\0';
204364562Sgshapiro			}
204464562Sgshapiro
204590792Sgshapiro			if (hostnum == 1 && skip_back != NULL)
204690792Sgshapiro			{
204790792Sgshapiro				/*
204890792Sgshapiro				**  Coattail piggybacking is no longer an
204990792Sgshapiro				**  option with the mail host next to be tried
205090792Sgshapiro				**  no longer the lowest MX preference
205190792Sgshapiro				**  (hostnum == 1 meaning we're on the second
205290792Sgshapiro				**  preference). We do not try to coattail
205390792Sgshapiro				**  piggyback more than the first MX preference.
205490792Sgshapiro				**  Revert 'tochain' to last location for
205590792Sgshapiro				**  coincidental piggybacking. This works this
205690792Sgshapiro				**  easily because the q_tchain kept getting
205790792Sgshapiro				**  added to the top of the linked list.
205890792Sgshapiro				*/
205990792Sgshapiro
206090792Sgshapiro				tochain = skip_back;
206190792Sgshapiro			}
206290792Sgshapiro
206364562Sgshapiro			if (*mxhosts[hostnum] == '\0')
206464562Sgshapiro			{
206538032Speter				syserr("deliver: null host name in signature");
206664562Sgshapiro				hostnum++;
206764562Sgshapiro				if (endp != NULL)
206864562Sgshapiro					*endp = sep;
206938032Speter				continue;
207038032Speter			}
207190792Sgshapiro			(void) sm_strlcpy(hostbuf, mxhosts[hostnum],
2072168515Sgshapiro					  sizeof(hostbuf));
207364562Sgshapiro			hostnum++;
207464562Sgshapiro			if (endp != NULL)
207564562Sgshapiro				*endp = sep;
207638032Speter
2077132943Sgshapiro  one_last_try:
207838032Speter			/* see if we already know that this host is fried */
207938032Speter			CurHostName = hostbuf;
208038032Speter			mci = mci_get(hostbuf, m);
208138032Speter			if (mci->mci_state != MCIS_CLOSED)
208238032Speter			{
208390792Sgshapiro				char *type;
208490792Sgshapiro
208538032Speter				if (tTd(11, 1))
208638032Speter				{
208790792Sgshapiro					sm_dprintf("openmailer: ");
2088132943Sgshapiro					mci_dump(sm_debug_file(), mci, false);
208938032Speter				}
209038032Speter				CurHostName = mci->mci_host;
209190792Sgshapiro				if (bitnset(M_LMTP, m->m_flags))
209290792Sgshapiro					type = "L";
209390792Sgshapiro				else if (bitset(MCIF_ESMTP, mci->mci_flags))
209490792Sgshapiro					type = "ES";
209590792Sgshapiro				else
209690792Sgshapiro					type = "S";
209790792Sgshapiro				message("Using cached %sMTP connection to %s via %s...",
209890792Sgshapiro					type, hostbuf, m->m_name);
209964562Sgshapiro				mci->mci_deliveries++;
210038032Speter				break;
210138032Speter			}
210238032Speter			mci->mci_mailer = m;
210338032Speter			if (mci->mci_exitstat != EX_OK)
210438032Speter			{
210538032Speter				if (mci->mci_exitstat == EX_TEMPFAIL)
210690792Sgshapiro					goodmxfound = true;
2107132943Sgshapiro
2108132943Sgshapiro				/* Try FallbackSmartHost? */
2109132943Sgshapiro				if (should_try_fbsh(e, &tried_fallbacksmarthost,
2110168515Sgshapiro						    hostbuf, sizeof(hostbuf),
2111132943Sgshapiro						    mci->mci_exitstat))
2112132943Sgshapiro					goto one_last_try;
2113132943Sgshapiro
211438032Speter				continue;
211538032Speter			}
211638032Speter
211738032Speter			if (mci_lock_host(mci) != EX_OK)
211838032Speter			{
211938032Speter				mci_setstat(mci, EX_TEMPFAIL, "4.4.5", NULL);
212090792Sgshapiro				goodmxfound = true;
212138032Speter				continue;
212238032Speter			}
212338032Speter
212438032Speter			/* try the connection */
212590792Sgshapiro			sm_setproctitle(true, e, "%s %s: %s",
212664562Sgshapiro					qid_printname(e),
212764562Sgshapiro					hostbuf, "user open");
212864562Sgshapiro# if NETUNIX
212964562Sgshapiro			if (mux_path != NULL)
213064562Sgshapiro			{
213138032Speter				message("Connecting to %s via %s...",
213264562Sgshapiro					mux_path, m->m_name);
213390792Sgshapiro				i = makeconnection_ds((char *) mux_path, mci);
213464562Sgshapiro			}
213538032Speter			else
213664562Sgshapiro# endif /* NETUNIX */
213764562Sgshapiro			{
213864562Sgshapiro				if (port == 0)
213964562Sgshapiro					message("Connecting to %s via %s...",
214064562Sgshapiro						hostbuf, m->m_name);
214164562Sgshapiro				else
214264562Sgshapiro					message("Connecting to %s port %d via %s...",
214364562Sgshapiro						hostbuf, ntohs(port),
214464562Sgshapiro						m->m_name);
214590792Sgshapiro				i = makeconnection(hostbuf, port, mci, e,
214690792Sgshapiro						   enough);
214764562Sgshapiro			}
214877349Sgshapiro			mci->mci_errno = errno;
214938032Speter			mci->mci_lastuse = curtime();
215064562Sgshapiro			mci->mci_deliveries = 0;
215138032Speter			mci->mci_exitstat = i;
2152223067Sgshapiro			mci_clr_extensions(mci);
215364562Sgshapiro# if NAMED_BIND
215438032Speter			mci->mci_herrno = h_errno;
215564562Sgshapiro# endif /* NAMED_BIND */
215690792Sgshapiro
215790792Sgshapiro			/*
215890792Sgshapiro			**  Have we tried long enough to get a connection?
215990792Sgshapiro			**	If yes, skip to the fallback MX hosts
216090792Sgshapiro			**	(if existent).
216190792Sgshapiro			*/
216290792Sgshapiro
216390792Sgshapiro			if (enough > 0 && mci->mci_lastuse >= enough)
216490792Sgshapiro			{
216590792Sgshapiro				int h;
216690792Sgshapiro# if NAMED_BIND
2167132943Sgshapiro				extern int NumFallbackMXHosts;
216890792Sgshapiro# else /* NAMED_BIND */
2169132943Sgshapiro				const int NumFallbackMXHosts = 0;
217090792Sgshapiro# endif /* NAMED_BIND */
217190792Sgshapiro
217290792Sgshapiro				if (hostnum < nummxhosts && LogLevel > 9)
217390792Sgshapiro					sm_syslog(LOG_INFO, e->e_id,
217490792Sgshapiro						  "Timeout.to_aconnect occurred before exhausting all addresses");
217590792Sgshapiro
217690792Sgshapiro				/* turn off timeout if fallback available */
2177132943Sgshapiro				if (NumFallbackMXHosts > 0)
217890792Sgshapiro					enough = 0;
217990792Sgshapiro
218090792Sgshapiro				/* skip to a fallback MX host */
2181132943Sgshapiro				h = nummxhosts - NumFallbackMXHosts;
218290792Sgshapiro				if (hostnum < h)
218390792Sgshapiro					hostnum = h;
218490792Sgshapiro			}
218538032Speter			if (i == EX_OK)
218638032Speter			{
218790792Sgshapiro				goodmxfound = true;
218894334Sgshapiro				markstats(e, firstto, STATS_CONNECT);
218938032Speter				mci->mci_state = MCIS_OPENING;
219038032Speter				mci_cache(mci);
219138032Speter				if (TrafficLogFile != NULL)
219290792Sgshapiro					(void) sm_io_fprintf(TrafficLogFile,
219390792Sgshapiro							     SM_TIME_DEFAULT,
219490792Sgshapiro							     "%05d === CONNECT %s\n",
219590792Sgshapiro							     (int) CurrentPid,
219690792Sgshapiro							     hostbuf);
219738032Speter				break;
219838032Speter			}
219938032Speter			else
220038032Speter			{
2201132943Sgshapiro				/* Try FallbackSmartHost? */
2202132943Sgshapiro				if (should_try_fbsh(e, &tried_fallbacksmarthost,
2203168515Sgshapiro						    hostbuf, sizeof(hostbuf), i))
2204132943Sgshapiro					goto one_last_try;
2205132943Sgshapiro
220664562Sgshapiro				if (tTd(11, 1))
220790792Sgshapiro					sm_dprintf("openmailer: makeconnection => stat=%d, errno=%d\n",
220890792Sgshapiro						   i, errno);
220938032Speter				if (i == EX_TEMPFAIL)
221090792Sgshapiro					goodmxfound = true;
221138032Speter				mci_unlock_host(mci);
221238032Speter			}
221338032Speter
221438032Speter			/* enter status of this host */
221538032Speter			setstat(i);
221638032Speter
221738032Speter			/* should print some message here for -v mode */
221838032Speter		}
221938032Speter		if (mci == NULL)
222038032Speter		{
222138032Speter			syserr("deliver: no host name");
222238032Speter			rcode = EX_SOFTWARE;
222338032Speter			goto give_up;
222438032Speter		}
222538032Speter		mci->mci_pid = 0;
222638032Speter	}
222738032Speter	else
222838032Speter	{
222938032Speter		/* flush any expired connections */
223038032Speter		(void) mci_scan(NULL);
223138032Speter		mci = NULL;
223238032Speter
223338032Speter		if (bitnset(M_LMTP, m->m_flags))
223438032Speter		{
223538032Speter			/* try to get a cached connection */
223638032Speter			mci = mci_get(m->m_name, m);
223738032Speter			if (mci->mci_host == NULL)
223838032Speter				mci->mci_host = m->m_name;
223938032Speter			CurHostName = mci->mci_host;
224038032Speter			if (mci->mci_state != MCIS_CLOSED)
224138032Speter			{
224238032Speter				message("Using cached LMTP connection for %s...",
224338032Speter					m->m_name);
224464562Sgshapiro				mci->mci_deliveries++;
224538032Speter				goto do_transfer;
224638032Speter			}
224738032Speter		}
224838032Speter
224938032Speter		/* announce the connection to verbose listeners */
225038032Speter		if (host == NULL || host[0] == '\0')
225138032Speter			message("Connecting to %s...", m->m_name);
225238032Speter		else
225338032Speter			message("Connecting to %s via %s...", host, m->m_name);
225438032Speter		if (TrafficLogFile != NULL)
225538032Speter		{
225638032Speter			char **av;
225738032Speter
225890792Sgshapiro			(void) sm_io_fprintf(TrafficLogFile, SM_TIME_DEFAULT,
225990792Sgshapiro					     "%05d === EXEC", (int) CurrentPid);
226038032Speter			for (av = pv; *av != NULL; av++)
226190792Sgshapiro				(void) sm_io_fprintf(TrafficLogFile,
226290792Sgshapiro						     SM_TIME_DEFAULT, " %s",
226390792Sgshapiro						     *av);
226490792Sgshapiro			(void) sm_io_fprintf(TrafficLogFile, SM_TIME_DEFAULT,
226590792Sgshapiro					     "\n");
226638032Speter		}
226738032Speter
226838032Speter#if XDEBUG
226938032Speter		checkfd012("before creating mail pipe");
227064562Sgshapiro#endif /* XDEBUG */
227138032Speter
227238032Speter		/* create a pipe to shove the mail through */
227338032Speter		if (pipe(mpvect) < 0)
227438032Speter		{
227538032Speter			syserr("%s... openmailer(%s): pipe (to mailer)",
227690792Sgshapiro			       shortenstring(e->e_to, MAXSHORTSTR), m->m_name);
227738032Speter			if (tTd(11, 1))
227890792Sgshapiro				sm_dprintf("openmailer: NULL\n");
227938032Speter			rcode = EX_OSERR;
228038032Speter			goto give_up;
228138032Speter		}
228238032Speter
228338032Speter#if XDEBUG
228438032Speter		/* make sure we didn't get one of the standard I/O files */
228538032Speter		if (mpvect[0] < 3 || mpvect[1] < 3)
228638032Speter		{
228738032Speter			syserr("%s... openmailer(%s): bogus mpvect %d %d",
228890792Sgshapiro			       shortenstring(e->e_to, MAXSHORTSTR), m->m_name,
228990792Sgshapiro			       mpvect[0], mpvect[1]);
229090792Sgshapiro			printopenfds(true);
229138032Speter			if (tTd(11, 1))
229290792Sgshapiro				sm_dprintf("openmailer: NULL\n");
229338032Speter			rcode = EX_OSERR;
229438032Speter			goto give_up;
229538032Speter		}
229638032Speter
229738032Speter		/* make sure system call isn't dead meat */
229838032Speter		checkfdopen(mpvect[0], "mpvect[0]");
229938032Speter		checkfdopen(mpvect[1], "mpvect[1]");
230038032Speter		if (mpvect[0] == mpvect[1] ||
230138032Speter		    (e->e_lockfp != NULL &&
230290792Sgshapiro		     (mpvect[0] == sm_io_getinfo(e->e_lockfp, SM_IO_WHAT_FD,
230390792Sgshapiro						 NULL) ||
230490792Sgshapiro		      mpvect[1] == sm_io_getinfo(e->e_lockfp, SM_IO_WHAT_FD,
230590792Sgshapiro						 NULL))))
230638032Speter		{
230738032Speter			if (e->e_lockfp == NULL)
230838032Speter				syserr("%s... openmailer(%s): overlapping mpvect %d %d",
230990792Sgshapiro				       shortenstring(e->e_to, MAXSHORTSTR),
231090792Sgshapiro				       m->m_name, mpvect[0], mpvect[1]);
231138032Speter			else
231238032Speter				syserr("%s... openmailer(%s): overlapping mpvect %d %d, lockfp = %d",
231390792Sgshapiro				       shortenstring(e->e_to, MAXSHORTSTR),
231490792Sgshapiro				       m->m_name, mpvect[0], mpvect[1],
231590792Sgshapiro				       sm_io_getinfo(e->e_lockfp,
231690792Sgshapiro						     SM_IO_WHAT_FD, NULL));
231738032Speter		}
231864562Sgshapiro#endif /* XDEBUG */
231938032Speter
232064562Sgshapiro		/* create a return pipe */
232164562Sgshapiro		if (pipe(rpvect) < 0)
232238032Speter		{
232364562Sgshapiro			syserr("%s... openmailer(%s): pipe (from mailer)",
232490792Sgshapiro			       shortenstring(e->e_to, MAXSHORTSTR),
232590792Sgshapiro			       m->m_name);
232664562Sgshapiro			(void) close(mpvect[0]);
232764562Sgshapiro			(void) close(mpvect[1]);
232864562Sgshapiro			if (tTd(11, 1))
232990792Sgshapiro				sm_dprintf("openmailer: NULL\n");
233064562Sgshapiro			rcode = EX_OSERR;
233164562Sgshapiro			goto give_up;
233238032Speter		}
233364562Sgshapiro#if XDEBUG
233464562Sgshapiro		checkfdopen(rpvect[0], "rpvect[0]");
233564562Sgshapiro		checkfdopen(rpvect[1], "rpvect[1]");
233664562Sgshapiro#endif /* XDEBUG */
233738032Speter
233838032Speter		/*
233938032Speter		**  Actually fork the mailer process.
234038032Speter		**	DOFORK is clever about retrying.
234138032Speter		**
234238032Speter		**	Dispose of SIGCHLD signal catchers that may be laying
234364562Sgshapiro		**	around so that endmailer will get it.
234438032Speter		*/
234538032Speter
234690792Sgshapiro		if (e->e_xfp != NULL)	/* for debugging */
234790792Sgshapiro			(void) sm_io_flush(e->e_xfp, SM_TIME_DEFAULT);
234890792Sgshapiro		(void) sm_io_flush(smioout, SM_TIME_DEFAULT);
234990792Sgshapiro		(void) sm_signal(SIGCHLD, SIG_DFL);
235064562Sgshapiro
235164562Sgshapiro
235238032Speter		DOFORK(FORK);
235338032Speter		/* pid is set by DOFORK */
235464562Sgshapiro
235538032Speter		if (pid < 0)
235638032Speter		{
235738032Speter			/* failure */
235838032Speter			syserr("%s... openmailer(%s): cannot fork",
235990792Sgshapiro			       shortenstring(e->e_to, MAXSHORTSTR), m->m_name);
236038032Speter			(void) close(mpvect[0]);
236138032Speter			(void) close(mpvect[1]);
236264562Sgshapiro			(void) close(rpvect[0]);
236364562Sgshapiro			(void) close(rpvect[1]);
236438032Speter			if (tTd(11, 1))
236590792Sgshapiro				sm_dprintf("openmailer: NULL\n");
236638032Speter			rcode = EX_OSERR;
236738032Speter			goto give_up;
236838032Speter		}
236938032Speter		else if (pid == 0)
237038032Speter		{
237164562Sgshapiro			int save_errno;
237290792Sgshapiro			int sff;
237338032Speter			int new_euid = NO_UID;
237438032Speter			int new_ruid = NO_UID;
237538032Speter			int new_gid = NO_GID;
237690792Sgshapiro			char *user = NULL;
237738032Speter			struct stat stb;
237838032Speter			extern int DtableSize;
237938032Speter
238090792Sgshapiro			CurrentPid = getpid();
238190792Sgshapiro
238280785Sgshapiro			/* clear the events to turn off SIGALRMs */
238390792Sgshapiro			sm_clear_events();
238480785Sgshapiro
238577349Sgshapiro			/* Reset global flags */
238677349Sgshapiro			RestartRequest = NULL;
238790792Sgshapiro			RestartWorkGroup = false;
238877349Sgshapiro			ShutdownRequest = NULL;
238977349Sgshapiro			PendingSignal = 0;
239077349Sgshapiro
239138032Speter			if (e->e_lockfp != NULL)
239290792Sgshapiro				(void) close(sm_io_getinfo(e->e_lockfp,
239390792Sgshapiro							   SM_IO_WHAT_FD,
239490792Sgshapiro							   NULL));
239538032Speter
239638032Speter			/* child -- set up input & exec mailer */
239790792Sgshapiro			(void) sm_signal(SIGALRM, sm_signal_noop);
239890792Sgshapiro			(void) sm_signal(SIGCHLD, SIG_DFL);
239990792Sgshapiro			(void) sm_signal(SIGHUP, SIG_IGN);
240090792Sgshapiro			(void) sm_signal(SIGINT, SIG_IGN);
240190792Sgshapiro			(void) sm_signal(SIGTERM, SIG_DFL);
240280785Sgshapiro# ifdef SIGUSR1
240390792Sgshapiro			(void) sm_signal(SIGUSR1, sm_signal_noop);
240480785Sgshapiro# endif /* SIGUSR1 */
240538032Speter
240638032Speter			if (m != FileMailer || stat(tochain->q_user, &stb) < 0)
240738032Speter				stb.st_mode = 0;
240838032Speter
240964562Sgshapiro# if HASSETUSERCONTEXT
241038032Speter			/*
241138032Speter			**  Set user resources.
241238032Speter			*/
241338032Speter
241438032Speter			if (contextaddr != NULL)
241538032Speter			{
2416110560Sgshapiro				int sucflags;
241738032Speter				struct passwd *pwd;
241838032Speter
241938032Speter				if (contextaddr->q_ruser != NULL)
242038032Speter					pwd = sm_getpwnam(contextaddr->q_ruser);
242138032Speter				else
242238032Speter					pwd = sm_getpwnam(contextaddr->q_user);
2423110560Sgshapiro				sucflags = LOGIN_SETRESOURCES|LOGIN_SETPRIORITY;
2424223701Strasz#ifdef LOGIN_SETCPUMASK
2425223701Strasz				sucflags |= LOGIN_SETCPUMASK;
2426223701Strasz#endif /* LOGIN_SETCPUMASK */
2427223701Strasz#ifdef LOGIN_SETLOGINCLASS
2428223701Strasz				sucflags |= LOGIN_SETLOGINCLASS;
2429223701Strasz#endif /* LOGIN_SETLOGINCLASS */
2430110560Sgshapiro#ifdef LOGIN_SETMAC
2431110560Sgshapiro				sucflags |= LOGIN_SETMAC;
2432110560Sgshapiro#endif /* LOGIN_SETMAC */
2433102528Sgshapiro				if (pwd != NULL &&
2434102528Sgshapiro				    setusercontext(NULL, pwd, pwd->pw_uid,
2435110560Sgshapiro						   sucflags) == -1 &&
2436102528Sgshapiro				    suidwarn)
2437102528Sgshapiro				{
2438102528Sgshapiro					syserr("openmailer: setusercontext() failed");
2439102528Sgshapiro					exit(EX_TEMPFAIL);
2440102528Sgshapiro				}
244138032Speter			}
244264562Sgshapiro# endif /* HASSETUSERCONTEXT */
244338032Speter
244490792Sgshapiro#if HASNICE
244538032Speter			/* tweak niceness */
244638032Speter			if (m->m_nice != 0)
244764562Sgshapiro				(void) nice(m->m_nice);
244890792Sgshapiro#endif /* HASNICE */
244938032Speter
245038032Speter			/* reset group id */
245138032Speter			if (bitnset(M_SPECIFIC_UID, m->m_flags))
2452132943Sgshapiro			{
2453132943Sgshapiro				if (m->m_gid == NO_GID)
2454132943Sgshapiro					new_gid = RunAsGid;
2455132943Sgshapiro				else
2456132943Sgshapiro					new_gid = m->m_gid;
2457132943Sgshapiro			}
245838032Speter			else if (bitset(S_ISGID, stb.st_mode))
245938032Speter				new_gid = stb.st_gid;
246038032Speter			else if (ctladdr != NULL && ctladdr->q_gid != 0)
246138032Speter			{
246238032Speter				if (!DontInitGroups)
246338032Speter				{
246490792Sgshapiro					user = ctladdr->q_ruser;
246590792Sgshapiro					if (user == NULL)
246690792Sgshapiro						user = ctladdr->q_user;
246738032Speter
246890792Sgshapiro					if (initgroups(user,
246990792Sgshapiro						       ctladdr->q_gid) == -1
247090792Sgshapiro					    && suidwarn)
247164562Sgshapiro					{
2472285303Sgshapiro						syserr("openmailer: initgroups(%s, %ld) failed",
2473285303Sgshapiro							user, (long) ctladdr->q_gid);
247464562Sgshapiro						exit(EX_TEMPFAIL);
247564562Sgshapiro					}
247638032Speter				}
247738032Speter				else
247838032Speter				{
247938032Speter					GIDSET_T gidset[1];
248038032Speter
248138032Speter					gidset[0] = ctladdr->q_gid;
248290792Sgshapiro					if (setgroups(1, gidset) == -1
248390792Sgshapiro					    && suidwarn)
248464562Sgshapiro					{
248538032Speter						syserr("openmailer: setgroups() failed");
248664562Sgshapiro						exit(EX_TEMPFAIL);
248764562Sgshapiro					}
248838032Speter				}
248938032Speter				new_gid = ctladdr->q_gid;
249038032Speter			}
249138032Speter			else
249238032Speter			{
249338032Speter				if (!DontInitGroups)
249438032Speter				{
249590792Sgshapiro					user = DefUser;
249690792Sgshapiro					if (initgroups(DefUser, DefGid) == -1 &&
249790792Sgshapiro					    suidwarn)
249864562Sgshapiro					{
2499285303Sgshapiro						syserr("openmailer: initgroups(%s, %ld) failed",
2500285303Sgshapiro						       DefUser, (long) DefGid);
250164562Sgshapiro						exit(EX_TEMPFAIL);
250264562Sgshapiro					}
250338032Speter				}
250438032Speter				else
250538032Speter				{
250638032Speter					GIDSET_T gidset[1];
250738032Speter
250838032Speter					gidset[0] = DefGid;
250990792Sgshapiro					if (setgroups(1, gidset) == -1
251090792Sgshapiro					    && suidwarn)
251164562Sgshapiro					{
251238032Speter						syserr("openmailer: setgroups() failed");
251364562Sgshapiro						exit(EX_TEMPFAIL);
251464562Sgshapiro					}
251538032Speter				}
2516132943Sgshapiro				if (m->m_gid == NO_GID)
251738032Speter					new_gid = DefGid;
251838032Speter				else
251938032Speter					new_gid = m->m_gid;
252038032Speter			}
252164562Sgshapiro			if (new_gid != NO_GID)
252264562Sgshapiro			{
252364562Sgshapiro				if (RunAsUid != 0 &&
252464562Sgshapiro				    bitnset(M_SPECIFIC_UID, m->m_flags) &&
252564562Sgshapiro				    new_gid != getgid() &&
252664562Sgshapiro				    new_gid != getegid())
252764562Sgshapiro				{
252864562Sgshapiro					/* Only root can change the gid */
2529285303Sgshapiro					syserr("openmailer: insufficient privileges to change gid, RunAsUid=%ld, new_gid=%ld, gid=%ld, egid=%ld",
2530285303Sgshapiro					       (long) RunAsUid, (long) new_gid,
2531285303Sgshapiro					       (long) getgid(), (long) getegid());
253264562Sgshapiro					exit(EX_TEMPFAIL);
253364562Sgshapiro				}
253438032Speter
253564562Sgshapiro				if (setgid(new_gid) < 0 && suidwarn)
253664562Sgshapiro				{
253764562Sgshapiro					syserr("openmailer: setgid(%ld) failed",
253864562Sgshapiro					       (long) new_gid);
253964562Sgshapiro					exit(EX_TEMPFAIL);
254064562Sgshapiro				}
254164562Sgshapiro			}
254264562Sgshapiro
254364562Sgshapiro			/* change root to some "safe" directory */
254464562Sgshapiro			if (m->m_rootdir != NULL)
254564562Sgshapiro			{
2546168515Sgshapiro				expand(m->m_rootdir, cbuf, sizeof(cbuf), e);
254764562Sgshapiro				if (tTd(11, 20))
254890792Sgshapiro					sm_dprintf("openmailer: chroot %s\n",
254998121Sgshapiro						   cbuf);
255098121Sgshapiro				if (chroot(cbuf) < 0)
255164562Sgshapiro				{
255264562Sgshapiro					syserr("openmailer: Cannot chroot(%s)",
255398121Sgshapiro					       cbuf);
255464562Sgshapiro					exit(EX_TEMPFAIL);
255564562Sgshapiro				}
255664562Sgshapiro				if (chdir("/") < 0)
255764562Sgshapiro				{
255864562Sgshapiro					syserr("openmailer: cannot chdir(/)");
255964562Sgshapiro					exit(EX_TEMPFAIL);
256064562Sgshapiro				}
256164562Sgshapiro			}
256264562Sgshapiro
256338032Speter			/* reset user id */
256438032Speter			endpwent();
256590792Sgshapiro			sm_mbdb_terminate();
256638032Speter			if (bitnset(M_SPECIFIC_UID, m->m_flags))
256780785Sgshapiro			{
2568132943Sgshapiro				if (m->m_uid == NO_UID)
2569132943Sgshapiro					new_euid = RunAsUid;
2570132943Sgshapiro				else
2571132943Sgshapiro					new_euid = m->m_uid;
257280785Sgshapiro
257380785Sgshapiro				/*
257480785Sgshapiro				**  Undo the effects of the uid change in main
257580785Sgshapiro				**  for signal handling.  The real uid may
257680785Sgshapiro				**  be used by mailer in adding a "From "
257780785Sgshapiro				**  line.
257880785Sgshapiro				*/
257980785Sgshapiro
258080785Sgshapiro				if (RealUid != 0 && RealUid != getuid())
258190792Sgshapiro				{
258290792Sgshapiro# if MAILER_SETUID_METHOD == USE_SETEUID
258390792Sgshapiro#  if HASSETREUID
258490792Sgshapiro					if (setreuid(RealUid, geteuid()) < 0)
258590792Sgshapiro					{
258690792Sgshapiro						syserr("openmailer: setreuid(%d, %d) failed",
258790792Sgshapiro						       (int) RealUid, (int) geteuid());
258890792Sgshapiro						exit(EX_OSERR);
258990792Sgshapiro					}
259090792Sgshapiro#  endif /* HASSETREUID */
259190792Sgshapiro# endif /* MAILER_SETUID_METHOD == USE_SETEUID */
259290792Sgshapiro# if MAILER_SETUID_METHOD == USE_SETREUID
259380785Sgshapiro					new_ruid = RealUid;
259490792Sgshapiro# endif /* MAILER_SETUID_METHOD == USE_SETREUID */
259590792Sgshapiro				}
259680785Sgshapiro			}
259738032Speter			else if (bitset(S_ISUID, stb.st_mode))
259838032Speter				new_ruid = stb.st_uid;
259938032Speter			else if (ctladdr != NULL && ctladdr->q_uid != 0)
260038032Speter				new_ruid = ctladdr->q_uid;
2601132943Sgshapiro			else if (m->m_uid != NO_UID)
260238032Speter				new_ruid = m->m_uid;
260338032Speter			else
260438032Speter				new_ruid = DefUid;
260594334Sgshapiro
260694334Sgshapiro# if _FFR_USE_SETLOGIN
260794334Sgshapiro			/* run disconnected from terminal and set login name */
260894334Sgshapiro			if (setsid() >= 0 &&
260994334Sgshapiro			    ctladdr != NULL && ctladdr->q_uid != 0 &&
261094334Sgshapiro			    new_euid == ctladdr->q_uid)
261194334Sgshapiro			{
261294334Sgshapiro				struct passwd *pwd;
261394334Sgshapiro
261494334Sgshapiro				pwd = sm_getpwuid(ctladdr->q_uid);
261594334Sgshapiro				if (pwd != NULL && suidwarn)
261694334Sgshapiro					(void) setlogin(pwd->pw_name);
261794334Sgshapiro				endpwent();
261894334Sgshapiro			}
261994334Sgshapiro# endif /* _FFR_USE_SETLOGIN */
262094334Sgshapiro
262138032Speter			if (new_euid != NO_UID)
262238032Speter			{
262364562Sgshapiro				if (RunAsUid != 0 && new_euid != RunAsUid)
262464562Sgshapiro				{
262564562Sgshapiro					/* Only root can change the uid */
2626285303Sgshapiro					syserr("openmailer: insufficient privileges to change uid, new_euid=%ld, RunAsUid=%ld",
2627285303Sgshapiro					       (long) new_euid, (long) RunAsUid);
262864562Sgshapiro					exit(EX_TEMPFAIL);
262964562Sgshapiro				}
263064562Sgshapiro
263138032Speter				vendor_set_uid(new_euid);
263264562Sgshapiro# if MAILER_SETUID_METHOD == USE_SETEUID
263338032Speter				if (seteuid(new_euid) < 0 && suidwarn)
263464562Sgshapiro				{
263538032Speter					syserr("openmailer: seteuid(%ld) failed",
263690792Sgshapiro					       (long) new_euid);
263764562Sgshapiro					exit(EX_TEMPFAIL);
263864562Sgshapiro				}
263964562Sgshapiro# endif /* MAILER_SETUID_METHOD == USE_SETEUID */
264064562Sgshapiro# if MAILER_SETUID_METHOD == USE_SETREUID
264138032Speter				if (setreuid(new_ruid, new_euid) < 0 && suidwarn)
264264562Sgshapiro				{
264338032Speter					syserr("openmailer: setreuid(%ld, %ld) failed",
264490792Sgshapiro					       (long) new_ruid, (long) new_euid);
264564562Sgshapiro					exit(EX_TEMPFAIL);
264664562Sgshapiro				}
264764562Sgshapiro# endif /* MAILER_SETUID_METHOD == USE_SETREUID */
264864562Sgshapiro# if MAILER_SETUID_METHOD == USE_SETUID
264938032Speter				if (new_euid != geteuid() && setuid(new_euid) < 0 && suidwarn)
265064562Sgshapiro				{
265138032Speter					syserr("openmailer: setuid(%ld) failed",
265290792Sgshapiro					       (long) new_euid);
265364562Sgshapiro					exit(EX_TEMPFAIL);
265464562Sgshapiro				}
265564562Sgshapiro# endif /* MAILER_SETUID_METHOD == USE_SETUID */
265638032Speter			}
265738032Speter			else if (new_ruid != NO_UID)
265838032Speter			{
265938032Speter				vendor_set_uid(new_ruid);
266038032Speter				if (setuid(new_ruid) < 0 && suidwarn)
266164562Sgshapiro				{
266238032Speter					syserr("openmailer: setuid(%ld) failed",
266390792Sgshapiro					       (long) new_ruid);
266464562Sgshapiro					exit(EX_TEMPFAIL);
266564562Sgshapiro				}
266638032Speter			}
266738032Speter
266838032Speter			if (tTd(11, 2))
2669285303Sgshapiro				sm_dprintf("openmailer: running as r/euid=%ld/%ld, r/egid=%ld/%ld\n",
2670285303Sgshapiro					   (long) getuid(), (long) geteuid(),
2671285303Sgshapiro					   (long) getgid(), (long) getegid());
267238032Speter
267338032Speter			/* move into some "safe" directory */
267438032Speter			if (m->m_execdir != NULL)
267538032Speter			{
267638032Speter				char *q;
267738032Speter
267838032Speter				for (p = m->m_execdir; p != NULL; p = q)
267938032Speter				{
268038032Speter					q = strchr(p, ':');
268138032Speter					if (q != NULL)
268238032Speter						*q = '\0';
2683168515Sgshapiro					expand(p, cbuf, sizeof(cbuf), e);
268438032Speter					if (q != NULL)
268538032Speter						*q++ = ':';
268638032Speter					if (tTd(11, 20))
268790792Sgshapiro						sm_dprintf("openmailer: trydir %s\n",
268898121Sgshapiro							   cbuf);
268998121Sgshapiro					if (cbuf[0] != '\0' &&
269098121Sgshapiro					    chdir(cbuf) >= 0)
269138032Speter						break;
269238032Speter				}
269338032Speter			}
269438032Speter
269590792Sgshapiro			/* Check safety of program to be run */
269690792Sgshapiro			sff = SFF_ROOTOK|SFF_EXECOK;
269790792Sgshapiro			if (!bitnset(DBS_RUNWRITABLEPROGRAM,
269890792Sgshapiro				     DontBlameSendmail))
269990792Sgshapiro				sff |= SFF_NOGWFILES|SFF_NOWWFILES;
270090792Sgshapiro			if (bitnset(DBS_RUNPROGRAMINUNSAFEDIRPATH,
270190792Sgshapiro				    DontBlameSendmail))
270290792Sgshapiro				sff |= SFF_NOPATHCHECK;
270390792Sgshapiro			else
270490792Sgshapiro				sff |= SFF_SAFEDIRPATH;
270590792Sgshapiro			ret = safefile(m->m_mailer, getuid(), getgid(),
270690792Sgshapiro				       user, sff, 0, NULL);
270790792Sgshapiro			if (ret != 0)
270890792Sgshapiro				sm_syslog(LOG_INFO, e->e_id,
270990792Sgshapiro					  "Warning: program %s unsafe: %s",
271090792Sgshapiro					  m->m_mailer, sm_errstring(ret));
271190792Sgshapiro
271238032Speter			/* arrange to filter std & diag output of command */
271364562Sgshapiro			(void) close(rpvect[0]);
271464562Sgshapiro			if (dup2(rpvect[1], STDOUT_FILENO) < 0)
271538032Speter			{
271664562Sgshapiro				syserr("%s... openmailer(%s): cannot dup pipe %d for stdout",
271764562Sgshapiro				       shortenstring(e->e_to, MAXSHORTSTR),
271864562Sgshapiro				       m->m_name, rpvect[1]);
271964562Sgshapiro				_exit(EX_OSERR);
272038032Speter			}
272164562Sgshapiro			(void) close(rpvect[1]);
272264562Sgshapiro
272338032Speter			if (dup2(STDOUT_FILENO, STDERR_FILENO) < 0)
272438032Speter			{
272538032Speter				syserr("%s... openmailer(%s): cannot dup stdout for stderr",
272690792Sgshapiro				       shortenstring(e->e_to, MAXSHORTSTR),
272790792Sgshapiro				       m->m_name);
272838032Speter				_exit(EX_OSERR);
272938032Speter			}
273038032Speter
273138032Speter			/* arrange to get standard input */
273238032Speter			(void) close(mpvect[1]);
273338032Speter			if (dup2(mpvect[0], STDIN_FILENO) < 0)
273438032Speter			{
273538032Speter				syserr("%s... openmailer(%s): cannot dup pipe %d for stdin",
273690792Sgshapiro				       shortenstring(e->e_to, MAXSHORTSTR),
273790792Sgshapiro				       m->m_name, mpvect[0]);
273838032Speter				_exit(EX_OSERR);
273938032Speter			}
274038032Speter			(void) close(mpvect[0]);
274138032Speter
274238032Speter			/* arrange for all the files to be closed */
2743132943Sgshapiro			sm_close_on_exec(STDERR_FILENO + 1, DtableSize);
274438032Speter
274594334Sgshapiro# if !_FFR_USE_SETLOGIN
274638032Speter			/* run disconnected from terminal */
274738032Speter			(void) setsid();
274894334Sgshapiro# endif /* !_FFR_USE_SETLOGIN */
274938032Speter
275038032Speter			/* try to execute the mailer */
275164562Sgshapiro			(void) execve(m->m_mailer, (ARGV_T) pv,
275264562Sgshapiro				      (ARGV_T) UserEnviron);
275364562Sgshapiro			save_errno = errno;
275438032Speter			syserr("Cannot exec %s", m->m_mailer);
275538032Speter			if (bitnset(M_LOCALMAILER, m->m_flags) ||
275664562Sgshapiro			    transienterror(save_errno))
275738032Speter				_exit(EX_OSERR);
275838032Speter			_exit(EX_UNAVAILABLE);
275938032Speter		}
276038032Speter
276138032Speter		/*
276238032Speter		**  Set up return value.
276338032Speter		*/
276438032Speter
276538032Speter		if (mci == NULL)
276638032Speter		{
276790792Sgshapiro			if (clever)
276890792Sgshapiro			{
276990792Sgshapiro				/*
277090792Sgshapiro				**  Allocate from general heap, not
277190792Sgshapiro				**  envelope rpool, because this mci
277290792Sgshapiro				**  is going to be cached.
277390792Sgshapiro				*/
277490792Sgshapiro
277590792Sgshapiro				mci = mci_new(NULL);
277690792Sgshapiro			}
277790792Sgshapiro			else
277890792Sgshapiro			{
277990792Sgshapiro				/*
278090792Sgshapiro				**  Prevent a storage leak by allocating
278190792Sgshapiro				**  this from the envelope rpool.
278290792Sgshapiro				*/
278390792Sgshapiro
278490792Sgshapiro				mci = mci_new(e->e_rpool);
278590792Sgshapiro			}
278638032Speter		}
278738032Speter		mci->mci_mailer = m;
278838032Speter		if (clever)
278938032Speter		{
279038032Speter			mci->mci_state = MCIS_OPENING;
279138032Speter			mci_cache(mci);
279238032Speter		}
279338032Speter		else
279438032Speter		{
279538032Speter			mci->mci_state = MCIS_OPEN;
279638032Speter		}
279738032Speter		mci->mci_pid = pid;
279838032Speter		(void) close(mpvect[0]);
279990792Sgshapiro		mci->mci_out = sm_io_open(SmFtStdiofd, SM_TIME_DEFAULT,
2800132943Sgshapiro					  (void *) &(mpvect[1]), SM_IO_WRONLY_B,
280190792Sgshapiro					  NULL);
280238032Speter		if (mci->mci_out == NULL)
280338032Speter		{
280438032Speter			syserr("deliver: cannot create mailer output channel, fd=%d",
280590792Sgshapiro			       mpvect[1]);
280638032Speter			(void) close(mpvect[1]);
280764562Sgshapiro			(void) close(rpvect[0]);
280864562Sgshapiro			(void) close(rpvect[1]);
280938032Speter			rcode = EX_OSERR;
281038032Speter			goto give_up;
281138032Speter		}
281264562Sgshapiro
281364562Sgshapiro		(void) close(rpvect[1]);
281490792Sgshapiro		mci->mci_in = sm_io_open(SmFtStdiofd, SM_TIME_DEFAULT,
2815132943Sgshapiro					 (void *) &(rpvect[0]), SM_IO_RDONLY_B,
281690792Sgshapiro					 NULL);
281764562Sgshapiro		if (mci->mci_in == NULL)
281838032Speter		{
281964562Sgshapiro			syserr("deliver: cannot create mailer input channel, fd=%d",
282064562Sgshapiro			       mpvect[1]);
282164562Sgshapiro			(void) close(rpvect[0]);
282290792Sgshapiro			(void) sm_io_close(mci->mci_out, SM_TIME_DEFAULT);
282364562Sgshapiro			mci->mci_out = NULL;
282464562Sgshapiro			rcode = EX_OSERR;
282564562Sgshapiro			goto give_up;
282638032Speter		}
282738032Speter	}
282838032Speter
282938032Speter	/*
283038032Speter	**  If we are in SMTP opening state, send initial protocol.
283138032Speter	*/
283238032Speter
283338032Speter	if (bitnset(M_7BITS, m->m_flags) &&
283438032Speter	    (!clever || mci->mci_state == MCIS_OPENING))
283538032Speter		mci->mci_flags |= MCIF_7BIT;
283638032Speter	if (clever && mci->mci_state != MCIS_CLOSED)
283738032Speter	{
283890792Sgshapiro# if STARTTLS || SASL
283990792Sgshapiro		int dotpos;
284090792Sgshapiro		char *srvname;
284190792Sgshapiro		extern SOCKADDR CurHostAddr;
284290792Sgshapiro# endif /* STARTTLS || SASL */
284390792Sgshapiro
284490792Sgshapiro# if SASL
284571345Sgshapiro#  define DONE_AUTH(f)		bitset(MCIF_AUTHACT, f)
284690792Sgshapiro# endif /* SASL */
284764562Sgshapiro# if STARTTLS
284871345Sgshapiro#  define DONE_STARTTLS(f)	bitset(MCIF_TLSACT, f)
284964562Sgshapiro# endif /* STARTTLS */
285071345Sgshapiro# define ONLY_HELO(f)		bitset(MCIF_ONLY_EHLO, f)
285171345Sgshapiro# define SET_HELO(f)		f |= MCIF_ONLY_EHLO
285271345Sgshapiro# define CLR_HELO(f)		f &= ~MCIF_ONLY_EHLO
285338032Speter
285490792Sgshapiro# if STARTTLS || SASL
285590792Sgshapiro		/* don't use CurHostName, it is changed in many places */
285690792Sgshapiro		if (mci->mci_host != NULL)
285790792Sgshapiro		{
285890792Sgshapiro			srvname = mci->mci_host;
285990792Sgshapiro			dotpos = strlen(srvname) - 1;
286090792Sgshapiro			if (dotpos >= 0)
286190792Sgshapiro			{
286290792Sgshapiro				if (srvname[dotpos] == '.')
286390792Sgshapiro					srvname[dotpos] = '\0';
286490792Sgshapiro				else
286590792Sgshapiro					dotpos = -1;
286690792Sgshapiro			}
286790792Sgshapiro		}
286890792Sgshapiro		else if (mci->mci_mailer != NULL)
286990792Sgshapiro		{
287090792Sgshapiro			srvname = mci->mci_mailer->m_name;
287190792Sgshapiro			dotpos = -1;
287290792Sgshapiro		}
287390792Sgshapiro		else
287490792Sgshapiro		{
287590792Sgshapiro			srvname = "local";
287690792Sgshapiro			dotpos = -1;
287790792Sgshapiro		}
287871345Sgshapiro
287990792Sgshapiro		/* don't set {server_name} to NULL or "": see getauth() */
288090792Sgshapiro		macdefine(&mci->mci_macro, A_TEMP, macid("{server_name}"),
288190792Sgshapiro			  srvname);
288264562Sgshapiro
288390792Sgshapiro		/* CurHostAddr is set by makeconnection() and mci_get() */
288490792Sgshapiro		if (CurHostAddr.sa.sa_family != 0)
288590792Sgshapiro		{
288690792Sgshapiro			macdefine(&mci->mci_macro, A_TEMP,
288790792Sgshapiro				  macid("{server_addr}"),
288890792Sgshapiro				  anynet_ntoa(&CurHostAddr));
288990792Sgshapiro		}
289090792Sgshapiro		else if (mci->mci_mailer != NULL)
289190792Sgshapiro		{
289290792Sgshapiro			/* mailer name is unique, use it as address */
289390792Sgshapiro			macdefine(&mci->mci_macro, A_PERM,
289490792Sgshapiro				  macid("{server_addr}"),
289590792Sgshapiro				  mci->mci_mailer->m_name);
289690792Sgshapiro		}
289790792Sgshapiro		else
289890792Sgshapiro		{
289990792Sgshapiro			/* don't set it to NULL or "": see getauth() */
290090792Sgshapiro			macdefine(&mci->mci_macro, A_PERM,
290190792Sgshapiro				  macid("{server_addr}"), "0");
290290792Sgshapiro		}
290390792Sgshapiro
290490792Sgshapiro		/* undo change of srvname (mci->mci_host) */
290590792Sgshapiro		if (dotpos >= 0)
290690792Sgshapiro			srvname[dotpos] = '.';
290790792Sgshapiro
290890792Sgshapiroreconnect:	/* after switching to an encrypted connection */
290990792Sgshapiro# endif /* STARTTLS || SASL */
291090792Sgshapiro
291190792Sgshapiro		/* set the current connection information */
291290792Sgshapiro		e->e_mci = mci;
291364562Sgshapiro# if SASL
291464562Sgshapiro		mci->mci_saslcap = NULL;
291564562Sgshapiro# endif /* SASL */
291671345Sgshapiro		smtpinit(m, mci, e, ONLY_HELO(mci->mci_flags));
291771345Sgshapiro		CLR_HELO(mci->mci_flags);
291864562Sgshapiro
291990792Sgshapiro		if (IS_DLVR_RETURN(e))
292090792Sgshapiro		{
292190792Sgshapiro			/*
292290792Sgshapiro			**  Check whether other side can deliver e-mail
292390792Sgshapiro			**  fast enough
292490792Sgshapiro			*/
292590792Sgshapiro
292690792Sgshapiro			if (!bitset(MCIF_DLVR_BY, mci->mci_flags))
292790792Sgshapiro			{
292890792Sgshapiro				e->e_status = "5.4.7";
292990792Sgshapiro				usrerrenh(e->e_status,
293090792Sgshapiro					  "554 Server does not support Deliver By");
293190792Sgshapiro				rcode = EX_UNAVAILABLE;
293290792Sgshapiro				goto give_up;
293390792Sgshapiro			}
293490792Sgshapiro			if (e->e_deliver_by > 0 &&
293590792Sgshapiro			    e->e_deliver_by - (curtime() - e->e_ctime) <
293690792Sgshapiro			    mci->mci_min_by)
293790792Sgshapiro			{
293890792Sgshapiro				e->e_status = "5.4.7";
293990792Sgshapiro				usrerrenh(e->e_status,
294090792Sgshapiro					  "554 Message can't be delivered in time; %ld < %ld",
294190792Sgshapiro					  e->e_deliver_by - (curtime() - e->e_ctime),
294290792Sgshapiro					  mci->mci_min_by);
294390792Sgshapiro				rcode = EX_UNAVAILABLE;
294490792Sgshapiro				goto give_up;
294590792Sgshapiro			}
294690792Sgshapiro		}
294790792Sgshapiro
294864562Sgshapiro# if STARTTLS
294964562Sgshapiro		/* first TLS then AUTH to provide a security layer */
295071345Sgshapiro		if (mci->mci_state != MCIS_CLOSED &&
295171345Sgshapiro		    !DONE_STARTTLS(mci->mci_flags))
295264562Sgshapiro		{
295364562Sgshapiro			int olderrors;
295464562Sgshapiro			bool usetls;
295564562Sgshapiro			bool saveQuickAbort = QuickAbort;
295664562Sgshapiro			bool saveSuprErrs = SuprErrs;
295771345Sgshapiro			char *host = NULL;
295864562Sgshapiro
295964562Sgshapiro			rcode = EX_OK;
296064562Sgshapiro			usetls = bitset(MCIF_TLS, mci->mci_flags);
296190792Sgshapiro			if (usetls)
296290792Sgshapiro				usetls = !iscltflgset(e, D_NOTLS);
296366494Sgshapiro
2964168515Sgshapiro			host = macvalue(macid("{server_name}"), e);
296564562Sgshapiro			if (usetls)
296664562Sgshapiro			{
296764562Sgshapiro				olderrors = Errors;
296890792Sgshapiro				QuickAbort = false;
296990792Sgshapiro				SuprErrs = true;
2970102528Sgshapiro				if (rscheck("try_tls", host, NULL, e,
2971285303Sgshapiro					    RSF_RMCOMM, 7, host, NOQID, NULL,
2972285303Sgshapiro					    NULL) != EX_OK
297364562Sgshapiro				    || Errors > olderrors)
2974168515Sgshapiro				{
297590792Sgshapiro					usetls = false;
2976168515Sgshapiro				}
297764562Sgshapiro				SuprErrs = saveSuprErrs;
297864562Sgshapiro				QuickAbort = saveQuickAbort;
297964562Sgshapiro			}
298064562Sgshapiro
298164562Sgshapiro			if (usetls)
298264562Sgshapiro			{
298364562Sgshapiro				if ((rcode = starttls(m, mci, e)) == EX_OK)
298464562Sgshapiro				{
298564562Sgshapiro					/* start again without STARTTLS */
298664562Sgshapiro					mci->mci_flags |= MCIF_TLSACT;
298764562Sgshapiro				}
298864562Sgshapiro				else
298964562Sgshapiro				{
299064562Sgshapiro					char *s;
299164562Sgshapiro
299264562Sgshapiro					/*
2993203004Sgshapiro					**  TLS negotiation failed, what to do?
299464562Sgshapiro					**  fall back to unencrypted connection
299564562Sgshapiro					**  or abort? How to decide?
299664562Sgshapiro					**  set a macro and call a ruleset.
299764562Sgshapiro					*/
299890792Sgshapiro
299964562Sgshapiro					mci->mci_flags &= ~MCIF_TLS;
300064562Sgshapiro					switch (rcode)
300164562Sgshapiro					{
300264562Sgshapiro					  case EX_TEMPFAIL:
300364562Sgshapiro						s = "TEMP";
300464562Sgshapiro						break;
300564562Sgshapiro					  case EX_USAGE:
300664562Sgshapiro						s = "USAGE";
300764562Sgshapiro						break;
300864562Sgshapiro					  case EX_PROTOCOL:
300964562Sgshapiro						s = "PROTOCOL";
301064562Sgshapiro						break;
301164562Sgshapiro					  case EX_SOFTWARE:
301264562Sgshapiro						s = "SOFTWARE";
301364562Sgshapiro						break;
3014157001Sgshapiro					  case EX_UNAVAILABLE:
3015157001Sgshapiro						s = "NONE";
3016157001Sgshapiro						break;
301764562Sgshapiro
301864562Sgshapiro					  /* everything else is a failure */
301964562Sgshapiro					  default:
302064562Sgshapiro						s = "FAILURE";
302164562Sgshapiro						rcode = EX_TEMPFAIL;
302264562Sgshapiro					}
302390792Sgshapiro					macdefine(&e->e_macro, A_PERM,
302490792Sgshapiro						  macid("{verify}"), s);
302564562Sgshapiro				}
302664562Sgshapiro			}
302764562Sgshapiro			else
302890792Sgshapiro				macdefine(&e->e_macro, A_PERM,
302990792Sgshapiro					  macid("{verify}"), "NONE");
303064562Sgshapiro			olderrors = Errors;
303190792Sgshapiro			QuickAbort = false;
303290792Sgshapiro			SuprErrs = true;
303364562Sgshapiro
303464562Sgshapiro			/*
303564562Sgshapiro			**  rcode == EX_SOFTWARE is special:
3036203004Sgshapiro			**  the TLS negotiation failed
303764562Sgshapiro			**  we have to drop the connection no matter what
303864562Sgshapiro			**  However, we call tls_server to give it the chance
303964562Sgshapiro			**  to log the problem and return an appropriate
304064562Sgshapiro			**  error code.
304164562Sgshapiro			*/
304290792Sgshapiro
304364562Sgshapiro			if (rscheck("tls_server",
304490792Sgshapiro				    macvalue(macid("{verify}"), e),
3045102528Sgshapiro				    NULL, e, RSF_RMCOMM|RSF_COUNT, 5,
3046285303Sgshapiro				    host, NOQID, NULL, NULL) != EX_OK ||
304764562Sgshapiro			    Errors > olderrors ||
304864562Sgshapiro			    rcode == EX_SOFTWARE)
304964562Sgshapiro			{
305064562Sgshapiro				char enhsc[ENHSCLEN];
305164562Sgshapiro				extern char MsgBuf[];
305264562Sgshapiro
305364562Sgshapiro				if (ISSMTPCODE(MsgBuf) &&
305464562Sgshapiro				    extenhsc(MsgBuf + 4, ' ', enhsc) > 0)
305564562Sgshapiro				{
305690792Sgshapiro					p = sm_rpool_strdup_x(e->e_rpool,
305790792Sgshapiro							      MsgBuf);
305864562Sgshapiro				}
305964562Sgshapiro				else
306064562Sgshapiro				{
306164562Sgshapiro					p = "403 4.7.0 server not authenticated.";
306290792Sgshapiro					(void) sm_strlcpy(enhsc, "4.7.0",
3063168515Sgshapiro							  sizeof(enhsc));
306464562Sgshapiro				}
306564562Sgshapiro				SuprErrs = saveSuprErrs;
306664562Sgshapiro				QuickAbort = saveQuickAbort;
306764562Sgshapiro
306864562Sgshapiro				if (rcode == EX_SOFTWARE)
306964562Sgshapiro				{
307064562Sgshapiro					/* drop the connection */
307164562Sgshapiro					mci->mci_state = MCIS_QUITING;
307264562Sgshapiro					if (mci->mci_in != NULL)
307364562Sgshapiro					{
307490792Sgshapiro						(void) sm_io_close(mci->mci_in,
307590792Sgshapiro								   SM_TIME_DEFAULT);
307664562Sgshapiro						mci->mci_in = NULL;
307764562Sgshapiro					}
307864562Sgshapiro					mci->mci_flags &= ~MCIF_TLSACT;
307964562Sgshapiro					(void) endmailer(mci, e, pv);
308064562Sgshapiro				}
308164562Sgshapiro				else
308264562Sgshapiro				{
308364562Sgshapiro					/* abort transfer */
308464562Sgshapiro					smtpquit(m, mci, e);
308564562Sgshapiro				}
308664562Sgshapiro
308771345Sgshapiro				/* avoid bogus error msg */
308871345Sgshapiro				mci->mci_errno = 0;
308971345Sgshapiro
309064562Sgshapiro				/* temp or permanent failure? */
309164562Sgshapiro				rcode = (*p == '4') ? EX_TEMPFAIL
309264562Sgshapiro						    : EX_UNAVAILABLE;
309390792Sgshapiro				mci_setstat(mci, rcode, enhsc, p);
309464562Sgshapiro
309564562Sgshapiro				/*
309664562Sgshapiro				**  hack to get the error message into
309764562Sgshapiro				**  the envelope (done in giveresponse())
309864562Sgshapiro				*/
309990792Sgshapiro
310090792Sgshapiro				(void) sm_strlcpy(SmtpError, p,
3101168515Sgshapiro						  sizeof(SmtpError));
310264562Sgshapiro			}
3103168515Sgshapiro			else if (mci->mci_state == MCIS_CLOSED)
3104168515Sgshapiro			{
3105168515Sgshapiro				/* connection close caused by 421 */
3106168515Sgshapiro				mci->mci_errno = 0;
3107168515Sgshapiro				rcode = EX_TEMPFAIL;
3108168515Sgshapiro				mci_setstat(mci, rcode, NULL, "421");
3109168515Sgshapiro			}
3110168515Sgshapiro			else
3111168515Sgshapiro				rcode = 0;
3112168515Sgshapiro
311364562Sgshapiro			QuickAbort = saveQuickAbort;
311464562Sgshapiro			SuprErrs = saveSuprErrs;
311571345Sgshapiro			if (DONE_STARTTLS(mci->mci_flags) &&
311671345Sgshapiro			    mci->mci_state != MCIS_CLOSED)
311764562Sgshapiro			{
311871345Sgshapiro				SET_HELO(mci->mci_flags);
3119223067Sgshapiro				mci_clr_extensions(mci);
312064562Sgshapiro				goto reconnect;
312164562Sgshapiro			}
312264562Sgshapiro		}
312364562Sgshapiro# endif /* STARTTLS */
312464562Sgshapiro# if SASL
312564562Sgshapiro		/* if other server supports authentication let's authenticate */
312664562Sgshapiro		if (mci->mci_state != MCIS_CLOSED &&
312764562Sgshapiro		    mci->mci_saslcap != NULL &&
312890792Sgshapiro		    !DONE_AUTH(mci->mci_flags) && !iscltflgset(e, D_NOAUTH))
312964562Sgshapiro		{
313090792Sgshapiro			/* Should we require some minimum authentication? */
313190792Sgshapiro			if ((ret = smtpauth(m, mci, e)) == EX_OK)
313264562Sgshapiro			{
313364562Sgshapiro				int result;
313490792Sgshapiro				sasl_ssf_t *ssf = NULL;
313564562Sgshapiro
313690792Sgshapiro				/* Get security strength (features) */
313764562Sgshapiro				result = sasl_getprop(mci->mci_conn, SASL_SSF,
313898121Sgshapiro# if SASL >= 20000
313998121Sgshapiro						      (const void **) &ssf);
314098121Sgshapiro# else /* SASL >= 20000 */
314164562Sgshapiro						      (void **) &ssf);
314298121Sgshapiro# endif /* SASL >= 20000 */
314390792Sgshapiro
314490792Sgshapiro				/* XXX authid? */
314564562Sgshapiro				if (LogLevel > 9)
314664562Sgshapiro					sm_syslog(LOG_INFO, NOQID,
314790792Sgshapiro						  "AUTH=client, relay=%.100s, mech=%.16s, bits=%d",
314864562Sgshapiro						  mci->mci_host,
314990792Sgshapiro						  macvalue(macid("{auth_type}"), e),
315090792Sgshapiro						  result == SASL_OK ? *ssf : 0);
315177349Sgshapiro
315264562Sgshapiro				/*
315390792Sgshapiro				**  Only switch to encrypted connection
315464562Sgshapiro				**  if a security layer has been negotiated
315564562Sgshapiro				*/
315690792Sgshapiro
315764562Sgshapiro				if (result == SASL_OK && *ssf > 0)
315864562Sgshapiro				{
3159159609Sgshapiro					int tmo;
3160159609Sgshapiro
316164562Sgshapiro					/*
316290792Sgshapiro					**  Convert I/O layer to use SASL.
316390792Sgshapiro					**  If the call fails, the connection
316490792Sgshapiro					**  is aborted.
316564562Sgshapiro					*/
316690792Sgshapiro
3167159609Sgshapiro					tmo = DATA_PROGRESS_TIMEOUT * 1000;
316890792Sgshapiro					if (sfdcsasl(&mci->mci_in,
316990792Sgshapiro						     &mci->mci_out,
3170159609Sgshapiro						     mci->mci_conn, tmo) == 0)
317164562Sgshapiro					{
3172223067Sgshapiro						mci_clr_extensions(mci);
317390792Sgshapiro						mci->mci_flags |= MCIF_AUTHACT|
317490792Sgshapiro								  MCIF_ONLY_EHLO;
317564562Sgshapiro						goto reconnect;
317664562Sgshapiro					}
317790792Sgshapiro					syserr("AUTH TLS switch failed in client");
317864562Sgshapiro				}
317964562Sgshapiro				/* else? XXX */
318064562Sgshapiro				mci->mci_flags |= MCIF_AUTHACT;
318164562Sgshapiro
318264562Sgshapiro			}
318390792Sgshapiro			else if (ret == EX_TEMPFAIL)
318490792Sgshapiro			{
318590792Sgshapiro				if (LogLevel > 8)
318690792Sgshapiro					sm_syslog(LOG_ERR, NOQID,
318790792Sgshapiro						  "AUTH=client, relay=%.100s, temporary failure, connection abort",
318890792Sgshapiro						  mci->mci_host);
318990792Sgshapiro				smtpquit(m, mci, e);
319090792Sgshapiro
319190792Sgshapiro				/* avoid bogus error msg */
319290792Sgshapiro				mci->mci_errno = 0;
319390792Sgshapiro				rcode = EX_TEMPFAIL;
3194132943Sgshapiro				mci_setstat(mci, rcode, "4.3.0", p);
319590792Sgshapiro
319690792Sgshapiro				/*
319790792Sgshapiro				**  hack to get the error message into
319890792Sgshapiro				**  the envelope (done in giveresponse())
319990792Sgshapiro				*/
320090792Sgshapiro
320190792Sgshapiro				(void) sm_strlcpy(SmtpError,
320290792Sgshapiro						  "Temporary AUTH failure",
3203168515Sgshapiro						  sizeof(SmtpError));
320490792Sgshapiro			}
320564562Sgshapiro		}
320664562Sgshapiro# endif /* SASL */
320738032Speter	}
320838032Speter
320964562Sgshapiro
321038032Speterdo_transfer:
321138032Speter	/* clear out per-message flags from connection structure */
321238032Speter	mci->mci_flags &= ~(MCIF_CVT7TO8|MCIF_CVT8TO7);
321338032Speter
321438032Speter	if (bitset(EF_HAS8BIT, e->e_flags) &&
321538032Speter	    !bitset(EF_DONT_MIME, e->e_flags) &&
321638032Speter	    bitnset(M_7BITS, m->m_flags))
321738032Speter		mci->mci_flags |= MCIF_CVT8TO7;
321838032Speter
321938032Speter#if MIME7TO8
322038032Speter	if (bitnset(M_MAKE8BIT, m->m_flags) &&
322138032Speter	    !bitset(MCIF_7BIT, mci->mci_flags) &&
322238032Speter	    (p = hvalue("Content-Transfer-Encoding", e->e_header)) != NULL &&
322390792Sgshapiro	     (sm_strcasecmp(p, "quoted-printable") == 0 ||
322490792Sgshapiro	      sm_strcasecmp(p, "base64") == 0) &&
322538032Speter	    (p = hvalue("Content-Type", e->e_header)) != NULL)
322638032Speter	{
322738032Speter		/* may want to convert 7 -> 8 */
322838032Speter		/* XXX should really parse it here -- and use a class XXX */
322990792Sgshapiro		if (sm_strncasecmp(p, "text/plain", 10) == 0 &&
323038032Speter		    (p[10] == '\0' || p[10] == ' ' || p[10] == ';'))
323138032Speter			mci->mci_flags |= MCIF_CVT7TO8;
323238032Speter	}
323364562Sgshapiro#endif /* MIME7TO8 */
323438032Speter
323538032Speter	if (tTd(11, 1))
323638032Speter	{
323790792Sgshapiro		sm_dprintf("openmailer: ");
3238132943Sgshapiro		mci_dump(sm_debug_file(), mci, false);
323938032Speter	}
324038032Speter
324190792Sgshapiro#if _FFR_CLIENT_SIZE
324290792Sgshapiro	/*
324390792Sgshapiro	**  See if we know the maximum size and
324490792Sgshapiro	**  abort if the message is too big.
324590792Sgshapiro	**
324690792Sgshapiro	**  NOTE: _FFR_CLIENT_SIZE is untested.
324790792Sgshapiro	*/
324890792Sgshapiro
324990792Sgshapiro	if (bitset(MCIF_SIZE, mci->mci_flags) &&
325090792Sgshapiro	    mci->mci_maxsize > 0 &&
325190792Sgshapiro	    e->e_msgsize > mci->mci_maxsize)
325290792Sgshapiro	{
325390792Sgshapiro		e->e_flags |= EF_NO_BODY_RETN;
325490792Sgshapiro		if (bitnset(M_LOCALMAILER, m->m_flags))
325590792Sgshapiro			e->e_status = "5.2.3";
325690792Sgshapiro		else
325790792Sgshapiro			e->e_status = "5.3.4";
325890792Sgshapiro
325990792Sgshapiro		usrerrenh(e->e_status,
326090792Sgshapiro			  "552 Message is too large; %ld bytes max",
326190792Sgshapiro			  mci->mci_maxsize);
326290792Sgshapiro		rcode = EX_DATAERR;
326390792Sgshapiro
326490792Sgshapiro		/* Need an e_message for error */
3265168515Sgshapiro		(void) sm_snprintf(SmtpError, sizeof(SmtpError),
326690792Sgshapiro				   "Message is too large; %ld bytes max",
326790792Sgshapiro				   mci->mci_maxsize);
326890792Sgshapiro		goto give_up;
326990792Sgshapiro	}
327090792Sgshapiro#endif /* _FFR_CLIENT_SIZE */
327190792Sgshapiro
327238032Speter	if (mci->mci_state != MCIS_OPEN)
327338032Speter	{
327438032Speter		/* couldn't open the mailer */
327538032Speter		rcode = mci->mci_exitstat;
327638032Speter		errno = mci->mci_errno;
327773188Sgshapiro		SM_SET_H_ERRNO(mci->mci_herrno);
327838032Speter		if (rcode == EX_OK)
327938032Speter		{
328038032Speter			/* shouldn't happen */
328164562Sgshapiro			syserr("554 5.3.5 deliver: mci=%lx rcode=%d errno=%d state=%d sig=%s",
328290792Sgshapiro			       (unsigned long) mci, rcode, errno,
328390792Sgshapiro			       mci->mci_state, firstsig);
3284132943Sgshapiro			mci_dump_all(smioout, true);
328538032Speter			rcode = EX_SOFTWARE;
328638032Speter		}
328764562Sgshapiro		else if (nummxhosts > hostnum)
328838032Speter		{
328938032Speter			/* try next MX site */
329038032Speter			goto tryhost;
329138032Speter		}
329238032Speter	}
329338032Speter	else if (!clever)
329438032Speter	{
3295157001Sgshapiro		bool ok;
3296157001Sgshapiro
329738032Speter		/*
329838032Speter		**  Format and send message.
329938032Speter		*/
330038032Speter
3301157001Sgshapiro		rcode = EX_OK;
3302157001Sgshapiro		errno = 0;
3303157001Sgshapiro		ok = putfromline(mci, e);
3304157001Sgshapiro		if (ok)
3305157001Sgshapiro			ok = (*e->e_puthdr)(mci, e->e_header, e, M87F_OUTER);
3306157001Sgshapiro		if (ok)
3307157001Sgshapiro			ok = (*e->e_putbody)(mci, e, NULL);
3308173340Sgshapiro		if (ok && bitset(MCIF_INLONGLINE, mci->mci_flags))
3309173340Sgshapiro			ok = putline("", mci);
331038032Speter
3311157001Sgshapiro		/*
3312157001Sgshapiro		**  Ignore an I/O error that was caused by EPIPE.
3313157001Sgshapiro		**  Some broken mailers don't read the entire body
3314157001Sgshapiro		**  but just exit() thus causing an I/O error.
3315157001Sgshapiro		*/
3316157001Sgshapiro
3317157001Sgshapiro		if (!ok && (sm_io_error(mci->mci_out) && errno == EPIPE))
3318157001Sgshapiro			ok = true;
3319157001Sgshapiro
3320157001Sgshapiro		/* (always) get the exit status */
332138032Speter		rcode = endmailer(mci, e, pv);
3322157001Sgshapiro		if (!ok)
3323157001Sgshapiro			rcode = EX_TEMPFAIL;
332490792Sgshapiro		if (rcode == EX_TEMPFAIL && SmtpError[0] == '\0')
332573188Sgshapiro		{
332673188Sgshapiro			/*
332773188Sgshapiro			**  Need an e_message for mailq display.
332873188Sgshapiro			**  We set SmtpError as
332973188Sgshapiro			*/
333073188Sgshapiro
3331168515Sgshapiro			(void) sm_snprintf(SmtpError, sizeof(SmtpError),
333290792Sgshapiro					   "%s mailer (%s) exited with EX_TEMPFAIL",
333390792Sgshapiro					   m->m_name, m->m_mailer);
333473188Sgshapiro		}
333538032Speter	}
333638032Speter	else
333738032Speter	{
333838032Speter		/*
333938032Speter		**  Send the MAIL FROM: protocol
334038032Speter		*/
334138032Speter
334290792Sgshapiro		/* XXX this isn't pipelined... */
334338032Speter		rcode = smtpmailfrom(m, mci, e);
334438032Speter		if (rcode == EX_OK)
334538032Speter		{
334638032Speter			register int i;
334790792Sgshapiro# if PIPELINING
334890792Sgshapiro			ADDRESS *volatile pchain;
334990792Sgshapiro# endif /* PIPELINING */
335038032Speter
335138032Speter			/* send the recipient list */
335238032Speter			tobuf[0] = '\0';
335390792Sgshapiro			mci->mci_retryrcpt = false;
335490792Sgshapiro			mci->mci_tolist = tobuf;
335590792Sgshapiro# if PIPELINING
335690792Sgshapiro			pchain = NULL;
335790792Sgshapiro			mci->mci_nextaddr = NULL;
335890792Sgshapiro# endif /* PIPELINING */
335964562Sgshapiro
336038032Speter			for (to = tochain; to != NULL; to = to->q_tchain)
336138032Speter			{
336290792Sgshapiro				if (!QS_IS_UNMARKED(to->q_state))
336338032Speter					continue;
336464562Sgshapiro
336590792Sgshapiro				/* mark recipient state as "ok so far" */
336690792Sgshapiro				to->q_state = QS_OK;
336790792Sgshapiro				e->e_to = to->q_paddr;
336864562Sgshapiro# if STARTTLS
336964562Sgshapiro				i = rscheck("tls_rcpt", to->q_user, NULL, e,
3370102528Sgshapiro					    RSF_RMCOMM|RSF_COUNT, 3,
3371285303Sgshapiro					    mci->mci_host, e->e_id, NULL, NULL);
337264562Sgshapiro				if (i != EX_OK)
337338032Speter				{
337490792Sgshapiro					markfailure(e, to, mci, i, false);
337590792Sgshapiro					giveresponse(i, to->q_status,  m, mci,
337690792Sgshapiro						     ctladdr, xstart, e, to);
337790792Sgshapiro					if (i == EX_TEMPFAIL)
337890792Sgshapiro					{
337990792Sgshapiro						mci->mci_retryrcpt = true;
338090792Sgshapiro						to->q_state = QS_RETRY;
338190792Sgshapiro					}
338264562Sgshapiro					continue;
338338032Speter				}
338464562Sgshapiro# endif /* STARTTLS */
338564562Sgshapiro
338690792Sgshapiro				i = smtprcpt(to, m, mci, e, ctladdr, xstart);
338790792Sgshapiro# if PIPELINING
338890792Sgshapiro				if (i == EX_OK &&
338990792Sgshapiro				    bitset(MCIF_PIPELINED, mci->mci_flags))
339064562Sgshapiro				{
339190792Sgshapiro					/*
339290792Sgshapiro					**  Add new element to list of
339390792Sgshapiro					**  recipients for pipelining.
339490792Sgshapiro					*/
339590792Sgshapiro
339690792Sgshapiro					to->q_pchain = NULL;
339790792Sgshapiro					if (mci->mci_nextaddr == NULL)
339890792Sgshapiro						mci->mci_nextaddr = to;
339990792Sgshapiro					if (pchain == NULL)
340090792Sgshapiro						pchain = to;
340190792Sgshapiro					else
340290792Sgshapiro					{
340390792Sgshapiro						pchain->q_pchain = to;
340490792Sgshapiro						pchain = pchain->q_pchain;
340590792Sgshapiro					}
340664562Sgshapiro				}
340790792Sgshapiro# endif /* PIPELINING */
340890792Sgshapiro				if (i != EX_OK)
340938032Speter				{
341090792Sgshapiro					markfailure(e, to, mci, i, false);
341198841Sgshapiro					giveresponse(i, to->q_status, m, mci,
341290792Sgshapiro						     ctladdr, xstart, e, to);
341390792Sgshapiro					if (i == EX_TEMPFAIL)
341490792Sgshapiro						to->q_state = QS_RETRY;
341538032Speter				}
341638032Speter			}
341738032Speter
341890792Sgshapiro			/* No recipients in list and no missing responses? */
341990792Sgshapiro			if (tobuf[0] == '\0'
342090792Sgshapiro# if PIPELINING
3421173340Sgshapiro			    && bitset(MCIF_PIPELINED, mci->mci_flags)
342290792Sgshapiro			    && mci->mci_nextaddr == NULL
342390792Sgshapiro# endif /* PIPELINING */
342490792Sgshapiro			   )
342538032Speter			{
342638032Speter				rcode = EX_OK;
342738032Speter				e->e_to = NULL;
342838032Speter				if (bitset(MCIF_CACHED, mci->mci_flags))
342938032Speter					smtprset(m, mci, e);
343038032Speter			}
343138032Speter			else
343238032Speter			{
343338032Speter				e->e_to = tobuf + 1;
343490792Sgshapiro				rcode = smtpdata(m, mci, e, ctladdr, xstart);
343538032Speter			}
343638032Speter		}
343764562Sgshapiro		if (rcode == EX_TEMPFAIL && nummxhosts > hostnum)
343838032Speter		{
343938032Speter			/* try next MX site */
344038032Speter			goto tryhost;
344138032Speter		}
344238032Speter	}
344338032Speter#if NAMED_BIND
344438032Speter	if (ConfigLevel < 2)
344538032Speter		_res.options |= RES_DEFNAMES | RES_DNSRCH;	/* XXX */
344664562Sgshapiro#endif /* NAMED_BIND */
344738032Speter
344838032Speter	if (tTd(62, 1))
344938032Speter		checkfds("after delivery");
345038032Speter
345138032Speter	/*
345238032Speter	**  Do final status disposal.
345338032Speter	**	We check for something in tobuf for the SMTP case.
345438032Speter	**	If we got a temporary failure, arrange to queue the
345538032Speter	**		addressees.
345638032Speter	*/
345738032Speter
345838032Speter  give_up:
345938032Speter	if (bitnset(M_LMTP, m->m_flags))
346038032Speter	{
346138032Speter		lmtp_rcode = rcode;
346238032Speter		tobuf[0] = '\0';
346390792Sgshapiro		anyok = false;
346490792Sgshapiro		strsize = 0;
346538032Speter	}
346638032Speter	else
346738032Speter		anyok = rcode == EX_OK;
346838032Speter
346938032Speter	for (to = tochain; to != NULL; to = to->q_tchain)
347038032Speter	{
347138032Speter		/* see if address already marked */
347264562Sgshapiro		if (!QS_IS_OK(to->q_state))
347338032Speter			continue;
347438032Speter
347538032Speter		/* if running LMTP, get the status for each address */
347638032Speter		if (bitnset(M_LMTP, m->m_flags))
347738032Speter		{
347838032Speter			if (lmtp_rcode == EX_OK)
347938032Speter				rcode = smtpgetstat(m, mci, e);
348038032Speter			if (rcode == EX_OK)
348138032Speter			{
348290792Sgshapiro				strsize += sm_strlcat2(tobuf + strsize, ",",
348390792Sgshapiro						to->q_paddr,
348490792Sgshapiro						tobufsize - strsize);
348590792Sgshapiro				SM_ASSERT(strsize < tobufsize);
348690792Sgshapiro				anyok = true;
348738032Speter			}
348838032Speter			else
348938032Speter			{
349038032Speter				e->e_to = to->q_paddr;
349190792Sgshapiro				markfailure(e, to, mci, rcode, true);
349264562Sgshapiro				giveresponse(rcode, to->q_status, m, mci,
349390792Sgshapiro					     ctladdr, xstart, e, to);
349438032Speter				e->e_to = tobuf + 1;
349538032Speter				continue;
349638032Speter			}
349738032Speter		}
349838032Speter		else
349938032Speter		{
350038032Speter			/* mark bad addresses */
350138032Speter			if (rcode != EX_OK)
350238032Speter			{
350338032Speter				if (goodmxfound && rcode == EX_NOHOST)
350438032Speter					rcode = EX_TEMPFAIL;
350590792Sgshapiro				markfailure(e, to, mci, rcode, true);
350638032Speter				continue;
350738032Speter			}
350838032Speter		}
350938032Speter
351038032Speter		/* successful delivery */
351164562Sgshapiro		to->q_state = QS_SENT;
351238032Speter		to->q_statdate = curtime();
351338032Speter		e->e_nsent++;
351464562Sgshapiro
351564562Sgshapiro		/*
351664562Sgshapiro		**  Checkpoint the send list every few addresses
351764562Sgshapiro		*/
351864562Sgshapiro
351966494Sgshapiro		if (CheckpointInterval > 0 && e->e_nsent >= CheckpointInterval)
352064562Sgshapiro		{
352190792Sgshapiro			queueup(e, false, false);
352264562Sgshapiro			e->e_nsent = 0;
352364562Sgshapiro		}
352464562Sgshapiro
352538032Speter		if (bitnset(M_LOCALMAILER, m->m_flags) &&
352638032Speter		    bitset(QPINGONSUCCESS, to->q_flags))
352738032Speter		{
352838032Speter			to->q_flags |= QDELIVERED;
352938032Speter			to->q_status = "2.1.5";
353090792Sgshapiro			(void) sm_io_fprintf(e->e_xfp, SM_TIME_DEFAULT,
353190792Sgshapiro					     "%s... Successfully delivered\n",
353290792Sgshapiro					     to->q_paddr);
353338032Speter		}
353438032Speter		else if (bitset(QPINGONSUCCESS, to->q_flags) &&
353538032Speter			 bitset(QPRIMARY, to->q_flags) &&
353638032Speter			 !bitset(MCIF_DSN, mci->mci_flags))
353738032Speter		{
353838032Speter			to->q_flags |= QRELAYED;
353990792Sgshapiro			(void) sm_io_fprintf(e->e_xfp, SM_TIME_DEFAULT,
354090792Sgshapiro					     "%s... relayed; expect no further notifications\n",
354190792Sgshapiro					     to->q_paddr);
354238032Speter		}
354390792Sgshapiro		else if (IS_DLVR_NOTIFY(e) &&
354490792Sgshapiro			 !bitset(MCIF_DLVR_BY, mci->mci_flags) &&
354590792Sgshapiro			 bitset(QPRIMARY, to->q_flags) &&
354690792Sgshapiro			 (!bitset(QHASNOTIFY, to->q_flags) ||
354790792Sgshapiro			  bitset(QPINGONSUCCESS, to->q_flags) ||
354890792Sgshapiro			  bitset(QPINGONFAILURE, to->q_flags) ||
354990792Sgshapiro			  bitset(QPINGONDELAY, to->q_flags)))
355090792Sgshapiro		{
355190792Sgshapiro			/* RFC 2852, 4.1.4.2: no NOTIFY, or not NEVER */
355290792Sgshapiro			to->q_flags |= QBYNRELAY;
355390792Sgshapiro			(void) sm_io_fprintf(e->e_xfp, SM_TIME_DEFAULT,
355490792Sgshapiro					     "%s... Deliver-by notify: relayed\n",
355590792Sgshapiro					     to->q_paddr);
355690792Sgshapiro		}
355790792Sgshapiro		else if (IS_DLVR_TRACE(e) &&
355890792Sgshapiro			 (!bitset(QHASNOTIFY, to->q_flags) ||
355990792Sgshapiro			  bitset(QPINGONSUCCESS, to->q_flags) ||
356090792Sgshapiro			  bitset(QPINGONFAILURE, to->q_flags) ||
356190792Sgshapiro			  bitset(QPINGONDELAY, to->q_flags)) &&
356290792Sgshapiro			 bitset(QPRIMARY, to->q_flags))
356390792Sgshapiro		{
356490792Sgshapiro			/* RFC 2852, 4.1.4: no NOTIFY, or not NEVER */
356590792Sgshapiro			to->q_flags |= QBYTRACE;
356690792Sgshapiro			(void) sm_io_fprintf(e->e_xfp, SM_TIME_DEFAULT,
356790792Sgshapiro					     "%s... Deliver-By trace: relayed\n",
356890792Sgshapiro					     to->q_paddr);
356990792Sgshapiro		}
357038032Speter	}
357138032Speter
357238032Speter	if (bitnset(M_LMTP, m->m_flags))
357338032Speter	{
357438032Speter		/*
357538032Speter		**  Global information applies to the last recipient only;
357638032Speter		**  clear it out to avoid bogus errors.
357738032Speter		*/
357838032Speter
357938032Speter		rcode = EX_OK;
358038032Speter		e->e_statmsg = NULL;
358138032Speter
358238032Speter		/* reset the mci state for the next transaction */
358390792Sgshapiro		if (mci != NULL &&
358490792Sgshapiro		    (mci->mci_state == MCIS_MAIL ||
358590792Sgshapiro		     mci->mci_state == MCIS_RCPT ||
358690792Sgshapiro		     mci->mci_state == MCIS_DATA))
3587125820Sgshapiro		{
358838032Speter			mci->mci_state = MCIS_OPEN;
3589125820Sgshapiro			SmtpPhase = mci->mci_phase = "idle";
3590125820Sgshapiro			sm_setproctitle(true, e, "%s: %s", CurHostName,
3591125820Sgshapiro					mci->mci_phase);
3592125820Sgshapiro		}
359338032Speter	}
359438032Speter
359538032Speter	if (tobuf[0] != '\0')
359690792Sgshapiro	{
3597285303Sgshapiro		giveresponse(rcode, NULL, m, mci, ctladdr, xstart, e, NULL);
359890792Sgshapiro#if 0
359990792Sgshapiro		/*
360090792Sgshapiro		**  This code is disabled for now because I am not
360190792Sgshapiro		**  sure that copying status from the first recipient
360290792Sgshapiro		**  to all non-status'ed recipients is a good idea.
360390792Sgshapiro		*/
360490792Sgshapiro
360590792Sgshapiro		if (tochain->q_message != NULL &&
360690792Sgshapiro		    !bitnset(M_LMTP, m->m_flags) && rcode != EX_OK)
360790792Sgshapiro		{
360890792Sgshapiro			for (to = tochain->q_tchain; to != NULL;
360990792Sgshapiro			     to = to->q_tchain)
361090792Sgshapiro			{
361190792Sgshapiro				/* see if address already marked */
361290792Sgshapiro				if (QS_IS_QUEUEUP(to->q_state) &&
361390792Sgshapiro				    to->q_message == NULL)
361490792Sgshapiro					to->q_message = sm_rpool_strdup_x(e->e_rpool,
361590792Sgshapiro							tochain->q_message);
361690792Sgshapiro			}
361790792Sgshapiro		}
361890792Sgshapiro#endif /* 0 */
361990792Sgshapiro	}
362038032Speter	if (anyok)
362190792Sgshapiro		markstats(e, tochain, STATS_NORMAL);
362238032Speter	mci_store_persistent(mci);
362338032Speter
362490792Sgshapiro	/* Some recipients were tempfailed, try them on the next host */
362590792Sgshapiro	if (mci != NULL && mci->mci_retryrcpt && nummxhosts > hostnum)
362690792Sgshapiro	{
362790792Sgshapiro		/* try next MX site */
362890792Sgshapiro		goto tryhost;
362990792Sgshapiro	}
363090792Sgshapiro
363138032Speter	/* now close the connection */
363238032Speter	if (clever && mci != NULL && mci->mci_state != MCIS_CLOSED &&
363338032Speter	    !bitset(MCIF_CACHED, mci->mci_flags))
363438032Speter		smtpquit(m, mci, e);
363538032Speter
363690792Sgshapirocleanup: ;
363790792Sgshapiro	}
363890792Sgshapiro	SM_FINALLY
363990792Sgshapiro	{
364090792Sgshapiro		/*
364190792Sgshapiro		**  Restore state and return.
364290792Sgshapiro		*/
364338032Speter#if XDEBUG
364438032Speter		char wbuf[MAXLINE];
364538032Speter
364638032Speter		/* make absolutely certain 0, 1, and 2 are in use */
3647168515Sgshapiro		(void) sm_snprintf(wbuf, sizeof(wbuf),
364890792Sgshapiro				   "%s... end of deliver(%s)",
364990792Sgshapiro				   e->e_to == NULL ? "NO-TO-LIST"
365090792Sgshapiro						   : shortenstring(e->e_to,
365190792Sgshapiro								   MAXSHORTSTR),
365290792Sgshapiro				  m->m_name);
365338032Speter		checkfd012(wbuf);
365464562Sgshapiro#endif /* XDEBUG */
365538032Speter
365690792Sgshapiro		errno = 0;
365790792Sgshapiro
365890792Sgshapiro		/*
365990792Sgshapiro		**  It was originally necessary to set macro 'g' to NULL
366090792Sgshapiro		**  because it previously pointed to an auto buffer.
366190792Sgshapiro		**  We don't do this any more, so this may be unnecessary.
366290792Sgshapiro		*/
366390792Sgshapiro
366490792Sgshapiro		macdefine(&e->e_macro, A_PERM, 'g', (char *) NULL);
366590792Sgshapiro		e->e_to = NULL;
366690792Sgshapiro	}
366790792Sgshapiro	SM_END_TRY
366864562Sgshapiro	return rcode;
366938032Speter}
367064562Sgshapiro
367190792Sgshapiro/*
367238032Speter**  MARKFAILURE -- mark a failure on a specific address.
367338032Speter**
367438032Speter**	Parameters:
367538032Speter**		e -- the envelope we are sending.
367638032Speter**		q -- the address to mark.
367738032Speter**		mci -- mailer connection information.
367838032Speter**		rcode -- the code signifying the particular failure.
367964562Sgshapiro**		ovr -- override an existing code?
368038032Speter**
368138032Speter**	Returns:
368238032Speter**		none.
368338032Speter**
368438032Speter**	Side Effects:
368538032Speter**		marks the address (and possibly the envelope) with the
368638032Speter**			failure so that an error will be returned or
368738032Speter**			the message will be queued, as appropriate.
368838032Speter*/
368938032Speter
369090792Sgshapirovoid
369164562Sgshapiromarkfailure(e, q, mci, rcode, ovr)
369238032Speter	register ENVELOPE *e;
369338032Speter	register ADDRESS *q;
369438032Speter	register MCI *mci;
369538032Speter	int rcode;
369664562Sgshapiro	bool ovr;
369738032Speter{
369890792Sgshapiro	int save_errno = errno;
369964562Sgshapiro	char *status = NULL;
370064562Sgshapiro	char *rstatus = NULL;
370138032Speter
370238032Speter	switch (rcode)
370338032Speter	{
370438032Speter	  case EX_OK:
370538032Speter		break;
370638032Speter
370738032Speter	  case EX_TEMPFAIL:
370838032Speter	  case EX_IOERR:
370938032Speter	  case EX_OSERR:
371064562Sgshapiro		q->q_state = QS_QUEUEUP;
371138032Speter		break;
371238032Speter
371338032Speter	  default:
371464562Sgshapiro		q->q_state = QS_BADADDR;
371538032Speter		break;
371638032Speter	}
371738032Speter
371838032Speter	/* find most specific error code possible */
371938032Speter	if (mci != NULL && mci->mci_status != NULL)
372038032Speter	{
372190792Sgshapiro		status = sm_rpool_strdup_x(e->e_rpool, mci->mci_status);
372238032Speter		if (mci->mci_rstatus != NULL)
372390792Sgshapiro			rstatus = sm_rpool_strdup_x(e->e_rpool,
372490792Sgshapiro						    mci->mci_rstatus);
372538032Speter		else
372664562Sgshapiro			rstatus = NULL;
372738032Speter	}
372838032Speter	else if (e->e_status != NULL)
372938032Speter	{
373064562Sgshapiro		status = e->e_status;
373164562Sgshapiro		rstatus = NULL;
373238032Speter	}
373338032Speter	else
373438032Speter	{
373538032Speter		switch (rcode)
373638032Speter		{
373738032Speter		  case EX_USAGE:
373864562Sgshapiro			status = "5.5.4";
373938032Speter			break;
374038032Speter
374138032Speter		  case EX_DATAERR:
374264562Sgshapiro			status = "5.5.2";
374338032Speter			break;
374438032Speter
374538032Speter		  case EX_NOUSER:
374664562Sgshapiro			status = "5.1.1";
374738032Speter			break;
374838032Speter
374938032Speter		  case EX_NOHOST:
375064562Sgshapiro			status = "5.1.2";
375138032Speter			break;
375238032Speter
375338032Speter		  case EX_NOINPUT:
375438032Speter		  case EX_CANTCREAT:
375538032Speter		  case EX_NOPERM:
375664562Sgshapiro			status = "5.3.0";
375738032Speter			break;
375838032Speter
375938032Speter		  case EX_UNAVAILABLE:
376038032Speter		  case EX_SOFTWARE:
376138032Speter		  case EX_OSFILE:
376238032Speter		  case EX_PROTOCOL:
376338032Speter		  case EX_CONFIG:
376464562Sgshapiro			status = "5.5.0";
376538032Speter			break;
376638032Speter
376738032Speter		  case EX_OSERR:
376838032Speter		  case EX_IOERR:
376964562Sgshapiro			status = "4.5.0";
377038032Speter			break;
377138032Speter
377238032Speter		  case EX_TEMPFAIL:
377364562Sgshapiro			status = "4.2.0";
377438032Speter			break;
377538032Speter		}
377638032Speter	}
377738032Speter
377864562Sgshapiro	/* new status? */
377964562Sgshapiro	if (status != NULL && *status != '\0' && (ovr || q->q_status == NULL ||
378064562Sgshapiro	    *q->q_status == '\0' || *q->q_status < *status))
378164562Sgshapiro	{
378264562Sgshapiro		q->q_status = status;
378364562Sgshapiro		q->q_rstatus = rstatus;
378464562Sgshapiro	}
378538032Speter	if (rcode != EX_OK && q->q_rstatus == NULL &&
378638032Speter	    q->q_mailer != NULL && q->q_mailer->m_diagtype != NULL &&
378790792Sgshapiro	    sm_strcasecmp(q->q_mailer->m_diagtype, "X-UNIX") == 0)
378838032Speter	{
378964562Sgshapiro		char buf[16];
379038032Speter
3791168515Sgshapiro		(void) sm_snprintf(buf, sizeof(buf), "%d", rcode);
379290792Sgshapiro		q->q_rstatus = sm_rpool_strdup_x(e->e_rpool, buf);
379338032Speter	}
379464562Sgshapiro
379564562Sgshapiro	q->q_statdate = curtime();
379664562Sgshapiro	if (CurHostName != NULL && CurHostName[0] != '\0' &&
379764562Sgshapiro	    mci != NULL && !bitset(M_LOCALMAILER, mci->mci_flags))
379890792Sgshapiro		q->q_statmta = sm_rpool_strdup_x(e->e_rpool, CurHostName);
379990792Sgshapiro
380090792Sgshapiro	/* restore errno */
380190792Sgshapiro	errno = save_errno;
380238032Speter}
380390792Sgshapiro/*
380438032Speter**  ENDMAILER -- Wait for mailer to terminate.
380538032Speter**
380638032Speter**	We should never get fatal errors (e.g., segmentation
380738032Speter**	violation), so we report those specially.  For other
380838032Speter**	errors, we choose a status message (into statmsg),
380938032Speter**	and if it represents an error, we print it.
381038032Speter**
381138032Speter**	Parameters:
381280785Sgshapiro**		mci -- the mailer connection info.
381338032Speter**		e -- the current envelope.
381438032Speter**		pv -- the parameter vector that invoked the mailer
381538032Speter**			(for error messages).
381638032Speter**
381738032Speter**	Returns:
381838032Speter**		exit code of mailer.
381938032Speter**
382038032Speter**	Side Effects:
382138032Speter**		none.
382238032Speter*/
382338032Speter
382464562Sgshapirostatic jmp_buf	EndWaitTimeout;
382564562Sgshapiro
382664562Sgshapirostatic void
3827141858Sgshapiroendwaittimeout(ignore)
3828141858Sgshapiro	int ignore;
382964562Sgshapiro{
383077349Sgshapiro	/*
383177349Sgshapiro	**  NOTE: THIS CAN BE CALLED FROM A SIGNAL HANDLER.  DO NOT ADD
383277349Sgshapiro	**	ANYTHING TO THIS ROUTINE UNLESS YOU KNOW WHAT YOU ARE
383377349Sgshapiro	**	DOING.
383477349Sgshapiro	*/
383577349Sgshapiro
383664562Sgshapiro	errno = ETIMEDOUT;
383764562Sgshapiro	longjmp(EndWaitTimeout, 1);
383864562Sgshapiro}
383964562Sgshapiro
384038032Speterint
384138032Speterendmailer(mci, e, pv)
384238032Speter	register MCI *mci;
384338032Speter	register ENVELOPE *e;
384438032Speter	char **pv;
384538032Speter{
384638032Speter	int st;
384764562Sgshapiro	int save_errno = errno;
384864562Sgshapiro	char buf[MAXLINE];
384990792Sgshapiro	SM_EVENT *ev = NULL;
385038032Speter
385164562Sgshapiro
385238032Speter	mci_unlock_host(mci);
385338032Speter
385477349Sgshapiro	/* close output to mailer */
385577349Sgshapiro	if (mci->mci_out != NULL)
3856141858Sgshapiro	{
385790792Sgshapiro		(void) sm_io_close(mci->mci_out, SM_TIME_DEFAULT);
3858141858Sgshapiro		mci->mci_out = NULL;
3859141858Sgshapiro	}
386077349Sgshapiro
386177349Sgshapiro	/* copy any remaining input to transcript */
386277349Sgshapiro	if (mci->mci_in != NULL && mci->mci_state != MCIS_ERROR &&
386377349Sgshapiro	    e->e_xfp != NULL)
386477349Sgshapiro	{
3865168515Sgshapiro		while (sfgets(buf, sizeof(buf), mci->mci_in,
386690792Sgshapiro			      TimeOuts.to_quit, "Draining Input") != NULL)
386790792Sgshapiro			(void) sm_io_fputs(e->e_xfp, SM_TIME_DEFAULT, buf);
386877349Sgshapiro	}
386977349Sgshapiro
387064562Sgshapiro#if SASL
387190792Sgshapiro	/* close SASL connection */
387264562Sgshapiro	if (bitset(MCIF_AUTHACT, mci->mci_flags))
387364562Sgshapiro	{
387464562Sgshapiro		sasl_dispose(&mci->mci_conn);
387564562Sgshapiro		mci->mci_flags &= ~MCIF_AUTHACT;
387664562Sgshapiro	}
387764562Sgshapiro#endif /* SASL */
387864562Sgshapiro
387964562Sgshapiro#if STARTTLS
388064562Sgshapiro	/* shutdown TLS */
388164562Sgshapiro	(void) endtlsclt(mci);
388264562Sgshapiro#endif /* STARTTLS */
388364562Sgshapiro
388464562Sgshapiro	/* now close the input */
388538032Speter	if (mci->mci_in != NULL)
3886141858Sgshapiro	{
388790792Sgshapiro		(void) sm_io_close(mci->mci_in, SM_TIME_DEFAULT);
3888141858Sgshapiro		mci->mci_in = NULL;
3889141858Sgshapiro	}
389038032Speter	mci->mci_state = MCIS_CLOSED;
389138032Speter
389264562Sgshapiro	errno = save_errno;
389364562Sgshapiro
389438032Speter	/* in the IPC case there is nothing to wait for */
389538032Speter	if (mci->mci_pid == 0)
389664562Sgshapiro		return EX_OK;
389738032Speter
389864562Sgshapiro	/* put a timeout around the wait */
389964562Sgshapiro	if (mci->mci_mailer->m_wait > 0)
390064562Sgshapiro	{
390164562Sgshapiro		if (setjmp(EndWaitTimeout) == 0)
390290792Sgshapiro			ev = sm_setevent(mci->mci_mailer->m_wait,
390390792Sgshapiro					 endwaittimeout, 0);
390464562Sgshapiro		else
390564562Sgshapiro		{
390666494Sgshapiro			syserr("endmailer %s: wait timeout (%ld)",
390764562Sgshapiro			       mci->mci_mailer->m_name,
390866494Sgshapiro			       (long) mci->mci_mailer->m_wait);
390964562Sgshapiro			return EX_TEMPFAIL;
391064562Sgshapiro		}
391164562Sgshapiro	}
391238032Speter
391364562Sgshapiro	/* wait for the mailer process, collect status */
391438032Speter	st = waitfor(mci->mci_pid);
391564562Sgshapiro	save_errno = errno;
391664562Sgshapiro	if (ev != NULL)
391790792Sgshapiro		sm_clrevent(ev);
391864562Sgshapiro	errno = save_errno;
391964562Sgshapiro
392038032Speter	if (st == -1)
392138032Speter	{
392238032Speter		syserr("endmailer %s: wait", mci->mci_mailer->m_name);
392364562Sgshapiro		return EX_SOFTWARE;
392438032Speter	}
392538032Speter
392638032Speter	if (WIFEXITED(st))
392738032Speter	{
392838032Speter		/* normal death -- return status */
392938032Speter		return (WEXITSTATUS(st));
393038032Speter	}
393138032Speter
393238032Speter	/* it died a horrid death */
393364562Sgshapiro	syserr("451 4.3.0 mailer %s died with signal %d%s",
393464562Sgshapiro		mci->mci_mailer->m_name, WTERMSIG(st),
393564562Sgshapiro		WCOREDUMP(st) ? " (core dumped)" :
393664562Sgshapiro		(WIFSTOPPED(st) ? " (stopped)" : ""));
393738032Speter
393838032Speter	/* log the arguments */
393938032Speter	if (pv != NULL && e->e_xfp != NULL)
394038032Speter	{
394138032Speter		register char **av;
394238032Speter
394390792Sgshapiro		(void) sm_io_fprintf(e->e_xfp, SM_TIME_DEFAULT, "Arguments:");
394438032Speter		for (av = pv; *av != NULL; av++)
394590792Sgshapiro			(void) sm_io_fprintf(e->e_xfp, SM_TIME_DEFAULT, " %s",
394690792Sgshapiro					     *av);
394790792Sgshapiro		(void) sm_io_fprintf(e->e_xfp, SM_TIME_DEFAULT, "\n");
394838032Speter	}
394938032Speter
395038032Speter	ExitStat = EX_TEMPFAIL;
395164562Sgshapiro	return EX_TEMPFAIL;
395238032Speter}
395390792Sgshapiro/*
395438032Speter**  GIVERESPONSE -- Interpret an error response from a mailer
395538032Speter**
395638032Speter**	Parameters:
395764562Sgshapiro**		status -- the status code from the mailer (high byte
395838032Speter**			only; core dumps must have been taken care of
395938032Speter**			already).
396064562Sgshapiro**		dsn -- the DSN associated with the address, if any.
396138032Speter**		m -- the mailer info for this mailer.
396238032Speter**		mci -- the mailer connection info -- can be NULL if the
396338032Speter**			response is given before the connection is made.
396438032Speter**		ctladdr -- the controlling address for the recipient
396538032Speter**			address(es).
396638032Speter**		xstart -- the transaction start time, for computing
396738032Speter**			transaction delays.
396838032Speter**		e -- the current envelope.
396990792Sgshapiro**		to -- the current recipient (NULL if none).
397038032Speter**
397138032Speter**	Returns:
397238032Speter**		none.
397338032Speter**
397438032Speter**	Side Effects:
397538032Speter**		Errors may be incremented.
397638032Speter**		ExitStat may be set.
397738032Speter*/
397838032Speter
397938032Spetervoid
398090792Sgshapirogiveresponse(status, dsn, m, mci, ctladdr, xstart, e, to)
398164562Sgshapiro	int status;
398264562Sgshapiro	char *dsn;
398338032Speter	register MAILER *m;
398438032Speter	register MCI *mci;
398538032Speter	ADDRESS *ctladdr;
398638032Speter	time_t xstart;
398738032Speter	ENVELOPE *e;
398890792Sgshapiro	ADDRESS *to;
398938032Speter{
399038032Speter	register const char *statmsg;
399164562Sgshapiro	int errnum = errno;
399264562Sgshapiro	int off = 4;
399390792Sgshapiro	bool usestat = false;
399464562Sgshapiro	char dsnbuf[ENHSCLEN];
399538032Speter	char buf[MAXLINE];
399690792Sgshapiro	char *exmsg;
399738032Speter
399838032Speter	if (e == NULL)
3999159609Sgshapiro	{
400038032Speter		syserr("giveresponse: null envelope");
4001159609Sgshapiro		/* NOTREACHED */
4002159609Sgshapiro		SM_ASSERT(0);
4003159609Sgshapiro	}
400438032Speter
400538032Speter	/*
400638032Speter	**  Compute status message from code.
400738032Speter	*/
400838032Speter
400990792Sgshapiro	exmsg = sm_sysexmsg(status);
401064562Sgshapiro	if (status == 0)
401138032Speter	{
401264562Sgshapiro		statmsg = "250 2.0.0 Sent";
401338032Speter		if (e->e_statmsg != NULL)
401438032Speter		{
4015168515Sgshapiro			(void) sm_snprintf(buf, sizeof(buf), "%s (%s)",
401690792Sgshapiro					   statmsg,
401790792Sgshapiro					   shortenstring(e->e_statmsg, 403));
401838032Speter			statmsg = buf;
401938032Speter		}
402038032Speter	}
402190792Sgshapiro	else if (exmsg == NULL)
402238032Speter	{
4023168515Sgshapiro		(void) sm_snprintf(buf, sizeof(buf),
402490792Sgshapiro				   "554 5.3.0 unknown mailer error %d",
402590792Sgshapiro				   status);
402664562Sgshapiro		status = EX_UNAVAILABLE;
402738032Speter		statmsg = buf;
402890792Sgshapiro		usestat = true;
402938032Speter	}
403064562Sgshapiro	else if (status == EX_TEMPFAIL)
403138032Speter	{
403238032Speter		char *bp = buf;
403338032Speter
403490792Sgshapiro		(void) sm_strlcpy(bp, exmsg + 1, SPACELEFT(buf, bp));
403538032Speter		bp += strlen(bp);
403638032Speter#if NAMED_BIND
403738032Speter		if (h_errno == TRY_AGAIN)
403890792Sgshapiro			statmsg = sm_errstring(h_errno + E_DNSBASE);
403938032Speter		else
404064562Sgshapiro#endif /* NAMED_BIND */
404138032Speter		{
404264562Sgshapiro			if (errnum != 0)
404390792Sgshapiro				statmsg = sm_errstring(errnum);
404438032Speter			else
404538032Speter				statmsg = SmtpError;
404638032Speter		}
404738032Speter		if (statmsg != NULL && statmsg[0] != '\0')
404864562Sgshapiro		{
404964562Sgshapiro			switch (errnum)
405064562Sgshapiro			{
405164562Sgshapiro#ifdef ENETDOWN
405264562Sgshapiro			  case ENETDOWN:	/* Network is down */
405364562Sgshapiro#endif /* ENETDOWN */
405464562Sgshapiro#ifdef ENETUNREACH
405564562Sgshapiro			  case ENETUNREACH:	/* Network is unreachable */
405664562Sgshapiro#endif /* ENETUNREACH */
405764562Sgshapiro#ifdef ENETRESET
405864562Sgshapiro			  case ENETRESET:	/* Network dropped connection on reset */
405964562Sgshapiro#endif /* ENETRESET */
406064562Sgshapiro#ifdef ECONNABORTED
406164562Sgshapiro			  case ECONNABORTED:	/* Software caused connection abort */
406264562Sgshapiro#endif /* ECONNABORTED */
406364562Sgshapiro#ifdef EHOSTDOWN
406464562Sgshapiro			  case EHOSTDOWN:	/* Host is down */
406564562Sgshapiro#endif /* EHOSTDOWN */
406664562Sgshapiro#ifdef EHOSTUNREACH
406764562Sgshapiro			  case EHOSTUNREACH:	/* No route to host */
406864562Sgshapiro#endif /* EHOSTUNREACH */
4069141858Sgshapiro				if (mci != NULL && mci->mci_host != NULL)
407064562Sgshapiro				{
407190792Sgshapiro					(void) sm_strlcpyn(bp,
407290792Sgshapiro							   SPACELEFT(buf, bp),
407390792Sgshapiro							   2, ": ",
407490792Sgshapiro							   mci->mci_host);
407564562Sgshapiro					bp += strlen(bp);
407664562Sgshapiro				}
407764562Sgshapiro				break;
407864562Sgshapiro			}
407990792Sgshapiro			(void) sm_strlcpyn(bp, SPACELEFT(buf, bp), 2, ": ",
408090792Sgshapiro					   statmsg);
408190792Sgshapiro			usestat = true;
408264562Sgshapiro		}
408338032Speter		statmsg = buf;
408438032Speter	}
408538032Speter#if NAMED_BIND
408664562Sgshapiro	else if (status == EX_NOHOST && h_errno != 0)
408738032Speter	{
408890792Sgshapiro		statmsg = sm_errstring(h_errno + E_DNSBASE);
4089168515Sgshapiro		(void) sm_snprintf(buf, sizeof(buf), "%s (%s)", exmsg + 1,
409090792Sgshapiro				   statmsg);
409138032Speter		statmsg = buf;
409290792Sgshapiro		usestat = true;
409338032Speter	}
409464562Sgshapiro#endif /* NAMED_BIND */
409538032Speter	else
409638032Speter	{
409790792Sgshapiro		statmsg = exmsg;
409864562Sgshapiro		if (*statmsg++ == ':' && errnum != 0)
409938032Speter		{
4100168515Sgshapiro			(void) sm_snprintf(buf, sizeof(buf), "%s: %s", statmsg,
410190792Sgshapiro					   sm_errstring(errnum));
410238032Speter			statmsg = buf;
410390792Sgshapiro			usestat = true;
410438032Speter		}
410594334Sgshapiro		else if (bitnset(M_LMTP, m->m_flags) && e->e_statmsg != NULL)
410694334Sgshapiro		{
4107168515Sgshapiro			(void) sm_snprintf(buf, sizeof(buf), "%s (%s)", statmsg,
410894334Sgshapiro					   shortenstring(e->e_statmsg, 403));
410994334Sgshapiro			statmsg = buf;
411094334Sgshapiro			usestat = true;
411194334Sgshapiro		}
411238032Speter	}
411338032Speter
411438032Speter	/*
411538032Speter	**  Print the message as appropriate
411638032Speter	*/
411738032Speter
411864562Sgshapiro	if (status == EX_OK || status == EX_TEMPFAIL)
411938032Speter	{
412038032Speter		extern char MsgBuf[];
412138032Speter
412264562Sgshapiro		if ((off = isenhsc(statmsg + 4, ' ')) > 0)
412364562Sgshapiro		{
412464562Sgshapiro			if (dsn == NULL)
412564562Sgshapiro			{
4126168515Sgshapiro				(void) sm_snprintf(dsnbuf, sizeof(dsnbuf),
412790792Sgshapiro						   "%.*s", off, statmsg + 4);
412864562Sgshapiro				dsn = dsnbuf;
412964562Sgshapiro			}
413064562Sgshapiro			off += 5;
413164562Sgshapiro		}
413264562Sgshapiro		else
413364562Sgshapiro		{
413464562Sgshapiro			off = 4;
413564562Sgshapiro		}
413664562Sgshapiro		message("%s", statmsg + off);
413764562Sgshapiro		if (status == EX_TEMPFAIL && e->e_xfp != NULL)
413890792Sgshapiro			(void) sm_io_fprintf(e->e_xfp, SM_TIME_DEFAULT, "%s\n",
413990792Sgshapiro					     &MsgBuf[4]);
414038032Speter	}
414138032Speter	else
414238032Speter	{
414364562Sgshapiro		char mbuf[ENHSCLEN + 4];
414438032Speter
414538032Speter		Errors++;
414664562Sgshapiro		if ((off = isenhsc(statmsg + 4, ' ')) > 0 &&
4147168515Sgshapiro		    off < sizeof(mbuf) - 4)
414864562Sgshapiro		{
414964562Sgshapiro			if (dsn == NULL)
415064562Sgshapiro			{
4151168515Sgshapiro				(void) sm_snprintf(dsnbuf, sizeof(dsnbuf),
415290792Sgshapiro						   "%.*s", off, statmsg + 4);
415364562Sgshapiro				dsn = dsnbuf;
415464562Sgshapiro			}
415564562Sgshapiro			off += 5;
415690792Sgshapiro
415790792Sgshapiro			/* copy only part of statmsg to mbuf */
415890792Sgshapiro			(void) sm_strlcpy(mbuf, statmsg, off);
4159168515Sgshapiro			(void) sm_strlcat(mbuf, " %s", sizeof(mbuf));
416064562Sgshapiro		}
416164562Sgshapiro		else
416264562Sgshapiro		{
416364562Sgshapiro			dsnbuf[0] = '\0';
4164168515Sgshapiro			(void) sm_snprintf(mbuf, sizeof(mbuf), "%.3s %%s",
416590792Sgshapiro					   statmsg);
416664562Sgshapiro			off = 4;
416764562Sgshapiro		}
416864562Sgshapiro		usrerr(mbuf, &statmsg[off]);
416938032Speter	}
417038032Speter
417138032Speter	/*
417238032Speter	**  Final cleanup.
4173285303Sgshapiro	**	Log a record of the transaction.  Compute the new ExitStat
4174285303Sgshapiro	**	-- if we already had an error, stick with that.
417538032Speter	*/
417638032Speter
417738032Speter	if (OpMode != MD_VERIFY && !bitset(EF_VRFYONLY, e->e_flags) &&
417864562Sgshapiro	    LogLevel > ((status == EX_TEMPFAIL) ? 8 : (status == EX_OK) ? 7 : 6))
4179285303Sgshapiro		logdelivery(m, mci, dsn, statmsg + off, ctladdr, xstart, e, to, status);
418038032Speter
418138032Speter	if (tTd(11, 2))
418290792Sgshapiro		sm_dprintf("giveresponse: status=%d, dsn=%s, e->e_message=%s, errnum=%d\n",
418390792Sgshapiro			   status,
418490792Sgshapiro			   dsn == NULL ? "<NULL>" : dsn,
418590792Sgshapiro			   e->e_message == NULL ? "<NULL>" : e->e_message,
418690792Sgshapiro			   errnum);
418738032Speter
418864562Sgshapiro	if (status != EX_TEMPFAIL)
418964562Sgshapiro		setstat(status);
419064562Sgshapiro	if (status != EX_OK && (status != EX_TEMPFAIL || e->e_message == NULL))
419190792Sgshapiro		e->e_message = sm_rpool_strdup_x(e->e_rpool, statmsg + off);
419290792Sgshapiro	if (status != EX_OK && to != NULL && to->q_message == NULL)
419338032Speter	{
419490792Sgshapiro		if (!usestat && e->e_message != NULL)
419590792Sgshapiro			to->q_message = sm_rpool_strdup_x(e->e_rpool,
419690792Sgshapiro							  e->e_message);
419790792Sgshapiro		else
419890792Sgshapiro			to->q_message = sm_rpool_strdup_x(e->e_rpool,
419990792Sgshapiro							  statmsg + off);
420038032Speter	}
420138032Speter	errno = 0;
420273188Sgshapiro	SM_SET_H_ERRNO(0);
420338032Speter}
420490792Sgshapiro/*
420538032Speter**  LOGDELIVERY -- log the delivery in the system log
420638032Speter**
420738032Speter**	Care is taken to avoid logging lines that are too long, because
420838032Speter**	some versions of syslog have an unfortunate proclivity for core
420938032Speter**	dumping.  This is a hack, to be sure, that is at best empirical.
421038032Speter**
421138032Speter**	Parameters:
421238032Speter**		m -- the mailer info.  Can be NULL for initial queue.
421338032Speter**		mci -- the mailer connection info -- can be NULL if the
421464562Sgshapiro**			log is occurring when no connection is active.
421564562Sgshapiro**		dsn -- the DSN attached to the status.
421664562Sgshapiro**		status -- the message to print for the status.
421738032Speter**		ctladdr -- the controlling address for the to list.
421838032Speter**		xstart -- the transaction start time, used for
421938032Speter**			computing transaction delay.
422038032Speter**		e -- the current envelope.
4221285303Sgshapiro**		to -- the current recipient (NULL if none).
4222285303Sgshapiro**		rcode -- status code
422338032Speter**
422438032Speter**	Returns:
422538032Speter**		none
422638032Speter**
422738032Speter**	Side Effects:
422838032Speter**		none
422938032Speter*/
423038032Speter
423138032Spetervoid
4232285303Sgshapirologdelivery(m, mci, dsn, status, ctladdr, xstart, e, to, rcode)
423338032Speter	MAILER *m;
423438032Speter	register MCI *mci;
423564562Sgshapiro	char *dsn;
423664562Sgshapiro	const char *status;
423738032Speter	ADDRESS *ctladdr;
423838032Speter	time_t xstart;
423938032Speter	register ENVELOPE *e;
4240285303Sgshapiro	ADDRESS *to;
4241285303Sgshapiro	int rcode;
424238032Speter{
424338032Speter	register char *bp;
424438032Speter	register char *p;
424538032Speter	int l;
424690792Sgshapiro	time_t now = curtime();
424738032Speter	char buf[1024];
424838032Speter
424964562Sgshapiro#if (SYSLOG_BUFSIZE) >= 256
425038032Speter	/* ctladdr: max 106 bytes */
425138032Speter	bp = buf;
425238032Speter	if (ctladdr != NULL)
425338032Speter	{
425490792Sgshapiro		(void) sm_strlcpyn(bp, SPACELEFT(buf, bp), 2, ", ctladdr=",
425590792Sgshapiro				   shortenstring(ctladdr->q_paddr, 83));
425638032Speter		bp += strlen(bp);
425738032Speter		if (bitset(QGOODUID, ctladdr->q_flags))
425838032Speter		{
425990792Sgshapiro			(void) sm_snprintf(bp, SPACELEFT(buf, bp), " (%d/%d)",
426090792Sgshapiro					   (int) ctladdr->q_uid,
426190792Sgshapiro					   (int) ctladdr->q_gid);
426238032Speter			bp += strlen(bp);
426338032Speter		}
426438032Speter	}
426538032Speter
426638032Speter	/* delay & xdelay: max 41 bytes */
426790792Sgshapiro	(void) sm_strlcpyn(bp, SPACELEFT(buf, bp), 2, ", delay=",
426890792Sgshapiro			   pintvl(now - e->e_ctime, true));
426938032Speter	bp += strlen(bp);
427038032Speter
427138032Speter	if (xstart != (time_t) 0)
427238032Speter	{
427390792Sgshapiro		(void) sm_strlcpyn(bp, SPACELEFT(buf, bp), 2, ", xdelay=",
427490792Sgshapiro				   pintvl(now - xstart, true));
427538032Speter		bp += strlen(bp);
427638032Speter	}
427738032Speter
427838032Speter	/* mailer: assume about 19 bytes (max 10 byte mailer name) */
427938032Speter	if (m != NULL)
428038032Speter	{
428190792Sgshapiro		(void) sm_strlcpyn(bp, SPACELEFT(buf, bp), 2, ", mailer=",
428290792Sgshapiro				   m->m_name);
428338032Speter		bp += strlen(bp);
428438032Speter	}
428538032Speter
4286285303Sgshapiro# if _FFR_LOG_MORE2
4287285303Sgshapiro#  if STARTTLS
4288285303Sgshapiro	p = macvalue(macid("{verify}"), e);
4289285303Sgshapiro	if (p == NULL || *p == '\0')
4290285303Sgshapiro		p = "NONE";
4291285303Sgshapiro	(void) sm_snprintf(bp, SPACELEFT(buf, bp), ", tls_verify=%.20s", p);
4292285303Sgshapiro	bp += strlen(bp);
4293285303Sgshapiro#  endif /* STARTTLS */
4294285303Sgshapiro# endif /* _FFR_LOG_MORE2 */
4295285303Sgshapiro
429664562Sgshapiro	/* pri: changes with each delivery attempt */
429790792Sgshapiro	(void) sm_snprintf(bp, SPACELEFT(buf, bp), ", pri=%ld",
4298244833Sgshapiro		PRT_NONNEGL(e->e_msgpriority));
429964562Sgshapiro	bp += strlen(bp);
430064562Sgshapiro
430138032Speter	/* relay: max 66 bytes for IPv4 addresses */
430238032Speter	if (mci != NULL && mci->mci_host != NULL)
430338032Speter	{
430438032Speter		extern SOCKADDR CurHostAddr;
430538032Speter
430690792Sgshapiro		(void) sm_strlcpyn(bp, SPACELEFT(buf, bp), 2, ", relay=",
430790792Sgshapiro				   shortenstring(mci->mci_host, 40));
430838032Speter		bp += strlen(bp);
430938032Speter
431038032Speter		if (CurHostAddr.sa.sa_family != 0)
431138032Speter		{
431290792Sgshapiro			(void) sm_snprintf(bp, SPACELEFT(buf, bp), " [%s]",
431390792Sgshapiro					   anynet_ntoa(&CurHostAddr));
431438032Speter		}
431538032Speter	}
431690792Sgshapiro	else if (strcmp(status, "quarantined") == 0)
431790792Sgshapiro	{
431890792Sgshapiro		if (e->e_quarmsg != NULL)
431990792Sgshapiro			(void) sm_snprintf(bp, SPACELEFT(buf, bp),
432090792Sgshapiro					   ", quarantine=%s",
432190792Sgshapiro					   shortenstring(e->e_quarmsg, 40));
432290792Sgshapiro	}
432364562Sgshapiro	else if (strcmp(status, "queued") != 0)
432438032Speter	{
432538032Speter		p = macvalue('h', e);
432638032Speter		if (p != NULL && p[0] != '\0')
432738032Speter		{
432890792Sgshapiro			(void) sm_snprintf(bp, SPACELEFT(buf, bp),
432990792Sgshapiro					   ", relay=%s", shortenstring(p, 40));
433038032Speter		}
433138032Speter	}
433238032Speter	bp += strlen(bp);
433338032Speter
433464562Sgshapiro	/* dsn */
433564562Sgshapiro	if (dsn != NULL && *dsn != '\0')
433664562Sgshapiro	{
433790792Sgshapiro		(void) sm_strlcpyn(bp, SPACELEFT(buf, bp), 2, ", dsn=",
433890792Sgshapiro				   shortenstring(dsn, ENHSCLEN));
433964562Sgshapiro		bp += strlen(bp);
434064562Sgshapiro	}
434138032Speter
4342147078Sgshapiro#if _FFR_LOG_NTRIES
4343147078Sgshapiro	/* ntries */
4344147078Sgshapiro	if (e->e_ntries >= 0)
4345147078Sgshapiro	{
4346147078Sgshapiro		(void) sm_snprintf(bp, SPACELEFT(buf, bp),
4347147078Sgshapiro				   ", ntries=%d", e->e_ntries + 1);
4348147078Sgshapiro		bp += strlen(bp);
4349147078Sgshapiro	}
4350147078Sgshapiro#endif /* _FFR_LOG_NTRIES */
4351147078Sgshapiro
435264562Sgshapiro# define STATLEN		(((SYSLOG_BUFSIZE) - 100) / 4)
435364562Sgshapiro# if (STATLEN) < 63
435464562Sgshapiro#  undef STATLEN
435564562Sgshapiro#  define STATLEN	63
435664562Sgshapiro# endif /* (STATLEN) < 63 */
435764562Sgshapiro# if (STATLEN) > 203
435864562Sgshapiro#  undef STATLEN
435964562Sgshapiro#  define STATLEN	203
436064562Sgshapiro# endif /* (STATLEN) > 203 */
436164562Sgshapiro
4362285303Sgshapiro#if _FFR_LOGREPLY
4363285303Sgshapiro	/*
4364285303Sgshapiro	**  Notes:
4365285303Sgshapiro	**  per-rcpt status: to->q_rstatus
4366285303Sgshapiro	**  global status: e->e_text
4367285303Sgshapiro	**
4368285303Sgshapiro	**  We (re)use STATLEN here, is that a good choice?
4369285303Sgshapiro	**
4370285303Sgshapiro	**  stat=Deferred: ...
4371285303Sgshapiro	**  has sometimes the same text?
4372285303Sgshapiro	**
4373285303Sgshapiro	**  Note: this doesn't show the stage at which the error happened.
4374285303Sgshapiro	**  can/should we log that?
4375285303Sgshapiro	**  XS_* in reply() basically encodes the state.
4376285303Sgshapiro	*/
4377285303Sgshapiro
4378285303Sgshapiro	/* only show errors */
4379285303Sgshapiro	if (rcode != EX_OK && to != NULL && to->q_rstatus != NULL &&
4380285303Sgshapiro	    *to->q_rstatus != '\0')
4381285303Sgshapiro	{
4382285303Sgshapiro		(void) sm_snprintf(bp, SPACELEFT(buf, bp),
4383285303Sgshapiro			", reply=%s",
4384285303Sgshapiro			shortenstring(to->q_rstatus, STATLEN));
4385285303Sgshapiro		bp += strlen(bp);
4386285303Sgshapiro	}
4387285303Sgshapiro	else if (rcode != EX_OK && e->e_text != NULL)
4388285303Sgshapiro	{
4389285303Sgshapiro		(void) sm_snprintf(bp, SPACELEFT(buf, bp),
4390285303Sgshapiro			", reply=%d %s%s%s",
4391285303Sgshapiro			e->e_rcode,
4392285303Sgshapiro			e->e_renhsc,
4393285303Sgshapiro			(e->e_renhsc[0] != '\0') ? " " : "",
4394285303Sgshapiro			shortenstring(e->e_text, STATLEN));
4395285303Sgshapiro		bp += strlen(bp);
4396285303Sgshapiro	}
4397285303Sgshapiro#endif
4398285303Sgshapiro
439938032Speter	/* stat: max 210 bytes */
4400168515Sgshapiro	if ((bp - buf) > (sizeof(buf) - ((STATLEN) + 20)))
440138032Speter	{
440238032Speter		/* desperation move -- truncate data */
4403168515Sgshapiro		bp = buf + sizeof(buf) - ((STATLEN) + 17);
440490792Sgshapiro		(void) sm_strlcpy(bp, "...", SPACELEFT(buf, bp));
440538032Speter		bp += 3;
440638032Speter	}
440738032Speter
440890792Sgshapiro	(void) sm_strlcpy(bp, ", stat=", SPACELEFT(buf, bp));
440938032Speter	bp += strlen(bp);
441038032Speter
441190792Sgshapiro	(void) sm_strlcpy(bp, shortenstring(status, STATLEN),
441290792Sgshapiro			  SPACELEFT(buf, bp));
441338032Speter
441438032Speter	/* id, to: max 13 + TOBUFSIZE bytes */
441538032Speter	l = SYSLOG_BUFSIZE - 100 - strlen(buf);
441690792Sgshapiro	if (l < 0)
441790792Sgshapiro		l = 0;
441864562Sgshapiro	p = e->e_to == NULL ? "NO-TO-LIST" : e->e_to;
441990792Sgshapiro	while (strlen(p) >= l)
442038032Speter	{
442164562Sgshapiro		register char *q;
442238032Speter
442364562Sgshapiro		for (q = p + l; q > p; q--)
442464562Sgshapiro		{
4425285303Sgshapiro			/* XXX a comma in an address will break this! */
442664562Sgshapiro			if (*q == ',')
442764562Sgshapiro				break;
442864562Sgshapiro		}
442964562Sgshapiro		if (p == q)
443064562Sgshapiro			break;
443190792Sgshapiro		sm_syslog(LOG_INFO, e->e_id, "to=%.*s [more]%s",
443266494Sgshapiro			  (int) (++q - p), p, buf);
443338032Speter		p = q;
443438032Speter	}
443564562Sgshapiro	sm_syslog(LOG_INFO, e->e_id, "to=%.*s%s", l, p, buf);
443638032Speter
443764562Sgshapiro#else /* (SYSLOG_BUFSIZE) >= 256 */
443838032Speter
443938032Speter	l = SYSLOG_BUFSIZE - 85;
444090792Sgshapiro	if (l < 0)
444190792Sgshapiro		l = 0;
444264562Sgshapiro	p = e->e_to == NULL ? "NO-TO-LIST" : e->e_to;
444390792Sgshapiro	while (strlen(p) >= l)
444438032Speter	{
444564562Sgshapiro		register char *q;
444638032Speter
444764562Sgshapiro		for (q = p + l; q > p; q--)
444864562Sgshapiro		{
444964562Sgshapiro			if (*q == ',')
445064562Sgshapiro				break;
445164562Sgshapiro		}
445264562Sgshapiro		if (p == q)
445364562Sgshapiro			break;
445464562Sgshapiro
445590792Sgshapiro		sm_syslog(LOG_INFO, e->e_id, "to=%.*s [more]",
445666494Sgshapiro			  (int) (++q - p), p);
445738032Speter		p = q;
445838032Speter	}
445964562Sgshapiro	sm_syslog(LOG_INFO, e->e_id, "to=%.*s", l, p);
446038032Speter
446138032Speter	if (ctladdr != NULL)
446238032Speter	{
446338032Speter		bp = buf;
446490792Sgshapiro		(void) sm_strlcpyn(bp, SPACELEFT(buf, bp), 2, "ctladdr=",
446590792Sgshapiro				   shortenstring(ctladdr->q_paddr, 83));
446638032Speter		bp += strlen(bp);
446738032Speter		if (bitset(QGOODUID, ctladdr->q_flags))
446838032Speter		{
446990792Sgshapiro			(void) sm_snprintf(bp, SPACELEFT(buf, bp), " (%d/%d)",
447090792Sgshapiro					   ctladdr->q_uid, ctladdr->q_gid);
447138032Speter			bp += strlen(bp);
447238032Speter		}
447338032Speter		sm_syslog(LOG_INFO, e->e_id, "%s", buf);
447438032Speter	}
447538032Speter	bp = buf;
447690792Sgshapiro	(void) sm_strlcpyn(bp, SPACELEFT(buf, bp), 2, "delay=",
447790792Sgshapiro			   pintvl(now - e->e_ctime, true));
447838032Speter	bp += strlen(bp);
447938032Speter	if (xstart != (time_t) 0)
448038032Speter	{
448190792Sgshapiro		(void) sm_strlcpyn(bp, SPACELEFT(buf, bp), 2, ", xdelay=",
448290792Sgshapiro				   pintvl(now - xstart, true));
448338032Speter		bp += strlen(bp);
448438032Speter	}
448538032Speter
448638032Speter	if (m != NULL)
448738032Speter	{
448890792Sgshapiro		(void) sm_strlcpyn(bp, SPACELEFT(buf, bp), 2, ", mailer=",
448990792Sgshapiro				   m->m_name);
449038032Speter		bp += strlen(bp);
449138032Speter	}
449238032Speter	sm_syslog(LOG_INFO, e->e_id, "%.1000s", buf);
449338032Speter
449438032Speter	buf[0] = '\0';
449538032Speter	bp = buf;
449638032Speter	if (mci != NULL && mci->mci_host != NULL)
449738032Speter	{
449838032Speter		extern SOCKADDR CurHostAddr;
449938032Speter
450090792Sgshapiro		(void) sm_snprintf(bp, SPACELEFT(buf, bp), "relay=%.100s",
450190792Sgshapiro				   mci->mci_host);
450238032Speter		bp += strlen(bp);
450338032Speter
450438032Speter		if (CurHostAddr.sa.sa_family != 0)
450590792Sgshapiro			(void) sm_snprintf(bp, SPACELEFT(buf, bp),
450690792Sgshapiro					   " [%.100s]",
450790792Sgshapiro					   anynet_ntoa(&CurHostAddr));
450838032Speter	}
450990792Sgshapiro	else if (strcmp(status, "quarantined") == 0)
451090792Sgshapiro	{
451190792Sgshapiro		if (e->e_quarmsg != NULL)
451290792Sgshapiro			(void) sm_snprintf(bp, SPACELEFT(buf, bp),
451390792Sgshapiro					   ", quarantine=%.100s",
451490792Sgshapiro					   e->e_quarmsg);
451590792Sgshapiro	}
451664562Sgshapiro	else if (strcmp(status, "queued") != 0)
451738032Speter	{
451838032Speter		p = macvalue('h', e);
451938032Speter		if (p != NULL && p[0] != '\0')
4520168515Sgshapiro			(void) sm_snprintf(buf, sizeof(buf), "relay=%.100s", p);
452138032Speter	}
452238032Speter	if (buf[0] != '\0')
452338032Speter		sm_syslog(LOG_INFO, e->e_id, "%.1000s", buf);
452438032Speter
452564562Sgshapiro	sm_syslog(LOG_INFO, e->e_id, "stat=%s", shortenstring(status, 63));
452664562Sgshapiro#endif /* (SYSLOG_BUFSIZE) >= 256 */
452738032Speter}
452890792Sgshapiro/*
452938032Speter**  PUTFROMLINE -- output a UNIX-style from line (or whatever)
453038032Speter**
453138032Speter**	This can be made an arbitrary message separator by changing $l
453238032Speter**
453338032Speter**	One of the ugliest hacks seen by human eyes is contained herein:
453438032Speter**	UUCP wants those stupid "remote from <host>" lines.  Why oh why
453538032Speter**	does a well-meaning programmer such as myself have to deal with
453638032Speter**	this kind of antique garbage????
453738032Speter**
453838032Speter**	Parameters:
453938032Speter**		mci -- the connection information.
454038032Speter**		e -- the envelope.
454138032Speter**
454238032Speter**	Returns:
4543157001Sgshapiro**		true iff line was written successfully
454438032Speter**
454538032Speter**	Side Effects:
454638032Speter**		outputs some text to fp.
454738032Speter*/
454838032Speter
4549157001Sgshapirobool
455038032Speterputfromline(mci, e)
455138032Speter	register MCI *mci;
455238032Speter	ENVELOPE *e;
455338032Speter{
455438032Speter	char *template = UnixFromLine;
455538032Speter	char buf[MAXLINE];
455638032Speter	char xbuf[MAXLINE];
455738032Speter
455838032Speter	if (bitnset(M_NHDR, mci->mci_mailer->m_flags))
4559157001Sgshapiro		return true;
456038032Speter
456138032Speter	mci->mci_flags |= MCIF_INHEADER;
456238032Speter
456338032Speter	if (bitnset(M_UGLYUUCP, mci->mci_mailer->m_flags))
456438032Speter	{
456538032Speter		char *bang;
456638032Speter
4567168515Sgshapiro		expand("\201g", buf, sizeof(buf), e);
456838032Speter		bang = strchr(buf, '!');
456938032Speter		if (bang == NULL)
457038032Speter		{
457138032Speter			char *at;
457238032Speter			char hname[MAXNAME];
457338032Speter
457464562Sgshapiro			/*
457542575Speter			**  If we can construct a UUCP path, do so
457638032Speter			*/
457738032Speter
457838032Speter			at = strrchr(buf, '@');
457938032Speter			if (at == NULL)
458038032Speter			{
4581168515Sgshapiro				expand("\201k", hname, sizeof(hname), e);
458238032Speter				at = hname;
458338032Speter			}
458438032Speter			else
458538032Speter				*at++ = '\0';
4586168515Sgshapiro			(void) sm_snprintf(xbuf, sizeof(xbuf),
458790792Sgshapiro					   "From %.800s  \201d remote from %.100s\n",
458890792Sgshapiro					   buf, at);
458938032Speter		}
459038032Speter		else
459138032Speter		{
459238032Speter			*bang++ = '\0';
4593168515Sgshapiro			(void) sm_snprintf(xbuf, sizeof(xbuf),
459490792Sgshapiro					   "From %.800s  \201d remote from %.100s\n",
459590792Sgshapiro					   bang, buf);
459638032Speter			template = xbuf;
459738032Speter		}
459838032Speter	}
4599168515Sgshapiro	expand(template, buf, sizeof(buf), e);
4600157001Sgshapiro	return putxline(buf, strlen(buf), mci, PXLF_HEADER);
460138032Speter}
4602157001Sgshapiro
460390792Sgshapiro/*
460438032Speter**  PUTBODY -- put the body of a message.
460538032Speter**
460638032Speter**	Parameters:
460738032Speter**		mci -- the connection information.
460838032Speter**		e -- the envelope to put out.
460938032Speter**		separator -- if non-NULL, a message separator that must
461038032Speter**			not be permitted in the resulting message.
461138032Speter**
461238032Speter**	Returns:
4613157001Sgshapiro**		true iff message was written successfully
461438032Speter**
461538032Speter**	Side Effects:
461638032Speter**		The message is written onto fp.
461738032Speter*/
461838032Speter
461938032Speter/* values for output state variable */
4620157001Sgshapiro#define OSTATE_HEAD	0	/* at beginning of line */
4621157001Sgshapiro#define OSTATE_CR	1	/* read a carriage return */
4622157001Sgshapiro#define OSTATE_INLINE	2	/* putting rest of line */
462338032Speter
4624157001Sgshapirobool
462538032Speterputbody(mci, e, separator)
462638032Speter	register MCI *mci;
462738032Speter	register ENVELOPE *e;
462838032Speter	char *separator;
462938032Speter{
463090792Sgshapiro	bool dead = false;
4631157001Sgshapiro	bool ioerr = false;
4632157001Sgshapiro	int save_errno;
463338032Speter	char buf[MAXLINE];
463490792Sgshapiro#if MIME8TO7
463542575Speter	char *boundaries[MAXMIMENESTING + 1];
463690792Sgshapiro#endif /* MIME8TO7 */
463738032Speter
463838032Speter	/*
463938032Speter	**  Output the body of the message
464038032Speter	*/
464138032Speter
464238032Speter	if (e->e_dfp == NULL && bitset(EF_HAS_DF, e->e_flags))
464338032Speter	{
464490792Sgshapiro		char *df = queuename(e, DATAFL_LETTER);
464538032Speter
464690792Sgshapiro		e->e_dfp = sm_io_open(SmFtStdio, SM_TIME_DEFAULT, df,
4647120256Sgshapiro				      SM_IO_RDONLY_B, NULL);
464838032Speter		if (e->e_dfp == NULL)
464964562Sgshapiro		{
465064562Sgshapiro			char *msg = "!putbody: Cannot open %s for %s from %s";
465164562Sgshapiro
465264562Sgshapiro			if (errno == ENOENT)
465364562Sgshapiro				msg++;
465464562Sgshapiro			syserr(msg, df, e->e_to, e->e_from.q_paddr);
465564562Sgshapiro		}
465690792Sgshapiro
465738032Speter	}
465838032Speter	if (e->e_dfp == NULL)
465938032Speter	{
466038032Speter		if (bitset(MCIF_INHEADER, mci->mci_flags))
466138032Speter		{
4662157001Sgshapiro			if (!putline("", mci))
4663157001Sgshapiro				goto writeerr;
466438032Speter			mci->mci_flags &= ~MCIF_INHEADER;
466538032Speter		}
4666157001Sgshapiro		if (!putline("<<< No Message Collected >>>", mci))
4667157001Sgshapiro			goto writeerr;
466838032Speter		goto endofmessage;
466938032Speter	}
467064562Sgshapiro
467138032Speter	if (e->e_dfino == (ino_t) 0)
467238032Speter	{
467338032Speter		struct stat stbuf;
467438032Speter
467590792Sgshapiro		if (fstat(sm_io_getinfo(e->e_dfp, SM_IO_WHAT_FD, NULL), &stbuf)
467690792Sgshapiro		    < 0)
467738032Speter			e->e_dfino = -1;
467838032Speter		else
467938032Speter		{
468038032Speter			e->e_dfdev = stbuf.st_dev;
468138032Speter			e->e_dfino = stbuf.st_ino;
468238032Speter		}
468338032Speter	}
468438032Speter
468590792Sgshapiro	/* paranoia: the data file should always be in a rewound state */
468664562Sgshapiro	(void) bfrewind(e->e_dfp);
468764562Sgshapiro
4688157001Sgshapiro	/* simulate an I/O timeout when used as source */
4689157001Sgshapiro	if (tTd(84, 101))
4690157001Sgshapiro		sleep(319);
4691157001Sgshapiro
469238032Speter#if MIME8TO7
469338032Speter	if (bitset(MCIF_CVT8TO7, mci->mci_flags))
469438032Speter	{
469538032Speter		/*
469638032Speter		**  Do 8 to 7 bit MIME conversion.
469738032Speter		*/
469838032Speter
469938032Speter		/* make sure it looks like a MIME message */
4700157001Sgshapiro		if (hvalue("MIME-Version", e->e_header) == NULL &&
4701157001Sgshapiro		    !putline("MIME-Version: 1.0", mci))
4702157001Sgshapiro			goto writeerr;
470338032Speter
470438032Speter		if (hvalue("Content-Type", e->e_header) == NULL)
470538032Speter		{
4706168515Sgshapiro			(void) sm_snprintf(buf, sizeof(buf),
470790792Sgshapiro					   "Content-Type: text/plain; charset=%s",
470890792Sgshapiro					   defcharset(e));
4709157001Sgshapiro			if (!putline(buf, mci))
4710157001Sgshapiro				goto writeerr;
471138032Speter		}
471238032Speter
471338032Speter		/* now do the hard work */
471438032Speter		boundaries[0] = NULL;
471538032Speter		mci->mci_flags |= MCIF_INHEADER;
4716159609Sgshapiro		if (mime8to7(mci, e->e_header, e, boundaries, M87F_OUTER, 0) ==
4717157001Sgshapiro								SM_IO_EOF)
4718157001Sgshapiro			goto writeerr;
471938032Speter	}
472038032Speter# if MIME7TO8
472138032Speter	else if (bitset(MCIF_CVT7TO8, mci->mci_flags))
472238032Speter	{
4723157001Sgshapiro		if (!mime7to8(mci, e->e_header, e))
4724157001Sgshapiro			goto writeerr;
472538032Speter	}
472664562Sgshapiro# endif /* MIME7TO8 */
472742575Speter	else if (MaxMimeHeaderLength > 0 || MaxMimeFieldLength > 0)
472842575Speter	{
472964562Sgshapiro		bool oldsuprerrs = SuprErrs;
473064562Sgshapiro
473142575Speter		/* Use mime8to7 to check multipart for MIME header overflows */
473242575Speter		boundaries[0] = NULL;
473342575Speter		mci->mci_flags |= MCIF_INHEADER;
473464562Sgshapiro
473564562Sgshapiro		/*
473664562Sgshapiro		**  If EF_DONT_MIME is set, we have a broken MIME message
473764562Sgshapiro		**  and don't want to generate a new bounce message whose
473864562Sgshapiro		**  body propagates the broken MIME.  We can't just not call
473964562Sgshapiro		**  mime8to7() as is done above since we need the security
474064562Sgshapiro		**  checks.  The best we can do is suppress the errors.
474164562Sgshapiro		*/
474264562Sgshapiro
474364562Sgshapiro		if (bitset(EF_DONT_MIME, e->e_flags))
474490792Sgshapiro			SuprErrs = true;
474564562Sgshapiro
4746157001Sgshapiro		if (mime8to7(mci, e->e_header, e, boundaries,
4747159609Sgshapiro				M87F_OUTER|M87F_NO8TO7, 0) == SM_IO_EOF)
4748157001Sgshapiro			goto writeerr;
474964562Sgshapiro
475064562Sgshapiro		/* restore SuprErrs */
475164562Sgshapiro		SuprErrs = oldsuprerrs;
475242575Speter	}
475338032Speter	else
475464562Sgshapiro#endif /* MIME8TO7 */
475538032Speter	{
475638032Speter		int ostate;
475738032Speter		register char *bp;
475838032Speter		register char *pbp;
475938032Speter		register int c;
476038032Speter		register char *xp;
476138032Speter		int padc;
476238032Speter		char *buflim;
476338032Speter		int pos = 0;
476464562Sgshapiro		char peekbuf[12];
476538032Speter
476638032Speter		if (bitset(MCIF_INHEADER, mci->mci_flags))
476738032Speter		{
4768157001Sgshapiro			if (!putline("", mci))
4769157001Sgshapiro				goto writeerr;
477038032Speter			mci->mci_flags &= ~MCIF_INHEADER;
477138032Speter		}
477238032Speter
477338032Speter		/* determine end of buffer; allow for short mailer lines */
4774168515Sgshapiro		buflim = &buf[sizeof(buf) - 1];
477538032Speter		if (mci->mci_mailer->m_linelimit > 0 &&
4776168515Sgshapiro		    mci->mci_mailer->m_linelimit < sizeof(buf) - 1)
477738032Speter			buflim = &buf[mci->mci_mailer->m_linelimit - 1];
477838032Speter
477938032Speter		/* copy temp file to output with mapping */
4780157001Sgshapiro		ostate = OSTATE_HEAD;
478138032Speter		bp = buf;
478238032Speter		pbp = peekbuf;
478390792Sgshapiro		while (!sm_io_error(mci->mci_out) && !dead)
478438032Speter		{
478538032Speter			if (pbp > peekbuf)
478638032Speter				c = *--pbp;
478790792Sgshapiro			else if ((c = sm_io_getc(e->e_dfp, SM_TIME_DEFAULT))
478890792Sgshapiro				 == SM_IO_EOF)
478938032Speter				break;
479038032Speter			if (bitset(MCIF_7BIT, mci->mci_flags))
479138032Speter				c &= 0x7f;
479238032Speter			switch (ostate)
479338032Speter			{
4794157001Sgshapiro			  case OSTATE_HEAD:
479538032Speter				if (c == '\0' &&
479690792Sgshapiro				    bitnset(M_NONULLS,
479790792Sgshapiro					    mci->mci_mailer->m_flags))
479838032Speter					break;
479938032Speter				if (c != '\r' && c != '\n' && bp < buflim)
480038032Speter				{
480138032Speter					*bp++ = c;
480238032Speter					break;
480338032Speter				}
480438032Speter
480538032Speter				/* check beginning of line for special cases */
480638032Speter				*bp = '\0';
480738032Speter				pos = 0;
480890792Sgshapiro				padc = SM_IO_EOF;
480938032Speter				if (buf[0] == 'F' &&
481090792Sgshapiro				    bitnset(M_ESCFROM, mci->mci_mailer->m_flags)
481190792Sgshapiro				    && strncmp(buf, "From ", 5) == 0)
481238032Speter				{
481338032Speter					padc = '>';
481438032Speter				}
481538032Speter				if (buf[0] == '-' && buf[1] == '-' &&
481638032Speter				    separator != NULL)
481738032Speter				{
481838032Speter					/* possible separator */
481938032Speter					int sl = strlen(separator);
482038032Speter
482190792Sgshapiro					if (strncmp(&buf[2], separator, sl)
482290792Sgshapiro					    == 0)
482338032Speter						padc = ' ';
482438032Speter				}
482538032Speter				if (buf[0] == '.' &&
482638032Speter				    bitnset(M_XDOT, mci->mci_mailer->m_flags))
482738032Speter				{
482838032Speter					padc = '.';
482938032Speter				}
483038032Speter
483138032Speter				/* now copy out saved line */
483238032Speter				if (TrafficLogFile != NULL)
483338032Speter				{
483490792Sgshapiro					(void) sm_io_fprintf(TrafficLogFile,
483590792Sgshapiro							     SM_TIME_DEFAULT,
483690792Sgshapiro							     "%05d >>> ",
483790792Sgshapiro							     (int) CurrentPid);
483890792Sgshapiro					if (padc != SM_IO_EOF)
483990792Sgshapiro						(void) sm_io_putc(TrafficLogFile,
484090792Sgshapiro								  SM_TIME_DEFAULT,
484190792Sgshapiro								  padc);
484238032Speter					for (xp = buf; xp < bp; xp++)
484390792Sgshapiro						(void) sm_io_putc(TrafficLogFile,
484490792Sgshapiro								  SM_TIME_DEFAULT,
484590792Sgshapiro								  (unsigned char) *xp);
484638032Speter					if (c == '\n')
484790792Sgshapiro						(void) sm_io_fputs(TrafficLogFile,
484890792Sgshapiro								   SM_TIME_DEFAULT,
484990792Sgshapiro								   mci->mci_mailer->m_eol);
485038032Speter				}
485190792Sgshapiro				if (padc != SM_IO_EOF)
485238032Speter				{
485390792Sgshapiro					if (sm_io_putc(mci->mci_out,
485490792Sgshapiro						       SM_TIME_DEFAULT, padc)
485590792Sgshapiro					    == SM_IO_EOF)
485664562Sgshapiro					{
485790792Sgshapiro						dead = true;
485864562Sgshapiro						continue;
485964562Sgshapiro					}
486038032Speter					pos++;
486138032Speter				}
486238032Speter				for (xp = buf; xp < bp; xp++)
486338032Speter				{
486490792Sgshapiro					if (sm_io_putc(mci->mci_out,
486590792Sgshapiro						       SM_TIME_DEFAULT,
486690792Sgshapiro						       (unsigned char) *xp)
486790792Sgshapiro					    == SM_IO_EOF)
486864562Sgshapiro					{
486990792Sgshapiro						dead = true;
487064562Sgshapiro						break;
487164562Sgshapiro					}
487238032Speter				}
487364562Sgshapiro				if (dead)
487464562Sgshapiro					continue;
487538032Speter				if (c == '\n')
487638032Speter				{
487790792Sgshapiro					if (sm_io_fputs(mci->mci_out,
487890792Sgshapiro							SM_TIME_DEFAULT,
487990792Sgshapiro							mci->mci_mailer->m_eol)
488090792Sgshapiro							== SM_IO_EOF)
488164562Sgshapiro						break;
488238032Speter					pos = 0;
488338032Speter				}
488438032Speter				else
488538032Speter				{
488638032Speter					pos += bp - buf;
488738032Speter					if (c != '\r')
4888112810Sgshapiro					{
4889112810Sgshapiro						SM_ASSERT(pbp < peekbuf +
4890112810Sgshapiro								sizeof(peekbuf));
489138032Speter						*pbp++ = c;
4892112810Sgshapiro					}
489338032Speter				}
489464562Sgshapiro
489538032Speter				bp = buf;
489638032Speter
489738032Speter				/* determine next state */
489838032Speter				if (c == '\n')
4899157001Sgshapiro					ostate = OSTATE_HEAD;
490038032Speter				else if (c == '\r')
4901157001Sgshapiro					ostate = OSTATE_CR;
490238032Speter				else
4903157001Sgshapiro					ostate = OSTATE_INLINE;
490438032Speter				continue;
490538032Speter
4906157001Sgshapiro			  case OSTATE_CR:
490738032Speter				if (c == '\n')
490838032Speter				{
490938032Speter					/* got CRLF */
491090792Sgshapiro					if (sm_io_fputs(mci->mci_out,
491190792Sgshapiro							SM_TIME_DEFAULT,
491290792Sgshapiro							mci->mci_mailer->m_eol)
491390792Sgshapiro							== SM_IO_EOF)
491464562Sgshapiro						continue;
491564562Sgshapiro
491638032Speter					if (TrafficLogFile != NULL)
491738032Speter					{
491890792Sgshapiro						(void) sm_io_fputs(TrafficLogFile,
491990792Sgshapiro								   SM_TIME_DEFAULT,
492090792Sgshapiro								   mci->mci_mailer->m_eol);
492138032Speter					}
4922168515Sgshapiro					pos = 0;
4923157001Sgshapiro					ostate = OSTATE_HEAD;
492438032Speter					continue;
492538032Speter				}
492638032Speter
492738032Speter				/* had a naked carriage return */
4928112810Sgshapiro				SM_ASSERT(pbp < peekbuf + sizeof(peekbuf));
492938032Speter				*pbp++ = c;
493038032Speter				c = '\r';
4931157001Sgshapiro				ostate = OSTATE_INLINE;
493238032Speter				goto putch;
493338032Speter
4934157001Sgshapiro			  case OSTATE_INLINE:
493538032Speter				if (c == '\r')
493638032Speter				{
4937157001Sgshapiro					ostate = OSTATE_CR;
493838032Speter					continue;
493938032Speter				}
494038032Speter				if (c == '\0' &&
494190792Sgshapiro				    bitnset(M_NONULLS,
494290792Sgshapiro					    mci->mci_mailer->m_flags))
494338032Speter					break;
494438032Speterputch:
494538032Speter				if (mci->mci_mailer->m_linelimit > 0 &&
494664562Sgshapiro				    pos >= mci->mci_mailer->m_linelimit - 1 &&
494738032Speter				    c != '\n')
494838032Speter				{
494964562Sgshapiro					int d;
495064562Sgshapiro
495164562Sgshapiro					/* check next character for EOL */
495264562Sgshapiro					if (pbp > peekbuf)
495364562Sgshapiro						d = *(pbp - 1);
495490792Sgshapiro					else if ((d = sm_io_getc(e->e_dfp,
495590792Sgshapiro								 SM_TIME_DEFAULT))
495690792Sgshapiro						 != SM_IO_EOF)
4957112810Sgshapiro					{
4958112810Sgshapiro						SM_ASSERT(pbp < peekbuf +
4959112810Sgshapiro								sizeof(peekbuf));
496064562Sgshapiro						*pbp++ = d;
4961112810Sgshapiro					}
496264562Sgshapiro
496390792Sgshapiro					if (d == '\n' || d == SM_IO_EOF)
496464562Sgshapiro					{
496564562Sgshapiro						if (TrafficLogFile != NULL)
496690792Sgshapiro							(void) sm_io_putc(TrafficLogFile,
496790792Sgshapiro									  SM_TIME_DEFAULT,
496890792Sgshapiro									  (unsigned char) c);
496990792Sgshapiro						if (sm_io_putc(mci->mci_out,
497090792Sgshapiro							       SM_TIME_DEFAULT,
497190792Sgshapiro							       (unsigned char) c)
497290792Sgshapiro							       == SM_IO_EOF)
497364562Sgshapiro						{
497490792Sgshapiro							dead = true;
497564562Sgshapiro							continue;
497664562Sgshapiro						}
497764562Sgshapiro						pos++;
497864562Sgshapiro						continue;
497964562Sgshapiro					}
498064562Sgshapiro
498190792Sgshapiro					if (sm_io_putc(mci->mci_out,
498290792Sgshapiro						       SM_TIME_DEFAULT, '!')
498390792Sgshapiro					    == SM_IO_EOF ||
498490792Sgshapiro					    sm_io_fputs(mci->mci_out,
498590792Sgshapiro							SM_TIME_DEFAULT,
498690792Sgshapiro							mci->mci_mailer->m_eol)
498790792Sgshapiro					    == SM_IO_EOF)
498864562Sgshapiro					{
498990792Sgshapiro						dead = true;
499064562Sgshapiro						continue;
499164562Sgshapiro					}
499264562Sgshapiro
499338032Speter					if (TrafficLogFile != NULL)
499438032Speter					{
499590792Sgshapiro						(void) sm_io_fprintf(TrafficLogFile,
499690792Sgshapiro								     SM_TIME_DEFAULT,
499790792Sgshapiro								     "!%s",
499890792Sgshapiro								     mci->mci_mailer->m_eol);
499938032Speter					}
5000157001Sgshapiro					ostate = OSTATE_HEAD;
5001112810Sgshapiro					SM_ASSERT(pbp < peekbuf +
5002112810Sgshapiro							sizeof(peekbuf));
500338032Speter					*pbp++ = c;
500438032Speter					continue;
500538032Speter				}
500638032Speter				if (c == '\n')
500738032Speter				{
500838032Speter					if (TrafficLogFile != NULL)
500990792Sgshapiro						(void) sm_io_fputs(TrafficLogFile,
501090792Sgshapiro								   SM_TIME_DEFAULT,
501190792Sgshapiro								   mci->mci_mailer->m_eol);
501290792Sgshapiro					if (sm_io_fputs(mci->mci_out,
501390792Sgshapiro							SM_TIME_DEFAULT,
501490792Sgshapiro							mci->mci_mailer->m_eol)
501590792Sgshapiro							== SM_IO_EOF)
501664562Sgshapiro						continue;
501738032Speter					pos = 0;
5018157001Sgshapiro					ostate = OSTATE_HEAD;
501938032Speter				}
502038032Speter				else
502138032Speter				{
502238032Speter					if (TrafficLogFile != NULL)
502390792Sgshapiro						(void) sm_io_putc(TrafficLogFile,
502490792Sgshapiro								  SM_TIME_DEFAULT,
502590792Sgshapiro								  (unsigned char) c);
502690792Sgshapiro					if (sm_io_putc(mci->mci_out,
502790792Sgshapiro						       SM_TIME_DEFAULT,
502890792Sgshapiro						       (unsigned char) c)
502990792Sgshapiro					    == SM_IO_EOF)
503064562Sgshapiro					{
503190792Sgshapiro						dead = true;
503264562Sgshapiro						continue;
503364562Sgshapiro					}
503438032Speter					pos++;
5035157001Sgshapiro					ostate = OSTATE_INLINE;
503638032Speter				}
503738032Speter				break;
503838032Speter			}
503938032Speter		}
504038032Speter
504138032Speter		/* make sure we are at the beginning of a line */
504238032Speter		if (bp > buf)
504338032Speter		{
504438032Speter			if (TrafficLogFile != NULL)
504538032Speter			{
504638032Speter				for (xp = buf; xp < bp; xp++)
504790792Sgshapiro					(void) sm_io_putc(TrafficLogFile,
504890792Sgshapiro							  SM_TIME_DEFAULT,
504990792Sgshapiro							  (unsigned char) *xp);
505038032Speter			}
505138032Speter			for (xp = buf; xp < bp; xp++)
505238032Speter			{
505390792Sgshapiro				if (sm_io_putc(mci->mci_out, SM_TIME_DEFAULT,
505490792Sgshapiro					       (unsigned char) *xp)
505590792Sgshapiro				    == SM_IO_EOF)
505664562Sgshapiro				{
505790792Sgshapiro					dead = true;
505864562Sgshapiro					break;
505964562Sgshapiro				}
506038032Speter			}
506138032Speter			pos += bp - buf;
506238032Speter		}
506364562Sgshapiro		if (!dead && pos > 0)
506438032Speter		{
506538032Speter			if (TrafficLogFile != NULL)
506690792Sgshapiro				(void) sm_io_fputs(TrafficLogFile,
506790792Sgshapiro						   SM_TIME_DEFAULT,
506890792Sgshapiro						   mci->mci_mailer->m_eol);
5069157001Sgshapiro			if (sm_io_fputs(mci->mci_out, SM_TIME_DEFAULT,
5070157001Sgshapiro					   mci->mci_mailer->m_eol) == SM_IO_EOF)
5071157001Sgshapiro				goto writeerr;
507238032Speter		}
507338032Speter	}
507438032Speter
507590792Sgshapiro	if (sm_io_error(e->e_dfp))
507638032Speter	{
507790792Sgshapiro		syserr("putbody: %s/%cf%s: read error",
507890792Sgshapiro		       qid_printqueue(e->e_dfqgrp, e->e_dfqdir),
507990792Sgshapiro		       DATAFL_LETTER, e->e_id);
508038032Speter		ExitStat = EX_IOERR;
5081157001Sgshapiro		ioerr = true;
508238032Speter	}
508338032Speter
508438032Speterendofmessage:
508564562Sgshapiro	/*
508664562Sgshapiro	**  Since mailfile() uses e_dfp in a child process,
508764562Sgshapiro	**  the file offset in the stdio library for the
508864562Sgshapiro	**  parent process will not agree with the in-kernel
508964562Sgshapiro	**  file offset since the file descriptor is shared
509064562Sgshapiro	**  between the processes.  Therefore, it is vital
509164562Sgshapiro	**  that the file always be rewound.  This forces the
509264562Sgshapiro	**  kernel offset (lseek) and stdio library (ftell)
509364562Sgshapiro	**  offset to match.
509464562Sgshapiro	*/
509564562Sgshapiro
5096157001Sgshapiro	save_errno = errno;
509764562Sgshapiro	if (e->e_dfp != NULL)
509864562Sgshapiro		(void) bfrewind(e->e_dfp);
509964562Sgshapiro
510038032Speter	/* some mailers want extra blank line at end of message */
510164562Sgshapiro	if (!dead && bitnset(M_BLANKEND, mci->mci_mailer->m_flags) &&
510238032Speter	    buf[0] != '\0' && buf[0] != '\n')
5103157001Sgshapiro	{
5104157001Sgshapiro		if (!putline("", mci))
5105157001Sgshapiro			goto writeerr;
5106157001Sgshapiro	}
510738032Speter
5108157001Sgshapiro	if (!dead &&
5109157001Sgshapiro	    (sm_io_flush(mci->mci_out, SM_TIME_DEFAULT) == SM_IO_EOF ||
5110157001Sgshapiro	     (sm_io_error(mci->mci_out) && errno != EPIPE)))
511138032Speter	{
5112157001Sgshapiro		save_errno = errno;
511338032Speter		syserr("putbody: write error");
511438032Speter		ExitStat = EX_IOERR;
5115157001Sgshapiro		ioerr = true;
511638032Speter	}
511764562Sgshapiro
5118157001Sgshapiro	errno = save_errno;
5119157001Sgshapiro	return !dead && !ioerr;
5120157001Sgshapiro
5121157001Sgshapiro  writeerr:
5122157001Sgshapiro	return false;
512338032Speter}
5124157001Sgshapiro
512590792Sgshapiro/*
512638032Speter**  MAILFILE -- Send a message to a file.
512738032Speter**
512890792Sgshapiro**	If the file has the set-user-ID/set-group-ID bits set, but NO
512990792Sgshapiro**	execute bits, sendmail will try to become the owner of that file
513038032Speter**	rather than the real user.  Obviously, this only works if
513138032Speter**	sendmail runs as root.
513238032Speter**
513338032Speter**	This could be done as a subordinate mailer, except that it
513438032Speter**	is used implicitly to save messages in ~/dead.letter.  We
513538032Speter**	view this as being sufficiently important as to include it
513638032Speter**	here.  For example, if the system is dying, we shouldn't have
513738032Speter**	to create another process plus some pipes to save the message.
513838032Speter**
513938032Speter**	Parameters:
514038032Speter**		filename -- the name of the file to send to.
514138032Speter**		mailer -- mailer definition for recipient -- if NULL,
514238032Speter**			use FileMailer.
514338032Speter**		ctladdr -- the controlling address header -- includes
514438032Speter**			the userid/groupid to be when sending.
514538032Speter**		sfflags -- flags for opening.
514638032Speter**		e -- the current envelope.
514738032Speter**
514838032Speter**	Returns:
514938032Speter**		The exit code associated with the operation.
515038032Speter**
515138032Speter**	Side Effects:
515238032Speter**		none.
515338032Speter*/
515438032Speter
515590792Sgshapiro# define RETURN(st)			exit(st);
515690792Sgshapiro
515738032Speterstatic jmp_buf	CtxMailfileTimeout;
515838032Speter
515938032Speterint
516038032Spetermailfile(filename, mailer, ctladdr, sfflags, e)
516138032Speter	char *volatile filename;
516238032Speter	MAILER *volatile mailer;
516338032Speter	ADDRESS *ctladdr;
516464562Sgshapiro	volatile long sfflags;
516538032Speter	register ENVELOPE *e;
516638032Speter{
516790792Sgshapiro	register SM_FILE_T *f;
516838032Speter	register pid_t pid = -1;
516964562Sgshapiro	volatile int mode;
517064562Sgshapiro	int len;
517164562Sgshapiro	off_t curoff;
517238032Speter	bool suidwarn = geteuid() == 0;
517338032Speter	char *p;
517464562Sgshapiro	char *volatile realfile;
517590792Sgshapiro	SM_EVENT *ev;
517698121Sgshapiro	char buf[MAXPATHLEN];
517798121Sgshapiro	char targetfile[MAXPATHLEN];
517838032Speter
517938032Speter	if (tTd(11, 1))
518038032Speter	{
518190792Sgshapiro		sm_dprintf("mailfile %s\n  ctladdr=", filename);
5182132943Sgshapiro		printaddr(sm_debug_file(), ctladdr, false);
518338032Speter	}
518438032Speter
518538032Speter	if (mailer == NULL)
518638032Speter		mailer = FileMailer;
518738032Speter
518838032Speter	if (e->e_xfp != NULL)
518990792Sgshapiro		(void) sm_io_flush(e->e_xfp, SM_TIME_DEFAULT);
519038032Speter
519138032Speter	/*
519238032Speter	**  Special case /dev/null.  This allows us to restrict file
519338032Speter	**  delivery to regular files only.
519438032Speter	*/
519538032Speter
519690792Sgshapiro	if (sm_path_isdevnull(filename))
519738032Speter		return EX_OK;
519838032Speter
519938032Speter	/* check for 8-bit available */
520038032Speter	if (bitset(EF_HAS8BIT, e->e_flags) &&
520138032Speter	    bitnset(M_7BITS, mailer->m_flags) &&
520238032Speter	    (bitset(EF_DONT_MIME, e->e_flags) ||
520338032Speter	     !(bitset(MM_MIME8BIT, MimeMode) ||
520438032Speter	       (bitset(EF_IS_MIME, e->e_flags) &&
520538032Speter		bitset(MM_CVTMIME, MimeMode)))))
520638032Speter	{
520738032Speter		e->e_status = "5.6.3";
520864562Sgshapiro		usrerrenh(e->e_status,
520990792Sgshapiro			  "554 Cannot send 8-bit data to 7-bit destination");
521090792Sgshapiro		errno = 0;
521164562Sgshapiro		return EX_DATAERR;
521238032Speter	}
521338032Speter
521464562Sgshapiro	/* Find the actual file */
521564562Sgshapiro	if (SafeFileEnv != NULL && SafeFileEnv[0] != '\0')
521664562Sgshapiro	{
521764562Sgshapiro		len = strlen(SafeFileEnv);
521864562Sgshapiro
521964562Sgshapiro		if (strncmp(SafeFileEnv, filename, len) == 0)
522064562Sgshapiro			filename += len;
522164562Sgshapiro
5222168515Sgshapiro		if (len + strlen(filename) + 1 >= sizeof(targetfile))
522364562Sgshapiro		{
522464562Sgshapiro			syserr("mailfile: filename too long (%s/%s)",
522564562Sgshapiro			       SafeFileEnv, filename);
522664562Sgshapiro			return EX_CANTCREAT;
522764562Sgshapiro		}
5228168515Sgshapiro		(void) sm_strlcpy(targetfile, SafeFileEnv, sizeof(targetfile));
522964562Sgshapiro		realfile = targetfile + len;
523064562Sgshapiro		if (*filename == '/')
523164562Sgshapiro			filename++;
523294334Sgshapiro		if (*filename != '\0')
523394334Sgshapiro		{
523494334Sgshapiro			/* paranoia: trailing / should be removed in readcf */
523594334Sgshapiro			if (targetfile[len - 1] != '/')
523694334Sgshapiro				(void) sm_strlcat(targetfile,
5237168515Sgshapiro						  "/", sizeof(targetfile));
523894334Sgshapiro			(void) sm_strlcat(targetfile, filename,
5239168515Sgshapiro					  sizeof(targetfile));
524094334Sgshapiro		}
524164562Sgshapiro	}
524264562Sgshapiro	else if (mailer->m_rootdir != NULL)
524364562Sgshapiro	{
5244168515Sgshapiro		expand(mailer->m_rootdir, targetfile, sizeof(targetfile), e);
524564562Sgshapiro		len = strlen(targetfile);
524664562Sgshapiro
524764562Sgshapiro		if (strncmp(targetfile, filename, len) == 0)
524864562Sgshapiro			filename += len;
524964562Sgshapiro
5250168515Sgshapiro		if (len + strlen(filename) + 1 >= sizeof(targetfile))
525164562Sgshapiro		{
525264562Sgshapiro			syserr("mailfile: filename too long (%s/%s)",
525364562Sgshapiro			       targetfile, filename);
525464562Sgshapiro			return EX_CANTCREAT;
525564562Sgshapiro		}
525664562Sgshapiro		realfile = targetfile + len;
525764562Sgshapiro		if (targetfile[len - 1] != '/')
5258168515Sgshapiro			(void) sm_strlcat(targetfile, "/", sizeof(targetfile));
525964562Sgshapiro		if (*filename == '/')
526090792Sgshapiro			(void) sm_strlcat(targetfile, filename + 1,
5261168515Sgshapiro					  sizeof(targetfile));
526264562Sgshapiro		else
526390792Sgshapiro			(void) sm_strlcat(targetfile, filename,
5264168515Sgshapiro					  sizeof(targetfile));
526564562Sgshapiro	}
526664562Sgshapiro	else
526764562Sgshapiro	{
5268168515Sgshapiro		if (sm_strlcpy(targetfile, filename, sizeof(targetfile)) >=
5269168515Sgshapiro		    sizeof(targetfile))
527064562Sgshapiro		{
527164562Sgshapiro			syserr("mailfile: filename too long (%s)", filename);
527264562Sgshapiro			return EX_CANTCREAT;
527364562Sgshapiro		}
527464562Sgshapiro		realfile = targetfile;
527564562Sgshapiro	}
527664562Sgshapiro
527738032Speter	/*
527838032Speter	**  Fork so we can change permissions here.
527938032Speter	**	Note that we MUST use fork, not vfork, because of
528038032Speter	**	the complications of calling subroutines, etc.
528138032Speter	*/
528238032Speter
528394334Sgshapiro
528494334Sgshapiro	/*
528594334Sgshapiro	**  Dispose of SIGCHLD signal catchers that may be laying
528694334Sgshapiro	**  around so that the waitfor() below will get it.
528794334Sgshapiro	*/
528894334Sgshapiro
528994334Sgshapiro	(void) sm_signal(SIGCHLD, SIG_DFL);
529094334Sgshapiro
529138032Speter	DOFORK(fork);
529238032Speter
529338032Speter	if (pid < 0)
529464562Sgshapiro		return EX_OSERR;
529538032Speter	else if (pid == 0)
529638032Speter	{
529738032Speter		/* child -- actually write to file */
529838032Speter		struct stat stb;
529938032Speter		MCI mcibuf;
530042575Speter		int err;
530138032Speter		volatile int oflags = O_WRONLY|O_APPEND;
530238032Speter
530377349Sgshapiro		/* Reset global flags */
530477349Sgshapiro		RestartRequest = NULL;
530590792Sgshapiro		RestartWorkGroup = false;
530677349Sgshapiro		ShutdownRequest = NULL;
530777349Sgshapiro		PendingSignal = 0;
530890792Sgshapiro		CurrentPid = getpid();
530977349Sgshapiro
531038032Speter		if (e->e_lockfp != NULL)
5311159609Sgshapiro		{
5312159609Sgshapiro			int fd;
531338032Speter
5314159609Sgshapiro			fd = sm_io_getinfo(e->e_lockfp, SM_IO_WHAT_FD, NULL);
5315159609Sgshapiro			/* SM_ASSERT(fd >= 0); */
5316159609Sgshapiro			if (fd >= 0)
5317159609Sgshapiro				(void) close(fd);
5318159609Sgshapiro		}
5319159609Sgshapiro
532090792Sgshapiro		(void) sm_signal(SIGINT, SIG_DFL);
532190792Sgshapiro		(void) sm_signal(SIGHUP, SIG_DFL);
532290792Sgshapiro		(void) sm_signal(SIGTERM, SIG_DFL);
532338032Speter		(void) umask(OldUmask);
532438032Speter		e->e_to = filename;
532538032Speter		ExitStat = EX_OK;
532638032Speter
532738032Speter		if (setjmp(CtxMailfileTimeout) != 0)
532838032Speter		{
532990792Sgshapiro			RETURN(EX_TEMPFAIL);
533038032Speter		}
533138032Speter
533238032Speter		if (TimeOuts.to_fileopen > 0)
533390792Sgshapiro			ev = sm_setevent(TimeOuts.to_fileopen, mailfiletimeout,
533490792Sgshapiro					 0);
533538032Speter		else
533638032Speter			ev = NULL;
533738032Speter
533890792Sgshapiro		/* check file mode to see if set-user-ID */
533964562Sgshapiro		if (stat(targetfile, &stb) < 0)
534064562Sgshapiro			mode = FileMode;
534142575Speter		else
534238032Speter			mode = stb.st_mode;
534338032Speter
534438032Speter		/* limit the errors to those actually caused in the child */
534538032Speter		errno = 0;
534638032Speter		ExitStat = EX_OK;
534738032Speter
534864562Sgshapiro		/* Allow alias expansions to use the S_IS{U,G}ID bits */
534964562Sgshapiro		if ((ctladdr != NULL && !bitset(QALIAS, ctladdr->q_flags)) ||
535064562Sgshapiro		    bitset(SFF_RUNASREALUID, sfflags))
535138032Speter		{
535290792Sgshapiro			/* ignore set-user-ID and set-group-ID bits */
535338032Speter			mode &= ~(S_ISGID|S_ISUID);
535464562Sgshapiro			if (tTd(11, 20))
535590792Sgshapiro				sm_dprintf("mailfile: ignoring set-user-ID/set-group-ID bits\n");
535638032Speter		}
535738032Speter
535890792Sgshapiro		/* we have to open the data file BEFORE setuid() */
535938032Speter		if (e->e_dfp == NULL && bitset(EF_HAS_DF, e->e_flags))
536038032Speter		{
536190792Sgshapiro			char *df = queuename(e, DATAFL_LETTER);
536238032Speter
536390792Sgshapiro			e->e_dfp = sm_io_open(SmFtStdio, SM_TIME_DEFAULT, df,
5364120256Sgshapiro					      SM_IO_RDONLY_B, NULL);
536538032Speter			if (e->e_dfp == NULL)
536638032Speter			{
536738032Speter				syserr("mailfile: Cannot open %s for %s from %s",
536838032Speter					df, e->e_to, e->e_from.q_paddr);
536938032Speter			}
537038032Speter		}
537138032Speter
537238032Speter		/* select a new user to run as */
537338032Speter		if (!bitset(SFF_RUNASREALUID, sfflags))
537438032Speter		{
537538032Speter			if (bitnset(M_SPECIFIC_UID, mailer->m_flags))
537638032Speter			{
537738032Speter				RealUserName = NULL;
5378132943Sgshapiro				if (mailer->m_uid == NO_UID)
5379132943Sgshapiro					RealUid = RunAsUid;
5380132943Sgshapiro				else
5381132943Sgshapiro					RealUid = mailer->m_uid;
538264562Sgshapiro				if (RunAsUid != 0 && RealUid != RunAsUid)
538364562Sgshapiro				{
538464562Sgshapiro					/* Only root can change the uid */
5385285303Sgshapiro					syserr("mailfile: insufficient privileges to change uid, RunAsUid=%ld, RealUid=%ld",
5386285303Sgshapiro						(long) RunAsUid, (long) RealUid);
538790792Sgshapiro					RETURN(EX_TEMPFAIL);
538864562Sgshapiro				}
538938032Speter			}
539038032Speter			else if (bitset(S_ISUID, mode))
539138032Speter			{
539238032Speter				RealUserName = NULL;
539338032Speter				RealUid = stb.st_uid;
539438032Speter			}
539538032Speter			else if (ctladdr != NULL && ctladdr->q_uid != 0)
539638032Speter			{
539738032Speter				if (ctladdr->q_ruser != NULL)
539838032Speter					RealUserName = ctladdr->q_ruser;
539938032Speter				else
540038032Speter					RealUserName = ctladdr->q_user;
540138032Speter				RealUid = ctladdr->q_uid;
540238032Speter			}
5403132943Sgshapiro			else if (mailer != NULL && mailer->m_uid != NO_UID)
540438032Speter			{
540538032Speter				RealUserName = DefUser;
540638032Speter				RealUid = mailer->m_uid;
540738032Speter			}
540838032Speter			else
540938032Speter			{
541038032Speter				RealUserName = DefUser;
541138032Speter				RealUid = DefUid;
541238032Speter			}
541338032Speter
541438032Speter			/* select a new group to run as */
541538032Speter			if (bitnset(M_SPECIFIC_UID, mailer->m_flags))
541664562Sgshapiro			{
5417132943Sgshapiro				if (mailer->m_gid == NO_GID)
5418132943Sgshapiro					RealGid = RunAsGid;
5419132943Sgshapiro				else
5420132943Sgshapiro					RealGid = mailer->m_gid;
542164562Sgshapiro				if (RunAsUid != 0 &&
542264562Sgshapiro				    (RealGid != getgid() ||
542364562Sgshapiro				     RealGid != getegid()))
542464562Sgshapiro				{
542564562Sgshapiro					/* Only root can change the gid */
5426285303Sgshapiro					syserr("mailfile: insufficient privileges to change gid, RealGid=%ld, RunAsUid=%ld, gid=%ld, egid=%ld",
5427285303Sgshapiro					       (long) RealGid, (long) RunAsUid,
5428285303Sgshapiro					       (long) getgid(), (long) getegid());
542990792Sgshapiro					RETURN(EX_TEMPFAIL);
543064562Sgshapiro				}
543164562Sgshapiro			}
543238032Speter			else if (bitset(S_ISGID, mode))
543338032Speter				RealGid = stb.st_gid;
543464562Sgshapiro			else if (ctladdr != NULL &&
543564562Sgshapiro				 ctladdr->q_uid == DefUid &&
543664562Sgshapiro				 ctladdr->q_gid == 0)
543771345Sgshapiro			{
543871345Sgshapiro				/*
543971345Sgshapiro				**  Special case:  This means it is an
544071345Sgshapiro				**  alias and we should act as DefaultUser.
544171345Sgshapiro				**  See alias()'s comments.
544271345Sgshapiro				*/
544371345Sgshapiro
544464562Sgshapiro				RealGid = DefGid;
544571345Sgshapiro				RealUserName = DefUser;
544671345Sgshapiro			}
544771345Sgshapiro			else if (ctladdr != NULL && ctladdr->q_uid != 0)
544871345Sgshapiro				RealGid = ctladdr->q_gid;
5449132943Sgshapiro			else if (mailer != NULL && mailer->m_gid != NO_GID)
545038032Speter				RealGid = mailer->m_gid;
545138032Speter			else
545238032Speter				RealGid = DefGid;
545338032Speter		}
545438032Speter
545538032Speter		/* last ditch */
545638032Speter		if (!bitset(SFF_ROOTOK, sfflags))
545738032Speter		{
545838032Speter			if (RealUid == 0)
545938032Speter				RealUid = DefUid;
546038032Speter			if (RealGid == 0)
546138032Speter				RealGid = DefGid;
546238032Speter		}
546338032Speter
546438032Speter		/* set group id list (needs /etc/group access) */
546538032Speter		if (RealUserName != NULL && !DontInitGroups)
546638032Speter		{
546738032Speter			if (initgroups(RealUserName, RealGid) == -1 && suidwarn)
546864562Sgshapiro			{
5469285303Sgshapiro				syserr("mailfile: initgroups(%s, %ld) failed",
5470285303Sgshapiro					RealUserName, (long) RealGid);
547190792Sgshapiro				RETURN(EX_TEMPFAIL);
547264562Sgshapiro			}
547338032Speter		}
547438032Speter		else
547538032Speter		{
547638032Speter			GIDSET_T gidset[1];
547738032Speter
547838032Speter			gidset[0] = RealGid;
547938032Speter			if (setgroups(1, gidset) == -1 && suidwarn)
548064562Sgshapiro			{
548138032Speter				syserr("mailfile: setgroups() failed");
548290792Sgshapiro				RETURN(EX_TEMPFAIL);
548364562Sgshapiro			}
548438032Speter		}
548538032Speter
548664562Sgshapiro		/*
548764562Sgshapiro		**  If you have a safe environment, go into it.
548864562Sgshapiro		*/
548964562Sgshapiro
549064562Sgshapiro		if (realfile != targetfile)
549138032Speter		{
549294334Sgshapiro			char save;
549394334Sgshapiro
549494334Sgshapiro			save = *realfile;
549564562Sgshapiro			*realfile = '\0';
549664562Sgshapiro			if (tTd(11, 20))
549790792Sgshapiro				sm_dprintf("mailfile: chroot %s\n", targetfile);
549864562Sgshapiro			if (chroot(targetfile) < 0)
549938032Speter			{
550038032Speter				syserr("mailfile: Cannot chroot(%s)",
550164562Sgshapiro				       targetfile);
550290792Sgshapiro				RETURN(EX_CANTCREAT);
550338032Speter			}
550494334Sgshapiro			*realfile = save;
550538032Speter		}
550664562Sgshapiro
550764562Sgshapiro		if (tTd(11, 40))
550890792Sgshapiro			sm_dprintf("mailfile: deliver to %s\n", realfile);
550964562Sgshapiro
551038032Speter		if (chdir("/") < 0)
551164562Sgshapiro		{
551238032Speter			syserr("mailfile: cannot chdir(/)");
551390792Sgshapiro			RETURN(EX_CANTCREAT);
551464562Sgshapiro		}
551538032Speter
551638032Speter		/* now reset the group and user ids */
551738032Speter		endpwent();
551890792Sgshapiro		sm_mbdb_terminate();
551938032Speter		if (setgid(RealGid) < 0 && suidwarn)
552064562Sgshapiro		{
552138032Speter			syserr("mailfile: setgid(%ld) failed", (long) RealGid);
552290792Sgshapiro			RETURN(EX_TEMPFAIL);
552364562Sgshapiro		}
552438032Speter		vendor_set_uid(RealUid);
552538032Speter		if (setuid(RealUid) < 0 && suidwarn)
552664562Sgshapiro		{
552738032Speter			syserr("mailfile: setuid(%ld) failed", (long) RealUid);
552890792Sgshapiro			RETURN(EX_TEMPFAIL);
552964562Sgshapiro		}
553038032Speter
553164562Sgshapiro		if (tTd(11, 2))
5532285303Sgshapiro			sm_dprintf("mailfile: running as r/euid=%ld/%ld, r/egid=%ld/%ld\n",
5533285303Sgshapiro				(long) getuid(), (long) geteuid(),
5534285303Sgshapiro				(long) getgid(), (long) getegid());
553564562Sgshapiro
553664562Sgshapiro
553738032Speter		/* move into some "safe" directory */
553838032Speter		if (mailer->m_execdir != NULL)
553938032Speter		{
554038032Speter			char *q;
554138032Speter
554238032Speter			for (p = mailer->m_execdir; p != NULL; p = q)
554338032Speter			{
554438032Speter				q = strchr(p, ':');
554538032Speter				if (q != NULL)
554638032Speter					*q = '\0';
5547168515Sgshapiro				expand(p, buf, sizeof(buf), e);
554838032Speter				if (q != NULL)
554938032Speter					*q++ = ':';
555038032Speter				if (tTd(11, 20))
555190792Sgshapiro					sm_dprintf("mailfile: trydir %s\n",
555290792Sgshapiro						   buf);
555338032Speter				if (buf[0] != '\0' && chdir(buf) >= 0)
555438032Speter					break;
555538032Speter			}
555638032Speter		}
555738032Speter
555864562Sgshapiro		/*
555964562Sgshapiro		**  Recheck the file after we have assumed the ID of the
556064562Sgshapiro		**  delivery user to make sure we can deliver to it as
556164562Sgshapiro		**  that user.  This is necessary if sendmail is running
556264562Sgshapiro		**  as root and the file is on an NFS mount which treats
556364562Sgshapiro		**  root as nobody.
556464562Sgshapiro		*/
556564562Sgshapiro
556664562Sgshapiro#if HASLSTAT
556764562Sgshapiro		if (bitnset(DBS_FILEDELIVERYTOSYMLINK, DontBlameSendmail))
556864562Sgshapiro			err = stat(realfile, &stb);
556964562Sgshapiro		else
557064562Sgshapiro			err = lstat(realfile, &stb);
557164562Sgshapiro#else /* HASLSTAT */
557264562Sgshapiro		err = stat(realfile, &stb);
557364562Sgshapiro#endif /* HASLSTAT */
557464562Sgshapiro
557564562Sgshapiro		if (err < 0)
557664562Sgshapiro		{
557764562Sgshapiro			stb.st_mode = ST_MODE_NOFILE;
557864562Sgshapiro			mode = FileMode;
557964562Sgshapiro			oflags |= O_CREAT|O_EXCL;
558064562Sgshapiro		}
558164562Sgshapiro		else if (bitset(S_IXUSR|S_IXGRP|S_IXOTH, mode) ||
558264562Sgshapiro			 (!bitnset(DBS_FILEDELIVERYTOHARDLINK,
558364562Sgshapiro				   DontBlameSendmail) &&
558464562Sgshapiro			  stb.st_nlink != 1) ||
558564562Sgshapiro			 (realfile != targetfile && !S_ISREG(mode)))
558664562Sgshapiro			exit(EX_CANTCREAT);
558764562Sgshapiro		else
558864562Sgshapiro			mode = stb.st_mode;
558964562Sgshapiro
559064562Sgshapiro		if (!bitnset(DBS_FILEDELIVERYTOSYMLINK, DontBlameSendmail))
559138032Speter			sfflags |= SFF_NOSLINK;
559264562Sgshapiro		if (!bitnset(DBS_FILEDELIVERYTOHARDLINK, DontBlameSendmail))
559338032Speter			sfflags |= SFF_NOHLINK;
559438032Speter		sfflags &= ~SFF_OPENASROOT;
559564562Sgshapiro		f = safefopen(realfile, oflags, mode, sfflags);
559638032Speter		if (f == NULL)
559738032Speter		{
559864562Sgshapiro			if (transienterror(errno))
559964562Sgshapiro			{
560064562Sgshapiro				usrerr("454 4.3.0 cannot open %s: %s",
560164562Sgshapiro				       shortenstring(realfile, MAXSHORTSTR),
560290792Sgshapiro				       sm_errstring(errno));
560390792Sgshapiro				RETURN(EX_TEMPFAIL);
560464562Sgshapiro			}
560564562Sgshapiro			else
560664562Sgshapiro			{
560764562Sgshapiro				usrerr("554 5.3.0 cannot open %s: %s",
560864562Sgshapiro				       shortenstring(realfile, MAXSHORTSTR),
560990792Sgshapiro				       sm_errstring(errno));
561090792Sgshapiro				RETURN(EX_CANTCREAT);
561164562Sgshapiro			}
561238032Speter		}
561390792Sgshapiro		if (filechanged(realfile, sm_io_getinfo(f, SM_IO_WHAT_FD, NULL),
561490792Sgshapiro		    &stb))
561538032Speter		{
561664562Sgshapiro			syserr("554 5.3.0 file changed after open");
561790792Sgshapiro			RETURN(EX_CANTCREAT);
561838032Speter		}
561990792Sgshapiro		if (fstat(sm_io_getinfo(f, SM_IO_WHAT_FD, NULL), &stb) < 0)
562038032Speter		{
562190792Sgshapiro			syserr("554 5.3.0 cannot fstat %s",
562290792Sgshapiro				sm_errstring(errno));
562390792Sgshapiro			RETURN(EX_CANTCREAT);
562438032Speter		}
562538032Speter
562664562Sgshapiro		curoff = stb.st_size;
562764562Sgshapiro
562838032Speter		if (ev != NULL)
562990792Sgshapiro			sm_clrevent(ev);
563038032Speter
5631168515Sgshapiro		memset(&mcibuf, '\0', sizeof(mcibuf));
563238032Speter		mcibuf.mci_mailer = mailer;
563338032Speter		mcibuf.mci_out = f;
563438032Speter		if (bitnset(M_7BITS, mailer->m_flags))
563538032Speter			mcibuf.mci_flags |= MCIF_7BIT;
563638032Speter
563738032Speter		/* clear out per-message flags from connection structure */
563838032Speter		mcibuf.mci_flags &= ~(MCIF_CVT7TO8|MCIF_CVT8TO7);
563938032Speter
564038032Speter		if (bitset(EF_HAS8BIT, e->e_flags) &&
564138032Speter		    !bitset(EF_DONT_MIME, e->e_flags) &&
564238032Speter		    bitnset(M_7BITS, mailer->m_flags))
564338032Speter			mcibuf.mci_flags |= MCIF_CVT8TO7;
564438032Speter
564538032Speter#if MIME7TO8
564638032Speter		if (bitnset(M_MAKE8BIT, mailer->m_flags) &&
564738032Speter		    !bitset(MCIF_7BIT, mcibuf.mci_flags) &&
564838032Speter		    (p = hvalue("Content-Transfer-Encoding", e->e_header)) != NULL &&
564990792Sgshapiro		    (sm_strcasecmp(p, "quoted-printable") == 0 ||
565090792Sgshapiro		     sm_strcasecmp(p, "base64") == 0) &&
565138032Speter		    (p = hvalue("Content-Type", e->e_header)) != NULL)
565238032Speter		{
565338032Speter			/* may want to convert 7 -> 8 */
565438032Speter			/* XXX should really parse it here -- and use a class XXX */
565590792Sgshapiro			if (sm_strncasecmp(p, "text/plain", 10) == 0 &&
565664562Sgshapiro			    (p[10] == '\0' || p[10] == ' ' || p[10] == ';'))
565738032Speter				mcibuf.mci_flags |= MCIF_CVT7TO8;
565838032Speter		}
565964562Sgshapiro#endif /* MIME7TO8 */
566038032Speter
5661157001Sgshapiro		if (!putfromline(&mcibuf, e) ||
5662157001Sgshapiro		    !(*e->e_puthdr)(&mcibuf, e->e_header, e, M87F_OUTER) ||
5663157001Sgshapiro		    !(*e->e_putbody)(&mcibuf, e, NULL) ||
5664157001Sgshapiro		    !putline("\n", &mcibuf) ||
5665157001Sgshapiro		    (sm_io_flush(f, SM_TIME_DEFAULT) != 0 ||
566690792Sgshapiro		    (SuperSafe != SAFE_NO &&
566790792Sgshapiro		     fsync(sm_io_getinfo(f, SM_IO_WHAT_FD, NULL)) < 0) ||
5668157001Sgshapiro		    sm_io_error(f)))
566938032Speter		{
567038032Speter			setstat(EX_IOERR);
567164562Sgshapiro#if !NOFTRUNCATE
567290792Sgshapiro			(void) ftruncate(sm_io_getinfo(f, SM_IO_WHAT_FD, NULL),
567390792Sgshapiro					 curoff);
567464562Sgshapiro#endif /* !NOFTRUNCATE */
567538032Speter		}
567638032Speter
567738032Speter		/* reset ISUID & ISGID bits for paranoid systems */
567838032Speter#if HASFCHMOD
567990792Sgshapiro		(void) fchmod(sm_io_getinfo(f, SM_IO_WHAT_FD, NULL),
568090792Sgshapiro			      (MODE_T) mode);
568164562Sgshapiro#else /* HASFCHMOD */
568264562Sgshapiro		(void) chmod(filename, (MODE_T) mode);
568364562Sgshapiro#endif /* HASFCHMOD */
568490792Sgshapiro		if (sm_io_close(f, SM_TIME_DEFAULT) < 0)
568564562Sgshapiro			setstat(EX_IOERR);
568690792Sgshapiro		(void) sm_io_flush(smioout, SM_TIME_DEFAULT);
568764562Sgshapiro		(void) setuid(RealUid);
568838032Speter		exit(ExitStat);
568964562Sgshapiro		/* NOTREACHED */
569038032Speter	}
569138032Speter	else
569238032Speter	{
569338032Speter		/* parent -- wait for exit status */
569438032Speter		int st;
569538032Speter
569638032Speter		st = waitfor(pid);
569738032Speter		if (st == -1)
569838032Speter		{
569938032Speter			syserr("mailfile: %s: wait", mailer->m_name);
570064562Sgshapiro			return EX_SOFTWARE;
570138032Speter		}
570238032Speter		if (WIFEXITED(st))
570390792Sgshapiro		{
570490792Sgshapiro			errno = 0;
570538032Speter			return (WEXITSTATUS(st));
570690792Sgshapiro		}
570738032Speter		else
570838032Speter		{
570938032Speter			syserr("mailfile: %s: child died on signal %d",
571038032Speter			       mailer->m_name, st);
571164562Sgshapiro			return EX_UNAVAILABLE;
571238032Speter		}
571364562Sgshapiro		/* NOTREACHED */
571438032Speter	}
571538032Speter	return EX_UNAVAILABLE;	/* avoid compiler warning on IRIX */
571638032Speter}
571738032Speter
571838032Speterstatic void
5719141858Sgshapiromailfiletimeout(ignore)
5720141858Sgshapiro	int ignore;
572138032Speter{
572277349Sgshapiro	/*
572377349Sgshapiro	**  NOTE: THIS CAN BE CALLED FROM A SIGNAL HANDLER.  DO NOT ADD
572477349Sgshapiro	**	ANYTHING TO THIS ROUTINE UNLESS YOU KNOW WHAT YOU ARE
572577349Sgshapiro	**	DOING.
572677349Sgshapiro	*/
572777349Sgshapiro
572877349Sgshapiro	errno = ETIMEDOUT;
572938032Speter	longjmp(CtxMailfileTimeout, 1);
573038032Speter}
573190792Sgshapiro/*
573238032Speter**  HOSTSIGNATURE -- return the "signature" for a host.
573338032Speter**
573438032Speter**	The signature describes how we are going to send this -- it
573538032Speter**	can be just the hostname (for non-Internet hosts) or can be
573638032Speter**	an ordered list of MX hosts.
573738032Speter**
573838032Speter**	Parameters:
573938032Speter**		m -- the mailer describing this host.
574038032Speter**		host -- the host name.
574138032Speter**
574238032Speter**	Returns:
574338032Speter**		The signature for this host.
574438032Speter**
574538032Speter**	Side Effects:
574638032Speter**		Can tweak the symbol table.
574738032Speter*/
574890792Sgshapiro
574964562Sgshapiro#define MAXHOSTSIGNATURE	8192	/* max len of hostsignature */
575038032Speter
575190792Sgshapirochar *
575264562Sgshapirohostsignature(m, host)
575338032Speter	register MAILER *m;
575438032Speter	char *host;
575538032Speter{
575638032Speter	register char *p;
575738032Speter	register STAB *s;
575890792Sgshapiro	time_t now;
575964562Sgshapiro#if NAMED_BIND
576064562Sgshapiro	char sep = ':';
576164562Sgshapiro	char prevsep = ':';
576238032Speter	int i;
576338032Speter	int len;
576438032Speter	int nmx;
576564562Sgshapiro	int hl;
576638032Speter	char *hp;
576738032Speter	char *endp;
576838032Speter	int oldoptions = _res.options;
576938032Speter	char *mxhosts[MAXMXHOSTS + 1];
577090792Sgshapiro	unsigned short mxprefs[MAXMXHOSTS + 1];
577164562Sgshapiro#endif /* NAMED_BIND */
577238032Speter
577364562Sgshapiro	if (tTd(17, 3))
577490792Sgshapiro		sm_dprintf("hostsignature(%s)\n", host);
577564562Sgshapiro
577638032Speter	/*
577777349Sgshapiro	**  If local delivery (and not remote), just return a constant.
577864562Sgshapiro	*/
577964562Sgshapiro
578077349Sgshapiro	if (bitnset(M_LOCALMAILER, m->m_flags) &&
578190792Sgshapiro	    strcmp(m->m_mailer, "[IPC]") != 0 &&
578290792Sgshapiro	    !(m->m_argv[0] != NULL && strcmp(m->m_argv[0], "TCP") == 0))
578364562Sgshapiro		return "localhost";
578464562Sgshapiro
5785147078Sgshapiro	/* an empty host does not have MX records */
5786147078Sgshapiro	if (*host == '\0')
5787147078Sgshapiro		return "_empty_";
5788147078Sgshapiro
578964562Sgshapiro	/*
579038032Speter	**  Check to see if this uses IPC -- if not, it can't have MX records.
579138032Speter	*/
579238032Speter
579390792Sgshapiro	if (strcmp(m->m_mailer, "[IPC]") != 0 ||
579490792Sgshapiro	    CurEnv->e_sendmode == SM_DEFER)
579538032Speter	{
579690792Sgshapiro		/* just an ordinary mailer or deferred mode */
579738032Speter		return host;
579838032Speter	}
579964562Sgshapiro#if NETUNIX
580064562Sgshapiro	else if (m->m_argv[0] != NULL &&
580164562Sgshapiro		 strcmp(m->m_argv[0], "FILE") == 0)
580264562Sgshapiro	{
580364562Sgshapiro		/* rendezvous in the file system, no MX records */
580464562Sgshapiro		return host;
580564562Sgshapiro	}
580664562Sgshapiro#endif /* NETUNIX */
580738032Speter
580838032Speter	/*
580938032Speter	**  Look it up in the symbol table.
581038032Speter	*/
581138032Speter
581290792Sgshapiro	now = curtime();
581338032Speter	s = stab(host, ST_HOSTSIG, ST_ENTER);
581490792Sgshapiro	if (s->s_hostsig.hs_sig != NULL)
581564562Sgshapiro	{
581690792Sgshapiro		if (s->s_hostsig.hs_exp >= now)
581790792Sgshapiro		{
581890792Sgshapiro			if (tTd(17, 3))
581990792Sgshapiro				sm_dprintf("hostsignature(): stab(%s) found %s\n", host,
582090792Sgshapiro					   s->s_hostsig.hs_sig);
582190792Sgshapiro			return s->s_hostsig.hs_sig;
582290792Sgshapiro		}
582390792Sgshapiro
582490792Sgshapiro		/* signature is expired: clear it */
582590792Sgshapiro		sm_free(s->s_hostsig.hs_sig);
582690792Sgshapiro		s->s_hostsig.hs_sig = NULL;
582764562Sgshapiro	}
582838032Speter
582990792Sgshapiro	/* set default TTL */
583090792Sgshapiro	s->s_hostsig.hs_exp = now + SM_DEFAULT_TTL;
583190792Sgshapiro
583238032Speter	/*
583390792Sgshapiro	**  Not already there or expired -- create a signature.
583438032Speter	*/
583538032Speter
583638032Speter#if NAMED_BIND
583738032Speter	if (ConfigLevel < 2)
583838032Speter		_res.options &= ~(RES_DEFNAMES | RES_DNSRCH);	/* XXX */
583938032Speter
584038032Speter	for (hp = host; hp != NULL; hp = endp)
584138032Speter	{
584264562Sgshapiro#if NETINET6
584364562Sgshapiro		if (*hp == '[')
584464562Sgshapiro		{
584564562Sgshapiro			endp = strchr(hp + 1, ']');
584664562Sgshapiro			if (endp != NULL)
584764562Sgshapiro				endp = strpbrk(endp + 1, ":,");
584864562Sgshapiro		}
584964562Sgshapiro		else
585064562Sgshapiro			endp = strpbrk(hp, ":,");
585164562Sgshapiro#else /* NETINET6 */
585264562Sgshapiro		endp = strpbrk(hp, ":,");
585364562Sgshapiro#endif /* NETINET6 */
585438032Speter		if (endp != NULL)
585564562Sgshapiro		{
585664562Sgshapiro			sep = *endp;
585738032Speter			*endp = '\0';
585864562Sgshapiro		}
585938032Speter
586038032Speter		if (bitnset(M_NOMX, m->m_flags))
586138032Speter		{
586238032Speter			/* skip MX lookups */
586338032Speter			nmx = 1;
586438032Speter			mxhosts[0] = hp;
586538032Speter		}
586638032Speter		else
586738032Speter		{
586838032Speter			auto int rcode;
586990792Sgshapiro			int ttl;
587038032Speter
587190792Sgshapiro			nmx = getmxrr(hp, mxhosts, mxprefs, true, &rcode, true,
587290792Sgshapiro				      &ttl);
587338032Speter			if (nmx <= 0)
587438032Speter			{
587580785Sgshapiro				int save_errno;
587638032Speter				register MCI *mci;
587738032Speter
587838032Speter				/* update the connection info for this host */
587980785Sgshapiro				save_errno = errno;
588038032Speter				mci = mci_get(hp, m);
588180785Sgshapiro				mci->mci_errno = save_errno;
588238032Speter				mci->mci_herrno = h_errno;
588371345Sgshapiro				mci->mci_lastuse = now;
588464562Sgshapiro				if (rcode == EX_NOHOST)
588564562Sgshapiro					mci_setstat(mci, rcode, "5.1.2",
588690792Sgshapiro						    "550 Host unknown");
588764562Sgshapiro				else
588864562Sgshapiro					mci_setstat(mci, rcode, NULL, NULL);
588938032Speter
589038032Speter				/* use the original host name as signature */
589138032Speter				nmx = 1;
589238032Speter				mxhosts[0] = hp;
589338032Speter			}
589464562Sgshapiro			if (tTd(17, 3))
589590792Sgshapiro				sm_dprintf("hostsignature(): getmxrr() returned %d, mxhosts[0]=%s\n",
589690792Sgshapiro					   nmx, mxhosts[0]);
589790792Sgshapiro
589890792Sgshapiro			/*
589990792Sgshapiro			**  Set new TTL: we use only one!
590090792Sgshapiro			**	We could try to use the minimum instead.
590190792Sgshapiro			*/
590290792Sgshapiro
590390792Sgshapiro			s->s_hostsig.hs_exp = now + SM_MIN(ttl, SM_DEFAULT_TTL);
590438032Speter		}
590538032Speter
590638032Speter		len = 0;
590738032Speter		for (i = 0; i < nmx; i++)
590838032Speter			len += strlen(mxhosts[i]) + 1;
590990792Sgshapiro		if (s->s_hostsig.hs_sig != NULL)
591090792Sgshapiro			len += strlen(s->s_hostsig.hs_sig) + 1;
591190792Sgshapiro		if (len < 0 || len >= MAXHOSTSIGNATURE)
591264562Sgshapiro		{
591364562Sgshapiro			sm_syslog(LOG_WARNING, NOQID, "hostsignature for host '%s' exceeds maxlen (%d): %d",
591464562Sgshapiro				  host, MAXHOSTSIGNATURE, len);
591564562Sgshapiro			len = MAXHOSTSIGNATURE;
591664562Sgshapiro		}
591790792Sgshapiro		p = sm_pmalloc_x(len);
591890792Sgshapiro		if (s->s_hostsig.hs_sig != NULL)
591938032Speter		{
592090792Sgshapiro			(void) sm_strlcpy(p, s->s_hostsig.hs_sig, len);
592190792Sgshapiro			sm_free(s->s_hostsig.hs_sig); /* XXX */
592290792Sgshapiro			s->s_hostsig.hs_sig = p;
592364562Sgshapiro			hl = strlen(p);
592464562Sgshapiro			p += hl;
592564562Sgshapiro			*p++ = prevsep;
592664562Sgshapiro			len -= hl + 1;
592738032Speter		}
592838032Speter		else
592990792Sgshapiro			s->s_hostsig.hs_sig = p;
593038032Speter		for (i = 0; i < nmx; i++)
593138032Speter		{
593264562Sgshapiro			hl = strlen(mxhosts[i]);
593364562Sgshapiro			if (len - 1 < hl || len <= 1)
593464562Sgshapiro			{
593564562Sgshapiro				/* force to drop out of outer loop */
593664562Sgshapiro				len = -1;
593764562Sgshapiro				break;
593864562Sgshapiro			}
593938032Speter			if (i != 0)
594064562Sgshapiro			{
594164562Sgshapiro				if (mxprefs[i] == mxprefs[i - 1])
594264562Sgshapiro					*p++ = ',';
594364562Sgshapiro				else
594464562Sgshapiro					*p++ = ':';
594564562Sgshapiro				len--;
594664562Sgshapiro			}
594790792Sgshapiro			(void) sm_strlcpy(p, mxhosts[i], len);
594864562Sgshapiro			p += hl;
594964562Sgshapiro			len -= hl;
595038032Speter		}
595164562Sgshapiro
595264562Sgshapiro		/*
595364562Sgshapiro		**  break out of loop if len exceeded MAXHOSTSIGNATURE
595464562Sgshapiro		**  because we won't have more space for further hosts
595564562Sgshapiro		**  anyway (separated by : in the .cf file).
595664562Sgshapiro		*/
595764562Sgshapiro
595864562Sgshapiro		if (len < 0)
595964562Sgshapiro			break;
596038032Speter		if (endp != NULL)
596164562Sgshapiro			*endp++ = sep;
596264562Sgshapiro		prevsep = sep;
596338032Speter	}
596490792Sgshapiro	makelower(s->s_hostsig.hs_sig);
596538032Speter	if (ConfigLevel < 2)
596638032Speter		_res.options = oldoptions;
596764562Sgshapiro#else /* NAMED_BIND */
596838032Speter	/* not using BIND -- the signature is just the host name */
596990792Sgshapiro	/*
597090792Sgshapiro	**  'host' points to storage that will be freed after we are
597190792Sgshapiro	**  done processing the current envelope, so we copy it.
597290792Sgshapiro	*/
597390792Sgshapiro	s->s_hostsig.hs_sig = sm_pstrdup_x(host);
597464562Sgshapiro#endif /* NAMED_BIND */
597538032Speter	if (tTd(17, 1))
597690792Sgshapiro		sm_dprintf("hostsignature(%s) = %s\n", host, s->s_hostsig.hs_sig);
597790792Sgshapiro	return s->s_hostsig.hs_sig;
597838032Speter}
597990792Sgshapiro/*
598064562Sgshapiro**  PARSE_HOSTSIGNATURE -- parse the "signature" and return MX host array.
598164562Sgshapiro**
598264562Sgshapiro**	The signature describes how we are going to send this -- it
598364562Sgshapiro**	can be just the hostname (for non-Internet hosts) or can be
598464562Sgshapiro**	an ordered list of MX hosts which must be randomized for equal
598564562Sgshapiro**	MX preference values.
598664562Sgshapiro**
598764562Sgshapiro**	Parameters:
598864562Sgshapiro**		sig -- the host signature.
598964562Sgshapiro**		mxhosts -- array to populate.
599090792Sgshapiro**		mailer -- mailer.
599164562Sgshapiro**
599264562Sgshapiro**	Returns:
599364562Sgshapiro**		The number of hosts inserted into mxhosts array.
599464562Sgshapiro**
599564562Sgshapiro**	Side Effects:
599664562Sgshapiro**		Randomizes equal MX preference hosts in mxhosts.
599764562Sgshapiro*/
599864562Sgshapiro
599964562Sgshapirostatic int
600064562Sgshapiroparse_hostsignature(sig, mxhosts, mailer)
600164562Sgshapiro	char *sig;
600264562Sgshapiro	char **mxhosts;
600364562Sgshapiro	MAILER *mailer;
600464562Sgshapiro{
600590792Sgshapiro	unsigned short curpref = 0;
600690792Sgshapiro	int nmx = 0, i, j;	/* NOTE: i, j, and nmx must have same type */
600764562Sgshapiro	char *hp, *endp;
600890792Sgshapiro	unsigned short prefer[MAXMXHOSTS];
600964562Sgshapiro	long rndm[MAXMXHOSTS];
601064562Sgshapiro
601164562Sgshapiro	for (hp = sig; hp != NULL; hp = endp)
601264562Sgshapiro	{
601364562Sgshapiro		char sep = ':';
601464562Sgshapiro
601564562Sgshapiro#if NETINET6
601664562Sgshapiro		if (*hp == '[')
601764562Sgshapiro		{
601864562Sgshapiro			endp = strchr(hp + 1, ']');
601964562Sgshapiro			if (endp != NULL)
602064562Sgshapiro				endp = strpbrk(endp + 1, ":,");
602164562Sgshapiro		}
602264562Sgshapiro		else
602364562Sgshapiro			endp = strpbrk(hp, ":,");
602464562Sgshapiro#else /* NETINET6 */
602564562Sgshapiro		endp = strpbrk(hp, ":,");
602664562Sgshapiro#endif /* NETINET6 */
602764562Sgshapiro		if (endp != NULL)
602864562Sgshapiro		{
602964562Sgshapiro			sep = *endp;
603064562Sgshapiro			*endp = '\0';
603164562Sgshapiro		}
603264562Sgshapiro
603364562Sgshapiro		mxhosts[nmx] = hp;
603464562Sgshapiro		prefer[nmx] = curpref;
603564562Sgshapiro		if (mci_match(hp, mailer))
603664562Sgshapiro			rndm[nmx] = 0;
603764562Sgshapiro		else
603864562Sgshapiro			rndm[nmx] = get_random();
603964562Sgshapiro
604064562Sgshapiro		if (endp != NULL)
604164562Sgshapiro		{
604264562Sgshapiro			/*
604364562Sgshapiro			**  Since we don't have the original MX prefs,
604464562Sgshapiro			**  make our own.  If the separator is a ':', that
604564562Sgshapiro			**  means the preference for the next host will be
604664562Sgshapiro			**  higher than this one, so simply increment curpref.
604764562Sgshapiro			*/
604864562Sgshapiro
604964562Sgshapiro			if (sep == ':')
605064562Sgshapiro				curpref++;
605164562Sgshapiro
605264562Sgshapiro			*endp++ = sep;
605364562Sgshapiro		}
605464562Sgshapiro		if (++nmx >= MAXMXHOSTS)
605564562Sgshapiro			break;
605664562Sgshapiro	}
605764562Sgshapiro
605864562Sgshapiro	/* sort the records using the random factor for equal preferences */
605964562Sgshapiro	for (i = 0; i < nmx; i++)
606064562Sgshapiro	{
606164562Sgshapiro		for (j = i + 1; j < nmx; j++)
606264562Sgshapiro		{
606364562Sgshapiro			/*
606464562Sgshapiro			**  List is already sorted by MX preference, only
606564562Sgshapiro			**  need to look for equal preference MX records
606664562Sgshapiro			*/
606764562Sgshapiro
606864562Sgshapiro			if (prefer[i] < prefer[j])
606964562Sgshapiro				break;
607064562Sgshapiro
607164562Sgshapiro			if (prefer[i] > prefer[j] ||
607264562Sgshapiro			    (prefer[i] == prefer[j] && rndm[i] > rndm[j]))
607364562Sgshapiro			{
607490792Sgshapiro				register unsigned short tempp;
607564562Sgshapiro				register long tempr;
607664562Sgshapiro				register char *temp1;
607764562Sgshapiro
607864562Sgshapiro				tempp = prefer[i];
607964562Sgshapiro				prefer[i] = prefer[j];
608064562Sgshapiro				prefer[j] = tempp;
608164562Sgshapiro				temp1 = mxhosts[i];
608264562Sgshapiro				mxhosts[i] = mxhosts[j];
608364562Sgshapiro				mxhosts[j] = temp1;
608464562Sgshapiro				tempr = rndm[i];
608564562Sgshapiro				rndm[i] = rndm[j];
608664562Sgshapiro				rndm[j] = tempr;
608764562Sgshapiro			}
608864562Sgshapiro		}
608964562Sgshapiro	}
609064562Sgshapiro	return nmx;
609164562Sgshapiro}
609264562Sgshapiro
609364562Sgshapiro# if STARTTLS
609464562Sgshapirostatic SSL_CTX	*clt_ctx = NULL;
609590792Sgshapirostatic bool	tls_ok_clt = true;
609664562Sgshapiro
609790792Sgshapiro/*
609890792Sgshapiro**  SETCLTTLS -- client side TLS: allow/disallow.
609964562Sgshapiro**
610064562Sgshapiro**	Parameters:
610190792Sgshapiro**		tls_ok -- should tls be done?
610290792Sgshapiro**
610390792Sgshapiro**	Returns:
610464562Sgshapiro**		none.
610564562Sgshapiro**
610690792Sgshapiro**	Side Effects:
610790792Sgshapiro**		sets tls_ok_clt (static variable in this module)
610890792Sgshapiro*/
610990792Sgshapiro
611090792Sgshapirovoid
611190792Sgshapirosetclttls(tls_ok)
611290792Sgshapiro	bool tls_ok;
611390792Sgshapiro{
611490792Sgshapiro	tls_ok_clt = tls_ok;
611590792Sgshapiro	return;
611690792Sgshapiro}
611790792Sgshapiro/*
611890792Sgshapiro**  INITCLTTLS -- initialize client side TLS
611990792Sgshapiro**
612090792Sgshapiro**	Parameters:
612190792Sgshapiro**		tls_ok -- should tls initialization be done?
612290792Sgshapiro**
612364562Sgshapiro**	Returns:
612464562Sgshapiro**		succeeded?
612590792Sgshapiro**
612690792Sgshapiro**	Side Effects:
612790792Sgshapiro**		sets tls_ok_clt (static variable in this module)
612864562Sgshapiro*/
612964562Sgshapiro
613064562Sgshapirobool
613190792Sgshapiroinitclttls(tls_ok)
613290792Sgshapiro	bool tls_ok;
613364562Sgshapiro{
613490792Sgshapiro	if (!tls_ok_clt)
613590792Sgshapiro		return false;
613690792Sgshapiro	tls_ok_clt = tls_ok;
613790792Sgshapiro	if (!tls_ok_clt)
613890792Sgshapiro		return false;
613964562Sgshapiro	if (clt_ctx != NULL)
614090792Sgshapiro		return true;	/* already done */
6141203004Sgshapiro	tls_ok_clt = inittls(&clt_ctx, TLS_I_CLT, Clt_SSL_Options, false,
6142203004Sgshapiro			     CltCertFile, CltKeyFile,
6143203004Sgshapiro			     CACertPath, CACertFile, DHParams);
614490792Sgshapiro	return tls_ok_clt;
614564562Sgshapiro}
614664562Sgshapiro
614790792Sgshapiro/*
614864562Sgshapiro**  STARTTLS -- try to start secure connection (client side)
614964562Sgshapiro**
615064562Sgshapiro**	Parameters:
615164562Sgshapiro**		m -- the mailer.
615264562Sgshapiro**		mci -- the mailer connection info.
615364562Sgshapiro**		e -- the envelope.
615464562Sgshapiro**
615564562Sgshapiro**	Returns:
615664562Sgshapiro**		success?
615764562Sgshapiro**		(maybe this should be some other code than EX_
615864562Sgshapiro**		that denotes which stage failed.)
615964562Sgshapiro*/
616064562Sgshapiro
616164562Sgshapirostatic int
616264562Sgshapirostarttls(m, mci, e)
616364562Sgshapiro	MAILER *m;
616464562Sgshapiro	MCI *mci;
616564562Sgshapiro	ENVELOPE *e;
616664562Sgshapiro{
616764562Sgshapiro	int smtpresult;
616866494Sgshapiro	int result = 0;
616966494Sgshapiro	int rfd, wfd;
617064562Sgshapiro	SSL *clt_ssl = NULL;
617190792Sgshapiro	time_t tlsstart;
617264562Sgshapiro
617390792Sgshapiro	if (clt_ctx == NULL && !initclttls(true))
617466494Sgshapiro		return EX_TEMPFAIL;
6175203004Sgshapiro
6176203004Sgshapiro# if USE_OPENSSL_ENGINE
6177223067Sgshapiro	if (!SSLEngineInitialized && !SSL_set_engine(NULL))
6178203004Sgshapiro	{
6179203004Sgshapiro		sm_syslog(LOG_ERR, NOQID,
6180203004Sgshapiro			  "STARTTLS=client, SSL_set_engine=failed");
6181203004Sgshapiro		return EX_TEMPFAIL;
6182203004Sgshapiro	}
6183223067Sgshapiro	SSLEngineInitialized = true;
6184203004Sgshapiro# endif /* USE_OPENSSL_ENGINE */
6185203004Sgshapiro
618664562Sgshapiro	smtpmessage("STARTTLS", m, mci);
618764562Sgshapiro
618864562Sgshapiro	/* get the reply */
6189132943Sgshapiro	smtpresult = reply(m, mci, e, TimeOuts.to_starttls, NULL, NULL,
6190132943Sgshapiro			XS_STARTTLS);
619164562Sgshapiro
619264562Sgshapiro	/* check return code from server */
6193157001Sgshapiro	if (REPLYTYPE(smtpresult) == 4)
619464562Sgshapiro		return EX_TEMPFAIL;
619564562Sgshapiro	if (smtpresult == 501)
619664562Sgshapiro		return EX_USAGE;
619764562Sgshapiro	if (smtpresult == -1)
619864562Sgshapiro		return smtpresult;
6199157001Sgshapiro
6200157001Sgshapiro	/* not an expected reply but we have to deal with it */
6201157001Sgshapiro	if (REPLYTYPE(smtpresult) == 5)
6202157001Sgshapiro		return EX_UNAVAILABLE;
620364562Sgshapiro	if (smtpresult != 220)
620464562Sgshapiro		return EX_PROTOCOL;
620564562Sgshapiro
620664562Sgshapiro	if (LogLevel > 13)
620790792Sgshapiro		sm_syslog(LOG_INFO, NOQID, "STARTTLS=client, start=ok");
620864562Sgshapiro
620964562Sgshapiro	/* start connection */
621064562Sgshapiro	if ((clt_ssl = SSL_new(clt_ctx)) == NULL)
621164562Sgshapiro	{
621264562Sgshapiro		if (LogLevel > 5)
621364562Sgshapiro		{
621490792Sgshapiro			sm_syslog(LOG_ERR, NOQID,
621590792Sgshapiro				  "STARTTLS=client, error: SSL_new failed");
621664562Sgshapiro			if (LogLevel > 9)
6217249729Sgshapiro				tlslogerr(LOG_WARNING, "client");
621864562Sgshapiro		}
621964562Sgshapiro		return EX_SOFTWARE;
622064562Sgshapiro	}
6221285303Sgshapiro	/* SSL_clear(clt_ssl); ? */
622264562Sgshapiro
6223285303Sgshapiro	if (get_tls_se_options(e, clt_ssl, false) != 0)
6224285303Sgshapiro	{
6225285303Sgshapiro		sm_syslog(LOG_ERR, NOQID,
6226285303Sgshapiro			  "STARTTLS=client, get_tls_se_options=fail");
6227285303Sgshapiro		return EX_SOFTWARE;
6228285303Sgshapiro	}
6229285303Sgshapiro
623090792Sgshapiro	rfd = sm_io_getinfo(mci->mci_in, SM_IO_WHAT_FD, NULL);
623190792Sgshapiro	wfd = sm_io_getinfo(mci->mci_out, SM_IO_WHAT_FD, NULL);
623266494Sgshapiro
623366494Sgshapiro	if (rfd < 0 || wfd < 0 ||
623490792Sgshapiro	    (result = SSL_set_rfd(clt_ssl, rfd)) != 1 ||
623590792Sgshapiro	    (result = SSL_set_wfd(clt_ssl, wfd)) != 1)
623664562Sgshapiro	{
623764562Sgshapiro		if (LogLevel > 5)
623864562Sgshapiro		{
623990792Sgshapiro			sm_syslog(LOG_ERR, NOQID,
624090792Sgshapiro				  "STARTTLS=client, error: SSL_set_xfd failed=%d",
624190792Sgshapiro				  result);
624264562Sgshapiro			if (LogLevel > 9)
6243249729Sgshapiro				tlslogerr(LOG_WARNING, "client");
624464562Sgshapiro		}
624564562Sgshapiro		return EX_SOFTWARE;
624664562Sgshapiro	}
624764562Sgshapiro	SSL_set_connect_state(clt_ssl);
624890792Sgshapiro	tlsstart = curtime();
624990792Sgshapiro
625090792Sgshapirossl_retry:
625164562Sgshapiro	if ((result = SSL_connect(clt_ssl)) <= 0)
625264562Sgshapiro	{
6253157001Sgshapiro		int i, ssl_err;
6254285303Sgshapiro		int save_errno = errno;
625564562Sgshapiro
6256157001Sgshapiro		ssl_err = SSL_get_error(clt_ssl, result);
6257157001Sgshapiro		i = tls_retry(clt_ssl, rfd, wfd, tlsstart,
6258157001Sgshapiro			TimeOuts.to_starttls, ssl_err, "client");
6259157001Sgshapiro		if (i > 0)
6260157001Sgshapiro			goto ssl_retry;
626190792Sgshapiro
6262157001Sgshapiro		if (LogLevel > 5)
626390792Sgshapiro		{
6264244833Sgshapiro			unsigned long l;
6265244833Sgshapiro			const char *sr;
6266244833Sgshapiro
6267244833Sgshapiro			l = ERR_peek_error();
6268244833Sgshapiro			sr = ERR_reason_error_string(l);
6269157001Sgshapiro			sm_syslog(LOG_WARNING, NOQID,
6270244833Sgshapiro				  "STARTTLS=client, error: connect failed=%d, reason=%s, SSL_error=%d, errno=%d, retry=%d",
6271244833Sgshapiro				  result, sr == NULL ? "unknown" : sr, ssl_err,
6272285303Sgshapiro				  save_errno, i);
6273244833Sgshapiro			if (LogLevel > 9)
6274249729Sgshapiro				tlslogerr(LOG_WARNING, "client");
6275110560Sgshapiro		}
627690792Sgshapiro
627764562Sgshapiro		SSL_free(clt_ssl);
627864562Sgshapiro		clt_ssl = NULL;
627964562Sgshapiro		return EX_SOFTWARE;
628064562Sgshapiro	}
628164562Sgshapiro	mci->mci_ssl = clt_ssl;
628290792Sgshapiro	result = tls_get_info(mci->mci_ssl, false, mci->mci_host,
628390792Sgshapiro			      &mci->mci_macro, true);
628464562Sgshapiro
628590792Sgshapiro	/* switch to use TLS... */
628664562Sgshapiro	if (sfdctls(&mci->mci_in, &mci->mci_out, mci->mci_ssl) == 0)
628764562Sgshapiro		return EX_OK;
628864562Sgshapiro
628964562Sgshapiro	/* failure */
629064562Sgshapiro	SSL_free(clt_ssl);
629164562Sgshapiro	clt_ssl = NULL;
629264562Sgshapiro	return EX_SOFTWARE;
629364562Sgshapiro}
629490792Sgshapiro/*
629564562Sgshapiro**  ENDTLSCLT -- shutdown secure connection (client side)
629664562Sgshapiro**
629764562Sgshapiro**	Parameters:
629864562Sgshapiro**		mci -- the mailer connection info.
629964562Sgshapiro**
630064562Sgshapiro**	Returns:
630164562Sgshapiro**		success?
630264562Sgshapiro*/
630390792Sgshapiro
630490792Sgshapirostatic int
630564562Sgshapiroendtlsclt(mci)
630664562Sgshapiro	MCI *mci;
630764562Sgshapiro{
630864562Sgshapiro	int r;
630964562Sgshapiro
631064562Sgshapiro	if (!bitset(MCIF_TLSACT, mci->mci_flags))
631164562Sgshapiro		return EX_OK;
631264562Sgshapiro	r = endtls(mci->mci_ssl, "client");
631364562Sgshapiro	mci->mci_flags &= ~MCIF_TLSACT;
631464562Sgshapiro	return r;
631564562Sgshapiro}
631690792Sgshapiro# endif /* STARTTLS */
631790792Sgshapiro# if STARTTLS || SASL
631890792Sgshapiro/*
631990792Sgshapiro**  ISCLTFLGSET -- check whether client flag is set.
632064562Sgshapiro**
632164562Sgshapiro**	Parameters:
632290792Sgshapiro**		e -- envelope.
632390792Sgshapiro**		flag -- flag to check in {client_flags}
632464562Sgshapiro**
632564562Sgshapiro**	Returns:
632690792Sgshapiro**		true iff flag is set.
632764562Sgshapiro*/
632864562Sgshapiro
632990792Sgshapirostatic bool
633090792Sgshapiroiscltflgset(e, flag)
633190792Sgshapiro	ENVELOPE *e;
633290792Sgshapiro	int flag;
633364562Sgshapiro{
633490792Sgshapiro	char *p;
633573188Sgshapiro
633690792Sgshapiro	p = macvalue(macid("{client_flags}"), e);
633790792Sgshapiro	if (p == NULL)
633890792Sgshapiro		return false;
633990792Sgshapiro	for (; *p != '\0'; p++)
634064562Sgshapiro	{
634190792Sgshapiro		/* look for just this one flag */
634290792Sgshapiro		if (*p == (char) flag)
634390792Sgshapiro			return true;
634464562Sgshapiro	}
634590792Sgshapiro	return false;
634664562Sgshapiro}
634790792Sgshapiro# endif /* STARTTLS || SASL */
6348