xref: /freebsd/contrib/sendmail/src/err.c (revision c86d59657f992c17a947200225b50f07e1776cd1)
1 /*
2  * Copyright (c) 1998-2001 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 #ifndef lint
15 static char id[] = "@(#)$Id: err.c,v 8.120.4.5 2001/08/17 22:09:40 ca Exp $";
16 #endif /* ! lint */
17 
18 #include <sendmail.h>
19 #ifdef LDAPMAP
20 # include <lber.h>
21 # include <ldap.h>			/* for LDAP error codes */
22 #endif /* LDAPMAP */
23 
24 
25 static void	putoutmsg __P((char *, bool, bool));
26 static void	puterrmsg __P((char *));
27 static char	*fmtmsg __P((char *, const char *, const char *, const char *,
28 			     int, const char *, va_list));
29 
30 /*
31 **  SYSERR -- Print error message.
32 **
33 **	Prints an error message via printf to the diagnostic output.
34 **
35 **	If the first character of the syserr message is `!' it will
36 **	log this as an ALERT message and exit immediately.  This can
37 **	leave queue files in an indeterminate state, so it should not
38 **	be used lightly.
39 **
40 **	Parameters:
41 **		fmt -- the format string.  If it does not begin with
42 **			a three-digit SMTP reply code, either 554 or
43 **			451 is assumed depending on whether errno
44 **			is set.
45 **		(others) -- parameters
46 **
47 **	Returns:
48 **		none
49 **		Through TopFrame if QuickAbort is set.
50 **
51 **	Side Effects:
52 **		increments Errors.
53 **		sets ExitStat.
54 */
55 
56 char		MsgBuf[BUFSIZ*2];	/* text of most recent message */
57 static char	HeldMessageBuf[sizeof MsgBuf];	/* for held messages */
58 
59 #if NAMED_BIND && !defined(NO_DATA)
60 # define NO_DATA	NO_ADDRESS
61 #endif /* NAMED_BIND && !defined(NO_DATA) */
62 
63 void
64 /*VARARGS1*/
65 #ifdef __STDC__
66 syserr(const char *fmt, ...)
67 #else /* __STDC__ */
68 syserr(fmt, va_alist)
69 	const char *fmt;
70 	va_dcl
71 #endif /* __STDC__ */
72 {
73 	register char *p;
74 	int save_errno = errno;
75 	bool panic;
76 	char *user;
77 	char *enhsc;
78 	char *errtxt;
79 	struct passwd *pw;
80 	char ubuf[80];
81 	VA_LOCAL_DECL
82 
83 	panic = *fmt == '!';
84 	if (panic)
85 	{
86 		fmt++;
87 		HoldErrs = FALSE;
88 	}
89 
90 	/* format and output the error message */
91 	if (save_errno == 0)
92 	{
93 		p = "554";
94 		enhsc = "5.0.0";
95 	}
96 	else
97 	{
98 		p = "451";
99 		enhsc = "4.0.0";
100 	}
101 	VA_START(fmt);
102 	errtxt = fmtmsg(MsgBuf, (char *) NULL, p, enhsc, save_errno, fmt, ap);
103 	VA_END;
104 	puterrmsg(MsgBuf);
105 
106 	/* save this message for mailq printing */
107 	if (!panic && CurEnv != NULL)
108 	{
109 		if (CurEnv->e_message != NULL)
110 			sm_free(CurEnv->e_message);
111 		CurEnv->e_message = newstr(errtxt);
112 	}
113 
114 	/* determine exit status if not already set */
115 	if (ExitStat == EX_OK)
116 	{
117 		if (save_errno == 0)
118 			ExitStat = EX_SOFTWARE;
119 		else
120 			ExitStat = EX_OSERR;
121 		if (tTd(54, 1))
122 			dprintf("syserr: ExitStat = %d\n", ExitStat);
123 	}
124 
125 	pw = sm_getpwuid(RealUid);
126 	if (pw != NULL)
127 		user = pw->pw_name;
128 	else
129 	{
130 		user = ubuf;
131 		snprintf(ubuf, sizeof ubuf, "UID%d", (int) RealUid);
132 	}
133 
134 	if (LogLevel > 0)
135 		sm_syslog(panic ? LOG_ALERT : LOG_CRIT,
136 			  CurEnv == NULL ? NOQID : CurEnv->e_id,
137 			  "SYSERR(%s): %.900s",
138 			  user, errtxt);
139 	switch (save_errno)
140 	{
141 	  case EBADF:
142 	  case ENFILE:
143 	  case EMFILE:
144 	  case ENOTTY:
145 #ifdef EFBIG
146 	  case EFBIG:
147 #endif /* EFBIG */
148 #ifdef ESPIPE
149 	  case ESPIPE:
150 #endif /* ESPIPE */
151 #ifdef EPIPE
152 	  case EPIPE:
153 #endif /* EPIPE */
154 #ifdef ENOBUFS
155 	  case ENOBUFS:
156 #endif /* ENOBUFS */
157 #ifdef ESTALE
158 	  case ESTALE:
159 #endif /* ESTALE */
160 		printopenfds(TRUE);
161 		mci_dump_all(TRUE);
162 		break;
163 	}
164 	if (panic)
165 	{
166 #ifdef XLA
167 		xla_all_end();
168 #endif /* XLA */
169 		sync_queue_time();
170 		if (tTd(0, 1))
171 			abort();
172 		exit(EX_OSERR);
173 	}
174 	errno = 0;
175 	if (QuickAbort)
176 		longjmp(TopFrame, 2);
177 }
178 /*
179 **  USRERR -- Signal user error.
180 **
181 **	This is much like syserr except it is for user errors.
182 **
183 **	Parameters:
184 **		fmt -- the format string.  If it does not begin with
185 **			a three-digit SMTP reply code, 501 is assumed.
186 **		(others) -- printf strings
187 **
188 **	Returns:
189 **		none
190 **		Through TopFrame if QuickAbort is set.
191 **
192 **	Side Effects:
193 **		increments Errors.
194 */
195 
196 /*VARARGS1*/
197 void
198 #ifdef __STDC__
199 usrerr(const char *fmt, ...)
200 #else /* __STDC__ */
201 usrerr(fmt, va_alist)
202 	const char *fmt;
203 	va_dcl
204 #endif /* __STDC__ */
205 {
206 	char *enhsc;
207 	char *errtxt;
208 	VA_LOCAL_DECL
209 
210 	if (fmt[0] == '5' || fmt[0] == '6')
211 		enhsc = "5.0.0";
212 	else if (fmt[0] == '4' || fmt[0] == '8')
213 		enhsc = "4.0.0";
214 	else if (fmt[0] == '2')
215 		enhsc = "2.0.0";
216 	else
217 		enhsc = NULL;
218 	VA_START(fmt);
219 	errtxt = fmtmsg(MsgBuf, CurEnv->e_to, "501", enhsc, 0, fmt, ap);
220 	VA_END;
221 
222 	if (SuprErrs)
223 		return;
224 
225 	/* save this message for mailq printing */
226 	switch (MsgBuf[0])
227 	{
228 	  case '4':
229 	  case '8':
230 		if (CurEnv->e_message != NULL)
231 			break;
232 
233 		/* FALLTHROUGH */
234 
235 	  case '5':
236 	  case '6':
237 		if (CurEnv->e_message != NULL)
238 			sm_free(CurEnv->e_message);
239 		if (MsgBuf[0] == '6')
240 		{
241 			char buf[MAXLINE];
242 
243 			snprintf(buf, sizeof buf, "Postmaster warning: %.*s",
244 				(int) sizeof buf - 22, errtxt);
245 			CurEnv->e_message = newstr(buf);
246 		}
247 		else
248 		{
249 			CurEnv->e_message = newstr(errtxt);
250 		}
251 		break;
252 	}
253 
254 	puterrmsg(MsgBuf);
255 
256 	if (LogLevel > 3 && LogUsrErrs)
257 		sm_syslog(LOG_NOTICE, CurEnv->e_id, "%.900s", errtxt);
258 
259 	if (QuickAbort)
260 		longjmp(TopFrame, 1);
261 }
262 /*
263 **  USRERRENH -- Signal user error.
264 **
265 **	Same as usrerr but with enhanced status code.
266 **
267 **	Parameters:
268 **		enhsc -- the enhanced status code.
269 **		fmt -- the format string.  If it does not begin with
270 **			a three-digit SMTP reply code, 501 is assumed.
271 **		(others) -- printf strings
272 **
273 **	Returns:
274 **		none
275 **		Through TopFrame if QuickAbort is set.
276 **
277 **	Side Effects:
278 **		increments Errors.
279 */
280 
281 /*VARARGS1*/
282 void
283 #ifdef __STDC__
284 usrerrenh(char *enhsc, const char *fmt, ...)
285 #else /* __STDC__ */
286 usrerrenh(enhsc, fmt, va_alist)
287 	char *enhsc;
288 	const char *fmt;
289 	va_dcl
290 #endif /* __STDC__ */
291 {
292 	char *errtxt;
293 	VA_LOCAL_DECL
294 
295 	if (enhsc == NULL || *enhsc == '\0')
296 	{
297 		if (fmt[0] == '5' || fmt[0] == '6')
298 			enhsc = "5.0.0";
299 		else if (fmt[0] == '4' || fmt[0] == '8')
300 			enhsc = "4.0.0";
301 		else if (fmt[0] == '2')
302 			enhsc = "2.0.0";
303 	}
304 	VA_START(fmt);
305 	errtxt = fmtmsg(MsgBuf, CurEnv->e_to, "501", enhsc, 0, fmt, ap);
306 	VA_END;
307 
308 	if (SuprErrs)
309 		return;
310 
311 	/* save this message for mailq printing */
312 	switch (MsgBuf[0])
313 	{
314 	  case '4':
315 	  case '8':
316 		if (CurEnv->e_message != NULL)
317 			break;
318 
319 		/* FALLTHROUGH */
320 
321 	  case '5':
322 	  case '6':
323 		if (CurEnv->e_message != NULL)
324 			sm_free(CurEnv->e_message);
325 		if (MsgBuf[0] == '6')
326 		{
327 			char buf[MAXLINE];
328 
329 			snprintf(buf, sizeof buf, "Postmaster warning: %.*s",
330 				(int) sizeof buf - 22, errtxt);
331 			CurEnv->e_message = newstr(buf);
332 		}
333 		else
334 		{
335 			CurEnv->e_message = newstr(errtxt);
336 		}
337 		break;
338 	}
339 
340 	puterrmsg(MsgBuf);
341 
342 	if (LogLevel > 3 && LogUsrErrs)
343 		sm_syslog(LOG_NOTICE, CurEnv->e_id, "%.900s", errtxt);
344 
345 	if (QuickAbort)
346 		longjmp(TopFrame, 1);
347 }
348 /*
349 **  MESSAGE -- print message (not necessarily an error)
350 **
351 **	Parameters:
352 **		msg -- the message (printf fmt) -- it can begin with
353 **			an SMTP reply code.  If not, 050 is assumed.
354 **		(others) -- printf arguments
355 **
356 **	Returns:
357 **		none
358 **
359 **	Side Effects:
360 **		none.
361 */
362 
363 /*VARARGS1*/
364 void
365 #ifdef __STDC__
366 message(const char *msg, ...)
367 #else /* __STDC__ */
368 message(msg, va_alist)
369 	const char *msg;
370 	va_dcl
371 #endif /* __STDC__ */
372 {
373 	char *errtxt;
374 	VA_LOCAL_DECL
375 
376 	errno = 0;
377 	VA_START(msg);
378 	errtxt = fmtmsg(MsgBuf, CurEnv->e_to, "050", (char *) NULL, 0, msg, ap);
379 	VA_END;
380 	putoutmsg(MsgBuf, FALSE, FALSE);
381 
382 	/* save this message for mailq printing */
383 	switch (MsgBuf[0])
384 	{
385 	  case '4':
386 	  case '8':
387 		if (CurEnv->e_message != NULL)
388 			break;
389 		/* FALLTHROUGH */
390 
391 	  case '5':
392 		if (CurEnv->e_message != NULL)
393 			sm_free(CurEnv->e_message);
394 		CurEnv->e_message = newstr(errtxt);
395 		break;
396 	}
397 }
398 /*
399 **  NMESSAGE -- print message (not necessarily an error)
400 **
401 **	Just like "message" except it never puts the to... tag on.
402 **
403 **	Parameters:
404 **		msg -- the message (printf fmt) -- if it begins
405 **			with a three digit SMTP reply code, that is used,
406 **			otherwise 050 is assumed.
407 **		(others) -- printf arguments
408 **
409 **	Returns:
410 **		none
411 **
412 **	Side Effects:
413 **		none.
414 */
415 
416 /*VARARGS1*/
417 void
418 #ifdef __STDC__
419 nmessage(const char *msg, ...)
420 #else /* __STDC__ */
421 nmessage(msg, va_alist)
422 	const char *msg;
423 	va_dcl
424 #endif /* __STDC__ */
425 {
426 	char *errtxt;
427 	VA_LOCAL_DECL
428 
429 	errno = 0;
430 	VA_START(msg);
431 	errtxt = fmtmsg(MsgBuf, (char *) NULL, "050",
432 			(char *) NULL, 0, msg, ap);
433 	VA_END;
434 	putoutmsg(MsgBuf, FALSE, FALSE);
435 
436 	/* save this message for mailq printing */
437 	switch (MsgBuf[0])
438 	{
439 	  case '4':
440 	  case '8':
441 		if (CurEnv->e_message != NULL)
442 			break;
443 		/* FALLTHROUGH */
444 
445 	  case '5':
446 		if (CurEnv->e_message != NULL)
447 			sm_free(CurEnv->e_message);
448 		CurEnv->e_message = newstr(errtxt);
449 		break;
450 	}
451 }
452 /*
453 **  PUTOUTMSG -- output error message to transcript and channel
454 **
455 **	Parameters:
456 **		msg -- message to output (in SMTP format).
457 **		holdmsg -- if TRUE, don't output a copy of the message to
458 **			our output channel.
459 **		heldmsg -- if TRUE, this is a previously held message;
460 **			don't log it to the transcript file.
461 **
462 **	Returns:
463 **		none.
464 **
465 **	Side Effects:
466 **		Outputs msg to the transcript.
467 **		If appropriate, outputs it to the channel.
468 **		Deletes SMTP reply code number as appropriate.
469 */
470 
471 static void
472 putoutmsg(msg, holdmsg, heldmsg)
473 	char *msg;
474 	bool holdmsg;
475 	bool heldmsg;
476 {
477 	char *errtxt = msg;
478 	char msgcode = msg[0];
479 
480 	/* display for debugging */
481 	if (tTd(54, 8))
482 		dprintf("--- %s%s%s\n", msg, holdmsg ? " (hold)" : "",
483 			heldmsg ? " (held)" : "");
484 
485 	/* map warnings to something SMTP can handle */
486 	if (msgcode == '6')
487 		msg[0] = '5';
488 	else if (msgcode == '8')
489 		msg[0] = '4';
490 
491 	/* output to transcript if serious */
492 	if (!heldmsg && CurEnv != NULL && CurEnv->e_xfp != NULL &&
493 	    strchr("45", msg[0]) != NULL)
494 		fprintf(CurEnv->e_xfp, "%s\n", msg);
495 
496 	if (LogLevel >= 15 && (OpMode == MD_SMTP || OpMode == MD_DAEMON))
497 		sm_syslog(LOG_INFO, CurEnv->e_id,
498 			  "--> %s%s",
499 			  msg, holdmsg ? " (held)" : "");
500 
501 	if (msgcode == '8')
502 		msg[0] = '0';
503 
504 	/* output to channel if appropriate */
505 	if (!Verbose && msg[0] == '0')
506 		return;
507 	if (holdmsg)
508 	{
509 		/* save for possible future display */
510 		msg[0] = msgcode;
511 		if (HeldMessageBuf[0] == '5' && msgcode == '4')
512 			return;
513 		snprintf(HeldMessageBuf, sizeof HeldMessageBuf, "%s", msg);
514 		return;
515 	}
516 
517 	(void) fflush(stdout);
518 
519 	if (OutChannel == NULL)
520 		return;
521 
522 	/* find actual text of error (after SMTP status codes) */
523 	if (ISSMTPREPLY(errtxt))
524 	{
525 		int l;
526 
527 		errtxt += 4;
528 		l = isenhsc(errtxt, ' ');
529 		if (l <= 0)
530 			l = isenhsc(errtxt, '\0');
531 		if (l > 0)
532 			errtxt += l + 1;
533 	}
534 
535 	/* if DisConnected, OutChannel now points to the transcript */
536 	if (!DisConnected &&
537 	    (OpMode == MD_SMTP || OpMode == MD_DAEMON || OpMode == MD_ARPAFTP))
538 		fprintf(OutChannel, "%s\r\n", msg);
539 	else
540 		fprintf(OutChannel, "%s\n", errtxt);
541 	if (TrafficLogFile != NULL)
542 		fprintf(TrafficLogFile, "%05d >>> %s\n", (int) getpid(),
543 			(OpMode == MD_SMTP || OpMode == MD_DAEMON) ? msg : errtxt);
544 	if (msg[3] == ' ')
545 		(void) fflush(OutChannel);
546 	if (!ferror(OutChannel) || DisConnected)
547 		return;
548 
549 	/*
550 	**  Error on output -- if reporting lost channel, just ignore it.
551 	**  Also, ignore errors from QUIT response (221 message) -- some
552 	**	rude servers don't read result.
553 	*/
554 
555 	if (InChannel == NULL || feof(InChannel) || ferror(InChannel) ||
556 	    strncmp(msg, "221", 3) == 0)
557 		return;
558 
559 	/* can't call syserr, 'cause we are using MsgBuf */
560 	HoldErrs = TRUE;
561 	if (LogLevel > 0)
562 		sm_syslog(LOG_CRIT, CurEnv->e_id,
563 			  "SYSERR: putoutmsg (%s): error on output channel sending \"%s\": %s",
564 			  CurHostName == NULL ? "NO-HOST" : CurHostName,
565 			  shortenstring(msg, MAXSHORTSTR), errstring(errno));
566 }
567 /*
568 **  PUTERRMSG -- like putoutmsg, but does special processing for error messages
569 **
570 **	Parameters:
571 **		msg -- the message to output.
572 **
573 **	Returns:
574 **		none.
575 **
576 **	Side Effects:
577 **		Sets the fatal error bit in the envelope as appropriate.
578 */
579 
580 static void
581 puterrmsg(msg)
582 	char *msg;
583 {
584 	char msgcode = msg[0];
585 
586 	/* output the message as usual */
587 	putoutmsg(msg, HoldErrs, FALSE);
588 
589 	/* be careful about multiple error messages */
590 	if (OnlyOneError)
591 		HoldErrs = TRUE;
592 
593 	/* signal the error */
594 	Errors++;
595 
596 	if (CurEnv == NULL)
597 		return;
598 
599 	if (msgcode == '6')
600 	{
601 		/* notify the postmaster */
602 		CurEnv->e_flags |= EF_PM_NOTIFY;
603 	}
604 	else if (msgcode == '5' && bitset(EF_GLOBALERRS, CurEnv->e_flags))
605 	{
606 		/* mark long-term fatal errors */
607 		CurEnv->e_flags |= EF_FATALERRS;
608 	}
609 }
610 /*
611 **  ISENHSC -- check whether a string contains an enhanced status code
612 **
613 **	Parameters:
614 **		s -- string with possible enhanced status code.
615 **		delim -- delim for enhanced status code.
616 **
617 **	Returns:
618 **		0  -- no enhanced status code.
619 **		>4 -- length of enhanced status code.
620 **
621 **	Side Effects:
622 **		none.
623 */
624 int
625 isenhsc(s, delim)
626 	const char *s;
627 	int delim;
628 {
629 	int l, h;
630 
631 	if (s == NULL)
632 		return 0;
633 	if (!((*s == '2' || *s == '4' || *s == '5') && s[1] == '.'))
634 		return 0;
635 	h = 0;
636 	l = 2;
637 	while (h < 3 && isascii(s[l + h]) && isdigit(s[l + h]))
638 		++h;
639 	if (h == 0 || s[l + h] != '.')
640 		return 0;
641 	l += h + 1;
642 	h = 0;
643 	while (h < 3 && isascii(s[l + h]) && isdigit(s[l + h]))
644 		++h;
645 	if (h == 0 || s[l + h] != delim)
646 		return 0;
647 	return l + h;
648 }
649 /*
650 **  EXTENHSC -- check and extract an enhanced status code
651 **
652 **	Parameters:
653 **		s -- string with possible enhanced status code.
654 **		delim -- delim for enhanced status code.
655 **		e -- pointer to storage for enhanced status code.
656 **			must be != NULL and have space for at least
657 **			10 characters ([245].[0-9]{1,3}.[0-9]{1,3})
658 **
659 **	Returns:
660 **		0  -- no enhanced status code.
661 **		>4 -- length of enhanced status code.
662 **
663 **	Side Effects:
664 **		fills e with enhanced status code.
665 */
666 int
667 extenhsc(s, delim, e)
668 	const char *s;
669 	int delim;
670 	char *e;
671 {
672 	int l, h;
673 
674 	if (s == NULL)
675 		return 0;
676 	if (!((*s == '2' || *s == '4' || *s == '5') && s[1] == '.'))
677 		return 0;
678 	h = 0;
679 	l = 2;
680 	e[0] = s[0];
681 	e[1] = '.';
682 	while (h < 3 && isascii(s[l + h]) && isdigit(s[l + h]))
683 	{
684 		e[l + h] = s[l + h];
685 		++h;
686 	}
687 	if (h == 0 || s[l + h] != '.')
688 		return 0;
689 	e[l + h] = '.';
690 	l += h + 1;
691 	h = 0;
692 	while (h < 3 && isascii(s[l + h]) && isdigit(s[l + h]))
693 	{
694 		e[l + h] = s[l + h];
695 		++h;
696 	}
697 	if (h == 0 || s[l + h] != delim)
698 		return 0;
699 	e[l + h] = '\0';
700 	return l + h;
701 }
702 /*
703 **  FMTMSG -- format a message into buffer.
704 **
705 **	Parameters:
706 **		eb -- error buffer to get result -- MUST BE MsgBuf.
707 **		to -- the recipient tag for this message.
708 **		num -- default three digit SMTP reply code.
709 **		enhsc -- enhanced status code.
710 **		en -- the error number to display.
711 **		fmt -- format of string.
712 **		ap -- arguments for fmt.
713 **
714 **	Returns:
715 **		pointer to error text beyond status codes.
716 **
717 **	Side Effects:
718 **		none.
719 */
720 
721 static char *
722 fmtmsg(eb, to, num, enhsc, eno, fmt, ap)
723 	register char *eb;
724 	const char *to;
725 	const char *num;
726 	const char *enhsc;
727 	int eno;
728 	const char *fmt;
729 	va_list ap;
730 {
731 	char del;
732 	int l;
733 	int spaceleft = sizeof MsgBuf;
734 	char *errtxt;
735 
736 	/* output the reply code */
737 	if (ISSMTPCODE(fmt))
738 	{
739 		num = fmt;
740 		fmt += 4;
741 	}
742 	if (num[3] == '-')
743 		del = '-';
744 	else
745 		del = ' ';
746 	(void) snprintf(eb, spaceleft, "%3.3s%c", num, del);
747 	eb += 4;
748 	spaceleft -= 4;
749 
750 	if ((l = isenhsc(fmt, ' ' )) > 0 && l < spaceleft - 4)
751 	{
752 		/* copy enh.status code including trailing blank */
753 		l++;
754 		(void) strlcpy(eb, fmt, l + 1);
755 		eb += l;
756 		spaceleft -= l;
757 		fmt += l;
758 	}
759 	else if ((l = isenhsc(enhsc, '\0')) > 0 && l < spaceleft - 4)
760 	{
761 		/* copy enh.status code */
762 		(void) strlcpy(eb, enhsc, l + 1);
763 		eb[l] = ' ';
764 		eb[++l] = '\0';
765 		eb += l;
766 		spaceleft -= l;
767 	}
768 	errtxt = eb;
769 
770 	/* output the file name and line number */
771 	if (FileName != NULL)
772 	{
773 		(void) snprintf(eb, spaceleft, "%s: line %d: ",
774 			shortenstring(FileName, 83), LineNumber);
775 		eb += (l = strlen(eb));
776 		spaceleft -= l;
777 	}
778 
779 	/*
780 	**  output the "to" address only if it is defined and one of the
781 	**  following codes is used:
782 	**  050 internal notices, e.g., alias expansion
783 	**  250 Ok
784 	**  252 Cannot VRFY user, but will accept message and attempt delivery
785 	**  450 Requested mail action not taken: mailbox unavailable
786 	**  550 Requested action not taken: mailbox unavailable
787 	**  553 Requested action not taken: mailbox name not allowed
788 	**
789 	**  Notice: this still isn't "the right thing", this code shouldn't
790 	**	(indirectly) depend on CurEnv->e_to.
791 	*/
792 
793 	if (to != NULL && to[0] != '\0' &&
794 	    (strncmp(num, "050", 3) == 0 ||
795 	     strncmp(num, "250", 3) == 0 ||
796 	     strncmp(num, "252", 3) == 0 ||
797 	     strncmp(num, "450", 3) == 0 ||
798 	     strncmp(num, "550", 3) == 0 ||
799 	     strncmp(num, "553", 3) == 0))
800 	{
801 		(void) snprintf(eb, spaceleft, "%s... ",
802 			shortenstring(to, MAXSHORTSTR));
803 		spaceleft -= strlen(eb);
804 		while (*eb != '\0')
805 			*eb++ &= 0177;
806 	}
807 
808 	/* output the message */
809 	(void) vsnprintf(eb, spaceleft, fmt, ap);
810 	spaceleft -= strlen(eb);
811 	while (*eb != '\0')
812 		*eb++ &= 0177;
813 
814 	/* output the error code, if any */
815 	if (eno != 0)
816 		(void) snprintf(eb, spaceleft, ": %s", errstring(eno));
817 
818 	return errtxt;
819 }
820 /*
821 **  BUFFER_ERRORS -- arrange to buffer future error messages
822 **
823 **	Parameters:
824 **		none
825 **
826 **	Returns:
827 **		none.
828 */
829 
830 void
831 buffer_errors()
832 {
833 	HeldMessageBuf[0] = '\0';
834 	HoldErrs = TRUE;
835 }
836 /*
837 **  FLUSH_ERRORS -- flush the held error message buffer
838 **
839 **	Parameters:
840 **		print -- if set, print the message, otherwise just
841 **			delete it.
842 **
843 **	Returns:
844 **		none.
845 */
846 
847 void
848 flush_errors(print)
849 	bool print;
850 {
851 	if (print && HeldMessageBuf[0] != '\0')
852 		putoutmsg(HeldMessageBuf, FALSE, TRUE);
853 	HeldMessageBuf[0] = '\0';
854 	HoldErrs = FALSE;
855 }
856 /*
857 **  ERRSTRING -- return string description of error code
858 **
859 **	Parameters:
860 **		errnum -- the error number to translate
861 **
862 **	Returns:
863 **		A string description of errnum.
864 **
865 **	Side Effects:
866 **		none.
867 */
868 
869 const char *
870 errstring(errnum)
871 	int errnum;
872 {
873 	char *dnsmsg;
874 	char *bp;
875 	static char buf[MAXLINE];
876 #if !HASSTRERROR && !defined(ERRLIST_PREDEFINED)
877 	extern char *sys_errlist[];
878 	extern int sys_nerr;
879 #endif /* !HASSTRERROR && !defined(ERRLIST_PREDEFINED) */
880 
881 	/*
882 	**  Handle special network error codes.
883 	**
884 	**	These are 4.2/4.3bsd specific; they should be in daemon.c.
885 	*/
886 
887 	dnsmsg = NULL;
888 	switch (errnum)
889 	{
890 #if defined(DAEMON) && defined(ETIMEDOUT)
891 	  case ETIMEDOUT:
892 	  case ECONNRESET:
893 		bp = buf;
894 # if HASSTRERROR
895 		snprintf(bp, SPACELEFT(buf, bp), "%s", strerror(errnum));
896 # else /* HASSTRERROR */
897 		if (errnum >= 0 && errnum < sys_nerr)
898 			snprintf(bp, SPACELEFT(buf, bp), "%s", sys_errlist[errnum]);
899 		else
900 			snprintf(bp, SPACELEFT(buf, bp), "Error %d", errnum);
901 # endif /* HASSTRERROR */
902 		bp += strlen(bp);
903 		if (CurHostName != NULL)
904 		{
905 			if (errnum == ETIMEDOUT)
906 			{
907 				snprintf(bp, SPACELEFT(buf, bp), " with ");
908 				bp += strlen(bp);
909 			}
910 			else
911 			{
912 				bp = buf;
913 				snprintf(bp, SPACELEFT(buf, bp),
914 					"Connection reset by ");
915 				bp += strlen(bp);
916 			}
917 			snprintf(bp, SPACELEFT(buf, bp), "%s",
918 				shortenstring(CurHostName, MAXSHORTSTR));
919 			bp += strlen(buf);
920 		}
921 		if (SmtpPhase != NULL)
922 		{
923 			snprintf(bp, SPACELEFT(buf, bp), " during %s",
924 				SmtpPhase);
925 		}
926 		return buf;
927 
928 	  case EHOSTDOWN:
929 		if (CurHostName == NULL)
930 			break;
931 		(void) snprintf(buf, sizeof buf, "Host %s is down",
932 			shortenstring(CurHostName, MAXSHORTSTR));
933 		return buf;
934 
935 	  case ECONNREFUSED:
936 		if (CurHostName == NULL)
937 			break;
938 		(void) snprintf(buf, sizeof buf, "Connection refused by %s",
939 			shortenstring(CurHostName, MAXSHORTSTR));
940 		return buf;
941 #endif /* defined(DAEMON) && defined(ETIMEDOUT) */
942 
943 #if NAMED_BIND
944 	  case HOST_NOT_FOUND + E_DNSBASE:
945 		dnsmsg = "host not found";
946 		break;
947 
948 	  case TRY_AGAIN + E_DNSBASE:
949 		dnsmsg = "host name lookup failure";
950 		break;
951 
952 	  case NO_RECOVERY + E_DNSBASE:
953 		dnsmsg = "non-recoverable error";
954 		break;
955 
956 	  case NO_DATA + E_DNSBASE:
957 		dnsmsg = "no data known";
958 		break;
959 #endif /* NAMED_BIND */
960 
961 	  case EPERM:
962 		/* SunOS gives "Not owner" -- this is the POSIX message */
963 		return "Operation not permitted";
964 
965 	/*
966 	**  Error messages used internally in sendmail.
967 	*/
968 
969 	  case E_SM_OPENTIMEOUT:
970 		return "Timeout on file open";
971 
972 	  case E_SM_NOSLINK:
973 		return "Symbolic links not allowed";
974 
975 	  case E_SM_NOHLINK:
976 		return "Hard links not allowed";
977 
978 	  case E_SM_REGONLY:
979 		return "Regular files only";
980 
981 	  case E_SM_ISEXEC:
982 		return "Executable files not allowed";
983 
984 	  case E_SM_WWDIR:
985 		return "World writable directory";
986 
987 	  case E_SM_GWDIR:
988 		return "Group writable directory";
989 
990 	  case E_SM_FILECHANGE:
991 		return "File changed after open";
992 
993 	  case E_SM_WWFILE:
994 		return "World writable file";
995 
996 	  case E_SM_GWFILE:
997 		return "Group writable file";
998 
999 	  case E_SM_GRFILE:
1000 		return "Group readable file";
1001 
1002 	  case E_SM_WRFILE:
1003 		return "World readable file";
1004 	}
1005 
1006 	if (dnsmsg != NULL)
1007 	{
1008 		bp = buf;
1009 		bp += strlcpy(bp, "Name server: ", sizeof buf);
1010 		if (CurHostName != NULL)
1011 		{
1012 			snprintf(bp, SPACELEFT(buf, bp), "%s: ",
1013 				shortenstring(CurHostName, MAXSHORTSTR));
1014 			bp += strlen(bp);
1015 		}
1016 		snprintf(bp, SPACELEFT(buf, bp), "%s", dnsmsg);
1017 		return buf;
1018 	}
1019 
1020 #ifdef LDAPMAP
1021 	if (errnum >= E_LDAPBASE)
1022 		return ldap_err2string(errnum - E_LDAPBASE);
1023 #endif /* LDAPMAP */
1024 
1025 #if HASSTRERROR
1026 	return strerror(errnum);
1027 #else /* HASSTRERROR */
1028 	if (errnum > 0 && errnum < sys_nerr)
1029 		return sys_errlist[errnum];
1030 
1031 	(void) snprintf(buf, sizeof buf, "Error %d", errnum);
1032 	return buf;
1033 #endif /* HASSTRERROR */
1034 }
1035