savemail.c revision 66497
138032Speter/*
264565Sgshapiro * Copyright (c) 1998-2000 Sendmail, Inc. and its suppliers.
364565Sgshapiro *	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
1566497Sgshapirostatic char id[] = "@(#)$Id: savemail.c,v 8.212.4.5 2000/08/22 22:46:00 gshapiro Exp $";
1664565Sgshapiro#endif /* ! lint */
1738032Speter
1864565Sgshapiro/* $FreeBSD: head/contrib/sendmail/src/savemail.c 66497 2000-10-01 02:03:50Z gshapiro $ */
1938032Speter
2064565Sgshapiro#include <sendmail.h>
2164565Sgshapiro
2264565Sgshapiro
2364565Sgshapirostatic void	errbody __P((MCI *, ENVELOPE *, char *));
2464565Sgshapirostatic bool	pruneroute __P((char *));
2564565Sgshapiro
2638032Speter/*
2738032Speter**  SAVEMAIL -- Save mail on error
2838032Speter**
2938032Speter**	If mailing back errors, mail it back to the originator
3038032Speter**	together with an error message; otherwise, just put it in
3138032Speter**	dead.letter in the user's home directory (if he exists on
3238032Speter**	this machine).
3338032Speter**
3438032Speter**	Parameters:
3538032Speter**		e -- the envelope containing the message in error.
3638032Speter**		sendbody -- if TRUE, also send back the body of the
3738032Speter**			message; otherwise just send the header.
3838032Speter**
3938032Speter**	Returns:
4038032Speter**		none
4138032Speter**
4238032Speter**	Side Effects:
4338032Speter**		Saves the letter, by writing or mailing it back to the
4438032Speter**		sender, or by putting it in dead.letter in her home
4538032Speter**		directory.
4638032Speter*/
4738032Speter
4838032Speter/* defines for state machine */
4964565Sgshapiro#define ESM_REPORT		0	/* report to sender's terminal */
5064565Sgshapiro#define ESM_MAIL		1	/* mail back to sender */
5164565Sgshapiro#define ESM_QUIET		2	/* mail has already been returned */
5264565Sgshapiro#define ESM_DEADLETTER		3	/* save in ~/dead.letter */
5364565Sgshapiro#define ESM_POSTMASTER		4	/* return to postmaster */
5464565Sgshapiro#define ESM_DEADLETTERDROP	5	/* save in DeadLetterDrop */
5564565Sgshapiro#define ESM_PANIC		6	/* call loseqfile() */
5664565Sgshapiro#define ESM_DONE		7	/* message is successfully delivered */
5738032Speter
5838032Speter
5938032Spetervoid
6038032Spetersavemail(e, sendbody)
6138032Speter	register ENVELOPE *e;
6238032Speter	bool sendbody;
6338032Speter{
6438032Speter	register struct passwd *pw;
6538032Speter	register FILE *fp;
6638032Speter	int state;
6738032Speter	auto ADDRESS *q = NULL;
6838032Speter	register char *p;
6938032Speter	MCI mcibuf;
7038032Speter	int flags;
7164565Sgshapiro	long sff;
7264565Sgshapiro	char buf[MAXLINE + 1];
7338032Speter
7438032Speter	if (tTd(6, 1))
7538032Speter	{
7664565Sgshapiro		dprintf("\nsavemail, errormode = %c, id = %s, ExitStat = %d\n  e_from=",
7738032Speter			e->e_errormode, e->e_id == NULL ? "NONE" : e->e_id,
7838032Speter			ExitStat);
7938032Speter		printaddr(&e->e_from, FALSE);
8038032Speter	}
8138032Speter
8238032Speter	if (e->e_id == NULL)
8338032Speter	{
8438032Speter		/* can't return a message with no id */
8538032Speter		return;
8638032Speter	}
8738032Speter
8838032Speter	/*
8938032Speter	**  In the unhappy event we don't know who to return the mail
9038032Speter	**  to, make someone up.
9138032Speter	*/
9238032Speter
9338032Speter	if (e->e_from.q_paddr == NULL)
9438032Speter	{
9538032Speter		e->e_sender = "Postmaster";
9638032Speter		if (parseaddr(e->e_sender, &e->e_from,
9738032Speter			      RF_COPYPARSE|RF_SENDERADDR, '\0', NULL, e) == NULL)
9838032Speter		{
9964565Sgshapiro			syserr("553 5.3.5 Cannot parse Postmaster!");
10042580Speter			finis(TRUE, EX_SOFTWARE);
10138032Speter		}
10238032Speter	}
10338032Speter	e->e_to = NULL;
10438032Speter
10538032Speter	/*
10638032Speter	**  Basic state machine.
10738032Speter	**
10838032Speter	**	This machine runs through the following states:
10938032Speter	**
11038032Speter	**	ESM_QUIET	Errors have already been printed iff the
11138032Speter	**			sender is local.
11238032Speter	**	ESM_REPORT	Report directly to the sender's terminal.
11338032Speter	**	ESM_MAIL	Mail response to the sender.
11438032Speter	**	ESM_DEADLETTER	Save response in ~/dead.letter.
11538032Speter	**	ESM_POSTMASTER	Mail response to the postmaster.
11664565Sgshapiro	**	ESM_DEADLETTERDROP
11764565Sgshapiro	**			If DeadLetterDrop set, save it there.
11838032Speter	**	ESM_PANIC	Save response anywhere possible.
11938032Speter	*/
12038032Speter
12138032Speter	/* determine starting state */
12238032Speter	switch (e->e_errormode)
12338032Speter	{
12438032Speter	  case EM_WRITE:
12538032Speter		state = ESM_REPORT;
12638032Speter		break;
12738032Speter
12838032Speter	  case EM_BERKNET:
12938032Speter	  case EM_MAIL:
13038032Speter		state = ESM_MAIL;
13138032Speter		break;
13238032Speter
13338032Speter	  case EM_PRINT:
13438032Speter	  case '\0':
13538032Speter		state = ESM_QUIET;
13638032Speter		break;
13738032Speter
13838032Speter	  case EM_QUIET:
13938032Speter		/* no need to return anything at all */
14038032Speter		return;
14138032Speter
14238032Speter	  default:
14364565Sgshapiro		syserr("554 5.3.0 savemail: bogus errormode x%x\n",
14464565Sgshapiro		       e->e_errormode);
14538032Speter		state = ESM_MAIL;
14638032Speter		break;
14738032Speter	}
14838032Speter
14938032Speter	/* if this is already an error response, send to postmaster */
15038032Speter	if (bitset(EF_RESPONSE, e->e_flags))
15138032Speter	{
15238032Speter		if (e->e_parent != NULL &&
15338032Speter		    bitset(EF_RESPONSE, e->e_parent->e_flags))
15438032Speter		{
15538032Speter			/* got an error sending a response -- can it */
15638032Speter			return;
15738032Speter		}
15838032Speter		state = ESM_POSTMASTER;
15938032Speter	}
16038032Speter
16138032Speter	while (state != ESM_DONE)
16238032Speter	{
16338032Speter		if (tTd(6, 5))
16464565Sgshapiro			dprintf("  state %d\n", state);
16538032Speter
16638032Speter		switch (state)
16738032Speter		{
16838032Speter		  case ESM_QUIET:
16938032Speter			if (bitnset(M_LOCALMAILER, e->e_from.q_mailer->m_flags))
17038032Speter				state = ESM_DEADLETTER;
17138032Speter			else
17238032Speter				state = ESM_MAIL;
17338032Speter			break;
17438032Speter
17538032Speter		  case ESM_REPORT:
17638032Speter
17738032Speter			/*
17838032Speter			**  If the user is still logged in on the same terminal,
17938032Speter			**  then write the error messages back to hir (sic).
18038032Speter			*/
18138032Speter
18238032Speter			p = ttypath();
18338032Speter			if (p == NULL || freopen(p, "w", stdout) == NULL)
18438032Speter			{
18538032Speter				state = ESM_MAIL;
18638032Speter				break;
18738032Speter			}
18838032Speter
18938032Speter			expand("\201n", buf, sizeof buf, e);
19038032Speter			printf("\r\nMessage from %s...\r\n", buf);
19138032Speter			printf("Errors occurred while sending mail.\r\n");
19238032Speter			if (e->e_xfp != NULL)
19338032Speter			{
19464565Sgshapiro				(void) bfrewind(e->e_xfp);
19564565Sgshapiro				printf("Transcript follows:\r\n");
19664565Sgshapiro				while (fgets(buf, sizeof buf, e->e_xfp) != NULL &&
19764565Sgshapiro				       !ferror(stdout))
19864565Sgshapiro					(void) fputs(buf, stdout);
19938032Speter			}
20038032Speter			else
20138032Speter			{
20238032Speter				syserr("Cannot open %s", queuename(e, 'x'));
20338032Speter				printf("Transcript of session is unavailable.\r\n");
20438032Speter			}
20538032Speter			printf("Original message will be saved in dead.letter.\r\n");
20638032Speter			state = ESM_DEADLETTER;
20738032Speter			break;
20838032Speter
20938032Speter		  case ESM_MAIL:
21038032Speter			/*
21138032Speter			**  If mailing back, do it.
21238032Speter			**	Throw away all further output.  Don't alias,
21338032Speter			**	since this could cause loops, e.g., if joe
21438032Speter			**	mails to joe@x, and for some reason the network
21538032Speter			**	for @x is down, then the response gets sent to
21638032Speter			**	joe@x, which gives a response, etc.  Also force
21738032Speter			**	the mail to be delivered even if a version of
21838032Speter			**	it has already been sent to the sender.
21938032Speter			**
22038032Speter			**  If this is a configuration or local software
22138032Speter			**	error, send to the local postmaster as well,
22238032Speter			**	since the originator can't do anything
22338032Speter			**	about it anyway.  Note that this is a full
22438032Speter			**	copy of the message (intentionally) so that
22538032Speter			**	the Postmaster can forward things along.
22638032Speter			*/
22738032Speter
22838032Speter			if (ExitStat == EX_CONFIG || ExitStat == EX_SOFTWARE)
22938032Speter			{
23038032Speter				(void) sendtolist("postmaster",
23138032Speter					  NULLADDR, &e->e_errorqueue, 0, e);
23238032Speter			}
23338032Speter			if (!emptyaddr(&e->e_from))
23438032Speter			{
23538032Speter				char from[TOBUFSIZE];
23638032Speter
23764565Sgshapiro				if (strlen(e->e_from.q_paddr) >= sizeof from)
23838032Speter				{
23938032Speter					state = ESM_POSTMASTER;
24038032Speter					break;
24138032Speter				}
24264565Sgshapiro				(void) strlcpy(from, e->e_from.q_paddr,
24364565Sgshapiro					       sizeof from);
24438032Speter
24538032Speter				if (!DontPruneRoutes && pruneroute(from))
24638032Speter				{
24738032Speter					ADDRESS *a;
24838032Speter
24938032Speter					for (a = e->e_errorqueue; a != NULL;
25038032Speter					     a = a->q_next)
25138032Speter					{
25238032Speter						if (sameaddr(a, &e->e_from))
25364565Sgshapiro							a->q_state = QS_DUPLICATE;
25438032Speter					}
25538032Speter				}
25638032Speter				(void) sendtolist(from, NULLADDR,
25738032Speter						  &e->e_errorqueue, 0, e);
25838032Speter			}
25938032Speter
26038032Speter			/*
26138032Speter			**  Deliver a non-delivery report to the
26238032Speter			**  Postmaster-designate (not necessarily
26338032Speter			**  Postmaster).  This does not include the
26438032Speter			**  body of the message, for privacy reasons.
26538032Speter			**  You really shouldn't need this.
26638032Speter			*/
26738032Speter
26838032Speter			e->e_flags |= EF_PM_NOTIFY;
26938032Speter
27038032Speter			/* check to see if there are any good addresses */
27138032Speter			for (q = e->e_errorqueue; q != NULL; q = q->q_next)
27264565Sgshapiro			{
27364565Sgshapiro				if (QS_IS_SENDABLE(q->q_state))
27438032Speter					break;
27564565Sgshapiro			}
27638032Speter			if (q == NULL)
27738032Speter			{
27838032Speter				/* this is an error-error */
27938032Speter				state = ESM_POSTMASTER;
28038032Speter				break;
28138032Speter			}
28238032Speter			if (returntosender(e->e_message, e->e_errorqueue,
28338032Speter					   sendbody ? RTSF_SEND_BODY
28438032Speter						    : RTSF_NO_BODY,
28538032Speter					   e) == 0)
28638032Speter			{
28738032Speter				state = ESM_DONE;
28838032Speter				break;
28938032Speter			}
29038032Speter
29138032Speter			/* didn't work -- return to postmaster */
29238032Speter			state = ESM_POSTMASTER;
29338032Speter			break;
29438032Speter
29538032Speter		  case ESM_POSTMASTER:
29638032Speter			/*
29738032Speter			**  Similar to previous case, but to system postmaster.
29838032Speter			*/
29938032Speter
30038032Speter			q = NULL;
30164565Sgshapiro			expand(DoubleBounceAddr, buf, sizeof buf, e);
30264565Sgshapiro			if (sendtolist(buf, NULLADDR, &q, 0, e) <= 0)
30338032Speter			{
30464565Sgshapiro				syserr("553 5.3.0 cannot parse %s!", buf);
30538032Speter				ExitStat = EX_SOFTWARE;
30664565Sgshapiro				state = ESM_DEADLETTERDROP;
30738032Speter				break;
30838032Speter			}
30938032Speter			flags = RTSF_PM_BOUNCE;
31038032Speter			if (sendbody)
31138032Speter				flags |= RTSF_SEND_BODY;
31238032Speter			if (returntosender(e->e_message, q, flags, e) == 0)
31338032Speter			{
31438032Speter				state = ESM_DONE;
31538032Speter				break;
31638032Speter			}
31738032Speter
31838032Speter			/* didn't work -- last resort */
31964565Sgshapiro			state = ESM_DEADLETTERDROP;
32038032Speter			break;
32138032Speter
32238032Speter		  case ESM_DEADLETTER:
32338032Speter			/*
32438032Speter			**  Save the message in dead.letter.
32538032Speter			**	If we weren't mailing back, and the user is
32638032Speter			**	local, we should save the message in
32738032Speter			**	~/dead.letter so that the poor person doesn't
32838032Speter			**	have to type it over again -- and we all know
32938032Speter			**	what poor typists UNIX users are.
33038032Speter			*/
33138032Speter
33238032Speter			p = NULL;
33338032Speter			if (bitnset(M_HASPWENT, e->e_from.q_mailer->m_flags))
33438032Speter			{
33538032Speter				if (e->e_from.q_home != NULL)
33638032Speter					p = e->e_from.q_home;
33766497Sgshapiro				else if ((pw = sm_getpwnam(e->e_from.q_user)) != NULL &&
33866497Sgshapiro					 *pw->pw_dir != '\0')
33938032Speter					p = pw->pw_dir;
34038032Speter			}
34138032Speter			if (p == NULL || e->e_dfp == NULL)
34238032Speter			{
34338032Speter				/* no local directory or no data file */
34438032Speter				state = ESM_MAIL;
34538032Speter				break;
34638032Speter			}
34738032Speter
34838032Speter			/* we have a home directory; write dead.letter */
34938032Speter			define('z', p, e);
35064565Sgshapiro
35164565Sgshapiro			/* get the sender for the UnixFromLine */
35264565Sgshapiro			p = macvalue('g', e);
35364565Sgshapiro			define('g', e->e_sender, e);
35464565Sgshapiro
35538032Speter			expand("\201z/dead.letter", buf, sizeof buf, e);
35664565Sgshapiro			sff = SFF_CREAT|SFF_REGONLY|SFF_RUNASREALUID;
35738032Speter			if (RealUid == 0)
35864565Sgshapiro				sff |= SFF_ROOTOK;
35938032Speter			e->e_to = buf;
36064565Sgshapiro			if (writable(buf, NULL, sff) &&
36164565Sgshapiro			    mailfile(buf, FileMailer, NULL, sff, e) == EX_OK)
36238032Speter			{
36338032Speter				int oldverb = Verbose;
36438032Speter
36564565Sgshapiro				if (OpMode != MD_DAEMON && OpMode != MD_SMTP)
36664565Sgshapiro					Verbose = 1;
36764565Sgshapiro				if (Verbose > 0)
36864565Sgshapiro					message("Saved message in %s", buf);
36938032Speter				Verbose = oldverb;
37064565Sgshapiro				define('g', p, e);
37138032Speter				state = ESM_DONE;
37238032Speter				break;
37338032Speter			}
37464565Sgshapiro			define('g', p, e);
37538032Speter			state = ESM_MAIL;
37638032Speter			break;
37738032Speter
37864565Sgshapiro		  case ESM_DEADLETTERDROP:
37938032Speter			/*
38064565Sgshapiro			**  Log the mail in DeadLetterDrop file.
38138032Speter			*/
38238032Speter
38338032Speter			if (e->e_class < 0)
38438032Speter			{
38538032Speter				state = ESM_DONE;
38638032Speter				break;
38738032Speter			}
38838032Speter
38938032Speter			if ((SafeFileEnv != NULL && SafeFileEnv[0] != '\0') ||
39064565Sgshapiro			    DeadLetterDrop == NULL ||
39164565Sgshapiro			    DeadLetterDrop[0] == '\0')
39238032Speter			{
39338032Speter				state = ESM_PANIC;
39438032Speter				break;
39538032Speter			}
39638032Speter
39764565Sgshapiro			sff = SFF_CREAT|SFF_REGONLY|SFF_ROOTOK|SFF_OPENASROOT|SFF_MUSTOWN;
39864565Sgshapiro			if (!writable(DeadLetterDrop, NULL, sff) ||
39938032Speter			    (fp = safefopen(DeadLetterDrop, O_WRONLY|O_APPEND,
40064565Sgshapiro					    FileMode, sff)) == NULL)
40138032Speter			{
40238032Speter				state = ESM_PANIC;
40338032Speter				break;
40438032Speter			}
40538032Speter
40664565Sgshapiro			memset(&mcibuf, '\0', sizeof mcibuf);
40738032Speter			mcibuf.mci_out = fp;
40838032Speter			mcibuf.mci_mailer = FileMailer;
40938032Speter			if (bitnset(M_7BITS, FileMailer->m_flags))
41038032Speter				mcibuf.mci_flags |= MCIF_7BIT;
41138032Speter
41264565Sgshapiro			/* get the sender for the UnixFromLine */
41364565Sgshapiro			p = macvalue('g', e);
41464565Sgshapiro			define('g', e->e_sender, e);
41564565Sgshapiro
41638032Speter			putfromline(&mcibuf, e);
41743733Speter			(*e->e_puthdr)(&mcibuf, e->e_header, e, M87F_OUTER);
41838032Speter			(*e->e_putbody)(&mcibuf, e, NULL);
41938032Speter			putline("\n", &mcibuf);
42038032Speter			(void) fflush(fp);
42164565Sgshapiro			if (ferror(fp) ||
42264565Sgshapiro			    fclose(fp) < 0)
42338032Speter				state = ESM_PANIC;
42438032Speter			else
42538032Speter			{
42638032Speter				int oldverb = Verbose;
42738032Speter
42864565Sgshapiro				if (OpMode != MD_DAEMON && OpMode != MD_SMTP)
42964565Sgshapiro					Verbose = 1;
43064565Sgshapiro				if (Verbose > 0)
43164565Sgshapiro					message("Saved message in %s",
43264565Sgshapiro						DeadLetterDrop);
43338032Speter				Verbose = oldverb;
43438032Speter				if (LogLevel > 3)
43538032Speter					sm_syslog(LOG_NOTICE, e->e_id,
43664565Sgshapiro						  "Saved message in %s",
43764565Sgshapiro						  DeadLetterDrop);
43838032Speter				state = ESM_DONE;
43938032Speter			}
44064565Sgshapiro			define('g', p, e);
44138032Speter			break;
44238032Speter
44338032Speter		  default:
44464565Sgshapiro			syserr("554 5.3.5 savemail: unknown state %d", state);
44538032Speter
44664565Sgshapiro			/* FALLTHROUGH */
44738032Speter
44838032Speter		  case ESM_PANIC:
44938032Speter			/* leave the locked queue & transcript files around */
45038032Speter			loseqfile(e, "savemail panic");
45166497Sgshapiro			errno = 0;
45238032Speter			syserr("!554 savemail: cannot save rejected email anywhere");
45338032Speter		}
45438032Speter	}
45538032Speter}
45638032Speter/*
45738032Speter**  RETURNTOSENDER -- return a message to the sender with an error.
45838032Speter**
45938032Speter**	Parameters:
46038032Speter**		msg -- the explanatory message.
46138032Speter**		returnq -- the queue of people to send the message to.
46238032Speter**		flags -- flags tweaking the operation:
46338032Speter**			RTSF_SENDBODY -- include body of message (otherwise
46438032Speter**				just send the header).
46538032Speter**			RTSF_PMBOUNCE -- this is a postmaster bounce.
46638032Speter**		e -- the current envelope.
46738032Speter**
46838032Speter**	Returns:
46938032Speter**		zero -- if everything went ok.
47038032Speter**		else -- some error.
47138032Speter**
47238032Speter**	Side Effects:
47338032Speter**		Returns the current message to the sender via
47438032Speter**		mail.
47538032Speter*/
47638032Speter
47738032Speter#define MAXRETURNS	6	/* max depth of returning messages */
47838032Speter#define ERRORFUDGE	100	/* nominal size of error message text */
47938032Speter
48038032Speterint
48138032Speterreturntosender(msg, returnq, flags, e)
48238032Speter	char *msg;
48338032Speter	ADDRESS *returnq;
48438032Speter	int flags;
48538032Speter	register ENVELOPE *e;
48638032Speter{
48738032Speter	register ENVELOPE *ee;
48838032Speter	ENVELOPE *oldcur = CurEnv;
48938032Speter	ENVELOPE errenvelope;
49038032Speter	static int returndepth = 0;
49138032Speter	register ADDRESS *q;
49238032Speter	char *p;
49338032Speter	char buf[MAXNAME + 1];
49438032Speter
49538032Speter	if (returnq == NULL)
49664565Sgshapiro		return -1;
49738032Speter
49838032Speter	if (msg == NULL)
49938032Speter		msg = "Unable to deliver mail";
50038032Speter
50138032Speter	if (tTd(6, 1))
50238032Speter	{
50364565Sgshapiro		dprintf("\n*** Return To Sender: msg=\"%s\", depth=%d, e=%lx, returnq=",
50464565Sgshapiro			msg, returndepth, (u_long) e);
50538032Speter		printaddr(returnq, TRUE);
50638032Speter		if (tTd(6, 20))
50738032Speter		{
50864565Sgshapiro			dprintf("Sendq=");
50938032Speter			printaddr(e->e_sendqueue, TRUE);
51038032Speter		}
51138032Speter	}
51238032Speter
51338032Speter	if (++returndepth >= MAXRETURNS)
51438032Speter	{
51538032Speter		if (returndepth != MAXRETURNS)
51664565Sgshapiro			syserr("554 5.3.0 returntosender: infinite recursion on %s",
51764565Sgshapiro			       returnq->q_paddr);
51838032Speter		/* don't "unrecurse" and fake a clean exit */
51938032Speter		/* returndepth--; */
52064565Sgshapiro		return 0;
52138032Speter	}
52238032Speter
52364565Sgshapiro	define('g', e->e_sender, e);
52438032Speter	define('u', NULL, e);
52538032Speter
52638032Speter	/* initialize error envelope */
52738032Speter	ee = newenvelope(&errenvelope, e);
52838032Speter	define('a', "\201b", ee);
52964565Sgshapiro	define('r', "", ee);
53038032Speter	define('s', "localhost", ee);
53138032Speter	define('_', "localhost", ee);
53264565Sgshapiro#if SASL
53364565Sgshapiro	define(macid("{auth_type}", NULL), "", ee);
53464565Sgshapiro	define(macid("{auth_authen}", NULL), "", ee);
53564565Sgshapiro	define(macid("{auth_author}", NULL), "", ee);
53664565Sgshapiro	define(macid("{auth_ssf}", NULL), "", ee);
53764565Sgshapiro#endif /* SASL */
53864565Sgshapiro#if STARTTLS
53964565Sgshapiro	define(macid("{cert_issuer}", NULL), "", ee);
54064565Sgshapiro	define(macid("{cert_subject}", NULL), "", ee);
54164565Sgshapiro	define(macid("{cipher_bits}", NULL), "", ee);
54264565Sgshapiro	define(macid("{cipher}", NULL), "", ee);
54364565Sgshapiro	define(macid("{tls_version}", NULL), "", ee);
54464565Sgshapiro	define(macid("{verify}", NULL), "", ee);
54564565Sgshapiro# if _FFR_TLS_1
54664565Sgshapiro	define(macid("{alg_bits}", NULL), "", ee);
54764565Sgshapiro	define(macid("{cn_issuer}", NULL), "", ee);
54864565Sgshapiro	define(macid("{cn_subject}", NULL), "", ee);
54964565Sgshapiro# endif /* _FFR_TLS_1 */
55064565Sgshapiro#endif /* STARTTLS */
55164565Sgshapiro
55238032Speter	ee->e_puthdr = putheader;
55338032Speter	ee->e_putbody = errbody;
55438032Speter	ee->e_flags |= EF_RESPONSE|EF_METOO;
55538032Speter	if (!bitset(EF_OLDSTYLE, e->e_flags))
55638032Speter		ee->e_flags &= ~EF_OLDSTYLE;
55764565Sgshapiro	if (bitset(EF_DONT_MIME, e->e_flags))
55864565Sgshapiro	{
55964565Sgshapiro		ee->e_flags |= EF_DONT_MIME;
56064565Sgshapiro
56164565Sgshapiro		/*
56264565Sgshapiro		**  If we can't convert to MIME and we don't pass
56364565Sgshapiro		**  8-bit, we can't send the body.
56464565Sgshapiro		*/
56564565Sgshapiro
56664565Sgshapiro		if (bitset(EF_HAS8BIT, e->e_flags) &&
56764565Sgshapiro		    !bitset(MM_PASS8BIT, MimeMode))
56864565Sgshapiro			flags &= ~RTSF_SEND_BODY;
56964565Sgshapiro	}
57064565Sgshapiro
57138032Speter	ee->e_sendqueue = returnq;
57238032Speter	ee->e_msgsize = ERRORFUDGE;
57364565Sgshapiro	if (bitset(RTSF_SEND_BODY, flags) &&
57464565Sgshapiro	    !bitset(PRIV_NOBODYRETN, PrivacyFlags))
57538032Speter		ee->e_msgsize += e->e_msgsize;
57638032Speter	else
57738032Speter		ee->e_flags |= EF_NO_BODY_RETN;
57838032Speter	initsys(ee);
57964565Sgshapiro
58064565Sgshapiro#if NAMED_BIND
58164565Sgshapiro	_res.retry = TimeOuts.res_retry[RES_TO_FIRST];
58264565Sgshapiro	_res.retrans = TimeOuts.res_retrans[RES_TO_FIRST];
58364565Sgshapiro#endif /* NAMED_BIND */
58438032Speter	for (q = returnq; q != NULL; q = q->q_next)
58538032Speter	{
58664565Sgshapiro		if (QS_IS_BADADDR(q->q_state))
58738032Speter			continue;
58838032Speter
58938032Speter		q->q_flags &= ~(QHASNOTIFY|Q_PINGFLAGS);
59038032Speter		q->q_flags |= QPINGONFAILURE;
59138032Speter
59264565Sgshapiro		if (!QS_IS_DEAD(q->q_state))
59338032Speter			ee->e_nrcpts++;
59438032Speter
59538032Speter		if (q->q_alias == NULL)
59664565Sgshapiro			addheader("To", q->q_paddr, 0, &ee->e_header);
59738032Speter	}
59838032Speter
59938032Speter	if (LogLevel > 5)
60038032Speter	{
60164565Sgshapiro		if (bitset(EF_RESPONSE, e->e_flags))
60238032Speter			p = "return to sender";
60364565Sgshapiro		else if (bitset(EF_WARNING, e->e_flags))
60464565Sgshapiro			p = "sender notify";
60538032Speter		else if (bitset(RTSF_PM_BOUNCE, flags))
60638032Speter			p = "postmaster notify";
60738032Speter		else
60838032Speter			p = "DSN";
60938032Speter		sm_syslog(LOG_INFO, e->e_id,
61064565Sgshapiro			  "%s: %s: %s",
61164565Sgshapiro			  ee->e_id, p, shortenstring(msg, MAXSHORTSTR));
61238032Speter	}
61338032Speter
61438032Speter	if (SendMIMEErrors)
61538032Speter	{
61664565Sgshapiro		addheader("MIME-Version", "1.0", 0, &ee->e_header);
61738032Speter
61838032Speter		(void) snprintf(buf, sizeof buf, "%s.%ld/%.100s",
61964565Sgshapiro				ee->e_id, (long) curtime(), MyHostName);
62038032Speter		ee->e_msgboundary = newstr(buf);
62138032Speter		(void) snprintf(buf, sizeof buf,
62238032Speter#if DSN
62364565Sgshapiro				"multipart/report; report-type=delivery-status;\n\tboundary=\"%s\"",
62464565Sgshapiro#else /* DSN */
62564565Sgshapiro				"multipart/mixed; boundary=\"%s\"",
62664565Sgshapiro#endif /* DSN */
62764565Sgshapiro				ee->e_msgboundary);
62864565Sgshapiro		addheader("Content-Type", buf, 0, &ee->e_header);
62938032Speter
63038032Speter		p = hvalue("Content-Transfer-Encoding", e->e_header);
63138032Speter		if (p != NULL && strcasecmp(p, "binary") != 0)
63238032Speter			p = NULL;
63338032Speter		if (p == NULL && bitset(EF_HAS8BIT, e->e_flags))
63438032Speter			p = "8bit";
63538032Speter		if (p != NULL)
63664565Sgshapiro			addheader("Content-Transfer-Encoding",
63764565Sgshapiro				  p, 0, &ee->e_header);
63838032Speter	}
63938032Speter	if (strncmp(msg, "Warning:", 8) == 0)
64038032Speter	{
64164565Sgshapiro		addheader("Subject", msg, 0, &ee->e_header);
64238032Speter		p = "warning-timeout";
64338032Speter	}
64438032Speter	else if (strncmp(msg, "Postmaster warning:", 19) == 0)
64538032Speter	{
64664565Sgshapiro		addheader("Subject", msg, 0, &ee->e_header);
64738032Speter		p = "postmaster-warning";
64838032Speter	}
64938032Speter	else if (strcmp(msg, "Return receipt") == 0)
65038032Speter	{
65164565Sgshapiro		addheader("Subject", msg, 0, &ee->e_header);
65238032Speter		p = "return-receipt";
65338032Speter	}
65438032Speter	else if (bitset(RTSF_PM_BOUNCE, flags))
65538032Speter	{
65664565Sgshapiro		snprintf(buf, sizeof buf,
65764565Sgshapiro			 "Postmaster notify: see transcript for details");
65864565Sgshapiro		addheader("Subject", buf, 0, &ee->e_header);
65938032Speter		p = "postmaster-notification";
66038032Speter	}
66138032Speter	else
66238032Speter	{
66364565Sgshapiro		snprintf(buf, sizeof buf,
66464565Sgshapiro			 "Returned mail: see transcript for details");
66564565Sgshapiro		addheader("Subject", buf, 0, &ee->e_header);
66638032Speter		p = "failure";
66738032Speter	}
66838032Speter	(void) snprintf(buf, sizeof buf, "auto-generated (%s)", p);
66964565Sgshapiro	addheader("Auto-Submitted", buf, 0, &ee->e_header);
67038032Speter
67138032Speter	/* fake up an address header for the from person */
67238032Speter	expand("\201n", buf, sizeof buf, e);
67364565Sgshapiro	if (parseaddr(buf, &ee->e_from,
67464565Sgshapiro		      RF_COPYALL|RF_SENDERADDR, '\0', NULL, e) == NULL)
67538032Speter	{
67664565Sgshapiro		syserr("553 5.3.5 Can't parse myself!");
67738032Speter		ExitStat = EX_SOFTWARE;
67838032Speter		returndepth--;
67964565Sgshapiro		return -1;
68038032Speter	}
68138032Speter	ee->e_from.q_flags &= ~(QHASNOTIFY|Q_PINGFLAGS);
68238032Speter	ee->e_from.q_flags |= QPINGONFAILURE;
68338032Speter	ee->e_sender = ee->e_from.q_paddr;
68438032Speter
68538032Speter	/* push state into submessage */
68638032Speter	CurEnv = ee;
68738032Speter	define('f', "\201n", ee);
68838032Speter	define('x', "Mail Delivery Subsystem", ee);
68938032Speter	eatheader(ee, TRUE);
69038032Speter
69138032Speter	/* mark statistics */
69238032Speter	markstats(ee, NULLADDR, FALSE);
69338032Speter
69438032Speter	/* actually deliver the error message */
69538032Speter	sendall(ee, SM_DELIVER);
69638032Speter
69738032Speter	/* restore state */
69838032Speter	dropenvelope(ee, TRUE);
69938032Speter	CurEnv = oldcur;
70038032Speter	returndepth--;
70138032Speter
70238032Speter	/* check for delivery errors */
70364565Sgshapiro	if (ee->e_parent == NULL ||
70464565Sgshapiro	    !bitset(EF_RESPONSE, ee->e_parent->e_flags))
70538032Speter		return 0;
70638032Speter	for (q = ee->e_sendqueue; q != NULL; q = q->q_next)
70738032Speter	{
70864565Sgshapiro		if (QS_IS_ATTEMPTED(q->q_state))
70938032Speter			return 0;
71038032Speter	}
71138032Speter	return -1;
71238032Speter}
71338032Speter/*
71438032Speter**  ERRBODY -- output the body of an error message.
71538032Speter**
71638032Speter**	Typically this is a copy of the transcript plus a copy of the
71738032Speter**	original offending message.
71838032Speter**
71938032Speter**	Parameters:
72038032Speter**		mci -- the mailer connection information.
72138032Speter**		e -- the envelope we are working in.
72238032Speter**		separator -- any possible MIME separator.
72338032Speter**
72438032Speter**	Returns:
72538032Speter**		none
72638032Speter**
72738032Speter**	Side Effects:
72838032Speter**		Outputs the body of an error message.
72938032Speter*/
73038032Speter
73164565Sgshapiro/* ARGSUSED2 */
73264565Sgshapirostatic void
73338032Spetererrbody(mci, e, separator)
73438032Speter	register MCI *mci;
73538032Speter	register ENVELOPE *e;
73638032Speter	char *separator;
73738032Speter{
73864565Sgshapiro	bool printheader;
73964565Sgshapiro	bool sendbody;
74064565Sgshapiro	bool pm_notify;
74164565Sgshapiro	int save_errno;
74238032Speter	register FILE *xfile;
74338032Speter	char *p;
74438032Speter	register ADDRESS *q = NULL;
74538032Speter	char buf[MAXLINE];
74638032Speter
74738032Speter	if (bitset(MCIF_INHEADER, mci->mci_flags))
74838032Speter	{
74938032Speter		putline("", mci);
75038032Speter		mci->mci_flags &= ~MCIF_INHEADER;
75138032Speter	}
75238032Speter	if (e->e_parent == NULL)
75338032Speter	{
75438032Speter		syserr("errbody: null parent");
75538032Speter		putline("   ----- Original message lost -----\n", mci);
75638032Speter		return;
75738032Speter	}
75838032Speter
75938032Speter	/*
76038032Speter	**  Output MIME header.
76138032Speter	*/
76238032Speter
76338032Speter	if (e->e_msgboundary != NULL)
76438032Speter	{
76538032Speter		putline("This is a MIME-encapsulated message", mci);
76638032Speter		putline("", mci);
76738032Speter		(void) snprintf(buf, sizeof buf, "--%s", e->e_msgboundary);
76838032Speter		putline(buf, mci);
76938032Speter		putline("", mci);
77038032Speter	}
77138032Speter
77238032Speter	/*
77338032Speter	**  Output introductory information.
77438032Speter	*/
77538032Speter
77638032Speter	pm_notify = FALSE;
77738032Speter	p = hvalue("subject", e->e_header);
77838032Speter	if (p != NULL && strncmp(p, "Postmaster ", 11) == 0)
77938032Speter		pm_notify = TRUE;
78038032Speter	else
78138032Speter	{
78238032Speter		for (q = e->e_parent->e_sendqueue; q != NULL; q = q->q_next)
78364565Sgshapiro		{
78464565Sgshapiro			if (QS_IS_BADADDR(q->q_state))
78538032Speter				break;
78664565Sgshapiro		}
78738032Speter	}
78838032Speter	if (!pm_notify && q == NULL &&
78938032Speter	    !bitset(EF_FATALERRS|EF_SENDRECEIPT, e->e_parent->e_flags))
79038032Speter	{
79138032Speter		putline("    **********************************************",
79238032Speter			mci);
79338032Speter		putline("    **      THIS IS A WARNING MESSAGE ONLY      **",
79438032Speter			mci);
79538032Speter		putline("    **  YOU DO NOT NEED TO RESEND YOUR MESSAGE  **",
79638032Speter			mci);
79738032Speter		putline("    **********************************************",
79838032Speter			mci);
79938032Speter		putline("", mci);
80038032Speter	}
80138032Speter	snprintf(buf, sizeof buf, "The original message was received at %s",
80264565Sgshapiro		 arpadate(ctime(&e->e_parent->e_ctime)));
80338032Speter	putline(buf, mci);
80438032Speter	expand("from \201_", buf, sizeof buf, e->e_parent);
80538032Speter	putline(buf, mci);
80664565Sgshapiro
80764565Sgshapiro	/* include id in postmaster copies */
80864565Sgshapiro	if (pm_notify && e->e_parent->e_id != NULL)
80964565Sgshapiro	{
81064565Sgshapiro		snprintf(buf, sizeof buf, "with id %s", e->e_parent->e_id);
81164565Sgshapiro		putline(buf, mci);
81264565Sgshapiro	}
81338032Speter	putline("", mci);
81438032Speter
81538032Speter	/*
81638032Speter	**  Output error message header (if specified and available).
81738032Speter	*/
81838032Speter
81964565Sgshapiro	if (ErrMsgFile != NULL &&
82064565Sgshapiro	    !bitset(EF_SENDRECEIPT, e->e_parent->e_flags))
82138032Speter	{
82238032Speter		if (*ErrMsgFile == '/')
82338032Speter		{
82464565Sgshapiro			long sff = SFF_ROOTOK|SFF_REGONLY;
82538032Speter
82638032Speter			if (DontLockReadFiles)
82738032Speter				sff |= SFF_NOLOCK;
82864565Sgshapiro			if (!bitnset(DBS_ERRORHEADERINUNSAFEDIRPATH,
82964565Sgshapiro				     DontBlameSendmail))
83038032Speter				sff |= SFF_SAFEDIRPATH;
83138032Speter			xfile = safefopen(ErrMsgFile, O_RDONLY, 0444, sff);
83238032Speter			if (xfile != NULL)
83338032Speter			{
83438032Speter				while (fgets(buf, sizeof buf, xfile) != NULL)
83538032Speter				{
83638032Speter					translate_dollars(buf);
83738032Speter					expand(buf, buf, sizeof buf, e);
83838032Speter					putline(buf, mci);
83938032Speter				}
84038032Speter				(void) fclose(xfile);
84138032Speter				putline("\n", mci);
84238032Speter			}
84338032Speter		}
84438032Speter		else
84538032Speter		{
84638032Speter			expand(ErrMsgFile, buf, sizeof buf, e);
84738032Speter			putline(buf, mci);
84838032Speter			putline("", mci);
84938032Speter		}
85038032Speter	}
85138032Speter
85238032Speter	/*
85338032Speter	**  Output message introduction
85438032Speter	*/
85538032Speter
85638032Speter	printheader = TRUE;
85738032Speter	for (q = e->e_parent->e_sendqueue; q != NULL; q = q->q_next)
85838032Speter	{
85964565Sgshapiro		if (!QS_IS_BADADDR(q->q_state) ||
86038032Speter		    !bitset(QPINGONFAILURE, q->q_flags))
86138032Speter			continue;
86238032Speter
86338032Speter		if (printheader)
86438032Speter		{
86538032Speter			putline("   ----- The following addresses had permanent fatal errors -----",
86638032Speter				mci);
86738032Speter			printheader = FALSE;
86838032Speter		}
86938032Speter
87038032Speter		snprintf(buf, sizeof buf, "%s",
87138032Speter			 shortenstring(q->q_paddr, MAXSHORTSTR));
87238032Speter		putline(buf, mci);
87364565Sgshapiro		if (q->q_rstatus != NULL)
87464565Sgshapiro		{
87564565Sgshapiro			snprintf(buf, sizeof buf, "    (reason: %s)",
87664565Sgshapiro				 shortenstring(exitstat(q->q_rstatus),
87764565Sgshapiro					       MAXSHORTSTR));
87864565Sgshapiro			putline(buf, mci);
87964565Sgshapiro		}
88038032Speter		if (q->q_alias != NULL)
88138032Speter		{
88238032Speter			snprintf(buf, sizeof buf, "    (expanded from: %s)",
88364565Sgshapiro				 shortenstring(q->q_alias->q_paddr,
88464565Sgshapiro					       MAXSHORTSTR));
88538032Speter			putline(buf, mci);
88638032Speter		}
88738032Speter	}
88838032Speter	if (!printheader)
88938032Speter		putline("", mci);
89038032Speter
89138032Speter	printheader = TRUE;
89238032Speter	for (q = e->e_parent->e_sendqueue; q != NULL; q = q->q_next)
89338032Speter	{
89464565Sgshapiro		if (QS_IS_BADADDR(q->q_state) ||
89538032Speter		    !bitset(QPRIMARY, q->q_flags) ||
89638032Speter		    !bitset(QDELAYED, q->q_flags))
89738032Speter			continue;
89838032Speter
89938032Speter		if (printheader)
90038032Speter		{
90138032Speter			putline("   ----- The following addresses had transient non-fatal errors -----",
90238032Speter				mci);
90338032Speter			printheader = FALSE;
90438032Speter		}
90538032Speter
90638032Speter		snprintf(buf, sizeof buf, "%s",
90738032Speter			 shortenstring(q->q_paddr, MAXSHORTSTR));
90838032Speter		putline(buf, mci);
90938032Speter		if (q->q_alias != NULL)
91038032Speter		{
91138032Speter			snprintf(buf, sizeof buf, "    (expanded from: %s)",
91264565Sgshapiro				 shortenstring(q->q_alias->q_paddr,
91364565Sgshapiro					       MAXSHORTSTR));
91438032Speter			putline(buf, mci);
91538032Speter		}
91638032Speter	}
91738032Speter	if (!printheader)
91838032Speter		putline("", mci);
91938032Speter
92038032Speter	printheader = TRUE;
92138032Speter	for (q = e->e_parent->e_sendqueue; q != NULL; q = q->q_next)
92238032Speter	{
92364565Sgshapiro		if (QS_IS_BADADDR(q->q_state) ||
92438032Speter		    !bitset(QPRIMARY, q->q_flags) ||
92538032Speter		    bitset(QDELAYED, q->q_flags))
92638032Speter			continue;
92738032Speter		else if (!bitset(QPINGONSUCCESS, q->q_flags))
92838032Speter			continue;
92938032Speter		else if (bitset(QRELAYED, q->q_flags))
93038032Speter			p = "relayed to non-DSN-aware mailer";
93138032Speter		else if (bitset(QDELIVERED, q->q_flags))
93238032Speter		{
93338032Speter			if (bitset(QEXPANDED, q->q_flags))
93438032Speter				p = "successfully delivered to mailing list";
93538032Speter			else
93638032Speter				p = "successfully delivered to mailbox";
93738032Speter		}
93838032Speter		else if (bitset(QEXPANDED, q->q_flags))
93938032Speter			p = "expanded by alias";
94038032Speter		else
94138032Speter			continue;
94238032Speter
94338032Speter		if (printheader)
94438032Speter		{
94538032Speter			putline("   ----- The following addresses had successful delivery notifications -----",
94638032Speter				mci);
94738032Speter			printheader = FALSE;
94838032Speter		}
94938032Speter
95038032Speter		snprintf(buf, sizeof buf, "%s  (%s)",
95164565Sgshapiro			 shortenstring(q->q_paddr, MAXSHORTSTR), p);
95238032Speter		putline(buf, mci);
95338032Speter		if (q->q_alias != NULL)
95438032Speter		{
95538032Speter			snprintf(buf, sizeof buf, "    (expanded from: %s)",
95664565Sgshapiro				 shortenstring(q->q_alias->q_paddr,
95764565Sgshapiro					       MAXSHORTSTR));
95838032Speter			putline(buf, mci);
95938032Speter		}
96038032Speter	}
96138032Speter	if (!printheader)
96238032Speter		putline("", mci);
96338032Speter
96438032Speter	/*
96538032Speter	**  Output transcript of errors
96638032Speter	*/
96738032Speter
96838032Speter	(void) fflush(stdout);
96964565Sgshapiro	if (e->e_parent->e_xfp == NULL)
97038032Speter	{
97164565Sgshapiro		putline("   ----- Transcript of session is unavailable -----\n",
97264565Sgshapiro			mci);
97338032Speter	}
97438032Speter	else
97538032Speter	{
97638032Speter		printheader = TRUE;
97764565Sgshapiro		(void) bfrewind(e->e_parent->e_xfp);
97838032Speter		if (e->e_xfp != NULL)
97938032Speter			(void) fflush(e->e_xfp);
98064565Sgshapiro		while (fgets(buf, sizeof buf, e->e_parent->e_xfp) != NULL)
98138032Speter		{
98238032Speter			if (printheader)
98364565Sgshapiro				putline("   ----- Transcript of session follows -----\n",
98464565Sgshapiro					mci);
98538032Speter			printheader = FALSE;
98638032Speter			putline(buf, mci);
98738032Speter		}
98838032Speter	}
98938032Speter	errno = 0;
99038032Speter
99138032Speter#if DSN
99238032Speter	/*
99338032Speter	**  Output machine-readable version.
99438032Speter	*/
99538032Speter
99638032Speter	if (e->e_msgboundary != NULL)
99738032Speter	{
99838032Speter		putline("", mci);
99938032Speter		(void) snprintf(buf, sizeof buf, "--%s", e->e_msgboundary);
100038032Speter		putline(buf, mci);
100138032Speter		putline("Content-Type: message/delivery-status", mci);
100238032Speter		putline("", mci);
100338032Speter
100438032Speter		/*
100538032Speter		**  Output per-message information.
100638032Speter		*/
100738032Speter
100838032Speter		/* original envelope id from MAIL FROM: line */
100938032Speter		if (e->e_parent->e_envid != NULL)
101038032Speter		{
101164565Sgshapiro			(void) snprintf(buf, sizeof buf,
101264565Sgshapiro					"Original-Envelope-Id: %.800s",
101364565Sgshapiro					xuntextify(e->e_parent->e_envid));
101438032Speter			putline(buf, mci);
101538032Speter		}
101638032Speter
101738032Speter		/* Reporting-MTA: is us (required) */
101838032Speter		(void) snprintf(buf, sizeof buf, "Reporting-MTA: dns; %.800s", MyHostName);
101938032Speter		putline(buf, mci);
102038032Speter
102138032Speter		/* DSN-Gateway: not relevant since we are not translating */
102238032Speter
102338032Speter		/* Received-From-MTA: shows where we got this message from */
102438032Speter		if (RealHostName != NULL)
102538032Speter		{
102638032Speter			/* XXX use $s for type? */
102738032Speter			if (e->e_parent->e_from.q_mailer == NULL ||
102838032Speter			    (p = e->e_parent->e_from.q_mailer->m_mtatype) == NULL)
102938032Speter				p = "dns";
103064565Sgshapiro			(void) snprintf(buf, sizeof buf,
103164565Sgshapiro					"Received-From-MTA: %s; %.800s",
103264565Sgshapiro					p, RealHostName);
103338032Speter			putline(buf, mci);
103438032Speter		}
103538032Speter
103638032Speter		/* Arrival-Date: -- when it arrived here */
103738032Speter		(void) snprintf(buf, sizeof buf, "Arrival-Date: %s",
103864565Sgshapiro				arpadate(ctime(&e->e_parent->e_ctime)));
103938032Speter		putline(buf, mci);
104038032Speter
104138032Speter		/*
104238032Speter		**  Output per-address information.
104338032Speter		*/
104438032Speter
104538032Speter		for (q = e->e_parent->e_sendqueue; q != NULL; q = q->q_next)
104638032Speter		{
104738032Speter			register ADDRESS *r;
104838032Speter			char *action;
104938032Speter
105064565Sgshapiro			if (QS_IS_BADADDR(q->q_state))
105138032Speter				action = "failed";
105238032Speter			else if (!bitset(QPRIMARY, q->q_flags))
105338032Speter				continue;
105438032Speter			else if (bitset(QDELIVERED, q->q_flags))
105538032Speter			{
105638032Speter				if (bitset(QEXPANDED, q->q_flags))
105738032Speter					action = "delivered (to mailing list)";
105838032Speter				else
105938032Speter					action = "delivered (to mailbox)";
106038032Speter			}
106138032Speter			else if (bitset(QRELAYED, q->q_flags))
106238032Speter				action = "relayed (to non-DSN-aware mailer)";
106338032Speter			else if (bitset(QEXPANDED, q->q_flags))
106438032Speter				action = "expanded (to multi-recipient alias)";
106538032Speter			else if (bitset(QDELAYED, q->q_flags))
106638032Speter				action = "delayed";
106738032Speter			else
106838032Speter				continue;
106938032Speter
107038032Speter			putline("", mci);
107138032Speter
107238032Speter			/* Original-Recipient: -- passed from on high */
107338032Speter			if (q->q_orcpt != NULL)
107438032Speter			{
107564565Sgshapiro				(void) snprintf(buf, sizeof buf,
107664565Sgshapiro						"Original-Recipient: %.800s",
107764565Sgshapiro						q->q_orcpt);
107838032Speter				putline(buf, mci);
107938032Speter			}
108038032Speter
108138032Speter			/* Final-Recipient: -- the name from the RCPT command */
108238032Speter			p = e->e_parent->e_from.q_mailer->m_addrtype;
108338032Speter			if (p == NULL)
108438032Speter				p = "rfc822";
108538032Speter			for (r = q; r->q_alias != NULL; r = r->q_alias)
108638032Speter				continue;
108764565Sgshapiro			if (strcasecmp(p, "rfc822") != 0)
108838032Speter			{
108938032Speter				(void) snprintf(buf, sizeof buf,
109064565Sgshapiro						"Final-Recipient: %s; %.800s",
109164565Sgshapiro						r->q_mailer->m_addrtype,
109264565Sgshapiro						r->q_user);
109338032Speter			}
109464565Sgshapiro			else if (strchr(r->q_user, '@') != NULL)
109564565Sgshapiro			{
109664565Sgshapiro				(void) snprintf(buf, sizeof buf,
109764565Sgshapiro						"Final-Recipient: %s; %.800s",
109864565Sgshapiro						p, r->q_user);
109964565Sgshapiro			}
110038032Speter			else if (strchr(r->q_paddr, '@') != NULL)
110138032Speter			{
110264565Sgshapiro				char *qp;
110364565Sgshapiro				bool b;
110464565Sgshapiro
110564565Sgshapiro				qp = r->q_paddr;
110664565Sgshapiro				/* strip brackets from address */
110764565Sgshapiro				b = FALSE;
110864565Sgshapiro				if (*qp == '<')
110964565Sgshapiro				{
111064565Sgshapiro					b = qp[strlen(qp) - 1]  == '>';
111164565Sgshapiro					if (b)
111264565Sgshapiro						qp[strlen(qp) - 1] = '\0';
111364565Sgshapiro					qp++;
111464565Sgshapiro				}
111538032Speter				(void) snprintf(buf, sizeof buf,
111664565Sgshapiro						"Final-Recipient: %s; %.800s",
111764565Sgshapiro						p, qp);
111864565Sgshapiro				/* undo damage */
111964565Sgshapiro				if (b)
112064565Sgshapiro					qp[strlen(qp)] = '>';
112138032Speter			}
112238032Speter			else
112338032Speter			{
112438032Speter				(void) snprintf(buf, sizeof buf,
112564565Sgshapiro						"Final-Recipient: %s; %.700s@%.100s",
112664565Sgshapiro						p, r->q_user, MyHostName);
112738032Speter			}
112838032Speter			putline(buf, mci);
112938032Speter
113038032Speter			/* X-Actual-Recipient: -- the real problem address */
113138032Speter			if (r != q && q->q_user[0] != '\0')
113238032Speter			{
113364565Sgshapiro				if (q->q_mailer != NULL &&
113464565Sgshapiro				    q->q_mailer->m_addrtype != NULL)
113564565Sgshapiro					p = q->q_mailer->m_addrtype;
113664565Sgshapiro				else
113764565Sgshapiro					p = "rfc822";
113864565Sgshapiro
113964565Sgshapiro				if (strcasecmp(p, "rfc822") == 0 &&
114064565Sgshapiro				    strchr(q->q_user, '@') == NULL)
114138032Speter				{
114238032Speter					(void) snprintf(buf, sizeof buf,
114364565Sgshapiro							"X-Actual-Recipient: %s; %.700s@%.100s",
114464565Sgshapiro							p, q->q_user,
114564565Sgshapiro							MyHostName);
114638032Speter				}
114738032Speter				else
114838032Speter				{
114938032Speter					(void) snprintf(buf, sizeof buf,
115064565Sgshapiro							"X-Actual-Recipient: %s; %.800s",
115164565Sgshapiro							p, q->q_user);
115238032Speter				}
115338032Speter				putline(buf, mci);
115438032Speter			}
115538032Speter
115638032Speter			/* Action: -- what happened? */
115738032Speter			snprintf(buf, sizeof buf, "Action: %s", action);
115838032Speter			putline(buf, mci);
115938032Speter
116038032Speter			/* Status: -- what _really_ happened? */
116138032Speter			if (q->q_status != NULL)
116238032Speter				p = q->q_status;
116364565Sgshapiro			else if (QS_IS_BADADDR(q->q_state))
116438032Speter				p = "5.0.0";
116564565Sgshapiro			else if (QS_IS_QUEUEUP(q->q_state))
116638032Speter				p = "4.0.0";
116738032Speter			else
116838032Speter				p = "2.0.0";
116938032Speter			snprintf(buf, sizeof buf, "Status: %s", p);
117038032Speter			putline(buf, mci);
117138032Speter
117238032Speter			/* Remote-MTA: -- who was I talking to? */
117338032Speter			if (q->q_statmta != NULL)
117438032Speter			{
117538032Speter				if (q->q_mailer == NULL ||
117638032Speter				    (p = q->q_mailer->m_mtatype) == NULL)
117738032Speter					p = "dns";
117838032Speter				(void) snprintf(buf, sizeof buf,
117964565Sgshapiro						"Remote-MTA: %s; %.800s",
118064565Sgshapiro						p, q->q_statmta);
118138032Speter				p = &buf[strlen(buf) - 1];
118238032Speter				if (*p == '.')
118338032Speter					*p = '\0';
118438032Speter				putline(buf, mci);
118538032Speter			}
118638032Speter
118738032Speter			/* Diagnostic-Code: -- actual result from other end */
118838032Speter			if (q->q_rstatus != NULL)
118938032Speter			{
119038032Speter				p = q->q_mailer->m_diagtype;
119138032Speter				if (p == NULL)
119238032Speter					p = "smtp";
119338032Speter				(void) snprintf(buf, sizeof buf,
119464565Sgshapiro						"Diagnostic-Code: %s; %.800s",
119564565Sgshapiro						p, q->q_rstatus);
119638032Speter				putline(buf, mci);
119738032Speter			}
119838032Speter
119938032Speter			/* Last-Attempt-Date: -- fine granularity */
120038032Speter			if (q->q_statdate == (time_t) 0L)
120138032Speter				q->q_statdate = curtime();
120238032Speter			(void) snprintf(buf, sizeof buf,
120364565Sgshapiro					"Last-Attempt-Date: %s",
120464565Sgshapiro					arpadate(ctime(&q->q_statdate)));
120538032Speter			putline(buf, mci);
120638032Speter
120738032Speter			/* Will-Retry-Until: -- for delayed messages only */
120864565Sgshapiro			if (QS_IS_QUEUEUP(q->q_state))
120938032Speter			{
121038032Speter				time_t xdate;
121138032Speter
121238032Speter				xdate = e->e_parent->e_ctime +
121338032Speter					TimeOuts.to_q_return[e->e_parent->e_timeoutclass];
121438032Speter				snprintf(buf, sizeof buf,
121564565Sgshapiro					 "Will-Retry-Until: %s",
121664565Sgshapiro					 arpadate(ctime(&xdate)));
121738032Speter				putline(buf, mci);
121838032Speter			}
121938032Speter		}
122038032Speter	}
122164565Sgshapiro#endif /* DSN */
122238032Speter
122338032Speter	/*
122438032Speter	**  Output text of original message
122538032Speter	*/
122638032Speter
122738032Speter	putline("", mci);
122838032Speter	if (bitset(EF_HAS_DF, e->e_parent->e_flags))
122938032Speter	{
123038032Speter		sendbody = !bitset(EF_NO_BODY_RETN, e->e_parent->e_flags) &&
123138032Speter			   !bitset(EF_NO_BODY_RETN, e->e_flags);
123238032Speter
123338032Speter		if (e->e_msgboundary == NULL)
123438032Speter		{
123538032Speter			if (sendbody)
123638032Speter				putline("   ----- Original message follows -----\n", mci);
123738032Speter			else
123838032Speter				putline("   ----- Message header follows -----\n", mci);
123938032Speter		}
124038032Speter		else
124138032Speter		{
124238032Speter			(void) snprintf(buf, sizeof buf, "--%s",
124364565Sgshapiro					e->e_msgboundary);
124438032Speter
124538032Speter			putline(buf, mci);
124638032Speter			(void) snprintf(buf, sizeof buf, "Content-Type: %s",
124764565Sgshapiro					sendbody ? "message/rfc822"
124864565Sgshapiro						 : "text/rfc822-headers");
124938032Speter			putline(buf, mci);
125038032Speter
125164565Sgshapiro			p = hvalue("Content-Transfer-Encoding",
125264565Sgshapiro				   e->e_parent->e_header);
125338032Speter			if (p != NULL && strcasecmp(p, "binary") != 0)
125438032Speter				p = NULL;
125564565Sgshapiro			if (p == NULL &&
125664565Sgshapiro			    bitset(EF_HAS8BIT, e->e_parent->e_flags))
125738032Speter				p = "8bit";
125838032Speter			if (p != NULL)
125938032Speter			{
126064565Sgshapiro				(void) snprintf(buf, sizeof buf,
126164565Sgshapiro						"Content-Transfer-Encoding: %s",
126264565Sgshapiro						p);
126338032Speter				putline(buf, mci);
126438032Speter			}
126538032Speter		}
126638032Speter		putline("", mci);
126764565Sgshapiro		save_errno = errno;
126843733Speter		putheader(mci, e->e_parent->e_header, e->e_parent, M87F_OUTER);
126964565Sgshapiro		errno = save_errno;
127038032Speter		if (sendbody)
127138032Speter			putbody(mci, e->e_parent, e->e_msgboundary);
127238032Speter		else if (e->e_msgboundary == NULL)
127338032Speter		{
127438032Speter			putline("", mci);
127538032Speter			putline("   ----- Message body suppressed -----", mci);
127638032Speter		}
127738032Speter	}
127838032Speter	else if (e->e_msgboundary == NULL)
127938032Speter	{
128038032Speter		putline("  ----- No message was collected -----\n", mci);
128138032Speter	}
128238032Speter
128338032Speter	if (e->e_msgboundary != NULL)
128438032Speter	{
128538032Speter		putline("", mci);
128638032Speter		(void) snprintf(buf, sizeof buf, "--%s--", e->e_msgboundary);
128738032Speter		putline(buf, mci);
128838032Speter	}
128938032Speter	putline("", mci);
129064565Sgshapiro	(void) fflush(mci->mci_out);
129138032Speter
129238032Speter	/*
129338032Speter	**  Cleanup and exit
129438032Speter	*/
129538032Speter
129638032Speter	if (errno != 0)
129738032Speter		syserr("errbody: I/O error");
129838032Speter}
129938032Speter/*
130038032Speter**  SMTPTODSN -- convert SMTP to DSN status code
130138032Speter**
130238032Speter**	Parameters:
130338032Speter**		smtpstat -- the smtp status code (e.g., 550).
130438032Speter**
130538032Speter**	Returns:
130638032Speter**		The DSN version of the status code.
130738032Speter*/
130838032Speter
130938032Speterchar *
131038032Spetersmtptodsn(smtpstat)
131138032Speter	int smtpstat;
131238032Speter{
131338032Speter	if (smtpstat < 0)
131438032Speter		return "4.4.2";
131538032Speter
131638032Speter	switch (smtpstat)
131738032Speter	{
131838032Speter	  case 450:	/* Req mail action not taken: mailbox unavailable */
131938032Speter		return "4.2.0";
132038032Speter
132138032Speter	  case 451:	/* Req action aborted: local error in processing */
132238032Speter		return "4.3.0";
132338032Speter
132438032Speter	  case 452:	/* Req action not taken: insufficient sys storage */
132538032Speter		return "4.3.1";
132638032Speter
132738032Speter	  case 500:	/* Syntax error, command unrecognized */
132838032Speter		return "5.5.2";
132938032Speter
133038032Speter	  case 501:	/* Syntax error in parameters or arguments */
133138032Speter		return "5.5.4";
133238032Speter
133338032Speter	  case 502:	/* Command not implemented */
133438032Speter		return "5.5.1";
133538032Speter
133638032Speter	  case 503:	/* Bad sequence of commands */
133738032Speter		return "5.5.1";
133838032Speter
133938032Speter	  case 504:	/* Command parameter not implemented */
134038032Speter		return "5.5.4";
134138032Speter
134238032Speter	  case 550:	/* Req mail action not taken: mailbox unavailable */
134338032Speter		return "5.2.0";
134438032Speter
134538032Speter	  case 551:	/* User not local; please try <...> */
134638032Speter		return "5.1.6";
134738032Speter
134838032Speter	  case 552:	/* Req mail action aborted: exceeded storage alloc */
134938032Speter		return "5.2.2";
135038032Speter
135138032Speter	  case 553:	/* Req action not taken: mailbox name not allowed */
135238032Speter		return "5.1.0";
135338032Speter
135438032Speter	  case 554:	/* Transaction failed */
135538032Speter		return "5.0.0";
135638032Speter	}
135738032Speter
135838032Speter	if ((smtpstat / 100) == 2)
135938032Speter		return "2.0.0";
136038032Speter	if ((smtpstat / 100) == 4)
136138032Speter		return "4.0.0";
136238032Speter	return "5.0.0";
136338032Speter}
136438032Speter/*
136538032Speter**  XTEXTIFY -- take regular text and turn it into DSN-style xtext
136638032Speter**
136738032Speter**	Parameters:
136838032Speter**		t -- the text to convert.
136938032Speter**		taboo -- additional characters that must be encoded.
137038032Speter**
137138032Speter**	Returns:
137238032Speter**		The xtext-ified version of the same string.
137338032Speter*/
137438032Speter
137538032Speterchar *
137638032Speterxtextify(t, taboo)
137738032Speter	register char *t;
137838032Speter	char *taboo;
137938032Speter{
138038032Speter	register char *p;
138138032Speter	int l;
138238032Speter	int nbogus;
138338032Speter	static char *bp = NULL;
138438032Speter	static int bplen = 0;
138538032Speter
138638032Speter	if (taboo == NULL)
138738032Speter		taboo = "";
138838032Speter
138938032Speter	/* figure out how long this xtext will have to be */
139038032Speter	nbogus = l = 0;
139138032Speter	for (p = t; *p != '\0'; p++)
139238032Speter	{
139338032Speter		register int c = (*p & 0xff);
139438032Speter
139538032Speter		/* ASCII dependence here -- this is the way the spec words it */
139638032Speter		if (c < '!' || c > '~' || c == '+' || c == '\\' || c == '(' ||
139738032Speter		    strchr(taboo, c) != NULL)
139838032Speter			nbogus++;
139938032Speter		l++;
140038032Speter	}
140138032Speter	if (nbogus == 0)
140238032Speter		return t;
140338032Speter	l += nbogus * 2 + 1;
140438032Speter
140538032Speter	/* now allocate space if necessary for the new string */
140638032Speter	if (l > bplen)
140738032Speter	{
140838032Speter		if (bp != NULL)
140938032Speter			free(bp);
141038032Speter		bp = xalloc(l);
141138032Speter		bplen = l;
141238032Speter	}
141338032Speter
141438032Speter	/* ok, copy the text with byte expansion */
141538032Speter	for (p = bp; *t != '\0'; )
141638032Speter	{
141738032Speter		register int c = (*t++ & 0xff);
141838032Speter
141938032Speter		/* ASCII dependence here -- this is the way the spec words it */
142038032Speter		if (c < '!' || c > '~' || c == '+' || c == '\\' || c == '(' ||
142138032Speter		    strchr(taboo, c) != NULL)
142238032Speter		{
142338032Speter			*p++ = '+';
142464565Sgshapiro			*p++ = "0123456789ABCDEF"[c >> 4];
142564565Sgshapiro			*p++ = "0123456789ABCDEF"[c & 0xf];
142638032Speter		}
142738032Speter		else
142838032Speter			*p++ = c;
142938032Speter	}
143038032Speter	*p = '\0';
143138032Speter	return bp;
143238032Speter}
143338032Speter/*
143438032Speter**  XUNTEXTIFY -- take xtext and turn it into plain text
143538032Speter**
143638032Speter**	Parameters:
143738032Speter**		t -- the xtextified text.
143838032Speter**
143938032Speter**	Returns:
144038032Speter**		The decoded text.  No attempt is made to deal with
144138032Speter**		null strings in the resulting text.
144238032Speter*/
144338032Speter
144438032Speterchar *
144538032Speterxuntextify(t)
144638032Speter	register char *t;
144738032Speter{
144838032Speter	register char *p;
144938032Speter	int l;
145038032Speter	static char *bp = NULL;
145138032Speter	static int bplen = 0;
145238032Speter
145338032Speter	/* heuristic -- if no plus sign, just return the input */
145438032Speter	if (strchr(t, '+') == NULL)
145538032Speter		return t;
145638032Speter
145738032Speter	/* xtext is always longer than decoded text */
145838032Speter	l = strlen(t);
145938032Speter	if (l > bplen)
146038032Speter	{
146138032Speter		if (bp != NULL)
146238032Speter			free(bp);
146338032Speter		bp = xalloc(l);
146438032Speter		bplen = l;
146538032Speter	}
146638032Speter
146738032Speter	/* ok, copy the text with byte compression */
146838032Speter	for (p = bp; *t != '\0'; t++)
146938032Speter	{
147038032Speter		register int c = *t & 0xff;
147138032Speter
147238032Speter		if (c != '+')
147338032Speter		{
147438032Speter			*p++ = c;
147538032Speter			continue;
147638032Speter		}
147738032Speter
147838032Speter		c = *++t & 0xff;
147938032Speter		if (!isascii(c) || !isxdigit(c))
148038032Speter		{
148138032Speter			/* error -- first digit is not hex */
148238032Speter			usrerr("bogus xtext: +%c", c);
148338032Speter			t--;
148438032Speter			continue;
148538032Speter		}
148638032Speter		if (isdigit(c))
148738032Speter			c -= '0';
148838032Speter		else if (isupper(c))
148938032Speter			c -= 'A' - 10;
149038032Speter		else
149138032Speter			c -= 'a' - 10;
149238032Speter		*p = c << 4;
149338032Speter
149438032Speter		c = *++t & 0xff;
149538032Speter		if (!isascii(c) || !isxdigit(c))
149638032Speter		{
149738032Speter			/* error -- second digit is not hex */
149838032Speter			usrerr("bogus xtext: +%x%c", *p >> 4, c);
149938032Speter			t--;
150038032Speter			continue;
150138032Speter		}
150238032Speter		if (isdigit(c))
150338032Speter			c -= '0';
150438032Speter		else if (isupper(c))
150538032Speter			c -= 'A' - 10;
150638032Speter		else
150738032Speter			c -= 'a' - 10;
150838032Speter		*p++ |= c;
150938032Speter	}
151038032Speter	*p = '\0';
151138032Speter	return bp;
151238032Speter}
151338032Speter/*
151438032Speter**  XTEXTOK -- check if a string is legal xtext
151538032Speter**
151638032Speter**	Xtext is used in Delivery Status Notifications.  The spec was
151738032Speter**	taken from RFC 1891, ``SMTP Service Extension for Delivery
151838032Speter**	Status Notifications''.
151938032Speter**
152038032Speter**	Parameters:
152138032Speter**		s -- the string to check.
152238032Speter**
152338032Speter**	Returns:
152438032Speter**		TRUE -- if 's' is legal xtext.
152538032Speter**		FALSE -- if it has any illegal characters in it.
152638032Speter*/
152738032Speter
152838032Speterbool
152938032Speterxtextok(s)
153038032Speter	char *s;
153138032Speter{
153238032Speter	int c;
153338032Speter
153438032Speter	while ((c = *s++) != '\0')
153538032Speter	{
153638032Speter		if (c == '+')
153738032Speter		{
153838032Speter			c = *s++;
153938032Speter			if (!isascii(c) || !isxdigit(c))
154038032Speter				return FALSE;
154138032Speter			c = *s++;
154238032Speter			if (!isascii(c) || !isxdigit(c))
154338032Speter				return FALSE;
154438032Speter		}
154538032Speter		else if (c < '!' || c > '~' || c == '=')
154638032Speter			return FALSE;
154738032Speter	}
154838032Speter	return TRUE;
154938032Speter}
155038032Speter/*
155138032Speter**  PRUNEROUTE -- prune an RFC-822 source route
155264565Sgshapiro**
155338032Speter**	Trims down a source route to the last internet-registered hop.
155438032Speter**	This is encouraged by RFC 1123 section 5.3.3.
155564565Sgshapiro**
155638032Speter**	Parameters:
155738032Speter**		addr -- the address
155864565Sgshapiro**
155938032Speter**	Returns:
156038032Speter**		TRUE -- address was modified
156138032Speter**		FALSE -- address could not be pruned
156264565Sgshapiro**
156338032Speter**	Side Effects:
156438032Speter**		modifies addr in-place
156538032Speter*/
156638032Speter
156764565Sgshapirostatic bool
156838032Speterpruneroute(addr)
156938032Speter	char *addr;
157038032Speter{
157138032Speter#if NAMED_BIND
157238032Speter	char *start, *at, *comma;
157338032Speter	char c;
157438032Speter	int rcode;
157538032Speter	int i;
157638032Speter	char hostbuf[BUFSIZ];
157738032Speter	char *mxhosts[MAXMXHOSTS + 1];
157838032Speter
157938032Speter	/* check to see if this is really a route-addr */
158038032Speter	if (*addr != '<' || addr[1] != '@' || addr[strlen(addr) - 1] != '>')
158138032Speter		return FALSE;
158238032Speter	start = strchr(addr, ':');
158338032Speter	at = strrchr(addr, '@');
158438032Speter	if (start == NULL || at == NULL || at < start)
158538032Speter		return FALSE;
158638032Speter
158738032Speter	/* slice off the angle brackets */
158838032Speter	i = strlen(at + 1);
158938032Speter	if (i >= (SIZE_T) sizeof hostbuf)
159038032Speter		return FALSE;
159164565Sgshapiro	(void) strlcpy(hostbuf, at + 1, sizeof hostbuf);
159238032Speter	hostbuf[i - 1] = '\0';
159338032Speter
159438032Speter	while (start)
159538032Speter	{
159664565Sgshapiro		if (getmxrr(hostbuf, mxhosts, NULL, FALSE, &rcode) > 0)
159738032Speter		{
159864565Sgshapiro			(void) strlcpy(addr + 1, start + 1, strlen(addr) - 1);
159938032Speter			return TRUE;
160038032Speter		}
160138032Speter		c = *start;
160238032Speter		*start = '\0';
160338032Speter		comma = strrchr(addr, ',');
160438032Speter		if (comma != NULL && comma[1] == '@' &&
160538032Speter		    strlen(comma + 2) < (SIZE_T) sizeof hostbuf)
160664565Sgshapiro			(void) strlcpy(hostbuf, comma + 2, sizeof hostbuf);
160738032Speter		else
160838032Speter			comma = NULL;
160938032Speter		*start = c;
161038032Speter		start = comma;
161138032Speter	}
161264565Sgshapiro#endif /* NAMED_BIND */
161338032Speter	return FALSE;
161438032Speter}
1615