//======================================================================= // // 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 #include #include #include #include #include #include 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 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: // // // // // 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. // ///////////////////////////////////////////////////////////////////////////// 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. // ///////////////////////////////////////////////////////////////////////////// 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. // ///////////////////////////////////////////////////////////////////////////// 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::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::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: // "|" // where: // is the expacted version number of the control // 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 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 // ||CurrentEngVer>|| // 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; }