/**@@@+++@@@@******************************************************************
**
** Microsoft Windows Media
** Copyright (C) Microsoft Corporation. All rights reserved.
**
***@@@---@@@@******************************************************************
*/

#include <drmcommon.h>
#include <drmcontextsizes.h>
#include <drmutilities.h>
#include <drmcrt.h>
#include <drmmanager.h>
#include <drmblackbox.h>
#include <drmpkcrypto.h>
#include <drmheaderparser.h>
#include <drmlicenseparser.h>
#include <drmlicacq.h>
#include <drmcipher.h>
#include <drmviewprops.h>
#include <drmchain.h>
#include <drmsecureclock.h>
#include <drmsyncstore.h>
#include <drmsecurestore.h>
#include <drmlicreason.h>
#include <oemimpl.h>
#include <drmxmlparser.h>
#include <drmopcodes.h>
#if DRM_SUPPORT_REVOCATION 
#include <drmrevocation.h>
#endif

#if DRM_SUPPORT_DATASTORE_PREALLOC == 1
#define DATASTORE_INITSIZE      512     /* prealloc 512 K for initial datastore size */
#define DATASTORE_GROWSIZE      512     /* grow 512 K for each datastore expansion */
#endif



DRM_VOID drmmanager_casserts (DRM_VOID)
{
    
    /* If any of these compile time asserts fire it is because size of internal contexts don't match */
    DRMSIZEASSERT (SIZEOF (DRM_MANAGER_DECRYPT_CONTEXT),  SIZEOF (DRM_CIPHER_CONTEXT));
    DRMSIZEASSERT (SIZEOF (DRM_MANAGER_ENCRYPT_CONTEXT),  SIZEOF (DRM_CIPHER_CONTEXT));    
#ifdef __unix__ 
	DRMSIZEASSERT (SIZEOF (DRM_MANAGER_CONTEXT), 2 * SIZEOF (DRM_MANAGER_CONTEXT_INTERNAL));
#else
	DRMSIZEASSERT (SIZEOF (DRM_MANAGER_CONTEXT), SIZEOF (DRM_MANAGER_CONTEXT_INTERNAL));
#endif
    /* If any of these compile time asserts fire it is because buffers aren't aligned as they should and could cause data type misalignment errors */
#if !SIXTEEN_BIT_ADDRESSING
    DRMCASSERT    ((DRM_OFFSET_OF (DRM_MANAGER_CONTEXT_INTERNAL, rgbDRMHeaderData) % SIZEOF (DRM_WCHAR)) == 0);
    DRMCASSERT    ((DRM_OFFSET_OF (DRM_MANAGER_CONTEXT_INTERNAL, rgbDRMLicense)    % SIZEOF (DRM_WCHAR)) == 0);        

    /* If any of these compile time asserts fire it is because buffers in the DRM manager context have been moved
       and we rely on them being adjacent. */
    DRMCASSERT( ENSURE_MEMBERS_ADJACENT(DRM_MANAGER_CONTEXT_INTERNAL, rgbDRMHeaderData, rgbDRMLicense) );    
#endif
    
}

static DRM_VOID _FreeDrmManagerInternalContexts( DRM_MANAGER_CONTEXT_INTERNAL *pContext )
{
    if( pContext )
    {
        if( pContext->fLicStoreOpen )
        {
            DRM_LST_Close( &pContext->oLicStoreContext ); 
            pContext->fLicStoreOpen = FALSE; 
        }
        if( pContext->fSecStoreGlobalContextOpen ) 
        { 
            DRM_SST_CloseKey( &pContext->oSecStoreGlobalContext, &( pContext->oHdsContext )  ); 
            pContext->fSecStoreGlobalContextOpen = FALSE; 
        } 
    }
}    

static DRM_WORD      _dwDeviceStoreBlockSize  =32*1024;
static DRM_HDSBLKNUM _eDeviceStoreBlockNumType=eDRM_HDSBLKNUM_DWORD;

/* 
** Create the Deivce store
*/
static DRM_RESULT DRM_API _CreateDeviceStore( 
    const DRM_WCHAR       *pwszDeviceStoreName, 
          DRM_HDS_CONTEXT *poHdsContext )
{
    DRM_RESULT dr=DRM_SUCCESS;

#if DRM_SUPPORT_DATASTORE_PREALLOC == 1
    dr = DRM_HDS_CreateStore2(pwszDeviceStoreName, 
                              _dwDeviceStoreBlockSize, 
                              _eDeviceStoreBlockNumType, 
                              poHdsContext, 
                              TRUE,
                              DATASTORE_INITSIZE);
#else
    dr = DRM_HDS_CreateStore(pwszDeviceStoreName, 
                             _dwDeviceStoreBlockSize, 
                             _eDeviceStoreBlockNumType, 
                             poHdsContext, 
                             TRUE);
#endif

    if ( dr ==  DRM_E_FILEWRITEERROR)
    {
        /*
        **  Some other process created the store concurrently. We can consider
        **  this operation as successful as store has been created. In the worst
        **  subsequent call to Open store will fail
        */
        dr = DRM_SUCCESS;
    }
    ChkDR(dr);

ErrorExit:
    if( DRM_FAILED( dr ) )
    {
        TRACE(("_CreateDeviceStore(): Device store '%S' cannot be created.\n", 
            pwszDeviceStoreName));
    }
    return dr;
}

static DRM_RESULT _SetupLicEvalObjectToShare( 
    DRM_MANAGER_CONTEXT_INTERNAL *pContext )
{
    DRM_RESULT dr = DRM_SUCCESS;
    DRM_BYTE bBBVer[__CB_DECL(VERSION_LEN)];


    ZEROMEM(&(pContext->oLicEvalContext), SIZEOF( DRM_LICEVAL_CONTEXT ) );
    MEMCPY(bBBVer, 
           pContext->oBlackBoxContext.cachedCertValues.m_BBCompatibilityCert.pk.version, 
           VERSION_LEN);
    ChkDR(DRM_UTL_GetVersionAsString(bBBVer, pContext->wszBBVerString));

    pContext->oLicEvalContext.dstrBBVer.pwszString =        pContext->wszBBVerString;
    pContext->oLicEvalContext.dstrBBVer.cchString  = DRM_wcslen(pContext->wszBBVerString);
    pContext->oLicEvalContext.dstrDRMVer           = g_dstrDRM_VERSION_STRING;
    INIT_DRM_STRING( pContext->oLicEvalContext.dstrContentHeader );

    if (pContext->eHeaderInContext==eHeaderIsV2)
    {    
        DRM_CONST_STRING dstrKID = EMPTY_DRM_STRING;
        
        DSTR_FROM_PB( &pContext->oLicEvalContext.dstrContentHeader, 
                       pContext->rgbDRMHeaderData,
                       pContext->cbHeaderData );

        ChkDR( DRM_HDR_GetAttribute( &(pContext->oLicEvalContext.dstrContentHeader), 
                                     NULL, 
                                     DRM_HEADER_ATTRIB_KID, 
                                     &dstrKID, 
                                     0 ) );

        ChkDR( DRM_UTL_DecodeKID( &dstrKID, &pContext->KID ) );
    }
    else if (pContext->eHeaderInContext==eHeaderIsV1)
    {
        DRM_WCHAR wszKID[CCH_BASE64_EQUIV(SIZEOF(DRM_KID))] = { 0 };
        DRM_CONST_STRING dstrKID   = EMPTY_DRM_STRING;
        DRM_SUBSTRING    dasstrKID = { 0 };
        DRM_V1Header    *pV1Header = (DRM_V1Header*)pContext->rgbDRMHeaderData;

        /* make sure the size of encoded KID is no more than CCH_BASE64_EQUIV(SIZEOF(DRM_KID)) */
        dasstrKID.m_ich = 0;
        dasstrKID.m_cch = ( pV1Header->cbKeyID <= CCH_BASE64_EQUIV(SIZEOF(DRM_KID)) )? 
                            pV1Header->cbKeyID : 
                            CCH_BASE64_EQUIV(SIZEOF(DRM_KID));
        
        dstrKID.cchString = CCH_BASE64_EQUIV(SIZEOF(DRM_KID));
        dstrKID.pwszString = wszKID;
        
        DRM_UTL_PromoteANSItoUNICODE((DRM_CHAR*)(pV1Header->pbKeyID), &dasstrKID, (DRM_STRING*)&dstrKID);

        ChkDR( DRM_UTL_DecodeKID( &dstrKID, &pContext->KID ) );
    }
    else if (pContext->eHeaderInContext==eKidOnly)
    {
        DRM_CONST_STRING dstrKID   = EMPTY_DRM_STRING;
        DSTR_FROM_PB( &dstrKID, 
                       pContext->rgbDRMHeaderData,
                       pContext->cbHeaderData );
        
        ChkDR( DRM_UTL_DecodeKID( &dstrKID, &pContext->KID ) );
    }
    else
    {
        /* no content header is given */
        ZEROMEM(pContext->KID.rgb, SIZEOF( pContext->KID.rgb ) );
    }
    
    ChkDR( DRM_LST_Open(&pContext->oLicStoreContext, &( pContext->oHdsContext ) ) );
    pContext->fLicStoreOpen = TRUE;

    /* Result is first SHA_DIGEST_LEN bytes of rgbDRMLicense */
    ChkDR(DRM_SST_CreateGlobalStorePassword(pContext->rgbDRMLicense, 
                                            (DRM_BYTE *) &pContext->oBlackBoxContext)); 

    /* Open the global key */
    ChkDR( DRM_SST_OpenKeyTokens( &pContext->oSecStoreGlobalContext, 
                        (DRM_ID *)&g_rgbSecStoreGlobalName, 
                                   NULL,
                                   pContext->rgbDRMLicense, 
                                   DRM_SECURE_STORE_CREATE_IF_NOT_EXISTS,
                                   SECURE_STORE_GLOBAL_DATA, 
                                  &pContext->oHdsContext ) );
    pContext->fSecStoreGlobalContextOpen = TRUE;
    
    pContext->oLicEvalContext.dwFlags     = LICEVAL_OPEN_CHECK_SETTINGS;    
    pContext->oLicEvalContext.pcontextBBX = &pContext->oBlackBoxContext;

    if (pContext->eHeaderInContext==eHeaderIsV2)
    {
        DSTR_FROM_PB( &pContext->oLicEvalContext.dstrContentHeader,
                       pContext->rgbDRMHeaderData,
                       pContext->cbHeaderData );
    }
    else
    {
        /* either V1 header or no header is given */
        INIT_DRM_STRING( pContext->oLicEvalContext.dstrContentHeader );
    }

#if DRM_SUPPORT_REVOCATION
    pContext->oLicEvalContext.fUpdatedRevocationList    =  FALSE;
    pContext->oLicEvalContext.idCRLsCurrent             =  pContext->idCRLsCurrent;
#endif
    pContext->oLicEvalContext.pbRevocationBuffer        =  pContext->pbRevocationBuffer;
    pContext->oLicEvalContext.cbRevocationBuffer        =  pContext->cbRevocationBuffer;
    pContext->oLicEvalContext.pcontextHDS               = &pContext->oHdsContext;
    pContext->oLicEvalContext.pcontextSSTGlobal         = &pContext->oSecStoreGlobalContext;
    pContext->oLicEvalContext.pcontextSSTLicense        = &pContext->rgcontextSST[0];
#if DRM_SUPPORT_CERTIFICATE_CACHING
    pContext->oLicEvalContext.pcontextSSTRevocation     = &pContext->contextSSTCertCache;
#else
    pContext->oLicEvalContext.pcontextSSTRevocation     =  NULL;
#endif    
    pContext->oLicEvalContext.fGlobalSecStoreWritable   =  FALSE;
    pContext->oLicEvalContext.fAppInfoValid             =  TRUE;
    pContext->oLicEvalContext.certinfoApp.appSec        =  pContext->oBlackBoxContext.cachedCertValues.appSec;
    pContext->oLicEvalContext.certinfoApp.appcd_subject =  pContext->oBlackBoxContext.cachedCertValues.appcd_subject;
    pContext->oLicEvalContext.certinfoSDK.appSec        =  3000;
    pContext->oLicEvalContext.certinfoSDK.appcd_subject =  0;
    pContext->oLicEvalContext.pLicStoreEnumContext      = &(pContext->oLicEnum [0]);

ErrorExit:
    return dr;
}

#if DRM_SUPPORT_DLA
static DRM_RESULT _PrepareForClientInfo( 
    DRM_MANAGER_CONTEXT_INTERNAL *pContext )
{
    DRM_RESULT       dr          = DRM_SUCCESS;
    DRM_CONST_STRING dstrValue   = EMPTY_DRM_STRING;
    
    /* Get the device certificate */
    DSTR_FROM_PB( &pContext->licensechallenge.dstrDeviceCert, pContext->rgbDRMLicense, SIZEOF( pContext->rgbDRMLicense ) );
    
    ChkDR( DRM_DDC_GetDeviceCertificate(
            (DRM_STRING*)&pContext->licensechallenge.dstrDeviceCert, 
            0,
            &pContext->oBlackBoxContext.CryptoContext) );

    /* Get the security level */
    ChkDR (DRM_DCP_GetAttribute (&pContext->licensechallenge.dstrDeviceCert,
                                 DRM_DEVCERT_GROUPSECURITYLEVEL, 
                                 NULL,
                                 &dstrValue));

    ChkDR (wcsntol( dstrValue.pwszString, 
                    dstrValue.cchString, 
      (DRM_LONG *) &pContext->licensechallenge.levelAppSecurity));

    /* Get the subject id */

    ChkDR (DRM_DCP_GetAttribute (&pContext->licensechallenge.dstrDeviceCert, 
                                 DRM_DEVCERT_SUBJECTID, 
                                 NULL, 
                                 &dstrValue));

    ChkDR (wcsntol( dstrValue.pwszString, 
                    dstrValue.cchString, 
      (DRM_LONG *) &pContext->licensechallenge.idSubject));

    ChkDR( DRM_BBX_GetClientId(&pContext->licensechallenge.clientid, &pContext->oBlackBoxContext));

    pContext->licensechallenge.pwszBBVer    =  pContext->oBlackBoxContext.cachedCertValues.wszLegacyVersion;
    pContext->licensechallenge.pcontextBBX  = &pContext->oBlackBoxContext;
ErrorExit:
    return ( dr );

}
#endif  /* DRM_SUPPORT_DLA */


#if DRM_SUPPORT_SECURE_CLOCK
static DRM_RESULT _CheckSecureClock( DRM_MANAGER_CONTEXT_INTERNAL *pContext );
#endif

static DRM_RESULT _MapDRMError(
    DRM_RESULT dr)
{
    return dr;
}

static DRM_RESULT _CompareMachineId( 
    DRM_CONST_STRING *pdstrMachineId )
{
    DRM_RESULT dr = DRM_SUCCESS;    
    DRM_WCHAR        wchUniqueId[MAX_UNIQUEID_LEN];
    DRM_CONST_STRING dstrID = EMPTY_DRM_STRING;

    ChkDRMString( pdstrMachineId );
    
    dstrID.cchString  = NO_OF( wchUniqueId );
    dstrID.pwszString =        wchUniqueId;
    
    ChkDR( OEM_GetUniqueID( wchUniqueId, &dstrID.cchString ) );    
    ChkBOOL( DRM_UTL_DSTRStringsEqual( pdstrMachineId, &dstrID ), DRM_E_MACHINEIDMISMATCH );

ErrorExit:
    return dr;
}

