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.

1873 lines
49 KiB

  1. #include "private.h"
  2. #include "throttle.h"
  3. #include "subsmgrp.h"
  4. #include <mluisupp.h>
  5. #define TF_THISMODULE TF_THROTTLER
  6. const int MAX_AUTOCACHESIZE_ASK = 2;
  7. const int MIN_CACHE_INCREASE = 1024; // in KB
  8. // Strings for cache restrictions
  9. const TCHAR c_szKeyRestrict[] = TEXT("Software\\Policies\\Microsoft\\Internet Explorer\\Control Panel");
  10. const TCHAR c_szCache[] = TEXT("Cache");
  11. CThrottler *CThrottler::s_pThrottler = NULL;
  12. const CFactoryData CThrottler::s_ThrottlerFactoryData =
  13. {
  14. &CLSID_SubscriptionThrottler, CreateInstance, 0
  15. };
  16. #ifdef DEBUG
  17. void DUMPITEM(CHAR *pszMsg, const SUBSCRIPTIONCOOKIE *pCookie)
  18. {
  19. ISubscriptionItem *psi;
  20. if (SUCCEEDED(SubscriptionItemFromCookie(FALSE, pCookie, &psi)))
  21. {
  22. BSTR bstrName;
  23. if (SUCCEEDED(ReadBSTR(psi, c_szPropName, &bstrName)))
  24. {
  25. TraceMsgA(TF_THISMODULE, "%s: %S", pszMsg, bstrName);
  26. SysFreeString(bstrName);
  27. }
  28. psi->Release();
  29. }
  30. }
  31. #else
  32. #define DUMPITEM(pszMsg, pCookie)
  33. #endif
  34. // dwSyncFlags has 8 bits of enum (EVENTMASK) and the rest is flags
  35. inline BOOL IsSyncEvent(DWORD dwSyncFlags, DWORD dwSyncEvent)
  36. {
  37. return (dwSyncFlags & SYNCMGRFLAG_EVENTMASK) == dwSyncEvent;
  38. }
  39. inline BOOL IsSyncEventFlag(DWORD dwSyncFlags, DWORD dwSyncEvent)
  40. {
  41. return (dwSyncFlags & dwSyncEvent) != 0;
  42. }
  43. inline BOOL IsIgnoreIdleSyncEvent(DWORD dwSyncFlags)
  44. {
  45. return !IsSyncEvent(dwSyncFlags, SYNCMGRFLAG_IDLE);
  46. /*
  47. return IsSyncEvent(dwSyncFlags, SYNCMGRFLAG_CONNECT) ||
  48. IsSyncEvent(dwSyncFlags, SYNCMGRFLAG_PENDINGDISCONNECT) ||
  49. IsSyncEvent(dwSyncFlags, SYNCMGRFLAG_MANUAL) ||
  50. IsSyncEvent(dwSyncFlags, SYNCMGRFLAG_INVOKE);
  51. */
  52. }
  53. inline BOOL IsScheduleSyncEvent(DWORD dwSyncFlags)
  54. {
  55. return IsSyncEvent(dwSyncFlags, SYNCMGRFLAG_SCHEDULED) ||
  56. IsSyncEvent(dwSyncFlags, SYNCMGRFLAG_IDLE);
  57. }
  58. class CThrottlerProxy : public ISubscriptionThrottler
  59. {
  60. public:
  61. CThrottlerProxy(CThrottler *pThrottler)
  62. {
  63. m_cRef = 1;
  64. m_pThrottler = pThrottler;
  65. m_pThrottler->ExternalAddRef();
  66. }
  67. STDMETHODIMP QueryInterface(REFIID riid, void **punk)
  68. {
  69. if ((riid == IID_IUnknown) || (riid == IID_ISubscriptionThrottler))
  70. {
  71. *punk = (ISubscriptionThrottler *)this;
  72. AddRef();
  73. return S_OK;
  74. }
  75. else
  76. {
  77. *punk = NULL;
  78. return E_NOINTERFACE;
  79. }
  80. }
  81. STDMETHODIMP_(ULONG) AddRef()
  82. {
  83. return ++m_cRef;
  84. }
  85. STDMETHODIMP_(ULONG) Release()
  86. {
  87. if (--m_cRef == 0)
  88. {
  89. delete this;
  90. return 0;
  91. }
  92. return m_cRef;
  93. }
  94. STDMETHODIMP GetSubscriptionRunState(
  95. /* [in] */ DWORD dwNumCookies,
  96. /* [size_is][in] */ const SUBSCRIPTIONCOOKIE *pCookies,
  97. /* [size_is][out] */ DWORD *pdwRunState)
  98. {
  99. return m_pThrottler->GetSubscriptionRunState(dwNumCookies, pCookies, pdwRunState);
  100. }
  101. STDMETHODIMP AbortItems(
  102. /* [in] */ DWORD dwNumCookies,
  103. /* [size_is][in] */ const SUBSCRIPTIONCOOKIE *pCookies)
  104. {
  105. return m_pThrottler->AbortItems(dwNumCookies, pCookies);
  106. }
  107. STDMETHODIMP AbortAll()
  108. {
  109. return m_pThrottler->AbortAll();
  110. }
  111. private:
  112. ULONG m_cRef;
  113. CThrottler *m_pThrottler;
  114. ~CThrottlerProxy()
  115. {
  116. m_pThrottler->ExternalRelease();
  117. }
  118. };
  119. HRESULT CThrottler::CreateInstance(IUnknown *punkOuter, IUnknown **ppunk)
  120. {
  121. HRESULT hr;
  122. ASSERT(NULL == punkOuter);
  123. ASSERT(NULL != ppunk);
  124. if (NULL != CThrottler::s_pThrottler)
  125. {
  126. *ppunk = new CThrottlerProxy(CThrottler::s_pThrottler);
  127. if (NULL != *ppunk)
  128. {
  129. hr = S_OK;
  130. }
  131. else
  132. {
  133. hr = E_OUTOFMEMORY;
  134. }
  135. }
  136. else
  137. {
  138. TraceMsg(TF_ALWAYS, "UNEXPECTED ERROR: Failed to attached to throttler in CreateInstance");
  139. hr = E_UNEXPECTED;
  140. }
  141. return hr;
  142. }
  143. CThrottler::CThrottler()
  144. {
  145. ASSERT(NULL == s_pThrottler);
  146. ASSERT(NULL == m_pItemsHead);
  147. ASSERT(NULL == m_pItemsTail);
  148. ASSERT(NULL == m_updateQueue[0]);
  149. // APPCOMPAT - this is only until msidle is multi-client aware.
  150. IdleEnd();
  151. //m_fUserIsIdle = TRUE; // TODO: need to determine this better
  152. IdleBegin(NULL);
  153. m_cRef = 1;
  154. }
  155. CThrottler::~CThrottler()
  156. {
  157. DBG("Destroying Throttler");
  158. ASSERT(GetCurrentThreadId() == m_dwThreadId);
  159. IdleEnd();
  160. // Destroy window
  161. if (m_hwndThrottler)
  162. {
  163. SetWindowLongPtr(m_hwndThrottler, GWLP_USERDATA, 0);
  164. DestroyWindow(m_hwndThrottler);
  165. m_hwndThrottler = NULL;
  166. }
  167. s_pThrottler = NULL;
  168. RevokeClassObject();
  169. }
  170. HRESULT CThrottler::RevokeClassObject()
  171. {
  172. HRESULT hr;
  173. if (m_dwRegister)
  174. {
  175. hr = CoRevokeClassObject(m_dwRegister);
  176. m_dwRegister = 0;
  177. }
  178. else
  179. {
  180. hr = S_FALSE;
  181. }
  182. return hr;
  183. }
  184. HRESULT /* static */ CThrottler::GetThrottler(CThrottler **ppThrottler)
  185. {
  186. HRESULT hr = S_OK;
  187. ASSERT(NULL != ppThrottler);
  188. if (NULL != ppThrottler)
  189. {
  190. *ppThrottler = NULL;
  191. // If there is no throttler create a new one
  192. if (NULL == s_pThrottler)
  193. {
  194. DBG("Creating new throttler in GetThrottler");
  195. s_pThrottler = new CThrottler;
  196. if (NULL != s_pThrottler)
  197. {
  198. IClassFactory *pcf = new CClassFactory(&s_ThrottlerFactoryData);
  199. if (NULL != pcf)
  200. {
  201. HRESULT hrRegister = CoRegisterClassObject(CLSID_SubscriptionThrottler,
  202. pcf,
  203. CLSCTX_LOCAL_SERVER,
  204. REGCLS_MULTIPLEUSE,
  205. &s_pThrottler->m_dwRegister);
  206. if (FAILED(hrRegister))
  207. {
  208. TraceMsg(TF_ALWAYS, "CoRegisterClassObject failed - other processes can't talk to us!");
  209. }
  210. pcf->Release();
  211. }
  212. *ppThrottler = s_pThrottler;
  213. #ifdef DEBUG
  214. s_pThrottler->m_dwThreadId = GetCurrentThreadId();
  215. #endif
  216. }
  217. else
  218. {
  219. DBG("Failed to create Throttler class factory");
  220. hr = E_OUTOFMEMORY;
  221. }
  222. }
  223. else
  224. {
  225. // Attach to existing throttler
  226. ASSERT(GetCurrentThreadId() == s_pThrottler->m_dwThreadId);
  227. s_pThrottler->AddRef();
  228. *ppThrottler = s_pThrottler;
  229. }
  230. }
  231. else
  232. {
  233. hr = E_INVALIDARG;
  234. }
  235. return hr;
  236. }
  237. void CThrottler::OnIdleStateChange(DWORD dwState)
  238. {
  239. if (NULL != s_pThrottler)
  240. {
  241. switch(dwState)
  242. {
  243. case STATE_USER_IDLE_BEGIN:
  244. DBG("OnIdleStateChange: Idle Begin");
  245. #ifdef DEBUG
  246. LogEvent(TEXT("Idle state begins"));
  247. #endif
  248. s_pThrottler->OnIdleBegin();
  249. break;
  250. case STATE_USER_IDLE_END:
  251. DBG("OnIdleStateChange: Idle End");
  252. #ifdef DEBUG
  253. LogEvent(TEXT("Idle state ends"));
  254. #endif
  255. s_pThrottler->OnIdleEnd();
  256. break;
  257. }
  258. }
  259. }
  260. void CThrottler::OnIdleBegin()
  261. {
  262. ASSERT(GetCurrentThreadId() == m_dwThreadId);
  263. m_fUserIsIdle = TRUE;
  264. FillTheQueue();
  265. }
  266. void CThrottler::OnIdleEnd()
  267. {
  268. ASSERT(GetCurrentThreadId() == m_dwThreadId);
  269. m_fUserIsIdle = FALSE;
  270. for (int i = 0; i < ARRAYSIZE(m_updateQueue); i++)
  271. {
  272. if ((NULL != m_updateQueue[i]) &&
  273. (m_updateQueue[i]->m_dwRunState & RS_SUSPENDONIDLE))
  274. {
  275. DUMPITEM("Suspending in CThrottler::OnIdleEnd", &m_updateQueue[i]->m_cookie);
  276. ISubscriptionAgentControl *pSubsAgentCtl;
  277. CUpdateItem *pUpdateItem = m_updateQueue[i];
  278. pSubsAgentCtl = m_updateQueue[i]->m_pSubsAgentCtl;
  279. m_updateQueue[i]->m_dwRunState &= ~RS_UPDATING;
  280. m_updateQueue[i]->m_dwRunState |= RS_SUSPENDED;
  281. m_updateQueue[i] = NULL;
  282. ASSERT(NULL != pSubsAgentCtl);
  283. if (SUCCEEDED(pSubsAgentCtl->PauseUpdate(0)))
  284. {
  285. WCHAR wszMsg[256];
  286. MLLoadStringW(IDS_UPDATE_PAUSED, wszMsg, ARRAYSIZE(wszMsg));
  287. NotifyHandlers(NH_UPDATEPROGRESS, &pUpdateItem->m_cookie, -1,
  288. -1, -1, WC_INTERNAL_S_PAUSED, wszMsg);
  289. }
  290. }
  291. }
  292. FillTheQueue();
  293. }
  294. STDMETHODIMP CThrottler::QueryInterface(REFIID riid, void **ppv)
  295. {
  296. ASSERT(GetCurrentThreadId() == m_dwThreadId);
  297. if (NULL == ppv)
  298. {
  299. return E_INVALIDARG;
  300. }
  301. if ((IID_IUnknown == riid) || (IID_ISubscriptionAgentEvents == riid))
  302. {
  303. *ppv = (ISubscriptionAgentEvents *)this;
  304. }
  305. else if (IID_ISubscriptionThrottler == riid)
  306. {
  307. *ppv = (ISubscriptionThrottler *)this;
  308. }
  309. else
  310. {
  311. *ppv = NULL;
  312. return E_NOINTERFACE;
  313. }
  314. AddRef();
  315. return S_OK;
  316. }
  317. ULONG CThrottler::ExternalAddRef()
  318. {
  319. AddRef();
  320. return ++m_cExternalRef;
  321. }
  322. ULONG CThrottler::ExternalRelease()
  323. {
  324. ULONG cRef = --m_cExternalRef;
  325. Release();
  326. return cRef;
  327. }
  328. STDMETHODIMP_(ULONG) CThrottler::AddRef()
  329. {
  330. ASSERT(GetCurrentThreadId() == m_dwThreadId);
  331. return ++m_cRef;
  332. }
  333. STDMETHODIMP_(ULONG) CThrottler::Release()
  334. {
  335. ASSERT(GetCurrentThreadId() == m_dwThreadId);
  336. if (--m_cRef == 0)
  337. {
  338. delete this;
  339. return 0;
  340. }
  341. return m_cRef;
  342. }
  343. HRESULT CThrottler::NotifyHandlers(int idCmd, const SUBSCRIPTIONCOOKIE *pSubscriptionCookie, ...)
  344. {
  345. ASSERT(GetCurrentThreadId() == m_dwThreadId);
  346. HRESULT hr = S_OK;
  347. va_list va;
  348. long lSizeDownloaded = -1;
  349. long lProgressCurrent = -1;
  350. long lProgressMax = -1;
  351. HRESULT hrParam = E_UNEXPECTED;
  352. LPCWSTR wszParam = NULL;
  353. va_start(va, pSubscriptionCookie);
  354. // First extract args
  355. switch (idCmd)
  356. {
  357. case NH_UPDATEBEGIN:
  358. // Nothing to do for now
  359. break;
  360. case NH_UPDATEPROGRESS:
  361. lSizeDownloaded = va_arg(va, long);
  362. lProgressCurrent = va_arg(va, long);
  363. lProgressMax = va_arg(va, long);
  364. hrParam = va_arg(va, HRESULT);
  365. wszParam = va_arg(va, LPCWSTR);
  366. break;
  367. case NH_UPDATEEND:
  368. lSizeDownloaded = va_arg(va, long);
  369. hrParam = va_arg(va, HRESULT);
  370. wszParam = va_arg(va, LPCWSTR);
  371. break;
  372. case NH_REPORTERROR:
  373. hrParam = va_arg(va, HRESULT);
  374. wszParam = va_arg(va, LPCWSTR);
  375. break;
  376. default:
  377. ASSERT(0); // Don't know what to do
  378. hr = E_UNEXPECTED;
  379. break;
  380. }
  381. // Now loop
  382. HRESULT hrTemp = S_OK;
  383. CSyncMgrNode *pSyncMgrNode = m_pSyncMgrs;
  384. while (pSyncMgrNode)
  385. {
  386. COfflineSync *pOfflineSync = pSyncMgrNode->m_pOfflineSync;
  387. pSyncMgrNode = pSyncMgrNode->m_pNext;
  388. switch (idCmd)
  389. {
  390. case NH_UPDATEBEGIN:
  391. hrTemp = pOfflineSync->UpdateBegin(pSubscriptionCookie);
  392. break;
  393. case NH_UPDATEPROGRESS:
  394. hrTemp = pOfflineSync->UpdateProgress(pSubscriptionCookie,
  395. lSizeDownloaded,
  396. lProgressCurrent,
  397. lProgressMax,
  398. hrParam,
  399. wszParam);
  400. break;
  401. case NH_UPDATEEND:
  402. hrTemp = pOfflineSync->UpdateEnd(pSubscriptionCookie,
  403. lSizeDownloaded,
  404. hrParam,
  405. wszParam);
  406. break;
  407. case NH_REPORTERROR:
  408. hrTemp = pOfflineSync->ReportError(pSubscriptionCookie,
  409. hrParam,
  410. wszParam);
  411. break;
  412. }
  413. if (FAILED(hrTemp))
  414. {
  415. hr = hrTemp;
  416. }
  417. }
  418. va_end(va);
  419. return hr;
  420. }
  421. // ISubscriptionAgentEvents members
  422. STDMETHODIMP CThrottler::UpdateBegin(const SUBSCRIPTIONCOOKIE *pSubscriptionCookie)
  423. {
  424. ASSERT(GetCurrentThreadId() == m_dwThreadId);
  425. HRESULT hr;
  426. CUpdateItem *pUpdateItem;
  427. hr = FindCookie(pSubscriptionCookie, &pUpdateItem);
  428. ASSERT(SUCCEEDED(hr));
  429. if (SUCCEEDED(hr))
  430. {
  431. DUMPITEM("CThrottler::UpdateBegin", pSubscriptionCookie);
  432. pUpdateItem->m_dwRunState &= ~(RS_READY | RS_SUSPENDED);
  433. pUpdateItem->m_dwRunState |= RS_UPDATING;
  434. hr = NotifyHandlers(NH_UPDATEBEGIN, pSubscriptionCookie);
  435. }
  436. return hr;
  437. }
  438. STDMETHODIMP CThrottler::UpdateProgress(
  439. const SUBSCRIPTIONCOOKIE *pSubscriptionCookie,
  440. long lSizeDownloaded,
  441. long lProgressCurrent,
  442. long lProgressMax,
  443. HRESULT hrStatus,
  444. LPCWSTR wszStatus)
  445. {
  446. ASSERT(GetCurrentThreadId() == m_dwThreadId);
  447. HRESULT hr;
  448. CUpdateItem *pUpdateItem;
  449. // TODO:
  450. // Adjust the max to fool syncmgr
  451. if (SUCCEEDED(FindCookie(pSubscriptionCookie, &pUpdateItem)))
  452. {
  453. if ((lProgressMax < 0) || (lProgressMax <= lProgressCurrent))
  454. {
  455. if (pUpdateItem->m_nMax <= lProgressCurrent)
  456. {
  457. pUpdateItem->m_nMax = (lProgressCurrent * 3) / 2;
  458. }
  459. lProgressMax = pUpdateItem->m_nMax;
  460. }
  461. }
  462. hr = NotifyHandlers(NH_UPDATEPROGRESS, pSubscriptionCookie, lSizeDownloaded,
  463. lProgressCurrent, lProgressMax, hrStatus, wszStatus);
  464. return hr;
  465. }
  466. STDMETHODIMP CThrottler::UpdateEnd(
  467. const SUBSCRIPTIONCOOKIE *pSubscriptionCookie,
  468. long lSizeDownloaded,
  469. HRESULT hrResult,
  470. LPCWSTR wszResult)
  471. {
  472. ASSERT(GetCurrentThreadId() == m_dwThreadId);
  473. HRESULT hr;
  474. CUpdateItem *pUpdateItem;
  475. SUBSCRIPTIONCOOKIE cookie = *pSubscriptionCookie;
  476. hr = FindCookie(pSubscriptionCookie, &pUpdateItem);
  477. if (SUCCEEDED(hr))
  478. {
  479. DUMPITEM("CThrottler::UpdateEnd", pSubscriptionCookie);
  480. pUpdateItem->m_dwRunState &= ~(RS_READY | RS_SUSPENDED | RS_UPDATING | RS_SUSPENDONIDLE);
  481. pUpdateItem->m_dwRunState |= RS_COMPLETED;
  482. RemoveItemFromList(pUpdateItem, TRUE);
  483. // ************************************************************************
  484. // Don't use anything that could have come from pUpdateItem after this
  485. // including the pSubscriptionCookie above which came from an agent which
  486. // probably no longer exists!
  487. // (actually, the agent keeps itself alive until this call returns)
  488. // ************************************************************************
  489. }
  490. hr = NotifyHandlers(NH_UPDATEEND, &cookie,
  491. lSizeDownloaded, hrResult, wszResult);
  492. FireSubscriptionEvent(SUBSNOTF_SYNC_STOP, &cookie);
  493. FillTheQueue();
  494. return hr;
  495. }
  496. STDMETHODIMP CThrottler::ReportError(
  497. const SUBSCRIPTIONCOOKIE *pSubscriptionCookie,
  498. HRESULT hrError,
  499. LPCWSTR wszError)
  500. {
  501. ASSERT(GetCurrentThreadId() == m_dwThreadId);
  502. HRESULT hr;
  503. if (INET_E_AGENT_EXCEEDING_CACHE_SIZE == hrError)
  504. {
  505. // Agent is notifying us that they're about to exceed the cache size.
  506. hr = AutoCacheSizeRequest(pSubscriptionCookie);
  507. }
  508. else
  509. hr = NotifyHandlers(NH_REPORTERROR, pSubscriptionCookie, hrError, wszError);
  510. return hr;
  511. }
  512. STDMETHODIMP CThrottler::GetSubscriptionRunState(
  513. /* [in] */ DWORD dwNumCookies,
  514. /* [size_is][in] */ const SUBSCRIPTIONCOOKIE *pCookies,
  515. /* [size_is][out] */ DWORD *pdwRunState)
  516. {
  517. ASSERT(GetCurrentThreadId() == m_dwThreadId);
  518. if ((0 == dwNumCookies) ||
  519. (NULL == pCookies) ||
  520. (NULL == pdwRunState))
  521. {
  522. return E_INVALIDARG;
  523. }
  524. for (DWORD i = 0; i < dwNumCookies; i++, pCookies++, pdwRunState++)
  525. {
  526. CUpdateItem *pUpdateItem;
  527. if (SUCCEEDED(FindCookie(pCookies, &pUpdateItem)))
  528. {
  529. *pdwRunState = pUpdateItem->m_dwRunState;
  530. }
  531. else
  532. {
  533. *pdwRunState = 0;
  534. }
  535. }
  536. return S_OK;
  537. }
  538. // DoAbortItem will cause the CThrottler to get released if the last running
  539. // agent is aborted (Agent notifies it's done, SyncMgr releases throttler,
  540. // then agent releases throttler)
  541. HRESULT CThrottler::DoAbortItem(CUpdateItem *pUpdateItem)
  542. {
  543. ASSERT(GetCurrentThreadId() == m_dwThreadId);
  544. HRESULT hr;
  545. ASSERT(((pUpdateItem->m_dwRunState & (RS_UPDATING | RS_SUSPENDED)) &&
  546. (NULL != pUpdateItem->m_pSubsAgentCtl))
  547. ||
  548. (NULL == pUpdateItem->m_pSubsAgentCtl));
  549. if ((pUpdateItem->m_dwRunState & (RS_UPDATING | RS_SUSPENDED)) &&
  550. (NULL != pUpdateItem->m_pSubsAgentCtl))
  551. {
  552. DUMPITEM("CThrottler::DoAbortItem with existing Agent", &pUpdateItem->m_cookie);
  553. hr = pUpdateItem->m_pSubsAgentCtl->AbortUpdate(0);
  554. }
  555. else
  556. {
  557. WCHAR wszMsg[256];
  558. MLLoadStringW(IDS_STATUS_ABORTED, wszMsg, ARRAYSIZE(wszMsg));
  559. DUMPITEM("CThrottler::DoAbortItem with no Agent", &pUpdateItem->m_cookie);
  560. ReportThrottlerError(&pUpdateItem->m_cookie, E_ABORT, wszMsg);
  561. hr = UpdateEnd(&pUpdateItem->m_cookie, 0, E_ABORT, wszMsg);
  562. }
  563. return hr;
  564. }
  565. STDMETHODIMP CThrottler::AbortItems(
  566. /* [in] */ DWORD dwNumCookies,
  567. /* [size_is][in] */ const SUBSCRIPTIONCOOKIE *pCookies)
  568. {
  569. ASSERT(GetCurrentThreadId() == m_dwThreadId);
  570. HRESULT hr;
  571. if ((0 == dwNumCookies) ||
  572. (NULL == pCookies))
  573. {
  574. return E_INVALIDARG;
  575. }
  576. if (FAILED(CreateThrottlerWnd()))
  577. return E_FAIL;
  578. hr = S_OK;
  579. void *pItem = MemAlloc(LMEM_FIXED, dwNumCookies * sizeof(SUBSCRIPTIONCOOKIE));
  580. if (pItem)
  581. {
  582. #ifdef DEBUG
  583. for (DWORD i = 0; i < dwNumCookies; i++)
  584. {
  585. DUMPITEM("Aborting in CThrottler::AbortItems", &pCookies[i]);
  586. }
  587. #endif
  588. memcpy(pItem, pCookies, dwNumCookies * sizeof(SUBSCRIPTIONCOOKIE));
  589. PostMessage(m_hwndThrottler, WM_THROTTLER_ABORTITEM, (WPARAM)dwNumCookies, (LPARAM)pItem);
  590. }
  591. else
  592. {
  593. DBG_WARN("Memory alloc failed in CThrottler::AbortItems");
  594. hr = S_FALSE;
  595. }
  596. return hr;
  597. }
  598. STDMETHODIMP CThrottler::ActuallyAbortItems(
  599. /* [in] */ DWORD dwNumCookies,
  600. /* [size_is][in] */ const SUBSCRIPTIONCOOKIE *pCookies)
  601. {
  602. ASSERT(GetCurrentThreadId() == m_dwThreadId);
  603. HRESULT hr;
  604. if ((0 == dwNumCookies) ||
  605. (NULL == pCookies))
  606. {
  607. return E_INVALIDARG;
  608. }
  609. hr = S_OK;
  610. // DoAbortItem will cause the CThrottler to get released if the last
  611. // running agent is aborted. Protect against that.
  612. AddRef();
  613. for (DWORD i = 0; i < dwNumCookies; i++, pCookies++)
  614. {
  615. HRESULT hrItem;
  616. CUpdateItem *pUpdateItem;
  617. hrItem = FindCookie(pCookies, &pUpdateItem);
  618. if (SUCCEEDED(hrItem))
  619. {
  620. hrItem = DoAbortItem(pUpdateItem);
  621. // ************************************************************************
  622. // pUpdateItem is no longer valid!
  623. // ************************************************************************
  624. }
  625. if (FAILED(hrItem))
  626. {
  627. hr = S_FALSE;
  628. }
  629. }
  630. Release();
  631. return hr;
  632. }
  633. HRESULT CThrottler::CreateThrottlerWnd()
  634. {
  635. ASSERT(GetCurrentThreadId() == m_dwThreadId);
  636. if (!m_hwndThrottler)
  637. {
  638. WNDCLASS wc;
  639. wc.style = 0;
  640. wc.lpfnWndProc = CThrottler::ThrottlerWndProc;
  641. wc.cbClsExtra = 0;
  642. wc.cbWndExtra = 0;
  643. wc.hInstance = g_hInst;
  644. wc.hIcon = NULL;
  645. wc.hCursor = NULL;
  646. wc.hbrBackground = (HBRUSH)NULL;
  647. wc.lpszMenuName = NULL;
  648. wc.lpszClassName = THROTTLER_WNDCLASS;
  649. RegisterClass(&wc);
  650. m_hwndThrottler = CreateWindow(THROTTLER_WNDCLASS, TEXT("YO"), WS_OVERLAPPED,
  651. CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,
  652. NULL, NULL, g_hInst, (LPVOID)this);
  653. if (NULL == m_hwndThrottler)
  654. {
  655. DBG_WARN("CThrottler CreateWindow failed");
  656. return E_FAIL;
  657. }
  658. }
  659. return S_OK;
  660. }
  661. LRESULT CThrottler::ThrottlerWndProc(HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam)
  662. {
  663. CThrottler *pThis = (CThrottler*) GetWindowLongPtr(hWnd, GWLP_USERDATA);
  664. // Validate pThis
  665. #ifdef DEBUG
  666. if (pThis && IsBadWritePtr(pThis, sizeof(*pThis)))
  667. {
  668. TraceMsg(TF_THISMODULE|TF_WARNING,
  669. "Invalid 'this' in ThrottlerWndProc (0x%08x) - already destroyed?", pThis);
  670. }
  671. if (pThis)
  672. {
  673. ASSERT(GetCurrentThreadId() == pThis->m_dwThreadId);
  674. ASSERT(GetCurrentThreadId() == GetWindowThreadProcessId(hWnd, NULL));
  675. }
  676. #endif
  677. switch (Msg)
  678. {
  679. case WM_CREATE :
  680. {
  681. LPCREATESTRUCT pcs = (LPCREATESTRUCT)lParam;
  682. if (!pcs || !(pcs->lpCreateParams))
  683. {
  684. DBG_WARN("Invalid param ThrottlerWndProc Create");
  685. return -1;
  686. }
  687. SetWindowLongPtr(hWnd, GWLP_USERDATA, (LONG_PTR) pcs->lpCreateParams);
  688. return 0;
  689. }
  690. case WM_THROTTLER_ABORTALL:
  691. if (pThis)
  692. pThis->ActuallyAbortAll();
  693. break;
  694. case WM_THROTTLER_ABORTITEM:
  695. if (pThis)
  696. pThis->ActuallyAbortItems((ULONG) wParam, (SUBSCRIPTIONCOOKIE*) lParam);
  697. MemFree((HLOCAL)lParam);
  698. break;
  699. case WM_THROTTLER_AUTOCACHESIZE_ASK:
  700. if (pThis)
  701. pThis->AutoCacheSizeAskUser((DWORD)lParam);
  702. break;
  703. default:
  704. return DefWindowProc(hWnd, Msg, wParam, lParam);
  705. }
  706. return 0;
  707. }
  708. STDMETHODIMP CThrottler::AbortAll()
  709. {
  710. ASSERT(GetCurrentThreadId() == m_dwThreadId);
  711. if (FAILED(CreateThrottlerWnd()))
  712. return E_FAIL;
  713. DBG("Aborting all items");
  714. PostMessage(m_hwndThrottler, WM_THROTTLER_ABORTALL, 0, 0);
  715. return S_OK;
  716. }
  717. STDMETHODIMP CThrottler::ActuallyAbortAll()
  718. {
  719. ASSERT(GetCurrentThreadId() == m_dwThreadId);
  720. HRESULT hr = S_OK;
  721. CUpdateItem *pItem = m_pItemsHead;
  722. if (FALSE == m_fAbortingAll)
  723. {
  724. m_fAbortingAll = TRUE;
  725. while (pItem)
  726. {
  727. CUpdateItem *pUpdateItem = pItem;
  728. // Move forward now since this item should get yanked!
  729. pItem = pItem->m_pNext;
  730. if (FAILED(DoAbortItem(pUpdateItem)))
  731. {
  732. hr = S_FALSE;
  733. }
  734. }
  735. m_fAbortingAll = FALSE;
  736. }
  737. else
  738. {
  739. hr = S_FALSE;
  740. }
  741. return hr;
  742. }
  743. HRESULT CThrottler::Advise(COfflineSync *pOfflineSync)
  744. {
  745. ASSERT(GetCurrentThreadId() == m_dwThreadId);
  746. HRESULT hr;
  747. CSyncMgrNode *pSyncMgrNode;
  748. ASSERT(NULL != pOfflineSync);
  749. #ifdef DEBUG
  750. pSyncMgrNode = m_pSyncMgrs;
  751. while (pSyncMgrNode)
  752. {
  753. if (pSyncMgrNode->m_pOfflineSync == pOfflineSync)
  754. {
  755. ASSERT(0); // Shouldn't advise more than once!
  756. }
  757. pSyncMgrNode = pSyncMgrNode->m_pNext;
  758. }
  759. #endif
  760. ASSERT(!m_hwndParent || (m_hwndParent == pOfflineSync->GetParentWindow()));
  761. m_hwndParent = pOfflineSync->GetParentWindow();
  762. pSyncMgrNode = new CSyncMgrNode(pOfflineSync, m_pSyncMgrs);
  763. if (NULL != pSyncMgrNode)
  764. {
  765. pOfflineSync->AddRef();
  766. m_pSyncMgrs = pSyncMgrNode;
  767. hr = S_OK;
  768. }
  769. else
  770. {
  771. hr = E_OUTOFMEMORY;
  772. }
  773. return hr;
  774. }
  775. HRESULT CThrottler::Unadvise(COfflineSync *pOfflineSync)
  776. {
  777. ASSERT(GetCurrentThreadId() == m_dwThreadId);
  778. HRESULT hr = E_FAIL;
  779. CSyncMgrNode *pSyncMgrNode;
  780. CSyncMgrNode **ppSyncMgrPrev;
  781. ASSERT(NULL != pOfflineSync);
  782. pSyncMgrNode = m_pSyncMgrs;
  783. ppSyncMgrPrev = &m_pSyncMgrs;
  784. while (pSyncMgrNode)
  785. {
  786. if (pSyncMgrNode->m_pOfflineSync == pOfflineSync)
  787. {
  788. *ppSyncMgrPrev = pSyncMgrNode->m_pNext;
  789. delete pSyncMgrNode;
  790. hr = S_OK;
  791. break;
  792. }
  793. ppSyncMgrPrev = &pSyncMgrNode->m_pNext;
  794. pSyncMgrNode = pSyncMgrNode->m_pNext;
  795. }
  796. ASSERT(SUCCEEDED(hr)); // This is internal goo so should not fail!
  797. if (NULL == m_pSyncMgrs)
  798. {
  799. // Everyone has lost interest in us...
  800. RevokeClassObject();
  801. s_pThrottler = NULL;
  802. while (m_cExternalRef > 0)
  803. {
  804. TraceMsg(TF_WARNING, "CThrottle::Unadvise m_cExternalRef = %d", m_cExternalRef);
  805. MSG msg;
  806. if (PeekMessage(&msg, NULL, 0, 0, TRUE))
  807. {
  808. DispatchMessage(&msg);
  809. }
  810. }
  811. }
  812. return hr;
  813. }
  814. int CThrottler::GetCookieIndexInQueue(const SUBSCRIPTIONCOOKIE *pCookie)
  815. {
  816. ASSERT(GetCurrentThreadId() == m_dwThreadId);
  817. int index = -1;
  818. for (int i = 0; i < ARRAYSIZE(m_updateQueue); i++)
  819. {
  820. if ((NULL != m_updateQueue[i]) && (m_updateQueue[i]->m_cookie == *pCookie))
  821. {
  822. index = i;
  823. break;
  824. }
  825. }
  826. return index;
  827. }
  828. void CThrottler::FailedUpdate(HRESULT hr, const SUBSCRIPTIONCOOKIE *pCookie)
  829. {
  830. ASSERT(GetCurrentThreadId() == m_dwThreadId);
  831. WCHAR wszMsg[256];
  832. int resID;
  833. switch (hr)
  834. {
  835. case INET_E_SCHEDULED_UPDATES_DISABLED:
  836. resID = IDS_SCHEDULED_UPDATES_DISABLED;
  837. break;
  838. case INET_E_SCHEDULED_UPDATES_RESTRICTED:
  839. resID = IDS_SCHEDULED_UPDATES_RESTRICTED;
  840. break;
  841. case INET_E_SCHEDULED_UPDATE_INTERVAL:
  842. resID = IDS_SCHEDULED_UPDATE_INTERVAL;
  843. break;
  844. case INET_E_SCHEDULED_EXCLUDE_RANGE:
  845. resID = IDS_SCHEDULED_EXCLUDE_RANGE;
  846. break;
  847. default:
  848. resID = IDS_CRAWL_STATUS_NOT_OK;
  849. break;
  850. }
  851. MLLoadStringW(resID, wszMsg, ARRAYSIZE(wszMsg));
  852. ReportThrottlerError(pCookie, hr, wszMsg);
  853. UpdateEnd(pCookie, 0, hr, wszMsg);
  854. }
  855. void CThrottler::RunItem(int queueSlot, CUpdateItem *pUpdateItem)
  856. {
  857. ASSERT(GetCurrentThreadId() == m_dwThreadId);
  858. HRESULT hr;
  859. ASSERT(NULL == m_updateQueue[queueSlot]);
  860. m_updateQueue[queueSlot] = pUpdateItem;
  861. if (pUpdateItem->m_dwRunState & RS_SUSPENDED)
  862. {
  863. DUMPITEM("Resuming suspended item in CThrottler::RunItem", &pUpdateItem->m_cookie);
  864. ASSERT(NULL != pUpdateItem->m_pSubsAgentCtl);
  865. pUpdateItem->m_dwRunState |= RS_UPDATING;
  866. pUpdateItem->m_dwRunState &= ~RS_SUSPENDED;
  867. hr = pUpdateItem->m_pSubsAgentCtl->ResumeUpdate(0);
  868. if (SUCCEEDED(hr))
  869. {
  870. WCHAR wszMsg[256];
  871. MLLoadStringW(IDS_UPDATE_RESUMING, wszMsg, ARRAYSIZE(wszMsg));
  872. NotifyHandlers(NH_UPDATEPROGRESS, &pUpdateItem->m_cookie, -1,
  873. -1, -1, WC_INTERNAL_S_RESUMING, wszMsg);
  874. }
  875. }
  876. else
  877. {
  878. ISubscriptionItem *psi;
  879. hr = SubscriptionItemFromCookie(FALSE, &pUpdateItem->m_cookie, &psi);
  880. if (SUCCEEDED(hr))
  881. {
  882. SUBSCRIPTIONITEMINFO sii;
  883. sii.cbSize = sizeof(SUBSCRIPTIONITEMINFO);
  884. hr = psi->GetSubscriptionItemInfo(&sii);
  885. if (SUCCEEDED(hr))
  886. {
  887. hr = CoCreateInstance(sii.clsidAgent,
  888. NULL,
  889. CLSCTX_INPROC_SERVER,
  890. IID_ISubscriptionAgentControl,
  891. (void**)&pUpdateItem->m_pSubsAgentCtl);
  892. if (SUCCEEDED(hr))
  893. {
  894. DUMPITEM("Running item in CThrottler::RunItem", &pUpdateItem->m_cookie);
  895. hr = pUpdateItem->m_pSubsAgentCtl->StartUpdate(psi,
  896. (ISubscriptionAgentEvents *)this);
  897. FireSubscriptionEvent(SUBSNOTF_SYNC_START, &pUpdateItem->m_cookie);
  898. }
  899. else
  900. {
  901. DBG_WARN("CoCreate Agent FAILED in CThrottler::RunItem");
  902. }
  903. }
  904. psi->Release();
  905. }
  906. else
  907. {
  908. DBG_WARN("SubscriptionItemFromCookie FAILED in CThrottler::RunItem");
  909. }
  910. }
  911. if (FAILED(hr))
  912. {
  913. FailedUpdate(hr, &pUpdateItem->m_cookie);
  914. }
  915. }
  916. int CThrottler::GetFreeQueueSlot()
  917. {
  918. ASSERT(GetCurrentThreadId() == m_dwThreadId);
  919. int index = -1;
  920. for (int i = 0; i < ARRAYSIZE(m_updateQueue); i++)
  921. {
  922. if (NULL == m_updateQueue[i])
  923. {
  924. index = i;
  925. break;
  926. }
  927. }
  928. return index;
  929. }
  930. void CThrottler::FillTheQueue()
  931. {
  932. ASSERT(GetCurrentThreadId() == m_dwThreadId);
  933. if ((FALSE == m_fFillingTheQueue) && // avoid re-entrancy
  934. (FALSE == m_fAbortingAll) && // avoid re-entrancy
  935. (FALSE == m_fAutoCacheSizePending)) // we have a dialog up for the user
  936. {
  937. m_fFillingTheQueue = TRUE;
  938. CUpdateItem *pNextItem = m_pItemsHead;
  939. CUpdateItem *pItem;
  940. while (NULL != pNextItem)
  941. {
  942. pItem = pNextItem;
  943. // Move ahead since this item may not be here
  944. // if we run it and it fails
  945. pNextItem = pNextItem->m_pNext;
  946. if (!(pItem->m_dwRunState & (RS_COMPLETED | RS_UPDATING)))
  947. {
  948. int freeSlot = GetFreeQueueSlot();
  949. if ((freeSlot >= 0) &&
  950. (m_fUserIsIdle || (!(pItem->m_dwRunState & RS_SUSPENDONIDLE))))
  951. {
  952. RunItem(freeSlot, pItem);
  953. }
  954. else
  955. {
  956. // If we didn't run it then let's make sure the UI reflects the current
  957. // state properly
  958. HRESULT hrStatus;
  959. WCHAR wszMsg[256];
  960. if ((pItem->m_dwRunState & RS_SUSPENDONIDLE) && (!m_fUserIsIdle))
  961. {
  962. MLLoadStringW(IDS_UPDATE_PAUSED, wszMsg, ARRAYSIZE(wszMsg));
  963. hrStatus = WC_INTERNAL_S_PAUSED;
  964. }
  965. else
  966. {
  967. StrCpyW(wszMsg, L" "); // Don't say it, I know what you're thinking...
  968. // ...if we don't do this, then the status
  969. // text won't change.
  970. hrStatus = WC_INTERNAL_S_PENDING;
  971. }
  972. NotifyHandlers(NH_UPDATEPROGRESS, &pItem->m_cookie, -1,
  973. -1, -1, hrStatus, wszMsg);
  974. }
  975. }
  976. }
  977. m_fFillingTheQueue = FALSE;
  978. }
  979. }
  980. HRESULT CThrottler::AddItemToListTail(CUpdateItem *pAddItem)
  981. {
  982. ASSERT(GetCurrentThreadId() == m_dwThreadId);
  983. HRESULT hr = S_OK;
  984. ASSERT(NULL != pAddItem);
  985. if (NULL != pAddItem)
  986. {
  987. if (NULL == m_pItemsTail)
  988. {
  989. // Nothing in the list
  990. ASSERT(NULL == m_pItemsHead);
  991. m_pItemsHead = pAddItem;
  992. }
  993. else
  994. {
  995. m_pItemsTail->m_pNext = pAddItem;
  996. }
  997. m_pItemsTail = pAddItem;
  998. }
  999. else
  1000. {
  1001. hr = E_UNEXPECTED;
  1002. }
  1003. ASSERT(NULL != m_pItemsHead);
  1004. ASSERT(NULL != m_pItemsTail);
  1005. ASSERT(NULL == m_pItemsTail->m_pNext);
  1006. ASSERT(SUCCEEDED(hr));
  1007. return hr;
  1008. }
  1009. HRESULT CThrottler::RemoveItemFromList(CUpdateItem *pRemoveItem, BOOL fDelete)
  1010. {
  1011. ASSERT(GetCurrentThreadId() == m_dwThreadId);
  1012. HRESULT hr = E_UNEXPECTED;
  1013. CUpdateItem *pItem = m_pItemsHead;
  1014. CUpdateItem *pPrevItem = NULL;
  1015. ASSERT(NULL != pRemoveItem);
  1016. ASSERT(NULL != m_pItemsHead);
  1017. ASSERT(NULL != m_pItemsTail);
  1018. if (NULL != pRemoveItem)
  1019. {
  1020. int queueIndex = GetCookieIndexInQueue(&pRemoveItem->m_cookie);
  1021. if (queueIndex >= 0)
  1022. {
  1023. m_updateQueue[queueIndex] = NULL;
  1024. }
  1025. while (pItem)
  1026. {
  1027. if (pItem == pRemoveItem)
  1028. {
  1029. if (NULL != pPrevItem)
  1030. {
  1031. // Removing beyond the head
  1032. pPrevItem->m_pNext = pItem->m_pNext;
  1033. }
  1034. else
  1035. {
  1036. // Removing the head
  1037. m_pItemsHead = pItem->m_pNext;
  1038. }
  1039. // Now fix the tail
  1040. if (m_pItemsTail == pRemoveItem)
  1041. {
  1042. m_pItemsTail = pPrevItem;
  1043. }
  1044. hr = S_OK;
  1045. break;
  1046. }
  1047. pPrevItem = pItem;
  1048. pItem = pItem->m_pNext;
  1049. }
  1050. if (fDelete)
  1051. {
  1052. delete pRemoveItem;
  1053. }
  1054. }
  1055. ASSERT(((NULL != m_pItemsHead) && (NULL != m_pItemsTail) && (NULL == m_pItemsTail->m_pNext)) ||
  1056. ((NULL == m_pItemsHead) && (NULL == m_pItemsTail)));
  1057. ASSERT(SUCCEEDED(hr));
  1058. // If we have just removed our last item from the list, check to see if we forced
  1059. // global online mode or autodialed and fix up if so.
  1060. if ((NULL == m_pItemsHead) && (m_fForcedGlobalOnline || m_fAutoDialed))
  1061. {
  1062. if (m_fForcedGlobalOnline)
  1063. {
  1064. SetGlobalOffline(TRUE);
  1065. m_fForcedGlobalOnline = FALSE;
  1066. m_fAutoDialed = FALSE;
  1067. }
  1068. else
  1069. {
  1070. ASSERT(m_fAutoDialed);
  1071. InternetAutodialHangup(0);
  1072. m_fAutoDialed=FALSE;
  1073. }
  1074. }
  1075. return hr;
  1076. }
  1077. HRESULT CThrottler::CanScheduledItemRun(ISubscriptionItem *pSubsItem)
  1078. {
  1079. ASSERT(GetCurrentThreadId() == m_dwThreadId);
  1080. // If this item is running as a result of a schedule invocation, then
  1081. // we need to check time/range restrictions.
  1082. HRESULT hr = S_OK;
  1083. const TCHAR c_szNoScheduledUpdates[] = TEXT("NoScheduledUpdates");
  1084. DWORD dwData;
  1085. DWORD cbData = sizeof(dwData);
  1086. // First check if the user has disabled scheduled updates in inetcpl.
  1087. if ((ERROR_SUCCESS == SHGetValue(HKEY_CURRENT_USER, c_szRegKey,
  1088. c_szNoScheduledUpdates, NULL, &dwData, &cbData))
  1089. && dwData)
  1090. {
  1091. hr = INET_E_SCHEDULED_UPDATES_DISABLED;
  1092. }
  1093. if (SUCCEEDED(hr))
  1094. {
  1095. // Check if admin has disabled scheduled updates altogether
  1096. if (SHRestricted2W(REST_NoScheduledUpdates, NULL, 0))
  1097. {
  1098. hr = INET_E_SCHEDULED_UPDATES_RESTRICTED;
  1099. }
  1100. }
  1101. if (SUCCEEDED(hr))
  1102. {
  1103. // Check if admin has set a minimum update interval.
  1104. DWORD dwMinUpdateInterval = SHRestricted2W(REST_MinUpdateInterval, NULL, 0);
  1105. if (dwMinUpdateInterval > 0)
  1106. {
  1107. DATE dt;
  1108. if (SUCCEEDED(ReadDATE(pSubsItem, c_szPropCompletionTime, &dt)))
  1109. {
  1110. SYSTEMTIME st;
  1111. GetLocalTime(&st);
  1112. CFileTime lastUpdate;
  1113. CFileTime currentTime;
  1114. VariantTimeToFileTime(dt, lastUpdate);
  1115. SystemTimeToFileTime(&st, &currentTime);
  1116. if ((currentTime - lastUpdate) <
  1117. ((__int64)dwMinUpdateInterval * ONE_MINUTE_IN_FILETIME))
  1118. {
  1119. hr = INET_E_SCHEDULED_UPDATE_INTERVAL;
  1120. }
  1121. }
  1122. }
  1123. }
  1124. if (SUCCEEDED(hr))
  1125. {
  1126. DWORD dwBegin = SHRestricted2W(REST_UpdateExcludeBegin, NULL, 0);
  1127. DWORD dwEnd = SHRestricted2W(REST_UpdateExcludeEnd, NULL, 0);
  1128. // Check if admin has specified a blackout time for scheduled updates.
  1129. if (dwBegin && dwEnd)
  1130. {
  1131. SYSTEMTIME st;
  1132. CFileTime ftNow,
  1133. ftBegin,
  1134. ftEnd;
  1135. GetLocalTime(&st);
  1136. SystemTimeToFileTime(&st, &ftNow);
  1137. st.wSecond = 0;
  1138. st.wMilliseconds = 0;
  1139. st.wHour = (WORD)dwBegin / 60;
  1140. st.wMinute = (WORD)dwBegin % 60;
  1141. SystemTimeToFileTime(&st, &ftBegin);
  1142. st.wHour = (WORD)dwEnd / 60;
  1143. st.wMinute = (WORD)dwEnd % 60;
  1144. SystemTimeToFileTime(&st, &ftEnd);
  1145. // if these values are normalized (ie. begin comes before end)
  1146. if (ftBegin <= ftEnd)
  1147. {
  1148. // Then just check to see if time now is between begin
  1149. // and end. (ie. ftEnd >= ftNow >= ftBegin)
  1150. if ((ftNow >= ftBegin) && (ftNow <= ftEnd))
  1151. {
  1152. hr = INET_E_SCHEDULED_EXCLUDE_RANGE;
  1153. }
  1154. }
  1155. else
  1156. {
  1157. // Begin and end are not normalized. So we check to see if
  1158. // now is before end or now is after begin.
  1159. // For example:
  1160. // Assuming begin is 6pm and end is 6am. If now is 5 pm, the
  1161. // item should run. If now is 10pm or 4am, it should not run.
  1162. if ((ftNow <= ftEnd) || (ftNow >= ftBegin))
  1163. {
  1164. hr = INET_E_SCHEDULED_EXCLUDE_RANGE;
  1165. }
  1166. }
  1167. }
  1168. }
  1169. return hr;
  1170. }
  1171. HRESULT CThrottler::RunCookies(DWORD dwNumCookies,
  1172. const SUBSCRIPTIONCOOKIE *pSubscriptionCookies,
  1173. DWORD dwSyncFlags)
  1174. {
  1175. ASSERT(GetCurrentThreadId() == m_dwThreadId);
  1176. HRESULT hr = S_OK;
  1177. DWORD i;
  1178. CUpdateItem *pUpdateItem;
  1179. DWORD nValidCookies;
  1180. ASSERT(NULL != m_pSyncMgrs);
  1181. ASSERT(0 != dwNumCookies);
  1182. ASSERT(NULL != pSubscriptionCookies);
  1183. if ((0 == dwNumCookies) ||
  1184. (NULL == pSubscriptionCookies))
  1185. {
  1186. return E_INVALIDARG;
  1187. }
  1188. // Check for global offline mode.
  1189. if (!m_fForcedGlobalOnline && IsGlobalOffline())
  1190. {
  1191. // Force global online mode so that our update will succeed.
  1192. DBG("CThrottler::RunCookies; forcing global online mode");
  1193. SetGlobalOffline(FALSE);
  1194. m_fForcedGlobalOnline = TRUE;
  1195. }
  1196. if (IsSyncEvent(dwSyncFlags, SYNCMGRFLAG_MANUAL) ||
  1197. IsSyncEvent(dwSyncFlags, SYNCMGRFLAG_INVOKE))
  1198. {
  1199. if (!InternetGetConnectedStateEx(NULL, NULL, 0, 0))
  1200. {
  1201. if (!InternetAutodial(INTERNET_AUTODIAL_FORCE_ONLINE, 0))
  1202. {
  1203. // REARCHITECT clean up this extra addref/release/return stuff
  1204. AddRef();
  1205. DBG("CThrottler::RunCookies autodial failed");
  1206. // Uh-oh. The user cancelled the dial after starting a
  1207. // manual update. Clean up and return.
  1208. if (m_fForcedGlobalOnline)
  1209. {
  1210. SetGlobalOffline(TRUE);
  1211. m_fForcedGlobalOnline=FALSE;
  1212. }
  1213. WCHAR wszMsg[256];
  1214. MLLoadStringW(IDS_STATUS_ABORTED, wszMsg, ARRAYSIZE(wszMsg));
  1215. for (i=0; i<dwNumCookies; i++)
  1216. {
  1217. ReportThrottlerError(&pSubscriptionCookies[i], E_ABORT, wszMsg);
  1218. UpdateEnd(&pSubscriptionCookies[i], 0, E_ABORT, wszMsg);
  1219. }
  1220. Release();
  1221. return S_FALSE; // E_ABORT;
  1222. }
  1223. // Autodial succeeded
  1224. m_fAutoDialed = TRUE;
  1225. }
  1226. }
  1227. SUBSCRIPTIONCOOKIE *pCookies = new SUBSCRIPTIONCOOKIE[dwNumCookies];
  1228. if (NULL != pCookies)
  1229. {
  1230. SUBSCRIPTIONCOOKIE *pCookie = pCookies;
  1231. memcpy(pCookies, pSubscriptionCookies, dwNumCookies * sizeof(SUBSCRIPTIONCOOKIE));
  1232. // ************************************************************************
  1233. // Don't add any return statements in the loop! We keep a ref on ourselves
  1234. // during this call in case we are Released by all of the sync handlers.
  1235. // ************************************************************************
  1236. AddRef();
  1237. nValidCookies = 0;
  1238. for (i = 0; i < dwNumCookies; i++, pCookie++)
  1239. {
  1240. if (*pCookie == GUID_NULL)
  1241. {
  1242. continue;
  1243. }
  1244. nValidCookies++;
  1245. if (IsSyncEvent(dwSyncFlags, SYNCMGRFLAG_IDLE))
  1246. {
  1247. m_fUserIsIdle = TRUE;
  1248. }
  1249. if (SUCCEEDED(FindCookie(pCookie, &pUpdateItem)))
  1250. {
  1251. if (IsIgnoreIdleSyncEvent(dwSyncFlags))
  1252. {
  1253. DUMPITEM("Removing RS_SUSPENDONIDLE in CThrottler::RunCookies", pCookie);
  1254. // Items updated manually are no longer subject to idle detection.
  1255. pUpdateItem->m_dwRunState &= ~RS_SUSPENDONIDLE;
  1256. }
  1257. if (IsSyncEventFlag(dwSyncFlags, SYNCMGRFLAG_MAYBOTHERUSER))
  1258. {
  1259. // We may now bother user for this item
  1260. pUpdateItem->m_dwRunState |= RS_MAYBOTHERUSER;
  1261. }
  1262. }
  1263. else
  1264. {
  1265. ISubscriptionItem *psi;
  1266. HRESULT hrItem = SubscriptionItemFromCookie(FALSE, pCookie, &psi);
  1267. if (SUCCEEDED(hrItem))
  1268. {
  1269. SUBSCRIPTIONITEMINFO sii;
  1270. sii.cbSize = sizeof(SUBSCRIPTIONITEMINFO);
  1271. hrItem = psi->GetSubscriptionItemInfo(&sii);
  1272. if (SUCCEEDED(hrItem))
  1273. {
  1274. DWORD dwRunState = RS_READY;
  1275. if (IsSyncEvent(dwSyncFlags, SYNCMGRFLAG_IDLE))
  1276. {
  1277. dwRunState |= RS_SUSPENDONIDLE;
  1278. }
  1279. if (IsSyncEventFlag(dwSyncFlags, SYNCMGRFLAG_MAYBOTHERUSER))
  1280. {
  1281. dwRunState |= RS_MAYBOTHERUSER;
  1282. }
  1283. if (IsScheduleSyncEvent(dwSyncFlags))
  1284. {
  1285. hrItem = CanScheduledItemRun(psi);
  1286. }
  1287. if (SUCCEEDED(hrItem))
  1288. {
  1289. pUpdateItem = new CUpdateItem(*pCookie, dwRunState);
  1290. if (NULL != pUpdateItem)
  1291. {
  1292. AddItemToListTail(pUpdateItem);
  1293. }
  1294. else
  1295. {
  1296. hrItem = E_OUTOFMEMORY;
  1297. }
  1298. }
  1299. }
  1300. psi->Release();
  1301. }
  1302. if (FAILED(hrItem))
  1303. {
  1304. // If we fail on an item, we will continue to try others, but
  1305. // we need to indicate failure for this one.
  1306. FailedUpdate(hrItem, pCookie);
  1307. hr = S_FALSE;
  1308. }
  1309. }
  1310. if (NULL == m_pSyncMgrs)
  1311. {
  1312. // We have been unadvised!
  1313. break;
  1314. }
  1315. }
  1316. // No point in trying to update if nobody wants to listen
  1317. if (NULL != m_pSyncMgrs)
  1318. {
  1319. FillTheQueue();
  1320. }
  1321. Release();
  1322. delete [] pCookies;
  1323. if (0 == nValidCookies)
  1324. {
  1325. hr = E_FAIL;
  1326. }
  1327. // ************************************************************************
  1328. // No member variable access after this since we could be dead!!!!
  1329. // ************************************************************************
  1330. }
  1331. else
  1332. {
  1333. hr = E_OUTOFMEMORY;
  1334. }
  1335. return hr;
  1336. }
  1337. HRESULT CThrottler::FindCookie(
  1338. const SUBSCRIPTIONCOOKIE *pSubscriptionCookie,
  1339. CUpdateItem **ppUpdateItem)
  1340. {
  1341. ASSERT(GetCurrentThreadId() == m_dwThreadId);
  1342. HRESULT hr = E_FAIL;
  1343. CUpdateItem *pItem = m_pItemsHead;
  1344. ASSERT(NULL != ppUpdateItem);
  1345. *ppUpdateItem = NULL;
  1346. while (pItem)
  1347. {
  1348. if (pItem->m_cookie == *pSubscriptionCookie)
  1349. {
  1350. *ppUpdateItem = pItem;
  1351. hr = S_OK;
  1352. break;
  1353. }
  1354. pItem = pItem->m_pNext;
  1355. }
  1356. return hr;
  1357. }
  1358. //==============================================================================
  1359. //
  1360. // Auto cache size increase
  1361. //
  1362. //==============================================================================
  1363. // We can return:
  1364. // E_PENDING - agent will pause and wait to be resumed or aborted
  1365. // INET_S_AGENT_INCREASED_CACHE_SIZE - agent will try making stuff sticky again
  1366. // anything else - agent will abort with INET_E_AGENT_CACHE_SIZE_EXCEEDED
  1367. HRESULT CThrottler::AutoCacheSizeRequest(
  1368. const SUBSCRIPTIONCOOKIE *pSubscriptionCookie)
  1369. {
  1370. ASSERT(GetCurrentThreadId() == m_dwThreadId);
  1371. HRESULT hr = S_OK;
  1372. DWORD dwCacheSizeKB;
  1373. int queueIndex;
  1374. DWORD dwValue, dwSize = sizeof(dwValue);
  1375. if (ERROR_SUCCESS == SHGetValue(HKEY_CURRENT_USER, c_szKeyRestrict, c_szCache, NULL, &dwValue, &dwSize)
  1376. && (dwValue != 0))
  1377. {
  1378. // Not allowed to change the cache size.
  1379. hr = E_FAIL;
  1380. }
  1381. queueIndex = GetCookieIndexInQueue(pSubscriptionCookie);
  1382. if (queueIndex >= 0)
  1383. {
  1384. if (!(m_updateQueue[queueIndex]->m_dwRunState & RS_MAYBOTHERUSER))
  1385. {
  1386. // We're not allowed to bother user. Fail.
  1387. hr = E_FAIL;
  1388. }
  1389. }
  1390. else
  1391. {
  1392. DBG_WARN("CThrottler::AutoCacheSizeRequest couldn't find cookie in run queue.");
  1393. hr = E_FAIL; // Couldn't find this cookie in our queue?!
  1394. }
  1395. if (SUCCEEDED(hr) && m_fAutoCacheSizePending)
  1396. {
  1397. // We're already asking the user to increase the cache size.
  1398. hr = E_PENDING;
  1399. }
  1400. if (SUCCEEDED(hr))
  1401. {
  1402. // Let's try to increase the cache.
  1403. if (SUCCEEDED(IncreaseCacheSize(&dwCacheSizeKB)))
  1404. {
  1405. hr = INET_S_AGENT_INCREASED_CACHE_SIZE;
  1406. }
  1407. else
  1408. {
  1409. // We need to ask the user.
  1410. if ((++ m_nAutoCacheSizeTimesAsked) > MAX_AUTOCACHESIZE_ASK)
  1411. {
  1412. hr = E_ABORT; // Already bothered them enough.
  1413. }
  1414. else
  1415. {
  1416. // Let's ask the user. We need unwind our call stack now, however.
  1417. // Tell the throttler to ask the user
  1418. if (SUCCEEDED(CreateThrottlerWnd()))
  1419. {
  1420. PostMessage(m_hwndThrottler, WM_THROTTLER_AUTOCACHESIZE_ASK, 0, dwCacheSizeKB);
  1421. m_fAutoCacheSizePending = TRUE;
  1422. hr = E_PENDING;
  1423. }
  1424. else
  1425. {
  1426. hr = E_FAIL;
  1427. }
  1428. }
  1429. }
  1430. } // !m_fAutoCacheSizePending
  1431. if (hr == E_PENDING)
  1432. {
  1433. // Mark this agent as paused.
  1434. int queueIndex = GetCookieIndexInQueue(pSubscriptionCookie);
  1435. ASSERT(queueIndex >= 0);
  1436. if (queueIndex >= 0)
  1437. {
  1438. m_updateQueue[queueIndex]->m_dwRunState &= ~RS_UPDATING;
  1439. m_updateQueue[queueIndex]->m_dwRunState |= RS_SUSPENDED;
  1440. m_updateQueue[queueIndex] = NULL;
  1441. }
  1442. }
  1443. return hr;
  1444. }
  1445. HRESULT CThrottler::AutoCacheSizeAskUser(DWORD dwCacheSizeKB)
  1446. {
  1447. ASSERT(GetCurrentThreadId() == m_dwThreadId);
  1448. ASSERT(m_fAutoCacheSizePending);
  1449. ASSERT(dwCacheSizeKB);
  1450. ASSERT(m_hwndParent);
  1451. HRESULT hr = E_FAIL;
  1452. // Keep-Alive
  1453. AddRef();
  1454. if (IDOK == ShellMessageBox(MLGetHinst(),
  1455. m_hwndParent,
  1456. MAKEINTRESOURCE(IDS_CACHELIMIT_MESSAGE),
  1457. MAKEINTRESOURCE(IDS_CACHELIMIT_TITLE),
  1458. MB_OKCANCEL | MB_SETFOREGROUND | MB_ICONQUESTION))
  1459. {
  1460. // Come up with a good cache size increase and resume agents
  1461. m_dwAutoCacheSizeIncrease = dwCacheSizeKB / 4;
  1462. if (m_dwAutoCacheSizeIncrease < MIN_CACHE_INCREASE)
  1463. {
  1464. m_dwAutoCacheSizeIncrease = MIN_CACHE_INCREASE;
  1465. }
  1466. m_dwMaxAutoCacheSize = dwCacheSizeKB + (2 * m_dwAutoCacheSizeIncrease);
  1467. if (SUCCEEDED(IncreaseCacheSize(NULL)))
  1468. {
  1469. hr = S_OK;
  1470. }
  1471. }
  1472. else
  1473. {
  1474. // Abort agents
  1475. }
  1476. m_fAutoCacheSizePending = FALSE;
  1477. if (FAILED(hr))
  1478. {
  1479. // User said no (or we couldn't increase the cache).
  1480. ActuallyAbortAll();
  1481. }
  1482. else
  1483. {
  1484. FillTheQueue();
  1485. }
  1486. Release();
  1487. return hr;
  1488. }
  1489. // Auto-increase cache size if user previously ok'd it
  1490. HRESULT CThrottler::IncreaseCacheSize(DWORD *pdwNewCacheSizeKB)
  1491. {
  1492. ASSERT(GetCurrentThreadId() == m_dwThreadId);
  1493. LPINTERNET_CACHE_CONFIG_INFOA pCCI=NULL;
  1494. DWORD dwSizeInKB=0, dwPercent;
  1495. DWORD dwNewSizeInKB=0;
  1496. HRESULT hr = E_FAIL;
  1497. if (SUCCEEDED(GetCacheInfo(&pCCI, &dwSizeInKB, &dwPercent)))
  1498. {
  1499. if (dwSizeInKB < m_dwMaxAutoCacheSize)
  1500. {
  1501. ASSERT(m_dwAutoCacheSizeIncrease > 1023); // At least 1 meg
  1502. if (m_dwAutoCacheSizeIncrease)
  1503. {
  1504. // We still have room to increase cache without asking the user. Use it.
  1505. dwNewSizeInKB = dwSizeInKB + m_dwAutoCacheSizeIncrease;
  1506. if (dwNewSizeInKB > m_dwMaxAutoCacheSize)
  1507. {
  1508. dwNewSizeInKB = m_dwMaxAutoCacheSize;
  1509. }
  1510. if (SUCCEEDED(SetCacheSize(pCCI, dwNewSizeInKB)))
  1511. {
  1512. hr = S_OK;
  1513. dwSizeInKB = dwNewSizeInKB;
  1514. DBG("Throttler just increased TIF cache size");
  1515. }
  1516. }
  1517. }
  1518. MemFree(pCCI);
  1519. }
  1520. if (pdwNewCacheSizeKB)
  1521. {
  1522. *pdwNewCacheSizeKB = dwSizeInKB;
  1523. }
  1524. return hr;
  1525. }