#define _GNU_SOURCE
#include <dlfcn.h>

#include "config.h"

#include <stdlib.h>
#include <string.h>
#include <stdio.h>

#define CK_PTR *
#define CK_DEFINE_FUNCTION(returnType, name) returnType name
#define CK_DECLARE_FUNCTION(returnType, name) returnType name
#define CK_DECLARE_FUNCTION_POINTER(returnType, name) returnType (* name)
#define CK_CALLBACK_FUNCTION(returnType, name) returnType (* name)
#ifndef NULL_PTR
#  define NULL_PTR 0
#endif

#include "./pkcs11/pkcs11.h"

static void *libcackey_wrap_handle = NULL_PTR;

static void libcackey_wrap_init(void) {
	Dl_info libinfo;
	int dladdr_ret;
	char *library, *libraryDir, *libraryDirLastSlash;

	if (libcackey_wrap_handle) {
		return;
	}

	dladdr_ret = dladdr(libcackey_wrap_init, &libinfo);
	if (dladdr_ret == 0) {
		fprintf(stderr, "Unable to resolve path: %s\n", dlerror());

		abort();

		return;
	}

	if (!libinfo.dli_fname) {
		fprintf(stderr, "Unable to lookup filename\n");

		abort();

		return;
	}

	libraryDir = strdup(libinfo.dli_fname);
	libraryDirLastSlash = strrchr(libraryDir, '/');
	if (!libraryDirLastSlash) {
		fprintf(stderr, "File name returned is not an absolute path: %s\n", libraryDir);

		abort();

		return;
	}
	*libraryDirLastSlash = '\0';

	asprintf(&library, "%s/libcackey.so", libraryDir);

	libcackey_wrap_handle = dlmopen(LM_ID_NEWLM, library, RTLD_LOCAL | RTLD_NOW);

	if (!libcackey_wrap_handle) {
		fprintf(stderr, "Unable to load \"%s\": %s\n", library, dlerror());

		abort();

		return;
	}

	free(library);

	return;
}

CK_DEFINE_FUNCTION(CK_RV, C_Initialize)(CK_VOID_PTR pInitArgs) {
	CK_RV (*func)(CK_VOID_PTR pInitArgs);

	libcackey_wrap_init();

	func = dlsym(libcackey_wrap_handle, "C_Initialize");

	return(func(pInitArgs));
}

CK_DEFINE_FUNCTION(CK_RV, C_Finalize)(CK_VOID_PTR pReserved) {
	CK_RV (*func)(CK_VOID_PTR pReserved);

	libcackey_wrap_init();

	func = dlsym(libcackey_wrap_handle, "C_Finalize");

	return(func(pReserved));
}

CK_DEFINE_FUNCTION(CK_RV, C_GetInfo)(CK_INFO_PTR pInfo) {
	CK_RV (*func)(CK_INFO_PTR pInfo);

	libcackey_wrap_init();

	func = dlsym(libcackey_wrap_handle, "C_GetInfo");

	return(func(pInfo));
}

CK_DEFINE_FUNCTION(CK_RV, C_GetSlotList)(CK_BBOOL tokenPresent, CK_SLOT_ID_PTR pSlotList, CK_ULONG_PTR pulCount) {
	CK_RV (*func)(CK_BBOOL tokenPresent, CK_SLOT_ID_PTR pSlotList, CK_ULONG_PTR pulCount);

	libcackey_wrap_init();

	func = dlsym(libcackey_wrap_handle, "C_GetSlotList");

	return(func(tokenPresent, pSlotList, pulCount));
}

CK_DEFINE_FUNCTION(CK_RV, C_GetSlotInfo)(CK_SLOT_ID slotID, CK_SLOT_INFO_PTR pInfo) {
	CK_RV (*func)(CK_SLOT_ID slotID, CK_SLOT_INFO_PTR pInfo);

	libcackey_wrap_init();

	func = dlsym(libcackey_wrap_handle, "C_GetSlotInfo");

	return(func(slotID, pInfo));
}

CK_DEFINE_FUNCTION(CK_RV, C_GetTokenInfo)(CK_SLOT_ID slotID, CK_TOKEN_INFO_PTR pInfo) {
	CK_RV (*func)(CK_SLOT_ID slotID, CK_TOKEN_INFO_PTR pInfo);

	libcackey_wrap_init();

	func = dlsym(libcackey_wrap_handle, "C_GetTokenInfo");

	return(func(slotID, pInfo));
}

CK_DEFINE_FUNCTION(CK_RV, C_WaitForSlotEvent)(CK_FLAGS flags, CK_SLOT_ID_PTR pSlotID, CK_VOID_PTR pReserved) {
	CK_RV (*func)(CK_FLAGS flags, CK_SLOT_ID_PTR pSlotID, CK_VOID_PTR pReserved);

	libcackey_wrap_init();

	func = dlsym(libcackey_wrap_handle, "C_WaitForSlotEvent");

	return(func(flags, pSlotID, pReserved));
}

CK_DEFINE_FUNCTION(CK_RV, C_GetMechanismList)(CK_SLOT_ID slotID, CK_MECHANISM_TYPE_PTR pMechanismList, CK_ULONG_PTR pulCount) {
	CK_RV (*func)(CK_SLOT_ID slotID, CK_MECHANISM_TYPE_PTR pMechanismList, CK_ULONG_PTR pulCount);

	libcackey_wrap_init();

	func = dlsym(libcackey_wrap_handle, "C_GetMechanismList");

	return(func(slotID, pMechanismList, pulCount));
}

