969 lines
22 KiB
C++
969 lines
22 KiB
C++
//=======================================================================
|
|
//
|
|
// Copyright (c) 1998-2000 Microsoft Corporation. All Rights Reserved.
|
|
//
|
|
// File: History.CPP
|
|
// Author: Charles Ma, 10/13/2000
|
|
//
|
|
// Revision History:
|
|
//
|
|
//
|
|
//
|
|
// Description:
|
|
//
|
|
// Class to handle history log
|
|
//
|
|
//=======================================================================
|
|
|
|
|
|
#include "iuengine.h"
|
|
#include <iucommon.h>
|
|
#include <fileutil.h>
|
|
#include <StringUtil.h>
|
|
#include <shlwapi.h> // for PathAppend() API
|
|
#include "history.h"
|
|
|
|
const TCHAR C_V3_LOG_FILE[] = _T("wuhistv3.log");
|
|
const TCHAR C_LOG_FILE[] = _T("iuhist.xml");
|
|
const TCHAR C_LOG_FILE_CORP[] = _T("iuhist_catalog.xml");
|
|
const TCHAR C_LOG_FILE_CORP_ADMIN[] = _T("iuhist_catalogAdmin.xml");
|
|
const OLECHAR C_IU_CORP_SITE[] = L"IU_CORP_SITE";
|
|
const OLECHAR C_HISTORICALSPEED[] = L"GetHistoricalSpeed";
|
|
|
|
//
|
|
// we use a global mutex name to let all clients, including services
|
|
// on terminal servers gain exclusive access for updating history on disk
|
|
//
|
|
#if defined(UNICODE) || defined(_UNICODE)
|
|
const TCHAR C_MUTEX_NAME[] = _T("Global\\6D7495AB-399E-4768-89CC-9444202E8412");
|
|
#else
|
|
const TCHAR C_MUTEX_NAME[] = _T("6D7495AB-399E-4768-89CC-9444202E8412");
|
|
#endif
|
|
|
|
#define CanSaveHistory (NULL != m_hMutex)
|
|
#define ReturnFailedAllocSetHrMsg(x) {if (NULL == (x)) {hr = E_OUTOFMEMORY; LOG_ErrorMsg(hr); return hr;}}
|
|
|
|
|
|
|
|
CIUHistory::CIUHistory()
|
|
: m_pszDownloadBasePath(NULL),
|
|
m_bstrCurrentClientName(NULL)
|
|
{
|
|
LOG_Block("CIUHisotry::CIUHistory()");
|
|
|
|
m_pxmlExisting = new CXmlItems(TRUE);
|
|
m_pxmlDownload = new CXmlItems(FALSE);
|
|
m_pxmlInstall = new CXmlItems(FALSE);
|
|
|
|
m_hMutex = CreateMutex(
|
|
NULL, // no security descriptor
|
|
FALSE, // mutex object not owned, yet
|
|
C_MUTEX_NAME
|
|
);
|
|
if (NULL == m_hMutex)
|
|
{
|
|
DWORD dwErr = GetLastError();
|
|
LOG_ErrorMsg(dwErr);
|
|
m_ErrorCode = HRESULT_FROM_WIN32(GetLastError());
|
|
}
|
|
else
|
|
{
|
|
LOG_Out(_T("Mutex created okay"));
|
|
m_ErrorCode = S_OK;
|
|
}
|
|
|
|
m_fSavePending = FALSE;
|
|
}
|
|
|
|
|
|
|
|
CIUHistory::~CIUHistory()
|
|
{
|
|
if (m_fSavePending)
|
|
{
|
|
SaveHistoryToDisk();
|
|
}
|
|
|
|
if (CanSaveHistory)
|
|
{
|
|
CloseHandle(m_hMutex);
|
|
}
|
|
|
|
if (NULL != m_pxmlExisting)
|
|
{
|
|
delete m_pxmlExisting;
|
|
}
|
|
|
|
if (NULL != m_pxmlDownload)
|
|
{
|
|
delete m_pxmlDownload;
|
|
}
|
|
|
|
if (NULL != m_pxmlInstall)
|
|
{
|
|
delete m_pxmlInstall;
|
|
}
|
|
|
|
SafeHeapFree(m_pszDownloadBasePath);
|
|
SysFreeString(m_bstrCurrentClientName);
|
|
}
|
|
|
|
|
|
|
|
// ------------------------------------------------------------------
|
|
//
|
|
// public function SetDownloadBasePath()
|
|
// this function should be called before AddHistoryItemDownloadStatus()
|
|
// for corporate case to set the download path that the user has input,
|
|
// so that we know where to save the history log.
|
|
//
|
|
// ------------------------------------------------------------------
|
|
HRESULT CIUHistory::SetDownloadBasePath(LPCTSTR pszDownloadedBasePath)
|
|
{
|
|
LOG_Block("SetDownloadBasePath()");
|
|
|
|
if (NULL != pszDownloadedBasePath)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
|
|
if (NULL != m_pszDownloadBasePath)
|
|
{
|
|
//
|
|
// most likely user called SetDownloadBasePath() at least twice
|
|
// within the same instance of this class
|
|
//
|
|
SafeHeapFree(m_pszDownloadBasePath);
|
|
}
|
|
|
|
|
|
m_pszDownloadBasePath = (LPTSTR)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, MAX_PATH * sizeof (TCHAR));
|
|
if (NULL == m_pszDownloadBasePath)
|
|
{
|
|
LOG_ErrorMsg(E_OUTOFMEMORY);
|
|
return E_OUTOFMEMORY;
|
|
}
|
|
|
|
hr = StringCchCopyEx(m_pszDownloadBasePath, MAX_PATH, pszDownloadedBasePath,
|
|
NULL, NULL, MISTSAFE_STRING_FLAGS);
|
|
if (FAILED(hr))
|
|
{
|
|
SafeHeapFree(m_pszDownloadBasePath);
|
|
LOG_ErrorMsg(hr);
|
|
return hr;
|
|
}
|
|
|
|
BSTR bstrCorpSite = SysAllocString(C_IU_CORP_SITE);
|
|
SetClientName(bstrCorpSite);
|
|
SafeSysFreeString(bstrCorpSite);
|
|
}
|
|
return S_OK;
|
|
}
|
|
|
|
|
|
|
|
// ------------------------------------------------------------------
|
|
//
|
|
// public function AddHistoryItemDownloadStatus()
|
|
// this function should be called when you want to record the
|
|
// download status of this item. A new history item will be
|
|
// added to the history file
|
|
//
|
|
// ------------------------------------------------------------------
|
|
HRESULT CIUHistory::AddHistoryItemDownloadStatus(
|
|
CXmlCatalog* pCatalog,
|
|
HANDLE_NODE hCatalogItem, // a handle points to node in catalog
|
|
_HISTORY_STATUS enDownloadStatus,
|
|
LPCTSTR lpcszDownloadedTo,
|
|
LPCTSTR lpcszClient,
|
|
DWORD dwErrorCode /*= 0*/
|
|
)
|
|
{
|
|
LOG_Block("AddHistoryItemDownloadStatus()");
|
|
|
|
HRESULT hr = S_OK;
|
|
|
|
HANDLE_NODE hDownloadItem = HANDLE_NODE_INVALID;
|
|
|
|
ReturnFailedAllocSetHrMsg(m_pxmlDownload);
|
|
|
|
if (NULL == lpcszClient || _T('\0') == lpcszClient[0])
|
|
{
|
|
hr = E_INVALIDARG;
|
|
LOG_ErrorMsg(hr);
|
|
return hr;
|
|
}
|
|
|
|
if (!CanSaveHistory)
|
|
{
|
|
return m_ErrorCode;
|
|
}
|
|
|
|
BSTR bstrDownloadedTo = NULL;
|
|
BSTR bstrClient = T2BSTR(lpcszClient);
|
|
BSTR bstrDownloadStatus = GetBSTRStatus(enDownloadStatus);
|
|
|
|
//
|
|
// append a new node
|
|
//
|
|
hr = m_pxmlDownload->AddItem(pCatalog, hCatalogItem, &hDownloadItem);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
m_pxmlDownload->AddTimeStamp(hDownloadItem);
|
|
if (0 != dwErrorCode)
|
|
{
|
|
m_pxmlDownload->AddDownloadStatus(hDownloadItem, bstrDownloadStatus, dwErrorCode);
|
|
}
|
|
else
|
|
{
|
|
m_pxmlDownload->AddDownloadStatus(hDownloadItem, bstrDownloadStatus);
|
|
}
|
|
|
|
bstrDownloadedTo = T2BSTR(lpcszDownloadedTo);
|
|
m_pxmlDownload->AddDownloadPath(hDownloadItem, bstrDownloadedTo);
|
|
m_pxmlDownload->AddClientInfo(hDownloadItem, bstrClient);
|
|
m_pxmlDownload->CloseItem(hDownloadItem);
|
|
|
|
m_fSavePending = TRUE;
|
|
}
|
|
|
|
SetClientName(bstrClient);
|
|
SysFreeString(bstrDownloadedTo);
|
|
SysFreeString(bstrClient);
|
|
SysFreeString(bstrDownloadStatus);
|
|
return hr;
|
|
}
|
|
|
|
|
|
|
|
// ------------------------------------------------------------------
|
|
//
|
|
// public function AddHistoryItemInstallStatus()
|
|
// this function should be called when you want to record the
|
|
// install status of this item. This function will go to the
|
|
// existing history tree and find the first item that matches
|
|
// the identity of hCatalogItem, and assume that one as
|
|
// the one you want to modify the install status
|
|
//
|
|
//
|
|
// return:
|
|
// HRESULT - S_OK if succeeded
|
|
// - E_HANDLE if can't find hCatalogItem from
|
|
// the current history log tree
|
|
// - or other HRESULT error
|
|
//
|
|
// ------------------------------------------------------------------
|
|
HRESULT CIUHistory::AddHistoryItemInstallStatus(
|
|
CXmlCatalog* pCatalog,
|
|
HANDLE_NODE hCatalogItem, // a handle points to node in catalog
|
|
_HISTORY_STATUS enInstallStatus,
|
|
LPCTSTR lpcszClient,
|
|
BOOL fNeedsReboot,
|
|
DWORD dwErrorCode /*= 0*/
|
|
)
|
|
{
|
|
LOG_Block("AddHistoryItemInstallStatus()");
|
|
|
|
HRESULT hr = S_OK;
|
|
|
|
HANDLE_NODE hInstallItem = HANDLE_NODE_INVALID;
|
|
|
|
ReturnFailedAllocSetHrMsg(m_pxmlInstall);
|
|
|
|
if (!CanSaveHistory)
|
|
{
|
|
return m_ErrorCode;
|
|
}
|
|
|
|
BSTR bstrClient = NULL;
|
|
BSTR bstrInstallStatus = GetBSTRStatus(enInstallStatus);
|
|
//
|
|
// append a new node
|
|
//
|
|
hr = m_pxmlInstall->AddItem(pCatalog, hCatalogItem, &hInstallItem);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
m_pxmlInstall->AddTimeStamp(hInstallItem);
|
|
if (0 != dwErrorCode)
|
|
{
|
|
m_pxmlInstall->AddInstallStatus(hInstallItem, bstrInstallStatus, fNeedsReboot, dwErrorCode);
|
|
}
|
|
else
|
|
{
|
|
m_pxmlInstall->AddInstallStatus(hInstallItem, bstrInstallStatus, fNeedsReboot);
|
|
}
|
|
bstrClient = T2BSTR(lpcszClient);
|
|
m_pxmlInstall->AddClientInfo(hInstallItem, bstrClient);
|
|
m_pxmlInstall->CloseItem(hInstallItem);
|
|
|
|
m_fSavePending = TRUE;
|
|
}
|
|
|
|
SysFreeString(bstrClient);
|
|
SysFreeString(bstrInstallStatus);
|
|
return hr;
|
|
}
|
|
|
|
|
|
|
|
// ------------------------------------------------------------------
|
|
//
|
|
// public function UpdateHistoryItemInstallStatus()
|
|
// this function should be called when you want to record the
|
|
// install status of this item. This function will go to the
|
|
// existing history tree and find the first item that matches
|
|
// the identity of hCatalogItem, and assume that one as
|
|
// the one you want to modify the install status
|
|
//
|
|
//
|
|
// return:
|
|
// HRESULT - S_OK if succeeded
|
|
// - E_HANDLE if can't find hCatalogItem from
|
|
// the current history log tree
|
|
// - or other HRESULT error
|
|
//
|
|
// ------------------------------------------------------------------
|
|
HRESULT CIUHistory::UpdateHistoryItemInstallStatus(
|
|
CXmlCatalog* pCatalog,
|
|
HANDLE_NODE hCatalogItem, // a handle points to node in catalog
|
|
_HISTORY_STATUS enInstallStatus,
|
|
BOOL fNeedsReboot,
|
|
DWORD dwErrorCode /*= 0*/
|
|
)
|
|
{
|
|
LOG_Block("UpdateHistoryItemInstallStatus()");
|
|
|
|
HRESULT hr = S_OK;
|
|
|
|
HANDLE_NODE hInstallItem = HANDLE_NODE_INVALID;
|
|
|
|
ReturnFailedAllocSetHrMsg(m_pxmlInstall);
|
|
|
|
if (!CanSaveHistory)
|
|
{
|
|
return m_ErrorCode;
|
|
}
|
|
|
|
BSTR bstrInstallStatus = GetBSTRStatus(enInstallStatus);
|
|
//
|
|
// append a new node
|
|
//
|
|
hr = m_pxmlInstall->FindItem(pCatalog, hCatalogItem, &hInstallItem);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
m_pxmlInstall->AddTimeStamp(hInstallItem);
|
|
if (0 != dwErrorCode)
|
|
{
|
|
m_pxmlInstall->UpdateItemInstallStatus(hInstallItem, bstrInstallStatus, fNeedsReboot, dwErrorCode);
|
|
}
|
|
else
|
|
{
|
|
m_pxmlInstall->UpdateItemInstallStatus(hInstallItem, bstrInstallStatus, fNeedsReboot);
|
|
}
|
|
m_pxmlInstall->CloseItem(hInstallItem);
|
|
|
|
m_fSavePending = TRUE;
|
|
}
|
|
|
|
SysFreeString(bstrInstallStatus);
|
|
return hr;
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
// ------------------------------------------------------------------
|
|
//
|
|
// public function RetrieveItemDownloadPath()
|
|
// this function will go to the existing history tree and find
|
|
// the first item that matches the identity of hCatalogItem, and
|
|
// assume that's the one you want to retrieve the download path from
|
|
//
|
|
// return:
|
|
// HRESULT - S_OK if succeeded
|
|
// - E_HANDLE if can't find hCatalogItem from
|
|
// the current history log tree
|
|
// - or other HRESULT error
|
|
//
|
|
// ------------------------------------------------------------------
|
|
HRESULT CIUHistory::RetrieveItemDownloadPath(
|
|
CXmlCatalog* pCatalog,
|
|
HANDLE_NODE hCatalogItem, // a handle points to node in catalog
|
|
BSTR* pbstrDownloadPath
|
|
)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
|
|
if (NULL == m_Existing.'DocumentPtr())
|
|
{
|
|
//
|
|
// need to read the existing history
|
|
//
|
|
WaitForSingleObject(m_hMutex, INFINITE);
|
|
|
|
hr = ReadHistoryFromDisk(NULL);
|
|
if (FAILED(hr))
|
|
{
|
|
//
|
|
// if we can't load the existing history
|
|
// we can't do anything here
|
|
//
|
|
ReleaseMutex(m_hMutex);
|
|
return hr;
|
|
}
|
|
|
|
ReleaseMutex(m_hMutex);
|
|
}
|
|
|
|
hr = m_Existing.GetItemDownloadPath(pCatalog, hCatalogItem, pbstrDownloadPath);
|
|
return hr;
|
|
}
|
|
*/
|
|
|
|
|
|
// ------------------------------------------------------------------
|
|
//
|
|
// public function ReadHistoryFromDisk()
|
|
// this function will read the history from the given file
|
|
//
|
|
// if the file path is NULL, assumes default IU log file locally
|
|
//
|
|
// ------------------------------------------------------------------
|
|
HRESULT CIUHistory::ReadHistoryFromDisk(LPCTSTR lpszLogFile, BOOL fCorpAdmin /*= FALSE*/)
|
|
{
|
|
LOG_Block("ReadHistoryFromDisk()");
|
|
|
|
HRESULT hr = S_OK;
|
|
TCHAR szLogPath[MAX_PATH];
|
|
|
|
ReturnFailedAllocSetHrMsg(m_pxmlExisting);
|
|
|
|
//
|
|
// check to see if we use designated path (comsumer)
|
|
// or user-specified path (corporate)
|
|
//
|
|
if ((NULL == lpszLogFile || _T('\0') == lpszLogFile[0]) && !fCorpAdmin)
|
|
{
|
|
GetIndustryUpdateDirectory(szLogPath);
|
|
hr = PathCchAppend(szLogPath, ARRAYSIZE(szLogPath), C_LOG_FILE);
|
|
if (FAILED(hr))
|
|
{
|
|
LOG_ErrorMsg(hr);
|
|
return hr;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// this is corporate case to read log file from
|
|
// a server location
|
|
//
|
|
if (fCorpAdmin)
|
|
{
|
|
GetIndustryUpdateDirectory(szLogPath);
|
|
hr = PathCchAppend(szLogPath, ARRAYSIZE(szLogPath), C_LOG_FILE_CORP_ADMIN);
|
|
if (FAILED(hr))
|
|
{
|
|
LOG_ErrorMsg(hr);
|
|
return hr;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
hr = StringCchCopyEx(szLogPath, ARRAYSIZE(szLogPath), lpszLogFile,
|
|
NULL, NULL, MISTSAFE_STRING_FLAGS);
|
|
if (FAILED(hr))
|
|
{
|
|
LOG_ErrorMsg(hr);
|
|
return hr;
|
|
}
|
|
|
|
hr = PathCchAppend(szLogPath, ARRAYSIZE(szLogPath), C_LOG_FILE_CORP);
|
|
if (FAILED(hr))
|
|
{
|
|
LOG_ErrorMsg(hr);
|
|
return hr;
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// if we are not passing in the class file path buffer,
|
|
// then update the class path buffer with this new path
|
|
//
|
|
if (szLogPath != m_szLogFilePath)
|
|
{
|
|
hr = StringCchCopyEx(m_szLogFilePath, ARRAYSIZE(m_szLogFilePath), szLogPath,
|
|
NULL, NULL, MISTSAFE_STRING_FLAGS);
|
|
if (FAILED(hr))
|
|
{
|
|
LOG_ErrorMsg(hr);
|
|
return hr;
|
|
}
|
|
}
|
|
|
|
//
|
|
// load the xml file
|
|
//
|
|
m_pxmlExisting->Clear();
|
|
|
|
BSTR bstrLogPath = T2BSTR(szLogPath);
|
|
hr = m_pxmlExisting->LoadXMLDocumentFile(bstrLogPath);
|
|
SysFreeString(bstrLogPath);
|
|
|
|
return hr;
|
|
}
|
|
|
|
|
|
|
|
// ------------------------------------------------------------------
|
|
//
|
|
// public function SaveHistoryToDisk()
|
|
// this function will re-read the history in exclusive mode, and
|
|
// merge the newly added data to the tree (so we don't overwrite
|
|
// new changes made by other instances of this control) and
|
|
// write it back
|
|
//
|
|
// ------------------------------------------------------------------
|
|
HRESULT CIUHistory::SaveHistoryToDisk(void)
|
|
{
|
|
LOG_Block("SaveHistoryToDisk()");
|
|
|
|
HRESULT hr = S_OK, hr2 = S_OK;
|
|
BSTR bstrLogFilePath = NULL;
|
|
|
|
ReturnFailedAllocSetHrMsg(m_pxmlExisting);
|
|
ReturnFailedAllocSetHrMsg(m_pxmlDownload);
|
|
ReturnFailedAllocSetHrMsg(m_pxmlInstall);
|
|
|
|
if (!CanSaveHistory)
|
|
{
|
|
return m_ErrorCode;
|
|
}
|
|
|
|
if (!m_fSavePending)
|
|
{
|
|
//
|
|
// nothing to save
|
|
//
|
|
return S_OK;
|
|
}
|
|
|
|
//
|
|
// first, we need to gain exclusive access
|
|
// to the log file before reading it
|
|
//
|
|
// since this is not a long process, so I
|
|
// don't think we need to take care of WM_QUIT
|
|
// message
|
|
//
|
|
WaitForSingleObject(m_hMutex, INFINITE);
|
|
|
|
BSTR bstrCorpSite = SysAllocString(C_IU_CORP_SITE);
|
|
ReturnFailedAllocSetHrMsg(bstrCorpSite);
|
|
|
|
if (!CompareBSTRsEqual(bstrCorpSite, m_bstrCurrentClientName))
|
|
{
|
|
SysFreeString(bstrCorpSite);
|
|
//
|
|
// re-read history file
|
|
//
|
|
hr = ReadHistoryFromDisk(NULL);
|
|
|
|
//
|
|
// comment out...if we get failure on reading,
|
|
// we recreate a new history file later when saving.
|
|
//
|
|
//if (FAILED(hr))
|
|
//{
|
|
// //
|
|
// // if we can't load the existing history
|
|
// // we can't do anything here
|
|
// //
|
|
// ReleaseMutex(m_hMutex);
|
|
// return hr;
|
|
//}
|
|
|
|
//
|
|
// merge changes:
|
|
//
|
|
// loop through m_Download, insert each node to top of m_Existing
|
|
//
|
|
hr = m_pxmlExisting->MergeItemDownloaded(m_pxmlDownload);
|
|
if (FAILED(hr))
|
|
{
|
|
ReleaseMutex(m_hMutex);
|
|
return hr;
|
|
}
|
|
|
|
//
|
|
// loop through m_Install, for each node in m_Install
|
|
// find the one in m_Existing, update install status
|
|
//
|
|
hr = m_pxmlExisting->UpdateItemInstalled(m_pxmlInstall);
|
|
if (FAILED(hr))
|
|
{
|
|
ReleaseMutex(m_hMutex);
|
|
return hr;
|
|
}
|
|
|
|
//
|
|
// save the xml file
|
|
//
|
|
bstrLogFilePath = T2BSTR(m_szLogFilePath);
|
|
hr = m_pxmlExisting->SaveXMLDocument(bstrLogFilePath);
|
|
SafeSysFreeString(bstrLogFilePath);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
m_fSavePending = FALSE;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// this is the corporate case...
|
|
//
|
|
SysFreeString(bstrCorpSite);
|
|
if (NULL != m_pszDownloadBasePath && _T('\0') != m_pszDownloadBasePath[0])
|
|
{
|
|
//
|
|
// re-read corp history from download base folder
|
|
//
|
|
ReadHistoryFromDisk(m_pszDownloadBasePath);
|
|
|
|
//
|
|
// merge new items downloaded
|
|
//
|
|
hr = m_pxmlExisting->MergeItemDownloaded(m_pxmlDownload);
|
|
if (FAILED(hr))
|
|
{
|
|
ReleaseMutex(m_hMutex);
|
|
return hr;
|
|
}
|
|
|
|
//
|
|
// save the xml file
|
|
//
|
|
bstrLogFilePath = T2BSTR(m_szLogFilePath);
|
|
hr = m_pxmlExisting->SaveXMLDocument(bstrLogFilePath);
|
|
SafeSysFreeString(bstrLogFilePath);
|
|
}
|
|
//
|
|
// re-read corp admin history from windowsupdate folder
|
|
//
|
|
ReadHistoryFromDisk(m_pszDownloadBasePath, TRUE);
|
|
|
|
//
|
|
// merge new items downloaded
|
|
//
|
|
hr2 = m_pxmlExisting->MergeItemDownloaded(m_pxmlDownload);
|
|
if (FAILED(hr2))
|
|
{
|
|
ReleaseMutex(m_hMutex);
|
|
return hr2;
|
|
}
|
|
|
|
//
|
|
// save the xml file
|
|
//
|
|
bstrLogFilePath = T2BSTR(m_szLogFilePath);
|
|
hr2 = m_pxmlExisting->SaveXMLDocument(bstrLogFilePath);
|
|
SafeSysFreeString(bstrLogFilePath);
|
|
if (SUCCEEDED(hr) && SUCCEEDED(hr2))
|
|
{
|
|
m_fSavePending = FALSE;
|
|
}
|
|
}
|
|
|
|
ReleaseMutex(m_hMutex);
|
|
SysFreeString(bstrLogFilePath);
|
|
hr = SUCCEEDED(hr) ? hr2 : hr;
|
|
return hr;
|
|
}
|
|
|
|
|
|
|
|
// ------------------------------------------------------------------
|
|
//
|
|
// public function to set the client name
|
|
//
|
|
// a client name is used to put in history to denode who
|
|
// caused download/install happened.
|
|
//
|
|
// ------------------------------------------------------------------
|
|
void CIUHistory::SetClientName(BSTR bstrClientName)
|
|
{
|
|
if (NULL != m_bstrCurrentClientName)
|
|
{
|
|
SysFreeString(m_bstrCurrentClientName);
|
|
m_bstrCurrentClientName = NULL;
|
|
}
|
|
if (NULL != bstrClientName)
|
|
{
|
|
m_bstrCurrentClientName = SysAllocString(bstrClientName);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
// ------------------------------------------------------------------
|
|
//
|
|
// public function GetHistory
|
|
//
|
|
// read the current history XML file and convert it
|
|
// into bstr to pass out
|
|
//
|
|
// ------------------------------------------------------------------
|
|
HRESULT CIUHistory::GetHistoryStr(
|
|
LPCTSTR lpszLogFile,
|
|
BSTR BeginDateTime,
|
|
BSTR EndDateTime,
|
|
BSTR* pbstrHistory)
|
|
{
|
|
LOG_Block("GetHistoryStr()");
|
|
|
|
HRESULT hr = S_OK;
|
|
|
|
ReturnFailedAllocSetHrMsg(m_pxmlExisting);
|
|
|
|
//
|
|
// need to read the existing history
|
|
//
|
|
WaitForSingleObject(m_hMutex, INFINITE);
|
|
|
|
BSTR bstrCorpSite = SysAllocString(C_IU_CORP_SITE);
|
|
if (bstrCorpSite == NULL)
|
|
{
|
|
hr = E_OUTOFMEMORY;
|
|
goto done;
|
|
}
|
|
|
|
if (CompareBSTRsEqual(bstrCorpSite, m_bstrCurrentClientName))
|
|
{
|
|
TCHAR szLogPath[MAX_PATH];
|
|
TCHAR szLogFileParam[MAX_PATH];
|
|
|
|
if (lpszLogFile != NULL && lpszLogFile[0] != _T('\0'))
|
|
{
|
|
hr = StringCchCopyEx(szLogFileParam, ARRAYSIZE(szLogFileParam), lpszLogFile,
|
|
NULL, NULL, MISTSAFE_STRING_FLAGS);
|
|
if (FAILED(hr))
|
|
{
|
|
LOG_ErrorMsg(hr);
|
|
goto done;
|
|
}
|
|
|
|
hr = PathCchAddBackslash(szLogFileParam, ARRAYSIZE(szLogFileParam));
|
|
if (FAILED(hr))
|
|
{
|
|
LOG_ErrorMsg(hr);
|
|
goto done;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
szLogFileParam[0] = _T('\0');
|
|
}
|
|
|
|
//
|
|
// corporate case
|
|
//
|
|
GetIndustryUpdateDirectory(szLogPath);
|
|
if (_T('\0') == szLogFileParam[0] || !lstrcmpi(szLogPath, szLogFileParam))
|
|
{
|
|
// corp admin history
|
|
hr = ReadHistoryFromDisk(szLogPath, TRUE);
|
|
}
|
|
else
|
|
{
|
|
// corp history
|
|
hr = ReadHistoryFromDisk(lpszLogFile);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
HRESULT hrAppend;
|
|
//
|
|
// consumer case
|
|
//
|
|
hr = ReadHistoryFromDisk(NULL);
|
|
|
|
//
|
|
// migrate V3 history to iuhist.xml
|
|
// - if succeeded, save the updated iuhist.xml file and delete wuhistv3.log
|
|
// - if failed, just log error and keep using the current iuhist.xml
|
|
//
|
|
TCHAR szLogPath[MAX_PATH];
|
|
GetWindowsUpdateV3Directory(szLogPath);
|
|
hrAppend = PathCchAppend(szLogPath, ARRAYSIZE(szLogPath), C_V3_LOG_FILE);
|
|
if (FAILED(hrAppend))
|
|
{
|
|
LOG_ErrorMsg(hrAppend);
|
|
if (SUCCEEDED(hr))
|
|
hr = hrAppend;
|
|
goto done;
|
|
}
|
|
|
|
if (0xffffffff != GetFileAttributes(szLogPath))
|
|
{
|
|
// V3 history file "wuhistv3.log" exists, so start migration
|
|
if (FAILED(m_pxmlExisting->MigrateV3History(szLogPath)))
|
|
{
|
|
LOG_Out(_T("Failed to migrate v3 consumer history"));
|
|
}
|
|
else
|
|
{
|
|
BSTR bstrLogFilePath = T2BSTR(m_szLogFilePath);
|
|
if (FAILED(m_pxmlExisting->SaveXMLDocument(bstrLogFilePath)))
|
|
{
|
|
LOG_Out(_T("Failed to save the updated history file %s"), m_szLogFilePath);
|
|
}
|
|
else
|
|
{
|
|
DeleteFile(szLogPath);
|
|
}
|
|
SafeSysFreeString(bstrLogFilePath);
|
|
}
|
|
}
|
|
}
|
|
|
|
done:
|
|
ReleaseMutex(m_hMutex);
|
|
SafeSysFreeString(bstrCorpSite);
|
|
|
|
if (FAILED(hr))
|
|
{
|
|
//
|
|
// if we can't load the existing history
|
|
// we can't do anything here. Return empty string.
|
|
//
|
|
*pbstrHistory = SysAllocString(L"");
|
|
LOG_Out(_T("Loading the history xml file failed"));
|
|
return S_FALSE;
|
|
}
|
|
|
|
//
|
|
// traverse history tree, inspect each node
|
|
// to see if time/clientName fit. If not, delete it
|
|
// then output the string
|
|
//
|
|
hr = m_pxmlExisting->GetFilteredHistoryBSTR(BeginDateTime, EndDateTime, m_bstrCurrentClientName, pbstrHistory);
|
|
|
|
return hr;
|
|
}
|
|
|
|
|
|
|
|
// *****************************************************************
|
|
//
|
|
// IUENGINE.DLL Public API:
|
|
//
|
|
// *****************************************************************
|
|
|
|
HRESULT WINAPI CEngUpdate::GetHistory(
|
|
BSTR bstrDateTimeFrom,
|
|
BSTR bstrDateTimeTo,
|
|
BSTR bstrClient,
|
|
BSTR bstrPath,
|
|
BSTR* pbstrLog)
|
|
{
|
|
LOG_Block("GetHistory()");
|
|
|
|
USES_IU_CONVERSION;
|
|
|
|
HRESULT hr = S_OK;
|
|
BSTR bsStart = NULL;
|
|
BSTR bsEnd = NULL;
|
|
CIUHistory cHistory;
|
|
|
|
//
|
|
// first, check to see if this is to ask historical speed
|
|
//
|
|
if (NULL != bstrClient && lstrcmpiW(C_HISTORICALSPEED, (LPWSTR)((LPOLESTR) bstrClient)) == 0)
|
|
{
|
|
HKEY hKey = NULL;
|
|
TCHAR szSpeed[32];
|
|
DWORD dwSpeed = 0x0;
|
|
DWORD dwSize = sizeof(dwSpeed);
|
|
LONG lResult = ERROR_SUCCESS;
|
|
|
|
//
|
|
// get speed here from reg; if failure, dwSpeed remains to be 0.
|
|
//
|
|
if (ERROR_SUCCESS == (lResult = RegOpenKeyEx(HKEY_LOCAL_MACHINE, REGKEY_IUCTL, 0, KEY_READ, &hKey)))
|
|
{
|
|
lResult = RegQueryValueEx(hKey, REGVAL_HISTORICALSPEED, NULL, NULL, (LPBYTE)&dwSpeed, &dwSize);
|
|
RegCloseKey(hKey);
|
|
|
|
if (ERROR_SUCCESS != lResult)
|
|
{
|
|
*pbstrLog = SysAllocString(L"0");
|
|
LOG_Out(_T("GetHistoricalSpeed registry key not found, it must be no downloads happened yet"));
|
|
return hr;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
*pbstrLog = SysAllocString(L"0");
|
|
LOG_ErrorMsg((DWORD)lResult);
|
|
return hr;
|
|
}
|
|
|
|
hr = StringCchPrintfEx(szSpeed, ARRAYSIZE(szSpeed), NULL, NULL, MISTSAFE_STRING_FLAGS,
|
|
_T("%d"), dwSpeed);
|
|
if (FAILED(hr))
|
|
{
|
|
*pbstrLog = SysAllocString(L"0");
|
|
LOG_ErrorMsg(hr);
|
|
return hr;
|
|
}
|
|
*pbstrLog = SysAllocString(T2OLE(szSpeed));
|
|
|
|
LOG_Out(_T("GetHistoricalSpeed get called! Return value %s"), szSpeed);
|
|
return hr;
|
|
}
|
|
|
|
//
|
|
// really asking history log
|
|
//
|
|
|
|
//
|
|
// set the client name
|
|
//
|
|
if (NULL != bstrClient && SysStringLen(bstrClient) > 0)
|
|
{
|
|
LOG_Out(_T("Set client name as %s"), OLE2T(bstrClient));
|
|
cHistory.SetClientName(bstrClient);
|
|
}
|
|
else
|
|
{
|
|
LOG_Out(_T("Set client name as NULL"));
|
|
cHistory.SetClientName(NULL);
|
|
}
|
|
|
|
//
|
|
// for script: they may pass empty string. we treat them
|
|
// as NULL
|
|
//
|
|
if (NULL != bstrDateTimeFrom && SysStringLen(bstrDateTimeFrom) > 0)
|
|
{
|
|
LOG_Out(_T("DateTimeFrom=%s"), OLE2T(bstrDateTimeFrom));
|
|
bsStart = bstrDateTimeFrom;
|
|
}
|
|
if (NULL != bstrDateTimeTo && SysStringLen(bstrDateTimeTo) > 0)
|
|
{
|
|
LOG_Out(_T("DateTimeTo=%s"), OLE2T(bstrDateTimeTo));
|
|
bsEnd = bstrDateTimeTo;
|
|
}
|
|
|
|
//
|
|
// we do NOT validate the format of these two date/time strings.
|
|
// They are supposed to be in XML datetime format. If not, then
|
|
// the returned history logs may be filtered incorrectly.
|
|
//
|
|
hr = cHistory.GetHistoryStr(OLE2T(bstrPath), bsStart, bsEnd, pbstrLog);
|
|
|
|
SysFreeString(bsStart);
|
|
SysFreeString(bsEnd);
|
|
return hr;
|
|
}
|