/** 
 * XML Security Library (http://www.aleksey.com/xmlsec).
 *
 * Error codes and error reporting functions.
 *
 * This is free software; see Copyright file in the source
 * distribution for preciese wording.
 * 
 * Copyright (C) 2002-2003 Aleksey Sanin <aleksey@aleksey.com>
 */
#include "globals.h"

#include <stdlib.h>
#include <stdio.h>
#include <stdarg.h>
#include <time.h>

#include <libxml/tree.h>

#include <xmlsec/xmlsec.h>
#include <xmlsec/xmltree.h>
#include <xmlsec/private.h>
#include <xmlsec/errors.h>

#define XMLSEC_ERRORS_BUFFER_SIZE	1024

typedef struct _xmlSecErrorDescription			xmlSecErrorDescription, *xmlSecErrorDescriptionPtr;
struct _xmlSecErrorDescription {
    int 		errorCode;
    const char*		errorMsg;
};

static xmlSecErrorDescription xmlSecErrorsTable[XMLSEC_ERRORS_MAX_NUMBER + 1] = {
  { XMLSEC_ERRORS_R_XMLSEC_FAILED, 		"xmlsec library function failed" }, 
  { XMLSEC_ERRORS_R_MALLOC_FAILED, 		"malloc function failed" }, 
  { XMLSEC_ERRORS_R_STRDUP_FAILED, 		"strdup function failed" }, 
  { XMLSEC_ERRORS_R_CRYPTO_FAILED, 		"crypto library function failed" }, 
  { XMLSEC_ERRORS_R_XML_FAILED, 		"libxml2 library function failed" }, 
  { XMLSEC_ERRORS_R_XSLT_FAILED, 		"libxslt library function failed" }, 
  { XMLSEC_ERRORS_R_IO_FAILED, 			"io function failed" }, 
  { XMLSEC_ERRORS_R_DISABLED, 			"feature is disabled" }, 
  { XMLSEC_ERRORS_R_NOT_IMPLEMENTED, 		"feature is not implemented" }, 
  { XMLSEC_ERRORS_R_INVALID_SIZE, 		"invalid size" }, 
  { XMLSEC_ERRORS_R_INVALID_DATA, 		"invalid data" }, 
  { XMLSEC_ERRORS_R_INVALID_RESULT, 		"invalid result" }, 
  { XMLSEC_ERRORS_R_INVALID_TYPE, 		"invalid type" }, 
  { XMLSEC_ERRORS_R_INVALID_OPERATION, 		"invalid operation" }, 
  { XMLSEC_ERRORS_R_INVALID_STATUS, 		"invalid status" }, 
  { XMLSEC_ERRORS_R_INVALID_FORMAT, 		"invalid format" }, 
  { XMLSEC_ERRORS_R_DATA_NOT_MATCH, 		"data do not match" }, 
  { XMLSEC_ERRORS_R_INVALID_NODE, 		"invalid node" }, 
  { XMLSEC_ERRORS_R_INVALID_NODE_CONTENT, 	"invalid node content" }, 
  { XMLSEC_ERRORS_R_INVALID_NODE_ATTRIBUTE, 	"invalid node attribute" }, 
  { XMLSEC_ERRORS_R_MISSING_NODE_ATTRIBUTE, 	"missing node attribute" }, 
  { XMLSEC_ERRORS_R_NODE_ALREADY_PRESENT, 	"node already present" }, 
  { XMLSEC_ERRORS_R_UNEXPECTED_NODE, 		"unexpected node" }, 
  { XMLSEC_ERRORS_R_NODE_NOT_FOUND, 		"node node found" }, 
  { XMLSEC_ERRORS_R_INVALID_TRANSFORM, 		"invalid transform" }, 
  { XMLSEC_ERRORS_R_INVALID_TRANSFORM_KEY, 	"invalid transform key" }, 
  { XMLSEC_ERRORS_R_INVALID_URI_TYPE, 		"invalid URI type" }, 
  { XMLSEC_ERRORS_R_TRANSFORM_SAME_DOCUMENT_REQUIRED, 	"same document is required for transform" }, 
  { XMLSEC_ERRORS_R_TRANSFORM_DISABLED, 	"transform is disabled" }, 
  { XMLSEC_ERRORS_R_INVALID_KEY_DATA, 		"invalid key data" }, 
  { XMLSEC_ERRORS_R_KEY_DATA_NOT_FOUND, 	"key data is not found" }, 
  { XMLSEC_ERRORS_R_KEY_DATA_ALREADY_EXIST, 	"key data already exist" }, 
  { XMLSEC_ERRORS_R_INVALID_KEY_DATA_SIZE, 	"invalid key data size" }, 
  { XMLSEC_ERRORS_R_KEY_NOT_FOUND, 		"key is not found" }, 
  { XMLSEC_ERRORS_R_KEYDATA_DISABLED, 		"key data is disabled" }, 
  { XMLSEC_ERRORS_R_MAX_RETRIEVALS_LEVEL, 	"maximum key retrieval level" }, 
  { XMLSEC_ERRORS_R_MAX_RETRIEVAL_TYPE_MISMATCH,"key retrieval type mismatch" }, 
  { XMLSEC_ERRORS_R_MAX_ENCKEY_LEVEL, 		"maximum encrypted key level" }, 
  { XMLSEC_ERRORS_R_CERT_VERIFY_FAILED, 	"certificate verification failed" }, 
  { XMLSEC_ERRORS_R_CERT_NOT_FOUND, 		"certificate is not found" }, 
  { XMLSEC_ERRORS_R_CERT_REVOKED, 		"certificate is revoked" }, 
  { XMLSEC_ERRORS_R_CERT_ISSUER_FAILED, 	"certificate issuer check failed" }, 
  { XMLSEC_ERRORS_R_CERT_NOT_YET_VALID, 	"certificate is not yet valid" }, 
  { XMLSEC_ERRORS_R_CERT_HAS_EXPIRED, 		"certificate has expirred" }, 
  { XMLSEC_ERRORS_R_DSIG_NO_REFERENCES, 	"Reference nodes are not found" }, 
  { XMLSEC_ERRORS_R_DSIG_INVALID_REFERENCE, 	"Reference verification failed" }, 
  { XMLSEC_ERRORS_R_ASSERTION, 			"assertion" }, 
  { 0,						NULL}
};

