#include "FNMT_P11.h"
#if defined WIN32
    #include <process.h>
#endif

// Objetos globales creados para PRUEBAS: habrá que ir metiéndolos donde corresponda
#include "ObjList.h"
#include "ExclusionMutua.h"
//#include "SessionList.h"

CSlotList & g_rSlotList = CSlotList::getInstance();
CSessionList & gSessionList = CSessionList::getInstance();

CK_ULONG g_ulGlobalFlags	= 0;
CK_ULONG g_ulThreadsWaiting = 0;

CExclusionMutua g_ExclMutua;

#if defined WIN32
    HINSTANCE g_hDllInst = 0;

    BOOL WINAPI DllMain( HINSTANCE hDllInst, DWORD fdwReason, LPVOID lpvReserved )
    {
        DBG_TRACE(("DllMain(0x%x,0x%x,0x%x) [%d.%d]\n",
                    hDllInst,fdwReason,lpvReserved,_getpid(),GetCurrentThreadId() ));
        g_hDllInst = hDllInst;
        switch (fdwReason)
        {
        case DLL_PROCESS_ATTACH:

            /*//g_ExclMutua = new CExclusionMutua();

            if (g_ExclMutua == NULL)
                return FALSE;

            // Se establecen las funciones Nativas
            g_ExclMutua.SetFunctionsPointer();

            if (g_ExclMutua.CreateMutex() != CKR_OK)
                return FALSE;*/

            break;

        case DLL_PROCESS_DETACH:
            break;
        }

        return TRUE;
    }
#else
void __attribute__ ((constructor)) p11_load(void);
void __attribute__ ((destructor)) p11_unload(void);

void p11_load(void)
{
    DBG_TRACE(("p11_load ....\n"));
}

void p11_unload(void)
{
    DBG_TRACE(("p11_unload ....\n"));
}

#endif

/* Session management */

/* C_OpenSession opens a session between an application and a
 * token. */
CK_DEFINE_FUNCTION(CK_RV, C_OpenSession)
(
  CK_SLOT_ID            slotID,        /* the slot's ID */
  CK_FLAGS              flags,         /* from CK_SESSION_INFO */
  CK_VOID_PTR           pApplication,  /* passed to callback */
  CK_NOTIFY             Notify,        /* callback function */
  CK_SESSION_HANDLE_PTR phSession      /* gets session handle */
)
{
	DBG_TRACE(("C_OpenSession(0x%08x,0x%08x,0x%08x,0x%08x,0x%08x)...\n",slotID,flags,pApplication,Notify,phSession));

	if (!(g_ulGlobalFlags & FLAG_LIBRERIA_INICIALIZADA))
		return (CKR_CRYPTOKI_NOT_INITIALIZED);

	CK_RV rvResult = CKR_OK;

	CSession *pP11Session = NULL;

	rvResult = g_ExclMutua.LockMutex();
	if (rvResult != CKR_OK)
		return rvResult;

	try
	{
		// Según documentación PKCS#11, por razones históricas CKF_SERIAL_SESSION
		// debe estar siempre activo.
		if(!(flags & CKF_SERIAL_SESSION))
			throw Pkcs11Exception(CKR_SESSION_PARALLEL_NOT_SUPPORTED);

		try
		{
			pP11Session = gSessionList.FindSessionInSlot(slotID);
		}
		catch (Pkcs11Exception & e)
		{
			if(e.getErrId() != CKR_SLOT_ID_INVALID)
				throw e;

			// Construímos el objeto P11Session
			pP11Session = new CSession(slotID);

			gSessionList.Add(slotID, pP11Session);
		}

		rvResult = pP11Session->OpenSession(flags, pApplication, Notify, phSession);

	}
	catch (Pkcs11Exception & e)
	{
		// Si hemos creado el objeto sesión pero no tiene handles, destruímos el objeto creado
		if( (pP11Session) && (pP11Session->GetNumberOfHandles()==0) )
			gSessionList.DeleteAllSessionsAndHandles(slotID);

		rvResult = e.getErrId();
	}

	DBG_TRACE(("C_OpenSession()=0x%08x\n",rvResult));

	g_ExclMutua.UnlockMutex();

	return rvResult;
}


/* C_CloseSession closes a session between an application and a
 * token. */
CK_DEFINE_FUNCTION(CK_RV, C_CloseSession)
(
  CK_SESSION_HANDLE hSession  /* the session's handle */
)
{
	DBG_TRACE(("C_CloseSession(0x%08x)...\n",hSession));

	if (!(g_ulGlobalFlags & FLAG_LIBRERIA_INICIALIZADA))
		return (CKR_CRYPTOKI_NOT_INITIALIZED);

	CK_RV rvResult = CKR_OK;
	CSession * pP11Session = NULL;

	rvResult = g_ExclMutua.LockMutex();
	if (rvResult != CKR_OK)
		return rvResult;

	try
	{
		//
		// Obtenemos el puntero a sesión
		//
		pP11Session = gSessionList.FindSession(hSession);

		if( pP11Session->GetNumberOfHandles() > 1 )
		{
			rvResult = pP11Session->CloseSession(hSession);

			gSessionList.DeleteHandle( hSession );
		}
		else
			gSessionList.DeleteSessionAndHandles( hSession );
	}
	catch (Pkcs11Exception & e)
	{
		rvResult = e.getErrId();

		if( (rvResult == CKR_SESSION_HANDLE_INVALID) && pP11Session )
			gSessionList.DeleteSessionAndHandles(hSession);
	}

	DBG_TRACE(("C_CloseSession()=0x%08x\n",rvResult));

	g_ExclMutua.UnlockMutex();

	return rvResult;
}

/* C_CloseAllSessions closes all sessions with a token. */
CK_DEFINE_FUNCTION(CK_RV, C_CloseAllSessions)
(
  CK_SLOT_ID     slotID  /* the token's slot */
)
{
	DBG_TRACE(("C_CloseAllSessions(0x%08x)...\n",slotID));

	if (!(g_ulGlobalFlags & FLAG_LIBRERIA_INICIALIZADA))
		return (CKR_CRYPTOKI_NOT_INITIALIZED);

	CK_RV rvResult = CKR_OK;

	rvResult = g_ExclMutua.LockMutex();
	if (rvResult != CKR_OK)
		return rvResult;

	try
	{
		gSessionList.DeleteAllSessionsAndHandles( slotID );
	}
	catch (Pkcs11Exception & e)
	{
		rvResult = e.getErrId();
	}

	DBG_TRACE(("C_CloseAllSessions()=0x%08x\n",rvResult));

	g_ExclMutua.UnlockMutex();

	return rvResult;
}

/* C_GetSessionInfo obtains information about the session. */
CK_DEFINE_FUNCTION(CK_RV, C_GetSessionInfo)
(
  CK_SESSION_HANDLE   hSession,  /* the session's handle */
  CK_SESSION_INFO_PTR pInfo      /* receives session info */
)
{
	DBG_TRACE(("C_GetSessionInfo(0x%08x,0x%08x)...\n",hSession,pInfo));

	if (!(g_ulGlobalFlags & FLAG_LIBRERIA_INICIALIZADA))
		return (CKR_CRYPTOKI_NOT_INITIALIZED);

	CK_RV rvResult = CKR_OK;
	CSession * pP11Session = NULL;

	rvResult = g_ExclMutua.LockMutex();
	if (rvResult != CKR_OK)
		return rvResult;

	if (NULL_PTR == pInfo)
	{
		rvResult = CKR_ARGUMENTS_BAD;
	}
	else
	{
		try
		{
			//
			// Obtenemos el puntero a sesión
			//
			pP11Session = gSessionList.FindSession(hSession);

			rvResult = pP11Session->GetSessionInfo(pInfo);
		}
		catch (Pkcs11Exception & e)
		{
			rvResult = e.getErrId();

			if( (rvResult == CKR_SESSION_HANDLE_INVALID) && pP11Session )
				gSessionList.DeleteSessionAndHandles(hSession);
		}

	}

	DBG_TRACE(("C_GetSessionInfo()=0x%08x\n",rvResult));

	g_ExclMutua.UnlockMutex();

	return rvResult;
}

/* C_GetOperationState obtains the state of the cryptographic operation
 * in a session. */
CK_DEFINE_FUNCTION(CK_RV, C_GetOperationState)
(
  CK_SESSION_HANDLE hSession,             /* session's handle */
  CK_BYTE_PTR       pOperationState,      /* gets state */
  CK_ULONG_PTR      pulOperationStateLen  /* gets state length */
)
{
	DBG_TRACE(("C_GetOperationState(0x%08x,0x%08x,0x%08x)...\n",hSession,pOperationState,pulOperationStateLen));

	if (!(g_ulGlobalFlags & FLAG_LIBRERIA_INICIALIZADA))
		return (CKR_CRYPTOKI_NOT_INITIALIZED);

	DBG_TRACE(("C_GetOperationState()=0x%08x\n",CKR_FUNCTION_NOT_SUPPORTED));
	return CKR_FUNCTION_NOT_SUPPORTED;
}

/* C_SetOperationState restores the state of the cryptographic
 * operation in a session. */
CK_DEFINE_FUNCTION(CK_RV, C_SetOperationState)
(
  CK_SESSION_HANDLE hSession,            /* session's handle */
  CK_BYTE_PTR      pOperationState,      /* holds state */
  CK_ULONG         ulOperationStateLen,  /* holds state length */
  CK_OBJECT_HANDLE hEncryptionKey,       /* en/decryption key */
  CK_OBJECT_HANDLE hAuthenticationKey    /* sign/verify key */
)
{
	DBG_TRACE(("C_SetOperationState(0x%08x,0x%08x,%d,0x%08x,0x%08x)...\n",hSession,pOperationState,ulOperationStateLen,hEncryptionKey,hAuthenticationKey));

	if (!(g_ulGlobalFlags & FLAG_LIBRERIA_INICIALIZADA))
		return (CKR_CRYPTOKI_NOT_INITIALIZED);

	DBG_TRACE(("C_SetOperationState()=0x%08x\n",CKR_FUNCTION_NOT_SUPPORTED));
	return CKR_FUNCTION_NOT_SUPPORTED;
}

