2025-04-27 07:49:33 -04:00

582 lines
16 KiB
C++

#include "precomp.h"
PassportLockedInteger PpNotificationThread::m_NextHandle;
//
// Constructor
//
PpNotificationThread::PpNotificationThread()
{
LocalConfigurationUpdated();
AddLocalConfigClient(dynamic_cast<IConfigurationUpdate*>(this), NULL);
}
//
// Destructor
//
PpNotificationThread::~PpNotificationThread()
{
}
//
// Add a CCD client to the notification list.
//
HRESULT
PpNotificationThread::AddCCDClient(
tstring& strCCDName,
ICCDUpdate* piUpdate,
HANDLE* phClientHandle)
{
HRESULT hr;
NOTIFICATION_CLIENT clientInfo;
clientInfo.dwNotificationType = NOTIF_CCD;
clientInfo.NotificationInterface.piCCDUpdate = piUpdate;
clientInfo.strCCDName = strCCDName;
clientInfo.hClientHandle = (HANDLE)(LONG_PTR)(++m_NextHandle);
{
PassportGuard<PassportLock> guard(m_ClientListLock);
m_ClientList.push_back(clientInfo);
}
if(phClientHandle != NULL)
{
*phClientHandle = clientInfo.hClientHandle;
}
hr = S_OK;
return hr;
}
//
// Add a configuration client to the notification list
//
HRESULT
PpNotificationThread::AddLocalConfigClient(
IConfigurationUpdate* piUpdate,
HANDLE* phClientHandle)
{
HRESULT hr;
NOTIFICATION_CLIENT clientInfo;
clientInfo.dwNotificationType = NOTIF_CONFIG;
clientInfo.NotificationInterface.piConfigUpdate = piUpdate;
clientInfo.hClientHandle = (HANDLE)(LONG_PTR)(++m_NextHandle);
{
PassportGuard<PassportLock> guard(m_ClientListLock);
m_ClientList.push_back(clientInfo);
}
if(phClientHandle != NULL)
{
*phClientHandle = clientInfo.hClientHandle;
}
hr = S_OK;
return hr;
}
//
// Remove a client (either type) from the notification list.
//
HRESULT
PpNotificationThread::RemoveClient(
HANDLE hClientHandle)
{
HRESULT hr;
PassportGuard<PassportLock> guard(m_ClientListLock);
for(CLIENT_LIST::iterator it = m_ClientList.begin(); it != m_ClientList.end(); it++)
{
if((*it).hClientHandle == hClientHandle)
{
m_ClientList.erase(it);
hr = S_OK;
goto Cleanup;
}
}
hr = E_FAIL;
Cleanup:
return hr;
}
//
// Do a manual refresh of a CCD.
//
HRESULT
PpNotificationThread::GetCCD(
tstring& strCCDName,
IXMLDocument** ppiXMLDocument,
BOOL bForceFetch)
{
HRESULT hr;
PpShadowDocument* pShadowDoc;
CCD_INFO ccdInfo;
{
PassportGuard<PassportLock> guard(m_CCDInfoLock);
// Get the CCD Information for the requested CCD
if(!GetCCDInfo(strCCDName, ccdInfo))
{
hr = E_INVALIDARG;
pShadowDoc = NULL;
goto Cleanup;
}
// Create a new shadow document for the CCD
if(ccdInfo.strCCDLocalFile.empty())
pShadowDoc = new PpShadowDocument(ccdInfo.strCCDURL);
else
pShadowDoc = new PpShadowDocument(ccdInfo.strCCDURL, ccdInfo.strCCDLocalFile);
}
if(!pShadowDoc)
{
hr = E_OUTOFMEMORY;
goto Cleanup;
}
// Do the update.
hr = pShadowDoc->GetDocument(ppiXMLDocument, bForceFetch);
//BUGBUG This is weird because currently other clients of this CCD will NOT get
// notified. I don't want to loop through the notification list here for
// two reasons:
//
// 1. The caller might be in the notification list and I don't to notify
// unnecessarily.
// 2. Don't want to put the overhead of notifying all clients on the
// caller's thread.
//
// The ideal solution would be to wake up our dedicated thread and have it
// do the notification. I haven't been able to find a way to signal a
// waitable time though.
Cleanup:
if(pShadowDoc != NULL)
delete pShadowDoc;
return hr;
}
void
PpNotificationThread::run(void)
{
HANDLE* pHandleArray = NULL;
LONG lResult;
PassportEvent RegChangeEvent(FALSE,FALSE);
DWORD dwCurCCDInfo;
DWORD dwCurArrayLen;
DWORD dwWaitResult;
DWORD dwError;
CCD_INFO_LIST::iterator it;
CRegKey PassportKey;
BOOL bKeyOpened;
HRESULT hr;
hr = CoInitializeEx(NULL, COINIT_MULTITHREADED);
_ASSERT(hr != S_FALSE);
lResult = PassportKey.Open(HKEY_LOCAL_MACHINE, TEXT("Software\\Microsoft\\Passport"), KEY_NOTIFY);
bKeyOpened = (lResult == ERROR_SUCCESS);
m_StartupThread.Set();
while(TRUE)
{
if(bKeyOpened)
{
lResult = RegNotifyChangeKeyValue((HKEY)PassportKey, TRUE,
REG_NOTIFY_CHANGE_NAME | REG_NOTIFY_CHANGE_LAST_SET,
(HANDLE)RegChangeEvent,
TRUE);
if(lResult != ERROR_SUCCESS)
dwError = GetLastError();
}
{
PassportGuard<PassportLock> guard(m_CCDInfoLock);
dwCurArrayLen = m_aciCCDInfoList.size() + 2;
pHandleArray = new HANDLE[dwCurArrayLen];
if(pHandleArray == NULL)
{
// BUGBUG Throw a low-memory alert here?
continue;
}
pHandleArray[0] = (HANDLE)m_ShutdownThread; // Handle 0 always contains the thread shutdown signal.
pHandleArray[1] = (HANDLE)RegChangeEvent; // Handle 1 always contains the registry change event.
for(it = m_aciCCDInfoList.begin(), dwCurCCDInfo = 0; it != m_aciCCDInfoList.end(); it++, dwCurCCDInfo++)
{
pHandleArray[dwCurCCDInfo + 2] = (*it).hCCDTimer;
}
}
dwWaitResult = WaitForMultipleObjects(dwCurArrayLen,
pHandleArray,
FALSE,
INFINITE);
switch(dwWaitResult)
{
case WAIT_FAILED:
dwError = GetLastError();
break;
// Thread shutdown has been signalled. Exit this thread.
case WAIT_OBJECT_0:
goto Cleanup;
// Registry change has been signalled. Notify all local config clients.
case WAIT_OBJECT_0 + 1:
{
PassportGuard<PassportLock> guard(m_ClientListLock);
for(CLIENT_LIST::iterator it = m_ClientList.begin(); it != m_ClientList.end(); it++)
{
if((*it).dwNotificationType == NOTIF_CONFIG)
{
(*it).NotificationInterface.piConfigUpdate->LocalConfigurationUpdated();
}
}
}
break;
// One of the CCD timers has been signalled. Read the CCD and notify all CCD clients.
default:
{
m_CCDInfoLock.acquire();
CCD_INFO_LIST aciTempCCDInfoList(m_aciCCDInfoList);
m_CCDInfoLock.release();
IXMLDocumentPtr xmlDoc;
PpShadowDocument ShadowDoc;
DWORD dwInfoIndex = dwWaitResult - WAIT_OBJECT_0 - 2;
m_aciCCDInfoList[dwInfoIndex].SetTimer();
// Fetch the CCD
ShadowDoc.SetURL(aciTempCCDInfoList[dwInfoIndex].strCCDURL);
if(!aciTempCCDInfoList[dwInfoIndex].strCCDLocalFile.empty())
ShadowDoc.SetLocalFile(aciTempCCDInfoList[dwInfoIndex].strCCDLocalFile);
if(ShadowDoc.GetDocument(&xmlDoc) == S_OK)
{
PassportGuard<PassportLock> guard(m_ClientListLock);
LPCTSTR pszUpdatedName = aciTempCCDInfoList[dwInfoIndex].strCCDName.c_str();
// Loop through client list and call any clients registered for the CCD that
// changed.
for(CLIENT_LIST::iterator it = m_ClientList.begin(); it != m_ClientList.end(); it++)
{
if(lstrcmpi(pszUpdatedName, (*it).strCCDName.c_str()) == 0)
{
(*it).NotificationInterface.piCCDUpdate->CCDUpdated(
pszUpdatedName,
(IXMLDocument*)xmlDoc);
}
}
}
}
break;
}
delete [] pHandleArray;
}
Cleanup:
if(pHandleArray != NULL)
delete [] pHandleArray;
CoUninitialize();
}
//
// Update our configuration. This is called from the constructor, and
// from the notification thread whenever the registry changes.
//
void
PpNotificationThread::LocalConfigurationUpdated()
{
CRegKey NexusRegKey;
LONG lResult;
DWORD dwIndex;
DWORD dwNameLen;
DWORD dwDefaultRefreshInterval;
TCHAR achNameBuf[64];
lResult = NexusRegKey.Open(HKEY_LOCAL_MACHINE,
TEXT("Software\\Microsoft\\Passport\\Nexus"),
KEY_READ);
if(lResult != ERROR_SUCCESS)
{
//BUGBUG This is a required reg key, throw an event.
return;
}
// Get the default refresh interval.
lResult = NexusRegKey.QueryDWORDValue(TEXT("CCDRefreshInterval"), dwDefaultRefreshInterval);
if(lResult != ERROR_SUCCESS)
{
//BUGBUG This is a required reg value, throw an event.
return;
}
//
// Lock down the list.
//
{
PassportGuard<PassportLock> guard(m_CCDInfoLock);
//
// Loop through existing list and remove any items whose corresponding keys
// have been removed.
//
CCD_INFO_LIST::iterator it;
for(it = m_aciCCDInfoList.begin(); it != m_aciCCDInfoList.end(); )
{
CRegKey CCDRegKey;
lResult = CCDRegKey.Open((HKEY)NexusRegKey, (*it).strCCDName.c_str(), KEY_READ);
if(lResult != ERROR_SUCCESS)
{
it = m_aciCCDInfoList.erase(it);
}
else
it++;
}
//
// Loop through each subkey and add/update the CCD info therein.
//
dwIndex = 0;
dwNameLen = sizeof(achNameBuf);
while(RegEnumKeyEx((HKEY)NexusRegKey, dwIndex,achNameBuf, &dwNameLen, NULL, NULL, NULL, NULL ) == ERROR_SUCCESS)
{
CRegKey CCDRegKey;
lResult = CCDRegKey.Open((HKEY)NexusRegKey, achNameBuf, KEY_READ);
if(lResult == ERROR_SUCCESS)
{
ReadCCDInfo(tstring(achNameBuf), dwDefaultRefreshInterval, CCDRegKey);
}
dwIndex++;
dwNameLen = sizeof(achNameBuf);
}
}
}
//
// This method starts the thread and then wait for the thread to get going.
//
bool
PpNotificationThread::start(void)
{
m_StartupThread.Reset();
bool bReturn = PassportThread::start();
//
// Now wait for the thread to start.
//
WaitForSingleObject((HANDLE)m_StartupThread, INFINITE);
return bReturn;
}
//
// This method just signals the shutdown event causing the thread to terminate immediately.
//
void
PpNotificationThread::stop(void)
{
m_ShutdownThread.Set();
}
//
// Private method for reading the CCD info for a single CCD subkey from
// the registry.
//
BOOL
PpNotificationThread::ReadCCDInfo(
tstring& strCCDName,
DWORD dwDefaultRefreshInterval,
CRegKey& CCDRegKey
)
{
BOOL bReturn = TRUE;
LONG lResult;
DWORD dwBufLen;
CCD_INFO_LIST::iterator it;
LPTSTR pszRemoteFile;
LPTSTR pszLocalFile;
DWORD dwCCDRefreshInterval;
//
// Read in the remote path to the CCD.
// CCDRemoteFile is the only required value. If it's not there, return FALSE.
//
lResult = CCDRegKey.QueryStringValue(TEXT("CCDRemoteFile"), NULL, &dwBufLen);
if(lResult == ERROR_SUCCESS)
{
pszRemoteFile = (LPTSTR)alloca(dwBufLen * sizeof(TCHAR));
CCDRegKey.QueryStringValue(TEXT("CCDRemoteFile"), pszRemoteFile, &dwBufLen);
while(*pszRemoteFile && _istspace(*pszRemoteFile)) pszRemoteFile++;
}
else
{
bReturn = FALSE;
goto Cleanup;
}
//
// Read in the refresh interval for this CCD.
//
lResult = CCDRegKey.QueryDWORDValue(TEXT("CCDRefreshInterval"), dwCCDRefreshInterval);
if(lResult != ERROR_SUCCESS)
dwCCDRefreshInterval = 0xFFFFFFFF;
//
// Read in local (backup) path for the CCD. This is an optional value.
//
lResult = CCDRegKey.QueryStringValue(TEXT("CCDLocalFile"), NULL, &dwBufLen);
if(lResult == ERROR_SUCCESS)
{
pszLocalFile = (LPTSTR)alloca(dwBufLen * sizeof(TCHAR));
CCDRegKey.QueryStringValue(TEXT("CCDLocalFile"), pszLocalFile, &dwBufLen);
while(*pszLocalFile && _istspace(*pszLocalFile)) pszLocalFile++;
}
else
{
pszLocalFile = (LPTSTR)alloca(sizeof(TCHAR));
*pszLocalFile = 0;
}
//
// If this CCD is already in the list, then update it.
//
for(it = m_aciCCDInfoList.begin(); it != m_aciCCDInfoList.end(); it++)
{
if(lstrcmp((*it).strCCDName.c_str(), strCCDName.c_str()) == 0)
{
// Check to see if the information has changed.
if(lstrcmpi(pszRemoteFile, (*it).strCCDURL.c_str()) != 0 ||
lstrcmpi(pszLocalFile, (*it).strCCDLocalFile.c_str()) != 0 ||
dwCCDRefreshInterval != (*it).dwCCDRefreshInterval ||
dwDefaultRefreshInterval != (*it).dwDefaultRefreshInterval
)
{
DWORD dwOldRefreshInterval = ((*it).dwCCDRefreshInterval == 0xFFFFFFFF ?
(*it).dwDefaultRefreshInterval :
(*it).dwCCDRefreshInterval);
DWORD dwNewRefreshInterval = (dwCCDRefreshInterval == 0xFFFFFFFF ?
dwDefaultRefreshInterval :
dwCCDRefreshInterval);
(*it).strCCDURL = pszRemoteFile;
(*it).strCCDLocalFile = pszLocalFile;
(*it).dwCCDRefreshInterval = dwCCDRefreshInterval;
(*it).dwDefaultRefreshInterval = dwDefaultRefreshInterval;
if(dwOldRefreshInterval != dwNewRefreshInterval)
(*it).SetTimer();
}
break;
}
}
//
// This is a new CCD, add it to the list.
//
if(it == m_aciCCDInfoList.end())
{
CCD_INFO ccdInfo;
ccdInfo.strCCDName = strCCDName;
ccdInfo.strCCDURL = pszRemoteFile;
ccdInfo.strCCDLocalFile = pszLocalFile;
ccdInfo.dwCCDRefreshInterval = dwCCDRefreshInterval;
ccdInfo.dwDefaultRefreshInterval = dwDefaultRefreshInterval;
ccdInfo.SetTimer();
m_aciCCDInfoList.push_back(ccdInfo);
}
bReturn = TRUE;
Cleanup:
return bReturn;
}
//
// Private method for retrieving a CCD_INFO structure given the CCD name.
//
BOOL
PpNotificationThread::GetCCDInfo(
tstring& strCCDName,
CCD_INFO& ccdInfo
)
{
CCD_INFO_LIST::iterator it;
for(it = m_aciCCDInfoList.begin(); it != m_aciCCDInfoList.end(); it++)
{
if(lstrcmpi((*it).strCCDName.c_str(), strCCDName.c_str()) == 0)
{
ccdInfo = (*it);
return TRUE;
}
}
return FALSE;
}