savemail.c revision 43733
138032Speter/*
238032Speter * Copyright (c) 1998 Sendmail, Inc.  All rights reserved.
338032Speter * Copyright (c) 1983, 1995-1997 Eric P. Allman.  All rights reserved.
438032Speter * Copyright (c) 1988, 1993
538032Speter *	The Regents of the University of California.  All rights reserved.
638032Speter *
738032Speter * By using this file, you agree to the terms and conditions set
838032Speter * forth in the LICENSE file which can be found at the top level of
938032Speter * the sendmail distribution.
1038032Speter *
1138032Speter */
1238032Speter
1338032Speter#ifndef lint
1443733Speterstatic char sccsid[] = "@(#)savemail.c	8.140 (Berkeley) 1/18/1999";
1538032Speter#endif /* not lint */
1638032Speter
1738032Speter# include "sendmail.h"
1838032Speter
1938032Speter/*
2038032Speter**  SAVEMAIL -- Save mail on error
2138032Speter**
2238032Speter**	If mailing back errors, mail it back to the originator
2338032Speter**	together with an error message; otherwise, just put it in
2438032Speter**	dead.letter in the user's home directory (if he exists on
2538032Speter**	this machine).
2638032Speter**
2738032Speter**	Parameters:
2838032Speter**		e -- the envelope containing the message in error.
2938032Speter**		sendbody -- if TRUE, also send back the body of the
3038032Speter**			message; otherwise just send the header.
3138032Speter**
3238032Speter**	Returns:
3338032Speter**		none
3438032Speter**
3538032Speter**	Side Effects:
3638032Speter**		Saves the letter, by writing or mailing it back to the
3738032Speter**		sender, or by putting it in dead.letter in her home
3838032Speter**		directory.
3938032Speter*/
4038032Speter
4138032Speter/* defines for state machine */
4238032Speter# define ESM_REPORT	0	/* report to sender's terminal */
4338032Speter# define ESM_MAIL	1	/* mail back to sender */
4438032Speter# define ESM_QUIET	2	/* messages have already been returned */
4538032Speter# define ESM_DEADLETTER	3	/* save in ~/dead.letter */
4638032Speter# define ESM_POSTMASTER	4	/* return to postmaster */
4738032Speter# define ESM_USRTMP	5	/* save in /usr/tmp/dead.letter */
4838032Speter# define ESM_PANIC	6	/* leave the locked queue/transcript files */
4938032Speter# define ESM_DONE	7	/* the message is successfully delivered */
5038032Speter
5138032Speter
5238032Spetervoid
5338032Spetersavemail(e, sendbody)
5438032Speter	register ENVELOPE *e;
5538032Speter	bool sendbody;
5638032Speter{
5738032Speter	register struct passwd *pw;
5838032Speter	register FILE *fp;
5938032Speter	int state;
6038032Speter	auto ADDRESS *q = NULL;
6138032Speter	register char *p;
6238032Speter	MCI mcibuf;
6338032Speter	int flags;
6438032Speter	char buf[MAXLINE+1];
6538032Speter	extern char *ttypath __P((void));
6638032Speter	extern bool writable __P((char *, ADDRESS *, int));
6738032Speter
6838032Speter	if (tTd(6, 1))
6938032Speter	{
7038032Speter		printf("\nsavemail, errormode = %c, id = %s, ExitStat = %d\n  e_from=",
7138032Speter			e->e_errormode, e->e_id == NULL ? "NONE" : e->e_id,
7238032Speter			ExitStat);
7338032Speter		printaddr(&e->e_from, FALSE);
7438032Speter	}
7538032Speter
7638032Speter	if (e->e_id == NULL)
7738032Speter	{
7838032Speter		/* can't return a message with no id */
7938032Speter		return;
8038032Speter	}
8138032Speter
8238032Speter	/*
8338032Speter	**  In the unhappy event we don't know who to return the mail
8438032Speter	**  to, make someone up.
8538032Speter	*/
8638032Speter
8738032Speter	if (e->e_from.q_paddr == NULL)
8838032Speter	{
8938032Speter		e->e_sender = "Postmaster";
9038032Speter		if (parseaddr(e->e_sender, &e->e_from,
9138032Speter			      RF_COPYPARSE|RF_SENDERADDR, '\0', NULL, e) == NULL)
9238032Speter		{
9338032Speter			syserr("553 Cannot parse Postmaster!");
9442580Speter			finis(TRUE, EX_SOFTWARE);
9538032Speter		}
9638032Speter	}
9738032Speter	e->e_to = NULL;
9838032Speter
9938032Speter	/*
10038032Speter	**  Basic state machine.
10138032Speter	**
10238032Speter	**	This machine runs through the following states:
10338032Speter	**
10438032Speter	**	ESM_QUIET	Errors have already been printed iff the
10538032Speter	**			sender is local.
10638032Speter	**	ESM_REPORT	Report directly to the sender's terminal.
10738032Speter	**	ESM_MAIL	Mail response to the sender.
10838032Speter	**	ESM_DEADLETTER	Save response in ~/dead.letter.
10938032Speter	**	ESM_POSTMASTER	Mail response to the postmaster.
11038032Speter	**	ESM_PANIC	Save response anywhere possible.
11138032Speter	*/
11238032Speter
11338032Speter	/* determine starting state */
11438032Speter	switch (e->e_errormode)
11538032Speter	{
11638032Speter	  case EM_WRITE:
11738032Speter		state = ESM_REPORT;
11838032Speter		break;
11938032Speter
12038032Speter	  case EM_BERKNET:
12138032Speter	  case EM_MAIL:
12238032Speter		state = ESM_MAIL;
12338032Speter		break;
12438032Speter
12538032Speter	  case EM_PRINT:
12638032Speter	  case '\0':
12738032Speter		state = ESM_QUIET;
12838032Speter		break;
12938032Speter
13038032Speter	  case EM_QUIET:
13138032Speter		/* no need to return anything at all */
13238032Speter		return;
13338032Speter
13438032Speter	  default:
13538032Speter		syserr("554 savemail: bogus errormode x%x\n", e->e_errormode);
13638032Speter		state = ESM_MAIL;
13738032Speter		break;
13838032Speter	}
13938032Speter
14038032Speter	/* if this is already an error response, send to postmaster */
14138032Speter	if (bitset(EF_RESPONSE, e->e_flags))
14238032Speter	{
14338032Speter		if (e->e_parent != NULL &&
14438032Speter		    bitset(EF_RESPONSE, e->e_parent->e_flags))
14538032Speter		{
14638032Speter			/* got an error sending a response -- can it */
14738032Speter			return;
14838032Speter		}
14938032Speter		state = ESM_POSTMASTER;
15038032Speter	}
15138032Speter
15238032Speter	while (state != ESM_DONE)
15338032Speter	{
15438032Speter		if (tTd(6, 5))
15538032Speter			printf("  state %d\n", state);
15638032Speter
15738032Speter		switch (state)
15838032Speter		{
15938032Speter		  case ESM_QUIET:
16038032Speter			if (bitnset(M_LOCALMAILER, e->e_from.q_mailer->m_flags))
16138032Speter				state = ESM_DEADLETTER;
16238032Speter			else
16338032Speter				state = ESM_MAIL;
16438032Speter			break;
16538032Speter
16638032Speter		  case ESM_REPORT:
16738032Speter
16838032Speter			/*
16938032Speter			**  If the user is still logged in on the same terminal,
17038032Speter			**  then write the error messages back to hir (sic).
17138032Speter			*/
17238032Speter
17338032Speter			p = ttypath();
17438032Speter			if (p == NULL || freopen(p, "w", stdout) == NULL)
17538032Speter			{
17638032Speter				state = ESM_MAIL;
17738032Speter				break;
17838032Speter			}
17938032Speter
18038032Speter			expand("\201n", buf, sizeof buf, e);
18138032Speter			printf("\r\nMessage from %s...\r\n", buf);
18238032Speter			printf("Errors occurred while sending mail.\r\n");
18338032Speter			if (e->e_xfp != NULL)
18438032Speter			{
18538032Speter				(void) fflush(e->e_xfp);
18638032Speter				fp = fopen(queuename(e, 'x'), "r");
18738032Speter			}
18838032Speter			else
18938032Speter				fp = NULL;
19038032Speter			if (fp == NULL)
19138032Speter			{
19238032Speter				syserr("Cannot open %s", queuename(e, 'x'));
19338032Speter				printf("Transcript of session is unavailable.\r\n");
19438032Speter			}
19538032Speter			else
19638032Speter			{
19738032Speter				printf("Transcript follows:\r\n");
19838032Speter				while (fgets(buf, sizeof buf, fp) != NULL &&
19938032Speter				       !ferror(stdout))
20038032Speter					fputs(buf, stdout);
20138032Speter				(void) xfclose(fp, "savemail transcript", e->e_id);
20238032Speter			}
20338032Speter			printf("Original message will be saved in dead.letter.\r\n");
20438032Speter			state = ESM_DEADLETTER;
20538032Speter			break;
20638032Speter
20738032Speter		  case ESM_MAIL:
20838032Speter			/*
20938032Speter			**  If mailing back, do it.
21038032Speter			**	Throw away all further output.  Don't alias,
21138032Speter			**	since this could cause loops, e.g., if joe
21238032Speter			**	mails to joe@x, and for some reason the network
21338032Speter			**	for @x is down, then the response gets sent to
21438032Speter			**	joe@x, which gives a response, etc.  Also force
21538032Speter			**	the mail to be delivered even if a version of
21638032Speter			**	it has already been sent to the sender.
21738032Speter			**
21838032Speter			**  If this is a configuration or local software
21938032Speter			**	error, send to the local postmaster as well,
22038032Speter			**	since the originator can't do anything
22138032Speter			**	about it anyway.  Note that this is a full
22238032Speter			**	copy of the message (intentionally) so that
22338032Speter			**	the Postmaster can forward things along.
22438032Speter			*/
22538032Speter
22638032Speter			if (ExitStat == EX_CONFIG || ExitStat == EX_SOFTWARE)
22738032Speter			{
22838032Speter				(void) sendtolist("postmaster",
22938032Speter					  NULLADDR, &e->e_errorqueue, 0, e);
23038032Speter			}
23138032Speter			if (!emptyaddr(&e->e_from))
23238032Speter			{
23338032Speter				char from[TOBUFSIZE];
23438032Speter				extern bool pruneroute __P((char *));
23538032Speter
23638032Speter				if (strlen(e->e_from.q_paddr) + 1 > sizeof from)
23738032Speter				{
23838032Speter					state = ESM_POSTMASTER;
23938032Speter					break;
24038032Speter				}
24138032Speter				strcpy(from, e->e_from.q_paddr);
24238032Speter
24338032Speter				if (!DontPruneRoutes && pruneroute(from))
24438032Speter				{
24538032Speter					ADDRESS *a;
24638032Speter
24738032Speter					for (a = e->e_errorqueue; a != NULL;
24838032Speter					     a = a->q_next)
24938032Speter					{
25038032Speter						if (sameaddr(a, &e->e_from))
25138032Speter							a->q_flags |= QDONTSEND;
25238032Speter					}
25338032Speter				}
25438032Speter				(void) sendtolist(from, NULLADDR,
25538032Speter						  &e->e_errorqueue, 0, e);
25638032Speter			}
25738032Speter
25838032Speter			/*
25938032Speter			**  Deliver a non-delivery report to the
26038032Speter			**  Postmaster-designate (not necessarily
26138032Speter			**  Postmaster).  This does not include the
26238032Speter			**  body of the message, for privacy reasons.
26338032Speter			**  You really shouldn't need this.
26438032Speter			*/
26538032Speter
26638032Speter			e->e_flags |= EF_PM_NOTIFY;
26738032Speter
26838032Speter			/* check to see if there are any good addresses */
26938032Speter			for (q = e->e_errorqueue; q != NULL; q = q->q_next)
27038032Speter				if (!bitset(QBADADDR|QDONTSEND, q->q_flags))
27138032Speter					break;
27238032Speter			if (q == NULL)
27338032Speter			{
27438032Speter				/* this is an error-error */
27538032Speter				state = ESM_POSTMASTER;
27638032Speter				break;
27738032Speter			}
27838032Speter			if (returntosender(e->e_message, e->e_errorqueue,
27938032Speter					   sendbody ? RTSF_SEND_BODY
28038032Speter						    : RTSF_NO_BODY,
28138032Speter					   e) == 0)
28238032Speter			{
28338032Speter				state = ESM_DONE;
28438032Speter				break;
28538032Speter			}
28638032Speter
28738032Speter			/* didn't work -- return to postmaster */
28838032Speter			state = ESM_POSTMASTER;
28938032Speter			break;
29038032Speter
29138032Speter		  case ESM_POSTMASTER:
29238032Speter			/*
29338032Speter			**  Similar to previous case, but to system postmaster.
29438032Speter			*/
29538032Speter
29638032Speter			q = NULL;
29738032Speter			if (sendtolist(DoubleBounceAddr,
29838032Speter				NULLADDR, &q, 0, e) <= 0)
29938032Speter			{
30038032Speter				syserr("553 cannot parse %s!", DoubleBounceAddr);
30138032Speter				ExitStat = EX_SOFTWARE;
30238032Speter				state = ESM_USRTMP;
30338032Speter				break;
30438032Speter			}
30538032Speter			flags = RTSF_PM_BOUNCE;
30638032Speter			if (sendbody)
30738032Speter				flags |= RTSF_SEND_BODY;
30838032Speter			if (returntosender(e->e_message, q, flags, e) == 0)
30938032Speter			{
31038032Speter				state = ESM_DONE;
31138032Speter				break;
31238032Speter			}
31338032Speter
31438032Speter			/* didn't work -- last resort */
31538032Speter			state = ESM_USRTMP;
31638032Speter			break;
31738032Speter
31838032Speter		  case ESM_DEADLETTER:
31938032Speter			/*
32038032Speter			**  Save the message in dead.letter.
32138032Speter			**	If we weren't mailing back, and the user is
32238032Speter			**	local, we should save the message in
32338032Speter			**	~/dead.letter so that the poor person doesn't
32438032Speter			**	have to type it over again -- and we all know
32538032Speter			**	what poor typists UNIX users are.
32638032Speter			*/
32738032Speter
32838032Speter			p = NULL;
32938032Speter			if (bitnset(M_HASPWENT, e->e_from.q_mailer->m_flags))
33038032Speter			{
33138032Speter				if (e->e_from.q_home != NULL)
33238032Speter					p = e->e_from.q_home;
33338032Speter				else if ((pw = sm_getpwnam(e->e_from.q_user)) != NULL)
33438032Speter					p = pw->pw_dir;
33538032Speter			}
33638032Speter			if (p == NULL || e->e_dfp == NULL)
33738032Speter			{
33838032Speter				/* no local directory or no data file */
33938032Speter				state = ESM_MAIL;
34038032Speter				break;
34138032Speter			}
34238032Speter
34338032Speter			/* we have a home directory; write dead.letter */
34438032Speter			define('z', p, e);
34538032Speter			expand("\201z/dead.letter", buf, sizeof buf, e);
34638032Speter			flags = SFF_CREAT|SFF_REGONLY|SFF_RUNASREALUID;
34738032Speter			if (RealUid == 0)
34838032Speter				flags |= SFF_ROOTOK;
34938032Speter			e->e_to = buf;
35038032Speter			if (mailfile(buf, FileMailer, NULL, flags, e) == EX_OK)
35138032Speter			{
35238032Speter				int oldverb = Verbose;
35338032Speter
35438032Speter				Verbose = 1;
35538032Speter				message("Saved message in %s", buf);
35638032Speter				Verbose = oldverb;
35738032Speter				state = ESM_DONE;
35838032Speter				break;
35938032Speter			}
36038032Speter			state = ESM_MAIL;
36138032Speter			break;
36238032Speter
36338032Speter		  case ESM_USRTMP:
36438032Speter			/*
36538032Speter			**  Log the mail in /usr/tmp/dead.letter.
36638032Speter			*/
36738032Speter
36838032Speter			if (e->e_class < 0)
36938032Speter			{
37038032Speter				state = ESM_DONE;
37138032Speter				break;
37238032Speter			}
37338032Speter
37438032Speter			if ((SafeFileEnv != NULL && SafeFileEnv[0] != '\0') ||
37538032Speter			    DeadLetterDrop == NULL || DeadLetterDrop[0] == '\0')
37638032Speter			{
37738032Speter				state = ESM_PANIC;
37838032Speter				break;
37938032Speter			}
38038032Speter
38138032Speter			flags = SFF_CREAT|SFF_REGONLY|SFF_ROOTOK|SFF_OPENASROOT|SFF_MUSTOWN;
38238032Speter			if (!writable(DeadLetterDrop, NULL, flags) ||
38338032Speter			    (fp = safefopen(DeadLetterDrop, O_WRONLY|O_APPEND,
38438032Speter					    FileMode, flags)) == NULL)
38538032Speter			{
38638032Speter				state = ESM_PANIC;
38738032Speter				break;
38838032Speter			}
38938032Speter
39038032Speter			bzero(&mcibuf, sizeof mcibuf);
39138032Speter			mcibuf.mci_out = fp;
39238032Speter			mcibuf.mci_mailer = FileMailer;
39338032Speter			if (bitnset(M_7BITS, FileMailer->m_flags))
39438032Speter				mcibuf.mci_flags |= MCIF_7BIT;
39538032Speter			mcibuf.mci_contentlen = 0;
39638032Speter
39738032Speter			putfromline(&mcibuf, e);
39843733Speter			(*e->e_puthdr)(&mcibuf, e->e_header, e, M87F_OUTER);
39938032Speter			(*e->e_putbody)(&mcibuf, e, NULL);
40038032Speter			putline("\n", &mcibuf);
40138032Speter			(void) fflush(fp);
40238032Speter			if (ferror(fp))
40338032Speter				state = ESM_PANIC;
40438032Speter			else
40538032Speter			{
40638032Speter				int oldverb = Verbose;
40738032Speter
40838032Speter				Verbose = 1;
40938032Speter				message("Saved message in %s", DeadLetterDrop);
41038032Speter				Verbose = oldverb;
41138032Speter				if (LogLevel > 3)
41238032Speter					sm_syslog(LOG_NOTICE, e->e_id,
41338032Speter						"Saved message in %s",
41438032Speter						DeadLetterDrop);
41538032Speter				state = ESM_DONE;
41638032Speter			}
41738032Speter			(void) xfclose(fp, "savemail", DeadLetterDrop);
41838032Speter			break;
41938032Speter
42038032Speter		  default:
42138032Speter			syserr("554 savemail: unknown state %d", state);
42238032Speter
42338032Speter			/* fall through ... */
42438032Speter
42538032Speter		  case ESM_PANIC:
42638032Speter			/* leave the locked queue & transcript files around */
42738032Speter			loseqfile(e, "savemail panic");
42838032Speter			syserr("!554 savemail: cannot save rejected email anywhere");
42938032Speter		}
43038032Speter	}
43138032Speter}
43238032Speter/*
43338032Speter**  RETURNTOSENDER -- return a message to the sender with an error.
43438032Speter**
43538032Speter**	Parameters:
43638032Speter**		msg -- the explanatory message.
43738032Speter**		returnq -- the queue of people to send the message to.
43838032Speter**		flags -- flags tweaking the operation:
43938032Speter**			RTSF_SENDBODY -- include body of message (otherwise
44038032Speter**				just send the header).
44138032Speter**			RTSF_PMBOUNCE -- this is a postmaster bounce.
44238032Speter**		e -- the current envelope.
44338032Speter**
44438032Speter**	Returns:
44538032Speter**		zero -- if everything went ok.
44638032Speter**		else -- some error.
44738032Speter**
44838032Speter**	Side Effects:
44938032Speter**		Returns the current message to the sender via
45038032Speter**		mail.
45138032Speter*/
45238032Speter
45338032Speter#define MAXRETURNS	6	/* max depth of returning messages */
45438032Speter#define ERRORFUDGE	100	/* nominal size of error message text */
45538032Speter
45638032Speterint
45738032Speterreturntosender(msg, returnq, flags, e)
45838032Speter	char *msg;
45938032Speter	ADDRESS *returnq;
46038032Speter	int flags;
46138032Speter	register ENVELOPE *e;
46238032Speter{
46338032Speter	register ENVELOPE *ee;
46438032Speter	ENVELOPE *oldcur = CurEnv;
46538032Speter	ENVELOPE errenvelope;
46638032Speter	static int returndepth = 0;
46738032Speter	register ADDRESS *q;
46838032Speter	char *p;
46938032Speter	char buf[MAXNAME + 1];
47038032Speter	extern void errbody __P((MCI *, ENVELOPE *, char *));
47138032Speter
47238032Speter	if (returnq == NULL)
47338032Speter		return (-1);
47438032Speter
47538032Speter	if (msg == NULL)
47638032Speter		msg = "Unable to deliver mail";
47738032Speter
47838032Speter	if (tTd(6, 1))
47938032Speter	{
48038032Speter		printf("\n*** Return To Sender: msg=\"%s\", depth=%d, e=%lx, returnq=",
48138032Speter		       msg, returndepth, (u_long) e);
48238032Speter		printaddr(returnq, TRUE);
48338032Speter		if (tTd(6, 20))
48438032Speter		{
48538032Speter			printf("Sendq=");
48638032Speter			printaddr(e->e_sendqueue, TRUE);
48738032Speter		}
48838032Speter	}
48938032Speter
49038032Speter	if (++returndepth >= MAXRETURNS)
49138032Speter	{
49238032Speter		if (returndepth != MAXRETURNS)
49338032Speter			syserr("554 returntosender: infinite recursion on %s", returnq->q_paddr);
49438032Speter		/* don't "unrecurse" and fake a clean exit */
49538032Speter		/* returndepth--; */
49638032Speter		return (0);
49738032Speter	}
49838032Speter
49938032Speter	define('g', e->e_from.q_paddr, e);
50038032Speter	define('u', NULL, e);
50138032Speter
50238032Speter	/* initialize error envelope */
50338032Speter	ee = newenvelope(&errenvelope, e);
50438032Speter	define('a', "\201b", ee);
50538032Speter	define('r', "internal", ee);
50638032Speter	define('s', "localhost", ee);
50738032Speter	define('_', "localhost", ee);
50838032Speter	ee->e_puthdr = putheader;
50938032Speter	ee->e_putbody = errbody;
51038032Speter	ee->e_flags |= EF_RESPONSE|EF_METOO;
51138032Speter	if (!bitset(EF_OLDSTYLE, e->e_flags))
51238032Speter		ee->e_flags &= ~EF_OLDSTYLE;
51338032Speter	ee->e_sendqueue = returnq;
51438032Speter	ee->e_msgsize = ERRORFUDGE;
51538032Speter	if (bitset(RTSF_SEND_BODY, flags))
51638032Speter		ee->e_msgsize += e->e_msgsize;
51738032Speter	else
51838032Speter		ee->e_flags |= EF_NO_BODY_RETN;
51938032Speter	initsys(ee);
52038032Speter	for (q = returnq; q != NULL; q = q->q_next)
52138032Speter	{
52238032Speter		if (bitset(QBADADDR, q->q_flags))
52338032Speter			continue;
52438032Speter
52538032Speter		q->q_flags &= ~(QHASNOTIFY|Q_PINGFLAGS);
52638032Speter		q->q_flags |= QPINGONFAILURE;
52738032Speter
52838032Speter		if (!bitset(QDONTSEND, q->q_flags))
52938032Speter			ee->e_nrcpts++;
53038032Speter
53138032Speter		if (q->q_alias == NULL)
53238032Speter			addheader("To", q->q_paddr, &ee->e_header);
53338032Speter	}
53438032Speter
53538032Speter	if (LogLevel > 5)
53638032Speter	{
53738032Speter		if (bitset(EF_RESPONSE|EF_WARNING, e->e_flags))
53838032Speter			p = "return to sender";
53938032Speter		else if (bitset(RTSF_PM_BOUNCE, flags))
54038032Speter			p = "postmaster notify";
54138032Speter		else
54238032Speter			p = "DSN";
54338032Speter		sm_syslog(LOG_INFO, e->e_id,
54438032Speter			"%s: %s: %s",
54538032Speter			ee->e_id, p, shortenstring(msg, MAXSHORTSTR));
54638032Speter	}
54738032Speter
54838032Speter	if (SendMIMEErrors)
54938032Speter	{
55038032Speter		addheader("MIME-Version", "1.0", &ee->e_header);
55138032Speter
55238032Speter		(void) snprintf(buf, sizeof buf, "%s.%ld/%.100s",
55340497Sbde			ee->e_id, (long)curtime(), MyHostName);
55438032Speter		ee->e_msgboundary = newstr(buf);
55538032Speter		(void) snprintf(buf, sizeof buf,
55638032Speter#if DSN
55738032Speter			"multipart/report; report-type=delivery-status;\n\tboundary=\"%s\"",
55838032Speter#else
55938032Speter			"multipart/mixed; boundary=\"%s\"",
56038032Speter#endif
56138032Speter			ee->e_msgboundary);
56238032Speter		addheader("Content-Type", buf, &ee->e_header);
56338032Speter
56438032Speter		p = hvalue("Content-Transfer-Encoding", e->e_header);
56538032Speter		if (p != NULL && strcasecmp(p, "binary") != 0)
56638032Speter			p = NULL;
56738032Speter		if (p == NULL && bitset(EF_HAS8BIT, e->e_flags))
56838032Speter			p = "8bit";
56938032Speter		if (p != NULL)
57038032Speter			addheader("Content-Transfer-Encoding", p, &ee->e_header);
57138032Speter	}
57238032Speter	if (strncmp(msg, "Warning:", 8) == 0)
57338032Speter	{
57438032Speter		addheader("Subject", msg, &ee->e_header);
57538032Speter		p = "warning-timeout";
57638032Speter	}
57738032Speter	else if (strncmp(msg, "Postmaster warning:", 19) == 0)
57838032Speter	{
57938032Speter		addheader("Subject", msg, &ee->e_header);
58038032Speter		p = "postmaster-warning";
58138032Speter	}
58238032Speter	else if (strcmp(msg, "Return receipt") == 0)
58338032Speter	{
58438032Speter		addheader("Subject", msg, &ee->e_header);
58538032Speter		p = "return-receipt";
58638032Speter	}
58738032Speter	else if (bitset(RTSF_PM_BOUNCE, flags))
58838032Speter	{
58938032Speter		snprintf(buf, sizeof buf, "Postmaster notify: %.*s",
59040497Sbde			(int)sizeof buf - 20, msg);
59138032Speter		addheader("Subject", buf, &ee->e_header);
59238032Speter		p = "postmaster-notification";
59338032Speter	}
59438032Speter	else
59538032Speter	{
59638032Speter		snprintf(buf, sizeof buf, "Returned mail: %.*s",
59740497Sbde			(int)sizeof buf - 20, msg);
59838032Speter		addheader("Subject", buf, &ee->e_header);
59938032Speter		p = "failure";
60038032Speter	}
60138032Speter	(void) snprintf(buf, sizeof buf, "auto-generated (%s)", p);
60238032Speter	addheader("Auto-Submitted", buf, &ee->e_header);
60338032Speter
60438032Speter	/* fake up an address header for the from person */
60538032Speter	expand("\201n", buf, sizeof buf, e);
60638032Speter	if (parseaddr(buf, &ee->e_from, RF_COPYALL|RF_SENDERADDR, '\0', NULL, e) == NULL)
60738032Speter	{
60838032Speter		syserr("553 Can't parse myself!");
60938032Speter		ExitStat = EX_SOFTWARE;
61038032Speter		returndepth--;
61138032Speter		return (-1);
61238032Speter	}
61338032Speter	ee->e_from.q_flags &= ~(QHASNOTIFY|Q_PINGFLAGS);
61438032Speter	ee->e_from.q_flags |= QPINGONFAILURE;
61538032Speter	ee->e_sender = ee->e_from.q_paddr;
61638032Speter
61738032Speter	/* push state into submessage */
61838032Speter	CurEnv = ee;
61938032Speter	define('f', "\201n", ee);
62038032Speter	define('x', "Mail Delivery Subsystem", ee);
62138032Speter	eatheader(ee, TRUE);
62238032Speter
62338032Speter	/* mark statistics */
62438032Speter	markstats(ee, NULLADDR, FALSE);
62538032Speter
62638032Speter	/* actually deliver the error message */
62738032Speter	sendall(ee, SM_DELIVER);
62838032Speter
62938032Speter	/* restore state */
63038032Speter	dropenvelope(ee, TRUE);
63138032Speter	CurEnv = oldcur;
63238032Speter	returndepth--;
63338032Speter
63438032Speter	/* check for delivery errors */
63538032Speter	if (ee->e_parent == NULL || !bitset(EF_RESPONSE, ee->e_parent->e_flags))
63638032Speter		return 0;
63738032Speter	for (q = ee->e_sendqueue; q != NULL; q = q->q_next)
63838032Speter	{
63938032Speter		if (bitset(QQUEUEUP|QSENT, q->q_flags))
64038032Speter			return 0;
64138032Speter	}
64238032Speter	return -1;
64338032Speter}
64438032Speter/*
64538032Speter**  ERRBODY -- output the body of an error message.
64638032Speter**
64738032Speter**	Typically this is a copy of the transcript plus a copy of the
64838032Speter**	original offending message.
64938032Speter**
65038032Speter**	Parameters:
65138032Speter**		mci -- the mailer connection information.
65238032Speter**		e -- the envelope we are working in.
65338032Speter**		separator -- any possible MIME separator.
65438032Speter**
65538032Speter**	Returns:
65638032Speter**		none
65738032Speter**
65838032Speter**	Side Effects:
65938032Speter**		Outputs the body of an error message.
66038032Speter*/
66138032Speter
66238032Spetervoid
66338032Spetererrbody(mci, e, separator)
66438032Speter	register MCI *mci;
66538032Speter	register ENVELOPE *e;
66638032Speter	char *separator;
66738032Speter{
66838032Speter	register FILE *xfile;
66938032Speter	char *p;
67038032Speter	register ADDRESS *q = NULL;
67138032Speter	bool printheader;
67238032Speter	bool sendbody;
67338032Speter	bool pm_notify;
67438032Speter	char buf[MAXLINE];
67538032Speter
67638032Speter	if (bitset(MCIF_INHEADER, mci->mci_flags))
67738032Speter	{
67838032Speter		putline("", mci);
67938032Speter		mci->mci_flags &= ~MCIF_INHEADER;
68038032Speter	}
68138032Speter	if (e->e_parent == NULL)
68238032Speter	{
68338032Speter		syserr("errbody: null parent");
68438032Speter		putline("   ----- Original message lost -----\n", mci);
68538032Speter		return;
68638032Speter	}
68738032Speter
68838032Speter	/*
68938032Speter	**  Output MIME header.
69038032Speter	*/
69138032Speter
69238032Speter	if (e->e_msgboundary != NULL)
69338032Speter	{
69438032Speter		putline("This is a MIME-encapsulated message", mci);
69538032Speter		putline("", mci);
69638032Speter		(void) snprintf(buf, sizeof buf, "--%s", e->e_msgboundary);
69738032Speter		putline(buf, mci);
69838032Speter		putline("", mci);
69938032Speter	}
70038032Speter
70138032Speter	/*
70238032Speter	**  Output introductory information.
70338032Speter	*/
70438032Speter
70538032Speter	pm_notify = FALSE;
70638032Speter	p = hvalue("subject", e->e_header);
70738032Speter	if (p != NULL && strncmp(p, "Postmaster ", 11) == 0)
70838032Speter		pm_notify = TRUE;
70938032Speter	else
71038032Speter	{
71138032Speter		for (q = e->e_parent->e_sendqueue; q != NULL; q = q->q_next)
71238032Speter			if (bitset(QBADADDR, q->q_flags))
71338032Speter				break;
71438032Speter	}
71538032Speter	if (!pm_notify && q == NULL &&
71638032Speter	    !bitset(EF_FATALERRS|EF_SENDRECEIPT, e->e_parent->e_flags))
71738032Speter	{
71838032Speter		putline("    **********************************************",
71938032Speter			mci);
72038032Speter		putline("    **      THIS IS A WARNING MESSAGE ONLY      **",
72138032Speter			mci);
72238032Speter		putline("    **  YOU DO NOT NEED TO RESEND YOUR MESSAGE  **",
72338032Speter			mci);
72438032Speter		putline("    **********************************************",
72538032Speter			mci);
72638032Speter		putline("", mci);
72738032Speter	}
72838032Speter	snprintf(buf, sizeof buf, "The original message was received at %s",
72938032Speter		arpadate(ctime(&e->e_parent->e_ctime)));
73038032Speter	putline(buf, mci);
73138032Speter	expand("from \201_", buf, sizeof buf, e->e_parent);
73238032Speter	putline(buf, mci);
73338032Speter	putline("", mci);
73438032Speter
73538032Speter	/*
73638032Speter	**  Output error message header (if specified and available).
73738032Speter	*/
73838032Speter
73938032Speter	if (ErrMsgFile != NULL && !bitset(EF_SENDRECEIPT, e->e_parent->e_flags))
74038032Speter	{
74138032Speter		if (*ErrMsgFile == '/')
74238032Speter		{
74338032Speter			int sff = SFF_ROOTOK|SFF_REGONLY;
74438032Speter
74538032Speter			if (DontLockReadFiles)
74638032Speter				sff |= SFF_NOLOCK;
74738032Speter			if (!bitset(DBS_ERRORHEADERINUNSAFEDIRPATH, DontBlameSendmail))
74838032Speter				sff |= SFF_SAFEDIRPATH;
74938032Speter			xfile = safefopen(ErrMsgFile, O_RDONLY, 0444, sff);
75038032Speter			if (xfile != NULL)
75138032Speter			{
75238032Speter				while (fgets(buf, sizeof buf, xfile) != NULL)
75338032Speter				{
75438032Speter					extern void translate_dollars __P((char *));
75538032Speter
75638032Speter					translate_dollars(buf);
75738032Speter					expand(buf, buf, sizeof buf, e);
75838032Speter					putline(buf, mci);
75938032Speter				}
76038032Speter				(void) fclose(xfile);
76138032Speter				putline("\n", mci);
76238032Speter			}
76338032Speter		}
76438032Speter		else
76538032Speter		{
76638032Speter			expand(ErrMsgFile, buf, sizeof buf, e);
76738032Speter			putline(buf, mci);
76838032Speter			putline("", mci);
76938032Speter		}
77038032Speter	}
77138032Speter
77238032Speter	/*
77338032Speter	**  Output message introduction
77438032Speter	*/
77538032Speter
77638032Speter	printheader = TRUE;
77738032Speter	for (q = e->e_parent->e_sendqueue; q != NULL; q = q->q_next)
77838032Speter	{
77938032Speter		if (!bitset(QBADADDR, q->q_flags) ||
78038032Speter		    !bitset(QPINGONFAILURE, q->q_flags))
78138032Speter			continue;
78238032Speter
78338032Speter		if (printheader)
78438032Speter		{
78538032Speter			putline("   ----- The following addresses had permanent fatal errors -----",
78638032Speter				mci);
78738032Speter			printheader = FALSE;
78838032Speter		}
78938032Speter
79038032Speter		snprintf(buf, sizeof buf, "%s",
79138032Speter			 shortenstring(q->q_paddr, MAXSHORTSTR));
79238032Speter		putline(buf, mci);
79338032Speter		if (q->q_alias != NULL)
79438032Speter		{
79538032Speter			snprintf(buf, sizeof buf, "    (expanded from: %s)",
79638032Speter				shortenstring(q->q_alias->q_paddr, MAXSHORTSTR));
79738032Speter			putline(buf, mci);
79838032Speter		}
79938032Speter	}
80038032Speter	if (!printheader)
80138032Speter		putline("", mci);
80238032Speter
80338032Speter	printheader = TRUE;
80438032Speter	for (q = e->e_parent->e_sendqueue; q != NULL; q = q->q_next)
80538032Speter	{
80638032Speter		if (bitset(QBADADDR, q->q_flags) ||
80738032Speter		    !bitset(QPRIMARY, q->q_flags) ||
80838032Speter		    !bitset(QDELAYED, q->q_flags))
80938032Speter			continue;
81038032Speter
81138032Speter		if (printheader)
81238032Speter		{
81338032Speter			putline("   ----- The following addresses had transient non-fatal errors -----",
81438032Speter				mci);
81538032Speter			printheader = FALSE;
81638032Speter		}
81738032Speter
81838032Speter		snprintf(buf, sizeof buf, "%s",
81938032Speter			 shortenstring(q->q_paddr, MAXSHORTSTR));
82038032Speter		putline(buf, mci);
82138032Speter		if (q->q_alias != NULL)
82238032Speter		{
82338032Speter			snprintf(buf, sizeof buf, "    (expanded from: %s)",
82438032Speter				shortenstring(q->q_alias->q_paddr, MAXSHORTSTR));
82538032Speter			putline(buf, mci);
82638032Speter		}
82738032Speter	}
82838032Speter	if (!printheader)
82938032Speter		putline("", mci);
83038032Speter
83138032Speter	printheader = TRUE;
83238032Speter	for (q = e->e_parent->e_sendqueue; q != NULL; q = q->q_next)
83338032Speter	{
83438032Speter		if (bitset(QBADADDR, q->q_flags) ||
83538032Speter		    !bitset(QPRIMARY, q->q_flags) ||
83638032Speter		    bitset(QDELAYED, q->q_flags))
83738032Speter			continue;
83838032Speter		else if (!bitset(QPINGONSUCCESS, q->q_flags))
83938032Speter			continue;
84038032Speter		else if (bitset(QRELAYED, q->q_flags))
84138032Speter			p = "relayed to non-DSN-aware mailer";
84238032Speter		else if (bitset(QDELIVERED, q->q_flags))
84338032Speter		{
84438032Speter			if (bitset(QEXPANDED, q->q_flags))
84538032Speter				p = "successfully delivered to mailing list";
84638032Speter			else
84738032Speter				p = "successfully delivered to mailbox";
84838032Speter		}
84938032Speter		else if (bitset(QEXPANDED, q->q_flags))
85038032Speter			p = "expanded by alias";
85138032Speter		else
85238032Speter			continue;
85338032Speter
85438032Speter		if (printheader)
85538032Speter		{
85638032Speter			putline("   ----- The following addresses had successful delivery notifications -----",
85738032Speter				mci);
85838032Speter			printheader = FALSE;
85938032Speter		}
86038032Speter
86138032Speter		snprintf(buf, sizeof buf, "%s  (%s)",
86238032Speter			shortenstring(q->q_paddr, MAXSHORTSTR), p);
86338032Speter		putline(buf, mci);
86438032Speter		if (q->q_alias != NULL)
86538032Speter		{
86638032Speter			snprintf(buf, sizeof buf, "    (expanded from: %s)",
86738032Speter				shortenstring(q->q_alias->q_paddr, MAXSHORTSTR));
86838032Speter			putline(buf, mci);
86938032Speter		}
87038032Speter	}
87138032Speter	if (!printheader)
87238032Speter		putline("", mci);
87338032Speter
87438032Speter	/*
87538032Speter	**  Output transcript of errors
87638032Speter	*/
87738032Speter
87838032Speter	(void) fflush(stdout);
87938032Speter	p = queuename(e->e_parent, 'x');
88038032Speter	if ((xfile = fopen(p, "r")) == NULL)
88138032Speter	{
88238032Speter		syserr("Cannot open %s", p);
88338032Speter		putline("   ----- Transcript of session is unavailable -----\n", mci);
88438032Speter	}
88538032Speter	else
88638032Speter	{
88738032Speter		printheader = TRUE;
88838032Speter		if (e->e_xfp != NULL)
88938032Speter			(void) fflush(e->e_xfp);
89038032Speter		while (fgets(buf, sizeof buf, xfile) != NULL)
89138032Speter		{
89238032Speter			if (printheader)
89338032Speter				putline("   ----- Transcript of session follows -----\n", mci);
89438032Speter			printheader = FALSE;
89538032Speter			putline(buf, mci);
89638032Speter		}
89738032Speter		(void) xfclose(xfile, "errbody xscript", p);
89838032Speter	}
89938032Speter	errno = 0;
90038032Speter
90138032Speter#if DSN
90238032Speter	/*
90338032Speter	**  Output machine-readable version.
90438032Speter	*/
90538032Speter
90638032Speter	if (e->e_msgboundary != NULL)
90738032Speter	{
90838032Speter		putline("", mci);
90938032Speter		(void) snprintf(buf, sizeof buf, "--%s", e->e_msgboundary);
91038032Speter		putline(buf, mci);
91138032Speter		putline("Content-Type: message/delivery-status", mci);
91238032Speter		putline("", mci);
91338032Speter
91438032Speter		/*
91538032Speter		**  Output per-message information.
91638032Speter		*/
91738032Speter
91838032Speter		/* original envelope id from MAIL FROM: line */
91938032Speter		if (e->e_parent->e_envid != NULL)
92038032Speter		{
92138032Speter			(void) snprintf(buf, sizeof buf, "Original-Envelope-Id: %.800s",
92238032Speter				xuntextify(e->e_parent->e_envid));
92338032Speter			putline(buf, mci);
92438032Speter		}
92538032Speter
92638032Speter		/* Reporting-MTA: is us (required) */
92738032Speter		(void) snprintf(buf, sizeof buf, "Reporting-MTA: dns; %.800s", MyHostName);
92838032Speter		putline(buf, mci);
92938032Speter
93038032Speter		/* DSN-Gateway: not relevant since we are not translating */
93138032Speter
93238032Speter		/* Received-From-MTA: shows where we got this message from */
93338032Speter		if (RealHostName != NULL)
93438032Speter		{
93538032Speter			/* XXX use $s for type? */
93638032Speter			if (e->e_parent->e_from.q_mailer == NULL ||
93738032Speter			    (p = e->e_parent->e_from.q_mailer->m_mtatype) == NULL)
93838032Speter				p = "dns";
93938032Speter			(void) snprintf(buf, sizeof buf, "Received-From-MTA: %s; %.800s",
94038032Speter				p, RealHostName);
94138032Speter			putline(buf, mci);
94238032Speter		}
94338032Speter
94438032Speter		/* Arrival-Date: -- when it arrived here */
94538032Speter		(void) snprintf(buf, sizeof buf, "Arrival-Date: %s",
94638032Speter			arpadate(ctime(&e->e_parent->e_ctime)));
94738032Speter		putline(buf, mci);
94838032Speter
94938032Speter		/*
95038032Speter		**  Output per-address information.
95138032Speter		*/
95238032Speter
95338032Speter		for (q = e->e_parent->e_sendqueue; q != NULL; q = q->q_next)
95438032Speter		{
95538032Speter			register ADDRESS *r;
95638032Speter			char *action;
95738032Speter
95838032Speter			if (bitset(QBADADDR, q->q_flags))
95938032Speter				action = "failed";
96038032Speter			else if (!bitset(QPRIMARY, q->q_flags))
96138032Speter				continue;
96238032Speter			else if (bitset(QDELIVERED, q->q_flags))
96338032Speter			{
96438032Speter				if (bitset(QEXPANDED, q->q_flags))
96538032Speter					action = "delivered (to mailing list)";
96638032Speter				else
96738032Speter					action = "delivered (to mailbox)";
96838032Speter			}
96938032Speter			else if (bitset(QRELAYED, q->q_flags))
97038032Speter				action = "relayed (to non-DSN-aware mailer)";
97138032Speter			else if (bitset(QEXPANDED, q->q_flags))
97238032Speter				action = "expanded (to multi-recipient alias)";
97338032Speter			else if (bitset(QDELAYED, q->q_flags))
97438032Speter				action = "delayed";
97538032Speter			else
97638032Speter				continue;
97738032Speter
97838032Speter			putline("", mci);
97938032Speter
98038032Speter			/* Original-Recipient: -- passed from on high */
98138032Speter			if (q->q_orcpt != NULL)
98238032Speter			{
98338032Speter				(void) snprintf(buf, sizeof buf, "Original-Recipient: %.800s",
98438032Speter					q->q_orcpt);
98538032Speter				putline(buf, mci);
98638032Speter			}
98738032Speter
98838032Speter			/* Final-Recipient: -- the name from the RCPT command */
98938032Speter			p = e->e_parent->e_from.q_mailer->m_addrtype;
99038032Speter			if (p == NULL)
99138032Speter				p = "rfc822";
99238032Speter			for (r = q; r->q_alias != NULL; r = r->q_alias)
99338032Speter				continue;
99438032Speter			if (strchr(r->q_user, '@') != NULL)
99538032Speter			{
99638032Speter				(void) snprintf(buf, sizeof buf,
99738032Speter					"Final-Recipient: %s; %.800s",
99838032Speter					p, r->q_user);
99938032Speter			}
100038032Speter			else if (strchr(r->q_paddr, '@') != NULL)
100138032Speter			{
100238032Speter				(void) snprintf(buf, sizeof buf,
100338032Speter					"Final-Recipient: %s; %.800s",
100438032Speter					p, r->q_paddr);
100538032Speter			}
100638032Speter			else
100738032Speter			{
100838032Speter				(void) snprintf(buf, sizeof buf,
100938032Speter					"Final-Recipient: %s; %.700s@%.100s",
101038032Speter					p, r->q_user, MyHostName);
101138032Speter			}
101238032Speter			putline(buf, mci);
101338032Speter
101438032Speter			/* X-Actual-Recipient: -- the real problem address */
101538032Speter			if (r != q && q->q_user[0] != '\0')
101638032Speter			{
101738032Speter				if (strchr(q->q_user, '@') == NULL)
101838032Speter				{
101938032Speter					(void) snprintf(buf, sizeof buf,
102038032Speter						"X-Actual-Recipient: %s; %.700s@%.100s",
102138032Speter						p, q->q_user, MyHostName);
102238032Speter				}
102338032Speter				else
102438032Speter				{
102538032Speter					(void) snprintf(buf, sizeof buf,
102638032Speter						"X-Actual-Recipient: %s; %.800s",
102738032Speter						p, q->q_user);
102838032Speter				}
102938032Speter				putline(buf, mci);
103038032Speter			}
103138032Speter
103238032Speter			/* Action: -- what happened? */
103338032Speter			snprintf(buf, sizeof buf, "Action: %s", action);
103438032Speter			putline(buf, mci);
103538032Speter
103638032Speter			/* Status: -- what _really_ happened? */
103738032Speter			if (q->q_status != NULL)
103838032Speter				p = q->q_status;
103938032Speter			else if (bitset(QBADADDR, q->q_flags))
104038032Speter				p = "5.0.0";
104138032Speter			else if (bitset(QQUEUEUP, q->q_flags))
104238032Speter				p = "4.0.0";
104338032Speter			else
104438032Speter				p = "2.0.0";
104538032Speter			snprintf(buf, sizeof buf, "Status: %s", p);
104638032Speter			putline(buf, mci);
104738032Speter
104838032Speter			/* Remote-MTA: -- who was I talking to? */
104938032Speter			if (q->q_statmta != NULL)
105038032Speter			{
105138032Speter				if (q->q_mailer == NULL ||
105238032Speter				    (p = q->q_mailer->m_mtatype) == NULL)
105338032Speter					p = "dns";
105438032Speter				(void) snprintf(buf, sizeof buf,
105538032Speter					"Remote-MTA: %s; %.800s",
105638032Speter					p, q->q_statmta);
105738032Speter				p = &buf[strlen(buf) - 1];
105838032Speter				if (*p == '.')
105938032Speter					*p = '\0';
106038032Speter				putline(buf, mci);
106138032Speter			}
106238032Speter
106338032Speter			/* Diagnostic-Code: -- actual result from other end */
106438032Speter			if (q->q_rstatus != NULL)
106538032Speter			{
106638032Speter				p = q->q_mailer->m_diagtype;
106738032Speter				if (p == NULL)
106838032Speter					p = "smtp";
106938032Speter				(void) snprintf(buf, sizeof buf,
107038032Speter					"Diagnostic-Code: %s; %.800s",
107138032Speter					p, q->q_rstatus);
107238032Speter				putline(buf, mci);
107338032Speter			}
107438032Speter
107538032Speter			/* Last-Attempt-Date: -- fine granularity */
107638032Speter			if (q->q_statdate == (time_t) 0L)
107738032Speter				q->q_statdate = curtime();
107838032Speter			(void) snprintf(buf, sizeof buf,
107938032Speter				"Last-Attempt-Date: %s",
108038032Speter				arpadate(ctime(&q->q_statdate)));
108138032Speter			putline(buf, mci);
108238032Speter
108338032Speter			/* Will-Retry-Until: -- for delayed messages only */
108438032Speter			if (bitset(QQUEUEUP, q->q_flags) &&
108538032Speter			    !bitset(QBADADDR, q->q_flags))
108638032Speter			{
108738032Speter				time_t xdate;
108838032Speter
108938032Speter				xdate = e->e_parent->e_ctime +
109038032Speter					TimeOuts.to_q_return[e->e_parent->e_timeoutclass];
109138032Speter				snprintf(buf, sizeof buf,
109238032Speter					"Will-Retry-Until: %s",
109338032Speter					arpadate(ctime(&xdate)));
109438032Speter				putline(buf, mci);
109538032Speter			}
109638032Speter		}
109738032Speter	}
109838032Speter#endif
109938032Speter
110038032Speter	/*
110138032Speter	**  Output text of original message
110238032Speter	*/
110338032Speter
110438032Speter	putline("", mci);
110538032Speter	if (bitset(EF_HAS_DF, e->e_parent->e_flags))
110638032Speter	{
110738032Speter		sendbody = !bitset(EF_NO_BODY_RETN, e->e_parent->e_flags) &&
110838032Speter			   !bitset(EF_NO_BODY_RETN, e->e_flags);
110938032Speter
111038032Speter		if (e->e_msgboundary == NULL)
111138032Speter		{
111238032Speter			if (sendbody)
111338032Speter				putline("   ----- Original message follows -----\n", mci);
111438032Speter			else
111538032Speter				putline("   ----- Message header follows -----\n", mci);
111638032Speter			(void) fflush(mci->mci_out);
111738032Speter		}
111838032Speter		else
111938032Speter		{
112038032Speter			(void) snprintf(buf, sizeof buf, "--%s",
112138032Speter				e->e_msgboundary);
112238032Speter
112338032Speter			putline(buf, mci);
112438032Speter			(void) snprintf(buf, sizeof buf, "Content-Type: %s",
112538032Speter				sendbody ? "message/rfc822"
112638032Speter					 : "text/rfc822-headers");
112738032Speter			putline(buf, mci);
112838032Speter
112938032Speter			p = hvalue("Content-Transfer-Encoding", e->e_parent->e_header);
113038032Speter			if (p != NULL && strcasecmp(p, "binary") != 0)
113138032Speter				p = NULL;
113238032Speter			if (p == NULL && bitset(EF_HAS8BIT, e->e_parent->e_flags))
113338032Speter				p = "8bit";
113438032Speter			if (p != NULL)
113538032Speter			{
113638032Speter				(void) snprintf(buf, sizeof buf, "Content-Transfer-Encoding: %s",
113738032Speter					p);
113838032Speter				putline(buf, mci);
113938032Speter			}
114038032Speter		}
114138032Speter		putline("", mci);
114243733Speter		putheader(mci, e->e_parent->e_header, e->e_parent, M87F_OUTER);
114338032Speter		if (sendbody)
114438032Speter			putbody(mci, e->e_parent, e->e_msgboundary);
114538032Speter		else if (e->e_msgboundary == NULL)
114638032Speter		{
114738032Speter			putline("", mci);
114838032Speter			putline("   ----- Message body suppressed -----", mci);
114938032Speter		}
115038032Speter	}
115138032Speter	else if (e->e_msgboundary == NULL)
115238032Speter	{
115338032Speter		putline("  ----- No message was collected -----\n", mci);
115438032Speter	}
115538032Speter
115638032Speter	if (e->e_msgboundary != NULL)
115738032Speter	{
115838032Speter		putline("", mci);
115938032Speter		(void) snprintf(buf, sizeof buf, "--%s--", e->e_msgboundary);
116038032Speter		putline(buf, mci);
116138032Speter	}
116238032Speter	putline("", mci);
116338032Speter
116438032Speter	/*
116538032Speter	**  Cleanup and exit
116638032Speter	*/
116738032Speter
116838032Speter	if (errno != 0)
116938032Speter		syserr("errbody: I/O error");
117038032Speter}
117138032Speter/*
117238032Speter**  SMTPTODSN -- convert SMTP to DSN status code
117338032Speter**
117438032Speter**	Parameters:
117538032Speter**		smtpstat -- the smtp status code (e.g., 550).
117638032Speter**
117738032Speter**	Returns:
117838032Speter**		The DSN version of the status code.
117938032Speter*/
118038032Speter
118138032Speterchar *
118238032Spetersmtptodsn(smtpstat)
118338032Speter	int smtpstat;
118438032Speter{
118538032Speter	if (smtpstat < 0)
118638032Speter		return "4.4.2";
118738032Speter
118838032Speter	switch (smtpstat)
118938032Speter	{
119038032Speter	  case 450:	/* Req mail action not taken: mailbox unavailable */
119138032Speter		return "4.2.0";
119238032Speter
119338032Speter	  case 451:	/* Req action aborted: local error in processing */
119438032Speter		return "4.3.0";
119538032Speter
119638032Speter	  case 452:	/* Req action not taken: insufficient sys storage */
119738032Speter		return "4.3.1";
119838032Speter
119938032Speter	  case 500:	/* Syntax error, command unrecognized */
120038032Speter		return "5.5.2";
120138032Speter
120238032Speter	  case 501:	/* Syntax error in parameters or arguments */
120338032Speter		return "5.5.4";
120438032Speter
120538032Speter	  case 502:	/* Command not implemented */
120638032Speter		return "5.5.1";
120738032Speter
120838032Speter	  case 503:	/* Bad sequence of commands */
120938032Speter		return "5.5.1";
121038032Speter
121138032Speter	  case 504:	/* Command parameter not implemented */
121238032Speter		return "5.5.4";
121338032Speter
121438032Speter	  case 550:	/* Req mail action not taken: mailbox unavailable */
121538032Speter		return "5.2.0";
121638032Speter
121738032Speter	  case 551:	/* User not local; please try <...> */
121838032Speter		return "5.1.6";
121938032Speter
122038032Speter	  case 552:	/* Req mail action aborted: exceeded storage alloc */
122138032Speter		return "5.2.2";
122238032Speter
122338032Speter	  case 553:	/* Req action not taken: mailbox name not allowed */
122438032Speter		return "5.1.0";
122538032Speter
122638032Speter	  case 554:	/* Transaction failed */
122738032Speter		return "5.0.0";
122838032Speter	}
122938032Speter
123038032Speter	if ((smtpstat / 100) == 2)
123138032Speter		return "2.0.0";
123238032Speter	if ((smtpstat / 100) == 4)
123338032Speter		return "4.0.0";
123438032Speter	return "5.0.0";
123538032Speter}
123638032Speter/*
123738032Speter**  XTEXTIFY -- take regular text and turn it into DSN-style xtext
123838032Speter**
123938032Speter**	Parameters:
124038032Speter**		t -- the text to convert.
124138032Speter**		taboo -- additional characters that must be encoded.
124238032Speter**
124338032Speter**	Returns:
124438032Speter**		The xtext-ified version of the same string.
124538032Speter*/
124638032Speter
124738032Speterchar *
124838032Speterxtextify(t, taboo)
124938032Speter	register char *t;
125038032Speter	char *taboo;
125138032Speter{
125238032Speter	register char *p;
125338032Speter	int l;
125438032Speter	int nbogus;
125538032Speter	static char *bp = NULL;
125638032Speter	static int bplen = 0;
125738032Speter
125838032Speter	if (taboo == NULL)
125938032Speter		taboo = "";
126038032Speter
126138032Speter	/* figure out how long this xtext will have to be */
126238032Speter	nbogus = l = 0;
126338032Speter	for (p = t; *p != '\0'; p++)
126438032Speter	{
126538032Speter		register int c = (*p & 0xff);
126638032Speter
126738032Speter		/* ASCII dependence here -- this is the way the spec words it */
126838032Speter		if (c < '!' || c > '~' || c == '+' || c == '\\' || c == '(' ||
126938032Speter		    strchr(taboo, c) != NULL)
127038032Speter			nbogus++;
127138032Speter		l++;
127238032Speter	}
127338032Speter	if (nbogus == 0)
127438032Speter		return t;
127538032Speter	l += nbogus * 2 + 1;
127638032Speter
127738032Speter	/* now allocate space if necessary for the new string */
127838032Speter	if (l > bplen)
127938032Speter	{
128038032Speter		if (bp != NULL)
128138032Speter			free(bp);
128238032Speter		bp = xalloc(l);
128338032Speter		bplen = l;
128438032Speter	}
128538032Speter
128638032Speter	/* ok, copy the text with byte expansion */
128738032Speter	for (p = bp; *t != '\0'; )
128838032Speter	{
128938032Speter		register int c = (*t++ & 0xff);
129038032Speter
129138032Speter		/* ASCII dependence here -- this is the way the spec words it */
129238032Speter		if (c < '!' || c > '~' || c == '+' || c == '\\' || c == '(' ||
129338032Speter		    strchr(taboo, c) != NULL)
129438032Speter		{
129538032Speter			*p++ = '+';
129638032Speter			*p++ = "0123456789abcdef"[c >> 4];
129738032Speter			*p++ = "0123456789abcdef"[c & 0xf];
129838032Speter		}
129938032Speter		else
130038032Speter			*p++ = c;
130138032Speter	}
130238032Speter	*p = '\0';
130338032Speter	return bp;
130438032Speter}
130538032Speter/*
130638032Speter**  XUNTEXTIFY -- take xtext and turn it into plain text
130738032Speter**
130838032Speter**	Parameters:
130938032Speter**		t -- the xtextified text.
131038032Speter**
131138032Speter**	Returns:
131238032Speter**		The decoded text.  No attempt is made to deal with
131338032Speter**		null strings in the resulting text.
131438032Speter*/
131538032Speter
131638032Speterchar *
131738032Speterxuntextify(t)
131838032Speter	register char *t;
131938032Speter{
132038032Speter	register char *p;
132138032Speter	int l;
132238032Speter	static char *bp = NULL;
132338032Speter	static int bplen = 0;
132438032Speter
132538032Speter	/* heuristic -- if no plus sign, just return the input */
132638032Speter	if (strchr(t, '+') == NULL)
132738032Speter		return t;
132838032Speter
132938032Speter	/* xtext is always longer than decoded text */
133038032Speter	l = strlen(t);
133138032Speter	if (l > bplen)
133238032Speter	{
133338032Speter		if (bp != NULL)
133438032Speter			free(bp);
133538032Speter		bp = xalloc(l);
133638032Speter		bplen = l;
133738032Speter	}
133838032Speter
133938032Speter	/* ok, copy the text with byte compression */
134038032Speter	for (p = bp; *t != '\0'; t++)
134138032Speter	{
134238032Speter		register int c = *t & 0xff;
134338032Speter
134438032Speter		if (c != '+')
134538032Speter		{
134638032Speter			*p++ = c;
134738032Speter			continue;
134838032Speter		}
134938032Speter
135038032Speter		c = *++t & 0xff;
135138032Speter		if (!isascii(c) || !isxdigit(c))
135238032Speter		{
135338032Speter			/* error -- first digit is not hex */
135438032Speter			usrerr("bogus xtext: +%c", c);
135538032Speter			t--;
135638032Speter			continue;
135738032Speter		}
135838032Speter		if (isdigit(c))
135938032Speter			c -= '0';
136038032Speter		else if (isupper(c))
136138032Speter			c -= 'A' - 10;
136238032Speter		else
136338032Speter			c -= 'a' - 10;
136438032Speter		*p = c << 4;
136538032Speter
136638032Speter		c = *++t & 0xff;
136738032Speter		if (!isascii(c) || !isxdigit(c))
136838032Speter		{
136938032Speter			/* error -- second digit is not hex */
137038032Speter			usrerr("bogus xtext: +%x%c", *p >> 4, c);
137138032Speter			t--;
137238032Speter			continue;
137338032Speter		}
137438032Speter		if (isdigit(c))
137538032Speter			c -= '0';
137638032Speter		else if (isupper(c))
137738032Speter			c -= 'A' - 10;
137838032Speter		else
137938032Speter			c -= 'a' - 10;
138038032Speter		*p++ |= c;
138138032Speter	}
138238032Speter	*p = '\0';
138338032Speter	return bp;
138438032Speter}
138538032Speter/*
138638032Speter**  XTEXTOK -- check if a string is legal xtext
138738032Speter**
138838032Speter**	Xtext is used in Delivery Status Notifications.  The spec was
138938032Speter**	taken from RFC 1891, ``SMTP Service Extension for Delivery
139038032Speter**	Status Notifications''.
139138032Speter**
139238032Speter**	Parameters:
139338032Speter**		s -- the string to check.
139438032Speter**
139538032Speter**	Returns:
139638032Speter**		TRUE -- if 's' is legal xtext.
139738032Speter**		FALSE -- if it has any illegal characters in it.
139838032Speter*/
139938032Speter
140038032Speterbool
140138032Speterxtextok(s)
140238032Speter	char *s;
140338032Speter{
140438032Speter	int c;
140538032Speter
140638032Speter	while ((c = *s++) != '\0')
140738032Speter	{
140838032Speter		if (c == '+')
140938032Speter		{
141038032Speter			c = *s++;
141138032Speter			if (!isascii(c) || !isxdigit(c))
141238032Speter				return FALSE;
141338032Speter			c = *s++;
141438032Speter			if (!isascii(c) || !isxdigit(c))
141538032Speter				return FALSE;
141638032Speter		}
141738032Speter		else if (c < '!' || c > '~' || c == '=')
141838032Speter			return FALSE;
141938032Speter	}
142038032Speter	return TRUE;
142138032Speter}
142238032Speter/*
142338032Speter**  PRUNEROUTE -- prune an RFC-822 source route
142438032Speter**
142538032Speter**	Trims down a source route to the last internet-registered hop.
142638032Speter**	This is encouraged by RFC 1123 section 5.3.3.
142738032Speter**
142838032Speter**	Parameters:
142938032Speter**		addr -- the address
143038032Speter**
143138032Speter**	Returns:
143238032Speter**		TRUE -- address was modified
143338032Speter**		FALSE -- address could not be pruned
143438032Speter**
143538032Speter**	Side Effects:
143638032Speter**		modifies addr in-place
143738032Speter*/
143838032Speter
143938032Speterbool
144038032Speterpruneroute(addr)
144138032Speter	char *addr;
144238032Speter{
144338032Speter#if NAMED_BIND
144438032Speter	char *start, *at, *comma;
144538032Speter	char c;
144638032Speter	int rcode;
144738032Speter	int i;
144838032Speter	char hostbuf[BUFSIZ];
144938032Speter	char *mxhosts[MAXMXHOSTS + 1];
145038032Speter
145138032Speter	/* check to see if this is really a route-addr */
145238032Speter	if (*addr != '<' || addr[1] != '@' || addr[strlen(addr) - 1] != '>')
145338032Speter		return FALSE;
145438032Speter	start = strchr(addr, ':');
145538032Speter	at = strrchr(addr, '@');
145638032Speter	if (start == NULL || at == NULL || at < start)
145738032Speter		return FALSE;
145838032Speter
145938032Speter	/* slice off the angle brackets */
146038032Speter	i = strlen(at + 1);
146138032Speter	if (i >= (SIZE_T) sizeof hostbuf)
146238032Speter		return FALSE;
146338032Speter	strcpy(hostbuf, at + 1);
146438032Speter	hostbuf[i - 1] = '\0';
146538032Speter
146638032Speter	while (start)
146738032Speter	{
146838032Speter		if (getmxrr(hostbuf, mxhosts, FALSE, &rcode) > 0)
146938032Speter		{
147038032Speter			strcpy(addr + 1, start + 1);
147138032Speter			return TRUE;
147238032Speter		}
147338032Speter		c = *start;
147438032Speter		*start = '\0';
147538032Speter		comma = strrchr(addr, ',');
147638032Speter		if (comma != NULL && comma[1] == '@' &&
147738032Speter		    strlen(comma + 2) < (SIZE_T) sizeof hostbuf)
147838032Speter			strcpy(hostbuf, comma + 2);
147938032Speter		else
148038032Speter			comma = NULL;
148138032Speter		*start = c;
148238032Speter		start = comma;
148338032Speter	}
148438032Speter#endif
148538032Speter	return FALSE;
148638032Speter}
1487