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