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

#include <drmcommon.h>
#include <drmutilities.h>
#include <drmcrt.h>
#include <drmcontextsizes.h>
#include <drmheaderparser.h>
#include <drmxmlparser.h>


static DRM_RESULT _BasicHeaderChecks( 
    const DRM_CONST_STRING *pdstrContentHeader,
    OUT   DRM_CONST_STRING *pdstrData )
{
    DRM_RESULT       dr = DRM_E_LOGICERR;    
    DRM_CONST_STRING dstrValue = EMPTY_DRM_STRING;

    DRMASSERT( pdstrContentHeader != NULL );

    /* Ensure the outter tag is WMRMHEADER */
    if( DRM_FAILED( DRM_XML_GetNode( pdstrContentHeader, &g_dstrTagWrmHeader, NULL, NULL, 0, NULL, pdstrData ) ) )
    {
        dr = CH_INVALID_HEADER;
        goto ErrorExit;
    }

    /* Make sure there is a version number */
    if( DRM_FAILED( DRM_XML_GetNodeAttribute( pdstrContentHeader, &g_dstrAttributeVersion, pdstrData ) ) ||
        pdstrData->cchString == 0 )
    {
        dr = CH_VERSION_MISSING;
        goto ErrorExit;
    }

    /* Make sure we understand the version of the header */
    if( !DRM_UTL_DSTRStringsEqual( &g_dstrAttributeVersionValue, pdstrData ) )
    {
        dr = CH_UNSUPPORTED_VERSION;
        goto ErrorExit;
    }

    /* Get the data section. */
    if( DRM_FAILED( DRM_XML_GetSubNode( pdstrContentHeader, &g_dstrTagData, NULL, NULL, 0, pdstrData, NULL, 1 ) ) )
    {
        dr = CH_INVALID_HEADER;
        goto ErrorExit;
    }

    /* Get the KID */
    if( DRM_FAILED( DRM_XML_GetSubNodeByPath( pdstrData, &g_dstrTagKID, NULL, NULL, NULL, &dstrValue, g_wchForwardSlash ) ) )
    {
        dr = CH_KID_MISSING;
        goto ErrorExit;
    }


    /* Get the LAINFO */
    if( DRM_FAILED( DRM_XML_GetSubNodeByPath( pdstrData, &g_dstrTagLAINFO, NULL, NULL, NULL, &dstrValue, g_wchForwardSlash ) ) )
    {
        dr = CH_LAINFO_MISSING;
        goto ErrorExit;
    }

    /* Get the Checksum */
    if( DRM_FAILED( DRM_XML_GetSubNodeByPath( pdstrData, &g_dstrTagChecksum, NULL, NULL, NULL, &dstrValue, g_wchForwardSlash ) ) )
    {
        dr = CH_CHECKSUM_MISSING;
        goto ErrorExit;
    }

    /* Enough fields were present.  Basic checks pass */
    dr = DRM_SUCCESS;
ErrorExit:
    return dr;
}


DRM_RESULT DRM_API DRM_HDR_GetAttribute(
    IN const DRM_CONST_STRING       *pdstrContentHeader,
    IN const DRM_CONST_STRING       *pdstrAttrName,
    IN       eDRM_HEADER_ATTRIBUTES  eAttribute,
    OUT      DRM_CONST_STRING       *pdstrValue,
    IN       DRM_WCHAR               chXPathSeparator)
{
    DRM_RESULT              dr = DRM_SUCCESS;    
    const DRM_CONST_STRING *pdstrString = NULL;
    DRM_CONST_STRING        dstrData = EMPTY_DRM_STRING;
    
    ChkDRMString( pdstrContentHeader );
    ChkArg( pdstrValue != NULL );

    if( chXPathSeparator == 0 )
    {
        chXPathSeparator = g_wchForwardSlash;
    }

    if( (eAttribute != DRM_HEADER_ATTRIB_OTHER) && ( NULL != pdstrAttrName ) )
    {
        /* If caller wants a known attribute -- we don't allow an attribute string */
        dr = DRM_E_INVALIDARG;
        goto ErrorExit;
    }
    
    ChkDR( _BasicHeaderChecks( pdstrContentHeader, &dstrData ) );    
    switch( eAttribute )
    {
    case DRM_HEADER_ATTRIB_VERSION:
        pdstrString = &g_dstrAttributeVersion;
        dr = DRM_XML_GetNodeAttribute( pdstrContentHeader, pdstrString, pdstrValue );        
        goto ErrorExit;
    case DRM_HEADER_ATTRIB_KID:
        pdstrString = &g_dstrTagKID;
        break;
    case DRM_HEADER_ATTRIB_LAINFO:
        pdstrString = &g_dstrTagLAINFO;
        break;
    case DRM_HEADER_ATTRIB_CHECKSUM:
        pdstrString = &g_dstrTagChecksum;
        break;
    case DRM_HEADER_ATTRIB_SECURITYVERSION:
        pdstrString = &g_dstrTagSecurityVersion;
        break;
    case DRM_HEADER_ATTRIB_OTHER:
        ChkArg( pdstrAttrName );
        pdstrString = pdstrAttrName;        
        break;
    default:
        return DRM_E_INVALIDARG;
    }

    ChkDR( DRM_XML_GetSubNodeByPath( &dstrData, pdstrString, NULL, NULL, NULL, pdstrValue, chXPathSeparator ) );
    dr = DRM_SUCCESS;

ErrorExit:
    return( dr );
}

