Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1917 lines
78 KiB

  1. //=======================================================================
  2. //
  3. // Copyright (c) 1998-2000 Microsoft Corporation. All Rights Reserved.
  4. //
  5. // File: downld.cpp
  6. //
  7. // Description:
  8. //
  9. // Implementation for the Download() function
  10. //
  11. //=======================================================================
  12. #include "iuengine.h" // PCH - must include first
  13. #include <iu.h>
  14. #include <iucommon.h>
  15. #include <download.h>
  16. #include <trust.h>
  17. #include <wininet.h>
  18. #include <fileutil.h>
  19. #include <shlwapi.h>
  20. #include "iuxml.h"
  21. #include "history.h"
  22. #include <schemakeys.h>
  23. //#include <serverPing.h> changed to use urllogging.h.
  24. #include <intshcut.h>
  25. #include <schemamisc.h>
  26. #include <WaitUtil.h>
  27. #include <urllogging.h>
  28. #define MAX_CORPORATE_PATH 100
  29. // named mutex used to update historical speed/time information in the registry.
  30. const TCHAR IU_MUTEX_HISTORICALSPEED_REGUPDATE[] = _T("{5f3255a9-9051-49b1-80b9-aac31c092af4}");
  31. const TCHAR IU_READMORE_LINK_NAME[] = _T("ReadMore.url");
  32. const CHAR SZ_DOWNLOAD_FINISHED[] = "Download finished";
  33. const LONG UPDATE_COMMAND = 0x0000000F;
  34. typedef struct IUDOWNLOADSTARTUPINFO
  35. {
  36. BSTR bstrClientName;
  37. BSTR bstrXmlCatalog;
  38. BSTR bstrDestinationFolder;
  39. LONG lMode;
  40. IUnknown *punkProgressListener;
  41. HWND hwnd;
  42. BSTR bstrUuidOperation;
  43. CEngUpdate* pEngUpdate;
  44. } IUDOWNLOADSTARTUPINFO, *PIUDOWNLOADSTARTUPINFO;
  45. // --------------------------------------------------------------------
  46. // function forward declarations
  47. // --------------------------------------------------------------------
  48. //
  49. // Callback function to provide status from the downloader
  50. //
  51. BOOL WINAPI DownloadCallback(VOID* pCallbackData, DWORD dwStatus, DWORD dwBytesTotal, DWORD dwBytesComplete, BSTR bstrXmlData, LONG* plCommandRequest);
  52. //
  53. // thread function used by DownloadAsync
  54. //
  55. DWORD WINAPI DownloadThreadProc(LPVOID lpv);
  56. /////////////////////////////////////////////////////////////////////////////
  57. // CreateReadMoreLink()
  58. //
  59. // If the item contains a "description/descriptionText/details" node, suck
  60. // out the URL and create a shortcut for it in the destination folder
  61. //
  62. // Input:
  63. // pxmlCatalog - CXmlCatalog containing downloaded items
  64. // hItem - Handle to current download item in the catalog
  65. // pszDestinationFolder - Folder where item is downloaded
  66. //
  67. // Return:
  68. // S_OK - Wrote the ReadMore.htm link
  69. // S_FALSE - details node didn't exist in item
  70. // <other> - HRESULT returned from calling other functions
  71. /////////////////////////////////////////////////////////////////////////////
  72. HRESULT CreateReadMoreLink(CXmlCatalog* pxmlCatalog, HANDLE_NODE hItem, LPCTSTR pszDestinationFolder)
  73. {
  74. USES_IU_CONVERSION;
  75. LOG_Block("CreateReadMoreLink");
  76. IXMLDOMNode* pItemNode = NULL;
  77. IXMLDOMNode* pReadMoreNode = NULL;
  78. IUniformResourceLocator* purl = NULL;
  79. IPersistFile* ppf = NULL;
  80. HRESULT hr;
  81. TCHAR szShortcut[MAX_PATH];
  82. BSTR bstrURL = NULL;
  83. if (NULL == pxmlCatalog || HANDLE_NODE_INVALID == hItem || NULL == pszDestinationFolder)
  84. {
  85. CleanUpIfFailedAndSetHrMsg(E_INVALIDARG);
  86. }
  87. //
  88. // Get <item> node in catalog
  89. //
  90. if (NULL == (pItemNode = pxmlCatalog->GetDOMNodebyHandle(hItem)))
  91. {
  92. CleanUpIfFailedAndSetHrMsg(E_INVALIDARG);
  93. }
  94. //
  95. // Get node containing ReadMore URL, or S_FALSE if it doesn't exist
  96. //
  97. hr = pItemNode->selectSingleNode(KEY_READMORE, &pReadMoreNode);
  98. if (S_OK != hr)
  99. {
  100. if (S_FALSE != hr)
  101. {
  102. LOG_ErrorMsg(hr);
  103. }
  104. goto CleanUp;
  105. }
  106. //
  107. // suck out the href attribute
  108. //
  109. CleanUpIfFailedAndSetHrMsg(GetAttribute(pReadMoreNode, KEY_HREF, &bstrURL));
  110. // Get pointers to the IID_IUniformResourceLocator and IID_IPersistFile interfaces
  111. // on the CLSID_InternetShortcut object
  112. CleanUpIfFailedAndSetHrMsg(CoCreateInstance(CLSID_InternetShortcut, \
  113. NULL, \
  114. CLSCTX_INPROC_SERVER, \
  115. IID_IUniformResourceLocator, \
  116. (LPVOID*)&purl));
  117. CleanUpIfFailedAndSetHrMsg(purl->QueryInterface(IID_IPersistFile, (LPVOID*)&ppf));
  118. // 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
  119. URL_COMPONENTS UrlComponents;
  120. // Break down the URL to get the Protocol Used
  121. // Specifically we need the server name, object to download, username and
  122. // password information.
  123. TCHAR szScheme[32];
  124. szScheme[0] = _T('\0');
  125. ZeroMemory(&UrlComponents, sizeof(UrlComponents));
  126. UrlComponents.dwStructSize = sizeof(UrlComponents);
  127. UrlComponents.lpszScheme = szScheme;
  128. UrlComponents.dwSchemeLength = ARRAYSIZE(szScheme);
  129. if (!InternetCrackUrl(OLE2T(bstrURL), 0, 0, &UrlComponents))
  130. {
  131. LOG_ErrorMsg(HRESULT_FROM_WIN32(GetLastError()));
  132. goto CleanUp;
  133. }
  134. if (szScheme[0] == _T('\0') || (0 != lstrcmpi(szScheme, _T("http")) && 0 != lstrcmpi(szScheme, _T("https"))))
  135. {
  136. // If the Scheme was undeterminable, or the scheme is not HTTP then we shouldn't trust this URL.
  137. LOG_ErrorMsg(E_UNEXPECTED);
  138. goto CleanUp;
  139. }
  140. //
  141. // Set the URL, form the shortcut path, and write the link
  142. //
  143. CleanUpIfFailedAndSetHrMsg(purl->SetURL(OLE2T(bstrURL), 0));
  144. hr = StringCchCopyEx(szShortcut, ARRAYSIZE(szShortcut), pszDestinationFolder,
  145. NULL, NULL, MISTSAFE_STRING_FLAGS);
  146. CleanUpIfFailedAndSetHrMsg(hr);
  147. hr = PathCchAppend(szShortcut, ARRAYSIZE(szShortcut), IU_READMORE_LINK_NAME);
  148. CleanUpIfFailedAndSetHrMsg(hr);
  149. CleanUpIfFailedAndSetHrMsg(ppf->Save(T2OLE(szShortcut), FALSE));
  150. CleanUp:
  151. SysFreeString(bstrURL);
  152. // pItemNode is owned by CXmlCatalog, don't release
  153. SafeReleaseNULL(pReadMoreNode);
  154. SafeReleaseNULL(ppf);
  155. SafeReleaseNULL(purl);
  156. return hr;
  157. }
  158. /////////////////////////////////////////////////////////////////////////////
  159. // CreateItemDependencyList()
  160. //
  161. // If the item contains a "dependencies" node, we want to walk
  162. // the dependendant Item List and list the proper order that installs should
  163. // be done in. If a dependant Item is not available in the current catalog it
  164. // will be ignored.
  165. //
  166. // Input:
  167. // pxmlCatalog - CXmlCatalog containing downloaded items
  168. // hItem - Handle to current download item in the catalog
  169. // pszDestinationFolder - Folder where item is downloaded
  170. //
  171. // Return:
  172. // S_OK - Dependency List Written
  173. // S_FALSE - No Dependencies Available
  174. // <other> - HRESULT returned from calling other functions
  175. /////////////////////////////////////////////////////////////////////////////
  176. HRESULT CreateItemDependencyList(CXmlCatalog* pxmlCatalog, HANDLE_NODE hItem, LPCTSTR pszDestinationFolder)
  177. {
  178. HRESULT hr = S_FALSE;
  179. HANDLE_NODE hDependentItemList = HANDLE_NODELIST_INVALID;
  180. HANDLE_NODE hDependentItem = HANDLE_NODE_INVALID;
  181. int iDependentItemOrder = 1;
  182. HANDLE hFile = INVALID_HANDLE_VALUE;
  183. DWORD dwBytesWritten;
  184. TCHAR szFileName[MAX_PATH];
  185. char szWriteBuffer[MAX_PATH + 12]; // max_path is the safe length for the identitystr plus room for the order information
  186. BSTR bstrIdentityStr = NULL;
  187. BOOL fWroteItem = FALSE;
  188. USES_IU_CONVERSION;
  189. hDependentItemList = pxmlCatalog->GetFirstItemDependency(hItem, &hDependentItem);
  190. if (HANDLE_NODELIST_INVALID != hDependentItemList)
  191. {
  192. hr = PathCchCombine(szFileName, ARRAYSIZE(szFileName), pszDestinationFolder, _T("order.txt"));
  193. if (FAILED(hr))
  194. {
  195. pxmlCatalog->CloseItem(hDependentItem);
  196. pxmlCatalog->CloseItemList(hDependentItemList);
  197. return hr;
  198. }
  199. hFile = CreateFile(szFileName, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
  200. if (INVALID_HANDLE_VALUE == hFile)
  201. {
  202. hr = HRESULT_FROM_WIN32(GetLastError());
  203. pxmlCatalog->CloseItem(hDependentItem);
  204. pxmlCatalog->CloseItemList(hDependentItemList);
  205. return hr;
  206. }
  207. hr = S_OK;
  208. while (hr == S_OK)
  209. {
  210. if (HANDLE_NODELIST_INVALID != hDependentItem)
  211. {
  212. pxmlCatalog->GetIdentityStr(hDependentItem, &bstrIdentityStr);
  213. hr = StringCchPrintfExA(szWriteBuffer, ARRAYSIZE(szWriteBuffer), NULL, NULL, MISTSAFE_STRING_FLAGS,
  214. "%d = %s\r\n", iDependentItemOrder, OLE2A(bstrIdentityStr));
  215. if (FAILED(hr))
  216. {
  217. SafeSysFreeString(bstrIdentityStr);
  218. pxmlCatalog->CloseItem(hDependentItem);
  219. pxmlCatalog->CloseItemList(hDependentItemList);
  220. return hr;
  221. }
  222. WriteFile(hFile, szWriteBuffer, lstrlenA(szWriteBuffer), &dwBytesWritten, NULL);
  223. iDependentItemOrder++;
  224. SafeSysFreeString(bstrIdentityStr);
  225. pxmlCatalog->CloseItem(hDependentItem);
  226. fWroteItem = TRUE;
  227. }
  228. hr = pxmlCatalog->GetNextItemDependency(hDependentItemList, &hDependentItem);
  229. }
  230. pxmlCatalog->CloseItemList(hDependentItemList);
  231. CloseHandle(hFile);
  232. if (!fWroteItem)
  233. DeleteFile(szFileName); // no dependencies written
  234. if (SUCCEEDED(hr))
  235. hr = S_OK; // convert S_FALSE to S_OK, we successfully wrote the dependencylist
  236. }
  237. return hr;
  238. }
  239. /////////////////////////////////////////////////////////////////////////////
  240. // _Download()
  241. //
  242. // Do synchronous downloading.
  243. // Input:
  244. // bstrClientName - the name of the client, for history logging use
  245. // bstrXmlCatalog - the xml catalog portion containing items to be downloaded
  246. // bstrDestinationFolder - the destination folder. Null will use the default IU folder
  247. // lMode - bitmask indicates throttled/foreground and notification options
  248. // punkProgressListener - the callback function pointer for reporting download progress
  249. // hWnd - the event msg window handler passed from the stub
  250. // Output:
  251. // pbstrXmlItems - the items with download status in xml format
  252. // e.g.
  253. // <id guid="2560AD4D-3ED3-49C6-A937-4368C0B0E06D" downloaded="1"/>
  254. /////////////////////////////////////////////////////////////////////////////
  255. HRESULT _Download(BSTR bstrClientName, BSTR bstrXmlCatalog, BSTR bstrDestinationFolder, LONG lMode,
  256. IUnknown *punkProgressListener, HWND hWnd, BSTR bstrUuidOperation, BSTR *pbstrXmlItems,
  257. CEngUpdate* pEngUpdate)
  258. {
  259. LOG_Block("Download()");
  260. HRESULT hr = S_OK;
  261. HRESULT hrGlobalItemFailure = S_OK;
  262. LPTSTR lpszClientInfo = NULL;
  263. TCHAR szBaseDestinationFolder[MAX_PATH];
  264. TCHAR szDestinationFolder[MAX_PATH];
  265. TCHAR szItemPath[MAX_PATH];
  266. LPTSTR pszCabUrl = NULL;
  267. HANDLE_NODE hCatalogItemList = HANDLE_NODELIST_INVALID;
  268. HANDLE_NODE hProviderList = HANDLE_NODELIST_INVALID;
  269. HANDLE_NODE hItem = HANDLE_NODE_INVALID;
  270. HANDLE_NODE hProvider = HANDLE_NODE_INVALID;
  271. HANDLE_NODE hXmlItem = HANDLE_NODE_INVALID;
  272. HANDLE_NODE hItemCabList = HANDLE_NODELIST_INVALID;
  273. BSTR bstrCabUrl = NULL;
  274. BSTR bstrLocalFileName = NULL;
  275. BSTR bstrProviderName = NULL;
  276. BSTR bstrProviderPublisher = NULL;
  277. BSTR bstrProviderUUID = NULL;
  278. BSTR bstrProviderIdentityStr = NULL;
  279. BSTR bstrItemPath = NULL;
  280. BSTR bstrInstallerType = NULL;
  281. BSTR bstrLanguage = NULL;
  282. BSTR bstrPlatformDir = NULL;
  283. BSTR bstrTemp = NULL;
  284. BSTR bstrCRC = NULL;
  285. BOOL fCabPatchAvail;
  286. BOOL fReboot;
  287. BOOL fExclusive;
  288. LONG lCommandCount;
  289. LONG lCabSize = 0;
  290. LPTSTR pszLocalFileName = NULL;
  291. LPTSTR pszAllocatedFileName = NULL;
  292. BOOL fNTFSDriveAvailable = FALSE;
  293. TCHAR szFileSystemType[12];
  294. TCHAR szLargestFATDrive[4];
  295. int iMaxNTFSDriveFreeSpace = 0;
  296. int iMaxDriveFreeSpace = 0;
  297. BOOL fCorpCase = FALSE;
  298. BOOL fContinue = TRUE; // for async mode
  299. BOOL fUseSuppliedPath = FALSE;
  300. long n;
  301. DWORD dwBytesDownloaded = 0;
  302. DWORD dwCount1, dwCount2, dwElapsedTime;
  303. DWORD dwTotalElapsedTime = 0;
  304. DWORD dwTotalBytesDownloaded = 0;
  305. DWORD dwWaitResult;
  306. DWORD dwHistoricalSpeed = 0;
  307. DWORD dwHistoricalTime = 0;
  308. DWORD dwSize;
  309. DWORD dwRet;
  310. HKEY hkeyIU = NULL;
  311. HANDLE hMutex = NULL;
  312. DCB_DATA CallbackData;
  313. {
  314. CXmlCatalog xmlCatalog;
  315. CXmlItems xmlItemList;
  316. LPTSTR ptszLivePingServerUrl = NULL;
  317. LPTSTR ptszCorpPingServerUrl = NULL;
  318. DWORD dwFlags = 0;
  319. // clear any previous cancel event
  320. ResetEvent(pEngUpdate->m_evtNeedToQuit);
  321. ZeroMemory(&CallbackData, sizeof(CallbackData));
  322. CallbackData.pOperationMgr = &pEngUpdate->m_OperationMgr;
  323. USES_IU_CONVERSION;
  324. CIUHistory history;
  325. lpszClientInfo = OLE2T(bstrClientName);
  326. if (NULL != (ptszLivePingServerUrl = (LPTSTR)HeapAlloc(
  327. GetProcessHeap(),
  328. HEAP_ZERO_MEMORY,
  329. INTERNET_MAX_URL_LENGTH * sizeof(TCHAR))))
  330. {
  331. if (FAILED(g_pUrlAgent->GetLivePingServer(ptszLivePingServerUrl, INTERNET_MAX_URL_LENGTH)))
  332. {
  333. LOG_Out(_T("failed to get live ping server URL"));
  334. SafeHeapFree(ptszLivePingServerUrl);
  335. }
  336. }
  337. else
  338. {
  339. LOG_Out(_T("failed to allocate memory for ptszLivePingServerUrl"));
  340. }
  341. if (NULL != (ptszCorpPingServerUrl = (LPTSTR)HeapAlloc(
  342. GetProcessHeap(),
  343. HEAP_ZERO_MEMORY,
  344. INTERNET_MAX_URL_LENGTH * sizeof(TCHAR))))
  345. {
  346. if (FAILED(g_pUrlAgent->GetCorpPingServer(ptszCorpPingServerUrl, INTERNET_MAX_URL_LENGTH)))
  347. {
  348. LOG_Out(_T("failed to get corp WU ping server URL"));
  349. SafeHeapFree(ptszCorpPingServerUrl);
  350. }
  351. }
  352. else
  353. {
  354. LOG_Out(_T("failed to allocate memory for ptszCorpPingServerUrl"));
  355. }
  356. CUrlLog pingSvr(lpszClientInfo, ptszLivePingServerUrl, ptszCorpPingServerUrl);
  357. SafeHeapFree(ptszLivePingServerUrl);
  358. SafeHeapFree(ptszCorpPingServerUrl);
  359. if (FAILED(hr = g_pUrlAgent->IsClientSpecifiedByPolicy(lpszClientInfo)))
  360. {
  361. LOG_ErrorMsg(hr);
  362. goto CleanUp;
  363. }
  364. //
  365. // Set the flags for use by DownloadFile
  366. //
  367. if (S_FALSE == hr)
  368. {
  369. dwFlags = 0;
  370. hr = S_OK;
  371. }
  372. else // S_OK
  373. {
  374. dwFlags = WUDF_DONTALLOWPROXY;
  375. LOG_Internet(_T("WUDF_DONTALLOWPROXY set"));
  376. }
  377. pszCabUrl = (LPTSTR) HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, INTERNET_MAX_URL_LENGTH * sizeof(TCHAR));
  378. if (NULL == pszCabUrl)
  379. {
  380. dwRet = GetLastError();
  381. hr = HRESULT_FROM_WIN32(dwRet);
  382. LOG_ErrorMsg(hr);
  383. goto CleanUp;
  384. }
  385. CallbackData.bstrOperationUuid = (NULL == bstrUuidOperation) ? NULL : SysAllocString(bstrUuidOperation);
  386. CallbackData.hEventFiringWnd = hWnd;
  387. if (NULL != punkProgressListener)
  388. {
  389. // get the IProgressListener interface pointer from the IUnknown pointer.. If the
  390. // interface is not supported the pProgressListener is set to NULL
  391. punkProgressListener->QueryInterface(IID_IProgressListener, (void**)&CallbackData.pProgressListener);
  392. }
  393. else
  394. {
  395. CallbackData.pProgressListener = NULL;
  396. }
  397. // Check for Corporate Download Handling Mode
  398. if ((DWORD) lMode & (DWORD) UPDATE_CORPORATE_MODE)
  399. {
  400. fCorpCase = TRUE;
  401. }
  402. // Check for Progress Notification Requested Mode
  403. if ((DWORD) lMode & (DWORD) UPDATE_NOTIFICATION_10PCT)
  404. {
  405. CallbackData.flProgressPercentage = (float).10;
  406. }
  407. else if ((DWORD) lMode & (DWORD) UPDATE_NOTIFICATION_5PCT)
  408. {
  409. CallbackData.flProgressPercentage = (float).05;
  410. }
  411. else if ((DWORD) lMode & (DWORD) UPDATE_NOTIFICATION_1PCT)
  412. {
  413. CallbackData.flProgressPercentage = (float).01;
  414. }
  415. else if ((DWORD) lMode & (DWORD) UPDATE_NOTIFICATION_COMPLETEONLY)
  416. {
  417. CallbackData.flProgressPercentage = (float) 1;
  418. }
  419. else
  420. {
  421. CallbackData.flProgressPercentage = 0;
  422. }
  423. if (NULL != bstrDestinationFolder && 0 < SysStringLen(bstrDestinationFolder))
  424. {
  425. if (SysStringLen(bstrDestinationFolder) > MAX_CORPORATE_PATH)
  426. {
  427. hr = E_INVALIDARG;
  428. LOG_ErrorMsg(hr);
  429. LogMessage("Catalog Download Path Greater Than (%d)", MAX_CORPORATE_PATH);
  430. goto CleanUp;
  431. }
  432. // Caller specified a Base Path - Set this Flag so we don't create our temp folder
  433. // structure under this path.
  434. fUseSuppliedPath = TRUE;
  435. //
  436. // user passed in a designated path, this is to signal the
  437. // download-no-install case, usually for corporate site
  438. //
  439. hr = StringCchCopyEx(szBaseDestinationFolder,
  440. ARRAYSIZE(szBaseDestinationFolder),
  441. OLE2T(bstrDestinationFolder),
  442. NULL, NULL, MISTSAFE_STRING_FLAGS);
  443. if (FAILED(hr))
  444. {
  445. LOG_ErrorMsg(hr);
  446. goto CleanUp;
  447. }
  448. //
  449. // verify that we have write access to this folder
  450. // --- most likely it's a UNC path
  451. //
  452. DWORD dwErr = ValidateFolder(szBaseDestinationFolder, TRUE);
  453. if (ERROR_SUCCESS != dwErr)
  454. {
  455. LOG_ErrorMsg(dwErr);
  456. goto CleanUp;
  457. }
  458. //
  459. // Find out if this Path is a UNC
  460. //
  461. if ('\\' == szBaseDestinationFolder[0] && '\\' == szBaseDestinationFolder[1])
  462. {
  463. // correct the path to the UNC to get the available space
  464. hr = StringCchCopyEx(szDestinationFolder, ARRAYSIZE(szDestinationFolder),
  465. szBaseDestinationFolder,
  466. NULL, NULL, MISTSAFE_STRING_FLAGS);
  467. if (FAILED(hr))
  468. {
  469. LOG_ErrorMsg(hr);
  470. goto CleanUp;
  471. }
  472. LPTSTR pszWalk = szDestinationFolder;
  473. pszWalk += 2; // skip the double slash
  474. pszWalk = StrChr(pszWalk, '\\'); // find the next slash (separate machine and share name)
  475. pszWalk += 1;
  476. pszWalk = StrChr(pszWalk, '\\'); // try to find the next slash (end of share name)
  477. if (NULL == pszWalk)
  478. {
  479. // no trailing slash and no further path information
  480. hr = PathCchAddBackslash(szDestinationFolder, ARRAYSIZE(szBaseDestinationFolder));
  481. if (FAILED(hr))
  482. {
  483. LOG_ErrorMsg(hr);
  484. goto CleanUp;
  485. }
  486. }
  487. else
  488. {
  489. // this path has a trailing slash (may have more path information, truncate after the slash)
  490. pszWalk += 1;
  491. *pszWalk = '\0';
  492. }
  493. GetFreeDiskSpace(szDestinationFolder, &iMaxDriveFreeSpace);
  494. }
  495. else
  496. {
  497. // path must be a local drive
  498. GetFreeDiskSpace(szBaseDestinationFolder[0], &iMaxDriveFreeSpace);
  499. }
  500. }
  501. else
  502. {
  503. //
  504. // user passed in NULL as the destination folder,
  505. // it means this is the normal case to download and install
  506. // updates for this machine. we will try to find the
  507. // drive with the most free space
  508. //
  509. TCHAR szDriveList[MAX_PATH];
  510. GetLogicalDriveStrings(MAX_PATH, szDriveList);
  511. LPTSTR pszCurrent = szDriveList;
  512. int iSize;
  513. //
  514. // find the local fixed drive with the 'most' free space
  515. //
  516. while (NULL != pszCurrent && *pszCurrent != _T('\0'))
  517. {
  518. fContinue = (WaitForSingleObject(pEngUpdate->m_evtNeedToQuit, 0) != WAIT_OBJECT_0);
  519. if (DRIVE_FIXED == GetDriveType(pszCurrent))
  520. {
  521. hr = GetFreeDiskSpace(*pszCurrent, &iSize);
  522. if (FAILED(hr))
  523. {
  524. LOG_Error(_T("Error Reading Drive Space %c, hr = 0x%08x"), *pszCurrent, hr);
  525. pszCurrent += (lstrlen(pszCurrent) + 1); // skip current and null terminater
  526. continue;
  527. }
  528. if (!GetVolumeInformation(pszCurrent, NULL, 0, NULL, NULL, NULL, szFileSystemType, ARRAYSIZE(szFileSystemType)))
  529. {
  530. DWORD dwErr = GetLastError();
  531. LOG_Error(_T("Error Reading VolumeInfo for Drive %c, GLE = %d"), *pszCurrent, dwErr);
  532. pszCurrent += (lstrlen(pszCurrent) + 1); // skip current and null terminater
  533. continue;
  534. }
  535. if (CSTR_EQUAL == CompareString(MAKELCID(MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US), SORT_DEFAULT), NORM_IGNORECASE,
  536. szFileSystemType, -1, _T("NTFS"), -1))
  537. {
  538. fNTFSDriveAvailable = TRUE;
  539. if (iSize > iMaxNTFSDriveFreeSpace)
  540. {
  541. iMaxNTFSDriveFreeSpace = iSize;
  542. hr = StringCchCopyEx(szBaseDestinationFolder, ARRAYSIZE(szBaseDestinationFolder), pszCurrent,
  543. NULL, NULL, MISTSAFE_STRING_FLAGS);
  544. if (FAILED(hr))
  545. {
  546. LOG_ErrorMsg(hr);
  547. continue;
  548. }
  549. }
  550. }
  551. else
  552. {
  553. // we want to keep track of non NTFS drive sizes in case there the largest
  554. // NTFS drive size is too small, but a FAT partition has enough space. In this
  555. // case we want to fall back to the FAT partition. Note: this is a behavior change
  556. // from the initial design where we treated NTFS and FAT as mutually exclusive with
  557. // NTFS always winning.
  558. if (iSize > iMaxDriveFreeSpace)
  559. {
  560. iMaxDriveFreeSpace = iSize;
  561. if (!fNTFSDriveAvailable)
  562. {
  563. // if no NTFS drive is available save this drive letter as the preferred
  564. hr = StringCchCopyEx(szBaseDestinationFolder, ARRAYSIZE(szBaseDestinationFolder), pszCurrent,
  565. NULL, NULL, MISTSAFE_STRING_FLAGS);
  566. if (FAILED(hr))
  567. {
  568. LOG_ErrorMsg(hr);
  569. continue;
  570. }
  571. hr = StringCchCopyEx(szLargestFATDrive, ARRAYSIZE(szLargestFATDrive), pszCurrent,
  572. NULL, NULL, MISTSAFE_STRING_FLAGS);
  573. if (FAILED(hr))
  574. {
  575. LOG_ErrorMsg(hr);
  576. continue;
  577. }
  578. }
  579. else
  580. {
  581. // NTFS drive exists, save this drive as a back up choice for the size check.
  582. hr = StringCchCopyEx(szLargestFATDrive, ARRAYSIZE(szLargestFATDrive), pszCurrent,
  583. NULL, NULL, MISTSAFE_STRING_FLAGS);
  584. if (FAILED(hr))
  585. {
  586. LOG_ErrorMsg(hr);
  587. continue;
  588. }
  589. }
  590. }
  591. }
  592. }
  593. pszCurrent += (lstrlen(pszCurrent) + 1); // skip current and null terminater
  594. }
  595. if (!fContinue)
  596. {
  597. hr = E_UNEXPECTED;
  598. goto CleanUp;
  599. }
  600. if ((0 == iMaxDriveFreeSpace) && (0 == iMaxNTFSDriveFreeSpace))
  601. {
  602. //
  603. // running on a system with no local drives?
  604. //
  605. hr = E_FAIL;
  606. LOG_ErrorMsg(hr);
  607. goto CleanUp;
  608. }
  609. }
  610. //
  611. // load the XML document into the XmlCatalog Class
  612. //
  613. if (WaitForSingleObject(pEngUpdate->m_evtNeedToQuit, 0) == WAIT_OBJECT_0)
  614. {
  615. hr = E_ABORT;
  616. goto CleanUp;
  617. }
  618. hr = xmlCatalog.LoadXMLDocument(bstrXmlCatalog, pEngUpdate->m_fOfflineMode);
  619. if (FAILED(hr))
  620. {
  621. LOG_ErrorMsg(hr);
  622. goto CleanUp;
  623. }
  624. // We need to find the total estimated size of the download we're about to do.
  625. // We'll walk the XML Catalog getting Size Info for each item.
  626. hr = xmlCatalog.GetTotalEstimatedSize(&CallbackData.lTotalDownloadSize);
  627. if (FAILED(hr))
  628. {
  629. LOG_ErrorMsg(hr);
  630. goto CleanUp;
  631. }
  632. CallbackData.lTotalDownloaded = 0;
  633. //
  634. // added by JHou - bug#314: download does not detect available free space on local hard drive
  635. //
  636. // The lTotalDownloadSize is the size of the download in Bytes, the MaxDriveSpace is in KBytes
  637. if ((CallbackData.lTotalDownloadSize / 1024) > ((fNTFSDriveAvailable) ? iMaxNTFSDriveFreeSpace : iMaxDriveFreeSpace))
  638. {
  639. // Before we bail out of the download we need to look to see if we excluded a chose a NTFS drive
  640. // over a FAT drive. If the NTFS drive doesn't have enough space, but a FAT drive does we want to
  641. // go ahead and allow the use of the FAT drive. This is a change in spec'd behavior per bug: 413079
  642. if ((CallbackData.lTotalDownloadSize / 1024) < iMaxDriveFreeSpace)
  643. {
  644. // no error.. a FAT partition has enough free space, use it instead
  645. hr = StringCchCopyEx(szBaseDestinationFolder, ARRAYSIZE(szBaseDestinationFolder), szLargestFATDrive,
  646. NULL, NULL, MISTSAFE_STRING_FLAGS);
  647. if (FAILED(hr))
  648. {
  649. LOG_ErrorMsg(hr);
  650. goto CleanUp;
  651. }
  652. }
  653. else
  654. {
  655. // tried both NTFS and FAT partitions.. none have enough space.. bail out.
  656. dwRet = ERROR_DISK_FULL;
  657. LOG_ErrorMsg(dwRet);
  658. hr = HRESULT_FROM_WIN32(dwRet);
  659. // need to write items result information for each item indicating it failed because of diskspace
  660. hrGlobalItemFailure = HRESULT_FROM_WIN32(dwRet);
  661. }
  662. }
  663. if (SUCCEEDED(hrGlobalItemFailure))
  664. {
  665. if (!fUseSuppliedPath)
  666. {
  667. // When a destination folder is specified, we don't need to add anything to it. If no path
  668. // is specified we pick a drive letter, so we need to add the WUTemp directory
  669. // to that base path.
  670. hr = StringCchCatEx(szBaseDestinationFolder, ARRAYSIZE(szBaseDestinationFolder), IU_WUTEMP,
  671. NULL, NULL, MISTSAFE_STRING_FLAGS);
  672. if (FAILED(hr))
  673. {
  674. LOG_ErrorMsg(hr);
  675. goto CleanUp;
  676. }
  677. }
  678. //
  679. // 500953 Allow Power Users to access WUTEMP
  680. //
  681. if (FAILED(hr = CreateDirectoryAndSetACLs(szBaseDestinationFolder, TRUE)))
  682. {
  683. LOG_ErrorMsg(hr);
  684. hrGlobalItemFailure = hr;
  685. }
  686. DWORD dwAttr = GetFileAttributes(szBaseDestinationFolder);
  687. if (INVALID_FILE_ATTRIBUTES == dwAttr || 0 == (FILE_ATTRIBUTE_DIRECTORY & dwAttr))
  688. {
  689. //
  690. // Only create directory if it doesn't already exist (Power Users can't
  691. // SetFileAttributes if an administrator created the directory originally).
  692. //
  693. if (!fUseSuppliedPath &&
  694. !SetFileAttributes(szBaseDestinationFolder, FILE_ATTRIBUTE_HIDDEN))
  695. {
  696. DWORD dwErr = GetLastError();
  697. LOG_ErrorMsg(dwErr);
  698. hr = HRESULT_FROM_WIN32(dwErr);
  699. hrGlobalItemFailure = HRESULT_FROM_WIN32(dwRet);
  700. }
  701. }
  702. #if defined(UNICODE) || defined(_UNICODE)
  703. LogMessage("Download destination root folder is: %ls", szBaseDestinationFolder);
  704. #else
  705. LogMessage("Download destination root folder is: %s", szBaseDestinationFolder);
  706. #endif
  707. if (fCorpCase)
  708. {
  709. history.SetDownloadBasePath(szBaseDestinationFolder);
  710. }
  711. }
  712. //
  713. // loop through each provider in the catalog, then each item in the provider
  714. //
  715. if (WaitForSingleObject(pEngUpdate->m_evtNeedToQuit, 0) == WAIT_OBJECT_0)
  716. {
  717. hr = E_ABORT;
  718. goto CleanUp;
  719. }
  720. hProviderList = xmlCatalog.GetFirstProvider(&hProvider);
  721. while (fContinue && HANDLE_NODE_INVALID != hProvider)
  722. {
  723. xmlCatalog.GetIdentity(hProvider, &bstrProviderName, &bstrProviderPublisher, &bstrProviderUUID);
  724. xmlCatalog.GetIdentityStr(hProvider, &bstrProviderIdentityStr);
  725. //
  726. // Get the Enumerator List of Items in this Provider, and get the first item
  727. //
  728. hCatalogItemList = xmlCatalog.GetFirstItem(hProvider, &hItem);
  729. if ((HANDLE_NODELIST_INVALID == hCatalogItemList) || (HANDLE_NODE_INVALID == hItem))
  730. {
  731. // No Items under this Provider
  732. xmlCatalog.GetNextProvider(hProviderList, &hProvider);
  733. continue;
  734. }
  735. while (fContinue && HANDLE_NODE_INVALID != hItem)
  736. {
  737. if (FAILED(hrGlobalItemFailure))
  738. {
  739. xmlItemList.AddItem(&xmlCatalog, hItem, &hXmlItem);
  740. bstrTemp = T2BSTR(_T(""));
  741. xmlItemList.AddDownloadPath(hXmlItem, bstrTemp);
  742. SafeSysFreeString(bstrTemp);
  743. history.AddHistoryItemDownloadStatus(&xmlCatalog, hItem, HISTORY_STATUS_FAILED, /*no download path*/_T(""), lpszClientInfo, hrGlobalItemFailure);
  744. xmlItemList.AddDownloadStatus(hXmlItem, KEY_STATUS_FAILED, hrGlobalItemFailure);
  745. xmlCatalog.CloseItem(hItem);
  746. xmlCatalog.GetNextItem(hCatalogItemList, &hItem);
  747. continue;
  748. }
  749. LONG lCallbackRequest = 0; // check if user set something in callback
  750. xmlCatalog.GetIdentityStr(hItem, &bstrItemPath);
  751. if (NULL == bstrItemPath)
  752. {
  753. LOG_Download(_T("Failed to Get Identity String for an Item"));
  754. xmlCatalog.CloseItem(hItem);
  755. xmlCatalog.GetNextItem(hCatalogItemList, &hItem);
  756. continue;
  757. }
  758. //
  759. // send out status to caller to tell which item we are about to download
  760. //
  761. BSTR bstrXmlItemForCallback = NULL;
  762. if (SUCCEEDED(xmlCatalog.GetBSTRItemForCallback(hItem, &bstrXmlItemForCallback)))
  763. {
  764. CallbackData.lCurrentItemSize = 0;
  765. DownloadCallback(&CallbackData,
  766. DOWNLOAD_STATUS_ITEMSTART,
  767. 0,
  768. 0,
  769. bstrXmlItemForCallback,
  770. &lCallbackRequest);
  771. SafeSysFreeString(bstrXmlItemForCallback);
  772. bstrXmlItemForCallback = NULL;
  773. if (UPDATE_COMMAND_CANCEL == lCallbackRequest)
  774. {
  775. LOG_Out(_T("Download Callback received UPDATE_COMMAND_CANCEL"));
  776. SetEvent(pEngUpdate->m_evtNeedToQuit); // asked to quit
  777. fContinue = FALSE;
  778. }
  779. else
  780. {
  781. //
  782. // check the global quit event. If quit, then server ping treat it as a cancel.
  783. //
  784. fContinue = (WaitForSingleObject(pEngUpdate->m_evtNeedToQuit, 0) != WAIT_OBJECT_0);
  785. }
  786. if (!fContinue)
  787. {
  788. continue; // or break, same effect.
  789. }
  790. }
  791. else
  792. {
  793. //
  794. // something wrong with this item, so we should skip it
  795. //
  796. continue;
  797. }
  798. if (fCorpCase)
  799. {
  800. LPCTSTR szName = NULL;
  801. // Corporate Folder Path is Constructed from Several Item Elements
  802. // Software | Driver\<Locale>\<ProviderIdentity>\<Platform>\<ItemIdentity>.<version>
  803. xmlCatalog.GetItemInstallInfo(hItem, &bstrInstallerType, &fExclusive, &fReboot, &lCommandCount);
  804. if (NULL == bstrInstallerType)
  805. {
  806. LOG_Download(_T("Missing InstallerType Info for Item %ls"), bstrItemPath);
  807. goto doneCorpCase;
  808. }
  809. if (CSTR_EQUAL == CompareStringW(MAKELCID(MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US), SORT_DEFAULT), NORM_IGNORECASE,
  810. (LPCWSTR)bstrInstallerType, -1, L"CDM", -1))
  811. {
  812. szName = _T("Driver");
  813. }
  814. else
  815. {
  816. szName = _T("Software");
  817. }
  818. hr = StringCchCopyEx(szItemPath, ARRAYSIZE(szItemPath), szName, NULL, NULL, MISTSAFE_STRING_FLAGS);
  819. if (FAILED(hr))
  820. goto doneCorpCase;
  821. xmlCatalog.GetItemLanguage(hItem, &bstrLanguage);
  822. xmlCatalog.GetCorpItemPlatformStr(hItem, &bstrPlatformDir);
  823. if (NULL == bstrLanguage || NULL == bstrPlatformDir)
  824. {
  825. LOG_Download(_T("Missing Language or Platform Info for Item %ls"), bstrItemPath);
  826. goto doneCorpCase;
  827. }
  828. hr = PathCchCombine(szDestinationFolder, ARRAYSIZE(szDestinationFolder), szBaseDestinationFolder, szItemPath);
  829. if (FAILED(hr))
  830. goto doneCorpCase;
  831. hr = PathCchAppend(szDestinationFolder, ARRAYSIZE(szDestinationFolder), OLE2T(bstrLanguage));
  832. if (FAILED(hr))
  833. goto doneCorpCase;
  834. hr = PathCchAppend(szDestinationFolder, ARRAYSIZE(szDestinationFolder), OLE2T(bstrProviderIdentityStr));
  835. if (FAILED(hr))
  836. goto doneCorpCase;
  837. hr = PathCchAppend(szDestinationFolder, ARRAYSIZE(szDestinationFolder), OLE2T(bstrPlatformDir));
  838. if (FAILED(hr))
  839. goto doneCorpCase;
  840. hr = PathCchAppend(szDestinationFolder, ARRAYSIZE(szDestinationFolder), OLE2T(bstrItemPath));
  841. if (FAILED(hr))
  842. goto doneCorpCase;
  843. doneCorpCase:
  844. SafeSysFreeString(bstrInstallerType);
  845. SafeSysFreeString(bstrLanguage);
  846. SafeSysFreeString(bstrPlatformDir);
  847. if (FAILED(hr))
  848. {
  849. LOG_ErrorMsg(hr);
  850. SafeSysFreeString(bstrItemPath);
  851. xmlCatalog.CloseItem(hItem);
  852. xmlCatalog.GetNextItem(hCatalogItemList, &hItem);
  853. continue;
  854. }
  855. }
  856. else
  857. {
  858. hr = PathCchCombine(szDestinationFolder, ARRAYSIZE(szDestinationFolder), szBaseDestinationFolder, OLE2T(bstrItemPath));
  859. if (FAILED(hr))
  860. {
  861. LOG_ErrorMsg(hr);
  862. SafeSysFreeString(bstrItemPath);
  863. xmlCatalog.CloseItem(hItem);
  864. xmlCatalog.GetNextItem(hCatalogItemList, &hItem);
  865. continue;
  866. }
  867. }
  868. if (FAILED(hr = CreateDirectoryAndSetACLs(szDestinationFolder, TRUE)))
  869. {
  870. LOG_ErrorMsg(hr);
  871. xmlCatalog.CloseItem(hItem);
  872. xmlCatalog.GetNextItem(hCatalogItemList, &hItem);
  873. SafeSysFreeString(bstrItemPath);
  874. continue;
  875. }
  876. //
  877. // Now get the collection of CodeBases for this Item
  878. //
  879. hItemCabList = xmlCatalog.GetItemFirstCodeBase(hItem, &bstrCabUrl, &bstrLocalFileName, &bstrCRC, &fCabPatchAvail, &lCabSize);
  880. if ((HANDLE_NODELIST_INVALID == hItemCabList) || (NULL == bstrCabUrl))
  881. {
  882. // No Cabs for this Item?? skip it.
  883. LOG_Download(_T("Item: %ls has no cabs, Skipping"), bstrItemPath);
  884. SafeSysFreeString(bstrItemPath);
  885. xmlCatalog.CloseItem(hItem);
  886. xmlCatalog.GetNextItem(hCatalogItemList, &hItem);
  887. continue;
  888. }
  889. while (fContinue && NULL != bstrCabUrl)
  890. {
  891. LPTSTR pszTempCabUrl = OLE2T(bstrCabUrl);
  892. // pszCabUrl is allocated to be INTERNET_MAX_URL_LENGTH above.
  893. hr = StringCchCopyEx(pszCabUrl, INTERNET_MAX_URL_LENGTH, pszTempCabUrl,
  894. NULL, NULL, MISTSAFE_STRING_FLAGS);
  895. if (FAILED(hr))
  896. {
  897. LOG_ErrorMsg(hr);
  898. break;
  899. }
  900. SafeMemFree(pszTempCabUrl);
  901. if (NULL != bstrLocalFileName && SysStringLen(bstrLocalFileName) > 0)
  902. {
  903. if (NULL != pszAllocatedFileName)
  904. {
  905. MemFree(pszAllocatedFileName);
  906. }
  907. pszAllocatedFileName = OLE2T(bstrLocalFileName);
  908. }
  909. else
  910. {
  911. //
  912. // has not specified file name, use the same file name in URL
  913. //
  914. // search for the last forward slash (will separate the URL from the filename)
  915. LPTSTR lpszLastSlash = StrRChr(pszCabUrl, NULL, _T('/'));
  916. if (NULL != lpszLastSlash)
  917. {
  918. // last slash was found, skip to next character (will be the beginning of the filename)
  919. lpszLastSlash++;
  920. }
  921. pszLocalFileName = lpszLastSlash;
  922. }
  923. // Download the Cab - Store Information for Progress Callbacks
  924. dwBytesDownloaded = 0;
  925. CallbackData.lCurrentItemSize = lCabSize;
  926. dwCount1 = GetTickCount();
  927. hr = DownloadFile(pszCabUrl, // fileurl to download
  928. szDestinationFolder, // destination folder for file
  929. (NULL != pszAllocatedFileName) ? pszAllocatedFileName : pszLocalFileName, // use AllocatedFileName if possible, else use localfilename
  930. &dwBytesDownloaded, // bytes downloaded for this file
  931. &pEngUpdate->m_evtNeedToQuit, // quit event array
  932. 1, // number of events
  933. DownloadCallback, // callback function
  934. &CallbackData, // data structure for callback function
  935. dwFlags);
  936. if (FAILED(hr))
  937. {
  938. //
  939. // added by JHou: bug335292 - Temporary folder not deleted when network plug removed
  940. //
  941. // only empty folder can be deleted successfully so if RemoveDirectory() failed that
  942. // may because it's not empty which means it's ok
  943. if (RemoveDirectory(szDestinationFolder) && fCorpCase)
  944. {
  945. HRESULT hrCopy;
  946. // If this Directory was successfully removed and this is the Corp Case we should
  947. // try to remove its parents up to the base directory.
  948. TCHAR szCorpDestinationFolderRemove[MAX_PATH];
  949. hrCopy = StringCchCopyEx(szCorpDestinationFolderRemove,
  950. ARRAYSIZE(szCorpDestinationFolderRemove),
  951. szDestinationFolder,
  952. NULL, NULL, MISTSAFE_STRING_FLAGS);
  953. if (FAILED(hrCopy))
  954. {
  955. LOG_ErrorMsg(hrCopy);
  956. break;
  957. }
  958. LPTSTR pszBackslash = NULL;
  959. PathRemoveBackslash(szBaseDestinationFolder); // strip any trailing backslashes - need to normalize this to compare when we're done walking the folder tree
  960. for (;;)
  961. {
  962. pszBackslash = StrRChr(szCorpDestinationFolderRemove, NULL, '\\');
  963. if (NULL == pszBackslash)
  964. break; // unexpected
  965. *pszBackslash = '\0';
  966. if (0 == StrCmp(szCorpDestinationFolderRemove, szBaseDestinationFolder))
  967. break; // reached the base directory, done removing directories;
  968. if (!RemoveDirectory(szCorpDestinationFolderRemove))
  969. break; // couldn't remove folder at this level, assume folder not empty, leave the rest of the structure intact.
  970. }
  971. }
  972. if (E_ABORT == hr)
  973. {
  974. LOG_Download(_T("DownloadFile function returns E_ABORT while downloading %s."), pszCabUrl);
  975. #if defined(UNICODE) || defined(_UNICODE)
  976. LogError(hr, "Download cancelled while processing file %ls", pszCabUrl);
  977. #else
  978. LogError(hr, "Download cancelled while processing file %s", pszCabUrl);
  979. #endif
  980. }
  981. else
  982. {
  983. LOG_Download(_T("Download Failed for URL: %s, Skipping remaining files for this Item"), pszCabUrl);
  984. #if defined(UNICODE) || defined(_UNICODE)
  985. LogError(hr, "Downloading file %ls, skipping remaining files for this Item", pszCabUrl);
  986. #else
  987. LogError(hr, "Downloading file %s, skipping remaining files for this Item", pszCabUrl);
  988. #endif
  989. }
  990. SafeSysFreeString(bstrCabUrl);
  991. //
  992. // since one file got error, we can exit the file loop for the current item
  993. // because missing one file will make this item not usable.
  994. //
  995. break;
  996. }
  997. dwCount2 = GetTickCount();
  998. if (0 != dwBytesDownloaded)
  999. {
  1000. if (dwCount1 < dwCount2) // normal case, no roll-over
  1001. {
  1002. dwElapsedTime = dwCount2 - dwCount1;
  1003. }
  1004. else
  1005. {
  1006. // roll-over case, should almost never happen
  1007. dwElapsedTime = (0xFFFFFFFF - dwCount1) + dwCount2;
  1008. }
  1009. dwTotalBytesDownloaded += dwBytesDownloaded;
  1010. dwTotalElapsedTime += dwElapsedTime;
  1011. }
  1012. // Form the full Path and Filename of the file we just downloaded
  1013. hr = PathCchCombine(szItemPath, ARRAYSIZE(szItemPath), szDestinationFolder,
  1014. (NULL != pszAllocatedFileName) ? pszAllocatedFileName : pszLocalFileName);
  1015. if (FAILED(hr))
  1016. {
  1017. DeleteFile(szItemPath);
  1018. break;
  1019. }
  1020. // Verify CRC
  1021. //---------------
  1022. if (NULL != bstrCRC)
  1023. {
  1024. TCHAR szCRCHash[CRC_HASH_STRING_LENGTH] = {'\0'};
  1025. hr = StringCchCopyEx(szCRCHash, ARRAYSIZE(szCRCHash), OLE2T(bstrCRC), NULL, NULL, MISTSAFE_STRING_FLAGS);
  1026. if (FAILED(hr))
  1027. {
  1028. // Something was wrong with the BSTR we got back from XML. Fail Safely, delete the file.
  1029. // The Failed HR will fail the item
  1030. DeleteFile(szItemPath);
  1031. break;
  1032. }
  1033. hr = VerifyFileCRC(szItemPath, szCRCHash);
  1034. if (HRESULT_FROM_WIN32(ERROR_CRC) == hr || FAILED(hr))
  1035. {
  1036. // The File CRC's Did Not Match, or we had a problem Calculating the CRC. Fail Safely, delete the file.
  1037. // The Failed HR will fail the item
  1038. DeleteFile(szItemPath);
  1039. break;
  1040. }
  1041. }
  1042. // Check Trust
  1043. //---------------
  1044. hr = VerifyFileTrust(szItemPath,
  1045. NULL,
  1046. ReadWUPolicyShowTrustUI()
  1047. );
  1048. if (FAILED(hr))
  1049. {
  1050. // File Was Not Trusted - Need to Delete it and fail the item
  1051. DeleteFile(szItemPath);
  1052. break;
  1053. }
  1054. #if defined(UNICODE) || defined(_UNICODE)
  1055. LogMessage("Downloaded file %ls", pszCabUrl);
  1056. LogMessage("Local path %ls", szItemPath);
  1057. #else
  1058. LogMessage("Downloaded file %s", pszCabUrl);
  1059. LogMessage("Local path %s", szItemPath);
  1060. #endif
  1061. SafeSysFreeString(bstrCabUrl);
  1062. SafeSysFreeString(bstrLocalFileName);
  1063. SafeSysFreeString(bstrCRC);
  1064. bstrCabUrl = bstrLocalFileName = NULL;
  1065. fContinue = SUCCEEDED(xmlCatalog.GetItemNextCodeBase(hItemCabList, &bstrCabUrl, &bstrLocalFileName, &bstrCRC, &fCabPatchAvail, &lCabSize)) &&
  1066. (WaitForSingleObject(pEngUpdate->m_evtNeedToQuit, 0) != WAIT_OBJECT_0);
  1067. }
  1068. // Write XMLItems entry for this download result
  1069. xmlItemList.AddItem(&xmlCatalog, hItem, &hXmlItem);
  1070. bstrTemp = T2BSTR(szDestinationFolder);
  1071. xmlItemList.AddDownloadPath(hXmlItem, bstrTemp);
  1072. SafeSysFreeString(bstrTemp);
  1073. //
  1074. // For "corporate" download write ReadMore Link before writing history (in case we fail
  1075. //
  1076. if (TRUE == fCorpCase)
  1077. {
  1078. //
  1079. // Ignore errors as we want to keep downloaded cab anyway
  1080. //
  1081. (void) CreateReadMoreLink(&xmlCatalog, hItem, szDestinationFolder);
  1082. (void) CreateItemDependencyList(&xmlCatalog, hItem, szDestinationFolder);
  1083. }
  1084. //
  1085. // Also add download history for this item
  1086. //
  1087. if (SUCCEEDED(hr))
  1088. {
  1089. history.AddHistoryItemDownloadStatus(&xmlCatalog, hItem, HISTORY_STATUS_COMPLETE, szDestinationFolder, lpszClientInfo);
  1090. xmlItemList.AddDownloadStatus(hXmlItem, KEY_STATUS_COMPLETE);
  1091. }
  1092. else
  1093. {
  1094. history.AddHistoryItemDownloadStatus(&xmlCatalog, hItem, HISTORY_STATUS_FAILED, szDestinationFolder, lpszClientInfo, hr);
  1095. xmlItemList.AddDownloadStatus(hXmlItem, KEY_STATUS_FAILED, hr);
  1096. }
  1097. //
  1098. // ping server to report the download status for this item
  1099. //
  1100. {
  1101. BSTR bstrIdentityPing = NULL;
  1102. if (SUCCEEDED(xmlCatalog.GetIdentityStrForPing(hItem, &bstrIdentityPing)))
  1103. {
  1104. URLLOGSTATUS status = SUCCEEDED(hr) ? URLLOGSTATUS_Success : URLLOGSTATUS_Failed;
  1105. if (E_ABORT == hr)
  1106. {
  1107. //
  1108. // user/system cancelled the current process
  1109. //
  1110. status = URLLOGSTATUS_Cancelled;
  1111. }
  1112. pingSvr.Ping(
  1113. TRUE, // on-line
  1114. URLLOGDESTINATION_DEFAULT, // going live or corp WU ping server
  1115. &pEngUpdate->m_evtNeedToQuit, // pt to cancel events
  1116. 1, // number of events
  1117. URLLOGACTIVITY_Download, // activity
  1118. status, // status code
  1119. hr, // error code, can be 0 or 1
  1120. OLE2T(bstrIdentityPing), // itemID
  1121. NULL // no device data can be given during dld phase
  1122. );
  1123. }
  1124. SafeSysFreeString(bstrIdentityPing);
  1125. //SafeSysFreeString(bstrPlatformPing);
  1126. //SafeSysFreeString(bstrLanguagePing);
  1127. }
  1128. xmlCatalog.CloseItemList(hItemCabList);
  1129. //
  1130. // done with this item, fire itemcomplete event
  1131. //
  1132. DownloadCallback(&CallbackData, DOWNLOAD_STATUS_ITEMCOMPLETE, CallbackData.lCurrentItemSize, 0, NULL, &lCallbackRequest);
  1133. SafeSysFreeString(bstrItemPath);
  1134. // get the next item. hItem will be HANDLE_NODE_INVALID when there are no
  1135. // remaining items.
  1136. xmlCatalog.CloseItem(hItem);
  1137. xmlCatalog.GetNextItem(hCatalogItemList, &hItem);
  1138. if (UPDATE_COMMAND_CANCEL == lCallbackRequest)
  1139. {
  1140. LOG_Out(_T("Download Callback received UPDATE_COMMAND_CANCEL"));
  1141. SetEvent(pEngUpdate->m_evtNeedToQuit); // asked to quit
  1142. fContinue = FALSE;
  1143. }
  1144. else
  1145. {
  1146. //
  1147. // check the global quit event. If quit, then server ping treat it as a cancel.
  1148. // TODO: also need to check the operation quit event!
  1149. //
  1150. fContinue = (WaitForSingleObject(pEngUpdate->m_evtNeedToQuit, 0) != WAIT_OBJECT_0);
  1151. }
  1152. }
  1153. xmlCatalog.CloseItemList(hCatalogItemList);
  1154. SafeSysFreeString(bstrProviderName);
  1155. SafeSysFreeString(bstrProviderPublisher);
  1156. SafeSysFreeString(bstrProviderUUID);
  1157. SafeSysFreeString(bstrProviderIdentityStr);
  1158. xmlCatalog.CloseItem(hProvider);
  1159. xmlCatalog.GetNextProvider(hProviderList, &hProvider);
  1160. fContinue = (WaitForSingleObject(pEngUpdate->m_evtNeedToQuit, 0) != WAIT_OBJECT_0);
  1161. }
  1162. xmlCatalog.CloseItemList(hProviderList);
  1163. RegOpenKey(HKEY_LOCAL_MACHINE, REGKEY_IUCTL, &hkeyIU);
  1164. hMutex = CreateMutex(NULL, FALSE, IU_MUTEX_HISTORICALSPEED_REGUPDATE);
  1165. if ((0 != dwTotalBytesDownloaded) && (0 != dwTotalElapsedTime) && (NULL != hkeyIU) && (NULL != hMutex))
  1166. {
  1167. HANDLE aHandles[2];
  1168. aHandles[0] = hMutex;
  1169. aHandles[1] = pEngUpdate->m_evtNeedToQuit;
  1170. dwWaitResult = MyMsgWaitForMultipleObjects(ARRAYSIZE(aHandles), aHandles, FALSE, /*30 seconds*/30000, QS_ALLINPUT);
  1171. if (WAIT_OBJECT_0 == dwWaitResult)
  1172. {
  1173. // convert elapsed time from milliseconds to seconds
  1174. dwTotalElapsedTime = dwTotalElapsedTime / 1000;
  1175. if (0 == dwTotalElapsedTime)
  1176. dwTotalElapsedTime = 1; // minimum one second
  1177. // we have the mutex, go ahead and read/write the reg information.
  1178. dwSize = sizeof(dwHistoricalSpeed);
  1179. RegQueryValueEx(hkeyIU, REGVAL_HISTORICALSPEED, NULL, NULL, (LPBYTE)&dwHistoricalSpeed, &dwSize);
  1180. dwSize = sizeof(dwHistoricalTime);
  1181. RegQueryValueEx(hkeyIU, REGVAL_TIMEELAPSED, NULL, NULL, (LPBYTE)&dwHistoricalTime, &dwSize);
  1182. // We need to get the Bytes Downloaded to add the bytes just downloaded
  1183. DWORD dwHistoricalBytes = dwHistoricalSpeed * dwHistoricalTime; // could be 0 if no previous history was recorded
  1184. dwHistoricalBytes += dwTotalBytesDownloaded; // new byte count
  1185. dwHistoricalTime += dwTotalElapsedTime; // new time count
  1186. dwHistoricalSpeed = dwHistoricalBytes / dwHistoricalTime; // calculate new speed bytes/second
  1187. RegSetValueEx(hkeyIU, REGVAL_HISTORICALSPEED, NULL, REG_DWORD, (LPBYTE)&dwHistoricalSpeed, sizeof(dwHistoricalSpeed));
  1188. RegSetValueEx(hkeyIU, REGVAL_TIMEELAPSED, NULL, REG_DWORD, (LPBYTE)&dwHistoricalTime, sizeof(dwHistoricalTime));
  1189. ReleaseMutex(hMutex);
  1190. CloseHandle(hMutex);
  1191. hMutex = NULL;
  1192. }
  1193. }
  1194. //
  1195. // We pass in pEngUpdate->m_evtNeedToQuit to MyMsgWaitForMultipleObjects above so it will exit immediately
  1196. // but don't bother to handle the WAIT_OBJECT_0 + 1 case there since the if statement may not
  1197. // execute and even if this is the case we still need to check pEngUpdate->m_evtNeedToQuit below anyway.
  1198. //
  1199. if (WaitForSingleObject(pEngUpdate->m_evtNeedToQuit, 0) == WAIT_OBJECT_0)
  1200. {
  1201. hr = E_ABORT;
  1202. }
  1203. CleanUp:
  1204. //
  1205. // add HRESULT in case the download failed before the download loop
  1206. //
  1207. if (S_OK != hr)
  1208. {
  1209. xmlItemList.AddGlobalErrorCodeIfNoItems(hr);
  1210. }
  1211. //
  1212. // generate result
  1213. //
  1214. xmlItemList.GetItemsBSTR(pbstrXmlItems);
  1215. SafeSysFreeString(CallbackData.bstrOperationUuid);
  1216. SafeHeapFree(pszCabUrl);
  1217. SafeSysFreeString(bstrCabUrl);
  1218. SafeSysFreeString(bstrLocalFileName);
  1219. SafeSysFreeString(bstrProviderName);
  1220. SafeSysFreeString(bstrProviderPublisher);
  1221. SafeSysFreeString(bstrProviderUUID);
  1222. SafeSysFreeString(bstrProviderIdentityStr);
  1223. SafeSysFreeString(bstrItemPath);
  1224. SafeSysFreeString(bstrInstallerType);
  1225. SafeSysFreeString(bstrLanguage);
  1226. SafeSysFreeString(bstrPlatformDir);
  1227. SafeSysFreeString(bstrTemp);
  1228. if (NULL != hkeyIU)
  1229. {
  1230. RegCloseKey(hkeyIU);
  1231. hkeyIU = NULL;
  1232. }
  1233. if (NULL != hMutex)
  1234. {
  1235. // shouldn't need to release, if hmutex is still valid at this point we were unable to
  1236. // get the mutex
  1237. CloseHandle(hMutex);
  1238. hMutex = NULL;
  1239. }
  1240. }
  1241. //
  1242. // notify that we are completed
  1243. //
  1244. if (NULL != punkProgressListener || NULL != hWnd)
  1245. {
  1246. DownloadCallback(&CallbackData, DOWNLOAD_STATUS_OPERATIONCOMPLETE, 0, 0, *pbstrXmlItems, NULL);
  1247. }
  1248. if (SUCCEEDED(hr))
  1249. {
  1250. LogMessage("%s %s", SZ_SEE_IUHIST, SZ_DOWNLOAD_FINISHED);
  1251. }
  1252. else
  1253. {
  1254. LogError(hr, "%s %s", SZ_SEE_IUHIST, SZ_DOWNLOAD_FINISHED);
  1255. }
  1256. return hr;
  1257. }
  1258. /////////////////////////////////////////////////////////////////////////////
  1259. // Download()
  1260. //
  1261. // Do synchronous downloading.
  1262. // Input:
  1263. // bstrXmlClientInfo - the credentials of the client in xml format
  1264. // bstrXmlCatalog - the xml catalog portion containing items to be downloaded
  1265. // bstrDestinationFolder - the destination folder. Null will use the default IU folder
  1266. // lMode - bitmask indicates throttled/foreground and notification options
  1267. // punkProgressListener - the callback function pointer for reporting download progress
  1268. // hWnd - the event msg window handler passed from the stub
  1269. // Output:
  1270. // pbstrXmlItems - the items with download status in xml format
  1271. // e.g.
  1272. // <id guid="2560AD4D-3ED3-49C6-A937-4368C0B0E06D" downloaded="1"/>
  1273. /////////////////////////////////////////////////////////////////////////////
  1274. HRESULT WINAPI CEngUpdate::Download(BSTR bstrXmlClientInfo, BSTR bstrXmlCatalog, BSTR bstrDestinationFolder, LONG lMode,
  1275. IUnknown *punkProgressListener, HWND hWnd, BSTR *pbstrXmlItems)
  1276. {
  1277. CXmlClientInfo clientInfo;
  1278. BSTR bstrClientName = NULL;
  1279. HRESULT hr;
  1280. LOG_Block("Download()");
  1281. LogMessage("Download started");
  1282. hr = clientInfo.LoadXMLDocument(bstrXmlClientInfo, m_fOfflineMode);
  1283. CleanUpIfFailedAndMsg(hr);
  1284. hr = clientInfo.GetClientName(&bstrClientName);
  1285. CleanUpIfFailedAndMsg(hr);
  1286. hr = _Download(
  1287. bstrClientName,
  1288. bstrXmlCatalog,
  1289. bstrDestinationFolder,
  1290. lMode,
  1291. punkProgressListener,
  1292. hWnd,
  1293. NULL, // no op id needed for sync download
  1294. pbstrXmlItems,
  1295. this);
  1296. CleanUp:
  1297. SysFreeString(bstrClientName);
  1298. return hr;
  1299. }
  1300. /////////////////////////////////////////////////////////////////////////////
  1301. // DownloadAsync()
  1302. //
  1303. // Download asynchronously - the method will return before completion.
  1304. // Input:
  1305. // bstrXmlClientInfo - the credentials of the client in xml format
  1306. // bstrXmlCatalog - the xml catalog portion containing items to be downloaded
  1307. // bstrDestinationFolder - the destination folder. Null will use the default IU folder
  1308. // lMode - indicates throttled or fore-ground downloading mode
  1309. // punkProgressListener - the callback function pointer for reporting download progress
  1310. // hWnd - the event msg window handler passed from the stub
  1311. // bstrUuidOperation - an id provided by the client to provide further
  1312. // identification to the operation as indexes may be reused.
  1313. // Output:
  1314. // pbstrUuidOperation - the operation ID. If it is not provided by the in bstrUuidOperation
  1315. // parameter (an empty string is passed), it will generate a new UUID,
  1316. // in which case, the caller will be responsible to free the memory of
  1317. // the string buffer that holds the generated UUID using SysFreeString().
  1318. // Otherwise, it returns the value passed by bstrUuidOperation.
  1319. /////////////////////////////////////////////////////////////////////////////
  1320. HRESULT WINAPI CEngUpdate::DownloadAsync(BSTR bstrXmlClientInfo, BSTR bstrXmlCatalog, BSTR bstrDestinationFolder, LONG lMode,
  1321. IUnknown *punkProgressListener, HWND hWnd, BSTR bstrUuidOperation, BSTR *pbstrUuidOperation)
  1322. {
  1323. HRESULT hr = S_OK;
  1324. BSTR bstrClientName = NULL;
  1325. DWORD dwThreadId = 0x0;
  1326. DWORD dwErr = 0x0;
  1327. HANDLE hThread = NULL;
  1328. GUID guid;
  1329. LPWSTR lpswClientInfo = NULL;
  1330. LPOLESTR pwszUuidOperation = NULL;
  1331. PIUDOWNLOADSTARTUPINFO pStartupInfo = NULL;
  1332. HANDLE hHeap = GetProcessHeap();
  1333. CXmlClientInfo clientInfo;
  1334. LOG_Block("DownloadAsync()");
  1335. LogMessage("Asynchronous Download started");
  1336. USES_IU_CONVERSION;
  1337. //
  1338. // validate parameters:
  1339. // if no catalog, or no return var, or no client info, this function can do nothing.
  1340. //
  1341. if ((NULL == bstrXmlCatalog) ||
  1342. (NULL == bstrXmlClientInfo) ||
  1343. (SysStringLen(bstrXmlCatalog) == 0) ||
  1344. (SysStringLen(bstrXmlClientInfo) == 0))
  1345. {
  1346. hr = E_INVALIDARG;
  1347. CleanUpIfFailedAndMsg(hr);
  1348. }
  1349. //
  1350. // validate the client info
  1351. //
  1352. hr = clientInfo.LoadXMLDocument(bstrXmlClientInfo, m_fOfflineMode);
  1353. CleanUpIfFailedAndMsg(hr);
  1354. hr = clientInfo.GetClientName(&bstrClientName);
  1355. CleanUpIfFailedAndMsg(hr);
  1356. if (NULL == (pStartupInfo = (PIUDOWNLOADSTARTUPINFO) HeapAlloc(hHeap, HEAP_ZERO_MEMORY, sizeof(IUDOWNLOADSTARTUPINFO))))
  1357. {
  1358. hr = E_OUTOFMEMORY;
  1359. LOG_ErrorMsg(hr);
  1360. goto CleanUp;
  1361. }
  1362. pStartupInfo->bstrClientName = SysAllocString(bstrClientName);
  1363. pStartupInfo->bstrXmlCatalog = SysAllocString(bstrXmlCatalog);
  1364. pStartupInfo->hwnd = hWnd;
  1365. pStartupInfo->lMode = lMode;
  1366. pStartupInfo->punkProgressListener = punkProgressListener;
  1367. pStartupInfo->pEngUpdate = this;
  1368. if (NULL != bstrDestinationFolder && SysStringLen(bstrDestinationFolder) > 0)
  1369. {
  1370. LOG_Download(_T("Caller specified destination folder=%s"), OLE2T(bstrDestinationFolder));
  1371. pStartupInfo->bstrDestinationFolder = SysAllocString(bstrDestinationFolder);
  1372. }
  1373. //
  1374. // session id is required for download operation
  1375. //
  1376. if (NULL != bstrUuidOperation && SysStringLen(bstrUuidOperation) > 0)
  1377. {
  1378. LOG_Download(_T("User passed in UUID %s"), OLE2T(bstrUuidOperation));
  1379. pStartupInfo->bstrUuidOperation = SysAllocString(bstrUuidOperation);
  1380. if (NULL != pbstrUuidOperation)
  1381. {
  1382. *pbstrUuidOperation = SysAllocString(bstrUuidOperation);
  1383. }
  1384. }
  1385. else
  1386. {
  1387. //
  1388. // if user doesn't have an operation id, we generate one
  1389. //
  1390. hr = CoCreateGuid(&guid);
  1391. if (FAILED(hr))
  1392. {
  1393. LOG_ErrorMsg(hr);
  1394. goto CleanUp;
  1395. }
  1396. hr = StringFromCLSID(guid, &pwszUuidOperation);
  1397. if (FAILED(hr))
  1398. {
  1399. LOG_ErrorMsg(hr);
  1400. goto CleanUp;
  1401. }
  1402. pStartupInfo->bstrUuidOperation = SysAllocString(pwszUuidOperation);
  1403. if (NULL != pbstrUuidOperation)
  1404. {
  1405. *pbstrUuidOperation = SysAllocString(pwszUuidOperation);
  1406. }
  1407. LOG_Download(_T("UUID generated %s"), OLE2T(pwszUuidOperation));
  1408. CoTaskMemFree(pwszUuidOperation);
  1409. }
  1410. InterlockedIncrement(&m_lThreadCounter);
  1411. if (NULL != pStartupInfo->punkProgressListener)
  1412. {
  1413. //
  1414. // since this is an async operation, to prevent caller free this object after
  1415. // this call returns, we pump up ref count here. The thread proc will
  1416. // release refcount after it finishes the work
  1417. //
  1418. pStartupInfo->punkProgressListener->AddRef();
  1419. }
  1420. hThread = CreateThread(NULL, 0, DownloadThreadProc, (LPVOID)pStartupInfo, 0, &dwThreadId);
  1421. if (NULL == hThread)
  1422. {
  1423. //
  1424. // clean up allocated strings in pStartupInfo.
  1425. //
  1426. dwErr = GetLastError();
  1427. hr = HRESULT_FROM_WIN32(dwErr);
  1428. LOG_ErrorMsg(hr);
  1429. SafeRelease(pStartupInfo->punkProgressListener);
  1430. InterlockedDecrement(&m_lThreadCounter);
  1431. }
  1432. else
  1433. {
  1434. LOG_Download(_T("Download thread generated successfully"));
  1435. }
  1436. CleanUp:
  1437. if (FAILED(hr))
  1438. {
  1439. LogError(hr, "Asynchronous Download failed during startup");
  1440. if (NULL != pStartupInfo)
  1441. {
  1442. SysFreeString(pStartupInfo->bstrDestinationFolder);
  1443. SysFreeString(pStartupInfo->bstrXmlCatalog);
  1444. SysFreeString(pStartupInfo->bstrClientName);
  1445. SysFreeString(pStartupInfo->bstrUuidOperation);
  1446. HeapFree(hHeap, 0, pStartupInfo);
  1447. }
  1448. }
  1449. SysFreeString(bstrClientName);
  1450. return hr;
  1451. }
  1452. /////////////////////////////////////////////////////////////////////////////
  1453. // DownloadCallback()
  1454. //
  1455. // Callback Function to recieve progress from IU Downloader.
  1456. //
  1457. // Input:
  1458. // pCallbackData - void pointer to DCB_DATA structure
  1459. // dwStatus - Current Download Status
  1460. // dwBytesTotal - Total Bytes of File being Downloaded
  1461. // dwBytesComplete - Bytes Downloaded so far
  1462. // bstrCompleteResult - Contains Item Result XML
  1463. //
  1464. // Output:
  1465. // plCommandRequest - Used to Instruct the Downloader to continue, abort, suspend...
  1466. //
  1467. // Return:
  1468. // 0 - always, exit code is irrelevant since calling thread doesn't check the
  1469. // status of this thread after creation.
  1470. /////////////////////////////////////////////////////////////////////////////
  1471. BOOL WINAPI DownloadCallback(VOID* pCallbackData, DWORD dwStatus, DWORD dwBytesTotal, DWORD dwBlockSizeDownloaded, BSTR bstrXmlData, LONG* plCommandRequest)
  1472. {
  1473. LOG_Block("DownloadCallback()");
  1474. HRESULT hr;
  1475. LONG lUpdateMask = 0;
  1476. float flNewPercentage;
  1477. EventData evtData;
  1478. char szProgressSize[64] = {'\0'};
  1479. BOOL fPostWaitSuccess = TRUE;
  1480. ZeroMemory((LPVOID) &evtData, sizeof(evtData));
  1481. USES_IU_CONVERSION;
  1482. P_DCB_DATA pCallbackParam = (P_DCB_DATA) pCallbackData;
  1483. if (NULL != pCallbackParam->bstrOperationUuid)
  1484. {
  1485. evtData.bstrUuidOperation = SysAllocString(pCallbackParam->bstrOperationUuid);
  1486. LOG_Download(_T("Found UUID=%s"), OLE2T(evtData.bstrUuidOperation));
  1487. }
  1488. if (dwBytesTotal != pCallbackParam->lCurrentItemSize && DOWNLOAD_STATUS_ITEMCOMPLETE != dwStatus)
  1489. {
  1490. pCallbackParam->lTotalDownloadSize = (pCallbackParam->lTotalDownloadSize - pCallbackParam->lCurrentItemSize) + dwBytesTotal;
  1491. pCallbackParam->lCurrentItemSize = dwBytesTotal;
  1492. }
  1493. // Keep the Total Downloaded Bytes Counter
  1494. if (0 != dwBlockSizeDownloaded && DOWNLOAD_STATUS_ITEMCOMPLETE != dwStatus)
  1495. pCallbackParam->lTotalDownloaded += dwBlockSizeDownloaded;
  1496. LOG_Download(_T("dwStatus=0x%08x"), dwStatus);
  1497. //
  1498. // if the Status is DOWNLOAD_STATUS_FILECOMPLETE we are done with this File
  1499. //
  1500. evtData.fItemCompleted = (dwStatus == DOWNLOAD_STATUS_ITEMCOMPLETE);
  1501. switch (dwStatus)
  1502. {
  1503. case DOWNLOAD_STATUS_ITEMSTART:
  1504. if (NULL != pCallbackParam->pProgressListener)
  1505. {
  1506. pCallbackParam->pProgressListener->OnItemStart(pCallbackParam->bstrOperationUuid,
  1507. bstrXmlData, &evtData.lCommandRequest);
  1508. }
  1509. else
  1510. {
  1511. // only use event window if no progresslistener interface was given
  1512. if (NULL != pCallbackParam->hEventFiringWnd)
  1513. {
  1514. evtData.bstrXmlData = bstrXmlData;
  1515. SendMessage(pCallbackParam->hEventFiringWnd, UM_EVENT_ITEMSTART, 0, LPARAM(&evtData));
  1516. evtData.bstrXmlData = NULL;
  1517. }
  1518. }
  1519. break;
  1520. case DOWNLOAD_STATUS_OK: // simple progress update
  1521. case DOWNLOAD_STATUS_ITEMCOMPLETE:
  1522. if (0 != pCallbackParam->flProgressPercentage)
  1523. {
  1524. // we need to give progress callbacks on given percentage increments
  1525. flNewPercentage = ((float)pCallbackParam->lTotalDownloaded / pCallbackParam->lTotalDownloadSize);
  1526. if (((flNewPercentage - pCallbackParam->flLastPercentage) >= pCallbackParam->flProgressPercentage) ||
  1527. ((1.0 - flNewPercentage) < 0.0001 && 1 != pCallbackParam->flProgressPercentage))
  1528. {
  1529. // The Difference between LastPercentComplete and CurrentPercentComplete complies with the
  1530. // Progress Percentage granuluarity OR the percentage is 100 (complete)
  1531. if (evtData.fItemCompleted)
  1532. {
  1533. //wsprintfA(szProgressSize, "%d", (int)flNewPercentage); // float should be 1.0, cast as int will be 1
  1534. //
  1535. // when we notify the user this "item", not file, completed, we don't need
  1536. // to pass out any percentage info.
  1537. //
  1538. szProgressSize[0] = _T('\0');
  1539. }
  1540. else
  1541. {
  1542. if ((1.0 - flNewPercentage) < 0.0001)
  1543. {
  1544. if (ARRAYSIZE(szProgressSize) >= 2)
  1545. {
  1546. szProgressSize[0] = _T('1');
  1547. szProgressSize[1] = _T('\0');
  1548. }
  1549. else
  1550. {
  1551. break;
  1552. }
  1553. }
  1554. else
  1555. {
  1556. hr = StringCchPrintfExA(szProgressSize, ARRAYSIZE(szProgressSize),
  1557. NULL, NULL, MISTSAFE_STRING_FLAGS,
  1558. ".%02d", (int)(flNewPercentage*100)); // string equivilant of a float
  1559. if (FAILED(hr))
  1560. {
  1561. LOG_ErrorMsg(hr);
  1562. break;
  1563. }
  1564. }
  1565. }
  1566. pCallbackParam->flLastPercentage = flNewPercentage;
  1567. }
  1568. else
  1569. {
  1570. // don't make a callback
  1571. break;
  1572. }
  1573. }
  1574. else
  1575. {
  1576. // No percentage callback was requested.. just give the byte values.
  1577. if (dwStatus == DOWNLOAD_STATUS_ITEMCOMPLETE)
  1578. {
  1579. szProgressSize[0] = _T('\0');
  1580. }
  1581. else
  1582. {
  1583. hr = StringCchPrintfExA(szProgressSize, ARRAYSIZE(szProgressSize),
  1584. NULL, NULL, MISTSAFE_STRING_FLAGS,
  1585. "%lu:%lu", (ULONG)pCallbackParam->lTotalDownloadSize, (ULONG)pCallbackParam->lTotalDownloaded);
  1586. if (FAILED(hr))
  1587. {
  1588. LOG_ErrorMsg(hr);
  1589. break;
  1590. }
  1591. }
  1592. }
  1593. evtData.bstrProgress = SysAllocString(A2OLE(szProgressSize));
  1594. if (NULL != pCallbackParam->pProgressListener)
  1595. {
  1596. pCallbackParam->pProgressListener->OnProgress(pCallbackParam->bstrOperationUuid,
  1597. evtData.fItemCompleted, evtData.bstrProgress, &evtData.lCommandRequest);
  1598. }
  1599. else
  1600. {
  1601. // only use event window if no progresslistener interface was given
  1602. if (NULL != pCallbackParam->hEventFiringWnd)
  1603. {
  1604. SendMessage(pCallbackParam->hEventFiringWnd, UM_EVENT_PROGRESS, 0, LPARAM(&evtData));
  1605. }
  1606. }
  1607. break;
  1608. case DOWNLOAD_STATUS_OPERATIONCOMPLETE:
  1609. if (NULL != pCallbackParam->pProgressListener)
  1610. {
  1611. pCallbackParam->pProgressListener->OnOperationComplete(pCallbackParam->bstrOperationUuid,
  1612. bstrXmlData);
  1613. }
  1614. else
  1615. {
  1616. // only use event window if no progresslistener interface was given
  1617. if (NULL != pCallbackParam->hEventFiringWnd)
  1618. {
  1619. evtData.bstrXmlData = bstrXmlData;
  1620. fPostWaitSuccess = WUPostEventAndBlock(pCallbackParam->hEventFiringWnd,
  1621. UM_EVENT_COMPLETE,
  1622. &evtData);
  1623. }
  1624. }
  1625. // Look for an existing Operation in the Mgr, and update the Complete Result if available.
  1626. if (pCallbackParam->pOperationMgr->FindOperation(OLE2T(pCallbackParam->bstrOperationUuid), &lUpdateMask, NULL))
  1627. {
  1628. pCallbackParam->pOperationMgr->UpdateOperation(OLE2T(pCallbackParam->bstrOperationUuid), lUpdateMask, bstrXmlData);
  1629. }
  1630. break;
  1631. case DOWNLOAD_STATUS_ABORTED:
  1632. case DOWNLOAD_STATUS_ERROR:
  1633. //
  1634. // abort case: user should know. nothing to report
  1635. // error case: progress callback doesn't give us any way of telling the caller that its an error, no reason to send the callback
  1636. // the itemcomplete callback will have the error status for the item.
  1637. //
  1638. break;
  1639. }
  1640. if (NULL != plCommandRequest) // we made a callback and the command request value was retrieved
  1641. {
  1642. *plCommandRequest = (LONG)((DWORD) evtData.lCommandRequest & (DWORD) UPDATE_COMMAND);
  1643. LOG_Download(_T("Command returned: 0x%08x"), *plCommandRequest);
  1644. }
  1645. // don't free up the strings below unless the wait succeeded in
  1646. // WUPostEventAndBlock. If we do free the strings up and the wait didn't
  1647. // succeed, then we run the risk of AVing ourselves. Note that fPostWaitSuccess
  1648. // is initialized to TRUE so if we will free these BSTRs if WUPostEventAndBlock
  1649. // is not called.
  1650. if (fPostWaitSuccess)
  1651. {
  1652. SysFreeString(evtData.bstrProgress);
  1653. SysFreeString(evtData.bstrUuidOperation);
  1654. }
  1655. return TRUE;
  1656. }
  1657. /////////////////////////////////////////////////////////////////////////////
  1658. // DownloadThreadProc()
  1659. //
  1660. // Thread Proc for Async Download. Retrieves the startup information from
  1661. // the input param and calls Download() from this seperate thread. The calling
  1662. // thread returns immediately.
  1663. //
  1664. // Input:
  1665. // lpv - void pointer to IUDOWNLOADSTARTINFO struct containing all information
  1666. // needed to call Download()
  1667. //
  1668. // Return:
  1669. // 0 - always, exit code is irrelevant since calling thread doesn't check the
  1670. // status of this thread after creation.
  1671. /////////////////////////////////////////////////////////////////////////////
  1672. DWORD WINAPI DownloadThreadProc(LPVOID lpv)
  1673. {
  1674. LOG_Block("DownloadThreadProc()");
  1675. //
  1676. // in this new thread need to call CoInitialize again
  1677. // but since we don't know who the caller is, what threading they
  1678. // are using, so we just use single apartment
  1679. //
  1680. HRESULT hr = CoInitialize(NULL);
  1681. if (FAILED(hr))
  1682. {
  1683. LogError(hr, "Asynchronous Download thread exiting");
  1684. LOG_ErrorMsg(hr);
  1685. return 0;
  1686. }
  1687. LOG_Download(_T("CoInitialize called successfully"));
  1688. PIUDOWNLOADSTARTUPINFO pStartupInfo = (PIUDOWNLOADSTARTUPINFO)lpv;
  1689. BSTR bstrXmlItems = NULL;
  1690. LOG_Download(_T("Download thread started, now the thread count=%d"), pStartupInfo->pEngUpdate->m_lThreadCounter);
  1691. //
  1692. // call synchronized download function in this thread
  1693. //
  1694. _Download(
  1695. pStartupInfo->bstrClientName,
  1696. pStartupInfo->bstrXmlCatalog,
  1697. pStartupInfo->bstrDestinationFolder,
  1698. pStartupInfo->lMode,
  1699. pStartupInfo->punkProgressListener,
  1700. pStartupInfo->hwnd,
  1701. pStartupInfo->bstrUuidOperation,
  1702. &bstrXmlItems,
  1703. pStartupInfo->pEngUpdate);
  1704. //
  1705. // pStartupInfo is a buffer allocated by calling thread, when we are done, we need to
  1706. // free it here
  1707. //
  1708. SysFreeString(pStartupInfo->bstrDestinationFolder);
  1709. SysFreeString(pStartupInfo->bstrXmlCatalog);
  1710. SysFreeString(pStartupInfo->bstrClientName);
  1711. SysFreeString(pStartupInfo->bstrUuidOperation);
  1712. SysFreeString(bstrXmlItems);
  1713. SafeRelease(pStartupInfo->punkProgressListener); // so the caller can free this object
  1714. CoUninitialize();
  1715. LOG_Download(_T("CoUninitialize called"));
  1716. InterlockedDecrement(&(pStartupInfo->pEngUpdate->m_lThreadCounter));
  1717. HeapFree(GetProcessHeap(), 0, pStartupInfo);
  1718. return 0;
  1719. }