138032Speter/*
2261194Sgshapiro * Copyright (c) 1998-2003, 2006, 2012, 2013 Proofpoint, 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
1464565Sgshapiro#include <sendmail.h>
1564565Sgshapiro
16266527SgshapiroSM_RCSID("@(#)$Id: savemail.c,v 8.319 2013-11-22 20:51:56 ca Exp $")
1764565Sgshapiro
18157006Sgshapirostatic bool	errbody __P((MCI *, ENVELOPE *, char *));
1964565Sgshapirostatic bool	pruneroute __P((char *));
2064565Sgshapiro
2138032Speter/*
2238032Speter**  SAVEMAIL -- Save mail on error
2338032Speter**
2438032Speter**	If mailing back errors, mail it back to the originator
2538032Speter**	together with an error message; otherwise, just put it in
2638032Speter**	dead.letter in the user's home directory (if he exists on
2738032Speter**	this machine).
2838032Speter**
2938032Speter**	Parameters:
3038032Speter**		e -- the envelope containing the message in error.
3190795Sgshapiro**		sendbody -- if true, also send back the body of the
3238032Speter**			message; otherwise just send the header.
3338032Speter**
3438032Speter**	Returns:
3590795Sgshapiro**		true if savemail panic'ed, (i.e., the data file should
3690795Sgshapiro**		be preserved by dropenvelope())
3738032Speter**
3838032Speter**	Side Effects:
3938032Speter**		Saves the letter, by writing or mailing it back to the
4038032Speter**		sender, or by putting it in dead.letter in her home
4138032Speter**		directory.
4238032Speter*/
4338032Speter
4438032Speter/* defines for state machine */
4564565Sgshapiro#define ESM_REPORT		0	/* report to sender's terminal */
4664565Sgshapiro#define ESM_MAIL		1	/* mail back to sender */
4764565Sgshapiro#define ESM_QUIET		2	/* mail has already been returned */
4864565Sgshapiro#define ESM_DEADLETTER		3	/* save in ~/dead.letter */
4964565Sgshapiro#define ESM_POSTMASTER		4	/* return to postmaster */
5064565Sgshapiro#define ESM_DEADLETTERDROP	5	/* save in DeadLetterDrop */
5164565Sgshapiro#define ESM_PANIC		6	/* call loseqfile() */
5264565Sgshapiro#define ESM_DONE		7	/* message is successfully delivered */
5338032Speter
5490795Sgshapirobool
5538032Spetersavemail(e, sendbody)
5638032Speter	register ENVELOPE *e;
5738032Speter	bool sendbody;
5838032Speter{
5990795Sgshapiro	register SM_FILE_T *fp;
6090795Sgshapiro	bool panic = false;
6138032Speter	int state;
6238032Speter	auto ADDRESS *q = NULL;
6338032Speter	register char *p;
6438032Speter	MCI mcibuf;
6538032Speter	int flags;
6664565Sgshapiro	long sff;
6764565Sgshapiro	char buf[MAXLINE + 1];
6898125Sgshapiro	char dlbuf[MAXPATHLEN];
6990795Sgshapiro	SM_MBDB_T user;
7038032Speter
7190795Sgshapiro
7238032Speter	if (tTd(6, 1))
7338032Speter	{
7490795Sgshapiro		sm_dprintf("\nsavemail, errormode = %c, id = %s, ExitStat = %d\n  e_from=",
7538032Speter			e->e_errormode, e->e_id == NULL ? "NONE" : e->e_id,
7638032Speter			ExitStat);
77132946Sgshapiro		printaddr(sm_debug_file(), &e->e_from, false);
7838032Speter	}
7938032Speter
8038032Speter	if (e->e_id == NULL)
8138032Speter	{
8238032Speter		/* can't return a message with no id */
8390795Sgshapiro		return panic;
8438032Speter	}
8538032Speter
8638032Speter	/*
8738032Speter	**  In the unhappy event we don't know who to return the mail
8838032Speter	**  to, make someone up.
8938032Speter	*/
9038032Speter
9138032Speter	if (e->e_from.q_paddr == NULL)
9238032Speter	{
9338032Speter		e->e_sender = "Postmaster";
9438032Speter		if (parseaddr(e->e_sender, &e->e_from,
9590795Sgshapiro			      RF_COPYPARSE|RF_SENDERADDR,
9690795Sgshapiro			      '\0', NULL, e, false) == NULL)
9738032Speter		{
9864565Sgshapiro			syserr("553 5.3.5 Cannot parse Postmaster!");
9990795Sgshapiro			finis(true, true, EX_SOFTWARE);
10038032Speter		}
10138032Speter	}
10238032Speter	e->e_to = NULL;
10338032Speter
10438032Speter	/*
10538032Speter	**  Basic state machine.
10638032Speter	**
10738032Speter	**	This machine runs through the following states:
10838032Speter	**
10938032Speter	**	ESM_QUIET	Errors have already been printed iff the
11038032Speter	**			sender is local.
11138032Speter	**	ESM_REPORT	Report directly to the sender's terminal.
11238032Speter	**	ESM_MAIL	Mail response to the sender.
11338032Speter	**	ESM_DEADLETTER	Save response in ~/dead.letter.
11438032Speter	**	ESM_POSTMASTER	Mail response to the postmaster.
11564565Sgshapiro	**	ESM_DEADLETTERDROP
11664565Sgshapiro	**			If DeadLetterDrop set, save it there.
11738032Speter	**	ESM_PANIC	Save response anywhere possible.
11838032Speter	*/
11938032Speter
12038032Speter	/* determine starting state */
12138032Speter	switch (e->e_errormode)
12238032Speter	{
12338032Speter	  case EM_WRITE:
12438032Speter		state = ESM_REPORT;
12538032Speter		break;
12638032Speter
12738032Speter	  case EM_BERKNET:
12838032Speter	  case EM_MAIL:
12938032Speter		state = ESM_MAIL;
13038032Speter		break;
13138032Speter
13238032Speter	  case EM_PRINT:
13338032Speter	  case '\0':
13438032Speter		state = ESM_QUIET;
13538032Speter		break;
13638032Speter
13738032Speter	  case EM_QUIET:
13838032Speter		/* no need to return anything at all */
13990795Sgshapiro		return panic;
14038032Speter
14138032Speter	  default:
14290795Sgshapiro		syserr("554 5.3.0 savemail: bogus errormode x%x",
14364565Sgshapiro		       e->e_errormode);
14438032Speter		state = ESM_MAIL;
14538032Speter		break;
14638032Speter	}
14738032Speter
14838032Speter	/* if this is already an error response, send to postmaster */
14938032Speter	if (bitset(EF_RESPONSE, e->e_flags))
15038032Speter	{
15138032Speter		if (e->e_parent != NULL &&
15238032Speter		    bitset(EF_RESPONSE, e->e_parent->e_flags))
15338032Speter		{
15438032Speter			/* got an error sending a response -- can it */
15590795Sgshapiro			return panic;
15638032Speter		}
15738032Speter		state = ESM_POSTMASTER;
15838032Speter	}
15938032Speter
16038032Speter	while (state != ESM_DONE)
16138032Speter	{
16238032Speter		if (tTd(6, 5))
16390795Sgshapiro			sm_dprintf("  state %d\n", state);
16438032Speter
16538032Speter		switch (state)
16638032Speter		{
16738032Speter		  case ESM_QUIET:
16838032Speter			if (bitnset(M_LOCALMAILER, e->e_from.q_mailer->m_flags))
16938032Speter				state = ESM_DEADLETTER;
17038032Speter			else
17138032Speter				state = ESM_MAIL;
17238032Speter			break;
17338032Speter
17438032Speter		  case ESM_REPORT:
17538032Speter
17638032Speter			/*
17738032Speter			**  If the user is still logged in on the same terminal,
17838032Speter			**  then write the error messages back to hir (sic).
17938032Speter			*/
18038032Speter
181132946Sgshapiro#if USE_TTYPATH
18238032Speter			p = ttypath();
183363466Sgshapiro#else
184132946Sgshapiro			p = NULL;
185363466Sgshapiro#endif
186132946Sgshapiro
18790795Sgshapiro			if (p == NULL || sm_io_reopen(SmFtStdio,
18890795Sgshapiro						      SM_TIME_DEFAULT,
18990795Sgshapiro						      p, SM_IO_WRONLY, NULL,
19090795Sgshapiro						      smioout) == NULL)
19138032Speter			{
19238032Speter				state = ESM_MAIL;
19338032Speter				break;
19438032Speter			}
19538032Speter
196168520Sgshapiro			expand("\201n", buf, sizeof(buf), e);
19790795Sgshapiro			(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
19890795Sgshapiro					     "\r\nMessage from %s...\r\n", buf);
19990795Sgshapiro			(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
20090795Sgshapiro					     "Errors occurred while sending mail.\r\n");
20138032Speter			if (e->e_xfp != NULL)
20238032Speter			{
20364565Sgshapiro				(void) bfrewind(e->e_xfp);
20490795Sgshapiro				(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
20590795Sgshapiro						     "Transcript follows:\r\n");
20690795Sgshapiro				while (sm_io_fgets(e->e_xfp, SM_TIME_DEFAULT,
207249729Sgshapiro						   buf, sizeof(buf)) >= 0 &&
20890795Sgshapiro				       !sm_io_error(smioout))
20990795Sgshapiro					(void) sm_io_fputs(smioout,
21090795Sgshapiro							   SM_TIME_DEFAULT,
21190795Sgshapiro							   buf);
21238032Speter			}
21338032Speter			else
21438032Speter			{
21590795Sgshapiro				syserr("Cannot open %s",
21690795Sgshapiro				       queuename(e, XSCRPT_LETTER));
21790795Sgshapiro				(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
21890795Sgshapiro						     "Transcript of session is unavailable.\r\n");
21938032Speter			}
22090795Sgshapiro			(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
22190795Sgshapiro					     "Original message will be saved in dead.letter.\r\n");
22238032Speter			state = ESM_DEADLETTER;
22338032Speter			break;
22438032Speter
22538032Speter		  case ESM_MAIL:
22638032Speter			/*
22738032Speter			**  If mailing back, do it.
22838032Speter			**	Throw away all further output.  Don't alias,
22938032Speter			**	since this could cause loops, e.g., if joe
23038032Speter			**	mails to joe@x, and for some reason the network
23138032Speter			**	for @x is down, then the response gets sent to
23238032Speter			**	joe@x, which gives a response, etc.  Also force
23338032Speter			**	the mail to be delivered even if a version of
23438032Speter			**	it has already been sent to the sender.
23538032Speter			**
23638032Speter			**  If this is a configuration or local software
23738032Speter			**	error, send to the local postmaster as well,
23838032Speter			**	since the originator can't do anything
23938032Speter			**	about it anyway.  Note that this is a full
24038032Speter			**	copy of the message (intentionally) so that
24138032Speter			**	the Postmaster can forward things along.
24238032Speter			*/
24338032Speter
24438032Speter			if (ExitStat == EX_CONFIG || ExitStat == EX_SOFTWARE)
24538032Speter			{
24690795Sgshapiro				(void) sendtolist("postmaster", NULLADDR,
24790795Sgshapiro						  &e->e_errorqueue, 0, e);
24838032Speter			}
24938032Speter			if (!emptyaddr(&e->e_from))
25038032Speter			{
25138032Speter				char from[TOBUFSIZE];
25238032Speter
25390795Sgshapiro				if (sm_strlcpy(from, e->e_from.q_paddr,
254168520Sgshapiro						sizeof(from)) >= sizeof(from))
25538032Speter				{
25638032Speter					state = ESM_POSTMASTER;
25738032Speter					break;
25838032Speter				}
25938032Speter
26090795Sgshapiro				if (!DontPruneRoutes)
26190795Sgshapiro					(void) pruneroute(from);
26238032Speter
26338032Speter				(void) sendtolist(from, NULLADDR,
26438032Speter						  &e->e_errorqueue, 0, e);
26538032Speter			}
26638032Speter
26738032Speter			/*
26838032Speter			**  Deliver a non-delivery report to the
26938032Speter			**  Postmaster-designate (not necessarily
27038032Speter			**  Postmaster).  This does not include the
27138032Speter			**  body of the message, for privacy reasons.
27238032Speter			**  You really shouldn't need this.
27338032Speter			*/
27438032Speter
27538032Speter			e->e_flags |= EF_PM_NOTIFY;
27638032Speter
27738032Speter			/* check to see if there are any good addresses */
27838032Speter			for (q = e->e_errorqueue; q != NULL; q = q->q_next)
27964565Sgshapiro			{
28064565Sgshapiro				if (QS_IS_SENDABLE(q->q_state))
28138032Speter					break;
28264565Sgshapiro			}
28338032Speter			if (q == NULL)
28438032Speter			{
28538032Speter				/* this is an error-error */
28638032Speter				state = ESM_POSTMASTER;
28738032Speter				break;
28838032Speter			}
28938032Speter			if (returntosender(e->e_message, e->e_errorqueue,
29038032Speter					   sendbody ? RTSF_SEND_BODY
29138032Speter						    : RTSF_NO_BODY,
29238032Speter					   e) == 0)
29338032Speter			{
29438032Speter				state = ESM_DONE;
29538032Speter				break;
29638032Speter			}
29738032Speter
29838032Speter			/* didn't work -- return to postmaster */
29938032Speter			state = ESM_POSTMASTER;
30038032Speter			break;
30138032Speter
30238032Speter		  case ESM_POSTMASTER:
30338032Speter			/*
30438032Speter			**  Similar to previous case, but to system postmaster.
30538032Speter			*/
30638032Speter
30738032Speter			q = NULL;
308168520Sgshapiro			expand(DoubleBounceAddr, buf, sizeof(buf), e);
30990795Sgshapiro
31090795Sgshapiro			/*
31190795Sgshapiro			**  Just drop it on the floor if DoubleBounceAddr
31290795Sgshapiro			**  expands to an empty string.
31390795Sgshapiro			*/
31490795Sgshapiro
31590795Sgshapiro			if (*buf == '\0')
31690795Sgshapiro			{
31790795Sgshapiro				state = ESM_DONE;
31890795Sgshapiro				break;
31990795Sgshapiro			}
32064565Sgshapiro			if (sendtolist(buf, NULLADDR, &q, 0, e) <= 0)
32138032Speter			{
32264565Sgshapiro				syserr("553 5.3.0 cannot parse %s!", buf);
32338032Speter				ExitStat = EX_SOFTWARE;
32464565Sgshapiro				state = ESM_DEADLETTERDROP;
32538032Speter				break;
32638032Speter			}
32738032Speter			flags = RTSF_PM_BOUNCE;
32838032Speter			if (sendbody)
32938032Speter				flags |= RTSF_SEND_BODY;
33038032Speter			if (returntosender(e->e_message, q, flags, e) == 0)
33138032Speter			{
33238032Speter				state = ESM_DONE;
33338032Speter				break;
33438032Speter			}
33538032Speter
33638032Speter			/* didn't work -- last resort */
33764565Sgshapiro			state = ESM_DEADLETTERDROP;
33838032Speter			break;
33938032Speter
34038032Speter		  case ESM_DEADLETTER:
34138032Speter			/*
34238032Speter			**  Save the message in dead.letter.
34338032Speter			**	If we weren't mailing back, and the user is
34438032Speter			**	local, we should save the message in
34538032Speter			**	~/dead.letter so that the poor person doesn't
34638032Speter			**	have to type it over again -- and we all know
34738032Speter			**	what poor typists UNIX users are.
34838032Speter			*/
34938032Speter
35038032Speter			p = NULL;
35138032Speter			if (bitnset(M_HASPWENT, e->e_from.q_mailer->m_flags))
35238032Speter			{
35338032Speter				if (e->e_from.q_home != NULL)
35438032Speter					p = e->e_from.q_home;
35590795Sgshapiro				else if (sm_mbdb_lookup(e->e_from.q_user, &user)
35690795Sgshapiro					 == EX_OK &&
35790795Sgshapiro					 *user.mbdb_homedir != '\0')
35890795Sgshapiro					p = user.mbdb_homedir;
35938032Speter			}
36038032Speter			if (p == NULL || e->e_dfp == NULL)
36138032Speter			{
36238032Speter				/* no local directory or no data file */
36338032Speter				state = ESM_MAIL;
36438032Speter				break;
36538032Speter			}
36638032Speter
36738032Speter			/* we have a home directory; write dead.letter */
36890795Sgshapiro			macdefine(&e->e_macro, A_TEMP, 'z', p);
36964565Sgshapiro
37064565Sgshapiro			/* get the sender for the UnixFromLine */
37164565Sgshapiro			p = macvalue('g', e);
37290795Sgshapiro			macdefine(&e->e_macro, A_PERM, 'g', e->e_sender);
37364565Sgshapiro
374168520Sgshapiro			expand("\201z/dead.letter", dlbuf, sizeof(dlbuf), e);
37564565Sgshapiro			sff = SFF_CREAT|SFF_REGONLY|SFF_RUNASREALUID;
37638032Speter			if (RealUid == 0)
37764565Sgshapiro				sff |= SFF_ROOTOK;
37898125Sgshapiro			e->e_to = dlbuf;
37998125Sgshapiro			if (writable(dlbuf, NULL, sff) &&
38098125Sgshapiro			    mailfile(dlbuf, FileMailer, NULL, sff, e) == EX_OK)
38138032Speter			{
38238032Speter				int oldverb = Verbose;
38338032Speter
38464565Sgshapiro				if (OpMode != MD_DAEMON && OpMode != MD_SMTP)
38564565Sgshapiro					Verbose = 1;
38664565Sgshapiro				if (Verbose > 0)
38798125Sgshapiro					message("Saved message in %s", dlbuf);
38838032Speter				Verbose = oldverb;
38990795Sgshapiro				macdefine(&e->e_macro, A_PERM, 'g', p);
39038032Speter				state = ESM_DONE;
39138032Speter				break;
39238032Speter			}
39390795Sgshapiro			macdefine(&e->e_macro, A_PERM, 'g', p);
39438032Speter			state = ESM_MAIL;
39538032Speter			break;
39638032Speter
39764565Sgshapiro		  case ESM_DEADLETTERDROP:
39838032Speter			/*
39964565Sgshapiro			**  Log the mail in DeadLetterDrop file.
40038032Speter			*/
40138032Speter
40238032Speter			if (e->e_class < 0)
40338032Speter			{
40438032Speter				state = ESM_DONE;
40538032Speter				break;
40638032Speter			}
40738032Speter
40838032Speter			if ((SafeFileEnv != NULL && SafeFileEnv[0] != '\0') ||
40964565Sgshapiro			    DeadLetterDrop == NULL ||
41064565Sgshapiro			    DeadLetterDrop[0] == '\0')
41138032Speter			{
41238032Speter				state = ESM_PANIC;
41338032Speter				break;
41438032Speter			}
41538032Speter
41664565Sgshapiro			sff = SFF_CREAT|SFF_REGONLY|SFF_ROOTOK|SFF_OPENASROOT|SFF_MUSTOWN;
41764565Sgshapiro			if (!writable(DeadLetterDrop, NULL, sff) ||
41838032Speter			    (fp = safefopen(DeadLetterDrop, O_WRONLY|O_APPEND,
41964565Sgshapiro					    FileMode, sff)) == NULL)
42038032Speter			{
42138032Speter				state = ESM_PANIC;
42238032Speter				break;
42338032Speter			}
42438032Speter
425168520Sgshapiro			memset(&mcibuf, '\0', sizeof(mcibuf));
42638032Speter			mcibuf.mci_out = fp;
42738032Speter			mcibuf.mci_mailer = FileMailer;
42838032Speter			if (bitnset(M_7BITS, FileMailer->m_flags))
42938032Speter				mcibuf.mci_flags |= MCIF_7BIT;
43038032Speter
43164565Sgshapiro			/* get the sender for the UnixFromLine */
43264565Sgshapiro			p = macvalue('g', e);
43390795Sgshapiro			macdefine(&e->e_macro, A_PERM, 'g', e->e_sender);
43464565Sgshapiro
435157006Sgshapiro			if (!putfromline(&mcibuf, e) ||
436157006Sgshapiro			    !(*e->e_puthdr)(&mcibuf, e->e_header, e,
437157006Sgshapiro					M87F_OUTER) ||
438157006Sgshapiro			    !(*e->e_putbody)(&mcibuf, e, NULL) ||
439157006Sgshapiro			    !putline("\n", &mcibuf) ||
440157006Sgshapiro			    sm_io_flush(fp, SM_TIME_DEFAULT) == SM_IO_EOF ||
441157006Sgshapiro			    sm_io_error(fp) ||
44290795Sgshapiro			    sm_io_close(fp, SM_TIME_DEFAULT) < 0)
44338032Speter				state = ESM_PANIC;
44438032Speter			else
44538032Speter			{
44638032Speter				int oldverb = Verbose;
44738032Speter
44864565Sgshapiro				if (OpMode != MD_DAEMON && OpMode != MD_SMTP)
44964565Sgshapiro					Verbose = 1;
45064565Sgshapiro				if (Verbose > 0)
45164565Sgshapiro					message("Saved message in %s",
45264565Sgshapiro						DeadLetterDrop);
45338032Speter				Verbose = oldverb;
45438032Speter				if (LogLevel > 3)
45538032Speter					sm_syslog(LOG_NOTICE, e->e_id,
45664565Sgshapiro						  "Saved message in %s",
45764565Sgshapiro						  DeadLetterDrop);
45838032Speter				state = ESM_DONE;
45938032Speter			}
46090795Sgshapiro			macdefine(&e->e_macro, A_PERM, 'g', p);
46138032Speter			break;
46238032Speter
46338032Speter		  default:
46464565Sgshapiro			syserr("554 5.3.5 savemail: unknown state %d", state);
46564565Sgshapiro			/* FALLTHROUGH */
46638032Speter
46738032Speter		  case ESM_PANIC:
46838032Speter			/* leave the locked queue & transcript files around */
46938032Speter			loseqfile(e, "savemail panic");
47090795Sgshapiro			panic = true;
47166497Sgshapiro			errno = 0;
47290795Sgshapiro			syserr("554 savemail: cannot save rejected email anywhere");
47390795Sgshapiro			state = ESM_DONE;
47490795Sgshapiro			break;
47538032Speter		}
47638032Speter	}
47790795Sgshapiro	return panic;
47838032Speter}
47990795Sgshapiro/*
48038032Speter**  RETURNTOSENDER -- return a message to the sender with an error.
48138032Speter**
48238032Speter**	Parameters:
48338032Speter**		msg -- the explanatory message.
48438032Speter**		returnq -- the queue of people to send the message to.
48538032Speter**		flags -- flags tweaking the operation:
48638032Speter**			RTSF_SENDBODY -- include body of message (otherwise
48738032Speter**				just send the header).
48838032Speter**			RTSF_PMBOUNCE -- this is a postmaster bounce.
48938032Speter**		e -- the current envelope.
49038032Speter**
49138032Speter**	Returns:
49238032Speter**		zero -- if everything went ok.
49338032Speter**		else -- some error.
49438032Speter**
49538032Speter**	Side Effects:
49690795Sgshapiro**		Returns the current message to the sender via mail.
49738032Speter*/
49838032Speter
49938032Speter#define MAXRETURNS	6	/* max depth of returning messages */
50090795Sgshapiro#define ERRORFUDGE	1024	/* nominal size of error message text */
50138032Speter
50238032Speterint
50338032Speterreturntosender(msg, returnq, flags, e)
50438032Speter	char *msg;
50538032Speter	ADDRESS *returnq;
50638032Speter	int flags;
50738032Speter	register ENVELOPE *e;
50838032Speter{
509244833Sgshapiro	int ret;
51038032Speter	register ENVELOPE *ee;
51138032Speter	ENVELOPE *oldcur = CurEnv;
51238032Speter	ENVELOPE errenvelope;
51338032Speter	static int returndepth = 0;
51438032Speter	register ADDRESS *q;
51538032Speter	char *p;
51638032Speter	char buf[MAXNAME + 1];
51738032Speter
51838032Speter	if (returnq == NULL)
51964565Sgshapiro		return -1;
52038032Speter
52138032Speter	if (msg == NULL)
52238032Speter		msg = "Unable to deliver mail";
52338032Speter
52438032Speter	if (tTd(6, 1))
52538032Speter	{
52690795Sgshapiro		sm_dprintf("\n*** Return To Sender: msg=\"%s\", depth=%d, e=%p, returnq=",
527363466Sgshapiro			msg, returndepth, (void *)e);
528132946Sgshapiro		printaddr(sm_debug_file(), returnq, true);
52938032Speter		if (tTd(6, 20))
53038032Speter		{
53190795Sgshapiro			sm_dprintf("Sendq=");
532132946Sgshapiro			printaddr(sm_debug_file(), e->e_sendqueue, true);
53338032Speter		}
53438032Speter	}
53538032Speter
53638032Speter	if (++returndepth >= MAXRETURNS)
53738032Speter	{
53838032Speter		if (returndepth != MAXRETURNS)
53964565Sgshapiro			syserr("554 5.3.0 returntosender: infinite recursion on %s",
54064565Sgshapiro			       returnq->q_paddr);
54138032Speter		/* don't "unrecurse" and fake a clean exit */
54238032Speter		/* returndepth--; */
54364565Sgshapiro		return 0;
54438032Speter	}
54538032Speter
54690795Sgshapiro	macdefine(&e->e_macro, A_PERM, 'g', e->e_sender);
54790795Sgshapiro	macdefine(&e->e_macro, A_PERM, 'u', NULL);
54838032Speter
54938032Speter	/* initialize error envelope */
55090795Sgshapiro	ee = newenvelope(&errenvelope, e, sm_rpool_new_x(NULL));
55190795Sgshapiro	macdefine(&ee->e_macro, A_PERM, 'a', "\201b");
55290795Sgshapiro	macdefine(&ee->e_macro, A_PERM, 'r', "");
55390795Sgshapiro	macdefine(&ee->e_macro, A_PERM, 's', "localhost");
55490795Sgshapiro	macdefine(&ee->e_macro, A_PERM, '_', "localhost");
555110563Sgshapiro	clrsessenvelope(ee);
55664565Sgshapiro
55738032Speter	ee->e_puthdr = putheader;
55838032Speter	ee->e_putbody = errbody;
55938032Speter	ee->e_flags |= EF_RESPONSE|EF_METOO;
56038032Speter	if (!bitset(EF_OLDSTYLE, e->e_flags))
56138032Speter		ee->e_flags &= ~EF_OLDSTYLE;
56264565Sgshapiro	if (bitset(EF_DONT_MIME, e->e_flags))
56364565Sgshapiro	{
56464565Sgshapiro		ee->e_flags |= EF_DONT_MIME;
56564565Sgshapiro
56664565Sgshapiro		/*
56764565Sgshapiro		**  If we can't convert to MIME and we don't pass
56864565Sgshapiro		**  8-bit, we can't send the body.
56964565Sgshapiro		*/
57064565Sgshapiro
57164565Sgshapiro		if (bitset(EF_HAS8BIT, e->e_flags) &&
57264565Sgshapiro		    !bitset(MM_PASS8BIT, MimeMode))
57364565Sgshapiro			flags &= ~RTSF_SEND_BODY;
57464565Sgshapiro	}
57564565Sgshapiro
57638032Speter	ee->e_sendqueue = returnq;
57790795Sgshapiro	ee->e_msgsize = 0;
57864565Sgshapiro	if (bitset(RTSF_SEND_BODY, flags) &&
57964565Sgshapiro	    !bitset(PRIV_NOBODYRETN, PrivacyFlags))
58090795Sgshapiro		ee->e_msgsize = ERRORFUDGE + e->e_msgsize;
58138032Speter	else
58238032Speter		ee->e_flags |= EF_NO_BODY_RETN;
58390795Sgshapiro
584285229Sgshapiro#if _FFR_BOUNCE_QUEUE
585285229Sgshapiro	if (BounceQueue != NOQGRP)
586285229Sgshapiro		ee->e_qgrp = ee->e_dfqgrp = BounceQueue;
587363466Sgshapiro#endif
58890795Sgshapiro	if (!setnewqueue(ee))
58990795Sgshapiro	{
59090795Sgshapiro		syserr("554 5.3.0 returntosender: cannot select queue for %s",
59190795Sgshapiro			       returnq->q_paddr);
59290795Sgshapiro		ExitStat = EX_UNAVAILABLE;
59390795Sgshapiro		returndepth--;
59490795Sgshapiro		return -1;
59590795Sgshapiro	}
59638032Speter	initsys(ee);
59764565Sgshapiro
59864565Sgshapiro#if NAMED_BIND
59964565Sgshapiro	_res.retry = TimeOuts.res_retry[RES_TO_FIRST];
60064565Sgshapiro	_res.retrans = TimeOuts.res_retrans[RES_TO_FIRST];
601363466Sgshapiro#endif
60238032Speter	for (q = returnq; q != NULL; q = q->q_next)
60338032Speter	{
60464565Sgshapiro		if (QS_IS_BADADDR(q->q_state))
60538032Speter			continue;
60638032Speter
60738032Speter		q->q_flags &= ~(QHASNOTIFY|Q_PINGFLAGS);
60838032Speter		q->q_flags |= QPINGONFAILURE;
60938032Speter
61064565Sgshapiro		if (!QS_IS_DEAD(q->q_state))
61138032Speter			ee->e_nrcpts++;
61238032Speter
61338032Speter		if (q->q_alias == NULL)
614168520Sgshapiro			addheader("To", q->q_paddr, 0, ee, true);
61538032Speter	}
61638032Speter
61738032Speter	if (LogLevel > 5)
61838032Speter	{
61964565Sgshapiro		if (bitset(EF_RESPONSE, e->e_flags))
62038032Speter			p = "return to sender";
62164565Sgshapiro		else if (bitset(EF_WARNING, e->e_flags))
62264565Sgshapiro			p = "sender notify";
62338032Speter		else if (bitset(RTSF_PM_BOUNCE, flags))
62438032Speter			p = "postmaster notify";
62538032Speter		else
62638032Speter			p = "DSN";
62790795Sgshapiro		sm_syslog(LOG_INFO, e->e_id, "%s: %s: %s",
62864565Sgshapiro			  ee->e_id, p, shortenstring(msg, MAXSHORTSTR));
62938032Speter	}
63038032Speter
63138032Speter	if (SendMIMEErrors)
63238032Speter	{
633168520Sgshapiro		addheader("MIME-Version", "1.0", 0, ee, true);
634168520Sgshapiro		(void) sm_snprintf(buf, sizeof(buf), "%s.%ld/%.100s",
63590795Sgshapiro				ee->e_id, (long)curtime(), MyHostName);
63690795Sgshapiro		ee->e_msgboundary = sm_rpool_strdup_x(ee->e_rpool, buf);
637168520Sgshapiro		(void) sm_snprintf(buf, sizeof(buf),
63838032Speter#if DSN
63964565Sgshapiro				"multipart/report; report-type=delivery-status;\n\tboundary=\"%s\"",
640363466Sgshapiro#else
64164565Sgshapiro				"multipart/mixed; boundary=\"%s\"",
642363466Sgshapiro#endif
64364565Sgshapiro				ee->e_msgboundary);
644168520Sgshapiro		addheader("Content-Type", buf, 0, ee, true);
64538032Speter
64638032Speter		p = hvalue("Content-Transfer-Encoding", e->e_header);
64790795Sgshapiro		if (p != NULL && sm_strcasecmp(p, "binary") != 0)
64838032Speter			p = NULL;
64938032Speter		if (p == NULL && bitset(EF_HAS8BIT, e->e_flags))
65038032Speter			p = "8bit";
65138032Speter		if (p != NULL)
652168520Sgshapiro			addheader("Content-Transfer-Encoding", p, 0, ee, true);
65338032Speter	}
65438032Speter	if (strncmp(msg, "Warning:", 8) == 0)
65538032Speter	{
656168520Sgshapiro		addheader("Subject", msg, 0, ee, true);
65738032Speter		p = "warning-timeout";
65838032Speter	}
65938032Speter	else if (strncmp(msg, "Postmaster warning:", 19) == 0)
66038032Speter	{
661168520Sgshapiro		addheader("Subject", msg, 0, ee, true);
66238032Speter		p = "postmaster-warning";
66338032Speter	}
66438032Speter	else if (strcmp(msg, "Return receipt") == 0)
66538032Speter	{
666168520Sgshapiro		addheader("Subject", msg, 0, ee, true);
66738032Speter		p = "return-receipt";
66838032Speter	}
66938032Speter	else if (bitset(RTSF_PM_BOUNCE, flags))
67038032Speter	{
671168520Sgshapiro		(void) sm_snprintf(buf, sizeof(buf),
67264565Sgshapiro			 "Postmaster notify: see transcript for details");
673168520Sgshapiro		addheader("Subject", buf, 0, ee, true);
67438032Speter		p = "postmaster-notification";
67538032Speter	}
67638032Speter	else
67738032Speter	{
678168520Sgshapiro		(void) sm_snprintf(buf, sizeof(buf),
67964565Sgshapiro			 "Returned mail: see transcript for details");
680168520Sgshapiro		addheader("Subject", buf, 0, ee, true);
68138032Speter		p = "failure";
68238032Speter	}
683168520Sgshapiro	(void) sm_snprintf(buf, sizeof(buf), "auto-generated (%s)", p);
684168520Sgshapiro	addheader("Auto-Submitted", buf, 0, ee, true);
68538032Speter
68638032Speter	/* fake up an address header for the from person */
687168520Sgshapiro	expand("\201n", buf, sizeof(buf), e);
68864565Sgshapiro	if (parseaddr(buf, &ee->e_from,
68990795Sgshapiro		      RF_COPYALL|RF_SENDERADDR, '\0', NULL, e, false) == NULL)
69038032Speter	{
69164565Sgshapiro		syserr("553 5.3.5 Can't parse myself!");
69238032Speter		ExitStat = EX_SOFTWARE;
69338032Speter		returndepth--;
69464565Sgshapiro		return -1;
69538032Speter	}
69638032Speter	ee->e_from.q_flags &= ~(QHASNOTIFY|Q_PINGFLAGS);
69738032Speter	ee->e_from.q_flags |= QPINGONFAILURE;
69838032Speter	ee->e_sender = ee->e_from.q_paddr;
69938032Speter
70038032Speter	/* push state into submessage */
70138032Speter	CurEnv = ee;
70290795Sgshapiro	macdefine(&ee->e_macro, A_PERM, 'f', "\201n");
70390795Sgshapiro	macdefine(&ee->e_macro, A_PERM, 'x', "Mail Delivery Subsystem");
70490795Sgshapiro	eatheader(ee, true, true);
70538032Speter
70638032Speter	/* mark statistics */
70790795Sgshapiro	markstats(ee, NULLADDR, STATS_NORMAL);
70838032Speter
709285229Sgshapiro#if _FFR_BOUNCE_QUEUE
710285229Sgshapiro	if (BounceQueue == NOQGRP)
711285229Sgshapiro	{
712285229Sgshapiro#endif
713285229Sgshapiro		/* actually deliver the error message */
714285229Sgshapiro		sendall(ee, SM_DELIVER);
715285229Sgshapiro#if _FFR_BOUNCE_QUEUE
716285229Sgshapiro	}
717285229Sgshapiro#endif
718203004Sgshapiro	(void) dropenvelope(ee, true, false);
71938032Speter
72038032Speter	/* check for delivery errors */
721244833Sgshapiro	ret = -1;
72264565Sgshapiro	if (ee->e_parent == NULL ||
72364565Sgshapiro	    !bitset(EF_RESPONSE, ee->e_parent->e_flags))
72438032Speter	{
725244833Sgshapiro		ret = 0;
72638032Speter	}
727244833Sgshapiro	else
728244833Sgshapiro	{
729244833Sgshapiro		for (q = ee->e_sendqueue; q != NULL; q = q->q_next)
730244833Sgshapiro		{
731244833Sgshapiro			if (QS_IS_ATTEMPTED(q->q_state))
732244833Sgshapiro			{
733244833Sgshapiro				ret = 0;
734244833Sgshapiro				break;
735244833Sgshapiro			}
736244833Sgshapiro		}
737244833Sgshapiro	}
738244833Sgshapiro
739244833Sgshapiro	/* restore state */
740244833Sgshapiro	sm_rpool_free(ee->e_rpool);
741244833Sgshapiro	CurEnv = oldcur;
742244833Sgshapiro	returndepth--;
743244833Sgshapiro
744244833Sgshapiro	return ret;
74538032Speter}
746244833Sgshapiro
747363466Sgshapiro
74890795Sgshapiro/*
749363466Sgshapiro**  DSNTYPENAME -- Returns the DSN name of the addrtype for this address
750363466Sgshapiro**
751363466Sgshapiro**	Sendmail's addrtypes are largely in different universes, and
752363466Sgshapiro**	'fred' may be a valid address in different addrtype
753363466Sgshapiro**	universes.
754363466Sgshapiro**
755363466Sgshapiro**	EAI extends the rfc822 universe rather than introduce a new
756363466Sgshapiro**	universe.  Because of that, sendmail uses the rfc822 addrtype,
757363466Sgshapiro**	but names it utf-8 when the EAI DSN extension requires that.
758363466Sgshapiro**
759363466Sgshapiro**	Parameters:
760363466Sgshapiro**		addrtype -- address type
761363466Sgshapiro**		addr -- the address
762363466Sgshapiro**
763363466Sgshapiro**	Returns:
764363466Sgshapiro**		type for DSN
765363466Sgshapiro**
766363466Sgshapiro*/
767363466Sgshapiro
768363466Sgshapirostatic const char *dsntypename __P((const char *, const char *));
769363466Sgshapiro
770363466Sgshapirostatic const char *
771363466Sgshapirodsntypename(addrtype, addr)
772363466Sgshapiro	const char *addrtype;
773363466Sgshapiro	const char *addr;
774363466Sgshapiro{
775363466Sgshapiro	if (sm_strcasecmp(addrtype, "rfc822") != 0)
776363466Sgshapiro		return addrtype;
777363466Sgshapiro#if _FFR_EAI
778363466Sgshapiro	if (!addr_is_ascii(addr))
779363466Sgshapiro		return "utf-8";
780363466Sgshapiro#endif
781363466Sgshapiro	return "rfc822";
782363466Sgshapiro}
783363466Sgshapiro
784363466Sgshapiro
785363466Sgshapiro/*
78638032Speter**  ERRBODY -- output the body of an error message.
78738032Speter**
78838032Speter**	Typically this is a copy of the transcript plus a copy of the
78938032Speter**	original offending message.
79038032Speter**
79138032Speter**	Parameters:
79238032Speter**		mci -- the mailer connection information.
79338032Speter**		e -- the envelope we are working in.
79490795Sgshapiro**		separator -- any possible MIME separator (unused).
79538032Speter**
79638032Speter**	Returns:
797159613Sgshapiro**		true iff body was written successfully
79838032Speter**
79938032Speter**	Side Effects:
80038032Speter**		Outputs the body of an error message.
80138032Speter*/
80238032Speter
80364565Sgshapiro/* ARGSUSED2 */
804157006Sgshapirostatic bool
80538032Spetererrbody(mci, e, separator)
80638032Speter	register MCI *mci;
80738032Speter	register ENVELOPE *e;
80838032Speter	char *separator;
80938032Speter{
81064565Sgshapiro	bool printheader;
81164565Sgshapiro	bool sendbody;
81264565Sgshapiro	bool pm_notify;
81364565Sgshapiro	int save_errno;
81490795Sgshapiro	register SM_FILE_T *xfile;
81538032Speter	char *p;
81638032Speter	register ADDRESS *q = NULL;
81790795Sgshapiro	char actual[MAXLINE];
81838032Speter	char buf[MAXLINE];
81938032Speter
82038032Speter	if (bitset(MCIF_INHEADER, mci->mci_flags))
82138032Speter	{
822157006Sgshapiro		if (!putline("", mci))
823157006Sgshapiro			goto writeerr;
82438032Speter		mci->mci_flags &= ~MCIF_INHEADER;
82538032Speter	}
82638032Speter	if (e->e_parent == NULL)
82738032Speter	{
82838032Speter		syserr("errbody: null parent");
829157006Sgshapiro		if (!putline("   ----- Original message lost -----\n", mci))
830157006Sgshapiro			goto writeerr;
831157006Sgshapiro		return true;
83238032Speter	}
83338032Speter
83438032Speter	/*
83538032Speter	**  Output MIME header.
83638032Speter	*/
83738032Speter
83838032Speter	if (e->e_msgboundary != NULL)
83938032Speter	{
840168520Sgshapiro		(void) sm_strlcpyn(buf, sizeof(buf), 2, "--", e->e_msgboundary);
841157006Sgshapiro		if (!putline("This is a MIME-encapsulated message", mci) ||
842157006Sgshapiro		    !putline("", mci) ||
843157006Sgshapiro		    !putline(buf, mci) ||
844157006Sgshapiro		    !putline("", mci))
845157006Sgshapiro			goto writeerr;
84638032Speter	}
84738032Speter
84838032Speter	/*
84938032Speter	**  Output introductory information.
85038032Speter	*/
85138032Speter
85290795Sgshapiro	pm_notify = false;
85338032Speter	p = hvalue("subject", e->e_header);
85438032Speter	if (p != NULL && strncmp(p, "Postmaster ", 11) == 0)
85590795Sgshapiro		pm_notify = true;
85638032Speter	else
85738032Speter	{
85838032Speter		for (q = e->e_parent->e_sendqueue; q != NULL; q = q->q_next)
85964565Sgshapiro		{
86064565Sgshapiro			if (QS_IS_BADADDR(q->q_state))
86138032Speter				break;
86264565Sgshapiro		}
86338032Speter	}
86438032Speter	if (!pm_notify && q == NULL &&
86538032Speter	    !bitset(EF_FATALERRS|EF_SENDRECEIPT, e->e_parent->e_flags))
86638032Speter	{
867157006Sgshapiro		if (!putline("    **********************************************",
868157006Sgshapiro			mci) ||
869157006Sgshapiro		    !putline("    **      THIS IS A WARNING MESSAGE ONLY      **",
870157006Sgshapiro			mci) ||
871157006Sgshapiro		    !putline("    **  YOU DO NOT NEED TO RESEND YOUR MESSAGE  **",
872157006Sgshapiro			mci) ||
873157006Sgshapiro		    !putline("    **********************************************",
874157006Sgshapiro			mci) ||
875157006Sgshapiro		    !putline("", mci))
876157006Sgshapiro			goto writeerr;
87738032Speter	}
878168520Sgshapiro	(void) sm_snprintf(buf, sizeof(buf),
87990795Sgshapiro		"The original message was received at %s",
88090795Sgshapiro		arpadate(ctime(&e->e_parent->e_ctime)));
881157006Sgshapiro	if (!putline(buf, mci))
882157006Sgshapiro		goto writeerr;
883168520Sgshapiro	expand("from \201_", buf, sizeof(buf), e->e_parent);
884157006Sgshapiro	if (!putline(buf, mci))
885157006Sgshapiro		goto writeerr;
88664565Sgshapiro
88764565Sgshapiro	/* include id in postmaster copies */
88864565Sgshapiro	if (pm_notify && e->e_parent->e_id != NULL)
88964565Sgshapiro	{
890168520Sgshapiro		(void) sm_strlcpyn(buf, sizeof(buf), 2, "with id ",
89190795Sgshapiro			e->e_parent->e_id);
892157006Sgshapiro		if (!putline(buf, mci))
893157006Sgshapiro			goto writeerr;
89464565Sgshapiro	}
895157006Sgshapiro	if (!putline("", mci))
896157006Sgshapiro		goto writeerr;
89738032Speter
89838032Speter	/*
89938032Speter	**  Output error message header (if specified and available).
90038032Speter	*/
90138032Speter
90264565Sgshapiro	if (ErrMsgFile != NULL &&
90364565Sgshapiro	    !bitset(EF_SENDRECEIPT, e->e_parent->e_flags))
90438032Speter	{
90538032Speter		if (*ErrMsgFile == '/')
90638032Speter		{
90764565Sgshapiro			long sff = SFF_ROOTOK|SFF_REGONLY;
90838032Speter
90938032Speter			if (DontLockReadFiles)
91038032Speter				sff |= SFF_NOLOCK;
91164565Sgshapiro			if (!bitnset(DBS_ERRORHEADERINUNSAFEDIRPATH,
91264565Sgshapiro				     DontBlameSendmail))
91338032Speter				sff |= SFF_SAFEDIRPATH;
91438032Speter			xfile = safefopen(ErrMsgFile, O_RDONLY, 0444, sff);
91538032Speter			if (xfile != NULL)
91638032Speter			{
91790795Sgshapiro				while (sm_io_fgets(xfile, SM_TIME_DEFAULT, buf,
918249729Sgshapiro						   sizeof(buf)) >= 0)
91938032Speter				{
920168520Sgshapiro					int lbs;
921168520Sgshapiro					bool putok;
922168520Sgshapiro					char *lbp;
923168520Sgshapiro
924168520Sgshapiro					lbs = sizeof(buf);
925168520Sgshapiro					lbp = translate_dollars(buf, buf, &lbs);
926168520Sgshapiro					expand(lbp, lbp, lbs, e);
927168520Sgshapiro					putok = putline(lbp, mci);
928168520Sgshapiro					if (lbp != buf)
929168520Sgshapiro						sm_free(lbp);
930168520Sgshapiro					if (!putok)
931157006Sgshapiro						goto writeerr;
93238032Speter				}
93390795Sgshapiro				(void) sm_io_close(xfile, SM_TIME_DEFAULT);
934157006Sgshapiro				if (!putline("\n", mci))
935157006Sgshapiro					goto writeerr;
93638032Speter			}
93738032Speter		}
93838032Speter		else
93938032Speter		{
940168520Sgshapiro			expand(ErrMsgFile, buf, sizeof(buf), e);
941157006Sgshapiro			if (!putline(buf, mci) || !putline("", mci))
942157006Sgshapiro				goto writeerr;
94338032Speter		}
94438032Speter	}
94538032Speter
94638032Speter	/*
94738032Speter	**  Output message introduction
94838032Speter	*/
94938032Speter
95090795Sgshapiro	/* permanent fatal errors */
95190795Sgshapiro	printheader = true;
95238032Speter	for (q = e->e_parent->e_sendqueue; q != NULL; q = q->q_next)
95338032Speter	{
95464565Sgshapiro		if (!QS_IS_BADADDR(q->q_state) ||
95538032Speter		    !bitset(QPINGONFAILURE, q->q_flags))
95638032Speter			continue;
95738032Speter
95838032Speter		if (printheader)
95938032Speter		{
960157006Sgshapiro			if (!putline("   ----- The following addresses had permanent fatal errors -----",
961157006Sgshapiro					mci))
962157006Sgshapiro				goto writeerr;
96390795Sgshapiro			printheader = false;
96438032Speter		}
96538032Speter
96690795Sgshapiro		(void) sm_strlcpy(buf, shortenstring(q->q_paddr, MAXSHORTSTR),
967168520Sgshapiro				  sizeof(buf));
968157006Sgshapiro		if (!putline(buf, mci))
969157006Sgshapiro			goto writeerr;
97064565Sgshapiro		if (q->q_rstatus != NULL)
97164565Sgshapiro		{
972168520Sgshapiro			(void) sm_snprintf(buf, sizeof(buf),
97390795Sgshapiro				"    (reason: %s)",
97490795Sgshapiro				shortenstring(exitstat(q->q_rstatus),
97590795Sgshapiro					      MAXSHORTSTR));
976157006Sgshapiro			if (!putline(buf, mci))
977157006Sgshapiro				goto writeerr;
97864565Sgshapiro		}
97938032Speter		if (q->q_alias != NULL)
98038032Speter		{
981168520Sgshapiro			(void) sm_snprintf(buf, sizeof(buf),
98290795Sgshapiro				"    (expanded from: %s)",
98390795Sgshapiro				shortenstring(q->q_alias->q_paddr,
98490795Sgshapiro					      MAXSHORTSTR));
985157006Sgshapiro			if (!putline(buf, mci))
986157006Sgshapiro				goto writeerr;
98738032Speter		}
98838032Speter	}
989157006Sgshapiro	if (!printheader && !putline("", mci))
990157006Sgshapiro		goto writeerr;
99138032Speter
99290795Sgshapiro	/* transient non-fatal errors */
99390795Sgshapiro	printheader = true;
99438032Speter	for (q = e->e_parent->e_sendqueue; q != NULL; q = q->q_next)
99538032Speter	{
99664565Sgshapiro		if (QS_IS_BADADDR(q->q_state) ||
99738032Speter		    !bitset(QPRIMARY, q->q_flags) ||
99890795Sgshapiro		    !bitset(QBYNDELAY, q->q_flags) ||
99938032Speter		    !bitset(QDELAYED, q->q_flags))
100038032Speter			continue;
100138032Speter
100238032Speter		if (printheader)
100338032Speter		{
1004157006Sgshapiro			if (!putline("   ----- The following addresses had transient non-fatal errors -----",
1005157006Sgshapiro					mci))
1006157006Sgshapiro				goto writeerr;
100790795Sgshapiro			printheader = false;
100838032Speter		}
100938032Speter
101090795Sgshapiro		(void) sm_strlcpy(buf, shortenstring(q->q_paddr, MAXSHORTSTR),
1011168520Sgshapiro				  sizeof(buf));
1012157006Sgshapiro		if (!putline(buf, mci))
1013157006Sgshapiro			goto writeerr;
101438032Speter		if (q->q_alias != NULL)
101538032Speter		{
1016168520Sgshapiro			(void) sm_snprintf(buf, sizeof(buf),
101790795Sgshapiro				"    (expanded from: %s)",
101890795Sgshapiro				shortenstring(q->q_alias->q_paddr,
101990795Sgshapiro					      MAXSHORTSTR));
1020157006Sgshapiro			if (!putline(buf, mci))
1021157006Sgshapiro				goto writeerr;
102238032Speter		}
102338032Speter	}
1024157006Sgshapiro	if (!printheader && !putline("", mci))
1025157006Sgshapiro		goto writeerr;
102638032Speter
102790795Sgshapiro	/* successful delivery notifications */
102890795Sgshapiro	printheader = true;
102938032Speter	for (q = e->e_parent->e_sendqueue; q != NULL; q = q->q_next)
103038032Speter	{
103164565Sgshapiro		if (QS_IS_BADADDR(q->q_state) ||
103238032Speter		    !bitset(QPRIMARY, q->q_flags) ||
103390795Sgshapiro		    bitset(QBYNDELAY, q->q_flags) ||
103438032Speter		    bitset(QDELAYED, q->q_flags))
103538032Speter			continue;
103690795Sgshapiro		else if (bitset(QBYNRELAY, q->q_flags))
103790795Sgshapiro			p = "Deliver-By notify: relayed";
103890795Sgshapiro		else if (bitset(QBYTRACE, q->q_flags))
103990795Sgshapiro			p = "Deliver-By trace: relayed";
104038032Speter		else if (!bitset(QPINGONSUCCESS, q->q_flags))
104138032Speter			continue;
104238032Speter		else if (bitset(QRELAYED, q->q_flags))
104338032Speter			p = "relayed to non-DSN-aware mailer";
104438032Speter		else if (bitset(QDELIVERED, q->q_flags))
104538032Speter		{
104638032Speter			if (bitset(QEXPANDED, q->q_flags))
104738032Speter				p = "successfully delivered to mailing list";
104838032Speter			else
104938032Speter				p = "successfully delivered to mailbox";
105038032Speter		}
105138032Speter		else if (bitset(QEXPANDED, q->q_flags))
105238032Speter			p = "expanded by alias";
105338032Speter		else
105438032Speter			continue;
105538032Speter
105638032Speter		if (printheader)
105738032Speter		{
1058157006Sgshapiro			if (!putline("   ----- The following addresses had successful delivery notifications -----",
1059157006Sgshapiro					mci))
1060157006Sgshapiro				goto writeerr;
106190795Sgshapiro			printheader = false;
106238032Speter		}
106338032Speter
1064168520Sgshapiro		(void) sm_snprintf(buf, sizeof(buf), "%s  (%s)",
106564565Sgshapiro			 shortenstring(q->q_paddr, MAXSHORTSTR), p);
1066157006Sgshapiro		if (!putline(buf, mci))
1067157006Sgshapiro			goto writeerr;
106838032Speter		if (q->q_alias != NULL)
106938032Speter		{
1070168520Sgshapiro			(void) sm_snprintf(buf, sizeof(buf),
107190795Sgshapiro				"    (expanded from: %s)",
107290795Sgshapiro				shortenstring(q->q_alias->q_paddr,
107390795Sgshapiro					      MAXSHORTSTR));
1074157006Sgshapiro			if (!putline(buf, mci))
1075157006Sgshapiro				goto writeerr;
107638032Speter		}
107738032Speter	}
1078157006Sgshapiro	if (!printheader && !putline("", mci))
1079157006Sgshapiro		goto writeerr;
108038032Speter
108138032Speter	/*
108238032Speter	**  Output transcript of errors
108338032Speter	*/
108438032Speter
108590795Sgshapiro	(void) sm_io_flush(smioout, SM_TIME_DEFAULT);
108664565Sgshapiro	if (e->e_parent->e_xfp == NULL)
108738032Speter	{
1088157006Sgshapiro		if (!putline("   ----- Transcript of session is unavailable -----\n",
1089157006Sgshapiro				mci))
1090157006Sgshapiro			goto writeerr;
109138032Speter	}
109238032Speter	else
109338032Speter	{
1094249729Sgshapiro		int blen;
1095249729Sgshapiro
109690795Sgshapiro		printheader = true;
109764565Sgshapiro		(void) bfrewind(e->e_parent->e_xfp);
109838032Speter		if (e->e_xfp != NULL)
109990795Sgshapiro			(void) sm_io_flush(e->e_xfp, SM_TIME_DEFAULT);
1100249729Sgshapiro		while ((blen = sm_io_fgets(e->e_parent->e_xfp, SM_TIME_DEFAULT,
1101249729Sgshapiro					buf, sizeof(buf))) >= 0)
110238032Speter		{
1103157006Sgshapiro			if (printheader && !putline("   ----- Transcript of session follows -----\n",
1104157006Sgshapiro						mci))
1105157006Sgshapiro				goto writeerr;
110690795Sgshapiro			printheader = false;
1107249729Sgshapiro			if (!putxline(buf, blen, mci, PXLF_MAPFROM))
1108157006Sgshapiro				goto writeerr;
110938032Speter		}
111038032Speter	}
111138032Speter	errno = 0;
111238032Speter
111338032Speter#if DSN
111438032Speter	/*
111538032Speter	**  Output machine-readable version.
111638032Speter	*/
111738032Speter
111838032Speter	if (e->e_msgboundary != NULL)
111938032Speter	{
1120168520Sgshapiro		(void) sm_strlcpyn(buf, sizeof(buf), 2, "--", e->e_msgboundary);
1121157006Sgshapiro		if (!putline("", mci) ||
1122157006Sgshapiro		    !putline(buf, mci) ||
1123363466Sgshapiro#if _FFR_EAI
1124363466Sgshapiro		    !putline(e->e_parent->e_smtputf8
1125363466Sgshapiro			     ? "Content-Type: message/global-delivery-status"
1126363466Sgshapiro			     : "Content-Type: message/delivery-status", mci) ||
1127363466Sgshapiro#else
1128157006Sgshapiro		    !putline("Content-Type: message/delivery-status", mci) ||
1129363466Sgshapiro#endif
1130157006Sgshapiro		    !putline("", mci))
1131157006Sgshapiro			goto writeerr;
113238032Speter
113338032Speter		/*
113438032Speter		**  Output per-message information.
113538032Speter		*/
113638032Speter
113738032Speter		/* original envelope id from MAIL FROM: line */
113838032Speter		if (e->e_parent->e_envid != NULL)
113938032Speter		{
1140168520Sgshapiro			(void) sm_snprintf(buf, sizeof(buf),
114164565Sgshapiro					"Original-Envelope-Id: %.800s",
114264565Sgshapiro					xuntextify(e->e_parent->e_envid));
1143157006Sgshapiro			if (!putline(buf, mci))
1144157006Sgshapiro				goto writeerr;
114538032Speter		}
114638032Speter
114738032Speter		/* Reporting-MTA: is us (required) */
1148168520Sgshapiro		(void) sm_snprintf(buf, sizeof(buf),
114990795Sgshapiro				   "Reporting-MTA: dns; %.800s", MyHostName);
1150157006Sgshapiro		if (!putline(buf, mci))
1151157006Sgshapiro			goto writeerr;
115238032Speter
115338032Speter		/* DSN-Gateway: not relevant since we are not translating */
115438032Speter
115538032Speter		/* Received-From-MTA: shows where we got this message from */
115638032Speter		if (RealHostName != NULL)
115738032Speter		{
115838032Speter			/* XXX use $s for type? */
115938032Speter			if (e->e_parent->e_from.q_mailer == NULL ||
116038032Speter			    (p = e->e_parent->e_from.q_mailer->m_mtatype) == NULL)
116138032Speter				p = "dns";
1162168520Sgshapiro			(void) sm_snprintf(buf, sizeof(buf),
116364565Sgshapiro					"Received-From-MTA: %s; %.800s",
116464565Sgshapiro					p, RealHostName);
1165157006Sgshapiro			if (!putline(buf, mci))
1166157006Sgshapiro				goto writeerr;
116738032Speter		}
116838032Speter
116938032Speter		/* Arrival-Date: -- when it arrived here */
1170168520Sgshapiro		(void) sm_strlcpyn(buf, sizeof(buf), 2, "Arrival-Date: ",
117164565Sgshapiro				arpadate(ctime(&e->e_parent->e_ctime)));
1172157006Sgshapiro		if (!putline(buf, mci))
1173157006Sgshapiro			goto writeerr;
117438032Speter
117590795Sgshapiro		/* Deliver-By-Date: -- when it should have been delivered */
117690795Sgshapiro		if (IS_DLVR_BY(e->e_parent))
117790795Sgshapiro		{
117890795Sgshapiro			time_t dbyd;
117990795Sgshapiro
118090795Sgshapiro			dbyd = e->e_parent->e_ctime + e->e_parent->e_deliver_by;
1181168520Sgshapiro			(void) sm_strlcpyn(buf, sizeof(buf), 2,
118290795Sgshapiro					"Deliver-By-Date: ",
118390795Sgshapiro					arpadate(ctime(&dbyd)));
1184157006Sgshapiro			if (!putline(buf, mci))
1185157006Sgshapiro				goto writeerr;
118690795Sgshapiro		}
118790795Sgshapiro
118838032Speter		/*
118938032Speter		**  Output per-address information.
119038032Speter		*/
119138032Speter
119238032Speter		for (q = e->e_parent->e_sendqueue; q != NULL; q = q->q_next)
119338032Speter		{
119438032Speter			char *action;
119538032Speter
119664565Sgshapiro			if (QS_IS_BADADDR(q->q_state))
119771348Sgshapiro			{
119871348Sgshapiro				/* RFC 1891, 6.2.6 (b) */
119971348Sgshapiro				if (bitset(QHASNOTIFY, q->q_flags) &&
120071348Sgshapiro				    !bitset(QPINGONFAILURE, q->q_flags))
120171348Sgshapiro					continue;
120238032Speter				action = "failed";
120371348Sgshapiro			}
120438032Speter			else if (!bitset(QPRIMARY, q->q_flags))
120538032Speter				continue;
120638032Speter			else if (bitset(QDELIVERED, q->q_flags))
120738032Speter			{
120838032Speter				if (bitset(QEXPANDED, q->q_flags))
120938032Speter					action = "delivered (to mailing list)";
121038032Speter				else
121138032Speter					action = "delivered (to mailbox)";
121238032Speter			}
121338032Speter			else if (bitset(QRELAYED, q->q_flags))
121438032Speter				action = "relayed (to non-DSN-aware mailer)";
121538032Speter			else if (bitset(QEXPANDED, q->q_flags))
121638032Speter				action = "expanded (to multi-recipient alias)";
121738032Speter			else if (bitset(QDELAYED, q->q_flags))
121838032Speter				action = "delayed";
121990795Sgshapiro			else if (bitset(QBYTRACE, q->q_flags))
122090795Sgshapiro				action = "relayed (Deliver-By trace mode)";
122190795Sgshapiro			else if (bitset(QBYNDELAY, q->q_flags))
122290795Sgshapiro				action = "delayed (Deliver-By notify mode)";
122390795Sgshapiro			else if (bitset(QBYNRELAY, q->q_flags))
122490795Sgshapiro				action = "relayed (Deliver-By notify mode)";
122538032Speter			else
122638032Speter				continue;
122738032Speter
1228157006Sgshapiro			if (!putline("", mci))
1229157006Sgshapiro				goto writeerr;
123038032Speter
123138032Speter			/* Original-Recipient: -- passed from on high */
123238032Speter			if (q->q_orcpt != NULL)
123338032Speter			{
1234249729Sgshapiro				p = strchr(q->q_orcpt, ';');
1235249729Sgshapiro
1236249729Sgshapiro				/*
1237249729Sgshapiro				**  p == NULL shouldn't happen due to
1238249729Sgshapiro				**  check in srvrsmtp.c
1239249729Sgshapiro				**  we could log an error in this case.
1240249729Sgshapiro				*/
1241249729Sgshapiro
1242249729Sgshapiro				if (p != NULL)
1243249729Sgshapiro				{
1244249729Sgshapiro					*p = '\0';
1245249729Sgshapiro					(void) sm_snprintf(buf, sizeof(buf),
1246249729Sgshapiro						"Original-Recipient: %.100s;%.700s",
1247249729Sgshapiro						q->q_orcpt, xuntextify(p + 1));
1248249729Sgshapiro					*p = ';';
1249249729Sgshapiro					if (!putline(buf, mci))
1250249729Sgshapiro						goto writeerr;
1251249729Sgshapiro				}
125238032Speter			}
125338032Speter
125490795Sgshapiro			/* Figure out actual recipient */
125590795Sgshapiro			actual[0] = '\0';
125690795Sgshapiro			if (q->q_user[0] != '\0')
125738032Speter			{
125864565Sgshapiro				if (q->q_mailer != NULL &&
125964565Sgshapiro				    q->q_mailer->m_addrtype != NULL)
126064565Sgshapiro					p = q->q_mailer->m_addrtype;
126164565Sgshapiro				else
126264565Sgshapiro					p = "rfc822";
126364565Sgshapiro
126490795Sgshapiro				if (sm_strcasecmp(p, "rfc822") == 0 &&
126564565Sgshapiro				    strchr(q->q_user, '@') == NULL)
126638032Speter				{
126790795Sgshapiro					(void) sm_snprintf(actual,
1268168520Sgshapiro							   sizeof(actual),
126990795Sgshapiro							   "%s; %.700s@%.100s",
1270363466Sgshapiro							   dsntypename(p, q->q_user),
1271363466Sgshapiro							   q->q_user,
127290795Sgshapiro							   MyHostName);
127338032Speter				}
127438032Speter				else
127538032Speter				{
127690795Sgshapiro					(void) sm_snprintf(actual,
1277168520Sgshapiro							   sizeof(actual),
127890795Sgshapiro							   "%s; %.800s",
1279363466Sgshapiro							   dsntypename(p, q->q_user),
1280363466Sgshapiro							   q->q_user);
128138032Speter				}
128290795Sgshapiro			}
128390795Sgshapiro
128490795Sgshapiro			/* Final-Recipient: -- the name from the RCPT command */
128590795Sgshapiro			if (q->q_finalrcpt == NULL)
128690795Sgshapiro			{
128790795Sgshapiro				/* should never happen */
128890795Sgshapiro				sm_syslog(LOG_ERR, e->e_id,
128990795Sgshapiro					  "returntosender: q_finalrcpt is NULL");
129090795Sgshapiro
129190795Sgshapiro				/* try to fall back to the actual recipient */
129290795Sgshapiro				if (actual[0] != '\0')
129390795Sgshapiro					q->q_finalrcpt = sm_rpool_strdup_x(e->e_rpool,
129490795Sgshapiro									   actual);
129590795Sgshapiro			}
129690795Sgshapiro
1297363466Sgshapiro#if _FFR_EAI
1298363466Sgshapiro			if (sm_strncasecmp("rfc822;", q->q_finalrcpt, 7) == 0 &&
1299363466Sgshapiro			    !addr_is_ascii(q->q_user))
1300363466Sgshapiro			{
1301363466Sgshapiro				char *a;
1302363466Sgshapiro				char utf8rcpt[1024];
1303363466Sgshapiro
1304363466Sgshapiro				a = strchr(q->q_finalrcpt, ';');
1305363466Sgshapiro				while(*a == ';' || *a == ' ')
1306363466Sgshapiro					a++;
1307363466Sgshapiro				sm_snprintf(utf8rcpt, sizeof(utf8rcpt),
1308363466Sgshapiro					    "utf-8; %.800s", a);
1309363466Sgshapiro				q->q_finalrcpt = sm_rpool_strdup_x(e->e_rpool,
1310363466Sgshapiro								   utf8rcpt);
1311363466Sgshapiro			}
1312363466Sgshapiro#endif
1313363466Sgshapiro
131490795Sgshapiro			if (q->q_finalrcpt != NULL)
131590795Sgshapiro			{
1316168520Sgshapiro				(void) sm_snprintf(buf, sizeof(buf),
131790795Sgshapiro						   "Final-Recipient: %s",
131890795Sgshapiro						   q->q_finalrcpt);
1319157006Sgshapiro				if (!putline(buf, mci))
1320157006Sgshapiro					goto writeerr;
132138032Speter			}
132238032Speter
132390795Sgshapiro			/* X-Actual-Recipient: -- the real problem address */
132490795Sgshapiro			if (actual[0] != '\0' &&
132590795Sgshapiro			    q->q_finalrcpt != NULL &&
1326141862Sgshapiro			    !bitset(PRIV_NOACTUALRECIPIENT, PrivacyFlags) &&
132790795Sgshapiro			    strcmp(actual, q->q_finalrcpt) != 0)
132890795Sgshapiro			{
1329168520Sgshapiro				(void) sm_snprintf(buf, sizeof(buf),
133090795Sgshapiro						   "X-Actual-Recipient: %s",
133190795Sgshapiro						   actual);
1332157006Sgshapiro				if (!putline(buf, mci))
1333157006Sgshapiro					goto writeerr;
133490795Sgshapiro			}
133590795Sgshapiro
133638032Speter			/* Action: -- what happened? */
1337168520Sgshapiro			(void) sm_strlcpyn(buf, sizeof(buf), 2, "Action: ",
133890795Sgshapiro				action);
1339157006Sgshapiro			if (!putline(buf, mci))
1340157006Sgshapiro				goto writeerr;
134138032Speter
134238032Speter			/* Status: -- what _really_ happened? */
134338032Speter			if (q->q_status != NULL)
134438032Speter				p = q->q_status;
134564565Sgshapiro			else if (QS_IS_BADADDR(q->q_state))
134638032Speter				p = "5.0.0";
134764565Sgshapiro			else if (QS_IS_QUEUEUP(q->q_state))
134838032Speter				p = "4.0.0";
134938032Speter			else
135038032Speter				p = "2.0.0";
1351168520Sgshapiro			(void) sm_strlcpyn(buf, sizeof(buf), 2, "Status: ", p);
1352157006Sgshapiro			if (!putline(buf, mci))
1353157006Sgshapiro				goto writeerr;
135438032Speter
135538032Speter			/* Remote-MTA: -- who was I talking to? */
135638032Speter			if (q->q_statmta != NULL)
135738032Speter			{
135838032Speter				if (q->q_mailer == NULL ||
135938032Speter				    (p = q->q_mailer->m_mtatype) == NULL)
136038032Speter					p = "dns";
1361168520Sgshapiro				(void) sm_snprintf(buf, sizeof(buf),
136264565Sgshapiro						"Remote-MTA: %s; %.800s",
136364565Sgshapiro						p, q->q_statmta);
136438032Speter				p = &buf[strlen(buf) - 1];
136538032Speter				if (*p == '.')
136638032Speter					*p = '\0';
1367157006Sgshapiro				if (!putline(buf, mci))
1368157006Sgshapiro					goto writeerr;
136938032Speter			}
137038032Speter
137138032Speter			/* Diagnostic-Code: -- actual result from other end */
137238032Speter			if (q->q_rstatus != NULL)
137338032Speter			{
1374159613Sgshapiro				if (q->q_mailer == NULL ||
1375159613Sgshapiro				    (p = q->q_mailer->m_diagtype) == NULL)
137638032Speter					p = "smtp";
1377168520Sgshapiro				(void) sm_snprintf(buf, sizeof(buf),
137864565Sgshapiro						"Diagnostic-Code: %s; %.800s",
137964565Sgshapiro						p, q->q_rstatus);
1380157006Sgshapiro				if (!putline(buf, mci))
1381157006Sgshapiro					goto writeerr;
138238032Speter			}
138338032Speter
138438032Speter			/* Last-Attempt-Date: -- fine granularity */
138538032Speter			if (q->q_statdate == (time_t) 0L)
138690795Sgshapiro				q->q_statdate = curtime();
1387168520Sgshapiro			(void) sm_strlcpyn(buf, sizeof(buf), 2,
138890795Sgshapiro					"Last-Attempt-Date: ",
138964565Sgshapiro					arpadate(ctime(&q->q_statdate)));
1390157006Sgshapiro			if (!putline(buf, mci))
1391157006Sgshapiro				goto writeerr;
139238032Speter
139338032Speter			/* Will-Retry-Until: -- for delayed messages only */
139464565Sgshapiro			if (QS_IS_QUEUEUP(q->q_state))
139538032Speter			{
139638032Speter				time_t xdate;
139738032Speter
139838032Speter				xdate = e->e_parent->e_ctime +
139938032Speter					TimeOuts.to_q_return[e->e_parent->e_timeoutclass];
1400168520Sgshapiro				(void) sm_strlcpyn(buf, sizeof(buf), 2,
140190795Sgshapiro					 "Will-Retry-Until: ",
140264565Sgshapiro					 arpadate(ctime(&xdate)));
1403157006Sgshapiro				if (!putline(buf, mci))
1404157006Sgshapiro					goto writeerr;
140538032Speter			}
140638032Speter		}
140738032Speter	}
140864565Sgshapiro#endif /* DSN */
140938032Speter
141038032Speter	/*
141138032Speter	**  Output text of original message
141238032Speter	*/
141338032Speter
1414157006Sgshapiro	if (!putline("", mci))
1415157006Sgshapiro		goto writeerr;
141638032Speter	if (bitset(EF_HAS_DF, e->e_parent->e_flags))
141738032Speter	{
141838032Speter		sendbody = !bitset(EF_NO_BODY_RETN, e->e_parent->e_flags) &&
141938032Speter			   !bitset(EF_NO_BODY_RETN, e->e_flags);
142038032Speter
142138032Speter		if (e->e_msgboundary == NULL)
142238032Speter		{
1423157006Sgshapiro			if (!putline(
1424157006Sgshapiro				sendbody
1425157006Sgshapiro				? "   ----- Original message follows -----\n"
1426157006Sgshapiro				: "   ----- Message header follows -----\n",
1427157006Sgshapiro				mci))
1428157006Sgshapiro			{
1429157006Sgshapiro				goto writeerr;
1430157006Sgshapiro			}
143138032Speter		}
143238032Speter		else
143338032Speter		{
1434168520Sgshapiro			(void) sm_strlcpyn(buf, sizeof(buf), 2, "--",
143564565Sgshapiro					e->e_msgboundary);
143638032Speter
1437157006Sgshapiro			if (!putline(buf, mci))
1438157006Sgshapiro				goto writeerr;
1439363466Sgshapiro#if _FFR_EAI
1440363466Sgshapiro			if (e->e_parent->e_smtputf8)
1441363466Sgshapiro				(void) sm_strlcpyn(buf, sizeof(buf), 2,
1442363466Sgshapiro						   "Content-Type: message/global",
1443363466Sgshapiro						   sendbody ? "" : "-headers");
1444363466Sgshapiro			else
1445363466Sgshapiro				(void) sm_strlcpyn(buf, sizeof(buf), 2,
1446363466Sgshapiro						   "Content-Type: ",
1447363466Sgshapiro						sendbody ? "message/rfc822"
1448363466Sgshapiro							 : "text/rfc822-headers");
1449363466Sgshapiro#else /* _FFR_EAI */
1450168520Sgshapiro			(void) sm_strlcpyn(buf, sizeof(buf), 2, "Content-Type: ",
145164565Sgshapiro					sendbody ? "message/rfc822"
145264565Sgshapiro						 : "text/rfc822-headers");
1453363466Sgshapiro#endif /* _FFR_EAI */
1454157006Sgshapiro			if (!putline(buf, mci))
1455157006Sgshapiro				goto writeerr;
145638032Speter
145764565Sgshapiro			p = hvalue("Content-Transfer-Encoding",
145864565Sgshapiro				   e->e_parent->e_header);
145990795Sgshapiro			if (p != NULL && sm_strcasecmp(p, "binary") != 0)
146038032Speter				p = NULL;
146164565Sgshapiro			if (p == NULL &&
146264565Sgshapiro			    bitset(EF_HAS8BIT, e->e_parent->e_flags))
146338032Speter				p = "8bit";
146438032Speter			if (p != NULL)
146538032Speter			{
1466168520Sgshapiro				(void) sm_snprintf(buf, sizeof(buf),
146764565Sgshapiro						"Content-Transfer-Encoding: %s",
146864565Sgshapiro						p);
1469157006Sgshapiro				if (!putline(buf, mci))
1470157006Sgshapiro					goto writeerr;
147138032Speter			}
147238032Speter		}
1473157006Sgshapiro		if (!putline("", mci))
1474157006Sgshapiro			goto writeerr;
147564565Sgshapiro		save_errno = errno;
1476157006Sgshapiro		if (!putheader(mci, e->e_parent->e_header, e->e_parent,
1477157006Sgshapiro				M87F_OUTER))
1478157006Sgshapiro			goto writeerr;
147964565Sgshapiro		errno = save_errno;
148038032Speter		if (sendbody)
1481157006Sgshapiro		{
1482157006Sgshapiro			if (!putbody(mci, e->e_parent, e->e_msgboundary))
1483157006Sgshapiro				goto writeerr;
1484157006Sgshapiro		}
148538032Speter		else if (e->e_msgboundary == NULL)
148638032Speter		{
1487157006Sgshapiro			if (!putline("", mci) ||
1488157006Sgshapiro			    !putline("   ----- Message body suppressed -----",
1489157006Sgshapiro					mci))
1490157006Sgshapiro			{
1491157006Sgshapiro				goto writeerr;
1492157006Sgshapiro			}
149338032Speter		}
149438032Speter	}
149538032Speter	else if (e->e_msgboundary == NULL)
149638032Speter	{
1497157006Sgshapiro		if (!putline("  ----- No message was collected -----\n", mci))
1498157006Sgshapiro			goto writeerr;
149938032Speter	}
150038032Speter
150138032Speter	if (e->e_msgboundary != NULL)
150238032Speter	{
1503168520Sgshapiro		(void) sm_strlcpyn(buf, sizeof(buf), 3, "--", e->e_msgboundary,
150490795Sgshapiro				   "--");
1505157006Sgshapiro		if (!putline("", mci) || !putline(buf, mci))
1506157006Sgshapiro			goto writeerr;
150738032Speter	}
1508157006Sgshapiro	if (!putline("", mci) ||
1509157006Sgshapiro	    sm_io_flush(mci->mci_out, SM_TIME_DEFAULT) == SM_IO_EOF)
1510157006Sgshapiro			goto writeerr;
151138032Speter
151238032Speter	/*
151338032Speter	**  Cleanup and exit
151438032Speter	*/
151538032Speter
151638032Speter	if (errno != 0)
1517157006Sgshapiro	{
1518157006Sgshapiro  writeerr:
151938032Speter		syserr("errbody: I/O error");
1520157006Sgshapiro		return false;
1521157006Sgshapiro	}
1522157006Sgshapiro	return true;
152338032Speter}
1524157006Sgshapiro
152590795Sgshapiro/*
152638032Speter**  SMTPTODSN -- convert SMTP to DSN status code
152738032Speter**
152838032Speter**	Parameters:
152938032Speter**		smtpstat -- the smtp status code (e.g., 550).
153038032Speter**
153138032Speter**	Returns:
153238032Speter**		The DSN version of the status code.
153390795Sgshapiro**
153490795Sgshapiro**	Storage Management:
153590795Sgshapiro**		smtptodsn() returns a pointer to a character string literal,
153690795Sgshapiro**		which will remain valid forever, and thus does not need to
153790795Sgshapiro**		be copied.  Current code relies on this property.
153838032Speter*/
153938032Speter
154038032Speterchar *
154138032Spetersmtptodsn(smtpstat)
154238032Speter	int smtpstat;
154338032Speter{
154438032Speter	if (smtpstat < 0)
154538032Speter		return "4.4.2";
154638032Speter
154738032Speter	switch (smtpstat)
154838032Speter	{
154938032Speter	  case 450:	/* Req mail action not taken: mailbox unavailable */
155038032Speter		return "4.2.0";
155138032Speter
155238032Speter	  case 451:	/* Req action aborted: local error in processing */
155338032Speter		return "4.3.0";
155438032Speter
155538032Speter	  case 452:	/* Req action not taken: insufficient sys storage */
155638032Speter		return "4.3.1";
155738032Speter
155838032Speter	  case 500:	/* Syntax error, command unrecognized */
155938032Speter		return "5.5.2";
156038032Speter
156138032Speter	  case 501:	/* Syntax error in parameters or arguments */
156238032Speter		return "5.5.4";
156338032Speter
156438032Speter	  case 502:	/* Command not implemented */
156538032Speter		return "5.5.1";
156638032Speter
156738032Speter	  case 503:	/* Bad sequence of commands */
156838032Speter		return "5.5.1";
156938032Speter
157038032Speter	  case 504:	/* Command parameter not implemented */
157138032Speter		return "5.5.4";
157238032Speter
157338032Speter	  case 550:	/* Req mail action not taken: mailbox unavailable */
157438032Speter		return "5.2.0";
157538032Speter
157638032Speter	  case 551:	/* User not local; please try <...> */
157738032Speter		return "5.1.6";
157838032Speter
157938032Speter	  case 552:	/* Req mail action aborted: exceeded storage alloc */
158038032Speter		return "5.2.2";
158138032Speter
158238032Speter	  case 553:	/* Req action not taken: mailbox name not allowed */
158338032Speter		return "5.1.0";
158438032Speter
158538032Speter	  case 554:	/* Transaction failed */
158638032Speter		return "5.0.0";
158738032Speter	}
158838032Speter
1589157006Sgshapiro	if (REPLYTYPE(smtpstat) == 2)
159038032Speter		return "2.0.0";
1591157006Sgshapiro	if (REPLYTYPE(smtpstat) == 4)
159238032Speter		return "4.0.0";
159338032Speter	return "5.0.0";
159438032Speter}
159590795Sgshapiro/*
159638032Speter**  XTEXTIFY -- take regular text and turn it into DSN-style xtext
159738032Speter**
159838032Speter**	Parameters:
159938032Speter**		t -- the text to convert.
160038032Speter**		taboo -- additional characters that must be encoded.
160138032Speter**
160238032Speter**	Returns:
160338032Speter**		The xtext-ified version of the same string.
160438032Speter*/
160538032Speter
160638032Speterchar *
160738032Speterxtextify(t, taboo)
160838032Speter	register char *t;
160938032Speter	char *taboo;
161038032Speter{
161138032Speter	register char *p;
161238032Speter	int l;
161338032Speter	int nbogus;
161438032Speter	static char *bp = NULL;
161538032Speter	static int bplen = 0;
161638032Speter
161738032Speter	if (taboo == NULL)
161838032Speter		taboo = "";
161938032Speter
162038032Speter	/* figure out how long this xtext will have to be */
162138032Speter	nbogus = l = 0;
162238032Speter	for (p = t; *p != '\0'; p++)
162338032Speter	{
162438032Speter		register int c = (*p & 0xff);
162538032Speter
162638032Speter		/* ASCII dependence here -- this is the way the spec words it */
162738032Speter		if (c < '!' || c > '~' || c == '+' || c == '\\' || c == '(' ||
162838032Speter		    strchr(taboo, c) != NULL)
162938032Speter			nbogus++;
163038032Speter		l++;
163138032Speter	}
163290795Sgshapiro	if (nbogus < 0)
163390795Sgshapiro	{
163490795Sgshapiro		/* since nbogus is ssize_t and wrapped, 2 * size_t would wrap */
163590795Sgshapiro		syserr("!xtextify string too long");
163690795Sgshapiro	}
163738032Speter	if (nbogus == 0)
163838032Speter		return t;
163938032Speter	l += nbogus * 2 + 1;
164038032Speter
164138032Speter	/* now allocate space if necessary for the new string */
164238032Speter	if (l > bplen)
164338032Speter	{
164438032Speter		if (bp != NULL)
164590795Sgshapiro			sm_free(bp); /* XXX */
164690795Sgshapiro		bp = sm_pmalloc_x(l);
164738032Speter		bplen = l;
164838032Speter	}
164938032Speter
165038032Speter	/* ok, copy the text with byte expansion */
165138032Speter	for (p = bp; *t != '\0'; )
165238032Speter	{
165338032Speter		register int c = (*t++ & 0xff);
165438032Speter
165538032Speter		/* ASCII dependence here -- this is the way the spec words it */
165638032Speter		if (c < '!' || c > '~' || c == '+' || c == '\\' || c == '(' ||
165738032Speter		    strchr(taboo, c) != NULL)
165838032Speter		{
165938032Speter			*p++ = '+';
166064565Sgshapiro			*p++ = "0123456789ABCDEF"[c >> 4];
166164565Sgshapiro			*p++ = "0123456789ABCDEF"[c & 0xf];
166238032Speter		}
166338032Speter		else
166438032Speter			*p++ = c;
166538032Speter	}
166638032Speter	*p = '\0';
166738032Speter	return bp;
166838032Speter}
166990795Sgshapiro/*
167038032Speter**  XUNTEXTIFY -- take xtext and turn it into plain text
167138032Speter**
167238032Speter**	Parameters:
167338032Speter**		t -- the xtextified text.
167438032Speter**
167538032Speter**	Returns:
167638032Speter**		The decoded text.  No attempt is made to deal with
167738032Speter**		null strings in the resulting text.
167838032Speter*/
167938032Speter
168038032Speterchar *
168138032Speterxuntextify(t)
168238032Speter	register char *t;
168338032Speter{
168438032Speter	register char *p;
168538032Speter	int l;
168638032Speter	static char *bp = NULL;
168738032Speter	static int bplen = 0;
168838032Speter
168938032Speter	/* heuristic -- if no plus sign, just return the input */
169038032Speter	if (strchr(t, '+') == NULL)
169138032Speter		return t;
169238032Speter
169338032Speter	/* xtext is always longer than decoded text */
169438032Speter	l = strlen(t);
169538032Speter	if (l > bplen)
169638032Speter	{
169738032Speter		if (bp != NULL)
169890795Sgshapiro			sm_free(bp); /* XXX */
169938032Speter		bp = xalloc(l);
170038032Speter		bplen = l;
170138032Speter	}
170238032Speter
170338032Speter	/* ok, copy the text with byte compression */
170438032Speter	for (p = bp; *t != '\0'; t++)
170538032Speter	{
170638032Speter		register int c = *t & 0xff;
170738032Speter
170838032Speter		if (c != '+')
170938032Speter		{
171038032Speter			*p++ = c;
171138032Speter			continue;
171238032Speter		}
171338032Speter
171438032Speter		c = *++t & 0xff;
171538032Speter		if (!isascii(c) || !isxdigit(c))
171638032Speter		{
171738032Speter			/* error -- first digit is not hex */
171838032Speter			usrerr("bogus xtext: +%c", c);
171938032Speter			t--;
172038032Speter			continue;
172138032Speter		}
172238032Speter		if (isdigit(c))
172338032Speter			c -= '0';
172438032Speter		else if (isupper(c))
172538032Speter			c -= 'A' - 10;
172638032Speter		else
172738032Speter			c -= 'a' - 10;
172838032Speter		*p = c << 4;
172938032Speter
173038032Speter		c = *++t & 0xff;
173138032Speter		if (!isascii(c) || !isxdigit(c))
173238032Speter		{
173338032Speter			/* error -- second digit is not hex */
173438032Speter			usrerr("bogus xtext: +%x%c", *p >> 4, c);
173538032Speter			t--;
173638032Speter			continue;
173738032Speter		}
173838032Speter		if (isdigit(c))
173938032Speter			c -= '0';
174038032Speter		else if (isupper(c))
174138032Speter			c -= 'A' - 10;
174238032Speter		else
174338032Speter			c -= 'a' - 10;
174438032Speter		*p++ |= c;
174538032Speter	}
174638032Speter	*p = '\0';
174738032Speter	return bp;
174838032Speter}
174990795Sgshapiro/*
175038032Speter**  XTEXTOK -- check if a string is legal xtext
175138032Speter**
175238032Speter**	Xtext is used in Delivery Status Notifications.  The spec was
175338032Speter**	taken from RFC 1891, ``SMTP Service Extension for Delivery
175438032Speter**	Status Notifications''.
175538032Speter**
175638032Speter**	Parameters:
175738032Speter**		s -- the string to check.
175838032Speter**
175938032Speter**	Returns:
176090795Sgshapiro**		true -- if 's' is legal xtext.
176190795Sgshapiro**		false -- if it has any illegal characters in it.
176238032Speter*/
176338032Speter
176438032Speterbool
176538032Speterxtextok(s)
176638032Speter	char *s;
176738032Speter{
176838032Speter	int c;
176938032Speter
177038032Speter	while ((c = *s++) != '\0')
177138032Speter	{
177238032Speter		if (c == '+')
177338032Speter		{
177438032Speter			c = *s++;
177538032Speter			if (!isascii(c) || !isxdigit(c))
177690795Sgshapiro				return false;
177738032Speter			c = *s++;
177838032Speter			if (!isascii(c) || !isxdigit(c))
177990795Sgshapiro				return false;
178038032Speter		}
178138032Speter		else if (c < '!' || c > '~' || c == '=')
178290795Sgshapiro			return false;
178338032Speter	}
178490795Sgshapiro	return true;
178538032Speter}
1786249729Sgshapiro
178790795Sgshapiro/*
1788249729Sgshapiro**  ISATOM -- check if a string is an "atom"
1789249729Sgshapiro**
1790249729Sgshapiro**	Parameters:
1791249729Sgshapiro**		s -- the string to check.
1792249729Sgshapiro**
1793249729Sgshapiro**	Returns:
1794249729Sgshapiro**		true -- iff s is an atom
1795249729Sgshapiro*/
1796249729Sgshapiro
1797249729Sgshapirobool
1798249729Sgshapiroisatom(s)
1799249729Sgshapiro	const char *s;
1800249729Sgshapiro{
1801249729Sgshapiro	int c;
1802249729Sgshapiro
1803249729Sgshapiro	if (s == NULL || *s == '\0')
1804249729Sgshapiro		return false;
1805249729Sgshapiro	while ((c = *s++) != '\0')
1806249729Sgshapiro	{
1807249729Sgshapiro		if (strchr("()<>@,;:\\.[]\"", c) != NULL)
1808249729Sgshapiro			return false;
1809249729Sgshapiro		if (c < '!' || c > '~')
1810249729Sgshapiro			return false;
1811249729Sgshapiro	}
1812249729Sgshapiro	return true;
1813249729Sgshapiro}
1814249729Sgshapiro/*
181538032Speter**  PRUNEROUTE -- prune an RFC-822 source route
181664565Sgshapiro**
181738032Speter**	Trims down a source route to the last internet-registered hop.
181838032Speter**	This is encouraged by RFC 1123 section 5.3.3.
181964565Sgshapiro**
182038032Speter**	Parameters:
182138032Speter**		addr -- the address
182264565Sgshapiro**
182338032Speter**	Returns:
182490795Sgshapiro**		true -- address was modified
182590795Sgshapiro**		false -- address could not be pruned
182664565Sgshapiro**
182738032Speter**	Side Effects:
182838032Speter**		modifies addr in-place
182938032Speter*/
183038032Speter
183164565Sgshapirostatic bool
183238032Speterpruneroute(addr)
183338032Speter	char *addr;
183438032Speter{
183538032Speter#if NAMED_BIND
183638032Speter	char *start, *at, *comma;
183738032Speter	char c;
183890795Sgshapiro	int braclev;
183938032Speter	int rcode;
184038032Speter	int i;
184138032Speter	char hostbuf[BUFSIZ];
184238032Speter	char *mxhosts[MAXMXHOSTS + 1];
184338032Speter
184438032Speter	/* check to see if this is really a route-addr */
184538032Speter	if (*addr != '<' || addr[1] != '@' || addr[strlen(addr) - 1] != '>')
184690795Sgshapiro		return false;
184790795Sgshapiro
184890795Sgshapiro	/*
184990795Sgshapiro	**  Can't simply find the first ':' is the address might be in the
185090795Sgshapiro	**  form:  "<@[IPv6:::1]:user@host>" and the first ':' in inside
185190795Sgshapiro	**  the IPv6 address.
185290795Sgshapiro	*/
185390795Sgshapiro
185490795Sgshapiro	start = addr;
185590795Sgshapiro	braclev = 0;
185690795Sgshapiro	while (*start != '\0')
185790795Sgshapiro	{
185890795Sgshapiro		if (*start == ':' && braclev <= 0)
185990795Sgshapiro			break;
186090795Sgshapiro		else if (*start == '[')
186190795Sgshapiro			braclev++;
186290795Sgshapiro		else if (*start == ']' && braclev > 0)
186390795Sgshapiro			braclev--;
186490795Sgshapiro		start++;
186590795Sgshapiro	}
186690795Sgshapiro	if (braclev > 0 || *start != ':')
186790795Sgshapiro		return false;
186890795Sgshapiro
186938032Speter	at = strrchr(addr, '@');
187090795Sgshapiro	if (at == NULL || at < start)
187190795Sgshapiro		return false;
187238032Speter
187338032Speter	/* slice off the angle brackets */
187438032Speter	i = strlen(at + 1);
1875168520Sgshapiro	if (i >= sizeof(hostbuf))
187690795Sgshapiro		return false;
1877168520Sgshapiro	(void) sm_strlcpy(hostbuf, at + 1, sizeof(hostbuf));
187838032Speter	hostbuf[i - 1] = '\0';
187938032Speter
188090795Sgshapiro	while (start != NULL)
188138032Speter	{
1882363466Sgshapiro		if (getmxrr(hostbuf, mxhosts, NULL, TRYFALLBACK, &rcode, NULL, -1)
1883363466Sgshapiro		    > 0)
188438032Speter		{
188590795Sgshapiro			(void) sm_strlcpy(addr + 1, start + 1,
188690795Sgshapiro					  strlen(addr) - 1);
188790795Sgshapiro			return true;
188838032Speter		}
188938032Speter		c = *start;
189038032Speter		*start = '\0';
189138032Speter		comma = strrchr(addr, ',');
189238032Speter		if (comma != NULL && comma[1] == '@' &&
1893168520Sgshapiro		    strlen(comma + 2) < sizeof(hostbuf))
1894168520Sgshapiro			(void) sm_strlcpy(hostbuf, comma + 2, sizeof(hostbuf));
189538032Speter		else
189638032Speter			comma = NULL;
189738032Speter		*start = c;
189838032Speter		start = comma;
189938032Speter	}
190064565Sgshapiro#endif /* NAMED_BIND */
190190795Sgshapiro	return false;
190238032Speter}
1903