CK_DEFINE_FUNCTION(CK_RV, C_GetMechanismInfo)(CK_SLOT_ID slotID, CK_MECHANISM_TYPE type, CK_MECHANISM_INFO_PTR pInfo) {
	CK_RV (*func)(CK_SLOT_ID slotID, CK_MECHANISM_TYPE type, CK_MECHANISM_INFO_PTR pInfo);

	libcackey_wrap_init();

	func = dlsym(libcackey_wrap_handle, "C_GetMechanismInfo");

	return(func(slotID, type, pInfo));
}

CK_DEFINE_FUNCTION(CK_RV, C_InitToken)(CK_SLOT_ID slotID, CK_UTF8CHAR_PTR pPin, CK_ULONG ulPinLen, CK_UTF8CHAR_PTR pLabel) {
	CK_RV (*func)(CK_SLOT_ID slotID, CK_UTF8CHAR_PTR pPin, CK_ULONG ulPinLen, CK_UTF8CHAR_PTR pLabel);

	libcackey_wrap_init();

	func = dlsym(libcackey_wrap_handle, "C_InitToken");

	return(func(slotID, pPin, ulPinLen, pLabel));
}

CK_DEFINE_FUNCTION(CK_RV, C_InitPIN)(CK_SESSION_HANDLE hSession, CK_UTF8CHAR_PTR pPin, CK_ULONG ulPinLen) {
	CK_RV (*func)(CK_SESSION_HANDLE hSession, CK_UTF8CHAR_PTR pPin, CK_ULONG ulPinLen);

	libcackey_wrap_init();

	func = dlsym(libcackey_wrap_handle, "C_InitPIN");

	return(func(hSession, pPin, ulPinLen));
}

CK_DEFINE_FUNCTION(CK_RV, C_SetPIN)(CK_SESSION_HANDLE hSession, CK_UTF8CHAR_PTR pOldPin, CK_ULONG ulOldPinLen, CK_UTF8CHAR_PTR pNewPin, CK_ULONG ulNewPinLen) {
	CK_RV (*func)(CK_SESSION_HANDLE hSession, CK_UTF8CHAR_PTR pOldPin, CK_ULONG ulOldPinLen, CK_UTF8CHAR_PTR pNewPin, CK_ULONG ulNewPinLen);

	libcackey_wrap_init();

	func = dlsym(libcackey_wrap_handle, "C_SetPIN");

	return(func(hSession, pOldPin, ulOldPinLen, pNewPin, ulNewPinLen));
}

CK_DEFINE_FUNCTION(CK_RV, C_OpenSession)(CK_SLOT_ID slotID, CK_FLAGS flags, CK_VOID_PTR pApplication, CK_NOTIFY notify, CK_SESSION_HANDLE_PTR phSession) {
	CK_RV (*func)(CK_SLOT_ID slotID, CK_FLAGS flags, CK_VOID_PTR pApplication, CK_NOTIFY notify, CK_SESSION_HANDLE_PTR phSession);

	libcackey_wrap_init();

	func = dlsym(libcackey_wrap_handle, "C_OpenSession");

	return(func(slotID, flags, pApplication, notify, phSession));
}

CK_DEFINE_FUNCTION(CK_RV, C_CloseSession)(CK_SESSION_HANDLE hSession) {
	CK_RV (*func)(CK_SESSION_HANDLE hSession);

	libcackey_wrap_init();

	func = dlsym(libcackey_wrap_handle, "C_CloseSession");

	return(func(hSession));
}

CK_DEFINE_FUNCTION(CK_RV, C_CloseAllSessions)(CK_SLOT_ID slotID) {
	CK_RV (*func)(CK_SLOT_ID slotID);

	libcackey_wrap_init();

	func = dlsym(libcackey_wrap_handle, "C_CloseAllSessions");

	return(func(slotID));
}

CK_DEFINE_FUNCTION(CK_RV, C_GetSessionInfo)(CK_SESSION_HANDLE hSession, CK_SESSION_INFO_PTR pInfo) {
	CK_RV (*func)(CK_SESSION_HANDLE hSession, CK_SESSION_INFO_PTR pInfo);

	libcackey_wrap_init();

	func = dlsym(libcackey_wrap_handle, "C_GetSessionInfo");

	return(func(hSession, pInfo));
}

CK_DEFINE_FUNCTION(CK_RV, C_GetOperationState)(CK_SESSION_HANDLE hSession, CK_BYTE_PTR pOperationState, CK_ULONG_PTR pulOperationStateLen) {
	CK_RV (*func)(CK_SESSION_HANDLE hSession, CK_BYTE_PTR pOperationState, CK_ULONG_PTR pulOperationStateLen);

	libcackey_wrap_init();

	func = dlsym(libcackey_wrap_handle, "C_GetOperationState");

	return(func(hSession, pOperationState, pulOperationStateLen));
}

CK_DEFINE_FUNCTION(CK_RV, C_SetOperationState)(CK_SESSION_HANDLE hSession, CK_BYTE_PTR pOperationState, CK_ULONG ulOperationStateLen, CK_OBJECT_HANDLE hEncryptionKey, CK_OBJECT_HANDLE hAuthenticationKey) {
	CK_RV (*func)(CK_SESSION_HANDLE hSession, CK_BYTE_PTR pOperationState, CK_ULONG ulOperationStateLen, CK_OBJECT_HANDLE hEncryptionKey, CK_OBJECT_HANDLE hAuthenticationKey);

	libcackey_wrap_init();

	func = dlsym(libcackey_wrap_handle, "C_SetOperationState");

	return(func(hSession, pOperationState, ulOperationStateLen, hEncryptionKey, hAuthenticationKey));
}

