138032Speter/*
2261363Sgshapiro * Copyright (c) 1998-2003, 2010 Proofpoint, Inc. and its suppliers.
364565Sgshapiro *	All rights reserved.
438032Speter * Copyright (c) 1983, 1995-1997 Eric P. Allman.  All rights reserved.
538032Speter * Copyright (c) 1988, 1993
638032Speter *	The Regents of the University of California.  All rights reserved.
738032Speter *
838032Speter * By using this file, you agree to the terms and conditions set
938032Speter * forth in the LICENSE file which can be found at the top level of
1038032Speter * the sendmail distribution.
1138032Speter *
1238032Speter */
1338032Speter
1490795Sgshapiro#include <sendmail.h>
1538032Speter
16266692SgshapiroSM_RCSID("@(#)$Id: err.c,v 8.206 2013-11-22 20:51:55 ca Exp $")
1790795Sgshapiro
1890795Sgshapiro#if LDAPMAP
1964565Sgshapiro# include <lber.h>
2064565Sgshapiro# include <ldap.h>			/* for LDAP error codes */
2164565Sgshapiro#endif /* LDAPMAP */
2264565Sgshapiro
2364565Sgshapirostatic void	putoutmsg __P((char *, bool, bool));
2464565Sgshapirostatic void	puterrmsg __P((char *));
2564565Sgshapirostatic char	*fmtmsg __P((char *, const char *, const char *, const char *,
2664565Sgshapiro			     int, const char *, va_list));
2764565Sgshapiro
2838032Speter/*
2990795Sgshapiro**  FATAL_ERROR -- handle a fatal exception
3090795Sgshapiro**
3190795Sgshapiro**	This function is installed as the default exception handler
3290795Sgshapiro**	in the main sendmail process, and in all child processes
3390795Sgshapiro**	that we create.  Its job is to handle exceptions that are not
3490795Sgshapiro**	handled at a lower level.
3590795Sgshapiro**
3690795Sgshapiro**	The theory is that unhandled exceptions will be 'fatal' class
3790795Sgshapiro**	exceptions (with an "F:" prefix), such as the out-of-memory
3890795Sgshapiro**	exception "F:sm.heap".  As such, they are handled by exiting
3990795Sgshapiro**	the process in exactly the same way that xalloc() in Sendmail 8.10
4090795Sgshapiro**	exits the process when it fails due to lack of memory:
4190795Sgshapiro**	we call syserr with a message beginning with "!".
4290795Sgshapiro**
4390795Sgshapiro**	Parameters:
4490795Sgshapiro**		exc -- exception which is terminating this process
4590795Sgshapiro**
4690795Sgshapiro**	Returns:
4790795Sgshapiro**		none
4890795Sgshapiro*/
4990795Sgshapiro
5090795Sgshapirovoid
5190795Sgshapirofatal_error(exc)
5290795Sgshapiro	SM_EXC_T *exc;
5390795Sgshapiro{
5490795Sgshapiro	static char buf[256];
5590795Sgshapiro	SM_FILE_T f;
5690795Sgshapiro
5790795Sgshapiro	/*
5890795Sgshapiro	**  This function may be called when the heap is exhausted.
5990795Sgshapiro	**  The following code writes the message for 'exc' into our
6090795Sgshapiro	**  static buffer without allocating memory or raising exceptions.
6190795Sgshapiro	*/
6290795Sgshapiro
6390795Sgshapiro	sm_strio_init(&f, buf, sizeof(buf));
6490795Sgshapiro	sm_exc_write(exc, &f);
6590795Sgshapiro	(void) sm_io_flush(&f, SM_TIME_DEFAULT);
6690795Sgshapiro
6790795Sgshapiro	/*
6890795Sgshapiro	**  Terminate the process after logging an error and cleaning up.
6990795Sgshapiro	**  Problems:
7090795Sgshapiro	**  - syserr decides what class of error this is by looking at errno.
7190795Sgshapiro	**    That's no good; we should look at the exc structure.
7290795Sgshapiro	**  - The cleanup code should be moved out of syserr
7390795Sgshapiro	**    and into individual exception handlers
7490795Sgshapiro	**    that are part of the module they clean up after.
7590795Sgshapiro	*/
7690795Sgshapiro
7790795Sgshapiro	errno = ENOMEM;
7890795Sgshapiro	syserr("!%s", buf);
7990795Sgshapiro}
8090795Sgshapiro
8190795Sgshapiro/*
8238032Speter**  SYSERR -- Print error message.
8338032Speter**
8490795Sgshapiro**	Prints an error message via sm_io_printf to the diagnostic output.
8538032Speter**
8638032Speter**	If the first character of the syserr message is `!' it will
8738032Speter**	log this as an ALERT message and exit immediately.  This can
8838032Speter**	leave queue files in an indeterminate state, so it should not
8938032Speter**	be used lightly.
9038032Speter**
9190795Sgshapiro**	If the first character of the syserr message is '!' or '@'
9290795Sgshapiro**	then syserr knows that the process is about to be terminated,
9390795Sgshapiro**	so the SMTP reply code defaults to 421.  Otherwise, the
9490795Sgshapiro**	reply code defaults to 451 or 554, depending on errno.
9590795Sgshapiro**
9638032Speter**	Parameters:
9790795Sgshapiro**		fmt -- the format string.  An optional '!' or '@',
9890795Sgshapiro**			followed by an optional three-digit SMTP
9990795Sgshapiro**			reply code, followed by message text.
10038032Speter**		(others) -- parameters
10138032Speter**
10238032Speter**	Returns:
10338032Speter**		none
10490795Sgshapiro**		Raises E:mta.quickabort if QuickAbort is set.
10538032Speter**
10638032Speter**	Side Effects:
10738032Speter**		increments Errors.
10838032Speter**		sets ExitStat.
10938032Speter*/
11038032Speter
11164565Sgshapirochar		MsgBuf[BUFSIZ*2];	/* text of most recent message */
112168520Sgshapirostatic char	HeldMessageBuf[sizeof(MsgBuf)];	/* for held messages */
11338032Speter
11438032Speter#if NAMED_BIND && !defined(NO_DATA)
11538032Speter# define NO_DATA	NO_ADDRESS
11664565Sgshapiro#endif /* NAMED_BIND && !defined(NO_DATA) */
11738032Speter
11838032Spetervoid
11938032Speter/*VARARGS1*/
12038032Speter#ifdef __STDC__
12138032Spetersyserr(const char *fmt, ...)
12264565Sgshapiro#else /* __STDC__ */
12338032Spetersyserr(fmt, va_alist)
12438032Speter	const char *fmt;
12538032Speter	va_dcl
12664565Sgshapiro#endif /* __STDC__ */
12738032Speter{
12838032Speter	register char *p;
12964565Sgshapiro	int save_errno = errno;
13038032Speter	bool panic;
13190795Sgshapiro	bool exiting;
13264565Sgshapiro	char *user;
13364565Sgshapiro	char *enhsc;
13464565Sgshapiro	char *errtxt;
13538032Speter	struct passwd *pw;
13638032Speter	char ubuf[80];
13790795Sgshapiro	SM_VA_LOCAL_DECL
13838032Speter
13990795Sgshapiro	switch (*fmt)
14038032Speter	{
14190795Sgshapiro	  case '!':
14290795Sgshapiro		++fmt;
14390795Sgshapiro		panic = true;
14490795Sgshapiro		exiting = true;
14590795Sgshapiro		break;
14690795Sgshapiro	  case '@':
14790795Sgshapiro		++fmt;
14890795Sgshapiro		panic = false;
14990795Sgshapiro		exiting = true;
15090795Sgshapiro		break;
15190795Sgshapiro	  default:
15290795Sgshapiro		panic = false;
15390795Sgshapiro		exiting = false;
15490795Sgshapiro		break;
15538032Speter	}
15638032Speter
15738032Speter	/* format and output the error message */
15890795Sgshapiro	if (exiting)
15964565Sgshapiro	{
16090795Sgshapiro		/*
16190795Sgshapiro		**  Since we are terminating the process,
16290795Sgshapiro		**  we are aborting the entire SMTP session,
16390795Sgshapiro		**  rather than just the current transaction.
16490795Sgshapiro		*/
16590795Sgshapiro
16690795Sgshapiro		p = "421";
16790795Sgshapiro		enhsc = "4.0.0";
16890795Sgshapiro	}
16990795Sgshapiro	else if (save_errno == 0)
17090795Sgshapiro	{
17138032Speter		p = "554";
17264565Sgshapiro		enhsc = "5.0.0";
17364565Sgshapiro	}
17438032Speter	else
17564565Sgshapiro	{
17638032Speter		p = "451";
17764565Sgshapiro		enhsc = "4.0.0";
17864565Sgshapiro	}
17990795Sgshapiro	SM_VA_START(ap, fmt);
18064565Sgshapiro	errtxt = fmtmsg(MsgBuf, (char *) NULL, p, enhsc, save_errno, fmt, ap);
18190795Sgshapiro	SM_VA_END(ap);
18238032Speter	puterrmsg(MsgBuf);
18338032Speter
18438032Speter	/* save this message for mailq printing */
18538032Speter	if (!panic && CurEnv != NULL)
18638032Speter	{
18790795Sgshapiro		char *nmsg = sm_rpool_strdup_x(CurEnv->e_rpool, errtxt);
18890795Sgshapiro
18990795Sgshapiro		if (CurEnv->e_rpool == NULL && CurEnv->e_message != NULL)
19077352Sgshapiro			sm_free(CurEnv->e_message);
19190795Sgshapiro		CurEnv->e_message = nmsg;
19238032Speter	}
19338032Speter
19438032Speter	/* determine exit status if not already set */
19538032Speter	if (ExitStat == EX_OK)
19638032Speter	{
19764565Sgshapiro		if (save_errno == 0)
19838032Speter			ExitStat = EX_SOFTWARE;
19938032Speter		else
20038032Speter			ExitStat = EX_OSERR;
20138032Speter		if (tTd(54, 1))
20290795Sgshapiro			sm_dprintf("syserr: ExitStat = %d\n", ExitStat);
20338032Speter	}
20438032Speter
20577352Sgshapiro	pw = sm_getpwuid(RealUid);
20638032Speter	if (pw != NULL)
20764565Sgshapiro		user = pw->pw_name;
20838032Speter	else
20938032Speter	{
21064565Sgshapiro		user = ubuf;
211168520Sgshapiro		(void) sm_snprintf(ubuf, sizeof(ubuf), "UID%d", (int) RealUid);
21238032Speter	}
21338032Speter
21438032Speter	if (LogLevel > 0)
21538032Speter		sm_syslog(panic ? LOG_ALERT : LOG_CRIT,
21638032Speter			  CurEnv == NULL ? NOQID : CurEnv->e_id,
21738032Speter			  "SYSERR(%s): %.900s",
21864565Sgshapiro			  user, errtxt);
21964565Sgshapiro	switch (save_errno)
22038032Speter	{
22138032Speter	  case EBADF:
22238032Speter	  case ENFILE:
22338032Speter	  case EMFILE:
22438032Speter	  case ENOTTY:
22538032Speter#ifdef EFBIG
22638032Speter	  case EFBIG:
22764565Sgshapiro#endif /* EFBIG */
22838032Speter#ifdef ESPIPE
22938032Speter	  case ESPIPE:
23064565Sgshapiro#endif /* ESPIPE */
23138032Speter#ifdef EPIPE
23238032Speter	  case EPIPE:
23364565Sgshapiro#endif /* EPIPE */
23438032Speter#ifdef ENOBUFS
23538032Speter	  case ENOBUFS:
23664565Sgshapiro#endif /* ENOBUFS */
23738032Speter#ifdef ESTALE
23838032Speter	  case ESTALE:
23964565Sgshapiro#endif /* ESTALE */
24090795Sgshapiro		printopenfds(true);
241132946Sgshapiro		mci_dump_all(smioout, true);
24238032Speter		break;
24338032Speter	}
24438032Speter	if (panic)
24538032Speter	{
24690795Sgshapiro#if XLA
24738032Speter		xla_all_end();
24864565Sgshapiro#endif /* XLA */
24964565Sgshapiro		sync_queue_time();
25038032Speter		if (tTd(0, 1))
25138032Speter			abort();
25238032Speter		exit(EX_OSERR);
25338032Speter	}
25438032Speter	errno = 0;
25538032Speter	if (QuickAbort)
25690795Sgshapiro		sm_exc_raisenew_x(&EtypeQuickAbort, 2);
25738032Speter}
25890795Sgshapiro/*
25938032Speter**  USRERR -- Signal user error.
26038032Speter**
26138032Speter**	This is much like syserr except it is for user errors.
26238032Speter**
26338032Speter**	Parameters:
26438032Speter**		fmt -- the format string.  If it does not begin with
26590795Sgshapiro**			a three-digit SMTP reply code, 550 is assumed.
26690795Sgshapiro**		(others) -- sm_io_printf strings
26738032Speter**
26838032Speter**	Returns:
26938032Speter**		none
27090795Sgshapiro**		Raises E:mta.quickabort if QuickAbort is set.
27138032Speter**
27238032Speter**	Side Effects:
27338032Speter**		increments Errors.
27438032Speter*/
27538032Speter
27638032Speter/*VARARGS1*/
27738032Spetervoid
27838032Speter#ifdef __STDC__
27938032Speterusrerr(const char *fmt, ...)
28064565Sgshapiro#else /* __STDC__ */
28138032Speterusrerr(fmt, va_alist)
28238032Speter	const char *fmt;
28338032Speter	va_dcl
28464565Sgshapiro#endif /* __STDC__ */
28538032Speter{
28664565Sgshapiro	char *enhsc;
28764565Sgshapiro	char *errtxt;
28890795Sgshapiro	SM_VA_LOCAL_DECL
28938032Speter
29064565Sgshapiro	if (fmt[0] == '5' || fmt[0] == '6')
29164565Sgshapiro		enhsc = "5.0.0";
29264565Sgshapiro	else if (fmt[0] == '4' || fmt[0] == '8')
29364565Sgshapiro		enhsc = "4.0.0";
29464565Sgshapiro	else if (fmt[0] == '2')
29564565Sgshapiro		enhsc = "2.0.0";
29664565Sgshapiro	else
29764565Sgshapiro		enhsc = NULL;
29890795Sgshapiro	SM_VA_START(ap, fmt);
29990795Sgshapiro	errtxt = fmtmsg(MsgBuf, CurEnv->e_to, "550", enhsc, 0, fmt, ap);
30090795Sgshapiro	SM_VA_END(ap);
30164565Sgshapiro
30238032Speter	if (SuprErrs)
30338032Speter		return;
30438032Speter
30564565Sgshapiro	/* save this message for mailq printing */
30664565Sgshapiro	switch (MsgBuf[0])
30764565Sgshapiro	{
30864565Sgshapiro	  case '4':
30964565Sgshapiro	  case '8':
31064565Sgshapiro		if (CurEnv->e_message != NULL)
31164565Sgshapiro			break;
31264565Sgshapiro
31364565Sgshapiro		/* FALLTHROUGH */
31464565Sgshapiro
31564565Sgshapiro	  case '5':
31664565Sgshapiro	  case '6':
31790795Sgshapiro		if (CurEnv->e_rpool == NULL && CurEnv->e_message != NULL)
31877352Sgshapiro			sm_free(CurEnv->e_message);
31964565Sgshapiro		if (MsgBuf[0] == '6')
32064565Sgshapiro		{
32164565Sgshapiro			char buf[MAXLINE];
32264565Sgshapiro
323168520Sgshapiro			(void) sm_snprintf(buf, sizeof(buf),
32490795Sgshapiro					   "Postmaster warning: %.*s",
325168520Sgshapiro					   (int) sizeof(buf) - 22, errtxt);
32690795Sgshapiro			CurEnv->e_message =
32790795Sgshapiro				sm_rpool_strdup_x(CurEnv->e_rpool, buf);
32864565Sgshapiro		}
32964565Sgshapiro		else
33064565Sgshapiro		{
33190795Sgshapiro			CurEnv->e_message =
33290795Sgshapiro				sm_rpool_strdup_x(CurEnv->e_rpool, errtxt);
33364565Sgshapiro		}
33464565Sgshapiro		break;
33564565Sgshapiro	}
33664565Sgshapiro
33764565Sgshapiro	puterrmsg(MsgBuf);
33864565Sgshapiro	if (LogLevel > 3 && LogUsrErrs)
33964565Sgshapiro		sm_syslog(LOG_NOTICE, CurEnv->e_id, "%.900s", errtxt);
34064565Sgshapiro	if (QuickAbort)
34190795Sgshapiro		sm_exc_raisenew_x(&EtypeQuickAbort, 1);
34264565Sgshapiro}
34390795Sgshapiro/*
34464565Sgshapiro**  USRERRENH -- Signal user error.
34564565Sgshapiro**
34664565Sgshapiro**	Same as usrerr but with enhanced status code.
34764565Sgshapiro**
34864565Sgshapiro**	Parameters:
34964565Sgshapiro**		enhsc -- the enhanced status code.
35064565Sgshapiro**		fmt -- the format string.  If it does not begin with
35190795Sgshapiro**			a three-digit SMTP reply code, 550 is assumed.
35290795Sgshapiro**		(others) -- sm_io_printf strings
35364565Sgshapiro**
35464565Sgshapiro**	Returns:
35564565Sgshapiro**		none
35690795Sgshapiro**		Raises E:mta.quickabort if QuickAbort is set.
35764565Sgshapiro**
35864565Sgshapiro**	Side Effects:
35964565Sgshapiro**		increments Errors.
36064565Sgshapiro*/
36164565Sgshapiro
362223067Sgshapiro/*VARARGS2*/
36364565Sgshapirovoid
36464565Sgshapiro#ifdef __STDC__
36564565Sgshapirousrerrenh(char *enhsc, const char *fmt, ...)
36664565Sgshapiro#else /* __STDC__ */
36764565Sgshapirousrerrenh(enhsc, fmt, va_alist)
36864565Sgshapiro	char *enhsc;
36964565Sgshapiro	const char *fmt;
37064565Sgshapiro	va_dcl
37164565Sgshapiro#endif /* __STDC__ */
37264565Sgshapiro{
37364565Sgshapiro	char *errtxt;
37490795Sgshapiro	SM_VA_LOCAL_DECL
37564565Sgshapiro
37664565Sgshapiro	if (enhsc == NULL || *enhsc == '\0')
37764565Sgshapiro	{
37864565Sgshapiro		if (fmt[0] == '5' || fmt[0] == '6')
37964565Sgshapiro			enhsc = "5.0.0";
38064565Sgshapiro		else if (fmt[0] == '4' || fmt[0] == '8')
38164565Sgshapiro			enhsc = "4.0.0";
38264565Sgshapiro		else if (fmt[0] == '2')
38364565Sgshapiro			enhsc = "2.0.0";
38464565Sgshapiro	}
38590795Sgshapiro	SM_VA_START(ap, fmt);
38690795Sgshapiro	errtxt = fmtmsg(MsgBuf, CurEnv->e_to, "550", enhsc, 0, fmt, ap);
38790795Sgshapiro	SM_VA_END(ap);
38838032Speter
38964565Sgshapiro	if (SuprErrs)
39064565Sgshapiro		return;
39164565Sgshapiro
39238032Speter	/* save this message for mailq printing */
39338032Speter	switch (MsgBuf[0])
39438032Speter	{
39538032Speter	  case '4':
39638032Speter	  case '8':
39738032Speter		if (CurEnv->e_message != NULL)
39838032Speter			break;
39938032Speter
40064565Sgshapiro		/* FALLTHROUGH */
40138032Speter
40238032Speter	  case '5':
40338032Speter	  case '6':
40490795Sgshapiro		if (CurEnv->e_rpool == NULL && CurEnv->e_message != NULL)
40577352Sgshapiro			sm_free(CurEnv->e_message);
40638032Speter		if (MsgBuf[0] == '6')
40738032Speter		{
40838032Speter			char buf[MAXLINE];
40938032Speter
410168520Sgshapiro			(void) sm_snprintf(buf, sizeof(buf),
41190795Sgshapiro					   "Postmaster warning: %.*s",
412168520Sgshapiro					   (int) sizeof(buf) - 22, errtxt);
41390795Sgshapiro			CurEnv->e_message =
41490795Sgshapiro				sm_rpool_strdup_x(CurEnv->e_rpool, buf);
41538032Speter		}
41638032Speter		else
41738032Speter		{
41890795Sgshapiro			CurEnv->e_message =
41990795Sgshapiro				sm_rpool_strdup_x(CurEnv->e_rpool, errtxt);
42038032Speter		}
42138032Speter		break;
42238032Speter	}
42338032Speter
42438032Speter	puterrmsg(MsgBuf);
42538032Speter	if (LogLevel > 3 && LogUsrErrs)
42664565Sgshapiro		sm_syslog(LOG_NOTICE, CurEnv->e_id, "%.900s", errtxt);
42738032Speter	if (QuickAbort)
42890795Sgshapiro		sm_exc_raisenew_x(&EtypeQuickAbort, 1);
42938032Speter}
430223067Sgshapiro
43190795Sgshapiro/*
43238032Speter**  MESSAGE -- print message (not necessarily an error)
43338032Speter**
43438032Speter**	Parameters:
43590795Sgshapiro**		msg -- the message (sm_io_printf fmt) -- it can begin with
43638032Speter**			an SMTP reply code.  If not, 050 is assumed.
43790795Sgshapiro**		(others) -- sm_io_printf arguments
43838032Speter**
43938032Speter**	Returns:
44038032Speter**		none
44138032Speter**
44238032Speter**	Side Effects:
44338032Speter**		none.
44438032Speter*/
44538032Speter
44638032Speter/*VARARGS1*/
44738032Spetervoid
44838032Speter#ifdef __STDC__
44938032Spetermessage(const char *msg, ...)
45064565Sgshapiro#else /* __STDC__ */
45138032Spetermessage(msg, va_alist)
45238032Speter	const char *msg;
45338032Speter	va_dcl
45464565Sgshapiro#endif /* __STDC__ */
45538032Speter{
45664565Sgshapiro	char *errtxt;
45790795Sgshapiro	SM_VA_LOCAL_DECL
45838032Speter
45938032Speter	errno = 0;
46090795Sgshapiro	SM_VA_START(ap, msg);
46164565Sgshapiro	errtxt = fmtmsg(MsgBuf, CurEnv->e_to, "050", (char *) NULL, 0, msg, ap);
46290795Sgshapiro	SM_VA_END(ap);
46390795Sgshapiro	putoutmsg(MsgBuf, false, false);
46438032Speter
46538032Speter	/* save this message for mailq printing */
46638032Speter	switch (MsgBuf[0])
46738032Speter	{
46838032Speter	  case '4':
46938032Speter	  case '8':
47038032Speter		if (CurEnv->e_message != NULL)
47138032Speter			break;
47264565Sgshapiro		/* FALLTHROUGH */
47338032Speter
47438032Speter	  case '5':
47590795Sgshapiro		if (CurEnv->e_rpool == NULL && CurEnv->e_message != NULL)
47677352Sgshapiro			sm_free(CurEnv->e_message);
477223067Sgshapiro		CurEnv->e_message = sm_rpool_strdup_x(CurEnv->e_rpool, errtxt);
47838032Speter		break;
47938032Speter	}
48038032Speter}
481223067Sgshapiro
482223067Sgshapiro
48390795Sgshapiro/*
48438032Speter**  NMESSAGE -- print message (not necessarily an error)
48538032Speter**
48638032Speter**	Just like "message" except it never puts the to... tag on.
48738032Speter**
48838032Speter**	Parameters:
48990795Sgshapiro**		msg -- the message (sm_io_printf fmt) -- if it begins
49038032Speter**			with a three digit SMTP reply code, that is used,
49138032Speter**			otherwise 050 is assumed.
49290795Sgshapiro**		(others) -- sm_io_printf arguments
49338032Speter**
49438032Speter**	Returns:
49538032Speter**		none
49638032Speter**
49738032Speter**	Side Effects:
49838032Speter**		none.
49938032Speter*/
50038032Speter
50138032Speter/*VARARGS1*/
50238032Spetervoid
50338032Speter#ifdef __STDC__
50438032Speternmessage(const char *msg, ...)
50564565Sgshapiro#else /* __STDC__ */
50638032Speternmessage(msg, va_alist)
50738032Speter	const char *msg;
50838032Speter	va_dcl
50964565Sgshapiro#endif /* __STDC__ */
51038032Speter{
51164565Sgshapiro	char *errtxt;
51290795Sgshapiro	SM_VA_LOCAL_DECL
51338032Speter
51438032Speter	errno = 0;
51590795Sgshapiro	SM_VA_START(ap, msg);
51664565Sgshapiro	errtxt = fmtmsg(MsgBuf, (char *) NULL, "050",
51764565Sgshapiro			(char *) NULL, 0, msg, ap);
51890795Sgshapiro	SM_VA_END(ap);
51990795Sgshapiro	putoutmsg(MsgBuf, false, false);
52038032Speter
52138032Speter	/* save this message for mailq printing */
52238032Speter	switch (MsgBuf[0])
52338032Speter	{
52438032Speter	  case '4':
52538032Speter	  case '8':
52638032Speter		if (CurEnv->e_message != NULL)
52738032Speter			break;
52864565Sgshapiro		/* FALLTHROUGH */
52938032Speter
53038032Speter	  case '5':
53190795Sgshapiro		if (CurEnv->e_rpool == NULL && CurEnv->e_message != NULL)
53277352Sgshapiro			sm_free(CurEnv->e_message);
533168520Sgshapiro		CurEnv->e_message = sm_rpool_strdup_x(CurEnv->e_rpool, errtxt);
53438032Speter		break;
53538032Speter	}
53638032Speter}
53790795Sgshapiro/*
53838032Speter**  PUTOUTMSG -- output error message to transcript and channel
53938032Speter**
54038032Speter**	Parameters:
54138032Speter**		msg -- message to output (in SMTP format).
54290795Sgshapiro**		holdmsg -- if true, don't output a copy of the message to
54338032Speter**			our output channel.
54490795Sgshapiro**		heldmsg -- if true, this is a previously held message;
54538032Speter**			don't log it to the transcript file.
54638032Speter**
54738032Speter**	Returns:
54838032Speter**		none.
54938032Speter**
55038032Speter**	Side Effects:
55138032Speter**		Outputs msg to the transcript.
55238032Speter**		If appropriate, outputs it to the channel.
55338032Speter**		Deletes SMTP reply code number as appropriate.
55438032Speter*/
55538032Speter
55664565Sgshapirostatic void
55738032Speterputoutmsg(msg, holdmsg, heldmsg)
55838032Speter	char *msg;
55938032Speter	bool holdmsg;
56038032Speter	bool heldmsg;
56138032Speter{
562168520Sgshapiro	char msgcode = msg[0];
56364565Sgshapiro	char *errtxt = msg;
564168520Sgshapiro	char *id;
56538032Speter
56638032Speter	/* display for debugging */
56738032Speter	if (tTd(54, 8))
56890795Sgshapiro		sm_dprintf("--- %s%s%s\n", msg, holdmsg ? " (hold)" : "",
56938032Speter			heldmsg ? " (held)" : "");
57038032Speter
57138032Speter	/* map warnings to something SMTP can handle */
57238032Speter	if (msgcode == '6')
57338032Speter		msg[0] = '5';
57438032Speter	else if (msgcode == '8')
57538032Speter		msg[0] = '4';
576168520Sgshapiro	id = (CurEnv != NULL) ? CurEnv->e_id : NULL;
57738032Speter
57838032Speter	/* output to transcript if serious */
57938032Speter	if (!heldmsg && CurEnv != NULL && CurEnv->e_xfp != NULL &&
58038032Speter	    strchr("45", msg[0]) != NULL)
58190795Sgshapiro		(void) sm_io_fprintf(CurEnv->e_xfp, SM_TIME_DEFAULT, "%s\n",
58290795Sgshapiro				     msg);
58338032Speter
58490795Sgshapiro	if (LogLevel > 14 && (OpMode == MD_SMTP || OpMode == MD_DAEMON))
585168520Sgshapiro		sm_syslog(LOG_INFO, id,
58690795Sgshapiro			  "--- %s%s%s", msg, holdmsg ? " (hold)" : "",
58790795Sgshapiro			  heldmsg ? " (held)" : "");
58838032Speter
58938032Speter	if (msgcode == '8')
59038032Speter		msg[0] = '0';
59138032Speter
59238032Speter	/* output to channel if appropriate */
59338032Speter	if (!Verbose && msg[0] == '0')
59438032Speter		return;
59538032Speter	if (holdmsg)
59638032Speter	{
59738032Speter		/* save for possible future display */
59838032Speter		msg[0] = msgcode;
59964565Sgshapiro		if (HeldMessageBuf[0] == '5' && msgcode == '4')
60064565Sgshapiro			return;
601168520Sgshapiro		(void) sm_strlcpy(HeldMessageBuf, msg, sizeof(HeldMessageBuf));
60238032Speter		return;
60338032Speter	}
60438032Speter
60590795Sgshapiro	(void) sm_io_flush(smioout, SM_TIME_DEFAULT);
60638032Speter
60738032Speter	if (OutChannel == NULL)
60838032Speter		return;
60964565Sgshapiro
61064565Sgshapiro	/* find actual text of error (after SMTP status codes) */
61164565Sgshapiro	if (ISSMTPREPLY(errtxt))
61264565Sgshapiro	{
61364565Sgshapiro		int l;
61464565Sgshapiro
61564565Sgshapiro		errtxt += 4;
61664565Sgshapiro		l = isenhsc(errtxt, ' ');
61764565Sgshapiro		if (l <= 0)
61864565Sgshapiro			l = isenhsc(errtxt, '\0');
61964565Sgshapiro		if (l > 0)
62064565Sgshapiro			errtxt += l + 1;
62164565Sgshapiro	}
62264565Sgshapiro
62338032Speter	/* if DisConnected, OutChannel now points to the transcript */
62438032Speter	if (!DisConnected &&
62538032Speter	    (OpMode == MD_SMTP || OpMode == MD_DAEMON || OpMode == MD_ARPAFTP))
62690795Sgshapiro		(void) sm_io_fprintf(OutChannel, SM_TIME_DEFAULT, "%s\r\n",
62790795Sgshapiro				     msg);
62838032Speter	else
62990795Sgshapiro		(void) sm_io_fprintf(OutChannel, SM_TIME_DEFAULT, "%s\n",
63090795Sgshapiro				     errtxt);
63138032Speter	if (TrafficLogFile != NULL)
63290795Sgshapiro		(void) sm_io_fprintf(TrafficLogFile, SM_TIME_DEFAULT,
63390795Sgshapiro				     "%05d >>> %s\n", (int) CurrentPid,
63490795Sgshapiro				     (OpMode == MD_SMTP || OpMode == MD_DAEMON)
63590795Sgshapiro					? msg : errtxt);
63690795Sgshapiro#if !PIPELINING
63790795Sgshapiro	/* XXX can't flush here for SMTP pipelining */
63838032Speter	if (msg[3] == ' ')
63990795Sgshapiro		(void) sm_io_flush(OutChannel, SM_TIME_DEFAULT);
64090795Sgshapiro	if (!sm_io_error(OutChannel) || DisConnected)
64138032Speter		return;
64238032Speter
64338032Speter	/*
64438032Speter	**  Error on output -- if reporting lost channel, just ignore it.
64538032Speter	**  Also, ignore errors from QUIT response (221 message) -- some
64638032Speter	**	rude servers don't read result.
64738032Speter	*/
64838032Speter
64990795Sgshapiro	if (InChannel == NULL || sm_io_eof(InChannel) ||
65090795Sgshapiro	    sm_io_error(InChannel) || strncmp(msg, "221", 3) == 0)
65138032Speter		return;
65238032Speter
65338032Speter	/* can't call syserr, 'cause we are using MsgBuf */
65490795Sgshapiro	HoldErrs = true;
65538032Speter	if (LogLevel > 0)
656168520Sgshapiro		sm_syslog(LOG_CRIT, id,
65764565Sgshapiro			  "SYSERR: putoutmsg (%s): error on output channel sending \"%s\": %s",
65890795Sgshapiro			  CURHOSTNAME,
65990795Sgshapiro			  shortenstring(msg, MAXSHORTSTR), sm_errstring(errno));
66090795Sgshapiro#endif /* !PIPELINING */
66138032Speter}
66290795Sgshapiro/*
66338032Speter**  PUTERRMSG -- like putoutmsg, but does special processing for error messages
66438032Speter**
66538032Speter**	Parameters:
66638032Speter**		msg -- the message to output.
66738032Speter**
66838032Speter**	Returns:
66938032Speter**		none.
67038032Speter**
67138032Speter**	Side Effects:
67238032Speter**		Sets the fatal error bit in the envelope as appropriate.
67338032Speter*/
67438032Speter
67564565Sgshapirostatic void
67638032Speterputerrmsg(msg)
67738032Speter	char *msg;
67838032Speter{
67938032Speter	char msgcode = msg[0];
68038032Speter
68138032Speter	/* output the message as usual */
68290795Sgshapiro	putoutmsg(msg, HoldErrs, false);
68338032Speter
68438032Speter	/* be careful about multiple error messages */
68538032Speter	if (OnlyOneError)
68690795Sgshapiro		HoldErrs = true;
68738032Speter
68838032Speter	/* signal the error */
68938032Speter	Errors++;
69038032Speter
69138032Speter	if (CurEnv == NULL)
69238032Speter		return;
69364565Sgshapiro
69438032Speter	if (msgcode == '6')
69538032Speter	{
69638032Speter		/* notify the postmaster */
69738032Speter		CurEnv->e_flags |= EF_PM_NOTIFY;
69838032Speter	}
69938032Speter	else if (msgcode == '5' && bitset(EF_GLOBALERRS, CurEnv->e_flags))
70038032Speter	{
70138032Speter		/* mark long-term fatal errors */
70238032Speter		CurEnv->e_flags |= EF_FATALERRS;
70338032Speter	}
70438032Speter}
70590795Sgshapiro/*
70664565Sgshapiro**  ISENHSC -- check whether a string contains an enhanced status code
70764565Sgshapiro**
70864565Sgshapiro**	Parameters:
70964565Sgshapiro**		s -- string with possible enhanced status code.
71064565Sgshapiro**		delim -- delim for enhanced status code.
71164565Sgshapiro**
71264565Sgshapiro**	Returns:
71364565Sgshapiro**		0  -- no enhanced status code.
71464565Sgshapiro**		>4 -- length of enhanced status code.
71564565Sgshapiro**
71664565Sgshapiro**	Side Effects:
71764565Sgshapiro**		none.
71864565Sgshapiro*/
71964565Sgshapiroint
72064565Sgshapiroisenhsc(s, delim)
72164565Sgshapiro	const char *s;
72264565Sgshapiro	int delim;
72364565Sgshapiro{
72464565Sgshapiro	int l, h;
72564565Sgshapiro
72664565Sgshapiro	if (s == NULL)
72764565Sgshapiro		return 0;
72864565Sgshapiro	if (!((*s == '2' || *s == '4' || *s == '5') && s[1] == '.'))
72964565Sgshapiro		return 0;
73064565Sgshapiro	h = 0;
73164565Sgshapiro	l = 2;
73264565Sgshapiro	while (h < 3 && isascii(s[l + h]) && isdigit(s[l + h]))
73364565Sgshapiro		++h;
73464565Sgshapiro	if (h == 0 || s[l + h] != '.')
73564565Sgshapiro		return 0;
73664565Sgshapiro	l += h + 1;
73764565Sgshapiro	h = 0;
73864565Sgshapiro	while (h < 3 && isascii(s[l + h]) && isdigit(s[l + h]))
73964565Sgshapiro		++h;
74064565Sgshapiro	if (h == 0 || s[l + h] != delim)
74164565Sgshapiro		return 0;
74264565Sgshapiro	return l + h;
74364565Sgshapiro}
74490795Sgshapiro/*
74564565Sgshapiro**  EXTENHSC -- check and extract an enhanced status code
74664565Sgshapiro**
74764565Sgshapiro**	Parameters:
74864565Sgshapiro**		s -- string with possible enhanced status code.
74964565Sgshapiro**		delim -- delim for enhanced status code.
75064565Sgshapiro**		e -- pointer to storage for enhanced status code.
75164565Sgshapiro**			must be != NULL and have space for at least
75264565Sgshapiro**			10 characters ([245].[0-9]{1,3}.[0-9]{1,3})
75364565Sgshapiro**
75464565Sgshapiro**	Returns:
75564565Sgshapiro**		0  -- no enhanced status code.
75664565Sgshapiro**		>4 -- length of enhanced status code.
75764565Sgshapiro**
75864565Sgshapiro**	Side Effects:
75964565Sgshapiro**		fills e with enhanced status code.
76064565Sgshapiro*/
76190795Sgshapiro
76264565Sgshapiroint
76364565Sgshapiroextenhsc(s, delim, e)
76464565Sgshapiro	const char *s;
76564565Sgshapiro	int delim;
76664565Sgshapiro	char *e;
76764565Sgshapiro{
76864565Sgshapiro	int l, h;
76964565Sgshapiro
77064565Sgshapiro	if (s == NULL)
77164565Sgshapiro		return 0;
77264565Sgshapiro	if (!((*s == '2' || *s == '4' || *s == '5') && s[1] == '.'))
77364565Sgshapiro		return 0;
77464565Sgshapiro	h = 0;
77564565Sgshapiro	l = 2;
77664565Sgshapiro	e[0] = s[0];
77764565Sgshapiro	e[1] = '.';
77864565Sgshapiro	while (h < 3 && isascii(s[l + h]) && isdigit(s[l + h]))
77964565Sgshapiro	{
78064565Sgshapiro		e[l + h] = s[l + h];
78164565Sgshapiro		++h;
78264565Sgshapiro	}
78364565Sgshapiro	if (h == 0 || s[l + h] != '.')
78464565Sgshapiro		return 0;
78564565Sgshapiro	e[l + h] = '.';
78664565Sgshapiro	l += h + 1;
78764565Sgshapiro	h = 0;
78864565Sgshapiro	while (h < 3 && isascii(s[l + h]) && isdigit(s[l + h]))
78964565Sgshapiro	{
79064565Sgshapiro		e[l + h] = s[l + h];
79164565Sgshapiro		++h;
79264565Sgshapiro	}
79364565Sgshapiro	if (h == 0 || s[l + h] != delim)
79464565Sgshapiro		return 0;
79564565Sgshapiro	e[l + h] = '\0';
79664565Sgshapiro	return l + h;
79764565Sgshapiro}
79890795Sgshapiro/*
79938032Speter**  FMTMSG -- format a message into buffer.
80038032Speter**
80138032Speter**	Parameters:
80264565Sgshapiro**		eb -- error buffer to get result -- MUST BE MsgBuf.
80338032Speter**		to -- the recipient tag for this message.
80464565Sgshapiro**		num -- default three digit SMTP reply code.
80564565Sgshapiro**		enhsc -- enhanced status code.
80638032Speter**		en -- the error number to display.
80738032Speter**		fmt -- format of string.
80864565Sgshapiro**		ap -- arguments for fmt.
80938032Speter**
81038032Speter**	Returns:
81164565Sgshapiro**		pointer to error text beyond status codes.
81238032Speter**
81338032Speter**	Side Effects:
81438032Speter**		none.
81538032Speter*/
81638032Speter
81764565Sgshapirostatic char *
81864565Sgshapirofmtmsg(eb, to, num, enhsc, eno, fmt, ap)
81938032Speter	register char *eb;
82038032Speter	const char *to;
82138032Speter	const char *num;
82264565Sgshapiro	const char *enhsc;
82338032Speter	int eno;
82438032Speter	const char *fmt;
82590795Sgshapiro	SM_VA_LOCAL_DECL
82638032Speter{
82738032Speter	char del;
82838032Speter	int l;
829168520Sgshapiro	int spaceleft = sizeof(MsgBuf);
83064565Sgshapiro	char *errtxt;
83138032Speter
83238032Speter	/* output the reply code */
83364565Sgshapiro	if (ISSMTPCODE(fmt))
83438032Speter	{
83538032Speter		num = fmt;
83638032Speter		fmt += 4;
83738032Speter	}
83838032Speter	if (num[3] == '-')
83938032Speter		del = '-';
84038032Speter	else
84138032Speter		del = ' ';
84290795Sgshapiro	if (SoftBounce && num[0] == '5')
84390795Sgshapiro	{
84490795Sgshapiro		/* replace 5 by 4 */
84590795Sgshapiro		(void) sm_snprintf(eb, spaceleft, "4%2.2s%c", num + 1, del);
84690795Sgshapiro	}
84790795Sgshapiro	else
848168520Sgshapiro		(void) sm_snprintf(eb, spaceleft, "%3.3s%c", num, del);
84938032Speter	eb += 4;
85038032Speter	spaceleft -= 4;
85138032Speter
85264565Sgshapiro	if ((l = isenhsc(fmt, ' ' )) > 0 && l < spaceleft - 4)
85364565Sgshapiro	{
85464565Sgshapiro		/* copy enh.status code including trailing blank */
85564565Sgshapiro		l++;
85690795Sgshapiro		(void) sm_strlcpy(eb, fmt, l + 1);
85764565Sgshapiro		eb += l;
85864565Sgshapiro		spaceleft -= l;
85964565Sgshapiro		fmt += l;
86064565Sgshapiro	}
86164565Sgshapiro	else if ((l = isenhsc(enhsc, '\0')) > 0 && l < spaceleft - 4)
86264565Sgshapiro	{
86364565Sgshapiro		/* copy enh.status code */
86490795Sgshapiro		(void) sm_strlcpy(eb, enhsc, l + 1);
86564565Sgshapiro		eb[l] = ' ';
86664565Sgshapiro		eb[++l] = '\0';
86764565Sgshapiro		eb += l;
86864565Sgshapiro		spaceleft -= l;
86964565Sgshapiro	}
87090795Sgshapiro	if (SoftBounce && eb[-l] == '5')
87190795Sgshapiro	{
87290795Sgshapiro		/* replace 5 by 4 */
87390795Sgshapiro		eb[-l] = '4';
87490795Sgshapiro	}
87564565Sgshapiro	errtxt = eb;
87664565Sgshapiro
87738032Speter	/* output the file name and line number */
87838032Speter	if (FileName != NULL)
87938032Speter	{
88090795Sgshapiro		(void) sm_snprintf(eb, spaceleft, "%s: line %d: ",
88190795Sgshapiro				   shortenstring(FileName, 83), LineNumber);
88238032Speter		eb += (l = strlen(eb));
88338032Speter		spaceleft -= l;
88438032Speter	}
88538032Speter
88682020Sgshapiro	/*
88782020Sgshapiro	**  output the "to" address only if it is defined and one of the
88882020Sgshapiro	**  following codes is used:
88982020Sgshapiro	**  050 internal notices, e.g., alias expansion
89082020Sgshapiro	**  250 Ok
89182020Sgshapiro	**  252 Cannot VRFY user, but will accept message and attempt delivery
89282020Sgshapiro	**  450 Requested mail action not taken: mailbox unavailable
89382020Sgshapiro	**  550 Requested action not taken: mailbox unavailable
89482020Sgshapiro	**  553 Requested action not taken: mailbox name not allowed
89582020Sgshapiro	**
89682020Sgshapiro	**  Notice: this still isn't "the right thing", this code shouldn't
89782020Sgshapiro	**	(indirectly) depend on CurEnv->e_to.
89882020Sgshapiro	*/
89982020Sgshapiro
90038032Speter	if (to != NULL && to[0] != '\0' &&
90182020Sgshapiro	    (strncmp(num, "050", 3) == 0 ||
90282020Sgshapiro	     strncmp(num, "250", 3) == 0 ||
90382020Sgshapiro	     strncmp(num, "252", 3) == 0 ||
90482020Sgshapiro	     strncmp(num, "450", 3) == 0 ||
90582020Sgshapiro	     strncmp(num, "550", 3) == 0 ||
90682020Sgshapiro	     strncmp(num, "553", 3) == 0))
90738032Speter	{
90890795Sgshapiro		(void) sm_strlcpyn(eb, spaceleft, 2,
90990795Sgshapiro				   shortenstring(to, MAXSHORTSTR), "... ");
91038032Speter		spaceleft -= strlen(eb);
91138032Speter		while (*eb != '\0')
91238032Speter			*eb++ &= 0177;
91338032Speter	}
91438032Speter
91538032Speter	/* output the message */
91690795Sgshapiro	(void) sm_vsnprintf(eb, spaceleft, fmt, ap);
91738032Speter	spaceleft -= strlen(eb);
91838032Speter	while (*eb != '\0')
91938032Speter		*eb++ &= 0177;
92038032Speter
92138032Speter	/* output the error code, if any */
92238032Speter	if (eno != 0)
92390795Sgshapiro		(void) sm_strlcpyn(eb, spaceleft, 2, ": ", sm_errstring(eno));
92464565Sgshapiro
92564565Sgshapiro	return errtxt;
92638032Speter}
92790795Sgshapiro/*
92838032Speter**  BUFFER_ERRORS -- arrange to buffer future error messages
92938032Speter**
93038032Speter**	Parameters:
93138032Speter**		none
93238032Speter**
93338032Speter**	Returns:
93438032Speter**		none.
93538032Speter*/
93638032Speter
93738032Spetervoid
93838032Speterbuffer_errors()
93938032Speter{
94038032Speter	HeldMessageBuf[0] = '\0';
94190795Sgshapiro	HoldErrs = true;
94238032Speter}
94390795Sgshapiro/*
94438032Speter**  FLUSH_ERRORS -- flush the held error message buffer
94538032Speter**
94638032Speter**	Parameters:
94738032Speter**		print -- if set, print the message, otherwise just
94838032Speter**			delete it.
94938032Speter**
95038032Speter**	Returns:
95138032Speter**		none.
95238032Speter*/
95338032Speter
95438032Spetervoid
95538032Speterflush_errors(print)
95638032Speter	bool print;
95738032Speter{
95838032Speter	if (print && HeldMessageBuf[0] != '\0')
95990795Sgshapiro		putoutmsg(HeldMessageBuf, false, true);
96038032Speter	HeldMessageBuf[0] = '\0';
96190795Sgshapiro	HoldErrs = false;
96238032Speter}
96390795Sgshapiro/*
96490795Sgshapiro**  SM_ERRSTRING -- return string description of error code
96538032Speter**
96638032Speter**	Parameters:
96738032Speter**		errnum -- the error number to translate
96838032Speter**
96938032Speter**	Returns:
97038032Speter**		A string description of errnum.
97138032Speter**
97238032Speter**	Side Effects:
97338032Speter**		none.
97438032Speter*/
97538032Speter
97638032Speterconst char *
97790795Sgshapirosm_errstring(errnum)
97838032Speter	int errnum;
97938032Speter{
98038032Speter	char *dnsmsg;
98138032Speter	char *bp;
98238032Speter	static char buf[MAXLINE];
98390795Sgshapiro#if HASSTRERROR
98490795Sgshapiro	char *err;
98590795Sgshapiro	char errbuf[30];
98690795Sgshapiro#endif /* HASSTRERROR */
98764565Sgshapiro#if !HASSTRERROR && !defined(ERRLIST_PREDEFINED)
98838032Speter	extern char *sys_errlist[];
98938032Speter	extern int sys_nerr;
99064565Sgshapiro#endif /* !HASSTRERROR && !defined(ERRLIST_PREDEFINED) */
99138032Speter
99238032Speter	/*
99338032Speter	**  Handle special network error codes.
99438032Speter	**
99538032Speter	**	These are 4.2/4.3bsd specific; they should be in daemon.c.
99638032Speter	*/
99738032Speter
99838032Speter	dnsmsg = NULL;
99938032Speter	switch (errnum)
100038032Speter	{
100138032Speter	  case ETIMEDOUT:
100238032Speter	  case ECONNRESET:
100338032Speter		bp = buf;
100490795Sgshapiro#if HASSTRERROR
100590795Sgshapiro		err = strerror(errnum);
100690795Sgshapiro		if (err == NULL)
100790795Sgshapiro		{
1008168520Sgshapiro			(void) sm_snprintf(errbuf, sizeof(errbuf),
100990795Sgshapiro					   "Error %d", errnum);
101090795Sgshapiro			err = errbuf;
101190795Sgshapiro		}
101290795Sgshapiro		(void) sm_strlcpy(bp, err, SPACELEFT(buf, bp));
101390795Sgshapiro#else /* HASSTRERROR */
101438032Speter		if (errnum >= 0 && errnum < sys_nerr)
101590795Sgshapiro			(void) sm_strlcpy(bp, sys_errlist[errnum],
101690795Sgshapiro					  SPACELEFT(buf, bp));
101738032Speter		else
101890795Sgshapiro			(void) sm_snprintf(bp, SPACELEFT(buf, bp),
101990795Sgshapiro				"Error %d", errnum);
102090795Sgshapiro#endif /* HASSTRERROR */
102138032Speter		bp += strlen(bp);
102238032Speter		if (CurHostName != NULL)
102338032Speter		{
102438032Speter			if (errnum == ETIMEDOUT)
102538032Speter			{
102690795Sgshapiro				(void) sm_snprintf(bp, SPACELEFT(buf, bp),
102790795Sgshapiro					" with ");
102838032Speter				bp += strlen(bp);
102938032Speter			}
103038032Speter			else
103138032Speter			{
103238032Speter				bp = buf;
103390795Sgshapiro				(void) sm_snprintf(bp, SPACELEFT(buf, bp),
103438032Speter					"Connection reset by ");
103538032Speter				bp += strlen(bp);
103638032Speter			}
103790795Sgshapiro			(void) sm_strlcpy(bp,
103890795Sgshapiro					shortenstring(CurHostName, MAXSHORTSTR),
103990795Sgshapiro					SPACELEFT(buf, bp));
104038032Speter			bp += strlen(buf);
104138032Speter		}
104238032Speter		if (SmtpPhase != NULL)
104338032Speter		{
104490795Sgshapiro			(void) sm_snprintf(bp, SPACELEFT(buf, bp),
104590795Sgshapiro				" during %s", SmtpPhase);
104638032Speter		}
104764565Sgshapiro		return buf;
104838032Speter
104938032Speter	  case EHOSTDOWN:
105038032Speter		if (CurHostName == NULL)
105138032Speter			break;
1052168520Sgshapiro		(void) sm_snprintf(buf, sizeof(buf), "Host %s is down",
105338032Speter			shortenstring(CurHostName, MAXSHORTSTR));
105464565Sgshapiro		return buf;
105538032Speter
105638032Speter	  case ECONNREFUSED:
105738032Speter		if (CurHostName == NULL)
105838032Speter			break;
1059168520Sgshapiro		(void) sm_strlcpyn(buf, sizeof(buf), 2, "Connection refused by ",
106038032Speter			shortenstring(CurHostName, MAXSHORTSTR));
106164565Sgshapiro		return buf;
106238032Speter
106364565Sgshapiro#if NAMED_BIND
106438032Speter	  case HOST_NOT_FOUND + E_DNSBASE:
106538032Speter		dnsmsg = "host not found";
106638032Speter		break;
106738032Speter
106838032Speter	  case TRY_AGAIN + E_DNSBASE:
106938032Speter		dnsmsg = "host name lookup failure";
107038032Speter		break;
107138032Speter
107238032Speter	  case NO_RECOVERY + E_DNSBASE:
107338032Speter		dnsmsg = "non-recoverable error";
107438032Speter		break;
107538032Speter
107638032Speter	  case NO_DATA + E_DNSBASE:
107738032Speter		dnsmsg = "no data known";
107838032Speter		break;
107964565Sgshapiro#endif /* NAMED_BIND */
108038032Speter
108138032Speter	  case EPERM:
108238032Speter		/* SunOS gives "Not owner" -- this is the POSIX message */
108338032Speter		return "Operation not permitted";
108438032Speter
108538032Speter	/*
108638032Speter	**  Error messages used internally in sendmail.
108738032Speter	*/
108838032Speter
108938032Speter	  case E_SM_OPENTIMEOUT:
109038032Speter		return "Timeout on file open";
109138032Speter
109238032Speter	  case E_SM_NOSLINK:
109338032Speter		return "Symbolic links not allowed";
109438032Speter
109538032Speter	  case E_SM_NOHLINK:
109638032Speter		return "Hard links not allowed";
109738032Speter
109838032Speter	  case E_SM_REGONLY:
109938032Speter		return "Regular files only";
110038032Speter
110138032Speter	  case E_SM_ISEXEC:
110238032Speter		return "Executable files not allowed";
110338032Speter
110438032Speter	  case E_SM_WWDIR:
110538032Speter		return "World writable directory";
110638032Speter
110738032Speter	  case E_SM_GWDIR:
110838032Speter		return "Group writable directory";
110938032Speter
111038032Speter	  case E_SM_FILECHANGE:
111138032Speter		return "File changed after open";
111238032Speter
111338032Speter	  case E_SM_WWFILE:
111438032Speter		return "World writable file";
111538032Speter
111638032Speter	  case E_SM_GWFILE:
111738032Speter		return "Group writable file";
111864565Sgshapiro
111964565Sgshapiro	  case E_SM_GRFILE:
112064565Sgshapiro		return "Group readable file";
112164565Sgshapiro
112264565Sgshapiro	  case E_SM_WRFILE:
112364565Sgshapiro		return "World readable file";
112438032Speter	}
112538032Speter
112638032Speter	if (dnsmsg != NULL)
112738032Speter	{
112838032Speter		bp = buf;
1129168520Sgshapiro		bp += sm_strlcpy(bp, "Name server: ", sizeof(buf));
113038032Speter		if (CurHostName != NULL)
113138032Speter		{
113290795Sgshapiro			(void) sm_strlcpyn(bp, SPACELEFT(buf, bp), 2,
113390795Sgshapiro				shortenstring(CurHostName, MAXSHORTSTR), ": ");
113438032Speter			bp += strlen(bp);
113538032Speter		}
113690795Sgshapiro		(void) sm_strlcpy(bp, dnsmsg, SPACELEFT(buf, bp));
113738032Speter		return buf;
113838032Speter	}
113938032Speter
114090795Sgshapiro#if LDAPMAP
114164565Sgshapiro	if (errnum >= E_LDAPBASE)
114264565Sgshapiro		return ldap_err2string(errnum - E_LDAPBASE);
114364565Sgshapiro#endif /* LDAPMAP */
114464565Sgshapiro
114538032Speter#if HASSTRERROR
114690795Sgshapiro	err = strerror(errnum);
114790795Sgshapiro	if (err == NULL)
114890795Sgshapiro	{
1149168520Sgshapiro		(void) sm_snprintf(buf, sizeof(buf), "Error %d", errnum);
115090795Sgshapiro		return buf;
115190795Sgshapiro	}
115290795Sgshapiro	return err;
115364565Sgshapiro#else /* HASSTRERROR */
115438032Speter	if (errnum > 0 && errnum < sys_nerr)
115564565Sgshapiro		return sys_errlist[errnum];
115638032Speter
1157168520Sgshapiro	(void) sm_snprintf(buf, sizeof(buf), "Error %d", errnum);
115864565Sgshapiro	return buf;
115964565Sgshapiro#endif /* HASSTRERROR */
116038032Speter}
1161