/* C_Login logs a user into a token. */
CK_DEFINE_FUNCTION(CK_RV, C_Login)
(
  CK_SESSION_HANDLE hSession,  /* the session's handle */
  CK_USER_TYPE      userType,  /* the user type */
  CK_UTF8CHAR_PTR   pPin,      /* the user's PIN */
  CK_ULONG          ulPinLen   /* the length of the PIN */
)
{
	DBG_TRACE(("C_Login(0x%08x,0x%08x,0x%08x,%d)...\n",hSession,userType,pPin,ulPinLen));

	if (!(g_ulGlobalFlags & FLAG_LIBRERIA_INICIALIZADA))
		return (CKR_CRYPTOKI_NOT_INITIALIZED);

	CK_RV rvResult = CKR_OK;
	CSession * pP11Session = NULL;

	rvResult = g_ExclMutua.LockMutex();
	if (rvResult != CKR_OK)
		return rvResult;

	try
	{
		//
		// Obtenemos el puntero a sesión
		//
		pP11Session = gSessionList.FindSession(hSession);

		rvResult = pP11Session->Login(userType,pPin,ulPinLen);
	}
	catch (Pkcs11Exception & e)
	{
		rvResult = e.getErrId();

		if( (rvResult == CKR_SESSION_HANDLE_INVALID) && pP11Session )
			gSessionList.DeleteSessionAndHandles(hSession);
	}

	DBG_TRACE(("C_Login()=0x%08x\n",rvResult));

	g_ExclMutua.UnlockMutex();

	return rvResult;
}

/* C_Logout logs a user out from a token. */
CK_DEFINE_FUNCTION(CK_RV, C_Logout)
(
  CK_SESSION_HANDLE hSession  /* the session's handle */
)
{
	DBG_TRACE(("C_Logout(0x%08x)...\n",hSession));

	if (!(g_ulGlobalFlags & FLAG_LIBRERIA_INICIALIZADA))
		return (CKR_CRYPTOKI_NOT_INITIALIZED);

	CK_RV rvResult = CKR_OK;
	CSession * pP11Session = NULL;

	rvResult = g_ExclMutua.LockMutex();
	if (rvResult != CKR_OK)
		return rvResult;

	try
	{
		//
		// Obtenemos el puntero a sesión
		//
		pP11Session = gSessionList.FindSession(hSession);

		rvResult = pP11Session->Logout();
	}
	catch (Pkcs11Exception & e)
	{
		rvResult = e.getErrId();

		if( (rvResult == CKR_SESSION_HANDLE_INVALID) && pP11Session )
			gSessionList.DeleteSessionAndHandles(hSession);
	}

	DBG_TRACE(("C_Logout()=0x%08x\n",rvResult));

	g_ExclMutua.UnlockMutex();

	return rvResult;
}


/* C_SetPIN modifies the PIN of the user who is logged in. */
CK_DEFINE_FUNCTION(CK_RV, C_SetPIN)
(
  CK_SESSION_HANDLE hSession,  /* the session's handle */
  CK_UTF8CHAR_PTR   pOldPin,   /* the old PIN */
  CK_ULONG          ulOldLen,  /* length of the old PIN */
  CK_UTF8CHAR_PTR   pNewPin,   /* the new PIN */
  CK_ULONG          ulNewLen   /* length of the new PIN */
)
{
	DBG_TRACE(("C_SetPIN(0x%08x,0x%08x,%d,0x%08x,%d)...\n",hSession,pOldPin,ulOldLen,pNewPin,ulNewLen));

	if (!(g_ulGlobalFlags & FLAG_LIBRERIA_INICIALIZADA))
		return (CKR_CRYPTOKI_NOT_INITIALIZED);

	CK_RV rvResult = CKR_OK;
	CSession * pP11Session = NULL;

	if (!(g_ulGlobalFlags & FLAG_LIBRERIA_INICIALIZADA))
		return (CKR_CRYPTOKI_NOT_INITIALIZED);

	rvResult = g_ExclMutua.LockMutex();
	if (rvResult != CKR_OK)
		return rvResult;
	
	try
	{
		//
		// Obtenemos el puntero a sesión
		//
		pP11Session = gSessionList.FindSession(hSession);

		rvResult = pP11Session->SetPIN(pOldPin,ulOldLen,pNewPin,ulNewLen);
	}
	catch (Pkcs11Exception & e)
	{
		rvResult = e.getErrId();
	}

	DBG_TRACE(("C_SetPIN()=0x%08x\n",rvResult));

	g_ExclMutua.UnlockMutex();

	return rvResult;
}

/* C_UnblockPIN modifies the PIN of the user who is logged in. */
CK_DEFINE_FUNCTION(CK_RV, C_UnblockPIN)
(    
  CK_SESSION_HANDLE hSession,			/* the session's handle */
  CK_UTF8CHAR_PTR   pUnblockCode,		/* the unblock code */
  CK_ULONG          ulUnblockCodeLen,	/* length of the unblock code */
  CK_UTF8CHAR_PTR   pNewPin,			/* the new PIN */
  CK_ULONG          ulNewLen			/* length of the new PIN */
)
{
	DBG_TRACE(("C_UnblockPIN(0x%08x,0x%08x,%d,0x%08x,%d)...\n",hSession,pUnblockCode,ulUnblockCodeLen,pNewPin,ulNewLen));

	if (!(g_ulGlobalFlags & FLAG_LIBRERIA_INICIALIZADA))
		return (CKR_CRYPTOKI_NOT_INITIALIZED);

	CK_RV rvResult = CKR_OK;
	CSession * pP11Session = NULL;

	if (!(g_ulGlobalFlags & FLAG_LIBRERIA_INICIALIZADA))
		return (CKR_CRYPTOKI_NOT_INITIALIZED);

	rvResult = g_ExclMutua.LockMutex();
	if (rvResult != CKR_OK)
		return rvResult;
	
	try
	{
		//
		// Obtenemos el puntero a sesión
		//
		pP11Session = gSessionList.FindSession(hSession);

		rvResult = pP11Session->UnblockPIN(pUnblockCode,ulUnblockCodeLen,pNewPin,ulNewLen);
	}
	catch (Pkcs11Exception & e)
	{
		rvResult = e.getErrId();
	}

	DBG_TRACE(("C_UnblockPIN()=0x%08x\n",rvResult));

	g_ExclMutua.UnlockMutex();

	return rvResult;
}

/* Object management */

/* C_CreateObject creates a new object. */
CK_DEFINE_FUNCTION(CK_RV, C_CreateObject)
(
  CK_SESSION_HANDLE hSession,    /* the session's handle */
  CK_ATTRIBUTE_PTR  pTemplate,   /* the object's template */
  CK_ULONG          ulCount,     /* attributes in template */
  CK_OBJECT_HANDLE_PTR phObject  /* gets new object's handle. */
)
{
	DBG_TRACE(("C_CreateObject(0x%08x,0x%08x,%d,0x%08x)...\n",hSession,pTemplate,ulCount,phObject));
	DBG_DUMP_TEMPLATE(( pTemplate, ulCount ));

	if (!(g_ulGlobalFlags & FLAG_LIBRERIA_INICIALIZADA))
		return (CKR_CRYPTOKI_NOT_INITIALIZED);

	CK_RV rvResult = CKR_OK;
	CSession * pP11Session = NULL;

	rvResult = g_ExclMutua.LockMutex();
	if (rvResult != CKR_OK)
		return rvResult;

	try
	{
		//
		// Obtenemos el puntero a sesión
		//
		pP11Session = gSessionList.FindSession(hSession);

		rvResult = pP11Session->CreateObject( pTemplate, ulCount, phObject);
	}
	catch (Pkcs11Exception & e)
	{
		rvResult = e.getErrId();

		if( (rvResult == CKR_SESSION_HANDLE_INVALID) && pP11Session )
			gSessionList.DeleteSessionAndHandles(hSession);
	}

	DBG_TRACE(("C_CreateObject()=0x%08x\n",rvResult));

	g_ExclMutua.UnlockMutex();

	return rvResult;
}

/* C_CopyObject copies an object, creating a new object for the
 * copy. */
CK_DEFINE_FUNCTION(CK_RV, C_CopyObject)
(
  CK_SESSION_HANDLE    hSession,    /* the session's handle */
  CK_OBJECT_HANDLE     hObject,     /* the object's handle */
  CK_ATTRIBUTE_PTR     pTemplate,   /* template for new object */
  CK_ULONG             ulCount,     /* attributes in template */
  CK_OBJECT_HANDLE_PTR phNewObject  /* receives handle of copy */
)
{
	DBG_TRACE(("C_CopyObject(0x%08x,0x%08x,0x%08x,%d,0x%08x)...\n",hSession,hObject,pTemplate,ulCount,phNewObject));
	DBG_DUMP_TEMPLATE(( pTemplate, ulCount ));

	if (!(g_ulGlobalFlags & FLAG_LIBRERIA_INICIALIZADA))
		return (CKR_CRYPTOKI_NOT_INITIALIZED);

	DBG_TRACE(("C_CopyObject()=0x%08x\n",CKR_FUNCTION_NOT_SUPPORTED));
	return CKR_FUNCTION_NOT_SUPPORTED;
}

/* C_DestroyObject destroys an object. */
CK_DEFINE_FUNCTION(CK_RV, C_DestroyObject)
(
  CK_SESSION_HANDLE hSession,  /* the session's handle */
  CK_OBJECT_HANDLE  hObject    /* the object's handle */
)
{
	DBG_TRACE(("C_DestroyObject(0x%08x,0x%08x)...\n",hSession,hObject));

	if (!(g_ulGlobalFlags & FLAG_LIBRERIA_INICIALIZADA))
		return (CKR_CRYPTOKI_NOT_INITIALIZED);

	CK_RV rvResult = CKR_OK;
	CSession * pP11Session = NULL;

	rvResult = g_ExclMutua.LockMutex();
	if (rvResult != CKR_OK)
		return rvResult;

	try
	{
		//
		// Obtenemos el puntero a sesión
		//
		pP11Session = gSessionList.FindSession(hSession);

		rvResult = pP11Session->DestroyObject( hObject );
	}
	catch (Pkcs11Exception & e)
	{
		rvResult = e.getErrId();

		if( (rvResult == CKR_SESSION_HANDLE_INVALID) && pP11Session )
			gSessionList.DeleteSessionAndHandles(hSession);
	}

	DBG_TRACE(("C_DestroyObject()=0x%08x\n", rvResult));

	g_ExclMutua.UnlockMutex();

	return rvResult;
}