DRM_RESULT DRM_API DRM_MGR_Initialize( 
    IN       DRM_MANAGER_CONTEXT  *pDrmContext,
    IN const DRM_CONST_STRING     *pdstrDeviceStoreName )
{
    DRM_RESULT       dr                    = DRM_SUCCESS;
    DRM_LONG         lValue                = 0;
    DRM_CONST_STRING dstrValue             = EMPTY_DRM_STRING;    
    DRM_STRING       dstrDeviceCert        = EMPTY_DRM_STRING;
    DRM_MANAGER_CONTEXT_INTERNAL *pContext = (DRM_MANAGER_CONTEXT_INTERNAL*) pDrmContext;

    DRM_PROFILING_ENTER_SCOPE(L"DRM_MGR_Initialize", g_pwszEnteringFunction, DRM_PROFILING_START_IF_NOT_STARTED);

    ChkArg(pDrmContext);
    ChkDRMString(pdstrDeviceStoreName);
    ZEROMEM(pDrmContext, SIZEOF (DRM_MANAGER_CONTEXT));

    pContext->fStoreOpened = FALSE;

    /* Copy Device Store file name in DRM Manager Context */
    if(pdstrDeviceStoreName->cchString > DRM_MAX_PATH)
    {
        ChkDR( DRM_E_INVALIDARG );
    }
#ifdef __unix__
    DSTR_FROM_PB( &dstrDeviceCert, pContext->rgbDRMLicense, 2*SIZEOF( pContext->rgbDRMLicense ) );
#else
	DSTR_FROM_PB( &dstrDeviceCert, pContext->rgbDRMLicense, SIZEOF( pContext->rgbDRMLicense ) );
#endif
    
    ChkDR( DRM_DDC_GetDeviceCertificate( 
            &dstrDeviceCert, 
            DRM_DCP_CREATE_DEVCERT_IF_NOT_EXISTING | DRM_DCP_VERIFY_DEVCERT,
            &pContext->oBlackBoxContext.CryptoContext) );

    ChkDR( DRM_DCP_LoadPropertiesCache(
            (DRM_CONST_STRING*)&dstrDeviceCert,
            &pContext->oBlackBoxContext.cachedCertValues,
            &pContext->oBlackBoxContext.CryptoContext));
    
    /* Compare UniqueID */
    ChkDR( DRM_DCP_GetAttribute(
            (DRM_CONST_STRING*)&dstrDeviceCert, 
            DRM_DEVCERT_SERIALNUMBER, 
            NULL,
            &dstrValue) );

    ChkDR( _CompareMachineId( &dstrValue ) );

    ChkDR( DRM_BBX_Initialize( &pContext->oBlackBoxContext ) );

    pContext->eHeaderInContext = eHeaderNotSet;

    ChkDR( OEM_StringCchCopyN( (DRM_WCHAR*)pContext->rgbDRMLicense,
                               SIZEOF( pContext->rgbDRMLicense ) / SIZEOF( DRM_WCHAR ),
                               pdstrDeviceStoreName->pwszString,
                               pdstrDeviceStoreName->cchString ) );
    
    /* Initialize device store. Open if not yet opened. Create if not yet created. */
    ChkDR( DRM_HDS_Init( &( pContext->oHdsContext ) ) );

#if DRM_SUPPORT_DATASTORE_PREALLOC == 1
    dr = DRM_HDS_OpenStore2(((DRM_WCHAR*)pContext->rgbDRMLicense), 
                            &( pContext->oHdsContext ),
                            DATASTORE_GROWSIZE);
#else
    dr = DRM_HDS_OpenStore(((DRM_WCHAR*)pContext->rgbDRMLicense), 
                           &( pContext->oHdsContext ));
#endif
    
    if ( dr == DRM_E_FILENOTFOUND )   /* store does not exist */
    {
        ChkDR(_CreateDeviceStore(((DRM_WCHAR*)pContext->rgbDRMLicense), &( pContext->oHdsContext ) ) );
        
#if DRM_SUPPORT_DATASTORE_PREALLOC == 1
        dr = DRM_HDS_OpenStore2(((DRM_WCHAR*)pContext->rgbDRMLicense), 
                                &( pContext->oHdsContext ),
                                DATASTORE_GROWSIZE);
#else
        dr = DRM_HDS_OpenStore(((DRM_WCHAR*)pContext->rgbDRMLicense), 
                               &( pContext->oHdsContext ));
#endif
    }
    ChkDR(dr);

    pContext->fStoreOpened = TRUE;

    ChkDR(_SetupLicEvalObjectToShare( pContext ));

    /* Explicitly make the global secure store writeabele */
    pContext->oLicEvalContext.fGlobalSecStoreWritable = TRUE;

#if DRM_SUPPORT_SECURE_CLOCK
    if ( pContext->oBlackBoxContext.cachedCertValues.dwFeatureFlags & DRM_FEATURE_SECURE_CLOCK )
    {
        ChkDR( _CheckSecureClock( pContext ) );
    }
#endif

#if DRM_SUPPORT_ANTIROLLBACK_CLOCK
    if ( pContext->oBlackBoxContext.cachedCertValues.dwFeatureFlags & DRM_FEATURE_ANTI_ROLLBACK_CLOCK )
    {
        ChkDR(DRM_LIC_CheckClockRollback(
                &pContext->oLicEvalContext,
                &pContext->oLicStoreContext,
                &pContext->oLicEnum[0],
                pContext->rgbDRMHeaderData + __CB_DECL(pContext->cbHeaderData),
                SIZEOF( pContext->rgbDRMHeaderData ) - pContext->cbHeaderData + SIZEOF(pContext->rgbDRMLicense),
                &( pContext->oHdsContext ) ) );
    }
#endif

#if DRM_SUPPORT_LICENSE_SYNC
    ChkDR( DRM_SNC_OpenStore( &pContext->oHdsContext, &pContext->contextSync ) );
#endif

ErrorExit:
    DRM_PROFILING_LEAVE_SCOPE(L"DRM_MGR_Initialize", g_pwszLeavingFunction);

    _FreeDrmManagerInternalContexts( pContext );

    if( DRM_FAILED( dr )
        && pContext != NULL )
    {
        DRM_MGR_Uninitialize( pDrmContext );
    }
        
    return ( dr );
}


DRM_VOID DRM_API DRM_MGR_Uninitialize( 
    IN DRM_MANAGER_CONTEXT *pDrmContext )
{
    DRM_RESULT dr = DRM_SUCCESS;
    DRM_MANAGER_CONTEXT_INTERNAL *pContext = (DRM_MANAGER_CONTEXT_INTERNAL*) pDrmContext;
    DRM_RESULT dr2 = DRM_SUCCESS;
    DRM_PROFILING_ENTER_SCOPE(L"DRM_MGR_Uninitialize", g_pwszEnteringFunction, DRM_PROFILING_DONT_START);

    ChkArg( pDrmContext );

    if (pContext->fBindThenCommit)
    {
        pContext->fBindThenCommit = FALSE;
    }

#if DRM_SUPPORT_LICENSE_SYNC
    (void)DRM_SNC_CloseStore(&pContext->contextSync);
#endif

    if ( pContext->fStoreOpened )
    {
        (void)DRM_HDS_CloseStore( &( pContext->oHdsContext ) );
    }
    (void)DRM_HDS_Uninit( &pContext->oHdsContext );
    ZEROMEM(&( pContext->oHdsContext ), SIZEOF( pContext->oHdsContext ) );

ErrorExit:
    DRM_PROFILING_LEAVE_SCOPE(L"DRM_MGR_Uninitialize", g_pwszLeavingFunction);
    DRM_PRO_StopProfiling();
    return ;    
}

DRM_RESULT DRM_API DRM_MGR_Reinitialize( 
    IN       DRM_MANAGER_CONTEXT  *pDrmContext )
{
    DRM_RESULT                    dr        = DRM_SUCCESS;
    DRM_MANAGER_CONTEXT_INTERNAL *pContext  = (DRM_MANAGER_CONTEXT_INTERNAL*) pDrmContext;

    ChkArg( pDrmContext != NULL );

    pContext->eHeaderInContext = eHeaderNotSet;
    ZEROMEM( pContext->rgbDRMHeaderData, SIZEOF( pContext->rgbDRMHeaderData ) );
    pContext->cbHeaderData = 0;

ErrorExit:
    return dr;
}

#if DRM_SUPPORT_REVOCATION
DRM_RESULT DRM_API DRM_MGR_SetRevocationBuffer( 
    IN       DRM_MANAGER_CONTEXT  *pDrmContext,
    IN       DRM_BYTE             *pbRevocationBuffer,
    IN       DRM_DWORD             cbRevocationBuffer )
{
    DRM_RESULT       dr                    = DRM_SUCCESS;
    DRM_MANAGER_CONTEXT_INTERNAL *pContext = (DRM_MANAGER_CONTEXT_INTERNAL*) pDrmContext;

    ChkArg( pDrmContext != NULL );

    pContext->pbRevocationBuffer = pbRevocationBuffer;
    pContext->cbRevocationBuffer = cbRevocationBuffer;
    pContext->oLicEvalContext.pbRevocationBuffer = pbRevocationBuffer;
    pContext->oLicEvalContext.cbRevocationBuffer = cbRevocationBuffer;


ErrorExit:
    return dr;
}

DRM_RESULT DRM_API DRM_MGR_GetRevocationBuffer( 
    IN       DRM_MANAGER_CONTEXT  *pDrmContext,
    OUT      DRM_BYTE             **ppbRevocationBuffer,
    OUT      DRM_DWORD             *pcbRevocationBuffer )
{
    DRM_RESULT       dr                    = DRM_SUCCESS;
    DRM_MANAGER_CONTEXT_INTERNAL *pContext = (DRM_MANAGER_CONTEXT_INTERNAL*) pDrmContext;

    ChkArg( pDrmContext != NULL
        && ppbRevocationBuffer != NULL 
        && pcbRevocationBuffer != NULL );

    *ppbRevocationBuffer = pContext->pbRevocationBuffer;
    *pcbRevocationBuffer = pContext->cbRevocationBuffer;

ErrorExit:
    return dr;
}

DRM_RESULT DRM_API DRM_MGR_UpdateRevocationVersionsCache( 
 IN OUT DRM_MANAGER_CONTEXT          *pDrmContext,
    OUT DRM_BOOL                     *pfUpdated)
{
    DRM_RESULT dr = DRM_SUCCESS;
    DRM_MANAGER_CONTEXT_INTERNAL *pContext = (DRM_MANAGER_CONTEXT_INTERNAL *)pDrmContext;

    ChkArg( pDrmContext != NULL );
    
    /* Result is first SHA_DIGEST_LEN bytes of rgbDRMLicense */
    ChkDR(DRM_SST_CreateGlobalStorePassword(pContext->rgbDRMLicense, 
                                            (DRM_BYTE *) &pContext->oBlackBoxContext)); 

    /* Open the global key */
    ChkDR( DRM_SST_OpenKeyTokens( &pContext->oSecStoreGlobalContext, 
                        (DRM_ID *)&g_rgbSecStoreGlobalName, 
                                   NULL,
                                   pContext->rgbDRMLicense, 
                                   DRM_SECURE_STORE_CREATE_IF_NOT_EXISTS,
                                   SECURE_STORE_GLOBAL_DATA, 
                                  &pContext->oHdsContext ) );
    pContext->fSecStoreGlobalContextOpen = TRUE;

    ChkDR( DRM_RVK_UpdateRevocationVersionsCache( pContext, 
                                                    pfUpdated ) );

ErrorExit:

    _FreeDrmManagerInternalContexts( pContext );
    
    return dr;
}

DRM_RESULT DRM_MGR_StoreRevocationLists(
    IN DRM_MANAGER_CONTEXT *f_pContext,
    IN DRM_DWORD f_cRevocationLists,
    IN DRM_RVK_LIST *f_pRevocationLists )
{
    DRM_RESULT dr = DRM_SUCCESS;
    DRM_MANAGER_CONTEXT_INTERNAL *pContext = (DRM_MANAGER_CONTEXT_INTERNAL*)f_pContext;

    ChkArg( pContext != NULL
         && f_pRevocationLists != NULL
         && f_cRevocationLists > 0 );

    ChkDR (_SetupLicEvalObjectToShare (pContext));


    ChkDR( DRM_RVK_StoreRevocationLists( &pContext->oLicEvalContext,
                       &pContext->oHdsContext,
                       &pContext->oSecStoreGlobalContext,
                       &pContext->oBlackBoxContext,
                        f_cRevocationLists,
                        f_pRevocationLists ) );

ErrorExit:
    _FreeDrmManagerInternalContexts( pContext );
    
    return dr;
}
#endif

DRM_RESULT DRM_API DRM_MGR_SetV2Header(
    IN       DRM_MANAGER_CONTEXT *pDrmContext,
    IN const DRM_BYTE            *pbHeader,
    IN       DRM_DWORD            cbHeader )
{
    DRM_RESULT                    dr        = DRM_SUCCESS;
    DRM_DWORD                     cchHeader = 0;
    DRM_MANAGER_CONTEXT_INTERNAL *pContext  = (DRM_MANAGER_CONTEXT_INTERNAL*) pDrmContext;

    ChkArg (pDrmContext                    != NULL
         && pbHeader                       != NULL 
         && cbHeader                        > 0 
         && (cbHeader % SIZEOF (DRM_WCHAR) == 0)); /* cbHeader must be even */

    if (pContext->fBindThenCommit)
    {
        pContext->fBindThenCommit = FALSE;
    }

    if( cbHeader > SIZEOF( pContext->rgbDRMHeaderData ) )
    {
        dr = DRM_E_INVALIDARG;  /* The header is too big for our buffer. */
        goto ErrorExit;
    }
    
    MEMCPY(pContext->rgbDRMHeaderData, pbHeader, cbHeader);
    pContext->cbHeaderData = cbHeader;
    cchHeader              = cbHeader / SIZEOF (DRM_WCHAR);
    
    if (((DRM_WCHAR*) (pContext->rgbDRMHeaderData)) [cchHeader - 1] == g_wchNull)
    {
        /* Strip off the NULL terminating character if it is there */
        pContext->cbHeaderData -= SIZEOF (DRM_WCHAR);
    }

    pContext->eHeaderInContext = eHeaderIsV2;

ErrorExit:
    return _MapDRMError(dr);
}

DRM_RESULT DRM_API DRM_MGR_SetV1Header(
    IN       DRM_MANAGER_CONTEXT *pDrmContext,
    IN const DRM_BYTE            *pbKeyID,
    IN       DRM_DWORD            cbKeyID,
    IN const DRM_BYTE            *pbSecretData,
    IN       DRM_DWORD            cbSecretData,
    IN const DRM_BYTE            *pbURL,
    IN       DRM_DWORD            cbURL)
{
    DRM_RESULT                    dr = DRM_SUCCESS;
    DRM_MANAGER_CONTEXT_INTERNAL *pContext = (DRM_MANAGER_CONTEXT_INTERNAL*) pDrmContext;
    DRM_V1Header                 *pV1Header = NULL;
    DRM_DWORD                     dwEncKidSize=0;

    ChkArg(pDrmContext);

    if (pContext->fBindThenCommit)
    {
        pContext->fBindThenCommit = FALSE;
    }

    if ( pContext->eHeaderInContext == eHeaderIsV2 )
    {
        goto ErrorExit; /* ignore this call if we already have a V2 header */
    }
    ChkArg( pbKeyID      != NULL && cbKeyID      > 0 );
    ChkArg( pbSecretData != NULL && cbSecretData > 0 );
    ChkArg( pbURL        != NULL && cbURL        > 0 );
    ChkArg( cbURL + cbKeyID + cbSecretData + SIZEOF(DRM_V1Header) <= SIZEOF(pContext->rgbDRMHeaderData) );

    pV1Header = (DRM_V1Header*)pContext->rgbDRMHeaderData;

    pV1Header->cbKeyID = 4 * (cbKeyID / 4);  /* KeyID length must be mutilple of 4 bytes. */
    pV1Header->pbKeyID = pV1Header->bBuffer;
    MEMCPY(pV1Header->pbKeyID, pbKeyID, cbKeyID);

    pV1Header->cbSecretData = cbSecretData;
    /* For 16-bit addressing the key ID must lie on a word boundary */
    DRMASSERT( cbKeyID % CB_NATIVE_BYTE == 0 );
    pV1Header->pbSecretData = &(pV1Header->bBuffer[ __CB_DECL(cbKeyID) ]);
    MEMCPY(pV1Header->pbSecretData, pbSecretData, cbSecretData);

    pV1Header->cbURL = cbURL;
    /* For 16-bit addressing the URL must lie on a word boundary */
    DRMASSERT( ( cbKeyID + cbSecretData ) % CB_NATIVE_BYTE == 0 );
    pV1Header->pbURL = &(pV1Header->bBuffer[ __CB_DECL(cbKeyID + cbSecretData) ]);
    MEMCPY(pV1Header->pbURL, pbURL, cbURL);

    pContext->cbHeaderData     = cbURL + cbKeyID + cbSecretData + SIZEOF(DRM_V1Header);
    pContext->eHeaderInContext = eHeaderIsV1;

ErrorExit:
    return _MapDRMError(dr);
}