static xmlSecErrorsCallback xmlSecErrorsClbk = xmlSecErrorsDefaultCallback;
static int  xmlSecPrintErrorMessages = 1;	/* whether the error messages will be printed immidiatelly */

/** 
 * xmlSecErrorsInit:
 *
 * Initializes the errors reporting. It is called from #xmlSecInit function.
 * and applications must not call this function directly.
 */
void 
xmlSecErrorsInit(void) {
}

/** 
 * xmlSecErrorsShutdown:
 *
 * Cleanups the errors reporting. It is called from #xmlSecShutdown function.
 * and applications must not call this function directly.
 */
void 
xmlSecErrorsShutdown(void) {
}

/**
 * xmlSecErrorsSetCallback:
 * @callback: 		the new errors callback function.
 *
 * Sets the errors callback function to @callback that will be called 
 * every time an error occurs.
 */
void 
xmlSecErrorsSetCallback(xmlSecErrorsCallback callback) {
    xmlSecErrorsClbk = callback;
}

/**
 * xmlSecErrorsDefaultCallback:
 * @file:		the error location file name (__FILE__ macro).
 * @line:		the error location line number (__LINE__ macro).
 * @func:		the error location function name (__FUNCTION__ macro).
 * @errorObject:	the error specific error object 
 * @errorSubject:	the error specific error subject.
 * @reason:		the error code.
 * @msg:		the additional error message.
 *
 * The default error reporting callback that utilizes LibXML
 * error reporting #xmlGenericError function.
 */