CK_DEFINE_FUNCTION(CK_RV, C_LoginMutexArg)(CK_SESSION_HANDLE hSession, CK_USER_TYPE userType, CK_UTF8CHAR_PTR pPin, CK_ULONG ulPinLen, int lock_mutex) {
	CK_RV (*func)(CK_SESSION_HANDLE hSession, CK_USER_TYPE userType, CK_UTF8CHAR_PTR pPin, CK_ULONG ulPinLen, int lock_mutex);

	libcackey_wrap_init();

	func = dlsym(libcackey_wrap_handle, "C_LoginMutexArg");

	return(func(hSession, userType, pPin, ulPinLen, lock_mutex));
}

CK_DEFINE_FUNCTION(CK_RV, C_Login)(CK_SESSION_HANDLE hSession, CK_USER_TYPE userType, CK_UTF8CHAR_PTR pPin, CK_ULONG ulPinLen) {
	CK_RV (*func)(CK_SESSION_HANDLE hSession, CK_USER_TYPE userType, CK_UTF8CHAR_PTR pPin, CK_ULONG ulPinLen);

	libcackey_wrap_init();

	func = dlsym(libcackey_wrap_handle, "C_Login");

	return(func(hSession, userType, pPin, ulPinLen));
}

CK_DEFINE_FUNCTION(CK_RV, C_Logout)(CK_SESSION_HANDLE hSession) {
	CK_RV (*func)(CK_SESSION_HANDLE hSession);

	libcackey_wrap_init();

	func = dlsym(libcackey_wrap_handle, "C_Logout");

	return(func(hSession));
}

CK_DEFINE_FUNCTION(CK_RV, C_CreateObject)(CK_SESSION_HANDLE hSession, CK_ATTRIBUTE_PTR pTemplate, CK_ULONG ulCount, CK_OBJECT_HANDLE_PTR phObject) {
	CK_RV (*func)(CK_SESSION_HANDLE hSession, CK_ATTRIBUTE_PTR pTemplate, CK_ULONG ulCount, CK_OBJECT_HANDLE_PTR phObject);

	libcackey_wrap_init();

	func = dlsym(libcackey_wrap_handle, "C_CreateObject");

	return(func(hSession, pTemplate, ulCount, phObject));
}

CK_DEFINE_FUNCTION(CK_RV, C_CopyObject)(CK_SESSION_HANDLE hSession, CK_OBJECT_HANDLE hObject, CK_ATTRIBUTE_PTR pTemplate, CK_ULONG ulCount, CK_OBJECT_HANDLE_PTR phNewObject) {
	CK_RV (*func)(CK_SESSION_HANDLE hSession, CK_OBJECT_HANDLE hObject, CK_ATTRIBUTE_PTR pTemplate, CK_ULONG ulCount, CK_OBJECT_HANDLE_PTR phNewObject);

	libcackey_wrap_init();

	func = dlsym(libcackey_wrap_handle, "C_CopyObject");

	return(func(hSession, hObject, pTemplate, ulCount, phNewObject));
}

CK_DEFINE_FUNCTION(CK_RV, C_DestroyObject)(CK_SESSION_HANDLE hSession, CK_OBJECT_HANDLE hObject) {
	CK_RV (*func)(CK_SESSION_HANDLE hSession, CK_OBJECT_HANDLE hObject);

	libcackey_wrap_init();

	func = dlsym(libcackey_wrap_handle, "C_DestroyObject");

	return(func(hSession, hObject));
}

CK_DEFINE_FUNCTION(CK_RV, C_GetObjectSize)(CK_SESSION_HANDLE hSession, CK_OBJECT_HANDLE hObject, CK_ULONG_PTR pulSize) {
	CK_RV (*func)(CK_SESSION_HANDLE hSession, CK_OBJECT_HANDLE hObject, CK_ULONG_PTR pulSize);

	libcackey_wrap_init();

	func = dlsym(libcackey_wrap_handle, "C_GetObjectSize");

	return(func(hSession, hObject, pulSize));
}

CK_DEFINE_FUNCTION(CK_RV, C_GetAttributeValue)(CK_SESSION_HANDLE hSession, CK_OBJECT_HANDLE hObject, CK_ATTRIBUTE_PTR pTemplate, CK_ULONG ulCount) {
	CK_RV (*func)(CK_SESSION_HANDLE hSession, CK_OBJECT_HANDLE hObject, CK_ATTRIBUTE_PTR pTemplate, CK_ULONG ulCount);

	libcackey_wrap_init();

	func = dlsym(libcackey_wrap_handle, "C_GetAttributeValue");

	return(func(hSession, hObject, pTemplate, ulCount));
}

CK_DEFINE_FUNCTION(CK_RV, C_SetAttributeValue)(CK_SESSION_HANDLE hSession, CK_OBJECT_HANDLE hObject, CK_ATTRIBUTE_PTR pTemplate, CK_ULONG ulCount) {
	CK_RV (*func)(CK_SESSION_HANDLE hSession, CK_OBJECT_HANDLE hObject, CK_ATTRIBUTE_PTR pTemplate, CK_ULONG ulCount);

	libcackey_wrap_init();

	func = dlsym(libcackey_wrap_handle, "C_SetAttributeValue");

	return(func(hSession, hObject, pTemplate, ulCount));
}