#if DRM_SUPPORT_DLA

static DRM_RESULT _GetUplinkState( 
    IN const DRM_CONST_STRING        *f_pdstrAction,
    IN       DRM_DWORD                f_iKID,
    IN OUT   DRM_LICENSE_CHALLENGE   *f_plicensechallenge,
    IN       DRM_VIEW_RIGHTS_CONTEXT *f_pcontextVR,
    IN       DRM_HDS_CONTEXT         *f_pcontextHDS )
{
    DRM_RESULT dr = DRM_SUCCESS;
    const DRM_CONST_STRING *rgpdstrRights [1] = { NULL };

    rgpdstrRights [0] = f_pdstrAction;       

    ChkDR( DRM_UTL_DecodeKID( &f_plicensechallenge->rgdstrUplinkKID [f_iKID], 
                              &f_pcontextVR->KID ) );
            
    ChkDR( DRM_ASD_GetLicenseAggregateData( rgpdstrRights, 
                                           &f_plicensechallenge->rglicensestatedata[f_iKID], 
                                            1, 
                                            f_pcontextVR, 
                                            f_pcontextHDS,
                                            FALSE,
                                            DRM_ASD_AGGREGATE_SIMPLE_AND_LEAF_LICENSES ) );
ErrorExit:
    return dr;
}

DRM_RESULT DRM_API DRM_MGR_GenerateLicenseChallenge(
    IN       DRM_MANAGER_CONTEXT  *pDrmContext,
    IN const DRM_CONST_STRING    **ppdstrRights, /* Array of DRM_CONST_STRING pointers */
    IN       DRM_DWORD             cRights,
       OUT   DRM_WCHAR            *pwszURL,
    IN OUT   DRM_DWORD            *pcchURL,
       OUT   DRM_CHAR             *pszChallenge,
    IN OUT   DRM_DWORD            *pcchChallenge )
{
    DRM_MANAGER_CONTEXT_INTERNAL *pcontextMGR       = (DRM_MANAGER_CONTEXT_INTERNAL *) pDrmContext;
    DRM_CONST_STRING              dstrLAINFO        = EMPTY_DRM_STRING;
    DRM_CONST_STRING              dstrdevcert       = EMPTY_DRM_STRING;
    DRM_DWORD  cbKID      = 0;
    DRM_DWORD  cbChallenge= 0;
    DRM_DWORD  cbRequired = 0;
    DRM_BOOL   fTooSmall  = FALSE;
    DRM_RESULT dr         = DRM_SUCCESS;
    DRM_BOOL   fInit      = FALSE;           

    DRM_PROFILING_ENTER_SCOPE(L"DRM_MGR_GenerateLicenseChallenge", g_pwszEnteringFunction, DRM_PROFILING_START_IF_NOT_STARTED);

    ChkArg (pDrmContext   != NULL
        &&  pcchChallenge != NULL);

    ChkArg ((cRights  > 0 && ppdstrRights != NULL)
        ||  (cRights == 0 && ppdstrRights == NULL));

    if (pcontextMGR->fBindThenCommit)
    {
        pcontextMGR->fBindThenCommit = FALSE;
    }

    if (pcontextMGR->eHeaderInContext == eHeaderIsV1)
    {
        ChkDR (DRM_E_V1_NOT_SUPPORTED);  /* V1 Lic Acq not supported */
    }
    ZEROMEM(&(pcontextMGR->licensechallenge), SIZEOF (DRM_LICENSE_CHALLENGE));

    if (pcontextMGR->eHeaderInContext == eHeaderIsV2)
    {

        DSTR_FROM_PB( &pcontextMGR->licensechallenge.dstrHeader,
                       pcontextMGR->rgbDRMHeaderData,
                       pcontextMGR->cbHeaderData );

        dr = DRM_HDR_GetAttribute ( &pcontextMGR->licensechallenge.dstrHeader, 
                                     NULL, 
                                     DRM_HEADER_ATTRIB_LAINFO, 
                                    &dstrLAINFO, 
                                     0);
        if (DRM_FAILED(dr))
        {
            /*Make sure this string is empty*/
            dr = DRM_SUCCESS;
            dstrLAINFO.cchString = 0;
            dstrLAINFO.pwszString = NULL;            
        }

        pcontextMGR->oViewRightsContext.pBBContext       = &pcontextMGR->oBlackBoxContext;
        for (cbRequired=0; cbRequired<DRM_MAX_LICENSE_CHAIN_DEPTH; cbRequired++)
        {
            pcontextMGR->oViewRightsContext.rgpLicQueryContext[cbRequired] =  &pcontextMGR->oLicEnum[cbRequired];
        }
        pcontextMGR->oViewRightsContext.pbGlobalSecStore = &pcontextMGR->oSecStoreGlobalContext;
        pcontextMGR->oViewRightsContext.pbLIDSecStore    = &pcontextMGR->rgcontextSST[0];
        pcontextMGR->oViewRightsContext.pbLicenseStoreXML= &pcontextMGR->oLicStoreContext;
        pcontextMGR->oViewRightsContext.pLicEval         = &pcontextMGR->oLicEvalContext;
        pcontextMGR->oViewRightsContext.pbBuffer         =  pcontextMGR->rgbDRMHeaderData 
                                                         +  __CB_DECL(pcontextMGR->cbHeaderData);
        pcontextMGR->oViewRightsContext.cbBuffer         =  SIZEOF (pcontextMGR->rgbDRMHeaderData) 
                                                         -          pcontextMGR->cbHeaderData 
                                                         +  SIZEOF (pcontextMGR->rgbDRMLicense);

        /* Start at index 1.  If we actaully find an uplink index 0 will hold the data for the KID of the header itself */
        pcontextMGR->licensechallenge.cUplinks = 1;
        while (DRM_SUCCEEDED (DRM_HDR_GetUplink( &pcontextMGR->licensechallenge.dstrHeader,
                                                  pcontextMGR->licensechallenge.cUplinks - 1,
                                                 &pcontextMGR->licensechallenge.rgdstrUplinkKID [pcontextMGR->licensechallenge.cUplinks])))
        {
            if (!fInit)
            {
                ChkDR( _SetupLicEvalObjectToShare( pcontextMGR ) );
                fInit = TRUE; 
            }

            ChkDR( _GetUplinkState( ppdstrRights[0],
                                    pcontextMGR->licensechallenge.cUplinks,
                                   &pcontextMGR->licensechallenge,
                                   &pcontextMGR->oViewRightsContext,
                                   &pcontextMGR->oHdsContext) );
            
            if( ++pcontextMGR->licensechallenge.cUplinks >= NO_OF (pcontextMGR->licensechallenge.rgdstrUplinkKID) )
            {
                break;
            }
        }
        if( fInit )
        {
            /*
            ** fInit is TRUE if we processed an UPLINK so we know they are there.
            ** Now add ASD for the KID in the header
            */
            ChkDR( DRM_HDR_GetAttribute( &pcontextMGR->licensechallenge.dstrHeader,
                                          NULL,
                                          DRM_HEADER_ATTRIB_KID,
                                         &pcontextMGR->licensechallenge.rgdstrUplinkKID[0],
                                          0 ) );
            ChkDR( _GetUplinkState( ppdstrRights[0],
                                    0,
                                   &pcontextMGR->licensechallenge,
                                   &pcontextMGR->oViewRightsContext,
                                   &pcontextMGR->oHdsContext) );
        }
        else
        {
            pcontextMGR->licensechallenge.cUplinks = 0;
        }
    }
    /*Check for the URL. If user asked for it and we dont have it, return an error*/    
    if (dstrLAINFO.cchString == 0 && pcchURL != NULL)
    {
        ChkDR(DRM_E_NO_URL);
    }

    /*
    ** It will prepare context for Client info node. 
    ** Devcert will loaded into the license buffer.
    */
    ChkDR( _PrepareForClientInfo( pcontextMGR ) );

    pcontextMGR->licensechallenge.ppdstrRights = ppdstrRights;
    pcontextMGR->licensechallenge.cRights      = cRights;

    /* request data now complete and cached in plicensechallenge; query for the required sizes */ 
    dr = DRM_LA_CreateChallenge(
            &pcontextMGR->licensechallenge,
            NULL, 
            &cbChallenge);

    if (dr != DRM_E_BUFFERTOOSMALL)
    {
        ChkDR (dr);
        
        dr = DRM_SUCCESS;
    }
    
    cbRequired = cbChallenge;
    if (pszChallenge == NULL
    || *pcchChallenge < (cbRequired + SIZEOF (DRM_CHAR)))
    {
        fTooSmall = TRUE;
    }
    
    if (pcchURL != NULL   
    && *pcchURL <  (dstrLAINFO.cchString + SIZEOF (DRM_CHAR)))
    {
        fTooSmall = TRUE;
    }
    
    if (pcchURL != NULL   
    &&  pwszURL == NULL)
    {
        fTooSmall = TRUE;
    }

    /* if any of the above conditionals evaluate TRUE,
       return the required size(s) */
    
    if (fTooSmall != FALSE)
    {
        if (pcchURL != NULL)
        {
            *pcchURL = dstrLAINFO.cchString + SIZEOF(DRM_CHAR);
        }

        *pcchChallenge = cbRequired + SIZEOF(DRM_CHAR);
        
        ChkDR (DRM_E_BUFFERTOOSMALL);
    }

    /* challenge buffer holds ANSI chars, no need to align it */
    ChkDR (DRM_LA_CreateChallenge (&pcontextMGR->licensechallenge,
                                    pszChallenge,
                                   &cbChallenge));

    if (pwszURL != NULL)
    {
        /*
        **  The buffer in pwszURL is the same size as the encoded URL.
        **  Hence no need to check for DRM_E_BUFFERTOOSMALL
        */

        /* We will be appending a NULL later, so we need to reserve that space */
        (*pcchURL)--;
        
        ChkDR( DRM_UTL_XMLDecode( dstrLAINFO.pwszString, 
                                  dstrLAINFO.cchString, 
                                  pwszURL,
                                  pcchURL ) );
                    
        /* We're now guaranteed to have this extra space for the NULL */
        pwszURL[ (*pcchURL)++ ] = g_wchNull;

    }

    *pcchChallenge = cbChallenge;    
    
    PUT_CHAR(pszChallenge, *pcchChallenge, '\0');

ErrorExit:
    _FreeDrmManagerInternalContexts (pcontextMGR);
    DRM_PROFILING_LEAVE_SCOPE(L"DRM_MGR_GenerateLicenseChallenge", g_pwszLeavingFunction);

    return dr;
}

/******************************************************************************
** 
** Function :   DRM_MGR_GetDeviceProperty
** 
** Synopsis :   Gets the device property.
** 
** Arguments :  f_pDrmContext           - pointer to DRM_MANAGER_CONTEXT
**              f_eProperty             - property type
**              f_pbProperties          - Output buffer for property
**              f_pcbProperties         - Size of output buffer; If this is too 
**                                        small, it is updated with the required
**                                        size.
** 
** Returns :    DRM_E_BUFFERTOOSMALL - output buffer too small
**              other errors

** Notes :      WM_DRM_CLIENTINFO is UNICODE blob which is NOT NULL terminated
**              WM_DRM_DRMVERSION is UNICODE blob which is NOT NULL terminated
**              WM_DRM_SECURITYVERSION is UNICODE blob which is NOT NULL terminated
**              WM_DRM_V2_HEADER_KID is base64 encoded KID UNICODE blob which is NOT NULL terminated
** 
******************************************************************************/
DRM_RESULT DRM_API DRM_MGR_GetDeviceProperty(
    IN       DRM_MANAGER_CONTEXT  *f_pDrmContext,
    IN       DRM_DEVICE_PROPERTY   f_eProperty, 
       OUT   _XMBContext          *f_pbProperty,
    IN OUT   DRM_DWORD            *f_pcbProperty )
{
    DRM_MANAGER_CONTEXT_INTERNAL *pcontextMGR       = (DRM_MANAGER_CONTEXT_INTERNAL *) f_pDrmContext;
    DRM_RESULT       dr             = DRM_SUCCESS;
    DRM_DWORD        cchEncoded     = 0; 
    DRM_CONST_STRING dstrScratch    = EMPTY_DRM_STRING;    
    DRM_DWORD        cbMinRequired  = 0;

    DRM_PROFILING_ENTER_SCOPE(L"DRM_MGR_GetDeviceProperty", g_pwszEnteringFunction, DRM_PROFILING_START_IF_NOT_STARTED);

    ChkArg (f_pDrmContext != NULL
        &&  f_pcbProperty != NULL);

    if (pcontextMGR->fBindThenCommit)
    {
        pcontextMGR->fBindThenCommit = FALSE;
    }

    switch(f_eProperty)
    {
        case WM_DRM_CLIENTINFO:
        {
            ZEROMEM(&(pcontextMGR->licensechallenge), SIZEOF (DRM_LICENSE_CHALLENGE));

            cchEncoded = SIZEOF(pcontextMGR->licensechallenge.rgbStack);
            
            /*It will prepare context for Client info node.*/
            ChkDR( _PrepareForClientInfo( pcontextMGR ) );
            dstrScratch.pwszString =         pcontextMGR->licensechallenge.pwszBBVer;
            dstrScratch.cchString  = DRM_wcslen (pcontextMGR->licensechallenge.pwszBBVer);

            dr = DRM_LA_GetClientInfo(
                    &pcontextMGR->licensechallenge.clientid,
                    pcontextMGR->licensechallenge.rgbStack,
                    &cchEncoded,
                    (DRM_CONST_STRING *)  &g_dstrReqTagClientVersionData,
                    &dstrScratch,
                    pcontextMGR->licensechallenge.levelAppSecurity,
                    pcontextMGR->licensechallenge.idSubject,
                    0,
                    NULL,
                    &pcontextMGR->licensechallenge.dstrDeviceCert,
                    NULL,
                    &cbMinRequired,
                    &pcontextMGR->oBlackBoxContext.CryptoContext,
                    TRUE);
            
            if (dr != DRM_E_BUFFERTOOSMALL)
            {
                ChkDR (dr);
                
                dr = DRM_SUCCESS;
            }
            if ( f_pbProperty == NULL 
            ||  *f_pcbProperty < cbMinRequired )
            {
                *f_pcbProperty = cbMinRequired;
                ChkDR(DRM_E_BUFFERTOOSMALL);
            }

            ChkDR( DRM_LA_GetClientInfo(
                    &pcontextMGR->licensechallenge.clientid,
                    pcontextMGR->licensechallenge.rgbStack,
                    &cchEncoded,
                    (DRM_CONST_STRING *)  &g_dstrReqTagClientVersionData,
                    &dstrScratch,
                    pcontextMGR->licensechallenge.levelAppSecurity,
                    pcontextMGR->licensechallenge.idSubject,
                    0,
                    NULL,
                    &pcontextMGR->licensechallenge.dstrDeviceCert,
                    f_pbProperty,
                    f_pcbProperty,
                    &pcontextMGR->oBlackBoxContext.CryptoContext,
                    TRUE));
        }
        break;
        case WM_DRM_DRMVERSION:
        {
            cbMinRequired = CB_DSTR(&g_dstrReqTagClientVersionData);

            if ( f_pbProperty == NULL 
            ||  *f_pcbProperty < cbMinRequired )
            {
                *f_pcbProperty = cbMinRequired;
                ChkDR(DRM_E_BUFFERTOOSMALL);
            }
            MEMCPY(f_pbProperty, PB_DSTR(&g_dstrReqTagClientVersionData), cbMinRequired );
            *f_pcbProperty = cbMinRequired;
        }
        break;
        case WM_DRM_SECURITYVERSION:
        {        
            cbMinRequired = DRM_wcslen(pcontextMGR->oBlackBoxContext.cachedCertValues.wszLegacyVersion) * SIZEOF(DRM_WCHAR);

            if ( f_pbProperty == NULL 
            ||  *f_pcbProperty < cbMinRequired )
            {
                *f_pcbProperty = cbMinRequired;
                ChkDR(DRM_E_BUFFERTOOSMALL);
            }
            MEMCPY(f_pbProperty, pcontextMGR->oBlackBoxContext.cachedCertValues.wszLegacyVersion, cbMinRequired );
            *f_pcbProperty = cbMinRequired;
        }
        break;
        case WM_DRM_V2_HEADER_KID:
        {        
            cbMinRequired = 0;

            if (pcontextMGR->eHeaderInContext == eHeaderIsV2)
            {
                DRM_CONST_STRING dstrKID = EMPTY_DRM_STRING;

                DSTR_FROM_PB( &pcontextMGR->oLicEvalContext.dstrContentHeader, 
                               pcontextMGR->rgbDRMHeaderData,
                               pcontextMGR->cbHeaderData );

                ChkDR( DRM_HDR_GetAttribute( &(pcontextMGR->oLicEvalContext.dstrContentHeader), 
                                             NULL, 
                                             DRM_HEADER_ATTRIB_KID, 
                                             &dstrKID, 
                                             0 ) );
                cbMinRequired = CB_DSTR (&dstrKID);
                if ( f_pbProperty == NULL 
                ||  *f_pcbProperty < cbMinRequired )
                {
                    *f_pcbProperty = cbMinRequired;
                    ChkDR(DRM_E_BUFFERTOOSMALL);
                }
                
                MEMCPY(f_pbProperty, PB_DSTR (&dstrKID), cbMinRequired );
                *f_pcbProperty = cbMinRequired;

            }
            else
            {

                ChkDR(DRM_E_HEADER_NOT_SET);
            }

        }
        break;
        default:
        {
            ChkDR(DRM_E_UNKNOWN_PROPERTY);
        }
    }

ErrorExit:

    DRM_PROFILING_LEAVE_SCOPE(L"DRM_MGR_GetDeviceProperty", g_pwszLeavingFunction);
    return dr;
}

