546 lines
15 KiB
C++
546 lines
15 KiB
C++
//////////////////////////////////////////////////////////////////////////
|
|
// ContentEnabler.cpp: Manages content enabler action.
|
|
//
|
|
// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF
|
|
// ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO
|
|
// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
|
|
// PARTICULAR PURPOSE.
|
|
//
|
|
// Copyright (c) Microsoft Corporation. All rights reserved.
|
|
//
|
|
//////////////////////////////////////////////////////////////////////////
|
|
|
|
#include "ProtectedPlayback.h"
|
|
#include "strsafe.h"
|
|
|
|
|
|
void LogEnableType(const GUID& guidEnableType);
|
|
void LogTrustStatus(MF_URL_TRUST_STATUS status);
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////
|
|
// Name: CreateInstance
|
|
// Description: Static class method to create the object.
|
|
//
|
|
// hNotify: Handle to the application window to receive notifications.
|
|
// ppManager: Receives an AddRef's pointer to the ContentProtectionManager
|
|
// object. The caller must release the pointer.
|
|
/////////////////////////////////////////////////////////////////////////
|
|
|
|
HRESULT ContentProtectionManager::CreateInstance(HWND hNotify, ContentProtectionManager **ppManager)
|
|
{
|
|
if (hNotify == NULL || ppManager == NULL)
|
|
{
|
|
return E_INVALIDARG;
|
|
}
|
|
|
|
HRESULT hr = S_OK;
|
|
|
|
ContentProtectionManager *pManager = new ContentProtectionManager(hNotify);
|
|
if (pManager == NULL)
|
|
{
|
|
hr = E_OUTOFMEMORY;
|
|
}
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
*ppManager = pManager;
|
|
(*ppManager)->AddRef();
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
/////////////////////////////////////////////////////////////////////////
|
|
// ContentProtectionManager constructor
|
|
/////////////////////////////////////////////////////////////////////////
|
|
|
|
ContentProtectionManager::ContentProtectionManager(HWND hNotify)
|
|
: m_nRefCount(0), m_pMEG(NULL), m_pResult(NULL), m_pEnabler(NULL), m_hwnd(hNotify),
|
|
m_state(Enabler_Ready), m_hrStatus(S_OK)
|
|
{
|
|
}
|
|
|
|
/////////////////////////////////////////////////////////////////////////
|
|
// ContentProtectionManager destructor
|
|
/////////////////////////////////////////////////////////////////////////
|
|
|
|
|
|
ContentProtectionManager::~ContentProtectionManager()
|
|
{
|
|
TRACE((L"~ContentEnabler\n"));
|
|
SAFE_RELEASE(m_pMEG);
|
|
SAFE_RELEASE(m_pResult);
|
|
SAFE_RELEASE(m_pEnabler);
|
|
}
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////
|
|
// AddRef
|
|
/////////////////////////////////////////////////////////////////////////
|
|
|
|
ULONG ContentProtectionManager::AddRef()
|
|
{
|
|
return InterlockedIncrement(&m_nRefCount);
|
|
}
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////
|
|
// Release
|
|
/////////////////////////////////////////////////////////////////////////
|
|
|
|
ULONG ContentProtectionManager::Release()
|
|
{
|
|
ULONG uCount = InterlockedDecrement(&m_nRefCount);
|
|
if (uCount == 0)
|
|
{
|
|
delete this;
|
|
}
|
|
// For thread safety, return a temporary variable.
|
|
return uCount;
|
|
}
|
|
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////
|
|
// QueryInterface
|
|
/////////////////////////////////////////////////////////////////////////
|
|
|
|
HRESULT ContentProtectionManager::QueryInterface(REFIID iid, void** ppv)
|
|
{
|
|
if (!ppv)
|
|
{
|
|
return E_POINTER;
|
|
}
|
|
if (iid == IID_IUnknown)
|
|
{
|
|
*ppv = static_cast<IUnknown*>(static_cast<IMFAsyncCallback*>(this));
|
|
}
|
|
else if (iid == IID_IMFAsyncCallback)
|
|
{
|
|
*ppv = static_cast<IMFAsyncCallback*>(this);
|
|
}
|
|
else if (iid == IID_IMFContentProtectionManager)
|
|
{
|
|
*ppv = static_cast<IMFContentProtectionManager*>(this);
|
|
}
|
|
else
|
|
{
|
|
*ppv = NULL;
|
|
return E_NOINTERFACE;
|
|
}
|
|
AddRef();
|
|
return S_OK;
|
|
}
|
|
|
|
|
|
// IMFContentProtectionManager methods
|
|
|
|
///////////////////////////////////////////////////////////////////////
|
|
// Name: BeginEnableContent
|
|
// Description: Called by the PMP session to start the enable action.
|
|
/////////////////////////////////////////////////////////////////////////
|
|
|
|
HRESULT ContentProtectionManager::BeginEnableContent(
|
|
IMFActivate *pEnablerActivate,
|
|
IMFTopology *pTopo,
|
|
IMFAsyncCallback *pCallback,
|
|
IUnknown *punkState
|
|
)
|
|
{
|
|
TRACE((L"ContentProtectionManager::BeginEnableContent"));
|
|
|
|
HRESULT hr = S_OK;
|
|
|
|
if (m_pEnabler != NULL)
|
|
{
|
|
TRACE((L"A previous call is still pending."));
|
|
return E_FAIL;
|
|
}
|
|
|
|
// Create an async result for later use.
|
|
hr = MFCreateAsyncResult(NULL, pCallback, punkState, &m_pResult);
|
|
LOG_MSG_IF_FAILED(L"MFCreateAsyncResult", hr);
|
|
|
|
// Create the enabler from the IMFActivate pointer.
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
hr = pEnablerActivate->ActivateObject(IID_IMFContentEnabler, (void**)&m_pEnabler);
|
|
LOG_MSG_IF_FAILED(L"ActivateObject", hr);
|
|
}
|
|
|
|
// Notify the application. The application will call DoEnable from the app thread.
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
m_state = Enabler_Ready; // Reset the state.
|
|
PostMessage(m_hwnd, WM_APP_CONTENT_ENABLER, 0, 0);
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////
|
|
// Name: EndEnableContent
|
|
// Description: Completes the enable action.
|
|
/////////////////////////////////////////////////////////////////////////
|
|
|
|
HRESULT ContentProtectionManager::EndEnableContent(IMFAsyncResult *pResult)
|
|
{
|
|
TRACE((L"ContentProtectionManager::EndEnableContent"));
|
|
|
|
|
|
if (pResult == NULL)
|
|
{
|
|
return E_POINTER;
|
|
}
|
|
|
|
// Release interfaces, so that we're ready to accept another call
|
|
// to BeginEnableContent.
|
|
SAFE_RELEASE(m_pResult);
|
|
SAFE_RELEASE(m_pEnabler);
|
|
SAFE_RELEASE(m_pMEG);
|
|
|
|
return m_hrStatus;
|
|
}
|
|
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////
|
|
// Name: Invoke
|
|
// Description: Callback for asynchronous BeginGetEvent method.
|
|
//
|
|
// pAsyncResult: Pointer to the result.
|
|
/////////////////////////////////////////////////////////////////////////
|
|
|
|
HRESULT ContentProtectionManager::Invoke(IMFAsyncResult *pAsyncResult)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
IMFMediaEvent* pEvent = NULL;
|
|
MediaEventType meType = MEUnknown; // Event type
|
|
PROPVARIANT varEventData; // Event data
|
|
|
|
PropVariantInit(&varEventData);
|
|
|
|
// Get the event from the event queue.
|
|
hr = m_pMEG->EndGetEvent(pAsyncResult, &pEvent);
|
|
LOG_MSG_IF_FAILED(L"IMediaEventGenerator::EndGetEvent", hr);
|
|
|
|
// Get the event type.
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
hr = pEvent->GetType(&meType);
|
|
}
|
|
|
|
// Get the event status. If the operation that triggered the event did
|
|
// not succeed, the status is a failure code.
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
hr = pEvent->GetStatus(&m_hrStatus);
|
|
}
|
|
|
|
// Get the event data.
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
hr = pEvent->GetValue(&varEventData);
|
|
}
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
// For the MEEnablerCompleted action, notify the application.
|
|
// Otehrwise, request another event.
|
|
TRACE((L"Content enabler event: %s", EventName(meType)));
|
|
|
|
if (meType == MEEnablerCompleted)
|
|
{
|
|
PostMessage(m_hwnd, WM_APP_CONTENT_ENABLER, 0, 0);
|
|
}
|
|
else
|
|
{
|
|
if (meType == MEEnablerProgress)
|
|
{
|
|
if (varEventData.vt == VT_LPWSTR)
|
|
{
|
|
TRACE((L"Progress: %s", varEventData.pwszVal));
|
|
}
|
|
}
|
|
m_pMEG->BeginGetEvent(this, NULL);
|
|
}
|
|
}
|
|
|
|
|
|
// Clean up.
|
|
PropVariantClear(&varEventData);
|
|
SAFE_RELEASE(pEvent);
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////
|
|
// Name: DoEnable
|
|
// Description: Does the enabler action.
|
|
//
|
|
// flags: If ForceNonSilent, then always use non-silent enable.
|
|
// Otherwise, use silent enable if possible.
|
|
////////////////////////////////////////////////////////////////////////
|
|
|
|
HRESULT ContentProtectionManager::DoEnable(EnablerFlags flags)
|
|
{
|
|
TRACE((L"ContentProtectionManager::DoEnable (flags =%d)", flags));
|
|
|
|
HRESULT hr = S_OK;
|
|
BOOL bAutomatic = FALSE;
|
|
GUID guidEnableType;
|
|
|
|
// Get the enable type. (Just for logging. We don't use it.)
|
|
hr = m_pEnabler->GetEnableType(&guidEnableType);
|
|
LOG_MSG_IF_FAILED(L"GetEnableType", hr);
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
LogEnableType(guidEnableType);
|
|
}
|
|
|
|
|
|
// Query for the IMFMediaEventGenerator interface so that we can get the
|
|
// enabler events.
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
hr = m_pEnabler->QueryInterface(IID_IMFMediaEventGenerator, (void**)&m_pMEG);
|
|
}
|
|
|
|
// Ask for the first event.
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
hr = m_pMEG->BeginGetEvent(this, NULL);
|
|
}
|
|
|
|
// Decide whether to use silent or non-silent enabling. If flags is ForceNonSilent,
|
|
// then we use non-silent. Otherwise, we query whether the enabler object supports
|
|
// silent enabling (also called "automatic" enabling).
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
if (flags == ForceNonSilent)
|
|
{
|
|
TRACE((L"Forcing non-silent enable."));
|
|
bAutomatic = FALSE;
|
|
}
|
|
else
|
|
{
|
|
hr = m_pEnabler->IsAutomaticSupported(&bAutomatic);
|
|
TRACE((L"IsAutomatic: auto = %d", bAutomatic));
|
|
}
|
|
}
|
|
|
|
// Start automatic or non-silent, depending.
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
if (bAutomatic)
|
|
{
|
|
m_state = Enabler_SilentInProgress;
|
|
TRACE((L"Content enabler: Automatic is supported"));
|
|
hr = m_pEnabler->AutomaticEnable();
|
|
}
|
|
else
|
|
{
|
|
m_state = Enabler_NonSilentInProgress;
|
|
TRACE((L"Content enabler: Using non-silent enabling"));
|
|
hr = DoNonSilentEnable();
|
|
}
|
|
}
|
|
|
|
if (FAILED(hr))
|
|
{
|
|
m_hrStatus = hr;
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////
|
|
// Name: CancelEnable
|
|
// Description: Cancels the current action.
|
|
//
|
|
// During silent enable, this cancels the enable action in progress.
|
|
// During non-silent enable, this cancels the MonitorEnable thread.
|
|
/////////////////////////////////////////////////////////////////////////
|
|
|
|
HRESULT ContentProtectionManager::CancelEnable()
|
|
{
|
|
HRESULT hr = S_OK;
|
|
if (m_state != Enabler_Complete)
|
|
{
|
|
hr = m_pEnabler->Cancel();
|
|
LOG_MSG_IF_FAILED(L"IMFContentEnabler::Cancel", hr);
|
|
|
|
if (FAILED(hr))
|
|
{
|
|
// If Cancel fails for some reason, queue the MEEnablerCompleted
|
|
// event ourselves. This will cause the current action to fail.
|
|
m_pMEG->QueueEvent(MEEnablerCompleted, GUID_NULL, hr, NULL);
|
|
}
|
|
}
|
|
return hr;
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////
|
|
// Name: CompleteEnable
|
|
// Description: Completes the current action.
|
|
//
|
|
// This method invokes the PMP session's callback.
|
|
/////////////////////////////////////////////////////////////////////////
|
|
|
|
HRESULT ContentProtectionManager::CompleteEnable()
|
|
{
|
|
m_state = Enabler_Complete;
|
|
|
|
// m_pResult can be NULL if the BeginEnableContent was not called.
|
|
// This is the case when the application initiates the enable action, eg
|
|
// when MFCreatePMPMediaSession fails and returns an IMFActivate pointer.
|
|
if (m_pResult)
|
|
{
|
|
TRACE((L"ContentProtectionManager: Invoking the pipeline's callback. (status = 0x%X)", m_hrStatus));
|
|
m_pResult->SetStatus(m_hrStatus);
|
|
MFInvokeCallback(m_pResult);
|
|
}
|
|
return S_OK;
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////
|
|
// Name: DoNonSilentEnable
|
|
// Description: Performs non-silent enable.
|
|
/////////////////////////////////////////////////////////////////////////
|
|
|
|
HRESULT ContentProtectionManager::DoNonSilentEnable()
|
|
{
|
|
|
|
// Trust status for the URL.
|
|
MF_URL_TRUST_STATUS trustStatus = MF_LICENSE_URL_UNTRUSTED;
|
|
|
|
WCHAR *sURL = NULL; // Enable URL
|
|
DWORD cchURL = 0; // Size of enable URL in characters.
|
|
|
|
BYTE *pPostData = NULL; // Buffer to hold HTTP POST data.
|
|
DWORD cbPostDataSize = 0; // Size of buffer, in bytes.
|
|
|
|
HRESULT hr = S_OK;
|
|
|
|
// Get the enable URL. This is where we get the enable data for non-silent enabling.
|
|
hr = m_pEnabler->GetEnableURL(&sURL, &cchURL, &trustStatus);
|
|
LOG_MSG_IF_FAILED(L"GetEnableURL", hr);
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
TRACE((L"Content enabler: URL = %s", sURL));
|
|
LogTrustStatus(trustStatus);
|
|
}
|
|
|
|
if (trustStatus != MF_LICENSE_URL_TRUSTED)
|
|
{
|
|
TRACE((L"The enabler URL is not trusted. Failing."));
|
|
hr = E_FAIL;
|
|
}
|
|
|
|
// Start the thread that monitors the non-silent enable action.
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
hr = m_pEnabler->MonitorEnable();
|
|
}
|
|
|
|
// Get the HTTP POST data
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
hr = m_pEnabler->GetEnableData(&pPostData, &cbPostDataSize);
|
|
LOG_MSG_IF_FAILED(L"GetEnableData", hr);
|
|
}
|
|
|
|
// Initialize the browser control.
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
hr = m_webHelper.Init((DispatchCallback*)this);
|
|
}
|
|
|
|
// Open the URL and send the HTTP POST data.
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
hr = m_webHelper.OpenURLWithData(sURL, pPostData, cbPostDataSize);
|
|
}
|
|
|
|
CoTaskMemFree(pPostData);
|
|
CoTaskMemFree(sURL);
|
|
|
|
return hr;
|
|
}
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////
|
|
// Name: OnDispatchInvoke
|
|
// Description: Called when browser control sends an event.
|
|
//
|
|
// dispIdMember: Dispatch ID from the DWebBrowserEvents2 interface.
|
|
/////////////////////////////////////////////////////////////////////////
|
|
|
|
void ContentProtectionManager::OnDispatchInvoke(DISPID dispIdMember)
|
|
{
|
|
if (dispIdMember == DISPID_ONQUIT)
|
|
{
|
|
// The user closed the browser window. Notify the application.
|
|
TRACE((TEXT("DISPID_ONQUIT")));
|
|
PostMessage(m_hwnd, WM_APP_BROWSER_DONE, 0, 0);
|
|
m_webHelper.Exit();
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void LogEnableType(const GUID& guidEnableType)
|
|
{
|
|
if (guidEnableType == MFENABLETYPE_WMDRMV1_LicenseAcquisition)
|
|
{
|
|
TRACE((L"MFENABLETYPE_WMDRMV1_LicenseAcquisition"));
|
|
}
|
|
else if (guidEnableType == MFENABLETYPE_WMDRMV7_LicenseAcquisition)
|
|
{
|
|
TRACE((L"MFENABLETYPE_WMDRMV7_LicenseAcquisition"));
|
|
}
|
|
else if (guidEnableType == MFENABLETYPE_WMDRMV7_Individualization)
|
|
{
|
|
TRACE((L"MFENABLETYPE_WMDRMV7_Individualization"));
|
|
}
|
|
else if (guidEnableType == MFENABLETYPE_MF_UpdateRevocationInformation)
|
|
{
|
|
TRACE((L"MFENABLETYPE_MF_UpdateRevocationInformation"));
|
|
}
|
|
else if (guidEnableType == MFENABLETYPE_MF_UpdateUntrustedComponent)
|
|
{
|
|
TRACE((L"MFENABLETYPE_MF_UpdateUntrustedComponent"));
|
|
}
|
|
else
|
|
{
|
|
TRACE((L"Unknown content enabler type."));
|
|
}
|
|
}
|
|
|
|
|
|
|
|
void LogTrustStatus(MF_URL_TRUST_STATUS status)
|
|
{
|
|
switch (status)
|
|
{
|
|
case MF_LICENSE_URL_UNTRUSTED:
|
|
TRACE((L"MF_LICENSE_URL_UNTRUSTED"));
|
|
break;
|
|
|
|
case MF_LICENSE_URL_TRUSTED:
|
|
TRACE((L"MF_LICENSE_URL_TRUSTED"));
|
|
break;
|
|
|
|
case MF_LICENSE_URL_TAMPERED:
|
|
TRACE((L"MF_LICENSE_URL_TAMPERED"));
|
|
break;
|
|
}
|
|
}
|