CK_DEFINE_FUNCTION(CK_RV, C_FindObjectsInit)(CK_SESSION_HANDLE hSession, CK_ATTRIBUTE_PTR pTemplate, CK_ULONG ulCount) {
	CK_RV (*func)(CK_SESSION_HANDLE hSession, CK_ATTRIBUTE_PTR pTemplate, CK_ULONG ulCount);

	libcackey_wrap_init();

	func = dlsym(libcackey_wrap_handle, "C_FindObjectsInit");

	return(func(hSession, pTemplate, ulCount));
}

CK_DEFINE_FUNCTION(CK_RV, C_FindObjects)(CK_SESSION_HANDLE hSession, CK_OBJECT_HANDLE_PTR phObject, CK_ULONG ulMaxObjectCount, CK_ULONG_PTR pulObjectCount) {
	CK_RV (*func)(CK_SESSION_HANDLE hSession, CK_OBJECT_HANDLE_PTR phObject, CK_ULONG ulMaxObjectCount, CK_ULONG_PTR pulObjectCount);

	libcackey_wrap_init();

	func = dlsym(libcackey_wrap_handle, "C_FindObjects");

	return(func(hSession, phObject, ulMaxObjectCount, pulObjectCount));
}

CK_DEFINE_FUNCTION(CK_RV, C_FindObjectsFinal)(CK_SESSION_HANDLE hSession) {
	CK_RV (*func)(CK_SESSION_HANDLE hSession);

	libcackey_wrap_init();

	func = dlsym(libcackey_wrap_handle, "C_FindObjectsFinal");

	return(func(hSession));
}

CK_DEFINE_FUNCTION(CK_RV, C_EncryptInit)(CK_SESSION_HANDLE hSession, CK_MECHANISM_PTR pMechanism, CK_OBJECT_HANDLE hKey) {
	CK_RV (*func)(CK_SESSION_HANDLE hSession, CK_MECHANISM_PTR pMechanism, CK_OBJECT_HANDLE hKey);

	libcackey_wrap_init();

	func = dlsym(libcackey_wrap_handle, "C_EncryptInit");

	return(func(hSession, pMechanism, hKey));
}

CK_DEFINE_FUNCTION(CK_RV, C_Encrypt)(CK_SESSION_HANDLE hSession, CK_BYTE_PTR pData, CK_ULONG ulDataLen, CK_BYTE_PTR pEncryptedData, CK_ULONG_PTR pulEncryptedDataLen) {
	CK_RV (*func)(CK_SESSION_HANDLE hSession, CK_BYTE_PTR pData, CK_ULONG ulDataLen, CK_BYTE_PTR pEncryptedData, CK_ULONG_PTR pulEncryptedDataLen);

	libcackey_wrap_init();

	func = dlsym(libcackey_wrap_handle, "C_Encrypt");

	return(func(hSession, pData, ulDataLen, pEncryptedData, pulEncryptedDataLen));
}

CK_DEFINE_FUNCTION(CK_RV, C_EncryptUpdate)(CK_SESSION_HANDLE hSession, CK_BYTE_PTR pPart, CK_ULONG ulPartLen, CK_BYTE_PTR pEncryptedPart, CK_ULONG_PTR pulEncryptedPartLen) {
	CK_RV (*func)(CK_SESSION_HANDLE hSession, CK_BYTE_PTR pPart, CK_ULONG ulPartLen, CK_BYTE_PTR pEncryptedPart, CK_ULONG_PTR pulEncryptedPartLen);

	libcackey_wrap_init();

	func = dlsym(libcackey_wrap_handle, "C_EncryptUpdate");

	return(func(hSession, pPart, ulPartLen, pEncryptedPart, pulEncryptedPartLen));
}

CK_DEFINE_FUNCTION(CK_RV, C_EncryptFinal)(CK_SESSION_HANDLE hSession, CK_BYTE_PTR pLastEncryptedPart, CK_ULONG_PTR pulLastEncryptedPartLen) {
	CK_RV (*func)(CK_SESSION_HANDLE hSession, CK_BYTE_PTR pLastEncryptedPart, CK_ULONG_PTR pulLastEncryptedPartLen);

	libcackey_wrap_init();

	func = dlsym(libcackey_wrap_handle, "C_EncryptFinal");

	return(func(hSession, pLastEncryptedPart, pulLastEncryptedPartLen));
}

CK_DEFINE_FUNCTION(CK_RV, C_DecryptInit)(CK_SESSION_HANDLE hSession, CK_MECHANISM_PTR pMechanism, CK_OBJECT_HANDLE hKey) {
	CK_RV (*func)(CK_SESSION_HANDLE hSession, CK_MECHANISM_PTR pMechanism, CK_OBJECT_HANDLE hKey);

	libcackey_wrap_init();

	func = dlsym(libcackey_wrap_handle, "C_DecryptInit");

	return(func(hSession, pMechanism, hKey));
}

CK_DEFINE_FUNCTION(CK_RV, C_Decrypt)(CK_SESSION_HANDLE hSession, CK_BYTE_PTR pEncryptedData, CK_ULONG ulEncryptedDataLen, CK_BYTE_PTR pData, CK_ULONG_PTR pulDataLen) {
	CK_RV (*func)(CK_SESSION_HANDLE hSession, CK_BYTE_PTR pEncryptedData, CK_ULONG ulEncryptedDataLen, CK_BYTE_PTR pData, CK_ULONG_PTR pulDataLen);

	libcackey_wrap_init();

	func = dlsym(libcackey_wrap_handle, "C_Decrypt");

	return(func(hSession, pEncryptedData, ulEncryptedDataLen, pData, pulDataLen));
}

