2025-11-28 00:35:46 +09:00

628 lines
22 KiB
C++

//*****************************************************************************
//
// Microsoft Windows Media
// Copyright (C) Microsoft Corporation. All rights reserved.
//
// FileName: DRMReader.cpp
//
// Abstract: This file contains the implementation for the
// CLicenseViewer class, which can show the DRM-related
// properties of the file, including information about any
// licenses the user may have for the content.
//
// For examples about how to query the DRM reader for
// properties, see the ShowRights method
//
//*****************************************************************************
#include "stdafx.h"
#include "DRMReader.h"
#include "assert.h"
///////////////////////////////////////////////////////////////////////////////
typedef struct tag_DRM_Properties
{
const WCHAR* pwszName; // Name of the DRM property to test
WMT_ATTR_DATATYPE wmtExpectedType; // Its expected type - we'll break on mismatch
WORD cbExpectedLength; // Its expected length - we'll break on mismatch
}
DRM_Properties;
static const DRM_Properties g_BoolProperties[] =
{
{ g_wszWMDRM_IsDRM, WMT_TYPE_BOOL, sizeof( BOOL ) }, // True if the file is DRM-protected
{ g_wszWMDRM_IsDRMCached, WMT_TYPE_BOOL, sizeof( BOOL ) }, // True if license was created locally
{ g_wszWMDRM_ActionAllowed_Playback, WMT_TYPE_BOOL, sizeof( BOOL ) }, // True if license allows playback
{ g_wszWMDRM_ActionAllowed_CopyToCD, WMT_TYPE_BOOL, sizeof( BOOL ) }, // True if license allows writing to a CD
{ g_wszWMDRM_ActionAllowed_CopyToNonSDMIDevice, WMT_TYPE_BOOL, sizeof( BOOL ) }, // True if license allows tranfer to a non-SDMI compliant device
{ g_wszWMDRM_ActionAllowed_CopyToSDMIDevice, WMT_TYPE_BOOL, sizeof( BOOL ) }, // True if license allows tranfer to a SDMI compliant device
{ g_wszWMDRM_ActionAllowed_Backup, WMT_TYPE_BOOL, sizeof( BOOL ) } // True if license allows itself to be backed-up
};
#define NUM_BOOL_PROPERTIES ( sizeof( g_BoolProperties ) / sizeof(g_BoolProperties[ 0 ] ) )
static const DRM_Properties g_LicenseProperties[] =
{
{ g_wszWMDRM_LicenseState_Playback, WMT_TYPE_BINARY, sizeof( WM_LICENSE_STATE_DATA ) }, // License data related to playback
{ g_wszWMDRM_LicenseState_CopyToCD, WMT_TYPE_BINARY, sizeof( WM_LICENSE_STATE_DATA ) }, // License data related to writing content to CD
{ g_wszWMDRM_LicenseState_CopyToNonSDMIDevice, WMT_TYPE_BINARY, sizeof( WM_LICENSE_STATE_DATA ) }, // License data related to transfer to non-SDMI compliant devices
{ g_wszWMDRM_LicenseState_CopyToSDMIDevice, WMT_TYPE_BINARY, sizeof( WM_LICENSE_STATE_DATA ) } // License data related to transfer to SDMI compliant devices
};
#define NUM_LICENSE_PROPERTIES ( sizeof( g_LicenseProperties ) / sizeof( g_LicenseProperties[ 0 ] ) )
static const DRM_Properties g_HeaderProperties[] =
{
{ L"DRMHeader.KID", WMT_TYPE_STRING, 0 }, // The Key ID for the content
{ L"DRMHeader.LAINFO", WMT_TYPE_STRING, 0 } // The license acquisition URL for the content
};
#define NUM_HEADER_PROPERTIES ( sizeof( g_HeaderProperties ) / sizeof( g_HeaderProperties[ 0 ] ) )
//------------------------------------------------------------------------------
// Name: CLicenseViewer::CLicenseViewer()
// Desc: Constructor.
//------------------------------------------------------------------------------
CLicenseViewer::CLicenseViewer()
{
m_pIWMReader = NULL;
m_pIWMDRMReader = NULL;
m_hEvent = NULL;
m_cRef = 1;
}
//------------------------------------------------------------------------------
// Name: CLicenseViewer::~CLicenseViewer()
// Desc: Destructor.
//------------------------------------------------------------------------------
CLicenseViewer::~CLicenseViewer()
{
Cleanup();
}
//------------------------------------------------------------------------------
// Name: CLicenseViewer::Initialize()
// Desc: Initialization.
//------------------------------------------------------------------------------
HRESULT STDMETHODCALLTYPE CLicenseViewer::Initialize()
{
HRESULT hr = S_OK;
do
{
//
// Reset the m_hr variable
//
m_hr = S_OK;
//
// Create callback event, if it doesn't already exist
//
if ( NULL == m_hEvent )
{
m_hEvent = CreateEvent( NULL, FALSE, FALSE, NULL );
if ( !m_hEvent )
{
hr = HRESULT_FROM_WIN32( ::GetLastError() );
_tprintf( _T( "CreateEvent failed 0x%08lX\n" ), hr );
break;
}
}
//
// Create a Reader and get the DRM reader interface
//
if ( NULL == m_pIWMReader )
{
hr = WMCreateReader( NULL, 0, &m_pIWMReader );
if ( FAILED( hr ) || NULL == m_pIWMReader )
{
hr = E_FAIL;
_tprintf( _T( "Failed to create WMReader 0x%08lX\n" ), hr );
break;
}
SAFE_RELEASE( m_pIWMDRMReader );
assert( m_pIWMReader );
hr = m_pIWMReader->QueryInterface( IID_IWMDRMReader, ( void **)&m_pIWMDRMReader );
if ( FAILED( hr ) )
{
_tprintf( _T( "Failed to QI for DRM Reader interface 0x%08lX\n" ), hr );
break;
}
}
}
while ( FALSE );
return hr;
}
//------------------------------------------------------------------------------
// Name: CLicenseViewer::OnSample()
// Desc: Implementation of IWMReaderCallback::OnSample.
//------------------------------------------------------------------------------
HRESULT STDMETHODCALLTYPE CLicenseViewer::OnSample( DWORD dwOutputNum, QWORD cnsSampleTime, QWORD cnsSampleDuration, DWORD dwFlags, INSSBuffer __RPC_FAR *pSample, void *pvContext )
{
//
// No samples are expected, but a value must be returned
//
return( S_OK );
}
//------------------------------------------------------------------------------
// Name: CLicenseViewer::OnStatus()
// Desc: Implementation of IWMStatusCallback::OnStatus.
//------------------------------------------------------------------------------
HRESULT STDMETHODCALLTYPE CLicenseViewer::OnStatus( WMT_STATUS Status, HRESULT hr, WMT_ATTR_DATATYPE dwType, BYTE *pValue, void *pvContext )
{
//
// If the file has been opened (even if there was an error), then set the event so that
// the other thread can stop waiting, since OnStatus is called on the WMFSDK's thread
//
switch ( Status )
{
case WMT_OPENED:
_tprintf( _T( "WMT_OPENED 0x%08lX\n" ), hr );
if ( FAILED( hr ) )
{
m_hr = hr; // Set the m_hr member variable so the error can be addressed later
}
assert( m_hEvent );
SetEvent( m_hEvent );
break;
}
return( S_OK );
}
//------------------------------------------------------------------------------
// Implementation of IUnknown methods
//------------------------------------------------------------------------------
HRESULT STDMETHODCALLTYPE CLicenseViewer::QueryInterface( REFIID riid, void **ppvObject )
{
HRESULT hr = S_OK;
if ( riid == IID_IWMReaderCallback )
{
AddRef();
*ppvObject = ( IWMReaderCallback* )this;
}
else
{
*ppvObject = NULL;
return( E_NOINTERFACE );
}
return( hr );
}
///////////////////////////////////////////////////////////////////////////////
ULONG STDMETHODCALLTYPE CLicenseViewer::AddRef( void )
{
return( InterlockedIncrement( &m_cRef ) );
}
///////////////////////////////////////////////////////////////////////////////
ULONG STDMETHODCALLTYPE CLicenseViewer::Release( void )
{
if ( 0 == InterlockedDecrement( &m_cRef ) )
{
delete this;
return 0;
}
return( m_cRef );
}
//------------------------------------------------------------------------------
// Name: CLicenseViewer::Close()
// Desc: Close the reader.
//------------------------------------------------------------------------------
HRESULT CLicenseViewer::Close( void )
{
HRESULT hr = S_OK;
if ( NULL != m_pIWMReader )
{
hr = m_pIWMReader->Close();
}
return ( hr );
}
//------------------------------------------------------------------------------
// Name: CLicenseViewer::Open()
// Desc: Create and open the reader.
//------------------------------------------------------------------------------
HRESULT CLicenseViewer::Open( __in LPWSTR pwszInFile )
{
static const DWORD OPEN_WAIT_LENGTH = 30000;
HRESULT hr = S_OK;
do
{
//
// Create the reader and callback event, if not already done
//
hr = Initialize();
if ( FAILED( hr ) )
break;
//
// Validate input parameter
//
if ( NULL == pwszInFile )
{
hr = E_INVALIDARG;
_tprintf( _T( "Filename should not be NULL.\n" ) );
break;
}
//
// Open the input file
//
assert( m_pIWMReader );
hr = m_pIWMReader->Open( pwszInFile, this, NULL );
if ( FAILED( hr ) )
{
_tprintf( _T( "Failed to open file 0x%08lX\n" ), hr );
break;
}
//
// Wait for the WMT_OPENED message to be sent, signaling the file has been opened. This
// message is received in the OnStatus method
//
assert( m_hEvent );
if ( WAIT_TIMEOUT == WaitForSingleObject( m_hEvent, OPEN_WAIT_LENGTH ) )
{
_tprintf( _T( "Failed to open file in a reasonable amount of time.\n" ) );
break;
}
//
// Check the m_hr variable, which will get set if an error occured while opening the file.
// m_hr will get set in the OnStatus method.
//
if ( FAILED( m_hr ) )
{
hr = m_hr;
switch( m_hr )
{
case NS_E_LICENSE_REQUIRED:
_tprintf( _T( "A license is required to open the given file.\n" ) );
break;
default:
_tprintf( _T( "An error occured while opening file \"%ws\": %0.8x.\n" ), pwszInFile, hr );
}
break;
}
}
while( FALSE );
if ( FAILED( hr ) )
{
Cleanup();
}
return( hr );
}
//------------------------------------------------------------------------------
// Name: CLicenseViewer::PrintLicenseStateData()
// Desc: Display license data.
//------------------------------------------------------------------------------
HRESULT CLicenseViewer::PrintLicenseStateData( LPCTSTR tszOutputPrefix, DRM_LICENSE_STATE_DATA* pDRMLicenseStateData )
{
HRESULT hr = S_OK;
DWORD dwCountIndex;
DWORD dwDateIndex;
assert( tszOutputPrefix );
assert( pDRMLicenseStateData );
do
{
//
// Print the type of right given by aggregating all the licenses for the content
//
_tprintf( _T( "%sDRM_LICENSE_DATA.dwStreamId: %ld\n" ), tszOutputPrefix, pDRMLicenseStateData->dwStreamId );
switch( pDRMLicenseStateData->dwCategory )
{
case WM_DRM_LICENSE_STATE_NORIGHT: // No rights to perform this action
_tprintf( _T( "%sDRM_LICENSE_DATA.dwCategory: WM_DRM_LICENSE_STATE_NORIGHT\n" ), tszOutputPrefix );
break;
case WM_DRM_LICENSE_STATE_UNLIM: // Unlimited rights to perform this action
_tprintf( _T( "%sDRM_LICENSE_DATA.dwCategory: WM_DRM_LICENSE_STATE_UNLIM\n" ), tszOutputPrefix );
break;
case WM_DRM_LICENSE_STATE_COUNT: // Action may only be performed a certain number of times
_tprintf( _T( "%sDRM_LICENSE_DATA.dwCategory: WM_DRM_LICENSE_STATE_COUNT\n" ), tszOutputPrefix );
break;
case WM_DRM_LICENSE_STATE_FROM: // Action cannot be performed until a specific date
_tprintf( _T( "%sDRM_LICENSE_DATA.dwCategory: WM_DRM_LICENSE_STATE_FROM\n" ), tszOutputPrefix );
break;
case WM_DRM_LICENSE_STATE_UNTIL: // Action cannot be performed after a certain date
_tprintf( _T( "%sDRM_LICENSE_DATA.dwCategory: WM_DRM_LICENSE_STATE_UNTIL\n" ), tszOutputPrefix );
break;
case WM_DRM_LICENSE_STATE_FROM_UNTIL: // Action can only be performed within a specific range of dates
_tprintf( _T( "%sDRM_LICENSE_DATA.dwCategory: WM_DRM_LICENSE_STATE_FROM_UNTIL\n" ), tszOutputPrefix );
break;
case WM_DRM_LICENSE_STATE_COUNT_FROM: // Action can only be performed a certain number of times, starting from a specific date
_tprintf( _T( "%sDRM_LICENSE_DATA.dwCategory: WM_DRM_LICENSE_STATE_COUNT_FROM\n" ), tszOutputPrefix );
break;
case WM_DRM_LICENSE_STATE_COUNT_UNTIL: // Action can be performed a certain number of times until a specific date
_tprintf( _T( "%sDRM_LICENSE_DATA.dwCategory: WM_DRM_LICENSE_STATE_COUNT_UNTIL\n" ), tszOutputPrefix );
break;
case WM_DRM_LICENSE_STATE_COUNT_FROM_UNTIL: // Action can only be performed a certain number of times, and only is a specific range of dates
_tprintf( _T( "%sDRM_LICENSE_DATA.dwCategory: WM_DRM_LICENSE_STATE_COUNT_FROM_UNTIL\n" ), tszOutputPrefix );
break;
case WM_DRM_LICENSE_STATE_EXPIRATION_AFTER_FIRSTUSE: // License restrictions don't occur until after the first use
_tprintf( _T( "%sDRM_LICENSE_DATA.dwCategory: WM_DRM_LICENSE_STATE_EXPIRATION_AFTER_FIRSTUSE\n" ), tszOutputPrefix );
break;
default:
_tprintf( _T( "%sDRM_LICENSE_DATA.dwCategory: Unknown! - %d\n" ), tszOutputPrefix, pDRMLicenseStateData->dwCategory );
}
//
// If count limited, print the number of times the action can be performed
//
if ( 0 != pDRMLicenseStateData->dwNumCounts )
{
_tprintf( _T( "%sDRM_LICENSE_DATA.dwCount:" ), tszOutputPrefix );
for( dwCountIndex = 0; dwCountIndex < pDRMLicenseStateData->dwNumCounts; dwCountIndex++ )
{
_tprintf( _T( " %ld" ), pDRMLicenseStateData->dwCount[ dwCountIndex ] );
}
_tprintf( _T( "\n" ) );
}
//
// If the action is date limited, print the date restriction(s)
//
if ( 0 != pDRMLicenseStateData->dwNumDates )
{
_tprintf( _T( "%sDRM_LICENSE_DATA.datetime:" ), tszOutputPrefix );
for( dwDateIndex = 0; dwDateIndex < pDRMLicenseStateData->dwNumDates; dwDateIndex++ )
{
SYSTEMTIME SystemTime;
WCHAR wszOn[128];
WCHAR wszAt[128];
//
// Convert the FILETIME to SYSTEMTIME and format into strings
//
FileTimeToSystemTime( &(pDRMLicenseStateData->datetime[ dwDateIndex ]), &SystemTime );
GetDateFormatW( LOCALE_USER_DEFAULT, 0, &SystemTime, L"ddd',' MMM dd yyyy", wszOn, 128 );
GetTimeFormatW( LOCALE_USER_DEFAULT, 0, &SystemTime, L"hh mm ss tt", wszAt, 128 );
_tprintf( _T( " On %ws at %ws" ), wszOn, wszAt );
}
_tprintf( _T( "\n" ) );
}
//
// If the aggregate license data cannot easily be represented, the "vague" value will be set.
// This will happen when two or more licenses with different right types (like COUNT and
// and FROM_UNTIL) make it impossible to represent the information simply. For instance,
// if license 1 allows 5 plays and license 2 allows unlimited playback during the current
// month, then the user is guaranteed at least 5 plays, but possibly an unlimited number,
// if done within the current month.
//
_tprintf( _T( "%sDRM_LICENSE_DATA.dwVague: %ld \n" ), tszOutputPrefix, pDRMLicenseStateData->dwVague );
}
while ( FALSE );
return hr;
}
//------------------------------------------------------------------------------
// Name: CLicenseViewer::ShowRights()
// Desc: Show DRM rights properties.
//------------------------------------------------------------------------------
HRESULT CLicenseViewer::ShowRights()
{
HRESULT hr = S_OK;
DWORD dwPropertyIndex;
WMT_ATTR_DATATYPE wmtType;
BOOL fBoolProperty;
WORD cbLength;
WM_LICENSE_STATE_DATA LicenseStateData;
WCHAR wszTemp[500];
LPCWSTR wszPropertyName;
const DRM_Properties* pCurrentProperty;
LPBYTE pbProperty = NULL;
DWORD dwStateIndex;
//
// Validate current state
//
if ( !m_pIWMDRMReader )
{
_tprintf( _T( "ShowRights: m_pIWMDRMReader is NULL." ) );
return E_FAIL;
}
_tprintf( _T( "ShowRights 0x%08lX\n" ), hr );
//
// Print the boolean properties
//
_tprintf( _T( " Basic rights information:\n" ) );
for ( dwPropertyIndex = 0; dwPropertyIndex < NUM_BOOL_PROPERTIES; dwPropertyIndex++ )
{
pCurrentProperty = &(g_BoolProperties[ dwPropertyIndex ]);
//
// Get the next property from the DRM reader
//
wszPropertyName = pCurrentProperty->pwszName;
cbLength = sizeof( BOOL );
assert( m_pIWMDRMReader );
assert( pCurrentProperty );
hr = m_pIWMDRMReader->GetDRMProperty( wszPropertyName, &wmtType, (BYTE *)&fBoolProperty, &cbLength );
if ( FAILED( hr ) )
{
_tprintf( _T( " Couldn't get value for property %ws (hr = 0x%08lX)\n" ), wszPropertyName, hr );
hr = S_OK; // Reset the HRESULT, since it was "handled"
}
else
{
//
// Check to make sure the property is the expected type and size
//
if ( wmtType != pCurrentProperty->wmtExpectedType )
{
_tprintf( _T( " Invalid returned property type\n" ) );
continue;
}
if ( sizeof( BOOL ) != cbLength )
{
_tprintf( _T( " Invalid returned property length\n" ) );
continue;
}
//
// Print the value of the property
//
_tprintf( _T( " %ws: %Ls\n" ), wszPropertyName, fBoolProperty ? _T( "Yes" ) : _T( "No" ) );
}
}
//
// Get the license state properties
//
_tprintf( _T( " Content rights information:\n" ) );
for( dwPropertyIndex = 0; dwPropertyIndex < NUM_LICENSE_PROPERTIES; dwPropertyIndex++ )
{
pCurrentProperty = &(g_LicenseProperties[ dwPropertyIndex ]);
//
// Get property from DRM reader
//
cbLength = pCurrentProperty->cbExpectedLength;
assert( m_pIWMDRMReader );
assert( pCurrentProperty );
hr = m_pIWMDRMReader->GetDRMProperty( pCurrentProperty->pwszName, &wmtType, (BYTE *)&LicenseStateData, &cbLength );
if ( FAILED( hr ) )
{
_tprintf( _T( " Couldn't get value for property %ws (hr = 0x%08lX)\n" ), pCurrentProperty->pwszName, hr );
hr = S_OK; // Reset the HRESULT, since it was "handled"
}
else
{
//
// Check that the property type and size are the expected values
//
if ( wmtType != pCurrentProperty->wmtExpectedType )
{
_tprintf( _T( " Invalid returned property type\n" ) );
continue;
}
if ( cbLength != pCurrentProperty->cbExpectedLength )
{
_tprintf( _T( " Invalid returned property length\n" ) );
continue;
}
//
// Print each license state
//
_tprintf( _T( " %ws:\n" ), pCurrentProperty->pwszName );
for( dwStateIndex = 0; dwStateIndex < LicenseStateData.dwNumStates; dwStateIndex++ )
{
hr = PrintLicenseStateData( _T(" "), &(LicenseStateData.stateData[ dwStateIndex ]) );
if ( FAILED( hr ) )
{
break;
}
}
if ( FAILED( hr ) )
{
break;
}
}
}
//
// Test the Header properties
//
_tprintf( _T( " Header Properies:\n" ) );
for( dwPropertyIndex = 0; dwPropertyIndex < NUM_HEADER_PROPERTIES; dwPropertyIndex++ )
{
pCurrentProperty = &(g_HeaderProperties[ dwPropertyIndex ]);
//
// Get the property from the DRM reader
//
cbLength = sizeof( wszTemp );
assert( m_pIWMDRMReader );
assert( pCurrentProperty );
hr = m_pIWMDRMReader->GetDRMProperty( pCurrentProperty->pwszName, &wmtType, (BYTE *)&wszTemp[ 0 ], &cbLength );
if ( FAILED( hr ) )
{
_tprintf( _T( " Couldn't get value for property %ws (hr = 0x%08lX)\n" ), pCurrentProperty->pwszName, hr );
hr = S_OK; // Reset the HRESULT, since it was "handled"
}
else
{
//
// Check that the type returned is the expected type
//
if ( wmtType != pCurrentProperty->wmtExpectedType )
{
_tprintf( _T( " Invalid returned property type\n" ) );
continue;
}
//
// Print the value of the property
//
_tprintf( _T( " %ws: %ws\n" ), pCurrentProperty->pwszName, wszTemp );
}
}
SAFE_ARRAYDELETE( pbProperty );
return( hr );
}
//------------------------------------------------------------------------------
// Name: CLicenseViewer::Cleanup()
// Desc: Release resources.
//------------------------------------------------------------------------------
HRESULT CLicenseViewer::Cleanup()
{
HRESULT hr = S_OK;
//
// Release all resources held in member variables
//
SAFE_RELEASE( m_pIWMDRMReader );
SAFE_RELEASE( m_pIWMReader );
SAFE_CLOSEHANDLE( m_hEvent );
return( hr );
}