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

#include <drmcommon.h>
#include <drmutilities.h>
#include <drmcrt.h>
#include <drmcontextsizes.h>
#include <drmexpreval.h>
#include <drmliceval.h>
#include <drmheaderparser.h>
#include <drmlicenseparser.h>
#include <drmsecurestore.h>
#include <drmxmlparser.h>
#include <drmlicreason.h>

#if DRM_SUPPORT_CONTENT_REVOCATION

static DRM_BOOL IsLicenseRevoked( 
    DRM_LICEVAL_CONTEXT *pcontextLicenseEval,
    DRM_HDS_CONTEXT     *poHdsContext );

static DRM_RESULT SetOrUpdateContentRevocation( 
    DRM_LICEVAL_CONTEXT *pcontextLicenseEval,
    DRM_HDS_CONTEXT     *poHdsContext);

#endif

static DRM_RESULT Eval(
          DRM_LICEVAL_CONTEXT *pcontextLicenseEval,
          DRM_CONST_STRING    *pdstrLIData,
    const DRM_CONST_STRING    *pdstrEvent, 
    const DRM_CONST_STRING    *pdstrAttrValue,
          DRM_BOOL             fCondition, 
          DRM_BOOL            *pfResult, 
          DRM_BOOL            *pfExisted );


/******************************************************************************
** 
** Function :   _IsCachedEvent
** 
** Synopsis :   Check whether we already have this event cached, and find the 
**              index of the entry in cache, if found
** 
** Arguments :  f_pdstrEvent    - Event string
**              f_pdstrType     - Type string if it is a "ONACTION" event
**              f_rgCachedEvents- Cache of events
**              f_piEvent       - Index of entry in cache. If event is not found 
**                                in cache, this returns -1
** 
** Returns :    TRUE - if event found in cache
**              FALSE- if not
** 
** Notes :      
** 
******************************************************************************/
DRM_BOOL _IsCachedEvent(
    IN  const   DRM_CONST_STRING    *f_pdstrEvent, 
    IN  const   DRM_CONST_STRING    *f_pdstrType,
    IN          DRM_CACHED_EVENT     f_rgCachedEvents[],
    IN          DRM_DWORD            f_cCachedEvents,
        OUT     DRM_LONG            *f_piEvent )
{
    DRM_DWORD   iEvent = 0;
    
    if ( DRM_UTL_DSTRStringsEqual( f_pdstrEvent, &g_dstrLicEvalOnAction) )
    {
        /* We might have already parsed this event  */        
        while ( iEvent < f_cCachedEvents)
        {
            if ( DRM_UTL_DSTRStringsEqual( f_pdstrType, &f_rgCachedEvents[iEvent].dstrType) )
            {
                *f_piEvent = iEvent;
                return TRUE;
            }
            iEvent++;
        }
    } 
    
    *f_piEvent = -1;
    return FALSE;
}

/* Secure store variable accessor/mutator functions.  These will be given to the Expression evaluator via it's context
   structure.  The implementation lives in exprvariable.c */

extern DRM_RESULT DRM_API GlobalSetVariable(
    const DRM_CONST_STRING *pStringToken, 
    const TOKEN            *pNewValue, 
          TOKEN            *pResult, 
          DRM_VOID         *pvOpaqueData);

extern DRM_RESULT DRM_API GlobalGetVariable(
    const DRM_CONST_STRING *pStringToken,
          TOKEN            *pResult,
          DRM_VOID         *pvOpaqueData);

DRM_RESULT DRM_API DRM_LEVL_EvaluateExpression(
    IN       DRM_LICEVAL_CONTEXT *pcontextLicenseEval,
    IN const DRM_CONST_STRING    *pdstrExpression,
    IN       DRM_BOOL            *pfValue)
{
    DRM_RESULT dr = DRM_SUCCESS;
    TOKEN tResult;

    ChkArg( pcontextLicenseEval && pfValue );
    ChkDRMString( pdstrExpression );

    pcontextLicenseEval->contextEXPR.GetVariable  = GlobalGetVariable;
    pcontextLicenseEval->contextEXPR.SetVariable  = GlobalSetVariable;
    pcontextLicenseEval->contextEXPR.pvOpaqueData = pcontextLicenseEval;
    pcontextLicenseEval->contextEXPR.pLicStoreEnumContext = pcontextLicenseEval->pLicStoreEnumContext;
    ChkDR( DRM_EXPR_EvaluateExpression( pdstrExpression, 
                                      &(pcontextLicenseEval->contextEXPR), 
                                       &tResult ) );
    if (tResult.TokenType == TOKEN_LONG)
    {
        *pfValue = ( tResult.val.lValue != 0);
    }

ErrorExit:
    return dr;
}