CK_DEFINE_FUNCTION(CK_RV, C_DecryptUpdate)(CK_SESSION_HANDLE hSession, CK_BYTE_PTR pEncryptedPart, CK_ULONG ulEncryptedPartLen, CK_BYTE_PTR pPart, CK_ULONG_PTR pulPartLen) {
	CK_RV (*func)(CK_SESSION_HANDLE hSession, CK_BYTE_PTR pEncryptedPart, CK_ULONG ulEncryptedPartLen, CK_BYTE_PTR pPart, CK_ULONG_PTR pulPartLen);

	libcackey_wrap_init();

	func = dlsym(libcackey_wrap_handle, "C_DecryptUpdate");

	return(func(hSession, pEncryptedPart, ulEncryptedPartLen, pPart, pulPartLen));
}

CK_DEFINE_FUNCTION(CK_RV, C_DecryptFinal)(CK_SESSION_HANDLE hSession, CK_BYTE_PTR pLastPart, CK_ULONG_PTR pulLastPartLen) {
	CK_RV (*func)(CK_SESSION_HANDLE hSession, CK_BYTE_PTR pLastPart, CK_ULONG_PTR pulLastPartLen);

	libcackey_wrap_init();

	func = dlsym(libcackey_wrap_handle, "C_DecryptFinal");

	return(func(hSession, pLastPart, pulLastPartLen));
}

CK_DEFINE_FUNCTION(CK_RV, C_DigestInit)(CK_SESSION_HANDLE hSession, CK_MECHANISM_PTR pMechanism) {
	CK_RV (*func)(CK_SESSION_HANDLE hSession, CK_MECHANISM_PTR pMechanism);

	libcackey_wrap_init();

	func = dlsym(libcackey_wrap_handle, "C_DigestInit");

	return(func(hSession, pMechanism));
}

CK_DEFINE_FUNCTION(CK_RV, C_Digest)(CK_SESSION_HANDLE hSession, CK_BYTE_PTR pData, CK_ULONG ulDataLen, CK_BYTE_PTR pDigest, CK_ULONG_PTR pulDigestLen) {
	CK_RV (*func)(CK_SESSION_HANDLE hSession, CK_BYTE_PTR pData, CK_ULONG ulDataLen, CK_BYTE_PTR pDigest, CK_ULONG_PTR pulDigestLen);

	libcackey_wrap_init();

	func = dlsym(libcackey_wrap_handle, "C_Digest");

	return(func(hSession, pData, ulDataLen, pDigest, pulDigestLen));
}

CK_DEFINE_FUNCTION(CK_RV, C_DigestUpdate)(CK_SESSION_HANDLE hSession, CK_BYTE_PTR pPart, CK_ULONG ulPartLen) {
	CK_RV (*func)(CK_SESSION_HANDLE hSession, CK_BYTE_PTR pPart, CK_ULONG ulPartLen);

	libcackey_wrap_init();

	func = dlsym(libcackey_wrap_handle, "C_DigestUpdate");

	return(func(hSession, pPart, ulPartLen));
}

CK_DEFINE_FUNCTION(CK_RV, C_DigestKey)(CK_SESSION_HANDLE hSession, CK_OBJECT_HANDLE hKey) {
	CK_RV (*func)(CK_SESSION_HANDLE hSession, CK_OBJECT_HANDLE hKey);

	libcackey_wrap_init();

	func = dlsym(libcackey_wrap_handle, "C_DigestKey");

	return(func(hSession, hKey));
}

CK_DEFINE_FUNCTION(CK_RV, C_DigestFinal)(CK_SESSION_HANDLE hSession, CK_BYTE_PTR pDigest, CK_ULONG_PTR pulDigestLen) {
	CK_RV (*func)(CK_SESSION_HANDLE hSession, CK_BYTE_PTR pDigest, CK_ULONG_PTR pulDigestLen);

	libcackey_wrap_init();

	func = dlsym(libcackey_wrap_handle, "C_DigestFinal");

	return(func(hSession, pDigest, pulDigestLen));
}

CK_DEFINE_FUNCTION(CK_RV, C_SignInit)(CK_SESSION_HANDLE hSession, CK_MECHANISM_PTR pMechanism, CK_OBJECT_HANDLE hKey) {
	CK_RV (*func)(CK_SESSION_HANDLE hSession, CK_MECHANISM_PTR pMechanism, CK_OBJECT_HANDLE hKey);

	libcackey_wrap_init();

	func = dlsym(libcackey_wrap_handle, "C_SignInit");

	return(func(hSession, pMechanism, hKey));
}

CK_DEFINE_FUNCTION(CK_RV, C_Sign)(CK_SESSION_HANDLE hSession, CK_BYTE_PTR pData, CK_ULONG ulDataLen, CK_BYTE_PTR pSignature, CK_ULONG_PTR pulSignatureLen) {
	CK_RV (*func)(CK_SESSION_HANDLE hSession, CK_BYTE_PTR pData, CK_ULONG ulDataLen, CK_BYTE_PTR pSignature, CK_ULONG_PTR pulSignatureLen);

	libcackey_wrap_init();

	func = dlsym(libcackey_wrap_handle, "C_Sign");

	return(func(hSession, pData, ulDataLen, pSignature, pulSignatureLen));
}

