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.

680 lines
17 KiB

  1. //+-------------------------------------------------------------------------
  2. //
  3. // Microsoft Windows
  4. //
  5. // Copyright (C) Microsoft Corporation, 2000
  6. //
  7. // File: catalog.cpp
  8. //
  9. //--------------------------------------------------------------------------
  10. #include "pch.h"
  11. #pragma hdrstop
  12. extern CLIENT_HANDLES ghClientHandles;
  13. extern ENGINE_EVENTS EngineEvents;
  14. const WCHAR AUJOBNAME[] = L"Windows Update";
  15. void DoDownloadStatus(
  16. DWORD dwDownloadMsg,
  17. IBackgroundCopyJob *pBGJob,
  18. IBackgroundCopyError *pBGErr)
  19. {
  20. DEBUGMSG("WUAUENG DoDownloadStatus!");
  21. switch(dwDownloadMsg)
  22. {
  23. case CATMSG_TRANSIENT_ERROR:
  24. {
  25. #ifdef DBG
  26. if (NULL == pBGErr &&
  27. S_OK != pBGJob->GetError(&pBGErr))
  28. {
  29. // break;
  30. }
  31. else
  32. {
  33. HRESULT hrErr;
  34. BG_ERROR_CONTEXT bgErrContext;
  35. // Formulate error message
  36. (void) pBGErr->GetError(&bgErrContext, &hrErr);
  37. DEBUGMSG("WUAUENG DoDownloadStatus - TRANSIENT_ERROR %#lx (context=%d)", hrErr, bgErrContext);
  38. pBGErr->Release();
  39. }
  40. #endif
  41. EngineEvents.SetEvent(IDOWNLOAD_TRANSIENT_ERROR_EVT);
  42. }
  43. break;
  44. case CATMSG_DOWNLOAD_IN_PROGRESS:
  45. {
  46. DEBUGMSG("WUAUENG DoDownloadStatus - DOWNLOAD_IN_PROGRESS");
  47. EngineEvents.SetEvent(IDOWNLOAD_DOWNLOAD_IN_PROGRESS);
  48. }
  49. break;
  50. case CATMSG_DOWNLOAD_COMPLETE: // Ping required
  51. {
  52. // Send ping for each successfully downloaded item.
  53. DEBUGMSG("WUAUENG DoDownloadStatus - file download done");
  54. EngineEvents.SetEvent(IDOWNLOAD_COMPLETE_EVT);
  55. }
  56. break;
  57. case CATMSG_DOWNLOAD_ERROR:
  58. {
  59. DEBUGMSG("WUAUENG DoDownloadStatus - DOWNLOAD_ERROR");
  60. //DEBUGMSG("WUAUENG DoDownloadStatus() ping error puid %d and error description %S", ptDownloadStatusData->m_errPuid, ptDownloadStatusData->m_tszErrDesc);
  61. IBackgroundCopyFile *pBGFile = NULL;
  62. // Found out which item caused the download error
  63. if (SUCCEEDED(pBGErr->GetFile(&pBGFile)))
  64. {
  65. BSTR bstrErrorItemId; // only used if dwDownloadMsg == CATMSG_DOWNLOAD_ERROR
  66. LPWSTR pwszLocalFileName = NULL;
  67. (void) pBGFile->GetLocalName(&pwszLocalFileName);
  68. // Ping for download failure
  69. if (NULL != (bstrErrorItemId = gpAUcatalog->FindItemIdByLocalFileName(pwszLocalFileName)))
  70. {
  71. HRESULT hrErr;
  72. BG_ERROR_CONTEXT bgErrContext;
  73. TCHAR tszMessage[30];
  74. // Formulate error message
  75. (void) pBGErr->GetError(&bgErrContext, &hrErr);
  76. (void)StringCchPrintfEx(tszMessage, ARRAYSIZE(tszMessage), NULL, NULL, MISTSAFE_STRING_FLAGS, _T("ctx=%d"), bgErrContext);
  77. gPingStatus.PingDownload(
  78. FALSE,
  79. URLLOGSTATUS_Failed,
  80. hrErr,
  81. W2T(bstrErrorItemId),
  82. NULL,
  83. tszMessage);
  84. }
  85. CoTaskMemFree(pwszLocalFileName);
  86. pBGFile->Release();
  87. }
  88. #ifdef DBG
  89. else
  90. {
  91. DEBUGMSG("WUAUENG DoDownloadStatus failed to get IBackgroundCopyFile object");
  92. }
  93. #endif
  94. }
  95. break;
  96. default:
  97. DEBUGMSG("WUAUENG DoDownloadStatus - invalid message");
  98. break;
  99. }
  100. }
  101. CAUDownloader::~CAUDownloader()
  102. {
  103. IBackgroundCopyJob * pjob;
  104. HRESULT hr ;
  105. // fixcode optimization check if m_refs != 0
  106. DEBUGMSG("CAUDownloader::~CAUDownloader() starts");
  107. if ( SUCCEEDED(FindDownloadJob(&pjob)))
  108. {
  109. DEBUGMSG("Found bits notify interface to release");
  110. if (FAILED(hr = pjob->SetNotifyInterface(NULL)))
  111. {
  112. DEBUGMSG(" failed to delete job notification interface %#lx", hr);
  113. }
  114. pjob->Release();
  115. }
  116. if ( FAILED(hr = CoDisconnectObject((IUnknown *)this, 0)) )
  117. {
  118. DEBUGMSG("CoDisconnectObject() failed %#lx", hr);
  119. }
  120. DEBUGMSG("WUAUENG: CAUDownloader destructed with m_refs = %d", m_refs);
  121. }
  122. ///////////////////////////////////////////////////////////////////////////////////
  123. // when service starts up, find last download job if there is one and reconnect AU to drizzle
  124. //////////////////////////////////////////////////////////////////////////////////
  125. HRESULT CAUDownloader::ContinueLastDownloadJob(/*const GUID & downloadid*/)
  126. {
  127. HRESULT hr = E_FAIL;
  128. IBackgroundCopyJob * pjob = NULL;
  129. DEBUGMSG("CAUDownloader::ContinueLastDownloadJob() starts");
  130. if (GUID_NULL != m_DownloadId)
  131. {
  132. if (SUCCEEDED(hr = FindDownloadJob(&pjob)) && SUCCEEDED(hr = ReconnectDownloadJob()))
  133. {
  134. DEBUGMSG("found and connected to previous download job ");
  135. goto done;
  136. }
  137. else
  138. {
  139. DEBUGMSG("fail to find or connect to previous download job");
  140. m_DownloadId = GUID_NULL;
  141. }
  142. }
  143. done:
  144. SafeRelease(pjob);
  145. DEBUGMSG("CAUDownloader::ContinueLastDownloadJob() ends");
  146. return hr;
  147. }
  148. HRESULT CAUDownloader::CreateDownloadJob(IBackgroundCopyJob **ppjob)
  149. {
  150. IBackgroundCopyManager * pmanager = NULL;
  151. HRESULT hr;
  152. *ppjob = NULL;
  153. m_DownloadId = GUID_NULL;
  154. if (FAILED(hr = CoCreateInstance(__uuidof(BackgroundCopyManager),
  155. NULL,
  156. CLSCTX_LOCAL_SERVER,
  157. __uuidof(IBackgroundCopyManager),
  158. (void **)&pmanager )))
  159. {
  160. DEBUGMSG("CreateDownloadJob : create manager failed %x ", hr);
  161. goto done;
  162. }
  163. if (FAILED(hr=pmanager->CreateJob( AUJOBNAME ,
  164. BG_JOB_TYPE_DOWNLOAD,
  165. &m_DownloadId,
  166. ppjob )))
  167. {
  168. DEBUGMSG("CreateDownloadJob : create job failed %x ", hr);
  169. goto done;
  170. }
  171. {
  172. DWORD fFullBandwidth = 0;;
  173. // change job priority to full bandwidth if appropriate reg key set.
  174. if ( SUCCEEDED(GetRegDWordValue(L"FullBandwidth", &fFullBandwidth)) && fFullBandwidth )
  175. {
  176. HRESULT hr1 = (*ppjob)->SetPriority(BG_JOB_PRIORITY_FOREGROUND);
  177. DEBUGMSG("CAUDownloader::CreateDownloadJob -- FULL BANDWIDTH, hr = %#lx", hr1);
  178. }
  179. }
  180. #ifdef DBG
  181. WCHAR szGUID[50]; //really need 39 bytes
  182. int iret;
  183. iret = StringFromGUID2(m_DownloadId, //GUID to be converted
  184. szGUID, //Pointer to resulting string
  185. ARRAYSIZE(szGUID));//Size of array at lpsz
  186. if (0 != iret)
  187. {
  188. DEBUGMSG("WUAUENG m_DownloadId = %S", szGUID);
  189. }
  190. #endif
  191. if (FAILED(hr = SetDrizzleNotifyInterface()))
  192. {
  193. DEBUGMSG("CreateDownloadJob : set notification interface failed %x", hr);
  194. }
  195. done:
  196. SafeRelease(pmanager);
  197. if (FAILED(hr))
  198. {
  199. Reset();
  200. SafeReleaseNULL(*ppjob);
  201. }
  202. return hr;
  203. }
  204. HRESULT CAUDownloader::FindDownloadJob(IBackgroundCopyJob ** ppjob)
  205. {
  206. IBackgroundCopyManager * pmanager = NULL;
  207. HRESULT hr;
  208. if (FAILED(hr = CoCreateInstance(__uuidof(BackgroundCopyManager),
  209. NULL,
  210. CLSCTX_LOCAL_SERVER,
  211. __uuidof(IBackgroundCopyManager),
  212. (void **)&pmanager )))
  213. {
  214. DEBUGMSG("FindDownloadJob : create manager failed %x ", hr);
  215. goto done;
  216. }
  217. if (FAILED(hr=pmanager->GetJob(m_DownloadId, ppjob )))
  218. {
  219. // DEBUGMSG("FindDownloadJob : get job failed %x ", hr); //might be expected
  220. }
  221. done:
  222. SafeRelease(pmanager);
  223. return hr;
  224. }
  225. STDMETHODIMP
  226. CAUDownloader::JobTransferred(
  227. IBackgroundCopyJob * pjob
  228. )
  229. {
  230. HRESULT hr;
  231. #if DBG
  232. //
  233. // Make sure the right job is finished.
  234. //
  235. {
  236. GUID jobId;
  237. if (FAILED( hr= pjob->GetId( &jobId )))
  238. {
  239. return hr;
  240. }
  241. if ( jobId != m_DownloadId )
  242. {
  243. DEBUGMSG("notified of completion of a download job that I don't own");
  244. }
  245. }
  246. #endif
  247. //
  248. // Transfer file ownership from downloader to catalogue.
  249. //
  250. if (FAILED(hr= pjob->Complete()))
  251. {
  252. return hr;
  253. }
  254. m_DoDownloadStatus(CATMSG_DOWNLOAD_COMPLETE, pjob);
  255. m_dwJobState = NO_BG_JOBSTATE;
  256. m_DownloadId = GUID_NULL;
  257. return S_OK;
  258. }
  259. STDMETHODIMP
  260. CAUDownloader::JobError(
  261. IBackgroundCopyJob * pjob,
  262. IBackgroundCopyError * perror
  263. )
  264. {
  265. // download encounter error
  266. m_DoDownloadStatus(CATMSG_DOWNLOAD_ERROR, pjob, perror);
  267. HRESULT hr;
  268. BG_ERROR_CONTEXT bgEContext;
  269. if (SUCCEEDED(perror->GetError(&bgEContext, &hr)))
  270. {
  271. DEBUGMSG("WUAUNEG JobError callback Context = %d, hr = 0x%x",bgEContext, hr);
  272. }
  273. Reset();
  274. m_FinishReason = JOB_ERROR;
  275. return S_OK;
  276. }
  277. STDMETHODIMP
  278. CAUDownloader::JobModification(
  279. IBackgroundCopyJob * pjob,
  280. DWORD /*dwReserved*/
  281. )
  282. {
  283. BG_JOB_STATE state;
  284. HRESULT hr;
  285. if (FAILED(hr= pjob->GetState(&state)))
  286. {
  287. return hr;
  288. }
  289. if (m_dwJobState == state)
  290. {
  291. goto Done;
  292. }
  293. DEBUGMSG("WUAUENG JobModification callback");
  294. switch (state)
  295. {
  296. case BG_JOB_STATE_QUEUED:
  297. DEBUGMSG("WUAUENG JobModification: Drizzle notified BG_JOB_STATE_QUEUED");
  298. ghClientHandles.ClientRemoveTrayIcon();
  299. break;
  300. case BG_JOB_STATE_TRANSFERRING:
  301. DEBUGMSG("WUAUENG JobModification: Drizzle notified BG_JOB_STATE_TRANSFERRING");
  302. m_DoDownloadStatus(CATMSG_DOWNLOAD_IN_PROGRESS, pjob);
  303. ghClientHandles.ClientAddTrayIcon();
  304. break;
  305. case BG_JOB_STATE_TRANSIENT_ERROR:
  306. {
  307. DEBUGMSG("WUAUENG JobModification: Drizzle notified BG_JOB_STATE_TRANSIENT_ERROR");
  308. m_DoDownloadStatus(CATMSG_TRANSIENT_ERROR, pjob);
  309. break;
  310. }
  311. case BG_JOB_STATE_SUSPENDED:
  312. case BG_JOB_STATE_ERROR: //What about BG_JOB_STATE_ERROR ?
  313. case BG_JOB_STATE_TRANSFERRED:
  314. case BG_JOB_STATE_ACKNOWLEDGED:
  315. case BG_JOB_STATE_CONNECTING:
  316. {
  317. DEBUGMSG("WUAUENG JobModification: Drizzle notified BG_JOB_STATE = %d", state);
  318. break;
  319. }
  320. default:
  321. {
  322. DEBUGMSG("WUAUENG Drizzle notified unexpected BG_JOB_STATE %d",state);
  323. }
  324. }
  325. m_dwJobState = state;
  326. Done:
  327. return S_OK;
  328. }
  329. HRESULT CAUDownloader::SetDrizzleNotifyInterface()
  330. {
  331. HRESULT hr ;
  332. IBackgroundCopyJob * pjob = NULL;
  333. if (FAILED(hr = FindDownloadJob(&pjob)))
  334. {
  335. DEBUGMSG("CAUDownloader::SetDrizzleNotifyInterface() got no download job with error %#lx", hr);
  336. goto done;
  337. }
  338. if (FAILED(hr = pjob->SetNotifyFlags(DRIZZLE_NOTIFY_FLAGS)))
  339. {
  340. DEBUGMSG("WUAUENG SetDrizzleNotifyInterface: set notification flags failed %#lx", hr);
  341. }
  342. else if (FAILED(hr = pjob->SetNotifyInterface(this)))
  343. {
  344. DEBUGMSG("WUAUENG SetDrizzleNotifyInterface: set notification interface failed %#lx", hr);
  345. }
  346. done:
  347. SafeRelease(pjob);
  348. return hr;
  349. }
  350. ///////////////////////////////////////////////////////////////////////////////////////
  351. // helper function to connect AU to the job got using its GUID
  352. //////////////////////////////////////////////////////////////////////////////////////
  353. HRESULT CAUDownloader::ReconnectDownloadJob()
  354. {
  355. BG_JOB_STATE state;
  356. HRESULT hr = E_FAIL;
  357. IBackgroundCopyJob * pjob = NULL;
  358. DEBUGMSG("ReconnectDownloadJob() starts");
  359. if ( (FAILED(hr = FindDownloadJob(&pjob)))
  360. || FAILED(hr = pjob->GetState(&state)))
  361. {
  362. DEBUGMSG("get no download job or fail to get job state");
  363. goto Done;
  364. }
  365. switch (state)
  366. {
  367. case BG_JOB_STATE_QUEUED:
  368. case BG_JOB_STATE_TRANSFERRING:
  369. case BG_JOB_STATE_CONNECTING:
  370. case BG_JOB_STATE_TRANSIENT_ERROR:
  371. case BG_JOB_STATE_SUSPENDED:
  372. case BG_JOB_STATE_ERROR:
  373. {
  374. DEBUGMSG("WUAUENG Trying to connect to drizzle again");
  375. if (FAILED(hr = SetDrizzleNotifyInterface()))
  376. {
  377. goto Done;
  378. }
  379. //fixcode: why need resume if error?
  380. if (BG_JOB_STATE_ERROR == state)
  381. {
  382. pjob->Resume(); //REVIEW, Is this really what we want to do?
  383. }
  384. break;
  385. }
  386. case BG_JOB_STATE_TRANSFERRED:
  387. {
  388. DEBUGMSG("WUAUENG Got BG_JOB_STATE_TRANSFERRED should work ok");
  389. if (FAILED(hr = pjob->Complete()))
  390. {
  391. goto Done;
  392. }
  393. m_DoDownloadStatus(CATMSG_DOWNLOAD_COMPLETE, pjob);
  394. break;
  395. }
  396. case BG_JOB_STATE_ACKNOWLEDGED:
  397. {
  398. //If the job was already acknowledged, we are assuming that the engine can continue
  399. DEBUGMSG("WUAUENG : Got BG_JOB_STATE_ACKNOWLEDGED should work ok");
  400. break;
  401. }
  402. case BG_JOB_STATE_CANCELLED:
  403. {
  404. DEBUGMSG("WUAUENG : Got BG_JOB_STATE_CANCELLED, should start again");
  405. goto Done;
  406. }
  407. default:
  408. {
  409. DEBUGMSG("WUAUENG Drizzle notified unexpected BG_JOB_STATE");
  410. }
  411. }
  412. hr = S_OK;
  413. m_dwJobState = state;
  414. Done:
  415. SafeRelease(pjob);
  416. if (FAILED(hr))
  417. {
  418. Reset();
  419. }
  420. DEBUGMSG("ReconnectDownloadJob() ends with result %#lx", hr);
  421. return hr;
  422. }
  423. /*****
  424. CAUDownloader::QueueDownloadFile() adds a file to download to drizzle's
  425. RETURNS:
  426. S_OK:
  427. *****/
  428. HRESULT CAUDownloader::QueueDownloadFile(LPCTSTR pszServerUrl, // full http url
  429. LPCTSTR pszLocalFile // local file name
  430. )
  431. {
  432. HRESULT hr = S_OK;
  433. DEBUGMSG("CAUDownloader::DownloadFile() starts");
  434. IBackgroundCopyJob * pjob = NULL;
  435. if (FAILED(hr = FindDownloadJob(&pjob)))
  436. {
  437. DEBUGMSG("no existing download job, create one ");
  438. if (FAILED(hr = CreateDownloadJob(&pjob)))
  439. {
  440. DEBUGMSG("fail to create a new download job");
  441. goto done;
  442. }
  443. }
  444. //fixcode: do we need to pause job first before adding files
  445. //
  446. // Add the file to the download job.
  447. //
  448. hr = pjob->AddFile( pszServerUrl, pszLocalFile);
  449. if (FAILED(hr))
  450. {
  451. DEBUGMSG(" adding file failed with %#lx", hr);
  452. goto done;
  453. }
  454. done:
  455. SafeRelease(pjob);
  456. if ( FAILED(hr) )
  457. {
  458. Reset();
  459. }
  460. return hr;
  461. }
  462. HRESULT CAUDownloader::StartDownload()
  463. {
  464. HRESULT hr = E_FAIL;
  465. IBackgroundCopyJob * pjob = NULL;
  466. if (FAILED(hr = FindDownloadJob(&pjob)))
  467. {
  468. DEBUGMSG(" fail to get download job with error %#lx", hr);
  469. goto done;
  470. }
  471. if (FAILED(hr = pjob->Resume()))
  472. {
  473. DEBUGMSG(" failed to start the download job");
  474. }
  475. done:
  476. SafeRelease(pjob);
  477. if (FAILED(hr))
  478. {
  479. Reset();
  480. }
  481. return hr;
  482. }
  483. //////////////////////////////////////////////////////////////////////////////////
  484. // cancel the job and reset CAUDownloader's state
  485. /////////////////////////////////////////////////////////////////////////////////
  486. void CAUDownloader::Reset()
  487. {
  488. IBackgroundCopyJob * pjob = NULL;
  489. if (SUCCEEDED(FindDownloadJob(&pjob)))
  490. {
  491. pjob->Cancel();
  492. pjob->Release();
  493. EngineEvents.SetEvent(IDOWNLOAD_DOWNLOAD_CANCELED);
  494. DEBUGMSG("Reset() job cancelled and released");
  495. }
  496. m_DownloadId = GUID_NULL;
  497. }
  498. HRESULT CAUDownloader::DrizzleOperation(DRIZZLEOPS dop)
  499. {
  500. HRESULT hrRet;
  501. IBackgroundCopyJob * pjob = NULL;
  502. if (FAILED(hrRet = FindDownloadJob(&pjob)))
  503. {
  504. DEBUGMSG("CAUDownloader::DrizzleOperation() on an invalid job");
  505. goto done;
  506. }
  507. switch (dop)
  508. {
  509. case DRIZZLEOPS_CANCEL:
  510. DEBUGMSG("Catalog: Canceling Drizzle Job");
  511. hrRet =pjob->Cancel();
  512. EngineEvents.SetEvent(IDOWNLOAD_DOWNLOAD_CANCELED);
  513. break;
  514. case DRIZZLEOPS_PAUSE:
  515. DEBUGMSG("Catalog: Pausing Drizzle Job");
  516. hrRet = pjob->Suspend();
  517. break;
  518. case DRIZZLEOPS_RESUME:
  519. DEBUGMSG("Catalog: Resuming Drizzle Job");
  520. hrRet = pjob->Resume();
  521. break;
  522. }
  523. done:
  524. SafeRelease(pjob);
  525. return hrRet;
  526. }
  527. /// pdwstatus actually contains the jobstate
  528. HRESULT CAUDownloader::getStatus(DWORD *pdwPercent, DWORD *pdwstatus)
  529. {
  530. BG_JOB_PROGRESS progress;
  531. BG_JOB_STATE state;
  532. HRESULT hr = S_OK;
  533. IBackgroundCopyJob * pjob = NULL;
  534. if (FAILED(hr = FindDownloadJob(&pjob)))
  535. {
  536. DEBUGMSG(" getStatus : no download job with error %#lx", hr);
  537. goto done;
  538. }
  539. if (FAILED(hr = pjob->GetState( &state )))
  540. {
  541. DEBUGMSG("WUAUENG: job->GetState failed");
  542. state = BG_JOB_STATE_QUEUED;
  543. goto done;
  544. }
  545. if (FAILED(hr = pjob->GetProgress( &progress )))
  546. {
  547. DEBUGMSG("WUAUENG: job->GetProgress failed");
  548. goto done;
  549. }
  550. if (progress.BytesTotal != BG_SIZE_UNKNOWN )
  551. {
  552. *pdwPercent = DWORD( 100 * float(progress.BytesTransferred) / float(progress.BytesTotal) );
  553. DEBUGMSG("getStatus is %d percent", *pdwPercent);
  554. }
  555. else
  556. {
  557. DEBUGMSG("getStatus, progress.BytesTotal= BG_SIZE_UNKNOWN, BytesTransfered = %d",progress.BytesTransferred);
  558. *pdwPercent = 0;
  559. }
  560. *pdwstatus = state;
  561. done:
  562. SafeRelease(pjob);
  563. return hr;
  564. }
  565. HRESULT STDMETHODCALLTYPE
  566. CAUDownloader::QueryInterface(
  567. REFIID riid,
  568. void __RPC_FAR *__RPC_FAR *ppvObject
  569. )
  570. {
  571. HRESULT hr = S_OK;
  572. *ppvObject = NULL;
  573. if (riid == __uuidof(IUnknown) ||
  574. riid == __uuidof(IBackgroundCopyCallback) )
  575. {
  576. *ppvObject = (IBackgroundCopyCallback *)this;
  577. ((IUnknown *)(*ppvObject))->AddRef();
  578. }
  579. else
  580. {
  581. hr = E_NOINTERFACE;
  582. }
  583. return hr;
  584. }
  585. ULONG STDMETHODCALLTYPE
  586. CAUDownloader::AddRef()
  587. {
  588. long cRef = InterlockedIncrement(&m_refs);
  589. DEBUGMSG("CAUDownloader AddRef = %d", cRef);
  590. return cRef;
  591. }
  592. ULONG STDMETHODCALLTYPE
  593. CAUDownloader::Release()
  594. {
  595. long cRef = InterlockedDecrement(&m_refs);
  596. DEBUGMSG("CAUDownloader Release = %d", cRef);
  597. return cRef;
  598. }