windows-nt/Source/XPSP1/NT/enduser/windows.com/iuctl/update.cpp
2020-09-26 16:20:57 +08:00

2401 lines
63 KiB
C++

//=======================================================================
//
// Copyright (c) 2000 Microsoft Corporation. All Rights Reserved.
//
// File: Update.cpp
//
// Owner: JHou
//
// Description:
//
// Industry Update v1.0 client control stub - Implementation of CUpdate
//
//
// Revision History:
//
// Date Author Desc
// ~~~~ ~~~~~~ ~~~~
// 9/15/2000 JHou created.
//
//=======================================================================
#include "stdafx.h"
#include "iu.h"
#include "iucommon.h"
#include "IUCtl.h"
#include "Update.h"
#include "iudl.h"
#include "selfupd.h"
#include "loadengine.h"
#include <logging.h>
#include <fileutil.h>
#include <trust.h>
#include <osdet.h>
#include <exdisp.h>
#include <UrlAgent.h>
#include <wusafefn.h>
typedef BOOL (WINAPI* pfn_InternetCrackUrl)(LPCTSTR, DWORD, DWORD, LPURL_COMPONENTS);
extern HANDLE g_hEngineLoadQuit;
extern CIUUrlAgent *g_pIUUrlAgent;
#define Initialized (2 == m_lInitState)
/////////////////////////////////////////////////////////////////////////////
//
// declaration of function template for CoFreeUnusedLibrariesEx(), which is
// available on Win98+ and Win2000+ only, in ole32.dll
//
/////////////////////////////////////////////////////////////////////////////
typedef void (WINAPI * PFN_CoFreeUnusedLibrariesEx) (IN DWORD dwUnloadDelay,
IN DWORD dwReserved);
extern "C" const CLSID CLSID_Update2;
typedef HRESULT (STDMETHODCALLTYPE* PROC_RegServer)(void);
DWORD MyGetModuleFileName(HMODULE hModule, LPTSTR pszBuf, DWORD cchBuf);
BOOL IsThisUpdate2();
/////////////////////////////////////////////////////////////////////////////
// CUpdate
/////////////////////////////////////////////////////////////////////////////
// Constructor
//
/////////////////////////////////////////////////////////////////////////////
CUpdate::CUpdate()
: m_EvtWindow(this),
m_dwSafety(0),
m_dwMode(0x0),
m_hValidated(E_FAIL), // container not validated yet
m_fUseCompression(TRUE),
m_fOfflineMode(FALSE),
m_hEngineModule(NULL),
m_pClientSite(NULL),
m_lInitState(0L),
m_dwUpdateInfo(0x0),
m_hIUEngine(NULL)
{
m_szReqControlVer[0] = _T('\0');
m_gfInit_csLock = SafeInitializeCriticalSection(&m_lock);
m_evtControlQuit = CreateEvent(NULL, TRUE, FALSE, NULL);
m_EvtWindow.Create();
/*
we decided to use the new Win32 API GetControlUpdateInfo() to expose these
data and let a wrapper control to call it so we won't have reboot issue on
OS prior to WinXP
//
// try to free unused libraries
//
HMODULE hOle32Dll = LoadLibrary(_T("ole32.dll"));
if (NULL != hOle32Dll)
{
//
// The min platforms support CoFreeUnusedLibrariesEx() are W2K and W98
// so we can't call it directly
//
PFN_CoFreeUnusedLibrariesEx pFreeLib = (PFN_CoFreeUnusedLibrariesEx)
GetProcAddress(hOle32Dll,
"CoFreeUnusedLibrariesEx");
if (NULL != pFreeLib)
{
//
// ask to release the unused library immediately, this will cause the COM objects
// that being released (e.g., set obj to nothing) unloaded from memory immediately,
// so in control update case we can safely jump to a to use <OBJECT> with codebase
// to update the control and it won't cause reboot even though on this page we already
// loaded the control
//
pFreeLib(0, 0);
}
FreeLibrary(hOle32Dll);
}
//
// figure out if we are upate 2, we are if this module name ends with "2.dll"
//
m_fIsThisUpdate2 = ::IsThisUpdate2();
*/
}
/////////////////////////////////////////////////////////////////////////////
// Destructor
//
/////////////////////////////////////////////////////////////////////////////
CUpdate::~CUpdate()
{
m_EvtWindow.Destroy();
if(m_gfInit_csLock)
{
DeleteCriticalSection(&m_lock);
}
if (NULL != m_evtControlQuit)
{
CloseHandle(m_evtControlQuit);
}
SafeReleaseNULL(m_pClientSite);
}
/////////////////////////////////////////////////////////////////////////////
// GetInterfaceSafetyOptions()
//
// Retrieves the safety options supported by the object.
/////////////////////////////////////////////////////////////////////////////
STDMETHODIMP CUpdate::GetInterfaceSafetyOptions(REFIID riid, DWORD *pdwSupportedOptions, DWORD *pdwEnabledOptions)
{
if (!m_gfInit_csLock)
{
return E_OUTOFMEMORY;
}
if (pdwSupportedOptions == NULL || pdwEnabledOptions == NULL)
return E_POINTER;
HRESULT hr = S_OK;
if (riid == IID_IDispatch)
{
*pdwSupportedOptions = INTERFACESAFE_FOR_UNTRUSTED_CALLER;
*pdwEnabledOptions = m_dwSafety & INTERFACESAFE_FOR_UNTRUSTED_CALLER;
}
else
{
*pdwSupportedOptions = 0;
*pdwEnabledOptions = 0;
hr = E_NOINTERFACE;
}
return hr;
}
/////////////////////////////////////////////////////////////////////////////
// SetInterfaceSafetyOptions()
//
// Makes the object safe for initialization or scripting.
/////////////////////////////////////////////////////////////////////////////
STDMETHODIMP CUpdate::SetInterfaceSafetyOptions(REFIID riid, DWORD dwOptionSetMask, DWORD dwEnabledOptions)
{
if (!m_gfInit_csLock)
{
return E_OUTOFMEMORY;
}
// If we're being asked to set our safe for scripting option then oblige
if (riid == IID_IDispatch)
{
// Store our current safety level to return in GetInterfaceSafetyOptions
m_dwSafety = dwEnabledOptions & dwOptionSetMask;
return S_OK;
}
return E_NOINTERFACE;
}
/////////////////////////////////////////////////////////////////////////////
// InterfaceSupportsErrorInfo()
//
// Indicates whether the interface identified by riid supports the
// IErrorInfo interface
/////////////////////////////////////////////////////////////////////////////
STDMETHODIMP CUpdate::InterfaceSupportsErrorInfo(REFIID riid)
{
if (!m_gfInit_csLock)
{
return E_OUTOFMEMORY;
}
static const IID* arr[] =
{
&IID_IUpdate
};
for (int i=0; i < sizeof(arr) / sizeof(arr[0]); i++)
{
if (InlineIsEqualGUID(*arr[i],riid))
return S_OK;
}
return S_FALSE;
}
/////////////////////////////////////////////////////////////////////////////
// GetSystemSpec()
//
// Gets the basic system specs.
// Input:
// bstrXmlClasses - a list of requested classes in xml format, NULL if any.
// For example:
// <devices>
// <class name="video"/>
// <class name="sound" id="2560AD4D-3ED3-49C6-A937-4368C0B0E06A"/>
// </devices>
// Return:
// pbstrXmlDetectionResult - the detection result in xml format.
/////////////////////////////////////////////////////////////////////////////
STDMETHODIMP CUpdate::GetSystemSpec(BSTR bstrXmlClasses,
BSTR* pbstrXmlDetectionResult)
{
HRESULT hr = E_FAIL;
LOG_Block("CUpdate::GetSystemSpec");
if (!m_gfInit_csLock)
{
return E_OUTOFMEMORY;
}
if (1 == IsWindowsUpdateUserAccessDisabled())
{
LOG_ErrorMsg(ERROR_SERVICE_DISABLED);
return HRESULT_FROM_WIN32(ERROR_SERVICE_DISABLED);
}
//
// load the engine if it's not up-to-date
//
if (Initialized && SUCCEEDED(hr = ValidateControlContainer()))
{
//
// engine is current, delegate the call to engine
//
PFN_GetSystemSpec pfnGetSystemSpec = (PFN_GetSystemSpec)GetProcAddress(m_hEngineModule, "EngGetSystemSpec");
DWORD dwFlags = 0x0;
if (m_fOfflineMode)
{
dwFlags |= FLAG_OFFLINE_MODE;
}
if (NULL != m_hIUEngine && NULL != pfnGetSystemSpec)
{
hr = pfnGetSystemSpec(m_hIUEngine, bstrXmlClasses, dwFlags, pbstrXmlDetectionResult);
}
else
{
LOG_ErrorMsg(ERROR_INVALID_DLL);
hr = HRESULT_FROM_WIN32(ERROR_INVALID_DLL);
}
}
return hr;
}
/////////////////////////////////////////////////////////////////////////////
// GetManifest()
//
// Gets a catalog base on the specified information.
// Input:
// bstrXmlClientInfo - the credentials of the client in xml format
// bstrXmlSystemSpec - the detected system specifications in xml
// bstrXmlQuery - the user query infomation in xml
// Return:
// pbstrXmlCatalog - the xml catalog retrieved
/////////////////////////////////////////////////////////////////////////////
STDMETHODIMP CUpdate::GetManifest(BSTR bstrXmlClientInfo,
BSTR bstrXmlSystemSpec,
BSTR bstrXmlQuery,
BSTR* pbstrXmlCatalog)
{
HRESULT hr = E_FAIL;
LOG_Block("CUpdate::GetManifest");
if (!m_gfInit_csLock)
{
return E_OUTOFMEMORY;
}
if (1 == IsWindowsUpdateUserAccessDisabled())
{
LOG_ErrorMsg(ERROR_SERVICE_DISABLED);
return HRESULT_FROM_WIN32(ERROR_SERVICE_DISABLED);
}
if (m_fOfflineMode)
{
// if we are in offline mode we can't download from the internet.
LOG_ErrorMsg(ERROR_INVALID_PARAMETER);
return HRESULT_FROM_WIN32(ERROR_INVALID_PARAMETER);
}
//
// load the engine if it's not up-to-date
//
if (Initialized && SUCCEEDED(hr = ValidateControlContainer()))
{
//
// engine is current, delegate the call to engine
//
PFN_GetManifest pfnGetManifest = (PFN_GetManifest)GetProcAddress(m_hEngineModule, "EngGetManifest");
if (NULL != m_hIUEngine && NULL != pfnGetManifest)
{
DWORD dwFlags = 0x0;
if (m_fUseCompression)
{
dwFlags |= FLAG_USE_COMPRESSION;
}
hr = pfnGetManifest(m_hIUEngine, bstrXmlClientInfo, bstrXmlSystemSpec, bstrXmlQuery, dwFlags, pbstrXmlCatalog);
}
else
{
LOG_ErrorMsg(ERROR_INVALID_DLL);
hr = HRESULT_FROM_WIN32(ERROR_INVALID_DLL);
}
}
return hr;
}
/////////////////////////////////////////////////////////////////////////////
// Detect()
//
// Do detection.
// Input:
// bstrXmlCatalog - the xml catalog portion containing items to be detected
// Output:
// pbstrXmlItems - the detected items in xml format
// e.g.
// <id guid="2560AD4D-3ED3-49C6-A937-4368C0B0E06D" installed="1" force="1"/>
/////////////////////////////////////////////////////////////////////////////
STDMETHODIMP CUpdate::Detect(BSTR bstrXmlCatalog,
BSTR* pbstrXmlItems)
{
HRESULT hr = E_FAIL;
LOG_Block("CUpdate::Detect");
if (!m_gfInit_csLock)
{
return E_OUTOFMEMORY;
}
if (1 == IsWindowsUpdateUserAccessDisabled())
{
LOG_ErrorMsg(ERROR_SERVICE_DISABLED);
return HRESULT_FROM_WIN32(ERROR_SERVICE_DISABLED);
}
//
// load the engine if it's not up-to-date
//
if (Initialized && SUCCEEDED(hr = ValidateControlContainer()))
{
//
// engine is current, delegate the call to engine
//
PFN_Detect pfnDetect = (PFN_Detect)GetProcAddress(m_hEngineModule, "EngDetect");
DWORD dwFlags = 0x0;
if (m_fOfflineMode)
{
dwFlags |= FLAG_OFFLINE_MODE;
}
if (NULL != m_hIUEngine && NULL != pfnDetect)
{
hr = pfnDetect(m_hIUEngine, bstrXmlCatalog, dwFlags, pbstrXmlItems);
}
else
{
LOG_ErrorMsg(ERROR_INVALID_DLL);
hr = HRESULT_FROM_WIN32(ERROR_INVALID_DLL);
}
}
return hr;
}
/////////////////////////////////////////////////////////////////////////////
// Download()
//
// Do synchronized downloading.
// Input:
// bstrXmlClientInfo - the credentials of the client in xml format
// bstrXmlCatalog - the xml catalog portion containing items to be downloaded
// bstrDestinationFolder - the destination folder. Null will use the default IU folder
// lMode - indicates throttled or fore-ground downloading mode
// punkProgressListener - the callback function pointer for reporting download progress
// Output:
// pbstrXmlItems - the items with download status in xml format
// e.g.
// <id guid="2560AD4D-3ED3-49C6-A937-4368C0B0E06D" downloaded="1"/>
/////////////////////////////////////////////////////////////////////////////
STDMETHODIMP CUpdate::Download(BSTR bstrXmlClientInfo,
BSTR bstrXmlCatalog,
BSTR bstrDestinationFolder,
LONG lMode,
IUnknown* punkProgressListener,
BSTR* pbstrXmlItems)
{
HRESULT hr = E_FAIL;
LOG_Block("CUpdate::Download");
if (!m_gfInit_csLock)
{
return E_OUTOFMEMORY;
}
if (1 == IsWindowsUpdateUserAccessDisabled())
{
LOG_ErrorMsg(ERROR_SERVICE_DISABLED);
return HRESULT_FROM_WIN32(ERROR_SERVICE_DISABLED);
}
if (m_fOfflineMode)
{
// if we are in offline mode we can't download from the internet.
LOG_ErrorMsg(ERROR_INVALID_PARAMETER);
return HRESULT_FROM_WIN32(ERROR_INVALID_PARAMETER);
}
//
// load the engine if it's not up-to-date
//
if (Initialized && SUCCEEDED(hr = ValidateControlContainer()))
{
//
// engine is current, delegate the call to engine
//
PFN_Download pfnDownload = (PFN_Download)GetProcAddress(m_hEngineModule, "EngDownload");
if (NULL != m_hIUEngine && NULL != pfnDownload)
{
hr = pfnDownload(m_hIUEngine,
bstrXmlClientInfo,
bstrXmlCatalog,
bstrDestinationFolder,
lMode,
punkProgressListener,
m_EvtWindow.GetEvtHWnd(), // should we send event msg for sync download?
pbstrXmlItems);
}
else
{
LOG_ErrorMsg(ERROR_INVALID_DLL);
hr = HRESULT_FROM_WIN32(ERROR_INVALID_DLL);
}
}
return hr;
}
/////////////////////////////////////////////////////////////////////////////
// DownloadAsync()
//
// Download asynchronously - the method will return before completion.
// Input:
// bstrXmlClientInfo - the credentials of the client in xml format
// bstrXmlCatalog - the xml catalog portion containing items to be downloaded
// bstrDestinationFolder - the destination folder. Null will use the default IU folder
// lMode - indicates throttled or fore-ground downloading mode
// punkProgressListener - the callback function pointer for reporting download progress
// bstrUuidOperation - an id provided by the client to provide further
// identification to the operation as indexes may be reused.
// Output:
// pbstrUuidOperation - the operation ID. If it is not provided by the in bstrUuidOperation
// parameter (an empty string is passed), it will generate a new UUID,
// in which case, the caller will be responsible to free the memory of
// the string buffer that holds the generated UUID using SysFreeString().
// Otherwise, it returns the value passed by bstrUuidOperation.
/////////////////////////////////////////////////////////////////////////////
STDMETHODIMP CUpdate::DownloadAsync(BSTR bstrXmlClientInfo,
BSTR bstrXmlCatalog,
BSTR bstrDestinationFolder,
LONG lMode,
IUnknown* punkProgressListener,
BSTR bstrUuidOperation,
BSTR* pbstrUuidOperation)
{
HRESULT hr = E_FAIL;
LOG_Block("CUpdate::DownloadAsync");
if (!m_gfInit_csLock)
{
return E_OUTOFMEMORY;
}
if (1 == IsWindowsUpdateUserAccessDisabled())
{
LOG_ErrorMsg(ERROR_SERVICE_DISABLED);
return HRESULT_FROM_WIN32(ERROR_SERVICE_DISABLED);
}
if (m_fOfflineMode)
{
// if we are in offline mode we can't download from the internet.
LOG_ErrorMsg(ERROR_INVALID_PARAMETER);
return HRESULT_FROM_WIN32(ERROR_INVALID_PARAMETER);
}
//
// load the engine if it's not up-to-date
//
if (Initialized && SUCCEEDED(hr = ValidateControlContainer()))
{
//
// engine is current, delegate the call to engine
//
PFN_DownloadAsync pfnDownloadAsync = (PFN_DownloadAsync)GetProcAddress(m_hEngineModule, "EngDownloadAsync");
if (NULL != m_hIUEngine && NULL != pfnDownloadAsync)
{
hr = pfnDownloadAsync(m_hIUEngine,
bstrXmlClientInfo,
bstrXmlCatalog,
bstrDestinationFolder,
lMode,
punkProgressListener,
m_EvtWindow.GetEvtHWnd(),
bstrUuidOperation,
pbstrUuidOperation);
}
else
{
LOG_ErrorMsg(ERROR_INVALID_DLL);
hr = HRESULT_FROM_WIN32(ERROR_INVALID_DLL);
}
}
return hr;
}
/////////////////////////////////////////////////////////////////////////////
// Install()
//
// Do synchronized installation.
// Input:
// bstrXmlCatalog - the xml catalog portion containing items to be installed
// bstrXmlDownloadedItems - the xml of downloaded items and their respective download
// result as described in the result schema. Install uses this
// to know whether the items were downloaded and if so where they
// were downloaded to so that it can install the items
// lMode - indicates different installation mode
// punkProgressListener - the callback function pointer for reporting install progress
// Output:
// pbstrXmlItems - the items with installation status in xml format
// e.g.
// <id guid="2560AD4D-3ED3-49C6-A937-4368C0B0E06D" installed="1"/>
/////////////////////////////////////////////////////////////////////////////
STDMETHODIMP CUpdate::Install(BSTR bstrXmlClientInfo,
BSTR bstrXmlCatalog,
BSTR bstrXmlDownloadedItems,
LONG lMode,
IUnknown* punkProgressListener,
BSTR* pbstrXmlItems)
{
HRESULT hr = E_FAIL;
LOG_Block("CUpdate::Install");
if (!m_gfInit_csLock)
{
return E_OUTOFMEMORY;
}
if (1 == IsWindowsUpdateUserAccessDisabled())
{
LOG_ErrorMsg(ERROR_SERVICE_DISABLED);
return HRESULT_FROM_WIN32(ERROR_SERVICE_DISABLED);
}
if (m_fOfflineMode)
{
// make sure offline mode parameter is set if SetProperty() Offline Mode was Set
lMode |= UPDATE_OFFLINE_MODE;
}
//
// load the engine if it's not up-to-date
//
if (Initialized && SUCCEEDED(hr = ValidateControlContainer()))
{
//
// engine is current, delegate the call to engine
//
PFN_Install pfnInstall = (PFN_Install)GetProcAddress(m_hEngineModule, "EngInstall");
if (NULL != m_hIUEngine && NULL != pfnInstall)
{
hr = pfnInstall(m_hIUEngine,
bstrXmlClientInfo,
bstrXmlCatalog,
bstrXmlDownloadedItems,
lMode,
punkProgressListener,
m_EvtWindow.GetEvtHWnd(),
pbstrXmlItems);
}
else
{
LOG_ErrorMsg(ERROR_INVALID_DLL);
hr = HRESULT_FROM_WIN32(ERROR_INVALID_DLL);
}
}
return hr;
}
/////////////////////////////////////////////////////////////////////////////
// InstallAsync()
//
// Install Asynchronously.
// Input:
// bstrXmlCatalog - the xml catalog portion containing items to be installed
// bstrXmlDownloadedItems - the xml of downloaded items and their respective download
// result as described in the result schema. Install uses this
// to know whether the items were downloaded and if so where they
// were downloaded to so that it can install the items
// lMode - indicates different installation mode
// punkProgressListener - the callback function pointer for reporting install progress
// bstrUuidOperation - an id provided by the client to provide further
// identification to the operation as indexes may be reused.
// Output:
// pbstrUuidOperation - the operation ID. If it is not provided by the in bstrUuidOperation
// parameter (an empty string is passed), it will generate a new UUID,
// in which case, the caller will be responsible to free the memory of
// the string buffer that holds the generated UUID using SysFreeString().
// Otherwise, it returns the value passed by bstrUuidOperation.
/////////////////////////////////////////////////////////////////////////////
STDMETHODIMP CUpdate::InstallAsync(BSTR bstrXmlClientInfo,
BSTR bstrXmlCatalog,
BSTR bstrXmlDownloadedItems,
LONG lMode,
IUnknown* punkProgressListener,
BSTR bstrUuidOperation,
BSTR* pbstrUuidOperation)
{
HRESULT hr = E_FAIL;
LOG_Block("CUpdate::InstallAsync");
if (!m_gfInit_csLock)
{
return E_OUTOFMEMORY;
}
if (1 == IsWindowsUpdateUserAccessDisabled())
{
LOG_ErrorMsg(ERROR_SERVICE_DISABLED);
return HRESULT_FROM_WIN32(ERROR_SERVICE_DISABLED);
}
if (m_fOfflineMode)
{
// make sure offline mode parameter is set if SetProperty() Offline Mode was Set
lMode |= UPDATE_OFFLINE_MODE;
}
//
// load the engine if it's not up-to-date
//
if (Initialized && SUCCEEDED(hr = ValidateControlContainer()))
{
//
// engine is current, delegate the call to engine
//
PFN_InstallAsync pfnInstallAsync = (PFN_InstallAsync)GetProcAddress(m_hEngineModule, "EngInstallAsync");
if (NULL != m_hIUEngine && NULL != pfnInstallAsync)
{
hr = pfnInstallAsync(m_hIUEngine,
bstrXmlClientInfo,
bstrXmlCatalog,
bstrXmlDownloadedItems,
lMode,
punkProgressListener,
m_EvtWindow.GetEvtHWnd(),
bstrUuidOperation,
pbstrUuidOperation);
}
else
{
LOG_ErrorMsg(ERROR_INVALID_DLL);
hr = HRESULT_FROM_WIN32(ERROR_INVALID_DLL);
}
}
return hr;
}
/////////////////////////////////////////////////////////////////////////////
// SetOperationMode()
// Set the operation status.
//
// Input:
// bstrUuidOperation - an id provided by the client to provide further
// identification to the operation as indexes may be reused.
// lMode - the mode affecting the operation:
//
// UPDATE_COMMAND_PAUSE
// UPDATE_COMMAND_RESUME
// UPDATE_COMMAND_CANCEL
// UPDATE_NOTIFICATION_COMPLETEONLY
// UPDATE_NOTIFICATION_ANYPROGRESS
// UPDATE_NOTIFICATION_1PCT
// UPDATE_NOTIFICATION_5PCT
// UPDATE_NOTIFICATION_10PCT
// UPDATE_SHOWUI
//
/////////////////////////////////////////////////////////////////////////////
STDMETHODIMP CUpdate::SetOperationMode(
BSTR bstrUuidOperation,
LONG lMode)
{
HRESULT hr = E_FAIL;
LOG_Block("CUpdate::SetOperationMode");
if (!m_gfInit_csLock)
{
return E_OUTOFMEMORY;
}
if (1 == IsWindowsUpdateUserAccessDisabled())
{
LOG_ErrorMsg(ERROR_SERVICE_DISABLED);
return HRESULT_FROM_WIN32(ERROR_SERVICE_DISABLED);
}
//
// load the engine if it's not up-to-date
//
if (Initialized && SUCCEEDED(hr = ValidateControlContainer()))
{
//
// engine is current, delegate the call to engine
//
PFN_SetOperationMode pfnSetOperationMode = (PFN_SetOperationMode)GetProcAddress(m_hEngineModule, "EngSetOperationMode");
if (NULL != m_hIUEngine && NULL != pfnSetOperationMode)
{
hr = pfnSetOperationMode(m_hIUEngine, bstrUuidOperation, lMode);
}
else
{
LOG_ErrorMsg(ERROR_INVALID_DLL);
hr = HRESULT_FROM_WIN32(ERROR_INVALID_DLL);
}
}
return hr;
}
/////////////////////////////////////////////////////////////////////////////
// GetHistory()
//
// Get the history log.
// Input:
// bstrDateTimeFrom - the start date and time for which a log is required.
// This is a string in ANSI format (YYYY-MM-DDTHH-MM).
// If the string is empty, there will be no date restriction
// of the returned history log.
// bstrDateTimeTo - the end date and time for which a log is required.
// This is a string in ANSI format (YYYY-MM-DDTHH-MM).
// If the string is empty, there will be no date restriction
// of the returned history log.
// bstrClient - the name of the client that initiated the action. If this parameter
// is null or an empty string, then there will be no filtering based
// on the client.
// bstrPath - the path used for download or install. Used in the corporate version
// by IT managers. If this parameter is null or an empty string, then
// there will be no filtering based on the path.
// Output:
// pbstrLog - the history log in xml format
/////////////////////////////////////////////////////////////////////////////
STDMETHODIMP CUpdate::GetHistory(BSTR bstrDateTimeFrom,
BSTR bstrDateTimeTo,
BSTR bstrClient,
BSTR bstrPath,
BSTR* pbstrLog)
{
HRESULT hr = E_FAIL;
LOG_Block("CUpdate::GetHistory");
if (!m_gfInit_csLock)
{
return E_OUTOFMEMORY;
}
if (1 == IsWindowsUpdateUserAccessDisabled())
{
LOG_ErrorMsg(ERROR_SERVICE_DISABLED);
return HRESULT_FROM_WIN32(ERROR_SERVICE_DISABLED);
}
//
// load the engine if it's not up-to-date
//
if (Initialized && SUCCEEDED(hr = ValidateControlContainer()))
{
//
// engine is current, delegate the call to engine
//
PFN_GetHistory pfnGetHistory = (PFN_GetHistory)GetProcAddress(m_hEngineModule, "EngGetHistory");
if (NULL != m_hIUEngine && NULL != pfnGetHistory)
{
hr = pfnGetHistory(m_hIUEngine,
bstrDateTimeFrom,
bstrDateTimeTo,
bstrClient,
bstrPath,
pbstrLog);
}
else
{
LOG_ErrorMsg(ERROR_INVALID_DLL);
hr = HRESULT_FROM_WIN32(ERROR_INVALID_DLL);
}
}
return hr;
}
/////////////////////////////////////////////////////////////////////////////
//
// private overriding function InternalRelease(), to unlock the engine
// if the reference (before the release) is 1, i.e., the last ref
// count about to be released
//
/////////////////////////////////////////////////////////////////////////////
ULONG CUpdate::InternalRelease()
{
if (1 == m_dwRef)
{
//
// the control is going to really gone, we need to make sure that
// if the engine is loaded, we unload it here.
//
UnlockEngine();
CleanupDownloadLib();
}
return CComObjectRootEx<CComMultiThreadModel>::InternalRelease();
}
/////////////////////////////////////////////////////////////////////////////
// UnlockEngine()
//
// release the engine dll if ref cnt of engine is down to zero
/////////////////////////////////////////////////////////////////////////////
HRESULT CUpdate::UnlockEngine()
{
if (!m_gfInit_csLock)
{
return E_OUTOFMEMORY;
}
TCHAR szSystemDir[MAX_PATH];
TCHAR szEngineDllPath[MAX_PATH];
TCHAR szEngineNewDllPath[MAX_PATH];
int iVerCheck = 0;
HRESULT hr = S_OK;
SetEvent(m_evtControlQuit);
EnterCriticalSection(&m_lock);
if (NULL != m_hEngineModule)
{
//
// We have to delete the engine instance we are using. This will clean up
// all resources, stop threads, etc. for the instance we own.
//
PFN_DeleteEngUpdateInstance pfnDeleteEngUpdateInstance = (PFN_DeleteEngUpdateInstance) GetProcAddress(m_hEngineModule, "DeleteEngUpdateInstance");
if (NULL != pfnDeleteEngUpdateInstance)
{
pfnDeleteEngUpdateInstance(m_hIUEngine);
}
//
// Cleanup any global threads (it checks to see if we are last instance)
//
PFN_ShutdownGlobalThreads pfnShutdownGlobalThreads = (PFN_ShutdownGlobalThreads) GetProcAddress(m_hEngineModule, "ShutdownGlobalThreads");
if (NULL != pfnShutdownGlobalThreads)
{
pfnShutdownGlobalThreads();
}
//
// unload engine
//
FreeLibrary(m_hEngineModule);
m_hEngineModule = NULL;
m_lInitState = 0; // mark as uninitialized
//
// get path of enginenew.dll
//
GetSystemDirectory(szSystemDir, ARRAYSIZE(szSystemDir));
hr = PathCchCombine(szEngineNewDllPath, ARRAYSIZE(szEngineNewDllPath), szSystemDir,ENGINENEWDLL);
if (FAILED(hr))
{
return hr;
}
//
// see if we should try to update the engine (locally)
//
HKEY hkey = NULL;
DWORD dwStatus = 0;
DWORD dwSize = sizeof(dwStatus);
if (ERROR_SUCCESS == RegOpenKey(HKEY_LOCAL_MACHINE, REGKEY_IUCTL, &hkey))
{
RegQueryValueEx(hkey, REGVAL_SELFUPDATESTATUS, NULL, NULL, (LPBYTE)&dwStatus, &dwSize);
}
if (FileExists(szEngineNewDllPath) &&
S_OK == VerifyFileTrust(szEngineNewDllPath, NULL, ReadWUPolicyShowTrustUI()) &&
SELFUPDATE_COMPLETE_UPDATE_BINARY_REQUIRED == dwStatus)
{
// an iuenginenew.dll exists, try replacing the engine.dll This will fail if this is
// not the last process using the engine. This is not a problem, when that process
// finishes it will rename the DLL.
hr = PathCchCombine(szEngineDllPath, ARRAYSIZE(szEngineDllPath), szSystemDir,ENGINEDLL);
if (FAILED(hr))
{
return hr;
}
if (SUCCEEDED(CompareFileVersion(szEngineDllPath, szEngineNewDllPath, &iVerCheck)) &&
iVerCheck < 0 &&
TRUE == MoveFileEx(szEngineNewDllPath, szEngineDllPath, MOVEFILE_REPLACE_EXISTING))
{
// Rename was Successful.. reset RegKey Information about SelfUpdate Status
// Because the rename was successful we know no other processes are interacting
// It should be safe to set the reg key.
dwStatus = 0; // PreFast
RegSetValueEx(hkey, REGVAL_SELFUPDATESTATUS, 0, REG_DWORD, (LPBYTE)&dwStatus, sizeof(dwStatus));
}
}
else if (SELFUPDATE_COMPLETE_UPDATE_BINARY_REQUIRED == dwStatus)
{
// registry indicates rename required, but enginenew DLL does not exist. Reset registry
dwStatus = 0;
RegSetValueEx(hkey, REGVAL_SELFUPDATESTATUS, 0, REG_DWORD, (LPBYTE)&dwStatus, sizeof(dwStatus));
}
if (NULL != hkey)
{
RegCloseKey(hkey);
}
}
LeaveCriticalSection(&m_lock);
ResetEvent(m_evtControlQuit);
return S_OK;
}
/**
*
* Get the mode of a specified operation.
*
* @param bstrUuidOperation: same as in SetOperationMode()
* @param plMode - the retval for the mode found in a bitmask for:
* (value in brackets [] means default)
* UPDATE_COMMAND_PAUSE (TRUE/[FALSE])
* UPDATE_COMMAND_RESUME (TRUE/[FALSE])
* UPDATE_NOTIFICATION_COMPLETEONLY (TRUE/[FALSE])
* UPDATE_NOTIFICATION_ANYPROGRESS ([TRUE]/FALSE)
* UPDATE_NOTIFICATION_1PCT (TRUE/[FALSE])
* UPDATE_NOTIFICATION_5PCT (TRUE/[FALSE])
* UPDATE_NOTIFICATION_10PCT (TRUE/[FALSE])
* UPDATE_SHOWUI (TRUE/[FALSE])
*
*/
STDMETHODIMP CUpdate::GetOperationMode(BSTR bstrUuidOperation, LONG *plMode)
{
HRESULT hr = E_FAIL;
LOG_Block("CUpdate::GetOperationMode");
if (!m_gfInit_csLock)
{
return E_OUTOFMEMORY;
}
if (1 == IsWindowsUpdateUserAccessDisabled())
{
LOG_ErrorMsg(ERROR_SERVICE_DISABLED);
return HRESULT_FROM_WIN32(ERROR_SERVICE_DISABLED);
}
//
// load the engine if it's not up-to-date
//
if (Initialized && SUCCEEDED(hr = ValidateControlContainer()))
{
//
// engine is current, delegate the call to engine
//
PFN_GetOperationMode pfnGetOperationMode = (PFN_GetOperationMode)GetProcAddress(m_hEngineModule, "EngGetOperationMode");
if (NULL != m_hIUEngine && NULL != pfnGetOperationMode)
{
hr = pfnGetOperationMode(m_hIUEngine, bstrUuidOperation, plMode);
}
else
{
LOG_ErrorMsg(ERROR_INVALID_DLL);
hr = HRESULT_FROM_WIN32(ERROR_INVALID_DLL);
}
}
return hr;
}
/**
*
* Set a property of this control
* Calling this method will not cause the engine loaded
*
* @param lProperty - the identifier to flag which property need changed
* UPDATE_PROP_OFFLINEMODE (TRUE/[FALSE])
* UPDATE_PROP_USECOMPRESSION ([TRUE]/FALSE)
*
* @param varValue - the value to change
*
*/
STDMETHODIMP CUpdate::SetProperty(LONG lProperty, VARIANT varValue)
{
LOG_Block("CUpdate::SetProperty");
HRESULT hr = S_OK;
if (!m_gfInit_csLock)
{
return E_OUTOFMEMORY;
}
if (1 == IsWindowsUpdateUserAccessDisabled())
{
LOG_ErrorMsg(ERROR_SERVICE_DISABLED);
return HRESULT_FROM_WIN32(ERROR_SERVICE_DISABLED);
}
switch(lProperty)
{
case UPDATE_PROP_USECOMPRESSION:
if (VT_BOOL != varValue.vt)
{
hr = E_INVALIDARG;
LOG_ErrorMsg(hr);
}
else
{
m_fUseCompression = (VARIANT_TRUE == varValue.boolVal) ? TRUE : FALSE;
}
break;
case UPDATE_PROP_OFFLINEMODE:
if (VT_BOOL != varValue.vt)
{
hr = E_INVALIDARG;
LOG_ErrorMsg(hr);
}
else
{
m_fOfflineMode = (VARIANT_TRUE == varValue.boolVal) ? TRUE : FALSE;
}
break;
default:
return E_NOTIMPL;
}
return S_OK;
}
/**
*
* Retrieve a property of this control
* Calling this method will not cause the engine loaded
*
* @param lProperty - the identifier to flag which property need retrieved
* UPDATE_PROP_OFFLINEMODE (TRUE/[FALSE])
* UPDATE_PROP_USECOMPRESSION ([TRUE]/FALSE)
*
* @param varValue - the value to retrieve
*
*/
STDMETHODIMP CUpdate::GetProperty(LONG lProperty, VARIANT *pvarValue)
{
LOG_Block("CUpdate::GetProperty");
if (!m_gfInit_csLock)
{
return E_OUTOFMEMORY;
}
if (1 == IsWindowsUpdateUserAccessDisabled())
{
LOG_ErrorMsg(ERROR_SERVICE_DISABLED);
return HRESULT_FROM_WIN32(ERROR_SERVICE_DISABLED);
}
if (NULL == pvarValue)
{
return E_INVALIDARG;
}
VariantInit(pvarValue);
switch(lProperty)
{
case UPDATE_PROP_USECOMPRESSION:
pvarValue->vt = VT_BOOL;
pvarValue->boolVal = (m_fUseCompression) ? VARIANT_TRUE : VARIANT_FALSE;
break;
case UPDATE_PROP_OFFLINEMODE:
pvarValue->vt = VT_BOOL;
pvarValue->boolVal = (m_fOfflineMode) ? VARIANT_TRUE : VARIANT_FALSE;
break;
default:
return E_NOTIMPL;
}
return S_OK;
}
/////////////////////////////////////////////////////////////////////////////
//
// Primarily expose shlwapi BrowseForFolder API, can also do checking
// on R/W access if flagged so.
//
// @param bstrStartFolder - the folder from which to start. If NULL or empty str
// is being passed in, then start from desktop
//
// @param flag - validating check
// UI_WRITABLE for checking write access, OK button may disabled.
// UI_READABLE for checking read access, OK button may disabled.
// NO_UI_WRITABLE for checking write access, return error if no access
// NO_UI_READABLE for checking read access, return error if no access
// 0 (default) for no checking.
//
// @param pbstrFolder - returned folder if a valid folder selected
//
/////////////////////////////////////////////////////////////////////////////
STDMETHODIMP CUpdate::BrowseForFolder(BSTR bstrStartFolder, LONG flag, BSTR* pbstrFolder)
{
HRESULT hr = E_FAIL;
LOG_Block("CUpdate::BrowseForFolder");
if (!m_gfInit_csLock)
{
return E_OUTOFMEMORY;
}
if (1 == IsWindowsUpdateUserAccessDisabled())
{
LOG_ErrorMsg(ERROR_SERVICE_DISABLED);
return HRESULT_FROM_WIN32(ERROR_SERVICE_DISABLED);
}
//
// load the engine if it's not up-to-date
//
if (Initialized && SUCCEEDED(hr = ValidateControlContainer()))
{
//
// engine is current, delegate the call to engine
//
PFN_BrowseForFolder pfnBrowseForFolder = (PFN_BrowseForFolder)GetProcAddress(m_hEngineModule, "EngBrowseForFolder");
if (NULL != m_hIUEngine && NULL != pfnBrowseForFolder)
{
hr = pfnBrowseForFolder(m_hIUEngine, bstrStartFolder, flag, pbstrFolder);
}
else
{
LOG_ErrorMsg(ERROR_INVALID_DLL);
hr = HRESULT_FROM_WIN32(ERROR_INVALID_DLL);
}
}
return hr;
}
/**
*
* Allows the Caller to Request the Control to do a Reboot
*
*/
STDMETHODIMP CUpdate::RebootMachine()
{
HRESULT hr = E_FAIL;
LOG_Block("CUpdate::RebootMachine");
if (!m_gfInit_csLock)
{
return E_OUTOFMEMORY;
}
if (1 == IsWindowsUpdateUserAccessDisabled())
{
LOG_ErrorMsg(ERROR_SERVICE_DISABLED);
return HRESULT_FROM_WIN32(ERROR_SERVICE_DISABLED);
}
//
// load the engine if it's not up-to-date
//
if (Initialized && SUCCEEDED(hr = ValidateControlContainer()))
{
PFN_RebootMachine pfnRebootMachine = (PFN_RebootMachine)GetProcAddress(m_hEngineModule, "EngRebootMachine");
if (NULL != m_hIUEngine && NULL != pfnRebootMachine)
{
hr = pfnRebootMachine(m_hIUEngine);
}
else
{
LOG_ErrorMsg(ERROR_INVALID_DLL);
hr = HRESULT_FROM_WIN32(ERROR_INVALID_DLL);
}
}
return hr;
}
//
// Override of IObjectWithSite::SetSite()
// Internet Explorer QIs for IObjectWithSite, and calls this method
// with a pointer to its IOleClientSite
//
STDMETHODIMP CUpdate::SetSite(IUnknown* pSite)
{
if (!m_gfInit_csLock)
{
return E_OUTOFMEMORY;
}
SafeReleaseNULL(m_pClientSite);
m_pClientSite = pSite;
if (NULL != m_pClientSite)
m_pClientSite->AddRef();
return IObjectWithSiteImpl<CUpdate>::SetSite(pSite);
}
/**
*
* Security feature: make sure if the user of this control is
* a web page then the URL can be found in iuident.txt
*
* This function should be called after iuident refreshed.
*
* Return: TRUE/FALSE, to tell if we can continue
*
*/
const TCHAR IDENT_IUSERVERCACHE[] = _T("IUServerURLs");
const TCHAR IDENT_IUSERVERCOUNT[] = _T("ServerCount");
const TCHAR IDENT_IUSERVER[] = _T("Server");
HRESULT CUpdate::ValidateControlContainer(void)
{
LOG_Block("ValidateControlContainer");
if (!m_gfInit_csLock)
{
return E_OUTOFMEMORY;
}
IServiceProvider* pISP = NULL;
IWebBrowserApp* pWeb = NULL;
BSTR bstrUrl = NULL;
LPTSTR lpszUrl = NULL;
#if !(defined(_UNICODE) || defined(UNICODE))
// ANSI build
LPSTR lpszAnsiUrl = NULL;
#endif
TCHAR szIUDir[MAX_PATH];
TCHAR szIdentFile[MAX_PATH];
TCHAR szServer[32];
LPTSTR szValidURL = NULL;
if (E_FAIL != m_hValidated)
{
//
// has been invalidated already
//
LOG_Internet(_T("Validate result: %s"), SUCCEEDED(m_hValidated) ? _T("S_OK") : _T("INET_E_INVALID_URL"));
return m_hValidated;
}
//
// check to see if the container is a web page/site, if
// not done so yet.
//
if (NULL != m_pClientSite)
{
//
// this is a web site!
//
m_hValidated = INET_E_INVALID_URL;
LOG_Internet(_T("Found control called by a web page"));
if (SUCCEEDED(m_pClientSite->QueryInterface(IID_IServiceProvider, (void**)&pISP)) &&
NULL != pISP &&
SUCCEEDED(pISP->QueryService(IID_IWebBrowserApp, IID_IWebBrowserApp, (void**)&pWeb)) &&
NULL != pWeb &&
SUCCEEDED(pWeb->get_LocationURL(&bstrUrl)) &&
NULL != bstrUrl)
{
#if defined(_UNICODE) || defined(UNICODE)
lpszUrl = bstrUrl;
#else // ANSI build
int nBufferLength = WideCharToMultiByte(CP_ACP, 0, bstrUrl, -1, NULL, 0, NULL, NULL);
lpszAnsiUrl = (LPSTR) HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, nBufferLength);
if (NULL == lpszAnsiUrl || 0 == nBufferLength)
{
//
// Unfortunately, we return this error in place of E_OUTOFMEMORY, but the most
// likely scenario that would cause this would be a security attack (bad URL)
//
goto CleanUp; // Will return INET_E_INVALID_URL
}
WideCharToMultiByte(CP_ACP, 0, bstrUrl, -1, lpszAnsiUrl, nBufferLength, NULL, NULL);
lpszUrl = lpszAnsiUrl;
#endif
LOG_Internet(_T("Web address = %s"), lpszUrl);
//
// no matter what protocol specified in this URL
// (can be anything: http, ftp, UNC, path...)
// we just need to verify it against iuident.txt
//
szValidURL = (LPTSTR)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, INTERNET_MAX_URL_LENGTH * sizeof(TCHAR));
if (NULL == szValidURL)
{
SafeReleaseNULL(pISP);
SafeReleaseNULL(pWeb);
SysFreeString(bstrUrl);
LOG_ErrorMsg(E_OUTOFMEMORY);
return E_OUTOFMEMORY;
}
GetIndustryUpdateDirectory(szIUDir);
m_hValidated = PathCchCombine(szIdentFile, ARRAYSIZE(szIdentFile), szIUDir,IDENTTXT);
if (FAILED(m_hValidated))
{
SafeReleaseNULL(pISP);
SafeReleaseNULL(pWeb);
SysFreeString(bstrUrl);
SafeHeapFree(szValidURL);
LOG_ErrorMsg(m_hValidated);
return m_hValidated;
}
// Fix of bug 557430: IU: Security: Use InternetCrackUrl to verify server url used by control.
URL_COMPONENTS urlComp;
ZeroMemory(&urlComp, sizeof(urlComp));
urlComp.dwStructSize = sizeof(urlComp);
// Only interested in the hostname
LPTSTR pszHostName = (LPTSTR) HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, INTERNET_MAX_URL_LENGTH * sizeof(TCHAR));
urlComp.lpszHostName = pszHostName;
urlComp.dwHostNameLength = INTERNET_MAX_URL_LENGTH;
#if defined(UNICODE)
pfn_InternetCrackUrl pfnInternetCrackUrl = (pfn_InternetCrackUrl)GetProcAddress(
GetModuleHandle(_T("wininet.dll")), "InternetCrackUrlW");
#else
pfn_InternetCrackUrl pfnInternetCrackUrl = (pfn_InternetCrackUrl)GetProcAddress(
GetModuleHandle(_T("wininet.dll")), "InternetCrackUrlA");
#endif
if (pfnInternetCrackUrl != NULL)
{
BOOL fRet = (*pfnInternetCrackUrl)(lpszUrl, 0, 0, &urlComp);
if (fRet==FALSE) {
SafeHeapFree(pszHostName);
m_hValidated = INET_E_INVALID_URL;
goto CleanUp;
}
}
else
{
SafeHeapFree(pszHostName);
SafeReleaseNULL(pISP);
SafeReleaseNULL(pWeb);
SysFreeString(bstrUrl);
SafeHeapFree(szValidURL);
m_hValidated = ERROR_PROC_NOT_FOUND;
LOG_ErrorMsg(m_hValidated);
return m_hValidated;
}
//
// get number to servers to compare
//
int iServerCnt = GetPrivateProfileInt(IDENT_IUSERVERCACHE,
IDENT_IUSERVERCOUNT,
-1,
szIdentFile);
//
// loop through
//
URL_COMPONENTS urlCompi;
m_hValidated = INET_E_INVALID_URL;
for (INT i=1; i<=iServerCnt; i++)
{
StringCchPrintfEx(szServer,ARRAYSIZE(szServer),NULL,NULL,MISTSAFE_STRING_FLAGS,_T("%s%d"), IDENT_IUSERVER, i);
//
// get valid server from iuident
//
GetPrivateProfileString(IDENT_IUSERVERCACHE,
szServer,
_T(""),
szValidURL,
INTERNET_MAX_URL_LENGTH,
szIdentFile);
ZeroMemory(&urlCompi, sizeof(urlCompi));
urlCompi.dwStructSize = sizeof(urlCompi);
LPTSTR pszHostNamei = (LPTSTR) HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, INTERNET_MAX_URL_LENGTH * sizeof(TCHAR));
urlCompi.lpszHostName = pszHostNamei;
urlCompi.dwHostNameLength = INTERNET_MAX_URL_LENGTH;
if (TRUE == (*pfnInternetCrackUrl)(szValidURL, 0, 0, &urlCompi))
{
if (0 == lstrcmpi(urlComp.lpszHostName, urlCompi.lpszHostName))
{
//
// Found the current site URL is in this valid URL domain!
//
LogMessage("Windows Update Web Site has a valid address: %ls", bstrUrl);
m_hValidated = S_OK;
SafeHeapFree(pszHostNamei);
break;
}
}
SafeHeapFree(pszHostNamei);
}
SafeHeapFree(pszHostName);
}
}
else
{
//
// NTRAID#NTBUG9-436604-2001/07/17-waltw Security fix: block possible user system information
// leak to non WU callers
//
// If the COM user doesn't call SetSite on our IObjectWithSiteImpl to set m_pClientSite or doesn't
// support IID_IWebBrowserApp functionality on the client site then we won't support them since
// we can't validate the URL that invoked us
//
m_hValidated = INET_E_INVALID_URL;
}
CleanUp:
SafeReleaseNULL(pISP);
SafeReleaseNULL(pWeb);
SysFreeString(bstrUrl);
SafeHeapFree(szValidURL);
LOG_Internet(_T("Validate result: %s"), SUCCEEDED(m_hValidated) ? _T("S_OK") : _T("INET_E_INVALID_URL"));
if (FAILED(m_hValidated) && NULL != lpszUrl)
{
#if defined(UNICODE) || defined(_UNICODE)
LogError(m_hValidated, "Site URL %ls is not valid", lpszUrl);
#else
LogError(m_hValidated, "Site URL %s is not valid", lpszUrl);
#endif
}
#if !(defined(UNICODE) || defined(_UNICODE))
if (NULL != lpszAnsiUrl)
{
HeapFree(GetProcessHeap(), 0, (LPVOID) lpszAnsiUrl);
}
#endif
return m_hValidated;
}
/////////////////////////////////////////////////////////////////////////////
//
// PRIVATE DetectEngine()
//
// download the ident and find out if need to update engine
//
// Note that this function itself is not thread safe. Need to call it
// inside critical section
//
/////////////////////////////////////////////////////////////////////////////
HRESULT CUpdate::DetectEngine(BOOL *pfUpdateAvail)
{
LOG_Block("GetPropUpdateInfo()");
if (!m_gfInit_csLock)
{
return E_OUTOFMEMORY;
}
HRESULT hr = S_OK;
if (NULL == pfUpdateAvail)
{
return E_INVALIDARG;
}
*pfUpdateAvail = FALSE;
//
// get the latest ident
//
if (NULL == m_hEngineModule)
{
//
// This is the first load of the engine for this instance, check for selfupdate first.
// First step is to check for an updated iuident.cab and download it.
//
//
// Only Download the Ident if we are NOT in Offline Mode
//
if (!m_fOfflineMode)
{
//
// download iuident and populate g_pIUUrlAgent
//
if (FAILED(hr = DownloadIUIdent_PopulateData()))
{
LOG_ErrorMsg(hr);
return hr;
}
//
// check engine update info. Note that since 2nd pram is FALSE
// means don't do any actual update, therefore its 1st argument
// is also ignored.
//
hr = SelfUpdateCheck(
FALSE, // async update? ignored now
FALSE, // don't do actual update
m_evtControlQuit, // quit event.
NULL, // no event firing
NULL // no callback needed
);
if (IU_SELFUPDATE_FAILED == hr)
{
LOG_Error(_T("SelfUpdate Failed, using current Engine DLL"));
hr = S_FALSE; // not fatal, let the existing engine work
}
*pfUpdateAvail = (S_FALSE == hr);
}
}
return hr;
}
/////////////////////////////////////////////////////////////////////////////
//
// Initialize() API must be called before any other API will function
//
// If any other API is called before the control is initialized,
// that API will return OLE_E_BLANK, signalling this OLE control is an
// uninitialized object (although in this case it's a bit different from
// its original meaning)
//
// Parameters:
//
// lInitFlag - IU_INIT_CHECK, cause Initialize() download ident and check if any
// of the components need updated. currently we support control version
// check and engine version check. Return value is a bit mask
//
// - IU_INIT_UPDATE_SYNC, cause Initialize() kicks off update engine
// process if already called by IU_INIT_CHECK and a new engine is available.
// When API returns, the update process is finished.
//
// - IU_INIT_UPDATE_ASYNC, cause Initialize() kicks off update engine
// process in Asynchronized mode if already called by IU_INIT_CHECK and
// a new engine is available. This API will return right after the
// update process starts.
//
// punkUpdateCompleteListener - this is a pointer to a user-implemented
// COM callback feature. It contains only one function OnComplete() that
// will be called when the engine update is done.
// This value can be NULL.
//
/////////////////////////////////////////////////////////////////////////////
STDMETHODIMP CUpdate::Initialize(LONG lInitFlag, IUnknown *punkUpdateCompleteListener, LONG *plRetVal)
{
HRESULT hr = S_OK;
LOG_Block("Initialize()");
if (!m_gfInit_csLock)
{
return E_OUTOFMEMORY;
}
TCHAR szFilePath[MAX_PATH + 1] = {0};
FILE_VERSION verControl;
int iCompareResult = 0;
DWORD dwErr = 0;
char szAnsiRequiredControlVersion[64];
LOG_Out(_T("Parameters: (0x%08x, 0x%08x, 0x%08x)"), lInitFlag, punkUpdateCompleteListener, plRetVal);
//
// we should to previlidge check first.
//
if (1 == IsWindowsUpdateUserAccessDisabled())
{
LOG_ErrorMsg(ERROR_SERVICE_DISABLED);
return HRESULT_FROM_WIN32(ERROR_SERVICE_DISABLED);
}
if (0x0 == GetLogonGroupInfo())
{
//
// if the current logon is neither member of admins nor power users
// or windows update is disabled, there is no need to continue
//
return E_ACCESSDENIED;
}
USES_CONVERSION;
EnterCriticalSection(&m_lock);
LPTSTR ptszLivePingServerUrl = NULL;
LPTSTR ptszCorpPingServerUrl = NULL;
if (NULL != (ptszCorpPingServerUrl = (LPTSTR)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, INTERNET_MAX_URL_LENGTH * sizeof(TCHAR))))
{
if (FAILED(g_pIUUrlAgent->GetCorpPingServer(ptszCorpPingServerUrl, INTERNET_MAX_URL_LENGTH)))
{
LOG_Out(_T("failed to get corp WU ping server URL"));
SafeHeapFree(ptszCorpPingServerUrl);
}
}
else
{
LOG_Out(_T("failed to allocate memory for ptszCorpPingServerUrl"));
}
if (IU_INIT_CHECK == lInitFlag)
{
// RAID: 453770 IU - IUCTL - Initialize check returns Engine update required
// after downloading new engine using Initialize Sync/Async
// Fix: initialize dwFlag to 0 rather than m_dwUpdateInfo (carried forward
// previous IU_UPDATE_ENGINE_BIT even when new engine had been brought down).
DWORD dwFlag = 0;
BOOL fEngineUpdate = FALSE;
FILE_VERSION verCurrent;
CleanUpIfFalseAndSetHrMsg((NULL == plRetVal), E_INVALIDARG);
hr = DetectEngine(&fEngineUpdate);
if (IU_SELFUPDATE_USENEWDLL == hr)
{
//
// found engine already been updated by someone,
// but not renamed to iuengine.dll yet
//
// doesn't matter to us,since we always try
// to load enginenew before we try to load eng.
//
hr = S_OK;
}
if (g_pIUUrlAgent->HasBeenPopulated())
{
ptszLivePingServerUrl = (LPTSTR)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, INTERNET_MAX_URL_LENGTH * sizeof(TCHAR));
CleanUpFailedAllocSetHrMsg(ptszLivePingServerUrl);
if (FAILED(g_pIUUrlAgent->GetLivePingServer(ptszLivePingServerUrl, INTERNET_MAX_URL_LENGTH)))
{
LOG_Out(_T("failed to get live ping server URL"));
SafeHeapFree(ptszLivePingServerUrl);
}
}
CleanUpIfFailedAndMsg(hr);
if (fEngineUpdate)
{
dwFlag = IU_UPDATE_ENGINE_BIT;
}
//
// get required version number of iuctl from iuident
//
GetIndustryUpdateDirectory(szFilePath);
CleanUpIfFailedAndSetHrMsg(PathCchAppend(szFilePath, ARRAYSIZE(szFilePath), IDENTTXT));
(void) GetPrivateProfileString(
_T("IUControl"),
_T("ControlVer"),
_T("0.0.0.0"),
m_szReqControlVer,
ARRAYSIZE(m_szReqControlVer),
szFilePath);
#ifdef UNICODE
WideCharToMultiByte(CP_ACP, 0, m_szReqControlVer, -1, szAnsiRequiredControlVersion,
sizeof(szAnsiRequiredControlVersion), NULL, NULL);
ConvertStringVerToFileVer(szAnsiRequiredControlVersion, &verControl);
#else
ConvertStringVerToFileVer(m_szReqControlVer, &verControl);
#endif
//
// get current iuctl.dll version number
//
szFilePath[0] = _T('\0');
if (0 == GetSystemDirectory(szFilePath, ARRAYSIZE(szFilePath)))
{
Win32MsgSetHrGotoCleanup(GetLastError());
}
CleanUpIfFailedAndSetHrMsg(PathCchAppend(szFilePath, ARRAYSIZE(szFilePath), _T("iuctl.dll")));
//
// change for bug fix 488487 by charlma 11/30/2001
// in order to output file ver to freelog, we dig out verionn here:
//
//
// if we failed to get the version, GetFileVersion() alraedy produced debug log.
// we just use 0.0.0.0 to do compare, since in that case we need to update it!
//
ZeroMemory((void*)(&verCurrent), sizeof(verCurrent));
if (GetFileVersion(szFilePath, &verCurrent))
{
LogMessage("Current iuctl.dll version: %d.%d.%d.%d",
verCurrent.Major,
verCurrent.Minor,
verCurrent.Build,
verCurrent.Ext);
}
iCompareResult = CompareFileVersion(verCurrent, verControl);
//CleanUpIfFailedAndSetHrMsg(CompareFileVersion(szFilePath, verControl, &iCompareResult));
if (iCompareResult < 0)
{
//
// if current control dll (szFilePath) has lower version
// then the one specified in ident
//
dwFlag |= IU_UPDATE_CONTROL_BIT;
#if defined(UNICODE) || defined(_UNICODE)
LogMessage("IUCtl needs update to %ls", m_szReqControlVer);
#else
LogMessage("IUCtl needs update to %s", m_szReqControlVer);
#endif
}
//
// also output engine version
//
if ((0 != GetSystemDirectory(szFilePath, ARRAYSIZE(szFilePath)) &&
SUCCEEDED(hr = PathCchAppend(szFilePath, ARRAYSIZE(szFilePath), _T("iuenginenew.dll"))) &&
GetFileVersion(szFilePath, &verCurrent)) ||
(0 != GetSystemDirectory(szFilePath, ARRAYSIZE(szFilePath)) &&
SUCCEEDED(hr = PathCchAppend(szFilePath, ARRAYSIZE(szFilePath), _T("iuengine.dll"))) &&
GetFileVersion(szFilePath, &verCurrent))
)
{
LogMessage("Current iuengine.dll version: %d.%d.%d.%d",
verCurrent.Major,
verCurrent.Minor,
verCurrent.Build,
verCurrent.Ext);
}
*plRetVal = (LONG) dwFlag;
m_dwUpdateInfo = dwFlag;
if (0x0 == dwFlag)
{
//
// no update needed. move to READY stage
//
m_lInitState = 2;
}
else
{
m_lInitState = 1; // we have update work to do!
}
}
else
{
BOOL fSync = (IU_INIT_UPDATE_SYNC == lInitFlag);
if (!fSync && (IU_INIT_UPDATE_ASYNC != lInitFlag))
{
//
// unknown flag
//
SetHrMsgAndGotoCleanUp(E_INVALIDARG);
}
if (1 != m_lInitState || (m_dwUpdateInfo & IU_UPDATE_CONTROL_BIT))
{
//
// if we are not indicated that update needed. this
// call shouldn't happen at all!
//
SetHrMsgAndGotoCleanUp(E_UNEXPECTED);
}
//
// we need to check update again before we kick off the
// actual update process since we don't know when last
// time you get the info saying we need to update. Probably
// it's already been updated, or is being updated.
//
// so we call the check function again but this time tell
// the check function that if update needed then do it.
//
hr = SelfUpdateCheck(fSync, TRUE, m_evtControlQuit, this, punkUpdateCompleteListener);
if (IU_SELFUPDATE_USENEWDLL == hr)
{
//
// found engine already been updated by someone,
// but not renamed to iuengine.dll yet
//
// doesn't matter to us,since we always try
// to load enginenew before we try to load eng.
//
m_lInitState = 2;
hr = S_OK;
}
if (fSync && SUCCEEDED(hr))
{
//
// synchronized update done and successful
//
m_lInitState = 2;
}
if (NULL != plRetVal)
{
*plRetVal = (LONG)hr; // result pass out: 0 or error code
}
}
if (2 == m_lInitState)
{
if (NULL == m_hEngineModule)
{
//
// check if iuengine new exist and validate the file
//
TCHAR szEnginePath[MAX_PATH + 1];
TCHAR szEngineNewPath[MAX_PATH + 1];
int cch = 0;
int iVerCheck = 0;
cch = GetSystemDirectory(szEnginePath, ARRAYSIZE(szEnginePath));
CleanUpIfFalseAndSetHrMsg(cch == 0 || cch >= ARRAYSIZE(szEnginePath), HRESULT_FROM_WIN32(GetLastError()));
(void) StringCchCopy(szEngineNewPath, ARRAYSIZE(szEngineNewPath), szEnginePath);
hr = PathCchAppend(szEnginePath, ARRAYSIZE(szEnginePath), ENGINEDLL);
CleanUpIfFailedAndMsg(hr);
hr = PathCchAppend(szEngineNewPath, ARRAYSIZE(szEngineNewPath), ENGINENEWDLL);
CleanUpIfFailedAndMsg(hr);
//
// try to verify trust of engine new
//
if (FileExists(szEngineNewPath) &&
S_OK == VerifyFileTrust(szEngineNewPath, NULL, ReadWUPolicyShowTrustUI()) &&
SUCCEEDED(CompareFileVersion(szEnginePath, szEngineNewPath, &iVerCheck)) &&
iVerCheck < 0)
{
//
// load the engine
//
m_hEngineModule = LoadLibraryFromSystemDir(_T("iuenginenew.dll"));
}
if (NULL != m_hEngineModule)
{
LOG_Internet(_T("IUCtl Using IUENGINENEW.DLL"));
}
else
{
LOG_Internet(_T("IUCtl Using IUENGINE.DLL"));
m_hEngineModule = LoadLibraryFromSystemDir(_T("iuengine.dll"));
if (NULL == m_hEngineModule)
{
dwErr = GetLastError();
LOG_ErrorMsg(dwErr);
hr = HRESULT_FROM_WIN32(dwErr);
}
}
//
// If load engine succeeded, get a CEngUpdate instance and start aynsc misc worker threads
//
if (NULL != m_hEngineModule)
{
#if defined(DBG)
// Log error if m_hIUEngine isn't NULL
if (NULL != m_hIUEngine)
{
LOG_Error(_T("m_hIUEngine should be NULL here!"));
}
#endif
PFN_CreateEngUpdateInstance pfnCreateEngUpdateInstance =
(PFN_CreateEngUpdateInstance) GetProcAddress(m_hEngineModule, "CreateEngUpdateInstance");
if (NULL != pfnCreateEngUpdateInstance)
{
m_hIUEngine = pfnCreateEngUpdateInstance();
}
if (NULL == m_hIUEngine)
{
hr = E_OUTOFMEMORY;
LOG_ErrorMsg(hr);
FreeLibrary(m_hEngineModule);
m_hEngineModule = NULL;
}
else
{
//
// If load engine and create instance succeeded, start aynsc misc worker threads
//
PFN_AsyncExtraWorkUponEngineLoad pfnAsyncExtraWorkUponEngineLoad =
(PFN_AsyncExtraWorkUponEngineLoad) GetProcAddress(m_hEngineModule, "AsyncExtraWorkUponEngineLoad");
if (NULL != pfnAsyncExtraWorkUponEngineLoad)
{
pfnAsyncExtraWorkUponEngineLoad();
}
}
}
}
if (IU_INIT_UPDATE_ASYNC == lInitFlag && SUCCEEDED(hr))
{
//
// this is a rare case: the previous Iniitalize() call tells
// that engine update needed, but now, when we try to update it
// in async mode, we found it's no longer true.
// Must be some other process already complete the engine update
// task since then. But it may or may not completed the change
// file name process yet.
//
// For us, we just need to signal that we are done to update.
//
//
// signal callback
//
IUpdateCompleteListener* pCallback = NULL;
if (NULL != punkUpdateCompleteListener && (SUCCEEDED(hr = punkUpdateCompleteListener->QueryInterface(IID_IUpdateCompleteListener, (void**) &pCallback))))
{
pCallback->OnComplete(dwErr);
pCallback->Release();
LOG_Out(_T("Returned from callback API OnComplete()"));
}
else
{
//
// signal event if user has not passed in a progress listner IUnknown ptr
//
HWND hWnd = m_EvtWindow.GetEvtHWnd();
if (NULL != hWnd)
{
PostMessage(hWnd, UM_EVENT_SELFUPDATE_COMPLETE, 0, (LPARAM)dwErr);
LOG_Out(_T("Fired event OnComplete()"));
}
}
hr = S_OK;
}
}
CleanUp:
PingEngineUpdate(
m_hEngineModule,
&g_hEngineLoadQuit,
1,
ptszLivePingServerUrl,
ptszCorpPingServerUrl,
hr,
_T("IU_SITE")); // Only the site (other than test) calls this function
LeaveCriticalSection(&m_lock);
SafeHeapFree(ptszLivePingServerUrl);
SafeHeapFree(ptszCorpPingServerUrl);
return hr;
}
HRESULT CUpdate::ChangeControlInitState(LONG lNewState)
{
HRESULT hr = S_OK;
LOG_Block("ChangeControlInitState()");
if (!m_gfInit_csLock)
{
return E_OUTOFMEMORY;
}
EnterCriticalSection(&m_lock);
m_lInitState = lNewState;
if (2 == m_lInitState && NULL == m_hEngineModule)
{
//
// load the engine
//
m_hEngineModule = LoadLibraryFromSystemDir(_T("iuenginenew.dll"));
if (NULL != m_hEngineModule)
{
LOG_Internet(_T("IUCtl Using IUENGINENEW.DLL"));
}
else
{
LOG_Internet(_T("IUCtl Using IUENGINE.DLL"));
m_hEngineModule = LoadLibraryFromSystemDir(_T("iuengine.dll"));
if (NULL == m_hEngineModule)
{
DWORD dwErr = GetLastError();
LOG_ErrorMsg(dwErr);
hr = HRESULT_FROM_WIN32(dwErr);
}
}
//
// Create the CEngUpdate instance
//
if (NULL != m_hEngineModule)
{
#if defined(DBG)
// Log error if m_hIUEngine isn't NULL
if (NULL != m_hIUEngine)
{
LOG_Error(_T("m_hIUEngine should be NULL here!"));
}
#endif
PFN_CreateEngUpdateInstance pfnCreateEngUpdateInstance =
(PFN_CreateEngUpdateInstance) GetProcAddress(m_hEngineModule, "CreateEngUpdateInstance");
if (NULL != pfnCreateEngUpdateInstance)
{
m_hIUEngine = pfnCreateEngUpdateInstance();
}
if (NULL == m_hIUEngine)
{
hr = E_OUTOFMEMORY;
LOG_ErrorMsg(hr);
FreeLibrary(m_hEngineModule);
m_hEngineModule = NULL;
}
}
}
LeaveCriticalSection(&m_lock);
return hr;
}
STDMETHODIMP CUpdate::PrepareSelfUpdate(LONG lStep)
{
return E_NOTIMPL;
}
/////////////////////////////////////////////////////////////////////////////
//
// Helper API to let the caller (script) knows the necessary information
// when Initialize() returns control need updated.
//
// For the current implementation, bstrClientName is ignored, and
// the returned bstr has format:
// "<version>|<url>"
// where:
// <version> is the expacted version number of the control
// <url> is the base url to get the control if this is a CorpWU policy controlled machine,
// or empty if this is a consumer machine (in that case caller, i.e., script, knows
// the default base url, which is the v4 live site)
//
// Script will need these two pieces of information in order to make a right <OBJECT> tag
// for control update.
//
/////////////////////////////////////////////////////////////////////////////
STDMETHODIMP CUpdate::GetControlExtraInfo(BSTR bstrClientName, BSTR *pbstrExtraInfo)
{
return E_NOTIMPL;
}
/////////////////////////////////////////////////////////////////////////////
//
// new Win32 API called by wrapper control to retrieve update info
//
//
/////////////////////////////////////////////////////////////////////////////
int GetControlUpdateInfo(LPTSTR lpszUpdateInfo, int cchBufferSize)
{
LOG_Block("GetControlUpdateInfo()");
HRESULT hr = S_OK;
int nSize = 0;
BOOL fCorpUser = FALSE, fBetaSelfUpdate = FALSE;
FILE_VERSION fvCurrentCtl, fvCurrentEngine;
TCHAR szDir[MAX_PATH];
TCHAR szFile[MAX_PATH];
TCHAR szExpectedEngVer[64]; // 64 should be more then enough
TCHAR szExpectedCtlVer[64]; // if not enough, then it's bad data anyway
HKEY hKey;
DWORD dwErr = ERROR_SUCCESS; // error code for this API
if (1 == IsWindowsUpdateUserAccessDisabled())
{
dwErr = ERROR_SERVICE_DISABLED;
LOG_ErrorMsg(ERROR_SERVICE_DISABLED);
goto CleanUp;
}
if (0x0 == GetLogonGroupInfo())
{
dwErr = ERROR_ACCESS_DENIED;
goto CleanUp;
}
if (FAILED(hr = DownloadIUIdent_PopulateData()))
{
LOG_ErrorMsg(hr);
dwErr = hr;
goto CleanUp;
}
//
// get current control ver
//
GetSystemDirectory(szDir, ARRAYSIZE(szDir));
hr = PathCchCombine(szFile, ARRAYSIZE(szFile), szDir,IUCTL);
if (FAILED(hr))
{
LOG_ErrorMsg(hr);
dwErr = hr;
goto CleanUp;
}
if (!GetFileVersion(szFile, &fvCurrentCtl))
{
ZeroMemory(&fvCurrentCtl, sizeof(fvCurrentCtl));
}
//
// get current engine ver
//
hr = PathCchCombine(szFile, ARRAYSIZE(szFile), szDir,ENGINENEWDLL);
if (FAILED(hr))
{
LOG_ErrorMsg(hr);
dwErr = hr;
goto CleanUp;
}
if (!GetFileVersion(szFile, &fvCurrentEngine))
{
//
// if no engine new there, check engine ver
//
hr = PathCchCombine(szFile, ARRAYSIZE(szFile), szDir,ENGINEDLL);
if (FAILED(hr))
{
LOG_ErrorMsg(hr);
dwErr = hr;
goto CleanUp;
}
if (!GetFileVersion(szFile, &fvCurrentEngine))
{
ZeroMemory(&fvCurrentCtl, sizeof(fvCurrentEngine));
}
}
//
// check if this is beta code
//
if (ERROR_SUCCESS == RegOpenKeyEx(HKEY_LOCAL_MACHINE, REGKEY_IUCTL,0, KEY_READ, &hKey))
{
// Check for Beta IU SelfUpdate Handling Requested
DWORD dwStatus = 0;
DWORD dwSize = sizeof(dwStatus);
DWORD dwRet = RegQueryValueEx(hKey, REGVAL_BETASELFUPDATE, NULL, NULL, (LPBYTE)&dwStatus, &dwSize);
if (1 == dwStatus)
{
fBetaSelfUpdate = TRUE;
}
RegCloseKey(hKey);
}
//
// get expected control ver
//
GetIndustryUpdateDirectory(szDir);
hr = PathCchCombine(szFile, ARRAYSIZE(szFile), szDir,IDENTTXT);
if (FAILED(hr))
{
LOG_ErrorMsg(hr);
dwErr = hr;
goto CleanUp;
}
GetPrivateProfileString(_T("IUControl"),
_T("ControlVer"),
_T(""),
szExpectedCtlVer,
ARRAYSIZE(szExpectedCtlVer),
szFile);
if ('\0' == szExpectedCtlVer[0])
{
//
// no selfupdate available, no server version information. bad ident?
//
dwErr = ERROR_FILE_CORRUPT;
goto CleanUp;
}
//
// get expected engine ver
//
GetIndustryUpdateDirectory(szDir);
hr = PathCchCombine(szFile, ARRAYSIZE(szFile), szDir,IDENTTXT);
if (FAILED(hr))
{
LOG_ErrorMsg(hr);
dwErr = hr;
goto CleanUp;
}
GetPrivateProfileString(fBetaSelfUpdate ? IDENT_IUBETASELFUPDATE : IDENT_IUSELFUPDATE,
IDENT_VERSION,
_T(""),
szExpectedEngVer,
ARRAYSIZE(szExpectedEngVer),
szFile);
if ('\0' == szExpectedEngVer[0])
{
//
// no selfupdate available, no server version information. bad ident?
//
dwErr = ERROR_FILE_CORRUPT;
goto CleanUp;
}
hr = g_pIUUrlAgent->IsIdentFromPolicy();
if (FAILED(hr))
{
dwErr = (DWORD)hr;
goto CleanUp;
}
fCorpUser = (S_OK == hr) ? TRUE : FALSE;
hr = S_OK;
//
// contscut data
// the constructed buffer will be in format
// <CurrentCtlVer>|<ExpCtlVer>|CurrentEngVer>|<ExpEngVer>|<baseUrl>
//
nSize = wnsprintf(lpszUpdateInfo, cchBufferSize, _T("%d.%d.%d.%d|%s|%d.%d.%d.%d|%s|%d"),
fvCurrentCtl.Major, fvCurrentCtl.Minor, fvCurrentCtl.Build, fvCurrentCtl.Ext,
szExpectedCtlVer,
fvCurrentEngine.Major, fvCurrentEngine.Minor, fvCurrentEngine.Build, fvCurrentEngine.Ext,
szExpectedEngVer,
fCorpUser ? 1 : 0);
if (nSize < 0)
{
nSize = 0;
dwErr = ERROR_INSUFFICIENT_BUFFER;
goto CleanUp;
}
CleanUp:
SetLastError(dwErr);
return nSize;
}