/*
 * Copyright (c) 2004-2006 Sendmail, Inc. and its suppliers.
 *	All rights reserved.
 *
 * By using this file, you agree to the terms and conditions set
 * forth in the LICENSE file which can be found at the top level of
 * the sendmail distribution.
 *
 */

#include "sm/generic.h"
SM_RCSID("@(#)$Id: smerr2txt.c,v 1.26 2007/11/05 05:47:50 ca Exp $")

#include "sm/error.h"
#include "sm/dns.h"
#include "sm/bdb.h"
#include "sm/rfc2821.h"
#define SMERR2TXT 1
#include "smerr2txt.h"

#if 0
/*
convert define line to struct entry:
:'a,.s/^#define \(SM[^ 	]*\).*/	{ \1,	"\1"	},/
*/
#endif /* 0 */

static e2s_T
e_modules[] =
{
	{ SM_EM_NONE,		"none"	},
	{ SM_EM_AQ,		"AQ"	},
	{ SM_EM_IQDB,		"IQDB"	},
	{ SM_EM_IBDB,		"IBDB"	},
	{ SM_EM_EDB,		"EDB"	},
	{ SM_EM_CDB,		"CDB"	},
	{ SM_EM_DA,		"delivery agent"	},
	{ SM_EM_OCC,		"open connection cache"	},
	{ SM_EM_DNS,		"DNS"	},
	{ SM_EM_SMTPC,		"SMTPC"	},
	{ SM_EM_SMTPS,		"SMTPS"	},
	{ SM_EM_MAPS,		"MAPS"	},
	{ SM_EM_MAP,		"MAP"	},
	{ SM_EM_BDB,		"BDB"	},
	{ SM_EM_STTHRIO,	"STTHRIO"	},
	{ SM_EM_STTHR,		"STTHR"	},
	{ SM_EM_EVTHR,		"EVTHR"	},
	{ SM_EM_RECCOM,		"RECCOM"	},
	{ SM_EM_HT,		"hash table"	},
	{ SM_EM_RSC,		"restricted size cache"	},
	{ SM_EM_STR,		"str"	},
	{ SM_EM_RCB,		"RCB"	},
	{ SM_EM_TLS,		"TLS"	},
	{ SM_EM_SASL,		"SASL"	},
	{ SM_EM_NET,		"NET"	},
	{ SM_EM_IO,		"IO"	},
	{ SM_EM_LOG,		"logging"	},
	{ SM_EM_ADDR,		"RFC 2821 address parsing"	},
	{ SM_EM_IP,		"IP address parsing"	},
	{ SM_EM_Q_Q2AR,		"QMGR: QMGR to SMAR"	},
	{ SM_EM_Q_AR2Q,		"QMGR: SMAR to QMGR"	},
	{ SM_EM_Q_Q2SS,		"QMGR: QMGR to SMTPS"	},
	{ SM_EM_Q_SS2Q,		"QMGR: SMTPS to QMGR"	},
	{ SM_EM_Q_Q2SC,		"QMGR: QMGR to SMTPC"	},
	{ SM_EM_Q_SC2Q,		"QMGR: SMTPC to QMGR"	},
	{ SM_EM_Q_RDIBDB,	"QMGR: read IBDB"	},
	{ SM_EM_Q_IQDB,		"QMGR: IQDB"	},
	{ SM_EM_Q_IBDB,		"QMGR: IBDB"	},
	{ SM_EM_Q_EDBC,		"QMGR: EDBC"	},
	{ SM_EM_Q_DSN,		"QMGR: DSN"	},
	{ SM_EM_Q_START,	"QMGR: startup"	},
	{ SM_EM_Q_CTL2Q,	"QMGR: control"	},
	{ SM_EM_Q_CONF,		"QMGR: configuration"	},
	{ SM_EM_Q_CLEAN,	"QMGR: cleanup"	},
	{ SM_EM_Q_SCHED,	"QMGR: scheduler"	},
	{ SM_EM_Q,		"QMGR: misc"	},
	{ SM_EM_AR_Q2AR,	"SMAR: QMGR to SMAR"	},
	{ SM_EM_AR_AR2Q,	"SMAR: SMAR to QMGR"	},
	{ SM_EM_AR_DNSW,	"SMAR: DNS write"	},
	{ SM_EM_AR_DNSR,	"SMAR: DNS read"	},
	{ SM_EM_AR_DNSD,	"SMAR: DNS read"	},
	{ SM_EM_AR_WAIT,	"SMAR: wait for async function"	},
	{ SM_EM_AR,		"SMAR: misc"	},
	{ SM_EM_CONF,		"configuration"	},
	{ SM_EM_PMILTER,	"pmilter"	},
	{ SM_EM_THR,		"thread"	},
	{ SM_EM_BATV,		"BATV"	},
	{ SM_EM_UTIL,		"utility functions"	},
	{ 0,			NULL		}
};