#endif     /* DRM_SUPPORT_DLA */


DRM_RESULT DRM_API DRM_MGR_ProcessLicenseResponse(
    IN DRM_MANAGER_CONTEXT     *f_pDrmContext,
    IN pfnStoreLicenseCallback  f_pfnCallback,
    IN DRM_VOID                *f_pvCallbackContext,
    IN DRM_BYTE                *f_pbResponse,
    IN DRM_DWORD                f_cbResponse )
{
    DRM_RESULT dr = DRM_SUCCESS;
    DRM_DWORD  i  = 0;
    DRM_MANAGER_CONTEXT_INTERNAL *pContext           = (DRM_MANAGER_CONTEXT_INTERNAL*) f_pDrmContext;
    DRM_VIEW_RIGHTS_CONTEXT      *pcontextViewRights = &pContext->oViewRightsContext;

    DRM_PROFILING_ENTER_SCOPE(L"DRM_MGR_ProcessLicenseResponse", g_pwszEnteringFunction, DRM_PROFILING_START_IF_NOT_STARTED);

    ChkArg (f_pDrmContext != NULL);
    if(!pContext->fStoreOpened)
    {
        ChkDR (DRM_E_DRMNOTINIT);
    }
    
    if (pContext->fBindThenCommit)
    {
        pContext->fBindThenCommit = FALSE;
    }

    ChkDR (_SetupLicEvalObjectToShare (pContext));    
    
    pcontextViewRights->pBBContext       = &pContext->oBlackBoxContext;
    for (i=0; i<DRM_MAX_LICENSE_CHAIN_DEPTH; i++)
    {
        pcontextViewRights->rgpLicQueryContext[i] =  &pContext->oLicEnum[i];
    }

    pcontextViewRights->pbGlobalSecStore = &pContext->oSecStoreGlobalContext;
    pcontextViewRights->pbLIDSecStore    = &pContext->rgcontextSST[0];
    pcontextViewRights->pbLicenseStoreXML= &pContext->oLicStoreContext;
    pcontextViewRights->pLicEval         = &pContext->oLicEvalContext;
    pcontextViewRights->pbBuffer         =  pContext->rgbDRMHeaderData 
                                         +  __CB_DECL(pContext->cbHeaderData);
    pcontextViewRights->cbBuffer         =  SIZEOF (pContext->rgbDRMHeaderData) 
                                         +  SIZEOF (pContext->rgbDRMLicense)
                                         -  pContext->cbHeaderData;    
    
#if DRM_SUPPORT_APP_REVOCATION
    pContext->oLicEvalContext.idCRLsCurrent.app = DRM_APP_REVOCATION_VERSION_NONE;
    pContext->oLicEvalContext.fUpdatedRevocationList = FALSE;
    dr = DRM_MGR_GetRevocationListVersion( (DRM_MANAGER_CONTEXT*)pContext,
                                           WM_DRM_REVOCATION_TYPE_APP,
                                          &pContext->oLicEvalContext.idCRLsCurrent.app );
    if( DRM_FAILED(dr) && dr == DRM_E_FILENOTFOUND )
    {
        dr = DRM_SUCCESS;
    }
    else
    {
        ChkDR( dr );
    }
#endif

#if DRM_SUPPORT_REVOCATION
    {
        DRM_ANSI_CONST_STRING dastrRevocationInfoData;

        DASTR_FROM_PB(&dastrRevocationInfoData, f_pbResponse, f_cbResponse);

        ChkDR( DRM_RVK_ProcessRevocationInfo(&pContext->oLicEvalContext,
                                             &pContext->oHdsContext,
                                             &pContext->oSecStoreGlobalContext,
                                             &pContext->oBlackBoxContext,
                                             &dastrRevocationInfoData) );
    }
#endif /* DRM_SUPPORT_REVOCATION */
    dr = DRM_LA_ProcessResponse( f_pbResponse, 
                                 f_cbResponse, 
                                &pContext->oLicEvalContext,
                                &pContext->oLicStoreContext,
                                 f_pfnCallback, 
                                 f_pvCallbackContext,
                                &pContext->oHdsContext,
                                 pcontextViewRights,
                                 pContext->rgbDRMLicense,
#if DRM_SUPPORT_LICENSE_SYNC
                                &pContext->contextSync,
#else
                                 NULL,
#endif
                                 NULL);

#if DRM_SUPPORT_REVOCATION
    if( pContext->oLicEvalContext.fUpdatedRevocationList )
    {
        pContext->idCRLsCurrent = pContext->oLicEvalContext.idCRLsCurrent;
        pContext->oFFLicense.idCRLsCurrent = pContext->oLicEvalContext.idCRLsCurrent;
        pContext->oFFLicense.fUpdatedRevocationList = TRUE;
    }
#endif

ErrorExit:

    DRM_PROFILING_LEAVE_SCOPE(L"DRM_MGR_ProcessLicenseResponse", g_pwszLeavingFunction);
    _FreeDrmManagerInternalContexts( pContext );
    return dr;
}

#if DRM_SUPPORT_METERING
typedef struct __tagBindCommit
{
    DRM_BYTE                rgbPassword  [__CB_DECL(SHA_DIGEST_LEN)];
    const DRM_CONST_STRING *rgpdstrActions [DRM_MAX_ACTIONS];
    DRM_DWORD               cActions;
} DRM_BIND_COMMIT_DATA;
#endif


/*****************************************************************************
** Function: DRM_MGR_Bind
**
** Synopsis: enumerate the passed-in license list and chain through, reporting
**           actions
** Arguments:
**           [f_pDrmContext]   -- initialized DRM_MANAGER context
**           [f_rgpdstrRights] -- array of constant strings (must not be stack
**                                variables) representing rights
**           [f_cRights]       -- size of f_rgpdstrRights array
**           [f_pfnOutputLevelsCallback]  -- callback for OPL
**           [f_pv]            -- passed through to f_pfnOutputLevelsCallback
*****************************************************************************/

DRM_RESULT DRM_API DRM_MGR_Bind(
    IN       DRM_MANAGER_CONTEXT         *f_pDrmContext,
    IN const DRM_CONST_STRING            *f_rgpdstrRights [],
    IN       DRM_DWORD                    f_cRights,
    IN       DRMPFNOUTPUTLEVELSCALLBACK   f_pfnOutputLevelsCallback,
    IN const DRM_VOID                    *f_pv,
       OUT   DRM_MANAGER_DECRYPT_CONTEXT *f_pcontextDCRY )
{
    DRM_RESULT dr  = DRM_SUCCESS;
    DRM_RESULT dr2 = DRM_SUCCESS;
    DRM_DWORD  cbLicContext = 0;
    DRM_BOOL   fEvalResult  = FALSE;    
#if DRM_SUPPORT_METERING
    DRM_BIND_COMMIT_DATA *pbcd   = NULL;
    DRM_DWORD             iRight = 0;
#endif
    DRM_MANAGER_CONTEXT_INTERNAL *pContext = (DRM_MANAGER_CONTEXT_INTERNAL*) f_pDrmContext;

    DRM_PROFILING_ENTER_SCOPE(L"DRM_MGR_Bind", g_pwszEnteringFunction, DRM_PROFILING_START_IF_NOT_STARTED);

    ChkArg(f_pDrmContext  != NULL );

    ChkArg ( (f_rgpdstrRights != NULL && f_cRights  > 0 && f_cRights <= DRM_MAX_ACTIONS)
        ||   (f_rgpdstrRights == NULL && f_cRights == 0) );

    if (pContext->fBindThenCommit)
    {
        pContext->fBindThenCommit = FALSE;
    }

    if (!pContext->fStoreOpened)
    {
        ChkDR (DRM_E_DRMNOTINIT);
    }

    for( cbLicContext = 0; cbLicContext < f_cRights; cbLicContext++ )
    {
        ChkDRMString( f_rgpdstrRights[cbLicContext] );
    }
    
    if( pContext->cbHeaderData     == 0 
     || pContext->eHeaderInContext == eHeaderNotSet ) 
    {
        dr = DRM_E_HEADER_NOT_SET;
        goto ErrorExit;
    }

    if (pContext->eHeaderInContext != eKidOnly)
    {
        ChkArg(f_pcontextDCRY != NULL );
    }


    pContext->dwChainDepth = 0;
    pContext->dwSourceid   = 0;
#if DRM_SUPPORT_REVOCATION
    pContext->dwLicRevInfoVer = 0;
#endif
    /* v2 header is unicode, # of bytes must be a even number. */
    DRMASSERT( (pContext->eHeaderInContext == eHeaderIsV2) && 
               !ISODD(pContext->cbHeaderData) );

    ChkDR(_SetupLicEvalObjectToShare( pContext ));

    if ( pContext->eHeaderInContext==eHeaderIsV2 )
    {
        DRM_CONST_STRING dstrCheckSum = EMPTY_DRM_STRING;

        /* Extract all the other pieces to fill the bindinginfo structure.  If they don't exists in the header */
        ChkDR( DRM_HDR_GetAttribute(
                &pContext->oLicEvalContext.dstrContentHeader, 
                NULL, 
                DRM_HEADER_ATTRIB_CHECKSUM, 
                &dstrCheckSum, 
                0) );

        /* If the following base64 decode fails because the buffer isn't big enough then it isn't a valid checksum anyway. */
        cbLicContext = CHECKSUM_LENGTH;
        ZEROMEM(pContext->oBindInfo[0].m_rgbChecksum, CHECKSUM_LENGTH );
        ChkDR(DRM_B64_DecodeW(&dstrCheckSum, &cbLicContext, pContext->oBindInfo[0].m_rgbChecksum, 0));
        if( cbLicContext != CHECKSUM_LENGTH )
        {
            dr = DRM_E_INVALIDLICENSE;
            goto ErrorExit;
        }
    }


    /* Fill the FindFirstLicense structure */
    ZEROMEM(&pContext->oFFLicense, SIZEOF( pContext->oFFLicense ) );

#if DRM_SUPPORT_REVOCATION
    pContext->oFFLicense.idCRLsCurrent     =  pContext->idCRLsCurrent;
#endif
    pContext->oFFLicense.pLicEval          = &pContext->oLicEvalContext;
    pContext->oFFLicense.pLicStore         = &pContext->oLicStoreContext;
    pContext->oFFLicense.pLicStoreEnum     =  pContext->oLicEnum;
    pContext->oFFLicense.pbBuffer          =  pContext->rgbDRMHeaderData 
                                           +  __CB_DECL(pContext->cbHeaderData); 
    pContext->oFFLicense.cbBuffer          =  SIZEOF (pContext->rgbDRMHeaderData)
                                           +  SIZEOF (pContext->rgbDRMLicense) 
                                           -  pContext->cbHeaderData;
    pContext->oFFLicense.pBindingInfo      =  pContext->oBindInfo;
    pContext->oFFLicense.pSecStoreLicense  =  pContext->rgcontextSST;
    pContext->oFFLicense.dwPolicyFlags     = 0;

    if (pContext->eHeaderInContext == eKidOnly)
    {
        pContext->oFFLicense.fCanBind = TRUE;
    }

    
    ChkDR(DRM_UTL_EnsureDataAlignment(
            pContext->oFFLicense.pbBuffer, 
            pContext->oFFLicense.cbBuffer, 
           &pContext->oFFLicense.pbBuffer, 
           &pContext->oFFLicense.cbBuffer, 
            SIZEOF (DRM_DWORD_PTR), 
            NULL));

    /* Query for licenses based on KID 
    ** Note: pContext->KID should be set up at this point 
    */
    ChkDR( DRM_LST_InitEnum(&pContext->oLicStoreContext, &pContext->KID, TRUE, &pContext->oLicEnum[0]) );
    while( TRUE )
    {   
        dr = DRM_LST_EnumNext( 
                &pContext->oLicEnum                 [0],
                &pContext->oFFLicense.rgkid         [0], 
                &pContext->oFFLicense.rglid         [0], 
                &(pContext->oFFLicense.rgslotHint   [0]),
                NULL); 

        if (dr == DRM_E_NOMORE)
        {
            dr = DRM_E_LICENSENOTFOUND;
            goto ErrorExit;
        }
        ChkDR( dr );

        if ( pContext->eHeaderInContext == eHeaderIsV1 )
        {                
            pContext->oFFLicense.pV1Header = (DRM_V1Header*)pContext->rgbDRMHeaderData;
        }
        else
        {
            pContext->oFFLicense.pV1Header = NULL;
        }
#if DRM_SUPPORT_SECURE_CLOCK
        if ( (pContext->oBlackBoxContext.cachedCertValues.dwFeatureFlags & DRM_FEATURE_SECURE_CLOCK) 
          && !pContext->fClockSet )
        {
           pContext->oLicEvalContext.fIgnoreTimeBoundLicense = TRUE;
        }
#endif            
        if ( (pContext->oBlackBoxContext.cachedCertValues.dwFeatureFlags & DRM_FEATURE_SECURE_CLOCK) == 0 
          && (pContext->oBlackBoxContext.cachedCertValues.dwFeatureFlags & DRM_FEATURE_ANTI_ROLLBACK_CLOCK) == 0 )
        {
           pContext->oLicEvalContext.fIgnoreTimeBoundLicense = TRUE;
        }

        dr = DRM_LIC_CompleteLicenseChain(&pContext->oFFLicense, 
                                           f_rgpdstrRights, 
                                           f_cRights,
                                          &pContext->dwChainDepth, 
                                          &pContext->oHdsContext,
                                           f_pfnOutputLevelsCallback,
                                           f_pv);

#if DRM_SUPPORT_REVOCATION
        /* If the call failed due to revocation, we should make sure the
        *  revocation version cache in the DRM_MANAGER_CONTEXT is up to date.
        *  If there have been any changes we should try again.
        */
        if(    dr == DRM_E_RIV_TOO_SMALL
            || dr == DRM_E_LICEVAL_REQUIRED_REVOCATION_LIST_NOT_AVAILABLE )
        {
            DRM_BOOL revocationUpdated = FALSE;
            
            DRM_RVK_UpdateRevocationVersionsCache( pContext, &revocationUpdated );

            if( revocationUpdated )
            {
                pContext->oFFLicense.idCRLsCurrent = pContext->idCRLsCurrent;
                
                dr = DRM_LIC_CompleteLicenseChain(&pContext->oFFLicense, 
                                                   f_rgpdstrRights, 
                                                   f_cRights,
                                                  &pContext->dwChainDepth, 
                                                  &pContext->oHdsContext,
                                                   f_pfnOutputLevelsCallback,
                                                   f_pv);
            }
        }
#endif

        if( pContext->oFFLicense.fCanBind )
        {
            goto ErrorExit;
        }

        if( DRM_SUCCEEDED( dr ) )
        {   /* 
            ** update the dwSourceid and dwLicRevInfoVer in DRM_MANAGER_CONTEXT_INTERNAL from 
            ** DRM_FFLICENSE
            */
            pContext->dwSourceid = pContext->oFFLicense.dwSourceid;
#if DRM_SUPPORT_REVOCATION
            pContext->dwLicRevInfoVer = pContext->oFFLicense.dwLicRevInfoVer;
#endif
            
            /*
            ** A license was found.  The license chain has a depth of dwChainDepth.  
            ** Send all the binding info to the blackbox 
            */

            if ( pContext->eHeaderInContext == eHeaderIsV1 )
            {
                DRM_V1Header *pV1Header = (DRM_V1Header*)pContext->rgbDRMHeaderData;

                dr = DRM_BBX_LegacyCipherKeySetup(
                        pContext->oFFLicense.pBindingInfo,
                        pV1Header->pbKeyID, 
                        pV1Header->cbKeyID, 
                        pV1Header->pbSecretData, 
                        pV1Header->cbSecretData,
                        (DRM_CIPHER_CONTEXT*)f_pcontextDCRY,
                       &pContext->oBlackBoxContext );
            }
            else
            {
                dr = DRM_BBX_CipherKeySetup(
                        pContext->oFFLicense.pBindingInfo, 
                        pContext->dwChainDepth, 
                        (DRM_CIPHER_CONTEXT*)f_pcontextDCRY, 
                       &pContext->oBlackBoxContext );
            }
            
            if( DRM_SUCCEEDED( dr ) )
            {
                dr = DRM_LIC_ReportActions(&pContext->oFFLicense,
                                            f_rgpdstrRights,
                                            f_cRights,
                                            pContext->dwChainDepth,
                                           &pContext->oHdsContext );


                if (DRM_SUCCEEDED(dr))
                {
                    dr = DRM_SUCCESS;
                    break;
                }
            }
        }
        else
        {
            DRM_DWORD iDelLic = 0;

            /* Delete any marked licenses since Commit will never do it as we just failed */
            for( iDelLic = 0; iDelLic < pContext->oFFLicense.dwChainDepth; iDelLic++ )
            {
                if( pContext->oFFLicense.rgfDeleteLicense[iDelLic] )
                {
                    /* Don't care if we fail, no reason to hold up anything if we
                       can't delete a license */
                    (void)DRM_LST_DeleteLicense( &pContext->oLicStoreContext, 
                               &pContext->oFFLicense.rgkid[iDelLic], 
                               &pContext->oFFLicense.rglid[iDelLic], 
                               &pContext->oFFLicense.rgslotHint[iDelLic] );
                }
            }
            
            if ( pContext->oLicEvalContext.lReasonForFail == LR_LICENSE_CLOCK_NOT_SET )
            {
                dr2 = DRM_E_CLK_NOT_SET;
            }
            else if ( pContext->oLicEvalContext.lReasonForFail == LR_LICENSE_EXPIRED )
            {
                dr2 = DRM_E_LICENSEEXPIRED;
            }
        }
    }

#if DRM_SUPPORT_METERING
    /* cache the actions/rights to pass to the Commit call */

    pbcd = (DRM_BIND_COMMIT_DATA *) pContext->rgbDRMLicense;

    pbcd->cActions = f_cRights;

    for (iRight = 0; iRight < f_cRights; iRight++)
    {
        pbcd->rgpdstrActions [iRight] = f_rgpdstrRights [iRight];
    }
#endif

    pContext->fBindThenCommit = TRUE;

ErrorExit:
    

    if( DRM_FAILED( dr ) )
    {
        if ( dr2 == DRM_E_CLK_NOT_SET )
        {
            if (pContext->oBlackBoxContext.cachedCertValues.dwFeatureFlags & DRM_FEATURE_SECURE_CLOCK)
            {
                dr = DRM_E_CLK_NOT_SET;
            }
            else
            {
                dr = DRM_E_NO_CLK_SUPPORTED;
            }
        } 
        else if ( DRM_FAILED( dr2 ) 
            && dr == DRM_E_LICENSENOTFOUND )
        {
            dr = dr2;
        }
    }
    _FreeDrmManagerInternalContexts( pContext );
    DRM_PROFILING_LEAVE_SCOPE(L"DRM_MGR_Bind", g_pwszLeavingFunction);
    return _MapDRMError(dr);
}