/* C_GetObjectSize gets the size of an object in bytes. */
CK_DEFINE_FUNCTION(CK_RV, C_GetObjectSize)
(
  CK_SESSION_HANDLE hSession,  /* the session's handle */
  CK_OBJECT_HANDLE  hObject,   /* the object's handle */
  CK_ULONG_PTR      pulSize    /* receives size of object */
)
{
	DBG_TRACE(("C_GetObjectSize(0x%08x,0x%08x,0x%08x)...\n",hSession,hObject,pulSize));

	if (!(g_ulGlobalFlags & FLAG_LIBRERIA_INICIALIZADA))
		return (CKR_CRYPTOKI_NOT_INITIALIZED);

	DBG_TRACE(("C_GetObjectSize()=0x%08x\n",CKR_FUNCTION_NOT_SUPPORTED));
	return CKR_FUNCTION_NOT_SUPPORTED;
}

/* C_GetAttributeValue obtains the value of one or more object
 * attributes. */
CK_DEFINE_FUNCTION(CK_RV, C_GetAttributeValue)
(
  CK_SESSION_HANDLE hSession,   /* the session's handle */
  CK_OBJECT_HANDLE  hObject,    /* the object's handle */
  CK_ATTRIBUTE_PTR  pTemplate,  /* specifies attrs; gets vals */
  CK_ULONG          ulCount     /* attributes in template */
)
{
	DBG_TRACE(("C_GetAttributeValue(0x%08x,0x%08x,0x%08x,%d)...\n",hSession,hObject,pTemplate,ulCount));
	DBG_DUMP_TEMPLATE(( pTemplate, ulCount ));

	CK_RV rvResult = CKR_OK;
	CSession * pP11Session = NULL;

	if (!(g_ulGlobalFlags & FLAG_LIBRERIA_INICIALIZADA))
		return (CKR_CRYPTOKI_NOT_INITIALIZED);

	rvResult = g_ExclMutua.LockMutex();
	if (rvResult != CKR_OK)
		return rvResult;

	try
	{
		//
		// Obtenemos el puntero a sesión
		//
		pP11Session = gSessionList.FindSession(hSession);

		rvResult = pP11Session->GetAttributeValue(hObject,pTemplate,ulCount);
	}
	catch (Pkcs11Exception & e)
	{
		rvResult = e.getErrId();

		if( (rvResult == CKR_SESSION_HANDLE_INVALID) && pP11Session )
			gSessionList.DeleteSessionAndHandles(hSession);
	}

	DBG_TRACE(("C_GetAttributeValue()=0x%08x\n",rvResult));
	DBG_DUMP_TEMPLATE(( pTemplate, ulCount));

	g_ExclMutua.UnlockMutex();

	return rvResult;
}

/* C_SetAttributeValue modifies the value of one or more object
 * attributes */
CK_DEFINE_FUNCTION(CK_RV, C_SetAttributeValue)
(
  CK_SESSION_HANDLE hSession,   /* the session's handle */
  CK_OBJECT_HANDLE  hObject,    /* the object's handle */
  CK_ATTRIBUTE_PTR  pTemplate,  /* specifies attrs and values */
  CK_ULONG          ulCount     /* attributes in template */
)
{
	DBG_TRACE(("C_SetAttributeValue(0x%08x,0x%08x,0x%08x,%d)...\n",hSession,hObject,pTemplate,ulCount));
	DBG_DUMP_TEMPLATE(( pTemplate, ulCount));

	if (!(g_ulGlobalFlags & FLAG_LIBRERIA_INICIALIZADA))
		return (CKR_CRYPTOKI_NOT_INITIALIZED);

	CK_RV rvResult = CKR_OK;
	CSession * pP11Session = NULL;

	rvResult = g_ExclMutua.LockMutex();
	if (rvResult != CKR_OK)
		return rvResult;

	try
	{
		//
		// Obtenemos el puntero a sesión
		//
		pP11Session = gSessionList.FindSession(hSession);

		rvResult = pP11Session->SetAttributeValue( hSession, hObject, pTemplate, ulCount);
	}
	catch (Pkcs11Exception & e)
	{
		rvResult = e.getErrId();

		if( (rvResult == CKR_SESSION_HANDLE_INVALID) && pP11Session )
			gSessionList.DeleteSessionAndHandles(hSession);
	}

	DBG_TRACE(("C_SetAttributeValue()=0x%08x\n",rvResult));

	g_ExclMutua.UnlockMutex();

	return rvResult;
}

/* C_FindObjectsInit initializes a search for token and session
 * objects that match a template. */
CK_DEFINE_FUNCTION(CK_RV, C_FindObjectsInit)
(
  CK_SESSION_HANDLE hSession,   /* the session's handle */
  CK_ATTRIBUTE_PTR  pTemplate,  /* attribute values to match */
  CK_ULONG          ulCount     /* attrs in search template */
)
{
	DBG_TRACE(("C_FindObjectsInit(0x%08x,0x%08x,%d)...\n",hSession,pTemplate,ulCount));
	DBG_DUMP_TEMPLATE(( pTemplate, ulCount));

	CK_RV rvResult = CKR_OK;
	CSession * pP11Session = NULL;

	if (!(g_ulGlobalFlags & FLAG_LIBRERIA_INICIALIZADA))
		return (CKR_CRYPTOKI_NOT_INITIALIZED);

	rvResult = g_ExclMutua.LockMutex();
	if (rvResult != CKR_OK)
		return rvResult;

	try
	{
		//
		// Obtenemos el puntero a sesión
		//
		pP11Session = gSessionList.FindSession(hSession);

		rvResult = pP11Session->FindObjectsInit(pTemplate,ulCount);
	}
	catch (Pkcs11Exception & e)
	{
		rvResult = e.getErrId();

		if( (rvResult == CKR_SESSION_HANDLE_INVALID) && pP11Session )
			gSessionList.DeleteSessionAndHandles(hSession);
	}

	DBG_TRACE(("C_FindObjectsInit()=0x%08x\n",rvResult));

	g_ExclMutua.UnlockMutex();

	return rvResult;
}

/* C_FindObjects continues a search for token and session
 * objects that match a template, obtaining additional object
 * handles. */
CK_DEFINE_FUNCTION(CK_RV, C_FindObjects)
(
 CK_SESSION_HANDLE    hSession,          /* session's handle */
 CK_OBJECT_HANDLE_PTR phObject,          /* gets obj. handles */
 CK_ULONG             ulMaxObjectCount,  /* max handles to get */
 CK_ULONG_PTR         pulObjectCount     /* actual # returned */
)
{
	DBG_TRACE(("C_FindObjects(0x%08x,0x%08x,%d,0x%08x)...\n",hSession,phObject,ulMaxObjectCount,pulObjectCount));

	CK_RV rvResult = CKR_OK;
	CSession * pP11Session = NULL;

	if (!(g_ulGlobalFlags & FLAG_LIBRERIA_INICIALIZADA))
		return (CKR_CRYPTOKI_NOT_INITIALIZED);

	rvResult = g_ExclMutua.LockMutex();
	if (rvResult != CKR_OK)
		return rvResult;

	try
	{
		//
		// Obtenemos el puntero a sesión
		//
		pP11Session = gSessionList.FindSession(hSession);

		rvResult = pP11Session->FindObjects(phObject,ulMaxObjectCount,pulObjectCount);
	}
	catch (Pkcs11Exception & e)
	{
		rvResult = e.getErrId();

		if( (rvResult == CKR_SESSION_HANDLE_INVALID) && pP11Session )
			gSessionList.DeleteSessionAndHandles(hSession);
	}

	DBG_TRACE(("C_FindObjects()=0x%08x\n",rvResult));
	#if defined _DEBUG
		if( (rvResult == CKR_OK) && phObject )
		{
			for( CK_ULONG idx=0; idx<*pulObjectCount; ++idx )
			{
				DBG_TRACE(("    %2d - 0x%08x\n",idx,phObject[idx]));
			}
		}
	#endif

	g_ExclMutua.UnlockMutex();

	return rvResult;
}

/* C_FindObjectsFinal finishes a search for token and session
 * objects. */
CK_DEFINE_FUNCTION(CK_RV, C_FindObjectsFinal)
(
  CK_SESSION_HANDLE hSession  /* the session's handle */
)
{
	DBG_TRACE(("C_FindObjectsFinal(0x%08x)...\n",hSession));

	CK_RV rvResult = CKR_OK;
	CSession * pP11Session = NULL;

	if (!(g_ulGlobalFlags & FLAG_LIBRERIA_INICIALIZADA))
		return (CKR_CRYPTOKI_NOT_INITIALIZED);

	rvResult = g_ExclMutua.LockMutex();
	if (rvResult != CKR_OK)
		return rvResult;

	try
	{
		//
		// Obtenemos el puntero a sesión
		//
		pP11Session = gSessionList.FindSession(hSession);

		rvResult = pP11Session->FindObjectsFinal();
	}
	catch (Pkcs11Exception & e)
	{
		rvResult = e.getErrId();

		if( (rvResult == CKR_SESSION_HANDLE_INVALID) && pP11Session )
			gSessionList.DeleteSessionAndHandles(hSession);
	}

	DBG_TRACE(("C_FindObjectsFinal()=0x%08x\n",rvResult));

	g_ExclMutua.UnlockMutex();

	return rvResult;
}


/* Encryption and decryption */