e2s_T
e_error[] =
{
	{ SM_E_NOMORE,		"no next entry"	},
	{ SM_E_UNEXPECTED,	"unexpected"	},
	{ SM_E_RANGE,		"input parameter out of range"	},
	{ SM_E_2BIG,		"input parameter too big"	},
	{ SM_E_FULL,		"object full"	},
	{ SM_E_OVFLW_SC,	"scalar overflow"	},
	{ SM_E_OVFLW_NS,	"nonscalar overflow"	},
	{ SM_E_NOTFOUND,	"not found"	},
	{ SM_E_STARTFAIL,	"start failure"	},
	{ SM_E_SYNTAX,		"syntax"	},
	{ SM_E_USAGE,		"usage"	},
	{ SM_E_DONTSTART,	"donotstart"	},
	{ SM_E_VER_MIX,		"version mismatch"	},
	{ SM_E_NO_AR,		"smar not available"	},
	{ SM_E_NO_DA,		"no delivery agent available"	},
	{ SM_E_NO_DA_FREE,	"no free delivery agent"	},
	{ SM_E_PR_V_MISM,	"protocol version mismatch"	},
	{ SM_E_PR_ERR,		"protocol error"	},
	{ SM_E_RCB2LONG,	"RCB too large"	},
	{ SM_E_TEMPMAP,		"temporary map error"	},
	{ SM_E_PERMMAP,		"permanent map error"	},
	{ SM_E_CLOSEDMAP,	"closedmap"	},
	{ SM_E_NOMAP,		"map is not available"	},
	{ SM_E_NOTIMPL,		"not implemented"	},
	{ SM_E_MXEMPTY,		"MX list is empty after removal of local addresses"	},
	{ SM_E_TTMYSELF,	"talking to myself"	},
	{ SM_E_ALIASEXP,	"alias expansion failed"	},
	{ SM_E_ALIAS_REC,	"aliases nested too deep"	},
	{ SM_E_IP_MISM,	"DNS answer from different server"	},
	{ SM_E_NOSIG,	"no signature"	},
	{ SM_E_EXPIRED,	"expired"	},
	{ SM_E_RSR_PRB,	"resource problem"	},
	{ SM_E_ILL_PIPE,	"illegal pipelining"	},
	{ SM_E_RD,	"read error"	},
	{ SM_E_EOF,	"EOF"	},
	{ SM_E_WR,	"write error"	},
	{ SM_E_BARE_NL,	"bare NL detected"	},
	{ SM_E_CONN_CLSD,	"internal connection to other module closed" },
	{ SM_E_WRONG_ID,	"internal error: communication context id mismatch"	},
	{ SM_SSL_ERROR_SYSCALL,	"TLS syscall error" },
	{ SM_SSL_ERROR_SSL,	"TLS protocol error" },
	{ SM_SSL_ERROR_GENERIC,	"TLS unknown error" },
	{ SM_E_UNAVAIL,	"unavailable" },

	{ DNSR_NOTFOUND,	"DNS record not found (authorative)" },
	{ DNSR_NO_DATA,	"valid name but no data record of requested type" },
	{ DNSR_TEMP,	"query failed: try again" },
	{ DNSR_PERM,	"query failed: do not retry" },
	{ DNSR_TIMEOUT,	"query timed out" },
	{ DNSR_REFUSED,	"query refused by nameserver" },
	{ DNSR_MXINVALID,	"MX record invalid"	},
	{ DNSR_PTRINVALID,	"PTR record invalid"	},
	{ DNSR_CNINVALID,	"CNAME record invalid"	},

	{ R2821_ERR_LEFT,	"missing '<'" },
	{ R2821_ERR_RIGHT,	"missing '>'" },
	{ R2821_ERR_ROUTE,	"route address not allowed" },
	{ R2821_ERR_HOST,	"domain part does not start with an atom" },
	{ R2821_ERR_FQDN,	"domain part is not a FQDN" },
	{ R2821_ERR_LOCAL,	"missing local part" },
	{ R2821_ERR_AT,	"missing '@'" },
	{ R2821_ERR_EMPTY,	"<> not allowed here" },
	{ R2821_ERR_MBOX,	"completely empty address" },
	{ R2821_ERR_COLON,	"missing ':' (unused)" },
	{ R2821_ERR_DOTSTR,	"\"dotstr\" syntax is wrong: expected atom after '.'" },
	{ R2821_ERR_DOMAIN,	"domain part is empty" },
	{ R2821_ERR_DOT,	"expecting ':'" },
	{ R2821_ERR_TRAILDOT,	"domain has trailing '.'" },
	{ R2821_ERR_ATDOMAIN,	"'@' in route address missing" },
	{ R2821_ERR_COMMA,	"comma in route address missing" },
	{ R2821_ERR_DOMSYNTAX,	"domain part has wrong syntax" },
	{ R2821_ERR_MORE,	"tokens after end" },
	{ R2821_ERR_DEL_MISS,	"domain does not end in delimiter" },
	{ R2821_ERR_TOOLONG,	"address too long" },
	{ R2821_ERR_TRAILBACKSL,	"trailing backslash" },
	{ R2821_ERR_CLS_BRACKET,	"too many closing brackets" },
	{ R2821_ERR_CLS_PARENTH,	"too many closing parentheses" },
	{ R2821_ERR_PARENTHESIS,	"parenthesis in address not allowed" },
	{ R2821_ERR_MUSTQUOTE,	"character must be quoted but isn't" },

	{ SM_CNF_E_NEWLINE_IN_STRING,	"newline in string" },
	{ SM_CNF_E_EOF_IN_STRING,	"EOF in string" },
	{ SM_CNF_E_BAD_CHAR,	"unexpected character" },
	{ SM_CNF_E_NO_MEMORY,	"out of memory" },
	{ SM_CNF_E_HEX_EXPECTED,	"hexadecimal digit expected after \\x \\u or \\U" },
	{ SM_CNF_E_CHAR_OVERFLOW,	"character constant too large" },
	{ SM_CNF_E_INVALID,	"invalid argument to configuration function" },
	{ SM_CNF_E_READ,	"error while reading configuration file" },
	{ SM_CNF_E_READ_OPEN,	"cannot open configuration file for reading" },
	{ SM_CNF_E_READ_CLOSE,	"error closing configuration file after reading" },
	{ SM_CNF_E_SYNTAX,	"syntax error" },
	{ SM_CNF_E_NOT_FOUND,	"not found" },
	{ SM_CNF_E_ALREADY,	"already exists" },
	{ SM_CNF_E_TYPE,	"invalid value" },
	{ SM_CNF_E_NUL_IN_STRING,	"ascii NUL character in string" },
	{ SM_CNF_E_TOO_MANY,	"too many elements in array" },

	{ 0,			NULL		}
};