void 
xmlSecErrorsDefaultCallback(const char* file, int line, const char* func,
			    const char* errorObject, const char* errorSubject,
			    int reason, const char* msg) {
    if(xmlSecPrintErrorMessages) {    
	const char* error_msg = NULL;
	xmlSecSize i;

	for(i = 0; (i < XMLSEC_ERRORS_MAX_NUMBER) && (xmlSecErrorsGetMsg(i) != NULL); ++i) {
	    if(xmlSecErrorsGetCode(i) == reason) {
		error_msg = xmlSecErrorsGetMsg(i);
		break;
	    }
	}
	xmlGenericError(xmlGenericErrorContext,
	    "func=%s:file=%s:line=%d:obj=%s:subj=%s:error=%d:%s:%s\n",
	    (func != NULL) ? func : "unknown",
	    (file != NULL) ? file : "unknown",
	    line,
	    (errorObject != NULL) ? errorObject : "unknown",
	    (errorSubject != NULL) ? errorSubject : "unknown",
	    reason,
	    (error_msg != NULL) ? error_msg : "",
	    (msg != NULL) ? msg : "");
    }
}

/**
 * xmlSecErrorsDefaultCallbackEnableOutput:
 * @enabled:		the flag.
 * 
 * Enables or disables calling LibXML2 callback from the default
 * errors callback.
 */
void 
xmlSecErrorsDefaultCallbackEnableOutput(int enabled) {
    xmlSecPrintErrorMessages = enabled;
}

/**
 * xmlSecErrorsGetCode:
 * @pos:		the error position.
 * 
 * Gets the known error code at position @pos.
 *
 * Returns: the known error code or 0 if @pos is greater than 
 * total number of known error codes.
 */
int 
xmlSecErrorsGetCode(xmlSecSize pos) {
    /* could not use asserts here! */
    if(pos < sizeof(xmlSecErrorsTable) / sizeof(xmlSecErrorsTable[0])) {
	return(xmlSecErrorsTable[pos].errorCode);
    }
    return(0);
}

/**
 * xmlSecErrorsGetMsg:
 * @pos:		the error position.
 *
 * Gets the known error message at position @pos.
 *
 * Returns: the known error message or NULL if @pos is greater than 
 * total number of known error codes.
 */
const char* 
xmlSecErrorsGetMsg(xmlSecSize pos) {
    /* could not use asserts here! */
    if(pos < sizeof(xmlSecErrorsTable) / sizeof(xmlSecErrorsTable[0])) {
	return(xmlSecErrorsTable[pos].errorMsg);
    }
    return(NULL);
}

/**
 * xmlSecError:
 * @file: 		the error location filename (__FILE__).
 * @line: 		the error location line number (__LINE__).
 * @func: 		the error location function (__FUNCTIION__).
 * @errorObject:	the error specific error object 
 * @errorSubject:	the error specific error subject. 
 * @reason: 		the error code.
 * @msg: 		the error message in printf format.
 * @...: 		the parameters for the @msg.
 *
 * Reports an error to the default (#xmlSecErrorsDefaultCallback) or 
 * application specific callback installed using #xmlSecErrorsSetCallback 
 * function.
 */
void	
xmlSecError(const char* file, int line, const char* func, 
	    const char* errorObject, const char* errorSubject,
  	    int reason, const char* msg, ...) {
	    
    if(xmlSecErrorsClbk != NULL) {
	xmlChar error_msg[XMLSEC_ERRORS_BUFFER_SIZE];
	
	if(msg != NULL) {
	    va_list va;

	    va_start(va, msg);
  	    xmlSecStrVPrintf(error_msg, sizeof(error_msg), BAD_CAST msg, va);
	    error_msg[sizeof(error_msg) - 1] = '\0';
	    va_end(va);	
	} else {
	    error_msg[0] = '\0';	    
	}
	xmlSecErrorsClbk(file, line, func, errorObject, errorSubject, reason, (char*)error_msg);
    }	
}