/* C_EncryptInit initializes an encryption operation. */
CK_DEFINE_FUNCTION(CK_RV, C_EncryptInit)
(
  CK_SESSION_HANDLE hSession,    /* the session's handle */
  CK_MECHANISM_PTR  pMechanism,  /* the encryption mechanism */
  CK_OBJECT_HANDLE  hKey         /* handle of encryption key */
)
{
	DBG_TRACE(("C_EncryptInit(0x%08x,0x%08x,0x%08x)...\n",hSession,pMechanism,hKey));

	if (!(g_ulGlobalFlags & FLAG_LIBRERIA_INICIALIZADA))
		return (CKR_CRYPTOKI_NOT_INITIALIZED);

	DBG_TRACE(("C_EncryptInit()=0x%08x\n",CKR_FUNCTION_NOT_SUPPORTED));
	return CKR_FUNCTION_NOT_SUPPORTED;
}

/* C_Encrypt encrypts single-part data. */
CK_DEFINE_FUNCTION(CK_RV, C_Encrypt)
(
  CK_SESSION_HANDLE hSession,            /* session's handle */
  CK_BYTE_PTR       pData,               /* the plaintext data */
  CK_ULONG          ulDataLen,           /* bytes of plaintext */
  CK_BYTE_PTR       pEncryptedData,      /* gets ciphertext */
  CK_ULONG_PTR      pulEncryptedDataLen  /* gets c-text size */
)
{
	DBG_TRACE(("C_Encrypt(0x%08x,0x%08x,%d,0x%08x,0x%08x)...\n",hSession,pData,ulDataLen,pEncryptedData,pulEncryptedDataLen));

	if (!(g_ulGlobalFlags & FLAG_LIBRERIA_INICIALIZADA))
		return (CKR_CRYPTOKI_NOT_INITIALIZED);

	DBG_TRACE(("C_Encrypt()=0x%08x\n",CKR_FUNCTION_NOT_SUPPORTED));
	return CKR_FUNCTION_NOT_SUPPORTED;
}

/* C_EncryptUpdate continues a multiple-part encryption
 * operation. */
CK_DEFINE_FUNCTION(CK_RV, C_EncryptUpdate)
(
  CK_SESSION_HANDLE hSession,           /* session's handle */
  CK_BYTE_PTR       pPart,              /* the plaintext data */
  CK_ULONG          ulPartLen,          /* plaintext data len */
  CK_BYTE_PTR       pEncryptedPart,     /* gets ciphertext */
  CK_ULONG_PTR      pulEncryptedPartLen /* gets c-text size */
)
{
	DBG_TRACE(("C_EncryptUpdate(0x%08x,0x%08x,%d,0x%08x,0x%08x)...\n",hSession,pPart,ulPartLen,pEncryptedPart,pulEncryptedPartLen));

	if (!(g_ulGlobalFlags & FLAG_LIBRERIA_INICIALIZADA))
		return (CKR_CRYPTOKI_NOT_INITIALIZED);

	DBG_TRACE(("C_EncryptUpdate()=0x%08x\n",CKR_FUNCTION_NOT_SUPPORTED));
	return CKR_FUNCTION_NOT_SUPPORTED;
}

/* C_EncryptFinal finishes a multiple-part encryption
 * operation. */
CK_DEFINE_FUNCTION(CK_RV, C_EncryptFinal)
(
  CK_SESSION_HANDLE hSession,                /* session handle */
  CK_BYTE_PTR       pLastEncryptedPart,      /* last c-text */
  CK_ULONG_PTR      pulLastEncryptedPartLen  /* gets last size */
)
{
	DBG_TRACE(("C_EncryptFinal(0x%08x,0x%08x,0x%08x)...\n",hSession,pLastEncryptedPart,pulLastEncryptedPartLen));

	if (!(g_ulGlobalFlags & FLAG_LIBRERIA_INICIALIZADA))
		return (CKR_CRYPTOKI_NOT_INITIALIZED);

	DBG_TRACE(("C_EncryptFinal()=0x%08x\n",CKR_FUNCTION_NOT_SUPPORTED));
	return CKR_FUNCTION_NOT_SUPPORTED;
}

/* C_DecryptInit initializes a decryption operation. */
CK_DEFINE_FUNCTION(CK_RV, C_DecryptInit)
(
  CK_SESSION_HANDLE hSession,    /* the session's handle */
  CK_MECHANISM_PTR  pMechanism,  /* the decryption mechanism */
  CK_OBJECT_HANDLE  hKey         /* handle of decryption key */
)
{
	DBG_TRACE(("C_DecryptInit(0x%08x,0x%08x,0x%08x)...\n",hSession,pMechanism,hKey));

	CK_RV rvResult = CKR_OK;
	CSession * pP11Session = NULL;

	if (!(g_ulGlobalFlags & FLAG_LIBRERIA_INICIALIZADA))
		return (CKR_CRYPTOKI_NOT_INITIALIZED);

	rvResult = g_ExclMutua.LockMutex();
	if (rvResult != CKR_OK)
		return rvResult;

	try
	{
		//
		// Obtenemos el puntero a sesión
		//
		pP11Session = gSessionList.FindSession(hSession);

		rvResult = pP11Session->DecryptInit(pMechanism,hKey);
	}
	catch (Pkcs11Exception & e)
	{
		rvResult = e.getErrId();

		if( (rvResult == CKR_SESSION_HANDLE_INVALID) && pP11Session )
			gSessionList.DeleteSessionAndHandles(hSession);
	}

	DBG_TRACE(("C_DecryptInit()=0x%08x\n",CKR_FUNCTION_NOT_SUPPORTED));

	g_ExclMutua.UnlockMutex();

	return rvResult;
}

/* C_Decrypt decrypts encrypted data in a single part. */
CK_DEFINE_FUNCTION(CK_RV, C_Decrypt)
(
  CK_SESSION_HANDLE hSession,           /* session's handle */
  CK_BYTE_PTR       pEncryptedData,     /* ciphertext */
  CK_ULONG          ulEncryptedDataLen, /* ciphertext length */
  CK_BYTE_PTR       pData,              /* gets plaintext */
  CK_ULONG_PTR      pulDataLen          /* gets p-text size */
)
{
	DBG_TRACE(("C_Decrypt(0x%08x,0x%08x,%d,0x%08x,0x%08x)...\n",hSession,pEncryptedData,ulEncryptedDataLen,pData,pulDataLen));

	CK_RV rvResult = CKR_OK;
	CSession * pP11Session = NULL;

	if (!(g_ulGlobalFlags & FLAG_LIBRERIA_INICIALIZADA))
		return (CKR_CRYPTOKI_NOT_INITIALIZED);

	rvResult = g_ExclMutua.LockMutex();
	if (rvResult != CKR_OK)
		return rvResult;

	try
	{
		//
		// Obtenemos el puntero a sesión
		//
		pP11Session = gSessionList.FindSession(hSession);

		rvResult = pP11Session->Decrypt( pEncryptedData, ulEncryptedDataLen,
										 pData, pulDataLen );
	}
	catch (Pkcs11Exception & e)
	{
		rvResult = e.getErrId();

		if( (rvResult == CKR_SESSION_HANDLE_INVALID) && pP11Session )
			gSessionList.DeleteSessionAndHandles(hSession);
	}

	DBG_TRACE(("C_Decrypt()=0x%08x\n",rvResult));

	g_ExclMutua.UnlockMutex();

	return rvResult;


}

/* C_DecryptUpdate continues a multiple-part decryption
 * operation. */
CK_DEFINE_FUNCTION(CK_RV, C_DecryptUpdate)
(
  CK_SESSION_HANDLE hSession,            /* session's handle */
  CK_BYTE_PTR       pEncryptedPart,      /* encrypted data */
  CK_ULONG          ulEncryptedPartLen,  /* input length */
  CK_BYTE_PTR       pPart,               /* gets plaintext */
  CK_ULONG_PTR      pulPartLen           /* p-text size */
)
{
	DBG_TRACE(("C_DecryptUpdate(0x%08x,0x%08x,%d,0x%08x,0x%08x)...\n",hSession,pEncryptedPart,ulEncryptedPartLen,pPart,pulPartLen));

	if (!(g_ulGlobalFlags & FLAG_LIBRERIA_INICIALIZADA))
		return (CKR_CRYPTOKI_NOT_INITIALIZED);

	DBG_TRACE(("C_DecryptUpdate()=0x%08x\n",CKR_FUNCTION_NOT_SUPPORTED));
	return CKR_FUNCTION_NOT_SUPPORTED;
}

/* C_DecryptFinal finishes a multiple-part decryption
 * operation. */
CK_DEFINE_FUNCTION(CK_RV, C_DecryptFinal)
(
  CK_SESSION_HANDLE hSession,       /* the session's handle */
  CK_BYTE_PTR       pLastPart,      /* gets plaintext */
  CK_ULONG_PTR      pulLastPartLen  /* p-text size */
)
{
	DBG_TRACE(("C_DecryptFinal(0x%08x,0x%08x,0x%08x)...\n",hSession,pLastPart,pulLastPartLen));

	if (!(g_ulGlobalFlags & FLAG_LIBRERIA_INICIALIZADA))
		return (CKR_CRYPTOKI_NOT_INITIALIZED);

	DBG_TRACE(("C_DecryptFinal()=0x%08x\n",CKR_FUNCTION_NOT_SUPPORTED));
	return CKR_FUNCTION_NOT_SUPPORTED;
}


/* Message digesting */

/* C_DigestInit initializes a message-digesting operation. */
CK_DEFINE_FUNCTION(CK_RV, C_DigestInit)
(
  CK_SESSION_HANDLE hSession,   /* the session's handle */
  CK_MECHANISM_PTR  pMechanism  /* the digesting mechanism */
)
{
	DBG_TRACE(("C_DigestInit(0x%08x,0x%08x)...\n",hSession,pMechanism));

	if (!(g_ulGlobalFlags & FLAG_LIBRERIA_INICIALIZADA))
		return (CKR_CRYPTOKI_NOT_INITIALIZED);

	DBG_TRACE(("C_DigestInit()=0x%08x\n",CKR_FUNCTION_NOT_SUPPORTED));
	return CKR_FUNCTION_NOT_SUPPORTED;
}

