|
|
//=======================================================================
//
// Copyright (c) 1998-2000 Microsoft Corporation. All Rights Reserved.
//
// File: downld.cpp
//
// Description:
//
// Implementation for the Download() function
//
//=======================================================================
#include "iuengine.h" // PCH - must include first
#include <iu.h>
#include <iucommon.h>
#include <download.h>
#include <trust.h>
#include <wininet.h>
#include <fileutil.h>
#include <shlwapi.h>
#include "iuxml.h"
#include "history.h"
#include <schemakeys.h>
//#include <serverPing.h> changed to use urllogging.h.
#include <intshcut.h>
#include <schemamisc.h>
#include <WaitUtil.h>
#include <urllogging.h>
#define MAX_CORPORATE_PATH 100
// named mutex used to update historical speed/time information in the registry.
const TCHAR IU_MUTEX_HISTORICALSPEED_REGUPDATE[] = _T("{5f3255a9-9051-49b1-80b9-aac31c092af4}"); const TCHAR IU_READMORE_LINK_NAME[] = _T("ReadMore.url"); const CHAR SZ_DOWNLOAD_FINISHED[] = "Download finished";
const LONG UPDATE_COMMAND = 0x0000000F;
typedef struct IUDOWNLOADSTARTUPINFO { BSTR bstrClientName; BSTR bstrXmlCatalog; BSTR bstrDestinationFolder; LONG lMode; IUnknown *punkProgressListener; HWND hwnd; BSTR bstrUuidOperation; CEngUpdate* pEngUpdate; } IUDOWNLOADSTARTUPINFO, *PIUDOWNLOADSTARTUPINFO;
// --------------------------------------------------------------------
// function forward declarations
// --------------------------------------------------------------------
//
// Callback function to provide status from the downloader
//
BOOL WINAPI DownloadCallback(VOID* pCallbackData, DWORD dwStatus, DWORD dwBytesTotal, DWORD dwBytesComplete, BSTR bstrXmlData, LONG* plCommandRequest);
//
// thread function used by DownloadAsync
//
DWORD WINAPI DownloadThreadProc(LPVOID lpv);
/////////////////////////////////////////////////////////////////////////////
// CreateReadMoreLink()
//
// If the item contains a "description/descriptionText/details" node, suck
// out the URL and create a shortcut for it in the destination folder
//
// Input:
// pxmlCatalog - CXmlCatalog containing downloaded items
// hItem - Handle to current download item in the catalog
// pszDestinationFolder - Folder where item is downloaded
//
// Return:
// S_OK - Wrote the ReadMore.htm link
// S_FALSE - details node didn't exist in item
// <other> - HRESULT returned from calling other functions
/////////////////////////////////////////////////////////////////////////////
HRESULT CreateReadMoreLink(CXmlCatalog* pxmlCatalog, HANDLE_NODE hItem, LPCTSTR pszDestinationFolder) { USES_IU_CONVERSION;
LOG_Block("CreateReadMoreLink");
IXMLDOMNode* pItemNode = NULL; IXMLDOMNode* pReadMoreNode = NULL; IUniformResourceLocator* purl = NULL; IPersistFile* ppf = NULL; HRESULT hr; TCHAR szShortcut[MAX_PATH]; BSTR bstrURL = NULL;
if (NULL == pxmlCatalog || HANDLE_NODE_INVALID == hItem || NULL == pszDestinationFolder) { CleanUpIfFailedAndSetHrMsg(E_INVALIDARG); }
//
// Get <item> node in catalog
//
if (NULL == (pItemNode = pxmlCatalog->GetDOMNodebyHandle(hItem))) { CleanUpIfFailedAndSetHrMsg(E_INVALIDARG); } //
// Get node containing ReadMore URL, or S_FALSE if it doesn't exist
//
hr = pItemNode->selectSingleNode(KEY_READMORE, &pReadMoreNode); if (S_OK != hr) { if (S_FALSE != hr) { LOG_ErrorMsg(hr); } goto CleanUp; }
//
// suck out the href attribute
//
CleanUpIfFailedAndSetHrMsg(GetAttribute(pReadMoreNode, KEY_HREF, &bstrURL));
// Get pointers to the IID_IUniformResourceLocator and IID_IPersistFile interfaces
// on the CLSID_InternetShortcut object
CleanUpIfFailedAndSetHrMsg(CoCreateInstance(CLSID_InternetShortcut, \ NULL, \ CLSCTX_INPROC_SERVER, \ IID_IUniformResourceLocator, \ (LPVOID*)&purl));
CleanUpIfFailedAndSetHrMsg(purl->QueryInterface(IID_IPersistFile, (LPVOID*)&ppf)); // We want to check the URL we got from the Manifest Data to make sure it is a HTTP URL, not some local file spec
URL_COMPONENTS UrlComponents; // Break down the URL to get the Protocol Used
// Specifically we need the server name, object to download, username and
// password information.
TCHAR szScheme[32]; szScheme[0] = _T('\0'); ZeroMemory(&UrlComponents, sizeof(UrlComponents)); UrlComponents.dwStructSize = sizeof(UrlComponents); UrlComponents.lpszScheme = szScheme; UrlComponents.dwSchemeLength = ARRAYSIZE(szScheme);
if (!InternetCrackUrl(OLE2T(bstrURL), 0, 0, &UrlComponents)) { LOG_ErrorMsg(HRESULT_FROM_WIN32(GetLastError())); goto CleanUp; }
if (szScheme[0] == _T('\0') || (0 != lstrcmpi(szScheme, _T("http")) && 0 != lstrcmpi(szScheme, _T("https")))) { // If the Scheme was undeterminable, or the scheme is not HTTP then we shouldn't trust this URL.
LOG_ErrorMsg(E_UNEXPECTED); goto CleanUp; } //
// Set the URL, form the shortcut path, and write the link
//
CleanUpIfFailedAndSetHrMsg(purl->SetURL(OLE2T(bstrURL), 0));
hr = StringCchCopyEx(szShortcut, ARRAYSIZE(szShortcut), pszDestinationFolder, NULL, NULL, MISTSAFE_STRING_FLAGS); CleanUpIfFailedAndSetHrMsg(hr);
hr = PathCchAppend(szShortcut, ARRAYSIZE(szShortcut), IU_READMORE_LINK_NAME); CleanUpIfFailedAndSetHrMsg(hr);
CleanUpIfFailedAndSetHrMsg(ppf->Save(T2OLE(szShortcut), FALSE));
CleanUp:
SysFreeString(bstrURL); // pItemNode is owned by CXmlCatalog, don't release
SafeReleaseNULL(pReadMoreNode); SafeReleaseNULL(ppf); SafeReleaseNULL(purl);
return hr; }
/////////////////////////////////////////////////////////////////////////////
// CreateItemDependencyList()
//
// If the item contains a "dependencies" node, we want to walk
// the dependendant Item List and list the proper order that installs should
// be done in. If a dependant Item is not available in the current catalog it
// will be ignored.
//
// Input:
// pxmlCatalog - CXmlCatalog containing downloaded items
// hItem - Handle to current download item in the catalog
// pszDestinationFolder - Folder where item is downloaded
//
// Return:
// S_OK - Dependency List Written
// S_FALSE - No Dependencies Available
// <other> - HRESULT returned from calling other functions
/////////////////////////////////////////////////////////////////////////////
HRESULT CreateItemDependencyList(CXmlCatalog* pxmlCatalog, HANDLE_NODE hItem, LPCTSTR pszDestinationFolder) { HRESULT hr = S_FALSE;
HANDLE_NODE hDependentItemList = HANDLE_NODELIST_INVALID; HANDLE_NODE hDependentItem = HANDLE_NODE_INVALID; int iDependentItemOrder = 1; HANDLE hFile = INVALID_HANDLE_VALUE; DWORD dwBytesWritten; TCHAR szFileName[MAX_PATH]; char szWriteBuffer[MAX_PATH + 12]; // max_path is the safe length for the identitystr plus room for the order information
BSTR bstrIdentityStr = NULL; BOOL fWroteItem = FALSE; USES_IU_CONVERSION; hDependentItemList = pxmlCatalog->GetFirstItemDependency(hItem, &hDependentItem); if (HANDLE_NODELIST_INVALID != hDependentItemList) { hr = PathCchCombine(szFileName, ARRAYSIZE(szFileName), pszDestinationFolder, _T("order.txt")); if (FAILED(hr)) { pxmlCatalog->CloseItem(hDependentItem); pxmlCatalog->CloseItemList(hDependentItemList); return hr; } hFile = CreateFile(szFileName, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); if (INVALID_HANDLE_VALUE == hFile) { hr = HRESULT_FROM_WIN32(GetLastError()); pxmlCatalog->CloseItem(hDependentItem); pxmlCatalog->CloseItemList(hDependentItemList); return hr; }
hr = S_OK; while (hr == S_OK) { if (HANDLE_NODELIST_INVALID != hDependentItem) { pxmlCatalog->GetIdentityStr(hDependentItem, &bstrIdentityStr);
hr = StringCchPrintfExA(szWriteBuffer, ARRAYSIZE(szWriteBuffer), NULL, NULL, MISTSAFE_STRING_FLAGS, "%d = %s\r\n", iDependentItemOrder, OLE2A(bstrIdentityStr)); if (FAILED(hr)) { SafeSysFreeString(bstrIdentityStr); pxmlCatalog->CloseItem(hDependentItem); pxmlCatalog->CloseItemList(hDependentItemList); return hr; } WriteFile(hFile, szWriteBuffer, lstrlenA(szWriteBuffer), &dwBytesWritten, NULL); iDependentItemOrder++;
SafeSysFreeString(bstrIdentityStr); pxmlCatalog->CloseItem(hDependentItem); fWroteItem = TRUE; } hr = pxmlCatalog->GetNextItemDependency(hDependentItemList, &hDependentItem); }
pxmlCatalog->CloseItemList(hDependentItemList); CloseHandle(hFile); if (!fWroteItem) DeleteFile(szFileName); // no dependencies written
if (SUCCEEDED(hr)) hr = S_OK; // convert S_FALSE to S_OK, we successfully wrote the dependencylist
} return hr; }
/////////////////////////////////////////////////////////////////////////////
// _Download()
//
// Do synchronous downloading.
// Input:
// bstrClientName - the name of the client, for history logging use
// bstrXmlCatalog - the xml catalog portion containing items to be downloaded
// bstrDestinationFolder - the destination folder. Null will use the default IU folder
// lMode - bitmask indicates throttled/foreground and notification options
// punkProgressListener - the callback function pointer for reporting download progress
// hWnd - the event msg window handler passed from the stub
// Output:
// pbstrXmlItems - the items with download status in xml format
// e.g.
// <id guid="2560AD4D-3ED3-49C6-A937-4368C0B0E06D" downloaded="1"/>
/////////////////////////////////////////////////////////////////////////////
HRESULT _Download(BSTR bstrClientName, BSTR bstrXmlCatalog, BSTR bstrDestinationFolder, LONG lMode, IUnknown *punkProgressListener, HWND hWnd, BSTR bstrUuidOperation, BSTR *pbstrXmlItems, CEngUpdate* pEngUpdate) { LOG_Block("Download()");
HRESULT hr = S_OK; HRESULT hrGlobalItemFailure = S_OK; LPTSTR lpszClientInfo = NULL; TCHAR szBaseDestinationFolder[MAX_PATH]; TCHAR szDestinationFolder[MAX_PATH]; TCHAR szItemPath[MAX_PATH]; LPTSTR pszCabUrl = NULL; HANDLE_NODE hCatalogItemList = HANDLE_NODELIST_INVALID; HANDLE_NODE hProviderList = HANDLE_NODELIST_INVALID; HANDLE_NODE hItem = HANDLE_NODE_INVALID; HANDLE_NODE hProvider = HANDLE_NODE_INVALID; HANDLE_NODE hXmlItem = HANDLE_NODE_INVALID; HANDLE_NODE hItemCabList = HANDLE_NODELIST_INVALID; BSTR bstrCabUrl = NULL; BSTR bstrLocalFileName = NULL; BSTR bstrProviderName = NULL; BSTR bstrProviderPublisher = NULL; BSTR bstrProviderUUID = NULL; BSTR bstrProviderIdentityStr = NULL; BSTR bstrItemPath = NULL; BSTR bstrInstallerType = NULL; BSTR bstrLanguage = NULL; BSTR bstrPlatformDir = NULL; BSTR bstrTemp = NULL; BSTR bstrCRC = NULL; BOOL fCabPatchAvail; BOOL fReboot; BOOL fExclusive; LONG lCommandCount; LONG lCabSize = 0; LPTSTR pszLocalFileName = NULL; LPTSTR pszAllocatedFileName = NULL; BOOL fNTFSDriveAvailable = FALSE; TCHAR szFileSystemType[12]; TCHAR szLargestFATDrive[4]; int iMaxNTFSDriveFreeSpace = 0; int iMaxDriveFreeSpace = 0; BOOL fCorpCase = FALSE; BOOL fContinue = TRUE; // for async mode
BOOL fUseSuppliedPath = FALSE; long n; DWORD dwBytesDownloaded = 0; DWORD dwCount1, dwCount2, dwElapsedTime; DWORD dwTotalElapsedTime = 0; DWORD dwTotalBytesDownloaded = 0; DWORD dwWaitResult; DWORD dwHistoricalSpeed = 0; DWORD dwHistoricalTime = 0; DWORD dwSize; DWORD dwRet; HKEY hkeyIU = NULL; HANDLE hMutex = NULL; DCB_DATA CallbackData;
{ CXmlCatalog xmlCatalog; CXmlItems xmlItemList; LPTSTR ptszLivePingServerUrl = NULL; LPTSTR ptszCorpPingServerUrl = NULL; DWORD dwFlags = 0;
// clear any previous cancel event
ResetEvent(pEngUpdate->m_evtNeedToQuit);
ZeroMemory(&CallbackData, sizeof(CallbackData)); CallbackData.pOperationMgr = &pEngUpdate->m_OperationMgr;
USES_IU_CONVERSION;
CIUHistory history;
lpszClientInfo = OLE2T(bstrClientName);
if (NULL != (ptszLivePingServerUrl = (LPTSTR)HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, INTERNET_MAX_URL_LENGTH * sizeof(TCHAR)))) { if (FAILED(g_pUrlAgent->GetLivePingServer(ptszLivePingServerUrl, INTERNET_MAX_URL_LENGTH))) { LOG_Out(_T("failed to get live ping server URL")); SafeHeapFree(ptszLivePingServerUrl); } } else { LOG_Out(_T("failed to allocate memory for ptszLivePingServerUrl")); }
if (NULL != (ptszCorpPingServerUrl = (LPTSTR)HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, INTERNET_MAX_URL_LENGTH * sizeof(TCHAR)))) { if (FAILED(g_pUrlAgent->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")); }
CUrlLog pingSvr(lpszClientInfo, ptszLivePingServerUrl, ptszCorpPingServerUrl);
SafeHeapFree(ptszLivePingServerUrl); SafeHeapFree(ptszCorpPingServerUrl);
if (FAILED(hr = g_pUrlAgent->IsClientSpecifiedByPolicy(lpszClientInfo))) { LOG_ErrorMsg(hr); goto CleanUp; }
//
// Set the flags for use by DownloadFile
//
if (S_FALSE == hr) { dwFlags = 0; hr = S_OK; } else // S_OK
{ dwFlags = WUDF_DONTALLOWPROXY; LOG_Internet(_T("WUDF_DONTALLOWPROXY set")); }
pszCabUrl = (LPTSTR) HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, INTERNET_MAX_URL_LENGTH * sizeof(TCHAR)); if (NULL == pszCabUrl) { dwRet = GetLastError(); hr = HRESULT_FROM_WIN32(dwRet); LOG_ErrorMsg(hr); goto CleanUp; }
CallbackData.bstrOperationUuid = (NULL == bstrUuidOperation) ? NULL : SysAllocString(bstrUuidOperation); CallbackData.hEventFiringWnd = hWnd; if (NULL != punkProgressListener) { // get the IProgressListener interface pointer from the IUnknown pointer.. If the
// interface is not supported the pProgressListener is set to NULL
punkProgressListener->QueryInterface(IID_IProgressListener, (void**)&CallbackData.pProgressListener); } else { CallbackData.pProgressListener = NULL; }
// Check for Corporate Download Handling Mode
if ((DWORD) lMode & (DWORD) UPDATE_CORPORATE_MODE) { fCorpCase = TRUE; }
// Check for Progress Notification Requested Mode
if ((DWORD) lMode & (DWORD) UPDATE_NOTIFICATION_10PCT) { CallbackData.flProgressPercentage = (float).10; } else if ((DWORD) lMode & (DWORD) UPDATE_NOTIFICATION_5PCT) { CallbackData.flProgressPercentage = (float).05; } else if ((DWORD) lMode & (DWORD) UPDATE_NOTIFICATION_1PCT) { CallbackData.flProgressPercentage = (float).01; } else if ((DWORD) lMode & (DWORD) UPDATE_NOTIFICATION_COMPLETEONLY) { CallbackData.flProgressPercentage = (float) 1; } else { CallbackData.flProgressPercentage = 0; }
if (NULL != bstrDestinationFolder && 0 < SysStringLen(bstrDestinationFolder)) { if (SysStringLen(bstrDestinationFolder) > MAX_CORPORATE_PATH) { hr = E_INVALIDARG; LOG_ErrorMsg(hr); LogMessage("Catalog Download Path Greater Than (%d)", MAX_CORPORATE_PATH); goto CleanUp; }
// Caller specified a Base Path - Set this Flag so we don't create our temp folder
// structure under this path.
fUseSuppliedPath = TRUE;
//
// user passed in a designated path, this is to signal the
// download-no-install case, usually for corporate site
//
hr = StringCchCopyEx(szBaseDestinationFolder, ARRAYSIZE(szBaseDestinationFolder), OLE2T(bstrDestinationFolder), NULL, NULL, MISTSAFE_STRING_FLAGS); if (FAILED(hr)) { LOG_ErrorMsg(hr); goto CleanUp; } //
// verify that we have write access to this folder
// --- most likely it's a UNC path
//
DWORD dwErr = ValidateFolder(szBaseDestinationFolder, TRUE); if (ERROR_SUCCESS != dwErr) { LOG_ErrorMsg(dwErr); goto CleanUp; }
//
// Find out if this Path is a UNC
//
if ('\\' == szBaseDestinationFolder[0] && '\\' == szBaseDestinationFolder[1]) { // correct the path to the UNC to get the available space
hr = StringCchCopyEx(szDestinationFolder, ARRAYSIZE(szDestinationFolder), szBaseDestinationFolder, NULL, NULL, MISTSAFE_STRING_FLAGS); if (FAILED(hr)) { LOG_ErrorMsg(hr); goto CleanUp; }
LPTSTR pszWalk = szDestinationFolder; pszWalk += 2; // skip the double slash
pszWalk = StrChr(pszWalk, '\\'); // find the next slash (separate machine and share name)
pszWalk += 1; pszWalk = StrChr(pszWalk, '\\'); // try to find the next slash (end of share name)
if (NULL == pszWalk) { // no trailing slash and no further path information
hr = PathCchAddBackslash(szDestinationFolder, ARRAYSIZE(szBaseDestinationFolder)); if (FAILED(hr)) { LOG_ErrorMsg(hr); goto CleanUp; } } else { // this path has a trailing slash (may have more path information, truncate after the slash)
pszWalk += 1; *pszWalk = '\0'; } GetFreeDiskSpace(szDestinationFolder, &iMaxDriveFreeSpace); } else { // path must be a local drive
GetFreeDiskSpace(szBaseDestinationFolder[0], &iMaxDriveFreeSpace); } } else { //
// user passed in NULL as the destination folder,
// it means this is the normal case to download and install
// updates for this machine. we will try to find the
// drive with the most free space
//
TCHAR szDriveList[MAX_PATH]; GetLogicalDriveStrings(MAX_PATH, szDriveList); LPTSTR pszCurrent = szDriveList; int iSize;
//
// find the local fixed drive with the 'most' free space
//
while (NULL != pszCurrent && *pszCurrent != _T('\0')) { fContinue = (WaitForSingleObject(pEngUpdate->m_evtNeedToQuit, 0) != WAIT_OBJECT_0); if (DRIVE_FIXED == GetDriveType(pszCurrent)) { hr = GetFreeDiskSpace(*pszCurrent, &iSize); if (FAILED(hr)) { LOG_Error(_T("Error Reading Drive Space %c, hr = 0x%08x"), *pszCurrent, hr); pszCurrent += (lstrlen(pszCurrent) + 1); // skip current and null terminater
continue; }
if (!GetVolumeInformation(pszCurrent, NULL, 0, NULL, NULL, NULL, szFileSystemType, ARRAYSIZE(szFileSystemType))) { DWORD dwErr = GetLastError(); LOG_Error(_T("Error Reading VolumeInfo for Drive %c, GLE = %d"), *pszCurrent, dwErr); pszCurrent += (lstrlen(pszCurrent) + 1); // skip current and null terminater
continue; } if (CSTR_EQUAL == CompareString(MAKELCID(MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US), SORT_DEFAULT), NORM_IGNORECASE, szFileSystemType, -1, _T("NTFS"), -1)) { fNTFSDriveAvailable = TRUE; if (iSize > iMaxNTFSDriveFreeSpace) { iMaxNTFSDriveFreeSpace = iSize; hr = StringCchCopyEx(szBaseDestinationFolder, ARRAYSIZE(szBaseDestinationFolder), pszCurrent, NULL, NULL, MISTSAFE_STRING_FLAGS); if (FAILED(hr)) { LOG_ErrorMsg(hr); continue; } } } else { // we want to keep track of non NTFS drive sizes in case there the largest
// NTFS drive size is too small, but a FAT partition has enough space. In this
// case we want to fall back to the FAT partition. Note: this is a behavior change
// from the initial design where we treated NTFS and FAT as mutually exclusive with
// NTFS always winning.
if (iSize > iMaxDriveFreeSpace) { iMaxDriveFreeSpace = iSize; if (!fNTFSDriveAvailable) { // if no NTFS drive is available save this drive letter as the preferred
hr = StringCchCopyEx(szBaseDestinationFolder, ARRAYSIZE(szBaseDestinationFolder), pszCurrent, NULL, NULL, MISTSAFE_STRING_FLAGS); if (FAILED(hr)) { LOG_ErrorMsg(hr); continue; }
hr = StringCchCopyEx(szLargestFATDrive, ARRAYSIZE(szLargestFATDrive), pszCurrent, NULL, NULL, MISTSAFE_STRING_FLAGS); if (FAILED(hr)) { LOG_ErrorMsg(hr); continue; } } else { // NTFS drive exists, save this drive as a back up choice for the size check.
hr = StringCchCopyEx(szLargestFATDrive, ARRAYSIZE(szLargestFATDrive), pszCurrent, NULL, NULL, MISTSAFE_STRING_FLAGS); if (FAILED(hr)) { LOG_ErrorMsg(hr); continue; } } } } } pszCurrent += (lstrlen(pszCurrent) + 1); // skip current and null terminater
}
if (!fContinue) { hr = E_UNEXPECTED; goto CleanUp; }
if ((0 == iMaxDriveFreeSpace) && (0 == iMaxNTFSDriveFreeSpace)) { //
// running on a system with no local drives?
//
hr = E_FAIL; LOG_ErrorMsg(hr); goto CleanUp; } }
//
// load the XML document into the XmlCatalog Class
//
if (WaitForSingleObject(pEngUpdate->m_evtNeedToQuit, 0) == WAIT_OBJECT_0) { hr = E_ABORT; goto CleanUp; }
hr = xmlCatalog.LoadXMLDocument(bstrXmlCatalog, pEngUpdate->m_fOfflineMode); if (FAILED(hr)) { LOG_ErrorMsg(hr); goto CleanUp; }
// We need to find the total estimated size of the download we're about to do.
// We'll walk the XML Catalog getting Size Info for each item.
hr = xmlCatalog.GetTotalEstimatedSize(&CallbackData.lTotalDownloadSize); if (FAILED(hr)) { LOG_ErrorMsg(hr); goto CleanUp; } CallbackData.lTotalDownloaded = 0;
//
// added by JHou - bug#314: download does not detect available free space on local hard drive
//
// The lTotalDownloadSize is the size of the download in Bytes, the MaxDriveSpace is in KBytes
if ((CallbackData.lTotalDownloadSize / 1024) > ((fNTFSDriveAvailable) ? iMaxNTFSDriveFreeSpace : iMaxDriveFreeSpace)) { // Before we bail out of the download we need to look to see if we excluded a chose a NTFS drive
// over a FAT drive. If the NTFS drive doesn't have enough space, but a FAT drive does we want to
// go ahead and allow the use of the FAT drive. This is a change in spec'd behavior per bug: 413079
if ((CallbackData.lTotalDownloadSize / 1024) < iMaxDriveFreeSpace) { // no error.. a FAT partition has enough free space, use it instead
hr = StringCchCopyEx(szBaseDestinationFolder, ARRAYSIZE(szBaseDestinationFolder), szLargestFATDrive, NULL, NULL, MISTSAFE_STRING_FLAGS); if (FAILED(hr)) { LOG_ErrorMsg(hr); goto CleanUp; } } else { // tried both NTFS and FAT partitions.. none have enough space.. bail out.
dwRet = ERROR_DISK_FULL; LOG_ErrorMsg(dwRet); hr = HRESULT_FROM_WIN32(dwRet); // need to write items result information for each item indicating it failed because of diskspace
hrGlobalItemFailure = HRESULT_FROM_WIN32(dwRet); } }
if (SUCCEEDED(hrGlobalItemFailure)) { if (!fUseSuppliedPath) { // When a destination folder is specified, we don't need to add anything to it. If no path
// is specified we pick a drive letter, so we need to add the WUTemp directory
// to that base path.
hr = StringCchCatEx(szBaseDestinationFolder, ARRAYSIZE(szBaseDestinationFolder), IU_WUTEMP, NULL, NULL, MISTSAFE_STRING_FLAGS); if (FAILED(hr)) { LOG_ErrorMsg(hr); goto CleanUp; } } //
// 500953 Allow Power Users to access WUTEMP
//
DWORD dwAttr = GetFileAttributes(szBaseDestinationFolder); if (INVALID_FILE_ATTRIBUTES == dwAttr || 0 == (FILE_ATTRIBUTE_DIRECTORY & dwAttr)) { //
// Only create directory if it doesn't already exist (Power Users can't
// SetFileAttributes if an administrator created the directory originally).
//
if (FAILED(hr = CreateDirectoryAndSetACLs(szBaseDestinationFolder, TRUE))) { LOG_ErrorMsg(hr); hrGlobalItemFailure = hr; } if (!fUseSuppliedPath && !SetFileAttributes(szBaseDestinationFolder, FILE_ATTRIBUTE_HIDDEN)) { DWORD dwErr = GetLastError(); LOG_ErrorMsg(dwErr); hr = HRESULT_FROM_WIN32(dwErr); hrGlobalItemFailure = HRESULT_FROM_WIN32(dwRet); } }
#if defined(UNICODE) || defined(_UNICODE)
LogMessage("Download destination root folder is: %ls", szBaseDestinationFolder); #else
LogMessage("Download destination root folder is: %s", szBaseDestinationFolder); #endif
if (fCorpCase) { history.SetDownloadBasePath(szBaseDestinationFolder); } }
//
// loop through each provider in the catalog, then each item in the provider
//
if (WaitForSingleObject(pEngUpdate->m_evtNeedToQuit, 0) == WAIT_OBJECT_0) { hr = E_ABORT; goto CleanUp; }
hProviderList = xmlCatalog.GetFirstProvider(&hProvider); while (fContinue && HANDLE_NODE_INVALID != hProvider) { xmlCatalog.GetIdentity(hProvider, &bstrProviderName, &bstrProviderPublisher, &bstrProviderUUID);
xmlCatalog.GetIdentityStr(hProvider, &bstrProviderIdentityStr);
//
// Get the Enumerator List of Items in this Provider, and get the first item
//
hCatalogItemList = xmlCatalog.GetFirstItem(hProvider, &hItem); if ((HANDLE_NODELIST_INVALID == hCatalogItemList) || (HANDLE_NODE_INVALID == hItem)) { // No Items under this Provider
xmlCatalog.GetNextProvider(hProviderList, &hProvider); continue; } while (fContinue && HANDLE_NODE_INVALID != hItem) { if (FAILED(hrGlobalItemFailure)) { xmlItemList.AddItem(&xmlCatalog, hItem, &hXmlItem); bstrTemp = T2BSTR(_T("")); xmlItemList.AddDownloadPath(hXmlItem, bstrTemp); SafeSysFreeString(bstrTemp); history.AddHistoryItemDownloadStatus(&xmlCatalog, hItem, HISTORY_STATUS_FAILED, /*no download path*/_T(""), lpszClientInfo, hrGlobalItemFailure); xmlItemList.AddDownloadStatus(hXmlItem, KEY_STATUS_FAILED, hrGlobalItemFailure); xmlCatalog.CloseItem(hItem); xmlCatalog.GetNextItem(hCatalogItemList, &hItem); continue; } LONG lCallbackRequest = 0; // check if user set something in callback
xmlCatalog.GetIdentityStr(hItem, &bstrItemPath); if (NULL == bstrItemPath) { LOG_Download(_T("Failed to Get Identity String for an Item")); xmlCatalog.CloseItem(hItem); xmlCatalog.GetNextItem(hCatalogItemList, &hItem); continue; }
//
// send out status to caller to tell which item we are about to download
//
BSTR bstrXmlItemForCallback = NULL; if (SUCCEEDED(xmlCatalog.GetBSTRItemForCallback(hItem, &bstrXmlItemForCallback))) { CallbackData.lCurrentItemSize = 0; DownloadCallback(&CallbackData, DOWNLOAD_STATUS_ITEMSTART, 0, 0, bstrXmlItemForCallback, &lCallbackRequest); SafeSysFreeString(bstrXmlItemForCallback); bstrXmlItemForCallback = NULL; if (UPDATE_COMMAND_CANCEL == lCallbackRequest) { LOG_Out(_T("Download Callback received UPDATE_COMMAND_CANCEL")); SetEvent(pEngUpdate->m_evtNeedToQuit); // asked to quit
fContinue = FALSE; } else { //
// check the global quit event. If quit, then server ping treat it as a cancel.
//
fContinue = (WaitForSingleObject(pEngUpdate->m_evtNeedToQuit, 0) != WAIT_OBJECT_0); } if (!fContinue) { continue; // or break, same effect.
} } else { //
// something wrong with this item, so we should skip it
//
continue; }
if (fCorpCase) { LPCTSTR szName = NULL; // Corporate Folder Path is Constructed from Several Item Elements
// Software | Driver\<Locale>\<ProviderIdentity>\<Platform>\<ItemIdentity>.<version>
xmlCatalog.GetItemInstallInfo(hItem, &bstrInstallerType, &fExclusive, &fReboot, &lCommandCount); if (NULL == bstrInstallerType) { LOG_Download(_T("Missing InstallerType Info for Item %ls"), bstrItemPath); goto doneCorpCase; }
if (CSTR_EQUAL == CompareStringW(MAKELCID(MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US), SORT_DEFAULT), NORM_IGNORECASE, (LPCWSTR)bstrInstallerType, -1, L"CDM", -1)) { szName = _T("Driver"); } else { szName = _T("Software"); }
hr = StringCchCopyEx(szItemPath, ARRAYSIZE(szItemPath), szName, NULL, NULL, MISTSAFE_STRING_FLAGS); if (FAILED(hr)) goto doneCorpCase;
xmlCatalog.GetItemLanguage(hItem, &bstrLanguage); xmlCatalog.GetCorpItemPlatformStr(hItem, &bstrPlatformDir); if (NULL == bstrLanguage || NULL == bstrPlatformDir) { LOG_Download(_T("Missing Language or Platform Info for Item %ls"), bstrItemPath); goto doneCorpCase; } hr = PathCchCombine(szDestinationFolder, ARRAYSIZE(szDestinationFolder), szBaseDestinationFolder, szItemPath); if (FAILED(hr)) goto doneCorpCase; hr = PathCchAppend(szDestinationFolder, ARRAYSIZE(szDestinationFolder), OLE2T(bstrLanguage)); if (FAILED(hr)) goto doneCorpCase; hr = PathCchAppend(szDestinationFolder, ARRAYSIZE(szDestinationFolder), OLE2T(bstrProviderIdentityStr)); if (FAILED(hr)) goto doneCorpCase; hr = PathCchAppend(szDestinationFolder, ARRAYSIZE(szDestinationFolder), OLE2T(bstrPlatformDir)); if (FAILED(hr)) goto doneCorpCase; hr = PathCchAppend(szDestinationFolder, ARRAYSIZE(szDestinationFolder), OLE2T(bstrItemPath)); if (FAILED(hr)) goto doneCorpCase; doneCorpCase: SafeSysFreeString(bstrInstallerType); SafeSysFreeString(bstrLanguage); SafeSysFreeString(bstrPlatformDir); if (FAILED(hr)) { LOG_ErrorMsg(hr); SafeSysFreeString(bstrItemPath); xmlCatalog.CloseItem(hItem); xmlCatalog.GetNextItem(hCatalogItemList, &hItem); continue; } } else { hr = PathCchCombine(szDestinationFolder, ARRAYSIZE(szDestinationFolder), szBaseDestinationFolder, OLE2T(bstrItemPath)); if (FAILED(hr)) { LOG_ErrorMsg(hr); SafeSysFreeString(bstrItemPath); xmlCatalog.CloseItem(hItem); xmlCatalog.GetNextItem(hCatalogItemList, &hItem); continue; } }
if (FAILED(hr = CreateDirectoryAndSetACLs(szDestinationFolder, TRUE))) { LOG_ErrorMsg(hr); xmlCatalog.CloseItem(hItem); xmlCatalog.GetNextItem(hCatalogItemList, &hItem); SafeSysFreeString(bstrItemPath); continue; }
//
// Now get the collection of CodeBases for this Item
//
hItemCabList = xmlCatalog.GetItemFirstCodeBase(hItem, &bstrCabUrl, &bstrLocalFileName, &bstrCRC, &fCabPatchAvail, &lCabSize); if ((HANDLE_NODELIST_INVALID == hItemCabList) || (NULL == bstrCabUrl)) { // No Cabs for this Item?? skip it.
LOG_Download(_T("Item: %ls has no cabs, Skipping"), bstrItemPath); SafeSysFreeString(bstrItemPath); xmlCatalog.CloseItem(hItem); xmlCatalog.GetNextItem(hCatalogItemList, &hItem); continue; }
while (fContinue && NULL != bstrCabUrl) { LPTSTR pszTempCabUrl = OLE2T(bstrCabUrl);
// pszCabUrl is allocated to be INTERNET_MAX_URL_LENGTH above.
hr = StringCchCopyEx(pszCabUrl, INTERNET_MAX_URL_LENGTH, pszTempCabUrl, NULL, NULL, MISTSAFE_STRING_FLAGS); if (FAILED(hr)) { LOG_ErrorMsg(hr); break; } SafeMemFree(pszTempCabUrl); if (NULL != bstrLocalFileName && SysStringLen(bstrLocalFileName) > 0) { if (NULL != pszAllocatedFileName) { MemFree(pszAllocatedFileName); } pszAllocatedFileName = OLE2T(bstrLocalFileName); } else { //
// has not specified file name, use the same file name in URL
//
// search for the last forward slash (will separate the URL from the filename)
LPTSTR lpszLastSlash = StrRChr(pszCabUrl, NULL, _T('/')); if (NULL != lpszLastSlash) { // last slash was found, skip to next character (will be the beginning of the filename)
lpszLastSlash++; } pszLocalFileName = lpszLastSlash; } // Download the Cab - Store Information for Progress Callbacks
dwBytesDownloaded = 0; CallbackData.lCurrentItemSize = lCabSize; dwCount1 = GetTickCount(); hr = DownloadFile(pszCabUrl, // fileurl to download
szDestinationFolder, // destination folder for file
(NULL != pszAllocatedFileName) ? pszAllocatedFileName : pszLocalFileName, // use AllocatedFileName if possible, else use localfilename
&dwBytesDownloaded, // bytes downloaded for this file
&pEngUpdate->m_evtNeedToQuit, // quit event array
1, // number of events
DownloadCallback, // callback function
&CallbackData, // data structure for callback function
dwFlags); if (FAILED(hr)) { //
// added by JHou: bug335292 - Temporary folder not deleted when network plug removed
//
// only empty folder can be deleted successfully so if RemoveDirectory() failed that
// may because it's not empty which means it's ok
if (RemoveDirectory(szDestinationFolder) && fCorpCase) { HRESULT hrCopy; // If this Directory was successfully removed and this is the Corp Case we should
// try to remove its parents up to the base directory.
TCHAR szCorpDestinationFolderRemove[MAX_PATH]; hrCopy = StringCchCopyEx(szCorpDestinationFolderRemove, ARRAYSIZE(szCorpDestinationFolderRemove), szDestinationFolder, NULL, NULL, MISTSAFE_STRING_FLAGS); if (FAILED(hrCopy)) { LOG_ErrorMsg(hrCopy); break; } LPTSTR pszBackslash = NULL; PathRemoveBackslash(szBaseDestinationFolder); // strip any trailing backslashes - need to normalize this to compare when we're done walking the folder tree
for (;;) { pszBackslash = StrRChr(szCorpDestinationFolderRemove, NULL, '\\'); if (NULL == pszBackslash) break; // unexpected
*pszBackslash = '\0'; if (0 == StrCmp(szCorpDestinationFolderRemove, szBaseDestinationFolder)) break; // reached the base directory, done removing directories;
if (!RemoveDirectory(szCorpDestinationFolderRemove)) break; // couldn't remove folder at this level, assume folder not empty, leave the rest of the structure intact.
} }
if (E_ABORT == hr) { LOG_Download(_T("DownloadFile function returns E_ABORT while downloading %s."), pszCabUrl); #if defined(UNICODE) || defined(_UNICODE)
LogError(hr, "Download cancelled while processing file %ls", pszCabUrl); #else
LogError(hr, "Download cancelled while processing file %s", pszCabUrl); #endif
} else { LOG_Download(_T("Download Failed for URL: %s, Skipping remaining files for this Item"), pszCabUrl); #if defined(UNICODE) || defined(_UNICODE)
LogError(hr, "Downloading file %ls, skipping remaining files for this Item", pszCabUrl); #else
LogError(hr, "Downloading file %s, skipping remaining files for this Item", pszCabUrl); #endif
} SafeSysFreeString(bstrCabUrl); //
// since one file got error, we can exit the file loop for the current item
// because missing one file will make this item not usable.
//
break; } dwCount2 = GetTickCount(); if (0 != dwBytesDownloaded) { if (dwCount1 < dwCount2) // normal case, no roll-over
{ dwElapsedTime = dwCount2 - dwCount1; } else { // roll-over case, should almost never happen
dwElapsedTime = (0xFFFFFFFF - dwCount1) + dwCount2; }
dwTotalBytesDownloaded += dwBytesDownloaded; dwTotalElapsedTime += dwElapsedTime; }
// Form the full Path and Filename of the file we just downloaded
hr = PathCchCombine(szItemPath, ARRAYSIZE(szItemPath), szDestinationFolder, (NULL != pszAllocatedFileName) ? pszAllocatedFileName : pszLocalFileName); if (FAILED(hr)) { DeleteFile(szItemPath); break; } // Verify CRC
//---------------
if (NULL != bstrCRC) { TCHAR szCRCHash[CRC_HASH_STRING_LENGTH] = {'\0'}; hr = StringCchCopyEx(szCRCHash, ARRAYSIZE(szCRCHash), OLE2T(bstrCRC), NULL, NULL, MISTSAFE_STRING_FLAGS); if (FAILED(hr)) { // Something was wrong with the BSTR we got back from XML. Fail Safely, delete the file.
// The Failed HR will fail the item
DeleteFile(szItemPath); break; } hr = VerifyFileCRC(szItemPath, szCRCHash); if (HRESULT_FROM_WIN32(ERROR_CRC) == hr || FAILED(hr)) { // The File CRC's Did Not Match, or we had a problem Calculating the CRC. Fail Safely, delete the file.
// The Failed HR will fail the item
DeleteFile(szItemPath); break; } }
// Check Trust
//---------------
hr = VerifyFileTrust(szItemPath, NULL, ReadWUPolicyShowTrustUI() ); if (FAILED(hr)) { // File Was Not Trusted - Need to Delete it and fail the item
DeleteFile(szItemPath); break; }
#if defined(UNICODE) || defined(_UNICODE)
LogMessage("Downloaded file %ls", pszCabUrl); LogMessage("Local path %ls", szItemPath); #else
LogMessage("Downloaded file %s", pszCabUrl); LogMessage("Local path %s", szItemPath); #endif
SafeSysFreeString(bstrCabUrl); SafeSysFreeString(bstrLocalFileName); SafeSysFreeString(bstrCRC); bstrCabUrl = bstrLocalFileName = NULL; fContinue = SUCCEEDED(xmlCatalog.GetItemNextCodeBase(hItemCabList, &bstrCabUrl, &bstrLocalFileName, &bstrCRC, &fCabPatchAvail, &lCabSize)) && (WaitForSingleObject(pEngUpdate->m_evtNeedToQuit, 0) != WAIT_OBJECT_0); }
// Write XMLItems entry for this download result
xmlItemList.AddItem(&xmlCatalog, hItem, &hXmlItem); bstrTemp = T2BSTR(szDestinationFolder); xmlItemList.AddDownloadPath(hXmlItem, bstrTemp); SafeSysFreeString(bstrTemp);
//
// For "corporate" download write ReadMore Link before writing history (in case we fail
//
if (TRUE == fCorpCase) { //
// Ignore errors as we want to keep downloaded cab anyway
//
(void) CreateReadMoreLink(&xmlCatalog, hItem, szDestinationFolder); (void) CreateItemDependencyList(&xmlCatalog, hItem, szDestinationFolder); }
//
// Also add download history for this item
//
if (SUCCEEDED(hr)) { history.AddHistoryItemDownloadStatus(&xmlCatalog, hItem, HISTORY_STATUS_COMPLETE, szDestinationFolder, lpszClientInfo); xmlItemList.AddDownloadStatus(hXmlItem, KEY_STATUS_COMPLETE); } else { history.AddHistoryItemDownloadStatus(&xmlCatalog, hItem, HISTORY_STATUS_FAILED, szDestinationFolder, lpszClientInfo, hr); xmlItemList.AddDownloadStatus(hXmlItem, KEY_STATUS_FAILED, hr); }
//
// ping server to report the download status for this item
//
{ BSTR bstrIdentityPing = NULL; if (SUCCEEDED(xmlCatalog.GetIdentityStrForPing(hItem, &bstrIdentityPing))) { URLLOGSTATUS status = SUCCEEDED(hr) ? URLLOGSTATUS_Success : URLLOGSTATUS_Failed; if (E_ABORT == hr) { //
// user/system cancelled the current process
//
status = URLLOGSTATUS_Cancelled; } pingSvr.Ping( TRUE, // on-line
URLLOGDESTINATION_DEFAULT, // going live or corp WU ping server
&pEngUpdate->m_evtNeedToQuit, // pt to cancel events
1, // number of events
URLLOGACTIVITY_Download, // activity
status, // status code
hr, // error code, can be 0 or 1
OLE2T(bstrIdentityPing), // itemID
NULL // no device data can be given during dld phase
); }
SafeSysFreeString(bstrIdentityPing); //SafeSysFreeString(bstrPlatformPing);
//SafeSysFreeString(bstrLanguagePing);
}
xmlCatalog.CloseItemList(hItemCabList);
//
// done with this item, fire itemcomplete event
//
DownloadCallback(&CallbackData, DOWNLOAD_STATUS_ITEMCOMPLETE, CallbackData.lCurrentItemSize, 0, NULL, &lCallbackRequest);
SafeSysFreeString(bstrItemPath); // get the next item. hItem will be HANDLE_NODE_INVALID when there are no
// remaining items.
xmlCatalog.CloseItem(hItem); xmlCatalog.GetNextItem(hCatalogItemList, &hItem); if (UPDATE_COMMAND_CANCEL == lCallbackRequest) { LOG_Out(_T("Download Callback received UPDATE_COMMAND_CANCEL")); SetEvent(pEngUpdate->m_evtNeedToQuit); // asked to quit
fContinue = FALSE; } else { //
// check the global quit event. If quit, then server ping treat it as a cancel.
// TODO: also need to check the operation quit event!
//
fContinue = (WaitForSingleObject(pEngUpdate->m_evtNeedToQuit, 0) != WAIT_OBJECT_0); } }
xmlCatalog.CloseItemList(hCatalogItemList);
SafeSysFreeString(bstrProviderName); SafeSysFreeString(bstrProviderPublisher); SafeSysFreeString(bstrProviderUUID); SafeSysFreeString(bstrProviderIdentityStr); xmlCatalog.CloseItem(hProvider); xmlCatalog.GetNextProvider(hProviderList, &hProvider); fContinue = (WaitForSingleObject(pEngUpdate->m_evtNeedToQuit, 0) != WAIT_OBJECT_0); }
xmlCatalog.CloseItemList(hProviderList);
RegOpenKey(HKEY_LOCAL_MACHINE, REGKEY_IUCTL, &hkeyIU); hMutex = CreateMutex(NULL, FALSE, IU_MUTEX_HISTORICALSPEED_REGUPDATE);
if ((0 != dwTotalBytesDownloaded) && (0 != dwTotalElapsedTime) && (NULL != hkeyIU) && (NULL != hMutex)) { HANDLE aHandles[2];
aHandles[0] = hMutex; aHandles[1] = pEngUpdate->m_evtNeedToQuit;
dwWaitResult = MyMsgWaitForMultipleObjects(ARRAYSIZE(aHandles), aHandles, FALSE, /*30 seconds*/30000, QS_ALLINPUT); if (WAIT_OBJECT_0 == dwWaitResult) { // convert elapsed time from milliseconds to seconds
dwTotalElapsedTime = dwTotalElapsedTime / 1000; if (0 == dwTotalElapsedTime) dwTotalElapsedTime = 1; // minimum one second
// we have the mutex, go ahead and read/write the reg information.
dwSize = sizeof(dwHistoricalSpeed); RegQueryValueEx(hkeyIU, REGVAL_HISTORICALSPEED, NULL, NULL, (LPBYTE)&dwHistoricalSpeed, &dwSize); dwSize = sizeof(dwHistoricalTime); RegQueryValueEx(hkeyIU, REGVAL_TIMEELAPSED, NULL, NULL, (LPBYTE)&dwHistoricalTime, &dwSize);
// We need to get the Bytes Downloaded to add the bytes just downloaded
DWORD dwHistoricalBytes = dwHistoricalSpeed * dwHistoricalTime; // could be 0 if no previous history was recorded
dwHistoricalBytes += dwTotalBytesDownloaded; // new byte count
dwHistoricalTime += dwTotalElapsedTime; // new time count
dwHistoricalSpeed = dwHistoricalBytes / dwHistoricalTime; // calculate new speed bytes/second
RegSetValueEx(hkeyIU, REGVAL_HISTORICALSPEED, NULL, REG_DWORD, (LPBYTE)&dwHistoricalSpeed, sizeof(dwHistoricalSpeed)); RegSetValueEx(hkeyIU, REGVAL_TIMEELAPSED, NULL, REG_DWORD, (LPBYTE)&dwHistoricalTime, sizeof(dwHistoricalTime)); ReleaseMutex(hMutex); CloseHandle(hMutex); hMutex = NULL; } }
//
// We pass in pEngUpdate->m_evtNeedToQuit to MyMsgWaitForMultipleObjects above so it will exit immediately
// but don't bother to handle the WAIT_OBJECT_0 + 1 case there since the if statement may not
// execute and even if this is the case we still need to check pEngUpdate->m_evtNeedToQuit below anyway.
//
if (WaitForSingleObject(pEngUpdate->m_evtNeedToQuit, 0) == WAIT_OBJECT_0) { hr = E_ABORT; }
CleanUp:
//
// add HRESULT in case the download failed before the download loop
//
if (S_OK != hr) { xmlItemList.AddGlobalErrorCodeIfNoItems(hr); }
//
// generate result
//
xmlItemList.GetItemsBSTR(pbstrXmlItems);
SafeSysFreeString(CallbackData.bstrOperationUuid); SafeHeapFree(pszCabUrl);
SafeSysFreeString(bstrCabUrl); SafeSysFreeString(bstrLocalFileName); SafeSysFreeString(bstrProviderName); SafeSysFreeString(bstrProviderPublisher); SafeSysFreeString(bstrProviderUUID); SafeSysFreeString(bstrProviderIdentityStr); SafeSysFreeString(bstrItemPath); SafeSysFreeString(bstrInstallerType); SafeSysFreeString(bstrLanguage); SafeSysFreeString(bstrPlatformDir); SafeSysFreeString(bstrTemp);
if (NULL != hkeyIU) { RegCloseKey(hkeyIU); hkeyIU = NULL; }
if (NULL != hMutex) { // shouldn't need to release, if hmutex is still valid at this point we were unable to
// get the mutex
CloseHandle(hMutex); hMutex = NULL; } }
//
// notify that we are completed
//
if (NULL != punkProgressListener || NULL != hWnd) { DownloadCallback(&CallbackData, DOWNLOAD_STATUS_OPERATIONCOMPLETE, 0, 0, *pbstrXmlItems, NULL); }
if (SUCCEEDED(hr)) { LogMessage("%s %s", SZ_SEE_IUHIST, SZ_DOWNLOAD_FINISHED); } else { LogError(hr, "%s %s", SZ_SEE_IUHIST, SZ_DOWNLOAD_FINISHED); }
return hr; }
/////////////////////////////////////////////////////////////////////////////
// Download()
//
// Do synchronous 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 - bitmask indicates throttled/foreground and notification options
// punkProgressListener - the callback function pointer for reporting download progress
// hWnd - the event msg window handler passed from the stub
// Output:
// pbstrXmlItems - the items with download status in xml format
// e.g.
// <id guid="2560AD4D-3ED3-49C6-A937-4368C0B0E06D" downloaded="1"/>
/////////////////////////////////////////////////////////////////////////////
HRESULT WINAPI CEngUpdate::Download(BSTR bstrXmlClientInfo, BSTR bstrXmlCatalog, BSTR bstrDestinationFolder, LONG lMode, IUnknown *punkProgressListener, HWND hWnd, BSTR *pbstrXmlItems) { CXmlClientInfo clientInfo; BSTR bstrClientName = NULL;
HRESULT hr;
LOG_Block("Download()");
LogMessage("Download started");
hr = clientInfo.LoadXMLDocument(bstrXmlClientInfo, m_fOfflineMode); CleanUpIfFailedAndMsg(hr);
hr = clientInfo.GetClientName(&bstrClientName); CleanUpIfFailedAndMsg(hr);
hr = _Download( bstrClientName, bstrXmlCatalog, bstrDestinationFolder, lMode, punkProgressListener, hWnd, NULL, // no op id needed for sync download
pbstrXmlItems, this);
CleanUp:
SysFreeString(bstrClientName); 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
// hWnd - the event msg window handler passed from the stub
// 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.
/////////////////////////////////////////////////////////////////////////////
HRESULT WINAPI CEngUpdate::DownloadAsync(BSTR bstrXmlClientInfo, BSTR bstrXmlCatalog, BSTR bstrDestinationFolder, LONG lMode, IUnknown *punkProgressListener, HWND hWnd, BSTR bstrUuidOperation, BSTR *pbstrUuidOperation) { HRESULT hr = S_OK; BSTR bstrClientName = NULL; DWORD dwThreadId = 0x0; DWORD dwErr = 0x0; HANDLE hThread = NULL; GUID guid; LPWSTR lpswClientInfo = NULL; LPOLESTR pwszUuidOperation = NULL; PIUDOWNLOADSTARTUPINFO pStartupInfo = NULL; HANDLE hHeap = GetProcessHeap(); CXmlClientInfo clientInfo;
LOG_Block("DownloadAsync()");
LogMessage("Asynchronous Download started");
USES_IU_CONVERSION;
//
// validate parameters:
// if no catalog, or no return var, or no client info, this function can do nothing.
//
if ((NULL == bstrXmlCatalog) || (NULL == bstrXmlClientInfo) || (SysStringLen(bstrXmlCatalog) == 0) || (SysStringLen(bstrXmlClientInfo) == 0)) { hr = E_INVALIDARG; CleanUpIfFailedAndMsg(hr); }
//
// validate the client info
//
hr = clientInfo.LoadXMLDocument(bstrXmlClientInfo, m_fOfflineMode); CleanUpIfFailedAndMsg(hr);
hr = clientInfo.GetClientName(&bstrClientName); CleanUpIfFailedAndMsg(hr);
if (NULL == (pStartupInfo = (PIUDOWNLOADSTARTUPINFO) HeapAlloc(hHeap, HEAP_ZERO_MEMORY, sizeof(IUDOWNLOADSTARTUPINFO)))) { hr = E_OUTOFMEMORY; LOG_ErrorMsg(hr); goto CleanUp; }
pStartupInfo->bstrClientName = SysAllocString(bstrClientName); pStartupInfo->bstrXmlCatalog = SysAllocString(bstrXmlCatalog); pStartupInfo->hwnd = hWnd; pStartupInfo->lMode = lMode; pStartupInfo->punkProgressListener = punkProgressListener; pStartupInfo->pEngUpdate = this; if (NULL != bstrDestinationFolder && SysStringLen(bstrDestinationFolder) > 0) { LOG_Download(_T("Caller specified destination folder=%s"), OLE2T(bstrDestinationFolder)); pStartupInfo->bstrDestinationFolder = SysAllocString(bstrDestinationFolder); }
//
// session id is required for download operation
//
if (NULL != bstrUuidOperation && SysStringLen(bstrUuidOperation) > 0) { LOG_Download(_T("User passed in UUID %s"), OLE2T(bstrUuidOperation)); pStartupInfo->bstrUuidOperation = SysAllocString(bstrUuidOperation); if (NULL != pbstrUuidOperation) { *pbstrUuidOperation = SysAllocString(bstrUuidOperation); } } else { //
// if user doesn't have an operation id, we generate one
//
hr = CoCreateGuid(&guid); if (FAILED(hr)) { LOG_ErrorMsg(hr); goto CleanUp; } hr = StringFromCLSID(guid, &pwszUuidOperation); if (FAILED(hr)) { LOG_ErrorMsg(hr); goto CleanUp; } pStartupInfo->bstrUuidOperation = SysAllocString(pwszUuidOperation); if (NULL != pbstrUuidOperation) { *pbstrUuidOperation = SysAllocString(pwszUuidOperation); } LOG_Download(_T("UUID generated %s"), OLE2T(pwszUuidOperation)); CoTaskMemFree(pwszUuidOperation); }
InterlockedIncrement(&m_lThreadCounter); if (NULL != pStartupInfo->punkProgressListener) { //
// since this is an async operation, to prevent caller free this object after
// this call returns, we pump up ref count here. The thread proc will
// release refcount after it finishes the work
//
pStartupInfo->punkProgressListener->AddRef(); }
hThread = CreateThread(NULL, 0, DownloadThreadProc, (LPVOID)pStartupInfo, 0, &dwThreadId); if (NULL == hThread) { //
// clean up allocated strings in pStartupInfo.
//
dwErr = GetLastError(); hr = HRESULT_FROM_WIN32(dwErr); LOG_ErrorMsg(hr); SafeRelease(pStartupInfo->punkProgressListener); InterlockedDecrement(&m_lThreadCounter); } else { LOG_Download(_T("Download thread generated successfully")); }
CleanUp: if (FAILED(hr)) { LogError(hr, "Asynchronous Download failed during startup");
if (NULL != pStartupInfo) { SysFreeString(pStartupInfo->bstrDestinationFolder); SysFreeString(pStartupInfo->bstrXmlCatalog); SysFreeString(pStartupInfo->bstrClientName); SysFreeString(pStartupInfo->bstrUuidOperation); HeapFree(hHeap, 0, pStartupInfo); } }
SysFreeString(bstrClientName);
return hr; }
/////////////////////////////////////////////////////////////////////////////
// DownloadCallback()
//
// Callback Function to recieve progress from IU Downloader.
//
// Input:
// pCallbackData - void pointer to DCB_DATA structure
// dwStatus - Current Download Status
// dwBytesTotal - Total Bytes of File being Downloaded
// dwBytesComplete - Bytes Downloaded so far
// bstrCompleteResult - Contains Item Result XML
//
// Output:
// plCommandRequest - Used to Instruct the Downloader to continue, abort, suspend...
//
// Return:
// 0 - always, exit code is irrelevant since calling thread doesn't check the
// status of this thread after creation.
/////////////////////////////////////////////////////////////////////////////
BOOL WINAPI DownloadCallback(VOID* pCallbackData, DWORD dwStatus, DWORD dwBytesTotal, DWORD dwBlockSizeDownloaded, BSTR bstrXmlData, LONG* plCommandRequest) { LOG_Block("DownloadCallback()");
HRESULT hr; LONG lUpdateMask = 0; float flNewPercentage; EventData evtData; char szProgressSize[64] = {'\0'}; BOOL fPostWaitSuccess = TRUE; ZeroMemory((LPVOID) &evtData, sizeof(evtData));
USES_IU_CONVERSION;
P_DCB_DATA pCallbackParam = (P_DCB_DATA) pCallbackData;
if (NULL != pCallbackParam->bstrOperationUuid) { evtData.bstrUuidOperation = SysAllocString(pCallbackParam->bstrOperationUuid); LOG_Download(_T("Found UUID=%s"), OLE2T(evtData.bstrUuidOperation)); }
if (dwBytesTotal != pCallbackParam->lCurrentItemSize && DOWNLOAD_STATUS_ITEMCOMPLETE != dwStatus) { pCallbackParam->lTotalDownloadSize = (pCallbackParam->lTotalDownloadSize - pCallbackParam->lCurrentItemSize) + dwBytesTotal; pCallbackParam->lCurrentItemSize = dwBytesTotal; }
// Keep the Total Downloaded Bytes Counter
if (0 != dwBlockSizeDownloaded && DOWNLOAD_STATUS_ITEMCOMPLETE != dwStatus) pCallbackParam->lTotalDownloaded += dwBlockSizeDownloaded;
LOG_Download(_T("dwStatus=0x%08x"), dwStatus);
//
// if the Status is DOWNLOAD_STATUS_FILECOMPLETE we are done with this File
//
evtData.fItemCompleted = (dwStatus == DOWNLOAD_STATUS_ITEMCOMPLETE);
switch (dwStatus) { case DOWNLOAD_STATUS_ITEMSTART:
if (NULL != pCallbackParam->pProgressListener) { pCallbackParam->pProgressListener->OnItemStart(pCallbackParam->bstrOperationUuid, bstrXmlData, &evtData.lCommandRequest); } else { // only use event window if no progresslistener interface was given
if (NULL != pCallbackParam->hEventFiringWnd) { evtData.bstrXmlData = bstrXmlData; SendMessage(pCallbackParam->hEventFiringWnd, UM_EVENT_ITEMSTART, 0, LPARAM(&evtData)); evtData.bstrXmlData = NULL; } }
break;
case DOWNLOAD_STATUS_OK: // simple progress update
case DOWNLOAD_STATUS_ITEMCOMPLETE: if (0 != pCallbackParam->flProgressPercentage) { // we need to give progress callbacks on given percentage increments
flNewPercentage = ((float)pCallbackParam->lTotalDownloaded / pCallbackParam->lTotalDownloadSize); if (((flNewPercentage - pCallbackParam->flLastPercentage) >= pCallbackParam->flProgressPercentage) || ((1.0 - flNewPercentage) < 0.0001 && 1 != pCallbackParam->flProgressPercentage)) { // The Difference between LastPercentComplete and CurrentPercentComplete complies with the
// Progress Percentage granuluarity OR the percentage is 100 (complete)
if (evtData.fItemCompleted) { //wsprintfA(szProgressSize, "%d", (int)flNewPercentage); // float should be 1.0, cast as int will be 1
//
// when we notify the user this "item", not file, completed, we don't need
// to pass out any percentage info.
//
szProgressSize[0] = _T('\0'); } else { if ((1.0 - flNewPercentage) < 0.0001) { if (ARRAYSIZE(szProgressSize) >= 2) { szProgressSize[0] = _T('1'); szProgressSize[1] = _T('\0'); } else { break; } } else { hr = StringCchPrintfExA(szProgressSize, ARRAYSIZE(szProgressSize), NULL, NULL, MISTSAFE_STRING_FLAGS, ".%02d", (int)(flNewPercentage*100)); // string equivilant of a float
if (FAILED(hr)) { LOG_ErrorMsg(hr); break; } } } pCallbackParam->flLastPercentage = flNewPercentage; } else { // don't make a callback
break; } } else { // No percentage callback was requested.. just give the byte values.
if (dwStatus == DOWNLOAD_STATUS_ITEMCOMPLETE) { szProgressSize[0] = _T('\0'); } else { hr = StringCchPrintfExA(szProgressSize, ARRAYSIZE(szProgressSize), NULL, NULL, MISTSAFE_STRING_FLAGS, "%lu:%lu", (ULONG)pCallbackParam->lTotalDownloadSize, (ULONG)pCallbackParam->lTotalDownloaded); if (FAILED(hr)) { LOG_ErrorMsg(hr); break; } } } evtData.bstrProgress = SysAllocString(A2OLE(szProgressSize)); if (NULL != pCallbackParam->pProgressListener) { pCallbackParam->pProgressListener->OnProgress(pCallbackParam->bstrOperationUuid, evtData.fItemCompleted, evtData.bstrProgress, &evtData.lCommandRequest); } else { // only use event window if no progresslistener interface was given
if (NULL != pCallbackParam->hEventFiringWnd) { SendMessage(pCallbackParam->hEventFiringWnd, UM_EVENT_PROGRESS, 0, LPARAM(&evtData)); } } break;
case DOWNLOAD_STATUS_OPERATIONCOMPLETE: if (NULL != pCallbackParam->pProgressListener) { pCallbackParam->pProgressListener->OnOperationComplete(pCallbackParam->bstrOperationUuid, bstrXmlData); } else { // only use event window if no progresslistener interface was given
if (NULL != pCallbackParam->hEventFiringWnd) { evtData.bstrXmlData = bstrXmlData; fPostWaitSuccess = WUPostEventAndBlock(pCallbackParam->hEventFiringWnd, UM_EVENT_COMPLETE, &evtData); } }
// Look for an existing Operation in the Mgr, and update the Complete Result if available.
if (pCallbackParam->pOperationMgr->FindOperation(OLE2T(pCallbackParam->bstrOperationUuid), &lUpdateMask, NULL)) { pCallbackParam->pOperationMgr->UpdateOperation(OLE2T(pCallbackParam->bstrOperationUuid), lUpdateMask, bstrXmlData); } break; case DOWNLOAD_STATUS_ABORTED: case DOWNLOAD_STATUS_ERROR: //
// abort case: user should know. nothing to report
// error case: progress callback doesn't give us any way of telling the caller that its an error, no reason to send the callback
// the itemcomplete callback will have the error status for the item.
//
break; } if (NULL != plCommandRequest) // we made a callback and the command request value was retrieved
{ *plCommandRequest = (LONG)((DWORD) evtData.lCommandRequest & (DWORD) UPDATE_COMMAND); LOG_Download(_T("Command returned: 0x%08x"), *plCommandRequest); }
// don't free up the strings below unless the wait succeeded in
// WUPostEventAndBlock. If we do free the strings up and the wait didn't
// succeed, then we run the risk of AVing ourselves. Note that fPostWaitSuccess
// is initialized to TRUE so if we will free these BSTRs if WUPostEventAndBlock
// is not called.
if (fPostWaitSuccess) { SysFreeString(evtData.bstrProgress); SysFreeString(evtData.bstrUuidOperation); } return TRUE; }
/////////////////////////////////////////////////////////////////////////////
// DownloadThreadProc()
//
// Thread Proc for Async Download. Retrieves the startup information from
// the input param and calls Download() from this seperate thread. The calling
// thread returns immediately.
//
// Input:
// lpv - void pointer to IUDOWNLOADSTARTINFO struct containing all information
// needed to call Download()
//
// Return:
// 0 - always, exit code is irrelevant since calling thread doesn't check the
// status of this thread after creation.
/////////////////////////////////////////////////////////////////////////////
DWORD WINAPI DownloadThreadProc(LPVOID lpv) { LOG_Block("DownloadThreadProc()"); //
// in this new thread need to call CoInitialize again
// but since we don't know who the caller is, what threading they
// are using, so we just use single apartment
//
HRESULT hr = CoInitialize(NULL); if (FAILED(hr)) { LogError(hr, "Asynchronous Download thread exiting"); LOG_ErrorMsg(hr); return 0; } LOG_Download(_T("CoInitialize called successfully"));
PIUDOWNLOADSTARTUPINFO pStartupInfo = (PIUDOWNLOADSTARTUPINFO)lpv; BSTR bstrXmlItems = NULL;
LOG_Download(_T("Download thread started, now the thread count=%d"), pStartupInfo->pEngUpdate->m_lThreadCounter);
//
// call synchronized download function in this thread
//
_Download( pStartupInfo->bstrClientName, pStartupInfo->bstrXmlCatalog, pStartupInfo->bstrDestinationFolder, pStartupInfo->lMode, pStartupInfo->punkProgressListener, pStartupInfo->hwnd, pStartupInfo->bstrUuidOperation, &bstrXmlItems, pStartupInfo->pEngUpdate); //
// pStartupInfo is a buffer allocated by calling thread, when we are done, we need to
// free it here
//
SysFreeString(pStartupInfo->bstrDestinationFolder); SysFreeString(pStartupInfo->bstrXmlCatalog); SysFreeString(pStartupInfo->bstrClientName); SysFreeString(pStartupInfo->bstrUuidOperation); SysFreeString(bstrXmlItems); SafeRelease(pStartupInfo->punkProgressListener); // so the caller can free this object
CoUninitialize(); LOG_Download(_T("CoUninitialize called"));
InterlockedDecrement(&(pStartupInfo->pEngUpdate->m_lThreadCounter));
HeapFree(GetProcessHeap(), 0, pStartupInfo); return 0; }
|