DRM_RESULT DRM_API DRM_HDR_GetUplink(
    IN const DRM_CONST_STRING *pdstrContentHeader,
    IN       DRM_DWORD         dwUplinkNumber,
    OUT      DRM_CONST_STRING *pdstrKid )
{
    DRM_RESULT dr = DRM_SUCCESS;    
    DRM_CONST_STRING dstrString = EMPTY_DRM_STRING;

    ChkDRMString( pdstrContentHeader );
    ChkArg( pdstrKid != NULL );

    ChkDR( DRM_XML_GetSubNodeByPath( pdstrContentHeader, &g_dstrXPathHeaderUplinks, NULL, NULL, &dstrString, NULL, g_wchForwardSlash ) );
    ChkDR( DRM_XML_GetSubNode( &dstrString, &g_dstrTagUplink, NULL, NULL, dwUplinkNumber, NULL, pdstrKid, 1) );
ErrorExit:
    return dr;
}

DRM_RESULT DRM_API DRM_HDR_Verify(
    IN const DRM_CONST_STRING   *pdstrContentHeader,
    IN const DRM_CONST_STRING   *pdstrPubKey,
    IN       DRM_CRYPTO_CONTEXT *pContext,
    OUT      DRM_LONG           *plResult)
{
    DRM_RESULT       dr = DRM_E_LOGICERR;    
    DRM_CONST_STRING dstrSig  = EMPTY_DRM_STRING;
    DRM_CONST_STRING dstrData = EMPTY_DRM_STRING;    
    DRM_DWORD        dwSize = SIZEOF( PUBKEY );    
    
    ChkDRMString( pdstrPubKey );
    ChkDRMString( pdstrContentHeader );
    ChkArg( plResult != NULL
         && pContext != NULL );

    ChkDR(DRM_B64_DecodeW(pdstrPubKey, &dwSize, (DRM_BYTE *)&(pContext->pubKey), 0 ) );    
    ChkDR( _BasicHeaderChecks( pdstrContentHeader, &dstrData ) );
    
    if( DRM_FAILED( DRM_XML_GetSubNodeByPath( pdstrContentHeader, &g_dstrXPathSigValue, NULL, NULL, NULL, &dstrSig, g_wchForwardSlash ) ) )
    {
        dr = CH_INVALID_HEADER;
        goto ErrorExit;
    }

    dwSize = PK_ENC_SIGNATURE_LEN;
    if( DRM_FAILED(DRM_B64_DecodeW(&dstrSig, &dwSize, pContext->signature, 0 ) ) )
    {
        dr = CH_UNABLE_TO_VERIFY;
        goto ErrorExit;
    }

    if ( DRM_PK_Verify( pContext->rgbCryptoContext, 
                      &(pContext->pubKey), 
                      PB_DSTR(&dstrData), 
                      CB_DSTR(&dstrData),
                      pContext->signature) )
    {
        *plResult = 1;
    }
    else
    {
        *plResult = 0;
    }
    dr = DRM_SUCCESS;

ErrorExit:
    return( dr );
}
