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.

993 lines
34 KiB

  1. #include <fusenetincludes.h>
  2. #include <bits.h>
  3. #include <assemblycache.h>
  4. #include "dialog.h"
  5. #include <assemblydownload.h>
  6. #include "..\id\sxsid.h"
  7. #include ".\patchapi.h"
  8. // Update services
  9. #include "server.h"
  10. #define DOWNLOAD_FLAGS_INTERNAL_TRAVERSE_LINK (DOWNLOAD_FLAGS_NOTIFY_COMPLETION + 1)
  11. #define PATCH_DIRECTORY L"__patch__\\"
  12. IBackgroundCopyManager* CAssemblyDownload::g_pManager = NULL;
  13. // ---------------------------------------------------------------------------
  14. // CreateAssemblyDownload
  15. // ---------------------------------------------------------------------------
  16. STDAPI CreateAssemblyDownload(IAssemblyDownload** ppDownload)
  17. {
  18. HRESULT hr = S_OK;
  19. CAssemblyDownload *pDownload = new(CAssemblyDownload);
  20. if (!pDownload)
  21. {
  22. hr = E_OUTOFMEMORY;
  23. goto exit;
  24. }
  25. exit:
  26. *ppDownload = (IAssemblyDownload*) pDownload;
  27. return hr;
  28. }
  29. // ---------------------------------------------------------------------------
  30. // ctor
  31. // ---------------------------------------------------------------------------
  32. CAssemblyDownload::CAssemblyDownload()
  33. : _dwSig('DLND'), _cRef(1), _hr(S_OK), _pRootEmit(NULL), _hNamedEvent(NULL),
  34. _pDlg(NULL)
  35. {}
  36. // ---------------------------------------------------------------------------
  37. // dtor
  38. // ---------------------------------------------------------------------------
  39. CAssemblyDownload::~CAssemblyDownload()
  40. {
  41. SAFERELEASE(_pRootEmit);
  42. SAFEDELETE(_pDlg);
  43. // BUGBUG: Do proper ref counting and release here
  44. //SAFERELEASE(g_pManager);
  45. }
  46. // IAssemblyDownload methods
  47. void MakeSequentialFileName(CString& sPath)
  48. {
  49. int iSeqNum = 1;
  50. int iIndex = sPath._cc;
  51. // BUGBUG: hack up name generation code
  52. sPath.Append(L"[1]");
  53. while (GetFileAttributes(sPath._pwz) != (DWORD)-1)
  54. {
  55. // keep incrementing till unique...
  56. iSeqNum++;
  57. // BUGBUG: note string len limitation
  58. WCHAR buffer[20];
  59. _itow(iSeqNum, buffer, 10);
  60. sPath[iIndex] = L'/';
  61. sPath.RemoveLastElement();
  62. sPath.Append(buffer);
  63. sPath.Append(L"]");
  64. // BUGBUG: MAX_PATH restriction?
  65. }
  66. }
  67. // ---------------------------------------------------------------------------
  68. // DownloadManifestAndDependencies
  69. // ---------------------------------------------------------------------------
  70. HRESULT CAssemblyDownload::DownloadManifestAndDependencies(
  71. LPWSTR pwzApplicationManifestUrl, HANDLE hNamedEvent, DWORD dwFlags)
  72. {
  73. HRESULT hr = S_OK;
  74. LPWSTR pwz = NULL;
  75. IBackgroundCopyJob *pJob = NULL;
  76. GUID guid = {0};
  77. CString sTempManifestPath;
  78. CString sAppUrl;
  79. CString sManifestFileName;
  80. // Create temporary manifest path from url.
  81. sAppUrl.Assign(pwzApplicationManifestUrl);
  82. sAppUrl.LastElement(sManifestFileName);
  83. CAssemblyCache::GetCacheRootDir(sTempManifestPath, CAssemblyCache::Staging);
  84. sTempManifestPath.Append(sManifestFileName._pwz);
  85. MakeSequentialFileName(sTempManifestPath);
  86. // BUGBUG - do real check
  87. if (!g_pManager)
  88. {
  89. CoCreateInstance(CLSID_BackgroundCopyManager, NULL, CLSCTX_LOCAL_SERVER,
  90. IID_IBackgroundCopyManager, (void**) &g_pManager);
  91. }
  92. // just give it a location name
  93. hr = g_pManager->CreateJob(pwzApplicationManifestUrl,
  94. BG_JOB_TYPE_DOWNLOAD, &guid, &pJob);
  95. // Init dialog object with job.
  96. if (dwFlags == DOWNLOAD_FLAGS_PROGRESS_UI)
  97. {
  98. hr = CreateDialogObject(&_pDlg, pJob);
  99. //felixybc no need _pDlg->_pDownload = this; // bugbug - if addref, circular refcount
  100. }
  101. else if ((dwFlags == DOWNLOAD_FLAGS_INTERNAL_TRAVERSE_LINK) && _pDlg)
  102. _pDlg->SetJobObject(pJob);
  103. else if (dwFlags == DOWNLOAD_FLAGS_NOTIFY_COMPLETION)
  104. _hNamedEvent = hNamedEvent;
  105. // Set job config info.
  106. hr = pJob->SetNotifyInterface(static_cast<IBackgroundCopyCallback*> (this));
  107. hr = pJob->SetNotifyFlags(BG_NOTIFY_JOB_MODIFICATION | BG_NOTIFY_JOB_TRANSFERRED | BG_NOTIFY_JOB_ERROR);
  108. hr = pJob->SetPriority(BG_JOB_PRIORITY_FOREGROUND);
  109. // Ensure local dir path exists.
  110. CAssemblyCache::CreateDirectoryHierarchy(NULL, sTempManifestPath._pwz);
  111. // Do the download;
  112. pJob->AddFile(pwzApplicationManifestUrl, sTempManifestPath._pwz);
  113. pJob->Resume();
  114. // We're releasing the job but BITS addrefs it.
  115. SAFERELEASE(pJob);
  116. // Pump messages if progress ui specified.
  117. if (dwFlags == DOWNLOAD_FLAGS_PROGRESS_UI)
  118. {
  119. MSG msg;
  120. BOOL bRet;
  121. DWORD dwError;
  122. while((bRet = GetMessage( &msg, _pDlg->_hwndDlg, 0, 0 )))
  123. {
  124. DWORD dwLow = LOWORD(msg.message);
  125. if (dwLow == WM_FINISH_DOWNLOAD || dwLow == WM_CANCEL_DOWNLOAD)
  126. {
  127. DestroyWindow(_pDlg->_hwndDlg);
  128. // BUGBUG: delete all committed files and app dir if canceling!
  129. if (dwLow == WM_CANCEL_DOWNLOAD)
  130. hr = E_ABORT;
  131. break;
  132. }
  133. if (bRet == -1)
  134. {
  135. dwError = GetLastError();
  136. DebugBreak();
  137. }
  138. if (!IsDialogMessage(_pDlg->_hwndDlg, &msg))
  139. {
  140. TranslateMessage( &msg );
  141. DispatchMessage( &msg );
  142. }
  143. }
  144. }
  145. return hr;
  146. }
  147. // ---------------------------------------------------------------------------
  148. // DoCacheUpdate
  149. // ---------------------------------------------------------------------------
  150. HRESULT CAssemblyDownload::DoCacheUpdate(IBackgroundCopyJob *pJob)
  151. {
  152. LPWSTR pwz = NULL;
  153. DWORD nCount = 0, cc = 0;
  154. SERIALIZED_LIST ManifestList = {0};
  155. IEnumBackgroundCopyFiles *pEnumFiles = NULL;
  156. IBackgroundCopyFile *pFile = NULL;
  157. IAssemblyCacheEmit *pEmit = NULL;
  158. IAssemblyCacheImport *pCacheImport = NULL;
  159. IBackgroundCopyJob *pChildJob = NULL;
  160. CString sDisplayName;
  161. CString sManifestStagingDir;
  162. CString sManifestFilePath, sManifestPatchFilePath;
  163. LPASSEMBLY_IDENTITY pPatchAssemblyId = NULL;
  164. BOOL fAdditionalDependencies = FALSE;
  165. CAssemblyCache::GetCacheRootDir(sManifestStagingDir, CAssemblyCache::Staging);
  166. // Commit files to disk
  167. pJob->Complete();
  168. // Get the file enumerator.
  169. pJob->EnumFiles(&pEnumFiles);
  170. pEnumFiles->GetCount(&nCount);
  171. for (DWORD i = 0; i < nCount; i++)
  172. {
  173. CString sLocalName(CString::COM_Allocator);
  174. pEnumFiles->Next(1, &pFile, NULL);
  175. pFile->GetLocalName(&pwz);
  176. sLocalName.TakeOwnership(pwz);
  177. // This is somewhat hacky - we rely on the local target path
  178. // returned from BITS to figure out if a manifest file.
  179. if (sLocalName.PathPrefixMatch(sManifestStagingDir._pwz) == S_OK)
  180. {
  181. // First thing we need to do is figure out if this
  182. // is a subscription manifest which we have to indirect
  183. // through
  184. LPASSEMBLY_MANIFEST_IMPORT pManifestImport = NULL;
  185. LPDEPENDENT_ASSEMBLY_INFO pDependAsmInfo = NULL;
  186. CString sManifestFileName;
  187. CString sDependantASMCodebase;
  188. DWORD dwManifestType = MANIFEST_TYPE_UNKNOWN;
  189. CreateAssemblyManifestImport(&pManifestImport, sLocalName._pwz);
  190. pManifestImport->ReportManifestType(&dwManifestType);
  191. if (dwManifestType == MANIFEST_TYPE_SUBSCRIPTION)
  192. {
  193. // BUGBUG: the hardcoded index '0'
  194. pManifestImport->GetNextAssembly(0, &pDependAsmInfo);
  195. }
  196. if (pDependAsmInfo)
  197. {
  198. // We know its a subscription
  199. // just transit to the referenced codebase.
  200. // BUGBUG - need to clean up the old manifest in staging dir.
  201. _hr = pDependAsmInfo->Get(DEPENDENT_ASM_CODEBASE, &pwz, &cc);
  202. sDependantASMCodebase.TakeOwnership(pwz, cc);
  203. if (sDependantASMCodebase._pwz)
  204. {
  205. LPASSEMBLY_IDENTITY pAsmId = NULL;
  206. // subscription's dependent asm's id == app's asm id (fully qualified)
  207. if ((_hr = pDependAsmInfo->GetAssemblyIdentity(&pAsmId)) == S_OK)
  208. {
  209. CString sName;
  210. _hr = pAsmId->GetAttribute(SXS_ASSEMBLY_IDENTITY_STD_ATTRIBUTE_NAME_NAME, &pwz, &cc);
  211. sName.TakeOwnership(pwz, cc);
  212. // _hr from above GetAttribute
  213. if (_hr == S_OK)
  214. {
  215. IAssemblyUpdate *pAssemblyUpdate = NULL;
  216. // register for updates
  217. if (SUCCEEDED(_hr = CoCreateInstance(CLSID_CAssemblyUpdate, NULL, CLSCTX_LOCAL_SERVER,
  218. IID_IAssemblyUpdate, (void**)&pAssemblyUpdate)))
  219. {
  220. CString sRemoteName(CString::COM_Allocator);
  221. LPWSTR pwzSubscriptionManifestCodebase = NULL;
  222. DWORD pollingInterval;
  223. pFile->GetRemoteName(&pwzSubscriptionManifestCodebase);
  224. sRemoteName.TakeOwnership(pwzSubscriptionManifestCodebase);
  225. // Get subscription polling interval from manifest
  226. _hr = pManifestImport->GetPollingInterval (&pollingInterval);
  227. _hr = pAssemblyUpdate->RegisterAssemblySubscription(sName._pwz,
  228. sRemoteName._pwz, pollingInterval);
  229. SAFERELEASE(pAssemblyUpdate);
  230. }
  231. // else
  232. // Error in update services. Cannot register subscription for updates - fail gracefully
  233. // BUGBUG: need a way to recover from this and register later
  234. }
  235. // else
  236. // Error in retrieving assembly name. Cannot register subscription for updates - fail gracefully
  237. // BUGBUG: This should not be allowed!
  238. // check if this download is necessary
  239. // download only if not in cache
  240. if ((_hr=CreateAssemblyCacheImport(&pCacheImport, pAsmId, CACHEIMP_CREATE_RETRIEVE_EXIST)) == S_FALSE)
  241. {
  242. if (_pDlg)
  243. {
  244. _pDlg->InitDialog(_pDlg->_hwndDlg);
  245. _pDlg->SetDlgState(DOWNLOADDLG_STATE_GETTING_APP_MANIFEST);
  246. }
  247. _hr = DownloadManifestAndDependencies(sDependantASMCodebase._pwz, NULL, DOWNLOAD_FLAGS_INTERNAL_TRAVERSE_LINK);
  248. }
  249. // else
  250. // assume it's being handled or it's done
  251. SAFERELEASE(pCacheImport);
  252. }
  253. // BUGBUG: should check file integrity
  254. SAFERELEASE(pAsmId);
  255. }
  256. else
  257. // redirected to a manifest which has no dependentassembly/codebase
  258. _hr = HRESULT_FROM_WIN32(ERROR_BAD_FORMAT);
  259. SAFERELEASE(pDependAsmInfo);
  260. // We're done with the subscription manifest now
  261. // and can release the interface and delete it from the manifest staging dir.
  262. SAFERELEASE(pManifestImport);
  263. ::DeleteFile(sLocalName._pwz);
  264. goto exit;
  265. }
  266. SAFERELEASE(pManifestImport);
  267. // Not a subscription manifest - pull down dependencies.
  268. fAdditionalDependencies = TRUE;
  269. // Generate the cache entry (assemblydir/manifest/<dirs>)
  270. // First callbac, _pRootEmit = NULL;
  271. CreateAssemblyCacheEmit(&pEmit, _pRootEmit, 0);
  272. // Generate manifest file codebase directory
  273. // used for enqueuing parsed dependencies.
  274. CString sCodebase(CString::COM_Allocator);
  275. pFile->GetRemoteName(&pwz);
  276. sCodebase.TakeOwnership(pwz);
  277. sCodebase.LastElement(sManifestFileName);
  278. sCodebase.RemoveLastElement();
  279. sCodebase.Append(L"/");
  280. // Create the cache entry.
  281. // (x86_foo_1.0.0.0_en-us/foo.manifest/<+extra dirs>)
  282. pEmit->CopyFile(sLocalName._pwz, sManifestFileName._pwz, MANIFEST);
  283. // If this is first cache entry created, save as root.
  284. if (!_pRootEmit)
  285. {
  286. _pRootEmit = pEmit;
  287. _pRootEmit->AddRef();
  288. }
  289. // QI for the import interface.
  290. pEmit->QueryInterface(IID_IAssemblyCacheImport, (LPVOID*) &pCacheImport);
  291. // First time through loop get the display name
  292. if (!i)
  293. {
  294. pCacheImport->GetDisplayName(&pwz, &cc);
  295. sDisplayName.TakeOwnership(pwz, cc);
  296. }
  297. // Line up it's dependencies for download and fire them off.
  298. // We pass the cache import interface which provides the
  299. // manifest enumeration. We could just as easily passed a manifest
  300. // interface but already have one in the pCacheImport
  301. EnqueueDependencies(pCacheImport, sCodebase, sDisplayName, &pChildJob);
  302. SAFERELEASE(pEmit);
  303. SAFERELEASE(pCacheImport);
  304. }
  305. // if file was a patch file, find the source and target, apply patch to source and move result to target
  306. // or if file was compressed, uncompress file
  307. else
  308. {
  309. // Grab mainfest file directory and append on PATCH_DIRECTORY
  310. // "C:\Program Files\Application Store\x86_foo_X.X.X.X\PATCH_DIRECTORY\"
  311. _pRootEmit->GetManifestFileDir(&pwz, &cc);
  312. sManifestFilePath.TakeOwnership(pwz, cc);
  313. sManifestPatchFilePath.Assign(sManifestFilePath);
  314. sManifestPatchFilePath.Append(PATCH_DIRECTORY);
  315. // if local file begins with the manifests patch direcotry, file is a patch file
  316. if (sLocalName.PathPrefixMatch(sManifestPatchFilePath._pwz) == S_OK)
  317. {
  318. CString sPatchDisplayName;
  319. // init pPatchAssemblyId only once
  320. if (!pPatchAssemblyId)
  321. {
  322. _hr = GetPatchDisplayNameFromFilePath (sLocalName, sPatchDisplayName);
  323. CreateAssemblyIdentityEx(&pPatchAssemblyId, 0, sPatchDisplayName._pwz);
  324. }
  325. // Check to see if file is a cab file (have to revamp IsCABbed to handle this by passing in the AssemblyId)
  326. // If it is CABbed, then have to call the FDI functions and pass in base directory (should have relative paths in
  327. // the cab .ddf file (done by tool).
  328. // using the patch file path, step through the manifest to to find
  329. // the source and target files associated with the patch file and
  330. // apply the patch file to the the source file to create the target file.
  331. _hr = ApplyPatchFile (pPatchAssemblyId, sLocalName._pwz);
  332. }
  333. }
  334. SAFERELEASE(pFile);
  335. }
  336. // if patched, delete patch directory
  337. if (pPatchAssemblyId)
  338. _hr = RemoveDirectoryAndChildren(sManifestPatchFilePath._pwz);
  339. SAFERELEASE(pEnumFiles);
  340. // Submit the job.
  341. if (pChildJob)
  342. {
  343. if (_pDlg)
  344. {
  345. _pDlg->InitDialog(_pDlg->_hwndDlg);
  346. _pDlg->SetDlgState(DOWNLOADDLG_STATE_GETTING_OTHER_FILES);
  347. _pDlg->SetJob(pChildJob);
  348. }
  349. pChildJob->Resume();
  350. SAFERELEASE(pChildJob);
  351. }
  352. // If no additional jobs
  353. if (!fAdditionalDependencies)
  354. {
  355. // If we have a dialog then DoFinish will commit bits.
  356. // BUGBUG - formalize done semantics
  357. _pRootEmit->Commit(0);
  358. if (_hNamedEvent)
  359. SetEvent(_hNamedEvent);
  360. _pDlg->SetDlgState(DOWNLOADDLG_STATE_ALL_DONE);
  361. }
  362. exit:
  363. SAFERELEASE(pPatchAssemblyId);
  364. return S_OK;
  365. }
  366. // ---------------------------------------------------------------------------
  367. //GetPatchDisplayNameFromFilePath
  368. // ---------------------------------------------------------------------------
  369. HRESULT CAssemblyDownload::GetPatchDisplayNameFromFilePath ( CString &sPatchFilePath, CString &sDisplayName)
  370. {
  371. CString pwzFilePath;
  372. LPWSTR pwzStart, pwzEnd;
  373. pwzFilePath.Assign(sPatchFilePath);
  374. // Search file path for the PATCH_DIRECTORY
  375. pwzStart = StrStr(pwzFilePath._pwz, PATCH_DIRECTORY);
  376. // Set start pointer the one directory below the PATCH_DIRECTORY
  377. // This is the Beginning of the Patch DisplayNameb
  378. pwzStart = StrChr(pwzStart, L'\\');
  379. pwzStart++;
  380. // Set end pointer to the end of the Patch DisplayName and null out the character
  381. pwzEnd = StrChr(pwzStart, L'\\');
  382. (*pwzEnd) = L'\0';
  383. sDisplayName.Assign(pwzStart);
  384. return S_OK;
  385. }
  386. // ---------------------------------------------------------------------------
  387. //ApplyPatchFile
  388. // ---------------------------------------------------------------------------
  389. HRESULT CAssemblyDownload::ApplyPatchFile ( LPASSEMBLY_IDENTITY pPatchAssemblyId, LPWSTR pwzPatchFilePath)
  390. {
  391. int i = 0;
  392. LPWSTR pwzSource = NULL, pwzTarget = NULL;
  393. LPWSTR pwzBuf;
  394. DWORD ccBuf;
  395. CString sPatchLocalName;
  396. CString sPatchDisplayName;
  397. CString sManifestDir, sPatchManifestDir;
  398. CString sSourcePath, sTargetPath, sPatchPath;
  399. IAssemblyManifestImport *pManifestImport = NULL;
  400. LPASSEMBLY_IDENTITY pTempPatchAssemblyId = NULL;
  401. LPASSEMBLY_CACHE_IMPORT pPatchImport = NULL;
  402. // Get DisplayName of the "Patch From" Assembly
  403. _hr = pPatchAssemblyId->GetDisplayName(ASMID_DISPLAYNAME_NOMANGLING, &pwzBuf, &ccBuf);
  404. sPatchDisplayName.TakeOwnership(pwzBuf, ccBuf);
  405. //Parse out the local file path from the full file path of the patch file
  406. pwzBuf= StrStr (pwzPatchFilePath, sPatchDisplayName._pwz);
  407. pwzBuf = StrChr(pwzBuf, L'\\');
  408. //Following commented out code needed for NULL patching
  409. /*
  410. // Take care of patching from "itself"
  411. if (StrStr (pwzPatchFilePath, sPatchDisplayName._pwz) != NULL)
  412. {
  413. pwzBuf= StrStr (pwzBuf, sPatchDisplayName._pwz);
  414. pwzBuf = StrChr(pwzBuf, L'\\');
  415. }
  416. */
  417. pwzBuf++;
  418. sPatchLocalName.Assign(pwzBuf);
  419. _pRootEmit->GetManifestImport(&pManifestImport);
  420. _pRootEmit->GetManifestFileDir(&pwzBuf, &ccBuf);
  421. sManifestDir.TakeOwnership (pwzBuf, ccBuf);
  422. // set up the patchAssemblyNode in the manifestimport
  423. while ((_hr = pManifestImport->GetNextPatchAssemblyId (i, &pTempPatchAssemblyId)) == S_OK)
  424. {
  425. if(FAILED(_hr =pPatchAssemblyId->IsEqual(pTempPatchAssemblyId)))
  426. goto exit;
  427. else if (_hr == S_OK)
  428. {
  429. _hr = pManifestImport->SetPatchAssemblyNode(i);
  430. // get the cacheImport for the "patch from" assembly
  431. if ((_hr = CreateAssemblyCacheImport(&pPatchImport, pPatchAssemblyId, CACHEIMP_CREATE_RETRIEVE_EXIST_COMPLETED))!= S_OK)
  432. goto exit;
  433. break;
  434. }
  435. i++;
  436. SAFERELEASE(pTempPatchAssemblyId);
  437. }
  438. // there has to be a matching patchassembly node.
  439. if (_hr != S_OK)
  440. goto exit;
  441. pPatchImport->GetManifestFileDir(&pwzBuf, &ccBuf);
  442. sPatchManifestDir.TakeOwnership (pwzBuf, ccBuf);
  443. if((_hr = pManifestImport->GetPatchFilePatchMapping(sPatchLocalName._pwz, &pwzSource, &pwzTarget)) != S_OK)
  444. goto exit;
  445. // Set up paths of source, target and patch files
  446. // set up Source path
  447. // If NULL patching, must add code to call sSourcePath.FreeBuffer is pwzSource is NULL
  448. /*
  449. sSourcePath.Assign(sManifestDir);
  450. sSourcePath.Append(PATCH_DIRECTORY);
  451. sSourcePath.Append(sPatchDisplayName);
  452. sSourcePath.Append(L"\\");
  453. */
  454. sSourcePath.Append(sPatchManifestDir);
  455. sSourcePath.Append(pwzSource);
  456. // set up Target path
  457. sTargetPath.Assign(sManifestDir);
  458. sTargetPath.Append(pwzTarget);
  459. // set up Patch path
  460. sPatchPath.Assign(pwzPatchFilePath);
  461. //Apply patchfile to sSource (grab from patch directory) and copy to path specified by sTarget
  462. if (!(ApplyPatchToFile((LPCWSTR)sPatchPath._pwz, (LPCWSTR)sSourcePath._pwz, (LPCWSTR)sTargetPath._pwz, 0)))
  463. _hr = E_FAIL;
  464. goto exit;
  465. exit:
  466. SAFEDELETEARRAY(pwzSource);
  467. SAFEDELETEARRAY(pwzTarget);
  468. SAFERELEASE(pTempPatchAssemblyId);
  469. SAFERELEASE(pManifestImport);
  470. SAFERELEASE(pPatchImport);
  471. return _hr;
  472. }
  473. // ---------------------------------------------------------------------------
  474. // EnqueueDependencies
  475. // ---------------------------------------------------------------------------
  476. HRESULT CAssemblyDownload::EnqueueDependencies(LPASSEMBLY_CACHE_IMPORT
  477. pCacheImport, CString &sCodebase, CString &sDisplayName, IBackgroundCopyJob **ppJob)
  478. {
  479. LPWSTR pwzBuf = NULL;
  480. LPWSTR pwzPatchFile, pwzSource;
  481. DWORD ccBuf = 0;
  482. DWORD n = 0, i=0;
  483. BOOL patchAvailable = FALSE;
  484. BOOL CABbed = FALSE;
  485. CString sLocalPatchDirectoryPath, sPatchAssemblyDisplayName, sPatchManifestDirectory;
  486. CString sManifestDirectory;
  487. GUID guid = {0};
  488. IBackgroundCopyJob *pJob = NULL;
  489. IAssemblyManifestImport *pManifestImport = NULL;
  490. IAssemblyIdentity *pIdentity = NULL;
  491. IAssemblyFileInfo *pAssemblyFile = NULL;
  492. LPDEPENDENT_ASSEMBLY_INFO pDependAsm = NULL;
  493. LPASSEMBLY_CACHE_EMIT pCacheEmit = NULL;
  494. LPASSEMBLY_CACHE_IMPORT pMaxCachedImport = NULL;
  495. LPASSEMBLY_CACHE_IMPORT pMaxPatchImport = NULL;
  496. LPASSEMBLY_IDENTITY pPatchAssemblyId = NULL;
  497. // Create a new job
  498. if (*ppJob)
  499. pJob = *ppJob;
  500. else
  501. {
  502. g_pManager->CreateJob(sDisplayName._pwz, BG_JOB_TYPE_DOWNLOAD, &guid, &pJob);
  503. pJob->SetNotifyInterface(static_cast<IBackgroundCopyCallback*> (this));
  504. _hr = pJob->SetNotifyFlags(BG_NOTIFY_JOB_MODIFICATION | BG_NOTIFY_JOB_TRANSFERRED | BG_NOTIFY_JOB_ERROR);
  505. _hr = pJob->SetPriority(BG_JOB_PRIORITY_FOREGROUND);
  506. }
  507. // Get the cache import's manifest interface
  508. pCacheImport->GetManifestImport(&pManifestImport);
  509. // Get the asm Id
  510. _hr = pManifestImport->GetAssemblyIdentity(&pIdentity);
  511. // Find max completed version, if any
  512. // Init newly created cache import with the highest completed version
  513. // else S_FALSE or E_* and pMaxCachedImport == NULL - no completed version
  514. _hr = CreateAssemblyCacheImport(&pMaxCachedImport, pIdentity, CACHEIMP_CREATE_RETRIEVE_MAX_COMPLETED);
  515. // Check to see if there is a suitable upgradable version to patch from already in cache
  516. while (pManifestImport->GetNextPatchAssemblyId (i, &pPatchAssemblyId) == S_OK)
  517. {
  518. if (FAILED(_hr = CreateAssemblyCacheImport(&pMaxPatchImport, pPatchAssemblyId, CACHEIMP_CREATE_RETRIEVE_EXIST_COMPLETED)))
  519. goto exit;
  520. if (_hr == S_FALSE)
  521. {
  522. if(FAILED(_hr =pPatchAssemblyId->IsEqual(pIdentity)))
  523. goto exit;
  524. else if (_hr == S_OK)
  525. {
  526. pMaxPatchImport = pCacheImport;
  527. pMaxPatchImport->AddRef();
  528. }
  529. }
  530. if (_hr == S_OK)
  531. {
  532. // Set the patch assembly node
  533. pManifestImport->SetPatchAssemblyNode(i);
  534. // grab the manifest directory
  535. pCacheImport->GetManifestFileDir(&pwzBuf, &ccBuf);
  536. sManifestDirectory.TakeOwnership(pwzBuf, ccBuf);
  537. // get the manifest directory of the "patch from" directory
  538. pMaxPatchImport->GetManifestFileDir(&pwzBuf, &ccBuf);
  539. sPatchManifestDirectory.TakeOwnership(pwzBuf, ccBuf);
  540. //get display name of patch assembly identity
  541. _hr = (pPatchAssemblyId->GetDisplayName(ASMID_DISPLAYNAME_NOMANGLING, &pwzBuf, &ccBuf));
  542. sPatchAssemblyDisplayName.TakeOwnership (pwzBuf, ccBuf);
  543. //get the local path of the "patch to" directory
  544. sLocalPatchDirectoryPath.Assign(PATCH_DIRECTORY);
  545. sLocalPatchDirectoryPath.PathCombine(sPatchAssemblyDisplayName);
  546. sLocalPatchDirectoryPath.Append(L"\\");
  547. sLocalPatchDirectoryPath.PathNormalize();
  548. //create the patch directory
  549. CAssemblyCache::CreateDirectoryHierarchy(sManifestDirectory._pwz, sLocalPatchDirectoryPath._pwz);
  550. patchAvailable = TRUE;
  551. break;
  552. }
  553. i++;
  554. // Release pPatchAssemblyId every time through the loop
  555. // If the break is executed, the last pPatchAssemblyId is Released at end of the function
  556. SAFERELEASE (pPatchAssemblyId);
  557. }
  558. SAFERELEASE(pIdentity);
  559. // Lazy init. QI for the emit interface.
  560. if (!pCacheEmit)
  561. _hr = pCacheImport->QueryInterface(IID_IAssemblyCacheEmit, (LPVOID*) &pCacheEmit);
  562. if (!pCacheEmit)
  563. {
  564. _hr = E_FAIL;
  565. goto exit;
  566. }
  567. //Check to see if files are contained in a CAB. If so, entire pjob is the on cab file.
  568. if (patchAvailable)
  569. {
  570. LPWSTR pwzCabName;
  571. if ((_hr =pManifestImport->IsCABbed(&pwzCabName)) == S_OK)
  572. {
  573. n=0;
  574. CString sFileName;
  575. CString sLocalFilePath;
  576. CString sRemoteUrl;
  577. sFileName.TakeOwnership(pwzCabName);
  578. // Form local file path
  579. pCacheImport->GetManifestFileDir(&pwzBuf, &ccBuf);
  580. sLocalFilePath.TakeOwnership(pwzBuf, ccBuf);
  581. // Combine and ensure backslashes.
  582. sLocalFilePath.PathCombine(sFileName);
  583. sLocalFilePath.PathNormalize();
  584. // Form remote name
  585. sRemoteUrl.Assign(sCodebase);
  586. sRemoteUrl.UrlCombine(sFileName);
  587. // add the file to the job.
  588. pJob->AddFile(sRemoteUrl._pwz, sLocalFilePath._pwz);
  589. CABbed = TRUE;
  590. }
  591. }
  592. // Submit files directly into their target dirs.
  593. while ((!CABbed) && (pManifestImport->GetNextFile(n++, &pAssemblyFile) == S_OK))
  594. {
  595. CString sFileName;
  596. CString sLocalFilePath;
  597. CString sRemoteUrl;
  598. BOOL bSkipFile = FALSE;
  599. // File name parsed from manifest.
  600. pAssemblyFile->Get(ASM_FILE_NAME, &pwzBuf, &ccBuf);
  601. sFileName.TakeOwnership(pwzBuf, ccBuf);
  602. // Check against the max committed version
  603. if (pMaxCachedImport)
  604. {
  605. LPWSTR pwzPath = NULL;
  606. if ((_hr = pMaxCachedImport->FindExistMatching(pAssemblyFile, &pwzPath)) == S_OK)
  607. {
  608. // Copy from existing cached copy to the new location
  609. // (Non-manifest files)
  610. if (SUCCEEDED(_hr = pCacheEmit->CopyFile(pwzPath, sFileName._pwz, OTHERFILES)))
  611. bSkipFile = TRUE;
  612. SAFEDELETEARRAY(pwzPath);
  613. }
  614. }
  615. if (!bSkipFile)
  616. {
  617. // Form local file path...
  618. // Manifest cache directory
  619. pCacheImport->GetManifestFileDir(&pwzBuf, &ccBuf);
  620. sLocalFilePath.TakeOwnership(pwzBuf, ccBuf);
  621. if (patchAvailable)
  622. {
  623. if(FAILED(_hr = pManifestImport->GetTargetPatchMapping(sFileName._pwz, &pwzSource, &pwzPatchFile)))
  624. goto exit;
  625. else if (_hr == S_OK)
  626. {
  627. CString sLocalPatchDirFileName, sOrigFileName;
  628. // Set up path of source file to be copied
  629. sOrigFileName.Assign(sPatchManifestDirectory);
  630. sOrigFileName.PathCombine(pwzSource);
  631. // Set up local path of where the source file will be copied to.
  632. sLocalPatchDirFileName.Assign(sLocalPatchDirectoryPath);
  633. sLocalPatchDirFileName.PathCombine(pwzSource);
  634. // Copy Source File into path directory
  635. // _hr = pCacheEmit->CopyFile(sOrigFileName._pwz, sLocalPatchDirFileName._pwz, OTHERFILES);
  636. sFileName.Assign (pwzPatchFile);
  637. // Append the patch directory to local file path
  638. sLocalFilePath.Append(sLocalPatchDirectoryPath);
  639. CAssemblyCache::CreateDirectoryHierarchy(sLocalFilePath._pwz, sFileName._pwz);
  640. SAFEDELETEARRAY(pwzSource);
  641. SAFEDELETEARRAY(pwzPatchFile);
  642. }
  643. }
  644. // Form local file path continued from above if statement
  645. // Combine and ensure backslashes.
  646. sLocalFilePath.PathCombine(sFileName);
  647. sLocalFilePath.PathNormalize();
  648. // Form remote name
  649. sRemoteUrl.Assign(sCodebase);
  650. sRemoteUrl.UrlCombine(sFileName);
  651. // add the file to the job.
  652. pJob->AddFile(sRemoteUrl._pwz, sLocalFilePath._pwz);
  653. }
  654. SAFERELEASE(pAssemblyFile);
  655. }
  656. // Submit assembly manifests into staging area
  657. // Note - we should also get assembly codebase and
  658. // use this instead or adjunctly to display name.
  659. // As is, there is a problem if the ref is partial.
  660. n = 0;
  661. while (pManifestImport->GetNextAssembly(n, &pDependAsm) == S_OK)
  662. {
  663. CString sAssemblyName;
  664. CString sLocalFilePath;
  665. CString sRemoteUrl;
  666. // Form local name (in staging area)....
  667. pDependAsm->GetAssemblyIdentity(&pIdentity);
  668. // Get the identity name
  669. pIdentity->GetAttribute(SXS_ASSEMBLY_IDENTITY_STD_ATTRIBUTE_NAME_NAME,
  670. &pwzBuf, &ccBuf);
  671. sAssemblyName.TakeOwnership(pwzBuf, ccBuf);
  672. // Form local cache path from identity name.
  673. // BUG?
  674. CAssemblyCache::GetCacheRootDir(sLocalFilePath, CAssemblyCache::Staging);
  675. sLocalFilePath.Append(sAssemblyName);
  676. sLocalFilePath.Append(L".manifest");
  677. MakeSequentialFileName(sLocalFilePath);
  678. // Get remote name, if any specified
  679. pDependAsm->Get(DEPENDENT_ASM_CODEBASE, &pwzBuf, &ccBuf);
  680. if (pwzBuf != NULL)
  681. sRemoteUrl.TakeOwnership(pwzBuf, ccBuf);
  682. else
  683. {
  684. // Form remote name - probing, in effect.
  685. sRemoteUrl.Assign(sCodebase);
  686. sRemoteUrl.UrlCombine(sAssemblyName);
  687. sRemoteUrl.Append(L"/");
  688. sRemoteUrl.Append(sAssemblyName);
  689. sRemoteUrl.Append(L".manifest");
  690. }
  691. pJob->AddFile(sRemoteUrl._pwz, sLocalFilePath._pwz);
  692. SAFERELEASE(pIdentity);
  693. SAFERELEASE(pDependAsm);
  694. n++;
  695. }
  696. *ppJob = pJob;
  697. _hr = S_OK;
  698. exit:
  699. SAFERELEASE(pManifestImport);
  700. SAFERELEASE(pCacheEmit);
  701. SAFERELEASE(pMaxCachedImport);
  702. SAFERELEASE(pPatchAssemblyId);
  703. SAFERELEASE(pMaxPatchImport );
  704. return _hr;
  705. }
  706. // IBackgroundCopyCallback methods
  707. // ---------------------------------------------------------------------------
  708. // JobTransferred
  709. // ---------------------------------------------------------------------------
  710. HRESULT CAssemblyDownload::JobTransferred(IBackgroundCopyJob *pJob)
  711. {
  712. if (_pDlg)
  713. _pDlg->HandleCOMCallback(pJob, TRUE);
  714. _hr = DoCacheUpdate(pJob);
  715. return _hr;
  716. }
  717. // ---------------------------------------------------------------------------
  718. // JobError
  719. // ---------------------------------------------------------------------------
  720. HRESULT CAssemblyDownload::JobError(IBackgroundCopyJob *pJob, IBackgroundCopyError *pError)
  721. {
  722. LPWSTR pwstr = NULL;
  723. pError->GetErrorDescription(MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US), &pwstr);
  724. // BUGBUG - need to do CoTaskMemFree on pwstr (or use a cstring)
  725. // _pDlg->HandleCOMCallback(pJob, TRUE);
  726. return S_OK;
  727. }
  728. // ---------------------------------------------------------------------------
  729. // JobModification
  730. // ---------------------------------------------------------------------------
  731. HRESULT CAssemblyDownload::JobModification(IBackgroundCopyJob *pJob, DWORD dwReserved)
  732. {
  733. if (_pDlg)
  734. _pDlg->HandleCOMCallback(pJob, TRUE);
  735. return S_OK;
  736. }
  737. // Privates
  738. // IUnknown methods
  739. // ---------------------------------------------------------------------------
  740. // CAssemblyDownload::QI
  741. // ---------------------------------------------------------------------------
  742. STDMETHODIMP
  743. CAssemblyDownload::QueryInterface(REFIID riid, void** ppvObj)
  744. {
  745. if ( IsEqualIID(riid, IID_IUnknown)
  746. || IsEqualIID(riid, IID_IAssemblyDownload)
  747. )
  748. {
  749. *ppvObj = static_cast<IAssemblyDownload*> (this);
  750. AddRef();
  751. return S_OK;
  752. }
  753. else if (IsEqualIID(riid, IID_IBackgroundCopyCallback))
  754. {
  755. *ppvObj = static_cast<IBackgroundCopyCallback*> (this);
  756. AddRef();
  757. return S_OK;
  758. }
  759. else
  760. {
  761. *ppvObj = NULL;
  762. return E_NOINTERFACE;
  763. }
  764. }
  765. // ---------------------------------------------------------------------------
  766. // CAssemblyDownload::AddRef
  767. // ---------------------------------------------------------------------------
  768. STDMETHODIMP_(ULONG)
  769. CAssemblyDownload::AddRef()
  770. {
  771. return InterlockedIncrement ((LONG*) &_cRef);
  772. }
  773. // ---------------------------------------------------------------------------
  774. // CAssemblyDownload::Release
  775. // ---------------------------------------------------------------------------
  776. STDMETHODIMP_(ULONG)
  777. CAssemblyDownload::Release()
  778. {
  779. ULONG lRet = InterlockedDecrement ((LONG*) &_cRef);
  780. if (!lRet)
  781. delete this;
  782. return lRet;
  783. }