/*****************************************************************************
** Function: DRM_MGR_GetSourceID
**
** Synopsis: This function should be called after DRM_MGR_Bind returns successfully. 
**           It returns the SourceID in the DRM_MANAGER_CONTEXT that was set in 
**           DRM_MGR_Bind.
** Arguments:
**           IN  *f_pDrmContext    -- initialized DRM_MANAGER context 
**           OUT *f_pdwSourceID
** Returns:
**           DRM_SUCCESS           -- On success
**           DRM_E_INVALIDARG      -- if any argument is invalid
*****************************************************************************/
DRM_RESULT DRM_API DRM_MGR_GetSourceID ( 
    IN     DRM_MANAGER_CONTEXT  *f_pDrmContext,
       OUT DRM_DWORD            *f_pdwSourceID )
{
    DRM_RESULT                    dr       = DRM_SUCCESS;    
    DRM_MANAGER_CONTEXT_INTERNAL *pContext = (DRM_MANAGER_CONTEXT_INTERNAL*) f_pDrmContext;
    
    ChkArg( f_pDrmContext != NULL 
         && f_pdwSourceID != NULL );

    *f_pdwSourceID = pContext->dwSourceid;
    
ErrorExit:
    return dr;
}

#if DRM_SUPPORT_REVOCATION
/*****************************************************************************
** Function: DRM_MGR_GetLicenseRevInfoVersion
**
** Synopsis: This function should be called after DRM_MGR_Bind returns successfully. 
**           It returns the rev info version in the DRM_MANAGER_CONTEXT that was set in 
**           DRM_MGR_Bind.
** Arguments:
**           IN  *f_pDrmContext    -- initialized DRM_MANAGER context 
**           OUT *f_pdwRevInfo
** Returns:
**           DRM_SUCCESS           -- On success
**           DRM_E_INVALIDARG      -- if any argument is invalid
*****************************************************************************/
DRM_RESULT DRM_API DRM_MGR_GetLicenseRevInfoVersion ( 
    IN     DRM_MANAGER_CONTEXT  *f_pDrmContext,
       OUT DRM_DWORD            *f_pdwRevInfo )
{
    DRM_RESULT                    dr       = DRM_SUCCESS;    
    DRM_MANAGER_CONTEXT_INTERNAL *pContext = (DRM_MANAGER_CONTEXT_INTERNAL*) f_pDrmContext;
    
    ChkArg( f_pDrmContext != NULL 
         && f_pdwRevInfo  != NULL );

    *f_pdwRevInfo = pContext->dwLicRevInfoVer;
    
ErrorExit:
    return dr;
}
#endif

DRM_RESULT DRM_API DRM_MGR_Commit( 
    IN     DRM_MANAGER_CONTEXT         *f_pcontextMGR )
{
    DRM_RESULT dr = DRM_SUCCESS;
    DRM_MANAGER_CONTEXT_INTERNAL *pcontextMGR = (DRM_MANAGER_CONTEXT_INTERNAL*) f_pcontextMGR;
#if DRM_SUPPORT_METERING
    DRM_BIND_COMMIT_DATA         *pbcd = NULL;
#endif

    DRM_PROFILING_ENTER_SCOPE(L"DRM_MGR_Commit", g_pwszEnteringFunction, DRM_PROFILING_START_IF_NOT_STARTED);
        
    ChkArg( f_pcontextMGR  != NULL );
    ChkArg(pcontextMGR->fBindThenCommit);

#if DRM_SUPPORT_METERING
    pbcd = (DRM_BIND_COMMIT_DATA *) pcontextMGR->rgbDRMLicense;
#endif

    if(!pcontextMGR->fStoreOpened)
    {
        ChkDR(DRM_E_DRMNOTINIT);
    }

    if(pcontextMGR->dwChainDepth == 0)
    {
        ChkDR(DRM_E_LICENSENOTBOUND);
    }

    for( ; pcontextMGR->dwChainDepth > 0; )
    {
        /* shift from cardinal to ordinal */
        
        pcontextMGR->dwChainDepth--;

        ChkDR( DRM_SST_CloseKey( &pcontextMGR->rgcontextSST[pcontextMGR->dwChainDepth], 
                                 &pcontextMGR->oHdsContext ) );

#if DRM_SUPPORT_METERING
        if (pcontextMGR->oFFLicense.rgfHasMetering [pcontextMGR->dwChainDepth])
        {             
            ChkDR(DRM_MTR_UpdateData(&pcontextMGR->oFFLicense.rgmid [pcontextMGR->dwChainDepth], 
                                     &pcontextMGR->oFFLicense.rglid [pcontextMGR->dwChainDepth],
                                     &pcontextMGR->oFFLicense.rgkid [pcontextMGR->dwChainDepth],
                                      pbcd->rgpdstrActions,
                                      pbcd->cActions,
            (DRM_METERING_CONTEXT *) &pcontextMGR->rgcontextSST [pcontextMGR->dwChainDepth],
                                     &pcontextMGR->oHdsContext,                                    
                                     (DRM_BYTE *) &pcontextMGR->oBlackBoxContext)); 
        }
#endif        
        if (pcontextMGR->oFFLicense.rgfDeleteLicense[pcontextMGR->dwChainDepth])
        {
            ChkDR(DRM_LST_Open(pcontextMGR->oFFLicense.pLicStore,
                               &pcontextMGR->oHdsContext));
            
            dr = DRM_LST_DeleteLicense( pcontextMGR->oFFLicense.pLicStore,
                                       &pcontextMGR->oFFLicense.rgkid[pcontextMGR->dwChainDepth],
                                       &pcontextMGR->oFFLicense.rglid[pcontextMGR->dwChainDepth],
                                       &(pcontextMGR->oFFLicense.rgslotHint[pcontextMGR->dwChainDepth]) );

            if (DRM_FAILED(dr))
            {
                /* try to close the lic store in case of failure */
                (void)DRM_LST_Close(pcontextMGR->oFFLicense.pLicStore);
                ChkDR(dr);
            }
#if DRM_SUPPORT_LICENSE_SYNC            
            dr = DRM_SNC_UpdateKID(&pcontextMGR->contextSync, 
                                   NULL, 
                                   &pcontextMGR->oFFLicense.rgkid[pcontextMGR->dwChainDepth]);

            if (DRM_FAILED(dr))
            {
                /* try to close the lic store in case of failure */
                ChkDR( DRM_LST_Close(pcontextMGR->oFFLicense.pLicStore) );                
            }
#endif

            ChkDR(DRM_LST_Close(pcontextMGR->oFFLicense.pLicStore));
        }
    }

ErrorExit:


    if( pcontextMGR != NULL )
    {
        pcontextMGR->fBindThenCommit = FALSE;
        pcontextMGR->dwChainDepth = 0;
    }
    DRM_PROFILING_LEAVE_SCOPE(L"DRM_MGR_Commit", g_pwszLeavingFunction);
    return dr;
}



DRM_RESULT DRM_API DRM_MGR_InitDecrypt(
    IN DRM_MANAGER_DECRYPT_CONTEXT  *f_pDecryptContext,
    IN DRM_BYTE                     *f_pbLast15,
    IN DRM_DWORD                     f_cbData )
{
    return DRM_BBX_InitDecrypt( 
            (DRM_CIPHER_CONTEXT*)f_pDecryptContext,
            f_pbLast15,
            f_cbData);
}



DRM_RESULT DRM_API DRM_MGR_Decrypt(
    IN DRM_MANAGER_DECRYPT_CONTEXT *pDecryptContext,
    IN OUT DRM_BYTE                *pbData,
    IN DRM_DWORD                    cbData )
{
    return DRM_BBX_Decrypt(cbData, pbData, (DRM_CIPHER_CONTEXT *) pDecryptContext);
}



DRM_RESULT DRM_API DRM_MGR_GetLicenseData(
    IN       DRM_MANAGER_CONTEXT    *pDrmContext,
    IN const DRM_CONST_STRING       *rgpdstrRights[], /* Array of DRM_CONST_STRING pointers */
    OUT      DRM_LICENSE_STATE_DATA  rgStateData[], /* array of DRM_LICENSE_STATE_DATAs */
    IN       DRM_DWORD               cRightsQueried )
{
    DRM_RESULT dr = DRM_SUCCESS;
    DRM_DWORD  i = 0;
    DRM_MANAGER_CONTEXT_INTERNAL *pContext = (DRM_MANAGER_CONTEXT_INTERNAL*) pDrmContext;

    DRM_PROFILING_ENTER_SCOPE(L"DRM_MGR_GetLicenseData", g_pwszEnteringFunction, DRM_PROFILING_START_IF_NOT_STARTED);
    
    ChkArg( rgpdstrRights != NULL
         && rgStateData   != NULL
         && cRightsQueried > 0 
         && pDrmContext   != NULL );

    if (pContext->fBindThenCommit)
    {
        pContext->fBindThenCommit = FALSE;
    }

    if (!pContext->fStoreOpened)
    {
        ChkDR (DRM_E_DRMNOTINIT);
    }
    if ( pContext->eHeaderInContext==eHeaderNotSet )
    {
        ChkDR(DRM_E_HEADER_NOT_SET);  /* no v1 or V2 header is setup */
    }
    ChkDR( _SetupLicEvalObjectToShare( pContext ) );

    /* Setup the stuff that GetProperties needs */
    MEMCPY(&pContext->oViewRightsContext.KID, &pContext->KID, SIZEOF(DRM_KID));

    pContext->oViewRightsContext.pBBContext       = &pContext->oBlackBoxContext;
    
    for (i = 0; i < DRM_MAX_LICENSE_CHAIN_DEPTH; i++)
    {
        pContext->oViewRightsContext.rgpLicQueryContext[i] =  &pContext->oLicEnum[i];
    }
    
    pContext->oViewRightsContext.pbGlobalSecStore = &pContext->oSecStoreGlobalContext;
    pContext->oViewRightsContext.pbLIDSecStore    = &pContext->rgcontextSST[0];
    pContext->oViewRightsContext.pbLicenseStoreXML= &pContext->oLicStoreContext;
    pContext->oViewRightsContext.pLicEval         = &(pContext->oLicEvalContext);
    pContext->oViewRightsContext.pbBuffer         =  pContext->rgbDRMHeaderData + __CB_DECL(pContext->cbHeaderData);
    pContext->oViewRightsContext.cbBuffer         =  SIZEOF( pContext->rgbDRMHeaderData ) - 
                                                     pContext->cbHeaderData + 
                                                     SIZEOF(pContext->rgbDRMLicense);

    ChkDR( DRM_ASD_GetLicenseAggregateData( rgpdstrRights, 
                                            rgStateData, 
                                            cRightsQueried, 
                                           &pContext->oViewRightsContext,
                                           &pContext->oHdsContext,
                                            FALSE,
                                            DRM_ASD_AGGREGATE_ROOT_LICENSES ) );
ErrorExit:
    _FreeDrmManagerInternalContexts( pContext );

    DRM_PROFILING_LEAVE_SCOPE(L"DRM_MGR_GetLicenseData", g_pwszLeavingFunction);

    return dr;
}



#if DRM_SUPPORT_METERING

