deliver.c revision 66494
138032Speter/*
264562Sgshapiro * Copyright (c) 1998-2000 Sendmail, Inc. and its suppliers.
364562Sgshapiro *	All rights reserved.
438032Speter * Copyright (c) 1983, 1995-1997 Eric P. Allman.  All rights reserved.
538032Speter * Copyright (c) 1988, 1993
638032Speter *	The Regents of the University of California.  All rights reserved.
738032Speter *
838032Speter * By using this file, you agree to the terms and conditions set
938032Speter * forth in the LICENSE file which can be found at the top level of
1038032Speter * the sendmail distribution.
1138032Speter *
1238032Speter */
1338032Speter
1438032Speter#ifndef lint
1566494Sgshapirostatic char id[] = "@(#)$Id: deliver.c,v 8.600.2.1.2.44 2000/09/21 21:52:17 ca Exp $";
1664562Sgshapiro#endif /* ! lint */
1738032Speter
1864562Sgshapiro#include <sendmail.h>
1938032Speter
2064562Sgshapiro
2138032Speter#if HASSETUSERCONTEXT
2238032Speter# include <login_cap.h>
2364562Sgshapiro#endif /* HASSETUSERCONTEXT */
2438032Speter
2564562Sgshapiro#if STARTTLS || (SASL && SFIO)
2664562Sgshapiro# include "sfsasl.h"
2764562Sgshapiro#endif /* STARTTLS || (SASL && SFIO) */
2864562Sgshapiro
2964562Sgshapirostatic int	deliver __P((ENVELOPE *, ADDRESS *));
3064562Sgshapirostatic void	dup_queue_file __P((ENVELOPE *, ENVELOPE *, int));
3164562Sgshapirostatic void	mailfiletimeout __P((void));
3264562Sgshapirostatic void	markfailure __P((ENVELOPE *, ADDRESS *, MCI *, int, bool));
3364562Sgshapirostatic int	parse_hostsignature __P((char *, char **, MAILER *));
3464562Sgshapirostatic void	sendenvelope __P((ENVELOPE *, int));
3564562Sgshapirostatic char	*hostsignature __P((MAILER *, char *));
3664562Sgshapiro
3738032Speter#if SMTP
3864562Sgshapiro# if STARTTLS
3964562Sgshapirostatic int	starttls __P((MAILER *, MCI *, ENVELOPE *));
4064562Sgshapiro# endif /* STARTTLS */
4164562Sgshapiro#endif /* SMTP */
4238032Speter
4338032Speter/*
4438032Speter**  SENDALL -- actually send all the messages.
4538032Speter**
4638032Speter**	Parameters:
4738032Speter**		e -- the envelope to send.
4838032Speter**		mode -- the delivery mode to use.  If SM_DEFAULT, use
4938032Speter**			the current e->e_sendmode.
5038032Speter**
5138032Speter**	Returns:
5238032Speter**		none.
5338032Speter**
5438032Speter**	Side Effects:
5538032Speter**		Scans the send lists and sends everything it finds.
5638032Speter**		Delivers any appropriate error messages.
5738032Speter**		If we are running in a non-interactive mode, takes the
5838032Speter**			appropriate action.
5938032Speter*/
6038032Speter
6138032Spetervoid
6238032Spetersendall(e, mode)
6338032Speter	ENVELOPE *e;
6438032Speter	int mode;
6538032Speter{
6638032Speter	register ADDRESS *q;
6738032Speter	char *owner;
6838032Speter	int otherowners;
6964562Sgshapiro	int save_errno;
7038032Speter	register ENVELOPE *ee;
7138032Speter	ENVELOPE *splitenv = NULL;
7238032Speter	int oldverbose = Verbose;
7338032Speter	bool somedeliveries = FALSE, expensive = FALSE;
7438032Speter	pid_t pid;
7538032Speter
7638032Speter	/*
7738032Speter	**  If this message is to be discarded, don't bother sending
7838032Speter	**  the message at all.
7938032Speter	*/
8038032Speter
8138032Speter	if (bitset(EF_DISCARD, e->e_flags))
8238032Speter	{
8338032Speter		if (tTd(13, 1))
8464562Sgshapiro			dprintf("sendall: discarding id %s\n", e->e_id);
8538032Speter		e->e_flags |= EF_CLRQUEUE;
8638032Speter		if (LogLevel > 4)
8738032Speter			sm_syslog(LOG_INFO, e->e_id, "discarded");
8838032Speter		markstats(e, NULL, TRUE);
8938032Speter		return;
9038032Speter	}
9138032Speter
9238032Speter	/*
9338032Speter	**  If we have had global, fatal errors, don't bother sending
9438032Speter	**  the message at all if we are in SMTP mode.  Local errors
9538032Speter	**  (e.g., a single address failing) will still cause the other
9638032Speter	**  addresses to be sent.
9738032Speter	*/
9838032Speter
9938032Speter	if (bitset(EF_FATALERRS, e->e_flags) &&
10038032Speter	    (OpMode == MD_SMTP || OpMode == MD_DAEMON))
10138032Speter	{
10238032Speter		e->e_flags |= EF_CLRQUEUE;
10338032Speter		return;
10438032Speter	}
10538032Speter
10638032Speter	/* determine actual delivery mode */
10738032Speter	if (mode == SM_DEFAULT)
10838032Speter	{
10938032Speter		mode = e->e_sendmode;
11038032Speter		if (mode != SM_VERIFY && mode != SM_DEFER &&
11138032Speter		    shouldqueue(e->e_msgpriority, e->e_ctime))
11238032Speter			mode = SM_QUEUE;
11338032Speter	}
11438032Speter
11538032Speter	if (tTd(13, 1))
11638032Speter	{
11764562Sgshapiro		dprintf("\n===== SENDALL: mode %c, id %s, e_from ",
11838032Speter			mode, e->e_id);
11938032Speter		printaddr(&e->e_from, FALSE);
12064562Sgshapiro		dprintf("\te_flags = ");
12138032Speter		printenvflags(e);
12264562Sgshapiro		dprintf("sendqueue:\n");
12338032Speter		printaddr(e->e_sendqueue, TRUE);
12438032Speter	}
12538032Speter
12638032Speter	/*
12738032Speter	**  Do any preprocessing necessary for the mode we are running.
12838032Speter	**	Check to make sure the hop count is reasonable.
12938032Speter	**	Delete sends to the sender in mailing lists.
13038032Speter	*/
13138032Speter
13238032Speter	CurEnv = e;
13338032Speter	if (tTd(62, 1))
13438032Speter		checkfds(NULL);
13538032Speter
13638032Speter	if (e->e_hopcount > MaxHopCount)
13738032Speter	{
13838032Speter		errno = 0;
13938032Speter#if QUEUE
14038032Speter		queueup(e, mode == SM_QUEUE || mode == SM_DEFER);
14164562Sgshapiro#endif /* QUEUE */
14238032Speter		e->e_flags |= EF_FATALERRS|EF_PM_NOTIFY|EF_CLRQUEUE;
14364562Sgshapiro		ExitStat = EX_UNAVAILABLE;
14464562Sgshapiro		syserr("554 5.0.0 Too many hops %d (%d max): from %s via %s, to %s",
14538032Speter			e->e_hopcount, MaxHopCount, e->e_from.q_paddr,
14638032Speter			RealHostName == NULL ? "localhost" : RealHostName,
14738032Speter			e->e_sendqueue->q_paddr);
14864562Sgshapiro		for (q = e->e_sendqueue; q != NULL; q = q->q_next)
14964562Sgshapiro		{
15064562Sgshapiro			if (QS_IS_DEAD(q->q_state))
15164562Sgshapiro				continue;
15264562Sgshapiro			q->q_state = QS_BADADDR;
15364562Sgshapiro			q->q_status = "5.4.6";
15464562Sgshapiro		}
15538032Speter		return;
15638032Speter	}
15738032Speter
15838032Speter	/*
15938032Speter	**  Do sender deletion.
16038032Speter	**
16164562Sgshapiro	**	If the sender should be queued up, skip this.
16238032Speter	**	This can happen if the name server is hosed when you
16338032Speter	**	are trying to send mail.  The result is that the sender
16438032Speter	**	is instantiated in the queue as a recipient.
16538032Speter	*/
16638032Speter
16738032Speter	if (!bitset(EF_METOO, e->e_flags) &&
16864562Sgshapiro	    !QS_IS_QUEUEUP(e->e_from.q_state))
16938032Speter	{
17038032Speter		if (tTd(13, 5))
17138032Speter		{
17264562Sgshapiro			dprintf("sendall: QS_SENDER ");
17338032Speter			printaddr(&e->e_from, FALSE);
17438032Speter		}
17564562Sgshapiro		e->e_from.q_state = QS_SENDER;
17638032Speter		(void) recipient(&e->e_from, &e->e_sendqueue, 0, e);
17738032Speter	}
17838032Speter
17938032Speter	/*
18038032Speter	**  Handle alias owners.
18138032Speter	**
18238032Speter	**	We scan up the q_alias chain looking for owners.
18338032Speter	**	We discard owners that are the same as the return path.
18438032Speter	*/
18538032Speter
18638032Speter	for (q = e->e_sendqueue; q != NULL; q = q->q_next)
18738032Speter	{
18838032Speter		register struct address *a;
18938032Speter
19038032Speter		for (a = q; a != NULL && a->q_owner == NULL; a = a->q_alias)
19138032Speter			continue;
19238032Speter		if (a != NULL)
19338032Speter			q->q_owner = a->q_owner;
19438032Speter
19538032Speter		if (q->q_owner != NULL &&
19664562Sgshapiro		    !QS_IS_DEAD(q->q_state) &&
19738032Speter		    strcmp(q->q_owner, e->e_from.q_paddr) == 0)
19838032Speter			q->q_owner = NULL;
19938032Speter	}
20038032Speter
20138032Speter	if (tTd(13, 25))
20238032Speter	{
20364562Sgshapiro		dprintf("\nAfter first owner pass, sendq =\n");
20438032Speter		printaddr(e->e_sendqueue, TRUE);
20538032Speter	}
20638032Speter
20738032Speter	owner = "";
20838032Speter	otherowners = 1;
20938032Speter	while (owner != NULL && otherowners > 0)
21038032Speter	{
21138032Speter		if (tTd(13, 28))
21264562Sgshapiro			dprintf("owner = \"%s\", otherowners = %d\n",
21338032Speter				owner, otherowners);
21438032Speter		owner = NULL;
21538032Speter		otherowners = bitset(EF_SENDRECEIPT, e->e_flags) ? 1 : 0;
21638032Speter
21738032Speter		for (q = e->e_sendqueue; q != NULL; q = q->q_next)
21838032Speter		{
21938032Speter			if (tTd(13, 30))
22038032Speter			{
22164562Sgshapiro				dprintf("Checking ");
22238032Speter				printaddr(q, FALSE);
22338032Speter			}
22464562Sgshapiro			if (QS_IS_DEAD(q->q_state))
22538032Speter			{
22638032Speter				if (tTd(13, 30))
22764562Sgshapiro					dprintf("    ... QS_IS_DEAD\n");
22838032Speter				continue;
22938032Speter			}
23038032Speter			if (tTd(13, 29) && !tTd(13, 30))
23138032Speter			{
23264562Sgshapiro				dprintf("Checking ");
23338032Speter				printaddr(q, FALSE);
23438032Speter			}
23538032Speter
23638032Speter			if (q->q_owner != NULL)
23738032Speter			{
23838032Speter				if (owner == NULL)
23938032Speter				{
24038032Speter					if (tTd(13, 40))
24164562Sgshapiro						dprintf("    ... First owner = \"%s\"\n",
24238032Speter							q->q_owner);
24338032Speter					owner = q->q_owner;
24438032Speter				}
24538032Speter				else if (owner != q->q_owner)
24638032Speter				{
24738032Speter					if (strcmp(owner, q->q_owner) == 0)
24838032Speter					{
24938032Speter						if (tTd(13, 40))
25064562Sgshapiro							dprintf("    ... Same owner = \"%s\"\n",
25138032Speter								owner);
25238032Speter
25338032Speter						/* make future comparisons cheap */
25438032Speter						q->q_owner = owner;
25538032Speter					}
25638032Speter					else
25738032Speter					{
25838032Speter						if (tTd(13, 40))
25964562Sgshapiro							dprintf("    ... Another owner \"%s\"\n",
26038032Speter								q->q_owner);
26138032Speter						otherowners++;
26238032Speter					}
26338032Speter					owner = q->q_owner;
26438032Speter				}
26538032Speter				else if (tTd(13, 40))
26664562Sgshapiro					dprintf("    ... Same owner = \"%s\"\n",
26738032Speter						owner);
26838032Speter			}
26938032Speter			else
27038032Speter			{
27138032Speter				if (tTd(13, 40))
27264562Sgshapiro					dprintf("    ... Null owner\n");
27338032Speter				otherowners++;
27438032Speter			}
27538032Speter
27664562Sgshapiro			if (QS_IS_BADADDR(q->q_state))
27764562Sgshapiro			{
27864562Sgshapiro				if (tTd(13, 30))
27964562Sgshapiro					dprintf("    ... QS_IS_BADADDR\n");
28064562Sgshapiro				continue;
28164562Sgshapiro			}
28264562Sgshapiro
28364562Sgshapiro			if (QS_IS_QUEUEUP(q->q_state))
28464562Sgshapiro			{
28564562Sgshapiro				MAILER *m = q->q_mailer;
28664562Sgshapiro
28764562Sgshapiro				/*
28864562Sgshapiro				**  If we have temporary address failures
28964562Sgshapiro				**  (e.g., dns failure) and a fallback MX is
29064562Sgshapiro				**  set, send directly to the fallback MX host.
29164562Sgshapiro				*/
29264562Sgshapiro
29364562Sgshapiro				if (FallBackMX != NULL &&
29464562Sgshapiro				    !wordinclass(FallBackMX, 'w') &&
29564562Sgshapiro				    mode != SM_VERIFY &&
29664562Sgshapiro				    (strcmp(m->m_mailer, "[IPC]") == 0 ||
29764562Sgshapiro				     strcmp(m->m_mailer, "[TCP]") == 0) &&
29864562Sgshapiro				    m->m_argv[0] != NULL &&
29964562Sgshapiro				    (strcmp(m->m_argv[0], "TCP") == 0 ||
30064562Sgshapiro				     strcmp(m->m_argv[0], "IPC") == 0))
30164562Sgshapiro				{
30264562Sgshapiro					int len;
30364562Sgshapiro					char *p;
30464562Sgshapiro
30564562Sgshapiro					if (tTd(13, 30))
30664562Sgshapiro						dprintf("    ... FallBackMX\n");
30764562Sgshapiro
30864562Sgshapiro					len = strlen(FallBackMX) + 3;
30964562Sgshapiro					p = xalloc(len);
31064562Sgshapiro					snprintf(p, len, "[%s]", FallBackMX);
31164562Sgshapiro					q->q_state = QS_OK;
31264562Sgshapiro					q->q_host = p;
31364562Sgshapiro				}
31464562Sgshapiro				else
31564562Sgshapiro				{
31664562Sgshapiro					if (tTd(13, 30))
31764562Sgshapiro						dprintf("    ... QS_IS_QUEUEUP\n");
31864562Sgshapiro					continue;
31964562Sgshapiro				}
32064562Sgshapiro			}
32164562Sgshapiro
32238032Speter			/*
32338032Speter			**  If this mailer is expensive, and if we don't
32438032Speter			**  want to make connections now, just mark these
32538032Speter			**  addresses and return.  This is useful if we
32638032Speter			**  want to batch connections to reduce load.  This
32738032Speter			**  will cause the messages to be queued up, and a
32838032Speter			**  daemon will come along to send the messages later.
32938032Speter			*/
33038032Speter
33164562Sgshapiro			if (NoConnect && !Verbose &&
33264562Sgshapiro			    bitnset(M_EXPENSIVE, q->q_mailer->m_flags))
33338032Speter			{
33438032Speter				if (tTd(13, 30))
33564562Sgshapiro					dprintf("    ... expensive\n");
33664562Sgshapiro				q->q_state = QS_QUEUEUP;
33764562Sgshapiro				expensive = TRUE;
33838032Speter			}
33964562Sgshapiro			else if (bitnset(M_HOLD, q->q_mailer->m_flags) &&
34064562Sgshapiro				 QueueLimitId == NULL &&
34164562Sgshapiro				 QueueLimitSender == NULL &&
34264562Sgshapiro				 QueueLimitRecipient == NULL)
34338032Speter			{
34438032Speter				if (tTd(13, 30))
34564562Sgshapiro					dprintf("    ... hold\n");
34664562Sgshapiro				q->q_state = QS_QUEUEUP;
34738032Speter				expensive = TRUE;
34838032Speter			}
34938032Speter			else
35038032Speter			{
35138032Speter				if (tTd(13, 30))
35264562Sgshapiro					dprintf("    ... deliverable\n");
35338032Speter				somedeliveries = TRUE;
35438032Speter			}
35538032Speter		}
35638032Speter
35738032Speter		if (owner != NULL && otherowners > 0)
35838032Speter		{
35938032Speter			/*
36038032Speter			**  Split this envelope into two.
36138032Speter			*/
36238032Speter
36364562Sgshapiro			ee = (ENVELOPE *) xalloc(sizeof *ee);
36438032Speter			*ee = *e;
36564562Sgshapiro			ee->e_message = NULL;
36638032Speter			ee->e_id = NULL;
36764562Sgshapiro			assign_queueid(ee);
36838032Speter
36938032Speter			if (tTd(13, 1))
37064562Sgshapiro				dprintf("sendall: split %s into %s, owner = \"%s\", otherowners = %d\n",
37138032Speter					e->e_id, ee->e_id, owner, otherowners);
37238032Speter
37338032Speter			ee->e_header = copyheader(e->e_header);
37438032Speter			ee->e_sendqueue = copyqueue(e->e_sendqueue);
37538032Speter			ee->e_errorqueue = copyqueue(e->e_errorqueue);
37638032Speter			ee->e_flags = e->e_flags & ~(EF_INQUEUE|EF_CLRQUEUE|EF_FATALERRS|EF_SENDRECEIPT|EF_RET_PARAM);
37738032Speter			ee->e_flags |= EF_NORECEIPT;
37838032Speter			setsender(owner, ee, NULL, '\0', TRUE);
37938032Speter			if (tTd(13, 5))
38038032Speter			{
38164562Sgshapiro				dprintf("sendall(split): QS_SENDER ");
38238032Speter				printaddr(&ee->e_from, FALSE);
38338032Speter			}
38464562Sgshapiro			ee->e_from.q_state = QS_SENDER;
38538032Speter			ee->e_dfp = NULL;
38664562Sgshapiro			ee->e_lockfp = NULL;
38738032Speter			ee->e_xfp = NULL;
38864562Sgshapiro			ee->e_queuedir = e->e_queuedir;
38938032Speter			ee->e_errormode = EM_MAIL;
39038032Speter			ee->e_sibling = splitenv;
39164562Sgshapiro			ee->e_statmsg = NULL;
39238032Speter			splitenv = ee;
39338032Speter
39438032Speter			for (q = e->e_sendqueue; q != NULL; q = q->q_next)
39538032Speter			{
39638032Speter				if (q->q_owner == owner)
39738032Speter				{
39864562Sgshapiro					q->q_state = QS_CLONED;
39938032Speter					if (tTd(13, 6))
40064562Sgshapiro						dprintf("\t... stripping %s from original envelope\n",
40138032Speter							q->q_paddr);
40238032Speter				}
40338032Speter			}
40438032Speter			for (q = ee->e_sendqueue; q != NULL; q = q->q_next)
40538032Speter			{
40638032Speter				if (q->q_owner != owner)
40738032Speter				{
40864562Sgshapiro					q->q_state = QS_CLONED;
40938032Speter					if (tTd(13, 6))
41064562Sgshapiro						dprintf("\t... dropping %s from cloned envelope\n",
41138032Speter							q->q_paddr);
41238032Speter				}
41338032Speter				else
41438032Speter				{
41538032Speter					/* clear DSN parameters */
41638032Speter					q->q_flags &= ~(QHASNOTIFY|Q_PINGFLAGS);
41738032Speter					q->q_flags |= DefaultNotify & ~QPINGONSUCCESS;
41838032Speter					if (tTd(13, 6))
41964562Sgshapiro						dprintf("\t... moving %s to cloned envelope\n",
42038032Speter							q->q_paddr);
42138032Speter				}
42238032Speter			}
42338032Speter
42438032Speter			if (mode != SM_VERIFY && bitset(EF_HAS_DF, e->e_flags))
42538032Speter				dup_queue_file(e, ee, 'd');
42664562Sgshapiro
42764562Sgshapiro			/*
42864562Sgshapiro			**  Give the split envelope access to the parent
42964562Sgshapiro			**  transcript file for errors obtained while
43064562Sgshapiro			**  processing the recipients (done before the
43164562Sgshapiro			**  envelope splitting).
43264562Sgshapiro			*/
43364562Sgshapiro
43464562Sgshapiro			if (e->e_xfp != NULL)
43564562Sgshapiro				ee->e_xfp = bfdup(e->e_xfp);
43664562Sgshapiro
43764562Sgshapiro			/* failed to dup e->e_xfp, start a new transcript */
43864562Sgshapiro			if (ee->e_xfp == NULL)
43964562Sgshapiro				openxscript(ee);
44064562Sgshapiro
44142575Speter			if (mode != SM_VERIFY && LogLevel > 4)
44238032Speter				sm_syslog(LOG_INFO, ee->e_id,
44364562Sgshapiro					  "clone %s, owner=%s",
44464562Sgshapiro					  e->e_id, owner);
44538032Speter		}
44638032Speter	}
44738032Speter
44838032Speter	if (owner != NULL)
44938032Speter	{
45038032Speter		setsender(owner, e, NULL, '\0', TRUE);
45138032Speter		if (tTd(13, 5))
45238032Speter		{
45364562Sgshapiro			dprintf("sendall(owner): QS_SENDER ");
45438032Speter			printaddr(&e->e_from, FALSE);
45538032Speter		}
45664562Sgshapiro		e->e_from.q_state = QS_SENDER;
45738032Speter		e->e_errormode = EM_MAIL;
45838032Speter		e->e_flags |= EF_NORECEIPT;
45938032Speter		e->e_flags &= ~EF_FATALERRS;
46038032Speter	}
46138032Speter
46238032Speter	/* if nothing to be delivered, just queue up everything */
46338032Speter	if (!somedeliveries && mode != SM_QUEUE && mode != SM_DEFER &&
46438032Speter	    mode != SM_VERIFY)
46538032Speter	{
46638032Speter		if (tTd(13, 29))
46764562Sgshapiro			dprintf("No deliveries: auto-queuing\n");
46838032Speter		mode = SM_QUEUE;
46938032Speter
47038032Speter		/* treat this as a delivery in terms of counting tries */
47138032Speter		e->e_dtime = curtime();
47238032Speter		if (!expensive)
47338032Speter			e->e_ntries++;
47438032Speter		for (ee = splitenv; ee != NULL; ee = ee->e_sibling)
47538032Speter		{
47638032Speter			ee->e_dtime = curtime();
47738032Speter			if (!expensive)
47838032Speter				ee->e_ntries++;
47938032Speter		}
48038032Speter	}
48138032Speter
48264562Sgshapiro#if QUEUE
48338032Speter	if ((mode == SM_QUEUE || mode == SM_DEFER || mode == SM_FORK ||
48438032Speter	     (mode != SM_VERIFY && SuperSafe)) &&
48538032Speter	    (!bitset(EF_INQUEUE, e->e_flags) || splitenv != NULL))
48638032Speter	{
48766494Sgshapiro		/*
48866494Sgshapiro		**  Be sure everything is instantiated in the queue.
48966494Sgshapiro		**  Split envelopes first in case the machine crashes.
49066494Sgshapiro		**  If the original were done first, we may lose
49166494Sgshapiro		**  recipients.
49266494Sgshapiro		*/
49366494Sgshapiro
49438032Speter		for (ee = splitenv; ee != NULL; ee = ee->e_sibling)
49538032Speter			queueup(ee, mode == SM_QUEUE || mode == SM_DEFER);
49666494Sgshapiro		queueup(e, mode == SM_QUEUE || mode == SM_DEFER);
49738032Speter	}
49838032Speter#endif /* QUEUE */
49938032Speter
50038032Speter	if (tTd(62, 10))
50138032Speter		checkfds("after envelope splitting");
50238032Speter
50338032Speter	/*
50438032Speter	**  If we belong in background, fork now.
50538032Speter	*/
50638032Speter
50738032Speter	if (tTd(13, 20))
50838032Speter	{
50964562Sgshapiro		dprintf("sendall: final mode = %c\n", mode);
51038032Speter		if (tTd(13, 21))
51138032Speter		{
51264562Sgshapiro			dprintf("\n================ Final Send Queue(s) =====================\n");
51364562Sgshapiro			dprintf("\n  *** Envelope %s, e_from=%s ***\n",
51438032Speter				e->e_id, e->e_from.q_paddr);
51538032Speter			printaddr(e->e_sendqueue, TRUE);
51638032Speter			for (ee = splitenv; ee != NULL; ee = ee->e_sibling)
51738032Speter			{
51864562Sgshapiro				dprintf("\n  *** Envelope %s, e_from=%s ***\n",
51938032Speter					ee->e_id, ee->e_from.q_paddr);
52038032Speter				printaddr(ee->e_sendqueue, TRUE);
52138032Speter			}
52264562Sgshapiro			dprintf("==========================================================\n\n");
52338032Speter		}
52438032Speter	}
52538032Speter	switch (mode)
52638032Speter	{
52738032Speter	  case SM_VERIFY:
52838032Speter		Verbose = 2;
52938032Speter		break;
53038032Speter
53138032Speter	  case SM_QUEUE:
53238032Speter	  case SM_DEFER:
53364562Sgshapiro#if HASFLOCK
53438032Speter  queueonly:
53564562Sgshapiro#endif /* HASFLOCK */
53638032Speter		if (e->e_nrcpts > 0)
53738032Speter			e->e_flags |= EF_INQUEUE;
53838032Speter		dropenvelope(e, splitenv != NULL);
53938032Speter		for (ee = splitenv; ee != NULL; ee = ee->e_sibling)
54038032Speter		{
54138032Speter			if (ee->e_nrcpts > 0)
54238032Speter				ee->e_flags |= EF_INQUEUE;
54338032Speter			dropenvelope(ee, FALSE);
54438032Speter		}
54538032Speter		return;
54638032Speter
54738032Speter	  case SM_FORK:
54838032Speter		if (e->e_xfp != NULL)
54938032Speter			(void) fflush(e->e_xfp);
55038032Speter
55164562Sgshapiro#if !HASFLOCK
55238032Speter		/*
55338032Speter		**  Since fcntl locking has the interesting semantic that
55438032Speter		**  the lock is owned by a process, not by an open file
55538032Speter		**  descriptor, we have to flush this to the queue, and
55638032Speter		**  then restart from scratch in the child.
55738032Speter		*/
55838032Speter
55938032Speter		{
56038032Speter			/* save id for future use */
56138032Speter			char *qid = e->e_id;
56238032Speter
56338032Speter			/* now drop the envelope in the parent */
56438032Speter			e->e_flags |= EF_INQUEUE;
56538032Speter			dropenvelope(e, splitenv != NULL);
56638032Speter
56738032Speter			/* arrange to reacquire lock after fork */
56838032Speter			e->e_id = qid;
56938032Speter		}
57038032Speter
57138032Speter		for (ee = splitenv; ee != NULL; ee = ee->e_sibling)
57238032Speter		{
57338032Speter			/* save id for future use */
57438032Speter			char *qid = ee->e_id;
57538032Speter
57638032Speter			/* drop envelope in parent */
57738032Speter			ee->e_flags |= EF_INQUEUE;
57838032Speter			dropenvelope(ee, FALSE);
57938032Speter
58038032Speter			/* and save qid for reacquisition */
58138032Speter			ee->e_id = qid;
58238032Speter		}
58338032Speter
58464562Sgshapiro#endif /* !HASFLOCK */
58538032Speter
58664562Sgshapiro		/*
58764562Sgshapiro		**  Since the delivery may happen in a child and the parent
58864562Sgshapiro		**  does not wait, the parent may close the maps thereby
58964562Sgshapiro		**  removing any shared memory used by the map.  Therefore,
59064562Sgshapiro		**  close the maps now so the child will dynamically open
59164562Sgshapiro		**  them if necessary.
59264562Sgshapiro		*/
59364562Sgshapiro
59464562Sgshapiro		closemaps();
59564562Sgshapiro
59638032Speter		pid = fork();
59738032Speter		if (pid < 0)
59838032Speter		{
59964562Sgshapiro			syserr("deliver: fork 1");
60064562Sgshapiro#if HASFLOCK
60138032Speter			goto queueonly;
60264562Sgshapiro#else /* HASFLOCK */
60338032Speter			e->e_id = NULL;
60438032Speter			for (ee = splitenv; ee != NULL; ee = ee->e_sibling)
60538032Speter				ee->e_id = NULL;
60638032Speter			return;
60764562Sgshapiro#endif /* HASFLOCK */
60838032Speter		}
60938032Speter		else if (pid > 0)
61038032Speter		{
61164562Sgshapiro#if HASFLOCK
61238032Speter			/* be sure we leave the temp files to our child */
61338032Speter			/* close any random open files in the envelope */
61438032Speter			closexscript(e);
61538032Speter			if (e->e_dfp != NULL)
61664562Sgshapiro				(void) bfclose(e->e_dfp);
61738032Speter			e->e_dfp = NULL;
61838032Speter			e->e_flags &= ~EF_HAS_DF;
61938032Speter
62038032Speter			/* can't call unlockqueue to avoid unlink of xfp */
62138032Speter			if (e->e_lockfp != NULL)
62264562Sgshapiro				(void) fclose(e->e_lockfp);
62364562Sgshapiro			else
62464562Sgshapiro				syserr("%s: sendall: null lockfp", e->e_id);
62538032Speter			e->e_lockfp = NULL;
62664562Sgshapiro#endif /* HASFLOCK */
62738032Speter
62838032Speter			/* make sure the parent doesn't own the envelope */
62938032Speter			e->e_id = NULL;
63038032Speter
63138032Speter			/* catch intermediate zombie */
63238032Speter			(void) waitfor(pid);
63338032Speter			return;
63438032Speter		}
63538032Speter
63666494Sgshapiro		/*
63766494Sgshapiro		**  Since we have accepted responsbility for the message,
63866494Sgshapiro		**  change the SIGTERM handler.  intsig() (the old handler)
63966494Sgshapiro		**  would remove the envelope if this was a command line
64066494Sgshapiro		**  message submission.
64166494Sgshapiro		*/
64266494Sgshapiro
64366494Sgshapiro		(void) setsignal(SIGTERM, SIG_DFL);
64466494Sgshapiro
64538032Speter		/* double fork to avoid zombies */
64638032Speter		pid = fork();
64738032Speter		if (pid > 0)
64838032Speter			exit(EX_OK);
64964562Sgshapiro		save_errno = errno;
65038032Speter
65138032Speter		/* be sure we are immune from the terminal */
65238032Speter		disconnect(2, e);
65364562Sgshapiro		clearstats();
65438032Speter
65538032Speter		/* prevent parent from waiting if there was an error */
65638032Speter		if (pid < 0)
65738032Speter		{
65864562Sgshapiro			errno = save_errno;
65964562Sgshapiro			syserr("deliver: fork 2");
66064562Sgshapiro#if HASFLOCK
66138032Speter			e->e_flags |= EF_INQUEUE;
66264562Sgshapiro#else /* HASFLOCK */
66338032Speter			e->e_id = NULL;
66464562Sgshapiro#endif /* HASFLOCK */
66542575Speter			finis(TRUE, ExitStat);
66638032Speter		}
66738032Speter
66838032Speter		/* be sure to give error messages in child */
66938032Speter		QuickAbort = FALSE;
67038032Speter
67138032Speter		/*
67238032Speter		**  Close any cached connections.
67338032Speter		**
67438032Speter		**	We don't send the QUIT protocol because the parent
67538032Speter		**	still knows about the connection.
67638032Speter		**
67738032Speter		**	This should only happen when delivering an error
67838032Speter		**	message.
67938032Speter		*/
68038032Speter
68138032Speter		mci_flush(FALSE, NULL);
68238032Speter
68364562Sgshapiro#if HASFLOCK
68438032Speter		break;
68564562Sgshapiro#else /* HASFLOCK */
68638032Speter
68738032Speter		/*
68838032Speter		**  Now reacquire and run the various queue files.
68938032Speter		*/
69038032Speter
69138032Speter		for (ee = splitenv; ee != NULL; ee = ee->e_sibling)
69238032Speter		{
69338032Speter			ENVELOPE *sibling = ee->e_sibling;
69438032Speter
69564562Sgshapiro			(void) dowork(ee->e_queuedir, ee->e_id,
69664562Sgshapiro				      FALSE, FALSE, ee);
69738032Speter			ee->e_sibling = sibling;
69838032Speter		}
69964562Sgshapiro		(void) dowork(e->e_queuedir, e->e_id,
70064562Sgshapiro			      FALSE, FALSE, e);
70142575Speter		finis(TRUE, ExitStat);
70264562Sgshapiro#endif /* HASFLOCK */
70338032Speter	}
70438032Speter
70538032Speter	sendenvelope(e, mode);
70638032Speter	dropenvelope(e, TRUE);
70738032Speter	for (ee = splitenv; ee != NULL; ee = ee->e_sibling)
70838032Speter	{
70938032Speter		CurEnv = ee;
71038032Speter		if (mode != SM_VERIFY)
71138032Speter			openxscript(ee);
71238032Speter		sendenvelope(ee, mode);
71338032Speter		dropenvelope(ee, TRUE);
71438032Speter	}
71538032Speter	CurEnv = e;
71638032Speter
71738032Speter	Verbose = oldverbose;
71838032Speter	if (mode == SM_FORK)
71942575Speter		finis(TRUE, ExitStat);
72038032Speter}
72138032Speter
72264562Sgshapirostatic void
72338032Spetersendenvelope(e, mode)
72438032Speter	register ENVELOPE *e;
72538032Speter	int mode;
72638032Speter{
72738032Speter	register ADDRESS *q;
72838032Speter	bool didany;
72938032Speter
73038032Speter	if (tTd(13, 10))
73164562Sgshapiro		dprintf("sendenvelope(%s) e_flags=0x%lx\n",
73238032Speter			e->e_id == NULL ? "[NOQUEUE]" : e->e_id,
73338032Speter			e->e_flags);
73438032Speter	if (LogLevel > 80)
73538032Speter		sm_syslog(LOG_DEBUG, e->e_id,
73664562Sgshapiro			  "sendenvelope, flags=0x%lx",
73764562Sgshapiro			  e->e_flags);
73838032Speter
73938032Speter	/*
74038032Speter	**  If we have had global, fatal errors, don't bother sending
74138032Speter	**  the message at all if we are in SMTP mode.  Local errors
74238032Speter	**  (e.g., a single address failing) will still cause the other
74338032Speter	**  addresses to be sent.
74438032Speter	*/
74538032Speter
74638032Speter	if (bitset(EF_FATALERRS, e->e_flags) &&
74738032Speter	    (OpMode == MD_SMTP || OpMode == MD_DAEMON))
74838032Speter	{
74938032Speter		e->e_flags |= EF_CLRQUEUE;
75038032Speter		return;
75138032Speter	}
75238032Speter
75364562Sgshapiro	/* Don't attempt deliveries if we want to bounce now */
75464562Sgshapiro	if (!bitset(EF_RESPONSE, e->e_flags) &&
75564562Sgshapiro	    TimeOuts.to_q_return[e->e_timeoutclass] == NOW)
75664562Sgshapiro		return;
75764562Sgshapiro
75838032Speter	/*
75938032Speter	**  Run through the list and send everything.
76038032Speter	**
76138032Speter	**	Set EF_GLOBALERRS so that error messages during delivery
76238032Speter	**	result in returned mail.
76338032Speter	*/
76438032Speter
76538032Speter	e->e_nsent = 0;
76638032Speter	e->e_flags |= EF_GLOBALERRS;
76764562Sgshapiro
76838032Speter	define(macid("{envid}", NULL), e->e_envid, e);
76938032Speter	define(macid("{bodytype}", NULL), e->e_bodytype, e);
77038032Speter	didany = FALSE;
77138032Speter
77238032Speter	/* now run through the queue */
77338032Speter	for (q = e->e_sendqueue; q != NULL; q = q->q_next)
77438032Speter	{
77538032Speter#if XDEBUG
77638032Speter		char wbuf[MAXNAME + 20];
77738032Speter
77838032Speter		(void) snprintf(wbuf, sizeof wbuf, "sendall(%.*s)",
77938032Speter			MAXNAME, q->q_paddr);
78038032Speter		checkfd012(wbuf);
78164562Sgshapiro#endif /* XDEBUG */
78238032Speter		if (mode == SM_VERIFY)
78338032Speter		{
78438032Speter			e->e_to = q->q_paddr;
78564562Sgshapiro			if (QS_IS_SENDABLE(q->q_state))
78638032Speter			{
78738032Speter				if (q->q_host != NULL && q->q_host[0] != '\0')
78838032Speter					message("deliverable: mailer %s, host %s, user %s",
78938032Speter						q->q_mailer->m_name,
79038032Speter						q->q_host,
79138032Speter						q->q_user);
79238032Speter				else
79338032Speter					message("deliverable: mailer %s, user %s",
79438032Speter						q->q_mailer->m_name,
79538032Speter						q->q_user);
79638032Speter			}
79738032Speter		}
79864562Sgshapiro		else if (QS_IS_OK(q->q_state))
79938032Speter		{
80064562Sgshapiro#if QUEUE
80138032Speter			/*
80238032Speter			**  Checkpoint the send list every few addresses
80338032Speter			*/
80438032Speter
80566494Sgshapiro			if (CheckpointInterval > 0 &&
80666494Sgshapiro			    e->e_nsent >= CheckpointInterval)
80738032Speter			{
80838032Speter				queueup(e, FALSE);
80938032Speter				e->e_nsent = 0;
81038032Speter			}
81164562Sgshapiro#endif /* QUEUE */
81238032Speter			(void) deliver(e, q);
81338032Speter			didany = TRUE;
81438032Speter		}
81538032Speter	}
81638032Speter	if (didany)
81738032Speter	{
81838032Speter		e->e_dtime = curtime();
81938032Speter		e->e_ntries++;
82038032Speter	}
82138032Speter
82238032Speter#if XDEBUG
82338032Speter	checkfd012("end of sendenvelope");
82464562Sgshapiro#endif /* XDEBUG */
82538032Speter}
82638032Speter/*
82738032Speter**  DUP_QUEUE_FILE -- duplicate a queue file into a split queue
82838032Speter**
82938032Speter**	Parameters:
83038032Speter**		e -- the existing envelope
83138032Speter**		ee -- the new envelope
83238032Speter**		type -- the queue file type (e.g., 'd')
83338032Speter**
83438032Speter**	Returns:
83538032Speter**		none
83638032Speter*/
83738032Speter
83864562Sgshapirostatic void
83938032Speterdup_queue_file(e, ee, type)
84038032Speter	struct envelope *e, *ee;
84138032Speter	int type;
84238032Speter{
84364562Sgshapiro	char f1buf[MAXPATHLEN], f2buf[MAXPATHLEN];
84438032Speter
84538032Speter	ee->e_dfp = NULL;
84638032Speter	ee->e_xfp = NULL;
84764562Sgshapiro
84864562Sgshapiro	/*
84964562Sgshapiro	**  Make sure both are in the same directory.
85064562Sgshapiro	*/
85164562Sgshapiro
85238032Speter	snprintf(f1buf, sizeof f1buf, "%s", queuename(e, type));
85338032Speter	snprintf(f2buf, sizeof f2buf, "%s", queuename(ee, type));
85438032Speter	if (link(f1buf, f2buf) < 0)
85538032Speter	{
85664562Sgshapiro		int save_errno = errno;
85738032Speter
85838032Speter		syserr("sendall: link(%s, %s)", f1buf, f2buf);
85964562Sgshapiro		if (save_errno == EEXIST)
86038032Speter		{
86138032Speter			if (unlink(f2buf) < 0)
86238032Speter			{
86338032Speter				syserr("!sendall: unlink(%s): permanent",
86438032Speter					f2buf);
86564562Sgshapiro				/* NOTREACHED */
86638032Speter			}
86738032Speter			if (link(f1buf, f2buf) < 0)
86838032Speter			{
86938032Speter				syserr("!sendall: link(%s, %s): permanent",
87038032Speter					f1buf, f2buf);
87164562Sgshapiro				/* NOTREACHED */
87238032Speter			}
87338032Speter		}
87438032Speter	}
87538032Speter}
87638032Speter/*
87738032Speter**  DOFORK -- do a fork, retrying a couple of times on failure.
87838032Speter**
87938032Speter**	This MUST be a macro, since after a vfork we are running
88038032Speter**	two processes on the same stack!!!
88138032Speter**
88238032Speter**	Parameters:
88338032Speter**		none.
88438032Speter**
88538032Speter**	Returns:
88638032Speter**		From a macro???  You've got to be kidding!
88738032Speter**
88838032Speter**	Side Effects:
88938032Speter**		Modifies the ==> LOCAL <== variable 'pid', leaving:
89038032Speter**			pid of child in parent, zero in child.
89138032Speter**			-1 on unrecoverable error.
89238032Speter**
89338032Speter**	Notes:
89438032Speter**		I'm awfully sorry this looks so awful.  That's
89538032Speter**		vfork for you.....
89638032Speter*/
89738032Speter
89864562Sgshapiro#define NFORKTRIES	5
89938032Speter
90064562Sgshapiro#ifndef FORK
90138032Speter# define FORK	fork
90264562Sgshapiro#endif /* ! FORK */
90338032Speter
90464562Sgshapiro#define DOFORK(fORKfN) \
90538032Speter{\
90638032Speter	register int i;\
90738032Speter\
90838032Speter	for (i = NFORKTRIES; --i >= 0; )\
90938032Speter	{\
91038032Speter		pid = fORKfN();\
91138032Speter		if (pid >= 0)\
91238032Speter			break;\
91338032Speter		if (i > 0)\
91464562Sgshapiro			(void) sleep((unsigned) NFORKTRIES - i);\
91538032Speter	}\
91638032Speter}
91738032Speter/*
91838032Speter**  DOFORK -- simple fork interface to DOFORK.
91938032Speter**
92038032Speter**	Parameters:
92138032Speter**		none.
92238032Speter**
92338032Speter**	Returns:
92438032Speter**		pid of child in parent.
92538032Speter**		zero in child.
92638032Speter**		-1 on error.
92738032Speter**
92838032Speter**	Side Effects:
92938032Speter**		returns twice, once in parent and once in child.
93038032Speter*/
93138032Speter
93238032Speterint
93338032Speterdofork()
93438032Speter{
93538032Speter	register pid_t pid = -1;
93638032Speter
93738032Speter	DOFORK(fork);
93864562Sgshapiro	return pid;
93938032Speter}
94038032Speter/*
94138032Speter**  DELIVER -- Deliver a message to a list of addresses.
94238032Speter**
94338032Speter**	This routine delivers to everyone on the same host as the
94438032Speter**	user on the head of the list.  It is clever about mailers
94538032Speter**	that don't handle multiple users.  It is NOT guaranteed
94638032Speter**	that it will deliver to all these addresses however -- so
94738032Speter**	deliver should be called once for each address on the
94838032Speter**	list.
94938032Speter**
95038032Speter**	Parameters:
95138032Speter**		e -- the envelope to deliver.
95238032Speter**		firstto -- head of the address list to deliver to.
95338032Speter**
95438032Speter**	Returns:
95538032Speter**		zero -- successfully delivered.
95638032Speter**		else -- some failure, see ExitStat for more info.
95738032Speter**
95838032Speter**	Side Effects:
95938032Speter**		The standard input is passed off to someone.
96038032Speter*/
96138032Speter
96238032Speter#ifndef NO_UID
96338032Speter# define NO_UID		-1
96464562Sgshapiro#endif /* ! NO_UID */
96538032Speter#ifndef NO_GID
96638032Speter# define NO_GID		-1
96764562Sgshapiro#endif /* ! NO_GID */
96838032Speter
96964562Sgshapirostatic int
97038032Speterdeliver(e, firstto)
97138032Speter	register ENVELOPE *e;
97238032Speter	ADDRESS *firstto;
97338032Speter{
97438032Speter	char *host;			/* host being sent to */
97538032Speter	char *user;			/* user being sent to */
97638032Speter	char **pvp;
97738032Speter	register char **mvp;
97838032Speter	register char *p;
97938032Speter	register MAILER *m;		/* mailer for this recipient */
98038032Speter	ADDRESS *volatile ctladdr;
98138032Speter	ADDRESS *volatile contextaddr = NULL;
98238032Speter	register MCI *volatile mci;
98338032Speter	register ADDRESS *to = firstto;
98438032Speter	volatile bool clever = FALSE;	/* running user smtp to this mailer */
98538032Speter	ADDRESS *volatile tochain = NULL; /* users chain in this mailer call */
98638032Speter	int rcode;			/* response code */
98738032Speter	int lmtp_rcode = EX_OK;
98864562Sgshapiro	int nummxhosts = 0;		/* number of MX hosts available */
98964562Sgshapiro	int hostnum = 0;		/* current MX host index */
99038032Speter	char *firstsig;			/* signature of firstto */
99138032Speter	pid_t pid = -1;
99238032Speter	char *volatile curhost;
99338032Speter	register u_short port = 0;
99464562Sgshapiro#if NETUNIX
99564562Sgshapiro	char *mux_path = NULL;		/* path to UNIX domain socket */
99664562Sgshapiro#endif /* NETUNIX */
99738032Speter	time_t xstart;
99838032Speter	bool suidwarn;
99938032Speter	bool anyok;			/* at least one address was OK */
100038032Speter	bool goodmxfound = FALSE;	/* at least one MX was OK */
100164562Sgshapiro	bool ovr;
100264562Sgshapiro#if _FFR_DYNAMIC_TOBUF
100364562Sgshapiro	int strsize;
100464562Sgshapiro	int rcptcount;
100564562Sgshapiro	static int tobufsize = 0;
100664562Sgshapiro	static char *tobuf = NULL;
100764562Sgshapiro#else /* _FFR_DYNAMIC_TOBUF */
100864562Sgshapiro	char tobuf[TOBUFSIZE];		/* text line of to people */
100964562Sgshapiro#endif /* _FFR_DYNAMIC_TOBUF */
101038032Speter	int mpvect[2];
101138032Speter	int rpvect[2];
101264562Sgshapiro	char *mxhosts[MAXMXHOSTS + 1];
101364562Sgshapiro	char *pv[MAXPV + 1];
101438032Speter	char buf[MAXNAME + 1];
101538032Speter	char rpathbuf[MAXNAME + 1];	/* translated return path */
101638032Speter
101738032Speter	errno = 0;
101864562Sgshapiro	if (!QS_IS_OK(to->q_state))
101964562Sgshapiro		return 0;
102038032Speter
102138032Speter	suidwarn = geteuid() == 0;
102238032Speter
102338032Speter	m = to->q_mailer;
102438032Speter	host = to->q_host;
102538032Speter	CurEnv = e;			/* just in case */
102638032Speter	e->e_statmsg = NULL;
102738032Speter#if SMTP
102838032Speter	SmtpError[0] = '\0';
102964562Sgshapiro#endif /* SMTP */
103038032Speter	xstart = curtime();
103138032Speter
103238032Speter	if (tTd(10, 1))
103364562Sgshapiro		dprintf("\n--deliver, id=%s, mailer=%s, host=`%s', first user=`%s'\n",
103438032Speter			e->e_id, m->m_name, host, to->q_user);
103538032Speter	if (tTd(10, 100))
103638032Speter		printopenfds(FALSE);
103738032Speter
103838032Speter	/*
103938032Speter	**  Clear $&{client_*} macros if this is a bounce message to
104038032Speter	**  prevent rejection by check_compat ruleset.
104138032Speter	*/
104264562Sgshapiro
104338032Speter	if (bitset(EF_RESPONSE, e->e_flags))
104438032Speter	{
104538032Speter		define(macid("{client_name}", NULL), "", e);
104638032Speter		define(macid("{client_addr}", NULL), "", e);
104738032Speter		define(macid("{client_port}", NULL), "", e);
104838032Speter	}
104964562Sgshapiro
105038032Speter	/*
105138032Speter	**  Do initial argv setup.
105238032Speter	**	Insert the mailer name.  Notice that $x expansion is
105338032Speter	**	NOT done on the mailer name.  Then, if the mailer has
105438032Speter	**	a picky -f flag, we insert it as appropriate.  This
105538032Speter	**	code does not check for 'pv' overflow; this places a
105638032Speter	**	manifest lower limit of 4 for MAXPV.
105738032Speter	**		The from address rewrite is expected to make
105838032Speter	**		the address relative to the other end.
105938032Speter	*/
106038032Speter
106138032Speter	/* rewrite from address, using rewriting rules */
106238032Speter	rcode = EX_OK;
106338032Speter	if (bitnset(M_UDBENVELOPE, e->e_from.q_mailer->m_flags))
106438032Speter		p = e->e_sender;
106538032Speter	else
106638032Speter		p = e->e_from.q_paddr;
106738032Speter	p = remotename(p, m, RF_SENDERADDR|RF_CANONICAL, &rcode, e);
106838032Speter	if (strlen(p) >= (SIZE_T) sizeof rpathbuf)
106938032Speter	{
107038032Speter		p = shortenstring(p, MAXSHORTSTR);
107138032Speter		syserr("remotename: huge return %s", p);
107238032Speter	}
107338032Speter	snprintf(rpathbuf, sizeof rpathbuf, "%s", p);
107438032Speter	define('g', rpathbuf, e);		/* translated return path */
107538032Speter	define('h', host, e);			/* to host */
107638032Speter	Errors = 0;
107738032Speter	pvp = pv;
107838032Speter	*pvp++ = m->m_argv[0];
107938032Speter
108038032Speter	/* insert -f or -r flag as appropriate */
108164562Sgshapiro	if (FromFlag &&
108264562Sgshapiro	    (bitnset(M_FOPT, m->m_flags) ||
108364562Sgshapiro	     bitnset(M_ROPT, m->m_flags)))
108438032Speter	{
108538032Speter		if (bitnset(M_FOPT, m->m_flags))
108638032Speter			*pvp++ = "-f";
108738032Speter		else
108838032Speter			*pvp++ = "-r";
108938032Speter		*pvp++ = newstr(rpathbuf);
109038032Speter	}
109138032Speter
109238032Speter	/*
109338032Speter	**  Append the other fixed parts of the argv.  These run
109438032Speter	**  up to the first entry containing "$u".  There can only
109538032Speter	**  be one of these, and there are only a few more slots
109638032Speter	**  in the pv after it.
109738032Speter	*/
109838032Speter
109938032Speter	for (mvp = m->m_argv; (p = *++mvp) != NULL; )
110038032Speter	{
110138032Speter		/* can't use strchr here because of sign extension problems */
110238032Speter		while (*p != '\0')
110338032Speter		{
110438032Speter			if ((*p++ & 0377) == MACROEXPAND)
110538032Speter			{
110638032Speter				if (*p == 'u')
110738032Speter					break;
110838032Speter			}
110938032Speter		}
111038032Speter
111138032Speter		if (*p != '\0')
111238032Speter			break;
111338032Speter
111438032Speter		/* this entry is safe -- go ahead and process it */
111538032Speter		expand(*mvp, buf, sizeof buf, e);
111638032Speter		*pvp++ = newstr(buf);
111738032Speter		if (pvp >= &pv[MAXPV - 3])
111838032Speter		{
111964562Sgshapiro			syserr("554 5.3.5 Too many parameters to %s before $u",
112064562Sgshapiro			       pv[0]);
112164562Sgshapiro			return -1;
112238032Speter		}
112338032Speter	}
112438032Speter
112538032Speter	/*
112638032Speter	**  If we have no substitution for the user name in the argument
112738032Speter	**  list, we know that we must supply the names otherwise -- and
112838032Speter	**  SMTP is the answer!!
112938032Speter	*/
113038032Speter
113138032Speter	if (*mvp == NULL)
113238032Speter	{
113338032Speter		/* running SMTP */
113464562Sgshapiro#if SMTP
113538032Speter		clever = TRUE;
113638032Speter		*pvp = NULL;
113764562Sgshapiro#else /* SMTP */
113838032Speter		/* oops!  we don't implement SMTP */
113964562Sgshapiro		syserr("554 5.3.5 SMTP style mailer not implemented");
114064562Sgshapiro		return EX_SOFTWARE;
114164562Sgshapiro#endif /* SMTP */
114238032Speter	}
114338032Speter
114438032Speter	/*
114538032Speter	**  At this point *mvp points to the argument with $u.  We
114638032Speter	**  run through our address list and append all the addresses
114738032Speter	**  we can.  If we run out of space, do not fret!  We can
114838032Speter	**  always send another copy later.
114938032Speter	*/
115038032Speter
115164562Sgshapiro#if _FFR_DYNAMIC_TOBUF
115264562Sgshapiro	e->e_to = NULL;
115364562Sgshapiro	strsize = 2;
115464562Sgshapiro	rcptcount = 0;
115564562Sgshapiro#else /* _FFR_DYNAMIC_TOBUF */
115638032Speter	tobuf[0] = '\0';
115738032Speter	e->e_to = tobuf;
115864562Sgshapiro#endif /* _FFR_DYNAMIC_TOBUF */
115964562Sgshapiro
116038032Speter	ctladdr = NULL;
116164562Sgshapiro	firstsig = hostsignature(firstto->q_mailer, firstto->q_host);
116238032Speter	for (; to != NULL; to = to->q_next)
116338032Speter	{
116438032Speter		/* avoid sending multiple recipients to dumb mailers */
116564562Sgshapiro#if _FFR_DYNAMIC_TOBUF
116664562Sgshapiro		if (tochain != NULL && !bitnset(M_MUSER, m->m_flags))
116764562Sgshapiro			break;
116864562Sgshapiro#else /* _FFR_DYNAMIC_TOBUF */
116938032Speter		if (tobuf[0] != '\0' && !bitnset(M_MUSER, m->m_flags))
117038032Speter			break;
117164562Sgshapiro#endif /* _FFR_DYNAMIC_TOBUF */
117238032Speter
117338032Speter		/* if already sent or not for this host, don't send */
117464562Sgshapiro		if (!QS_IS_OK(to->q_state) ||
117538032Speter		    to->q_mailer != firstto->q_mailer ||
117664562Sgshapiro		    strcmp(hostsignature(to->q_mailer, to->q_host),
117764562Sgshapiro			   firstsig) != 0)
117838032Speter			continue;
117938032Speter
118038032Speter		/* avoid overflowing tobuf */
118164562Sgshapiro#if _FFR_DYNAMIC_TOBUF
118264562Sgshapiro		strsize += strlen(to->q_paddr) + 1;
118364562Sgshapiro		if (!clever && strsize > TOBUFSIZE)
118464562Sgshapiro			break;
118564562Sgshapiro
118664562Sgshapiro		if (++rcptcount > to->q_mailer->m_maxrcpt)
118764562Sgshapiro			break;
118864562Sgshapiro#else /* _FFR_DYNAMIC_TOBUF */
118938032Speter		if (sizeof tobuf < (strlen(to->q_paddr) + strlen(tobuf) + 2))
119038032Speter			break;
119164562Sgshapiro#endif /* _FFR_DYNAMIC_TOBUF */
119238032Speter
119338032Speter		if (tTd(10, 1))
119438032Speter		{
119564562Sgshapiro			dprintf("\nsend to ");
119638032Speter			printaddr(to, FALSE);
119738032Speter		}
119838032Speter
119938032Speter		/* compute effective uid/gid when sending */
120038032Speter		if (bitnset(M_RUNASRCPT, to->q_mailer->m_flags))
120138032Speter			contextaddr = ctladdr = getctladdr(to);
120238032Speter
120338032Speter		if (tTd(10, 2))
120438032Speter		{
120564562Sgshapiro			dprintf("ctladdr=");
120638032Speter			printaddr(ctladdr, FALSE);
120738032Speter		}
120838032Speter
120938032Speter		user = to->q_user;
121038032Speter		e->e_to = to->q_paddr;
121138032Speter
121238032Speter		/*
121338032Speter		**  Check to see that these people are allowed to
121438032Speter		**  talk to each other.
121566494Sgshapiro		**  Check also for overflow of e_msgsize.
121638032Speter		*/
121738032Speter
121866494Sgshapiro		if (m->m_maxsize != 0 &&
121966494Sgshapiro		    (e->e_msgsize > m->m_maxsize || e->e_msgsize < 0))
122038032Speter		{
122138032Speter			e->e_flags |= EF_NO_BODY_RETN;
122238032Speter			if (bitnset(M_LOCALMAILER, to->q_mailer->m_flags))
122338032Speter				to->q_status = "5.2.3";
122438032Speter			else
122538032Speter				to->q_status = "5.3.4";
122664562Sgshapiro			/* set to->q_rstatus = NULL; or to the following? */
122764562Sgshapiro			usrerrenh(to->q_status,
122864562Sgshapiro				  "552 Message is too large; %ld bytes max",
122964562Sgshapiro				  m->m_maxsize);
123064562Sgshapiro			markfailure(e, to, NULL, EX_UNAVAILABLE, FALSE);
123164562Sgshapiro			giveresponse(EX_UNAVAILABLE, to->q_status, m,
123264562Sgshapiro				     NULL, ctladdr, xstart, e);
123338032Speter			continue;
123438032Speter		}
123538032Speter#if NAMED_BIND
123638032Speter		h_errno = 0;
123764562Sgshapiro#endif /* NAMED_BIND */
123838032Speter
123964562Sgshapiro		ovr = TRUE;
124038032Speter		/* do config file checking of compatibility */
124164562Sgshapiro		rcode = rscheck("check_compat", e->e_from.q_paddr, to->q_paddr,
124264562Sgshapiro				e, TRUE, TRUE, 4);
124338032Speter		if (rcode == EX_OK)
124438032Speter		{
124542575Speter			/* do in-code checking if not discarding */
124642575Speter			if (!bitset(EF_DISCARD, e->e_flags))
124764562Sgshapiro			{
124842575Speter				rcode = checkcompat(to, e);
124964562Sgshapiro				ovr = FALSE;
125064562Sgshapiro			}
125138032Speter		}
125238032Speter		if (rcode != EX_OK)
125338032Speter		{
125464562Sgshapiro			markfailure(e, to, NULL, rcode, ovr);
125564562Sgshapiro			giveresponse(rcode, to->q_status, m,
125664562Sgshapiro				     NULL, ctladdr, xstart, e);
125738032Speter			continue;
125838032Speter		}
125942575Speter		if (bitset(EF_DISCARD, e->e_flags))
126042575Speter		{
126142575Speter			if (tTd(10, 5))
126242575Speter			{
126364562Sgshapiro				dprintf("deliver: discarding recipient ");
126442575Speter				printaddr(to, FALSE);
126542575Speter			}
126638032Speter
126764562Sgshapiro			/* pretend the message was sent */
126864562Sgshapiro			/* XXX should we log something here? */
126964562Sgshapiro			to->q_state = QS_DISCARDED;
127064562Sgshapiro
127142575Speter			/*
127242575Speter			**  Remove discard bit to prevent discard of
127364562Sgshapiro			**  future recipients.  This is safe because the
127464562Sgshapiro			**  true "global discard" has been handled before
127564562Sgshapiro			**  we get here.
127642575Speter			*/
127764562Sgshapiro
127842575Speter			e->e_flags &= ~EF_DISCARD;
127942575Speter			continue;
128042575Speter		}
128142575Speter
128238032Speter		/*
128338032Speter		**  Strip quote bits from names if the mailer is dumb
128438032Speter		**	about them.
128538032Speter		*/
128638032Speter
128738032Speter		if (bitnset(M_STRIPQ, m->m_flags))
128838032Speter		{
128938032Speter			stripquotes(user);
129038032Speter			stripquotes(host);
129138032Speter		}
129238032Speter
129338032Speter		/* hack attack -- delivermail compatibility */
129438032Speter		if (m == ProgMailer && *user == '|')
129538032Speter			user++;
129638032Speter
129738032Speter		/*
129838032Speter		**  If an error message has already been given, don't
129938032Speter		**	bother to send to this address.
130038032Speter		**
130138032Speter		**	>>>>>>>>>> This clause assumes that the local mailer
130238032Speter		**	>> NOTE >> cannot do any further aliasing; that
130338032Speter		**	>>>>>>>>>> function is subsumed by sendmail.
130438032Speter		*/
130538032Speter
130664562Sgshapiro		if (!QS_IS_OK(to->q_state))
130738032Speter			continue;
130838032Speter
130938032Speter		/*
131038032Speter		**  See if this user name is "special".
131138032Speter		**	If the user name has a slash in it, assume that this
131238032Speter		**	is a file -- send it off without further ado.  Note
131338032Speter		**	that this type of addresses is not processed along
131438032Speter		**	with the others, so we fudge on the To person.
131538032Speter		*/
131638032Speter
131738032Speter		if (strcmp(m->m_mailer, "[FILE]") == 0)
131838032Speter		{
131938032Speter			define('u', user, e);	/* to user */
132038032Speter			p = to->q_home;
132138032Speter			if (p == NULL && ctladdr != NULL)
132238032Speter				p = ctladdr->q_home;
132338032Speter			define('z', p, e);	/* user's home */
132438032Speter			expand(m->m_argv[1], buf, sizeof buf, e);
132538032Speter			if (strlen(buf) > 0)
132638032Speter				rcode = mailfile(buf, m, ctladdr, SFF_CREAT, e);
132738032Speter			else
132838032Speter			{
132938032Speter				syserr("empty filename specification for mailer %s",
133038032Speter				       m->m_name);
133138032Speter				rcode = EX_CONFIG;
133238032Speter			}
133364562Sgshapiro			giveresponse(rcode, to->q_status, m, NULL,
133464562Sgshapiro				     ctladdr, xstart, e);
133564562Sgshapiro			markfailure(e, to, NULL, rcode, TRUE);
133638032Speter			e->e_nsent++;
133738032Speter			if (rcode == EX_OK)
133838032Speter			{
133964562Sgshapiro				to->q_state = QS_SENT;
134038032Speter				if (bitnset(M_LOCALMAILER, m->m_flags) &&
134138032Speter				    bitset(QPINGONSUCCESS, to->q_flags))
134238032Speter				{
134338032Speter					to->q_flags |= QDELIVERED;
134438032Speter					to->q_status = "2.1.5";
134538032Speter					fprintf(e->e_xfp, "%s... Successfully delivered\n",
134638032Speter						to->q_paddr);
134738032Speter				}
134838032Speter			}
134938032Speter			to->q_statdate = curtime();
135038032Speter			markstats(e, to, FALSE);
135138032Speter			continue;
135238032Speter		}
135338032Speter
135438032Speter		/*
135538032Speter		**  Address is verified -- add this user to mailer
135638032Speter		**  argv, and add it to the print list of recipients.
135738032Speter		*/
135838032Speter
135938032Speter		/* link together the chain of recipients */
136038032Speter		to->q_tchain = tochain;
136138032Speter		tochain = to;
136238032Speter
136364562Sgshapiro#if _FFR_DYNAMIC_TOBUF
136464562Sgshapiro		e->e_to = "[CHAIN]";
136564562Sgshapiro#else /* _FFR_DYNAMIC_TOBUF */
136638032Speter		/* create list of users for error messages */
136764562Sgshapiro		(void) strlcat(tobuf, ",", sizeof tobuf);
136864562Sgshapiro		(void) strlcat(tobuf, to->q_paddr, sizeof tobuf);
136964562Sgshapiro#endif /* _FFR_DYNAMIC_TOBUF */
137064562Sgshapiro
137138032Speter		define('u', user, e);		/* to user */
137238032Speter		p = to->q_home;
137338032Speter		if (p == NULL && ctladdr != NULL)
137438032Speter			p = ctladdr->q_home;
137538032Speter		define('z', p, e);	/* user's home */
137638032Speter
137764562Sgshapiro		/* set the ${dsn_notify} macro if applicable */
137864562Sgshapiro		if (bitset(QHASNOTIFY, to->q_flags))
137964562Sgshapiro		{
138064562Sgshapiro			char notify[MAXLINE];
138164562Sgshapiro
138264562Sgshapiro			notify[0] = '\0';
138364562Sgshapiro			if (bitset(QPINGONSUCCESS, to->q_flags))
138464562Sgshapiro				(void) strlcat(notify, "SUCCESS,",
138564562Sgshapiro					       sizeof notify);
138664562Sgshapiro			if (bitset(QPINGONFAILURE, to->q_flags))
138764562Sgshapiro				(void) strlcat(notify, "FAILURE,",
138864562Sgshapiro					       sizeof notify);
138964562Sgshapiro			if (bitset(QPINGONDELAY, to->q_flags))
139064562Sgshapiro				(void) strlcat(notify, "DELAY,", sizeof notify);
139164562Sgshapiro
139264562Sgshapiro			/* Set to NEVER or drop trailing comma */
139364562Sgshapiro			if (notify[0] == '\0')
139464562Sgshapiro				(void) strlcat(notify, "NEVER", sizeof notify);
139564562Sgshapiro			else
139664562Sgshapiro				notify[strlen(notify) - 1] = '\0';
139764562Sgshapiro
139864562Sgshapiro			define(macid("{dsn_notify}", NULL), newstr(notify), e);
139964562Sgshapiro		}
140064562Sgshapiro		else
140164562Sgshapiro			define(macid("{dsn_notify}", NULL), NULL, e);
140264562Sgshapiro
140338032Speter		/*
140438032Speter		**  Expand out this user into argument list.
140538032Speter		*/
140638032Speter
140738032Speter		if (!clever)
140838032Speter		{
140938032Speter			expand(*mvp, buf, sizeof buf, e);
141038032Speter			*pvp++ = newstr(buf);
141138032Speter			if (pvp >= &pv[MAXPV - 2])
141238032Speter			{
141338032Speter				/* allow some space for trailing parms */
141438032Speter				break;
141538032Speter			}
141638032Speter		}
141738032Speter	}
141838032Speter
141938032Speter	/* see if any addresses still exist */
142064562Sgshapiro#if _FFR_DYNAMIC_TOBUF
142164562Sgshapiro	if (tochain == NULL)
142264562Sgshapiro#else /* _FFR_DYNAMIC_TOBUF */
142338032Speter	if (tobuf[0] == '\0')
142464562Sgshapiro#endif /* _FFR_DYNAMIC_TOBUF */
142538032Speter	{
142638032Speter		define('g', (char *) NULL, e);
142764562Sgshapiro		e->e_to = NULL;
142864562Sgshapiro		return 0;
142938032Speter	}
143038032Speter
143138032Speter	/* print out messages as full list */
143264562Sgshapiro#if _FFR_DYNAMIC_TOBUF
143364562Sgshapiro	{
143464562Sgshapiro		int l = 1;
143564562Sgshapiro		char *tobufptr;
143664562Sgshapiro
143764562Sgshapiro		for (to = tochain; to != NULL; to = to->q_tchain)
143864562Sgshapiro			l += strlen(to->q_paddr) + 1;
143964562Sgshapiro		if (l < TOBUFSIZE)
144064562Sgshapiro			l = TOBUFSIZE;
144164562Sgshapiro		if (l > tobufsize)
144264562Sgshapiro		{
144364562Sgshapiro			if (tobuf != NULL)
144464562Sgshapiro				free(tobuf);
144564562Sgshapiro			tobufsize = l;
144664562Sgshapiro			tobuf = xalloc(tobufsize);
144764562Sgshapiro		}
144864562Sgshapiro		tobufptr = tobuf;
144964562Sgshapiro		*tobufptr = '\0';
145064562Sgshapiro		for (to = tochain; to != NULL; to = to->q_tchain)
145164562Sgshapiro		{
145264562Sgshapiro			snprintf(tobufptr, tobufsize - (tobufptr - tobuf),
145364562Sgshapiro				 ",%s", to->q_paddr);
145464562Sgshapiro			tobufptr += strlen(tobufptr);
145564562Sgshapiro		}
145664562Sgshapiro	}
145764562Sgshapiro#endif /* _FFR_DYNAMIC_TOBUF */
145838032Speter	e->e_to = tobuf + 1;
145938032Speter
146038032Speter	/*
146138032Speter	**  Fill out any parameters after the $u parameter.
146238032Speter	*/
146338032Speter
146438032Speter	while (!clever && *++mvp != NULL)
146538032Speter	{
146638032Speter		expand(*mvp, buf, sizeof buf, e);
146738032Speter		*pvp++ = newstr(buf);
146838032Speter		if (pvp >= &pv[MAXPV])
146964562Sgshapiro			syserr("554 5.3.0 deliver: pv overflow after $u for %s",
147064562Sgshapiro			       pv[0]);
147138032Speter	}
147238032Speter	*pvp++ = NULL;
147338032Speter
147438032Speter	/*
147538032Speter	**  Call the mailer.
147638032Speter	**	The argument vector gets built, pipes
147738032Speter	**	are created as necessary, and we fork & exec as
147838032Speter	**	appropriate.
147938032Speter	**	If we are running SMTP, we just need to clean up.
148038032Speter	*/
148138032Speter
148264562Sgshapiro	/* XXX this seems a bit wierd */
148338032Speter	if (ctladdr == NULL && m != ProgMailer && m != FileMailer &&
148438032Speter	    bitset(QGOODUID, e->e_from.q_flags))
148538032Speter		ctladdr = &e->e_from;
148638032Speter
148738032Speter#if NAMED_BIND
148838032Speter	if (ConfigLevel < 2)
148938032Speter		_res.options &= ~(RES_DEFNAMES | RES_DNSRCH);	/* XXX */
149064562Sgshapiro#endif /* NAMED_BIND */
149138032Speter
149238032Speter	if (tTd(11, 1))
149338032Speter	{
149464562Sgshapiro		dprintf("openmailer:");
149538032Speter		printav(pv);
149638032Speter	}
149738032Speter	errno = 0;
149838032Speter#if NAMED_BIND
149938032Speter	h_errno = 0;
150064562Sgshapiro#endif /* NAMED_BIND */
150138032Speter
150238032Speter	CurHostName = NULL;
150338032Speter
150438032Speter	/*
150538032Speter	**  Deal with the special case of mail handled through an IPC
150638032Speter	**  connection.
150738032Speter	**	In this case we don't actually fork.  We must be
150838032Speter	**	running SMTP for this to work.  We will return a
150938032Speter	**	zero pid to indicate that we are running IPC.
151038032Speter	**  We also handle a debug version that just talks to stdin/out.
151138032Speter	*/
151238032Speter
151338032Speter	curhost = NULL;
151438032Speter	SmtpPhase = NULL;
151538032Speter	mci = NULL;
151638032Speter
151738032Speter#if XDEBUG
151838032Speter	{
151938032Speter		char wbuf[MAXLINE];
152038032Speter
152138032Speter		/* make absolutely certain 0, 1, and 2 are in use */
152238032Speter		snprintf(wbuf, sizeof wbuf, "%s... openmailer(%s)",
152338032Speter			shortenstring(e->e_to, MAXSHORTSTR), m->m_name);
152438032Speter		checkfd012(wbuf);
152538032Speter	}
152664562Sgshapiro#endif /* XDEBUG */
152738032Speter
152838032Speter	/* check for 8-bit available */
152938032Speter	if (bitset(EF_HAS8BIT, e->e_flags) &&
153038032Speter	    bitnset(M_7BITS, m->m_flags) &&
153138032Speter	    (bitset(EF_DONT_MIME, e->e_flags) ||
153238032Speter	     !(bitset(MM_MIME8BIT, MimeMode) ||
153338032Speter	       (bitset(EF_IS_MIME, e->e_flags) &&
153464562Sgshapiro		bitset(MM_CVTMIME, MimeMode)))))
153538032Speter	{
153664562Sgshapiro		e->e_status = "5.6.3";
153764562Sgshapiro		usrerrenh(e->e_status,
153864562Sgshapiro		       "554 Cannot send 8-bit data to 7-bit destination");
153938032Speter		rcode = EX_DATAERR;
154038032Speter		goto give_up;
154138032Speter	}
154238032Speter
154338032Speter	if (tTd(62, 8))
154438032Speter		checkfds("before delivery");
154538032Speter
154638032Speter	/* check for Local Person Communication -- not for mortals!!! */
154738032Speter	if (strcmp(m->m_mailer, "[LPC]") == 0)
154838032Speter	{
154938032Speter		mci = (MCI *) xalloc(sizeof *mci);
155064562Sgshapiro		memset((char *) mci, '\0', sizeof *mci);
155138032Speter		mci->mci_in = stdin;
155238032Speter		mci->mci_out = stdout;
155338032Speter		mci->mci_state = clever ? MCIS_OPENING : MCIS_OPEN;
155438032Speter		mci->mci_mailer = m;
155538032Speter	}
155638032Speter	else if (strcmp(m->m_mailer, "[IPC]") == 0 ||
155738032Speter		 strcmp(m->m_mailer, "[TCP]") == 0)
155838032Speter	{
155938032Speter#if DAEMON
156038032Speter		register int i;
156138032Speter
156238032Speter		if (pv[0] == NULL || pv[1] == NULL || pv[1][0] == '\0')
156338032Speter		{
156464562Sgshapiro			syserr("null destination for %s mailer", m->m_mailer);
156538032Speter			rcode = EX_CONFIG;
156638032Speter			goto give_up;
156738032Speter		}
156838032Speter
156964562Sgshapiro# if NETUNIX
157064562Sgshapiro		if (strcmp(pv[0], "FILE") == 0)
157164562Sgshapiro		{
157264562Sgshapiro			curhost = CurHostName = "localhost";
157364562Sgshapiro			mux_path = pv[1];
157464562Sgshapiro		}
157564562Sgshapiro		else
157664562Sgshapiro# endif /* NETUNIX */
157764562Sgshapiro		{
157864562Sgshapiro			CurHostName = pv[1];
157964562Sgshapiro			curhost = hostsignature(m, pv[1]);
158064562Sgshapiro		}
158138032Speter
158238032Speter		if (curhost == NULL || curhost[0] == '\0')
158338032Speter		{
158438032Speter			syserr("null host signature for %s", pv[1]);
158538032Speter			rcode = EX_CONFIG;
158638032Speter			goto give_up;
158738032Speter		}
158838032Speter
158938032Speter		if (!clever)
159038032Speter		{
159164562Sgshapiro			syserr("554 5.3.5 non-clever IPC");
159238032Speter			rcode = EX_CONFIG;
159338032Speter			goto give_up;
159438032Speter		}
159564562Sgshapiro		if (pv[2] != NULL
159664562Sgshapiro# if NETUNIX
159764562Sgshapiro		    && mux_path == NULL
159864562Sgshapiro# endif /* NETUNIX */
159964562Sgshapiro		    )
160038032Speter		{
160164562Sgshapiro			port = htons((u_short)atoi(pv[2]));
160238032Speter			if (port == 0)
160338032Speter			{
160464562Sgshapiro# ifdef NO_GETSERVBYNAME
160564562Sgshapiro				syserr("Invalid port number: %s", pv[2]);
160664562Sgshapiro# else /* NO_GETSERVBYNAME */
160738032Speter				struct servent *sp = getservbyname(pv[2], "tcp");
160838032Speter
160938032Speter				if (sp == NULL)
161038032Speter					syserr("Service %s unknown", pv[2]);
161138032Speter				else
161238032Speter					port = sp->s_port;
161364562Sgshapiro# endif /* NO_GETSERVBYNAME */
161438032Speter			}
161538032Speter		}
161664562Sgshapiro
161764562Sgshapiro		nummxhosts = parse_hostsignature(curhost, mxhosts, m);
161838032Spetertryhost:
161964562Sgshapiro		while (hostnum < nummxhosts)
162038032Speter		{
162164562Sgshapiro			char sep = ':';
162264562Sgshapiro			char *endp;
162338032Speter			static char hostbuf[MAXNAME + 1];
162438032Speter
162564562Sgshapiro# if NETINET6
162664562Sgshapiro			if (*mxhosts[hostnum] == '[')
162738032Speter			{
162864562Sgshapiro				endp = strchr(mxhosts[hostnum] + 1, ']');
162964562Sgshapiro				if (endp != NULL)
163064562Sgshapiro					endp = strpbrk(endp + 1, ":,");
163164562Sgshapiro			}
163264562Sgshapiro			else
163364562Sgshapiro				endp = strpbrk(mxhosts[hostnum], ":,");
163464562Sgshapiro# else /* NETINET6 */
163564562Sgshapiro			endp = strpbrk(mxhosts[hostnum], ":,");
163664562Sgshapiro# endif /* NETINET6 */
163764562Sgshapiro			if (endp != NULL)
163864562Sgshapiro			{
163964562Sgshapiro				sep = *endp;
164064562Sgshapiro				*endp = '\0';
164164562Sgshapiro			}
164264562Sgshapiro
164364562Sgshapiro			if (*mxhosts[hostnum] == '\0')
164464562Sgshapiro			{
164538032Speter				syserr("deliver: null host name in signature");
164664562Sgshapiro				hostnum++;
164764562Sgshapiro				if (endp != NULL)
164864562Sgshapiro					*endp = sep;
164938032Speter				continue;
165038032Speter			}
165164562Sgshapiro			(void) strlcpy(hostbuf, mxhosts[hostnum],
165264562Sgshapiro				       sizeof hostbuf);
165364562Sgshapiro			hostnum++;
165464562Sgshapiro			if (endp != NULL)
165564562Sgshapiro				*endp = sep;
165638032Speter
165738032Speter			/* see if we already know that this host is fried */
165838032Speter			CurHostName = hostbuf;
165938032Speter			mci = mci_get(hostbuf, m);
166038032Speter			if (mci->mci_state != MCIS_CLOSED)
166138032Speter			{
166238032Speter				if (tTd(11, 1))
166338032Speter				{
166464562Sgshapiro					dprintf("openmailer: ");
166538032Speter					mci_dump(mci, FALSE);
166638032Speter				}
166738032Speter				CurHostName = mci->mci_host;
166838032Speter				message("Using cached %sSMTP connection to %s via %s...",
166938032Speter					bitset(MCIF_ESMTP, mci->mci_flags) ? "E" : "",
167038032Speter					hostbuf, m->m_name);
167164562Sgshapiro				mci->mci_deliveries++;
167238032Speter				break;
167338032Speter			}
167438032Speter			mci->mci_mailer = m;
167538032Speter			if (mci->mci_exitstat != EX_OK)
167638032Speter			{
167738032Speter				if (mci->mci_exitstat == EX_TEMPFAIL)
167838032Speter					goodmxfound = TRUE;
167938032Speter				continue;
168038032Speter			}
168138032Speter
168238032Speter			if (mci_lock_host(mci) != EX_OK)
168338032Speter			{
168438032Speter				mci_setstat(mci, EX_TEMPFAIL, "4.4.5", NULL);
168538032Speter				goodmxfound = TRUE;
168638032Speter				continue;
168738032Speter			}
168838032Speter
168938032Speter			/* try the connection */
169064562Sgshapiro			sm_setproctitle(TRUE, e, "%s %s: %s",
169164562Sgshapiro					qid_printname(e),
169264562Sgshapiro					hostbuf, "user open");
169364562Sgshapiro# if NETUNIX
169464562Sgshapiro			if (mux_path != NULL)
169564562Sgshapiro			{
169638032Speter				message("Connecting to %s via %s...",
169764562Sgshapiro					mux_path, m->m_name);
169864562Sgshapiro				i = makeconnection_ds(mux_path, mci);
169964562Sgshapiro			}
170038032Speter			else
170164562Sgshapiro# endif /* NETUNIX */
170264562Sgshapiro			{
170364562Sgshapiro				if (port == 0)
170464562Sgshapiro					message("Connecting to %s via %s...",
170564562Sgshapiro						hostbuf, m->m_name);
170664562Sgshapiro				else
170764562Sgshapiro					message("Connecting to %s port %d via %s...",
170864562Sgshapiro						hostbuf, ntohs(port),
170964562Sgshapiro						m->m_name);
171064562Sgshapiro				i = makeconnection(hostbuf, port, mci, e);
171164562Sgshapiro			}
171238032Speter			mci->mci_lastuse = curtime();
171364562Sgshapiro			mci->mci_deliveries = 0;
171438032Speter			mci->mci_exitstat = i;
171538032Speter			mci->mci_errno = errno;
171664562Sgshapiro# if NAMED_BIND
171738032Speter			mci->mci_herrno = h_errno;
171864562Sgshapiro# endif /* NAMED_BIND */
171938032Speter			if (i == EX_OK)
172038032Speter			{
172138032Speter				goodmxfound = TRUE;
172238032Speter				mci->mci_state = MCIS_OPENING;
172338032Speter				mci_cache(mci);
172438032Speter				if (TrafficLogFile != NULL)
172538032Speter					fprintf(TrafficLogFile, "%05d === CONNECT %s\n",
172638032Speter						(int) getpid(), hostbuf);
172738032Speter				break;
172838032Speter			}
172938032Speter			else
173038032Speter			{
173164562Sgshapiro				if (tTd(11, 1))
173264562Sgshapiro					dprintf("openmailer: makeconnection => stat=%d, errno=%d\n",
173338032Speter						i, errno);
173438032Speter				if (i == EX_TEMPFAIL)
173538032Speter					goodmxfound = TRUE;
173638032Speter				mci_unlock_host(mci);
173738032Speter			}
173838032Speter
173938032Speter			/* enter status of this host */
174038032Speter			setstat(i);
174138032Speter
174238032Speter			/* should print some message here for -v mode */
174338032Speter		}
174438032Speter		if (mci == NULL)
174538032Speter		{
174638032Speter			syserr("deliver: no host name");
174738032Speter			rcode = EX_SOFTWARE;
174838032Speter			goto give_up;
174938032Speter		}
175038032Speter		mci->mci_pid = 0;
175164562Sgshapiro#else /* DAEMON */
175264562Sgshapiro		syserr("554 5.3.5 openmailer: no IPC");
175338032Speter		if (tTd(11, 1))
175464562Sgshapiro			dprintf("openmailer: NULL\n");
175538032Speter		rcode = EX_UNAVAILABLE;
175638032Speter		goto give_up;
175738032Speter#endif /* DAEMON */
175838032Speter	}
175938032Speter	else
176038032Speter	{
176138032Speter		/* flush any expired connections */
176238032Speter		(void) mci_scan(NULL);
176338032Speter		mci = NULL;
176438032Speter
176538032Speter#if SMTP
176638032Speter		if (bitnset(M_LMTP, m->m_flags))
176738032Speter		{
176838032Speter			/* try to get a cached connection */
176938032Speter			mci = mci_get(m->m_name, m);
177038032Speter			if (mci->mci_host == NULL)
177138032Speter				mci->mci_host = m->m_name;
177238032Speter			CurHostName = mci->mci_host;
177338032Speter			if (mci->mci_state != MCIS_CLOSED)
177438032Speter			{
177538032Speter				message("Using cached LMTP connection for %s...",
177638032Speter					m->m_name);
177764562Sgshapiro				mci->mci_deliveries++;
177838032Speter				goto do_transfer;
177938032Speter			}
178038032Speter		}
178164562Sgshapiro#endif /* SMTP */
178238032Speter
178338032Speter		/* announce the connection to verbose listeners */
178438032Speter		if (host == NULL || host[0] == '\0')
178538032Speter			message("Connecting to %s...", m->m_name);
178638032Speter		else
178738032Speter			message("Connecting to %s via %s...", host, m->m_name);
178838032Speter		if (TrafficLogFile != NULL)
178938032Speter		{
179038032Speter			char **av;
179138032Speter
179238032Speter			fprintf(TrafficLogFile, "%05d === EXEC", (int) getpid());
179338032Speter			for (av = pv; *av != NULL; av++)
179438032Speter				fprintf(TrafficLogFile, " %s", *av);
179538032Speter			fprintf(TrafficLogFile, "\n");
179638032Speter		}
179738032Speter
179838032Speter#if XDEBUG
179938032Speter		checkfd012("before creating mail pipe");
180064562Sgshapiro#endif /* XDEBUG */
180138032Speter
180238032Speter		/* create a pipe to shove the mail through */
180338032Speter		if (pipe(mpvect) < 0)
180438032Speter		{
180538032Speter			syserr("%s... openmailer(%s): pipe (to mailer)",
180638032Speter				shortenstring(e->e_to, MAXSHORTSTR), m->m_name);
180738032Speter			if (tTd(11, 1))
180864562Sgshapiro				dprintf("openmailer: NULL\n");
180938032Speter			rcode = EX_OSERR;
181038032Speter			goto give_up;
181138032Speter		}
181238032Speter
181338032Speter#if XDEBUG
181438032Speter		/* make sure we didn't get one of the standard I/O files */
181538032Speter		if (mpvect[0] < 3 || mpvect[1] < 3)
181638032Speter		{
181738032Speter			syserr("%s... openmailer(%s): bogus mpvect %d %d",
181838032Speter				shortenstring(e->e_to, MAXSHORTSTR), m->m_name,
181938032Speter				mpvect[0], mpvect[1]);
182038032Speter			printopenfds(TRUE);
182138032Speter			if (tTd(11, 1))
182264562Sgshapiro				dprintf("openmailer: NULL\n");
182338032Speter			rcode = EX_OSERR;
182438032Speter			goto give_up;
182538032Speter		}
182638032Speter
182738032Speter		/* make sure system call isn't dead meat */
182838032Speter		checkfdopen(mpvect[0], "mpvect[0]");
182938032Speter		checkfdopen(mpvect[1], "mpvect[1]");
183038032Speter		if (mpvect[0] == mpvect[1] ||
183138032Speter		    (e->e_lockfp != NULL &&
183238032Speter		     (mpvect[0] == fileno(e->e_lockfp) ||
183338032Speter		      mpvect[1] == fileno(e->e_lockfp))))
183438032Speter		{
183538032Speter			if (e->e_lockfp == NULL)
183638032Speter				syserr("%s... openmailer(%s): overlapping mpvect %d %d",
183738032Speter					shortenstring(e->e_to, MAXSHORTSTR),
183838032Speter					m->m_name, mpvect[0], mpvect[1]);
183938032Speter			else
184038032Speter				syserr("%s... openmailer(%s): overlapping mpvect %d %d, lockfp = %d",
184138032Speter					shortenstring(e->e_to, MAXSHORTSTR),
184238032Speter					m->m_name, mpvect[0], mpvect[1],
184338032Speter					fileno(e->e_lockfp));
184438032Speter		}
184564562Sgshapiro#endif /* XDEBUG */
184638032Speter
184764562Sgshapiro		/* create a return pipe */
184864562Sgshapiro		if (pipe(rpvect) < 0)
184938032Speter		{
185064562Sgshapiro			syserr("%s... openmailer(%s): pipe (from mailer)",
185164562Sgshapiro				shortenstring(e->e_to, MAXSHORTSTR),
185264562Sgshapiro				m->m_name);
185364562Sgshapiro			(void) close(mpvect[0]);
185464562Sgshapiro			(void) close(mpvect[1]);
185564562Sgshapiro			if (tTd(11, 1))
185664562Sgshapiro				dprintf("openmailer: NULL\n");
185764562Sgshapiro			rcode = EX_OSERR;
185864562Sgshapiro			goto give_up;
185938032Speter		}
186064562Sgshapiro#if XDEBUG
186164562Sgshapiro		checkfdopen(rpvect[0], "rpvect[0]");
186264562Sgshapiro		checkfdopen(rpvect[1], "rpvect[1]");
186364562Sgshapiro#endif /* XDEBUG */
186438032Speter
186538032Speter		/*
186638032Speter		**  Actually fork the mailer process.
186738032Speter		**	DOFORK is clever about retrying.
186838032Speter		**
186938032Speter		**	Dispose of SIGCHLD signal catchers that may be laying
187064562Sgshapiro		**	around so that endmailer will get it.
187138032Speter		*/
187238032Speter
187338032Speter		if (e->e_xfp != NULL)
187464562Sgshapiro			(void) fflush(e->e_xfp);	/* for debugging */
187538032Speter		(void) fflush(stdout);
187638032Speter		(void) setsignal(SIGCHLD, SIG_DFL);
187764562Sgshapiro
187864562Sgshapiro
187938032Speter		DOFORK(FORK);
188038032Speter		/* pid is set by DOFORK */
188164562Sgshapiro
188238032Speter		if (pid < 0)
188338032Speter		{
188438032Speter			/* failure */
188538032Speter			syserr("%s... openmailer(%s): cannot fork",
188638032Speter				shortenstring(e->e_to, MAXSHORTSTR), m->m_name);
188738032Speter			(void) close(mpvect[0]);
188838032Speter			(void) close(mpvect[1]);
188964562Sgshapiro			(void) close(rpvect[0]);
189064562Sgshapiro			(void) close(rpvect[1]);
189138032Speter			if (tTd(11, 1))
189264562Sgshapiro				dprintf("openmailer: NULL\n");
189338032Speter			rcode = EX_OSERR;
189438032Speter			goto give_up;
189538032Speter		}
189638032Speter		else if (pid == 0)
189738032Speter		{
189838032Speter			int i;
189964562Sgshapiro			int save_errno;
190038032Speter			int new_euid = NO_UID;
190138032Speter			int new_ruid = NO_UID;
190238032Speter			int new_gid = NO_GID;
190338032Speter			struct stat stb;
190438032Speter			extern int DtableSize;
190538032Speter
190638032Speter			if (e->e_lockfp != NULL)
190738032Speter				(void) close(fileno(e->e_lockfp));
190838032Speter
190938032Speter			/* child -- set up input & exec mailer */
191038032Speter			(void) setsignal(SIGINT, SIG_IGN);
191138032Speter			(void) setsignal(SIGHUP, SIG_IGN);
191238032Speter			(void) setsignal(SIGTERM, SIG_DFL);
191338032Speter
191438032Speter			if (m != FileMailer || stat(tochain->q_user, &stb) < 0)
191538032Speter				stb.st_mode = 0;
191638032Speter
191764562Sgshapiro# if HASSETUSERCONTEXT
191838032Speter			/*
191938032Speter			**  Set user resources.
192038032Speter			*/
192138032Speter
192238032Speter			if (contextaddr != NULL)
192338032Speter			{
192438032Speter				struct passwd *pwd;
192538032Speter
192638032Speter				if (contextaddr->q_ruser != NULL)
192738032Speter					pwd = sm_getpwnam(contextaddr->q_ruser);
192838032Speter				else
192938032Speter					pwd = sm_getpwnam(contextaddr->q_user);
193038032Speter				if (pwd != NULL)
193138032Speter					(void) setusercontext(NULL,
193238032Speter						pwd, pwd->pw_uid,
193338032Speter						LOGIN_SETRESOURCES|LOGIN_SETPRIORITY);
193438032Speter			}
193564562Sgshapiro# endif /* HASSETUSERCONTEXT */
193638032Speter
193738032Speter			/* tweak niceness */
193838032Speter			if (m->m_nice != 0)
193964562Sgshapiro				(void) nice(m->m_nice);
194038032Speter
194138032Speter			/* reset group id */
194238032Speter			if (bitnset(M_SPECIFIC_UID, m->m_flags))
194338032Speter				new_gid = m->m_gid;
194438032Speter			else if (bitset(S_ISGID, stb.st_mode))
194538032Speter				new_gid = stb.st_gid;
194638032Speter			else if (ctladdr != NULL && ctladdr->q_gid != 0)
194738032Speter			{
194838032Speter				if (!DontInitGroups)
194938032Speter				{
195038032Speter					char *u = ctladdr->q_ruser;
195138032Speter
195238032Speter					if (u == NULL)
195338032Speter						u = ctladdr->q_user;
195438032Speter
195538032Speter					if (initgroups(u, ctladdr->q_gid) == -1 && suidwarn)
195664562Sgshapiro					{
195738032Speter						syserr("openmailer: initgroups(%s, %d) failed",
195838032Speter							u, ctladdr->q_gid);
195964562Sgshapiro						exit(EX_TEMPFAIL);
196064562Sgshapiro					}
196138032Speter				}
196238032Speter				else
196338032Speter				{
196438032Speter					GIDSET_T gidset[1];
196538032Speter
196638032Speter					gidset[0] = ctladdr->q_gid;
196738032Speter					if (setgroups(1, gidset) == -1 && suidwarn)
196864562Sgshapiro					{
196938032Speter						syserr("openmailer: setgroups() failed");
197064562Sgshapiro						exit(EX_TEMPFAIL);
197164562Sgshapiro					}
197238032Speter				}
197338032Speter				new_gid = ctladdr->q_gid;
197438032Speter			}
197538032Speter			else
197638032Speter			{
197738032Speter				if (!DontInitGroups)
197838032Speter				{
197938032Speter					if (initgroups(DefUser, DefGid) == -1 && suidwarn)
198064562Sgshapiro					{
198138032Speter						syserr("openmailer: initgroups(%s, %d) failed",
198238032Speter							DefUser, DefGid);
198364562Sgshapiro						exit(EX_TEMPFAIL);
198464562Sgshapiro					}
198538032Speter				}
198638032Speter				else
198738032Speter				{
198838032Speter					GIDSET_T gidset[1];
198938032Speter
199038032Speter					gidset[0] = DefGid;
199138032Speter					if (setgroups(1, gidset) == -1 && suidwarn)
199264562Sgshapiro					{
199338032Speter						syserr("openmailer: setgroups() failed");
199464562Sgshapiro						exit(EX_TEMPFAIL);
199564562Sgshapiro					}
199638032Speter				}
199738032Speter				if (m->m_gid == 0)
199838032Speter					new_gid = DefGid;
199938032Speter				else
200038032Speter					new_gid = m->m_gid;
200138032Speter			}
200264562Sgshapiro			if (new_gid != NO_GID)
200364562Sgshapiro			{
200464562Sgshapiro				if (RunAsUid != 0 &&
200564562Sgshapiro				    bitnset(M_SPECIFIC_UID, m->m_flags) &&
200664562Sgshapiro				    new_gid != getgid() &&
200764562Sgshapiro				    new_gid != getegid())
200864562Sgshapiro				{
200964562Sgshapiro					/* Only root can change the gid */
201064562Sgshapiro					syserr("openmailer: insufficient privileges to change gid");
201164562Sgshapiro					exit(EX_TEMPFAIL);
201264562Sgshapiro				}
201338032Speter
201464562Sgshapiro				if (setgid(new_gid) < 0 && suidwarn)
201564562Sgshapiro				{
201664562Sgshapiro					syserr("openmailer: setgid(%ld) failed",
201764562Sgshapiro					       (long) new_gid);
201864562Sgshapiro					exit(EX_TEMPFAIL);
201964562Sgshapiro				}
202064562Sgshapiro			}
202164562Sgshapiro
202264562Sgshapiro			/* change root to some "safe" directory */
202364562Sgshapiro			if (m->m_rootdir != NULL)
202464562Sgshapiro			{
202564562Sgshapiro				expand(m->m_rootdir, buf, sizeof buf, e);
202664562Sgshapiro				if (tTd(11, 20))
202764562Sgshapiro					dprintf("openmailer: chroot %s\n",
202864562Sgshapiro						buf);
202964562Sgshapiro				if (chroot(buf) < 0)
203064562Sgshapiro				{
203164562Sgshapiro					syserr("openmailer: Cannot chroot(%s)",
203264562Sgshapiro					       buf);
203364562Sgshapiro					exit(EX_TEMPFAIL);
203464562Sgshapiro				}
203564562Sgshapiro				if (chdir("/") < 0)
203664562Sgshapiro				{
203764562Sgshapiro					syserr("openmailer: cannot chdir(/)");
203864562Sgshapiro					exit(EX_TEMPFAIL);
203964562Sgshapiro				}
204064562Sgshapiro			}
204164562Sgshapiro
204238032Speter			/* reset user id */
204338032Speter			endpwent();
204438032Speter			if (bitnset(M_SPECIFIC_UID, m->m_flags))
204538032Speter				new_euid = m->m_uid;
204638032Speter			else if (bitset(S_ISUID, stb.st_mode))
204738032Speter				new_ruid = stb.st_uid;
204838032Speter			else if (ctladdr != NULL && ctladdr->q_uid != 0)
204938032Speter				new_ruid = ctladdr->q_uid;
205038032Speter			else if (m->m_uid != 0)
205138032Speter				new_ruid = m->m_uid;
205238032Speter			else
205338032Speter				new_ruid = DefUid;
205438032Speter			if (new_euid != NO_UID)
205538032Speter			{
205664562Sgshapiro				if (RunAsUid != 0 && new_euid != RunAsUid)
205764562Sgshapiro				{
205864562Sgshapiro					/* Only root can change the uid */
205964562Sgshapiro					syserr("openmailer: insufficient privileges to change uid");
206064562Sgshapiro					exit(EX_TEMPFAIL);
206164562Sgshapiro				}
206264562Sgshapiro
206338032Speter				vendor_set_uid(new_euid);
206464562Sgshapiro# if MAILER_SETUID_METHOD == USE_SETEUID
206538032Speter				if (seteuid(new_euid) < 0 && suidwarn)
206664562Sgshapiro				{
206738032Speter					syserr("openmailer: seteuid(%ld) failed",
206838032Speter						(long) new_euid);
206964562Sgshapiro					exit(EX_TEMPFAIL);
207064562Sgshapiro				}
207164562Sgshapiro# endif /* MAILER_SETUID_METHOD == USE_SETEUID */
207264562Sgshapiro# if MAILER_SETUID_METHOD == USE_SETREUID
207338032Speter				if (setreuid(new_ruid, new_euid) < 0 && suidwarn)
207464562Sgshapiro				{
207538032Speter					syserr("openmailer: setreuid(%ld, %ld) failed",
207638032Speter						(long) new_ruid, (long) new_euid);
207764562Sgshapiro					exit(EX_TEMPFAIL);
207864562Sgshapiro				}
207964562Sgshapiro# endif /* MAILER_SETUID_METHOD == USE_SETREUID */
208064562Sgshapiro# if MAILER_SETUID_METHOD == USE_SETUID
208138032Speter				if (new_euid != geteuid() && setuid(new_euid) < 0 && suidwarn)
208264562Sgshapiro				{
208338032Speter					syserr("openmailer: setuid(%ld) failed",
208438032Speter						(long) new_euid);
208564562Sgshapiro					exit(EX_TEMPFAIL);
208664562Sgshapiro				}
208764562Sgshapiro# endif /* MAILER_SETUID_METHOD == USE_SETUID */
208838032Speter			}
208938032Speter			else if (new_ruid != NO_UID)
209038032Speter			{
209138032Speter				vendor_set_uid(new_ruid);
209238032Speter				if (setuid(new_ruid) < 0 && suidwarn)
209364562Sgshapiro				{
209438032Speter					syserr("openmailer: setuid(%ld) failed",
209538032Speter						(long) new_ruid);
209664562Sgshapiro					exit(EX_TEMPFAIL);
209764562Sgshapiro				}
209838032Speter			}
209938032Speter
210038032Speter			if (tTd(11, 2))
210164562Sgshapiro				dprintf("openmailer: running as r/euid=%d/%d, r/egid=%d/%d\n",
210264562Sgshapiro					(int) getuid(), (int) geteuid(),
210364562Sgshapiro					(int) getgid(), (int) getegid());
210438032Speter
210538032Speter			/* move into some "safe" directory */
210638032Speter			if (m->m_execdir != NULL)
210738032Speter			{
210838032Speter				char *q;
210938032Speter
211038032Speter				for (p = m->m_execdir; p != NULL; p = q)
211138032Speter				{
211238032Speter					q = strchr(p, ':');
211338032Speter					if (q != NULL)
211438032Speter						*q = '\0';
211538032Speter					expand(p, buf, sizeof buf, e);
211638032Speter					if (q != NULL)
211738032Speter						*q++ = ':';
211838032Speter					if (tTd(11, 20))
211964562Sgshapiro						dprintf("openmailer: trydir %s\n",
212038032Speter							buf);
212138032Speter					if (buf[0] != '\0' && chdir(buf) >= 0)
212238032Speter						break;
212338032Speter				}
212438032Speter			}
212538032Speter
212638032Speter			/* arrange to filter std & diag output of command */
212764562Sgshapiro			(void) close(rpvect[0]);
212864562Sgshapiro			if (dup2(rpvect[1], STDOUT_FILENO) < 0)
212938032Speter			{
213064562Sgshapiro				syserr("%s... openmailer(%s): cannot dup pipe %d for stdout",
213164562Sgshapiro				       shortenstring(e->e_to, MAXSHORTSTR),
213264562Sgshapiro				       m->m_name, rpvect[1]);
213364562Sgshapiro				_exit(EX_OSERR);
213438032Speter			}
213564562Sgshapiro			(void) close(rpvect[1]);
213664562Sgshapiro
213738032Speter			if (dup2(STDOUT_FILENO, STDERR_FILENO) < 0)
213838032Speter			{
213938032Speter				syserr("%s... openmailer(%s): cannot dup stdout for stderr",
214038032Speter					shortenstring(e->e_to, MAXSHORTSTR),
214138032Speter					m->m_name);
214238032Speter				_exit(EX_OSERR);
214338032Speter			}
214438032Speter
214538032Speter			/* arrange to get standard input */
214638032Speter			(void) close(mpvect[1]);
214738032Speter			if (dup2(mpvect[0], STDIN_FILENO) < 0)
214838032Speter			{
214938032Speter				syserr("%s... openmailer(%s): cannot dup pipe %d for stdin",
215038032Speter					shortenstring(e->e_to, MAXSHORTSTR),
215138032Speter					m->m_name, mpvect[0]);
215238032Speter				_exit(EX_OSERR);
215338032Speter			}
215438032Speter			(void) close(mpvect[0]);
215538032Speter
215638032Speter			/* arrange for all the files to be closed */
215738032Speter			for (i = 3; i < DtableSize; i++)
215838032Speter			{
215938032Speter				register int j;
216038032Speter
216138032Speter				if ((j = fcntl(i, F_GETFD, 0)) != -1)
216264562Sgshapiro					(void) fcntl(i, F_SETFD,
216364562Sgshapiro						     j | FD_CLOEXEC);
216438032Speter			}
216538032Speter
216638032Speter			/* run disconnected from terminal */
216738032Speter			(void) setsid();
216838032Speter
216938032Speter			/* try to execute the mailer */
217064562Sgshapiro			(void) execve(m->m_mailer, (ARGV_T) pv,
217164562Sgshapiro				      (ARGV_T) UserEnviron);
217264562Sgshapiro			save_errno = errno;
217338032Speter			syserr("Cannot exec %s", m->m_mailer);
217438032Speter			if (bitnset(M_LOCALMAILER, m->m_flags) ||
217564562Sgshapiro			    transienterror(save_errno))
217638032Speter				_exit(EX_OSERR);
217738032Speter			_exit(EX_UNAVAILABLE);
217838032Speter		}
217938032Speter
218038032Speter		/*
218138032Speter		**  Set up return value.
218238032Speter		*/
218338032Speter
218438032Speter		if (mci == NULL)
218538032Speter		{
218638032Speter			mci = (MCI *) xalloc(sizeof *mci);
218764562Sgshapiro			memset((char *) mci, '\0', sizeof *mci);
218838032Speter		}
218938032Speter		mci->mci_mailer = m;
219038032Speter		if (clever)
219138032Speter		{
219238032Speter			mci->mci_state = MCIS_OPENING;
219338032Speter			mci_cache(mci);
219438032Speter		}
219538032Speter		else
219638032Speter		{
219738032Speter			mci->mci_state = MCIS_OPEN;
219838032Speter		}
219938032Speter		mci->mci_pid = pid;
220038032Speter		(void) close(mpvect[0]);
220138032Speter		mci->mci_out = fdopen(mpvect[1], "w");
220238032Speter		if (mci->mci_out == NULL)
220338032Speter		{
220438032Speter			syserr("deliver: cannot create mailer output channel, fd=%d",
220538032Speter				mpvect[1]);
220638032Speter			(void) close(mpvect[1]);
220764562Sgshapiro			(void) close(rpvect[0]);
220864562Sgshapiro			(void) close(rpvect[1]);
220938032Speter			rcode = EX_OSERR;
221038032Speter			goto give_up;
221138032Speter		}
221264562Sgshapiro
221364562Sgshapiro		(void) close(rpvect[1]);
221464562Sgshapiro		mci->mci_in = fdopen(rpvect[0], "r");
221564562Sgshapiro		if (mci->mci_in == NULL)
221638032Speter		{
221764562Sgshapiro			syserr("deliver: cannot create mailer input channel, fd=%d",
221864562Sgshapiro			       mpvect[1]);
221964562Sgshapiro			(void) close(rpvect[0]);
222064562Sgshapiro			(void) fclose(mci->mci_out);
222164562Sgshapiro			mci->mci_out = NULL;
222264562Sgshapiro			rcode = EX_OSERR;
222364562Sgshapiro			goto give_up;
222438032Speter		}
222564562Sgshapiro
222664562Sgshapiro		/* Don't cache non-clever connections */
222764562Sgshapiro		if (!clever)
222838032Speter			mci->mci_flags |= MCIF_TEMP;
222938032Speter	}
223038032Speter
223138032Speter	/*
223238032Speter	**  If we are in SMTP opening state, send initial protocol.
223338032Speter	*/
223438032Speter
223538032Speter	if (bitnset(M_7BITS, m->m_flags) &&
223638032Speter	    (!clever || mci->mci_state == MCIS_OPENING))
223738032Speter		mci->mci_flags |= MCIF_7BIT;
223838032Speter#if SMTP
223938032Speter	if (clever && mci->mci_state != MCIS_CLOSED)
224038032Speter	{
224164562Sgshapiro		static u_short again;
224264562Sgshapiro# if SASL && SFIO
224364562Sgshapiro#  define DONE_TLS_B	0x01
224464562Sgshapiro#  define DONE_TLS	bitset(DONE_TLS_B, again)
224564562Sgshapiro# endif /* SASL && SFIO */
224664562Sgshapiro# if STARTTLS
224764562Sgshapiro#  define DONE_STARTTLS_B	0x02
224864562Sgshapiro#  define DONE_STARTTLS	bitset(DONE_STARTTLS_B, again)
224964562Sgshapiro# endif /* STARTTLS */
225064562Sgshapiro# define ONLY_HELO_B	0x04
225164562Sgshapiro# define ONLY_HELO	bitset(ONLY_HELO_B, again)
225264562Sgshapiro# define SET_HELO	again |= ONLY_HELO_B
225364562Sgshapiro# define CLR_HELO	again &= ~ONLY_HELO_B
225438032Speter
225564562Sgshapiro		again = 0;
225664562Sgshapiro# if STARTTLS || (SASL && SFIO)
225764562Sgshapiroreconnect:	/* after switching to an authenticated connection */
225864562Sgshapiro# endif /* STARTTLS || (SASL && SFIO) */
225964562Sgshapiro
226064562Sgshapiro# if SASL
226164562Sgshapiro		mci->mci_saslcap = NULL;
226264562Sgshapiro# endif /* SASL */
226364562Sgshapiro		smtpinit(m, mci, e, ONLY_HELO);
226464562Sgshapiro		CLR_HELO;
226564562Sgshapiro
226664562Sgshapiro# if STARTTLS
226764562Sgshapiro		/* first TLS then AUTH to provide a security layer */
226864562Sgshapiro		if (mci->mci_state != MCIS_CLOSED && !DONE_STARTTLS)
226964562Sgshapiro		{
227064562Sgshapiro			int olderrors;
227164562Sgshapiro			bool hasdot;
227264562Sgshapiro			bool usetls;
227364562Sgshapiro			bool saveQuickAbort = QuickAbort;
227464562Sgshapiro			bool saveSuprErrs = SuprErrs;
227566494Sgshapiro#  if _FFR_TLS_CLT1
227666494Sgshapiro			char *p;
227766494Sgshapiro#  endif /* _FFR_TLS_CLT1 */
227864562Sgshapiro			extern SOCKADDR CurHostAddr;
227964562Sgshapiro
228064562Sgshapiro			rcode = EX_OK;
228164562Sgshapiro			usetls = bitset(MCIF_TLS, mci->mci_flags);
228266494Sgshapiro#  if _FFR_TLS_CLT1
228366494Sgshapiro			if (usetls &&
228466494Sgshapiro			    (p = macvalue(macid("{client_flags}", NULL), e))
228566494Sgshapiro			    != NULL)
228666494Sgshapiro			{
228766494Sgshapiro				for (; *p != '\0'; p++)
228866494Sgshapiro				{
228966494Sgshapiro					/* look for just this one flag */
229066494Sgshapiro					if (*p == D_CLTNOTLS)
229166494Sgshapiro					{
229266494Sgshapiro						usetls = FALSE;
229366494Sgshapiro						break;
229466494Sgshapiro					}
229566494Sgshapiro				}
229666494Sgshapiro			}
229766494Sgshapiro#  endif /* _FFR_TLS_CLT1 */
229866494Sgshapiro
229964562Sgshapiro			hasdot = CurHostName[strlen(CurHostName) - 1] == '.';
230064562Sgshapiro			if (hasdot)
230164562Sgshapiro				CurHostName[strlen(CurHostName) - 1] = '\0';
230264562Sgshapiro			define(macid("{server_name}", NULL),
230364562Sgshapiro			       newstr(CurHostName), e);
230464562Sgshapiro			if (CurHostAddr.sa.sa_family != 0)
230564562Sgshapiro				define(macid("{server_addr}", NULL),
230664562Sgshapiro				       newstr(anynet_ntoa(&CurHostAddr)), e);
230764562Sgshapiro			else
230864562Sgshapiro				define(macid("{server_addr}", NULL), NULL, e);
230964562Sgshapiro#  if _FFR_TLS_O_T
231064562Sgshapiro			if (usetls)
231164562Sgshapiro			{
231264562Sgshapiro				olderrors = Errors;
231364562Sgshapiro				QuickAbort = FALSE;
231464562Sgshapiro				SuprErrs = TRUE;
231564562Sgshapiro				if (rscheck("try_tls", CurHostName, NULL,
231664562Sgshapiro					    e, TRUE, FALSE, 8) != EX_OK
231764562Sgshapiro				    || Errors > olderrors)
231864562Sgshapiro					usetls = FALSE;
231964562Sgshapiro				SuprErrs = saveSuprErrs;
232064562Sgshapiro				QuickAbort = saveQuickAbort;
232164562Sgshapiro			}
232264562Sgshapiro#  endif /* _FFR_TLS_O_T */
232364562Sgshapiro
232464562Sgshapiro			/* undo change of CurHostName */
232564562Sgshapiro			if (hasdot)
232664562Sgshapiro				CurHostName[strlen(CurHostName)] = '.';
232764562Sgshapiro			if (usetls)
232864562Sgshapiro			{
232964562Sgshapiro				if ((rcode = starttls(m, mci, e)) == EX_OK)
233064562Sgshapiro				{
233164562Sgshapiro					/* start again without STARTTLS */
233264562Sgshapiro					again |= DONE_STARTTLS_B;
233364562Sgshapiro					mci->mci_flags |= MCIF_TLSACT;
233464562Sgshapiro				}
233564562Sgshapiro				else
233664562Sgshapiro				{
233764562Sgshapiro					char *s;
233864562Sgshapiro
233964562Sgshapiro					/*
234064562Sgshapiro					**  TLS negotation failed, what to do?
234164562Sgshapiro					**  fall back to unencrypted connection
234264562Sgshapiro					**  or abort? How to decide?
234364562Sgshapiro					**  set a macro and call a ruleset.
234464562Sgshapiro					*/
234564562Sgshapiro					mci->mci_flags &= ~MCIF_TLS;
234664562Sgshapiro					switch (rcode)
234764562Sgshapiro					{
234864562Sgshapiro					  case EX_TEMPFAIL:
234964562Sgshapiro						s = "TEMP";
235064562Sgshapiro						break;
235164562Sgshapiro					  case EX_USAGE:
235264562Sgshapiro						s = "USAGE";
235364562Sgshapiro						break;
235464562Sgshapiro					  case EX_PROTOCOL:
235564562Sgshapiro						s = "PROTOCOL";
235664562Sgshapiro						break;
235764562Sgshapiro					  case EX_SOFTWARE:
235864562Sgshapiro						s = "SOFTWARE";
235964562Sgshapiro						break;
236064562Sgshapiro
236164562Sgshapiro					  /* everything else is a failure */
236264562Sgshapiro					  default:
236364562Sgshapiro						s = "FAILURE";
236464562Sgshapiro						rcode = EX_TEMPFAIL;
236564562Sgshapiro					}
236664562Sgshapiro					define(macid("{verify}", NULL),
236764562Sgshapiro					       newstr(s), e);
236864562Sgshapiro				}
236964562Sgshapiro			}
237064562Sgshapiro			else
237164562Sgshapiro				define(macid("{verify}", NULL), "NONE", e);
237264562Sgshapiro			olderrors = Errors;
237364562Sgshapiro			QuickAbort = FALSE;
237464562Sgshapiro			SuprErrs = TRUE;
237564562Sgshapiro
237664562Sgshapiro			/*
237764562Sgshapiro			**  rcode == EX_SOFTWARE is special:
237864562Sgshapiro			**  the TLS negotation failed
237964562Sgshapiro			**  we have to drop the connection no matter what
238064562Sgshapiro			**  However, we call tls_server to give it the chance
238164562Sgshapiro			**  to log the problem and return an appropriate
238264562Sgshapiro			**  error code.
238364562Sgshapiro			*/
238464562Sgshapiro			if (rscheck("tls_server",
238564562Sgshapiro				     macvalue(macid("{verify}", NULL), e),
238664562Sgshapiro				     NULL, e, TRUE, TRUE, 6) != EX_OK ||
238764562Sgshapiro			    Errors > olderrors ||
238864562Sgshapiro			    rcode == EX_SOFTWARE)
238964562Sgshapiro			{
239064562Sgshapiro				char enhsc[ENHSCLEN];
239164562Sgshapiro				extern char MsgBuf[];
239264562Sgshapiro
239364562Sgshapiro				if (ISSMTPCODE(MsgBuf) &&
239464562Sgshapiro				    extenhsc(MsgBuf + 4, ' ', enhsc) > 0)
239564562Sgshapiro				{
239664562Sgshapiro					p = newstr(MsgBuf);
239764562Sgshapiro				}
239864562Sgshapiro				else
239964562Sgshapiro				{
240064562Sgshapiro					p = "403 4.7.0 server not authenticated.";
240164562Sgshapiro					(void) strlcpy(enhsc, "4.7.0",
240264562Sgshapiro						       sizeof enhsc);
240364562Sgshapiro				}
240464562Sgshapiro				SuprErrs = saveSuprErrs;
240564562Sgshapiro				QuickAbort = saveQuickAbort;
240664562Sgshapiro
240764562Sgshapiro				if (rcode == EX_SOFTWARE)
240864562Sgshapiro				{
240964562Sgshapiro					/* drop the connection */
241064562Sgshapiro					mci->mci_state = MCIS_QUITING;
241164562Sgshapiro					if (mci->mci_in != NULL)
241264562Sgshapiro					{
241364562Sgshapiro						(void) fclose(mci->mci_in);
241464562Sgshapiro						mci->mci_in = NULL;
241564562Sgshapiro					}
241664562Sgshapiro					mci->mci_flags &= ~MCIF_TLSACT;
241764562Sgshapiro					(void) endmailer(mci, e, pv);
241864562Sgshapiro				}
241964562Sgshapiro				else
242064562Sgshapiro				{
242164562Sgshapiro					/* abort transfer */
242264562Sgshapiro					smtpquit(m, mci, e);
242364562Sgshapiro				}
242464562Sgshapiro
242564562Sgshapiro				/* temp or permanent failure? */
242664562Sgshapiro				rcode = (*p == '4') ? EX_TEMPFAIL
242764562Sgshapiro						    : EX_UNAVAILABLE;
242864562Sgshapiro				mci_setstat(mci, rcode, newstr(enhsc), p);
242964562Sgshapiro
243064562Sgshapiro				/*
243164562Sgshapiro				**  hack to get the error message into
243264562Sgshapiro				**  the envelope (done in giveresponse())
243364562Sgshapiro				*/
243464562Sgshapiro				(void) strlcpy(SmtpError, p, sizeof SmtpError);
243564562Sgshapiro			}
243664562Sgshapiro			QuickAbort = saveQuickAbort;
243764562Sgshapiro			SuprErrs = saveSuprErrs;
243864562Sgshapiro			if (DONE_STARTTLS && mci->mci_state != MCIS_CLOSED)
243964562Sgshapiro			{
244064562Sgshapiro				SET_HELO;
244164562Sgshapiro				mci->mci_flags &= ~MCIF_EXTENS;
244264562Sgshapiro				goto reconnect;
244364562Sgshapiro			}
244464562Sgshapiro		}
244564562Sgshapiro# endif /* STARTTLS */
244664562Sgshapiro# if SASL
244764562Sgshapiro		/* if other server supports authentication let's authenticate */
244864562Sgshapiro		if (mci->mci_state != MCIS_CLOSED &&
244964562Sgshapiro		    mci->mci_saslcap != NULL &&
245064562Sgshapiro#  if SFIO
245164562Sgshapiro		    !DONE_TLS &&
245264562Sgshapiro#  endif /* SFIO */
245364562Sgshapiro		    SASLInfo != NULL)
245464562Sgshapiro		{
245564562Sgshapiro			/*
245664562Sgshapiro			**  should we require some minimum authentication?
245764562Sgshapiro			**  XXX ignore result?
245864562Sgshapiro			*/
245964562Sgshapiro			if (smtpauth(m, mci, e) == EX_OK)
246064562Sgshapiro			{
246164562Sgshapiro#  if SFIO
246264562Sgshapiro				int result;
246364562Sgshapiro				sasl_ssf_t *ssf;
246464562Sgshapiro
246564562Sgshapiro				/* get security strength (features) */
246664562Sgshapiro				result = sasl_getprop(mci->mci_conn, SASL_SSF,
246764562Sgshapiro						      (void **) &ssf);
246864562Sgshapiro				if (LogLevel > 9)
246964562Sgshapiro					sm_syslog(LOG_INFO, NOQID,
247064562Sgshapiro						  "SASL: outgoing connection to %.64s: mech=%.16s, bits=%d",
247164562Sgshapiro						  mci->mci_host,
247264562Sgshapiro						  macvalue(macid("{auth_type}",
247364562Sgshapiro								 NULL), e),
247464562Sgshapiro						  *ssf);
247564562Sgshapiro				/*
247664562Sgshapiro				**  only switch to encrypted connection
247764562Sgshapiro				**  if a security layer has been negotiated
247864562Sgshapiro				*/
247964562Sgshapiro				if (result == SASL_OK && *ssf > 0)
248064562Sgshapiro				{
248164562Sgshapiro					/*
248264562Sgshapiro					**  convert sfio stuff to use SASL
248364562Sgshapiro					**  check return values
248464562Sgshapiro					**  if the call fails,
248564562Sgshapiro					**  fall back to unencrypted version
248664562Sgshapiro					**  unless some cf option requires
248764562Sgshapiro					**  encryption then the connection must
248864562Sgshapiro					**  be aborted
248964562Sgshapiro					*/
249064562Sgshapiro					if (sfdcsasl(mci->mci_in, mci->mci_out,
249164562Sgshapiro						     mci->mci_conn) == 0)
249264562Sgshapiro					{
249364562Sgshapiro						again |= DONE_TLS_B;
249464562Sgshapiro						SET_HELO;
249564562Sgshapiro						mci->mci_flags &= ~MCIF_EXTENS;
249664562Sgshapiro						mci->mci_flags |= MCIF_AUTHACT;
249764562Sgshapiro						goto reconnect;
249864562Sgshapiro					}
249964562Sgshapiro					syserr("SASL TLS switch failed in client");
250064562Sgshapiro				}
250164562Sgshapiro				/* else? XXX */
250264562Sgshapiro#  endif /* SFIO */
250364562Sgshapiro				mci->mci_flags |= MCIF_AUTHACT;
250464562Sgshapiro
250564562Sgshapiro			}
250664562Sgshapiro		}
250764562Sgshapiro# endif /* SASL */
250838032Speter	}
250938032Speter
251064562Sgshapiro#endif /* SMTP */
251164562Sgshapiro
251238032Speterdo_transfer:
251338032Speter	/* clear out per-message flags from connection structure */
251438032Speter	mci->mci_flags &= ~(MCIF_CVT7TO8|MCIF_CVT8TO7);
251538032Speter
251638032Speter	if (bitset(EF_HAS8BIT, e->e_flags) &&
251738032Speter	    !bitset(EF_DONT_MIME, e->e_flags) &&
251838032Speter	    bitnset(M_7BITS, m->m_flags))
251938032Speter		mci->mci_flags |= MCIF_CVT8TO7;
252038032Speter
252138032Speter#if MIME7TO8
252238032Speter	if (bitnset(M_MAKE8BIT, m->m_flags) &&
252338032Speter	    !bitset(MCIF_7BIT, mci->mci_flags) &&
252438032Speter	    (p = hvalue("Content-Transfer-Encoding", e->e_header)) != NULL &&
252538032Speter	     (strcasecmp(p, "quoted-printable") == 0 ||
252638032Speter	      strcasecmp(p, "base64") == 0) &&
252738032Speter	    (p = hvalue("Content-Type", e->e_header)) != NULL)
252838032Speter	{
252938032Speter		/* may want to convert 7 -> 8 */
253038032Speter		/* XXX should really parse it here -- and use a class XXX */
253138032Speter		if (strncasecmp(p, "text/plain", 10) == 0 &&
253238032Speter		    (p[10] == '\0' || p[10] == ' ' || p[10] == ';'))
253338032Speter			mci->mci_flags |= MCIF_CVT7TO8;
253438032Speter	}
253564562Sgshapiro#endif /* MIME7TO8 */
253638032Speter
253738032Speter	if (tTd(11, 1))
253838032Speter	{
253964562Sgshapiro		dprintf("openmailer: ");
254038032Speter		mci_dump(mci, FALSE);
254138032Speter	}
254238032Speter
254338032Speter	if (mci->mci_state != MCIS_OPEN)
254438032Speter	{
254538032Speter		/* couldn't open the mailer */
254638032Speter		rcode = mci->mci_exitstat;
254738032Speter		errno = mci->mci_errno;
254838032Speter#if NAMED_BIND
254938032Speter		h_errno = mci->mci_herrno;
255064562Sgshapiro#endif /* NAMED_BIND */
255138032Speter		if (rcode == EX_OK)
255238032Speter		{
255338032Speter			/* shouldn't happen */
255464562Sgshapiro			syserr("554 5.3.5 deliver: mci=%lx rcode=%d errno=%d state=%d sig=%s",
255564562Sgshapiro				(u_long) mci, rcode, errno, mci->mci_state,
255638032Speter				firstsig);
255738032Speter			mci_dump_all(TRUE);
255838032Speter			rcode = EX_SOFTWARE;
255938032Speter		}
256038032Speter#if DAEMON
256164562Sgshapiro		else if (nummxhosts > hostnum)
256238032Speter		{
256338032Speter			/* try next MX site */
256438032Speter			goto tryhost;
256538032Speter		}
256664562Sgshapiro#endif /* DAEMON */
256738032Speter	}
256838032Speter	else if (!clever)
256938032Speter	{
257038032Speter		/*
257138032Speter		**  Format and send message.
257238032Speter		*/
257338032Speter
257438032Speter		putfromline(mci, e);
257543730Speter		(*e->e_puthdr)(mci, e->e_header, e, M87F_OUTER);
257638032Speter		(*e->e_putbody)(mci, e, NULL);
257738032Speter
257838032Speter		/* get the exit status */
257938032Speter		rcode = endmailer(mci, e, pv);
258038032Speter	}
258138032Speter	else
258238032Speter#if SMTP
258338032Speter	{
258438032Speter		/*
258538032Speter		**  Send the MAIL FROM: protocol
258638032Speter		*/
258738032Speter
258838032Speter		rcode = smtpmailfrom(m, mci, e);
258938032Speter		if (rcode == EX_OK)
259038032Speter		{
259138032Speter			register char *t = tobuf;
259238032Speter			register int i;
259338032Speter
259438032Speter			/* send the recipient list */
259538032Speter			tobuf[0] = '\0';
259664562Sgshapiro
259738032Speter			for (to = tochain; to != NULL; to = to->q_tchain)
259838032Speter			{
259938032Speter				e->e_to = to->q_paddr;
260064562Sgshapiro#if !_FFR_DYNAMIC_TOBUF
260164562Sgshapiro				if (strlen(to->q_paddr) +
260264562Sgshapiro				    (t - tobuf) + 2 > sizeof tobuf)
260338032Speter				{
260438032Speter					/* not enough room */
260538032Speter					continue;
260638032Speter				}
260764562Sgshapiro#endif /* !_FFR_DYNAMIC_TOBUF */
260864562Sgshapiro
260964562Sgshapiro# if STARTTLS
261064562Sgshapiro#  if _FFR_TLS_RCPT
261164562Sgshapiro				i = rscheck("tls_rcpt", to->q_user, NULL, e,
261264562Sgshapiro					    TRUE, TRUE, 4);
261364562Sgshapiro				if (i != EX_OK)
261438032Speter				{
261564562Sgshapiro					markfailure(e, to, mci, i, FALSE);
261664562Sgshapiro					giveresponse(i, to->q_status,  m,
261764562Sgshapiro						     mci, ctladdr, xstart, e);
261864562Sgshapiro					continue;
261938032Speter				}
262064562Sgshapiro#  endif /* _FFR_TLS_RCPT */
262164562Sgshapiro# endif /* STARTTLS */
262264562Sgshapiro
262364562Sgshapiro				if ((i = smtprcpt(to, m, mci, e)) != EX_OK)
262464562Sgshapiro				{
262564562Sgshapiro					markfailure(e, to, mci, i, FALSE);
262664562Sgshapiro					giveresponse(i, to->q_status,  m,
262764562Sgshapiro						     mci, ctladdr, xstart, e);
262864562Sgshapiro				}
262938032Speter				else
263038032Speter				{
263138032Speter					*t++ = ',';
263238032Speter					for (p = to->q_paddr; *p; *t++ = *p++)
263338032Speter						continue;
263438032Speter					*t = '\0';
263538032Speter				}
263638032Speter			}
263738032Speter
263838032Speter			/* now send the data */
263938032Speter			if (tobuf[0] == '\0')
264038032Speter			{
264138032Speter				rcode = EX_OK;
264238032Speter				e->e_to = NULL;
264338032Speter				if (bitset(MCIF_CACHED, mci->mci_flags))
264438032Speter					smtprset(m, mci, e);
264538032Speter			}
264638032Speter			else
264738032Speter			{
264838032Speter				e->e_to = tobuf + 1;
264938032Speter				rcode = smtpdata(m, mci, e);
265038032Speter			}
265138032Speter		}
265238032Speter# if DAEMON
265364562Sgshapiro		if (rcode == EX_TEMPFAIL && nummxhosts > hostnum)
265438032Speter		{
265538032Speter			/* try next MX site */
265638032Speter			goto tryhost;
265738032Speter		}
265864562Sgshapiro# endif /* DAEMON */
265938032Speter	}
266064562Sgshapiro#else /* SMTP */
266138032Speter	{
266264562Sgshapiro		syserr("554 5.3.5 deliver: need SMTP compiled to use clever mailer");
266338032Speter		rcode = EX_CONFIG;
266438032Speter		goto give_up;
266538032Speter	}
266638032Speter#endif /* SMTP */
266738032Speter#if NAMED_BIND
266838032Speter	if (ConfigLevel < 2)
266938032Speter		_res.options |= RES_DEFNAMES | RES_DNSRCH;	/* XXX */
267064562Sgshapiro#endif /* NAMED_BIND */
267138032Speter
267238032Speter	if (tTd(62, 1))
267338032Speter		checkfds("after delivery");
267438032Speter
267538032Speter	/*
267638032Speter	**  Do final status disposal.
267738032Speter	**	We check for something in tobuf for the SMTP case.
267838032Speter	**	If we got a temporary failure, arrange to queue the
267938032Speter	**		addressees.
268038032Speter	*/
268138032Speter
268238032Speter  give_up:
268338032Speter#if SMTP
268438032Speter	if (bitnset(M_LMTP, m->m_flags))
268538032Speter	{
268638032Speter		lmtp_rcode = rcode;
268738032Speter		tobuf[0] = '\0';
268838032Speter		anyok = FALSE;
268938032Speter	}
269038032Speter	else
269164562Sgshapiro#endif /* SMTP */
269238032Speter		anyok = rcode == EX_OK;
269338032Speter
269438032Speter	for (to = tochain; to != NULL; to = to->q_tchain)
269538032Speter	{
269638032Speter		/* see if address already marked */
269764562Sgshapiro		if (!QS_IS_OK(to->q_state))
269838032Speter			continue;
269938032Speter
270038032Speter#if SMTP
270138032Speter		/* if running LMTP, get the status for each address */
270238032Speter		if (bitnset(M_LMTP, m->m_flags))
270338032Speter		{
270438032Speter			if (lmtp_rcode == EX_OK)
270538032Speter				rcode = smtpgetstat(m, mci, e);
270638032Speter			if (rcode == EX_OK)
270738032Speter			{
270866494Sgshapiro#if _FFR_DYNAMIC_TOBUF
270966494Sgshapiro				(void) strlcat(tobuf, ",", tobufsize);
271066494Sgshapiro				(void) strlcat(tobuf, to->q_paddr, tobufsize);
271166494Sgshapiro#else /* _FFR_DYNAMIC_TOBUF */
271264562Sgshapiro				if (strlen(to->q_paddr) +
271364562Sgshapiro				    strlen(tobuf) + 2 > sizeof tobuf)
271438032Speter				{
271538032Speter					syserr("LMTP tobuf overflow");
271638032Speter				}
271738032Speter				else
271838032Speter				{
271964562Sgshapiro					(void) strlcat(tobuf, ",",
272064562Sgshapiro						       sizeof tobuf);
272164562Sgshapiro					(void) strlcat(tobuf, to->q_paddr,
272264562Sgshapiro						       sizeof tobuf);
272338032Speter				}
272466494Sgshapiro#endif /* _FFR_DYNAMIC_TOBUF */
272538032Speter				anyok = TRUE;
272638032Speter			}
272738032Speter			else
272838032Speter			{
272938032Speter				e->e_to = to->q_paddr;
273064562Sgshapiro				markfailure(e, to, mci, rcode, TRUE);
273164562Sgshapiro				giveresponse(rcode, to->q_status, m, mci,
273264562Sgshapiro					     ctladdr, xstart, e);
273338032Speter				e->e_to = tobuf + 1;
273438032Speter				continue;
273538032Speter			}
273638032Speter		}
273738032Speter		else
273864562Sgshapiro#endif /* SMTP */
273938032Speter		{
274038032Speter			/* mark bad addresses */
274138032Speter			if (rcode != EX_OK)
274238032Speter			{
274338032Speter				if (goodmxfound && rcode == EX_NOHOST)
274438032Speter					rcode = EX_TEMPFAIL;
274564562Sgshapiro				markfailure(e, to, mci, rcode, TRUE);
274638032Speter				continue;
274738032Speter			}
274838032Speter		}
274938032Speter
275038032Speter		/* successful delivery */
275164562Sgshapiro		to->q_state = QS_SENT;
275238032Speter		to->q_statdate = curtime();
275338032Speter		e->e_nsent++;
275464562Sgshapiro
275564562Sgshapiro#if QUEUE
275664562Sgshapiro		/*
275764562Sgshapiro		**  Checkpoint the send list every few addresses
275864562Sgshapiro		*/
275964562Sgshapiro
276066494Sgshapiro		if (CheckpointInterval > 0 && e->e_nsent >= CheckpointInterval)
276164562Sgshapiro		{
276264562Sgshapiro			queueup(e, FALSE);
276364562Sgshapiro			e->e_nsent = 0;
276464562Sgshapiro		}
276564562Sgshapiro#endif /* QUEUE */
276664562Sgshapiro
276738032Speter		if (bitnset(M_LOCALMAILER, m->m_flags) &&
276838032Speter		    bitset(QPINGONSUCCESS, to->q_flags))
276938032Speter		{
277038032Speter			to->q_flags |= QDELIVERED;
277138032Speter			to->q_status = "2.1.5";
277238032Speter			fprintf(e->e_xfp, "%s... Successfully delivered\n",
277338032Speter				to->q_paddr);
277438032Speter		}
277538032Speter		else if (bitset(QPINGONSUCCESS, to->q_flags) &&
277638032Speter			 bitset(QPRIMARY, to->q_flags) &&
277738032Speter			 !bitset(MCIF_DSN, mci->mci_flags))
277838032Speter		{
277938032Speter			to->q_flags |= QRELAYED;
278038032Speter			fprintf(e->e_xfp, "%s... relayed; expect no further notifications\n",
278138032Speter				to->q_paddr);
278238032Speter		}
278338032Speter	}
278438032Speter
278538032Speter#if SMTP
278638032Speter	if (bitnset(M_LMTP, m->m_flags))
278738032Speter	{
278838032Speter		/*
278938032Speter		**  Global information applies to the last recipient only;
279038032Speter		**  clear it out to avoid bogus errors.
279138032Speter		*/
279238032Speter
279338032Speter		rcode = EX_OK;
279438032Speter		e->e_statmsg = NULL;
279538032Speter
279638032Speter		/* reset the mci state for the next transaction */
279738032Speter		if (mci != NULL && mci->mci_state == MCIS_ACTIVE)
279838032Speter			mci->mci_state = MCIS_OPEN;
279938032Speter	}
280064562Sgshapiro#endif /* SMTP */
280138032Speter
280238032Speter	if (tobuf[0] != '\0')
280364562Sgshapiro		giveresponse(rcode, NULL, m, mci, ctladdr, xstart, e);
280438032Speter	if (anyok)
280538032Speter		markstats(e, tochain, FALSE);
280638032Speter	mci_store_persistent(mci);
280738032Speter
280838032Speter#if SMTP
280938032Speter	/* now close the connection */
281038032Speter	if (clever && mci != NULL && mci->mci_state != MCIS_CLOSED &&
281138032Speter	    !bitset(MCIF_CACHED, mci->mci_flags))
281238032Speter		smtpquit(m, mci, e);
281364562Sgshapiro#endif /* SMTP */
281438032Speter
281538032Speter	/*
281638032Speter	**  Restore state and return.
281738032Speter	*/
281838032Speter
281938032Speter#if XDEBUG
282038032Speter	{
282138032Speter		char wbuf[MAXLINE];
282238032Speter
282338032Speter		/* make absolutely certain 0, 1, and 2 are in use */
282438032Speter		snprintf(wbuf, sizeof wbuf, "%s... end of deliver(%s)",
282538032Speter			e->e_to == NULL ? "NO-TO-LIST"
282638032Speter					: shortenstring(e->e_to, MAXSHORTSTR),
282738032Speter			m->m_name);
282838032Speter		checkfd012(wbuf);
282938032Speter	}
283064562Sgshapiro#endif /* XDEBUG */
283138032Speter
283238032Speter	errno = 0;
283338032Speter	define('g', (char *) NULL, e);
283464562Sgshapiro	e->e_to = NULL;
283564562Sgshapiro	return rcode;
283638032Speter}
283764562Sgshapiro
283838032Speter/*
283938032Speter**  MARKFAILURE -- mark a failure on a specific address.
284038032Speter**
284138032Speter**	Parameters:
284238032Speter**		e -- the envelope we are sending.
284338032Speter**		q -- the address to mark.
284438032Speter**		mci -- mailer connection information.
284538032Speter**		rcode -- the code signifying the particular failure.
284664562Sgshapiro**		ovr -- override an existing code?
284738032Speter**
284838032Speter**	Returns:
284938032Speter**		none.
285038032Speter**
285138032Speter**	Side Effects:
285238032Speter**		marks the address (and possibly the envelope) with the
285338032Speter**			failure so that an error will be returned or
285438032Speter**			the message will be queued, as appropriate.
285538032Speter*/
285638032Speter
285764562Sgshapirostatic void
285864562Sgshapiromarkfailure(e, q, mci, rcode, ovr)
285938032Speter	register ENVELOPE *e;
286038032Speter	register ADDRESS *q;
286138032Speter	register MCI *mci;
286238032Speter	int rcode;
286364562Sgshapiro	bool ovr;
286438032Speter{
286564562Sgshapiro	char *status = NULL;
286664562Sgshapiro	char *rstatus = NULL;
286738032Speter
286838032Speter	switch (rcode)
286938032Speter	{
287038032Speter	  case EX_OK:
287138032Speter		break;
287238032Speter
287338032Speter	  case EX_TEMPFAIL:
287438032Speter	  case EX_IOERR:
287538032Speter	  case EX_OSERR:
287664562Sgshapiro		q->q_state = QS_QUEUEUP;
287738032Speter		break;
287838032Speter
287938032Speter	  default:
288064562Sgshapiro		q->q_state = QS_BADADDR;
288138032Speter		break;
288238032Speter	}
288338032Speter
288438032Speter	/* find most specific error code possible */
288538032Speter	if (mci != NULL && mci->mci_status != NULL)
288638032Speter	{
288764562Sgshapiro		status = mci->mci_status;
288838032Speter		if (mci->mci_rstatus != NULL)
288964562Sgshapiro			rstatus = newstr(mci->mci_rstatus);
289038032Speter		else
289164562Sgshapiro			rstatus = NULL;
289238032Speter	}
289338032Speter	else if (e->e_status != NULL)
289438032Speter	{
289564562Sgshapiro		status = e->e_status;
289664562Sgshapiro		rstatus = NULL;
289738032Speter	}
289838032Speter	else
289938032Speter	{
290038032Speter		switch (rcode)
290138032Speter		{
290238032Speter		  case EX_USAGE:
290364562Sgshapiro			status = "5.5.4";
290438032Speter			break;
290538032Speter
290638032Speter		  case EX_DATAERR:
290764562Sgshapiro			status = "5.5.2";
290838032Speter			break;
290938032Speter
291038032Speter		  case EX_NOUSER:
291164562Sgshapiro			status = "5.1.1";
291238032Speter			break;
291338032Speter
291438032Speter		  case EX_NOHOST:
291564562Sgshapiro			status = "5.1.2";
291638032Speter			break;
291738032Speter
291838032Speter		  case EX_NOINPUT:
291938032Speter		  case EX_CANTCREAT:
292038032Speter		  case EX_NOPERM:
292164562Sgshapiro			status = "5.3.0";
292238032Speter			break;
292338032Speter
292438032Speter		  case EX_UNAVAILABLE:
292538032Speter		  case EX_SOFTWARE:
292638032Speter		  case EX_OSFILE:
292738032Speter		  case EX_PROTOCOL:
292838032Speter		  case EX_CONFIG:
292964562Sgshapiro			status = "5.5.0";
293038032Speter			break;
293138032Speter
293238032Speter		  case EX_OSERR:
293338032Speter		  case EX_IOERR:
293464562Sgshapiro			status = "4.5.0";
293538032Speter			break;
293638032Speter
293738032Speter		  case EX_TEMPFAIL:
293864562Sgshapiro			status = "4.2.0";
293938032Speter			break;
294038032Speter		}
294138032Speter	}
294238032Speter
294364562Sgshapiro	/* new status? */
294464562Sgshapiro	if (status != NULL && *status != '\0' && (ovr || q->q_status == NULL ||
294564562Sgshapiro	    *q->q_status == '\0' || *q->q_status < *status))
294664562Sgshapiro	{
294764562Sgshapiro		q->q_status = status;
294864562Sgshapiro		q->q_rstatus = rstatus;
294964562Sgshapiro	}
295038032Speter	if (rcode != EX_OK && q->q_rstatus == NULL &&
295138032Speter	    q->q_mailer != NULL && q->q_mailer->m_diagtype != NULL &&
295264562Sgshapiro	    strcasecmp(q->q_mailer->m_diagtype, "X-UNIX") == 0)
295338032Speter	{
295464562Sgshapiro		char buf[16];
295538032Speter
295638032Speter		(void) snprintf(buf, sizeof buf, "%d", rcode);
295738032Speter		q->q_rstatus = newstr(buf);
295838032Speter	}
295964562Sgshapiro
296064562Sgshapiro	q->q_statdate = curtime();
296164562Sgshapiro	if (CurHostName != NULL && CurHostName[0] != '\0' &&
296264562Sgshapiro	    mci != NULL && !bitset(M_LOCALMAILER, mci->mci_flags))
296364562Sgshapiro		q->q_statmta = newstr(CurHostName);
296438032Speter}
296538032Speter/*
296638032Speter**  ENDMAILER -- Wait for mailer to terminate.
296738032Speter**
296838032Speter**	We should never get fatal errors (e.g., segmentation
296938032Speter**	violation), so we report those specially.  For other
297038032Speter**	errors, we choose a status message (into statmsg),
297138032Speter**	and if it represents an error, we print it.
297238032Speter**
297338032Speter**	Parameters:
297438032Speter**		pid -- pid of mailer.
297538032Speter**		e -- the current envelope.
297638032Speter**		pv -- the parameter vector that invoked the mailer
297738032Speter**			(for error messages).
297838032Speter**
297938032Speter**	Returns:
298038032Speter**		exit code of mailer.
298138032Speter**
298238032Speter**	Side Effects:
298338032Speter**		none.
298438032Speter*/
298538032Speter
298664562Sgshapirostatic jmp_buf	EndWaitTimeout;
298764562Sgshapiro
298864562Sgshapirostatic void
298964562Sgshapiroendwaittimeout()
299064562Sgshapiro{
299164562Sgshapiro	errno = ETIMEDOUT;
299264562Sgshapiro	longjmp(EndWaitTimeout, 1);
299364562Sgshapiro}
299464562Sgshapiro
299538032Speterint
299638032Speterendmailer(mci, e, pv)
299738032Speter	register MCI *mci;
299838032Speter	register ENVELOPE *e;
299938032Speter	char **pv;
300038032Speter{
300138032Speter	int st;
300264562Sgshapiro	int save_errno = errno;
300364562Sgshapiro	char buf[MAXLINE];
300464562Sgshapiro	EVENT *ev = NULL;
300538032Speter
300664562Sgshapiro
300738032Speter	mci_unlock_host(mci);
300838032Speter
300964562Sgshapiro#if SASL
301064562Sgshapiro	/* shutdown SASL */
301164562Sgshapiro	if (bitset(MCIF_AUTHACT, mci->mci_flags))
301264562Sgshapiro	{
301364562Sgshapiro		sasl_dispose(&mci->mci_conn);
301464562Sgshapiro		mci->mci_flags &= ~MCIF_AUTHACT;
301564562Sgshapiro	}
301664562Sgshapiro#endif /* SASL */
301764562Sgshapiro
301864562Sgshapiro#if STARTTLS
301964562Sgshapiro	/* shutdown TLS */
302064562Sgshapiro	(void) endtlsclt(mci);
302164562Sgshapiro#endif /* STARTTLS */
302264562Sgshapiro
302364562Sgshapiro	/* close output to mailer */
302464562Sgshapiro	if (mci->mci_out != NULL)
302564562Sgshapiro		(void) fclose(mci->mci_out);
302664562Sgshapiro
302764562Sgshapiro	/* copy any remaining input to transcript */
302864562Sgshapiro	if (mci->mci_in != NULL && mci->mci_state != MCIS_ERROR &&
302964562Sgshapiro	    e->e_xfp != NULL)
303064562Sgshapiro	{
303164562Sgshapiro		while (sfgets(buf, sizeof buf, mci->mci_in,
303264562Sgshapiro		       TimeOuts.to_quit, "Draining Input") != NULL)
303364562Sgshapiro			(void) fputs(buf, e->e_xfp);
303464562Sgshapiro	}
303564562Sgshapiro
303664562Sgshapiro	/* now close the input */
303738032Speter	if (mci->mci_in != NULL)
303864562Sgshapiro		(void) fclose(mci->mci_in);
303938032Speter	mci->mci_in = mci->mci_out = NULL;
304038032Speter	mci->mci_state = MCIS_CLOSED;
304138032Speter
304264562Sgshapiro	errno = save_errno;
304364562Sgshapiro
304438032Speter	/* in the IPC case there is nothing to wait for */
304538032Speter	if (mci->mci_pid == 0)
304664562Sgshapiro		return EX_OK;
304738032Speter
304864562Sgshapiro	/* put a timeout around the wait */
304964562Sgshapiro	if (mci->mci_mailer->m_wait > 0)
305064562Sgshapiro	{
305164562Sgshapiro		if (setjmp(EndWaitTimeout) == 0)
305264562Sgshapiro			ev = setevent(mci->mci_mailer->m_wait,
305364562Sgshapiro				      endwaittimeout, 0);
305464562Sgshapiro		else
305564562Sgshapiro		{
305666494Sgshapiro			syserr("endmailer %s: wait timeout (%ld)",
305764562Sgshapiro			       mci->mci_mailer->m_name,
305866494Sgshapiro			       (long) mci->mci_mailer->m_wait);
305964562Sgshapiro			return EX_TEMPFAIL;
306064562Sgshapiro		}
306164562Sgshapiro	}
306238032Speter
306364562Sgshapiro	/* wait for the mailer process, collect status */
306438032Speter	st = waitfor(mci->mci_pid);
306564562Sgshapiro	save_errno = errno;
306664562Sgshapiro	if (ev != NULL)
306764562Sgshapiro		clrevent(ev);
306864562Sgshapiro	errno = save_errno;
306964562Sgshapiro
307038032Speter	if (st == -1)
307138032Speter	{
307238032Speter		syserr("endmailer %s: wait", mci->mci_mailer->m_name);
307364562Sgshapiro		return EX_SOFTWARE;
307438032Speter	}
307538032Speter
307638032Speter	if (WIFEXITED(st))
307738032Speter	{
307838032Speter		/* normal death -- return status */
307938032Speter		return (WEXITSTATUS(st));
308038032Speter	}
308138032Speter
308238032Speter	/* it died a horrid death */
308364562Sgshapiro	syserr("451 4.3.0 mailer %s died with signal %d%s",
308464562Sgshapiro		mci->mci_mailer->m_name, WTERMSIG(st),
308564562Sgshapiro		WCOREDUMP(st) ? " (core dumped)" :
308664562Sgshapiro		(WIFSTOPPED(st) ? " (stopped)" : ""));
308738032Speter
308838032Speter	/* log the arguments */
308938032Speter	if (pv != NULL && e->e_xfp != NULL)
309038032Speter	{
309138032Speter		register char **av;
309238032Speter
309338032Speter		fprintf(e->e_xfp, "Arguments:");
309438032Speter		for (av = pv; *av != NULL; av++)
309538032Speter			fprintf(e->e_xfp, " %s", *av);
309638032Speter		fprintf(e->e_xfp, "\n");
309738032Speter	}
309838032Speter
309938032Speter	ExitStat = EX_TEMPFAIL;
310064562Sgshapiro	return EX_TEMPFAIL;
310138032Speter}
310238032Speter/*
310338032Speter**  GIVERESPONSE -- Interpret an error response from a mailer
310438032Speter**
310538032Speter**	Parameters:
310664562Sgshapiro**		status -- the status code from the mailer (high byte
310738032Speter**			only; core dumps must have been taken care of
310838032Speter**			already).
310964562Sgshapiro**		dsn -- the DSN associated with the address, if any.
311038032Speter**		m -- the mailer info for this mailer.
311138032Speter**		mci -- the mailer connection info -- can be NULL if the
311238032Speter**			response is given before the connection is made.
311338032Speter**		ctladdr -- the controlling address for the recipient
311438032Speter**			address(es).
311538032Speter**		xstart -- the transaction start time, for computing
311638032Speter**			transaction delays.
311738032Speter**		e -- the current envelope.
311838032Speter**
311938032Speter**	Returns:
312038032Speter**		none.
312138032Speter**
312238032Speter**	Side Effects:
312338032Speter**		Errors may be incremented.
312438032Speter**		ExitStat may be set.
312538032Speter*/
312638032Speter
312738032Spetervoid
312864562Sgshapirogiveresponse(status, dsn, m, mci, ctladdr, xstart, e)
312964562Sgshapiro	int status;
313064562Sgshapiro	char *dsn;
313138032Speter	register MAILER *m;
313238032Speter	register MCI *mci;
313338032Speter	ADDRESS *ctladdr;
313438032Speter	time_t xstart;
313538032Speter	ENVELOPE *e;
313638032Speter{
313738032Speter	register const char *statmsg;
313838032Speter	extern char *SysExMsg[];
313938032Speter	register int i;
314064562Sgshapiro	int errnum = errno;
314164562Sgshapiro	int off = 4;
314238032Speter	extern int N_SysEx;
314364562Sgshapiro	char dsnbuf[ENHSCLEN];
314438032Speter	char buf[MAXLINE];
314538032Speter
314638032Speter	if (e == NULL)
314738032Speter		syserr("giveresponse: null envelope");
314838032Speter
314938032Speter	/*
315038032Speter	**  Compute status message from code.
315138032Speter	*/
315238032Speter
315364562Sgshapiro	i = status - EX__BASE;
315464562Sgshapiro	if (status == 0)
315538032Speter	{
315664562Sgshapiro		statmsg = "250 2.0.0 Sent";
315738032Speter		if (e->e_statmsg != NULL)
315838032Speter		{
315938032Speter			(void) snprintf(buf, sizeof buf, "%s (%s)",
316064562Sgshapiro					statmsg,
316164562Sgshapiro					shortenstring(e->e_statmsg, 403));
316238032Speter			statmsg = buf;
316338032Speter		}
316438032Speter	}
316538032Speter	else if (i < 0 || i >= N_SysEx)
316638032Speter	{
316764562Sgshapiro		(void) snprintf(buf, sizeof buf,
316864562Sgshapiro				"554 5.3.0 unknown mailer error %d",
316964562Sgshapiro				status);
317064562Sgshapiro		status = EX_UNAVAILABLE;
317138032Speter		statmsg = buf;
317238032Speter	}
317364562Sgshapiro	else if (status == EX_TEMPFAIL)
317438032Speter	{
317538032Speter		char *bp = buf;
317638032Speter
317738032Speter		snprintf(bp, SPACELEFT(buf, bp), "%s", SysExMsg[i] + 1);
317838032Speter		bp += strlen(bp);
317938032Speter#if NAMED_BIND
318038032Speter		if (h_errno == TRY_AGAIN)
318138032Speter			statmsg = errstring(h_errno+E_DNSBASE);
318238032Speter		else
318364562Sgshapiro#endif /* NAMED_BIND */
318438032Speter		{
318564562Sgshapiro			if (errnum != 0)
318664562Sgshapiro				statmsg = errstring(errnum);
318738032Speter			else
318838032Speter			{
318938032Speter#if SMTP
319038032Speter				statmsg = SmtpError;
319138032Speter#else /* SMTP */
319238032Speter				statmsg = NULL;
319338032Speter#endif /* SMTP */
319438032Speter			}
319538032Speter		}
319638032Speter		if (statmsg != NULL && statmsg[0] != '\0')
319764562Sgshapiro		{
319864562Sgshapiro			switch (errnum)
319964562Sgshapiro			{
320064562Sgshapiro#ifdef ENETDOWN
320164562Sgshapiro			  case ENETDOWN:	/* Network is down */
320264562Sgshapiro#endif /* ENETDOWN */
320364562Sgshapiro#ifdef ENETUNREACH
320464562Sgshapiro			  case ENETUNREACH:	/* Network is unreachable */
320564562Sgshapiro#endif /* ENETUNREACH */
320664562Sgshapiro#ifdef ENETRESET
320764562Sgshapiro			  case ENETRESET:	/* Network dropped connection on reset */
320864562Sgshapiro#endif /* ENETRESET */
320964562Sgshapiro#ifdef ECONNABORTED
321064562Sgshapiro			  case ECONNABORTED:	/* Software caused connection abort */
321164562Sgshapiro#endif /* ECONNABORTED */
321264562Sgshapiro#ifdef EHOSTDOWN
321364562Sgshapiro			  case EHOSTDOWN:	/* Host is down */
321464562Sgshapiro#endif /* EHOSTDOWN */
321564562Sgshapiro#ifdef EHOSTUNREACH
321664562Sgshapiro			  case EHOSTUNREACH:	/* No route to host */
321764562Sgshapiro#endif /* EHOSTUNREACH */
321864562Sgshapiro				if (mci->mci_host != NULL)
321964562Sgshapiro				{
322064562Sgshapiro					snprintf(bp, SPACELEFT(buf, bp),
322164562Sgshapiro						 ": %s", mci->mci_host);
322264562Sgshapiro					bp += strlen(bp);
322364562Sgshapiro				}
322464562Sgshapiro				break;
322564562Sgshapiro			}
322638032Speter			snprintf(bp, SPACELEFT(buf, bp), ": %s", statmsg);
322764562Sgshapiro		}
322838032Speter		statmsg = buf;
322938032Speter	}
323038032Speter#if NAMED_BIND
323164562Sgshapiro	else if (status == EX_NOHOST && h_errno != 0)
323238032Speter	{
323338032Speter		statmsg = errstring(h_errno + E_DNSBASE);
323438032Speter		(void) snprintf(buf, sizeof buf, "%s (%s)",
323538032Speter			SysExMsg[i] + 1, statmsg);
323638032Speter		statmsg = buf;
323738032Speter	}
323864562Sgshapiro#endif /* NAMED_BIND */
323938032Speter	else
324038032Speter	{
324138032Speter		statmsg = SysExMsg[i];
324264562Sgshapiro		if (*statmsg++ == ':' && errnum != 0)
324338032Speter		{
324438032Speter			(void) snprintf(buf, sizeof buf, "%s: %s",
324564562Sgshapiro				statmsg, errstring(errnum));
324638032Speter			statmsg = buf;
324738032Speter		}
324838032Speter	}
324938032Speter
325038032Speter	/*
325138032Speter	**  Print the message as appropriate
325238032Speter	*/
325338032Speter
325464562Sgshapiro	if (status == EX_OK || status == EX_TEMPFAIL)
325538032Speter	{
325638032Speter		extern char MsgBuf[];
325738032Speter
325864562Sgshapiro		if ((off = isenhsc(statmsg + 4, ' ')) > 0)
325964562Sgshapiro		{
326064562Sgshapiro			if (dsn == NULL)
326164562Sgshapiro			{
326264562Sgshapiro				snprintf(dsnbuf, sizeof dsnbuf,
326364562Sgshapiro					 "%.*s", off, statmsg + 4);
326464562Sgshapiro				dsn = dsnbuf;
326564562Sgshapiro			}
326664562Sgshapiro			off += 5;
326764562Sgshapiro		}
326864562Sgshapiro		else
326964562Sgshapiro		{
327064562Sgshapiro			off = 4;
327164562Sgshapiro		}
327264562Sgshapiro		message("%s", statmsg + off);
327364562Sgshapiro		if (status == EX_TEMPFAIL && e->e_xfp != NULL)
327438032Speter			fprintf(e->e_xfp, "%s\n", &MsgBuf[4]);
327538032Speter	}
327638032Speter	else
327738032Speter	{
327864562Sgshapiro		char mbuf[ENHSCLEN + 4];
327938032Speter
328038032Speter		Errors++;
328164562Sgshapiro		if ((off = isenhsc(statmsg + 4, ' ')) > 0 &&
328264562Sgshapiro		    off < sizeof mbuf - 4)
328364562Sgshapiro		{
328464562Sgshapiro			if (dsn == NULL)
328564562Sgshapiro			{
328664562Sgshapiro				snprintf(dsnbuf, sizeof dsnbuf,
328764562Sgshapiro					 "%.*s", off, statmsg + 4);
328864562Sgshapiro				dsn = dsnbuf;
328964562Sgshapiro			}
329064562Sgshapiro			off += 5;
329164562Sgshapiro			(void) strlcpy(mbuf, statmsg, off);
329264562Sgshapiro			(void) strlcat(mbuf, " %s", sizeof mbuf);
329364562Sgshapiro		}
329464562Sgshapiro		else
329564562Sgshapiro		{
329664562Sgshapiro			dsnbuf[0] = '\0';
329764562Sgshapiro			(void) snprintf(mbuf, sizeof mbuf, "%.3s %%s", statmsg);
329864562Sgshapiro			off = 4;
329964562Sgshapiro		}
330064562Sgshapiro		usrerr(mbuf, &statmsg[off]);
330138032Speter	}
330238032Speter
330338032Speter	/*
330438032Speter	**  Final cleanup.
330538032Speter	**	Log a record of the transaction.  Compute the new
330638032Speter	**	ExitStat -- if we already had an error, stick with
330738032Speter	**	that.
330838032Speter	*/
330938032Speter
331038032Speter	if (OpMode != MD_VERIFY && !bitset(EF_VRFYONLY, e->e_flags) &&
331164562Sgshapiro	    LogLevel > ((status == EX_TEMPFAIL) ? 8 : (status == EX_OK) ? 7 : 6))
331264562Sgshapiro		logdelivery(m, mci, dsn, statmsg + off, ctladdr, xstart, e);
331338032Speter
331438032Speter	if (tTd(11, 2))
331564562Sgshapiro		dprintf("giveresponse: status=%d, dsn=%s, e->e_message=%s\n",
331664562Sgshapiro			status,
331764562Sgshapiro			dsn == NULL ? "<NULL>" : dsn,
331864562Sgshapiro			e->e_message == NULL ? "<NULL>" : e->e_message);
331938032Speter
332064562Sgshapiro	if (status != EX_TEMPFAIL)
332164562Sgshapiro		setstat(status);
332264562Sgshapiro	if (status != EX_OK && (status != EX_TEMPFAIL || e->e_message == NULL))
332338032Speter	{
332438032Speter		if (e->e_message != NULL)
332538032Speter			free(e->e_message);
332664562Sgshapiro		e->e_message = newstr(statmsg + off);
332738032Speter	}
332838032Speter	errno = 0;
332938032Speter#if NAMED_BIND
333038032Speter	h_errno = 0;
333164562Sgshapiro#endif /* NAMED_BIND */
333238032Speter}
333338032Speter/*
333438032Speter**  LOGDELIVERY -- log the delivery in the system log
333538032Speter**
333638032Speter**	Care is taken to avoid logging lines that are too long, because
333738032Speter**	some versions of syslog have an unfortunate proclivity for core
333838032Speter**	dumping.  This is a hack, to be sure, that is at best empirical.
333938032Speter**
334038032Speter**	Parameters:
334138032Speter**		m -- the mailer info.  Can be NULL for initial queue.
334238032Speter**		mci -- the mailer connection info -- can be NULL if the
334364562Sgshapiro**			log is occurring when no connection is active.
334464562Sgshapiro**		dsn -- the DSN attached to the status.
334564562Sgshapiro**		status -- the message to print for the status.
334638032Speter**		ctladdr -- the controlling address for the to list.
334738032Speter**		xstart -- the transaction start time, used for
334838032Speter**			computing transaction delay.
334938032Speter**		e -- the current envelope.
335038032Speter**
335138032Speter**	Returns:
335238032Speter**		none
335338032Speter**
335438032Speter**	Side Effects:
335538032Speter**		none
335638032Speter*/
335738032Speter
335838032Spetervoid
335964562Sgshapirologdelivery(m, mci, dsn, status, ctladdr, xstart, e)
336038032Speter	MAILER *m;
336138032Speter	register MCI *mci;
336264562Sgshapiro	char *dsn;
336364562Sgshapiro	const char *status;
336438032Speter	ADDRESS *ctladdr;
336538032Speter	time_t xstart;
336638032Speter	register ENVELOPE *e;
336738032Speter{
336838032Speter	register char *bp;
336938032Speter	register char *p;
337038032Speter	int l;
337138032Speter	char buf[1024];
337238032Speter
337364562Sgshapiro#if (SYSLOG_BUFSIZE) >= 256
337438032Speter	/* ctladdr: max 106 bytes */
337538032Speter	bp = buf;
337638032Speter	if (ctladdr != NULL)
337738032Speter	{
337838032Speter		snprintf(bp, SPACELEFT(buf, bp), ", ctladdr=%s",
337964562Sgshapiro			 shortenstring(ctladdr->q_paddr, 83));
338038032Speter		bp += strlen(bp);
338138032Speter		if (bitset(QGOODUID, ctladdr->q_flags))
338238032Speter		{
338338032Speter			(void) snprintf(bp, SPACELEFT(buf, bp), " (%d/%d)",
338464562Sgshapiro					(int) ctladdr->q_uid,
338564562Sgshapiro					(int) ctladdr->q_gid);
338638032Speter			bp += strlen(bp);
338738032Speter		}
338838032Speter	}
338938032Speter
339038032Speter	/* delay & xdelay: max 41 bytes */
339138032Speter	snprintf(bp, SPACELEFT(buf, bp), ", delay=%s",
339264562Sgshapiro		 pintvl(curtime() - e->e_ctime, TRUE));
339338032Speter	bp += strlen(bp);
339438032Speter
339538032Speter	if (xstart != (time_t) 0)
339638032Speter	{
339738032Speter		snprintf(bp, SPACELEFT(buf, bp), ", xdelay=%s",
339864562Sgshapiro			 pintvl(curtime() - xstart, TRUE));
339938032Speter		bp += strlen(bp);
340038032Speter	}
340138032Speter
340238032Speter	/* mailer: assume about 19 bytes (max 10 byte mailer name) */
340338032Speter	if (m != NULL)
340438032Speter	{
340538032Speter		snprintf(bp, SPACELEFT(buf, bp), ", mailer=%s", m->m_name);
340638032Speter		bp += strlen(bp);
340738032Speter	}
340838032Speter
340964562Sgshapiro	/* pri: changes with each delivery attempt */
341064562Sgshapiro	snprintf(bp, SPACELEFT(buf, bp), ", pri=%ld", e->e_msgpriority);
341164562Sgshapiro	bp += strlen(bp);
341264562Sgshapiro
341338032Speter	/* relay: max 66 bytes for IPv4 addresses */
341438032Speter	if (mci != NULL && mci->mci_host != NULL)
341538032Speter	{
341638032Speter# if DAEMON
341738032Speter		extern SOCKADDR CurHostAddr;
341864562Sgshapiro# endif /* DAEMON */
341938032Speter
342038032Speter		snprintf(bp, SPACELEFT(buf, bp), ", relay=%s",
342164562Sgshapiro			 shortenstring(mci->mci_host, 40));
342238032Speter		bp += strlen(bp);
342338032Speter
342438032Speter# if DAEMON
342538032Speter		if (CurHostAddr.sa.sa_family != 0)
342638032Speter		{
342738032Speter			snprintf(bp, SPACELEFT(buf, bp), " [%s]",
342864562Sgshapiro				 anynet_ntoa(&CurHostAddr));
342938032Speter		}
343064562Sgshapiro# endif /* DAEMON */
343138032Speter	}
343264562Sgshapiro	else if (strcmp(status, "queued") != 0)
343338032Speter	{
343438032Speter		p = macvalue('h', e);
343538032Speter		if (p != NULL && p[0] != '\0')
343638032Speter		{
343738032Speter			snprintf(bp, SPACELEFT(buf, bp), ", relay=%s",
343864562Sgshapiro				 shortenstring(p, 40));
343938032Speter		}
344038032Speter	}
344138032Speter	bp += strlen(bp);
344238032Speter
344364562Sgshapiro	/* dsn */
344464562Sgshapiro	if (dsn != NULL && *dsn != '\0')
344564562Sgshapiro	{
344664562Sgshapiro		snprintf(bp, SPACELEFT(buf, bp), ", dsn=%s",
344764562Sgshapiro			 shortenstring(dsn, ENHSCLEN));
344864562Sgshapiro		bp += strlen(bp);
344964562Sgshapiro	}
345038032Speter
345164562Sgshapiro# define STATLEN		(((SYSLOG_BUFSIZE) - 100) / 4)
345264562Sgshapiro# if (STATLEN) < 63
345364562Sgshapiro#  undef STATLEN
345464562Sgshapiro#  define STATLEN	63
345564562Sgshapiro# endif /* (STATLEN) < 63 */
345664562Sgshapiro# if (STATLEN) > 203
345764562Sgshapiro#  undef STATLEN
345864562Sgshapiro#  define STATLEN	203
345964562Sgshapiro# endif /* (STATLEN) > 203 */
346064562Sgshapiro
346138032Speter	/* stat: max 210 bytes */
346238032Speter	if ((bp - buf) > (sizeof buf - ((STATLEN) + 20)))
346338032Speter	{
346438032Speter		/* desperation move -- truncate data */
346538032Speter		bp = buf + sizeof buf - ((STATLEN) + 17);
346664562Sgshapiro		(void) strlcpy(bp, "...", SPACELEFT(buf, bp));
346738032Speter		bp += 3;
346838032Speter	}
346938032Speter
347064562Sgshapiro	(void) strlcpy(bp, ", stat=", SPACELEFT(buf, bp));
347138032Speter	bp += strlen(bp);
347238032Speter
347364562Sgshapiro	(void) strlcpy(bp, shortenstring(status, STATLEN), SPACELEFT(buf, bp));
347438032Speter
347538032Speter	/* id, to: max 13 + TOBUFSIZE bytes */
347638032Speter	l = SYSLOG_BUFSIZE - 100 - strlen(buf);
347764562Sgshapiro	p = e->e_to == NULL ? "NO-TO-LIST" : e->e_to;
347838032Speter	while (strlen(p) >= (SIZE_T) l)
347938032Speter	{
348064562Sgshapiro		register char *q;
348138032Speter
348264562Sgshapiro#if _FFR_DYNAMIC_TOBUF
348364562Sgshapiro		for (q = p + l; q > p; q--)
348464562Sgshapiro		{
348564562Sgshapiro			if (*q == ',')
348664562Sgshapiro				break;
348764562Sgshapiro		}
348864562Sgshapiro		if (p == q)
348964562Sgshapiro			break;
349064562Sgshapiro#else /* _FFR_DYNAMIC_TOBUF */
349164562Sgshapiro		q = strchr(p + l, ',');
349238032Speter		if (q == NULL)
349338032Speter			break;
349464562Sgshapiro#endif /* _FFR_DYNAMIC_TOBUF */
349564562Sgshapiro
349638032Speter		sm_syslog(LOG_INFO, e->e_id,
349764562Sgshapiro			  "to=%.*s [more]%s",
349866494Sgshapiro			  (int) (++q - p), p, buf);
349938032Speter		p = q;
350038032Speter	}
350164562Sgshapiro#if _FFR_DYNAMIC_TOBUF
350264562Sgshapiro	sm_syslog(LOG_INFO, e->e_id, "to=%.*s%s", l, p, buf);
350364562Sgshapiro#else /* _FFR_DYNAMIC_TOBUF */
350438032Speter	sm_syslog(LOG_INFO, e->e_id, "to=%s%s", p, buf);
350564562Sgshapiro#endif /* _FFR_DYNAMIC_TOBUF */
350638032Speter
350764562Sgshapiro#else /* (SYSLOG_BUFSIZE) >= 256 */
350838032Speter
350938032Speter	l = SYSLOG_BUFSIZE - 85;
351064562Sgshapiro	p = e->e_to == NULL ? "NO-TO-LIST" : e->e_to;
351138032Speter	while (strlen(p) >= (SIZE_T) l)
351238032Speter	{
351364562Sgshapiro		register char *q;
351438032Speter
351564562Sgshapiro#if _FFR_DYNAMIC_TOBUF
351664562Sgshapiro		for (q = p + l; q > p; q--)
351764562Sgshapiro		{
351864562Sgshapiro			if (*q == ',')
351964562Sgshapiro				break;
352064562Sgshapiro		}
352164562Sgshapiro		if (p == q)
352264562Sgshapiro			break;
352364562Sgshapiro#else /* _FFR_DYNAMIC_TOBUF */
352464562Sgshapiro		q = strchr(p + l, ',');
352538032Speter		if (q == NULL)
352638032Speter			break;
352764562Sgshapiro#endif /* _FFR_DYNAMIC_TOBUF */
352864562Sgshapiro
352938032Speter		sm_syslog(LOG_INFO, e->e_id,
353064562Sgshapiro			  "to=%.*s [more]",
353166494Sgshapiro			  (int) (++q - p), p);
353238032Speter		p = q;
353338032Speter	}
353464562Sgshapiro#if _FFR_DYNAMIC_TOBUF
353564562Sgshapiro	sm_syslog(LOG_INFO, e->e_id, "to=%.*s", l, p);
353664562Sgshapiro#else /* _FFR_DYNAMIC_TOBUF */
353738032Speter	sm_syslog(LOG_INFO, e->e_id, "to=%s", p);
353864562Sgshapiro#endif /* _FFR_DYNAMIC_TOBUF */
353938032Speter
354038032Speter	if (ctladdr != NULL)
354138032Speter	{
354238032Speter		bp = buf;
354338032Speter		snprintf(bp, SPACELEFT(buf, bp), "ctladdr=%s",
354464562Sgshapiro			 shortenstring(ctladdr->q_paddr, 83));
354538032Speter		bp += strlen(bp);
354638032Speter		if (bitset(QGOODUID, ctladdr->q_flags))
354738032Speter		{
354838032Speter			(void) snprintf(bp, SPACELEFT(buf, bp), " (%d/%d)",
354938032Speter					ctladdr->q_uid, ctladdr->q_gid);
355038032Speter			bp += strlen(bp);
355138032Speter		}
355238032Speter		sm_syslog(LOG_INFO, e->e_id, "%s", buf);
355338032Speter	}
355438032Speter	bp = buf;
355538032Speter	snprintf(bp, SPACELEFT(buf, bp), "delay=%s",
355664562Sgshapiro		 pintvl(curtime() - e->e_ctime, TRUE));
355738032Speter	bp += strlen(bp);
355838032Speter	if (xstart != (time_t) 0)
355938032Speter	{
356038032Speter		snprintf(bp, SPACELEFT(buf, bp), ", xdelay=%s",
356164562Sgshapiro			 pintvl(curtime() - xstart, TRUE));
356238032Speter		bp += strlen(bp);
356338032Speter	}
356438032Speter
356538032Speter	if (m != NULL)
356638032Speter	{
356738032Speter		snprintf(bp, SPACELEFT(buf, bp), ", mailer=%s", m->m_name);
356838032Speter		bp += strlen(bp);
356938032Speter	}
357038032Speter	sm_syslog(LOG_INFO, e->e_id, "%.1000s", buf);
357138032Speter
357238032Speter	buf[0] = '\0';
357338032Speter	bp = buf;
357438032Speter	if (mci != NULL && mci->mci_host != NULL)
357538032Speter	{
357638032Speter# if DAEMON
357738032Speter		extern SOCKADDR CurHostAddr;
357864562Sgshapiro# endif /* DAEMON */
357938032Speter
358038032Speter		snprintf(bp, SPACELEFT(buf, bp), "relay=%.100s", mci->mci_host);
358138032Speter		bp += strlen(bp);
358238032Speter
358338032Speter# if DAEMON
358438032Speter		if (CurHostAddr.sa.sa_family != 0)
358538032Speter			snprintf(bp, SPACELEFT(buf, bp), " [%.100s]",
358638032Speter				anynet_ntoa(&CurHostAddr));
358764562Sgshapiro# endif /* DAEMON */
358838032Speter	}
358964562Sgshapiro	else if (strcmp(status, "queued") != 0)
359038032Speter	{
359138032Speter		p = macvalue('h', e);
359238032Speter		if (p != NULL && p[0] != '\0')
359338032Speter			snprintf(buf, sizeof buf, "relay=%.100s", p);
359438032Speter	}
359538032Speter	if (buf[0] != '\0')
359638032Speter		sm_syslog(LOG_INFO, e->e_id, "%.1000s", buf);
359738032Speter
359864562Sgshapiro	sm_syslog(LOG_INFO, e->e_id, "stat=%s", shortenstring(status, 63));
359964562Sgshapiro#endif /* (SYSLOG_BUFSIZE) >= 256 */
360038032Speter}
360138032Speter/*
360238032Speter**  PUTFROMLINE -- output a UNIX-style from line (or whatever)
360338032Speter**
360438032Speter**	This can be made an arbitrary message separator by changing $l
360538032Speter**
360638032Speter**	One of the ugliest hacks seen by human eyes is contained herein:
360738032Speter**	UUCP wants those stupid "remote from <host>" lines.  Why oh why
360838032Speter**	does a well-meaning programmer such as myself have to deal with
360938032Speter**	this kind of antique garbage????
361038032Speter**
361138032Speter**	Parameters:
361238032Speter**		mci -- the connection information.
361338032Speter**		e -- the envelope.
361438032Speter**
361538032Speter**	Returns:
361638032Speter**		none
361738032Speter**
361838032Speter**	Side Effects:
361938032Speter**		outputs some text to fp.
362038032Speter*/
362138032Speter
362238032Spetervoid
362338032Speterputfromline(mci, e)
362438032Speter	register MCI *mci;
362538032Speter	ENVELOPE *e;
362638032Speter{
362738032Speter	char *template = UnixFromLine;
362838032Speter	char buf[MAXLINE];
362938032Speter	char xbuf[MAXLINE];
363038032Speter
363138032Speter	if (bitnset(M_NHDR, mci->mci_mailer->m_flags))
363238032Speter		return;
363338032Speter
363438032Speter	mci->mci_flags |= MCIF_INHEADER;
363538032Speter
363638032Speter	if (bitnset(M_UGLYUUCP, mci->mci_mailer->m_flags))
363738032Speter	{
363838032Speter		char *bang;
363938032Speter
364038032Speter		expand("\201g", buf, sizeof buf, e);
364138032Speter		bang = strchr(buf, '!');
364238032Speter		if (bang == NULL)
364338032Speter		{
364438032Speter			char *at;
364538032Speter			char hname[MAXNAME];
364638032Speter
364764562Sgshapiro			/*
364842575Speter			**  If we can construct a UUCP path, do so
364938032Speter			*/
365038032Speter
365138032Speter			at = strrchr(buf, '@');
365238032Speter			if (at == NULL)
365338032Speter			{
365464562Sgshapiro				expand("\201k", hname, sizeof hname, e);
365538032Speter				at = hname;
365638032Speter			}
365738032Speter			else
365838032Speter				*at++ = '\0';
365938032Speter			(void) snprintf(xbuf, sizeof xbuf,
366038032Speter				"From %.800s  \201d remote from %.100s\n",
366138032Speter				buf, at);
366238032Speter		}
366338032Speter		else
366438032Speter		{
366538032Speter			*bang++ = '\0';
366638032Speter			(void) snprintf(xbuf, sizeof xbuf,
366738032Speter				"From %.800s  \201d remote from %.100s\n",
366838032Speter				bang, buf);
366938032Speter			template = xbuf;
367038032Speter		}
367138032Speter	}
367238032Speter	expand(template, buf, sizeof buf, e);
367338032Speter	putxline(buf, strlen(buf), mci, PXLF_HEADER);
367438032Speter}
367538032Speter/*
367638032Speter**  PUTBODY -- put the body of a message.
367738032Speter**
367838032Speter**	Parameters:
367938032Speter**		mci -- the connection information.
368038032Speter**		e -- the envelope to put out.
368138032Speter**		separator -- if non-NULL, a message separator that must
368238032Speter**			not be permitted in the resulting message.
368338032Speter**
368438032Speter**	Returns:
368538032Speter**		none.
368638032Speter**
368738032Speter**	Side Effects:
368838032Speter**		The message is written onto fp.
368938032Speter*/
369038032Speter
369138032Speter/* values for output state variable */
369238032Speter#define OS_HEAD		0	/* at beginning of line */
369338032Speter#define OS_CR		1	/* read a carriage return */
369438032Speter#define OS_INLINE	2	/* putting rest of line */
369538032Speter
369638032Spetervoid
369738032Speterputbody(mci, e, separator)
369838032Speter	register MCI *mci;
369938032Speter	register ENVELOPE *e;
370038032Speter	char *separator;
370138032Speter{
370264562Sgshapiro	bool dead = FALSE;
370338032Speter	char buf[MAXLINE];
370442575Speter	char *boundaries[MAXMIMENESTING + 1];
370538032Speter
370638032Speter	/*
370738032Speter	**  Output the body of the message
370838032Speter	*/
370938032Speter
371038032Speter	if (e->e_dfp == NULL && bitset(EF_HAS_DF, e->e_flags))
371138032Speter	{
371238032Speter		char *df = queuename(e, 'd');
371338032Speter
371438032Speter		e->e_dfp = fopen(df, "r");
371538032Speter		if (e->e_dfp == NULL)
371664562Sgshapiro		{
371764562Sgshapiro			char *msg = "!putbody: Cannot open %s for %s from %s";
371864562Sgshapiro
371964562Sgshapiro			if (errno == ENOENT)
372064562Sgshapiro				msg++;
372164562Sgshapiro			syserr(msg, df, e->e_to, e->e_from.q_paddr);
372264562Sgshapiro		}
372338032Speter	}
372438032Speter	if (e->e_dfp == NULL)
372538032Speter	{
372638032Speter		if (bitset(MCIF_INHEADER, mci->mci_flags))
372738032Speter		{
372838032Speter			putline("", mci);
372938032Speter			mci->mci_flags &= ~MCIF_INHEADER;
373038032Speter		}
373138032Speter		putline("<<< No Message Collected >>>", mci);
373238032Speter		goto endofmessage;
373338032Speter	}
373464562Sgshapiro
373538032Speter	if (e->e_dfino == (ino_t) 0)
373638032Speter	{
373738032Speter		struct stat stbuf;
373838032Speter
373938032Speter		if (fstat(fileno(e->e_dfp), &stbuf) < 0)
374038032Speter			e->e_dfino = -1;
374138032Speter		else
374238032Speter		{
374338032Speter			e->e_dfdev = stbuf.st_dev;
374438032Speter			e->e_dfino = stbuf.st_ino;
374538032Speter		}
374638032Speter	}
374738032Speter
374864562Sgshapiro	/* paranoia: the df file should always be in a rewound state */
374964562Sgshapiro	(void) bfrewind(e->e_dfp);
375064562Sgshapiro
375138032Speter#if MIME8TO7
375238032Speter	if (bitset(MCIF_CVT8TO7, mci->mci_flags))
375338032Speter	{
375438032Speter		/*
375538032Speter		**  Do 8 to 7 bit MIME conversion.
375638032Speter		*/
375738032Speter
375838032Speter		/* make sure it looks like a MIME message */
375938032Speter		if (hvalue("MIME-Version", e->e_header) == NULL)
376038032Speter			putline("MIME-Version: 1.0", mci);
376138032Speter
376238032Speter		if (hvalue("Content-Type", e->e_header) == NULL)
376338032Speter		{
376438032Speter			snprintf(buf, sizeof buf,
376538032Speter				"Content-Type: text/plain; charset=%s",
376638032Speter				defcharset(e));
376738032Speter			putline(buf, mci);
376838032Speter		}
376938032Speter
377038032Speter		/* now do the hard work */
377138032Speter		boundaries[0] = NULL;
377238032Speter		mci->mci_flags |= MCIF_INHEADER;
377364562Sgshapiro		(void) mime8to7(mci, e->e_header, e, boundaries, M87F_OUTER);
377438032Speter	}
377538032Speter# if MIME7TO8
377638032Speter	else if (bitset(MCIF_CVT7TO8, mci->mci_flags))
377738032Speter	{
377864562Sgshapiro		(void) mime7to8(mci, e->e_header, e);
377938032Speter	}
378064562Sgshapiro# endif /* MIME7TO8 */
378142575Speter	else if (MaxMimeHeaderLength > 0 || MaxMimeFieldLength > 0)
378242575Speter	{
378364562Sgshapiro		bool oldsuprerrs = SuprErrs;
378464562Sgshapiro
378542575Speter		/* Use mime8to7 to check multipart for MIME header overflows */
378642575Speter		boundaries[0] = NULL;
378742575Speter		mci->mci_flags |= MCIF_INHEADER;
378864562Sgshapiro
378964562Sgshapiro		/*
379064562Sgshapiro		**  If EF_DONT_MIME is set, we have a broken MIME message
379164562Sgshapiro		**  and don't want to generate a new bounce message whose
379264562Sgshapiro		**  body propagates the broken MIME.  We can't just not call
379364562Sgshapiro		**  mime8to7() as is done above since we need the security
379464562Sgshapiro		**  checks.  The best we can do is suppress the errors.
379564562Sgshapiro		*/
379664562Sgshapiro
379764562Sgshapiro		if (bitset(EF_DONT_MIME, e->e_flags))
379864562Sgshapiro			SuprErrs = TRUE;
379964562Sgshapiro
380064562Sgshapiro		(void) mime8to7(mci, e->e_header, e, boundaries,
380164562Sgshapiro				M87F_OUTER|M87F_NO8TO7);
380264562Sgshapiro
380364562Sgshapiro		/* restore SuprErrs */
380464562Sgshapiro		SuprErrs = oldsuprerrs;
380542575Speter	}
380638032Speter	else
380764562Sgshapiro#endif /* MIME8TO7 */
380838032Speter	{
380938032Speter		int ostate;
381038032Speter		register char *bp;
381138032Speter		register char *pbp;
381238032Speter		register int c;
381338032Speter		register char *xp;
381438032Speter		int padc;
381538032Speter		char *buflim;
381638032Speter		int pos = 0;
381764562Sgshapiro		char peekbuf[12];
381838032Speter
381938032Speter		if (bitset(MCIF_INHEADER, mci->mci_flags))
382038032Speter		{
382138032Speter			putline("", mci);
382238032Speter			mci->mci_flags &= ~MCIF_INHEADER;
382338032Speter		}
382438032Speter
382538032Speter		/* determine end of buffer; allow for short mailer lines */
382638032Speter		buflim = &buf[sizeof buf - 1];
382738032Speter		if (mci->mci_mailer->m_linelimit > 0 &&
382838032Speter		    mci->mci_mailer->m_linelimit < sizeof buf - 1)
382938032Speter			buflim = &buf[mci->mci_mailer->m_linelimit - 1];
383038032Speter
383138032Speter		/* copy temp file to output with mapping */
383238032Speter		ostate = OS_HEAD;
383338032Speter		bp = buf;
383438032Speter		pbp = peekbuf;
383564562Sgshapiro		while (!ferror(mci->mci_out) && !dead)
383638032Speter		{
383738032Speter			if (pbp > peekbuf)
383838032Speter				c = *--pbp;
383938032Speter			else if ((c = getc(e->e_dfp)) == EOF)
384038032Speter				break;
384138032Speter			if (bitset(MCIF_7BIT, mci->mci_flags))
384238032Speter				c &= 0x7f;
384338032Speter			switch (ostate)
384438032Speter			{
384538032Speter			  case OS_HEAD:
384638032Speter#if _FFR_NONULLS
384738032Speter				if (c == '\0' &&
384838032Speter				    bitnset(M_NONULLS, mci->mci_mailer->m_flags))
384938032Speter					break;
385064562Sgshapiro#endif /* _FFR_NONULLS */
385138032Speter				if (c != '\r' && c != '\n' && bp < buflim)
385238032Speter				{
385338032Speter					*bp++ = c;
385438032Speter					break;
385538032Speter				}
385638032Speter
385738032Speter				/* check beginning of line for special cases */
385838032Speter				*bp = '\0';
385938032Speter				pos = 0;
386038032Speter				padc = EOF;
386138032Speter				if (buf[0] == 'F' &&
386238032Speter				    bitnset(M_ESCFROM, mci->mci_mailer->m_flags) &&
386338032Speter				    strncmp(buf, "From ", 5) == 0)
386438032Speter				{
386538032Speter					padc = '>';
386638032Speter				}
386738032Speter				if (buf[0] == '-' && buf[1] == '-' &&
386838032Speter				    separator != NULL)
386938032Speter				{
387038032Speter					/* possible separator */
387138032Speter					int sl = strlen(separator);
387238032Speter
387338032Speter					if (strncmp(&buf[2], separator, sl) == 0)
387438032Speter						padc = ' ';
387538032Speter				}
387638032Speter				if (buf[0] == '.' &&
387738032Speter				    bitnset(M_XDOT, mci->mci_mailer->m_flags))
387838032Speter				{
387938032Speter					padc = '.';
388038032Speter				}
388138032Speter
388238032Speter				/* now copy out saved line */
388338032Speter				if (TrafficLogFile != NULL)
388438032Speter				{
388538032Speter					fprintf(TrafficLogFile, "%05d >>> ",
388638032Speter						(int) getpid());
388738032Speter					if (padc != EOF)
388864562Sgshapiro						(void) putc(padc,
388964562Sgshapiro							    TrafficLogFile);
389038032Speter					for (xp = buf; xp < bp; xp++)
389164562Sgshapiro						(void) putc((unsigned char) *xp,
389264562Sgshapiro							    TrafficLogFile);
389338032Speter					if (c == '\n')
389464562Sgshapiro						(void) fputs(mci->mci_mailer->m_eol,
389538032Speter						      TrafficLogFile);
389638032Speter				}
389738032Speter				if (padc != EOF)
389838032Speter				{
389964562Sgshapiro					if (putc(padc, mci->mci_out) == EOF)
390064562Sgshapiro					{
390164562Sgshapiro						dead = TRUE;
390264562Sgshapiro						continue;
390364562Sgshapiro					}
390438032Speter					pos++;
390538032Speter				}
390638032Speter				for (xp = buf; xp < bp; xp++)
390738032Speter				{
390864562Sgshapiro					if (putc((unsigned char) *xp,
390964562Sgshapiro						 mci->mci_out) == EOF)
391064562Sgshapiro					{
391164562Sgshapiro						dead = TRUE;
391264562Sgshapiro						break;
391364562Sgshapiro					}
391464562Sgshapiro
391564562Sgshapiro					/* record progress for DATA timeout */
391664562Sgshapiro					DataProgress = TRUE;
391738032Speter				}
391864562Sgshapiro				if (dead)
391964562Sgshapiro					continue;
392038032Speter				if (c == '\n')
392138032Speter				{
392264562Sgshapiro					if (fputs(mci->mci_mailer->m_eol,
392364562Sgshapiro						  mci->mci_out) == EOF)
392464562Sgshapiro						break;
392538032Speter					pos = 0;
392638032Speter				}
392738032Speter				else
392838032Speter				{
392938032Speter					pos += bp - buf;
393038032Speter					if (c != '\r')
393138032Speter						*pbp++ = c;
393238032Speter				}
393364562Sgshapiro
393464562Sgshapiro				/* record progress for DATA timeout */
393564562Sgshapiro				DataProgress = TRUE;
393638032Speter				bp = buf;
393738032Speter
393838032Speter				/* determine next state */
393938032Speter				if (c == '\n')
394038032Speter					ostate = OS_HEAD;
394138032Speter				else if (c == '\r')
394238032Speter					ostate = OS_CR;
394338032Speter				else
394438032Speter					ostate = OS_INLINE;
394538032Speter				continue;
394638032Speter
394738032Speter			  case OS_CR:
394838032Speter				if (c == '\n')
394938032Speter				{
395038032Speter					/* got CRLF */
395164562Sgshapiro					if (fputs(mci->mci_mailer->m_eol,
395264562Sgshapiro						  mci->mci_out) == EOF)
395364562Sgshapiro						continue;
395464562Sgshapiro
395564562Sgshapiro					/* record progress for DATA timeout */
395664562Sgshapiro					DataProgress = TRUE;
395764562Sgshapiro
395838032Speter					if (TrafficLogFile != NULL)
395938032Speter					{
396064562Sgshapiro						(void) fputs(mci->mci_mailer->m_eol,
396164562Sgshapiro							     TrafficLogFile);
396238032Speter					}
396338032Speter					ostate = OS_HEAD;
396438032Speter					continue;
396538032Speter				}
396638032Speter
396738032Speter				/* had a naked carriage return */
396838032Speter				*pbp++ = c;
396938032Speter				c = '\r';
397038032Speter				ostate = OS_INLINE;
397138032Speter				goto putch;
397238032Speter
397338032Speter			  case OS_INLINE:
397438032Speter				if (c == '\r')
397538032Speter				{
397638032Speter					ostate = OS_CR;
397738032Speter					continue;
397838032Speter				}
397938032Speter#if _FFR_NONULLS
398038032Speter				if (c == '\0' &&
398138032Speter				    bitnset(M_NONULLS, mci->mci_mailer->m_flags))
398238032Speter					break;
398364562Sgshapiro#endif /* _FFR_NONULLS */
398438032Speterputch:
398538032Speter				if (mci->mci_mailer->m_linelimit > 0 &&
398664562Sgshapiro				    pos >= mci->mci_mailer->m_linelimit - 1 &&
398738032Speter				    c != '\n')
398838032Speter				{
398964562Sgshapiro					int d;
399064562Sgshapiro
399164562Sgshapiro					/* check next character for EOL */
399264562Sgshapiro					if (pbp > peekbuf)
399364562Sgshapiro						d = *(pbp - 1);
399464562Sgshapiro					else if ((d = getc(e->e_dfp)) != EOF)
399564562Sgshapiro						*pbp++ = d;
399664562Sgshapiro
399764562Sgshapiro					if (d == '\n' || d == EOF)
399864562Sgshapiro					{
399964562Sgshapiro						if (TrafficLogFile != NULL)
400064562Sgshapiro							(void) putc((unsigned char) c,
400164562Sgshapiro							    TrafficLogFile);
400264562Sgshapiro						if (putc((unsigned char) c,
400364562Sgshapiro							 mci->mci_out) == EOF)
400464562Sgshapiro						{
400564562Sgshapiro							dead = TRUE;
400664562Sgshapiro							continue;
400764562Sgshapiro						}
400864562Sgshapiro						pos++;
400964562Sgshapiro						continue;
401064562Sgshapiro					}
401164562Sgshapiro
401264562Sgshapiro					if (putc('!', mci->mci_out) == EOF ||
401364562Sgshapiro					    fputs(mci->mci_mailer->m_eol,
401464562Sgshapiro						  mci->mci_out) == EOF)
401564562Sgshapiro					{
401664562Sgshapiro						dead = TRUE;
401764562Sgshapiro						continue;
401864562Sgshapiro					}
401964562Sgshapiro
402064562Sgshapiro					/* record progress for DATA timeout */
402164562Sgshapiro					DataProgress = TRUE;
402264562Sgshapiro
402338032Speter					if (TrafficLogFile != NULL)
402438032Speter					{
402538032Speter						fprintf(TrafficLogFile, "!%s",
402638032Speter							mci->mci_mailer->m_eol);
402738032Speter					}
402838032Speter					ostate = OS_HEAD;
402938032Speter					*pbp++ = c;
403038032Speter					continue;
403138032Speter				}
403238032Speter				if (c == '\n')
403338032Speter				{
403438032Speter					if (TrafficLogFile != NULL)
403564562Sgshapiro						(void) fputs(mci->mci_mailer->m_eol,
403638032Speter						      TrafficLogFile);
403764562Sgshapiro					if (fputs(mci->mci_mailer->m_eol,
403864562Sgshapiro						  mci->mci_out) == EOF)
403964562Sgshapiro						continue;
404038032Speter					pos = 0;
404138032Speter					ostate = OS_HEAD;
404238032Speter				}
404338032Speter				else
404438032Speter				{
404538032Speter					if (TrafficLogFile != NULL)
404664562Sgshapiro						(void) putc((unsigned char) c,
404764562Sgshapiro							    TrafficLogFile);
404864562Sgshapiro					if (putc((unsigned char) c,
404964562Sgshapiro						 mci->mci_out) == EOF)
405064562Sgshapiro					{
405164562Sgshapiro						dead = TRUE;
405264562Sgshapiro						continue;
405364562Sgshapiro					}
405438032Speter					pos++;
405538032Speter					ostate = OS_INLINE;
405638032Speter				}
405764562Sgshapiro
405864562Sgshapiro				/* record progress for DATA timeout */
405964562Sgshapiro				DataProgress = TRUE;
406038032Speter				break;
406138032Speter			}
406238032Speter		}
406338032Speter
406438032Speter		/* make sure we are at the beginning of a line */
406538032Speter		if (bp > buf)
406638032Speter		{
406738032Speter			if (TrafficLogFile != NULL)
406838032Speter			{
406938032Speter				for (xp = buf; xp < bp; xp++)
407064562Sgshapiro					(void) putc((unsigned char) *xp,
407164562Sgshapiro						    TrafficLogFile);
407238032Speter			}
407338032Speter			for (xp = buf; xp < bp; xp++)
407438032Speter			{
407564562Sgshapiro				if (putc((unsigned char) *xp, mci->mci_out) ==
407664562Sgshapiro				    EOF)
407764562Sgshapiro				{
407864562Sgshapiro					dead = TRUE;
407964562Sgshapiro					break;
408064562Sgshapiro				}
408164562Sgshapiro
408264562Sgshapiro				/* record progress for DATA timeout */
408364562Sgshapiro				DataProgress = TRUE;
408438032Speter			}
408538032Speter			pos += bp - buf;
408638032Speter		}
408764562Sgshapiro		if (!dead && pos > 0)
408838032Speter		{
408938032Speter			if (TrafficLogFile != NULL)
409064562Sgshapiro				(void) fputs(mci->mci_mailer->m_eol,
409164562Sgshapiro					     TrafficLogFile);
409264562Sgshapiro			(void) fputs(mci->mci_mailer->m_eol, mci->mci_out);
409364562Sgshapiro
409464562Sgshapiro			/* record progress for DATA timeout */
409564562Sgshapiro			DataProgress = TRUE;
409638032Speter		}
409738032Speter	}
409838032Speter
409938032Speter	if (ferror(e->e_dfp))
410038032Speter	{
410164562Sgshapiro		syserr("putbody: %s/df%s: read error",
410264562Sgshapiro		       qid_printqueue(e->e_queuedir), e->e_id);
410338032Speter		ExitStat = EX_IOERR;
410438032Speter	}
410538032Speter
410638032Speterendofmessage:
410764562Sgshapiro	/*
410864562Sgshapiro	**  Since mailfile() uses e_dfp in a child process,
410964562Sgshapiro	**  the file offset in the stdio library for the
411064562Sgshapiro	**  parent process will not agree with the in-kernel
411164562Sgshapiro	**  file offset since the file descriptor is shared
411264562Sgshapiro	**  between the processes.  Therefore, it is vital
411364562Sgshapiro	**  that the file always be rewound.  This forces the
411464562Sgshapiro	**  kernel offset (lseek) and stdio library (ftell)
411564562Sgshapiro	**  offset to match.
411664562Sgshapiro	*/
411764562Sgshapiro
411864562Sgshapiro	if (e->e_dfp != NULL)
411964562Sgshapiro		(void) bfrewind(e->e_dfp);
412064562Sgshapiro
412138032Speter	/* some mailers want extra blank line at end of message */
412264562Sgshapiro	if (!dead && bitnset(M_BLANKEND, mci->mci_mailer->m_flags) &&
412338032Speter	    buf[0] != '\0' && buf[0] != '\n')
412438032Speter		putline("", mci);
412538032Speter
412638032Speter	(void) fflush(mci->mci_out);
412738032Speter	if (ferror(mci->mci_out) && errno != EPIPE)
412838032Speter	{
412938032Speter		syserr("putbody: write error");
413038032Speter		ExitStat = EX_IOERR;
413138032Speter	}
413264562Sgshapiro
413338032Speter	errno = 0;
413438032Speter}
413538032Speter/*
413638032Speter**  MAILFILE -- Send a message to a file.
413738032Speter**
413838032Speter**	If the file has the setuid/setgid bits set, but NO execute
413938032Speter**	bits, sendmail will try to become the owner of that file
414038032Speter**	rather than the real user.  Obviously, this only works if
414138032Speter**	sendmail runs as root.
414238032Speter**
414338032Speter**	This could be done as a subordinate mailer, except that it
414438032Speter**	is used implicitly to save messages in ~/dead.letter.  We
414538032Speter**	view this as being sufficiently important as to include it
414638032Speter**	here.  For example, if the system is dying, we shouldn't have
414738032Speter**	to create another process plus some pipes to save the message.
414838032Speter**
414938032Speter**	Parameters:
415038032Speter**		filename -- the name of the file to send to.
415138032Speter**		mailer -- mailer definition for recipient -- if NULL,
415238032Speter**			use FileMailer.
415338032Speter**		ctladdr -- the controlling address header -- includes
415438032Speter**			the userid/groupid to be when sending.
415538032Speter**		sfflags -- flags for opening.
415638032Speter**		e -- the current envelope.
415738032Speter**
415838032Speter**	Returns:
415938032Speter**		The exit code associated with the operation.
416038032Speter**
416138032Speter**	Side Effects:
416238032Speter**		none.
416338032Speter*/
416438032Speter
416538032Speterstatic jmp_buf	CtxMailfileTimeout;
416638032Speter
416738032Speterint
416838032Spetermailfile(filename, mailer, ctladdr, sfflags, e)
416938032Speter	char *volatile filename;
417038032Speter	MAILER *volatile mailer;
417138032Speter	ADDRESS *ctladdr;
417264562Sgshapiro	volatile long sfflags;
417338032Speter	register ENVELOPE *e;
417438032Speter{
417538032Speter	register FILE *f;
417638032Speter	register pid_t pid = -1;
417764562Sgshapiro	volatile int mode;
417864562Sgshapiro	int len;
417964562Sgshapiro	off_t curoff;
418038032Speter	bool suidwarn = geteuid() == 0;
418138032Speter	char *p;
418264562Sgshapiro	char *volatile realfile;
418338032Speter	EVENT *ev;
418464562Sgshapiro	char buf[MAXLINE + 1];
418564562Sgshapiro	char targetfile[MAXPATHLEN + 1];
418638032Speter
418738032Speter	if (tTd(11, 1))
418838032Speter	{
418964562Sgshapiro		dprintf("mailfile %s\n  ctladdr=", filename);
419038032Speter		printaddr(ctladdr, FALSE);
419138032Speter	}
419238032Speter
419338032Speter	if (mailer == NULL)
419438032Speter		mailer = FileMailer;
419538032Speter
419638032Speter	if (e->e_xfp != NULL)
419764562Sgshapiro		(void) fflush(e->e_xfp);
419838032Speter
419938032Speter	/*
420038032Speter	**  Special case /dev/null.  This allows us to restrict file
420138032Speter	**  delivery to regular files only.
420238032Speter	*/
420338032Speter
420438032Speter	if (strcmp(filename, "/dev/null") == 0)
420538032Speter		return EX_OK;
420638032Speter
420738032Speter	/* check for 8-bit available */
420838032Speter	if (bitset(EF_HAS8BIT, e->e_flags) &&
420938032Speter	    bitnset(M_7BITS, mailer->m_flags) &&
421038032Speter	    (bitset(EF_DONT_MIME, e->e_flags) ||
421138032Speter	     !(bitset(MM_MIME8BIT, MimeMode) ||
421238032Speter	       (bitset(EF_IS_MIME, e->e_flags) &&
421338032Speter		bitset(MM_CVTMIME, MimeMode)))))
421438032Speter	{
421538032Speter		e->e_status = "5.6.3";
421664562Sgshapiro		usrerrenh(e->e_status,
421764562Sgshapiro		       "554 Cannot send 8-bit data to 7-bit destination");
421864562Sgshapiro		return EX_DATAERR;
421938032Speter	}
422038032Speter
422164562Sgshapiro	/* Find the actual file */
422264562Sgshapiro	if (SafeFileEnv != NULL && SafeFileEnv[0] != '\0')
422364562Sgshapiro	{
422464562Sgshapiro		len = strlen(SafeFileEnv);
422564562Sgshapiro
422664562Sgshapiro		if (strncmp(SafeFileEnv, filename, len) == 0)
422764562Sgshapiro			filename += len;
422864562Sgshapiro
422964562Sgshapiro		if (len + strlen(filename) + 1 > MAXPATHLEN)
423064562Sgshapiro		{
423164562Sgshapiro			syserr("mailfile: filename too long (%s/%s)",
423264562Sgshapiro			       SafeFileEnv, filename);
423364562Sgshapiro			return EX_CANTCREAT;
423464562Sgshapiro		}
423564562Sgshapiro		(void) strlcpy(targetfile, SafeFileEnv, sizeof targetfile);
423664562Sgshapiro		realfile = targetfile + len;
423764562Sgshapiro		if (targetfile[len - 1] != '/')
423864562Sgshapiro			(void) strlcat(targetfile, "/", sizeof targetfile);
423964562Sgshapiro		if (*filename == '/')
424064562Sgshapiro			filename++;
424164562Sgshapiro		(void) strlcat(targetfile, filename, sizeof targetfile);
424264562Sgshapiro	}
424364562Sgshapiro	else if (mailer->m_rootdir != NULL)
424464562Sgshapiro	{
424564562Sgshapiro		expand(mailer->m_rootdir, targetfile, sizeof targetfile, e);
424664562Sgshapiro		len = strlen(targetfile);
424764562Sgshapiro
424864562Sgshapiro		if (strncmp(targetfile, filename, len) == 0)
424964562Sgshapiro			filename += len;
425064562Sgshapiro
425164562Sgshapiro		if (len + strlen(filename) + 1 > MAXPATHLEN)
425264562Sgshapiro		{
425364562Sgshapiro			syserr("mailfile: filename too long (%s/%s)",
425464562Sgshapiro			       targetfile, filename);
425564562Sgshapiro			return EX_CANTCREAT;
425664562Sgshapiro		}
425764562Sgshapiro		realfile = targetfile + len;
425864562Sgshapiro		if (targetfile[len - 1] != '/')
425964562Sgshapiro			(void) strlcat(targetfile, "/", sizeof targetfile);
426064562Sgshapiro		if (*filename == '/')
426164562Sgshapiro			(void) strlcat(targetfile, filename + 1,
426264562Sgshapiro				       sizeof targetfile);
426364562Sgshapiro		else
426464562Sgshapiro			(void) strlcat(targetfile, filename, sizeof targetfile);
426564562Sgshapiro	}
426664562Sgshapiro	else
426764562Sgshapiro	{
426864562Sgshapiro		if (strlen(filename) > MAXPATHLEN)
426964562Sgshapiro		{
427064562Sgshapiro			syserr("mailfile: filename too long (%s)", filename);
427164562Sgshapiro			return EX_CANTCREAT;
427264562Sgshapiro		}
427364562Sgshapiro		(void) strlcpy(targetfile, filename, sizeof targetfile);
427464562Sgshapiro		realfile = targetfile;
427564562Sgshapiro	}
427664562Sgshapiro
427738032Speter	/*
427838032Speter	**  Fork so we can change permissions here.
427938032Speter	**	Note that we MUST use fork, not vfork, because of
428038032Speter	**	the complications of calling subroutines, etc.
428138032Speter	*/
428238032Speter
428338032Speter	DOFORK(fork);
428438032Speter
428538032Speter	if (pid < 0)
428664562Sgshapiro		return EX_OSERR;
428738032Speter	else if (pid == 0)
428838032Speter	{
428938032Speter		/* child -- actually write to file */
429038032Speter		struct stat stb;
429138032Speter		MCI mcibuf;
429242575Speter		int err;
429338032Speter		volatile int oflags = O_WRONLY|O_APPEND;
429438032Speter
429538032Speter		if (e->e_lockfp != NULL)
429638032Speter			(void) close(fileno(e->e_lockfp));
429738032Speter
429838032Speter		(void) setsignal(SIGINT, SIG_DFL);
429938032Speter		(void) setsignal(SIGHUP, SIG_DFL);
430038032Speter		(void) setsignal(SIGTERM, SIG_DFL);
430138032Speter		(void) umask(OldUmask);
430238032Speter		e->e_to = filename;
430338032Speter		ExitStat = EX_OK;
430438032Speter
430538032Speter		if (setjmp(CtxMailfileTimeout) != 0)
430638032Speter		{
430738032Speter			exit(EX_TEMPFAIL);
430838032Speter		}
430938032Speter
431038032Speter		if (TimeOuts.to_fileopen > 0)
431138032Speter			ev = setevent(TimeOuts.to_fileopen, mailfiletimeout, 0);
431238032Speter		else
431338032Speter			ev = NULL;
431438032Speter
431564562Sgshapiro		/* check file mode to see if setuid */
431664562Sgshapiro		if (stat(targetfile, &stb) < 0)
431764562Sgshapiro			mode = FileMode;
431842575Speter		else
431938032Speter			mode = stb.st_mode;
432038032Speter
432138032Speter		/* limit the errors to those actually caused in the child */
432238032Speter		errno = 0;
432338032Speter		ExitStat = EX_OK;
432438032Speter
432564562Sgshapiro		/* Allow alias expansions to use the S_IS{U,G}ID bits */
432664562Sgshapiro		if ((ctladdr != NULL && !bitset(QALIAS, ctladdr->q_flags)) ||
432764562Sgshapiro		    bitset(SFF_RUNASREALUID, sfflags))
432838032Speter		{
432938032Speter			/* ignore setuid and setgid bits */
433038032Speter			mode &= ~(S_ISGID|S_ISUID);
433164562Sgshapiro			if (tTd(11, 20))
433264562Sgshapiro				dprintf("mailfile: ignoring setuid/setgid bits\n");
433338032Speter		}
433438032Speter
433538032Speter		/* we have to open the dfile BEFORE setuid */
433638032Speter		if (e->e_dfp == NULL && bitset(EF_HAS_DF, e->e_flags))
433738032Speter		{
433838032Speter			char *df = queuename(e, 'd');
433938032Speter
434038032Speter			e->e_dfp = fopen(df, "r");
434138032Speter			if (e->e_dfp == NULL)
434238032Speter			{
434338032Speter				syserr("mailfile: Cannot open %s for %s from %s",
434438032Speter					df, e->e_to, e->e_from.q_paddr);
434538032Speter			}
434638032Speter		}
434738032Speter
434838032Speter		/* select a new user to run as */
434938032Speter		if (!bitset(SFF_RUNASREALUID, sfflags))
435038032Speter		{
435138032Speter			if (bitnset(M_SPECIFIC_UID, mailer->m_flags))
435238032Speter			{
435338032Speter				RealUserName = NULL;
435438032Speter				RealUid = mailer->m_uid;
435564562Sgshapiro				if (RunAsUid != 0 && RealUid != RunAsUid)
435664562Sgshapiro				{
435764562Sgshapiro					/* Only root can change the uid */
435864562Sgshapiro					syserr("mailfile: insufficient privileges to change uid");
435964562Sgshapiro					exit(EX_TEMPFAIL);
436064562Sgshapiro				}
436138032Speter			}
436238032Speter			else if (bitset(S_ISUID, mode))
436338032Speter			{
436438032Speter				RealUserName = NULL;
436538032Speter				RealUid = stb.st_uid;
436638032Speter			}
436738032Speter			else if (ctladdr != NULL && ctladdr->q_uid != 0)
436838032Speter			{
436938032Speter				if (ctladdr->q_ruser != NULL)
437038032Speter					RealUserName = ctladdr->q_ruser;
437138032Speter				else
437238032Speter					RealUserName = ctladdr->q_user;
437338032Speter				RealUid = ctladdr->q_uid;
437438032Speter			}
437538032Speter			else if (mailer != NULL && mailer->m_uid != 0)
437638032Speter			{
437738032Speter				RealUserName = DefUser;
437838032Speter				RealUid = mailer->m_uid;
437938032Speter			}
438038032Speter			else
438138032Speter			{
438238032Speter				RealUserName = DefUser;
438338032Speter				RealUid = DefUid;
438438032Speter			}
438538032Speter
438638032Speter			/* select a new group to run as */
438738032Speter			if (bitnset(M_SPECIFIC_UID, mailer->m_flags))
438864562Sgshapiro			{
438938032Speter				RealGid = mailer->m_gid;
439064562Sgshapiro				if (RunAsUid != 0 &&
439164562Sgshapiro				    (RealGid != getgid() ||
439264562Sgshapiro				     RealGid != getegid()))
439364562Sgshapiro				{
439464562Sgshapiro					/* Only root can change the gid */
439564562Sgshapiro					syserr("mailfile: insufficient privileges to change gid");
439664562Sgshapiro					exit(EX_TEMPFAIL);
439764562Sgshapiro				}
439864562Sgshapiro			}
439938032Speter			else if (bitset(S_ISGID, mode))
440038032Speter				RealGid = stb.st_gid;
440138032Speter			else if (ctladdr != NULL && ctladdr->q_uid != 0)
440238032Speter				RealGid = ctladdr->q_gid;
440364562Sgshapiro			else if (ctladdr != NULL &&
440464562Sgshapiro				 ctladdr->q_uid == DefUid &&
440564562Sgshapiro				 ctladdr->q_gid == 0)
440664562Sgshapiro				RealGid = DefGid;
440738032Speter			else if (mailer != NULL && mailer->m_gid != 0)
440838032Speter				RealGid = mailer->m_gid;
440938032Speter			else
441038032Speter				RealGid = DefGid;
441138032Speter		}
441238032Speter
441338032Speter		/* last ditch */
441438032Speter		if (!bitset(SFF_ROOTOK, sfflags))
441538032Speter		{
441638032Speter			if (RealUid == 0)
441738032Speter				RealUid = DefUid;
441838032Speter			if (RealGid == 0)
441938032Speter				RealGid = DefGid;
442038032Speter		}
442138032Speter
442238032Speter		/* set group id list (needs /etc/group access) */
442338032Speter		if (RealUserName != NULL && !DontInitGroups)
442438032Speter		{
442538032Speter			if (initgroups(RealUserName, RealGid) == -1 && suidwarn)
442664562Sgshapiro			{
442738032Speter				syserr("mailfile: initgroups(%s, %d) failed",
442838032Speter					RealUserName, RealGid);
442964562Sgshapiro				exit(EX_TEMPFAIL);
443064562Sgshapiro			}
443138032Speter		}
443238032Speter		else
443338032Speter		{
443438032Speter			GIDSET_T gidset[1];
443538032Speter
443638032Speter			gidset[0] = RealGid;
443738032Speter			if (setgroups(1, gidset) == -1 && suidwarn)
443864562Sgshapiro			{
443938032Speter				syserr("mailfile: setgroups() failed");
444064562Sgshapiro				exit(EX_TEMPFAIL);
444164562Sgshapiro			}
444238032Speter		}
444338032Speter
444464562Sgshapiro		/*
444564562Sgshapiro		**  If you have a safe environment, go into it.
444664562Sgshapiro		*/
444764562Sgshapiro
444864562Sgshapiro		if (realfile != targetfile)
444938032Speter		{
445064562Sgshapiro			*realfile = '\0';
445164562Sgshapiro			if (tTd(11, 20))
445264562Sgshapiro				dprintf("mailfile: chroot %s\n", targetfile);
445364562Sgshapiro			if (chroot(targetfile) < 0)
445438032Speter			{
445538032Speter				syserr("mailfile: Cannot chroot(%s)",
445664562Sgshapiro				       targetfile);
445738032Speter				exit(EX_CANTCREAT);
445838032Speter			}
445964562Sgshapiro			*realfile = '/';
446038032Speter		}
446164562Sgshapiro
446264562Sgshapiro		if (tTd(11, 40))
446364562Sgshapiro			dprintf("mailfile: deliver to %s\n", realfile);
446464562Sgshapiro
446538032Speter		if (chdir("/") < 0)
446664562Sgshapiro		{
446738032Speter			syserr("mailfile: cannot chdir(/)");
446864562Sgshapiro			exit(EX_CANTCREAT);
446964562Sgshapiro		}
447038032Speter
447138032Speter		/* now reset the group and user ids */
447238032Speter		endpwent();
447338032Speter		if (setgid(RealGid) < 0 && suidwarn)
447464562Sgshapiro		{
447538032Speter			syserr("mailfile: setgid(%ld) failed", (long) RealGid);
447664562Sgshapiro			exit(EX_TEMPFAIL);
447764562Sgshapiro		}
447838032Speter		vendor_set_uid(RealUid);
447938032Speter		if (setuid(RealUid) < 0 && suidwarn)
448064562Sgshapiro		{
448138032Speter			syserr("mailfile: setuid(%ld) failed", (long) RealUid);
448264562Sgshapiro			exit(EX_TEMPFAIL);
448364562Sgshapiro		}
448438032Speter
448564562Sgshapiro		if (tTd(11, 2))
448664562Sgshapiro			dprintf("mailfile: running as r/euid=%d/%d, r/egid=%d/%d\n",
448764562Sgshapiro				(int) getuid(), (int) geteuid(),
448864562Sgshapiro				(int) getgid(), (int) getegid());
448964562Sgshapiro
449064562Sgshapiro
449138032Speter		/* move into some "safe" directory */
449238032Speter		if (mailer->m_execdir != NULL)
449338032Speter		{
449438032Speter			char *q;
449538032Speter
449638032Speter			for (p = mailer->m_execdir; p != NULL; p = q)
449738032Speter			{
449838032Speter				q = strchr(p, ':');
449938032Speter				if (q != NULL)
450038032Speter					*q = '\0';
450138032Speter				expand(p, buf, sizeof buf, e);
450238032Speter				if (q != NULL)
450338032Speter					*q++ = ':';
450438032Speter				if (tTd(11, 20))
450564562Sgshapiro					dprintf("mailfile: trydir %s\n", buf);
450638032Speter				if (buf[0] != '\0' && chdir(buf) >= 0)
450738032Speter					break;
450838032Speter			}
450938032Speter		}
451038032Speter
451164562Sgshapiro		/*
451264562Sgshapiro		**  Recheck the file after we have assumed the ID of the
451364562Sgshapiro		**  delivery user to make sure we can deliver to it as
451464562Sgshapiro		**  that user.  This is necessary if sendmail is running
451564562Sgshapiro		**  as root and the file is on an NFS mount which treats
451664562Sgshapiro		**  root as nobody.
451764562Sgshapiro		*/
451864562Sgshapiro
451964562Sgshapiro#if HASLSTAT
452064562Sgshapiro		if (bitnset(DBS_FILEDELIVERYTOSYMLINK, DontBlameSendmail))
452164562Sgshapiro			err = stat(realfile, &stb);
452264562Sgshapiro		else
452364562Sgshapiro			err = lstat(realfile, &stb);
452464562Sgshapiro#else /* HASLSTAT */
452564562Sgshapiro		err = stat(realfile, &stb);
452664562Sgshapiro#endif /* HASLSTAT */
452764562Sgshapiro
452864562Sgshapiro		if (err < 0)
452964562Sgshapiro		{
453064562Sgshapiro			stb.st_mode = ST_MODE_NOFILE;
453164562Sgshapiro			mode = FileMode;
453264562Sgshapiro			oflags |= O_CREAT|O_EXCL;
453364562Sgshapiro		}
453464562Sgshapiro		else if (bitset(S_IXUSR|S_IXGRP|S_IXOTH, mode) ||
453564562Sgshapiro			 (!bitnset(DBS_FILEDELIVERYTOHARDLINK,
453664562Sgshapiro				   DontBlameSendmail) &&
453764562Sgshapiro			  stb.st_nlink != 1) ||
453864562Sgshapiro			 (realfile != targetfile && !S_ISREG(mode)))
453964562Sgshapiro			exit(EX_CANTCREAT);
454064562Sgshapiro		else
454164562Sgshapiro			mode = stb.st_mode;
454264562Sgshapiro
454364562Sgshapiro		if (!bitnset(DBS_FILEDELIVERYTOSYMLINK, DontBlameSendmail))
454438032Speter			sfflags |= SFF_NOSLINK;
454564562Sgshapiro		if (!bitnset(DBS_FILEDELIVERYTOHARDLINK, DontBlameSendmail))
454638032Speter			sfflags |= SFF_NOHLINK;
454738032Speter		sfflags &= ~SFF_OPENASROOT;
454864562Sgshapiro		f = safefopen(realfile, oflags, mode, sfflags);
454938032Speter		if (f == NULL)
455038032Speter		{
455164562Sgshapiro			if (transienterror(errno))
455264562Sgshapiro			{
455364562Sgshapiro				usrerr("454 4.3.0 cannot open %s: %s",
455464562Sgshapiro				       shortenstring(realfile, MAXSHORTSTR),
455564562Sgshapiro				       errstring(errno));
455664562Sgshapiro				exit(EX_TEMPFAIL);
455764562Sgshapiro			}
455864562Sgshapiro			else
455964562Sgshapiro			{
456064562Sgshapiro				usrerr("554 5.3.0 cannot open %s: %s",
456164562Sgshapiro				       shortenstring(realfile, MAXSHORTSTR),
456264562Sgshapiro				       errstring(errno));
456364562Sgshapiro				exit(EX_CANTCREAT);
456464562Sgshapiro			}
456538032Speter		}
456664562Sgshapiro		if (filechanged(realfile, fileno(f), &stb))
456738032Speter		{
456864562Sgshapiro			syserr("554 5.3.0 file changed after open");
456938032Speter			exit(EX_CANTCREAT);
457038032Speter		}
457138032Speter		if (fstat(fileno(f), &stb) < 0)
457238032Speter		{
457364562Sgshapiro			syserr("554 5.3.0 cannot fstat %s", errstring(errno));
457438032Speter			exit(EX_CANTCREAT);
457538032Speter		}
457638032Speter
457764562Sgshapiro		curoff = stb.st_size;
457864562Sgshapiro
457938032Speter		if (ev != NULL)
458038032Speter			clrevent(ev);
458138032Speter
458264562Sgshapiro		memset(&mcibuf, '\0', sizeof mcibuf);
458338032Speter		mcibuf.mci_mailer = mailer;
458438032Speter		mcibuf.mci_out = f;
458538032Speter		if (bitnset(M_7BITS, mailer->m_flags))
458638032Speter			mcibuf.mci_flags |= MCIF_7BIT;
458738032Speter
458838032Speter		/* clear out per-message flags from connection structure */
458938032Speter		mcibuf.mci_flags &= ~(MCIF_CVT7TO8|MCIF_CVT8TO7);
459038032Speter
459138032Speter		if (bitset(EF_HAS8BIT, e->e_flags) &&
459238032Speter		    !bitset(EF_DONT_MIME, e->e_flags) &&
459338032Speter		    bitnset(M_7BITS, mailer->m_flags))
459438032Speter			mcibuf.mci_flags |= MCIF_CVT8TO7;
459538032Speter
459638032Speter#if MIME7TO8
459738032Speter		if (bitnset(M_MAKE8BIT, mailer->m_flags) &&
459838032Speter		    !bitset(MCIF_7BIT, mcibuf.mci_flags) &&
459938032Speter		    (p = hvalue("Content-Transfer-Encoding", e->e_header)) != NULL &&
460038032Speter		    (strcasecmp(p, "quoted-printable") == 0 ||
460138032Speter		     strcasecmp(p, "base64") == 0) &&
460238032Speter		    (p = hvalue("Content-Type", e->e_header)) != NULL)
460338032Speter		{
460438032Speter			/* may want to convert 7 -> 8 */
460538032Speter			/* XXX should really parse it here -- and use a class XXX */
460638032Speter			if (strncasecmp(p, "text/plain", 10) == 0 &&
460764562Sgshapiro			    (p[10] == '\0' || p[10] == ' ' || p[10] == ';'))
460838032Speter				mcibuf.mci_flags |= MCIF_CVT7TO8;
460938032Speter		}
461064562Sgshapiro#endif /* MIME7TO8 */
461138032Speter
461238032Speter		putfromline(&mcibuf, e);
461343730Speter		(*e->e_puthdr)(&mcibuf, e->e_header, e, M87F_OUTER);
461438032Speter		(*e->e_putbody)(&mcibuf, e, NULL);
461538032Speter		putline("\n", &mcibuf);
461664562Sgshapiro		if (fflush(f) < 0 ||
461764562Sgshapiro		    (SuperSafe && fsync(fileno(f)) < 0) ||
461864562Sgshapiro		    ferror(f))
461938032Speter		{
462038032Speter			setstat(EX_IOERR);
462164562Sgshapiro#if !NOFTRUNCATE
462264562Sgshapiro			(void) ftruncate(fileno(f), curoff);
462364562Sgshapiro#endif /* !NOFTRUNCATE */
462438032Speter		}
462538032Speter
462638032Speter		/* reset ISUID & ISGID bits for paranoid systems */
462738032Speter#if HASFCHMOD
462864562Sgshapiro		(void) fchmod(fileno(f), (MODE_T) mode);
462964562Sgshapiro#else /* HASFCHMOD */
463064562Sgshapiro		(void) chmod(filename, (MODE_T) mode);
463164562Sgshapiro#endif /* HASFCHMOD */
463264562Sgshapiro		if (fclose(f) < 0)
463364562Sgshapiro			setstat(EX_IOERR);
463438032Speter		(void) fflush(stdout);
463564562Sgshapiro		(void) setuid(RealUid);
463638032Speter		exit(ExitStat);
463764562Sgshapiro		/* NOTREACHED */
463838032Speter	}
463938032Speter	else
464038032Speter	{
464138032Speter		/* parent -- wait for exit status */
464238032Speter		int st;
464338032Speter
464438032Speter		st = waitfor(pid);
464538032Speter		if (st == -1)
464638032Speter		{
464738032Speter			syserr("mailfile: %s: wait", mailer->m_name);
464864562Sgshapiro			return EX_SOFTWARE;
464938032Speter		}
465038032Speter		if (WIFEXITED(st))
465138032Speter			return (WEXITSTATUS(st));
465238032Speter		else
465338032Speter		{
465438032Speter			syserr("mailfile: %s: child died on signal %d",
465538032Speter			       mailer->m_name, st);
465664562Sgshapiro			return EX_UNAVAILABLE;
465738032Speter		}
465864562Sgshapiro		/* NOTREACHED */
465938032Speter	}
466038032Speter	return EX_UNAVAILABLE;	/* avoid compiler warning on IRIX */
466138032Speter}
466238032Speter
466338032Speterstatic void
466438032Spetermailfiletimeout()
466538032Speter{
466638032Speter	longjmp(CtxMailfileTimeout, 1);
466738032Speter}
466838032Speter/*
466938032Speter**  HOSTSIGNATURE -- return the "signature" for a host.
467038032Speter**
467138032Speter**	The signature describes how we are going to send this -- it
467238032Speter**	can be just the hostname (for non-Internet hosts) or can be
467338032Speter**	an ordered list of MX hosts.
467438032Speter**
467538032Speter**	Parameters:
467638032Speter**		m -- the mailer describing this host.
467738032Speter**		host -- the host name.
467838032Speter**
467938032Speter**	Returns:
468038032Speter**		The signature for this host.
468138032Speter**
468238032Speter**	Side Effects:
468338032Speter**		Can tweak the symbol table.
468438032Speter*/
468564562Sgshapiro#define MAXHOSTSIGNATURE	8192	/* max len of hostsignature */
468638032Speter
468764562Sgshapirostatic char *
468864562Sgshapirohostsignature(m, host)
468938032Speter	register MAILER *m;
469038032Speter	char *host;
469138032Speter{
469238032Speter	register char *p;
469338032Speter	register STAB *s;
469464562Sgshapiro#if NAMED_BIND
469564562Sgshapiro	char sep = ':';
469664562Sgshapiro	char prevsep = ':';
469738032Speter	int i;
469838032Speter	int len;
469938032Speter	int nmx;
470064562Sgshapiro	int hl;
470138032Speter	char *hp;
470238032Speter	char *endp;
470338032Speter	int oldoptions = _res.options;
470438032Speter	char *mxhosts[MAXMXHOSTS + 1];
470564562Sgshapiro	u_short mxprefs[MAXMXHOSTS + 1];
470664562Sgshapiro#endif /* NAMED_BIND */
470738032Speter
470864562Sgshapiro	if (tTd(17, 3))
470964562Sgshapiro		dprintf("hostsignature(%s)\n", host);
471064562Sgshapiro
471138032Speter	/*
471264562Sgshapiro	**  If local delivery, just return a constant.
471364562Sgshapiro	*/
471464562Sgshapiro
471564562Sgshapiro	if (bitnset(M_LOCALMAILER, m->m_flags))
471664562Sgshapiro		return "localhost";
471764562Sgshapiro
471864562Sgshapiro	/*
471938032Speter	**  Check to see if this uses IPC -- if not, it can't have MX records.
472038032Speter	*/
472138032Speter
472238032Speter	p = m->m_mailer;
472364562Sgshapiro	if (strcmp(p, "[IPC]") != 0 &&
472464562Sgshapiro	    strcmp(p, "[TCP]") != 0)
472538032Speter	{
472638032Speter		/* just an ordinary mailer */
472738032Speter		return host;
472838032Speter	}
472964562Sgshapiro#if NETUNIX
473064562Sgshapiro	else if (m->m_argv[0] != NULL &&
473164562Sgshapiro		 strcmp(m->m_argv[0], "FILE") == 0)
473264562Sgshapiro	{
473364562Sgshapiro		/* rendezvous in the file system, no MX records */
473464562Sgshapiro		return host;
473564562Sgshapiro	}
473664562Sgshapiro#endif /* NETUNIX */
473738032Speter
473838032Speter	/*
473938032Speter	**  Look it up in the symbol table.
474038032Speter	*/
474138032Speter
474238032Speter	s = stab(host, ST_HOSTSIG, ST_ENTER);
474338032Speter	if (s->s_hostsig != NULL)
474464562Sgshapiro	{
474564562Sgshapiro		if (tTd(17, 3))
474664562Sgshapiro			dprintf("hostsignature(): stab(%s) found %s\n", host,
474764562Sgshapiro				s->s_hostsig);
474838032Speter		return s->s_hostsig;
474964562Sgshapiro	}
475038032Speter
475138032Speter	/*
475238032Speter	**  Not already there -- create a signature.
475338032Speter	*/
475438032Speter
475538032Speter#if NAMED_BIND
475638032Speter	if (ConfigLevel < 2)
475738032Speter		_res.options &= ~(RES_DEFNAMES | RES_DNSRCH);	/* XXX */
475838032Speter
475938032Speter	for (hp = host; hp != NULL; hp = endp)
476038032Speter	{
476164562Sgshapiro#if NETINET6
476264562Sgshapiro		if (*hp == '[')
476364562Sgshapiro		{
476464562Sgshapiro			endp = strchr(hp + 1, ']');
476564562Sgshapiro			if (endp != NULL)
476664562Sgshapiro				endp = strpbrk(endp + 1, ":,");
476764562Sgshapiro		}
476864562Sgshapiro		else
476964562Sgshapiro			endp = strpbrk(hp, ":,");
477064562Sgshapiro#else /* NETINET6 */
477164562Sgshapiro		endp = strpbrk(hp, ":,");
477264562Sgshapiro#endif /* NETINET6 */
477338032Speter		if (endp != NULL)
477464562Sgshapiro		{
477564562Sgshapiro			sep = *endp;
477638032Speter			*endp = '\0';
477764562Sgshapiro		}
477838032Speter
477938032Speter		if (bitnset(M_NOMX, m->m_flags))
478038032Speter		{
478138032Speter			/* skip MX lookups */
478238032Speter			nmx = 1;
478338032Speter			mxhosts[0] = hp;
478438032Speter		}
478538032Speter		else
478638032Speter		{
478738032Speter			auto int rcode;
478838032Speter
478964562Sgshapiro			nmx = getmxrr(hp, mxhosts, mxprefs, TRUE, &rcode);
479038032Speter			if (nmx <= 0)
479138032Speter			{
479238032Speter				register MCI *mci;
479338032Speter
479438032Speter				/* update the connection info for this host */
479538032Speter				mci = mci_get(hp, m);
479638032Speter				mci->mci_errno = errno;
479738032Speter				mci->mci_herrno = h_errno;
479838032Speter				mci->mci_lastuse = curtime();
479964562Sgshapiro				if (rcode == EX_NOHOST)
480064562Sgshapiro					mci_setstat(mci, rcode, "5.1.2",
480164562Sgshapiro						"550 Host unknown");
480264562Sgshapiro				else
480364562Sgshapiro					mci_setstat(mci, rcode, NULL, NULL);
480438032Speter
480538032Speter				/* use the original host name as signature */
480638032Speter				nmx = 1;
480738032Speter				mxhosts[0] = hp;
480838032Speter			}
480964562Sgshapiro			if (tTd(17, 3))
481064562Sgshapiro				dprintf("hostsignature(): getmxrr() returned %d, mxhosts[0]=%s\n",
481164562Sgshapiro					nmx, mxhosts[0]);
481238032Speter		}
481338032Speter
481438032Speter		len = 0;
481538032Speter		for (i = 0; i < nmx; i++)
481638032Speter			len += strlen(mxhosts[i]) + 1;
481738032Speter		if (s->s_hostsig != NULL)
481838032Speter			len += strlen(s->s_hostsig) + 1;
481964562Sgshapiro		if (len >= MAXHOSTSIGNATURE)
482064562Sgshapiro		{
482164562Sgshapiro			sm_syslog(LOG_WARNING, NOQID, "hostsignature for host '%s' exceeds maxlen (%d): %d",
482264562Sgshapiro				  host, MAXHOSTSIGNATURE, len);
482364562Sgshapiro			len = MAXHOSTSIGNATURE;
482464562Sgshapiro		}
482538032Speter		p = xalloc(len);
482638032Speter		if (s->s_hostsig != NULL)
482738032Speter		{
482864562Sgshapiro			(void) strlcpy(p, s->s_hostsig, len);
482938032Speter			free(s->s_hostsig);
483038032Speter			s->s_hostsig = p;
483164562Sgshapiro			hl = strlen(p);
483264562Sgshapiro			p += hl;
483364562Sgshapiro			*p++ = prevsep;
483464562Sgshapiro			len -= hl + 1;
483538032Speter		}
483638032Speter		else
483738032Speter			s->s_hostsig = p;
483838032Speter		for (i = 0; i < nmx; i++)
483938032Speter		{
484064562Sgshapiro			hl = strlen(mxhosts[i]);
484164562Sgshapiro			if (len - 1 < hl || len <= 1)
484264562Sgshapiro			{
484364562Sgshapiro				/* force to drop out of outer loop */
484464562Sgshapiro				len = -1;
484564562Sgshapiro				break;
484664562Sgshapiro			}
484738032Speter			if (i != 0)
484864562Sgshapiro			{
484964562Sgshapiro				if (mxprefs[i] == mxprefs[i - 1])
485064562Sgshapiro					*p++ = ',';
485164562Sgshapiro				else
485264562Sgshapiro					*p++ = ':';
485364562Sgshapiro				len--;
485464562Sgshapiro			}
485564562Sgshapiro			(void) strlcpy(p, mxhosts[i], len);
485664562Sgshapiro			p += hl;
485764562Sgshapiro			len -= hl;
485838032Speter		}
485964562Sgshapiro
486064562Sgshapiro		/*
486164562Sgshapiro		**  break out of loop if len exceeded MAXHOSTSIGNATURE
486264562Sgshapiro		**  because we won't have more space for further hosts
486364562Sgshapiro		**  anyway (separated by : in the .cf file).
486464562Sgshapiro		*/
486564562Sgshapiro
486664562Sgshapiro		if (len < 0)
486764562Sgshapiro			break;
486838032Speter		if (endp != NULL)
486964562Sgshapiro			*endp++ = sep;
487064562Sgshapiro		prevsep = sep;
487138032Speter	}
487238032Speter	makelower(s->s_hostsig);
487338032Speter	if (ConfigLevel < 2)
487438032Speter		_res.options = oldoptions;
487564562Sgshapiro#else /* NAMED_BIND */
487638032Speter	/* not using BIND -- the signature is just the host name */
487738032Speter	s->s_hostsig = host;
487864562Sgshapiro#endif /* NAMED_BIND */
487938032Speter	if (tTd(17, 1))
488064562Sgshapiro		dprintf("hostsignature(%s) = %s\n", host, s->s_hostsig);
488138032Speter	return s->s_hostsig;
488238032Speter}
488364562Sgshapiro/*
488464562Sgshapiro**  PARSE_HOSTSIGNATURE -- parse the "signature" and return MX host array.
488564562Sgshapiro**
488664562Sgshapiro**	The signature describes how we are going to send this -- it
488764562Sgshapiro**	can be just the hostname (for non-Internet hosts) or can be
488864562Sgshapiro**	an ordered list of MX hosts which must be randomized for equal
488964562Sgshapiro**	MX preference values.
489064562Sgshapiro**
489164562Sgshapiro**	Parameters:
489264562Sgshapiro**		sig -- the host signature.
489364562Sgshapiro**		mxhosts -- array to populate.
489464562Sgshapiro**
489564562Sgshapiro**	Returns:
489664562Sgshapiro**		The number of hosts inserted into mxhosts array.
489764562Sgshapiro**
489864562Sgshapiro**	Side Effects:
489964562Sgshapiro**		Randomizes equal MX preference hosts in mxhosts.
490064562Sgshapiro*/
490164562Sgshapiro
490264562Sgshapirostatic int
490364562Sgshapiroparse_hostsignature(sig, mxhosts, mailer)
490464562Sgshapiro	char *sig;
490564562Sgshapiro	char **mxhosts;
490664562Sgshapiro	MAILER *mailer;
490764562Sgshapiro{
490864562Sgshapiro	int nmx = 0;
490964562Sgshapiro	int curpref = 0;
491064562Sgshapiro	int i, j;
491164562Sgshapiro	char *hp, *endp;
491264562Sgshapiro	u_short prefer[MAXMXHOSTS];
491364562Sgshapiro	long rndm[MAXMXHOSTS];
491464562Sgshapiro
491564562Sgshapiro	for (hp = sig; hp != NULL; hp = endp)
491664562Sgshapiro	{
491764562Sgshapiro		char sep = ':';
491864562Sgshapiro
491964562Sgshapiro#if NETINET6
492064562Sgshapiro		if (*hp == '[')
492164562Sgshapiro		{
492264562Sgshapiro			endp = strchr(hp + 1, ']');
492364562Sgshapiro			if (endp != NULL)
492464562Sgshapiro				endp = strpbrk(endp + 1, ":,");
492564562Sgshapiro		}
492664562Sgshapiro		else
492764562Sgshapiro			endp = strpbrk(hp, ":,");
492864562Sgshapiro#else /* NETINET6 */
492964562Sgshapiro		endp = strpbrk(hp, ":,");
493064562Sgshapiro#endif /* NETINET6 */
493164562Sgshapiro		if (endp != NULL)
493264562Sgshapiro		{
493364562Sgshapiro			sep = *endp;
493464562Sgshapiro			*endp = '\0';
493564562Sgshapiro		}
493664562Sgshapiro
493764562Sgshapiro		mxhosts[nmx] = hp;
493864562Sgshapiro		prefer[nmx] = curpref;
493964562Sgshapiro		if (mci_match(hp, mailer))
494064562Sgshapiro			rndm[nmx] = 0;
494164562Sgshapiro		else
494264562Sgshapiro			rndm[nmx] = get_random();
494364562Sgshapiro
494464562Sgshapiro		if (endp != NULL)
494564562Sgshapiro		{
494664562Sgshapiro			/*
494764562Sgshapiro			**  Since we don't have the original MX prefs,
494864562Sgshapiro			**  make our own.  If the separator is a ':', that
494964562Sgshapiro			**  means the preference for the next host will be
495064562Sgshapiro			**  higher than this one, so simply increment curpref.
495164562Sgshapiro			*/
495264562Sgshapiro
495364562Sgshapiro			if (sep == ':')
495464562Sgshapiro				curpref++;
495564562Sgshapiro
495664562Sgshapiro			*endp++ = sep;
495764562Sgshapiro		}
495864562Sgshapiro		if (++nmx >= MAXMXHOSTS)
495964562Sgshapiro			break;
496064562Sgshapiro	}
496164562Sgshapiro
496264562Sgshapiro	/* sort the records using the random factor for equal preferences */
496364562Sgshapiro	for (i = 0; i < nmx; i++)
496464562Sgshapiro	{
496564562Sgshapiro		for (j = i + 1; j < nmx; j++)
496664562Sgshapiro		{
496764562Sgshapiro			/*
496864562Sgshapiro			**  List is already sorted by MX preference, only
496964562Sgshapiro			**  need to look for equal preference MX records
497064562Sgshapiro			*/
497164562Sgshapiro
497264562Sgshapiro			if (prefer[i] < prefer[j])
497364562Sgshapiro				break;
497464562Sgshapiro
497564562Sgshapiro			if (prefer[i] > prefer[j] ||
497664562Sgshapiro			    (prefer[i] == prefer[j] && rndm[i] > rndm[j]))
497764562Sgshapiro			{
497864562Sgshapiro				register u_short tempp;
497964562Sgshapiro				register long tempr;
498064562Sgshapiro				register char *temp1;
498164562Sgshapiro
498264562Sgshapiro				tempp = prefer[i];
498364562Sgshapiro				prefer[i] = prefer[j];
498464562Sgshapiro				prefer[j] = tempp;
498564562Sgshapiro				temp1 = mxhosts[i];
498664562Sgshapiro				mxhosts[i] = mxhosts[j];
498764562Sgshapiro				mxhosts[j] = temp1;
498864562Sgshapiro				tempr = rndm[i];
498964562Sgshapiro				rndm[i] = rndm[j];
499064562Sgshapiro				rndm[j] = tempr;
499164562Sgshapiro			}
499264562Sgshapiro		}
499364562Sgshapiro	}
499464562Sgshapiro	return nmx;
499564562Sgshapiro}
499664562Sgshapiro
499764562Sgshapiro#if SMTP
499864562Sgshapiro# if STARTTLS
499964562Sgshapirostatic SSL_CTX	*clt_ctx = NULL;
500064562Sgshapiro
500164562Sgshapiro/*
500264562Sgshapiro**  INITCLTTLS -- initialize client side TLS
500364562Sgshapiro**
500464562Sgshapiro**	Parameters:
500564562Sgshapiro**		none.
500664562Sgshapiro**
500764562Sgshapiro**	Returns:
500864562Sgshapiro**		succeeded?
500964562Sgshapiro*/
501064562Sgshapiro
501164562Sgshapirobool
501264562Sgshapiroinitclttls()
501364562Sgshapiro{
501464562Sgshapiro	if (clt_ctx != NULL)
501564562Sgshapiro		return TRUE;	/* already done */
501664562Sgshapiro	return inittls(&clt_ctx, TLS_I_CLT, FALSE, CltCERTfile, Cltkeyfile,
501764562Sgshapiro		       CACERTpath, CACERTfile, DHParams);
501864562Sgshapiro}
501964562Sgshapiro
502064562Sgshapiro/*
502164562Sgshapiro**  STARTTLS -- try to start secure connection (client side)
502264562Sgshapiro**
502364562Sgshapiro**	Parameters:
502464562Sgshapiro**		m -- the mailer.
502564562Sgshapiro**		mci -- the mailer connection info.
502664562Sgshapiro**		e -- the envelope.
502764562Sgshapiro**
502864562Sgshapiro**	Returns:
502964562Sgshapiro**		success?
503064562Sgshapiro**		(maybe this should be some other code than EX_
503164562Sgshapiro**		that denotes which stage failed.)
503264562Sgshapiro*/
503364562Sgshapiro
503464562Sgshapirostatic int
503564562Sgshapirostarttls(m, mci, e)
503664562Sgshapiro	MAILER *m;
503764562Sgshapiro	MCI *mci;
503864562Sgshapiro	ENVELOPE *e;
503964562Sgshapiro{
504064562Sgshapiro	int smtpresult;
504166494Sgshapiro	int result = 0;
504266494Sgshapiro	int rfd, wfd;
504364562Sgshapiro	SSL *clt_ssl = NULL;
504464562Sgshapiro
504566494Sgshapiro	if (clt_ctx == NULL && !initclttls())
504666494Sgshapiro		return EX_TEMPFAIL;
504764562Sgshapiro	smtpmessage("STARTTLS", m, mci);
504864562Sgshapiro
504964562Sgshapiro	/* get the reply */
505064562Sgshapiro	smtpresult = reply(m, mci, e, TimeOuts.to_datafinal, NULL, NULL);
505164562Sgshapiro	/* which timeout? XXX */
505264562Sgshapiro
505364562Sgshapiro	/* check return code from server */
505464562Sgshapiro	if (smtpresult == 454)
505564562Sgshapiro		return EX_TEMPFAIL;
505664562Sgshapiro	if (smtpresult == 501)
505764562Sgshapiro		return EX_USAGE;
505864562Sgshapiro	if (smtpresult == -1)
505964562Sgshapiro		return smtpresult;
506064562Sgshapiro	if (smtpresult != 220)
506164562Sgshapiro		return EX_PROTOCOL;
506264562Sgshapiro
506364562Sgshapiro	if (LogLevel > 13)
506464562Sgshapiro		sm_syslog(LOG_INFO, e->e_id, "TLS: start client");
506564562Sgshapiro
506664562Sgshapiro	/* start connection */
506764562Sgshapiro	if ((clt_ssl = SSL_new(clt_ctx)) == NULL)
506864562Sgshapiro	{
506964562Sgshapiro		if (LogLevel > 5)
507064562Sgshapiro		{
507164562Sgshapiro			sm_syslog(LOG_ERR, e->e_id,
507264562Sgshapiro				  "TLS: error: client: SSL_new failed");
507364562Sgshapiro			if (LogLevel > 9)
507464562Sgshapiro				tlslogerr();
507564562Sgshapiro		}
507664562Sgshapiro		return EX_SOFTWARE;
507764562Sgshapiro	}
507864562Sgshapiro
507966494Sgshapiro	rfd = fileno(mci->mci_in);
508066494Sgshapiro	wfd = fileno(mci->mci_out);
508166494Sgshapiro
508264562Sgshapiro	/* SSL_clear(clt_ssl); ? */
508366494Sgshapiro	if (rfd < 0 || wfd < 0 ||
508466494Sgshapiro	    (result = SSL_set_rfd(clt_ssl, rfd)) <= 0 ||
508566494Sgshapiro	    (result = SSL_set_wfd(clt_ssl, wfd)) <= 0)
508664562Sgshapiro	{
508764562Sgshapiro		if (LogLevel > 5)
508864562Sgshapiro		{
508964562Sgshapiro			sm_syslog(LOG_ERR, e->e_id,
509064562Sgshapiro				  "TLS: error: SSL_set_xfd failed=%d", result);
509164562Sgshapiro			if (LogLevel > 9)
509264562Sgshapiro				tlslogerr();
509364562Sgshapiro		}
509464562Sgshapiro		return EX_SOFTWARE;
509564562Sgshapiro	}
509664562Sgshapiro	SSL_set_connect_state(clt_ssl);
509764562Sgshapiro	if ((result = SSL_connect(clt_ssl)) <= 0)
509864562Sgshapiro	{
509964562Sgshapiro		int i;
510064562Sgshapiro
510164562Sgshapiro		/* what to do in this case? */
510264562Sgshapiro		i = SSL_get_error(clt_ssl, result);
510364562Sgshapiro		if (LogLevel > 5)
510464562Sgshapiro		{
510564562Sgshapiro			sm_syslog(LOG_ERR, e->e_id,
510664562Sgshapiro				  "TLS: error: SSL_connect failed=%d (%d)",
510764562Sgshapiro				  result, i);
510864562Sgshapiro			if (LogLevel > 9)
510964562Sgshapiro				tlslogerr();
511064562Sgshapiro		}
511164562Sgshapiro		SSL_free(clt_ssl);
511264562Sgshapiro		clt_ssl = NULL;
511364562Sgshapiro		return EX_SOFTWARE;
511464562Sgshapiro	}
511564562Sgshapiro	mci->mci_ssl = clt_ssl;
511664562Sgshapiro	result = tls_get_info(clt_ssl, e, FALSE, mci->mci_host);
511764562Sgshapiro
511864562Sgshapiro	/* switch to use SSL... */
511964562Sgshapiro#if SFIO
512064562Sgshapiro	if (sfdctls(mci->mci_in, mci->mci_out, mci->mci_ssl) == 0)
512164562Sgshapiro		return EX_OK;
512264562Sgshapiro#else /* SFIO */
512364562Sgshapiro# if _FFR_TLS_TOREK
512464562Sgshapiro	if (sfdctls(&mci->mci_in, &mci->mci_out, mci->mci_ssl) == 0)
512564562Sgshapiro		return EX_OK;
512664562Sgshapiro# endif /* _FFR_TLS_TOREK */
512764562Sgshapiro#endif /* SFIO */
512864562Sgshapiro
512964562Sgshapiro	/* failure */
513064562Sgshapiro	SSL_free(clt_ssl);
513164562Sgshapiro	clt_ssl = NULL;
513264562Sgshapiro	return EX_SOFTWARE;
513364562Sgshapiro}
513464562Sgshapiro
513564562Sgshapiro/*
513664562Sgshapiro**  ENDTLSCLT -- shutdown secure connection (client side)
513764562Sgshapiro**
513864562Sgshapiro**	Parameters:
513964562Sgshapiro**		mci -- the mailer connection info.
514064562Sgshapiro**
514164562Sgshapiro**	Returns:
514264562Sgshapiro**		success?
514364562Sgshapiro*/
514464562Sgshapiroint
514564562Sgshapiroendtlsclt(mci)
514664562Sgshapiro	MCI *mci;
514764562Sgshapiro{
514864562Sgshapiro	int r;
514964562Sgshapiro
515064562Sgshapiro	if (!bitset(MCIF_TLSACT, mci->mci_flags))
515164562Sgshapiro		return EX_OK;
515264562Sgshapiro	r = endtls(mci->mci_ssl, "client");
515364562Sgshapiro	mci->mci_flags &= ~MCIF_TLSACT;
515464562Sgshapiro	return r;
515564562Sgshapiro}
515664562Sgshapiro/*
515764562Sgshapiro**  ENDTLS -- shutdown secure connection
515864562Sgshapiro**
515964562Sgshapiro**	Parameters:
516064562Sgshapiro**		ssl -- SSL connection information.
516164562Sgshapiro**		side -- srv/clt (for logging).
516264562Sgshapiro**
516364562Sgshapiro**	Returns:
516464562Sgshapiro**		success?
516564562Sgshapiro*/
516664562Sgshapiro
516764562Sgshapiroint
516864562Sgshapiroendtls(ssl, side)
516964562Sgshapiro	SSL *ssl;
517064562Sgshapiro	char *side;
517164562Sgshapiro{
517264562Sgshapiro	if (ssl != NULL)
517364562Sgshapiro	{
517464562Sgshapiro		int r;
517564562Sgshapiro
517664562Sgshapiro		if ((r = SSL_shutdown(ssl)) < 0)
517764562Sgshapiro		{
517864562Sgshapiro			if (LogLevel > 11)
517964562Sgshapiro				sm_syslog(LOG_WARNING, NOQID,
518064562Sgshapiro					  "SSL_shutdown %s failed: %d",
518164562Sgshapiro					  side, r);
518264562Sgshapiro			return EX_SOFTWARE;
518364562Sgshapiro		}
518464562Sgshapiro		else if (r == 0)
518564562Sgshapiro		{
518664562Sgshapiro			if (LogLevel > 13)
518764562Sgshapiro				sm_syslog(LOG_WARNING, NOQID,
518864562Sgshapiro					  "SSL_shutdown %s not done",
518964562Sgshapiro					  side);
519064562Sgshapiro			return EX_SOFTWARE;
519164562Sgshapiro		}
519264562Sgshapiro		SSL_free(ssl);
519364562Sgshapiro		ssl = NULL;
519464562Sgshapiro	}
519564562Sgshapiro	return EX_OK;
519664562Sgshapiro}
519764562Sgshapiro# endif /* STARTTLS */
519864562Sgshapiro#endif /* SMTP */
5199