savemail.c revision 159613
138032Speter/*
2159613Sgshapiro * Copyright (c) 1998-2003, 2006 Sendmail, Inc. and its suppliers.
364565Sgshapiro *	All rights reserved.
438032Speter * Copyright (c) 1983, 1995-1997 Eric P. Allman.  All rights reserved.
538032Speter * Copyright (c) 1988, 1993
638032Speter *	The Regents of the University of California.  All rights reserved.
738032Speter *
838032Speter * By using this file, you agree to the terms and conditions set
938032Speter * forth in the LICENSE file which can be found at the top level of
1038032Speter * the sendmail distribution.
1138032Speter *
12157006Sgshapiro *	$FreeBSD: head/contrib/sendmail/src/savemail.c 159613 2006-06-14 16:25:31Z gshapiro $
13157006Sgshapiro *
1438032Speter */
1538032Speter
1664565Sgshapiro#include <sendmail.h>
1764565Sgshapiro
18159613SgshapiroSM_RCSID("@(#)$Id: savemail.c,v 8.308 2006/04/18 01:31:33 ca Exp $")
1964565Sgshapiro
20157006Sgshapirostatic bool	errbody __P((MCI *, ENVELOPE *, char *));
2164565Sgshapirostatic bool	pruneroute __P((char *));
2264565Sgshapiro
2338032Speter/*
2438032Speter**  SAVEMAIL -- Save mail on error
2538032Speter**
2638032Speter**	If mailing back errors, mail it back to the originator
2738032Speter**	together with an error message; otherwise, just put it in
2838032Speter**	dead.letter in the user's home directory (if he exists on
2938032Speter**	this machine).
3038032Speter**
3138032Speter**	Parameters:
3238032Speter**		e -- the envelope containing the message in error.
3390795Sgshapiro**		sendbody -- if true, also send back the body of the
3438032Speter**			message; otherwise just send the header.
3538032Speter**
3638032Speter**	Returns:
3790795Sgshapiro**		true if savemail panic'ed, (i.e., the data file should
3890795Sgshapiro**		be preserved by dropenvelope())
3938032Speter**
4038032Speter**	Side Effects:
4138032Speter**		Saves the letter, by writing or mailing it back to the
4238032Speter**		sender, or by putting it in dead.letter in her home
4338032Speter**		directory.
4438032Speter*/
4538032Speter
4638032Speter/* defines for state machine */
4764565Sgshapiro#define ESM_REPORT		0	/* report to sender's terminal */
4864565Sgshapiro#define ESM_MAIL		1	/* mail back to sender */
4964565Sgshapiro#define ESM_QUIET		2	/* mail has already been returned */
5064565Sgshapiro#define ESM_DEADLETTER		3	/* save in ~/dead.letter */
5164565Sgshapiro#define ESM_POSTMASTER		4	/* return to postmaster */
5264565Sgshapiro#define ESM_DEADLETTERDROP	5	/* save in DeadLetterDrop */
5364565Sgshapiro#define ESM_PANIC		6	/* call loseqfile() */
5464565Sgshapiro#define ESM_DONE		7	/* message is successfully delivered */
5538032Speter
5690795Sgshapirobool
5738032Spetersavemail(e, sendbody)
5838032Speter	register ENVELOPE *e;
5938032Speter	bool sendbody;
6038032Speter{
6190795Sgshapiro	register SM_FILE_T *fp;
6290795Sgshapiro	bool panic = false;
6338032Speter	int state;
6438032Speter	auto ADDRESS *q = NULL;
6538032Speter	register char *p;
6638032Speter	MCI mcibuf;
6738032Speter	int flags;
6864565Sgshapiro	long sff;
6964565Sgshapiro	char buf[MAXLINE + 1];
7098125Sgshapiro	char dlbuf[MAXPATHLEN];
7190795Sgshapiro	SM_MBDB_T user;
7238032Speter
7390795Sgshapiro
7438032Speter	if (tTd(6, 1))
7538032Speter	{
7690795Sgshapiro		sm_dprintf("\nsavemail, errormode = %c, id = %s, ExitStat = %d\n  e_from=",
7738032Speter			e->e_errormode, e->e_id == NULL ? "NONE" : e->e_id,
7838032Speter			ExitStat);
79132946Sgshapiro		printaddr(sm_debug_file(), &e->e_from, false);
8038032Speter	}
8138032Speter
8238032Speter	if (e->e_id == NULL)
8338032Speter	{
8438032Speter		/* can't return a message with no id */
8590795Sgshapiro		return panic;
8638032Speter	}
8738032Speter
8838032Speter	/*
8938032Speter	**  In the unhappy event we don't know who to return the mail
9038032Speter	**  to, make someone up.
9138032Speter	*/
9238032Speter
9338032Speter	if (e->e_from.q_paddr == NULL)
9438032Speter	{
9538032Speter		e->e_sender = "Postmaster";
9638032Speter		if (parseaddr(e->e_sender, &e->e_from,
9790795Sgshapiro			      RF_COPYPARSE|RF_SENDERADDR,
9890795Sgshapiro			      '\0', NULL, e, false) == NULL)
9938032Speter		{
10064565Sgshapiro			syserr("553 5.3.5 Cannot parse Postmaster!");
10190795Sgshapiro			finis(true, true, EX_SOFTWARE);
10238032Speter		}
10338032Speter	}
10438032Speter	e->e_to = NULL;
10538032Speter
10638032Speter	/*
10738032Speter	**  Basic state machine.
10838032Speter	**
10938032Speter	**	This machine runs through the following states:
11038032Speter	**
11138032Speter	**	ESM_QUIET	Errors have already been printed iff the
11238032Speter	**			sender is local.
11338032Speter	**	ESM_REPORT	Report directly to the sender's terminal.
11438032Speter	**	ESM_MAIL	Mail response to the sender.
11538032Speter	**	ESM_DEADLETTER	Save response in ~/dead.letter.
11638032Speter	**	ESM_POSTMASTER	Mail response to the postmaster.
11764565Sgshapiro	**	ESM_DEADLETTERDROP
11864565Sgshapiro	**			If DeadLetterDrop set, save it there.
11938032Speter	**	ESM_PANIC	Save response anywhere possible.
12038032Speter	*/
12138032Speter
12238032Speter	/* determine starting state */
12338032Speter	switch (e->e_errormode)
12438032Speter	{
12538032Speter	  case EM_WRITE:
12638032Speter		state = ESM_REPORT;
12738032Speter		break;
12838032Speter
12938032Speter	  case EM_BERKNET:
13038032Speter	  case EM_MAIL:
13138032Speter		state = ESM_MAIL;
13238032Speter		break;
13338032Speter
13438032Speter	  case EM_PRINT:
13538032Speter	  case '\0':
13638032Speter		state = ESM_QUIET;
13738032Speter		break;
13838032Speter
13938032Speter	  case EM_QUIET:
14038032Speter		/* no need to return anything at all */
14190795Sgshapiro		return panic;
14238032Speter
14338032Speter	  default:
14490795Sgshapiro		syserr("554 5.3.0 savemail: bogus errormode x%x",
14564565Sgshapiro		       e->e_errormode);
14638032Speter		state = ESM_MAIL;
14738032Speter		break;
14838032Speter	}
14938032Speter
15038032Speter	/* if this is already an error response, send to postmaster */
15138032Speter	if (bitset(EF_RESPONSE, e->e_flags))
15238032Speter	{
15338032Speter		if (e->e_parent != NULL &&
15438032Speter		    bitset(EF_RESPONSE, e->e_parent->e_flags))
15538032Speter		{
15638032Speter			/* got an error sending a response -- can it */
15790795Sgshapiro			return panic;
15838032Speter		}
15938032Speter		state = ESM_POSTMASTER;
16038032Speter	}
16138032Speter
16238032Speter	while (state != ESM_DONE)
16338032Speter	{
16438032Speter		if (tTd(6, 5))
16590795Sgshapiro			sm_dprintf("  state %d\n", state);
16638032Speter
16738032Speter		switch (state)
16838032Speter		{
16938032Speter		  case ESM_QUIET:
17038032Speter			if (bitnset(M_LOCALMAILER, e->e_from.q_mailer->m_flags))
17138032Speter				state = ESM_DEADLETTER;
17238032Speter			else
17338032Speter				state = ESM_MAIL;
17438032Speter			break;
17538032Speter
17638032Speter		  case ESM_REPORT:
17738032Speter
17838032Speter			/*
17938032Speter			**  If the user is still logged in on the same terminal,
18038032Speter			**  then write the error messages back to hir (sic).
18138032Speter			*/
18238032Speter
183132946Sgshapiro#if USE_TTYPATH
18438032Speter			p = ttypath();
185132946Sgshapiro#else /* USE_TTYPATH */
186132946Sgshapiro			p = NULL;
187132946Sgshapiro#endif /* USE_TTYPATH */
188132946Sgshapiro
18990795Sgshapiro			if (p == NULL || sm_io_reopen(SmFtStdio,
19090795Sgshapiro						      SM_TIME_DEFAULT,
19190795Sgshapiro						      p, SM_IO_WRONLY, NULL,
19290795Sgshapiro						      smioout) == NULL)
19338032Speter			{
19438032Speter				state = ESM_MAIL;
19538032Speter				break;
19638032Speter			}
19738032Speter
19838032Speter			expand("\201n", buf, sizeof buf, e);
19990795Sgshapiro			(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
20090795Sgshapiro					     "\r\nMessage from %s...\r\n", buf);
20190795Sgshapiro			(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
20290795Sgshapiro					     "Errors occurred while sending mail.\r\n");
20338032Speter			if (e->e_xfp != NULL)
20438032Speter			{
20564565Sgshapiro				(void) bfrewind(e->e_xfp);
20690795Sgshapiro				(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
20790795Sgshapiro						     "Transcript follows:\r\n");
20890795Sgshapiro				while (sm_io_fgets(e->e_xfp, SM_TIME_DEFAULT,
20990795Sgshapiro						   buf, sizeof buf) != NULL &&
21090795Sgshapiro				       !sm_io_error(smioout))
21190795Sgshapiro					(void) sm_io_fputs(smioout,
21290795Sgshapiro							   SM_TIME_DEFAULT,
21390795Sgshapiro							   buf);
21438032Speter			}
21538032Speter			else
21638032Speter			{
21790795Sgshapiro				syserr("Cannot open %s",
21890795Sgshapiro				       queuename(e, XSCRPT_LETTER));
21990795Sgshapiro				(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
22090795Sgshapiro						     "Transcript of session is unavailable.\r\n");
22138032Speter			}
22290795Sgshapiro			(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
22390795Sgshapiro					     "Original message will be saved in dead.letter.\r\n");
22438032Speter			state = ESM_DEADLETTER;
22538032Speter			break;
22638032Speter
22738032Speter		  case ESM_MAIL:
22838032Speter			/*
22938032Speter			**  If mailing back, do it.
23038032Speter			**	Throw away all further output.  Don't alias,
23138032Speter			**	since this could cause loops, e.g., if joe
23238032Speter			**	mails to joe@x, and for some reason the network
23338032Speter			**	for @x is down, then the response gets sent to
23438032Speter			**	joe@x, which gives a response, etc.  Also force
23538032Speter			**	the mail to be delivered even if a version of
23638032Speter			**	it has already been sent to the sender.
23738032Speter			**
23838032Speter			**  If this is a configuration or local software
23938032Speter			**	error, send to the local postmaster as well,
24038032Speter			**	since the originator can't do anything
24138032Speter			**	about it anyway.  Note that this is a full
24238032Speter			**	copy of the message (intentionally) so that
24338032Speter			**	the Postmaster can forward things along.
24438032Speter			*/
24538032Speter
24638032Speter			if (ExitStat == EX_CONFIG || ExitStat == EX_SOFTWARE)
24738032Speter			{
24890795Sgshapiro				(void) sendtolist("postmaster", NULLADDR,
24990795Sgshapiro						  &e->e_errorqueue, 0, e);
25038032Speter			}
25138032Speter			if (!emptyaddr(&e->e_from))
25238032Speter			{
25338032Speter				char from[TOBUFSIZE];
25438032Speter
25590795Sgshapiro				if (sm_strlcpy(from, e->e_from.q_paddr,
25690795Sgshapiro						sizeof from) >= sizeof from)
25738032Speter				{
25838032Speter					state = ESM_POSTMASTER;
25938032Speter					break;
26038032Speter				}
26138032Speter
26290795Sgshapiro				if (!DontPruneRoutes)
26390795Sgshapiro					(void) pruneroute(from);
26438032Speter
26538032Speter				(void) sendtolist(from, NULLADDR,
26638032Speter						  &e->e_errorqueue, 0, e);
26738032Speter			}
26838032Speter
26938032Speter			/*
27038032Speter			**  Deliver a non-delivery report to the
27138032Speter			**  Postmaster-designate (not necessarily
27238032Speter			**  Postmaster).  This does not include the
27338032Speter			**  body of the message, for privacy reasons.
27438032Speter			**  You really shouldn't need this.
27538032Speter			*/
27638032Speter
27738032Speter			e->e_flags |= EF_PM_NOTIFY;
27838032Speter
27938032Speter			/* check to see if there are any good addresses */
28038032Speter			for (q = e->e_errorqueue; q != NULL; q = q->q_next)
28164565Sgshapiro			{
28264565Sgshapiro				if (QS_IS_SENDABLE(q->q_state))
28338032Speter					break;
28464565Sgshapiro			}
28538032Speter			if (q == NULL)
28638032Speter			{
28738032Speter				/* this is an error-error */
28838032Speter				state = ESM_POSTMASTER;
28938032Speter				break;
29038032Speter			}
29138032Speter			if (returntosender(e->e_message, e->e_errorqueue,
29238032Speter					   sendbody ? RTSF_SEND_BODY
29338032Speter						    : RTSF_NO_BODY,
29438032Speter					   e) == 0)
29538032Speter			{
29638032Speter				state = ESM_DONE;
29738032Speter				break;
29838032Speter			}
29938032Speter
30038032Speter			/* didn't work -- return to postmaster */
30138032Speter			state = ESM_POSTMASTER;
30238032Speter			break;
30338032Speter
30438032Speter		  case ESM_POSTMASTER:
30538032Speter			/*
30638032Speter			**  Similar to previous case, but to system postmaster.
30738032Speter			*/
30838032Speter
30938032Speter			q = NULL;
31064565Sgshapiro			expand(DoubleBounceAddr, buf, sizeof buf, e);
31190795Sgshapiro
31290795Sgshapiro			/*
31390795Sgshapiro			**  Just drop it on the floor if DoubleBounceAddr
31490795Sgshapiro			**  expands to an empty string.
31590795Sgshapiro			*/
31690795Sgshapiro
31790795Sgshapiro			if (*buf == '\0')
31890795Sgshapiro			{
31990795Sgshapiro				state = ESM_DONE;
32090795Sgshapiro				break;
32190795Sgshapiro			}
32264565Sgshapiro			if (sendtolist(buf, NULLADDR, &q, 0, e) <= 0)
32338032Speter			{
32464565Sgshapiro				syserr("553 5.3.0 cannot parse %s!", buf);
32538032Speter				ExitStat = EX_SOFTWARE;
32664565Sgshapiro				state = ESM_DEADLETTERDROP;
32738032Speter				break;
32838032Speter			}
32938032Speter			flags = RTSF_PM_BOUNCE;
33038032Speter			if (sendbody)
33138032Speter				flags |= RTSF_SEND_BODY;
33238032Speter			if (returntosender(e->e_message, q, flags, e) == 0)
33338032Speter			{
33438032Speter				state = ESM_DONE;
33538032Speter				break;
33638032Speter			}
33738032Speter
33838032Speter			/* didn't work -- last resort */
33964565Sgshapiro			state = ESM_DEADLETTERDROP;
34038032Speter			break;
34138032Speter
34238032Speter		  case ESM_DEADLETTER:
34338032Speter			/*
34438032Speter			**  Save the message in dead.letter.
34538032Speter			**	If we weren't mailing back, and the user is
34638032Speter			**	local, we should save the message in
34738032Speter			**	~/dead.letter so that the poor person doesn't
34838032Speter			**	have to type it over again -- and we all know
34938032Speter			**	what poor typists UNIX users are.
35038032Speter			*/
35138032Speter
35238032Speter			p = NULL;
35338032Speter			if (bitnset(M_HASPWENT, e->e_from.q_mailer->m_flags))
35438032Speter			{
35538032Speter				if (e->e_from.q_home != NULL)
35638032Speter					p = e->e_from.q_home;
35790795Sgshapiro				else if (sm_mbdb_lookup(e->e_from.q_user, &user)
35890795Sgshapiro					 == EX_OK &&
35990795Sgshapiro					 *user.mbdb_homedir != '\0')
36090795Sgshapiro					p = user.mbdb_homedir;
36138032Speter			}
36238032Speter			if (p == NULL || e->e_dfp == NULL)
36338032Speter			{
36438032Speter				/* no local directory or no data file */
36538032Speter				state = ESM_MAIL;
36638032Speter				break;
36738032Speter			}
36838032Speter
36938032Speter			/* we have a home directory; write dead.letter */
37090795Sgshapiro			macdefine(&e->e_macro, A_TEMP, 'z', p);
37164565Sgshapiro
37264565Sgshapiro			/* get the sender for the UnixFromLine */
37364565Sgshapiro			p = macvalue('g', e);
37490795Sgshapiro			macdefine(&e->e_macro, A_PERM, 'g', e->e_sender);
37564565Sgshapiro
37698125Sgshapiro			expand("\201z/dead.letter", dlbuf, sizeof dlbuf, e);
37764565Sgshapiro			sff = SFF_CREAT|SFF_REGONLY|SFF_RUNASREALUID;
37838032Speter			if (RealUid == 0)
37964565Sgshapiro				sff |= SFF_ROOTOK;
38098125Sgshapiro			e->e_to = dlbuf;
38198125Sgshapiro			if (writable(dlbuf, NULL, sff) &&
38298125Sgshapiro			    mailfile(dlbuf, FileMailer, NULL, sff, e) == EX_OK)
38338032Speter			{
38438032Speter				int oldverb = Verbose;
38538032Speter
38664565Sgshapiro				if (OpMode != MD_DAEMON && OpMode != MD_SMTP)
38764565Sgshapiro					Verbose = 1;
38864565Sgshapiro				if (Verbose > 0)
38998125Sgshapiro					message("Saved message in %s", dlbuf);
39038032Speter				Verbose = oldverb;
39190795Sgshapiro				macdefine(&e->e_macro, A_PERM, 'g', p);
39238032Speter				state = ESM_DONE;
39338032Speter				break;
39438032Speter			}
39590795Sgshapiro			macdefine(&e->e_macro, A_PERM, 'g', p);
39638032Speter			state = ESM_MAIL;
39738032Speter			break;
39838032Speter
39964565Sgshapiro		  case ESM_DEADLETTERDROP:
40038032Speter			/*
40164565Sgshapiro			**  Log the mail in DeadLetterDrop file.
40238032Speter			*/
40338032Speter
40438032Speter			if (e->e_class < 0)
40538032Speter			{
40638032Speter				state = ESM_DONE;
40738032Speter				break;
40838032Speter			}
40938032Speter
41038032Speter			if ((SafeFileEnv != NULL && SafeFileEnv[0] != '\0') ||
41164565Sgshapiro			    DeadLetterDrop == NULL ||
41264565Sgshapiro			    DeadLetterDrop[0] == '\0')
41338032Speter			{
41438032Speter				state = ESM_PANIC;
41538032Speter				break;
41638032Speter			}
41738032Speter
41864565Sgshapiro			sff = SFF_CREAT|SFF_REGONLY|SFF_ROOTOK|SFF_OPENASROOT|SFF_MUSTOWN;
41964565Sgshapiro			if (!writable(DeadLetterDrop, NULL, sff) ||
42038032Speter			    (fp = safefopen(DeadLetterDrop, O_WRONLY|O_APPEND,
42164565Sgshapiro					    FileMode, sff)) == NULL)
42238032Speter			{
42338032Speter				state = ESM_PANIC;
42438032Speter				break;
42538032Speter			}
42638032Speter
42764565Sgshapiro			memset(&mcibuf, '\0', sizeof mcibuf);
42838032Speter			mcibuf.mci_out = fp;
42938032Speter			mcibuf.mci_mailer = FileMailer;
43038032Speter			if (bitnset(M_7BITS, FileMailer->m_flags))
43138032Speter				mcibuf.mci_flags |= MCIF_7BIT;
43238032Speter
43364565Sgshapiro			/* get the sender for the UnixFromLine */
43464565Sgshapiro			p = macvalue('g', e);
43590795Sgshapiro			macdefine(&e->e_macro, A_PERM, 'g', e->e_sender);
43664565Sgshapiro
437157006Sgshapiro			if (!putfromline(&mcibuf, e) ||
438157006Sgshapiro			    !(*e->e_puthdr)(&mcibuf, e->e_header, e,
439157006Sgshapiro					M87F_OUTER) ||
440157006Sgshapiro			    !(*e->e_putbody)(&mcibuf, e, NULL) ||
441157006Sgshapiro			    !putline("\n", &mcibuf) ||
442157006Sgshapiro			    sm_io_flush(fp, SM_TIME_DEFAULT) == SM_IO_EOF ||
443157006Sgshapiro			    sm_io_error(fp) ||
44490795Sgshapiro			    sm_io_close(fp, SM_TIME_DEFAULT) < 0)
44538032Speter				state = ESM_PANIC;
44638032Speter			else
44738032Speter			{
44838032Speter				int oldverb = Verbose;
44938032Speter
45064565Sgshapiro				if (OpMode != MD_DAEMON && OpMode != MD_SMTP)
45164565Sgshapiro					Verbose = 1;
45264565Sgshapiro				if (Verbose > 0)
45364565Sgshapiro					message("Saved message in %s",
45464565Sgshapiro						DeadLetterDrop);
45538032Speter				Verbose = oldverb;
45638032Speter				if (LogLevel > 3)
45738032Speter					sm_syslog(LOG_NOTICE, e->e_id,
45864565Sgshapiro						  "Saved message in %s",
45964565Sgshapiro						  DeadLetterDrop);
46038032Speter				state = ESM_DONE;
46138032Speter			}
46290795Sgshapiro			macdefine(&e->e_macro, A_PERM, 'g', p);
46338032Speter			break;
46438032Speter
46538032Speter		  default:
46664565Sgshapiro			syserr("554 5.3.5 savemail: unknown state %d", state);
46764565Sgshapiro			/* FALLTHROUGH */
46838032Speter
46938032Speter		  case ESM_PANIC:
47038032Speter			/* leave the locked queue & transcript files around */
47138032Speter			loseqfile(e, "savemail panic");
47290795Sgshapiro			panic = true;
47366497Sgshapiro			errno = 0;
47490795Sgshapiro			syserr("554 savemail: cannot save rejected email anywhere");
47590795Sgshapiro			state = ESM_DONE;
47690795Sgshapiro			break;
47738032Speter		}
47838032Speter	}
47990795Sgshapiro	return panic;
48038032Speter}
48190795Sgshapiro/*
48238032Speter**  RETURNTOSENDER -- return a message to the sender with an error.
48338032Speter**
48438032Speter**	Parameters:
48538032Speter**		msg -- the explanatory message.
48638032Speter**		returnq -- the queue of people to send the message to.
48738032Speter**		flags -- flags tweaking the operation:
48838032Speter**			RTSF_SENDBODY -- include body of message (otherwise
48938032Speter**				just send the header).
49038032Speter**			RTSF_PMBOUNCE -- this is a postmaster bounce.
49138032Speter**		e -- the current envelope.
49238032Speter**
49338032Speter**	Returns:
49438032Speter**		zero -- if everything went ok.
49538032Speter**		else -- some error.
49638032Speter**
49738032Speter**	Side Effects:
49890795Sgshapiro**		Returns the current message to the sender via mail.
49938032Speter*/
50038032Speter
50138032Speter#define MAXRETURNS	6	/* max depth of returning messages */
50290795Sgshapiro#define ERRORFUDGE	1024	/* nominal size of error message text */
50338032Speter
50438032Speterint
50538032Speterreturntosender(msg, returnq, flags, e)
50638032Speter	char *msg;
50738032Speter	ADDRESS *returnq;
50838032Speter	int flags;
50938032Speter	register ENVELOPE *e;
51038032Speter{
51138032Speter	register ENVELOPE *ee;
51238032Speter	ENVELOPE *oldcur = CurEnv;
51338032Speter	ENVELOPE errenvelope;
51438032Speter	static int returndepth = 0;
51538032Speter	register ADDRESS *q;
51638032Speter	char *p;
51738032Speter	char buf[MAXNAME + 1];
51838032Speter
51938032Speter	if (returnq == NULL)
52064565Sgshapiro		return -1;
52138032Speter
52238032Speter	if (msg == NULL)
52338032Speter		msg = "Unable to deliver mail";
52438032Speter
52538032Speter	if (tTd(6, 1))
52638032Speter	{
52790795Sgshapiro		sm_dprintf("\n*** Return To Sender: msg=\"%s\", depth=%d, e=%p, returnq=",
52890795Sgshapiro			msg, returndepth, e);
529132946Sgshapiro		printaddr(sm_debug_file(), returnq, true);
53038032Speter		if (tTd(6, 20))
53138032Speter		{
53290795Sgshapiro			sm_dprintf("Sendq=");
533132946Sgshapiro			printaddr(sm_debug_file(), e->e_sendqueue, true);
53438032Speter		}
53538032Speter	}
53638032Speter
53738032Speter	if (++returndepth >= MAXRETURNS)
53838032Speter	{
53938032Speter		if (returndepth != MAXRETURNS)
54064565Sgshapiro			syserr("554 5.3.0 returntosender: infinite recursion on %s",
54164565Sgshapiro			       returnq->q_paddr);
54238032Speter		/* don't "unrecurse" and fake a clean exit */
54338032Speter		/* returndepth--; */
54464565Sgshapiro		return 0;
54538032Speter	}
54638032Speter
54790795Sgshapiro	macdefine(&e->e_macro, A_PERM, 'g', e->e_sender);
54890795Sgshapiro	macdefine(&e->e_macro, A_PERM, 'u', NULL);
54938032Speter
55038032Speter	/* initialize error envelope */
55190795Sgshapiro	ee = newenvelope(&errenvelope, e, sm_rpool_new_x(NULL));
55290795Sgshapiro	macdefine(&ee->e_macro, A_PERM, 'a', "\201b");
55390795Sgshapiro	macdefine(&ee->e_macro, A_PERM, 'r', "");
55490795Sgshapiro	macdefine(&ee->e_macro, A_PERM, 's', "localhost");
55590795Sgshapiro	macdefine(&ee->e_macro, A_PERM, '_', "localhost");
556110563Sgshapiro	clrsessenvelope(ee);
55764565Sgshapiro
55838032Speter	ee->e_puthdr = putheader;
55938032Speter	ee->e_putbody = errbody;
56038032Speter	ee->e_flags |= EF_RESPONSE|EF_METOO;
56138032Speter	if (!bitset(EF_OLDSTYLE, e->e_flags))
56238032Speter		ee->e_flags &= ~EF_OLDSTYLE;
56364565Sgshapiro	if (bitset(EF_DONT_MIME, e->e_flags))
56464565Sgshapiro	{
56564565Sgshapiro		ee->e_flags |= EF_DONT_MIME;
56664565Sgshapiro
56764565Sgshapiro		/*
56864565Sgshapiro		**  If we can't convert to MIME and we don't pass
56964565Sgshapiro		**  8-bit, we can't send the body.
57064565Sgshapiro		*/
57164565Sgshapiro
57264565Sgshapiro		if (bitset(EF_HAS8BIT, e->e_flags) &&
57364565Sgshapiro		    !bitset(MM_PASS8BIT, MimeMode))
57464565Sgshapiro			flags &= ~RTSF_SEND_BODY;
57564565Sgshapiro	}
57664565Sgshapiro
57738032Speter	ee->e_sendqueue = returnq;
57890795Sgshapiro	ee->e_msgsize = 0;
57964565Sgshapiro	if (bitset(RTSF_SEND_BODY, flags) &&
58064565Sgshapiro	    !bitset(PRIV_NOBODYRETN, PrivacyFlags))
58190795Sgshapiro		ee->e_msgsize = ERRORFUDGE + e->e_msgsize;
58238032Speter	else
58338032Speter		ee->e_flags |= EF_NO_BODY_RETN;
58490795Sgshapiro
58590795Sgshapiro	if (!setnewqueue(ee))
58690795Sgshapiro	{
58790795Sgshapiro		syserr("554 5.3.0 returntosender: cannot select queue for %s",
58890795Sgshapiro			       returnq->q_paddr);
58990795Sgshapiro		ExitStat = EX_UNAVAILABLE;
59090795Sgshapiro		returndepth--;
59190795Sgshapiro		return -1;
59290795Sgshapiro	}
59338032Speter	initsys(ee);
59464565Sgshapiro
59564565Sgshapiro#if NAMED_BIND
59664565Sgshapiro	_res.retry = TimeOuts.res_retry[RES_TO_FIRST];
59764565Sgshapiro	_res.retrans = TimeOuts.res_retrans[RES_TO_FIRST];
59864565Sgshapiro#endif /* NAMED_BIND */
59938032Speter	for (q = returnq; q != NULL; q = q->q_next)
60038032Speter	{
60164565Sgshapiro		if (QS_IS_BADADDR(q->q_state))
60238032Speter			continue;
60338032Speter
60438032Speter		q->q_flags &= ~(QHASNOTIFY|Q_PINGFLAGS);
60538032Speter		q->q_flags |= QPINGONFAILURE;
60638032Speter
60764565Sgshapiro		if (!QS_IS_DEAD(q->q_state))
60838032Speter			ee->e_nrcpts++;
60938032Speter
61038032Speter		if (q->q_alias == NULL)
61190795Sgshapiro			addheader("To", q->q_paddr, 0, ee);
61238032Speter	}
61338032Speter
61438032Speter	if (LogLevel > 5)
61538032Speter	{
61664565Sgshapiro		if (bitset(EF_RESPONSE, e->e_flags))
61738032Speter			p = "return to sender";
61864565Sgshapiro		else if (bitset(EF_WARNING, e->e_flags))
61964565Sgshapiro			p = "sender notify";
62038032Speter		else if (bitset(RTSF_PM_BOUNCE, flags))
62138032Speter			p = "postmaster notify";
62238032Speter		else
62338032Speter			p = "DSN";
62490795Sgshapiro		sm_syslog(LOG_INFO, e->e_id, "%s: %s: %s",
62564565Sgshapiro			  ee->e_id, p, shortenstring(msg, MAXSHORTSTR));
62638032Speter	}
62738032Speter
62838032Speter	if (SendMIMEErrors)
62938032Speter	{
63090795Sgshapiro		addheader("MIME-Version", "1.0", 0, ee);
63190795Sgshapiro		(void) sm_snprintf(buf, sizeof buf, "%s.%ld/%.100s",
63290795Sgshapiro				ee->e_id, (long)curtime(), MyHostName);
63390795Sgshapiro		ee->e_msgboundary = sm_rpool_strdup_x(ee->e_rpool, buf);
63490795Sgshapiro		(void) sm_snprintf(buf, sizeof buf,
63538032Speter#if DSN
63664565Sgshapiro				"multipart/report; report-type=delivery-status;\n\tboundary=\"%s\"",
63764565Sgshapiro#else /* DSN */
63864565Sgshapiro				"multipart/mixed; boundary=\"%s\"",
63964565Sgshapiro#endif /* DSN */
64064565Sgshapiro				ee->e_msgboundary);
64190795Sgshapiro		addheader("Content-Type", buf, 0, ee);
64238032Speter
64338032Speter		p = hvalue("Content-Transfer-Encoding", e->e_header);
64490795Sgshapiro		if (p != NULL && sm_strcasecmp(p, "binary") != 0)
64538032Speter			p = NULL;
64638032Speter		if (p == NULL && bitset(EF_HAS8BIT, e->e_flags))
64738032Speter			p = "8bit";
64838032Speter		if (p != NULL)
64990795Sgshapiro			addheader("Content-Transfer-Encoding", p, 0, ee);
65038032Speter	}
65138032Speter	if (strncmp(msg, "Warning:", 8) == 0)
65238032Speter	{
65390795Sgshapiro		addheader("Subject", msg, 0, ee);
65438032Speter		p = "warning-timeout";
65538032Speter	}
65638032Speter	else if (strncmp(msg, "Postmaster warning:", 19) == 0)
65738032Speter	{
65890795Sgshapiro		addheader("Subject", msg, 0, ee);
65938032Speter		p = "postmaster-warning";
66038032Speter	}
66138032Speter	else if (strcmp(msg, "Return receipt") == 0)
66238032Speter	{
66390795Sgshapiro		addheader("Subject", msg, 0, ee);
66438032Speter		p = "return-receipt";
66538032Speter	}
66638032Speter	else if (bitset(RTSF_PM_BOUNCE, flags))
66738032Speter	{
66890795Sgshapiro		(void) sm_snprintf(buf, sizeof buf,
66964565Sgshapiro			 "Postmaster notify: see transcript for details");
67090795Sgshapiro		addheader("Subject", buf, 0, ee);
67138032Speter		p = "postmaster-notification";
67238032Speter	}
67338032Speter	else
67438032Speter	{
67590795Sgshapiro		(void) sm_snprintf(buf, sizeof buf,
67664565Sgshapiro			 "Returned mail: see transcript for details");
67790795Sgshapiro		addheader("Subject", buf, 0, ee);
67838032Speter		p = "failure";
67938032Speter	}
68090795Sgshapiro	(void) sm_snprintf(buf, sizeof buf, "auto-generated (%s)", p);
68190795Sgshapiro	addheader("Auto-Submitted", buf, 0, ee);
68238032Speter
68338032Speter	/* fake up an address header for the from person */
68438032Speter	expand("\201n", buf, sizeof buf, e);
68564565Sgshapiro	if (parseaddr(buf, &ee->e_from,
68690795Sgshapiro		      RF_COPYALL|RF_SENDERADDR, '\0', NULL, e, false) == NULL)
68738032Speter	{
68864565Sgshapiro		syserr("553 5.3.5 Can't parse myself!");
68938032Speter		ExitStat = EX_SOFTWARE;
69038032Speter		returndepth--;
69164565Sgshapiro		return -1;
69238032Speter	}
69338032Speter	ee->e_from.q_flags &= ~(QHASNOTIFY|Q_PINGFLAGS);
69438032Speter	ee->e_from.q_flags |= QPINGONFAILURE;
69538032Speter	ee->e_sender = ee->e_from.q_paddr;
69638032Speter
69738032Speter	/* push state into submessage */
69838032Speter	CurEnv = ee;
69990795Sgshapiro	macdefine(&ee->e_macro, A_PERM, 'f', "\201n");
70090795Sgshapiro	macdefine(&ee->e_macro, A_PERM, 'x', "Mail Delivery Subsystem");
70190795Sgshapiro	eatheader(ee, true, true);
70238032Speter
70338032Speter	/* mark statistics */
70490795Sgshapiro	markstats(ee, NULLADDR, STATS_NORMAL);
70538032Speter
70638032Speter	/* actually deliver the error message */
70738032Speter	sendall(ee, SM_DELIVER);
70838032Speter
70938032Speter	/* restore state */
71090795Sgshapiro	dropenvelope(ee, true, false);
71190795Sgshapiro	sm_rpool_free(ee->e_rpool);
71238032Speter	CurEnv = oldcur;
71338032Speter	returndepth--;
71438032Speter
71538032Speter	/* check for delivery errors */
71664565Sgshapiro	if (ee->e_parent == NULL ||
71764565Sgshapiro	    !bitset(EF_RESPONSE, ee->e_parent->e_flags))
71838032Speter		return 0;
71938032Speter	for (q = ee->e_sendqueue; q != NULL; q = q->q_next)
72038032Speter	{
72164565Sgshapiro		if (QS_IS_ATTEMPTED(q->q_state))
72238032Speter			return 0;
72338032Speter	}
72438032Speter	return -1;
72538032Speter}
72690795Sgshapiro/*
72738032Speter**  ERRBODY -- output the body of an error message.
72838032Speter**
72938032Speter**	Typically this is a copy of the transcript plus a copy of the
73038032Speter**	original offending message.
73138032Speter**
73238032Speter**	Parameters:
73338032Speter**		mci -- the mailer connection information.
73438032Speter**		e -- the envelope we are working in.
73590795Sgshapiro**		separator -- any possible MIME separator (unused).
73638032Speter**
73738032Speter**	Returns:
738159613Sgshapiro**		true iff body was written successfully
73938032Speter**
74038032Speter**	Side Effects:
74138032Speter**		Outputs the body of an error message.
74238032Speter*/
74338032Speter
74464565Sgshapiro/* ARGSUSED2 */
745157006Sgshapirostatic bool
74638032Spetererrbody(mci, e, separator)
74738032Speter	register MCI *mci;
74838032Speter	register ENVELOPE *e;
74938032Speter	char *separator;
75038032Speter{
75164565Sgshapiro	bool printheader;
75264565Sgshapiro	bool sendbody;
75364565Sgshapiro	bool pm_notify;
75464565Sgshapiro	int save_errno;
75590795Sgshapiro	register SM_FILE_T *xfile;
75638032Speter	char *p;
75738032Speter	register ADDRESS *q = NULL;
75890795Sgshapiro	char actual[MAXLINE];
75938032Speter	char buf[MAXLINE];
76038032Speter
76138032Speter	if (bitset(MCIF_INHEADER, mci->mci_flags))
76238032Speter	{
763157006Sgshapiro		if (!putline("", mci))
764157006Sgshapiro			goto writeerr;
76538032Speter		mci->mci_flags &= ~MCIF_INHEADER;
76638032Speter	}
76738032Speter	if (e->e_parent == NULL)
76838032Speter	{
76938032Speter		syserr("errbody: null parent");
770157006Sgshapiro		if (!putline("   ----- Original message lost -----\n", mci))
771157006Sgshapiro			goto writeerr;
772157006Sgshapiro		return true;
77338032Speter	}
77438032Speter
77538032Speter	/*
77638032Speter	**  Output MIME header.
77738032Speter	*/
77838032Speter
77938032Speter	if (e->e_msgboundary != NULL)
78038032Speter	{
78190795Sgshapiro		(void) sm_strlcpyn(buf, sizeof buf, 2, "--", e->e_msgboundary);
782157006Sgshapiro		if (!putline("This is a MIME-encapsulated message", mci) ||
783157006Sgshapiro		    !putline("", mci) ||
784157006Sgshapiro		    !putline(buf, mci) ||
785157006Sgshapiro		    !putline("", mci))
786157006Sgshapiro			goto writeerr;
78738032Speter	}
78838032Speter
78938032Speter	/*
79038032Speter	**  Output introductory information.
79138032Speter	*/
79238032Speter
79390795Sgshapiro	pm_notify = false;
79438032Speter	p = hvalue("subject", e->e_header);
79538032Speter	if (p != NULL && strncmp(p, "Postmaster ", 11) == 0)
79690795Sgshapiro		pm_notify = true;
79738032Speter	else
79838032Speter	{
79938032Speter		for (q = e->e_parent->e_sendqueue; q != NULL; q = q->q_next)
80064565Sgshapiro		{
80164565Sgshapiro			if (QS_IS_BADADDR(q->q_state))
80238032Speter				break;
80364565Sgshapiro		}
80438032Speter	}
80538032Speter	if (!pm_notify && q == NULL &&
80638032Speter	    !bitset(EF_FATALERRS|EF_SENDRECEIPT, e->e_parent->e_flags))
80738032Speter	{
808157006Sgshapiro		if (!putline("    **********************************************",
809157006Sgshapiro			mci) ||
810157006Sgshapiro		    !putline("    **      THIS IS A WARNING MESSAGE ONLY      **",
811157006Sgshapiro			mci) ||
812157006Sgshapiro		    !putline("    **  YOU DO NOT NEED TO RESEND YOUR MESSAGE  **",
813157006Sgshapiro			mci) ||
814157006Sgshapiro		    !putline("    **********************************************",
815157006Sgshapiro			mci) ||
816157006Sgshapiro		    !putline("", mci))
817157006Sgshapiro			goto writeerr;
81838032Speter	}
81990795Sgshapiro	(void) sm_snprintf(buf, sizeof buf,
82090795Sgshapiro		"The original message was received at %s",
82190795Sgshapiro		arpadate(ctime(&e->e_parent->e_ctime)));
822157006Sgshapiro	if (!putline(buf, mci))
823157006Sgshapiro		goto writeerr;
82438032Speter	expand("from \201_", buf, sizeof buf, e->e_parent);
825157006Sgshapiro	if (!putline(buf, mci))
826157006Sgshapiro		goto writeerr;
82764565Sgshapiro
82864565Sgshapiro	/* include id in postmaster copies */
82964565Sgshapiro	if (pm_notify && e->e_parent->e_id != NULL)
83064565Sgshapiro	{
83190795Sgshapiro		(void) sm_strlcpyn(buf, sizeof buf, 2, "with id ",
83290795Sgshapiro			e->e_parent->e_id);
833157006Sgshapiro		if (!putline(buf, mci))
834157006Sgshapiro			goto writeerr;
83564565Sgshapiro	}
836157006Sgshapiro	if (!putline("", mci))
837157006Sgshapiro		goto writeerr;
83838032Speter
83938032Speter	/*
84038032Speter	**  Output error message header (if specified and available).
84138032Speter	*/
84238032Speter
84364565Sgshapiro	if (ErrMsgFile != NULL &&
84464565Sgshapiro	    !bitset(EF_SENDRECEIPT, e->e_parent->e_flags))
84538032Speter	{
84638032Speter		if (*ErrMsgFile == '/')
84738032Speter		{
84864565Sgshapiro			long sff = SFF_ROOTOK|SFF_REGONLY;
84938032Speter
85038032Speter			if (DontLockReadFiles)
85138032Speter				sff |= SFF_NOLOCK;
85264565Sgshapiro			if (!bitnset(DBS_ERRORHEADERINUNSAFEDIRPATH,
85364565Sgshapiro				     DontBlameSendmail))
85438032Speter				sff |= SFF_SAFEDIRPATH;
85538032Speter			xfile = safefopen(ErrMsgFile, O_RDONLY, 0444, sff);
85638032Speter			if (xfile != NULL)
85738032Speter			{
85890795Sgshapiro				while (sm_io_fgets(xfile, SM_TIME_DEFAULT, buf,
85990795Sgshapiro						   sizeof buf) != NULL)
86038032Speter				{
86138032Speter					translate_dollars(buf);
86238032Speter					expand(buf, buf, sizeof buf, e);
863157006Sgshapiro					if (!putline(buf, mci))
864157006Sgshapiro						goto writeerr;
86538032Speter				}
86690795Sgshapiro				(void) sm_io_close(xfile, SM_TIME_DEFAULT);
867157006Sgshapiro				if (!putline("\n", mci))
868157006Sgshapiro					goto writeerr;
86938032Speter			}
87038032Speter		}
87138032Speter		else
87238032Speter		{
87338032Speter			expand(ErrMsgFile, buf, sizeof buf, e);
874157006Sgshapiro			if (!putline(buf, mci) || !putline("", mci))
875157006Sgshapiro				goto writeerr;
87638032Speter		}
87738032Speter	}
87838032Speter
87938032Speter	/*
88038032Speter	**  Output message introduction
88138032Speter	*/
88238032Speter
88390795Sgshapiro	/* permanent fatal errors */
88490795Sgshapiro	printheader = true;
88538032Speter	for (q = e->e_parent->e_sendqueue; q != NULL; q = q->q_next)
88638032Speter	{
88764565Sgshapiro		if (!QS_IS_BADADDR(q->q_state) ||
88838032Speter		    !bitset(QPINGONFAILURE, q->q_flags))
88938032Speter			continue;
89038032Speter
89138032Speter		if (printheader)
89238032Speter		{
893157006Sgshapiro			if (!putline("   ----- The following addresses had permanent fatal errors -----",
894157006Sgshapiro					mci))
895157006Sgshapiro				goto writeerr;
89690795Sgshapiro			printheader = false;
89738032Speter		}
89838032Speter
89990795Sgshapiro		(void) sm_strlcpy(buf, shortenstring(q->q_paddr, MAXSHORTSTR),
90090795Sgshapiro				  sizeof buf);
901157006Sgshapiro		if (!putline(buf, mci))
902157006Sgshapiro			goto writeerr;
90364565Sgshapiro		if (q->q_rstatus != NULL)
90464565Sgshapiro		{
90590795Sgshapiro			(void) sm_snprintf(buf, sizeof buf,
90690795Sgshapiro				"    (reason: %s)",
90790795Sgshapiro				shortenstring(exitstat(q->q_rstatus),
90890795Sgshapiro					      MAXSHORTSTR));
909157006Sgshapiro			if (!putline(buf, mci))
910157006Sgshapiro				goto writeerr;
91164565Sgshapiro		}
91238032Speter		if (q->q_alias != NULL)
91338032Speter		{
91490795Sgshapiro			(void) sm_snprintf(buf, sizeof buf,
91590795Sgshapiro				"    (expanded from: %s)",
91690795Sgshapiro				shortenstring(q->q_alias->q_paddr,
91790795Sgshapiro					      MAXSHORTSTR));
918157006Sgshapiro			if (!putline(buf, mci))
919157006Sgshapiro				goto writeerr;
92038032Speter		}
92138032Speter	}
922157006Sgshapiro	if (!printheader && !putline("", mci))
923157006Sgshapiro		goto writeerr;
92438032Speter
92590795Sgshapiro	/* transient non-fatal errors */
92690795Sgshapiro	printheader = true;
92738032Speter	for (q = e->e_parent->e_sendqueue; q != NULL; q = q->q_next)
92838032Speter	{
92964565Sgshapiro		if (QS_IS_BADADDR(q->q_state) ||
93038032Speter		    !bitset(QPRIMARY, q->q_flags) ||
93190795Sgshapiro		    !bitset(QBYNDELAY, q->q_flags) ||
93238032Speter		    !bitset(QDELAYED, q->q_flags))
93338032Speter			continue;
93438032Speter
93538032Speter		if (printheader)
93638032Speter		{
937157006Sgshapiro			if (!putline("   ----- The following addresses had transient non-fatal errors -----",
938157006Sgshapiro					mci))
939157006Sgshapiro				goto writeerr;
94090795Sgshapiro			printheader = false;
94138032Speter		}
94238032Speter
94390795Sgshapiro		(void) sm_strlcpy(buf, shortenstring(q->q_paddr, MAXSHORTSTR),
94490795Sgshapiro				  sizeof buf);
945157006Sgshapiro		if (!putline(buf, mci))
946157006Sgshapiro			goto writeerr;
94738032Speter		if (q->q_alias != NULL)
94838032Speter		{
94990795Sgshapiro			(void) sm_snprintf(buf, sizeof buf,
95090795Sgshapiro				"    (expanded from: %s)",
95190795Sgshapiro				shortenstring(q->q_alias->q_paddr,
95290795Sgshapiro					      MAXSHORTSTR));
953157006Sgshapiro			if (!putline(buf, mci))
954157006Sgshapiro				goto writeerr;
95538032Speter		}
95638032Speter	}
957157006Sgshapiro	if (!printheader && !putline("", mci))
958157006Sgshapiro		goto writeerr;
95938032Speter
96090795Sgshapiro	/* successful delivery notifications */
96190795Sgshapiro	printheader = true;
96238032Speter	for (q = e->e_parent->e_sendqueue; q != NULL; q = q->q_next)
96338032Speter	{
96464565Sgshapiro		if (QS_IS_BADADDR(q->q_state) ||
96538032Speter		    !bitset(QPRIMARY, q->q_flags) ||
96690795Sgshapiro		    bitset(QBYNDELAY, q->q_flags) ||
96738032Speter		    bitset(QDELAYED, q->q_flags))
96838032Speter			continue;
96990795Sgshapiro		else if (bitset(QBYNRELAY, q->q_flags))
97090795Sgshapiro			p = "Deliver-By notify: relayed";
97190795Sgshapiro		else if (bitset(QBYTRACE, q->q_flags))
97290795Sgshapiro			p = "Deliver-By trace: relayed";
97338032Speter		else if (!bitset(QPINGONSUCCESS, q->q_flags))
97438032Speter			continue;
97538032Speter		else if (bitset(QRELAYED, q->q_flags))
97638032Speter			p = "relayed to non-DSN-aware mailer";
97738032Speter		else if (bitset(QDELIVERED, q->q_flags))
97838032Speter		{
97938032Speter			if (bitset(QEXPANDED, q->q_flags))
98038032Speter				p = "successfully delivered to mailing list";
98138032Speter			else
98238032Speter				p = "successfully delivered to mailbox";
98338032Speter		}
98438032Speter		else if (bitset(QEXPANDED, q->q_flags))
98538032Speter			p = "expanded by alias";
98638032Speter		else
98738032Speter			continue;
98838032Speter
98938032Speter		if (printheader)
99038032Speter		{
991157006Sgshapiro			if (!putline("   ----- The following addresses had successful delivery notifications -----",
992157006Sgshapiro					mci))
993157006Sgshapiro				goto writeerr;
99490795Sgshapiro			printheader = false;
99538032Speter		}
99638032Speter
99790795Sgshapiro		(void) sm_snprintf(buf, sizeof buf, "%s  (%s)",
99864565Sgshapiro			 shortenstring(q->q_paddr, MAXSHORTSTR), p);
999157006Sgshapiro		if (!putline(buf, mci))
1000157006Sgshapiro			goto writeerr;
100138032Speter		if (q->q_alias != NULL)
100238032Speter		{
100390795Sgshapiro			(void) sm_snprintf(buf, sizeof buf,
100490795Sgshapiro				"    (expanded from: %s)",
100590795Sgshapiro				shortenstring(q->q_alias->q_paddr,
100690795Sgshapiro					      MAXSHORTSTR));
1007157006Sgshapiro			if (!putline(buf, mci))
1008157006Sgshapiro				goto writeerr;
100938032Speter		}
101038032Speter	}
1011157006Sgshapiro	if (!printheader && !putline("", mci))
1012157006Sgshapiro		goto writeerr;
101338032Speter
101438032Speter	/*
101538032Speter	**  Output transcript of errors
101638032Speter	*/
101738032Speter
101890795Sgshapiro	(void) sm_io_flush(smioout, SM_TIME_DEFAULT);
101964565Sgshapiro	if (e->e_parent->e_xfp == NULL)
102038032Speter	{
1021157006Sgshapiro		if (!putline("   ----- Transcript of session is unavailable -----\n",
1022157006Sgshapiro				mci))
1023157006Sgshapiro			goto writeerr;
102438032Speter	}
102538032Speter	else
102638032Speter	{
102790795Sgshapiro		printheader = true;
102864565Sgshapiro		(void) bfrewind(e->e_parent->e_xfp);
102938032Speter		if (e->e_xfp != NULL)
103090795Sgshapiro			(void) sm_io_flush(e->e_xfp, SM_TIME_DEFAULT);
103190795Sgshapiro		while (sm_io_fgets(e->e_parent->e_xfp, SM_TIME_DEFAULT, buf,
103290795Sgshapiro				   sizeof buf) != NULL)
103338032Speter		{
1034157006Sgshapiro			if (printheader && !putline("   ----- Transcript of session follows -----\n",
1035157006Sgshapiro						mci))
1036157006Sgshapiro				goto writeerr;
103790795Sgshapiro			printheader = false;
1038157006Sgshapiro			if (!putline(buf, mci))
1039157006Sgshapiro				goto writeerr;
104038032Speter		}
104138032Speter	}
104238032Speter	errno = 0;
104338032Speter
104438032Speter#if DSN
104538032Speter	/*
104638032Speter	**  Output machine-readable version.
104738032Speter	*/
104838032Speter
104938032Speter	if (e->e_msgboundary != NULL)
105038032Speter	{
105190795Sgshapiro		(void) sm_strlcpyn(buf, sizeof buf, 2, "--", e->e_msgboundary);
1052157006Sgshapiro		if (!putline("", mci) ||
1053157006Sgshapiro		    !putline(buf, mci) ||
1054157006Sgshapiro		    !putline("Content-Type: message/delivery-status", mci) ||
1055157006Sgshapiro		    !putline("", mci))
1056157006Sgshapiro			goto writeerr;
105738032Speter
105838032Speter		/*
105938032Speter		**  Output per-message information.
106038032Speter		*/
106138032Speter
106238032Speter		/* original envelope id from MAIL FROM: line */
106338032Speter		if (e->e_parent->e_envid != NULL)
106438032Speter		{
106590795Sgshapiro			(void) sm_snprintf(buf, sizeof buf,
106664565Sgshapiro					"Original-Envelope-Id: %.800s",
106764565Sgshapiro					xuntextify(e->e_parent->e_envid));
1068157006Sgshapiro			if (!putline(buf, mci))
1069157006Sgshapiro				goto writeerr;
107038032Speter		}
107138032Speter
107238032Speter		/* Reporting-MTA: is us (required) */
107390795Sgshapiro		(void) sm_snprintf(buf, sizeof buf,
107490795Sgshapiro				   "Reporting-MTA: dns; %.800s", MyHostName);
1075157006Sgshapiro		if (!putline(buf, mci))
1076157006Sgshapiro			goto writeerr;
107738032Speter
107838032Speter		/* DSN-Gateway: not relevant since we are not translating */
107938032Speter
108038032Speter		/* Received-From-MTA: shows where we got this message from */
108138032Speter		if (RealHostName != NULL)
108238032Speter		{
108338032Speter			/* XXX use $s for type? */
108438032Speter			if (e->e_parent->e_from.q_mailer == NULL ||
108538032Speter			    (p = e->e_parent->e_from.q_mailer->m_mtatype) == NULL)
108638032Speter				p = "dns";
108790795Sgshapiro			(void) sm_snprintf(buf, sizeof buf,
108864565Sgshapiro					"Received-From-MTA: %s; %.800s",
108964565Sgshapiro					p, RealHostName);
1090157006Sgshapiro			if (!putline(buf, mci))
1091157006Sgshapiro				goto writeerr;
109238032Speter		}
109338032Speter
109438032Speter		/* Arrival-Date: -- when it arrived here */
109590795Sgshapiro		(void) sm_strlcpyn(buf, sizeof buf, 2, "Arrival-Date: ",
109664565Sgshapiro				arpadate(ctime(&e->e_parent->e_ctime)));
1097157006Sgshapiro		if (!putline(buf, mci))
1098157006Sgshapiro			goto writeerr;
109938032Speter
110090795Sgshapiro		/* Deliver-By-Date: -- when it should have been delivered */
110190795Sgshapiro		if (IS_DLVR_BY(e->e_parent))
110290795Sgshapiro		{
110390795Sgshapiro			time_t dbyd;
110490795Sgshapiro
110590795Sgshapiro			dbyd = e->e_parent->e_ctime + e->e_parent->e_deliver_by;
110690795Sgshapiro			(void) sm_strlcpyn(buf, sizeof buf, 2,
110790795Sgshapiro					"Deliver-By-Date: ",
110890795Sgshapiro					arpadate(ctime(&dbyd)));
1109157006Sgshapiro			if (!putline(buf, mci))
1110157006Sgshapiro				goto writeerr;
111190795Sgshapiro		}
111290795Sgshapiro
111338032Speter		/*
111438032Speter		**  Output per-address information.
111538032Speter		*/
111638032Speter
111738032Speter		for (q = e->e_parent->e_sendqueue; q != NULL; q = q->q_next)
111838032Speter		{
111938032Speter			char *action;
112038032Speter
112164565Sgshapiro			if (QS_IS_BADADDR(q->q_state))
112271348Sgshapiro			{
112371348Sgshapiro				/* RFC 1891, 6.2.6 (b) */
112471348Sgshapiro				if (bitset(QHASNOTIFY, q->q_flags) &&
112571348Sgshapiro				    !bitset(QPINGONFAILURE, q->q_flags))
112671348Sgshapiro					continue;
112738032Speter				action = "failed";
112871348Sgshapiro			}
112938032Speter			else if (!bitset(QPRIMARY, q->q_flags))
113038032Speter				continue;
113138032Speter			else if (bitset(QDELIVERED, q->q_flags))
113238032Speter			{
113338032Speter				if (bitset(QEXPANDED, q->q_flags))
113438032Speter					action = "delivered (to mailing list)";
113538032Speter				else
113638032Speter					action = "delivered (to mailbox)";
113738032Speter			}
113838032Speter			else if (bitset(QRELAYED, q->q_flags))
113938032Speter				action = "relayed (to non-DSN-aware mailer)";
114038032Speter			else if (bitset(QEXPANDED, q->q_flags))
114138032Speter				action = "expanded (to multi-recipient alias)";
114238032Speter			else if (bitset(QDELAYED, q->q_flags))
114338032Speter				action = "delayed";
114490795Sgshapiro			else if (bitset(QBYTRACE, q->q_flags))
114590795Sgshapiro				action = "relayed (Deliver-By trace mode)";
114690795Sgshapiro			else if (bitset(QBYNDELAY, q->q_flags))
114790795Sgshapiro				action = "delayed (Deliver-By notify mode)";
114890795Sgshapiro			else if (bitset(QBYNRELAY, q->q_flags))
114990795Sgshapiro				action = "relayed (Deliver-By notify mode)";
115038032Speter			else
115138032Speter				continue;
115238032Speter
1153157006Sgshapiro			if (!putline("", mci))
1154157006Sgshapiro				goto writeerr;
115538032Speter
115638032Speter			/* Original-Recipient: -- passed from on high */
115738032Speter			if (q->q_orcpt != NULL)
115838032Speter			{
115990795Sgshapiro				(void) sm_snprintf(buf, sizeof buf,
116064565Sgshapiro						"Original-Recipient: %.800s",
116164565Sgshapiro						q->q_orcpt);
1162157006Sgshapiro				if (!putline(buf, mci))
1163157006Sgshapiro					goto writeerr;
116438032Speter			}
116538032Speter
116690795Sgshapiro			/* Figure out actual recipient */
116790795Sgshapiro			actual[0] = '\0';
116890795Sgshapiro			if (q->q_user[0] != '\0')
116938032Speter			{
117064565Sgshapiro				if (q->q_mailer != NULL &&
117164565Sgshapiro				    q->q_mailer->m_addrtype != NULL)
117264565Sgshapiro					p = q->q_mailer->m_addrtype;
117364565Sgshapiro				else
117464565Sgshapiro					p = "rfc822";
117564565Sgshapiro
117690795Sgshapiro				if (sm_strcasecmp(p, "rfc822") == 0 &&
117764565Sgshapiro				    strchr(q->q_user, '@') == NULL)
117838032Speter				{
117990795Sgshapiro					(void) sm_snprintf(actual,
118090795Sgshapiro							   sizeof actual,
118190795Sgshapiro							   "%s; %.700s@%.100s",
118290795Sgshapiro							   p, q->q_user,
118390795Sgshapiro							   MyHostName);
118438032Speter				}
118538032Speter				else
118638032Speter				{
118790795Sgshapiro					(void) sm_snprintf(actual,
118890795Sgshapiro							   sizeof actual,
118990795Sgshapiro							   "%s; %.800s",
119090795Sgshapiro							   p, q->q_user);
119138032Speter				}
119290795Sgshapiro			}
119390795Sgshapiro
119490795Sgshapiro			/* Final-Recipient: -- the name from the RCPT command */
119590795Sgshapiro			if (q->q_finalrcpt == NULL)
119690795Sgshapiro			{
119790795Sgshapiro				/* should never happen */
119890795Sgshapiro				sm_syslog(LOG_ERR, e->e_id,
119990795Sgshapiro					  "returntosender: q_finalrcpt is NULL");
120090795Sgshapiro
120190795Sgshapiro				/* try to fall back to the actual recipient */
120290795Sgshapiro				if (actual[0] != '\0')
120390795Sgshapiro					q->q_finalrcpt = sm_rpool_strdup_x(e->e_rpool,
120490795Sgshapiro									   actual);
120590795Sgshapiro			}
120690795Sgshapiro
120790795Sgshapiro			if (q->q_finalrcpt != NULL)
120890795Sgshapiro			{
120990795Sgshapiro				(void) sm_snprintf(buf, sizeof buf,
121090795Sgshapiro						   "Final-Recipient: %s",
121190795Sgshapiro						   q->q_finalrcpt);
1212157006Sgshapiro				if (!putline(buf, mci))
1213157006Sgshapiro					goto writeerr;
121438032Speter			}
121538032Speter
121690795Sgshapiro			/* X-Actual-Recipient: -- the real problem address */
121790795Sgshapiro			if (actual[0] != '\0' &&
121890795Sgshapiro			    q->q_finalrcpt != NULL &&
1219141862Sgshapiro#if _FFR_PRIV_NOACTUALRECIPIENT
1220141862Sgshapiro			    !bitset(PRIV_NOACTUALRECIPIENT, PrivacyFlags) &&
1221141862Sgshapiro#endif /* _FFR_PRIV_NOACTUALRECIPIENT */
122290795Sgshapiro			    strcmp(actual, q->q_finalrcpt) != 0)
122390795Sgshapiro			{
122490795Sgshapiro				(void) sm_snprintf(buf, sizeof buf,
122590795Sgshapiro						   "X-Actual-Recipient: %s",
122690795Sgshapiro						   actual);
1227157006Sgshapiro				if (!putline(buf, mci))
1228157006Sgshapiro					goto writeerr;
122990795Sgshapiro			}
123090795Sgshapiro
123138032Speter			/* Action: -- what happened? */
123290795Sgshapiro			(void) sm_strlcpyn(buf, sizeof buf, 2, "Action: ",
123390795Sgshapiro				action);
1234157006Sgshapiro			if (!putline(buf, mci))
1235157006Sgshapiro				goto writeerr;
123638032Speter
123738032Speter			/* Status: -- what _really_ happened? */
123838032Speter			if (q->q_status != NULL)
123938032Speter				p = q->q_status;
124064565Sgshapiro			else if (QS_IS_BADADDR(q->q_state))
124138032Speter				p = "5.0.0";
124264565Sgshapiro			else if (QS_IS_QUEUEUP(q->q_state))
124338032Speter				p = "4.0.0";
124438032Speter			else
124538032Speter				p = "2.0.0";
124690795Sgshapiro			(void) sm_strlcpyn(buf, sizeof buf, 2, "Status: ", p);
1247157006Sgshapiro			if (!putline(buf, mci))
1248157006Sgshapiro				goto writeerr;
124938032Speter
125038032Speter			/* Remote-MTA: -- who was I talking to? */
125138032Speter			if (q->q_statmta != NULL)
125238032Speter			{
125338032Speter				if (q->q_mailer == NULL ||
125438032Speter				    (p = q->q_mailer->m_mtatype) == NULL)
125538032Speter					p = "dns";
125690795Sgshapiro				(void) sm_snprintf(buf, sizeof buf,
125764565Sgshapiro						"Remote-MTA: %s; %.800s",
125864565Sgshapiro						p, q->q_statmta);
125938032Speter				p = &buf[strlen(buf) - 1];
126038032Speter				if (*p == '.')
126138032Speter					*p = '\0';
1262157006Sgshapiro				if (!putline(buf, mci))
1263157006Sgshapiro					goto writeerr;
126438032Speter			}
126538032Speter
126638032Speter			/* Diagnostic-Code: -- actual result from other end */
126738032Speter			if (q->q_rstatus != NULL)
126838032Speter			{
1269159613Sgshapiro				if (q->q_mailer == NULL ||
1270159613Sgshapiro				    (p = q->q_mailer->m_diagtype) == NULL)
127138032Speter					p = "smtp";
127290795Sgshapiro				(void) sm_snprintf(buf, sizeof buf,
127364565Sgshapiro						"Diagnostic-Code: %s; %.800s",
127464565Sgshapiro						p, q->q_rstatus);
1275157006Sgshapiro				if (!putline(buf, mci))
1276157006Sgshapiro					goto writeerr;
127738032Speter			}
127838032Speter
127938032Speter			/* Last-Attempt-Date: -- fine granularity */
128038032Speter			if (q->q_statdate == (time_t) 0L)
128190795Sgshapiro				q->q_statdate = curtime();
128290795Sgshapiro			(void) sm_strlcpyn(buf, sizeof buf, 2,
128390795Sgshapiro					"Last-Attempt-Date: ",
128464565Sgshapiro					arpadate(ctime(&q->q_statdate)));
1285157006Sgshapiro			if (!putline(buf, mci))
1286157006Sgshapiro				goto writeerr;
128738032Speter
128838032Speter			/* Will-Retry-Until: -- for delayed messages only */
128964565Sgshapiro			if (QS_IS_QUEUEUP(q->q_state))
129038032Speter			{
129138032Speter				time_t xdate;
129238032Speter
129338032Speter				xdate = e->e_parent->e_ctime +
129438032Speter					TimeOuts.to_q_return[e->e_parent->e_timeoutclass];
129590795Sgshapiro				(void) sm_strlcpyn(buf, sizeof buf, 2,
129690795Sgshapiro					 "Will-Retry-Until: ",
129764565Sgshapiro					 arpadate(ctime(&xdate)));
1298157006Sgshapiro				if (!putline(buf, mci))
1299157006Sgshapiro					goto writeerr;
130038032Speter			}
130138032Speter		}
130238032Speter	}
130364565Sgshapiro#endif /* DSN */
130438032Speter
130538032Speter	/*
130638032Speter	**  Output text of original message
130738032Speter	*/
130838032Speter
1309157006Sgshapiro	if (!putline("", mci))
1310157006Sgshapiro		goto writeerr;
131138032Speter	if (bitset(EF_HAS_DF, e->e_parent->e_flags))
131238032Speter	{
131338032Speter		sendbody = !bitset(EF_NO_BODY_RETN, e->e_parent->e_flags) &&
131438032Speter			   !bitset(EF_NO_BODY_RETN, e->e_flags);
131538032Speter
131638032Speter		if (e->e_msgboundary == NULL)
131738032Speter		{
1318157006Sgshapiro			if (!putline(
1319157006Sgshapiro				sendbody
1320157006Sgshapiro				? "   ----- Original message follows -----\n"
1321157006Sgshapiro				: "   ----- Message header follows -----\n",
1322157006Sgshapiro				mci))
1323157006Sgshapiro			{
1324157006Sgshapiro				goto writeerr;
1325157006Sgshapiro			}
132638032Speter		}
132738032Speter		else
132838032Speter		{
132990795Sgshapiro			(void) sm_strlcpyn(buf, sizeof buf, 2, "--",
133064565Sgshapiro					e->e_msgboundary);
133138032Speter
1332157006Sgshapiro			if (!putline(buf, mci))
1333157006Sgshapiro				goto writeerr;
133490795Sgshapiro			(void) sm_strlcpyn(buf, sizeof buf, 2, "Content-Type: ",
133564565Sgshapiro					sendbody ? "message/rfc822"
133664565Sgshapiro						 : "text/rfc822-headers");
1337157006Sgshapiro			if (!putline(buf, mci))
1338157006Sgshapiro				goto writeerr;
133938032Speter
134064565Sgshapiro			p = hvalue("Content-Transfer-Encoding",
134164565Sgshapiro				   e->e_parent->e_header);
134290795Sgshapiro			if (p != NULL && sm_strcasecmp(p, "binary") != 0)
134338032Speter				p = NULL;
134464565Sgshapiro			if (p == NULL &&
134564565Sgshapiro			    bitset(EF_HAS8BIT, e->e_parent->e_flags))
134638032Speter				p = "8bit";
134738032Speter			if (p != NULL)
134838032Speter			{
134990795Sgshapiro				(void) sm_snprintf(buf, sizeof buf,
135064565Sgshapiro						"Content-Transfer-Encoding: %s",
135164565Sgshapiro						p);
1352157006Sgshapiro				if (!putline(buf, mci))
1353157006Sgshapiro					goto writeerr;
135438032Speter			}
135538032Speter		}
1356157006Sgshapiro		if (!putline("", mci))
1357157006Sgshapiro			goto writeerr;
135864565Sgshapiro		save_errno = errno;
1359157006Sgshapiro		if (!putheader(mci, e->e_parent->e_header, e->e_parent,
1360157006Sgshapiro				M87F_OUTER))
1361157006Sgshapiro			goto writeerr;
136264565Sgshapiro		errno = save_errno;
136338032Speter		if (sendbody)
1364157006Sgshapiro		{
1365157006Sgshapiro			if (!putbody(mci, e->e_parent, e->e_msgboundary))
1366157006Sgshapiro				goto writeerr;
1367157006Sgshapiro		}
136838032Speter		else if (e->e_msgboundary == NULL)
136938032Speter		{
1370157006Sgshapiro			if (!putline("", mci) ||
1371157006Sgshapiro			    !putline("   ----- Message body suppressed -----",
1372157006Sgshapiro					mci))
1373157006Sgshapiro			{
1374157006Sgshapiro				goto writeerr;
1375157006Sgshapiro			}
137638032Speter		}
137738032Speter	}
137838032Speter	else if (e->e_msgboundary == NULL)
137938032Speter	{
1380157006Sgshapiro		if (!putline("  ----- No message was collected -----\n", mci))
1381157006Sgshapiro			goto writeerr;
138238032Speter	}
138338032Speter
138438032Speter	if (e->e_msgboundary != NULL)
138538032Speter	{
138690795Sgshapiro		(void) sm_strlcpyn(buf, sizeof buf, 3, "--", e->e_msgboundary,
138790795Sgshapiro				   "--");
1388157006Sgshapiro		if (!putline("", mci) || !putline(buf, mci))
1389157006Sgshapiro			goto writeerr;
139038032Speter	}
1391157006Sgshapiro	if (!putline("", mci) ||
1392157006Sgshapiro	    sm_io_flush(mci->mci_out, SM_TIME_DEFAULT) == SM_IO_EOF)
1393157006Sgshapiro			goto writeerr;
139438032Speter
139538032Speter	/*
139638032Speter	**  Cleanup and exit
139738032Speter	*/
139838032Speter
139938032Speter	if (errno != 0)
1400157006Sgshapiro	{
1401157006Sgshapiro  writeerr:
140238032Speter		syserr("errbody: I/O error");
1403157006Sgshapiro		return false;
1404157006Sgshapiro	}
1405157006Sgshapiro	return true;
140638032Speter}
1407157006Sgshapiro
140890795Sgshapiro/*
140938032Speter**  SMTPTODSN -- convert SMTP to DSN status code
141038032Speter**
141138032Speter**	Parameters:
141238032Speter**		smtpstat -- the smtp status code (e.g., 550).
141338032Speter**
141438032Speter**	Returns:
141538032Speter**		The DSN version of the status code.
141690795Sgshapiro**
141790795Sgshapiro**	Storage Management:
141890795Sgshapiro**		smtptodsn() returns a pointer to a character string literal,
141990795Sgshapiro**		which will remain valid forever, and thus does not need to
142090795Sgshapiro**		be copied.  Current code relies on this property.
142138032Speter*/
142238032Speter
142338032Speterchar *
142438032Spetersmtptodsn(smtpstat)
142538032Speter	int smtpstat;
142638032Speter{
142738032Speter	if (smtpstat < 0)
142838032Speter		return "4.4.2";
142938032Speter
143038032Speter	switch (smtpstat)
143138032Speter	{
143238032Speter	  case 450:	/* Req mail action not taken: mailbox unavailable */
143338032Speter		return "4.2.0";
143438032Speter
143538032Speter	  case 451:	/* Req action aborted: local error in processing */
143638032Speter		return "4.3.0";
143738032Speter
143838032Speter	  case 452:	/* Req action not taken: insufficient sys storage */
143938032Speter		return "4.3.1";
144038032Speter
144138032Speter	  case 500:	/* Syntax error, command unrecognized */
144238032Speter		return "5.5.2";
144338032Speter
144438032Speter	  case 501:	/* Syntax error in parameters or arguments */
144538032Speter		return "5.5.4";
144638032Speter
144738032Speter	  case 502:	/* Command not implemented */
144838032Speter		return "5.5.1";
144938032Speter
145038032Speter	  case 503:	/* Bad sequence of commands */
145138032Speter		return "5.5.1";
145238032Speter
145338032Speter	  case 504:	/* Command parameter not implemented */
145438032Speter		return "5.5.4";
145538032Speter
145638032Speter	  case 550:	/* Req mail action not taken: mailbox unavailable */
145738032Speter		return "5.2.0";
145838032Speter
145938032Speter	  case 551:	/* User not local; please try <...> */
146038032Speter		return "5.1.6";
146138032Speter
146238032Speter	  case 552:	/* Req mail action aborted: exceeded storage alloc */
146338032Speter		return "5.2.2";
146438032Speter
146538032Speter	  case 553:	/* Req action not taken: mailbox name not allowed */
146638032Speter		return "5.1.0";
146738032Speter
146838032Speter	  case 554:	/* Transaction failed */
146938032Speter		return "5.0.0";
147038032Speter	}
147138032Speter
1472157006Sgshapiro	if (REPLYTYPE(smtpstat) == 2)
147338032Speter		return "2.0.0";
1474157006Sgshapiro	if (REPLYTYPE(smtpstat) == 4)
147538032Speter		return "4.0.0";
147638032Speter	return "5.0.0";
147738032Speter}
147890795Sgshapiro/*
147938032Speter**  XTEXTIFY -- take regular text and turn it into DSN-style xtext
148038032Speter**
148138032Speter**	Parameters:
148238032Speter**		t -- the text to convert.
148338032Speter**		taboo -- additional characters that must be encoded.
148438032Speter**
148538032Speter**	Returns:
148638032Speter**		The xtext-ified version of the same string.
148738032Speter*/
148838032Speter
148938032Speterchar *
149038032Speterxtextify(t, taboo)
149138032Speter	register char *t;
149238032Speter	char *taboo;
149338032Speter{
149438032Speter	register char *p;
149538032Speter	int l;
149638032Speter	int nbogus;
149738032Speter	static char *bp = NULL;
149838032Speter	static int bplen = 0;
149938032Speter
150038032Speter	if (taboo == NULL)
150138032Speter		taboo = "";
150238032Speter
150338032Speter	/* figure out how long this xtext will have to be */
150438032Speter	nbogus = l = 0;
150538032Speter	for (p = t; *p != '\0'; p++)
150638032Speter	{
150738032Speter		register int c = (*p & 0xff);
150838032Speter
150938032Speter		/* ASCII dependence here -- this is the way the spec words it */
151038032Speter		if (c < '!' || c > '~' || c == '+' || c == '\\' || c == '(' ||
151138032Speter		    strchr(taboo, c) != NULL)
151238032Speter			nbogus++;
151338032Speter		l++;
151438032Speter	}
151590795Sgshapiro	if (nbogus < 0)
151690795Sgshapiro	{
151790795Sgshapiro		/* since nbogus is ssize_t and wrapped, 2 * size_t would wrap */
151890795Sgshapiro		syserr("!xtextify string too long");
151990795Sgshapiro	}
152038032Speter	if (nbogus == 0)
152138032Speter		return t;
152238032Speter	l += nbogus * 2 + 1;
152338032Speter
152438032Speter	/* now allocate space if necessary for the new string */
152538032Speter	if (l > bplen)
152638032Speter	{
152738032Speter		if (bp != NULL)
152890795Sgshapiro			sm_free(bp); /* XXX */
152990795Sgshapiro		bp = sm_pmalloc_x(l);
153038032Speter		bplen = l;
153138032Speter	}
153238032Speter
153338032Speter	/* ok, copy the text with byte expansion */
153438032Speter	for (p = bp; *t != '\0'; )
153538032Speter	{
153638032Speter		register int c = (*t++ & 0xff);
153738032Speter
153838032Speter		/* ASCII dependence here -- this is the way the spec words it */
153938032Speter		if (c < '!' || c > '~' || c == '+' || c == '\\' || c == '(' ||
154038032Speter		    strchr(taboo, c) != NULL)
154138032Speter		{
154238032Speter			*p++ = '+';
154364565Sgshapiro			*p++ = "0123456789ABCDEF"[c >> 4];
154464565Sgshapiro			*p++ = "0123456789ABCDEF"[c & 0xf];
154538032Speter		}
154638032Speter		else
154738032Speter			*p++ = c;
154838032Speter	}
154938032Speter	*p = '\0';
155038032Speter	return bp;
155138032Speter}
155290795Sgshapiro/*
155338032Speter**  XUNTEXTIFY -- take xtext and turn it into plain text
155438032Speter**
155538032Speter**	Parameters:
155638032Speter**		t -- the xtextified text.
155738032Speter**
155838032Speter**	Returns:
155938032Speter**		The decoded text.  No attempt is made to deal with
156038032Speter**		null strings in the resulting text.
156138032Speter*/
156238032Speter
156338032Speterchar *
156438032Speterxuntextify(t)
156538032Speter	register char *t;
156638032Speter{
156738032Speter	register char *p;
156838032Speter	int l;
156938032Speter	static char *bp = NULL;
157038032Speter	static int bplen = 0;
157138032Speter
157238032Speter	/* heuristic -- if no plus sign, just return the input */
157338032Speter	if (strchr(t, '+') == NULL)
157438032Speter		return t;
157538032Speter
157638032Speter	/* xtext is always longer than decoded text */
157738032Speter	l = strlen(t);
157838032Speter	if (l > bplen)
157938032Speter	{
158038032Speter		if (bp != NULL)
158190795Sgshapiro			sm_free(bp); /* XXX */
158238032Speter		bp = xalloc(l);
158338032Speter		bplen = l;
158438032Speter	}
158538032Speter
158638032Speter	/* ok, copy the text with byte compression */
158738032Speter	for (p = bp; *t != '\0'; t++)
158838032Speter	{
158938032Speter		register int c = *t & 0xff;
159038032Speter
159138032Speter		if (c != '+')
159238032Speter		{
159338032Speter			*p++ = c;
159438032Speter			continue;
159538032Speter		}
159638032Speter
159738032Speter		c = *++t & 0xff;
159838032Speter		if (!isascii(c) || !isxdigit(c))
159938032Speter		{
160038032Speter			/* error -- first digit is not hex */
160138032Speter			usrerr("bogus xtext: +%c", c);
160238032Speter			t--;
160338032Speter			continue;
160438032Speter		}
160538032Speter		if (isdigit(c))
160638032Speter			c -= '0';
160738032Speter		else if (isupper(c))
160838032Speter			c -= 'A' - 10;
160938032Speter		else
161038032Speter			c -= 'a' - 10;
161138032Speter		*p = c << 4;
161238032Speter
161338032Speter		c = *++t & 0xff;
161438032Speter		if (!isascii(c) || !isxdigit(c))
161538032Speter		{
161638032Speter			/* error -- second digit is not hex */
161738032Speter			usrerr("bogus xtext: +%x%c", *p >> 4, c);
161838032Speter			t--;
161938032Speter			continue;
162038032Speter		}
162138032Speter		if (isdigit(c))
162238032Speter			c -= '0';
162338032Speter		else if (isupper(c))
162438032Speter			c -= 'A' - 10;
162538032Speter		else
162638032Speter			c -= 'a' - 10;
162738032Speter		*p++ |= c;
162838032Speter	}
162938032Speter	*p = '\0';
163038032Speter	return bp;
163138032Speter}
163290795Sgshapiro/*
163338032Speter**  XTEXTOK -- check if a string is legal xtext
163438032Speter**
163538032Speter**	Xtext is used in Delivery Status Notifications.  The spec was
163638032Speter**	taken from RFC 1891, ``SMTP Service Extension for Delivery
163738032Speter**	Status Notifications''.
163838032Speter**
163938032Speter**	Parameters:
164038032Speter**		s -- the string to check.
164138032Speter**
164238032Speter**	Returns:
164390795Sgshapiro**		true -- if 's' is legal xtext.
164490795Sgshapiro**		false -- if it has any illegal characters in it.
164538032Speter*/
164638032Speter
164738032Speterbool
164838032Speterxtextok(s)
164938032Speter	char *s;
165038032Speter{
165138032Speter	int c;
165238032Speter
165338032Speter	while ((c = *s++) != '\0')
165438032Speter	{
165538032Speter		if (c == '+')
165638032Speter		{
165738032Speter			c = *s++;
165838032Speter			if (!isascii(c) || !isxdigit(c))
165990795Sgshapiro				return false;
166038032Speter			c = *s++;
166138032Speter			if (!isascii(c) || !isxdigit(c))
166290795Sgshapiro				return false;
166338032Speter		}
166438032Speter		else if (c < '!' || c > '~' || c == '=')
166590795Sgshapiro			return false;
166638032Speter	}
166790795Sgshapiro	return true;
166838032Speter}
166990795Sgshapiro/*
167038032Speter**  PRUNEROUTE -- prune an RFC-822 source route
167164565Sgshapiro**
167238032Speter**	Trims down a source route to the last internet-registered hop.
167338032Speter**	This is encouraged by RFC 1123 section 5.3.3.
167464565Sgshapiro**
167538032Speter**	Parameters:
167638032Speter**		addr -- the address
167764565Sgshapiro**
167838032Speter**	Returns:
167990795Sgshapiro**		true -- address was modified
168090795Sgshapiro**		false -- address could not be pruned
168164565Sgshapiro**
168238032Speter**	Side Effects:
168338032Speter**		modifies addr in-place
168438032Speter*/
168538032Speter
168664565Sgshapirostatic bool
168738032Speterpruneroute(addr)
168838032Speter	char *addr;
168938032Speter{
169038032Speter#if NAMED_BIND
169138032Speter	char *start, *at, *comma;
169238032Speter	char c;
169390795Sgshapiro	int braclev;
169438032Speter	int rcode;
169538032Speter	int i;
169638032Speter	char hostbuf[BUFSIZ];
169738032Speter	char *mxhosts[MAXMXHOSTS + 1];
169838032Speter
169938032Speter	/* check to see if this is really a route-addr */
170038032Speter	if (*addr != '<' || addr[1] != '@' || addr[strlen(addr) - 1] != '>')
170190795Sgshapiro		return false;
170290795Sgshapiro
170390795Sgshapiro	/*
170490795Sgshapiro	**  Can't simply find the first ':' is the address might be in the
170590795Sgshapiro	**  form:  "<@[IPv6:::1]:user@host>" and the first ':' in inside
170690795Sgshapiro	**  the IPv6 address.
170790795Sgshapiro	*/
170890795Sgshapiro
170990795Sgshapiro	start = addr;
171090795Sgshapiro	braclev = 0;
171190795Sgshapiro	while (*start != '\0')
171290795Sgshapiro	{
171390795Sgshapiro		if (*start == ':' && braclev <= 0)
171490795Sgshapiro			break;
171590795Sgshapiro		else if (*start == '[')
171690795Sgshapiro			braclev++;
171790795Sgshapiro		else if (*start == ']' && braclev > 0)
171890795Sgshapiro			braclev--;
171990795Sgshapiro		start++;
172090795Sgshapiro	}
172190795Sgshapiro	if (braclev > 0 || *start != ':')
172290795Sgshapiro		return false;
172390795Sgshapiro
172438032Speter	at = strrchr(addr, '@');
172590795Sgshapiro	if (at == NULL || at < start)
172690795Sgshapiro		return false;
172738032Speter
172838032Speter	/* slice off the angle brackets */
172938032Speter	i = strlen(at + 1);
173090795Sgshapiro	if (i >= sizeof hostbuf)
173190795Sgshapiro		return false;
173290795Sgshapiro	(void) sm_strlcpy(hostbuf, at + 1, sizeof hostbuf);
173338032Speter	hostbuf[i - 1] = '\0';
173438032Speter
173590795Sgshapiro	while (start != NULL)
173638032Speter	{
173790795Sgshapiro		if (getmxrr(hostbuf, mxhosts, NULL, false,
173890795Sgshapiro			    &rcode, true, NULL) > 0)
173938032Speter		{
174090795Sgshapiro			(void) sm_strlcpy(addr + 1, start + 1,
174190795Sgshapiro					  strlen(addr) - 1);
174290795Sgshapiro			return true;
174338032Speter		}
174438032Speter		c = *start;
174538032Speter		*start = '\0';
174638032Speter		comma = strrchr(addr, ',');
174738032Speter		if (comma != NULL && comma[1] == '@' &&
174890795Sgshapiro		    strlen(comma + 2) < sizeof hostbuf)
174990795Sgshapiro			(void) sm_strlcpy(hostbuf, comma + 2, sizeof hostbuf);
175038032Speter		else
175138032Speter			comma = NULL;
175238032Speter		*start = c;
175338032Speter		start = comma;
175438032Speter	}
175564565Sgshapiro#endif /* NAMED_BIND */
175690795Sgshapiro	return false;
175738032Speter}
1758