/* XXX HACK XXX is there a DB function to do this? */

e2s_T
e_bdb4_2[] =
{
	{ DB_DONOTINDEX,	"DB_DONOTINDEX" },
#ifdef DB_FILEOPEN
	{ DB_FILEOPEN,	"DB_FILEOPEN" },
#endif
	{ DB_KEYEMPTY,	"DB_KEYEMPTY" },
	{ DB_KEYEXIST,	"DB_KEYEXIST" },
	{ DB_LOCK_DEADLOCK,	"DB_LOCK_DEADLOCK" },
	{ DB_LOCK_NOTGRANTED,	"DB_LOCK_NOTGRANTED" },
	{ DB_NOSERVER,	"DB_NOSERVER" },
	{ DB_NOSERVER_HOME,	"DB_NOSERVER_HOME" },
	{ DB_NOSERVER_ID,	"DB_NOSERVER_ID" },
	{ DB_NOTFOUND,	"DB_NOTFOUND" },
	{ DB_OLD_VERSION,	"DB_OLD_VERSION" },
	{ DB_PAGE_NOTFOUND,	"DB_PAGE_NOTFOUND" },
	{ DB_REP_DUPMASTER,	"DB_REP_DUPMASTER" },
#ifdef DB_REP_HANDLE_DEAD
	{ DB_REP_HANDLE_DEAD,	"DB_REP_HANDLE_DEAD" },
#endif
	{ DB_REP_HOLDELECTION,	"DB_REP_HOLDELECTION" },
#ifdef DB_REP_ISPERM
	{ DB_REP_ISPERM,	"DB_REP_ISPERM" },
#endif
	{ DB_REP_NEWMASTER,	"DB_REP_NEWMASTER" },
	{ DB_REP_NEWSITE,	"DB_REP_NEWSITE" },
#ifdef DB_REP_NOTPERM
	{ DB_REP_NOTPERM,	"DB_REP_NOTPERM" },
#endif
#ifdef DB_REP_OUTDATED
	{ DB_REP_OUTDATED,	"DB_REP_OUTDATED" },
#endif
	{ DB_REP_UNAVAIL,	"DB_REP_UNAVAIL" },
	{ DB_RUNRECOVERY,	"DB_RUNRECOVERY" },
	{ DB_SECONDARY_BAD,	"DB_SECONDARY_BAD" },
#ifdef DB_VERIFY_BAD
	{ DB_VERIFY_BAD,	"DB_VERIFY_BAD" },
#endif
#if defined(DB_BUFFER_SMALL) && ENOMEM != DB_BUFFER_SMALL
	{ DB_BUFFER_SMALL,	"DB_BUFFER_SMALL" },
#endif
	{ 0,			NULL		}
};