/* C_Digest digests data in a single part. */
CK_DEFINE_FUNCTION(CK_RV, C_Digest)
(
  CK_SESSION_HANDLE hSession,     /* the session's handle */
  CK_BYTE_PTR       pData,        /* data to be digested */
  CK_ULONG          ulDataLen,    /* bytes of data to digest */
  CK_BYTE_PTR       pDigest,      /* gets the message digest */
  CK_ULONG_PTR      pulDigestLen  /* gets digest length */
)
{
	DBG_TRACE(("C_Digest(0x%08x,0x%08x,%d,0x%08x,0x%08x)...\n",hSession,pData,ulDataLen,pDigest,pulDigestLen));

	if (!(g_ulGlobalFlags & FLAG_LIBRERIA_INICIALIZADA))
		return (CKR_CRYPTOKI_NOT_INITIALIZED);

	DBG_TRACE(("C_Digest()=0x%08x\n",CKR_FUNCTION_NOT_SUPPORTED));
	return CKR_FUNCTION_NOT_SUPPORTED;
}

/* C_DigestUpdate continues a multiple-part message-digesting
 * operation. */
CK_DEFINE_FUNCTION(CK_RV, C_DigestUpdate)
(
  CK_SESSION_HANDLE hSession,  /* the session's handle */
  CK_BYTE_PTR       pPart,     /* data to be digested */
  CK_ULONG          ulPartLen  /* bytes of data to be digested */
)
{
	DBG_TRACE(("C_DigestUpdate(0x%08x,0x%08x,%d)...\n",hSession,pPart,ulPartLen));

	if (!(g_ulGlobalFlags & FLAG_LIBRERIA_INICIALIZADA))
		return (CKR_CRYPTOKI_NOT_INITIALIZED);

	DBG_TRACE(("C_DigestUpdate()=0x%08x\n",CKR_FUNCTION_NOT_SUPPORTED));
	return CKR_FUNCTION_NOT_SUPPORTED;
}

/* C_DigestKey continues a multi-part message-digesting
 * operation, by digesting the value of a secret key as part of
 * the data already digested. */
CK_DEFINE_FUNCTION(CK_RV, C_DigestKey)
(
  CK_SESSION_HANDLE hSession,  /* the session's handle */
  CK_OBJECT_HANDLE  hKey       /* secret key to digest */
)
{
	DBG_TRACE(("C_DigestKey(0x%08x,0x%08x)...\n",hSession,hKey));

	if (!(g_ulGlobalFlags & FLAG_LIBRERIA_INICIALIZADA))
		return (CKR_CRYPTOKI_NOT_INITIALIZED);

	DBG_TRACE(("C_DigestKey()=0x%08x\n",CKR_FUNCTION_NOT_SUPPORTED));
	return CKR_FUNCTION_NOT_SUPPORTED;
}

/* C_DigestFinal finishes a multiple-part message-digesting
 * operation. */
CK_DEFINE_FUNCTION(CK_RV, C_DigestFinal)
(
  CK_SESSION_HANDLE hSession,     /* the session's handle */
  CK_BYTE_PTR       pDigest,      /* gets the message digest */
  CK_ULONG_PTR      pulDigestLen  /* gets byte count of digest */
)
{
	DBG_TRACE(("C_DigestFinal(0x%08x,0x%08x,0x%08x)...\n",hSession,pDigest,pulDigestLen));

	if (!(g_ulGlobalFlags & FLAG_LIBRERIA_INICIALIZADA))
		return (CKR_CRYPTOKI_NOT_INITIALIZED);

	DBG_TRACE(("C_DigestFinal()=0x%08x\n",CKR_FUNCTION_NOT_SUPPORTED));
	return CKR_FUNCTION_NOT_SUPPORTED;
}


/* Signing and MACing */

/* C_SignInit initializes a signature (private key encryption)
 * operation, where the signature is (will be) an appendix to
 * the data, and plaintext cannot be recovered from the
 *signature. */
CK_DEFINE_FUNCTION(CK_RV, C_SignInit)
(
  CK_SESSION_HANDLE hSession,    /* the session's handle */
  CK_MECHANISM_PTR  pMechanism,  /* the signature mechanism */
  CK_OBJECT_HANDLE  hKey         /* handle of signature key */
)
{
	DBG_TRACE(("C_SignInit(0x%08x,0x%08x,0x%08x)...\n",hSession,pMechanism,hKey));

	CK_RV rvResult = CKR_OK;
	CSession * pP11Session = NULL;

	if (!(g_ulGlobalFlags & FLAG_LIBRERIA_INICIALIZADA))
		return (CKR_CRYPTOKI_NOT_INITIALIZED);

	rvResult = g_ExclMutua.LockMutex();
	if (rvResult != CKR_OK)
		return rvResult;

	try
	{
		//
		// Obtenemos el puntero a sesión
		//
		pP11Session = gSessionList.FindSession(hSession);

		rvResult = pP11Session->SignInit(pMechanism,hKey);
	}
	catch (Pkcs11Exception & e)
	{
		rvResult = e.getErrId();

		if( (rvResult == CKR_SESSION_HANDLE_INVALID) && pP11Session )
			gSessionList.DeleteSessionAndHandles(hSession);
	}

	DBG_TRACE(("C_SignInit()=0x%08x\n",rvResult));

	g_ExclMutua.UnlockMutex();

	return rvResult;
}

/* C_Sign signs (encrypts with private key) data in a single
 * part, where the signature is (will be) an appendix to the
 * data, and plaintext cannot be recovered from the signature. */
CK_DEFINE_FUNCTION(CK_RV, C_Sign)
(
  CK_SESSION_HANDLE hSession,        /* the session's handle */
  CK_BYTE_PTR       pData,           /* the data to sign */
  CK_ULONG          ulDataLen,       /* count of bytes to sign */
  CK_BYTE_PTR       pSignature,      /* gets the signature */
  CK_ULONG_PTR      pulSignatureLen  /* gets signature length */
)
{
	DBG_TRACE(("C_Sign(0x%08x,0x%08x,%d,0x%08x,0x%08x)...\n",hSession,pData,ulDataLen,pSignature,pulSignatureLen));

	CK_RV rvResult = CKR_OK;
	CSession * pP11Session = NULL;

	if (!(g_ulGlobalFlags & FLAG_LIBRERIA_INICIALIZADA))
		return (CKR_CRYPTOKI_NOT_INITIALIZED);

	rvResult = g_ExclMutua.LockMutex();
	if (rvResult != CKR_OK)
		return rvResult;

	try
	{
		//
		// Obtenemos el puntero a sesión
		//
		pP11Session = gSessionList.FindSession(hSession);

		rvResult = pP11Session->Sign( pData, ulDataLen,
									  pSignature, pulSignatureLen );

		// Si el PIN ha caducado por haber realizado una firma, 
		// reseteamos el estado de las sesiones de ese slot
		if( rvResult == CKR_PIN_EXPIRED ) 
		{
			//gSessionList.ResetSessionsInSlot(hSession);
			rvResult = CKR_OK; 
		}
	}
	catch (Pkcs11Exception & e)
	{
		rvResult = e.getErrId();

		if( (rvResult == CKR_SESSION_HANDLE_INVALID) && pP11Session )
			gSessionList.DeleteSessionAndHandles(hSession);
	}

	DBG_TRACE(("C_Sign()=0x%08x\n",rvResult));

	g_ExclMutua.UnlockMutex();

	return rvResult;
}

/* C_SignUpdate continues a multiple-part signature operation,
 * where the signature is (will be) an appendix to the data,
 * and plaintext cannot be recovered from the signature. */
CK_DEFINE_FUNCTION(CK_RV, C_SignUpdate)
(
  CK_SESSION_HANDLE hSession,  /* the session's handle */
  CK_BYTE_PTR       pPart,     /* the data to sign */
  CK_ULONG          ulPartLen  /* count of bytes to sign */
)
{
	DBG_TRACE(("C_SignUpdate(0x%08x,0x%08x,%d)...\n",hSession,pPart,ulPartLen));

	CK_RV rvResult = CKR_OK;
	CSession * pP11Session = NULL;

	if (!(g_ulGlobalFlags & FLAG_LIBRERIA_INICIALIZADA))
		return (CKR_CRYPTOKI_NOT_INITIALIZED);

	rvResult = g_ExclMutua.LockMutex();
	if (rvResult != CKR_OK)
		return rvResult;

	try
	{
		//
		// Obtenemos el puntero a sesión
		//
		pP11Session = gSessionList.FindSession(hSession);

		rvResult = pP11Session->SignUpdate( pPart, ulPartLen );
	}
	catch (Pkcs11Exception & e)
	{
		rvResult = e.getErrId();

		if( (rvResult == CKR_SESSION_HANDLE_INVALID) && pP11Session )
			gSessionList.DeleteSessionAndHandles(hSession);
	}

	DBG_TRACE(("C_SignUpdate()=0x%08x\n",rvResult));

	g_ExclMutua.UnlockMutex();

	return rvResult;
}

/* C_SignFinal finishes a multiple-part signature operation,
 * returning the signature. */
CK_DEFINE_FUNCTION(CK_RV, C_SignFinal)
(
  CK_SESSION_HANDLE hSession,        /* the session's handle */
  CK_BYTE_PTR       pSignature,      /* gets the signature */
  CK_ULONG_PTR      pulSignatureLen  /* gets signature length */
)
{
	DBG_TRACE(("C_SignFinal(0x%08x,0x%08x,0x%08x)...\n",hSession,pSignature,pulSignatureLen));

	CK_RV rvResult = CKR_OK;
	CSession * pP11Session = NULL;

	if (!(g_ulGlobalFlags & FLAG_LIBRERIA_INICIALIZADA))
		return (CKR_CRYPTOKI_NOT_INITIALIZED);

	rvResult = g_ExclMutua.LockMutex();
	if (rvResult != CKR_OK)
		return rvResult;

	try
	{
		//
		// Obtenemos el puntero a sesión
		//
		pP11Session = gSessionList.FindSession(hSession);

		rvResult = pP11Session->SignFinal( pSignature, pulSignatureLen );
	}
	catch (Pkcs11Exception & e)
	{
		rvResult = e.getErrId();

		if( (rvResult == CKR_SESSION_HANDLE_INVALID) && pP11Session )
			gSessionList.DeleteSessionAndHandles(hSession);
	}

	DBG_TRACE(("C_SignFinal()=0x%08x\n",rvResult));

	g_ExclMutua.UnlockMutex();

	return rvResult;
}

