/* HINT: That is a thread entry point and wrapper for the real serverloop. */
 OpcUa_Void OpcUa_P_SocketManager_ServerLoopThread(OpcUa_Void* a_pArgument)
{
    OpcUa_StatusCode                uStatus                 = OpcUa_Good;                   /* only needed for internal reasons */
    OpcUa_InternalSocketManager*    pInternalSocketManager  = (OpcUa_InternalSocketManager*)a_pArgument;
    OpcUa_Int32                     iSocketManagerSlot;

    OpcUa_Trace(OPCUA_TRACE_LEVEL_INFO, "NetworkThread: Message Loop started...\n");

    do
    {
        uStatus = OpcUa_P_SocketManager_ServeLoopInternal(  pInternalSocketManager,
                                                            OpcUa_UInt32_Max,
                                                            OpcUa_False);

        if(OpcUa_IsEqual(OpcUa_GoodShutdownEvent))
        {
            /* leave this loop if a shutdown was signalled */
            break;
        }

    } while(OpcUa_IsGood(uStatus));

    /* Debug Output */
    OpcUa_Trace(OPCUA_TRACE_LEVEL_INFO, "NetworkThread: Message Loop shutting down! (0x%08X)\n", uStatus);

    if(pInternalSocketManager->Flags.bSpawnThreadOnAccept != 0)
    {
#if OPCUA_USE_SYNCHRONISATION
        OpcUa_P_Mutex_Lock(pInternalSocketManager->pMutex);
#endif /* OPCUA_USE_SYNCHRONISATION */

        for(iSocketManagerSlot = 0; iSocketManagerSlot < OPCUA_SOCKET_MAXMANAGERS; iSocketManagerSlot++)
        {
            if(pInternalSocketManager->pSocketManagers[iSocketManagerSlot] != OpcUa_Null)
            {
                OpcUa_InternalSocketManager *pSpawnedSocketManager = pInternalSocketManager->pSocketManagers[iSocketManagerSlot];
                pInternalSocketManager->pSocketManagers[iSocketManagerSlot] = OpcUa_Null;

                pSpawnedSocketManager->pThreadToJoin  = pInternalSocketManager->pThreadToJoin;
                pInternalSocketManager->pThreadToJoin = pSpawnedSocketManager->pThread;
                pSpawnedSocketManager->pThread        = OpcUa_Null;

                OpcUa_P_SocketManager_InterruptLoop(pSpawnedSocketManager,
                                                    OPCUA_SOCKET_SHUTDOWN_EVENT,
                                                    OpcUa_False);
            }
        }

#if OPCUA_USE_SYNCHRONISATION
        OpcUa_P_Mutex_Unlock(pInternalSocketManager->pMutex);
#endif /* OPCUA_USE_SYNCHRONISATION */

        if(pInternalSocketManager->pThreadToJoin != OpcUa_Null)
        {
            OpcUa_P_Thread_Delete(&pInternalSocketManager->pThreadToJoin);
        }
    }

    return;
}
/*
 * ToDo: problems with RSA_PKCS1_OAEP_PADDING -> RSA_PKCS1_PSS_PADDING is
 * needed (Version 0.9.9); RSA_PKCS1_OAEP_PADDING is just for encryption
 */
OpcUa_StatusCode OpcUa_P_OpenSSL_RSA_Private_Sign(
    OpcUa_CryptoProvider* a_pProvider,
    OpcUa_ByteString      a_data,
    OpcUa_Key*            a_privateKey,
    OpcUa_Int16           a_padding,          /* e.g. RSA_PKCS1_PADDING */
    OpcUa_ByteString*     a_pSignature)       /* output length >= key length */
{
    EVP_PKEY*               pSSLPrivateKey  = OpcUa_Null;
    const unsigned char*    pData           = OpcUa_Null;
    int                     iErr            = 0;

OpcUa_InitializeStatus(OpcUa_Module_P_OpenSSL, "RSA_Private_Sign");

    /* unused parameters */
    OpcUa_ReferenceParameter(a_pProvider);
    OpcUa_ReferenceParameter(a_padding);

    /* check parameters */
    OpcUa_ReturnErrorIfArgumentNull(a_privateKey);
    OpcUa_ReturnErrorIfArgumentNull(a_pSignature);
    pData = a_privateKey->Key.Data;
    OpcUa_ReturnErrorIfArgumentNull(pData);
    OpcUa_ReturnErrorIfTrue((a_privateKey->Type != OpcUa_Crypto_KeyType_Rsa_Private), OpcUa_BadInvalidArgument);

    /* convert private key and check key length against buffer length */
    pSSLPrivateKey = d2i_PrivateKey(EVP_PKEY_RSA, OpcUa_Null, &pData, a_privateKey->Key.Length);
    OpcUa_GotoErrorIfTrue((pSSLPrivateKey == OpcUa_Null), OpcUa_BadUnexpectedError);
    OpcUa_GotoErrorIfTrue((a_pSignature->Length < RSA_size(pSSLPrivateKey->pkey.rsa)), OpcUa_BadInvalidArgument);

    /* sign data */
    iErr = RSA_sign(NID_sha1, a_data.Data, a_data.Length, a_pSignature->Data, (unsigned int*)&a_pSignature->Length, pSSLPrivateKey->pkey.rsa);
    OpcUa_GotoErrorIfTrue((iErr != 1), OpcUa_BadUnexpectedError);

    /* free internal key representation */
    EVP_PKEY_free(pSSLPrivateKey);

OpcUa_ReturnStatusCode;
OpcUa_BeginErrorHandling;

    if(OpcUa_IsEqual(OpcUa_BadUnexpectedError))
    {
        long    lErr    = ERR_get_error();
        char*   szErr   = ERR_error_string(lErr, 0);

        if(szErr != OpcUa_Null)
        {
            OpcUa_P_Trace("*** RSA_Private_Sign: ");
            OpcUa_P_Trace(szErr);
            OpcUa_P_Trace(" ***\n");
        }
    }

    if(pSSLPrivateKey != OpcUa_Null)
    {
        EVP_PKEY_free(pSSLPrivateKey);
    }

OpcUa_FinishErrorHandling;
}