CK_DEFINE_FUNCTION(CK_RV, C_SignUpdate)(CK_SESSION_HANDLE hSession, CK_BYTE_PTR pPart, CK_ULONG ulPartLen) {
	CK_RV (*func)(CK_SESSION_HANDLE hSession, CK_BYTE_PTR pPart, CK_ULONG ulPartLen);

	libcackey_wrap_init();

	func = dlsym(libcackey_wrap_handle, "C_SignUpdate");

	return(func(hSession, pPart, ulPartLen));
}

CK_DEFINE_FUNCTION(CK_RV, C_SignFinal)(CK_SESSION_HANDLE hSession, CK_BYTE_PTR pSignature, CK_ULONG_PTR pulSignatureLen) {
	CK_RV (*func)(CK_SESSION_HANDLE hSession, CK_BYTE_PTR pSignature, CK_ULONG_PTR pulSignatureLen);

	libcackey_wrap_init();

	func = dlsym(libcackey_wrap_handle, "C_SignFinal");

	return(func(hSession, pSignature, pulSignatureLen));
}

CK_DEFINE_FUNCTION(CK_RV, C_SignRecoverInit)(CK_SESSION_HANDLE hSession, CK_MECHANISM_PTR pMechanism, CK_OBJECT_HANDLE hKey) {
	CK_RV (*func)(CK_SESSION_HANDLE hSession, CK_MECHANISM_PTR pMechanism, CK_OBJECT_HANDLE hKey);

	libcackey_wrap_init();

	func = dlsym(libcackey_wrap_handle, "C_SignRecoverInit");

	return(func(hSession, pMechanism, hKey));
}

CK_DEFINE_FUNCTION(CK_RV, C_SignRecover)(CK_SESSION_HANDLE hSession, CK_BYTE_PTR pData, CK_ULONG ulDataLen, CK_BYTE_PTR pSignature, CK_ULONG_PTR pulSignatureLen) {
	CK_RV (*func)(CK_SESSION_HANDLE hSession, CK_BYTE_PTR pData, CK_ULONG ulDataLen, CK_BYTE_PTR pSignature, CK_ULONG_PTR pulSignatureLen);

	libcackey_wrap_init();

	func = dlsym(libcackey_wrap_handle, "C_SignRecover");

	return(func(hSession, pData, ulDataLen, pSignature, pulSignatureLen));
}

CK_DEFINE_FUNCTION(CK_RV, C_VerifyInit)(CK_SESSION_HANDLE hSession, CK_MECHANISM_PTR pMechanism, CK_OBJECT_HANDLE hKey) {
	CK_RV (*func)(CK_SESSION_HANDLE hSession, CK_MECHANISM_PTR pMechanism, CK_OBJECT_HANDLE hKey);

	libcackey_wrap_init();

	func = dlsym(libcackey_wrap_handle, "C_VerifyInit");

	return(func(hSession, pMechanism, hKey));
}

CK_DEFINE_FUNCTION(CK_RV, C_Verify)(CK_SESSION_HANDLE hSession, CK_BYTE_PTR pData, CK_ULONG ulDataLen, CK_BYTE_PTR pSignature, CK_ULONG ulSignatureLen) {
	CK_RV (*func)(CK_SESSION_HANDLE hSession, CK_BYTE_PTR pData, CK_ULONG ulDataLen, CK_BYTE_PTR pSignature, CK_ULONG ulSignatureLen);

	libcackey_wrap_init();

	func = dlsym(libcackey_wrap_handle, "C_Verify");

	return(func(hSession, pData, ulDataLen, pSignature, ulSignatureLen));
}

CK_DEFINE_FUNCTION(CK_RV, C_VerifyUpdate)(CK_SESSION_HANDLE hSession, CK_BYTE_PTR pPart, CK_ULONG ulPartLen) {
	CK_RV (*func)(CK_SESSION_HANDLE hSession, CK_BYTE_PTR pPart, CK_ULONG ulPartLen);

	libcackey_wrap_init();

	func = dlsym(libcackey_wrap_handle, "C_VerifyUpdate");

	return(func(hSession, pPart, ulPartLen));
}

CK_DEFINE_FUNCTION(CK_RV, C_VerifyFinal)(CK_SESSION_HANDLE hSession, CK_BYTE_PTR pSignature, CK_ULONG ulSignatureLen) {
	CK_RV (*func)(CK_SESSION_HANDLE hSession, CK_BYTE_PTR pSignature, CK_ULONG ulSignatureLen);

	libcackey_wrap_init();

	func = dlsym(libcackey_wrap_handle, "C_VerifyFinal");

	return(func(hSession, pSignature, ulSignatureLen));
}

CK_DEFINE_FUNCTION(CK_RV, C_VerifyRecoverInit)(CK_SESSION_HANDLE hSession, CK_MECHANISM_PTR pMechanism, CK_OBJECT_HANDLE hKey) {
	CK_RV (*func)(CK_SESSION_HANDLE hSession, CK_MECHANISM_PTR pMechanism, CK_OBJECT_HANDLE hKey);

	libcackey_wrap_init();

	func = dlsym(libcackey_wrap_handle, "C_VerifyRecoverInit");

	return(func(hSession, pMechanism, hKey));
}

CK_DEFINE_FUNCTION(CK_RV, C_VerifyRecover)(CK_SESSION_HANDLE hSession, CK_BYTE_PTR pSignature, CK_ULONG ulSignatureLen, CK_BYTE_PTR pData, CK_ULONG_PTR pulDataLen) {
	CK_RV (*func)(CK_SESSION_HANDLE hSession, CK_BYTE_PTR pSignature, CK_ULONG ulSignatureLen, CK_BYTE_PTR pData, CK_ULONG_PTR pulDataLen);

	libcackey_wrap_init();

	func = dlsym(libcackey_wrap_handle, "C_VerifyRecover");

	return(func(hSession, pSignature, ulSignatureLen, pData, pulDataLen));
}