DRM_RESULT DRM_API DRM_MGR_GenerateMeterChallenge(
    IN       DRM_MANAGER_CONTEXT *pcontextMGR,
    IN const DRM_CONST_STRING    *pdstrMeterCert,
       OUT   DRM_WCHAR           *pwszURL,
    IN OUT   DRM_DWORD           *pcchURL,
       OUT   _XMBContext         *pbChallenge,
    IN OUT   DRM_DWORD           *pcbChallenge)
{
    DRM_MANAGER_CONTEXT_INTERNAL *pcontext = (DRM_MANAGER_CONTEXT_INTERNAL *) pcontextMGR;
    DRM_RESULT                    dr       = DRM_SUCCESS;

    DRM_PROFILING_ENTER_SCOPE(L"DRM_MGR_GenerateMeterChallenge", g_pwszEnteringFunction, DRM_PROFILING_START_IF_NOT_STARTED);

    ChkArg (pcontextMGR    != NULL
         && pdstrMeterCert != NULL
         && pcbChallenge   != NULL);

    if (pcontext->fBindThenCommit)
    {
        pcontext->fBindThenCommit = FALSE;
    }

    if (!pcontext->fStoreOpened)
    {
        ChkDR (DRM_E_DRMNOTINIT);
    }

    if ( (pcontext->oBlackBoxContext.cachedCertValues.dwFeatureFlags & DRM_FEATURE_METERING) == 0)
    {
        ChkDR (DRM_E_METERING_NOT_SUPPORTED);
    }

    ChkDR( _SetupLicEvalObjectToShare( pcontext ) );

    ChkDR(DRM_MTR_GenerateMeterChallenge( &pcontext->oLicEvalContext,
                                           pcontext->rgbDRMLicense,
                                   SIZEOF( pcontext->rgbDRMLicense ),
                                           pdstrMeterCert,
                                           pbChallenge,
                                           pcbChallenge,
                                           pwszURL,
                                           pcchURL ) );
    
ErrorExit:

    DRM_PROFILING_LEAVE_SCOPE(L"DRM_MGR_GenerateMeterChallenge", g_pwszLeavingFunction);

    return dr;                                           
}

                    
DRM_RESULT DRM_API DRM_MGR_ProcessMeterResponse(
    IN   DRM_MANAGER_CONTEXT  *pcontextMGR,
    IN   DRM_BYTE             *pbData,
    IN   DRM_DWORD             cbData,
    OUT  DRM_DWORD            *pfFlagsOut)
{
    DRM_MANAGER_CONTEXT_INTERNAL *pcontext = (DRM_MANAGER_CONTEXT_INTERNAL *) pcontextMGR;
    DRM_RESULT                    dr       = DRM_SUCCESS;

    DRM_PROFILING_ENTER_SCOPE(L"DRM_MGR_ProcessMeterResponse", g_pwszEnteringFunction, DRM_PROFILING_START_IF_NOT_STARTED);
    
    ChkArg (pcontextMGR   != NULL
         && pbData        != NULL
         && pfFlagsOut    != NULL
         && cbData         > 0);
        
    if (pcontext->fBindThenCommit)
    {
        pcontext->fBindThenCommit = FALSE;
    }

    if(!pcontext->fStoreOpened)
    {
        ChkDR (DRM_E_DRMNOTINIT);
    }

    if ((pcontext->oBlackBoxContext.cachedCertValues.dwFeatureFlags & DRM_FEATURE_METERING) == 0)
    {
        ChkDR (DRM_E_METERING_NOT_SUPPORTED);
    }

    ChkDR( _SetupLicEvalObjectToShare( pcontext ) );
         
    dr = DRM_MTR_ProcessMeterResponse( &pcontext->oLicEvalContext,
                                        pcontext->rgbDRMLicense,
                                SIZEOF( pcontext->rgbDRMLicense ),
                                        pbData,
                                        cbData,
                                        pfFlagsOut);

ErrorExit:

    DRM_PROFILING_LEAVE_SCOPE(L"DRM_MGR_ProcessMeterResponse", g_pwszLeavingFunction);
    return dr;                                         
}
#endif /* DRM_SUPPORT_METERING */



#if DRM_SUPPORT_LICENSE_SYNC 

DRM_RESULT DRM_API DRM_MGR_GenerateSyncChallenge(
    IN     DRM_MANAGER_CONTEXT *pcontextMGR,
    IN     DRM_DWORD            cMaxCount,
    IN     DRM_DWORD            cMaxHours,
    IN     DRM_DWORD            f_iKIDStart,
    IN     DRM_DWORD            f_cKIDs,
       OUT DRM_DWORD           *f_piKIDNext,
       OUT DRM_DWORD           *f_pcKIDs,
       OUT _XMBContext         *pbData,
    IN OUT DRM_DWORD           *pcbData)
{
    DRM_MANAGER_CONTEXT_INTERNAL *pcontextManager    = (DRM_MANAGER_CONTEXT_INTERNAL *) pcontextMGR;
    DRM_VIEW_RIGHTS_CONTEXT      *pcontextViewRights = NULL;
    DRM_STACK_ALLOCATOR_CONTEXT   contextStack       = { 0 };
    DRM_HDS_CONTEXT *pcontextHDS  = NULL;
    DRM_RESULT       dr           = DRM_SUCCESS;
    DRM_DWORD        cbStack      = 0,
                     cbViewRights = 0;
    DRM_DWORD        i            = 0;

    DRM_PROFILING_ENTER_SCOPE(L"DRM_MGR_GenerateSyncChallenge", g_pwszEnteringFunction, DRM_PROFILING_START_IF_NOT_STARTED);
    
    ChkArg (pcontextMGR != NULL
         && pcbData     != NULL);

    if (pcontextManager->fBindThenCommit)
    {
        pcontextManager->fBindThenCommit = FALSE;
    }

    /* caller must do DRM_MGR_Initialize before DRM_MGR_GenerateSyncChallenge */

    if(!pcontextManager->fStoreOpened)
    {
        ChkDR (DRM_E_DRMNOTINIT);
    }
    
    pcontextHDS        = &pcontextManager->oHdsContext;
    pcontextViewRights = &pcontextManager->oViewRightsContext;

    /* neither view rights nor GenerateSyncChallenge need a 
    ** lot of stack; split this one between both  
    ** | rgbLicense .... |
    **   ^ stack
    **          ^ viewrights
    ** 
    */

    cbStack      = SIZEOF (DRM_SYNC_CONTEXT)
                 + SIZEOF (DRM_SYNC_ENUM_CONTEXT)
                 + 20;

    cbViewRights = SIZEOF (pcontextManager->rgbDRMLicense)
                 - cbStack;

    contextStack.pbStack = pcontextManager->rgbDRMLicense;
    contextStack.cbStack = cbStack;

    pcontextViewRights->pbBuffer =  contextStack.pbStack
                                 +  __CB_DECL(contextStack.cbStack);
    pcontextViewRights->cbBuffer =  cbViewRights;

    /* set up view rights context for license store query */

    pcontextViewRights->pBBContext       = &pcontextManager->oBlackBoxContext;
    
    for (i=0; i<DRM_MAX_LICENSE_CHAIN_DEPTH; i++)
    {
        pcontextViewRights->rgpLicQueryContext[i] = &pcontextManager->oLicEnum[i];
    }
    
    pcontextViewRights->pbGlobalSecStore = &pcontextManager->oSecStoreGlobalContext;
    pcontextViewRights->pbLIDSecStore    = &pcontextManager->rgcontextSST [0];
    pcontextViewRights->pbLicenseStoreXML= &pcontextManager->oLicStoreContext;
    pcontextViewRights->pLicEval         = &pcontextManager->oLicEvalContext;

    dr = DRM_SNC_GenerateSyncChallenge(
            pcontextViewRights,
            pcontextHDS,
           &contextStack,
            cMaxCount,
            cMaxHours,
            f_iKIDStart,
            f_cKIDs,
            f_piKIDNext,
            f_pcKIDs,
            pbData,
            pcbData);

ErrorExit:

    DRM_PROFILING_LEAVE_SCOPE(L"DRM_MGR_GenerateSyncChallenge", g_pwszLeavingFunction);
    return dr;                                           
}
#endif   /* DRM_SUPPORT_LICENSE_SYNC */



#if DRM_SUPPORT_SECURE_CLOCK

/*******************************************************************
 * Internal function to be called at DRM_MGR_Init for Secure
 * Clock.
 *******************************************************************/
 
static DRM_RESULT _CheckSecureClock( 
    DRM_MANAGER_CONTEXT_INTERNAL *pContext )
{
    DRM_RESULT dr = DRM_SUCCESS;
    DRM_SECSTORE_CONTEXT    *poSecureStoreContext = NULL;

    /* Result is first SHA_DIGEST_LEN bytes of rgbDRMLicense */
    ChkDR(DRM_SST_CreateGlobalStorePassword(
            pContext->rgbDRMLicense, 
            (DRM_BYTE *)&pContext->oBlackBoxContext)); 

    /* As only SHA_DIGEST_LEN bytes are getting used for password, 
      other bytes can be used for DRM_SECSTORE_CONTEXT */
    poSecureStoreContext = ( DRM_SECSTORE_CONTEXT * )(pContext->rgbDRMLicense + __CB_DECL(SHA_DIGEST_LEN) );

    ChkDR( DRM_CLK_InitCheckSecureClock( 
            pContext->rgbDRMLicense,
            &( pContext->oBlackBoxContext.CryptoContext ),
            &( pContext->oHdsContext ),
            &( pContext->fClockSet ),
            poSecureStoreContext,
            &( pContext->oLicEvalContext ) ) );


ErrorExit:
    return dr;
}


DRM_RESULT DRM_API DRM_MGR_ClkProcessResponse(
    IN  DRM_MANAGER_CONTEXT *pDrmContext,
    IN  DRM_BYTE            *pbResponse,
    IN  DRM_DWORD            cbResponse,
    OUT DRM_RESULT          *pResult)
{
    DRM_RESULT dr = DRM_SUCCESS;
    DRM_MANAGER_CONTEXT_INTERNAL *pContext = (DRM_MANAGER_CONTEXT_INTERNAL*) pDrmContext;
    DRM_SECSTORE_CONTEXT    *poSecureStoreContext = NULL;

    DRM_PROFILING_ENTER_SCOPE(L"DRM_MGR_ClkProcessResponse", g_pwszEnteringFunction, DRM_PROFILING_START_IF_NOT_STARTED);

    ChkArg( pDrmContext != NULL
         && pbResponse  != NULL
         && cbResponse  != 0
         && pResult     != NULL );
    
    if (pContext->fBindThenCommit)
    {
        pContext->fBindThenCommit = FALSE;
    }

    if( (pContext->oBlackBoxContext.cachedCertValues.dwFeatureFlags & DRM_FEATURE_SECURE_CLOCK) == 0 )
    {
        ChkDR( DRM_E_CLK_NOT_SUPPORTED );
    }

    /* Result is first SHA_DIGEST_LEN bytes of rgbDRMLicense */
    ChkDR(DRM_SST_CreateGlobalStorePassword(pContext->rgbDRMLicense, 
                                           (DRM_BYTE *) &pContext->oBlackBoxContext)); 

   /* As only SHA_DIGEST_LEN bytes are getting used for password, 
      other bytes can be used for DRM_SECSTORE_CONTEXT */
    poSecureStoreContext = ( DRM_SECSTORE_CONTEXT * )(pContext->rgbDRMLicense + __CB_DECL(SHA_DIGEST_LEN) );

    ChkDR(_SetupLicEvalObjectToShare( pContext ));

    /* Explicitly make the global secure store writeabele */
    pContext->oLicEvalContext.fGlobalSecStoreWritable = TRUE;

    /* Call DRM_CLK_ProcessResponse */
    ChkDR( DRM_CLK_ProcessResponse(
           &pContext->oBlackBoxContext.cachedCertValues.pubkeySecureClockServer,
            pbResponse, 
            cbResponse, 
            pContext->rgbDRMLicense,  
            pResult,
           &pContext->oBlackBoxContext.CryptoContext,
           &pContext->oHdsContext,
            poSecureStoreContext,
           &pContext->oLicEvalContext) );

    /*After successful processing response, Set DRM Manager Clock flag*/
    pContext->fClockSet = TRUE;
    
ErrorExit:

    DRM_PROFILING_LEAVE_SCOPE(L"DRM_MGR_ClkProcessResponse", g_pwszEnteringFunction);

    _FreeDrmManagerInternalContexts( pContext );
    return dr;
}


DRM_RESULT DRM_API DRM_MGR_ClkGenerateChallenge(
    IN     DRM_MANAGER_CONTEXT  *pDrmContext,
       OUT DRM_WCHAR            *pwszURL,
    IN OUT DRM_DWORD            *pcchURL,
       OUT DRM_BYTE             *pbChallenge,
    IN OUT DRM_DWORD            *pcbChallenge )
{
    DRM_RESULT dr = DRM_SUCCESS;
    DRM_MANAGER_CONTEXT_INTERNAL *pContext = (DRM_MANAGER_CONTEXT_INTERNAL*) pDrmContext;    
    DRM_CONST_STRING dstrURL        = EMPTY_DRM_STRING;
    DRM_CONST_STRING dstrDeviceCert = EMPTY_DRM_STRING;

    DRM_PROFILING_ENTER_SCOPE(L"DRM_MGR_ClkGenerateChallenge", g_pwszEnteringFunction, DRM_PROFILING_START_IF_NOT_STARTED);

    ChkArg( pDrmContext  != NULL
         && pcbChallenge != NULL );
    
    if (pContext->fBindThenCommit)
    {
        pContext->fBindThenCommit = FALSE;
    }

    if( (pContext->oBlackBoxContext.cachedCertValues.dwFeatureFlags & DRM_FEATURE_SECURE_CLOCK) == 0 )
    {
        ChkDR( DRM_E_CLK_NOT_SUPPORTED );
    }

    DSTR_FROM_PB( &dstrDeviceCert, pContext->rgbDRMLicense, SIZEOF( pContext->rgbDRMLicense ) );
    
    ChkDR( DRM_DDC_GetDeviceCertificate( 
            (DRM_STRING*)&dstrDeviceCert, 
            0,
            &pContext->oBlackBoxContext.CryptoContext) );
    
    ChkDR( DRM_DCP_GetAttribute( 
            &dstrDeviceCert,
            DRM_DEVCERT_SECURECLOCKURL,
            NULL,
            &dstrURL) );
    
    /* Result is first SHA_DIGEST_LEN bytes of oSecStoreGlobalContext */
    ChkDR(DRM_SST_CreateGlobalStorePassword(
            (DRM_BYTE*)&pContext->oSecStoreGlobalContext, 
            (DRM_BYTE *)&pContext->oBlackBoxContext)); 

    dr = DRM_CLK_CreateChallenge(
            &dstrURL, 
 (DRM_BYTE*)&pContext->oSecStoreGlobalContext,
            &pContext->oBlackBoxContext.CryptoContext,
            &pContext->oHdsContext,
            &pContext->rgcontextSST[0],
             pwszURL,
             pcchURL,
             pbChallenge,
             pcbChallenge);

ErrorExit:

    DRM_PROFILING_LEAVE_SCOPE(L"DRM_MGR_ClkGenerateChallenge", g_pwszLeavingFunction);

    return dr;
}

DRM_RESULT DRM_API DRM_MGR_GetSecureClock(
    IN     DRM_MANAGER_CONTEXT  *pDrmContext,
    IN     DRM_WCHAR            *pwszSecureTime,
    IN OUT DRM_DWORD            *pcchSecureTime, 
    IN     DRM_DWORD            *pdwFlags,
    IN     DRM_BYTE             *pbSecureTimeWithStatus,
    IN OUT  DRM_DWORD           *pcbSecureTimeWithStatus  )
{
    DRM_RESULT dr = DRM_SUCCESS;
    DRM_MANAGER_CONTEXT_INTERNAL *pContext = (DRM_MANAGER_CONTEXT_INTERNAL*) pDrmContext;
    DRM_SECSTORE_CONTEXT    *poSecureStoreContext = NULL;

    DRM_PROFILING_ENTER_SCOPE(L"DRM_MGR_GetSecureClock", g_pwszEnteringFunction, DRM_PROFILING_START_IF_NOT_STARTED);
    
    ChkArg( pDrmContext != NULL );

    if (pContext->fBindThenCommit)
    {
        pContext->fBindThenCommit = FALSE;
    }

    if( (pContext->oBlackBoxContext.cachedCertValues.dwFeatureFlags & DRM_FEATURE_SECURE_CLOCK) == 0 )
    {
        ChkDR( DRM_E_CLK_NOT_SUPPORTED );
    }

    /* Result is first SHA_DIGEST_LEN bytes of rgbDRMLicense */
    ChkDR(DRM_SST_CreateGlobalStorePassword(
            pContext->rgbDRMLicense, 
            (DRM_BYTE *) &pContext->oBlackBoxContext)); 

   /* As only SHA_DIGEST_LEN bytes are getting used for password, 
      other bytes can be used for DRM_SECSTORE_CONTEXT */
    poSecureStoreContext = ( DRM_SECSTORE_CONTEXT * )(pContext->rgbDRMLicense + __CB_DECL(SHA_DIGEST_LEN) );

    /* Call DRM_CLK_GetSecureClock */

    dr = DRM_CLK_GetSecureClock(  
            pwszSecureTime,
            pcchSecureTime, 
            pdwFlags,
            pbSecureTimeWithStatus,
            pcbSecureTimeWithStatus,
            pContext->rgbDRMLicense,  
            &( pContext->oBlackBoxContext.CryptoContext ),
            &( pContext->oHdsContext ),
            poSecureStoreContext);
    

ErrorExit:

    DRM_PROFILING_LEAVE_SCOPE(L"DRM_MGR_GetSecureClock", g_pwszLeavingFunction);
    
    return dr;
}
#endif    /* DRM_SUPPORT_SECURE_CLOCK */



