savemail.c revision 110563
1/*
2 * Copyright (c) 1998-2002 Sendmail, Inc. and its suppliers.
3 *	All rights reserved.
4 * Copyright (c) 1983, 1995-1997 Eric P. Allman.  All rights reserved.
5 * Copyright (c) 1988, 1993
6 *	The Regents of the University of California.  All rights reserved.
7 *
8 * By using this file, you agree to the terms and conditions set
9 * forth in the LICENSE file which can be found at the top level of
10 * the sendmail distribution.
11 *
12 * $FreeBSD: head/contrib/sendmail/src/savemail.c 110563 2003-02-08 20:35:51Z gshapiro $
13 *
14 */
15
16#include <sendmail.h>
17
18SM_RCSID("@(#)$Id: savemail.c,v 8.299.2.1 2002/10/23 15:08:47 ca Exp $")
19
20static void	errbody __P((MCI *, ENVELOPE *, char *));
21static bool	pruneroute __P((char *));
22
23/*
24**  SAVEMAIL -- Save mail on error
25**
26**	If mailing back errors, mail it back to the originator
27**	together with an error message; otherwise, just put it in
28**	dead.letter in the user's home directory (if he exists on
29**	this machine).
30**
31**	Parameters:
32**		e -- the envelope containing the message in error.
33**		sendbody -- if true, also send back the body of the
34**			message; otherwise just send the header.
35**
36**	Returns:
37**		true if savemail panic'ed, (i.e., the data file should
38**		be preserved by dropenvelope())
39**
40**	Side Effects:
41**		Saves the letter, by writing or mailing it back to the
42**		sender, or by putting it in dead.letter in her home
43**		directory.
44*/
45
46/* defines for state machine */
47#define ESM_REPORT		0	/* report to sender's terminal */
48#define ESM_MAIL		1	/* mail back to sender */
49#define ESM_QUIET		2	/* mail has already been returned */
50#define ESM_DEADLETTER		3	/* save in ~/dead.letter */
51#define ESM_POSTMASTER		4	/* return to postmaster */
52#define ESM_DEADLETTERDROP	5	/* save in DeadLetterDrop */
53#define ESM_PANIC		6	/* call loseqfile() */
54#define ESM_DONE		7	/* message is successfully delivered */
55
56bool
57savemail(e, sendbody)
58	register ENVELOPE *e;
59	bool sendbody;
60{
61	register SM_FILE_T *fp;
62	bool panic = false;
63	int state;
64	auto ADDRESS *q = NULL;
65	register char *p;
66	MCI mcibuf;
67	int flags;
68	long sff;
69	char buf[MAXLINE + 1];
70	char dlbuf[MAXPATHLEN];
71	SM_MBDB_T user;
72
73
74	if (tTd(6, 1))
75	{
76		sm_dprintf("\nsavemail, errormode = %c, id = %s, ExitStat = %d\n  e_from=",
77			e->e_errormode, e->e_id == NULL ? "NONE" : e->e_id,
78			ExitStat);
79		printaddr(&e->e_from, false);
80	}
81
82	if (e->e_id == NULL)
83	{
84		/* can't return a message with no id */
85		return panic;
86	}
87
88	/*
89	**  In the unhappy event we don't know who to return the mail
90	**  to, make someone up.
91	*/
92
93	if (e->e_from.q_paddr == NULL)
94	{
95		e->e_sender = "Postmaster";
96		if (parseaddr(e->e_sender, &e->e_from,
97			      RF_COPYPARSE|RF_SENDERADDR,
98			      '\0', NULL, e, false) == NULL)
99		{
100			syserr("553 5.3.5 Cannot parse Postmaster!");
101			finis(true, true, EX_SOFTWARE);
102		}
103	}
104	e->e_to = NULL;
105
106	/*
107	**  Basic state machine.
108	**
109	**	This machine runs through the following states:
110	**
111	**	ESM_QUIET	Errors have already been printed iff the
112	**			sender is local.
113	**	ESM_REPORT	Report directly to the sender's terminal.
114	**	ESM_MAIL	Mail response to the sender.
115	**	ESM_DEADLETTER	Save response in ~/dead.letter.
116	**	ESM_POSTMASTER	Mail response to the postmaster.
117	**	ESM_DEADLETTERDROP
118	**			If DeadLetterDrop set, save it there.
119	**	ESM_PANIC	Save response anywhere possible.
120	*/
121
122	/* determine starting state */
123	switch (e->e_errormode)
124	{
125	  case EM_WRITE:
126		state = ESM_REPORT;
127		break;
128
129	  case EM_BERKNET:
130	  case EM_MAIL:
131		state = ESM_MAIL;
132		break;
133
134	  case EM_PRINT:
135	  case '\0':
136		state = ESM_QUIET;
137		break;
138
139	  case EM_QUIET:
140		/* no need to return anything at all */
141		return panic;
142
143	  default:
144		syserr("554 5.3.0 savemail: bogus errormode x%x",
145		       e->e_errormode);
146		state = ESM_MAIL;
147		break;
148	}
149
150	/* if this is already an error response, send to postmaster */
151	if (bitset(EF_RESPONSE, e->e_flags))
152	{
153		if (e->e_parent != NULL &&
154		    bitset(EF_RESPONSE, e->e_parent->e_flags))
155		{
156			/* got an error sending a response -- can it */
157			return panic;
158		}
159		state = ESM_POSTMASTER;
160	}
161
162	while (state != ESM_DONE)
163	{
164		if (tTd(6, 5))
165			sm_dprintf("  state %d\n", state);
166
167		switch (state)
168		{
169		  case ESM_QUIET:
170			if (bitnset(M_LOCALMAILER, e->e_from.q_mailer->m_flags))
171				state = ESM_DEADLETTER;
172			else
173				state = ESM_MAIL;
174			break;
175
176		  case ESM_REPORT:
177
178			/*
179			**  If the user is still logged in on the same terminal,
180			**  then write the error messages back to hir (sic).
181			*/
182
183			p = ttypath();
184			if (p == NULL || sm_io_reopen(SmFtStdio,
185						      SM_TIME_DEFAULT,
186						      p, SM_IO_WRONLY, NULL,
187						      smioout) == NULL)
188			{
189				state = ESM_MAIL;
190				break;
191			}
192
193			expand("\201n", buf, sizeof buf, e);
194			(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
195					     "\r\nMessage from %s...\r\n", buf);
196			(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
197					     "Errors occurred while sending mail.\r\n");
198			if (e->e_xfp != NULL)
199			{
200				(void) bfrewind(e->e_xfp);
201				(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
202						     "Transcript follows:\r\n");
203				while (sm_io_fgets(e->e_xfp, SM_TIME_DEFAULT,
204						   buf, sizeof buf) != NULL &&
205				       !sm_io_error(smioout))
206					(void) sm_io_fputs(smioout,
207							   SM_TIME_DEFAULT,
208							   buf);
209			}
210			else
211			{
212				syserr("Cannot open %s",
213				       queuename(e, XSCRPT_LETTER));
214				(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
215						     "Transcript of session is unavailable.\r\n");
216			}
217			(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
218					     "Original message will be saved in dead.letter.\r\n");
219			state = ESM_DEADLETTER;
220			break;
221
222		  case ESM_MAIL:
223			/*
224			**  If mailing back, do it.
225			**	Throw away all further output.  Don't alias,
226			**	since this could cause loops, e.g., if joe
227			**	mails to joe@x, and for some reason the network
228			**	for @x is down, then the response gets sent to
229			**	joe@x, which gives a response, etc.  Also force
230			**	the mail to be delivered even if a version of
231			**	it has already been sent to the sender.
232			**
233			**  If this is a configuration or local software
234			**	error, send to the local postmaster as well,
235			**	since the originator can't do anything
236			**	about it anyway.  Note that this is a full
237			**	copy of the message (intentionally) so that
238			**	the Postmaster can forward things along.
239			*/
240
241			if (ExitStat == EX_CONFIG || ExitStat == EX_SOFTWARE)
242			{
243				(void) sendtolist("postmaster", NULLADDR,
244						  &e->e_errorqueue, 0, e);
245			}
246			if (!emptyaddr(&e->e_from))
247			{
248				char from[TOBUFSIZE];
249
250				if (sm_strlcpy(from, e->e_from.q_paddr,
251						sizeof from) >= sizeof from)
252				{
253					state = ESM_POSTMASTER;
254					break;
255				}
256
257				if (!DontPruneRoutes)
258					(void) pruneroute(from);
259
260				(void) sendtolist(from, NULLADDR,
261						  &e->e_errorqueue, 0, e);
262			}
263
264			/*
265			**  Deliver a non-delivery report to the
266			**  Postmaster-designate (not necessarily
267			**  Postmaster).  This does not include the
268			**  body of the message, for privacy reasons.
269			**  You really shouldn't need this.
270			*/
271
272			e->e_flags |= EF_PM_NOTIFY;
273
274			/* check to see if there are any good addresses */
275			for (q = e->e_errorqueue; q != NULL; q = q->q_next)
276			{
277				if (QS_IS_SENDABLE(q->q_state))
278					break;
279			}
280			if (q == NULL)
281			{
282				/* this is an error-error */
283				state = ESM_POSTMASTER;
284				break;
285			}
286			if (returntosender(e->e_message, e->e_errorqueue,
287					   sendbody ? RTSF_SEND_BODY
288						    : RTSF_NO_BODY,
289					   e) == 0)
290			{
291				state = ESM_DONE;
292				break;
293			}
294
295			/* didn't work -- return to postmaster */
296			state = ESM_POSTMASTER;
297			break;
298
299		  case ESM_POSTMASTER:
300			/*
301			**  Similar to previous case, but to system postmaster.
302			*/
303
304			q = NULL;
305			expand(DoubleBounceAddr, buf, sizeof buf, e);
306
307			/*
308			**  Just drop it on the floor if DoubleBounceAddr
309			**  expands to an empty string.
310			*/
311
312			if (*buf == '\0')
313			{
314				state = ESM_DONE;
315				break;
316			}
317			if (sendtolist(buf, NULLADDR, &q, 0, e) <= 0)
318			{
319				syserr("553 5.3.0 cannot parse %s!", buf);
320				ExitStat = EX_SOFTWARE;
321				state = ESM_DEADLETTERDROP;
322				break;
323			}
324			flags = RTSF_PM_BOUNCE;
325			if (sendbody)
326				flags |= RTSF_SEND_BODY;
327			if (returntosender(e->e_message, q, flags, e) == 0)
328			{
329				state = ESM_DONE;
330				break;
331			}
332
333			/* didn't work -- last resort */
334			state = ESM_DEADLETTERDROP;
335			break;
336
337		  case ESM_DEADLETTER:
338			/*
339			**  Save the message in dead.letter.
340			**	If we weren't mailing back, and the user is
341			**	local, we should save the message in
342			**	~/dead.letter so that the poor person doesn't
343			**	have to type it over again -- and we all know
344			**	what poor typists UNIX users are.
345			*/
346
347			p = NULL;
348			if (bitnset(M_HASPWENT, e->e_from.q_mailer->m_flags))
349			{
350				if (e->e_from.q_home != NULL)
351					p = e->e_from.q_home;
352				else if (sm_mbdb_lookup(e->e_from.q_user, &user)
353					 == EX_OK &&
354					 *user.mbdb_homedir != '\0')
355					p = user.mbdb_homedir;
356			}
357			if (p == NULL || e->e_dfp == NULL)
358			{
359				/* no local directory or no data file */
360				state = ESM_MAIL;
361				break;
362			}
363
364			/* we have a home directory; write dead.letter */
365			macdefine(&e->e_macro, A_TEMP, 'z', p);
366
367			/* get the sender for the UnixFromLine */
368			p = macvalue('g', e);
369			macdefine(&e->e_macro, A_PERM, 'g', e->e_sender);
370
371			expand("\201z/dead.letter", dlbuf, sizeof dlbuf, e);
372			sff = SFF_CREAT|SFF_REGONLY|SFF_RUNASREALUID;
373			if (RealUid == 0)
374				sff |= SFF_ROOTOK;
375			e->e_to = dlbuf;
376			if (writable(dlbuf, NULL, sff) &&
377			    mailfile(dlbuf, FileMailer, NULL, sff, e) == EX_OK)
378			{
379				int oldverb = Verbose;
380
381				if (OpMode != MD_DAEMON && OpMode != MD_SMTP)
382					Verbose = 1;
383				if (Verbose > 0)
384					message("Saved message in %s", dlbuf);
385				Verbose = oldverb;
386				macdefine(&e->e_macro, A_PERM, 'g', p);
387				state = ESM_DONE;
388				break;
389			}
390			macdefine(&e->e_macro, A_PERM, 'g', p);
391			state = ESM_MAIL;
392			break;
393
394		  case ESM_DEADLETTERDROP:
395			/*
396			**  Log the mail in DeadLetterDrop file.
397			*/
398
399			if (e->e_class < 0)
400			{
401				state = ESM_DONE;
402				break;
403			}
404
405			if ((SafeFileEnv != NULL && SafeFileEnv[0] != '\0') ||
406			    DeadLetterDrop == NULL ||
407			    DeadLetterDrop[0] == '\0')
408			{
409				state = ESM_PANIC;
410				break;
411			}
412
413			sff = SFF_CREAT|SFF_REGONLY|SFF_ROOTOK|SFF_OPENASROOT|SFF_MUSTOWN;
414			if (!writable(DeadLetterDrop, NULL, sff) ||
415			    (fp = safefopen(DeadLetterDrop, O_WRONLY|O_APPEND,
416					    FileMode, sff)) == NULL)
417			{
418				state = ESM_PANIC;
419				break;
420			}
421
422			memset(&mcibuf, '\0', sizeof mcibuf);
423			mcibuf.mci_out = fp;
424			mcibuf.mci_mailer = FileMailer;
425			if (bitnset(M_7BITS, FileMailer->m_flags))
426				mcibuf.mci_flags |= MCIF_7BIT;
427
428			/* get the sender for the UnixFromLine */
429			p = macvalue('g', e);
430			macdefine(&e->e_macro, A_PERM, 'g', e->e_sender);
431
432			putfromline(&mcibuf, e);
433			(*e->e_puthdr)(&mcibuf, e->e_header, e, M87F_OUTER);
434			(*e->e_putbody)(&mcibuf, e, NULL);
435			putline("\n", &mcibuf); /* XXX EOL from FileMailer? */
436			(void) sm_io_flush(fp, SM_TIME_DEFAULT);
437			if (sm_io_error(fp) ||
438			    sm_io_close(fp, SM_TIME_DEFAULT) < 0)
439				state = ESM_PANIC;
440			else
441			{
442				int oldverb = Verbose;
443
444				if (OpMode != MD_DAEMON && OpMode != MD_SMTP)
445					Verbose = 1;
446				if (Verbose > 0)
447					message("Saved message in %s",
448						DeadLetterDrop);
449				Verbose = oldverb;
450				if (LogLevel > 3)
451					sm_syslog(LOG_NOTICE, e->e_id,
452						  "Saved message in %s",
453						  DeadLetterDrop);
454				state = ESM_DONE;
455			}
456			macdefine(&e->e_macro, A_PERM, 'g', p);
457			break;
458
459		  default:
460			syserr("554 5.3.5 savemail: unknown state %d", state);
461			/* FALLTHROUGH */
462
463		  case ESM_PANIC:
464			/* leave the locked queue & transcript files around */
465			loseqfile(e, "savemail panic");
466			panic = true;
467			errno = 0;
468			syserr("554 savemail: cannot save rejected email anywhere");
469			state = ESM_DONE;
470			break;
471		}
472	}
473	return panic;
474}
475/*
476**  RETURNTOSENDER -- return a message to the sender with an error.
477**
478**	Parameters:
479**		msg -- the explanatory message.
480**		returnq -- the queue of people to send the message to.
481**		flags -- flags tweaking the operation:
482**			RTSF_SENDBODY -- include body of message (otherwise
483**				just send the header).
484**			RTSF_PMBOUNCE -- this is a postmaster bounce.
485**		e -- the current envelope.
486**
487**	Returns:
488**		zero -- if everything went ok.
489**		else -- some error.
490**
491**	Side Effects:
492**		Returns the current message to the sender via mail.
493*/
494
495#define MAXRETURNS	6	/* max depth of returning messages */
496#define ERRORFUDGE	1024	/* nominal size of error message text */
497
498int
499returntosender(msg, returnq, flags, e)
500	char *msg;
501	ADDRESS *returnq;
502	int flags;
503	register ENVELOPE *e;
504{
505	register ENVELOPE *ee;
506	ENVELOPE *oldcur = CurEnv;
507	ENVELOPE errenvelope;
508	static int returndepth = 0;
509	register ADDRESS *q;
510	char *p;
511	char buf[MAXNAME + 1];
512
513	if (returnq == NULL)
514		return -1;
515
516	if (msg == NULL)
517		msg = "Unable to deliver mail";
518
519	if (tTd(6, 1))
520	{
521		sm_dprintf("\n*** Return To Sender: msg=\"%s\", depth=%d, e=%p, returnq=",
522			msg, returndepth, e);
523		printaddr(returnq, true);
524		if (tTd(6, 20))
525		{
526			sm_dprintf("Sendq=");
527			printaddr(e->e_sendqueue, true);
528		}
529	}
530
531	if (++returndepth >= MAXRETURNS)
532	{
533		if (returndepth != MAXRETURNS)
534			syserr("554 5.3.0 returntosender: infinite recursion on %s",
535			       returnq->q_paddr);
536		/* don't "unrecurse" and fake a clean exit */
537		/* returndepth--; */
538		return 0;
539	}
540
541	macdefine(&e->e_macro, A_PERM, 'g', e->e_sender);
542	macdefine(&e->e_macro, A_PERM, 'u', NULL);
543
544	/* initialize error envelope */
545	ee = newenvelope(&errenvelope, e, sm_rpool_new_x(NULL));
546	macdefine(&ee->e_macro, A_PERM, 'a', "\201b");
547	macdefine(&ee->e_macro, A_PERM, 'r', "");
548	macdefine(&ee->e_macro, A_PERM, 's', "localhost");
549	macdefine(&ee->e_macro, A_PERM, '_', "localhost");
550	clrsessenvelope(ee);
551
552	ee->e_puthdr = putheader;
553	ee->e_putbody = errbody;
554	ee->e_flags |= EF_RESPONSE|EF_METOO;
555	if (!bitset(EF_OLDSTYLE, e->e_flags))
556		ee->e_flags &= ~EF_OLDSTYLE;
557	if (bitset(EF_DONT_MIME, e->e_flags))
558	{
559		ee->e_flags |= EF_DONT_MIME;
560
561		/*
562		**  If we can't convert to MIME and we don't pass
563		**  8-bit, we can't send the body.
564		*/
565
566		if (bitset(EF_HAS8BIT, e->e_flags) &&
567		    !bitset(MM_PASS8BIT, MimeMode))
568			flags &= ~RTSF_SEND_BODY;
569	}
570
571	ee->e_sendqueue = returnq;
572	ee->e_msgsize = 0;
573	if (bitset(RTSF_SEND_BODY, flags) &&
574	    !bitset(PRIV_NOBODYRETN, PrivacyFlags))
575		ee->e_msgsize = ERRORFUDGE + e->e_msgsize;
576	else
577		ee->e_flags |= EF_NO_BODY_RETN;
578
579	if (!setnewqueue(ee))
580	{
581		syserr("554 5.3.0 returntosender: cannot select queue for %s",
582			       returnq->q_paddr);
583		ExitStat = EX_UNAVAILABLE;
584		returndepth--;
585		return -1;
586	}
587	initsys(ee);
588
589#if NAMED_BIND
590	_res.retry = TimeOuts.res_retry[RES_TO_FIRST];
591	_res.retrans = TimeOuts.res_retrans[RES_TO_FIRST];
592#endif /* NAMED_BIND */
593	for (q = returnq; q != NULL; q = q->q_next)
594	{
595		if (QS_IS_BADADDR(q->q_state))
596			continue;
597
598		q->q_flags &= ~(QHASNOTIFY|Q_PINGFLAGS);
599		q->q_flags |= QPINGONFAILURE;
600
601		if (!QS_IS_DEAD(q->q_state))
602			ee->e_nrcpts++;
603
604		if (q->q_alias == NULL)
605			addheader("To", q->q_paddr, 0, ee);
606	}
607
608	if (LogLevel > 5)
609	{
610		if (bitset(EF_RESPONSE, e->e_flags))
611			p = "return to sender";
612		else if (bitset(EF_WARNING, e->e_flags))
613			p = "sender notify";
614		else if (bitset(RTSF_PM_BOUNCE, flags))
615			p = "postmaster notify";
616		else
617			p = "DSN";
618		sm_syslog(LOG_INFO, e->e_id, "%s: %s: %s",
619			  ee->e_id, p, shortenstring(msg, MAXSHORTSTR));
620	}
621
622	if (SendMIMEErrors)
623	{
624		addheader("MIME-Version", "1.0", 0, ee);
625		(void) sm_snprintf(buf, sizeof buf, "%s.%ld/%.100s",
626				ee->e_id, (long)curtime(), MyHostName);
627		ee->e_msgboundary = sm_rpool_strdup_x(ee->e_rpool, buf);
628		(void) sm_snprintf(buf, sizeof buf,
629#if DSN
630				"multipart/report; report-type=delivery-status;\n\tboundary=\"%s\"",
631#else /* DSN */
632				"multipart/mixed; boundary=\"%s\"",
633#endif /* DSN */
634				ee->e_msgboundary);
635		addheader("Content-Type", buf, 0, ee);
636
637		p = hvalue("Content-Transfer-Encoding", e->e_header);
638		if (p != NULL && sm_strcasecmp(p, "binary") != 0)
639			p = NULL;
640		if (p == NULL && bitset(EF_HAS8BIT, e->e_flags))
641			p = "8bit";
642		if (p != NULL)
643			addheader("Content-Transfer-Encoding", p, 0, ee);
644	}
645	if (strncmp(msg, "Warning:", 8) == 0)
646	{
647		addheader("Subject", msg, 0, ee);
648		p = "warning-timeout";
649	}
650	else if (strncmp(msg, "Postmaster warning:", 19) == 0)
651	{
652		addheader("Subject", msg, 0, ee);
653		p = "postmaster-warning";
654	}
655	else if (strcmp(msg, "Return receipt") == 0)
656	{
657		addheader("Subject", msg, 0, ee);
658		p = "return-receipt";
659	}
660	else if (bitset(RTSF_PM_BOUNCE, flags))
661	{
662		(void) sm_snprintf(buf, sizeof buf,
663			 "Postmaster notify: see transcript for details");
664		addheader("Subject", buf, 0, ee);
665		p = "postmaster-notification";
666	}
667	else
668	{
669		(void) sm_snprintf(buf, sizeof buf,
670			 "Returned mail: see transcript for details");
671		addheader("Subject", buf, 0, ee);
672		p = "failure";
673	}
674	(void) sm_snprintf(buf, sizeof buf, "auto-generated (%s)", p);
675	addheader("Auto-Submitted", buf, 0, ee);
676
677	/* fake up an address header for the from person */
678	expand("\201n", buf, sizeof buf, e);
679	if (parseaddr(buf, &ee->e_from,
680		      RF_COPYALL|RF_SENDERADDR, '\0', NULL, e, false) == NULL)
681	{
682		syserr("553 5.3.5 Can't parse myself!");
683		ExitStat = EX_SOFTWARE;
684		returndepth--;
685		return -1;
686	}
687	ee->e_from.q_flags &= ~(QHASNOTIFY|Q_PINGFLAGS);
688	ee->e_from.q_flags |= QPINGONFAILURE;
689	ee->e_sender = ee->e_from.q_paddr;
690
691	/* push state into submessage */
692	CurEnv = ee;
693	macdefine(&ee->e_macro, A_PERM, 'f', "\201n");
694	macdefine(&ee->e_macro, A_PERM, 'x', "Mail Delivery Subsystem");
695	eatheader(ee, true, true);
696
697	/* mark statistics */
698	markstats(ee, NULLADDR, STATS_NORMAL);
699
700	/* actually deliver the error message */
701	sendall(ee, SM_DELIVER);
702
703	/* restore state */
704	dropenvelope(ee, true, false);
705	sm_rpool_free(ee->e_rpool);
706	CurEnv = oldcur;
707	returndepth--;
708
709	/* check for delivery errors */
710	if (ee->e_parent == NULL ||
711	    !bitset(EF_RESPONSE, ee->e_parent->e_flags))
712		return 0;
713	for (q = ee->e_sendqueue; q != NULL; q = q->q_next)
714	{
715		if (QS_IS_ATTEMPTED(q->q_state))
716			return 0;
717	}
718	return -1;
719}
720/*
721**  ERRBODY -- output the body of an error message.
722**
723**	Typically this is a copy of the transcript plus a copy of the
724**	original offending message.
725**
726**	Parameters:
727**		mci -- the mailer connection information.
728**		e -- the envelope we are working in.
729**		separator -- any possible MIME separator (unused).
730**
731**	Returns:
732**		none
733**
734**	Side Effects:
735**		Outputs the body of an error message.
736*/
737
738/* ARGSUSED2 */
739static void
740errbody(mci, e, separator)
741	register MCI *mci;
742	register ENVELOPE *e;
743	char *separator;
744{
745	bool printheader;
746	bool sendbody;
747	bool pm_notify;
748	int save_errno;
749	register SM_FILE_T *xfile;
750	char *p;
751	register ADDRESS *q = NULL;
752	char actual[MAXLINE];
753	char buf[MAXLINE];
754
755	if (bitset(MCIF_INHEADER, mci->mci_flags))
756	{
757		putline("", mci);
758		mci->mci_flags &= ~MCIF_INHEADER;
759	}
760	if (e->e_parent == NULL)
761	{
762		syserr("errbody: null parent");
763		putline("   ----- Original message lost -----\n", mci);
764		return;
765	}
766
767	/*
768	**  Output MIME header.
769	*/
770
771	if (e->e_msgboundary != NULL)
772	{
773		putline("This is a MIME-encapsulated message", mci);
774		putline("", mci);
775		(void) sm_strlcpyn(buf, sizeof buf, 2, "--", e->e_msgboundary);
776		putline(buf, mci);
777		putline("", mci);
778	}
779
780	/*
781	**  Output introductory information.
782	*/
783
784	pm_notify = false;
785	p = hvalue("subject", e->e_header);
786	if (p != NULL && strncmp(p, "Postmaster ", 11) == 0)
787		pm_notify = true;
788	else
789	{
790		for (q = e->e_parent->e_sendqueue; q != NULL; q = q->q_next)
791		{
792			if (QS_IS_BADADDR(q->q_state))
793				break;
794		}
795	}
796	if (!pm_notify && q == NULL &&
797	    !bitset(EF_FATALERRS|EF_SENDRECEIPT, e->e_parent->e_flags))
798	{
799		putline("    **********************************************",
800			mci);
801		putline("    **      THIS IS A WARNING MESSAGE ONLY      **",
802			mci);
803		putline("    **  YOU DO NOT NEED TO RESEND YOUR MESSAGE  **",
804			mci);
805		putline("    **********************************************",
806			mci);
807		putline("", mci);
808	}
809	(void) sm_snprintf(buf, sizeof buf,
810		"The original message was received at %s",
811		arpadate(ctime(&e->e_parent->e_ctime)));
812	putline(buf, mci);
813	expand("from \201_", buf, sizeof buf, e->e_parent);
814	putline(buf, mci);
815
816	/* include id in postmaster copies */
817	if (pm_notify && e->e_parent->e_id != NULL)
818	{
819		(void) sm_strlcpyn(buf, sizeof buf, 2, "with id ",
820			e->e_parent->e_id);
821		putline(buf, mci);
822	}
823	putline("", mci);
824
825	/*
826	**  Output error message header (if specified and available).
827	*/
828
829	if (ErrMsgFile != NULL &&
830	    !bitset(EF_SENDRECEIPT, e->e_parent->e_flags))
831	{
832		if (*ErrMsgFile == '/')
833		{
834			long sff = SFF_ROOTOK|SFF_REGONLY;
835
836			if (DontLockReadFiles)
837				sff |= SFF_NOLOCK;
838			if (!bitnset(DBS_ERRORHEADERINUNSAFEDIRPATH,
839				     DontBlameSendmail))
840				sff |= SFF_SAFEDIRPATH;
841			xfile = safefopen(ErrMsgFile, O_RDONLY, 0444, sff);
842			if (xfile != NULL)
843			{
844				while (sm_io_fgets(xfile, SM_TIME_DEFAULT, buf,
845						   sizeof buf) != NULL)
846				{
847					translate_dollars(buf);
848					expand(buf, buf, sizeof buf, e);
849					putline(buf, mci);
850				}
851				(void) sm_io_close(xfile, SM_TIME_DEFAULT);
852				putline("\n", mci);
853			}
854		}
855		else
856		{
857			expand(ErrMsgFile, buf, sizeof buf, e);
858			putline(buf, mci);
859			putline("", mci);
860		}
861	}
862
863	/*
864	**  Output message introduction
865	*/
866
867	/* permanent fatal errors */
868	printheader = true;
869	for (q = e->e_parent->e_sendqueue; q != NULL; q = q->q_next)
870	{
871		if (!QS_IS_BADADDR(q->q_state) ||
872		    !bitset(QPINGONFAILURE, q->q_flags))
873			continue;
874
875		if (printheader)
876		{
877			putline("   ----- The following addresses had permanent fatal errors -----",
878				mci);
879			printheader = false;
880		}
881
882		(void) sm_strlcpy(buf, shortenstring(q->q_paddr, MAXSHORTSTR),
883				  sizeof buf);
884		putline(buf, mci);
885		if (q->q_rstatus != NULL)
886		{
887			(void) sm_snprintf(buf, sizeof buf,
888				"    (reason: %s)",
889				shortenstring(exitstat(q->q_rstatus),
890					      MAXSHORTSTR));
891			putline(buf, mci);
892		}
893		if (q->q_alias != NULL)
894		{
895			(void) sm_snprintf(buf, sizeof buf,
896				"    (expanded from: %s)",
897				shortenstring(q->q_alias->q_paddr,
898					      MAXSHORTSTR));
899			putline(buf, mci);
900		}
901	}
902	if (!printheader)
903		putline("", mci);
904
905	/* transient non-fatal errors */
906	printheader = true;
907	for (q = e->e_parent->e_sendqueue; q != NULL; q = q->q_next)
908	{
909		if (QS_IS_BADADDR(q->q_state) ||
910		    !bitset(QPRIMARY, q->q_flags) ||
911		    !bitset(QBYNDELAY, q->q_flags) ||
912		    !bitset(QDELAYED, q->q_flags))
913			continue;
914
915		if (printheader)
916		{
917			putline("   ----- The following addresses had transient non-fatal errors -----",
918				mci);
919			printheader = false;
920		}
921
922		(void) sm_strlcpy(buf, shortenstring(q->q_paddr, MAXSHORTSTR),
923				  sizeof buf);
924		putline(buf, mci);
925		if (q->q_alias != NULL)
926		{
927			(void) sm_snprintf(buf, sizeof buf,
928				"    (expanded from: %s)",
929				shortenstring(q->q_alias->q_paddr,
930					      MAXSHORTSTR));
931			putline(buf, mci);
932		}
933	}
934	if (!printheader)
935		putline("", mci);
936
937	/* successful delivery notifications */
938	printheader = true;
939	for (q = e->e_parent->e_sendqueue; q != NULL; q = q->q_next)
940	{
941		if (QS_IS_BADADDR(q->q_state) ||
942		    !bitset(QPRIMARY, q->q_flags) ||
943		    bitset(QBYNDELAY, q->q_flags) ||
944		    bitset(QDELAYED, q->q_flags))
945			continue;
946		else if (bitset(QBYNRELAY, q->q_flags))
947			p = "Deliver-By notify: relayed";
948		else if (bitset(QBYTRACE, q->q_flags))
949			p = "Deliver-By trace: relayed";
950		else if (!bitset(QPINGONSUCCESS, q->q_flags))
951			continue;
952		else if (bitset(QRELAYED, q->q_flags))
953			p = "relayed to non-DSN-aware mailer";
954		else if (bitset(QDELIVERED, q->q_flags))
955		{
956			if (bitset(QEXPANDED, q->q_flags))
957				p = "successfully delivered to mailing list";
958			else
959				p = "successfully delivered to mailbox";
960		}
961		else if (bitset(QEXPANDED, q->q_flags))
962			p = "expanded by alias";
963		else
964			continue;
965
966		if (printheader)
967		{
968			putline("   ----- The following addresses had successful delivery notifications -----",
969				mci);
970			printheader = false;
971		}
972
973		(void) sm_snprintf(buf, sizeof buf, "%s  (%s)",
974			 shortenstring(q->q_paddr, MAXSHORTSTR), p);
975		putline(buf, mci);
976		if (q->q_alias != NULL)
977		{
978			(void) sm_snprintf(buf, sizeof buf,
979				"    (expanded from: %s)",
980				shortenstring(q->q_alias->q_paddr,
981					      MAXSHORTSTR));
982			putline(buf, mci);
983		}
984	}
985	if (!printheader)
986		putline("", mci);
987
988	/*
989	**  Output transcript of errors
990	*/
991
992	(void) sm_io_flush(smioout, SM_TIME_DEFAULT);
993	if (e->e_parent->e_xfp == NULL)
994	{
995		putline("   ----- Transcript of session is unavailable -----\n",
996			mci);
997	}
998	else
999	{
1000		printheader = true;
1001		(void) bfrewind(e->e_parent->e_xfp);
1002		if (e->e_xfp != NULL)
1003			(void) sm_io_flush(e->e_xfp, SM_TIME_DEFAULT);
1004		while (sm_io_fgets(e->e_parent->e_xfp, SM_TIME_DEFAULT, buf,
1005				   sizeof buf) != NULL)
1006		{
1007			if (printheader)
1008				putline("   ----- Transcript of session follows -----\n",
1009					mci);
1010			printheader = false;
1011			putline(buf, mci);
1012		}
1013	}
1014	errno = 0;
1015
1016#if DSN
1017	/*
1018	**  Output machine-readable version.
1019	*/
1020
1021	if (e->e_msgboundary != NULL)
1022	{
1023		putline("", mci);
1024		(void) sm_strlcpyn(buf, sizeof buf, 2, "--", e->e_msgboundary);
1025		putline(buf, mci);
1026		putline("Content-Type: message/delivery-status", mci);
1027		putline("", mci);
1028
1029		/*
1030		**  Output per-message information.
1031		*/
1032
1033		/* original envelope id from MAIL FROM: line */
1034		if (e->e_parent->e_envid != NULL)
1035		{
1036			(void) sm_snprintf(buf, sizeof buf,
1037					"Original-Envelope-Id: %.800s",
1038					xuntextify(e->e_parent->e_envid));
1039			putline(buf, mci);
1040		}
1041
1042		/* Reporting-MTA: is us (required) */
1043		(void) sm_snprintf(buf, sizeof buf,
1044				   "Reporting-MTA: dns; %.800s", MyHostName);
1045		putline(buf, mci);
1046
1047		/* DSN-Gateway: not relevant since we are not translating */
1048
1049		/* Received-From-MTA: shows where we got this message from */
1050		if (RealHostName != NULL)
1051		{
1052			/* XXX use $s for type? */
1053			if (e->e_parent->e_from.q_mailer == NULL ||
1054			    (p = e->e_parent->e_from.q_mailer->m_mtatype) == NULL)
1055				p = "dns";
1056			(void) sm_snprintf(buf, sizeof buf,
1057					"Received-From-MTA: %s; %.800s",
1058					p, RealHostName);
1059			putline(buf, mci);
1060		}
1061
1062		/* Arrival-Date: -- when it arrived here */
1063		(void) sm_strlcpyn(buf, sizeof buf, 2, "Arrival-Date: ",
1064				arpadate(ctime(&e->e_parent->e_ctime)));
1065		putline(buf, mci);
1066
1067		/* Deliver-By-Date: -- when it should have been delivered */
1068		if (IS_DLVR_BY(e->e_parent))
1069		{
1070			time_t dbyd;
1071
1072			dbyd = e->e_parent->e_ctime + e->e_parent->e_deliver_by;
1073			(void) sm_strlcpyn(buf, sizeof buf, 2,
1074					"Deliver-By-Date: ",
1075					arpadate(ctime(&dbyd)));
1076			putline(buf, mci);
1077		}
1078
1079		/*
1080		**  Output per-address information.
1081		*/
1082
1083		for (q = e->e_parent->e_sendqueue; q != NULL; q = q->q_next)
1084		{
1085			char *action;
1086
1087			if (QS_IS_BADADDR(q->q_state))
1088			{
1089				/* RFC 1891, 6.2.6 (b) */
1090				if (bitset(QHASNOTIFY, q->q_flags) &&
1091				    !bitset(QPINGONFAILURE, q->q_flags))
1092					continue;
1093				action = "failed";
1094			}
1095			else if (!bitset(QPRIMARY, q->q_flags))
1096				continue;
1097			else if (bitset(QDELIVERED, q->q_flags))
1098			{
1099				if (bitset(QEXPANDED, q->q_flags))
1100					action = "delivered (to mailing list)";
1101				else
1102					action = "delivered (to mailbox)";
1103			}
1104			else if (bitset(QRELAYED, q->q_flags))
1105				action = "relayed (to non-DSN-aware mailer)";
1106			else if (bitset(QEXPANDED, q->q_flags))
1107				action = "expanded (to multi-recipient alias)";
1108			else if (bitset(QDELAYED, q->q_flags))
1109				action = "delayed";
1110			else if (bitset(QBYTRACE, q->q_flags))
1111				action = "relayed (Deliver-By trace mode)";
1112			else if (bitset(QBYNDELAY, q->q_flags))
1113				action = "delayed (Deliver-By notify mode)";
1114			else if (bitset(QBYNRELAY, q->q_flags))
1115				action = "relayed (Deliver-By notify mode)";
1116			else
1117				continue;
1118
1119			putline("", mci);
1120
1121			/* Original-Recipient: -- passed from on high */
1122			if (q->q_orcpt != NULL)
1123			{
1124				(void) sm_snprintf(buf, sizeof buf,
1125						"Original-Recipient: %.800s",
1126						q->q_orcpt);
1127				putline(buf, mci);
1128			}
1129
1130			/* Figure out actual recipient */
1131			actual[0] = '\0';
1132			if (q->q_user[0] != '\0')
1133			{
1134				if (q->q_mailer != NULL &&
1135				    q->q_mailer->m_addrtype != NULL)
1136					p = q->q_mailer->m_addrtype;
1137				else
1138					p = "rfc822";
1139
1140				if (sm_strcasecmp(p, "rfc822") == 0 &&
1141				    strchr(q->q_user, '@') == NULL)
1142				{
1143					(void) sm_snprintf(actual,
1144							   sizeof actual,
1145							   "%s; %.700s@%.100s",
1146							   p, q->q_user,
1147							   MyHostName);
1148				}
1149				else
1150				{
1151					(void) sm_snprintf(actual,
1152							   sizeof actual,
1153							   "%s; %.800s",
1154							   p, q->q_user);
1155				}
1156			}
1157
1158			/* Final-Recipient: -- the name from the RCPT command */
1159			if (q->q_finalrcpt == NULL)
1160			{
1161				/* should never happen */
1162				sm_syslog(LOG_ERR, e->e_id,
1163					  "returntosender: q_finalrcpt is NULL");
1164
1165				/* try to fall back to the actual recipient */
1166				if (actual[0] != '\0')
1167					q->q_finalrcpt = sm_rpool_strdup_x(e->e_rpool,
1168									   actual);
1169			}
1170
1171			if (q->q_finalrcpt != NULL)
1172			{
1173				(void) sm_snprintf(buf, sizeof buf,
1174						   "Final-Recipient: %s",
1175						   q->q_finalrcpt);
1176				putline(buf, mci);
1177			}
1178
1179			/* X-Actual-Recipient: -- the real problem address */
1180			if (actual[0] != '\0' &&
1181			    q->q_finalrcpt != NULL &&
1182			    strcmp(actual, q->q_finalrcpt) != 0)
1183			{
1184				(void) sm_snprintf(buf, sizeof buf,
1185						   "X-Actual-Recipient: %s",
1186						   actual);
1187				putline(buf, mci);
1188			}
1189
1190			/* Action: -- what happened? */
1191			(void) sm_strlcpyn(buf, sizeof buf, 2, "Action: ",
1192				action);
1193			putline(buf, mci);
1194
1195			/* Status: -- what _really_ happened? */
1196			if (q->q_status != NULL)
1197				p = q->q_status;
1198			else if (QS_IS_BADADDR(q->q_state))
1199				p = "5.0.0";
1200			else if (QS_IS_QUEUEUP(q->q_state))
1201				p = "4.0.0";
1202			else
1203				p = "2.0.0";
1204			(void) sm_strlcpyn(buf, sizeof buf, 2, "Status: ", p);
1205			putline(buf, mci);
1206
1207			/* Remote-MTA: -- who was I talking to? */
1208			if (q->q_statmta != NULL)
1209			{
1210				if (q->q_mailer == NULL ||
1211				    (p = q->q_mailer->m_mtatype) == NULL)
1212					p = "dns";
1213				(void) sm_snprintf(buf, sizeof buf,
1214						"Remote-MTA: %s; %.800s",
1215						p, q->q_statmta);
1216				p = &buf[strlen(buf) - 1];
1217				if (*p == '.')
1218					*p = '\0';
1219				putline(buf, mci);
1220			}
1221
1222			/* Diagnostic-Code: -- actual result from other end */
1223			if (q->q_rstatus != NULL)
1224			{
1225				p = q->q_mailer->m_diagtype;
1226				if (p == NULL)
1227					p = "smtp";
1228				(void) sm_snprintf(buf, sizeof buf,
1229						"Diagnostic-Code: %s; %.800s",
1230						p, q->q_rstatus);
1231				putline(buf, mci);
1232			}
1233
1234			/* Last-Attempt-Date: -- fine granularity */
1235			if (q->q_statdate == (time_t) 0L)
1236				q->q_statdate = curtime();
1237			(void) sm_strlcpyn(buf, sizeof buf, 2,
1238					"Last-Attempt-Date: ",
1239					arpadate(ctime(&q->q_statdate)));
1240			putline(buf, mci);
1241
1242			/* Will-Retry-Until: -- for delayed messages only */
1243			if (QS_IS_QUEUEUP(q->q_state))
1244			{
1245				time_t xdate;
1246
1247				xdate = e->e_parent->e_ctime +
1248					TimeOuts.to_q_return[e->e_parent->e_timeoutclass];
1249				(void) sm_strlcpyn(buf, sizeof buf, 2,
1250					 "Will-Retry-Until: ",
1251					 arpadate(ctime(&xdate)));
1252				putline(buf, mci);
1253			}
1254		}
1255	}
1256#endif /* DSN */
1257
1258	/*
1259	**  Output text of original message
1260	*/
1261
1262	putline("", mci);
1263	if (bitset(EF_HAS_DF, e->e_parent->e_flags))
1264	{
1265		sendbody = !bitset(EF_NO_BODY_RETN, e->e_parent->e_flags) &&
1266			   !bitset(EF_NO_BODY_RETN, e->e_flags);
1267
1268		if (e->e_msgboundary == NULL)
1269		{
1270			if (sendbody)
1271				putline("   ----- Original message follows -----\n", mci);
1272			else
1273				putline("   ----- Message header follows -----\n", mci);
1274		}
1275		else
1276		{
1277			(void) sm_strlcpyn(buf, sizeof buf, 2, "--",
1278					e->e_msgboundary);
1279
1280			putline(buf, mci);
1281			(void) sm_strlcpyn(buf, sizeof buf, 2, "Content-Type: ",
1282					sendbody ? "message/rfc822"
1283						 : "text/rfc822-headers");
1284			putline(buf, mci);
1285
1286			p = hvalue("Content-Transfer-Encoding",
1287				   e->e_parent->e_header);
1288			if (p != NULL && sm_strcasecmp(p, "binary") != 0)
1289				p = NULL;
1290			if (p == NULL &&
1291			    bitset(EF_HAS8BIT, e->e_parent->e_flags))
1292				p = "8bit";
1293			if (p != NULL)
1294			{
1295				(void) sm_snprintf(buf, sizeof buf,
1296						"Content-Transfer-Encoding: %s",
1297						p);
1298				putline(buf, mci);
1299			}
1300		}
1301		putline("", mci);
1302		save_errno = errno;
1303		putheader(mci, e->e_parent->e_header, e->e_parent, M87F_OUTER);
1304		errno = save_errno;
1305		if (sendbody)
1306			putbody(mci, e->e_parent, e->e_msgboundary);
1307		else if (e->e_msgboundary == NULL)
1308		{
1309			putline("", mci);
1310			putline("   ----- Message body suppressed -----", mci);
1311		}
1312	}
1313	else if (e->e_msgboundary == NULL)
1314	{
1315		putline("  ----- No message was collected -----\n", mci);
1316	}
1317
1318	if (e->e_msgboundary != NULL)
1319	{
1320		putline("", mci);
1321		(void) sm_strlcpyn(buf, sizeof buf, 3, "--", e->e_msgboundary,
1322				   "--");
1323		putline(buf, mci);
1324	}
1325	putline("", mci);
1326	(void) sm_io_flush(mci->mci_out, SM_TIME_DEFAULT);
1327
1328	/*
1329	**  Cleanup and exit
1330	*/
1331
1332	if (errno != 0)
1333		syserr("errbody: I/O error");
1334}
1335/*
1336**  SMTPTODSN -- convert SMTP to DSN status code
1337**
1338**	Parameters:
1339**		smtpstat -- the smtp status code (e.g., 550).
1340**
1341**	Returns:
1342**		The DSN version of the status code.
1343**
1344**	Storage Management:
1345**		smtptodsn() returns a pointer to a character string literal,
1346**		which will remain valid forever, and thus does not need to
1347**		be copied.  Current code relies on this property.
1348*/
1349
1350char *
1351smtptodsn(smtpstat)
1352	int smtpstat;
1353{
1354	if (smtpstat < 0)
1355		return "4.4.2";
1356
1357	switch (smtpstat)
1358	{
1359	  case 450:	/* Req mail action not taken: mailbox unavailable */
1360		return "4.2.0";
1361
1362	  case 451:	/* Req action aborted: local error in processing */
1363		return "4.3.0";
1364
1365	  case 452:	/* Req action not taken: insufficient sys storage */
1366		return "4.3.1";
1367
1368	  case 500:	/* Syntax error, command unrecognized */
1369		return "5.5.2";
1370
1371	  case 501:	/* Syntax error in parameters or arguments */
1372		return "5.5.4";
1373
1374	  case 502:	/* Command not implemented */
1375		return "5.5.1";
1376
1377	  case 503:	/* Bad sequence of commands */
1378		return "5.5.1";
1379
1380	  case 504:	/* Command parameter not implemented */
1381		return "5.5.4";
1382
1383	  case 550:	/* Req mail action not taken: mailbox unavailable */
1384		return "5.2.0";
1385
1386	  case 551:	/* User not local; please try <...> */
1387		return "5.1.6";
1388
1389	  case 552:	/* Req mail action aborted: exceeded storage alloc */
1390		return "5.2.2";
1391
1392	  case 553:	/* Req action not taken: mailbox name not allowed */
1393		return "5.1.0";
1394
1395	  case 554:	/* Transaction failed */
1396		return "5.0.0";
1397	}
1398
1399	if ((smtpstat / 100) == 2)
1400		return "2.0.0";
1401	if ((smtpstat / 100) == 4)
1402		return "4.0.0";
1403	return "5.0.0";
1404}
1405/*
1406**  XTEXTIFY -- take regular text and turn it into DSN-style xtext
1407**
1408**	Parameters:
1409**		t -- the text to convert.
1410**		taboo -- additional characters that must be encoded.
1411**
1412**	Returns:
1413**		The xtext-ified version of the same string.
1414*/
1415
1416char *
1417xtextify(t, taboo)
1418	register char *t;
1419	char *taboo;
1420{
1421	register char *p;
1422	int l;
1423	int nbogus;
1424	static char *bp = NULL;
1425	static int bplen = 0;
1426
1427	if (taboo == NULL)
1428		taboo = "";
1429
1430	/* figure out how long this xtext will have to be */
1431	nbogus = l = 0;
1432	for (p = t; *p != '\0'; p++)
1433	{
1434		register int c = (*p & 0xff);
1435
1436		/* ASCII dependence here -- this is the way the spec words it */
1437		if (c < '!' || c > '~' || c == '+' || c == '\\' || c == '(' ||
1438		    strchr(taboo, c) != NULL)
1439			nbogus++;
1440		l++;
1441	}
1442	if (nbogus < 0)
1443	{
1444		/* since nbogus is ssize_t and wrapped, 2 * size_t would wrap */
1445		syserr("!xtextify string too long");
1446	}
1447	if (nbogus == 0)
1448		return t;
1449	l += nbogus * 2 + 1;
1450
1451	/* now allocate space if necessary for the new string */
1452	if (l > bplen)
1453	{
1454		if (bp != NULL)
1455			sm_free(bp); /* XXX */
1456		bp = sm_pmalloc_x(l);
1457		bplen = l;
1458	}
1459
1460	/* ok, copy the text with byte expansion */
1461	for (p = bp; *t != '\0'; )
1462	{
1463		register int c = (*t++ & 0xff);
1464
1465		/* ASCII dependence here -- this is the way the spec words it */
1466		if (c < '!' || c > '~' || c == '+' || c == '\\' || c == '(' ||
1467		    strchr(taboo, c) != NULL)
1468		{
1469			*p++ = '+';
1470			*p++ = "0123456789ABCDEF"[c >> 4];
1471			*p++ = "0123456789ABCDEF"[c & 0xf];
1472		}
1473		else
1474			*p++ = c;
1475	}
1476	*p = '\0';
1477	return bp;
1478}
1479/*
1480**  XUNTEXTIFY -- take xtext and turn it into plain text
1481**
1482**	Parameters:
1483**		t -- the xtextified text.
1484**
1485**	Returns:
1486**		The decoded text.  No attempt is made to deal with
1487**		null strings in the resulting text.
1488*/
1489
1490char *
1491xuntextify(t)
1492	register char *t;
1493{
1494	register char *p;
1495	int l;
1496	static char *bp = NULL;
1497	static int bplen = 0;
1498
1499	/* heuristic -- if no plus sign, just return the input */
1500	if (strchr(t, '+') == NULL)
1501		return t;
1502
1503	/* xtext is always longer than decoded text */
1504	l = strlen(t);
1505	if (l > bplen)
1506	{
1507		if (bp != NULL)
1508			sm_free(bp); /* XXX */
1509		bp = xalloc(l);
1510		bplen = l;
1511	}
1512
1513	/* ok, copy the text with byte compression */
1514	for (p = bp; *t != '\0'; t++)
1515	{
1516		register int c = *t & 0xff;
1517
1518		if (c != '+')
1519		{
1520			*p++ = c;
1521			continue;
1522		}
1523
1524		c = *++t & 0xff;
1525		if (!isascii(c) || !isxdigit(c))
1526		{
1527			/* error -- first digit is not hex */
1528			usrerr("bogus xtext: +%c", c);
1529			t--;
1530			continue;
1531		}
1532		if (isdigit(c))
1533			c -= '0';
1534		else if (isupper(c))
1535			c -= 'A' - 10;
1536		else
1537			c -= 'a' - 10;
1538		*p = c << 4;
1539
1540		c = *++t & 0xff;
1541		if (!isascii(c) || !isxdigit(c))
1542		{
1543			/* error -- second digit is not hex */
1544			usrerr("bogus xtext: +%x%c", *p >> 4, c);
1545			t--;
1546			continue;
1547		}
1548		if (isdigit(c))
1549			c -= '0';
1550		else if (isupper(c))
1551			c -= 'A' - 10;
1552		else
1553			c -= 'a' - 10;
1554		*p++ |= c;
1555	}
1556	*p = '\0';
1557	return bp;
1558}
1559/*
1560**  XTEXTOK -- check if a string is legal xtext
1561**
1562**	Xtext is used in Delivery Status Notifications.  The spec was
1563**	taken from RFC 1891, ``SMTP Service Extension for Delivery
1564**	Status Notifications''.
1565**
1566**	Parameters:
1567**		s -- the string to check.
1568**
1569**	Returns:
1570**		true -- if 's' is legal xtext.
1571**		false -- if it has any illegal characters in it.
1572*/
1573
1574bool
1575xtextok(s)
1576	char *s;
1577{
1578	int c;
1579
1580	while ((c = *s++) != '\0')
1581	{
1582		if (c == '+')
1583		{
1584			c = *s++;
1585			if (!isascii(c) || !isxdigit(c))
1586				return false;
1587			c = *s++;
1588			if (!isascii(c) || !isxdigit(c))
1589				return false;
1590		}
1591		else if (c < '!' || c > '~' || c == '=')
1592			return false;
1593	}
1594	return true;
1595}
1596/*
1597**  PRUNEROUTE -- prune an RFC-822 source route
1598**
1599**	Trims down a source route to the last internet-registered hop.
1600**	This is encouraged by RFC 1123 section 5.3.3.
1601**
1602**	Parameters:
1603**		addr -- the address
1604**
1605**	Returns:
1606**		true -- address was modified
1607**		false -- address could not be pruned
1608**
1609**	Side Effects:
1610**		modifies addr in-place
1611*/
1612
1613static bool
1614pruneroute(addr)
1615	char *addr;
1616{
1617#if NAMED_BIND
1618	char *start, *at, *comma;
1619	char c;
1620	int braclev;
1621	int rcode;
1622	int i;
1623	char hostbuf[BUFSIZ];
1624	char *mxhosts[MAXMXHOSTS + 1];
1625
1626	/* check to see if this is really a route-addr */
1627	if (*addr != '<' || addr[1] != '@' || addr[strlen(addr) - 1] != '>')
1628		return false;
1629
1630	/*
1631	**  Can't simply find the first ':' is the address might be in the
1632	**  form:  "<@[IPv6:::1]:user@host>" and the first ':' in inside
1633	**  the IPv6 address.
1634	*/
1635
1636	start = addr;
1637	braclev = 0;
1638	while (*start != '\0')
1639	{
1640		if (*start == ':' && braclev <= 0)
1641			break;
1642		else if (*start == '[')
1643			braclev++;
1644		else if (*start == ']' && braclev > 0)
1645			braclev--;
1646		start++;
1647	}
1648	if (braclev > 0 || *start != ':')
1649		return false;
1650
1651	at = strrchr(addr, '@');
1652	if (at == NULL || at < start)
1653		return false;
1654
1655	/* slice off the angle brackets */
1656	i = strlen(at + 1);
1657	if (i >= sizeof hostbuf)
1658		return false;
1659	(void) sm_strlcpy(hostbuf, at + 1, sizeof hostbuf);
1660	hostbuf[i - 1] = '\0';
1661
1662	while (start != NULL)
1663	{
1664		if (getmxrr(hostbuf, mxhosts, NULL, false,
1665			    &rcode, true, NULL) > 0)
1666		{
1667			(void) sm_strlcpy(addr + 1, start + 1,
1668					  strlen(addr) - 1);
1669			return true;
1670		}
1671		c = *start;
1672		*start = '\0';
1673		comma = strrchr(addr, ',');
1674		if (comma != NULL && comma[1] == '@' &&
1675		    strlen(comma + 2) < sizeof hostbuf)
1676			(void) sm_strlcpy(hostbuf, comma + 2, sizeof hostbuf);
1677		else
1678			comma = NULL;
1679		*start = c;
1680		start = comma;
1681	}
1682#endif /* NAMED_BIND */
1683	return false;
1684}
1685