/* C_SignRecoverInit initializes a signature operation, where
 * the data can be recovered from the signature. */
CK_DEFINE_FUNCTION(CK_RV, C_SignRecoverInit)
(
  CK_SESSION_HANDLE hSession,   /* the session's handle */
  CK_MECHANISM_PTR  pMechanism, /* the signature mechanism */
  CK_OBJECT_HANDLE  hKey        /* handle of the signature key */
)
{
	DBG_TRACE(("C_SignRecoverInit(0x%08x,0x%08x,0x%08x)...\n",hSession,pMechanism,hKey));

	if (!(g_ulGlobalFlags & FLAG_LIBRERIA_INICIALIZADA))
		return (CKR_CRYPTOKI_NOT_INITIALIZED);

	DBG_TRACE(("C_SignRecoverInit()=0x%08x\n",CKR_FUNCTION_NOT_SUPPORTED));
	return CKR_FUNCTION_NOT_SUPPORTED;
}

/* C_SignRecover signs data in a single operation, where the
 * data can be recovered from the signature. */
CK_DEFINE_FUNCTION(CK_RV, C_SignRecover)
(
  CK_SESSION_HANDLE hSession,        /* the session's handle */
  CK_BYTE_PTR       pData,           /* the data to sign */
  CK_ULONG          ulDataLen,       /* count of bytes to sign */
  CK_BYTE_PTR       pSignature,      /* gets the signature */
  CK_ULONG_PTR      pulSignatureLen  /* gets signature length */
)
{
	DBG_TRACE(("C_SignRecover(0x%08x,0x%08x,%d,0x%08x,0x%08x)...\n",hSession,pData,ulDataLen,pSignature,pulSignatureLen));

	if (!(g_ulGlobalFlags & FLAG_LIBRERIA_INICIALIZADA))
		return (CKR_CRYPTOKI_NOT_INITIALIZED);

	DBG_TRACE(("C_SignRecover()=0x%08x\n",CKR_FUNCTION_NOT_SUPPORTED));
	return CKR_FUNCTION_NOT_SUPPORTED;
}


/* Verifying signatures and MACs */

/* C_VerifyInit initializes a verification operation, where the
 * signature is an appendix to the data, and plaintext cannot
 *  cannot be recovered from the signature (e.g. DSA). */
CK_DEFINE_FUNCTION(CK_RV, C_VerifyInit)
(
  CK_SESSION_HANDLE hSession,    /* the session's handle */
  CK_MECHANISM_PTR  pMechanism,  /* the verification mechanism */
  CK_OBJECT_HANDLE  hKey         /* verification key */
)
{
	DBG_TRACE(("C_VerifyInit(0x%08x,0x%08x,0x%08x)...\n",hSession,pMechanism,hKey));

	CK_RV rvResult = CKR_OK;
	CSession * pP11Session = NULL;

	if (!(g_ulGlobalFlags & FLAG_LIBRERIA_INICIALIZADA))
		return (CKR_CRYPTOKI_NOT_INITIALIZED);

	rvResult = g_ExclMutua.LockMutex();
	if (rvResult != CKR_OK)
		return rvResult;

	try
	{
		//
		// Obtenemos el puntero a sesión
		//
		pP11Session = gSessionList.FindSession(hSession);

		rvResult = pP11Session->VerifyInit(pMechanism,hKey);
	}
	catch (Pkcs11Exception & e)
	{
		rvResult = e.getErrId();

		if( (rvResult == CKR_SESSION_HANDLE_INVALID) && pP11Session )
			gSessionList.DeleteSessionAndHandles(hSession);
	}

	DBG_TRACE(("C_VerifyInit()=0x%08x\n",rvResult));

	g_ExclMutua.UnlockMutex();

	return rvResult;
}

/* C_Verify verifies a signature in a single-part operation,
 * where the signature is an appendix to the data, and plaintext
 * cannot be recovered from the signature. */
CK_DEFINE_FUNCTION(CK_RV, C_Verify)
(
  CK_SESSION_HANDLE hSession,       /* the session's handle */
  CK_BYTE_PTR       pData,          /* signed data */
  CK_ULONG          ulDataLen,      /* length of signed data */
  CK_BYTE_PTR       pSignature,     /* signature */
  CK_ULONG          ulSignatureLen  /* signature length*/
)
{
	DBG_TRACE(("C_Verify(0x%08x,0x%08x,%d,0x%08x,%d)...\n",hSession,pData,ulDataLen,pSignature,ulSignatureLen));

	CK_RV rvResult = CKR_OK;
	CSession * pP11Session = NULL;

	if (!(g_ulGlobalFlags & FLAG_LIBRERIA_INICIALIZADA))
		return (CKR_CRYPTOKI_NOT_INITIALIZED);

	rvResult = g_ExclMutua.LockMutex();
	if (rvResult != CKR_OK)
		return rvResult;

	try
	{
		//
		// Obtenemos el puntero a sesión
		//
		pP11Session = gSessionList.FindSession(hSession);

		rvResult = pP11Session->Verify( pData, ulDataLen,
										pSignature, ulSignatureLen );
	}
	catch (Pkcs11Exception & e)
	{
		rvResult = e.getErrId();

		if( (rvResult == CKR_SESSION_HANDLE_INVALID) && pP11Session )
			gSessionList.DeleteSessionAndHandles(hSession);
	}

	DBG_TRACE(("C_Verify()=0x%08x\n",rvResult));

	g_ExclMutua.UnlockMutex();

	return rvResult;
}

/* C_VerifyUpdate continues a multiple-part verification
 * operation, where the signature is an appendix to the data,
 * and plaintext cannot be recovered from the signature. */
CK_DEFINE_FUNCTION(CK_RV, C_VerifyUpdate)
(
  CK_SESSION_HANDLE hSession,  /* the session's handle */
  CK_BYTE_PTR       pPart,     /* signed data */
  CK_ULONG          ulPartLen  /* length of signed data */
)
{
	DBG_TRACE(("C_VerifyUpdate(0x%08x,0x%08x,%d)...\n",hSession,pPart,ulPartLen));

	CK_RV rvResult = CKR_OK;
	CSession * pP11Session = NULL;

	if (!(g_ulGlobalFlags & FLAG_LIBRERIA_INICIALIZADA))
		return (CKR_CRYPTOKI_NOT_INITIALIZED);

	rvResult = g_ExclMutua.LockMutex();
	if (rvResult != CKR_OK)
		return rvResult;

	try
	{
		//
		// Obtenemos el puntero a sesión
		//
		pP11Session = gSessionList.FindSession(hSession);
		rvResult = pP11Session->VerifyUpdate( pPart, ulPartLen );
	}
	catch (Pkcs11Exception & e)
	{
		rvResult = e.getErrId();

		if( (rvResult == CKR_SESSION_HANDLE_INVALID) && pP11Session )
			gSessionList.DeleteSessionAndHandles(hSession);
	}

	DBG_TRACE(("C_VerifyUpdate()=0x%08x\n",rvResult));

	g_ExclMutua.UnlockMutex();

	return rvResult;
}

/* C_VerifyFinal finishes a multiple-part verification
 * operation, checking the signature. */
CK_DEFINE_FUNCTION(CK_RV, C_VerifyFinal)
(
  CK_SESSION_HANDLE hSession,       /* the session's handle */
  CK_BYTE_PTR       pSignature,     /* signature to verify */
  CK_ULONG          ulSignatureLen  /* signature length */
)
{
	DBG_TRACE(("C_VerifyFinal(0x%08x,0x%08x,%d)...\n",hSession,pSignature,ulSignatureLen));

	CK_RV rvResult = CKR_OK;
	CSession * pP11Session = NULL;

	if (!(g_ulGlobalFlags & FLAG_LIBRERIA_INICIALIZADA))
		return (CKR_CRYPTOKI_NOT_INITIALIZED);

	rvResult = g_ExclMutua.LockMutex();
	if (rvResult != CKR_OK)
		return rvResult;

	try
	{
		//
		// Obtenemos el puntero a sesión
		//
		pP11Session = gSessionList.FindSession(hSession);
		rvResult = pP11Session->VerifyFinal( pSignature, ulSignatureLen );
	}
	catch (Pkcs11Exception & e)
	{
		rvResult = e.getErrId();

		if( (rvResult == CKR_SESSION_HANDLE_INVALID) && pP11Session )
			gSessionList.DeleteSessionAndHandles(hSession);
	}

	DBG_TRACE(("C_VerifyFinal()=0x%08x\n",rvResult));

	g_ExclMutua.UnlockMutex();

	return rvResult;
}

/* C_VerifyRecoverInit initializes a signature verification
 * operation, where the data is recovered from the signature. */
CK_DEFINE_FUNCTION(CK_RV, C_VerifyRecoverInit)
(
  CK_SESSION_HANDLE hSession,    /* the session's handle */
  CK_MECHANISM_PTR  pMechanism,  /* the verification mechanism */
  CK_OBJECT_HANDLE  hKey         /* verification key */
)
{
	DBG_TRACE(("C_VerifyRecoverInit(0x%08x,0x%08x,0x%08x)...\n",hSession,pMechanism,hKey));

	if (!(g_ulGlobalFlags & FLAG_LIBRERIA_INICIALIZADA))
		return (CKR_CRYPTOKI_NOT_INITIALIZED);

	DBG_TRACE(("C_VerifyRecoverInit()=0x%08x\n",CKR_FUNCTION_NOT_SUPPORTED));
	return CKR_FUNCTION_NOT_SUPPORTED;
}

/* C_VerifyRecover verifies a signature in a single-part
 * operation, where the data is recovered from the signature. */