#if 0
static e2s_T
e_dns[] =
{
	{ DNSR_NOTFOUND,	"DNSR Not Found" },
	{ DNSR_NO_DATA,	"Valid name but no data record of requested type" },
	{ DNSR_TEMP,	"query failed: try again" },
	{ DNSR_PERM,	"query failed: don't retry" },
	{ DNSR_TIMEOUT,	"query timed out" },
	{ DNSR_REFUSED,	"query refused by nameserver" },
	{ DNSR_MXINVALID,	"MX record invalid"	},
	{ DNSR_PTRINVALID,	"PTR record invalid"	},
	{ DNSR_CNINVALID,	"CNAME record invalid"	},
	{ 0,			NULL		}
};
#endif /* 0 */

char *
finde(int ecode, e2s_P table)
{
	int i;

	i = 0;
	while (table[i].e2s_code != 0 || table[i].e2s_str != NULL)
	{
		if (table[i].e2s_code == ecode)
			return table[i].e2s_str;
		if (sm_error_value(table[i].e2s_code) == sm_error_value(ecode))
			return table[i].e2s_str;
		++i;
	}
	return NULL;
}

char *
smmod2txt(sm_error_T err)
{
	char *errtxt;

	errtxt = finde(sm_error_module(err), e_modules);
	return (errtxt != NULL) ? errtxt : SM_ETXT_UNKNOWN;
}

char *
smerr2txt(sm_error_T err)
{
	int errornumber, eno;
	char *errtxt;

	eno = errornumber = sm_error_value(err);
	errtxt = NULL;

	if (err == SM_IO_EOF)
		errtxt = "EOF";
	else if (errornumber > 0 && errornumber < sm_sys_nerr)
		errtxt = strerror(errornumber);
	else if (SM_IS_BDB_ERR(err))
#if 0
		/*
		**  If this is used then every application needs to link
		**  against libdb.
		*/

		errtxt = db_strerror(eno);
#else
		errtxt = finde(eno, e_bdb4_2);
#endif
#if 0
	else if (errornumber >= DNS_ERR_BASE && errornumber < R2821_BASE)
		errtxt = finde(err, e_dns);
#endif /* 0 */
	else
		errtxt = finde(errornumber, e_error);
	return (errtxt != NULL) ? errtxt : SM_ETXT_UNKNOWN;
}


syntax highlighted by Code2HTML, v. 0.9.1