CK_DEFINE_FUNCTION(CK_RV, C_DigestEncryptUpdate)(CK_SESSION_HANDLE hSession, CK_BYTE_PTR pPart, CK_ULONG ulPartLen, CK_BYTE_PTR pEncryptedPart, CK_ULONG_PTR pulEncryptedPartLen) {
	CK_RV (*func)(CK_SESSION_HANDLE hSession, CK_BYTE_PTR pPart, CK_ULONG ulPartLen, CK_BYTE_PTR pEncryptedPart, CK_ULONG_PTR pulEncryptedPartLen);

	libcackey_wrap_init();

	func = dlsym(libcackey_wrap_handle, "C_DigestEncryptUpdate");

	return(func(hSession, pPart, ulPartLen, pEncryptedPart, pulEncryptedPartLen));
}

CK_DEFINE_FUNCTION(CK_RV, C_DecryptDigestUpdate)(CK_SESSION_HANDLE hSession, CK_BYTE_PTR pEncryptedPart, CK_ULONG ulEncryptedPartLen, CK_BYTE_PTR pPart, CK_ULONG_PTR pulPartLen) {
	CK_RV (*func)(CK_SESSION_HANDLE hSession, CK_BYTE_PTR pEncryptedPart, CK_ULONG ulEncryptedPartLen, CK_BYTE_PTR pPart, CK_ULONG_PTR pulPartLen);

	libcackey_wrap_init();

	func = dlsym(libcackey_wrap_handle, "C_DecryptDigestUpdate");

	return(func(hSession, pEncryptedPart, ulEncryptedPartLen, pPart, pulPartLen));
}

CK_DEFINE_FUNCTION(CK_RV, C_SignEncryptUpdate)(CK_SESSION_HANDLE hSession, CK_BYTE_PTR pPart, CK_ULONG ulPartLen, CK_BYTE_PTR pEncryptedPart, CK_ULONG_PTR pulEncryptedPartLen) {
	CK_RV (*func)(CK_SESSION_HANDLE hSession, CK_BYTE_PTR pPart, CK_ULONG ulPartLen, CK_BYTE_PTR pEncryptedPart, CK_ULONG_PTR pulEncryptedPartLen);

	libcackey_wrap_init();

	func = dlsym(libcackey_wrap_handle, "C_SignEncryptUpdate");

	return(func(hSession, pPart, ulPartLen, pEncryptedPart, pulEncryptedPartLen));
}

CK_DEFINE_FUNCTION(CK_RV, C_DecryptVerifyUpdate)(CK_SESSION_HANDLE hSession, CK_BYTE_PTR pEncryptedPart, CK_ULONG ulEncryptedPartLen, CK_BYTE_PTR pPart, CK_ULONG_PTR pulPartLen) {
	CK_RV (*func)(CK_SESSION_HANDLE hSession, CK_BYTE_PTR pEncryptedPart, CK_ULONG ulEncryptedPartLen, CK_BYTE_PTR pPart, CK_ULONG_PTR pulPartLen);

	libcackey_wrap_init();

	func = dlsym(libcackey_wrap_handle, "C_DecryptVerifyUpdate");

	return(func(hSession, pEncryptedPart, ulEncryptedPartLen, pPart, pulPartLen));
}

CK_DEFINE_FUNCTION(CK_RV, C_GenerateKey)(CK_SESSION_HANDLE hSession, CK_MECHANISM_PTR pMechanism, CK_ATTRIBUTE_PTR pTemplate, CK_ULONG ulCount, CK_OBJECT_HANDLE_PTR phKey) {
	CK_RV (*func)(CK_SESSION_HANDLE hSession, CK_MECHANISM_PTR pMechanism, CK_ATTRIBUTE_PTR pTemplate, CK_ULONG ulCount, CK_OBJECT_HANDLE_PTR phKey);

	libcackey_wrap_init();

	func = dlsym(libcackey_wrap_handle, "C_GenerateKey");

	return(func(hSession, pMechanism, pTemplate, ulCount, phKey));
}

CK_DEFINE_FUNCTION(CK_RV, C_GenerateKeyPair)(CK_SESSION_HANDLE hSession, CK_MECHANISM_PTR pMechanism, CK_ATTRIBUTE_PTR pPublicKeyTemplate, CK_ULONG ulPublicKeyAttributeCount, CK_ATTRIBUTE_PTR pPrivateKeyTemplate, CK_ULONG ulPrivateKeyAttributeCount, CK_OBJECT_HANDLE_PTR phPublicKey, CK_OBJECT_HANDLE_PTR phPrivateKey) {
	CK_RV (*func)(CK_SESSION_HANDLE hSession, CK_MECHANISM_PTR pMechanism, CK_ATTRIBUTE_PTR pPublicKeyTemplate, CK_ULONG ulPublicKeyAttributeCount, CK_ATTRIBUTE_PTR pPrivateKeyTemplate, CK_ULONG ulPrivateKeyAttributeCount, CK_OBJECT_HANDLE_PTR phPublicKey, CK_OBJECT_HANDLE_PTR phPrivateKey);

	libcackey_wrap_init();

	func = dlsym(libcackey_wrap_handle, "C_GenerateKeyPair");

	return(func(hSession, pMechanism, pPublicKeyTemplate, ulPublicKeyAttributeCount, pPrivateKeyTemplate, ulPrivateKeyAttributeCount, phPublicKey, phPrivateKey));
}