CK_DEFINE_FUNCTION(CK_RV, C_VerifyRecover)
(
  CK_SESSION_HANDLE hSession,        /* the session's handle */
  CK_BYTE_PTR       pSignature,      /* signature to verify */
  CK_ULONG          ulSignatureLen,  /* signature length */
  CK_BYTE_PTR       pData,           /* gets signed data */
  CK_ULONG_PTR      pulDataLen       /* gets signed data len */
)
{
	DBG_TRACE(("C_VerifyRecover(0x%08x,0x%08x,%d,0x%08x,0x%08x)...\n",hSession,pSignature,ulSignatureLen,pData,pulDataLen));

	if (!(g_ulGlobalFlags & FLAG_LIBRERIA_INICIALIZADA))
		return (CKR_CRYPTOKI_NOT_INITIALIZED);

	DBG_TRACE(("C_VerifyRecover()=0x%08x\n",CKR_FUNCTION_NOT_SUPPORTED));
	return CKR_FUNCTION_NOT_SUPPORTED;
}


/* Dual-function cryptographic operations */

/* C_DigestEncryptUpdate continues a multiple-part digesting
 * and encryption operation. */
CK_DEFINE_FUNCTION(CK_RV, C_DigestEncryptUpdate)
(
  CK_SESSION_HANDLE hSession,            /* session's handle */
  CK_BYTE_PTR       pPart,               /* the plaintext data */
  CK_ULONG          ulPartLen,           /* plaintext length */
  CK_BYTE_PTR       pEncryptedPart,      /* gets ciphertext */
  CK_ULONG_PTR      pulEncryptedPartLen  /* gets c-text length */
)
{
	DBG_TRACE(("C_DigestEncryptUpdate(0x%08x,0x%08x,%d,0x%08x,0x%08x)...\n",hSession,pPart,ulPartLen,pEncryptedPart,pulEncryptedPartLen));

	if (!(g_ulGlobalFlags & FLAG_LIBRERIA_INICIALIZADA))
		return (CKR_CRYPTOKI_NOT_INITIALIZED);

	DBG_TRACE(("C_DigestEncryptUpdate()=0x%08x\n",CKR_FUNCTION_NOT_SUPPORTED));
	return CKR_FUNCTION_NOT_SUPPORTED;
}

/* C_DecryptDigestUpdate continues a multiple-part decryption and
 * digesting operation. */
CK_DEFINE_FUNCTION(CK_RV, C_DecryptDigestUpdate)
(
  CK_SESSION_HANDLE hSession,            /* session's handle */
  CK_BYTE_PTR       pEncryptedPart,      /* ciphertext */
  CK_ULONG          ulEncryptedPartLen,  /* ciphertext length */
  CK_BYTE_PTR       pPart,               /* gets plaintext */
  CK_ULONG_PTR      pulPartLen           /* gets plaintext len */
)
{
	DBG_TRACE(("C_DecryptDigestUpdate(0x%08x,0x%08x,%d,0x%08x,0x%08x)...\n",hSession,pEncryptedPart,ulEncryptedPartLen,pPart,pulPartLen));

	if (!(g_ulGlobalFlags & FLAG_LIBRERIA_INICIALIZADA))
		return (CKR_CRYPTOKI_NOT_INITIALIZED);

	DBG_TRACE(("C_DecryptDigestUpdate()=0x%08x\n",CKR_FUNCTION_NOT_SUPPORTED));
	return CKR_FUNCTION_NOT_SUPPORTED;
}

/* C_SignEncryptUpdate continues a multiple-part signing and
 * encryption operation. */
CK_DEFINE_FUNCTION(CK_RV, C_SignEncryptUpdate)
(
  CK_SESSION_HANDLE hSession,            /* session's handle */
  CK_BYTE_PTR       pPart,               /* the plaintext data */
  CK_ULONG          ulPartLen,           /* plaintext length */
  CK_BYTE_PTR       pEncryptedPart,      /* gets ciphertext */
  CK_ULONG_PTR      pulEncryptedPartLen  /* gets c-text length */
)
{
	DBG_TRACE(("C_SignEncryptUpdate(0x%08x,0x%08x,%d,0x%08x,0x%08x)...\n",hSession,pPart,ulPartLen,pEncryptedPart,pulEncryptedPartLen));

	if (!(g_ulGlobalFlags & FLAG_LIBRERIA_INICIALIZADA))
		return (CKR_CRYPTOKI_NOT_INITIALIZED);

	DBG_TRACE(("C_SignEncryptUpdate()=0x%08x\n",CKR_FUNCTION_NOT_SUPPORTED));
	return CKR_FUNCTION_NOT_SUPPORTED;
}

/* C_DecryptVerifyUpdate continues a multiple-part decryption and
 * verify operation. */
CK_DEFINE_FUNCTION(CK_RV, C_DecryptVerifyUpdate)
(
  CK_SESSION_HANDLE hSession,            /* session's handle */
  CK_BYTE_PTR       pEncryptedPart,      /* ciphertext */
  CK_ULONG          ulEncryptedPartLen,  /* ciphertext length */
  CK_BYTE_PTR       pPart,               /* gets plaintext */
  CK_ULONG_PTR      pulPartLen           /* gets p-text length */
)
{
	DBG_TRACE(("C_DecryptVerifyUpdate(0x%08x,0x%08x,%d,0x%08x,0x%08x)...\n",hSession,pEncryptedPart,ulEncryptedPartLen,pPart,pulPartLen));

	if (!(g_ulGlobalFlags & FLAG_LIBRERIA_INICIALIZADA))
		return (CKR_CRYPTOKI_NOT_INITIALIZED);

	DBG_TRACE(("C_DecryptVerifyUpdate()=0x%08x\n",CKR_FUNCTION_NOT_SUPPORTED));
	return CKR_FUNCTION_NOT_SUPPORTED;
}


/* Key management */

/* C_GenerateKey generates a secret key, creating a new key
 * object. */
CK_DEFINE_FUNCTION(CK_RV, C_GenerateKey)
(
  CK_SESSION_HANDLE    hSession,    /* the session's handle */
  CK_MECHANISM_PTR     pMechanism,  /* key generation mech. */
  CK_ATTRIBUTE_PTR     pTemplate,   /* template for new key */
  CK_ULONG             ulCount,     /* # of attrs in template */
  CK_OBJECT_HANDLE_PTR phKey        /* gets handle of new key */
)
{
	DBG_TRACE(("C_GenerateKey(0x%08x,0x%08x,0x%08x,%d,0x%08x)...\n",hSession,pMechanism,pTemplate,ulCount,phKey));
	DBG_DUMP_TEMPLATE(( pTemplate, ulCount));

	if (!(g_ulGlobalFlags & FLAG_LIBRERIA_INICIALIZADA))
		return (CKR_CRYPTOKI_NOT_INITIALIZED);

	DBG_TRACE(("C_GenerateKey()=0x%08x\n",CKR_FUNCTION_NOT_SUPPORTED));
	return CKR_FUNCTION_NOT_SUPPORTED;
}

/* C_GenerateKeyPair generates a public-key/private-key pair,
 * creating new key objects. */
CK_DEFINE_FUNCTION(CK_RV, C_GenerateKeyPair)
(
  CK_SESSION_HANDLE    hSession,                    /* session handle */
  CK_MECHANISM_PTR     pMechanism,                  /* key-gen mechanism */
  CK_ATTRIBUTE_PTR     pPublicKeyTemplate,          /* template for public key */
  CK_ULONG             ulPublicKeyAttributeCount,   /* # public attributes */
  CK_ATTRIBUTE_PTR     pPrivateKeyTemplate,         /* template for private key */
  CK_ULONG             ulPrivateKeyAttributeCount,  /* # private attributes */
  CK_OBJECT_HANDLE_PTR phPublicKey,                 /* gets pub. keyhandle */
  CK_OBJECT_HANDLE_PTR phPrivateKey                 /* gets priv. key handle */
)
{
	DBG_TRACE(("C_GenerateKeyPair(0x%08x,0x%08x,0x%08x,%d,0x%08x,%d,0x%08x,0x%08x)...\n",
					hSession,pMechanism,
					pPublicKeyTemplate,ulPublicKeyAttributeCount,
					pPrivateKeyTemplate,ulPrivateKeyAttributeCount,
					phPublicKey,phPrivateKey));

	DBG_TRACE(( "  Template clave pública: ..\n"));
	DBG_DUMP_TEMPLATE(( pPublicKeyTemplate, ulPublicKeyAttributeCount));

	DBG_TRACE(( "  Template clave privada: ..\n"));
	DBG_DUMP_TEMPLATE(( pPrivateKeyTemplate, ulPrivateKeyAttributeCount));

	if (!(g_ulGlobalFlags & FLAG_LIBRERIA_INICIALIZADA))
		return (CKR_CRYPTOKI_NOT_INITIALIZED);

	CK_RV rvResult = CKR_OK;

	CSession *pP11Session = NULL;

	rvResult = g_ExclMutua.LockMutex();
	if (rvResult != CKR_OK)
		return rvResult;

	try
	{
		//
		// Obtenemos el puntero a sesión
		//
		pP11Session = gSessionList.FindSession(hSession);

		rvResult = pP11Session->GenerateKeyPair(  pMechanism,
												  pPublicKeyTemplate, ulPublicKeyAttributeCount,
												  pPrivateKeyTemplate, ulPrivateKeyAttributeCount,
												  phPublicKey,
												  phPrivateKey );
	}
	catch (Pkcs11Exception & e)
	{
		rvResult = e.getErrId();

		if( (rvResult == CKR_SESSION_HANDLE_INVALID) && pP11Session )
			gSessionList.DeleteSessionAndHandles(hSession);
	}

	DBG_TRACE(("C_GenerateKeyPair()=0x%08x\n",rvResult));

	g_ExclMutua.UnlockMutex();

	return rvResult;
}

