Source code of Windows XP (NT5)
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.

1138 lines
36 KiB

  1. #include <windows.h>
  2. #include <urlmon.h>
  3. #include <wininet.h>
  4. #include "resource.h"
  5. #include "advpext.h"
  6. #include "download.h"
  7. #include "patchdownload.h"
  8. #include "util.h"
  9. extern "C"
  10. {
  11. #include "patchapi.h"
  12. #include "redblack.h"
  13. #include "crc32.h"
  14. }
  15. extern HINF g_hInf;
  16. extern HINSTANCE g_hInstance;
  17. extern HWND g_hProgressDlg;
  18. extern BOOL g_fAbort;
  19. HANDLE g_hDownloadProcess = NULL;
  20. HRESULT WINAPI DownloadAndPatchFiles(DWORD dwFileCount, DOWNLOAD_FILEINFO* pFileInfo, LPCSTR lpszUrl,
  21. LPCSTR lpszPath, PATCH_DOWNLOAD_CALLBACK pfnCallback, LPVOID lpvContext)
  22. {
  23. HRESULT hr = S_OK;
  24. CSiteMgr csite;
  25. hr = LoadSetupAPIFuncs();
  26. if(FAILED(hr))
  27. {
  28. return hr;
  29. }
  30. SetProgressText(IDS_INIT);
  31. //Download the sites.dat file
  32. hr = csite.Initialize(lpszUrl);
  33. if(FAILED(hr))
  34. {
  35. return hr;
  36. }
  37. CPatchDownloader cpdwn(pFileInfo, dwFileCount, pfnCallback);
  38. return cpdwn.InternalDownloadAndPatchFiles(lpszPath, &csite, lpvContext);
  39. }
  40. HRESULT CPatchDownloader::InternalDownloadAndPatchFiles(LPCTSTR lpszPath, CSiteMgr* pSite, LPVOID lpvContext)
  41. {
  42. HRESULT hr = S_OK;
  43. PATCH_THREAD_INFO PatchThreadInfo;
  44. HANDLE hThreadPatcher = NULL;
  45. ULONG DownloadClientId = 0;
  46. int nCount = 0;
  47. LPTSTR lpszUrl;
  48. BOOL fUseWin9xDirectory = FALSE;
  49. if(!GetTempPath(sizeof(m_lpszDownLoadDir), m_lpszDownLoadDir))
  50. {
  51. //Unable to get temp folder, Create a folder in the path sent to us
  52. wsprintf(m_lpszDownLoadDir, "%s\\%s", lpszPath, "AdvExt");
  53. }
  54. //Since the binary is different for NT and 9x, because of binding, we cannot put the files
  55. //under same directory on server. So for 9x put it under a subdirectory and modify the
  56. //url accordingly.
  57. HKEY hKey;
  58. OSVERSIONINFO osvi;
  59. osvi.dwOSVersionInfoSize = sizeof(osvi);
  60. GetVersionEx(&osvi);
  61. if(VER_PLATFORM_WIN32_NT != osvi.dwPlatformId &&
  62. ERROR_SUCCESS == RegOpenKeyEx(HKEY_LOCAL_MACHINE, "SOFTWARE\\Microsoft\\Advanced INF Setup", 0,
  63. KEY_ALL_ACCESS, &hKey))
  64. {
  65. DWORD dwSize = sizeof(DWORD);
  66. if(ERROR_SUCCESS == RegQueryValueEx(hKey, "Usewin9xDirectory", 0, 0,
  67. (LPBYTE)&fUseWin9xDirectory, &dwSize))
  68. {
  69. RegDeleteValue(hKey, "Usewin9xDirectory");
  70. }
  71. RegCloseKey(hKey);
  72. }
  73. if (DownloadClientId == 0)
  74. {
  75. // Need to generate a unique DownloadClientId for this machine, but
  76. // it needs to be consistent (persistent) if same machine downloads
  77. // twice, even across process destroy/restart. First we check the
  78. // registry to see if we have previously generated a unique Id for
  79. // this machine, and use that if we find it. Otherwise, we generate
  80. // a unique DownloadClientId and store it in the registry so future
  81. // instances will use the same value.
  82. //
  83. LONG RegStatus;
  84. HKEY hKey;
  85. DWORD dwHow;
  86. DWORD dwValueSize;
  87. DWORD dwValueType;
  88. DWORD dwValue;
  89. RegStatus = RegCreateKeyEx(HKEY_LOCAL_MACHINE,
  90. "SOFTWARE\\Microsoft\\Advanced INF Setup\\AdvExt",
  91. 0,
  92. NULL,
  93. REG_OPTION_NON_VOLATILE,
  94. KEY_ALL_ACCESS,
  95. NULL,
  96. &hKey,
  97. &dwHow
  98. );
  99. if ( RegStatus == ERROR_SUCCESS )
  100. {
  101. dwValueSize = sizeof(dwValue);
  102. RegStatus = RegQueryValueEx(hKey, "DownloadClientId", NULL, &dwValueType, (LPBYTE)&dwValue, &dwValueSize);
  103. dwValue &= 0xFFFFFFF0;
  104. if ((RegStatus == ERROR_SUCCESS) && (dwValueType == REG_DWORD) && (dwValue != 0))
  105. {
  106. DownloadClientId = dwValue;
  107. }
  108. else
  109. {
  110. DownloadClientId = GenerateUniqueClientId();
  111. dwValue = DownloadClientId;
  112. RegSetValueEx(hKey, "DownloadClientId", 0, REG_DWORD, (LPBYTE)&dwValue, sizeof(dwValue));
  113. }
  114. RegCloseKey( hKey );
  115. }
  116. else
  117. {
  118. // Failed to open/create registry key, so fall back to just
  119. // creating a unique ID for this process instance. At least
  120. // it will show the same client id if the user hits "retry".
  121. //
  122. DownloadClientId = GenerateUniqueClientId();
  123. }
  124. }
  125. m_hSubAllocator = CreateSubAllocator(0x10000, 0x10000);
  126. if(!m_hSubAllocator)
  127. {
  128. hr = HRESULT_FROM_WIN32(GetLastError());
  129. WriteToLog("Memory allocation failed. Can't do much. Exiting with hr=%1!lx!\n", hr);
  130. goto done;
  131. }
  132. //Set the parameters that needs to be passed on to the thread.
  133. if(!m_lpfnCallback)
  134. {
  135. m_lpfnCallback = PatchCallback;
  136. m_lpvContext = (LPVOID)lpszPath;
  137. }
  138. else
  139. {
  140. m_lpvContext = lpvContext;
  141. }
  142. PatchThreadInfo.hFileDownloadEvent = _hDL;
  143. PatchThreadInfo.FileListInfo.FileList = m_lpFileInfo;
  144. PatchThreadInfo.FileListInfo.FileCount = m_dwFileCount;
  145. PatchThreadInfo.FileListInfo.Callback = m_lpfnCallback;
  146. PatchThreadInfo.FileListInfo.CallbackContext = m_lpvContext;
  147. PatchThreadInfo.lpdwnProgressInfo = &m_DownloadInfo;
  148. m_DownloadInfo.dwFilesRemaining = m_dwFileCount;
  149. m_DownloadInfo.dwFilesToDownload = m_dwFileCount;
  150. m_DownloadInfo.dwBytesToDownload = 0;
  151. m_DownloadInfo.dwBytesRemaining = 0;
  152. //Create an event to signal patchthread is ready to process download request
  153. g_hDownloadProcess = CreateEvent(NULL, TRUE, FALSE, NULL);
  154. if(!g_hDownloadProcess)
  155. {
  156. hr = HRESULT_FROM_WIN32(GetLastError());
  157. WriteToLog("Create event failed with error code:%1!lx!\n", hr);
  158. goto done;
  159. }
  160. //Do till we have download files or we retry 3 times
  161. while(nCount++ < 3 && m_DownloadInfo.dwFilesRemaining && !g_fAbort)
  162. {
  163. WriteToLog("\n%1!d! try: Number of Files:%2!d!\n", nCount, m_DownloadInfo.dwFilesRemaining);
  164. _hDLResult = 0;
  165. ResetEvent(g_hDownloadProcess);
  166. hThreadPatcher = CreateThread(NULL, 0, PatchThread, &PatchThreadInfo, 0, &m_dwPatchThreadId);
  167. if(!hThreadPatcher)
  168. {
  169. hr = HRESULT_FROM_WIN32(GetLastError());
  170. goto done;
  171. }
  172. //Generate the request buffer that would be sent on POST
  173. hr = CreateRequestBuffer(DownloadClientId);
  174. if(FAILED(hr))
  175. {
  176. WriteToLog("\nCreateRequestBuffer failed with error code:%1!lx!\n", hr);
  177. goto done;
  178. }
  179. //Get the url from where bytes needs to be downloaded.
  180. if(!pSite->GetNextSite(&lpszUrl, &m_lpszSiteName))
  181. {
  182. WriteToLog("GetNextSite returned false. No site info??");
  183. hr = E_UNEXPECTED;
  184. goto done;
  185. }
  186. TCHAR szURL[INTERNET_MAX_URL_LENGTH];
  187. if(fUseWin9xDirectory)
  188. {
  189. lstrcpy(szURL, lpszUrl);
  190. if(*(lpszUrl + lstrlen(lpszUrl) - 1) == '/')
  191. {
  192. lstrcat(szURL, "win9x");
  193. }
  194. else
  195. {
  196. lstrcat(szURL, "/win9x");
  197. }
  198. lpszUrl = szURL;
  199. }
  200. //Notify callback we are about to begin download
  201. ProtectedPatchDownloadCallback(m_lpfnCallback, PATCH_DOWNLOAD_BEGIN, (LPVOID)lpszUrl, m_lpvContext);
  202. hr = DoDownload(lpszUrl, NULL);
  203. WriteToLog("DownloadFile returned:%1!lx!\n\n", hr);
  204. SetProgressText(IDS_CLEANUP);
  205. //Ask the patch thread to quit once it finishes download.
  206. SetEvent(_hDL);
  207. //Wait till patch thread finishes its work
  208. while(1)
  209. {
  210. DWORD dw = MsgWaitForMultipleObjects(1, &hThreadPatcher, FALSE, 1000, QS_ALLINPUT);
  211. if(dw == WAIT_OBJECT_0)
  212. {
  213. break;
  214. }
  215. MSG msg;
  216. while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
  217. {
  218. DispatchMessage(&msg);
  219. }
  220. }
  221. CloseHandle(hThreadPatcher);
  222. hThreadPatcher = NULL;
  223. //Setup the downloadinfo structure, incase we need to re-download some files
  224. m_DownloadInfo.dwFilesToDownload = m_DownloadInfo.dwFilesRemaining;
  225. m_DownloadInfo.dwBytesToDownload = 0;
  226. m_DownloadInfo.dwBytesRemaining = 0;
  227. if(m_DownloadInfo.dwFilesToDownload)
  228. {
  229. SetProgressText(IDS_RETRY);
  230. }
  231. m_dwServerFileCount=0;
  232. ResetEvent(_hDL);
  233. }
  234. done:
  235. if(g_hDownloadProcess)
  236. {
  237. CloseHandle(g_hDownloadProcess);
  238. }
  239. if(!hr && m_DownloadInfo.dwFilesToDownload)
  240. {
  241. hr = E_FAIL;
  242. WriteToLog("\nSome files could not be downloaded\n");
  243. }
  244. WriteToLog("DownloadAndPatchFiles returning:%1!lx!\n", hr);
  245. return hr;
  246. }
  247. HRESULT CPatchDownloader :: CreateRequestBuffer(DWORD dwDownloadClientID)
  248. {
  249. LPTSTR lpRequestPointer, lpFileNamePortionOfRequest;
  250. HRESULT hr = S_OK;
  251. DWORD i;
  252. DWORD dwHeapSize = 64*1024;
  253. if(!m_lpFileInfo)
  254. {
  255. return E_INVALIDARG;
  256. }
  257. m_lpszRequestBuffer = (LPTSTR)ResizeBuffer(NULL, dwHeapSize, FALSE);
  258. if(!m_lpszRequestBuffer)
  259. {
  260. hr = HRESULT_FROM_WIN32(GetLastError());
  261. goto done;
  262. }
  263. lpRequestPointer = m_lpszRequestBuffer;
  264. lpRequestPointer += wsprintf(lpRequestPointer, "SessionId:%u\n", dwDownloadClientID);
  265. lpRequestPointer += wsprintf(lpRequestPointer, "FileList:%d\n", m_DownloadInfo.dwFilesToDownload);
  266. WriteToLog("Download ClientID:%1!lx! Number of Files:%2!d!\n", dwDownloadClientID, m_DownloadInfo.dwFilesToDownload);
  267. lpFileNamePortionOfRequest = lpRequestPointer;
  268. for(i=0; i < m_dwFileCount; i++)
  269. {
  270. if ((DWORD)( lpRequestPointer - m_lpszRequestBuffer ) > (DWORD)( dwHeapSize - (DWORD)MAX_PATH ))
  271. {
  272. dwHeapSize = dwHeapSize * 2;
  273. m_lpszRequestBuffer = (LPTSTR)ResizeBuffer(m_lpszRequestBuffer, dwHeapSize, FALSE);
  274. if(!m_lpszRequestBuffer)
  275. {
  276. hr = HRESULT_FROM_WIN32(GetLastError());
  277. goto done;
  278. }
  279. }
  280. if(m_lpFileInfo[i].dwFlags != PATCHFLAG_DOWNLOAD_NEEDED)
  281. {
  282. //Probably already downloaded
  283. continue;
  284. }
  285. if ((m_lpFileInfo[i].lpszExistingFilePatchSignature == NULL ) ||
  286. (*m_lpFileInfo[i].lpszExistingFilePatchSignature == 0 ))
  287. {
  288. // No file to patch from, request whole file.
  289. lpRequestPointer += wsprintf(lpRequestPointer, "%s\n", m_lpFileInfo[i].lpszFileNameToDownload);
  290. }
  291. else
  292. {
  293. lpRequestPointer += wsprintf(lpRequestPointer, "%s,%s\n", m_lpFileInfo[i].lpszFileNameToDownload,
  294. m_lpFileInfo[i].lpszExistingFilePatchSignature);
  295. }
  296. }
  297. // Now terminate list with "empty" entry.
  298. *lpRequestPointer++ = '\n';
  299. *lpRequestPointer++ = 0;
  300. m_dwRequestDataLength = lpRequestPointer - m_lpszRequestBuffer;
  301. // Now lowercase all the filenames in the request (this offloads the case consistency work from
  302. // the server -- the server expects the request to be all lowercase).
  303. MyLowercase(lpFileNamePortionOfRequest);
  304. WriteToLog("RequestBuffer: Size=%1!d!\n\n", m_dwRequestDataLength);
  305. WriteToLog("%1", m_lpszRequestBuffer);
  306. done:
  307. if(FAILED(hr))
  308. {
  309. ResizeBuffer(m_lpszRequestBuffer, 0, 0);
  310. }
  311. WriteToLog("\nCreateRequestBuffer returning %1!lx!\n", hr);
  312. return hr;
  313. }
  314. CPatchDownloader::CPatchDownloader(DOWNLOAD_FILEINFO* pdwn, DWORD dwFileCount, PATCH_DOWNLOAD_CALLBACK lpfn)
  315. {
  316. m_lpFileInfo = pdwn;
  317. m_dwFileCount = dwFileCount;
  318. m_lpfnCallback = lpfn;
  319. m_hCurrentFileHandle = NULL;
  320. m_dwCurrentFileIndex = 0;
  321. m_lpFileList = NULL;
  322. m_dwServerFileCount = 0;
  323. }
  324. CPatchDownloader::~CPatchDownloader()
  325. {
  326. DestroySubAllocator(m_hSubAllocator);
  327. }
  328. STDMETHODIMP CPatchDownloader::GetBindInfo( DWORD *grfBINDF, BINDINFO *pbindInfo)
  329. {
  330. *grfBINDF = BINDF_ASYNCHRONOUS | BINDF_ASYNCSTORAGE | BINDF_PULLDATA | BINDF_RESYNCHRONIZE | BINDF_NOWRITECACHE;
  331. pbindInfo->cbSize = sizeof(BINDINFO);
  332. memset(&pbindInfo->stgmedData, 0, sizeof(STGMEDIUM));
  333. pbindInfo->stgmedData.tymed = TYMED_HGLOBAL;
  334. pbindInfo->stgmedData.hGlobal = m_lpszRequestBuffer;
  335. pbindInfo->grfBindInfoF = BINDINFOF_URLENCODESTGMEDDATA;
  336. pbindInfo->dwBindVerb = BINDVERB_POST;
  337. pbindInfo->szCustomVerb = NULL;
  338. pbindInfo->cbstgmedData = m_dwRequestDataLength;
  339. pbindInfo->szExtraInfo = NULL;
  340. return(NOERROR);
  341. }
  342. STDMETHODIMP CPatchDownloader::OnProgress(ULONG ulProgress, ULONG ulProgressMax, ULONG ulStatusCode, LPCWSTR pwzStatusText)
  343. {
  344. int PatchStatusCode = -1;
  345. UINT uID;
  346. switch(ulStatusCode)
  347. {
  348. case BINDSTATUS_FINDINGRESOURCE:
  349. PatchStatusCode = PATCH_DOWNLOAD_FINDINGSITE;
  350. uID = IDS_BINDS_FINDING;
  351. break;
  352. case BINDSTATUS_CONNECTING:
  353. PatchStatusCode = PATCH_DOWNLOAD_CONNECTING;
  354. uID = IDS_BINDS_CONN;
  355. break;
  356. case BINDSTATUS_BEGINDOWNLOADDATA:
  357. PatchStatusCode = PATCH_DOWNLOAD_DOWNLOADINGDATA;
  358. uID = IDS_BINDS_DOWNLOADING;
  359. break;
  360. case BINDSTATUS_ENDDOWNLOADDATA:
  361. PatchStatusCode = PATCH_DOWNLOAD_ENDDOWNLOADINGDATA;
  362. uID = IDS_BINDS_ENDDOWNLOAD;
  363. break;
  364. }
  365. if(PatchStatusCode != -1 && pwzStatusText)
  366. {
  367. TCHAR szBuffer[MAX_PATH], szTemplate[MAX_PATH];
  368. LoadString(g_hInstance, uID, szTemplate, sizeof(szTemplate));
  369. wsprintf(szBuffer, szTemplate, m_lpszSiteName);
  370. ProtectedPatchDownloadCallback(m_lpfnCallback, (PATCH_DOWNLOAD_REASON)PatchStatusCode, szBuffer, m_lpvContext);
  371. }
  372. return NOERROR;
  373. }
  374. STDMETHODIMP CPatchDownloader::OnDataAvailable(DWORD grfBSCF, DWORD dwSize, FORMATETC *pFmtetc, STGMEDIUM *pstgmed)
  375. {
  376. HRESULT hr = NOERROR;
  377. TCHAR szBuffer[4096];
  378. DWORD dwRead = 0, dwWritten=0;
  379. do
  380. {
  381. hr = pstgmed->pstm->Read(szBuffer, 4096, &dwRead);
  382. if((SUCCEEDED(hr) || (hr == E_PENDING)) && dwRead > 0)
  383. {
  384. if(!ProcessDownloadChunk(szBuffer, dwRead))
  385. {
  386. WriteToLog("ProcessDownloadChunk returning FALSE. Aborting downloading\n");
  387. hr = E_ABORT;
  388. }
  389. }
  390. } while (hr == NOERROR && !g_fAbort);
  391. if(g_fAbort)
  392. Abort();
  393. return hr;
  394. }
  395. BOOL CPatchDownloader :: ProcessDownloadChunk(LPTSTR lpBuffer, DWORD dwLength)
  396. {
  397. CHAR TargetFile[ MAX_PATH ];
  398. ULONG Actual;
  399. ULONG WriteSize;
  400. BOOL Success;
  401. if ( m_dwServerFileCount == 0 )
  402. {
  403. //
  404. // Haven't processed headers yet.
  405. //
  406. // We expect header to look like this:
  407. //
  408. // "<head><title>"
  409. // "Download Stream of Files"
  410. // "</title></head>\n"
  411. // "<body>\n"
  412. // "FileList:%d\n"
  413. // "filename,%d\n"
  414. // "filename,%d\n"
  415. // ...etc...
  416. // "filename,%d\n"
  417. // "</body>\n"
  418. //
  419. // BUGBUG: if headers don't all fit in first chunk, we're screwed.
  420. //
  421. PCHAR EndOfHeader;
  422. PCHAR FileCountText;
  423. PCHAR FileNameText;
  424. PCHAR FileSizeText;
  425. ULONG FileSize;
  426. ULONG FileBytes;
  427. ULONG i;
  428. PCHAR p;
  429. EndOfHeader = ScanForSequence(lpBuffer, dwLength,
  430. "</body>\n",
  431. sizeof( "</body>\n" ) - 1 // not including terminator
  432. );
  433. if( EndOfHeader == NULL ) {
  434. SetLastError( ERROR_INVALID_DATA );
  435. return FALSE;
  436. }
  437. EndOfHeader += sizeof( "</body>\n" ) - 1 ;
  438. p = ScanForSequence(lpBuffer, EndOfHeader - lpBuffer, "FileList:", sizeof( "FileList:" ) - 1);
  439. if ( p == NULL )
  440. {
  441. SetLastError( ERROR_INVALID_DATA );
  442. return FALSE;
  443. }
  444. p += sizeof( "FileList:" ) - 1;
  445. FileCountText = p;
  446. p = ScanForChar( p, '\n', EndOfHeader - p );
  447. *p++ = 0;
  448. m_dwServerFileCount = StrToInt( FileCountText );
  449. WriteToLog("Total Files to be downloaded:%1!d!\n", m_dwServerFileCount);
  450. if(m_dwServerFileCount == 0 )
  451. {
  452. SetLastError( ERROR_INVALID_DATA );
  453. return FALSE;
  454. }
  455. m_lpFileList = (LPFILE) SubAllocate(m_hSubAllocator, m_dwServerFileCount * sizeof(FILE));
  456. if(!m_lpFileList)
  457. {
  458. return FALSE;
  459. }
  460. m_dwCurrentFileIndex = 0;
  461. FileBytes = 0;
  462. for ( i = 0; i < m_dwServerFileCount; i++ )
  463. {
  464. FileNameText = p;
  465. p = ScanForChar( p, ',', EndOfHeader - p );
  466. if (( p == NULL ) || ( p == FileNameText ))
  467. {
  468. SetLastError( ERROR_INVALID_DATA );
  469. return FALSE;
  470. }
  471. *p++ = 0;
  472. FileSizeText = p;
  473. p = ScanForChar( p, '\n', EndOfHeader - p );
  474. if ( p == NULL )
  475. {
  476. SetLastError( ERROR_INVALID_DATA );
  477. return FALSE;
  478. }
  479. *p++ = 0;
  480. FileSize = TextToUnsignedNum(FileSizeText);
  481. if ( FileSize == 0 )
  482. {
  483. SetLastError( ERROR_INVALID_DATA );
  484. return FALSE;
  485. }
  486. FileBytes += FileSize;
  487. m_lpFileList[i].dwFileSize = FileSize;
  488. m_lpFileList[i].lpszFileName = MySubAllocStrDup(m_hSubAllocator, FileNameText);
  489. if (m_lpFileList[i].lpszFileName == NULL)
  490. {
  491. return FALSE;
  492. }
  493. WriteToLog("File Name:%1 \t File Size:%2!d!\n", m_lpFileList[i].lpszFileName, m_lpFileList[i].dwFileSize);
  494. }
  495. // If we get to here, all the files in the header have been processed,
  496. // so we can set the state variables and continue with parsing raw
  497. // file data.
  498. m_DownloadInfo.dwBytesToDownload = FileBytes;
  499. m_DownloadInfo.dwBytesRemaining = FileBytes;
  500. dwLength -= ( EndOfHeader - lpBuffer );
  501. lpBuffer = EndOfHeader;
  502. WriteToLog("\nTotal %1!d! bytes(%2!d! Files) to be downloaded\n", FileBytes, m_dwServerFileCount);
  503. }
  504. // Process raw file info.
  505. m_DownloadInfo.dwBytesRemaining -= dwLength;
  506. if(!ProtectedPatchDownloadCallback(m_lpfnCallback, PATCH_DOWNLOAD_PROGRESS, (LPVOID)&m_DownloadInfo, m_lpvContext))
  507. {
  508. g_fAbort = TRUE;
  509. return FALSE;
  510. }
  511. while(dwLength > 0)
  512. {
  513. if (m_hCurrentFileHandle == NULL || m_hCurrentFileHandle == INVALID_HANDLE_VALUE)
  514. {
  515. if (m_dwCurrentFileIndex >= m_dwServerFileCount)
  516. {
  517. SetLastError( ERROR_INVALID_DATA );
  518. return FALSE; // more data than we expected
  519. }
  520. // Now open this file.
  521. CombinePaths(m_lpszDownLoadDir, m_lpFileList[m_dwCurrentFileIndex].lpszFileName, TargetFile );
  522. m_dwCurrentFileSize = m_lpFileList[m_dwCurrentFileIndex].dwFileSize;
  523. m_dwCurrFileSizeRemaining = m_dwCurrentFileSize;
  524. m_hCurrentFileHandle = CreateFile(TargetFile, GENERIC_WRITE, FILE_SHARE_READ, NULL,
  525. CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL );
  526. if (m_hCurrentFileHandle == INVALID_HANDLE_VALUE )
  527. {
  528. return FALSE;
  529. }
  530. }
  531. WriteSize = min( dwLength, m_dwCurrFileSizeRemaining);
  532. Success = WriteFile(m_hCurrentFileHandle, lpBuffer, WriteSize, &Actual, NULL);
  533. if(!Success)
  534. {
  535. return FALSE;
  536. }
  537. if(Actual != WriteSize)
  538. {
  539. SetLastError( ERROR_INVALID_PARAMETER );
  540. WriteToLog("Error:Actual size not equal to write size for %1. Aborting\n",
  541. m_lpFileList[m_dwCurrentFileIndex].lpszFileName);
  542. return FALSE;
  543. }
  544. m_dwCurrFileSizeRemaining -= WriteSize;
  545. if(m_dwCurrFileSizeRemaining == 0 )
  546. {
  547. CloseHandle(m_hCurrentFileHandle);
  548. m_hCurrentFileHandle = NULL;
  549. // Pass this file off to the patch thread.
  550. LPTSTR lpszFileName = (LPTSTR)ResizeBuffer(NULL, MAX_PATH, FALSE);
  551. CombinePaths(m_lpszDownLoadDir, m_lpFileList[m_dwCurrentFileIndex].lpszFileName, TargetFile );
  552. lstrcpy(lpszFileName, TargetFile);
  553. WaitForSingleObject(g_hDownloadProcess, 10000);
  554. PostThreadMessage(m_dwPatchThreadId, WM_FILEAVAILABLE, 0, (LPARAM)lpszFileName);
  555. m_dwCurrentFileIndex += 1;
  556. }
  557. lpBuffer += WriteSize;
  558. dwLength -= WriteSize;
  559. }
  560. return TRUE;
  561. }
  562. DWORD WINAPI PatchThread(IN LPVOID ThreadParam)
  563. {
  564. PPATCH_THREAD_INFO PatchThreadInfo = (PPATCH_THREAD_INFO) ThreadParam;
  565. PFILE_LIST_INFO FileListInfo = &PatchThreadInfo->FileListInfo;
  566. PDOWNLOAD_INFO ProgressInfo = PatchThreadInfo->lpdwnProgressInfo;
  567. NAME_TREE FileNameTree;
  568. PNAME_NODE FileNameNode;
  569. PDOWNLOAD_FILEINFO FileInfo;
  570. DWORD Status;
  571. BOOL bIsPatch;
  572. HANDLE hSubAllocator;
  573. ULONG i;
  574. BOOL fSuccess, fQuit = FALSE;
  575. MSG msg;
  576. //
  577. // First thing we need to do is construct a btree of the filenames
  578. // we expect to get in the queue so we can quickly find the corresponding
  579. // FileList entry when given a filename by the downloader. It will take
  580. // the downloader a little while to get connected, so this CPU intensive
  581. // task shouldn't slow anything down.
  582. //
  583. SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_LOWEST);
  584. PeekMessage(&msg, NULL, WM_USER, WM_USER, PM_NOREMOVE);
  585. hSubAllocator = CreateSubAllocator( 0x10000, 0x10000 );
  586. if ( hSubAllocator == NULL )
  587. {
  588. return ERROR_NOT_ENOUGH_MEMORY;
  589. }
  590. NameRbInitTree( &FileNameTree, hSubAllocator );
  591. TCHAR SourceFileName[MAX_PATH];
  592. for ( i = 0; i < FileListInfo->FileCount; i++ )
  593. {
  594. if(FileListInfo->FileList[ i ].dwFlags != PATCHFLAG_DOWNLOAD_NEEDED)
  595. {
  596. //Probably already downloaded and this is the second attempt
  597. continue;
  598. }
  599. lstrcpy( SourceFileName, FileListInfo->FileList[ i ].lpszFileNameToDownload);
  600. MyLowercase( SourceFileName );
  601. FileNameNode = NameRbInsert(&FileNameTree, SourceFileName);
  602. if ( FileNameNode == NULL )
  603. {
  604. DestroySubAllocator( hSubAllocator );
  605. return ERROR_NOT_ENOUGH_MEMORY;
  606. }
  607. if (FileNameNode->Context != NULL )
  608. {
  609. //
  610. // BUGBUG: Same filename in list twice. Should never be the
  611. // case since we check for duplicates before putting
  612. // them in the queue.
  613. //
  614. }
  615. FileNameNode->Context = &FileListInfo->FileList[ i ];
  616. // Now add another node in the tree based on the compressed filename.
  617. ConvertToCompressedFileName( SourceFileName );
  618. FileNameNode = NameRbInsert(&FileNameTree, SourceFileName);
  619. if ( FileNameNode == NULL )
  620. {
  621. DestroySubAllocator( hSubAllocator );
  622. return ERROR_NOT_ENOUGH_MEMORY;
  623. }
  624. if ( FileNameNode->Context != NULL )
  625. {
  626. // BUGBUG: Same filename in list twice. This can happen if two
  627. // different files collide on a compressed name (like
  628. // foo.db1 and foo.db2 colliding on foo.db_).
  629. //
  630. // We don't have a good solution for this right now.
  631. //
  632. }
  633. //Set the contect to the file info. When we get back the file from server, we can get the full
  634. //info about this
  635. FileNameNode->Context = &FileListInfo->FileList[ i ];
  636. //Make sure we are not asked to quit
  637. if(PeekMessage(&msg, NULL, 0, 0, PM_REMOVE) && msg.message == WM_QUIT)
  638. {
  639. goto done;
  640. }
  641. }
  642. //
  643. // Now wait for file downloads to be delivered to us.
  644. //
  645. SetEvent(g_hDownloadProcess);
  646. while (!g_fAbort && !fQuit)
  647. {
  648. LPTSTR lpszDownloadFileName, lpszSourceFileName;
  649. TCHAR szRealFileName[MAX_PATH];
  650. //
  651. // We're going to wait here with a timeout so that if the download
  652. // is stuck in InternetReadFile waiting for data, we can keep a
  653. // heartbeat going to the progress dialog and also check for cancel.
  654. //
  655. Status = MsgWaitForMultipleObjects(1, &PatchThreadInfo->hFileDownloadEvent, FALSE, 1000, QS_ALLINPUT);
  656. if (Status == WAIT_TIMEOUT )
  657. {
  658. //Keep updating the callback
  659. fSuccess = ProtectedPatchDownloadCallback(FileListInfo->Callback, PATCH_DOWNLOAD_PROGRESS,
  660. ProgressInfo, FileListInfo->CallbackContext);
  661. if (!fSuccess)
  662. {
  663. g_fAbort = TRUE;
  664. break;
  665. }
  666. continue;
  667. }
  668. if(Status == WAIT_OBJECT_0)
  669. {
  670. fQuit = TRUE;
  671. }
  672. while(PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
  673. {
  674. if(msg.message == WM_FILEAVAILABLE)
  675. {
  676. lpszDownloadFileName = (LPTSTR)msg.lParam;
  677. }
  678. else
  679. {
  680. continue;
  681. }
  682. // Ok, now we have a filename lpszDownloadFileName that was just downloaded to the
  683. // temp directory.The filename may be in one of the following
  684. // forms:
  685. // foo.dll
  686. // foo.dl_
  687. // foo.dll._p
  688. //
  689. // We have both "foo.dll" and "foo.dl_" in our name tree, but we
  690. // don't have "foo.dll._p", so we look for that form of the name
  691. // first and convert it to "foo.dll" before looking in name tree.
  692. fSuccess = TRUE;
  693. lpszSourceFileName = PathFindFileName(lpszDownloadFileName);
  694. ASSERT(lpszSourceFileName);
  695. lstrcpyn(szRealFileName, lpszDownloadFileName, lpszSourceFileName - lpszDownloadFileName + 1);
  696. MyLowercase(lpszSourceFileName);
  697. LPTSTR lpExt = PathFindExtension(lpszSourceFileName);
  698. bIsPatch = FALSE;
  699. if(lpExt && !lstrcmp(lpExt, "._p"))
  700. {
  701. bIsPatch = TRUE;
  702. *lpExt = 0; // truncate trailing "._p" to leave base file name
  703. }
  704. FileNameNode = NameRbFind( &FileNameTree, lpszSourceFileName);
  705. if ( bIsPatch )
  706. {
  707. *lpExt = '.'; // restore complete patch source file name
  708. }
  709. if (FileNameNode != NULL)
  710. {
  711. FileInfo = (PDOWNLOAD_FILEINFO)FileNameNode->Context;
  712. lstrcat(szRealFileName, FileInfo->lpszFileNameToDownload);
  713. if ( bIsPatch )
  714. {
  715. fSuccess = ApplyPatchToFile(
  716. lpszDownloadFileName, // patch file
  717. FileInfo->lpszExistingFileToPatchFrom, // old file
  718. szRealFileName, // new file
  719. 0
  720. );
  721. }
  722. else
  723. {
  724. FixTimeStampOnCompressedFile(lpszSourceFileName);
  725. if(lstrcmpi(lpszDownloadFileName, szRealFileName))
  726. {
  727. fSuccess = MySetupDecompressOrCopyFile(
  728. lpszDownloadFileName, // compressed or whole file
  729. szRealFileName // new file
  730. );
  731. }
  732. }
  733. if (fSuccess)
  734. {
  735. //Notify callback. If it thinks that the hash is incorrect, don't mark the file
  736. //as downloaded, so that we may retry download of this file.
  737. fSuccess = VerifyHash(szRealFileName);
  738. if(fSuccess)
  739. {
  740. fSuccess = ProtectedPatchDownloadCallback(FileListInfo->Callback, PATCH_DOWNLOAD_FILE_COMPLETED,
  741. szRealFileName, FileListInfo->CallbackContext);
  742. if(fSuccess == FALSE)
  743. {
  744. //If callback returned false, we need to abort
  745. WriteToLog("\tDownload complete callback returned false. Aborting\n");
  746. ProtectedPatchDownloadCallback(FileListInfo->Callback, PATCH_DOWNLOAD_ABORT,
  747. NULL, FileListInfo->CallbackContext);
  748. break;
  749. }
  750. else
  751. {
  752. FileInfo->dwFlags = 0;
  753. //Notify callback that 1 file was successfully downloaded
  754. WriteToLog("\tSuccesssfully downloaded %1\n", FileInfo->lpszFileNameToDownload);
  755. ProgressInfo->dwFilesRemaining -= 1;
  756. fSuccess = ProtectedPatchDownloadCallback(FileListInfo->Callback, PATCH_DOWNLOAD_PROGRESS,
  757. ProgressInfo, FileListInfo->CallbackContext);
  758. if(!fSuccess)
  759. {
  760. g_fAbort = TRUE;
  761. return FALSE;
  762. }
  763. }
  764. }
  765. else
  766. {
  767. //Mark it so that we resend the request. Remove patch signature so we get full files instead
  768. // of patches.
  769. WriteToLog("\tHash Incorrect. Need to Re-download %1\n", FileInfo->lpszFileNameToDownload);
  770. FileInfo->dwFlags = PATCHFLAG_DOWNLOAD_NEEDED;
  771. if(FileInfo->lpszExistingFilePatchSignature)
  772. {
  773. LocalFree(FileInfo->lpszExistingFilePatchSignature);
  774. FileInfo->lpszExistingFilePatchSignature = NULL;
  775. }
  776. }
  777. }
  778. else
  779. {
  780. // Patch or decompress failed. notify callback it failed.
  781. WriteToLog("\tPatch or decompression failed for %1\n", FileInfo->lpszFileNameToDownload);
  782. fSuccess = ProtectedPatchDownloadCallback(FileListInfo->Callback, PATCH_DOWNLOAD_FILE_FAILED,
  783. FileInfo, FileListInfo->CallbackContext);
  784. //If callback says to continue or retry download, we do so. If it needs to abort, it return 0
  785. if (!fSuccess)
  786. {
  787. ProtectedPatchDownloadCallback(FileListInfo->Callback, PATCH_DOWNLOAD_ABORT,
  788. NULL, FileListInfo->CallbackContext);
  789. break;
  790. }
  791. if(fSuccess == PATCH_DOWNLOAD_FLAG_RETRY)
  792. {
  793. FileInfo->dwFlags = PATCHFLAG_DOWNLOAD_NEEDED;
  794. if(FileInfo->lpszExistingFilePatchSignature)
  795. {
  796. LocalFree(FileInfo->lpszExistingFilePatchSignature);
  797. FileInfo->lpszExistingFilePatchSignature = NULL;
  798. }
  799. }
  800. else if(fSuccess == PATCH_DOWNLOAD_FLAG_CONTINUE)
  801. {
  802. FileInfo->dwFlags = 0;
  803. }
  804. }
  805. //Delete the temp file. We might be having 2 temp files if this was a patch.
  806. if(lstrcmpi(lpszDownloadFileName, szRealFileName))
  807. {
  808. DeleteFile(lpszDownloadFileName);
  809. }
  810. DeleteFile(szRealFileName);
  811. ResizeBuffer(lpszDownloadFileName, 0, 0);
  812. }
  813. }
  814. }
  815. done:
  816. DestroySubAllocator( hSubAllocator ); // free entire btree
  817. return 0;
  818. }
  819. BOOL VerifyHash(LPTSTR lpszFile)
  820. {
  821. TCHAR szHashFromInf[40];
  822. TCHAR szHashFromFile[40];
  823. // Verify MD5 of new file against the MD5 from inf. If we cannot verify
  824. // the MD5 for any reason, then leave the file alone (success).
  825. // Only if computed MD5 does not match do we reject the file.
  826. LPTSTR lpFileName = PathFindFileName(lpszFile);
  827. if(GetHashidFromINF(lpFileName, szHashFromInf, sizeof(szHashFromInf)) &&
  828. GetFilePatchSignatureA(lpszFile, PATCH_OPTION_SIGNATURE_MD5, NULL, 0, 0, 0, 0,
  829. sizeof(szHashFromFile), szHashFromFile))
  830. {
  831. if (lstrcmpi(szHashFromFile, szHashFromInf))
  832. {
  833. WriteToLog("Hash Incorrect. File hash: %1 Inf hash: %2. Need to Re-download %3\n",
  834. szHashFromFile, szHashFromInf, lpFileName);
  835. return FALSE;
  836. }
  837. }
  838. else
  839. {
  840. WriteToLog("Warning:Could not get hashid for %1 in inf file\n", lpFileName);
  841. }
  842. return TRUE;
  843. }
  844. BOOL ProtectedPatchDownloadCallback(PATCH_DOWNLOAD_CALLBACK Callback, IN PATCH_DOWNLOAD_REASON CallbackReason,
  845. IN PVOID CallbackData, IN PVOID CallBackContext)
  846. {
  847. BOOL Success = TRUE;
  848. if (Callback != NULL )
  849. {
  850. __try
  851. {
  852. Success = Callback(CallbackReason, CallbackData, CallBackContext);
  853. }
  854. __except( EXCEPTION_EXECUTE_HANDLER )
  855. {
  856. SetLastError( GetExceptionCode());
  857. Success = FALSE;
  858. }
  859. }
  860. return Success;
  861. }
  862. BOOL WINAPI PatchCallback(PATCH_DOWNLOAD_REASON Reason, PVOID lpvInfo, PVOID lpvCallBackContext)
  863. {
  864. switch (Reason)
  865. {
  866. case PATCH_DOWNLOAD_ENDDOWNLOADINGDATA:
  867. case PATCH_DOWNLOAD_CONNECTING:
  868. case PATCH_DOWNLOAD_FINDINGSITE:
  869. case PATCH_DOWNLOAD_DOWNLOADINGDATA:
  870. {
  871. LPTSTR lpsz = (LPTSTR)lpvInfo;
  872. lpsz[90] = NULL;
  873. SetProgressText(lpsz);
  874. }
  875. break;
  876. case PATCH_DOWNLOAD_PROGRESS:
  877. {
  878. char szBuffer[100], szText[MAX_PATH];
  879. PDOWNLOAD_INFO ProgressInfo = (PDOWNLOAD_INFO)lpvInfo;
  880. LoadString(g_hInstance, IDS_BYTEINFO, szBuffer, sizeof(szBuffer));
  881. DWORD dwBytesDownloaded = ProgressInfo->dwBytesToDownload - ProgressInfo->dwBytesRemaining;
  882. wsprintf(szText, szBuffer, dwBytesDownloaded, ProgressInfo->dwBytesToDownload);
  883. if(g_hProgressDlg && ProgressInfo->dwBytesToDownload)
  884. {
  885. SetProgressText(szText);
  886. }
  887. break;
  888. }
  889. case PATCH_DOWNLOAD_FILE_COMPLETED: // AdditionalInfo is Source file downloaded
  890. {
  891. TCHAR szDstFile[MAX_PATH];
  892. LPTSTR lpFileName = PathFindFileName((LPCTSTR)lpvInfo);
  893. CombinePaths((LPTSTR)lpvCallBackContext, lpFileName, szDstFile);
  894. CopyFile((LPCTSTR)lpvInfo, szDstFile, FALSE);
  895. }
  896. break;
  897. case PATCH_DOWNLOAD_FILE_FAILED:
  898. //ask it to retry
  899. return PATCH_DOWNLOAD_FLAG_RETRY;
  900. default:
  901. break;
  902. }
  903. return TRUE;
  904. }