#if DRM_SUPPORT_ANTIROLLBACK_CLOCK
/* Get the "machine.datetime" for the antirollback clock */
DRM_RESULT GetMachineDatetime(
    IN  DRM_LICEVAL_CONTEXT *pcontextLicenseEval,
    OUT DRM_UINT64 *u64MachineDateTime
)
{
    DRM_RESULT  dr               = DRM_SUCCESS;
    TOKEN       tMachineDatetime = {0};

    ChkArg( pcontextLicenseEval != NULL
          && u64MachineDateTime != NULL );

    /* Use GlobalGetVariable for its side effects */
    ChkDR( GlobalGetVariable( &g_dstrDRM_LS_MACHINE_DATETIME, 
                        &tMachineDatetime, 
                        pcontextLicenseEval ));

    if ( tMachineDatetime.TokenType != TOKEN_DATETIME )
    {
        ChkDR( CPRMEXP_RETRIEVAL_FAILURE );
    }

    *u64MachineDateTime = tMachineDatetime.val.u64DateTime;
ErrorExit:
    return dr;
}
#endif

DRM_RESULT DRM_API DRM_LEVL_PerformOperations(
    IN  DRM_LICEVAL_CONTEXT          *pcontextLicenseEval,
    IN  eDRM_LICEVAL_OPERATIONS       eOperation,
    IN  eDRM_LICEVAL_OPERATION_STATE  eOperationState,
    IN  const DRM_CONST_STRING       *pdstrAction,     /* Only required if DRM_LICENSE_EVAL_ACTION is passed as eOperation */
    OUT DRM_BOOL                     *pfPerform,
    OUT DRM_BOOL                     *pfActionExisted,
    IN  DRM_HDS_CONTEXT              *pcontextHDS)
{
    DRM_RESULT          dr          = DRM_SUCCESS;
    DRM_LONG            lResult     = 0;
    DRM_CONST_STRING    dstrAction  = EMPTY_DRM_STRING;
    DRM_CONST_STRING    dstrLIData  = EMPTY_DRM_STRING;
    DRM_BOOL            fCondition  = TRUE;
    DRM_CONST_STRING    dstrKID     = EMPTY_DRM_STRING;

    DRM_PROFILING_ENTER_SCOPE(L"DRM_LEVL_PerformOperations", g_pwszEnteringFunction, DRM_PROFILING_DONT_START);
                
    ChkArg( pcontextLicenseEval                        != NULL
         && pcontextLicenseEval->pcontextBBX           != NULL /* The other pointers in the context structure *may* not be needed so don't fail just yet for those */
         && pcontextHDS                                != NULL );
    
    switch( eOperationState )
    {
    case DRM_LICENSE_EVAL_CAN_DO_OPERATION:
        fCondition = TRUE;
        break;
    case DRM_LICENSE_EVAL_DONE_WITH_OPERATION:
        fCondition = FALSE;
        break;
    default:
        ChkDR( DRM_E_INVALIDARG );
    }

    if( fCondition && NULL == pfPerform )
    {
        ChkDR( DRM_E_INVALIDARG );
    }

    if( pcontextLicenseEval->dwFlags & 
        ~(  LICEVAL_VERIFY_IGNORE_VERIFICATION
          | LICEVAL_VERIFY_CERT_WITH_EXPIRYCHECK
          | LICEVAL_VERIFY_CERT_WITHOUT_EXPIRYCHECK
          | LICEVAL_VERIFY_SIGNATURE ) )
    {
        /* Some flag we don't recognize was given. */
        ChkDR( DRM_E_INVALIDARG );
    }

    if( !(pcontextLicenseEval->dwFlags & LICEVAL_VERIFY_SIGNATURE)
     &&  (  pcontextLicenseEval->dwFlags & LICEVAL_VERIFY_CERT_WITH_EXPIRYCHECK
         || pcontextLicenseEval->dwFlags & LICEVAL_VERIFY_CERT_WITHOUT_EXPIRYCHECK ) )
    {
        /* If you are checking the cert chain you must also check the signature */
        ChkDR( DRM_E_INVALIDARG );
    }

    if( pfPerform )
    {
        *pfPerform = FALSE;
    }

    if (!pcontextLicenseEval->fReserved)
    {
        if ( pcontextLicenseEval->dstrContentLicense.pwszString == NULL )
        {
            ChkDR( LICEVAL_LICENSE_NOT_SUPPLIED );
        }
        
        if( (pcontextLicenseEval->dwFlags & LICEVAL_VERIFY_IGNORE_VERIFICATION) == 0 )
        {
            /*
            **  Some XML optimizations can be done here as well. Ignoring for
            **  now since the XML operations in here are relatively inexpensive.
            */
            ChkDR( DRM_LIC_VerifySignature( &pcontextLicenseEval->dstrContentLicense, 
                                             pcontextLicenseEval->pcontextBBX, 
                                            &lResult) );
            if (lResult == 0)
            {
                ChkDR(LICEVAL_INVALID_LICENSE);
            }
            
#if !DRM_SUPPORT_SYMMETRIC_OPTIMIZATIONS
            if ( (pcontextLicenseEval->dwFlags & LICEVAL_VERIFY_CERT_WITH_EXPIRYCHECK) 
              || (pcontextLicenseEval->dwFlags & LICEVAL_VERIFY_CERT_WITHOUT_EXPIRYCHECK) )
            {
                /* During storing, we normally should check expiry of license server certificates. */
                ChkDR( DRM_LIC_VerifyCertChain( &pcontextLicenseEval->dstrContentLicense,
                                                ( pcontextLicenseEval->dwFlags & LICEVAL_VERIFY_CERT_WITH_EXPIRYCHECK ) ? TRUE : FALSE,
                                                 pcontextLicenseEval, 
                                                &lResult) );
                if (lResult == 0)
                {
                    ChkDR(LICEVAL_INVALID_LICENSE);
                }
            }
#endif
        }

#if DRM_SUPPORT_CONTENT_REVOCATION
        /* Process content revocation lists if any from this license. */
        dr = SetOrUpdateContentRevocation( pcontextLicenseEval, pcontextHDS );
        if ( DRM_FAILED( dr ) || IsLicenseRevoked( pcontextLicenseEval, pcontextHDS ) )
        {
            /* Treat as failure and revoke this license. */
            /* Set a value in the ExprControl  */
            /* mpExprControl->m_lReasonForUnusable = LR_LICENSE_CONTENT_REVOKED; */
            dr = LICEVAL_LICENSE_REVOKED;        
            goto ErrorExit;
        }
#endif        
        pcontextLicenseEval->fReserved = TRUE; 
    }

    if ( !pcontextLicenseEval->fUseCachedAttribs )
    {
        ChkDR( DRM_LIC_GetAttribute( &(pcontextLicenseEval->dstrContentLicense), NULL, DRM_LICENSE_ATTRIB_LID, &dstrLIData, &dstrAction, 0 ) );
        ChkDR( DRM_UTL_StringToGuid( &dstrAction, (DRM_GUID*)pcontextLicenseEval->LID.rgb ) );
        ChkDR( DRM_LIC_GetAttribute( &(pcontextLicenseEval->dstrContentLicense), NULL, DRM_LICENSE_ATTRIB_KID, &dstrLIData, &dstrKID, 0 ) );
        ChkDR( DRM_UTL_DecodeKID( &dstrKID, &pcontextLicenseEval->KID ) );        
    }
    ZEROMEM( &(pcontextLicenseEval->PMExpiryDate), SIZEOF( pcontextLicenseEval->PMExpiryDate ) );
    pcontextLicenseEval->PMExpiryDate.wYear  = 9999; /* unlimited expiry */
    pcontextLicenseEval->PMExpiryDate.wMonth = 12;
    pcontextLicenseEval->PMExpiryDate.wDay   = 31;
    pcontextLicenseEval->lPMRights           = 0;

#if DRM_SUPPORT_PMLICENSE
    pcontextLicenseEval->pwszPMLicVersion = &g_dstrPMLicenseVersionString;
#else
    pcontextLicenseEval->pwszPMLicVersion = NULL;
#endif

    /* Actually can do action stuff here.  */
    switch(eOperation)
    {
        case DRM_LICENSE_EVAL_SELECT:
        {
            if( eOperationState == DRM_LICENSE_EVAL_CAN_DO_OPERATION )
            {
                /* Fetch KID, ISSUEDATE, and CONTENTPUBKEY from the license. */
                DRM_CONST_STRING dstrHeaderKID = EMPTY_DRM_STRING;                

#if DRM_SUPPORT_CONTENT_REVOCATION
                if (pcontextLicenseEval->dstrContentHeader.pwszString!=NULL && 
                    pcontextLicenseEval->dstrContentHeader.cchString>0 &&
                    pcontextLicenseEval->dwChainDepth == 0)
                {
                    dr = DRM_HDR_GetAttribute( &(pcontextLicenseEval->dstrContentHeader), NULL, DRM_HEADER_ATTRIB_KID, &dstrHeaderKID, 0 );
                    if (dr == DRM_SUCCESS)
                    {
                        /* Match KID only if it can be retrieved from header. Thus, any license can be selected for an empty content header */
                        /* as long as the condition allows. If the condition refers to content data, then it will fail. */
                        /* Compare the KID */
                        
                        if ( !pcontextLicenseEval->fUseCachedAttribs )
                        {
                            if ( !DRM_UTL_DSTRStringsEqual( &dstrKID, &dstrHeaderKID ) )
                            {
                                dr = LICEVAL_KID_MISMATCH;
                                goto ErrorExit;
                            }
                        
                            (void)DRM_LIC_GetAttribute( &(pcontextLicenseEval->dstrContentLicense), 
                                                        NULL, 
                                                        DRM_LICENSE_ATTRIB_CONTENTPUBKEY, 
                                                        &dstrLIData, 
                                                        &pcontextLicenseEval->rgdstrCachedAttribs[eDRM_LIC_CACHED_ATTRIB_CONTENTPUBKEY], 
                                                        0 );
                        }
                        else
                        {
                            if ( !DRM_UTL_DSTRStringsEqual( &pcontextLicenseEval->rgdstrCachedAttribs[eDRM_LIC_CACHED_ATTRIB_KID], 
                                                            &dstrHeaderKID ) )
                            {
                                dr = LICEVAL_KID_MISMATCH;
                                goto ErrorExit;
                            }                            
                        }

                        if ( pcontextLicenseEval->rgdstrCachedAttribs[eDRM_LIC_CACHED_ATTRIB_CONTENTPUBKEY].cchString != 0 )
                        {
                            ChkDR( DRM_HDR_Verify( &(pcontextLicenseEval->dstrContentHeader), 
                                                   &(pcontextLicenseEval->rgdstrCachedAttribs[eDRM_LIC_CACHED_ATTRIB_CONTENTPUBKEY]), 
                                                   &pcontextLicenseEval->pcontextBBX->CryptoContext, 
                                                   &lResult ) );                        
                            if (lResult == 0)
                            {
                                dr = LICEVAL_CONTENT_SIGNATURE_FAILURE;
                                goto ErrorExit;
                            }
                        }
                    }
                }
#endif                
            }

            dstrAction.cchString  = g_dstrLicEvalOnSelect.cchString;
            dstrAction.pwszString = g_dstrLicEvalOnSelect.pwszString;
        }
            break;
        case DRM_LICENSE_EVAL_DELETE:
                dstrAction.cchString  = g_dstrLicEvalOnDelete.cchString;
                dstrAction.pwszString = g_dstrLicEvalOnDelete.pwszString;
            break;
        case DRM_LICENSE_EVAL_STORE:
            fCondition = FALSE;
            dstrAction.cchString  = g_dstrLicEvalOnStore.cchString;
            dstrAction.pwszString = g_dstrLicEvalOnStore.pwszString;
            break;
#if DRM_SUPPORT_ANTIROLLBACK_CLOCK             
        case DRM_LICENSE_EVAL_REPORTCLOCK_ROLLBACK:
            if (fCondition)
            {
                ChkDR(DRM_E_INVALIDARG);
            }
            ASSIGN_DRM_STRING(dstrAction, g_dstrLicEvalOnClockRollback);
            break;
#endif
        case DRM_LICENSE_EVAL_ACTION:
            {
                ChkArg(pdstrAction != NULL);
                dstrAction.cchString  = g_dstrLicEvalOnAction.cchString;
                dstrAction.pwszString = g_dstrLicEvalOnAction.pwszString;
            }
            break;
        default: 
            ChkDR(DRM_E_INVALIDARG); 
    }

    pcontextLicenseEval->contextEXPR.GetVariable  = GlobalGetVariable;
    pcontextLicenseEval->contextEXPR.SetVariable  = GlobalSetVariable;
    pcontextLicenseEval->contextEXPR.pvOpaqueData = pcontextLicenseEval;
    pcontextLicenseEval->contextEXPR.pLicStoreEnumContext = pcontextLicenseEval->pLicStoreEnumContext;
    MEMCPY(&pcontextLicenseEval->contextEXPR.KID, &pcontextLicenseEval->KID, SIZEOF(DRM_KID));
    MEMCPY(&pcontextLicenseEval->contextEXPR.LID, &pcontextLicenseEval->LID, SIZEOF(DRM_LID));

    dr = Eval( pcontextLicenseEval, 
               &dstrLIData, 
               &dstrAction, 
               pdstrAction, 
               fCondition, 
               pfPerform, 
               pfActionExisted );

ErrorExit:

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


/*--------------------------------------------------------------------
** 
** Function :   DRM_LEVL_IsLicenseReadyForDeletion
** 
** Synopsis :   Checks if this license has expired 
** 
** Arguments :  [pcontextLicenseEval] - License eval context
**              [pfDelete] - Pointer to bool which is set to true if 
**                  this license can be deleted
** 
** Returns :    
** 
** Notes :      TODO:   For now this function is mostly a CUT&PASTE
**              job from DRM_LEVL_PerformOperations, with a lot of 
**              duplicate code. The common sections should be factored
**              out into a separate subroutine
** 
--------------------------------------------------------------------*/
DRM_RESULT DRM_API DRM_LEVL_IsLicenseReadyForDeletion( 
    DRM_LICEVAL_CONTEXT *pcontextLicenseEval,
    DRM_BOOL            *pfDelete 
    )
{
    DRM_RESULT          dr         = DRM_SUCCESS;
    DRM_CONST_STRING    dstrBuffer = EMPTY_DRM_STRING;
    DRM_CONST_STRING    dstrLIData = EMPTY_DRM_STRING;
    DRM_BOOL            fResult    = FALSE;

    /*  The other pointers in the context structure *may* not be 
        needed so don't fail just yet for those */
    ChkArg(  pcontextLicenseEval              != NULL
          && pfDelete                         != NULL    
          && pcontextLicenseEval->pcontextBBX != NULL );

    /*  Clear output */
    *pfDelete = FALSE;

    ChkDR( DRM_LIC_GetAttribute( &(pcontextLicenseEval->dstrContentLicense), 
                                 NULL, 
                                 DRM_LICENSE_ATTRIB_LID, 
                                 &dstrLIData,
                                 &dstrBuffer, 
                                 0 ) );
    ChkDR(DRM_UTL_StringToGuid( &dstrBuffer, (DRM_GUID*)pcontextLicenseEval->LID.rgb ) );
    ChkDR(DRM_LIC_GetAttribute( &pcontextLicenseEval->dstrContentLicense, 
                                NULL, 
                                DRM_LICENSE_ATTRIB_KID, 
                                &dstrLIData,
                                &dstrBuffer, 
                                0 ) );
    ChkDR( DRM_UTL_DecodeKID( &dstrBuffer, &pcontextLicenseEval->KID ) );

#if DRM_SUPPORT_PMLICENSE
    pcontextLicenseEval->pwszPMLicVersion = &g_dstrPMLicenseVersionString;
#else
    pcontextLicenseEval->pwszPMLicVersion = NULL;
#endif

    MEMCPY(
        &pcontextLicenseEval->contextEXPR.KID, 
        &pcontextLicenseEval->KID, 
        SIZEOF(DRM_KID)
        );
    MEMCPY(
        &pcontextLicenseEval->contextEXPR.LID, 
        &pcontextLicenseEval->LID, 
        SIZEOF(DRM_LID)
        ); 
    /*  Is license selectable   */
    ChkDR(DRM_LEVL_PerformOperations( 
            pcontextLicenseEval, 
            DRM_LICENSE_EVAL_SELECT, 
            DRM_LICENSE_EVAL_CAN_DO_OPERATION, 
            NULL, 
            &fResult, 
            NULL, 
            pcontextLicenseEval->pcontextHDS));
    if( (!fResult && pcontextLicenseEval->lReasonForFail == LR_LICENSE_EXPIRED)
        || pcontextLicenseEval->fDeleteLicense )
    {
        /* License cannot be selected because it is expired */
        *pfDelete = TRUE;
    }

ErrorExit:
    return ( dr );
}

#if DRM_SUPPORT_CONTENT_REVOCATION

static DRM_RESULT GetContentRevocationInStore(
    IN const DRM_LICEVAL_CONTEXT *pcontextLEVL,
    IN const DRM_CONST_STRING    *pdstrContentOwnerPubKey,
       OUT   DRM_CONST_STRING    *pdstrContentRevocation,
    IN       DRM_HDS_CONTEXT     *pcontextHDS )
{
    DRM_RESULT dr = DRM_SUCCESS;
    DRM_DWORD    cbBuffer = 0;
    DRM_LID      lid = { 0 };
    SHA_CONTEXT  contextSHA;
    DRM_BYTE     rgbPassword[__CB_DECL(SHA_DIGEST_LEN)];


    ChkArg( pcontextLEVL != NULL
         && pcontextHDS  != NULL 
         && pdstrContentRevocation != NULL );

    ChkDRMString( pdstrContentOwnerPubKey );
    
    DRM_SHA_Init     (&contextSHA);
    DRM_SHA_Update   (PB_DSTR (pdstrContentOwnerPubKey), 
                      CB_DSTR (pdstrContentOwnerPubKey), 
                     &contextSHA);
    DRM_SHA_Finalize (&contextSHA, lid.rgb);

    ChkDR( DRM_SST_CreateLicenseStatePassword( &lid, rgbPassword, (DRM_BYTE*)pcontextLEVL->pcontextBBX ) );

    cbBuffer = pcontextLEVL->cbRevocationBuffer;

    ChkDR( DRM_SST_GetData( pcontextLEVL->pcontextSSTRevocation, 
                           &lid, 
                            NULL,
                            rgbPassword,
                            SECURE_STORE_REVOCATION_DATA,
                            pcontextHDS, 
                            pcontextLEVL->pbRevocationBuffer, 
                           &cbBuffer));

    DSTR_FROM_PB(pdstrContentRevocation, pcontextLEVL->pbRevocationBuffer, cbBuffer);
        
    dr = DRM_SUCCESS;
ErrorExit:
    return(dr);
}

static DRM_RESULT SetContentRevocationInStore(
    IN const DRM_LICEVAL_CONTEXT *pcontextLEVL,
    IN const DRM_CONST_STRING    *pdstrContentOwnerPubKey, 
    IN const DRM_CONST_STRING    *pdstrContentRevocation,
    IN       DRM_HDS_CONTEXT     *pcontextHDS )
{
    DRM_RESULT   dr  = DRM_SUCCESS;    
    DRM_LID      lid = { 0 };
    SHA_CONTEXT  contextSHA;
    DRM_BYTE     rgbPassword [__CB_DECL(SHA_DIGEST_LEN)];
            
    ChkArg( pcontextLEVL != NULL
         && pcontextHDS  != NULL );
    ChkDRMString( pdstrContentOwnerPubKey );
    ChkDRMString( pdstrContentRevocation  );
   
    DRM_SHA_Init     (&contextSHA);
    DRM_SHA_Update   (PB_DSTR (pdstrContentOwnerPubKey), 
                      CB_DSTR (pdstrContentOwnerPubKey), 
                     &contextSHA);
    DRM_SHA_Finalize (&contextSHA, lid.rgb);

    ChkDR( DRM_SST_CreateLicenseStatePassword( &lid, rgbPassword, (DRM_BYTE*)pcontextLEVL->pcontextBBX ) );

    ChkDR( DRM_SST_SetData (pcontextLEVL->pcontextSSTRevocation, 
                           &lid, 
                            NULL,
                            rgbPassword,
                            SECURE_STORE_REVOCATION_DATA,
                            pcontextHDS, 
                            PB_DSTR(pdstrContentRevocation),
                            CB_DSTR(pdstrContentRevocation)));
    
    dr = DRM_SUCCESS;
ErrorExit:
    if( DRM_FAILED( dr ) )
    {
        dr = LICEVAL_UPDATE_FAILURE;
    }
    return ( dr );
}

static DRM_RESULT SetOrUpdateContentRevocation( 
    DRM_LICEVAL_CONTEXT *pcontextLicenseEval,
    DRM_HDS_CONTEXT     *poHdsContext )
{
    DRM_RESULT dr = DRM_SUCCESS;
    DRM_CONST_STRING dstrContentRev                = EMPTY_DRM_STRING;
    DRM_CONST_STRING dstrContentOwnerPubkey        = EMPTY_DRM_STRING;
    DRM_CONST_STRING dstrSecStateContentRevocation = EMPTY_DRM_STRING;
    DRM_DWORD dwLicSequenceNumber = 0;
    DRM_DWORD dwSecStateSequenceNumber = 0;
    DRM_DWORD dwIndex = 0;


    /* Retrieve content revocation from the license one by one and process. */
    while( DRM_SUCCESS == dr )
    {        
        dr = DRM_LIC_GetContentRevocation( &(pcontextLicenseEval->dstrContentLicense),
                                            dwIndex,
                                           &dwLicSequenceNumber,
                                           &dstrContentRev,
                                           &dstrContentOwnerPubkey,
                                            NULL,
                                           &pcontextLicenseEval->pcontextBBX->CryptoContext );
        if ( DRM_SUCCESS == dr )
        {
            if( DRM_SUCCESS != GetContentRevocationInStore(pcontextLicenseEval, &dstrContentOwnerPubkey, &dstrSecStateContentRevocation, poHdsContext ) 
             || DRM_SUCCESS != DRM_LIC_VerifyContentRevocation( &dstrSecStateContentRevocation, 
                                                                 NULL, 
                                                                &dwSecStateSequenceNumber, 
                                                                 NULL, 
                                                                 NULL, 
                                                                &pcontextLicenseEval->pcontextBBX->CryptoContext)
             || dwSecStateSequenceNumber < dwLicSequenceNumber )
            {
                /* The one in secure state seems older. Write the new one */
                dr = SetContentRevocationInStore(pcontextLicenseEval, &dstrContentOwnerPubkey, &dstrContentRev, poHdsContext );
            }
        }
        dwIndex++;
    }

    return ( dr );
}

static DRM_BOOL IsLicenseRevoked(
    DRM_LICEVAL_CONTEXT *pcontextLicenseEval,
    DRM_HDS_CONTEXT     *poHdsContext )
{
    DRM_RESULT dr                                   = DRM_SUCCESS;    
    DRM_CONST_STRING dstrContentPubKey              = EMPTY_DRM_STRING;
    DRM_CONST_STRING dstrSecStateContentRevocation  = EMPTY_DRM_STRING;
    DRM_CONST_STRING dstrCondition                  = EMPTY_DRM_STRING;
    DRM_BOOL         fResult                        = FALSE;    
    
    /* Check if the license has public key. If not, it cannot be revoked. */
    dr = DRM_LIC_GetAttribute( &(pcontextLicenseEval->dstrContentLicense), 
                               NULL, 
                               DRM_LICENSE_ATTRIB_CONTENTPUBKEY,
                               NULL,
                               &dstrContentPubKey, 0); /* optional. Don't check for errors. */
    if ( dr == DRM_SUCCESS && dstrContentPubKey.pwszString )
    {
        /* License contains content owner public key. Retrieve content revocation section for this public key, if any. */
        dr = GetContentRevocationInStore(pcontextLicenseEval, &dstrContentPubKey, &dstrSecStateContentRevocation, poHdsContext);
        if (dr == DRM_SUCCESS)
        {
            dr = DRM_LIC_VerifyContentRevocation( &dstrSecStateContentRevocation, 
                                                   NULL, 
                                                   NULL, 
                                                   NULL, 
                                                  &dstrCondition, 
                                                  &pcontextLicenseEval->pcontextBBX->CryptoContext);
            if (dr == DRM_SUCCESS)
            {
                /* It look like a good one. Evaluate the condition to determine if it allows the use of license. */
                if ( NULL == dstrCondition.pwszString || 0 == dstrCondition.cchString )
                {
                    goto ErrorExit; /* missing condition is treated as the one that allows use of license. */
                }

                /* DRM_LEVL_EvaluateExpression will return FALSE if the expression evaluates correctly and the license is revoked
                ** So we can set fResult to TRUE intialy and then invert it (if fResult is TRUE on the way out of DRM_LEVL_EvaluateExpression
                ** then the license IS NOT revoked.  If it is false then the license IS revoked
                */
                fResult = TRUE;
                ChkDR( DRM_LEVL_EvaluateExpression( pcontextLicenseEval, &dstrCondition, &fResult ) );
                fResult = !fResult;
            }
        }
    }

ErrorExit:
    return fResult;
}

#endif

static DRM_RESULT Eval(
          DRM_LICEVAL_CONTEXT *pcontextLicenseEval,
          DRM_CONST_STRING    *pdstrLIData,
    const DRM_CONST_STRING    *pdstrEvent, 
    const DRM_CONST_STRING    *pdstrAttrValue,
          DRM_BOOL             fCondition, 
          DRM_BOOL            *pfResult, 
          DRM_BOOL            *pfExisted)
{
    DRM_RESULT          dr          = DRM_SUCCESS; 
    DRM_LONG            iEvents     = 0;
    DRM_CONST_STRING    dstrExpr    = EMPTY_DRM_STRING;
    TOKEN               tResult;    
    
    ChkArg( pdstrEvent && ( !fCondition || pfResult ) );

    /* Set the default value for missing event or condition */
    if ( DRM_UTL_DSTRStringsEqual( pdstrEvent, &g_dstrLicEvalOnAction ) )
    {
        *pfResult = FALSE;   /* An action is missing => that right is not allowed. */
    }
    else
    {
        *pfResult = TRUE;    /* For other events, the default is to assume true. */
    }

    if (   pcontextLicenseEval->fUseCachedAttribs
        && _IsCachedEvent( pdstrEvent, 
                           pdstrAttrValue, 
                           pcontextLicenseEval->rgCachedEvents, 
                           pcontextLicenseEval->cCachedEvents,
                           &iEvents ) )
    {
        if (fCondition)
        {
            ASSIGN_DRM_STRING( dstrExpr, pcontextLicenseEval->rgCachedEvents[iEvents].dstrCondition );
        }
        else
        {
            ASSIGN_DRM_STRING( dstrExpr, pcontextLicenseEval->rgCachedEvents[iEvents].dstrAction );
        } 
        if ( dstrExpr.cchString == 0 )
        {
            dr = DRM_E_XMLNOTFOUND;
        }
    }
    else
    {
        if (fCondition)
        {
            dr = DRM_LIC_GetEvent(&(pcontextLicenseEval->dstrContentLicense), pdstrEvent, pdstrAttrValue, pdstrLIData, &dstrExpr, NULL, NULL);
        }
        else
        {
            dr = DRM_LIC_GetEvent(&(pcontextLicenseEval->dstrContentLicense), pdstrEvent, pdstrAttrValue, pdstrLIData, NULL, &dstrExpr, NULL);
        }
    }
    if( DRM_FAILED( dr ) )
    {
        dr = DRM_SUCCESS;
        if( pfExisted )
        {
            *pfExisted = FALSE;
        }
        goto ErrorExit;
    }
    
    if ( dstrExpr.cchString == 0)
    {
        if (pfResult)
        {
            *pfResult = TRUE; /* A missing CONDITION or missing expression in CONDITION. */
        }
        if( pfExisted )
        {
            *pfExisted = FALSE;
        }
        dr = DRM_SUCCESS;
        goto ErrorExit;
    }    
    
    if( pfExisted )
    {
        *pfExisted = TRUE;
    }

    dr = DRM_EXPR_EvaluateExpression( &dstrExpr, &pcontextLicenseEval->contextEXPR, &tResult );

    if( !fCondition )
    {
        /* It's an action.  Eat the errors */
        dr = DRM_SUCCESS;
        if( pfResult )
        {
            *pfResult = TRUE;
        }
    }
    else
    {
        ChkDR( dr );
        /* It's a condition */
        if( tResult.TokenType == TOKEN_LONG )
        {
            if( pfResult != NULL )
            {
                *pfResult = ( tResult.val.lValue != 0);
            }
        }
    }

ErrorExit:
    return ( dr );
}

/******************************************************************************
**
** Function :   DRM_LEVL_GetLicenseReasonForUnusable
**
** Synopsis :   Parse the license again to get the reason for failure.
**
** Arguments :  f_pcontextLicEval - Liceval context
**              f_plReasonForUnusable - Reason for failure
**
** Returns :
**
** Notes :      This is a workaround to compensate for the fact that v9
**              licenses do not set the reason correctly sometimes.
**
******************************************************************************/
DRM_RESULT DRM_LEVL_GetLicenseReasonForUnusable(
    IN       DRM_LICEVAL_CONTEXT    *f_pcontextLicEval,
       OUT   DRM_LONG               *f_plReasonForUnusable )
{
    DRM_RESULT  dr       = DRM_SUCCESS;
    DRMFILETIME fileTime = {0};
    DRMFILETIME currDate = {0};

    ChkArg( f_pcontextLicEval != NULL );
    ChkArg( f_plReasonForUnusable != NULL );

    *f_plReasonForUnusable = 0;

    OEM_GetDeviceTime( &currDate );

    dr = DRM_ASD_ParseV2License( &f_pcontextLicEval->dstrContentLicense,
                                 &g_dstrDRM_LS_BEGDATE_ATTR,
                                 NULL,
                                 &fileTime );

    if( dr == DRM_SUCCESS
     && DRM_UTL_DateLessThan( &currDate, &fileTime ) )
    {
        *f_plReasonForUnusable = LR_LICENSE_NOTENABLED;
    }
    else
    {
        dr = DRM_ASD_ParseV2License( &f_pcontextLicEval->dstrContentLicense,
                                     &g_dstrDRM_LS_ENDDATE_ATTR,
                                     NULL,
                                     &fileTime);

        if( dr == DRM_SUCCESS
         && DRM_UTL_DateLessThan( &fileTime, &currDate ) )
        {
            *f_plReasonForUnusable = LR_LICENSE_EXPIRED;
        }
        else
        {
            DRM_DWORD   dwAppSec = 0;
            dr = DRM_ASD_ParseV2License( &f_pcontextLicEval->dstrContentLicense,
                                         &g_dstrDRM_LS_APPSEC_ATTR,
                                         &dwAppSec,
                                         NULL );
            if( dr == DRM_SUCCESS
             && (   ( f_pcontextLicEval->fAppInfoValid && f_pcontextLicEval->certinfoApp.appSec < dwAppSec )
                 || f_pcontextLicEval->certinfoSDK.appSec < dwAppSec ) )
            {
                *f_plReasonForUnusable = LR_LICENSE_APPSECLOW;
            }
        }
    }

ErrorExit:
    return dr;
}