/* C_WrapKey wraps (i.e., encrypts) a key. */
CK_DEFINE_FUNCTION(CK_RV, C_WrapKey)
(
  CK_SESSION_HANDLE hSession,        /* the session's handle */
  CK_MECHANISM_PTR  pMechanism,      /* the wrapping mechanism */
  CK_OBJECT_HANDLE  hWrappingKey,    /* wrapping key */
  CK_OBJECT_HANDLE  hKey,            /* key to be wrapped */
  CK_BYTE_PTR       pWrappedKey,     /* gets wrapped key */
  CK_ULONG_PTR      pulWrappedKeyLen /* gets wrapped key size */
)
{
	DBG_TRACE(("C_WrapKey(0x%08x,0x%08x,0x%08x,0x%08x,0x%08x,0x%08x)...\n",hSession,pMechanism,hWrappingKey,hKey,pWrappedKey,pulWrappedKeyLen));

	if (!(g_ulGlobalFlags & FLAG_LIBRERIA_INICIALIZADA))
		return (CKR_CRYPTOKI_NOT_INITIALIZED);

	DBG_TRACE(("C_WrapKey()=0x%08x\n",CKR_FUNCTION_NOT_SUPPORTED));
	return CKR_FUNCTION_NOT_SUPPORTED;
}

/* C_UnwrapKey unwraps (decrypts) a wrapped key, creating a new
 * key object. */
CK_DEFINE_FUNCTION(CK_RV, C_UnwrapKey)
(
  CK_SESSION_HANDLE    hSession,          /* session's handle */
  CK_MECHANISM_PTR     pMechanism,        /* unwrapping mech. */
  CK_OBJECT_HANDLE     hUnwrappingKey,    /* unwrapping key */
  CK_BYTE_PTR          pWrappedKey,       /* the wrapped key */
  CK_ULONG             ulWrappedKeyLen,   /* wrapped key len */
  CK_ATTRIBUTE_PTR     pTemplate,         /* new key template */
  CK_ULONG             ulAttributeCount,  /* template length */
  CK_OBJECT_HANDLE_PTR phKey              /* gets new handle */
)
{
	DBG_TRACE(("C_UnwrapKey(0x%08x,0x%08x,0x%08x,0x%08x,%d,0x%08x,%d,0x%08x)...\n",
		hSession,pMechanism,hUnwrappingKey,
		pWrappedKey,ulWrappedKeyLen,
		pTemplate,ulAttributeCount,phKey));
	DBG_DUMP_TEMPLATE(( pTemplate, ulAttributeCount ));

	if (!(g_ulGlobalFlags & FLAG_LIBRERIA_INICIALIZADA))
		return (CKR_CRYPTOKI_NOT_INITIALIZED);

	DBG_TRACE(("C_UnwrapKey()=0x%08x\n",CKR_FUNCTION_NOT_SUPPORTED));
	return CKR_FUNCTION_NOT_SUPPORTED;
}

/* C_DeriveKey derives a key from a base key, creating a new key
 * object. */
CK_DEFINE_FUNCTION(CK_RV, C_DeriveKey)
(
  CK_SESSION_HANDLE    hSession,          /* session's handle */
  CK_MECHANISM_PTR     pMechanism,        /* key deriv. mech. */
  CK_OBJECT_HANDLE     hBaseKey,          /* base key */
  CK_ATTRIBUTE_PTR     pTemplate,         /* new key template */
  CK_ULONG             ulAttributeCount,  /* template length */
  CK_OBJECT_HANDLE_PTR phKey              /* gets new handle */
)
{
	DBG_TRACE(("C_DeriveKey(0x%08x,0x%08x,0x%08x,0x%08x,%d,0x%08x)...\n",
		hSession,pMechanism,hBaseKey,pTemplate,ulAttributeCount,phKey));
	DBG_DUMP_TEMPLATE(( pTemplate, ulAttributeCount ));

	if (!(g_ulGlobalFlags & FLAG_LIBRERIA_INICIALIZADA))
		return (CKR_CRYPTOKI_NOT_INITIALIZED);

	DBG_TRACE(("C_DeriveKey()=0x%08x\n",CKR_FUNCTION_NOT_SUPPORTED));
	return CKR_FUNCTION_NOT_SUPPORTED;
}


/* Random number generation */

/* C_SeedRandom mixes additional seed material into the token's
 * random number generator. */
CK_DEFINE_FUNCTION(CK_RV, C_SeedRandom)
(
  CK_SESSION_HANDLE hSession,  /* the session's handle */
  CK_BYTE_PTR       pSeed,     /* the seed material */
  CK_ULONG          ulSeedLen  /* length of seed material */
)
{
	DBG_TRACE(("C_SeedRandom(0x%08x,0x%08x,%d)...\n",hSession,pSeed,ulSeedLen));
	DBG_TRACE(("C_SeedRandom()=0x%08x\n",CKR_FUNCTION_NOT_SUPPORTED));
	return CKR_FUNCTION_NOT_SUPPORTED;
}

/* C_GenerateRandom generates random data. */
CK_DEFINE_FUNCTION(CK_RV, C_GenerateRandom)
(
  CK_SESSION_HANDLE hSession,    /* the session's handle */
  CK_BYTE_PTR       pRandomData,  /* receives the random data */
  CK_ULONG          ulRandomLen  /* # of bytes to generate */
)
{
	DBG_TRACE(("C_GenerateRandom(0x%08x,0x%08x,%d)...\n",hSession,pRandomData,ulRandomLen));

	CK_RV rvResult = CKR_OK;
	CSession * pP11Session = NULL;

	if (!(g_ulGlobalFlags & FLAG_LIBRERIA_INICIALIZADA))
		return (CKR_CRYPTOKI_NOT_INITIALIZED);

	rvResult = g_ExclMutua.LockMutex();
	if (rvResult != CKR_OK)
		return rvResult;

	try
	{
		//
		// Obtenemos el puntero a sesión
		//
		pP11Session = gSessionList.FindSession(hSession);

		rvResult = pP11Session->GenerateRandom(pRandomData, ulRandomLen);
	}
	catch (Pkcs11Exception & e)
	{
		rvResult = e.getErrId();

		if( (rvResult == CKR_SESSION_HANDLE_INVALID) && pP11Session )
			gSessionList.DeleteSessionAndHandles(hSession);
	}

	DBG_TRACE(("C_GenerateRandom()=0x%08x\n",rvResult));

	g_ExclMutua.UnlockMutex();

	return rvResult;
}


/* Parallel function management */

/* C_GetFunctionStatus is a legacy function; it obtains an
 * updated status of a function running in parallel with an
 * application. */
CK_DEFINE_FUNCTION(CK_RV, C_GetFunctionStatus)
(
  CK_SESSION_HANDLE hSession  /* the session's handle */
)
{
	DBG_TRACE(("C_GetFunctionStatus(0x%08x)...\n",hSession));

	if (!(g_ulGlobalFlags & FLAG_LIBRERIA_INICIALIZADA))
		return (CKR_CRYPTOKI_NOT_INITIALIZED);

	DBG_TRACE(("C_GetFunctionStatus()=0x%08x\n",CKR_FUNCTION_NOT_SUPPORTED));
	return CKR_FUNCTION_NOT_SUPPORTED;
}


/* C_CancelFunction is a legacy function; it cancels a function
 * running in parallel. */
CK_DEFINE_FUNCTION(CK_RV, C_CancelFunction)
(
  CK_SESSION_HANDLE hSession  /* the session's handle */
)
{
	DBG_TRACE(("C_CancelFunction(0x%08x)...\n",hSession));

	if (!(g_ulGlobalFlags & FLAG_LIBRERIA_INICIALIZADA))
		return (CKR_CRYPTOKI_NOT_INITIALIZED);

	DBG_TRACE(("C_CancelFunction()=0x%08x\n",CKR_FUNCTION_NOT_SUPPORTED));
	return CKR_FUNCTION_NOT_SUPPORTED;
}


/* Functions added in for Cryptoki Version 2.01 or later */

/* C_WaitForSlotEvent waits for a slot event (token insertion,
 * removal, etc.) to occur. */
CK_DEFINE_FUNCTION(CK_RV, C_WaitForSlotEvent)
(
  CK_FLAGS flags,        /* blocking/nonblocking flag */
  CK_SLOT_ID_PTR pSlot,  /* location that receives the slot ID */
  CK_VOID_PTR pRserved   /* reserved.  Should be NULL_PTR */
)
{
	CK_RV rvResult			= CKR_OK;
	LONG lResult			= SCARD_S_SUCCESS;

	DWORD dwTimeout			= 1000;	// 1 segundo

	DBG_TRACE(("C_WaitForSlotEvent(0x%08x,0x%08x,0x%08x)...\n",flags,pSlot,pRserved));

	if (!(g_ulGlobalFlags & FLAG_LIBRERIA_INICIALIZADA))
		return (CKR_CRYPTOKI_NOT_INITIALIZED);

	// Incrementamos el número de threads que están esperando eventos en todos los slots
	g_ulThreadsWaiting++;

	try
	{
		// Si los flags nos indican que no se bloquee el timeout es 0
		if (flags & CKF_DONT_BLOCK)
			dwTimeout = 0;

		do
		{
			// Comprobamos que no se haya hecho un C_Finalize entre medias
			if (!(g_ulGlobalFlags & FLAG_LIBRERIA_INICIALIZADA))
				return (CKR_CRYPTOKI_NOT_INITIALIZED);

			lResult = g_rSlotList.WaitForSlotStatusChange(pSlot, dwTimeout);
		}
		while ((lResult == SCARD_E_TIMEOUT) && !(flags & CKF_DONT_BLOCK));

		if (lResult == SCARD_S_SUCCESS)
		{
			// La funcion EsperarCambioEstado devuelve OK cuando se ha
			// producido un cambio de estado en algun slot. Devuelve
			// el identificador del slot cuyo cambio de estado se ha
			// producido.
			rvResult = CKR_OK;
		}
		else if ((lResult == SCARD_E_TIMEOUT) && (flags & CKF_DONT_BLOCK))
		{
			// Se ha cumplido el timeout y los flags nos indican que
			// no se bloquee la funcion. Por lo tanto el error
			// a indicar es que no se ha producido ningun evento.
			throw Pkcs11Exception(CKR_NO_EVENT);
		}
		else
			throw Pkcs11Exception(CSlot::PCSCErr2P11Err(lResult));

	}
	catch (Pkcs11Exception & e)
	{
		rvResult = e.getErrId();
	}

	// Decrementamos el número de threads en situación de espera
	g_ulThreadsWaiting--;

	DBG_TRACE(("C_WaitForSlotEvent()=0x%08x\n", rvResult));

	return rvResult;
}