CK_DEFINE_FUNCTION(CK_RV, C_WrapKey)(CK_SESSION_HANDLE hSession, CK_MECHANISM_PTR pMechanism, CK_OBJECT_HANDLE hWrappingKey, CK_OBJECT_HANDLE hKey, CK_BYTE_PTR pWrappedKey, CK_ULONG_PTR pulWrappedKeyLen) {
	CK_RV (*func)(CK_SESSION_HANDLE hSession, CK_MECHANISM_PTR pMechanism, CK_OBJECT_HANDLE hWrappingKey, CK_OBJECT_HANDLE hKey, CK_BYTE_PTR pWrappedKey, CK_ULONG_PTR pulWrappedKeyLen);

	libcackey_wrap_init();

	func = dlsym(libcackey_wrap_handle, "C_WrapKey");

	return(func(hSession, pMechanism, hWrappingKey, hKey, pWrappedKey, pulWrappedKeyLen));
}

CK_DEFINE_FUNCTION(CK_RV, C_UnwrapKey)(CK_SESSION_HANDLE hSession, CK_MECHANISM_PTR pMechanism, CK_OBJECT_HANDLE hUnwrappingKey, CK_BYTE_PTR pWrappedKey, CK_ULONG ulWrappedKeyLen, CK_ATTRIBUTE_PTR pTemplate, CK_ULONG ulAttributeCount, CK_OBJECT_HANDLE_PTR phKey) {
	CK_RV (*func)(CK_SESSION_HANDLE hSession, CK_MECHANISM_PTR pMechanism, CK_OBJECT_HANDLE hUnwrappingKey, CK_BYTE_PTR pWrappedKey, CK_ULONG ulWrappedKeyLen, CK_ATTRIBUTE_PTR pTemplate, CK_ULONG ulAttributeCount, CK_OBJECT_HANDLE_PTR phKey);

	libcackey_wrap_init();

	func = dlsym(libcackey_wrap_handle, "C_UnwrapKey");

	return(func(hSession, pMechanism, hUnwrappingKey, pWrappedKey, ulWrappedKeyLen, pTemplate, ulAttributeCount, phKey));
}

CK_DEFINE_FUNCTION(CK_RV, C_DeriveKey)(CK_SESSION_HANDLE hSession, CK_MECHANISM_PTR pMechanism, CK_OBJECT_HANDLE hBaseKey, CK_ATTRIBUTE_PTR pTemplate, CK_ULONG ulAttributeCount, CK_OBJECT_HANDLE_PTR phKey) {
	CK_RV (*func)(CK_SESSION_HANDLE hSession, CK_MECHANISM_PTR pMechanism, CK_OBJECT_HANDLE hBaseKey, CK_ATTRIBUTE_PTR pTemplate, CK_ULONG ulAttributeCount, CK_OBJECT_HANDLE_PTR phKey);

	libcackey_wrap_init();

	func = dlsym(libcackey_wrap_handle, "C_DeriveKey");

	return(func(hSession, pMechanism, hBaseKey, pTemplate, ulAttributeCount, phKey));
}

CK_DEFINE_FUNCTION(CK_RV, C_SeedRandom)(CK_SESSION_HANDLE hSession, CK_BYTE_PTR pSeed, CK_ULONG ulSeedLen) {
	CK_RV (*func)(CK_SESSION_HANDLE hSession, CK_BYTE_PTR pSeed, CK_ULONG ulSeedLen);

	libcackey_wrap_init();

	func = dlsym(libcackey_wrap_handle, "C_SeedRandom");

	return(func(hSession, pSeed, ulSeedLen));
}

CK_DEFINE_FUNCTION(CK_RV, C_GenerateRandom)(CK_SESSION_HANDLE hSession, CK_BYTE_PTR pRandomData, CK_ULONG ulRandomLen) {
	CK_RV (*func)(CK_SESSION_HANDLE hSession, CK_BYTE_PTR pRandomData, CK_ULONG ulRandomLen);

	libcackey_wrap_init();

	func = dlsym(libcackey_wrap_handle, "C_GenerateRandom");

	return(func(hSession, pRandomData, ulRandomLen));
}

CK_DEFINE_FUNCTION(CK_RV, C_GetFunctionStatus)(CK_SESSION_HANDLE hSession) {
	CK_RV (*func)(CK_SESSION_HANDLE hSession);

	libcackey_wrap_init();

	func = dlsym(libcackey_wrap_handle, "C_GetFunctionStatus");

	return(func(hSession));
}

CK_DEFINE_FUNCTION(CK_RV, C_CancelFunction)(CK_SESSION_HANDLE hSession) {
	CK_RV (*func)(CK_SESSION_HANDLE hSession);

	libcackey_wrap_init();

	func = dlsym(libcackey_wrap_handle, "C_CancelFunction");

	return(func(hSession));
}

CK_DEFINE_FUNCTION(CK_RV, C_GetFunctionList)(CK_FUNCTION_LIST_PTR_PTR ppFunctionList) {
	CK_RV (*func)(CK_FUNCTION_LIST_PTR_PTR ppFunctionList);

	libcackey_wrap_init();

	func = dlsym(libcackey_wrap_handle, "C_GetFunctionList");

	return(func(ppFunctionList));
}