#if DRM_SUPPORT_ENCRYPTION

DRM_RESULT DRM_API DRM_MGR_CreateEncryptContext(
    IN const DRM_BYTE                     rgbKey[__CB_DECL(DRMCIPHERKEYLEN)],
    OUT      DRM_MANAGER_ENCRYPT_CONTEXT *pEncryptContext )
{
    return DRM_CPHR_Init((DRM_CIPHER_CONTEXT*) pEncryptContext, DRMCIPHERKEYLEN, rgbKey );
}

DRM_RESULT DRM_API DRM_MGR_Encrypt(
    IN     DRM_MANAGER_ENCRYPT_CONTEXT *pEncryptContext,
    IN OUT DRM_BYTE                    *pbData,
    IN     DRM_DWORD                    cbData )
{
    return DRM_CPHR_Encrypt( (DRM_CIPHER_CONTEXT*) pEncryptContext, cbData, pbData);
}

#endif  /* DRM_SUPPORT_ENCRYPTION */



DRM_RESULT DRM_API DRM_MGR_CleanupLicenseStore(
    IN       DRM_MANAGER_CONTEXT     *pDrmContext,
    IN const DRM_VOID                *pvCallerData,
    IN       DRM_DWORD                dwCallbackInterval,
    IN       pfnStoreCleanupProgress  pfnCallback )
{
    DRM_RESULT                    dr       =  DRM_SUCCESS;
    DRM_MANAGER_CONTEXT_INTERNAL *pContext = (DRM_MANAGER_CONTEXT_INTERNAL *) pDrmContext;
    DRM_PROFILING_ENTER_SCOPE(L"DRM_MGR_CleanupLicenseStore", g_pwszEnteringFunction, DRM_PROFILING_START_IF_NOT_STARTED);
    
    ChkArg ( pDrmContext != NULL );

    if (pContext->fBindThenCommit)
    {
        pContext->fBindThenCommit = FALSE;
    }

    ChkDR( _SetupLicEvalObjectToShare( pContext ) ); 
    
    dr = DRM_LST_Clean ( 
            (DRM_BYTE *)&pContext->oLicEvalContext,
            (DRM_BYTE *)&pContext->oLicStoreContext,
            pContext->rgbDRMLicense,
            DRM_MAX_LICENSESIZE,
            pvCallerData,
            dwCallbackInterval,
            pfnCallback,
            &( pContext->oHdsContext ) );
    
ErrorExit:
    DRM_PROFILING_LEAVE_SCOPE(L"DRM_MGR_CleanupLicenseStore", g_pwszLeavingFunction);
    _FreeDrmManagerInternalContexts( pContext );
    return dr;                                           
}


DRM_RESULT DRM_API DRM_MGR_GetLicenseState(
    IN       DRM_MANAGER_CONTEXT            *pDrmContext,
    IN  const DRM_CONST_STRING              *pdstrQuery,
    OUT      DRM_LICENSE_STATE_CATEGORY     *pCategory,
    OUT      DRM_DWORD                      *pdwReserved1,
    OUT      DRM_DWORD                      *pdwReserved2,
    OUT      DRM_DWORD                      *pdwReserved3)
{
    DRM_RESULT          dr              =  DRM_SUCCESS;
    DRM_LONG            lCanBind        = 0;
    DRM_CONST_STRING    dstrRoot        = EMPTY_DRM_STRING;
    DRM_CONST_STRING    dstrData        = EMPTY_DRM_STRING;
    DRM_CONST_STRING    dstrValue       = EMPTY_DRM_STRING;
    DRM_CONST_STRING   *pdstrRights [1];
    DRM_MANAGER_CONTEXT_INTERNAL *pContext   = (DRM_MANAGER_CONTEXT_INTERNAL *) pDrmContext;
    DRM_PROFILING_ENTER_SCOPE(L"DRM_MGR_GetLicenseState", g_pwszEnteringFunction, DRM_PROFILING_START_IF_NOT_STARTED);
  
    ChkArg ( pDrmContext != NULL 
        &&   pCategory   != NULL);

    ChkDRMString( pdstrQuery );

    /*Init the out parameter*/

    if(pdwReserved1)
    {
        *pdwReserved1 = 0;
    }
    if(pdwReserved2)
    {
        *pdwReserved2 = 0;
    }
    if(pdwReserved3)
    {
        *pdwReserved3 = 0;
    }


    *pCategory = WM_DRM_LICENSE_STATE_NORIGHT;

/*
<LICENSESTATE type = “challenge”>
    <DATA>
        <ACTION>Play</ACTION> 
        <KID>KID directly from license</KID>
        <CANBIND>1</CANBIND> 
    </DATA>
</LICENSESTATE >
*/
    
    ChkDR( DRM_XML_GetNode( pdstrQuery, &g_dstrTagLicenseStateRequestRoot,  NULL, NULL, 0, NULL, &dstrRoot    ) );
    ChkDR( DRM_XML_GetNode( &dstrRoot,  &g_dstrTagData,                    NULL, NULL, 0, NULL, &dstrData    ) );
    ChkDR( DRM_XML_GetNode( &dstrData,  &g_dstrTagAction,                  NULL, NULL, 0, NULL, &dstrValue   ) );

    if (!DRM_UTL_DSTRStringsEqual( &dstrValue, &g_dstrWMDRM_RIGHT_PLAYBACK ))
    {
        ChkDR(DRM_E_INVALIDRIGHT);
    }
    ChkDR( DRM_XML_GetNode( &dstrData,  &g_dstrTagCanBind,                 NULL, NULL, 0, NULL, &dstrValue   ) );

    ChkDR( wcsntol( dstrValue.pwszString, dstrValue.cchString, &lCanBind ) );
    if (lCanBind != 1)
    {
        ChkDR(DRM_E_INVALIDARG);
    }

    ChkDR( DRM_XML_GetNode( &dstrData,  &g_dstrTagKID,                     NULL, NULL, 0, NULL, &dstrValue   ) );

    if ( CB_DSTR(&dstrValue) >  DRM_MAX_HEADERDATASIZE 
      || CB_DSTR(&dstrValue) == 0)
    {
        ChkDR(DRM_E_INVALIDARG);
    }
    MEMCPY(pContext->rgbDRMHeaderData, PB_DSTR(&dstrValue), CB_DSTR(&dstrValue));
    pContext->cbHeaderData     = CB_DSTR(&dstrValue);
    pContext->eHeaderInContext = eKidOnly;

    pdstrRights [0] = (DRM_CONST_STRING *) &g_dstrWMDRM_RIGHT_PLAYBACK;

    dr = DRM_MGR_Bind( pDrmContext, 
                       pdstrRights,
                       NO_OF(pdstrRights),
                       NULL,
                       NULL,
                       NULL);

    if( DRM_FAILED( dr ) )
    {
        if ( dr == DRM_E_CLK_NOT_SET )
        {
            *pCategory = WM_DRM_LICENSE_STATE_UNLIM;
        }
        dr = DRM_SUCCESS;
        goto ErrorExit;
    }
   *pCategory = WM_DRM_LICENSE_STATE_UNLIM;
    
ErrorExit:
    DRM_PROFILING_LEAVE_SCOPE(L"DRM_MGR_GetLicenseState", g_pwszLeavingFunction);
    return dr;                                           

}

static DRM_RESULT _DeleteLicenses(
    IN       DRM_MANAGER_CONTEXT_INTERNAL *f_pContext,
    IN       DRM_CONST_STRING             *f_pdstrLGPubKey,
    IN       DRM_KID                      *f_pKID,
        OUT  DRM_DWORD                    *f_pcLicDeleted)
{
    DRM_RESULT          dr           = DRM_SUCCESS;
    DRM_DWORD           cbLicSize    = 0;
    DRM_CONST_STRING    dstrLicense  = EMPTY_DRM_STRING;
    DRM_CONST_STRING    dstrLGPubkey = EMPTY_DRM_STRING;

    ChkArg( f_pContext       != NULL
         && f_pdstrLGPubKey  != NULL
         && f_pKID           != NULL
         && f_pcLicDeleted   != NULL);

    *f_pcLicDeleted = 0;

    dr = DRM_LST_InitEnum(&f_pContext->oLicStoreContext, f_pKID, FALSE,  &f_pContext->oLicEnum[0]);

    if (DRM_SUCCEEDED(dr))
    {
        while( TRUE )
        {   
            DRM_HDS_SLOT_HINT slotHint;

            dr = DRM_LST_EnumNext( &f_pContext->oLicEnum[0],
                                    NULL, 
                                   &f_pContext->oFFLicense.rglid [0], 
                                   &slotHint,
                                   &cbLicSize); 

            if (dr == DRM_E_NOMORE)
            {
                dr = DRM_SUCCESS;
                goto ErrorExit;
            }
            ChkDR( dr );

            if (cbLicSize > SIZEOF(f_pContext->rgbDRMLicense))
            {
                /*Can't read the license. Just continue...*/
                continue;
            }

            ChkDRContinue( DRM_LST_GetLicense( &f_pContext->oLicStoreContext,
                                                f_pKID,
                                               &f_pContext->oFFLicense.rglid [0],
                                               &slotHint, 
                                                f_pContext->rgbDRMLicense,
                                               &cbLicSize) );

            DSTR_FROM_PB(&dstrLicense, f_pContext->rgbDRMLicense, cbLicSize);

            ChkDRContinue( DRM_LIC_GetAttribute( &dstrLicense, 
                                                 &g_dstrTagRevocationPubKey, 
                                                  DRM_LICENSE_ATTRIB_META_ATTRIBUTE, 
                                                  NULL, 
                                                 &dstrLGPubkey, 
                                                  0 ) );

            if (DRM_UTL_DSTRStringsEqual( &dstrLGPubkey, f_pdstrLGPubKey) )
            {
                dr = DRM_LST_EnumDelete(&f_pContext->oLicEnum[0]);
                if (DRM_SUCCEEDED(dr))
                {
                    (*f_pcLicDeleted)++;  
                }

#if DRM_SUPPORT_LICENSE_SYNC
                (void) DRM_SNC_DeleteKID( &f_pContext->contextSync, f_pKID);
#endif
            }
        }
    }

ErrorExit:
    return dr;                                           
}


/*
<RESPONSE type="dlrb"> 
<DATA>
    <VERSION value="verionval"/>
    <LGPUBKEY value="lgapubkeyval"/>
    <KID value="kid1"/>
    <KID value="kid2"/>
    <!-- ...-->
</DATA>
<SIGNATURE>
    <HASHALGORITH type="SHA"/>
    <SIGNALGORITHM type="MSDRM"/>
    <VALUE>...</VALUE>
</SIGNATURE>
</RESPONSE>
*/


static DRM_RESULT _ProcessLicenseRevocationList(
    IN       DRM_MANAGER_CONTEXT_INTERNAL *f_pContext,
    IN       DRM_DWORD                     f_iKids,
    IN       DRM_DWORD                     f_cKids,
    IN       DRM_BYTE                     *f_pbList,
    IN       DRM_DWORD                     f_cbList,
        OUT  DRM_DWORD                    *f_piKids,
        OUT  DRM_DWORD                    *f_pcLicDeleted)
{
    DRM_DWORD           iKids       = 0;
    DRM_DWORD           cbBuffer    = 0;
    DRM_RESULT          dr          = DRM_SUCCESS;
    DRM_CONST_STRING    dstrRoot    = EMPTY_DRM_STRING;
    DRM_CONST_STRING    dstrData    = EMPTY_DRM_STRING;
    DRM_CONST_STRING    dstrPubKey  = EMPTY_DRM_STRING;
    DRM_CONST_STRING    dstrSign    = EMPTY_DRM_STRING;
    DRM_CONST_STRING    dstrBlob    = EMPTY_DRM_STRING;
    DRM_CONST_STRING    dstrValue   = EMPTY_DRM_STRING;
    

    ChkArg( f_pContext     != NULL
         && f_pbList       != NULL
         && f_piKids       != NULL
         && f_pcLicDeleted != NULL
         && f_cbList       > 0);

    *f_piKids       = 0;
    *f_pcLicDeleted = 0;

    if(f_cKids == 0)
    {
        goto ErrorExit;
    }

    DSTR_FROM_PB(&dstrBlob, f_pbList, f_cbList);

    ChkDR( DRM_XML_GetNode( &dstrBlob, &g_dstrTagDeviceLicenseRevocationRoot,  NULL, NULL, 0, NULL,       &dstrRoot    ) );
    ChkDR( DRM_XML_GetNode( &dstrRoot, &g_dstrTagData,                         NULL, NULL, 0, NULL,       &dstrData    ) );
    ChkDR( DRM_XML_GetNode( &dstrData, &g_dstrTagRevocationPubKey,             NULL, NULL, 0, &dstrValue, NULL         ) );

    ChkDR( DRM_XML_GetNodeAttribute( &dstrValue, &g_dstrLabelValue, &dstrPubKey ) );


    /*Check the signature first*/
    
    ChkDR( DRM_XML_GetNode( &dstrRoot, &g_dstrTagSignature, NULL, NULL, 0, NULL, &dstrSign  ) );
    ChkDR( DRM_XML_GetNode( &dstrSign, &g_dstrTagValue,     NULL, NULL, 0, NULL, &dstrValue ) );

    /*Base64 decode Pubkey and this signature*/
    cbBuffer = SIZEOF(PUBKEY);
    ChkDR(DRM_B64_DecodeW( &dstrPubKey, &cbBuffer, (DRM_BYTE *)&f_pContext->oBlackBoxContext.CryptoContext.pubKey, 0) );

    cbBuffer = SIZEOF(f_pContext->oBlackBoxContext.CryptoContext.signature);
    ChkDR(DRM_B64_DecodeW( &dstrValue, &cbBuffer, f_pContext->oBlackBoxContext.CryptoContext.signature, 0) );

    ChkDR( DRM_XML_GetNode( &dstrRoot, &g_dstrTagData, NULL, NULL, 0, &dstrValue, NULL ) );
    
    
    if (! DRM_PK_Verify( f_pContext->oBlackBoxContext.CryptoContext.rgbCryptoContext, 
                        &f_pContext->oBlackBoxContext.CryptoContext.pubKey, 
                         PB_DSTR(&dstrValue), 
                         CB_DSTR(&dstrValue), 
                         f_pContext->oBlackBoxContext.CryptoContext.signature))
    {
        ChkDR(DRM_E_INVALID_SIGNATURE);
    }

    f_pContext->fLicStoreOpen = FALSE;
    ChkDR( DRM_LST_Open( &f_pContext->oLicStoreContext, &f_pContext->oHdsContext ) ); 
    f_pContext->fLicStoreOpen = TRUE;


    for (iKids = f_iKids; iKids < f_iKids + f_cKids; iKids++, (*f_piKids)++)
    {
       DRM_DWORD  nDeleted = 0;
        
        /*reuse dstrBlob*/

        dr = DRM_XML_GetNode( &dstrData, &g_dstrTagKID, NULL, NULL, iKids, &dstrBlob, NULL );
        
        if ( DRM_SUCCEEDED( dr ) )
        {

             /*Call function to delete licenses*/
            ChkDRContinue( DRM_XML_GetNodeAttribute( &dstrBlob, &g_dstrLabelValue, &dstrValue ) );

            ChkDRContinue( DRM_UTL_DecodeKID( &dstrValue, &f_pContext->KID ) );

            (void)_DeleteLicenses( f_pContext, 
                                  &dstrPubKey,
                                  &f_pContext->KID,
                                  &nDeleted);

            *f_pcLicDeleted += nDeleted;
        }
        else
        {
            if (dr == DRM_E_XMLNOTFOUND)
            {
                /* no more KIDS found */
                dr = DRM_SUCCESS;
                break;
            }
            ChkDR(dr);  /* other errors */
        }

    }


ErrorExit:

    if (f_pContext->fLicStoreOpen)
    {
        (void) DRM_LST_Close( &f_pContext->oLicStoreContext );
    }
   
    return dr;                                           
}


