138032Speter/*
2363466Sgshapiro * Copyright (c) 1998-2003, 2010, 2015 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
16266527SgshapiroSM_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 */
21363466Sgshapiro#endif
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:
97285229Sgshapiro**		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
116363466Sgshapiro#endif
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;
130285229Sgshapiro	bool panic, exiting, keep;
13164565Sgshapiro	char *user;
13264565Sgshapiro	char *enhsc;
13364565Sgshapiro	char *errtxt;
13438032Speter	struct passwd *pw;
13538032Speter	char ubuf[80];
13690795Sgshapiro	SM_VA_LOCAL_DECL
13738032Speter
138285229Sgshapiro	panic = exiting = keep = false;
13990795Sgshapiro	switch (*fmt)
14038032Speter	{
14190795Sgshapiro	  case '!':
14290795Sgshapiro		++fmt;
143285229Sgshapiro		panic = exiting = true;
14490795Sgshapiro		break;
14590795Sgshapiro	  case '@':
14690795Sgshapiro		++fmt;
14790795Sgshapiro		exiting = true;
14890795Sgshapiro		break;
149285229Sgshapiro	  case '+':
150285229Sgshapiro		++fmt;
151285229Sgshapiro		keep = true;
152285229Sgshapiro		break;
15390795Sgshapiro	  default:
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 */
185285229Sgshapiro	if (!panic && CurEnv != NULL && (!keep || CurEnv->e_message == 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:
227363466Sgshapiro#endif
22838032Speter#ifdef ESPIPE
22938032Speter	  case ESPIPE:
230363466Sgshapiro#endif
23138032Speter#ifdef EPIPE
23238032Speter	  case EPIPE:
233363466Sgshapiro#endif
23438032Speter#ifdef ENOBUFS
23538032Speter	  case ENOBUFS:
236363466Sgshapiro#endif
23738032Speter#ifdef ESTALE
23838032Speter	  case ESTALE:
239363466Sgshapiro#endif
24090795Sgshapiro		printopenfds(true);
241132946Sgshapiro		mci_dump_all(smioout, true);
24238032Speter		break;
24338032Speter	}
24438032Speter	if (panic)
24538032Speter	{
24690795Sgshapiro#if XLA
24738032Speter		xla_all_end();
248363466Sgshapiro#endif
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}
258363466Sgshapiro
25990795Sgshapiro/*
26038032Speter**  USRERR -- Signal user error.
26138032Speter**
26238032Speter**	This is much like syserr except it is for user errors.
26338032Speter**
26438032Speter**	Parameters:
26538032Speter**		fmt -- the format string.  If it does not begin with
26690795Sgshapiro**			a three-digit SMTP reply code, 550 is assumed.
26790795Sgshapiro**		(others) -- sm_io_printf strings
26838032Speter**
26938032Speter**	Returns:
27038032Speter**		none
27190795Sgshapiro**		Raises E:mta.quickabort if QuickAbort is set.
27238032Speter**
27338032Speter**	Side Effects:
27438032Speter**		increments Errors.
27538032Speter*/
27638032Speter
27738032Speter/*VARARGS1*/
27838032Spetervoid
27938032Speter#ifdef __STDC__
28038032Speterusrerr(const char *fmt, ...)
28164565Sgshapiro#else /* __STDC__ */
28238032Speterusrerr(fmt, va_alist)
28338032Speter	const char *fmt;
28438032Speter	va_dcl
28564565Sgshapiro#endif /* __STDC__ */
28638032Speter{
28764565Sgshapiro	char *enhsc;
28864565Sgshapiro	char *errtxt;
28990795Sgshapiro	SM_VA_LOCAL_DECL
29038032Speter
29164565Sgshapiro	if (fmt[0] == '5' || fmt[0] == '6')
29264565Sgshapiro		enhsc = "5.0.0";
29364565Sgshapiro	else if (fmt[0] == '4' || fmt[0] == '8')
29464565Sgshapiro		enhsc = "4.0.0";
29564565Sgshapiro	else if (fmt[0] == '2')
29664565Sgshapiro		enhsc = "2.0.0";
29764565Sgshapiro	else
29864565Sgshapiro		enhsc = NULL;
29990795Sgshapiro	SM_VA_START(ap, fmt);
30090795Sgshapiro	errtxt = fmtmsg(MsgBuf, CurEnv->e_to, "550", enhsc, 0, fmt, ap);
30190795Sgshapiro	SM_VA_END(ap);
30264565Sgshapiro
30338032Speter	if (SuprErrs)
30438032Speter		return;
30538032Speter
30664565Sgshapiro	/* save this message for mailq printing */
30764565Sgshapiro	switch (MsgBuf[0])
30864565Sgshapiro	{
30964565Sgshapiro	  case '4':
31064565Sgshapiro	  case '8':
31164565Sgshapiro		if (CurEnv->e_message != NULL)
31264565Sgshapiro			break;
31364565Sgshapiro
31464565Sgshapiro		/* FALLTHROUGH */
31564565Sgshapiro
31664565Sgshapiro	  case '5':
31764565Sgshapiro	  case '6':
31890795Sgshapiro		if (CurEnv->e_rpool == NULL && CurEnv->e_message != NULL)
31977352Sgshapiro			sm_free(CurEnv->e_message);
32064565Sgshapiro		if (MsgBuf[0] == '6')
32164565Sgshapiro		{
32264565Sgshapiro			char buf[MAXLINE];
32364565Sgshapiro
324168520Sgshapiro			(void) sm_snprintf(buf, sizeof(buf),
32590795Sgshapiro					   "Postmaster warning: %.*s",
326168520Sgshapiro					   (int) sizeof(buf) - 22, errtxt);
32790795Sgshapiro			CurEnv->e_message =
32890795Sgshapiro				sm_rpool_strdup_x(CurEnv->e_rpool, buf);
32964565Sgshapiro		}
33064565Sgshapiro		else
33164565Sgshapiro		{
33290795Sgshapiro			CurEnv->e_message =
33390795Sgshapiro				sm_rpool_strdup_x(CurEnv->e_rpool, errtxt);
33464565Sgshapiro		}
33564565Sgshapiro		break;
33664565Sgshapiro	}
33764565Sgshapiro
33864565Sgshapiro	puterrmsg(MsgBuf);
33964565Sgshapiro	if (LogLevel > 3 && LogUsrErrs)
34064565Sgshapiro		sm_syslog(LOG_NOTICE, CurEnv->e_id, "%.900s", errtxt);
34164565Sgshapiro	if (QuickAbort)
34290795Sgshapiro		sm_exc_raisenew_x(&EtypeQuickAbort, 1);
34364565Sgshapiro}
344363466Sgshapiro
34590795Sgshapiro/*
34664565Sgshapiro**  USRERRENH -- Signal user error.
34764565Sgshapiro**
34864565Sgshapiro**	Same as usrerr but with enhanced status code.
34964565Sgshapiro**
35064565Sgshapiro**	Parameters:
35164565Sgshapiro**		enhsc -- the enhanced status code.
35264565Sgshapiro**		fmt -- the format string.  If it does not begin with
35390795Sgshapiro**			a three-digit SMTP reply code, 550 is assumed.
35490795Sgshapiro**		(others) -- sm_io_printf strings
35564565Sgshapiro**
35664565Sgshapiro**	Returns:
35764565Sgshapiro**		none
35890795Sgshapiro**		Raises E:mta.quickabort if QuickAbort is set.
35964565Sgshapiro**
36064565Sgshapiro**	Side Effects:
36164565Sgshapiro**		increments Errors.
36264565Sgshapiro*/
36364565Sgshapiro
364223067Sgshapiro/*VARARGS2*/
36564565Sgshapirovoid
36664565Sgshapiro#ifdef __STDC__
36764565Sgshapirousrerrenh(char *enhsc, const char *fmt, ...)
36864565Sgshapiro#else /* __STDC__ */
36964565Sgshapirousrerrenh(enhsc, fmt, va_alist)
37064565Sgshapiro	char *enhsc;
37164565Sgshapiro	const char *fmt;
37264565Sgshapiro	va_dcl
37364565Sgshapiro#endif /* __STDC__ */
37464565Sgshapiro{
37564565Sgshapiro	char *errtxt;
37690795Sgshapiro	SM_VA_LOCAL_DECL
37764565Sgshapiro
37864565Sgshapiro	if (enhsc == NULL || *enhsc == '\0')
37964565Sgshapiro	{
38064565Sgshapiro		if (fmt[0] == '5' || fmt[0] == '6')
38164565Sgshapiro			enhsc = "5.0.0";
38264565Sgshapiro		else if (fmt[0] == '4' || fmt[0] == '8')
38364565Sgshapiro			enhsc = "4.0.0";
38464565Sgshapiro		else if (fmt[0] == '2')
38564565Sgshapiro			enhsc = "2.0.0";
38664565Sgshapiro	}
38790795Sgshapiro	SM_VA_START(ap, fmt);
38890795Sgshapiro	errtxt = fmtmsg(MsgBuf, CurEnv->e_to, "550", enhsc, 0, fmt, ap);
38990795Sgshapiro	SM_VA_END(ap);
39038032Speter
39164565Sgshapiro	if (SuprErrs)
39264565Sgshapiro		return;
39364565Sgshapiro
39438032Speter	/* save this message for mailq printing */
39538032Speter	switch (MsgBuf[0])
39638032Speter	{
39738032Speter	  case '4':
39838032Speter	  case '8':
39938032Speter		if (CurEnv->e_message != NULL)
40038032Speter			break;
40138032Speter
40264565Sgshapiro		/* FALLTHROUGH */
40338032Speter
40438032Speter	  case '5':
40538032Speter	  case '6':
40690795Sgshapiro		if (CurEnv->e_rpool == NULL && CurEnv->e_message != NULL)
40777352Sgshapiro			sm_free(CurEnv->e_message);
40838032Speter		if (MsgBuf[0] == '6')
40938032Speter		{
41038032Speter			char buf[MAXLINE];
41138032Speter
412168520Sgshapiro			(void) sm_snprintf(buf, sizeof(buf),
41390795Sgshapiro					   "Postmaster warning: %.*s",
414168520Sgshapiro					   (int) sizeof(buf) - 22, errtxt);
41590795Sgshapiro			CurEnv->e_message =
41690795Sgshapiro				sm_rpool_strdup_x(CurEnv->e_rpool, buf);
41738032Speter		}
41838032Speter		else
41938032Speter		{
42090795Sgshapiro			CurEnv->e_message =
42190795Sgshapiro				sm_rpool_strdup_x(CurEnv->e_rpool, errtxt);
42238032Speter		}
42338032Speter		break;
42438032Speter	}
42538032Speter
42638032Speter	puterrmsg(MsgBuf);
42738032Speter	if (LogLevel > 3 && LogUsrErrs)
42864565Sgshapiro		sm_syslog(LOG_NOTICE, CurEnv->e_id, "%.900s", errtxt);
42938032Speter	if (QuickAbort)
43090795Sgshapiro		sm_exc_raisenew_x(&EtypeQuickAbort, 1);
43138032Speter}
432223067Sgshapiro
43390795Sgshapiro/*
43438032Speter**  MESSAGE -- print message (not necessarily an error)
43538032Speter**
43638032Speter**	Parameters:
43790795Sgshapiro**		msg -- the message (sm_io_printf fmt) -- it can begin with
43838032Speter**			an SMTP reply code.  If not, 050 is assumed.
43990795Sgshapiro**		(others) -- sm_io_printf arguments
44038032Speter**
44138032Speter**	Returns:
44238032Speter**		none
44338032Speter*/
44438032Speter
44538032Speter/*VARARGS1*/
44638032Spetervoid
44738032Speter#ifdef __STDC__
44838032Spetermessage(const char *msg, ...)
44964565Sgshapiro#else /* __STDC__ */
45038032Spetermessage(msg, va_alist)
45138032Speter	const char *msg;
45238032Speter	va_dcl
45364565Sgshapiro#endif /* __STDC__ */
45438032Speter{
45564565Sgshapiro	char *errtxt;
45690795Sgshapiro	SM_VA_LOCAL_DECL
45738032Speter
45838032Speter	errno = 0;
45990795Sgshapiro	SM_VA_START(ap, msg);
46064565Sgshapiro	errtxt = fmtmsg(MsgBuf, CurEnv->e_to, "050", (char *) NULL, 0, msg, ap);
46190795Sgshapiro	SM_VA_END(ap);
46290795Sgshapiro	putoutmsg(MsgBuf, false, false);
46338032Speter
46438032Speter	/* save this message for mailq printing */
46538032Speter	switch (MsgBuf[0])
46638032Speter	{
46738032Speter	  case '4':
46838032Speter	  case '8':
46938032Speter		if (CurEnv->e_message != NULL)
47038032Speter			break;
47164565Sgshapiro		/* FALLTHROUGH */
47238032Speter
47338032Speter	  case '5':
47490795Sgshapiro		if (CurEnv->e_rpool == NULL && CurEnv->e_message != NULL)
47577352Sgshapiro			sm_free(CurEnv->e_message);
476223067Sgshapiro		CurEnv->e_message = sm_rpool_strdup_x(CurEnv->e_rpool, errtxt);
47738032Speter		break;
47838032Speter	}
47938032Speter}
480223067Sgshapiro
481285229Sgshapiro#if _FFR_PROXY
482285229Sgshapiro/*
483285229Sgshapiro**  EMESSAGE -- print message (not necessarily an error)
484285229Sgshapiro**	(same as message() but requires reply code and enhanced status code)
485285229Sgshapiro**
486285229Sgshapiro**	Parameters:
487285229Sgshapiro**		replycode -- SMTP reply code.
488285229Sgshapiro**		enhsc -- enhanced status code.
489285229Sgshapiro**		msg -- the message (sm_io_printf fmt) -- it can begin with
490285229Sgshapiro**			an SMTP reply code.  If not, 050 is assumed.
491285229Sgshapiro**		(others) -- sm_io_printf arguments
492285229Sgshapiro**
493285229Sgshapiro**	Returns:
494285229Sgshapiro**		none
495285229Sgshapiro*/
496223067Sgshapiro
497285229Sgshapiro/*VARARGS3*/
498285229Sgshapirovoid
499285229Sgshapiro# ifdef __STDC__
500285229Sgshapiroemessage(const char *replycode, const char *enhsc, const char *msg, ...)
501285229Sgshapiro# else /* __STDC__ */
502285229Sgshapiroemessage(replycode, enhsc, msg, va_alist)
503285229Sgshapiro	const char *replycode;
504285229Sgshapiro	const char *enhsc;
505285229Sgshapiro	const char *msg;
506285229Sgshapiro	va_dcl
507285229Sgshapiro# endif /* __STDC__ */
508285229Sgshapiro{
509285229Sgshapiro	char *errtxt;
510285229Sgshapiro	SM_VA_LOCAL_DECL
511285229Sgshapiro
512285229Sgshapiro	errno = 0;
513285229Sgshapiro	SM_VA_START(ap, msg);
514285229Sgshapiro	errtxt = fmtmsg(MsgBuf, CurEnv->e_to, replycode, enhsc, 0, msg, ap);
515285229Sgshapiro	SM_VA_END(ap);
516285229Sgshapiro	putoutmsg(MsgBuf, false, false);
517285229Sgshapiro
518285229Sgshapiro	/* save this message for mailq printing */
519285229Sgshapiro	switch (MsgBuf[0])
520285229Sgshapiro	{
521285229Sgshapiro	  case '4':
522285229Sgshapiro	  case '8':
523285229Sgshapiro		if (CurEnv->e_message != NULL)
524285229Sgshapiro			break;
525285229Sgshapiro		/* FALLTHROUGH */
526285229Sgshapiro
527285229Sgshapiro	  case '5':
528285229Sgshapiro		if (CurEnv->e_rpool == NULL && CurEnv->e_message != NULL)
529285229Sgshapiro			sm_free(CurEnv->e_message);
530285229Sgshapiro		CurEnv->e_message = sm_rpool_strdup_x(CurEnv->e_rpool, errtxt);
531285229Sgshapiro		break;
532285229Sgshapiro	}
533285229Sgshapiro}
534285229Sgshapiro
53590795Sgshapiro/*
536285229Sgshapiro**  EXTSC -- check and extract a status codes
537285229Sgshapiro**
538285229Sgshapiro**	Parameters:
539285229Sgshapiro**		msg -- string with possible enhanced status code.
540285229Sgshapiro**		delim -- delim for enhanced status code.
541285229Sgshapiro**		replycode -- pointer to storage for SMTP reply code;
542285229Sgshapiro**			must be != NULL and have space for at least
543285229Sgshapiro**			4 characters.
544285229Sgshapiro**		enhsc -- pointer to storage for enhanced status code;
545285229Sgshapiro**			must be != NULL and have space for at least
546285229Sgshapiro**			10 characters ([245].[0-9]{1,3}.[0-9]{1,3})
547285229Sgshapiro**
548285229Sgshapiro**	Returns:
549285229Sgshapiro**		-1  -- no SMTP reply code.
550285229Sgshapiro**		>=3 -- offset of error text in msg.
551285229Sgshapiro**		(<=4  -- no enhanced status code)
552285229Sgshapiro*/
553285229Sgshapiro
554285229Sgshapiroint
555285229Sgshapiroextsc(msg, delim, replycode, enhsc)
556285229Sgshapiro	const char *msg;
557285229Sgshapiro	int delim;
558285229Sgshapiro	char *replycode;
559285229Sgshapiro	char *enhsc;
560285229Sgshapiro{
561285229Sgshapiro	int offset;
562285229Sgshapiro
563285229Sgshapiro	SM_REQUIRE(replycode != NULL);
564285229Sgshapiro	SM_REQUIRE(enhsc != NULL);
565285229Sgshapiro	replycode[0] = '\0';
566285229Sgshapiro	enhsc[0] = '\0';
567285229Sgshapiro	if (msg == NULL)
568285229Sgshapiro		return -1;
569285229Sgshapiro	if (!ISSMTPREPLY(msg))
570285229Sgshapiro		return -1;
571285229Sgshapiro	sm_strlcpy(replycode, msg, 4);
572285229Sgshapiro	if (msg[3] == '\0')
573285229Sgshapiro		return 3;
574285229Sgshapiro	offset = 4;
575285229Sgshapiro	if (isenhsc(msg + 4, delim))
576285229Sgshapiro		offset = extenhsc(msg + 4, delim, enhsc) + 4;
577285229Sgshapiro	return offset;
578285229Sgshapiro}
579285229Sgshapiro#endif /* _FFR_PROXY */
580285229Sgshapiro
581285229Sgshapiro/*
58238032Speter**  NMESSAGE -- print message (not necessarily an error)
58338032Speter**
58438032Speter**	Just like "message" except it never puts the to... tag on.
58538032Speter**
58638032Speter**	Parameters:
58790795Sgshapiro**		msg -- the message (sm_io_printf fmt) -- if it begins
58838032Speter**			with a three digit SMTP reply code, that is used,
58938032Speter**			otherwise 050 is assumed.
59090795Sgshapiro**		(others) -- sm_io_printf arguments
59138032Speter**
59238032Speter**	Returns:
59338032Speter**		none
59438032Speter*/
59538032Speter
59638032Speter/*VARARGS1*/
59738032Spetervoid
59838032Speter#ifdef __STDC__
59938032Speternmessage(const char *msg, ...)
60064565Sgshapiro#else /* __STDC__ */
60138032Speternmessage(msg, va_alist)
60238032Speter	const char *msg;
60338032Speter	va_dcl
60464565Sgshapiro#endif /* __STDC__ */
60538032Speter{
60664565Sgshapiro	char *errtxt;
60790795Sgshapiro	SM_VA_LOCAL_DECL
60838032Speter
60938032Speter	errno = 0;
61090795Sgshapiro	SM_VA_START(ap, msg);
61164565Sgshapiro	errtxt = fmtmsg(MsgBuf, (char *) NULL, "050",
61264565Sgshapiro			(char *) NULL, 0, msg, ap);
61390795Sgshapiro	SM_VA_END(ap);
61490795Sgshapiro	putoutmsg(MsgBuf, false, false);
61538032Speter
61638032Speter	/* save this message for mailq printing */
61738032Speter	switch (MsgBuf[0])
61838032Speter	{
61938032Speter	  case '4':
62038032Speter	  case '8':
62138032Speter		if (CurEnv->e_message != NULL)
62238032Speter			break;
62364565Sgshapiro		/* FALLTHROUGH */
62438032Speter
62538032Speter	  case '5':
62690795Sgshapiro		if (CurEnv->e_rpool == NULL && CurEnv->e_message != NULL)
62777352Sgshapiro			sm_free(CurEnv->e_message);
628168520Sgshapiro		CurEnv->e_message = sm_rpool_strdup_x(CurEnv->e_rpool, errtxt);
62938032Speter		break;
63038032Speter	}
63138032Speter}
632363466Sgshapiro
63390795Sgshapiro/*
63438032Speter**  PUTOUTMSG -- output error message to transcript and channel
63538032Speter**
63638032Speter**	Parameters:
63738032Speter**		msg -- message to output (in SMTP format).
63890795Sgshapiro**		holdmsg -- if true, don't output a copy of the message to
63938032Speter**			our output channel.
64090795Sgshapiro**		heldmsg -- if true, this is a previously held message;
64138032Speter**			don't log it to the transcript file.
64238032Speter**
64338032Speter**	Returns:
64438032Speter**		none.
64538032Speter**
64638032Speter**	Side Effects:
64738032Speter**		Outputs msg to the transcript.
64838032Speter**		If appropriate, outputs it to the channel.
64938032Speter**		Deletes SMTP reply code number as appropriate.
65038032Speter*/
65138032Speter
65264565Sgshapirostatic void
65338032Speterputoutmsg(msg, holdmsg, heldmsg)
65438032Speter	char *msg;
65538032Speter	bool holdmsg;
65638032Speter	bool heldmsg;
65738032Speter{
658168520Sgshapiro	char msgcode = msg[0];
65964565Sgshapiro	char *errtxt = msg;
660168520Sgshapiro	char *id;
66138032Speter
66238032Speter	/* display for debugging */
66338032Speter	if (tTd(54, 8))
66490795Sgshapiro		sm_dprintf("--- %s%s%s\n", msg, holdmsg ? " (hold)" : "",
66538032Speter			heldmsg ? " (held)" : "");
66638032Speter
66738032Speter	/* map warnings to something SMTP can handle */
66838032Speter	if (msgcode == '6')
66938032Speter		msg[0] = '5';
67038032Speter	else if (msgcode == '8')
67138032Speter		msg[0] = '4';
672168520Sgshapiro	id = (CurEnv != NULL) ? CurEnv->e_id : NULL;
67338032Speter
67438032Speter	/* output to transcript if serious */
67538032Speter	if (!heldmsg && CurEnv != NULL && CurEnv->e_xfp != NULL &&
67638032Speter	    strchr("45", msg[0]) != NULL)
67790795Sgshapiro		(void) sm_io_fprintf(CurEnv->e_xfp, SM_TIME_DEFAULT, "%s\n",
67890795Sgshapiro				     msg);
67938032Speter
68090795Sgshapiro	if (LogLevel > 14 && (OpMode == MD_SMTP || OpMode == MD_DAEMON))
681168520Sgshapiro		sm_syslog(LOG_INFO, id,
68290795Sgshapiro			  "--- %s%s%s", msg, holdmsg ? " (hold)" : "",
68390795Sgshapiro			  heldmsg ? " (held)" : "");
68438032Speter
68538032Speter	if (msgcode == '8')
68638032Speter		msg[0] = '0';
68738032Speter
68838032Speter	/* output to channel if appropriate */
68938032Speter	if (!Verbose && msg[0] == '0')
69038032Speter		return;
69138032Speter	if (holdmsg)
69238032Speter	{
69338032Speter		/* save for possible future display */
69438032Speter		msg[0] = msgcode;
69564565Sgshapiro		if (HeldMessageBuf[0] == '5' && msgcode == '4')
69664565Sgshapiro			return;
697168520Sgshapiro		(void) sm_strlcpy(HeldMessageBuf, msg, sizeof(HeldMessageBuf));
69838032Speter		return;
69938032Speter	}
70038032Speter
70190795Sgshapiro	(void) sm_io_flush(smioout, SM_TIME_DEFAULT);
70238032Speter
70338032Speter	if (OutChannel == NULL)
70438032Speter		return;
70564565Sgshapiro
70664565Sgshapiro	/* find actual text of error (after SMTP status codes) */
70764565Sgshapiro	if (ISSMTPREPLY(errtxt))
70864565Sgshapiro	{
70964565Sgshapiro		int l;
71064565Sgshapiro
71164565Sgshapiro		errtxt += 4;
71264565Sgshapiro		l = isenhsc(errtxt, ' ');
71364565Sgshapiro		if (l <= 0)
71464565Sgshapiro			l = isenhsc(errtxt, '\0');
71564565Sgshapiro		if (l > 0)
71664565Sgshapiro			errtxt += l + 1;
71764565Sgshapiro	}
71864565Sgshapiro
71938032Speter	/* if DisConnected, OutChannel now points to the transcript */
72038032Speter	if (!DisConnected &&
72138032Speter	    (OpMode == MD_SMTP || OpMode == MD_DAEMON || OpMode == MD_ARPAFTP))
72290795Sgshapiro		(void) sm_io_fprintf(OutChannel, SM_TIME_DEFAULT, "%s\r\n",
72390795Sgshapiro				     msg);
72438032Speter	else
72590795Sgshapiro		(void) sm_io_fprintf(OutChannel, SM_TIME_DEFAULT, "%s\n",
72690795Sgshapiro				     errtxt);
72738032Speter	if (TrafficLogFile != NULL)
72890795Sgshapiro		(void) sm_io_fprintf(TrafficLogFile, SM_TIME_DEFAULT,
72990795Sgshapiro				     "%05d >>> %s\n", (int) CurrentPid,
73090795Sgshapiro				     (OpMode == MD_SMTP || OpMode == MD_DAEMON)
73190795Sgshapiro					? msg : errtxt);
73290795Sgshapiro#if !PIPELINING
73390795Sgshapiro	/* XXX can't flush here for SMTP pipelining */
73438032Speter	if (msg[3] == ' ')
73590795Sgshapiro		(void) sm_io_flush(OutChannel, SM_TIME_DEFAULT);
73690795Sgshapiro	if (!sm_io_error(OutChannel) || DisConnected)
73738032Speter		return;
73838032Speter
73938032Speter	/*
74038032Speter	**  Error on output -- if reporting lost channel, just ignore it.
74138032Speter	**  Also, ignore errors from QUIT response (221 message) -- some
74238032Speter	**	rude servers don't read result.
74338032Speter	*/
74438032Speter
74590795Sgshapiro	if (InChannel == NULL || sm_io_eof(InChannel) ||
74690795Sgshapiro	    sm_io_error(InChannel) || strncmp(msg, "221", 3) == 0)
74738032Speter		return;
74838032Speter
74938032Speter	/* can't call syserr, 'cause we are using MsgBuf */
75090795Sgshapiro	HoldErrs = true;
75138032Speter	if (LogLevel > 0)
752168520Sgshapiro		sm_syslog(LOG_CRIT, id,
75364565Sgshapiro			  "SYSERR: putoutmsg (%s): error on output channel sending \"%s\": %s",
75490795Sgshapiro			  CURHOSTNAME,
75590795Sgshapiro			  shortenstring(msg, MAXSHORTSTR), sm_errstring(errno));
75690795Sgshapiro#endif /* !PIPELINING */
75738032Speter}
758363466Sgshapiro
75990795Sgshapiro/*
76038032Speter**  PUTERRMSG -- like putoutmsg, but does special processing for error messages
76138032Speter**
76238032Speter**	Parameters:
76338032Speter**		msg -- the message to output.
76438032Speter**
76538032Speter**	Returns:
76638032Speter**		none.
76738032Speter**
76838032Speter**	Side Effects:
76938032Speter**		Sets the fatal error bit in the envelope as appropriate.
77038032Speter*/
77138032Speter
77264565Sgshapirostatic void
77338032Speterputerrmsg(msg)
77438032Speter	char *msg;
77538032Speter{
77638032Speter	char msgcode = msg[0];
77738032Speter
77838032Speter	/* output the message as usual */
77990795Sgshapiro	putoutmsg(msg, HoldErrs, false);
78038032Speter
78138032Speter	/* be careful about multiple error messages */
78238032Speter	if (OnlyOneError)
78390795Sgshapiro		HoldErrs = true;
78438032Speter
78538032Speter	/* signal the error */
78638032Speter	Errors++;
78738032Speter
78838032Speter	if (CurEnv == NULL)
78938032Speter		return;
79064565Sgshapiro
79138032Speter	if (msgcode == '6')
79238032Speter	{
79338032Speter		/* notify the postmaster */
79438032Speter		CurEnv->e_flags |= EF_PM_NOTIFY;
79538032Speter	}
79638032Speter	else if (msgcode == '5' && bitset(EF_GLOBALERRS, CurEnv->e_flags))
79738032Speter	{
79838032Speter		/* mark long-term fatal errors */
79938032Speter		CurEnv->e_flags |= EF_FATALERRS;
80038032Speter	}
80138032Speter}
802363466Sgshapiro
80390795Sgshapiro/*
80464565Sgshapiro**  ISENHSC -- check whether a string contains an enhanced status code
80564565Sgshapiro**
80664565Sgshapiro**	Parameters:
80764565Sgshapiro**		s -- string with possible enhanced status code.
80864565Sgshapiro**		delim -- delim for enhanced status code.
80964565Sgshapiro**
81064565Sgshapiro**	Returns:
81164565Sgshapiro**		0  -- no enhanced status code.
81264565Sgshapiro**		>4 -- length of enhanced status code.
81364565Sgshapiro*/
814363466Sgshapiro
81564565Sgshapiroint
81664565Sgshapiroisenhsc(s, delim)
81764565Sgshapiro	const char *s;
81864565Sgshapiro	int delim;
81964565Sgshapiro{
82064565Sgshapiro	int l, h;
82164565Sgshapiro
82264565Sgshapiro	if (s == NULL)
82364565Sgshapiro		return 0;
82464565Sgshapiro	if (!((*s == '2' || *s == '4' || *s == '5') && s[1] == '.'))
82564565Sgshapiro		return 0;
82664565Sgshapiro	h = 0;
82764565Sgshapiro	l = 2;
82864565Sgshapiro	while (h < 3 && isascii(s[l + h]) && isdigit(s[l + h]))
82964565Sgshapiro		++h;
83064565Sgshapiro	if (h == 0 || s[l + h] != '.')
83164565Sgshapiro		return 0;
83264565Sgshapiro	l += h + 1;
83364565Sgshapiro	h = 0;
83464565Sgshapiro	while (h < 3 && isascii(s[l + h]) && isdigit(s[l + h]))
83564565Sgshapiro		++h;
83664565Sgshapiro	if (h == 0 || s[l + h] != delim)
83764565Sgshapiro		return 0;
83864565Sgshapiro	return l + h;
83964565Sgshapiro}
840363466Sgshapiro
84190795Sgshapiro/*
84264565Sgshapiro**  EXTENHSC -- check and extract an enhanced status code
84364565Sgshapiro**
84464565Sgshapiro**	Parameters:
84564565Sgshapiro**		s -- string with possible enhanced status code.
84664565Sgshapiro**		delim -- delim for enhanced status code.
84764565Sgshapiro**		e -- pointer to storage for enhanced status code.
84864565Sgshapiro**			must be != NULL and have space for at least
84964565Sgshapiro**			10 characters ([245].[0-9]{1,3}.[0-9]{1,3})
85064565Sgshapiro**
85164565Sgshapiro**	Returns:
85264565Sgshapiro**		0  -- no enhanced status code.
85364565Sgshapiro**		>4 -- length of enhanced status code.
85464565Sgshapiro**
85564565Sgshapiro**	Side Effects:
85664565Sgshapiro**		fills e with enhanced status code.
85764565Sgshapiro*/
85890795Sgshapiro
85964565Sgshapiroint
86064565Sgshapiroextenhsc(s, delim, e)
86164565Sgshapiro	const char *s;
86264565Sgshapiro	int delim;
86364565Sgshapiro	char *e;
86464565Sgshapiro{
86564565Sgshapiro	int l, h;
86664565Sgshapiro
86764565Sgshapiro	if (s == NULL)
86864565Sgshapiro		return 0;
86964565Sgshapiro	if (!((*s == '2' || *s == '4' || *s == '5') && s[1] == '.'))
87064565Sgshapiro		return 0;
87164565Sgshapiro	h = 0;
87264565Sgshapiro	l = 2;
87364565Sgshapiro	e[0] = s[0];
87464565Sgshapiro	e[1] = '.';
87564565Sgshapiro	while (h < 3 && isascii(s[l + h]) && isdigit(s[l + h]))
87664565Sgshapiro	{
87764565Sgshapiro		e[l + h] = s[l + h];
87864565Sgshapiro		++h;
87964565Sgshapiro	}
88064565Sgshapiro	if (h == 0 || s[l + h] != '.')
88164565Sgshapiro		return 0;
88264565Sgshapiro	e[l + h] = '.';
88364565Sgshapiro	l += h + 1;
88464565Sgshapiro	h = 0;
88564565Sgshapiro	while (h < 3 && isascii(s[l + h]) && isdigit(s[l + h]))
88664565Sgshapiro	{
88764565Sgshapiro		e[l + h] = s[l + h];
88864565Sgshapiro		++h;
88964565Sgshapiro	}
89064565Sgshapiro	if (h == 0 || s[l + h] != delim)
89164565Sgshapiro		return 0;
89264565Sgshapiro	e[l + h] = '\0';
89364565Sgshapiro	return l + h;
89464565Sgshapiro}
895363466Sgshapiro
89690795Sgshapiro/*
89738032Speter**  FMTMSG -- format a message into buffer.
89838032Speter**
89938032Speter**	Parameters:
90064565Sgshapiro**		eb -- error buffer to get result -- MUST BE MsgBuf.
90138032Speter**		to -- the recipient tag for this message.
90264565Sgshapiro**		num -- default three digit SMTP reply code.
90364565Sgshapiro**		enhsc -- enhanced status code.
90438032Speter**		en -- the error number to display.
905363466Sgshapiro**		fmt -- format of string: See NOTE below.
90664565Sgshapiro**		ap -- arguments for fmt.
90738032Speter**
90838032Speter**	Returns:
90964565Sgshapiro**		pointer to error text beyond status codes.
91038032Speter**
911363466Sgshapiro**	NOTE:
912363466Sgshapiro**		Do NOT use "%s" as fmt if the argument starts with an SMTP
913363466Sgshapiro**		reply code!
91438032Speter*/
91538032Speter
91664565Sgshapirostatic char *
91764565Sgshapirofmtmsg(eb, to, num, enhsc, eno, fmt, ap)
91838032Speter	register char *eb;
91938032Speter	const char *to;
92038032Speter	const char *num;
92164565Sgshapiro	const char *enhsc;
92238032Speter	int eno;
92338032Speter	const char *fmt;
92490795Sgshapiro	SM_VA_LOCAL_DECL
92538032Speter{
92638032Speter	char del;
92738032Speter	int l;
928168520Sgshapiro	int spaceleft = sizeof(MsgBuf);
92964565Sgshapiro	char *errtxt;
93038032Speter
93138032Speter	/* output the reply code */
93264565Sgshapiro	if (ISSMTPCODE(fmt))
93338032Speter	{
93438032Speter		num = fmt;
93538032Speter		fmt += 4;
93638032Speter	}
93738032Speter	if (num[3] == '-')
93838032Speter		del = '-';
93938032Speter	else
94038032Speter		del = ' ';
94190795Sgshapiro	if (SoftBounce && num[0] == '5')
94290795Sgshapiro	{
94390795Sgshapiro		/* replace 5 by 4 */
94490795Sgshapiro		(void) sm_snprintf(eb, spaceleft, "4%2.2s%c", num + 1, del);
94590795Sgshapiro	}
94690795Sgshapiro	else
947168520Sgshapiro		(void) sm_snprintf(eb, spaceleft, "%3.3s%c", num, del);
94838032Speter	eb += 4;
94938032Speter	spaceleft -= 4;
95038032Speter
95164565Sgshapiro	if ((l = isenhsc(fmt, ' ' )) > 0 && l < spaceleft - 4)
95264565Sgshapiro	{
95364565Sgshapiro		/* copy enh.status code including trailing blank */
95464565Sgshapiro		l++;
95590795Sgshapiro		(void) sm_strlcpy(eb, fmt, l + 1);
95664565Sgshapiro		eb += l;
95764565Sgshapiro		spaceleft -= l;
95864565Sgshapiro		fmt += l;
95964565Sgshapiro	}
96064565Sgshapiro	else if ((l = isenhsc(enhsc, '\0')) > 0 && l < spaceleft - 4)
96164565Sgshapiro	{
96264565Sgshapiro		/* copy enh.status code */
96390795Sgshapiro		(void) sm_strlcpy(eb, enhsc, l + 1);
96464565Sgshapiro		eb[l] = ' ';
96564565Sgshapiro		eb[++l] = '\0';
96664565Sgshapiro		eb += l;
96764565Sgshapiro		spaceleft -= l;
96864565Sgshapiro	}
96990795Sgshapiro	if (SoftBounce && eb[-l] == '5')
97090795Sgshapiro	{
97190795Sgshapiro		/* replace 5 by 4 */
97290795Sgshapiro		eb[-l] = '4';
97390795Sgshapiro	}
97464565Sgshapiro	errtxt = eb;
97564565Sgshapiro
97638032Speter	/* output the file name and line number */
97738032Speter	if (FileName != NULL)
97838032Speter	{
97990795Sgshapiro		(void) sm_snprintf(eb, spaceleft, "%s: line %d: ",
98090795Sgshapiro				   shortenstring(FileName, 83), LineNumber);
98138032Speter		eb += (l = strlen(eb));
98238032Speter		spaceleft -= l;
98338032Speter	}
98438032Speter
98582020Sgshapiro	/*
98682020Sgshapiro	**  output the "to" address only if it is defined and one of the
98782020Sgshapiro	**  following codes is used:
98882020Sgshapiro	**  050 internal notices, e.g., alias expansion
98982020Sgshapiro	**  250 Ok
99082020Sgshapiro	**  252 Cannot VRFY user, but will accept message and attempt delivery
99182020Sgshapiro	**  450 Requested mail action not taken: mailbox unavailable
99282020Sgshapiro	**  550 Requested action not taken: mailbox unavailable
99382020Sgshapiro	**  553 Requested action not taken: mailbox name not allowed
99482020Sgshapiro	**
99582020Sgshapiro	**  Notice: this still isn't "the right thing", this code shouldn't
99682020Sgshapiro	**	(indirectly) depend on CurEnv->e_to.
99782020Sgshapiro	*/
99882020Sgshapiro
99938032Speter	if (to != NULL && to[0] != '\0' &&
100082020Sgshapiro	    (strncmp(num, "050", 3) == 0 ||
100182020Sgshapiro	     strncmp(num, "250", 3) == 0 ||
100282020Sgshapiro	     strncmp(num, "252", 3) == 0 ||
100382020Sgshapiro	     strncmp(num, "450", 3) == 0 ||
100482020Sgshapiro	     strncmp(num, "550", 3) == 0 ||
100582020Sgshapiro	     strncmp(num, "553", 3) == 0))
100638032Speter	{
100790795Sgshapiro		(void) sm_strlcpyn(eb, spaceleft, 2,
100890795Sgshapiro				   shortenstring(to, MAXSHORTSTR), "... ");
100938032Speter		spaceleft -= strlen(eb);
1010363466Sgshapiro#if _FFR_EAI
1011363466Sgshapiro		eb += strlen(eb);
1012363466Sgshapiro#else
101338032Speter		while (*eb != '\0')
101438032Speter			*eb++ &= 0177;
1015363466Sgshapiro#endif
101638032Speter	}
101738032Speter
101838032Speter	/* output the message */
101990795Sgshapiro	(void) sm_vsnprintf(eb, spaceleft, fmt, ap);
102038032Speter	spaceleft -= strlen(eb);
1021363466Sgshapiro#if _FFR_EAI
1022363466Sgshapiro	eb += strlen(eb);
1023363466Sgshapiro#else
102438032Speter	while (*eb != '\0')
102538032Speter		*eb++ &= 0177;
1026363466Sgshapiro#endif
102738032Speter
102838032Speter	/* output the error code, if any */
102938032Speter	if (eno != 0)
103090795Sgshapiro		(void) sm_strlcpyn(eb, spaceleft, 2, ": ", sm_errstring(eno));
103164565Sgshapiro
103264565Sgshapiro	return errtxt;
103338032Speter}
1034363466Sgshapiro
103590795Sgshapiro/*
103638032Speter**  BUFFER_ERRORS -- arrange to buffer future error messages
103738032Speter**
103838032Speter**	Parameters:
103938032Speter**		none
104038032Speter**
104138032Speter**	Returns:
104238032Speter**		none.
104338032Speter*/
104438032Speter
104538032Spetervoid
104638032Speterbuffer_errors()
104738032Speter{
104838032Speter	HeldMessageBuf[0] = '\0';
104990795Sgshapiro	HoldErrs = true;
105038032Speter}
1051363466Sgshapiro
105290795Sgshapiro/*
105338032Speter**  FLUSH_ERRORS -- flush the held error message buffer
105438032Speter**
105538032Speter**	Parameters:
105638032Speter**		print -- if set, print the message, otherwise just
105738032Speter**			delete it.
105838032Speter**
105938032Speter**	Returns:
106038032Speter**		none.
106138032Speter*/
106238032Speter
106338032Spetervoid
106438032Speterflush_errors(print)
106538032Speter	bool print;
106638032Speter{
106738032Speter	if (print && HeldMessageBuf[0] != '\0')
106890795Sgshapiro		putoutmsg(HeldMessageBuf, false, true);
106938032Speter	HeldMessageBuf[0] = '\0';
107090795Sgshapiro	HoldErrs = false;
107138032Speter}
107290795Sgshapiro/*
107390795Sgshapiro**  SM_ERRSTRING -- return string description of error code
107438032Speter**
107538032Speter**	Parameters:
107638032Speter**		errnum -- the error number to translate
107738032Speter**
107838032Speter**	Returns:
107938032Speter**		A string description of errnum.
108038032Speter*/
108138032Speter
108238032Speterconst char *
108390795Sgshapirosm_errstring(errnum)
108438032Speter	int errnum;
108538032Speter{
108638032Speter	char *dnsmsg;
108738032Speter	char *bp;
108838032Speter	static char buf[MAXLINE];
108990795Sgshapiro#if HASSTRERROR
109090795Sgshapiro	char *err;
109190795Sgshapiro	char errbuf[30];
1092363466Sgshapiro#endif
109364565Sgshapiro#if !HASSTRERROR && !defined(ERRLIST_PREDEFINED)
109438032Speter	extern char *sys_errlist[];
109538032Speter	extern int sys_nerr;
1096363466Sgshapiro#endif
109738032Speter
109838032Speter	/*
109938032Speter	**  Handle special network error codes.
110038032Speter	**
110138032Speter	**	These are 4.2/4.3bsd specific; they should be in daemon.c.
110238032Speter	*/
110338032Speter
110438032Speter	dnsmsg = NULL;
110538032Speter	switch (errnum)
110638032Speter	{
110738032Speter	  case ETIMEDOUT:
110838032Speter	  case ECONNRESET:
110938032Speter		bp = buf;
111090795Sgshapiro#if HASSTRERROR
111190795Sgshapiro		err = strerror(errnum);
111290795Sgshapiro		if (err == NULL)
111390795Sgshapiro		{
1114168520Sgshapiro			(void) sm_snprintf(errbuf, sizeof(errbuf),
111590795Sgshapiro					   "Error %d", errnum);
111690795Sgshapiro			err = errbuf;
111790795Sgshapiro		}
111890795Sgshapiro		(void) sm_strlcpy(bp, err, SPACELEFT(buf, bp));
111990795Sgshapiro#else /* HASSTRERROR */
112038032Speter		if (errnum >= 0 && errnum < sys_nerr)
112190795Sgshapiro			(void) sm_strlcpy(bp, sys_errlist[errnum],
112290795Sgshapiro					  SPACELEFT(buf, bp));
112338032Speter		else
112490795Sgshapiro			(void) sm_snprintf(bp, SPACELEFT(buf, bp),
112590795Sgshapiro				"Error %d", errnum);
112690795Sgshapiro#endif /* HASSTRERROR */
112738032Speter		bp += strlen(bp);
112838032Speter		if (CurHostName != NULL)
112938032Speter		{
113038032Speter			if (errnum == ETIMEDOUT)
113138032Speter			{
113290795Sgshapiro				(void) sm_snprintf(bp, SPACELEFT(buf, bp),
113390795Sgshapiro					" with ");
113438032Speter				bp += strlen(bp);
113538032Speter			}
113638032Speter			else
113738032Speter			{
113838032Speter				bp = buf;
113990795Sgshapiro				(void) sm_snprintf(bp, SPACELEFT(buf, bp),
114038032Speter					"Connection reset by ");
114138032Speter				bp += strlen(bp);
114238032Speter			}
114390795Sgshapiro			(void) sm_strlcpy(bp,
114490795Sgshapiro					shortenstring(CurHostName, MAXSHORTSTR),
114590795Sgshapiro					SPACELEFT(buf, bp));
114638032Speter			bp += strlen(buf);
114738032Speter		}
114838032Speter		if (SmtpPhase != NULL)
114938032Speter		{
115090795Sgshapiro			(void) sm_snprintf(bp, SPACELEFT(buf, bp),
115190795Sgshapiro				" during %s", SmtpPhase);
115238032Speter		}
115364565Sgshapiro		return buf;
115438032Speter
115538032Speter	  case EHOSTDOWN:
115638032Speter		if (CurHostName == NULL)
115738032Speter			break;
1158168520Sgshapiro		(void) sm_snprintf(buf, sizeof(buf), "Host %s is down",
115938032Speter			shortenstring(CurHostName, MAXSHORTSTR));
116064565Sgshapiro		return buf;
116138032Speter
116238032Speter	  case ECONNREFUSED:
116338032Speter		if (CurHostName == NULL)
116438032Speter			break;
1165168520Sgshapiro		(void) sm_strlcpyn(buf, sizeof(buf), 2, "Connection refused by ",
116638032Speter			shortenstring(CurHostName, MAXSHORTSTR));
116764565Sgshapiro		return buf;
116838032Speter
116964565Sgshapiro#if NAMED_BIND
117038032Speter	  case HOST_NOT_FOUND + E_DNSBASE:
117138032Speter		dnsmsg = "host not found";
117238032Speter		break;
117338032Speter
117438032Speter	  case TRY_AGAIN + E_DNSBASE:
117538032Speter		dnsmsg = "host name lookup failure";
117638032Speter		break;
117738032Speter
117838032Speter	  case NO_RECOVERY + E_DNSBASE:
117938032Speter		dnsmsg = "non-recoverable error";
118038032Speter		break;
118138032Speter
118238032Speter	  case NO_DATA + E_DNSBASE:
118338032Speter		dnsmsg = "no data known";
118438032Speter		break;
118564565Sgshapiro#endif /* NAMED_BIND */
118638032Speter
118738032Speter	  case EPERM:
118838032Speter		/* SunOS gives "Not owner" -- this is the POSIX message */
118938032Speter		return "Operation not permitted";
119038032Speter
119138032Speter	/*
119238032Speter	**  Error messages used internally in sendmail.
119338032Speter	*/
119438032Speter
119538032Speter	  case E_SM_OPENTIMEOUT:
119638032Speter		return "Timeout on file open";
119738032Speter
119838032Speter	  case E_SM_NOSLINK:
119938032Speter		return "Symbolic links not allowed";
120038032Speter
120138032Speter	  case E_SM_NOHLINK:
120238032Speter		return "Hard links not allowed";
120338032Speter
120438032Speter	  case E_SM_REGONLY:
120538032Speter		return "Regular files only";
120638032Speter
120738032Speter	  case E_SM_ISEXEC:
120838032Speter		return "Executable files not allowed";
120938032Speter
121038032Speter	  case E_SM_WWDIR:
121138032Speter		return "World writable directory";
121238032Speter
121338032Speter	  case E_SM_GWDIR:
121438032Speter		return "Group writable directory";
121538032Speter
121638032Speter	  case E_SM_FILECHANGE:
121738032Speter		return "File changed after open";
121838032Speter
121938032Speter	  case E_SM_WWFILE:
122038032Speter		return "World writable file";
122138032Speter
122238032Speter	  case E_SM_GWFILE:
122338032Speter		return "Group writable file";
122464565Sgshapiro
122564565Sgshapiro	  case E_SM_GRFILE:
122664565Sgshapiro		return "Group readable file";
122764565Sgshapiro
122864565Sgshapiro	  case E_SM_WRFILE:
122964565Sgshapiro		return "World readable file";
123038032Speter	}
123138032Speter
123238032Speter	if (dnsmsg != NULL)
123338032Speter	{
123438032Speter		bp = buf;
1235168520Sgshapiro		bp += sm_strlcpy(bp, "Name server: ", sizeof(buf));
123638032Speter		if (CurHostName != NULL)
123738032Speter		{
123890795Sgshapiro			(void) sm_strlcpyn(bp, SPACELEFT(buf, bp), 2,
123990795Sgshapiro				shortenstring(CurHostName, MAXSHORTSTR), ": ");
124038032Speter			bp += strlen(bp);
124138032Speter		}
124290795Sgshapiro		(void) sm_strlcpy(bp, dnsmsg, SPACELEFT(buf, bp));
124338032Speter		return buf;
124438032Speter	}
124538032Speter
124690795Sgshapiro#if LDAPMAP
1247285229Sgshapiro	if (errnum >= E_LDAPBASE - E_LDAP_SHIM)
124864565Sgshapiro		return ldap_err2string(errnum - E_LDAPBASE);
1249363466Sgshapiro#endif
125064565Sgshapiro
125138032Speter#if HASSTRERROR
125290795Sgshapiro	err = strerror(errnum);
125390795Sgshapiro	if (err == NULL)
125490795Sgshapiro	{
1255168520Sgshapiro		(void) sm_snprintf(buf, sizeof(buf), "Error %d", errnum);
125690795Sgshapiro		return buf;
125790795Sgshapiro	}
125890795Sgshapiro	return err;
125964565Sgshapiro#else /* HASSTRERROR */
126038032Speter	if (errnum > 0 && errnum < sys_nerr)
126164565Sgshapiro		return sys_errlist[errnum];
126238032Speter
1263168520Sgshapiro	(void) sm_snprintf(buf, sizeof(buf), "Error %d", errnum);
126464565Sgshapiro	return buf;
126564565Sgshapiro#endif /* HASSTRERROR */
126638032Speter}
1267