138032Speter/*
2249729Sgshapiro * Copyright (c) 1998-2003, 2006, 2012, 2013 Sendmail, Inc. and its suppliers.
364565Sgshapiro *	All rights reserved.
438032Speter * Copyright (c) 1983, 1995-1997 Eric P. Allman.  All rights reserved.
538032Speter * Copyright (c) 1988, 1993
638032Speter *	The Regents of the University of California.  All rights reserved.
738032Speter *
838032Speter * By using this file, you agree to the terms and conditions set
938032Speter * forth in the LICENSE file which can be found at the top level of
1038032Speter * the sendmail distribution.
1138032Speter *
1238032Speter */
1338032Speter
1464565Sgshapiro#include <sendmail.h>
1564565Sgshapiro
16249729SgshapiroSM_RCSID("@(#)$Id: savemail.c,v 8.318 2013/03/12 15:24:54 ca Exp $")
1764565Sgshapiro
18157006Sgshapirostatic bool	errbody __P((MCI *, ENVELOPE *, char *));
1964565Sgshapirostatic bool	pruneroute __P((char *));
2064565Sgshapiro
2138032Speter/*
2238032Speter**  SAVEMAIL -- Save mail on error
2338032Speter**
2438032Speter**	If mailing back errors, mail it back to the originator
2538032Speter**	together with an error message; otherwise, just put it in
2638032Speter**	dead.letter in the user's home directory (if he exists on
2738032Speter**	this machine).
2838032Speter**
2938032Speter**	Parameters:
3038032Speter**		e -- the envelope containing the message in error.
3190795Sgshapiro**		sendbody -- if true, also send back the body of the
3238032Speter**			message; otherwise just send the header.
3338032Speter**
3438032Speter**	Returns:
3590795Sgshapiro**		true if savemail panic'ed, (i.e., the data file should
3690795Sgshapiro**		be preserved by dropenvelope())
3738032Speter**
3838032Speter**	Side Effects:
3938032Speter**		Saves the letter, by writing or mailing it back to the
4038032Speter**		sender, or by putting it in dead.letter in her home
4138032Speter**		directory.
4238032Speter*/
4338032Speter
4438032Speter/* defines for state machine */
4564565Sgshapiro#define ESM_REPORT		0	/* report to sender's terminal */
4664565Sgshapiro#define ESM_MAIL		1	/* mail back to sender */
4764565Sgshapiro#define ESM_QUIET		2	/* mail has already been returned */
4864565Sgshapiro#define ESM_DEADLETTER		3	/* save in ~/dead.letter */
4964565Sgshapiro#define ESM_POSTMASTER		4	/* return to postmaster */
5064565Sgshapiro#define ESM_DEADLETTERDROP	5	/* save in DeadLetterDrop */
5164565Sgshapiro#define ESM_PANIC		6	/* call loseqfile() */
5264565Sgshapiro#define ESM_DONE		7	/* message is successfully delivered */
5338032Speter
5490795Sgshapirobool
5538032Spetersavemail(e, sendbody)
5638032Speter	register ENVELOPE *e;
5738032Speter	bool sendbody;
5838032Speter{
5990795Sgshapiro	register SM_FILE_T *fp;
6090795Sgshapiro	bool panic = false;
6138032Speter	int state;
6238032Speter	auto ADDRESS *q = NULL;
6338032Speter	register char *p;
6438032Speter	MCI mcibuf;
6538032Speter	int flags;
6664565Sgshapiro	long sff;
6764565Sgshapiro	char buf[MAXLINE + 1];
6898125Sgshapiro	char dlbuf[MAXPATHLEN];
6990795Sgshapiro	SM_MBDB_T user;
7038032Speter
7190795Sgshapiro
7238032Speter	if (tTd(6, 1))
7338032Speter	{
7490795Sgshapiro		sm_dprintf("\nsavemail, errormode = %c, id = %s, ExitStat = %d\n  e_from=",
7538032Speter			e->e_errormode, e->e_id == NULL ? "NONE" : e->e_id,
7638032Speter			ExitStat);
77132946Sgshapiro		printaddr(sm_debug_file(), &e->e_from, false);
7838032Speter	}
7938032Speter
8038032Speter	if (e->e_id == NULL)
8138032Speter	{
8238032Speter		/* can't return a message with no id */
8390795Sgshapiro		return panic;
8438032Speter	}
8538032Speter
8638032Speter	/*
8738032Speter	**  In the unhappy event we don't know who to return the mail
8838032Speter	**  to, make someone up.
8938032Speter	*/
9038032Speter
9138032Speter	if (e->e_from.q_paddr == NULL)
9238032Speter	{
9338032Speter		e->e_sender = "Postmaster";
9438032Speter		if (parseaddr(e->e_sender, &e->e_from,
9590795Sgshapiro			      RF_COPYPARSE|RF_SENDERADDR,
9690795Sgshapiro			      '\0', NULL, e, false) == NULL)
9738032Speter		{
9864565Sgshapiro			syserr("553 5.3.5 Cannot parse Postmaster!");
9990795Sgshapiro			finis(true, true, EX_SOFTWARE);
10038032Speter		}
10138032Speter	}
10238032Speter	e->e_to = NULL;
10338032Speter
10438032Speter	/*
10538032Speter	**  Basic state machine.
10638032Speter	**
10738032Speter	**	This machine runs through the following states:
10838032Speter	**
10938032Speter	**	ESM_QUIET	Errors have already been printed iff the
11038032Speter	**			sender is local.
11138032Speter	**	ESM_REPORT	Report directly to the sender's terminal.
11238032Speter	**	ESM_MAIL	Mail response to the sender.
11338032Speter	**	ESM_DEADLETTER	Save response in ~/dead.letter.
11438032Speter	**	ESM_POSTMASTER	Mail response to the postmaster.
11564565Sgshapiro	**	ESM_DEADLETTERDROP
11664565Sgshapiro	**			If DeadLetterDrop set, save it there.
11738032Speter	**	ESM_PANIC	Save response anywhere possible.
11838032Speter	*/
11938032Speter
12038032Speter	/* determine starting state */
12138032Speter	switch (e->e_errormode)
12238032Speter	{
12338032Speter	  case EM_WRITE:
12438032Speter		state = ESM_REPORT;
12538032Speter		break;
12638032Speter
12738032Speter	  case EM_BERKNET:
12838032Speter	  case EM_MAIL:
12938032Speter		state = ESM_MAIL;
13038032Speter		break;
13138032Speter
13238032Speter	  case EM_PRINT:
13338032Speter	  case '\0':
13438032Speter		state = ESM_QUIET;
13538032Speter		break;
13638032Speter
13738032Speter	  case EM_QUIET:
13838032Speter		/* no need to return anything at all */
13990795Sgshapiro		return panic;
14038032Speter
14138032Speter	  default:
14290795Sgshapiro		syserr("554 5.3.0 savemail: bogus errormode x%x",
14364565Sgshapiro		       e->e_errormode);
14438032Speter		state = ESM_MAIL;
14538032Speter		break;
14638032Speter	}
14738032Speter
14838032Speter	/* if this is already an error response, send to postmaster */
14938032Speter	if (bitset(EF_RESPONSE, e->e_flags))
15038032Speter	{
15138032Speter		if (e->e_parent != NULL &&
15238032Speter		    bitset(EF_RESPONSE, e->e_parent->e_flags))
15338032Speter		{
15438032Speter			/* got an error sending a response -- can it */
15590795Sgshapiro			return panic;
15638032Speter		}
15738032Speter		state = ESM_POSTMASTER;
15838032Speter	}
15938032Speter
16038032Speter	while (state != ESM_DONE)
16138032Speter	{
16238032Speter		if (tTd(6, 5))
16390795Sgshapiro			sm_dprintf("  state %d\n", state);
16438032Speter
16538032Speter		switch (state)
16638032Speter		{
16738032Speter		  case ESM_QUIET:
16838032Speter			if (bitnset(M_LOCALMAILER, e->e_from.q_mailer->m_flags))
16938032Speter				state = ESM_DEADLETTER;
17038032Speter			else
17138032Speter				state = ESM_MAIL;
17238032Speter			break;
17338032Speter
17438032Speter		  case ESM_REPORT:
17538032Speter
17638032Speter			/*
17738032Speter			**  If the user is still logged in on the same terminal,
17838032Speter			**  then write the error messages back to hir (sic).
17938032Speter			*/
18038032Speter
181132946Sgshapiro#if USE_TTYPATH
18238032Speter			p = ttypath();
183132946Sgshapiro#else /* USE_TTYPATH */
184132946Sgshapiro			p = NULL;
185132946Sgshapiro#endif /* USE_TTYPATH */
186132946Sgshapiro
18790795Sgshapiro			if (p == NULL || sm_io_reopen(SmFtStdio,
18890795Sgshapiro						      SM_TIME_DEFAULT,
18990795Sgshapiro						      p, SM_IO_WRONLY, NULL,
19090795Sgshapiro						      smioout) == NULL)
19138032Speter			{
19238032Speter				state = ESM_MAIL;
19338032Speter				break;
19438032Speter			}
19538032Speter
196168520Sgshapiro			expand("\201n", buf, sizeof(buf), e);
19790795Sgshapiro			(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
19890795Sgshapiro					     "\r\nMessage from %s...\r\n", buf);
19990795Sgshapiro			(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
20090795Sgshapiro					     "Errors occurred while sending mail.\r\n");
20138032Speter			if (e->e_xfp != NULL)
20238032Speter			{
20364565Sgshapiro				(void) bfrewind(e->e_xfp);
20490795Sgshapiro				(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
20590795Sgshapiro						     "Transcript follows:\r\n");
20690795Sgshapiro				while (sm_io_fgets(e->e_xfp, SM_TIME_DEFAULT,
207249729Sgshapiro						   buf, sizeof(buf)) >= 0 &&
20890795Sgshapiro				       !sm_io_error(smioout))
20990795Sgshapiro					(void) sm_io_fputs(smioout,
21090795Sgshapiro							   SM_TIME_DEFAULT,
21190795Sgshapiro							   buf);
21238032Speter			}
21338032Speter			else
21438032Speter			{
21590795Sgshapiro				syserr("Cannot open %s",
21690795Sgshapiro				       queuename(e, XSCRPT_LETTER));
21790795Sgshapiro				(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
21890795Sgshapiro						     "Transcript of session is unavailable.\r\n");
21938032Speter			}
22090795Sgshapiro			(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
22190795Sgshapiro					     "Original message will be saved in dead.letter.\r\n");
22238032Speter			state = ESM_DEADLETTER;
22338032Speter			break;
22438032Speter
22538032Speter		  case ESM_MAIL:
22638032Speter			/*
22738032Speter			**  If mailing back, do it.
22838032Speter			**	Throw away all further output.  Don't alias,
22938032Speter			**	since this could cause loops, e.g., if joe
23038032Speter			**	mails to joe@x, and for some reason the network
23138032Speter			**	for @x is down, then the response gets sent to
23238032Speter			**	joe@x, which gives a response, etc.  Also force
23338032Speter			**	the mail to be delivered even if a version of
23438032Speter			**	it has already been sent to the sender.
23538032Speter			**
23638032Speter			**  If this is a configuration or local software
23738032Speter			**	error, send to the local postmaster as well,
23838032Speter			**	since the originator can't do anything
23938032Speter			**	about it anyway.  Note that this is a full
24038032Speter			**	copy of the message (intentionally) so that
24138032Speter			**	the Postmaster can forward things along.
24238032Speter			*/
24338032Speter
24438032Speter			if (ExitStat == EX_CONFIG || ExitStat == EX_SOFTWARE)
24538032Speter			{
24690795Sgshapiro				(void) sendtolist("postmaster", NULLADDR,
24790795Sgshapiro						  &e->e_errorqueue, 0, e);
24838032Speter			}
24938032Speter			if (!emptyaddr(&e->e_from))
25038032Speter			{
25138032Speter				char from[TOBUFSIZE];
25238032Speter
25390795Sgshapiro				if (sm_strlcpy(from, e->e_from.q_paddr,
254168520Sgshapiro						sizeof(from)) >= sizeof(from))
25538032Speter				{
25638032Speter					state = ESM_POSTMASTER;
25738032Speter					break;
25838032Speter				}
25938032Speter
26090795Sgshapiro				if (!DontPruneRoutes)
26190795Sgshapiro					(void) pruneroute(from);
26238032Speter
26338032Speter				(void) sendtolist(from, NULLADDR,
26438032Speter						  &e->e_errorqueue, 0, e);
26538032Speter			}
26638032Speter
26738032Speter			/*
26838032Speter			**  Deliver a non-delivery report to the
26938032Speter			**  Postmaster-designate (not necessarily
27038032Speter			**  Postmaster).  This does not include the
27138032Speter			**  body of the message, for privacy reasons.
27238032Speter			**  You really shouldn't need this.
27338032Speter			*/
27438032Speter
27538032Speter			e->e_flags |= EF_PM_NOTIFY;
27638032Speter
27738032Speter			/* check to see if there are any good addresses */
27838032Speter			for (q = e->e_errorqueue; q != NULL; q = q->q_next)
27964565Sgshapiro			{
28064565Sgshapiro				if (QS_IS_SENDABLE(q->q_state))
28138032Speter					break;
28264565Sgshapiro			}
28338032Speter			if (q == NULL)
28438032Speter			{
28538032Speter				/* this is an error-error */
28638032Speter				state = ESM_POSTMASTER;
28738032Speter				break;
28838032Speter			}
28938032Speter			if (returntosender(e->e_message, e->e_errorqueue,
29038032Speter					   sendbody ? RTSF_SEND_BODY
29138032Speter						    : RTSF_NO_BODY,
29238032Speter					   e) == 0)
29338032Speter			{
29438032Speter				state = ESM_DONE;
29538032Speter				break;
29638032Speter			}
29738032Speter
29838032Speter			/* didn't work -- return to postmaster */
29938032Speter			state = ESM_POSTMASTER;
30038032Speter			break;
30138032Speter
30238032Speter		  case ESM_POSTMASTER:
30338032Speter			/*
30438032Speter			**  Similar to previous case, but to system postmaster.
30538032Speter			*/
30638032Speter
30738032Speter			q = NULL;
308168520Sgshapiro			expand(DoubleBounceAddr, buf, sizeof(buf), e);
30990795Sgshapiro
31090795Sgshapiro			/*
31190795Sgshapiro			**  Just drop it on the floor if DoubleBounceAddr
31290795Sgshapiro			**  expands to an empty string.
31390795Sgshapiro			*/
31490795Sgshapiro
31590795Sgshapiro			if (*buf == '\0')
31690795Sgshapiro			{
31790795Sgshapiro				state = ESM_DONE;
31890795Sgshapiro				break;
31990795Sgshapiro			}
32064565Sgshapiro			if (sendtolist(buf, NULLADDR, &q, 0, e) <= 0)
32138032Speter			{
32264565Sgshapiro				syserr("553 5.3.0 cannot parse %s!", buf);
32338032Speter				ExitStat = EX_SOFTWARE;
32464565Sgshapiro				state = ESM_DEADLETTERDROP;
32538032Speter				break;
32638032Speter			}
32738032Speter			flags = RTSF_PM_BOUNCE;
32838032Speter			if (sendbody)
32938032Speter				flags |= RTSF_SEND_BODY;
33038032Speter			if (returntosender(e->e_message, q, flags, e) == 0)
33138032Speter			{
33238032Speter				state = ESM_DONE;
33338032Speter				break;
33438032Speter			}
33538032Speter
33638032Speter			/* didn't work -- last resort */
33764565Sgshapiro			state = ESM_DEADLETTERDROP;
33838032Speter			break;
33938032Speter
34038032Speter		  case ESM_DEADLETTER:
34138032Speter			/*
34238032Speter			**  Save the message in dead.letter.
34338032Speter			**	If we weren't mailing back, and the user is
34438032Speter			**	local, we should save the message in
34538032Speter			**	~/dead.letter so that the poor person doesn't
34638032Speter			**	have to type it over again -- and we all know
34738032Speter			**	what poor typists UNIX users are.
34838032Speter			*/
34938032Speter
35038032Speter			p = NULL;
35138032Speter			if (bitnset(M_HASPWENT, e->e_from.q_mailer->m_flags))
35238032Speter			{
35338032Speter				if (e->e_from.q_home != NULL)
35438032Speter					p = e->e_from.q_home;
35590795Sgshapiro				else if (sm_mbdb_lookup(e->e_from.q_user, &user)
35690795Sgshapiro					 == EX_OK &&
35790795Sgshapiro					 *user.mbdb_homedir != '\0')
35890795Sgshapiro					p = user.mbdb_homedir;
35938032Speter			}
36038032Speter			if (p == NULL || e->e_dfp == NULL)
36138032Speter			{
36238032Speter				/* no local directory or no data file */
36338032Speter				state = ESM_MAIL;
36438032Speter				break;
36538032Speter			}
36638032Speter
36738032Speter			/* we have a home directory; write dead.letter */
36890795Sgshapiro			macdefine(&e->e_macro, A_TEMP, 'z', p);
36964565Sgshapiro
37064565Sgshapiro			/* get the sender for the UnixFromLine */
37164565Sgshapiro			p = macvalue('g', e);
37290795Sgshapiro			macdefine(&e->e_macro, A_PERM, 'g', e->e_sender);
37364565Sgshapiro
374168520Sgshapiro			expand("\201z/dead.letter", dlbuf, sizeof(dlbuf), e);
37564565Sgshapiro			sff = SFF_CREAT|SFF_REGONLY|SFF_RUNASREALUID;
37638032Speter			if (RealUid == 0)
37764565Sgshapiro				sff |= SFF_ROOTOK;
37898125Sgshapiro			e->e_to = dlbuf;
37998125Sgshapiro			if (writable(dlbuf, NULL, sff) &&
38098125Sgshapiro			    mailfile(dlbuf, FileMailer, NULL, sff, e) == EX_OK)
38138032Speter			{
38238032Speter				int oldverb = Verbose;
38338032Speter
38464565Sgshapiro				if (OpMode != MD_DAEMON && OpMode != MD_SMTP)
38564565Sgshapiro					Verbose = 1;
38664565Sgshapiro				if (Verbose > 0)
38798125Sgshapiro					message("Saved message in %s", dlbuf);
38838032Speter				Verbose = oldverb;
38990795Sgshapiro				macdefine(&e->e_macro, A_PERM, 'g', p);
39038032Speter				state = ESM_DONE;
39138032Speter				break;
39238032Speter			}
39390795Sgshapiro			macdefine(&e->e_macro, A_PERM, 'g', p);
39438032Speter			state = ESM_MAIL;
39538032Speter			break;
39638032Speter
39764565Sgshapiro		  case ESM_DEADLETTERDROP:
39838032Speter			/*
39964565Sgshapiro			**  Log the mail in DeadLetterDrop file.
40038032Speter			*/
40138032Speter
40238032Speter			if (e->e_class < 0)
40338032Speter			{
40438032Speter				state = ESM_DONE;
40538032Speter				break;
40638032Speter			}
40738032Speter
40838032Speter			if ((SafeFileEnv != NULL && SafeFileEnv[0] != '\0') ||
40964565Sgshapiro			    DeadLetterDrop == NULL ||
41064565Sgshapiro			    DeadLetterDrop[0] == '\0')
41138032Speter			{
41238032Speter				state = ESM_PANIC;
41338032Speter				break;
41438032Speter			}
41538032Speter
41664565Sgshapiro			sff = SFF_CREAT|SFF_REGONLY|SFF_ROOTOK|SFF_OPENASROOT|SFF_MUSTOWN;
41764565Sgshapiro			if (!writable(DeadLetterDrop, NULL, sff) ||
41838032Speter			    (fp = safefopen(DeadLetterDrop, O_WRONLY|O_APPEND,
41964565Sgshapiro					    FileMode, sff)) == NULL)
42038032Speter			{
42138032Speter				state = ESM_PANIC;
42238032Speter				break;
42338032Speter			}
42438032Speter
425168520Sgshapiro			memset(&mcibuf, '\0', sizeof(mcibuf));
42638032Speter			mcibuf.mci_out = fp;
42738032Speter			mcibuf.mci_mailer = FileMailer;
42838032Speter			if (bitnset(M_7BITS, FileMailer->m_flags))
42938032Speter				mcibuf.mci_flags |= MCIF_7BIT;
43038032Speter
43164565Sgshapiro			/* get the sender for the UnixFromLine */
43264565Sgshapiro			p = macvalue('g', e);
43390795Sgshapiro			macdefine(&e->e_macro, A_PERM, 'g', e->e_sender);
43464565Sgshapiro
435157006Sgshapiro			if (!putfromline(&mcibuf, e) ||
436157006Sgshapiro			    !(*e->e_puthdr)(&mcibuf, e->e_header, e,
437157006Sgshapiro					M87F_OUTER) ||
438157006Sgshapiro			    !(*e->e_putbody)(&mcibuf, e, NULL) ||
439157006Sgshapiro			    !putline("\n", &mcibuf) ||
440157006Sgshapiro			    sm_io_flush(fp, SM_TIME_DEFAULT) == SM_IO_EOF ||
441157006Sgshapiro			    sm_io_error(fp) ||
44290795Sgshapiro			    sm_io_close(fp, SM_TIME_DEFAULT) < 0)
44338032Speter				state = ESM_PANIC;
44438032Speter			else
44538032Speter			{
44638032Speter				int oldverb = Verbose;
44738032Speter
44864565Sgshapiro				if (OpMode != MD_DAEMON && OpMode != MD_SMTP)
44964565Sgshapiro					Verbose = 1;
45064565Sgshapiro				if (Verbose > 0)
45164565Sgshapiro					message("Saved message in %s",
45264565Sgshapiro						DeadLetterDrop);
45338032Speter				Verbose = oldverb;
45438032Speter				if (LogLevel > 3)
45538032Speter					sm_syslog(LOG_NOTICE, e->e_id,
45664565Sgshapiro						  "Saved message in %s",
45764565Sgshapiro						  DeadLetterDrop);
45838032Speter				state = ESM_DONE;
45938032Speter			}
46090795Sgshapiro			macdefine(&e->e_macro, A_PERM, 'g', p);
46138032Speter			break;
46238032Speter
46338032Speter		  default:
46464565Sgshapiro			syserr("554 5.3.5 savemail: unknown state %d", state);
46564565Sgshapiro			/* FALLTHROUGH */
46638032Speter
46738032Speter		  case ESM_PANIC:
46838032Speter			/* leave the locked queue & transcript files around */
46938032Speter			loseqfile(e, "savemail panic");
47090795Sgshapiro			panic = true;
47166497Sgshapiro			errno = 0;
47290795Sgshapiro			syserr("554 savemail: cannot save rejected email anywhere");
47390795Sgshapiro			state = ESM_DONE;
47490795Sgshapiro			break;
47538032Speter		}
47638032Speter	}
47790795Sgshapiro	return panic;
47838032Speter}
47990795Sgshapiro/*
48038032Speter**  RETURNTOSENDER -- return a message to the sender with an error.
48138032Speter**
48238032Speter**	Parameters:
48338032Speter**		msg -- the explanatory message.
48438032Speter**		returnq -- the queue of people to send the message to.
48538032Speter**		flags -- flags tweaking the operation:
48638032Speter**			RTSF_SENDBODY -- include body of message (otherwise
48738032Speter**				just send the header).
48838032Speter**			RTSF_PMBOUNCE -- this is a postmaster bounce.
48938032Speter**		e -- the current envelope.
49038032Speter**
49138032Speter**	Returns:
49238032Speter**		zero -- if everything went ok.
49338032Speter**		else -- some error.
49438032Speter**
49538032Speter**	Side Effects:
49690795Sgshapiro**		Returns the current message to the sender via mail.
49738032Speter*/
49838032Speter
49938032Speter#define MAXRETURNS	6	/* max depth of returning messages */
50090795Sgshapiro#define ERRORFUDGE	1024	/* nominal size of error message text */
50138032Speter
50238032Speterint
50338032Speterreturntosender(msg, returnq, flags, e)
50438032Speter	char *msg;
50538032Speter	ADDRESS *returnq;
50638032Speter	int flags;
50738032Speter	register ENVELOPE *e;
50838032Speter{
509244833Sgshapiro	int ret;
51038032Speter	register ENVELOPE *ee;
51138032Speter	ENVELOPE *oldcur = CurEnv;
51238032Speter	ENVELOPE errenvelope;
51338032Speter	static int returndepth = 0;
51438032Speter	register ADDRESS *q;
51538032Speter	char *p;
51638032Speter	char buf[MAXNAME + 1];
51738032Speter
51838032Speter	if (returnq == NULL)
51964565Sgshapiro		return -1;
52038032Speter
52138032Speter	if (msg == NULL)
52238032Speter		msg = "Unable to deliver mail";
52338032Speter
52438032Speter	if (tTd(6, 1))
52538032Speter	{
52690795Sgshapiro		sm_dprintf("\n*** Return To Sender: msg=\"%s\", depth=%d, e=%p, returnq=",
52790795Sgshapiro			msg, returndepth, e);
528132946Sgshapiro		printaddr(sm_debug_file(), returnq, true);
52938032Speter		if (tTd(6, 20))
53038032Speter		{
53190795Sgshapiro			sm_dprintf("Sendq=");
532132946Sgshapiro			printaddr(sm_debug_file(), e->e_sendqueue, true);
53338032Speter		}
53438032Speter	}
53538032Speter
53638032Speter	if (++returndepth >= MAXRETURNS)
53738032Speter	{
53838032Speter		if (returndepth != MAXRETURNS)
53964565Sgshapiro			syserr("554 5.3.0 returntosender: infinite recursion on %s",
54064565Sgshapiro			       returnq->q_paddr);
54138032Speter		/* don't "unrecurse" and fake a clean exit */
54238032Speter		/* returndepth--; */
54364565Sgshapiro		return 0;
54438032Speter	}
54538032Speter
54690795Sgshapiro	macdefine(&e->e_macro, A_PERM, 'g', e->e_sender);
54790795Sgshapiro	macdefine(&e->e_macro, A_PERM, 'u', NULL);
54838032Speter
54938032Speter	/* initialize error envelope */
55090795Sgshapiro	ee = newenvelope(&errenvelope, e, sm_rpool_new_x(NULL));
55190795Sgshapiro	macdefine(&ee->e_macro, A_PERM, 'a', "\201b");
55290795Sgshapiro	macdefine(&ee->e_macro, A_PERM, 'r', "");
55390795Sgshapiro	macdefine(&ee->e_macro, A_PERM, 's', "localhost");
55490795Sgshapiro	macdefine(&ee->e_macro, A_PERM, '_', "localhost");
555110563Sgshapiro	clrsessenvelope(ee);
55664565Sgshapiro
55738032Speter	ee->e_puthdr = putheader;
55838032Speter	ee->e_putbody = errbody;
55938032Speter	ee->e_flags |= EF_RESPONSE|EF_METOO;
56038032Speter	if (!bitset(EF_OLDSTYLE, e->e_flags))
56138032Speter		ee->e_flags &= ~EF_OLDSTYLE;
56264565Sgshapiro	if (bitset(EF_DONT_MIME, e->e_flags))
56364565Sgshapiro	{
56464565Sgshapiro		ee->e_flags |= EF_DONT_MIME;
56564565Sgshapiro
56664565Sgshapiro		/*
56764565Sgshapiro		**  If we can't convert to MIME and we don't pass
56864565Sgshapiro		**  8-bit, we can't send the body.
56964565Sgshapiro		*/
57064565Sgshapiro
57164565Sgshapiro		if (bitset(EF_HAS8BIT, e->e_flags) &&
57264565Sgshapiro		    !bitset(MM_PASS8BIT, MimeMode))
57364565Sgshapiro			flags &= ~RTSF_SEND_BODY;
57464565Sgshapiro	}
57564565Sgshapiro
57638032Speter	ee->e_sendqueue = returnq;
57790795Sgshapiro	ee->e_msgsize = 0;
57864565Sgshapiro	if (bitset(RTSF_SEND_BODY, flags) &&
57964565Sgshapiro	    !bitset(PRIV_NOBODYRETN, PrivacyFlags))
58090795Sgshapiro		ee->e_msgsize = ERRORFUDGE + e->e_msgsize;
58138032Speter	else
58238032Speter		ee->e_flags |= EF_NO_BODY_RETN;
58390795Sgshapiro
58490795Sgshapiro	if (!setnewqueue(ee))
58590795Sgshapiro	{
58690795Sgshapiro		syserr("554 5.3.0 returntosender: cannot select queue for %s",
58790795Sgshapiro			       returnq->q_paddr);
58890795Sgshapiro		ExitStat = EX_UNAVAILABLE;
58990795Sgshapiro		returndepth--;
59090795Sgshapiro		return -1;
59190795Sgshapiro	}
59238032Speter	initsys(ee);
59364565Sgshapiro
59464565Sgshapiro#if NAMED_BIND
59564565Sgshapiro	_res.retry = TimeOuts.res_retry[RES_TO_FIRST];
59664565Sgshapiro	_res.retrans = TimeOuts.res_retrans[RES_TO_FIRST];
59764565Sgshapiro#endif /* NAMED_BIND */
59838032Speter	for (q = returnq; q != NULL; q = q->q_next)
59938032Speter	{
60064565Sgshapiro		if (QS_IS_BADADDR(q->q_state))
60138032Speter			continue;
60238032Speter
60338032Speter		q->q_flags &= ~(QHASNOTIFY|Q_PINGFLAGS);
60438032Speter		q->q_flags |= QPINGONFAILURE;
60538032Speter
60664565Sgshapiro		if (!QS_IS_DEAD(q->q_state))
60738032Speter			ee->e_nrcpts++;
60838032Speter
60938032Speter		if (q->q_alias == NULL)
610168520Sgshapiro			addheader("To", q->q_paddr, 0, ee, true);
61138032Speter	}
61238032Speter
61338032Speter	if (LogLevel > 5)
61438032Speter	{
61564565Sgshapiro		if (bitset(EF_RESPONSE, e->e_flags))
61638032Speter			p = "return to sender";
61764565Sgshapiro		else if (bitset(EF_WARNING, e->e_flags))
61864565Sgshapiro			p = "sender notify";
61938032Speter		else if (bitset(RTSF_PM_BOUNCE, flags))
62038032Speter			p = "postmaster notify";
62138032Speter		else
62238032Speter			p = "DSN";
62390795Sgshapiro		sm_syslog(LOG_INFO, e->e_id, "%s: %s: %s",
62464565Sgshapiro			  ee->e_id, p, shortenstring(msg, MAXSHORTSTR));
62538032Speter	}
62638032Speter
62738032Speter	if (SendMIMEErrors)
62838032Speter	{
629168520Sgshapiro		addheader("MIME-Version", "1.0", 0, ee, true);
630168520Sgshapiro		(void) sm_snprintf(buf, sizeof(buf), "%s.%ld/%.100s",
63190795Sgshapiro				ee->e_id, (long)curtime(), MyHostName);
63290795Sgshapiro		ee->e_msgboundary = sm_rpool_strdup_x(ee->e_rpool, buf);
633168520Sgshapiro		(void) sm_snprintf(buf, sizeof(buf),
63438032Speter#if DSN
63564565Sgshapiro				"multipart/report; report-type=delivery-status;\n\tboundary=\"%s\"",
63664565Sgshapiro#else /* DSN */
63764565Sgshapiro				"multipart/mixed; boundary=\"%s\"",
63864565Sgshapiro#endif /* DSN */
63964565Sgshapiro				ee->e_msgboundary);
640168520Sgshapiro		addheader("Content-Type", buf, 0, ee, true);
64138032Speter
64238032Speter		p = hvalue("Content-Transfer-Encoding", e->e_header);
64390795Sgshapiro		if (p != NULL && sm_strcasecmp(p, "binary") != 0)
64438032Speter			p = NULL;
64538032Speter		if (p == NULL && bitset(EF_HAS8BIT, e->e_flags))
64638032Speter			p = "8bit";
64738032Speter		if (p != NULL)
648168520Sgshapiro			addheader("Content-Transfer-Encoding", p, 0, ee, true);
64938032Speter	}
65038032Speter	if (strncmp(msg, "Warning:", 8) == 0)
65138032Speter	{
652168520Sgshapiro		addheader("Subject", msg, 0, ee, true);
65338032Speter		p = "warning-timeout";
65438032Speter	}
65538032Speter	else if (strncmp(msg, "Postmaster warning:", 19) == 0)
65638032Speter	{
657168520Sgshapiro		addheader("Subject", msg, 0, ee, true);
65838032Speter		p = "postmaster-warning";
65938032Speter	}
66038032Speter	else if (strcmp(msg, "Return receipt") == 0)
66138032Speter	{
662168520Sgshapiro		addheader("Subject", msg, 0, ee, true);
66338032Speter		p = "return-receipt";
66438032Speter	}
66538032Speter	else if (bitset(RTSF_PM_BOUNCE, flags))
66638032Speter	{
667168520Sgshapiro		(void) sm_snprintf(buf, sizeof(buf),
66864565Sgshapiro			 "Postmaster notify: see transcript for details");
669168520Sgshapiro		addheader("Subject", buf, 0, ee, true);
67038032Speter		p = "postmaster-notification";
67138032Speter	}
67238032Speter	else
67338032Speter	{
674168520Sgshapiro		(void) sm_snprintf(buf, sizeof(buf),
67564565Sgshapiro			 "Returned mail: see transcript for details");
676168520Sgshapiro		addheader("Subject", buf, 0, ee, true);
67738032Speter		p = "failure";
67838032Speter	}
679168520Sgshapiro	(void) sm_snprintf(buf, sizeof(buf), "auto-generated (%s)", p);
680168520Sgshapiro	addheader("Auto-Submitted", buf, 0, ee, true);
68138032Speter
68238032Speter	/* fake up an address header for the from person */
683168520Sgshapiro	expand("\201n", buf, sizeof(buf), e);
68464565Sgshapiro	if (parseaddr(buf, &ee->e_from,
68590795Sgshapiro		      RF_COPYALL|RF_SENDERADDR, '\0', NULL, e, false) == NULL)
68638032Speter	{
68764565Sgshapiro		syserr("553 5.3.5 Can't parse myself!");
68838032Speter		ExitStat = EX_SOFTWARE;
68938032Speter		returndepth--;
69064565Sgshapiro		return -1;
69138032Speter	}
69238032Speter	ee->e_from.q_flags &= ~(QHASNOTIFY|Q_PINGFLAGS);
69338032Speter	ee->e_from.q_flags |= QPINGONFAILURE;
69438032Speter	ee->e_sender = ee->e_from.q_paddr;
69538032Speter
69638032Speter	/* push state into submessage */
69738032Speter	CurEnv = ee;
69890795Sgshapiro	macdefine(&ee->e_macro, A_PERM, 'f', "\201n");
69990795Sgshapiro	macdefine(&ee->e_macro, A_PERM, 'x', "Mail Delivery Subsystem");
70090795Sgshapiro	eatheader(ee, true, true);
70138032Speter
70238032Speter	/* mark statistics */
70390795Sgshapiro	markstats(ee, NULLADDR, STATS_NORMAL);
70438032Speter
70538032Speter	/* actually deliver the error message */
70638032Speter	sendall(ee, SM_DELIVER);
707203004Sgshapiro	(void) dropenvelope(ee, true, false);
70838032Speter
70938032Speter	/* check for delivery errors */
710244833Sgshapiro	ret = -1;
71164565Sgshapiro	if (ee->e_parent == NULL ||
71264565Sgshapiro	    !bitset(EF_RESPONSE, ee->e_parent->e_flags))
71338032Speter	{
714244833Sgshapiro		ret = 0;
71538032Speter	}
716244833Sgshapiro	else
717244833Sgshapiro	{
718244833Sgshapiro		for (q = ee->e_sendqueue; q != NULL; q = q->q_next)
719244833Sgshapiro		{
720244833Sgshapiro			if (QS_IS_ATTEMPTED(q->q_state))
721244833Sgshapiro			{
722244833Sgshapiro				ret = 0;
723244833Sgshapiro				break;
724244833Sgshapiro			}
725244833Sgshapiro		}
726244833Sgshapiro	}
727244833Sgshapiro
728244833Sgshapiro	/* restore state */
729244833Sgshapiro	sm_rpool_free(ee->e_rpool);
730244833Sgshapiro	CurEnv = oldcur;
731244833Sgshapiro	returndepth--;
732244833Sgshapiro
733244833Sgshapiro	return ret;
73438032Speter}
735244833Sgshapiro
73690795Sgshapiro/*
73738032Speter**  ERRBODY -- output the body of an error message.
73838032Speter**
73938032Speter**	Typically this is a copy of the transcript plus a copy of the
74038032Speter**	original offending message.
74138032Speter**
74238032Speter**	Parameters:
74338032Speter**		mci -- the mailer connection information.
74438032Speter**		e -- the envelope we are working in.
74590795Sgshapiro**		separator -- any possible MIME separator (unused).
74638032Speter**
74738032Speter**	Returns:
748159613Sgshapiro**		true iff body was written successfully
74938032Speter**
75038032Speter**	Side Effects:
75138032Speter**		Outputs the body of an error message.
75238032Speter*/
75338032Speter
75464565Sgshapiro/* ARGSUSED2 */
755157006Sgshapirostatic bool
75638032Spetererrbody(mci, e, separator)
75738032Speter	register MCI *mci;
75838032Speter	register ENVELOPE *e;
75938032Speter	char *separator;
76038032Speter{
76164565Sgshapiro	bool printheader;
76264565Sgshapiro	bool sendbody;
76364565Sgshapiro	bool pm_notify;
76464565Sgshapiro	int save_errno;
76590795Sgshapiro	register SM_FILE_T *xfile;
76638032Speter	char *p;
76738032Speter	register ADDRESS *q = NULL;
76890795Sgshapiro	char actual[MAXLINE];
76938032Speter	char buf[MAXLINE];
77038032Speter
77138032Speter	if (bitset(MCIF_INHEADER, mci->mci_flags))
77238032Speter	{
773157006Sgshapiro		if (!putline("", mci))
774157006Sgshapiro			goto writeerr;
77538032Speter		mci->mci_flags &= ~MCIF_INHEADER;
77638032Speter	}
77738032Speter	if (e->e_parent == NULL)
77838032Speter	{
77938032Speter		syserr("errbody: null parent");
780157006Sgshapiro		if (!putline("   ----- Original message lost -----\n", mci))
781157006Sgshapiro			goto writeerr;
782157006Sgshapiro		return true;
78338032Speter	}
78438032Speter
78538032Speter	/*
78638032Speter	**  Output MIME header.
78738032Speter	*/
78838032Speter
78938032Speter	if (e->e_msgboundary != NULL)
79038032Speter	{
791168520Sgshapiro		(void) sm_strlcpyn(buf, sizeof(buf), 2, "--", e->e_msgboundary);
792157006Sgshapiro		if (!putline("This is a MIME-encapsulated message", mci) ||
793157006Sgshapiro		    !putline("", mci) ||
794157006Sgshapiro		    !putline(buf, mci) ||
795157006Sgshapiro		    !putline("", mci))
796157006Sgshapiro			goto writeerr;
79738032Speter	}
79838032Speter
79938032Speter	/*
80038032Speter	**  Output introductory information.
80138032Speter	*/
80238032Speter
80390795Sgshapiro	pm_notify = false;
80438032Speter	p = hvalue("subject", e->e_header);
80538032Speter	if (p != NULL && strncmp(p, "Postmaster ", 11) == 0)
80690795Sgshapiro		pm_notify = true;
80738032Speter	else
80838032Speter	{
80938032Speter		for (q = e->e_parent->e_sendqueue; q != NULL; q = q->q_next)
81064565Sgshapiro		{
81164565Sgshapiro			if (QS_IS_BADADDR(q->q_state))
81238032Speter				break;
81364565Sgshapiro		}
81438032Speter	}
81538032Speter	if (!pm_notify && q == NULL &&
81638032Speter	    !bitset(EF_FATALERRS|EF_SENDRECEIPT, e->e_parent->e_flags))
81738032Speter	{
818157006Sgshapiro		if (!putline("    **********************************************",
819157006Sgshapiro			mci) ||
820157006Sgshapiro		    !putline("    **      THIS IS A WARNING MESSAGE ONLY      **",
821157006Sgshapiro			mci) ||
822157006Sgshapiro		    !putline("    **  YOU DO NOT NEED TO RESEND YOUR MESSAGE  **",
823157006Sgshapiro			mci) ||
824157006Sgshapiro		    !putline("    **********************************************",
825157006Sgshapiro			mci) ||
826157006Sgshapiro		    !putline("", mci))
827157006Sgshapiro			goto writeerr;
82838032Speter	}
829168520Sgshapiro	(void) sm_snprintf(buf, sizeof(buf),
83090795Sgshapiro		"The original message was received at %s",
83190795Sgshapiro		arpadate(ctime(&e->e_parent->e_ctime)));
832157006Sgshapiro	if (!putline(buf, mci))
833157006Sgshapiro		goto writeerr;
834168520Sgshapiro	expand("from \201_", buf, sizeof(buf), e->e_parent);
835157006Sgshapiro	if (!putline(buf, mci))
836157006Sgshapiro		goto writeerr;
83764565Sgshapiro
83864565Sgshapiro	/* include id in postmaster copies */
83964565Sgshapiro	if (pm_notify && e->e_parent->e_id != NULL)
84064565Sgshapiro	{
841168520Sgshapiro		(void) sm_strlcpyn(buf, sizeof(buf), 2, "with id ",
84290795Sgshapiro			e->e_parent->e_id);
843157006Sgshapiro		if (!putline(buf, mci))
844157006Sgshapiro			goto writeerr;
84564565Sgshapiro	}
846157006Sgshapiro	if (!putline("", mci))
847157006Sgshapiro		goto writeerr;
84838032Speter
84938032Speter	/*
85038032Speter	**  Output error message header (if specified and available).
85138032Speter	*/
85238032Speter
85364565Sgshapiro	if (ErrMsgFile != NULL &&
85464565Sgshapiro	    !bitset(EF_SENDRECEIPT, e->e_parent->e_flags))
85538032Speter	{
85638032Speter		if (*ErrMsgFile == '/')
85738032Speter		{
85864565Sgshapiro			long sff = SFF_ROOTOK|SFF_REGONLY;
85938032Speter
86038032Speter			if (DontLockReadFiles)
86138032Speter				sff |= SFF_NOLOCK;
86264565Sgshapiro			if (!bitnset(DBS_ERRORHEADERINUNSAFEDIRPATH,
86364565Sgshapiro				     DontBlameSendmail))
86438032Speter				sff |= SFF_SAFEDIRPATH;
86538032Speter			xfile = safefopen(ErrMsgFile, O_RDONLY, 0444, sff);
86638032Speter			if (xfile != NULL)
86738032Speter			{
86890795Sgshapiro				while (sm_io_fgets(xfile, SM_TIME_DEFAULT, buf,
869249729Sgshapiro						   sizeof(buf)) >= 0)
87038032Speter				{
871168520Sgshapiro					int lbs;
872168520Sgshapiro					bool putok;
873168520Sgshapiro					char *lbp;
874168520Sgshapiro
875168520Sgshapiro					lbs = sizeof(buf);
876168520Sgshapiro					lbp = translate_dollars(buf, buf, &lbs);
877168520Sgshapiro					expand(lbp, lbp, lbs, e);
878168520Sgshapiro					putok = putline(lbp, mci);
879168520Sgshapiro					if (lbp != buf)
880168520Sgshapiro						sm_free(lbp);
881168520Sgshapiro					if (!putok)
882157006Sgshapiro						goto writeerr;
88338032Speter				}
88490795Sgshapiro				(void) sm_io_close(xfile, SM_TIME_DEFAULT);
885157006Sgshapiro				if (!putline("\n", mci))
886157006Sgshapiro					goto writeerr;
88738032Speter			}
88838032Speter		}
88938032Speter		else
89038032Speter		{
891168520Sgshapiro			expand(ErrMsgFile, buf, sizeof(buf), e);
892157006Sgshapiro			if (!putline(buf, mci) || !putline("", mci))
893157006Sgshapiro				goto writeerr;
89438032Speter		}
89538032Speter	}
89638032Speter
89738032Speter	/*
89838032Speter	**  Output message introduction
89938032Speter	*/
90038032Speter
90190795Sgshapiro	/* permanent fatal errors */
90290795Sgshapiro	printheader = true;
90338032Speter	for (q = e->e_parent->e_sendqueue; q != NULL; q = q->q_next)
90438032Speter	{
90564565Sgshapiro		if (!QS_IS_BADADDR(q->q_state) ||
90638032Speter		    !bitset(QPINGONFAILURE, q->q_flags))
90738032Speter			continue;
90838032Speter
90938032Speter		if (printheader)
91038032Speter		{
911157006Sgshapiro			if (!putline("   ----- The following addresses had permanent fatal errors -----",
912157006Sgshapiro					mci))
913157006Sgshapiro				goto writeerr;
91490795Sgshapiro			printheader = false;
91538032Speter		}
91638032Speter
91790795Sgshapiro		(void) sm_strlcpy(buf, shortenstring(q->q_paddr, MAXSHORTSTR),
918168520Sgshapiro				  sizeof(buf));
919157006Sgshapiro		if (!putline(buf, mci))
920157006Sgshapiro			goto writeerr;
92164565Sgshapiro		if (q->q_rstatus != NULL)
92264565Sgshapiro		{
923168520Sgshapiro			(void) sm_snprintf(buf, sizeof(buf),
92490795Sgshapiro				"    (reason: %s)",
92590795Sgshapiro				shortenstring(exitstat(q->q_rstatus),
92690795Sgshapiro					      MAXSHORTSTR));
927157006Sgshapiro			if (!putline(buf, mci))
928157006Sgshapiro				goto writeerr;
92964565Sgshapiro		}
93038032Speter		if (q->q_alias != NULL)
93138032Speter		{
932168520Sgshapiro			(void) sm_snprintf(buf, sizeof(buf),
93390795Sgshapiro				"    (expanded from: %s)",
93490795Sgshapiro				shortenstring(q->q_alias->q_paddr,
93590795Sgshapiro					      MAXSHORTSTR));
936157006Sgshapiro			if (!putline(buf, mci))
937157006Sgshapiro				goto writeerr;
93838032Speter		}
93938032Speter	}
940157006Sgshapiro	if (!printheader && !putline("", mci))
941157006Sgshapiro		goto writeerr;
94238032Speter
94390795Sgshapiro	/* transient non-fatal errors */
94490795Sgshapiro	printheader = true;
94538032Speter	for (q = e->e_parent->e_sendqueue; q != NULL; q = q->q_next)
94638032Speter	{
94764565Sgshapiro		if (QS_IS_BADADDR(q->q_state) ||
94838032Speter		    !bitset(QPRIMARY, q->q_flags) ||
94990795Sgshapiro		    !bitset(QBYNDELAY, q->q_flags) ||
95038032Speter		    !bitset(QDELAYED, q->q_flags))
95138032Speter			continue;
95238032Speter
95338032Speter		if (printheader)
95438032Speter		{
955157006Sgshapiro			if (!putline("   ----- The following addresses had transient non-fatal errors -----",
956157006Sgshapiro					mci))
957157006Sgshapiro				goto writeerr;
95890795Sgshapiro			printheader = false;
95938032Speter		}
96038032Speter
96190795Sgshapiro		(void) sm_strlcpy(buf, shortenstring(q->q_paddr, MAXSHORTSTR),
962168520Sgshapiro				  sizeof(buf));
963157006Sgshapiro		if (!putline(buf, mci))
964157006Sgshapiro			goto writeerr;
96538032Speter		if (q->q_alias != NULL)
96638032Speter		{
967168520Sgshapiro			(void) sm_snprintf(buf, sizeof(buf),
96890795Sgshapiro				"    (expanded from: %s)",
96990795Sgshapiro				shortenstring(q->q_alias->q_paddr,
97090795Sgshapiro					      MAXSHORTSTR));
971157006Sgshapiro			if (!putline(buf, mci))
972157006Sgshapiro				goto writeerr;
97338032Speter		}
97438032Speter	}
975157006Sgshapiro	if (!printheader && !putline("", mci))
976157006Sgshapiro		goto writeerr;
97738032Speter
97890795Sgshapiro	/* successful delivery notifications */
97990795Sgshapiro	printheader = true;
98038032Speter	for (q = e->e_parent->e_sendqueue; q != NULL; q = q->q_next)
98138032Speter	{
98264565Sgshapiro		if (QS_IS_BADADDR(q->q_state) ||
98338032Speter		    !bitset(QPRIMARY, q->q_flags) ||
98490795Sgshapiro		    bitset(QBYNDELAY, q->q_flags) ||
98538032Speter		    bitset(QDELAYED, q->q_flags))
98638032Speter			continue;
98790795Sgshapiro		else if (bitset(QBYNRELAY, q->q_flags))
98890795Sgshapiro			p = "Deliver-By notify: relayed";
98990795Sgshapiro		else if (bitset(QBYTRACE, q->q_flags))
99090795Sgshapiro			p = "Deliver-By trace: relayed";
99138032Speter		else if (!bitset(QPINGONSUCCESS, q->q_flags))
99238032Speter			continue;
99338032Speter		else if (bitset(QRELAYED, q->q_flags))
99438032Speter			p = "relayed to non-DSN-aware mailer";
99538032Speter		else if (bitset(QDELIVERED, q->q_flags))
99638032Speter		{
99738032Speter			if (bitset(QEXPANDED, q->q_flags))
99838032Speter				p = "successfully delivered to mailing list";
99938032Speter			else
100038032Speter				p = "successfully delivered to mailbox";
100138032Speter		}
100238032Speter		else if (bitset(QEXPANDED, q->q_flags))
100338032Speter			p = "expanded by alias";
100438032Speter		else
100538032Speter			continue;
100638032Speter
100738032Speter		if (printheader)
100838032Speter		{
1009157006Sgshapiro			if (!putline("   ----- The following addresses had successful delivery notifications -----",
1010157006Sgshapiro					mci))
1011157006Sgshapiro				goto writeerr;
101290795Sgshapiro			printheader = false;
101338032Speter		}
101438032Speter
1015168520Sgshapiro		(void) sm_snprintf(buf, sizeof(buf), "%s  (%s)",
101664565Sgshapiro			 shortenstring(q->q_paddr, MAXSHORTSTR), p);
1017157006Sgshapiro		if (!putline(buf, mci))
1018157006Sgshapiro			goto writeerr;
101938032Speter		if (q->q_alias != NULL)
102038032Speter		{
1021168520Sgshapiro			(void) sm_snprintf(buf, sizeof(buf),
102290795Sgshapiro				"    (expanded from: %s)",
102390795Sgshapiro				shortenstring(q->q_alias->q_paddr,
102490795Sgshapiro					      MAXSHORTSTR));
1025157006Sgshapiro			if (!putline(buf, mci))
1026157006Sgshapiro				goto writeerr;
102738032Speter		}
102838032Speter	}
1029157006Sgshapiro	if (!printheader && !putline("", mci))
1030157006Sgshapiro		goto writeerr;
103138032Speter
103238032Speter	/*
103338032Speter	**  Output transcript of errors
103438032Speter	*/
103538032Speter
103690795Sgshapiro	(void) sm_io_flush(smioout, SM_TIME_DEFAULT);
103764565Sgshapiro	if (e->e_parent->e_xfp == NULL)
103838032Speter	{
1039157006Sgshapiro		if (!putline("   ----- Transcript of session is unavailable -----\n",
1040157006Sgshapiro				mci))
1041157006Sgshapiro			goto writeerr;
104238032Speter	}
104338032Speter	else
104438032Speter	{
1045249729Sgshapiro		int blen;
1046249729Sgshapiro
104790795Sgshapiro		printheader = true;
104864565Sgshapiro		(void) bfrewind(e->e_parent->e_xfp);
104938032Speter		if (e->e_xfp != NULL)
105090795Sgshapiro			(void) sm_io_flush(e->e_xfp, SM_TIME_DEFAULT);
1051249729Sgshapiro		while ((blen = sm_io_fgets(e->e_parent->e_xfp, SM_TIME_DEFAULT,
1052249729Sgshapiro					buf, sizeof(buf))) >= 0)
105338032Speter		{
1054157006Sgshapiro			if (printheader && !putline("   ----- Transcript of session follows -----\n",
1055157006Sgshapiro						mci))
1056157006Sgshapiro				goto writeerr;
105790795Sgshapiro			printheader = false;
1058249729Sgshapiro			if (!putxline(buf, blen, mci, PXLF_MAPFROM))
1059157006Sgshapiro				goto writeerr;
106038032Speter		}
106138032Speter	}
106238032Speter	errno = 0;
106338032Speter
106438032Speter#if DSN
106538032Speter	/*
106638032Speter	**  Output machine-readable version.
106738032Speter	*/
106838032Speter
106938032Speter	if (e->e_msgboundary != NULL)
107038032Speter	{
1071168520Sgshapiro		(void) sm_strlcpyn(buf, sizeof(buf), 2, "--", e->e_msgboundary);
1072157006Sgshapiro		if (!putline("", mci) ||
1073157006Sgshapiro		    !putline(buf, mci) ||
1074157006Sgshapiro		    !putline("Content-Type: message/delivery-status", mci) ||
1075157006Sgshapiro		    !putline("", mci))
1076157006Sgshapiro			goto writeerr;
107738032Speter
107838032Speter		/*
107938032Speter		**  Output per-message information.
108038032Speter		*/
108138032Speter
108238032Speter		/* original envelope id from MAIL FROM: line */
108338032Speter		if (e->e_parent->e_envid != NULL)
108438032Speter		{
1085168520Sgshapiro			(void) sm_snprintf(buf, sizeof(buf),
108664565Sgshapiro					"Original-Envelope-Id: %.800s",
108764565Sgshapiro					xuntextify(e->e_parent->e_envid));
1088157006Sgshapiro			if (!putline(buf, mci))
1089157006Sgshapiro				goto writeerr;
109038032Speter		}
109138032Speter
109238032Speter		/* Reporting-MTA: is us (required) */
1093168520Sgshapiro		(void) sm_snprintf(buf, sizeof(buf),
109490795Sgshapiro				   "Reporting-MTA: dns; %.800s", MyHostName);
1095157006Sgshapiro		if (!putline(buf, mci))
1096157006Sgshapiro			goto writeerr;
109738032Speter
109838032Speter		/* DSN-Gateway: not relevant since we are not translating */
109938032Speter
110038032Speter		/* Received-From-MTA: shows where we got this message from */
110138032Speter		if (RealHostName != NULL)
110238032Speter		{
110338032Speter			/* XXX use $s for type? */
110438032Speter			if (e->e_parent->e_from.q_mailer == NULL ||
110538032Speter			    (p = e->e_parent->e_from.q_mailer->m_mtatype) == NULL)
110638032Speter				p = "dns";
1107168520Sgshapiro			(void) sm_snprintf(buf, sizeof(buf),
110864565Sgshapiro					"Received-From-MTA: %s; %.800s",
110964565Sgshapiro					p, RealHostName);
1110157006Sgshapiro			if (!putline(buf, mci))
1111157006Sgshapiro				goto writeerr;
111238032Speter		}
111338032Speter
111438032Speter		/* Arrival-Date: -- when it arrived here */
1115168520Sgshapiro		(void) sm_strlcpyn(buf, sizeof(buf), 2, "Arrival-Date: ",
111664565Sgshapiro				arpadate(ctime(&e->e_parent->e_ctime)));
1117157006Sgshapiro		if (!putline(buf, mci))
1118157006Sgshapiro			goto writeerr;
111938032Speter
112090795Sgshapiro		/* Deliver-By-Date: -- when it should have been delivered */
112190795Sgshapiro		if (IS_DLVR_BY(e->e_parent))
112290795Sgshapiro		{
112390795Sgshapiro			time_t dbyd;
112490795Sgshapiro
112590795Sgshapiro			dbyd = e->e_parent->e_ctime + e->e_parent->e_deliver_by;
1126168520Sgshapiro			(void) sm_strlcpyn(buf, sizeof(buf), 2,
112790795Sgshapiro					"Deliver-By-Date: ",
112890795Sgshapiro					arpadate(ctime(&dbyd)));
1129157006Sgshapiro			if (!putline(buf, mci))
1130157006Sgshapiro				goto writeerr;
113190795Sgshapiro		}
113290795Sgshapiro
113338032Speter		/*
113438032Speter		**  Output per-address information.
113538032Speter		*/
113638032Speter
113738032Speter		for (q = e->e_parent->e_sendqueue; q != NULL; q = q->q_next)
113838032Speter		{
113938032Speter			char *action;
114038032Speter
114164565Sgshapiro			if (QS_IS_BADADDR(q->q_state))
114271348Sgshapiro			{
114371348Sgshapiro				/* RFC 1891, 6.2.6 (b) */
114471348Sgshapiro				if (bitset(QHASNOTIFY, q->q_flags) &&
114571348Sgshapiro				    !bitset(QPINGONFAILURE, q->q_flags))
114671348Sgshapiro					continue;
114738032Speter				action = "failed";
114871348Sgshapiro			}
114938032Speter			else if (!bitset(QPRIMARY, q->q_flags))
115038032Speter				continue;
115138032Speter			else if (bitset(QDELIVERED, q->q_flags))
115238032Speter			{
115338032Speter				if (bitset(QEXPANDED, q->q_flags))
115438032Speter					action = "delivered (to mailing list)";
115538032Speter				else
115638032Speter					action = "delivered (to mailbox)";
115738032Speter			}
115838032Speter			else if (bitset(QRELAYED, q->q_flags))
115938032Speter				action = "relayed (to non-DSN-aware mailer)";
116038032Speter			else if (bitset(QEXPANDED, q->q_flags))
116138032Speter				action = "expanded (to multi-recipient alias)";
116238032Speter			else if (bitset(QDELAYED, q->q_flags))
116338032Speter				action = "delayed";
116490795Sgshapiro			else if (bitset(QBYTRACE, q->q_flags))
116590795Sgshapiro				action = "relayed (Deliver-By trace mode)";
116690795Sgshapiro			else if (bitset(QBYNDELAY, q->q_flags))
116790795Sgshapiro				action = "delayed (Deliver-By notify mode)";
116890795Sgshapiro			else if (bitset(QBYNRELAY, q->q_flags))
116990795Sgshapiro				action = "relayed (Deliver-By notify mode)";
117038032Speter			else
117138032Speter				continue;
117238032Speter
1173157006Sgshapiro			if (!putline("", mci))
1174157006Sgshapiro				goto writeerr;
117538032Speter
117638032Speter			/* Original-Recipient: -- passed from on high */
117738032Speter			if (q->q_orcpt != NULL)
117838032Speter			{
1179249729Sgshapiro				p = strchr(q->q_orcpt, ';');
1180249729Sgshapiro
1181249729Sgshapiro				/*
1182249729Sgshapiro				**  p == NULL shouldn't happen due to
1183249729Sgshapiro				**  check in srvrsmtp.c
1184249729Sgshapiro				**  we could log an error in this case.
1185249729Sgshapiro				*/
1186249729Sgshapiro
1187249729Sgshapiro				if (p != NULL)
1188249729Sgshapiro				{
1189249729Sgshapiro					*p = '\0';
1190249729Sgshapiro					(void) sm_snprintf(buf, sizeof(buf),
1191249729Sgshapiro						"Original-Recipient: %.100s;%.700s",
1192249729Sgshapiro						q->q_orcpt, xuntextify(p + 1));
1193249729Sgshapiro					*p = ';';
1194249729Sgshapiro					if (!putline(buf, mci))
1195249729Sgshapiro						goto writeerr;
1196249729Sgshapiro				}
119738032Speter			}
119838032Speter
119990795Sgshapiro			/* Figure out actual recipient */
120090795Sgshapiro			actual[0] = '\0';
120190795Sgshapiro			if (q->q_user[0] != '\0')
120238032Speter			{
120364565Sgshapiro				if (q->q_mailer != NULL &&
120464565Sgshapiro				    q->q_mailer->m_addrtype != NULL)
120564565Sgshapiro					p = q->q_mailer->m_addrtype;
120664565Sgshapiro				else
120764565Sgshapiro					p = "rfc822";
120864565Sgshapiro
120990795Sgshapiro				if (sm_strcasecmp(p, "rfc822") == 0 &&
121064565Sgshapiro				    strchr(q->q_user, '@') == NULL)
121138032Speter				{
121290795Sgshapiro					(void) sm_snprintf(actual,
1213168520Sgshapiro							   sizeof(actual),
121490795Sgshapiro							   "%s; %.700s@%.100s",
121590795Sgshapiro							   p, q->q_user,
121690795Sgshapiro							   MyHostName);
121738032Speter				}
121838032Speter				else
121938032Speter				{
122090795Sgshapiro					(void) sm_snprintf(actual,
1221168520Sgshapiro							   sizeof(actual),
122290795Sgshapiro							   "%s; %.800s",
122390795Sgshapiro							   p, q->q_user);
122438032Speter				}
122590795Sgshapiro			}
122690795Sgshapiro
122790795Sgshapiro			/* Final-Recipient: -- the name from the RCPT command */
122890795Sgshapiro			if (q->q_finalrcpt == NULL)
122990795Sgshapiro			{
123090795Sgshapiro				/* should never happen */
123190795Sgshapiro				sm_syslog(LOG_ERR, e->e_id,
123290795Sgshapiro					  "returntosender: q_finalrcpt is NULL");
123390795Sgshapiro
123490795Sgshapiro				/* try to fall back to the actual recipient */
123590795Sgshapiro				if (actual[0] != '\0')
123690795Sgshapiro					q->q_finalrcpt = sm_rpool_strdup_x(e->e_rpool,
123790795Sgshapiro									   actual);
123890795Sgshapiro			}
123990795Sgshapiro
124090795Sgshapiro			if (q->q_finalrcpt != NULL)
124190795Sgshapiro			{
1242168520Sgshapiro				(void) sm_snprintf(buf, sizeof(buf),
124390795Sgshapiro						   "Final-Recipient: %s",
124490795Sgshapiro						   q->q_finalrcpt);
1245157006Sgshapiro				if (!putline(buf, mci))
1246157006Sgshapiro					goto writeerr;
124738032Speter			}
124838032Speter
124990795Sgshapiro			/* X-Actual-Recipient: -- the real problem address */
125090795Sgshapiro			if (actual[0] != '\0' &&
125190795Sgshapiro			    q->q_finalrcpt != NULL &&
1252141862Sgshapiro			    !bitset(PRIV_NOACTUALRECIPIENT, PrivacyFlags) &&
125390795Sgshapiro			    strcmp(actual, q->q_finalrcpt) != 0)
125490795Sgshapiro			{
1255168520Sgshapiro				(void) sm_snprintf(buf, sizeof(buf),
125690795Sgshapiro						   "X-Actual-Recipient: %s",
125790795Sgshapiro						   actual);
1258157006Sgshapiro				if (!putline(buf, mci))
1259157006Sgshapiro					goto writeerr;
126090795Sgshapiro			}
126190795Sgshapiro
126238032Speter			/* Action: -- what happened? */
1263168520Sgshapiro			(void) sm_strlcpyn(buf, sizeof(buf), 2, "Action: ",
126490795Sgshapiro				action);
1265157006Sgshapiro			if (!putline(buf, mci))
1266157006Sgshapiro				goto writeerr;
126738032Speter
126838032Speter			/* Status: -- what _really_ happened? */
126938032Speter			if (q->q_status != NULL)
127038032Speter				p = q->q_status;
127164565Sgshapiro			else if (QS_IS_BADADDR(q->q_state))
127238032Speter				p = "5.0.0";
127364565Sgshapiro			else if (QS_IS_QUEUEUP(q->q_state))
127438032Speter				p = "4.0.0";
127538032Speter			else
127638032Speter				p = "2.0.0";
1277168520Sgshapiro			(void) sm_strlcpyn(buf, sizeof(buf), 2, "Status: ", p);
1278157006Sgshapiro			if (!putline(buf, mci))
1279157006Sgshapiro				goto writeerr;
128038032Speter
128138032Speter			/* Remote-MTA: -- who was I talking to? */
128238032Speter			if (q->q_statmta != NULL)
128338032Speter			{
128438032Speter				if (q->q_mailer == NULL ||
128538032Speter				    (p = q->q_mailer->m_mtatype) == NULL)
128638032Speter					p = "dns";
1287168520Sgshapiro				(void) sm_snprintf(buf, sizeof(buf),
128864565Sgshapiro						"Remote-MTA: %s; %.800s",
128964565Sgshapiro						p, q->q_statmta);
129038032Speter				p = &buf[strlen(buf) - 1];
129138032Speter				if (*p == '.')
129238032Speter					*p = '\0';
1293157006Sgshapiro				if (!putline(buf, mci))
1294157006Sgshapiro					goto writeerr;
129538032Speter			}
129638032Speter
129738032Speter			/* Diagnostic-Code: -- actual result from other end */
129838032Speter			if (q->q_rstatus != NULL)
129938032Speter			{
1300159613Sgshapiro				if (q->q_mailer == NULL ||
1301159613Sgshapiro				    (p = q->q_mailer->m_diagtype) == NULL)
130238032Speter					p = "smtp";
1303168520Sgshapiro				(void) sm_snprintf(buf, sizeof(buf),
130464565Sgshapiro						"Diagnostic-Code: %s; %.800s",
130564565Sgshapiro						p, q->q_rstatus);
1306157006Sgshapiro				if (!putline(buf, mci))
1307157006Sgshapiro					goto writeerr;
130838032Speter			}
130938032Speter
131038032Speter			/* Last-Attempt-Date: -- fine granularity */
131138032Speter			if (q->q_statdate == (time_t) 0L)
131290795Sgshapiro				q->q_statdate = curtime();
1313168520Sgshapiro			(void) sm_strlcpyn(buf, sizeof(buf), 2,
131490795Sgshapiro					"Last-Attempt-Date: ",
131564565Sgshapiro					arpadate(ctime(&q->q_statdate)));
1316157006Sgshapiro			if (!putline(buf, mci))
1317157006Sgshapiro				goto writeerr;
131838032Speter
131938032Speter			/* Will-Retry-Until: -- for delayed messages only */
132064565Sgshapiro			if (QS_IS_QUEUEUP(q->q_state))
132138032Speter			{
132238032Speter				time_t xdate;
132338032Speter
132438032Speter				xdate = e->e_parent->e_ctime +
132538032Speter					TimeOuts.to_q_return[e->e_parent->e_timeoutclass];
1326168520Sgshapiro				(void) sm_strlcpyn(buf, sizeof(buf), 2,
132790795Sgshapiro					 "Will-Retry-Until: ",
132864565Sgshapiro					 arpadate(ctime(&xdate)));
1329157006Sgshapiro				if (!putline(buf, mci))
1330157006Sgshapiro					goto writeerr;
133138032Speter			}
133238032Speter		}
133338032Speter	}
133464565Sgshapiro#endif /* DSN */
133538032Speter
133638032Speter	/*
133738032Speter	**  Output text of original message
133838032Speter	*/
133938032Speter
1340157006Sgshapiro	if (!putline("", mci))
1341157006Sgshapiro		goto writeerr;
134238032Speter	if (bitset(EF_HAS_DF, e->e_parent->e_flags))
134338032Speter	{
134438032Speter		sendbody = !bitset(EF_NO_BODY_RETN, e->e_parent->e_flags) &&
134538032Speter			   !bitset(EF_NO_BODY_RETN, e->e_flags);
134638032Speter
134738032Speter		if (e->e_msgboundary == NULL)
134838032Speter		{
1349157006Sgshapiro			if (!putline(
1350157006Sgshapiro				sendbody
1351157006Sgshapiro				? "   ----- Original message follows -----\n"
1352157006Sgshapiro				: "   ----- Message header follows -----\n",
1353157006Sgshapiro				mci))
1354157006Sgshapiro			{
1355157006Sgshapiro				goto writeerr;
1356157006Sgshapiro			}
135738032Speter		}
135838032Speter		else
135938032Speter		{
1360168520Sgshapiro			(void) sm_strlcpyn(buf, sizeof(buf), 2, "--",
136164565Sgshapiro					e->e_msgboundary);
136238032Speter
1363157006Sgshapiro			if (!putline(buf, mci))
1364157006Sgshapiro				goto writeerr;
1365168520Sgshapiro			(void) sm_strlcpyn(buf, sizeof(buf), 2, "Content-Type: ",
136664565Sgshapiro					sendbody ? "message/rfc822"
136764565Sgshapiro						 : "text/rfc822-headers");
1368157006Sgshapiro			if (!putline(buf, mci))
1369157006Sgshapiro				goto writeerr;
137038032Speter
137164565Sgshapiro			p = hvalue("Content-Transfer-Encoding",
137264565Sgshapiro				   e->e_parent->e_header);
137390795Sgshapiro			if (p != NULL && sm_strcasecmp(p, "binary") != 0)
137438032Speter				p = NULL;
137564565Sgshapiro			if (p == NULL &&
137664565Sgshapiro			    bitset(EF_HAS8BIT, e->e_parent->e_flags))
137738032Speter				p = "8bit";
137838032Speter			if (p != NULL)
137938032Speter			{
1380168520Sgshapiro				(void) sm_snprintf(buf, sizeof(buf),
138164565Sgshapiro						"Content-Transfer-Encoding: %s",
138264565Sgshapiro						p);
1383157006Sgshapiro				if (!putline(buf, mci))
1384157006Sgshapiro					goto writeerr;
138538032Speter			}
138638032Speter		}
1387157006Sgshapiro		if (!putline("", mci))
1388157006Sgshapiro			goto writeerr;
138964565Sgshapiro		save_errno = errno;
1390157006Sgshapiro		if (!putheader(mci, e->e_parent->e_header, e->e_parent,
1391157006Sgshapiro				M87F_OUTER))
1392157006Sgshapiro			goto writeerr;
139364565Sgshapiro		errno = save_errno;
139438032Speter		if (sendbody)
1395157006Sgshapiro		{
1396157006Sgshapiro			if (!putbody(mci, e->e_parent, e->e_msgboundary))
1397157006Sgshapiro				goto writeerr;
1398157006Sgshapiro		}
139938032Speter		else if (e->e_msgboundary == NULL)
140038032Speter		{
1401157006Sgshapiro			if (!putline("", mci) ||
1402157006Sgshapiro			    !putline("   ----- Message body suppressed -----",
1403157006Sgshapiro					mci))
1404157006Sgshapiro			{
1405157006Sgshapiro				goto writeerr;
1406157006Sgshapiro			}
140738032Speter		}
140838032Speter	}
140938032Speter	else if (e->e_msgboundary == NULL)
141038032Speter	{
1411157006Sgshapiro		if (!putline("  ----- No message was collected -----\n", mci))
1412157006Sgshapiro			goto writeerr;
141338032Speter	}
141438032Speter
141538032Speter	if (e->e_msgboundary != NULL)
141638032Speter	{
1417168520Sgshapiro		(void) sm_strlcpyn(buf, sizeof(buf), 3, "--", e->e_msgboundary,
141890795Sgshapiro				   "--");
1419157006Sgshapiro		if (!putline("", mci) || !putline(buf, mci))
1420157006Sgshapiro			goto writeerr;
142138032Speter	}
1422157006Sgshapiro	if (!putline("", mci) ||
1423157006Sgshapiro	    sm_io_flush(mci->mci_out, SM_TIME_DEFAULT) == SM_IO_EOF)
1424157006Sgshapiro			goto writeerr;
142538032Speter
142638032Speter	/*
142738032Speter	**  Cleanup and exit
142838032Speter	*/
142938032Speter
143038032Speter	if (errno != 0)
1431157006Sgshapiro	{
1432157006Sgshapiro  writeerr:
143338032Speter		syserr("errbody: I/O error");
1434157006Sgshapiro		return false;
1435157006Sgshapiro	}
1436157006Sgshapiro	return true;
143738032Speter}
1438157006Sgshapiro
143990795Sgshapiro/*
144038032Speter**  SMTPTODSN -- convert SMTP to DSN status code
144138032Speter**
144238032Speter**	Parameters:
144338032Speter**		smtpstat -- the smtp status code (e.g., 550).
144438032Speter**
144538032Speter**	Returns:
144638032Speter**		The DSN version of the status code.
144790795Sgshapiro**
144890795Sgshapiro**	Storage Management:
144990795Sgshapiro**		smtptodsn() returns a pointer to a character string literal,
145090795Sgshapiro**		which will remain valid forever, and thus does not need to
145190795Sgshapiro**		be copied.  Current code relies on this property.
145238032Speter*/
145338032Speter
145438032Speterchar *
145538032Spetersmtptodsn(smtpstat)
145638032Speter	int smtpstat;
145738032Speter{
145838032Speter	if (smtpstat < 0)
145938032Speter		return "4.4.2";
146038032Speter
146138032Speter	switch (smtpstat)
146238032Speter	{
146338032Speter	  case 450:	/* Req mail action not taken: mailbox unavailable */
146438032Speter		return "4.2.0";
146538032Speter
146638032Speter	  case 451:	/* Req action aborted: local error in processing */
146738032Speter		return "4.3.0";
146838032Speter
146938032Speter	  case 452:	/* Req action not taken: insufficient sys storage */
147038032Speter		return "4.3.1";
147138032Speter
147238032Speter	  case 500:	/* Syntax error, command unrecognized */
147338032Speter		return "5.5.2";
147438032Speter
147538032Speter	  case 501:	/* Syntax error in parameters or arguments */
147638032Speter		return "5.5.4";
147738032Speter
147838032Speter	  case 502:	/* Command not implemented */
147938032Speter		return "5.5.1";
148038032Speter
148138032Speter	  case 503:	/* Bad sequence of commands */
148238032Speter		return "5.5.1";
148338032Speter
148438032Speter	  case 504:	/* Command parameter not implemented */
148538032Speter		return "5.5.4";
148638032Speter
148738032Speter	  case 550:	/* Req mail action not taken: mailbox unavailable */
148838032Speter		return "5.2.0";
148938032Speter
149038032Speter	  case 551:	/* User not local; please try <...> */
149138032Speter		return "5.1.6";
149238032Speter
149338032Speter	  case 552:	/* Req mail action aborted: exceeded storage alloc */
149438032Speter		return "5.2.2";
149538032Speter
149638032Speter	  case 553:	/* Req action not taken: mailbox name not allowed */
149738032Speter		return "5.1.0";
149838032Speter
149938032Speter	  case 554:	/* Transaction failed */
150038032Speter		return "5.0.0";
150138032Speter	}
150238032Speter
1503157006Sgshapiro	if (REPLYTYPE(smtpstat) == 2)
150438032Speter		return "2.0.0";
1505157006Sgshapiro	if (REPLYTYPE(smtpstat) == 4)
150638032Speter		return "4.0.0";
150738032Speter	return "5.0.0";
150838032Speter}
150990795Sgshapiro/*
151038032Speter**  XTEXTIFY -- take regular text and turn it into DSN-style xtext
151138032Speter**
151238032Speter**	Parameters:
151338032Speter**		t -- the text to convert.
151438032Speter**		taboo -- additional characters that must be encoded.
151538032Speter**
151638032Speter**	Returns:
151738032Speter**		The xtext-ified version of the same string.
151838032Speter*/
151938032Speter
152038032Speterchar *
152138032Speterxtextify(t, taboo)
152238032Speter	register char *t;
152338032Speter	char *taboo;
152438032Speter{
152538032Speter	register char *p;
152638032Speter	int l;
152738032Speter	int nbogus;
152838032Speter	static char *bp = NULL;
152938032Speter	static int bplen = 0;
153038032Speter
153138032Speter	if (taboo == NULL)
153238032Speter		taboo = "";
153338032Speter
153438032Speter	/* figure out how long this xtext will have to be */
153538032Speter	nbogus = l = 0;
153638032Speter	for (p = t; *p != '\0'; p++)
153738032Speter	{
153838032Speter		register int c = (*p & 0xff);
153938032Speter
154038032Speter		/* ASCII dependence here -- this is the way the spec words it */
154138032Speter		if (c < '!' || c > '~' || c == '+' || c == '\\' || c == '(' ||
154238032Speter		    strchr(taboo, c) != NULL)
154338032Speter			nbogus++;
154438032Speter		l++;
154538032Speter	}
154690795Sgshapiro	if (nbogus < 0)
154790795Sgshapiro	{
154890795Sgshapiro		/* since nbogus is ssize_t and wrapped, 2 * size_t would wrap */
154990795Sgshapiro		syserr("!xtextify string too long");
155090795Sgshapiro	}
155138032Speter	if (nbogus == 0)
155238032Speter		return t;
155338032Speter	l += nbogus * 2 + 1;
155438032Speter
155538032Speter	/* now allocate space if necessary for the new string */
155638032Speter	if (l > bplen)
155738032Speter	{
155838032Speter		if (bp != NULL)
155990795Sgshapiro			sm_free(bp); /* XXX */
156090795Sgshapiro		bp = sm_pmalloc_x(l);
156138032Speter		bplen = l;
156238032Speter	}
156338032Speter
156438032Speter	/* ok, copy the text with byte expansion */
156538032Speter	for (p = bp; *t != '\0'; )
156638032Speter	{
156738032Speter		register int c = (*t++ & 0xff);
156838032Speter
156938032Speter		/* ASCII dependence here -- this is the way the spec words it */
157038032Speter		if (c < '!' || c > '~' || c == '+' || c == '\\' || c == '(' ||
157138032Speter		    strchr(taboo, c) != NULL)
157238032Speter		{
157338032Speter			*p++ = '+';
157464565Sgshapiro			*p++ = "0123456789ABCDEF"[c >> 4];
157564565Sgshapiro			*p++ = "0123456789ABCDEF"[c & 0xf];
157638032Speter		}
157738032Speter		else
157838032Speter			*p++ = c;
157938032Speter	}
158038032Speter	*p = '\0';
158138032Speter	return bp;
158238032Speter}
158390795Sgshapiro/*
158438032Speter**  XUNTEXTIFY -- take xtext and turn it into plain text
158538032Speter**
158638032Speter**	Parameters:
158738032Speter**		t -- the xtextified text.
158838032Speter**
158938032Speter**	Returns:
159038032Speter**		The decoded text.  No attempt is made to deal with
159138032Speter**		null strings in the resulting text.
159238032Speter*/
159338032Speter
159438032Speterchar *
159538032Speterxuntextify(t)
159638032Speter	register char *t;
159738032Speter{
159838032Speter	register char *p;
159938032Speter	int l;
160038032Speter	static char *bp = NULL;
160138032Speter	static int bplen = 0;
160238032Speter
160338032Speter	/* heuristic -- if no plus sign, just return the input */
160438032Speter	if (strchr(t, '+') == NULL)
160538032Speter		return t;
160638032Speter
160738032Speter	/* xtext is always longer than decoded text */
160838032Speter	l = strlen(t);
160938032Speter	if (l > bplen)
161038032Speter	{
161138032Speter		if (bp != NULL)
161290795Sgshapiro			sm_free(bp); /* XXX */
161338032Speter		bp = xalloc(l);
161438032Speter		bplen = l;
161538032Speter	}
161638032Speter
161738032Speter	/* ok, copy the text with byte compression */
161838032Speter	for (p = bp; *t != '\0'; t++)
161938032Speter	{
162038032Speter		register int c = *t & 0xff;
162138032Speter
162238032Speter		if (c != '+')
162338032Speter		{
162438032Speter			*p++ = c;
162538032Speter			continue;
162638032Speter		}
162738032Speter
162838032Speter		c = *++t & 0xff;
162938032Speter		if (!isascii(c) || !isxdigit(c))
163038032Speter		{
163138032Speter			/* error -- first digit is not hex */
163238032Speter			usrerr("bogus xtext: +%c", c);
163338032Speter			t--;
163438032Speter			continue;
163538032Speter		}
163638032Speter		if (isdigit(c))
163738032Speter			c -= '0';
163838032Speter		else if (isupper(c))
163938032Speter			c -= 'A' - 10;
164038032Speter		else
164138032Speter			c -= 'a' - 10;
164238032Speter		*p = c << 4;
164338032Speter
164438032Speter		c = *++t & 0xff;
164538032Speter		if (!isascii(c) || !isxdigit(c))
164638032Speter		{
164738032Speter			/* error -- second digit is not hex */
164838032Speter			usrerr("bogus xtext: +%x%c", *p >> 4, c);
164938032Speter			t--;
165038032Speter			continue;
165138032Speter		}
165238032Speter		if (isdigit(c))
165338032Speter			c -= '0';
165438032Speter		else if (isupper(c))
165538032Speter			c -= 'A' - 10;
165638032Speter		else
165738032Speter			c -= 'a' - 10;
165838032Speter		*p++ |= c;
165938032Speter	}
166038032Speter	*p = '\0';
166138032Speter	return bp;
166238032Speter}
166390795Sgshapiro/*
166438032Speter**  XTEXTOK -- check if a string is legal xtext
166538032Speter**
166638032Speter**	Xtext is used in Delivery Status Notifications.  The spec was
166738032Speter**	taken from RFC 1891, ``SMTP Service Extension for Delivery
166838032Speter**	Status Notifications''.
166938032Speter**
167038032Speter**	Parameters:
167138032Speter**		s -- the string to check.
167238032Speter**
167338032Speter**	Returns:
167490795Sgshapiro**		true -- if 's' is legal xtext.
167590795Sgshapiro**		false -- if it has any illegal characters in it.
167638032Speter*/
167738032Speter
167838032Speterbool
167938032Speterxtextok(s)
168038032Speter	char *s;
168138032Speter{
168238032Speter	int c;
168338032Speter
168438032Speter	while ((c = *s++) != '\0')
168538032Speter	{
168638032Speter		if (c == '+')
168738032Speter		{
168838032Speter			c = *s++;
168938032Speter			if (!isascii(c) || !isxdigit(c))
169090795Sgshapiro				return false;
169138032Speter			c = *s++;
169238032Speter			if (!isascii(c) || !isxdigit(c))
169390795Sgshapiro				return false;
169438032Speter		}
169538032Speter		else if (c < '!' || c > '~' || c == '=')
169690795Sgshapiro			return false;
169738032Speter	}
169890795Sgshapiro	return true;
169938032Speter}
1700249729Sgshapiro
170190795Sgshapiro/*
1702249729Sgshapiro**  ISATOM -- check if a string is an "atom"
1703249729Sgshapiro**
1704249729Sgshapiro**	Parameters:
1705249729Sgshapiro**		s -- the string to check.
1706249729Sgshapiro**
1707249729Sgshapiro**	Returns:
1708249729Sgshapiro**		true -- iff s is an atom
1709249729Sgshapiro*/
1710249729Sgshapiro
1711249729Sgshapirobool
1712249729Sgshapiroisatom(s)
1713249729Sgshapiro	const char *s;
1714249729Sgshapiro{
1715249729Sgshapiro	int c;
1716249729Sgshapiro
1717249729Sgshapiro	if (s == NULL || *s == '\0')
1718249729Sgshapiro		return false;
1719249729Sgshapiro	while ((c = *s++) != '\0')
1720249729Sgshapiro	{
1721249729Sgshapiro		if (strchr("()<>@,;:\\.[]\"", c) != NULL)
1722249729Sgshapiro			return false;
1723249729Sgshapiro		if (c < '!' || c > '~')
1724249729Sgshapiro			return false;
1725249729Sgshapiro	}
1726249729Sgshapiro	return true;
1727249729Sgshapiro}
1728249729Sgshapiro/*
172938032Speter**  PRUNEROUTE -- prune an RFC-822 source route
173064565Sgshapiro**
173138032Speter**	Trims down a source route to the last internet-registered hop.
173238032Speter**	This is encouraged by RFC 1123 section 5.3.3.
173364565Sgshapiro**
173438032Speter**	Parameters:
173538032Speter**		addr -- the address
173664565Sgshapiro**
173738032Speter**	Returns:
173890795Sgshapiro**		true -- address was modified
173990795Sgshapiro**		false -- address could not be pruned
174064565Sgshapiro**
174138032Speter**	Side Effects:
174238032Speter**		modifies addr in-place
174338032Speter*/
174438032Speter
174564565Sgshapirostatic bool
174638032Speterpruneroute(addr)
174738032Speter	char *addr;
174838032Speter{
174938032Speter#if NAMED_BIND
175038032Speter	char *start, *at, *comma;
175138032Speter	char c;
175290795Sgshapiro	int braclev;
175338032Speter	int rcode;
175438032Speter	int i;
175538032Speter	char hostbuf[BUFSIZ];
175638032Speter	char *mxhosts[MAXMXHOSTS + 1];
175738032Speter
175838032Speter	/* check to see if this is really a route-addr */
175938032Speter	if (*addr != '<' || addr[1] != '@' || addr[strlen(addr) - 1] != '>')
176090795Sgshapiro		return false;
176190795Sgshapiro
176290795Sgshapiro	/*
176390795Sgshapiro	**  Can't simply find the first ':' is the address might be in the
176490795Sgshapiro	**  form:  "<@[IPv6:::1]:user@host>" and the first ':' in inside
176590795Sgshapiro	**  the IPv6 address.
176690795Sgshapiro	*/
176790795Sgshapiro
176890795Sgshapiro	start = addr;
176990795Sgshapiro	braclev = 0;
177090795Sgshapiro	while (*start != '\0')
177190795Sgshapiro	{
177290795Sgshapiro		if (*start == ':' && braclev <= 0)
177390795Sgshapiro			break;
177490795Sgshapiro		else if (*start == '[')
177590795Sgshapiro			braclev++;
177690795Sgshapiro		else if (*start == ']' && braclev > 0)
177790795Sgshapiro			braclev--;
177890795Sgshapiro		start++;
177990795Sgshapiro	}
178090795Sgshapiro	if (braclev > 0 || *start != ':')
178190795Sgshapiro		return false;
178290795Sgshapiro
178338032Speter	at = strrchr(addr, '@');
178490795Sgshapiro	if (at == NULL || at < start)
178590795Sgshapiro		return false;
178638032Speter
178738032Speter	/* slice off the angle brackets */
178838032Speter	i = strlen(at + 1);
1789168520Sgshapiro	if (i >= sizeof(hostbuf))
179090795Sgshapiro		return false;
1791168520Sgshapiro	(void) sm_strlcpy(hostbuf, at + 1, sizeof(hostbuf));
179238032Speter	hostbuf[i - 1] = '\0';
179338032Speter
179490795Sgshapiro	while (start != NULL)
179538032Speter	{
179690795Sgshapiro		if (getmxrr(hostbuf, mxhosts, NULL, false,
179790795Sgshapiro			    &rcode, true, NULL) > 0)
179838032Speter		{
179990795Sgshapiro			(void) sm_strlcpy(addr + 1, start + 1,
180090795Sgshapiro					  strlen(addr) - 1);
180190795Sgshapiro			return true;
180238032Speter		}
180338032Speter		c = *start;
180438032Speter		*start = '\0';
180538032Speter		comma = strrchr(addr, ',');
180638032Speter		if (comma != NULL && comma[1] == '@' &&
1807168520Sgshapiro		    strlen(comma + 2) < sizeof(hostbuf))
1808168520Sgshapiro			(void) sm_strlcpy(hostbuf, comma + 2, sizeof(hostbuf));
180938032Speter		else
181038032Speter			comma = NULL;
181138032Speter		*start = c;
181238032Speter		start = comma;
181338032Speter	}
181464565Sgshapiro#endif /* NAMED_BIND */
181590795Sgshapiro	return false;
181638032Speter}
1817