/*****************************************************************************
** Function:    DRM_MGR_GetRevocationList
**
** Synopsis:    open the secure store and get the version number and buffer containing the 
**              revocation list currently stored based upon the type of revocation list
**              specified
**
** Arguments:   [f_pDrmContext]        -- Opaque DRM Manager context initialized by a call to 
**                                        DRM_MGR_Initialize.
**              [f_eRevType]           -- Type of Revocation List and Version to retrieve.
**              [f_pbBuffer]           --
**              [f_pcbBuffer]          -- size of f_pbBuffer, in bytes, returns size of  
**                                        revocation list
**              [f_pdwVersion]         -- Returns version of revocation list.
**
** Returns:     DRM_SUCCESS on success or
**              DRM_E_INVALIDARG if any parameter is invalid or
**              DRM_E_PRIVKEYREADERROR if BB context not initialized or
**              DRM_E_FILENOTFOUND if revocation list doesn't exist in secure store or
**              DRM_E_BUFFERTOOSMALL if *f_pcbBuffer smaller than required buffer size to return 
**                                     revocation list or
**              any return code as defined in drmresults.h
*****************************************************************************/
DRM_RESULT DRM_API DRM_MGR_GetRevocationList(
    IN        DRM_MANAGER_CONTEXT        *f_pDrmContext,
    IN        DRM_REVOCATION_TYPE_ENUM    f_eRevType,
    OUT       DRM_BYTE                   *f_pbBuffer,
    IN OUT    DRM_DWORD                  *f_pcbBuffer,
    OUT       DRM_DWORD                  *f_pdwVersion)
{
    DRM_RESULT dr = DRM_SUCCESS;
    DRM_MANAGER_CONTEXT_INTERNAL *pContext = (DRM_MANAGER_CONTEXT_INTERNAL*) f_pDrmContext;
    DRM_BYTE   rgbPassword [__CB_DECL(SHA_DIGEST_LEN)] = { 0x00 };  

	/*Unreferenced*/
    /*DRM_SECSTORE_CONTEXT oSecStoreContext;*/
    DRM_BB_CONTEXT *poBlackBoxContext = NULL;
    DRM_SECSTORE_CONTEXT *poSecStoreContext = NULL;
    DRM_DWORD cbRevocationBuffer = 0;
   
    ChkArg( f_pDrmContext && f_pdwVersion );

    poBlackBoxContext = &pContext->oBlackBoxContext;
    poSecStoreContext = &pContext->oSecStoreGlobalContext;

#if DRM_SUPPORT_REVOCATION
    ChkDR( DRM_SST_CreateGlobalStorePassword( rgbPassword, (DRM_BYTE*)poBlackBoxContext ) );

    ChkDR( DRM_SST_OpenKeyTokens(  poSecStoreContext, 
                        (DRM_ID *)&g_rgbSecStoreGlobalName, 
                                   NULL,
                                   rgbPassword, 
                                   DRM_SECURE_STORE_CREATE_IF_NOT_EXISTS,
                                   SECURE_STORE_GLOBAL_DATA, 
                                  &pContext->oHdsContext ) );

    ChkDR( _CreateRevocationStorePassword( poBlackBoxContext, rgbPassword ) );

    cbRevocationBuffer = pContext->cbRevocationBuffer;
    switch(f_eRevType)
    {
    case WM_DRM_REVOCATION_TYPE_APP:
        {
#if DRM_SUPPORT_APP_REVOCATION
            if( f_pcbBuffer == NULL )
            {
                /* The caller is likely only interested in the version */
                dr = DRM_RVK_GetCurrentAppRevocationList(
                    poSecStoreContext,
                    rgbPassword,
                    pContext->pbRevocationBuffer,
                    &cbRevocationBuffer,
                    &pContext->oHdsContext,
                    f_pdwVersion );
                if( dr == DRM_E_BUFFERTOOSMALL )
                {
                    dr = DRM_E_REVOCATION_BUFFERTOOSMALL;
                }
                ChkDR( dr );
            }
            else
            {
                ChkDR( DRM_RVK_GetCurrentAppRevocationList(
                    poSecStoreContext,
                    rgbPassword,
                    f_pbBuffer,
                    f_pcbBuffer,
                    &pContext->oHdsContext,
                    f_pdwVersion ) );
            }
#else
            ChkDR( DRM_E_NOTIMPL );
#endif /* DRM_SUPPORT_APP_REVOCATION */
        }
        break;
    case WM_DRM_REVOCATION_TYPE_WMDRMPD:
        {
#if DRM_SUPPORT_DEVICE_REVOCATION
            if( f_pcbBuffer == NULL )
            {
                /* The caller is likely only interested in the version */
                dr = DRM_RVK_GetDeviceRevocationList(
                    &poBlackBoxContext->CryptoContext,
                    poSecStoreContext,
                    &pContext->oHdsContext,
                    rgbPassword,
                    pContext->pbRevocationBuffer,
                    &cbRevocationBuffer,
                    f_pdwVersion );
                if( dr == DRM_E_BUFFERTOOSMALL )
                {
                    dr = DRM_E_REVOCATION_BUFFERTOOSMALL;
                }
                ChkDR( dr );
            }
            else
            {
                ChkDR( DRM_RVK_GetDeviceRevocationList(
                    &poBlackBoxContext->CryptoContext,
                    poSecStoreContext,
                    &pContext->oHdsContext,
                    rgbPassword,
                    f_pbBuffer,
                    f_pcbBuffer,
                    f_pdwVersion ) );
            }
#else
            ChkDR( DRM_E_NOTIMPL );
#endif  /* DRM_SUPPORT_DEVICE_REVOCATION */
        }
        break;
    case WM_DRM_REVOCATION_TYPE_WMDRMND:
        {
#if DRM_SUPPORT_WMDRMNET
            if( f_pcbBuffer == NULL )
            {
                dr = DRM_RVK_GetWMDRMNETList(
                    &poBlackBoxContext->CryptoContext,
                    poSecStoreContext,
                    &pContext->oHdsContext,
                    rgbPassword,
                    pContext->pbRevocationBuffer,
                    &cbRevocationBuffer,
                    f_pdwVersion );
                if( dr == DRM_E_BUFFERTOOSMALL )
                {
                    dr = DRM_E_REVOCATION_BUFFERTOOSMALL;
                }
                ChkDR( dr );
            }
            else
            {
                ChkDR( DRM_RVK_GetWMDRMNETList(
                    &poBlackBoxContext->CryptoContext,
                    poSecStoreContext,
                    &pContext->oHdsContext,
                    rgbPassword,
                    f_pbBuffer,
                    f_pcbBuffer,
                    f_pdwVersion ) );
            }
#else
            ChkDR( DRM_E_NOTIMPL );
#endif /* DRM_SUPPORT_WMDRMNET */
        }
        break;
    default:
        ChkDR(DRM_E_INVALIDARG);
        break;
    }

#else
    ChkDR( DRM_E_NOTIMPL );
#endif /* DRM_SUPPORT_REVOCATION */


ErrorExit:
    poSecStoreContext->fInited = TRUE;
    DRM_SST_CloseKey( poSecStoreContext, &pContext->oHdsContext );
    return dr;
}

/*****************************************************************************
** Function:    DRM_MGR_GetRevocationListVersion
**
** Synopsis:    open the secure store and get the version number for the 
**              revocation list currently stored based upon the type of revocation list
**              specified
**
** Arguments:   [f_pDrmContext]        -- Opaque DRM Manager context initialized by a call to 
**                                        DRM_MGR_Initialize.
**              [f_eRevType]           -- Type of Revocation List and Version to retrieve.
**              [f_pdwVersion]         -- Returns version of revocation list.
**
** Returns:     DRM_SUCCESS on success or
**              DRM_E_INVALIDARG if any parameter is invalid or
**              DRM_E_PRIVKEYREADERROR if BB context not initialized or
**              DRM_E_FILENOTFOUND if revocation list doesn't exist in secure store or
**              any return code as defined in drmresults.h
*****************************************************************************/
DRM_RESULT DRM_API DRM_MGR_GetRevocationListVersion(
    IN        DRM_MANAGER_CONTEXT        *f_pDrmContext,
    IN        DRM_REVOCATION_TYPE_ENUM    f_eRevType,
    OUT       DRM_DWORD                  *f_pdwVersion)
{
    DRM_RESULT dr = DRM_SUCCESS;
    DRM_MANAGER_CONTEXT_INTERNAL *pContext = (DRM_MANAGER_CONTEXT_INTERNAL*)f_pDrmContext;
    
    ChkArg( f_pDrmContext && f_pdwVersion );

    ChkDR( DRM_MGR_GetRevocationList( f_pDrmContext, 
                            f_eRevType, 
                            NULL, 
                            NULL, 
                            f_pdwVersion ) );

ErrorExit:

    return dr;
}


#if DRM_SUPPORT_REVOCATION
/*****************************************************************************
** Function:    DRM_MGR_GetRevInfo
**
** Synopsis:    open the secure store and get the RevInfo structure
**
** Arguments:   [f_pDrmContext]        -- Opaque DRM Manager context initialized by a call to 
**                                        DRM_MGR_Initialize.
**              [f_eRevType]           -- Type of Revocation List and Version to retrieve.
**              [f_pdwVersion]         -- Returns version of revocation list.
**
** Returns:     DRM_SUCCESS on success or
**              DRM_E_INVALIDARG if any parameter is invalid or
**              DRM_E_PRIVKEYREADERROR if BB context not initialized or
**              DRM_E_FILENOTFOUND if revocation list doesn't exist in secure store or
**              any return code as defined in drmresults.h
*****************************************************************************/
DRM_RESULT DRM_API DRM_MGR_GetRevInfo(
    IN     DRM_MANAGER_CONTEXT *f_pDrmContext,
       OUT DRM_BYTE            *f_pbRevInfo,
    IN OUT DRM_DWORD           *f_pcbRevInfo)
{
    DRM_RESULT dr = DRM_SUCCESS;
    DRM_RLVI   RLVI;
    DRM_DWORD  cbRevocationBuffer = 0;
    DRM_MANAGER_CONTEXT_INTERNAL *pContext = (DRM_MANAGER_CONTEXT_INTERNAL *) f_pDrmContext;

    MEMSET( &RLVI, 0, SIZEOF( DRM_RLVI ) );

    ChkArg( f_pDrmContext != NULL
            && f_pcbRevInfo != NULL );

    cbRevocationBuffer = pContext->cbRevocationBuffer;
    dr = DRM_RVK_GetCurrentRevocationInfo(&pContext->oSecStoreGlobalContext,
                                            &pContext->oBlackBoxContext,
                                            &pContext->oHdsContext,
                                            pContext->pbRevocationBuffer,
                                            &cbRevocationBuffer,
                                            &RLVI);
    if( dr == DRM_E_BUFFERTOOSMALL )
    {
        dr = DRM_E_REVOCATION_BUFFERTOOSMALL;
    }
    ChkDR( dr );

    if( cbRevocationBuffer > 0 )
    {
        /* Base64 encode expects the number of unicode characters in the buffer, not bytes */
        *f_pcbRevInfo /= SIZEOF( DRM_WCHAR );
        
        dr = DRM_B64_EncodeW( pContext->pbRevocationBuffer,
                            cbRevocationBuffer,
                (DRM_WCHAR*)f_pbRevInfo,
                            f_pcbRevInfo,
                            0 );
        
        *f_pcbRevInfo *= SIZEOF( DRM_WCHAR );
        ChkDR( dr );
    }
    else
    {
        *f_pcbRevInfo = 0;
    }


ErrorExit:

    return dr;
}
#endif /* DRM_SUPPORT_REVOCATION */



/**********************************************************************
**
** Function:    DRM_MGR_GetWMDRMNETMinAppSec
**
** Synopsis:    Gets License minimum application security level.  Must be called
**              after a successful bind via DRM_MGR_Bind and prior to DRM_MGR_Commit.
**
** Arguments:   [f_pDrmContext]        -- Opaque DRM Manager context initialized by a call to 
**                                        DRM_MGR_Initialize
**              [f_pdwMinAppSec]       -- Returns License Chains minimum app security level. 
**
** Returns:     DRM_SUCCESS on success or
**              DRM_E_INVALIDARG if any parameter is invalid or
**              DRM_E_LICENSENOTBOUND if license hasn't been bound by calling DRM_MGR_Bind successfully
**              any return code as defined in drmrestults.h
**
**********************************************************************/
DRM_RESULT DRM_API DRM_MGR_GetWMDRMNETMinAppSec(
    IN        DRM_MANAGER_CONTEXT        *f_pDrmContext,
    OUT       DRM_DWORD                  *f_pdwMinAppSec)
{
    DRM_RESULT dr = DRM_SUCCESS;
    DRM_MANAGER_CONTEXT_INTERNAL *pContext = (DRM_MANAGER_CONTEXT_INTERNAL *)f_pDrmContext;

#ifndef DRM_SUPPORT_WMDRMNET
    ChkDR( DRM_E_NOTIMPL );
#else

    ChkArg( f_pDrmContext && f_pdwMinAppSec );

    if( !pContext->fBindThenCommit )
    {
        ChkDR( DRM_E_LICENSENOTBOUND );
    }

    *f_pdwMinAppSec = pContext->oFFLicense.dwMinimumRequiredAppSec;

#endif /* DRM_SUPPORT_WMDRMNET */

ErrorExit:
    return dr;
}

DRM_RESULT DRM_API DRM_MGR_ProcessCommand(
    IN       DRM_MANAGER_CONTEXT   *f_pcontextMGR,
    IN       DRM_DWORD              f_dwOperationCode,
    IN       DRM_DWORD              f_dwRequestArgument1,
    IN       DRM_DWORD              f_dwRequestArgument2,
    IN       DRM_DWORD              f_dwRequestArgument3,
    IN       DRM_DWORD              f_dwRequestArgument4,
    IN       DRM_BYTE              *f_pbRequestData,
    IN       DRM_DWORD              f_dwRequestDataLength,
        OUT  DRM_DWORD             *f_pdwResponseResult1,
        OUT  DRM_DWORD             *f_pdwResponseResult2,
        OUT  DRM_DWORD             *f_pdwResponseResult3,
        OUT  DRM_DWORD             *f_pdwResponseResult4)
{

    DRM_RESULT dr = DRM_SUCCESS;
    DRM_MANAGER_CONTEXT_INTERNAL *pContext   = (DRM_MANAGER_CONTEXT_INTERNAL *) f_pcontextMGR;

    ChkArg( f_pcontextMGR != NULL);

      

    switch( f_dwOperationCode )
    {
        case OpGetVersion:
        {
           ChkArg(f_pdwResponseResult1 != NULL 
               && f_pdwResponseResult2 != NULL);

          *f_pdwResponseResult1 = JANUS_MAJOR_VERSION;   /* 10 */
          *f_pdwResponseResult2 = JANUS_MINOR_VERSION;   /* 1  */
           dr = DRM_SUCCESS;
           break;
       }
       case OpProcessLicenseRevocationList:
       {
          ChkDR(_ProcessLicenseRevocationList( pContext, 
                                               f_dwRequestArgument1, 
                                               f_dwRequestArgument2,
                                               f_pbRequestData,
                                               f_dwRequestDataLength,
                                               f_pdwResponseResult1,
                                               f_pdwResponseResult2));
          
          break;
       }
       default:
          dr = DRM_E_NOTIMPL;
    }

ErrorExit:

    return dr;           
}

DRM_RESULT DRM_API DRM_MGR_ProcessRequest( 
    IN       DRM_MANAGER_CONTEXT   *f_pcontextMGR,
    IN       DRM_DWORD              f_dwOperationCode,
    IN       DRM_DWORD              f_dwRequestArgument1,
    IN       DRM_DWORD              f_dwRequestArgument2,
    IN       DRM_DWORD              f_dwRequestArgument3,
    IN       DRM_DWORD              f_dwRequestArgument4,
        OUT  DRM_DWORD             *f_pdwResponseResult1,
        OUT  DRM_DWORD             *f_pdwResponseResult2,
        OUT  DRM_DWORD             *f_pdwResponseResult3,
        OUT  DRM_DWORD             *f_pdwResponseResult4,
        OUT  DRM_BYTE              *f_pbResponseData,
        OUT  DRM_DWORD             *f_pdwResponseDataLength)

{
      return DRM_E_NOTIMPL;
}

